From 78ebddeece7bf3e99437e44b1061bd0576958530 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 22 Oct 2021 10:43:08 +1030 Subject: [PATCH 0001/1530] subd: clean up our fd shuffling logic. It's both complex and flawed, as ZmnSCPxj points out. Make a generic fd ordering routine, and use it. Plus, test it! Signed-off-by: Rusty Russell --- lightningd/subd.c | 131 ++++++++++----------- lightningd/test/run-shuffle_fds.c | 185 ++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 70 deletions(-) create mode 100644 lightningd/test/run-shuffle_fds.c diff --git a/lightningd/subd.c b/lightningd/subd.c index 517ad5b70ad6..066419d29757 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -19,53 +19,72 @@ #include #include -static bool move_fd(int from, int to) +/* Carefully move fd *@from to @to: on success *from set to to */ +static bool move_fd(int *from, int to) { - assert(from >= 0); + assert(*from >= 0); /* dup2 with same arguments may be a no-op, but * the later close would make the fd invalid. * Handle this edge case. */ - if (from == to) + if (*from == to) return true; - if (dup2(from, to) == -1) + if (dup2(*from, to) == -1) return false; /* dup2 does not duplicate flags, copy it here. * This should be benign; the only POSIX-defined * flag is FD_CLOEXEC, and we only use it rarely. */ - if (fcntl(to, F_SETFD, fcntl(from, F_GETFD)) < 0) + if (fcntl(to, F_SETFD, fcntl(*from, F_GETFD)) < 0) return false; - close(from); + close(*from); + *from = to; return true; } -/* Like the above, but move the fd from whatever it currently has - * to any other unused fd number that is *not* its current value. - */ -static bool move_fd_any(int *fd) +/* Returns index of fds which is == this fd, or -1 */ +static int fd_used(int **fds, size_t num_fds, int fd) { - int old_fd = *fd; - int new_fd; - - assert(old_fd >= 0); - - if ((new_fd = dup(old_fd)) == -1) - return false; - - /* dup does not duplicate flags. */ - if (fcntl(new_fd, F_SETFD, fcntl(old_fd, F_GETFD)) < 0) - return false; - - close(old_fd); + for (size_t i = 0; i < num_fds; i++) { + if (*fds[i] == fd) + return i; + } + return -1; +} - *fd = new_fd; +/* Move an series of fd pointers into 0, 1, ... */ +static bool shuffle_fds(int **fds, size_t num_fds) +{ + /* If we need to move an fd out the way, this is a good place to start + * looking */ + size_t next_free_fd = num_fds; + for (size_t i = 0; i < num_fds; i++) { + int in_the_way; + + /* Already in the right place? Great! */ + if (*fds[i] == i) + continue; + /* Is something we care about in the way? */ + in_the_way = fd_used(fds + i, num_fds - i, i); + if (in_the_way != -1) { + /* Find a high-numbered unused fd. */ + while (fd_used(fds + i, num_fds - i, next_free_fd) != -1) + next_free_fd++; + /* Trick: in_the_way is offset by i! */ + if (!move_fd(fds[i + in_the_way], next_free_fd)) + return false; + next_free_fd++; + } - assert(old_fd != *fd); + /* Now there should be nothing in the way. */ + assert(fd_used(fds, num_fds, i) == -1); + if (!move_fd(fds[i], i)) + return false; + } return true; } @@ -191,65 +210,37 @@ static int subd(const char *path, const char *name, goto close_execfail_fail; if (childpid == 0) { - int fdnum = 3, stdin_is_now = STDIN_FILENO; size_t num_args; char *args[] = { NULL, NULL, NULL, NULL, NULL }; + int **fds = tal_arr(tmpctx, int *, 3); + int stdout = STDOUT_FILENO, stderr = STDERR_FILENO; close(childmsg[0]); close(execfail[0]); - // msg = STDIN - if (childmsg[1] != STDIN_FILENO) { - /* Do we need to move STDIN out the way? */ - stdin_is_now = dup(STDIN_FILENO); - if (!move_fd(childmsg[1], STDIN_FILENO)) - goto child_errno_fail; - } + /* msg = STDIN (0) */ + fds[0] = &childmsg[1]; + /* These are untouched */ + fds[1] = &stdout; + fds[2] = &stderr; - /* Dup any extra fds up first. */ while ((fd = va_arg(*ap, int *)) != NULL) { - int actual_fd = *fd; - /* If this were stdin, we moved it above! */ - if (actual_fd == STDIN_FILENO) - actual_fd = stdin_is_now; - - /* If we would overwrite important fds, move those. */ - if (fdnum == dev_disconnect_fd) { - if (!move_fd_any(&dev_disconnect_fd)) - goto child_errno_fail; - } - if (fdnum == execfail[1]) { - if (!move_fd_any(&execfail[1])) - goto child_errno_fail; - } - - if (!move_fd(actual_fd, fdnum)) - goto child_errno_fail; - fdnum++; + assert(*fd != -1); + tal_arr_expand(&fds, fd); } - /* Move dev_disconnect_fd *after* the extra fds above. */ - if (dev_disconnect_fd != -1) { - /* Do not overwrite execfail[1]. */ - if (fdnum == execfail[1]) { - if (!move_fd_any(&execfail[1])) - goto child_errno_fail; - } - if (!move_fd(dev_disconnect_fd, fdnum)) - goto child_errno_fail; - dev_disconnect_fd = fdnum; - fdnum++; - } + /* If we have a dev_disconnect_fd, add it after. */ + if (dev_disconnect_fd != -1) + tal_arr_expand(&fds, &dev_disconnect_fd); + + /* Finally, the fd to report exec errors on */ + tal_arr_expand(&fds, &execfail[1]); - /* Move execfail[1] *after* the fds we will pass - * to the subdaemon. */ - if (!move_fd(execfail[1], fdnum)) + if (!shuffle_fds(fds, tal_count(fds))) goto child_errno_fail; - execfail[1] = fdnum; - fdnum++; /* Make (fairly!) sure all other fds are closed. */ - closefrom(fdnum); + closefrom(tal_count(fds) + 1); num_args = 0; args[num_args++] = tal_strdup(NULL, path); diff --git a/lightningd/test/run-shuffle_fds.c b/lightningd/test/run-shuffle_fds.c new file mode 100644 index 000000000000..7f9a3806036c --- /dev/null +++ b/lightningd/test/run-shuffle_fds.c @@ -0,0 +1,185 @@ +#include "config.h" +#include +#include +#include + +#undef dup2 +#undef close +#undef fcntl + +#define dup2 test_dup2 +#define close test_close +#define fcntl test_fcntl + +/* Indexed by fd num, -1 == not open. */ +#define MAX_TEST_FDS 100 +static int test_fd_arr[MAX_TEST_FDS]; + +static int test_dup2(int oldfd, int newfd) +{ + /* Must not clobber an existing fd */ + assert(test_fd_arr[newfd] == -1); + test_fd_arr[newfd] = test_fd_arr[oldfd]; + return 0; +} + +static int test_close(int fd) +{ + assert(test_fd_arr[fd] != -1); + test_fd_arr[fd] = -1; + return 0; +} + +static int test_fcntl(int fd, int cmd, ... /* arg */ ) +{ + assert(test_fd_arr[fd] != -1); + return 0; +} + +#include "../subd.c" +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for db_begin_transaction_ */ +void db_begin_transaction_(struct db *db UNNEEDED, const char *location UNNEEDED) +{ fprintf(stderr, "db_begin_transaction_ called!\n"); abort(); } +/* Generated stub for db_commit_transaction */ +void db_commit_transaction(struct db *db UNNEEDED) +{ fprintf(stderr, "db_commit_transaction called!\n"); abort(); } +/* Generated stub for db_in_transaction */ +bool db_in_transaction(struct db *db UNNEEDED) +{ fprintf(stderr, "db_in_transaction called!\n"); abort(); } +/* Generated stub for fatal */ +void fatal(const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "fatal called!\n"); abort(); } +/* Generated stub for fromwire_bigsize */ +bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_status_fail */ +bool fromwire_status_fail(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, enum status_failreason *failreason UNNEEDED, wirestring **desc UNNEEDED) +{ fprintf(stderr, "fromwire_status_fail called!\n"); abort(); } +/* Generated stub for fromwire_status_peer_billboard */ +bool fromwire_status_peer_billboard(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *perm UNNEEDED, wirestring **happenings UNNEEDED) +{ fprintf(stderr, "fromwire_status_peer_billboard called!\n"); abort(); } +/* Generated stub for fromwire_status_peer_error */ +bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct channel_id *channel UNNEEDED, wirestring **desc UNNEEDED, bool *warning UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **error_for_them UNNEEDED) +{ fprintf(stderr, "fromwire_status_peer_error called!\n"); abort(); } +/* Generated stub for fromwire_status_version */ +bool fromwire_status_version(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, wirestring **version UNNEEDED) +{ fprintf(stderr, "fromwire_status_version called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for log_ */ +void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, + const struct node_id *node_id UNNEEDED, + bool call_notifier UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "log_ called!\n"); abort(); } +/* Generated stub for log_prefix */ +const char *log_prefix(const struct log *log UNNEEDED) +{ fprintf(stderr, "log_prefix called!\n"); abort(); } +/* Generated stub for log_print_level */ +enum log_level log_print_level(struct log *log UNNEEDED) +{ fprintf(stderr, "log_print_level called!\n"); abort(); } +/* Generated stub for log_status_msg */ +bool log_status_msg(struct log *log UNNEEDED, + const struct node_id *node_id UNNEEDED, + const u8 *msg UNNEEDED) +{ fprintf(stderr, "log_status_msg called!\n"); abort(); } +/* Generated stub for new_log */ +struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, + const struct node_id *default_node_id UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "new_log called!\n"); abort(); } +/* Generated stub for per_peer_state_set_fds_arr */ +void per_peer_state_set_fds_arr(struct per_peer_state *pps UNNEEDED, const int *fds UNNEEDED) +{ fprintf(stderr, "per_peer_state_set_fds_arr called!\n"); abort(); } +/* Generated stub for subdaemon_path */ +const char *subdaemon_path(const tal_t *ctx UNNEEDED, const struct lightningd *ld UNNEEDED, const char *name UNNEEDED) +{ fprintf(stderr, "subdaemon_path called!\n"); abort(); } +/* Generated stub for towire_bigsize */ +void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) +{ fprintf(stderr, "towire_bigsize called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for version */ +const char *version(void) +{ fprintf(stderr, "version called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static void run_test_(int fd0, ...) +{ + va_list ap; + int fd, i; + int *test_fds = tal_arr(tmpctx, int, 1); + int **test_fd_ptrs; + + /* They start all closed */ + memset(test_fd_arr, 0xFF, sizeof(test_fd_arr)); + + test_fds[0] = fd0; + test_fd_arr[fd0] = fd0; + + va_start(ap, fd0); + while ((fd = va_arg(ap, int)) != -1) { + tal_arr_expand(&test_fds, fd); + test_fd_arr[fd] = fd; + } + va_end(ap); + + test_fd_ptrs = tal_arr(tmpctx, int *, tal_count(test_fds)); + for (i = 0; i < tal_count(test_fds); i++) + test_fd_ptrs[i] = &test_fds[i]; + + assert(shuffle_fds(test_fd_ptrs, tal_count(test_fd_ptrs))); + + /* Make sure fds ended up where expected */ + i = 0; + assert(test_fd_arr[i++] == fd0); + va_start(ap, fd0); + while ((fd = va_arg(ap, int)) != -1) + assert(test_fd_arr[i++] == fd); + va_end(ap); + + /* And rest were closed */ + for (; i < MAX_TEST_FDS; i++) + assert(test_fd_arr[i] == -1); +} + +#define run_test(...) \ + run_test_(__VA_ARGS__, -1) + +int main(int argc, char *argv[]) +{ + common_setup(argv[0]); + + run_test(0); + run_test(1); + run_test(0, 1); + run_test(0, 1, 3); + run_test(3, 2, 1, 0); + run_test(5, 2, 1); + + common_shutdown(); +} From c3847484d865f17e1166e5762aee031fa671dbe6 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Wed, 10 Mar 2021 21:05:28 +0800 Subject: [PATCH 0002/1530] Makefile: Avoid overriding `PYTHONPATH`. On some distributions (e.g. Gnu Guix) Python packages are not installed in some standard directory, rather they are installed in different places and the `PYTHONPATH` variable is modified to include the different places. So, we must not use the name `PYTHONPATH` in our `Makefile` since `make` will replace the `PYTHONPATH` environment variable, preventing e.g. `tools/generate-wire.py` from finding `python-mako` installed on such distributions. --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index efd3feba06a2..a4725f9815f3 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ endif # (method=thread to support xdist) PYTEST_OPTS := -v -p no:logging $(PYTEST_OPTS) -PYTHONPATH=$(shell pwd)/contrib/pyln-client:$(shell pwd)/contrib/pyln-testing:$(shell pwd)/contrib/pyln-proto/:$(shell pwd)/external/lnprototest:$(shell pwd)/contrib/pyln-spec/bolt1:$(shell pwd)/contrib/pyln-spec/bolt2:$(shell pwd)/contrib/pyln-spec/bolt4:$(shell pwd)/contrib/pyln-spec/bolt7 +MY_CHECK_PYTHONPATH=$${PYTHONPATH}$${PYTHONPATH:+:}$(shell pwd)/contrib/pyln-client:$(shell pwd)/contrib/pyln-testing:$(shell pwd)/contrib/pyln-proto/:$(shell pwd)/external/lnprototest:$(shell pwd)/contrib/pyln-spec/bolt1:$(shell pwd)/contrib/pyln-spec/bolt2:$(shell pwd)/contrib/pyln-spec/bolt4:$(shell pwd)/contrib/pyln-spec/bolt7 # Collect generated python files to be excluded from lint checks PYTHON_GENERATED= @@ -401,7 +401,7 @@ ifeq ($(PYTEST),) @echo "py.test is required to run the protocol tests, please install using 'pip3 install -r requirements.txt', and rerun 'configure'."; false else ifeq ($(DEVELOPER),1) - @(cd external/lnprototest && PYTHONPATH=$(PYTHONPATH) LIGHTNING_SRC=../.. $(PYTEST) --runner lnprototest.clightning.Runner $(PYTEST_OPTS)) + @(cd external/lnprototest && PYTHONPATH=$(MY_CHECK_PYTHONPATH) LIGHTNING_SRC=../.. $(PYTEST) --runner lnprototest.clightning.Runner $(PYTEST_OPTS)) else @echo "lnprototest target requires DEVELOPER=1, skipping" endif @@ -413,7 +413,7 @@ ifeq ($(PYTEST),) exit 1 else # Explicitly hand DEVELOPER and VALGRIND so you can override on make cmd line. - PYTHONPATH=$(PYTHONPATH) TEST_DEBUG=1 DEVELOPER=$(DEVELOPER) VALGRIND=$(VALGRIND) $(PYTEST) tests/ $(PYTEST_OPTS) + PYTHONPATH=$(MY_CHECK_PYTHONPATH) TEST_DEBUG=1 DEVELOPER=$(DEVELOPER) VALGRIND=$(VALGRIND) $(PYTEST) tests/ $(PYTEST_OPTS) endif # Keep includes in alpha order. @@ -470,7 +470,7 @@ PYSRC=$(shell git ls-files "*.py" | grep -v /text.py) contrib/pylightning/lightn # allows it to find that PYLN_PATH=$(shell pwd)/lightningd:$(PATH) check-pyln-%: $(BIN_PROGRAMS) $(PKGLIBEXEC_PROGRAMS) $(PLUGINS) - @(cd contrib/$(shell echo $@ | cut -b 7-) && PATH=$(PYLN_PATH) PYTHONPATH=$(PYTHONPATH) $(MAKE) check) + @(cd contrib/$(shell echo $@ | cut -b 7-) && PATH=$(PYLN_PATH) PYTHONPATH=$(MY_CHECK_PYTHONPATH) $(MAKE) check) check-python: check-python-flake8 check-pytest-pyln-proto check-pyln-client check-pyln-testing @@ -482,7 +482,7 @@ check-python-flake8: @flake8 --ignore=E501,E731,E741,W503 --exclude $(shell echo ${PYTHON_GENERATED} | sed 's/ \+/,/g') ${PYSRC} check-pytest-pyln-proto: - PATH=$(PYLN_PATH) PYTHONPATH=$(PYTHONPATH) $(PYTEST) contrib/pyln-proto/tests/ + PATH=$(PYLN_PATH) PYTHONPATH=$(MY_CHECK_PYTHONPATH) $(PYTEST) contrib/pyln-proto/tests/ check-includes: check-src-includes check-hdr-includes @tools/check-includes.sh From c00202a0daedf11c9871ef78a843d648442f2f95 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:35 +1030 Subject: [PATCH 0003/1530] pytest: remove test_shutdown_alternate_txid. We're about to require that fundchannel_complete() take a PSBT, where it does sanity checks to avoid this error, making this a difficult mistake to make. Is it time to remove this functionality anyway? @cdecker? Signed-off-by: Rusty Russell --- tests/test_closing.py | 47 ------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index eab628020562..ea85d82ec878 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3070,53 +3070,6 @@ def test_segwit_shutdown_script(node_factory, bitcoind, executor): l1.rpc.fundchannel(l2.info['id'], 10**6) -@pytest.mark.openchannel('v1') -def test_shutdown_alternate_txid(node_factory, bitcoind): - l1, l2 = node_factory.line_graph(2, fundchannel=False, - opts={'experimental-shutdown-wrong-funding': None, - 'allow-deprecated-apis': True}) - - amount = 1000000 - amount_msat = Millisatoshi(amount * 1000) - - # Let's make a classic fundchannel mistake (wrong txid!) - addr = l1.rpc.fundchannel_start(l2.info['id'], amount_msat)['funding_address'] - txid = bitcoind.rpc.sendtoaddress(addr, amount / 10**8) - - # Gotta figure out which output manually :( - tx = bitcoind.rpc.getrawtransaction(txid, 1) - for n, out in enumerate(tx['vout']): - if scriptpubkey_addr(out['scriptPubKey']) == addr: - txout = n - - bitcoind.generate_block(1, wait_for_mempool=1) - - # Wrong txid, wrong txout! - wrong_txid = txid[16:] + txid[:16] - wrong_txout = txout ^ 1 - l1.rpc.fundchannel_complete(l2.info['id'], wrong_txid, wrong_txout) - - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] != []) - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'][0]['state'] == 'CHANNELD_AWAITING_LOCKIN') - - closeaddr = l1.rpc.newaddr()['bech32'] - - # Oops, try rescuing it! - l1.rpc.call('close', {'id': l2.info['id'], 'destination': closeaddr, 'wrong_funding': txid + ':' + str(txout)}) - - # Just make sure node has no funds. - assert l1.rpc.listfunds()['outputs'] == [] - - bitcoind.generate_block(100, wait_for_mempool=1) - sync_blockheight(bitcoind, [l1, l2]) - - # We will see our funds return. - assert len(l1.rpc.listfunds()['outputs']) == 1 - - wait_for(lambda: l2.rpc.listpeers()['peers'] == []) - wait_for(lambda: l1.rpc.listpeers()['peers'] == []) - - @unittest.skipIf(not EXPERIMENTAL_FEATURES, "Needs anchor_outputs") @pytest.mark.developer("needs to set dev-disconnect") def test_closing_higherfee(node_factory, bitcoind, executor): From 0c0a301062c6fbceb4ce98ea81834a4419ec4e8e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:41 +1030 Subject: [PATCH 0004/1530] fundchannel_complete: remove deprecated txid/txout params. Changelog-Removed: JSON-RPC: `fundchannel_complete` `txid` and `txout` parameters (deprecated in v0.10.0) --- contrib/pyln-client/pyln/client/lightning.py | 29 +----- lightningd/opening_control.c | 96 ++++++++------------ 2 files changed, 41 insertions(+), 84 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 5ca8c2117624..f80006e91eaa 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -2,7 +2,6 @@ import logging import os import socket -import warnings from contextlib import contextmanager from decimal import Decimal from json import JSONEncoder @@ -757,34 +756,16 @@ def fundchannel_cancel(self, node_id): } return self.call("fundchannel_cancel", payload) - def _deprecated_fundchannel_complete(self, node_id, funding_txid, funding_txout): - warnings.warn("fundchannel_complete: funding_txid & funding_txout replaced by psbt: expect removal" - " in Mid-2021", - DeprecationWarning) - + def fundchannel_complete(self, node_id, psbt): + """ + Complete channel establishment with {id}, using {psbt}. + """ payload = { "id": node_id, - "txid": funding_txid, - "txout": funding_txout, + "psbt": psbt, } return self.call("fundchannel_complete", payload) - def fundchannel_complete(self, node_id, *args, **kwargs): - """ - Complete channel establishment with {id}, using {psbt}. - """ - if 'txid' in kwargs or len(args) == 2: - return self._deprecated_fundchannel_complete(node_id, *args, **kwargs) - - def _fundchannel_complete(node_id, psbt): - payload = { - "id": node_id, - "psbt": psbt, - } - return self.call("fundchannel_complete", payload) - - return _fundchannel_complete(node_id, *args, **kwargs) - def getinfo(self): """ Show information about this node. diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 3b5b6e61debb..4df90dc51abe 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -974,34 +974,12 @@ static struct command_result *json_fundchannel_complete(struct command *cmd, struct wally_psbt *funding_psbt; u32 *funding_txout_num = NULL; struct funding_channel *fc; - bool old_api; - - /* params is NULL for initial parameter desc generation! */ - if (params && deprecated_apis) { - /* We used to have a three-arg version. */ - if (params->type == JSMN_ARRAY) - old_api = (params->size == 3); - else - old_api = (json_get_member(buffer, params, "txid") - != NULL); - if (old_api) { - if (!param(cmd, buffer, params, - p_req("id", param_node_id, &id), - p_req("txid", param_txid, &funding_txid), - p_req("txout", param_number, &funding_txout_num), - NULL)) - return command_param_failed(); - } - } else - old_api = false; - - if (!old_api) { - if (!param(cmd, buffer, params, - p_req("id", param_node_id, &id), - p_req("psbt", param_psbt, &funding_psbt), - NULL)) - return command_param_failed(); - } + + if (!param(cmd, buffer, params, + p_req("id", param_node_id, &id), + p_req("psbt", param_psbt, &funding_psbt), + NULL)) + return command_param_failed(); peer = peer_by_id(cmd->ld, id); if (!peer) { @@ -1024,40 +1002,38 @@ static struct command_result *json_fundchannel_complete(struct command *cmd, fc = peer->uncommitted_channel->fc; - if (!old_api) { - /* Figure out the correct output, and perform sanity checks. */ - for (size_t i = 0; i < funding_psbt->tx->num_outputs; i++) { - if (memeq(funding_psbt->tx->outputs[i].script, - funding_psbt->tx->outputs[i].script_len, - fc->funding_scriptpubkey, - tal_bytelen(fc->funding_scriptpubkey))) { - if (funding_txout_num) - return command_fail(cmd, FUNDING_PSBT_INVALID, - "Two outputs to open channel"); - funding_txout_num = tal(cmd, u32); - *funding_txout_num = i; - } + /* Figure out the correct output, and perform sanity checks. */ + for (size_t i = 0; i < funding_psbt->tx->num_outputs; i++) { + if (memeq(funding_psbt->tx->outputs[i].script, + funding_psbt->tx->outputs[i].script_len, + fc->funding_scriptpubkey, + tal_bytelen(fc->funding_scriptpubkey))) { + if (funding_txout_num) + return command_fail(cmd, FUNDING_PSBT_INVALID, + "Two outputs to open channel"); + funding_txout_num = tal(cmd, u32); + *funding_txout_num = i; } - if (!funding_txout_num) - return command_fail(cmd, FUNDING_PSBT_INVALID, - "No output to open channel"); - - /* Can't really check amounts for elements. */ - if (!chainparams->is_elements - && !amount_sat_eq(amount_sat(funding_psbt->tx->outputs - [*funding_txout_num].satoshi), - fc->funding_sats)) - return command_fail(cmd, FUNDING_PSBT_INVALID, - "Output to open channel is %"PRIu64"sat," - " should be %s", - funding_psbt->tx->outputs - [*funding_txout_num].satoshi, - type_to_string(tmpctx, struct amount_sat, - &fc->funding_sats)); - - funding_txid = tal(cmd, struct bitcoin_txid); - psbt_txid(NULL, funding_psbt, funding_txid, NULL); } + if (!funding_txout_num) + return command_fail(cmd, FUNDING_PSBT_INVALID, + "No output to open channel"); + + /* Can't really check amounts for elements. */ + if (!chainparams->is_elements + && !amount_sat_eq(amount_sat(funding_psbt->tx->outputs + [*funding_txout_num].satoshi), + fc->funding_sats)) + return command_fail(cmd, FUNDING_PSBT_INVALID, + "Output to open channel is %"PRIu64"sat," + " should be %s", + funding_psbt->tx->outputs + [*funding_txout_num].satoshi, + type_to_string(tmpctx, struct amount_sat, + &fc->funding_sats)); + + funding_txid = tal(cmd, struct bitcoin_txid); + psbt_txid(NULL, funding_psbt, funding_txid, NULL); /* Fun fact: our wire protocol only allows 16 bits for outnum. * That is reflected in our encoding scheme for short_channel_id. */ From ecdc15591ba5414eaf3fa8c3cab4486099d6070b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:41 +1030 Subject: [PATCH 0005/1530] custommsg hook: remove message field. Changelog-Removed: Plugins: The `message` field on the `custommsg` hook (deprecated in v0.10.0) --- lightningd/peer_control.c | 47 +-------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e06c262eac56..a71bc358e461 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2464,52 +2464,7 @@ static void custommsg_payload_serialize(struct custommsg_payload *payload, struct json_stream *stream, struct plugin *plugin) { - /* Backward compat for broken custommsg: if we get a custommsg - * from an old c-lightning node, then we must identify and - * strip the prefix from the payload. If it's a new one, we - * need to add the frame for the `message` for backward - * compatibility. */ - size_t msglen = tal_bytelen(payload->msg), framedlen, unframedlen, max; - const u8 *unframed, *framed, *p = payload->msg; - u8 *tmp; - max = msglen; - - if (msglen >= 4 && fromwire_u16(&p, &max) == WIRE_CUSTOMMSG_OUT && - fromwire_u16(&p, &max) == msglen - 4 && deprecated_apis) { - /* This is from an old c-lightning implementation that - * erroneously sent the framed message over the - * connection. */ - unframed = payload->msg + 4; - unframedlen = msglen - 4; - framed = payload->msg; - framedlen = msglen; - } else { - /* This is from a new c-lightning, which correctly - * sent the raw custommsg without framing. We still - * need to reconstruct the wrong message since plugins - * may rely on it. */ - if (deprecated_apis) { - tmp = tal_arr(tmpctx, u8, 0); - towire_u16(&tmp, WIRE_CUSTOMMSG_OUT); - towire_u16(&tmp, msglen); - towire(&tmp, payload->msg, msglen); - framedlen = msglen + 4; - framed = tmp; - } - - unframed = payload->msg; - unframedlen = msglen; - } - - if (deprecated_apis) { - json_add_hex(stream, "message", framed, framedlen); - json_add_string( - stream, "warning", - "The `message` field is deprecated and has been replaced " - "with the payload` field which skips the internal type and " - "the length prefix. Please update to use that instead."); - } - json_add_hex(stream, "payload", unframed, unframedlen); + json_add_hex_talarr(stream, "payload", payload->msg); json_add_node_id(stream, "peer_id", &payload->peer_id); } From 2f247c7bfbe8eca92b5a2b0619ce5c01f9278679 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:41 +1030 Subject: [PATCH 0006/1530] torv2: remove support for advertizing and connecting. October was the date Torv2 is no longer supported by the Tor Project; it will probably not work at all by next release, so we should remove it now even though it's not quite the 6 months we prefer for deprecation cycles. I still see 110 nodes advertizing Torv2 (vs 10,292 Torv3); we still parse and display it, we just don't advertize or connect to it. Signed-off-by: Rusty Russell --- common/json_helpers.c | 2 +- common/test/run-ip_port_parsing.c | 18 ++++++++---------- common/wireaddr.c | 18 ++++++++---------- common/wireaddr.h | 2 +- connectd/connectd.c | 16 ++++++++++------ connectd/netaddress.c | 4 ++-- devtools/gossipwith.c | 2 +- lightningd/options.c | 10 ++++------ tests/test_gossip.py | 5 +---- 9 files changed, 36 insertions(+), 41 deletions(-) diff --git a/common/json_helpers.c b/common/json_helpers.c index 65308d7ba37d..44efdffbd44c 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -273,7 +273,7 @@ void json_add_address(struct json_stream *response, const char *fieldname, json_add_string(response, "type", "ipv6"); json_add_string(response, "address", addrstr); json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_TOR_V2) { + } else if (addr->type == ADDR_TYPE_TOR_V2_REMOVED) { json_add_string(response, "type", "torv2"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index ad9f73d77b04..68b1bdc55d42 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -182,19 +182,17 @@ int main(int argc, char *argv[]) assert(parse_wireaddr("4ruvswpqec5i2gogopxl4vm5bruzknbvbylov2awbo4rxiq4cimdldad.onion", &addr, 1, false, NULL)); assert(addr.port == 1); - assert(parse_wireaddr("odpzvneidqdf5hdq.onion:49150", &addr, 1, false, NULL)); - assert(addr.port == 49150); - - assert(parse_wireaddr("odpzvneidqdf5hdq.onion", &addr, 1, false, NULL)); - assert(addr.port == 1); + /* We don't accept torv2 any more */ + assert(!parse_wireaddr("odpzvneidqdf5hdq.onion:49150", &addr, 1, false, NULL)); + assert(!parse_wireaddr("odpzvneidqdf5hdq.onion", &addr, 1, false, NULL)); - // Don't accept legacy hidden services with deprecated APIs on + /* Neither allow_deprecated = true nor false will parse it now */ + assert(!parse_wireaddr_internal("odpzvneidqdf5hdq.onion", &addr_int, 1, + false, false, false, false, NULL)); assert(!parse_wireaddr_internal("odpzvneidqdf5hdq.onion", &addr_int, 1, - false, false, false, /* allow_deprecated = */ false, NULL)); - assert(parse_wireaddr_internal("odpzvneidqdf5hdq.onion", &addr_int, 1, - false, false, false, /* allow_deprecated = */ true, NULL)); + false, false, false, true, NULL)); - assert(tal_count(wireaddr_from_hostname(tmpctx, "odpzvneidqdf5hdq.onion", 1, NULL, NULL, NULL)) > 0); + assert(wireaddr_from_hostname(tmpctx, "odpzvneidqdf5hdq.onion", 1, NULL, NULL, NULL) == NULL); assert(wireaddr_from_hostname(tmpctx, "aaa.onion", 1, NULL, NULL, NULL) == NULL); common_shutdown(); diff --git a/common/wireaddr.c b/common/wireaddr.c index 6851df8f80eb..08b0683fb738 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -31,7 +31,7 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) case ADDR_TYPE_IPV6: addr->addrlen = 16; break; - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: addr->addrlen = TOR_V2_ADDRLEN; break; case ADDR_TYPE_TOR_V3: @@ -209,7 +209,7 @@ bool wireaddr_is_wildcard(const struct wireaddr *addr) case ADDR_TYPE_IPV6: case ADDR_TYPE_IPV4: return memeqzero(addr->addr, addr->addrlen); - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_WEBSOCKET: return false; @@ -255,7 +255,7 @@ char *fmt_wireaddr_without_port(const tal_t * ctx, const struct wireaddr *a) if (!inet_ntop(AF_INET6, a->addr, addrstr, INET6_ADDRSTRLEN)) return "Unprintable-ipv6-address"; return tal_fmt(ctx, "[%s]", addrstr); - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: return tal_fmt(ctx, "%s.onion", b32_encode(tmpctx, a->addr, a->addrlen)); @@ -345,9 +345,7 @@ wireaddr_from_hostname(const tal_t *ctx, u8 *dec = b32_decode(tmpctx, hostname, strlen(hostname) - strlen(".onion")); tal_resize(&addrs, 1); - if (tal_count(dec) == TOR_V2_ADDRLEN) { - addrs[0].type = ADDR_TYPE_TOR_V2; - } else if (tal_count(dec) == TOR_V3_ADDRLEN) { + if (tal_count(dec) == TOR_V3_ADDRLEN) { addrs[0].type = ADDR_TYPE_TOR_V3; } else { if (err_msg) @@ -591,9 +589,9 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, addr->itype = ADDR_INTERNAL_WIREADDR; if (parse_wireaddr(arg, &addr->u.wireaddr, port, dns_ok ? NULL : &needed_dns, err_msg)) { - if (!allow_deprecated && addr->u.wireaddr.type == ADDR_TYPE_TOR_V2) { + if (addr->u.wireaddr.type == ADDR_TYPE_TOR_V2_REMOVED) { if (err_msg) - *err_msg = "v2 Tor onion services are deprecated"; + *err_msg = "v2 Tor onion services not supported"; return false; } @@ -695,7 +693,7 @@ struct addrinfo *wireaddr_to_addrinfo(const tal_t *ctx, ai->ai_addrlen = sizeof(*sin6); ai->ai_addr = (struct sockaddr *)sin6; return ai; - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_WEBSOCKET: break; @@ -750,7 +748,7 @@ bool all_tor_addresses(const struct wireaddr_internal *wireaddr) case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: return false; - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_WEBSOCKET: continue; diff --git a/common/wireaddr.h b/common/wireaddr.h index d7d894d23ca9..b484be47feb4 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -50,7 +50,7 @@ struct sockaddr_un; enum wire_addr_type { ADDR_TYPE_IPV4 = 1, ADDR_TYPE_IPV6 = 2, - ADDR_TYPE_TOR_V2 = 3, + ADDR_TYPE_TOR_V2_REMOVED = 3, ADDR_TYPE_TOR_V3 = 4, ADDR_TYPE_WEBSOCKET = 6, }; diff --git a/connectd/connectd.c b/connectd/connectd.c index 68c767813aa4..4cd25fbc4837 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -962,7 +962,9 @@ static void try_connect_one_addr(struct connecting *connect) break; case ADDR_INTERNAL_WIREADDR: switch (addr->u.wireaddr.type) { - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: + af = -1; + break; case ADDR_TYPE_TOR_V3: use_proxy = true; break; @@ -1154,7 +1156,7 @@ static bool handle_wireaddr_listen(struct daemon *daemon, return false; /* Handle specially by callers. */ case ADDR_TYPE_WEBSOCKET: - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: break; } @@ -1659,9 +1661,12 @@ static void add_gossip_addrs(struct wireaddr_internal **addrs, /* Wrap each one in a wireaddr_internal and add to addrs. */ for (size_t i = 0; i < tal_count(normal_addrs); i++) { + /* This is not supported, ignore. */ + if (normal_addrs[i].type == ADDR_TYPE_TOR_V2_REMOVED) + continue; + /* add TOR addresses in a second loop */ - if (normal_addrs[i].type == ADDR_TYPE_TOR_V2 || - normal_addrs[i].type == ADDR_TYPE_TOR_V3) + if (normal_addrs[i].type == ADDR_TYPE_TOR_V3) continue; if (wireaddr_int_equals_wireaddr(addrhint, &normal_addrs[i])) continue; @@ -1672,8 +1677,7 @@ static void add_gossip_addrs(struct wireaddr_internal **addrs, } /* so connectd prefers direct connections if possible. */ for (size_t i = 0; i < tal_count(normal_addrs); i++) { - if (normal_addrs[i].type != ADDR_TYPE_TOR_V2 && - normal_addrs[i].type != ADDR_TYPE_TOR_V3) + if (normal_addrs[i].type != ADDR_TYPE_TOR_V3) continue; if (wireaddr_int_equals_wireaddr(addrhint, &normal_addrs[i])) continue; diff --git a/connectd/netaddress.c b/connectd/netaddress.c index ce55cfa3939d..dff03e92d308 100644 --- a/connectd/netaddress.c +++ b/connectd/netaddress.c @@ -135,7 +135,7 @@ static bool IsRFC4843(const struct wireaddr *addr) static bool IsTor(const struct wireaddr *addr) { - return addr->type == ADDR_TYPE_TOR_V2 || addr->type == ADDR_TYPE_TOR_V3; + return addr->type == ADDR_TYPE_TOR_V3; } static bool IsLocal(const struct wireaddr *addr) @@ -256,7 +256,7 @@ bool guess_address(struct wireaddr *addr) memcpy(addr->addr, &sin6.sin6_addr, addr->addrlen); return ret; } - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: case ADDR_TYPE_WEBSOCKET: status_broken("Cannot guess address type %u", addr->type); diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 2bd0a4778305..97287103d36b 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -317,7 +317,7 @@ int main(int argc, char *argv[]) case ADDR_INTERNAL_WIREADDR: switch (addr.u.wireaddr.type) { - case ADDR_TYPE_TOR_V2: + case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: opt_usage_exit_fail("Don't support proxy use"); break; diff --git a/lightningd/options.c b/lightningd/options.c index d8431c13c542..152575254f87 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -254,9 +254,8 @@ static char *opt_add_addr(const char *arg, struct lightningd *ld) /* handle in case you used the addr option with an .onion */ if (parse_wireaddr_internal(arg, &addr, 0, true, false, true, deprecated_apis, NULL)) { - if (addr.itype == ADDR_INTERNAL_WIREADDR && ( - addr.u.wireaddr.type == ADDR_TYPE_TOR_V2 || - addr.u.wireaddr.type == ADDR_TYPE_TOR_V3)) { + if (addr.itype == ADDR_INTERNAL_WIREADDR && + addr.u.wireaddr.type == ADDR_TYPE_TOR_V3) { log_unusual(ld->log, "You used `--addr=%s` option with an .onion address, please use" " `--announce-addr` ! You are lucky in this node live some wizards and" " fairies, we have done this for you and announce, Be as hidden as wished", @@ -302,9 +301,8 @@ static char *opt_add_bind_addr(const char *arg, struct lightningd *ld) /* handle in case you used the bind option with an .onion */ if (parse_wireaddr_internal(arg, &addr, 0, true, false, true, deprecated_apis, NULL)) { - if (addr.itype == ADDR_INTERNAL_WIREADDR && ( - addr.u.wireaddr.type == ADDR_TYPE_TOR_V2 || - addr.u.wireaddr.type == ADDR_TYPE_TOR_V3)) { + if (addr.itype == ADDR_INTERNAL_WIREADDR && + addr.u.wireaddr.type == ADDR_TYPE_TOR_V3) { log_unusual(ld->log, "You used `--bind-addr=%s` option with an .onion address," " You are lucky in this node live some wizards and" " fairies, we have done this for you and don't announce, Be as hidden as wished", diff --git a/tests/test_gossip.py b/tests/test_gossip.py index b69531f8006f..b45e430311b9 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -918,9 +918,7 @@ def test_gossip_addresses(node_factory, bitcoind): '[::]:3', '127.0.0.1:2', 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', - '3fyb44wdhnd2ghhl.onion:1234' ], - 'allow-deprecated-apis': True, }) l2 = node_factory.get_node() l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -934,8 +932,7 @@ def test_gossip_addresses(node_factory, bitcoind): assert len(nodes) == 1 and nodes[0]['addresses'] == [ {'type': 'ipv4', 'address': '127.0.0.1', 'port': 2}, {'type': 'ipv6', 'address': '::', 'port': 3}, - {'type': 'torv2', 'address': '3fyb44wdhnd2ghhl.onion', 'port': 1234}, - {'type': 'torv3', 'address': 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', 'port': 9735} + {'type': 'torv3', 'address': 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', 'port': 9735}, ] From 2a35f33cb0c1faa51f514b6004f4e4b2c3f8f29a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:41 +1030 Subject: [PATCH 0007/1530] doc/TOR.md: Make it clear that Tor == Torv3. And switch ```` to ``` (emacs colorization was confused!). Signed-off-by: Rusty Russell --- doc/TOR.md | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/doc/TOR.md b/doc/TOR.md index e81bf3c94ca7..4b87c5c05231 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -2,10 +2,7 @@ To use any Tor features with c-lightning you must have Tor installed and running. -Note that [Tor v2 onion services are deprecated since mid-2020](https://blog.torproject.org/v2-deprecation-timeline) -and that C-lightning deprecated their support since mid-2021. - -You can check your installed Tor version with `tor --version` or `sudo tor --version` +Note that we only support Tor v3: you can check your installed Tor version with `tor --version` or `sudo tor --version` If Tor is not installed you can install it on Debian based Linux systems (Ubuntu, Debian, etc) with the following command: @@ -198,11 +195,11 @@ You can configure the service authenticated by cookie or by password: ##### Service authenticated by cookie Add the following lines in the `/etc/tor/torrc` file: -```` +``` ControlPort 9051 CookieAuthentication 1 CookieAuthFileGroupReadable 1 -```` +``` ##### Service authenticated by password @@ -222,7 +219,7 @@ This returns a line like ``` ControlPort 9051 HashedControlPassword 16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F -```` +``` Save the file and restart the Tor service. In linux: @@ -256,15 +253,14 @@ Add the following lines in the `/etc/tor/torrc` file (you might already have done this if for example you connected Bitcoin over Tor): -```` +``` ControlPort 9051 CookieAuthentication 1 CookieAuthFileGroupReadable 1 -```` +``` Then you can use `--addr=statictor:127.0.0.1:9051` instead of `--announce-addr=.onionAddressV3`. -By default V3 onion addresses are generated. Note that you have to specify a `--bind-addr` first before using `--addr=statictor:`. @@ -280,9 +276,6 @@ incoming data to your node via this .onion address. You can then specify multiple `statictor:` options with different `BLOB`s. -However, even if you have multiple persistent addresses, you can -only announce up to one onion service (v3). -This is a limitation of the BOLT spec. It is still possible for other nodes to contact you by those other hidden services. @@ -293,14 +286,13 @@ address. ##### Explicit Control -If you want to create a version 3 address, you must also add `HiddenServiceVersion 3` so -the whole section will be: +If you want to create your own hidden address, the whole section will be: -```` +``` HiddenServiceDir /var/lib/tor/lightningd-service_v3/ HiddenServiceVersion 3 HiddenServicePort 1234 127.0.0.1:9735 -```` +``` The hidden lightning service will be reachable at port 1234 (global port) of the .onion address, which will be created at the restart of the @@ -416,7 +408,7 @@ lightning-cli connect nodeID yourexternalIPAddress Port ``` through Clearnet. -#### Case #3 c-lightning has a public IP address and a non-persisten Tor service address +#### Case #3 c-lightning has a public IP address and a non-persistent Tor service address In this case other nodes can connect to you via Clearnet or Tor. @@ -461,7 +453,7 @@ Other nodes will not be able to `connect` to you unless you communicate them how You will find your .onion address with the command `lightning-cli getinfo` and the other nodes will be able to connect to it through the 9735 port. -#### Case #6 c-lightning has a public IP address and a fixed Tor v3 hidden service +#### Case #6 c-lightning has a public IP address and a fixed Tor hidden service You will be reachable via Clearnet, via Tor to the .onion if it is communicated to the node that wants to connect with our node. @@ -482,7 +474,7 @@ To make your external hidden service public you add: ``` to the options to publish your IP number. -#### Case #7 c-lightning has no public IP address, a fixed Tor V3 service address +#### Case #7 c-lightning has no public IP address, a fixed Tor hidden service The Persistent addresses can be created with the steps [outlined above](#creation-of-an-hidden-service-for-a-persistent-onion-address). From 3dcab9793d04912f9e0e04d55d930c286eb7d6e0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:41 +1030 Subject: [PATCH 0008/1530] doc/TOR.md: simplify, and don't cover autotor. autotor is older, but statictor is better. Your options are really "use HiddenServiceDir in torrc" vs "use statictor", with the issue that statictor requires you to configure Tor for control access by c-lightning. Signed-off-by: Rusty Russell --- doc/TOR.md | 342 +++++++++++++++++++++-------------------------------- 1 file changed, 132 insertions(+), 210 deletions(-) diff --git a/doc/TOR.md b/doc/TOR.md index 4b87c5c05231..781af0cd70cf 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -35,6 +35,7 @@ To provide the node with a .onion address you can: * create a **persistent** address with a hidden service. + ### Quick Start On Linux It is easy to create a single persistent Tor address and not announce a public IP. @@ -176,82 +177,58 @@ on those. ### Detailed Discussion -#### Creation of an auto service for non-persistent .onion addresses - -To provide the node a non-persistent .onion address it -is necessary to access the Tor auto service. These types of addresses change -each time the Tor service is restarted. - -*NOTE:If the node is required to be reachable only by **persistent** .onion addresses, this -part can be skipped and it is necessary to set up a hidden service with the steps -outlined in the next section.* - -To create and use the auto service follow these steps: - -Edit the Tor config file `/etc/tor/torrc` - -You can configure the service authenticated by cookie or by password: +#### Three Ways to Create .onion Addresses for C-lightning -##### Service authenticated by cookie -Add the following lines in the `/etc/tor/torrc` file: +You have have Tor create an onion address for you, and tell +c-lightning to use that, or you can have c-lightning tell Tor to +create the same onion address every time it starts up, or you can have +c-lightning tell Tor to create a new onion address every time. -``` -ControlPort 9051 -CookieAuthentication 1 -CookieAuthFileGroupReadable 1 -``` +#### Tor-Created .onion Address -##### Service authenticated by password +Having Tor create an onion address lets you run other services (e.g. +a web server) at that same address, and you just tell that address to +c-lightning and it doesn't have to talk to the Tor server at all. -Alternatively, you can set the authentication -to the service with a password by following these steps: +Put the following in your `/etc/tor/torrc` file: -1. Create a hash of your password with ``` -tor --hash-password yourpassword +HiddenServiceDir /var/lib/tor/lightningd-service_v3/ +HiddenServiceVersion 3 +HiddenServicePort 1234 127.0.0.1:9735 ``` -This returns a line like - -`16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F` - -2. put these lines in the `/etc/tor/torrc` file: -``` -ControlPort 9051 -HashedControlPassword 16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F -``` +The hidden lightning service will be reachable at port 1234 (global port) +of the .onion address, which will be created at the restart of the +Tor service. Both types of addresses can coexist on the same node. Save the file and restart the Tor service. In linux: `/etc/init.d/tor restart` or `sudo systemctl start tor` depending on the configuration of your system. -The auto service is used by adding `--addr=autotor:127.0.0.1:9051` if you -want the address to be public or `--bind-addr=autotor:127.0.0.1:9051` if you -don't want to publish it. - -In the case where the auto service is authenticated through a password, it will -be necessary to add the option `--tor-service-password=yourpassword` (not the hash). - -The created non-persistent .onion address will be shown by the `lightning-cli getinfo` -command. The other nodes will be able to `connect` to this .onion address through the -9735 port. +You will find the newly created address (myaddress.onion) with: +``` +sudo cat /var/lib/tor/lightningd-service_v3/hostname +``` -#### Creation of a hidden service for a persistent .onion address +Now you need to tell c-lightning to advertize that onion hostname and +port, by placing `announce-addr=myaddress.onion` in your lightning +config. -To have a persistent .onion address other nodes can connect to, it -is necessary to set up a [Tor Hidden Service]. +#### Letting C-lightning Control Tor -*NOTE: In the case where only non-persistent addresses are required, -you don't have to create the hidden service and you can skip this part.* +To have c-lightning control your Tor addresses, you have to tell Tor +to accept control commands from c-lightning, either by using a cookie, +or a password. -##### Automatic persistent .onion address +##### Service authenticated by cookie -It is possible to generate persistent .onion addresses automatically. +This tells Tor to create a cookie file each time: lightningd will have +to be in the same group as tor (e.g. debian-tor): you can look at +`/run/tor/control.authcookie` to check the group name. -Add the following lines in the `/etc/tor/torrc` file -(you might already have done this if for example you connected Bitcoin -over Tor): +Add the following lines in the `/etc/tor/torrc` file: ``` ControlPort 9051 @@ -259,77 +236,78 @@ CookieAuthentication 1 CookieAuthFileGroupReadable 1 ``` -Then you can use `--addr=statictor:127.0.0.1:9051` instead of -`--announce-addr=.onionAddressV3`. - -Note that you have to specify a `--bind-addr` first before using -`--addr=statictor:`. -Generally `--bind-addr=127.0.0.1:9735` should work fine. +Save the file and restart the Tor service. -You can also have multiple persistent .onion addresses -by adding `/torblob=BLOB`, where `BLOB` is 32 to 64 ***random*** -bytes of text. -Note that this blob will be used to derive the secret key behind -the .onion address and you should keep the blob secret otherwise -anyone who steals it can spoof your .onion address and block -incoming data to your node via this .onion address. -You can then specify multiple `statictor:` options with different -`BLOB`s. +##### Service authenticated by password -It is still possible for other nodes to contact you by those -other hidden services. +This tells Tor to allow password access: you also need to tell lightningd +what the password is. -Finally, the default external port number for the autogenerated -persistent .onion address will be 9735, but you can change this by -adding `/torport=9999` to change the external port for the .onion -address. +Create a hash of your password with +``` +tor --hash-password yourpassword +``` -##### Explicit Control +This returns a line like -If you want to create your own hidden address, the whole section will be: +`16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F` +Put these lines in the `/etc/tor/torrc` file: ``` -HiddenServiceDir /var/lib/tor/lightningd-service_v3/ -HiddenServiceVersion 3 -HiddenServicePort 1234 127.0.0.1:9735 +ControlPort 9051 +HashedControlPassword 16:533E3963988E038560A8C4EE6BBEE8DB106B38F9C8A7F81FE38D2A3B1F ``` -The hidden lightning service will be reachable at port 1234 (global port) -of the .onion address, which will be created at the restart of the -Tor service. Both types of addresses can coexist on the same node. +Save the file and restart the Tor service. -Save the file and restart the Tor service. In linux: +Put `tor-service-password=yourpassword` (not the hash) in your +lightning configuration file. -`/etc/init.d/tor restart` or `sudo systemctl start tor` depending -on the configuration of your system. +##### C-Lightning Creating Persistent Hidden Addresses -You will find the newly created address with: -``` -sudo cat /var/lib/tor/lightningd-service_v3/hostname -``` +This is usually better than transient addresses, as nodes won't have +to wait for gossip propagation to find out your new address each time +you restart. + +Once you've configured access to Tor as described above, you need +to add *two* lines in your lightningd config file: + +1. A local address which lightningd can tell Tor to connect to when + connections come in, e.g. `bind-addr=127.0.0.1:9735`. +2. After that, a `addr=statictor:127.0.0.1:9051` to tell + c-lightning to set up and announce a Tor onion address (and tell + Tor to send connections to our real address, above). -Now you are able to create: +You can use `bind-addr` if you want to set up the onion address and +not announce it to the world for some reason. -* Persistent version 3 hidden services. +You may add more `addr` lines if you want to advertize other +addresses. + +There is an older method, called "autotor" instead of "statictor" +which creates a different Tor address on each restart, which is +usually not very helpful; you need to use `lightning-cli getinfo` to +see what address it is currently using, and other peers need to wait +for fresh gossip messages if you announce it, before they can connect. -Let's see how to use them. ### What do we support | Case # | IP Number | Hidden service |Incoming / Outgoing Tor | | ------- | ------------- | ------------------------- |------------------------- | 1 | Public | NO | Outgoing | -| 6 | Public | v3 | Incoming [1] | -| 7 | Not Announced | v3 | Incoming | -| 8 | Public | NO | Outcoing socks5 . | +| 2 | Public | FIXED BY TOR | Incoming [1] | +| 3 | Public | FIXED BY C-LIGHTNING | Incoming [1] | +| 4 | Not Announced | FIXED BY TOR | Incoming [1] | +| 5 | Not Announced | FIXED BY C-LIGHTNING | Incoming [1] | + NOTE: 1. In all the "Incoming" use case, the node can also make "Outgoing" Tor -connections (connect to a .onion address) by adding the -`--proxy=127.0.0.1:9050` option. +connections (connect to a .onion address) by adding the `proxy=127.0.0.1:9050` option. -#### Case #1 c-lightning has a public IP address and no Tor hidden service address, but can connect to an onion address via a Tor socks 5 proxy. +#### Case #1: Public IP address and no Tor address, but can connect to Tor addresses Without a .onion address, the node won't be reachable through Tor by other nodes but it will always be able to `connect` to a Tor enabled node @@ -337,7 +315,7 @@ nodes but it will always be able to `connect` to a Tor enabled node service socks5 proxy. When the Tor service starts it creates a socks5 proxy which is by default at the address 127.0.0.1:9050. -If the node is started with the option `--proxy=127.0.0.1:9050` the node +If the node is started with the option `proxy=127.0.0.1:9050` the node will be always able to connect to nodes with .onion address through the socks5 proxy. @@ -345,18 +323,22 @@ proxy. Tor capabilities.** If you want to `connect` to nodes ONLY via the Tor proxy, you have to add the -`--always-use-proxy=true` option. +`always-use-proxy=true` option (though if you only advertize Tor addresses, +we also assume you want to always use the proxy). -You can announce your public IP address through the usual method: +You can announce your public IP address through the usual method: if +your node is in an internal network: ``` ---bind-addr=internalIPAddress:port --announce-addr=externalIpAddress +bind-addr=internalIPAddress:port +announce-addr=externalIpAddress ``` -if the node is into an internal network + +or if it has a public IP address: + ``` ---addr=externalIpAddress +addr=externalIpAddress ``` -if the node is not inside an internal network. TIP: If you are unsure which of the two is suitable for you, find your internal and external address and see if they match. @@ -369,142 +351,82 @@ and your internal IP Address with: `ip route get 1 | awk '{print $NF;exit}'` If they match you can use the `--addr` command line option. -#### Case #2 c-lightning has a public IP address and a fixed Tor hidden service address that is persistent, so that external users can connect to this node. - -To have your external IP address and your .onion address announced, you use the -``` ---bind-addr=yourInternalIPAddress:port --announce-addr=yourexternalIPAddress:port --announce-addr=your.onionAddress:port` -``` -or -``` ---bind-addr=yourInternalIPAddress:port --announce-addr=yourexternalIPAddress:port --addr=statictor:127.0.0.1:9051` -``` -options. +#### Case #2: Public IP address, and a fixed Tor address in torrc -If you are not inside an internal network you can use -``` ---addr=yourIPAddress:port --announce-addr=your.onionAddress:port -``` -or -``` ---addr=yourIPAddress:port --addr=statictor:127.0.0.1:9051 -``` +Other nodes can connect to you entirely over Tor, and the Tor address +doesn't change every time you restart. -your.onionAddress is the one created with the Tor hidden service ([see above](#creation-of-an-hidden-service-for-a-persistent-onion-address)). -The port is the one indicated as the hidden service port. If the hidden service creation -line is `HiddenServicePort 1234 127.0.0.1:9735` the .onion address will be reachable at -the 1234 port (the global port). +You simply tell c-lightning to advertize both addresses (you can use +`sudo cat /var/lib/tor/lightningd-service_v3/hostname` to get your +Tor-assigned onion address). -For `statictor` the `127.0.0.1` is your computer, and `9051` is the -Tor Control Port you set up in the `/etc/tor/torrc` file. +If you have an internal IP address: -It will be possible to connect to this node with: -``` -lightning-cli connect nodeID .onionAddress globalPort -``` -through Tor where .onion address is in the form `xxxxxxxxxxxxxxxxxxxxxxxxxx.onion`, Or ``` -lightning-cli connect nodeID yourexternalIPAddress Port +bind-addr=yourInternalIPAddress:port +announce-addr=yourexternalIPAddress:port +announce-addr=your.onionAddress:port ``` -through Clearnet. -#### Case #3 c-lightning has a public IP address and a non-persistent Tor service address - -In this case other nodes can connect to you via Clearnet or Tor. - -To announce your IP address to the network, you add: +Or an external address: ``` ---bind-addr=internalAddress:port --announce-addr=yourExternalIPAddress +addr=yourIPAddress:port +announce-addr=your.onionAddress:port ``` -or `--addr=yourExternalIPAddress`if you are NOT on an internal network. - -To get your non-persistent Tor address, add -`--addr=autotor:127.0.0.1:9051` if you want to announce it or -`--bind-addr=autotor:127.0.0.1:9051` if you don't want to announce it. -If the auto service is protected by password ([see above](#service-authenticated-by-password)) it is necessary to -specify it with the option `--tor-service-password=yourpassword` (not the hash). +#### Case #3: Public IP address, and a fixed Tor address set by C-lightning -You will obtain the generated non persisten .onion address by reading the results of the -`lightning-cli getinfo` command. Other nodes will be able to connect to the -.onion address through the 9735 port. +Other nodes can connect to you entirely over Tor, and the Tor address +doesn't change every time you restart. -#### Case #4 c-lightning has no public IP address, but has a fixed Tor hidden service address that is persistent +See "Letting C-lightning Control Tor" for how to get c-lightning +talking to Tor. -Other nodes can connect to the announced .onion address created with the -hidden service ([see above](#creation-of-an-hidden-service-for-a-persistent-onion-address)). +If you have an internal IP address: -In this case In the `lightningd` command line you will specify: ``` ---bind-addr=yourInternalIPAddress:port --announce-addr=your.onionAddress:port +bind-addr=yourInternalIPAddress:port +announce-addr=yourexternalIPAddress:port +addr=statictor:127.0.0.1:9051 ``` -or `--addr=your.onionAddress:port` if you are NOT on an internal network. - -#### Case #5 c-lightning has no public IP address, and has no fixed Tor hidden service address -In this case it is difficult to track the node. -You specify just: +Or an external address: ``` ---bind-addr=yourInternalIPAddress:port --addr=autotor:127.0.0.1:9051 +addr=yourIPAddress:port +addr=statictor:127.0.0.1:9051 ``` -In the `lightningd` command line. -Other nodes will not be able to `connect` to you unless you communicate them how to reach you. -You will find your .onion address with the command `lightning-cli getinfo` and the other nodes will -be able to connect to it through the 9735 port. +#### Case #4: Unannounced IP address, and a fixed Tor address in torrc -#### Case #6 c-lightning has a public IP address and a fixed Tor hidden service +Other nodes can only connect to you over Tor. -You will be reachable via Clearnet, via Tor to the .onion if it is communicated to the node that wants to -connect with our node. +You simply tell c-lightning to advertize the Tor address (you can use +`sudo cat /var/lib/tor/lightningd-service_v3/hostname` to get your +Tor-assigned onion address). -To make your external IP address public you add: ``` ---bind-addr=yourInternalAddress:port --announce-addr=yourexternalIPAddress:port`. -``` -If the node is not on an internal network the option will be: -`--addr=yourexternalIPAddress:port`. - -Once the .onion addresses have been created with the procedures [oulined above](#creation-of-an-hidden-service-for-a-persistent-onion-address), -the node is already reachable at the .onion address. - -To make your external hidden service public you add: -``` ---announce-addr=.onionAddressV3:port +announce-addr=your.onionAddress:port +proxy=127.0.0.1:9050 +always-use-proxy=true ``` -to the options to publish your IP number. -#### Case #7 c-lightning has no public IP address, a fixed Tor hidden service +#### Case #4: Unannounced IP address, and a fixed Tor address set by C-lightning -The Persistent addresses can be created with the steps [outlined above](#creation-of-an-hidden-service-for-a-persistent-onion-address). +Other nodes can only connect to you over Tor. -To create your non-persistent Tor address, add -`--addr=autotor:127.0.0.1:9051` if you want to announce it or -`--bind-addr=autotor:127.0.0.1:9051` if you don't want to announce it. +See "Letting C-lightning Control Tor" for how to get c-lightning +talking to Tor. -Also you must specify `--tor-service-password=yourpassword` (not the hash) to access the -Tor service at 9051 If you have protected them with the password (no additional options if -they are protected with a cookie file. [See above](#creation-of-an-auto-service-for-non-persistent-onion-addresses)). - -To make your external onion service public you add: -``` ---bind-addr=yourInternalIPAddress:port --announce-addr=your.onionAddressV3:port -``` -#### Case #8 c-lightning has a public IP address and no Tor addresses - -The external address is communicated by the -``` ---bind-addr=internalIPAddress:port --announce-addr=yourexternalIPAddress:port` ``` -or `--addr=yourexternalIPAddress:port` if the node is not inside an internal network. - -The node can connect to any V4/6 ip address via a IPV4/6 socks 5 proxy by specifing -``` ---proxy=127.0.0.1:9050 --always-use-proxy=true +addr=statictor:127.0.0.1:9051 +proxy=127.0.0.1:9050 +always-use-proxy=true ``` ## References +The lightningd-config manual page covers the various address cases in detail. + [The Tor project](https://www.torproject.org/) [tor FAQ]: https://www.torproject.org/docs/faq.html.en#WhatIsTor From 9d18180172946b0d90f36d88897d9ea7d27ce697 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:41 +1030 Subject: [PATCH 0009/1530] lightningd: really do allow two Torv3 addresses. This surprised me, since the CHANGELOG for [0.8.2] said: We now announce multiple addresses of the same type, if given. ([3609](https://github.com/ElementsProject/lightning/pull/3609)) But it lied! Changelog-Fixed: We really do allow providing multiple addresses of the same type. Signed-off-by: Rusty Russell --- common/test/run-wireaddr.c | 74 ++++++++++++++------------------------ common/wireaddr.c | 31 ++++++++++++++++ common/wireaddr.h | 3 ++ lightningd/options.c | 31 +++++++--------- tests/test_gossip.py | 6 ++++ 5 files changed, 78 insertions(+), 67 deletions(-) diff --git a/common/test/run-wireaddr.c b/common/test/run-wireaddr.c index 3602ae2c7057..c6e19d8b752b 100644 --- a/common/test/run-wireaddr.c +++ b/common/test/run-wireaddr.c @@ -112,39 +112,6 @@ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNE { fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ -static bool wireaddr_internal_eq(const struct wireaddr_internal *a, - const struct wireaddr_internal *b, - bool cmp_torservice_blob) -{ - if (a->itype != b->itype) - return false; - - switch (a->itype) { - case ADDR_INTERNAL_SOCKNAME: - return streq(a->u.sockname, b->u.sockname); - case ADDR_INTERNAL_ALLPROTO: - return a->u.port == b->u.port; - case ADDR_INTERNAL_AUTOTOR: - case ADDR_INTERNAL_STATICTOR: - if (!wireaddr_eq(&a->u.torservice.address, - &b->u.torservice.address)) - return false; - if (a->u.torservice.port != b->u.torservice.port) - return false; - if (!cmp_torservice_blob) - return true; - return memeq(a->u.torservice.blob, sizeof(a->u.torservice.blob), - b->u.torservice.blob, sizeof(b->u.torservice.blob)); - case ADDR_INTERNAL_FORPROXY: - if (!streq(a->u.unresolved.name, b->u.unresolved.name)) - return false; - return a->u.unresolved.port == b->u.unresolved.port; - case ADDR_INTERNAL_WIREADDR: - return wireaddr_eq(&a->u.wireaddr, &b->u.wireaddr); - } - abort(); -} - int main(int argc, char *argv[]) { const char *err; @@ -155,87 +122,98 @@ int main(int argc, char *argv[]) assert(parse_wireaddr_internal("127.0.0.1", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("127.0.0.1:9735", &expect->u.wireaddr, 0, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* IPv4 address with port. */ assert(parse_wireaddr_internal("127.0.0.1:1", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("127.0.0.1:1", &expect->u.wireaddr, 0, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* Simple IPv6 address. */ assert(parse_wireaddr_internal("::1", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("::1", &expect->u.wireaddr, DEFAULT_PORT, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* IPv6 address with port. */ assert(parse_wireaddr_internal("[::1]:1", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("::1", &expect->u.wireaddr, 1, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* autotor address */ assert(parse_wireaddr_internal("autotor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = DEFAULT_PORT; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with port */ assert(parse_wireaddr_internal("autotor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = DEFAULT_PORT; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with torport */ assert(parse_wireaddr_internal("autotor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = 9055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with port and torport */ assert(parse_wireaddr_internal("autotor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = 10055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* statictor address */ assert(parse_wireaddr_internal("statictor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = DEFAULT_PORT; + memset(expect->u.torservice.blob, 0, sizeof(expect->u.torservice.blob)); + strcpy((char *)expect->u.torservice.blob, STATIC_TOR_MAGIC_STRING); assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port */ assert(parse_wireaddr_internal("statictor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = DEFAULT_PORT; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with torport */ assert(parse_wireaddr_internal("statictor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 9055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port and torport */ assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 10055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); + + /* statictor address with port and torport and torblob */ + assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055/torblob=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", &addr, DEFAULT_PORT, false, false, false, false, &err)); + expect->itype = ADDR_INTERNAL_STATICTOR; + expect->u.torservice.port = 10055; + /* This is actually nul terminated */ + memset(expect->u.torservice.blob, 'x', sizeof(expect->u.torservice.blob)-1); + assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); + assert(wireaddr_internal_eq(&addr, expect)); /* local socket path */ assert(parse_wireaddr_internal("/tmp/foo.sock", &addr, DEFAULT_PORT, false, false, false, false, &err)); expect->itype = ADDR_INTERNAL_SOCKNAME; strcpy(expect->u.sockname, "/tmp/foo.sock"); - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* Unresolved */ assert(!parse_wireaddr_internal("ozlabs.org", &addr, DEFAULT_PORT, false, false, false, false, &err)); @@ -244,7 +222,7 @@ int main(int argc, char *argv[]) expect->itype = ADDR_INTERNAL_FORPROXY; strcpy(expect->u.unresolved.name, "ozlabs.org"); expect->u.unresolved.port = DEFAULT_PORT; - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); /* Unresolved with port */ assert(!parse_wireaddr_internal("ozlabs.org:1234", &addr, DEFAULT_PORT, false, false, false, false, &err)); @@ -253,7 +231,7 @@ int main(int argc, char *argv[]) expect->itype = ADDR_INTERNAL_FORPROXY; strcpy(expect->u.unresolved.name, "ozlabs.org"); expect->u.unresolved.port = 1234; - assert(wireaddr_internal_eq(&addr, expect, false)); + assert(wireaddr_internal_eq(&addr, expect)); tal_free(expect); common_shutdown(); diff --git a/common/wireaddr.c b/common/wireaddr.c index 08b0683fb738..4f4deac832e8 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -454,6 +454,37 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 defport, return res; } +bool wireaddr_internal_eq(const struct wireaddr_internal *a, + const struct wireaddr_internal *b) +{ + if (a->itype != b->itype) + return false; + + switch (a->itype) { + case ADDR_INTERNAL_SOCKNAME: + return streq(a->u.sockname, b->u.sockname); + case ADDR_INTERNAL_ALLPROTO: + return a->u.port == b->u.port; + case ADDR_INTERNAL_STATICTOR: + if (!memeq(a->u.torservice.blob, sizeof(a->u.torservice.blob), + b->u.torservice.blob, sizeof(b->u.torservice.blob))) + return false; + /* fall thru */ + case ADDR_INTERNAL_AUTOTOR: + if (!wireaddr_eq(&a->u.torservice.address, + &b->u.torservice.address)) + return false; + return a->u.torservice.port == b->u.torservice.port; + case ADDR_INTERNAL_FORPROXY: + if (!streq(a->u.unresolved.name, b->u.unresolved.name)) + return false; + return a->u.unresolved.port == b->u.unresolved.port; + case ADDR_INTERNAL_WIREADDR: + return wireaddr_eq(&a->u.wireaddr, &b->u.wireaddr); + } + abort(); +} + bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, bool wildcard_ok, bool dns_ok, bool unresolved_ok, bool allow_deprecated, diff --git a/common/wireaddr.h b/common/wireaddr.h index b484be47feb4..cf16914d7098 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -146,6 +146,9 @@ struct wireaddr_internal { char sockname[sizeof(((struct sockaddr_un *)0)->sun_path)]; } u; }; + +bool wireaddr_internal_eq(const struct wireaddr_internal *a, + const struct wireaddr_internal *b); bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, bool wildcard_ok, bool dns_ok, bool unresolved_ok, bool allow_deprecated, diff --git a/lightningd/options.c b/lightningd/options.c index 152575254f87..b666a26ee17b 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -198,6 +198,18 @@ static char *opt_add_addr_withtype(const char *arg, deprecated_apis, &err_msg)) { return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } + + /* Sanity check for exact duplicates. */ + for (size_t i = 0; i < tal_count(ld->proposed_wireaddr); i++) { + /* Only compare announce vs announce and bind vs bind */ + if ((ld->proposed_listen_announce[i] & ala) == 0) + continue; + + if (wireaddr_internal_eq(&ld->proposed_wireaddr[i], &wi)) + return tal_fmt(NULL, "Duplicate %s address %s", + ala & ADDR_ANNOUNCE ? "announce" : "listen", + type_to_string(tmpctx, struct wireaddr_internal, &wi)); + } tal_arr_expand(&ld->proposed_wireaddr, wi); return NULL; @@ -205,7 +217,6 @@ static char *opt_add_addr_withtype(const char *arg, static char *opt_add_announce_addr(const char *arg, struct lightningd *ld) { - const struct wireaddr *wn; size_t n = tal_count(ld->proposed_wireaddr); char *err; @@ -226,24 +237,6 @@ static char *opt_add_announce_addr(const char *arg, struct lightningd *ld) return tal_fmt(NULL, "address '%s' is not announcable", arg); - /* gossipd will refuse to announce the second one, sure, but it's - * better to check and fail now if they've explicitly asked for it. */ - wn = &ld->proposed_wireaddr[n].u.wireaddr; - for (size_t i = 0; i < n; i++) { - const struct wireaddr *wi; - - if (ld->proposed_listen_announce[i] != ADDR_ANNOUNCE) - continue; - assert(ld->proposed_wireaddr[i].itype == ADDR_INTERNAL_WIREADDR); - wi = &ld->proposed_wireaddr[i].u.wireaddr; - - if (wn->type != wi->type) - continue; - return tal_fmt(NULL, "Cannot announce address %s;" - " already have %s which is the same type", - type_to_string(tmpctx, struct wireaddr, wn), - type_to_string(tmpctx, struct wireaddr, wi)); - } return NULL; } diff --git a/tests/test_gossip.py b/tests/test_gossip.py index b45e430311b9..648606faaf87 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -916,8 +916,11 @@ def test_gossip_addresses(node_factory, bitcoind): l1 = node_factory.get_node(options={ 'announce-addr': [ '[::]:3', + '[::]', '127.0.0.1:2', + '127.0.0.1', 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', + '4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion:1234' ], }) l2 = node_factory.get_node() @@ -931,8 +934,11 @@ def test_gossip_addresses(node_factory, bitcoind): nodes = l2.rpc.listnodes(l1.info['id'])['nodes'] assert len(nodes) == 1 and nodes[0]['addresses'] == [ {'type': 'ipv4', 'address': '127.0.0.1', 'port': 2}, + {'type': 'ipv4', 'address': '127.0.0.1', 'port': 9735}, {'type': 'ipv6', 'address': '::', 'port': 3}, + {'type': 'ipv6', 'address': '::', 'port': 9735}, {'type': 'torv3', 'address': 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', 'port': 9735}, + {'type': 'torv3', 'address': '4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion', 'port': 1234}, ] From b2c762969cb0de5e6c45ef902ce1e27db64d8d16 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:42 +1030 Subject: [PATCH 0010/1530] wireaddr: clean up tor parsing. blob[] is really a string from the commandline; leave it as a char. And parsing is much simpler than this code makes it seem! Signed-off-by: Rusty Russell --- common/base64.c | 2 +- common/base64.h | 2 +- common/wireaddr.c | 64 ++++++++++++++++---------------------- common/wireaddr.h | 4 +-- connectd/connectd.c | 2 +- connectd/tor_autoservice.c | 4 +-- connectd/tor_autoservice.h | 2 +- 7 files changed, 34 insertions(+), 46 deletions(-) diff --git a/common/base64.c b/common/base64.c index f7e7f7ef5a7b..be8e08a10f36 100644 --- a/common/base64.c +++ b/common/base64.c @@ -5,7 +5,7 @@ * We import base64 from libsodium to generate tor V3 ED25519-V3 onions from blobs */ -char *b64_encode(const tal_t *ctx, const u8 *data, size_t len) +char *b64_encode(const tal_t *ctx, const void *data, size_t len) { char *str = tal_arr(ctx, char, sodium_base64_encoded_len(len, sodium_base64_VARIANT_ORIGINAL) + 1); diff --git a/common/base64.h b/common/base64.h index dee4510cd37c..f963541dba17 100644 --- a/common/base64.h +++ b/common/base64.h @@ -4,6 +4,6 @@ #include #include -char *b64_encode(const tal_t *ctx, const u8 *data, size_t len); +char *b64_encode(const tal_t *ctx, const void *data, size_t len); #endif /* LIGHTNING_COMMON_BASE64_H */ diff --git a/common/wireaddr.c b/common/wireaddr.c index 4f4deac832e8..6638f2e6e72d 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -520,21 +520,18 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, char **parts = tal_strsplit(tmpctx, arg, "/", STR_EMPTY_OK); for (size_t i = 1; i < tal_count(parts)-1; i++) { - if (tal_strreg(tmpctx, parts[i], "torport")) { + if (strstarts(parts[i], "torport=")) { char *endp = NULL; - char **parts_2 = tal_strsplit(tmpctx, parts[i], "=", STR_EMPTY_OK); - if (tal_count(parts_2) == 3) { - addr->u.torservice.port = strtol((const char *)parts_2[1], &endp, 10); - if (addr->u.torservice.port <= 0 || *endp != '\0') { - if (err_msg) - *err_msg = "Bad :torport: number"; - return false; - } - } else { + addr->u.torservice.port = strtol(parts[i]+strlen("torport="), &endp, 10); + if (addr->u.torservice.port <= 0 || *endp != '\0') { if (err_msg) - *err_msg = "Bad :torport: format"; + *err_msg = "Bad :torport: number"; return false; } + } else { + if (err_msg) + *err_msg = tal_fmt(tmpctx, "unknown tor arg %s", parts[i]); + return false; } } @@ -551,47 +548,38 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, bool use_magic_blob = true; addr->itype = ADDR_INTERNAL_STATICTOR; addr->u.torservice.port = DEFAULT_PORT; - memset(&(addr->u.torservice.blob[0]), 0, sizeof(addr->u.torservice.blob)); + memset(addr->u.torservice.blob, 0, sizeof(addr->u.torservice.blob)); /* Format is separated by slash. */ char **parts = tal_strsplit(tmpctx, arg, "/", STR_EMPTY_OK); for (size_t i = 1; i < tal_count(parts)-1; i++) { - if (tal_strreg(tmpctx, parts[i], "torport")) { + if (strstarts(parts[i], "torport=")) { char *endp = NULL; - char **parts_eq = tal_strsplit(tmpctx, parts[i], "=", STR_EMPTY_OK); - if (tal_count(parts_eq) == 3) { - addr->u.torservice.port = strtol((const char *)parts_eq[1], &endp, 10); - if (addr->u.torservice.port <= 0 || *endp != '\0') { - if (err_msg) - *err_msg = "Bad :torport: number"; - return false; - } - } else { + addr->u.torservice.port = strtol(parts[i]+strlen("torport="), &endp, 10); + if (addr->u.torservice.port <= 0 || *endp != '\0') { if (err_msg) - *err_msg = "Bad :torport: format"; + *err_msg = "Bad :torport: number"; return false; } - } - if (tal_strreg(tmpctx, parts[i], "torblob")) { - char **parts_eq = tal_strsplit(tmpctx, parts[i], "=", STR_EMPTY_OK); - if (tal_count(parts_eq) == 3) { - if (strlen((char *)parts_eq[1]) == 0) { - if (err_msg) - *err_msg = "Blob too short"; - return false; - } - strncpy((char *)&(addr->u.torservice.blob[0]), - (const char *)parts_eq[1], TOR_V3_BLOBLEN); - use_magic_blob = false; + } else if (strstarts(parts[i], "torblob=")) { + const char *blobdata = parts[i] + strlen("torblob="); + if (strlen(blobdata) > TOR_V3_BLOBLEN) { + if (err_msg) + *err_msg = "torblob too long"; + return false; } + strcpy(addr->u.torservice.blob, blobdata); + use_magic_blob = false; + } else { + if (err_msg) + *err_msg = tal_fmt(tmpctx, "unknown tor arg %s", parts[i]); + return false; } } if (use_magic_blob) { /* when statictor called just with the service address and or port generate the unique onion */ - strncpy((char *)&(addr->u.torservice.blob[0]), - tal_fmt(tmpctx, STATIC_TOR_MAGIC_STRING), - strlen(STATIC_TOR_MAGIC_STRING)); + strcpy(addr->u.torservice.blob, STATIC_TOR_MAGIC_STRING); } service_addr = tal_fmt(tmpctx, "%s", parts[0] + strlen("statictor:")); diff --git a/common/wireaddr.h b/common/wireaddr.h index cf16914d7098..3f9cddc6249d 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -134,8 +134,8 @@ struct wireaddr_internal { struct wireaddr address; /* Tor port to use */ u16 port; - /* Blob to use to create tor service */ - u8 blob[TOR_V3_BLOBLEN + 1]; + /* Nul-terminated blob to use to create tor service */ + char blob[TOR_V3_BLOBLEN + 1]; } torservice; /* ADDR_INTERNAL_FORPROXY */ struct unresolved { diff --git a/connectd/connectd.c b/connectd/connectd.c index 4cd25fbc4837..1150f6ee7c5e 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1232,7 +1232,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, struct sockaddr_un addrun; int fd; struct wireaddr_internal *binding; - const u8 *blob = NULL; + const char *blob = NULL; struct secret random; struct pubkey pb; struct wireaddr *toraddr; diff --git a/connectd/tor_autoservice.c b/connectd/tor_autoservice.c index 7ede2efb0f12..e471231f5324 100644 --- a/connectd/tor_autoservice.c +++ b/connectd/tor_autoservice.c @@ -146,7 +146,7 @@ static struct wireaddr *make_onion(const tal_t *ctx, static struct wireaddr *make_fixed_onion(const tal_t *ctx, struct rbuf *rbuf, - const struct wireaddr *local, const u8 *blob, u16 port) + const struct wireaddr *local, const char *blob, u16 port) { char *line; struct wireaddr *onion; @@ -323,7 +323,7 @@ struct wireaddr *tor_autoservice(const tal_t *ctx, struct wireaddr *tor_fixed_service(const tal_t *ctx, const struct wireaddr_internal *tor_serviceaddr, const char *tor_password, - const u8 *blob, + const char *blob, const struct wireaddr *bind, const u8 index) { diff --git a/connectd/tor_autoservice.h b/connectd/tor_autoservice.h index 6906a40be967..e88731ac9de4 100644 --- a/connectd/tor_autoservice.h +++ b/connectd/tor_autoservice.h @@ -15,7 +15,7 @@ struct wireaddr *tor_autoservice(const tal_t *ctx, struct wireaddr *tor_fixed_service(const tal_t *ctx, const struct wireaddr_internal *tor_serviceaddr, const char *tor_password, - const u8 *blob, + const char *blob, const struct wireaddr *bind, const u8 index); From ff276e958b0af62d263e7849f440f0b8465b1a43 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:42 +1030 Subject: [PATCH 0011/1530] common: test that our sodium-based base64 encoding is identitcal to ccan's. Signed-off-by: Rusty Russell --- common/test/run-base64.c | 134 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 common/test/run-base64.c diff --git a/common/test/run-base64.c b/common/test/run-base64.c new file mode 100644 index 000000000000..899e83b6b48e --- /dev/null +++ b/common/test/run-base64.c @@ -0,0 +1,134 @@ +#include "../base64.c" +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +int main(int argc, char *argv[]) +{ + u8 data[1024]; + + common_setup(argv[0]); + + for (size_t i = 0; i < sizeof(data); i++) { + char *sodium, *ours, *ccan; + memset(data, i, i); + + ours = b64_encode(tmpctx, data, i); + ccan = tal_arr(tmpctx, char, base64_encoded_length(i) + 1); + ccan[base64_encode(ccan, base64_encoded_length(i), (char *)data, i)] = '\0'; + sodium = tal_arr(tmpctx, char, sodium_base64_encoded_len(i, sodium_base64_VARIANT_ORIGINAL) + 1); + sodium = sodium_bin2base64(sodium, tal_count(sodium), data, i, sodium_base64_VARIANT_ORIGINAL); + + assert(streq(ccan, sodium)); + assert(streq(ccan, ours)); + } + common_shutdown(); +} From 35c7f56c7372a143f7ffb0a1b27b3e44c1c5bb61 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Nov 2021 10:57:42 +1030 Subject: [PATCH 0012/1530] common: use ccan/base64 instead of libsodium. For consistency. Signed-off-by: Rusty Russell --- common/base64.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/common/base64.c b/common/base64.c index be8e08a10f36..75db1a4698f3 100644 --- a/common/base64.c +++ b/common/base64.c @@ -1,15 +1,16 @@ +#include "config.h" +#include +#include #include -#include - -/* Decode/encode from/to base64, base64 helper functions. - * We import base64 from libsodium to generate tor V3 ED25519-V3 onions from blobs -*/ +/* Decode/encode from/to base64, base64 helper functions.*/ char *b64_encode(const tal_t *ctx, const void *data, size_t len) { - char *str = tal_arr(ctx, char, sodium_base64_encoded_len(len, sodium_base64_VARIANT_ORIGINAL) + 1); + size_t slen = base64_encoded_length(len), enclen; + char *str = tal_arr(ctx, char, slen + 1); + enclen = base64_encode(str, slen, data, len); + assert(enclen == slen); - str = sodium_bin2base64(str, tal_count(str), data, - len, sodium_base64_VARIANT_ORIGINAL); + str[enclen] = '\0'; return str; } From 09bbda4bca7c45b1dce201957cbe09f4642b532a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 12 Nov 2021 14:27:16 +1030 Subject: [PATCH 0013/1530] pytest: don't create 5 nodes in test_fetchinvoice. CI seems to be OOM killing us; 5 may be too many under valgrind. VALGRIND=1 pytest tests/test_pay.py::test_fetchinvoice Before: 1 passed in 199.33s (0:03:19) After: 1 passed in 177.91s (0:02:57) Signed-off-by: Rusty Russell --- tests/test_pay.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/test_pay.py b/tests/test_pay.py index e7a0145b6dc6..709978ae82ca 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4499,7 +4499,7 @@ def test_fetchinvoice(node_factory, bitcoind): 'recurrence_label': 'test recurrence'}) # Check we can request invoice without a channel. - l4, l5 = node_factory.get_nodes(2, opts={'experimental-offers': None}) + l4 = node_factory.get_node(options={'experimental-offers': None}) l4.rpc.connect(l2.info['id'], 'localhost', l2.port) # ... even if we can't find ourselves. l4.rpc.call('fetchinvoice', {'offer': offer3['bolt12'], @@ -4510,10 +4510,6 @@ def test_fetchinvoice(node_factory, bitcoind): l4.rpc.connect(l3.info['id'], 'localhost', l3.port) l4.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - # ... even if we are also in gossmap. - node_factory.join_nodes([l3, l5], wait_for_announce=True) - l4.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - # Now, test amount in different currency! plugin = os.path.join(os.path.dirname(__file__), 'plugins/currencyUSDAUD5000.py') l3.rpc.plugin_start(plugin) From 65bb989cf179e8c8593cb315cb26a6c508d20ac3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 12 Nov 2021 17:14:46 +1030 Subject: [PATCH 0014/1530] pytest: don't checksum plugins on startup in VALGRIND developer mode. This loads up 20MB of plugins temporarily; we seem to be getting OOM killed under CI and I wonder if this is contributing. Doesn't significantly reduce runtime here, but I have lots of memory. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 1 + lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 +++ lightningd/options.c | 4 ++++ lightningd/plugin.c | 13 +++++++++---- tests/test_plugin.py | 3 +++ 6 files changed, 21 insertions(+), 4 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index f986e4ffcdae..dfc2fd666017 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -675,6 +675,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai self.daemon.opts["dev-debugger"] = os.getenv("DEBUG_SUBD") if valgrind: self.daemon.env["LIGHTNINGD_DEV_NO_BACKTRACE"] = "1" + self.daemon.opts["dev-no-plugin-checksum"] = None else: # Under valgrind, scanning can access uninitialized mem. self.daemon.env["LIGHTNINGD_DEV_MEMLEAK"] = "1" diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 1af81a79032b..b20520d4ae7a 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -114,6 +114,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * same exact code users will be running. */ #if DEVELOPER ld->dev_debug_subprocess = NULL; + ld->dev_no_plugin_checksum = false; ld->dev_disconnect_fd = -1; ld->dev_subdaemon_fail = false; ld->dev_allow_localhost = false; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 553f10a785b4..772de83923b3 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -207,6 +207,9 @@ struct lightningd { /* If we want to debug a subdaemon/plugin. */ const char *dev_debug_subprocess; + /* If we have --dev-no-plugin-checksum */ + bool dev_no_plugin_checksum; + /* If we have a --dev-disconnect file */ int dev_disconnect_fd; diff --git a/lightningd/options.c b/lightningd/options.c index b666a26ee17b..54dde2aa640d 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -584,6 +584,10 @@ static void dev_register_opts(struct lightningd *ld) opt_register_early_arg("--dev-debugger=", opt_subprocess_debug, NULL, ld, "Invoke gdb at start of "); + opt_register_early_noarg("--dev-no-plugin-checksum", opt_set_bool, + &ld->dev_no_plugin_checksum, + "Don't checksum plugins to detect changes"); + opt_register_noarg("--dev-no-reconnect", opt_set_invbool, &ld->reconnect, "Disable automatic reconnect-attempts by this node, but accept incoming"); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 39e6e480c939..ee3c2c967a78 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -209,9 +209,14 @@ static void destroy_plugin(struct plugin *p) } } -static u32 file_checksum(const char* path) +static u32 file_checksum(struct lightningd *ld, const char *path) { - char *content = grab_file(tmpctx, path); + char *content; + + if (IFDEV(ld->dev_no_plugin_checksum, false)) + return 0; + + content = grab_file(tmpctx, path); if (content == NULL) return 0; return crc32c(0, content, tal_count(content)); } @@ -231,7 +236,7 @@ struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES, if (important) p_temp->important = true; /* stop and restart plugin on different checksum */ - chksum = file_checksum(path); + chksum = file_checksum(plugins->ld, path); if (p_temp->checksum != chksum && !p_temp->important) { plugin_kill(p_temp, LOG_INFORM, "Plugin changed, needs restart."); @@ -246,7 +251,7 @@ struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES, p = tal(plugins, struct plugin); p->plugins = plugins; p->cmd = tal_strdup(p, path); - p->checksum = file_checksum(p->cmd); + p->checksum = file_checksum(plugins->ld, p->cmd); p->shortname = path_basename(p, p->cmd); p->start_cmd = start_cmd; diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b590ba680cdb..c8eeff43486b 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2520,6 +2520,9 @@ def init(options, configuration, plugin): # get a node that is not started so we can put a plugin in its lightning_dir n = node_factory.get_node(start=False) + if "dev-no-plugin-checksum" in n.daemon.opts: + del n.daemon.opts["dev-no-plugin-checksum"] + lndir = n.daemon.lightning_dir # write hello world plugin to lndir/plugins From ee1749bb5ea5673940de3528132bfad2fc6e2f38 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Fri, 5 Nov 2021 13:16:35 -0400 Subject: [PATCH 0015/1530] gitignore: ignore eggs and pyln versions --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index a1295d37823a..e532534c0bde 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,8 @@ contrib/pylightning/pylightning.egg-info/ contrib/pyln-*/build/ contrib/pyln-*/dist/ contrib/pyln-*/pyln_*.egg-info/ +contrib/pyln-*/.eggs/ +contrib/pyln-*/pyln/*/__version__.py release/ tests/plugins/test_selfdisable_after_getmanifest .DS_Store From 3210213dc3a099ce1f5e864505b4a866a731bec9 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Sun, 7 Nov 2021 08:26:59 -0500 Subject: [PATCH 0016/1530] doc: add missing dev deps, upgrade and don't use sudo for pip --- doc/INSTALL.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 0b078b10cb58..26d570585c6b 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -37,9 +37,10 @@ Get dependencies: sudo apt-get update sudo apt-get install -y \ - autoconf automake build-essential git libtool libgmp-dev \ - libsqlite3-dev python3 python3-mako net-tools zlib1g-dev libsodium-dev \ + autoconf automake build-essential git libtool libgmp-dev libsqlite3-dev \ + python3 python3-mako python3-pip net-tools zlib1g-dev libsodium-dev \ gettext + pip3 install --user mrkd If you don't have Bitcoin installed locally you'll need to install that as well. It's now available via [snapd](https://snapcraft.io/bitcoin-core). @@ -57,8 +58,10 @@ Clone lightning: For development or running tests, get additional dependencies: - sudo apt-get install -y valgrind python3-pip libpq-dev - sudo pip3 install -r requirements.txt --use-feature=in-tree-build + sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ + libsecp256k1-dev + pip3 install --upgrade pip + pip3 install --user -r requirements.txt Build lightning: From 1f951a558816e286fa37023a3252e3b34905e6ec Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Sun, 7 Nov 2021 11:36:24 -0500 Subject: [PATCH 0017/1530] doc: update HACKING.md build dep instructions --- doc/HACKING.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/HACKING.md b/doc/HACKING.md index 90961e0b2a50..9d3237864679 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -163,12 +163,10 @@ Build and Development Install `valgrind` and the python dependencies for best results: ``` -sudo apt install valgrind cppcheck shellcheck libsecp256k1-dev -pip3 install --user \ - -r requirements.txt \ - -r contrib/pyln-client/requirements.txt \ - -r contrib/pyln-proto/requirements.txt \ - -r contrib/pyln-testing/requirements.txt +sudo apt update +sudo apt install valgrind cppcheck shellcheck libsecp256k1-dev libpq-dev +pip3 install --upgrade pip +pip3 install --user -r requirements.txt ``` Re-run `configure` for the python dependencies and build using `make`. From cd3643617910b822c2b161078a1e3eae2ac21a7c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Nov 2021 06:30:05 +1030 Subject: [PATCH 0018/1530] paystatus: remove doubled amount_msat. It's always been there: ``` { "pay": [ { "bolt11": "...", "amount_msat": "1000msat", "amount_msat": "1000msat", "destination": "03...", "attempts": [ { "strategy": "Initial attempt", "start_time": "2021-11-06T04:20:20.135Z", "age_in_seconds": 229032, "end_time": "2021-11-06T04:20:27.792Z", "state": "completed", "success": { "id": 31994, "payment_preimage": "..." } } ] } ] } ``` Reported-by: denis2342 on IRC Changelog-Fixed: JSON-RPC: `paystatus` entries no longer have two identical `amount_msat` entries. Signed-off-by: Rusty Russell --- plugins/pay.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 49dd4375bc40..bcb9e89fe79e 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1598,9 +1598,6 @@ static struct command_result *json_paystatus(struct command *cmd, if (p->invstring) json_add_invstring(ret, p->invstring); json_add_amount_msat_only(ret, "amount_msat", p->amount); - json_add_string( - ret, "amount_msat", - type_to_string(tmpctx, struct amount_msat, &p->amount)); json_add_node_id(ret, "destination", p->destination); From 6409608696dbb53a8a6e3c0ef0f11d71c7fbf5ed Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Mon, 8 Nov 2021 16:10:28 +0000 Subject: [PATCH 0019/1530] pyln-bolt7: fix requirements.txt --- contrib/pyln-spec/bolt7/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-spec/bolt7/requirements.txt b/contrib/pyln-spec/bolt7/requirements.txt index 8fb143e3fd7d..16f3090944f2 100644 --- a/contrib/pyln-spec/bolt7/requirements.txt +++ b/contrib/pyln-spec/bolt7/requirements.txt @@ -1 +1 @@ -pyln.proto +pyln-proto From 67f9a8e4d6c391039419c065b2521283583ced0d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 11 Nov 2021 14:44:08 +1030 Subject: [PATCH 0020/1530] contrib: fix other pyln-spec package requirements too. Signed-off-by: Rusty Russell --- contrib/pyln-spec/bolt1/requirements.txt | 2 +- contrib/pyln-spec/bolt2/requirements.txt | 2 +- contrib/pyln-spec/bolt4/requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-spec/bolt1/requirements.txt b/contrib/pyln-spec/bolt1/requirements.txt index a939f32cbd37..c863b024f737 100644 --- a/contrib/pyln-spec/bolt1/requirements.txt +++ b/contrib/pyln-spec/bolt1/requirements.txt @@ -1 +1 @@ -pyln.proto >= 0.9.3 +pyln-proto >= 0.9.3 diff --git a/contrib/pyln-spec/bolt2/requirements.txt b/contrib/pyln-spec/bolt2/requirements.txt index a939f32cbd37..c863b024f737 100644 --- a/contrib/pyln-spec/bolt2/requirements.txt +++ b/contrib/pyln-spec/bolt2/requirements.txt @@ -1 +1 @@ -pyln.proto >= 0.9.3 +pyln-proto >= 0.9.3 diff --git a/contrib/pyln-spec/bolt4/requirements.txt b/contrib/pyln-spec/bolt4/requirements.txt index a939f32cbd37..c863b024f737 100644 --- a/contrib/pyln-spec/bolt4/requirements.txt +++ b/contrib/pyln-spec/bolt4/requirements.txt @@ -1 +1 @@ -pyln.proto >= 0.9.3 +pyln-proto >= 0.9.3 From 82c66b10dff264ad317ad5c983c427260f4ea0f5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:20:46 +1030 Subject: [PATCH 0021/1530] pytest: allow bad gossip in pruning test. If we forget a channel, we can get upset when we get an update about it: ``` 2021-11-04T00:35:43.8242370Z lightningd-3: 2021-11-04T00:29:22.073Z DEBUG gossipd: Pruning channel 103x1x1 from network view (ages 61 and 22s) ... 2021-11-04T00:35:43.8263502Z lightningd-3: 2021-11-04T00:29:22.509Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-gossipd: Bad gossip order: WIRE_CHANNEL_UPDATE before announcement 103x1x1/0 ``` Signed-off-by: Rusty Russell --- tests/test_gossip.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 648606faaf87..2a4bc7ca361c 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -27,7 +27,8 @@ def test_gossip_pruning(node_factory, bitcoind): """ Create channel and see it being updated in time before pruning """ - l1, l2, l3 = node_factory.get_nodes(3, opts={'dev-fast-gossip-prune': None}) + l1, l2, l3 = node_factory.get_nodes(3, opts={'dev-fast-gossip-prune': None, + 'allow_bad_gossip': True}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l2.rpc.connect(l3.info['id'], 'localhost', l3.port) From 68043c2e8ccf00e2f7a48f62d32d2c4d041cfd01 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:21:46 +1030 Subject: [PATCH 0022/1530] common: clean up autodata in common_shutdown(). valgrind locally complains about the allocations in autodata leaking: ``` ==138200== 16 bytes in 1 blocks are still reachable in loss record 1 of 2 ==138200== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==138200== by 0x10D41A: autodata_register_ (autodata.c:20) ==138200== by 0x10E7B8: register_autotype_type_to_string (type_to_string.h:79) ==138200== by 0x10F5CA: register_one_type_to_string0 (block.c:259) ==138200== by 0x19734C: __libc_csu_init (in /home/rusty/devel/cvs/lightning/common/test/run-route-specific) ==138200== by 0x4A3D03F: (below main) (libc-start.c:264) ==138200== ==138200== 176 bytes in 1 blocks are still reachable in loss record 2 of 2 ==138200== at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==138200== by 0x10D472: autodata_register_ (autodata.c:26) ==138200== by 0x122D37: register_autotype_type_to_string (type_to_string.h:79) ==138200== by 0x122F1F: register_one_type_to_string0 (node_id.c:50) ==138200== by 0x19734C: __libc_csu_init (in /home/rusty/devel/cvs/lightning/common/test/run-route-specific) ==138200== by 0x4A3D03F: (below main) (libc-start.c:264) ==138200== make: *** [Makefile:638: unittest/common/test/run-route-specific] Error 7 ``` Signed-off-by: Rusty Russell --- common/autodata.c | 14 ++++++++++++++ common/autodata.h | 3 +++ common/setup.c | 2 ++ connectd/Makefile | 2 +- tests/plugins/Makefile | 2 +- 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/common/autodata.c b/common/autodata.c index 4273a9b0bdbf..ddeab76fd0ef 100644 --- a/common/autodata.c +++ b/common/autodata.c @@ -38,3 +38,17 @@ void *autodata_get_(const char *typename, size_t *nump) *nump = t->num; return t->ptrs; } + +static bool free_one(const char *member, + struct typereg *t, void *unused) +{ + free(t->ptrs); + free(t); + return true; +} + +void autodata_cleanup(void) +{ + strmap_iterate(&typemap, free_one, NULL); + strmap_clear(&typemap); +} diff --git a/common/autodata.h b/common/autodata.h index 7d05217b4ce1..60c300bab928 100644 --- a/common/autodata.h +++ b/common/autodata.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #define AUTODATA_TYPE(name, type) \ static inline void register_autotype_##name(const type *t) { \ @@ -23,4 +24,6 @@ void autodata_register_(const char *typename, const void *ptr); void *autodata_get_(const char *typename, size_t *nump); +/* Call on shutdown to keep valgrind leak detection happy. */ +void autodata_cleanup(void); #endif /* LIGHTNING_COMMON_AUTODATA_H */ diff --git a/common/setup.c b/common/setup.c index 6f4c4182a3af..46879e6e1419 100644 --- a/common/setup.c +++ b/common/setup.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -58,4 +59,5 @@ void common_shutdown(void) tal_free(tmpctx); wally_cleanup(0); tal_free(wally_tal_ctx); + autodata_cleanup(); } diff --git a/connectd/Makefile b/connectd/Makefile index 8d30894c8964..8e17d4c17a8d 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -76,6 +76,6 @@ CONNECTD_COMMON_OBJS := \ lightningd/lightning_connectd: $(CONNECTD_OBJS) $(CONNECTD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(HSMD_CLIENT_OBJS) -lightningd/lightning_websocketd: $(WEBSOCKETD_OBJS) common/setup.o common/utils.o +lightningd/lightning_websocketd: $(WEBSOCKETD_OBJS) common/setup.o common/utils.o common/autodata.o include connectd/test/Makefile diff --git a/tests/plugins/Makefile b/tests/plugins/Makefile index 94ba9576287a..3f2e6e3aaa27 100644 --- a/tests/plugins/Makefile +++ b/tests/plugins/Makefile @@ -8,7 +8,7 @@ $(PLUGIN_TESTLIBPLUGIN_OBJS): $(PLUGIN_LIB_HEADER) PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_SRC := tests/plugins/test_selfdisable_after_getmanifest.c PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS := $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_SRC:.c=.o) -tests/plugins/test_selfdisable_after_getmanifest: bitcoin/chainparams.o $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS) common/json.o common/json_stream.o common/setup.o common/utils.o $(JSMN_OBJS) $(CCAN_OBJS) +tests/plugins/test_selfdisable_after_getmanifest: bitcoin/chainparams.o $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS) common/autodata.o common/json.o common/json_stream.o common/setup.o common/utils.o $(JSMN_OBJS) $(CCAN_OBJS) # Make sure these depend on everything. ALL_TEST_PROGRAMS += tests/plugins/test_libplugin tests/plugins/test_selfdisable_after_getmanifest From 57328fe59ec209fb31bc082cf61462dc83fc95fd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:22:46 +1030 Subject: [PATCH 0023/1530] tests: use common_setup/common_shutdown to avoid leaks. It also does more checks (like taken() checks). Signed-off-by: Rusty Russell --- cli/test/Makefile | 1 + cli/test/run-human-mode.c | 5 +++-- cli/test/run-large-input.c | 6 +++--- cli/test/run-remove-hint.c | 5 +++-- common/test/run-bolt12_decode.c | 10 +++++----- common/test/run-bolt12_merkle-json.c | 5 +++-- common/test/run-gossmap-fp16.c | 7 +++++-- common/test/run-route-specific.c | 13 ++++--------- common/test/run-route.c | 11 ++++------- gossipd/test/run-crc32_of_update.c | 4 +++- gossipd/test/run-extended-info.c | 6 ++++-- gossipd/test/run-next_block_range.c | 4 +++- lightningd/test/run-find_my_abspath.c | 10 +++++----- lightningd/test/run-log-pruning.c | 7 +++++-- onchaind/test/run-grind_feerate-bug.c | 5 +++-- plugins/test/run-route-overlong.c | 12 ++++-------- wallet/test/run-wallet.c | 3 ++- 17 files changed, 60 insertions(+), 54 deletions(-) diff --git a/cli/test/Makefile b/cli/test/Makefile index 14daeb11ea7c..e49837f229b9 100644 --- a/cli/test/Makefile +++ b/cli/test/Makefile @@ -16,6 +16,7 @@ CLI_TEST_COMMON_OBJS := \ common/pseudorand.o \ common/memleak.o \ common/msg_queue.o \ + common/setup.o \ common/utils.o \ common/type_to_string.o \ common/permute_tx.o diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index d0dcea17ef97..a3a122b1d0e8 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -162,7 +163,7 @@ ssize_t test_read(int fd UNUSED, void *buf, size_t len) int main(int argc UNUSED, char *argv[]) { - setup_locale(); + common_setup(argv[0]); char *fake_argv[] = { argv[0], "--lightning-dir=/tmp/", "-H", "listconfigs", "-N", "none", NULL }; @@ -170,7 +171,7 @@ int main(int argc UNUSED, char *argv[]) max_read_return = -1; assert(test_main(6, fake_argv) == 0); assert(!taken_any()); - take_cleanup(); + common_shutdown(); return 0; } diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index dec5bc571053..7bb64233326b 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -171,7 +172,7 @@ ssize_t test_read(int fd UNUSED, void *buf, size_t len) int main(int argc UNUSED, char *argv[]) { - setup_locale(); + common_setup(argv[0]); char *fake_argv[] = { argv[0], "--lightning-dir=/tmp/", "test", "-N", "none", NULL }; @@ -200,7 +201,6 @@ int main(int argc UNUSED, char *argv[]) max_read_return = -1; assert(test_main(5, fake_argv) == 0); tal_free(response); - assert(!taken_any()); - take_cleanup(); + common_shutdown(); return 0; } diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index c31bebc8e98e..3311a9aec7d3 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -168,7 +169,7 @@ int test_chdir(const char *path) int main(int argc UNUSED, char *argv[]) { - setup_locale(); + common_setup(argv[0]); char *fake_argv[] = { argv[0], "--lightning-dir=/tmp/", "test", "-N", "none", NULL }; @@ -190,6 +191,6 @@ int main(int argc UNUSED, char *argv[]) "num_connected=1\n")); tal_free(output); assert(!taken_any()); - take_cleanup(); + common_shutdown(); return 0; } diff --git a/common/test/run-bolt12_decode.c b/common/test/run-bolt12_decode.c index 0cac9920e775..a975a253f391 100644 --- a/common/test/run-bolt12_decode.c +++ b/common/test/run-bolt12_decode.c @@ -6,6 +6,7 @@ #include #include #include +#include bool deprecated_apis = false; @@ -174,8 +175,7 @@ int main(int argc, char *argv[]) jsmntok_t toks[5000]; const jsmntok_t *t; - setup_locale(); - setup_tmpctx(); + common_setup(argv[0]); if (argv[1]) json = grab_file(tmpctx, argv[1]); @@ -187,8 +187,7 @@ int main(int argc, char *argv[]) "bolt12/format-string-test.json")); if (!json) { printf("test file not found, skipping\n"); - tal_free(tmpctx); - exit(0); + goto out; } } @@ -213,6 +212,7 @@ int main(int argc, char *argv[]) "lno", &dlen, &fail) != NULL); assert(actual == valid); } - tal_free(tmpctx); +out: + common_shutdown(); return 0; } diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index e242ada259f9..1fb1b9916754 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -129,8 +129,7 @@ int main(int argc, char *argv[]) "bolt12/merkle-test.json")); if (!json) { printf("test file not found, skipping\n"); - tal_free(tmpctx); - exit(0); + goto out; } } @@ -179,6 +178,8 @@ int main(int argc, char *argv[]) abort(); printf(" - WRAPPED OK\n"); } + +out: common_shutdown(); return 0; } diff --git a/common/test/run-gossmap-fp16.c b/common/test/run-gossmap-fp16.c index 8c3ba923d457..5b96d36817b1 100644 --- a/common/test/run-gossmap-fp16.c +++ b/common/test/run-gossmap-fp16.c @@ -1,4 +1,5 @@ #include "../fp16.c" +#include #include #include #include @@ -106,12 +107,12 @@ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNE { fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ -int main(void) +int main(int argc, char *argv[]) { /* 5 bit exponent, 11 bit mantissa. */ u32 exponent, mantissa; - setup_locale(); + common_setup(argv[0]); /* These can be represented exactly. */ for (exponent = 0; exponent < (1 << 5); exponent++) { @@ -140,4 +141,6 @@ int main(void) /* Round up works, even if it causes overflow. */ assert(fp16_to_u64(u64_to_fp16(0xffffffff, true)) == (1ULL << 32)); + + common_shutdown(); } diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 6998ca1ab13b..b0e80ba30231 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -180,10 +181,8 @@ static bool route_can_carry_unless_disabled(const struct gossmap *map, return route_can_carry_even_disabled(map, c, dir, amount, arg); } -int main(void) +int main(int argc, char *argv[]) { - setup_locale(); - struct node_id a, b, c, d; struct gossmap_node *a_node, *b_node, *c_node, *d_node; const struct dijkstra *dij; @@ -194,10 +193,7 @@ int main(void) char gossip_version = GOSSIP_STORE_VERSION; char gossipfilename[] = "/tmp/run-route-specific-gossipstore.XXXXXX"; - secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY - | SECP256K1_CONTEXT_SIGN); - setup_tmpctx(); - + common_setup(argv[0]); node_id_from_hexstr("03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf", strlen("03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf"), &a); @@ -309,7 +305,6 @@ int main(void) AMOUNT_MSAT(499968+1), 0); assert(!route); - tal_free(tmpctx); - secp256k1_context_destroy(secp256k1_ctx); + common_shutdown(); return 0; } diff --git a/common/test/run-route.c b/common/test/run-route.c index fb9d756a4542..53dd81a930d6 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -174,9 +175,9 @@ static void node_id_from_privkey(const struct privkey *p, struct node_id *id) node_id_from_pubkey(id, &k); } -int main(void) +int main(int argc, char *argv[]) { - setup_locale(); + common_setup(argv[0]); struct node_id a, b, c, d; struct gossmap_node *a_node, *b_node, *c_node, *d_node; @@ -189,9 +190,6 @@ int main(void) char gossip_version = GOSSIP_STORE_VERSION; char gossipfilename[] = "/tmp/run-route-gossipstore.XXXXXX"; - secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY - | SECP256K1_CONTEXT_SIGN); - setup_tmpctx(); chainparams = chainparams_for_network("regtest"); store_fd = mkstemp(gossipfilename); @@ -313,7 +311,6 @@ int main(void) assert(amount_msat_eq(route[0].amount, AMOUNT_MSAT(3000000 + 6))); assert(route[0].delay == 15); - tal_free(tmpctx); - secp256k1_context_destroy(secp256k1_ctx); + common_shutdown(); return 0; } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 82033c8d3796..2d59a9ca0603 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -5,6 +5,7 @@ int unused_main(int argc, char *argv[]); #undef main #include #include +#include #include /* AUTOGENERATED MOCKS START */ @@ -148,7 +149,7 @@ bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) int main(int argc, char *argv[]) { u8 *update; - setup_locale(); + common_setup(argv[0]); update = tal_hexdata(NULL, "010276df7e70c63cc2b63ef1c062b99c6d934a80ef2fd4dae9e1d86d277f47674af3255a97fa52ade7f129263f591ed784996eba6383135896cc117a438c8029328206226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100005d50f933000000900000000000000000000003e80000000a", strlen("010276df7e70c63cc2b63ef1c062b99c6d934a80ef2fd4dae9e1d86d277f47674af3255a97fa52ade7f129263f591ed784996eba6383135896cc117a438c8029328206226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100005d50f933000000900000000000000000000003e80000000a")); @@ -159,5 +160,6 @@ int main(int argc, char *argv[]) strlen("010206737e9e18d3e4d0ab4066ccaecdcc10e648c5f1c5413f1610747e0d463fa7fa39c1b02ea2fd694275ecfefe4fe9631f24afd182ab75b805e16cd550941f858c06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006d00000100005d50f935010000300000000000000000000000640000000b00000000000186a0")); assert(crc32_of_update(update) == 0xf32ce968); tal_free(update); + common_shutdown(); return 0; } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 38cfc3fbcd75..276caf655d18 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #ifdef NDEBUG @@ -491,11 +492,11 @@ static u8 *test_query_short_channel_ids(const char *test_vector, return msg; } -int main(void) +int main(int argc, char *argv[]) { jsmntok_t *toks = tal_arr(NULL, jsmntok_t, 1000); - setup_locale(); + common_setup(argv[0]); for (size_t i = 0; i < ARRAY_SIZE(test_vectors); i++) { jsmn_parser parser; @@ -524,5 +525,6 @@ int main(void) tal_free(m); } tal_free(toks); + common_shutdown(); return 0; } diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index e193d3f5d393..c57081c1fcfa 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -117,7 +118,7 @@ int main(int argc, char *argv[]) { struct seeker *seeker = tal(NULL, struct seeker); - setup_locale(); + common_setup(argv[0]); seeker->daemon = tal(seeker, struct daemon); @@ -152,4 +153,5 @@ int main(int argc, char *argv[]) -1); tal_free(seeker); + common_shutdown(); } diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 411b3add8b77..02605e39a0d2 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -5,6 +5,7 @@ int unused_main(int argc, char *argv[]); #include "../io_loop_with_timers.c" #include "../lightningd.c" #include "../subd.c" +#include /* AUTOGENERATED MOCKS START */ /* Generated stub for activate_peers */ @@ -245,15 +246,14 @@ struct wallet *wallet_new(struct lightningd *ld UNNEEDED, struct timers *timers struct log *crashlog; #undef main -int main(int argc UNUSED, char *argv[] UNUSED) +int main(int argc UNUSED, char *argv[]) { - setup_locale(); + common_setup(argv[0]); char *argv0; /* We're assuming we're run from top build dir. */ const char *answer; - setup_tmpctx(); answer = path_canon(tmpctx, "lightningd/test/run-find_my_abspath"); /* Various different ways we could find ourselves. */ @@ -286,7 +286,7 @@ int main(int argc UNUSED, char *argv[] UNUSED) assert(streq(find_my_abspath(tmpctx, argv0), answer)); assert(!taken_any()); - take_cleanup(); - tal_free(tmpctx); + common_shutdown(); + return 0; } diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index f1150f276a0b..c59d9d2631cf 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -1,4 +1,5 @@ #include "../log.c" +#include /* AUTOGENERATED MOCKS START */ /* Generated stub for command_fail */ @@ -87,12 +88,13 @@ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ -int main(void) +int main(int argc, char *argv[]) { struct log_book *lb; struct log *l; - setup_locale(); + common_setup(argv[0]); + lb = new_log_book(NULL, (sizeof(struct log_entry) + sizeof("test XXXXXX")) *100); @@ -133,4 +135,5 @@ int main(void) /* Freeing (last) log frees logbook */ tal_free(l); + common_shutdown(); } diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 17b360244997..1798dc456976 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -307,8 +307,10 @@ u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) return (u8 *)ctx; } -bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) +bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES) { + if (taken(msg)) + tal_free(msg); return true; } @@ -422,6 +424,5 @@ int main(int argc, char *argv[]) htlc_scripts, false); assert(ret == 2); - take_cleanup(); common_shutdown(); } diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index b11c647369cc..3b4159ce2c9b 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -1,6 +1,7 @@ #include "../libplugin-pay.c" #include #include +#include #include #include @@ -324,10 +325,8 @@ static void node_id_from_privkey(const struct privkey *p, struct node_id *id) /* We create an arrangement of nodes, each node N connected to N+1 and * to node 1. The cost for each N to N+1 route is 1, for N to 1 is * 2^N. That means it's always cheapest to go the longer route */ -int main(void) +int main(int argc, char *argv[]) { - setup_locale(); - struct node_id ids[NUM_NODES]; int store_fd; struct payment *p; @@ -335,9 +334,7 @@ int main(void) char gossip_version = GOSSIP_STORE_VERSION; char gossipfilename[] = "/tmp/run-route-overlong.XXXXXX"; - secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY - | SECP256K1_CONTEXT_SIGN); - setup_tmpctx(); + common_setup(argv[0]); chainparams = chainparams_for_network("regtest"); store_fd = mkstemp(gossipfilename); assert(write(store_fd, &gossip_version, sizeof(gossip_version)) @@ -401,7 +398,6 @@ int main(void) assert(tal_count(r) == 2); } - tal_free(tmpctx); - secp256k1_context_destroy(secp256k1_ctx); + common_shutdown(); return 0; } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 8b927e8555a5..4463d9c292a8 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1082,6 +1082,8 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx) 5), "wallet_add_utxo with close_info and csv > 1"); CHECK_MSG(!wallet_err, wallet_err); + /* Normally freed by destroy_channel, but we don't call that */ + tal_free(channel.peer); /* Select everything but 5 csv-locked utxo */ utxos = tal_arr(w, const struct utxo *, 0); @@ -1890,7 +1892,6 @@ int main(int argc, const char *argv[]) /* Do not clean up in the case of an error, we might want to debug the * database. */ if (ok) { - take_cleanup(); common_shutdown(); } return !ok; From 787fbb12285ace35415208ad8e0966b6c235cb9b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:23:46 +1030 Subject: [PATCH 0024/1530] db: create simple hashtable of fields in SELECT. This simplistically maps names to numbers, eg: SELECT foo, bar FROM tbl; 'foo' -> 0 'bar' -> 1 If a statement is too complex for our simple parsing, we treat it as a single field (which currently it always is). Signed-off-by: Rusty Russell --- devtools/sql-rewrite.py | 66 ++++++++++++++++++++++++++++++++++++++--- wallet/db_common.h | 9 ++++++ wallet/wallet.c | 2 +- 3 files changed, 72 insertions(+), 5 deletions(-) diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py index 4a93a9d9b052..6cfb44ce634f 100755 --- a/devtools/sql-rewrite.py +++ b/devtools/sql-rewrite.py @@ -83,13 +83,58 @@ def rewrite_single(self, q): "postgres": PostgresRewriter(), } + +# djb2 is simple and effective: see http://www.cse.yorku.ca/~oz/hash.html +def hash_djb2(string): + val = 5381 + for s in string: + val = ((val * 33) & 0xFFFFFFFF) ^ ord(s) + return val + + +def colname_htable(query): + assert query.upper().startswith("SELECT") + colquery = query[6:query.upper().index(" FROM ")] + colnames = colquery.split(',') + + # If split caused unbalanced brackets, it's complex: assume + # a single field! + if any([colname.count('(') != colname.count(')') for colname in colnames]): + return [('"' + colquery.strip() + '"', 0)] + + # 50% density htable + tablesize = len(colnames) * 2 - 1 + table = [("NULL", -1)] * tablesize + for colnum, colname in enumerate(colnames): + colname = colname.strip() + # SELECT xxx AS yyy -> Y + as_clause = colname.upper().find(" AS ") + if as_clause != -1: + colname = colname[as_clause + 4:].strip() + + pos = hash_djb2(colname) % tablesize + while table[pos][0] != "NULL": + pos = (pos + 1) % tablesize + table[pos] = ('"' + colname + '"', colnum) + return table + + template = Template("""#ifndef LIGHTNINGD_WALLET_GEN_DB_${f.upper()} #define LIGHTNINGD_WALLET_GEN_DB_${f.upper()} #include +#include #include #if HAVE_${f.upper()} +% for colname, table in colhtables.items(): +static const struct sqlname_map ${colname}[] = { +% for t in table: + { ${t[0]}, ${t[1]} }, +% endfor +}; + +% endfor struct db_query db_${f}_queries[] = { @@ -99,6 +144,10 @@ def rewrite_single(self, q): .query = "${elem['query']}", .placeholders = ${elem['placeholders']}, .readonly = ${elem['readonly']}, +% if elem['colnames'] is not None: + .colnames = ${elem['colnames']}, + .num_colnames = ARRAY_SIZE(${elem['colnames']}), +% endif }, % endfor }; @@ -129,6 +178,7 @@ def chunk(pofile): if chunk != []: yield chunk + colhtables = {} queries = [] for c in chunk(pofile): @@ -140,13 +190,21 @@ def chunk(pofile): # Strip header and surrounding quotes query = c[i][7:][:-1] + is_select = query.upper().startswith("SELECT") + if is_select: + colnames = 'col_table{}'.format(len(queries)) + colhtables[colnames] = colname_htable(query) + else: + colnames = None + queries.append({ 'name': query, 'query': query, 'placeholders': query.count('?'), - 'readonly': "true" if query.upper().startswith("SELECT") else "false", + 'readonly': "true" if is_select else "false", + 'colnames': colnames, }) - return queries + return colhtables, queries if __name__ == "__main__": @@ -165,7 +223,7 @@ def chunk(pofile): rewriter = rewriters[dialect] - queries = extract_queries(sys.argv[1]) + colhtables, queries = extract_queries(sys.argv[1]) queries = rewriter.rewrite(queries) - print(template.render(f=dialect, queries=queries)) + print(template.render(f=dialect, queries=queries, colhtables=colhtables)) diff --git a/wallet/db_common.h b/wallet/db_common.h index 5db081a6fbb9..5ca35ffacb43 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -49,6 +49,10 @@ struct db_query { /* Is this a read-only query? If it is there's no need to tell plugins * about it. */ bool readonly; + + /* If this is a select statement, what column names */ + const struct sqlname_map *colnames; + size_t num_colnames; }; enum db_binding_type { @@ -155,5 +159,10 @@ AUTODATA_TYPE(db_backends, struct db_config); */ void db_changes_add(struct db_stmt *db_stmt, const char * expanded); +/* devtools/sql-rewrite.py generates this simple htable */ +struct sqlname_map { + const char *sqlname; + int val; +}; #endif /* LIGHTNING_WALLET_DB_COMMON_H */ diff --git a/wallet/wallet.c b/wallet/wallet.c index 1492f358a433..07444f139555 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4321,7 +4321,7 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w) stmt = db_prepare_v2(w->db, SQL("SELECT" " CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)" - "FROM forwarded_payments " + " FROM forwarded_payments " "WHERE state = ?;")); db_bind_int(stmt, 0, wallet_forward_status_in_db(FORWARD_SETTLED)); db_query_prepared(stmt); From 5b482eb04b8177119ece48eb6adb9635e88a5680 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:24:46 +1030 Subject: [PATCH 0025/1530] db: db_col_ variants for accessing SELECT statements by name. Signed-off-by: Rusty Russell --- wallet/db.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++++++++ wallet/db.h | 60 ++++++++- 2 files changed, 401 insertions(+), 1 deletion(-) diff --git a/wallet/db.c b/wallet/db.c index 2b0b9740cca7..5b2b3da4da07 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -2168,6 +2168,323 @@ u8 *db_column_talarr(const tal_t *ctx, struct db_stmt *stmt, int col) db_column_bytes(stmt, col), 0); } +/* Modern variants: by name */ +u64 db_col_u64(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) { + log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + return 0; + } + return stmt->db->config->column_u64_fn(stmt, col); +} + +int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) + return def; + else + return db_column_int(stmt, col); +} + +int db_col_int(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) { + log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + return 0; + } + return stmt->db->config->column_int_fn(stmt, col); +} + +size_t db_col_bytes(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) { + log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + return 0; + } + return stmt->db->config->column_bytes_fn(stmt, col); +} + +int db_col_is_null(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + return stmt->db->config->column_is_null_fn(stmt, col); +} + +const void *db_col_blob(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) { + log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + return NULL; + } + return stmt->db->config->column_blob_fn(stmt, col); +} + +const unsigned char *db_col_text(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) { + log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + return NULL; + } + return stmt->db->config->column_text_fn(stmt, col); +} + +void db_col_preimage(struct db_stmt *stmt, const char *colname, + struct preimage *preimage) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + size_t size = sizeof(struct preimage); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(preimage, raw, size); +} + +void db_col_channel_id(struct db_stmt *stmt, const char *colname, struct channel_id *dest) +{ + size_t col = db_query_colnum(stmt, colname); + + assert(db_column_bytes(stmt, col) == sizeof(dest->id)); + memcpy(dest->id, db_column_blob(stmt, col), sizeof(dest->id)); +} + +void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *dest) +{ + size_t col = db_query_colnum(stmt, colname); + + assert(db_column_bytes(stmt, col) == sizeof(dest->k)); + memcpy(dest->k, db_column_blob(stmt, col), sizeof(dest->k)); +} + +struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, + const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + struct node_id *ret; + size_t n = db_column_bytes(stmt, col) / sizeof(ret->k); + const u8 *arr = db_column_blob(stmt, col); + assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); + ret = tal_arr(ctx, struct node_id, n); + + for (size_t i = 0; i < n; i++) + memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); + + return ret; +} + +void db_col_pubkey(struct db_stmt *stmt, + const char *colname, + struct pubkey *dest) +{ + size_t col = db_query_colnum(stmt, colname); + bool ok; + assert(db_column_bytes(stmt, col) == PUBKEY_CMPR_LEN); + ok = pubkey_from_der(db_column_blob(stmt, col), PUBKEY_CMPR_LEN, dest); + assert(ok); +} + +bool db_col_short_channel_id(struct db_stmt *stmt, const char *colname, + struct short_channel_id *dest) +{ + size_t col = db_query_colnum(stmt, colname); + const char *source = db_column_blob(stmt, col); + size_t sourcelen = db_column_bytes(stmt, col); + return short_channel_id_from_str(source, sourcelen, dest); +} + +struct short_channel_id * +db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *ser; + size_t len; + struct short_channel_id *ret; + + ser = db_column_blob(stmt, col); + len = db_column_bytes(stmt, col); + ret = tal_arr(ctx, struct short_channel_id, 0); + + while (len != 0) { + struct short_channel_id scid; + fromwire_short_channel_id(&ser, &len, &scid); + tal_arr_expand(&ret, scid); + } + + return ret; +} + +bool db_col_signature(struct db_stmt *stmt, const char *colname, + secp256k1_ecdsa_signature *sig) +{ + size_t col = db_query_colnum(stmt, colname); + assert(db_column_bytes(stmt, col) == 64); + return secp256k1_ecdsa_signature_parse_compact( + secp256k1_ctx, sig, db_column_blob(stmt, col)) == 1; +} + +struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + struct timeabs t; + u64 timestamp = db_column_u64(stmt, col); + t.ts.tv_sec = timestamp / NSEC_IN_SEC; + t.ts.tv_nsec = timestamp % NSEC_IN_SEC; + return t; + +} + +struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *src = db_column_blob(stmt, col); + size_t len = db_column_bytes(stmt, col); + return pull_bitcoin_tx(ctx, &src, &len); +} + +struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *src = db_column_blob(stmt, col); + size_t len = db_column_bytes(stmt, col); + return psbt_from_bytes(ctx, src, len); +} + +struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + struct wally_psbt *psbt = db_column_psbt(ctx, stmt, col); + if (!psbt) + return NULL; + return bitcoin_tx_with_psbt(ctx, psbt); +} + +void *db_col_arr_(const tal_t *ctx, struct db_stmt *stmt, const char *colname, + size_t bytes, const char *label, const char *caller) +{ + size_t col = db_query_colnum(stmt, colname); + size_t sourcelen; + void *p; + + if (db_column_is_null(stmt, col)) + return NULL; + + sourcelen = db_column_bytes(stmt, col); + + if (sourcelen % bytes != 0) + db_fatal("%s: %s/%zu column size for %zu not a multiple of %s (%zu)", + caller, colname, col, sourcelen, label, bytes); + + p = tal_arr_label(ctx, char, sourcelen, label); + memcpy(p, db_column_blob(stmt, col), sourcelen); + return p; +} + +void db_col_amount_msat_or_default(struct db_stmt *stmt, + const char *colname, + struct amount_msat *msat, + struct amount_msat def) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) + *msat = def; + else + msat->millisatoshis = db_column_u64(stmt, col); /* Raw: low level function */ +} + +void db_col_amount_msat(struct db_stmt *stmt, const char *colname, + struct amount_msat *msat) +{ + size_t col = db_query_colnum(stmt, colname); + + msat->millisatoshis = db_column_u64(stmt, col); /* Raw: low level function */ +} + +void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat) +{ + size_t col = db_query_colnum(stmt, colname); + + sat->satoshis = db_column_u64(stmt, col); /* Raw: low level function */ +} + +struct json_escape *db_col_json_escape(const tal_t *ctx, + struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + return json_escape_string_(ctx, db_column_blob(stmt, col), + db_column_bytes(stmt, col)); +} + +void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + size_t size = sizeof(struct sha256); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(sha, raw, size); +} + +void db_col_sha256d(struct db_stmt *stmt, const char *colname, + struct sha256_double *shad) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + size_t size = sizeof(struct sha256_double); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(shad, raw, size); +} + +void db_col_secret(struct db_stmt *stmt, const char *colname, struct secret *s) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + assert(db_column_bytes(stmt, col) == sizeof(struct secret)); + raw = db_column_blob(stmt, col); + memcpy(s, raw, sizeof(struct secret)); +} + +struct secret *db_col_secret_arr(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + return db_column_arr(ctx, stmt, col, struct secret); +} + +void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t) +{ + size_t col = db_query_colnum(stmt, colname); + + db_column_sha256d(stmt, col, &t->shad); +} + +struct onionreply *db_col_onionreply(const tal_t *ctx, + struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + struct onionreply *r = tal(ctx, struct onionreply); + r->contents = tal_dup_arr(r, u8, + db_column_blob(stmt, col), + db_column_bytes(stmt, col), 0); + return r; +} + bool db_exec_prepared_v2(struct db_stmt *stmt TAKES) { bool ret = stmt->db->config->exec_fn(stmt); @@ -2224,3 +2541,28 @@ const char **db_changes(struct db *db) { return db->changes; } + +/* Matches the hash function used in devtools/sql-rewrite.py */ +static u32 hash_djb2(const char *str) +{ + u32 hash = 5381; + for (size_t i = 0; str[i]; i++) + hash = ((hash << 5) + hash) ^ str[i]; + return hash; +} + +size_t db_query_colnum(const struct db_stmt *stmt, + const char *colname) +{ + u32 col; + + assert(stmt->query->colnames != NULL); + + col = hash_djb2(colname) % stmt->query->num_colnames; + /* Will crash on NULL, which is the Right Thing */ + while (!streq(stmt->query->colnames[col].sqlname, + colname)) { + col = (col + 1) % stmt->query->num_colnames; + } + return stmt->query->colnames[col].val; +} diff --git a/wallet/db.h b/wallet/db.h index 00986d4f8f15..a18e5cf3b341 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -128,6 +128,7 @@ void db_bind_onionreply(struct db_stmt *stmt, int col, void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr); bool db_step(struct db_stmt *stmt); + u64 db_column_u64(struct db_stmt *stmt, int col); int db_column_int(struct db_stmt *stmt, int col); size_t db_column_bytes(struct db_stmt *stmt, int col); @@ -178,6 +179,63 @@ void db_column_amount_msat_or_default(struct db_stmt *stmt, int col, struct amount_msat *msat, struct amount_msat def); +/* Modern variants: get columns by name from SELECT */ +/* Bridge function to get column number from SELECT + (must exist) */ +size_t db_query_colnum(const struct db_stmt *stmt, + const char *colname); + +u64 db_col_u64(struct db_stmt *stmt, const char *colname); +int db_col_int(struct db_stmt *stmt, const char *colname); +size_t db_col_bytes(struct db_stmt *stmt, const char *colname); +int db_col_is_null(struct db_stmt *stmt, const char *colname); +const void* db_col_blob(struct db_stmt *stmt, const char *colname); +const unsigned char *db_col_text(struct db_stmt *stmt, const char *colname); +void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage); +void db_col_amount_msat(struct db_stmt *stmt, const char *colname, struct amount_msat *msat); +void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat); +struct json_escape *db_col_json_escape(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha); +void db_col_sha256d(struct db_stmt *stmt, const char *colname, struct sha256_double *shad); +void db_col_secret(struct db_stmt *stmt, const char *colname, struct secret *s); +struct secret *db_col_secret_arr(const tal_t *ctx, struct db_stmt *stmt, + const char *colname); +void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t); +void db_col_channel_id(struct db_stmt *stmt, const char *colname, struct channel_id *dest); +void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *ni); +struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, + const char *colname); +void db_col_pubkey(struct db_stmt *stmt, const char *colname, + struct pubkey *p); +bool db_col_short_channel_id(struct db_stmt *stmt, const char *colname, + struct short_channel_id *dest); +struct short_channel_id * +db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +bool db_col_signature(struct db_stmt *stmt, const char *colname, + secp256k1_ecdsa_signature *sig); +struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname); +struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); + +struct onionreply *db_col_onionreply(const tal_t *ctx, + struct db_stmt *stmt, const char *colname); + +#define db_col_arr(ctx, stmt, colname, type) \ + ((type *)db_col_arr_((ctx), (stmt), (colname), \ + sizeof(type), TAL_LABEL(type, "[]"), \ + __func__)) +void *db_col_arr_(const tal_t *ctx, struct db_stmt *stmt, const char *colname, + size_t bytes, const char *label, const char *caller); + + +/* Some useful default variants */ +int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def); +void db_col_amount_msat_or_default(struct db_stmt *stmt, const char *colname, + struct amount_msat *msat, + struct amount_msat def); + + /** * db_exec_prepared -- Execute a prepared statement * @@ -202,7 +260,7 @@ bool db_exec_prepared_v2(struct db_stmt *stmt TAKES); * After preparing a query using `db_prepare`, and after binding all non-null * variables using the `db_bind_*` functions, it can be executed with this * function. This function must be called before calling `db_step` or any of - * the `db_column_*` column access functions. + * the `db_col_*` column access functions. * * If you are not executing a read-only statement, please use * `db_exec_prepared` instead. From befab73070b625ceb8e774601aacb05cc4809c20 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:25:46 +1030 Subject: [PATCH 0026/1530] db: improve db_col_* APIs. 1. db_col_text becomes db_col_strdup, which is what is usually wanted. 2. db_col_short_channel_id becomes db_col_short_channel_id_str, to emphasize that it stores in string form. Modern versions should store u64. Signed-off-by: Rusty Russell --- wallet/db.c | 11 +++++++---- wallet/db.h | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/wallet/db.c b/wallet/db.c index 5b2b3da4da07..9f53bff77b56 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -2230,7 +2230,9 @@ const void *db_col_blob(struct db_stmt *stmt, const char *colname) return stmt->db->config->column_blob_fn(stmt, col); } -const unsigned char *db_col_text(struct db_stmt *stmt, const char *colname) +char *db_col_strdup(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) { size_t col = db_query_colnum(stmt, colname); @@ -2238,7 +2240,7 @@ const unsigned char *db_col_text(struct db_stmt *stmt, const char *colname) log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); return NULL; } - return stmt->db->config->column_text_fn(stmt, col); + return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); } void db_col_preimage(struct db_stmt *stmt, const char *colname, @@ -2295,8 +2297,9 @@ void db_col_pubkey(struct db_stmt *stmt, assert(ok); } -bool db_col_short_channel_id(struct db_stmt *stmt, const char *colname, - struct short_channel_id *dest) +/* Yes, we put this in as a string. Past mistakes; do not use! */ +bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, + struct short_channel_id *dest) { size_t col = db_query_colnum(stmt, colname); const char *source = db_column_blob(stmt, col); diff --git a/wallet/db.h b/wallet/db.h index a18e5cf3b341..9729623ba9f0 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -190,7 +190,9 @@ int db_col_int(struct db_stmt *stmt, const char *colname); size_t db_col_bytes(struct db_stmt *stmt, const char *colname); int db_col_is_null(struct db_stmt *stmt, const char *colname); const void* db_col_blob(struct db_stmt *stmt, const char *colname); -const unsigned char *db_col_text(struct db_stmt *stmt, const char *colname); +char *db_col_strdup(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname); void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage); void db_col_amount_msat(struct db_stmt *stmt, const char *colname, struct amount_msat *msat); void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat); @@ -207,7 +209,7 @@ struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); void db_col_pubkey(struct db_stmt *stmt, const char *colname, struct pubkey *p); -bool db_col_short_channel_id(struct db_stmt *stmt, const char *colname, +bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, struct short_channel_id *dest); struct short_channel_id * db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); From c2a4285a7c4e7c6829202a5c258ac547ceb00eb9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:26:46 +1030 Subject: [PATCH 0027/1530] wallet: use db_col_ accessors in wallet/invoices.c Signed-off-by: Rusty Russell --- wallet/invoices.c | 59 ++++++++++++++++++++-------------------- wallet/test/run-wallet.c | 2 +- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/wallet/invoices.c b/wallet/invoices.c index 95ab4cb038e7..0f6c84a7996c 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -75,44 +75,42 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, struct db_stmt *stmt) { struct invoice_details *dtl = tal(ctx, struct invoice_details); - dtl->state = db_column_int(stmt, 0); + dtl->state = db_col_int(stmt, "state"); - db_column_preimage(stmt, 1, &dtl->r); + db_col_preimage(stmt, "payment_key", &dtl->r); - db_column_sha256(stmt, 2, &dtl->rhash); + db_col_sha256(stmt, "payment_hash", &dtl->rhash); - dtl->label = db_column_json_escape(dtl, stmt, 3); + dtl->label = db_col_json_escape(dtl, stmt, "label"); - if (!db_column_is_null(stmt, 4)) { + if (!db_col_is_null(stmt, "msatoshi")) { dtl->msat = tal(dtl, struct amount_msat); - db_column_amount_msat(stmt, 4, dtl->msat); + db_col_amount_msat(stmt, "msatoshi", dtl->msat); } else { dtl->msat = NULL; } - dtl->expiry_time = db_column_u64(stmt, 5); + dtl->expiry_time = db_col_u64(stmt, "expiry_time"); if (dtl->state == PAID) { - dtl->pay_index = db_column_u64(stmt, 6); - db_column_amount_msat(stmt, 7, &dtl->received); - dtl->paid_timestamp = db_column_u64(stmt, 8); + dtl->pay_index = db_col_u64(stmt, "pay_index"); + db_col_amount_msat(stmt, "msatoshi_received", &dtl->received); + dtl->paid_timestamp = db_col_u64(stmt, "paid_timestamp"); } - dtl->invstring = tal_strndup(dtl, db_column_blob(stmt, 9), - db_column_bytes(stmt, 9)); + dtl->invstring = db_col_strdup(dtl, stmt, "bolt11"); - if (!db_column_is_null(stmt, 10)) - dtl->description = tal_strdup( - dtl, (const char *)db_column_text(stmt, 10)); + if (!db_col_is_null(stmt, "description")) + dtl->description = db_col_strdup(dtl, stmt, + "description"); else dtl->description = NULL; - dtl->features = tal_dup_arr(dtl, u8, - db_column_blob(stmt, 11), - db_column_bytes(stmt, 11), 0); - if (!db_column_is_null(stmt, 12)) { + dtl->features = db_col_arr(dtl, stmt, "features", u8); + if (!db_col_is_null(stmt, "local_offer_id")) { dtl->local_offer_id = tal(dtl, struct sha256); - db_column_sha256(stmt, 12, dtl->local_offer_id); + db_col_sha256(stmt, "local_offer_id", + dtl->local_offer_id); } else dtl->local_offer_id = NULL; @@ -182,7 +180,7 @@ static void trigger_expiration(struct invoices *invoices) while (db_step(stmt)) { idn = tal(tmpctx, struct invoice_id_node); list_add_tail(&idlist, &idn->list); - idn->id = db_column_u64(stmt, 0); + idn->id = db_col_u64(stmt, "id"); } tal_free(stmt); @@ -220,11 +218,12 @@ static void install_expiration_timer(struct invoices *invoices) res = db_step(stmt); assert(res); - if (db_column_is_null(stmt, 0)) + if (db_col_is_null(stmt, "MIN(expiry_time)")) /* Nothing to install */ goto done; - invoices->min_expiry_time = db_column_u64(stmt, 0); + invoices->min_expiry_time = db_col_u64(stmt, + "MIN(expiry_time)"); memset(&expiry, 0, sizeof(expiry)); expiry.ts.tv_sec = invoices->min_expiry_time; @@ -343,7 +342,7 @@ bool invoices_find_by_label(struct invoices *invoices, return false; } - pinvoice->id = db_column_u64(stmt, 0); + pinvoice->id = db_col_u64(stmt, "id"); tal_free(stmt); return true; } @@ -364,7 +363,7 @@ bool invoices_find_by_rhash(struct invoices *invoices, tal_free(stmt); return false; } else { - pinvoice->id = db_column_u64(stmt, 0); + pinvoice->id = db_col_u64(stmt, "id"); tal_free(stmt); return true; } @@ -387,7 +386,7 @@ bool invoices_find_unpaid(struct invoices *invoices, tal_free(stmt); return false; } else { - pinvoice->id = db_column_u64(stmt, 0); + pinvoice->id = db_col_u64(stmt, "id"); tal_free(stmt); return true; } @@ -497,7 +496,7 @@ static enum invoice_status invoice_get_status(struct invoices *invoices, struct res = db_step(stmt); assert(res); - state = db_column_int(stmt, 0); + state = db_col_int(stmt, "state"); tal_free(stmt); return state; } @@ -514,11 +513,11 @@ static void maybe_mark_offer_used(struct db *db, struct invoice invoice) db_query_prepared(stmt); db_step(stmt); - if (db_column_is_null(stmt, 0)) { + if (db_col_is_null(stmt, "local_offer_id")) { tal_free(stmt); return; } - db_column_sha256(stmt, 0, &local_offer_id); + db_col_sha256(stmt, "local_offer_id", &local_offer_id); tal_free(stmt); wallet_offer_mark_used(db, &local_offer_id); @@ -609,7 +608,7 @@ void invoices_waitany(const tal_t *ctx, db_query_prepared(stmt); if (db_step(stmt)) { - invoice.id = db_column_u64(stmt, 0); + invoice.id = db_col_u64(stmt, "id"); cb(&invoice, cbarg); } else { diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 4463d9c292a8..533fd19ae7f5 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1498,7 +1498,7 @@ static int count_inflights(struct wallet *w, u64 channel_dbid) db_query_prepared(stmt); if (!db_step(stmt)) abort(); - count = db_column_int(stmt, 0); + count = db_col_int(stmt, "COUNT(1)"); tal_free(stmt); return count; } From b8a240bd15a401cc6e9543e2e67230620dc7b763 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:27:46 +1030 Subject: [PATCH 0028/1530] wallet: convert wallet.c to db_col_ APIs Signed-off-by: Rusty Russell --- wallet/wallet.c | 720 +++++++++++++++++++++++++----------------------- 1 file changed, 374 insertions(+), 346 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index 07444f139555..72e14d1ae1df 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -64,8 +64,8 @@ static void outpointfilters_init(struct wallet *w) db_query_prepared(stmt); while (db_step(stmt)) { - db_column_txid(stmt, 0, &outpoint.txid); - outpoint.n = db_column_int(stmt, 1); + db_col_txid(stmt, "txid", &outpoint.txid); + outpoint.n = db_col_int(stmt, "outnum"); outpointfilter_add(w->utxoset_outpoints, &outpoint); } tal_free(stmt); @@ -175,51 +175,49 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt) { struct utxo *utxo = tal(ctx, struct utxo); u32 *blockheight, *spendheight; - db_column_txid(stmt, 0, &utxo->outpoint.txid); - utxo->outpoint.n = db_column_int(stmt, 1); - db_column_amount_sat(stmt, 2, &utxo->amount); - utxo->is_p2sh = db_column_int(stmt, 3) == p2sh_wpkh; - utxo->status = db_column_int(stmt, 4); - utxo->keyindex = db_column_int(stmt, 5); - if (!db_column_is_null(stmt, 6)) { + db_col_txid(stmt, "prev_out_tx", &utxo->outpoint.txid); + utxo->outpoint.n = db_col_int(stmt, "prev_out_index"); + db_col_amount_sat(stmt, "value", &utxo->amount); + utxo->is_p2sh = db_col_int(stmt, "type") == p2sh_wpkh; + utxo->status = db_col_int(stmt, "status"); + utxo->keyindex = db_col_int(stmt, "keyindex"); + if (!db_col_is_null(stmt, "channel_id")) { utxo->close_info = tal(utxo, struct unilateral_close_info); - utxo->close_info->channel_id = db_column_u64(stmt, 6); - db_column_node_id(stmt, 7, &utxo->close_info->peer_id); - if (!db_column_is_null(stmt, 8)) { + utxo->close_info->channel_id = db_col_u64(stmt, "channel_id"); + db_col_node_id(stmt, "peer_id", &utxo->close_info->peer_id); + if (!db_col_is_null(stmt, "commitment_point")) { utxo->close_info->commitment_point = tal(utxo->close_info, struct pubkey); - db_column_pubkey(stmt, 8, - utxo->close_info->commitment_point); + db_col_pubkey(stmt, "commitment_point", + utxo->close_info->commitment_point); } else utxo->close_info->commitment_point = NULL; utxo->close_info->option_anchor_outputs - = db_column_int(stmt, 9); - utxo->close_info->csv = db_column_int(stmt, 14); + = db_col_int(stmt, "option_anchor_outputs"); + utxo->close_info->csv = db_col_int(stmt, "csv_lock"); } else { utxo->close_info = NULL; } - utxo->scriptPubkey = - tal_dup_arr(utxo, u8, db_column_blob(stmt, 12), - db_column_bytes(stmt, 12), 0); + utxo->scriptPubkey = db_col_arr(utxo, stmt, "scriptpubkey", u8); utxo->blockheight = NULL; utxo->spendheight = NULL; - if (!db_column_is_null(stmt, 10)) { + if (!db_col_is_null(stmt, "confirmation_height")) { blockheight = tal(utxo, u32); - *blockheight = db_column_int(stmt, 10); + *blockheight = db_col_int(stmt, "confirmation_height"); utxo->blockheight = blockheight; } - if (!db_column_is_null(stmt, 11)) { + if (!db_col_is_null(stmt, "spend_height")) { spendheight = tal(utxo, u32); - *spendheight = db_column_int(stmt, 11); + *spendheight = db_col_int(stmt, "spend_height"); utxo->spendheight = spendheight; } /* This column can be null if 0.9.1 db or below. */ - utxo->reserved_til = db_column_int_or_default(stmt, 13, 0); + utxo->reserved_til = db_col_int_or_default(stmt, "reserved_til", 0); return utxo; } @@ -816,8 +814,8 @@ bool wallet_shachain_load(struct wallet *wallet, u64 id, return false; } - chain->chain.min_index = db_column_u64(stmt, 0); - chain->chain.num_valid = db_column_u64(stmt, 1); + chain->chain.min_index = db_col_u64(stmt, "min_index"); + chain->chain.num_valid = db_col_u64(stmt, "num_valid"); tal_free(stmt); /* Load shachain known entries */ @@ -828,9 +826,9 @@ bool wallet_shachain_load(struct wallet *wallet, u64 id, db_query_prepared(stmt); while (db_step(stmt)) { - int pos = db_column_int(stmt, 2); - chain->chain.known[pos].index = db_column_u64(stmt, 0); - db_column_sha256(stmt, 1, &chain->chain.known[pos].hash); + int pos = db_col_int(stmt, "pos"); + chain->chain.known[pos].index = db_col_u64(stmt, "idx"); + db_col_sha256(stmt, "hash", &chain->chain.known[pos].hash); } tal_free(stmt); return true; @@ -838,7 +836,7 @@ bool wallet_shachain_load(struct wallet *wallet, u64 id, static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) { - const unsigned char *addrstr; + const char *addrstr; struct peer *peer = NULL; struct node_id id; struct wireaddr_internal addr; @@ -852,18 +850,18 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) if (!db_step(stmt)) goto done; - if (db_column_is_null(stmt, 1)) + if (db_col_is_null(stmt, "node_id")) goto done; - db_column_node_id(stmt, 1, &id); + db_col_node_id(stmt, "node_id", &id); - addrstr = db_column_text(stmt, 2); - if (!parse_wireaddr_internal((const char*)addrstr, &addr, DEFAULT_PORT, + addrstr = db_col_strdup(tmpctx, stmt, "address"); + if (!parse_wireaddr_internal(addrstr, &addr, DEFAULT_PORT, false, false, true, true, NULL)) goto done; /* FIXME: save incoming in db! */ - peer = new_peer(w->ld, db_column_u64(stmt, 0), &id, &addr, false); + peer = new_peer(w->ld, db_col_u64(stmt, "id"), &id, &addr, false); done: tal_free(stmt); @@ -884,7 +882,7 @@ wallet_htlc_sigs_load(const tal_t *ctx, struct wallet *w, u64 channelid, while (db_step(stmt)) { struct bitcoin_signature sig; - db_column_signature(stmt, 0, &sig.s); + db_col_signature(stmt, "signature", &sig.s); /* BOLT #3: * ## HTLC-Timeout and HTLC-Success Transactions *... @@ -923,7 +921,8 @@ bool wallet_remote_ann_sigs_load(const tal_t *ctx, struct wallet *w, u64 id, assert(res); /* if only one sig exists, forget the sig and hope peer send new ones*/ - if (db_column_is_null(stmt, 0) || db_column_is_null(stmt, 1)) { + if (db_col_is_null(stmt, "remote_ann_node_sig") + || db_col_is_null(stmt, "remote_ann_bitcoin_sig")) { *remote_ann_node_sig = *remote_ann_bitcoin_sig = NULL; tal_free(stmt); return true; @@ -933,10 +932,10 @@ bool wallet_remote_ann_sigs_load(const tal_t *ctx, struct wallet *w, u64 id, *remote_ann_node_sig = tal(ctx, secp256k1_ecdsa_signature); *remote_ann_bitcoin_sig = tal(ctx, secp256k1_ecdsa_signature); - if (!db_column_signature(stmt, 0, *remote_ann_node_sig)) + if (!db_col_signature(stmt, "remote_ann_node_sig", *remote_ann_node_sig)) goto fail; - if (!db_column_signature(stmt, 1, *remote_ann_bitcoin_sig)) + if (!db_col_signature(stmt, "remote_ann_bitcoin_sig", *remote_ann_bitcoin_sig)) goto fail; tal_free(stmt); @@ -963,8 +962,8 @@ static struct fee_states *wallet_channel_fee_states_load(struct wallet *w, /* Start with blank slate. */ fee_states = new_fee_states(w, opener, NULL); while (db_step(stmt)) { - enum htlc_state hstate = htlc_state_in_db(db_column_int(stmt, 0)); - u32 feerate = db_column_int(stmt, 1); + enum htlc_state hstate = htlc_state_in_db(db_col_int(stmt, "hstate")); + u32 feerate = db_col_int(stmt, "feerate_per_kw"); if (fee_states->feerate[hstate] != NULL) { log_broken(w->log, @@ -999,8 +998,8 @@ static struct height_states *wallet_channel_height_states_load(struct wallet *w, /* Start with blank slate. */ states = new_height_states(w, opener, NULL); while (db_step(stmt)) { - enum htlc_state hstate = htlc_state_in_db(db_column_int(stmt, 0)); - u32 blockheight = db_column_int(stmt, 1); + enum htlc_state hstate = htlc_state_in_db(db_col_int(stmt, "hstate")); + u32 blockheight = db_col_int(stmt, "blockheight"); if (states->height[hstate] != NULL) { log_broken(w->log, @@ -1128,21 +1127,21 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, u32 lease_chan_max_msat, lease_blockheight_start; u16 lease_chan_max_ppt; - db_column_txid(stmt, 0, &funding.txid); - funding.n = db_column_int(stmt, 1), - db_column_amount_sat(stmt, 3, &funding_sat); - db_column_amount_sat(stmt, 4, &our_funding_sat); - if (!db_column_signature(stmt, 7, &last_sig.s)) + db_col_txid(stmt, "funding_tx_id", &funding.txid); + funding.n = db_col_int(stmt, "funding_tx_outnum"), + db_col_amount_sat(stmt, "funding_satoshi", &funding_sat); + db_col_amount_sat(stmt, "our_funding_satoshi", &our_funding_sat); + if (!db_col_signature(stmt, "last_sig", &last_sig.s)) return NULL; last_sig.sighash_type = SIGHASH_ALL; - if (!db_column_is_null(stmt, 10)) { + if (!db_col_is_null(stmt, "lease_commit_sig")) { lease_commit_sig = tal(tmpctx, secp256k1_ecdsa_signature); - db_column_signature(stmt, 10, lease_commit_sig); - lease_chan_max_msat = db_column_int(stmt, 11); - lease_chan_max_ppt = db_column_int(stmt, 12); - lease_blockheight_start = db_column_int(stmt, 13); + db_col_signature(stmt, "lease_commit_sig", lease_commit_sig); + lease_chan_max_msat = db_col_int(stmt, "lease_chan_max_msat"); + lease_chan_max_ppt = db_col_int(stmt, "lease_chan_max_ppt"); + lease_blockheight_start = db_col_int(stmt, "lease_blockheight_start"); } else { lease_commit_sig = NULL; lease_chan_max_msat = 0; @@ -1151,20 +1150,20 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, } inflight = new_inflight(chan, &funding, - db_column_int(stmt, 2), + db_col_int(stmt, "funding_feerate"), funding_sat, our_funding_sat, - db_column_psbt(tmpctx, stmt, 5), - db_column_psbt_to_tx(tmpctx, stmt, 6), + db_col_psbt(tmpctx, stmt, "funding_psbt"), + db_col_psbt_to_tx(tmpctx, stmt, "last_tx"), last_sig, - db_column_int(stmt, 9), + db_col_int(stmt, "lease_expiry"), lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_blockheight_start); /* Pull out the serialized tx-sigs-received-ness */ - inflight->remote_tx_sigs = db_column_int(stmt, 8); + inflight->remote_tx_sigs = db_col_int(stmt, "funding_tx_remote_sigs_received"); return inflight; } @@ -1242,7 +1241,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm u32 lease_chan_max_msat; u16 lease_chan_max_ppt; - peer_dbid = db_column_u64(stmt, 1); + peer_dbid = db_col_u64(stmt, "peer_id"); peer = find_peer_by_dbid(w->ld, peer_dbid); if (!peer) { peer = wallet_peer_load(w, peer_dbid); @@ -1251,23 +1250,26 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm } } - if (!db_column_is_null(stmt, 2)) { + if (!db_col_is_null(stmt, "short_channel_id")) { scid = tal(tmpctx, struct short_channel_id); - if (!db_column_short_channel_id(stmt, 2, scid)) + if (!db_col_short_channel_id_str(stmt, "short_channel_id", scid)) return NULL; } else { scid = NULL; } - ok &= wallet_shachain_load(w, db_column_u64(stmt, 29), &wshachain); + ok &= wallet_shachain_load(w, db_col_u64(stmt, "shachain_remote_id"), + &wshachain); - remote_shutdown_scriptpubkey = db_column_arr(tmpctx, stmt, 30, u8); - local_shutdown_scriptpubkey = db_column_arr(tmpctx, stmt, 50, u8); + remote_shutdown_scriptpubkey = db_col_arr(tmpctx, stmt, + "shutdown_scriptpubkey_remote", u8); + local_shutdown_scriptpubkey = db_col_arr(tmpctx, stmt, + "shutdown_scriptpubkey_local", u8); /* Do we have a last_sent_commit, if yes, populate */ - if (!db_column_is_null(stmt, 43)) { - const u8 *cursor = db_column_blob(stmt, 43); - size_t len = db_column_bytes(stmt, 43); + if (!db_col_is_null(stmt, "last_sent_commit")) { + const u8 *cursor = db_col_blob(stmt, "last_sent_commit"); + size_t len = db_col_bytes(stmt, "last_sent_commit"); size_t n = 0; last_sent_commit = tal_arr(tmpctx, struct changed_htlc, n); while (len) { @@ -1279,43 +1281,44 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm last_sent_commit = NULL; #ifdef COMPAT_V060 - if (!last_sent_commit && !db_column_is_null(stmt, 32)) { + if (!last_sent_commit && !db_col_is_null(stmt, "last_sent_commit_state")) { last_sent_commit = tal(tmpctx, struct changed_htlc); - last_sent_commit->newstate = db_column_u64(stmt, 32); - last_sent_commit->id = db_column_u64(stmt, 33); + last_sent_commit->newstate = db_col_u64(stmt, "last_sent_commit_state"); + last_sent_commit->id = db_col_u64(stmt, "last_sent_commit_id"); } #endif - if (!db_column_is_null(stmt, 42)) { + if (!db_col_is_null(stmt, "future_per_commitment_point")) { future_per_commitment_point = tal(tmpctx, struct pubkey); - db_column_pubkey(stmt, 42, future_per_commitment_point); + db_col_pubkey(stmt, "future_per_commitment_point", + future_per_commitment_point); } else future_per_commitment_point = NULL; - db_column_channel_id(stmt, 3, &cid); - channel_config_id = db_column_u64(stmt, 4); + db_col_channel_id(stmt, "full_channel_id", &cid); + channel_config_id = db_col_u64(stmt, "channel_config_local"); ok &= wallet_channel_config_load(w, channel_config_id, &our_config); - db_column_sha256d(stmt, 13, &funding.txid.shad); - funding.n = db_column_int(stmt, 14), - ok &= db_column_signature(stmt, 35, &last_sig.s); + db_col_sha256d(stmt, "funding_tx_id", &funding.txid.shad); + funding.n = db_col_int(stmt, "funding_tx_outnum"), + ok &= db_col_signature(stmt, "last_sig", &last_sig.s); last_sig.sighash_type = SIGHASH_ALL; /* Populate channel_info */ - db_column_pubkey(stmt, 20, &channel_info.remote_fundingkey); - db_column_pubkey(stmt, 21, &channel_info.theirbase.revocation); - db_column_pubkey(stmt, 22, &channel_info.theirbase.payment); - db_column_pubkey(stmt, 23, &channel_info.theirbase.htlc); - db_column_pubkey(stmt, 24, &channel_info.theirbase.delayed_payment); - db_column_pubkey(stmt, 25, &channel_info.remote_per_commit); - db_column_pubkey(stmt, 26, &channel_info.old_remote_per_commit); - - wallet_channel_config_load(w, db_column_u64(stmt, 5), + db_col_pubkey(stmt, "fundingkey_remote", &channel_info.remote_fundingkey); + db_col_pubkey(stmt, "revocation_basepoint_remote", &channel_info.theirbase.revocation); + db_col_pubkey(stmt, "payment_basepoint_remote", &channel_info.theirbase.payment); + db_col_pubkey(stmt, "htlc_basepoint_remote", &channel_info.theirbase.htlc); + db_col_pubkey(stmt, "delayed_payment_basepoint_remote", &channel_info.theirbase.delayed_payment); + db_col_pubkey(stmt, "per_commit_remote", &channel_info.remote_per_commit); + db_col_pubkey(stmt, "old_per_commit_remote", &channel_info.old_remote_per_commit); + + wallet_channel_config_load(w, db_col_u64(stmt, "channel_config_remote"), &channel_info.their_config); fee_states = wallet_channel_fee_states_load(w, - db_column_u64(stmt, 0), - db_column_int(stmt, 7)); + db_col_u64(stmt, "id"), + db_col_int(stmt, "funder")); if (!fee_states) ok = false; @@ -1327,8 +1330,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm /* Blockheight states for the channel! */ height_states = wallet_channel_height_states_load(w, - db_column_u64(stmt, 0), - db_column_int(stmt, 7)); + db_col_u64(stmt, "id"), + db_col_int(stmt, "funder")); if (!height_states) ok = false; @@ -1337,103 +1340,109 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm return NULL; } - final_key_idx = db_column_u64(stmt, 31); + final_key_idx = db_col_u64(stmt, "shutdown_keyidx_local"); if (final_key_idx < 0) { tal_free(fee_states); log_broken(w->log, "%s: Final key < 0", __func__); return NULL; } - db_column_pubkey(stmt, 53, &local_basepoints.revocation); - db_column_pubkey(stmt, 54, &local_basepoints.payment); - db_column_pubkey(stmt, 55, &local_basepoints.htlc); - db_column_pubkey(stmt, 56, &local_basepoints.delayed_payment); - db_column_pubkey(stmt, 57, &local_funding_pubkey); - if (db_column_is_null(stmt, 58)) + db_col_pubkey(stmt, "revocation_basepoint_local", + &local_basepoints.revocation); + db_col_pubkey(stmt, "payment_basepoint_local", + &local_basepoints.payment); + db_col_pubkey(stmt, "htlc_basepoint_local", + &local_basepoints.htlc); + db_col_pubkey(stmt, "delayed_payment_basepoint_local", + &local_basepoints.delayed_payment); + db_col_pubkey(stmt, "funding_pubkey_local", &local_funding_pubkey); + if (db_col_is_null(stmt, "shutdown_wrong_txid")) shutdown_wrong_funding = NULL; else { shutdown_wrong_funding = tal(tmpctx, struct bitcoin_outpoint); - db_column_txid(stmt, 58, &shutdown_wrong_funding->txid); - shutdown_wrong_funding->n = db_column_int(stmt, 59); + db_col_txid(stmt, "shutdown_wrong_txid", + &shutdown_wrong_funding->txid); + shutdown_wrong_funding->n + = db_col_int(stmt, "shutdown_wrong_outnum"); } - db_column_amount_sat(stmt, 15, &funding_sat); - db_column_amount_sat(stmt, 16, &our_funding_sat); - db_column_amount_msat(stmt, 18, &push_msat); - db_column_amount_msat(stmt, 19, &our_msat); - db_column_amount_msat(stmt, 40, &msat_to_us_min); - db_column_amount_msat(stmt, 41, &msat_to_us_max); + db_col_amount_sat(stmt, "funding_satoshi", &funding_sat); + db_col_amount_sat(stmt, "our_funding_satoshi", &our_funding_sat); + db_col_amount_msat(stmt, "push_msatoshi", &push_msat); + db_col_amount_msat(stmt, "msatoshi_local", &our_msat); + db_col_amount_msat(stmt, "msatoshi_to_us_min", &msat_to_us_min); + db_col_amount_msat(stmt, "msatoshi_to_us_max", &msat_to_us_max); - if (!db_column_is_null(stmt, 61)) { + if (!db_col_is_null(stmt, "lease_commit_sig")) { lease_commit_sig = tal(w, secp256k1_ecdsa_signature); - db_column_signature(stmt, 61, lease_commit_sig); - lease_chan_max_msat = db_column_int(stmt, 62); - lease_chan_max_ppt = db_column_int(stmt, 63); + db_col_signature(stmt, "lease_commit_sig", lease_commit_sig); + lease_chan_max_msat = db_col_int(stmt, "lease_chan_max_msat"); + lease_chan_max_ppt = db_col_int(stmt, "lease_chan_max_ppt"); } else { lease_commit_sig = NULL; lease_chan_max_msat = 0; lease_chan_max_ppt = 0; } - if (db_column_int(stmt, 49)) + if (db_col_int(stmt, "option_anchor_outputs")) type = channel_type_anchor_outputs(NULL); - else if (db_column_u64(stmt, 47) != 0x7FFFFFFFFFFFFFFFULL) + else if (db_col_u64(stmt, "local_static_remotekey_start") != 0x7FFFFFFFFFFFFFFFULL) type = channel_type_static_remotekey(NULL); else type = channel_type_none(NULL); - chan = new_channel(peer, db_column_u64(stmt, 0), + chan = new_channel(peer, db_col_u64(stmt, "id"), &wshachain, - db_column_int(stmt, 6), - db_column_int(stmt, 7), + db_col_int(stmt, "state"), + db_col_int(stmt, "funder"), NULL, /* Set up fresh log */ "Loaded from database", - db_column_int(stmt, 8), + db_col_int(stmt, "channel_flags"), &our_config, - db_column_int(stmt, 9), - db_column_u64(stmt, 10), - db_column_u64(stmt, 11), - db_column_u64(stmt, 12), + db_col_int(stmt, "minimum_depth"), + db_col_u64(stmt, "next_index_local"), + db_col_u64(stmt, "next_index_remote"), + db_col_u64(stmt, "next_htlc_id"), &funding, funding_sat, push_msat, our_funding_sat, - db_column_int(stmt, 17) != 0, + db_col_int(stmt, "funding_locked_remote") != 0, scid, &cid, our_msat, msat_to_us_min, /* msatoshi_to_us_min */ msat_to_us_max, /* msatoshi_to_us_max */ - db_column_psbt_to_tx(tmpctx, stmt, 34), + db_col_psbt_to_tx(tmpctx, stmt, "last_tx"), &last_sig, wallet_htlc_sigs_load(tmpctx, w, - db_column_u64(stmt, 0), - db_column_int(stmt, 49)), + db_col_u64(stmt, "id"), + db_col_int(stmt, "option_anchor_outputs")), &channel_info, take(fee_states), remote_shutdown_scriptpubkey, local_shutdown_scriptpubkey, final_key_idx, - db_column_int(stmt, 36) != 0, + db_col_int(stmt, "last_was_revoke") != 0, last_sent_commit, - db_column_u64(stmt, 37), - db_column_int(stmt, 38), - db_column_int(stmt, 39), + db_col_u64(stmt, "first_blocknum"), + db_col_int(stmt, "min_possible_feerate"), + db_col_int(stmt, "max_possible_feerate"), /* Not connected */ false, &local_basepoints, &local_funding_pubkey, future_per_commitment_point, - db_column_int(stmt, 44), - db_column_int(stmt, 45), - db_column_arr(tmpctx, stmt, 46, u8), - db_column_u64(stmt, 47), - db_column_u64(stmt, 48), + db_col_int(stmt, "feerate_base"), + db_col_int(stmt, "feerate_ppm"), + db_col_arr(tmpctx, stmt, "remote_upfront_shutdown_script", u8), + db_col_u64(stmt, "local_static_remotekey_start"), + db_col_u64(stmt, "remote_static_remotekey_start"), type, - db_column_int(stmt, 51), - db_column_int(stmt, 52), + db_col_int(stmt, "closer"), + db_col_int(stmt, "state_change_reason"), shutdown_wrong_funding, take(height_states), - db_column_int(stmt, 60), + db_col_int(stmt, "lease_expiry"), lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt); @@ -1455,7 +1464,7 @@ static void set_max_channel_dbid(struct wallet *w) w->max_channel_dbid = 0; if (db_step(stmt)) - w->max_channel_dbid = db_column_u64(stmt, 0); + w->max_channel_dbid = db_col_u64(stmt, "id"); tal_free(stmt); } @@ -1657,14 +1666,26 @@ void wallet_channel_stats_load(struct wallet *w, /* This must succeed, since we know the channel exists */ assert(res); - stats->in_payments_offered = db_column_int_or_default(stmt, 0, 0); - stats->in_payments_fulfilled = db_column_int_or_default(stmt, 1, 0); - db_column_amount_msat_or_default(stmt, 2, &stats->in_msatoshi_offered, AMOUNT_MSAT(0)); - db_column_amount_msat_or_default(stmt, 3, &stats->in_msatoshi_fulfilled, AMOUNT_MSAT(0)); - stats->out_payments_offered = db_column_int_or_default(stmt, 4, 0); - stats->out_payments_fulfilled = db_column_int_or_default(stmt, 5, 0); - db_column_amount_msat_or_default(stmt, 6, &stats->out_msatoshi_offered, AMOUNT_MSAT(0)); - db_column_amount_msat_or_default(stmt, 7, &stats->out_msatoshi_fulfilled, AMOUNT_MSAT(0)); + stats->in_payments_offered + = db_col_int_or_default(stmt, "in_payments_offered", 0); + stats->in_payments_fulfilled + = db_col_int_or_default(stmt, "in_payments_fulfilled", 0); + db_col_amount_msat_or_default(stmt, "in_msatoshi_offered", + &stats->in_msatoshi_offered, + AMOUNT_MSAT(0)); + db_col_amount_msat_or_default(stmt, "in_msatoshi_fulfilled", + &stats->in_msatoshi_fulfilled, + AMOUNT_MSAT(0)); + stats->out_payments_offered + = db_col_int_or_default(stmt, "out_payments_offered", 0); + stats->out_payments_fulfilled + = db_col_int_or_default(stmt, "out_payments_fulfilled", 0); + db_col_amount_msat_or_default(stmt, "out_msatoshi_offered", + &stats->out_msatoshi_offered, + AMOUNT_MSAT(0)); + db_col_amount_msat_or_default(stmt, "out_msatoshi_fulfilled", + &stats->out_msatoshi_fulfilled, + AMOUNT_MSAT(0)); tal_free(stmt); } @@ -1678,9 +1699,9 @@ void wallet_blocks_heights(struct wallet *w, u32 def, u32 *min, u32 *max) /* If we ever processed a block we'll get the latest block in the chain */ if (db_step(stmt)) { - if (!db_column_is_null(stmt, 0)) { - *min = db_column_int(stmt, 0); - *max = db_column_int(stmt, 1); + if (!db_col_is_null(stmt, "MIN(height)")) { + *min = db_col_int(stmt, "MIN(height)"); + *max = db_col_int(stmt, "MAX(height)"); } } tal_free(stmt); @@ -1729,7 +1750,6 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, struct channel_config *cc) { bool ok = true; - int col = 1; const char *query = SQL( "SELECT id, dust_limit_satoshis, max_htlc_value_in_flight_msat, " "channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, " @@ -1743,14 +1763,16 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, return false; cc->id = id; - db_column_amount_sat(stmt, col++, &cc->dust_limit); - db_column_amount_msat(stmt, col++, &cc->max_htlc_value_in_flight); - db_column_amount_sat(stmt, col++, &cc->channel_reserve); - db_column_amount_msat(stmt, col++, &cc->htlc_minimum); - cc->to_self_delay = db_column_int(stmt, col++); - cc->max_accepted_htlcs = db_column_int(stmt, col++); - db_column_amount_msat(stmt, col++, &cc->max_dust_htlc_exposure_msat); - assert(col == 8); + db_col_amount_sat(stmt, "dust_limit_satoshis", &cc->dust_limit); + db_col_amount_msat(stmt, "max_htlc_value_in_flight_msat", + &cc->max_htlc_value_in_flight); + db_col_amount_sat(stmt, "channel_reserve_satoshis", + &cc->channel_reserve); + db_col_amount_msat(stmt, "htlc_minimum_msat", &cc->htlc_minimum); + cc->to_self_delay = db_col_int(stmt, "to_self_delay"); + cc->max_accepted_htlcs = db_col_int(stmt, "max_accepted_htlcs"); + db_col_amount_msat(stmt, "max_dust_htlc_exposure_msat", + &cc->max_dust_htlc_exposure_msat); tal_free(stmt); return ok; } @@ -2032,11 +2054,11 @@ struct state_change_entry *wallet_state_change_get(struct wallet *w, db_query_prepared(stmt); while (db_step(stmt)) { - tmp.timestamp = db_column_timeabs(stmt, 0); - tmp.old_state = db_column_int(stmt, 1); - tmp.new_state = db_column_int(stmt, 2); - tmp.cause = db_column_int(stmt, 3); - tmp.message = tal_strdup(res, (const char *)db_column_text(stmt, 4)); + tmp.timestamp = db_col_timeabs(stmt, "timestamp"); + tmp.old_state = db_col_int(stmt, "old_state"); + tmp.new_state = db_col_int(stmt, "new_state"); + tmp.cause = db_col_int(stmt, "cause"); + tmp.message = db_col_strdup(res, stmt, "message"); tal_arr_expand(&res, tmp); } tal_free(stmt); @@ -2055,7 +2077,7 @@ static void wallet_peer_save(struct wallet *w, struct peer *peer) if (db_step(stmt)) { /* So we already knew this peer, just return its dbid */ - peer->dbid = db_column_u64(stmt, 0); + peer->dbid = db_col_u64(stmt, "id"); tal_free(stmt); /* Since we're at it update the wireaddr */ @@ -2488,40 +2510,41 @@ static bool wallet_stmt2htlc_in(struct channel *channel, struct db_stmt *stmt, struct htlc_in *in) { bool ok = true; - in->dbid = db_column_u64(stmt, 0); - in->key.id = db_column_u64(stmt, 1); + in->dbid = db_col_u64(stmt, "id"); + in->key.id = db_col_u64(stmt, "channel_htlc_id"); in->key.channel = channel; - db_column_amount_msat(stmt, 2, &in->msat); - in->cltv_expiry = db_column_int(stmt, 3); - in->hstate = db_column_int(stmt, 4); + db_col_amount_msat(stmt, "msatoshi", &in->msat); + in->cltv_expiry = db_col_int(stmt, "cltv_expiry"); + in->hstate = db_col_int(stmt, "hstate"); in->status = NULL; /* FIXME: save blinding in db !*/ in->blinding = NULL; - db_column_sha256(stmt, 5, &in->payment_hash); + db_col_sha256(stmt, "payment_hash", &in->payment_hash); - if (!db_column_is_null(stmt, 6)) { + if (!db_col_is_null(stmt, "payment_key")) { in->preimage = tal(in, struct preimage); - db_column_preimage(stmt, 6, in->preimage); + db_col_preimage(stmt, "payment_key", in->preimage); } else { in->preimage = NULL; } - assert(db_column_bytes(stmt, 7) == sizeof(in->onion_routing_packet)); - memcpy(&in->onion_routing_packet, db_column_blob(stmt, 7), + assert(db_col_bytes(stmt, "routing_onion") + == sizeof(in->onion_routing_packet)); + memcpy(&in->onion_routing_packet, db_col_blob(stmt, "routing_onion"), sizeof(in->onion_routing_packet)); - if (db_column_is_null(stmt, 8)) + if (db_col_is_null(stmt, "failuremsg")) in->failonion = NULL; else - in->failonion = db_column_onionreply(in, stmt, 8); - in->badonion = db_column_int(stmt, 9); - if (db_column_is_null(stmt, 11)) { + in->failonion = db_col_onionreply(in, stmt, "failuremsg"); + in->badonion = db_col_int(stmt, "malformed_onion"); + if (db_col_is_null(stmt, "shared_secret")) { in->shared_secret = NULL; } else { - assert(db_column_bytes(stmt, 11) == sizeof(struct secret)); + assert(db_col_bytes(stmt, "shared_secret") == sizeof(struct secret)); in->shared_secret = tal(in, struct secret); - memcpy(in->shared_secret, db_column_blob(stmt, 11), + memcpy(in->shared_secret, db_col_blob(stmt, "shared_secret"), sizeof(struct secret)); #ifdef COMPAT_V062 if (memeqzero(in->shared_secret, sizeof(*in->shared_secret))) @@ -2530,12 +2553,12 @@ static bool wallet_stmt2htlc_in(struct channel *channel, } #ifdef COMPAT_V072 - if (db_column_is_null(stmt, 12)) { + if (db_col_is_null(stmt, "received_time")) { in->received_time.ts.tv_sec = 0; in->received_time.ts.tv_nsec = 0; } else #endif /* COMPAT_V072 */ - in->received_time = db_column_timeabs(stmt, 12); + in->received_time = db_col_timeabs(stmt, "received_time"); #ifdef COMPAT_V080 /* This field is now reserved for badonion codes: the rest should @@ -2552,13 +2575,13 @@ static bool wallet_stmt2htlc_in(struct channel *channel, } #endif - if (!db_column_is_null(stmt, 13)) { + if (!db_col_is_null(stmt, "we_filled")) { in->we_filled = tal(in, bool); - *in->we_filled = db_column_int(stmt, 13); + *in->we_filled = db_col_int(stmt, "we_filled"); } else in->we_filled = NULL; - in->fail_immediate = db_column_int(stmt, 14); + in->fail_immediate = db_col_int(stmt, "fail_immediate"); return ok; } @@ -2570,42 +2593,42 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, struct htlc_in_map *unconnected_htlcs_in) { bool ok = true; - out->dbid = db_column_u64(stmt, 0); - out->key.id = db_column_u64(stmt, 1); + out->dbid = db_col_u64(stmt, "id"); + out->key.id = db_col_u64(stmt, "channel_htlc_id"); out->key.channel = channel; - db_column_amount_msat(stmt, 2, &out->msat); - out->cltv_expiry = db_column_int(stmt, 3); - out->hstate = db_column_int(stmt, 4); - db_column_sha256(stmt, 5, &out->payment_hash); + db_col_amount_msat(stmt, "msatoshi", &out->msat); + out->cltv_expiry = db_col_int(stmt, "cltv_expiry"); + out->hstate = db_col_int(stmt, "hstate"); + db_col_sha256(stmt, "payment_hash", &out->payment_hash); /* FIXME: save blinding in db !*/ out->blinding = NULL; - if (!db_column_is_null(stmt, 6)) { + if (!db_col_is_null(stmt, "payment_key")) { out->preimage = tal(out, struct preimage); - db_column_preimage(stmt, 6, out->preimage); + db_col_preimage(stmt, "payment_key", out->preimage); } else { out->preimage = NULL; } - assert(db_column_bytes(stmt, 7) == sizeof(out->onion_routing_packet)); - memcpy(&out->onion_routing_packet, db_column_blob(stmt, 7), + assert(db_col_bytes(stmt, "routing_onion") + == sizeof(out->onion_routing_packet)); + memcpy(&out->onion_routing_packet, db_col_blob(stmt, "routing_onion"), sizeof(out->onion_routing_packet)); - if (db_column_is_null(stmt, 8)) + if (db_col_is_null(stmt, "failuremsg")) out->failonion = NULL; else - out->failonion = db_column_onionreply(out, stmt, 8); + out->failonion = db_col_onionreply(out, stmt, "failuremsg"); - if (db_column_is_null(stmt, 14)) + if (db_col_is_null(stmt, "localfailmsg")) out->failmsg = NULL; else - out->failmsg = tal_dup_arr(out, u8, db_column_blob(stmt, 14), - db_column_bytes(stmt, 14), 0); + out->failmsg = db_col_arr(out, stmt, "localfailmsg", u8); out->in = NULL; - if (!db_column_is_null(stmt, 10)) { - u64 in_id = db_column_u64(stmt, 10); + if (!db_col_is_null(stmt, "origin_htlc")) { + u64 in_id = db_col_u64(stmt, "origin_htlc"); struct htlc_in *hin; hin = remove_htlc_in_by_dbid(unconnected_htlcs_in, in_id); @@ -2624,8 +2647,8 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, #endif } } else { - out->partid = db_column_u64(stmt, 13); - out->groupid = db_column_u64(stmt, 15); + out->partid = db_col_u64(stmt, "partid"); + out->groupid = db_col_u64(stmt, "groupid"); out->am_origin = true; } @@ -2686,7 +2709,7 @@ bool wallet_htlcs_load_in_for_channel(struct wallet *wallet, ", malformed_onion" ", origin_htlc" ", shared_secret" - ", received_time" + ", received_time" // 12 ", we_filled" ", fail_immediate" " FROM channel_htlcs" @@ -2867,14 +2890,14 @@ struct htlc_stub *wallet_htlc_stubs(const tal_t *ctx, struct wallet *wallet, while (db_step(stmt)) { struct htlc_stub stub; - assert(db_column_u64(stmt, 0) == chan->dbid); + assert(db_col_u64(stmt, "channel_id") == chan->dbid); /* FIXME: merge these two enums */ - stub.owner = db_column_int(stmt, 1)==DIRECTION_INCOMING?REMOTE:LOCAL; - stub.cltv_expiry = db_column_int(stmt, 2); - stub.id = db_column_u64(stmt, 3); + stub.owner = db_col_int(stmt, "direction")==DIRECTION_INCOMING?REMOTE:LOCAL; + stub.cltv_expiry = db_col_int(stmt, "cltv_expiry"); + stub.id = db_col_u64(stmt, "channel_htlc_id"); - db_column_sha256(stmt, 4, &payment_hash); + db_col_sha256(stmt, "payment_hash", &payment_hash); ripemd160(&stub.ripemd, payment_hash.u.u8, sizeof(payment_hash.u)); tal_arr_expand(&stubs, stub); } @@ -3072,8 +3095,8 @@ u64 wallet_payment_get_groupid(struct wallet *wallet, db_bind_sha256(stmt, 0, payment_hash); db_query_prepared(stmt); - if (db_step(stmt) && !db_column_is_null(stmt, 0)) { - groupid = db_column_u64(stmt, 0); + if (db_step(stmt) && !db_col_is_null(stmt, "MAX(groupid)")) { + groupid = db_col_u64(stmt, "MAX(groupid)"); } tal_free(stmt); return groupid; @@ -3094,82 +3117,83 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, struct db_stmt *stmt) { struct wallet_payment *payment = tal(ctx, struct wallet_payment); - payment->id = db_column_u64(stmt, 0); - payment->status = db_column_int(stmt, 1); + payment->id = db_col_u64(stmt, "id"); + payment->status = db_col_int(stmt, "status"); - if (!db_column_is_null(stmt, 2)) { + if (!db_col_is_null(stmt, "destination")) { payment->destination = tal(payment, struct node_id); - db_column_node_id(stmt, 2, payment->destination); + db_col_node_id(stmt, "destination", payment->destination); } else { payment->destination = NULL; } - db_column_amount_msat(stmt, 3, &payment->msatoshi); - db_column_sha256(stmt, 4, &payment->payment_hash); + db_col_amount_msat(stmt, "msatoshi", &payment->msatoshi); + db_col_sha256(stmt, "payment_hash", &payment->payment_hash); - payment->timestamp = db_column_int(stmt, 5); - if (!db_column_is_null(stmt, 6)) { + payment->timestamp = db_col_int(stmt, "timestamp"); + if (!db_col_is_null(stmt, "payment_preimage")) { payment->payment_preimage = tal(payment, struct preimage); - db_column_preimage(stmt, 6, payment->payment_preimage); + db_col_preimage(stmt, "payment_preimage", + payment->payment_preimage); } else payment->payment_preimage = NULL; /* We either used `sendpay` or `sendonion` with the `shared_secrets` * argument. */ - if (!db_column_is_null(stmt, 7)) - payment->path_secrets = db_column_secret_arr(payment, stmt, 7); + if (!db_col_is_null(stmt, "path_secrets")) + payment->path_secrets + = db_col_secret_arr(payment, stmt, "path_secrets"); else payment->path_secrets = NULL; /* Either none, or both are set */ - assert(db_column_is_null(stmt, 8) == db_column_is_null(stmt, 9)); - if (!db_column_is_null(stmt, 8)) { - payment->route_nodes = db_column_node_id_arr(payment, stmt, 8); + assert(db_col_is_null(stmt, "route_nodes") + == db_col_is_null(stmt, "route_channels")); + if (!db_col_is_null(stmt, "route_nodes")) { + payment->route_nodes + = db_col_node_id_arr(payment, stmt, "route_nodes"); payment->route_channels = - db_column_short_channel_id_arr(payment, stmt, 9); + db_col_short_channel_id_arr(payment, stmt, "route_channels"); } else { payment->route_nodes = NULL; payment->route_channels = NULL; } - db_column_amount_msat(stmt, 10, &payment->msatoshi_sent); + db_col_amount_msat(stmt, "msatoshi_sent", &payment->msatoshi_sent); - if (!db_column_is_null(stmt, 11) && db_column_text(stmt, 11) != NULL) - payment->label = - tal_strdup(payment, (const char *)db_column_text(stmt, 11)); + if (!db_col_is_null(stmt, "description")) + payment->label = db_col_strdup(payment, stmt, "description"); else payment->label = NULL; - if (!db_column_is_null(stmt, 12) && db_column_text(stmt, 12) != NULL) - payment->invstring = tal_strdup( - payment, (const char *)db_column_text(stmt, 12)); + if (!db_col_is_null(stmt, "bolt11")) + payment->invstring = db_col_strdup(payment, stmt, "bolt11"); else payment->invstring = NULL; - if (!db_column_is_null(stmt, 13)) - payment->failonion = - tal_dup_arr(payment, u8, db_column_blob(stmt, 13), - db_column_bytes(stmt, 13), 0); + if (!db_col_is_null(stmt, "failonionreply")) + payment->failonion + = db_col_arr(payment, stmt, "failonionreply", u8); else payment->failonion = NULL; - if (!db_column_is_null(stmt, 14)) - db_column_amount_msat(stmt, 14, &payment->total_msat); + if (!db_col_is_null(stmt, "total_msat")) + db_col_amount_msat(stmt, "total_msat", &payment->total_msat); else payment->total_msat = AMOUNT_MSAT(0); - if (!db_column_is_null(stmt, 15)) - payment->partid = db_column_u64(stmt, 15); + if (!db_col_is_null(stmt, "partid")) + payment->partid = db_col_u64(stmt, "partid"); else payment->partid = 0; - if (!db_column_is_null(stmt, 16)) { + if (!db_col_is_null(stmt, "local_offer_id")) { payment->local_offer_id = tal(payment, struct sha256); - db_column_sha256(stmt, 16, payment->local_offer_id); + db_col_sha256(stmt, "local_offer_id", payment->local_offer_id); } else payment->local_offer_id = NULL; - payment->groupid = db_column_u64(stmt, 17); + payment->groupid = db_col_u64(stmt, "groupid"); return payment; } @@ -3292,7 +3316,6 @@ void wallet_payment_get_failinfo(const tal_t *ctx, { struct db_stmt *stmt; bool resb; - size_t len; stmt = db_prepare_v2(wallet->db, SQL("SELECT failonionreply, faildestperm" @@ -3308,40 +3331,38 @@ void wallet_payment_get_failinfo(const tal_t *ctx, resb = db_step(stmt); assert(resb); - if (db_column_is_null(stmt, 0)) + if (db_col_is_null(stmt, "failonionreply")) *failonionreply = NULL; else { - *failonionreply = db_column_onionreply(ctx, stmt, 0); + *failonionreply = db_col_onionreply(ctx, stmt, "failonionreply"); } - *faildestperm = db_column_int(stmt, 1) != 0; - *failindex = db_column_int(stmt, 2); - *failcode = (enum onion_wire) db_column_int(stmt, 3); - if (db_column_is_null(stmt, 4)) + *faildestperm = db_col_int(stmt, "faildestperm") != 0; + *failindex = db_col_int(stmt, "failindex"); + *failcode = (enum onion_wire) db_col_int(stmt, "failcode"); + if (db_col_is_null(stmt, "failnode")) *failnode = NULL; else { *failnode = tal(ctx, struct node_id); - db_column_node_id(stmt, 4, *failnode); + db_col_node_id(stmt, "failnode", *failnode); } - if (db_column_is_null(stmt, 5)) + if (db_col_is_null(stmt, "failchannel")) *failchannel = NULL; else { *failchannel = tal(ctx, struct short_channel_id); - resb = db_column_short_channel_id(stmt, 5, *failchannel); + resb = db_col_short_channel_id_str(stmt, "failchannel", + *failchannel); assert(resb); /* For pre-0.6.2 dbs, direction will be 0 */ - *faildirection = db_column_int(stmt, 8); + *faildirection = db_col_int(stmt, "faildirection"); } - if (db_column_is_null(stmt, 6)) + if (db_col_is_null(stmt, "failupdate")) *failupdate = NULL; else { - len = db_column_bytes(stmt, 6); - *failupdate = tal_arr(ctx, u8, len); - memcpy(*failupdate, db_column_blob(stmt, 6), len); + *failupdate = db_col_arr(ctx, stmt, "failupdate", u8); } - if (!db_column_is_null(stmt, 7)) - *faildetail = tal_strndup(ctx, db_column_blob(stmt, 7), - db_column_bytes(stmt, 7)); + if (!db_col_is_null(stmt, "faildetail")) + *faildetail = db_col_strdup(ctx, stmt, "faildetail"); else *faildetail = NULL; @@ -3560,7 +3581,7 @@ bool wallet_network_check(struct wallet *w) db_query_prepared(stmt); if (db_step(stmt)) { - db_column_sha256d(stmt, 0, &chainhash.shad); + db_col_sha256d(stmt, "blobval", &chainhash.shad); tal_free(stmt); if (!bitcoin_blkid_eq(&chainhash, &chainparams->genesis_blockhash)) { @@ -3603,8 +3624,8 @@ static void wallet_utxoset_prune(struct wallet *w, const u32 blockheight) while (db_step(stmt)) { struct bitcoin_outpoint outpoint; - db_column_txid(stmt, 0, &outpoint.txid); - outpoint.n = db_column_int(stmt, 1); + db_col_txid(stmt, "txid", &outpoint.txid); + outpoint.n = db_col_int(stmt, "outnum"); outpointfilter_remove(w->utxoset_outpoints, &outpoint); } tal_free(stmt); @@ -3805,14 +3826,13 @@ struct outpoint *wallet_outpoint_for_scid(struct wallet *w, tal_t *ctx, op->blockheight = short_channel_id_blocknum(scid); op->txindex = short_channel_id_txnum(scid); op->outpoint.n = short_channel_id_outnum(scid); - db_column_txid(stmt, 0, &op->outpoint.txid); - if (db_column_is_null(stmt, 1)) + db_col_txid(stmt, "txid", &op->outpoint.txid); + if (db_col_is_null(stmt, "spendheight")) op->spendheight = 0; else - op->spendheight = db_column_int(stmt, 1); - op->scriptpubkey = tal_arr(op, u8, db_column_bytes(stmt, 2)); - memcpy(op->scriptpubkey, db_column_blob(stmt, 2), db_column_bytes(stmt, 2)); - db_column_amount_sat(stmt, 3, &op->sat); + op->spendheight = db_col_int(stmt, "spendheight"); + op->scriptpubkey = db_col_arr(op, stmt, "scriptpubkey", u8); + db_col_amount_sat(stmt, "satoshis", &op->sat); tal_free(stmt); return op; @@ -3828,9 +3848,9 @@ static const struct short_channel_id *db_scids(const tal_t *ctx, struct short_channel_id scid; u64 blocknum, txnum, outnum; bool ok; - blocknum = db_column_int(stmt, 0); - txnum = db_column_int(stmt, 1); - outnum = db_column_int(stmt, 2); + blocknum = db_col_int(stmt, "blockheight"); + txnum = db_col_int(stmt, "txindex"); + outnum = db_col_int(stmt, "outnum"); ok = mk_short_channel_id(&scid, blocknum, txnum, outnum); assert(ok); @@ -3969,11 +3989,11 @@ void wallet_transaction_annotate(struct wallet *w, fatal("Attempting to annotate a transaction we don't have: %s", type_to_string(tmpctx, struct bitcoin_txid, txid)); - if (!db_column_is_null(stmt, 0)) - type |= db_column_u64(stmt, 0); + if (!db_col_is_null(stmt, "type")) + type |= db_col_u64(stmt, "type"); - if (channel_id == 0 && !db_column_is_null(stmt, 1)) - channel_id = db_column_u64(stmt, 1); + if (channel_id == 0 && !db_col_is_null(stmt, "channel_id")) + channel_id = db_col_u64(stmt, "channel_id"); tal_free(stmt); @@ -4005,8 +4025,8 @@ bool wallet_transaction_type(struct wallet *w, const struct bitcoin_txid *txid, return false; } - if (!db_column_is_null(stmt, 0)) - *type = db_column_u64(stmt, 0); + if (!db_col_is_null(stmt, "type")) + *type = db_col_u64(stmt, "type"); else *type = 0; @@ -4028,8 +4048,8 @@ struct bitcoin_tx *wallet_transaction_get(const tal_t *ctx, struct wallet *w, return NULL; } - if (!db_column_is_null(stmt, 0)) - tx = db_column_tx(ctx, stmt, 0); + if (!db_col_is_null(stmt, "rawtx")) + tx = db_col_tx(ctx, stmt, "rawtx"); else tx = NULL; @@ -4050,8 +4070,8 @@ u32 wallet_transaction_height(struct wallet *w, const struct bitcoin_txid *txid) return 0; } - if (!db_column_is_null(stmt, 0)) - blockheight = db_column_int(stmt, 0); + if (!db_col_is_null(stmt, "blockheight")) + blockheight = db_col_int(stmt, "blockheight"); else blockheight = 0; tal_free(stmt); @@ -4074,12 +4094,12 @@ struct txlocator *wallet_transaction_locate(const tal_t *ctx, struct wallet *w, return NULL; } - if (db_column_is_null(stmt, 0)) + if (db_col_is_null(stmt, "blockheight")) loc = NULL; else { loc = tal(ctx, struct txlocator); - loc->blkheight = db_column_int(stmt, 0); - loc->index = db_column_int(stmt, 1); + loc->blkheight = db_col_int(stmt, "blockheight"); + loc->index = db_col_int(stmt, "txindex"); } tal_free(stmt); return loc; @@ -4100,7 +4120,7 @@ struct bitcoin_txid *wallet_transactions_by_height(const tal_t *ctx, while (db_step(stmt)) { count++; tal_resize(&txids, count); - db_column_txid(stmt, 0, &txids[count-1]); + db_col_txid(stmt, "id", &txids[count-1]); } tal_free(stmt); @@ -4143,7 +4163,7 @@ u32 *wallet_onchaind_channels(struct wallet *w, while (db_step(stmt)) { count++; tal_resize(&channel_ids, count); - channel_ids[count-1] = db_column_u64(stmt, 0); + channel_ids[count-1] = db_col_u64(stmt, "DISTINCT(channel_id)"); } tal_free(stmt); @@ -4176,12 +4196,12 @@ struct channeltx *wallet_channeltxs_get(struct wallet *w, const tal_t *ctx, tal_resize(&res, count); res[count-1].channel_id = channel_id; - res[count-1].type = db_column_int(stmt, 0); - res[count-1].blockheight = db_column_int(stmt, 1); - res[count-1].tx = db_column_tx(ctx, stmt, 2); - res[count-1].input_num = db_column_int(stmt, 3); - res[count-1].depth = db_column_int(stmt, 4); - db_column_txid(stmt, 5, &res[count-1].txid); + res[count-1].type = db_col_int(stmt, "c.type"); + res[count-1].blockheight = db_col_int(stmt, "c.blockheight"); + res[count-1].tx = db_col_tx(ctx, stmt, "t.rawtx"); + res[count-1].input_num = db_col_int(stmt, "c.input_num"); + res[count-1].depth = db_col_int(stmt, "depth"); + db_col_txid(stmt, "txid", &res[count-1].txid); } tal_free(stmt); return res; @@ -4329,7 +4349,7 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w) res = db_step(stmt); assert(res); - db_column_amount_msat(stmt, 0, &total); + db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)", &total); tal_free(stmt); return total; @@ -4419,11 +4439,11 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, for (count=0; db_step(stmt); count++) { tal_resize(&results, count+1); struct forwarding *cur = &results[count]; - cur->status = db_column_int(stmt, 0); - db_column_amount_msat(stmt, 1, &cur->msat_in); + cur->status = db_col_int(stmt, "f.state"); + db_col_amount_msat(stmt, "in_msatoshi", &cur->msat_in); - if (!db_column_is_null(stmt, 2)) { - db_column_amount_msat(stmt, 2, &cur->msat_out); + if (!db_col_is_null(stmt, "out_msatoshi")) { + db_col_amount_msat(stmt, "out_msatoshi", &cur->msat_out); if (!amount_msat_sub(&cur->fee, cur->msat_in, cur->msat_out)) { log_broken(w->log, "Forwarded in %s less than out %s!", type_to_string(tmpctx, struct amount_msat, @@ -4441,35 +4461,37 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, cur->fee = AMOUNT_MSAT(0); } - if (!db_column_is_null(stmt, 3)) { + if (!db_col_is_null(stmt, "payment_hash")) { cur->payment_hash = tal(ctx, struct sha256); - db_column_sha256(stmt, 3, cur->payment_hash); + db_col_sha256(stmt, "payment_hash", cur->payment_hash); } else { cur->payment_hash = NULL; } - cur->channel_in.u64 = db_column_u64(stmt, 4); + cur->channel_in.u64 = db_col_u64(stmt, "in_channel_scid"); - if (!db_column_is_null(stmt, 5)) { - cur->channel_out.u64 = db_column_u64(stmt, 5); + if (!db_col_is_null(stmt, "out_channel_scid")) { + cur->channel_out.u64 + = db_col_u64(stmt, "out_channel_scid"); } else { assert(cur->status == FORWARD_LOCAL_FAILED); cur->channel_out.u64 = 0; } - cur->received_time = db_column_timeabs(stmt, 6); + cur->received_time = db_col_timeabs(stmt, "f.received_time"); - if (!db_column_is_null(stmt, 7)) { + if (!db_col_is_null(stmt, "f.resolved_time")) { cur->resolved_time = tal(ctx, struct timeabs); - *cur->resolved_time = db_column_timeabs(stmt, 7); + *cur->resolved_time + = db_col_timeabs(stmt, "f.resolved_time"); } else { cur->resolved_time = NULL; } - if (!db_column_is_null(stmt, 8)) { + if (!db_col_is_null(stmt, "f.failcode")) { assert(cur->status == FORWARD_FAILED || cur->status == FORWARD_LOCAL_FAILED); - cur->failcode = db_column_int(stmt, 8); + cur->failcode = db_col_int(stmt, "f.failcode"); } else { cur->failcode = 0; } @@ -4511,7 +4533,7 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t for (count = 0; db_step(stmt); count++) { struct bitcoin_txid curtxid; - db_column_txid(stmt, 0, &curtxid); + db_col_txid(stmt, "t.id", &curtxid); /* If this is a new entry, allocate it in the array and set * the common fields (all fields from the transactions table. */ @@ -4519,15 +4541,16 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t last = curtxid; tal_resize(&txs, tal_count(txs) + 1); cur = &txs[tal_count(txs) - 1]; - db_column_txid(stmt, 0, &cur->id); - cur->tx = db_column_tx(txs, stmt, 1); - cur->rawtx = tal_dup_arr(txs, u8, db_column_blob(stmt, 1), - db_column_bytes(stmt, 1), 0); + db_col_txid(stmt, "t.id", &cur->id); + cur->tx = db_col_tx(txs, stmt, "t.rawtx"); + cur->rawtx = db_col_arr(txs, stmt, "t.rawtx", u8); /* TX may be unconfirmed. */ - if (!db_column_is_null(stmt, 2)) { - cur->blockheight = db_column_int(stmt, 2); - if (!db_column_is_null(stmt, 3)) { - cur->txindex = db_column_int(stmt, 3); + if (!db_col_is_null(stmt, "t.blockheight")) { + cur->blockheight + = db_col_int(stmt, "t.blockheight"); + if (!db_col_is_null(stmt, "t.txindex")) { + cur->txindex + = db_col_int(stmt, "t.txindex"); } else { cur->txindex = 0; } @@ -4535,12 +4558,14 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t cur->blockheight = 0; cur->txindex = 0; } - if (!db_column_is_null(stmt, 4)) - cur->annotation.type = db_column_u64(stmt, 4); + if (!db_col_is_null(stmt, "txtype")) + cur->annotation.type + = db_col_u64(stmt, "txtype"); else cur->annotation.type = 0; - if (!db_column_is_null(stmt, 5)) - db_column_short_channel_id(stmt, 5, &cur->annotation.channel); + if (!db_col_is_null(stmt, "txchan")) + db_col_short_channel_id_str(stmt, "txchan", + &cur->annotation.channel); else cur->annotation.channel.u64 = 0; @@ -4554,9 +4579,10 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t /* Check if we have any annotations. If there are none the * fields are all set to null */ - if (!db_column_is_null(stmt, 6)) { - enum wallet_tx_annotation_type loc = db_column_int(stmt, 6); - int idx = db_column_int(stmt, 7); + if (!db_col_is_null(stmt, "a.location")) { + enum wallet_tx_annotation_type loc + = db_col_int(stmt, "a.location"); + int idx = db_col_int(stmt, "ann_idx"); struct tx_annotation *ann; /* Select annotation from array to fill in. */ @@ -4568,9 +4594,11 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t fatal("Transaction annotations are only available for inputs and outputs. Value %d", loc); /* cppcheck-suppress uninitvar - false positive on fatal() above */ - ann->type = db_column_int(stmt, 8); - if (!db_column_is_null(stmt, 9)) - db_column_short_channel_id(stmt, 9, &ann->channel); + ann->type = db_col_int(stmt, "annotation_type"); + if (!db_col_is_null(stmt, "c.short_channel_id")) + db_col_short_channel_id_str(stmt, + "c.short_channel_id", + &ann->channel); else ann->channel.u64 = 0; } @@ -4618,10 +4646,10 @@ struct penalty_base *wallet_penalty_base_load_for_channel(const tal_t *ctx, while (db_step(stmt)) { struct penalty_base pb; - pb.commitment_num = db_column_u64(stmt, 0); - db_column_txid(stmt, 1, &pb.txid); - pb.outnum = db_column_int(stmt, 2); - db_column_amount_sat(stmt, 3, &pb.amount); + pb.commitment_num = db_col_u64(stmt, "commitnum"); + db_col_txid(stmt, "txid", &pb.txid); + pb.outnum = db_col_int(stmt, "outnum"); + db_col_amount_sat(stmt, "amount", &pb.amount); tal_arr_expand(&res, pb); } tal_free(stmt); @@ -4702,15 +4730,15 @@ char *wallet_offer_find(const tal_t *ctx, return NULL; } - bolt12 = tal_strdup(ctx, cast_signed(const char *, db_column_text(stmt, 0))); + bolt12 = db_col_strdup(ctx, stmt, "bolt12"); if (label) { - if (db_column_is_null(stmt, 1)) + if (db_col_is_null(stmt, "label")) *label = NULL; else - *label = db_column_json_escape(ctx, stmt, 1); + *label = db_col_json_escape(ctx, stmt, "label"); } if (status) - *status = offer_status_in_db(db_column_int(stmt, 2)); + *status = offer_status_in_db(db_col_int(stmt, "status")); tal_free(stmt); return bolt12; } @@ -4732,7 +4760,7 @@ struct db_stmt *wallet_offer_id_next(struct wallet *w, if (!db_step(stmt)) return tal_free(stmt); - db_column_sha256(stmt, 0, offer_id); + db_col_sha256(stmt, "offer_id", offer_id); return stmt; } @@ -4794,7 +4822,7 @@ void wallet_offer_mark_used(struct db *db, const struct sha256 *offer_id) __func__, type_to_string(tmpctx, struct sha256, offer_id)); - status = offer_status_in_db(db_column_int(stmt, 0)); + status = offer_status_in_db(db_col_int(stmt, "status")); tal_free(stmt); if (!offer_status_active(status)) @@ -4839,13 +4867,13 @@ static void db_bind_datastore_key(struct db_stmt *stmt, db_bind_blob(stmt, pos, joined, len); } -static const char **db_column_datastore_key(const tal_t *ctx, - struct db_stmt *stmt, - int col) +static const char **db_col_datastore_key(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) { char **key; - const u8 *joined = db_column_blob(stmt, col); - size_t len = db_column_bytes(stmt, col); + const u8 *joined = db_col_blob(stmt, colname); + size_t len = db_col_bytes(stmt, colname); key = tal_arr(ctx, char *, 0); do { @@ -4934,11 +4962,11 @@ struct db_stmt *wallet_datastore_next(const tal_t *ctx, if (!db_step(stmt)) return tal_free(stmt); - *key = db_column_datastore_key(ctx, stmt, 0); + *key = db_col_datastore_key(ctx, stmt, "key"); if (data) - *data = db_column_talarr(ctx, stmt, 1); + *data = db_col_arr(ctx, stmt, "data", u8); if (generation) - *generation = db_column_u64(stmt, 2); + *generation = db_col_u64(stmt, "generation"); return stmt; } From 663c8c6c02a26bf44a6f5f307384e21006334df2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:28:46 +1030 Subject: [PATCH 0029/1530] wallet: convert db internal routines to db_col_ Signed-off-by: Rusty Russell --- wallet/db.c | 64 ++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/wallet/db.c b/wallet/db.c index 9f53bff77b56..aa6fedc4de1a 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1201,7 +1201,7 @@ static int db_get_version(struct db *db) } if (db_step(stmt)) - res = db_column_int(stmt, 0); + res = db_col_int(stmt, "version"); tal_free(stmt); return res; @@ -1271,7 +1271,7 @@ u32 db_data_version_get(struct db *db) stmt = db_prepare_v2(db, SQL("SELECT intval FROM vars WHERE name = 'data_version'")); db_query_prepared(stmt); db_step(stmt); - version = db_column_int(stmt, 0); + version = db_col_int(stmt, "intval"); tal_free(stmt); return version; } @@ -1309,7 +1309,7 @@ s64 db_get_intvar(struct db *db, char *varname, s64 defval) goto done; if (db_step(stmt)) - res = db_column_int(stmt, 0); + res = db_col_int(stmt, "intval"); done: tal_free(stmt); @@ -1399,23 +1399,23 @@ void fillin_missing_scriptpubkeys(struct lightningd *ld, struct db *db, struct pubkey key; struct db_stmt *update_stmt; - type = db_column_int(stmt, 0); - keyindex = db_column_int(stmt, 1); - db_column_txid(stmt, 2, &txid); - outnum = db_column_int(stmt, 3); + type = db_col_int(stmt, "type"); + keyindex = db_col_int(stmt, "keyindex"); + db_col_txid(stmt, "prev_out_tx", &txid); + outnum = db_col_int(stmt, "prev_out_index"); /* This indiciates whether or not we have 'close_info' */ - if (!db_column_is_null(stmt, 4)) { + if (!db_col_is_null(stmt, "channel_id")) { struct pubkey *commitment_point; struct node_id peer_id; u64 channel_id; u8 *msg; - channel_id = db_column_u64(stmt, 4); - db_column_node_id(stmt, 5, &peer_id); - if (!db_column_is_null(stmt, 6)) { + channel_id = db_col_u64(stmt, "channel_id"); + db_col_node_id(stmt, "peer_id", &peer_id); + if (!db_col_is_null(stmt, "commitment_point")) { commitment_point = tal(stmt, struct pubkey); - db_column_pubkey(stmt, 6, commitment_point); + db_col_pubkey(stmt, "commitment_point", commitment_point); } else commitment_point = NULL; @@ -1479,9 +1479,9 @@ static void fillin_missing_channel_id(struct lightningd *ld, struct db *db, struct bitcoin_outpoint funding; struct channel_id cid; - id = db_column_u64(stmt, 0); - db_column_txid(stmt, 1, &funding.txid); - funding.n = db_column_int(stmt, 2); + id = db_col_u64(stmt, "id"); + db_col_txid(stmt, "funding_tx_id", &funding.txid); + funding.n = db_col_int(stmt, "funding_tx_outnum"); derive_channel_id(&cid, &funding); update_stmt = db_prepare_v2(db, SQL("UPDATE channels" @@ -1522,8 +1522,8 @@ static void fillin_missing_local_basepoints(struct lightningd *ld, struct basepoints base; struct pubkey funding_pubkey; - dbid = db_column_u64(stmt, 0); - db_column_node_id(stmt, 1, &peer_id); + dbid = db_col_u64(stmt, "channels.id"); + db_col_node_id(stmt, "peers.node_id", &peer_id); if (!wire_sync_write(mc->hsm_fd, towire_hsmd_get_channel_basepoints( @@ -1619,19 +1619,19 @@ migrate_inflight_last_tx_to_psbt(struct lightningd *ld, struct db *db, u64 cdb_id; u8 *funding_wscript; - cdb_id = db_column_u64(stmt, 0); - last_tx = db_column_tx(stmt, stmt, 3); + cdb_id = db_col_u64(stmt, "c.id"); + last_tx = db_col_tx(stmt, stmt, "inflight.last_tx"); assert(last_tx != NULL); /* If we've forgotten about the peer_id * because we closed / forgot the channel, * we can skip this. */ - if (db_column_is_null(stmt, 1)) + if (db_col_is_null(stmt, "p.node_id")) continue; - db_column_node_id(stmt, 1, &peer_id); - db_column_amount_sat(stmt, 5, &funding_sat); - db_column_pubkey(stmt, 2, &remote_funding_pubkey); - db_column_txid(stmt, 6, &funding_txid); + db_col_node_id(stmt, "p.node_id", &peer_id); + db_col_amount_sat(stmt, "inflight.funding_satoshi", &funding_sat); + db_col_pubkey(stmt, "c.fundingkey_remote", &remote_funding_pubkey); + db_col_txid(stmt, "inflight.funding_tx_id", &funding_txid); get_channel_basepoints(ld, &peer_id, cdb_id, &local_basepoints, &local_funding_pubkey); @@ -1645,7 +1645,7 @@ migrate_inflight_last_tx_to_psbt(struct lightningd *ld, struct db *db, funding_sat); psbt_input_set_witscript(last_tx->psbt, 0, funding_wscript); - if (!db_column_signature(stmt, 4, &last_sig.s)) + if (!db_col_signature(stmt, "inflight.last_sig", &last_sig.s)) abort(); last_sig.sighash_type = SIGHASH_ALL; @@ -1704,18 +1704,18 @@ void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db, u64 cdb_id; u8 *funding_wscript; - cdb_id = db_column_u64(stmt, 0); - last_tx = db_column_tx(stmt, stmt, 2); + cdb_id = db_col_u64(stmt, "c.id"); + last_tx = db_col_tx(stmt, stmt, "c.last_tx"); assert(last_tx != NULL); /* If we've forgotten about the peer_id * because we closed / forgot the channel, * we can skip this. */ - if (db_column_is_null(stmt, 1)) + if (db_col_is_null(stmt, "p.node_id")) continue; - db_column_node_id(stmt, 1, &peer_id); - db_column_amount_sat(stmt, 3, &funding_sat); - db_column_pubkey(stmt, 4, &remote_funding_pubkey); + db_col_node_id(stmt, "p.node_id", &peer_id); + db_col_amount_sat(stmt, "c.funding_satoshi", &funding_sat); + db_col_pubkey(stmt, "c.fundingkey_remote", &remote_funding_pubkey); get_channel_basepoints(ld, &peer_id, cdb_id, &local_basepoints, &local_funding_pubkey); @@ -1737,7 +1737,7 @@ void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db, } - if (!db_column_signature(stmt, 5, &last_sig.s)) + if (!db_col_signature(stmt, "c.last_sig", &last_sig.s)) abort(); last_sig.sighash_type = SIGHASH_ALL; From 738261651399c66b1581e436adb77f7eb6d203ff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:29:46 +1030 Subject: [PATCH 0030/1530] CCAN: import strset. Signed-off-by: Rusty Russell --- Makefile | 4 + ccan/ccan/strset/_info | 72 +++ ccan/ccan/strset/strset.c | 309 ++++++++++++ ccan/ccan/strset/strset.h | 167 +++++++ ccan/ccan/strset/test/run-hibit.c | 82 +++ ccan/ccan/strset/test/run-iterate-const.c | 30 ++ ccan/ccan/strset/test/run-order.c | 81 +++ ccan/ccan/strset/test/run-prefix.c | 87 ++++ ccan/ccan/strset/test/run.c | 70 +++ ccan/ccan/strset/tools/Makefile | 31 ++ ccan/ccan/strset/tools/cbspeed.c | 583 ++++++++++++++++++++++ ccan/ccan/strset/tools/speed.c | 237 +++++++++ 12 files changed, 1753 insertions(+) create mode 100644 ccan/ccan/strset/_info create mode 100644 ccan/ccan/strset/strset.c create mode 100644 ccan/ccan/strset/strset.h create mode 100644 ccan/ccan/strset/test/run-hibit.c create mode 100644 ccan/ccan/strset/test/run-iterate-const.c create mode 100644 ccan/ccan/strset/test/run-order.c create mode 100644 ccan/ccan/strset/test/run-prefix.c create mode 100644 ccan/ccan/strset/test/run.c create mode 100644 ccan/ccan/strset/tools/Makefile create mode 100644 ccan/ccan/strset/tools/cbspeed.c create mode 100644 ccan/ccan/strset/tools/speed.c diff --git a/Makefile b/Makefile index a4725f9815f3..c95e8156ca90 100644 --- a/Makefile +++ b/Makefile @@ -132,6 +132,7 @@ CCAN_OBJS := \ ccan-str-hex.o \ ccan-str.o \ ccan-strmap.o \ + ccan-strset.o \ ccan-take.o \ ccan-tal-grab_file.o \ ccan-tal-link.o \ @@ -200,6 +201,7 @@ CCAN_HEADERS := \ $(CCANDIR)/ccan/str/str.h \ $(CCANDIR)/ccan/str/str_debug.h \ $(CCANDIR)/ccan/strmap/strmap.h \ + $(CCANDIR)/ccan/strset/strset.h \ $(CCANDIR)/ccan/structeq/structeq.h \ $(CCANDIR)/ccan/take/take.h \ $(CCANDIR)/ccan/tal/grab_file/grab_file.h \ @@ -815,6 +817,8 @@ ccan-cdump.o: $(CCANDIR)/ccan/cdump/cdump.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) ccan-strmap.o: $(CCANDIR)/ccan/strmap/strmap.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) +ccan-strset.o: $(CCANDIR)/ccan/strset/strset.c + @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) ccan-crypto-siphash24.o: $(CCANDIR)/ccan/crypto/siphash24/siphash24.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) ccan-htable.o: $(CCANDIR)/ccan/htable/htable.c diff --git a/ccan/ccan/strset/_info b/ccan/ccan/strset/_info new file mode 100644 index 000000000000..982a9c0f851a --- /dev/null +++ b/ccan/ccan/strset/_info @@ -0,0 +1,72 @@ +#include "config.h" +#include +#include + +/** + * strset - an ordered set of strings + * + * This code implements an ordered set of string as a critbit tree. See: + * + * http://cr.yp.to/critbit.html + * http://github.com/agl/critbit (which this code is based on) + * + * Note that ccan/htable is faster and uses less memory, but doesn't provide + * ordered or prefix operations. + * + * Example: + * // Print all words in order. + * #include + * #include + * #include + * #include + * + * static bool dump(const char *member, void *unused) + * { + * printf("%s ", member); + * return true; // Keep going with iteration. + * } + * + * int main(void) + * { + * struct strset words; + * char *file, *word; + * + * strset_init(&words); + * file = grab_fd(NULL, 0); + * if (!file) + * err(1, "Reading stdin"); + * + * for (word = strtok(file, " \t\r\n"); + * word; + * word = strtok(NULL, " \t\r\n")) { + * strset_add(&words, word); + * } + * strset_iterate(&words, dump, NULL); + * printf("\n"); + * return 0; + * } + * // Given "foo bar" outputs "bar foo \n" + * // Given "foo foo bar" outputs "bar foo \n" + * + * License: CC0 (but some dependencies are LGPL!) + * Author: Rusty Russell + * Ccanlint: + * license_depends_compat FAIL + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/ilog\n" + "ccan/likely\n" + "ccan/short_types\n" + "ccan/str\n" + "ccan/typesafe_cb\n"); + return 0; + } + + return 1; +} diff --git a/ccan/ccan/strset/strset.c b/ccan/ccan/strset/strset.c new file mode 100644 index 000000000000..06b0d7a76c35 --- /dev/null +++ b/ccan/ccan/strset/strset.c @@ -0,0 +1,309 @@ +/* This code is based on the public domain code at + * http://github.com/agl/critbit writtem by Adam Langley + * . + * + * Here are the main implementation differences: + * (1) We don't strdup the string on insert; we use the pointer we're given. + * (2) We use a straight bit number rather than a mask; it's simpler. + * (3) We don't use the bottom bit of the pointer, but instead use a leading + * zero to distinguish nodes from strings. + * (4) The empty string (which would look like a node) is handled + * using a special "empty node". + * (5) Delete returns the string, so you can free it if you want to. + * (6) Unions instead of void *, bool instead of int. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct node { + /* To differentiate us from strings. */ + char nul_byte; + /* The bit where these children differ. */ + u8 bit_num; + /* The byte number where first bit differs (-1 == empty string node). */ + size_t byte_num; + /* These point to strings or nodes. */ + struct strset child[2]; +}; + +/* Closest member to this in a non-empty set. */ +static const char *closest(struct strset n, const char *member) +{ + size_t len = strlen(member); + const u8 *bytes = (const u8 *)member; + + /* Anything with first byte 0 is a node. */ + while (!n.u.s[0]) { + u8 direction = 0; + + /* Special node which represents the empty string. */ + if (unlikely(n.u.n->byte_num == (size_t)-1)) { + n = n.u.n->child[0]; + break; + } + + if (n.u.n->byte_num < len) { + u8 c = bytes[n.u.n->byte_num]; + direction = (c >> n.u.n->bit_num) & 1; + } + n = n.u.n->child[direction]; + } + return n.u.s; +} + +char *strset_get(const struct strset *set, const char *member) +{ + const char *str; + + /* Non-empty set? */ + if (set->u.n) { + str = closest(*set, member); + if (streq(member, str)) + return (char *)str; + } + errno = ENOENT; + return NULL; +} + +static bool set_string(struct strset *set, + struct strset *n, const char *member) +{ + /* Substitute magic empty node if this is the empty string */ + if (unlikely(!member[0])) { + n->u.n = malloc(sizeof(*n->u.n)); + if (unlikely(!n->u.n)) { + errno = ENOMEM; + return false; + } + n->u.n->nul_byte = '\0'; + n->u.n->byte_num = (size_t)-1; + /* Attach the string to child[0] */ + n = &n->u.n->child[0]; + } + n->u.s = member; + return true; +} + +bool strset_add(struct strset *set, const char *member) +{ + size_t len = strlen(member); + const u8 *bytes = (const u8 *)member; + struct strset *np; + const char *str; + struct node *newn; + size_t byte_num; + u8 bit_num, new_dir; + + /* Empty set? */ + if (!set->u.n) { + return set_string(set, set, member); + } + + /* Find closest existing member. */ + str = closest(*set, member); + + /* Find where they differ. */ + for (byte_num = 0; str[byte_num] == member[byte_num]; byte_num++) { + if (member[byte_num] == '\0') { + /* All identical! */ + errno = EEXIST; + return false; + } + } + + /* Find which bit differs (if we had ilog8, we'd use it) */ + bit_num = ilog32_nz((u8)str[byte_num] ^ bytes[byte_num]) - 1; + assert(bit_num < CHAR_BIT); + + /* Which direction do we go at this bit? */ + new_dir = ((bytes[byte_num]) >> bit_num) & 1; + + /* Allocate new node. */ + newn = malloc(sizeof(*newn)); + if (!newn) { + errno = ENOMEM; + return false; + } + newn->nul_byte = '\0'; + newn->byte_num = byte_num; + newn->bit_num = bit_num; + if (unlikely(!set_string(set, &newn->child[new_dir], member))) { + free(newn); + return false; + } + + /* Find where to insert: not closest, but first which differs! */ + np = set; + while (!np->u.s[0]) { + u8 direction = 0; + + /* Special node which represents the empty string will + * break here too! */ + if (np->u.n->byte_num > byte_num) + break; + /* Subtle: bit numbers are "backwards" for comparison */ + if (np->u.n->byte_num == byte_num && np->u.n->bit_num < bit_num) + break; + + if (np->u.n->byte_num < len) { + u8 c = bytes[np->u.n->byte_num]; + direction = (c >> np->u.n->bit_num) & 1; + } + np = &np->u.n->child[direction]; + } + + newn->child[!new_dir]= *np; + np->u.n = newn; + return true; +} + +char *strset_del(struct strset *set, const char *member) +{ + size_t len = strlen(member); + const u8 *bytes = (const u8 *)member; + struct strset *parent = NULL, *n; + const char *ret = NULL; + u8 direction = 0; /* prevent bogus gcc warning. */ + + /* Empty set? */ + if (!set->u.n) { + errno = ENOENT; + return NULL; + } + + /* Find closest, but keep track of parent. */ + n = set; + /* Anything with first byte 0 is a node. */ + while (!n->u.s[0]) { + u8 c = 0; + + /* Special node which represents the empty string. */ + if (unlikely(n->u.n->byte_num == (size_t)-1)) { + const char *empty_str = n->u.n->child[0].u.s; + + if (member[0]) { + errno = ENOENT; + return NULL; + } + + /* Sew empty string back so remaining logic works */ + free(n->u.n); + n->u.s = empty_str; + break; + } + + parent = n; + if (n->u.n->byte_num < len) { + c = bytes[n->u.n->byte_num]; + direction = (c >> n->u.n->bit_num) & 1; + } else + direction = 0; + n = &n->u.n->child[direction]; + } + + /* Did we find it? */ + if (!streq(member, n->u.s)) { + errno = ENOENT; + return NULL; + } + + ret = n->u.s; + + if (!parent) { + /* We deleted last node. */ + set->u.n = NULL; + } else { + struct node *old = parent->u.n; + /* Raise other node to parent. */ + *parent = old->child[!direction]; + free(old); + } + + return (char *)ret; +} + +static bool iterate(struct strset n, + bool (*handle)(const char *, void *), const void *data) +{ + if (n.u.s[0]) + return handle(n.u.s, (void *)data); + if (unlikely(n.u.n->byte_num == (size_t)-1)) + return handle(n.u.n->child[0].u.s, (void *)data); + + return iterate(n.u.n->child[0], handle, data) + && iterate(n.u.n->child[1], handle, data); +} + +void strset_iterate_(const struct strset *set, + bool (*handle)(const char *, void *), const void *data) +{ + /* Empty set? */ + if (!set->u.n) + return; + + iterate(*set, handle, data); +} + +const struct strset *strset_prefix(const struct strset *set, const char *prefix) +{ + const struct strset *n, *top; + size_t len = strlen(prefix); + const u8 *bytes = (const u8 *)prefix; + + /* Empty set -> return empty set. */ + if (!set->u.n) + return set; + + top = n = set; + + /* We walk to find the top, but keep going to check prefix matches. */ + while (!n->u.s[0]) { + u8 c = 0, direction; + + /* Special node which represents the empty string. */ + if (unlikely(n->u.n->byte_num == (size_t)-1)) { + n = &n->u.n->child[0]; + break; + } + + if (n->u.n->byte_num < len) + c = bytes[n->u.n->byte_num]; + + direction = (c >> n->u.n->bit_num) & 1; + n = &n->u.n->child[direction]; + if (c) + top = n; + } + + if (!strstarts(n->u.s, prefix)) { + /* Convenient return for prefixes which do not appear in set. */ + static const struct strset empty_set; + return &empty_set; + } + + return top; +} + +static void clear(struct strset n) +{ + if (!n.u.s[0]) { + if (likely(n.u.n->byte_num != (size_t)-1)) { + clear(n.u.n->child[0]); + clear(n.u.n->child[1]); + } + free(n.u.n); + } +} + +void strset_clear(struct strset *set) +{ + if (set->u.n) + clear(*set); + set->u.n = NULL; +} diff --git a/ccan/ccan/strset/strset.h b/ccan/ccan/strset/strset.h new file mode 100644 index 000000000000..9d6f1ae343f5 --- /dev/null +++ b/ccan/ccan/strset/strset.h @@ -0,0 +1,167 @@ +#ifndef CCAN_STRSET_H +#define CCAN_STRSET_H +#include "config.h" +#include +#include +#include + +/** + * struct strset - representation of a string set + * + * It's exposed here to allow you to embed it and so we can inline the + * trivial functions. + */ +struct strset { + union { + struct node *n; + const char *s; + } u; +}; + +/** + * strset_init - initialize a string set (empty) + * + * For completeness; if you've arranged for it to be NULL already you don't + * need this. + * + * Example: + * struct strset set; + * + * strset_init(&set); + */ +static inline void strset_init(struct strset *set) +{ + set->u.n = NULL; +} + +/** + * strset_empty - is this string set empty? + * @set: the set. + * + * Example: + * if (!strset_empty(&set)) + * abort(); + */ +static inline bool strset_empty(const struct strset *set) +{ + return set->u.n == NULL; +} + +/** + * strset_get - is this a member of this string set? + * @set: the set. + * @member: the string to search for. + * + * Returns the member, or NULL if it isn't in the set (and sets errno + * = ENOENT). + * + * Example: + * if (strset_get(&set, "hello")) + * printf("hello is in the set\n"); + */ +char *strset_get(const struct strset *set, const char *member); + +/** + * strset_add - place a member in the string set. + * @set: the set. + * @member: the string to place in the set. + * + * This returns false if we run out of memory (errno = ENOMEM), or + * (more normally) if that string already appears in the set (EEXIST). + * + * Note that the pointer is placed in the set, the string is not copied. If + * you want a copy in the set, use strdup(). + * + * Example: + * if (!strset_add(&set, "goodbye")) + * printf("goodbye was already in the set\n"); + */ +bool strset_add(struct strset *set, const char *member); + +/** + * strset_del - remove a member from the string set. + * @set: the set. + * @member: the string to remove from the set. + * + * This returns the string which was passed to strset_add(), or NULL if + * the string was not in the map (in which case it sets errno = ENOENT). + * + * This means that if you allocated a string (eg. using strdup()), you can + * free it here. + * + * Example: + * if (!strset_del(&set, "goodbye")) + * printf("goodbye was not in the set?\n"); + */ +char *strset_del(struct strset *set, const char *member); + +/** + * strset_clear - remove every member from the set. + * @set: the set. + * + * The set will be empty after this. + * + * Example: + * strset_clear(&set); + */ +void strset_clear(struct strset *set); + +/** + * strset_iterate - ordered iteration over a set + * @set: the set. + * @handle: the function to call. + * @arg: the argument for the function (types should match). + * + * You should not alter the set within the @handle function! If it returns + * false, the iteration will stop. + * + * Example: + * static bool dump_some(const char *member, int *num) + * { + * // Only dump out num nodes. + * if (*(num--) == 0) + * return false; + * printf("%s\n", member); + * return true; + * } + * + * static void dump_set(const struct strset *set) + * { + * int max = 100; + * strset_iterate(set, dump_some, &max); + * if (max < 0) + * printf("... (truncated to 100 entries)\n"); + * } + */ +#define strset_iterate(set, handle, arg) \ + strset_iterate_((set), typesafe_cb_preargs(bool, void *, \ + (handle), (arg), \ + const char *), \ + (arg)) +void strset_iterate_(const struct strset *set, + bool (*handle)(const char *, void *), const void *data); + + +/** + * strset_prefix - return a subset matching a prefix + * @set: the set. + * @prefix: the prefix. + * + * This returns a pointer into @set, so don't alter @set while using + * the return value. You can use strset_iterate(), strset_test() or + * strset_empty() on the returned pointer. + * + * Example: + * static void dump_prefix(const struct strset *set, const char *prefix) + * { + * int max = 100; + * printf("Nodes with prefix %s:\n", prefix); + * strset_iterate(strset_prefix(set, prefix), dump_some, &max); + * if (max < 0) + * printf("... (truncated to 100 entries)\n"); + * } + */ +const struct strset *strset_prefix(const struct strset *set, + const char *prefix); + +#endif /* CCAN_STRSET_H */ diff --git a/ccan/ccan/strset/test/run-hibit.c b/ccan/ccan/strset/test/run-hibit.c new file mode 100644 index 000000000000..82f4c9225147 --- /dev/null +++ b/ccan/ccan/strset/test/run-hibit.c @@ -0,0 +1,82 @@ +/* Test high bit handling. */ +#include +#include +#include + +#define NUM 1000 + +static void encode(char template[3], unsigned int val) +{ + assert(val < 255 * 255); + template[0] = (val / 255) + 1; + template[1] = (val % 255) + 1; + template[2] = '\0'; +} + +static bool in_order(const char *value, unsigned int *count) +{ + char template[3]; + encode(template, *count); + ok1(streq(value, template)); + (*count)++; + return true; +} + +static bool in_order_by_2(const char *value, unsigned int *count) +{ + char template[3]; + encode(template, *count); + ok1(streq(value, template)); + (*count) += 2; + return true; +} + +static bool dump(const char *value, void *unused) +{ + diag("%s", value); + return true; +} + +int main(void) +{ + struct strset set; + unsigned int i; + char *str[NUM]; + + plan_tests(NUM + 3 * NUM / 2); + strset_init(&set); + + for (i = 0; i < NUM; i++) { + char template[3]; + encode(template, i); + str[i] = strdup(template); + } + + for (i = 0; i < NUM; i++) + strset_add(&set, str[i]); + + strset_iterate(&set, dump, NULL); + + /* Iterate. */ + i = 0; + strset_iterate(&set, in_order, &i); + + /* Preserve order after deletion. */ + for (i = 0; i < NUM; i += 2) + ok1(strset_del(&set, str[i]) == str[i]); + + i = 1; + strset_iterate(&set, in_order_by_2, &i); + + for (i = 1; i < NUM; i += 2) + ok1(strset_del(&set, str[i]) == str[i]); + + /* empty traverse. */ + strset_iterate(&set, in_order_by_2, (unsigned int *)NULL); + + for (i = 0; i < NUM; i++) + free(str[i]); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/strset/test/run-iterate-const.c b/ccan/ccan/strset/test/run-iterate-const.c new file mode 100644 index 000000000000..9f2b13e26349 --- /dev/null +++ b/ccan/ccan/strset/test/run-iterate-const.c @@ -0,0 +1,30 @@ +#include +#include +#include + +static bool found = false; + +/* Make sure const args work. */ +static bool find_string(const char *str, const char *cmp) +{ + if (strcmp(str, cmp) == 0) + found = true; + return true; +} + +int main(void) +{ + struct strset set; + + plan_tests(3); + + strset_init(&set); + ok1(strset_add(&set, "hello")); + ok1(strset_add(&set, "world")); + strset_iterate(&set, find_string, (const char *)"hello"); + ok1(found); + strset_clear(&set); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/strset/test/run-order.c b/ccan/ccan/strset/test/run-order.c new file mode 100644 index 000000000000..910a9f7a2b55 --- /dev/null +++ b/ccan/ccan/strset/test/run-order.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include + +#define NUM 1000 + +static bool in_order(const char *value, unsigned int *count) +{ + int i = atoi(value); + ok1(*count == i); + (*count)++; + return true; +} + +static bool in_order_by_2(const char *value, unsigned int *count) +{ + int i = atoi(value); + ok1(*count == i); + (*count) += 2; + return true; +} + +static bool dump(const char *value, void *unused) +{ + diag("%s", value); + return true; +} + +int main(void) +{ + struct strset set; + unsigned int i; + char *str[NUM]; + + plan_tests(NUM * 2 + 3 * NUM / 2); + strset_init(&set); + + for (i = 0; i < NUM; i++) { + char template[10]; + sprintf(template, "%08u", i); + str[i] = strdup(template); + } + + for (i = 0; i < NUM; i++) + strset_add(&set, str[i]); + + strset_iterate(&set, dump, NULL); + + /* Iterate. */ + i = 0; + strset_iterate(&set, in_order, &i); + + /* Preserve order after deletion. */ + for (i = 0; i < NUM; i += 2) + ok1(strset_del(&set, str[i]) == str[i]); + + i = 1; + strset_iterate(&set, in_order_by_2, &i); + + for (i = 1; i < NUM; i += 2) + ok1(strset_del(&set, str[i]) == str[i]); + + /* empty traverse. */ + strset_iterate(&set, in_order_by_2, (unsigned int *)NULL); + + /* insert backwards, should be fine. */ + for (i = 0; i < NUM; i++) + strset_add(&set, str[NUM-1-i]); + + i = 0; + strset_iterate(&set, in_order, &i); + + strset_clear(&set); + + for (i = 0; i < NUM; i++) + free(str[i]); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/strset/test/run-prefix.c b/ccan/ccan/strset/test/run-prefix.c new file mode 100644 index 000000000000..e88f2dd0c141 --- /dev/null +++ b/ccan/ccan/strset/test/run-prefix.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +/* Must be > 100, see below. */ +#define NUM 200 + +static bool in_order(const char *value, unsigned int *count) +{ + int i = atoi(value); + ok1(*count == i); + (*count)++; + return true; +} + +static bool find_empty(const char *value, char *empty) +{ + if (value == empty) + pass("Found empty entry!"); + return true; +} + +int main(void) +{ + struct strset set; + const struct strset *sub; + unsigned int i; + char *str[NUM], *empty; + + plan_tests(7 + 1 + 10 + 100); + strset_init(&set); + + for (i = 0; i < NUM; i++) { + char template[10]; + sprintf(template, "%08u", i); + str[i] = strdup(template); + } + + for (i = 0; i < NUM; i++) + strset_add(&set, str[i]); + + /* Nothing */ + sub = strset_prefix(&set, "a"); + ok1(strset_empty(sub)); + + /* Everything */ + sub = strset_prefix(&set, "0"); + ok1(sub->u.n == set.u.n); + sub = strset_prefix(&set, ""); + ok1(sub->u.n == set.u.n); + + /* Singleton. */ + sub = strset_prefix(&set, "00000000"); + i = 0; + strset_iterate(sub, in_order, &i); + ok1(i == 1); + + /* First 10. */ + sub = strset_prefix(&set, "0000000"); + i = 0; + strset_iterate(sub, in_order, &i); + ok1(i == 10); + + /* First 100. */ + sub = strset_prefix(&set, "000000"); + i = 0; + strset_iterate(sub, in_order, &i); + ok1(i == 100); + + /* Everything, *plus* empty string. */ + empty = strdup(""); + strset_add(&set, empty); + + sub = strset_prefix(&set, ""); + /* Check we get *our* empty string back! */ + strset_iterate(sub, find_empty, empty); + + strset_clear(&set); + + for (i = 0; i < NUM; i++) + free(str[i]); + free(empty); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/strset/test/run.c b/ccan/ccan/strset/test/run.c new file mode 100644 index 000000000000..a84172d9e1f9 --- /dev/null +++ b/ccan/ccan/strset/test/run.c @@ -0,0 +1,70 @@ +#include +#include +#include + +int main(void) +{ + struct strset set; + const char str[] = "hello"; + const char none[] = ""; + char *dup = strdup(str); + + /* This is how many tests you plan to run */ + plan_tests(36); + + strset_init(&set); + + ok1(!strset_get(&set, str)); + ok1(errno == ENOENT); + ok1(!strset_get(&set, none)); + ok1(errno == ENOENT); + ok1(!strset_del(&set, str)); + ok1(errno == ENOENT); + ok1(!strset_del(&set, none)); + ok1(errno == ENOENT); + + ok1(strset_add(&set, str)); + ok1(strset_get(&set, str)); + /* We compare the string, not the pointer. */ + ok1(strset_get(&set, dup)); + ok1(!strset_get(&set, none)); + ok1(errno == ENOENT); + + /* Add of duplicate should fail. */ + ok1(!strset_add(&set, dup)); + ok1(errno == EEXIST); + + /* Delete should return original string. */ + ok1(strset_del(&set, dup) == str); + ok1(!strset_get(&set, str)); + ok1(errno == ENOENT); + ok1(!strset_get(&set, none)); + ok1(errno == ENOENT); + + /* Try insert and delete of empty string. */ + ok1(strset_add(&set, none)); + ok1(strset_get(&set, none)); + ok1(!strset_get(&set, str)); + ok1(errno == ENOENT); + + /* Delete should return original string. */ + ok1(strset_del(&set, "") == none); + ok1(!strset_get(&set, str)); + ok1(errno == ENOENT); + ok1(!strset_get(&set, none)); + ok1(errno == ENOENT); + + /* Both at once... */ + ok1(strset_add(&set, none)); + ok1(strset_add(&set, str)); + ok1(strset_get(&set, str)); + ok1(strset_get(&set, none)); + ok1(strset_del(&set, "") == none); + ok1(strset_del(&set, dup) == str); + + ok1(set.u.n == NULL); + free(dup); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/strset/tools/Makefile b/ccan/ccan/strset/tools/Makefile new file mode 100644 index 000000000000..3222e34ec2f3 --- /dev/null +++ b/ccan/ccan/strset/tools/Makefile @@ -0,0 +1,31 @@ +CCANDIR=../../.. +CFLAGS=-Wall -Werror -O3 -I$(CCANDIR) +#CFLAGS=-Wall -Werror -g -I$(CCANDIR) + +all: cbspeed speed + +CCAN_OBJS:=ccan-tal.o ccan-tal-str.o ccan-tal-grab_file.o ccan-take.o ccan-time.o ccan-str.o ccan-noerr.o ccan-list.o + +cbspeed: cbspeed.o $(CCAN_OBJS) + +speed: speed.o $(CCAN_OBJS) + +clean: + rm -f cbspeed speed speed.o cbspeed.o *.o + +ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-tal-str.o: $(CCANDIR)/ccan/tal/str/str.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-take.o: $(CCANDIR)/ccan/take/take.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-tal-grab_file.o: $(CCANDIR)/ccan/tal/grab_file/grab_file.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-time.o: $(CCANDIR)/ccan/time/time.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-list.o: $(CCANDIR)/ccan/list/list.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-str.o: $(CCANDIR)/ccan/str/str.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-noerr.o: $(CCANDIR)/ccan/noerr/noerr.c + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/ccan/ccan/strset/tools/cbspeed.c b/ccan/ccan/strset/tools/cbspeed.c new file mode 100644 index 000000000000..5d551b6e2c7f --- /dev/null +++ b/ccan/ccan/strset/tools/cbspeed.c @@ -0,0 +1,583 @@ +/* Simple speed tests using original critbit code (modified not to allocate). + * + * Results on my 32 bit Intel(R) Core(TM) i5 CPU M 560 @ 2.67GHz, gcc 4.5.2: + * Run 100 times: Min-Max(Avg) + #01: Initial insert: 237-257(239) + #02: Initial lookup (match): 180-197(181) + #03: Initial lookup (miss): 171-190(172) + #04: Initial lookup (random): 441-455(446) + #05: Initial delete all: 127-148(128) + #06: Initial re-inserting: 219-298(221) + #07: Deleting first half: 101-109(102) + #08: Adding (a different) half: 159-165(160) + #09: Lookup after half-change (match): 203-216(204) + #10: Lookup after half-change (miss): 217-225(218) + #11: Churn 1: 298-311(300) + #12: Churn 2: 298-318(301) + #13: Churn 3: 301-322(304) + #14: Post-Churn lookup (match): 189-196(190) + #15: Post-Churn lookup (miss): 189-197(191) + #16: Post-Churn lookup (random): 500-531(506) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CRITBIT source */ +typedef struct { + void *root; +} critbit0_tree; + +int critbit0_contains(critbit0_tree *t, const char *u); +int critbit0_insert(critbit0_tree *t, const char *u); +int critbit0_delete(critbit0_tree *t, const char *u); +void critbit0_clear(critbit0_tree *t); +int critbit0_allprefixed(critbit0_tree *t, const char *prefix, + int (*handle) (const char *, void *), void *arg); + +#define uint8 uint8_t +#define uint32 uint32_t + +static size_t allocated; + +/*2:*/ + +#include +#include +#include + +#include +#include + +typedef struct{ +void*child[2]; +uint32 byte; +uint8 otherbits; +}critbit0_node; + +/*:2*//*3:*/ + +int +critbit0_contains(critbit0_tree*t,const char*u){ +const uint8*ubytes= (void*)u; +const size_t ulen= strlen(u); +uint8*p= t->root; + +/*4:*/ + +if(!p)return 0; + +/*:4*/ + +/*5:*/ + +while(1&(intptr_t)p){ +critbit0_node*q= (void*)(p-1); +/*6:*/ + +uint8 c= 0; +if(q->bytebyte]; +const int direction= (1+(q->otherbits|c))>>8; + +/*:6*/ + +p= q->child[direction]; +} + +/*:5*/ + +/*7:*/ + +return 0==strcmp(u,(const char*)p); + +/*:7*/ + +} + +/*:3*//*8:*/ + +int critbit0_insert(critbit0_tree*t,const char*u) +{ +const uint8*const ubytes= (void*)u; +const size_t ulen= strlen(u); +uint8*p= t->root; + +/*9:*/ + +if(!p){ +#if 0 +char*x; +int a= posix_memalign((void**)&x,sizeof(void*),ulen+1); +if(a)return 0; +memcpy(x,u,ulen+1); +t->root= x; +#else +t->root = (char *)u; +#endif +return 2; +} + +/*:9*/ + +/*5:*/ + +while(1&(intptr_t)p){ +critbit0_node*q= (void*)(p-1); +/*6:*/ + +uint8 c= 0; +if(q->bytebyte]; +const int direction= (1+(q->otherbits|c))>>8; + +/*:6*/ + +p= q->child[direction]; +} + +/*:5*/ + +/*10:*/ + +/*11:*/ + +uint32 newbyte; +uint32 newotherbits; + +for(newbyte= 0;newbyte>8; + +/*:12*/ + + +/*:10*/ + +/*13:*/ + +/*14:*/ + +critbit0_node*newnode; +if(posix_memalign((void**)&newnode,sizeof(void*),sizeof(critbit0_node)))return 0; +allocated++; +char*x; +#if 0 +if(posix_memalign((void**)&x,sizeof(void*),ulen+1)){ +free(newnode); +return 0; +} +memcpy(x,ubytes,ulen+1); +#else +x = (char *)u; +#endif +newnode->byte= newbyte; +newnode->otherbits= newotherbits; +newnode->child[1-newdirection]= x; + +/*:14*/ + +/*15:*/ + +void**wherep= &t->root; +for(;;){ +uint8*p= *wherep; +if(!(1&(intptr_t)p))break; +critbit0_node*q= (void*)(p-1); +if(q->byte> newbyte)break; +if(q->byte==newbyte&&q->otherbits> newotherbits)break; +uint8 c= 0; +if(q->bytebyte]; +const int direction= (1+(q->otherbits|c))>>8; +wherep= q->child+direction; +} + +newnode->child[newdirection]= *wherep; +*wherep= (void*)(1+(char*)newnode); + +/*:15*/ + + +/*:13*/ + + +return 2; +} + +/*:8*//*16:*/ + +int critbit0_delete(critbit0_tree*t,const char*u){ +const uint8*ubytes= (void*)u; +const size_t ulen= strlen(u); +uint8*p= t->root; +void**wherep= &t->root; +void**whereq= 0; +critbit0_node*q= 0; +int direction= 0; + +/*17:*/ + +if(!p)return 0; + +/*:17*/ + +/*18:*/ + +while(1&(intptr_t)p){ +whereq= wherep; +q= (void*)(p-1); +uint8 c= 0; +if(q->bytebyte]; +direction= (1+(q->otherbits|c))>>8; +wherep= q->child+direction; +p= *wherep; +} + +/*:18*/ + +/*19:*/ + +if(0!=strcmp(u,(const char*)p))return 0; +#if 0 +free(p); +#endif + +/*:19*/ + +/*20:*/ + +if(!whereq){ +t->root= 0; +return 1; +} + +*whereq= q->child[1-direction]; +free(q); +allocated--; +/*:20*/ + + +return 1; +} + +/*:16*//*21:*/ + +static void +traverse(void*top){ +/*22:*/ + +uint8*p= top; + +if(1&(intptr_t)p){ +critbit0_node*q= (void*)(p-1); +traverse(q->child[0]); +traverse(q->child[1]); +free(q); +allocated--; +}else{ +#if 0 +free(p); +#endif +} + +/*:22*/ + +} + +void critbit0_clear(critbit0_tree*t) +{ +if(t->root)traverse(t->root); +t->root= NULL; +} + +/*:21*//*23:*/ + +static int +allprefixed_traverse(uint8*top, +int(*handle)(const char*,void*),void*arg){ +/*26:*/ + +if(1&(intptr_t)top){ +critbit0_node*q= (void*)(top-1); +int direction; +for(direction= 0;direction<2;++direction) +switch(allprefixed_traverse(q->child[direction],handle,arg)){ +case 1:break; +case 0:return 0; +default:return-1; +} +return 1; +} + +/*:26*/ + +/*27:*/ + +return handle((const char*)top,arg);/*:27*/ + +} + +int +critbit0_allprefixed(critbit0_tree*t,const char*prefix, +int(*handle)(const char*,void*),void*arg){ +const uint8*ubytes= (void*)prefix; +const size_t ulen= strlen(prefix); +uint8*p= t->root; +uint8*top= p; +size_t i; + +if(!p)return 1; +/*24:*/ + +while(1&(intptr_t)p){ +critbit0_node*q= (void*)(p-1); +uint8 c= 0; +if(q->bytebyte]; +const int direction= (1+(q->otherbits|c))>>8; +p= q->child[direction]; +if(q->byte +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Nanoseconds per operation */ +static size_t normalize(const struct timeabs *start, + const struct timeabs *stop, + unsigned int num) +{ + return time_to_nsec(time_divide(time_between(*stop, *start), num)); +} + +int main(int argc, char *argv[]) +{ + size_t i, j, num; + struct timeabs start, stop; + struct strset set; + char **words, **misswords; + + words = tal_strsplit(NULL, grab_file(NULL, + argv[1] ? argv[1] : "/usr/share/dict/words"), + "\n", STR_NO_EMPTY); + strset_init(&set); + num = tal_count(words) - 1; + printf("%zu words\n", num); + + /* Append and prepend last char for miss testing. */ + misswords = tal_arr(words, char *, num); + for (i = 0; i < num; i++) { + char lastc; + if (strlen(words[i])) + lastc = words[i][strlen(words[i])-1]; + else + lastc = 'z'; + misswords[i] = tal_fmt(misswords, "%c%s%c%c", + lastc, words[i], lastc, lastc); + } + + printf("#01: Initial insert: "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i++) + strset_add(&set, words[i]); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + +#if 0 + printf("Nodes allocated: %zu (%zu bytes)\n", + allocated, allocated * sizeof(critbit0_node)); +#endif + + printf("#02: Initial lookup (match): "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i++) + if (!strset_get(&set, words[i])) + abort(); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#03: Initial lookup (miss): "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i++) { + if (strset_get(&set, misswords[i])) + abort(); + } + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + /* Lookups in order are very cache-friendly for judy; try random */ + printf("#04: Initial lookup (random): "); + fflush(stdout); + start = time_now(); + for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num) + if (!strset_get(&set, words[j])) + abort(); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#05: Initial delete all: "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i++) + if (!strset_del(&set, words[i])) + abort(); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#06: Initial re-inserting: "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i++) + strset_add(&set, words[i]); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#07: Deleting first half: "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i+=2) + if (!strset_del(&set, words[i])) + abort(); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#08: Adding (a different) half: "); + fflush(stdout); + + start = time_now(); + for (i = 0; i < num; i+=2) + strset_add(&set, misswords[i]); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#09: Lookup after half-change (match): "); + fflush(stdout); + start = time_now(); + for (i = 1; i < num; i+=2) + if (!strset_get(&set, words[i])) + abort(); + for (i = 0; i < num; i+=2) { + if (!strset_get(&set, misswords[i])) + abort(); + } + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#10: Lookup after half-change (miss): "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i+=2) + if (strset_get(&set, words[i])) + abort(); + for (i = 1; i < num; i+=2) { + if (strset_get(&set, misswords[i])) + abort(); + } + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + /* Hashtables with delete markers can fill with markers over time. + * so do some changes to see how it operates in long-term. */ + printf("#11: Churn 1: "); + start = time_now(); + for (j = 0; j < num; j+=2) { + if (!strset_del(&set, misswords[j])) + abort(); + if (!strset_add(&set, words[j])) + abort(); + } + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#12: Churn 2: "); + start = time_now(); + for (j = 1; j < num; j+=2) { + if (!strset_del(&set, words[j])) + abort(); + if (!strset_add(&set, misswords[j])) + abort(); + } + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#13: Churn 3: "); + start = time_now(); + for (j = 1; j < num; j+=2) { + if (!strset_del(&set, misswords[j])) + abort(); + if (!strset_add(&set, words[j])) + abort(); + } + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + /* Now it's back to normal... */ + printf("#14: Post-Churn lookup (match): "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i++) + if (!strset_get(&set, words[i])) + abort(); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + printf("#15: Post-Churn lookup (miss): "); + fflush(stdout); + start = time_now(); + for (i = 0; i < num; i++) { + if (strset_get(&set, misswords[i])) + abort(); + } + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + /* Lookups in order are very cache-friendly for judy; try random */ + printf("#16: Post-Churn lookup (random): "); + fflush(stdout); + start = time_now(); + for (i = 0, j = 0; i < num; i++, j = (j + 10007) % num) + if (!strset_get(&set, words[j])) + abort(); + stop = time_now(); + printf(" %zu ns\n", normalize(&start, &stop, num)); + + return 0; +} From c462ccae1a9deb59a0ed07aeb29a4bc4b73b1aaa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:30:46 +1030 Subject: [PATCH 0031/1530] wallet: have db track what columns are accessed in DEVELOPER mode. And add db_col_ignore helper for cases where it's deliberate. Signed-off-by: Rusty Russell --- wallet/db.c | 64 ++++++++++++++++++++++++++++++++++++++++-- wallet/db.h | 6 ++-- wallet/db_common.h | 6 ++++ wallet/invoices.c | 4 +++ wallet/wallet.c | 69 ++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 138 insertions(+), 11 deletions(-) diff --git a/wallet/db.c b/wallet/db.c index aa6fedc4de1a..7961aa3c3e77 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -881,6 +881,23 @@ static void db_stmt_free(struct db_stmt *stmt) if (!stmt->executed) fatal("Freeing an un-executed statement from %s: %s", stmt->location, stmt->query->query); +#if DEVELOPER + /* If they never got a db_step, we don't track */ + if (stmt->cols_used) { + for (size_t i = 0; i < stmt->query->num_colnames; i++) { + if (!stmt->query->colnames[i].sqlname) + continue; + if (!strset_get(stmt->cols_used, + stmt->query->colnames[i].sqlname)) { + log_broken(stmt->db->log, + "Never accessed column %s in query %s", + stmt->query->colnames[i].sqlname, + stmt->query->query); + } + } + strset_clear(stmt->cols_used); + } +#endif if (stmt->inner_stmt) stmt->db->config->stmt_free_fn(stmt); assert(stmt->inner_stmt == NULL); @@ -929,6 +946,10 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, list_add(&db->pending_statements, &stmt->list); +#if DEVELOPER + stmt->cols_used = NULL; +#endif /* DEVELOPER */ + return stmt; } @@ -937,8 +958,19 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, bool db_step(struct db_stmt *stmt) { + bool ret; + assert(stmt->executed); - return stmt->db->config->step_fn(stmt); + ret = stmt->db->config->step_fn(stmt); + +#if DEVELOPER + /* We only track cols_used if we return a result! */ + if (ret && !stmt->cols_used) { + stmt->cols_used = tal(stmt, struct strset); + strset_init(stmt->cols_used); + } +#endif + return ret; } u64 db_column_u64(struct db_stmt *stmt, int col) @@ -1432,6 +1464,8 @@ void fillin_missing_scriptpubkeys(struct lightningd *ld, struct db *db, fatal("HSM gave bad hsm_get_output_scriptpubkey_reply %s", tal_hex(msg, msg)); } else { + db_col_ignore(stmt, "peer_id"); + db_col_ignore(stmt, "commitment_point"); /* Build from bip32_base */ bip32_pubkey(mc->bip32_base, &key, keyindex); if (type == p2sh_wpkh) { @@ -1623,11 +1657,18 @@ migrate_inflight_last_tx_to_psbt(struct lightningd *ld, struct db *db, last_tx = db_col_tx(stmt, stmt, "inflight.last_tx"); assert(last_tx != NULL); + /* FIXME: This is only needed inside the select? */ + db_col_ignore(stmt, "inflight.last_tx"); + /* If we've forgotten about the peer_id * because we closed / forgot the channel, * we can skip this. */ - if (db_col_is_null(stmt, "p.node_id")) + if (db_col_is_null(stmt, "p.node_id")) { + db_col_ignore(stmt, "inflight.last_sig"); + db_col_ignore(stmt, "inflight.funding_satoshi"); + db_col_ignore(stmt, "inflight.funding_tx_id"); continue; + } db_col_node_id(stmt, "p.node_id", &peer_id); db_col_amount_sat(stmt, "inflight.funding_satoshi", &funding_sat); db_col_pubkey(stmt, "c.fundingkey_remote", &remote_funding_pubkey); @@ -1711,8 +1752,13 @@ void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db, /* If we've forgotten about the peer_id * because we closed / forgot the channel, * we can skip this. */ - if (db_col_is_null(stmt, "p.node_id")) + if (db_col_is_null(stmt, "p.node_id")) { + db_col_ignore(stmt, "c.funding_satoshi"); + db_col_ignore(stmt, "c.fundingkey_remote"); + db_col_ignore(stmt, "c.last_sig"); continue; + } + db_col_node_id(stmt, "p.node_id", &peer_id); db_col_amount_sat(stmt, "c.funding_satoshi", &funding_sat); db_col_pubkey(stmt, "c.fundingkey_remote", &remote_funding_pubkey); @@ -2567,5 +2613,17 @@ size_t db_query_colnum(const struct db_stmt *stmt, colname)) { col = (col + 1) % stmt->query->num_colnames; } + +#if DEVELOPER + strset_add(stmt->cols_used, colname); +#endif + return stmt->query->colnames[col].val; } + +void db_col_ignore(struct db_stmt *stmt, const char *colname) +{ +#if DEVELOPER + db_query_colnum(stmt, colname); +#endif +} diff --git a/wallet/db.h b/wallet/db.h index 9729623ba9f0..c05e8d4597b6 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -182,8 +182,7 @@ void db_column_amount_msat_or_default(struct db_stmt *stmt, int col, /* Modern variants: get columns by name from SELECT */ /* Bridge function to get column number from SELECT (must exist) */ -size_t db_query_colnum(const struct db_stmt *stmt, - const char *colname); +size_t db_query_colnum(const struct db_stmt *stmt, const char *colname); u64 db_col_u64(struct db_stmt *stmt, const char *colname); int db_col_int(struct db_stmt *stmt, const char *colname); @@ -238,6 +237,9 @@ void db_col_amount_msat_or_default(struct db_stmt *stmt, const char *colname, struct amount_msat def); +/* Explicitly ignore a column (so we don't complain you didn't use it!) */ +void db_col_ignore(struct db_stmt *stmt, const char *colname); + /** * db_exec_prepared -- Execute a prepared statement * diff --git a/wallet/db_common.h b/wallet/db_common.h index 5ca35ffacb43..470ccb2e6bbf 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include /* For testing, we want to catch fatal messages. */ @@ -99,6 +100,11 @@ struct db_stmt { bool executed; int row; + +#if DEVELOPER + /* Map as we reference into a SELECT statement in query. */ + struct strset *cols_used; +#endif }; struct db_config { diff --git a/wallet/invoices.c b/wallet/invoices.c index 0f6c84a7996c..1142dcb80ca7 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -96,6 +96,10 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, dtl->pay_index = db_col_u64(stmt, "pay_index"); db_col_amount_msat(stmt, "msatoshi_received", &dtl->received); dtl->paid_timestamp = db_col_u64(stmt, "paid_timestamp"); + } else { + db_col_ignore(stmt, "pay_index"); + db_col_ignore(stmt, "msatoshi_received"); + db_col_ignore(stmt, "paid_timestamp"); } dtl->invstring = db_col_strdup(dtl, stmt, "bolt11"); diff --git a/wallet/wallet.c b/wallet/wallet.c index 72e14d1ae1df..263fbfe48f75 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -109,6 +109,7 @@ static bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, /* If we get a result, that means a clash. */ if (db_step(stmt)) { + db_col_ignore(stmt, "*"); tal_free(stmt); return false; } @@ -197,6 +198,10 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt) utxo->close_info->csv = db_col_int(stmt, "csv_lock"); } else { utxo->close_info = NULL; + db_col_ignore(stmt, "peer_id"); + db_col_ignore(stmt, "commitment_point"); + db_col_ignore(stmt, "option_anchor_outputs"); + db_col_ignore(stmt, "csv_lock"); } utxo->scriptPubkey = db_col_arr(utxo, stmt, "scriptpubkey", u8); @@ -607,6 +612,7 @@ bool wallet_add_onchaind_utxo(struct wallet *w, /* If we get a result, that means a clash. */ if (db_step(stmt)) { + db_col_ignore(stmt, "*"); tal_free(stmt); return false; } @@ -850,8 +856,11 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) if (!db_step(stmt)) goto done; - if (db_col_is_null(stmt, "node_id")) + if (db_col_is_null(stmt, "node_id")) { + db_col_ignore(stmt, "address"); + db_col_ignore(stmt, "id"); goto done; + } db_col_node_id(stmt, "node_id", &id); @@ -923,6 +932,7 @@ bool wallet_remote_ann_sigs_load(const tal_t *ctx, struct wallet *w, u64 id, /* if only one sig exists, forget the sig and hope peer send new ones*/ if (db_col_is_null(stmt, "remote_ann_node_sig") || db_col_is_null(stmt, "remote_ann_bitcoin_sig")) { + db_col_ignore(stmt, "remote_ann_bitcoin_sig"); *remote_ann_node_sig = *remote_ann_bitcoin_sig = NULL; tal_free(stmt); return true; @@ -1147,6 +1157,9 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, lease_chan_max_msat = 0; lease_chan_max_ppt = 0; lease_blockheight_start = 0; + db_col_ignore(stmt, "lease_chan_max_msat"); + db_col_ignore(stmt, "lease_chan_max_ppt"); + db_col_ignore(stmt, "lease_blockheight_start"); } inflight = new_inflight(chan, &funding, @@ -1287,6 +1300,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm last_sent_commit->id = db_col_u64(stmt, "last_sent_commit_id"); } #endif + db_col_ignore(stmt, "last_sent_commit_state"); + db_col_ignore(stmt, "last_sent_commit_id"); if (!db_col_is_null(stmt, "future_per_commitment_point")) { future_per_commitment_point = tal(tmpctx, struct pubkey); @@ -1356,9 +1371,10 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm db_col_pubkey(stmt, "delayed_payment_basepoint_local", &local_basepoints.delayed_payment); db_col_pubkey(stmt, "funding_pubkey_local", &local_funding_pubkey); - if (db_col_is_null(stmt, "shutdown_wrong_txid")) + if (db_col_is_null(stmt, "shutdown_wrong_txid")) { + db_col_ignore(stmt, "shutdown_wrong_outnum"); shutdown_wrong_funding = NULL; - else { + } else { shutdown_wrong_funding = tal(tmpctx, struct bitcoin_outpoint); db_col_txid(stmt, "shutdown_wrong_txid", &shutdown_wrong_funding->txid); @@ -1379,6 +1395,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm lease_chan_max_msat = db_col_int(stmt, "lease_chan_max_msat"); lease_chan_max_ppt = db_col_int(stmt, "lease_chan_max_ppt"); } else { + db_col_ignore(stmt, "lease_chan_max_msat"); + db_col_ignore(stmt, "lease_chan_max_ppt"); lease_commit_sig = NULL; lease_chan_max_msat = 0; lease_chan_max_ppt = 0; @@ -1552,6 +1570,9 @@ static bool wallet_channels_load_active(struct wallet *w) ok = false; break; } + /* FIXME: Remove */ + db_col_ignore(stmt, "local_feerate_per_kw"); + db_col_ignore(stmt, "remote_feerate_per_kw"); count++; } log_debug(w->log, "Loaded %d channels from DB", count); @@ -1702,6 +1723,8 @@ void wallet_blocks_heights(struct wallet *w, u32 def, u32 *min, u32 *max) if (!db_col_is_null(stmt, "MIN(height)")) { *min = db_col_int(stmt, "MIN(height)"); *max = db_col_int(stmt, "MAX(height)"); + } else { + db_col_ignore(stmt, "MAX(height)"); } } tal_free(stmt); @@ -1762,6 +1785,8 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, if (!db_step(stmt)) return false; + /* FIXME */ + db_col_ignore(stmt, "id"); cc->id = id; db_col_amount_sat(stmt, "dust_limit_satoshis", &cc->dust_limit); db_col_amount_msat(stmt, "max_htlc_value_in_flight_msat", @@ -2583,6 +2608,9 @@ static bool wallet_stmt2htlc_in(struct channel *channel, in->fail_immediate = db_col_int(stmt, "fail_immediate"); + /* FIXME: Don't fetch this at all! */ + db_col_ignore(stmt, "origin_htlc"); + return ok; } @@ -2646,12 +2674,19 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, in_id, out->dbid); #endif } + db_col_ignore(stmt, "partid"); + db_col_ignore(stmt, "groupid"); } else { out->partid = db_col_u64(stmt, "partid"); out->groupid = db_col_u64(stmt, "groupid"); out->am_origin = true; } + /* FIXME: don't SELECT these columns */ + db_col_ignore(stmt, "received_time"); + db_col_ignore(stmt, "shared_secret"); + db_col_ignore(stmt, "malformed_onion"); + return ok; } @@ -2974,6 +3009,7 @@ void wallet_payment_store(struct wallet *wallet, db_query_prepared(stmt); res = db_step(stmt); assert(res); + db_col_ignore(stmt, "status"); tal_free(stmt); #endif return; @@ -3345,9 +3381,10 @@ void wallet_payment_get_failinfo(const tal_t *ctx, *failnode = tal(ctx, struct node_id); db_col_node_id(stmt, "failnode", *failnode); } - if (db_col_is_null(stmt, "failchannel")) + if (db_col_is_null(stmt, "failchannel")) { + db_col_ignore(stmt, "faildirection"); *failchannel = NULL; - else { + } else { *failchannel = tal(ctx, struct short_channel_id); resb = db_col_short_channel_id_str(stmt, "failchannel", *failchannel); @@ -3793,6 +3830,8 @@ bool wallet_have_block(struct wallet *w, u32 blockheight) db_bind_int(stmt, 0, blockheight); db_query_prepared(stmt); result = db_step(stmt); + if (result) + db_col_ignore(stmt, "height"); tal_free(stmt); return result; } @@ -3925,6 +3964,7 @@ void wallet_transaction_add(struct wallet *w, const struct wally_tx *tx, db_bind_tx(stmt, 3, tx); db_exec_prepared_v2(take(stmt)); } else { + db_col_ignore(stmt, "blockheight"); tal_free(stmt); if (blockheight) { @@ -3994,6 +4034,8 @@ void wallet_transaction_annotate(struct wallet *w, if (channel_id == 0 && !db_col_is_null(stmt, "channel_id")) channel_id = db_col_u64(stmt, "channel_id"); + else + db_col_ignore(stmt, "channel_id"); tal_free(stmt); @@ -4555,6 +4597,7 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t cur->txindex = 0; } } else { + db_col_ignore(stmt, "t.txindex"); cur->blockheight = 0; cur->txindex = 0; } @@ -4601,6 +4644,10 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t &ann->channel); else ann->channel.u64 = 0; + } else { + db_col_ignore(stmt, "ann_idx"); + db_col_ignore(stmt, "annotation_type"); + db_col_ignore(stmt, "c.short_channel_id"); } } tal_free(stmt); @@ -4736,9 +4783,14 @@ char *wallet_offer_find(const tal_t *ctx, *label = NULL; else *label = db_col_json_escape(ctx, stmt, "label"); - } + } else + db_col_ignore(stmt, "label"); + if (status) *status = offer_status_in_db(db_col_int(stmt, "status")); + else + db_col_ignore(stmt, "status"); + tal_free(stmt); return bolt12; } @@ -4965,8 +5017,13 @@ struct db_stmt *wallet_datastore_next(const tal_t *ctx, *key = db_col_datastore_key(ctx, stmt, "key"); if (data) *data = db_col_arr(ctx, stmt, "data", u8); + else + db_col_ignore(stmt, "data"); + if (generation) *generation = db_col_u64(stmt, "generation"); + else + db_col_ignore(stmt, "generation"); return stmt; } From 2e6df892749400412d168e0323e510fda353e60b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:31:46 +1030 Subject: [PATCH 0032/1530] wallet: remove redundant field counters, ignored fields. Signed-off-by: Rusty Russell --- wallet/wallet.c | 203 ++++++++++++++++++++++-------------------------- 1 file changed, 92 insertions(+), 111 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index 263fbfe48f75..b26ec943d823 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1035,20 +1035,20 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) struct db_stmt *stmt; stmt = db_prepare_v2(w->db, SQL("INSERT INTO channel_funding_inflights (" - " channel_id" // 0 - ", funding_tx_id" // 1 - ", funding_tx_outnum" // 2 - ", funding_feerate" // 3 - ", funding_satoshi" // 4 - ", our_funding_satoshi" // 5 - ", funding_psbt" // 6 - ", last_tx" // 7 - ", last_sig" // 8 - ", lease_commit_sig" // 9 - ", lease_chan_max_msat" // 10 - ", lease_chan_max_ppt" // 11 - ", lease_expiry" // 12 - ", lease_blockheight_start" // 13 + " channel_id" + ", funding_tx_id" + ", funding_tx_outnum" + ", funding_feerate" + ", funding_satoshi" + ", our_funding_satoshi" + ", funding_psbt" + ", last_tx" + ", last_sig" + ", lease_commit_sig" + ", lease_chan_max_msat" + ", lease_chan_max_ppt" + ", lease_expiry" + ", lease_blockheight_start" ") VALUES (" "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); @@ -1187,22 +1187,22 @@ static bool wallet_channel_load_inflights(struct wallet *w, struct db_stmt *stmt; stmt = db_prepare_v2(w->db, SQL("SELECT" - " funding_tx_id" // 0 - ", funding_tx_outnum" // 1 - ", funding_feerate" // 2 - ", funding_satoshi" // 3 - ", our_funding_satoshi" // 4 - ", funding_psbt" // 5 - ", last_tx" // 6 - ", last_sig" // 7 - ", funding_tx_remote_sigs_received" //8 - ", lease_expiry" // 9 - ", lease_commit_sig" // 10 - ", lease_chan_max_msat" // 11 - ", lease_chan_max_ppt" // 12 - ", lease_blockheight_start" // 13 + " funding_tx_id" + ", funding_tx_outnum" + ", funding_feerate" + ", funding_satoshi" + ", our_funding_satoshi" + ", funding_psbt" + ", last_tx" + ", last_sig" + ", funding_tx_remote_sigs_received" + ", lease_expiry" + ", lease_commit_sig" + ", lease_chan_max_msat" + ", lease_chan_max_ppt" + ", lease_blockheight_start" " FROM channel_funding_inflights" - " WHERE channel_id = ?" // ?0 + " WHERE channel_id = ?" " ORDER BY funding_feerate")); db_bind_u64(stmt, 0, chan->dbid); @@ -1495,70 +1495,68 @@ static bool wallet_channels_load_active(struct wallet *w) /* We load all channels */ stmt = db_prepare_v2(w->db, SQL("SELECT" - " id" // 0 - ", peer_id" // 1 - ", short_channel_id" // 2 - ", full_channel_id" // 3 - ", channel_config_local" // 4 - ", channel_config_remote" // 5 - ", state" // 6 - ", funder" // 7 - ", channel_flags" // 8 - ", minimum_depth" // 9 - ", next_index_local" // 10 - ", next_index_remote" // 11 - ", next_htlc_id" // 12 - ", funding_tx_id" // 13 - ", funding_tx_outnum" // 14 - ", funding_satoshi" // 15 - ", our_funding_satoshi" // 16 - ", funding_locked_remote" // 17 - ", push_msatoshi" // 18 - ", msatoshi_local" // 19 - ", fundingkey_remote" // 20 - ", revocation_basepoint_remote" // 21 - ", payment_basepoint_remote" // 22 - ", htlc_basepoint_remote" // 23 + " 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" // 25 - ", old_per_commit_remote" // 26 - ", local_feerate_per_kw" // 27 - ", remote_feerate_per_kw" // 28 - ", shachain_remote_id" // 29 - ", shutdown_scriptpubkey_remote" // 30 - ", shutdown_keyidx_local" // 31 - ", last_sent_commit_state" // 32 - ", last_sent_commit_id" // 33 - ", last_tx" // 34 - ", last_sig" // 35 - ", last_was_revoke" // 36 - ", first_blocknum" // 37 - ", min_possible_feerate" // 38 - ", max_possible_feerate" // 39 - ", msatoshi_to_us_min" // 40 - ", msatoshi_to_us_max" // 41 - ", future_per_commitment_point" // 42 - ", last_sent_commit" // 43 - ", feerate_base" // 44 - ", feerate_ppm" // 45 - ", remote_upfront_shutdown_script" // 46 - ", local_static_remotekey_start" // 47 - ", remote_static_remotekey_start" // 48 - ", option_anchor_outputs" // 49 - ", shutdown_scriptpubkey_local" // 50 - ", closer" // 51 - ", state_change_reason" // 52 - ", revocation_basepoint_local" // 53 - ", payment_basepoint_local" // 54 - ", htlc_basepoint_local" // 55 - ", delayed_payment_basepoint_local" // 56 - ", funding_pubkey_local" // 57 - ", shutdown_wrong_txid" // 58 - ", shutdown_wrong_outnum" // 59 - ", lease_expiry" // 60 - ", lease_commit_sig" // 61 - ", lease_chan_max_msat" // 62 - ", lease_chan_max_ppt" // 63 + ", per_commit_remote" + ", old_per_commit_remote" + ", 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" + ", local_static_remotekey_start" + ", remote_static_remotekey_start" + ", 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" + ", lease_expiry" + ", lease_commit_sig" + ", lease_chan_max_msat" + ", lease_chan_max_ppt" " FROM channels" " WHERE state != ?;")); //? 0 db_bind_int(stmt, 0, CLOSED); @@ -1570,9 +1568,6 @@ static bool wallet_channels_load_active(struct wallet *w) ok = false; break; } - /* FIXME: Remove */ - db_col_ignore(stmt, "local_feerate_per_kw"); - db_col_ignore(stmt, "remote_feerate_per_kw"); count++; } log_debug(w->log, "Loaded %d channels from DB", count); @@ -1774,7 +1769,7 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, { bool ok = true; const char *query = SQL( - "SELECT id, dust_limit_satoshis, max_htlc_value_in_flight_msat, " + "SELECT dust_limit_satoshis, max_htlc_value_in_flight_msat, " "channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, " "max_accepted_htlcs, max_dust_htlc_exposure_msat" " FROM channel_configs WHERE id= ? ;"); @@ -1785,8 +1780,6 @@ bool wallet_channel_config_load(struct wallet *w, const u64 id, if (!db_step(stmt)) return false; - /* FIXME */ - db_col_ignore(stmt, "id"); cc->id = id; db_col_amount_sat(stmt, "dust_limit_satoshis", &cc->dust_limit); db_col_amount_msat(stmt, "max_htlc_value_in_flight_msat", @@ -2608,9 +2601,6 @@ static bool wallet_stmt2htlc_in(struct channel *channel, in->fail_immediate = db_col_int(stmt, "fail_immediate"); - /* FIXME: Don't fetch this at all! */ - db_col_ignore(stmt, "origin_htlc"); - return ok; } @@ -2682,11 +2672,6 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, out->am_origin = true; } - /* FIXME: don't SELECT these columns */ - db_col_ignore(stmt, "received_time"); - db_col_ignore(stmt, "shared_secret"); - db_col_ignore(stmt, "malformed_onion"); - return ok; } @@ -2742,9 +2727,8 @@ bool wallet_htlcs_load_in_for_channel(struct wallet *wallet, ", routing_onion" ", failuremsg" ", malformed_onion" - ", origin_htlc" ", shared_secret" - ", received_time" // 12 + ", received_time" ", we_filled" ", fail_immediate" " FROM channel_htlcs" @@ -2789,10 +2773,7 @@ bool wallet_htlcs_load_out_for_channel(struct wallet *wallet, ", payment_key" ", routing_onion" ", failuremsg" - ", malformed_onion" ", origin_htlc" - ", shared_secret" - ", received_time" ", partid" ", localfailmsg" ", groupid" From 53c9d9853d8dec21f4342a66a8f91757ae721b7e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:32:46 +1030 Subject: [PATCH 0033/1530] wallet: remove db_column_ functions. Keep some as internal helpers only. Signed-off-by: Rusty Russell --- wallet/db.c | 348 +++++++--------------------------------------------- wallet/db.h | 50 -------- 2 files changed, 46 insertions(+), 352 deletions(-) diff --git a/wallet/db.c b/wallet/db.c index 7961aa3c3e77..d33747ba4c02 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -973,64 +973,6 @@ bool db_step(struct db_stmt *stmt) return ret; } -u64 db_column_u64(struct db_stmt *stmt, int col) -{ - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %d in query %s", col, stmt->query->query); - return 0; - } - return stmt->db->config->column_u64_fn(stmt, col); -} - -int db_column_int_or_default(struct db_stmt *stmt, int col, int def) -{ - if (db_column_is_null(stmt, col)) - return def; - else - return db_column_int(stmt, col); -} - -int db_column_int(struct db_stmt *stmt, int col) -{ - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %d in query %s", col, stmt->query->query); - return 0; - } - return stmt->db->config->column_int_fn(stmt, col); -} - -size_t db_column_bytes(struct db_stmt *stmt, int col) -{ - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %d in query %s", col, stmt->query->query); - return 0; - } - return stmt->db->config->column_bytes_fn(stmt, col); -} - -int db_column_is_null(struct db_stmt *stmt, int col) -{ - return stmt->db->config->column_is_null_fn(stmt, col); -} - -const void *db_column_blob(struct db_stmt *stmt, int col) -{ - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %d in query %s", col, stmt->query->query); - return NULL; - } - return stmt->db->config->column_blob_fn(stmt, col); -} - -const unsigned char *db_column_text(struct db_stmt *stmt, int col) -{ - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %d in query %s", col, stmt->query->query); - return NULL; - } - return stmt->db->config->column_text_fn(stmt, col); -} - size_t db_count_changes(struct db_stmt *stmt) { assert(stmt->executed); @@ -1996,233 +1938,47 @@ void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr) db_bind_blob(stmt, col, arr, tal_bytelen(arr)); } -void db_column_preimage(struct db_stmt *stmt, int col, - struct preimage *preimage) -{ - const u8 *raw; - size_t size = sizeof(struct preimage); - assert(db_column_bytes(stmt, col) == size); - raw = db_column_blob(stmt, col); - memcpy(preimage, raw, size); -} - -void db_column_channel_id(struct db_stmt *stmt, int col, struct channel_id *dest) -{ - assert(db_column_bytes(stmt, col) == sizeof(dest->id)); - memcpy(dest->id, db_column_blob(stmt, col), sizeof(dest->id)); -} - -void db_column_node_id(struct db_stmt *stmt, int col, struct node_id *dest) -{ - assert(db_column_bytes(stmt, col) == sizeof(dest->k)); - memcpy(dest->k, db_column_blob(stmt, col), sizeof(dest->k)); -} - -struct node_id *db_column_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, - int col) -{ - struct node_id *ret; - size_t n = db_column_bytes(stmt, col) / sizeof(ret->k); - const u8 *arr = db_column_blob(stmt, col); - assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); - ret = tal_arr(ctx, struct node_id, n); - - for (size_t i = 0; i < n; i++) - memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); - - return ret; -} - -void db_column_pubkey(struct db_stmt *stmt, int pos, struct pubkey *dest) -{ - bool ok; - assert(db_column_bytes(stmt, pos) == PUBKEY_CMPR_LEN); - ok = pubkey_from_der(db_column_blob(stmt, pos), PUBKEY_CMPR_LEN, dest); - assert(ok); -} - -bool db_column_short_channel_id(struct db_stmt *stmt, int col, - struct short_channel_id *dest) -{ - const char *source = db_column_blob(stmt, col); - size_t sourcelen = db_column_bytes(stmt, col); - return short_channel_id_from_str(source, sourcelen, dest); -} - -struct short_channel_id * -db_column_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, int col) -{ - const u8 *ser; - size_t len; - struct short_channel_id *ret; - - ser = db_column_blob(stmt, col); - len = db_column_bytes(stmt, col); - ret = tal_arr(ctx, struct short_channel_id, 0); - - while (len != 0) { - struct short_channel_id scid; - fromwire_short_channel_id(&ser, &len, &scid); - tal_arr_expand(&ret, scid); - } - - return ret; -} - -bool db_column_signature(struct db_stmt *stmt, int col, - secp256k1_ecdsa_signature *sig) -{ - assert(db_column_bytes(stmt, col) == 64); - return secp256k1_ecdsa_signature_parse_compact( - secp256k1_ctx, sig, db_column_blob(stmt, col)) == 1; -} - -struct timeabs db_column_timeabs(struct db_stmt *stmt, int col) -{ - struct timeabs t; - u64 timestamp = db_column_u64(stmt, col); - t.ts.tv_sec = timestamp / NSEC_IN_SEC; - t.ts.tv_nsec = timestamp % NSEC_IN_SEC; - return t; - -} - -struct bitcoin_tx *db_column_tx(const tal_t *ctx, struct db_stmt *stmt, int col) -{ - const u8 *src = db_column_blob(stmt, col); - size_t len = db_column_bytes(stmt, col); - return pull_bitcoin_tx(ctx, &src, &len); -} - -struct wally_psbt *db_column_psbt(const tal_t *ctx, struct db_stmt *stmt, int col) -{ - const u8 *src = db_column_blob(stmt, col); - size_t len = db_column_bytes(stmt, col); - return psbt_from_bytes(ctx, src, len); -} - -struct bitcoin_tx *db_column_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, int col) +/* Local helpers once you have column number */ +static bool db_column_is_null(struct db_stmt *stmt, int col) { - struct wally_psbt *psbt = db_column_psbt(ctx, stmt, col); - if (!psbt) - return NULL; - return bitcoin_tx_with_psbt(ctx, psbt); + return stmt->db->config->column_is_null_fn(stmt, col); } -void *db_column_arr_(const tal_t *ctx, struct db_stmt *stmt, int col, - size_t bytes, const char *label, const char *caller) +/* Returns true (and warns) if it's nul */ +static bool db_column_null_warn(struct db_stmt *stmt, const char *colname, + int col) { - size_t sourcelen; - void *p; - - if (db_column_is_null(stmt, col)) - return NULL; - - sourcelen = db_column_bytes(stmt, col); + if (!db_column_is_null(stmt, col)) + return false; - if (sourcelen % bytes != 0) - db_fatal("%s: column size %zu not a multiple of %s (%zu)", - caller, sourcelen, label, bytes); - - p = tal_arr_label(ctx, char, sourcelen, label); - memcpy(p, db_column_blob(stmt, col), sourcelen); - return p; + log_broken(stmt->db->log, "Accessing a null column %s/%i in query %s", + colname, col, + stmt->query->query); + return true; } -void db_column_amount_msat_or_default(struct db_stmt *stmt, int col, - struct amount_msat *msat, - struct amount_msat def) +static size_t db_column_bytes(struct db_stmt *stmt, int col) { if (db_column_is_null(stmt, col)) - *msat = def; - else - msat->millisatoshis = db_column_u64(stmt, col); /* Raw: low level function */ -} - -void db_column_amount_msat(struct db_stmt *stmt, int col, - struct amount_msat *msat) -{ - msat->millisatoshis = db_column_u64(stmt, col); /* Raw: low level function */ -} - -void db_column_amount_sat(struct db_stmt *stmt, int col, struct amount_sat *sat) -{ - sat->satoshis = db_column_u64(stmt, col); /* Raw: low level function */ -} - -struct json_escape *db_column_json_escape(const tal_t *ctx, - struct db_stmt *stmt, int col) -{ - return json_escape_string_(ctx, db_column_blob(stmt, col), - db_column_bytes(stmt, col)); -} - -void db_column_sha256(struct db_stmt *stmt, int col, struct sha256 *sha) -{ - const u8 *raw; - size_t size = sizeof(struct sha256); - assert(db_column_bytes(stmt, col) == size); - raw = db_column_blob(stmt, col); - memcpy(sha, raw, size); -} - -void db_column_sha256d(struct db_stmt *stmt, int col, - struct sha256_double *shad) -{ - const u8 *raw; - size_t size = sizeof(struct sha256_double); - assert(db_column_bytes(stmt, col) == size); - raw = db_column_blob(stmt, col); - memcpy(shad, raw, size); -} - -void db_column_secret(struct db_stmt *stmt, int col, struct secret *s) -{ - const u8 *raw; - assert(db_column_bytes(stmt, col) == sizeof(struct secret)); - raw = db_column_blob(stmt, col); - memcpy(s, raw, sizeof(struct secret)); -} - -struct secret *db_column_secret_arr(const tal_t *ctx, struct db_stmt *stmt, - int col) -{ - return db_column_arr(ctx, stmt, col, struct secret); -} - -void db_column_txid(struct db_stmt *stmt, int pos, struct bitcoin_txid *t) -{ - db_column_sha256d(stmt, pos, &t->shad); -} - -struct onionreply *db_column_onionreply(const tal_t *ctx, - struct db_stmt *stmt, int col) -{ - struct onionreply *r = tal(ctx, struct onionreply); - r->contents = tal_dup_arr(r, u8, - db_column_blob(stmt, col), - db_column_bytes(stmt, col), 0); - return r; + return 0; + return stmt->db->config->column_bytes_fn(stmt, col); } -u8 *db_column_talarr(const tal_t *ctx, struct db_stmt *stmt, int col) +static const void *db_column_blob(struct db_stmt *stmt, int col) { if (db_column_is_null(stmt, col)) return NULL; - return tal_dup_arr(ctx, u8, - db_column_blob(stmt, col), - db_column_bytes(stmt, col), 0); + return stmt->db->config->column_blob_fn(stmt, col); } -/* Modern variants: by name */ + u64 db_col_u64(struct db_stmt *stmt, const char *colname) { size_t col = db_query_colnum(stmt, colname); - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + if (db_column_null_warn(stmt, colname, col)) return 0; - } + return stmt->db->config->column_u64_fn(stmt, col); } @@ -2233,17 +1989,16 @@ int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def) if (db_column_is_null(stmt, col)) return def; else - return db_column_int(stmt, col); + return stmt->db->config->column_int_fn(stmt, col); } int db_col_int(struct db_stmt *stmt, const char *colname) { size_t col = db_query_colnum(stmt, colname); - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + if (db_column_null_warn(stmt, colname, col)) return 0; - } + return stmt->db->config->column_int_fn(stmt, col); } @@ -2251,28 +2006,24 @@ size_t db_col_bytes(struct db_stmt *stmt, const char *colname) { size_t col = db_query_colnum(stmt, colname); - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + if (db_column_null_warn(stmt, colname, col)) return 0; - } + return stmt->db->config->column_bytes_fn(stmt, col); } int db_col_is_null(struct db_stmt *stmt, const char *colname) { - size_t col = db_query_colnum(stmt, colname); - - return stmt->db->config->column_is_null_fn(stmt, col); + return db_column_is_null(stmt, db_query_colnum(stmt, colname)); } const void *db_col_blob(struct db_stmt *stmt, const char *colname) { size_t col = db_query_colnum(stmt, colname); - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + if (db_column_null_warn(stmt, colname, col)) return NULL; - } + return stmt->db->config->column_blob_fn(stmt, col); } @@ -2282,10 +2033,9 @@ char *db_col_strdup(const tal_t *ctx, { size_t col = db_query_colnum(stmt, colname); - if (db_column_is_null(stmt, col)) { - log_broken(stmt->db->log, "Accessing a null column %s/%zu in query %s", colname, col, stmt->query->query); + if (db_column_null_warn(stmt, colname, col)) return NULL; - } + return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); } @@ -2326,6 +2076,7 @@ struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); ret = tal_arr(ctx, struct node_id, n); + db_column_null_warn(stmt, colname, col); for (size_t i = 0; i < n; i++) memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); @@ -2350,6 +2101,7 @@ bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, size_t col = db_query_colnum(stmt, colname); const char *source = db_column_blob(stmt, col); size_t sourcelen = db_column_bytes(stmt, col); + db_column_null_warn(stmt, colname, col); return short_channel_id_from_str(source, sourcelen, dest); } @@ -2361,6 +2113,7 @@ db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char * size_t len; struct short_channel_id *ret; + db_column_null_warn(stmt, colname, col); ser = db_column_blob(stmt, col); len = db_column_bytes(stmt, col); ret = tal_arr(ctx, struct short_channel_id, 0); @@ -2385,9 +2138,8 @@ bool db_col_signature(struct db_stmt *stmt, const char *colname, struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname) { - size_t col = db_query_colnum(stmt, colname); struct timeabs t; - u64 timestamp = db_column_u64(stmt, col); + u64 timestamp = db_col_u64(stmt, colname); t.ts.tv_sec = timestamp / NSEC_IN_SEC; t.ts.tv_nsec = timestamp % NSEC_IN_SEC; return t; @@ -2399,6 +2151,8 @@ struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char size_t col = db_query_colnum(stmt, colname); const u8 *src = db_column_blob(stmt, col); size_t len = db_column_bytes(stmt, col); + + db_column_null_warn(stmt, colname, col); return pull_bitcoin_tx(ctx, &src, &len); } @@ -2407,13 +2161,14 @@ struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const cha size_t col = db_query_colnum(stmt, colname); const u8 *src = db_column_blob(stmt, col); size_t len = db_column_bytes(stmt, col); + + db_column_null_warn(stmt, colname, col); return psbt_from_bytes(ctx, src, len); } struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) { - size_t col = db_query_colnum(stmt, colname); - struct wally_psbt *psbt = db_column_psbt(ctx, stmt, col); + struct wally_psbt *psbt = db_col_psbt(ctx, stmt, colname); if (!psbt) return NULL; return bitcoin_tx_with_psbt(ctx, psbt); @@ -2450,26 +2205,22 @@ void db_col_amount_msat_or_default(struct db_stmt *stmt, if (db_column_is_null(stmt, col)) *msat = def; else - msat->millisatoshis = db_column_u64(stmt, col); /* Raw: low level function */ + msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ } void db_col_amount_msat(struct db_stmt *stmt, const char *colname, struct amount_msat *msat) { - size_t col = db_query_colnum(stmt, colname); - - msat->millisatoshis = db_column_u64(stmt, col); /* Raw: low level function */ + msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ } void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat) { - size_t col = db_query_colnum(stmt, colname); - - sat->satoshis = db_column_u64(stmt, col); /* Raw: low level function */ + sat->satoshis = db_col_u64(stmt, colname); /* Raw: low level function */ } struct json_escape *db_col_json_escape(const tal_t *ctx, - struct db_stmt *stmt, const char *colname) + struct db_stmt *stmt, const char *colname) { size_t col = db_query_colnum(stmt, colname); @@ -2511,26 +2262,19 @@ struct secret *db_col_secret_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname) { - size_t col = db_query_colnum(stmt, colname); - - return db_column_arr(ctx, stmt, col, struct secret); + return db_col_arr(ctx, stmt, colname, struct secret); } void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t) { - size_t col = db_query_colnum(stmt, colname); - - db_column_sha256d(stmt, col, &t->shad); + db_col_sha256d(stmt, colname, &t->shad); } struct onionreply *db_col_onionreply(const tal_t *ctx, struct db_stmt *stmt, const char *colname) { - size_t col = db_query_colnum(stmt, colname); struct onionreply *r = tal(ctx, struct onionreply); - r->contents = tal_dup_arr(r, u8, - db_column_blob(stmt, col), - db_column_bytes(stmt, col), 0); + r->contents = db_col_arr(ctx, stmt, colname, u8); return r; } diff --git a/wallet/db.h b/wallet/db.h index c05e8d4597b6..e51e75fd6b71 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -129,56 +129,6 @@ void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr); bool db_step(struct db_stmt *stmt); -u64 db_column_u64(struct db_stmt *stmt, int col); -int db_column_int(struct db_stmt *stmt, int col); -size_t db_column_bytes(struct db_stmt *stmt, int col); -int db_column_is_null(struct db_stmt *stmt, int col); -const void* db_column_blob(struct db_stmt *stmt, int col); -const unsigned char *db_column_text(struct db_stmt *stmt, int col); -void db_column_preimage(struct db_stmt *stmt, int col, struct preimage *preimage); -void db_column_amount_msat(struct db_stmt *stmt, int col, struct amount_msat *msat); -void db_column_amount_sat(struct db_stmt *stmt, int col, struct amount_sat *sat); -struct json_escape *db_column_json_escape(const tal_t *ctx, struct db_stmt *stmt, int col); -void db_column_sha256(struct db_stmt *stmt, int col, struct sha256 *sha); -void db_column_sha256d(struct db_stmt *stmt, int col, struct sha256_double *shad); -void db_column_secret(struct db_stmt *stmt, int col, struct secret *s); -struct secret *db_column_secret_arr(const tal_t *ctx, struct db_stmt *stmt, - int col); -void db_column_txid(struct db_stmt *stmt, int pos, struct bitcoin_txid *t); -void db_column_channel_id(struct db_stmt *stmt, int col, struct channel_id *dest); -void db_column_node_id(struct db_stmt *stmt, int pos, struct node_id *ni); -struct node_id *db_column_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, - int col); -void db_column_pubkey(struct db_stmt *stmt, int pos, struct pubkey *p); -bool db_column_short_channel_id(struct db_stmt *stmt, int col, - struct short_channel_id *dest); -struct short_channel_id * -db_column_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, int col); -bool db_column_signature(struct db_stmt *stmt, int col, - secp256k1_ecdsa_signature *sig); -struct timeabs db_column_timeabs(struct db_stmt *stmt, int col); -struct bitcoin_tx *db_column_tx(const tal_t *ctx, struct db_stmt *stmt, int col); -struct wally_psbt *db_column_psbt(const tal_t *ctx, struct db_stmt *stmt, int col); -struct bitcoin_tx *db_column_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, int col); - -struct onionreply *db_column_onionreply(const tal_t *ctx, - struct db_stmt *stmt, int col); -u8 *db_column_talarr(const tal_t *ctx, struct db_stmt *stmt, int col); - -#define db_column_arr(ctx, stmt, col, type) \ - ((type *)db_column_arr_((ctx), (stmt), (col), \ - sizeof(type), TAL_LABEL(type, "[]"), \ - __func__)) -void *db_column_arr_(const tal_t *ctx, struct db_stmt *stmt, int col, - size_t bytes, const char *label, const char *caller); - - -/* Some useful default variants */ -int db_column_int_or_default(struct db_stmt *stmt, int col, int def); -void db_column_amount_msat_or_default(struct db_stmt *stmt, int col, - struct amount_msat *msat, - struct amount_msat def); - /* Modern variants: get columns by name from SELECT */ /* Bridge function to get column number from SELECT (must exist) */ From fcf3d0ce6ce51baa641bdd691354a4fcf1cf3939 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:33:46 +1030 Subject: [PATCH 0034/1530] db: turn generated queries array into a simple hash table. Since we have that functionality, let's use it. Also, make table const. Signed-off-by: Rusty Russell --- devtools/sql-rewrite.py | 24 ++++++++++++++++++++---- wallet/db.c | 33 +++++++++++++++++---------------- wallet/db_common.h | 6 +++--- wallet/db_postgres.c | 4 ++-- wallet/db_sqlite3.c | 4 ++-- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py index 6cfb44ce634f..e6f70689c260 100755 --- a/devtools/sql-rewrite.py +++ b/devtools/sql-rewrite.py @@ -27,6 +27,8 @@ def rewrite_single(self, query): def rewrite(self, queries): for i, q in enumerate(queries): + if q['name'] is None: + continue org = q['query'] queries[i]['query'] = self.rewrite_single(org) eprint("Rewritten statement\n\tfrom {}\n\t to {}".format(org, q['query'])) @@ -136,10 +138,11 @@ def colname_htable(query): % endfor -struct db_query db_${f}_queries[] = { +const struct db_query db_${f}_queries[] = { % for elem in queries: { +% if elem['name'] is not None: .name = "${elem['name']}", .query = "${elem['query']}", .placeholders = ${elem['placeholders']}, @@ -147,19 +150,32 @@ def colname_htable(query): % if elem['colnames'] is not None: .colnames = ${elem['colnames']}, .num_colnames = ARRAY_SIZE(${elem['colnames']}), +% endif % endif }, % endfor }; -#define DB_${f.upper()}_QUERY_COUNT ${len(queries)} - #endif /* HAVE_${f.upper()} */ #endif /* LIGHTNINGD_WALLET_GEN_DB_${f.upper()} */ """) +def queries_htable(queries): + # Converts a list of queries into a hash table. + tablesize = len(queries) * 2 - 1 + htable = [{'name': None}] * tablesize + + for q in queries: + pos = hash_djb2(q['name']) % tablesize + while htable[pos]['name'] is not None: + pos = (pos + 1) % tablesize + htable[pos] = q + + return htable + + def extract_queries(pofile): # Given a po-file, extract all queries and their associated names, and # return them as a list. @@ -204,7 +220,7 @@ def chunk(pofile): 'readonly': "true" if is_select else "false", 'colnames': colnames, }) - return colhtables, queries + return colhtables, queries_htable(queries) if __name__ == "__main__": diff --git a/wallet/db.c b/wallet/db.c index d33747ba4c02..4e9eb042bdc6 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -903,12 +903,20 @@ static void db_stmt_free(struct db_stmt *stmt) assert(stmt->inner_stmt == NULL); } +/* Matches the hash function used in devtools/sql-rewrite.py */ +static u32 hash_djb2(const char *str) +{ + u32 hash = 5381; + for (size_t i = 0; str[i]; i++) + hash = ((hash << 5) + hash) ^ str[i]; + return hash; +} + struct db_stmt *db_prepare_v2_(const char *location, struct db *db, const char *query_id) { struct db_stmt *stmt = tal(db, struct db_stmt); - size_t num_slots; - stmt->query = NULL; + size_t num_slots, pos; /* Normalize query_id paths, because unit tests are compiled with this * prefix. */ @@ -920,14 +928,16 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, "transaction: %s", location); /* Look up the query by its ID */ - for (size_t i = 0; i < db->config->num_queries; i++) { - if (streq(query_id, db->config->queries[i].name)) { - stmt->query = &db->config->queries[i]; + pos = hash_djb2(query_id) % db->config->query_table_size; + for (;;) { + if (!db->config->query_table[pos].name) + fatal("Could not resolve query %s", query_id); + if (streq(query_id, db->config->query_table[pos].name)) { + stmt->query = &db->config->query_table[pos]; break; } + pos = (pos + 1) % db->config->query_table_size; } - if (stmt->query == NULL) - fatal("Could not resolve query %s", query_id); num_slots = stmt->query->placeholders; /* Allocate the slots for placeholders/bindings, zeroed next since @@ -2335,15 +2345,6 @@ const char **db_changes(struct db *db) return db->changes; } -/* Matches the hash function used in devtools/sql-rewrite.py */ -static u32 hash_djb2(const char *str) -{ - u32 hash = 5381; - for (size_t i = 0; str[i]; i++) - hash = ((hash << 5) + hash) ^ str[i]; - return hash; -} - size_t db_query_colnum(const struct db_stmt *stmt, const char *colname) { diff --git a/wallet/db_common.h b/wallet/db_common.h index 470ccb2e6bbf..c892402849d3 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -84,7 +84,7 @@ struct db_stmt { struct db *db; /* Which SQL statement are we trying to execute? */ - struct db_query *query; + const struct db_query *query; /* Which parameters are we binding to the statement? */ struct db_binding *bindings; @@ -109,8 +109,8 @@ struct db_stmt { struct db_config { const char *name; - struct db_query *queries; - size_t num_queries; + const struct db_query *query_table; + size_t query_table_size; /* Function used to execute a statement that doesn't result in a * response. */ diff --git a/wallet/db_postgres.c b/wallet/db_postgres.c index 3a190a870fc6..a8a239e93bc2 100644 --- a/wallet/db_postgres.c +++ b/wallet/db_postgres.c @@ -275,8 +275,8 @@ static bool db_postgres_vacuum(struct db *db) struct db_config db_postgres_config = { .name = "postgres", - .queries = db_postgres_queries, - .num_queries = DB_POSTGRES_QUERY_COUNT, + .query_table = db_postgres_queries, + .query_table_size = ARRAY_SIZE(db_postgres_queries), .exec_fn = db_postgres_exec, .query_fn = db_postgres_query, .step_fn = db_postgres_step, diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index 6ff339ffed74..00d6af7daf57 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -253,8 +253,8 @@ static bool db_sqlite3_vacuum(struct db *db) struct db_config db_sqlite3_config = { .name = "sqlite3", - .queries = db_sqlite3_queries, - .num_queries = DB_SQLITE3_QUERY_COUNT, + .query_table = db_sqlite3_queries, + .query_table_size = ARRAY_SIZE(db_sqlite3_queries), .exec_fn = &db_sqlite3_exec, .query_fn = &db_sqlite3_query, .step_fn = &db_sqlite3_step, From 6c34e522dd39d03eb53f3a7068e39b19b01cd74a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 15 Nov 2021 04:34:46 +1030 Subject: [PATCH 0035/1530] wallet: db column manipulation helpers. Removing columns and renaming them is easy in Postgres, hard in sqlite3. Signed-off-by: Rusty Russell --- wallet/db_common.h | 7 ++ wallet/db_postgres.c | 47 +++++++++ wallet/db_sqlite3.c | 235 +++++++++++++++++++++++++++++++++++++++++++ wallet/test/run-db.c | 86 ++++++++++++++++ 4 files changed, 375 insertions(+) diff --git a/wallet/db_common.h b/wallet/db_common.h index c892402849d3..0ab196c29081 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -151,6 +151,13 @@ struct db_config { void (*teardown_fn)(struct db *db); bool (*vacuum_fn)(struct db *db); + + bool (*rename_column)(struct db *db, + const char *tablename, + const char *from, const char *to); + bool (*delete_columns)(struct db *db, + const char *tablename, + const char **colnames, size_t num_cols); }; /* Provide a way for DB backends to register themselves */ diff --git a/wallet/db_postgres.c b/wallet/db_postgres.c index a8a239e93bc2..34f6562d8bb2 100644 --- a/wallet/db_postgres.c +++ b/wallet/db_postgres.c @@ -273,6 +273,51 @@ static bool db_postgres_vacuum(struct db *db) return true; } +static bool db_postgres_rename_column(struct db *db, + const char *tablename, + const char *from, const char *to) +{ + PGresult *res; + char *cmd; + + cmd = tal_fmt(db, "ALTER TABLE %s RENAME %s TO %s;", + tablename, from, to); + res = PQexec(db->conn, cmd); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + db->error = tal_fmt(db, "Rename '%s' failed: %s", + cmd, PQerrorMessage(db->conn)); + PQclear(res); + return false; + } + PQclear(res); + return true; +} + +static bool db_postgres_delete_columns(struct db *db, + const char *tablename, + const char **colnames, size_t num_cols) +{ + PGresult *res; + char *cmd; + + cmd = tal_fmt(db, "ALTER TABLE %s ", tablename); + for (size_t i = 0; i < num_cols; i++) { + if (i != 0) + tal_append_fmt(&cmd, ", "); + tal_append_fmt(&cmd, "DROP %s", colnames[i]); + } + tal_append_fmt(&cmd, ";"); + res = PQexec(db->conn, cmd); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + db->error = tal_fmt(db, "Delete '%s' failed: %s", + cmd, PQerrorMessage(db->conn)); + PQclear(res); + return false; + } + PQclear(res); + return true; +} + struct db_config db_postgres_config = { .name = "postgres", .query_table = db_postgres_queries, @@ -296,6 +341,8 @@ struct db_config db_postgres_config = { .setup_fn = db_postgres_setup, .teardown_fn = db_postgres_teardown, .vacuum_fn = db_postgres_vacuum, + .rename_column = db_postgres_rename_column, + .delete_columns = db_postgres_delete_columns, }; AUTODATA(db_backends, &db_postgres_config); diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index 00d6af7daf57..11d639a556f0 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -1,5 +1,6 @@ #include "db_sqlite3_sqlgen.c" #include +#include #include #if HAVE_SQLITE3 @@ -251,6 +252,238 @@ static bool db_sqlite3_vacuum(struct db *db) return err == SQLITE_DONE; } +static bool colname_to_delete(const char **colnames, + size_t num_colnames, + const char *columnname) +{ + for (size_t i = 0; i < num_colnames; i++) { + if (streq(columnname, colnames[i])) + return true; + } + return false; +} + +static const char *find_column_name(const tal_t *ctx, + const char *sqlpart, + size_t *after) +{ + size_t start = 0; + + while (isspace(sqlpart[start])) + start++; + *after = strspn(sqlpart + start, "abcdefghijklmnopqrstuvwxyz_0123456789") + start; + if (*after == start) + return NULL; + return tal_strndup(ctx, sqlpart + start, *after - start); +} + +/* Move table out the way, return columns */ +static char **prepare_table_manip(const tal_t *ctx, + struct db *db, const char *tablename) +{ + sqlite3_stmt *stmt; + const char *sql; + char *cmd, *bracket; + char **parts; + int err; + + /* Get schema. */ + sqlite3_prepare_v2(db->conn, "SELECT sql FROM sqlite_master WHERE type = ? AND name = ?;", -1, &stmt, NULL); + sqlite3_bind_text(stmt, 1, "table", strlen("table"), SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, tablename, strlen(tablename), SQLITE_TRANSIENT); + err = sqlite3_step(stmt); + if (err != SQLITE_ROW) { + db->error = tal_fmt(db, "getting schema: %s", + sqlite3_errmsg(db->conn)); + sqlite3_finalize(stmt); + return NULL; + } + + sql = tal_strdup(tmpctx, (const char *)sqlite3_column_text(stmt, 0)); + sqlite3_finalize(stmt); + + bracket = strchr(sql, '('); + if (!strstarts(sql, "CREATE TABLE") || !bracket) { + db->error = tal_fmt(db, "strange schema for %s: %s", + tablename, sql); + return NULL; + } + + /* Split after ( by commas: any lower case is assumed to be a field */ + parts = tal_strsplit(ctx, bracket + 1, ",", STR_EMPTY_OK); + + /* Turn off foreign keys first. */ + sqlite3_prepare_v2(db->conn, "PRAGMA foreign_keys = OFF;", -1, &stmt, NULL); + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite_stmt_err; + sqlite3_finalize(stmt); + + cmd = tal_fmt(tmpctx, "ALTER TABLE %s RENAME TO temp_%s;", + tablename, tablename); + sqlite3_prepare_v2(db->conn, cmd, -1, &stmt, NULL); + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite_stmt_err; + sqlite3_finalize(stmt); + + return parts; + +sqlite_stmt_err: + db->error = tal_fmt(db, "%s", sqlite3_errmsg(db->conn)); + sqlite3_finalize(stmt); + return tal_free(parts); +} + +static bool complete_table_manip(struct db *db, + const char *tablename, + const char **coldefs, + const char **oldcolnames) +{ + sqlite3_stmt *stmt; + char *create_cmd, *insert_cmd, *drop_cmd; + + /* Create table */ + create_cmd = tal_fmt(tmpctx, "CREATE TABLE %s (", tablename); + for (size_t i = 0; i < tal_count(coldefs); i++) { + if (i != 0) + tal_append_fmt(&create_cmd, ", "); + tal_append_fmt(&create_cmd, "%s", coldefs[i]); + } + tal_append_fmt(&create_cmd, ";"); + + sqlite3_prepare_v2(db->conn, create_cmd, -1, &stmt, NULL); + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite_stmt_err; + sqlite3_finalize(stmt); + + /* Populate table from old one */ + insert_cmd = tal_fmt(tmpctx, "INSERT INTO %s SELECT ", tablename); + for (size_t i = 0; i < tal_count(oldcolnames); i++) { + if (i != 0) + tal_append_fmt(&insert_cmd, ", "); + tal_append_fmt(&insert_cmd, "%s", oldcolnames[i]); + } + tal_append_fmt(&insert_cmd, " FROM temp_%s;", tablename); + + sqlite3_prepare_v2(db->conn, insert_cmd, -1, &stmt, NULL); + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite_stmt_err; + sqlite3_finalize(stmt); + + /* Cleanup temp table */ + drop_cmd = tal_fmt(tmpctx, "DROP TABLE temp_%s;", tablename); + sqlite3_prepare_v2(db->conn, drop_cmd, -1, &stmt, NULL); + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite_stmt_err; + sqlite3_finalize(stmt); + + /* Allow links between them (esp. cascade deletes!) */ + sqlite3_prepare_v2(db->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); + if (sqlite3_step(stmt) != SQLITE_DONE) + goto sqlite_stmt_err; + sqlite3_finalize(stmt); + + return true; + +sqlite_stmt_err: + db->error = tal_fmt(db, "%s", sqlite3_errmsg(db->conn)); + sqlite3_finalize(stmt); + return false; +} + +static bool db_sqlite3_rename_column(struct db *db, + const char *tablename, + const char *from, const char *to) +{ + char **parts; + const char **coldefs, **oldcolnames; + bool colname_found = false; + + parts = prepare_table_manip(tmpctx, db, tablename); + if (!parts) + return false; + + coldefs = tal_arr(tmpctx, const char *, 0); + oldcolnames = tal_arr(tmpctx, const char *, 0); + + for (size_t i = 0; parts[i]; i++) { + /* columnname DETAILS */ + size_t after_name; + const char *colname = find_column_name(tmpctx, parts[i], + &after_name); + + /* Things like "PRIMARY KEY xxx" must be copied verbatim */ + if (!colname) { + tal_arr_expand(&coldefs, parts[i]); + continue; + } + if (streq(colname, from)) { + char *newdef; + colname_found = true; + /* Create column with new name */ + newdef = tal_fmt(coldefs, + "%s%s", to, parts[i] + after_name); + tal_arr_expand(&coldefs, newdef); + tal_arr_expand(&oldcolnames, colname); + } else { + /* Not mentioned, keep it as is! */ + tal_arr_expand(&coldefs, parts[i]); + tal_arr_expand(&oldcolnames, colname); + } + } + + if (!colname_found) { + db->error = tal_fmt(db, "No column called %s", from); + return false; + } + return complete_table_manip(db, tablename, coldefs, oldcolnames); +} + +static bool db_sqlite3_delete_columns(struct db *db, + const char *tablename, + const char **colnames, size_t num_cols) +{ + char **parts; + const char **coldefs, **oldcolnames; + size_t colnames_found = 0; + + parts = prepare_table_manip(tmpctx, db, tablename); + if (!parts) + return false; + + coldefs = tal_arr(tmpctx, const char *, 0); + oldcolnames = tal_arr(tmpctx, const char *, 0); + + for (size_t i = 0; parts[i]; i++) { + /* columnname DETAILS */ + size_t after_name; + const char *colname = find_column_name(tmpctx, parts[i], + &after_name); + + /* Things like "PRIMARY KEY xxx" must be copied verbatim */ + if (!colname) { + tal_arr_expand(&coldefs, parts[i]); + continue; + } + + /* Don't mention columns we're supposed to delete */ + if (colname_to_delete(colnames, num_cols, colname)) { + colnames_found++; + continue; + } + + /* Keep it as is! */ + tal_arr_expand(&coldefs, parts[i]); + tal_arr_expand(&oldcolnames, colname); + } + + if (colnames_found != num_cols) { + db->error = tal_fmt(db, "Only %zu/%zu columns found", + colnames_found, num_cols); + return false; + } + return complete_table_manip(db, tablename, coldefs, oldcolnames); +} + struct db_config db_sqlite3_config = { .name = "sqlite3", .query_table = db_sqlite3_queries, @@ -275,6 +508,8 @@ struct db_config db_sqlite3_config = { .teardown_fn = &db_sqlite3_close, .vacuum_fn = db_sqlite3_vacuum, + .rename_column = db_sqlite3_rename_column, + .delete_columns = db_sqlite3_delete_columns, }; AUTODATA(db_backends, &db_sqlite3_config); diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 92ae8a87a241..c6123e9ab16d 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -167,6 +167,91 @@ static bool test_vars(struct lightningd *ld) return true; } +static bool test_manip_columns(void) +{ + struct db_stmt *stmt; + struct db *db = create_test_db(); + const char *field1 = "field1"; + + db_begin_transaction(db); + /* tablea refers to tableb */ + stmt = db_prepare_v2(db, SQL("CREATE TABLE tablea (" + " id BIGSERIAL" + ", field1 INTEGER" + ", PRIMARY KEY (id))")); + CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("INSERT INTO tablea (id, field1) VALUES (0, 1);")); + CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("CREATE TABLE tableb (" + " id REFERENCES tablea(id) ON DELETE CASCADE" + ", field1 INTEGER" + ", field2 INTEGER);")); + CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("INSERT INTO tableb (id, field1, field2) VALUES (0, 1, 2);")); + CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + tal_free(stmt); + /* Don't let it try to set a version field (we don't have one!) */ + db->dirty = false; + db->changes = tal_arr(db, const char *, 0); + db_commit_transaction(db); + + /* Rename tablea.field1 -> table1.field1a. */ + CHECK(db->config->rename_column(db, "tablea", "field1", "field1a")); + /* Remove tableb.field1. */ + CHECK(db->config->delete_columns(db, "tableb", &field1, 1)); + + db_begin_transaction(db); + stmt = db_prepare_v2(db, SQL("SELECT id, field1a FROM tablea;")); + CHECK_MSG(db_query_prepared(stmt), "db_query_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + CHECK(db_step(stmt)); + CHECK(db_col_u64(stmt, "id") == 0); + CHECK(db_col_u64(stmt, "field1a") == 1); + CHECK(!db_step(stmt)); + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("SELECT id, field2 FROM tableb;")); + CHECK_MSG(db_query_prepared(stmt), "db_query_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + CHECK(db_step(stmt)); + CHECK(db_col_u64(stmt, "id") == 0); + CHECK(db_col_u64(stmt, "field2") == 2); + CHECK(!db_step(stmt)); + tal_free(stmt); + db->dirty = false; + db->changes = tal_arr(db, const char *, 0); + db_commit_transaction(db); + + db_begin_transaction(db); + /* This will actually fail */ + stmt = db_prepare_v2(db, SQL("SELECT field1 FROM tablea;")); + CHECK_MSG(!db_query_prepared(stmt), "db_query_prepared must fail"); + db->dirty = false; + db->changes = tal_arr(db, const char *, 0); + db_commit_transaction(db); + + db_begin_transaction(db); + /* This will actually fail */ + stmt = db_prepare_v2(db, SQL("SELECT field1 FROM tableb;")); + CHECK_MSG(!db_query_prepared(stmt), "db_query_prepared must fail"); + db->dirty = false; + db->changes = tal_arr(db, const char *, 0); + db_commit_transaction(db); + + tal_free(db); + return true; +} + int main(int argc, char *argv[]) { bool ok = true; @@ -179,6 +264,7 @@ int main(int argc, char *argv[]) ok &= test_empty_db_migrate(ld); ok &= test_vars(ld); ok &= test_primitives(); + ok &= test_manip_columns(); tal_free(ld); common_shutdown(); From a2946836751992678f31e0c1a384d7fb4146a017 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Wed, 27 Oct 2021 17:49:26 +0800 Subject: [PATCH 0036/1530] wallet/db_sqlite3.c: Support direct replication of SQLITE3 backends. ChangeLog-Added: With the `sqlite3://` scheme for `--wallet` option, you can now specify a second file path for real-time database backup by separating it from the main file path with a `:` character. --- doc/BACKUP.md | 89 ++++++++++++++++ doc/lightningd-config.5.md | 7 ++ tests/test_db.py | 34 ++++++ wallet/db_sqlite3.c | 212 +++++++++++++++++++++++++++++++++---- 4 files changed, 324 insertions(+), 18 deletions(-) diff --git a/doc/BACKUP.md b/doc/BACKUP.md index d7a7ca3e8a14..5c582b7c103e 100644 --- a/doc/BACKUP.md +++ b/doc/BACKUP.md @@ -82,6 +82,95 @@ any in-channel funds. To recover in-channel funds, you need to use one or more of the other backup strategies below. +## SQLITE3 `--wallet=${main}:${backup}` And Remote NFS Mount + +`/!\` WHO SHOULD DO THIS: Casual users. + +`/!\` **CAUTION** `/!\` This technique is only supported on 0.10.3 +or later. +On earlier versions, the `:` character is not special and will be +considered part of the path of the database file. + +When using the SQLITE3 backend (the default), you can specify a +second database file to replicate to, by separating the second +file with a single `:` character in the `--wallet` option, after +the main database filename. + +For example, if the user running `lightningd` is named `user`, and +you are on the Bitcoin mainnet with the default `${LIGHTNINGDIR}`, you +can specify in your `config` file: + + wallet=sqlite3:///home/user/.lightning/bitcoin/lightningd.sqlite3:/my/backup/lightningd.sqlite3 + +Or via command line: + + lightningd --wallet=sqlite3:///home/user/.lightning/bitcoin/lightningd.sqlite3:/my/backup/lightningd.sqlite3 + +If the second database file does not exist but the directory that would +contain it does exist, the file is created. +If the directory of the second database file does not exist, `lightningd` will +fail at startup. +If the second database file already exists, on startup it will be overwritten +with the main database. +During operation, all database updates will be done on both databases. + +The main and backup files will **not** be identical at every byte, but they +will still contain the same data. + +It is recommended that you use **the same filename** for both files, just on +different directories. + +This has the advantage compared to the `backup` plugin below of requiring +exactly the same amount of space on both the main and backup storage. +The `backup` plugin will take more space on the backup than on the main +storage. +It has the disadvantage that it will only work with the SQLITE3 backend and +is not supported by the PostgreSQL backend, and is unlikely to be supported +on any future database backends. + +You can only specify *one* replica. + +It is recommended that you use a network-mounted filesystem for the backup +destination. +For example, if you have a NAS you can access remotely. + +At the minimum, set the backup to a different storage device. +This is no better than just using RAID-1 (and the RAID-1 will probably be +faster) but this is easier to set up --- just plug in a commodity USB +flash disk (with metal casing, since a lot of writes are done and you need +to dissipate the heat quickly) and use it as the backup location, without +repartitioning your OS disk, for example. + +Do note that files are not stored encrypted, so you should really not do +this with rented space ("cloud storage"). + +To recover, simply get **all** the backup database files. +Note that SQLITE3 will sometimes create a `-journal` or `-wal` file, which +is necessary to ensure correct recovery of the backup; you need to copy +those too, with corresponding renames if you use a different filename for +the backup database, e.g. if you named the backup `backup.sqlite3` and +when you recover you find `backup.sqlite3` and `backup.sqlite3-journal` +files, you rename `backup.sqlite3` to `lightningd.sqlite3` and +`backup.sqlite3-journal` to `lightningd.sqlite3-journal`. +Note that the `-journal` or `-wal` file may or may not exist, but if they +*do*, you *must* recover them as well +(there can be an `-shm` file as well in WAL mode, but it is unnecessary; +it is only used by SQLITE3 as a hack for portable shared memory, and +contains no useful data; SQLITE3 will ignore its contents always). +It is recommended that you use **the same filename** for both main and +backup databases (just on different directories), and put the backup in +its own directory, so that you can just recover all the files in that +directory without worrying about missing any needed files or correctly +renaming. + +If your backup destination is a network-mounted filesystem that is in a +remote location, then even loss of all hardware in one location will allow +you to still recover your Lightning funds. + +However, if instead you are just replicating the database on another +storage device in a single location, you remain vulnerable to disasters +like fire or computer confiscation. + ## `backup` Plugin And Remote NFS Mount `/!\` WHO SHOULD DO THIS: Casual users. diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 1d947d5f9469..61f3c8c545e5 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -193,6 +193,13 @@ The default wallet corresponds to the following DSN: --wallet=sqlite3://$HOME/.lightning/bitcoin/lightningd.sqlite3 ``` +For the `sqlite3` scheme, you can specify a single backup database file +by separating it with a `:` character, like so: + +``` +--wallet=sqlite3://$HOME/.lightning/bitcoin/lightningd.sqlite3:/backup/lightningd.sqlite3 +``` + The following is an example of a postgresql wallet DSN: ``` diff --git a/tests/test_db.py b/tests/test_db.py index 3d018b55f85e..05379dd1e5ed 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -7,6 +7,7 @@ import base64 import os import pytest +import shutil import time import unittest @@ -379,3 +380,36 @@ def test_local_basepoints_cache(bitcoind, node_factory): # after we verified. l1.restart() l2.restart() + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Tests a feature unique to SQLITE3 backend") +def test_sqlite3_builtin_backup(bitcoind, node_factory): + l1 = node_factory.get_node(start=False) + + # Figure out the path to the actual db. + main_db_file = l1.db.path + # Create a backup copy in the same location with the suffix .bak + backup_db_file = main_db_file + ".bak" + + # Provide the --wallet option and start. + l1.daemon.opts['wallet'] = "sqlite3://" + main_db_file + ':' + backup_db_file + l1.start() + + # Get an address and put some funds. + addr = l1.rpc.newaddr()['bech32'] + bitcoind.rpc.sendtoaddress(addr, 1) + bitcoind.generate_block(1) + wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1) + + # Stop the node. + l1.stop() + + # Copy the backup over the main db file. + shutil.copyfile(backup_db_file, main_db_file) + + # Remove the --wallet option and start. + del l1.daemon.opts['wallet'] + l1.start() + + # Should still see the funds. + assert(len(l1.rpc.listfunds()['outputs']) == 1) diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index 11d639a556f0..8f6f0ec28892 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -6,33 +6,136 @@ #if HAVE_SQLITE3 #include +struct db_sqlite3 { + /* The actual db connection. */ + sqlite3 *conn; + /* A replica db connection, if requested, or NULL otherwise. */ + sqlite3 *backup_conn; +}; + +/** + * @param conn: The db->conn void * pointer. + * + * @return the actual sqlite3 connection. + */ +static inline +sqlite3 *conn2sql(void *conn) +{ + struct db_sqlite3 *wrapper = (struct db_sqlite3 *) conn; + return wrapper->conn; +} + +static void replicate_statement(struct db_sqlite3 *wrapper, + const char *qry) +{ + sqlite3_stmt *stmt; + int err; + + if (!wrapper->backup_conn) + return; + + sqlite3_prepare_v2(wrapper->backup_conn, + qry, -1, &stmt, NULL); + err = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + if (err != SQLITE_DONE) + db_fatal("Failed to replicate query: %s: %s: %s", + sqlite3_errstr(err), + sqlite3_errmsg(wrapper->backup_conn), + qry); +} + +static void db_sqlite3_changes_add(struct db_sqlite3 *wrapper, + struct db_stmt *stmt, + const char *qry) +{ + replicate_statement(wrapper, qry); + db_changes_add(stmt, qry); +} + +/* Check if both sqlite3 databases have a data_version variable, + * *and* are the same. + */ +static bool have_same_data_version(sqlite3 *a, sqlite3 *b) +{ + sqlite3_stmt *stmt; + const char *qry = "SELECT intval FROM vars" + " WHERE name = 'data_version';"; + int err; + + u64 version_a; + u64 version_b; + + sqlite3_prepare_v2(a, qry, -1, &stmt, NULL); + err = sqlite3_step(stmt); + if (err != SQLITE_ROW) { + sqlite3_finalize(stmt); + return false; + } + version_a = sqlite3_column_int64(stmt, 0); + sqlite3_finalize(stmt); + + sqlite3_prepare_v2(b, qry, -1, &stmt, NULL); + err = sqlite3_step(stmt); + if (err != SQLITE_ROW) { + sqlite3_finalize(stmt); + return false; + } + version_b = sqlite3_column_int64(stmt, 0); + sqlite3_finalize(stmt); + + return version_a == version_b; +} + #if !HAVE_SQLITE3_EXPANDED_SQL /* Prior to sqlite3 v3.14, we have to use tracing to dump statements */ +struct db_sqlite3_trace { + struct db_sqlite3 *wrapper; + struct db_stmt *stmt; +}; + static void trace_sqlite3(void *stmtv, const char *stmt) { - struct db_stmt *s = (struct db_stmt*)stmtv; - db_changes_add(s, stmt); + struct db_sqlite3_trace *trace = (struct db_sqlite3_trace *)stmtv; + struct db_sqlite3 *wrapper = trace->wrapper; + struct db_stmt *s = trace->stmt; + db_sqlite3_changes_add(wrapper, s, stmt); } #endif static const char *db_sqlite3_fmt_error(struct db_stmt *stmt) { return tal_fmt(stmt, "%s: %s: %s", stmt->location, stmt->query->query, - sqlite3_errmsg(stmt->db->conn)); + sqlite3_errmsg(conn2sql(stmt->db->conn))); } static bool db_sqlite3_setup(struct db *db) { char *filename; + char *sep; + char *backup_filename = NULL; sqlite3_stmt *stmt; sqlite3 *sql; int err, flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; + struct db_sqlite3 *wrapper; + if (!strstarts(db->filename, "sqlite3://") || strlen(db->filename) < 10) db_fatal("Could not parse the wallet DSN: %s", db->filename); /* Strip the scheme from the dsn. */ filename = db->filename + strlen("sqlite3://"); + /* Look for a replica specification. */ + sep = strchr(filename, ':'); + if (sep) { + /* Split at ':'. */ + filename = tal_strndup(db, filename, sep - filename); + backup_filename = tal_strdup(db, sep + 1); + } + + wrapper = tal(db, struct db_sqlite3); + db->conn = wrapper; err = sqlite3_open_v2(filename, &sql, flags, NULL); @@ -40,7 +143,55 @@ static bool db_sqlite3_setup(struct db *db) db_fatal("failed to open database %s: %s", filename, sqlite3_errstr(err)); } - db->conn = sql; + wrapper->conn = sql; + + if (!backup_filename) + wrapper->backup_conn = NULL; + else { + err = sqlite3_open_v2(backup_filename, + &wrapper->backup_conn, + flags, NULL); + if (err != SQLITE_OK) { + db_fatal("failed to open backup database %s: %s", + backup_filename, + sqlite3_errstr(err)); + } + + sqlite3_prepare_v2(wrapper->backup_conn, + "PRAGMA foreign_keys = ON;", -1, &stmt, + NULL); + err = sqlite3_step(stmt); + sqlite3_finalize(stmt); + + if (err != SQLITE_DONE) { + db_fatal("failed to use backup database %s: %s", + backup_filename, + sqlite3_errstr(err)); + } + } + + /* If we have a backup db, but it does not have a matching + * data_version, copy over the main database. */ + if (wrapper->backup_conn && + !have_same_data_version(wrapper->conn, wrapper->backup_conn)) { + /* Copy the main database over the backup database. */ + sqlite3_backup *copier = sqlite3_backup_init(wrapper->backup_conn, + "main", + wrapper->conn, + "main"); + if (!copier) { + db_fatal("failed to initiate copy to %s: %s", + backup_filename, + sqlite3_errmsg(wrapper->backup_conn)); + } + err = sqlite3_backup_step(copier, -1); + if (err != SQLITE_DONE) { + db_fatal("failed to copy database to %s: %s", + backup_filename, + sqlite3_errstr(err)); + } + sqlite3_backup_finish(copier); + } /* In case another process (litestream?) grabs a lock, we don't * want to return SQLITE_BUSY immediately (which will cause a @@ -48,9 +199,10 @@ static bool db_sqlite3_setup(struct db *db) * We *could* make this an option, but surely the user prefers a * long timeout over an outright crash. */ - sqlite3_busy_timeout(db->conn, 60000); + sqlite3_busy_timeout(conn2sql(db->conn), 60000); - sqlite3_prepare_v2(db->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); + sqlite3_prepare_v2(conn2sql(db->conn), + "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); err = sqlite3_step(stmt); sqlite3_finalize(stmt); return err == SQLITE_DONE; @@ -59,7 +211,7 @@ static bool db_sqlite3_setup(struct db *db) static bool db_sqlite3_query(struct db_stmt *stmt) { sqlite3_stmt *s; - sqlite3 *conn = (sqlite3*)stmt->db->conn; + sqlite3 *conn = conn2sql(stmt->db->conn); int err; err = sqlite3_prepare_v2(conn, stmt->query->query, -1, &s, NULL); @@ -108,10 +260,15 @@ static bool db_sqlite3_exec(struct db_stmt *stmt) { int err; bool success; + struct db_sqlite3 *wrapper = (struct db_sqlite3 *) stmt->db->conn; + #if !HAVE_SQLITE3_EXPANDED_SQL /* Register the tracing function if we don't have an explicit way of * expanding the statement. */ - sqlite3_trace(stmt->db->conn, trace_sqlite3, stmt); + struct db_sqlite3_trace trace; + trace.wrapper = wrapper; + trace.stmt = stmt; + sqlite3_trace(conn2sql(stmt->db->conn), trace_sqlite3, &trace); #endif if (!db_sqlite3_query(stmt)) { @@ -132,7 +289,7 @@ static bool db_sqlite3_exec(struct db_stmt *stmt) /* Manually expand and call the callback */ char *expanded_sql; expanded_sql = sqlite3_expanded_sql(stmt->inner_stmt); - db_changes_add(stmt, expanded_sql); + db_sqlite3_changes_add(wrapper, stmt, expanded_sql); sqlite3_free(expanded_sql); #endif success = true; @@ -141,7 +298,7 @@ static bool db_sqlite3_exec(struct db_stmt *stmt) #if !HAVE_SQLITE3_EXPANDED_SQL /* Unregister the trace callback to avoid it accessing the potentially * stale pointer to stmt */ - sqlite3_trace(stmt->db->conn, NULL, NULL); + sqlite3_trace(conn2sql(stmt->db->conn), NULL, NULL); #endif return success; @@ -157,11 +314,16 @@ static bool db_sqlite3_begin_tx(struct db *db) { int err; char *errmsg; - err = sqlite3_exec(db->conn, "BEGIN TRANSACTION;", NULL, NULL, &errmsg); + + struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn; + + err = sqlite3_exec(conn2sql(db->conn), + "BEGIN TRANSACTION;", NULL, NULL, &errmsg); if (err != SQLITE_OK) { db->error = tal_fmt(db, "Failed to begin a transaction: %s", errmsg); return false; } + replicate_statement(wrapper, "BEGIN TRANSACTION;"); return true; } @@ -169,11 +331,16 @@ static bool db_sqlite3_commit_tx(struct db *db) { int err; char *errmsg; - err = sqlite3_exec(db->conn, "COMMIT;", NULL, NULL, &errmsg); + + struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn; + + err = sqlite3_exec(conn2sql(db->conn), + "COMMIT;", NULL, NULL, &errmsg); if (err != SQLITE_OK) { db->error = tal_fmt(db, "Failed to commit a transaction: %s", errmsg); return false; } + replicate_statement(wrapper, "COMMIT;"); return true; } @@ -222,19 +389,24 @@ static void db_sqlite3_stmt_free(struct db_stmt *stmt) static size_t db_sqlite3_count_changes(struct db_stmt *stmt) { - sqlite3 *s = stmt->db->conn; + sqlite3 *s = conn2sql(stmt->db->conn); return sqlite3_changes(s); } static void db_sqlite3_close(struct db *db) { - sqlite3_close(db->conn); - db->conn = NULL; + struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn; + + if (wrapper->backup_conn) + sqlite3_close(wrapper->backup_conn); + sqlite3_close(wrapper->conn); + + db->conn = tal_free(db->conn); } static u64 db_sqlite3_last_insert_id(struct db_stmt *stmt) { - sqlite3 *s = stmt->db->conn; + sqlite3 *s = conn2sql(stmt->db->conn); return sqlite3_last_insert_rowid(s); } @@ -243,11 +415,15 @@ static bool db_sqlite3_vacuum(struct db *db) int err; sqlite3_stmt *stmt; - sqlite3_prepare_v2(db->conn, "VACUUM;", -1, &stmt, NULL); + struct db_sqlite3 *wrapper = (struct db_sqlite3 *) db->conn; + + sqlite3_prepare_v2(conn2sql(db->conn), "VACUUM;", -1, &stmt, NULL); err = sqlite3_step(stmt); if (err != SQLITE_DONE) - db->error = tal_fmt(db, "%s", sqlite3_errmsg(db->conn)); + db->error = tal_fmt(db, "%s", + sqlite3_errmsg(conn2sql(db->conn))); sqlite3_finalize(stmt); + replicate_statement(wrapper, "VACUUM;"); return err == SQLITE_DONE; } From d8c59fca77213c0b6db3555fedc4c78406f92316 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 16 Nov 2021 10:37:43 +1030 Subject: [PATCH 0037/1530] lightningd: fix compilation error on OpenBSD ``` cc lightningd/subd.c lightningd/subd.c:216:7: error: expected identifier or '(' int stdout = STDOUT_FILENO, stderr = STDERR_FILENO; ^ /usr/include/stdio.h:198:17: note: expanded from macro 'stdout' ^ lightningd/subd.c:216:7: error: expected ')' /usr/include/stdio.h:198:17: note: expanded from macro 'stdout' ^ lightningd/subd.c:216:7: note: to match this '(' /usr/include/stdio.h:198:16: note: expanded from macro 'stdout' ^ lightningd/subd.c:224:12: error: cannot take the address of an rvalue of type 'FILE *' (aka 'struct __sFILE *') fds[1] = &stdout; ^~~~~~~ lightningd/subd.c:225:12: error: cannot take the address of an rvalue of type 'FILE *' (aka 'struct __sFILE *') fds[2] = &stderr; ^~~~~~~ 4 errors generated. gmake: *** [Makefile:279: lightningd/subd.o] Error 1 ``` Changelog-None: introduced since last release. Fixes: #4914 Signed-off-by: Rusty Russell ``` --- lightningd/subd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lightningd/subd.c b/lightningd/subd.c index 066419d29757..178928eda716 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -213,7 +213,7 @@ static int subd(const char *path, const char *name, size_t num_args; char *args[] = { NULL, NULL, NULL, NULL, NULL }; int **fds = tal_arr(tmpctx, int *, 3); - int stdout = STDOUT_FILENO, stderr = STDERR_FILENO; + int stdoutfd = STDOUT_FILENO, stderrfd = STDERR_FILENO; close(childmsg[0]); close(execfail[0]); @@ -221,8 +221,8 @@ static int subd(const char *path, const char *name, /* msg = STDIN (0) */ fds[0] = &childmsg[1]; /* These are untouched */ - fds[1] = &stdout; - fds[2] = &stderr; + fds[1] = &stdoutfd; + fds[2] = &stderrfd; while ((fd = va_arg(*ap, int *)) != NULL) { assert(*fd != -1); From efeb1bc65b0a1429023b2d26ef338db226efa499 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Nov 2021 10:29:40 +1030 Subject: [PATCH 0038/1530] wallet: fix sqlite3 column renaming since db backup merge. Because db->conn is a void *, changing it (from a direct pointer to a pointer to a pair of pointers) did not break compile if one place hadn't been update. The result was a confusing failure: sqlite3 complaining about API misuse, since the db->conn pointer was not a valid db handle any more. This is one case where avoiding a void * is hard: we might not even have the postgresql types, since it might not be installed. But a union would have been superior here. Signed-off-by: Rusty Russell --- wallet/db_sqlite3.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index 8f6f0ec28892..6cc324f6ed63 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -462,15 +462,17 @@ static char **prepare_table_manip(const tal_t *ctx, char *cmd, *bracket; char **parts; int err; + struct db_sqlite3 *wrapper = (struct db_sqlite3 *)db->conn; /* Get schema. */ - sqlite3_prepare_v2(db->conn, "SELECT sql FROM sqlite_master WHERE type = ? AND name = ?;", -1, &stmt, NULL); + sqlite3_prepare_v2(wrapper->conn, "SELECT sql FROM sqlite_master WHERE type = ? AND name = ?;", -1, &stmt, NULL); sqlite3_bind_text(stmt, 1, "table", strlen("table"), SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, tablename, strlen(tablename), SQLITE_TRANSIENT); + err = sqlite3_step(stmt); if (err != SQLITE_ROW) { db->error = tal_fmt(db, "getting schema: %s", - sqlite3_errmsg(db->conn)); + sqlite3_errmsg(wrapper->conn)); sqlite3_finalize(stmt); return NULL; } @@ -489,22 +491,26 @@ static char **prepare_table_manip(const tal_t *ctx, parts = tal_strsplit(ctx, bracket + 1, ",", STR_EMPTY_OK); /* Turn off foreign keys first. */ - sqlite3_prepare_v2(db->conn, "PRAGMA foreign_keys = OFF;", -1, &stmt, NULL); + sqlite3_prepare_v2(wrapper->conn, "PRAGMA foreign_keys = OFF;", -1, &stmt, NULL); if (sqlite3_step(stmt) != SQLITE_DONE) goto sqlite_stmt_err; sqlite3_finalize(stmt); cmd = tal_fmt(tmpctx, "ALTER TABLE %s RENAME TO temp_%s;", tablename, tablename); - sqlite3_prepare_v2(db->conn, cmd, -1, &stmt, NULL); + sqlite3_prepare_v2(wrapper->conn, cmd, -1, &stmt, NULL); if (sqlite3_step(stmt) != SQLITE_DONE) goto sqlite_stmt_err; sqlite3_finalize(stmt); + /* Make sure we do the same to backup! */ + replicate_statement(wrapper, "PRAGMA foreign_keys = OFF;"); + replicate_statement(wrapper, cmd); + return parts; sqlite_stmt_err: - db->error = tal_fmt(db, "%s", sqlite3_errmsg(db->conn)); + db->error = tal_fmt(db, "%s", sqlite3_errmsg(wrapper->conn)); sqlite3_finalize(stmt); return tal_free(parts); } @@ -516,6 +522,7 @@ static bool complete_table_manip(struct db *db, { sqlite3_stmt *stmt; char *create_cmd, *insert_cmd, *drop_cmd; + struct db_sqlite3 *wrapper = (struct db_sqlite3 *)db->conn; /* Create table */ create_cmd = tal_fmt(tmpctx, "CREATE TABLE %s (", tablename); @@ -526,11 +533,14 @@ static bool complete_table_manip(struct db *db, } tal_append_fmt(&create_cmd, ";"); - sqlite3_prepare_v2(db->conn, create_cmd, -1, &stmt, NULL); + sqlite3_prepare_v2(wrapper->conn, create_cmd, -1, &stmt, NULL); if (sqlite3_step(stmt) != SQLITE_DONE) goto sqlite_stmt_err; sqlite3_finalize(stmt); + /* Make sure we do the same to backup! */ + replicate_statement(wrapper, create_cmd); + /* Populate table from old one */ insert_cmd = tal_fmt(tmpctx, "INSERT INTO %s SELECT ", tablename); for (size_t i = 0; i < tal_count(oldcolnames); i++) { @@ -540,28 +550,31 @@ static bool complete_table_manip(struct db *db, } tal_append_fmt(&insert_cmd, " FROM temp_%s;", tablename); - sqlite3_prepare_v2(db->conn, insert_cmd, -1, &stmt, NULL); + sqlite3_prepare_v2(wrapper->conn, insert_cmd, -1, &stmt, NULL); if (sqlite3_step(stmt) != SQLITE_DONE) goto sqlite_stmt_err; sqlite3_finalize(stmt); + replicate_statement(wrapper, insert_cmd); /* Cleanup temp table */ drop_cmd = tal_fmt(tmpctx, "DROP TABLE temp_%s;", tablename); - sqlite3_prepare_v2(db->conn, drop_cmd, -1, &stmt, NULL); + sqlite3_prepare_v2(wrapper->conn, drop_cmd, -1, &stmt, NULL); if (sqlite3_step(stmt) != SQLITE_DONE) goto sqlite_stmt_err; sqlite3_finalize(stmt); + replicate_statement(wrapper, drop_cmd); /* Allow links between them (esp. cascade deletes!) */ - sqlite3_prepare_v2(db->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); + sqlite3_prepare_v2(wrapper->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); if (sqlite3_step(stmt) != SQLITE_DONE) goto sqlite_stmt_err; sqlite3_finalize(stmt); + replicate_statement(wrapper, "PRAGMA foreign_keys = ON;"); return true; sqlite_stmt_err: - db->error = tal_fmt(db, "%s", sqlite3_errmsg(db->conn)); + db->error = tal_fmt(db, "%s", sqlite3_errmsg(wrapper->conn)); sqlite3_finalize(stmt); return false; } From 163d3a9203922a0493cf6038493bd4b5e078d987 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Wed, 17 Nov 2021 18:25:58 +0800 Subject: [PATCH 0039/1530] doc/BACKUP.md: Discourage litestream use. ChangeLog-None --- doc/BACKUP.md | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/doc/BACKUP.md b/doc/BACKUP.md index 5c582b7c103e..c6346e7002c7 100644 --- a/doc/BACKUP.md +++ b/doc/BACKUP.md @@ -418,13 +418,16 @@ This can be difficult to create remote replicas due to the latency. [pqsyncreplication]: https://www.postgresql.org/docs/13/warm-standby.html#SYNCHRONOUS-REPLICATION ## SQLite Litestream Replication -`/!\` WHO SHOULD DO THIS: Casual users -`/!\` **CAUTION** `/!\` This technique will only be safe on 0.10.2 -or later. -Earlier versions will crash regularly with "database is locked" errors, -as Litestream puts a read-shared lock on the database. -0.10.2 adds a 60-second timeout for locking. +`/!\` **CAUTION** `/!\` Previous versions of this document recommended +this technique, but we no longer do so. +According to [issue 4857][], even with a 60-second timeout that we added +in 0.10.2, this leads to constant crashing of `lightningd` in some +situations. +This section will be removed completely six months after 0.10.3. +Consider using `--wallet=sqlite3://${main}:${backup}` above, instead. + +[issue 4857]: https://github.com/ElementsProject/lightning/issues/4857 One of the simpler things on any system is to use Litestream to replicate the SQLite database. It continuously streams SQLite changes to file or external storage - the cloud storage option @@ -575,14 +578,20 @@ Even if the backup is not corrupted, take note that this backup strategy should still be a last resort; recovery of all funds is still not assured with this backup strategy. +`sqlite3` has `.dump` and `VACUUM INTO` commands, but note that +those lock the main database for long time periods, which will +negatively affect your `lightningd` instance. + ### `sqlite3` `.dump` or `VACUUM INTO` Commands -`/!\` **CAUTION** `/!\` This technique will only be safe on 0.10.2 -or later. -Earlier versions will crash regularly with "database is locked" -errors, as `.dump` and `VACUUM INTO` put a read-shared lock on the -database. -0.10.2 adds a 60-second timeout for locking. +`/!\` **CAUTION** `/!\` Previous versions of this document recommended +this technique, but we no longer do so. +According to [issue 4857][], even with a 60-second timeout that we added +in 0.10.2, this may lead to constant crashing of `lightningd` in some +situations; this technique uses substantially the same techniques as +`litestream`. +This section will be removed completely six months after 0.10.3. +Consider using `--wallet=sqlite3://${main}:${backup}` above, instead. Use the `sqlite3` command on the `lightningd.sqlite3` file, and feed it with `.dump "/path/to/backup.sqlite3"` or `VACUUM INTO From a9e261fc20b22d5a77a55276be442ede5baf4fef Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Tue, 23 Nov 2021 14:08:01 +0800 Subject: [PATCH 0040/1530] doc/PLUGINS.md: Clarify that `db_write` hook only works on SQLITE3. I noticed that `wallet/db_postgres.c` never actually calls `db_changes_add`. PostgreSQL arguably has a better replication system (a PostgreSQL cluster) than what `db_write` hook can offer, so rather than make `db_write` work on PostgreSQL, just document that it does not actually work there. ChangeLog-none --- doc/PLUGINS.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index a6f94a77cc63..7b57538de0e8 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -939,7 +939,11 @@ hook subscribers would not get called. ### `db_write` -This hook is called whenever a change is about to be committed to the database. +This hook is called whenever a change is about to be committed to the database, +if you are using a SQLITE3 database (the default). +This hook will be useless (the `"writes"` field will always be empty) if you are +using a PostgreSQL database. + It is currently extremely restricted: 1. a plugin registering for this hook should not perform anything that may cause From 8b348a548566e0edbe0a2176276d8c6739ada6e4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Nov 2021 14:01:18 +1030 Subject: [PATCH 0041/1530] ccan: import udpated version (with better tal_dump() format). Signed-off-by: Rusty Russell --- ccan/README | 2 +- ccan/ccan/base64/base64.c | 6 +++--- ccan/ccan/base64/base64.h | 10 +++++----- ccan/ccan/tal/tal.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ccan/README b/ccan/README index 79a99f513c35..340f4c3bc84f 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2519-gcc888f28 +CCAN version: init-2520-gca7c5a9e diff --git a/ccan/ccan/base64/base64.c b/ccan/ccan/base64/base64.c index a216f4781a50..b2326293a992 100644 --- a/ccan/ccan/base64/base64.c +++ b/ccan/ccan/base64/base64.c @@ -118,7 +118,7 @@ size_t base64_decoded_length(size_t srclen) return ((srclen+3)/4*3); } -int base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3], +ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3], const char src[4]) { signed char a; @@ -143,7 +143,7 @@ int base64_decode_quartet_using_maps(const base64_maps_t *maps, char dest[3], } -int base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3], +ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3], const char * src, const size_t srclen) { char longsrc[4]; @@ -178,7 +178,7 @@ ssize_t base64_decode_using_maps(const base64_maps_t *maps, { ssize_t dest_offset = 0; ssize_t i; - size_t more; + ssize_t more; if (destlen < base64_decoded_length(srclen)) { errno = EOVERFLOW; diff --git a/ccan/ccan/base64/base64.h b/ccan/ccan/base64/base64.h index 405dc63fd445..cef30d257673 100644 --- a/ccan/ccan/base64/base64.h +++ b/ccan/ccan/base64/base64.h @@ -103,8 +103,8 @@ ssize_t base64_decode_using_maps(const base64_maps_t *maps, * @return Number of decoded bytes set in dest. -1 on error (and errno set) * @note sets errno = EDOM if src contains invalid characters */ -int base64_decode_quartet_using_maps(const base64_maps_t *maps, - char dest[3], const char src[4]); +ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps, + char dest[3], const char src[4]); /** * base64_decode_tail_using_maps - decode the final bytes of a base64 string using a specific alphabet @@ -116,8 +116,8 @@ int base64_decode_quartet_using_maps(const base64_maps_t *maps, * @note sets errno = EDOM if src contains invalid characters * @note sets errno = EINVAL if src is an invalid base64 tail */ -int base64_decode_tail_using_maps(const base64_maps_t *maps, char *dest, - const char *src, size_t srclen); +ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char *dest, + const char *src, size_t srclen); /* the rfc4648 functions: */ @@ -212,7 +212,7 @@ ssize_t base64_decode(char *dest, size_t destlen, * @note sets errno = EDOM if src contains invalid characters */ static inline -int base64_decode_quartet(char dest[3], const char src[4]) +ssize_t base64_decode_quartet(char dest[3], const char src[4]) { return base64_decode_quartet_using_maps(&base64_maps_rfc4648, dest, src); diff --git a/ccan/ccan/tal/tal.c b/ccan/ccan/tal/tal.c index fec47ac5c0f5..05d52e1dbe5c 100644 --- a/ccan/ccan/tal/tal.c +++ b/ccan/ccan/tal/tal.c @@ -837,7 +837,7 @@ static void dump_node(unsigned int indent, const struct tal_hdr *t) switch (p->type) { case CHILDREN: c = (struct children *)p; - fprintf(stderr, " CHILDREN(%p):parent=%p,children={%p,%p}\n", + fprintf(stderr, " CHILDREN(%p):parent=%p,children={%p,%p}", p, c->parent, c->children.n.prev, c->children.n.next); break; From 1cbdb9cc1282043a6c73eb1b2005358a4cb7aad5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Nov 2021 14:36:03 +1030 Subject: [PATCH 0042/1530] devtools/taldump-analyze.py: script to analyze tal_dump() output. Example output on my production machine, running v0.10.2rc1-9-g3d8293d: ``` Top sizes by name backtrace: 1172021248 bytes, 4578208 items wallet/txfilter.c:111:struct bitcoin_outpoint: 49584636 bytes, 1377351 items lightningd/plugin.c:1651:char[]: 17261056 bytes, 16 items lightningd/log.c:282:char[]: 16653908 bytes, 838086 items wally_tal: 13390257 bytes, 122503 items lightningd/log.c:250:struct log_entry[]: 7340032 bytes, 1 items lightningd/jsonrpc.c:939:char[]: 6979648 bytes, 214 items lightningd/log.c:440:u8[]: 3278359 bytes, 1338 items common/memleak.c:51:struct memleak_notleak: 2216763 bytes, 2216763 items Top sizes by path ['']: 2579337788 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd']: 831485962 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'wallet/wallet.c:77:struct wallet']: 756169631 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'wallet/wallet.c:77:struct wallet', 'wallet/txfilter.c:133:struct outpointfilter']: 756166227 bytes, 2 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'wallet/wallet.c:77:struct wallet', 'wallet/txfilter.c:133:struct outpointfilter', 'wallet/txfilter.c:134:struct outpointset']: 706581191 bytes, 2 items ['', 'ccan/ccan/tal/link/link.c:40:struct linkable']: 457580181 bytes, 1 items ['', 'ccan/ccan/tal/link/link.c:40:struct linkable', 'lightningd/log.c:236:struct log_book']: 430307262 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/chaintopology.c:1061:struct chain_topology']: 48054597 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/chaintopology.c:1061:struct chain_topology', 'lightningd/chaintopology.c:853:struct block']: 47076437 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/chaintopology.c:1061:struct chain_topology', 'lightningd/chaintopology.c:853:struct block', 'bitcoin/block.c:210:struct bitcoin_tx *[]']: 47034285 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/plugin.c:77:struct plugins']: 17569217 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/plugin.c:77:struct plugins', 'lightningd/plugin.c:246:struct plugin', 'lightningd/plugin.c:1651:char[]']: 16784384 bytes, 5 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/chaintopology.c:1061:struct chain_topology', 'lightningd/chaintopology.c:853:struct block', 'bitcoin/block.c:210:struct bitcoin_tx *[]', 'bitcoin/tx.c:605:struct bitcoin_tx']: 13730127 bytes, 452 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/jsonrpc.c:1189:struct jsonrpc']: 7745890 bytes, 1 items ['', 'ccan/ccan/tal/link/link.c:40:struct linkable', 'lightningd/log.c:236:struct log_book', 'lightningd/log.c:250:struct log_entry[]']: 7340032 bytes, 1 items ['', 'lightningd/lightningd.c:103:struct lightningd', 'lightningd/chaintopology.c:1061:struct chain_topology', 'lightningd/chaintopology.c:853:struct block', 'bitcoin/block.c:210:struct bitcoin_tx *[]', 'bitcoin/tx.c:605:struct bitcoin_tx', 'wally_tal']: 4480328 bytes, 166 items ['', 'tmpctx']: 0 bytes, 1 items ``` --- devtools/taldump-analyze.py | 73 +++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 devtools/taldump-analyze.py diff --git a/devtools/taldump-analyze.py b/devtools/taldump-analyze.py new file mode 100755 index 000000000000..b35562654389 --- /dev/null +++ b/devtools/taldump-analyze.py @@ -0,0 +1,73 @@ +#! /usr/bin/env python3 +import sys +import re + +# If you have names on their own lines, it's an old tal_dump which +# put a \n after children: +# awk '/CHILDREN/ { SAVED=$0 } !/CHILDREN/ { print SAVED$0; SAVED="" }' < lightningd-tal_dump > lightningd-tal_dump-children-on-same-line + +# Dict of paths -> [total size,count] +by_path = {} +by_name = {} + +SIZE_THRESHOLD = 1000000 + + +def process(path, name, indent, bytelen): + # Finish any previous entries. + while indent < len(path): + # Don't track little ones + if by_path[','.join(path)][0] < SIZE_THRESHOLD: + del by_path[','.join(path)] + path = path[:-1] + + # Add new child + assert indent == len(path) + + # Parents must exist! Add bytes to all their tallies + for i in range(0, len(path)): + by_path[','.join(path[:i])][0] += bytelen + path += [name] + # Might already exist + prev = by_path.get(','.join(path), (0, 0)) + by_path[','.join(path)] = [prev[0] + bytelen, prev[1] + 1] + prev = by_name.get(name, (0, 0)) + by_name[name] = (prev[0] + bytelen, prev[1] + 1) + + return path + + +cur_path = [] + +infile = open(sys.argv[1], 'rt') +while True: + line = infile.readline() + if not line: + break + + stripped = line.lstrip(' ') + indent = (len(line) - len(stripped)) // 2 + if not stripped.startswith('0x'): + print("Ignoring {}".format(line), end='') + continue + + bytelen = int(re.search('len=([0-9]*)', line).group(1)) + name = re.search('"(.*)"', line) + if not name: + # This can only happen at the root! + assert cur_path == [] + name = '' + else: + name = name.group(1) + cur_path = process(cur_path, name, indent, bytelen) + + +print("Top sizes by name") +for k, v in sorted(by_name.items(), key=lambda x: -x[1][0]): + if v[0] < SIZE_THRESHOLD: + break + print("{}: {} bytes, {} items".format(k, v[0], v[1])) + +print("Top sizes by path") +for k, v in sorted(by_path.items(), key=lambda x: -x[1][0]): + print("{}: {} bytes, {} items".format(k.split(','), v[0], v[1])) From 86e49e02739f6ecd42a18778e3be07370bb0d286 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Nov 2021 14:36:05 +1030 Subject: [PATCH 0043/1530] lightningd: remove some unnneded notleak(). We now reach into the uintmap, so this is unnecesary. Signed-off-by: Rusty Russell --- lightningd/bitcoind.c | 3 +-- lightningd/channel_control.c | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 2a6e41689865..ba6bf9d63cba 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -405,8 +405,7 @@ void bitcoind_getrawblockbyheight_(struct bitcoind *bitcoind, req = jsonrpc_request_start(bitcoind, "getrawblockbyheight", bitcoind->log, NULL, getrawblockbyheight_callback, - /* Freed in cb. */ - notleak(call)); + call); json_add_num(req->stream, "height", height); jsonrpc_request_end(req); bitcoin_plugin_send(bitcoind, req); diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index ea169e304e9d..c0169222097a 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -992,7 +992,8 @@ struct command_result *cancel_channel_before_broadcast(struct command *cmd, bitcoind_getutxout(cmd->ld->topology->bitcoind, &cancel_channel->funding, process_check_funding_broadcast, - notleak(tal_steal(NULL, cc))); + /* Freed by callback */ + tal_steal(NULL, cc)); return command_still_pending(cmd); } From 0b45d862feb4662509ad1d6cf2e33a058af5f969 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Nov 2021 06:29:18 +1030 Subject: [PATCH 0044/1530] log: don't leak log prefixes. Do proper refcounting on log prefixes; previously we kept them around, which is fine, but the extra notleak() and backtrace in developer mode could get quite heavy (I ended up with 1G of backtraces!). This is mainly due to creating one on every JSONRPC command, and running clboss. Signed-off-by: Rusty Russell Changelog-Fixed: lightningd: remove slow memory leak in DEVELOPER builds. --- lightningd/log.c | 53 +++++++++++++++++++++++-------- lightningd/log.h | 9 +++++- lightningd/notification.c | 2 +- lightningd/test/run-log-pruning.c | 3 +- 4 files changed, 50 insertions(+), 17 deletions(-) diff --git a/lightningd/log.c b/lightningd/log.c index 3d8385eeeb6c..84603aea1112 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -55,12 +55,34 @@ struct log_book { struct log { struct log_book *lr; const struct node_id *default_node_id; - const char *prefix; + struct log_prefix *prefix; /* Non-NULL once it's been initialized */ enum log_level *print_level; }; +static struct log_prefix *log_prefix_new(const tal_t *ctx, + const char *prefix TAKES) +{ + struct log_prefix *lp = tal(ctx, struct log_prefix); + lp->refcnt = 1; + lp->prefix = tal_strdup(lp, prefix); + return lp; +} + +static void log_prefix_drop(struct log_prefix *lp) +{ + if (--lp->refcnt == 0) + tal_free(lp); +} + +static struct log_prefix *log_prefix_get(struct log_prefix *lp) +{ + assert(lp->refcnt); + lp->refcnt++; + return lp; +} + /* Avoids duplicate node_id entries. */ struct node_id_cache { size_t count; @@ -184,6 +206,7 @@ static size_t delete_entry(struct log_book *log, struct log_entry *i) if (i->nc && --i->nc->count == 0) tal_free(i->nc); free(i->log); + log_prefix_drop(i->prefix); tal_free(i->io); return 1 + i->skipped; @@ -254,13 +277,14 @@ struct log_book *new_log_book(struct lightningd *ld, size_t max_mem) return lr; } -static enum log_level filter_level(struct log_book *lr, const char *prefix) +static enum log_level filter_level(struct log_book *lr, + const struct log_prefix *lp) { struct print_filter *i; assert(lr->default_print_level != NULL); list_for_each(&lr->print_filters, i, list) { - if (strstr(prefix, i->prefix)) + if (strstr(lp->prefix, i->prefix)) return i->level; } return *lr->default_print_level; @@ -277,9 +301,9 @@ new_log(const tal_t *ctx, struct log_book *record, log->lr = tal_link(log, record); va_start(ap, fmt); - /* log->lr owns this, since its entries keep a pointer to it. */ - /* FIXME: Refcount this! */ - log->prefix = notleak(tal_vfmt(log->lr, fmt, ap)); + /* Owned by the log book itself, since it can be referenced + * by log entries, too */ + log->prefix = log_prefix_new(log->lr, take(tal_vfmt(NULL, fmt, ap))); va_end(ap); if (default_node_id) log->default_node_id = tal_dup(log, struct node_id, @@ -294,7 +318,7 @@ new_log(const tal_t *ctx, struct log_book *record, const char *log_prefix(const struct log *log) { - return log->prefix; + return log->prefix->prefix; } enum log_level log_print_level(struct log *log) @@ -343,7 +367,7 @@ static struct log_entry *new_log_entry(struct log *log, enum log_level level, l->time = time_now(); l->level = level; l->skipped = 0; - l->prefix = log->prefix; + l->prefix = log_prefix_get(log->prefix); l->io = NULL; if (!node_id) node_id = log->default_node_id; @@ -367,7 +391,7 @@ static struct log_entry *new_log_entry(struct log *log, enum log_level level, static void maybe_print(struct log *log, const struct log_entry *l) { if (l->level >= log_print_level(log)) - log_to_file(log->prefix, l->level, + log_to_file(log->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, l->log, l->io, tal_bytelen(l->io), @@ -417,7 +441,7 @@ void log_io(struct log *log, enum log_level dir, /* Print first, in case we need to truncate. */ if (l->level >= log_print_level(log)) - log_to_file(log->prefix, l->level, + log_to_file(log->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, str, data, len, @@ -482,7 +506,7 @@ static void log_each_line_(const struct log_book *lr, func(l->skipped, time_between(l->time, lr->init_time), l->level, l->nc ? &l->nc->node_id : NULL, - l->prefix, l->log, l->io, arg); + l->prefix->prefix, l->log, l->io, arg); } } @@ -585,13 +609,14 @@ static void show_log_level(char buf[OPT_SHOW_LEN], const struct log *log) static char *arg_log_prefix(const char *arg, struct log *log) { /* log->lr owns this, since it keeps a pointer to it. */ - log->prefix = tal_strdup(log->lr, arg); + tal_free(log->prefix); + log->prefix = log_prefix_new(log->lr, arg); return NULL; } static void show_log_prefix(char buf[OPT_SHOW_LEN], const struct log *log) { - strncpy(buf, log->prefix, OPT_SHOW_LEN); + strncpy(buf, log->prefix->prefix, OPT_SHOW_LEN); } static int signalfds[2]; @@ -712,7 +737,7 @@ void logging_options_parsed(struct log_book *lr) const struct log_entry *l = &lr->log[i]; if (l->level >= filter_level(lr, l->prefix)) - log_to_file(l->prefix, l->level, + log_to_file(l->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, l->log, l->io, tal_bytelen(l->io), diff --git a/lightningd/log.h b/lightningd/log.h index e66e1010066e..daf7896e3763 100644 --- a/lightningd/log.h +++ b/lightningd/log.h @@ -69,12 +69,19 @@ struct command_result *param_loglevel(struct command *cmd, const jsmntok_t *tok, enum log_level **level); +/* Reference counted log_prefix. Log entries keep a pointer, and they + * can outlast the log entry point which created them. */ +struct log_prefix { + size_t refcnt; + const char *prefix; +}; + struct log_entry { struct timeabs time; enum log_level level; unsigned int skipped; struct node_id_cache *nc; - const char *prefix; + struct log_prefix *prefix; char *log; /* Iff LOG_IO */ const u8 *io; diff --git a/lightningd/notification.c b/lightningd/notification.c index fa2e38b09aea..f8bdd2924306 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -108,7 +108,7 @@ static void warning_notification_serialize(struct json_stream *stream, * the absolute time, like when channels failed. */ json_add_time(stream, "time", l->time.ts); json_add_timeiso(stream, "timestamp", &l->time); - json_add_string(stream, "source", l->prefix); + json_add_string(stream, "source", l->prefix->prefix); json_add_string(stream, "log", l->log); json_object_end(stream); /* .warning */ } diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index c59d9d2631cf..d029aac3498a 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -110,7 +110,8 @@ int main(int argc, char *argv[]) assert(lb->log[i].level == LOG_DBG); assert(lb->log[i].skipped == 0); assert(lb->log[i].nc == NULL); - assert(streq(lb->log[i].prefix, "test prefix")); + assert(lb->log[i].prefix->refcnt == 101); + assert(streq(lb->log[i].prefix->prefix, "test prefix")); assert(streq(lb->log[i].log, tal_fmt(lb, "test %06zi", i))); assert(lb->log[i].io == NULL); } From 6c9b75275191f0efdf1036ec5b26aa334662462b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Nov 2021 06:29:20 +1030 Subject: [PATCH 0045/1530] memleak: make notleak use the tal name instead of a boutique struct. This lets us mark it directly. Get rid of long-unused "notleaks" member of struct lightningd too. Signed-off-by: Rusty Russell --- common/memleak.c | 45 ++++++++++--------- common/memleak.h | 2 +- common/test/run-bolt12_merkle-json.c | 6 --- common/test/run-bolt12_merkle.c | 6 --- common/test/run-gossmap_local.c | 6 --- common/test/run-json.c | 6 --- common/test/run-param.c | 6 --- common/test/run-route-specific.c | 6 --- common/test/run-route.c | 6 --- connectd/test/run-initiator-success.c | 3 -- connectd/test/run-responder-success.c | 3 -- gossipd/test/run-check_channel_announcement.c | 8 +--- gossipd/test/run-check_node_announcement.c | 8 +--- gossipd/test/run-crc32_of_update.c | 8 +--- gossipd/test/run-extended-info.c | 6 --- gossipd/test/run-next_block_range.c | 6 --- gossipd/test/run-txout_failure.c | 8 +--- lightningd/lightningd.h | 3 -- onchaind/test/run-grind_feerate-bug.c | 2 +- onchaind/test/run-grind_feerate.c | 2 +- plugins/test/run-route-overlong.c | 2 +- wallet/test/run-wallet.c | 4 -- wire/test/run-peer-wire.c | 6 --- wire/test/run-tlvstream.c | 6 --- 24 files changed, 32 insertions(+), 132 deletions(-) diff --git a/common/memleak.c b/common/memleak.c index 74de106dd090..c436cdc7ded5 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -25,31 +25,37 @@ #include #include #include +#include #include +#include struct backtrace_state *backtrace_state; #if DEVELOPER static bool memleak_track; -struct memleak_notleak { - bool plus_children; -}; - struct memleak_helper { void (*cb)(struct htable *memtable, const tal_t *); }; -void *notleak_(const void *ptr, bool plus_children) +void *notleak_(void *ptr, bool plus_children) { - struct memleak_notleak *notleak; - + const char *name; /* If we're not tracking, don't do anything. */ if (!memleak_track) return cast_const(void *, ptr); - notleak = tal(ptr, struct memleak_notleak); - notleak->plus_children = plus_children; + /* We use special tal names to mark notleak */ + name = tal_name(ptr); + if (!name) + name = ""; + if (plus_children) + name = tal_fmt(tmpctx, "%s **NOTLEAK_IGNORE_CHILDREN**", + name); + else + name = tal_fmt(tmpctx, "%s **NOTLEAK**", name); + tal_set_name(ptr, name); + return cast_const(void *, ptr); } @@ -80,9 +86,8 @@ static void children_into_htable(const void *exclude1, const void *exclude2, if (streq(name, "backtrace")) continue; - /* Don't add our own memleak_helpers or notleak() */ - if (strends(name, "struct memleak_helper") - || strends(name, "struct memleak_notleak")) + /* Don't add our own memleak_helpers */ + if (strends(name, "struct memleak_helper")) continue; /* Don't add tal_link objects */ @@ -101,7 +106,6 @@ static void children_into_htable(const void *exclude1, const void *exclude2, if (streq(name, "tmpctx")) continue; } - htable_add(memtable, hash_ptr(i, NULL), i); children_into_htable(exclude1, exclude2, memtable, i); } } @@ -265,12 +269,11 @@ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) if (name && strends(name, "struct memleak_helper")) { const struct memleak_helper *mh = i; mh->cb(memtable, p); - } else if (name && strends(name, "struct memleak_notleak")) { - const struct memleak_notleak *notleak = i; - if (notleak->plus_children) - remove_with_children(memtable, p); - else - pointer_referenced(memtable, p); + } else if (name && strends(name, " **NOTLEAK**")) { + pointer_referenced(memtable, p); + memleak_remove_region(memtable, p, tal_bytelen(p)); + } else if (name && strends(name, " **NOTLEAK_IGNORE_CHILDREN**")) { + remove_with_children(memtable, p); memleak_remove_region(memtable, p, tal_bytelen(p)); } else if (name && strends(name, "_notleak")) { pointer_referenced(memtable, i); @@ -360,8 +363,8 @@ bool dump_memleak(struct htable *memtable, return found_leak; } #else /* !DEVELOPER */ -void *notleak_(const void *ptr, bool plus_children UNNEEDED) +void *notleak_(void *ptr, bool plus_children UNNEEDED) { - return cast_const(void *, ptr); + return ptr; } #endif /* !DEVELOPER */ diff --git a/common/memleak.h b/common/memleak.h index 3460988cf890..aa72ba5812cc 100644 --- a/common/memleak.h +++ b/common/memleak.h @@ -35,7 +35,7 @@ void memleak_init(void); #define memleak_typeof(var) void * #endif /* !HAVE_TYPEOF */ -void *notleak_(const void *ptr, bool plus_children); +void *notleak_(void *ptr, bool plus_children); #if DEVELOPER /** diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index 1fb1b9916754..eb62379cbc7c 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -22,9 +22,6 @@ void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } @@ -50,9 +47,6 @@ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } diff --git a/common/test/run-bolt12_merkle.c b/common/test/run-bolt12_merkle.c index f403361904a1..4799255fc26c 100644 --- a/common/test/run-bolt12_merkle.c +++ b/common/test/run-bolt12_merkle.c @@ -24,18 +24,12 @@ int features_unsupported(const struct feature_set *our_features UNNEEDED, void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_onionmsg_path */ struct onionmsg_path *fromwire_onionmsg_path(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "fromwire_onionmsg_path called!\n"); abort(); } /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_onionmsg_path */ void towire_onionmsg_path(u8 **p UNNEEDED, const struct onionmsg_path *onionmsg_path UNNEEDED) { fprintf(stderr, "towire_onionmsg_path called!\n"); abort(); } diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 4dcedc6b0882..e2a198f54e96 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -17,18 +17,12 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for type_to_string_ */ const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, union printable_types u UNNEEDED) diff --git a/common/test/run-json.c b/common/test/run-json.c index 3434b5a02d72..c1e51ba2fb5a 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -7,9 +7,6 @@ #include /* AUTOGENERATED MOCKS START */ -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, @@ -19,9 +16,6 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, size_t *err_index UNNEEDED) { fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-param.c b/common/test/run-param.c index b74a5a86c659..71bdd277d2ce 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -34,9 +34,6 @@ struct command_result *command_fail(struct command *cmd, } /* AUTOGENERATED MOCKS START */ -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, @@ -83,9 +80,6 @@ int segwit_addr_decode( bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, size_t *err_index UNNEEDED) { fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index b0e80ba30231..4451e51ae216 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -26,9 +26,6 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, @@ -44,9 +41,6 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-route.c b/common/test/run-route.c index 53dd81a930d6..07dd66e3d3e0 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -19,9 +19,6 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, @@ -37,9 +34,6 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index 926fa7c8a7de..627e0e2414dd 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -83,9 +83,6 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/connectd/test/run-responder-success.c b/connectd/test/run-responder-success.c index 0a24a1009922..a531f19ca35a 100644 --- a/connectd/test/run-responder-success.c +++ b/connectd/test/run-responder-success.c @@ -83,9 +83,6 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 58e489332b79..20ef9b41919a 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -43,9 +43,6 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_wireaddr_array */ struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) { fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } @@ -117,7 +114,7 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } /* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) @@ -128,9 +125,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int main(int argc, char *argv[]) diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index c4a9818ec299..3c637a685eb4 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -11,9 +11,6 @@ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_update */ bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } @@ -63,7 +60,7 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } /* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for queue_peer_msg */ void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) @@ -81,9 +78,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_hsmd_cupdate_sig_req */ u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED) { fprintf(stderr, "towire_hsmd_cupdate_sig_req called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 2d59a9ca0603..1dbf50df000a 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -33,9 +33,6 @@ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } @@ -96,7 +93,7 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } /* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) @@ -121,9 +118,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_hsmd_cupdate_sig_req */ u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED) { fprintf(stderr, "towire_hsmd_cupdate_sig_req called!\n"); abort(); } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 276caf655d18..935dca7c33e0 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -36,9 +36,6 @@ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *e /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } @@ -85,9 +82,6 @@ void queue_peer_from_store(struct peer *peer UNNEEDED, /* Generated stub for queue_peer_msg */ void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "queue_peer_msg called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index c57081c1fcfa..aad6a39699bd 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -10,9 +10,6 @@ /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -71,9 +68,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for would_ratelimit_cupdate */ bool would_ratelimit_cupdate(struct routing_state *rstate UNNEEDED, const struct half_chan *hc UNNEEDED, diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index a196f90f85bc..e07e6d461430 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -14,9 +14,6 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for fromwire_wireaddr_array */ struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) { fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } @@ -78,7 +75,7 @@ bool nannounce_different(struct gossip_store *gs UNNEEDED, bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) @@ -93,9 +90,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 772de83923b3..8fabc615076a 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -226,9 +226,6 @@ struct lightningd { bool dev_fast_gossip; bool dev_fast_gossip_prune; - /* Things we've marked as not leaking. */ - const void **notleaks; - /* This is the forced private key for the node. */ struct privkey *dev_force_privkey; diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 1798dc456976..87268cbba0a2 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -170,7 +170,7 @@ struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx UNNEEDED, struct amount_sat amount UNNEEDED) { fprintf(stderr, "new_coin_withdrawal_sat called!\n"); abort(); } /* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for onchaind_wire_name */ const char *onchaind_wire_name(int e UNNEEDED) diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 5f5f8495ae7b..d15c6afc968e 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -193,7 +193,7 @@ struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx UNNEEDED, struct amount_sat amount UNNEEDED) { fprintf(stderr, "new_coin_withdrawal_sat called!\n"); abort(); } /* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for peer_billboard */ void peer_billboard(bool perm UNNEEDED, const char *fmt UNNEEDED, ...) diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 3b4159ce2c9b..c4abb47e50f1 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -193,7 +193,7 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED, struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED) { fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); } /* Generated stub for notleak_ */ -void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for plugin_err */ void plugin_err(struct plugin *p UNNEEDED, const char *fmt UNNEEDED, ...) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 533fd19ae7f5..e5b170ccd1b7 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -280,10 +280,6 @@ void json_add_channel_id(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, const struct channel_id *cid UNNEEDED) { fprintf(stderr, "json_add_channel_id called!\n"); abort(); } -/* Generated stub for json_add_hex */ -void json_add_hex(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, - const void *data UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "json_add_hex called!\n"); abort(); } /* Generated stub for json_add_hex_talarr */ void json_add_hex_talarr(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 327a0c59cd38..91a641da4599 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -16,12 +16,6 @@ extern secp256k1_context *secp256k1_ctx; /* AUTOGENERATED MOCKS START */ -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* memsetting pubkeys doesn't work */ diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index c0e280ab05c4..3c06a7e9f1dc 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -25,15 +25,9 @@ static const char *reason; void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_channel_type */ -struct channel_type *fromwire_channel_type(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) -{ fprintf(stderr, "fromwire_channel_type called!\n"); abort(); } /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_channel_type */ -void towire_channel_type(u8 **p UNNEEDED, const struct channel_type *channel_type UNNEEDED) -{ fprintf(stderr, "towire_channel_type called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ From c1f534a159ad97eaaea1cd61bcc54b1e9bcd620c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 25 Nov 2021 06:29:20 +1030 Subject: [PATCH 0046/1530] backtrace: avoid duplicate backtrace objects. This happened in my tal_dump(), and I couldn't see how we ended up with object having more than one "backtrace". Adding asserts that we never added a second backtrace didn't trigger. Finally I wondered if we were tal_steal() backtraces, and sure enough we do that blinding in one place: libwally wrapping. So fix that. Signed-off-by: Rusty Russell --- common/utils.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/common/utils.c b/common/utils.c index a9ed0f23c986..311de3e0a8e6 100644 --- a/common/utils.c +++ b/common/utils.c @@ -32,8 +32,16 @@ void tal_wally_end(const tal_t *parent) { tal_t *p; while ((p = tal_first(wally_tal_ctx)) != NULL) { - if (p != parent) + if (p != parent) { +#if DEVELOPER + /* Don't steal backtrace from wally_tal_ctx! */ + if (tal_name(p) && streq(tal_name(p), "backtrace")) { + tal_free(p); + continue; + } +#endif /* DEVELOPER */ tal_steal(parent, p); + } } wally_tal_ctx = tal_free(wally_tal_ctx); } From 7a6fd700781688ab451ebfc6671e864ec545fbd1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 27 Nov 2021 20:44:51 +1030 Subject: [PATCH 0047/1530] pytest: Reduce memory consumption by test_plugin_disable It runs 6 nodes: under valgrind this ends up consuming 5.3 GB RSS. By stopping nodes between, we peak about 1G RSS. Measured using: (while true; do echo $(for i in 4 5 6; do ps uh | tr -s ' ' | cut -d\ -f$i | tally; done); sleep 5; done)& (Which measures my other processes as well, but that's only about 100M). Signed-off-by: Rusty Russell --- tests/test_plugin.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index c8eeff43486b..ce8476f8fbf8 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -282,6 +282,7 @@ def test_plugin_disable(node_factory): with pytest.raises(RpcError): n.rpc.hello(name='Sun') assert n.daemon.is_in_log('helloworld.py: disabled via disable-plugin') + n.stop() # Also works by basename. n = node_factory.get_node(options=OrderedDict([('plugin-dir', plugin_dir), @@ -290,6 +291,7 @@ def test_plugin_disable(node_factory): with pytest.raises(RpcError): n.rpc.hello(name='Sun') assert n.daemon.is_in_log('helloworld.py: disabled via disable-plugin') + n.stop() # Other order also works! n = node_factory.get_node(options=OrderedDict([('disable-plugin', @@ -298,6 +300,7 @@ def test_plugin_disable(node_factory): with pytest.raises(RpcError): n.rpc.hello(name='Sun') assert n.daemon.is_in_log('helloworld.py: disabled via disable-plugin') + n.stop() # Both orders of explicit specification work. n = node_factory.get_node(options=OrderedDict([('disable-plugin', @@ -308,6 +311,7 @@ def test_plugin_disable(node_factory): with pytest.raises(RpcError): n.rpc.hello(name='Sun') assert n.daemon.is_in_log('helloworld.py: disabled via disable-plugin') + n.stop() # Both orders of explicit specification work. n = node_factory.get_node(options=OrderedDict([('plugin', @@ -322,6 +326,7 @@ def test_plugin_disable(node_factory): # Still disabled if we load directory. n.rpc.plugin_startdir(directory=os.path.join(os.getcwd(), "contrib/plugins")) n.daemon.wait_for_log('helloworld.py: disabled via disable-plugin') + n.stop() # Check that list works n = node_factory.get_node(options={'disable-plugin': From e6ba66063e04470b7521698aeaccc054c4d21471 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 27 Nov 2021 21:47:59 +1030 Subject: [PATCH 0048/1530] pytest: don't valgrind plugins under CI if many nodes. Just pick one at random to trace fully. Eg. test_gossip.py::test_getroute_exclude (creates 5 nodes) Before: %MEM VSZ RSS 27.6 15941008 5106764 After: %MEM VSZ RSS 15.6 12234844 3009016 Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index dfc2fd666017..3d0723e866f3 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -640,6 +640,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai allow_bad_gossip=False, db=None, port=None, disconnect=None, random_hsm=None, options=None, jsonschemas={}, + valgrind_plugins=True, **kwargs): self.bitcoin = bitcoind self.executor = executor @@ -690,11 +691,14 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai if dsn is not None: self.daemon.opts['wallet'] = dsn if valgrind: + trace_skip_pattern = '*python*,*bitcoin-cli*,*elements-cli*' + if not valgrind_plugins: + trace_skip_pattern += ',*plugins*' self.daemon.cmd_prefix = [ 'valgrind', '-q', '--trace-children=yes', - '--trace-children-skip=*python*,*bitcoin-cli*,*elements-cli*', + '--trace-children-skip={}'.format(trace_skip_pattern), '--error-exitcode=7', '--log-file={}/valgrind-errors.%p'.format(self.daemon.lightning_dir) ] @@ -1280,12 +1284,20 @@ def get_nodes(self, num_nodes, opts=None): assert len(opts) == num_nodes + # Only trace one random node's plugins, to avoid OOM. + if SLOW_MACHINE: + valgrind_plugins = [False] * num_nodes + valgrind_plugins[random.randint(0, num_nodes - 1)] = True + else: + valgrind_plugins = [True] * num_nodes + jobs = [] for i in range(num_nodes): node_opts, cli_opts = self.split_options(opts[i]) jobs.append(self.executor.submit( self.get_node, options=cli_opts, - node_id=self.get_node_id(), **node_opts + node_id=self.get_node_id(), **node_opts, + valgrind_plugins=valgrind_plugins[i] )) return [j.result() for j in jobs] From a642d9f3dcabd7fce624587118fef58ad40e5131 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Wed, 24 Nov 2021 08:50:48 +0800 Subject: [PATCH 0049/1530] bitcoin/chainparams.c: Change `signet` BIP173 name to `tbs`. Fixes: #4924 ChangeLog-Changed: `signet` addresses and invoices now use `tbs` instead of `tb`. --- bitcoin/chainparams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index 9fb6d9c04a2b..2f61badbad33 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -79,7 +79,7 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_TEST_PRIVATE}, .is_elements = false}, {.network_name = "signet", - .bip173_name = "tb", + .bip173_name = "tbs", .bip70_name = "signet", // 00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6 .genesis_blockhash = {{{.u.u8 = {0xf6, 0x1e, 0xee, 0x3b, 0x63, 0xa3, 0x80, From 38df2a3a4caa4dc9d61d0fd9760643e84b002107 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 4 Nov 2021 15:10:21 +0100 Subject: [PATCH 0050/1530] ci: Introduce doc sanity check in Github action. Signed-off-by: Vincenzo Palazzo --- .github/workflows/ci.yaml | 26 +++ doc/lightning-listconfigs.7 | 273 ++++++++++++++++++++++++++++ doc/lightning-listconfigs.7.md | 1 + doc/schemas/listconfigs.schema.json | 4 + 4 files changed, 304 insertions(+) create mode 100644 doc/lightning-listconfigs.7 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 89c39b0793c0..f71e5c72ce85 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -68,6 +68,32 @@ jobs: path: report.* if-no-files-found: ignore + check-dock: + name: Check c-lightning doc + runs-on: ubuntu-20.04 + env: + DEVELOPER: 1 + VALGRIND: 0 + EXPERIMENTAL_FEATURES: 0 + COMPAT: 1 + steps: + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Set up Python 3.6 + uses: actions/setup-python@v2 + with: + python-version: 3.6 + + - name: Install dependencies + run: bash -x .github/scripts/setup.sh + + - name: Check Doc + run: | + pip install mako + ./configure + make check-doc + proto-test: name: Protocol Test Config runs-on: ubuntu-20.04 diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 new file mode 100644 index 000000000000..1ef66bf32009 --- /dev/null +++ b/doc/lightning-listconfigs.7 @@ -0,0 +1,273 @@ +.TH "LIGHTNING-LISTCONFIGS" "7" "" "" "lightning-listconfigs" +.SH NAME +lightning-listconfigs - Command to list all configuration options\. +.SH SYNOPSIS + +\fBlistconfigs\fR [config] + +.SH DESCRIPTION + +The \fBlistconfigs\fR RPC command to list all configuration options, or with \fIconfig\fR, just that one\. + + +The returned values reflect the current configuration, including +showing default values (\fBdev-\fR options are not shown)\. + +.SH EXAMPLE JSON REQUEST +.nf +.RS +{ + "id": 82, + "method": "listconfigs", + "params": { + "config": "network" + } +} +.RE + +.fi +.SH RETURN VALUE + +On success, an object is returned, containing: + +.RS +.IP \[bu] +\fB# version\fR (string, optional): Special field indicating the current version +.IP \[bu] +\fBplugins\fR (array of objects, optional): +.RS +.IP \[bu] +\fBpath\fR (string): Full path of the plugin +.IP \[bu] +\fBname\fR (string): short name of the plugin +.IP \[bu] +\fBoptions\fR (object, optional): Specific options set for this plugin: + +.RE + +.IP \[bu] +\fBimportant-plugins\fR (array of objects, optional): +.RS +.IP \[bu] +\fBpath\fR (string): Full path of the plugin +.IP \[bu] +\fBname\fR (string): short name of the plugin +.IP \[bu] +\fBoptions\fR (object, optional): Specific options set for this plugin: + +.RE + +.IP \[bu] +\fBconf\fR (string, optional): \fBconf\fR field from cmdline, or default +.IP \[bu] +\fBlightning-dir\fR (string, optional): \fBlightning-dir\fR field from config or cmdline, or default +.IP \[bu] +\fBnetwork\fR (string, optional): \fBnetwork\fR field from config or cmdline, or default +.IP \[bu] +\fBallow-deprecated-apis\fR (boolean, optional): \fBallow-deprecated-apis\fR field from config or cmdline, or default +.IP \[bu] +\fBrpc-file\fR (string, optional): \fBrpc-file\fR field from config or cmdline, or default +.IP \[bu] +\fBdisable-plugin\fR (array of strings, optional): +.RS +.IP \[bu] +\fBdisable-plugin\fR field from config or cmdline + +.RE + +.IP \[bu] +\fBalways-use-proxy\fR (boolean, optional): \fBalways-use-proxy\fR field from config or cmdline, or default +.IP \[bu] +\fBdaemon\fR (boolean, optional): \fBdaemon\fR field from config or cmdline, or default +.IP \[bu] +\fBwallet\fR (string, optional): \fBwallet\fR field from config or cmdline, or default +.IP \[bu] +\fBlarge-channels\fR (boolean, optional): \fBlarge-channels\fR field from config or cmdline, or default +.IP \[bu] +\fBexperimental-dual-fund\fR (boolean, optional): \fBexperimental-dual-fund\fR field from config or cmdline, or default +.IP \[bu] +\fBexperimental-onion-messages\fR (boolean, optional): \fBexperimental-onion-messages\fR field from config or cmdline, or default +.IP \[bu] +\fBexperimental-offers\fR (boolean, optional): \fBexperimental-offers\fR field from config or cmdline, or default +.IP \[bu] +\fBexperimental-shutdown-wrong-funding\fR (boolean, optional): \fBexperimental-shutdown-wrong-funding\fR field from config or cmdline, or default +.IP \[bu] +\fBrgb\fR (hex, optional): \fBrgb\fR field from config or cmdline, or default (always 6 characters) +.IP \[bu] +\fBalias\fR (string, optional): \fBalias\fR field from config or cmdline, or default +.IP \[bu] +\fBpid-file\fR (string, optional): \fBpid-file\fR field from config or cmdline, or default +.IP \[bu] +\fBignore-fee-limits\fR (boolean, optional): \fBignore-fee-limits\fR field from config or cmdline, or default +.IP \[bu] +\fBwatchtime-blocks\fR (u32, optional): \fBwatchtime-blocks\fR field from config or cmdline, or default +.IP \[bu] +\fBmax-locktime-blocks\fR (u32, optional): \fBmax-locktime-blocks\fR field from config or cmdline, or default +.IP \[bu] +\fBfunding-confirms\fR (u32, optional): \fBfunding-confirms\fR field from config or cmdline, or default +.IP \[bu] +\fBcltv-delta\fR (u32, optional): \fBcltv-delta\fR field from config or cmdline, or default +.IP \[bu] +\fBcltv-final\fR (u32, optional): \fBcltv-final\fR field from config or cmdline, or default +.IP \[bu] +\fBcommit-time\fR (u32, optional): \fBcommit-time\fR field from config or cmdline, or default +.IP \[bu] +\fBfee-base\fR (u32, optional): \fBfee-base\fR field from config or cmdline, or default +.IP \[bu] +\fBrescan\fR (integer, optional): \fBrescan\fR field from config or cmdline, or default +.IP \[bu] +\fBfee-per-satoshi\fR (u32, optional): \fBfee-per-satoshi\fR field from config or cmdline, or default +.IP \[bu] +\fBmax-concurrent-htlcs\fR (u32, optional): \fBmax-concurrent-htlcs\fR field from config or cmdline, or default +.IP \[bu] +\fBmin-capacity-sat\fR (u64, optional): \fBmin-capacity-sat\fR field from config or cmdline, or default +.IP \[bu] +\fBaddr\fR (string, optional): \fBaddr\fR field from config or cmdline (can be more than one) +.IP \[bu] +\fBannounce-addr\fR (string, optional): \fBannounce-addr\fR field from config or cmdline (can be more than one) +.IP \[bu] +\fBbind-addr\fR (string, optional): \fBbind-addr\fR field from config or cmdline (can be more than one) +.IP \[bu] +\fBoffline\fR (boolean, optional): \fBtrue\fR if \fBoffline\fR was set in config or cmdline +.IP \[bu] +\fBautolisten\fR (boolean, optional): \fBautolisten\fR field from config or cmdline, or default +.IP \[bu] +\fBproxy\fR (string, optional): \fBproxy\fR field from config or cmdline, or default +.IP \[bu] +\fBdisable-dns\fR (boolean, optional): \fBtrue\fR if \fBdisable-dns\fR was set in config or cmdline +.IP \[bu] +\fBencrypted-hsm\fR (boolean, optional): \fBtrue\fR if \fBencrypted-hsm\fR was set in config or cmdline +.IP \[bu] +\fBrpc-file-mode\fR (string, optional): \fBrpc-file-mode\fR field from config or cmdline, or default +.IP \[bu] +\fBlog-level\fR (string, optional): \fBlog-level\fR field from config or cmdline, or default +.IP \[bu] +\fBlog-prefix\fR (string, optional): \fBlog-prefix\fR field from config or cmdline, or default +.IP \[bu] +\fBlog-file\fR (string, optional): \fBlog-file\fR field from config or cmdline, or default +.IP \[bu] +\fBlog-timestamps\fR (boolean, optional): \fBlog-timestamps\fR field from config or cmdline, or default +.IP \[bu] +\fBforce-feerates\fR (string, optional): force-feerate configuration setting, if any +.IP \[bu] +\fBsubdaemon\fR (string, optional): \fBsubdaemon\fR fields from config or cmdline if any (can be more than one) +.IP \[bu] +\fBtor-service-password\fR (string, optional): \fBtor-service-password\fR field from config or cmdline, if any + +.RE + +On failure, one of the following error codes may be returned: + +.RS +.IP \[bu] +-32602: Error in given parameters or field with \fIconfig\fR name doesn't exist\. + +.RE +.SH EXAMPLE JSON RESPONSE +.nf +.RS +{ + "# version": "v0.9.0-1", + "lightning-dir": "/media/vincent/Maxtor/sanboxTestWrapperRPC/lightning_dir_dev", + "network": "testnet", + "allow-deprecated-apis": true, + "rpc-file": "lightning-rpc", + "plugins": [ + { + "path": "/home/vincent/Github/plugins/sauron/sauron.py", + "name": "sauron.py", + "options": { + "sauron-api-endpoint": "http://blockstream.info/testnet/api/", + "sauron-tor-proxy": "" + } + }, + { + "path": "/home/vincent/Github/reckless/reckless.py", + "name": "reckless.py" + } + ], + "important-plugins": [ + { + "path": "/home/vincent/Github/lightning/lightningd/../plugins/autoclean", + "name": "autoclean", + "options": { + "autocleaninvoice-cycle": null, + "autocleaninvoice-expired-by": null + } + }, + { + "path": "/home/vincent/Github/lightning/lightningd/../plugins/fundchannel", + "name": "fundchannel" + }, + { + "path": "/home/vincent/Github/lightning/lightningd/../plugins/keysend", + "name": "keysend" + }, + { + "path": "/home/vincent/Github/lightning/lightningd/../plugins/pay", + "name": "pay", + "options": { + "disable-mpp": false + } + } + ], + "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/autoclean", + "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/fundchannel", + "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/keysend", + "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/pay", + "plugin": "/home/vincent/Github/plugins/sauron/sauron.py", + "plugin": "/home/vincent/Github/reckless/reckless.py", + "disable-plugin": [ + "bcli" + ], + "always-use-proxy": false, + "daemon": "false", + "wallet": "sqlite3:///media/vincent/Maxtor/sanboxTestWrapperRPC/lightning_dir_dev/testnet/lightningd.sqlite3", + "wumbo": false, + "wumbo": false, + "rgb": "03ad98", + "alias": "BRUCEWAYN-TES-DEV", + "pid-file": "/media/vincent/Maxtor/sanboxTestWrapperRPC/lightning_dir_dev/lightningd-testne...", + "ignore-fee-limits": true, + "watchtime-blocks": 6, + "max-locktime-blocks": 2016, + "funding-confirms": 1, + "commit-fee-min": 0, + "commit-fee-max": 0, + "cltv-delta": 6, + "cltv-final": 10, + "commit-time": 10, + "fee-base": 1, + "rescan": 30, + "fee-per-satoshi": 10, + "max-concurrent-htlcs": 483, + "min-capacity-sat": 10000, + "addr": "autotor:127.0.0.1:9051", + "bind-addr": "127.0.0.1:9735", + "announce-addr": "fp463inc4w3lamhhduytrwdwq6q6uzugtaeapylqfc43agrdnnqsheyd.onion:9735", + "offline": "false", + "autolisten": true, + "proxy": "127.0.0.1:9050", + "disable-dns": "false", + "enable-autotor-v2-mode": "false", + "encrypted-hsm": false, + "rpc-file-mode": "0600", + "log-level": "DEBUG", + "log-prefix": "lightningd", +} +.RE + +.fi +.SH AUTHOR + +Vincenzo Palazzo \fI wrote the initial version of this man page, but many others did the hard work of actually implementing this rpc command\. + +.SH SEE ALSO + +\fBlightning-getinfo\fR(7), \fBlightningd-config\fR(5) + +.SH RESOURCES + +Main web site: \fIhttps://github.com/ElementsProject/lightning\fR + +\" SHA256STAMP:b6e48314de5642ec61a3e2c989ac0197630c2a3c0e8e6d86020b597679049400 diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 52ee273eb5bf..bddae8497d71 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -88,6 +88,7 @@ On success, an object is returned, containing: - **log-timestamps** (boolean, optional): `log-timestamps` field from config or cmdline, or default - **force-feerates** (string, optional): force-feerate configuration setting, if any - **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one) +- **fetchinvoice-noconnect** (boolean, optional): `featchinvoice-noconnect` fileds from config or cmdline, or default - **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index ca24ba76983c..1ecdb0c3585e 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -251,6 +251,10 @@ "type": "string", "description": "`subdaemon` fields from config or cmdline if any (can be more than one)" }, + "fetchinvoice-noconnect": { + "type": "boolean", + "description": "`featchinvoice-noconnect` fileds from config or cmdline, or default" + }, "tor-service-password": { "type": "string", "description": "`tor-service-password` field from config or cmdline, if any" From b933b2fa4de2a058c5976edccc512756fa6225e2 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 4 Nov 2021 15:13:43 +0100 Subject: [PATCH 0051/1530] tools: add a tool to formatting schema, and fixed (hack) the Makefile code checking. This include the following commits: - review 1/2: move from tab to space, and remove the exp. prop from doc; - review 2/2: remove experimental features; Signed-off-by: Vincenzo Palazzo --- doc/Makefile | 12 ++++++++++-- doc/schemas/WRITING_SCHEMAS.md | 3 +++ doc/undoc-flags.json | 21 +++++++++++++++++++++ tools/check-manpage.sh | 10 ++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 doc/undoc-flags.json diff --git a/doc/Makefile b/doc/Makefile index 783a528d88ed..d193d8aa4bfb 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -89,7 +89,15 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-help.7 \ doc/lightning-getlog.7 -doc-all: $(MANPAGES) doc/index.rst +doc-all: fmt-schema $(MANPAGES) doc/index.rst + +#FIXME(vincenzopalazzo) we don't need to change a file if it is well format, +#so we can use diff to check difference in the real json and in the .fmt +fmt-schema: + @echo "Checking schema fmt!" + @for f in $$(find doc/schemas -type f -name '*.json'); do cat "$$f" | jq . > "$$f.fmt" ; mv "$$f.fmt" "$$f" ; done + +check-doc: check-config-docs check-manpages # Some manpages use a schema, so need that added. MARKDOWN_WITH_SCHEMA := $(shell grep -l GENERATE-FROM-SCHEMA $(MANPAGES:=.md)) @@ -149,7 +157,7 @@ check-manpages: all-programs check-config-docs # Makes sure that fields mentioned in schema are in man page, and vice versa. check-config-docs: @for c in `sed -n 's/^ "\(.*\)": {/\1/p' doc/schemas/listconfigs.schema.json | grep -v '^# version$$' | grep -v '^plugins$$' | grep -v '^important-plugins$$'`; do if ! grep -q "^ \*\*$$c\*\*" doc/lightningd-config.5.md; then echo "$$c undocumented!"; exit 1; fi; done - @for c in `grep -v '\[plugin ' doc/lightningd-config.5.md | sed -n 's/^ \*\*\([^*]*\)\*\*.*/\1/p' | grep -v '^\(help\|version\|mainnet\|testnet\|signet\|plugin\|important-plugin\|plugin-dir\|clear-plugins\)$$'`; do if ! grep -q '^ "'"$$c"'"' doc/schemas/listconfigs.schema.json; then echo "$$c documented but not in schema!"; exit 1; fi; done + @for c in `grep -v '\[plugin ' doc/lightningd-config.5.md | sed -n 's/^ \*\*\([^*]*\)\*\*.*/\1/p' | grep -v '^\(help\|version\|mainnet\|testnet\|signet\|plugin\|important-plugin\|plugin-dir\|clear-plugins\)$$'`; do if ! grep -q '"'"$$c"'"' doc/schemas/listconfigs.schema.json; then echo "$$c documented but not in schema!"; exit 1; fi; done doc-maintainer-clean: $(RM) $(MANPAGES) diff --git a/doc/schemas/WRITING_SCHEMAS.md b/doc/schemas/WRITING_SCHEMAS.md index eaf45fb55c5a..b40ede11b70b 100644 --- a/doc/schemas/WRITING_SCHEMAS.md +++ b/doc/schemas/WRITING_SCHEMAS.md @@ -39,6 +39,9 @@ You should always list all fields which are *always* present in We extend the basic types; see [contrib/pyln-testing/pyln/testing/fixtures.py](fixtures.py). +In addition, before to commit a new schema or a new version of it, make sure that it +is well formatted. If you don't want do it by hand, use `make fmt-schema` that uses +jq under the hood. ### Using Conditional Fields diff --git a/doc/undoc-flags.json b/doc/undoc-flags.json new file mode 100644 index 000000000000..3665fce5e852 --- /dev/null +++ b/doc/undoc-flags.json @@ -0,0 +1,21 @@ +{ + "flags": [ + "experimental-accept-extra-tlv-types", + "channel-fee-max-base-msat", + "channel-fee-max-proportional-thousandths", + "funder-fund-probability", + "funder-fuzz-percent", + "funder-lease-requests-only", + "funder-max-their-funding", + "funder-min-their-funding", + "funder-per-channel-max", + "funder-per-channel-min", + "funder-policy", + "funder-policy-mod", + "funder-reserve-tank", + "lease-fee-base-msat", + "lease-fee-basis", + "lease-funding-weight", + "fetchinvoice-noconnect" + ] +} \ No newline at end of file diff --git a/tools/check-manpage.sh b/tools/check-manpage.sh index 09daf3e044dc..09ad861af95d 100755 --- a/tools/check-manpage.sh +++ b/tools/check-manpage.sh @@ -32,6 +32,16 @@ CMD_OPTNAMES=$(get_cmd_opts "$1" | sort) # Now, gather (long) opt names from man page, make sure they match. MAN_OPTNAMES=$(sed -E -n 's/^ \*\*(--)?([^*/]*)\*\*(=?).*/\2\3/p' < "$2" | sort) +# Remove undocumented proprieties, usually these proprieties are +# under experimental phases. +for flag in $(jq '.flags[]' Date: Thu, 4 Nov 2021 15:15:51 +0100 Subject: [PATCH 0052/1530] jsonschema: Formatting json schema and regenerate docs Signed-off-by: Vincenzo Palazzo --- doc/lightning-addgossip.7.md | 2 +- doc/lightning-autocleaninvoice.7.md | 2 +- doc/lightning-check.7.md | 2 +- doc/lightning-checkmessage.7.md | 2 +- doc/lightning-close.7.md | 2 +- doc/lightning-connect.7.md | 2 +- doc/lightning-createinvoice.7.md | 2 +- doc/lightning-createonion.7.md | 2 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-decode.7.md | 2 +- doc/lightning-decodepay.7.md | 2 +- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-delexpiredinvoice.7.md | 2 +- doc/lightning-delinvoice.7.md | 2 +- doc/lightning-delpay.7.md | 2 +- doc/lightning-disableoffer.7.md | 2 +- doc/lightning-disconnect.7.md | 2 +- doc/lightning-feerates.7.md | 2 +- doc/lightning-fetchinvoice.7.md | 2 +- doc/lightning-fundchannel.7.md | 2 +- doc/lightning-fundchannel_cancel.7.md | 2 +- doc/lightning-fundchannel_complete.7.md | 2 +- doc/lightning-fundchannel_start.7.md | 2 +- doc/lightning-funderupdate.7.md | 2 +- doc/lightning-fundpsbt.7.md | 2 +- doc/lightning-getinfo.7.md | 2 +- doc/lightning-getlog.7.md | 2 +- doc/lightning-getroute.7.md | 2 +- doc/lightning-getsharedsecret.7.md | 2 +- doc/lightning-help.7.md | 2 +- doc/lightning-invoice.7.md | 2 +- doc/lightning-keysend.7.md | 2 +- doc/lightning-listchannels.7.md | 2 +- doc/lightning-listconfigs.7 | 8 +- doc/lightning-listconfigs.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listforwards.7.md | 2 +- doc/lightning-listfunds.7.md | 4 +- doc/lightning-listinvoices.7.md | 2 +- doc/lightning-listnodes.7.md | 2 +- doc/lightning-listoffers.7.md | 2 +- doc/lightning-listpays.7.md | 2 +- doc/lightning-listpeers.7.md | 2 +- doc/lightning-listsendpays.7.md | 2 +- doc/lightning-listtransactions.7.md | 2 +- doc/lightning-multifundchannel.7.md | 2 +- doc/lightning-multiwithdraw.7.md | 2 +- doc/lightning-newaddr.7.md | 2 +- doc/lightning-notifications.7.md | 2 +- doc/lightning-offer.7.md | 2 +- doc/lightning-offerout.7.md | 2 +- doc/lightning-openchannel_abort.7.md | 2 +- doc/lightning-openchannel_bump.7.md | 2 +- doc/lightning-openchannel_init.7.md | 2 +- doc/lightning-openchannel_signed.7.md | 2 +- doc/lightning-openchannel_update.7.md | 2 +- doc/lightning-parsefeerate.7.md | 2 +- doc/lightning-pay.7.md | 2 +- doc/lightning-ping.7.md | 2 +- doc/lightning-plugin.7.md | 2 +- doc/lightning-reserveinputs.7.md | 2 +- doc/lightning-sendcustommsg.7.md | 2 +- doc/lightning-sendinvoice.7.md | 2 +- doc/lightning-sendonion.7.md | 2 +- doc/lightning-sendonionmessage.7.md | 2 +- doc/lightning-sendpay.7.md | 2 +- doc/lightning-sendpsbt.7.md | 2 +- doc/lightning-setchannelfee.7.md | 2 +- doc/lightning-signmessage.7.md | 2 +- doc/lightning-signpsbt.7.md | 2 +- doc/lightning-stop.7.md | 2 +- doc/lightning-txdiscard.7.md | 2 +- doc/lightning-txprepare.7.md | 2 +- doc/lightning-txsend.7.md | 2 +- doc/lightning-unreserveinputs.7.md | 2 +- doc/lightning-utxopsbt.7.md | 2 +- doc/lightning-waitanyinvoice.7.md | 2 +- doc/lightning-waitblockheight.7.md | 2 +- doc/lightning-waitinvoice.7.md | 2 +- doc/lightning-waitsendpay.7.md | 2 +- doc/lightning-withdraw.7.md | 2 +- doc/schemas/addgossip.schema.json | 9 +- doc/schemas/autocleaninvoice.schema.json | 81 +- doc/schemas/check.schema.json | 22 +- doc/schemas/checkmessage.schema.json | 112 +- doc/schemas/close.schema.json | 86 +- doc/schemas/connect.schema.json | 186 +- doc/schemas/createinvoice.schema.json | 146 +- doc/schemas/createonion.schema.json | 41 +- doc/schemas/datastore.schema.json | 50 +- doc/schemas/decode.schema.json | 1704 +++++++------- doc/schemas/decodepay.schema.json | 311 +-- doc/schemas/deldatastore.schema.json | 50 +- doc/schemas/delexpiredinvoice.schema.json | 11 +- doc/schemas/delinvoice.schema.json | 332 +-- doc/schemas/delpay.schema.json | 176 +- doc/schemas/disableoffer.schema.json | 81 +- doc/schemas/disconnect.schema.json | 9 +- doc/schemas/feerates.schema.json | 244 ++- doc/schemas/fetchinvoice.schema.json | 139 +- doc/schemas/fundchannel.schema.json | 59 +- doc/schemas/fundchannel_cancel.schema.json | 20 +- doc/schemas/fundchannel_complete.schema.json | 37 +- doc/schemas/fundchannel_start.schema.json | 37 +- doc/schemas/funderupdate.schema.json | 162 +- doc/schemas/fundpsbt.schema.json | 131 +- doc/schemas/getinfo.schema.json | 332 +-- doc/schemas/getlog.schema.json | 284 +-- doc/schemas/getroute.schema.json | 100 +- doc/schemas/getsharedsecret.schema.json | 24 +- doc/schemas/help.schema.json | 73 +- doc/schemas/invoice.schema.json | 95 +- doc/schemas/keysend.schema.json | 114 +- doc/schemas/listchannels.schema.json | 165 +- doc/schemas/listconfigs.schema.json | 524 ++--- doc/schemas/listdatastore.schema.json | 72 +- doc/schemas/listforwards.schema.json | 429 ++-- doc/schemas/listfunds.schema.json | 481 ++-- doc/schemas/listinvoices.schema.json | 277 +-- doc/schemas/listnodes.schema.json | 350 +-- doc/schemas/listoffers.schema.json | 99 +- doc/schemas/listpays.schema.json | 309 +-- doc/schemas/listpeers.schema.json | 2070 ++++++++++-------- doc/schemas/listsendpays.schema.json | 360 +-- doc/schemas/listtransactions.schema.json | 291 ++- doc/schemas/multifundchannel.schema.json | 180 +- doc/schemas/multiwithdraw.schema.json | 29 +- doc/schemas/newaddr.schema.json | 26 +- doc/schemas/notifications.schema.json | 9 +- doc/schemas/offer.schema.json | 90 +- doc/schemas/offerout.schema.json | 98 +- doc/schemas/openchannel_abort.schema.json | 42 +- doc/schemas/openchannel_bump.schema.json | 55 +- doc/schemas/openchannel_init.schema.json | 55 +- doc/schemas/openchannel_signed.schema.json | 42 +- doc/schemas/openchannel_update.schema.json | 59 +- doc/schemas/parsefeerate.schema.json | 20 +- doc/schemas/pay.schema.json | 114 +- doc/schemas/ping.schema.json | 20 +- doc/schemas/plugin.schema.json | 160 +- doc/schemas/reserveinputs.schema.json | 82 +- doc/schemas/sendcustommsg.schema.json | 18 +- doc/schemas/sendinvoice.schema.json | 189 +- doc/schemas/sendonion.schema.json | 263 +-- doc/schemas/sendonionmessage.schema.json | 11 +- doc/schemas/sendpay.schema.json | 285 +-- doc/schemas/sendpsbt.schema.json | 29 +- doc/schemas/setchannelfee.schema.json | 83 +- doc/schemas/signmessage.schema.json | 46 +- doc/schemas/signpsbt.schema.json | 20 +- doc/schemas/stop.schema.json | 8 +- doc/schemas/txdiscard.schema.json | 27 +- doc/schemas/txprepare.schema.json | 36 +- doc/schemas/txsend.schema.json | 36 +- doc/schemas/unreserveinputs.schema.json | 123 +- doc/schemas/utxopsbt.schema.json | 127 +- doc/schemas/waitanyinvoice.schema.json | 228 +- doc/schemas/waitblockheight.schema.json | 18 +- doc/schemas/waitinvoice.schema.json | 228 +- doc/schemas/waitsendpay.schema.json | 212 +- doc/schemas/withdraw.schema.json | 36 +- 161 files changed, 7552 insertions(+), 6407 deletions(-) diff --git a/doc/lightning-addgossip.7.md b/doc/lightning-addgossip.7.md index 5d009fba13dc..fba520c5a5f9 100644 --- a/doc/lightning-addgossip.7.md +++ b/doc/lightning-addgossip.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:86b47e27413141ffd6e8b79d83ac4a5cc80e5a7e6f5ba4b0df6825e744f9eea7) +[comment]: # ( SHA256STAMP:1a64fbaed63ffee21df3d46956a6dca193982b1b135a9b095e68652a720c77ac) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index cfcc8c6dc7dc..270f63b876b8 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -50,4 +50,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:dc9caae8f7ca886630f2685ea972fb1113ffcfd5a5e46c2d212c3c4bbc4e5f44) +[comment]: # ( SHA256STAMP:4506a00326dbfa7d44cbf891ad31cbfa66351d852aa0c58735bae03d32938edb) diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index 2747771e1602..a15bba93e88a 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -40,4 +40,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:47419eb2a6d8d83866773a49170f6fa6047de01435d602fa08487c59d87fb93c) +[comment]: # ( SHA256STAMP:10d986d91af6315ee755d119cb1b77f306e2360105191116282f3faead350ce8) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index b12f988e0cb2..da056b3fc89a 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -50,4 +50,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:eb1ea64cb5e8e16e8e7214f38184b60f2c30d8e7f68db1623fabda665bf1fdec) +[comment]: # ( SHA256STAMP:6df0e61e28118786861aacc073e3289268fe1b00837c3fd02a537aa13e5acae5) diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index b4bb0e21bdb4..c88b6b87f794 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -133,4 +133,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:db5ba99eb3393f6c55833f0bbace34b3ca504d490a25cb26c53b8790ae325981) +[comment]: # ( SHA256STAMP:3540adff8d75123f90598a2c0657924c0d5a53aa26716980f9a59879fcfb1f6b) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index 8801b1599568..2209f67ec8fa 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -89,4 +89,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:306d0966721f20c9a8688f33a030114a5ac095c6218eeb7fafd88fca97b9ee52) +[comment]: # ( SHA256STAMP:540ce22f5d912b59732b8b2659e4a950d1344eb926901e26476a246d9eb473b8) diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index 9c6610690c0c..459c206c7b8f 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -74,4 +74,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4582bb142f91497a1bc09a10c745d5c03b5540c6c53675426a6c5c5d079420a9) +[comment]: # ( SHA256STAMP:ea89ed849c8ad6cac8e1e136999046d1f7589bf176be0e65438174357f87ed11) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 402a0d485029..b35d33836aa6 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -136,4 +136,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:a00e746c41b59c6f34fb4a875cf3bf1cded7101f3e4d6d5b35f577d6aca6387f) +[comment]: # ( SHA256STAMP:23e999fedfde74a200c0e6626fa828548b3b60952de30c5885cd63b1922f4508) diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index 3be24c720572..cc81552bfbcf 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -65,4 +65,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:fd53ebaf7190249460f62aa509010207dbdbc3ae3c90279f49d3c61d4a621c7d) +[comment]: # ( SHA256STAMP:89e1f4926dd83df233b92aae626de776ec3bb2d29887ec29e8cf479ee2a16b85) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 07eceb288277..6708071ed4b8 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -180,4 +180,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7ddb369a73cb0bf8782e2176d0726e13015d9099f71a5ceda39d3e1cb7cd2241) +[comment]: # ( SHA256STAMP:cd4a18d07c23b6a995f8db7cac25344e340e512c786b8cf743f56ecceeb84960) diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index e7b72ffaff2a..e74da14f6858 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -69,4 +69,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b6cf489b74ca9719c8adc7d17115f8109d61e288e77f8b3bed02ac64d7ede08f) +[comment]: # ( SHA256STAMP:d92e1197708fff40f8ad71ccec3c0d8122d8088da1803c02bb042b09dbf2ee33) diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index fa8855b03d28..477887641b1c 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -48,4 +48,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b9d59e2d14e91b9440f75fc1577e9f26848821d87d8281e5ca2393adf412fa0d) +[comment]: # ( SHA256STAMP:ac7468cf6eadc8ab85216b4d5ecb55a32f3d0bc84180f477151c3748901824de) diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index 447a1d346357..664661ac603c 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -38,4 +38,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cfc042477a22946d8f3a07fbecf543a0063a73b54f955b1fc6adc4e8a294ec32) +[comment]: # ( SHA256STAMP:20cca78dbc3681427e1d536ba2f81e0bc05e2b5209edf884137f2ad25e642e84) diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 714cd4988f42..935c3e80d2cb 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:63d72da38a7b758ef7fb7f3a35fbb1c48fd3f3c2a5bffef6559fc98dccd77cf6) +[comment]: # ( SHA256STAMP:cd3b009a6ef0c220ca21c6d8e3a5716ca2080997016cf00a2e26defc03cfac73) diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index 9d532363c9c0..a5c32f37f68a 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -101,4 +101,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:2fe7a92ce837282fc473f8f56b2040910fb1dcf8d0d7768fda5695dd5a5b4f01) +[comment]: # ( SHA256STAMP:af8299cd87efe8254969069851d99bffffa033013f4a8b9fc94cdab6cfa0ff78) diff --git a/doc/lightning-disableoffer.7.md b/doc/lightning-disableoffer.7.md index d70c0ff466e8..652d4fdd2694 100644 --- a/doc/lightning-disableoffer.7.md +++ b/doc/lightning-disableoffer.7.md @@ -74,4 +74,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:75f31b4614cfb2083d7f5c5de17d0892aae8504434da600132668b326fa4e0a4) +[comment]: # ( SHA256STAMP:a7dbc87d991d1040283b5fbfe732fb9bc7c81efad3aa8b5bfb11ffe59ed3f069) diff --git a/doc/lightning-disconnect.7.md b/doc/lightning-disconnect.7.md index 8d5b60bb5253..19424cdef826 100644 --- a/doc/lightning-disconnect.7.md +++ b/doc/lightning-disconnect.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:86b47e27413141ffd6e8b79d83ac4a5cc80e5a7e6f5ba4b0df6825e744f9eea7) +[comment]: # ( SHA256STAMP:1a64fbaed63ffee21df3d46956a6dca193982b1b135a9b095e68652a720c77ac) diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index 1770699d5b0a..d1beb1eabf9b 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -119,4 +119,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:aa3360c535d2f7e42adead8e78a290a21c5bd88d412f9afa53dfe7388c7edeab) +[comment]: # ( SHA256STAMP:8fe321fcba7b3a471f4f83f98638dbc820fc0abe91f3d53ca55fdb0222e17a8d) diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index 39253cdfbb0a..bb7c3f34c4c6 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -88,4 +88,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5820acc18bb293035f040e2079363cd2ae03fd31be0c4de78b957502fbd31b4e) +[comment]: # ( SHA256STAMP:e0033e40d86355e51abb48472f802a9a713ed5b2725828467515f9541207dac5) diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index b1b1205093b3..1861d3b571d3 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -113,4 +113,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:43dfc7b25dad697f3589492a5bdcd895b8edbc2d39e9edb6a9c18313fac9ca50) +[comment]: # ( SHA256STAMP:4a8d7c524cfe257f961531929d14d3589efb6ecd182a33e92aade30af90406f8) diff --git a/doc/lightning-fundchannel_cancel.7.md b/doc/lightning-fundchannel_cancel.7.md index 9815ff5d0929..55133d9109b8 100644 --- a/doc/lightning-fundchannel_cancel.7.md +++ b/doc/lightning-fundchannel_cancel.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9c612f1a2569f40743dff555768e2138ce31f7794eeec06d5ff834461be2e45a) +[comment]: # ( SHA256STAMP:d433fc29ad064a09c92160038973c7161bb56947166a2701c0d5d278e276917c) diff --git a/doc/lightning-fundchannel_complete.7.md b/doc/lightning-fundchannel_complete.7.md index 0b6cbdc54323..e1690fe2074e 100644 --- a/doc/lightning-fundchannel_complete.7.md +++ b/doc/lightning-fundchannel_complete.7.md @@ -61,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:29c1e2bbc878ffb6b7e41f552afc608af58e335a329c79d09680e8e2995ff0d5) +[comment]: # ( SHA256STAMP:6fad848d20b3e0a6085790085b0aa91d24cc33e4b0127fc40521ce9c102182d1) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index 4e501d14a573..af9694d4b925 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -79,4 +79,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8d7a9f3cf343f7c72148a5d0b1aa0217cd9ef91e8c618dc2ff1077b15b9f25d4) +[comment]: # ( SHA256STAMP:eab533b02f2bffecef27724078461ed25f3a9b729c2432b80bbdc35aea670ca2) diff --git a/doc/lightning-funderupdate.7.md b/doc/lightning-funderupdate.7.md index 1be23d842b7a..cec36163b816 100644 --- a/doc/lightning-funderupdate.7.md +++ b/doc/lightning-funderupdate.7.md @@ -146,4 +146,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ed47e802b2e61f46461f5b65312c8c4de40e64a0970d1d745e59d63201dbfae8) +[comment]: # ( SHA256STAMP:01be8ecebe9025991de323bde9bc41591a9cde1b106fa01fc328451d31eb9a70) diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index d9b61d63712d..4dfca65eb19c 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -115,4 +115,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:fd84205c50f43a2e55b0adb1c4d0709cd5d909c211148b005674e8e1afffba53) +[comment]: # ( SHA256STAMP:635cc321d158bcdb3ca24f13c9955cc9b867e34247c1cf3d91edbbe21eb06a48) diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 1d6bd5187b65..40e1f98d0174 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -117,4 +117,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:50c41a77a5f440cc22e5df9e3748e4280cd4508469887382690c580f10bc5af4) +[comment]: # ( SHA256STAMP:8374064ca0f95ab0c20d3edaf7f3742316af98f4d1e0e8de88922524f1ea3ce5) diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index d7b18f92bc15..3637fe010d94 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -90,4 +90,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:155507c9105521bb4c113232d9bc6fd585cce269303ee8e7e27ea803ca8405cf) +[comment]: # ( SHA256STAMP:963fc75819ffb63271f25e31f30a6fe54457803d8bf296556589f76874fb39c0) diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index c004a2f75542..f597678cd3dd 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -309,4 +309,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a297c66baaf5dd18528e4e8bc1bac3348536fed7be474f1c26475b88198a2c1e) +[comment]: # ( SHA256STAMP:0c1f92ff24ae0277fed3cf3fd41f2f45e4a57558a4b61fc51a1a698b4f3d8f01) diff --git a/doc/lightning-getsharedsecret.7.md b/doc/lightning-getsharedsecret.7.md index 7b337c5008b3..72b24eedc6db 100644 --- a/doc/lightning-getsharedsecret.7.md +++ b/doc/lightning-getsharedsecret.7.md @@ -91,4 +91,4 @@ RESOURCES * Main web site: -[comment]: # ( SHA256STAMP:41cce7390624f69aeac9118887ac751825087e0ef792cff8d5197500af9539ba) +[comment]: # ( SHA256STAMP:e54898c6b950be6242a641212b71b6ce33ea31068f3572cd42be5d2b87365eb7) diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index 6b5739320f4d..2bccd6cc7ca7 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -67,4 +67,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:2e41227e559aac229adc2cab0f0a2e591ed0ad33218bfe8647ce4022f25332fd) +[comment]: # ( SHA256STAMP:262fdbfc6ea8142c57637e5c48a9536b0e8577ef823ebfc830cc0c14d56fb08d) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index ada739a9b519..692eea43170b 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -113,4 +113,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a65026d45e3a70b98c97d838c08748f873aeab20321d930de17fb45cf5022848) +[comment]: # ( SHA256STAMP:f7d82473482e5454fc03641fddcfa97984e6a597e7ad377ced2cbed1512e91ed) diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index 7759d70cca40..559d9813f82e 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -114,4 +114,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1c931f2ff49a169011ca6c2abde58281570a1db0dfbdca829105999723fe8bb8) +[comment]: # ( SHA256STAMP:bf507985544575c4ef2fe194fda6a693378cb8ab3bfb30ca7a7c066be271be29) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index a1876b1684bd..1cc762ec100c 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -77,4 +77,4 @@ Lightning RFC site - BOLT \#7: -[comment]: # ( SHA256STAMP:ab173bcd769b6abd351a670e63be6adfbdfb0c24abcef157c0912b28f86d2250) +[comment]: # ( SHA256STAMP:c2ebd6407a66ad5f67b5fd933552a468e306b1fed7868f92985c24e321861fae) diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index 1ef66bf32009..5818f32f41d7 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -92,6 +92,8 @@ On success, an object is returned, containing: .IP \[bu] \fBexperimental-shutdown-wrong-funding\fR (boolean, optional): \fBexperimental-shutdown-wrong-funding\fR field from config or cmdline, or default .IP \[bu] +\fBexperimental-websocket-port\fR (u16, optional): \fBexperimental-websocket-port\fR field from config or cmdline, or default +.IP \[bu] \fBrgb\fR (hex, optional): \fBrgb\fR field from config or cmdline, or default (always 6 characters) .IP \[bu] \fBalias\fR (string, optional): \fBalias\fR field from config or cmdline, or default @@ -120,6 +122,8 @@ On success, an object is returned, containing: .IP \[bu] \fBmax-concurrent-htlcs\fR (u32, optional): \fBmax-concurrent-htlcs\fR field from config or cmdline, or default .IP \[bu] +\fBmax-dust-htlc-exposure-msat\fR (msat, optional): \fBmax-dust-htlc-exposure-mast\fR field from config or cmdline, or default +.IP \[bu] \fBmin-capacity-sat\fR (u64, optional): \fBmin-capacity-sat\fR field from config or cmdline, or default .IP \[bu] \fBaddr\fR (string, optional): \fBaddr\fR field from config or cmdline (can be more than one) @@ -152,6 +156,8 @@ On success, an object is returned, containing: .IP \[bu] \fBsubdaemon\fR (string, optional): \fBsubdaemon\fR fields from config or cmdline if any (can be more than one) .IP \[bu] +\fBfetchinvoice-noconnect\fR (boolean, optional): \fBfeatchinvoice-noconnect\fR fileds from config or cmdline, or default +.IP \[bu] \fBtor-service-password\fR (string, optional): \fBtor-service-password\fR field from config or cmdline, if any .RE @@ -270,4 +276,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:b6e48314de5642ec61a3e2c989ac0197630c2a3c0e8e6d86020b597679049400 +\" SHA256STAMP:37b3fd1b2c5b2c903c25579f96d0bb4116955f3aebbf6bbb97f8a62e352cf440 diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index bddae8497d71..b61f7d01fc1d 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -208,4 +208,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:71a911b67203f75e7c1f717be611f505713fce4e8113fc4a84c89bc50730d2bf) +[comment]: # ( SHA256STAMP:59b197ad256bd701744ed5aa9f663166e48ef6320cf3a1538af0bd855daa3186) diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index e88a9330748b..316e719e90ad 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -46,4 +46,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:052a5b86380b78fe292fae2f0378b4bd3d200f838a6ad36e0ee8bed619cb9d1c) +[comment]: # ( SHA256STAMP:1e5d31c36f5aa2d2cb6bedb07a94b18880ba95529885c104b177d91bf251d420) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 9d2ad9d77ed7..1ff8b2b70829 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8d5189bf6f515520f1acd225f372e1123378eab01b032313b5956089a635c2f7) +[comment]: # ( SHA256STAMP:abfaaa00817734d8acb77d02d7c024112c90605a8f93a134971a617ab4d383f9) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index 248938f263bf..a2a5ec3a7749 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -37,7 +37,7 @@ On success, an object is returned, containing: - **reserved_to_block** (u32): Block height where reservation will expire - **channels** (array of objects): - **peer_id** (pubkey): the peer with which the channel is opened - - **our_amount_msat** (msat): available satoshis on our node's end of the channel + - **our_amount_msat** (msat): available satoshis on our node’s end of the channel - **amount_msat** (msat): total channel value - **funding_txid** (txid): funding transaction id - **funding_output** (u32): the 0-based index of the output in the funding transaction @@ -67,4 +67,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:26ae5f60c67eb3a481191ccf109b09ded7dc14d2881898b9f0173ef05a2ef47b) +[comment]: # ( SHA256STAMP:7e2ee47b9e35c222ee8b671745990800feaba771cf60fbe8390c2afd040e878f) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index eed0b2c48130..763a8d2329c2 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -56,4 +56,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5fc525b5436359e2271f4131a626bcd1654792d980e478738a0564dcedb58761) +[comment]: # ( SHA256STAMP:3dc5d5b8f7796d29e0d174d96e93915cbc7131b173a1547de022e021c55e8db6) diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index 59208b5a9062..39d184904ad7 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -95,4 +95,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:f9e1f4655b416c5e60279cf11a832bc4c652f503e48095dc3cf39deee5f0c769) +[comment]: # ( SHA256STAMP:4a5cfb1cf3d7fd77e49d6e7e369a9a6d374345b011d7db2fa9b4062156869ca4) diff --git a/doc/lightning-listoffers.7.md b/doc/lightning-listoffers.7.md index 7cf74ee2ec06..baca636d23eb 100644 --- a/doc/lightning-listoffers.7.md +++ b/doc/lightning-listoffers.7.md @@ -80,4 +80,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:12402a6d1f50df80e6efa5f10de38911385e2e5386e67d38d15b7045d661c78a) +[comment]: # ( SHA256STAMP:5cae5e0e423e66b02602ecc433de9686b16630979e794944059c65a100f54f9e) diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index da8e5e66a919..a862b51e1267 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -56,4 +56,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7dd58bcc0886cc981dff1534fac6753a4554c801a6c03e8ed7295166fcd5f89d) +[comment]: # ( SHA256STAMP:6ffbb1273de04f356cf79dab9a988ab030eee3317cb22e10d12d1c672249fc67) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 6bdf46e5c50a..f3ff7e96bd64 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -376,4 +376,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:7d11de5d2ff844d6f9e4b8093bd135c7a38d17f159ba7aca4821c5365ca11e71) +[comment]: # ( SHA256STAMP:956a13291bebc808bf1505a5d2030280aca441c5ca9991a6baae70c8715429a4) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index fc8c1e8b193e..941078994535 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4a3ec9bd1d5b1959856d3822245e94793712d23f82f5909a415125810bdcd538) +[comment]: # ( SHA256STAMP:1dfcb495e0004b9dadffd7f69b58275bf9168c9f4007675b390ebbaea07ffde6) diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index b0f7fc1795da..767a92bde531 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -104,4 +104,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:f170fb4e2ab3bf44d6159f9bcf5445102db3774f19cb61e7b8c68acfaac51688) +[comment]: # ( SHA256STAMP:ba0624377601e6e90c2ca90b709fd076f3ed0f2b813f73553ec6b935eeec54a1) diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index 2cd8a062dfad..e42b0a228972 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -158,4 +158,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:acad87896396156dff379973010ed1243af8bbb3ee5cfcc052ca177e0725bce3) +[comment]: # ( SHA256STAMP:a6358ad8d361ae4104c727e6b8ab342923a613b78d5f13552794f827a1125e8b) diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index e756698d6027..52b5379d8bcb 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -71,4 +71,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:0b374dbb074a5eed153d6a153526c20bd1681f67245dfc42a8ec3a2f1510c6b5) +[comment]: # ( SHA256STAMP:044cdcd69e6ece931b6d0f9b25dd842fd456ee479725e610c03694210256583f) diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index ca616c307b7f..e2dda2a4c457 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -55,4 +55,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:af64240aaa930f899eda7b146a5cdc22abb7572d20ff6f9012097a84b3affeaa) +[comment]: # ( SHA256STAMP:1a7b5336bdb0dbc93c9e160bb36c20c0d0d3fb908bdd85a84499fbc99680f3a6) diff --git a/doc/lightning-notifications.7.md b/doc/lightning-notifications.7.md index bd190df2cba8..5667fb773bc2 100644 --- a/doc/lightning-notifications.7.md +++ b/doc/lightning-notifications.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:86b47e27413141ffd6e8b79d83ac4a5cc80e5a7e6f5ba4b0df6825e744f9eea7) +[comment]: # ( SHA256STAMP:1a64fbaed63ffee21df3d46956a6dca193982b1b135a9b095e68652a720c77ac) diff --git a/doc/lightning-offer.7.md b/doc/lightning-offer.7.md index bc6d5421b950..0fa3a4218039 100644 --- a/doc/lightning-offer.7.md +++ b/doc/lightning-offer.7.md @@ -134,4 +134,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c35ff51ba9c0f373c22cbf42504d1328299e5af0e37e7ae3f80324ff2c10100a) +[comment]: # ( SHA256STAMP:4bbcec9c30f77239db780945965ad5cccf702365c3e592921fac57ed6bfd080f) diff --git a/doc/lightning-offerout.7.md b/doc/lightning-offerout.7.md index b52d408c7ac9..a70db8707595 100644 --- a/doc/lightning-offerout.7.md +++ b/doc/lightning-offerout.7.md @@ -99,4 +99,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:99a4e1cdf68ec7bcf05b9d6b088b8a84f01d085fae5a1565192f488ff8484570) +[comment]: # ( SHA256STAMP:fb60c3239f3d47b421f842304263ec73f864a307b77e39265653c3e85880a483) diff --git a/doc/lightning-openchannel_abort.7.md b/doc/lightning-openchannel_abort.7.md index a379edba62a7..77748c6af79d 100644 --- a/doc/lightning-openchannel_abort.7.md +++ b/doc/lightning-openchannel_abort.7.md @@ -54,4 +54,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:898dd91de80eb022badf10bb572af5ad9211f17364207a1837f15dd0bc252eca) +[comment]: # ( SHA256STAMP:78d6fbc1044e3a499ca618ea71845aa04043f46c169f4f50763644c7a3e35572) diff --git a/doc/lightning-openchannel_bump.7.md b/doc/lightning-openchannel_bump.7.md index e1940d185ad4..2eb748c4c5f6 100644 --- a/doc/lightning-openchannel_bump.7.md +++ b/doc/lightning-openchannel_bump.7.md @@ -80,4 +80,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:4c15f6f42116769c12494154266359e025753150ec7862937c2864fd99dd3d45) +[comment]: # ( SHA256STAMP:0b3c4fc19cdad9162b91585c4af2dc5293ecd8925628d10b612cd777dcdedeea) diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index a4052ad6fc34..bd043a91ced6 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:d37b879bb07c8a29c3733620792bff777d8316e22cea9abd38efca9585a790e0) +[comment]: # ( SHA256STAMP:bd405699ff27104ccc97dec81be9de1e7459c91333d78616268e4e9c198ee5af) diff --git a/doc/lightning-openchannel_signed.7.md b/doc/lightning-openchannel_signed.7.md index 38270bfc8513..51dec370a7bf 100644 --- a/doc/lightning-openchannel_signed.7.md +++ b/doc/lightning-openchannel_signed.7.md @@ -66,4 +66,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:d0d4435e0b0885f8f3685ea381cf0b020db2ff1ea5ca44c7d0d09e59d0419cc7) +[comment]: # ( SHA256STAMP:d85297e1ab0b3bbf206082c479dcfdc6469461e531321ce5576c6ff7f296d481) diff --git a/doc/lightning-openchannel_update.7.md b/doc/lightning-openchannel_update.7.md index 8257740620b7..55618e278c32 100644 --- a/doc/lightning-openchannel_update.7.md +++ b/doc/lightning-openchannel_update.7.md @@ -71,4 +71,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a8f8b9f3ad51bea47548602a73c4e0844b8ed1493054a226ec97397ccef25aa4) +[comment]: # ( SHA256STAMP:22ff9536e97ea194d9d9ba10a4f3244a0818a1605502b7ed25241a3a97f041d1) diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md index 6635b9a57a54..8afc62370c1b 100644 --- a/doc/lightning-parsefeerate.7.md +++ b/doc/lightning-parsefeerate.7.md @@ -43,4 +43,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a7bb800571a9ae47b3fb417ef02ce62de8afb2a710c45961b4871e270b4560d9) +[comment]: # ( SHA256STAMP:045cab2977c7fcc12ed5267d4007a704028a00bc07f90265ed1cd7a46a414e63) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index cc241c8eb600..49dc3be818ca 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -143,4 +143,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1c931f2ff49a169011ca6c2abde58281570a1db0dfbdca829105999723fe8bb8) +[comment]: # ( SHA256STAMP:bf507985544575c4ef2fe194fda6a693378cb8ab3bfb30ca7a7c066be271be29) diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index 9408b06f2cc4..225b48bf7383 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -69,4 +69,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:aa91cd20bea0156c4b2b2ca377945c383ad3ae06d31b972fbce9fc1999cfbc70) +[comment]: # ( SHA256STAMP:a78a1d58cfe1fbf654ee58aad20ffcaa075f63bd8774c64be5ec28857e95ae3b) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index d7759f927eb1..3b4f455c15af 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -62,4 +62,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:418af4df95dc56c0d1bf1d42dded0a5fcbc875800a603d80ea6d18bb2ff0b531) +[comment]: # ( SHA256STAMP:a07c71d232c39c0b959d07b9391d107413841753b67443d5f3698e1afd9cd2e4) diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index ac799598d7b5..2cdc30649b10 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -63,4 +63,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:77e6483379efd12bde3532e3d86a4305c83c7ab31819eef4b1ec0a993f6d24bc) +[comment]: # ( SHA256STAMP:a675e16a820eca4da07743ace010deaa12aa51d2c3d73d4db6b32ffb8ee65f7a) diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index 1517b3eb5b61..a7ef72579409 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -68,4 +68,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ebaf8137d91cab5c0899dc749079ab5f1bbe8df998c155b41496b80820128af3) +[comment]: # ( SHA256STAMP:f07e2df415b1ec2ad7b19acdd2909616d7aa54bb3c5ae69359d4c8b87ee839bf) diff --git a/doc/lightning-sendinvoice.7.md b/doc/lightning-sendinvoice.7.md index 4c2c02c181c0..6ca11f5dc6ea 100644 --- a/doc/lightning-sendinvoice.7.md +++ b/doc/lightning-sendinvoice.7.md @@ -78,4 +78,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4788ac272caee6e3dc8200cfbaf9dafffd2d10880b33f3d06e067767c1e0eee8) +[comment]: # ( SHA256STAMP:b4cf6c380589a566c695e96f3668294b4411a57c7724aa68b293bac9e2194462) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index c906c38f9415..ad88418c6e1f 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -127,4 +127,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:aeade675a3e42ffc0b7be2bfefe429fdc5b52e6f4000687db90dfffd5b0b588d) +[comment]: # ( SHA256STAMP:d588d85b79f709a57441479504ee8761331c852284ebb8effeab91a557437517) diff --git a/doc/lightning-sendonionmessage.7.md b/doc/lightning-sendonionmessage.7.md index b371e9c9607b..03627a56ab33 100644 --- a/doc/lightning-sendonionmessage.7.md +++ b/doc/lightning-sendonionmessage.7.md @@ -50,4 +50,4 @@ Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:9125b71f093569c584c116210e488f11331029fca930dada6c040009daeb09f8) +[comment]: # ( SHA256STAMP:39a66bd8e28db8780d7b1365372f7cc638b32a80bb5515657d381b4520f06901) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 1bcddd7fc180..41e4ec2c30d3 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -127,4 +127,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f7572da509a442c08f73460c042d8e2aa950747ce175ebb9b89d32b88add6de6) +[comment]: # ( SHA256STAMP:44540ace609ccfa7b023526d7a92ba7cf4a6058f3ae2124c20fa65b92137e41b) diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index 33eba7570631..fd150b9d38fa 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -65,4 +65,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:0b374dbb074a5eed153d6a153526c20bd1681f67245dfc42a8ec3a2f1510c6b5) +[comment]: # ( SHA256STAMP:044cdcd69e6ece931b6d0f9b25dd842fd456ee479725e610c03694210256583f) diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index da2b8c1614bd..cefcf6f4d9b0 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -81,4 +81,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1f040269076ac47e5ed973225bfddbcffdec83dd91f6df143d29bcb981de04ed) +[comment]: # ( SHA256STAMP:2245fde48f1858886e0f484cb3d96331fef9c41b0081ae51478d912189c38907) diff --git a/doc/lightning-signmessage.7.md b/doc/lightning-signmessage.7.md index 86e125dcebc5..86fa363fd986 100644 --- a/doc/lightning-signmessage.7.md +++ b/doc/lightning-signmessage.7.md @@ -41,4 +41,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:54ed4ed0f8e10de232b82793ffa6a1794ef632f8fd315deeb276803f6f2d639d) +[comment]: # ( SHA256STAMP:028ade4d84a65b0438347897f4ff5cfc99b5f22d8320b606c9630a1f9da16ef2) diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index ed479897f7cf..cef87f311044 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -71,4 +71,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:d306b6b0c64e9739bf6a3c6751137f23da6300c6cd8abc3fcd7ebfae9caae732) +[comment]: # ( SHA256STAMP:5f7bc2a5f8b6fe72bf70caa3ff14d6f1260c2d366becbc7798ee3bb0374e0b1b) diff --git a/doc/lightning-stop.7.md b/doc/lightning-stop.7.md index f5f79a9f1c27..3c86d5194a5c 100644 --- a/doc/lightning-stop.7.md +++ b/doc/lightning-stop.7.md @@ -42,4 +42,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:42c2ed4003d088c2b42260d7c1098ae81cfe2f91fb6fce3a7dffe6b8729a5181) +[comment]: # ( SHA256STAMP:bbdf7415bc7de519ca944c28326c334d9f014f4c987d7e3017ac628c6d1c55ec) diff --git a/doc/lightning-txdiscard.7.md b/doc/lightning-txdiscard.7.md index 8cd8a5aca76b..1144328306a5 100644 --- a/doc/lightning-txdiscard.7.md +++ b/doc/lightning-txdiscard.7.md @@ -44,4 +44,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1edb3d89e7c231cae344baf6bacf658bbd104c751b1e7dc6a32bb83d102a98eb) +[comment]: # ( SHA256STAMP:ced935d8d9047fe1dfb746fc72aafc5b99a8b7b639f854a56478884e5205ffb9) diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index 0d7ad3c9e1d3..dcc46344aa3e 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -84,4 +84,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3b18b65b58bce0ce6fcacd49d86b6da2b59828c4b3474d1de0f6ab76d810044c) +[comment]: # ( SHA256STAMP:f16a12290870442316c8f3fb552627637610ab6a7cfed9082089040c78dce2be) diff --git a/doc/lightning-txsend.7.md b/doc/lightning-txsend.7.md index 6505ca628fac..8bb18b6c1893 100644 --- a/doc/lightning-txsend.7.md +++ b/doc/lightning-txsend.7.md @@ -44,4 +44,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f97eb686421f6c9636a97100787cb3af3b482e07434d9e2b158ed09f06335f32) +[comment]: # ( SHA256STAMP:5bf27f1cbcb247cde5c5570f90be77fc7e8b3e8c80622e75c31e6ac445f2b910) diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index 14106f3bfb07..869adbb3f576 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -53,4 +53,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4239256a270c707e6beb2da43fb5069ebc59affb9f22df8fc67c19229aca3f44) +[comment]: # ( SHA256STAMP:f7aca3e1a40d66e07986cb9e98033e815c4eea2237dc75664a6c47951a8132ed) diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index bf111880e962..01db6aede6ba 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -100,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3be73c6c58be24510cfa792ad428990664ebf1e01d6cdb8c245607aea376d79a) +[comment]: # ( SHA256STAMP:3c3734a0eb4c2fabf216e11e729ad582cb1fb91dbcb8d2bfc44d56f3206f20fc) diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index a4e8184905fb..6dafab1ad074 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:53448470413976900d69dfd360b36e16b893b2db2bb959ee06bdfc7a98e58d7b) +[comment]: # ( SHA256STAMP:33df5fb9bcbcb6d2240d0d18b970b2300414aae36b81fb276fcedfc21480d22f) diff --git a/doc/lightning-waitblockheight.7.md b/doc/lightning-waitblockheight.7.md index 2605244fa9a2..a84561bacd98 100644 --- a/doc/lightning-waitblockheight.7.md +++ b/doc/lightning-waitblockheight.7.md @@ -38,4 +38,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bf58d803775bc97144d92449d85de21189fa00fe9cec22bd64bc7cde87ebfe06) +[comment]: # ( SHA256STAMP:98f9993935e2820e8e407d1743764346ca6fa1b72228cc82827617a2ed3f3c80) diff --git a/doc/lightning-waitinvoice.7.md b/doc/lightning-waitinvoice.7.md index 9d4cad467036..c3447b5addfe 100644 --- a/doc/lightning-waitinvoice.7.md +++ b/doc/lightning-waitinvoice.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:53448470413976900d69dfd360b36e16b893b2db2bb959ee06bdfc7a98e58d7b) +[comment]: # ( SHA256STAMP:33df5fb9bcbcb6d2240d0d18b970b2300414aae36b81fb276fcedfc21480d22f) diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index 42d526d094df..abca8ea0ed31 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -101,4 +101,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c40814f929fb6d741e0724ba75f0833e52fae1f03ed2d1fac9a8ba1186ceabab) +[comment]: # ( SHA256STAMP:3f89cf80acc1e9363509f0a053a617f8b381790823f8cd05fa6c708eb72fcc7e) diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index aec49c3624dc..204f2131c64d 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:10741263b2d8890b368d48ab7be8d2fc9bd149afa73c5ba7022dfb6903f0135d) +[comment]: # ( SHA256STAMP:cef8d48a59313019e671900621426733d47be2f0c22d5cb2d06ce0b9b7d43592) diff --git a/doc/schemas/addgossip.schema.json b/doc/schemas/addgossip.schema.json index b797a82c2f6f..1aad2dcae935 100644 --- a/doc/schemas/addgossip.schema.json +++ b/doc/schemas/addgossip.schema.json @@ -1,7 +1,6 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "properties": { - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": {} } diff --git a/doc/schemas/autocleaninvoice.schema.json b/doc/schemas/autocleaninvoice.schema.json index 7ed3db3d513b..f370eee56490 100644 --- a/doc/schemas/autocleaninvoice.schema.json +++ b/doc/schemas/autocleaninvoice.schema.json @@ -1,41 +1,48 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "enabled" ], + "$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": { - "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": { } - } + "enabled": {} } + } } diff --git a/doc/schemas/check.schema.json b/doc/schemas/check.schema.json index faecc7d78a6a..5b4010afb3ba 100644 --- a/doc/schemas/check.schema.json +++ b/doc/schemas/check.schema.json @@ -1,12 +1,14 @@ { - "$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" ] + "$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/checkmessage.schema.json b/doc/schemas/checkmessage.schema.json index 7bc2833bd142..c4bd81988416 100644 --- a/doc/schemas/checkmessage.schema.json +++ b/doc/schemas/checkmessage.schema.json @@ -1,56 +1,66 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "verified" ], - "additionalProperties": true, - "properties": { - "verified": { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "verified" + ], + "additionalProperties": true, + "properties": { + "verified": { + "type": "boolean", + "description": "Whether the signature was valid" + } + }, + "allOf": [ + { + "if": { + "properties": { + "verified": { "type": "boolean", - "description": "Whether the signature was valid" + "enum": [ + true + ] + } } + }, + "then": { + "additionalProperties": false, + "required": [ + "pubkey" + ], + "properties": { + "verified": {}, + "pubkey": { + "type": "pubkey", + "description": "the *pubkey* parameter, or the pubkey found by looking for known nodes" + } + } + } }, - "allOf": [ - { - "if": { - "properties": { - "verified": { - "type": "boolean", - "enum": [ true ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "pubkey" ], - "properties": { - "verified": { }, - "pubkey": { - "type": "pubkey", - "description": "the *pubkey* parameter, or the pubkey found by looking for known nodes" - } - } - } - }, - { - "if": { - "properties": { - "verified": { - "type": "boolean", - "enum": [ false ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "pubkey" ], - "properties": { - "verified": { }, - "pubkey": { - "type": "pubkey", - "description": "the *pubkey* (if any) which could have signed this; this is usually not useful!" - } - } - } - } - ] + { + "if": { + "properties": { + "verified": { + "type": "boolean", + "enum": [ + false + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "pubkey" + ], + "properties": { + "verified": {}, + "pubkey": { + "type": "pubkey", + "description": "the *pubkey* (if any) which could have signed this; this is usually not useful!" + } + } + } + } + ] } diff --git a/doc/schemas/close.schema.json b/doc/schemas/close.schema.json index b28dee6459c9..8e547dae3dfb 100644 --- a/doc/schemas/close.schema.json +++ b/doc/schemas/close.schema.json @@ -1,41 +1,53 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "type" ], + "$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": { - "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": { } - } + "type": {} } + } } diff --git a/doc/schemas/connect.schema.json b/doc/schemas/connect.schema.json index 3ce55591c5fb..557292b652c5 100644 --- a/doc/schemas/connect.schema.json +++ b/doc/schemas/connect.schema.json @@ -1,82 +1,110 @@ { - "$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" - } - } - } - } - ] - } + "$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" + } + } + } + } + ] } + } } diff --git a/doc/schemas/createinvoice.schema.json b/doc/schemas/createinvoice.schema.json index 8e79c8094966..f1d6e1828dc0 100644 --- a/doc/schemas/createinvoice.schema.json +++ b/doc/schemas/createinvoice.schema.json @@ -1,71 +1,81 @@ { - "$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 - }, - "payer_note": { - "type": "string", - "description": "the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only)." - } + "$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 + }, + "payer_note": { + "type": "string", + "description": "the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only)." } + } } diff --git a/doc/schemas/createonion.schema.json b/doc/schemas/createonion.schema.json index 7917163e18df..7b4df99239c0 100644 --- a/doc/schemas/createonion.schema.json +++ b/doc/schemas/createonion.schema.json @@ -1,22 +1,25 @@ { - "$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 - } - } + "$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 + } } + } } diff --git a/doc/schemas/datastore.schema.json b/doc/schemas/datastore.schema.json index 783642a4f3be..92aa077041ab 100644 --- a/doc/schemas/datastore.schema.json +++ b/doc/schemas/datastore.schema.json @@ -1,27 +1,29 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "key" ], - "properties": { - "key": { - "type": "array", - "items": { - "type": "string", - "description": "Part of the key added to the datastore" - } - }, - "generation": { - "type": "u64", - "description": "The number of times this has been updated" - }, - "hex": { - "type": "hex", - "description": "The hex data which has been added to the datastore" - }, - "string": { - "type": "string", - "description": "The data as a string, if it's valid utf-8" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "key" + ], + "properties": { + "key": { + "type": "array", + "items": { + "type": "string", + "description": "Part of the key added to the datastore" + } + }, + "generation": { + "type": "u64", + "description": "The number of times this has been updated" + }, + "hex": { + "type": "hex", + "description": "The hex data which has been added to the datastore" + }, + "string": { + "type": "string", + "description": "The data as a string, if it's valid utf-8" } + } } diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json index d985e365a1fb..9e12c1588876 100644 --- a/doc/schemas/decode.schema.json +++ b/doc/schemas/decode.schema.json @@ -1,802 +1,908 @@ { - "$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!" - } + "$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" }, - "allOf": [ - { - "if": { - "properties": { - "type": { - "type": "string", - "enum": [ "bolt12 offer" ] - }, - "valid": { - "type": "boolean", - "enum": [ true ] - } - } - }, - "then": { - "required": [ "offer_id", "node_id", "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": "point32", - "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", "created_at", "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": "point32", - "description": "x-only public key of the offering node" - }, - "signature": { - "type": "bip340sig", - "description": "BIP-340 signature of the *node_id* on this offer" - }, - "chain": { - "type": "hex", - "description": "which blockchain this invoice is for (missing implies bitcoin mainnet only)", - "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": "point32", - "description": "the transient key which identifies the payer" - }, - "payer_info": { - "type": "hex", - "description": "the payer-provided blob to derive payer_key" - }, - "timestamp": { - "deprecated": true - }, - "created_at": { - "type": "u64", - "description": "the UNIX timestamp of invoice creation" - }, - "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 *created_at* 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": { }, - "chain": { }, - "amount_msat": { }, - "send_invoice": { }, - "refund_for": { }, - "description": { }, - "vendor": { }, - "features": { }, - "paths": { }, - "quantity": { }, - "recurrence_counter": { }, - "recurrence_start": { }, - "recurrence_basetime": { }, - "payer_key": { }, - "payer_info": { }, - "timestamp": { }, - "created_at": { }, - "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_created_at": { - "type": "string", - "description": "Missing **created_at**" - }, - "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 the offer this is requesting (merkle hash of non-signature fields)", - "maxLength": 64, - "minLength": 64 - }, - "chain": { - "type": "hex", - "description": "which blockchain this invoice_request is for (missing implies bitcoin mainnet only)", - "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": "point32", - "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": { }, - "chain": { }, - "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" - } - } - } - } - } - } - } - ] + "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", + "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": "point32", + "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", + "created_at", + "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": "point32", + "description": "x-only public key of the offering node" + }, + "signature": { + "type": "bip340sig", + "description": "BIP-340 signature of the *node_id* on this offer" + }, + "chain": { + "type": "hex", + "description": "which blockchain this invoice is for (missing implies bitcoin mainnet only)", + "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": "point32", + "description": "the transient key which identifies the payer" + }, + "payer_info": { + "type": "hex", + "description": "the payer-provided blob to derive payer_key" + }, + "timestamp": { + "deprecated": true + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp of invoice creation" + }, + "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 *created_at* 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": {}, + "chain": {}, + "amount_msat": {}, + "send_invoice": {}, + "refund_for": {}, + "description": {}, + "vendor": {}, + "features": {}, + "paths": {}, + "quantity": {}, + "recurrence_counter": {}, + "recurrence_start": {}, + "recurrence_basetime": {}, + "payer_key": {}, + "payer_info": {}, + "timestamp": {}, + "created_at": {}, + "payment_hash": {}, + "relative_expiry": {}, + "min_final_cltv_expiry": {}, + "fallbacks": { + "type": "array", + "items": { + "type": "object", + "required": [ + "version", + "hex" + ], + "properties": { + "version": {}, + "hex": {}, + "address": {}, + "warning_invoice_fallbacks_version_invalid": { + "type": "string", + "description": "**version** is > 16" + } + } + } + }, + "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_created_at": { + "type": "string", + "description": "Missing **created_at**" + }, + "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**" + } + } + } + }, + { + "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 the offer this is requesting (merkle hash of non-signature fields)", + "maxLength": 64, + "minLength": 64 + }, + "chain": { + "type": "hex", + "description": "which blockchain this invoice_request is for (missing implies bitcoin mainnet only)", + "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": "point32", + "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": {}, + "chain": {}, + "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 index 9494042936c6..15fd12298657 100644 --- a/doc/schemas/decodepay.schema.json +++ b/doc/schemas/decodepay.schema.json @@ -1,146 +1,171 @@ { - "$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" - } - } - } - } + "$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" + } + } + } } + } } diff --git a/doc/schemas/deldatastore.schema.json b/doc/schemas/deldatastore.schema.json index 4f0c7c347043..c5a2875b6a44 100644 --- a/doc/schemas/deldatastore.schema.json +++ b/doc/schemas/deldatastore.schema.json @@ -1,27 +1,29 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "key" ], - "properties": { - "key": { - "type": "array", - "items": { - "type": "string", - "description": "Part of the key added to the datastore" - } - }, - "generation": { - "type": "u64", - "description": "The number of times this has been updated" - }, - "hex": { - "type": "hex", - "description": "The hex data which has removed from the datastore" - }, - "string": { - "type": "string", - "description": "The data as a string, if it's valid utf-8" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "key" + ], + "properties": { + "key": { + "type": "array", + "items": { + "type": "string", + "description": "Part of the key added to the datastore" + } + }, + "generation": { + "type": "u64", + "description": "The number of times this has been updated" + }, + "hex": { + "type": "hex", + "description": "The hex data which has removed from the datastore" + }, + "string": { + "type": "string", + "description": "The data as a string, if it's valid utf-8" } + } } diff --git a/doc/schemas/delexpiredinvoice.schema.json b/doc/schemas/delexpiredinvoice.schema.json index 3f3efd17a47a..f99496c5ac84 100644 --- a/doc/schemas/delexpiredinvoice.schema.json +++ b/doc/schemas/delexpiredinvoice.schema.json @@ -1,8 +1,7 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ ], - "additionalProperties": false, - "properties": { - } + "$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 index bbcf61fca5a2..1803a7fbdfe8 100644 --- a/doc/schemas/delinvoice.schema.json +++ b/doc/schemas/delinvoice.schema.json @@ -1,159 +1,179 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "label", "payment_hash", "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" - }, - "msatoshi": { - "deprecated": "true" - }, - "amount_msat": { - "type": "msat", - "description": "the amount required to pay this invoice" - }, - "description": { - "type": "string", - "description": "description used in the invoice" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "status": { - "type": "string", - "description": "State of invoice", - "enum": [ "paid", "expired", "unpaid" ] - }, - "expires_at": { - "type": "u64", - "description": "UNIX timestamp when invoice expires (or expired)" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "label", + "payment_hash", + "status", + "expires_at" + ], + "additionalProperties": true, + "properties": { + "label": { + "type": "string", + "description": "Unique label given at creation time" }, - "allOf": [ - { - "if": { - "required": [ "bolt12" ] - }, - "then": { - "required": [ ], - "additionalProperties": false, - "properties": { - "label": { }, - "bolt12": { }, - "status": { }, - "expires_at": { }, - "msatoshi": { }, - "amount_msat": { }, - "description": { }, - "payment_hash": { }, - "pay_index": { }, - "amount_received_msat": { }, - "paid_at": { }, - "payment_preimage": { }, - "local_offer_id": { - "type": "hex", - "description": "offer for which this invoice was created" - }, - "payer_note": { - "type": "string", - "description": "the optional *payer_note* from invoice_request which created this invoice" - } - } - }, - "else": { - "required": [ "bolt11" ], - "additionalProperties": false, - "properties": { - "label": { }, - "bolt11": { }, - "status": { }, - "expires_at": { }, - "msatoshi": { }, - "amount_msat": { }, - "description": { }, - "payment_hash": { }, - "pay_index": { }, - "amount_received_msat": { }, - "paid_at": { }, - "payment_preimage": { } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "paid" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "pay_index", "amount_received_msat", "paid_at", "payment_preimage" ], - "properties": { - "label": { }, - "bolt11": { }, - "bolt12": { }, - "status": { }, - "expires_at": { }, - "msatoshi": { }, - "amount_msat": { }, - "description": { }, - "payment_hash": { }, - "payer_note": { }, - "local_offer_id": { }, - "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 - } - } - }, - "else": { - "additionalProperties": false, - "properties": { - "label": { }, - "bolt11": { }, - "bolt12": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "description": { }, - "payment_hash": { }, - "expires_at": { }, - "pay_index": { }, - "payer_note": { }, - "local_offer_id": { } - } - } - } - ] + "bolt11": { + "type": "string", + "description": "BOLT11 string" + }, + "bolt12": { + "type": "string", + "description": "BOLT12 string" + }, + "msatoshi": { + "deprecated": "true" + }, + "amount_msat": { + "type": "msat", + "description": "the amount required to pay this invoice" + }, + "description": { + "type": "string", + "description": "description used in the invoice" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "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": [], + "additionalProperties": false, + "properties": { + "label": {}, + "bolt12": {}, + "status": {}, + "expires_at": {}, + "msatoshi": {}, + "amount_msat": {}, + "description": {}, + "payment_hash": {}, + "pay_index": {}, + "amount_received_msat": {}, + "paid_at": {}, + "payment_preimage": {}, + "local_offer_id": { + "type": "hex", + "description": "offer for which this invoice was created" + }, + "payer_note": { + "type": "string", + "description": "the optional *payer_note* from invoice_request which created this invoice" + } + } + }, + "else": { + "required": [ + "bolt11" + ], + "additionalProperties": false, + "properties": { + "label": {}, + "bolt11": {}, + "status": {}, + "expires_at": {}, + "msatoshi": {}, + "amount_msat": {}, + "description": {}, + "payment_hash": {}, + "pay_index": {}, + "amount_received_msat": {}, + "paid_at": {}, + "payment_preimage": {} + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "paid" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "pay_index", + "amount_received_msat", + "paid_at", + "payment_preimage" + ], + "properties": { + "label": {}, + "bolt11": {}, + "bolt12": {}, + "status": {}, + "expires_at": {}, + "msatoshi": {}, + "amount_msat": {}, + "description": {}, + "payment_hash": {}, + "payer_note": {}, + "local_offer_id": {}, + "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 + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "label": {}, + "bolt11": {}, + "bolt12": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "description": {}, + "payment_hash": {}, + "expires_at": {}, + "pay_index": {}, + "payer_note": {}, + "local_offer_id": {} + } + } + } + ] } diff --git a/doc/schemas/delpay.schema.json b/doc/schemas/delpay.schema.json index d603a1d01d6c..0f31780f2fbd 100644 --- a/doc/schemas/delpay.schema.json +++ b/doc/schemas/delpay.schema.json @@ -1,85 +1,97 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "payments" ], - "additionalProperties": false, - "properties": { - "payments": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "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" - }, - "msatoshi_sent": { - "deprecated": true - }, - "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" - }, - "msatoshi": { - "deprecated": true - }, - "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" - }, - "groupid": { - "type": "u64", - "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" - }, - "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." - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "payments" + ], + "additionalProperties": false, + "properties": { + "payments": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "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" + }, + "msatoshi_sent": { + "deprecated": true + }, + "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" + }, + "msatoshi": { + "deprecated": true + }, + "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" + }, + "groupid": { + "type": "u64", + "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" + }, + "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." + } + } + } } + } } diff --git a/doc/schemas/disableoffer.schema.json b/doc/schemas/disableoffer.schema.json index c781a54b0b60..8b7331bf5de4 100644 --- a/doc/schemas/disableoffer.schema.json +++ b/doc/schemas/disableoffer.schema.json @@ -1,39 +1,48 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "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" - }, - "bolt12_unsigned": { - "type": "string", - "description": "The bolt12 string representing this offer, without signature" - }, - "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" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "offer_id", + "active", + "single_use", + "bolt12", + "bolt12_unsigned", + "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" + }, + "bolt12_unsigned": { + "type": "string", + "description": "The bolt12 string representing this offer, without signature" + }, + "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 index b797a82c2f6f..1aad2dcae935 100644 --- a/doc/schemas/disconnect.schema.json +++ b/doc/schemas/disconnect.schema.json @@ -1,7 +1,6 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "properties": { - } + "$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 index 3f25817e7303..980f1d20c5a6 100644 --- a/doc/schemas/feerates.schema.json +++ b/doc/schemas/feerates.schema.json @@ -1,119 +1,131 @@ { - "$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" - } - } - } + "$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 index ff538b58cfda..951dcd19d15f 100644 --- a/doc/schemas/fetchinvoice.schema.json +++ b/doc/schemas/fetchinvoice.schema.json @@ -1,68 +1,77 @@ { - "$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" - } - } - } + "$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 index 44775d377513..40ee1deb1df7 100644 --- a/doc/schemas/fundchannel.schema.json +++ b/doc/schemas/fundchannel.schema.json @@ -1,30 +1,35 @@ { - "$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`" - } + "$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 index a9e1e663e091..2c507db3e20f 100644 --- a/doc/schemas/fundchannel_cancel.schema.json +++ b/doc/schemas/fundchannel_cancel.schema.json @@ -1,12 +1,14 @@ { - "$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" - } + "$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 index 90b29faeefc0..3b3d0eefbaae 100644 --- a/doc/schemas/fundchannel_complete.schema.json +++ b/doc/schemas/fundchannel_complete.schema.json @@ -1,19 +1,24 @@ { - "$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" - } + "$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 index 335e1eb1f0b4..b7cc51b859ff 100644 --- a/doc/schemas/fundchannel_start.schema.json +++ b/doc/schemas/fundchannel_start.schema.json @@ -1,20 +1,23 @@ { - "$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`" - } + "$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/funderupdate.schema.json b/doc/schemas/funderupdate.schema.json index a130be14dd1a..4bca1365220b 100644 --- a/doc/schemas/funderupdate.schema.json +++ b/doc/schemas/funderupdate.schema.json @@ -1,76 +1,92 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": ["summary", "policy", "policy_mod", "leases_only", "min_their_funding_msat", "max_their_funding_msat", "per_channel_min_msat", "per_channel_max_msat", "reserve_tank_msat", "fuzz_percent", "fund_probability"], - "properties": { - "summary": { - "type": "string", - "description": "Summary of the current funding policy e.g. (match 100)" - }, - "policy": { - "type": "string", - "enum": [ "match", "available", "fixed" ], - "description": "Policy funder plugin will use to decide how much captial to commit to a v2 open channel request" - }, - "policy_mod": { - "type": "u32", - "description": "The *policy_mod* is the number or 'modification' to apply to the policy." - }, - "leases_only": { - "type": "boolean", - "description": "Only contribute funds to `option_will_fund` lease requests." - }, - "min_their_funding_msat": { - "type": "msat", - "description": "The minimum funding sats that we require from peer to activate our funding policy." - }, - "max_their_funding_msat": { - "type": "msat", - "description": "The maximum funding sats that we'll allow from peer to activate our funding policy." - }, - "per_channel_min_msat": { - "type": "msat", - "description": "The minimum amount that we will fund a channel open with." - }, - "per_channel_max_msat": { - "type": "msat", - "description": "The maximum amount that we will fund a channel open with." - }, - "reserve_tank_msat": { - "type": "msat", - "description": "Amount of sats to leave available in the node wallet." - }, - "fuzz_percent": { - "type": "u32", - "description": "Percentage to fuzz our funding amount by." - }, - "fund_probability": { - "type": "u32", - "description": "Percent of opens to consider funding. 100 means we'll consider funding every requested open channel request." - }, - "lease_fee_base_msat": { - "type": "msat", - "description": "Flat fee to charge for a channel lease." - }, - "lease_fee_basis": { - "type": "u32", - "description": "Proportional fee to charge for a channel lease, calculated as 1/10,000th of requested funds." - }, - "funding_weight": { - "type": "u32", - "description": "Transaction weight the channel opener will pay us for a leased funding transaction." - }, - "channel_fee_max_base_msat": { - "type": "msat", - "description": "Maximum channel_fee_base_msat we'll charge for routing funds leased on this channel." - }, - "channel_fee_max_proportional_thousandths": { - "type": "u32", - "description": "Maximum channel_fee_proportional_millitionths we'll charge for routing funds leased on this channel, in thousandths." - }, - "compact_lease": { - "type": "hex", - "description": "Compact description of the channel lease parameters." - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "summary", + "policy", + "policy_mod", + "leases_only", + "min_their_funding_msat", + "max_their_funding_msat", + "per_channel_min_msat", + "per_channel_max_msat", + "reserve_tank_msat", + "fuzz_percent", + "fund_probability" + ], + "properties": { + "summary": { + "type": "string", + "description": "Summary of the current funding policy e.g. (match 100)" + }, + "policy": { + "type": "string", + "enum": [ + "match", + "available", + "fixed" + ], + "description": "Policy funder plugin will use to decide how much captial to commit to a v2 open channel request" + }, + "policy_mod": { + "type": "u32", + "description": "The *policy_mod* is the number or 'modification' to apply to the policy." + }, + "leases_only": { + "type": "boolean", + "description": "Only contribute funds to `option_will_fund` lease requests." + }, + "min_their_funding_msat": { + "type": "msat", + "description": "The minimum funding sats that we require from peer to activate our funding policy." + }, + "max_their_funding_msat": { + "type": "msat", + "description": "The maximum funding sats that we'll allow from peer to activate our funding policy." + }, + "per_channel_min_msat": { + "type": "msat", + "description": "The minimum amount that we will fund a channel open with." + }, + "per_channel_max_msat": { + "type": "msat", + "description": "The maximum amount that we will fund a channel open with." + }, + "reserve_tank_msat": { + "type": "msat", + "description": "Amount of sats to leave available in the node wallet." + }, + "fuzz_percent": { + "type": "u32", + "description": "Percentage to fuzz our funding amount by." + }, + "fund_probability": { + "type": "u32", + "description": "Percent of opens to consider funding. 100 means we'll consider funding every requested open channel request." + }, + "lease_fee_base_msat": { + "type": "msat", + "description": "Flat fee to charge for a channel lease." + }, + "lease_fee_basis": { + "type": "u32", + "description": "Proportional fee to charge for a channel lease, calculated as 1/10,000th of requested funds." + }, + "funding_weight": { + "type": "u32", + "description": "Transaction weight the channel opener will pay us for a leased funding transaction." + }, + "channel_fee_max_base_msat": { + "type": "msat", + "description": "Maximum channel_fee_base_msat we'll charge for routing funds leased on this channel." + }, + "channel_fee_max_proportional_thousandths": { + "type": "u32", + "description": "Maximum channel_fee_proportional_millitionths we'll charge for routing funds leased on this channel, in thousandths." + }, + "compact_lease": { + "type": "hex", + "description": "Compact description of the channel lease parameters." } + } } diff --git a/doc/schemas/fundpsbt.schema.json b/doc/schemas/fundpsbt.schema.json index 8aac22c6d46c..84f0969e77f9 100644 --- a/doc/schemas/fundpsbt.schema.json +++ b/doc/schemas/fundpsbt.schema.json @@ -1,61 +1,76 @@ { - "$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 *reserve* was true or a non-zero number, 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" - } - } - } - } + "$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 *reserve* was true or a non-zero number, 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 index 02e2c5b430e3..4e0eda46a409 100644 --- a/doc/schemas/getinfo.schema.json +++ b/doc/schemas/getinfo.schema.json @@ -1,148 +1,190 @@ { - "$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", "port" ], - "additionalProperties": true, - "properties": { - "type": { - "type": "string", - "enum": [ "ipv4", "ipv6", "torv2", "torv3", "websocket" ], - "description": "Type of connection" - }, - "port": { - "type": "u16", - "description": "port number" - } - }, - "if": { - "properties": { - "type": { - "type": "string", - "enum": [ "ipv4", "ipv6", "torv2", "torv3" ] - } - } - }, - "then": { - "required": [ "type", "address", "port" ], - "additionalProperties": false, - "properties": { - "type": { }, - "port": { }, - "address": { - "type": "string", - "description": "address in expected format for **type**" - } - } - }, - "else": { - "required": [ "type", "port" ], - "additionalProperties": false, - "properties": { - "type": { }, - "port": { } - } - } - } - }, - "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." - } + "$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", + "port" + ], + "additionalProperties": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "ipv4", + "ipv6", + "torv2", + "torv3", + "websocket" + ], + "description": "Type of connection" + }, + "port": { + "type": "u16", + "description": "port number" + } + }, + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "ipv4", + "ipv6", + "torv2", + "torv3" + ] + } + } + }, + "then": { + "required": [ + "type", + "address", + "port" + ], + "additionalProperties": false, + "properties": { + "type": {}, + "port": {}, + "address": { + "type": "string", + "description": "address in expected format for **type**" + } + } + }, + "else": { + "required": [ + "type", + "port" + ], + "additionalProperties": false, + "properties": { + "type": {}, + "port": {} + } + } + } + }, + "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 index 23c9981b5b69..b2ba62b7555d 100644 --- a/doc/schemas/getlog.schema.json +++ b/doc/schemas/getlog.schema.json @@ -1,127 +1,163 @@ { - "$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" - } - } - } - } - ] - } - } + "$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 index 9e5ebe7ebc03..d6878da411ac 100644 --- a/doc/schemas/getroute.schema.json +++ b/doc/schemas/getroute.schema.json @@ -1,47 +1,59 @@ { - "$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" ] - } - } - } - } + "$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/doc/schemas/getsharedsecret.schema.json b/doc/schemas/getsharedsecret.schema.json index 143d0e87cda3..276ea1e09463 100644 --- a/doc/schemas/getsharedsecret.schema.json +++ b/doc/schemas/getsharedsecret.schema.json @@ -1,14 +1,16 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "shared_secret" ], - "properties": { - "shared_secret": { - "type": "hex", - "description": "the SHA-2 of the compressed encoding of the shared secp256k1 point", - "maxLength": 64, - "minLength": 64 - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "shared_secret" + ], + "properties": { + "shared_secret": { + "type": "hex", + "description": "the SHA-2 of the compressed encoding of the shared secp256k1 point", + "maxLength": 64, + "minLength": 64 } + } } diff --git a/doc/schemas/help.schema.json b/doc/schemas/help.schema.json index 37c9ce150d1d..df8777c0d28e 100644 --- a/doc/schemas/help.schema.json +++ b/doc/schemas/help.schema.json @@ -1,35 +1,42 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "help" ], - "properties": { - "help": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "command", "category", "description", "verbose" ], - "properties": { - "command": { - "type": "string", - "description": "the command" - }, - "category": { - "type": "string", - "description": "the category for this command (useful for grouping)" - }, - "description": { - "type": "string", - "description": "a one-line description of the purpose of this command" - }, - "verbose": { - "type": "string", - "description": "a full description of this command (including whether it's deprecated)" - } - } - } - }, - "format-hint": { } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "help" + ], + "properties": { + "help": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "command", + "category", + "description", + "verbose" + ], + "properties": { + "command": { + "type": "string", + "description": "the command" + }, + "category": { + "type": "string", + "description": "the category for this command (useful for grouping)" + }, + "description": { + "type": "string", + "description": "a one-line description of the purpose of this command" + }, + "verbose": { + "type": "string", + "description": "a full description of this command (including whether it's deprecated)" + } + } + } + }, + "format-hint": {} + } } diff --git a/doc/schemas/invoice.schema.json b/doc/schemas/invoice.schema.json index 4e79765559ea..fcb3f899a034 100644 --- a/doc/schemas/invoice.schema.json +++ b/doc/schemas/invoice.schema.json @@ -1,48 +1,53 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "payment_hash", "expires_at", "bolt11", "payment_secret" ], - "properties": { - "bolt11": { - "type": "string", - "description": "the bolt11 string" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "payment_secret": { - "type": "hex", - "description": "the *payment_secret* to place in the onion", - "maxLength": 64, - "minLength": 64 - }, - "expires_at": { - "type": "u64", - "description": "UNIX timestamp of when invoice expires" - }, - "warning_capacity": { - "type": "string", - "description": "even using all possible channels, there's not enough incoming capacity to pay this invoice." - }, - "warning_offline": { - "type": "string", - "description": "there would be enough incoming capacity, but some channels are offline, so there isn't." - }, - "warning_deadends": { - "type": "string", - "description": "there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't." - }, - "warning_private_unused": { - "type": "string", - "description": "there would be enough incoming capacity, but some channels are unannounced and *exposeprivatechannels* is *false*, so there isn't." - }, - "warning_mpp": { - "type": "string", - "description": "there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments." - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "payment_hash", + "expires_at", + "bolt11", + "payment_secret" + ], + "properties": { + "bolt11": { + "type": "string", + "description": "the bolt11 string" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "payment_secret": { + "type": "hex", + "description": "the *payment_secret* to place in the onion", + "maxLength": 64, + "minLength": 64 + }, + "expires_at": { + "type": "u64", + "description": "UNIX timestamp of when invoice expires" + }, + "warning_capacity": { + "type": "string", + "description": "even using all possible channels, there's not enough incoming capacity to pay this invoice." + }, + "warning_offline": { + "type": "string", + "description": "there would be enough incoming capacity, but some channels are offline, so there isn't." + }, + "warning_deadends": { + "type": "string", + "description": "there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't." + }, + "warning_private_unused": { + "type": "string", + "description": "there would be enough incoming capacity, but some channels are unannounced and *exposeprivatechannels* is *false*, so there isn't." + }, + "warning_mpp": { + "type": "string", + "description": "there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments." } + } } diff --git a/doc/schemas/keysend.schema.json b/doc/schemas/keysend.schema.json index b3bd48ccc092..1a4f395f9fcd 100644 --- a/doc/schemas/keysend.schema.json +++ b/doc/schemas/keysend.schema.json @@ -1,55 +1,65 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "payment_preimage", "payment_hash", "created_at", "parts", "amount_msat", "amount_sent_msat", "status" ], - "properties": { - "payment_preimage": { - "type": "hex", - "description": "the proof of payment: SHA256 of this **payment_hash**", - "maxLength": 64, - "minLength": 64 - }, - "destination": { - "type": "pubkey", - "description": "the final destination of the payment" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "created_at": { - "type": "number", - "description": "the UNIX timestamp showing when this payment was initiated" - }, - "parts": { - "type": "u32", - "description": "how many attempts this took" - }, - "msatoshi": { - "deprecated": true - }, - "amount_msat": { - "type": "msat", - "description": "Amount the recipient received" - }, - "msatoshi_sent": { - "deprecated": true - }, - "amount_sent_msat": { - "type": "msat", - "description": "Total amount we sent (including fees)" - }, - "warning_partial_completion": { - "type": "string", - "description": "Not all parts of a multi-part payment have completed" - }, - "status": { - "type": "string", - "enum": [ "complete" ], - "description": "status of payment" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "payment_preimage", + "payment_hash", + "created_at", + "parts", + "amount_msat", + "amount_sent_msat", + "status" + ], + "properties": { + "payment_preimage": { + "type": "hex", + "description": "the proof of payment: SHA256 of this **payment_hash**", + "maxLength": 64, + "minLength": 64 + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "created_at": { + "type": "number", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "parts": { + "type": "u32", + "description": "how many attempts this took" + }, + "msatoshi": { + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "Amount the recipient received" + }, + "msatoshi_sent": { + "deprecated": true + }, + "amount_sent_msat": { + "type": "msat", + "description": "Total amount we sent (including fees)" + }, + "warning_partial_completion": { + "type": "string", + "description": "Not all parts of a multi-part payment have completed" + }, + "status": { + "type": "string", + "enum": [ + "complete" + ], + "description": "status of payment" } + } } diff --git a/doc/schemas/listchannels.schema.json b/doc/schemas/listchannels.schema.json index bd1ab36b764c..a187e6196394 100644 --- a/doc/schemas/listchannels.schema.json +++ b/doc/schemas/listchannels.schema.json @@ -1,77 +1,94 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "channels" ], - "properties": { - "channels": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "source", "destination", "short_channel_id", "public", "amount_msat", "message_flags", "channel_flags", "active", "last_update", "base_fee_millisatoshi", "fee_per_millionth", "delay", "htlc_minimum_msat", "features" ], - "properties": { - "source": { - "type": "pubkey", - "description": "the source node" - }, - "destination": { - "type": "pubkey", - "description": "the destination node" - }, - "public": { - "type": "boolean", - "description": "true if this is announced (otherwise it must be our channel)" - }, - "amount_msat": { - "type": "msat", - "description": "the total capacity of this channel (always a whole number of satoshis)" - }, - "message_flags": { - "type": "u8", - "description": "as defined by BOLT #7" - }, - "channel_flags": { - "type": "u8", - "description": "as defined by BOLT #7" - }, - "active": { - "type": "boolean", - "description": "true unless source has disabled it, or it's a local channel and the peer is disconnected or it's still opening or closing" - }, - "last_update": { - "type": "u32", - "description": "UNIX timestamp on the last channel_update from *source*" - }, - "base_fee_millisatoshi": { - "type": "u32", - "description": "Base fee changed by *source* to use this channel" - }, - "fee_per_millionth": { - "type": "u32", - "description": "Proportional fee changed by *source* to use this channel, in parts-per-million" - }, - "delay": { - "type": "u32", - "description": "The number of blocks delay required by *source* to use this channel" - }, - "htlc_minimum_msat": { - "type": "msat", - "description": "The smallest payment *source* will allow via this channel" - }, - "satoshis": { - "deprecated": true - }, - "htlc_maximum_msat": { - "type": "msat", - "description": "The largest payment *source* will allow via this channel" - }, - "features": { - "type": "hex", - "description": "BOLT #9 features bitmap for this channel" - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channels" + ], + "properties": { + "channels": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "source", + "destination", + "short_channel_id", + "public", + "amount_msat", + "message_flags", + "channel_flags", + "active", + "last_update", + "base_fee_millisatoshi", + "fee_per_millionth", + "delay", + "htlc_minimum_msat", + "features" + ], + "properties": { + "source": { + "type": "pubkey", + "description": "the source node" + }, + "destination": { + "type": "pubkey", + "description": "the destination node" + }, + "public": { + "type": "boolean", + "description": "true if this is announced (otherwise it must be our channel)" + }, + "amount_msat": { + "type": "msat", + "description": "the total capacity of this channel (always a whole number of satoshis)" + }, + "message_flags": { + "type": "u8", + "description": "as defined by BOLT #7" + }, + "channel_flags": { + "type": "u8", + "description": "as defined by BOLT #7" + }, + "active": { + "type": "boolean", + "description": "true unless source has disabled it, or it's a local channel and the peer is disconnected or it's still opening or closing" + }, + "last_update": { + "type": "u32", + "description": "UNIX timestamp on the last channel_update from *source*" + }, + "base_fee_millisatoshi": { + "type": "u32", + "description": "Base fee changed by *source* to use this channel" + }, + "fee_per_millionth": { + "type": "u32", + "description": "Proportional fee changed by *source* to use this channel, in parts-per-million" + }, + "delay": { + "type": "u32", + "description": "The number of blocks delay required by *source* to use this channel" + }, + "htlc_minimum_msat": { + "type": "msat", + "description": "The smallest payment *source* will allow via this channel" + }, + "satoshis": { + "deprecated": true + }, + "htlc_maximum_msat": { + "type": "msat", + "description": "The largest payment *source* will allow via this channel" + }, + "features": { + "type": "hex", + "description": "BOLT #9 features bitmap for this channel" + } + } + } } + } } diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index 1ecdb0c3585e..ac041bf8dea8 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -1,263 +1,267 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ ], - "properties": { - "# version": { - "type": "string", - "description": "Special field indicating the current version" - }, - "plugins": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "path", "name" ], - "description": "`plugin` field from config or cmdline", - "properties": { - "path": { - "type": "string", - "description": "Full path of the plugin" - }, - "name": { - "type": "string", - "description": "short name of the plugin" - }, - "options": { - "type": "object", - "additionalProperties": true, - "required": [ ], - "description": "Specific options set for this plugin", - "properties": { - } - } - } - } - }, - "important-plugins": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "path", "name" ], - "description": "`important-plugin` field from config or cmdline, or built-in", - "properties": { - "path": { - "type": "string", - "description": "Full path of the plugin" - }, - "name": { - "type": "string", - "description": "short name of the plugin" - }, - "options": { - "type": "object", - "additionalProperties": true, - "required": [ ], - "description": "Specific options set for this plugin", - "properties": { - } - } - } - } - }, - "conf": { - "type": "string", - "description": "`conf` field from cmdline, or default" - }, - "lightning-dir": { - "type": "string", - "description": "`lightning-dir` field from config or cmdline, or default" - }, - "network": { - "type": "string", - "description": "`network` field from config or cmdline, or default" - }, - "allow-deprecated-apis": { - "type": "boolean", - "description": "`allow-deprecated-apis` field from config or cmdline, or default" - }, - "rpc-file": { - "type": "string", - "description": "`rpc-file` field from config or cmdline, or default" - }, - "disable-plugin": { - "type": "array", - "items": { - "type": "string", - "description": "`disable-plugin` field from config or cmdline" - } - }, - "always-use-proxy": { - "type": "boolean", - "description": "`always-use-proxy` field from config or cmdline, or default" - }, - "daemon": { - "type": "boolean", - "description": "`daemon` field from config or cmdline, or default" - }, - "wallet": { - "type": "string", - "description": "`wallet` field from config or cmdline, or default" - }, - "large-channels": { - "type": "boolean", - "description": "`large-channels` field from config or cmdline, or default" - }, - "experimental-dual-fund": { - "type": "boolean", - "description": "`experimental-dual-fund` field from config or cmdline, or default" - }, - "experimental-onion-messages": { - "type": "boolean", - "description": "`experimental-onion-messages` field from config or cmdline, or default" - }, - "experimental-offers": { - "type": "boolean", - "description": "`experimental-offers` field from config or cmdline, or default" - }, - "experimental-shutdown-wrong-funding": { - "type": "boolean", - "description": "`experimental-shutdown-wrong-funding` field from config or cmdline, or default" - }, - "experimental-websocket-port": { - "type": "u16", - "description": "`experimental-websocket-port` field from config or cmdline, or default" - }, - "rgb": { - "type": "hex", - "description": "`rgb` field from config or cmdline, or default", - "maxLength": 6, - "minLength": 6 - }, - "alias": { - "type": "string", - "description": "`alias` field from config or cmdline, or default" - }, - "pid-file": { - "type": "string", - "description": "`pid-file` field from config or cmdline, or default" - }, - "ignore-fee-limits": { - "type": "boolean", - "description": "`ignore-fee-limits` field from config or cmdline, or default" - }, - "watchtime-blocks": { - "type": "u32", - "description": "`watchtime-blocks` field from config or cmdline, or default" - }, - "max-locktime-blocks": { - "type": "u32", - "description": "`max-locktime-blocks` field from config or cmdline, or default" - }, - "funding-confirms": { - "type": "u32", - "description": "`funding-confirms` field from config or cmdline, or default" - }, - "cltv-delta": { - "type": "u32", - "description": "`cltv-delta` field from config or cmdline, or default" - }, - "cltv-final": { - "type": "u32", - "description": "`cltv-final` field from config or cmdline, or default" - }, - "commit-time": { - "type": "u32", - "description": "`commit-time` field from config or cmdline, or default" - }, - "fee-base": { - "type": "u32", - "description": "`fee-base` field from config or cmdline, or default" - }, - "rescan": { - "type": "integer", - "description": "`rescan` field from config or cmdline, or default" - }, - "fee-per-satoshi": { - "type": "u32", - "description": "`fee-per-satoshi` field from config or cmdline, or default" - }, - "max-concurrent-htlcs": { - "type": "u32", - "description": "`max-concurrent-htlcs` field from config or cmdline, or default" - }, - "max-dust-htlc-exposure-msat": { - "type": "msat", - "description": "`max-dust-htlc-exposure-mast` field from config or cmdline, or default" - }, - "min-capacity-sat": { - "type": "u64", - "description": "`min-capacity-sat` field from config or cmdline, or default" - }, - "addr": { - "type": "string", - "description": "`addr` field from config or cmdline (can be more than one)" - }, - "announce-addr": { - "type": "string", - "description": "`announce-addr` field from config or cmdline (can be more than one)" - }, - "bind-addr": { - "type": "string", - "description": "`bind-addr` field from config or cmdline (can be more than one)" - }, - "offline": { - "type": "boolean", - "description": "`true` if `offline` was set in config or cmdline" - }, - "autolisten": { - "type": "boolean", - "description": "`autolisten` field from config or cmdline, or default" - }, - "proxy": { - "type": "string", - "description": "`proxy` field from config or cmdline, or default" - }, - "disable-dns": { - "type": "boolean", - "description": "`true` if `disable-dns` was set in config or cmdline" - }, - "encrypted-hsm": { - "type": "boolean", - "description": "`true` if `encrypted-hsm` was set in config or cmdline" - }, - "rpc-file-mode": { - "type": "string", - "description": "`rpc-file-mode` field from config or cmdline, or default" - }, - "log-level": { - "type": "string", - "description": "`log-level` field from config or cmdline, or default" - }, - "log-prefix": { - "type": "string", - "description": "`log-prefix` field from config or cmdline, or default" - }, - "log-file": { - "type": "string", - "description": "`log-file` field from config or cmdline, or default" - }, - "log-timestamps": { - "type": "boolean", - "description": "`log-timestamps` field from config or cmdline, or default" - }, - "force-feerates": { - "type": "string", - "description": "force-feerate configuration setting, if any" - }, - "subdaemon": { - "type": "string", - "description": "`subdaemon` fields from config or cmdline if any (can be more than one)" - }, - "fetchinvoice-noconnect": { - "type": "boolean", - "description": "`featchinvoice-noconnect` fileds from config or cmdline, or default" - }, - "tor-service-password": { - "type": "string", - "description": "`tor-service-password` field from config or cmdline, if any" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": { + "# version": { + "type": "string", + "description": "Special field indicating the current version" + }, + "plugins": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "name" + ], + "description": "`plugin` field from config or cmdline", + "properties": { + "path": { + "type": "string", + "description": "Full path of the plugin" + }, + "name": { + "type": "string", + "description": "short name of the plugin" + }, + "options": { + "type": "object", + "additionalProperties": true, + "required": [], + "description": "Specific options set for this plugin", + "properties": {} + } + } + } + }, + "important-plugins": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "name" + ], + "description": "`important-plugin` field from config or cmdline, or built-in", + "properties": { + "path": { + "type": "string", + "description": "Full path of the plugin" + }, + "name": { + "type": "string", + "description": "short name of the plugin" + }, + "options": { + "type": "object", + "additionalProperties": true, + "required": [], + "description": "Specific options set for this plugin", + "properties": {} + } + } + } + }, + "conf": { + "type": "string", + "description": "`conf` field from cmdline, or default" + }, + "lightning-dir": { + "type": "string", + "description": "`lightning-dir` field from config or cmdline, or default" + }, + "network": { + "type": "string", + "description": "`network` field from config or cmdline, or default" + }, + "allow-deprecated-apis": { + "type": "boolean", + "description": "`allow-deprecated-apis` field from config or cmdline, or default" + }, + "rpc-file": { + "type": "string", + "description": "`rpc-file` field from config or cmdline, or default" + }, + "disable-plugin": { + "type": "array", + "items": { + "type": "string", + "description": "`disable-plugin` field from config or cmdline" + } + }, + "always-use-proxy": { + "type": "boolean", + "description": "`always-use-proxy` field from config or cmdline, or default" + }, + "daemon": { + "type": "boolean", + "description": "`daemon` field from config or cmdline, or default" + }, + "wallet": { + "type": "string", + "description": "`wallet` field from config or cmdline, or default" + }, + "large-channels": { + "type": "boolean", + "description": "`large-channels` field from config or cmdline, or default" + }, + "experimental-dual-fund": { + "type": "boolean", + "description": "`experimental-dual-fund` field from config or cmdline, or default" + }, + "experimental-onion-messages": { + "type": "boolean", + "description": "`experimental-onion-messages` field from config or cmdline, or default" + }, + "experimental-offers": { + "type": "boolean", + "description": "`experimental-offers` field from config or cmdline, or default" + }, + "experimental-shutdown-wrong-funding": { + "type": "boolean", + "description": "`experimental-shutdown-wrong-funding` field from config or cmdline, or default" + }, + "experimental-websocket-port": { + "type": "u16", + "description": "`experimental-websocket-port` field from config or cmdline, or default" + }, + "rgb": { + "type": "hex", + "description": "`rgb` field from config or cmdline, or default", + "maxLength": 6, + "minLength": 6 + }, + "alias": { + "type": "string", + "description": "`alias` field from config or cmdline, or default" + }, + "pid-file": { + "type": "string", + "description": "`pid-file` field from config or cmdline, or default" + }, + "ignore-fee-limits": { + "type": "boolean", + "description": "`ignore-fee-limits` field from config or cmdline, or default" + }, + "watchtime-blocks": { + "type": "u32", + "description": "`watchtime-blocks` field from config or cmdline, or default" + }, + "max-locktime-blocks": { + "type": "u32", + "description": "`max-locktime-blocks` field from config or cmdline, or default" + }, + "funding-confirms": { + "type": "u32", + "description": "`funding-confirms` field from config or cmdline, or default" + }, + "cltv-delta": { + "type": "u32", + "description": "`cltv-delta` field from config or cmdline, or default" + }, + "cltv-final": { + "type": "u32", + "description": "`cltv-final` field from config or cmdline, or default" + }, + "commit-time": { + "type": "u32", + "description": "`commit-time` field from config or cmdline, or default" + }, + "fee-base": { + "type": "u32", + "description": "`fee-base` field from config or cmdline, or default" + }, + "rescan": { + "type": "integer", + "description": "`rescan` field from config or cmdline, or default" + }, + "fee-per-satoshi": { + "type": "u32", + "description": "`fee-per-satoshi` field from config or cmdline, or default" + }, + "max-concurrent-htlcs": { + "type": "u32", + "description": "`max-concurrent-htlcs` field from config or cmdline, or default" + }, + "max-dust-htlc-exposure-msat": { + "type": "msat", + "description": "`max-dust-htlc-exposure-mast` field from config or cmdline, or default" + }, + "min-capacity-sat": { + "type": "u64", + "description": "`min-capacity-sat` field from config or cmdline, or default" + }, + "addr": { + "type": "string", + "description": "`addr` field from config or cmdline (can be more than one)" + }, + "announce-addr": { + "type": "string", + "description": "`announce-addr` field from config or cmdline (can be more than one)" + }, + "bind-addr": { + "type": "string", + "description": "`bind-addr` field from config or cmdline (can be more than one)" + }, + "offline": { + "type": "boolean", + "description": "`true` if `offline` was set in config or cmdline" + }, + "autolisten": { + "type": "boolean", + "description": "`autolisten` field from config or cmdline, or default" + }, + "proxy": { + "type": "string", + "description": "`proxy` field from config or cmdline, or default" + }, + "disable-dns": { + "type": "boolean", + "description": "`true` if `disable-dns` was set in config or cmdline" + }, + "encrypted-hsm": { + "type": "boolean", + "description": "`true` if `encrypted-hsm` was set in config or cmdline" + }, + "rpc-file-mode": { + "type": "string", + "description": "`rpc-file-mode` field from config or cmdline, or default" + }, + "log-level": { + "type": "string", + "description": "`log-level` field from config or cmdline, or default" + }, + "log-prefix": { + "type": "string", + "description": "`log-prefix` field from config or cmdline, or default" + }, + "log-file": { + "type": "string", + "description": "`log-file` field from config or cmdline, or default" + }, + "log-timestamps": { + "type": "boolean", + "description": "`log-timestamps` field from config or cmdline, or default" + }, + "force-feerates": { + "type": "string", + "description": "force-feerate configuration setting, if any" + }, + "subdaemon": { + "type": "string", + "description": "`subdaemon` fields from config or cmdline if any (can be more than one)" + }, + "fetchinvoice-noconnect": { + "type": "boolean", + "description": "`featchinvoice-noconnect` fileds from config or cmdline, or default" + }, + "tor-service-password": { + "type": "string", + "description": "`tor-service-password` field from config or cmdline, if any" } + } } diff --git a/doc/schemas/listdatastore.schema.json b/doc/schemas/listdatastore.schema.json index 0ce336f04c21..a07f793de3b4 100644 --- a/doc/schemas/listdatastore.schema.json +++ b/doc/schemas/listdatastore.schema.json @@ -1,37 +1,41 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "datastore" ], - "properties": { - "datastore": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "key" ], - "properties": { - "key": { - "type": "array", - "items": { - "type": "string", - "description": "Part of the key added to the datastore" - } - }, - "generation": { - "type": "u64", - "description": "The number of times this has been updated" - }, - "hex": { - "type": "hex", - "description": "The hex data from the datastore" - }, - "string": { - "type": "string", - "description": "The data as a string, if it's valid utf-8" - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "datastore" + ], + "properties": { + "datastore": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "key" + ], + "properties": { + "key": { + "type": "array", + "items": { + "type": "string", + "description": "Part of the key added to the datastore" + } + }, + "generation": { + "type": "u64", + "description": "The number of times this has been updated" + }, + "hex": { + "type": "hex", + "description": "The hex data from the datastore" + }, + "string": { + "type": "string", + "description": "The data as a string, if it's valid utf-8" + } + } + } } + } } diff --git a/doc/schemas/listforwards.schema.json b/doc/schemas/listforwards.schema.json index f52b10ee05d1..1f311a5d6cb6 100644 --- a/doc/schemas/listforwards.schema.json +++ b/doc/schemas/listforwards.schema.json @@ -1,205 +1,230 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "forwards" ], - "properties": { - "forwards": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "in_channel", "in_msat", "status", "received_time" ], - "properties": { - "in_channel": { - "type": "short_channel_id", - "description": "the channel that received the HTLC" - }, - "in_msatoshi": { - "deprecated": true - }, - "in_msat": { - "type": "msat", - "description": "the value of the incoming HTLC" - }, - "status": { - "type": "string", - "enum": [ "offered", "settled", "local_failed", "failed" ], - "description": "still ongoing, completed, failed locally, or failed after forwarding" - }, - "received_time": { - "type": "number", - "description": "the UNIX timestamp when this was received" - }, - "out_channel": { - "type": "short_channel_id", - "description": "the channel that the HTLC was forwarded to" - }, - "payment_hash": { - "type": "hex", - "description": "payment hash sought by HTLC", - "maxLength": 64, - "minLength": 64 - } - }, - "allOf": [ - { - "if": { - "required": [ "out_channel" ] - }, - "then": { - "additionalProperties": false, - "required": [ "fee_msat", "out_msat" ], - "properties": { - "in_channel": { }, - "in_msatoshi": { }, - "in_msat": { }, - "status": { }, - "received_time": { }, - "resolved_time": { }, - "out_channel": { }, - "payment_hash": { }, - "failcode": { }, - "failreason": { }, - "fee": { - "deprecated": true - }, - "fee_msat": { - "type": "msat", - "description": "the amount this paid in fees" - }, - "out_msatoshi": { - "deprecated": true - }, - "out_msat": { - "type": "msat", - "description": "the amount we sent out the *out_channel*" - } - } - }, - "else": { - "additionalProperties": false, - "required": [ ], - "properties": { - "in_channel": { }, - "in_msatoshi": { }, - "in_msat": { }, - "status": { }, - "received_time": { }, - "resolved_time": { }, - "payment_hash": { }, - "failcode": { }, - "failreason": { } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "settled", "failed" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "resolved_time" ], - "properties": { - "in_channel": { }, - "in_msatoshi": { }, - "in_msat": { }, - "status": { }, - "received_time": { }, - "out_channel": { }, - "payment_hash": { }, - "fee": { }, - "fee_msat": { }, - "out_msatoshi": { }, - "out_msat": { }, - "failcode": { }, - "failreason": { }, - "resolved_time": { - "type": "number", - "description": "the UNIX timestamp when this was resolved" - } - } - }, - "else": { - "additionalProperties": false, - "properties": { - "in_channel": { }, - "in_msatoshi": { }, - "in_msat": { }, - "status": { }, - "received_time": { }, - "out_channel": { }, - "payment_hash": { }, - "fee": { }, - "fee_msat": { }, - "failcode": { }, - "failreason": { }, - "out_msatoshi": { }, - "out_msat": { } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "local_failed", "failed" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ ], - "properties": { - "in_channel": { }, - "in_msatoshi": { }, - "in_msat": { }, - "status": { }, - "received_time": { }, - "out_channel": { }, - "payment_hash": { }, - "fee": { }, - "fee_msat": { }, - "out_msatoshi": { }, - "out_msat": { }, - "resolved_time": { }, - "failcode": { - "type": "u32", - "description": "the numeric onion code returned" - }, - "failreason": { - "type": "string", - "description": "the name of the onion code returned" - } - } - }, - "else": { - "additionalProperties": false, - "required": [ ], - "properties": { - "in_channel": { }, - "in_msatoshi": { }, - "in_msat": { }, - "status": { }, - "received_time": { }, - "out_channel": { }, - "payment_hash": { }, - "fee": { }, - "fee_msat": { }, - "out_msatoshi": { }, - "out_msat": { }, - "resolved_time": { } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "forwards" + ], + "properties": { + "forwards": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "in_channel", + "in_msat", + "status", + "received_time" + ], + "properties": { + "in_channel": { + "type": "short_channel_id", + "description": "the channel that received the HTLC" + }, + "in_msatoshi": { + "deprecated": true + }, + "in_msat": { + "type": "msat", + "description": "the value of the incoming HTLC" + }, + "status": { + "type": "string", + "enum": [ + "offered", + "settled", + "local_failed", + "failed" + ], + "description": "still ongoing, completed, failed locally, or failed after forwarding" + }, + "received_time": { + "type": "number", + "description": "the UNIX timestamp when this was received" + }, + "out_channel": { + "type": "short_channel_id", + "description": "the channel that the HTLC was forwarded to" + }, + "payment_hash": { + "type": "hex", + "description": "payment hash sought by HTLC", + "maxLength": 64, + "minLength": 64 + } + }, + "allOf": [ + { + "if": { + "required": [ + "out_channel" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "fee_msat", + "out_msat" + ], + "properties": { + "in_channel": {}, + "in_msatoshi": {}, + "in_msat": {}, + "status": {}, + "received_time": {}, + "resolved_time": {}, + "out_channel": {}, + "payment_hash": {}, + "failcode": {}, + "failreason": {}, + "fee": { + "deprecated": true + }, + "fee_msat": { + "type": "msat", + "description": "the amount this paid in fees" + }, + "out_msatoshi": { + "deprecated": true + }, + "out_msat": { + "type": "msat", + "description": "the amount we sent out the *out_channel*" + } + } + }, + "else": { + "additionalProperties": false, + "required": [], + "properties": { + "in_channel": {}, + "in_msatoshi": {}, + "in_msat": {}, + "status": {}, + "received_time": {}, + "resolved_time": {}, + "payment_hash": {}, + "failcode": {}, + "failreason": {} + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "settled", + "failed" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "resolved_time" + ], + "properties": { + "in_channel": {}, + "in_msatoshi": {}, + "in_msat": {}, + "status": {}, + "received_time": {}, + "out_channel": {}, + "payment_hash": {}, + "fee": {}, + "fee_msat": {}, + "out_msatoshi": {}, + "out_msat": {}, + "failcode": {}, + "failreason": {}, + "resolved_time": { + "type": "number", + "description": "the UNIX timestamp when this was resolved" + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "in_channel": {}, + "in_msatoshi": {}, + "in_msat": {}, + "status": {}, + "received_time": {}, + "out_channel": {}, + "payment_hash": {}, + "fee": {}, + "fee_msat": {}, + "failcode": {}, + "failreason": {}, + "out_msatoshi": {}, + "out_msat": {} + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "local_failed", + "failed" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "in_channel": {}, + "in_msatoshi": {}, + "in_msat": {}, + "status": {}, + "received_time": {}, + "out_channel": {}, + "payment_hash": {}, + "fee": {}, + "fee_msat": {}, + "out_msatoshi": {}, + "out_msat": {}, + "resolved_time": {}, + "failcode": { + "type": "u32", + "description": "the numeric onion code returned" + }, + "failreason": { + "type": "string", + "description": "the name of the onion code returned" + } + } + }, + "else": { + "additionalProperties": false, + "required": [], + "properties": { + "in_channel": {}, + "in_msatoshi": {}, + "in_msat": {}, + "status": {}, + "received_time": {}, + "out_channel": {}, + "payment_hash": {}, + "fee": {}, + "fee_msat": {}, + "out_msatoshi": {}, + "out_msat": {}, + "resolved_time": {} + } + } + } + ] + } } + } } diff --git a/doc/schemas/listfunds.schema.json b/doc/schemas/listfunds.schema.json index 84172c855c95..a5d5b875129a 100644 --- a/doc/schemas/listfunds.schema.json +++ b/doc/schemas/listfunds.schema.json @@ -1,217 +1,270 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "outputs", "channels" ], - "properties": { - "outputs": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "txid", "output", "amount_msat", "scriptpubkey", "status", "reserved" ], - "properties": { - "txid": { - "type": "txid", - "description": "the ID of the spendable transaction" - }, - "output": { - "type": "u32", - "description": "the index within *txid*" - }, - "amount_msat": { - "type": "msat", - "description": "the amount of the output" - }, - "value": { - "type": "u64", - "deprecated": true - }, - "scriptpubkey": { - "type": "hex", - "description": "the scriptPubkey of the output" - }, - "address": { - "type": "string", - "description": "the bitcoin address of the output" - }, - "redeemscript": { - "type": "hex", - "description": "the redeemscript, only if it's p2sh-wrapped" - }, - "status": { - "type": "string", - "enum": [ "unconfirmed", "confirmed", "spent" ] - } - }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "confirmed" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "blockheight" ], - "properties": { - "txid": { }, - "output": { }, - "amount_msat": { }, - "scriptpubkey": { }, - "address": { }, - "value": { }, - "redeemscript": { }, - "status": { }, - "reserved": { }, - "reserved_to_block": { }, - "blockheight": { - "type": "u32", - "description": "Block height where it was confirmed" - } - } - } - }, - { - "if": { - "properties": { - "reserved": { - "type": "boolean", - "enum": [ "true" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "reserved_to_block" ], - "properties": { - "txid": { }, - "output": { }, - "amount_msat": { }, - "scriptpubkey": { }, - "address": { }, - "value": { }, - "redeemscript": { }, - "status": { }, - "blockheight": { }, - "reserved": { }, - "reserved_to_block": { - "type": "u32", - "description": "Block height where reservation will expire" - } - } - } - } - ] - } - }, - "channels": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "peer_id", "our_amount_msat", "amount_msat", "funding_txid", "funding_output", "connected", "state" ], - "properties": { - "peer_id": { - "type": "pubkey", - "description": "the peer with which the channel is opened" - }, - "our_amount_msat": { - "type": "msat", - "description": "available satoshis on our node’s end of the channel" - }, - "channel_sat": { - "deprecated": true - }, - "amount_msat": { - "type": "msat", - "description": "total channel value" - }, - "channel_total_sat": { - "deprecated": true - }, - "funding_txid": { - "type": "txid", - "description": "funding transaction id" - }, - "funding_output": { - "type": "u32", - "description": "the 0-based index of the output in the funding transaction" - }, - "connected": { - "type": "boolean", - "description": "whether the channel peer is connected" - }, - "state": { - "type": "string", - "enum": [ "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN" ], - "description": "the channel state, in particular \"CHANNELD_NORMAL\" means the channel can be used normally" - } - }, - "allOf": [ - { - "if": { - "properties": { - "state": { - "type": "string", - "enum": [ "CHANNELD_NORMAL" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "short_channel_id" ], - "properties": { - "peer_id": { }, - "our_amount_msat": { }, - "channel_sat": { }, - "amount_msat": { }, - "channel_total_sat": { }, - "funding_txid": { }, - "funding_output": { }, - "connected": { }, - "state": { }, - "short_channel_id": { - "type": "short_channel_id", - "description": "short channel id of channel" - } - } - } - }, - { - "if": { - "properties": { - "state": { - "type": "string", - "enum": [ "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ ], - "properties": { - "peer_id": { }, - "our_amount_msat": { }, - "channel_sat": { }, - "amount_msat": { }, - "channel_total_sat": { }, - "funding_txid": { }, - "funding_output": { }, - "connected": { }, - "state": { }, - "short_channel_id": { - "type": "short_channel_id", - "description": "short channel id of channel (only if funding reached lockin depth before closing)" - } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "outputs", + "channels" + ], + "properties": { + "outputs": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "txid", + "output", + "amount_msat", + "scriptpubkey", + "status", + "reserved" + ], + "properties": { + "txid": { + "type": "txid", + "description": "the ID of the spendable transaction" + }, + "output": { + "type": "u32", + "description": "the index within *txid*" + }, + "amount_msat": { + "type": "msat", + "description": "the amount of the output" + }, + "value": { + "type": "u64", + "deprecated": true + }, + "scriptpubkey": { + "type": "hex", + "description": "the scriptPubkey of the output" + }, + "address": { + "type": "string", + "description": "the bitcoin address of the output" + }, + "redeemscript": { + "type": "hex", + "description": "the redeemscript, only if it's p2sh-wrapped" + }, + "status": { + "type": "string", + "enum": [ + "unconfirmed", + "confirmed", + "spent" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "confirmed" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "blockheight" + ], + "properties": { + "txid": {}, + "output": {}, + "amount_msat": {}, + "scriptpubkey": {}, + "address": {}, + "value": {}, + "redeemscript": {}, + "status": {}, + "reserved": {}, + "reserved_to_block": {}, + "blockheight": { + "type": "u32", + "description": "Block height where it was confirmed" + } + } + } + }, + { + "if": { + "properties": { + "reserved": { + "type": "boolean", + "enum": [ + "true" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "reserved_to_block" + ], + "properties": { + "txid": {}, + "output": {}, + "amount_msat": {}, + "scriptpubkey": {}, + "address": {}, + "value": {}, + "redeemscript": {}, + "status": {}, + "blockheight": {}, + "reserved": {}, + "reserved_to_block": { + "type": "u32", + "description": "Block height where reservation will expire" + } + } + } + } + ] + } + }, + "channels": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "peer_id", + "our_amount_msat", + "amount_msat", + "funding_txid", + "funding_output", + "connected", + "state" + ], + "properties": { + "peer_id": { + "type": "pubkey", + "description": "the peer with which the channel is opened" + }, + "our_amount_msat": { + "type": "msat", + "description": "available satoshis on our node’s end of the channel" + }, + "channel_sat": { + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "total channel value" + }, + "channel_total_sat": { + "deprecated": true + }, + "funding_txid": { + "type": "txid", + "description": "funding transaction id" + }, + "funding_output": { + "type": "u32", + "description": "the 0-based index of the output in the funding transaction" + }, + "connected": { + "type": "boolean", + "description": "whether the channel peer is connected" + }, + "state": { + "type": "string", + "enum": [ + "OPENINGD", + "CHANNELD_AWAITING_LOCKIN", + "CHANNELD_NORMAL", + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN", + "DUALOPEND_OPEN_INIT", + "DUALOPEND_AWAITING_LOCKIN" + ], + "description": "the channel state, in particular \"CHANNELD_NORMAL\" means the channel can be used normally" + } + }, + "allOf": [ + { + "if": { + "properties": { + "state": { + "type": "string", + "enum": [ + "CHANNELD_NORMAL" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "short_channel_id" + ], + "properties": { + "peer_id": {}, + "our_amount_msat": {}, + "channel_sat": {}, + "amount_msat": {}, + "channel_total_sat": {}, + "funding_txid": {}, + "funding_output": {}, + "connected": {}, + "state": {}, + "short_channel_id": { + "type": "short_channel_id", + "description": "short channel id of channel" + } + } + } + }, + { + "if": { + "properties": { + "state": { + "type": "string", + "enum": [ + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "peer_id": {}, + "our_amount_msat": {}, + "channel_sat": {}, + "amount_msat": {}, + "channel_total_sat": {}, + "funding_txid": {}, + "funding_output": {}, + "connected": {}, + "state": {}, + "short_channel_id": { + "type": "short_channel_id", + "description": "short channel id of channel (only if funding reached lockin depth before closing)" + } + } + } + } + ] + } } + } } diff --git a/doc/schemas/listinvoices.schema.json b/doc/schemas/listinvoices.schema.json index 0de5af67f69c..bbd505684058 100644 --- a/doc/schemas/listinvoices.schema.json +++ b/doc/schemas/listinvoices.schema.json @@ -1,132 +1,151 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "invoices" ], - "properties": { - "invoices": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "label", "description", "payment_hash", "status", "expires_at" ], - "properties": { - "label": { - "type": "string", - "description": "unique label supplied at invoice creation" - }, - "description": { - "type": "string", - "description": "description used in the invoice" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "status": { - "type": "string", - "enum": [ "unpaid", "paid", "expired" ], - "description": "Whether it's paid, unpaid or unpayable" - }, - "expires_at": { - "type": "u64", - "description": "UNIX timestamp of when it will become / became unpayable" - }, - "msatoshi": { - "deprecated": "true" - }, - "amount_msat": { - "type": "msat", - "description": "the amount required to pay this invoice" - }, - "bolt11": { - "type": "string", - "description": "the BOLT11 string (always present unless *bolt12* is)" - }, - "bolt12": { - "type": "string", - "description": "the BOLT12 string (always present unless *bolt11* is)" - }, - "local_offer_id": { - "type": "hex", - "description": "the *id* of our offer which created this invoice (**experimental-offers** only).", - "maxLength": 64, - "minLength": 64 - }, - "payer_note": { - "type": "string", - "description": "the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only)." - } - }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "paid" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "pay_index", "amount_received_msat", "paid_at", "payment_preimage" ], - "properties": { - "label": { }, - "description": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "bolt11": { }, - "bolt12": { }, - "local_offer_id": { }, - "payer_note": { }, - "expires_at": { }, - "pay_index": { - "type": "u64", - "description": "Unique incrementing index for this payment" - }, - "msatoshi_received": { - "deprecated": true - }, - "amount_received_msat": { - "type": "msat", - "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" - }, - "paid_at": { - "type": "u64", - "description": "UNIX timestamp of when it was paid" - }, - "payment_preimage": { - "type": "hex", - "description": "proof of payment", - "maxLength": 64, - "minLength": 64 - } - } - }, - "else": { - "additionalProperties": false, - "properties": { - "label": { }, - "description": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "bolt11": { }, - "bolt12": { }, - "local_offer_id": { }, - "payer_note": { }, - "expires_at": { } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "invoices" + ], + "properties": { + "invoices": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "label", + "description", + "payment_hash", + "status", + "expires_at" + ], + "properties": { + "label": { + "type": "string", + "description": "unique label supplied at invoice creation" + }, + "description": { + "type": "string", + "description": "description used in the invoice" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ + "unpaid", + "paid", + "expired" + ], + "description": "Whether it's paid, unpaid or unpayable" + }, + "expires_at": { + "type": "u64", + "description": "UNIX timestamp of when it will become / became unpayable" + }, + "msatoshi": { + "deprecated": "true" + }, + "amount_msat": { + "type": "msat", + "description": "the amount required to pay this invoice" + }, + "bolt11": { + "type": "string", + "description": "the BOLT11 string (always present unless *bolt12* is)" + }, + "bolt12": { + "type": "string", + "description": "the BOLT12 string (always present unless *bolt11* is)" + }, + "local_offer_id": { + "type": "hex", + "description": "the *id* of our offer which created this invoice (**experimental-offers** only).", + "maxLength": 64, + "minLength": 64 + }, + "payer_note": { + "type": "string", + "description": "the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only)." + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "paid" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "pay_index", + "amount_received_msat", + "paid_at", + "payment_preimage" + ], + "properties": { + "label": {}, + "description": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "bolt11": {}, + "bolt12": {}, + "local_offer_id": {}, + "payer_note": {}, + "expires_at": {}, + "pay_index": { + "type": "u64", + "description": "Unique incrementing index for this payment" + }, + "msatoshi_received": { + "deprecated": true + }, + "amount_received_msat": { + "type": "msat", + "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" + }, + "paid_at": { + "type": "u64", + "description": "UNIX timestamp of when it was paid" + }, + "payment_preimage": { + "type": "hex", + "description": "proof of payment", + "maxLength": 64, + "minLength": 64 + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "label": {}, + "description": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "bolt11": {}, + "bolt12": {}, + "local_offer_id": {}, + "payer_note": {}, + "expires_at": {} + } + } + } + ] + } } + } } diff --git a/doc/schemas/listnodes.schema.json b/doc/schemas/listnodes.schema.json index 4437bfbc8777..d88250f663bd 100644 --- a/doc/schemas/listnodes.schema.json +++ b/doc/schemas/listnodes.schema.json @@ -1,158 +1,198 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "nodes" ], - "properties": { - "nodes": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "nodeid" ], - "properties": { - "nodeid": { - "type": "pubkey", - "description": "the public key of the node" - }, - "last_timestamp": { - "type": "u32", - "description": "A node_announcement has been received for this node (UNIX timestamp)" - } - }, - "allOf": [ - { - "if": { - "required": [ "last_timestamp" ] - }, - "then": { - "additionalProperties": false, - "required": [ "nodeid", "last_timestamp", "alias", "color", "features", "addresses" ], - "properties": { - "nodeid": { }, - "last_timestamp": { }, - "option_will_fund": { }, - "alias": { - "type": "string", - "description": "The fun alias this node advertized", - "maxLength": 32 - }, - "color": { - "type": "hex", - "description": "The favorite RGB color this node advertized", - "minLength": 6, - "maxLength": 6 - }, - "features": { - "type": "hex", - "description": "BOLT #9 features bitmap this node advertized" - }, - "addresses": { - "type": "array", - "description": "The addresses this node advertized", - "items": { - "type": "object", - "required": [ "type", "port" ], - "additionalProperties": true, - "properties": { - "type": { - "type": "string", - "enum": [ "ipv4", "ipv6", "torv2", "torv3", "websocket" ], - "description": "Type of connection" - }, - "port": { - "type": "u16", - "description": "port number" - } - }, - "if": { - "properties": { - "type": { - "type": "string", - "enum": [ "ipv4", "ipv6", "torv2", "torv3" ] - } - } - }, - "then": { - "required": [ "type", "address", "port" ], - "additionalProperties": false, - "properties": { - "type": { }, - "port": { }, - "address": { - "type": "string", - "description": "address in expected format for **type**" - } - } - }, - "else": { - "required": [ "type", "port" ], - "additionalProperties": false, - "properties": { - "type": { }, - "port": { } - } - } - } - } - } - }, - "else": { - "additionalProperties": false, - "properties": { - "nodeid": { } - } - } - }, - { - "if": { - "required": [ "option_will_fund" ] - }, - "then": { - "additionalProperties": true, - "required": [ "option_will_fund" ], - "properties": { - "option_will_fund": { - "type": "object", - "additionalProperties": false, - "required": [ "lease_fee_base_msat", - "lease_fee_basis", - "funding_weight", - "channel_fee_max_base_msat", - "channel_fee_max_proportional_thousandths", - "compact_lease" ], - "properties": { - "lease_fee_base_msat": { - "type": "msat", - "description": "the fixed fee for a lease (whole number of satoshis)" - }, - "lease_fee_basis": { - "type": "u32", - "description": "the proportional fee in basis points (parts per 10,000) for a lease" - }, - "funding_weight": { - "type": "u32", - "description": "the onchain weight you'll have to pay for a lease" - }, - "channel_fee_max_base_msat": { - "type": "msat", - "description": "the maximum base routing fee this node will charge during the lease" - }, - "channel_fee_max_proportional_thousandths": { - "type": "u32", - "description": "the maximum proportional routing fee this node will charge during the lease (in thousandths, not millionths like channel_update)" - }, - "compact_lease": { - "type": "hex", - "description": "the lease as represented in the node_announcement" - } - } - } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "nodes" + ], + "properties": { + "nodes": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "nodeid" + ], + "properties": { + "nodeid": { + "type": "pubkey", + "description": "the public key of the node" + }, + "last_timestamp": { + "type": "u32", + "description": "A node_announcement has been received for this node (UNIX timestamp)" + } + }, + "allOf": [ + { + "if": { + "required": [ + "last_timestamp" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "nodeid", + "last_timestamp", + "alias", + "color", + "features", + "addresses" + ], + "properties": { + "nodeid": {}, + "last_timestamp": {}, + "option_will_fund": {}, + "alias": { + "type": "string", + "description": "The fun alias this node advertized", + "maxLength": 32 + }, + "color": { + "type": "hex", + "description": "The favorite RGB color this node advertized", + "minLength": 6, + "maxLength": 6 + }, + "features": { + "type": "hex", + "description": "BOLT #9 features bitmap this node advertized" + }, + "addresses": { + "type": "array", + "description": "The addresses this node advertized", + "items": { + "type": "object", + "required": [ + "type", + "port" + ], + "additionalProperties": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "ipv4", + "ipv6", + "torv2", + "torv3", + "websocket" + ], + "description": "Type of connection" + }, + "port": { + "type": "u16", + "description": "port number" + } + }, + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "ipv4", + "ipv6", + "torv2", + "torv3" + ] + } + } + }, + "then": { + "required": [ + "type", + "address", + "port" + ], + "additionalProperties": false, + "properties": { + "type": {}, + "port": {}, + "address": { + "type": "string", + "description": "address in expected format for **type**" + } + } + }, + "else": { + "required": [ + "type", + "port" + ], + "additionalProperties": false, + "properties": { + "type": {}, + "port": {} + } + } + } + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "nodeid": {} + } + } + }, + { + "if": { + "required": [ + "option_will_fund" + ] + }, + "then": { + "additionalProperties": true, + "required": [ + "option_will_fund" + ], + "properties": { + "option_will_fund": { + "type": "object", + "additionalProperties": false, + "required": [ + "lease_fee_base_msat", + "lease_fee_basis", + "funding_weight", + "channel_fee_max_base_msat", + "channel_fee_max_proportional_thousandths", + "compact_lease" + ], + "properties": { + "lease_fee_base_msat": { + "type": "msat", + "description": "the fixed fee for a lease (whole number of satoshis)" + }, + "lease_fee_basis": { + "type": "u32", + "description": "the proportional fee in basis points (parts per 10,000) for a lease" + }, + "funding_weight": { + "type": "u32", + "description": "the onchain weight you'll have to pay for a lease" + }, + "channel_fee_max_base_msat": { + "type": "msat", + "description": "the maximum base routing fee this node will charge during the lease" + }, + "channel_fee_max_proportional_thousandths": { + "type": "u32", + "description": "the maximum proportional routing fee this node will charge during the lease (in thousandths, not millionths like channel_update)" + }, + "compact_lease": { + "type": "hex", + "description": "the lease as represented in the node_announcement" + } + } + } + } + } + } + ] + } } + } } diff --git a/doc/schemas/listoffers.schema.json b/doc/schemas/listoffers.schema.json index b7f21d112ff5..9f5be747da54 100644 --- a/doc/schemas/listoffers.schema.json +++ b/doc/schemas/listoffers.schema.json @@ -1,48 +1,57 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "offers" ], - "properties": { - "offers": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "used" ], - "properties": { - "offer_id": { - "type": "hex", - "description": "the id of this offer (merkle hash of non-signature fields)", - "maxLength": 64, - "minLength": 64 - }, - "active": { - "type": "boolean", - "description": "whether this can still be used" - }, - "single_use": { - "type": "boolean", - "description": "whether this expires as soon as it's paid" - }, - "bolt12": { - "type": "string", - "description": "the bolt12 encoding of the offer" - }, - "bolt12_unsigned": { - "type": "string", - "description": "the bolt12 encoding of the offer, without signature" - }, - "used": { - "type": "boolean", - "description": "True if an associated invoice has been paid" - }, - "label": { - "type": "string", - "description": "the (optional) user-specified label" - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "offers" + ], + "properties": { + "offers": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "offer_id", + "active", + "single_use", + "bolt12", + "bolt12_unsigned", + "used" + ], + "properties": { + "offer_id": { + "type": "hex", + "description": "the id of this offer (merkle hash of non-signature fields)", + "maxLength": 64, + "minLength": 64 + }, + "active": { + "type": "boolean", + "description": "whether this can still be used" + }, + "single_use": { + "type": "boolean", + "description": "whether this expires as soon as it's paid" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 encoding of the offer" + }, + "bolt12_unsigned": { + "type": "string", + "description": "the bolt12 encoding of the offer, without signature" + }, + "used": { + "type": "boolean", + "description": "True if an associated invoice has been paid" + }, + "label": { + "type": "string", + "description": "the (optional) user-specified label" + } + } + } } + } } diff --git a/doc/schemas/listpays.schema.json b/doc/schemas/listpays.schema.json index 78c4ee4a1c73..f10062ea2180 100644 --- a/doc/schemas/listpays.schema.json +++ b/doc/schemas/listpays.schema.json @@ -1,147 +1,168 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "pays" ], - "properties": { - "pays": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "payment_hash", "status", "created_at" ], - "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" - }, - "created_at": { - "type": "u64", - "description": "the UNIX timestamp showing when this payment was initiated" - }, - "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)." - } - }, - "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" - } - } - } - }, - { - "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)." - } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "failed" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ ], - "properties": { - "payment_hash": { }, - "status": { }, - "destination": { }, - "created_at": { }, - "label": { }, - "bolt11": { }, - "bolt12": { }, - "amount_sent_msat": { }, - "erroronion": { - "type": "hex", - "description": "the error onion returned on failure, if any." - } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "pays" + ], + "properties": { + "pays": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "payment_hash", + "status", + "created_at" + ], + "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" + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "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)." + } + }, + "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" + } + } + } + }, + { + "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)." + } + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "failed" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "payment_hash": {}, + "status": {}, + "destination": {}, + "created_at": {}, + "label": {}, + "bolt11": {}, + "bolt12": {}, + "amount_sent_msat": {}, + "erroronion": { + "type": "hex", + "description": "the error onion returned on failure, if any." + } + } + } + } + ] + } } + } } diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index 42b753445672..1a37fd2bc6b4 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -1,951 +1,1125 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "peers" ], - "properties": { - "peers": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "id", "connected", "channels" ], - "properties": { - "id": { - "type": "pubkey", - "description": "the public key of the peer" - }, - "connected": { - "type": "boolean", - "description": "True if the peer is currently connected" - }, - "log": { - "type": "array", - "description": "if *level* is specified, logs for this peer", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "type" ], - "properties": { - "type": { - "type": "string", - "enum": [ "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO_IN", "IO_OUT" ] - } - }, - "allOf": [ - { - "if": { - "properties": { - "type": { - "enum": [ "SKIPPED" ] - } - } - }, - "then": { - "type": "object", - "additionalProperties": false, - "required": [ "num_skipped" ], - "properties": { - "type": { }, - "num_skipped": { - "type": "u32", - "description": "number of deleted/omitted entries" - } - } - } - }, - { - "if": { - "properties": { - "type": { - "enum": [ "BROKEN", "UNUSUAL", "INFO", "DEBUG" ] - } - } - }, - "then": { - "type": "object", - "additionalProperties": false, - "required": [ "time", "source", "log", "node_id" ], - "properties": { - "type": { }, - "time": { - "type": "string", - "description": "UNIX timestamp with 9 decimal places" - }, - "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": { - "properties": { - "type": { - "enum": [ "IO_IN", "IO_OUT" ] - } - } - }, - "then": { - "type": "object", - "additionalProperties": false, - "required": [ "time", "source", "log", "node_id", "data" ], - "properties": { - "type": { }, - "time": { - "type": "string", - "description": "UNIX timestamp with 9 decimal places" - }, - "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" - }, - "data": { - "type": "hex", - "description": "The IO which occurred" - } - } - } - } - ] - } - }, - "channels": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "state", "opener", "features" ], - "properties": { - "state": { - "type": "string", - "enum": [ "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN" ], - "description": "the channel state, in particular \"CHANNELD_NORMAL\" means the channel can be used normally" - }, - "scratch_txid": { - "type": "txid", - "description": "The txid we would use if we went onchain now" - }, - "feerate": { - "type": "object", - "description": "Feerates for the current tx", - "additionalProperties": false, - "required": [ "perkw", "perkb" ], - "properties": { - "perkw": { - "type": "u32", - "description": "Feerate per 1000 weight (i.e kSipa)" - }, - "perkb": { - "type": "u32", - "description": "Feerate per 1000 virtual bytes" - } - } - }, - "owner": { - "type": "string", - "description": "The current subdaemon controlling this connection" - }, - "short_channel_id": { - "type": "short_channel_id", - "description": "The short_channel_id (once locked in)" - }, - "channel_id": { - "type": "hex", - "description": "The full channel_id", - "minLength": 64, - "maxLength": 64 - }, - "funding_txid": { - "type": "txid", - "description": "ID of the funding transaction" - }, - "initial_feerate": { - "type": "string", - "description": "For inflight opens, the first feerate used to initiate the channel open" - }, - "last_feerate": { - "type": "string", - "description": "For inflight opens, the most recent feerate used on the channel open" - }, - "next_feerate": { - "type": "string", - "description": "For inflight opens, the next feerate we'll use for the channel open" - }, - "next_fee_step": { - "type": "u32", - "description": "For inflight opens, the next feerate step we'll use for the channel open" - }, - "inflight": { - "type": "array", - "description": "Current candidate funding transactions (only for dual-funding)", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "funding_txid", "funding_outnum", "feerate", "total_funding_msat", "our_funding_msat", "scratch_txid" ], - "properties": { - "funding_txid": { - "type": "txid", - "description": "ID of the funding transaction" - }, - "funding_outnum": { - "type": "u32", - "description": "The 0-based output number of the funding transaction which opens the channel" - }, - "feerate": { - "type": "string", - "description": "The feerate for this funding transaction in per-1000-weight, with \"kpw\" appended" - }, - "total_funding_msat": { - "type": "msat", - "description": "total amount in the channel" - }, - "our_funding_msat": { - "type": "msat", - "description": "amount we have in the channel" - }, - "scratch_txid": { - "type": "txid", - "description": "The commitment transaction txid we would use if we went onchain now" - } - } - } - }, - "close_to": { - "type": "hex", - "description": "scriptPubkey which we have to close to if we mutual close" - }, - "private": { - "type": "boolean", - "description": "if False, we will not announce this channel" - }, - "opener": { - "type": "string", - "enum": [ "local", "remote" ], - "description": "Who initiated the channel" - }, - "closer": { - "FIXME": "deprecated_apis turns off null!", - "type": [ "string", "null" ], - "enum": [ "local", "remote", null ], - "description": "Who initiated the channel close (`null` is deprecated!)" - }, - "features": { - "type": "array", - "items": { - "type": "string", - "enum": [ "option_static_remotekey", "option_anchor_outputs" ], - "description": "BOLT #9 features which apply to this channel" - } - }, - "funding": { - "type": "object", - "additionalProperties": false, - "required": [ "local_msat", "remote_msat" ], - "properties": { - "local_msat": { - "type": "msat", - "description": "Amount of channel we funded" - }, - "remote_msat": { - "type": "msat", - "description": "Amount of channel they funded" - } - } - }, - "funding_allocation_msat": { - "deprecated": true - }, - "funding_msat": { - "deprecated": true - }, - "to_us_msat": { - "type": "msat", - "description": "how much of channel is owed to us" - }, - "min_to_us_msat": { - "type": "msat", - "description": "least amount owed to us ever" - }, - "max_to_us_msat": { - "type": "msat", - "description": "most amount owed to us ever" - }, - "total_msat": { - "type": "msat", - "description": "total amount in the channel" - }, - "fee_base_msat": { - "type": "msat", - "description": "amount we charge to use the channel" - }, - "fee_proportional_millionths": { - "type": "u32", - "description": "amount we charge to use the channel in parts-per-million" - }, - "dust_limit_msat": { - "type": "msat", - "description": "minimum amount for an output on the channel transactions" - }, - "max_total_htlc_in_msat": { - "type": "msat", - "description": "max amount accept in a single payment" - }, - "their_reserve_msat": { - "type": "msat", - "description": "minimum we insist they keep in channel" - }, - "our_reserve_msat": { - "type": "msat", - "description": "minimum they insist we keep in channel" - }, - "spendable_msat": { - "type": "msat", - "description": "total we could send through channel" - }, - "receivable_msat": { - "type": "msat", - "description": "total peer could send through channel" - }, - "minimum_htlc_in_msat": { - "type": "msat", - "description": "the minimum amount HTLC we accept" - }, - "their_to_self_delay": { - "type": "u32", - "description": "the number of blocks before they can take their funds if they unilateral close" - }, - "our_to_self_delay": { - "type": "u32", - "description": "the number of blocks before we can take our funds if we unilateral close" - }, - "max_accepted_htlcs": { - "type": "u32", - "description": "Maximum number of incoming HTLC we will accept at once" - }, - "msatoshi_to_us": { - "deprecated": true - }, - "msatoshi_to_us_min": { - "deprecated": true - }, - "msatoshi_to_us_max": { - "deprecated": true - }, - "msatoshi_total": { - "deprecated": true - }, - "dust_limit_satoshis": { - "deprecated": true - }, - "max_htlc_value_in_flight_msat": { - "deprecated": true - }, - "our_channel_reserve_satoshis": { - "deprecated": true - }, - "their_channel_reserve_satoshis": { - "deprecated": true - }, - "spendable_msatoshi": { - "deprecated": true - }, - "receivable_msatoshi": { - "deprecated": true - }, - "htlc_minimum_msat": { - "deprecated": true - }, - "state_changes": { - "type": "array", - "description": "Prior state changes", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "timestamp", "old_state", "new_state", "cause", "message" ], - "properties": { - "timestamp": { - "type": "string", - "description": "UTC timestamp of form YYYY-mm-ddTHH:MM:SS.%03dZ" - }, - "old_state": { - "type": "string", - "enum": [ "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN" ], - "description": "Previous state" - }, - "new_state": { - "type": "string", - "enum": [ "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN" ], - "description": "New state" - }, - "cause": { - "type": "string", - "enum": [ "unknown", "local", "user", "remote", "protocol", "onchain" ], - "description": "What caused the change" - }, - "message": { - "type": "string", - "description": "Human-readable explanation" - } - } - } - }, - "status": { - "type": "array", - "items": { - "type": "string", - "description": "Billboard log of significant changes" - } - }, - "in_payments_offered": { - "type": "u64", - "description": "Number of incoming payment attempts" - }, - "in_offered_msat": { - "type": "msat", - "description": "Total amount of incoming payment attempts" - }, - "in_msatoshi_offered": { - "deprecated": true - }, - "in_payments_fulfilled": { - "type": "u64", - "description": "Number of successful incoming payment attempts" - }, - "in_fulfilled_msat": { - "type": "msat", - "description": "Total amount of successful incoming payment attempts" - }, - "in_msatoshi_fulfilled": { - "deprecated": true - }, - "out_payments_offered": { - "type": "u64", - "description": "Number of outgoing payment attempts" - }, - "out_offered_msat": { - "type": "msat", - "description": "Total amount of outgoing payment attempts" - }, - "out_msatoshi_offered": { - "deprecated": true - }, - "out_payments_fulfilled": { - "type": "u64", - "description": "Number of successful outgoing payment attempts" - }, - "out_fulfilled_msat": { - "type": "msat", - "description": "Total amount of successful outgoing payment attempts" - }, - "out_msatoshi_fulfilled": { - "deprecated": true - }, - "htlcs": { - "type": "array", - "description": "current HTLCs in this channel", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "direction", "id", "amount_msat", "expiry", "payment_hash", "state" ], - "properties": { - "direction": { - "type": "string", - "enum": [ "in", "out" ], - "description": "Whether it came from peer, or is going to peer" - }, - "id": { - "type": "u64", - "description": "Unique ID for this htlc on this channel in this direction" - }, - "amount_msat": { - "type": "msat", - "description": "Amount send/received for this HTLC" - }, - "msatoshi": { - "deprecated": true - }, - "expiry": { - "type": "u32", - "description": "Block this HTLC expires at" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the payment_preimage which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "local_trimmed": { - "type": "boolean", - "enum": [ true ], - "description": "if this is too small to enforce onchain" - }, - "status": { - "type": "string", - "description": "set if this HTLC is currently waiting on a hook (and shows what plugin)" - } - }, - "allOf": [ - { - "if": { - "properties": { - "direction": { - "enum": [ "out" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "state" ], - "properties": { - "direction": { }, - "id": { }, - "amount_msat": { }, - "msatoshi": { }, - "expiry": { }, - "payment_hash": { }, - "local_trimmed": { }, - "status": { }, - "state": { - "type": "string", - "enum": [ "SENT_ADD_HTLC", "SENT_ADD_COMMIT", "RCVD_ADD_REVOCATION", "RCVD_ADD_ACK_COMMIT", "SENT_ADD_ACK_REVOCATION", "RCVD_REMOVE_HTLC", "RCVD_REMOVE_COMMIT", "SENT_REMOVE_REVOCATION", "SENT_REMOVE_ACK_COMMIT", "RCVD_REMOVE_ACK_REVOCATION" ], - "description": "Status of the HTLC" - } - } - } - }, - { - "if": { - "properties": { - "direction": { - "enum": [ "in" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "state" ], - "properties": { - "direction": { }, - "id": { }, - "amount_msat": { }, - "msatoshi": { }, - "expiry": { }, - "payment_hash": { }, - "local_trimmed": { }, - "status": { }, - "state": { - "type": "string", - "enum": [ "RCVD_ADD_HTLC", "RCVD_ADD_COMMIT", "SENT_ADD_REVOCATION", "SENT_ADD_ACK_COMMIT", "RCVD_ADD_ACK_REVOCATION", "SENT_REMOVE_HTLC", "SENT_REMOVE_COMMIT", "RCVD_REMOVE_REVOCATION", "RCVD_REMOVE_ACK_COMMIT", "SENT_REMOVE_ACK_REVOCATION" ], - "description": "Status of the HTLC" - } - } - } - } - ] - } - } - }, - "allOf": [ - { - "if": { - "required": [ "close_to" ] - }, - "then": { - "additionalProperties": false, - "required": [ ], - "properties": { - "state": { }, - "scratch_txid": { }, - "feerate": { }, - "owner": { }, - "short_channel_id": { }, - "channel_id": { }, - "funding_txid": { }, - "close_to": { }, - "private": { }, - "opener": { }, - "closer": { }, - "features": { }, - "funding": { }, - "funding_allocation_msat": { }, - "funding_msat": { }, - "to_us_msat": { }, - "min_to_us_msat": { }, - "max_to_us_msat": { }, - "total_msat": { }, - "fee_base_msat": { }, - "fee_proportional_millionths": { }, - "dust_limit_msat": { }, - "max_total_htlc_in_msat": { }, - "their_reserve_msat": { }, - "our_reserve_msat": { }, - "spendable_msat": { }, - "receivable_msat": { }, - "minimum_htlc_in_msat": { }, - "spendable_msatoshi": { }, - "receivable_msatoshi": { }, - "their_to_self_delay": { }, - "our_to_self_delay": { }, - "max_accepted_htlcs": { }, - "msatoshi_to_us": { }, - "msatoshi_to_us_min": { }, - "msatoshi_to_us_max": { }, - "msatoshi_total": { }, - "dust_limit_satoshis": { }, - "max_htlc_value_in_flight_msat": { }, - "our_channel_reserve_satoshis": { }, - "their_channel_reserve_satoshis": { }, - "spendable_satoshis": { }, - "receivable_satoshis": { }, - "htlc_minimum_msat": { }, - "state_changes": { }, - "status": { }, - "in_payments_offered": { }, - "in_offered_msat": { }, - "in_msatoshi_offered": { }, - "in_payments_fulfilled": { }, - "in_fulfilled_msat": { }, - "in_msatoshi_fulfilled": { }, - "out_payments_offered": { }, - "out_offered_msat": { }, - "out_msatoshi_offered": { }, - "out_payments_fulfilled": { }, - "out_fulfilled_msat": { }, - "out_msatoshi_fulfilled": { }, - "htlcs": { }, - "initial_feerate": { }, - "last_feerate": { }, - "next_feerate": { }, - "inflight": { }, - "last_tx_fee": { }, - "last_tx_fee_msat": { }, - "direction": { }, - "close_to_addr": { - "type": "string", - "description": "The bitcoin address we will close to" - } - } - } - }, - { - "if": { - "required": [ "scratch_txid" ] - }, - "then": { - "additionalProperties": false, - "required": [ "last_tx_fee_msat" ], - "properties": { - "state": { }, - "scratch_txid": { }, - "feerate": { }, - "owner": { }, - "short_channel_id": { }, - "channel_id": { }, - "funding_txid": { }, - "inflight": { }, - "close_to": { }, - "private": { }, - "opener": { }, - "closer": { }, - "features": { }, - "funding": { }, - "funding_allocation_msat": { }, - "funding_msat": { }, - "to_us_msat": { }, - "min_to_us_msat": { }, - "max_to_us_msat": { }, - "total_msat": { }, - "fee_base_msat": { }, - "fee_proportional_millionths": { }, - "dust_limit_msat": { }, - "max_total_htlc_in_msat": { }, - "their_reserve_msat": { }, - "our_reserve_msat": { }, - "spendable_msat": { }, - "receivable_msat": { }, - "minimum_htlc_in_msat": { }, - "spendable_msatoshi": { }, - "receivable_msatoshi": { }, - "their_to_self_delay": { }, - "our_to_self_delay": { }, - "max_accepted_htlcs": { }, - "msatoshi_to_us": { }, - "msatoshi_to_us_min": { }, - "msatoshi_to_us_max": { }, - "msatoshi_total": { }, - "dust_limit_satoshis": { }, - "max_htlc_value_in_flight_msat": { }, - "our_channel_reserve_satoshis": { }, - "their_channel_reserve_satoshis": { }, - "spendable_satoshis": { }, - "receivable_satoshis": { }, - "htlc_minimum_msat": { }, - "state_changes": { }, - "status": { }, - "in_payments_offered": { }, - "in_offered_msat": { }, - "in_msatoshi_offered": { }, - "in_payments_fulfilled": { }, - "in_fulfilled_msat": { }, - "in_msatoshi_fulfilled": { }, - "out_payments_offered": { }, - "out_offered_msat": { }, - "out_msatoshi_offered": { }, - "out_payments_fulfilled": { }, - "out_fulfilled_msat": { }, - "out_msatoshi_fulfilled": { }, - "htlcs": { }, - "initial_feerate": { }, - "last_feerate": { }, - "next_feerate": { }, - "close_to_addr": { }, - "initial_feerate": { }, - "last_feerate": { }, - "next_feerate": { }, - "direction": { }, - "last_tx_fee": { - "deprecated": true - }, - "last_tx_fee_msat": { - "type": "msat", - "description": "fee attached to this the current tx" - } - } - } - }, - { - "if": { - "required": [ "short_channel_id" ] - }, - "then": { - "additionalProperties": false, - "required": [ "direction" ], - "properties": { - "state": { }, - "scratch_txid": { }, - "feerate": { }, - "owner": { }, - "short_channel_id": { }, - "channel_id": { }, - "funding_txid": { }, - "inflight": { }, - "close_to": { }, - "private": { }, - "opener": { }, - "closer": { }, - "features": { }, - "funding": { }, - "funding_allocation_msat": { }, - "funding_msat": { }, - "to_us_msat": { }, - "min_to_us_msat": { }, - "max_to_us_msat": { }, - "total_msat": { }, - "fee_base_msat": { }, - "fee_proportional_millionths": { }, - "dust_limit_msat": { }, - "max_total_htlc_in_msat": { }, - "their_reserve_msat": { }, - "our_reserve_msat": { }, - "spendable_msat": { }, - "receivable_msat": { }, - "minimum_htlc_in_msat": { }, - "spendable_msatoshi": { }, - "receivable_msatoshi": { }, - "their_to_self_delay": { }, - "our_to_self_delay": { }, - "max_accepted_htlcs": { }, - "msatoshi_to_us": { }, - "msatoshi_to_us_min": { }, - "msatoshi_to_us_max": { }, - "msatoshi_total": { }, - "dust_limit_satoshis": { }, - "max_htlc_value_in_flight_msat": { }, - "our_channel_reserve_satoshis": { }, - "their_channel_reserve_satoshis": { }, - "spendable_satoshis": { }, - "receivable_satoshis": { }, - "htlc_minimum_msat": { }, - "state_changes": { }, - "status": { }, - "in_payments_offered": { }, - "in_offered_msat": { }, - "in_msatoshi_offered": { }, - "in_payments_fulfilled": { }, - "in_fulfilled_msat": { }, - "in_msatoshi_fulfilled": { }, - "out_payments_offered": { }, - "out_offered_msat": { }, - "out_msatoshi_offered": { }, - "out_payments_fulfilled": { }, - "out_fulfilled_msat": { }, - "out_msatoshi_fulfilled": { }, - "htlcs": { }, - "initial_feerate": { }, - "last_feerate": { }, - "next_feerate": { }, - "last_tx_fee": { }, - "close_to_addr": { }, - "initial_feerate": { }, - "last_feerate": { }, - "next_feerate": { }, - "last_tx_fee": { }, - "last_tx_fee_msat": { }, - "direction": { - "type": "u32", - "description": "0 if we're the lesser node_id, 1 if we're the greater" - } - } - } - }, - { - "if": { - "required": [ "inflight" ] - }, - "then": { - "additionalProperties": false, - "required": [ "initial_feerate", "last_feerate", "next_feerate" ], - "properties": { - "state": { }, - "scratch_txid": { }, - "feerate": { }, - "owner": { }, - "short_channel_id": { }, - "channel_id": { }, - "funding_txid": { }, - "close_to": { }, - "private": { }, - "opener": { }, - "closer": { }, - "features": { }, - "funding": { }, - "funding_allocation_msat": { }, - "funding_msat": { }, - "to_us_msat": { }, - "min_to_us_msat": { }, - "max_to_us_msat": { }, - "total_msat": { }, - "fee_base_msat": { }, - "fee_proportional_millionths": { }, - "dust_limit_msat": { }, - "max_total_htlc_in_msat": { }, - "their_reserve_msat": { }, - "our_reserve_msat": { }, - "spendable_msat": { }, - "receivable_msat": { }, - "minimum_htlc_in_msat": { }, - "spendable_msatoshi": { }, - "receivable_msatoshi": { }, - "their_to_self_delay": { }, - "our_to_self_delay": { }, - "max_accepted_htlcs": { }, - "msatoshi_to_us": { }, - "msatoshi_to_us_min": { }, - "msatoshi_to_us_max": { }, - "msatoshi_total": { }, - "dust_limit_satoshis": { }, - "max_htlc_value_in_flight_msat": { }, - "our_channel_reserve_satoshis": { }, - "their_channel_reserve_satoshis": { }, - "spendable_satoshis": { }, - "receivable_satoshis": { }, - "htlc_minimum_msat": { }, - "state_changes": { }, - "status": { }, - "in_payments_offered": { }, - "in_offered_msat": { }, - "in_msatoshi_offered": { }, - "in_payments_fulfilled": { }, - "in_fulfilled_msat": { }, - "in_msatoshi_fulfilled": { }, - "out_payments_offered": { }, - "out_offered_msat": { }, - "out_msatoshi_offered": { }, - "out_payments_fulfilled": { }, - "out_fulfilled_msat": { }, - "out_msatoshi_fulfilled": { }, - "htlcs": { }, - "inflight": { }, - "last_tx_fee": { }, - "close_to_addr": { }, - "direction": { }, - "last_tx_fee": { }, - "last_tx_fee_msat": { }, - "initial_feerate": { - "type": "string", - "description": "The feerate for the initial funding transaction in per-1000-weight, with \"kpw\" appended" - }, - "last_feerate": { - "type": "string", - "description": "The feerate for the latest funding transaction in per-1000-weight, with \"kpw\" appended" - }, - "next_feerate": { - "type": "string", - "description": "The minimum feerate for the next funding transaction in per-1000-weight, with \"kpw\" appended" - } - } - } - } - ] - } - } - }, - "allOf": [ - { - "if": { - "additionalProperties": true, - "properties": { - "connected": { - "enum": [ true ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "netaddr", "features" ], - "properties": { - "id": { }, - "channels": { }, - "connected": { }, - "htlcs": { }, - "log": { }, - "last_tx_fee": { }, - "netaddr": { - "type": "array", - "minItems": 1, - "maxItems": 1, - "description": "A single entry array", - "items": { - "type": "string", - "description": "address, e.g. 1.2.3.4:1234" - } - }, - "features": { - "type": "hex", - "description": "bitmap of BOLT #9 features from peer's INIT message" } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "peers" + ], + "properties": { + "peers": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "id", + "connected", + "channels" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "the public key of the peer" + }, + "connected": { + "type": "boolean", + "description": "True if the peer is currently connected" + }, + "log": { + "type": "array", + "description": "if *level* is specified, logs for this peer", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "SKIPPED", + "BROKEN", + "UNUSUAL", + "INFO", + "DEBUG", + "IO_IN", + "IO_OUT" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "enum": [ + "SKIPPED" + ] + } + } + }, + "then": { + "type": "object", + "additionalProperties": false, + "required": [ + "num_skipped" + ], + "properties": { + "type": {}, + "num_skipped": { + "type": "u32", + "description": "number of deleted/omitted entries" + } + } + } + }, + { + "if": { + "properties": { + "type": { + "enum": [ + "BROKEN", + "UNUSUAL", + "INFO", + "DEBUG" + ] + } + } + }, + "then": { + "type": "object", + "additionalProperties": false, + "required": [ + "time", + "source", + "log", + "node_id" + ], + "properties": { + "type": {}, + "time": { + "type": "string", + "description": "UNIX timestamp with 9 decimal places" + }, + "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": { + "properties": { + "type": { + "enum": [ + "IO_IN", + "IO_OUT" + ] + } + } + }, + "then": { + "type": "object", + "additionalProperties": false, + "required": [ + "time", + "source", + "log", + "node_id", + "data" + ], + "properties": { + "type": {}, + "time": { + "type": "string", + "description": "UNIX timestamp with 9 decimal places" + }, + "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" + }, + "data": { + "type": "hex", + "description": "The IO which occurred" + } + } + } + } + ] + } + }, + "channels": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "state", + "opener", + "features" + ], + "properties": { + "state": { + "type": "string", + "enum": [ + "OPENINGD", + "CHANNELD_AWAITING_LOCKIN", + "CHANNELD_NORMAL", + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN", + "DUALOPEND_OPEN_INIT", + "DUALOPEND_AWAITING_LOCKIN" + ], + "description": "the channel state, in particular \"CHANNELD_NORMAL\" means the channel can be used normally" + }, + "scratch_txid": { + "type": "txid", + "description": "The txid we would use if we went onchain now" + }, + "feerate": { + "type": "object", + "description": "Feerates for the current tx", + "additionalProperties": false, + "required": [ + "perkw", + "perkb" + ], + "properties": { + "perkw": { + "type": "u32", + "description": "Feerate per 1000 weight (i.e kSipa)" + }, + "perkb": { + "type": "u32", + "description": "Feerate per 1000 virtual bytes" + } + } + }, + "owner": { + "type": "string", + "description": "The current subdaemon controlling this connection" + }, + "short_channel_id": { + "type": "short_channel_id", + "description": "The short_channel_id (once locked in)" + }, + "channel_id": { + "type": "hex", + "description": "The full channel_id", + "minLength": 64, + "maxLength": 64 + }, + "funding_txid": { + "type": "txid", + "description": "ID of the funding transaction" + }, + "initial_feerate": { + "type": "string", + "description": "For inflight opens, the first feerate used to initiate the channel open" + }, + "last_feerate": { + "type": "string", + "description": "For inflight opens, the most recent feerate used on the channel open" + }, + "next_feerate": { + "type": "string", + "description": "For inflight opens, the next feerate we'll use for the channel open" + }, + "next_fee_step": { + "type": "u32", + "description": "For inflight opens, the next feerate step we'll use for the channel open" + }, + "inflight": { + "type": "array", + "description": "Current candidate funding transactions (only for dual-funding)", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "funding_txid", + "funding_outnum", + "feerate", + "total_funding_msat", + "our_funding_msat", + "scratch_txid" + ], + "properties": { + "funding_txid": { + "type": "txid", + "description": "ID of the funding transaction" + }, + "funding_outnum": { + "type": "u32", + "description": "The 0-based output number of the funding transaction which opens the channel" + }, + "feerate": { + "type": "string", + "description": "The feerate for this funding transaction in per-1000-weight, with \"kpw\" appended" + }, + "total_funding_msat": { + "type": "msat", + "description": "total amount in the channel" + }, + "our_funding_msat": { + "type": "msat", + "description": "amount we have in the channel" + }, + "scratch_txid": { + "type": "txid", + "description": "The commitment transaction txid we would use if we went onchain now" + } + } + } + }, + "close_to": { + "type": "hex", + "description": "scriptPubkey which we have to close to if we mutual close" + }, + "private": { + "type": "boolean", + "description": "if False, we will not announce this channel" + }, + "opener": { + "type": "string", + "enum": [ + "local", + "remote" + ], + "description": "Who initiated the channel" + }, + "closer": { + "FIXME": "deprecated_apis turns off null!", + "type": [ + "string", + "null" + ], + "enum": [ + "local", + "remote", + null + ], + "description": "Who initiated the channel close (`null` is deprecated!)" + }, + "features": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "option_static_remotekey", + "option_anchor_outputs" + ], + "description": "BOLT #9 features which apply to this channel" + } + }, + "funding": { + "type": "object", + "additionalProperties": false, + "required": [ + "local_msat", + "remote_msat" + ], + "properties": { + "local_msat": { + "type": "msat", + "description": "Amount of channel we funded" + }, + "remote_msat": { + "type": "msat", + "description": "Amount of channel they funded" + } + } + }, + "funding_allocation_msat": { + "deprecated": true + }, + "funding_msat": { + "deprecated": true + }, + "to_us_msat": { + "type": "msat", + "description": "how much of channel is owed to us" + }, + "min_to_us_msat": { + "type": "msat", + "description": "least amount owed to us ever" + }, + "max_to_us_msat": { + "type": "msat", + "description": "most amount owed to us ever" + }, + "total_msat": { + "type": "msat", + "description": "total amount in the channel" + }, + "fee_base_msat": { + "type": "msat", + "description": "amount we charge to use the channel" + }, + "fee_proportional_millionths": { + "type": "u32", + "description": "amount we charge to use the channel in parts-per-million" + }, + "dust_limit_msat": { + "type": "msat", + "description": "minimum amount for an output on the channel transactions" + }, + "max_total_htlc_in_msat": { + "type": "msat", + "description": "max amount accept in a single payment" + }, + "their_reserve_msat": { + "type": "msat", + "description": "minimum we insist they keep in channel" + }, + "our_reserve_msat": { + "type": "msat", + "description": "minimum they insist we keep in channel" + }, + "spendable_msat": { + "type": "msat", + "description": "total we could send through channel" + }, + "receivable_msat": { + "type": "msat", + "description": "total peer could send through channel" + }, + "minimum_htlc_in_msat": { + "type": "msat", + "description": "the minimum amount HTLC we accept" + }, + "their_to_self_delay": { + "type": "u32", + "description": "the number of blocks before they can take their funds if they unilateral close" + }, + "our_to_self_delay": { + "type": "u32", + "description": "the number of blocks before we can take our funds if we unilateral close" + }, + "max_accepted_htlcs": { + "type": "u32", + "description": "Maximum number of incoming HTLC we will accept at once" + }, + "msatoshi_to_us": { + "deprecated": true + }, + "msatoshi_to_us_min": { + "deprecated": true + }, + "msatoshi_to_us_max": { + "deprecated": true + }, + "msatoshi_total": { + "deprecated": true + }, + "dust_limit_satoshis": { + "deprecated": true + }, + "max_htlc_value_in_flight_msat": { + "deprecated": true + }, + "our_channel_reserve_satoshis": { + "deprecated": true + }, + "their_channel_reserve_satoshis": { + "deprecated": true + }, + "spendable_msatoshi": { + "deprecated": true + }, + "receivable_msatoshi": { + "deprecated": true + }, + "htlc_minimum_msat": { + "deprecated": true + }, + "state_changes": { + "type": "array", + "description": "Prior state changes", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "timestamp", + "old_state", + "new_state", + "cause", + "message" + ], + "properties": { + "timestamp": { + "type": "string", + "description": "UTC timestamp of form YYYY-mm-ddTHH:MM:SS.%03dZ" + }, + "old_state": { + "type": "string", + "enum": [ + "OPENINGD", + "CHANNELD_AWAITING_LOCKIN", + "CHANNELD_NORMAL", + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN", + "DUALOPEND_OPEN_INIT", + "DUALOPEND_AWAITING_LOCKIN" + ], + "description": "Previous state" + }, + "new_state": { + "type": "string", + "enum": [ + "OPENINGD", + "CHANNELD_AWAITING_LOCKIN", + "CHANNELD_NORMAL", + "CHANNELD_SHUTTING_DOWN", + "CLOSINGD_SIGEXCHANGE", + "CLOSINGD_COMPLETE", + "AWAITING_UNILATERAL", + "FUNDING_SPEND_SEEN", + "ONCHAIN", + "DUALOPEND_OPEN_INIT", + "DUALOPEND_AWAITING_LOCKIN" + ], + "description": "New state" + }, + "cause": { + "type": "string", + "enum": [ + "unknown", + "local", + "user", + "remote", + "protocol", + "onchain" + ], + "description": "What caused the change" + }, + "message": { + "type": "string", + "description": "Human-readable explanation" + } + } + } + }, + "status": { + "type": "array", + "items": { + "type": "string", + "description": "Billboard log of significant changes" + } + }, + "in_payments_offered": { + "type": "u64", + "description": "Number of incoming payment attempts" + }, + "in_offered_msat": { + "type": "msat", + "description": "Total amount of incoming payment attempts" + }, + "in_msatoshi_offered": { + "deprecated": true + }, + "in_payments_fulfilled": { + "type": "u64", + "description": "Number of successful incoming payment attempts" + }, + "in_fulfilled_msat": { + "type": "msat", + "description": "Total amount of successful incoming payment attempts" + }, + "in_msatoshi_fulfilled": { + "deprecated": true + }, + "out_payments_offered": { + "type": "u64", + "description": "Number of outgoing payment attempts" + }, + "out_offered_msat": { + "type": "msat", + "description": "Total amount of outgoing payment attempts" + }, + "out_msatoshi_offered": { + "deprecated": true + }, + "out_payments_fulfilled": { + "type": "u64", + "description": "Number of successful outgoing payment attempts" + }, + "out_fulfilled_msat": { + "type": "msat", + "description": "Total amount of successful outgoing payment attempts" + }, + "out_msatoshi_fulfilled": { + "deprecated": true + }, + "htlcs": { + "type": "array", + "description": "current HTLCs in this channel", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "direction", + "id", + "amount_msat", + "expiry", + "payment_hash", + "state" + ], + "properties": { + "direction": { + "type": "string", + "enum": [ + "in", + "out" + ], + "description": "Whether it came from peer, or is going to peer" + }, + "id": { + "type": "u64", + "description": "Unique ID for this htlc on this channel in this direction" + }, + "amount_msat": { + "type": "msat", + "description": "Amount send/received for this HTLC" + }, + "msatoshi": { + "deprecated": true + }, + "expiry": { + "type": "u32", + "description": "Block this HTLC expires at" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the payment_preimage which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "local_trimmed": { + "type": "boolean", + "enum": [ + true + ], + "description": "if this is too small to enforce onchain" + }, + "status": { + "type": "string", + "description": "set if this HTLC is currently waiting on a hook (and shows what plugin)" + } + }, + "allOf": [ + { + "if": { + "properties": { + "direction": { + "enum": [ + "out" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "state" + ], + "properties": { + "direction": {}, + "id": {}, + "amount_msat": {}, + "msatoshi": {}, + "expiry": {}, + "payment_hash": {}, + "local_trimmed": {}, + "status": {}, + "state": { + "type": "string", + "enum": [ + "SENT_ADD_HTLC", + "SENT_ADD_COMMIT", + "RCVD_ADD_REVOCATION", + "RCVD_ADD_ACK_COMMIT", + "SENT_ADD_ACK_REVOCATION", + "RCVD_REMOVE_HTLC", + "RCVD_REMOVE_COMMIT", + "SENT_REMOVE_REVOCATION", + "SENT_REMOVE_ACK_COMMIT", + "RCVD_REMOVE_ACK_REVOCATION" + ], + "description": "Status of the HTLC" + } + } + } + }, + { + "if": { + "properties": { + "direction": { + "enum": [ + "in" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "state" + ], + "properties": { + "direction": {}, + "id": {}, + "amount_msat": {}, + "msatoshi": {}, + "expiry": {}, + "payment_hash": {}, + "local_trimmed": {}, + "status": {}, + "state": { + "type": "string", + "enum": [ + "RCVD_ADD_HTLC", + "RCVD_ADD_COMMIT", + "SENT_ADD_REVOCATION", + "SENT_ADD_ACK_COMMIT", + "RCVD_ADD_ACK_REVOCATION", + "SENT_REMOVE_HTLC", + "SENT_REMOVE_COMMIT", + "RCVD_REMOVE_REVOCATION", + "RCVD_REMOVE_ACK_COMMIT", + "SENT_REMOVE_ACK_REVOCATION" + ], + "description": "Status of the HTLC" + } + } + } + } + ] + } + } + }, + "allOf": [ + { + "if": { + "required": [ + "close_to" + ] + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "state": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "close_to": {}, + "private": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "funding_allocation_msat": {}, + "funding_msat": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "initial_feerate": {}, + "last_feerate": {}, + "next_feerate": {}, + "inflight": {}, + "last_tx_fee": {}, + "last_tx_fee_msat": {}, + "direction": {}, + "close_to_addr": { + "type": "string", + "description": "The bitcoin address we will close to" + } + } + } + }, + { + "if": { + "required": [ + "scratch_txid" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "last_tx_fee_msat" + ], + "properties": { + "state": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "inflight": {}, + "close_to": {}, + "private": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "funding_allocation_msat": {}, + "funding_msat": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "initial_feerate": {}, + "last_feerate": {}, + "next_feerate": {}, + "close_to_addr": {}, + "direction": {}, + "last_tx_fee": { + "deprecated": true + }, + "last_tx_fee_msat": { + "type": "msat", + "description": "fee attached to this the current tx" + } + } + } + }, + { + "if": { + "required": [ + "short_channel_id" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "direction" + ], + "properties": { + "state": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "inflight": {}, + "close_to": {}, + "private": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "funding_allocation_msat": {}, + "funding_msat": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "initial_feerate": {}, + "last_feerate": {}, + "next_feerate": {}, + "last_tx_fee": {}, + "close_to_addr": {}, + "last_tx_fee_msat": {}, + "direction": { + "type": "u32", + "description": "0 if we're the lesser node_id, 1 if we're the greater" + } + } + } + }, + { + "if": { + "required": [ + "inflight" + ] + }, + "then": { + "additionalProperties": false, + "required": [ + "initial_feerate", + "last_feerate", + "next_feerate" + ], + "properties": { + "state": {}, + "scratch_txid": {}, + "feerate": {}, + "owner": {}, + "short_channel_id": {}, + "channel_id": {}, + "funding_txid": {}, + "close_to": {}, + "private": {}, + "opener": {}, + "closer": {}, + "features": {}, + "funding": {}, + "funding_allocation_msat": {}, + "funding_msat": {}, + "to_us_msat": {}, + "min_to_us_msat": {}, + "max_to_us_msat": {}, + "total_msat": {}, + "fee_base_msat": {}, + "fee_proportional_millionths": {}, + "dust_limit_msat": {}, + "max_total_htlc_in_msat": {}, + "their_reserve_msat": {}, + "our_reserve_msat": {}, + "spendable_msat": {}, + "receivable_msat": {}, + "minimum_htlc_in_msat": {}, + "spendable_msatoshi": {}, + "receivable_msatoshi": {}, + "their_to_self_delay": {}, + "our_to_self_delay": {}, + "max_accepted_htlcs": {}, + "msatoshi_to_us": {}, + "msatoshi_to_us_min": {}, + "msatoshi_to_us_max": {}, + "msatoshi_total": {}, + "dust_limit_satoshis": {}, + "max_htlc_value_in_flight_msat": {}, + "our_channel_reserve_satoshis": {}, + "their_channel_reserve_satoshis": {}, + "spendable_satoshis": {}, + "receivable_satoshis": {}, + "htlc_minimum_msat": {}, + "state_changes": {}, + "status": {}, + "in_payments_offered": {}, + "in_offered_msat": {}, + "in_msatoshi_offered": {}, + "in_payments_fulfilled": {}, + "in_fulfilled_msat": {}, + "in_msatoshi_fulfilled": {}, + "out_payments_offered": {}, + "out_offered_msat": {}, + "out_msatoshi_offered": {}, + "out_payments_fulfilled": {}, + "out_fulfilled_msat": {}, + "out_msatoshi_fulfilled": {}, + "htlcs": {}, + "inflight": {}, + "last_tx_fee": {}, + "close_to_addr": {}, + "direction": {}, + "last_tx_fee_msat": {}, + "initial_feerate": { + "type": "string", + "description": "The feerate for the initial funding transaction in per-1000-weight, with \"kpw\" appended" + }, + "last_feerate": { + "type": "string", + "description": "The feerate for the latest funding transaction in per-1000-weight, with \"kpw\" appended" + }, + "next_feerate": { + "type": "string", + "description": "The minimum feerate for the next funding transaction in per-1000-weight, with \"kpw\" appended" + } + } + } + } + ] + } + } + }, + "allOf": [ + { + "if": { + "additionalProperties": true, + "properties": { + "connected": { + "enum": [ + true + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "netaddr", + "features" + ], + "properties": { + "id": {}, + "channels": {}, + "connected": {}, + "htlcs": {}, + "log": {}, + "last_tx_fee": {}, + "netaddr": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "description": "A single entry array", + "items": { + "type": "string", + "description": "address, e.g. 1.2.3.4:1234" + } + }, + "features": { + "type": "hex", + "description": "bitmap of BOLT #9 features from peer's INIT message" + } + } + } + } + ] + } } + } } diff --git a/doc/schemas/listsendpays.schema.json b/doc/schemas/listsendpays.schema.json index d94a2543174e..8daaaf82713e 100644 --- a/doc/schemas/listsendpays.schema.json +++ b/doc/schemas/listsendpays.schema.json @@ -1,173 +1,193 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "payments" ], - "properties": { - "payments": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "id", "payment_hash", "status", "created_at", "amount_sent_msat" ], - "properties": { - "id": { - "type": "u64", - "description": "unique ID for this payment attempt" - }, - "groupid": { - "type": "u64", - "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" - }, - "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" - }, - "msatoshi": { - "deprecated": true - }, - "amount_msat": { - "type": "msat", - "description": "The amount delivered to destination (if known)" - }, - "destination": { - "type": "pubkey", - "description": "the final destination of the payment if known" - }, - "created_at": { - "type": "u64", - "description": "the UNIX timestamp showing when this payment was initiated" - }, - "msatoshi_sent": { - "deprecated": true - }, - "amount_sent_msat": { - "type": "msat", - "description": "The amount sent" - }, - "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)." - } - }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "complete" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "payment_preimage" ], - "properties": { - "id": { }, - "partid": { }, - "groupid": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "bolt11": { }, - "bolt12": { }, - "payment_preimage": { - "type": "hex", - "description": "the proof of payment: SHA256 of this **payment_hash**", - "maxLength": 64, - "minLength": 64 - } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "failed" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ ], - "properties": { - "id": { }, - "partid": { }, - "groupid": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "bolt11": { }, - "bolt12": { }, - "erroronion": { - "type": "hex", - "description": "the onion message returned" - } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "pending" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ ], - "properties": { - "id": { }, - "partid": { }, - "groupid": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "bolt11": { }, - "bolt12": { } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "payments" + ], + "properties": { + "payments": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "id", + "payment_hash", + "status", + "created_at", + "amount_sent_msat" + ], + "properties": { + "id": { + "type": "u64", + "description": "unique ID for this payment attempt" + }, + "groupid": { + "type": "u64", + "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" + }, + "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" + }, + "msatoshi": { + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "The amount delivered to destination (if known)" + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment if known" + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "msatoshi_sent": { + "deprecated": true + }, + "amount_sent_msat": { + "type": "msat", + "description": "The amount sent" + }, + "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)." + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "complete" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "payment_preimage" + ], + "properties": { + "id": {}, + "partid": {}, + "groupid": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "bolt11": {}, + "bolt12": {}, + "payment_preimage": { + "type": "hex", + "description": "the proof of payment: SHA256 of this **payment_hash**", + "maxLength": 64, + "minLength": 64 + } + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "failed" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "id": {}, + "partid": {}, + "groupid": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "bolt11": {}, + "bolt12": {}, + "erroronion": { + "type": "hex", + "description": "the onion message returned" + } + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "pending" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "id": {}, + "partid": {}, + "groupid": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "bolt11": {}, + "bolt12": {} + } + } + } + ] + } } + } } diff --git a/doc/schemas/listtransactions.schema.json b/doc/schemas/listtransactions.schema.json index 7539a892168d..a5c5d5e5f70d 100644 --- a/doc/schemas/listtransactions.schema.json +++ b/doc/schemas/listtransactions.schema.json @@ -1,121 +1,176 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "transactions" ], - "properties": { - "transactions": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "hash", "rawtx", "blockheight", "txindex", "locktime", "version", "inputs", "outputs" ], - "properties": { - "hash": { - "type": "txid", - "description": "the transaction id" - }, - "rawtx": { - "type": "hex", - "description": "the raw transaction" - }, - "blockheight": { - "type": "u32", - "description": "the block height of this tx" - }, - "txindex": { - "type": "u32", - "description": "the transaction number within the block" - }, - "type": { - "type": "array", - "items": { - "type": "string", - "enum": [ "theirs", "deposit", "withdraw", "channel_funding","channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat" ], - "description": "Reason we care about this transaction (*EXPERIMENTAL_FEATURES* only)" - } - }, - "channel": { - "type": "short_channel_id", - "description": "the channel this transaction is associated with (*EXPERIMENTAL_FEATURES* only)" - }, - "locktime": { - "type": "u32", - "description": "The nLocktime for this tx" - }, - "version": { - "type": "u32", - "description": "The nVersion for this tx" - }, - "inputs": { - "type": "array", - "description": "Each input, in order", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "txid", "index", "sequence" ], - "properties": { - "txid": { - "type": "txid", - "description": "the transaction id spent" - }, - "index": { - "type": "u32", - "description": "the output spent" - }, - "sequence": { - "type": "u32", - "description": "the nSequence value" - }, - "type": { - "type": "string", - "enum": [ "theirs", "deposit", "withdraw", "channel_funding","channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat" ], - "description": "the purpose of this input (*EXPERIMENTAL_FEATURES* only)" - }, - "channel": { - "type": "short_channel_id", - "description": "the channel this input is associated with (*EXPERIMENTAL_FEATURES* only)" - } - } - } - }, - "outputs": { - "type": "array", - "description": "Each output, in order", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "index", "msat" , "scriptPubKey" ], - "properties": { - "index": { - "type": "u32", - "description": "the 0-based output number" - }, - "satoshis": { - "deprecated": true - }, - "msat": { - "type": "msat", - "description": "the amount of the output" - }, - "scriptPubKey": { - "type": "hex", - "description": "the scriptPubKey" - }, - "type": { - "type": "string", - "enum": [ "theirs", "deposit", "withdraw", "channel_funding","channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat" ], - "description": "the purpose of this output (*EXPERIMENTAL_FEATURES* only)" - }, - "channel": { - "type": "short_channel_id", - "description": "the channel this output is associated with (*EXPERIMENTAL_FEATURES* only)" - } - } - } - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "transactions" + ], + "properties": { + "transactions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "hash", + "rawtx", + "blockheight", + "txindex", + "locktime", + "version", + "inputs", + "outputs" + ], + "properties": { + "hash": { + "type": "txid", + "description": "the transaction id" + }, + "rawtx": { + "type": "hex", + "description": "the raw transaction" + }, + "blockheight": { + "type": "u32", + "description": "the block height of this tx" + }, + "txindex": { + "type": "u32", + "description": "the transaction number within the block" + }, + "type": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "theirs", + "deposit", + "withdraw", + "channel_funding", + "channel_mutual_close", + "channel_unilateral_close", + "channel_sweep", + "channel_htlc_success", + "channel_htlc_timeout", + "channel_penalty", + "channel_unilateral_cheat" + ], + "description": "Reason we care about this transaction (*EXPERIMENTAL_FEATURES* only)" + } + }, + "channel": { + "type": "short_channel_id", + "description": "the channel this transaction is associated with (*EXPERIMENTAL_FEATURES* only)" + }, + "locktime": { + "type": "u32", + "description": "The nLocktime for this tx" + }, + "version": { + "type": "u32", + "description": "The nVersion for this tx" + }, + "inputs": { + "type": "array", + "description": "Each input, in order", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "txid", + "index", + "sequence" + ], + "properties": { + "txid": { + "type": "txid", + "description": "the transaction id spent" + }, + "index": { + "type": "u32", + "description": "the output spent" + }, + "sequence": { + "type": "u32", + "description": "the nSequence value" + }, + "type": { + "type": "string", + "enum": [ + "theirs", + "deposit", + "withdraw", + "channel_funding", + "channel_mutual_close", + "channel_unilateral_close", + "channel_sweep", + "channel_htlc_success", + "channel_htlc_timeout", + "channel_penalty", + "channel_unilateral_cheat" + ], + "description": "the purpose of this input (*EXPERIMENTAL_FEATURES* only)" + }, + "channel": { + "type": "short_channel_id", + "description": "the channel this input is associated with (*EXPERIMENTAL_FEATURES* only)" + } + } + } + }, + "outputs": { + "type": "array", + "description": "Each output, in order", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "index", + "msat", + "scriptPubKey" + ], + "properties": { + "index": { + "type": "u32", + "description": "the 0-based output number" + }, + "satoshis": { + "deprecated": true + }, + "msat": { + "type": "msat", + "description": "the amount of the output" + }, + "scriptPubKey": { + "type": "hex", + "description": "the scriptPubKey" + }, + "type": { + "type": "string", + "enum": [ + "theirs", + "deposit", + "withdraw", + "channel_funding", + "channel_mutual_close", + "channel_unilateral_close", + "channel_sweep", + "channel_htlc_success", + "channel_htlc_timeout", + "channel_penalty", + "channel_unilateral_cheat" + ], + "description": "the purpose of this output (*EXPERIMENTAL_FEATURES* only)" + }, + "channel": { + "type": "short_channel_id", + "description": "the channel this output is associated with (*EXPERIMENTAL_FEATURES* only)" + } + } + } + } + } + } } + } } diff --git a/doc/schemas/multifundchannel.schema.json b/doc/schemas/multifundchannel.schema.json index 5022d32a9eda..18754c491033 100644 --- a/doc/schemas/multifundchannel.schema.json +++ b/doc/schemas/multifundchannel.schema.json @@ -1,83 +1,103 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "tx", "txid", "channel_ids" ], - "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" - }, - "channel_ids": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "id", "channel_id", "outnum" ], - "properties": { - "id": { - "type": "pubkey", - "description": "The peer we opened the channel with" - }, - "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`" - } - } - } - }, - "failed": { - "type": "array", - "description": "any peers we failed to open with (if *minchannels* was specified less than the number of destinations)", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "id", "method", "error" ], - "properties": { - "id": { - "type": "pubkey", - "description": "The peer we failed to open the channel with" - }, - "method": { - "type": "string", - "enum": [ "connect", "openchannel_init", "fundchannel_start", "fundchannel_complete" ], - "description": "What stage we failed at" - }, - "error": { - "type": "object", - "additionalProperties": false, - "required": [ "code", "message" ], - "properties": { - "code": { - "type": "integer", - "description": "JSON error code from failing stage" - }, - "message": { - "type": "string", - "description": "Message from stage" - }, - "data": { - "untyped": true, - "description": "Additional error data" - } - } - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "tx", + "txid", + "channel_ids" + ], + "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" + }, + "channel_ids": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "channel_id", + "outnum" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "The peer we opened the channel with" + }, + "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`" + } + } + } + }, + "failed": { + "type": "array", + "description": "any peers we failed to open with (if *minchannels* was specified less than the number of destinations)", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "method", + "error" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "The peer we failed to open the channel with" + }, + "method": { + "type": "string", + "enum": [ + "connect", + "openchannel_init", + "fundchannel_start", + "fundchannel_complete" + ], + "description": "What stage we failed at" + }, + "error": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "integer", + "description": "JSON error code from failing stage" + }, + "message": { + "type": "string", + "description": "Message from stage" + }, + "data": { + "untyped": true, + "description": "Additional error data" + } + } + } + } + } } + } } diff --git a/doc/schemas/multiwithdraw.schema.json b/doc/schemas/multiwithdraw.schema.json index d8095507d2d6..d3c831d17544 100644 --- a/doc/schemas/multiwithdraw.schema.json +++ b/doc/schemas/multiwithdraw.schema.json @@ -1,16 +1,19 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "tx", "txid" ], - "properties": { - "tx": { - "type": "hex", - "description": "The raw transaction which was sent" - }, - "txid": { - "type": "txid", - "description": "The txid of the **tx**" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "tx", + "txid" + ], + "properties": { + "tx": { + "type": "hex", + "description": "The raw transaction which was sent" + }, + "txid": { + "type": "txid", + "description": "The txid of the **tx**" } + } } diff --git a/doc/schemas/newaddr.schema.json b/doc/schemas/newaddr.schema.json index 4da493aacde5..8bfa737a9ec5 100644 --- a/doc/schemas/newaddr.schema.json +++ b/doc/schemas/newaddr.schema.json @@ -1,16 +1,16 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ ], - "properties": { - "bech32": { - "type": "string", - "description": "The bech32 (native segwit) address" - }, - "p2sh-segwit": { - "type": "string", - "description": "The p2sh-wrapped address" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": { + "bech32": { + "type": "string", + "description": "The bech32 (native segwit) address" + }, + "p2sh-segwit": { + "type": "string", + "description": "The p2sh-wrapped address" } + } } diff --git a/doc/schemas/notifications.schema.json b/doc/schemas/notifications.schema.json index b797a82c2f6f..1aad2dcae935 100644 --- a/doc/schemas/notifications.schema.json +++ b/doc/schemas/notifications.schema.json @@ -1,7 +1,6 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "properties": { - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": {} } diff --git a/doc/schemas/offer.schema.json b/doc/schemas/offer.schema.json index 45e0e7872f22..b57c306ed8ee 100644 --- a/doc/schemas/offer.schema.json +++ b/doc/schemas/offer.schema.json @@ -1,43 +1,53 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "used", "created" ], - "properties": { - "offer_id": { - "type": "hex", - "description": "the id of this offer (merkle hash of non-signature fields)", - "maxLength": 64, - "minLength": 64 - }, - "active": { - "type": "boolean", - "enum": [ true ], - "description": "whether this can still be used" - }, - "single_use": { - "type": "boolean", - "description": "whether this expires as soon as it's paid (reflects the *single_use* parameter)" - }, - "bolt12": { - "type": "string", - "description": "the bolt12 encoding of the offer" - }, - "bolt12_unsigned": { - "type": "string", - "description": "the bolt12 encoding of the offer, without a signature" - }, - "used": { - "type": "boolean", - "description": "True if an associated invoice has been paid" - }, - "created": { - "type": "boolean", - "description": "false if the offer already existed" - }, - "label": { - "type": "string", - "description": "the (optional) user-specified label" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "offer_id", + "active", + "single_use", + "bolt12", + "bolt12_unsigned", + "used", + "created" + ], + "properties": { + "offer_id": { + "type": "hex", + "description": "the id of this offer (merkle hash of non-signature fields)", + "maxLength": 64, + "minLength": 64 + }, + "active": { + "type": "boolean", + "enum": [ + true + ], + "description": "whether this can still be used" + }, + "single_use": { + "type": "boolean", + "description": "whether this expires as soon as it's paid (reflects the *single_use* parameter)" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 encoding of the offer" + }, + "bolt12_unsigned": { + "type": "string", + "description": "the bolt12 encoding of the offer, without a signature" + }, + "used": { + "type": "boolean", + "description": "True if an associated invoice has been paid" + }, + "created": { + "type": "boolean", + "description": "false if the offer already existed" + }, + "label": { + "type": "string", + "description": "the (optional) user-specified label" } + } } diff --git a/doc/schemas/offerout.schema.json b/doc/schemas/offerout.schema.json index 531d9dad8f8b..12f7972cc20c 100644 --- a/doc/schemas/offerout.schema.json +++ b/doc/schemas/offerout.schema.json @@ -1,45 +1,59 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "offer_id", "active", "single_use", "bolt12", "bolt12_unsigned", "used", "created" ], - "properties": { - "offer_id": { - "type": "hex", - "description": "the id of this offer (merkle hash of non-signature fields)", - "maxLength": 64, - "minLength": 64 - }, - "active": { - "type": "boolean", - "enum": [ true ], - "description": "whether this will pay a matching incoming invoice" - }, - "single_use": { - "type": "boolean", - "enum": [ true ], - "description": "whether this expires as soon as it's paid out" - }, - "bolt12": { - "type": "string", - "description": "the bolt12 encoding of the offer" - }, - "bolt12_unsigned": { - "type": "string", - "description": "the bolt12 encoding of the offer, without a signature" - }, - "used": { - "type": "boolean", - "enum": [ false ], - "description": "True if an incoming invoice has been paid" - }, - "created": { - "type": "boolean", - "description": "false if the offer already existed" - }, - "label": { - "type": "string", - "description": "the (optional) user-specified label" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "offer_id", + "active", + "single_use", + "bolt12", + "bolt12_unsigned", + "used", + "created" + ], + "properties": { + "offer_id": { + "type": "hex", + "description": "the id of this offer (merkle hash of non-signature fields)", + "maxLength": 64, + "minLength": 64 + }, + "active": { + "type": "boolean", + "enum": [ + true + ], + "description": "whether this will pay a matching incoming invoice" + }, + "single_use": { + "type": "boolean", + "enum": [ + true + ], + "description": "whether this expires as soon as it's paid out" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 encoding of the offer" + }, + "bolt12_unsigned": { + "type": "string", + "description": "the bolt12 encoding of the offer, without a signature" + }, + "used": { + "type": "boolean", + "enum": [ + false + ], + "description": "True if an incoming invoice has been paid" + }, + "created": { + "type": "boolean", + "description": "false if the offer already existed" + }, + "label": { + "type": "string", + "description": "the (optional) user-specified label" } + } } diff --git a/doc/schemas/openchannel_abort.schema.json b/doc/schemas/openchannel_abort.schema.json index a24f32847812..645f8fdb490f 100644 --- a/doc/schemas/openchannel_abort.schema.json +++ b/doc/schemas/openchannel_abort.schema.json @@ -1,22 +1,26 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "channel_id", "channel_canceled", "reason" ], - "properties": { - "channel_id": { - "type": "hex", - "description": "the channel id of the aborted channel", - "maxLength": 64, - "minLength": 64 - }, - "channel_canceled": { - "type": "boolean", - "description": "whether this is completely canceled (there may be remaining in-flight transactions)" - }, - "reason": { - "type": "string", - "description": "usually \"Abort requested\", but if it happened to fail at the same time it could be different" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channel_id", + "channel_canceled", + "reason" + ], + "properties": { + "channel_id": { + "type": "hex", + "description": "the channel id of the aborted channel", + "maxLength": 64, + "minLength": 64 + }, + "channel_canceled": { + "type": "boolean", + "description": "whether this is completely canceled (there may be remaining in-flight transactions)" + }, + "reason": { + "type": "string", + "description": "usually \"Abort requested\", but if it happened to fail at the same time it could be different" } + } } diff --git a/doc/schemas/openchannel_bump.schema.json b/doc/schemas/openchannel_bump.schema.json index 509a47022012..4b3d41ae1351 100644 --- a/doc/schemas/openchannel_bump.schema.json +++ b/doc/schemas/openchannel_bump.schema.json @@ -1,27 +1,34 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "channel_id", "psbt", "commitments_secured", "funding_serial" ], - "properties": { - "channel_id": { - "type": "hex", - "description": "the channel id of the channel", - "maxLength": 64, - "minLength": 64 - }, - "psbt": { - "type": "string", - "description": "the (incomplete) PSBT of the RBF transaction" - }, - "commitments_secured": { - "type": "boolean", - "enum": [ false ], - "description": "whether the *psbt* is complete" - }, - "funding_serial": { - "type": "u64", - "description": "the serial_id of the funding output in the *psbt*" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channel_id", + "psbt", + "commitments_secured", + "funding_serial" + ], + "properties": { + "channel_id": { + "type": "hex", + "description": "the channel id of the channel", + "maxLength": 64, + "minLength": 64 + }, + "psbt": { + "type": "string", + "description": "the (incomplete) PSBT of the RBF transaction" + }, + "commitments_secured": { + "type": "boolean", + "enum": [ + false + ], + "description": "whether the *psbt* is complete" + }, + "funding_serial": { + "type": "u64", + "description": "the serial_id of the funding output in the *psbt*" } + } } diff --git a/doc/schemas/openchannel_init.schema.json b/doc/schemas/openchannel_init.schema.json index 17698593ee13..767205ef661d 100644 --- a/doc/schemas/openchannel_init.schema.json +++ b/doc/schemas/openchannel_init.schema.json @@ -1,27 +1,34 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "channel_id", "psbt", "commitments_secured", "funding_serial" ], - "properties": { - "channel_id": { - "type": "hex", - "description": "the channel id of the channel", - "maxLength": 64, - "minLength": 64 - }, - "psbt": { - "type": "string", - "description": "the (incomplete) PSBT of the funding transaction" - }, - "commitments_secured": { - "type": "boolean", - "enum": [ false ], - "description": "whether the *psbt* is complete" - }, - "funding_serial": { - "type": "u64", - "description": "the serial_id of the funding output in the *psbt*" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channel_id", + "psbt", + "commitments_secured", + "funding_serial" + ], + "properties": { + "channel_id": { + "type": "hex", + "description": "the channel id of the channel", + "maxLength": 64, + "minLength": 64 + }, + "psbt": { + "type": "string", + "description": "the (incomplete) PSBT of the funding transaction" + }, + "commitments_secured": { + "type": "boolean", + "enum": [ + false + ], + "description": "whether the *psbt* is complete" + }, + "funding_serial": { + "type": "u64", + "description": "the serial_id of the funding output in the *psbt*" } + } } diff --git a/doc/schemas/openchannel_signed.schema.json b/doc/schemas/openchannel_signed.schema.json index cb144778e845..23b9a11d7e3f 100644 --- a/doc/schemas/openchannel_signed.schema.json +++ b/doc/schemas/openchannel_signed.schema.json @@ -1,22 +1,26 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "channel_id", "tx", "txid" ], - "properties": { - "channel_id": { - "type": "hex", - "description": "the channel id of the channel", - "maxLength": 64, - "minLength": 64 - }, - "tx": { - "type": "hex", - "description": "the funding transaction" - }, - "txid": { - "type": "txid", - "description": "The txid of the **tx**" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channel_id", + "tx", + "txid" + ], + "properties": { + "channel_id": { + "type": "hex", + "description": "the channel id of the channel", + "maxLength": 64, + "minLength": 64 + }, + "tx": { + "type": "hex", + "description": "the funding transaction" + }, + "txid": { + "type": "txid", + "description": "The txid of the **tx**" } + } } diff --git a/doc/schemas/openchannel_update.schema.json b/doc/schemas/openchannel_update.schema.json index 7f59d43d3adb..91acc1d5e0ea 100644 --- a/doc/schemas/openchannel_update.schema.json +++ b/doc/schemas/openchannel_update.schema.json @@ -1,30 +1,35 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "channel_id", "psbt", "commitments_secured", "funding_outnum" ], - "properties": { - "channel_id": { - "type": "hex", - "description": "the channel id of the channel", - "maxLength": 64, - "minLength": 64 - }, - "psbt": { - "type": "string", - "description": "the PSBT of the funding transaction" - }, - "commitments_secured": { - "type": "boolean", - "description": "whether the *psbt* is complete (if true, sign *psbt* and call `openchannel_signed` to complete the channel open)" - }, - "funding_outnum": { - "type": "u32", - "description": "The index of the funding output in the psbt" - }, - "close_to": { - "type": "hex", - "description": "scriptPubkey which we have to close to if we mutual close" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channel_id", + "psbt", + "commitments_secured", + "funding_outnum" + ], + "properties": { + "channel_id": { + "type": "hex", + "description": "the channel id of the channel", + "maxLength": 64, + "minLength": 64 + }, + "psbt": { + "type": "string", + "description": "the PSBT of the funding transaction" + }, + "commitments_secured": { + "type": "boolean", + "description": "whether the *psbt* is complete (if true, sign *psbt* and call `openchannel_signed` to complete the channel open)" + }, + "funding_outnum": { + "type": "u32", + "description": "The index of the funding output in the psbt" + }, + "close_to": { + "type": "hex", + "description": "scriptPubkey which we have to close to if we mutual close" } + } } diff --git a/doc/schemas/parsefeerate.schema.json b/doc/schemas/parsefeerate.schema.json index 0af834cfe3fd..e431936b1eb5 100644 --- a/doc/schemas/parsefeerate.schema.json +++ b/doc/schemas/parsefeerate.schema.json @@ -1,13 +1,13 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ ], - "properties": { - "perkw": { - "type": "u32", - "description": "Value of *feerate_str* in kilosipa", - "additionalProperties": false - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": { + "perkw": { + "type": "u32", + "description": "Value of *feerate_str* in kilosipa", + "additionalProperties": false } + } } diff --git a/doc/schemas/pay.schema.json b/doc/schemas/pay.schema.json index b3bd48ccc092..1a4f395f9fcd 100644 --- a/doc/schemas/pay.schema.json +++ b/doc/schemas/pay.schema.json @@ -1,55 +1,65 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "payment_preimage", "payment_hash", "created_at", "parts", "amount_msat", "amount_sent_msat", "status" ], - "properties": { - "payment_preimage": { - "type": "hex", - "description": "the proof of payment: SHA256 of this **payment_hash**", - "maxLength": 64, - "minLength": 64 - }, - "destination": { - "type": "pubkey", - "description": "the final destination of the payment" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "created_at": { - "type": "number", - "description": "the UNIX timestamp showing when this payment was initiated" - }, - "parts": { - "type": "u32", - "description": "how many attempts this took" - }, - "msatoshi": { - "deprecated": true - }, - "amount_msat": { - "type": "msat", - "description": "Amount the recipient received" - }, - "msatoshi_sent": { - "deprecated": true - }, - "amount_sent_msat": { - "type": "msat", - "description": "Total amount we sent (including fees)" - }, - "warning_partial_completion": { - "type": "string", - "description": "Not all parts of a multi-part payment have completed" - }, - "status": { - "type": "string", - "enum": [ "complete" ], - "description": "status of payment" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "payment_preimage", + "payment_hash", + "created_at", + "parts", + "amount_msat", + "amount_sent_msat", + "status" + ], + "properties": { + "payment_preimage": { + "type": "hex", + "description": "the proof of payment: SHA256 of this **payment_hash**", + "maxLength": 64, + "minLength": 64 + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "created_at": { + "type": "number", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "parts": { + "type": "u32", + "description": "how many attempts this took" + }, + "msatoshi": { + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "Amount the recipient received" + }, + "msatoshi_sent": { + "deprecated": true + }, + "amount_sent_msat": { + "type": "msat", + "description": "Total amount we sent (including fees)" + }, + "warning_partial_completion": { + "type": "string", + "description": "Not all parts of a multi-part payment have completed" + }, + "status": { + "type": "string", + "enum": [ + "complete" + ], + "description": "status of payment" } + } } diff --git a/doc/schemas/ping.schema.json b/doc/schemas/ping.schema.json index 5d0d132e787a..4ea5056a940b 100644 --- a/doc/schemas/ping.schema.json +++ b/doc/schemas/ping.schema.json @@ -1,12 +1,14 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "totlen" ], - "properties": { - "totlen": { - "type": "u16", - "description": "the answer length of the reply message (including header: 0 means no reply expected)" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "totlen" + ], + "properties": { + "totlen": { + "type": "u16", + "description": "the answer length of the reply message (including header: 0 means no reply expected)" } + } } diff --git a/doc/schemas/plugin.schema.json b/doc/schemas/plugin.schema.json index fab8966e27ba..f59c4c51375e 100644 --- a/doc/schemas/plugin.schema.json +++ b/doc/schemas/plugin.schema.json @@ -1,71 +1,95 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "command" ], - "properties": { - "command": { - "type": "string", - "enum": [ "start", "stop", "rescan", "startdir", "list" ], - "description": "the subcommand this is responding to" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "command" + ], + "properties": { + "command": { + "type": "string", + "enum": [ + "start", + "stop", + "rescan", + "startdir", + "list" + ], + "description": "the subcommand this is responding to" + } + }, + "allOf": [ + { + "if": { + "properties": { + "command": { + "type": "string", + "enum": [ + "start", + "startdir", + "rescan", + "list" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "command", + "plugins" + ], + "properties": { + "command": {}, + "plugins": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "active" + ], + "properties": { + "name": { + "type": "string", + "description": "full pathname of the plugin" + }, + "active": { + "type": "boolean", + "description": "status; since plugins are configured asynchronously, a freshly started plugin may not appear immediately." + } + } + } + } + } + } }, - "allOf": [ - { - "if": { - "properties": { - "command": { - "type": "string", - "enum": [ "start", "startdir", "rescan", "list" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "command", "plugins" ], - "properties": { - "command": { }, - "plugins": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "name", "active" ], - "properties": { - "name": { - "type": "string", - "description": "full pathname of the plugin" - }, - "active": { - "type": "boolean", - "description": "status; since plugins are configured asynchronously, a freshly started plugin may not appear immediately." - } - } - } - } - } - } - }, - { - "if": { - "properties": { - "command": { - "type": "string", - "enum": [ "stop" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "command", "result" ], - "properties": { - "command": { }, - "result": { - "type": "string", - "description": "A message saying it successfully stopped" - } - } - } - } - ] + { + "if": { + "properties": { + "command": { + "type": "string", + "enum": [ + "stop" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "command", + "result" + ], + "properties": { + "command": {}, + "result": { + "type": "string", + "description": "A message saying it successfully stopped" + } + } + } + } + ] } diff --git a/doc/schemas/reserveinputs.schema.json b/doc/schemas/reserveinputs.schema.json index 8adcc03d3db6..adfc66b9df76 100644 --- a/doc/schemas/reserveinputs.schema.json +++ b/doc/schemas/reserveinputs.schema.json @@ -1,39 +1,49 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "reservations" ], - "properties": { - "reservations": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "txid", "vout", "was_reserved", "reserved", "reserved_to_block" ], - "properties": { - "txid": { - "type": "txid", - "description": "the transaction id" - }, - "vout": { - "type": "u32", - "description": "the output number which was reserved" - }, - "was_reserved": { - "type": "boolean", - "description": "whether the input was already reserved" - }, - "reserved": { - "type": "boolean", - "enum": [ true ], - "description": "whether the input is now reserved" - }, - "reserved_to_block": { - "type": "u32", - "description": "what blockheight the reservation will expire" - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "reservations" + ], + "properties": { + "reservations": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "txid", + "vout", + "was_reserved", + "reserved", + "reserved_to_block" + ], + "properties": { + "txid": { + "type": "txid", + "description": "the transaction id" + }, + "vout": { + "type": "u32", + "description": "the output number which was reserved" + }, + "was_reserved": { + "type": "boolean", + "description": "whether the input was already reserved" + }, + "reserved": { + "type": "boolean", + "enum": [ + true + ], + "description": "whether the input is now reserved" + }, + "reserved_to_block": { + "type": "u32", + "description": "what blockheight the reservation will expire" + } + } + } } + } } diff --git a/doc/schemas/sendcustommsg.schema.json b/doc/schemas/sendcustommsg.schema.json index 21ce1eb8b250..cc61e6190979 100644 --- a/doc/schemas/sendcustommsg.schema.json +++ b/doc/schemas/sendcustommsg.schema.json @@ -1,11 +1,13 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "status" ], - "properties": { - "status": { - "type": "string", - "description": "Information about where message was queued" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "status" + ], + "properties": { + "status": { + "type": "string", + "description": "Information about where message was queued" } + } } diff --git a/doc/schemas/sendinvoice.schema.json b/doc/schemas/sendinvoice.schema.json index df21950a697a..d1be691bb25f 100644 --- a/doc/schemas/sendinvoice.schema.json +++ b/doc/schemas/sendinvoice.schema.json @@ -1,89 +1,106 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "label", "description", "payment_hash", "status", "expires_at" ], - "properties": { - "label": { - "type": "string", - "description": "unique label supplied at invoice creation" - }, - "description": { - "type": "string", - "description": "description used in the invoice" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "status": { - "type": "string", - "enum": [ "unpaid", "paid", "expired" ], - "description": "Whether it's paid, unpaid or unpayable" - }, - "expires_at": { - "type": "u64", - "description": "UNIX timestamp of when it will become / became unpayable" - }, - "msatoshi": { - "deprecated": "true" - }, - "amount_msat": { - "type": "msat", - "description": "the amount required to pay this invoice" - }, - "bolt12": { - "type": "string", - "description": "the BOLT12 string" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "label", + "description", + "payment_hash", + "status", + "expires_at" + ], + "properties": { + "label": { + "type": "string", + "description": "unique label supplied at invoice creation" }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "paid" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "pay_index", "amount_received_msat", "paid_at", "payment_preimage" ], - "properties": { - "label": { }, - "description": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "bolt12": { }, - "expires_at": { }, - "pay_index": { - "type": "u64", - "description": "Unique incrementing index for this payment" - }, - "msatoshi_received": { - "deprecated": true - }, - "amount_received_msat": { - "type": "msat", - "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" - }, - "paid_at": { - "type": "u64", - "description": "UNIX timestamp of when it was paid" - }, - "payment_preimage": { - "type": "hex", - "description": "proof of payment", - "maxLength": 64, - "minLength": 64 - } - } - } - } - ] + "description": { + "type": "string", + "description": "description used in the invoice" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ + "unpaid", + "paid", + "expired" + ], + "description": "Whether it's paid, unpaid or unpayable" + }, + "expires_at": { + "type": "u64", + "description": "UNIX timestamp of when it will become / became unpayable" + }, + "msatoshi": { + "deprecated": "true" + }, + "amount_msat": { + "type": "msat", + "description": "the amount required to pay this invoice" + }, + "bolt12": { + "type": "string", + "description": "the BOLT12 string" + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "paid" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "pay_index", + "amount_received_msat", + "paid_at", + "payment_preimage" + ], + "properties": { + "label": {}, + "description": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "bolt12": {}, + "expires_at": {}, + "pay_index": { + "type": "u64", + "description": "Unique incrementing index for this payment" + }, + "msatoshi_received": { + "deprecated": true + }, + "amount_received_msat": { + "type": "msat", + "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" + }, + "paid_at": { + "type": "u64", + "description": "UNIX timestamp of when it was paid" + }, + "payment_preimage": { + "type": "hex", + "description": "proof of payment", + "maxLength": 64, + "minLength": 64 + } + } + } + } + ] } diff --git a/doc/schemas/sendonion.schema.json b/doc/schemas/sendonion.schema.json index 060078036a2c..56474b44ed26 100644 --- a/doc/schemas/sendonion.schema.json +++ b/doc/schemas/sendonion.schema.json @@ -1,127 +1,142 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "id", "payment_hash", "status", "created_at", "amount_sent_msat" ], - "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", "complete" ], - "description": "status of the payment (could be complete if already sent previously)" - }, - "msatoshi": { - "deprecated": true - }, - "amount_msat": { - "type": "msat", - "description": "The amount delivered to destination (if known)" - }, - "destination": { - "type": "pubkey", - "description": "the final destination of the payment if known" - }, - "created_at": { - "type": "u64", - "description": "the UNIX timestamp showing when this payment was initiated" - }, - "msatoshi_sent": { - "deprecated": true - }, - "amount_sent_msat": { - "type": "msat", - "description": "The amount sent" - }, - "label": { - "type": "string", - "description": "the label, if given to sendpay" - }, - "bolt11": { - "type": "string", - "description": "the bolt11 string (if supplied)" - }, - "bolt12": { - "type": "string", - "description": "the bolt12 string (if supplied: **experimental-offers** only)." - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "id", + "payment_hash", + "status", + "created_at", + "amount_sent_msat" + ], + "properties": { + "id": { + "type": "u64", + "description": "unique ID for this payment attempt" }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "complete" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "payment_preimage" ], - "properties": { - "id": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "groupid": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "bolt11": { }, - "bolt12": { }, - "payment_preimage": { - "type": "hex", - "description": "the proof of payment: SHA256 of this **payment_hash**", - "maxLength": 64, - "minLength": 64 - } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "pending" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ ], - "properties": { - "id": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "groupid": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "bolt11": { }, - "bolt12": { }, - "message": { - "type": "string", - "description": "Monitor status with listpays or waitsendpay" - } - } - } - } - ] + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ + "pending", + "complete" + ], + "description": "status of the payment (could be complete if already sent previously)" + }, + "msatoshi": { + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "The amount delivered to destination (if known)" + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment if known" + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "msatoshi_sent": { + "deprecated": true + }, + "amount_sent_msat": { + "type": "msat", + "description": "The amount sent" + }, + "label": { + "type": "string", + "description": "the label, if given to sendpay" + }, + "bolt11": { + "type": "string", + "description": "the bolt11 string (if supplied)" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 string (if supplied: **experimental-offers** only)." + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "complete" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "payment_preimage" + ], + "properties": { + "id": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "groupid": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "bolt11": {}, + "bolt12": {}, + "payment_preimage": { + "type": "hex", + "description": "the proof of payment: SHA256 of this **payment_hash**", + "maxLength": 64, + "minLength": 64 + } + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "pending" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [], + "properties": { + "id": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "groupid": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "bolt11": {}, + "bolt12": {}, + "message": { + "type": "string", + "description": "Monitor status with listpays or waitsendpay" + } + } + } + } + ] } diff --git a/doc/schemas/sendonionmessage.schema.json b/doc/schemas/sendonionmessage.schema.json index 6354c7d8208f..65571ad4c703 100644 --- a/doc/schemas/sendonionmessage.schema.json +++ b/doc/schemas/sendonionmessage.schema.json @@ -1,8 +1,7 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ ], - "properties": { - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": {} } diff --git a/doc/schemas/sendpay.schema.json b/doc/schemas/sendpay.schema.json index 5089d45534de..fec96ba54804 100644 --- a/doc/schemas/sendpay.schema.json +++ b/doc/schemas/sendpay.schema.json @@ -1,137 +1,154 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "id", "payment_hash", "status", "created_at", "amount_sent_msat" ], - "properties": { - "id": { - "type": "u64", - "description": "unique ID for this payment attempt" - }, - "groupid": { - "type": "u64", - "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "status": { - "type": "string", - "enum": [ "pending", "complete" ], - "description": "status of the payment (could be complete if already sent previously)" - }, - "msatoshi": { - "deprecated": true - }, - "amount_msat": { - "type": "msat", - "description": "The amount delivered to destination (if known)" - }, - "destination": { - "type": "pubkey", - "description": "the final destination of the payment if known" - }, - "created_at": { - "type": "u64", - "description": "the UNIX timestamp showing when this payment was initiated" - }, - "msatoshi_sent": { - "deprecated": true - }, - "amount_sent_msat": { - "type": "msat", - "description": "The amount sent" - }, - "label": { - "type": "string", - "description": "the *label*, if given to sendpay" - }, - "partid": { - "type": "u64", - "description": "the *partid*, if given to sendpay" - }, - "bolt11": { - "type": "string", - "description": "the bolt11 string (if supplied)" - }, - "bolt12": { - "type": "string", - "description": "the bolt12 string (if supplied: **experimental-offers** only)." - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "id", + "payment_hash", + "status", + "created_at", + "amount_sent_msat" + ], + "properties": { + "id": { + "type": "u64", + "description": "unique ID for this payment attempt" }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "complete" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "payment_preimage" ], - "properties": { - "id": { }, - "groupid": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "partid": { }, - "bolt11": { }, - "bolt12": { }, - "payment_preimage": { - "type": "hex", - "description": "the proof of payment: SHA256 of this **payment_hash**", - "maxLength": 64, - "minLength": 64 - } - } - } - }, - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "pending" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "message" ], - "properties": { - "id": { }, - "groupid": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "partid": { }, - "bolt11": { }, - "bolt12": { }, - "message": { - "type": "string", - "description": "Monitor status with listpays or waitsendpay" - } - } - } - } - ] + "groupid": { + "type": "u64", + "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ + "pending", + "complete" + ], + "description": "status of the payment (could be complete if already sent previously)" + }, + "msatoshi": { + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "The amount delivered to destination (if known)" + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment if known" + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "msatoshi_sent": { + "deprecated": true + }, + "amount_sent_msat": { + "type": "msat", + "description": "The amount sent" + }, + "label": { + "type": "string", + "description": "the *label*, if given to sendpay" + }, + "partid": { + "type": "u64", + "description": "the *partid*, if given to sendpay" + }, + "bolt11": { + "type": "string", + "description": "the bolt11 string (if supplied)" + }, + "bolt12": { + "type": "string", + "description": "the bolt12 string (if supplied: **experimental-offers** only)." + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "complete" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "payment_preimage" + ], + "properties": { + "id": {}, + "groupid": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "partid": {}, + "bolt11": {}, + "bolt12": {}, + "payment_preimage": { + "type": "hex", + "description": "the proof of payment: SHA256 of this **payment_hash**", + "maxLength": 64, + "minLength": 64 + } + } + } + }, + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "pending" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "message" + ], + "properties": { + "id": {}, + "groupid": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "partid": {}, + "bolt11": {}, + "bolt12": {}, + "message": { + "type": "string", + "description": "Monitor status with listpays or waitsendpay" + } + } + } + } + ] } diff --git a/doc/schemas/sendpsbt.schema.json b/doc/schemas/sendpsbt.schema.json index d8095507d2d6..d3c831d17544 100644 --- a/doc/schemas/sendpsbt.schema.json +++ b/doc/schemas/sendpsbt.schema.json @@ -1,16 +1,19 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "tx", "txid" ], - "properties": { - "tx": { - "type": "hex", - "description": "The raw transaction which was sent" - }, - "txid": { - "type": "txid", - "description": "The txid of the **tx**" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "tx", + "txid" + ], + "properties": { + "tx": { + "type": "hex", + "description": "The raw transaction which was sent" + }, + "txid": { + "type": "txid", + "description": "The txid of the **tx**" } + } } diff --git a/doc/schemas/setchannelfee.schema.json b/doc/schemas/setchannelfee.schema.json index fab668c8ca00..9f6113a0a88d 100644 --- a/doc/schemas/setchannelfee.schema.json +++ b/doc/schemas/setchannelfee.schema.json @@ -1,41 +1,48 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "base", "ppm", "channels" ], - "properties": { - "base": { - "type": "u32", - "description": "The fee_base_msat value" - }, - "ppm": { - "type": "u32", - "description": "The fee_proportional_millionths value" - }, - "channels": { - "type": "array", - "description": "channel(s) whose rate is now set", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ "peer_id", "channel_id" ], - "properties": { - "peer_id": { - "type": "pubkey", - "description": "The node_id of the peer" - }, - "channel_id": { - "type": "hex", - "description": "The channel_id of the channel", - "minLength": 64, - "maxLength": 64 - }, - "short_channel_id": { - "type": "short_channel_id", - "description": "the short_channel_id (if locked in)" - } - } - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "base", + "ppm", + "channels" + ], + "properties": { + "base": { + "type": "u32", + "description": "The fee_base_msat value" + }, + "ppm": { + "type": "u32", + "description": "The fee_proportional_millionths value" + }, + "channels": { + "type": "array", + "description": "channel(s) whose rate is now set", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "peer_id", + "channel_id" + ], + "properties": { + "peer_id": { + "type": "pubkey", + "description": "The node_id of the peer" + }, + "channel_id": { + "type": "hex", + "description": "The channel_id of the channel", + "minLength": 64, + "maxLength": 64 + }, + "short_channel_id": { + "type": "short_channel_id", + "description": "the short_channel_id (if locked in)" + } + } + } } + } } diff --git a/doc/schemas/signmessage.schema.json b/doc/schemas/signmessage.schema.json index 4c866e72f751..42eea4e774f4 100644 --- a/doc/schemas/signmessage.schema.json +++ b/doc/schemas/signmessage.schema.json @@ -1,24 +1,28 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "signature", "recid", "zbase" ], - "properties": { - "signature": { - "type": "hex", - "description": "The signature", - "minLength": 128, - "maxLength": 128 - }, - "recid": { - "type": "hex", - "description": "The recovery id (0, 1, 2 or 3)", - "minLength": 2, - "maxLength": 2 - }, - "zbase": { - "type": "string", - "description": "*signature* and *recid* encoded in a style compatible with **lnd**'s [SignMessageRequest](https://api.lightning.community/#grpc-request-signmessagerequest)" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "signature", + "recid", + "zbase" + ], + "properties": { + "signature": { + "type": "hex", + "description": "The signature", + "minLength": 128, + "maxLength": 128 + }, + "recid": { + "type": "hex", + "description": "The recovery id (0, 1, 2 or 3)", + "minLength": 2, + "maxLength": 2 + }, + "zbase": { + "type": "string", + "description": "*signature* and *recid* encoded in a style compatible with **lnd**'s [SignMessageRequest](https://api.lightning.community/#grpc-request-signmessagerequest)" } + } } diff --git a/doc/schemas/signpsbt.schema.json b/doc/schemas/signpsbt.schema.json index f17efe20165b..271f58c9bd78 100644 --- a/doc/schemas/signpsbt.schema.json +++ b/doc/schemas/signpsbt.schema.json @@ -1,12 +1,14 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "signed_psbt" ], - "properties": { - "signed_psbt": { - "type": "string", - "description": "The fully signed PSBT" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "signed_psbt" + ], + "properties": { + "signed_psbt": { + "type": "string", + "description": "The fully signed PSBT" } + } } diff --git a/doc/schemas/stop.schema.json b/doc/schemas/stop.schema.json index b4110edf934a..00105c74383b 100644 --- a/doc/schemas/stop.schema.json +++ b/doc/schemas/stop.schema.json @@ -1,5 +1,7 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "string", - "enum": [ "Shutdown complete" ] + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "string", + "enum": [ + "Shutdown complete" + ] } diff --git a/doc/schemas/txdiscard.schema.json b/doc/schemas/txdiscard.schema.json index 33576f4179f9..956380618c6d 100644 --- a/doc/schemas/txdiscard.schema.json +++ b/doc/schemas/txdiscard.schema.json @@ -1,15 +1,18 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "unsigned_tx", "txid" ], - "properties": { - "unsigned_tx": { - "type": "hex", - "description": "the unsigned transaction" - }, - "txid": { - "type": "txid", - "description": "the transaction id of *unsigned_tx*" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "unsigned_tx", + "txid" + ], + "properties": { + "unsigned_tx": { + "type": "hex", + "description": "the unsigned transaction" + }, + "txid": { + "type": "txid", + "description": "the transaction id of *unsigned_tx*" } + } } diff --git a/doc/schemas/txprepare.schema.json b/doc/schemas/txprepare.schema.json index 7ee0df8c1ab6..2377c322c123 100644 --- a/doc/schemas/txprepare.schema.json +++ b/doc/schemas/txprepare.schema.json @@ -1,19 +1,23 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "psbt", "unsigned_tx", "txid" ], - "properties": { - "psbt": { - "type": "string", - "description": "the PSBT representing the unsigned transaction" - }, - "unsigned_tx": { - "type": "hex", - "description": "the unsigned transaction" - }, - "txid": { - "type": "txid", - "description": "the transaction id of *unsigned_tx*; you hand this to lightning-txsend(7) or lightning-txdiscard(7), as the inputs of this transaction are reserved." - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "psbt", + "unsigned_tx", + "txid" + ], + "properties": { + "psbt": { + "type": "string", + "description": "the PSBT representing the unsigned transaction" + }, + "unsigned_tx": { + "type": "hex", + "description": "the unsigned transaction" + }, + "txid": { + "type": "txid", + "description": "the transaction id of *unsigned_tx*; you hand this to lightning-txsend(7) or lightning-txdiscard(7), as the inputs of this transaction are reserved." } + } } diff --git a/doc/schemas/txsend.schema.json b/doc/schemas/txsend.schema.json index 51f87a2e89ef..e7a47f1ca164 100644 --- a/doc/schemas/txsend.schema.json +++ b/doc/schemas/txsend.schema.json @@ -1,19 +1,23 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "psbt", "tx", "txid" ], - "properties": { - "psbt": { - "type": "string", - "description": "the completed PSBT representing the signed transaction" - }, - "tx": { - "type": "hex", - "description": "the fully signed transaction" - }, - "txid": { - "type": "txid", - "description": "the transaction id of *tx*" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "psbt", + "tx", + "txid" + ], + "properties": { + "psbt": { + "type": "string", + "description": "the completed PSBT representing the signed transaction" + }, + "tx": { + "type": "hex", + "description": "the fully signed transaction" + }, + "txid": { + "type": "txid", + "description": "the transaction id of *tx*" } + } } diff --git a/doc/schemas/unreserveinputs.schema.json b/doc/schemas/unreserveinputs.schema.json index b816140344de..264f603a26cf 100644 --- a/doc/schemas/unreserveinputs.schema.json +++ b/doc/schemas/unreserveinputs.schema.json @@ -1,59 +1,70 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": false, - "required": [ "reservations" ], - "properties": { - "reservations": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": true, - "required": [ "txid", "vout", "was_reserved", "reserved" ], - "properties": { - "txid": { - "type": "txid", - "description": "the transaction id" - }, - "vout": { - "type": "u32", - "description": "the output number which was reserved" - }, - "was_reserved": { - "type": "boolean", - "description": "whether the input was already reserved (usually `true`)" - }, - "reserved": { - "type": "boolean", - "description": "whether the input is now reserved (may still be `true` if it was reserved for a long time)" - } - }, - "allOf": [ - { - "if": { - "additionalProperties": true, - "properties": { - "reserved": { - "enum": [ true ] - } - } - }, - "then": { - "required": [ "reserved_to_block" ], - "properties": { - "txid": { }, - "vout": { }, - "was_reserved": { }, - "reserved": { }, - "reserved_to_block": { - "type": "u32", - "description": "what blockheight the reservation will expire" - } - } - } - } - ] - } - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "reservations" + ], + "properties": { + "reservations": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "txid", + "vout", + "was_reserved", + "reserved" + ], + "properties": { + "txid": { + "type": "txid", + "description": "the transaction id" + }, + "vout": { + "type": "u32", + "description": "the output number which was reserved" + }, + "was_reserved": { + "type": "boolean", + "description": "whether the input was already reserved (usually `true`)" + }, + "reserved": { + "type": "boolean", + "description": "whether the input is now reserved (may still be `true` if it was reserved for a long time)" + } + }, + "allOf": [ + { + "if": { + "additionalProperties": true, + "properties": { + "reserved": { + "enum": [ + true + ] + } + } + }, + "then": { + "required": [ + "reserved_to_block" + ], + "properties": { + "txid": {}, + "vout": {}, + "was_reserved": {}, + "reserved": {}, + "reserved_to_block": { + "type": "u32", + "description": "what blockheight the reservation will expire" + } + } + } + } + ] + } } + } } diff --git a/doc/schemas/utxopsbt.schema.json b/doc/schemas/utxopsbt.schema.json index 85c150239ffd..d24ad03488f7 100644 --- a/doc/schemas/utxopsbt.schema.json +++ b/doc/schemas/utxopsbt.schema.json @@ -1,60 +1,73 @@ { - "$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 *reserve* was true or a non-zero number, 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", - "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" - } - } - } - } + "$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 *reserve* was true or a non-zero number, 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", + "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/waitanyinvoice.schema.json b/doc/schemas/waitanyinvoice.schema.json index 85ee1b6e7127..8c4b9edada54 100644 --- a/doc/schemas/waitanyinvoice.schema.json +++ b/doc/schemas/waitanyinvoice.schema.json @@ -1,108 +1,124 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "label", "description", "payment_hash", "status", "expires_at" ], - "properties": { - "label": { - "type": "string", - "description": "unique label supplied at invoice creation" - }, - "description": { - "type": "string", - "description": "description used in the invoice" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "status": { - "type": "string", - "enum": [ "paid", "expired" ], - "description": "Whether it's paid or expired" - }, - "expires_at": { - "type": "u64", - "description": "UNIX timestamp of when it will become / became unpayable" - }, - "msatoshi": { - "deprecated": "true" - }, - "amount_msat": { - "type": "msat", - "description": "the amount required to pay this invoice" - }, - "bolt11": { - "type": "string", - "description": "the BOLT11 string (always present unless *bolt12* is)" - }, - "bolt12": { - "type": "string", - "description": "the BOLT12 string (always present unless *bolt11* is)" - } - }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "paid" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "pay_index", "amount_received_msat", "paid_at", "payment_preimage" ], - "properties": { - "label": { }, - "description": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "bolt11": { }, - "bolt12": { }, - "expires_at": { }, - "pay_index": { - "type": "u64", - "description": "Unique incrementing index for this payment" - }, - "msatoshi_received": { - "deprecated": true - }, - "amount_received_msat": { - "type": "msat", - "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" - }, - "paid_at": { - "type": "u64", - "description": "UNIX timestamp of when it was paid" - }, - "payment_preimage": { - "type": "hex", - "description": "proof of payment", - "maxLength": 64, - "minLength": 64 - } - } - }, - "else": { - "additionalProperties": false, - "properties": { - "label": { }, - "description": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "bolt11": { }, - "bolt12": { }, - "expires_at": { } - } - } - } - ] + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "label", + "description", + "payment_hash", + "status", + "expires_at" + ], + "properties": { + "label": { + "type": "string", + "description": "unique label supplied at invoice creation" + }, + "description": { + "type": "string", + "description": "description used in the invoice" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ + "paid", + "expired" + ], + "description": "Whether it's paid or expired" + }, + "expires_at": { + "type": "u64", + "description": "UNIX timestamp of when it will become / became unpayable" + }, + "msatoshi": { + "deprecated": "true" + }, + "amount_msat": { + "type": "msat", + "description": "the amount required to pay this invoice" + }, + "bolt11": { + "type": "string", + "description": "the BOLT11 string (always present unless *bolt12* is)" + }, + "bolt12": { + "type": "string", + "description": "the BOLT12 string (always present unless *bolt11* is)" + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "paid" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "pay_index", + "amount_received_msat", + "paid_at", + "payment_preimage" + ], + "properties": { + "label": {}, + "description": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "bolt11": {}, + "bolt12": {}, + "expires_at": {}, + "pay_index": { + "type": "u64", + "description": "Unique incrementing index for this payment" + }, + "msatoshi_received": { + "deprecated": true + }, + "amount_received_msat": { + "type": "msat", + "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" + }, + "paid_at": { + "type": "u64", + "description": "UNIX timestamp of when it was paid" + }, + "payment_preimage": { + "type": "hex", + "description": "proof of payment", + "maxLength": 64, + "minLength": 64 + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "label": {}, + "description": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "bolt11": {}, + "bolt12": {}, + "expires_at": {} + } + } + } + ] } diff --git a/doc/schemas/waitblockheight.schema.json b/doc/schemas/waitblockheight.schema.json index 5b889377ea5f..71fd35ca3897 100644 --- a/doc/schemas/waitblockheight.schema.json +++ b/doc/schemas/waitblockheight.schema.json @@ -1,11 +1,13 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "blockheight" ], - "properties": { - "blockheight": { - "type": "u32", - "description": "The current block height (>= *blockheight* parameter)" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "blockheight" + ], + "properties": { + "blockheight": { + "type": "u32", + "description": "The current block height (>= *blockheight* parameter)" } + } } diff --git a/doc/schemas/waitinvoice.schema.json b/doc/schemas/waitinvoice.schema.json index 85ee1b6e7127..8c4b9edada54 100644 --- a/doc/schemas/waitinvoice.schema.json +++ b/doc/schemas/waitinvoice.schema.json @@ -1,108 +1,124 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "label", "description", "payment_hash", "status", "expires_at" ], - "properties": { - "label": { - "type": "string", - "description": "unique label supplied at invoice creation" - }, - "description": { - "type": "string", - "description": "description used in the invoice" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "status": { - "type": "string", - "enum": [ "paid", "expired" ], - "description": "Whether it's paid or expired" - }, - "expires_at": { - "type": "u64", - "description": "UNIX timestamp of when it will become / became unpayable" - }, - "msatoshi": { - "deprecated": "true" - }, - "amount_msat": { - "type": "msat", - "description": "the amount required to pay this invoice" - }, - "bolt11": { - "type": "string", - "description": "the BOLT11 string (always present unless *bolt12* is)" - }, - "bolt12": { - "type": "string", - "description": "the BOLT12 string (always present unless *bolt11* is)" - } - }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "paid" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "pay_index", "amount_received_msat", "paid_at", "payment_preimage" ], - "properties": { - "label": { }, - "description": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "bolt11": { }, - "bolt12": { }, - "expires_at": { }, - "pay_index": { - "type": "u64", - "description": "Unique incrementing index for this payment" - }, - "msatoshi_received": { - "deprecated": true - }, - "amount_received_msat": { - "type": "msat", - "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" - }, - "paid_at": { - "type": "u64", - "description": "UNIX timestamp of when it was paid" - }, - "payment_preimage": { - "type": "hex", - "description": "proof of payment", - "maxLength": 64, - "minLength": 64 - } - } - }, - "else": { - "additionalProperties": false, - "properties": { - "label": { }, - "description": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "bolt11": { }, - "bolt12": { }, - "expires_at": { } - } - } - } - ] + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "label", + "description", + "payment_hash", + "status", + "expires_at" + ], + "properties": { + "label": { + "type": "string", + "description": "unique label supplied at invoice creation" + }, + "description": { + "type": "string", + "description": "description used in the invoice" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ + "paid", + "expired" + ], + "description": "Whether it's paid or expired" + }, + "expires_at": { + "type": "u64", + "description": "UNIX timestamp of when it will become / became unpayable" + }, + "msatoshi": { + "deprecated": "true" + }, + "amount_msat": { + "type": "msat", + "description": "the amount required to pay this invoice" + }, + "bolt11": { + "type": "string", + "description": "the BOLT11 string (always present unless *bolt12* is)" + }, + "bolt12": { + "type": "string", + "description": "the BOLT12 string (always present unless *bolt11* is)" + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "paid" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "pay_index", + "amount_received_msat", + "paid_at", + "payment_preimage" + ], + "properties": { + "label": {}, + "description": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "bolt11": {}, + "bolt12": {}, + "expires_at": {}, + "pay_index": { + "type": "u64", + "description": "Unique incrementing index for this payment" + }, + "msatoshi_received": { + "deprecated": true + }, + "amount_received_msat": { + "type": "msat", + "description": "the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay)" + }, + "paid_at": { + "type": "u64", + "description": "UNIX timestamp of when it was paid" + }, + "payment_preimage": { + "type": "hex", + "description": "proof of payment", + "maxLength": 64, + "minLength": 64 + } + } + }, + "else": { + "additionalProperties": false, + "properties": { + "label": {}, + "description": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "bolt11": {}, + "bolt12": {}, + "expires_at": {} + } + } + } + ] } diff --git a/doc/schemas/waitsendpay.schema.json b/doc/schemas/waitsendpay.schema.json index 5932a9b37640..853a07610db2 100644 --- a/doc/schemas/waitsendpay.schema.json +++ b/doc/schemas/waitsendpay.schema.json @@ -1,103 +1,115 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ "id", "payment_hash", "status", "created_at", "amount_sent_msat" ], - "properties": { - "id": { - "type": "u64", - "description": "unique ID for this payment attempt" - }, - "groupid": { - "type": "u64", - "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" - }, - "payment_hash": { - "type": "hex", - "description": "the hash of the *payment_preimage* which will prove payment", - "maxLength": 64, - "minLength": 64 - }, - "status": { - "type": "string", - "enum": [ "complete" ], - "description": "status of the payment" - }, - "msatoshi": { - "deprecated": true - }, - "amount_msat": { - "type": "msat", - "description": "The amount delivered to destination (if known)" - }, - "destination": { - "type": "pubkey", - "description": "the final destination of the payment if known" - }, - "created_at": { - "type": "u64", - "description": "the UNIX timestamp showing when this payment was initiated" - }, - "msatoshi_sent": { - "deprecated": true - }, - "amount_sent_msat": { - "type": "msat", - "description": "The amount sent" - }, - "label": { - "type": "string", - "description": "the label, if given to sendpay" - }, - "partid": { - "type": "u64", - "description": "the *partid*, 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)." - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": true, + "required": [ + "id", + "payment_hash", + "status", + "created_at", + "amount_sent_msat" + ], + "properties": { + "id": { + "type": "u64", + "description": "unique ID for this payment attempt" }, - "allOf": [ - { - "if": { - "properties": { - "status": { - "type": "string", - "enum": [ "complete" ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ "payment_preimage" ], - "properties": { - "id": { }, - "groupid": { }, - "payment_hash": { }, - "status": { }, - "msatoshi": { }, - "amount_msat": { }, - "destination": { }, - "created_at": { }, - "msatoshi_sent": { }, - "amount_sent_msat": { }, - "label": { }, - "partid": { }, - "bolt11": { }, - "bolt12": { }, - "payment_preimage": { - "type": "hex", - "description": "the proof of payment: SHA256 of this **payment_hash**", - "maxLength": 64, - "minLength": 64 - } - } - } - } - ] + "groupid": { + "type": "u64", + "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" + }, + "payment_hash": { + "type": "hex", + "description": "the hash of the *payment_preimage* which will prove payment", + "maxLength": 64, + "minLength": 64 + }, + "status": { + "type": "string", + "enum": [ + "complete" + ], + "description": "status of the payment" + }, + "msatoshi": { + "deprecated": true + }, + "amount_msat": { + "type": "msat", + "description": "The amount delivered to destination (if known)" + }, + "destination": { + "type": "pubkey", + "description": "the final destination of the payment if known" + }, + "created_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was initiated" + }, + "msatoshi_sent": { + "deprecated": true + }, + "amount_sent_msat": { + "type": "msat", + "description": "The amount sent" + }, + "label": { + "type": "string", + "description": "the label, if given to sendpay" + }, + "partid": { + "type": "u64", + "description": "the *partid*, 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)." + } + }, + "allOf": [ + { + "if": { + "properties": { + "status": { + "type": "string", + "enum": [ + "complete" + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "payment_preimage" + ], + "properties": { + "id": {}, + "groupid": {}, + "payment_hash": {}, + "status": {}, + "msatoshi": {}, + "amount_msat": {}, + "destination": {}, + "created_at": {}, + "msatoshi_sent": {}, + "amount_sent_msat": {}, + "label": {}, + "partid": {}, + "bolt11": {}, + "bolt12": {}, + "payment_preimage": { + "type": "hex", + "description": "the proof of payment: SHA256 of this **payment_hash**", + "maxLength": 64, + "minLength": 64 + } + } + } + } + ] } diff --git a/doc/schemas/withdraw.schema.json b/doc/schemas/withdraw.schema.json index 3e2dbac42cc2..3249ccf042c2 100644 --- a/doc/schemas/withdraw.schema.json +++ b/doc/schemas/withdraw.schema.json @@ -1,19 +1,23 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "required": [ "psbt", "tx", "txid" ], - "properties": { - "tx": { - "type": "hex", - "description": "the fully signed bitcoin transaction" - }, - "txid": { - "type": "txid", - "description": "the transaction id of *tx*" - }, - "psbt": { - "type": "string", - "description": "the PSBT representing the unsigned transaction" - } + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "psbt", + "tx", + "txid" + ], + "properties": { + "tx": { + "type": "hex", + "description": "the fully signed bitcoin transaction" + }, + "txid": { + "type": "txid", + "description": "the transaction id of *tx*" + }, + "psbt": { + "type": "string", + "description": "the PSBT representing the unsigned transaction" } + } } From ed80590fe1b9f38d18c9067ab4c368870612ab64 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 4 Nov 2021 15:20:33 +0100 Subject: [PATCH 0053/1530] doc: Document max-dust-htlc-exposure-msat the command line option Signed-off-by: Vincenzo Palazzo --- doc/lightningd-config.5.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 61f3c8c545e5..5940f2c282c1 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -438,6 +438,9 @@ Disable the DNS bootstrapping mechanism to find a node by its node ID. Set a Tor control password, which may be needed for *autotor:* to authenticate to the Tor control port. + **max-dust-htlc-exposure-msat** +Option which limits the total amount of sats to be allowed as dust on a channel. + ### Lightning Plugins lightningd(8) supports plugins, which offer additional configuration @@ -500,7 +503,7 @@ which are in draft status in the BOLT specifications. **experimental-offers** Specifying this enables the `offers` and `fetchinvoice` plugins and -corresponding functionality, which are in draft status as BOLT12. +corresponding functionality, which are in draft status as BOLT12. This usually requires **experimental-onion-messages** as well. See lightning-offer(7) and lightning-fetchinvoice(7). @@ -517,7 +520,7 @@ remote node has opened a channel but claims it used the incorrect txid negotiate a clean shutdown with the txid they offer. **experimental-dual-fund** - + Specifying this enables support for the dual funding protocol, allowing both parties to contribute funds to a channel. The decision about whether to add funds or not to a proposed channel is handled @@ -560,4 +563,3 @@ COPYING Note: the modules in the ccan/ directory have their own licenses, but the rest of the code is covered by the BSD-style MIT license. - From 9c45557959333b0f2ca534c663d8665d32350dcd Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 4 Nov 2021 19:29:46 +0100 Subject: [PATCH 0054/1530] doc: Fixed doc check with the last options Changelog-None Signed-off-by: Vincenzo Palazzo --- doc/lightningd-config.5.md | 8 ++++---- doc/undoc-flags.json | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 5940f2c282c1..7e4c9daa32f3 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -309,6 +309,9 @@ transactions: default is 100. Number of HTLCs one channel can handle concurrently in each direction. Should be between 1 and 483 (default 30). + **max-dust-htlc-exposure-msat**=*MILLISATOSHI* +Option which limits the total amount of sats to be allowed as dust on a channel. + **cltv-delta**=*BLOCKS* The number of blocks between incoming payments and outgoing payments: this needs to be enough to make sure that if we have to, we can close @@ -438,9 +441,6 @@ Disable the DNS bootstrapping mechanism to find a node by its node ID. Set a Tor control password, which may be needed for *autotor:* to authenticate to the Tor control port. - **max-dust-htlc-exposure-msat** -Option which limits the total amount of sats to be allowed as dust on a channel. - ### Lightning Plugins lightningd(8) supports plugins, which offer additional configuration @@ -527,7 +527,7 @@ about whether to add funds or not to a proposed channel is handled automatically by a plugin that implements the appropriate logic for your needs. The default behavior is to not contribute funds. - **experimental-websocket-port** + **experimental-websocket-port**=*PORT* Specifying this enables support for accepting incoming WebSocket connections on that port, on any IPv4 and IPv6 addresses you listen diff --git a/doc/undoc-flags.json b/doc/undoc-flags.json index 3665fce5e852..123b7b313568 100644 --- a/doc/undoc-flags.json +++ b/doc/undoc-flags.json @@ -15,7 +15,6 @@ "funder-reserve-tank", "lease-fee-base-msat", "lease-fee-basis", - "lease-funding-weight", - "fetchinvoice-noconnect" + "lease-funding-weight" ] } \ No newline at end of file From 35c6f90666e64ff229217b136f4d05f0d545acdd Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 5 Nov 2021 23:43:36 +0100 Subject: [PATCH 0055/1530] doc: Fixed grammars and add jq as dev dependences. Signed-off-by: Vincenzo Palazzo --- doc/INSTALL.md | 12 ++++++------ doc/schemas/WRITING_SCHEMAS.md | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 26d570585c6b..42e2e508148d 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -55,11 +55,11 @@ Clone lightning: git clone https://github.com/ElementsProject/lightning.git cd lightning - + For development or running tests, get additional dependencies: sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ - libsecp256k1-dev + libsecp256k1-dev jq pip3 install --upgrade pip pip3 install --user -r requirements.txt @@ -155,7 +155,7 @@ mrkd is required to build man pages from markdown files (not done by the port): See `/usr/ports/net-p2p/c-lightning/Makefile` for instructions on how to build from an arbitrary git commit, instead of the latest release tag. -**Note**: Make sure you've set an utf-8 locale, e.g. +**Note**: Make sure you've set an utf-8 locale, e.g. `export LC_CTYPE=en_US.UTF-8`, otherwise manpage installation may fail. Running lightning: @@ -173,7 +173,7 @@ Configure lightningd: copy `/usr/local/etc/lightningd-bitcoin.conf.sample` to To Build on OpenBSD -------------------- -OS version: OpenBSD 6.7 +OS version: OpenBSD 6.7 Install dependencies: ``` @@ -316,7 +316,7 @@ To cross-compile for Raspberry Pi -------------------- Obtain the [official Raspberry Pi toolchains](https://github.com/raspberrypi/tools). -This document assumes compilation will occur towards the Raspberry Pi 3 +This document assumes compilation will occur towards the Raspberry Pi 3 (arm-linux-gnueabihf as of Mar. 2018). Depending on your toolchain location and target arch, source env variables @@ -361,7 +361,7 @@ Download and build sqlite3: Download and build gmp: wget https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz - tar xvf gmp-6.1.2.tar.xz + tar xvf gmp-6.1.2.tar.xz cd gmp-6.1.2 ./configure --disable-assembly --host=$target_host --prefix=$QEMU_LD_PREFIX make diff --git a/doc/schemas/WRITING_SCHEMAS.md b/doc/schemas/WRITING_SCHEMAS.md index b40ede11b70b..2303ad668572 100644 --- a/doc/schemas/WRITING_SCHEMAS.md +++ b/doc/schemas/WRITING_SCHEMAS.md @@ -39,7 +39,7 @@ You should always list all fields which are *always* present in We extend the basic types; see [contrib/pyln-testing/pyln/testing/fixtures.py](fixtures.py). -In addition, before to commit a new schema or a new version of it, make sure that it +In addition, before committing a new schema or a new version of it, make sure that it is well formatted. If you don't want do it by hand, use `make fmt-schema` that uses jq under the hood. From 45f6bf6cf199bf6c5bda3f4b765c33bf75914a55 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 15 Sep 2021 16:19:06 +0200 Subject: [PATCH 0056/1530] options: fix ld proposed_wireaddr proposed_listen_announce get out of sync Incase the error handling happening after the quted line is non-critical: ``` return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); ``` we should not expand the proposed_listen_announce array without adding a proposed_wireaddr. So we move the expand of proposed_listen_announce to the location where we also expand the proposed_wireaddr. Changelog-None --- lightningd/options.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/options.c b/lightningd/options.c index 54dde2aa640d..6c042caffc3e 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -191,7 +191,6 @@ static char *opt_add_addr_withtype(const char *arg, assert(arg != NULL); - tal_arr_expand(&ld->proposed_listen_announce, ala); if (!parse_wireaddr_internal(arg, &wi, ld->portnum, wildcard_ok, !ld->always_use_proxy, false, @@ -210,6 +209,8 @@ static char *opt_add_addr_withtype(const char *arg, ala & ADDR_ANNOUNCE ? "announce" : "listen", type_to_string(tmpctx, struct wireaddr_internal, &wi)); } + + tal_arr_expand(&ld->proposed_listen_announce, ala); tal_arr_expand(&ld->proposed_wireaddr, wi); return NULL; From 0b07de78fcb0174d078d7f9f057d9232c01f0476 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 21 Sep 2021 15:50:57 +0200 Subject: [PATCH 0057/1530] options: make --disable-dns an early arg --- lightningd/options.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index 6c042caffc3e..57d9ec85f2f7 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1050,8 +1050,8 @@ static void register_opts(struct lightningd *ld) "Comma separated list of extra TLV types to accept."); #endif - opt_register_noarg("--disable-dns", opt_set_invbool, &ld->config.use_dns, - "Disable DNS lookups of peers"); + opt_register_early_noarg("--disable-dns", opt_set_invbool, &ld->config.use_dns, + "Disable DNS lookups of peers"); if (deprecated_apis) opt_register_noarg("--enable-autotor-v2-mode", opt_set_invbool, &ld->config.use_v3_autotor, From 907a0e88556ce9eda76c2cd0b569df462224b4c2 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 28 Sep 2021 17:20:45 +0200 Subject: [PATCH 0058/1530] chore: gitignore gdb_history --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index e532534c0bde..54957608219f 100644 --- a/.gitignore +++ b/.gitignore @@ -51,7 +51,6 @@ contrib/pyln-*/.eggs/ contrib/pyln-*/pyln/*/__version__.py release/ tests/plugins/test_selfdisable_after_getmanifest -.DS_Store # Ignore generated files devtools/features @@ -60,3 +59,7 @@ doc/lightning*.[1578] *_wiregen.[ch] *_printgen.[ch] *_gettextgen.po + +# Ignore unrelated stuff +.DS_Store +.gdb_history From 300303a8f2f1571a0253eac99869ab4842128629 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 29 Sep 2021 11:40:05 +0200 Subject: [PATCH 0059/1530] options: fix respect --disable-dns when parsing wireaddress Changelog-Fixed: Options: Respect --always-use-proxy AND --disable-dns when parsing wireaddresses to listen on. --- lightningd/options.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lightningd/options.c b/lightningd/options.c index 57d9ec85f2f7..e186b6e1cadf 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -188,12 +188,14 @@ static char *opt_add_addr_withtype(const char *arg, { char const *err_msg; struct wireaddr_internal wi; + bool dns_ok; assert(arg != NULL); + dns_ok = !ld->always_use_proxy && ld->config.use_dns; if (!parse_wireaddr_internal(arg, &wi, ld->portnum, - wildcard_ok, !ld->always_use_proxy, false, + wildcard_ok, dns_ok, false, deprecated_apis, &err_msg)) { return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } From 25bd09716f5af07f236c1787764af33e895c838f Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Fri, 1 Oct 2021 13:14:46 +0200 Subject: [PATCH 0060/1530] wireaddr: adds helper is_ipaddr, is_toraddr and is_dnsaddr --- common/test/run-ip_port_parsing.c | 33 +++++++++++++ common/wireaddr.c | 79 ++++++++++++++++++++++++++++++- common/wireaddr.h | 10 ++++ connectd/connectd.c | 1 + 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index 68b1bdc55d42..090b7f235879 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -117,6 +117,26 @@ int main(int argc, char *argv[]) common_setup(argv[0]); + /* Check IP/TOR/DNS parser */ + assert(is_ipaddr("192.168.1.2")); + assert(!is_ipaddr("foo.bar.1.2")); + assert(is_toraddr("qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion")); + assert(is_toraddr("qubesos4rrrrz6n4.onion")); + assert(!is_toraddr("QUBESOSfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion")); + assert(!is_toraddr("QUBESOS4rrrrz6n4.onion")); + assert(!is_toraddr("qubesos-asa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion")); + assert(is_dnsaddr("example.com")); + assert(is_dnsaddr("example.digits123.com")); + assert(is_dnsaddr("example-hyphen.com")); + assert(is_dnsaddr("123example.com")); + assert(is_dnsaddr("example123.com")); + assert(is_dnsaddr("is-valid.3hostname123.com")); + assert(!is_dnsaddr("UPPERCASE.invalid.com")); + assert(!is_dnsaddr("-.invalid.com")); + assert(!is_dnsaddr("invalid.-example.com")); + assert(!is_dnsaddr("invalid.example-.com")); + assert(!is_dnsaddr("invalid..example.com")); + /* Grossly invalid. */ assert(!separate_address_and_port(tmpctx, "[", &ip, &port)); assert(!separate_address_and_port(tmpctx, "[123", &ip, &port)); @@ -148,6 +168,19 @@ int main(int argc, char *argv[]) assert(streq(ip, "192.168.2.255")); assert(port == 0); + /* DNS types */ + assert(separate_address_and_port(tmpctx, "example.com:42", &ip, &port)); + assert(streq(ip, "example.com")); + assert(port == 42); + assert(separate_address_and_port(tmpctx, "sub.example.com:21", &ip, &port)); + assert(streq(ip, "sub.example.com")); + assert(port == 21); + port = 123; + assert(separate_address_and_port(tmpctx, "sub.example.com", &ip, &port)); + assert(streq(ip, "sub.example.com")); + assert(port == 123); + port = 0; + // unusual but possibly valid case assert(separate_address_and_port(tmpctx, "[::1]", &ip, &port)); assert(streq(ip, "::1")); diff --git a/common/wireaddr.c b/common/wireaddr.c index 6638f2e6e72d..f9afbc48e392 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -287,8 +287,8 @@ REGISTER_TYPE_TO_STRING(wireaddr, fmt_wireaddr); * Returns false if it wasn't one of these forms. If it returns true, * it only overwrites *port if it was specified by above. */ -static bool separate_address_and_port(const tal_t *ctx, const char *arg, - char **addr, u16 *port) +bool separate_address_and_port(const tal_t *ctx, const char *arg, + char **addr, u16 *port) { char *portcolon; @@ -322,6 +322,81 @@ static bool separate_address_and_port(const tal_t *ctx, const char *arg, return true; } +bool is_ipaddr(const char *arg) +{ + struct in_addr v4; + struct in6_addr v6; + if (inet_pton(AF_INET, arg, &v4)) + return true; + if (inet_pton(AF_INET6, arg, &v6)) + return true; + return false; +} + +bool is_toraddr(const char *arg) +{ + size_t i, arglen; + arglen = strlen(arg); + if (!strends(arg, ".onion")) + return false; + if (arglen != 16 + 6 && arglen != 56 + 6) + return false; + for (i = 0; i < arglen - 6; i++) { + if (arg[i] >= 'a' && arg[i] <= 'z') + continue; + if (arg[i] >= '0' && arg[i] <= '9') + continue; + return false; + } + return true; +} + +/* Rules: + * + * - not longer than 255 + * - segments are separated with . dot + * - segments do not start or end with - hyphen + * - segments must be longer thant zero + * - lowercase a-z and digits 0-9 and - hyphen + */ +bool is_dnsaddr(const char *arg) +{ + size_t i, arglen; + int lastdot; + + if (is_ipaddr(arg) || is_toraddr(arg)) + return false; + + /* now that its not IP or TOR, check its a DNS name */ + arglen = strlen(arg); + if (arglen > 255) + return false; + lastdot = -1; + for (i = 0; i < arglen; i++) { + if (arg[i] == '.') { + /* segment must be longer than zero */ + if (i - lastdot == 1) + return false; + /* last segment can not end with hypen */ + if (i != 0 && arg[i-1] == '-') + return false; + lastdot = i; + continue; + } + /* segment cannot start with hyphen */ + if (i == lastdot + 1 && arg[i] == '-') + return false; + if (arg[i] >= 'a' && arg[i] <= 'z') + continue; + if (arg[i] >= '0' && arg[i] <= '9') + continue; + if (arg[i] == '-') + continue; + return false; + } + return true; +} + struct wireaddr * wireaddr_from_hostname(const tal_t *ctx, const char *hostname, diff --git a/common/wireaddr.h b/common/wireaddr.h index 3f9cddc6249d..e9dc0bf8e7cb 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -149,6 +149,16 @@ struct wireaddr_internal { bool wireaddr_internal_eq(const struct wireaddr_internal *a, const struct wireaddr_internal *b); + +bool separate_address_and_port(const tal_t *ctx, const char *arg, + char **addr, u16 *port); + +bool is_ipaddr(const char *arg); + +bool is_toraddr(const char *arg); + +bool is_dnsaddr(const char *arg); + bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, bool wildcard_ok, bool dns_ok, bool unresolved_ok, bool allow_deprecated, diff --git a/connectd/connectd.c b/connectd/connectd.c index 1150f6ee7c5e..7cd457d759cb 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -875,6 +875,7 @@ static struct io_plan *conn_init(struct io_conn *conn, break; case ADDR_INTERNAL_WIREADDR: /* If it was a Tor address, we wouldn't be here. */ + assert(!is_toraddr((char*)addr->u.wireaddr.addr)); ai = wireaddr_to_addrinfo(tmpctx, &addr->u.wireaddr); break; } From 01e8a523e9a23f5d64166696f7629a13947672e3 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Fri, 1 Oct 2021 13:47:29 +0200 Subject: [PATCH 0061/1530] bolt7: allow announcement of ADDR_TYPE_DNS --- common/json_helpers.c | 4 +++ common/wireaddr.c | 12 +++++++ common/wireaddr.h | 11 +++++-- connectd/connectd.c | 6 ++++ connectd/netaddress.c | 1 + devtools/gossipwith.c | 3 ++ doc/lightning-getinfo.7.md | 6 ++-- doc/lightning-listnodes.7.md | 12 +++---- doc/schemas/getinfo.schema.json | 2 ++ doc/schemas/listnodes.schema.json | 2 ++ lightningd/options.c | 55 +++++++++++++++++++++---------- tests/test_gossip.py | 34 +++++++++++++++---- 12 files changed, 112 insertions(+), 36 deletions(-) diff --git a/common/json_helpers.c b/common/json_helpers.c index 44efdffbd44c..bcde33a6162f 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -281,6 +281,10 @@ void json_add_address(struct json_stream *response, const char *fieldname, json_add_string(response, "type", "torv3"); json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_DNS) { + json_add_string(response, "type", "dns"); + json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); + json_add_num(response, "port", addr->port); } else if (addr->type == ADDR_TYPE_WEBSOCKET) { json_add_string(response, "type", "websocket"); json_add_num(response, "port", addr->port); diff --git a/common/wireaddr.c b/common/wireaddr.c index f9afbc48e392..8130fee5dd98 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -37,6 +37,11 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) case ADDR_TYPE_TOR_V3: addr->addrlen = TOR_V3_ADDRLEN; break; + case ADDR_TYPE_DNS: + addr->addrlen = fromwire_u8(cursor, max); + memset(&addr->addr, 0, sizeof(addr->addr)); + addr->addr[addr->addrlen] = 0; + break; case ADDR_TYPE_WEBSOCKET: addr->addrlen = 0; break; @@ -52,6 +57,8 @@ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) void towire_wireaddr(u8 **pptr, const struct wireaddr *addr) { towire_u8(pptr, addr->type); + if (addr->type == ADDR_TYPE_DNS) + towire_u8(pptr, addr->addrlen); towire(pptr, addr->addr, addr->addrlen); towire_u16(pptr, addr->port); } @@ -211,6 +218,7 @@ bool wireaddr_is_wildcard(const struct wireaddr *addr) return memeqzero(addr->addr, addr->addrlen); case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: case ADDR_TYPE_WEBSOCKET: return false; } @@ -259,6 +267,8 @@ char *fmt_wireaddr_without_port(const tal_t * ctx, const struct wireaddr *a) case ADDR_TYPE_TOR_V3: return tal_fmt(ctx, "%s.onion", b32_encode(tmpctx, a->addr, a->addrlen)); + case ADDR_TYPE_DNS: + return tal_fmt(ctx, "%s", a->addr); case ADDR_TYPE_WEBSOCKET: return tal_strdup(ctx, "websocket"); } @@ -789,6 +799,7 @@ struct addrinfo *wireaddr_to_addrinfo(const tal_t *ctx, return ai; case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: case ADDR_TYPE_WEBSOCKET: break; } @@ -841,6 +852,7 @@ bool all_tor_addresses(const struct wireaddr_internal *wireaddr) switch (wireaddr[i].u.wireaddr.type) { case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: + case ADDR_TYPE_DNS: return false; case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: diff --git a/common/wireaddr.h b/common/wireaddr.h index e9dc0bf8e7cb..8b8b79904b72 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -37,13 +37,18 @@ struct sockaddr_un; * where `checksum = sha3(".onion checksum" | pubkey || version)[:2]` */ +/* BOLT-hostnames #7: + * * `5`: DNS hostname; data = `[byte:len][len*byte:hostname][u16:port]` (length up to 258) + */ + /* BOLT-websockets #7: * * `6`: WebSocket port; data = `[2:port]` (length 2) */ #define TOR_V2_ADDRLEN 10 #define TOR_V3_ADDRLEN 35 -#define LARGEST_ADDRLEN TOR_V3_ADDRLEN +#define DNS_ADDRLEN 255 +#define LARGEST_ADDRLEN DNS_ADDRLEN #define TOR_V3_BLOBLEN 64 #define STATIC_TOR_MAGIC_STRING "gen-default-toraddress" @@ -52,10 +57,10 @@ enum wire_addr_type { ADDR_TYPE_IPV6 = 2, ADDR_TYPE_TOR_V2_REMOVED = 3, ADDR_TYPE_TOR_V3 = 4, - ADDR_TYPE_WEBSOCKET = 6, + ADDR_TYPE_DNS = 5, + ADDR_TYPE_WEBSOCKET = 6 }; -/* Structure now fit for tor support */ struct wireaddr { enum wire_addr_type type; u8 addrlen; diff --git a/connectd/connectd.c b/connectd/connectd.c index 7cd457d759cb..b92058f2c71d 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -975,6 +975,9 @@ static void try_connect_one_addr(struct connecting *connect) case ADDR_TYPE_IPV6: af = AF_INET6; break; + case ADDR_TYPE_DNS: + // TODO: resolve with getaddrinfo and set af + break; case ADDR_TYPE_WEBSOCKET: af = -1; break; @@ -1159,6 +1162,7 @@ static bool handle_wireaddr_listen(struct daemon *daemon, case ADDR_TYPE_WEBSOCKET: case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: break; } status_failed(STATUS_FAIL_INTERNAL_ERROR, @@ -1614,6 +1618,8 @@ static void add_seed_addrs(struct wireaddr_internal **addrs, NULL, broken_reply, NULL); if (new_addrs) { for (size_t j = 0; j < tal_count(new_addrs); j++) { + if (new_addrs[j].type == ADDR_TYPE_DNS) + continue; struct wireaddr_internal a; a.itype = ADDR_INTERNAL_WIREADDR; a.u.wireaddr = new_addrs[j]; diff --git a/connectd/netaddress.c b/connectd/netaddress.c index dff03e92d308..5f9fb57fd3fa 100644 --- a/connectd/netaddress.c +++ b/connectd/netaddress.c @@ -258,6 +258,7 @@ bool guess_address(struct wireaddr *addr) } case ADDR_TYPE_TOR_V2_REMOVED: case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: case ADDR_TYPE_WEBSOCKET: status_broken("Cannot guess address type %u", addr->type); break; diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 97287103d36b..f356f00089bb 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -324,6 +324,9 @@ int main(int argc, char *argv[]) case ADDR_TYPE_WEBSOCKET: opt_usage_exit_fail("Don't support websockets"); break; + case ADDR_TYPE_DNS: + opt_usage_exit_fail("Don't support DNS"); + break; case ADDR_TYPE_IPV4: af = AF_INET; break; diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 40e1f98d0174..a3d4b5b118b1 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -40,10 +40,10 @@ On success, an object is returned, containing: - **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", "websocket") + - **type** (string): Type of connection (one of "dns", "ipv4", "ipv6", "torv2", "torv3", "websocket") - **port** (u16): port number - If **type** is "ipv4", "ipv6", "torv2" or "torv3": + If **type** is "dns", "ipv4", "ipv6", "torv2" or "torv3": - **address** (string): address in expected format for **type** - **binding** (array of objects, optional): The addresses we are listening on: - **type** (string): Type of connection (one of "local socket", "ipv4", "ipv6", "torv2", "torv3") @@ -117,4 +117,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:8374064ca0f95ab0c20d3edaf7f3742316af98f4d1e0e8de88922524f1ea3ce5) +[comment]: # ( SHA256STAMP:90a3bacb6cb4456119afee8e60677c29bf5f46c4cd950e660a9f9c8e0433b473) diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index 39d184904ad7..3f36ce07f9cf 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -36,10 +36,10 @@ If **last_timestamp** is present: - **color** (hex): The favorite RGB color this node advertized (always 6 characters) - **features** (hex): BOLT #9 features bitmap this node advertized - **addresses** (array of objects): The addresses this node advertized: - - **type** (string): Type of connection (one of "ipv4", "ipv6", "torv2", "torv3", "websocket") + - **type** (string): Type of connection (one of "dns", "ipv4", "ipv6", "torv2", "torv3", "websocket") - **port** (u16): port number - If **type** is "ipv4", "ipv6", "torv2" or "torv3": + If **type** is "dns", "ipv4", "ipv6", "torv2" or "torv3": - **address** (string): address in expected format for **type** If **option_will_fund** is present: @@ -52,9 +52,9 @@ If **option_will_fund** is present: - **compact_lease** (hex): the lease as represented in the node_announcement [comment]: # (GENERATE-FROM-SCHEMA-END) - + On failure, one of the following error codes may be returned: - + - -32602: Error in given parameters. EXAMPLE JSON RESPONSE @@ -89,10 +89,10 @@ Vincenzo Palazzo <> wrote the initial version o SEE ALSO -------- -FIXME: +FIXME: RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:4a5cfb1cf3d7fd77e49d6e7e369a9a6d374345b011d7db2fa9b4062156869ca4) +[comment]: # ( SHA256STAMP:85400c9c1741943e2e02935b4f14fd187a7db6056410e42adec07ef3c6772f5f) diff --git a/doc/schemas/getinfo.schema.json b/doc/schemas/getinfo.schema.json index 4e0eda46a409..fc07c5de9bb5 100644 --- a/doc/schemas/getinfo.schema.json +++ b/doc/schemas/getinfo.schema.json @@ -86,6 +86,7 @@ "type": { "type": "string", "enum": [ + "dns", "ipv4", "ipv6", "torv2", @@ -104,6 +105,7 @@ "type": { "type": "string", "enum": [ + "dns", "ipv4", "ipv6", "torv2", diff --git a/doc/schemas/listnodes.schema.json b/doc/schemas/listnodes.schema.json index d88250f663bd..42637bff65f1 100644 --- a/doc/schemas/listnodes.schema.json +++ b/doc/schemas/listnodes.schema.json @@ -74,6 +74,7 @@ "type": { "type": "string", "enum": [ + "dns", "ipv4", "ipv6", "torv2", @@ -92,6 +93,7 @@ "type": { "type": "string", "enum": [ + "dns", "ipv4", "ipv6", "torv2", diff --git a/lightningd/options.c b/lightningd/options.c index e186b6e1cadf..8e903cfb3c94 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -189,31 +189,52 @@ static char *opt_add_addr_withtype(const char *arg, char const *err_msg; struct wireaddr_internal wi; bool dns_ok; + char *address; + u16 port; assert(arg != NULL); dns_ok = !ld->always_use_proxy && ld->config.use_dns; - if (!parse_wireaddr_internal(arg, &wi, - ld->portnum, - wildcard_ok, dns_ok, false, - deprecated_apis, &err_msg)) { - return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); - } + if (!separate_address_and_port(tmpctx, arg, &address, &port)) + return tal_fmt(NULL, "Unable to parse address:port '%s'", arg); - /* Sanity check for exact duplicates. */ - for (size_t i = 0; i < tal_count(ld->proposed_wireaddr); i++) { - /* Only compare announce vs announce and bind vs bind */ - if ((ld->proposed_listen_announce[i] & ala) == 0) - continue; + if (is_ipaddr(address) || is_toraddr(address) || ala != ADDR_ANNOUNCE) { + if (!parse_wireaddr_internal(arg, &wi, ld->portnum, + wildcard_ok, dns_ok, false, + deprecated_apis, &err_msg)) { + return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); + } + + /* Sanity check for exact duplicates. */ + for (size_t i = 0; i < tal_count(ld->proposed_wireaddr); i++) { + /* Only compare announce vs announce and bind vs bind */ + if ((ld->proposed_listen_announce[i] & ala) == 0) + continue; + + if (wireaddr_internal_eq(&ld->proposed_wireaddr[i], &wi)) + return tal_fmt(NULL, "Duplicate %s address %s", + ala & ADDR_ANNOUNCE ? "announce" : "listen", + type_to_string(tmpctx, struct wireaddr_internal, &wi)); + } + + tal_arr_expand(&ld->proposed_listen_announce, ala); + tal_arr_expand(&ld->proposed_wireaddr, wi); + } - if (wireaddr_internal_eq(&ld->proposed_wireaddr[i], &wi)) - return tal_fmt(NULL, "Duplicate %s address %s", - ala & ADDR_ANNOUNCE ? "announce" : "listen", - type_to_string(tmpctx, struct wireaddr_internal, &wi)); + /* Add ADDR_TYPE_DNS to announce DNS hostnames */ + if (is_dnsaddr(address) && ala & ADDR_ANNOUNCE) { + memset(&wi, 0, sizeof(wi)); + wi.itype = ADDR_INTERNAL_WIREADDR; + wi.u.wireaddr.type = ADDR_TYPE_DNS; + wi.u.wireaddr.addrlen = strlen(address); + strncpy((char * restrict)&wi.u.wireaddr.addr, + address, sizeof(wi.u.wireaddr.addr) - 1); + wi.u.wireaddr.port = port; + + tal_arr_expand(&ld->proposed_listen_announce, ADDR_ANNOUNCE); + tal_arr_expand(&ld->proposed_wireaddr, wi); } - tal_arr_expand(&ld->proposed_listen_announce, ala); - tal_arr_expand(&ld->proposed_wireaddr, wi); return NULL; } diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 2a4bc7ca361c..785eaeaaae8c 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -110,9 +110,11 @@ def test_announce_address(node_factory, bitcoind): """Make sure our announcements are well formed.""" # We do not allow announcement of duplicates. - opts = {'announce-addr': + opts = {'disable-dns': None, 'announce-addr': ['4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion', '1.2.3.4:1234', + 'localhost:1235', + 'example.com:1236', '::'], 'log-level': 'io', 'dev-allow-localhost': None} @@ -126,12 +128,30 @@ def test_announce_address(node_factory, bitcoind): l2.wait_channel_active(scid) # We should see it send node announce with all addresses (257 = 0x0101) - # local ephemeral port is masked out. - l1.daemon.wait_for_log(r"\[OUT\] 0101.*47" - "010102030404d2" - "017f000001...." - "02000000000000000000000000000000002607" - "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba50230032607") + # Note: local ephemeral port is masked out. + # Note: Since we `disable-dns` it should not announce a resolved IPv4 + # or IPv6 address for example.com + # + # Also expect the address descriptor types to be sorted! + # BOLT #7: + # - MUST place address descriptors in ascending order. + l1.daemon.wait_for_log(r"\[OUT\] 0101.*0063" + "010102030404d2" # IPv4 01 1.2.3.4:1234 + "017f000001...." # IPv4 01 127.0.0.1:wxyz + "02000000000000000000000000000000002607" # IPv6 02 :::9735 + "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba50230032607" # TORv3 04 + "05096c6f63616c686f737404d3" # DNS 05 len localhost:1235 + "050b6578616d706c652e636f6d04d4") # DNS 05 len example.com:1236 + + # Check other node can parse these + addresses = l2.rpc.listnodes(l1.info['id'])['nodes'][0]['addresses'] + addresses_dns = [address for address in addresses if address['type'] == 'dns'] + assert len(addresses) == 6 + assert len(addresses_dns) == 2 + assert addresses_dns[0]['address'] == 'localhost' + assert addresses_dns[0]['port'] == 1235 + assert addresses_dns[1]['address'] == 'example.com' + assert addresses_dns[1]['port'] == 1236 @pytest.mark.developer("needs DEVELOPER=1") From cb20d6747e82188792ea3a49bcc17285ab35934e Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Thu, 7 Oct 2021 13:01:05 +0200 Subject: [PATCH 0062/1530] connectd: resolve ADDR_TYPE_DNS This will resolve ADDR_TYPE_DNS wireaddr by expanding connect->addrs with one new wireaddr ADDR_INTERNAL_WIREADDR per DNS result and calling recursion --- connectd/connectd.c | 52 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index b92058f2c71d..fd682788ff37 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -874,6 +874,8 @@ static struct io_plan *conn_init(struct io_conn *conn, "Can't connect to forproxy address"); break; case ADDR_INTERNAL_WIREADDR: + /* DNS should have been resolved before */ + assert(addr->u.wireaddr.type != ADDR_TYPE_DNS); /* If it was a Tor address, we wouldn't be here. */ assert(!is_toraddr((char*)addr->u.wireaddr.addr)); ai = wireaddr_to_addrinfo(tmpctx, &addr->u.wireaddr); @@ -926,6 +928,12 @@ static void try_connect_one_addr(struct connecting *connect) bool use_proxy = connect->daemon->always_use_proxy; const struct wireaddr_internal *addr = &connect->addrs[connect->addrnum]; struct io_conn *conn; + bool use_dns = connect->daemon->use_dns; + struct addrinfo hints, *ais, *aii; + struct wireaddr_internal addrhint; + int gai_err; + struct sockaddr_in *sa4; + struct sockaddr_in6 *sa6; /* In case we fail without a connection, make destroy_io_conn happy */ connect->conn = NULL; @@ -976,8 +984,48 @@ static void try_connect_one_addr(struct connecting *connect) af = AF_INET6; break; case ADDR_TYPE_DNS: - // TODO: resolve with getaddrinfo and set af - break; + if (use_proxy) /* hand it to the proxy */ + break; + if (!use_dns) /* ignore DNS when we can't use it */ + goto next; + /* Resolve with getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = AF_UNSPEC; + hints.ai_protocol = 0; + hints.ai_flags = AI_ADDRCONFIG; + gai_err = getaddrinfo((char *)addr->u.wireaddr.addr, + tal_fmt(tmpctx, "%d", + addr->u.wireaddr.port), + &hints, &ais); + if (gai_err != 0) { + status_debug("DNS with getaddrinfo gave: %s", + gai_strerror(gai_err)); + goto next; + } + /* create new addrhints on-the-fly per result ... */ + for (aii = ais; aii; aii = aii->ai_next) { + addrhint.itype = ADDR_INTERNAL_WIREADDR; + if (aii->ai_family == AF_INET) { + sa4 = (struct sockaddr_in *) aii->ai_addr; + wireaddr_from_ipv4(&addrhint.u.wireaddr, + &sa4->sin_addr, + addr->u.wireaddr.port); + } else if (aii->ai_family == AF_INET6) { + sa6 = (struct sockaddr_in6 *) aii->ai_addr; + wireaddr_from_ipv6(&addrhint.u.wireaddr, + &sa6->sin6_addr, + addr->u.wireaddr.port); + } else { + /* skip unsupported ai_family */ + continue; + } + tal_arr_expand(&connect->addrs, addrhint); + /* don't forget to update convenience pointer */ + addr = &connect->addrs[connect->addrnum]; + } + freeaddrinfo(ais); + goto next; case ADDR_TYPE_WEBSOCKET: af = -1; break; From 2b8896a0b532179dd04c67e69e9ddd7571d9105a Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Fri, 8 Oct 2021 15:37:56 +0200 Subject: [PATCH 0063/1530] pytest: test connecting to a DNS only announced node --- tests/test_gossip.py | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 785eaeaaae8c..b7913cb98733 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -154,6 +154,70 @@ def test_announce_address(node_factory, bitcoind): assert addresses_dns[1]['port'] == 1236 +@pytest.mark.developer("gossip without DEVELOPER=1 is slow") +def test_announce_and_connect_via_dns(node_factory, bitcoind): + """ Test that DNS annoucements propagate and can be used when connecting. + + - First node announces only a FQDN like 'localhost.localdomain'. + - Second node gets a channel with first node. + - Third node just connects to second node. + - Fourth node with DNS disabled also connects to second node. + - Wait for gossip so third and fourth node sees first node. + - Third node must be able to 'resolve' 'localhost.localdomain' + and connect to first node. + - Fourth node must not be able to connect because he has disabled DNS. + + Notes: + - --disable-dns is needed so the first node does not announce 127.0.0.1 itself. + - 'dev-allow-localhost' must not be set, so it does not resolve localhost anyway. + """ + opts1 = {'disable-dns': None, + 'announce-addr': ['localhost.localdomain:12345'], # announce dns + 'bind-addr': ['127.0.0.1:12345', '[::1]:12345']} # and bind local IPs + opts3 = {'may_reconnect': True} + opts4 = {'disable-dns': None} + l1, l2, l3, l4 = node_factory.get_nodes(4, opts=[opts1, {}, opts3, opts4]) + + # In order to enable DNS on a pyln testnode we need to delete the + # 'disable-dns' opt (which is added by pyln test utils) and restart it. + del l3.daemon.opts['disable-dns'] + l3.restart() + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l3.rpc.connect(l2.info['id'], 'localhost', l2.port) + l4.rpc.connect(l2.info['id'], 'localhost', l2.port) + scid, _ = l1.fundchannel(l2, 10**6) + bitcoind.generate_block(5) + + # wait until l3 and l4 see l1 via gossip with announced addresses + wait_for(lambda: len(l3.rpc.listnodes(l1.info['id'])['nodes']) == 1) + wait_for(lambda: len(l4.rpc.listnodes(l1.info['id'])['nodes']) == 1) + wait_for(lambda: 'addresses' in l3.rpc.listnodes(l1.info['id'])['nodes'][0]) + wait_for(lambda: 'addresses' in l4.rpc.listnodes(l1.info['id'])['nodes'][0]) + addresses = l3.rpc.listnodes(l1.info['id'])['nodes'][0]['addresses'] + assert(len(addresses) == 1) # no other addresses must be announced for this + assert(addresses[0]['type'] == 'dns') + assert(addresses[0]['address'] == 'localhost.localdomain') + assert(addresses[0]['port'] == 12345) + + # now l3 must be able to use DNS to resolve and connect to l1 + result = l3.rpc.connect(l1.info['id']) + assert result['id'] == l1.info['id'] + assert result['direction'] == 'out' + assert result['address']['port'] == 12345 + if result['address']['type'] == 'ipv4': + assert result['address']['address'] == '127.0.0.1' + elif result['address']['type'] == 'ipv6': + assert result['address']['address'] == '::1' + else: + assert False + + # l4 however must not be able to connect because he used '--disable-dns' + # This raises RpcError code 401, currently with an empty error message. + with pytest.raises(RpcError, match=r"401"): + l4.rpc.connect(l1.info['id']) + + @pytest.mark.developer("needs DEVELOPER=1") def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): # Updates get backdated 5 seconds with --dev-fast-gossip. From a3ea9fdc87b2361f47b2cffe90b996df1c73ed2c Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 19 Oct 2021 11:32:52 +0200 Subject: [PATCH 0064/1530] chore: use EXPERIMENTAL for BOLT7 DNS #911 Changelog-EXPERIMENTAL: Ability to announce DNS addresses --- connectd/connectd.c | 6 ++++++ lightningd/options.c | 2 ++ tests/test_gossip.py | 19 ++++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index fd682788ff37..9a56abe2fea5 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -928,12 +928,14 @@ static void try_connect_one_addr(struct connecting *connect) bool use_proxy = connect->daemon->always_use_proxy; const struct wireaddr_internal *addr = &connect->addrs[connect->addrnum]; struct io_conn *conn; +#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ bool use_dns = connect->daemon->use_dns; struct addrinfo hints, *ais, *aii; struct wireaddr_internal addrhint; int gai_err; struct sockaddr_in *sa4; struct sockaddr_in6 *sa6; +#endif /* In case we fail without a connection, make destroy_io_conn happy */ connect->conn = NULL; @@ -984,6 +986,7 @@ static void try_connect_one_addr(struct connecting *connect) af = AF_INET6; break; case ADDR_TYPE_DNS: +#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ if (use_proxy) /* hand it to the proxy */ break; if (!use_dns) /* ignore DNS when we can't use it */ @@ -1025,6 +1028,7 @@ static void try_connect_one_addr(struct connecting *connect) addr = &connect->addrs[connect->addrnum]; } freeaddrinfo(ais); +#endif goto next; case ADDR_TYPE_WEBSOCKET: af = -1; @@ -1666,8 +1670,10 @@ static void add_seed_addrs(struct wireaddr_internal **addrs, NULL, broken_reply, NULL); if (new_addrs) { for (size_t j = 0; j < tal_count(new_addrs); j++) { +#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ if (new_addrs[j].type == ADDR_TYPE_DNS) continue; +#endif struct wireaddr_internal a; a.itype = ADDR_INTERNAL_WIREADDR; a.u.wireaddr = new_addrs[j]; diff --git a/lightningd/options.c b/lightningd/options.c index 8e903cfb3c94..4e79efeb32f2 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -221,6 +221,7 @@ static char *opt_add_addr_withtype(const char *arg, tal_arr_expand(&ld->proposed_wireaddr, wi); } +#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ /* Add ADDR_TYPE_DNS to announce DNS hostnames */ if (is_dnsaddr(address) && ala & ADDR_ANNOUNCE) { memset(&wi, 0, sizeof(wi)); @@ -234,6 +235,7 @@ static char *opt_add_addr_withtype(const char *arg, tal_arr_expand(&ld->proposed_listen_announce, ADDR_ANNOUNCE); tal_arr_expand(&ld->proposed_wireaddr, wi); } +#endif return NULL; diff --git a/tests/test_gossip.py b/tests/test_gossip.py index b7913cb98733..30c82cb6f8d7 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -4,7 +4,8 @@ from fixtures import TEST_NETWORK from pyln.client import RpcError, Millisatoshi from utils import ( - DEVELOPER, wait_for, TIMEOUT, only_one, sync_blockheight, expected_node_features, COMPAT + DEVELOPER, wait_for, TIMEOUT, only_one, sync_blockheight, + expected_node_features, COMPAT, EXPERIMENTAL_FEATURES ) import json @@ -118,6 +119,13 @@ def test_announce_address(node_factory, bitcoind): '::'], 'log-level': 'io', 'dev-allow-localhost': None} + if not EXPERIMENTAL_FEATURES: # BOLT7 DNS RFC #911 + opts = {'disable-dns': None, 'announce-addr': + ['4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion', + '1.2.3.4:1234', + '::'], + 'log-level': 'io', + 'dev-allow-localhost': None} l1, l2 = node_factory.get_nodes(2, opts=[opts, {}]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -127,6 +135,14 @@ def test_announce_address(node_factory, bitcoind): l1.wait_channel_active(scid) l2.wait_channel_active(scid) + if not EXPERIMENTAL_FEATURES: # BOLT7 DNS RFC #911 + l1.daemon.wait_for_log(r"\[OUT\] 0101.*47" + "010102030404d2" + "017f000001...." + "02000000000000000000000000000000002607" + "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba50230032607") + return + # We should see it send node announce with all addresses (257 = 0x0101) # Note: local ephemeral port is masked out. # Note: Since we `disable-dns` it should not announce a resolved IPv4 @@ -154,6 +170,7 @@ def test_announce_address(node_factory, bitcoind): assert addresses_dns[1]['port'] == 1236 +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") @pytest.mark.developer("gossip without DEVELOPER=1 is slow") def test_announce_and_connect_via_dns(node_factory, bitcoind): """ Test that DNS annoucements propagate and can be used when connecting. From 5fb36742330761ee8102446591216aed895f9535 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 3 Nov 2021 09:59:10 +0200 Subject: [PATCH 0065/1530] testing: test hook semantics is preserved in shutdown plugins expect their hooks to work also in shutdown, see issue #4883 --- tests/plugins/misc_notifications.py | 11 +++++++++-- tests/test_plugin.py | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/tests/plugins/misc_notifications.py b/tests/plugins/misc_notifications.py index ac3af645c7ac..bc0ab621e566 100755 --- a/tests/plugins/misc_notifications.py +++ b/tests/plugins/misc_notifications.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 """Plugin to be used to test miscellaneous notifications. - -Only used for 'channel_opened' for now. """ from pyln.client import Plugin +from time import sleep +import sys plugin = Plugin() @@ -27,4 +27,11 @@ def channel_state_changed(plugin, channel_state_changed, **kwargs): plugin.log("channel_state_changed {}".format(channel_state_changed)) +@plugin.subscribe("shutdown") +def shutdown(plugin, **kwargs): + plugin.log("delaying shutdown with 5s") + sleep(5) + sys.exit(0) + + plugin.run() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ce8476f8fbf8..5d054af6acc0 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1084,6 +1084,32 @@ def test_htlc_accepted_hook_direct_restart(node_factory, executor): f1.result() +def test_htlc_accepted_hook_shutdown(node_factory, executor): + """Hooks of important-plugins are never removed and these plugins are kept + alive until after subdaemons are shutdown. + """ + l1, l2 = node_factory.line_graph(2, opts=[ + {'may_reconnect': True, 'log-level': 'info'}, + {'may_reconnect': True, 'log-level': 'debug', + 'plugin': [os.path.join(os.getcwd(), 'tests/plugins/misc_notifications.py')], + 'important-plugin': [os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py')]} + ]) + + i1 = l2.rpc.invoice(msatoshi=1000, label="inv1", description="desc")['bolt11'] + + # fail_htlcs.py makes payment fail + with pytest.raises(RpcError): + l1.rpc.pay(i1) + + f_stop = executor.submit(l2.rpc.stop) + l2.daemon.wait_for_log(r'plugin-misc_notifications.py: delaying shutdown with 5s') + + # Should still fail htlc while shutting down + with pytest.raises(RpcError): + l1.rpc.pay(i1) + f_stop.result() + + @pytest.mark.developer("without DEVELOPER=1, gossip v slow") def test_htlc_accepted_hook_forward_restart(node_factory, executor): """l2 restarts while it is pondering what to do with an HTLC. From 5f69674faaf4763e4457431e27c35147e0d52bc5 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 10 Nov 2021 11:39:02 +0200 Subject: [PATCH 0066/1530] lightningd: shutdown plugins after subdaemons and assert no write access to db because: - shutdown_subdaemons can trigger db write, comments in that function say so at least - resurrecting the main event loop with subdaemons still running is counter productive in shutting down activity (such as htlc's, hook_calls etc.) - custom behavior injected by plugins via hooks should be consistent, see test in previous commmit IDEA: in shutdown_plugins, when starting new io_loop: - A plugin that is still running can return a jsonrpc_request response, this triggers response_cb, which cannot be handled because subdaemons are gone -> so any response_cb should be blocked/aborted - jsonrpc is still there, so users (such as plugins) can make new jsonrpc_request's which cannot be handled because subdaemons are gone -> so new rpc_request should also be blocked - But we do want to send/receive notifications and log messages (handled in jsonrpc as jsonrpc_notification) as these do not trigger subdaemon calls or db_write's Log messages and notifications do not have "id" field, where jsonrpc_request *do* have an "id" field PLAN (hypothesis): - hack into plugin_read_json_one OR plugin_response_handle to filter-out json with an "id" field, this should block/abandon any jsonrpc_request responses (and new jsonrpc_requests for plugins?) Q. Can internal (so not via plugin) jsonrpc_requests called in the main io_loop return/revive in the shutdown io_loop? A. No. All code under lightningd/ returning command_still_pending depends on either a subdaemon, timer or plugin. In shutdown loop the subdaemons are dead, timer struct cleared and plugins will be taken care of (in next commits). fixup: we can only io_break the main io_loop once --- lightningd/jsonrpc.c | 2 +- lightningd/lightningd.c | 5 +++-- lightningd/plugin.c | 45 ++++++++++++++++++++-------------------- lightningd/plugin.h | 2 +- lightningd/plugin_hook.c | 1 + wallet/db.c | 6 ++++++ wallet/db.h | 3 +++ wallet/db_common.h | 3 +++ 8 files changed, 41 insertions(+), 26 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 09c0559e1027..3b2d2e077989 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -972,7 +972,7 @@ static struct io_plan *start_json_stream(struct io_conn *conn, io_wake(conn); /* Once the stop_conn conn is drained, we can shut down. */ - if (jcon->ld->stop_conn == conn) { + if (jcon->ld->stop_conn == conn && jcon->ld->state == LD_STATE_RUNNING) { /* Return us to toplevel lightningd.c */ io_break(jcon->ld); /* We never come back. */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index b20520d4ae7a..248090c81593 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -242,6 +242,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) */ ld->plugins = plugins_new(ld, ld->log_book, ld); ld->plugins->startup = true; + ld->plugins->shutdown = false; /*~ This is set when a JSON RPC command comes in to shut us down. */ ld->stop_conn = NULL; @@ -1187,10 +1188,10 @@ int main(int argc, char *argv[]) /* We're not going to collect our children. */ remove_sigchild_handler(); + shutdown_subdaemons(ld); - /* Tell plugins we're shutting down. */ + /* Tell plugins we're shutting down, closes the db for write access. */ shutdown_plugins(ld); - shutdown_subdaemons(ld); /* Clean up the JSON-RPC. This needs to happen in a DB transaction since * it might actually be touching the DB in some destructors, e.g., diff --git a/lightningd/plugin.c b/lightningd/plugin.c index ee3c2c967a78..c0f3582b31d5 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -187,19 +187,18 @@ static void destroy_plugin(struct plugin *p) if (p->plugin_state == AWAITING_GETMANIFEST_RESPONSE) check_plugins_manifests(p->plugins); - /* If this was the last one init was waiting for, handle cmd replies */ - if (p->plugin_state == AWAITING_INIT_RESPONSE) - check_plugins_initted(p->plugins); - - /* If we are shutting down, do not continue to checking if - * the dying plugin is important. */ + /* Daemon shutdown overrules plugin's importance; aborts init checks */ if (p->plugins->shutdown) { /* But return if this was the last plugin! */ if (list_empty(&p->plugins->plugins)) - io_break(p->plugins); + io_break(destroy_plugin); return; } + /* If this was the last one init was waiting for, handle cmd replies */ + if (p->plugin_state == AWAITING_INIT_RESPONSE) + check_plugins_initted(p->plugins); + /* Now check if the dying plugin is important. */ if (p->important) { log_broken(p->log, @@ -2082,46 +2081,48 @@ bool was_plugin_destroyed(struct plugin_destroyed *pd) static void plugin_shutdown_timeout(struct lightningd *ld) { - io_break(ld->plugins); + io_break(plugin_shutdown_timeout); } void shutdown_plugins(struct lightningd *ld) { struct plugin *p, *next; - /* This makes sure we don't complain about important plugins - * vanishing! */ + /* Don't complain about important plugins vanishing and + * crash any attempt to write to db. */ ld->plugins->shutdown = true; /* Tell them all to shutdown; if they care. */ list_for_each_safe(&ld->plugins->plugins, p, next, list) { /* Kill immediately, deletes self from list. */ - if (!notify_plugin_shutdown(ld, p)) + if (p->plugin_state != INIT_COMPLETE || !notify_plugin_shutdown(ld, p)) tal_free(p); } /* If anyone was interested in shutdown, give them time. */ if (!list_empty(&ld->plugins->plugins)) { - struct oneshot *t; + struct timers *orig_timers, *timer; - /* 30 seconds should do it. */ - t = new_reltimer(ld->timers, ld, - time_from_sec(30), - plugin_shutdown_timeout, ld); + /* 30 seconds should do it, use a clean timers struct */ + orig_timers = ld->timers; + timer = tal(NULL, struct timers); + timers_init(timer, time_mono()); + new_reltimer(timer, timer, time_from_sec(30), + plugin_shutdown_timeout, ld); - io_loop_with_timers(ld); - tal_free(t); + ld->timers = timer; + void *ret = io_loop_with_timers(ld); + assert(ret == plugin_shutdown_timeout || ret == destroy_plugin); + ld->timers = orig_timers; + tal_free(timer); /* Report and free remaining plugins. */ while (!list_empty(&ld->plugins->plugins)) { p = list_pop(&ld->plugins->plugins, struct plugin, list); log_debug(ld->log, - "%s: failed to shutdown, killing.", + "%s: failed to self-terminate in time, killing.", p->shortname); tal_free(p); } } - - /* NULL stops notifications trying to access plugins. */ - ld->plugins = tal_free(ld->plugins); } diff --git a/lightningd/plugin.h b/lightningd/plugin.h index dcb3780464bf..5aef22ce1bd2 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -113,7 +113,7 @@ struct plugins { /* Blacklist of plugins from --disable-plugin */ const char **blacklist; - /* Whether we are shutting down (`plugins_free` is called) */ + /* Whether we are shutting down, blocks db write's */ bool shutdown; /* Index to show what order they were added in */ diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index db033428e1bf..b7411e84cb8c 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -337,6 +337,7 @@ void plugin_hook_db_sync(struct db *db) size_t i; size_t num_hooks; + db_check_plugins_not_shutdown(db); const char **changes = db_changes(db); num_hooks = tal_count(hook->hooks); if (num_hooks == 0) diff --git a/wallet/db.c b/wallet/db.c index 4e9eb042bdc6..cd4d486916a4 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1266,6 +1266,7 @@ struct db *db_setup(const tal_t *ctx, struct lightningd *ld, struct db *db = db_open(ctx, ld->wallet_dsn); bool migrated; db->log = new_log(db, ld->log_book, NULL, "database"); + db->plugins_shutdown = &ld->plugins->shutdown; db_begin_transaction(db); @@ -2340,6 +2341,11 @@ void db_changes_add(struct db_stmt *stmt, const char * expanded) tal_arr_expand(&db->changes, tal_strdup(db->changes, expanded)); } +void db_check_plugins_not_shutdown(struct db *db) +{ + assert(!*db->plugins_shutdown); +} + const char **db_changes(struct db *db) { return db->changes; diff --git a/wallet/db.h b/wallet/db.h index e51e75fd6b71..9907fdcbd061 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -249,6 +249,9 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, #define db_prepare_v2(db,query) \ db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) +/* Check that plugins are not shutting down when calling db_write hook */ +void db_check_plugins_not_shutdown(struct db *db); + /** * Access pending changes that have been added to the current transaction. */ diff --git a/wallet/db_common.h b/wallet/db_common.h index 0ab196c29081..ea635f7ebcc6 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -34,6 +34,9 @@ struct db { * Used to bump the data_version in the DB.*/ bool dirty; + /* Only needed to check shutdown state of plugins */ + const bool *plugins_shutdown; + /* The current DB version we expect to update if changes are * committed. */ u32 data_version; From 63bd569bf60df9eef581e375dff028ded889086e Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Tue, 2 Nov 2021 09:55:19 +0200 Subject: [PATCH 0067/1530] lightningd: cleanup, freeing jsonrpc in shutdown cannot trigger db write's anymore since PR #3867 utxos are unreserved by height, destroy_utxos and related functions are not used anymore so clean them up also However free(ld->jsonrpc) still needs to happen before free(ld) because its destructors need list_head pointers from ld --- lightningd/jsonrpc.h | 2 +- lightningd/lightningd.c | 6 +----- wallet/wallet.c | 27 --------------------------- wallet/wallet.h | 7 ------- 4 files changed, 2 insertions(+), 40 deletions(-) diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 16fc01049fd7..1455891d49b3 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -22,7 +22,7 @@ enum command_mode { /* Context for a command (from JSON, but might outlive the connection!). */ /* FIXME: move definition into jsonrpc.c */ struct command { - /* Off json_cmd->commands */ + /* Off list jcon->commands */ struct list_node list; /* The global state */ struct lightningd *ld; diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 248090c81593..6294c70aa8fa 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1193,12 +1193,8 @@ int main(int argc, char *argv[]) /* Tell plugins we're shutting down, closes the db for write access. */ shutdown_plugins(ld); - /* Clean up the JSON-RPC. This needs to happen in a DB transaction since - * it might actually be touching the DB in some destructors, e.g., - * unreserving UTXOs (see #1737) */ - db_begin_transaction(ld->wallet->db); + /* Cleanup JSON RPC separately: destructors assume some list_head * in ld */ tal_free(ld->jsonrpc); - db_commit_transaction(ld->wallet->db); /* Clean our our HTLC maps, since they use malloc. */ htlc_in_map_clear(&ld->htlcs_in); diff --git a/wallet/wallet.c b/wallet/wallet.c index b26ec943d823..ba4c8c3bb8cc 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -401,35 +401,8 @@ bool wallet_unreserve_output(struct wallet *w, OUTPUT_STATE_AVAILABLE); } -/** - * unreserve_utxo - Mark a reserved UTXO as available again - */ -static void unreserve_utxo(struct wallet *w, const struct utxo *unres) -{ - if (!wallet_update_output_status(w, &unres->outpoint, - OUTPUT_STATE_RESERVED, - OUTPUT_STATE_AVAILABLE)) { - fatal("Unable to unreserve output"); - } -} - -/** - * destroy_utxos - Destructor for an array of pointers to utxo - */ -static void destroy_utxos(const struct utxo **utxos, struct wallet *w) -{ - for (size_t i = 0; i < tal_count(utxos); i++) - unreserve_utxo(w, utxos[i]); -} - -void wallet_persist_utxo_reservation(struct wallet *w, const struct utxo **utxos) -{ - tal_del_destructor2(utxos, destroy_utxos, w); -} - void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos) { - tal_del_destructor2(utxos, destroy_utxos, w); for (size_t i = 0; i < tal_count(utxos); i++) { if (!wallet_update_output_status( w, &utxos[i]->outpoint, diff --git a/wallet/wallet.h b/wallet/wallet.h index ad2bcbfc0643..833f3ef60621 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1411,13 +1411,6 @@ void add_unreleased_tx(struct wallet *w, struct unreleased_tx *utx); /* These will touch the db, so need to be explicitly freed. */ void free_unreleased_txs(struct wallet *w); -/* wallet_persist_utxo_reservation - Removes destructor - * - * Persists the reservation in the database (until a restart) - * instead of clearing the reservation when the utxo object - * is destroyed */ -void wallet_persist_utxo_reservation(struct wallet *w, const struct utxo **utxos); - /* wallet_unreserve_output - Unreserve a utxo * * We unreserve utxos so that they can be spent elsewhere. From ef503f2feaa0c183b6325ef2cb0e90b865095952 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 10 Nov 2021 09:41:43 +0200 Subject: [PATCH 0068/1530] testing: remove test_stop_pending_fundchannel Not needed anymore, see previous commit --- tests/test_misc.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 47aac06e1288..f26ddc2b3af8 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -27,33 +27,6 @@ import unittest -@pytest.mark.developer("needs --dev-disconnect") -@pytest.mark.openchannel('v1') -def test_stop_pending_fundchannel(node_factory, executor): - """Stop the daemon while waiting for an accept_channel - - This used to crash the node, since we were calling unreserve_utxo while - freeing the daemon, but that needs a DB transaction to be open. - - """ - l1, l2 = node_factory.get_nodes(2) - - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - - # We want l2 to stop replying altogether, not disconnect - os.kill(l2.daemon.proc.pid, signal.SIGSTOP) - - # The fundchannel call will not terminate so run it in a future - executor.submit(l1.fundchannel, l2, 10**6) - l1.daemon.wait_for_log('peer_out WIRE_OPEN_CHANNEL') - - l1.rpc.stop() - - # Now allow l2 a clean shutdown - os.kill(l2.daemon.proc.pid, signal.SIGCONT) - l2.rpc.stop() - - def test_names(node_factory): # Note: # private keys: From 209614677adbc3494cf3c63aa3fd6919b558e154 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 10 Nov 2021 11:08:14 +0200 Subject: [PATCH 0069/1530] JSON RPC: In the shutdown loop, ignore plugin responses to JSON RPC requests --- lightningd/plugin.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index c0f3582b31d5..62f3a8e5fe1d 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -520,6 +520,11 @@ static const char *plugin_response_handle(struct plugin *plugin, "Received a JSON-RPC response for non-existent request"); } + /* Ignore responses when shutting down */ + if (plugin->plugins->ld->state == LD_STATE_SHUTDOWN) { + return NULL; + } + /* We expect the request->cb to copy if needed */ pd = plugin_detect_destruction(plugin); request->response_cb(plugin->buffer, toks, idtok, request->response_cb_arg); From 0388314ef89aeb50fc785e0a531063b43a415cfa Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 10 Nov 2021 11:08:49 +0200 Subject: [PATCH 0070/1530] JSON RPC: Calls made in shutdown loop receive error code -5: LIGHTNINGD_SHUTDOWN --- common/jsonrpc_errors.h | 3 +++ lightningd/jsonrpc.c | 5 +++++ lightningd/lightningd.c | 2 ++ 3 files changed, 10 insertions(+) diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index d7e5219f5bb6..f7cb09776738 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -28,6 +28,9 @@ static const errcode_t PLUGIN_ERROR = -3; /* Plugin terminated while handling a request. */ static const errcode_t PLUGIN_TERMINATED = -4; +/* Lightningd is shutting down while handling a request. */ +static const errcode_t LIGHTNINGD_SHUTDOWN = -5; + /* Errors from `pay`, `sendpay`, or `waitsendpay` commands */ static const errcode_t PAY_IN_PROGRESS = 200; static const errcode_t PAY_RHASH_ALREADY_USED = 201; diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 3b2d2e077989..abaa940d15c8 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -933,6 +933,11 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) json_tok_full(jcon->buffer, method)); } + if (jcon->ld->state == LD_STATE_SHUTDOWN) { + return command_fail(c, LIGHTNINGD_SHUTDOWN, + "lightningd is shutting down"); + } + rpc_hook = tal(c, struct rpc_command_hook_payload); rpc_hook->cmd = c; /* Duplicate since we might outlive the connection */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 6294c70aa8fa..5a9cc58a62cd 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1168,6 +1168,8 @@ int main(int argc, char *argv[]) * shut down. */ assert(io_loop_ret == ld); + + /* Fail JSON RPC requests and ignore plugin's responses */ ld->state = LD_STATE_SHUTDOWN; stop_fd = -1; From 89cbdf4dcedfdaec82a21be5e61d8100c5ff6416 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 10 Nov 2021 10:31:06 +0200 Subject: [PATCH 0071/1530] testing: test that RPC calls made in shutdown fail and receive error code -5 and the two conditions in which plugins can receive shutdown notification --- tests/plugins/misc_notifications.py | 20 +++++++++++++++++++- tests/test_plugin.py | 8 +++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/tests/plugins/misc_notifications.py b/tests/plugins/misc_notifications.py index bc0ab621e566..d1b04f320f54 100755 --- a/tests/plugins/misc_notifications.py +++ b/tests/plugins/misc_notifications.py @@ -2,9 +2,10 @@ """Plugin to be used to test miscellaneous notifications. """ -from pyln.client import Plugin +from pyln.client import Plugin, RpcError from time import sleep import sys +import pytest plugin = Plugin() @@ -29,6 +30,23 @@ def channel_state_changed(plugin, channel_state_changed, **kwargs): @plugin.subscribe("shutdown") def shutdown(plugin, **kwargs): + plugin.log("received shutdown notification") + + # 'shutdown' notification can be called in two ways, from `plugin stop` or from + # lightningd's shutdown loop, we test which one by making `getinfo` call + try: + plugin.rpc.getinfo() + plugin.rpc.datastore(key='test', string='Allowed', mode="create-or-append") + plugin.log("datastore success") + except RpcError as e: + if e.error == {'code': -5, 'message': 'lightningd is shutting down'}: + # JSON RPC is disabled by now, but can do logging + with pytest.raises(RpcError, match=r'-5.*lightningd is shutting down'): + plugin.rpc.datastore(key='test', string='Not allowed', mode="create-or-append") + plugin.log("datastore failed") + else: + raise + plugin.log("delaying shutdown with 5s") sleep(5) sys.exit(0) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 5d054af6acc0..3141aaa4d75c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1086,7 +1086,7 @@ def test_htlc_accepted_hook_direct_restart(node_factory, executor): def test_htlc_accepted_hook_shutdown(node_factory, executor): """Hooks of important-plugins are never removed and these plugins are kept - alive until after subdaemons are shutdown. + alive until after subdaemons are shutdown. Also tests shutdown notification. """ l1, l2 = node_factory.line_graph(2, opts=[ {'may_reconnect': True, 'log-level': 'info'}, @@ -1095,6 +1095,10 @@ def test_htlc_accepted_hook_shutdown(node_factory, executor): 'important-plugin': [os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py')]} ]) + l2.rpc.plugin_stop(os.path.join(os.getcwd(), 'tests/plugins/misc_notifications.py')) + l2.daemon.wait_for_log(r'datastore success') + l2.rpc.plugin_start(os.path.join(os.getcwd(), 'tests/plugins/misc_notifications.py')) + i1 = l2.rpc.invoice(msatoshi=1000, label="inv1", description="desc")['bolt11'] # fail_htlcs.py makes payment fail @@ -1107,6 +1111,8 @@ def test_htlc_accepted_hook_shutdown(node_factory, executor): # Should still fail htlc while shutting down with pytest.raises(RpcError): l1.rpc.pay(i1) + + l2.daemon.wait_for_log(r'datastore failed') f_stop.result() From 2240f09f8b168117c57330e639243e76ab2cc2e5 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Sat, 6 Nov 2021 10:02:34 +0200 Subject: [PATCH 0072/1530] testing: fix test_closing_higherfee shutdown_subdaemons frees the channel and calls destroy_close_command_on_channel_destroy, see gdb: 0 destroy_close_command_on_channel_destroy (_=0x55db6ca38e18, cc=0x55db6ca43338) at lightningd/closing_control.c:94 1 0x000055db6a8181b5 in notify (ctx=0x55db6ca38df0, type=TAL_NOTIFY_FREE, info=0x55db6ca38e18, saved_errno=0) at ccan/ccan/tal/tal.c:237 2 0x000055db6a8186bb in del_tree (t=0x55db6ca38df0, orig=0x55db6ca38e18, saved_errno=0) at ccan/ccan/tal/tal.c:402 3 0x000055db6a818a47 in tal_free (ctx=0x55db6ca38e18) at ccan/ccan/tal/tal.c:486 4 0x000055db6a73fffa in shutdown_subdaemons (ld=0x55db6c8b4ca8) at lightningd/lightningd.c:543 5 0x000055db6a741098 in main (argc=21, argv=0x7ffffa3e8048) at lightningd/lightningd.c:1192 Before this PR, there was no io_loop after shutdown_subdaemons and client side raised a general `Connection to RPC server lost.` Now we test the more specific `Channel forgotten before proper close.`, which is good! BTW, this test was added recently in PR #4599. --- tests/test_closing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index ea85d82ec878..ea0f448d45f1 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3099,7 +3099,7 @@ def test_closing_higherfee(node_factory, bitcoind, executor): l1.daemon.wait_for_log(r'deriving max fee from rate 30000 -> 16440sat \(not 1000000sat\)') # This will fail because l1 restarted! - with pytest.raises(RpcError, match=r'Connection to RPC server lost.'): + with pytest.raises(RpcError, match=r'Channel forgotten before proper close.'): fut.result(TIMEOUT) # But we still complete negotiation! From aae5e7822f2f320e29d168d224ad27b2b7d0d7f5 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Sun, 7 Nov 2021 12:15:31 +0200 Subject: [PATCH 0073/1530] testing: fix test_upgrade_statickey_onchaind Seems a timing issue that should be figured out, his makes the test pass. --- tests/test_connection.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 005032cc2c7c..0726697bd9de 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3541,6 +3541,9 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): l1.daemon.wait_for_log('option_static_remotekey enabled at 1/1') # Pre-statickey penalty works. + # FIXME: Without this sleep, l1 will broadcasts one tx more compared to good + # case, causing `wait_for_onchaind_broadcast` to timeout. + time.sleep(5) bitcoind.rpc.sendrawtransaction(tx) bitcoind.generate_block(1) From e08528b7b5cf5063a20da745b8013f132521759d Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 8 Nov 2021 16:22:49 +0200 Subject: [PATCH 0074/1530] libplugin-pay: fix valgrind error for the case rpc "listpeers" returns an error, such as in shutdown --- plugins/libplugin-pay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 01626e664daa..e2d240ba22e4 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3231,7 +3231,7 @@ static struct command_result *direct_pay_listpeers(struct command *cmd, json_to_listpeers_result(tmpctx, buffer, toks); struct direct_pay_data *d = payment_mod_directpay_get_data(p); - if (tal_count(r->peers) == 1) { + if (r && tal_count(r->peers) == 1) { struct listpeers_peer *peer = r->peers[0]; if (!peer->connected) goto cont; From ae4623c21a30e2b32e0cc17989fec71398a9d210 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 8 Nov 2021 18:19:20 +0200 Subject: [PATCH 0075/1530] lightingd: removal of sigchild_handler in shutdown now also closes its pipe Setting SIGCHLD back to default (i.e. ignored) makes waitpid hang on an old SIGCHLD that was still in the pipe? This happens running test_important_plugin with developer=1: (or with dev=0 and build-in plugins subscribed to "shutdown") 0 0x00007ff8336b6437 in __GI___waitpid (pid=-1, stat_loc=0x0, options=1) at ../sysdeps/unix/sysv/linux/waitpid.c:30 1 0x000055fb468f733a in sigchld_rfd_in (conn=0x55fb47c7cfc8, ld=0x55fb47bdce58) at lightningd/lightningd.c:785 2 0x000055fb469bcc6b in next_plan (conn=0x55fb47c7cfc8, plan=0x55fb47c7cfe8) at ccan/ccan/io/io.c:59 3 0x000055fb469bd80b in do_plan (conn=0x55fb47c7cfc8, plan=0x55fb47c7cfe8, idle_on_epipe=false) at ccan/ccan/io/io.c:407 4 0x000055fb469bd849 in io_ready (conn=0x55fb47c7cfc8, pollflags=1) at ccan/ccan/io/io.c:417 5 0x000055fb469bfa26 in io_loop (timers=0x55fb47c41198, expired=0x7ffdf4be9028) at ccan/ccan/io/poll.c:453 6 0x000055fb468f1be9 in io_loop_with_timers (ld=0x55fb47bdce58) at lightningd/io_loop_with_timers.c:21 7 0x000055fb46924817 in shutdown_plugins (ld=0x55fb47bdce58) at lightningd/plugin.c:2114 8 0x000055fb468f7c92 in main (argc=22, argv=0x7ffdf4be9228) at lightningd/lightningd.c:1195 --- lightningd/lightningd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 5a9cc58a62cd..3df71745c438 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -757,13 +757,14 @@ static int setup_sig_handlers(void) /*~ This removes the SIGCHLD handler, so we don't try to write * to a broken pipe. */ -static void remove_sigchild_handler(void) +static void remove_sigchild_handler(struct io_conn *sigchld_conn) { struct sigaction sigchild; memset(&sigchild, 0, sizeof(struct sigaction)); sigchild.sa_handler = SIG_DFL; sigaction(SIGCHLD, &sigchild, NULL); + io_close(sigchld_conn); } /*~ This is the routine which sets up the sigchild handling. We just @@ -854,6 +855,7 @@ int main(int argc, char *argv[]) struct htlc_in_map *unconnected_htlcs_in; struct ext_key *bip32_base; int sigchld_rfd; + struct io_conn *sigchld_conn; int exit_code = 0; char **orig_argv; bool try_reexec; @@ -1099,10 +1101,8 @@ int main(int argc, char *argv[]) * "funding transaction spent" event which creates it. */ onchaind_replay_channels(ld); - /*~ Now handle sigchld, so we can clean up appropriately. - * We don't keep a pointer to this, so our simple leak detection - * code gets upset unless we mark it notleak(). */ - notleak(io_new_conn(ld, sigchld_rfd, sigchld_rfd_in, ld)); + /*~ Now handle sigchld, so we can clean up appropriately. */ + sigchld_conn = notleak(io_new_conn(ld, sigchld_rfd, sigchld_rfd_in, ld)); /*~ Mark ourselves live. * @@ -1189,7 +1189,7 @@ int main(int argc, char *argv[]) stop_topology(ld->topology); /* We're not going to collect our children. */ - remove_sigchild_handler(); + remove_sigchild_handler(sigchld_conn); shutdown_subdaemons(ld); /* Tell plugins we're shutting down, closes the db for write access. */ From 4dded23cd3da6e3268f3e91e966db59c1f735c75 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Tue, 23 Nov 2021 13:01:56 +0200 Subject: [PATCH 0076/1530] hsmtool: use flag TCSANOW when disabling tty ECHO, fixes rare hang in test_hsm* No idea why TCSAFLUSH was used, could not find anything in PR comments. Also cannot explain exactly what causes the problem, but the hang can be reproduced *with* TCSAFLUSH and not with TCSANOW. According to termios doc: TCSANOW the change occurs immediately. TCSAFLUSH the change occurs after all output written to the object referred by fd has been transmitted, and all input that has been received but not read will be discarded before the change is made. --- common/hsm_encryption.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index c52fa8d6ca90..7e5778854d59 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -103,7 +103,7 @@ char *read_stdin_pass(char **reason) } temp_term = current_term; temp_term.c_lflag &= ~ECHO; - if (tcsetattr(fileno(stdin), TCSAFLUSH, &temp_term) != 0) { + if (tcsetattr(fileno(stdin), TCSANOW, &temp_term) != 0) { *reason = "Could not disable pass echoing."; return NULL; } @@ -114,7 +114,7 @@ char *read_stdin_pass(char **reason) } /* Restore the original terminal */ - if (tcsetattr(fileno(stdin), TCSAFLUSH, ¤t_term) != 0) { + if (tcsetattr(fileno(stdin), TCSANOW, ¤t_term) != 0) { *reason = "Could not restore terminal options."; free(passwd); return NULL; From a2a480e636476708a7226e257a9ca35de1fc00f4 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 24 Nov 2021 15:16:43 +0200 Subject: [PATCH 0077/1530] doc: update shutdown notification, changelog Fixes: #4785 Fixes: #4883 Changelog-Changed: Plugins: `shutdown` notification is now send when lightningd is almost completely shutdown, RPC calls then fail with error code -5. --- doc/PLUGINS.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 7b57538de0e8..c1b94c22e00a 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -793,12 +793,14 @@ here, with the peer's signatures attached. ### `shutdown` -Called when lightningd is shutting down, or this plugin has been -shutdown by the plugin stop command. The plugin has 30 seconds to -exit itself, otherwise it's killed. - -Because lightningd can crash or be killed, a plugin cannot rely on -this function always called. +Send in two situations: lightningd is (almost completely) shutdown, or the plugin +`stop` command has been called for this plugin. In both cases the plugin has 30 +seconds to exit itself, otherwise it's killed. + +In the shutdown case, plugins should not interact with lightnind except via (id-less) +logging or notifications. New rpc calls will fail with error code -5 and (plugin's) +responses will be ignored. Because lightningd can crash or be killed, a plugin cannot +rely on the shutdown notification always been send. ## Hooks From 166ee4bac8a16301d8f336e7293b81863725bea6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:35:44 +1030 Subject: [PATCH 0078/1530] plugins/Makefile: improve header dependencies. Not all plugins depended on their headers. Keep it simple: all plugins depend on all plugin headers. Signed-off-by: Rusty Russell --- plugins/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index 79bbadea3149..3562243cd8c4 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -141,8 +141,10 @@ PLUGIN_COMMON_OBJS := \ wire/tlvstream.o \ wire/towire.o +# Make all plugins depend on all plugin headers, for simplicity. +$(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER) + plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o -$(PLUGIN_PAY_OBJS): $(PLUGIN_PAY_LIB_HEADER) plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) @@ -165,8 +167,6 @@ plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) -$(PLUGIN_ALL_OBJS): $(PLUGIN_LIB_HEADER) - # Generated from PLUGINS definition in plugins/Makefile ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile From b74848f6f6fc5a878b562667b0bc5230f474c555 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:04 +1030 Subject: [PATCH 0079/1530] common: remove support for pre v0.10.2 onionmessages. Temporarily disable sendpay_blinding test which uses obsolete onionmsg; there's still some debate on the PR about how blinded HTLCs will work. Changelog-EXPERIMENTAL: onionmessage: removed support for v0.10.1 onion messages. Signed-off-by: Rusty Russell --- channeld/channeld.c | 1 - common/blindedpath.c | 89 ------ common/blindedpath.h | 7 - devtools/blindedpath.c | 10 +- doc/PLUGINS.md | 8 +- gossipd/gossipd.c | 269 ---------------- gossipd/gossipd_wire.csv | 22 -- lightningd/gossip_control.c | 7 - lightningd/onion_message.c | 415 +------------------------ lightningd/onion_message.h | 3 - openingd/dualopend.c | 3 - plugins/fetchinvoice.c | 125 +------- plugins/offers.c | 112 +------ plugins/offers.h | 4 - plugins/offers_inv_hook.c | 26 +- plugins/offers_inv_hook.h | 8 +- plugins/offers_invreq_hook.c | 28 +- plugins/offers_invreq_hook.h | 8 +- tests/test_misc.py | 71 ----- tests/test_pay.py | 18 +- wire/extracted_onion_01_offers.patch | 23 +- wire/extracted_onion_02_newonion.patch | 30 -- wire/extracted_peer_01_offers.patch | 12 - wire/extracted_peer_05_newonion.patch | 6 +- wire/onion_wire.csv | 11 - wire/peer_wire.c | 2 - wire/peer_wire.csv | 6 - 27 files changed, 53 insertions(+), 1271 deletions(-) delete mode 100644 wire/extracted_onion_02_newonion.patch delete mode 100644 wire/extracted_peer_01_offers.patch diff --git a/channeld/channeld.c b/channeld/channeld.c index 8d26ad1ac9b4..73dad4261c65 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2321,7 +2321,6 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_WARNING: case WIRE_ERROR: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: abort(); } diff --git a/common/blindedpath.c b/common/blindedpath.c index 7ab13e303c36..ab16d8569511 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -11,95 +11,6 @@ #define SUPERVERBOSE(...) #endif -/* Obsolete version: use enctlv() helper. */ -struct onionmsg_path **make_blindedpath(const tal_t *ctx, - const struct pubkey *route, - struct pubkey *initial_blinding, - struct pubkey *final_blinding) -{ - struct privkey e; - struct pubkey *pk_e, *b; - struct secret *rho; - struct onionmsg_path **path; - size_t num = tal_count(route); - - if (!num) - abort(); - - /* E(i) */ - pk_e = tal_arr(tmpctx, struct pubkey, num); - /* B(i) */ - b = tal_arr(tmpctx, struct pubkey, num); - /* rho(i) */ - rho = tal_arr(tmpctx, struct secret, num); - - randombytes_buf(&e, sizeof(e)); - if (!pubkey_from_privkey(&e, &pk_e[0])) - abort(); - - for (size_t i = 0; i < num; i++) { - struct secret ss; - struct secret hmac; - struct sha256 h; - - if (secp256k1_ecdh(secp256k1_ctx, ss.data, - &route[i].pubkey, e.secret.data, - NULL, NULL) != 1) - abort(); - - subkey_from_hmac("blinded_node_id", &ss, &hmac); - b[i] = route[i]; - if (i != 0) { - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &b[i].pubkey, hmac.data) != 1) - abort(); - } - subkey_from_hmac("rho", &ss, &rho[i]); - blinding_hash_e_and_ss(&pk_e[i], &ss, &h); - if (i != num-1) - blinding_next_pubkey(&pk_e[i], &h, &pk_e[i+1]); - blinding_next_privkey(&e, &h, &e); - } - - *initial_blinding = pk_e[0]; - *final_blinding = pk_e[num-1]; - - path = tal_arr(ctx, struct onionmsg_path *, num); - for (size_t i = 0; i < num; i++) { - path[i] = tal(path, struct onionmsg_path); - path[i]->node_id = b[i]; - } - - for (size_t i = 0; i < num - 1; i++) { - const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - struct tlv_encmsg_tlvs *inner; - int ret; - - /* Inner is encrypted */ - inner = tlv_encmsg_tlvs_new(tmpctx); - /* FIXME: We could support scids, too */ - inner->next_node_id = cast_const(struct pubkey *, &route[i+1]); - - path[i]->enctlv = tal_arr(path, u8, 0); - towire_encmsg_tlvs(&path[i]->enctlv, inner); - towire_pad(&path[i]->enctlv, - crypto_aead_chacha20poly1305_ietf_ABYTES); - - ret = crypto_aead_chacha20poly1305_ietf_encrypt(path[i]->enctlv, NULL, - path[i]->enctlv, - tal_bytelen(path[i]->enctlv) - crypto_aead_chacha20poly1305_ietf_ABYTES, - NULL, 0, - NULL, npub, - rho[i].data); - assert(ret == 0); - } - - /* Final one has no enctlv */ - path[num-1]->enctlv = NULL; - - return path; -} - /* Blinds node_id and calculates next blinding factor. */ static bool blind_node(const struct privkey *blinding, const struct secret *ss, diff --git a/common/blindedpath.h b/common/blindedpath.h index d4adb7bec6b0..1a54f5c1da2e 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -10,13 +10,6 @@ struct pubkey; struct privkey; struct secret; -/* Fills in *initial_blinding and *final_blinding and returns - * onionmsg_path array for this route */ -struct onionmsg_path **make_blindedpath(const tal_t *ctx, - const struct pubkey *route, - struct pubkey *initial_blinding, - struct pubkey *final_blinding); - /** * create_enctlv - Encrypt an encmsg to form an enctlv. * @ctx: tal context diff --git a/devtools/blindedpath.c b/devtools/blindedpath.c index 759b2781e631..7647e8c5894f 100644 --- a/devtools/blindedpath.c +++ b/devtools/blindedpath.c @@ -144,15 +144,7 @@ int main(int argc, char **argv) /* Inner is encrypted */ inner = tlv_encmsg_tlvs_new(tmpctx); - /* Use scid if they provided one */ - if (scids[i]) { - inner->obs_next_short_channel_id - = tal_dup(inner, struct short_channel_id, - scids[i]); - } else { - inner->next_node_id - = tal_dup(inner, struct pubkey, &nodes[i+1]); - } + inner->next_node_id = tal_dup(inner, struct pubkey, &nodes[i+1]); p = tal_arr(tmpctx, u8, 0); towire_encmsg_tlvs(&p, inner); diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index c1b94c22e00a..752b412335bb 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1514,13 +1514,12 @@ type prefix, since c-lightning does not know how to parse the message. Because this is a chained hook, the daemon expects the result to be `{'result': 'continue'}`. It will fail if something else is returned. -### `onion_message`, `onion_message_blinded` and `onion_message_ourpath` +### `onion_message_blinded` and `onion_message_ourpath` **(WARNING: experimental-offers only)** -These three hooks are almost identical, in that they are called when -an onion message is received. The `onion_message` hook is only used -for obsolete unblinded messages, and can be ignored for modern usage. +These two hooks are almost identical, in that they are called when +an onion message is received. `onion_message_blinded` is used for unsolicited messages (where the source knows that it is sending to this node), and @@ -1556,6 +1555,7 @@ The payload for a call follows this format: All fields shown here are optional. We suggest just returning `{'result': 'continue'}`; any other result +Signed-off-by: Rusty Russell will cause the message not to be handed to any other hooks. ## Bitcoin backend diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index bbbf4804c972..cdc75156ded2 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -350,235 +350,6 @@ static bool handle_local_channel_announcement(struct daemon *daemon, return true; } -/* Peer sends obsolete onion msg. */ -static u8 *handle_obs_onion_message(struct peer *peer, const u8 *msg) -{ - enum onion_wire badreason; - struct onionpacket *op; - struct secret ss, *blinding_ss; - struct pubkey *blinding_in, ephemeral; - struct route_step *rs; - u8 *onion; - const u8 *cursor; - size_t max, maxlen; - struct tlv_onionmsg_payload *om; - struct tlv_obs_onion_message_tlvs *tlvs = tlv_obs_onion_message_tlvs_new(msg); - - /* Ignore unless explicitly turned on. */ - if (!feature_offered(peer->daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], - OPT_ONION_MESSAGES)) - return NULL; - - /* FIXME: ratelimit! */ - if (!fromwire_obs_onion_message(msg, msg, &onion, tlvs)) - return towire_warningfmt(peer, NULL, "Bad onion_message"); - - /* We unwrap the onion now. */ - op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); - if (!op) { - status_debug("peer %s: onion msg: can't parse onionpacket: %s", - type_to_string(tmpctx, struct node_id, &peer->id), - onion_wire_name(badreason)); - return NULL; - } - - ephemeral = op->ephemeralkey; - if (tlvs->blinding) { - struct secret hmac; - - /* E(i) */ - blinding_in = tal_dup(msg, struct pubkey, tlvs->blinding); - status_debug("peer %s: blinding in = %s", - type_to_string(tmpctx, struct node_id, &peer->id), - type_to_string(tmpctx, struct pubkey, blinding_in)); - blinding_ss = tal(msg, struct secret); - ecdh(blinding_in, blinding_ss); - - /* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */ - subkey_from_hmac("blinded_node_id", blinding_ss, &hmac); - - /* We instead tweak the *ephemeral* key from the onion and use - * our normal privkey: since hsmd knows only how to ECDH with - * our real key */ - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &ephemeral.pubkey, - hmac.data) != 1) { - status_debug("peer %s: onion msg: can't tweak pubkey", - type_to_string(tmpctx, struct node_id, &peer->id)); - return NULL; - } - } else { - blinding_ss = NULL; - blinding_in = NULL; - } - - ecdh(&ephemeral, &ss); - - /* We make sure we can parse onion packet, so we know if shared secret - * is actually valid (this checks hmac). */ - rs = process_onionpacket(tmpctx, op, &ss, NULL, 0, false); - if (!rs) { - status_debug("peer %s: onion msg: can't process onionpacket ss=%s", - type_to_string(tmpctx, struct node_id, &peer->id), - type_to_string(tmpctx, struct secret, &ss)); - return NULL; - } - - /* The raw payload is prepended with length in the TLV world. */ - cursor = rs->raw_payload; - max = tal_bytelen(rs->raw_payload); - maxlen = fromwire_bigsize(&cursor, &max); - if (!cursor) { - status_debug("peer %s: onion msg: Invalid hop payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - if (maxlen > max) { - status_debug("peer %s: onion msg: overlong hop payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - - om = tlv_onionmsg_payload_new(msg); - if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { - status_debug("peer %s: onion msg: invalid onionmsg_payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - - /* If we weren't given a blinding factor, tlv can provide one. */ - if (om->obs_blinding && !blinding_ss) { - /* E(i) */ - blinding_in = tal_dup(msg, struct pubkey, om->obs_blinding); - blinding_ss = tal(msg, struct secret); - - ecdh(blinding_in, blinding_ss); - } - - if (om->enctlv) { - const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - u8 *dec; - struct secret rho; - int ret; - - if (!blinding_ss) { - status_debug("peer %s: enctlv but no blinding?", - type_to_string(tmpctx, struct node_id, &peer->id)); - return NULL; - } - - /* We need this to decrypt enctlv */ - subkey_from_hmac("rho", blinding_ss, &rho); - - /* Overrides next_scid / next_node */ - if (tal_bytelen(om->enctlv) - < crypto_aead_chacha20poly1305_ietf_ABYTES) { - status_debug("peer %s: enctlv too short for mac", - type_to_string(tmpctx, struct node_id, &peer->id)); - return NULL; - } - - dec = tal_arr(msg, u8, - tal_bytelen(om->enctlv) - - crypto_aead_chacha20poly1305_ietf_ABYTES); - ret = crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL, - NULL, - om->enctlv, - tal_bytelen(om->enctlv), - NULL, 0, - npub, - rho.data); - if (ret != 0) { - status_debug("peer %s: Failed to decrypt enctlv field", - type_to_string(tmpctx, struct node_id, &peer->id)); - return NULL; - } - - status_debug("peer %s: enctlv -> %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, dec)); - - /* Replace onionmsg with one from enctlv */ - cursor = dec; - maxlen = tal_bytelen(dec); - - om = tlv_onionmsg_payload_new(msg); - if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { - status_debug("peer %s: onion msg: invalid enctlv onionmsg_payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, dec)); - return NULL; - } - } else if (blinding_ss && rs->nextcase != ONION_END) { - status_debug("peer %s: Onion had %s, but not enctlv?", - type_to_string(tmpctx, struct node_id, &peer->id), - tlvs->blinding ? "blinding" : "om blinding"); - return NULL; - } - - if (rs->nextcase == ONION_END) { - struct pubkey *blinding; - const struct onionmsg_path **path; - u8 *omsg; - - if (om->obs_reply_path) { - blinding = &om->obs_reply_path->blinding; - path = cast_const2(const struct onionmsg_path **, - om->obs_reply_path->path); - } else { - blinding = NULL; - path = NULL; - } - - /* We re-marshall here by policy, before handing to lightningd */ - omsg = tal_arr(tmpctx, u8, 0); - towire_tlvstream_raw(&omsg, om->fields); - daemon_conn_send(peer->daemon->master, - take(towire_gossipd_got_obs_onionmsg_to_us(NULL, - blinding_in, - blinding, - path, - omsg))); - } else { - struct pubkey *next_blinding; - struct node_id *next_node; - - /* This *MUST* have instructions on where to go next. */ - if (!om->obs_next_short_channel_id && !om->obs_next_node_id) { - status_debug("peer %s: onion msg: no next field in %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - - if (blinding_ss) { - /* E(i-1) = H(E(i) || ss(i)) * E(i) */ - struct sha256 h; - blinding_hash_e_and_ss(blinding_in, blinding_ss, &h); - next_blinding = tal(msg, struct pubkey); - blinding_next_pubkey(blinding_in, &h, next_blinding); - } else - next_blinding = NULL; - - if (om->obs_next_node_id) { - next_node = tal(tmpctx, struct node_id); - node_id_from_pubkey(next_node, om->obs_next_node_id); - } else - next_node = NULL; - - daemon_conn_send(peer->daemon->master, - take(towire_gossipd_got_obs_onionmsg_forward(NULL, - om->obs_next_short_channel_id, - next_node, - next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); - } - return NULL; -} - /* Peer sends onion msg. */ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) { @@ -727,38 +498,6 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) return NULL; } -/* We send an obsolete onion msg. */ -static struct io_plan *obs_onionmsg_req(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - struct node_id id; - u8 *onion_routing_packet; - struct pubkey *blinding; - struct peer *peer; - - if (!fromwire_gossipd_send_obs_onionmsg(msg, msg, &id, &onion_routing_packet, - &blinding)) - master_badmsg(WIRE_GOSSIPD_SEND_OBS_ONIONMSG, msg); - - /* Even if lightningd were to check for valid ids, there's a race - * where it might vanish before we read this command; cleaner to - * handle it here with 'sent' = false. */ - peer = find_peer(daemon, &id); - if (peer) { - struct tlv_obs_onion_message_tlvs *tlvs; - - tlvs = tlv_obs_onion_message_tlvs_new(msg); - if (blinding) - tlvs->blinding = tal_dup(tlvs, struct pubkey, blinding); - - queue_peer_msg(peer, - take(towire_obs_onion_message(NULL, - onion_routing_packet, - tlvs))); - } - return daemon_conn_read_next(conn, daemon->master); -} - static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { @@ -814,9 +553,6 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, case WIRE_REPLY_SHORT_CHANNEL_IDS_END: err = handle_reply_short_channel_ids_end(peer, msg); goto handled_relay; - case WIRE_OBS_ONION_MESSAGE: - err = handle_obs_onion_message(peer, msg); - goto handled_relay; case WIRE_ONION_MESSAGE: err = handle_onion_message(peer, msg); goto handled_relay; @@ -1587,9 +1323,6 @@ static struct io_plan *recv_req(struct io_conn *conn, break; #endif /* !DEVELOPER */ - case WIRE_GOSSIPD_SEND_OBS_ONIONMSG: - return obs_onionmsg_req(conn, daemon, msg); - case WIRE_GOSSIPD_SEND_ONIONMSG: return onionmsg_req(conn, daemon, msg); @@ -1599,8 +1332,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_TO_US: - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_FORWARD: case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: break; diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 73a1e68210e9..2deab8752c2b 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -74,22 +74,6 @@ msgdata,gossipd_dev_compact_store_reply,success,bool, msgtype,gossipd_new_blockheight,3026 msgdata,gossipd_new_blockheight,blockheight,u32, -# Tell lightningd we got an obsolete onion message (for us, or to fwd) -msgtype,gossipd_got_obs_onionmsg_to_us,3142 -msgdata,gossipd_got_obs_onionmsg_to_us,blinding_in,?pubkey, -msgdata,gossipd_got_obs_onionmsg_to_us,reply_blinding,?pubkey, -msgdata,gossipd_got_obs_onionmsg_to_us,reply_path_len,u16, -msgdata,gossipd_got_obs_onionmsg_to_us,reply_path,onionmsg_path,reply_path_len -msgdata,gossipd_got_obs_onionmsg_to_us,rawmsg_len,u16, -msgdata,gossipd_got_obs_onionmsg_to_us,rawmsg,u8,rawmsg_len - -msgtype,gossipd_got_obs_onionmsg_forward,3143 -msgdata,gossipd_got_obs_onionmsg_forward,next_scid,?short_channel_id, -msgdata,gossipd_got_obs_onionmsg_forward,next_node_id,?node_id, -msgdata,gossipd_got_obs_onionmsg_forward,next_blinding,?pubkey, -msgdata,gossipd_got_obs_onionmsg_forward,next_onion_len,u16, -msgdata,gossipd_got_obs_onionmsg_forward,next_onion,u8,next_onion_len - msgtype,gossipd_got_onionmsg_to_us,3145 msgdata,gossipd_got_onionmsg_to_us,node_alias,pubkey, msgdata,gossipd_got_onionmsg_to_us,self_id,?secret, @@ -101,12 +85,6 @@ msgdata,gossipd_got_onionmsg_to_us,rawmsg_len,u16, msgdata,gossipd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len # Lightningd tells us to send a onion message. -msgtype,gossipd_send_obs_onionmsg,3040 -msgdata,gossipd_send_obs_onionmsg,id,node_id, -msgdata,gossipd_send_obs_onionmsg,onion_len,u16, -msgdata,gossipd_send_obs_onionmsg,onion,u8,onion_len -msgdata,gossipd_send_obs_onionmsg,blinding,?pubkey, - msgtype,gossipd_send_onionmsg,3041 msgdata,gossipd_send_onionmsg,id,node_id, msgdata,gossipd_send_onionmsg,onion_len,u16, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index fd9caec3afa5..a1bd82203942 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -123,7 +123,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_DEV_COMPACT_STORE: case WIRE_GOSSIPD_DEV_SET_TIME: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: - case WIRE_GOSSIPD_SEND_OBS_ONIONMSG: case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: /* This is a reply, so never gets through to here. */ @@ -134,12 +133,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_ADDGOSSIP_REPLY: break; - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_TO_US: - handle_obs_onionmsg_to_us(gossip->ld, msg); - break; - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_FORWARD: - handle_obs_onionmsg_forward(gossip->ld, msg); - break; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: handle_onionmsg_to_us(gossip->ld, msg); break; diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 476232f09622..e1bdd27a1306 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -15,15 +15,11 @@ #include struct onion_message_hook_payload { - /* Pre-spec or modern? */ - bool obsolete; - /* Optional */ - struct pubkey *blinding_in; /* obsolete only */ struct pubkey *reply_blinding; struct onionmsg_path **reply_path; - struct pubkey *reply_first_node; /* non-obsolete only */ - struct pubkey *our_alias; /* non-obsolete only */ + struct pubkey *reply_first_node; + struct pubkey *our_alias; struct tlv_onionmsg_payload *om; }; @@ -53,34 +49,16 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload, struct plugin *plugin) { json_object_start(stream, "onion_message"); - json_add_bool(stream, "obsolete", payload->obsolete); - if (payload->blinding_in) - json_add_pubkey(stream, "blinding_in", payload->blinding_in); if (payload->our_alias) json_add_pubkey(stream, "our_alias", payload->our_alias); - /* Modern style. */ if (payload->reply_first_node) { json_add_blindedpath(stream, "reply_blindedpath", payload->reply_blinding, payload->reply_first_node, payload->reply_path); - } else if (payload->reply_path) { - json_array_start(stream, "reply_path"); - for (size_t i = 0; i < tal_count(payload->reply_path); i++) { - json_object_start(stream, NULL); - json_add_pubkey(stream, "id", - &payload->reply_path[i]->node_id); - if (tal_bytelen(payload->reply_path[i]->enctlv) != 0) - json_add_hex_talarr(stream, "enctlv", - payload->reply_path[i]->enctlv); - if (i == 0) - json_add_pubkey(stream, "blinding", - payload->reply_blinding); - json_object_end(stream); - } - json_array_end(stream); } + /* Common convenience fields */ if (payload->om->invoice_request) json_add_hex_talarr(stream, "invoice_request", @@ -117,12 +95,6 @@ onion_message_hook_cb(struct onion_message_hook_payload *payload STEALS) /* Two hooks, because it's critical we only accept blinding if we expect that * exact blinding key. Otherwise, we can be probed using old blinded paths. */ -REGISTER_PLUGIN_HOOK(onion_message, - plugin_hook_continue, - onion_message_hook_cb, - onion_message_serialize, - struct onion_message_hook_payload *); - REGISTER_PLUGIN_HOOK(onion_message_blinded, plugin_hook_continue, onion_message_hook_cb, @@ -135,96 +107,6 @@ REGISTER_PLUGIN_HOOK(onion_message_ourpath, onion_message_serialize, struct onion_message_hook_payload *); -void handle_obs_onionmsg_to_us(struct lightningd *ld, const u8 *msg) -{ - struct onion_message_hook_payload *payload; - u8 *submsg; - size_t submsglen; - const u8 *subptr; - -#if DEVELOPER - if (ld->dev_ignore_obsolete_onion) - return; -#endif - - payload = tal(ld, struct onion_message_hook_payload); - payload->obsolete = true; - payload->reply_first_node = NULL; - payload->om = tlv_onionmsg_payload_new(payload); - payload->our_alias = NULL; - - if (!fromwire_gossipd_got_obs_onionmsg_to_us(payload, msg, - &payload->blinding_in, - &payload->reply_blinding, - &payload->reply_path, - &submsg)) { - log_broken(ld->log, "bad got_onionmsg_tous: %s", - tal_hex(tmpctx, msg)); - return; - } - submsglen = tal_bytelen(submsg); - subptr = submsg; - if (!fromwire_onionmsg_payload(&subptr, - &submsglen, payload->om)) { - tal_free(payload); - log_broken(ld->log, "bad got_onionmsg_tous om: %s", - tal_hex(tmpctx, msg)); - return; - } - tal_free(submsg); - - if (payload->reply_path && !payload->reply_blinding) { - log_broken(ld->log, - "No reply blinding, ignoring reply path"); - payload->reply_path = tal_free(payload->reply_path); - } - - log_debug(ld->log, "Got obsolete onionmsg%s%s", - payload->reply_blinding ? " reply_blinding": "", - payload->reply_path ? " reply_path": ""); - - if (payload->blinding_in) - plugin_hook_call_onion_message_blinded(ld, payload); - else - plugin_hook_call_onion_message(ld, payload); -} - -void handle_obs_onionmsg_forward(struct lightningd *ld, const u8 *msg) -{ - struct short_channel_id *next_scid; - struct node_id *next_node; - struct pubkey *next_blinding; - u8 *onion; - - if (!fromwire_gossipd_got_obs_onionmsg_forward(msg, msg, &next_scid, - &next_node, - &next_blinding, &onion)) { - log_broken(ld->log, "bad got_onionmsg_forward: %s", - tal_hex(tmpctx, msg)); - return; - } - - if (next_scid) { - struct channel *outchan = any_channel_by_scid(ld, next_scid); - if (outchan) - next_node = &outchan->peer->id; - } - - if (!next_node) { - log_debug(ld->log, "Cannot forward onionmsg to %s", - next_scid ? type_to_string(tmpctx, - struct short_channel_id, - next_scid) - : "unspecified dest"); - } else { - subd_send_msg(ld->gossip, - take(towire_gossipd_send_obs_onionmsg(NULL, - next_node, - onion, - next_blinding))); - } -} - void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) { struct onion_message_hook_payload *payload; @@ -233,15 +115,8 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) size_t submsglen; const u8 *subptr; -#if DEVELOPER - if (ld->dev_ignore_modern_onion) - return; -#endif - payload = tal(ld, struct onion_message_hook_payload); - payload->obsolete = false; payload->om = tlv_onionmsg_payload_new(payload); - payload->blinding_in = NULL; payload->our_alias = tal(payload, struct pubkey); if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg, @@ -290,290 +165,6 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) plugin_hook_call_onion_message_blinded(ld, payload); } -struct hop { - struct pubkey id; - struct short_channel_id *scid; - struct pubkey *blinding; - u8 *enctlv; - u8 *invoice; - u8 *invoice_req; - u8 *invoice_err; - u8 *rawtlv; -}; - -static struct command_result *param_hops(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct hop **hops) -{ - size_t i; - const jsmntok_t *t; - - if (tok->type != JSMN_ARRAY || tok->size == 0) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s must be an (non-empty) array", name); - - *hops = tal_arr(cmd, struct hop, tok->size); - json_for_each_arr(i, t, tok) { - const jsmntok_t *tid, *tscid, *tblinding, *tenctlv, *trawtlv, - *tinvoice, *tinvoicereq, *tinvoiceerr; - - tid = json_get_member(buffer, t, "id"); - if (!tid) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] does not have 'id'", - name, i); - tscid = json_get_member(buffer, t, "short_channel_id"); - tblinding = json_get_member(buffer, t, "blinding"); - tenctlv = json_get_member(buffer, t, "enctlv"); - tinvoice = json_get_member(buffer, t, "invoice"); - tinvoicereq = json_get_member(buffer, t, "invoice_request"); - tinvoiceerr = json_get_member(buffer, t, "invoice_error"); - trawtlv = json_get_member(buffer, t, "rawtlv"); - - if (trawtlv && (tscid || tblinding || tenctlv || tinvoice || tinvoicereq)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] has 'rawtlv' with other fields", - name, i); - - if (tblinding) { - (*hops)[i].blinding = tal(*hops, struct pubkey); - if (!json_to_pubkey(buffer, tblinding, - (*hops)[i].blinding)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'blinding' is invalid", name, i); - } else - (*hops)[i].blinding = NULL; - - if (!json_to_pubkey(buffer, tid, &(*hops)[i].id)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'id' is invalid", name, i); - if (tscid) { - (*hops)[i].scid = tal(*hops, struct short_channel_id); - if (!json_to_short_channel_id(buffer, tscid, - (*hops)[i].scid)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'short_channel_id' is invalid", name, i); - } else - (*hops)[i].scid = NULL; - - if (tenctlv) { - (*hops)[i].enctlv = - json_tok_bin_from_hex(*hops, buffer, tenctlv); - if (!(*hops)[i].enctlv) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'enctlv' is invalid", name, i); - } else - (*hops)[i].enctlv = NULL; - - if (tinvoice) { - (*hops)[i].invoice = - json_tok_bin_from_hex(*hops, buffer, tinvoice); - if (!(*hops)[i].invoice) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'invoice' is invalid", name, i); - } else - (*hops)[i].invoice = NULL; - - if (tinvoicereq) { - (*hops)[i].invoice_req = - json_tok_bin_from_hex(*hops, buffer, tinvoicereq); - if (!(*hops)[i].invoice_req) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'invoice_request' is invalid", name, i); - } else - (*hops)[i].invoice_req = NULL; - - if (tinvoiceerr) { - (*hops)[i].invoice_err = - json_tok_bin_from_hex(*hops, buffer, tinvoiceerr); - if (!(*hops)[i].invoice_err) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'invoice_request' is invalid", name, i); - } else - (*hops)[i].invoice_err = NULL; - - if (trawtlv) { - (*hops)[i].rawtlv = - json_tok_bin_from_hex(*hops, buffer, trawtlv); - if (!(*hops)[i].rawtlv) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'rawtlv' is invalid", name, i); - } else - (*hops)[i].rawtlv = NULL; - } - return NULL; -} - -static struct command_result *param_reply_path(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct tlv_onionmsg_payload_obs_reply_path **reply_path) -{ - const jsmntok_t *tblinding, *tpath, *t; - size_t i; - - *reply_path = tal(cmd, struct tlv_onionmsg_payload_obs_reply_path); - tblinding = json_get_member(buffer, tok, "blinding"); - if (!tblinding) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s has no 'blinding'", name); - if (!json_to_pubkey(buffer, tblinding, &(*reply_path)->blinding)) - return command_fail_badparam(cmd, name, buffer, tblinding, - "'blinding' should be valid pubkey"); - - tpath = json_get_member(buffer, tok, "path"); - if (!tpath || tpath->type != JSMN_ARRAY) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s has no 'path' array", name); - - (*reply_path)->path = tal_arr(*reply_path, struct onionmsg_path *, - tpath->size); - json_for_each_arr(i, t, tpath) { - const jsmntok_t *tid, *tenctlv; - struct onionmsg_path *path; - - path = (*reply_path)->path[i] = tal((*reply_path)->path, - struct onionmsg_path); - tid = json_get_member(buffer, t, "id"); - if (!tid) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'id' is missing", - name, i); - if (!json_to_pubkey(buffer, tid, &path->node_id)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'id' is invalid", - name, i); - - tenctlv = json_get_member(buffer, t, "enctlv"); - if (!tenctlv) { - /* Optional for final destination */ - if (i != tpath->size - 1) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'enctlv' is missing", - name, i); - path->enctlv = NULL; - } else { - path->enctlv = json_tok_bin_from_hex(path, - buffer, tenctlv); - if (!path->enctlv) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'enctlv' is invalid", - name, i); - } - } - - return NULL; -} - -/* Generate ->rawtlv if not already supplied. */ -static void populate_tlvs(struct hop *hops, - struct tlv_onionmsg_payload_obs_reply_path *reply_path) -{ - for (size_t i = 0; i < tal_count(hops); i++) { - struct tlv_onionmsg_payload *tlv; - - if (hops[i].rawtlv) - continue; - - tlv = tlv_onionmsg_payload_new(tmpctx); - /* If they don't give scid, use next node id */ - if (hops[i].scid) { - tlv->obs_next_short_channel_id - = tal_dup(tlv, struct short_channel_id, - hops[i].scid); - } else if (i != tal_count(hops)-1) { - tlv->obs_next_node_id = tal_dup(tlv, struct pubkey, - &hops[i+1].id); - } - if (hops[i].blinding) { - tlv->obs_blinding = tal_dup(tlv, struct pubkey, - hops[i].blinding); - } - /* Note: tal_dup_talarr returns NULL for NULL */ - tlv->enctlv = tal_dup_talarr(tlv, u8, hops[i].enctlv); - tlv->invoice = tal_dup_talarr(tlv, u8, hops[i].invoice); - tlv->invoice_request = tal_dup_talarr(tlv, u8, - hops[i].invoice_req); - tlv->invoice_error = tal_dup_talarr(tlv, u8, - hops[i].invoice_err); - - if (i == tal_count(hops)-1 && reply_path) - tlv->obs_reply_path = reply_path; - - hops[i].rawtlv = tal_arr(hops, u8, 0); - towire_onionmsg_payload(&hops[i].rawtlv, tlv); - } -} - -static struct command_result *json_send_obs_onion_message(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - struct hop *hops; - struct tlv_onionmsg_payload_obs_reply_path *reply_path; - struct sphinx_path *sphinx_path; - struct onionpacket *op; - struct secret *path_secrets; - struct node_id first_id; - size_t onion_size; - - if (!param(cmd, buffer, params, - p_req("hops", param_hops, &hops), - p_opt("reply_path", param_reply_path, &reply_path), - NULL)) - return command_param_failed(); - - if (!feature_offered(cmd->ld->our_features->bits[NODE_ANNOUNCE_FEATURE], - OPT_ONION_MESSAGES)) - return command_fail(cmd, LIGHTNINGD, - "experimental-onion-messages not enabled"); - - node_id_from_pubkey(&first_id, &hops[0].id); - - /* Sanity check first; gossipd doesn't bother telling us if peer - * can't be reached. */ - if (!peer_by_id(cmd->ld, &first_id)) - return command_fail(cmd, LIGHTNINGD, "Unknown first peer"); - - /* Create an onion which encodes this. */ - populate_tlvs(hops, reply_path); - sphinx_path = sphinx_path_new(cmd, NULL); - for (size_t i = 0; i < tal_count(hops); i++) - sphinx_add_modern_hop(sphinx_path, &hops[i].id, hops[i].rawtlv); - - /* BOLT-onion-message #4: - * - SHOULD set `len` to 1366 or 32834. - */ - if (sphinx_path_payloads_size(sphinx_path) <= ROUTING_INFO_SIZE) - onion_size = ROUTING_INFO_SIZE; - else - onion_size = 32768; - - op = create_onionpacket(tmpctx, sphinx_path, onion_size, &path_secrets); - if (!op) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Creating onion failed (tlvs too long?)"); - - subd_send_msg(cmd->ld->gossip, - take(towire_gossipd_send_obs_onionmsg(NULL, &first_id, - serialize_onionpacket(tmpctx, op), - NULL))); - - return command_success(cmd, json_stream_success(cmd)); -} - -static const struct json_command send_obs_onion_message_command = { - "sendobsonionmessage", - "utility", - json_send_obs_onion_message, - "Send message over {hops} (id, [short_channel_id], [blinding], [enctlv], [invoice], [invoice_request], [invoice_error], [rawtlv]) with optional {reply_path} (blinding, path[id, enctlv])" -}; -AUTODATA(json_command, &send_obs_onion_message_command); - struct onion_hop { struct pubkey node; u8 *tlv; diff --git a/lightningd/onion_message.h b/lightningd/onion_message.h index 02f93f8d26af..43f55fdf41a7 100644 --- a/lightningd/onion_message.h +++ b/lightningd/onion_message.h @@ -5,9 +5,6 @@ struct lightningd; -void handle_obs_onionmsg_to_us(struct lightningd *ld, const u8 *msg); -void handle_obs_onionmsg_forward(struct lightningd *ld, const u8 *msg); - void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg); #endif /* LIGHTNING_LIGHTNINGD_ONION_MESSAGE_H */ diff --git a/openingd/dualopend.c b/openingd/dualopend.c index e3eb065282b2..528c3e604043 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1286,7 +1286,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: @@ -1633,7 +1632,6 @@ static bool run_tx_interactive(struct state *state, case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_TX_SIGNATURES: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: @@ -3687,7 +3685,6 @@ static u8 *handle_peer_in(struct state *state) case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 336d058e8a9a..b66785673174 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -28,9 +28,7 @@ static LIST_HEAD(sent_list); struct sent { /* We're in sent_invreqs, awaiting reply. */ struct list_node list; - /* The blinding factor used by reply (obsolete only) */ - struct pubkey *reply_blinding; - /* The alias used by reply (modern only) */ + /* The alias used by reply */ struct pubkey *reply_alias; /* The command which sent us. */ struct command *cmd; @@ -49,17 +47,6 @@ struct sent { u32 wait_timeout; }; -static struct sent *find_sent_by_blinding(const struct pubkey *blinding) -{ - struct sent *i; - - list_for_each(&sent_list, i, list) { - if (i->reply_blinding && pubkey_eq(i->reply_blinding, blinding)) - return i; - } - return NULL; -} - static struct sent *find_sent_by_alias(const struct pubkey *alias) { struct sent *i; @@ -443,48 +430,6 @@ static struct command_result *recv_modern_onion_message(struct command *cmd, return command_hook_success(cmd); } -static struct command_result *recv_obs_onion_message(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - const jsmntok_t *om, *blindingtok; - bool obsolete; - struct sent *sent; - struct pubkey blinding; - struct command_result *err; - - om = json_get_member(buf, params, "onion_message"); - json_to_bool(buf, json_get_member(buf, om, "obsolete"), &obsolete); - if (!obsolete) - return command_hook_success(cmd); - - blindingtok = json_get_member(buf, om, "blinding_in"); - if (!blindingtok || !json_to_pubkey(buf, blindingtok, &blinding)) - return command_hook_success(cmd); - - sent = find_sent_by_blinding(&blinding); - if (!sent) { - plugin_log(cmd->plugin, LOG_DBG, - "No match for obsolete onion %.*s", - json_tok_full_len(om), - json_tok_full(buf, om)); - return command_hook_success(cmd); - } - - plugin_log(cmd->plugin, LOG_DBG, "Received onion message: %.*s", - json_tok_full_len(params), - json_tok_full(buf, params)); - - err = handle_error(cmd, sent, buf, om); - if (err) - return err; - - if (sent->invreq) - return handle_invreq_response(cmd, sent, buf, om); - - return command_hook_success(cmd); -} - static void destroy_sent(struct sent *sent) { list_del(&sent->list); @@ -681,7 +626,7 @@ static struct pubkey *path_to_node(const tal_t *ctx, return nodes; } -/* Marshal arguments for sending obsolete and modern onion messages */ +/* Marshal arguments for sending onion messages */ struct sending { struct sent *sent; const char *msgfield; @@ -692,63 +637,6 @@ struct sending { struct sent *sent); }; -/* Send this message down this path, with blinded reply path */ -static struct command_result *send_obs_message(struct command *cmd, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct sending *sending) -{ - struct pubkey *backwards; - struct onionmsg_path **path; - struct pubkey blinding; - struct out_req *req; - struct sent *sent = sending->sent; - - /* FIXME: Maybe we should allow this? */ - if (tal_count(sent->path) == 1) - return command_fail(cmd, PAY_ROUTE_NOT_FOUND, - "Refusing to talk to ourselves"); - - /* Reverse path is offset by one. */ - backwards = tal_arr(tmpctx, struct pubkey, tal_count(sent->path) - 1); - for (size_t i = 0; i < tal_count(backwards); i++) - backwards[tal_count(backwards)-1-i] = sent->path[i]; - - /* Ok, now make reply for onion_message */ - sent->reply_blinding = tal(sent, struct pubkey); - path = make_blindedpath(tmpctx, backwards, &blinding, - sent->reply_blinding); - - req = jsonrpc_request_start(cmd->plugin, cmd, "sendobsonionmessage", - sending->done, - forward_error, - sent); - json_array_start(req->js, "hops"); - for (size_t i = 1; i < tal_count(sent->path); i++) { - json_object_start(req->js, NULL); - json_add_pubkey(req->js, "id", &sent->path[i]); - if (i == tal_count(sent->path) - 1) - json_add_hex_talarr(req->js, - sending->msgfield, sending->msgval); - json_object_end(req->js); - } - json_array_end(req->js); - - json_object_start(req->js, "reply_path"); - json_add_pubkey(req->js, "blinding", &blinding); - json_array_start(req->js, "path"); - for (size_t i = 0; i < tal_count(path); i++) { - json_object_start(req->js, NULL); - json_add_pubkey(req->js, "id", &path[i]->node_id); - if (path[i]->enctlv) - json_add_hex_talarr(req->js, "enctlv", path[i]->enctlv); - json_object_end(req->js); - } - json_array_end(req->js); - json_object_end(req->js); - return send_outreq(cmd->plugin, req); -} - static struct command_result * send_modern_message(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, @@ -810,10 +698,9 @@ send_modern_message(struct command *cmd, payloads[nhops-1]->reply_path = reply_path; req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", - /* We try obsolete msg next */ - send_obs_message, + sending->done, forward_error, - sending); + sending->sent); json_add_pubkey(req->js, "first_id", &sent->path[1]); json_add_pubkey(req->js, "blinding", &fwd_blinding); json_array_start(req->js, "hops"); @@ -1942,10 +1829,6 @@ static const char *init(struct plugin *p, const char *buf UNUSED, } static const struct plugin_hook hooks[] = { - { - "onion_message_blinded", - recv_obs_onion_message - }, { "onion_message_ourpath", recv_modern_onion_message diff --git a/plugins/offers.c b/plugins/offers.c index 94c1016504ff..3d69bfe8f488 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -41,11 +41,11 @@ static struct command_result *sendonionmessage_error(struct command *cmd, } /* FIXME: replyfield string interface is to accomodate obsolete API */ -static struct command_result * -send_modern_onion_reply(struct command *cmd, - struct tlv_onionmsg_payload_reply_path *reply_path, - const char *replyfield, - const u8 *replydata) +struct command_result * +send_onion_reply(struct command *cmd, + struct tlv_onionmsg_payload_reply_path *reply_path, + const char *replyfield, + const u8 *replydata) { struct out_req *req; size_t nhops = tal_count(reply_path->path); @@ -84,104 +84,17 @@ send_modern_onion_reply(struct command *cmd, return send_outreq(cmd->plugin, req); } -struct command_result *WARN_UNUSED_RESULT -send_onion_reply(struct command *cmd, - struct tlv_onionmsg_payload_reply_path *reply_path, - const char *jsonbuf, - const jsmntok_t *replytok, - const char *replyfield, - const u8 *replydata) -{ - struct out_req *req; - size_t i; - const jsmntok_t *t; - - if (reply_path) - return send_modern_onion_reply(cmd, reply_path, - replyfield, replydata); - - plugin_log(cmd->plugin, LOG_DBG, "sending obs reply %s = %s", - replyfield, tal_hex(tmpctx, replydata)); - - /* Send to requester, using return route. */ - req = jsonrpc_request_start(cmd->plugin, cmd, "sendobsonionmessage", - finished, sendonionmessage_error, NULL); - - /* Add reply into last hop. */ - json_array_start(req->js, "hops"); - json_for_each_arr(i, t, replytok) { - size_t j; - const jsmntok_t *t2; - - plugin_log(cmd->plugin, LOG_DBG, "hops[%zu/%i]", - i, replytok->size); - json_object_start(req->js, NULL); - json_for_each_obj(j, t2, t) - json_add_tok(req->js, - json_strdup(tmpctx, jsonbuf, t2), - t2+1, jsonbuf); - if (i == replytok->size - 1) { - plugin_log(cmd->plugin, LOG_DBG, "... adding %s", - replyfield); - json_add_hex_talarr(req->js, replyfield, replydata); - } - json_object_end(req->js); - } - json_array_end(req->js); - return send_outreq(cmd->plugin, req); -} - -static struct command_result *onion_message_call(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - const jsmntok_t *om, *invreqtok, *invtok; - - if (!offers_enabled) - return command_hook_success(cmd); - - om = json_get_member(buf, params, "onion_message"); - - invreqtok = json_get_member(buf, om, "invoice_request"); - if (invreqtok) { - const jsmntok_t *replytok; - - replytok = json_get_member(buf, om, "reply_path"); - if (replytok && replytok->size > 0) - return handle_invoice_request(cmd, buf, - invreqtok, replytok, NULL); - else - plugin_log(cmd->plugin, LOG_DBG, - "invoice_request without reply_path"); - } - - invtok = json_get_member(buf, om, "invoice"); - if (invtok) { - const jsmntok_t *replytok; - - replytok = json_get_member(buf, om, "reply_path"); - return handle_invoice(cmd, buf, invtok, replytok, NULL); - } - - return command_hook_success(cmd); -} - static struct command_result *onion_message_modern_call(struct command *cmd, const char *buf, const jsmntok_t *params) { const jsmntok_t *om, *replytok, *invreqtok, *invtok; - bool obsolete; struct tlv_onionmsg_payload_reply_path *reply_path; if (!offers_enabled) return command_hook_success(cmd); om = json_get_member(buf, params, "onion_message"); - json_to_bool(buf, json_get_member(buf, om, "obsolete"), &obsolete); - if (obsolete) - return command_hook_success(cmd); - replytok = json_get_member(buf, om, "reply_blindedpath"); if (replytok) { reply_path = json_to_reply_path(cmd, buf, replytok); @@ -194,10 +107,11 @@ static struct command_result *onion_message_modern_call(struct command *cmd, invreqtok = json_get_member(buf, om, "invoice_request"); if (invreqtok) { + const u8 *invreqbin = json_tok_bin_from_hex(tmpctx, buf, invreqtok); if (reply_path) - return handle_invoice_request(cmd, buf, - invreqtok, - NULL, reply_path); + return handle_invoice_request(cmd, + invreqbin, + reply_path); else plugin_log(cmd->plugin, LOG_DBG, "invoice_request without reply_path"); @@ -205,17 +119,15 @@ static struct command_result *onion_message_modern_call(struct command *cmd, invtok = json_get_member(buf, om, "invoice"); if (invtok) { - return handle_invoice(cmd, buf, invtok, NULL, reply_path); + const u8 *invbin = json_tok_bin_from_hex(tmpctx, buf, invtok); + if (invbin) + return handle_invoice(cmd, invbin, reply_path); } return command_hook_success(cmd); } static const struct plugin_hook hooks[] = { - { - "onion_message", - onion_message_call - }, { "onion_message_blinded", onion_message_modern_call diff --git a/plugins/offers.h b/plugins/offers.h index 2c58df73d0d9..dab0c6216f4d 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -9,11 +9,7 @@ struct command; /* Helper to send a reply */ struct command_result *WARN_UNUSED_RESULT send_onion_reply(struct command *cmd, - /* Preferred */ struct tlv_onionmsg_payload_reply_path *reply_path, - /* Used if reply_path is NULL */ - const char *jsonbuf, - const jsmntok_t *replytok, const char *replyfield, const u8 *replydata); #endif /* LIGHTNING_PLUGINS_OFFERS_H */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index a2fa9e56b1d6..22d24544872e 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -10,9 +10,7 @@ struct inv { struct tlv_invoice *inv; - const char *buf; /* May be NULL */ - const jsmntok_t *replytok; struct tlv_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ @@ -41,7 +39,7 @@ fail_inv_level(struct command *cmd, plugin_log(cmd->plugin, l, "%s", msg); /* Only reply if they gave us a path */ - if (!inv->replytok && !inv->reply_path) + if (!inv->reply_path) return command_hook_success(cmd); /* Don't send back internal error details. */ @@ -55,8 +53,7 @@ fail_inv_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, inv->reply_path, inv->buf, inv->replytok, - "invoice_error", errdata); + return send_onion_reply(cmd, inv->reply_path, "invoice_error", errdata); } static struct command_result *WARN_UNUSED_RESULT @@ -315,12 +312,9 @@ static struct command_result *listoffers_error(struct command *cmd, } struct command_result *handle_invoice(struct command *cmd, - const char *buf, - const jsmntok_t *invtok, - const jsmntok_t *replytok, - struct tlv_onionmsg_payload_reply_path *reply_path) + const u8 *invbin, + struct tlv_onionmsg_payload_reply_path *reply_path STEALS) { - const u8 *invbin = json_tok_bin_from_hex(cmd, buf, invtok); size_t len = tal_count(invbin); struct inv *inv = tal(cmd, struct inv); struct out_req *req; @@ -328,17 +322,7 @@ struct command_result *handle_invoice(struct command *cmd, int bad_feature; struct sha256 m, shash; - if (reply_path) { - inv->buf = NULL; - inv->replytok = NULL; - inv->reply_path = reply_path; - } else { - /* Make a copy of entire buffer, for later. */ - inv->buf = tal_dup_arr(inv, char, buf, replytok->end, 0); - inv->replytok = tal_dup_arr(inv, jsmntok_t, replytok, - json_next(replytok) - replytok, 0); - inv->reply_path = NULL; - } + inv->reply_path = tal_steal(inv, reply_path); inv->inv = tlv_invoice_new(cmd); if (!fromwire_invoice(&invbin, &len, inv->inv)) { diff --git a/plugins/offers_inv_hook.h b/plugins/offers_inv_hook.h index 4e862d30abee..fbc12ace6815 100644 --- a/plugins/offers_inv_hook.h +++ b/plugins/offers_inv_hook.h @@ -3,10 +3,8 @@ #include "config.h" #include -/* We got an onionmessage with an invoice! replytok/reply_path could be NULL. */ +/* We got an onionmessage with an invoice! reply_path could be NULL. */ struct command_result *handle_invoice(struct command *cmd, - const char *buf, - const jsmntok_t *invtok, - const jsmntok_t *replytok, - struct tlv_onionmsg_payload_reply_path *reply_path); + const u8 *invbin, + struct tlv_onionmsg_payload_reply_path *reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */ diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 0391b84e9b90..313ef3a65f6e 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -15,10 +15,6 @@ /* We need to keep the reply path around so we can reply with invoice */ struct invreq { struct tlv_invoice_request *invreq; - const char *buf; - /* If obsolete style */ - const jsmntok_t *replytok; - /* If modern style. */ struct tlv_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ @@ -63,9 +59,7 @@ fail_invreq_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, invreq->reply_path, - invreq->buf, invreq->replytok, - "invoice_error", errdata); + return send_onion_reply(cmd, invreq->reply_path, "invoice_error", errdata); } static struct command_result *WARN_UNUSED_RESULT PRINTF_FMT(3,4) @@ -184,8 +178,7 @@ static struct command_result *createinvoice_done(struct command *cmd, json_tok_full(buf, t)); } - return send_onion_reply(cmd, ir->reply_path, ir->buf, ir->replytok, - "invoice", rawinv); + return send_onion_reply(cmd, ir->reply_path, "invoice", rawinv); } static struct command_result *createinvoice_error(struct command *cmd, @@ -836,28 +829,15 @@ static struct command_result *handle_offerless_request(struct command *cmd, } struct command_result *handle_invoice_request(struct command *cmd, - const char *buf, - const jsmntok_t *invreqtok, - const jsmntok_t *replytok, + const u8 *invreqbin, struct tlv_onionmsg_payload_reply_path *reply_path) { - const u8 *invreqbin = json_tok_bin_from_hex(cmd, buf, invreqtok); size_t len = tal_count(invreqbin); struct invreq *ir = tal(cmd, struct invreq); struct out_req *req; int bad_feature; - /* Make a copy of entire buffer, for later. */ - if (reply_path) { - ir->buf = NULL; - ir->replytok = NULL; - ir->reply_path = reply_path; - } else { - ir->buf = tal_dup_arr(ir, char, buf, replytok->end, 0); - ir->replytok = tal_dup_arr(ir, jsmntok_t, replytok, - json_next(replytok) - replytok, 0); - ir->reply_path = NULL; - } + ir->reply_path = tal_steal(ir, reply_path); ir->invreq = tlv_invoice_request_new(cmd); if (!fromwire_invoice_request(&invreqbin, &len, ir->invreq)) { diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index 0ea8f023be1a..a51d0e800421 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -7,10 +7,6 @@ extern u32 cltv_final; /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, - const char *buf, - const jsmntok_t *invreqtok, - /* Obsolete onion */ - const jsmntok_t *replytok, - /* Modern onion */ - struct tlv_onionmsg_payload_reply_path *reply_path); + const u8 *invreqbin, + struct tlv_onionmsg_payload_reply_path *reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */ diff --git a/tests/test_misc.py b/tests/test_misc.py index f26ddc2b3af8..b7e2120cd62a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2252,77 +2252,6 @@ def test_sendcustommsg(node_factory): ]) -def test_sendobsonionmessage(node_factory): - l1, l2, l3 = node_factory.line_graph(3, opts={'experimental-onion-messages': None}) - - blindedpathtool = os.path.join(os.path.dirname(__file__), "..", "devtools", "blindedpath") - - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id']}, - {'id': l3.info['id']}]}) - assert l3.daemon.wait_for_log('Got obsolete onionmsg') - - # Now by SCID. - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id'], - 'short_channel_id': l2.get_channel_scid(l3)}, - {'id': l3.info['id']}]}) - assert l3.daemon.wait_for_log('Got obsolete onionmsg') - - # Now test blinded path. - output = subprocess.check_output( - [blindedpathtool, '--simple-output', 'create', l2.info['id'], l3.info['id']] - ).decode('ASCII').strip() - - # First line is blinding, then then . - blinding, p1, p1enc, p2 = output.split('\n') - # First hop can't be blinded! - assert p1 == l2.info['id'] - - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id'], - 'blinding': blinding, - 'enctlv': p1enc}, - {'id': p2}]}) - assert l3.daemon.wait_for_log('Got obsolete onionmsg') - - -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "Needs sendobsonionmessage") -def test_sendobsonionmessage_reply(node_factory): - blindedpathtool = os.path.join(os.path.dirname(__file__), "..", "devtools", "blindedpath") - - plugin = os.path.join(os.path.dirname(__file__), "plugins", "onionmessage-reply.py") - l1, l2, l3 = node_factory.line_graph(3, opts={'plugin': plugin}) - - # Make reply path - output = subprocess.check_output( - [blindedpathtool, '--simple-output', 'create', l2.info['id'], l1.info['id']] - ).decode('ASCII').strip() - - # First line is blinding, then then . - blinding, p1, p1enc, p2 = output.split('\n') - # First hop can't be blinded! - assert p1 == l2.info['id'] - - # Also tests oversize payload which won't fit in 1366-byte onion. - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id']}, - {'id': l3.info['id'], - 'invoice': '77' * 15000}], - 'reply_path': - {'blinding': blinding, - 'path': [{'id': p1, 'enctlv': p1enc}, {'id': p2}]}}) - - assert l3.daemon.wait_for_log('Got obsolete onionmsg reply_blinding reply_path') - assert l3.daemon.wait_for_log("Got onion_message invoice '{}'".format('77' * 15000)) - assert l3.daemon.wait_for_log('Sent reply via') - assert l1.daemon.wait_for_log('Got obsolete onionmsg') - - @pytest.mark.developer("needs --dev-force-privkey") def test_getsharedsecret(node_factory): """ diff --git a/tests/test_pay.py b/tests/test_pay.py index 709978ae82ca..541a6eafe2f8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3259,6 +3259,7 @@ def test_reject_invalid_payload(node_factory): l1.rpc.waitsendpay(inv['payment_hash']) +@pytest.mark.skip("Needs to be updated for modern onion") @unittest.skipIf(not EXPERIMENTAL_FEATURES, "Needs blinding args to sendpay") def test_sendpay_blinding(node_factory): l1, l2, l3, l4 = node_factory.line_graph(4) @@ -4351,23 +4352,6 @@ def test_fetchinvoice_3hop(node_factory, bitcoind): l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - # Test with obsolete onion. - l4.stop() - l4.daemon.opts['dev-no-modern-onion'] = None - l4.start() - l4.rpc.connect(l3.info['id'], 'localhost', l3.port) - - l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - - # Test with modern onion. - l4.stop() - del l4.daemon.opts['dev-no-modern-onion'] - l4.daemon.opts['dev-no-obsolete-onion'] = None - l4.start() - l4.rpc.connect(l3.info['id'], 'localhost', l3.port) - - l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - def test_fetchinvoice(node_factory, bitcoind): # We remove the conversion plugin on l3, causing it to get upset. diff --git a/wire/extracted_onion_01_offers.patch b/wire/extracted_onion_01_offers.patch index 24e876856b93..44a313ce0ce4 100644 --- a/wire/extracted_onion_01_offers.patch +++ b/wire/extracted_onion_01_offers.patch @@ -1,30 +1,29 @@ --- wire/extracted_onion_wire_csv 2020-03-25 10:24:12.861645774 +1030 +++ - 2020-03-26 13:47:13.498294435 +1030 -@@ -8,6 +8,31 @@ +@@ -8,6 +8,30 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,onionmsg_payload,obs_next_node_id,4 -+tlvdata,onionmsg_payload,obs_next_node_id,node_id,point, -+tlvtype,onionmsg_payload,obs_next_short_channel_id,6 -+tlvdata,onionmsg_payload,obs_next_short_channel_id,short_channel_id,short_channel_id, -+tlvtype,onionmsg_payload,obs_reply_path,8 -+tlvdata,onionmsg_payload,obs_reply_path,blinding,point, -+tlvdata,onionmsg_payload,obs_reply_path,path,onionmsg_path,... ++tlvtype,onionmsg_payload,reply_path,2 ++tlvdata,onionmsg_payload,reply_path,first_node_id,point, ++tlvdata,onionmsg_payload,reply_path,blinding,point, ++tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... +tlvtype,onionmsg_payload,enctlv,10 +tlvdata,onionmsg_payload,enctlv,enctlv,byte,... -+tlvtype,onionmsg_payload,obs_blinding,12 -+tlvdata,onionmsg_payload,obs_blinding,blinding,point, +tlvtype,onionmsg_payload,invoice_request,64 +tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... +tlvtype,onionmsg_payload,invoice,66 +tlvdata,onionmsg_payload,invoice,invoice,byte,... +tlvtype,onionmsg_payload,invoice_error,68 +tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... ++tlvtype,encmsg_tlvs,padding,1 ++tlvdata,encmsg_tlvs,padding,pad,byte,... +tlvtype,encmsg_tlvs,next_node_id,4 +tlvdata,encmsg_tlvs,next_node_id,node_id,point, -+tlvtype,encmsg_tlvs,obs_next_short_channel_id,6 -+tlvdata,encmsg_tlvs,obs_next_short_channel_id,short_channel_id,short_channel_id, ++tlvtype,encmsg_tlvs,next_blinding,12 ++tlvdata,encmsg_tlvs,next_blinding,blinding,point, ++tlvtype,encmsg_tlvs,self_id,14 ++tlvdata,encmsg_tlvs,self_id,data,byte,... +subtype,onionmsg_path +subtypedata,onionmsg_path,node_id,point, +subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_onion_02_newonion.patch b/wire/extracted_onion_02_newonion.patch deleted file mode 100644 index 6a36d3516a99..000000000000 --- a/wire/extracted_onion_02_newonion.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- onion_wire.csv 2021-08-25 12:41:02.872253965 +0930 -+++ onion_wire.csv.raw 2021-08-25 13:52:00.748767887 +0930 -@@ -8,6 +8,10 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, - tlvtype,tlv_payload,payment_data,8 - tlvdata,tlv_payload,payment_data,payment_secret,byte,32 - tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,onionmsg_payload,reply_path,2 -+tlvdata,onionmsg_payload,reply_path,first_node_id,point, -+tlvdata,onionmsg_payload,reply_path,blinding,point, -+tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... - tlvtype,onionmsg_payload,obs_next_node_id,4 - tlvdata,onionmsg_payload,obs_next_node_id,node_id,point, - tlvtype,onionmsg_payload,obs_next_short_channel_id,6 -@@ -29,10 +29,16 @@ tlvtype,onionmsg_payload,invoice,66 - tlvdata,onionmsg_payload,invoice,invoice,byte,... - tlvtype,onionmsg_payload,invoice_error,68 - tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... -+tlvtype,encmsg_tlvs,padding,1 -+tlvdata,encmsg_tlvs,padding,pad,byte,... - tlvtype,encmsg_tlvs,next_node_id,4 - tlvdata,encmsg_tlvs,next_node_id,node_id,point, - tlvtype,encmsg_tlvs,obs_next_short_channel_id,6 - tlvdata,encmsg_tlvs,obs_next_short_channel_id,short_channel_id,short_channel_id, -+tlvtype,encmsg_tlvs,next_blinding,12 -+tlvdata,encmsg_tlvs,next_blinding,blinding,point, -+tlvtype,encmsg_tlvs,self_id,14 -+tlvdata,encmsg_tlvs,self_id,data,byte,... - subtype,onionmsg_path - subtypedata,onionmsg_path,node_id,point, - subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_peer_01_offers.patch b/wire/extracted_peer_01_offers.patch deleted file mode 100644 index ca910f6ad25a..000000000000 --- a/wire/extracted_peer_01_offers.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- wire/extracted_peer_wire_csv 2020-03-11 10:30:35.744376417 +1030 -+++ - 2020-03-26 13:47:13.409755567 +1030 -@@ -211,3 +211,9 @@ - msgdata,gossip_timestamp_filter,chain_hash,chain_hash, - msgdata,gossip_timestamp_filter,first_timestamp,u32, - msgdata,gossip_timestamp_filter,timestamp_range,u32, -+msgtype,obs_onion_message,385,option_onion_messages -+msgdata,obs_onion_message,len,u16, -+msgdata,obs_onion_message,onionmsg,byte,len -+msgdata,obs_onion_message,obs_onion_message_tlvs,obs_onion_message_tlvs, -+tlvtype,obs_onion_message_tlvs,blinding,2 -+tlvdata,obs_onion_message_tlvs,blinding,blinding,point, diff --git a/wire/extracted_peer_05_newonion.patch b/wire/extracted_peer_05_newonion.patch index 97c1629e5d62..7e581997db72 100644 --- a/wire/extracted_peer_05_newonion.patch +++ b/wire/extracted_peer_05_newonion.patch @@ -1,9 +1,9 @@ --- peer_wire.csv 2021-08-25 12:41:02.876254003 +0930 +++ peer_wire.csv.raw 2021-08-25 13:42:31.991693809 +0930 @@ -320,3 +210,7 @@ - msgdata,obs_onion_message,obs_onion_message_tlvs,obs_onion_message_tlvs, - tlvtype,obs_onion_message_tlvs,blinding,2 - tlvdata,obs_onion_message_tlvs,blinding,blinding,point, + msgdata,gossip_timestamp_filter,chain_hash,chain_hash, + msgdata,gossip_timestamp_filter,first_timestamp,u32, + msgdata,gossip_timestamp_filter,timestamp_range,u32, +msgtype,onion_message,387,option_onion_messages +msgdata,onion_message,blinding,point, +msgdata,onion_message,len,u16, diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index e60849a87cd3..e3c7bc27f8cb 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -12,17 +12,8 @@ tlvtype,onionmsg_payload,reply_path,2 tlvdata,onionmsg_payload,reply_path,first_node_id,point, tlvdata,onionmsg_payload,reply_path,blinding,point, tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... -tlvtype,onionmsg_payload,obs_next_node_id,4 -tlvdata,onionmsg_payload,obs_next_node_id,node_id,point, -tlvtype,onionmsg_payload,obs_next_short_channel_id,6 -tlvdata,onionmsg_payload,obs_next_short_channel_id,short_channel_id,short_channel_id, -tlvtype,onionmsg_payload,obs_reply_path,8 -tlvdata,onionmsg_payload,obs_reply_path,blinding,point, -tlvdata,onionmsg_payload,obs_reply_path,path,onionmsg_path,... tlvtype,onionmsg_payload,enctlv,10 tlvdata,onionmsg_payload,enctlv,enctlv,byte,... -tlvtype,onionmsg_payload,obs_blinding,12 -tlvdata,onionmsg_payload,obs_blinding,blinding,point, tlvtype,onionmsg_payload,invoice_request,64 tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... tlvtype,onionmsg_payload,invoice,66 @@ -33,8 +24,6 @@ tlvtype,encmsg_tlvs,padding,1 tlvdata,encmsg_tlvs,padding,pad,byte,... tlvtype,encmsg_tlvs,next_node_id,4 tlvdata,encmsg_tlvs,next_node_id,node_id,point, -tlvtype,encmsg_tlvs,obs_next_short_channel_id,6 -tlvdata,encmsg_tlvs,obs_next_short_channel_id,short_channel_id,short_channel_id, tlvtype,encmsg_tlvs,next_blinding,12 tlvdata,encmsg_tlvs,next_blinding,blinding,point, tlvtype,encmsg_tlvs,self_id,14 diff --git a/wire/peer_wire.c b/wire/peer_wire.c index d0515fda5f15..7fb1d2ae8fc6 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -34,7 +34,6 @@ static bool unknown_type(enum peer_wire t) case WIRE_REPLY_CHANNEL_RANGE: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: case WIRE_TX_ADD_OUTPUT: @@ -64,7 +63,6 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_QUERY_CHANNEL_RANGE: case WIRE_REPLY_CHANNEL_RANGE: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: return true; case WIRE_WARNING: case WIRE_INIT: diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 85cf20b4d267..23b9d8bc58ec 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -328,12 +328,6 @@ msgtype,gossip_timestamp_filter,265,gossip_queries msgdata,gossip_timestamp_filter,chain_hash,chain_hash, msgdata,gossip_timestamp_filter,first_timestamp,u32, msgdata,gossip_timestamp_filter,timestamp_range,u32, -msgtype,obs_onion_message,385,option_onion_messages -msgdata,obs_onion_message,len,u16, -msgdata,obs_onion_message,onionmsg,byte,len -msgdata,obs_onion_message,obs_onion_message_tlvs,obs_onion_message_tlvs, -tlvtype,obs_onion_message_tlvs,blinding,2 -tlvdata,obs_onion_message_tlvs,blinding,blinding,point, msgtype,onion_message,387,option_onion_messages msgdata,onion_message,blinding,point, msgdata,onion_message,len,u16, From 1ec6346f3dc4c77323ab390f58b0845f07b68cce Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:04 +1030 Subject: [PATCH 0080/1530] common: rename current onion message structures to obs2_. Yes, we changed the spec again. Hopefully for the last time! Signed-off-by: Rusty Russell --- channeld/channeld.c | 2 +- common/blindedpath.c | 78 +++++++++++++-------------- common/blindedpath.h | 8 +-- common/json_helpers.c | 8 +-- common/json_helpers.h | 4 +- common/test/run-blindedpath_enctlv.c | 22 ++++---- devtools/blindedpath.c | 18 +++---- gossipd/gossipd.c | 36 ++++++------- gossipd/gossipd_wire.csv | 2 +- lightningd/onion_message.c | 40 +++++++------- openingd/dualopend.c | 6 +-- plugins/fetchinvoice.c | 44 +++++++-------- plugins/offers.c | 12 ++--- plugins/offers.h | 2 +- plugins/offers_inv_hook.c | 4 +- plugins/offers_inv_hook.h | 2 +- plugins/offers_invreq_hook.c | 4 +- plugins/offers_invreq_hook.h | 2 +- wire/extracted_onion_01_offers.patch | 40 +++++++------- wire/extracted_onion_exp_enctlv.patch | 6 +-- wire/extracted_peer_05_newonion.patch | 8 +-- wire/onion_wire.csv | 40 +++++++------- wire/peer_wire.c | 4 +- wire/peer_wire.csv | 8 +-- 24 files changed, 200 insertions(+), 200 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 73dad4261c65..5e5eeb4a8c78 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2320,7 +2320,7 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_PING: case WIRE_WARNING: case WIRE_ERROR: - case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: abort(); } diff --git a/common/blindedpath.c b/common/blindedpath.c index ab16d8569511..9e85698116a9 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -61,7 +61,7 @@ static bool blind_node(const struct privkey *blinding, static u8 *enctlv_from_encmsg(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *node, - const struct tlv_encmsg_tlvs *encmsg, + const struct tlv_obs2_encmsg_tlvs *encmsg, struct privkey *next_blinding, struct pubkey *node_alias) { @@ -89,7 +89,7 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx, /* Marshall */ ret = tal_arr(ctx, u8, 0); - towire_encmsg_tlvs(&ret, encmsg); + towire_obs2_encmsg_tlvs(&ret, encmsg); SUPERVERBOSE("\t\"encmsg_hex\": \"%s\",\n", tal_hex(tmpctx, ret)); /* @@ -136,16 +136,16 @@ bool unblind_onion(const struct pubkey *blinding, hmac.data) == 1; } -static struct tlv_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv) +static struct tlv_obs2_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) { struct secret rho; u8 *dec; const u8 *cursor; size_t maxlen; - struct tlv_encmsg_tlvs *encmsg; + struct tlv_obs2_encmsg_tlvs *encmsg; /* All-zero npub */ static const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; @@ -179,21 +179,21 @@ static struct tlv_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, * - if the `enctlv` is not a valid TLV... * - MUST drop the message. */ - encmsg = tlv_encmsg_tlvs_new(ctx); - if (!fromwire_encmsg_tlvs(&cursor, &maxlen, encmsg) + encmsg = tlv_obs2_encmsg_tlvs_new(ctx); + if (!fromwire_obs2_encmsg_tlvs(&cursor, &maxlen, encmsg) || !tlv_fields_valid(encmsg->fields, NULL, NULL)) return tal_free(encmsg); return encmsg; } -bool decrypt_enctlv(const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - struct pubkey *next_node, - struct pubkey *next_blinding) +bool decrypt_obs2_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) { - struct tlv_encmsg_tlvs *encmsg; + struct tlv_obs2_encmsg_tlvs *encmsg; encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); if (!encmsg) @@ -244,15 +244,15 @@ bool decrypt_enctlv(const struct pubkey *blinding, return true; } -bool decrypt_final_enctlv(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - const struct pubkey *my_id, - struct pubkey *alias, - struct secret **self_id) +bool decrypt_obs2_final_enctlv(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **self_id) { - struct tlv_encmsg_tlvs *encmsg; + struct tlv_obs2_encmsg_tlvs *encmsg; struct secret node_id_blinding; /* Repeat the tweak to get the alias it was using for us */ @@ -276,16 +276,16 @@ bool decrypt_final_enctlv(const tal_t *ctx, return true; } -u8 *create_enctlv(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *node, - const struct pubkey *next_node, - size_t padlen, - const struct pubkey *override_blinding, - struct privkey *next_blinding, - struct pubkey *node_alias) +u8 *create_obs2_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct pubkey *next_node, + size_t padlen, + const struct pubkey *override_blinding, + struct privkey *next_blinding, + struct pubkey *node_alias) { - struct tlv_encmsg_tlvs *encmsg = tlv_encmsg_tlvs_new(tmpctx); + struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); if (padlen) encmsg->padding = tal_arrz(encmsg, u8, padlen); encmsg->next_node_id = cast_const(struct pubkey *, next_node); @@ -295,14 +295,14 @@ u8 *create_enctlv(const tal_t *ctx, next_blinding, node_alias); } -u8 *create_final_enctlv(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *final_node, - size_t padlen, - const struct secret *self_id, - struct pubkey *node_alias) +u8 *create_obs2_final_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *final_node, + size_t padlen, + const struct secret *self_id, + struct pubkey *node_alias) { - struct tlv_encmsg_tlvs *encmsg = tlv_encmsg_tlvs_new(tmpctx); + struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); struct privkey unused_next_blinding; if (padlen) diff --git a/common/blindedpath.h b/common/blindedpath.h index 1a54f5c1da2e..a207c316d43f 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -23,7 +23,7 @@ struct secret; * * Returns the enctlv blob, or NULL if the secret is invalid. */ -u8 *create_enctlv(const tal_t *ctx, +u8 *create_obs2_enctlv(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *node, const struct pubkey *next_node, @@ -44,7 +44,7 @@ u8 *create_enctlv(const tal_t *ctx, * * If it fails, it means one of the privkeys is bad. */ -u8 *create_final_enctlv(const tal_t *ctx, +u8 *create_obs2_final_enctlv(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *final_node, size_t padlen, @@ -77,7 +77,7 @@ bool unblind_onion(const struct pubkey *blinding, * * Returns false if decryption failed or encmsg was malformed. */ -bool decrypt_enctlv(const struct pubkey *blinding, +bool decrypt_obs2_enctlv(const struct pubkey *blinding, const struct secret *ss, const u8 *enctlv, struct pubkey *next_node, @@ -96,7 +96,7 @@ bool decrypt_enctlv(const struct pubkey *blinding, * * Returns false if decryption failed or encmsg was malformed. */ -bool decrypt_final_enctlv(const tal_t *ctx, +bool decrypt_obs2_final_enctlv(const tal_t *ctx, const struct pubkey *blinding, const struct secret *ss, const u8 *enctlv, diff --git a/common/json_helpers.c b/common/json_helpers.c index bcde33a6162f..0fa2233c90ca 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -141,15 +141,15 @@ struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, return psbt_from_b64(ctx, buffer + tok->start, tok->end - tok->start); } -struct tlv_onionmsg_payload_reply_path * -json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) { - struct tlv_onionmsg_payload_reply_path *rpath; + struct tlv_obs2_onionmsg_payload_reply_path *rpath; const jsmntok_t *hops, *t; size_t i; const char *err; - rpath = tal(ctx, struct tlv_onionmsg_payload_reply_path); + rpath = tal(ctx, struct tlv_obs2_onionmsg_payload_reply_path); err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", JSON_SCAN(json_to_pubkey, &rpath->blinding), JSON_SCAN(json_to_pubkey, &rpath->first_node_id), diff --git a/common/json_helpers.h b/common/json_helpers.h index e3001a0e7b16..c6febd343cc1 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -80,8 +80,8 @@ bool split_tok(const char *buffer, const jsmntok_t *tok, jsmntok_t *b); /* Extract reply path from this JSON */ -struct tlv_onionmsg_payload_reply_path * -json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); /* Helpers for outputting JSON results */ diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index ec735ec10eb8..867b8c82f284 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -84,7 +84,7 @@ static void test_decrypt(const struct pubkey *blinding, mykey = me; assert(unblind_onion(blinding, test_ecdh, &dummy, &ss)); - assert(decrypt_enctlv(blinding, &ss, enctlv, &next_node, &next_blinding)); + assert(decrypt_obs2_enctlv(blinding, &ss, enctlv, &next_node, &next_blinding)); pubkey_from_privkey(expected_next_blinding_priv, &expected_next_blinding); assert(pubkey_eq(&next_blinding, &expected_next_blinding)); @@ -106,8 +106,8 @@ static void test_final_decrypt(const struct pubkey *blinding, mykey = me; pubkey_from_privkey(me, &my_pubkey); assert(unblind_onion(blinding, test_ecdh, &dummy, &ss)); - assert(decrypt_final_enctlv(tmpctx, blinding, &ss, enctlv, &my_pubkey, - &alias, &self_id)); + assert(decrypt_obs2_final_enctlv(tmpctx, blinding, &ss, enctlv, &my_pubkey, + &alias, &self_id)); assert(pubkey_eq(&alias, expected_alias)); assert(secret_eq_consttime(self_id, expected_self_id)); @@ -150,8 +150,8 @@ int main(int argc, char *argv[]) "\t},\n", type_to_string(tmpctx, struct pubkey, &bob_id)); - enctlv = create_enctlv(tmpctx, &blinding, &alice_id, &bob_id, - 0, NULL, &blinding, &alias); + enctlv = create_obs2_enctlv(tmpctx, &blinding, &alice_id, &bob_id, + 0, NULL, &blinding, &alias); printf("\t\"enctlv_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -180,8 +180,8 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct pubkey, &carol_id), type_to_string(tmpctx, struct privkey, &override_blinding)); - enctlv = create_enctlv(tmpctx, &blinding, &bob_id, &carol_id, - 0, &override_blinding_pub, &blinding, &alias); + enctlv = create_obs2_enctlv(tmpctx, &blinding, &bob_id, &carol_id, + 0, &override_blinding_pub, &blinding, &alias); printf("\t\"enctlv_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -209,8 +209,8 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct pubkey, &dave_id), tal_hex(tmpctx, tal_arrz(tmpctx, u8, 35))); - enctlv = create_enctlv(tmpctx, &blinding, &carol_id, &dave_id, - 35, NULL, &blinding, &alias); + enctlv = create_obs2_enctlv(tmpctx, &blinding, &carol_id, &dave_id, + 35, NULL, &blinding, &alias); printf("\t\"enctlv_hex\": \"%s\"\n" "}]\n", tal_hex(tmpctx, enctlv)); @@ -220,8 +220,8 @@ int main(int argc, char *argv[]) /* FIXME: Add this to the test vectors! */ fclose(stdout); memset(&self_id, 0x77, sizeof(self_id)); - enctlv = create_final_enctlv(tmpctx, &blinding, &dave_id, - 0, &self_id, &alias); + enctlv = create_obs2_final_enctlv(tmpctx, &blinding, &dave_id, + 0, &self_id, &alias); pubkey_from_privkey(&blinding, &blinding_pub); test_final_decrypt(&blinding_pub, enctlv, &dave, &alias, &self_id); diff --git a/devtools/blindedpath.c b/devtools/blindedpath.c index 7647e8c5894f..57d320894832 100644 --- a/devtools/blindedpath.c +++ b/devtools/blindedpath.c @@ -138,17 +138,17 @@ int main(int argc, char **argv) u8 *p; u8 buf[BIGSIZE_MAX_LEN]; const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - struct tlv_onionmsg_payload *outer; - struct tlv_encmsg_tlvs *inner; + struct tlv_obs2_onionmsg_payload *outer; + struct tlv_obs2_encmsg_tlvs *inner; int ret; /* Inner is encrypted */ - inner = tlv_encmsg_tlvs_new(tmpctx); + inner = tlv_obs2_encmsg_tlvs_new(tmpctx); inner->next_node_id = tal_dup(inner, struct pubkey, &nodes[i+1]); p = tal_arr(tmpctx, u8, 0); - towire_encmsg_tlvs(&p, inner); + towire_obs2_encmsg_tlvs(&p, inner); - outer = tlv_onionmsg_payload_new(tmpctx); + outer = tlv_obs2_onionmsg_payload_new(tmpctx); outer->enctlv = tal_arr(outer, u8, tal_count(p) + crypto_aead_chacha20poly1305_ietf_ABYTES); ret = crypto_aead_chacha20poly1305_ietf_encrypt(outer->enctlv, NULL, @@ -160,7 +160,7 @@ int main(int argc, char **argv) assert(ret == 0); p = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&p, outer); + towire_obs2_onionmsg_payload(&p, outer); ret = bigsize_put(buf, tal_bytelen(p)); if (simpleout) { @@ -194,7 +194,7 @@ int main(int argc, char **argv) struct secret hmac, rho; struct route_step *rs; const u8 *cursor; - struct tlv_onionmsg_payload *outer; + struct tlv_obs2_onionmsg_payload *outer; size_t max, len; struct pubkey res; struct sha256 h; @@ -257,8 +257,8 @@ int main(int argc, char **argv) /* Always true since we're non-legacy */ assert(len == max); - outer = tlv_onionmsg_payload_new(tmpctx); - if (!fromwire_onionmsg_payload(&cursor, &max, outer)) + outer = tlv_obs2_onionmsg_payload_new(tmpctx); + if (!fromwire_obs2_onionmsg_payload(&cursor, &max, outer)) errx(1, "Invalid payload %s", tal_hex(tmpctx, rs->raw_payload)); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index cdc75156ded2..b9e07a35375a 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -350,15 +350,15 @@ static bool handle_local_channel_announcement(struct daemon *daemon, return true; } -/* Peer sends onion msg. */ -static u8 *handle_onion_message(struct peer *peer, const u8 *msg) +/* Peer sends obsolete onion msg. */ +static u8 *handle_obs2_onion_message(struct peer *peer, const u8 *msg) { enum onion_wire badreason; struct onionpacket *op; struct pubkey blinding, ephemeral; struct route_step *rs; u8 *onion; - struct tlv_onionmsg_payload *om; + struct tlv_obs2_onionmsg_payload *om; struct secret ss, onion_ss; const u8 *cursor; size_t max, maxlen; @@ -369,7 +369,7 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) return NULL; /* FIXME: ratelimit! */ - if (!fromwire_onion_message(msg, msg, &blinding, &onion)) + if (!fromwire_obs2_onion_message(msg, msg, &blinding, &onion)) return towire_warningfmt(peer, NULL, "Bad onion_message"); /* We unwrap the onion now. */ @@ -411,8 +411,8 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) return NULL; } - om = tlv_onionmsg_payload_new(msg); - if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { + om = tlv_obs2_onionmsg_payload_new(msg); + if (!fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)) { status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", tal_hex(tmpctx, rs->raw_payload)); return NULL; @@ -433,9 +433,9 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) if (!om->enctlv) { alias = me; self_id = NULL; - } else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss, - om->enctlv, &me, &alias, - &self_id)) { + } else if (!decrypt_obs2_final_enctlv(tmpctx, &blinding, &ss, + om->enctlv, &me, &alias, + &self_id)) { status_peer_debug(&peer->id, "onion msg: failed to decrypt enctlv" " %s", tal_hex(tmpctx, om->enctlv)); @@ -469,8 +469,8 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) struct node_id next_node_id; /* This fails as expected if no enctlv. */ - if (!decrypt_enctlv(&blinding, &ss, om->enctlv, &next_node, - &next_blinding)) { + if (!decrypt_obs2_enctlv(&blinding, &ss, om->enctlv, &next_node, + &next_blinding)) { status_peer_debug(&peer->id, "onion msg: invalid enctlv %s", tal_hex(tmpctx, om->enctlv)); @@ -490,9 +490,9 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) return NULL; } queue_peer_msg(next_peer, - take(towire_onion_message(NULL, - &next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); + take(towire_obs2_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); } return NULL; @@ -514,8 +514,8 @@ static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, peer = find_peer(daemon, &id); if (peer) { queue_peer_msg(peer, - take(towire_onion_message(NULL, - &blinding, onionmsg))); + take(towire_obs2_onion_message(NULL, + &blinding, onionmsg))); } return daemon_conn_read_next(conn, daemon->master); } @@ -553,8 +553,8 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, case WIRE_REPLY_SHORT_CHANNEL_IDS_END: err = handle_reply_short_channel_ids_end(peer, msg); goto handled_relay; - case WIRE_ONION_MESSAGE: - err = handle_onion_message(peer, msg); + case WIRE_OBS2_ONION_MESSAGE: + err = handle_obs2_onion_message(peer, msg); goto handled_relay; /* These are non-gossip messages (!is_msg_for_gossipd()) */ diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 2deab8752c2b..4a1e49a35b0a 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -84,7 +84,7 @@ msgdata,gossipd_got_onionmsg_to_us,reply_path,onionmsg_path,reply_path_len msgdata,gossipd_got_onionmsg_to_us,rawmsg_len,u16, msgdata,gossipd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len -# Lightningd tells us to send a onion message. +# Lightningd tells us to send an onion message. msgtype,gossipd_send_onionmsg,3041 msgdata,gossipd_send_onionmsg,id,node_id, msgdata,gossipd_send_onionmsg,onion_len,u16, diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index e1bdd27a1306..1a0de05e963a 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -21,7 +21,7 @@ struct onion_message_hook_payload { struct pubkey *reply_first_node; struct pubkey *our_alias; - struct tlv_onionmsg_payload *om; + struct tlv_obs2_onionmsg_payload *om; }; static void json_add_blindedpath(struct json_stream *stream, @@ -116,7 +116,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) const u8 *subptr; payload = tal(ld, struct onion_message_hook_payload); - payload->om = tlv_onionmsg_payload_new(payload); + payload->om = tlv_obs2_onionmsg_payload_new(payload); payload->our_alias = tal(payload, struct pubkey); if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg, @@ -138,8 +138,8 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) submsglen = tal_bytelen(submsg); subptr = submsg; - if (!fromwire_onionmsg_payload(&subptr, - &submsglen, payload->om)) { + if (!fromwire_obs2_onionmsg_payload(&subptr, + &submsglen, payload->om)) { tal_free(payload); log_broken(ld->log, "bad got_onionmsg_tous om: %s", tal_hex(tmpctx, msg)); @@ -327,26 +327,26 @@ static struct command_result *json_blindedpath(struct command *cmd, for (size_t i = 0; i < nhops - 1; i++) { path[i] = tal(path, struct onionmsg_path); - path[i]->enctlv = create_enctlv(path[i], - &blinding_iter, - &ids[i], - &ids[i+1], - /* FIXME: Pad? */ - 0, - NULL, - &blinding_iter, - &path[i]->node_id); + path[i]->enctlv = create_obs2_enctlv(path[i], + &blinding_iter, + &ids[i], + &ids[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &path[i]->node_id); } /* FIXME: Add padding! */ path[nhops-1] = tal(path, struct onionmsg_path); - path[nhops-1]->enctlv = create_final_enctlv(path[nhops-1], - &blinding_iter, - &ids[nhops-1], - /* FIXME: Pad? */ - 0, - &cmd->ld->onion_reply_secret, - &path[nhops-1]->node_id); + path[nhops-1]->enctlv = create_obs2_final_enctlv(path[nhops-1], + &blinding_iter, + &ids[nhops-1], + /* FIXME: Pad? */ + 0, + &cmd->ld->onion_reply_secret, + &path[nhops-1]->node_id); response = json_stream_success(cmd); json_add_blindedpath(response, "blindedpath", diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 528c3e604043..903aaa7f25cd 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1285,7 +1285,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: @@ -1631,7 +1631,7 @@ static bool run_tx_interactive(struct state *state, case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_TX_SIGNATURES: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: @@ -3684,7 +3684,7 @@ static u8 *handle_peer_in(struct state *state) case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index b66785673174..06d2f4097c14 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -639,14 +639,14 @@ struct sending { static struct command_result * send_modern_message(struct command *cmd, - struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *reply_path, struct sending *sending) { struct sent *sent = sending->sent; struct privkey blinding_iter; struct pubkey fwd_blinding, *node_alias; size_t nhops = tal_count(sent->path); - struct tlv_onionmsg_payload **payloads; + struct tlv_obs2_onionmsg_payload **payloads; struct out_req *req; /* Now create enctlvs for *forward* path. */ @@ -658,29 +658,29 @@ send_modern_message(struct command *cmd, &blinding_iter)); /* We overallocate: this node (0) doesn't have payload or alias */ - payloads = tal_arr(cmd, struct tlv_onionmsg_payload *, nhops); + payloads = tal_arr(cmd, struct tlv_obs2_onionmsg_payload *, nhops); node_alias = tal_arr(cmd, struct pubkey, nhops); for (size_t i = 1; i < nhops - 1; i++) { - payloads[i] = tlv_onionmsg_payload_new(payloads); - payloads[i]->enctlv = create_enctlv(payloads[i], - &blinding_iter, - &sent->path[i], - &sent->path[i+1], - /* FIXME: Pad? */ - 0, - NULL, - &blinding_iter, - &node_alias[i]); + payloads[i] = tlv_obs2_onionmsg_payload_new(payloads); + payloads[i]->enctlv = create_obs2_enctlv(payloads[i], + &blinding_iter, + &sent->path[i], + &sent->path[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &node_alias[i]); } /* Final payload contains the actual data. */ - payloads[nhops-1] = tlv_onionmsg_payload_new(payloads); + payloads[nhops-1] = tlv_obs2_onionmsg_payload_new(payloads); /* We don't include enctlv in final, but it gives us final alias */ - if (!create_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], - /* FIXME: Pad? */ 0, - NULL, - &node_alias[nhops-1])) { + if (!create_obs2_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], + /* FIXME: Pad? */ 0, + NULL, + &node_alias[nhops-1])) { /* Should not happen! */ return command_fail(cmd, LIGHTNINGD, "Could create final enctlv"); @@ -709,7 +709,7 @@ send_modern_message(struct command *cmd, json_object_start(req->js, NULL); json_add_pubkey(req->js, "id", &node_alias[i]); tlv = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&tlv, payloads[i]); + towire_obs2_onionmsg_payload(&tlv, payloads[i]); json_add_hex_talarr(req->js, "tlv", tlv); json_object_end(req->js); } @@ -724,10 +724,10 @@ static struct command_result *use_reply_path(struct command *cmd, const jsmntok_t *result, struct sending *sending) { - struct tlv_onionmsg_payload_reply_path *rpath; + struct tlv_obs2_onionmsg_payload_reply_path *rpath; - rpath = json_to_reply_path(cmd, buf, - json_get_member(buf, result, "blindedpath")); + rpath = json_to_obs2_reply_path(cmd, buf, + json_get_member(buf, result, "blindedpath")); if (!rpath) plugin_err(cmd->plugin, "could not parse reply path %.*s?", diff --git a/plugins/offers.c b/plugins/offers.c index 3d69bfe8f488..0de0718a91ab 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -43,7 +43,7 @@ static struct command_result *sendonionmessage_error(struct command *cmd, /* FIXME: replyfield string interface is to accomodate obsolete API */ struct command_result * send_onion_reply(struct command *cmd, - struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *reply_path, const char *replyfield, const u8 *replydata) { @@ -57,13 +57,13 @@ send_onion_reply(struct command *cmd, json_add_pubkey(req->js, "blinding", &reply_path->blinding); json_array_start(req->js, "hops"); for (size_t i = 0; i < nhops; i++) { - struct tlv_onionmsg_payload *omp; + struct tlv_obs2_onionmsg_payload *omp; u8 *tlv; json_object_start(req->js, NULL); json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); - omp = tlv_onionmsg_payload_new(tmpctx); + omp = tlv_obs2_onionmsg_payload_new(tmpctx); omp->enctlv = reply_path->path[i]->enctlv; /* Put payload in last hop. */ @@ -76,7 +76,7 @@ send_onion_reply(struct command *cmd, } } tlv = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&tlv, omp); + towire_obs2_onionmsg_payload(&tlv, omp); json_add_hex_talarr(req->js, "tlv", tlv); json_object_end(req->js); } @@ -89,7 +89,7 @@ static struct command_result *onion_message_modern_call(struct command *cmd, const jsmntok_t *params) { const jsmntok_t *om, *replytok, *invreqtok, *invtok; - struct tlv_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *reply_path; if (!offers_enabled) return command_hook_success(cmd); @@ -97,7 +97,7 @@ static struct command_result *onion_message_modern_call(struct command *cmd, om = json_get_member(buf, params, "onion_message"); replytok = json_get_member(buf, om, "reply_blindedpath"); if (replytok) { - reply_path = json_to_reply_path(cmd, buf, replytok); + reply_path = json_to_obs2_reply_path(cmd, buf, replytok); if (!reply_path) plugin_err(cmd->plugin, "Invalid reply path %.*s?", json_tok_full_len(replytok), diff --git a/plugins/offers.h b/plugins/offers.h index dab0c6216f4d..00a5480fb77d 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -9,7 +9,7 @@ struct command; /* Helper to send a reply */ struct command_result *WARN_UNUSED_RESULT send_onion_reply(struct command *cmd, - struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *reply_path, const char *replyfield, const u8 *replydata); #endif /* LIGHTNING_PLUGINS_OFFERS_H */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index 22d24544872e..efbaa6f6fdad 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -11,7 +11,7 @@ struct inv { struct tlv_invoice *inv; /* May be NULL */ - struct tlv_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ struct tlv_offer *offer; @@ -313,7 +313,7 @@ static struct command_result *listoffers_error(struct command *cmd, struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS) + struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS) { size_t len = tal_count(invbin); struct inv *inv = tal(cmd, struct inv); diff --git a/plugins/offers_inv_hook.h b/plugins/offers_inv_hook.h index fbc12ace6815..fae90f98122e 100644 --- a/plugins/offers_inv_hook.h +++ b/plugins/offers_inv_hook.h @@ -6,5 +6,5 @@ /* We got an onionmessage with an invoice! reply_path could be NULL. */ struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS); + struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */ diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 313ef3a65f6e..ef37f393e0c0 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -15,7 +15,7 @@ /* We need to keep the reply path around so we can reply with invoice */ struct invreq { struct tlv_invoice_request *invreq; - struct tlv_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ struct tlv_offer *offer; @@ -830,7 +830,7 @@ static struct command_result *handle_offerless_request(struct command *cmd, struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_onionmsg_payload_reply_path *reply_path) + struct tlv_obs2_onionmsg_payload_reply_path *reply_path) { size_t len = tal_count(invreqbin); struct invreq *ir = tal(cmd, struct invreq); diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index a51d0e800421..502679970efc 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -8,5 +8,5 @@ extern u32 cltv_final; /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS); + struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */ diff --git a/wire/extracted_onion_01_offers.patch b/wire/extracted_onion_01_offers.patch index 44a313ce0ce4..90c0cb6c41ac 100644 --- a/wire/extracted_onion_01_offers.patch +++ b/wire/extracted_onion_01_offers.patch @@ -4,26 +4,26 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,onionmsg_payload,reply_path,2 -+tlvdata,onionmsg_payload,reply_path,first_node_id,point, -+tlvdata,onionmsg_payload,reply_path,blinding,point, -+tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... -+tlvtype,onionmsg_payload,enctlv,10 -+tlvdata,onionmsg_payload,enctlv,enctlv,byte,... -+tlvtype,onionmsg_payload,invoice_request,64 -+tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... -+tlvtype,onionmsg_payload,invoice,66 -+tlvdata,onionmsg_payload,invoice,invoice,byte,... -+tlvtype,onionmsg_payload,invoice_error,68 -+tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... -+tlvtype,encmsg_tlvs,padding,1 -+tlvdata,encmsg_tlvs,padding,pad,byte,... -+tlvtype,encmsg_tlvs,next_node_id,4 -+tlvdata,encmsg_tlvs,next_node_id,node_id,point, -+tlvtype,encmsg_tlvs,next_blinding,12 -+tlvdata,encmsg_tlvs,next_blinding,blinding,point, -+tlvtype,encmsg_tlvs,self_id,14 -+tlvdata,encmsg_tlvs,self_id,data,byte,... ++tlvtype,obs2_onionmsg_payload,reply_path,2 ++tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, ++tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, ++tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... ++tlvtype,obs2_onionmsg_payload,enctlv,10 ++tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... ++tlvtype,obs2_onionmsg_payload,invoice_request,64 ++tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... ++tlvtype,obs2_onionmsg_payload,invoice,66 ++tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... ++tlvtype,obs2_onionmsg_payload,invoice_error,68 ++tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... ++tlvtype,obs2_encmsg_tlvs,padding,1 ++tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... ++tlvtype,obs2_encmsg_tlvs,next_node_id,4 ++tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, ++tlvtype,obs2_encmsg_tlvs,next_blinding,12 ++tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, ++tlvtype,obs2_encmsg_tlvs,self_id,14 ++tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... +subtype,onionmsg_path +subtypedata,onionmsg_path,node_id,point, +subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_onion_exp_enctlv.patch b/wire/extracted_onion_exp_enctlv.patch index ea55b7704492..59d5b08c6141 100644 --- a/wire/extracted_onion_exp_enctlv.patch +++ b/wire/extracted_onion_exp_enctlv.patch @@ -8,6 +8,6 @@ +tlvdata,tlv_payload,enctlv,enctlv,byte,... +tlvtype,tlv_payload,blinding_seed,12 +tlvdata,tlv_payload,blinding_seed,blinding_seed,pubkey, - tlvtype,onionmsg_payload,reply_path,2 - tlvdata,onionmsg_payload,reply_path,first_node_id,point, - tlvdata,onionmsg_payload,reply_path,blinding,point, + tlvtype,obs2_onionmsg_payload,reply_path,2 + tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, + tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, diff --git a/wire/extracted_peer_05_newonion.patch b/wire/extracted_peer_05_newonion.patch index 7e581997db72..ed35699aadc7 100644 --- a/wire/extracted_peer_05_newonion.patch +++ b/wire/extracted_peer_05_newonion.patch @@ -4,7 +4,7 @@ msgdata,gossip_timestamp_filter,chain_hash,chain_hash, msgdata,gossip_timestamp_filter,first_timestamp,u32, msgdata,gossip_timestamp_filter,timestamp_range,u32, -+msgtype,onion_message,387,option_onion_messages -+msgdata,onion_message,blinding,point, -+msgdata,onion_message,len,u16, -+msgdata,onion_message,onionmsg,byte,len ++msgtype,obs2_onion_message,387,option_onion_messages ++msgdata,obs2_onion_message,blinding,point, ++msgdata,obs2_onion_message,len,u16, ++msgdata,obs2_onion_message,onionmsg,byte,len diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index e3c7bc27f8cb..ca41fd885bdb 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -8,26 +8,26 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -tlvtype,onionmsg_payload,reply_path,2 -tlvdata,onionmsg_payload,reply_path,first_node_id,point, -tlvdata,onionmsg_payload,reply_path,blinding,point, -tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... -tlvtype,onionmsg_payload,enctlv,10 -tlvdata,onionmsg_payload,enctlv,enctlv,byte,... -tlvtype,onionmsg_payload,invoice_request,64 -tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... -tlvtype,onionmsg_payload,invoice,66 -tlvdata,onionmsg_payload,invoice,invoice,byte,... -tlvtype,onionmsg_payload,invoice_error,68 -tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... -tlvtype,encmsg_tlvs,padding,1 -tlvdata,encmsg_tlvs,padding,pad,byte,... -tlvtype,encmsg_tlvs,next_node_id,4 -tlvdata,encmsg_tlvs,next_node_id,node_id,point, -tlvtype,encmsg_tlvs,next_blinding,12 -tlvdata,encmsg_tlvs,next_blinding,blinding,point, -tlvtype,encmsg_tlvs,self_id,14 -tlvdata,encmsg_tlvs,self_id,data,byte,... +tlvtype,obs2_onionmsg_payload,reply_path,2 +tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, +tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, +tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... +tlvtype,obs2_onionmsg_payload,enctlv,10 +tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... +tlvtype,obs2_onionmsg_payload,invoice_request,64 +tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... +tlvtype,obs2_onionmsg_payload,invoice,66 +tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... +tlvtype,obs2_onionmsg_payload,invoice_error,68 +tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... +tlvtype,obs2_encmsg_tlvs,padding,1 +tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... +tlvtype,obs2_encmsg_tlvs,next_node_id,4 +tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, +tlvtype,obs2_encmsg_tlvs,next_blinding,12 +tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, +tlvtype,obs2_encmsg_tlvs,self_id,14 +tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... subtype,onionmsg_path subtypedata,onionmsg_path,node_id,point, subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/peer_wire.c b/wire/peer_wire.c index 7fb1d2ae8fc6..1a259da3fa71 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -33,7 +33,7 @@ static bool unknown_type(enum peer_wire t) case WIRE_QUERY_CHANNEL_RANGE: case WIRE_REPLY_CHANNEL_RANGE: case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: case WIRE_TX_ADD_OUTPUT: @@ -62,7 +62,7 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_QUERY_CHANNEL_RANGE: case WIRE_REPLY_CHANNEL_RANGE: - case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: return true; case WIRE_WARNING: case WIRE_INIT: diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 23b9d8bc58ec..0a28459fa123 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -328,7 +328,7 @@ msgtype,gossip_timestamp_filter,265,gossip_queries msgdata,gossip_timestamp_filter,chain_hash,chain_hash, msgdata,gossip_timestamp_filter,first_timestamp,u32, msgdata,gossip_timestamp_filter,timestamp_range,u32, -msgtype,onion_message,387,option_onion_messages -msgdata,onion_message,blinding,point, -msgdata,onion_message,len,u16, -msgdata,onion_message,onionmsg,byte,len +msgtype,obs2_onion_message,387,option_onion_messages +msgdata,obs2_onion_message,blinding,point, +msgdata,obs2_onion_message,len,u16, +msgdata,obs2_onion_message,onionmsg,byte,len From b3af5f5a2cc98ccd7e16c46ef3447282a36dcc58 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:04 +1030 Subject: [PATCH 0081/1530] spec: import latest onionmessage spec, based on routeblinding. This is from 6e99c5feaf60cb797507d181fe583224309318e9 We renamed the enctlv field to encrypted_recipient_data in the spec, and the new onion_message is message 513. We don't handle it until the next patch. Two renames: 1. blinding_seed -> blinding_point. 2. enctlv -> encrypted_recipient_data. We don't do a compat cycle for our JSON APIs for these experimental features only used by our own plugins, we just rename. Signed-off-by: Rusty Russell --- channeld/channeld.c | 1 + common/blindedpath.c | 232 +++++++++++++++++++++++--- common/blindedpath.h | 54 ++++-- common/json_helpers.c | 40 ++++- common/json_helpers.h | 4 + common/onion.c | 16 +- common/test/run-blindedpath_enctlv.c | 18 +- devtools/blindedpath.c | 6 +- devtools/bolt12-cli.c | 2 +- doc/PLUGINS.md | 2 +- doc/lightning-decode.7.md | 6 +- doc/schemas/decode.schema.json | 8 +- gossipd/gossipd.c | 10 ++ lightningd/onion_message.c | 7 +- lightningd/pay.c | 2 +- openingd/dualopend.c | 3 + plugins/offers.c | 4 +- wire/extracted_onion_exp_enctlv.patch | 12 +- wire/extracted_peer_05_newonion.patch | 6 +- wire/onion_wire.csv | 28 +++- wire/peer_wire.c | 2 + wire/peer_wire.csv | 4 + 22 files changed, 386 insertions(+), 81 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 5e5eeb4a8c78..7849a24a83f5 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2321,6 +2321,7 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_WARNING: case WIRE_ERROR: case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: abort(); } diff --git a/common/blindedpath.c b/common/blindedpath.c index 9e85698116a9..7994813b769e 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -58,12 +58,12 @@ static bool blind_node(const struct privkey *blinding, return true; } -static u8 *enctlv_from_encmsg(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *node, - const struct tlv_obs2_encmsg_tlvs *encmsg, - struct privkey *next_blinding, - struct pubkey *node_alias) +static u8 *enctlv_from_encmsg_raw(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const u8 *raw_encmsg TAKES, + struct privkey *next_blinding, + struct pubkey *node_alias) { /* https://github.com/lightningnetwork/lightning-rfc/blob/route-blinding/proposals/route-blinding.md */ struct secret ss, rho; @@ -87,9 +87,7 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx, if (!blind_node(blinding, &ss, node, node_alias, next_blinding)) return NULL; - /* Marshall */ - ret = tal_arr(ctx, u8, 0); - towire_obs2_encmsg_tlvs(&ret, encmsg); + ret = tal_dup_talarr(ctx, u8, raw_encmsg); SUPERVERBOSE("\t\"encmsg_hex\": \"%s\",\n", tal_hex(tmpctx, ret)); /* @@ -115,6 +113,32 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx, return ret; } +static u8 *enctlv_from_obs2_encmsg(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct tlv_obs2_encmsg_tlvs *encmsg, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + u8 *encmsg_raw = tal_arr(NULL, u8, 0); + towire_obs2_encmsg_tlvs(&encmsg_raw, encmsg); + return enctlv_from_encmsg_raw(ctx, blinding, node, take(encmsg_raw), + next_blinding, node_alias); +} + +static u8 *enctlv_from_encmsg(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct tlv_encrypted_data_tlv *encmsg, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + u8 *encmsg_raw = tal_arr(NULL, u8, 0); + towire_encrypted_data_tlv(&encmsg_raw, encmsg); + return enctlv_from_encmsg_raw(ctx, blinding, node, take(encmsg_raw), + next_blinding, node_alias); +} + bool unblind_onion(const struct pubkey *blinding, void (*ecdh)(const struct pubkey *point, struct secret *ss), struct pubkey *onion_key, @@ -136,16 +160,13 @@ bool unblind_onion(const struct pubkey *blinding, hmac.data) == 1; } -static struct tlv_obs2_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv) +static u8 *decrypt_encmsg_raw(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) { struct secret rho; u8 *dec; - const u8 *cursor; - size_t maxlen; - struct tlv_obs2_encmsg_tlvs *encmsg; /* All-zero npub */ static const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; @@ -161,7 +182,7 @@ static struct tlv_obs2_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, if (tal_bytelen(enctlv) < crypto_aead_chacha20poly1305_ietf_ABYTES) return NULL; - dec = tal_arr(tmpctx, u8, tal_bytelen(enctlv) + dec = tal_arr(ctx, u8, tal_bytelen(enctlv) - crypto_aead_chacha20poly1305_ietf_ABYTES); if (crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL, NULL, @@ -169,10 +190,19 @@ static struct tlv_obs2_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, NULL, 0, npub, rho.data) != 0) - return NULL; + return tal_free(dec); + + return dec; +} - cursor = dec; - maxlen = tal_bytelen(dec); +static struct tlv_obs2_encmsg_tlvs *decrypt_obs2_encmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) +{ + struct tlv_obs2_encmsg_tlvs *encmsg; + const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); + size_t maxlen = tal_bytelen(cursor); /* BOLT-onion-message #4: * @@ -187,6 +217,156 @@ static struct tlv_obs2_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, return encmsg; } +static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) +{ + struct tlv_encrypted_data_tlv *encmsg; + const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); + size_t maxlen = tal_bytelen(cursor); + + /* BOLT-onion-message #4: + * + * - if the `enctlv` is not a valid TLV... + * - MUST drop the message. + */ + encmsg = tlv_encrypted_data_tlv_new(ctx); + if (!fromwire_encrypted_data_tlv(&cursor, &maxlen, encmsg) + || !tlv_fields_valid(encmsg->fields, NULL, NULL)) + return tal_free(encmsg); + + return encmsg; +} + +bool decrypt_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) +{ + struct tlv_encrypted_data_tlv *encmsg; + + encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + /* BOLT-onion-message #4: + * + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` ... does not contain + * `next_node_id`: + * - MUST drop the message. + */ + if (!encmsg->next_node_id) + return false; + + /* BOLT-onion-message #4: + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` contains `path_id`: + * - MUST drop the message. + */ + if (encmsg->path_id) + return false; + + /* BOLT-onion-message #4: + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if `blinding` is specified in the `enctlv`: + * - MUST pass that as `blinding` in the `onion_message` + * - otherwise: + * - MUST pass `blinding` derived as in + * [Route Blinding][route-blinding] (i.e. + * `E(i+1) = H(E(i) || ss(i)) * E(i)`). + */ + *next_node = *encmsg->next_node_id; + if (encmsg->next_blinding_override) + *next_blinding = *encmsg->next_blinding_override; + else { + /* E(i-1) = H(E(i) || ss(i)) * E(i) */ + struct sha256 h; + blinding_hash_e_and_ss(blinding, ss, &h); + blinding_next_pubkey(blinding, &h, next_blinding); + } + return true; +} + +bool decrypt_final_enctlv(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **path_id) +{ + struct tlv_encrypted_data_tlv *encmsg; + struct secret node_id_blinding; + + /* Repeat the tweak to get the alias it was using for us */ + subkey_from_hmac("blinded_node_id", ss, &node_id_blinding); + *alias = *my_id; + if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, + &alias->pubkey, + node_id_blinding.data) != 1) + return false; + + encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + if (tal_bytelen(encmsg->path_id) == sizeof(**path_id)) { + *path_id = tal(ctx, struct secret); + memcpy(*path_id, encmsg->path_id, sizeof(**path_id)); + } else + *path_id = NULL; + + return true; +} + +u8 *create_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct pubkey *next_node, + size_t padlen, + const struct pubkey *next_blinding_override, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + struct tlv_encrypted_data_tlv *encmsg = tlv_encrypted_data_tlv_new(tmpctx); + if (padlen) + encmsg->padding = tal_arrz(encmsg, u8, padlen); + encmsg->next_node_id = cast_const(struct pubkey *, next_node); + encmsg->next_blinding_override = cast_const(struct pubkey *, next_blinding_override); + + return enctlv_from_encmsg(ctx, blinding, node, encmsg, + next_blinding, node_alias); +} + +u8 *create_final_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *final_node, + size_t padlen, + const struct secret *path_id, + struct pubkey *node_alias) +{ + struct tlv_encrypted_data_tlv *encmsg = tlv_encrypted_data_tlv_new(tmpctx); + struct privkey unused_next_blinding; + + if (padlen) + encmsg->padding = tal_arrz(encmsg, u8, padlen); + if (path_id) + encmsg->path_id = (u8 *)tal_dup(encmsg, struct secret, path_id); + + return enctlv_from_encmsg(ctx, blinding, final_node, encmsg, + &unused_next_blinding, node_alias); +} + +/* Obsolete variants */ bool decrypt_obs2_enctlv(const struct pubkey *blinding, const struct secret *ss, const u8 *enctlv, @@ -195,7 +375,7 @@ bool decrypt_obs2_enctlv(const struct pubkey *blinding, { struct tlv_obs2_encmsg_tlvs *encmsg; - encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); + encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); if (!encmsg) return false; @@ -263,7 +443,7 @@ bool decrypt_obs2_final_enctlv(const tal_t *ctx, node_id_blinding.data) != 1) return false; - encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); + encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); if (!encmsg) return false; @@ -291,8 +471,8 @@ u8 *create_obs2_enctlv(const tal_t *ctx, encmsg->next_node_id = cast_const(struct pubkey *, next_node); encmsg->next_blinding = cast_const(struct pubkey *, override_blinding); - return enctlv_from_encmsg(ctx, blinding, node, encmsg, - next_blinding, node_alias); + return enctlv_from_obs2_encmsg(ctx, blinding, node, encmsg, + next_blinding, node_alias); } u8 *create_obs2_final_enctlv(const tal_t *ctx, @@ -310,6 +490,6 @@ u8 *create_obs2_final_enctlv(const tal_t *ctx, if (self_id) encmsg->self_id = (u8 *)tal_dup(encmsg, struct secret, self_id); - return enctlv_from_encmsg(ctx, blinding, final_node, encmsg, - &unused_next_blinding, node_alias); + return enctlv_from_obs2_encmsg(ctx, blinding, final_node, encmsg, + &unused_next_blinding, node_alias); } diff --git a/common/blindedpath.h b/common/blindedpath.h index a207c316d43f..f3415939551f 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -17,18 +17,18 @@ struct secret; * @node: the pubkey of the node to encrypt for * @next_node: the pubkey of the next node, to place in enctlv * @padlen: if non-zero, the bytes of padding to add (also adds 2 byte padding hdr) - * @override_blinding: the optional blinding point to place in enctlv + * @next_blinding_override: the optional blinding point to place in enctlv * @next_blinding: (out) e(i+1), the next blinding secret. * @node_alias: (out) the blinded pubkey of the node to tell the recipient. * * Returns the enctlv blob, or NULL if the secret is invalid. */ -u8 *create_obs2_enctlv(const tal_t *ctx, +u8 *create_enctlv(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *node, const struct pubkey *next_node, size_t padlen, - const struct pubkey *override_blinding, + const struct pubkey *next_blinding_override, struct privkey *next_blinding, struct pubkey *node_alias) NON_NULL_ARGS(2, 3, 4, 7, 8); @@ -39,16 +39,16 @@ u8 *create_obs2_enctlv(const tal_t *ctx, * @blinding: e(i), the blinding secret * @final_node: the pubkey of the node to encrypt for * @padlen: if non-zero, the bytes of padding to add (also adds 2 byte padding hdr) - * @self_id: secret to include in enctlv, if not NULL. + * @path_id: secret to include in enctlv, if not NULL. * @node_alias: (out) the blinded pubkey of the node to tell the recipient. * * If it fails, it means one of the privkeys is bad. */ -u8 *create_obs2_final_enctlv(const tal_t *ctx, +u8 *create_final_enctlv(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *final_node, size_t padlen, - const struct secret *self_id, + const struct secret *path_id, struct pubkey *node_alias) NON_NULL_ARGS(2, 3, 6); @@ -77,7 +77,7 @@ bool unblind_onion(const struct pubkey *blinding, * * Returns false if decryption failed or encmsg was malformed. */ -bool decrypt_obs2_enctlv(const struct pubkey *blinding, +bool decrypt_enctlv(const struct pubkey *blinding, const struct secret *ss, const u8 *enctlv, struct pubkey *next_node, @@ -86,23 +86,55 @@ bool decrypt_obs2_enctlv(const struct pubkey *blinding, /** * decrypt_final_enctlv - Decrypt an encmsg to form an enctlv. - * @ctx: tal context for @self_id + * @ctx: tal context for @path_id * @blinding: E(i), the blinding pubkey the previous peer gave us. * @ss: the blinding secret from unblind_onion(). * @enctlv: the enctlv from the onion (tal, may be NULL). * @my_id: the pubkey of this node. * @alias: (out) the node_id this was addressed to. - * @self_id: (out) the secret contained in the enctlv, if any. + * @path_id: (out) the secret contained in the enctlv, if any (NULL if invalid or unset) * * Returns false if decryption failed or encmsg was malformed. */ -bool decrypt_obs2_final_enctlv(const tal_t *ctx, +bool decrypt_final_enctlv(const tal_t *ctx, const struct pubkey *blinding, const struct secret *ss, const u8 *enctlv, const struct pubkey *my_id, struct pubkey *alias, - struct secret **self_id) + struct secret **path_id) + NON_NULL_ARGS(1, 2, 4, 5); + +/* Obsolete variants */ +u8 *create_obs2_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct pubkey *next_node, + size_t padlen, + const struct pubkey *override_blinding, + struct privkey *next_blinding, + struct pubkey *node_alias) + NON_NULL_ARGS(2, 3, 4, 7, 8); +u8 *create_obs2_final_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *final_node, + size_t padlen, + const struct secret *self_id, + struct pubkey *node_alias) + NON_NULL_ARGS(2, 3, 6); +bool decrypt_obs2_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) + NON_NULL_ARGS(1, 2, 4, 5); +bool decrypt_obs2_final_enctlv(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **self_id) NON_NULL_ARGS(1, 2, 4, 5); #endif /* LIGHTNING_COMMON_BLINDEDPATH_H */ diff --git a/common/json_helpers.c b/common/json_helpers.c index 0fa2233c90ca..ee7f235318d6 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -164,12 +164,48 @@ json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *t rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); json_for_each_arr(i, t, hops) { rpath->path[i] = tal(rpath->path, struct onionmsg_path); - err = json_scan(tmpctx, buffer, t, "{id:%,enctlv:%}", + err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", JSON_SCAN(json_to_pubkey, &rpath->path[i]->node_id), JSON_SCAN_TAL(rpath->path[i], json_tok_bin_from_hex, - &rpath->path[i]->enctlv)); + &rpath->path[i]->encrypted_recipient_data)); + if (err) + return tal_free(rpath); + } + + return rpath; +} + +struct tlv_onionmsg_payload_reply_path * +json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + struct tlv_onionmsg_payload_reply_path *rpath; + const jsmntok_t *hops, *t; + size_t i; + const char *err; + + rpath = tal(ctx, struct tlv_onionmsg_payload_reply_path); + err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", + JSON_SCAN(json_to_pubkey, &rpath->blinding), + JSON_SCAN(json_to_pubkey, &rpath->first_node_id), + NULL); + if (err) + return tal_free(rpath); + + hops = json_get_member(buffer, tok, "hops"); + if (!hops || hops->size < 1) + return tal_free(rpath); + + rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); + json_for_each_arr(i, t, hops) { + rpath->path[i] = tal(rpath->path, struct onionmsg_path); + err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", + JSON_SCAN(json_to_pubkey, + &rpath->path[i]->node_id), + JSON_SCAN_TAL(rpath->path[i], + json_tok_bin_from_hex, + &rpath->path[i]->encrypted_recipient_data)); if (err) return tal_free(rpath); } diff --git a/common/json_helpers.h b/common/json_helpers.h index c6febd343cc1..3e10642404a2 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -80,6 +80,10 @@ bool split_tok(const char *buffer, const jsmntok_t *tok, jsmntok_t *b); /* Extract reply path from this JSON */ +struct tlv_onionmsg_payload_reply_path * +json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); + +/* Obsolete version! */ struct tlv_obs2_onionmsg_payload_reply_path * json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); diff --git a/common/onion.c b/common/onion.c index 4743117da2aa..aeab17de1a53 100644 --- a/common/onion.c +++ b/common/onion.c @@ -82,8 +82,8 @@ u8 *onion_nonfinal_hop(const tal_t *ctx, tlv->short_channel_id = cast_const(struct short_channel_id *, scid); #if EXPERIMENTAL_FEATURES - tlv->blinding_seed = cast_const(struct pubkey *, blinding); - tlv->enctlv = cast_const(u8 *, enctlv); + tlv->blinding_point = cast_const(struct pubkey *, blinding); + tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); #endif return make_tlv_hop(ctx, tlv); } else { @@ -135,8 +135,8 @@ u8 *onion_final_hop(const tal_t *ctx, tlv->payment_data = &tlv_pdata; } #if EXPERIMENTAL_FEATURES - tlv->blinding_seed = cast_const(struct pubkey *, blinding); - tlv->enctlv = cast_const(u8 *, enctlv); + tlv->blinding_point = cast_const(struct pubkey *, blinding); + tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); #endif return make_tlv_hop(ctx, tlv); } else { @@ -362,10 +362,10 @@ struct onion_payload *onion_decode(const tal_t *ctx, #if EXPERIMENTAL_FEATURES if (!p->blinding) { /* If we have no blinding, it could be in TLV. */ - if (tlv->blinding_seed) { + if (tlv->blinding_point) { p->blinding = tal_dup(p, struct pubkey, - tlv->blinding_seed); + tlv->blinding_point); ecdh(p->blinding, &p->blinding_ss); } } else @@ -377,12 +377,12 @@ struct onion_payload *onion_decode(const tal_t *ctx, if (rs->nextcase == ONION_FORWARD) { struct tlv_tlv_payload *ntlv; - if (!tlv->enctlv) + if (!tlv->encrypted_recipient_data) goto fail; ntlv = decrypt_tlv(tmpctx, &p->blinding_ss, - tlv->enctlv); + tlv->encrypted_recipient_data); if (!ntlv) goto fail; diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index 867b8c82f284..477220765fd6 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -136,7 +136,7 @@ int main(int argc, char *argv[]) /* We output the JSON test vectors. */ printf("[{"); - json_strfield("test name", "Simple enctlv for Alice, next is Bob"); + json_strfield("test name", "Simple encrypted_recipient_data for Alice, next is Bob"); json_strfield("node_privkey", type_to_string(tmpctx, struct privkey, &alice)); json_strfield("node_id", @@ -145,14 +145,14 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct privkey, &blinding)); json_strfield("blinding", type_to_string(tmpctx, struct pubkey, &blinding_pub)); - printf("\t\"encmsg\": {\n" + printf("\t\"encrypted_data_tlv\": {\n" "\t\t\"next_node_id\": \"%s\"\n" "\t},\n", type_to_string(tmpctx, struct pubkey, &bob_id)); enctlv = create_obs2_enctlv(tmpctx, &blinding, &alice_id, &bob_id, 0, NULL, &blinding, &alias); - printf("\t\"enctlv_hex\": \"%s\"\n" + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -164,7 +164,7 @@ int main(int argc, char *argv[]) printf("{"); json_strfield("test name", - "Blinding-key-override enctlv for Bob, next is Carol"); + "Blinding-key-override encrypted_recipient_data for Bob, next is Carol"); json_strfield("node_privkey", type_to_string(tmpctx, struct privkey, &bob)); json_strfield("node_id", @@ -173,7 +173,7 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct privkey, &blinding)); json_strfield("blinding", type_to_string(tmpctx, struct pubkey, &blinding_pub)); - printf("\t\"encmsg\": {\n" + printf("\t\"encrypted_data_tlv\": {\n" "\t\t\"next_node_id\": \"%s\",\n" "\t\t\"blinding\": \"%s\"\n" "\t},\n", @@ -182,7 +182,7 @@ int main(int argc, char *argv[]) enctlv = create_obs2_enctlv(tmpctx, &blinding, &bob_id, &carol_id, 0, &override_blinding_pub, &blinding, &alias); - printf("\t\"enctlv_hex\": \"%s\"\n" + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -193,7 +193,7 @@ int main(int argc, char *argv[]) blinding_pub = override_blinding_pub; printf("{"); - json_strfield("test name", "Padded enctlv for Carol, next is Dave"); + json_strfield("test name", "Padded encrypted_recipient_data for Carol, next is Dave"); json_strfield("node_privkey", type_to_string(tmpctx, struct privkey, &carol)); json_strfield("node_id", @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct privkey, &blinding)); json_strfield("blinding", type_to_string(tmpctx, struct pubkey, &blinding_pub)); - printf("\t\"encmsg\": {\n" + printf("\t\"encrypted_data_tlv\": {\n" "\t\t\"next_node_id\": \"%s\",\n" "\t\t\"padding\": \"%s\"\n" "\t},\n", @@ -211,7 +211,7 @@ int main(int argc, char *argv[]) enctlv = create_obs2_enctlv(tmpctx, &blinding, &carol_id, &dave_id, 35, NULL, &blinding, &alias); - printf("\t\"enctlv_hex\": \"%s\"\n" + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" "}]\n", tal_hex(tmpctx, enctlv)); diff --git a/devtools/blindedpath.c b/devtools/blindedpath.c index 57d320894832..3e46662ab2a8 100644 --- a/devtools/blindedpath.c +++ b/devtools/blindedpath.c @@ -269,11 +269,11 @@ int main(int argc, char **argv) /* Look for enctlv */ if (!outer->enctlv) - errx(1, "No enctlv field"); + errx(1, "No encrypted_recipient_data field"); if (tal_bytelen(outer->enctlv) < crypto_aead_chacha20poly1305_ietf_ABYTES) - errx(1, "enctlv field too short"); + errx(1, "encrypted_recipient_data field too short"); dec = tal_arr(tmpctx, u8, tal_bytelen(outer->enctlv) @@ -286,7 +286,7 @@ int main(int argc, char **argv) npub, rho.data); if (ret != 0) - errx(1, "Failed to decrypt enctlv field"); + errx(1, "Failed to decrypt encrypted_recipient_data field"); printf("Contents: %s\n", tal_hex(tmpctx, dec)); diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index aa4dbf2bbfdb..4e63c13beb8f 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -270,7 +270,7 @@ static bool print_blindedpaths(struct blinded_path **paths, printf(" %s:%s", type_to_string(tmpctx, struct pubkey, &p[j]->node_id), - tal_hex(tmpctx, p[j]->enctlv)); + tal_hex(tmpctx, p[j]->encrypted_recipient_data)); if (blindedpay) { if (bp_idx < tal_count(blindedpay)) printf("fee=%u/%u,cltv=%u,features=%s", diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 752b412335bb..07e04537494a 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1542,7 +1542,7 @@ The payload for a call follows this format: "reply_first_node": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", "reply_blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", "reply_path": [ {"id": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", - "enctlv": "0a020d0d", + "encrypted_recipient_data": "0a020d0d", "blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f"} ], "invoice_request": "0a020d0d", "invoice": "0a020d0d", diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 6708071ed4b8..81d8a62b9453 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -42,7 +42,7 @@ If **type** is "bolt12 offer", and **valid** is *true*: - **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 + - **encrypted_recipient_data** (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: @@ -82,7 +82,7 @@ If **type** is "bolt12 invoice", and **valid** is *true*: - **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 + - **encrypted_recipient_data** (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 @@ -180,4 +180,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cd4a18d07c23b6a995f8db7cac25344e340e512c786b8cf743f56ecceeb84960) +[comment]: # ( SHA256STAMP:d05b5fc1bf230b3bbd03e2023fb0c6bbefb700f7c3cfb43512da48dbce45f005) diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json index 9e12c1588876..808ef8f4313d 100644 --- a/doc/schemas/decode.schema.json +++ b/doc/schemas/decode.schema.json @@ -146,7 +146,7 @@ "type": "object", "required": [ "node_id", - "enctlv" + "encrypted_recipient_data" ], "additionalProperties": false, "properties": { @@ -154,7 +154,7 @@ "type": "pubkey", "description": "node_id of the hop" }, - "enctlv": { + "encrypted_recipient_data": { "type": "hex", "description": "encrypted TLV entry for this hop" } @@ -388,7 +388,7 @@ "type": "object", "required": [ "node_id", - "enctlv" + "encrypted_recipient_data" ], "additionalProperties": false, "properties": { @@ -396,7 +396,7 @@ "type": "pubkey", "description": "node_id of the hop" }, - "enctlv": { + "encrypted_recipient_data": { "type": "hex", "description": "encrypted TLV entry for this hop" } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index b9e07a35375a..419ed2ca14bf 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -520,6 +520,13 @@ static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, return daemon_conn_read_next(conn, daemon->master); } +/* Peer sends an onion msg. */ +static u8 *handle_onion_message(struct peer *peer, const u8 *msg) +{ + /* FIXME! */ + return NULL; +} + /*~ This is where the per-peer daemons send us messages. It's either forwarded * gossip, or a request for information. We deliberately use non-overlapping * message types so we can distinguish them. */ @@ -556,6 +563,9 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, case WIRE_OBS2_ONION_MESSAGE: err = handle_obs2_onion_message(peer, msg); goto handled_relay; + case WIRE_ONION_MESSAGE: + err = handle_onion_message(peer, msg); + goto handled_relay; /* These are non-gossip messages (!is_msg_for_gossipd()) */ case WIRE_WARNING: diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 1a0de05e963a..9d5ef23ab982 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -37,7 +37,8 @@ static void json_add_blindedpath(struct json_stream *stream, for (size_t i = 0; i < tal_count(path); i++) { json_object_start(stream, NULL); json_add_pubkey(stream, "id", &path[i]->node_id); - json_add_hex_talarr(stream, "enctlv", path[i]->enctlv); + json_add_hex_talarr(stream, "encrypted_recipient_data", + path[i]->encrypted_recipient_data); json_object_end(stream); }; json_array_end(stream); @@ -327,7 +328,7 @@ static struct command_result *json_blindedpath(struct command *cmd, for (size_t i = 0; i < nhops - 1; i++) { path[i] = tal(path, struct onionmsg_path); - path[i]->enctlv = create_obs2_enctlv(path[i], + path[i]->encrypted_recipient_data = create_obs2_enctlv(path[i], &blinding_iter, &ids[i], &ids[i+1], @@ -340,7 +341,7 @@ static struct command_result *json_blindedpath(struct command *cmd, /* FIXME: Add padding! */ path[nhops-1] = tal(path, struct onionmsg_path); - path[nhops-1]->enctlv = create_obs2_final_enctlv(path[nhops-1], + path[nhops-1]->encrypted_recipient_data = create_obs2_final_enctlv(path[nhops-1], &blinding_iter, &ids[nhops-1], /* FIXME: Pad? */ diff --git a/lightningd/pay.c b/lightningd/pay.c index 6f4db608f121..c6f414f2ee87 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1392,7 +1392,7 @@ static struct command_result *param_route_hops(struct command *cmd, p_opt("direction", param_number, &direction), p_opt("style", param_route_hop_style, &style), p_opt("blinding", param_pubkey, &blinding), - p_opt("enctlv", param_bin_from_hex, &enctlv), + p_opt("encrypted_recipient_data", param_bin_from_hex, &enctlv), NULL)) return command_param_failed(); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 903aaa7f25cd..795fce83cb10 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1286,6 +1286,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: @@ -1632,6 +1633,7 @@ static bool run_tx_interactive(struct state *state, case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: case WIRE_TX_SIGNATURES: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: @@ -3685,6 +3687,7 @@ static u8 *handle_peer_in(struct state *state) case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: diff --git a/plugins/offers.c b/plugins/offers.c index 0de0718a91ab..4a9f9888977c 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -64,7 +64,7 @@ send_onion_reply(struct command *cmd, json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); omp = tlv_obs2_onionmsg_payload_new(tmpctx); - omp->enctlv = reply_path->path[i]->enctlv; + omp->enctlv = reply_path->path[i]->encrypted_recipient_data; /* Put payload in last hop. */ if (i == nhops - 1) { @@ -227,7 +227,7 @@ static void json_add_onionmsg_path(struct json_stream *js, { json_object_start(js, fieldname); json_add_pubkey(js, "node_id", &path->node_id); - json_add_hex_talarr(js, "enctlv", path->enctlv); + json_add_hex_talarr(js, "encrypted_recipient_data", path->encrypted_recipient_data); if (payinfo) { json_add_u32(js, "fee_base_msat", payinfo->fee_base_msat); json_add_u32(js, "fee_proportional_millionths", diff --git a/wire/extracted_onion_exp_enctlv.patch b/wire/extracted_onion_exp_enctlv.patch index 59d5b08c6141..1897193b8c8b 100644 --- a/wire/extracted_onion_exp_enctlv.patch +++ b/wire/extracted_onion_exp_enctlv.patch @@ -1,13 +1,15 @@ --- wire/extracted_onion_wire_csv 2020-02-25 05:52:39.612291156 +1030 +++ - 2020-03-20 15:11:55.763880895 +1030 -@@ -8,6 +8,10 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, +--- wire/onion_exp_wire.csv.~1~ 2021-11-17 10:56:59.947630815 +1030 ++++ wire/onion_exp_wire.csv 2021-11-17 10:59:39.304581244 +1030 +@@ -8,6 +8,10 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,tlv_payload,enctlv,10 -+tlvdata,tlv_payload,enctlv,enctlv,byte,... -+tlvtype,tlv_payload,blinding_seed,12 -+tlvdata,tlv_payload,blinding_seed,blinding_seed,pubkey, ++tlvtype,tlv_payload,encrypted_recipient_data,10 ++tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... ++tlvtype,tlv_payload,blinding_point,12 ++tlvdata,tlv_payload,blinding_point,blinding,point, tlvtype,obs2_onionmsg_payload,reply_path,2 tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, diff --git a/wire/extracted_peer_05_newonion.patch b/wire/extracted_peer_05_newonion.patch index ed35699aadc7..b14d5471e381 100644 --- a/wire/extracted_peer_05_newonion.patch +++ b/wire/extracted_peer_05_newonion.patch @@ -1,6 +1,6 @@ --- peer_wire.csv 2021-08-25 12:41:02.876254003 +0930 +++ peer_wire.csv.raw 2021-08-25 13:42:31.991693809 +0930 -@@ -320,3 +210,7 @@ +@@ -320,3 +210,11 @@ msgdata,gossip_timestamp_filter,chain_hash,chain_hash, msgdata,gossip_timestamp_filter,first_timestamp,u32, msgdata,gossip_timestamp_filter,timestamp_range,u32, @@ -8,3 +8,7 @@ +msgdata,obs2_onion_message,blinding,point, +msgdata,obs2_onion_message,len,u16, +msgdata,obs2_onion_message,onionmsg,byte,len ++msgtype,onion_message,513,option_onion_messages ++msgdata,onion_message,blinding,point, ++msgdata,onion_message,len,u16, ++msgdata,onion_message,onionmsg,byte,len diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index ca41fd885bdb..2ac0c4cff516 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -28,10 +28,36 @@ tlvtype,obs2_encmsg_tlvs,next_blinding,12 tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, tlvtype,obs2_encmsg_tlvs,self_id,14 tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... +tlvtype,tlv_payload,encrypted_recipient_data,10 +tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... +tlvtype,tlv_payload,blinding_point,12 +tlvdata,tlv_payload,blinding_point,blinding,point, +tlvtype,encrypted_data_tlv,padding,1 +tlvdata,encrypted_data_tlv,padding,padding,byte,... +tlvtype,encrypted_data_tlv,short_channel_id,2 +tlvdata,encrypted_data_tlv,short_channel_id,short_channel_id,short_channel_id, +tlvtype,encrypted_data_tlv,next_node_id,4 +tlvdata,encrypted_data_tlv,next_node_id,node_id,point, +tlvtype,encrypted_data_tlv,path_id,6 +tlvdata,encrypted_data_tlv,path_id,data,byte,... +tlvtype,encrypted_data_tlv,next_blinding_override,8 +tlvdata,encrypted_data_tlv,next_blinding_override,blinding,point, +tlvtype,onionmsg_payload,reply_path,2 +tlvdata,onionmsg_payload,reply_path,first_node_id,point, +tlvdata,onionmsg_payload,reply_path,blinding,point, +tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... +tlvtype,onionmsg_payload,encrypted_data_tlv,4 +tlvdata,onionmsg_payload,encrypted_data_tlv,encrypted_data_tlv,byte,... +tlvtype,onionmsg_payload,invoice_request,64 +tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... +tlvtype,onionmsg_payload,invoice,66 +tlvdata,onionmsg_payload,invoice,invoice,byte,... +tlvtype,onionmsg_payload,invoice_error,68 +tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... subtype,onionmsg_path subtypedata,onionmsg_path,node_id,point, subtypedata,onionmsg_path,enclen,u16, -subtypedata,onionmsg_path,enctlv,byte,enclen +subtypedata,onionmsg_path,encrypted_recipient_data,byte,enclen msgtype,invalid_realm,PERM|1 msgtype,temporary_node_failure,NODE|2 msgtype,permanent_node_failure,PERM|NODE|2 diff --git a/wire/peer_wire.c b/wire/peer_wire.c index 1a259da3fa71..aa3f3133eb50 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -34,6 +34,7 @@ static bool unknown_type(enum peer_wire t) case WIRE_REPLY_CHANNEL_RANGE: case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: case WIRE_TX_ADD_OUTPUT: @@ -63,6 +64,7 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_QUERY_CHANNEL_RANGE: case WIRE_REPLY_CHANNEL_RANGE: case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: return true; case WIRE_WARNING: case WIRE_INIT: diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 0a28459fa123..4043c63509bf 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -332,3 +332,7 @@ msgtype,obs2_onion_message,387,option_onion_messages msgdata,obs2_onion_message,blinding,point, msgdata,obs2_onion_message,len,u16, msgdata,obs2_onion_message,onionmsg,byte,len +msgtype,onion_message,513,option_onion_messages +msgdata,onion_message,blinding,point, +msgdata,onion_message,len,u16, +msgdata,onion_message,onionmsg,byte,len From 5cf2c2fbd7e781345e5c0364263ac616614b8b6c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0082/1530] lightningd: return both obsolete and modern blindedpaths from "blindedpath" RPC. Signed-off-by: Rusty Russell --- lightningd/onion_message.c | 42 +++++++++++++++++++++++++++++++------- plugins/fetchinvoice.c | 2 +- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 9d5ef23ab982..6b73080dd324 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -292,8 +292,8 @@ static struct command_result *json_blindedpath(struct command *cmd, { struct pubkey *ids; struct onionmsg_path **path; - struct privkey blinding_iter; - struct pubkey first_blinding, first_node, me; + struct privkey first_blinding, blinding_iter; + struct pubkey first_blinding_pubkey, first_node, me; size_t nhops; struct json_stream *response; @@ -317,8 +317,8 @@ static struct command_result *json_blindedpath(struct command *cmd, type_to_string(tmpctx, struct pubkey, &ids[nhops-1])); - randombytes_buf(&blinding_iter, sizeof(blinding_iter)); - if (!pubkey_from_privkey(&blinding_iter, &first_blinding)) + randombytes_buf(&first_blinding, sizeof(first_blinding)); + if (!pubkey_from_privkey(&first_blinding, &first_blinding_pubkey)) /* Should not happen! */ return command_fail(cmd, LIGHTNINGD, "Could not convert blinding to pubkey!"); @@ -326,9 +326,10 @@ static struct command_result *json_blindedpath(struct command *cmd, /* We convert ids into aliases as we go. */ path = tal_arr(cmd, struct onionmsg_path *, nhops); + blinding_iter = first_blinding; for (size_t i = 0; i < nhops - 1; i++) { path[i] = tal(path, struct onionmsg_path); - path[i]->encrypted_recipient_data = create_obs2_enctlv(path[i], + path[i]->encrypted_recipient_data = create_enctlv(path[i], &blinding_iter, &ids[i], &ids[i+1], @@ -341,7 +342,7 @@ static struct command_result *json_blindedpath(struct command *cmd, /* FIXME: Add padding! */ path[nhops-1] = tal(path, struct onionmsg_path); - path[nhops-1]->encrypted_recipient_data = create_obs2_final_enctlv(path[nhops-1], + path[nhops-1]->encrypted_recipient_data = create_final_enctlv(path[nhops-1], &blinding_iter, &ids[nhops-1], /* FIXME: Pad? */ @@ -351,7 +352,34 @@ static struct command_result *json_blindedpath(struct command *cmd, response = json_stream_success(cmd); json_add_blindedpath(response, "blindedpath", - &first_blinding, &first_node, path); + &first_blinding_pubkey, &first_node, path); + + /* Now create obsolete one! */ + blinding_iter = first_blinding; + for (size_t i = 0; i < nhops - 1; i++) { + path[i] = tal(path, struct onionmsg_path); + path[i]->encrypted_recipient_data = create_obs2_enctlv(path[i], + &blinding_iter, + &ids[i], + &ids[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &path[i]->node_id); + } + + /* FIXME: Add padding! */ + path[nhops-1] = tal(path, struct onionmsg_path); + path[nhops-1]->encrypted_recipient_data = create_obs2_final_enctlv(path[nhops-1], + &blinding_iter, + &ids[nhops-1], + /* FIXME: Pad? */ + 0, + &cmd->ld->onion_reply_secret, + &path[nhops-1]->node_id); + json_add_blindedpath(response, "obs2blindedpath", + &first_blinding_pubkey, &first_node, path); return command_success(cmd, response); } diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 06d2f4097c14..59eb153b6360 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -727,7 +727,7 @@ static struct command_result *use_reply_path(struct command *cmd, struct tlv_obs2_onionmsg_payload_reply_path *rpath; rpath = json_to_obs2_reply_path(cmd, buf, - json_get_member(buf, result, "blindedpath")); + json_get_member(buf, result, "obs2blindedpath")); if (!rpath) plugin_err(cmd->plugin, "could not parse reply path %.*s?", From e7b263304e12eba115e7addc9a4af15a0853c651 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0083/1530] lightningd: Send updated onion spec messages. It's very similar to the previous, but there are a few changes: 1. The enctlv fields are numbered differently. 2. The message itself is a different number. The onionmsg_path type is the same, however, so we keep that constant at least. The result is a lot of cut & paste, but we will delete the old one next release. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 12 ++-- gossipd/gossipd_wire.csv | 1 + lightningd/onion_message.c | 35 ++++++++++-- plugins/fetchinvoice.c | 108 ++++++++++++++++++++++++++++++++--- plugins/offers.c | 63 ++++++++++++++++++-- plugins/offers.h | 3 +- plugins/offers_inv_hook.c | 11 ++-- plugins/offers_invreq_hook.c | 12 ++-- 8 files changed, 213 insertions(+), 32 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 419ed2ca14bf..3ac14a19f7ab 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -505,17 +505,21 @@ static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, u8 *onionmsg; struct pubkey blinding; struct peer *peer; + bool obs2; - if (!fromwire_gossipd_send_onionmsg(msg, msg, &id, &onionmsg, &blinding)) + if (!fromwire_gossipd_send_onionmsg(msg, msg, &obs2, &id, &onionmsg, &blinding)) master_badmsg(WIRE_GOSSIPD_SEND_ONIONMSG, msg); /* Even though lightningd checks for valid ids, there's a race * where it might vanish before we read this command. */ peer = find_peer(daemon, &id); if (peer) { - queue_peer_msg(peer, - take(towire_obs2_onion_message(NULL, - &blinding, onionmsg))); + u8 *omsg; + if (obs2) + omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg); + else + omsg = towire_onion_message(NULL, &blinding, onionmsg); + queue_peer_msg(peer, take(omsg)); } return daemon_conn_read_next(conn, daemon->master); } diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 4a1e49a35b0a..ea1986f4cc95 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -86,6 +86,7 @@ msgdata,gossipd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len # Lightningd tells us to send an onion message. msgtype,gossipd_send_onionmsg,3041 +msgdata,gossipd_send_onionmsg,obs2,bool, msgdata,gossipd_send_onionmsg,id,node_id, msgdata,gossipd_send_onionmsg,onion_len,u16, msgdata,gossipd_send_onionmsg,onion,u8,onion_len diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 6b73080dd324..73a088b7411a 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -199,10 +199,11 @@ static struct command_result *param_onion_hops(struct command *cmd, return NULL; } -static struct command_result *json_sendonionmessage(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) +static struct command_result *json_sendonionmessage2(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params, + bool obs2) { struct onion_hop *hops; struct node_id *first_id; @@ -248,13 +249,29 @@ static struct command_result *json_sendonionmessage(struct command *cmd, "Creating onion failed (tlvs too long?)"); subd_send_msg(cmd->ld->gossip, - take(towire_gossipd_send_onionmsg(NULL, first_id, + take(towire_gossipd_send_onionmsg(NULL, obs2, first_id, serialize_onionpacket(tmpctx, op), blinding))); return command_success(cmd, json_stream_success(cmd)); } +static struct command_result *json_sendonionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj, + const jsmntok_t *params) +{ + return json_sendonionmessage2(cmd, buffer, obj, params, false); +} + +static struct command_result *json_sendobs2onionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj, + const jsmntok_t *params) +{ + return json_sendonionmessage2(cmd, buffer, obj, params, true); +} + static const struct json_command sendonionmessage_command = { "sendonionmessage", "utility", @@ -263,6 +280,14 @@ static const struct json_command sendonionmessage_command = { }; AUTODATA(json_command, &sendonionmessage_command); +static const struct json_command sendobs2onionmessage_command = { + "sendobs2onionmessage", + "utility", + json_sendobs2onionmessage, + "Send obsolete message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)" +}; +AUTODATA(json_command, &sendobs2onionmessage_command); + static struct command_result *param_pubkeys(struct command *cmd, const char *name, const char *buffer, diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 59eb153b6360..8296008729da 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -631,6 +631,7 @@ struct sending { struct sent *sent; const char *msgfield; const u8 *msgval; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; struct command_result *(*done)(struct command *cmd, const char *buf UNUSED, const jsmntok_t *result UNUSED, @@ -638,9 +639,10 @@ struct sending { }; static struct command_result * -send_modern_message(struct command *cmd, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path, - struct sending *sending) +send_obs2_message(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct sending *sending) { struct sent *sent = sending->sent; struct privkey blinding_iter; @@ -695,9 +697,9 @@ send_modern_message(struct command *cmd, payloads[nhops-1]->invoice = cast_const(u8 *, sending->msgval); } - payloads[nhops-1]->reply_path = reply_path; + payloads[nhops-1]->reply_path = sending->obs2_reply_path; - req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", + req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", sending->done, forward_error, sending->sent); @@ -717,6 +719,87 @@ send_modern_message(struct command *cmd, return send_outreq(cmd->plugin, req); } +static struct command_result * +send_modern_message(struct command *cmd, + struct tlv_onionmsg_payload_reply_path *reply_path, + struct sending *sending) +{ + struct sent *sent = sending->sent; + struct privkey blinding_iter; + struct pubkey fwd_blinding, *node_alias; + size_t nhops = tal_count(sent->path); + struct tlv_onionmsg_payload **payloads; + struct out_req *req; + + /* Now create enctlvs for *forward* path. */ + randombytes_buf(&blinding_iter, sizeof(blinding_iter)); + if (!pubkey_from_privkey(&blinding_iter, &fwd_blinding)) + return command_fail(cmd, LIGHTNINGD, + "Could not convert blinding %s to pubkey!", + type_to_string(tmpctx, struct privkey, + &blinding_iter)); + + /* We overallocate: this node (0) doesn't have payload or alias */ + payloads = tal_arr(cmd, struct tlv_onionmsg_payload *, nhops); + node_alias = tal_arr(cmd, struct pubkey, nhops); + + for (size_t i = 1; i < nhops - 1; i++) { + payloads[i] = tlv_onionmsg_payload_new(payloads); + payloads[i]->encrypted_data_tlv = create_enctlv(payloads[i], + &blinding_iter, + &sent->path[i], + &sent->path[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &node_alias[i]); + } + /* Final payload contains the actual data. */ + payloads[nhops-1] = tlv_onionmsg_payload_new(payloads); + + /* We don't include enctlv in final, but it gives us final alias */ + if (!create_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], + /* FIXME: Pad? */ 0, + NULL, + &node_alias[nhops-1])) { + /* Should not happen! */ + return command_fail(cmd, LIGHTNINGD, + "Could create final enctlv"); + } + + /* FIXME: This interface is a string for sendobsonionmessage! */ + if (streq(sending->msgfield, "invoice_request")) { + payloads[nhops-1]->invoice_request + = cast_const(u8 *, sending->msgval); + } else { + assert(streq(sending->msgfield, "invoice")); + payloads[nhops-1]->invoice + = cast_const(u8 *, sending->msgval); + } + payloads[nhops-1]->reply_path = reply_path; + + req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", + /* Try sending older version next */ + send_obs2_message, + forward_error, + sending); + json_add_pubkey(req->js, "first_id", &sent->path[1]); + json_add_pubkey(req->js, "blinding", &fwd_blinding); + json_array_start(req->js, "hops"); + for (size_t i = 1; i < nhops; i++) { + u8 *tlv; + json_object_start(req->js, NULL); + json_add_pubkey(req->js, "id", &node_alias[i]); + tlv = tal_arr(tmpctx, u8, 0); + towire_onionmsg_payload(&tlv, payloads[i]); + json_add_hex_talarr(req->js, "tlv", tlv); + json_object_end(req->js); + } + json_array_end(req->js); + return send_outreq(cmd->plugin, req); +} + /* Lightningd gives us reply path, since we don't know secret to put * in final so it will recognize it. */ static struct command_result *use_reply_path(struct command *cmd, @@ -724,16 +807,25 @@ static struct command_result *use_reply_path(struct command *cmd, const jsmntok_t *result, struct sending *sending) { - struct tlv_obs2_onionmsg_payload_reply_path *rpath; + struct tlv_onionmsg_payload_reply_path *rpath; - rpath = json_to_obs2_reply_path(cmd, buf, - json_get_member(buf, result, "obs2blindedpath")); + rpath = json_to_reply_path(cmd, buf, + json_get_member(buf, result, "blindedpath")); if (!rpath) plugin_err(cmd->plugin, "could not parse reply path %.*s?", json_tok_full_len(result), json_tok_full(buf, result)); + sending->obs2_reply_path = json_to_obs2_reply_path(cmd, buf, + json_get_member(buf, result, + "obs2blindedpath")); + if (!sending->obs2_reply_path) + plugin_err(cmd->plugin, + "could not parse obs2 reply path %.*s?", + json_tok_full_len(result), + json_tok_full(buf, result)); + /* Remember our alias we used so we can recognize reply */ sending->sent->reply_alias = tal_dup(sending->sent, struct pubkey, diff --git a/plugins/offers.c b/plugins/offers.c index 4a9f9888977c..995eb0f716e2 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -41,16 +41,16 @@ static struct command_result *sendonionmessage_error(struct command *cmd, } /* FIXME: replyfield string interface is to accomodate obsolete API */ -struct command_result * -send_onion_reply(struct command *cmd, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path, - const char *replyfield, - const u8 *replydata) +static struct command_result * +send_obs2_onion_reply(struct command *cmd, + struct tlv_obs2_onionmsg_payload_reply_path *reply_path, + const char *replyfield, + const u8 *replydata) { struct out_req *req; size_t nhops = tal_count(reply_path->path); - req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", + req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", finished, sendonionmessage_error, NULL); json_add_pubkey(req->js, "first_id", &reply_path->first_node_id); @@ -84,6 +84,57 @@ send_onion_reply(struct command *cmd, return send_outreq(cmd->plugin, req); } +struct command_result * +send_onion_reply(struct command *cmd, + struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, + const char *replyfield, + const u8 *replydata) +{ + struct out_req *req; + size_t nhops; + + /* Exactly one must be set! */ + assert(!reply_path != !obs2_reply_path); + if (obs2_reply_path) + return send_obs2_onion_reply(cmd, obs2_reply_path, replyfield, replydata); + + req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", + finished, sendonionmessage_error, NULL); + + json_add_pubkey(req->js, "first_id", &reply_path->first_node_id); + json_add_pubkey(req->js, "blinding", &reply_path->blinding); + json_array_start(req->js, "hops"); + + nhops = tal_count(reply_path->path); + for (size_t i = 0; i < nhops; i++) { + struct tlv_onionmsg_payload *omp; + u8 *tlv; + + json_object_start(req->js, NULL); + json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); + + omp = tlv_onionmsg_payload_new(tmpctx); + omp->encrypted_data_tlv = reply_path->path[i]->encrypted_recipient_data; + + /* Put payload in last hop. */ + if (i == nhops - 1) { + if (streq(replyfield, "invoice")) { + omp->invoice = cast_const(u8 *, replydata); + } else { + assert(streq(replyfield, "invoice_error")); + omp->invoice_error = cast_const(u8 *, replydata); + } + } + tlv = tal_arr(tmpctx, u8, 0); + towire_onionmsg_payload(&tlv, omp); + json_add_hex_talarr(req->js, "tlv", tlv); + json_object_end(req->js); + } + json_array_end(req->js); + return send_outreq(cmd->plugin, req); +} + static struct command_result *onion_message_modern_call(struct command *cmd, const char *buf, const jsmntok_t *params) diff --git a/plugins/offers.h b/plugins/offers.h index 00a5480fb77d..68feefa12d69 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -9,7 +9,8 @@ struct command; /* Helper to send a reply */ struct command_result *WARN_UNUSED_RESULT send_onion_reply(struct command *cmd, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path, + struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, const char *replyfield, const u8 *replydata); #endif /* LIGHTNING_PLUGINS_OFFERS_H */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index efbaa6f6fdad..29271827115f 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -11,7 +11,8 @@ struct inv { struct tlv_invoice *inv; /* May be NULL */ - struct tlv_obs2_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; + struct tlv_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ struct tlv_offer *offer; @@ -39,7 +40,7 @@ fail_inv_level(struct command *cmd, plugin_log(cmd->plugin, l, "%s", msg); /* Only reply if they gave us a path */ - if (!inv->reply_path) + if (!inv->reply_path && !inv->obs2_reply_path) return command_hook_success(cmd); /* Don't send back internal error details. */ @@ -53,7 +54,8 @@ fail_inv_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, inv->reply_path, "invoice_error", errdata); + return send_onion_reply(cmd, inv->reply_path, inv->obs2_reply_path, + "invoice_error", errdata); } static struct command_result *WARN_UNUSED_RESULT @@ -322,7 +324,8 @@ struct command_result *handle_invoice(struct command *cmd, int bad_feature; struct sha256 m, shash; - inv->reply_path = tal_steal(inv, reply_path); + inv->obs2_reply_path = tal_steal(inv, reply_path); + inv->reply_path = NULL; inv->inv = tlv_invoice_new(cmd); if (!fromwire_invoice(&invbin, &len, inv->inv)) { diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index ef37f393e0c0..92ba923c3110 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -15,7 +15,8 @@ /* We need to keep the reply path around so we can reply with invoice */ struct invreq { struct tlv_invoice_request *invreq; - struct tlv_obs2_onionmsg_payload_reply_path *reply_path; + struct tlv_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; /* The offer, once we've looked it up. */ struct tlv_offer *offer; @@ -59,7 +60,8 @@ fail_invreq_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, invreq->reply_path, "invoice_error", errdata); + return send_onion_reply(cmd, invreq->reply_path, invreq->obs2_reply_path, + "invoice_error", errdata); } static struct command_result *WARN_UNUSED_RESULT PRINTF_FMT(3,4) @@ -178,7 +180,8 @@ static struct command_result *createinvoice_done(struct command *cmd, json_tok_full(buf, t)); } - return send_onion_reply(cmd, ir->reply_path, "invoice", rawinv); + return send_onion_reply(cmd, ir->reply_path, ir->obs2_reply_path, + "invoice", rawinv); } static struct command_result *createinvoice_error(struct command *cmd, @@ -837,7 +840,8 @@ struct command_result *handle_invoice_request(struct command *cmd, struct out_req *req; int bad_feature; - ir->reply_path = tal_steal(ir, reply_path); + ir->obs2_reply_path = tal_steal(ir, reply_path); + ir->reply_path = NULL; ir->invreq = tlv_invoice_request_new(cmd); if (!fromwire_invoice_request(&invreqbin, &len, ir->invreq)) { From 5361107c9c0554ffe8277f9c115ae7723caf815b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0084/1530] gossipd: handle receipt of modern onion message. And wire it through to the hook; update the plugins to recognize modern vs obs2 onions. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 144 ++++++++++++++++++++++++++++++++++- gossipd/gossipd_wire.csv | 1 + lightningd/onion_message.c | 101 +++++++++++++++++------- plugins/offers.c | 33 +++++--- plugins/offers_inv_hook.c | 7 +- plugins/offers_inv_hook.h | 3 +- plugins/offers_invreq_hook.c | 7 +- plugins/offers_invreq_hook.h | 3 +- 8 files changed, 250 insertions(+), 49 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 3ac14a19f7ab..5491ac0a106d 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -458,6 +458,7 @@ static u8 *handle_obs2_onion_message(struct peer *peer, const u8 *msg) towire_tlvstream_raw(&omsg, om->fields); daemon_conn_send(peer->daemon->master, take(towire_gossipd_got_onionmsg_to_us(NULL, + true, /* obs2 */ &alias, self_id, reply_blinding, first_node_id, @@ -527,7 +528,148 @@ static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, /* Peer sends an onion msg. */ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) { - /* FIXME! */ + enum onion_wire badreason; + struct onionpacket *op; + struct pubkey blinding, ephemeral; + struct route_step *rs; + u8 *onion; + struct tlv_onionmsg_payload *om; + struct secret ss, onion_ss; + const u8 *cursor; + size_t max, maxlen; + + /* Ignore unless explicitly turned on. */ + if (!feature_offered(peer->daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], + OPT_ONION_MESSAGES)) + return NULL; + + /* FIXME: ratelimit! */ + if (!fromwire_onion_message(msg, msg, &blinding, &onion)) + return towire_warningfmt(peer, NULL, "Bad onion_message"); + + /* We unwrap the onion now. */ + op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); + if (!op) { + status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", + onion_wire_name(badreason)); + return NULL; + } + + ephemeral = op->ephemeralkey; + if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { + status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); + return NULL; + } + + /* Now get onion shared secret and parse it. */ + ecdh(&ephemeral, &onion_ss); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + if (!rs) { + status_peer_debug(&peer->id, + "onion msg: can't process onionpacket ss=%s", + type_to_string(tmpctx, struct secret, &onion_ss)); + return NULL; + } + + /* The raw payload is prepended with length in the modern onion. */ + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + if (!cursor) { + status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return NULL; + } + if (maxlen > max) { + status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return NULL; + } + + om = tlv_onionmsg_payload_new(msg); + if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { + status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return NULL; + } + + if (rs->nextcase == ONION_END) { + struct pubkey *reply_blinding, *first_node_id, me, alias; + const struct onionmsg_path **reply_path; + struct secret *self_id; + u8 *omsg; + + if (!pubkey_from_node_id(&me, &peer->daemon->id)) { + status_broken("Failed to convert own id"); + return NULL; + } + + /* Final enctlv is actually optional */ + if (!om->encrypted_data_tlv) { + alias = me; + self_id = NULL; + } else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss, + om->encrypted_data_tlv, &me, &alias, + &self_id)) { + status_peer_debug(&peer->id, + "onion msg: failed to decrypt enctlv" + " %s", tal_hex(tmpctx, om->encrypted_data_tlv)); + return NULL; + } + + if (om->reply_path) { + first_node_id = &om->reply_path->first_node_id; + reply_blinding = &om->reply_path->blinding; + reply_path = cast_const2(const struct onionmsg_path **, + om->reply_path->path); + } else { + first_node_id = NULL; + reply_blinding = NULL; + reply_path = NULL; + } + + /* We re-marshall here by policy, before handing to lightningd */ + omsg = tal_arr(tmpctx, u8, 0); + towire_tlvstream_raw(&omsg, om->fields); + daemon_conn_send(peer->daemon->master, + take(towire_gossipd_got_onionmsg_to_us(NULL, + false, /* !obs2 */ + &alias, self_id, + reply_blinding, + first_node_id, + reply_path, + omsg))); + } else { + struct pubkey next_node, next_blinding; + struct peer *next_peer; + struct node_id next_node_id; + + /* This fails as expected if no enctlv. */ + if (!decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, + &next_blinding)) { + status_peer_debug(&peer->id, + "onion msg: invalid enctlv %s", + tal_hex(tmpctx, om->encrypted_data_tlv)); + return NULL; + } + + /* FIXME: Handle short_channel_id! */ + node_id_from_pubkey(&next_node_id, &next_node); + next_peer = find_peer(peer->daemon, &next_node_id); + if (!next_peer) { + status_peer_debug(&peer->id, + "onion msg: unknown next peer %s", + type_to_string(tmpctx, + struct pubkey, + &next_node)); + return NULL; + } + queue_peer_msg(next_peer, + take(towire_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); + } + return NULL; } diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index ea1986f4cc95..8f49421d0312 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -75,6 +75,7 @@ msgtype,gossipd_new_blockheight,3026 msgdata,gossipd_new_blockheight,blockheight,u32, msgtype,gossipd_got_onionmsg_to_us,3145 +msgdata,gossipd_got_onionmsg_to_us,obs2,bool, msgdata,gossipd_got_onionmsg_to_us,node_alias,pubkey, msgdata,gossipd_got_onionmsg_to_us,self_id,?secret, msgdata,gossipd_got_onionmsg_to_us,reply_blinding,?pubkey, diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 73a088b7411a..5d4a2f6d4d6a 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -21,7 +21,9 @@ struct onion_message_hook_payload { struct pubkey *reply_first_node; struct pubkey *our_alias; - struct tlv_obs2_onionmsg_payload *om; + /* Exactly one of these is set! */ + struct tlv_onionmsg_payload *om; + struct tlv_obs2_onionmsg_payload *obs2_om; }; static void json_add_blindedpath(struct json_stream *stream, @@ -61,28 +63,55 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload, } /* Common convenience fields */ - if (payload->om->invoice_request) - json_add_hex_talarr(stream, "invoice_request", - payload->om->invoice_request); - if (payload->om->invoice) - json_add_hex_talarr(stream, "invoice", payload->om->invoice); - - if (payload->om->invoice_error) - json_add_hex_talarr(stream, "invoice_error", - payload->om->invoice_error); - - json_array_start(stream, "unknown_fields"); - for (size_t i = 0; i < tal_count(payload->om->fields); i++) { - if (payload->om->fields[i].meta) - continue; - json_object_start(stream, NULL); - json_add_u64(stream, "number", payload->om->fields[i].numtype); - json_add_hex(stream, "value", - payload->om->fields[i].value, - payload->om->fields[i].length); - json_object_end(stream); + if (payload->obs2_om) { + json_add_bool(stream, "obs2", true); + if (payload->obs2_om->invoice_request) + json_add_hex_talarr(stream, "invoice_request", + payload->obs2_om->invoice_request); + if (payload->obs2_om->invoice) + json_add_hex_talarr(stream, "invoice", payload->obs2_om->invoice); + + if (payload->obs2_om->invoice_error) + json_add_hex_talarr(stream, "invoice_error", + payload->obs2_om->invoice_error); + + json_array_start(stream, "unknown_fields"); + for (size_t i = 0; i < tal_count(payload->obs2_om->fields); i++) { + if (payload->obs2_om->fields[i].meta) + continue; + json_object_start(stream, NULL); + json_add_u64(stream, "number", payload->obs2_om->fields[i].numtype); + json_add_hex(stream, "value", + payload->obs2_om->fields[i].value, + payload->obs2_om->fields[i].length); + json_object_end(stream); + } + json_array_end(stream); + } else { + json_add_bool(stream, "obs2", false); + if (payload->om->invoice_request) + json_add_hex_talarr(stream, "invoice_request", + payload->om->invoice_request); + if (payload->om->invoice) + json_add_hex_talarr(stream, "invoice", payload->om->invoice); + + if (payload->om->invoice_error) + json_add_hex_talarr(stream, "invoice_error", + payload->om->invoice_error); + + json_array_start(stream, "unknown_fields"); + for (size_t i = 0; i < tal_count(payload->om->fields); i++) { + if (payload->om->fields[i].meta) + continue; + json_object_start(stream, NULL); + json_add_u64(stream, "number", payload->om->fields[i].numtype); + json_add_hex(stream, "value", + payload->om->fields[i].value, + payload->om->fields[i].length); + json_object_end(stream); + } + json_array_end(stream); } - json_array_end(stream); json_object_end(stream); } @@ -113,14 +142,15 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) struct onion_message_hook_payload *payload; u8 *submsg; struct secret *self_id; + bool obs2; size_t submsglen; const u8 *subptr; payload = tal(ld, struct onion_message_hook_payload); - payload->om = tlv_obs2_onionmsg_payload_new(payload); payload->our_alias = tal(payload, struct pubkey); if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg, + &obs2, payload->our_alias, &self_id, &payload->reply_blinding, @@ -139,12 +169,25 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) submsglen = tal_bytelen(submsg); subptr = submsg; - if (!fromwire_obs2_onionmsg_payload(&subptr, - &submsglen, payload->om)) { - tal_free(payload); - log_broken(ld->log, "bad got_onionmsg_tous om: %s", - tal_hex(tmpctx, msg)); - return; + if (obs2) { + payload->om = NULL; + payload->obs2_om = tlv_obs2_onionmsg_payload_new(payload); + if (!fromwire_obs2_onionmsg_payload(&subptr, + &submsglen, payload->obs2_om)) { + tal_free(payload); + log_broken(ld->log, "bad got_onionmsg_tous obs2 om: %s", + tal_hex(tmpctx, msg)); + return; + } + } else { + payload->obs2_om = NULL; + payload->om = tlv_onionmsg_payload_new(payload); + if (!fromwire_onionmsg_payload(&subptr, &submsglen, payload->om)) { + tal_free(payload); + log_broken(ld->log, "bad got_onionmsg_tous om: %s", + tal_hex(tmpctx, msg)); + return; + } } tal_free(submsg); diff --git a/plugins/offers.c b/plugins/offers.c index 995eb0f716e2..a58d1edd06f2 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -140,7 +140,8 @@ static struct command_result *onion_message_modern_call(struct command *cmd, const jsmntok_t *params) { const jsmntok_t *om, *replytok, *invreqtok, *invtok; - struct tlv_obs2_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path = NULL; + struct tlv_onionmsg_payload_reply_path *reply_path = NULL; if (!offers_enabled) return command_hook_success(cmd); @@ -148,21 +149,31 @@ static struct command_result *onion_message_modern_call(struct command *cmd, om = json_get_member(buf, params, "onion_message"); replytok = json_get_member(buf, om, "reply_blindedpath"); if (replytok) { - reply_path = json_to_obs2_reply_path(cmd, buf, replytok); - if (!reply_path) - plugin_err(cmd->plugin, "Invalid reply path %.*s?", - json_tok_full_len(replytok), - json_tok_full(buf, replytok)); - } else - reply_path = NULL; + bool obs2; + json_to_bool(buf, json_get_member(buf, om, "obs2"), &obs2); + if (obs2) { + obs2_reply_path = json_to_obs2_reply_path(cmd, buf, replytok); + if (!obs2_reply_path) + plugin_err(cmd->plugin, "Invalid obs2 reply path %.*s?", + json_tok_full_len(replytok), + json_tok_full(buf, replytok)); + } else { + reply_path = json_to_reply_path(cmd, buf, replytok); + if (!reply_path) + plugin_err(cmd->plugin, "Invalid reply path %.*s?", + json_tok_full_len(replytok), + json_tok_full(buf, replytok)); + } + } invreqtok = json_get_member(buf, om, "invoice_request"); if (invreqtok) { const u8 *invreqbin = json_tok_bin_from_hex(tmpctx, buf, invreqtok); - if (reply_path) + if (reply_path || obs2_reply_path) return handle_invoice_request(cmd, invreqbin, - reply_path); + reply_path, + obs2_reply_path); else plugin_log(cmd->plugin, LOG_DBG, "invoice_request without reply_path"); @@ -172,7 +183,7 @@ static struct command_result *onion_message_modern_call(struct command *cmd, if (invtok) { const u8 *invbin = json_tok_bin_from_hex(tmpctx, buf, invtok); if (invbin) - return handle_invoice(cmd, invbin, reply_path); + return handle_invoice(cmd, invbin, reply_path, obs2_reply_path); } return command_hook_success(cmd); diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index 29271827115f..cd9e00f3ba88 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -315,7 +315,8 @@ static struct command_result *listoffers_error(struct command *cmd, struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS) + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS) { size_t len = tal_count(invbin); struct inv *inv = tal(cmd, struct inv); @@ -324,8 +325,8 @@ struct command_result *handle_invoice(struct command *cmd, int bad_feature; struct sha256 m, shash; - inv->obs2_reply_path = tal_steal(inv, reply_path); - inv->reply_path = NULL; + inv->obs2_reply_path = tal_steal(inv, obs2_reply_path); + inv->reply_path = tal_steal(inv, reply_path); inv->inv = tlv_invoice_new(cmd); if (!fromwire_invoice(&invbin, &len, inv->inv)) { diff --git a/plugins/offers_inv_hook.h b/plugins/offers_inv_hook.h index fae90f98122e..a733d6c4626e 100644 --- a/plugins/offers_inv_hook.h +++ b/plugins/offers_inv_hook.h @@ -6,5 +6,6 @@ /* We got an onionmessage with an invoice! reply_path could be NULL. */ struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS); + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */ diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 92ba923c3110..6c6efabc2493 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -833,15 +833,16 @@ static struct command_result *handle_offerless_request(struct command *cmd, struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path) + struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path) { size_t len = tal_count(invreqbin); struct invreq *ir = tal(cmd, struct invreq); struct out_req *req; int bad_feature; - ir->obs2_reply_path = tal_steal(ir, reply_path); - ir->reply_path = NULL; + ir->obs2_reply_path = tal_steal(ir, obs2_reply_path); + ir->reply_path = tal_steal(ir, reply_path); ir->invreq = tlv_invoice_request_new(cmd); if (!fromwire_invoice_request(&invreqbin, &len, ir->invreq)) { diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index 502679970efc..b33c8d86015e 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -8,5 +8,6 @@ extern u32 cltv_final; /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS); + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */ From 605b3bf98533e2b0a6a34343e3047677a8d99f53 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0085/1530] pytest: re-enable modern/obsolete fetchonion tests. Signed-off-by: Rusty Russell --- lightningd/onion_message.c | 13 ++++++++++--- tests/test_pay.py | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 5d4a2f6d4d6a..9fcf475c5997 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -146,7 +146,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) size_t submsglen; const u8 *subptr; - payload = tal(ld, struct onion_message_hook_payload); + payload = tal(tmpctx, struct onion_message_hook_payload); payload->our_alias = tal(payload, struct pubkey); if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg, @@ -162,6 +162,13 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) return; } +#if DEVELOPER + if (!obs2 && ld->dev_ignore_modern_onion) + return; + if (obs2 && ld->dev_ignore_obsolete_onion) + return; +#endif + /* If there's no self_id, or it's not correct, ignore alias: alias * means we created the path it's using. */ if (!self_id || !secret_eq_consttime(self_id, &ld->onion_reply_secret)) @@ -174,7 +181,6 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) payload->obs2_om = tlv_obs2_onionmsg_payload_new(payload); if (!fromwire_obs2_onionmsg_payload(&subptr, &submsglen, payload->obs2_om)) { - tal_free(payload); log_broken(ld->log, "bad got_onionmsg_tous obs2 om: %s", tal_hex(tmpctx, msg)); return; @@ -183,7 +189,6 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) payload->obs2_om = NULL; payload->om = tlv_onionmsg_payload_new(payload); if (!fromwire_onionmsg_payload(&subptr, &submsglen, payload->om)) { - tal_free(payload); log_broken(ld->log, "bad got_onionmsg_tous om: %s", tal_hex(tmpctx, msg)); return; @@ -203,6 +208,8 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) payload->our_alias ? " via-ourpath": "", payload->reply_path ? " reply_path": ""); + /* We'll free this on return */ + tal_steal(ld, payload); if (payload->our_alias) plugin_hook_call_onion_message_ourpath(ld, payload); else diff --git a/tests/test_pay.py b/tests/test_pay.py index 541a6eafe2f8..2213066b3bb9 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4352,6 +4352,23 @@ def test_fetchinvoice_3hop(node_factory, bitcoind): l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) + # Test with obsolete onion. + l4.stop() + l4.daemon.opts['dev-no-modern-onion'] = None + l4.start() + l4.rpc.connect(l3.info['id'], 'localhost', l3.port) + + l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) + + # Test with modern onion. + l4.stop() + del l4.daemon.opts['dev-no-modern-onion'] + l4.daemon.opts['dev-no-obsolete-onion'] = None + l4.start() + l4.rpc.connect(l3.info['id'], 'localhost', l3.port) + + l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) + def test_fetchinvoice(node_factory, bitcoind): # We remove the conversion plugin on l3, causing it to get upset. From 1c230eee86f5e8640c550b5e6356572db3e66b2a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0086/1530] test: hack in test for (now-obsolete!) test vector Thomas emailed me. Signed-off-by: Rusty Russell --- gossipd/test/Makefile | 15 + gossipd/test/run-check_channel_announcement.c | 24 + gossipd/test/run-check_node_announcement.c | 24 + gossipd/test/run-crc32_of_update.c | 24 + gossipd/test/run-extended-info.c | 24 + gossipd/test/run-next_block_range.c | 24 + gossipd/test/run-onion_message.c | 580 ++++++++++++++++++ gossipd/test/run-txout_failure.c | 24 + 8 files changed, 739 insertions(+) create mode 100644 gossipd/test/run-onion_message.c diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index f4057a4cf222..6c4f150b894f 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -10,18 +10,23 @@ GOSSIPD_TEST_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ common/bigsize.o \ + common/blindedpath.o \ common/channel_id.o \ common/features.o \ + common/hmac.o \ common/node_id.o \ common/json.o \ common/json_helpers.o \ common/lease_rates.o \ + common/onion.o \ common/pseudorand.o \ common/setup.o \ + common/sphinx.o \ common/type_to_string.o \ common/utils.o \ gossipd/gossip_store_wiregen.o \ wire/peer$(EXP)_wiregen.o \ + wire/onion$(EXP)_wiregen.o \ wire/fromwire.o \ wire/tlvstream.o \ wire/towire.o @@ -29,6 +34,16 @@ GOSSIPD_TEST_COMMON_OBJS := \ ALL_C_SOURCES += $(GOSSIPD_TEST_SRC) ALL_TEST_PROGRAMS += $(GOSSIPD_TEST_PROGRAMS) +# Extra stuff needed for onion tests +gossipd/test/run-onion_message: \ + wire/onion$(EXP)_wiregen.o \ + common/blindedpath.o \ + common/blinding.o \ + common/hmac.o \ + common/onion.o \ + common/sphinx.o \ + + $(GOSSIPD_TEST_PROGRAMS): $(GOSSIPD_TEST_COMMON_OBJS) $(BITCOIN_OBJS) # Test objects depend on ../ src and headers. diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 20ef9b41919a..10cdd2e1eeb7 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -29,17 +29,38 @@ In particular, we set feature bit 19. The spec says we should set feature bit 1 #include "../common/wire_error.c" #include "../routing.c" +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for cupdate_different */ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -107,6 +128,9 @@ bool nannounce_different(struct gossip_store *gs UNNEEDED, const u8 *nannounce UNNEEDED, bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 3c637a685eb4..87ff8f188869 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -1,10 +1,31 @@ #include "../gossip_generation.c" +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } @@ -53,6 +74,9 @@ void json_object_end(struct json_stream *js UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 1dbf50df000a..86483e8a26a3 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -3,12 +3,30 @@ int unused_main(int argc, char *argv[]); #include "../queries.c" #include "../gossip_generation.c" #undef main +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for daemon_conn_read_next */ struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, struct daemon_conn *dc UNNEEDED) @@ -27,6 +45,9 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } @@ -86,6 +107,9 @@ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UN /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 935dca7c33e0..bf0d460a82ed 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -4,9 +4,12 @@ #include "../queries.c" #include +#include #include +#include #include #include +#include #include #include @@ -15,6 +18,21 @@ #endif /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for daemon_conn_read_next */ struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, struct daemon_conn *dc UNNEEDED) @@ -33,6 +51,9 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -72,6 +93,9 @@ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UN /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index aad6a39699bd..0be48d45d7eb 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -1,12 +1,33 @@ #include "../seeker.c" #include +#include #include +#include #include +#include #include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -26,6 +47,9 @@ void json_object_end(struct json_stream *js UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c new file mode 100644 index 000000000000..5289233dc4c4 --- /dev/null +++ b/gossipd/test/run-onion_message.c @@ -0,0 +1,580 @@ +int unused_main(int argc, char *argv[]); +#define main unused_main +#include "../gossipd.c" +#undef main +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if DEVELOPER +bool dev_suppress_gossip; + +/* Generated stub for dev_set_max_scids_encode_size */ +struct io_plan *dev_set_max_scids_encode_size(struct io_conn *conn UNNEEDED, + struct daemon *daemon UNNEEDED, + const u8 *msg UNNEEDED) +{ fprintf(stderr, "dev_set_max_scids_encode_size called!\n"); abort(); } +/* Generated stub for dump_memleak */ +bool dump_memleak(struct htable *memtable UNNEEDED, + void (*print)(const char *fmt UNNEEDED, ...)) +{ fprintf(stderr, "dump_memleak called!\n"); abort(); } +/* Generated stub for memleak_status_broken */ +void memleak_status_broken(const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "memleak_status_broken called!\n"); abort(); } +#endif + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for add_to_txout_failures */ +void add_to_txout_failures(struct routing_state *rstate UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "add_to_txout_failures called!\n"); abort(); } +/* Generated stub for daemon_conn_new_ */ +struct daemon_conn *daemon_conn_new_(const tal_t *ctx UNNEEDED, int fd UNNEEDED, + struct io_plan *(*recv)(struct io_conn * UNNEEDED, + const u8 * UNNEEDED, + void *) UNNEEDED, + void (*outq_empty)(void *) UNNEEDED, + void *arg UNNEEDED) +{ fprintf(stderr, "daemon_conn_new_ called!\n"); abort(); } +/* Generated stub for daemon_conn_read_next */ +struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, + struct daemon_conn *dc UNNEEDED) +{ fprintf(stderr, "daemon_conn_read_next called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } +/* Generated stub for daemon_conn_send_fd */ +void daemon_conn_send_fd(struct daemon_conn *dc UNNEEDED, int fd UNNEEDED) +{ fprintf(stderr, "daemon_conn_send_fd called!\n"); abort(); } +/* Generated stub for daemon_shutdown */ +void daemon_shutdown(void) +{ fprintf(stderr, "daemon_shutdown called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } +/* Generated stub for ecdh_hsmd_setup */ +void ecdh_hsmd_setup(int hsm_fd UNNEEDED, + void (*failed)(enum status_failreason UNNEEDED, + const char *fmt UNNEEDED, ...)) +{ fprintf(stderr, "ecdh_hsmd_setup called!\n"); abort(); } +/* Generated stub for first_chan */ +struct chan *first_chan(const struct node *node UNNEEDED, struct chan_map_iter *i UNNEEDED) +{ fprintf(stderr, "first_chan called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for free_chan */ +void free_chan(struct routing_state *rstate UNNEEDED, struct chan *chan UNNEEDED) +{ fprintf(stderr, "free_chan called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_addgossip */ +bool fromwire_gossipd_addgossip(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **msg UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_addgossip called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_dev_set_time */ +bool fromwire_gossipd_dev_set_time(const void *p UNNEEDED, u32 *dev_gossip_time UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_dev_set_time called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_dev_suppress */ +bool fromwire_gossipd_dev_suppress(const void *p UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_dev_suppress called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_addrs */ +bool fromwire_gossipd_get_addrs(const void *p UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_addrs called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_stripped_cupdate */ +bool fromwire_gossipd_get_stripped_cupdate(const void *p UNNEEDED, struct short_channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_stripped_cupdate called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_txout_reply */ +bool fromwire_gossipd_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct amount_sat *satoshis UNNEEDED, u8 **outscript UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_txout_reply called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_update */ +bool fromwire_gossipd_get_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_update called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_init */ +bool fromwire_gossipd_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct feature_set **our_features UNNEEDED, struct node_id *id UNNEEDED, u8 rgb[3] UNNEEDED, u8 alias[32] UNNEEDED, struct wireaddr **announcable UNNEEDED, u32 **dev_gossip_time UNNEEDED, bool *dev_fast_gossip UNNEEDED, bool *dev_fast_gossip_prune UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_init called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_local_channel_announcement */ +bool fromwire_gossipd_local_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **cannount UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_local_channel_announcement called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_local_channel_close */ +bool fromwire_gossipd_local_channel_close(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_local_channel_close called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_new_blockheight */ +bool fromwire_gossipd_new_blockheight(const void *p UNNEEDED, u32 *blockheight UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_new_blockheight called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_new_lease_rates */ +bool fromwire_gossipd_new_lease_rates(const void *p UNNEEDED, struct lease_rates *rates UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_new_lease_rates called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_new_peer */ +bool fromwire_gossipd_new_peer(const void *p UNNEEDED, struct node_id *id UNNEEDED, bool *gossip_queries_feature UNNEEDED, bool *initial_routing_sync UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_new_peer called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_outpoint_spent */ +bool fromwire_gossipd_outpoint_spent(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_outpoint_spent called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_send_onionmsg */ +bool fromwire_gossipd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *obs2 UNNEEDED, struct node_id *id UNNEEDED, u8 **onion UNNEEDED, struct pubkey *blinding UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_send_onionmsg called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } +/* Generated stub for get_node */ +struct node *get_node(struct routing_state *rstate UNNEEDED, + const struct node_id *id UNNEEDED) +{ fprintf(stderr, "get_node called!\n"); abort(); } +/* Generated stub for gossip_store_compact */ +bool gossip_store_compact(struct gossip_store *gs UNNEEDED) +{ fprintf(stderr, "gossip_store_compact called!\n"); abort(); } +/* Generated stub for gossip_store_get */ +const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, + struct gossip_store *gs UNNEEDED, + u64 offset UNNEEDED) +{ fprintf(stderr, "gossip_store_get called!\n"); abort(); } +/* Generated stub for gossip_store_load */ +u32 gossip_store_load(struct routing_state *rstate UNNEEDED, struct gossip_store *gs UNNEEDED) +{ fprintf(stderr, "gossip_store_load called!\n"); abort(); } +/* Generated stub for gossip_store_readonly_fd */ +int gossip_store_readonly_fd(struct gossip_store *gs UNNEEDED) +{ fprintf(stderr, "gossip_store_readonly_fd called!\n"); abort(); } +/* Generated stub for gossip_time_now */ +struct timeabs gossip_time_now(const struct routing_state *rstate UNNEEDED) +{ fprintf(stderr, "gossip_time_now called!\n"); abort(); } +/* Generated stub for gossipd_peerd_wire_name */ +const char *gossipd_peerd_wire_name(int e UNNEEDED) +{ fprintf(stderr, "gossipd_peerd_wire_name called!\n"); abort(); } +/* Generated stub for handle_channel_announcement */ +u8 *handle_channel_announcement(struct routing_state *rstate UNNEEDED, + const u8 *announce TAKES UNNEEDED, + u32 current_blockheight UNNEEDED, + const struct short_channel_id **scid UNNEEDED, + struct peer *peer UNNEEDED) +{ fprintf(stderr, "handle_channel_announcement called!\n"); abort(); } +/* Generated stub for handle_channel_update */ +u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *update TAKES UNNEEDED, + struct peer *peer UNNEEDED, + struct short_channel_id *unknown_scid UNNEEDED, + bool force UNNEEDED) +{ fprintf(stderr, "handle_channel_update called!\n"); abort(); } +/* Generated stub for handle_local_channel_update */ +bool handle_local_channel_update(struct daemon *daemon UNNEEDED, + const struct node_id *src UNNEEDED, + const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_local_channel_update called!\n"); abort(); } +/* Generated stub for handle_node_announcement */ +u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, + struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED) +{ fprintf(stderr, "handle_node_announcement called!\n"); abort(); } +/* Generated stub for handle_pending_cannouncement */ +bool handle_pending_cannouncement(struct daemon *daemon UNNEEDED, + struct routing_state *rstate UNNEEDED, + const struct short_channel_id *scid UNNEEDED, + const struct amount_sat sat UNNEEDED, + const u8 *txscript UNNEEDED) +{ fprintf(stderr, "handle_pending_cannouncement called!\n"); abort(); } +/* Generated stub for handle_query_channel_range */ +const u8 *handle_query_channel_range(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_query_channel_range called!\n"); abort(); } +/* Generated stub for handle_query_short_channel_ids */ +const u8 *handle_query_short_channel_ids(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_query_short_channel_ids called!\n"); abort(); } +/* Generated stub for handle_reply_channel_range */ +const u8 *handle_reply_channel_range(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_reply_channel_range called!\n"); abort(); } +/* Generated stub for handle_reply_short_channel_ids_end */ +const u8 *handle_reply_short_channel_ids_end(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_reply_short_channel_ids_end called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for master_badmsg */ +void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) +{ fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for maybe_send_own_node_announce */ +void maybe_send_own_node_announce(struct daemon *daemon UNNEEDED, bool startup UNNEEDED) +{ fprintf(stderr, "maybe_send_own_node_announce called!\n"); abort(); } +/* Generated stub for maybe_send_query_responses */ +void maybe_send_query_responses(struct peer *peer UNNEEDED) +{ fprintf(stderr, "maybe_send_query_responses called!\n"); abort(); } +/* Generated stub for memleak_find_allocations */ +struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, + const void *exclude1 UNNEEDED, + const void *exclude2 UNNEEDED) +{ fprintf(stderr, "memleak_find_allocations called!\n"); abort(); } +/* Generated stub for memleak_remove_region */ +void memleak_remove_region(struct htable *memtable UNNEEDED, + const void *p UNNEEDED, size_t bytelen UNNEEDED) +{ fprintf(stderr, "memleak_remove_region called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } +/* Generated stub for new_reltimer_ */ +struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, + const tal_t *ctx UNNEEDED, + struct timerel expire UNNEEDED, + void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) +{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); } +/* Generated stub for new_routing_state */ +struct routing_state *new_routing_state(const tal_t *ctx UNNEEDED, + const struct node_id *local_id UNNEEDED, + struct list_head *peers UNNEEDED, + struct timers *timers UNNEEDED, + const u32 *dev_gossip_time TAKES UNNEEDED, + bool dev_fast_gossip UNNEEDED, + bool dev_fast_gossip_prune UNNEEDED) +{ fprintf(stderr, "new_routing_state called!\n"); abort(); } +/* Generated stub for new_seeker */ +struct seeker *new_seeker(struct daemon *daemon UNNEEDED) +{ fprintf(stderr, "new_seeker called!\n"); abort(); } +/* Generated stub for next_chan */ +struct chan *next_chan(const struct node *node UNNEEDED, struct chan_map_iter *i UNNEEDED) +{ fprintf(stderr, "next_chan called!\n"); abort(); } +/* Generated stub for notleak_ */ +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) +{ fprintf(stderr, "notleak_ called!\n"); abort(); } +/* Generated stub for query_unknown_channel */ +void query_unknown_channel(struct daemon *daemon UNNEEDED, + struct peer *peer UNNEEDED, + const struct short_channel_id *id UNNEEDED) +{ fprintf(stderr, "query_unknown_channel called!\n"); abort(); } +/* Generated stub for query_unknown_node */ +void query_unknown_node(struct seeker *seeker UNNEEDED, struct peer *peer UNNEEDED) +{ fprintf(stderr, "query_unknown_node called!\n"); abort(); } +/* Generated stub for refresh_local_channel */ +void refresh_local_channel(struct daemon *daemon UNNEEDED, + struct local_chan *local_chan UNNEEDED, + bool even_if_identical UNNEEDED) +{ fprintf(stderr, "refresh_local_channel called!\n"); abort(); } +/* Generated stub for remove_channel_from_store */ +void remove_channel_from_store(struct routing_state *rstate UNNEEDED, + struct chan *chan UNNEEDED) +{ fprintf(stderr, "remove_channel_from_store called!\n"); abort(); } +/* Generated stub for remove_unknown_scid */ +bool remove_unknown_scid(struct seeker *seeker UNNEEDED, + const struct short_channel_id *scid UNNEEDED, + bool found UNNEEDED) +{ fprintf(stderr, "remove_unknown_scid called!\n"); abort(); } +/* Generated stub for route_prune */ +void route_prune(struct routing_state *rstate UNNEEDED) +{ fprintf(stderr, "route_prune called!\n"); abort(); } +/* Generated stub for routing_add_private_channel */ +bool routing_add_private_channel(struct routing_state *rstate UNNEEDED, + const struct peer *peer UNNEEDED, + const u8 *msg UNNEEDED, u64 index UNNEEDED) +{ fprintf(stderr, "routing_add_private_channel called!\n"); abort(); } +/* Generated stub for sanitize_error */ +char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "sanitize_error called!\n"); abort(); } +/* Generated stub for seeker_setup_peer_gossip */ +void seeker_setup_peer_gossip(struct seeker *seeker UNNEEDED, struct peer *peer UNNEEDED) +{ fprintf(stderr, "seeker_setup_peer_gossip called!\n"); abort(); } +/* Generated stub for status_failed */ +void status_failed(enum status_failreason code UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "status_failed called!\n"); abort(); } +/* Generated stub for status_fmt */ +void status_fmt(enum log_level level UNNEEDED, + const struct node_id *peer UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for status_setup_async */ +void status_setup_async(struct daemon_conn *master UNNEEDED) +{ fprintf(stderr, "status_setup_async called!\n"); abort(); } +/* Generated stub for subdaemon_setup */ +void subdaemon_setup(int argc UNNEEDED, char *argv[]) +{ fprintf(stderr, "subdaemon_setup called!\n"); abort(); } +/* Generated stub for timer_expired */ +void timer_expired(tal_t *ctx UNNEEDED, struct timer *timer UNNEEDED) +{ fprintf(stderr, "timer_expired called!\n"); abort(); } +/* Generated stub for towire_gossipd_addgossip_reply */ +u8 *towire_gossipd_addgossip_reply(const tal_t *ctx UNNEEDED, const wirestring *err UNNEEDED) +{ fprintf(stderr, "towire_gossipd_addgossip_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_dev_compact_store_reply */ +u8 *towire_gossipd_dev_compact_store_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED) +{ fprintf(stderr, "towire_gossipd_dev_compact_store_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_dev_memleak_reply */ +u8 *towire_gossipd_dev_memleak_reply(const tal_t *ctx UNNEEDED, bool leak UNNEEDED) +{ fprintf(stderr, "towire_gossipd_dev_memleak_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_addrs_reply */ +u8 *towire_gossipd_get_addrs_reply(const tal_t *ctx UNNEEDED, const struct wireaddr *addrs UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_addrs_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_stripped_cupdate_reply */ +u8 *towire_gossipd_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const u8 *stripped_update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_stripped_cupdate_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_txout */ +u8 *towire_gossipd_get_txout(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_txout called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_update_reply */ +u8 *towire_gossipd_get_update_reply(const tal_t *ctx UNNEEDED, const u8 *update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_update_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_got_onionmsg_to_us */ +u8 *towire_gossipd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEEDED, const struct pubkey *node_alias UNNEEDED, const struct secret *self_id UNNEEDED, const struct pubkey *reply_blinding UNNEEDED, const struct pubkey *reply_first_node UNNEEDED, const struct onionmsg_path **reply_path UNNEEDED, const u8 *rawmsg UNNEEDED) +{ fprintf(stderr, "towire_gossipd_got_onionmsg_to_us called!\n"); abort(); } +/* Generated stub for towire_gossipd_init_reply */ +u8 *towire_gossipd_init_reply(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "towire_gossipd_init_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_new_peer_reply */ +u8 *towire_gossipd_new_peer_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED, const struct gossip_state *gs UNNEEDED) +{ fprintf(stderr, "towire_gossipd_new_peer_reply called!\n"); abort(); } +/* Generated stub for towire_warningfmt */ +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(); } +/* AUTOGENERATED MOCKS END */ + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey->secret.data, NULL, NULL) != 1) + abort(); +} + +static void json_strfield(const char *name, const char *val) +{ + printf("\t\"%s\": \"%s\",\n", name, val); +} + +static void json_onionmsg_payload(const struct tlv_obs2_onionmsg_payload *om) +{ + if (om->reply_path) { + printf("\t\"reply_path\": {\n"); + json_strfield("first_node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->first_node_id)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->blinding)); + printf("\t\"path\": [\n"); + for (size_t i = 0; i < tal_count(om->reply_path->path); i++) { + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->path[i]->node_id)); + json_strfield("encrypted_recipient_data", + tal_hex(tmpctx, + om->reply_path->path[i]->encrypted_recipient_data)); + } + printf("]}\n"); + } + if (om->invoice) + json_strfield("invoice", tal_hex(tmpctx, om->invoice)); + if (om->invoice_request) + json_strfield("invoice_request", + tal_hex(tmpctx, om->invoice_request)); + if (om->invoice_error) + json_strfield("invoice_error", + tal_hex(tmpctx, om->invoice_error)); +} + +/* Return next onion (and updates blinding), or NULL */ +static u8 *json_test(const char *testname, + const u8 *data, + const struct privkey *me, + const struct privkey *blinding_priv, + struct pubkey *blinding) +{ + struct pubkey my_id, next_node; + struct secret ss, onion_ss; + struct pubkey ephemeral; + struct route_step *rs; + const u8 *cursor; + size_t max, maxlen; + struct onionpacket *op; + struct tlv_obs2_onionmsg_payload *om; + + op = parse_onionpacket(tmpctx, data, tal_bytelen(data), NULL); + assert(op); + + pubkey_from_privkey(me, &my_id); + printf("{"); + json_strfield("test name", testname); + json_strfield("reader_privkey", + type_to_string(tmpctx, struct privkey, me)); + json_strfield("reader_id", + type_to_string(tmpctx, struct pubkey, &my_id)); + + if (blinding_priv) + json_strfield("blinding_privkey", + type_to_string(tmpctx, struct privkey, + blinding_priv)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, blinding)); + printf("\"onionmsg\": {\n"); + json_strfield("raw", tal_hex(tmpctx, data)); + json_strfield("version", tal_fmt(tmpctx, "%i", op->version)); + json_strfield("public_key", + type_to_string(tmpctx, struct pubkey, &op->ephemeralkey)); + json_strfield("hop_payloads", + tal_hex(tmpctx, op->routinginfo)); + json_strfield("hmac", + tal_hexstr(tmpctx, &op->hmac, sizeof(op->hmac))); + printf("},\n"); + + ephemeral = op->ephemeralkey; + + /* Set this for test_ecdh */ + mykey = me; + assert(unblind_onion(blinding, test_ecdh, &ephemeral, &ss)); + json_strfield("ECDH shared secret", + type_to_string(tmpctx, struct secret, &ss)); + /* Reproduce internal calc from unblind_onion */ + { + struct secret hmac; + subkey_from_hmac("blinded_node_id", &ss, &hmac); + json_strfield("HMAC256(\\\"blinded_node_id\\\", ss(i)) * k(i)", + type_to_string(tmpctx, struct secret, &hmac)); + } + json_strfield("Tweaked onion pubkey", + type_to_string(tmpctx, struct pubkey, &ephemeral)); + + /* Now get onion shared secret and parse it. */ + test_ecdh(&ephemeral, &onion_ss); + json_strfield("onion shared secret", + type_to_string(tmpctx, struct secret, &onion_ss)); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + assert(rs); + + printf("\"onion contents\": {\n"); + json_strfield("raw", tal_hex(tmpctx, rs->raw_payload)); + + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + json_strfield("length", tal_fmt(tmpctx, "%zu", maxlen)); + json_strfield("rawtlv", tal_hexstr(tmpctx, cursor, maxlen)); + json_strfield("hmac", tal_hexstr(tmpctx, rs->next->hmac.bytes, + sizeof(rs->next->hmac.bytes))); + om = tlv_obs2_onionmsg_payload_new(tmpctx); + assert(fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)); + + json_onionmsg_payload(om); + + /* We expect one of these. */ + assert(om->enctlv); + + printf("\t\"encrypted_data_tlv\": {\n"); + json_strfield("raw", tal_hex(tmpctx, om->enctlv)); + + if (rs->nextcase == ONION_END) { + struct secret *self_id; + struct pubkey alias; + assert(decrypt_obs2_final_enctlv(tmpctx, + blinding, &ss, + om->enctlv, + &my_id, &alias, &self_id)); + if (self_id) { + json_strfield("self_id", + type_to_string(tmpctx, struct secret, + self_id)); + } + printf("}\n"); + return NULL; + } else { + assert(decrypt_obs2_enctlv(blinding, &ss, om->enctlv, &next_node, + blinding)); + json_strfield("next_node", + type_to_string(tmpctx, struct pubkey, &next_node)); + json_strfield("next_blinding", + type_to_string(tmpctx, struct pubkey, + blinding)); + printf("}"); + printf("},\n"); + return serialize_onionpacket(tmpctx, rs->next); + } +} + +int main(int argc, char *argv[]) +{ + struct onionpacket *op; + u8 *data; + struct privkey alice, bob, carol, dave, blinding_priv; + struct pubkey alice_id, bob_id, carol_id, dave_id; + struct pubkey blinding; + + common_setup(argv[0]); + + memset(&alice, 'A', sizeof(alice)); + memset(&bob, 'B', sizeof(bob)); + memset(&carol, 'C', sizeof(carol)); + memset(&dave, 'D', sizeof(dave)); + pubkey_from_privkey(&alice, &alice_id); + pubkey_from_privkey(&bob, &bob_id); + pubkey_from_privkey(&carol, &carol_id); + pubkey_from_privkey(&dave, &dave_id); + + /* ThomasH sends via email: + * + * { + * "version":0, + * "public_key": + * "0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", + * "hop_payloads": + * "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + * "hmac": "564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac" + * } + */ + op = tal(tmpctx, struct onionpacket); + op->version = 0; + assert(pubkey_from_hexstr("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", strlen("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967"), &op->ephemeralkey)); + assert(hex_decode("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac", + strlen("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac"), + &op->hmac, sizeof(op->hmac))); + op->routinginfo = tal_hexdata(op, "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + strlen("37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a")); + + data = serialize_onionpacket(tmpctx, op); + printf("[\n"); + + memset(&blinding_priv, 5, sizeof(blinding_priv)); + pubkey_from_privkey(&blinding_priv, &blinding); + + data = json_test("onion message for Alice", + data, + &alice, + &blinding_priv, + &blinding); + + data = json_test("onion message for Bob", + data, + &bob, + NULL, + &blinding); + + data = json_test("onion message for Carol", + data, + &carol, + NULL, + &blinding); + + data = json_test("onion message for Dave", + data, + &dave, + NULL, + &blinding); + + assert(!data); + printf("]\n"); + + common_shutdown(); + return 0; +} diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index e07e6d461430..29685664f15d 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -1,16 +1,37 @@ #include "../routing.c" #include "../common/timeout.c" +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for cupdate_different */ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -74,6 +95,9 @@ bool nannounce_different(struct gossip_store *gs UNNEEDED, const u8 *nannounce UNNEEDED, bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } From e02e0a197dd9e74fc5053351b7c8fbbf0e159c8e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0087/1530] common/test/run-blindedpath_enctlv: use modern functions to create test vector. And include the final destination enctlv. Signed-off-by: Rusty Russell --- common/test/run-blindedpath_enctlv.c | 47 +++++++++++++++++++--------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index 477220765fd6..221e0fa89a20 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -84,7 +84,7 @@ static void test_decrypt(const struct pubkey *blinding, mykey = me; assert(unblind_onion(blinding, test_ecdh, &dummy, &ss)); - assert(decrypt_obs2_enctlv(blinding, &ss, enctlv, &next_node, &next_blinding)); + assert(decrypt_enctlv(blinding, &ss, enctlv, &next_node, &next_blinding)); pubkey_from_privkey(expected_next_blinding_priv, &expected_next_blinding); assert(pubkey_eq(&next_blinding, &expected_next_blinding)); @@ -106,7 +106,7 @@ static void test_final_decrypt(const struct pubkey *blinding, mykey = me; pubkey_from_privkey(me, &my_pubkey); assert(unblind_onion(blinding, test_ecdh, &dummy, &ss)); - assert(decrypt_obs2_final_enctlv(tmpctx, blinding, &ss, enctlv, &my_pubkey, + assert(decrypt_final_enctlv(tmpctx, blinding, &ss, enctlv, &my_pubkey, &alias, &self_id)); assert(pubkey_eq(&alias, expected_alias)); @@ -150,8 +150,8 @@ int main(int argc, char *argv[]) "\t},\n", type_to_string(tmpctx, struct pubkey, &bob_id)); - enctlv = create_obs2_enctlv(tmpctx, &blinding, &alice_id, &bob_id, - 0, NULL, &blinding, &alias); + enctlv = create_enctlv(tmpctx, &blinding, &alice_id, &bob_id, + 0, NULL, &blinding, &alias); printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -180,8 +180,8 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct pubkey, &carol_id), type_to_string(tmpctx, struct privkey, &override_blinding)); - enctlv = create_obs2_enctlv(tmpctx, &blinding, &bob_id, &carol_id, - 0, &override_blinding_pub, &blinding, &alias); + enctlv = create_enctlv(tmpctx, &blinding, &bob_id, &carol_id, + 0, &override_blinding_pub, &blinding, &alias); printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -209,21 +209,40 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct pubkey, &dave_id), tal_hex(tmpctx, tal_arrz(tmpctx, u8, 35))); - enctlv = create_obs2_enctlv(tmpctx, &blinding, &carol_id, &dave_id, - 35, NULL, &blinding, &alias); + enctlv = create_enctlv(tmpctx, &blinding, &carol_id, &dave_id, + 35, NULL, &blinding, &alias); printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" - "}]\n", + "},\n", tal_hex(tmpctx, enctlv)); test_decrypt(&blinding_pub, enctlv, &carol, &dave_id, &blinding); - /* FIXME: Add this to the test vectors! */ - fclose(stdout); - memset(&self_id, 0x77, sizeof(self_id)); - enctlv = create_obs2_final_enctlv(tmpctx, &blinding, &dave_id, - 0, &self_id, &alias); + for (size_t i = 0; i < sizeof(self_id); i++) + self_id.data[i] = i+1; + printf("{"); + json_strfield("test name", "Final enctlv for Dave"); + json_strfield("node_privkey", + type_to_string(tmpctx, struct privkey, &dave)); + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, &dave_id)); + json_strfield("blinding_secret", + type_to_string(tmpctx, struct privkey, &blinding)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, &blinding_pub)); + printf("\t\"encrypted_data_tlv\": {\n" + "\t\t\"self_id\": \"%s\"\n" + "\t},\n", + type_to_string(tmpctx, struct secret, &self_id)); + + enctlv = create_final_enctlv(tmpctx, &blinding, &dave_id, + 0, &self_id, &alias); + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n", + tal_hex(tmpctx, enctlv)); + + printf("}]\n"); pubkey_from_privkey(&blinding, &blinding_pub); + test_final_decrypt(&blinding_pub, enctlv, &dave, &alias, &self_id); common_shutdown(); } From 4c63fedce94380843f42a471fe9894e65cade0e3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0088/1530] test: make an onionmessage test vector. This builds on the enctlv vectors, but actually goes all the way to creating a modern onionmessage. Thanks to Thomas H for corrections! Signed-off-by: Rusty Russell --- common/test/Makefile | 2 +- common/test/run-blindedpath_enctlv.c | 14 ++ common/test/run-blindedpath_onion.c | 254 +++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 1 deletion(-) create mode 100644 common/test/run-blindedpath_onion.c diff --git a/common/test/Makefile b/common/test/Makefile index e6644f3744ef..d46e49010686 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -20,7 +20,7 @@ ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) # Sphinx test wants to decode TLVs. common/test/run-sphinx: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o -common/test/run-blindedpath_enctlv: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o +common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/test/run-param \ common/test/run-json: \ diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index 221e0fa89a20..f0e0a36d902e 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -2,6 +2,7 @@ #include "../blinding.c" #include "../hmac.c" #include "../type_to_string.c" +#include #include #include @@ -47,12 +48,25 @@ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max /* Generated stub for fromwire_amount_sat */ struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } /* Generated stub for towire_amount_msat */ void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) { fprintf(stderr, "towire_amount_msat called!\n"); abort(); } /* Generated stub for towire_amount_sat */ void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) { fprintf(stderr, "towire_amount_sat called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static void json_strfield(const char *name, const char *val) diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c new file mode 100644 index 000000000000..f20058b8183a --- /dev/null +++ b/common/test/run-blindedpath_onion.c @@ -0,0 +1,254 @@ +/* Pipe through jq for test vector! */ +#include "../bigsize.c" +#include "../blindedpath.c" +#include "../blinding.c" +#include "../hmac.c" +#include "../onion.c" +#include "../sphinx.c" +#include "../type_to_string.c" +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_msat_eq */ +bool amount_msat_eq(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED) +{ fprintf(stderr, "amount_msat_eq called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } +/* Generated stub for pubkey_from_node_id */ +bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static void json_strfield(const char *name, const char *val) +{ + printf("\t\"%s\": \"%s\",\n", name, val); +} + +#define ALICE 0 +#define BOB 1 +#define CAROL 2 +#define DAVE 3 + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *ecdh_key; + +void ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + ecdh_key->secret.data, NULL, NULL) != 1) + abort(); +} + +static u8 *next_onion(const tal_t *ctx, u8 *omsg, + const struct privkey *nodekey, + const struct pubkey *expected_blinding) +{ + struct onionpacket *op; + struct pubkey blinding, ephemeral; + struct pubkey next_node, next_blinding; + struct tlv_onionmsg_payload *om; + struct secret ss, onion_ss; + const u8 *cursor; + size_t max, maxlen; + struct route_step *rs; + + assert(fromwire_onion_message(tmpctx, omsg, &blinding, &omsg)); + assert(pubkey_eq(&blinding, expected_blinding)); + + op = parse_onionpacket(tmpctx, omsg, tal_bytelen(omsg), NULL); + ephemeral = op->ephemeralkey; + + ecdh_key = nodekey; + assert(unblind_onion(&blinding, ecdh, &ephemeral, &ss)); + + ecdh(&ephemeral, &onion_ss); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + assert(rs); + + /* The raw payload is prepended with length in the modern onion. */ + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + om = tlv_onionmsg_payload_new(tmpctx); + assert(fromwire_onionmsg_payload(&cursor, &maxlen, om)); + + if (rs->nextcase == ONION_END) + return NULL; + + assert(decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, + &next_blinding)); + return towire_onion_message(ctx, &next_blinding, + serialize_onionpacket(tmpctx, rs->next)); +} + +int main(int argc, char *argv[]) +{ + struct privkey nodekey[4], blinding[4], override_blinding; + struct pubkey id[4], blinding_pub[4], override_blinding_pub, alias[4]; + struct secret self_id; + u8 *enctlv[4]; + u8 *onionmsg_payload[4]; + u8 *omsg; + struct sphinx_path *sphinx_path; + struct secret *path_secrets; + struct onionpacket *op; + const char *names[] = {"Alice", "Bob", "Carol", "Dave"}; + + common_setup(argv[0]); + + for (size_t i = 0; i < 4; i++) { + memset(&nodekey[i], names[i][0], sizeof(nodekey[i])); + pubkey_from_privkey(&nodekey[i], &id[i]); + } + + /* Make enctlvs as per enctlv test vectors */ + memset(&blinding[ALICE], 5, sizeof(blinding[ALICE])); + pubkey_from_privkey(&blinding[ALICE], &blinding_pub[ALICE]); + + enctlv[ALICE] = create_enctlv(tmpctx, &blinding[ALICE], + &id[ALICE], &id[BOB], + 0, NULL, &blinding[BOB], &alias[ALICE]); + + pubkey_from_privkey(&blinding[BOB], &blinding_pub[BOB]); + + /* We override blinding for Carol. */ + memset(&override_blinding, 7, sizeof(override_blinding)); + pubkey_from_privkey(&override_blinding, &override_blinding_pub); + enctlv[BOB] = create_enctlv(tmpctx, &blinding[BOB], + &id[BOB], &id[CAROL], + 0, &override_blinding_pub, + &blinding[CAROL], &alias[BOB]); + + /* That replaced the blinding */ + blinding[CAROL] = override_blinding; + blinding_pub[CAROL] = override_blinding_pub; + + enctlv[CAROL] = create_enctlv(tmpctx, &blinding[CAROL], + &id[CAROL], &id[DAVE], + 35, NULL, &blinding[DAVE], &alias[CAROL]); + + for (size_t i = 0; i < sizeof(self_id); i++) + self_id.data[i] = i+1; + + enctlv[DAVE] = create_final_enctlv(tmpctx, &blinding[DAVE], &id[DAVE], + 0, &self_id, &alias[DAVE]); + pubkey_from_privkey(&blinding[DAVE], &blinding_pub[DAVE]); + + /* Create an onion which encodes this. */ + sphinx_path = sphinx_path_new(tmpctx, NULL); + for (size_t i = 0; i < 4; i++) { + struct tlv_onionmsg_payload *payload + = tlv_onionmsg_payload_new(tmpctx); + payload->encrypted_data_tlv = enctlv[i]; + onionmsg_payload[i] = tal_arr(tmpctx, u8, 0); + towire_onionmsg_payload(&onionmsg_payload[i], payload); + sphinx_add_modern_hop(sphinx_path, &alias[i], + onionmsg_payload[i]); + } + op = create_onionpacket(tmpctx, sphinx_path, ROUTING_INFO_SIZE, + &path_secrets); + + /* And finally, the onion message as handed to Alice */ + omsg = towire_onion_message(tmpctx, &blinding_pub[ALICE], + serialize_onionpacket(tmpctx, op)); + + printf("{\n"); + json_strfield("description", + "Onion message encoding enctlv as per enctlvs.json"); + printf("\t\"onionmsgs\": ["); + for (size_t i = 0; i < 4; i++) { + printf("{\n"); + json_strfield("node name", names[i]); + json_strfield("node_secret", + type_to_string(tmpctx, struct privkey, + &nodekey[i])); + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, &id[i])); + + printf("\t\"onion_message\": {"); + json_strfield("raw", tal_hex(tmpctx, omsg)); + json_strfield("blinding_secret", + type_to_string(tmpctx, struct privkey, + &blinding[i])); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, &blinding_pub[i])); + json_strfield("blinded_alias", + type_to_string(tmpctx, struct pubkey, &alias[i])); + json_strfield("onionmsg_payload", + tal_hex(tmpctx, onionmsg_payload[i])); + printf("\"enctlv\": \"%s\"}\n", tal_hex(tmpctx, enctlv[i])); + + printf("}"); + if (i != DAVE) + printf(",\n"); + + /* Unwrap for next hop */ + omsg = next_onion(tmpctx, omsg, &nodekey[i], &blinding_pub[i]); + } + assert(!omsg); + printf("\n]}\n"); + + common_shutdown(); +} From 894f1841b44c567d0232bf8b8df7867b03ddf9e9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0089/1530] features: don't announce onion_messages in bolt11 invoices. As noted by Joost. Signed-off-by: Rusty Russell --- common/features.c | 2 +- common/features.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/features.c b/common/features.c index c91ab95039e1..f84375989990 100644 --- a/common/features.c +++ b/common/features.c @@ -69,7 +69,7 @@ static const struct feature_style feature_styles[] = { { OPT_ONION_MESSAGES, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, - [BOLT11_FEATURE] = FEATURE_REPRESENT, + [BOLT11_FEATURE] = FEATURE_DONT_REPRESENT, [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} }, { OPT_SHUTDOWN_WRONG_FUNDING, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, diff --git a/common/features.h b/common/features.h index 4bb2a78ec5b1..d41c81320eaa 100644 --- a/common/features.h +++ b/common/features.h @@ -137,7 +137,7 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); /* BOLT-1ede04a1a3225581e265b3ce96984ba88253a4a4 #9: * - * | 38/39 | `option_onion_messages` |... INC+ ... + * | 38/39 | `option_onion_messages` |... IN ... */ #define OPT_ONION_MESSAGES 38 From 163d3a16f4861315b5c5134d491e428fae4edba1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0090/1530] doc: clean up offers bolt quotes As of 2b923a0367c5f9154fcec706e3302cc4658dd889. Recurrence quotes need to be marked separately, since they're no longer in offers main bolt. Signed-off-by: Rusty Russell --- common/bolt12.c | 23 +++++++++-------------- common/bolt12_merkle.c | 14 +++++++------- common/features.h | 2 +- devtools/bolt12-cli.c | 4 ++-- lightningd/offer.c | 2 +- plugins/fetchinvoice.c | 35 +++++++++++++++++++++-------------- plugins/offers.c | 6 +++--- plugins/offers_inv_hook.c | 6 ++++-- plugins/offers_invreq_hook.c | 28 +++++++++++++++------------- 9 files changed, 63 insertions(+), 57 deletions(-) diff --git a/common/bolt12.c b/common/bolt12.c index 0c95c732e611..db9408210b4a 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -22,15 +22,10 @@ bool bolt12_chains_match(const struct bitcoin_blkid *chains, /* BOLT-offers #12: * The reader of an invoice_request: *... - * - MUST fail the request if `chains` does not include (or - * imply) a supported chain. - */ - /* BOLT-offers #12: - * - * - if the chain for the invoice is not solely bitcoin: - * - MUST specify `chains` the invoice is valid for. - * - otherwise: - * - the bitcoin chain is implied as the first and only entry. + * - if `chain` is not present: + * - MUST fail the request if bitcoin is not a supported chain. + * - otherwise: + * - MUST fail the request if `chain` is not a supported chain. */ num_chains = tal_count(chains); if (num_chains == 0) { @@ -340,7 +335,7 @@ static u64 time_change(u64 prevstart, u32 number, u64 offer_period_start(u64 basetime, size_t n, const struct tlv_offer_recurrence *recur) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * 1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months), * 3 (years). */ @@ -365,13 +360,13 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence, u64 basetime, u64 period_idx, u64 *start, u64 *end) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contains `recurrence_paywindow`: */ if (recurrence_paywindow) { u64 pstart = offer_period_start(basetime, period_idx, recurrence); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer has a `recurrence_basetime` or the * `recurrence_counter` is non-zero: * - SHOULD NOT send an `invoice_request` for a period prior to @@ -389,7 +384,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence, && recurrence_paywindow->seconds_after < 60) *end = pstart + 60; } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - otherwise: * - SHOULD NOT send an `invoice_request` with * `recurrence_counter` is non-zero for a period whose @@ -401,7 +396,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence, *start = offer_period_start(basetime, period_idx-1, recurrence); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - SHOULD NOT send an `invoice_request` for a period which * has already passed. */ diff --git a/common/bolt12_merkle.c b/common/bolt12_merkle.c index 2865b7e5528b..d9085f86c8ee 100644 --- a/common/bolt12_merkle.c +++ b/common/bolt12_merkle.c @@ -54,7 +54,7 @@ static void h_simpletag_ctx(struct sha256_ctx *sctx, const char *tag) /* BOLT-offers #12: * The Merkle tree's leaves are, in TLV-ascending order for each tlv: * 1. The H(`LnLeaf`,tlv). - * 2. The H(`LnAll`|all-tlvs,tlv) where "all-tlvs" consists of all non-signature TLV entries appended in ascending order. + * 2. The H(`LnAll`||all-tlvs,tlv) where "all-tlvs" consists of all non-signature TLV entries appended in ascending order. */ /* Create a sha256_ctx which has the tag part done. */ @@ -107,7 +107,7 @@ static void calc_lnleaf(const struct tlv_field *field, struct sha256 *hash) } /* BOLT-offers #12: - * The Merkle tree inner nodes are H(`LnBranch`, lesser-SHA256|greater-SHA256); + * The Merkle tree inner nodes are H(`LnBranch`, lesser-SHA256||greater-SHA256) */ static struct sha256 *merkle_pair(const tal_t *ctx, const struct sha256 *a, const struct sha256 *b) @@ -200,11 +200,11 @@ void merkle_tlv(const struct tlv_field *fields, struct sha256 *merkle) * * Each form is signed using one or more TLV signature elements; TLV * types 240 through 1000 are considered signature elements. For these - * the tag is `lightning` | `messagename` | `fieldname`, and `msg` is the - * Merkle-root; `lightning` is the literal 9-byte ASCII string, - * `messagename` is the name of the TLV stream being signed (i.e. `offer`, - * `invoice_request` or `invoice`) and the `fieldname` is the TLV field - * containing the signature (e.g. `signature` or `payer_signature`). + * the tag is "lightning" || `messagename` || `fieldname`, and `msg` is the + * Merkle-root; "lightning" is the literal 9-byte ASCII string, + * `messagename` is the name of the TLV stream being signed (i.e. "offer", + * "invoice_request" or "invoice") and the `fieldname` is the TLV field + * containing the signature (e.g. "signature" or "payer_signature"). */ void sighash_from_merkle(const char *messagename, const char *fieldname, diff --git a/common/features.h b/common/features.h index d41c81320eaa..0bb9dfea95af 100644 --- a/common/features.h +++ b/common/features.h @@ -135,7 +135,7 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); */ #define OPT_QUIESCE 34 -/* BOLT-1ede04a1a3225581e265b3ce96984ba88253a4a4 #9: +/* BOLT-offers #9: * * | 38/39 | `option_onion_messages` |... IN ... */ diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index 4e63c13beb8f..2e6dc0064058 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -174,8 +174,8 @@ static bool print_recurrance(const struct tlv_offer_recurrence *recurrence, const char *unit; bool ok = true; - /* BOLT-offers #12: - * Thus, each payment has: + /* BOLT-offers-recurrence #12: + * Thus, each offer containing a recurring payment has: * 1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months), * 3 (years). * 2. A `period`, defining how often (in `time_unit`) it has to be paid. diff --git a/lightningd/offer.c b/lightningd/offer.c index da81c9f53f23..09734163c399 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -310,7 +310,7 @@ static struct command_result *prev_payment(struct command *cmd, if (!inv->recurrence_counter) continue; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence_base` with * `start_any_period` non-zero: * - MUST include `recurrence_start` diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 8296008729da..1df3dd64c2aa 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -238,6 +238,15 @@ static struct command_result *handle_invreq_response(struct command *cmd, goto badinv; /* BOLT-offers #12: + * - if the invoice is a reply to an `invoice_request`: + *... + * - MUST reject the invoice unless the following fields are equal or + * unset exactly as they are in the `invoice_request:` + * - `quantity` + * - `payer_key` + * - `payer_info` + */ + /* BOLT-offers-recurrence #12: * - if the invoice is a reply to an `invoice_request`: *... * - MUST reject the invoice unless the following fields are equal or @@ -279,7 +288,7 @@ static struct command_result *handle_invreq_response(struct command *cmd, } else expected_amount = NULL; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence`: * - MUST reject the invoice if `recurrence_basetime` is not set. */ @@ -1080,7 +1089,7 @@ static struct command_result *invreq_done(struct command *cmd, if (sent->invreq->recurrence_start) period_idx += *sent->invreq->recurrence_start; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence_limit`: * - MUST NOT send an `invoice_request` for a period greater * than `max_period` @@ -1093,7 +1102,7 @@ static struct command_result *invreq_done(struct command *cmd, period_idx, *sent->offer->recurrence_limit); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - SHOULD NOT send an `invoice_request` for a period which has * already passed. */ @@ -1257,11 +1266,11 @@ static struct command_result *json_fetchinvoice(struct command *cmd, && time_now().ts.tv_sec > *sent->offer->absolute_expiry) return command_fail(cmd, OFFER_EXPIRED, "Offer expired"); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer did not specify `amount`: * - MUST specify `amount`.`msat` in multiples of the minimum - * lightning-payable unit (e.g. milli-satoshis for bitcoin) for the - * first `chains` entry. + * lightning-payable unit (e.g. milli-satoshis for bitcoin) for + * `chain` (or for bitcoin, if there is no `chain`). * - otherwise: * - MAY omit `amount`. * - if it sets `amount`: @@ -1309,16 +1318,16 @@ static struct command_result *json_fetchinvoice(struct command *cmd, "quantity parameter unnecessary"); } - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence`: */ if (sent->offer->recurrence) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - for the initial request: *... * - MUST set `recurrence_counter` `counter` to 0. */ - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - for any successive requests: *... * - MUST set `recurrence_counter` `counter` to one greater @@ -1328,7 +1337,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "needs recurrence_counter"); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence_base` with * `start_any_period` non-zero: * - MUST include `recurrence_start` @@ -1353,7 +1362,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "needs recurrence_label"); } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - otherwise: * - MUST NOT set `recurrence_counter`. * - MUST NOT set `recurrence_start` @@ -1758,9 +1767,7 @@ static struct command_result *json_sendinvoice(struct command *cmd, */ sent->inv->payer_key = sent->offer->node_id; - /* BOLT-offers #12: - * - FIXME: recurrence! - */ + /* FIXME: recurrence? */ if (sent->offer->recurrence) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "FIXME: handle recurring send_invoice offer!"); diff --git a/plugins/offers.c b/plugins/offers.c index a58d1edd06f2..c4e1319711d3 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -339,7 +339,7 @@ static bool json_add_blinded_paths(struct json_stream *js, static const char *recurrence_time_unit_name(u8 time_unit) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * `time_unit` defining 0 (seconds), 1 (days), 2 (months), 3 (years). */ switch (time_unit) { @@ -608,7 +608,7 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->recurrence_start) json_add_u32(js, "recurrence_start", *invoice->recurrence_start); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence`: * - MUST reject the invoice if `recurrence_basetime` is not * set. @@ -733,7 +733,7 @@ static void json_add_invoice_request(struct json_stream *js, /* BOLT-offers #12: * - MUST fail the request if `payer_key` is not present. - * - MUST fail the request if `chains` does not include (or imply) a supported chain. + *... * - MUST fail the request if `features` contains unknown even bits. * - MUST fail the request if `offer_id` is not present. */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index cd9e00f3ba88..e68b79aa6807 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -354,8 +354,10 @@ struct command_result *handle_invoice(struct command *cmd, * * The reader of an invoice_request: *... - * - MUST fail the request if `chains` does not include (or imply) a - * supported chain. + * - if `chain` is not present: + * - MUST fail the request if bitcoin is not a supported chain. + * - otherwise: + * - MUST fail the request if `chain` is not a supported chain. */ if (!bolt12_chain_matches(inv->inv->chain, chainparams, inv->inv->chains)) { return fail_inv(cmd, inv, diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 6c6efabc2493..84c8f36f5fde 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -109,7 +109,7 @@ test_field(struct command *cmd, return NULL; } -/* BOLT-offers #12: +/* BOLT-offers-recurrence #12: * - if the invoice corresponds to an offer with `recurrence`: * ... * - if it sets `relative_expiry`: @@ -230,7 +230,7 @@ static struct command_result *check_period(struct command *cmd, if (ir->offer->recurrence_base) basetime = ir->offer->recurrence_base->basetime; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the invoice corresponds to an offer with `recurrence`: * - MUST set `recurrence_basetime` to the start of period #0 as * calculated by [Period Calculation](#offer-period-calculation). @@ -239,7 +239,7 @@ static struct command_result *check_period(struct command *cmd, period_idx = *ir->invreq->recurrence_counter; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer had `recurrence_base` and `start_any_period` * was 1: * - MUST fail the request if there is no `recurrence_start` @@ -255,14 +255,14 @@ static struct command_result *check_period(struct command *cmd, return err; period_idx += *ir->invreq->recurrence_start; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - MUST set (or not set) `recurrence_start` exactly as the * invoice_request did. */ ir->inv->recurrence_start = tal_dup(ir->inv, u32, ir->invreq->recurrence_start); } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - otherwise: * - MUST fail the request if there is a `recurrence_start` @@ -275,7 +275,7 @@ static struct command_result *check_period(struct command *cmd, return err; } - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer has a `recurrence_limit`: * - MUST fail the request if the period index is greater than * `max_period`. @@ -309,7 +309,7 @@ static struct command_result *check_period(struct command *cmd, set_recurring_inv_expiry(ir->inv, paywindow_end); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - if `recurrence_counter` is non-zero: *... @@ -475,7 +475,7 @@ static struct command_result *invreq_base_amount_simple(struct command *cmd, *amt = amount_msat(raw_amount); } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - otherwise: * - MUST fail the request if it does not contain `amount`. @@ -534,7 +534,7 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd, /* Last of all, we handle recurrence details, which often requires * further lookups. */ - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - MUST set (or not set) `recurrence_counter` exactly as the * invoice_request did. */ @@ -723,7 +723,7 @@ static struct command_result *listoffers_done(struct command *cmd, } if (ir->offer->recurrence) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - if the offer had a `recurrence`: * - MUST fail the request if there is no `recurrence_counter` @@ -733,7 +733,7 @@ static struct command_result *listoffers_done(struct command *cmd, if (err) return err; } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - otherwise (the offer had no `recurrence`): * - MUST fail the request if there is a `recurrence_counter` * field. @@ -870,8 +870,10 @@ struct command_result *handle_invoice_request(struct command *cmd, * * The reader of an invoice_request: *... - * - MUST fail the request if `chains` does not include (or imply) a - * supported chain. + * - if `chain` is not present: + * - MUST fail the request if bitcoin is not a supported chain. + * - otherwise: + * - MUST fail the request if `chain` is not a supported chain. */ if (!bolt12_chain_matches(ir->invreq->chain, chainparams, ir->invreq->chains)) { From 296437c6557320f85fbfd1b50567e946bdbff111 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0091/1530] bolt12: remove deprecated `chains` fields We also move recurrence fields into a separate spec patch. Signed-off-by: Rusty Russell --- common/bolt12.c | 43 ++++++++------------ common/bolt12.h | 7 +--- plugins/fetchinvoice.c | 8 ---- plugins/offers.c | 6 +-- plugins/offers_inv_hook.c | 4 +- plugins/offers_invreq_hook.c | 7 +--- wire/bolt12_exp_wire.csv | 4 -- wire/bolt12_wire.csv | 4 -- wire/extracted_bolt12_01_recurrence.patch | 48 +++++++++++++++++++++++ 9 files changed, 69 insertions(+), 62 deletions(-) create mode 100644 wire/extracted_bolt12_01_recurrence.patch diff --git a/common/bolt12.c b/common/bolt12.c index db9408210b4a..5ab64f31e1c6 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -8,11 +8,11 @@ #include #include -bool bolt12_chains_match(const struct bitcoin_blkid *chains, - const struct chainparams *must_be_chain) +/* If chains is NULL, max_num_chains is ignored */ +static bool bolt12_chains_match(const struct bitcoin_blkid *chains, + size_t max_num_chains, + const struct chainparams *must_be_chain) { - size_t num_chains; - /* BOLT-offers #12: * - if the chain for the invoice is not solely bitcoin: * - MUST specify `chains` the offer is valid for. @@ -27,13 +27,12 @@ bool bolt12_chains_match(const struct bitcoin_blkid *chains, * - otherwise: * - MUST fail the request if `chain` is not a supported chain. */ - num_chains = tal_count(chains); - if (num_chains == 0) { - num_chains = 1; + if (!chains) { + max_num_chains = 1; chains = &chainparams_for_network("bitcoin")->genesis_blockhash; } - for (size_t i = 0; i < num_chains; i++) { + for (size_t i = 0; i < max_num_chains; i++) { if (bitcoin_blkid_eq(&chains[i], &must_be_chain->genesis_blockhash)) return true; @@ -43,28 +42,20 @@ bool bolt12_chains_match(const struct bitcoin_blkid *chains, } bool bolt12_chain_matches(const struct bitcoin_blkid *chain, - const struct chainparams *must_be_chain, - const struct bitcoin_blkid *deprecated_chains) + const struct chainparams *must_be_chain) { - /* Obsolete: We used to put an array in here, but we only ever - * used a single value */ - if (deprecated_apis && !chain) - chain = deprecated_chains; - - if (!chain) - chain = &chainparams_for_network("bitcoin")->genesis_blockhash; - - return bitcoin_blkid_eq(chain, &must_be_chain->genesis_blockhash); + return bolt12_chains_match(chain, 1, must_be_chain); } static char *check_features_and_chain(const tal_t *ctx, const struct feature_set *our_features, const struct chainparams *must_be_chain, const u8 *features, - const struct bitcoin_blkid *chains) + const struct bitcoin_blkid *chains, + size_t num_chains) { if (must_be_chain) { - if (!bolt12_chains_match(chains, must_be_chain)) + if (!bolt12_chains_match(chains, num_chains, must_be_chain)) return tal_fmt(ctx, "wrong chain"); } @@ -190,7 +181,8 @@ struct tlv_offer *offer_decode(const tal_t *ctx, *fail = check_features_and_chain(ctx, our_features, must_be_chain, offer->features, - offer->chains); + offer->chains, + tal_count(offer->chains)); if (*fail) return tal_free(offer); @@ -242,9 +234,7 @@ struct tlv_invoice_request *invrequest_decode(const tal_t *ctx, *fail = check_features_and_chain(ctx, our_features, must_be_chain, invrequest->features, - invrequest->chain - ? invrequest->chain - : invrequest->chains); + invrequest->chain, 1); if (*fail) return tal_free(invrequest); @@ -283,8 +273,7 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx, *fail = check_features_and_chain(ctx, our_features, must_be_chain, invoice->features, - invoice->chain - ? invoice->chain : invoice->chains); + invoice->chain, 1); if (*fail) return tal_free(invoice); diff --git a/common/bolt12.h b/common/bolt12.h index e1b0c00e71bf..e451c2897685 100644 --- a/common/bolt12.h +++ b/common/bolt12.h @@ -99,14 +99,9 @@ bool bolt12_check_signature(const struct tlv_field *fields, const struct point32 *key, const struct bip340sig *sig); -/* Given a tal_arr of chains, does it contain this chain? */ -bool bolt12_chains_match(const struct bitcoin_blkid *chains, - const struct chainparams *must_be_chain); - /* Given a single bolt12 chain, does it match? (NULL == bitcoin) */ bool bolt12_chain_matches(const struct bitcoin_blkid *chain, - const struct chainparams *must_be_chain, - const struct bitcoin_blkid *deprecated_chains); + const struct chainparams *must_be_chain); /* Given a basetime, when does period N start? */ u64 offer_period_start(u64 basetime, size_t n, diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 1df3dd64c2aa..afc185f0c7d5 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -1383,10 +1383,6 @@ static struct command_result *json_fetchinvoice(struct command *cmd, * - the bitcoin chain is implied as the first and only entry. */ if (!streq(chainparams->network_name, "bitcoin")) { - if (deprecated_apis) { - invreq->chains = tal_arr(invreq, struct bitcoin_blkid, 1); - invreq->chains[0] = chainparams->genesis_blockhash; - } invreq->chain = tal_dup(invreq, struct bitcoin_blkid, &chainparams->genesis_blockhash); } @@ -1780,10 +1776,6 @@ static struct command_result *json_sendinvoice(struct command *cmd, * - the bitcoin chain is implied as the first and only entry. */ if (!streq(chainparams->network_name, "bitcoin")) { - if (deprecated_apis) { - sent->inv->chains = tal_arr(sent->inv, struct bitcoin_blkid, 1); - sent->inv->chains[0] = chainparams->genesis_blockhash; - } sent->inv->chain = tal_dup(sent->inv, struct bitcoin_blkid, &chainparams->genesis_blockhash); } diff --git a/plugins/offers.c b/plugins/offers.c index c4e1319711d3..7e6a933a1d4d 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -539,8 +539,6 @@ static void json_add_b12_invoice(struct json_stream *js, { bool valid = true; - if (invoice->chains) - json_add_chains(js, invoice->chains); if (invoice->chain) json_add_sha256(js, "chain", &invoice->chain->shad.sha); if (invoice->offer_id) @@ -679,7 +677,7 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->fallbacks) valid &= json_add_fallbacks(js, - invoice->chain ? invoice->chain : invoice->chains, + invoice->chain, invoice->fallbacks->fallbacks); /* BOLT-offers #12: @@ -726,8 +724,6 @@ static void json_add_invoice_request(struct json_stream *js, { bool valid = true; - if (invreq->chains) - json_add_chains(js, invreq->chains); if (invreq->chain) json_add_sha256(js, "chain", &invreq->chain->shad.sha); diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index e68b79aa6807..765e2da3e46b 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -359,9 +359,9 @@ struct command_result *handle_invoice(struct command *cmd, * - otherwise: * - MUST fail the request if `chain` is not a supported chain. */ - if (!bolt12_chain_matches(inv->inv->chain, chainparams, inv->inv->chains)) { + if (!bolt12_chain_matches(inv->inv->chain, chainparams)) { return fail_inv(cmd, inv, - "Wrong chains %s", + "Wrong chain %s", tal_hex(tmpctx, inv->inv->chain)); } diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 84c8f36f5fde..4562c7d7dbce 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -754,10 +754,6 @@ static struct command_result *listoffers_done(struct command *cmd, * - MUST specify `chains` the offer is valid for. */ if (!streq(chainparams->network_name, "bitcoin")) { - if (deprecated_apis) { - ir->inv->chains = tal_arr(ir->inv, struct bitcoin_blkid, 1); - ir->inv->chains[0] = chainparams->genesis_blockhash; - } ir->inv->chain = tal_dup(ir->inv, struct bitcoin_blkid, &chainparams->genesis_blockhash); } @@ -875,8 +871,7 @@ struct command_result *handle_invoice_request(struct command *cmd, * - otherwise: * - MUST fail the request if `chain` is not a supported chain. */ - if (!bolt12_chain_matches(ir->invreq->chain, chainparams, - ir->invreq->chains)) { + if (!bolt12_chain_matches(ir->invreq->chain, chainparams)) { return fail_invreq(cmd, ir, "Wrong chain %s", tal_hex(tmpctx, ir->invreq->chain)); diff --git a/wire/bolt12_exp_wire.csv b/wire/bolt12_exp_wire.csv index a53ca3cdf8a5..4077edb8d222 100644 --- a/wire/bolt12_exp_wire.csv +++ b/wire/bolt12_exp_wire.csv @@ -42,8 +42,6 @@ subtypedata,blinded_path,first_node_id,point, subtypedata,blinded_path,blinding,point, subtypedata,blinded_path,num_hops,byte, subtypedata,blinded_path,path,onionmsg_path,num_hops -tlvtype,invoice_request,chains,2 -tlvdata,invoice_request,chains,chains,chain_hash,... tlvtype,invoice_request,chain,3 tlvdata,invoice_request,chain,chain,chain_hash, tlvtype,invoice_request,offer_id,4 @@ -68,8 +66,6 @@ tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, tlvtype,invoice_request,payer_signature,240 tlvdata,invoice_request,payer_signature,sig,bip340sig, -tlvtype,invoice,chains,2 -tlvdata,invoice,chains,chains,chain_hash,... tlvtype,invoice,chain,3 tlvdata,invoice,chain,chain,chain_hash, tlvtype,invoice,offer_id,4 diff --git a/wire/bolt12_wire.csv b/wire/bolt12_wire.csv index a53ca3cdf8a5..4077edb8d222 100644 --- a/wire/bolt12_wire.csv +++ b/wire/bolt12_wire.csv @@ -42,8 +42,6 @@ subtypedata,blinded_path,first_node_id,point, subtypedata,blinded_path,blinding,point, subtypedata,blinded_path,num_hops,byte, subtypedata,blinded_path,path,onionmsg_path,num_hops -tlvtype,invoice_request,chains,2 -tlvdata,invoice_request,chains,chains,chain_hash,... tlvtype,invoice_request,chain,3 tlvdata,invoice_request,chain,chain,chain_hash, tlvtype,invoice_request,offer_id,4 @@ -68,8 +66,6 @@ tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, tlvtype,invoice_request,payer_signature,240 tlvdata,invoice_request,payer_signature,sig,bip340sig, -tlvtype,invoice,chains,2 -tlvdata,invoice,chains,chains,chain_hash,... tlvtype,invoice,chain,3 tlvdata,invoice,chain,chain,chain_hash, tlvtype,invoice,offer_id,4 diff --git a/wire/extracted_bolt12_01_recurrence.patch b/wire/extracted_bolt12_01_recurrence.patch new file mode 100644 index 000000000000..186841e476dc --- /dev/null +++ b/wire/extracted_bolt12_01_recurrence.patch @@ -0,0 +1,48 @@ +diff --git b/wire/bolt12_wire.csv a/wire/bolt12_wire.csv +index 726c3c0a1..a53ca3cdf 100644 +--- b/wire/bolt12_wire.csv ++++ a/wire/bolt12_wire.csv +@@ -18,6 +18,18 @@ tlvtype,offer,quantity_min,22 + tlvdata,offer,quantity_min,min,tu64, + tlvtype,offer,quantity_max,24 + tlvdata,offer,quantity_max,max,tu64, ++tlvtype,offer,recurrence,26 ++tlvdata,offer,recurrence,time_unit,byte, ++tlvdata,offer,recurrence,period,tu32, ++tlvtype,offer,recurrence_paywindow,64 ++tlvdata,offer,recurrence_paywindow,seconds_before,u32, ++tlvdata,offer,recurrence_paywindow,proportional_amount,byte, ++tlvdata,offer,recurrence_paywindow,seconds_after,tu32, ++tlvtype,offer,recurrence_limit,66 ++tlvdata,offer,recurrence_limit,max_period,tu32, ++tlvtype,offer,recurrence_base,28 ++tlvdata,offer,recurrence_base,start_any_period,byte, ++tlvdata,offer,recurrence_base,basetime,tu64, + tlvtype,offer,node_id,30 + tlvdata,offer,node_id,node_id,point32, + tlvtype,offer,send_invoice,54 +@@ -40,6 +54,10 @@ tlvtype,invoice_request,features,12 + tlvdata,invoice_request,features,features,byte,... + tlvtype,invoice_request,quantity,32 + tlvdata,invoice_request,quantity,quantity,tu64, ++tlvtype,invoice_request,recurrence_counter,36 ++tlvdata,invoice_request,recurrence_counter,counter,tu32, ++tlvtype,invoice_request,recurrence_start,68 ++tlvdata,invoice_request,recurrence_start,period_offset,tu32, + tlvtype,invoice_request,payer_key,38 + tlvdata,invoice_request,payer_key,key,point32, + tlvtype,invoice_request,payer_note,39 +@@ -74,6 +94,13 @@ tlvtype,invoice,quantity,32 + tlvdata,invoice,quantity,quantity,tu64, + tlvtype,invoice,refund_for,34 + tlvdata,invoice,refund_for,refunded_payment_hash,sha256, ++tlvtype,invoice,recurrence_counter,36 ++tlvdata,invoice,recurrence_counter,counter,tu32, ++tlvtype,invoice,send_invoice,54 ++tlvtype,invoice,recurrence_start,68 ++tlvdata,invoice,recurrence_start,period_offset,tu32, ++tlvtype,invoice,recurrence_basetime,64 ++tlvdata,invoice,recurrence_basetime,basetime,tu64, + tlvtype,invoice,payer_key,38 + tlvdata,invoice,payer_key,key,point32, + tlvtype,invoice,payer_note,39 From 132ac0b1b408105522fe0feedbdcf651e5560f09 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0092/1530] blinding: check against two of the test vectors in the PR. Signed-off-by: Rusty Russell --- common/test/Makefile | 2 + .../test/run-route_blinding_override_test.c | 366 ++++++++++++++++++ common/test/run-route_blinding_test.c | 249 ++++++++++++ 3 files changed, 617 insertions(+) create mode 100644 common/test/run-route_blinding_override_test.c create mode 100644 common/test/run-route_blinding_test.c diff --git a/common/test/Makefile b/common/test/Makefile index d46e49010686..d441bfaa74e2 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -21,6 +21,8 @@ ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) # Sphinx test wants to decode TLVs. common/test/run-sphinx: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o +common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o +common/test/run-route_blinding_override_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o common/test/run-param \ common/test/run-json: \ diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c new file mode 100644 index 000000000000..5025c8ff7d2f --- /dev/null +++ b/common/test/run-route_blinding_override_test.c @@ -0,0 +1,366 @@ +#include "config.h" +#include "../bigsize.c" +#include "../blindedpath.c" +#include "../blinding.c" +#include "../hmac.c" +#include "../type_to_string.c" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_sat_to_msat */ + bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED, + struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static u8 *json_to_enctlvs(const tal_t *ctx, + const char *buf, const jsmntok_t *tlvs) +{ + struct tlv_encrypted_data_tlv *enctlv = tlv_encrypted_data_tlv_new(tmpctx); + size_t i; + const jsmntok_t *t; + u8 *ret, *appended = tal_arr(tmpctx, u8, 0); + + json_for_each_obj(i, t, tlvs) { + if (json_tok_streq(buf, t, "short_channel_id")) { + enctlv->short_channel_id = tal(enctlv, struct short_channel_id); + assert(json_to_short_channel_id(buf, t+1, + enctlv->short_channel_id)); + } else if (json_tok_streq(buf, t, "padding")) { + enctlv->padding = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->padding); + } else if (json_tok_streq(buf, t, "next_node_id")) { + enctlv->next_node_id = tal(enctlv, struct pubkey); + assert(json_to_pubkey(buf, t+1, + enctlv->next_node_id)); + } else if (json_tok_streq(buf, t, "path_id")) { + enctlv->path_id = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->path_id); + } else if (json_tok_streq(buf, t, "next_blinding_override")) { + enctlv->next_blinding_override = tal(enctlv, struct pubkey); + assert(json_to_pubkey(buf, t+1, + enctlv->next_blinding_override)); + } else { + u16 tagnum; + u8 *val; + assert(json_tok_startswith(buf, t, "unknown_tag_")); + tagnum = atoi(buf + t->start + strlen("unknown_tag_")); + assert(tagnum); + val = json_tok_bin_from_hex(enctlv, buf, t+1); + assert(val); + + /* We can't actually represent these in a way towire_ + * will see, so we literally append them */ + towire_bigsize(&appended, tagnum); + towire_bigsize(&appended, tal_bytelen(val)); + towire_u8_array(&appended, val, tal_bytelen(val)); + } + } + ret = tal_arr(ctx, u8, 0); + towire_encrypted_data_tlv(&ret, enctlv); + towire_u8_array(&ret, appended, tal_bytelen(appended)); + return ret; +} + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey->secret.data, NULL, NULL) != 1) + abort(); +} + +int main(int argc, char *argv[]) +{ + char *json; + size_t i, num_sender_hops; + jsmn_parser parser; + jsmntok_t toks[5000]; + const jsmntok_t *t, *recip_route_hops, *recip_blinding_hops, + *sender_route_hops, *sender_blinding_hops, *unblinding_hops; + struct pubkey *ids; + u8 **enctlvs, **encrypted_data; + struct privkey blinding; + + common_setup(argv[0]); + + if (argv[1]) + json = grab_file(tmpctx, argv[1]); + else { + char *dir = getenv("BOLTDIR"); + json = grab_file(tmpctx, + path_join(tmpctx, + dir ? dir : "../lightning-rfc", + "bolt04/route-blinding-override-test.json")); + if (!json) { + printf("test file not found, skipping\n"); + goto out; + } + } + + jsmn_init(&parser); + if (jsmn_parse(&parser, json, strlen(json), toks, ARRAY_SIZE(toks)) < 0) + abort(); + + /* We concatenate the sender_route_blinding and the + * recipient_route_blinding to form a contiguous sequence of + * enctlvs */ + recip_route_hops = json_get_member(json, json_get_member(json, toks, "recipient_route"), "hops"); + sender_route_hops = json_get_member(json, json_get_member(json, toks, "sender_route"), "hops"); + recip_blinding_hops = json_get_member(json, json_get_member(json, toks, "recipient_route_blinding"), "hops"); + sender_blinding_hops = json_get_member(json, json_get_member(json, toks, "sender_route_blinding"), "hops"); + unblinding_hops = json_get_member(json, json_get_member(json, toks, "unblinding"), "hops"); + + assert(recip_route_hops->size == recip_blinding_hops->size); + assert(sender_route_hops->size == sender_blinding_hops->size); + num_sender_hops = sender_route_hops->size; + + ids = tal_arr(tmpctx, struct pubkey, + num_sender_hops + recip_route_hops->size); + enctlvs = tal_arr(tmpctx, u8 *, num_sender_hops + recip_route_hops->size); + json_for_each_arr(i, t, sender_route_hops) { + u8 *expected; + assert(json_to_pubkey(json, json_get_member(json, t, "node_id"), + &ids[i])); + enctlvs[i] = json_tok_bin_from_hex(enctlvs, json, + json_get_member(json, t, "encoded_tlvs")); + expected = json_to_enctlvs(tmpctx, json, + json_get_member(json, t, "tlvs")); + assert(memeq(expected, tal_bytelen(expected), + enctlvs[i], tal_bytelen(enctlvs[i]))); + } + + json_for_each_arr(i, t, recip_route_hops) { + u8 *expected; + assert(json_to_pubkey(json, json_get_member(json, t, "node_id"), + &ids[i + num_sender_hops])); + enctlvs[i + num_sender_hops] + = json_tok_bin_from_hex(enctlvs, json, + json_get_member(json, t, "encoded_tlvs")); + expected = json_to_enctlvs(tmpctx, json, + json_get_member(json, t, "tlvs")); + assert(memeq(expected, tal_bytelen(expected), + enctlvs[i + num_sender_hops], + tal_bytelen(enctlvs[i + num_sender_hops]))); + } + + encrypted_data = tal_arr(tmpctx, u8 *, + num_sender_hops + recip_route_hops->size); + + /* Now do the blinding. */ + json_for_each_arr(i, t, sender_blinding_hops) { + struct secret s; + struct pubkey pubkey, expected_pubkey; + u8 *expected_encdata; + struct pubkey alias, expected_alias; + + assert(json_to_secret(json, + json_get_member(json, t, "ephemeral_privkey"), + &s)); + + /* First blinding is stated, remainder are derived! */ + if (i == 0) { + blinding.secret = s; + } else + assert(secret_eq_consttime(&blinding.secret, &s)); + + assert(pubkey_from_privkey(&blinding, &pubkey)); + json_to_pubkey(json, json_get_member(json, t, "ephemeral_pubkey"), + &expected_pubkey); + assert(pubkey_eq(&pubkey, &expected_pubkey)); + + encrypted_data[i] = enctlv_from_encmsg_raw(encrypted_data, + &blinding, + &ids[i], + enctlvs[i], + &blinding, + &alias); + expected_encdata = json_tok_bin_from_hex(tmpctx,json, + json_get_member(json, t, + "encrypted_data")); + assert(memeq(encrypted_data[i], tal_bytelen(encrypted_data[i]), + expected_encdata, tal_bytelen(expected_encdata))); + + json_to_pubkey(json, json_get_member(json, t, "blinded_node_id"), + &expected_alias); + assert(pubkey_eq(&alias, &expected_alias)); + } + + /* At this point, we override the blinding! */ + json_for_each_arr(i, t, recip_blinding_hops) { + struct secret s; + struct pubkey pubkey, expected_pubkey; + u8 *expected_encdata; + struct pubkey alias, expected_alias; + + assert(json_to_secret(json, + json_get_member(json, t, "ephemeral_privkey"), + &s)); + + /* First blinding is from next_blinding_override, + * remainder are derived! */ + if (i == 0) { + blinding.secret = s; + } else + assert(secret_eq_consttime(&blinding.secret, &s)); + + assert(pubkey_from_privkey(&blinding, &pubkey)); + json_to_pubkey(json, json_get_member(json, t, "ephemeral_pubkey"), + &expected_pubkey); + assert(pubkey_eq(&pubkey, &expected_pubkey)); + + encrypted_data[i + num_sender_hops] + = enctlv_from_encmsg_raw(tmpctx, + &blinding, + &ids[i + num_sender_hops], + enctlvs[i + num_sender_hops], + &blinding, + &alias); + expected_encdata = json_tok_bin_from_hex(tmpctx,json, + json_get_member(json, t, + "encrypted_data")); + assert(memeq(encrypted_data[i + num_sender_hops], + tal_bytelen(encrypted_data[i + num_sender_hops]), + expected_encdata, tal_bytelen(expected_encdata))); + + json_to_pubkey(json, json_get_member(json, t, "blinded_node_id"), + &expected_alias); + assert(pubkey_eq(&alias, &expected_alias)); + } + + /* Now try unblinding */ + json_for_each_arr(i, t, unblinding_hops) { + struct privkey me; + struct secret ss; + struct pubkey blinding, expected_blinding; + struct pubkey onion_key, next_node; + + assert(json_to_secret(json, + json_get_member(json, t, "node_privkey"), + &me.secret)); + + mykey = &me; + assert(json_to_pubkey(json, + json_get_member(json, t, "ephemeral_pubkey"), + &blinding)); + + assert(unblind_onion(&blinding, test_ecdh, &onion_key, &ss)); + if (i != unblinding_hops->size - 1) { + assert(decrypt_enctlv(&blinding, &ss, encrypted_data[i], &next_node, &blinding)); + assert(json_to_pubkey(json, + json_get_member(json, t, "next_ephemeral_pubkey"), + &expected_blinding)); + assert(pubkey_eq(&blinding, &expected_blinding)); + } else { + struct secret *path_id; + struct pubkey my_id, alias; + assert(pubkey_from_privkey(&me, &my_id)); + assert(decrypt_final_enctlv(tmpctx, &blinding, &ss, + encrypted_data[i], + &my_id, &alias, + &path_id)); + } + } + +out: + common_shutdown(); +} diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c new file mode 100644 index 000000000000..d9f141839536 --- /dev/null +++ b/common/test/run-route_blinding_test.c @@ -0,0 +1,249 @@ +#include "config.h" +#include "../bigsize.c" +#include "../blindedpath.c" +#include "../blinding.c" +#include "../hmac.c" +#include "../type_to_string.c" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_sat_to_msat */ + bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED, + struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static u8 *json_to_enctlvs(const tal_t *ctx, + const char *buf, const jsmntok_t *tlvs) +{ + struct tlv_encrypted_data_tlv *enctlv = tlv_encrypted_data_tlv_new(tmpctx); + size_t i; + const jsmntok_t *t; + u8 *ret, *appended = tal_arr(tmpctx, u8, 0); + + json_for_each_obj(i, t, tlvs) { + if (json_tok_streq(buf, t, "short_channel_id")) { + enctlv->short_channel_id = tal(enctlv, struct short_channel_id); + assert(json_to_short_channel_id(buf, t+1, + enctlv->short_channel_id)); + } else if (json_tok_streq(buf, t, "padding")) { + enctlv->padding = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->padding); + } else if (json_tok_streq(buf, t, "next_node_id")) { + enctlv->next_node_id = tal(enctlv, struct pubkey); + assert(json_to_pubkey(buf, t+1, + enctlv->next_node_id)); + } else if (json_tok_streq(buf, t, "path_id")) { + enctlv->path_id = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->path_id); + } else { + u16 tagnum; + u8 *val; + assert(json_tok_startswith(buf, t, "unknown_tag_")); + tagnum = atoi(buf + t->start + strlen("unknown_tag_")); + assert(tagnum); + val = json_tok_bin_from_hex(enctlv, buf, t+1); + assert(val); + + /* We can't actually represent these in a way towire_ + * will see, so we literally append them */ + towire_bigsize(&appended, tagnum); + towire_bigsize(&appended, tal_bytelen(val)); + towire_u8_array(&appended, val, tal_bytelen(val)); + } + } + ret = tal_arr(ctx, u8, 0); + towire_encrypted_data_tlv(&ret, enctlv); + towire_u8_array(&ret, appended, tal_bytelen(appended)); + return ret; +} + +int main(int argc, char *argv[]) +{ + char *json; + size_t i; + jsmn_parser parser; + jsmntok_t toks[5000]; + const jsmntok_t *t, *hops_tok; + struct pubkey *ids; + u8 **enctlvs; + struct privkey blinding; + + common_setup(argv[0]); + + if (argv[1]) + json = grab_file(tmpctx, argv[1]); + else { + char *dir = getenv("BOLTDIR"); + json = grab_file(tmpctx, + path_join(tmpctx, + dir ? dir : "../lightning-rfc", + "bolt04/route-blinding-test.json")); + if (!json) { + printf("test file not found, skipping\n"); + goto out; + } + } + + jsmn_init(&parser); + if (jsmn_parse(&parser, json, strlen(json), toks, ARRAY_SIZE(toks)) < 0) + abort(); + + hops_tok = json_get_member(json, json_get_member(json, toks, "route"), "hops"); + ids = tal_arr(tmpctx, struct pubkey, hops_tok->size); + enctlvs = tal_arr(tmpctx, u8 *, hops_tok->size); + + json_for_each_arr(i, t, hops_tok) { + u8 *expected; + assert(json_to_pubkey(json, json_get_member(json, t, "node_id"), + &ids[i])); + enctlvs[i] = json_tok_bin_from_hex(enctlvs, json, + json_get_member(json, t, "encoded_tlvs")); + expected = json_to_enctlvs(tmpctx, json, + json_get_member(json, t, "tlvs")); + assert(memeq(expected, tal_bytelen(expected), + enctlvs[i], tal_bytelen(enctlvs[i]))); + } + + /* Now do the blinding. */ + hops_tok = json_get_member(json, json_get_member(json, toks, "blinding"), "hops"); + assert(hops_tok->size == tal_count(ids)); + + json_for_each_arr(i, t, hops_tok) { + struct secret s; + struct pubkey pubkey, expected_pubkey; + u8 *enctlv, *expected_enctlv; + struct pubkey alias, expected_alias; + + assert(json_to_secret(json, + json_get_member(json, t, "ephemeral_privkey"), + &s)); + + /* First blinding is stated, remainder are derived! */ + if (i == 0) { + blinding.secret = s; + } else + assert(secret_eq_consttime(&blinding.secret, &s)); + + assert(pubkey_from_privkey(&blinding, &pubkey)); + json_to_pubkey(json, json_get_member(json, t, "ephemeral_pubkey"), + &expected_pubkey); + assert(pubkey_eq(&pubkey, &expected_pubkey)); + + enctlv = enctlv_from_encmsg_raw(tmpctx, + &blinding, + &ids[i], + enctlvs[i], + &blinding, + &alias); + expected_enctlv = json_tok_bin_from_hex(tmpctx,json, + json_get_member(json, t, + "encrypted_data")); + assert(memeq(enctlv, tal_bytelen(enctlv), + expected_enctlv, tal_bytelen(expected_enctlv))); + + json_to_pubkey(json, json_get_member(json, t, "blinded_node_id"), + &expected_alias); + assert(pubkey_eq(&alias, &expected_alias)); + } + +out: + common_shutdown(); +} From e2698c5fc3098e3db3e70ce46664a4a07d695a32 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 13:36:05 +1030 Subject: [PATCH 0093/1530] lightningd: fix leak report for self_id This has been here for a while: self_id hangs around while we're calling the hook, but now it triggers sometimes. ``` E ValueError: E Node errors: E Global errors: E - Node /tmp/ltests-3mcyp67u/test_dev_rawrequest_1/lightning-1/ has memory leaks: [ E { E "backtrace": [ E "ccan/ccan/tal/tal.c:442 (tal_alloc_)", E "gossipd/gossipd_wiregen.c:528 (fromwire_gossipd_got_onionmsg_to_us)", E "lightningd/onion_message.c:152 (handle_onionmsg_to_us)", E "lightningd/gossip_control.c:137 (gossip_msg)", E "lightningd/subd.c:548 (sd_msg_read)", E "ccan/ccan/io/io.c:59 (next_plan)", E "ccan/ccan/io/io.c:407 (do_plan)", E "ccan/ccan/io/io.c:417 (io_ready)", E "ccan/ccan/io/poll.c:453 (io_loop)", E "lightningd/io_loop_with_timers.c:21 (io_loop_with_timers)", E "lightningd/lightningd.c:1164 (main)" E ], E "label": "gossipd/gossipd_wiregen.c:528:struct secret", E "parents": [ E "lightningd/onion_message.c:149:struct onion_message_hook_payload", E "lightningd/plugin_hook.c:81:struct hook_instance *[]" E ], E "value": "0x55cf3cbc9458" E } E ] ``` --- lightningd/onion_message.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 9fcf475c5997..88034edc8006 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -173,6 +173,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) * means we created the path it's using. */ if (!self_id || !secret_eq_consttime(self_id, &ld->onion_reply_secret)) payload->our_alias = tal_free(payload->our_alias); + tal_free(self_id); submsglen = tal_bytelen(submsg); subptr = submsg; From 5a5cf8c69678fa73f211f391a9e938a0b991bb95 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 30 Nov 2021 10:43:56 +1030 Subject: [PATCH 0094/1530] pytest: fix flake in testing. As noted in https://github.com/ElementsProject/lightning/pull/4897/commits/0a406230d051a83f9f029054efe2dd24e87887a8#diff-5871d4c569454db5e625383975462132da0bd03d32df145d8d72d8fafd86d952R3544-R3546 Turns out we sometimes hang up before l2 sees the previous tx revoked, so we get a normal unilateral close, not a cheat. Reported-by: Simon Vrouwe Signed-off-by: Rusty Russell --- tests/test_connection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 0726697bd9de..7740bf85b967 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3540,10 +3540,10 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_log('option_static_remotekey enabled at 1/1') + # Make sure l2 gets REVOKE_AND_ACK from previous. + l2.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') + # Pre-statickey penalty works. - # FIXME: Without this sleep, l1 will broadcasts one tx more compared to good - # case, causing `wait_for_onchaind_broadcast` to timeout. - time.sleep(5) bitcoind.rpc.sendrawtransaction(tx) bitcoind.generate_block(1) From 3433ff5e155a7d9cf2008e27c9094a7b1e482bd9 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Tue, 23 Nov 2021 13:32:44 +0800 Subject: [PATCH 0095/1530] wallet/db.c, wallet/wallet.c: Add a partial index to speed up startup. Closes: #4901 Tested by `EXPLAIN QUERY PLAN` on sqlite3; #4901 shows the result from @whitslack doing a similar partial index on PostgreSQL on his ~1000 chan node. ChangeLog-Added: db: Speed up loading of pending HTLCs during startup by using a partial index. --- wallet/db.c | 10 ++++++++++ wallet/wallet.c | 18 +++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/wallet/db.c b/wallet/db.c index cd4d486916a4..e10217b6a098 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -2,8 +2,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -858,6 +860,14 @@ static struct migration dbmigrations[] = { /* Issue #4887: reset the payments.id sequence after the migration above. Since this is a SELECT statement that would otherwise fail, make it an INSERT into the `vars` table.*/ {SQL("/*PSQL*/INSERT INTO vars (name, intval) VALUES ('payment_id_reset', setval(pg_get_serial_sequence('payments', 'id'), COALESCE((SELECT MAX(id)+1 FROM payments), 1)))"), NULL}, + + /* Issue #4901: Partial index speeds up startup on nodes with ~1000 channels. */ + {&SQL("CREATE INDEX channel_htlcs_speedup_unresolved_idx" + " ON channel_htlcs(channel_id, direction)" + " WHERE hstate NOT IN (9, 19);") + [BUILD_ASSERT_OR_ZERO( 9 == RCVD_REMOVE_ACK_REVOCATION) + + BUILD_ASSERT_OR_ZERO(19 == SENT_REMOVE_ACK_REVOCATION)], + NULL}, }; /* Leak tracking. */ diff --git a/wallet/wallet.c b/wallet/wallet.c index ba4c8c3bb8cc..29bd385e5fd2 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2707,10 +2707,16 @@ bool wallet_htlcs_load_in_for_channel(struct wallet *wallet, " FROM channel_htlcs" " WHERE direction= ?" " AND channel_id= ?" - " AND hstate != ?")); + " AND hstate NOT IN (?, ?)")); db_bind_int(stmt, 0, DIRECTION_INCOMING); db_bind_u64(stmt, 1, chan->dbid); - db_bind_int(stmt, 2, SENT_REMOVE_ACK_REVOCATION); + /* We need to generate `hstate NOT IN (9, 19)` in order to match + * the `WHERE` clause of the database index; incoming HTLCs will + * never actually get the state `RCVD_REMOVE_ACK_REVOCATION`. + * See https://sqlite.org/partialindex.html#queries_using_partial_indexes + */ + db_bind_int(stmt, 2, RCVD_REMOVE_ACK_REVOCATION); /* Not gonna happen. */ + db_bind_int(stmt, 3, SENT_REMOVE_ACK_REVOCATION); db_query_prepared(stmt); while (db_step(stmt)) { @@ -2753,10 +2759,16 @@ bool wallet_htlcs_load_out_for_channel(struct wallet *wallet, " FROM channel_htlcs" " WHERE direction = ?" " AND channel_id = ?" - " AND hstate != ?")); + " AND hstate NOT IN (?, ?)")); db_bind_int(stmt, 0, DIRECTION_OUTGOING); db_bind_u64(stmt, 1, chan->dbid); + /* We need to generate `hstate NOT IN (9, 19)` in order to match + * the `WHERE` clause of the database index; outgoing HTLCs will + * never actually get the state `SENT_REMOVE_ACK_REVOCATION`. + * See https://sqlite.org/partialindex.html#queries_using_partial_indexes + */ db_bind_int(stmt, 2, RCVD_REMOVE_ACK_REVOCATION); + db_bind_int(stmt, 3, SENT_REMOVE_ACK_REVOCATION); /* Not gonna happen. */ db_query_prepared(stmt); while (db_step(stmt)) { From 80661f920b8ea1f2cd1970129cff9ca13747ac66 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Thu, 2 Dec 2021 16:48:25 +0800 Subject: [PATCH 0096/1530] doc/lightning-datastore.7.md: Fix typos. Changelog-None --- doc/lightning-datastore.7.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index cc81552bfbcf..f94e706aa9ea 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -13,13 +13,13 @@ The **datastore** RPC command allows plugins to store data in the c-lightning database, for later retrieval. *key* is an array of values (though a single value is treated as a -one-element array), to form a heirarchy. Using the first element of +one-element array), to form a hierarchy. Using the first element of the key as the plugin name (e.g. `[ "summary" ]`) is recommended. A key can either have children or a value, never both: parents are created and removed automatically. -*mode* is one of "must-create" (default, fails it it already exists), -"must-replace" (fails it it doesn't already exist), +*mode* is one of "must-create" (default, fails if it already exists), +"must-replace" (fails if it doesn't already exist), "create-or-replace" (never fails), "must-append" (must already exist, append this to what's already there) or "create-or-append" (append if anything is there, otherwise create). From 384c359c79f47cff197fbe9fc7121ad5a3e4d322 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Thu, 4 Nov 2021 17:06:01 -0400 Subject: [PATCH 0097/1530] refactor: move exclude parsing to json_tok --- common/json_tok.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ common/json_tok.h | 10 ++++++++ common/route.h | 18 ++++++++++++++ plugins/topology.c | 52 +++------------------------------------- 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/common/json_tok.c b/common/json_tok.c index 33e4b7d90c12..ad159cfd1003 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -10,6 +10,7 @@ #include #include #include +#include struct command_result *param_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, @@ -649,6 +650,64 @@ param_routehint_array(struct command *cmd, const char *name, const char *buffer, return NULL; } +struct command_result *param_route_exclusion(struct command *cmd, + const char *name, const char *buffer, const jsmntok_t *tok, + struct route_exclusion **re) +{ + *re = tal(cmd, struct route_exclusion); + struct short_channel_id_dir *chan_id = + tal(tmpctx, struct short_channel_id_dir); + if (!short_channel_id_dir_from_str(buffer + tok->start, + tok->end - tok->start, + chan_id)) { + struct node_id *node_id = tal(tmpctx, struct node_id); + + if (!json_to_node_id(buffer, tok, node_id)) + return command_fail_badparam(cmd, "exclude", + buffer, tok, + "should be short_channel_id_dir or node_id"); + + (*re)->type = EXCLUDE_NODE; + (*re)->u.node_id = *node_id; + } else { + (*re)->type = EXCLUDE_CHANNEL; + (*re)->u.chan_id = *chan_id; + } + + return NULL; +} + +struct command_result * +param_route_exclusion_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct route_exclusion ***res) +{ + size_t i; + const jsmntok_t *curr; + char *element_name; + struct command_result *err; + if (tok->type != JSMN_ARRAY) { + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "Exclude array %s (\"%s\") is not an array", + name, json_strdup(tmpctx, buffer, tok)); + } + + *res = tal_arr(cmd, struct route_exclusion *, 0); + json_for_each_arr(i, curr, tok) { + struct route_exclusion *element; + element_name = tal_fmt(cmd, "%s[%zu]", name, i); + err = param_route_exclusion(cmd, element_name, buffer, curr, &element); + if (err != NULL) { + return err; + } + tal_arr_expand(res, element); + + tal_free(element_name); + } + return NULL; +} + struct command_result *param_lease_hex(struct command *cmd, const char *name, const char *buffer, diff --git a/common/json_tok.h b/common/json_tok.h index 74dbf58a68ac..cbaa61dfabdb 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -18,6 +18,7 @@ struct channel_id; struct command; struct command_result; struct json_escape; +struct route_exclusion; struct sha256; struct wally_psbt; @@ -205,6 +206,15 @@ struct command_result * param_routehint_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_info ***ris); +struct command_result *param_route_exclusion(struct command *cmd, + const char *name, const char *buffer, const jsmntok_t *tok, + struct route_exclusion **re); + +struct command_result * +param_route_exclusion_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct route_exclusion ***res); + /** * Parse a 'compact-lease' (serialized lease_rates) back into lease_rates */ diff --git a/common/route.h b/common/route.h index 1fc474234da3..11963fdab231 100644 --- a/common/route.h +++ b/common/route.h @@ -74,4 +74,22 @@ struct route_hop *route_from_dijkstra(const tal_t *ctx, const struct gossmap_node *src, struct amount_msat final_amount, u32 final_cltv); + +/* + * Manually exlude nodes or channels from a route. + * Used with `getroute` and `pay` commands + */ +enum route_exclusion_type { + EXCLUDE_CHANNEL = 1, + EXCLUDE_NODE = 2 +}; + +struct route_exclusion { + enum route_exclusion_type type; + union { + struct short_channel_id_dir chan_id; + struct node_id node_id; + } u; +}; + #endif /* LIGHTNING_COMMON_ROUTE_H */ diff --git a/plugins/topology.c b/plugins/topology.c index 3d30582d621b..cfc3bd8eae21 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -30,19 +30,6 @@ static struct gossmap *get_gossmap(void) /* Convenience global since route_score_fuzz doesn't take args. 0 to 1. */ static double fuzz; -enum exclude_entry_type { - EXCLUDE_CHANNEL = 1, - EXCLUDE_NODE = 2 -}; - -struct exclude_entry { - enum exclude_entry_type type; - union { - struct short_channel_id_dir chan_id; - struct node_id node_id; - } u; -}; - /* Prioritize costs over distance, but with fuzz. Cost must be * the same when the same channel queried, so we base it on that. */ static u64 route_score_fuzz(u32 distance, @@ -68,7 +55,7 @@ static bool can_carry(const struct gossmap *map, const struct gossmap_chan *c, int dir, struct amount_msat amount, - const struct exclude_entry **excludes) + struct route_exclusion **excludes) { struct node_id dstid; @@ -143,12 +130,11 @@ static struct command_result *json_getroute(struct command *cmd, { struct node_id *destination; struct node_id *source; - const jsmntok_t *excludetok; struct amount_msat *msat; u32 *cltv; /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ u64 *riskfactor_millionths, *fuzz_millionths; - const struct exclude_entry **excluded; + struct route_exclusion **excluded; u32 *max_hops; const struct dijkstra *dij; struct route_hop *route; @@ -164,7 +150,7 @@ static struct command_result *json_getroute(struct command *cmd, p_opt_def("fromid", param_node_id, &source, local_id), p_opt_def("fuzzpercent", param_millionths, &fuzz_millionths, 5000000), - p_opt("exclude", param_array, &excludetok), + p_opt("exclude", param_route_exclusion_array, &excluded), p_opt_def("maxhops", param_number, &max_hops, ROUTING_MAX_HOPS), NULL)) return command_param_failed(); @@ -176,38 +162,6 @@ static struct command_result *json_getroute(struct command *cmd, buffer, params, "should be <= 100"); - if (excludetok) { - const jsmntok_t *t; - size_t i; - - excluded = tal_arr(cmd, const struct exclude_entry *, 0); - - json_for_each_arr(i, t, excludetok) { - struct exclude_entry *entry = tal(excluded, struct exclude_entry); - struct short_channel_id_dir *chan_id = tal(tmpctx, struct short_channel_id_dir); - if (!short_channel_id_dir_from_str(buffer + t->start, - t->end - t->start, - chan_id)) { - struct node_id *node_id = tal(tmpctx, struct node_id); - - if (!json_to_node_id(buffer, t, node_id)) - return command_fail_badparam(cmd, "exclude", - buffer, t, - "should be short_channel_id or node_id"); - - entry->type = EXCLUDE_NODE; - entry->u.node_id = *node_id; - } else { - entry->type = EXCLUDE_CHANNEL; - entry->u.chan_id = *chan_id; - } - - tal_arr_expand(&excluded, entry); - } - } else { - excluded = NULL; - } - gossmap = get_gossmap(); src = gossmap_find_node(gossmap, source); if (!src) From fa6f01d5b15b52c41edc7df2ccb7fb7d82ac8c98 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Thu, 18 Nov 2021 14:24:24 -0500 Subject: [PATCH 0098/1530] Add route exclusion payment modifier and pay rpc exclude arg Changelog-Added: Add `exclude` option for `pay` command to manually exclude channels or nodes when finding a route. --- plugins/libplugin-pay.c | 42 +++++++++++++++++++++++++++++++++++++++++ plugins/libplugin-pay.h | 6 ++++++ plugins/pay.c | 10 ++++++++++ 3 files changed, 58 insertions(+) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index e2d240ba22e4..88ce1b199cbd 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3885,3 +3885,45 @@ static void payee_incoming_limit_step_cb(void *d UNUSED, struct payment *p) REGISTER_PAYMENT_MODIFIER(payee_incoming_limit, void *, NULL, payee_incoming_limit_step_cb); + +static struct route_exclusions_data * +route_exclusions_data_init(struct payment *p) +{ + struct route_exclusions_data *d; + if (p->parent != NULL) { + return payment_mod_route_exclusions_get_data(p->parent); + } else { + d = tal(p, struct route_exclusions_data); + d->exclusions = NULL; + } + return d; +} + +static void route_exclusions_step_cb(struct route_exclusions_data *d, + struct payment *p) +{ + if (p->parent) + return payment_continue(p); + struct route_exclusion **exclusions = d->exclusions; + for (size_t i = 0; i < tal_count(exclusions); i++) { + struct route_exclusion *e = exclusions[i]; + if (e->type == EXCLUDE_CHANNEL) { + channel_hints_update(p, e->u.chan_id.scid, e->u.chan_id.dir, + false, false, NULL, NULL); + } else { + if (node_id_eq(&e->u.node_id, p->destination)) { + payment_abort(p, "Payee is manually excluded"); + return; + } else if (node_id_eq(&e->u.node_id, p->local_id)) { + payment_abort(p, "Payer is manually excluded"); + return; + } + + tal_arr_expand(&p->excluded_nodes, e->u.node_id); + } + } + payment_continue(p); +} + +REGISTER_PAYMENT_MODIFIER(route_exclusions, struct route_exclusions_data *, + route_exclusions_data_init, route_exclusions_step_cb); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 92566b7454af..b2b4ce7feb40 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -406,6 +406,10 @@ struct adaptive_split_mod_data { u32 htlc_budget; }; +struct route_exclusions_data { + struct route_exclusion **exclusions; +}; + /* List of globally available payment modifiers. */ REGISTER_PAYMENT_MODIFIER_HEADER(retry, struct retry_mod_data); REGISTER_PAYMENT_MODIFIER_HEADER(routehints, struct routehints_data); @@ -426,6 +430,8 @@ REGISTER_PAYMENT_MODIFIER_HEADER(local_channel_hints, void); * we detect the payee to have, in order to not exhaust the number of HTLCs * each of those channels can bear. */ REGISTER_PAYMENT_MODIFIER_HEADER(payee_incoming_limit, void); +REGISTER_PAYMENT_MODIFIER_HEADER(route_exclusions, struct route_exclusions_data); + struct payment *payment_new(tal_t *ctx, struct command *cmd, struct payment *parent, diff --git a/plugins/pay.c b/plugins/pay.c index bcb9e89fe79e..cbd2c7c15c79 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -2255,7 +2255,14 @@ payment_listsendpays_previous(struct command *cmd, const char *buf, } struct payment_modifier *paymod_mods[] = { + /* NOTE: The order in which these four paymods are executed is + * significant! + * local_channel_hints *must* execute first before route_exclusions + * which *must* execute before directpay. + * exemptfee *must* also execute before directpay. + */ &local_channel_hints_pay_mod, + &route_exclusions_pay_mod, &exemptfee_pay_mod, &directpay_pay_mod, &shadowroute_pay_mod, @@ -2305,6 +2312,7 @@ static struct command_result *json_paymod(struct command *cmd, struct sha256 *local_offer_id; const struct tlv_invoice *b12; struct out_req *req; + struct route_exclusion **exclusions; #if DEVELOPER bool *use_shadow; #endif @@ -2326,6 +2334,7 @@ static struct command_result *json_paymod(struct command *cmd, maxdelay_default), p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), p_opt("localofferid", param_sha256, &local_offer_id), + p_opt("exclude", param_route_exclusion_array, &exclusions), #if DEVELOPER p_opt_def("use_shadow", param_bool, &use_shadow, true), #endif @@ -2479,6 +2488,7 @@ static struct command_result *json_paymod(struct command *cmd, shadow_route = payment_mod_shadowroute_get_data(p); payment_mod_presplit_get_data(p)->disable = disablempp; payment_mod_adaptive_splitter_get_data(p)->disable = disablempp; + payment_mod_route_exclusions_get_data(p)->exclusions = exclusions; /* This is an MPP enabled pay command, disable amount fuzzing. */ shadow_route->fuzz_amount = false; From 69bc1191cb524613452cb5fa7323e75514def752 Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Thu, 18 Nov 2021 14:24:37 -0500 Subject: [PATCH 0099/1530] tests: add pay test for exclude arg --- contrib/pyln-client/pyln/client/lightning.py | 6 +++-- tests/test_pay.py | 27 ++++++++++++++++++++ tests/test_plugin.py | 2 +- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index f80006e91eaa..9d37fb86e9f6 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -616,7 +616,7 @@ def dev_memleak(self): def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, - maxdelay=None, exemptfee=None, use_shadow=True): + maxdelay=None, exemptfee=None, use_shadow=True, exclude=[]): """ A developer version of `pay`, with the possibility to deactivate shadow routing (used for testing). @@ -631,6 +631,7 @@ def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, "maxdelay": maxdelay, "exemptfee": exemptfee, "use_shadow": use_shadow, + "exclude": exclude, } return self.call("pay", payload) @@ -989,7 +990,7 @@ def newaddr(self, addresstype=None): def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, - maxdelay=None, exemptfee=None): + maxdelay=None, exemptfee=None, exclude=[]): """ Send payment specified by {bolt11} with {msatoshi} (ignored if {bolt11} has an amount), optional {label} @@ -1004,6 +1005,7 @@ def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, "retry_for": retry_for, "maxdelay": maxdelay, "exemptfee": exemptfee, + "exclude": exclude, } return self.call("pay", payload) diff --git a/tests/test_pay.py b/tests/test_pay.py index 2213066b3bb9..1232c3a443aa 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5001,3 +5001,30 @@ def test_sendpay_grouping(node_factory, bitcoind): pays = l1.rpc.listpays()['pays'] assert(len(pays) == 3) assert([p['status'] for p in pays] == ['failed', 'failed', 'complete']) + + +def test_pay_manual_exclude(node_factory, bitcoind): + l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) + l1_id = l1.rpc.getinfo()['id'] + l2_id = l2.rpc.getinfo()['id'] + l3_id = l3.rpc.getinfo()['id'] + chan12 = l1.rpc.listpeers(l2_id)['peers'][0]['channels'][0] + chan23 = l2.rpc.listpeers(l3_id)['peers'][0]['channels'][0] + scid12 = chan12['short_channel_id'] + '/' + str(chan12['direction']) + scid23 = chan23['short_channel_id'] + '/' + str(chan23['direction']) + inv = l3.rpc.invoice(msatoshi='123000', label='label1', description='desc')['bolt11'] + # Exclude the payer node id + with pytest.raises(RpcError, match=r'Payer is manually excluded'): + l1.rpc.pay(inv, exclude=[l1_id]) + # Exclude the direct payee node id + with pytest.raises(RpcError, match=r'Payee is manually excluded'): + l2.rpc.pay(inv, exclude=[l3_id]) + # Exclude intermediate node id + with pytest.raises(RpcError, match=r'is not reachable directly and all routehints were unusable.'): + l1.rpc.pay(inv, exclude=[l2_id]) + # Exclude intermediate channel id + with pytest.raises(RpcError, match=r'is not reachable directly and all routehints were unusable.'): + l1.rpc.pay(inv, exclude=[scid12]) + # Exclude direct channel id + with pytest.raises(RpcError, match=r'is not reachable directly and all routehints were unusable.'): + l2.rpc.pay(inv, exclude=[scid23]) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3141aaa4d75c..777647d743de 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -397,7 +397,7 @@ def test_pay_plugin(node_factory): # Make sure usage messages are present. msg = 'pay bolt11 [msatoshi] [label] [riskfactor] [maxfeepercent] '\ - '[retry_for] [maxdelay] [exemptfee] [localofferid]' + '[retry_for] [maxdelay] [exemptfee] [localofferid] [exclude]' if DEVELOPER: msg += ' [use_shadow]' assert only_one(l1.rpc.help('pay')['help'])['command'] == msg From 5483465fb561f0c8cde3752ffce571b917b2aa0c Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Thu, 18 Nov 2021 14:24:49 -0500 Subject: [PATCH 0100/1530] doc: add exclude arg doc for pay rpc --- doc/lightning-pay.7.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index 49dc3be818ca..988a3cce9a9b 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -6,6 +6,7 @@ SYNOPSIS **pay** *bolt11* \[*msatoshi*\] \[*label*\] \[*riskfactor*\] \[*maxfeepercent*\] \[*retry\_for*\] \[*maxdelay*\] \[*exemptfee*\] +\[*exclude*\] DESCRIPTION ----------- @@ -40,6 +41,11 @@ finding routes and retrying the payment. However, a payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case. +*exclude* is a JSON array of short-channel-id/direction (e.g. \[ +"564334x877x1/0", "564195x1292x0/1" \]) or node-id which should be excluded +from consideration for routing. The default is not to exclude any channels +or nodes. + When using *lightning-cli*, you may skip optional parameters by using *null*. Alternatively, use **-k** option to provide parameters by name. From 84d83bd7160923daddf87aa4b043291c20b89d37 Mon Sep 17 00:00:00 2001 From: Jan Sarenik Date: Thu, 2 Dec 2021 17:03:08 +0100 Subject: [PATCH 0101/1530] .github: Fix FreeBSD workflow The docs https://github.com/marketplace/actions/freebsd-vm say that `runs-on:` must be `macos-10.15`. --- .github/workflows/bsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 6facf4fab6e3..bea605fef489 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: testfreebsd: - runs-on: macos-latest + runs-on: macos-10.15 name: Build and test on FreeBSD env: DEVELOPER: 1 From 94cb42bee649cd50fa0d0f3ec6972dff06d87ce0 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Thu, 2 Dec 2021 17:16:42 +0100 Subject: [PATCH 0102/1530] status: do not log ping and pong packets Debug logging of ping and pong packets turned out to be too excessive since this is now done on all channels and without config option. Changelog-None --- common/status.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/common/status.c b/common/status.c index 61fbdc3ef97c..499484c39f61 100644 --- a/common/status.c +++ b/common/status.c @@ -98,13 +98,25 @@ static void status_io_full(enum log_level iodir, status_send(take(towire_status_io(NULL, iodir, peer, who, p))); } +static bool status_peer_io_filter_packettype(const u8 *p) +{ + int msg_type = fromwire_peektype(p); + switch (msg_type) { + case WIRE_PING: + case WIRE_PONG: + return true; + } + return false; +} + static void status_peer_io_short(enum log_level iodir, const struct node_id *peer, const u8 *p) { - status_peer_debug(peer, "%s %s", - iodir == LOG_IO_OUT ? "peer_out" : "peer_in", - peer_wire_name(fromwire_peektype(p))); + if (!status_peer_io_filter_packettype(p)) + status_peer_debug(peer, "%s %s", + iodir == LOG_IO_OUT ? "peer_out" : "peer_in", + peer_wire_name(fromwire_peektype(p))); } void status_peer_io(enum log_level iodir, From cf40e585c3b7dcbb144f20cd94c36aad500ab8cd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Dec 2021 15:05:20 +1030 Subject: [PATCH 0103/1530] pytest: test to show conflict between websocket and wildcard addresses. Signed-off-by: Rusty Russell --- tests/test_connection.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 7740bf85b967..93c4ce3c24aa 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3746,11 +3746,17 @@ def test_old_feerate(node_factory): l1.pay(l2, 1000) +@pytest.mark.skip('Broken') @pytest.mark.developer("needs --dev-allow-localhost") def test_websocket(node_factory): ws_port = reserve() + port1, port2 = reserve(), reserve() + # We need a wildcard to show the websocket bug, but we need a real + # address to give us something to announce. l1, l2 = node_factory.line_graph(2, opts=[{'experimental-websocket-port': ws_port, + 'addr': [':' + str(port1), + '127.0.0.1: ' + str(port2)], 'dev-allow-localhost': None}, {'dev-allow-localhost': None}], wait_for_announce=True) @@ -3804,7 +3810,7 @@ def recv(self, maxlen): # Check node_announcement has websocket assert (only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['addresses'] - == [{'type': 'ipv4', 'address': '127.0.0.1', 'port': l1.port}, {'type': 'websocket', 'port': ws_port}]) + == [{'type': 'ipv4', 'address': '127.0.0.1', 'port': port2}, {'type': 'websocket', 'port': ws_port}]) @pytest.mark.developer("dev-disconnect required") From ff556fefc623ef0e53b375f08fc6ea126680edc5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Dec 2021 16:52:56 +1030 Subject: [PATCH 0104/1530] connectd: fix websocket binding when we're doing both IPv4 and IPv6 on same port. We would fail connectd when listening on the IPv6 version failed; instead we should allow that. Changelog-Experimental: experimental-websocket-port fixed to work with default addresses. Signed-off-by: Rusty Russell --- connectd/connectd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 9a56abe2fea5..677f9bd0b469 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1413,14 +1413,13 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, /* Override with websocket port */ addr = binding[i].u.wireaddr; addr.port = daemon->websocket_port; - handle_wireaddr_listen(daemon, &addr, false, true); - announced_some = true; + if (handle_wireaddr_listen(daemon, &addr, true, true)) + announced_some = true; /* FIXME: We don't report these bindings to * lightningd, so they don't appear in * getinfo. */ } - /* We add the websocket port to the announcement if it * applies to any */ if (announced_some) { From b45544c6596c4e57b6987182f8e31f10456c66ac Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 2 Dec 2021 17:38:48 +1030 Subject: [PATCH 0105/1530] options: fix handling of wildcard (allproto) address. We treated ':' as an empty DNS name in EXPERIMENTAL, which is wrong. Signed-off-by: Rusty Russell --- common/wireaddr.c | 9 +++++++-- common/wireaddr.h | 2 ++ lightningd/options.c | 5 ++++- tests/test_connection.py | 1 - 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 8130fee5dd98..48a1b3c37992 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -361,6 +361,11 @@ bool is_toraddr(const char *arg) return true; } +bool is_wildcardaddr(const char *arg) +{ + return streq(arg, ""); +} + /* Rules: * * - not longer than 255 @@ -374,7 +379,7 @@ bool is_dnsaddr(const char *arg) size_t i, arglen; int lastdot; - if (is_ipaddr(arg) || is_toraddr(arg)) + if (is_ipaddr(arg) || is_toraddr(arg) || is_wildcardaddr(arg)) return false; /* now that its not IP or TOR, check its a DNS name */ @@ -684,7 +689,7 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, /* An empty string means IPv4 and IPv6 (which under Linux by default * means just IPv6, and IPv4 gets autobound). */ - if (wildcard_ok && streq(ip, "")) { + if (wildcard_ok && is_wildcardaddr(ip)) { addr->itype = ADDR_INTERNAL_ALLPROTO; addr->u.port = splitport; return true; diff --git a/common/wireaddr.h b/common/wireaddr.h index 8b8b79904b72..ed83194d1af5 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -162,6 +162,8 @@ bool is_ipaddr(const char *arg); bool is_toraddr(const char *arg); +bool is_wildcardaddr(const char *arg); + bool is_dnsaddr(const char *arg); bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, diff --git a/lightningd/options.c b/lightningd/options.c index 4e79efeb32f2..9fb1eac0ffab 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -198,7 +198,10 @@ static char *opt_add_addr_withtype(const char *arg, if (!separate_address_and_port(tmpctx, arg, &address, &port)) return tal_fmt(NULL, "Unable to parse address:port '%s'", arg); - if (is_ipaddr(address) || is_toraddr(address) || ala != ADDR_ANNOUNCE) { + if (is_ipaddr(address) + || is_toraddr(address) + || is_wildcardaddr(address) + || ala != ADDR_ANNOUNCE) { if (!parse_wireaddr_internal(arg, &wi, ld->portnum, wildcard_ok, dns_ok, false, deprecated_apis, &err_msg)) { diff --git a/tests/test_connection.py b/tests/test_connection.py index 93c4ce3c24aa..eb62b1b9322b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3746,7 +3746,6 @@ def test_old_feerate(node_factory): l1.pay(l2, 1000) -@pytest.mark.skip('Broken') @pytest.mark.developer("needs --dev-allow-localhost") def test_websocket(node_factory): ws_port = reserve() From 5284ee4dae1f0ac417764af93ce296cc01c3c355 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 3 Dec 2021 09:08:00 +1030 Subject: [PATCH 0106/1530] connectd: don't advertize websocket support if we have no other (non-Tor) addrs. Signed-off-by: Rusty Russell --- connectd/connectd.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 677f9bd0b469..8ed57ebb0b3f 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1420,9 +1420,13 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, * getinfo. */ } - /* We add the websocket port to the announcement if it - * applies to any */ - if (announced_some) { + /* We add the websocket port to the announcement if we made one + * *and* we have other announced addresses. */ + /* BOLT-websocket #7: + * - MUST NOT add a `type 6` address unless there is also at + * least one address of different type. + */ + if (announced_some && tal_count(*announcable) != 0) { wireaddr_from_websocket(&addr, daemon->websocket_port); add_announcable(announcable, &addr); } From 1f79aad830094a3b73ee1c77d55da2e32ecf3c30 Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Sun, 5 Dec 2021 02:47:38 -0500 Subject: [PATCH 0107/1530] common/utils: introduce tmpdir_mkstemp; use it Various unit tests were creating temporary files unconditionally in /tmp and were not cleaning up after themselves. Introduce a new variant of mkstemp(3p) that respects the TMPDIR environment variable, and use it in the offending unit tests. This allows each test run to use a dedicated TMPDIR that can be cleaned up after the run. Changelog-None Signed-off-by: Matt Whitlock --- common/test/run-gossmap_local.c | 5 +++-- common/test/run-route-specific.c | 5 +++-- common/test/run-route.c | 5 +++-- common/utils.c | 15 +++++++++++++++ common/utils.h | 4 ++++ plugins/test/run-route-overlong.c | 5 +++-- wallet/test/run-db.c | 6 ++++-- wallet/test/run-wallet.c | 5 +++-- 8 files changed, 38 insertions(+), 12 deletions(-) diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index e2a198f54e96..20b08d562fd4 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -7,6 +7,7 @@ #include #include #include +#include #include /* AUTOGENERATED MOCKS START */ @@ -292,7 +293,7 @@ static void check_nannounce(const u8 *nannounce, int main(int argc, char *argv[]) { int fd; - char gossfile[] = "/tmp/run-gossip_local.XXXXXX"; + char *gossfile; struct gossmap *map; struct node_id l1, l2, l3, l4; struct short_channel_id scid23, scid12, scid_local; @@ -306,7 +307,7 @@ int main(int argc, char *argv[]) common_setup(argv[0]); - fd = mkstemp(gossfile); + fd = tmpdir_mkstemp(tmpctx, "run-gossip_local.XXXXXX", &gossfile); assert(write_all(fd, canned_map, sizeof(canned_map))); map = gossmap_load(tmpctx, gossfile, NULL); diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 4451e51ae216..33ab55dced41 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -185,7 +186,7 @@ int main(int argc, char *argv[]) struct gossmap *gossmap; const double riskfactor = 1.0; char gossip_version = GOSSIP_STORE_VERSION; - char gossipfilename[] = "/tmp/run-route-specific-gossipstore.XXXXXX"; + char *gossipfilename; common_setup(argv[0]); node_id_from_hexstr("03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf", @@ -203,7 +204,7 @@ int main(int argc, char *argv[]) chainparams = chainparams_for_network("regtest"); - store_fd = mkstemp(gossipfilename); + store_fd = tmpdir_mkstemp(tmpctx, "run-route-specific-gossipstore.XXXXXX", &gossipfilename); assert(write(store_fd, &gossip_version, sizeof(gossip_version)) == sizeof(gossip_version)); diff --git a/common/test/run-route.c b/common/test/run-route.c index 07dd66e3d3e0..0db4d77d90f2 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -182,11 +183,11 @@ int main(int argc, char *argv[]) struct gossmap *gossmap; const double riskfactor = 1.0; char gossip_version = GOSSIP_STORE_VERSION; - char gossipfilename[] = "/tmp/run-route-gossipstore.XXXXXX"; + char *gossipfilename; chainparams = chainparams_for_network("regtest"); - store_fd = mkstemp(gossipfilename); + store_fd = tmpdir_mkstemp(tmpctx, "run-route-gossipstore.XXXXXX", &gossipfilename); assert(write(store_fd, &gossip_version, sizeof(gossip_version)) == sizeof(gossip_version)); gossmap = gossmap_load(tmpctx, gossipfilename, NULL); diff --git a/common/utils.c b/common/utils.c index 311de3e0a8e6..57bc8f02e956 100644 --- a/common/utils.c +++ b/common/utils.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -217,3 +218,17 @@ char *utf8_str(const tal_t *ctx, const u8 *buf TAKES, size_t buflen) ret[buflen] = '\0'; return ret; } + +int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created) +{ + char *tmpdir = getenv("TMPDIR"); + char *path = path_join(ctx, tmpdir ?: "/tmp", template); + int fd = mkstemp(path); + + if (fd >= 0) + *created = path; + else + tal_free(path); + + return fd; +} diff --git a/common/utils.h b/common/utils.h index 0282f5a53018..66ca58a4d84a 100644 --- a/common/utils.h +++ b/common/utils.h @@ -152,4 +152,8 @@ STRUCTEQ_DEF(ripemd160, 0, u); /* Context which all wally allocations use (see common/setup.c) */ extern const tal_t *wally_tal_ctx; +/* Like mkstemp but resolves template relative to $TMPDIR (or /tmp if unset). + * Returns created temporary path name at *created if successful. */ +int tmpdir_mkstemp(const tal_t *ctx, const char *template TAKES, char **created); + #endif /* LIGHTNING_COMMON_UTILS_H */ diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index c4abb47e50f1..ce67a15a9a6b 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -332,11 +333,11 @@ int main(int argc, char *argv[]) struct payment *p; struct payment_modifier **mods; char gossip_version = GOSSIP_STORE_VERSION; - char gossipfilename[] = "/tmp/run-route-overlong.XXXXXX"; + char *gossipfilename; common_setup(argv[0]); chainparams = chainparams_for_network("regtest"); - store_fd = mkstemp(gossipfilename); + store_fd = tmpdir_mkstemp(tmpctx, "run-route-overlong.XXXXXX", &gossipfilename); assert(write(store_fd, &gossip_version, sizeof(gossip_version)) == sizeof(gossip_version)); diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index c6123e9ab16d..9f7e17d6a253 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -13,6 +13,7 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const s #include "test_utils.h" #include +#include #include #include @@ -76,14 +77,15 @@ void plugin_hook_db_sync(struct db *db UNNEEDED) static struct db *create_test_db(void) { struct db *db; - char *dsn, filename[] = "/tmp/ldb-XXXXXX"; + char *dsn, *filename; - int fd = mkstemp(filename); + int fd = tmpdir_mkstemp(tmpctx, "ldb-XXXXXX", &filename); if (fd == -1) return NULL; close(fd); dsn = tal_fmt(NULL, "sqlite3://%s", filename); + tal_free(filename); db = db_open(NULL, dsn); db->data_version = 0; tal_free(dsn); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index e5b170ccd1b7..43425ab0501f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -18,6 +18,7 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const s #include "wallet/db.c" #include +#include #include bool deprecated_apis = true; @@ -917,8 +918,8 @@ static void cleanup_test_wallet(struct wallet *w, char *filename) static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx) { - char *dsn, *filename = tal_fmt(ctx, "/tmp/ldb-XXXXXX"); - int fd = mkstemp(filename); + char *dsn, *filename; + int fd = tmpdir_mkstemp(ctx, "ldb-XXXXXX", &filename); struct wallet *w = tal(ctx, struct wallet); static unsigned char badseed[BIP32_ENTROPY_LEN_128]; const struct ext_key *bip32_base = NULL; From bdabef9a9b594d6804443c5afc69c234ffe731ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 5 Dec 2021 13:25:47 +1030 Subject: [PATCH 0108/1530] pytest: fix occasional flake in test_announce_address We make sure the gossip msg was sent, but the other node might not have digested it yet: ``` # Check other node can parse these > addresses = l2.rpc.listnodes(l1.info['id'])['nodes'][0]['addresses'] E KeyError: 'addresses' ``` Signed-off-by: Rusty Russell --- tests/test_gossip.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 30c82cb6f8d7..f66f9c8f794f 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -159,7 +159,8 @@ def test_announce_address(node_factory, bitcoind): "05096c6f63616c686f737404d3" # DNS 05 len localhost:1235 "050b6578616d706c652e636f6d04d4") # DNS 05 len example.com:1236 - # Check other node can parse these + # Check other node can parse these (make sure it has digested msg) + wait_for(lambda: 'addresses' in l2.rpc.listnodes(l1.info['id'])['nodes'][0]) addresses = l2.rpc.listnodes(l1.info['id'])['nodes'][0]['addresses'] addresses_dns = [address for address in addresses if address['type'] == 'dns'] assert len(addresses) == 6 From 4ffda340d3f7881228015a2f3a2e13573a406693 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 4 Dec 2021 21:53:56 +1030 Subject: [PATCH 0109/1530] check: make sure all files outside contrib/ include "config.h" first. And turn "" includes into full-path (which makes it easier to put config.h first, and finds some cases check-includes.sh missed previously). config.h sets _GNU_SOURCE which really needs to be done before any '#includes': we mainly got away with it with glibc, but other platforms like Alpine may have stricter requirements. Signed-off-by: Rusty Russell --- bitcoin/base58.c | 11 +++++----- bitcoin/chainparams.c | 3 ++- bitcoin/feerate.c | 1 + bitcoin/preimage.c | 1 + bitcoin/privkey.c | 3 ++- bitcoin/pubkey.c | 5 +++-- bitcoin/script.c | 11 +++++----- bitcoin/shadouble.c | 3 ++- bitcoin/short_channel_id.c | 1 + bitcoin/signature.c | 13 ++++++------ bitcoin/test/run-bitcoin_block_from_hex.c | 1 + bitcoin/varint.c | 3 ++- ccan_compat.h | 5 +++++ channeld/channeld.c | 1 + channeld/commit_tx.c | 1 + channeld/test/run-commit_tx.c | 1 + channeld/test/run-full_channel.c | 1 + channeld/watchtower.c | 4 ++-- closingd/closingd.c | 1 + common/addr.c | 3 ++- common/base32.c | 1 + common/bech32.c | 4 ++-- common/bech32_util.c | 3 ++- common/billboard.c | 3 ++- common/bip32.c | 1 + common/blindedpath.c | 1 + common/blinding.c | 1 + common/blockheight_states.c | 1 + common/bolt11.c | 1 + common/bolt11_json.c | 1 + common/bolt12.c | 1 + common/bolt12_merkle.c | 1 + common/channel_config.c | 1 + common/channel_id.c | 1 + common/channel_type.c | 1 + common/close_tx.c | 8 +++---- common/configdir.c | 3 ++- common/crypto_state.c | 1 + common/crypto_sync.c | 1 + common/daemon_conn.c | 1 + common/decode_array.c | 1 + common/derive_basepoints.c | 1 + common/descriptor_checksum.c | 1 + common/dijkstra.c | 1 + common/ecdh_hsmd.c | 1 + common/features.c | 3 ++- common/fee_states.c | 1 + common/gossip_rcvd_filter.c | 1 + common/hash_u5.c | 1 + common/hmac.c | 1 + common/hsm_encryption.c | 1 + common/htlc_state.c | 1 + common/htlc_trim.c | 1 + common/htlc_tx.c | 1 + common/htlc_wire.c | 1 + common/initial_commit_tx.c | 1 + common/io_lock.c | 3 ++- common/iso4217.c | 1 + common/json_helpers.c | 1 + common/json_stream.c | 1 + common/json_tok.c | 1 + common/key_derive.c | 1 + common/keyset.c | 1 + common/onion.c | 1 + common/onionreply.c | 1 + common/param.c | 1 + common/peer_billboard.c | 1 + common/peer_failed.c | 1 + common/permute_tx.c | 3 ++- common/ping.c | 1 + common/psbt_internal.c | 1 + common/pseudorand.c | 3 ++- common/random_select.c | 1 + common/shutdown_scriptpubkey.c | 1 + common/socket_close.c | 3 ++- common/status_levels.c | 1 + common/status_wire.c | 1 + common/subdaemon.c | 1 + common/test/run-amount.c | 1 + common/test/run-base64.c | 1 + common/test/run-bigsize.c | 1 + common/test/run-blindedpath_enctlv.c | 1 + common/test/run-blindedpath_onion.c | 1 + common/test/run-bolt11.c | 1 + common/test/run-bolt12_merkle.c | 1 + common/test/run-bolt12_period.c | 1 + common/test/run-cryptomsg.c | 1 + common/test/run-derive_basepoints.c | 1 + common/test/run-features.c | 1 + common/test/run-gossip_rcvd_filter.c | 1 + common/test/run-gossmap-fp16.c | 1 + common/test/run-gossmap_guess_node_id.c | 1 + common/test/run-gossmap_local.c | 1 + common/test/run-ip_port_parsing.c | 1 + common/test/run-json.c | 1 + common/test/run-json_scan.c | 1 + common/test/run-key_derive.c | 1 + common/test/run-lease_rates.c | 1 + common/test/run-lock.c | 1 + common/test/run-route-specific.c | 1 + common/test/run-route.c | 1 + common/test/run-softref.c | 1 + common/test/run-sphinx-xor_cipher_stream.c | 1 + common/test/run-sphinx.c | 1 + common/test/run-wireaddr.c | 1 + common/timeout.c | 3 ++- common/utils.c | 3 ++- common/version.c | 3 ++- common/wallet.c | 1 + common/wire_error.c | 1 + connectd/connectd.c | 1 + connectd/peer_exchange_initmsg.c | 1 + connectd/sha1.c | 1 + connectd/tor.c | 1 + devtools/bolt11-cli.c | 1 + devtools/bolt12-cli.c | 1 + devtools/clean_topo.c | 1 + devtools/create-gossipstore.c | 1 + devtools/decodemsg.c | 1 + devtools/dump-gossipstore.c | 1 + devtools/gossipwith.c | 1 + devtools/lightning-checkmessage.c | 1 + devtools/mkclose.c | 1 + devtools/mkcommit.c | 1 + devtools/mkencoded.c | 1 + devtools/mkfunding.c | 1 + devtools/print_wire.c | 1 + devtools/print_wire.h | 1 + devtools/route.c | 1 + gossipd/gossip_generation.c | 1 + gossipd/gossip_store.c | 4 ++-- gossipd/gossipd.c | 1 + gossipd/queries.c | 1 + gossipd/routing.c | 3 ++- gossipd/seeker.c | 1 + gossipd/test/run-check_channel_announcement.c | 1 + gossipd/test/run-check_node_announcement.c | 1 + gossipd/test/run-crc32_of_update.c | 1 + gossipd/test/run-next_block_range.c | 1 + gossipd/test/run-onion_message.c | 1 + gossipd/test/run-txout_failure.c | 1 + hsmd/capabilities.h | 1 + hsmd/hsmd.c | 1 + hsmd/libhsmd.c | 1 + hsmd/libhsmd.h | 1 + hsmd/libhsmd_status.c | 1 + lightningd/bitcoind.c | 17 ++++++++------- lightningd/chaintopology.c | 21 +++++++++---------- lightningd/channel.c | 1 + lightningd/channel_control.c | 1 + lightningd/closing_control.c | 1 + lightningd/coin_mvts.c | 1 + lightningd/connect_control.c | 1 + lightningd/datastore.c | 1 + lightningd/dual_open_control.c | 1 + lightningd/gossip_control.c | 13 ++++++------ lightningd/gossip_msg.c | 1 + lightningd/hsm_control.c | 7 ++++--- lightningd/htlc_end.c | 1 + lightningd/htlc_set.c | 1 + lightningd/invoice.c | 3 ++- lightningd/io_loop_with_timers.c | 4 ++-- lightningd/json.c | 1 + lightningd/jsonrpc.c | 2 +- lightningd/lightningd.c | 14 +++++++------ lightningd/log.c | 3 ++- lightningd/log_status.c | 1 + lightningd/memdump.c | 3 ++- lightningd/notification.c | 1 + lightningd/offer.c | 1 + lightningd/onchain_control.c | 1 + lightningd/onion_message.c | 1 + lightningd/opening_common.c | 1 + lightningd/opening_control.c | 3 ++- lightningd/options.c | 1 + lightningd/pay.c | 3 ++- lightningd/peer_control.c | 7 ++++--- lightningd/peer_htlcs.c | 1 + lightningd/ping.c | 1 + lightningd/plugin.c | 1 + lightningd/plugin_control.c | 1 + lightningd/plugin_hook.c | 1 + lightningd/routehint.c | 1 + lightningd/signmessage.c | 1 + lightningd/subd.c | 1 + lightningd/test/run-find_my_abspath.c | 1 + lightningd/test/run-invoice-select-inchan.c | 1 + lightningd/test/run-jsonrpc.c | 1 + lightningd/test/run-log-pruning.c | 1 + lightningd/watch.c | 1 + onchaind/onchaind.c | 1 + onchaind/onchaind_wire.c | 1 + onchaind/test/run-grind_feerate-bug.c | 1 + onchaind/test/run-grind_feerate.c | 1 + openingd/common.c | 1 + openingd/dualopend.c | 1 + openingd/openingd.c | 1 + plugins/autoclean.c | 1 + plugins/bcli.c | 1 + plugins/fetchinvoice.c | 1 + plugins/funder_policy.c | 1 + plugins/keysend.c | 1 + plugins/libplugin-pay.c | 1 + plugins/libplugin.c | 1 + plugins/offers.c | 1 + plugins/offers_inv_hook.c | 1 + plugins/offers_invreq_hook.c | 1 + plugins/offers_offer.c | 1 + plugins/pay.c | 1 + plugins/spender/fundchannel.c | 1 + plugins/spender/main.c | 1 + plugins/spender/multiwithdraw.c | 1 + plugins/test/run-funder_policy.c | 1 + plugins/test/run-route-overlong.c | 1 + plugins/topology.c | 1 + plugins/txprepare.c | 1 + tests/fuzz/fuzz-addr.c | 1 + tests/fuzz/fuzz-descriptor_checksum.c | 1 + tests/fuzz/libfuzz.c | 1 + tests/fuzz/libfuzz.h | 1 + tests/plugins/test_libplugin.c | 1 + .../test_selfdisable_after_getmanifest.c | 1 + tools/check-bolt.c | 1 + tools/check-includes.sh | 10 +++++++++ tools/hsmtool.c | 1 + tools/test/enum.c | 1 + tools/test/enum.h | 1 + tools/test/run-test-wire.c | 1 + wallet/db.c | 4 ++-- wallet/db_postgres.c | 3 ++- wallet/db_sqlite3.c | 1 + wallet/invoices.c | 7 ++++--- wallet/reservation.c | 1 + wallet/test/run-db.c | 1 + wallet/test/run-wallet.c | 1 + wallet/test/test_utils.c | 1 + wallet/test/test_utils.h | 1 + wallet/txfilter.c | 4 ++-- wallet/wallet.c | 6 +++--- wallet/walletrpc.c | 1 + wire/fromwire.c | 3 ++- wire/peer_wire.c | 1 + wire/test/run-peer-wire.c | 1 + wire/test/run-tlvstream.c | 1 + wire/towire.c | 1 + wire/wire_io.c | 1 + 246 files changed, 351 insertions(+), 102 deletions(-) diff --git a/bitcoin/base58.c b/bitcoin/base58.c index 05828b5b7fb9..e874b0b8ee0d 100644 --- a/bitcoin/base58.c +++ b/bitcoin/base58.c @@ -3,11 +3,12 @@ // Copyright (c) 2009-2012 The Bitcoin Developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "address.h" -#include "base58.h" -#include "privkey.h" -#include "pubkey.h" -#include "shadouble.h" +#include "config.h" +#include +#include +#include +#include +#include #include #include diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index 2f61badbad33..d253e04aca9e 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -1,4 +1,5 @@ -#include "chainparams.h" +#include "config.h" +#include #include #include #include diff --git a/bitcoin/feerate.c b/bitcoin/feerate.c index 88c4af7626aa..fc73423940db 100644 --- a/bitcoin/feerate.c +++ b/bitcoin/feerate.c @@ -1,3 +1,4 @@ +#include "config.h" #include u32 feerate_from_style(u32 feerate, enum feerate_style style) diff --git a/bitcoin/preimage.c b/bitcoin/preimage.c index ce7d69a18d5f..8fcafa01d085 100644 --- a/bitcoin/preimage.c +++ b/bitcoin/preimage.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/bitcoin/privkey.c b/bitcoin/privkey.c index e250a1313105..646d655a9f3d 100644 --- a/bitcoin/privkey.c +++ b/bitcoin/privkey.c @@ -1,4 +1,5 @@ -#include "privkey.h" +#include "config.h" +#include #include #include #include diff --git a/bitcoin/pubkey.c b/bitcoin/pubkey.c index 2e26259ec068..80807813fb0d 100644 --- a/bitcoin/pubkey.c +++ b/bitcoin/pubkey.c @@ -1,6 +1,7 @@ -#include "privkey.h" -#include "pubkey.h" +#include "config.h" #include +#include +#include #include #include #include diff --git a/bitcoin/script.c b/bitcoin/script.c index 6a3e6f8c4ab3..805a44a96d7d 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -1,9 +1,10 @@ -#include "address.h" -#include "locktime.h" -#include "preimage.h" -#include "pubkey.h" -#include "script.h" +#include "config.h" #include +#include +#include +#include +#include +#include #include #include #include diff --git a/bitcoin/shadouble.c b/bitcoin/shadouble.c index 775b893433e8..fb7299804357 100644 --- a/bitcoin/shadouble.c +++ b/bitcoin/shadouble.c @@ -1,4 +1,5 @@ -#include "shadouble.h" +#include "config.h" +#include #include #include #include diff --git a/bitcoin/short_channel_id.c b/bitcoin/short_channel_id.c index d67004d830ad..ff77ee2ac7d6 100644 --- a/bitcoin/short_channel_id.c +++ b/bitcoin/short_channel_id.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/bitcoin/signature.c b/bitcoin/signature.c index c835c680c157..4e0cda8ada98 100644 --- a/bitcoin/signature.c +++ b/bitcoin/signature.c @@ -1,11 +1,12 @@ -#include "privkey.h" -#include "pubkey.h" -#include "script.h" -#include "shadouble.h" -#include "signature.h" -#include "tx.h" +#include "config.h" #include +#include #include +#include +#include +#include +#include +#include #include #include #include diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index c4a23dfc9ccc..c00d99778be0 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../block.c" #include "../psbt.c" #include "../shadouble.c" diff --git a/bitcoin/varint.c b/bitcoin/varint.c index 290e28586335..4852af2ed65f 100644 --- a/bitcoin/varint.c +++ b/bitcoin/varint.c @@ -1,4 +1,5 @@ -#include "varint.h" +#include "config.h" +#include size_t varint_size(varint_t v) { diff --git a/ccan_compat.h b/ccan_compat.h index f0337f33f9cd..565327eafdc5 100644 --- a/ccan_compat.h +++ b/ccan_compat.h @@ -1,7 +1,12 @@ #ifndef LIGHTNING_CCAN_COMPAT_H #define LIGHTNING_CCAN_COMPAT_H + /* Magical file included from config.h (ie. everywhere) which renames * sha256 routines so they don't clash with libwally-core's internal ones */ + +/* So, for obvious reasons, this is an exception to the usual rule that we +#include "config.h" + * in all files. */ #define sha256(sha, p, size) ccan_sha256(sha, p, size) #define sha256_init(ctx) ccan_sha256_init(ctx) #define sha256_update(ctx, p, size) ccan_sha256_update(ctx, p, size) diff --git a/channeld/channeld.c b/channeld/channeld.c index 7849a24a83f5..895d92131984 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -10,6 +10,7 @@ * reading and writing synchronously we could deadlock if we hit buffer * limits, unlikely as that is. */ +#include "config.h" #include #include #include diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index 9c2f7499f7ae..1e87ae6ee908 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index 7dfeed50c7c6..7610fbd9a3b7 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index 1212f596f9b6..2a5112dee2d0 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../../common/blockheight_states.c" #include "../../common/channel_id.c" #include "../../common/fee_states.c" diff --git a/channeld/watchtower.c b/channeld/watchtower.c index cdf11a106fe0..a895dfc1d31b 100644 --- a/channeld/watchtower.c +++ b/channeld/watchtower.c @@ -1,7 +1,7 @@ -#include "watchtower.h" - +#include "config.h" #include #include +#include #include #include #include diff --git a/closingd/closingd.c b/closingd/closingd.c index 10a8279d202f..b3f54e3b7dfb 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/addr.c b/common/addr.c index c1ff6ce1034f..bbe073c97b5c 100644 --- a/common/addr.c +++ b/common/addr.c @@ -1,7 +1,8 @@ -#include "addr.h" +#include "config.h" #include #include #include +#include #include char *encode_scriptpubkey_to_addr(const tal_t *ctx, diff --git a/common/base32.c b/common/base32.c index efe3dad6e5a5..b6658bc46b13 100644 --- a/common/base32.c +++ b/common/base32.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/bech32.c b/common/bech32.c index de8b053024d1..7e949f6c3400 100644 --- a/common/bech32.c +++ b/common/bech32.c @@ -21,9 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "bech32.h" - +#include "config.h" #include +#include #include static uint32_t bech32_polymod_step(uint32_t pre) { diff --git a/common/bech32_util.c b/common/bech32_util.c index 26168b81469f..ed6b51d0214c 100644 --- a/common/bech32_util.c +++ b/common/bech32_util.c @@ -1,6 +1,7 @@ -#include "bech32_util.h" +#include "config.h" #include #include +#include static u8 get_bit(const u8 *src, size_t bitoff) { diff --git a/common/billboard.c b/common/billboard.c index a9f178e708bd..04a685e369d8 100644 --- a/common/billboard.c +++ b/common/billboard.c @@ -1,5 +1,6 @@ -#include "billboard.h" +#include "config.h" #include +#include #include char *billboard_message(const tal_t *ctx, diff --git a/common/bip32.c b/common/bip32.c index f4be88500dd7..3a29d9bda098 100644 --- a/common/bip32.c +++ b/common/bip32.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/blindedpath.c b/common/blindedpath.c index 7994813b769e..7b60c0f0ccea 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/blinding.c b/common/blinding.c index e74312c404f0..b92c731c0313 100644 --- a/common/blinding.c +++ b/common/blinding.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/blockheight_states.c b/common/blockheight_states.c index 3a5a151d13e2..b8f1843d7b3b 100644 --- a/common/blockheight_states.c +++ b/common/blockheight_states.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/bolt11.c b/common/bolt11.c index 09bb2e4336b9..19be87a69e8c 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/bolt11_json.c b/common/bolt11_json.c index b66dd5de3cb9..a97577e94959 100644 --- a/common/bolt11_json.c +++ b/common/bolt11_json.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/bolt12.c b/common/bolt12.c index 5ab64f31e1c6..6fa064bdc0e1 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/bolt12_merkle.c b/common/bolt12_merkle.c index d9085f86c8ee..575529f6180c 100644 --- a/common/bolt12_merkle.c +++ b/common/bolt12_merkle.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/channel_config.c b/common/channel_config.c index c4e991f4987e..55e32ae6dc66 100644 --- a/common/channel_config.c +++ b/common/channel_config.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/channel_id.c b/common/channel_id.c index 726297fa4231..517427b7469c 100644 --- a/common/channel_id.c +++ b/common/channel_id.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/channel_type.c b/common/channel_type.c index 890b191c6be4..87f0ab2bc3aa 100644 --- a/common/channel_type.c +++ b/common/channel_type.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/close_tx.c b/common/close_tx.c index 236d7ababce4..e1eb16ba6838 100644 --- a/common/close_tx.c +++ b/common/close_tx.c @@ -1,8 +1,8 @@ -#include "bitcoin/script.h" -#include "bitcoin/tx.h" -#include "close_tx.h" -#include "permute_tx.h" +#include "config.h" #include +#include +#include +#include #include struct bitcoin_tx *create_close_tx(const tal_t *ctx, diff --git a/common/configdir.c b/common/configdir.c index c71c001ff4c4..c03abe18cf9e 100644 --- a/common/configdir.c +++ b/common/configdir.c @@ -1,4 +1,4 @@ -#include "configdir.h" +#include "config.h" #include #include #include @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/common/crypto_state.c b/common/crypto_state.c index f056afc64fe0..d592ddfd3500 100644 --- a/common/crypto_state.c +++ b/common/crypto_state.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/crypto_sync.c b/common/crypto_sync.c index ad24251a07d8..93733884b073 100644 --- a/common/crypto_sync.c +++ b/common/crypto_sync.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/daemon_conn.c b/common/daemon_conn.c index 9d77f3729501..0a9a1ef2582c 100644 --- a/common/daemon_conn.c +++ b/common/daemon_conn.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/decode_array.c b/common/decode_array.c index c4df6f1034ba..8093282b7b16 100644 --- a/common/decode_array.c +++ b/common/decode_array.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/derive_basepoints.c b/common/derive_basepoints.c index 3b708da35bf7..cbb3b6525c3e 100644 --- a/common/derive_basepoints.c +++ b/common/derive_basepoints.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/descriptor_checksum.c b/common/descriptor_checksum.c index a29aec86d5a4..14487cb65441 100644 --- a/common/descriptor_checksum.c +++ b/common/descriptor_checksum.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/dijkstra.c b/common/dijkstra.c index e4d9c0ead84b..5607a1c606ab 100644 --- a/common/dijkstra.c +++ b/common/dijkstra.c @@ -1,5 +1,6 @@ /* Without this, gheap is *really* slow! Comment out for debugging. */ #define NDEBUG +#include "config.h" #include #include #include diff --git a/common/ecdh_hsmd.c b/common/ecdh_hsmd.c index d8afac9b9706..b8546ea99031 100644 --- a/common/ecdh_hsmd.c +++ b/common/ecdh_hsmd.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/features.c b/common/features.c index f84375989990..6647e7ff7455 100644 --- a/common/features.c +++ b/common/features.c @@ -1,7 +1,8 @@ -#include "features.h" +#include "config.h" #include #include #include +#include #include enum feature_copy_style { diff --git a/common/fee_states.c b/common/fee_states.c index 451d3849f150..4176972f8833 100644 --- a/common/fee_states.c +++ b/common/fee_states.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/gossip_rcvd_filter.c b/common/gossip_rcvd_filter.c index cf8e942d2dc5..0c6b3d350ff0 100644 --- a/common/gossip_rcvd_filter.c +++ b/common/gossip_rcvd_filter.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/hash_u5.c b/common/hash_u5.c index 00a1dea57e14..7e23a0da616a 100644 --- a/common/hash_u5.c +++ b/common/hash_u5.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/hmac.c b/common/hmac.c index 8cd7436f879a..3b83e3b28cf0 100644 --- a/common/hmac.c +++ b/common/hmac.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index 7e5778854d59..b66c3ea8784f 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/htlc_state.c b/common/htlc_state.c index 4a0e6312b113..75b841d3837e 100644 --- a/common/htlc_state.c +++ b/common/htlc_state.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include "htlc_state_names_gen.h" diff --git a/common/htlc_trim.c b/common/htlc_trim.c index dccfd26d993f..711f9ec7d857 100644 --- a/common/htlc_trim.c +++ b/common/htlc_trim.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/htlc_tx.c b/common/htlc_tx.c index 53ec9ab13a1f..50552128c449 100644 --- a/common/htlc_tx.c +++ b/common/htlc_tx.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/htlc_wire.c b/common/htlc_wire.c index 801c8124bf64..3a175e832e41 100644 --- a/common/htlc_wire.c +++ b/common/htlc_wire.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/initial_commit_tx.c b/common/initial_commit_tx.c index b2c9e956d06e..6da3fa3a0115 100644 --- a/common/initial_commit_tx.c +++ b/common/initial_commit_tx.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/io_lock.c b/common/io_lock.c index d32a510d0e71..05caaafbeaa6 100644 --- a/common/io_lock.c +++ b/common/io_lock.c @@ -1,6 +1,7 @@ -#include "io_lock.h" +#include "config.h" #include #include +#include struct io_lock { bool locked; diff --git a/common/iso4217.c b/common/iso4217.c index f00ade86eccb..a164e91c4c04 100644 --- a/common/iso4217.c +++ b/common/iso4217.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/json_helpers.c b/common/json_helpers.c index ee7f235318d6..17ee699357aa 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/json_stream.c b/common/json_stream.c index 48c7c7726474..0025eb5b82ea 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -1,3 +1,4 @@ +#include "config.h" #include /* To reach into io_plan: not a public header! */ #include diff --git a/common/json_tok.c b/common/json_tok.c index ad159cfd1003..ad88451eb10b 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/key_derive.c b/common/key_derive.c index 732a121fd44c..b5ed2424b1c3 100644 --- a/common/key_derive.c +++ b/common/key_derive.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/keyset.c b/common/keyset.c index 680d891c49c4..9630c76f7720 100644 --- a/common/keyset.c +++ b/common/keyset.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/onion.c b/common/onion.c index aeab17de1a53..69e0e32db059 100644 --- a/common/onion.c +++ b/common/onion.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/onionreply.c b/common/onionreply.c index 2d4c4c1b873b..6a6e58ff96b2 100644 --- a/common/onionreply.c +++ b/common/onionreply.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/param.c b/common/param.c index 40e5f0abbc7d..5c3b30ad5cab 100644 --- a/common/param.c +++ b/common/param.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/peer_billboard.c b/common/peer_billboard.c index 7ed816eb284d..840e97c7b76e 100644 --- a/common/peer_billboard.c +++ b/common/peer_billboard.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/peer_failed.c b/common/peer_failed.c index a6b0c55cdd40..97ec96cfab31 100644 --- a/common/peer_failed.c +++ b/common/peer_failed.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/permute_tx.c b/common/permute_tx.c index 3ab7aff7310e..bf97206d519e 100644 --- a/common/permute_tx.c +++ b/common/permute_tx.c @@ -1,4 +1,5 @@ -#include "permute_tx.h" +#include "config.h" +#include #include static bool input_better(const struct wally_tx_input *a, diff --git a/common/ping.c b/common/ping.c index bcb2f2555603..e7a32eebd192 100644 --- a/common/ping.c +++ b/common/ping.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/psbt_internal.c b/common/psbt_internal.c index 0a034ddc8ae1..19460f01e58f 100644 --- a/common/psbt_internal.c +++ b/common/psbt_internal.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/pseudorand.c b/common/pseudorand.c index 6e9a5d214087..c08e21849e54 100644 --- a/common/pseudorand.c +++ b/common/pseudorand.c @@ -1,9 +1,10 @@ -#include "pseudorand.h" +#include "config.h" #include #include #include #include #include +#include #include #include diff --git a/common/random_select.c b/common/random_select.c index 1acaeb00558c..47b5407f24d9 100644 --- a/common/random_select.c +++ b/common/random_select.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/shutdown_scriptpubkey.c b/common/shutdown_scriptpubkey.c index ceaf5dde39f9..866931ee506f 100644 --- a/common/shutdown_scriptpubkey.c +++ b/common/shutdown_scriptpubkey.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/socket_close.c b/common/socket_close.c index cf9cbcf98180..3c192e47e38d 100644 --- a/common/socket_close.c +++ b/common/socket_close.c @@ -1,5 +1,6 @@ -#include "socket_close.h" +#include "config.h" #include +#include #include #include #include diff --git a/common/status_levels.c b/common/status_levels.c index 99ddfd890913..8746ec8d9a80 100644 --- a/common/status_levels.c +++ b/common/status_levels.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/status_wire.c b/common/status_wire.c index 25afd376bd18..26ad92526e8e 100644 --- a/common/status_wire.c +++ b/common/status_wire.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/common/subdaemon.c b/common/subdaemon.c index 24b0a0a21972..6ed2e443815a 100644 --- a/common/subdaemon.c +++ b/common/subdaemon.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/test/run-amount.c b/common/test/run-amount.c index 780377574062..de4ac8458114 100644 --- a/common/test/run-amount.c +++ b/common/test/run-amount.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../amount.c" #include #include diff --git a/common/test/run-base64.c b/common/test/run-base64.c index 899e83b6b48e..3261d998f81a 100644 --- a/common/test/run-base64.c +++ b/common/test/run-base64.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../base64.c" #include #include diff --git a/common/test/run-bigsize.c b/common/test/run-bigsize.c index bf60a2f3da5e..85c82a2ac45a 100644 --- a/common/test/run-bigsize.c +++ b/common/test/run-bigsize.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index f0e0a36d902e..a1d32e5a3af3 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../bigsize.c" #include "../blinding.c" #include "../hmac.c" diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c index f20058b8183a..191e157af697 100644 --- a/common/test/run-blindedpath_onion.c +++ b/common/test/run-blindedpath_onion.c @@ -1,4 +1,5 @@ /* Pipe through jq for test vector! */ +#include "config.h" #include "../bigsize.c" #include "../blindedpath.c" #include "../blinding.c" diff --git a/common/test/run-bolt11.c b/common/test/run-bolt11.c index 0a0862eef8ae..2dbb1bd7e981 100644 --- a/common/test/run-bolt11.c +++ b/common/test/run-bolt11.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../amount.c" #include "../bech32.c" #include "../bech32_util.c" diff --git a/common/test/run-bolt12_merkle.c b/common/test/run-bolt12_merkle.c index 4799255fc26c..1610e3ea8737 100644 --- a/common/test/run-bolt12_merkle.c +++ b/common/test/run-bolt12_merkle.c @@ -1,4 +1,5 @@ #define SUPERVERBOSE printf +#include "config.h" /* Needed before including bolt12_merkle.c: */ #include #include diff --git a/common/test/run-bolt12_period.c b/common/test/run-bolt12_period.c index 556cf51b233f..e82ab6a0d0ae 100644 --- a/common/test/run-bolt12_period.c +++ b/common/test/run-bolt12_period.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../bolt12.c" #include "../json.c" #include diff --git a/common/test/run-cryptomsg.c b/common/test/run-cryptomsg.c index a072c2806fe7..963e49648b36 100644 --- a/common/test/run-cryptomsg.c +++ b/common/test/run-cryptomsg.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/test/run-derive_basepoints.c b/common/test/run-derive_basepoints.c index 5e2a636ff61f..e7ebaab864f9 100644 --- a/common/test/run-derive_basepoints.c +++ b/common/test/run-derive_basepoints.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../derive_basepoints.c" #include #include diff --git a/common/test/run-features.c b/common/test/run-features.c index fefc3f78a315..e93870521182 100644 --- a/common/test/run-features.c +++ b/common/test/run-features.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../features.c" #include "../memleak.c" #include diff --git a/common/test/run-gossip_rcvd_filter.c b/common/test/run-gossip_rcvd_filter.c index e15c098ee5ba..270df9bd4933 100644 --- a/common/test/run-gossip_rcvd_filter.c +++ b/common/test/run-gossip_rcvd_filter.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../gossip_rcvd_filter.c" #include "../pseudorand.c" #include "../../wire/fromwire.c" diff --git a/common/test/run-gossmap-fp16.c b/common/test/run-gossmap-fp16.c index 5b96d36817b1..a3df4b47f26e 100644 --- a/common/test/run-gossmap-fp16.c +++ b/common/test/run-gossmap-fp16.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../fp16.c" #include #include diff --git a/common/test/run-gossmap_guess_node_id.c b/common/test/run-gossmap_guess_node_id.c index f87204a6bd1e..8b4188bc58b4 100644 --- a/common/test/run-gossmap_guess_node_id.c +++ b/common/test/run-gossmap_guess_node_id.c @@ -1,4 +1,5 @@ /* Test conversion assumptions used by gossmap_guess_node_id */ +#include "config.h" #include "../node_id.c" #include #include diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 20b08d562fd4..605c2cb8d54b 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -1,4 +1,5 @@ /* Test local modifications to gossmap */ +#include "config.h" #include "../amount.c" #include "../fp16.c" #include "../gossmap.c" diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index 090b7f235879..2be030de60e3 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../common/base32.c" #include "../common/wireaddr.c" diff --git a/common/test/run-json.c b/common/test/run-json.c index c1e51ba2fb5a..711368786daf 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../json_helpers.c" #include #include diff --git a/common/test/run-json_scan.c b/common/test/run-json_scan.c index bd76b344756e..2da2119965b7 100644 --- a/common/test/run-json_scan.c +++ b/common/test/run-json_scan.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../json.c" #include #include diff --git a/common/test/run-key_derive.c b/common/test/run-key_derive.c index dd3c7c3d9f39..3c98cb50b1a3 100644 --- a/common/test/run-key_derive.c +++ b/common/test/run-key_derive.c @@ -1,5 +1,6 @@ #define SUPERVERBOSE +#include "config.h" #include #include #include diff --git a/common/test/run-lease_rates.c b/common/test/run-lease_rates.c index 08155e4ed8f4..034134bf03dd 100644 --- a/common/test/run-lease_rates.c +++ b/common/test/run-lease_rates.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../amount.c" #include "../lease_rates.c" #include diff --git a/common/test/run-lock.c b/common/test/run-lock.c index 579f99b711c2..36e5323a9875 100644 --- a/common/test/run-lock.c +++ b/common/test/run-lock.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../io_lock.c" #include #include diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 33ab55dced41..e58ef4689afc 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -5,6 +5,7 @@ * getchannels: * {'channels': [{'active': True, 'short_id': '6990x2x1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'last_update': 1504064344}, {'active': True, 'short_id': '6989x2x1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, {'active': True, 'short_id': '6990x2x1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, {'active': True, 'short_id': '6989x2x1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}]} */ +#include "config.h" #include #include #include diff --git a/common/test/run-route.c b/common/test/run-route.c index 0db4d77d90f2..e1fac36ebfe4 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/test/run-softref.c b/common/test/run-softref.c index 1f5368cafd49..b5f32fc4b6c9 100644 --- a/common/test/run-softref.c +++ b/common/test/run-softref.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/common/test/run-sphinx-xor_cipher_stream.c b/common/test/run-sphinx-xor_cipher_stream.c index a742cc5abc89..963e4bda1687 100644 --- a/common/test/run-sphinx-xor_cipher_stream.c +++ b/common/test/run-sphinx-xor_cipher_stream.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../sphinx.c" #include #include diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index 4b842f626e28..0145682d8a33 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../hmac.c" #include "../onion.c" #include "../onionreply.c" diff --git a/common/test/run-wireaddr.c b/common/test/run-wireaddr.c index c6e19d8b752b..42973fee8f66 100644 --- a/common/test/run-wireaddr.c +++ b/common/test/run-wireaddr.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../wireaddr.c" #include #include diff --git a/common/timeout.c b/common/timeout.c index ec6f2287ecc1..a19b85ffda6c 100644 --- a/common/timeout.c +++ b/common/timeout.c @@ -1,4 +1,5 @@ -#include "timeout.h" +#include "config.h" +#include #include struct oneshot { diff --git a/common/utils.c b/common/utils.c index 57bc8f02e956..b9fbded5b401 100644 --- a/common/utils.c +++ b/common/utils.c @@ -1,9 +1,10 @@ -#include "utils.h" +#include "config.h" #include #include #include #include #include +#include #include #include diff --git a/common/version.c b/common/version.c index f96c8798cc20..e64bc2595799 100644 --- a/common/version.c +++ b/common/version.c @@ -1,5 +1,6 @@ -#include "version.h" +#include "config.h" #include +#include #include #include diff --git a/common/wallet.c b/common/wallet.c index 9c0964f555df..599463e9f45d 100644 --- a/common/wallet.c +++ b/common/wallet.c @@ -1,3 +1,4 @@ +#include "config.h" #include enum wallet_tx_type fromwire_wallet_tx_type(const u8 **cursor, size_t *max) diff --git a/common/wire_error.c b/common/wire_error.c index 416d50a66156..189c573a852e 100644 --- a/common/wire_error.c +++ b/common/wire_error.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/connectd/connectd.c b/connectd/connectd.c index 8ed57ebb0b3f..3bebaae02c40 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -7,6 +7,7 @@ * up to lightningd which will fire up a specific per-peer daemon to talk to * it. */ +#include "config.h" #include #include #include diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 3f49a66f92ad..9398bb75adad 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/connectd/sha1.c b/connectd/sha1.c index d9e4951978a6..842ebed3613b 100644 --- a/connectd/sha1.c +++ b/connectd/sha1.c @@ -1,4 +1,5 @@ /* hex variants removed -- RR */ +#include "config.h" #include /******************************************************************************* diff --git a/connectd/tor.c b/connectd/tor.c index 4527fe3ddc08..95ba253abc42 100644 --- a/connectd/tor.c +++ b/connectd/tor.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index 5ee97f9bbe68..33c088eb0e8b 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index 2e6dc0064058..4191d0848e32 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/clean_topo.c b/devtools/clean_topo.c index 07f05d4322d8..532282221869 100644 --- a/devtools/clean_topo.c +++ b/devtools/clean_topo.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/devtools/create-gossipstore.c b/devtools/create-gossipstore.c index 0feaa083be21..b4f6980f0842 100644 --- a/devtools/create-gossipstore.c +++ b/devtools/create-gossipstore.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/decodemsg.c b/devtools/decodemsg.c index 03c40d4df818..8512b4793ea7 100644 --- a/devtools/decodemsg.c +++ b/devtools/decodemsg.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index f5a520285207..a99e918beb6e 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index f356f00089bb..0e2093e6764c 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -1,4 +1,5 @@ /* Simple tool to route gossip from a peer. */ +#include "config.h" #include #include #include diff --git a/devtools/lightning-checkmessage.c b/devtools/lightning-checkmessage.c index bd82bc552cf2..490111097eaf 100644 --- a/devtools/lightning-checkmessage.c +++ b/devtools/lightning-checkmessage.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/mkclose.c b/devtools/mkclose.c index eb2ada40d6df..f60b6175fe07 100644 --- a/devtools/mkclose.c +++ b/devtools/mkclose.c @@ -4,6 +4,7 @@ * * lightning/devtools/mkclose 189c40b0728f382fe91c87270926584e48e0af3a6789f37454afee6c7560311d 0 0.00999877btc 253 0.00999877btc 0000000000000000000000000000000000000000000000000000000000000010 0000000000000000000000000000000000000000000000000000000000000020 026957e53b46df017bd6460681d068e1d23a7b027de398272d0b15f59b78d060a9 03a9f795ff2e4c27091f40e8f8277301824d1c3dfa6b0204aa92347314e41b1033 */ +#include "config.h" #include #include #include diff --git a/devtools/mkcommit.c b/devtools/mkcommit.c index f6afbf32d402..288cae1185f6 100644 --- a/devtools/mkcommit.c +++ b/devtools/mkcommit.c @@ -8,6 +8,7 @@ 0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000021 0000000000000000000000000000000000000000000000000000000000000022 0000000000000000000000000000000000000000000000000000000000000023 0000000000000000000000000000000000000000000000000000000000000024 \ 0000000000000000000000000000000000000000000000000000000000000010 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 0000000000000000000000000000000000000000000000000000000000000011 0000000000000000000000000000000000000000000000000000000000000012 0000000000000000000000000000000000000000000000000000000000000013 0000000000000000000000000000000000000000000000000000000000000014 */ +#include "config.h" #include #include #include diff --git a/devtools/mkencoded.c b/devtools/mkencoded.c index f53fc8677f45..f76d2ab817ed 100644 --- a/devtools/mkencoded.c +++ b/devtools/mkencoded.c @@ -1,4 +1,5 @@ /* Simple wrapper to create zlib or raw encodings of hex. */ +#include "config.h" #include #include #include diff --git a/devtools/mkfunding.c b/devtools/mkfunding.c index 0eba19538035..878212de086a 100644 --- a/devtools/mkfunding.c +++ b/devtools/mkfunding.c @@ -6,6 +6,7 @@ * * lightning/devtools/mkfunding 16835ac8c154b616baac524163f41fb0c4f82c7b972ad35d4d6f18d854f6856b 3 0.03btc 253 16c5027616e940d1e72b4c172557b3b799a93c0582f924441174ea556aadd01c 0000000000000000000000000000000000000000000000000000000000000050 0000000000000000000000000000000000000000000000000000000000000060 */ +#include "config.h" #include #include #include diff --git a/devtools/print_wire.c b/devtools/print_wire.c index f9e591f38cd0..08bfc7cdbcb3 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/devtools/print_wire.h b/devtools/print_wire.h index 9fb969652e7f..5f9f2aff3833 100644 --- a/devtools/print_wire.h +++ b/devtools/print_wire.h @@ -1,5 +1,6 @@ #ifndef LIGHTNING_DEVTOOLS_PRINT_WIRE_H #define LIGHTNING_DEVTOOLS_PRINT_WIRE_H +#include "config.h" #include #include #include diff --git a/devtools/route.c b/devtools/route.c index 3fab83f8d431..68c1633d7373 100644 --- a/devtools/route.c +++ b/devtools/route.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 4ca12da625e3..7e71e5b81262 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -1,5 +1,6 @@ /* Routines to make our own gossip messages. Not as in "we're the gossip * generation, man!" */ +#include "config.h" #include #include #include diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index b5ac792a39f5..408e79adfd36 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -1,5 +1,4 @@ -#include "gossip_store.h" - +#include "config.h" #include #include #include @@ -10,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 5491ac0a106d..6b1c7a4ab821 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -10,6 +10,7 @@ * The gossip protocol itself is fairly simple, but has some twists which * add complexity to this daemon. */ +#include "config.h" #include #include #include diff --git a/gossipd/queries.c b/gossipd/queries.c index 2da8438913a4..b46a683b9a8e 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -1,4 +1,5 @@ /* Routines to generate and handle gossip query messages */ +#include "config.h" #include #include #include diff --git a/gossipd/routing.c b/gossipd/routing.c index f6cab15c17f0..d246b2ee53fc 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1,4 +1,4 @@ -#include "routing.h" +#include "config.h" #include #include #include @@ -13,6 +13,7 @@ #include #include #include +#include #ifndef SUPERVERBOSE #define SUPERVERBOSE(...) diff --git a/gossipd/seeker.c b/gossipd/seeker.c index 476d0a0121b2..fbff2dd1af52 100644 --- a/gossipd/seeker.c +++ b/gossipd/seeker.c @@ -1,4 +1,5 @@ /* This contains the code which actively seeks out gossip from peers */ +#include "config.h" #include #include #include diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 10cdd2e1eeb7..28b29a341031 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -27,6 +27,7 @@ bitcoin_key_2=02b3e55c7a1a6cdf17a83a801f7f8f698e4980323e2584f27a643a1b0519ebf8c7 In particular, we set feature bit 19. The spec says we should set feature bit 18 (which is clearly wrong!). */ +#include "config.h" #include "../common/wire_error.c" #include "../routing.c" #include diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 87ff8f188869..ca982558b22f 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../gossip_generation.c" #include #include diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 86483e8a26a3..87e6094b2aaa 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -1,3 +1,4 @@ +#include "config.h" int unused_main(int argc, char *argv[]); #define main unused_main #include "../queries.c" diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index 0be48d45d7eb..e298e35f9c95 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../seeker.c" #include #include diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 5289233dc4c4..6cfa824e8fcb 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -1,3 +1,4 @@ +#include "config.h" int unused_main(int argc, char *argv[]); #define main unused_main #include "../gossipd.c" diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 29685664f15d..09b1ddbba162 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../routing.c" #include "../common/timeout.c" #include diff --git a/hsmd/capabilities.h b/hsmd/capabilities.h index a95177901a61..e538ed35b3f1 100644 --- a/hsmd/capabilities.h +++ b/hsmd/capabilities.h @@ -1,5 +1,6 @@ #ifndef LIGHTNING_HSMD_CAPABILITIES_H #define LIGHTNING_HSMD_CAPABILITIES_H +#include "config.h" #define HSM_CAP_ECDH 1 #define HSM_CAP_SIGN_GOSSIP 2 diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 9e038539c43b..8ea2fba9520a 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -6,6 +6,7 @@ * which indicates what it's allowed to ask for. We're entirely driven * by request, response. */ +#include "config.h" #include #include #include diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index c06db8d4b0b3..ece1f85f89fb 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/hsmd/libhsmd.h b/hsmd/libhsmd.h index 5918fa57b11a..05d427cc9eea 100644 --- a/hsmd/libhsmd.h +++ b/hsmd/libhsmd.h @@ -1,6 +1,7 @@ #ifndef LIGHTNING_HSMD_LIBHSMD_H #define LIGHTNING_HSMD_LIBHSMD_H +#include "config.h" #include #include #include diff --git a/hsmd/libhsmd_status.c b/hsmd/libhsmd_status.c index 41ac3295a87d..919e01879f7f 100644 --- a/hsmd/libhsmd_status.c +++ b/hsmd/libhsmd_status.c @@ -11,6 +11,7 @@ * primitives to link time, and here we provide simple ones that just * print to stdout, as alternatives to the status wire protocol ones. */ +#include "config.h" #include #include u8 *hsmd_status_bad_request(struct hsmd_client *client, const u8 *msg, const char *error) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index ba6bf9d63cba..91711b62e072 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -3,21 +3,22 @@ * by using bitcoin-cli, but the interface we use to gather Bitcoin data is * standardized and you can use another plugin as the Bitcoin backend, or * even make your own! */ -#include "bitcoin/base58.h" -#include "bitcoin/block.h" -#include "bitcoin/feerate.h" -#include "bitcoin/script.h" -#include "bitcoin/shadouble.h" -#include "bitcoind.h" -#include "lightningd.h" -#include "log.h" +#include "config.h" +#include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include +#include +#include #include /* The names of the requests we can make to our Bitcoin backend. */ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index e3a9899ba78a..c7070c70caae 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -1,14 +1,7 @@ -#include "bitcoin/block.h" -#include "bitcoin/feerate.h" -#include "bitcoin/script.h" -#include "bitcoin/tx.h" -#include "bitcoind.h" -#include "chaintopology.h" -#include "channel.h" -#include "jsonrpc.h" -#include "lightningd.h" -#include "log.h" -#include "watch.h" +#include "config.h" +#include +#include +#include #include #include #include @@ -18,10 +11,16 @@ #include #include #include +#include +#include +#include #include #include #include #include +#include +#include +#include #include #include diff --git a/lightningd/channel.c b/lightningd/channel.c index 13b7ba8ed330..4f7b3a0bebbd 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index c0169222097a..b8d90cbcba23 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index eef5e921cfc3..96f600c7889a 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 068d5b122b73..d371ac28e90e 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index d17d3e780194..a01366d5a1a8 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/datastore.c b/lightningd/datastore.c index a58a8f1151d0..74b8a7cfce72 100644 --- a/lightningd/datastore.c +++ b/lightningd/datastore.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 5fdf7d5b19f4..c546eb75eeb7 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -2,6 +2,7 @@ * dualopend subdaemons. It manages the callbacks and database * saves and funding tx watching for a channel open */ +#include "config.h" #include #include #include diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index a1bd82203942..d64575220a04 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -1,9 +1,4 @@ -#include "bitcoind.h" -#include "chaintopology.h" -#include "gossip_control.h" -#include "lightningd.h" -#include "peer_control.h" -#include "subd.h" +#include "config.h" #include #include #include @@ -11,9 +6,15 @@ #include #include #include +#include +#include +#include #include #include +#include #include +#include +#include static void got_txout(struct bitcoind *bitcoind, const struct bitcoin_tx_output *output, diff --git a/lightningd/gossip_msg.c b/lightningd/gossip_msg.c index 6b51248a7ff0..20dcc5f97625 100644 --- a/lightningd/gossip_msg.c +++ b/lightningd/gossip_msg.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index ec28cab8c437..323ebaba2ee9 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -1,6 +1,4 @@ -#include "hsm_control.h" -#include "lightningd.h" -#include "subd.h" +#include "config.h" #include #include #include @@ -10,8 +8,11 @@ #include #include #include +#include #include #include +#include +#include #include #include #include diff --git a/lightningd/htlc_end.c b/lightningd/htlc_end.c index 461d77d17a18..a5225f119c3d 100644 --- a/lightningd/htlc_end.c +++ b/lightningd/htlc_end.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/htlc_set.c b/lightningd/htlc_set.c index 8db291a1eb59..40e5caa6881b 100644 --- a/lightningd/htlc_set.c +++ b/lightningd/htlc_set.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 07d176aa16b3..9c37ca30a76c 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1,4 +1,4 @@ -#include "invoice.h" +#include "config.h" #include #include #include @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/io_loop_with_timers.c b/lightningd/io_loop_with_timers.c index 7daa1f375b99..20f9b0618bb2 100644 --- a/lightningd/io_loop_with_timers.c +++ b/lightningd/io_loop_with_timers.c @@ -1,7 +1,7 @@ -#include "io_loop_with_timers.h" - +#include "config.h" #include #include +#include #include void *io_loop_with_timers(struct lightningd *ld) diff --git a/lightningd/json.c b/lightningd/json.c index 73d1837aafca..93e30f2b8b7e 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index abaa940d15c8..e80d3edbc07c 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -13,7 +13,7 @@ * that point, the `json_connection` becomes the owner (or it's simply freed). */ /* eg: { "jsonrpc":"2.0", "method" : "dev-echo", "params" : [ "hello", "Arabella!" ], "id" : "1" } */ -#include "ccan/config.h" +#include "config.h" #include #include #include diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 3df71745c438..5a9f6c9d19c8 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -17,12 +17,9 @@ /*~ Notice how includes are in ASCII order: this is actually enforced by * the build system under `make check-source`. It avoids merge conflicts - * and keeps things consistent. */ -#include "gossip_control.h" -#include "hsm_control.h" -#include "lightningd.h" -#include "peer_control.h" -#include "subd.h" + * and keeps things consistent. It also make sure you include "config.h" + * before anything else. */ +#include "config.h" /*~ This is Ian Lance Taylor's libbacktrace. It turns out that it's * horrifically difficult to obtain a decent backtrace in C; the standard @@ -65,10 +62,15 @@ #include #include #include +#include +#include #include +#include #include #include +#include #include +#include #include #include #include diff --git a/lightningd/log.c b/lightningd/log.c index 84603aea1112..292eba39c66c 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -1,4 +1,4 @@ -#include "log.h" +#include "config.h" #include #include #include @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/log_status.c b/lightningd/log_status.c index a999f9955a21..314882c283f3 100644 --- a/lightningd/log_status.c +++ b/lightningd/log_status.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 0996ab83b8e0..9503decbe39e 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -1,5 +1,5 @@ /* Only possible if we're in developer mode. */ -#include "memdump.h" +#include "config.h" #if DEVELOPER #include #include @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/notification.c b/lightningd/notification.c index f8bdd2924306..82612e6a2753 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/offer.c b/lightningd/offer.c index 09734163c399..e8118a493e70 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index c325a6c9de17..da24e6c4c2db 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 88034edc8006..e6e51f5ebccd 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index d203aed8d2a4..9af928d28b8a 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 4df90dc51abe..83f73b9a0886 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1,4 +1,5 @@ -#include "bitcoin/feerate.h" +#include "config.h" +#include #include #include #include diff --git a/lightningd/options.c b/lightningd/options.c index 9fb1eac0ffab..c335425c3e38 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/pay.c b/lightningd/pay.c index c6f414f2ee87..f4db4fc527f5 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1,4 +1,4 @@ -#include "pay.h" +#include "config.h" #include #include #include @@ -14,6 +14,7 @@ #include #include #include +#include #include /* Routing failure object */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index a71bc358e461..415b4d69f467 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1,6 +1,4 @@ -#include "lightningd.h" -#include "peer_control.h" -#include "subd.h" +#include "config.h" #include #include #include @@ -50,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -57,8 +56,10 @@ #include #include #include +#include #include #include +#include #include #include #include diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 2820cd28a614..5914deb25829 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/ping.c b/lightningd/ping.c index eca50097d29e..e97351595ae7 100644 --- a/lightningd/ping.c +++ b/lightningd/ping.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 62f3a8e5fe1d..d37b6135cac1 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/plugin_control.c b/lightningd/plugin_control.c index 05f5e6723e7c..9bd62d57c621 100644 --- a/lightningd/plugin_control.c +++ b/lightningd/plugin_control.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index b7411e84cb8c..a1aa68cca992 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/routehint.c b/lightningd/routehint.c index 4da52679afa1..4d4ebd6adb0b 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index d0d38f42cc10..44dfe52bfae7 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/subd.c b/lightningd/subd.c index 178928eda716..ee9d0d79d4b9 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 02605e39a0d2..41db06076c4d 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -1,3 +1,4 @@ +#include "config.h" #define main unused_main int unused_main(int argc, char *argv[]); #include "../../common/base32.c" diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 6a3e6d055989..242b8b689de5 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../invoice.c" #include "../peer_control.c" #include "../routehint.c" diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 3505dfd79879..1479612d1138 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../../common/json_stream.c" #include "../jsonrpc.c" #include "../json.c" diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index d029aac3498a..6bda5f337c18 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../log.c" #include diff --git a/lightningd/watch.c b/lightningd/watch.c index ca7f0acdef88..48b5d7d834ea 100644 --- a/lightningd/watch.c +++ b/lightningd/watch.c @@ -26,6 +26,7 @@ * * WE ASSUME NO MALLEABILITY! This requires segregated witness. */ +#include "config.h" #include #include #include diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 87bce4eb9a3b..47fa0d3d58a5 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/onchaind/onchaind_wire.c b/onchaind/onchaind_wire.c index 8548b97b5c63..389b86572a8a 100644 --- a/onchaind/onchaind_wire.c +++ b/onchaind/onchaind_wire.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 87268cbba0a2..cdcc6bbd8054 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -2,6 +2,7 @@ * No valid signature found for 3 htlc_timeout_txs feerate 10992-15370, last tx 0200000001a02a38c6ec5541963704a2a035b3094b18d69cc25cc7419d75e02894618329720000000000000000000191ea3000000000002200208bfadb3554f41cc06f00de0ec2e2f91e36ee45b5006a1f606146784755356ba532f10800, input 3215967sat, signature 3045022100917efdc8577e8578aef5e513fad25edbb55921466e8ffccb05ce8bb05a54ae6902205c2fded9d7bfc290920821bfc828720bc24287f3dad9a62fb4f806e2404ed0f401, cltvs 585998/585998/586034 wscripts 76a914f454b1fe5b95428d6beec58ed3131a6ea611b2fa8763ac672103f83ca95b22920e71487736a7284696dd52443fd8f7ce683153ac31d1d1db7da67c820120876475527c21026ebaa1d08757b86110e40e3f4a081803eec694e23ec75ee0bfd753589df896e752ae67a9148dbcec4a5d782dd87588801607ea7dfc8874ffee88ac6868/76a914f454b1fe5b95428d6beec58ed3131a6ea611b2fa8763ac672103f83ca95b22920e71487736a7284696dd52443fd8f7ce683153ac31d1d1db7da67c820120876475527c21026ebaa1d08757b86110e40e3f4a081803eec694e23ec75ee0bfd753589df896e752ae67a9148dbcec4a5d782dd87588801607ea7dfc8874ffee88ac6868/76a914f454b1fe5b95428d6beec58ed3131a6ea611b2fa8763ac672103f83ca95b22920e71487736a7284696dd52443fd8f7ce683153ac31d1d1db7da67c820120876475527c21026ebaa1d08757b86110e40e3f4a081803eec694e23ec75ee0bfd753589df896e752ae67a9148dbcec4a5d782dd87588801607ea7dfc8874ffee88ac6868 (version v0.7.1-57-gb3215a8)" */ +#include "config.h" #include #include #include diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index d15c6afc968e..a59812b5d67c 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/openingd/common.c b/openingd/common.c index f9b0b112cc2b..a5d6ff41285a 100644 --- a/openingd/common.c +++ b/openingd/common.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 795fce83cb10..163c2bed1a0b 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -11,6 +11,7 @@ * new and improved, two-party opening protocol, which allows bother peers to * contribute inputs to the transaction */ +#include "config.h" #include #include #include diff --git a/openingd/openingd.c b/openingd/openingd.c index c8d2cc0d286d..1464b72ac76f 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -7,6 +7,7 @@ * there's nothing permanent about the channel: lightningd will only have to * commit to the database once openingd succeeds. */ +#include "config.h" #include #include #include diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 3218dc65fb2f..360848c159dc 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/bcli.c b/plugins/bcli.c index a223f2c18c13..d4c1bb4213c5 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index afc185f0c7d5..91f8cc9a8851 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/funder_policy.c b/plugins/funder_policy.c index b079637e64ec..e28fe679cf91 100644 --- a/plugins/funder_policy.c +++ b/plugins/funder_policy.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/keysend.c b/plugins/keysend.c index e0a8e36f490b..4c762c3dbe46 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 88ce1b199cbd..0a3c32546793 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/libplugin.c b/plugins/libplugin.c index d24c8514772c..e77fecb7b4b2 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/offers.c b/plugins/offers.c index 7e6a933a1d4d..4e64cbfb7aac 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -1,4 +1,5 @@ /* This plugin covers both sending and receiving offers */ +#include "config.h" #include #include #include diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index 765e2da3e46b..cc8731ae6c0c 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 4562c7d7dbce..0b7866603b6a 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/offers_offer.c b/plugins/offers_offer.c index 54c97b97bd8f..a7bebf17d529 100644 --- a/plugins/offers_offer.c +++ b/plugins/offers_offer.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/pay.c b/plugins/pay.c index cbd2c7c15c79..b454d7a8c0a0 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index fa1e2db25c9a..f0b2b1a4e247 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/spender/main.c b/plugins/spender/main.c index 717613319d8f..23698567aed4 100644 --- a/plugins/spender/main.c +++ b/plugins/spender/main.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/spender/multiwithdraw.c b/plugins/spender/multiwithdraw.c index 941c84d6a710..afe7a3534c7f 100644 --- a/plugins/spender/multiwithdraw.c +++ b/plugins/spender/multiwithdraw.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/test/run-funder_policy.c b/plugins/test/run-funder_policy.c index eecaa89e4475..8efb543e5958 100644 --- a/plugins/test/run-funder_policy.c +++ b/plugins/test/run-funder_policy.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../funder_policy.c" #include #include diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index ce67a15a9a6b..3999a31cfe87 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../libplugin-pay.c" #include #include diff --git a/plugins/topology.c b/plugins/topology.c index cfc3bd8eae21..9c5444222936 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/plugins/txprepare.c b/plugins/txprepare.c index dcb514a4912d..82776adc0f19 100644 --- a/plugins/txprepare.c +++ b/plugins/txprepare.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/tests/fuzz/fuzz-addr.c b/tests/fuzz/fuzz-addr.c index 57c54ac4795d..96379da48054 100644 --- a/tests/fuzz/fuzz-addr.c +++ b/tests/fuzz/fuzz-addr.c @@ -1,3 +1,4 @@ +#include "config.h" #include "common/utils.h" #include diff --git a/tests/fuzz/fuzz-descriptor_checksum.c b/tests/fuzz/fuzz-descriptor_checksum.c index 7b37efc8302b..a9a8dd6f519a 100644 --- a/tests/fuzz/fuzz-descriptor_checksum.c +++ b/tests/fuzz/fuzz-descriptor_checksum.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/tests/fuzz/libfuzz.c b/tests/fuzz/libfuzz.c index 91aceddd3659..8612846e38a6 100644 --- a/tests/fuzz/libfuzz.c +++ b/tests/fuzz/libfuzz.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include diff --git a/tests/fuzz/libfuzz.h b/tests/fuzz/libfuzz.h index 3d28f74d3633..3884a33fc13f 100644 --- a/tests/fuzz/libfuzz.h +++ b/tests/fuzz/libfuzz.h @@ -1,6 +1,7 @@ #ifndef LIGHTNING_TESTS_FUZZ_LIBFUZZ_H #define LIGHTNING_TESTS_FUZZ_LIBFUZZ_H +#include "config.h" #include #include #include diff --git a/tests/plugins/test_libplugin.c b/tests/plugins/test_libplugin.c index 49440be826b1..bb786a1b8fcf 100644 --- a/tests/plugins/test_libplugin.c +++ b/tests/plugins/test_libplugin.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/tests/plugins/test_selfdisable_after_getmanifest.c b/tests/plugins/test_selfdisable_after_getmanifest.c index 69c07c3e1e87..443aae09bafe 100644 --- a/tests/plugins/test_selfdisable_after_getmanifest.c +++ b/tests/plugins/test_selfdisable_after_getmanifest.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/tools/check-bolt.c b/tools/check-bolt.c index c915fb174169..2149caf898cb 100644 --- a/tools/check-bolt.c +++ b/tools/check-bolt.c @@ -1,5 +1,6 @@ /* Simple program to search for BOLT references in C files and make sure * they're accurate. */ +#include "config.h" #include #include #include diff --git a/tools/check-includes.sh b/tools/check-includes.sh index e75c20b75132..74d0e1b2c55f 100755 --- a/tools/check-includes.sh +++ b/tools/check-includes.sh @@ -20,6 +20,11 @@ do echo EXIT_CODE=1 fi + # Ignore contrib/. + if [ "${HEADER_FILE##contrib/}" = "$HEADER_FILE" -a "$(grep '#include' $HEADER_FILE | head -n1)" != '#include "config.h"' ]; then + echo "${HEADER_FILE}:1:does not include config.h first" + EXIT_CODE=1 + fi done # Check redundant includes @@ -62,6 +67,11 @@ for C_FILE in $(filter_suffix c); do echo "${C_FILE} does not include $H_FILE" >& 2 EXIT_CODE=1 fi + # Ignore contrib/. + if [ "${C_FILE##contrib/}" = "$C_FILE" -a "$(grep '#include' $C_FILE | head -n1)" != '#include "config.h"' ]; then + echo "${C_FILE}:1:does not include config.h first" + EXIT_CODE=1 + fi done exit ${EXIT_CODE} diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 74e5d3408bf8..5f5457a2e6d1 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/tools/test/enum.c b/tools/test/enum.c index df77744bcd0c..b8ad28ee5b73 100644 --- a/tools/test/enum.c +++ b/tools/test/enum.c @@ -1,3 +1,4 @@ +#include "config.h" #include "enum.h" #include diff --git a/tools/test/enum.h b/tools/test/enum.h index dee45be09ba2..702105e94531 100644 --- a/tools/test/enum.h +++ b/tools/test/enum.h @@ -1,5 +1,6 @@ #ifndef LIGHTNING_TOOLS_TEST_ENUM_H #define LIGHTNING_TOOLS_TEST_ENUM_H +#include "config.h" #include #include diff --git a/tools/test/run-test-wire.c b/tools/test/run-test-wire.c index 5b8e3ec1b469..28ae95df0085 100644 --- a/tools/test/run-test-wire.c +++ b/tools/test/run-test-wire.c @@ -1,3 +1,4 @@ +#include "config.h" #include "test_gen.h" #include "print_gen.h" diff --git a/wallet/db.c b/wallet/db.c index e10217b6a098..f0117c063390 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1,5 +1,4 @@ -#include "db.h" - +#include "config.h" #include #include #include @@ -13,6 +12,7 @@ #include #include #include +#include #include #include diff --git a/wallet/db_postgres.c b/wallet/db_postgres.c index 34f6562d8bb2..91db5beffa32 100644 --- a/wallet/db_postgres.c +++ b/wallet/db_postgres.c @@ -1,8 +1,9 @@ -#include "db_postgres_sqlgen.c" +#include "config.h" #include #include #include #include +#include #if HAVE_POSTGRES /* Indented in order not to trigger the inclusion order check */ diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index 6cc324f6ed63..4459e6b2e45c 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -1,3 +1,4 @@ +#include "config.h" #include "db_sqlite3_sqlgen.c" #include #include diff --git a/wallet/invoices.c b/wallet/invoices.c index 1142dcb80ca7..1d6e268ec0df 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -1,8 +1,9 @@ -#include "db.h" -#include "invoices.h" -#include "wallet.h" +#include "config.h" #include #include +#include +#include +#include struct invoice_waiter { /* Is this waiter already triggered? */ diff --git a/wallet/reservation.c b/wallet/reservation.c index 46ba907b83bb..eea112e185b8 100644 --- a/wallet/reservation.c +++ b/wallet/reservation.c @@ -1,4 +1,5 @@ /* Dealing with reserving UTXOs */ +#include "config.h" #include #include #include diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 9f7e17d6a253..1766613246ae 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -1,3 +1,4 @@ +#include "config.h" #include static void db_test_fatal(const char *fmt, ...); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 43425ab0501f..213b54c44df7 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1,3 +1,4 @@ +#include "config.h" #include static void wallet_test_fatal(const char *fmt, ...); diff --git a/wallet/test/test_utils.c b/wallet/test/test_utils.c index ef1fd8b0b063..69c514d40387 100644 --- a/wallet/test/test_utils.c +++ b/wallet/test/test_utils.c @@ -1,3 +1,4 @@ +#include "config.h" #include "test_utils.h" /* diff --git a/wallet/test/test_utils.h b/wallet/test/test_utils.h index 8500b60bc868..fa322f8e12d3 100644 --- a/wallet/test/test_utils.h +++ b/wallet/test/test_utils.h @@ -1,6 +1,7 @@ #ifndef LIGHTNING_WALLET_TEST_TEST_UTILS_H #define LIGHTNING_WALLET_TEST_TEST_UTILS_H +#include "config.h" #include "lightningd/lightningd.h" /* Definitions "inspired" by libsecp256k1 */ diff --git a/wallet/txfilter.c b/wallet/txfilter.c index 1e4c47ab9aa2..c801da7fc6af 100644 --- a/wallet/txfilter.c +++ b/wallet/txfilter.c @@ -1,10 +1,10 @@ -#include "txfilter.h" - +#include "config.h" #include #include #include #include #include +#include #include static size_t scriptpubkey_hash(const u8 *out) diff --git a/wallet/wallet.c b/wallet/wallet.c index 29bd385e5fd2..e035686f4425 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1,6 +1,4 @@ -#include "invoices.h" -#include "wallet.h" - +#include "config.h" #include #include #include @@ -17,7 +15,9 @@ #include #include #include +#include #include +#include #include #define SQLITE_MAX_UINT 0x7FFFFFFFFFFFFFFF diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 6fe87d0bb334..5c1fa2e7aabe 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -1,3 +1,4 @@ +#include "config.h" #include #include #include diff --git a/wire/fromwire.c b/wire/fromwire.c index 4121180b9b5b..e960f6e8e684 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -1,9 +1,10 @@ -#include "wire.h" +#include "config.h" #include #include #include #include #include +#include #ifndef SUPERVERBOSE #define SUPERVERBOSE(...) diff --git a/wire/peer_wire.c b/wire/peer_wire.c index aa3f3133eb50..340085d282cc 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -1,3 +1,4 @@ +#include "config.h" #include static bool unknown_type(enum peer_wire t) diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 91a641da4599..c6b025c41524 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -1,3 +1,4 @@ +#include "config.h" #include "../towire.c" #include "../fromwire.c" #include "../peer_wire.c" diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index 3c06a7e9f1dc..9fe2959d165d 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -1,3 +1,4 @@ +#include "config.h" #include static const char *reason; diff --git a/wire/towire.c b/wire/towire.c index d4697997e62e..6b104dd603d8 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -1,3 +1,4 @@ +#include "config.h" #include "wire.h" #include #include diff --git a/wire/wire_io.c b/wire/wire_io.c index 39af137e45bc..0d7054ded7b1 100644 --- a/wire/wire_io.c +++ b/wire/wire_io.c @@ -1,3 +1,4 @@ +#include "config.h" /* FIXME: io_plan needs size_t */ #include #include From c3899806eed074d1d1ef20f3b4c6ade5f5bab749 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 4 Dec 2021 21:53:58 +1030 Subject: [PATCH 0110/1530] common: remove unused io_lock.c Signed-off-by: Rusty Russell --- common/Makefile | 1 - common/io_lock.c | 88 --------------- common/io_lock.h | 54 ---------- common/test/run-lock.c | 227 --------------------------------------- lightningd/Makefile | 1 - lightningd/test/Makefile | 1 - 6 files changed, 372 deletions(-) delete mode 100644 common/io_lock.c delete mode 100644 common/io_lock.h delete mode 100644 common/test/run-lock.c diff --git a/common/Makefile b/common/Makefile index 44d02c1c2252..2f5368ad6f6a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -47,7 +47,6 @@ COMMON_SRC_NOGEN := \ common/htlc_wire.c \ common/initial_channel.c \ common/initial_commit_tx.c \ - common/io_lock.c \ common/iso4217.c \ common/json.c \ common/json_helpers.c \ diff --git a/common/io_lock.c b/common/io_lock.c deleted file mode 100644 index 05caaafbeaa6..000000000000 --- a/common/io_lock.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "config.h" -#include -#include -#include - -struct io_lock { - bool locked; -}; - -/* Struct to hold information while we wait for the lock to be freed */ -struct io_lock_waiter { - struct io_plan *(*next)(struct io_conn *conn, void *next_arg); - void *arg; - struct io_lock *lock; - enum io_direction dir; -}; - -struct io_lock *io_lock_new(const tal_t *ctx) -{ - struct io_lock *lock = tal(ctx, struct io_lock); - lock->locked = false; - return lock; -} - -static struct io_plan *io_lock_try_acquire(struct io_conn *conn, - struct io_lock_waiter *waiter) -{ - /* Destructure waiter, since we might be freeing it below */ - struct io_plan *(*next)(struct io_conn *, void *) = waiter->next; - void *next_arg = waiter->arg; - - if (!waiter->lock->locked) { - waiter->lock->locked = true; - tal_free(waiter); - return next(conn, next_arg); - } else { - switch (waiter->dir) { - case IO_IN: - return io_wait(conn, waiter->lock, io_lock_try_acquire, - waiter); - case IO_OUT: - return io_out_wait(conn, waiter->lock, - io_lock_try_acquire, waiter); - } - /* Should not happen if waiter->dir is a valid enum - * value */ - abort(); - } -} - -static struct io_plan *io_lock_acquire_dir( - struct io_conn *conn, struct io_lock *lock, enum io_direction dir, - struct io_plan *(*next)(struct io_conn *, void *), void *arg) -{ - /* FIXME: We can avoid one allocation if we lock and call next here directly */ - struct io_lock_waiter *waiter = tal(lock, struct io_lock_waiter); - waiter->next = next; - waiter->arg = arg; - waiter->lock = lock; - waiter->dir = dir; - return io_lock_try_acquire(conn, waiter); -} - -struct io_plan * -io_lock_acquire_out_(struct io_conn *conn, struct io_lock *lock, - struct io_plan *(*next)(struct io_conn *, void *), void *arg) -{ - return io_lock_acquire_dir(conn, lock, IO_OUT, next, arg); -} - -struct io_plan * -io_lock_acquire_in_(struct io_conn *conn, struct io_lock *lock, - struct io_plan *(*next)(struct io_conn *, void *), void *arg) -{ - return io_lock_acquire_dir(conn, lock, IO_IN, next, arg); -} - -void io_lock_release(struct io_lock *lock) -{ - assert(lock->locked); - lock->locked = false; - io_wake(lock); -} - -bool io_lock_taken(const struct io_lock *lock) -{ - return lock->locked; -} diff --git a/common/io_lock.h b/common/io_lock.h deleted file mode 100644 index 03823c47d79f..000000000000 --- a/common/io_lock.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef LIGHTNING_COMMON_IO_LOCK_H -#define LIGHTNING_COMMON_IO_LOCK_H - -#include "config.h" -#include -struct io_lock; - -/** - * Create a new lock - */ -struct io_lock *io_lock_new(const tal_t *ctx); - -/** - * Acquire lock @lock before proceeding to @next - * - * Attempts to acquire the lock before proceeding with next. If the - * lock is free this reduces to `io_always`, otherwise we put @conn in - * wait until we get notified about the lock being released. - */ -#define io_lock_acquire_out(conn, lock, next, arg) \ - io_lock_acquire_out_((conn), (lock), \ - typesafe_cb_preargs(struct io_plan *, void *, \ - (next), (arg), \ - struct io_conn *), \ - (arg)) - -struct io_plan *io_lock_acquire_out_(struct io_conn *conn, struct io_lock *lock, - struct io_plan *(*next)(struct io_conn *, - void *), - void *arg); - -#define io_lock_acquire_in(conn, lock, next, arg) \ - io_lock_acquire_in_((conn), (lock), \ - typesafe_cb_preargs(struct io_plan *, void *, \ - (next), (arg), \ - struct io_conn *), \ - (arg)) - -struct io_plan *io_lock_acquire_in_(struct io_conn *conn, struct io_lock *lock, - struct io_plan *(*next)(struct io_conn *, - void *), - void *arg); - -/** - * Release the lock and notify waiters so they can proceed. - */ -void io_lock_release(struct io_lock *lock); - -/** - * Is this lock acquired? - */ -bool io_lock_taken(const struct io_lock *lock); - -#endif /* LIGHTNING_COMMON_IO_LOCK_H */ diff --git a/common/test/run-lock.c b/common/test/run-lock.c deleted file mode 100644 index 36e5323a9875..000000000000 --- a/common/test/run-lock.c +++ /dev/null @@ -1,227 +0,0 @@ -#include "config.h" -#include "../io_lock.c" -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for amount_asset_is_main */ -bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) -{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } -/* Generated stub for amount_asset_to_sat */ -struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) -{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } -/* Generated stub for amount_sat */ -struct amount_sat amount_sat(u64 satoshis UNNEEDED) -{ fprintf(stderr, "amount_sat called!\n"); abort(); } -/* Generated stub for amount_sat_add */ - bool amount_sat_add(struct amount_sat *val UNNEEDED, - struct amount_sat a UNNEEDED, - struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } -/* Generated stub for amount_sat_eq */ -bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } -/* Generated stub for amount_sat_greater_eq */ -bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } -/* Generated stub for amount_sat_sub */ - bool amount_sat_sub(struct amount_sat *val UNNEEDED, - struct amount_sat a UNNEEDED, - struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } -/* Generated stub for amount_sat_to_asset */ -struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) -{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } -/* Generated stub for amount_tx_fee */ -struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) -{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for fromwire */ -const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) -{ fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } -/* Generated stub for fromwire_bool */ -bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } -/* Generated stub for fromwire_fail */ -void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } -/* Generated stub for fromwire_secp256k1_ecdsa_signature */ -void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for fromwire_sha256 */ -void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } -/* Generated stub for fromwire_tal_arrn */ -u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, - const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } -/* Generated stub for fromwire_u32 */ -u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } -/* Generated stub for fromwire_u64 */ -u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } -/* Generated stub for fromwire_u8 */ -u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } -/* Generated stub for fromwire_u8_array */ -void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for towire */ -void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } -/* Generated stub for towire_bool */ -void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) -{ fprintf(stderr, "towire_bool called!\n"); abort(); } -/* Generated stub for towire_secp256k1_ecdsa_signature */ -void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, - const secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for towire_sha256 */ -void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } -/* Generated stub for towire_u32 */ -void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) -{ fprintf(stderr, "towire_u32 called!\n"); abort(); } -/* Generated stub for towire_u64 */ -void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) -{ fprintf(stderr, "towire_u64 called!\n"); abort(); } -/* Generated stub for towire_u8 */ -void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) -{ fprintf(stderr, "towire_u8 called!\n"); abort(); } -/* Generated stub for towire_u8_array */ -void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -#define num_writers 10 -#define num_writes 10 - -struct read_state { - int pos; - - /* What have we read from the funnel end? Should be - * num_writers sets of num_writes consecutive identical - * numbers */ - u8 reads[num_writers*num_writes]; - - /* All tasks reading from upstream writers will serialize on this */ - struct io_lock *lock; -}; - -/* The read context per connection */ -struct reader_state { - struct read_state *read_state; - - /* Where are we reading from? */ - struct io_conn *upstream; - u8 buf; - - int count; -}; - -struct write_state { - int count; - u8 id; -}; - -static struct io_plan *writer(struct io_conn *conn, struct write_state *ws) -{ - if (ws->count++ == num_writes) - return io_close(conn); - return io_write(conn, &ws->id, 1, writer, ws); -} - -static struct io_plan *reader(struct io_conn *conn, struct reader_state *reader_state) -{ - struct read_state *rs = reader_state->read_state; - rs->reads[rs->pos] = reader_state->buf; - rs->pos++; - reader_state->count++; - - if (reader_state->count == num_writes) { - io_lock_release(reader_state->read_state->lock); - return io_close(conn); - } else { - return io_read(conn, &reader_state->buf, 1, reader, reader_state); - } -} - -static struct io_plan *reader_start(struct io_conn *conn, struct reader_state *reader_state) -{ - return io_read(conn, &reader_state->buf, 1, reader, reader_state); -} - -static struct io_plan *reader_locked(struct io_conn *conn, struct reader_state *rs) -{ - return io_lock_acquire_in(conn, rs->read_state->lock, reader_start, rs); -} - -/* - * Creates a fan-in funnel from num_writers socketpairs into a single - * reader - * - * writers - * \\|// - * reader - */ -static bool test_multi_write(const tal_t *ctx) -{ - struct write_state ws[num_writers]; - struct read_state sink; - struct reader_state rs[num_writers]; - int fds[2]; - - sink.pos = 0; - sink.lock = io_lock_new(ctx); - memset(&sink.reads, 0, sizeof(sink.reads)); - - for (size_t i=0; i Date: Sat, 4 Dec 2021 21:54:58 +1030 Subject: [PATCH 0111/1530] tools/check-includes.sh: make shellcheck happy. Signed-off-by: Rusty Russell --- tools/check-includes.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/check-includes.sh b/tools/check-includes.sh index 74d0e1b2c55f..c7a1e345040c 100755 --- a/tools/check-includes.sh +++ b/tools/check-includes.sh @@ -21,7 +21,7 @@ do EXIT_CODE=1 fi # Ignore contrib/. - if [ "${HEADER_FILE##contrib/}" = "$HEADER_FILE" -a "$(grep '#include' $HEADER_FILE | head -n1)" != '#include "config.h"' ]; then + if [ "${HEADER_FILE##contrib/}" = "$HEADER_FILE" ] && [ "$(grep '#include' "$HEADER_FILE" | head -n1)" != '#include "config.h"' ]; then echo "${HEADER_FILE}:1:does not include config.h first" EXIT_CODE=1 fi @@ -68,7 +68,7 @@ for C_FILE in $(filter_suffix c); do EXIT_CODE=1 fi # Ignore contrib/. - if [ "${C_FILE##contrib/}" = "$C_FILE" -a "$(grep '#include' $C_FILE | head -n1)" != '#include "config.h"' ]; then + if [ "${C_FILE##contrib/}" = "$C_FILE" ] && [ "$(grep '#include' "$C_FILE" | head -n1)" != '#include "config.h"' ]; then echo "${C_FILE}:1:does not include config.h first" EXIT_CODE=1 fi From f64df6fbb55cf50c3b9244ec69728374b09bcbf0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 4 Dec 2021 21:55:00 +1030 Subject: [PATCH 0112/1530] devtools: simple script to look for functions which don't seem to be used. Gives a rough first-pass, at least. Signed-off-by: Rusty Russell --- devtools/unused-functions.sh | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100755 devtools/unused-functions.sh diff --git a/devtools/unused-functions.sh b/devtools/unused-functions.sh new file mode 100755 index 000000000000..44793546721d --- /dev/null +++ b/devtools/unused-functions.sh @@ -0,0 +1,30 @@ +#! /bin/sh + +# Files to look inside: don't count test files. We don't use git grep +# because we want to find occurences in wiregen files. +FILES=$(find ./* -name '*.[ch]' | grep -v /test/run- | grep -v ^./ccan/ | grep -v ^./external/ | grep -v ^./tests/) + +if [ $# = 0 ]; then + HEADERS=$(echo [a-z]*/*.h) +else + HEADERS="$*" +fi + +for hfile in $HEADERS; do + # Don't worry about wiregen unused functions. + if [ "${hfile%_wiregen.h}" != "${hfile}" ]; then + continue + fi + # shellcheck disable=SC2010 disable=SC2086 + USING=$(ls $FILES | grep -v "^./${hfile%.h}\.") + funcs=$(sed -n 's/^[^#].* \**\([a-z0-9_]*\)(.*/\1/p;s/^\([a-z0-9_]*\)(.*/\1/p' < "$hfile") + for f in $funcs; do + # Ignore C and H files both: don't use git grep since we want to find + # occurrences in generated files too. + # echo Looking through $(echo $FILES | grep -v "^${hfile%.h}\.") + # shellcheck disable=SC2086 + if ! grep -qw $f $USING; then + echo "$(grep -nHw "$f" "$hfile" | head -n1)": "$f" unused + fi + done +done From d9968bbc0cec07647f1432ba6b08d58d6f60131e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 4 Dec 2021 21:55:06 +1030 Subject: [PATCH 0113/1530] bitcoin: remove unused functions, or make static. Signed-off-by: Rusty Russell --- bitcoin/base58.c | 55 -------------------- bitcoin/base58.h | 7 --- bitcoin/block.c | 15 +----- bitcoin/block.h | 8 --- bitcoin/chainparams.c | 8 --- bitcoin/chainparams.h | 6 --- bitcoin/locktime.c | 49 +----------------- bitcoin/locktime.h | 11 ---- bitcoin/psbt.c | 19 ++----- bitcoin/psbt.h | 33 +----------- bitcoin/pubkey.c | 4 +- bitcoin/pubkey.h | 4 -- bitcoin/script.c | 7 --- bitcoin/script.h | 2 - bitcoin/short_channel_id.c | 18 +------ bitcoin/short_channel_id.h | 7 --- bitcoin/test/run-bitcoin_block_from_hex.c | 25 ++++----- bitcoin/test/run-tx-encode.c | 16 ------ bitcoin/tx.c | 59 ---------------------- bitcoin/tx.h | 16 ------ common/test/run-amount.c | 6 --- common/test/run-base64.c | 12 ----- common/test/run-bigsize.c | 12 ----- common/test/run-bolt12_decode.c | 12 ----- common/test/run-bolt12_period.c | 12 ----- common/test/run-cryptomsg.c | 12 ----- common/test/run-derive_basepoints.c | 12 ----- common/test/run-features.c | 6 --- common/test/run-gossip_rcvd_filter.c | 9 ---- common/test/run-gossmap-fp16.c | 12 ----- common/test/run-gossmap_guess_node_id.c | 12 ----- common/test/run-ip_port_parsing.c | 6 --- common/test/run-json_remove.c | 12 ----- common/test/run-json_scan.c | 12 ----- common/test/run-key_derive.c | 12 ----- common/test/run-lease_rates.c | 6 --- common/test/run-psbt_diff.c | 6 --- common/test/run-softref.c | 12 ----- common/test/run-sphinx-xor_cipher_stream.c | 6 --- common/test/run-sphinx.c | 6 --- common/test/run-wireaddr.c | 6 --- common/type_to_string.h | 2 - connectd/test/run-initiator-success.c | 6 --- connectd/test/run-responder-success.c | 6 --- connectd/test/run-websocket.c | 6 --- wire/test/Makefile | 2 +- wire/test/run-peer-wire.c | 7 ++- wire/test/run-tlvstream.c | 3 ++ 48 files changed, 28 insertions(+), 574 deletions(-) diff --git a/bitcoin/base58.c b/bitcoin/base58.c index e874b0b8ee0d..0af71d8f3785 100644 --- a/bitcoin/base58.c +++ b/bitcoin/base58.c @@ -65,63 +65,8 @@ static bool from_base58(u8 *version, return true; } -bool bitcoin_from_base58(u8 *version, struct bitcoin_address *addr, - const char *base58, size_t len) -{ - return from_base58(version, &addr->addr, base58, len); -} - - -bool p2sh_from_base58(u8 *version, struct ripemd160 *p2sh, const char *base58, - size_t len) -{ - - return from_base58(version, p2sh, base58, len); -} - bool ripemd160_from_base58(u8 *version, struct ripemd160 *rmd, const char *base58, size_t base58_len) { return from_base58(version, rmd, base58, base58_len); } - -bool key_from_base58(const char *base58, size_t base58_len, - bool *test_net, struct privkey *priv, struct pubkey *key) -{ - // 1 byte version, 32 byte private key, 1 byte compressed, 4 byte checksum - u8 keybuf[1 + 32 + 1 + 4]; - char *terminated_base58 = tal_dup_arr(NULL, char, base58, base58_len, 1); - terminated_base58[base58_len] = '\0'; - size_t keybuflen = sizeof(keybuf); - - - size_t written = 0; - int r = wally_base58_to_bytes(terminated_base58, BASE58_FLAG_CHECKSUM, keybuf, keybuflen, &written); - wally_bzero(terminated_base58, base58_len + 1); - tal_free(terminated_base58); - if (r != WALLY_OK || written > keybuflen) - return false; - - /* Byte after key should be 1 to represent a compressed key. */ - if (keybuf[1 + 32] != 1) - return false; - - if (keybuf[0] == 128) - *test_net = false; - else if (keybuf[0] == 239) - *test_net = true; - else - return false; - - /* Copy out secret. */ - memcpy(priv->secret.data, keybuf + 1, sizeof(priv->secret.data)); - - if (!secp256k1_ec_seckey_verify(secp256k1_ctx, priv->secret.data)) - return false; - - /* Get public key, too. */ - if (!pubkey_from_privkey(priv, key)) - return false; - - return true; -} diff --git a/bitcoin/base58.h b/bitcoin/base58.h index 0af3a16bde72..b5a9b505082f 100644 --- a/bitcoin/base58.h +++ b/bitcoin/base58.h @@ -12,17 +12,10 @@ struct bitcoin_address; /* Bitcoin address encoded in base58, with version and checksum */ char *bitcoin_to_base58(const tal_t *ctx, const struct chainparams *chainparams, const struct bitcoin_address *addr); -bool bitcoin_from_base58(u8 *version, struct bitcoin_address *addr, - const char *base58, size_t len); /* P2SH address encoded as base58, with version and checksum */ char *p2sh_to_base58(const tal_t *ctx, const struct chainparams *chainparams, const struct ripemd160 *p2sh); -bool p2sh_from_base58(u8 *version, struct ripemd160 *p2sh, const char *base58, - size_t len); - -bool key_from_base58(const char *base58, size_t base58_len, - bool *test_net, struct privkey *priv, struct pubkey *key); /* Decode a p2pkh or p2sh into the ripemd160 hash */ bool ripemd160_from_base58(u8 *version, struct ripemd160 *rmd, diff --git a/bitcoin/block.c b/bitcoin/block.c index bd8446cbd9af..5e037e4f45b4 100644 --- a/bitcoin/block.c +++ b/bitcoin/block.c @@ -229,19 +229,8 @@ void bitcoin_block_blkid(const struct bitcoin_block *b, *out = b->hdr.hash; } -/* We do the same hex-reversing crud as txids. */ -bool bitcoin_blkid_from_hex(const char *hexstr, size_t hexstr_len, - struct bitcoin_blkid *blockid) -{ - struct bitcoin_txid fake_txid; - if (!bitcoin_txid_from_hex(hexstr, hexstr_len, &fake_txid)) - return false; - blockid->shad = fake_txid.shad; - return true; -} - -bool bitcoin_blkid_to_hex(const struct bitcoin_blkid *blockid, - char *hexstr, size_t hexstr_len) +static bool bitcoin_blkid_to_hex(const struct bitcoin_blkid *blockid, + char *hexstr, size_t hexstr_len) { struct bitcoin_txid fake_txid; fake_txid.shad = blockid->shad; diff --git a/bitcoin/block.h b/bitcoin/block.h index e6f460a9b7e2..546a7371fbdb 100644 --- a/bitcoin/block.h +++ b/bitcoin/block.h @@ -45,14 +45,6 @@ bitcoin_block_from_hex(const tal_t *ctx, const struct chainparams *chainparams, void bitcoin_block_blkid(const struct bitcoin_block *block, struct bitcoin_blkid *out); -/* Parse hex string to get blockid (reversed, a-la bitcoind). */ -bool bitcoin_blkid_from_hex(const char *hexstr, size_t hexstr_len, - struct bitcoin_blkid *blockid); - -/* Get hex string of blockid (reversed, a-la bitcoind). */ -bool bitcoin_blkid_to_hex(const struct bitcoin_blkid *blockid, - char *hexstr, size_t hexstr_len); - /* Marshalling/unmarshaling over the wire */ void towire_bitcoin_blkid(u8 **pptr, const struct bitcoin_blkid *blkid); void fromwire_bitcoin_blkid(const u8 **cursor, size_t *max, diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index d253e04aca9e..d1e6158662d7 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -226,14 +226,6 @@ const struct chainparams *chainparams_for_network(const char *network_name) return NULL; } -const struct chainparams **chainparams_for_networks(const tal_t *ctx) -{ - const struct chainparams **params = tal_arr(ctx, const struct chainparams*, 0); - for (size_t i = 0; i < ARRAY_SIZE(networks); i++) - tal_arr_expand(¶ms, &networks[i]); - return params; -} - const struct chainparams *chainparams_by_chainhash(const struct bitcoin_blkid *chain_hash) { for (size_t i = 0; i < ARRAY_SIZE(networks); i++) { diff --git a/bitcoin/chainparams.h b/bitcoin/chainparams.h index 0a4cc5a3f193..dc16852f2c7d 100644 --- a/bitcoin/chainparams.h +++ b/bitcoin/chainparams.h @@ -41,12 +41,6 @@ struct chainparams { */ const struct chainparams *chainparams_for_network(const char *network_name); -/** - * chainparams_for_networks - Get blockchain parameters for all known networks, - * as a tal array. - */ -const struct chainparams **chainparams_for_networks(const tal_t *ctx); - /** * chainparams_by_bip173 - Helper to get a network by its bip173 name * diff --git a/bitcoin/locktime.c b/bitcoin/locktime.c index d8ce95aa700e..5bbaedff3953 100644 --- a/bitcoin/locktime.c +++ b/bitcoin/locktime.c @@ -23,60 +23,13 @@ static bool abs_is_seconds(u32 locktime) return locktime >= SECONDS_POINT; } -bool rel_locktime_is_seconds(const struct rel_locktime *rel) -{ - return rel->locktime & BIP68_SECONDS_FLAG; -} - -u32 rel_locktime_to_seconds(const struct rel_locktime *rel) -{ - assert(rel_locktime_is_seconds(rel)); - return (rel->locktime & BIP68_LOCKTIME_MASK) << BIP68_SECONDS_SHIFT; -} - -u32 rel_locktime_to_blocks(const struct rel_locktime *rel) -{ - assert(!rel_locktime_is_seconds(rel)); - return rel->locktime & BIP68_LOCKTIME_MASK; -} - bool blocks_to_abs_locktime(u32 blocks, struct abs_locktime *abs) { return abs_blocks_to_locktime(blocks, &abs->locktime); } -bool abs_locktime_is_seconds(const struct abs_locktime *abs) -{ - return abs_is_seconds(abs->locktime); -} - -u32 abs_locktime_to_seconds(const struct abs_locktime *abs) -{ - assert(abs_locktime_is_seconds(abs)); - return abs->locktime; -} - u32 abs_locktime_to_blocks(const struct abs_locktime *abs) { - assert(!abs_locktime_is_seconds(abs)); + assert(!abs_is_seconds(abs->locktime)); return abs->locktime; } - -static char *fmt_rel_locktime(const tal_t *ctx, const struct rel_locktime *rl) -{ - if (rel_locktime_is_seconds(rl)) - return tal_fmt(ctx, "+%usec", rel_locktime_to_seconds(rl)); - else - return tal_fmt(ctx, "+%ublocks", rel_locktime_to_blocks(rl)); -} - -static char *fmt_abs_locktime(const tal_t *ctx, const struct abs_locktime *al) -{ - if (abs_locktime_is_seconds(al)) - return tal_fmt(ctx, "%usec", abs_locktime_to_seconds(al)); - else - return tal_fmt(ctx, "%ublocks", abs_locktime_to_blocks(al)); -} - -REGISTER_TYPE_TO_STRING(rel_locktime, fmt_rel_locktime); -REGISTER_TYPE_TO_STRING(abs_locktime, fmt_abs_locktime); diff --git a/bitcoin/locktime.h b/bitcoin/locktime.h index ca9ac15272d6..fc99ca82d440 100644 --- a/bitcoin/locktime.h +++ b/bitcoin/locktime.h @@ -4,23 +4,12 @@ #include #include -/* As used by nSequence and OP_CHECKSEQUENCEVERIFY (BIP68) */ -struct rel_locktime { - u32 locktime; -}; - -bool rel_locktime_is_seconds(const struct rel_locktime *rel); -u32 rel_locktime_to_seconds(const struct rel_locktime *rel); -u32 rel_locktime_to_blocks(const struct rel_locktime *rel); - /* As used by nLocktime and OP_CHECKLOCKTIMEVERIFY (BIP65) */ struct abs_locktime { u32 locktime; }; bool blocks_to_abs_locktime(u32 blocks, struct abs_locktime *abs); -bool abs_locktime_is_seconds(const struct abs_locktime *abs); -u32 abs_locktime_to_seconds(const struct abs_locktime *abs); u32 abs_locktime_to_blocks(const struct abs_locktime *abs); #endif /* LIGHTNING_BITCOIN_LOCKTIME_H */ diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index 17937695c8d2..f3fda99edecc 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -12,7 +12,7 @@ #include -void psbt_destroy(struct wally_psbt *psbt) +static void psbt_destroy(struct wally_psbt *psbt) { wally_psbt_free(psbt); } @@ -430,17 +430,6 @@ bool psbt_has_input(const struct wally_psbt *psbt, return false; } -bool psbt_input_set_redeemscript(struct wally_psbt *psbt, size_t in, - const u8 *redeemscript) -{ - int wally_err; - assert(psbt->num_inputs > in); - wally_err = wally_psbt_input_set_redeem_script(&psbt->inputs[in], - redeemscript, - tal_bytelen(redeemscript)); - return wally_err == WALLY_OK; -} - struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt, size_t in) { @@ -558,9 +547,9 @@ void psbt_input_set_unknown(const tal_t *ctx, abort(); } -void *psbt_get_unknown(const struct wally_map *map, - const u8 *key, - size_t *val_len) +static void *psbt_get_unknown(const struct wally_map *map, + const u8 *key, + size_t *val_len) { size_t index; diff --git a/bitcoin/psbt.h b/bitcoin/psbt.h index 386ef7a714c7..3aadc5028964 100644 --- a/bitcoin/psbt.h +++ b/bitcoin/psbt.h @@ -17,18 +17,6 @@ struct bitcoin_signature; struct bitcoin_txid; struct pubkey; -/** psbt_destroy - Destroy a PSBT that is not tal-allocated - * - * @psbt - the PSBT to destroy - * - * WARNING Do NOT call this function directly if you got the - * PSBT from create_psbt, new_psbt, psbt_from_bytes, - * psbt_from_b64, or fromwire_wally_psbt. - * Those functions register this function as a `tal_destructor` - * automatically. - */ -void psbt_destroy(struct wally_psbt *psbt); - /** * create_psbt - Create a new psbt object * @@ -172,16 +160,7 @@ WARN_UNUSED_RESULT bool psbt_input_set_signature(struct wally_psbt *psbt, size_t const struct bitcoin_signature *sig); void psbt_input_set_witscript(struct wally_psbt *psbt, size_t in, const u8 *wscript); -void psbt_elements_input_init(struct wally_psbt *psbt, size_t in, - const u8 *scriptPubkey, - struct amount_asset *asset, - const u8 *nonce); -void psbt_elements_input_init_witness(struct wally_psbt *psbt, size_t in, - const u8 *witscript, - struct amount_asset *asset, - const u8 *nonce); -bool psbt_input_set_redeemscript(struct wally_psbt *psbt, size_t in, - const u8 *redeemscript); + /* psbt_input_set_unknown - Set the given Key-Value in the psbt's input keymap * @ctx - tal context for allocations * @in - psbt input to set key-value on @@ -194,16 +173,6 @@ void psbt_input_set_unknown(const tal_t *ctx, const u8 *key, const void *value, size_t value_len); -/* psbt_get_unknown - Fetch the value from the given map at key - * - * @map - map of unknowns to search for key - * @key - key of key-value pair to return value for - * @value_len - (out) length of value (if found) - * - * Returns: value at @key, or NULL if not found */ -void *psbt_get_unknown(const struct wally_map *map, - const u8 *key, - size_t *val_len); /* psbt_get_lightning - Fetch a proprietary lightning value from the given map * diff --git a/bitcoin/pubkey.c b/bitcoin/pubkey.c index 80807813fb0d..00ce2f1398ab 100644 --- a/bitcoin/pubkey.c +++ b/bitcoin/pubkey.c @@ -71,7 +71,7 @@ char *pubkey_to_hexstr(const tal_t *ctx, const struct pubkey *key) } REGISTER_TYPE_TO_STRING(pubkey, pubkey_to_hexstr); -char *secp256k1_pubkey_to_hexstr(const tal_t *ctx, const secp256k1_pubkey *key) +static char *secp256k1_pubkey_to_hexstr(const tal_t *ctx, const secp256k1_pubkey *key) { unsigned char der[PUBKEY_CMPR_LEN]; size_t outlen = sizeof(der); @@ -150,7 +150,7 @@ void towire_point32(u8 **pptr, const struct point32 *point32) towire(pptr, output, sizeof(output)); } -char *point32_to_hexstr(const tal_t *ctx, const struct point32 *point32) +static char *point32_to_hexstr(const tal_t *ctx, const struct point32 *point32) { u8 output[32]; diff --git a/bitcoin/pubkey.h b/bitcoin/pubkey.h index 4474c91c96ef..3eb52add0645 100644 --- a/bitcoin/pubkey.h +++ b/bitcoin/pubkey.h @@ -32,9 +32,6 @@ bool pubkey_from_hexstr(const char *derstr, size_t derlen, struct pubkey *key); /* Convert from hex string of DER (scriptPubKey from validateaddress) */ char *pubkey_to_hexstr(const tal_t *ctx, const struct pubkey *key); -/* Convenience wrapper for a raw secp256k1_pubkey */ -char *secp256k1_pubkey_to_hexstr(const tal_t *ctx, const secp256k1_pubkey *key); - /* Point from secret */ bool pubkey_from_secret(const struct secret *secret, struct pubkey *key); @@ -75,5 +72,4 @@ void fromwire_pubkey(const u8 **cursor, size_t *max, struct pubkey *pubkey); void towire_point32(u8 **pptr, const struct point32 *pubkey); void fromwire_point32(const u8 **cursor, size_t *max, struct point32 *pubkey); -char *point32_to_hexstr(const tal_t *ctx, const struct point32 *point32); #endif /* LIGHTNING_BITCOIN_PUBKEY_H */ diff --git a/bitcoin/script.c b/bitcoin/script.c index 805a44a96d7d..316388696548 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -187,13 +187,6 @@ u8 *scriptpubkey_p2pkh(const tal_t *ctx, const struct bitcoin_address *addr) return script; } -u8 *scriptpubkey_opreturn(const tal_t *ctx) -{ - u8 *script = tal_arr(ctx, u8, 0); - - add_op(&script, OP_RETURN); - return script; -} u8 *scriptpubkey_opreturn_padded(const tal_t *ctx) { u8 *script = tal_arr(ctx, u8, 0); diff --git a/bitcoin/script.h b/bitcoin/script.h index 6ef701f0b36d..89f225ae860f 100644 --- a/bitcoin/script.h +++ b/bitcoin/script.h @@ -27,8 +27,6 @@ u8 *scriptpubkey_p2sh_hash(const tal_t *ctx, const struct ripemd160 *redeemhash) /* Create an output script using p2pkh */ u8 *scriptpubkey_p2pkh(const tal_t *ctx, const struct bitcoin_address *addr); -/* Create a prunable output script */ -u8 *scriptpubkey_opreturn(const tal_t *ctx); /* Create a prunable output script with 20 random bytes. * This is needed since a spend from a p2wpkh to an `OP_RETURN` without * any other outputs would result in a transaction smaller than the diff --git a/bitcoin/short_channel_id.c b/bitcoin/short_channel_id.c index ff77ee2ac7d6..d6c341cea016 100644 --- a/bitcoin/short_channel_id.c +++ b/bitcoin/short_channel_id.c @@ -78,8 +78,8 @@ bool short_channel_id_dir_from_str(const char *str, size_t strlen, return true; } -char *short_channel_id_dir_to_str(const tal_t *ctx, - const struct short_channel_id_dir *scidd) +static char *short_channel_id_dir_to_str(const tal_t *ctx, + const struct short_channel_id_dir *scidd) { char *str, *scidstr = short_channel_id_to_str(NULL, &scidd->scid); str = tal_fmt(ctx, "%s/%u", scidstr, scidd->dir); @@ -96,22 +96,8 @@ void towire_short_channel_id(u8 **pptr, towire_u64(pptr, short_channel_id->u64); } -void towire_short_channel_id_dir(u8 **pptr, - const struct short_channel_id_dir *scidd) -{ - towire_short_channel_id(pptr, &scidd->scid); - towire_bool(pptr, scidd->dir); -} - void fromwire_short_channel_id(const u8 **cursor, size_t *max, struct short_channel_id *short_channel_id) { short_channel_id->u64 = fromwire_u64(cursor, max); } - -void fromwire_short_channel_id_dir(const u8 **cursor, size_t *max, - struct short_channel_id_dir *scidd) -{ - fromwire_short_channel_id(cursor, max, &scidd->scid); - scidd->dir = fromwire_bool(cursor, max); -} diff --git a/bitcoin/short_channel_id.h b/bitcoin/short_channel_id.h index 2407729875e0..bac77d8d8b06 100644 --- a/bitcoin/short_channel_id.h +++ b/bitcoin/short_channel_id.h @@ -69,17 +69,10 @@ char *short_channel_id_to_str(const tal_t *ctx, const struct short_channel_id *s bool WARN_UNUSED_RESULT short_channel_id_dir_from_str(const char *str, size_t strlen, struct short_channel_id_dir *scidd); -char *short_channel_id_dir_to_str(const tal_t *ctx, - const struct short_channel_id_dir *scidd); - /* Marshal/unmarshal */ void towire_short_channel_id(u8 **pptr, const struct short_channel_id *short_channel_id); -void towire_short_channel_id_dir(u8 **pptr, - const struct short_channel_id_dir *scidd); void fromwire_short_channel_id(const u8 **cursor, size_t *max, struct short_channel_id *short_channel_id); -void fromwire_short_channel_id_dir(const u8 **cursor, size_t *max, - struct short_channel_id_dir *scidd); #endif /* LIGHTNING_BITCOIN_SHORT_CHANNEL_ID_H */ diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index c00d99778be0..96d4e51201b2 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -43,22 +43,12 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_fail */ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } /* Generated stub for fromwire_sha256 */ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } -/* Generated stub for fromwire_tal_arrn */ -u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, - const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -77,15 +67,9 @@ void script_push_bytes(u8 **scriptp UNNEEDED, const void *mem UNNEEDED, size_t l /* Generated stub for scriptpubkey_p2wsh */ u8 *scriptpubkey_p2wsh(const tal_t *ctx UNNEEDED, const u8 *witnessscript UNNEEDED) { fprintf(stderr, "scriptpubkey_p2wsh called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } @@ -144,6 +128,15 @@ static const char block[] = STRUCTEQ_DEF(sha256_double, 0, sha); +static bool bitcoin_blkid_from_hex(const char *hexstr, size_t hexstr_len, + struct bitcoin_blkid *blockid) +{ + struct bitcoin_txid fake_txid; + if (!bitcoin_txid_from_hex(hexstr, hexstr_len, &fake_txid)) + return false; + blockid->shad = fake_txid.shad; + return true; +} int main(int argc, const char *argv[]) { struct bitcoin_blkid prev; diff --git a/bitcoin/test/run-tx-encode.c b/bitcoin/test/run-tx-encode.c index 7fb160b19704..1aae8e5815de 100644 --- a/bitcoin/test/run-tx-encode.c +++ b/bitcoin/test/run-tx-encode.c @@ -44,22 +44,12 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_fail */ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } /* Generated stub for fromwire_sha256 */ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } -/* Generated stub for fromwire_tal_arrn */ -u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, - const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -78,15 +68,9 @@ void script_push_bytes(u8 **scriptp UNNEEDED, const void *mem UNNEEDED, size_t l /* Generated stub for scriptpubkey_p2wsh */ u8 *scriptpubkey_p2wsh(const tal_t *ctx UNNEEDED, const u8 *witnessscript UNNEEDED) { fprintf(stderr, "scriptpubkey_p2wsh called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 81a5635c79b6..318a2ec94f2e 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -97,16 +97,6 @@ int bitcoin_tx_add_output(struct bitcoin_tx *tx, const u8 *script, return i; } -int bitcoin_tx_add_multi_outputs(struct bitcoin_tx *tx, - struct bitcoin_tx_output **outputs) -{ - for (size_t j = 0; j < tal_count(outputs); j++) - bitcoin_tx_add_output(tx, outputs[j]->script, - NULL, outputs[j]->amount); - - return tx->wtx->num_outputs; -} - bool elements_wtx_output_is_fee(const struct wally_tx *tx, int outnum) { assert(outnum < tx->num_outputs); @@ -376,20 +366,6 @@ void bitcoin_tx_input_set_script(struct bitcoin_tx *tx, int innum, u8 *script) tal_wally_end(tx->psbt); } -const u8 *bitcoin_tx_input_get_witness(const tal_t *ctx, - const struct bitcoin_tx *tx, int innum, - int witnum) -{ - const u8 *witness_item; - struct wally_tx_witness_item *item; - assert(innum < tx->wtx->num_inputs); - assert(witnum < tx->wtx->inputs[innum].witness->num_items); - item = &tx->wtx->inputs[innum].witness->items[witnum]; - witness_item = - tal_dup_arr(ctx, u8, item->witness, item->witness_len, 0); - return witness_item; -} - /* FIXME: remove */ void bitcoin_tx_input_get_txid(const struct bitcoin_tx *tx, int innum, struct bitcoin_txid *out) @@ -747,16 +723,6 @@ struct bitcoin_tx *fromwire_bitcoin_tx(const tal_t *ctx, return tx; } -struct wally_tx *fromwire_wally_tx(const tal_t *ctx, - const u8 **cursor, size_t *max) -{ - struct wally_tx *wtx; - wtx = pull_wtx(ctx, cursor, max); - if (!wtx) - return fromwire_fail(cursor, max); - return wtx; -} - void towire_bitcoin_txid(u8 **pptr, const struct bitcoin_txid *txid) { towire_sha256_double(pptr, &txid->shad); @@ -784,31 +750,6 @@ void towire_bitcoin_tx(u8 **pptr, const struct bitcoin_tx *tx) towire_wally_psbt(pptr, tx->psbt); } -void towire_wally_tx(u8 **pptr, const struct wally_tx *wtx) -{ - u8 *lin = linearize_wtx(tmpctx, wtx); - towire_u8_array(pptr, lin, tal_count(lin)); -} - -struct bitcoin_tx_output *fromwire_bitcoin_tx_output(const tal_t *ctx, - const u8 **cursor, size_t *max) -{ - struct bitcoin_tx_output *output = tal(ctx, struct bitcoin_tx_output); - output->amount = fromwire_amount_sat(cursor, max); - u16 script_len = fromwire_u16(cursor, max); - output->script = fromwire_tal_arrn(output, cursor, max, script_len); - if (!*cursor) - return tal_free(output); - return output; -} - -void towire_bitcoin_tx_output(u8 **pptr, const struct bitcoin_tx_output *output) -{ - towire_amount_sat(pptr, output->amount); - towire_u16(pptr, tal_count(output->script)); - towire_u8_array(pptr, output->script, tal_count(output->script)); -} - bool wally_tx_input_spends(const struct wally_tx_input *input, const struct bitcoin_outpoint *outpoint) { diff --git a/bitcoin/tx.h b/bitcoin/tx.h index b6583948d1dc..85bb38444480 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -99,10 +99,6 @@ int bitcoin_tx_add_output(struct bitcoin_tx *tx, const u8 *script, u8 *wscript, struct amount_sat amount); -/* Add mutiple output to tx. */ -int bitcoin_tx_add_multi_outputs(struct bitcoin_tx *tx, - struct bitcoin_tx_output **outputs); - /* Set the locktime for a transaction */ void bitcoin_tx_set_locktime(struct bitcoin_tx *tx, u32 locktime); @@ -186,13 +182,6 @@ void bitcoin_tx_input_set_witness(struct bitcoin_tx *tx, int innum, */ void bitcoin_tx_input_set_script(struct bitcoin_tx *tx, int innum, u8 *script); -/** - * Helper to get a witness as a tal_arr array. - */ -const u8 *bitcoin_tx_input_get_witness(const tal_t *ctx, - const struct bitcoin_tx *tx, int innum, - int witnum); - /** * Wrap the raw txhash in the wally_tx_input into a bitcoin_txid */ @@ -260,13 +249,8 @@ void fromwire_bitcoin_txid(const u8 **cursor, size_t *max, struct bitcoin_txid *txid); struct bitcoin_tx *fromwire_bitcoin_tx(const tal_t *ctx, const u8 **cursor, size_t *max); -struct bitcoin_tx_output *fromwire_bitcoin_tx_output(const tal_t *ctx, - const u8 **cursor, size_t *max); -struct wally_tx *fromwire_wally_tx(const tal_t *ctx, const u8 **cursor, size_t *max); void towire_bitcoin_txid(u8 **pptr, const struct bitcoin_txid *txid); void towire_bitcoin_tx(u8 **pptr, const struct bitcoin_tx *tx); -void towire_bitcoin_tx_output(u8 **pptr, const struct bitcoin_tx_output *output); -void towire_wally_tx(u8 **pptr, const struct wally_tx *wtx); void towire_bitcoin_outpoint(u8 **pptr, const struct bitcoin_outpoint *outp); void fromwire_bitcoin_outpoint(const u8 **cursor, size_t *max, struct bitcoin_outpoint *outp); diff --git a/common/test/run-amount.c b/common/test/run-amount.c index de4ac8458114..923f0a9b7ef8 100644 --- a/common/test/run-amount.c +++ b/common/test/run-amount.c @@ -24,9 +24,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -52,9 +49,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-base64.c b/common/test/run-base64.c index 3261d998f81a..97571a5529ae 100644 --- a/common/test/run-base64.c +++ b/common/test/run-base64.c @@ -44,9 +44,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -64,9 +61,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -82,9 +76,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -95,9 +86,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-bigsize.c b/common/test/run-bigsize.c index 85c82a2ac45a..d972f17e22e5 100644 --- a/common/test/run-bigsize.c +++ b/common/test/run-bigsize.c @@ -45,9 +45,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -65,9 +62,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -93,9 +87,6 @@ char *json_member_direct(struct json_stream *js UNNEEDED, /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -106,9 +97,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-bolt12_decode.c b/common/test/run-bolt12_decode.c index a975a253f391..68732b035243 100644 --- a/common/test/run-bolt12_decode.c +++ b/common/test/run-bolt12_decode.c @@ -50,9 +50,6 @@ int features_unsupported(const struct feature_set *our_features UNNEEDED, /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -82,9 +79,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -128,9 +122,6 @@ struct tlv_offer *tlv_offer_new(const tal_t *ctx UNNEEDED) /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -150,9 +141,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-bolt12_period.c b/common/test/run-bolt12_period.c index e82ab6a0d0ae..933e8c0d70d9 100644 --- a/common/test/run-bolt12_period.c +++ b/common/test/run-bolt12_period.c @@ -53,9 +53,6 @@ bool from_bech32_charset(const tal_t *ctx UNNEEDED, /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -85,9 +82,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -135,9 +129,6 @@ char *to_bech32_charset(const tal_t *ctx UNNEEDED, /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -157,9 +148,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-cryptomsg.c b/common/test/run-cryptomsg.c index 963e49648b36..39e18f9df6e1 100644 --- a/common/test/run-cryptomsg.c +++ b/common/test/run-cryptomsg.c @@ -40,9 +40,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -60,9 +57,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -78,9 +72,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -91,9 +82,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-derive_basepoints.c b/common/test/run-derive_basepoints.c index e7ebaab864f9..e53b89754eac 100644 --- a/common/test/run-derive_basepoints.c +++ b/common/test/run-derive_basepoints.c @@ -46,9 +46,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -66,9 +63,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -84,9 +78,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -97,9 +88,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-features.c b/common/test/run-features.c index e93870521182..3e7dbd6db9c3 100644 --- a/common/test/run-features.c +++ b/common/test/run-features.c @@ -39,9 +39,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -77,9 +74,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } diff --git a/common/test/run-gossip_rcvd_filter.c b/common/test/run-gossip_rcvd_filter.c index 270df9bd4933..473229bdec88 100644 --- a/common/test/run-gossip_rcvd_filter.c +++ b/common/test/run-gossip_rcvd_filter.c @@ -37,18 +37,12 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for memleak_remove_htable */ void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) { fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -59,9 +53,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-gossmap-fp16.c b/common/test/run-gossmap-fp16.c index a3df4b47f26e..b7b5ff1ca05a 100644 --- a/common/test/run-gossmap-fp16.c +++ b/common/test/run-gossmap-fp16.c @@ -40,9 +40,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -60,9 +57,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -78,9 +72,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -91,9 +82,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-gossmap_guess_node_id.c b/common/test/run-gossmap_guess_node_id.c index 8b4188bc58b4..bf0f55b796d5 100644 --- a/common/test/run-gossmap_guess_node_id.c +++ b/common/test/run-gossmap_guess_node_id.c @@ -41,9 +41,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -61,9 +58,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -79,9 +73,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -92,9 +83,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index 2be030de60e3..6e386dff4bdd 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -41,9 +41,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -79,9 +76,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index 06b659603e99..f821b95f4aeb 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -39,9 +39,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -59,9 +56,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -87,9 +81,6 @@ char *json_member_direct(struct json_stream *js UNNEEDED, /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -100,9 +91,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-json_scan.c b/common/test/run-json_scan.c index 2da2119965b7..230a5348d757 100644 --- a/common/test/run-json_scan.c +++ b/common/test/run-json_scan.c @@ -39,9 +39,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -59,9 +56,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -87,9 +81,6 @@ char *json_member_direct(struct json_stream *js UNNEEDED, /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -100,9 +91,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-key_derive.c b/common/test/run-key_derive.c index 3c98cb50b1a3..25f246b27b64 100644 --- a/common/test/run-key_derive.c +++ b/common/test/run-key_derive.c @@ -44,9 +44,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -64,9 +61,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -82,9 +76,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -95,9 +86,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-lease_rates.c b/common/test/run-lease_rates.c index 034134bf03dd..5d8b7d0e8597 100644 --- a/common/test/run-lease_rates.c +++ b/common/test/run-lease_rates.c @@ -28,9 +28,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -59,9 +56,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-psbt_diff.c b/common/test/run-psbt_diff.c index 9312010d6237..4c8ff0587841 100644 --- a/common/test/run-psbt_diff.c +++ b/common/test/run-psbt_diff.c @@ -25,9 +25,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -56,9 +53,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-softref.c b/common/test/run-softref.c index b5f32fc4b6c9..5f5642d143ab 100644 --- a/common/test/run-softref.c +++ b/common/test/run-softref.c @@ -41,9 +41,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -61,9 +58,6 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } -/* Generated stub for fromwire_u16 */ -u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -79,9 +73,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } @@ -92,9 +83,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-sphinx-xor_cipher_stream.c b/common/test/run-sphinx-xor_cipher_stream.c index 963e4bda1687..dc1bd585c504 100644 --- a/common/test/run-sphinx-xor_cipher_stream.c +++ b/common/test/run-sphinx-xor_cipher_stream.c @@ -38,9 +38,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -108,9 +105,6 @@ void subkey_from_hmac(const char *prefix UNNEEDED, /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index 0145682d8a33..7cb201a8e7b5 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -51,9 +51,6 @@ size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED) /* Generated stub for fromwire_amount_msat */ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bigsize */ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } @@ -72,9 +69,6 @@ bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra /* Generated stub for towire_amount_msat */ void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) { fprintf(stderr, "towire_amount_msat called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } diff --git a/common/test/run-wireaddr.c b/common/test/run-wireaddr.c index 42973fee8f66..3fc66fa03f23 100644 --- a/common/test/run-wireaddr.c +++ b/common/test/run-wireaddr.c @@ -45,9 +45,6 @@ char *b32_encode(const tal_t *ctx UNNEEDED, const void *data UNNEEDED, size_t le /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -83,9 +80,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } diff --git a/common/type_to_string.h b/common/type_to_string.h index c58e712da779..171ac476a843 100644 --- a/common/type_to_string.h +++ b/common/type_to_string.h @@ -15,8 +15,6 @@ union printable_types { const struct sha256 *sha256; const struct sha256_double *sha256_double; const struct ripemd160 *ripemd160; - const struct rel_locktime *rel_locktime; - const struct abs_locktime *abs_locktime; const struct bitcoin_tx *bitcoin_tx; const struct htlc *htlc; const struct preimage *preimage; diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index 627e0e2414dd..27bb98bc9e03 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -48,9 +48,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -86,9 +83,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } diff --git a/connectd/test/run-responder-success.c b/connectd/test/run-responder-success.c index a531f19ca35a..1bf317e3f328 100644 --- a/connectd/test/run-responder-success.c +++ b/connectd/test/run-responder-success.c @@ -48,9 +48,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -86,9 +83,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } diff --git a/connectd/test/run-websocket.c b/connectd/test/run-websocket.c index 8d50415bc24f..db4ff47eb805 100644 --- a/connectd/test/run-websocket.c +++ b/connectd/test/run-websocket.c @@ -83,9 +83,6 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -121,9 +118,6 @@ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } diff --git a/wire/test/Makefile b/wire/test/Makefile index d38fb9aad985..adf452dbdb06 100644 --- a/wire/test/Makefile +++ b/wire/test/Makefile @@ -16,7 +16,7 @@ WIRE_TEST_COMMON_OBJS := \ common/utils.o # run-tlvstream.c needs to reach into bitcoin/pubkey for SUPERVERBOSE -$(WIRE_TEST_PROGRAMS): $(WIRE_TEST_COMMON_OBJS) $(filter-out bitcoin/pubkey.o,$(BITCOIN_OBJS)) +$(WIRE_TEST_PROGRAMS): $(WIRE_TEST_COMMON_OBJS) $(filter-out bitcoin/pubkey.o bitcoin/chainparams.o,$(BITCOIN_OBJS)) # We put a dependency on non-exp sources here, so they get built even if # we're EXPERIMENTAL_FEATURES. diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index c6b025c41524..8eb92aeb6d50 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -3,6 +3,7 @@ #include "../fromwire.c" #include "../peer_wire.c" #include "bitcoin/pubkey.c" +#include "bitcoin/chainparams.c" #include "common/amount.c" #include "common/channel_id.c" #include "common/node_id.c" @@ -1019,7 +1020,6 @@ int main(int argc, char *argv[]) void *ctx = tal(NULL, char); size_t i; u8 *msg; - const struct chainparams **chains; common_setup(argv[0]); @@ -1099,8 +1099,7 @@ int main(int argc, char *argv[]) assert(error_eq(&e, e2)); test_corruption(&e, e2, error); - chains = chainparams_for_networks(ctx); - for (i = 0; i < tal_count(chains); i++) { + for (i = 0; i < ARRAY_SIZE(networks); i++) { memset(&init, 2, sizeof(init)); init.globalfeatures = tal_arr(ctx, u8, 2); memset(init.globalfeatures, 2, 2); @@ -1108,7 +1107,7 @@ int main(int argc, char *argv[]) memset(init.localfeatures, 2, 2); init.tlvs = tlv_init_tlvs_new(ctx); init.tlvs->networks = tal_arr(init.tlvs, struct bitcoin_blkid, 1); - init.tlvs->networks[0] = chains[i]->genesis_blockhash; + init.tlvs->networks[0] = networks[i].genesis_blockhash; msg = towire_struct_init(ctx, &init); init2 = fromwire_struct_init(ctx, msg); assert(init_eq(&init, init2)); diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index 9fe2959d165d..b658d85f89d9 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -22,6 +22,9 @@ static const char *reason; #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for chainparams_by_chainhash */ +const struct chainparams *chainparams_by_chainhash(const struct bitcoin_blkid *chain_hash UNNEEDED) +{ fprintf(stderr, "chainparams_by_chainhash called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) From 786732601c3e794685db261d6949ec6152bb7662 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 4 Dec 2021 21:56:06 +1030 Subject: [PATCH 0114/1530] common: remove unused functions or make static. Signed-off-by: Rusty Russell --- common/bolt12.c | 6 +-- common/bolt12.h | 15 -------- common/dijkstra.c | 6 --- common/dijkstra.h | 3 -- common/gossip_store.h | 6 --- common/htlc_wire.c | 8 ++-- common/htlc_wire.h | 4 -- common/json.c | 19 ++-------- common/json.h | 7 ---- common/json_helpers.c | 12 ------ common/json_helpers.h | 5 --- common/json_stream.c | 8 +++- common/json_stream.h | 9 ----- common/json_tok.c | 8 ++-- common/json_tok.h | 4 -- common/permute_tx.c | 87 ------------------------------------------- common/permute_tx.h | 7 ---- 17 files changed, 22 insertions(+), 192 deletions(-) diff --git a/common/bolt12.c b/common/bolt12.c index 6fa064bdc0e1..a57a166277c7 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -415,17 +415,17 @@ struct tlv_invoice *invoice_decode(const tal_t *ctx, return invoice; } -bool bolt12_has_invoice_prefix(const char *str) +static bool bolt12_has_invoice_prefix(const char *str) { return strstarts(str, "lni1") || strstarts(str, "LNI1"); } -bool bolt12_has_request_prefix(const char *str) +static bool bolt12_has_request_prefix(const char *str) { return strstarts(str, "lnr1") || strstarts(str, "LNR1"); } -bool bolt12_has_offer_prefix(const char *str) +static bool bolt12_has_offer_prefix(const char *str) { return strstarts(str, "lno1") || strstarts(str, "LNO1"); } diff --git a/common/bolt12.h b/common/bolt12.h index e451c2897685..4aa4ce76149c 100644 --- a/common/bolt12.h +++ b/common/bolt12.h @@ -115,21 +115,6 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence, u64 *period_start, u64 *period_end); -/** - * Preliminary prefix check to see if the string might be a bolt12 invoice. - */ -bool bolt12_has_invoice_prefix(const char *str); - -/** - * Preliminary prefix check to see if the string might be a bolt12 request. - */ -bool bolt12_has_request_prefix(const char *str); - -/** - * Preliminary prefix check to see if the string might be a bolt12 offer. - */ -bool bolt12_has_offer_prefix(const char *str); - /** * Preliminary prefix check to see if the string might be a bolt12 string. */ diff --git a/common/dijkstra.c b/common/dijkstra.c index 5607a1c606ab..1920017a8413 100644 --- a/common/dijkstra.c +++ b/common/dijkstra.c @@ -35,12 +35,6 @@ u32 dijkstra_distance(const struct dijkstra *dij, u32 node_idx) return dij[node_idx].distance; } -/* Total CLTV delay */ -u32 dijkstra_delay(const struct dijkstra *dij, u32 node_idx) -{ - return dij[node_idx].total_delay; -} - struct gossmap_chan *dijkstra_best_chan(const struct dijkstra *dij, u32 node_idx) { diff --git a/common/dijkstra.h b/common/dijkstra.h index 788182cd66c9..1a7d0a209260 100644 --- a/common/dijkstra.h +++ b/common/dijkstra.h @@ -39,9 +39,6 @@ dijkstra_(const tal_t *ctx, /* Returns UINT_MAX if unreachable. */ u32 dijkstra_distance(const struct dijkstra *dij, u32 node_idx); -/* Total CLTV delay (0 if unreachable) */ -u32 dijkstra_delay(const struct dijkstra *dij, u32 node_idx); - /* Best path we found to here */ struct gossmap_chan *dijkstra_best_chan(const struct dijkstra *dij, u32 node_idx); diff --git a/common/gossip_store.h b/common/gossip_store.h index 5e7d13bce538..b23e99f43cf6 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -43,12 +43,6 @@ struct gossip_hdr { */ u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps); -/** - * Switches the gossip store fd, and gets to the correct offset. - */ -void gossip_store_switch_fd(struct per_peer_state *pps, - int newfd, u64 offset_shorter); - /** * Sets up the tiemstamp filter once they told us to set it.( */ diff --git a/common/htlc_wire.c b/common/htlc_wire.c index 3a175e832e41..d0a024fe4639 100644 --- a/common/htlc_wire.c +++ b/common/htlc_wire.c @@ -5,8 +5,8 @@ #include #include -struct failed_htlc *failed_htlc_dup(const tal_t *ctx, - const struct failed_htlc *f TAKES) +static struct failed_htlc *failed_htlc_dup(const tal_t *ctx, + const struct failed_htlc *f TAKES) { struct failed_htlc *newf; @@ -129,7 +129,7 @@ void towire_failed_htlc(u8 **pptr, const struct failed_htlc *failed) } } -void towire_htlc_state(u8 **pptr, const enum htlc_state hstate) +static void towire_htlc_state(u8 **pptr, const enum htlc_state hstate) { towire_u8(pptr, hstate); } @@ -234,7 +234,7 @@ struct failed_htlc *fromwire_failed_htlc(const tal_t *ctx, const u8 **cursor, si return failed; } -enum htlc_state fromwire_htlc_state(const u8 **cursor, size_t *max) +static enum htlc_state fromwire_htlc_state(const u8 **cursor, size_t *max) { enum htlc_state hstate = fromwire_u8(cursor, max); if (hstate >= HTLC_STATE_INVALID) { diff --git a/common/htlc_wire.h b/common/htlc_wire.h index 0779dbfec1c8..80fe232172b6 100644 --- a/common/htlc_wire.h +++ b/common/htlc_wire.h @@ -74,14 +74,11 @@ struct existing_htlc *new_existing_htlc(const tal_t *ctx, const struct preimage *preimage TAKES, const struct failed_htlc *failed TAKES); -struct failed_htlc *failed_htlc_dup(const tal_t *ctx, const struct failed_htlc *f TAKES); - void towire_added_htlc(u8 **pptr, const struct added_htlc *added); void towire_existing_htlc(u8 **pptr, const struct existing_htlc *existing); void towire_fulfilled_htlc(u8 **pptr, const struct fulfilled_htlc *fulfilled); void towire_failed_htlc(u8 **pptr, const struct failed_htlc *failed); void towire_changed_htlc(u8 **pptr, const struct changed_htlc *changed); -void towire_htlc_state(u8 **pptr, const enum htlc_state hstate); void towire_side(u8 **pptr, const enum side side); void towire_shachain(u8 **pptr, const struct shachain *shachain); @@ -95,7 +92,6 @@ struct failed_htlc *fromwire_failed_htlc(const tal_t *ctx, const u8 **cursor, size_t *max); void fromwire_changed_htlc(const u8 **cursor, size_t *max, struct changed_htlc *changed); -enum htlc_state fromwire_htlc_state(const u8 **cursor, size_t *max); enum side fromwire_side(const u8 **cursor, size_t *max); void fromwire_shachain(const u8 **cursor, size_t *max, struct shachain *shachain); diff --git a/common/json.c b/common/json.c index d48edcb24826..1955b4f8302e 100644 --- a/common/json.c +++ b/common/json.c @@ -300,8 +300,9 @@ const jsmntok_t *json_next(const jsmntok_t *tok) return t; } -const jsmntok_t *json_get_membern(const char *buffer, const jsmntok_t tok[], - const char *label, size_t len) +static const jsmntok_t *json_get_membern(const char *buffer, + const jsmntok_t tok[], + const char *label, size_t len) { const jsmntok_t *t; size_t i; @@ -665,20 +666,6 @@ const char *jsmntype_to_string(jsmntype_t t) return "INVALID"; } -void json_tok_print(const char *buffer, const jsmntok_t *tok) -{ - const jsmntok_t *first = tok; - const jsmntok_t *last = json_next(tok); - printf("size: %d, count: %td\n", tok->size, last - first); - while (first != last) { - printf("%td. %.*s, %s\n", first - tok, - first->end - first->start, buffer + first->start, - jsmntype_to_string(first->type)); - first++; - } - printf("\n"); -} - jsmntok_t *json_tok_copy(const tal_t *ctx, const jsmntok_t *tok) { return tal_dup_arr(ctx, jsmntok_t, tok, json_next(tok) - tok, 0); diff --git a/common/json.h b/common/json.h index 922d5a94f8a2..eb4fb05487f0 100644 --- a/common/json.h +++ b/common/json.h @@ -90,10 +90,6 @@ const jsmntok_t *json_next(const jsmntok_t *tok); const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], const char *label); -/* Get top-level member, with explicit label length */ -const jsmntok_t *json_get_membern(const char *buffer, const jsmntok_t tok[], - const char *label, size_t len); - /* Get index'th array member. */ const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index); @@ -134,9 +130,6 @@ jsmntok_t *json_parse_simple(const tal_t *ctx, const char *input, int len); /* Convert a jsmntype_t enum to a human readable string. */ const char *jsmntype_to_string(jsmntype_t t); -/* Print a json value for debugging purposes. */ -void json_tok_print(const char *buffer, const jsmntok_t *params); - /* Return a copy of a json value as an array. */ jsmntok_t *json_tok_copy(const tal_t *ctx, const jsmntok_t *tok); diff --git a/common/json_helpers.c b/common/json_helpers.c index 17ee699357aa..8d9cb35224c4 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -282,18 +282,6 @@ void json_add_short_channel_id(struct json_stream *response, short_channel_id_outnum(scid)); } -void json_add_short_channel_id_dir(struct json_stream *response, - const char *fieldname, - const struct short_channel_id_dir *scidd) -{ - json_add_member(response, fieldname, true, "%dx%dx%d/%d", - short_channel_id_blocknum(&scidd->scid), - short_channel_id_txnum(&scidd->scid), - short_channel_id_outnum(&scidd->scid), - scidd->dir - ); -} - void json_add_address(struct json_stream *response, const char *fieldname, const struct wireaddr *addr) { diff --git a/common/json_helpers.h b/common/json_helpers.h index 3e10642404a2..e37cb95e835e 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -132,11 +132,6 @@ void json_add_short_channel_id(struct json_stream *response, const char *fieldname, const struct short_channel_id *id); -/* '"fieldname" : "1234:5:6/7"' */ -void json_add_short_channel_id_dir(struct json_stream *response, - const char *fieldname, - const struct short_channel_id_dir *id); - /* JSON serialize a network address for a node */ void json_add_address(struct json_stream *response, const char *fieldname, const struct wireaddr *addr); diff --git a/common/json_stream.c b/common/json_stream.c index 0025eb5b82ea..59a8a88bf0c7 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -43,7 +43,13 @@ struct json_stream *json_stream_dup(const tal_t *ctx, return js; } -bool json_stream_still_writing(const struct json_stream *js) +/** + * json_stream_still_writing - is someone currently writing to this stream? + * @js: the json_stream. + * + * Has this json_stream not been closed yet? + */ +static bool json_stream_still_writing(const struct json_stream *js) { return js->writer != NULL; } diff --git a/common/json_stream.h b/common/json_stream.h index f47f4cf38135..db595ca770f8 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -65,15 +65,6 @@ void json_stream_close(struct json_stream *js, struct command *writer); /* For low-level JSON stream access: */ void json_stream_log_suppress(struct json_stream *js, const char *cmd_name); -/** - * json_stream_still_writing - is someone currently writing to this stream? - * @js: the json_stream. - * - * Has this json_stream not been closed yet? - */ -bool json_stream_still_writing(const struct json_stream *js); - - /* '"fieldname" : [ ' or '[ ' if fieldname is NULL */ void json_array_start(struct json_stream *js, const char *fieldname); /* '"fieldname" : { ' or '{ ' if fieldname is NULL */ diff --git a/common/json_tok.c b/common/json_tok.c index ad88451eb10b..4be957dfe436 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -581,9 +581,11 @@ struct command_result *param_extra_tlvs(struct command *cmd, const char *name, return NULL; } -struct command_result *param_routehint(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - struct route_info **ri) +static struct command_result *param_routehint(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct route_info **ri) { size_t i; const jsmntok_t *curr; diff --git a/common/json_tok.h b/common/json_tok.h index cbaa61dfabdb..e4c59d5ce7e1 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -198,10 +198,6 @@ struct command_result *param_extra_tlvs(struct command *cmd, const char *name, const jsmntok_t *tok, struct tlv_field **fields); -struct command_result *param_routehint(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - struct route_info **ri); - struct command_result * param_routehint_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_info ***ris); diff --git a/common/permute_tx.c b/common/permute_tx.c index bf97206d519e..75fdf6140a71 100644 --- a/common/permute_tx.c +++ b/common/permute_tx.c @@ -2,93 +2,6 @@ #include #include -static bool input_better(const struct wally_tx_input *a, - const struct wally_tx_input *b) -{ - int cmp; - - cmp = memcmp(a->txhash, b->txhash, sizeof(a->txhash)); - if (cmp != 0) - return cmp < 0; - if (a->index != b->index) - return a->index < b->index; - - /* These shouldn't happen, but let's get a canonical order anyway. */ - if (a->script_len != b->script_len) - return a->script_len < b->script_len; - - cmp = memcmp(a->script, b->script, a->script_len); - if (cmp != 0) - return cmp < 0; - return a->sequence < b->sequence; -} - -static size_t find_best_in(struct wally_tx_input *inputs, size_t num) -{ - size_t i, best = 0; - - for (i = 1; i < num; i++) { - if (input_better(&inputs[i], &inputs[best])) - best = i; - } - return best; -} - -static void swap_wally_inputs(struct wally_tx_input *inputs, - struct wally_tx_input *psbt_global_ins, - struct wally_psbt_input *psbt_ins, - const void **map, - size_t i1, size_t i2) -{ - struct wally_tx_input tmpinput; - struct wally_psbt_input tmppsbtin; - const void *tmp; - - if (i1 == i2) - return; - - tmpinput = inputs[i1]; - inputs[i1] = inputs[i2]; - inputs[i2] = tmpinput; - - /* For the PSBT, we swap the psbt inputs and - * the global tx's inputs */ - tmpinput = psbt_global_ins[i1]; - psbt_global_ins[i1] = psbt_global_ins[i2]; - psbt_global_ins[i2] = tmpinput; - - tmppsbtin = psbt_ins[i1]; - psbt_ins[i1] = psbt_ins[i2]; - psbt_ins[i2] = tmppsbtin; - - if (map) { - tmp = map[i1]; - map[i1] = map[i2]; - map[i2] = tmp; - } -} - -void permute_inputs(struct bitcoin_tx *tx, const void **map) -{ - size_t i, best_pos; - struct wally_tx_input *inputs = tx->wtx->inputs; - size_t num_inputs = tx->wtx->num_inputs; - - /* We can't permute nothing! */ - if (num_inputs == 0) - return; - - /* Now do a dumb sort (num_inputs is small). */ - for (i = 0; i < num_inputs-1; i++) { - best_pos = i + find_best_in(inputs + i, num_inputs - i); - /* Swap best into first place. */ - swap_wally_inputs(tx->wtx->inputs, - tx->psbt->tx->inputs, - tx->psbt->inputs, - map, i, best_pos); - } -} - static void swap_wally_outputs(struct wally_tx_output *outputs, struct wally_tx_output *psbt_global_outs, struct wally_psbt_output *psbt_outs, diff --git a/common/permute_tx.h b/common/permute_tx.h index 4107fd430b46..ac44c8e01f3d 100644 --- a/common/permute_tx.h +++ b/common/permute_tx.h @@ -5,13 +5,6 @@ struct htlc; -/** - * permute_inputs: permute the transaction inputs into BIP69 order. - * @tx: the transaction whose inputs are to be sorted (inputs must be tal_arr). - * @map: if non-NULL, pointers to be permuted the same as the inputs. - */ -void permute_inputs(struct bitcoin_tx *tx, const void **map); - /** * permute_outputs: permute the transaction outputs into BIP69 + cltv order. * @tx: the transaction whose outputs are to be sorted (outputs must be tal_arr). From 484222b0a191412ee7a6cb9e937362683cc8e361 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 4 Dec 2021 21:57:06 +1030 Subject: [PATCH 0115/1530] daemons: remove unused functions or make static. Signed-off-by: Rusty Russell --- gossipd/gossip_store.h | 3 - gossipd/gossipd.h | 6 -- lightningd/channel_control.c | 2 +- lightningd/channel_control.h | 3 - lightningd/connect_control.h | 1 - lightningd/gossip_msg.c | 187 ----------------------------------- lightningd/gossip_msg.h | 15 --- lightningd/plugin.c | 105 ++++++++++---------- lightningd/plugin.h | 14 --- plugins/funder_policy.c | 2 +- plugins/funder_policy.h | 14 --- wallet/invoices.h | 16 --- wallet/wallet.c | 112 +++++++-------------- wallet/wallet.h | 95 +----------------- wallet/walletrpc.c | 6 +- wallet/walletrpc.h | 7 -- wire/tlvstream.c | 45 --------- wire/tlvstream.h | 6 -- 18 files changed, 95 insertions(+), 544 deletions(-) diff --git a/gossipd/gossip_store.h b/gossipd/gossip_store.h index 597c781c7af3..f717491896fe 100644 --- a/gossipd/gossip_store.h +++ b/gossipd/gossip_store.h @@ -92,7 +92,4 @@ bool gossip_store_compact(struct gossip_store *gs); */ int gossip_store_readonly_fd(struct gossip_store *gs); -/* Callback inside gossipd when store is compacted */ -void update_peers_broadcast_index(struct list_head *peers, u32 offset); - #endif /* LIGHTNING_GOSSIPD_GOSSIP_STORE_H */ diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index db37dbfdcf9d..fe2082c74b26 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -128,10 +128,4 @@ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); void queue_peer_from_store(struct peer *peer, const struct broadcastable *bcast); -/* Reset gossip range for this peer. */ -void setup_gossip_range(struct peer *peer); - -/* A peer has given us these short channel ids: see if we need to catch up */ -void process_scids(struct daemon *daemon, const struct short_channel_id *scids); - #endif /* LIGHTNING_GOSSIPD_GOSSIPD_H */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index b8d90cbcba23..e6fb0af00ab0 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -404,7 +404,7 @@ static void handle_error_channel(struct channel *channel, forget(channel); } -void forget_channel(struct channel *channel, const char *why) +static void forget_channel(struct channel *channel, const char *why) { channel->error = towire_errorfmt(channel, &channel->cid, "%s", why); diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index bf32b5897ba4..ce91cf68bcfc 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -36,9 +36,6 @@ bool channel_on_funding_locked(struct channel *channel, /* Record channel open (coin movement notifications) */ void channel_record_open(struct channel *channel); -/* Forget a channel. Deletes the channel and handles all - * associated waiting commands, if present. Notifies peer if available */ -void forget_channel(struct channel *channel, const char *err_msg); /* A channel has unrecoverably fallen behind */ void channel_fallen_behind(struct channel *channel, const u8 *msg); diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 9714aa158996..830332b3d37c 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -15,6 +15,5 @@ void delay_then_reconnect(struct channel *channel, u32 seconds_delay, void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr); -void gossip_connect_result(struct lightningd *ld, const u8 *msg); #endif /* LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H */ diff --git a/lightningd/gossip_msg.c b/lightningd/gossip_msg.c index 20dcc5f97625..9e74c5904831 100644 --- a/lightningd/gossip_msg.c +++ b/lightningd/gossip_msg.c @@ -5,113 +5,6 @@ #include #include -struct gossip_getnodes_entry *fromwire_gossip_getnodes_entry(const tal_t *ctx, - const u8 **pptr, size_t *max) -{ - u8 numaddresses, i; - struct gossip_getnodes_entry *entry; - u16 flen; - bool has_rates; - - entry = tal(ctx, struct gossip_getnodes_entry); - fromwire_node_id(pptr, max, &entry->nodeid); - - entry->last_timestamp = fromwire_u64(pptr, max); - if (entry->last_timestamp < 0) { - entry->addresses = NULL; - return entry; - } - - flen = fromwire_u16(pptr, max); - entry->features = fromwire_tal_arrn(entry, pptr, max, flen); - - numaddresses = fromwire_u8(pptr, max); - - entry->addresses = tal_arr(entry, struct wireaddr, numaddresses); - for (i=0; iaddresses[i])) - return fromwire_fail(pptr, max); - } - fromwire(pptr, max, entry->alias, ARRAY_SIZE(entry->alias)); - fromwire(pptr, max, entry->color, ARRAY_SIZE(entry->color)); - - has_rates = fromwire_u8(pptr, max); - if (has_rates) { - entry->rates = tal(entry, struct lease_rates); - fromwire_lease_rates(pptr, max, entry->rates); - } else - entry->rates = NULL; - - return entry; -} - -void towire_gossip_getnodes_entry(u8 **pptr, - const struct gossip_getnodes_entry *entry) -{ - towire_node_id(pptr, &entry->nodeid); - towire_u64(pptr, entry->last_timestamp); - - if (entry->last_timestamp < 0) - return; - - towire_u16(pptr, tal_count(entry->features)); - towire_u8_array(pptr, entry->features, tal_count(entry->features)); - towire_u8(pptr, tal_count(entry->addresses)); - for (size_t i = 0; i < tal_count(entry->addresses); i++) { - towire_wireaddr(pptr, &entry->addresses[i]); - } - towire(pptr, entry->alias, ARRAY_SIZE(entry->alias)); - towire(pptr, entry->color, ARRAY_SIZE(entry->color)); - - if (entry->rates) { - towire_u8(pptr, 1); - towire_lease_rates(pptr, entry->rates); - } else - towire_u8(pptr, 0); -} - -struct route_hop *fromwire_route_hop(const tal_t *ctx, - const u8 **pptr, size_t *max) -{ - struct route_hop *entry = tal(ctx, struct route_hop); - size_t enclen; - - fromwire_node_id(pptr, max, &entry->node_id); - fromwire_short_channel_id(pptr, max, &entry->scid); - entry->direction = fromwire_u8(pptr, max); - entry->amount = fromwire_amount_msat(pptr, max); - entry->delay = fromwire_u32(pptr, max); - entry->style = fromwire_u8(pptr, max); - if (fromwire_bool(pptr, max)) { - entry->blinding = tal(entry, struct pubkey); - fromwire_pubkey(pptr, max, entry->blinding); - } - enclen = fromwire_u16(pptr, max); - if (enclen) - entry->enctlv = fromwire_tal_arrn(entry, pptr, max, enclen); - else - entry->enctlv = NULL; - return entry; -} - -void towire_route_hop(u8 **pptr, const struct route_hop *entry) -{ - towire_node_id(pptr, &entry->node_id); - towire_short_channel_id(pptr, &entry->scid); - towire_u8(pptr, entry->direction); - towire_amount_msat(pptr, entry->amount); - towire_u32(pptr, entry->delay); - towire_u8(pptr, entry->style); - if (entry->blinding) { - towire_bool(pptr, true); - towire_pubkey(pptr, entry->blinding); - } else - towire_bool(pptr, false); - towire_u16(pptr, tal_bytelen(entry->enctlv)); - towire_u8_array(pptr, entry->enctlv, tal_bytelen(entry->enctlv)); -} - void fromwire_route_info(const u8 **pptr, size_t *max, struct route_info *entry) { fromwire_node_id(pptr, max, &entry->pubkey); @@ -129,83 +22,3 @@ void towire_route_info(u8 **pptr, const struct route_info *entry) towire_u32(pptr, entry->fee_proportional_millionths); towire_u16(pptr, entry->cltv_expiry_delta); } - -static void fromwire_gossip_halfchannel_entry(const u8 **pptr, size_t *max, - struct gossip_halfchannel_entry *entry) -{ - entry->message_flags = fromwire_u8(pptr, max); - entry->channel_flags = fromwire_u8(pptr, max); - entry->last_update_timestamp = fromwire_u32(pptr, max); - entry->delay = fromwire_u32(pptr, max); - entry->base_fee_msat = fromwire_u32(pptr, max); - entry->fee_per_millionth = fromwire_u32(pptr, max); - entry->min = fromwire_amount_msat(pptr, max); - entry->max = fromwire_amount_msat(pptr, max); -} - -struct gossip_getchannels_entry * -fromwire_gossip_getchannels_entry(const tal_t *ctx, - const u8 **pptr, size_t *max) -{ - struct gossip_getchannels_entry *entry; - - entry= tal(ctx, struct gossip_getchannels_entry); - fromwire_node_id(pptr, max, &entry->node[0]); - fromwire_node_id(pptr, max, &entry->node[1]); - entry->sat = fromwire_amount_sat(pptr, max); - fromwire_short_channel_id(pptr, max, &entry->short_channel_id); - entry->public = fromwire_bool(pptr, max); - entry->local_disabled = fromwire_bool(pptr, max); - entry->features = fromwire_tal_arrn(entry, - pptr, max, fromwire_u16(pptr, max)); - - if (fromwire_bool(pptr, max)) { - entry->e[0] = tal(entry, struct gossip_halfchannel_entry); - fromwire_gossip_halfchannel_entry(pptr, max, entry->e[0]); - } else - entry->e[0] = NULL; - if (fromwire_bool(pptr, max)) { - entry->e[1] = tal(entry, struct gossip_halfchannel_entry); - fromwire_gossip_halfchannel_entry(pptr, max, entry->e[1]); - } else - entry->e[1] = NULL; - - return entry; -} - -static void towire_gossip_halfchannel_entry(u8 **pptr, - const struct gossip_halfchannel_entry *entry) -{ - towire_u8(pptr, entry->message_flags); - towire_u8(pptr, entry->channel_flags); - towire_u32(pptr, entry->last_update_timestamp); - towire_u32(pptr, entry->delay); - towire_u32(pptr, entry->base_fee_msat); - towire_u32(pptr, entry->fee_per_millionth); - towire_amount_msat(pptr, entry->min); - towire_amount_msat(pptr, entry->max); -} - -void towire_gossip_getchannels_entry(u8 **pptr, - const struct gossip_getchannels_entry *entry) -{ - towire_node_id(pptr, &entry->node[0]); - towire_node_id(pptr, &entry->node[1]); - towire_amount_sat(pptr, entry->sat); - towire_short_channel_id(pptr, &entry->short_channel_id); - towire_bool(pptr, entry->public); - towire_bool(pptr, entry->local_disabled); - towire_u16(pptr, tal_bytelen(entry->features)); - towire_u8_array(pptr, entry->features, tal_bytelen(entry->features)); - if (entry->e[0]) { - towire_bool(pptr, true); - towire_gossip_halfchannel_entry(pptr, entry->e[0]); - } else - towire_bool(pptr, false); - - if (entry->e[1]) { - towire_bool(pptr, true); - towire_gossip_halfchannel_entry(pptr, entry->e[1]); - } else - towire_bool(pptr, false); -} diff --git a/lightningd/gossip_msg.h b/lightningd/gossip_msg.h index b259705d93ec..3e65236d6fb7 100644 --- a/lightningd/gossip_msg.h +++ b/lightningd/gossip_msg.h @@ -38,22 +38,7 @@ struct gossip_getchannels_entry { u8 *features; }; -struct gossip_getnodes_entry * -fromwire_gossip_getnodes_entry(const tal_t *ctx, const u8 **pptr, size_t *max); -void towire_gossip_getnodes_entry(u8 **pptr, - const struct gossip_getnodes_entry *entry); - -struct route_hop *fromwire_route_hop(const tal_t *ctx, - const u8 **pptr, size_t *max); -void towire_route_hop(u8 **pprt, const struct route_hop *entry); - void fromwire_route_info(const u8 **pprt, size_t *max, struct route_info *entry); void towire_route_info(u8 **pprt, const struct route_info *entry); -struct gossip_getchannels_entry * -fromwire_gossip_getchannels_entry(const tal_t *ctx, - const u8 **pptr, size_t *max); -void towire_gossip_getchannels_entry( - u8 **pptr, const struct gossip_getchannels_entry *entry); - #endif /* LIGHTNING_LIGHTNINGD_GOSSIP_MSG_H */ diff --git a/lightningd/plugin.c b/lightningd/plugin.c index d37b6135cac1..142f20ed143b 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -110,6 +110,30 @@ static void plugin_check_subscriptions(struct plugins *plugins, } } +static bool plugins_any_in_state(const struct plugins *plugins, + enum plugin_state state) +{ + const struct plugin *p; + + list_for_each(&plugins->plugins, p, list) { + if (p->plugin_state == state) + return true; + } + return false; +} + +static bool plugins_all_in_state(const struct plugins *plugins, + enum plugin_state state) +{ + const struct plugin *p; + + list_for_each(&plugins->plugins, p, list) { + if (p->plugin_state != state) + return false; + } + return true; +} + /* Once they've all replied with their manifests, we can order them. */ static void check_plugins_manifests(struct plugins *plugins) { @@ -493,6 +517,36 @@ static const char *plugin_notification_handle(struct plugin *plugin, return NULL; } +struct plugin_destroyed { + const struct plugin *plugin; +}; + +static void mark_plugin_destroyed(const struct plugin *unused, + struct plugin_destroyed *pd) +{ + pd->plugin = NULL; +} + +static struct plugin_destroyed * +plugin_detect_destruction(const struct plugin *plugin) +{ + struct plugin_destroyed *pd = tal(NULL, struct plugin_destroyed); + pd->plugin = plugin; + tal_add_destructor2(plugin, mark_plugin_destroyed, pd); + return pd; +} + +static bool was_plugin_destroyed(struct plugin_destroyed *pd) +{ + if (pd->plugin) { + tal_del_destructor2(pd->plugin, mark_plugin_destroyed, pd); + tal_free(pd); + return false; + } + tal_free(pd); + return true; +} + /* Returns the error string, or NULL */ static const char *plugin_response_handle(struct plugin *plugin, const jsmntok_t *toks, @@ -1459,28 +1513,6 @@ static const char *plugin_parse_getmanifest_response(const char *buffer, return err; } -bool plugins_any_in_state(const struct plugins *plugins, enum plugin_state state) -{ - const struct plugin *p; - - list_for_each(&plugins->plugins, p, list) { - if (p->plugin_state == state) - return true; - } - return false; -} - -bool plugins_all_in_state(const struct plugins *plugins, enum plugin_state state) -{ - const struct plugin *p; - - list_for_each(&plugins->plugins, p, list) { - if (p->plugin_state != state) - return false; - } - return true; -} - /** * Callback for the plugin_manifest request. */ @@ -2056,35 +2088,6 @@ void plugins_set_builtin_plugins_dir(struct plugins *plugins, NULL, NULL); } -struct plugin_destroyed { - const struct plugin *plugin; -}; - -static void mark_plugin_destroyed(const struct plugin *unused, - struct plugin_destroyed *pd) -{ - pd->plugin = NULL; -} - -struct plugin_destroyed *plugin_detect_destruction(const struct plugin *plugin) -{ - struct plugin_destroyed *pd = tal(NULL, struct plugin_destroyed); - pd->plugin = plugin; - tal_add_destructor2(plugin, mark_plugin_destroyed, pd); - return pd; -} - -bool was_plugin_destroyed(struct plugin_destroyed *pd) -{ - if (pd->plugin) { - tal_del_destructor2(pd->plugin, mark_plugin_destroyed, pd); - tal_free(pd); - return false; - } - tal_free(pd); - return true; -} - static void plugin_shutdown_timeout(struct lightningd *ld) { io_break(plugin_shutdown_timeout); diff --git a/lightningd/plugin.h b/lightningd/plugin.h index 5aef22ce1bd2..d26509472a30 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -266,16 +266,6 @@ struct command_result *plugin_register_all_complete(struct lightningd *ld, */ void plugins_config(struct plugins *plugins); -/** - * Are any plugins at this state still? - */ -bool plugins_any_in_state(const struct plugins *plugins, enum plugin_state state); - -/** - * Are all plugins at this state? - */ -bool plugins_all_in_state(const struct plugins *plugins, enum plugin_state state); - /** * This populates the jsonrpc request with the plugin/lightningd specifications */ @@ -372,8 +362,4 @@ struct log *plugin_get_log(struct plugin *plugin); void plugins_set_builtin_plugins_dir(struct plugins *plugins, const char *dir); -/* Pair of functions to detect if plugin destroys itself: must always - * call both! */ -struct plugin_destroyed *plugin_detect_destruction(const struct plugin *plugin); -bool was_plugin_destroyed(struct plugin_destroyed *destroyed); #endif /* LIGHTNING_LIGHTNINGD_PLUGIN_H */ diff --git a/plugins/funder_policy.c b/plugins/funder_policy.c index e28fe679cf91..5584cfd58cd0 100644 --- a/plugins/funder_policy.c +++ b/plugins/funder_policy.c @@ -51,7 +51,7 @@ const char *funder_policy_desc(const tal_t *ctx, /* FIXME: add in more info? */ } -struct funder_policy * +static struct funder_policy * new_funder_policy(const tal_t *ctx, enum funder_opt opt, u64 policy_mod, diff --git a/plugins/funder_policy.h b/plugins/funder_policy.h index 80840b3e6fa8..8b38ec51c1a8 100644 --- a/plugins/funder_policy.h +++ b/plugins/funder_policy.h @@ -63,20 +63,6 @@ struct funder_policy { struct lease_rates *rates; }; -struct funder_policy * -new_funder_policy(const tal_t *ctx, - enum funder_opt opt, - u64 policy_mod, - struct amount_sat min_their_funding, - struct amount_sat max_their_funding, - struct amount_sat per_channel_min, - struct amount_sat per_channel_max, - u32 fuzz_factor, - struct amount_sat reserve_tank, - u32 fund_probability, - bool leases_only, - struct lease_rates *rates); - /* Get a new funder_policy, set to the defaults */ struct funder_policy * default_funder_policy(const tal_t *ctx, diff --git a/wallet/invoices.h b/wallet/invoices.h index 7a65971f5c8b..00acf384c772 100644 --- a/wallet/invoices.h +++ b/wallet/invoices.h @@ -118,22 +118,6 @@ bool invoices_delete(struct invoices *invoices, void invoices_delete_expired(struct invoices *invoices, u64 max_expiry_time); -/** - * invoices_autoclean_set - Set up automatic deletion of - * expired invoices. - * - * @invoices - the invoice handler. - * @cycle_seconds - The number of seconds to repeat the - * automatic deletion. If 0, do not perform automatic - * deletion. - * @expiry_by - Each cycle, delete invoices that - * have been expired for at least `expiry_by` - * seconds. - */ -void invoices_autoclean_set(struct invoices *invoices, - u64 cycle_seconds, - u64 expired_by); - /** * invoices_iterate - Iterate over all existing invoices * diff --git a/wallet/wallet.c b/wallet/wallet.c index e035686f4425..98d650ba6a87 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -393,25 +393,6 @@ struct utxo *wallet_utxo_get(const tal_t *ctx, struct wallet *w, return utxo; } -bool wallet_unreserve_output(struct wallet *w, - const struct bitcoin_outpoint *outpoint) -{ - return wallet_update_output_status(w, outpoint, - OUTPUT_STATE_RESERVED, - OUTPUT_STATE_AVAILABLE); -} - -void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos) -{ - for (size_t i = 0; i < tal_count(utxos); i++) { - if (!wallet_update_output_status( - w, &utxos[i]->outpoint, - OUTPUT_STATE_RESERVED, OUTPUT_STATE_SPENT)) { - fatal("Unable to mark output as spent"); - } - } -} - static void db_set_utxo(struct db *db, const struct utxo *utxo) { struct db_stmt *stmt; @@ -634,8 +615,8 @@ bool wallet_add_onchaind_utxo(struct wallet *w, return true; } -bool wallet_can_spend(struct wallet *w, const u8 *script, - u32 *index, bool *output_is_p2sh) +static bool wallet_can_spend(struct wallet *w, const u8 *script, + u32 *index, bool *output_is_p2sh) { struct ext_key ext; u64 bip32_max_index = db_get_intvar(w->db, "bip32_max_index", 0); @@ -774,8 +755,8 @@ bool wallet_shachain_add_hash(struct wallet *wallet, return true; } -bool wallet_shachain_load(struct wallet *wallet, u64 id, - struct wallet_shachain *chain) +static bool wallet_shachain_load(struct wallet *wallet, u64 id, + struct wallet_shachain *chain) { struct db_stmt *stmt; chain->id = id; @@ -1194,6 +1175,37 @@ static bool wallet_channel_load_inflights(struct wallet *w, return ok; } +static bool wallet_channel_config_load(struct wallet *w, const u64 id, + struct channel_config *cc) +{ + bool ok = true; + const char *query = SQL( + "SELECT dust_limit_satoshis, max_htlc_value_in_flight_msat, " + "channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, " + "max_accepted_htlcs, max_dust_htlc_exposure_msat" + " FROM channel_configs WHERE id= ? ;"); + struct db_stmt *stmt = db_prepare_v2(w->db, query); + db_bind_u64(stmt, 0, id); + db_query_prepared(stmt); + + if (!db_step(stmt)) + return false; + + cc->id = id; + db_col_amount_sat(stmt, "dust_limit_satoshis", &cc->dust_limit); + db_col_amount_msat(stmt, "max_htlc_value_in_flight_msat", + &cc->max_htlc_value_in_flight); + db_col_amount_sat(stmt, "channel_reserve_satoshis", + &cc->channel_reserve); + db_col_amount_msat(stmt, "htlc_minimum_msat", &cc->htlc_minimum); + cc->to_self_delay = db_col_int(stmt, "to_self_delay"); + cc->max_accepted_htlcs = db_col_int(stmt, "max_accepted_htlcs"); + db_col_amount_msat(stmt, "max_dust_htlc_exposure_msat", + &cc->max_dust_htlc_exposure_msat); + tal_free(stmt); + return ok; +} + /** * wallet_stmt2channel - Helper to populate a wallet_channel from a `db_stmt` */ @@ -1737,37 +1749,6 @@ static void wallet_channel_config_save(struct wallet *w, db_exec_prepared_v2(take(stmt)); } -bool wallet_channel_config_load(struct wallet *w, const u64 id, - struct channel_config *cc) -{ - bool ok = true; - const char *query = SQL( - "SELECT dust_limit_satoshis, max_htlc_value_in_flight_msat, " - "channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, " - "max_accepted_htlcs, max_dust_htlc_exposure_msat" - " FROM channel_configs WHERE id= ? ;"); - struct db_stmt *stmt = db_prepare_v2(w->db, query); - db_bind_u64(stmt, 0, id); - db_query_prepared(stmt); - - if (!db_step(stmt)) - return false; - - cc->id = id; - db_col_amount_sat(stmt, "dust_limit_satoshis", &cc->dust_limit); - db_col_amount_msat(stmt, "max_htlc_value_in_flight_msat", - &cc->max_htlc_value_in_flight); - db_col_amount_sat(stmt, "channel_reserve_satoshis", - &cc->channel_reserve); - db_col_amount_msat(stmt, "htlc_minimum_msat", &cc->htlc_minimum); - cc->to_self_delay = db_col_int(stmt, "to_self_delay"); - cc->max_accepted_htlcs = db_col_int(stmt, "max_accepted_htlcs"); - db_col_amount_msat(stmt, "max_dust_htlc_exposure_msat", - &cc->max_dust_htlc_exposure_msat); - tal_free(stmt); - return ok; -} - u64 wallet_get_channel_dbid(struct wallet *wallet) { return ++wallet->max_channel_dbid; @@ -3064,29 +3045,6 @@ void wallet_payment_store(struct wallet *wallet, } } -void wallet_payment_delete(struct wallet *wallet, - const struct sha256 *payment_hash, - u64 partid) -{ - struct db_stmt *stmt; - struct wallet_payment *payment; - - payment = find_unstored_payment(wallet, payment_hash, partid); - if (payment) { - tal_free(payment); - return; - } - - stmt = db_prepare_v2( - wallet->db, SQL("DELETE FROM payments WHERE payment_hash = ?" - " AND partid = ?")); - - db_bind_sha256(stmt, 0, payment_hash); - db_bind_u64(stmt, 1, partid); - - db_exec_prepared_v2(take(stmt)); -} - u64 wallet_payment_get_groupid(struct wallet *wallet, const struct sha256 *payment_hash) { diff --git a/wallet/wallet.h b/wallet/wallet.h index 833f3ef60621..619cb464bb1d 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -490,37 +490,6 @@ void wallet_unreserve_utxo(struct wallet *w, struct utxo *utxo, struct utxo *wallet_utxo_get(const tal_t *ctx, struct wallet *w, const struct bitcoin_outpoint *outpoint); -/** - * wallet_select_specific - Select utxos given an array of txids and an array of outputs index - * - * Returns an array of `utxo` structs. - */ -const struct utxo **wallet_select_specific(const tal_t *ctx, struct wallet *w, - struct bitcoin_txid **txids, - u32 **outnums); - -/** - * wallet_confirm_utxos - Once we've spent a set of utxos, mark them confirmed. - * - * May be called once the transaction spending these UTXOs has been - * broadcast. If something fails use `tal_free(utxos)` instead to undo - * the reservation. - */ -void wallet_confirm_utxos(struct wallet *w, const struct utxo **utxos); - -/** - * wallet_can_spend - Do we have the private key matching this scriptpubkey? - * - * FIXME: This is very slow with lots of inputs! - * - * @w: (in) wallet holding the pubkeys to check against (privkeys are on HSM) - * @script: (in) the script to check - * @index: (out) the bip32 derivation index that matched the script - * @output_is_p2sh: (out) whether the script is a p2sh, or p2wpkh - */ -bool wallet_can_spend(struct wallet *w, const u8 *script, - u32 *index, bool *output_is_p2sh); - /** * wallet_get_newindex - get a new index from the wallet. * @ld: (in) lightning daemon @@ -537,17 +506,6 @@ bool wallet_shachain_add_hash(struct wallet *wallet, uint64_t index, const struct secret *hash); -/** - * wallet_shachain_load -- Load an existing shachain from the wallet. - * - * @wallet: the wallet to load from - * @id: the shachain id to load - * @chain: where to load the shachain into - */ -bool wallet_shachain_load(struct wallet *wallet, u64 id, - struct wallet_shachain *chain); - - /** * wallet_get_uncommitted_channel_dbid -- get a unique channel dbid * @@ -617,12 +575,6 @@ struct state_change_entry *wallet_state_change_get(struct wallet *w, */ void wallet_peer_delete(struct wallet *w, u64 peer_dbid); -/** - * wallet_channel_config_load -- Load channel_config from database into cc - */ -bool wallet_channel_config_load(struct wallet *w, const u64 id, - struct channel_config *cc); - /** * wallet_init_channels -- Loads active channels into peers * and inits the dbid counter for next channel. @@ -938,16 +890,6 @@ bool wallet_invoice_delete(struct wallet *wallet, void wallet_invoice_delete_expired(struct wallet *wallet, u64 max_expiry_time); -/** - * wallet_invoice_autoclean - Set up a repeating autoclean of - * expired invoices. - * Cleans (deletes) expired invoices every @cycle_seconds. - * Clean only those invoices that have been expired for at - * least @expired_by seconds or more. - */ -void wallet_invoice_autoclean(struct wallet * wallet, - u64 cycle_seconds, - u64 expired_by); /** * wallet_invoice_iterate - Iterate over all existing invoices @@ -1082,15 +1024,6 @@ void wallet_payment_setup(struct wallet *wallet, struct wallet_payment *payment) void wallet_payment_store(struct wallet *wallet, struct wallet_payment *payment TAKES); -/** - * wallet_payment_delete - Remove a payment - * - * Removes the payment from the database. - */ -void wallet_payment_delete(struct wallet *wallet, - const struct sha256 *payment_hash, - u64 partid); - /** * wallet_payment_delete_by_hash - Remove a payment * @@ -1391,32 +1324,6 @@ bool wallet_remote_ann_sigs_load(const tal_t *ctx, struct wallet *w, u64 id, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig); -/** - * wallet_clean_utxos: clean up any reserved UTXOs on restart. - * @w: wallet - * - * If we crash, it's unclear if we have actually used the inputs. eg. if - * we crash around transaction broadcast. - * - * We ask bitcoind to clarify in this case. - */ -void wallet_clean_utxos(struct wallet *w, struct bitcoind *bitcoind); - -/* Operations for unreleased transactions */ -struct unreleased_tx *find_unreleased_tx(struct wallet *w, - const struct bitcoin_txid *txid); -void remove_unreleased_tx(struct unreleased_tx *utx); -void add_unreleased_tx(struct wallet *w, struct unreleased_tx *utx); - -/* These will touch the db, so need to be explicitly freed. */ -void free_unreleased_txs(struct wallet *w); - -/* wallet_unreserve_output - Unreserve a utxo - * - * We unreserve utxos so that they can be spent elsewhere. - * */ -bool wallet_unreserve_output(struct wallet *w, - const struct bitcoin_outpoint *outpoint); /** * Get a list of transactions that we track in the wallet. * @@ -1553,7 +1460,7 @@ char *wallet_offer_find(const tal_t *ctx, * @w: the wallet * @offer_id: the first offer id (if returns non-NULL) * - * Returns pointer to hand as @stmt to wallet_offer_next(), or NULL. + * Returns pointer to hand as @stmt to wallet_offer_id_next(), or NULL. * If you choose not to call wallet_offer_id_next() you must free it! */ struct db_stmt *wallet_offer_id_first(struct wallet *w, diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 5c1fa2e7aabe..e0c5405afe6b 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -291,9 +291,9 @@ static void json_add_utxo(struct json_stream *response, json_object_end(response); } -void json_add_utxos(struct json_stream *response, - struct wallet *wallet, - struct utxo **utxos) +static void json_add_utxos(struct json_stream *response, + struct wallet *wallet, + struct utxo **utxos) { for (size_t i = 0; i < tal_count(utxos); i++) json_add_utxo(response, NULL, wallet, utxos[i]); diff --git a/wallet/walletrpc.h b/wallet/walletrpc.h index 25af4a0a525f..5a2136fa1aa6 100644 --- a/wallet/walletrpc.h +++ b/wallet/walletrpc.h @@ -1,16 +1,9 @@ #ifndef LIGHTNING_WALLET_WALLETRPC_H #define LIGHTNING_WALLET_WALLETRPC_H #include "config.h" -#include -struct command; -struct json_stream; struct utxo; -void json_add_utxos(struct json_stream *response, - struct wallet *wallet, - struct utxo **utxos); - /* We evaluate reserved timeouts lazily, so use this. */ bool is_reserved(const struct utxo *utxo, u32 current_height); diff --git a/wire/tlvstream.c b/wire/tlvstream.c index 7b529b202b96..9bf10a77431a 100644 --- a/wire/tlvstream.c +++ b/wire/tlvstream.c @@ -82,51 +82,6 @@ void tlvstream_set_tu32(struct tlv_field **stream, u64 type, u32 value) tlvstream_set_raw(stream, type, take(ser), tal_bytelen(ser)); } -bool tlvstream_get_short_channel_id(struct tlv_field *stream, u64 type, - struct short_channel_id *value) -{ - struct tlv_field *raw = tlvstream_get_raw(stream, type); - const u8 *v; - size_t max; - if (raw == NULL || raw->length != 8) - return false; - - max = raw->length; - v = raw->value; - fromwire_short_channel_id(&v, &max, value); - - return true; -} - -bool tlvstream_get_tu64(struct tlv_field *stream, u64 type, u64 *value) -{ - struct tlv_field *raw = tlvstream_get_raw(stream, type); - const u8 *v; - size_t max; - if (raw == NULL || raw->length != 8) - return false; - - max = raw->length; - v = raw->value; - *value = fromwire_tu64(&v, &max); - - return true; -} - -bool tlvstream_get_tu32(struct tlv_field *stream, u64 type, u32 *value) -{ - struct tlv_field *raw = tlvstream_get_raw(stream, type); - const u8 *v; - size_t max; - if (raw == NULL || raw->length != 8) - return false; - - max = raw->length; - v = raw->value; - *value = fromwire_tu64(&v, &max); - return true; -} - bool fromwire_tlv(const u8 **cursor, size_t *max, const struct tlv_record_type *types, size_t num_types, void *record, struct tlv_field **fields) diff --git a/wire/tlvstream.h b/wire/tlvstream.h index eb155ce4591a..e289513e60be 100644 --- a/wire/tlvstream.h +++ b/wire/tlvstream.h @@ -55,10 +55,4 @@ void tlvstream_set_short_channel_id(struct tlv_field **stream, u64 type, void tlvstream_set_tu64(struct tlv_field **stream, u64 type, u64 value); void tlvstream_set_tu32(struct tlv_field **stream, u64 type, u32 value); -/* Generic primitive gettes for tlvstreams. */ -bool tlvstream_get_short_channel_id(struct tlv_field *stream, u64 type, - struct short_channel_id *value); -bool tlvstream_get_tu64(struct tlv_field *stream, u64 type, u64 *value); -bool tlvstream_get_tu32(struct tlv_field *stream, u64 type, u32 *value); - #endif /* LIGHTNING_WIRE_TLVSTREAM_H */ From d22fd599973175e3d1a484a7aa2efd9916116bc5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 4 Dec 2021 21:57:32 +1030 Subject: [PATCH 0116/1530] gossipd: remove gossip_msg.[ch] This was a remnant from when we used to get routing from gossipd. Signed-off-by: Rusty Russell --- channeld/Makefile | 1 - connectd/Makefile | 1 - connectd/connectd.c | 2 ++ connectd/connectd_wire.csv | 3 ++- gossipd/Makefile | 1 - gossipd/gossipd.c | 2 +- lightningd/Makefile | 1 - lightningd/gossip_msg.c | 24 --------------------- lightningd/gossip_msg.h | 44 -------------------------------------- 9 files changed, 5 insertions(+), 74 deletions(-) delete mode 100644 lightningd/gossip_msg.c delete mode 100644 lightningd/gossip_msg.h diff --git a/channeld/Makefile b/channeld/Makefile index 5e7626c37962..6f26ef416219 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -92,7 +92,6 @@ CHANNELD_COMMON_OBJS := \ common/wireaddr.o \ gossipd/gossipd_peerd_wiregen.o \ gossipd/gossip_store_wiregen.o \ - lightningd/gossip_msg.o \ wire/fromwire.o \ wire/towire.o diff --git a/connectd/Makefile b/connectd/Makefile index 8e17d4c17a8d..76cb7079f7ce 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -71,7 +71,6 @@ CONNECTD_COMMON_OBJS := \ common/wireaddr.o \ common/wire_error.o \ gossipd/gossipd_wiregen.o \ - lightningd/gossip_msg.o \ wire/onion$(EXP)_wiregen.o lightningd/lightning_connectd: $(CONNECTD_OBJS) $(CONNECTD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(HSMD_CLIENT_OBJS) diff --git a/connectd/connectd.c b/connectd/connectd.c index 3bebaae02c40..1a2130a8b5da 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -10,7 +10,9 @@ #include "config.h" #include #include +#include #include +#include #include #include #include diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 0719838d380c..bddaf972af7e 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -1,8 +1,9 @@ +#include #include #include +#include #include #include -#include msgtype,connectd_init,2000 msgdata,connectd_init,chainparams,chainparams, diff --git a/gossipd/Makefile b/gossipd/Makefile index 465c4bb6fd41..f31acfe92c0c 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -75,7 +75,6 @@ GOSSIPD_COMMON_OBJS := \ common/wireaddr.o \ common/wire_error.o \ connectd/connectd_gossipd_wiregen.o \ - lightningd/gossip_msg.o \ wire/onion$(EXP)_wiregen.o lightningd/lightning_gossipd: $(GOSSIPD_OBJS) $(GOSSIPD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(HSMD_CLIENT_OBJS) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 6b1c7a4ab821..250cd7f4fb8d 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -34,8 +34,8 @@ #include #include #include +#include #include -#include #include /*~ A channel consists of a `struct half_chan` for each direction, each of diff --git a/lightningd/Makefile b/lightningd/Makefile index 898b0b44c684..2cdb6b0d00b7 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -11,7 +11,6 @@ LIGHTNINGD_SRC := \ lightningd/connect_control.c \ lightningd/onion_message.c \ lightningd/gossip_control.c \ - lightningd/gossip_msg.c \ lightningd/hsm_control.c \ lightningd/htlc_end.c \ lightningd/htlc_set.c \ diff --git a/lightningd/gossip_msg.c b/lightningd/gossip_msg.c deleted file mode 100644 index 9e74c5904831..000000000000 --- a/lightningd/gossip_msg.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include - -void fromwire_route_info(const u8 **pptr, size_t *max, struct route_info *entry) -{ - fromwire_node_id(pptr, max, &entry->pubkey); - fromwire_short_channel_id(pptr, max, &entry->short_channel_id); - entry->fee_base_msat = fromwire_u32(pptr, max); - entry->fee_proportional_millionths = fromwire_u32(pptr, max); - entry->cltv_expiry_delta = fromwire_u16(pptr, max); -} - -void towire_route_info(u8 **pptr, const struct route_info *entry) -{ - towire_node_id(pptr, &entry->pubkey); - towire_short_channel_id(pptr, &entry->short_channel_id); - towire_u32(pptr, entry->fee_base_msat); - towire_u32(pptr, entry->fee_proportional_millionths); - towire_u16(pptr, entry->cltv_expiry_delta); -} diff --git a/lightningd/gossip_msg.h b/lightningd/gossip_msg.h deleted file mode 100644 index 3e65236d6fb7..000000000000 --- a/lightningd/gossip_msg.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef LIGHTNING_LIGHTNINGD_GOSSIP_MSG_H -#define LIGHTNING_LIGHTNINGD_GOSSIP_MSG_H -#include "config.h" -#include -#include - -struct route_info; -struct lease_rates; - -struct gossip_getnodes_entry { - struct node_id nodeid; - s64 last_timestamp; /* -1 means never: following fields ignored */ - u8 *features; - struct wireaddr *addresses; - u8 alias[32]; - u8 color[3]; - struct lease_rates *rates; -}; - -struct gossip_halfchannel_entry { - u8 message_flags; - u8 channel_flags; - u32 last_update_timestamp; - u32 delay; - u32 base_fee_msat; - u32 fee_per_millionth; - struct amount_msat min, max; -}; - -struct gossip_getchannels_entry { - struct node_id node[2]; - struct amount_sat sat; - struct short_channel_id short_channel_id; - bool public; - bool local_disabled; - /* NULL if we haven't received an update */ - struct gossip_halfchannel_entry *e[2]; - u8 *features; -}; - -void fromwire_route_info(const u8 **pprt, size_t *max, struct route_info *entry); -void towire_route_info(u8 **pprt, const struct route_info *entry); - -#endif /* LIGHTNING_LIGHTNINGD_GOSSIP_MSG_H */ From 78fb78478b673fceac7180faa6a5b6336a6bcd8e Mon Sep 17 00:00:00 2001 From: Andrew Toth Date: Thu, 2 Dec 2021 11:31:12 -0500 Subject: [PATCH 0117/1530] make: remove generated files when running clean --- Makefile | 2 +- wallet/Makefile | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c95e8156ca90..f2b83f2d9b03 100644 --- a/Makefile +++ b/Makefile @@ -610,7 +610,6 @@ distclean: clean maintainer-clean: distclean @echo 'This command is intended for maintainers to use; it' @echo 'deletes files that may need special tools to rebuild.' - $(RM) $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES) # We used to have gen_ files, now we have _gen files. obsclean: @@ -618,6 +617,7 @@ obsclean: clean: obsclean $(RM) $(CCAN_OBJS) $(CDUMP_OBJS) $(ALL_OBJS) + $(RM) $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES) $(RM) $(ALL_PROGRAMS) $(RM) $(ALL_TEST_PROGRAMS) $(RM) $(ALL_FUZZ_TARGETS) diff --git a/wallet/Makefile b/wallet/Makefile index 4697680607c8..007872f4c151 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -46,7 +46,8 @@ wallet/db_%_sqlgen.c: wallet/statements_gettextgen.po devtools/sql-rewrite.py $( $(call VERBOSE,"sql-rewrite $@",devtools/sql-rewrite.py wallet/statements_gettextgen.po $* > $@ && $(call SHA256STAMP,//,)); \ fi -maintainer-clean: wallet-maintainer-clean +maintainer-clean: clean +clean: wallet-maintainer-clean wallet-maintainer-clean: $(RM) wallet/statements.po $(RM) wallet/statements_gettextgen.po From 96f28323bdcc0b177eab92b7a5e0796c635949cc Mon Sep 17 00:00:00 2001 From: Jan Sarenik Date: Mon, 1 Nov 2021 21:17:20 +0100 Subject: [PATCH 0118/1530] Set default port according to network The idea is to have different default ports for different networks. Current default port is `9735` for everything. Let's use it for the mainnet and reuse the difference added to the default port from `rpc_port` values in `bitcoin/chainstate.c`. Testnet would be `19735` (adding rpc_port - 8332 = `10000`). Signet would be `39735` (adding rpc_port - 8332 = `30000`). Regtest would be `19846` (adding rpc_port - 8332 = `10111`). With Vincenzo's kind pair-programming help over tmate. Two other commits were squashed into this one so that bisecting never ends up in half-baked state: 1. chainparams: Fix regtest default rpc_port bitcoind -help says this: -rpcport= Listen for JSON-RPC connections on (default: 8332, testnet: 18332, signet: 38332, regtest: 18443) 2. test_gossip: Default port for regtest hex: 2607 is now .... (could be 4d86 but Elements uses another port) dec: 9735 is now any port (could be 19846 ^^ but now is for any port) The lines which were binding to default port were removed as the default port is different on each network. NOTE: Remember not to modify gossip_store tests which loads everything raw including the checksums. Changelog-Changed: If the port is unspecified, the default port is chosen according to used network similarly to Bitcoin Core. --- bitcoin/chainparams.c | 2 +- lightningd/lightningd.c | 5 ++++- tests/test_gossip.py | 16 +++++----------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index d1e6158662d7..4f0134cd4417 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -64,7 +64,7 @@ const struct chainparams networks[] = { 0x5b, 0xbf, 0x28, 0xc3, 0x4f, 0x3a, 0x5e, 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f}}}}, - .rpc_port = 18332, + .rpc_port = 18443, .cli = "bitcoin-cli", .cli_args = "-regtest", .cli_min_supported_version = 150000, diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 5a9f6c9d19c8..cfb0398eff11 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -199,7 +199,6 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * NULL. So we start with a zero-length array. */ ld->proposed_wireaddr = tal_arr(ld, struct wireaddr_internal, 0); ld->proposed_listen_announce = tal_arr(ld, enum addr_listen_announce, 0); - ld->portnum = DEFAULT_PORT; ld->listen = true; ld->autolisten = true; ld->reconnect = true; @@ -954,6 +953,10 @@ int main(int argc, char *argv[]) * non-early opts. This also forks if they say --daemon. */ handle_early_opts(ld, argc, argv); + /*~ Set the default portnum according to the used network + * similarly to what Bitcoin Core does to ports by default. */ + ld->portnum = DEFAULT_PORT + chainparams->rpc_port - 8332; + /*~ Initialize all the plugins we just registered, so they can * do their thing and tell us about themselves (including * options registration). */ diff --git a/tests/test_gossip.py b/tests/test_gossip.py index f66f9c8f794f..2eeefd4092d0 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -139,8 +139,8 @@ def test_announce_address(node_factory, bitcoind): l1.daemon.wait_for_log(r"\[OUT\] 0101.*47" "010102030404d2" "017f000001...." - "02000000000000000000000000000000002607" - "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba50230032607") + "0200000000000000000000000000000000...." + "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba5023003....") return # We should see it send node announce with all addresses (257 = 0x0101) @@ -154,8 +154,8 @@ def test_announce_address(node_factory, bitcoind): l1.daemon.wait_for_log(r"\[OUT\] 0101.*0063" "010102030404d2" # IPv4 01 1.2.3.4:1234 "017f000001...." # IPv4 01 127.0.0.1:wxyz - "02000000000000000000000000000000002607" # IPv6 02 :::9735 - "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba50230032607" # TORv3 04 + "0200000000000000000000000000000000...." # IPv6 02 ::: + "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba5023003...." # TORv3 04 "05096c6f63616c686f737404d3" # DNS 05 len localhost:1235 "050b6578616d706c652e636f6d04d4") # DNS 05 len example.com:1236 @@ -1019,10 +1019,7 @@ def test_gossip_addresses(node_factory, bitcoind): l1 = node_factory.get_node(options={ 'announce-addr': [ '[::]:3', - '[::]', '127.0.0.1:2', - '127.0.0.1', - 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', '4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion:1234' ], }) @@ -1037,10 +1034,7 @@ def test_gossip_addresses(node_factory, bitcoind): nodes = l2.rpc.listnodes(l1.info['id'])['nodes'] assert len(nodes) == 1 and nodes[0]['addresses'] == [ {'type': 'ipv4', 'address': '127.0.0.1', 'port': 2}, - {'type': 'ipv4', 'address': '127.0.0.1', 'port': 9735}, {'type': 'ipv6', 'address': '::', 'port': 3}, - {'type': 'ipv6', 'address': '::', 'port': 9735}, - {'type': 'torv3', 'address': 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', 'port': 9735}, {'type': 'torv3', 'address': '4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion', 'port': 1234}, ] @@ -1912,7 +1906,7 @@ def test_statictor_onions(node_factory): }) assert l1.daemon.is_in_log('127.0.0.1:{}'.format(l1.port)) - assert l2.daemon.is_in_log('x2y4zvh4fn5q3eouuh7nxnc7zeawrqoutljrup2xjtiyxgx3emgkemad.onion:9735,127.0.0.1:{}'.format(l2.port)) + assert l2.daemon.is_in_log('x2y4zvh4fn5q3eouuh7nxnc7zeawrqoutljrup2xjtiyxgx3emgkemad.onion:{},127.0.0.1:{}'.format(l2.port, l2.port)) @pytest.mark.developer("needs a running Tor service instance at port 9151 or 9051") From 4274080d81e930a2c1c8fb007dda55665f314aad Mon Sep 17 00:00:00 2001 From: Jan Sarenik Date: Fri, 5 Nov 2021 09:53:04 +0100 Subject: [PATCH 0119/1530] doc: lightningd-config.5.md - default ports Also nit: s/possible/possibly suggested by michaelfolkson. --- doc/lightningd-config.5.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 7e4c9daa32f3..80e633071c0b 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -345,8 +345,9 @@ network. ### Networking options Note that for simple setups, the implicit *autolisten* option does the -right thing: it will try to bind to port 9735 on IPv4 and IPv6, and will -announce it to peers if it seems like a public address. +right thing: for the mainnet (bitcoin) network it will try to bind to +port 9735 on IPv4 and IPv6, and will announce it to peers if it seems +like a public address. You can instead use *addr* to override this (eg. to change the port), or precisely control where to bind and what to announce with the @@ -360,27 +361,31 @@ Set an IP address (v4 or v6) or automatic Tor address to listen on and An empty 'IPADDRESS' is a special value meaning bind to IPv4 and/or IPv6 on all interfaces, '0.0.0.0' means bind to all IPv4 -interfaces, '::' means 'bind to all IPv6 interfaces'. If 'PORT' is -not specified, 9735 is used. If we can determine a public IP -address from the resulting binding, the address is announced. +interfaces, '::' means 'bind to all IPv6 interfaces'. +If 'PORT' is not specified, the default port 9735 is used for mainnet +(testnet: 19735, signet: 39735, regtest: 19846). +If we can determine a public IP address from the resulting binding, +the address is announced. If the argument begins with 'autotor:' then it is followed by the IPv4 or IPv6 address of the Tor control port (default port 9051), -and this will be used to configure a Tor hidden service for port 9735. +and this will be used to configure a Tor hidden service for port 9735 +in case of mainnet (bitcoin) network whereas other networks (testnet, +signet, regtest) will set the same default ports they use for non-Tor +addresses (see above). The Tor hidden service will be configured to point to the -first IPv4 or IPv6 address we bind to. +first IPv4 or IPv6 address we bind to and is by default unique to +your node's id. If the argument begins with 'statictor:' then it is followed by the IPv4 or IPv6 address of the Tor control port (default port 9051), -and this will be used to configure a static Tor hidden service for port 9735. -The Tor hidden service will be configured to point to the -first IPv4 or IPv6 address we bind to and is by default unique to -your nodes id. You can add the text '/torblob=BLOB' followed by up to +and this will be used to configure a static Tor hidden service. +You can add the text '/torblob=BLOB' followed by up to 64 Bytes of text to generate from this text a v3 onion service address text unique to the first 32 Byte of this text. You can also use an postfix '/torport=TORPORT' to select the external tor binding. The result is that over tor your node is accessible by a port -defined by you and possible different from your local node port assignment +defined by you and possibly different from your local node port assignment. This option can be used multiple times to add more addresses, and its use disables autolisten. If necessary, and 'always-use-proxy' From 8898af1ea7881b294c6b9a562d8a6455ca2a36c9 Mon Sep 17 00:00:00 2001 From: JohnOnGit Date: Thu, 4 Nov 2021 19:06:44 +0100 Subject: [PATCH 0120/1530] Upgrade bitcoin-cli to 22.0 --- Dockerfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index a6b7e1d4734c..3f6713da7774 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,16 +17,16 @@ RUN wget -qO /opt/tini "https://github.com/krallin/tini/releases/download/v0.18. && echo "12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855 /opt/tini" | sha256sum -c - \ && chmod +x /opt/tini -ARG BITCOIN_VERSION=0.18.1 +ARG BITCOIN_VERSION=22.0 ENV BITCOIN_TARBALL bitcoin-${BITCOIN_VERSION}-x86_64-linux-gnu.tar.gz ENV BITCOIN_URL https://bitcoincore.org/bin/bitcoin-core-$BITCOIN_VERSION/$BITCOIN_TARBALL -ENV BITCOIN_ASC_URL https://bitcoincore.org/bin/bitcoin-core-$BITCOIN_VERSION/SHA256SUMS.asc +ENV BITCOIN_ASC_URL https://bitcoincore.org/bin/bitcoin-core-$BITCOIN_VERSION/SHA256SUMS RUN mkdir /opt/bitcoin && cd /opt/bitcoin \ && wget -qO $BITCOIN_TARBALL "$BITCOIN_URL" \ - && wget -qO bitcoin.asc "$BITCOIN_ASC_URL" \ - && grep $BITCOIN_TARBALL bitcoin.asc | tee SHA256SUMS.asc \ - && sha256sum -c SHA256SUMS.asc \ + && wget -qO bitcoin "$BITCOIN_ASC_URL" \ + && grep $BITCOIN_TARBALL bitcoin | tee SHA256SUMS \ + && sha256sum -c SHA256SUMS \ && BD=bitcoin-$BITCOIN_VERSION/bin \ && tar -xzvf $BITCOIN_TARBALL $BD/bitcoin-cli --strip-components=1 \ && rm $BITCOIN_TARBALL @@ -48,7 +48,7 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:buster-slim as builder ENV LIGHTNINGD_VERSION=master -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential git libtool python3 python3-mako wget gnupg dirmngr git gettext +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git gettext RUN wget -q https://zlib.net/zlib-1.2.11.tar.gz \ && tar xvf zlib-1.2.11.tar.gz \ @@ -79,6 +79,7 @@ RUN git clone --recursive /tmp/lightning . && \ ARG DEVELOPER=0 ENV PYTHON_VERSION=3 +RUN pip3 install mrkd RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install FROM debian:buster-slim as final From bad09887e08cadd0082e58b1d0bd4ab7cf7f085c Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sat, 23 Oct 2021 12:32:38 +0200 Subject: [PATCH 0121/1530] pytest: fix test_pluginconnected_hook_chaining The last line of the testcase was checking on the wrong node l3 instead of l1. l3 didn't had the plugins configured that would produce the log entry we were looking for not to be present. Changelog-None --- tests/test_plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 777647d743de..cad8915ce9b2 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -448,7 +448,7 @@ def check_disconnect(): return peers == [] or not peers[0]['connected'] wait_for(check_disconnect) - assert not l3.daemon.is_in_log(f"peer_connected_logger_b {l3id}") + assert not l1.daemon.is_in_log(f"peer_connected_logger_b {l3id}") def test_async_rpcmethod(node_factory, executor): From d5f2b1126cfa9daec75db10648afee04fc614982 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Thu, 25 Nov 2021 22:45:04 +0900 Subject: [PATCH 0122/1530] Fix dockerfile for arm32/64 --- contrib/linuxarm32v7.Dockerfile | 3 ++- contrib/linuxarm64v8.Dockerfile | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/linuxarm32v7.Dockerfile b/contrib/linuxarm32v7.Dockerfile index 7d27ab66945b..e3064b5b3e26 100644 --- a/contrib/linuxarm32v7.Dockerfile +++ b/contrib/linuxarm32v7.Dockerfile @@ -48,7 +48,7 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:buster-slim as builder ENV LIGHTNINGD_VERSION=master -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-mako wget gnupg dirmngr git \ +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git \ libc6-armhf-cross gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf ENV target_host=arm-linux-gnueabihf @@ -92,6 +92,7 @@ RUN git clone --recursive /tmp/lightning . && \ ARG DEVELOPER=0 ENV PYTHON_VERSION=3 +RUN pip3 install mrkd RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install FROM arm32v7/debian:buster-slim as final diff --git a/contrib/linuxarm64v8.Dockerfile b/contrib/linuxarm64v8.Dockerfile index 5f5bf88236bd..bb29aead50fe 100644 --- a/contrib/linuxarm64v8.Dockerfile +++ b/contrib/linuxarm64v8.Dockerfile @@ -48,7 +48,7 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:buster-slim as builder ENV LIGHTNINGD_VERSION=master -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-mako wget gnupg dirmngr git \ +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git \ libc6-arm64-cross gcc-aarch64-linux-gnu g++-aarch64-linux-gnu ENV target_host=aarch64-linux-gnu @@ -91,6 +91,7 @@ RUN git clone --recursive /tmp/lightning . && \ ARG DEVELOPER=0 ENV PYTHON_VERSION=3 +RUN pip3 install mrkd RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install FROM arm64v8/debian:buster-slim as final From c2d2cc127497de195ff9c5706dccda684860c54d Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 8 Dec 2021 12:26:25 +0100 Subject: [PATCH 0123/1530] connectd: fix empty error message 1. Adds the missing DNS error massages so they can be handled by connect_control. 2. Prepends a 'All addresses failed' to code 401 message, so we always have at least some error message to the user. Changelog-None --- connectd/connectd.c | 24 ++++++++++++++++++++---- tests/test_gossip.py | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 1a2130a8b5da..2a7128f493e6 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -948,7 +948,8 @@ static void try_connect_one_addr(struct connecting *connect) connect_failed(connect->daemon, &connect->id, connect->seconds_waited, connect->addrhint, CONNECT_ALL_ADDRESSES_FAILED, - "%s", connect->errors); + "All addresses failed: %s", + connect->errors); tal_free(connect); return; } @@ -992,8 +993,14 @@ static void try_connect_one_addr(struct connecting *connect) #if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ if (use_proxy) /* hand it to the proxy */ break; - if (!use_dns) /* ignore DNS when we can't use it */ + if (!use_dns) { /* ignore DNS when we can't use it */ + tal_append_fmt(&connect->errors, + "%s: dns disabled. ", + type_to_string(tmpctx, + struct wireaddr_internal, + addr)); goto next; + } /* Resolve with getaddrinfo */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; @@ -1005,8 +1012,12 @@ static void try_connect_one_addr(struct connecting *connect) addr->u.wireaddr.port), &hints, &ais); if (gai_err != 0) { - status_debug("DNS with getaddrinfo gave: %s", - gai_strerror(gai_err)); + tal_append_fmt(&connect->errors, + "%s: getaddrinfo error '%s'. ", + type_to_string(tmpctx, + struct wireaddr_internal, + addr), + gai_strerror(gai_err)); goto next; } /* create new addrhints on-the-fly per result ... */ @@ -1032,6 +1043,11 @@ static void try_connect_one_addr(struct connecting *connect) } freeaddrinfo(ais); #endif + tal_append_fmt(&connect->errors, + "%s: EXPERIMENTAL_FEATURES needed. ", + type_to_string(tmpctx, + struct wireaddr_internal, + addr)); goto next; case ADDR_TYPE_WEBSOCKET: af = -1; diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 2eeefd4092d0..466238d73e60 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -232,7 +232,7 @@ def test_announce_and_connect_via_dns(node_factory, bitcoind): # l4 however must not be able to connect because he used '--disable-dns' # This raises RpcError code 401, currently with an empty error message. - with pytest.raises(RpcError, match=r"401"): + with pytest.raises(RpcError, match=r"401.*dns disabled"): l4.rpc.connect(l1.info['id']) From be196b42197b8c119c733233af1b9858ce66134f Mon Sep 17 00:00:00 2001 From: benthecarman Date: Fri, 10 Dec 2021 17:54:22 -0600 Subject: [PATCH 0124/1530] Update .gitignore for tmp files Changelog-None --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 54957608219f..99d8703b2af8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.dSYM *.rej *.pyc +*.tmp .cppcheck-suppress .mypy_cache TAGS From f936fa926f3efcc3454e375ee2f04f801e2bfc14 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 6 Dec 2021 16:28:55 +0200 Subject: [PATCH 0125/1530] plugins: simplify shutdown loop, simply close the db The only thing that needs ld->wallet after this is destroy_invoices_waiter (off jsonrpc) Could not find any other destructors (destroy_*) that need wallet or db access after this. Any db access would now segfault. --- lightningd/lightningd.c | 2 +- lightningd/plugin.c | 26 +++++++++----------------- lightningd/plugin_hook.c | 1 - wallet/db.c | 6 ------ wallet/db.h | 3 --- wallet/db_common.h | 3 --- 6 files changed, 10 insertions(+), 31 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index cfb0398eff11..54b47317d6ce 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1197,7 +1197,7 @@ int main(int argc, char *argv[]) remove_sigchild_handler(sigchld_conn); shutdown_subdaemons(ld); - /* Tell plugins we're shutting down, closes the db for write access. */ + /* Tell plugins we're shutting down, closes the db. */ shutdown_plugins(ld); /* Cleanup JSON RPC separately: destructors assume some list_head * in ld */ diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 142f20ed143b..d1e785468131 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -2088,18 +2088,13 @@ void plugins_set_builtin_plugins_dir(struct plugins *plugins, NULL, NULL); } -static void plugin_shutdown_timeout(struct lightningd *ld) -{ - io_break(plugin_shutdown_timeout); -} - void shutdown_plugins(struct lightningd *ld) { struct plugin *p, *next; - /* Don't complain about important plugins vanishing and - * crash any attempt to write to db. */ + /* Don't complain about important plugins vanishing; close the db. */ ld->plugins->shutdown = true; + ld->wallet->db = tal_free(ld->wallet->db); /* Tell them all to shutdown; if they care. */ list_for_each_safe(&ld->plugins->plugins, p, next, list) { @@ -2110,20 +2105,17 @@ void shutdown_plugins(struct lightningd *ld) /* If anyone was interested in shutdown, give them time. */ if (!list_empty(&ld->plugins->plugins)) { - struct timers *orig_timers, *timer; + struct timers *timer; + struct timer *expired; /* 30 seconds should do it, use a clean timers struct */ - orig_timers = ld->timers; timer = tal(NULL, struct timers); timers_init(timer, time_mono()); - new_reltimer(timer, timer, time_from_sec(30), - plugin_shutdown_timeout, ld); - - ld->timers = timer; - void *ret = io_loop_with_timers(ld); - assert(ret == plugin_shutdown_timeout || ret == destroy_plugin); - ld->timers = orig_timers; - tal_free(timer); + new_reltimer(timer, timer, time_from_sec(30), NULL, NULL); + + void *ret = io_loop(timer, &expired); + assert(ret == NULL || ret == destroy_plugin); + /* Report and free remaining plugins. */ while (!list_empty(&ld->plugins->plugins)) { diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index a1aa68cca992..29a5de2aa16a 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -338,7 +338,6 @@ void plugin_hook_db_sync(struct db *db) size_t i; size_t num_hooks; - db_check_plugins_not_shutdown(db); const char **changes = db_changes(db); num_hooks = tal_count(hook->hooks); if (num_hooks == 0) diff --git a/wallet/db.c b/wallet/db.c index f0117c063390..bfbd9a7291a8 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1276,7 +1276,6 @@ struct db *db_setup(const tal_t *ctx, struct lightningd *ld, struct db *db = db_open(ctx, ld->wallet_dsn); bool migrated; db->log = new_log(db, ld->log_book, NULL, "database"); - db->plugins_shutdown = &ld->plugins->shutdown; db_begin_transaction(db); @@ -2351,11 +2350,6 @@ void db_changes_add(struct db_stmt *stmt, const char * expanded) tal_arr_expand(&db->changes, tal_strdup(db->changes, expanded)); } -void db_check_plugins_not_shutdown(struct db *db) -{ - assert(!*db->plugins_shutdown); -} - const char **db_changes(struct db *db) { return db->changes; diff --git a/wallet/db.h b/wallet/db.h index 9907fdcbd061..e51e75fd6b71 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -249,9 +249,6 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, #define db_prepare_v2(db,query) \ db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) -/* Check that plugins are not shutting down when calling db_write hook */ -void db_check_plugins_not_shutdown(struct db *db); - /** * Access pending changes that have been added to the current transaction. */ diff --git a/wallet/db_common.h b/wallet/db_common.h index ea635f7ebcc6..0ab196c29081 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -34,9 +34,6 @@ struct db { * Used to bump the data_version in the DB.*/ bool dirty; - /* Only needed to check shutdown state of plugins */ - const bool *plugins_shutdown; - /* The current DB version we expect to update if changes are * committed. */ u32 data_version; From 605fda7214780b6ae4bf0d384e724204e33f560d Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 6 Dec 2021 16:45:50 +0200 Subject: [PATCH 0126/1530] common: cleanup unsused parameter in timer_expired() --- channeld/channeld.c | 2 +- common/timeout.c | 2 +- common/timeout.h | 2 +- connectd/connectd.c | 2 +- gossipd/gossipd.c | 2 +- gossipd/test/run-onion_message.c | 2 +- gossipd/test/run-txout_failure.c | 4 ++-- lightningd/io_loop_with_timers.c | 2 +- lightningd/test/run-find_my_abspath.c | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 895d92131984..6b19d94001ef 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -4077,7 +4077,7 @@ int main(int argc, char *argv[]) expired = timers_expire(&peer->timers, now); if (expired) { - timer_expired(peer, expired); + timer_expired(expired); continue; } diff --git a/common/timeout.c b/common/timeout.c index a19b85ffda6c..d4b4b480f1bf 100644 --- a/common/timeout.c +++ b/common/timeout.c @@ -36,7 +36,7 @@ void *reltimer_arg(struct oneshot *t) return t->arg; } -void timer_expired(tal_t *ctx, struct timer *timer) +void timer_expired(struct timer *timer) { struct oneshot *t = container_of(timer, struct oneshot, timer); diff --git a/common/timeout.h b/common/timeout.h index a515e72eb257..55b1a4b633ac 100644 --- a/common/timeout.h +++ b/common/timeout.h @@ -18,6 +18,6 @@ struct oneshot *new_reltimer_(struct timers *timers, /* Get timer arg. */ void *reltimer_arg(struct oneshot *t); -void timer_expired(tal_t *ctx, struct timer *timer); +void timer_expired(struct timer *timer); #endif /* LIGHTNING_COMMON_TIMEOUT_H */ diff --git a/connectd/connectd.c b/connectd/connectd.c index 2a7128f493e6..e855552f9c74 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -2091,7 +2091,7 @@ int main(int argc, char *argv[]) for (;;) { struct timer *expired; io_loop(&daemon->timers, &expired); - timer_expired(daemon, expired); + timer_expired(expired); } } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 250cd7f4fb8d..3fc121b83480 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1548,7 +1548,7 @@ int main(int argc, char *argv[]) struct timer *expired = NULL; io_loop(&daemon->timers, &expired); - timer_expired(daemon, expired); + timer_expired(expired); } } diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 6cfa824e8fcb..473554f3f720 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -302,7 +302,7 @@ void status_setup_async(struct daemon_conn *master UNNEEDED) void subdaemon_setup(int argc UNNEEDED, char *argv[]) { fprintf(stderr, "subdaemon_setup called!\n"); abort(); } /* Generated stub for timer_expired */ -void timer_expired(tal_t *ctx UNNEEDED, struct timer *timer UNNEEDED) +void timer_expired(struct timer *timer UNNEEDED) { fprintf(stderr, "timer_expired called!\n"); abort(); } /* Generated stub for towire_gossipd_addgossip_reply */ u8 *towire_gossipd_addgossip_reply(const tal_t *ctx UNNEEDED, const wirestring *err UNNEEDED) diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 09b1ddbba162..ba7791c329a3 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -162,7 +162,7 @@ int main(int argc, char *argv[]) t = timers_expire(&timers, timemono_add(time_mono(), time_from_sec(3601))); assert(t); - timer_expired(NULL, t); + timer_expired(t); /* Still there, just old. Refresh scid1 */ assert(rstate->num_txout_failures == 0); @@ -172,7 +172,7 @@ int main(int argc, char *argv[]) t = timers_expire(&timers, timemono_add(time_mono(), time_from_sec(3601))); assert(t); - timer_expired(NULL, t); + timer_expired(t); assert(rstate->num_txout_failures == 0); assert(in_txout_failures(rstate, &scid1)); diff --git a/lightningd/io_loop_with_timers.c b/lightningd/io_loop_with_timers.c index 20f9b0618bb2..246ca9979f4b 100644 --- a/lightningd/io_loop_with_timers.c +++ b/lightningd/io_loop_with_timers.c @@ -26,7 +26,7 @@ void *io_loop_with_timers(struct lightningd *ld) /* This routine is legal in early startup, too. */ if (ld->wallet) db_begin_transaction(ld->wallet->db); - timer_expired(ld, expired); + timer_expired(expired); if (ld->wallet) db_commit_transaction(ld->wallet->db); } diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 41db06076c4d..24e53c97abad 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -207,7 +207,7 @@ void shutdown_plugins(struct lightningd *ld UNNEEDED) void stop_topology(struct chain_topology *topo UNNEEDED) { fprintf(stderr, "stop_topology called!\n"); abort(); } /* Generated stub for timer_expired */ -void timer_expired(tal_t *ctx UNNEEDED, struct timer *timer UNNEEDED) +void timer_expired(struct timer *timer UNNEEDED) { fprintf(stderr, "timer_expired called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) From 9b70c9d63bd7916136dc0b0d31b1c08f04735651 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 8 Dec 2021 12:41:58 +0200 Subject: [PATCH 0127/1530] testing: fix flake in test_fetchinvoice_3hop --- tests/test_pay.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index 1232c3a443aa..96922a82f41a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4352,6 +4352,10 @@ def test_fetchinvoice_3hop(node_factory, bitcoind): l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) + # Make sure l4 handled both onions before shutting down + l1.daemon.wait_for_log(r'plugin-fetchinvoice: Received modern onion .*obs2\\":false') + l1.daemon.wait_for_log(r'plugin-fetchinvoice: No match for modern onion.*obs2\\":true') + # Test with obsolete onion. l4.stop() l4.daemon.opts['dev-no-modern-onion'] = None From 28816c387c7069e7e3c5817e6da518438c8ce32b Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 8 Dec 2021 19:58:09 +0200 Subject: [PATCH 0128/1530] testing: remove test_htlc_accepted_hook_shutdown, move relevant parts into test_plugin_shutdown The fact that plugins are kept alive untill the very end also ensures their hooks are not silently unregistered. --- tests/plugins/misc_notifications.py | 8 ++--- tests/test_plugin.py | 53 +++++++++-------------------- 2 files changed, 18 insertions(+), 43 deletions(-) diff --git a/tests/plugins/misc_notifications.py b/tests/plugins/misc_notifications.py index d1b04f320f54..eb019e16a8ef 100755 --- a/tests/plugins/misc_notifications.py +++ b/tests/plugins/misc_notifications.py @@ -3,7 +3,6 @@ """ from pyln.client import Plugin, RpcError -from time import sleep import sys import pytest @@ -30,25 +29,22 @@ def channel_state_changed(plugin, channel_state_changed, **kwargs): @plugin.subscribe("shutdown") def shutdown(plugin, **kwargs): - plugin.log("received shutdown notification") # 'shutdown' notification can be called in two ways, from `plugin stop` or from # lightningd's shutdown loop, we test which one by making `getinfo` call try: plugin.rpc.getinfo() plugin.rpc.datastore(key='test', string='Allowed', mode="create-or-append") - plugin.log("datastore success") + plugin.log("via plugin stop, datastore success") except RpcError as e: if e.error == {'code': -5, 'message': 'lightningd is shutting down'}: # JSON RPC is disabled by now, but can do logging with pytest.raises(RpcError, match=r'-5.*lightningd is shutting down'): plugin.rpc.datastore(key='test', string='Not allowed', mode="create-or-append") - plugin.log("datastore failed") + plugin.log("via lightningd shutdown, datastore failed") else: raise - plugin.log("delaying shutdown with 5s") - sleep(5) sys.exit(0) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index cad8915ce9b2..d80c6febbec2 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1084,38 +1084,6 @@ def test_htlc_accepted_hook_direct_restart(node_factory, executor): f1.result() -def test_htlc_accepted_hook_shutdown(node_factory, executor): - """Hooks of important-plugins are never removed and these plugins are kept - alive until after subdaemons are shutdown. Also tests shutdown notification. - """ - l1, l2 = node_factory.line_graph(2, opts=[ - {'may_reconnect': True, 'log-level': 'info'}, - {'may_reconnect': True, 'log-level': 'debug', - 'plugin': [os.path.join(os.getcwd(), 'tests/plugins/misc_notifications.py')], - 'important-plugin': [os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py')]} - ]) - - l2.rpc.plugin_stop(os.path.join(os.getcwd(), 'tests/plugins/misc_notifications.py')) - l2.daemon.wait_for_log(r'datastore success') - l2.rpc.plugin_start(os.path.join(os.getcwd(), 'tests/plugins/misc_notifications.py')) - - i1 = l2.rpc.invoice(msatoshi=1000, label="inv1", description="desc")['bolt11'] - - # fail_htlcs.py makes payment fail - with pytest.raises(RpcError): - l1.rpc.pay(i1) - - f_stop = executor.submit(l2.rpc.stop) - l2.daemon.wait_for_log(r'plugin-misc_notifications.py: delaying shutdown with 5s') - - # Should still fail htlc while shutting down - with pytest.raises(RpcError): - l1.rpc.pay(i1) - - l2.daemon.wait_for_log(r'datastore failed') - f_stop.result() - - @pytest.mark.developer("without DEVELOPER=1, gossip v slow") def test_htlc_accepted_hook_forward_restart(node_factory, executor): """l2 restarts while it is pondering what to do with an HTLC. @@ -2593,22 +2561,33 @@ def init(options, configuration, plugin): def test_plugin_shutdown(node_factory): - """test 'shutdown' notification""" + """test 'shutdown' notifications, via `plugin stop` or via `stop`""" + p = os.path.join(os.getcwd(), "tests/plugins/test_libplugin") - l1 = node_factory.get_node(options={'plugin': p}) + p2 = os.path.join(os.getcwd(), 'tests/plugins/misc_notifications.py') + l1 = node_factory.get_node(options={'plugin': [p, p2]}) l1.rpc.plugin_stop(p) l1.daemon.wait_for_log(r"test_libplugin: shutdown called") # FIXME: clean this up! l1.daemon.wait_for_log(r"test_libplugin: Killing plugin: exited during normal operation") - # Now try timeout. + # Via `plugin stop` it can make RPC calls before it (self-)terminates + l1.rpc.plugin_stop(p2) + l1.daemon.wait_for_log(r'misc_notifications.py: via plugin stop, datastore success') + l1.rpc.plugin_start(p2) + + # Now try timeout via `plugin stop` l1.rpc.plugin_start(p, dont_shutdown=True) l1.rpc.plugin_stop(p) l1.daemon.wait_for_log(r"test_libplugin: shutdown called") l1.daemon.wait_for_log(r"test_libplugin: Timeout on shutdown: killing anyway") - # Now, should also shutdown on finish. - l1.rpc.plugin_start(p) + # Now, should also shutdown or timeout on finish, RPC calls then fail with error code -5 + l1.rpc.plugin_start(p, dont_shutdown=True) + needle = l1.daemon.logsearch_start l1.rpc.stop() l1.daemon.wait_for_log(r"test_libplugin: shutdown called") + l1.daemon.logsearch_start = needle # we don't know what comes first + l1.daemon.is_in_log(r'misc_notifications.py: via lightningd shutdown, datastore failed') + l1.daemon.is_in_log(r'test_libplugin: failed to self-terminate in time, killing.') From 426ff0abfff8b080b22095b9f0b670c7192f54ea Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 13 Dec 2021 10:49:24 +0200 Subject: [PATCH 0129/1530] lightningd: cleanup obsolete plugins->shutdown flag After leaving the main event loop, the only path to destroy_plugin goes via shutdown_plugins. --- lightningd/lightningd.c | 1 - lightningd/plugin.c | 9 +++------ lightningd/plugin.h | 3 --- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 54b47317d6ce..f406e68d04b9 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -243,7 +243,6 @@ static struct lightningd *new_lightningd(const tal_t *ctx) */ ld->plugins = plugins_new(ld, ld->log_book, ld); ld->plugins->startup = true; - ld->plugins->shutdown = false; /*~ This is set when a JSON RPC command comes in to shut us down. */ ld->stop_conn = NULL; diff --git a/lightningd/plugin.c b/lightningd/plugin.c index d1e785468131..e0dff81885af 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -83,7 +83,6 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book, p->startup = true; p->plugin_cmds = tal_arr(p, struct plugin_command *, 0); p->blacklist = tal_arr(p, const char *, 0); - p->shutdown = false; p->plugin_idx = 0; #if DEVELOPER p->dev_builtin_plugins_unimportant = false; @@ -213,7 +212,7 @@ static void destroy_plugin(struct plugin *p) check_plugins_manifests(p->plugins); /* Daemon shutdown overrules plugin's importance; aborts init checks */ - if (p->plugins->shutdown) { + if (p->plugins->ld->state == LD_STATE_SHUTDOWN) { /* But return if this was the last plugin! */ if (list_empty(&p->plugins->plugins)) io_break(destroy_plugin); @@ -786,7 +785,7 @@ static void plugin_conn_finish(struct io_conn *conn, struct plugin *plugin) { /* This is expected at shutdown of course. */ plugin_kill(plugin, - plugin->plugins->shutdown + plugin->plugins->ld->state == LD_STATE_SHUTDOWN ? LOG_DBG : LOG_INFORM, "exited %s", state_desc(plugin)); } @@ -2092,8 +2091,7 @@ void shutdown_plugins(struct lightningd *ld) { struct plugin *p, *next; - /* Don't complain about important plugins vanishing; close the db. */ - ld->plugins->shutdown = true; + /* The next io_loop does not need db access, close it. */ ld->wallet->db = tal_free(ld->wallet->db); /* Tell them all to shutdown; if they care. */ @@ -2116,7 +2114,6 @@ void shutdown_plugins(struct lightningd *ld) void *ret = io_loop(timer, &expired); assert(ret == NULL || ret == destroy_plugin); - /* Report and free remaining plugins. */ while (!list_empty(&ld->plugins->plugins)) { p = list_pop(&ld->plugins->plugins, struct plugin, list); diff --git a/lightningd/plugin.h b/lightningd/plugin.h index d26509472a30..14b13ff54a73 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -113,9 +113,6 @@ struct plugins { /* Blacklist of plugins from --disable-plugin */ const char **blacklist; - /* Whether we are shutting down, blocks db write's */ - bool shutdown; - /* Index to show what order they were added in */ u64 plugin_idx; From 405239e353730bf80e8919547cdc9ce64c18a113 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 6 Dec 2021 10:33:52 +1030 Subject: [PATCH 0130/1530] doc/Makefile: speed up schema check, only do on check-doc. Don't do this on every build, it takes 4 seconds. And split it into individual targets, so make can parallelize (1.6 seconds here). Signed-off-by: Rusty Russell --- doc/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index d193d8aa4bfb..0c90b195e10f 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -89,15 +89,15 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-help.7 \ doc/lightning-getlog.7 -doc-all: fmt-schema $(MANPAGES) doc/index.rst +doc-all: $(MANPAGES) doc/index.rst -#FIXME(vincenzopalazzo) we don't need to change a file if it is well format, -#so we can use diff to check difference in the real json and in the .fmt -fmt-schema: - @echo "Checking schema fmt!" - @for f in $$(find doc/schemas -type f -name '*.json'); do cat "$$f" | jq . > "$$f.fmt" ; mv "$$f.fmt" "$$f" ; done +SCHEMAS := $(wildcard doc/schemas/*.json) +check-fmt-schemas: $(SCHEMAS:%=check-fmt-schema/%) -check-doc: check-config-docs check-manpages +check-fmt-schema/%: % + @jq . < "$*" > "$*".fmt && diff -u "$*" "$*.fmt" && rm "$*.fmt" + +check-doc: check-config-docs check-manpages check-fmt-schemas # Some manpages use a schema, so need that added. MARKDOWN_WITH_SCHEMA := $(shell grep -l GENERATE-FROM-SCHEMA $(MANPAGES:=.md)) From fe9f426e07a03453417b5b5ce55eb808ce974b79 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Fri, 29 Oct 2021 16:45:39 -0700 Subject: [PATCH 0131/1530] hsmd: Add hsmd_validate_revocation. --- channeld/channeld.c | 14 ++++++++++++++ hsmd/hsmd.c | 2 ++ hsmd/hsmd_wire.csv | 8 ++++++++ hsmd/libhsmd.c | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/channeld/channeld.c b/channeld/channeld.c index 6b19d94001ef..4b1dcf12052c 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1785,6 +1785,7 @@ static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg) struct secret old_commit_secret; struct privkey privkey; struct channel_id channel_id; + const u8 *revocation_msg; struct pubkey per_commit_point, next_per_commit; const struct htlc **changed_htlcs = tal_arr(msg, const struct htlc *, 0); @@ -1799,6 +1800,19 @@ static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg) "Unexpected revoke_and_ack"); } + /* Submit the old revocation secret to the signer so it can + * independently verify that the latest state is commited. It + * is also validated in this routine after the signer returns. + */ + revocation_msg = towire_hsmd_validate_revocation(tmpctx, + peer->next_index[REMOTE] - 2, + &old_commit_secret); + revocation_msg = hsm_req(tmpctx, take(revocation_msg)); + if (!fromwire_hsmd_validate_revocation_reply(revocation_msg)) + status_failed(STATUS_FAIL_HSM_IO, + "Bad hsmd_validate_revocation_reply: %s", + tal_hex(tmpctx, revocation_msg)); + /* BOLT #2: * * A receiving node: diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 8ea2fba9520a..015f7c126bca 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -641,6 +641,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) #endif /* DEVELOPER */ case WIRE_HSMD_SIGN_COMMITMENT_TX: + case WIRE_HSMD_VALIDATE_REVOCATION: case WIRE_HSMD_SIGN_PENALTY_TO_US: case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX: case WIRE_HSMD_SIGN_REMOTE_HTLC_TX: @@ -676,6 +677,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSMD_VALIDATE_REVOCATION_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 2aada572727c..c67d79fce9c8 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -107,6 +107,14 @@ msgdata,hsmd_sign_commitment_tx,remote_funding_key,pubkey, msgtype,hsmd_sign_commitment_tx_reply,105 msgdata,hsmd_sign_commitment_tx_reply,sig,bitcoin_signature, +# Vaidate the counterparty's revocation secret +msgtype,hsmd_validate_revocation,36 +msgdata,hsmd_validate_revocation,revoke_num,u64, +msgdata,hsmd_validate_revocation,per_commitment_secret,secret, + +# No value returned. +msgtype,hsmd_validate_revocation_reply,136 + # Onchaind asks HSM to sign a spend to-us. Four variants, since each set # of keys is derived differently... # FIXME: Have master tell hsmd the keyindex, so it can validate output! diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index ece1f85f89fb..a74a0a82e03f 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -95,6 +95,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX: case WIRE_HSMD_SIGN_REMOTE_HTLC_TX: + case WIRE_HSMD_VALIDATE_REVOCATION: return (client->capabilities & HSM_CAP_SIGN_REMOTE_TX) != 0; case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX: @@ -128,6 +129,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSMD_VALIDATE_REVOCATION_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY: @@ -1258,6 +1260,23 @@ static u8 *handle_sign_commitment_tx(struct hsmd_client *c, const u8 *msg_in) return towire_hsmd_sign_commitment_tx_reply(NULL, &sig); } +/* This stub implementation is overriden by fully validating signers + * that need to independently verify that the latest state is + * commited. */ +static u8 *handle_validate_revocation(struct hsmd_client *c, const u8 *msg_in) +{ + u64 revoke_num; + struct secret old_secret; + + if (!fromwire_hsmd_validate_revocation(msg_in, + &revoke_num, &old_secret)) + return hsmd_status_malformed_request(c, msg_in); + + /* Stub implementation, relies on validation in channeld. */ + + return towire_hsmd_validate_revocation_reply(NULL); +} + /*~ This is used when a commitment transaction is onchain, and has an HTLC * output paying to us (because we have the preimage); this signs that * transaction, which lightningd will broadcast to collect the funds. */ @@ -1431,6 +1450,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_sign_penalty_to_us(client, msg); case WIRE_HSMD_SIGN_COMMITMENT_TX: return handle_sign_commitment_tx(client, msg); + case WIRE_HSMD_VALIDATE_REVOCATION: + return handle_validate_revocation(client, msg); case WIRE_HSMD_SIGN_REMOTE_HTLC_TO_US: return handle_sign_remote_htlc_to_us(client, msg); case WIRE_HSMD_SIGN_DELAYED_PAYMENT_TO_US: @@ -1447,6 +1468,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSMD_VALIDATE_REVOCATION_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY: From 522ee5c59524e0e02169243be2b71f4d58a2c14e Mon Sep 17 00:00:00 2001 From: Denis Ahrens Date: Wed, 10 Nov 2021 18:39:07 +0100 Subject: [PATCH 0132/1530] fix 'listincoming' field incoming_capacity_msat. incoming_capacity_msat field showed the value as microsat. Changelog-Fixed: JSON-RPC: listincoming showed incoming_capacity_msat field 1000 times actual value. --- plugins/topology.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/topology.c b/plugins/topology.c index 9c5444222936..cca6f0cd9c16 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -558,12 +558,12 @@ static struct command_result *json_listnodes(struct command *cmd, } /* What is capacity of peer attached to chan #n? */ -static struct amount_sat peer_capacity(const struct gossmap *gossmap, +static struct amount_msat peer_capacity(const struct gossmap *gossmap, const struct gossmap_node *me, const struct gossmap_node *peer, const struct gossmap_chan *ourchan) { - struct amount_sat capacity = AMOUNT_SAT(0); + struct amount_msat capacity = AMOUNT_MSAT(0); for (size_t i = 0; i < peer->num_chans; i++) { int dir; @@ -573,8 +573,8 @@ static struct amount_sat peer_capacity(const struct gossmap *gossmap, continue; if (!c->half[!dir].enabled) continue; - if (!amount_sat_add(&capacity, capacity, - amount_sat(fp16_to_u64(c->half[dir] + if (!amount_msat_add(&capacity, capacity, + amount_msat(fp16_to_u64(c->half[dir] .htlc_max)))) continue; } @@ -625,7 +625,7 @@ static struct command_result *json_listincoming(struct command *cmd, json_add_u32(js, "fee_proportional_millionths", ourchan->half[!dir].proportional_fee); json_add_u32(js, "cltv_expiry_delta", ourchan->half[!dir].delay); - json_add_amount_sat_only(js, "incoming_capacity_msat", + json_add_amount_msat_only(js, "incoming_capacity_msat", peer_capacity(gossmap, me, peer, ourchan)); json_object_end(js); From 3af15d2f5e634ded3382373f93daf0fe4169e297 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Mon, 29 Nov 2021 15:01:46 +0800 Subject: [PATCH 0133/1530] Revert "bitcoin/chainparams.c: Change `signet` BIP173 name to `tbs`." This reverts commit a642d9f3dcabd7fce624587118fef58ad40e5131. --- bitcoin/chainparams.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index 4f0134cd4417..9286f751957c 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -80,7 +80,7 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_TEST_PRIVATE}, .is_elements = false}, {.network_name = "signet", - .bip173_name = "tbs", + .bip173_name = "tb", .bip70_name = "signet", // 00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6 .genesis_blockhash = {{{.u.u8 = {0xf6, 0x1e, 0xee, 0x3b, 0x63, 0xa3, 0x80, From d088288daa80e0e3f1afbf932b98814ba7d6f9b2 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj jxPCSnmZ Date: Mon, 29 Nov 2021 15:10:06 +0800 Subject: [PATCH 0134/1530] bitcoin/chainparams.h: Split BIP173 name into onchain and Lightning HRPs. Fixes: #4937 --- bitcoin/chainparams.c | 28 ++++++++++++++++++---------- bitcoin/chainparams.h | 10 ++++++++-- common/addr.c | 4 ++-- common/bolt11.c | 10 ++++------ common/bolt11_json.c | 10 +++++----- common/json_tok.c | 6 +++--- devtools/bolt11-cli.c | 10 +++++----- devtools/bolt12-cli.c | 2 +- lightningd/coin_mvts.c | 4 ++-- plugins/offers.c | 4 ++-- wallet/walletrpc.c | 2 +- 11 files changed, 51 insertions(+), 39 deletions(-) diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index 9286f751957c..3785a500c731 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -27,7 +27,8 @@ static u8 liquid_regtest_fee_asset[] = { const struct chainparams networks[] = { {.network_name = "bitcoin", - .bip173_name = "bc", + .onchain_hrp = "bc", + .lightning_hrp = "bc", .bip70_name = "main", .genesis_blockhash = {{{.u.u8 = {0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, @@ -57,7 +58,8 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_MAIN_PRIVATE}, .is_elements = false}, {.network_name = "regtest", - .bip173_name = "bcrt", + .onchain_hrp = "bcrt", + .lightning_hrp = "bcrt", .bip70_name = "regtest", .genesis_blockhash = {{{.u.u8 = {0x06, 0x22, 0x6e, 0x46, 0x11, 0x1a, 0x0b, 0x59, 0xca, 0xaf, 0x12, 0x60, 0x43, 0xeb, @@ -80,7 +82,8 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_TEST_PRIVATE}, .is_elements = false}, {.network_name = "signet", - .bip173_name = "tb", + .onchain_hrp = "tb", + .lightning_hrp = "tbs", .bip70_name = "signet", // 00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6 .genesis_blockhash = {{{.u.u8 = {0xf6, 0x1e, 0xee, 0x3b, 0x63, 0xa3, 0x80, @@ -103,7 +106,8 @@ const struct chainparams networks[] = { .is_elements = false, }, {.network_name = "testnet", - .bip173_name = "tb", + .onchain_hrp = "tb", + .lightning_hrp = "tb", .bip70_name = "test", .genesis_blockhash = {{{.u.u8 = {0x43, 0x49, 0x7f, 0xd7, 0xf8, 0x26, 0x95, 0x71, 0x08, 0xf4, 0xa3, 0x0f, 0xd9, 0xce, @@ -125,7 +129,8 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_TEST_PRIVATE}, .is_elements = false}, {.network_name = "litecoin", - .bip173_name = "ltc", + .onchain_hrp = "ltc", + .lightning_hrp = "ltc", .bip70_name = "main", .genesis_blockhash = {{{.u.u8 = {0xe2, 0xbf, 0x04, 0x7e, 0x7e, 0x5a, 0x19, 0x1a, 0xa4, 0xef, 0x34, 0xd3, 0x14, 0x97, @@ -148,7 +153,8 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_MAIN_PRIVATE}, .is_elements = false}, {.network_name = "litecoin-testnet", - .bip173_name = "tltc", + .onchain_hrp = "tltc", + .lightning_hrp = "tltc", .bip70_name = "test", .genesis_blockhash = {{{.u.u8 = {0xa0, 0x29, 0x3e, 0x4e, 0xeb, 0x3d, 0xa6, 0xe6, 0xf5, 0x6f, 0x81, 0xed, 0x59, 0x5f, @@ -171,7 +177,8 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_TEST_PRIVATE}, .is_elements = false}, {.network_name = "liquid-regtest", - .bip173_name = "ert", + .onchain_hrp = "ert", + .lightning_hrp = "ert", .bip70_name = "liquid-regtest", .genesis_blockhash = {{{.u.u8 = {0x9f, 0x87, 0xeb, 0x58, 0x0b, 0x9e, 0x5f, 0x11, 0xdc, 0x21, 0x1e, 0x9f, 0xb6, 0x6a, @@ -193,7 +200,8 @@ const struct chainparams networks[] = { .bip32_privkey_version = BIP32_VER_TEST_PRIVATE}, .is_elements = true}, {.network_name = "liquid", - .bip173_name = "ex", + .onchain_hrp = "ex", + .lightning_hrp = "ex", .bip70_name = "liquidv1", .genesis_blockhash = {{{.u.u8 = {0x14, 0x66, 0x27, 0x58, 0x36, 0x22, 0x0d, 0xb2, 0x94, 0x4c, 0xa0, 0x59, 0xa3, 0xa1, @@ -236,10 +244,10 @@ const struct chainparams *chainparams_by_chainhash(const struct bitcoin_blkid *c return NULL; } -const struct chainparams *chainparams_by_bip173(const char *bip173_name) +const struct chainparams *chainparams_by_lightning_hrp(const char *lightning_hrp) { for (size_t i = 0; i < ARRAY_SIZE(networks); i++) { - if (streq(bip173_name, networks[i].bip173_name)) { + if (streq(lightning_hrp, networks[i].lightning_hrp)) { return &networks[i]; } } diff --git a/bitcoin/chainparams.h b/bitcoin/chainparams.h index dc16852f2c7d..b4acb2c27de4 100644 --- a/bitcoin/chainparams.h +++ b/bitcoin/chainparams.h @@ -10,7 +10,13 @@ struct chainparams { const char *network_name; - const char *bip173_name; + /* Unfortunately starting with signet, we now have diverging + * conventions for the "BIP173" Human Readable Part (HRP). + * On onchain signet, the HRP is `tb` , but on Lightning + * signet the HRP is `tbs`. + */ + const char *onchain_hrp; + const char *lightning_hrp; /*'bip70_name' is corresponding to the 'chain' field of * the API 'getblockchaininfo' */ const char *bip70_name; @@ -46,7 +52,7 @@ const struct chainparams *chainparams_for_network(const char *network_name); * * This lets us decode BOLT11 addresses. */ -const struct chainparams *chainparams_by_bip173(const char *bip173_name); +const struct chainparams *chainparams_by_lightning_hrp(const char *lightning_hrp); /** * chainparams_by_chainhash - Helper to get a network by its genesis blockhash diff --git a/common/addr.c b/common/addr.c index bbe073c97b5c..97b19b896fa8 100644 --- a/common/addr.c +++ b/common/addr.c @@ -20,8 +20,8 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx, if (is_p2sh(scriptPubkey, &sh)) return p2sh_to_base58(ctx, chainparams, &sh); - out = tal_arr(ctx, char, 73 + strlen(chainparams->bip173_name)); - if (!segwit_addr_encode(out, chainparams->bip173_name, 0, + out = tal_arr(ctx, char, 73 + strlen(chainparams->onchain_hrp)); + if (!segwit_addr_encode(out, chainparams->onchain_hrp, 0, scriptPubkey + 2, scriptLen - 2)) return tal_free(out); diff --git a/common/bolt11.c b/common/bolt11.c index 19be87a69e8c..cad30717f2a1 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -598,17 +598,15 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, return decode_fail(b11, fail, "Prefix '%s' does not start with ln", prefix); - /* Signet chose to use prefix 'tb', just like testnet. So we tread - * carefully here: */ if (must_be_chain) { - if (streq(prefix + 2, must_be_chain->bip173_name)) + if (streq(prefix + 2, must_be_chain->lightning_hrp)) b11->chain = must_be_chain; else return decode_fail(b11, fail, "Prefix %s is not for %s", prefix + 2, must_be_chain->network_name); } else { - b11->chain = chainparams_by_bip173(prefix + 2); + b11->chain = chainparams_by_lightning_hrp(prefix + 2); if (!b11->chain) return decode_fail(b11, fail, "Unknown chain %s", prefix + 2); @@ -1097,9 +1095,9 @@ char *bolt11_encode_(const tal_t *ctx, amount = msat * 10 / multipliers[i].m10; } hrp = tal_fmt(tmpctx, "ln%s%"PRIu64"%c", - b11->chain->bip173_name, amount, postfix); + b11->chain->lightning_hrp, amount, postfix); } else - hrp = tal_fmt(tmpctx, "ln%s", b11->chain->bip173_name); + hrp = tal_fmt(tmpctx, "ln%s", b11->chain->lightning_hrp); /* BOLT #11: * diff --git a/common/bolt11_json.c b/common/bolt11_json.c index a97577e94959..4fc1dacec925 100644 --- a/common/bolt11_json.c +++ b/common/bolt11_json.c @@ -28,15 +28,15 @@ static void json_add_fallback(struct json_stream *response, json_add_string(response, "addr", p2sh_to_base58(tmpctx, chain, &sh)); } else if (is_p2wpkh(fallback, &pkh)) { - char out[73 + strlen(chain->bip173_name)]; + char out[73 + strlen(chain->onchain_hrp)]; json_add_string(response, "type", "P2WPKH"); - if (segwit_addr_encode(out, chain->bip173_name, 0, + if (segwit_addr_encode(out, chain->onchain_hrp, 0, (const u8 *)&pkh, sizeof(pkh))) json_add_string(response, "addr", out); } else if (is_p2wsh(fallback, &wsh)) { - char out[73 + strlen(chain->bip173_name)]; + char out[73 + strlen(chain->onchain_hrp)]; json_add_string(response, "type", "P2WSH"); - if (segwit_addr_encode(out, chain->bip173_name, 0, + if (segwit_addr_encode(out, chain->onchain_hrp, 0, (const u8 *)&wsh, sizeof(wsh))) json_add_string(response, "addr", out); } @@ -47,7 +47,7 @@ static void json_add_fallback(struct json_stream *response, void json_add_bolt11(struct json_stream *response, const struct bolt11 *b11) { - json_add_string(response, "currency", b11->chain->bip173_name); + json_add_string(response, "currency", b11->chain->lightning_hrp); json_add_u64(response, "created_at", b11->timestamp); json_add_u64(response, "expiry", b11->expiry); json_add_node_id(response, "payee", &b11->receiver_id); diff --git a/common/json_tok.c b/common/json_tok.c index 4be957dfe436..5ae6880142bf 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -377,9 +377,9 @@ static const char *segwit_addr_net_decode(int *witness_version, const struct chainparams *chainparams) { if (segwit_addr_decode(witness_version, witness_program, - witness_program_len, chainparams->bip173_name, + witness_program_len, chainparams->onchain_hrp, addrz)) - return chainparams->bip173_name; + return chainparams->onchain_hrp; else return NULL; } @@ -443,7 +443,7 @@ json_to_address_scriptpubkey(const tal_t *ctx, *scriptpubkey = scriptpubkey_witness_raw(ctx, witness_version, witness_program, witness_program_len); parsed = true; - right_network = streq(bip173, chainparams->bip173_name); + right_network = streq(bip173, chainparams->onchain_hrp); } } /* Insert other parsers that accept null-terminated string here. */ diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index 33c088eb0e8b..cb644baab72e 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) if (!b11) errx(ERROR_BAD_DECODE, "%s", fail); - printf("currency: %s\n", b11->chain->bip173_name); + printf("currency: %s\n", b11->chain->lightning_hrp); printf("timestamp: %"PRIu64" (%s)\n", b11->timestamp, fmt_time(ctx, b11->timestamp)); printf("expiry: %"PRIu64" (%s)\n", @@ -136,13 +136,13 @@ int main(int argc, char *argv[]) b11->chain, &sh)); } else if (is_p2wpkh(b11->fallbacks[i], &pkh)) { - char out[73 + strlen(b11->chain->bip173_name)]; - if (segwit_addr_encode(out, b11->chain->bip173_name, 0, + char out[73 + strlen(b11->chain->onchain_hrp)]; + if (segwit_addr_encode(out, b11->chain->onchain_hrp, 0, (const u8 *)&pkh, sizeof(pkh))) printf("fallback-P2WPKH: %s\n", out); } else if (is_p2wsh(b11->fallbacks[i], &wsh)) { - char out[73 + strlen(b11->chain->bip173_name)]; - if (segwit_addr_encode(out, b11->chain->bip173_name, 0, + char out[73 + strlen(b11->chain->onchain_hrp)]; + if (segwit_addr_encode(out, b11->chain->onchain_hrp, 0, (const u8 *)&wsh, sizeof(wsh))) printf("fallback-P2WSH: %s\n", out); } diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index 4191d0848e32..cd8f4878f9de 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -110,7 +110,7 @@ static bool print_amount(const struct bitcoin_blkid *chains, &chains[0])); ok = false; } else - currency = ch->bip173_name; + currency = ch->lightning_hrp; } minor_unit = 11; } else { diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index d371ac28e90e..b827310d9e89 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -20,7 +20,7 @@ void notify_channel_mvt(struct lightningd *ld, const struct channel_coin_mvt *mv timestamp = time_now().ts.tv_sec; count = update_count(ld); - cm = finalize_channel_mvt(mvt, mvt, chainparams->bip173_name, + cm = finalize_channel_mvt(mvt, mvt, chainparams->lightning_hrp, timestamp, &ld->id, count); notify_coin_mvt(ld, cm); } @@ -33,7 +33,7 @@ void notify_chain_mvt(struct lightningd *ld, const struct chain_coin_mvt *mvt) timestamp = time_now().ts.tv_sec; count = update_count(ld); - cm = finalize_chain_mvt(mvt, mvt, chainparams->bip173_name, + cm = finalize_chain_mvt(mvt, mvt, chainparams->onchain_hrp, timestamp, &ld->id, count); notify_coin_mvt(ld, cm); } diff --git a/plugins/offers.c b/plugins/offers.c index 4e64cbfb7aac..36bd20a85632 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -468,10 +468,10 @@ static bool json_add_fallback_address(struct json_stream *js, const struct chainparams *chain, u8 version, const u8 *address) { - char out[73 + strlen(chain->bip173_name)]; + char out[73 + strlen(chain->onchain_hrp)]; /* Does extra checks, in particular checks v0 sizes */ - if (segwit_addr_encode(out, chain->bip173_name, version, + if (segwit_addr_encode(out, chain->onchain_hrp, version, address, tal_bytelen(address))) { json_add_string(js, "address", out); return true; diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index e0c5405afe6b..5aac2c19daa0 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -48,7 +48,7 @@ encode_pubkey_to_addr(const tal_t *ctx, chainparams, &h160); } else { - hrp = chainparams->bip173_name; + hrp = chainparams->onchain_hrp; /* out buffer is 73 + strlen(human readable part), * see common/bech32.h*/ From e8f43ef6ca19d86e2a8929e0b657a3570f3f7ed8 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 9 Dec 2021 10:05:58 -0800 Subject: [PATCH 0135/1530] wallet: make wallet_can_spend non-static because needed --- wallet/wallet.c | 4 ++-- wallet/wallet.h | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index 98d650ba6a87..4d13d01bb774 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -615,8 +615,8 @@ bool wallet_add_onchaind_utxo(struct wallet *w, return true; } -static bool wallet_can_spend(struct wallet *w, const u8 *script, - u32 *index, bool *output_is_p2sh) +bool wallet_can_spend(struct wallet *w, const u8 *script, + u32 *index, bool *output_is_p2sh) { struct ext_key ext; u64 bip32_max_index = db_get_intvar(w->db, "bip32_max_index", 0); diff --git a/wallet/wallet.h b/wallet/wallet.h index 619cb464bb1d..ad73893ca46c 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -490,6 +490,19 @@ void wallet_unreserve_utxo(struct wallet *w, struct utxo *utxo, struct utxo *wallet_utxo_get(const tal_t *ctx, struct wallet *w, const struct bitcoin_outpoint *outpoint); +/** + * wallet_can_spend - Do we have the private key matching this scriptpubkey? + * + * FIXME: This is very slow with lots of inputs! + * + * @w: (in) wallet holding the pubkeys to check against (privkeys are on HSM) + * @script: (in) the script to check + * @index: (out) the bip32 derivation index that matched the script + * @output_is_p2sh: (out) whether the script is a p2sh, or p2wpkh + */ +bool wallet_can_spend(struct wallet *w, const u8 *script, + u32 *index, bool *output_is_p2sh); + /** * wallet_get_newindex - get a new index from the wallet. * @ld: (in) lightning daemon From bb574be839968a471bf52062d59c367c195f5a1f Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 4 Nov 2021 10:02:12 -0700 Subject: [PATCH 0136/1530] hsmd: Add hsmd_new_channel --- hsmd/hsmd.c | 2 ++ hsmd/hsmd_wire.csv | 8 ++++++++ hsmd/libhsmd.c | 20 ++++++++++++++++++++ lightningd/channel.c | 9 +++++++++ lightningd/opening_common.c | 13 +++++++++++++ wallet/test/run-wallet.c | 4 ++++ 6 files changed, 56 insertions(+) diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 015f7c126bca..1775de8a73ff 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -640,6 +640,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_DEV_MEMLEAK: #endif /* DEVELOPER */ + case WIRE_HSMD_NEW_CHANNEL: case WIRE_HSMD_SIGN_COMMITMENT_TX: case WIRE_HSMD_VALIDATE_REVOCATION: case WIRE_HSMD_SIGN_PENALTY_TO_US: @@ -671,6 +672,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_CANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_CUPDATE_SIG_REPLY: case WIRE_HSMD_CLIENT_HSMFD_REPLY: + case WIRE_HSMD_NEW_CHANNEL_REPLY: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY: case WIRE_HSMD_SIGN_INVOICE_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index c67d79fce9c8..6b1de36a3d05 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -23,6 +23,14 @@ msgdata,hsmd_init_reply,bip32,ext_key, msgdata,hsmd_init_reply,bolt12,point32, msgdata,hsmd_init_reply,onion_reply_secret,secret, +# Declare a new channel. +msgtype,hsmd_new_channel,30 +msgdata,hsmd_new_channel,id,node_id, +msgdata,hsmd_new_channel,dbid,u64, + +# No value returned. +msgtype,hsmd_new_channel_reply,130 + # Get a new HSM FD, with the specified capabilities msgtype,hsmd_client_hsmfd,9 # Which identity to use for requests diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index a74a0a82e03f..7ca5b333a326 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -105,6 +105,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, return (client->capabilities & HSM_CAP_SIGN_WILL_FUND_OFFER) != 0; case WIRE_HSMD_INIT: + case WIRE_HSMD_NEW_CHANNEL: case WIRE_HSMD_CLIENT_HSMFD: case WIRE_HSMD_SIGN_WITHDRAWAL: case WIRE_HSMD_SIGN_INVOICE: @@ -123,6 +124,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_CANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_CUPDATE_SIG_REPLY: case WIRE_HSMD_CLIENT_HSMFD_REPLY: + case WIRE_HSMD_NEW_CHANNEL_REPLY: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY: case WIRE_HSMD_SIGN_INVOICE_REPLY: @@ -279,6 +281,21 @@ static void get_channel_seed(const struct node_id *peer_id, u64 dbid, info, strlen(info)); } +/* ~This stub implementation is overriden by fully validating signers + * that need to manage per-channel state. */ +static u8 *handle_new_channel(struct hsmd_client *c, const u8 *msg_in) +{ + struct node_id peer_id; + u64 dbid; + + if (!fromwire_hsmd_new_channel(msg_in, &peer_id, &dbid)) + return hsmd_status_malformed_request(c, msg_in); + + /* Stub implementation */ + + return towire_hsmd_new_channel_reply(NULL); +} + /*~ For almost every wallet tx we use the BIP32 seed, but not for onchain * unilateral closes from a peer: they (may) have an output to us using a * public key based on the channel basepoints. It's a bit spammy to spend @@ -1412,6 +1429,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, "libhsmd", hsmd_wire_name(t)); + case WIRE_HSMD_NEW_CHANNEL: + return handle_new_channel(client, msg); case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: return handle_get_output_scriptpubkey(client, msg); case WIRE_HSMD_CHECK_FUTURE_SECRET: @@ -1462,6 +1481,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_CANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_CUPDATE_SIG_REPLY: case WIRE_HSMD_CLIENT_HSMFD_REPLY: + case WIRE_HSMD_NEW_CHANNEL_REPLY: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY: case WIRE_HSMD_SIGN_INVOICE_REPLY: diff --git a/lightningd/channel.c b/lightningd/channel.c index 4f7b3a0bebbd..617b89ad9589 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -218,6 +218,7 @@ struct channel *new_unsaved_channel(struct peer *peer, { struct lightningd *ld = peer->ld; struct channel *channel = tal(ld, struct channel); + u8 *msg; channel->peer = peer; /* Not saved to the database yet! */ @@ -284,6 +285,14 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->their_shachain.id = 0; shachain_init(&channel->their_shachain.chain); + msg = towire_hsmd_new_channel(NULL, &peer->id, channel->unsaved_dbid); + if (!wire_sync_write(ld->hsm_fd, take(msg))) + fatal("Could not write to HSM: %s", strerror(errno)); + msg = wire_sync_read(tmpctx, ld->hsm_fd); + if (!fromwire_hsmd_new_channel_reply(msg)) + fatal("HSM gave bad hsm_new_channel_reply %s", + tal_hex(msg, msg)); + get_channel_basepoints(ld, &peer->id, channel->unsaved_dbid, &channel->local_basepoints, &channel->local_funding_pubkey); diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 9af928d28b8a..635aaeef5975 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -11,6 +13,7 @@ #include #include #include +#include static void destroy_uncommitted_channel(struct uncommitted_channel *uc) { @@ -34,6 +37,7 @@ new_uncommitted_channel(struct peer *peer) { struct lightningd *ld = peer->ld; struct uncommitted_channel *uc = tal(ld, struct uncommitted_channel); + u8 *new_channel_msg; uc->peer = peer; assert(!peer->uncommitted_channel); @@ -49,6 +53,15 @@ new_uncommitted_channel(struct peer *peer) memset(&uc->cid, 0xFF, sizeof(uc->cid)); + /* Declare the new channel to the HSM. */ + new_channel_msg = towire_hsmd_new_channel(NULL, &uc->peer->id, uc->dbid); + if (!wire_sync_write(ld->hsm_fd, take(new_channel_msg))) + fatal("Could not write to HSM: %s", strerror(errno)); + new_channel_msg = wire_sync_read(tmpctx, ld->hsm_fd); + if (!fromwire_hsmd_new_channel_reply(new_channel_msg)) + fatal("HSM gave bad hsm_new_channel_reply %s", + tal_hex(new_channel_msg, new_channel_msg)); + get_channel_basepoints(ld, &uc->peer->id, uc->dbid, &uc->local_basepoints, &uc->local_funding_pubkey); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 213b54c44df7..2d65f6e7f0ed 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -138,6 +138,10 @@ bool fromwire_custommsg_in(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 /* Generated stub for fromwire_gossipd_get_stripped_cupdate_reply */ bool fromwire_gossipd_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **stripped_update UNNEEDED) { fprintf(stderr, "fromwire_gossipd_get_stripped_cupdate_reply called!\n"); abort(); } +u8 *towire_hsmd_new_channel(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 dbid UNNEEDED) +{ fprintf(stderr, "towire_hsmd_new_channel called!\n"); abort(); } +bool fromwire_hsmd_new_channel_reply(const void *p UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_new_channel_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED) { fprintf(stderr, "fromwire_hsmd_get_output_scriptpubkey_reply called!\n"); abort(); } From 335ef3fb69f923f6f30213f68881d10ee6d977ed Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 4 Nov 2021 10:48:43 -0700 Subject: [PATCH 0137/1530] hsmd: Add hsmd_ready_channel --- hsmd/hsmd.c | 2 ++ hsmd/hsmd_wire.csv | 22 +++++++++++++ hsmd/libhsmd.c | 58 ++++++++++++++++++++++++++++++++ lightningd/dual_open_control.c | 60 ++++++++++++++++++++++++++++++++++ lightningd/opening_control.c | 37 +++++++++++++++++++-- openingd/dualopend.c | 60 ++++++++++++++++++++++++++++++++++ openingd/dualopend_wire.csv | 3 ++ openingd/openingd.c | 49 ++++++++++++++++++++++++++- openingd/openingd_wire.csv | 2 ++ 9 files changed, 289 insertions(+), 4 deletions(-) diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 1775de8a73ff..96c76ef4e8a3 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -641,6 +641,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) #endif /* DEVELOPER */ case WIRE_HSMD_NEW_CHANNEL: + case WIRE_HSMD_READY_CHANNEL: case WIRE_HSMD_SIGN_COMMITMENT_TX: case WIRE_HSMD_VALIDATE_REVOCATION: case WIRE_HSMD_SIGN_PENALTY_TO_US: @@ -673,6 +674,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_CUPDATE_SIG_REPLY: case WIRE_HSMD_CLIENT_HSMFD_REPLY: case WIRE_HSMD_NEW_CHANNEL_REPLY: + case WIRE_HSMD_READY_CHANNEL_REPLY: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY: case WIRE_HSMD_SIGN_INVOICE_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 6b1de36a3d05..2359341a41da 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -52,6 +52,28 @@ msgtype,hsmd_get_channel_basepoints_reply,110 msgdata,hsmd_get_channel_basepoints_reply,basepoints,basepoints, msgdata,hsmd_get_channel_basepoints_reply,funding_pubkey,pubkey, +#include +# Provide channel parameters. +msgtype,hsmd_ready_channel,31 +msgdata,hsmd_ready_channel,is_outbound,bool, +msgdata,hsmd_ready_channel,channel_value,amount_sat, +msgdata,hsmd_ready_channel,push_value,amount_msat, +msgdata,hsmd_ready_channel,funding_txid,bitcoin_txid, +msgdata,hsmd_ready_channel,funding_txout,u16, +msgdata,hsmd_ready_channel,local_to_self_delay,u16, +msgdata,hsmd_ready_channel,local_shutdown_script_len,u16, +msgdata,hsmd_ready_channel,local_shutdown_script,u8,local_shutdown_script_len +msgdata,hsmd_ready_channel,local_shutdown_wallet_index,?u32, +msgdata,hsmd_ready_channel,remote_basepoints,basepoints, +msgdata,hsmd_ready_channel,remote_funding_pubkey,pubkey, +msgdata,hsmd_ready_channel,remote_to_self_delay,u16, +msgdata,hsmd_ready_channel,remote_shutdown_script_len,u16, +msgdata,hsmd_ready_channel,remote_shutdown_script,u8,remote_shutdown_script_len +msgdata,hsmd_ready_channel,channel_type,channel_type, + +# No value returned. +msgtype,hsmd_ready_channel_reply,131 + # Return signature for a funding tx. #include diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 7ca5b333a326..13b0fe03aa59 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -91,6 +91,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_GET_PER_COMMITMENT_POINT: case WIRE_HSMD_CHECK_FUTURE_SECRET: + case WIRE_HSMD_READY_CHANNEL: return (client->capabilities & HSM_CAP_COMMITMENT_POINT) != 0; case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX: @@ -125,6 +126,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_CUPDATE_SIG_REPLY: case WIRE_HSMD_CLIENT_HSMFD_REPLY: case WIRE_HSMD_NEW_CHANNEL_REPLY: + case WIRE_HSMD_READY_CHANNEL_REPLY: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY: case WIRE_HSMD_SIGN_INVOICE_REPLY: @@ -296,6 +298,59 @@ static u8 *handle_new_channel(struct hsmd_client *c, const u8 *msg_in) return towire_hsmd_new_channel_reply(NULL); } +static bool mem_is_zero(const void *mem, size_t len) +{ + size_t i; + for (i = 0; i < len; ++i) + if (((const unsigned char *)mem)[i]) + return false; + return true; +} + +/* ~This stub implementation is overriden by fully validating signers + * that need the unchanging channel parameters. */ +static u8 *handle_ready_channel(struct hsmd_client *c, const u8 *msg_in) +{ + bool is_outbound; + struct amount_sat channel_value; + struct amount_msat push_value; + struct bitcoin_txid funding_txid; + u16 funding_txout; + u16 local_to_self_delay; + u8 *local_shutdown_script; + u32 *local_shutdown_wallet_index; + struct basepoints remote_basepoints; + struct pubkey remote_funding_pubkey; + u16 remote_to_self_delay; + u8 *remote_shutdown_script; + struct amount_msat value_msat; + struct channel_type *channel_type; + + if (!fromwire_hsmd_ready_channel(tmpctx, msg_in, &is_outbound, + &channel_value, &push_value, &funding_txid, + &funding_txout, &local_to_self_delay, + &local_shutdown_script, + &local_shutdown_wallet_index, + &remote_basepoints, + &remote_funding_pubkey, + &remote_to_self_delay, + &remote_shutdown_script, + &channel_type)) + return hsmd_status_malformed_request(c, msg_in); + + /* Stub implementation */ + + /* Fail fast if any values are uninitialized or obviously wrong. */ + assert(amount_sat_greater(channel_value, AMOUNT_SAT(0))); + assert(amount_sat_to_msat(&value_msat, channel_value)); + assert(amount_msat_less_eq(push_value, value_msat)); + assert(!mem_is_zero(&funding_txid, sizeof(funding_txid))); + assert(local_to_self_delay > 0); + assert(remote_to_self_delay > 0); + + return towire_hsmd_ready_channel_reply(NULL); +} + /*~ For almost every wallet tx we use the BIP32 seed, but not for onchain * unilateral closes from a peer: they (may) have an output to us using a * public key based on the channel basepoints. It's a bit spammy to spend @@ -1431,6 +1486,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_NEW_CHANNEL: return handle_new_channel(client, msg); + case WIRE_HSMD_READY_CHANNEL: + return handle_ready_channel(client, msg); case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: return handle_get_output_scriptpubkey(client, msg); case WIRE_HSMD_CHECK_FUTURE_SECRET: @@ -1482,6 +1539,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_CUPDATE_SIG_REPLY: case WIRE_HSMD_CLIENT_HSMFD_REPLY: case WIRE_HSMD_NEW_CHANNEL_REPLY: + case WIRE_HSMD_READY_CHANNEL_REPLY: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY: case WIRE_HSMD_SIGN_INVOICE_REPLY: diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index c546eb75eeb7..a53c38be9214 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -616,6 +616,7 @@ openchannel2_hook_cb(struct openchannel2_payload *payload STEALS) { struct subd *dualopend = payload->dualopend; struct channel *channel = payload->channel; + u32 *our_shutdown_script_wallet_index; u8 *msg; /* Our daemon died! */ @@ -646,6 +647,19 @@ openchannel2_hook_cb(struct openchannel2_payload *payload STEALS) return subd_send_msg(dualopend, take(msg)); } + /* Determine the wallet index for our_shutdown_scriptpubkey, + * NULL if not found. */ + u32 found_wallet_index; + bool is_p2sh; + if (wallet_can_spend(dualopend->ld->wallet, + payload->our_shutdown_scriptpubkey, + &found_wallet_index, + &is_p2sh)) { + our_shutdown_script_wallet_index = tal(tmpctx, u32); + *our_shutdown_script_wallet_index = found_wallet_index; + } else + our_shutdown_script_wallet_index = NULL; + channel->cid = payload->channel_id; channel->opener = REMOTE; channel->open_attempt = new_channel_open_attempt(channel); @@ -653,6 +667,7 @@ openchannel2_hook_cb(struct openchannel2_payload *payload STEALS) payload->accepter_funding, payload->psbt, payload->our_shutdown_scriptpubkey, + our_shutdown_script_wallet_index, payload->rates); subd_send_msg(dualopend, take(msg)); @@ -2468,6 +2483,7 @@ static struct command_result *json_openchannel_init(struct command *cmd, struct amount_sat *amount, psbt_val, *request_amt; struct wally_psbt *psbt; const u8 *our_upfront_shutdown_script; + u32 *our_upfront_shutdown_script_wallet_index; struct open_attempt *oa; struct lease_rates *rates; struct command_result *res; @@ -2603,9 +2619,23 @@ static struct command_result *json_openchannel_init(struct command *cmd, oa->our_upfront_shutdown_script = tal_steal(oa, our_upfront_shutdown_script); + /* Determine the wallet index for our_upfront_shutdown_script, + * NULL if not found. */ + u32 found_wallet_index; + bool is_p2sh; + if (wallet_can_spend(cmd->ld->wallet, + oa->our_upfront_shutdown_script, + &found_wallet_index, + &is_p2sh)) { + our_upfront_shutdown_script_wallet_index = tal(tmpctx, u32); + *our_upfront_shutdown_script_wallet_index = found_wallet_index; + } else + our_upfront_shutdown_script_wallet_index = NULL; + msg = towire_dualopend_opener_init(NULL, psbt, *amount, oa->our_upfront_shutdown_script, + our_upfront_shutdown_script_wallet_index, *feerate_per_kw, *feerate_per_kw_funding, channel->channel_flags, @@ -2989,6 +3019,7 @@ static struct command_result *json_queryrates(struct command *cmd, struct amount_sat *amount, *request_amt; struct wally_psbt *psbt; struct open_attempt *oa; + u32 *our_upfront_shutdown_script_wallet_index; u8 *msg; struct command_result *res; @@ -3060,9 +3091,23 @@ static struct command_result *json_queryrates(struct command *cmd, /* empty psbt to start */ psbt = create_psbt(tmpctx, 0, 0, 0); + /* Determine the wallet index for our_upfront_shutdown_script, + * NULL if not found. */ + u32 found_wallet_index; + bool is_p2sh; + if (wallet_can_spend(cmd->ld->wallet, + oa->our_upfront_shutdown_script, + &found_wallet_index, + &is_p2sh)) { + our_upfront_shutdown_script_wallet_index = tal(tmpctx, u32); + *our_upfront_shutdown_script_wallet_index = found_wallet_index; + } else + our_upfront_shutdown_script_wallet_index = NULL; + msg = towire_dualopend_opener_init(NULL, psbt, *amount, oa->our_upfront_shutdown_script, + our_upfront_shutdown_script_wallet_index, *feerate_per_kw, *feerate_per_kw_funding, channel->channel_flags, @@ -3201,6 +3246,7 @@ void peer_restart_dualopend(struct peer *peer, struct channel_config unused_config; struct channel_inflight *inflight; int hsmfd; + u32 *local_shutdown_script_wallet_index; u8 *msg; if (channel_unsaved(channel)) { @@ -3243,6 +3289,19 @@ void peer_restart_dualopend(struct peer *peer, blockheight = get_blockheight(channel->blockheight_states, channel->opener, LOCAL); + /* Determine the wallet index for the LOCAL shutdown_scriptpubkey, + * NULL if not found. */ + u32 found_wallet_index; + bool is_p2sh; + if (wallet_can_spend(peer->ld->wallet, + channel->shutdown_scriptpubkey[LOCAL], + &found_wallet_index, + &is_p2sh)) { + local_shutdown_script_wallet_index = tal(tmpctx, u32); + *local_shutdown_script_wallet_index = found_wallet_index; + } else + local_shutdown_script_wallet_index = NULL; + msg = towire_dualopend_reinit(NULL, chainparams, peer->ld->our_features, @@ -3271,6 +3330,7 @@ void peer_restart_dualopend(struct peer *peer, channel->shutdown_scriptpubkey[REMOTE] != NULL, channel->shutdown_scriptpubkey[LOCAL], channel->remote_upfront_shutdown_script, + local_shutdown_script_wallet_index, inflight->remote_tx_sigs, channel->fee_states, channel->channel_flags, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 83f73b9a0886..dd378219f90c 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -649,6 +649,7 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS) const u8 *our_upfront_shutdown_script = payload->our_upfront_shutdown_script; const char *errmsg = payload->errmsg; struct uncommitted_channel* uc = payload->uc; + u32 *upfront_shutdown_script_wallet_index; /* We want to free this, whatever happens. */ tal_steal(tmpctx, payload); @@ -669,9 +670,24 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS) uc->got_offer = true; } + /* Determine the wallet index for our_upfront_shutdown_script, + * NULL if not found. */ + u32 found_wallet_index; + bool is_p2sh; + if (wallet_can_spend(payload->openingd->ld->wallet, + our_upfront_shutdown_script, + &found_wallet_index, + &is_p2sh)) { + upfront_shutdown_script_wallet_index = tal(tmpctx, u32); + *upfront_shutdown_script_wallet_index = found_wallet_index; + } else + upfront_shutdown_script_wallet_index = NULL; + + subd_send_msg(openingd, take(towire_openingd_got_offer_reply(NULL, errmsg, - our_upfront_shutdown_script))); + our_upfront_shutdown_script, + upfront_shutdown_script_wallet_index))); } static bool @@ -760,8 +776,8 @@ static void opening_got_offer(struct subd *openingd, /* Tell them they can't open, if we already have open channel. */ if (peer_active_channel(uc->peer)) { subd_send_msg(openingd, - take(towire_openingd_got_offer_reply(NULL, - "Already have active channel", NULL))); + take(towire_openingd_got_offer_reply( + NULL, "Already have active channel", NULL, NULL))); return; } @@ -1110,6 +1126,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, u8 *msg = NULL; struct amount_sat *amount; struct amount_msat *push_msat; + u32 *upfront_shutdown_script_wallet_index; fc->cmd = cmd; fc->cancels = tal_arr(fc, struct command *, 0); @@ -1220,10 +1237,24 @@ static struct command_result *json_fundchannel_start(struct command *cmd, fc->our_upfront_shutdown_script = tal_steal(fc, fc->our_upfront_shutdown_script); + /* Determine the wallet index for our_upfront_shutdown_script, + * NULL if not found. */ + u32 found_wallet_index; + bool is_p2sh; + if (wallet_can_spend(fc->cmd->ld->wallet, + fc->our_upfront_shutdown_script, + &found_wallet_index, + &is_p2sh)) { + upfront_shutdown_script_wallet_index = tal(tmpctx, u32); + *upfront_shutdown_script_wallet_index = found_wallet_index; + } else + upfront_shutdown_script_wallet_index = NULL; + msg = towire_openingd_funder_start(NULL, *amount, fc->push, fc->our_upfront_shutdown_script, + upfront_shutdown_script_wallet_index, *feerate_per_kw, fc->channel_flags); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 163c2bed1a0b..7711f6224da2 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -181,6 +181,9 @@ struct state { /* If non-NULL, this is the scriptpubkey we/they *must* close with */ u8 *upfront_shutdown_script[NUM_SIDES]; + /* If non-NULL, the wallet index for the LOCAL script */ + u32 *local_upfront_shutdown_wallet_index; + /* The channel structure, as defined in common/initial_channel.h. While * the structure has room for HTLCs, those routines are * channeld-specific as initial channels never have HTLCs. */ @@ -1799,6 +1802,28 @@ static u8 *accepter_commits(struct state *state, type = default_channel_type(NULL, state->our_features, state->their_features); + + /*~ Report the channel parameters to the signer. */ + msg = towire_hsmd_ready_channel(NULL, + false, /* is_outbound */ + total, + our_msats, + &tx_state->funding.txid, + tx_state->funding.n, + tx_state->localconf.to_self_delay, + state->upfront_shutdown_script[LOCAL], + state->local_upfront_shutdown_wallet_index, + &state->their_points, + &state->their_funding_pubkey, + tx_state->remoteconf.to_self_delay, + state->upfront_shutdown_script[REMOTE], + type); + wire_sync_write(HSM_FD, take(msg)); + msg = wire_sync_read(tmpctx, HSM_FD); + if (!fromwire_hsmd_ready_channel_reply(msg)) + status_failed(STATUS_FAIL_HSM_IO, "Bad ready_channel_reply %s", + tal_hex(tmpctx, msg)); + state->channel = new_initial_channel(state, &state->channel_id, &tx_state->funding, @@ -2139,6 +2164,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) &tx_state->accepter_funding, &tx_state->psbt, &state->upfront_shutdown_script[LOCAL], + &state->local_upfront_shutdown_wallet_index, &tx_state->rates)) master_badmsg(WIRE_DUALOPEND_GOT_OFFER_REPLY, msg); @@ -2367,6 +2393,7 @@ static u8 *opener_commits(struct state *state, const u8 *wscript; u8 *msg; char *error; + struct amount_msat their_msats; const struct channel_type *type; wscript = bitcoin_redeem_2of2(tmpctx, &state->our_funding_pubkey, @@ -2409,9 +2436,40 @@ static u8 *opener_commits(struct state *state, return NULL; } + if (!amount_sat_to_msat(&their_msats, tx_state->accepter_funding)) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Overflow error, can't convert accepter_funding %s" + " to msats", + type_to_string(tmpctx, struct amount_sat, + &tx_state->accepter_funding)); + return NULL; + } + /* Ok, we're mostly good now? Let's do this */ type = default_channel_type(NULL, state->our_features, state->their_features); + + /*~ Report the channel parameters to the signer. */ + msg = towire_hsmd_ready_channel(NULL, + true, /* is_outbound */ + total, + their_msats, + &tx_state->funding.txid, + tx_state->funding.n, + tx_state->localconf.to_self_delay, + state->upfront_shutdown_script[LOCAL], + state->local_upfront_shutdown_wallet_index, + &state->their_points, + &state->their_funding_pubkey, + tx_state->remoteconf.to_self_delay, + state->upfront_shutdown_script[REMOTE], + type); + wire_sync_write(HSM_FD, take(msg)); + msg = wire_sync_read(tmpctx, HSM_FD); + if (!fromwire_hsmd_ready_channel_reply(msg)) + status_failed(STATUS_FAIL_HSM_IO, "Bad ready_channel_reply %s", + tal_hex(tmpctx, msg)); + state->channel = new_initial_channel(state, &cid, &tx_state->funding, @@ -2612,6 +2670,7 @@ static void opener_start(struct state *state, u8 *msg) &tx_state->psbt, &tx_state->opener_funding, &state->upfront_shutdown_script[LOCAL], + &state->local_upfront_shutdown_wallet_index, &state->feerate_per_kw_commitment, &tx_state->feerate_per_kw_funding, &state->channel_flags, @@ -3826,6 +3885,7 @@ int main(int argc, char *argv[]) &state->shutdown_sent[REMOTE], &state->upfront_shutdown_script[LOCAL], &state->upfront_shutdown_script[REMOTE], + &state->local_upfront_shutdown_wallet_index, &state->tx_state->remote_funding_sigs_rcvd, &fee_states, &state->channel_flags, diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 75fbb4d5bdf4..3b3620c7472f 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -61,6 +61,7 @@ msgdata,dualopend_reinit,local_shutdown_len,u16, msgdata,dualopend_reinit,local_shutdown_scriptpubkey,u8,local_shutdown_len msgdata,dualopend_reinit,remote_shutdown_len,u16, msgdata,dualopend_reinit,remote_shutdown_scriptpubkey,u8,remote_shutdown_len +msgdata,dualopend_reinit,local_shutdown_wallet_index,?u32, msgdata,dualopend_reinit,remote_funding_sigs_received,bool, msgdata,dualopend_reinit,fee_states,fee_states, msgdata,dualopend_reinit,channel_flags,u8, @@ -94,6 +95,7 @@ msgdata,dualopend_got_offer_reply,accepter_funding,amount_sat, msgdata,dualopend_got_offer_reply,psbt,wally_psbt, msgdata,dualopend_got_offer_reply,shutdown_len,u16, msgdata,dualopend_got_offer_reply,our_shutdown_scriptpubkey,?u8,shutdown_len +msgdata,dualopend_got_offer_reply,our_shutdown_wallet_index,?u32, # must go last because of embedded tu32 msgdata,dualopend_got_offer_reply,lease_rates,?lease_rates, @@ -172,6 +174,7 @@ msgdata,dualopend_opener_init,psbt,wally_psbt, msgdata,dualopend_opener_init,funding_amount,amount_sat, msgdata,dualopend_opener_init,local_shutdown_len,u16, msgdata,dualopend_opener_init,local_shutdown_scriptpubkey,u8,local_shutdown_len +msgdata,dualopend_opener_init,local_shutdown_wallet_index,?u32, msgdata,dualopend_opener_init,feerate_per_kw,u32, msgdata,dualopend_opener_init,feerate_per_kw_funding,u32, msgdata,dualopend_opener_init,channel_flags,u8, diff --git a/openingd/openingd.c b/openingd/openingd.c index 1464b72ac76f..54f4be8462d9 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -84,6 +84,9 @@ struct state { /* If non-NULL, this is the scriptpubkey we/they *must* close with */ u8 *upfront_shutdown_script[NUM_SIDES]; + /* If non-NULL, the wallet index for the LOCAL script */ + u32 *local_upfront_shutdown_wallet_index; + /* This is a cluster of fields in open_channel and accept_channel which * indicate the restrictions each side places on the channel. */ struct channel_config localconf, remoteconf; @@ -519,6 +522,27 @@ static bool funder_finalize_channel_setup(struct state *state, char *err_reason; struct wally_tx_output *direct_outputs[NUM_SIDES]; + /*~ Channel is ready; Report the channel parameters to the signer. */ + msg = towire_hsmd_ready_channel(NULL, + true, /* is_outbound */ + state->funding_sats, + state->push_msat, + &state->funding.txid, + state->funding.n, + state->localconf.to_self_delay, + state->upfront_shutdown_script[LOCAL], + state->local_upfront_shutdown_wallet_index, + &state->their_points, + &state->their_funding_pubkey, + state->remoteconf.to_self_delay, + state->upfront_shutdown_script[REMOTE], + state->channel_type); + wire_sync_write(HSM_FD, take(msg)); + msg = wire_sync_read(tmpctx, HSM_FD); + if (!fromwire_hsmd_ready_channel_reply(msg)) + status_failed(STATUS_FAIL_HSM_IO, "Bad ready_channel_reply %s", + tal_hex(tmpctx, msg)); + /*~ Now we can initialize the `struct channel`. This represents * the current channel state and is how we can generate the current * commitment transaction. @@ -979,7 +1003,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) /* We don't allocate off tmpctx, because that's freed inside * opening_negotiate_msg */ if (!fromwire_openingd_got_offer_reply(state, msg, &err_reason, - &state->upfront_shutdown_script[LOCAL])) + &state->upfront_shutdown_script[LOCAL], + &state->local_upfront_shutdown_wallet_index)) master_badmsg(WIRE_OPENINGD_GOT_OFFER_REPLY, msg); /* If they give us a reason to reject, do so. */ @@ -1055,6 +1080,27 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &state->channel_id), type_to_string(msg, struct channel_id, &id_in)); + /*~ Channel is ready; Report the channel parameters to the signer. */ + msg = towire_hsmd_ready_channel(NULL, + false, /* is_outbound */ + state->funding_sats, + state->push_msat, + &state->funding.txid, + state->funding.n, + state->localconf.to_self_delay, + state->upfront_shutdown_script[LOCAL], + state->local_upfront_shutdown_wallet_index, + &theirs, + &their_funding_pubkey, + state->remoteconf.to_self_delay, + state->upfront_shutdown_script[REMOTE], + state->channel_type); + wire_sync_write(HSM_FD, take(msg)); + msg = wire_sync_read(tmpctx, HSM_FD); + if (!fromwire_hsmd_ready_channel_reply(msg)) + status_failed(STATUS_FAIL_HSM_IO, "Bad ready_channel_reply %s", + tal_hex(tmpctx, msg)); + /* Now we can create the channel structure. */ state->channel = new_initial_channel(state, &state->channel_id, @@ -1318,6 +1364,7 @@ static u8 *handle_master_in(struct state *state) &state->funding_sats, &state->push_msat, &state->upfront_shutdown_script[LOCAL], + &state->local_upfront_shutdown_wallet_index, &state->feerate_per_kw, &channel_flags)) master_badmsg(WIRE_OPENINGD_FUNDER_START, msg); diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index 39278ec58cb0..7a688c7f692f 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -55,6 +55,7 @@ msgtype,openingd_got_offer_reply,6105 msgdata,openingd_got_offer_reply,rejection,?wirestring, msgdata,openingd_got_offer_reply,shutdown_len,u16, msgdata,openingd_got_offer_reply,our_shutdown_scriptpubkey,?u8,shutdown_len +msgdata,openingd_got_offer_reply,our_shutdown_wallet_index,?u32, #include # Openingd->master: we've successfully offered channel. @@ -85,6 +86,7 @@ msgdata,openingd_funder_start,funding_satoshis,amount_sat, msgdata,openingd_funder_start,push_msat,amount_msat, msgdata,openingd_funder_start,len_upfront,u16, msgdata,openingd_funder_start,upfront_shutdown_script,u8,len_upfront +msgdata,openingd_funder_start,upfront_shutdown_wallet_index,?u32, msgdata,openingd_funder_start,feerate_per_kw,u32, msgdata,openingd_funder_start,channel_flags,u8, From bf71d250a78dc9359e20324931b852133f47d691 Mon Sep 17 00:00:00 2001 From: lightning-developer <95319454+lightning-developer@users.noreply.github.com> Date: Wed, 8 Dec 2021 23:12:43 +0100 Subject: [PATCH 0138/1530] Removed FIXME to match bolts PR 918 If PR 918 is merged to the bolts this requirement will be dropped from the spec and we do not need to have a FIXME in the code base. --- common/ping.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/common/ping.c b/common/ping.c index e7a32eebd192..9ff545ec90b4 100644 --- a/common/ping.c +++ b/common/ping.c @@ -13,14 +13,6 @@ bool check_ping_make_pong(const tal_t *ctx, const u8 *ping, u8 **pong) return false; tal_free(ignored); - /* FIXME: */ - /* BOLT #1: - * - * A node receiving a `ping` message: - * - SHOULD fail the channels if it has received significantly in - * excess of one `ping` per 30 seconds. - */ - /* BOLT #1: * * A node receiving a `ping` message: From 82124b7733b3ef20683b7c36b63f7b92b9f69c04 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 8 Dec 2021 18:34:36 +0100 Subject: [PATCH 0139/1530] doc: force mistune==0.8.4 during pip package resolution. Signed-off-by: Vincenzo Palazzo Changelog-None: doc: force mistune==0.8.4 during pip package resolution. --- doc/INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 42e2e508148d..bec63e75df54 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -40,7 +40,7 @@ Get dependencies: autoconf automake build-essential git libtool libgmp-dev libsqlite3-dev \ python3 python3-mako python3-pip net-tools zlib1g-dev libsodium-dev \ gettext - pip3 install --user mrkd + pip3 install --user mrkd mistune==0.8.4 If you don't have Bitcoin installed locally you'll need to install that as well. It's now available via [snapd](https://snapcraft.io/bitcoin-core). From 1ac5a1b7e2433ee246903d24562e1604978e2c8a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 6 Dec 2021 17:16:24 +1030 Subject: [PATCH 0140/1530] pytest: restore gossip advertizement tests for default port. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 466238d73e60..7696b2642490 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1019,7 +1019,10 @@ def test_gossip_addresses(node_factory, bitcoind): l1 = node_factory.get_node(options={ 'announce-addr': [ '[::]:3', + '[::]', '127.0.0.1:2', + '127.0.0.1', + 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', '4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion:1234' ], }) @@ -1032,9 +1035,18 @@ def test_gossip_addresses(node_factory, bitcoind): .format(l1.info['id'])) nodes = l2.rpc.listnodes(l1.info['id'])['nodes'] + if TEST_NETWORK == 'regtest': + default_port = 19846 + else: + assert TEST_NETWORK == 'liquid-regtest' + default_port = 20735 + assert len(nodes) == 1 and nodes[0]['addresses'] == [ {'type': 'ipv4', 'address': '127.0.0.1', 'port': 2}, + {'type': 'ipv4', 'address': '127.0.0.1', 'port': default_port}, {'type': 'ipv6', 'address': '::', 'port': 3}, + {'type': 'ipv6', 'address': '::', 'port': default_port}, + {'type': 'torv3', 'address': 'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion', 'port': default_port}, {'type': 'torv3', 'address': '4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion', 'port': 1234}, ] From a98ccac7774297f255f0a4f585384609af87c3d6 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 12 Dec 2021 14:27:00 +0100 Subject: [PATCH 0141/1530] pay: Disable channels that are not in normal state Since we have the exact state of the channels from the `listpeers` response we can filter out the ones that are not yet normal or not anymore. This is more of a safety net, given that the `gossip_store` should contain local disables, but better not be racy :-) Changelog-None --- plugins/libplugin-pay.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 0a3c32546793..ae82c1497ce1 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -2310,7 +2310,7 @@ local_channel_hints_listpeers(struct command *cmd, const char *buffer, const jsmntok_t *toks, struct payment *p) { const jsmntok_t *peers, *peer, *channels, *channel, *spendsats, *scid, - *dir, *connected, *max_htlc, *htlcs; + *dir, *connected, *max_htlc, *htlcs, *state; size_t i, j; peers = json_get_member(buffer, toks, "peers"); @@ -2331,13 +2331,19 @@ local_channel_hints_listpeers(struct command *cmd, const char *buffer, dir = json_get_member(buffer, channel, "direction"); max_htlc = json_get_member(buffer, channel, "max_accepted_htlcs"); htlcs = json_get_member(buffer, channel, "htlcs"); + state = json_get_member(buffer, channel, "state"); if (spendsats == NULL || scid == NULL || dir == NULL || - max_htlc == NULL || + max_htlc == NULL || state == NULL || max_htlc->type != JSMN_PRIMITIVE || htlcs == NULL || htlcs->type != JSMN_ARRAY) continue; + /* Filter out local channels if they are + * either a) disconnected, or b) not in normal + * state. */ json_to_bool(buffer, connected, &h.enabled); + h.enabled &= json_tok_streq(buffer, state, "CHANNELD_NORMAL"); + json_to_short_channel_id(buffer, scid, &h.scid.scid); json_to_int(buffer, dir, &h.scid.dir); From 064e239ae16bc0fc360218bfb134eaf74b6a7b9c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 14 Dec 2021 10:09:53 +1030 Subject: [PATCH 0142/1530] ccan: update, add graphql module. Signed-off-by: Rusty Russell --- Makefile | 1 + ccan/README | 2 +- ccan/ccan/graphql/LICENSE | 1 + ccan/ccan/graphql/_info | 66 + ccan/ccan/graphql/graphql.c | 968 +++++++++++ ccan/ccan/graphql/graphql.h | 291 ++++ ccan/ccan/graphql/test/run.c | 3177 ++++++++++++++++++++++++++++++++++ 7 files changed, 4505 insertions(+), 1 deletion(-) create mode 120000 ccan/ccan/graphql/LICENSE create mode 100644 ccan/ccan/graphql/_info create mode 100644 ccan/ccan/graphql/graphql.c create mode 100644 ccan/ccan/graphql/graphql.h create mode 100644 ccan/ccan/graphql/test/run.c diff --git a/Makefile b/Makefile index f2b83f2d9b03..5a768d1ed00c 100644 --- a/Makefile +++ b/Makefile @@ -170,6 +170,7 @@ CCAN_HEADERS := \ $(CCANDIR)/ccan/endian/endian.h \ $(CCANDIR)/ccan/err/err.h \ $(CCANDIR)/ccan/fdpass/fdpass.h \ + $(CCANDIR)/ccan/graphql/graphql.h \ $(CCANDIR)/ccan/htable/htable.h \ $(CCANDIR)/ccan/htable/htable_type.h \ $(CCANDIR)/ccan/ilog/ilog.h \ diff --git a/ccan/README b/ccan/README index 340f4c3bc84f..2b5d2c37df82 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2520-gca7c5a9e +CCAN version: init-2522-g21543f83 diff --git a/ccan/ccan/graphql/LICENSE b/ccan/ccan/graphql/LICENSE new file mode 120000 index 000000000000..2354d12945d3 --- /dev/null +++ b/ccan/ccan/graphql/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/ccan/graphql/_info b/ccan/ccan/graphql/_info new file mode 100644 index 000000000000..29202414ecac --- /dev/null +++ b/ccan/ccan/graphql/_info @@ -0,0 +1,66 @@ +#include "config.h" +#include +#include + +/** + * graphql - Routines to lex and parse GraphQL. + * + * This code contains routines to lex and parse GraphQL code. + * This code was written per the spec at: + * https://spec.graphql.org/draft/ + * ...dated Fri, May 21, 2021 at the time of writing. + * Copyright (c) 2021 WhiteCloudFarm.org + * + * + * Example: + * + * int main(int argc, char *argv[]) { + * + * const char *input_string = "{ fieldName }"; + * struct list_head *output_tokens; + * struct graphql_executable_document *output_document; + * + * const char *errmsg = graphql_lexparse( + * NULL, // tal context + * input_string, + * &output_tokens, // variable to receive tokens + * &output_document); // variable to receive AST + * + * if (errmsg) { + * struct graphql_token *last_token; + * last_token = list_tail(output_tokens, struct graphql_token, node); + * printf("Line %d, col %d: %s", + * last_token->source_line, + * last_token->source_column + last_token->source_len, + * errmsg); + * } else { + * // Normally you would check every indirection in the resulting AST for null + * // pointers, but for simplicity of example: + * printf("A field from the parsed string: %s\n", + * output_document->first_def->op_def->sel_set-> + * first->field->name->token_string); + * } + * + * output_tokens = tal_free(output_tokens); + * } + * + * License: BSD-MIT + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/list\n"); + printf("ccan/str\n"); + printf("ccan/tal\n"); + printf("ccan/tal/str\n"); + printf("ccan/utf8\n"); + return 0; + } + + return 1; +} + diff --git a/ccan/ccan/graphql/graphql.c b/ccan/ccan/graphql/graphql.c new file mode 100644 index 000000000000..640a76bf84bb --- /dev/null +++ b/ccan/ccan/graphql/graphql.c @@ -0,0 +1,968 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#include "graphql.h" + +#include "ccan/tal/str/str.h" +#include "ccan/utf8/utf8.h" + + +/* GraphQL character classes + * + * These definitions are meant to reflect the GraphQL specification as + * literally as possible. + */ +#define SOURCE_CHAR(c) ((c) == '\t' || (c) == '\n' || (c) == '\r' || ((c) >= 32 && (c) <= 65535)) +#define WHITE_SPACE(c) ((c) == '\t' || (c) == ' ') +#define LINE_TERMINATOR(c) ((c) == '\n' || (c) == '\r') +#define COMMENT(c) ((c) == '#') +#define COMMENT_CHAR(c) (SOURCE_CHAR(c) && !LINE_TERMINATOR(c)) +#define STRING_CHAR(c) (SOURCE_CHAR(c) && !LINE_TERMINATOR(c) && (c)!='"' && (c)!='\\') +#define BLOCK_STRING_CHAR(c) (SOURCE_CHAR(c)) +#define COMMA(c) ((c) == ',') +#define EOF_CHAR(c) ((c) == 0 || (c) == 4) +#define PUNCTUATOR(c) (strchr("!$&().:=@[]{|}", c)) +#define HEX_DIGIT(c) (DIGIT(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) +#define DIGIT(c) ((c) >= '0' && (c) <= '9') +#define NAME_START(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || (c) == '_') +#define NAME_CONTINUE(c) (NAME_START(c) || DIGIT(c)) + +// Helper for copying an overlapping string, since strcpy() is not safe for that +#define cpystr(d,s) { char *cpystr_p; char *cpystr_q; for(cpystr_p = (s), cpystr_q = (d); *cpystr_p;) *cpystr_q++ = *cpystr_p++; *cpystr_q++ = *cpystr_p++; } + +/* Parser shorthands + * + * These shorthands are motivated by the parser functions, so they can be + * written in a format that corresponds closely to the specification. + */ +#define RET static void * +#define PARAMS struct list_head *tokens, struct list_head *used, const char **err +#define ARGS tokens, used, err +#define INIT(type) \ + struct graphql_token *rollback_top = list_top(tokens, struct graphql_token, node); \ + struct graphql_##type *obj = talz(tokens, struct graphql_##type); \ + (void)rollback_top; /* avoids unused variable warning */ \ + +#define EXIT \ + goto exit_label; /* avoids unused label warning */ \ + exit_label: \ + if (*err) obj = tal_free(obj); \ + return obj; \ + +#define CONSUME_ONE list_add(used, &list_pop(tokens, struct graphql_token, node)->node); +#define RESTORE_ONE list_add(tokens, &list_pop(used, struct graphql_token, node)->node); +#define ROLLBACK(args) while (list_top(tokens, struct graphql_token, node) != rollback_top) { RESTORE_ONE; } +#define OR if (!*err) goto exit_label; *err = NULL; +#define REQ if (*err) { ROLLBACK(args); goto exit_label; } +#define OPT *err = NULL; +#define WHILE_OPT while(!*err); *err = NULL; +#define LOOKAHEAD(args, tok) struct graphql_token *tok = list_top(tokens, struct graphql_token, node); +#define MSG(msg) if (*err) *err = msg; + + +/* The following parser functions are written in a way that corresponds to the + * grammar defined in the GraphQL specification. The code is not intended to + * look like normal C code; it's designed for parsing clarity rather than C + * style. Think of it as something generated rather than something to read. + * For that reason, the functions follow special rules: + * + * - The declaration is standardized with RET and PARAMS + * - The "err" argument is assumed to be NULL upon entrance + * - The "err" argument is set on failure + * - If the function fails to parse, then "tokens" shall be as it was upon entrance + * - INIT and EXIT macros are used + * - Macros such as REQ and OPT facilitate readability and conciseness + */ + +/* The following functions construct the "leaves" of the abstract syntax tree. */ + +RET parse_keyword(PARAMS, const char *keyword, const char *errmsg) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, node); + if (!tok || tok->token_type != 'a') { + *err = errmsg; return NULL; + } + if (!streq(tok->token_string, keyword)) { + *err = errmsg; return NULL; + } + CONSUME_ONE; + return tok; +} + +// Note: a static buffer is used here. +RET parse_punct(PARAMS, int punct) { + static char punctbuf[16]; + struct graphql_token *tok = list_top(tokens, struct graphql_token, node); + if (!tok || tok->token_type != punct) { + if (punct == PUNCT_SPREAD) + sprintf(punctbuf, "expected: '...'"); + else + sprintf(punctbuf, "expected: '%c'", punct); + *err = punctbuf; return NULL; + } + CONSUME_ONE; + return tok; +} + +RET parse_name(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, node); + if (!tok || tok->token_type != 'a') { + *err = "name expected"; return NULL; + } + CONSUME_ONE; + return tok; +} + +RET parse_int(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, node); + if (!tok || tok->token_type != 'i') { + *err = "integer expected"; return NULL; + } + CONSUME_ONE; + return tok; +} + +RET parse_float(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, node); + if (!tok || tok->token_type != 'f') { + *err = "float expected"; return NULL; + } + CONSUME_ONE; + return tok; +} + +RET parse_string(PARAMS) { + struct graphql_token *tok = list_top(tokens, struct graphql_token, node); + if (!tok || tok->token_type != 's') { + *err = "string expected"; return NULL; + } + CONSUME_ONE; + return tok; +} + +// The following functions create the branches of the AST. + +/* +RET parse_non_null_type_2(PARAMS) { + INIT(non_null_type); + parse_list_type(ARGS); REQ; + parse_punct(ARGS, '!'); REQ; + EXIT; +} + +RET parse_non_null_type_1(PARAMS) { + INIT(non_null_type); + parse_named_type(ARGS); REQ; + parse_punct(ARGS, '!'); REQ; + EXIT; +} + +RET parse_non_null_type(PARAMS) { + INIT(non_null_type); + parse_non_null_type_1(ARGS); OR + parse_non_null_type_2(ARGS); + EXIT; +} + +RET parse_list_type(PARAMS) { + INIT(list_type); + parse_punct(ARGS, '['); REQ + parse_type(ARGS); REQ + parse_punct(ARGS, ']'); REQ + EXIT; +} +*/ + +RET parse_named_type(PARAMS) { + INIT(named_type); + obj->name = parse_name(ARGS); + EXIT; +} + +RET parse_type(PARAMS) { + INIT(type); + obj->named = parse_named_type(ARGS); +/* + OR + obj->list = parse_list_type(ARGS); OR + obj->non_null = parse_non_null_type(ARGS); +*/ + EXIT; +} + +RET parse_variable(PARAMS) { + INIT(variable); + parse_punct(ARGS, '$'); REQ + obj->name = parse_name(ARGS); REQ + EXIT; +} + +RET parse_value(PARAMS); + +RET parse_list_value(PARAMS) { + INIT(list_value); + parse_punct(ARGS, '['); REQ + parse_punct(ARGS, ']'); + while (*err) { + *err = NULL; + parse_value(ARGS); MSG("expected: value or ']'"); REQ + parse_punct(ARGS, ']'); + } + EXIT; +} + +RET parse_enum_value(PARAMS) { + INIT(enum_value); + obj->val = parse_name(ARGS); REQ + struct graphql_token *tok = list_top(used, struct graphql_token, node); + if (streq(tok->token_string, "true") + || streq(tok->token_string, "false") + || streq(tok->token_string, "null")) { + *err = "enum value cannot be true, false, or null"; + ROLLBACK(ARGS); + } + EXIT; +} + +RET parse_null_value(PARAMS) { + INIT(null_value); + obj->val = parse_keyword(ARGS, "null", "null expected"); + EXIT; +} + +RET parse_string_value(PARAMS) { + INIT(string_value); + obj->val = parse_string(ARGS); + EXIT; +} + +RET parse_boolean_value(PARAMS) { + INIT(boolean_value); + obj->val = parse_keyword(ARGS, "true", "invalid boolean value"); OR + obj->val = parse_keyword(ARGS, "false", "invalid boolean value"); + EXIT; +} + +RET parse_float_value(PARAMS) { + INIT(float_value); + obj->val = parse_float(ARGS); + EXIT; +} + +RET parse_int_value(PARAMS) { + INIT(int_value); + obj->val = parse_int(ARGS); + EXIT; +} + +RET parse_object_field(PARAMS) { + INIT(object_field); + obj->name = parse_name(ARGS); REQ + parse_punct(ARGS, ':'); REQ + obj->val = parse_value(ARGS); REQ + EXIT; +} + +RET parse_object_value(PARAMS) { + INIT(object_value); + parse_punct(ARGS, '{'); REQ + parse_punct(ARGS, '}'); + struct graphql_object_field *p = NULL; + while (*err) { + *err = NULL; + if (!p) { + obj->first = p = parse_object_field(ARGS); MSG("expected: object field or '}'"); REQ + } else { + p->next = parse_object_field(ARGS); MSG("expected: object field or '}'"); REQ + p = p->next; + } + parse_punct(ARGS, '}'); + } + EXIT; +} + +RET parse_default_value(PARAMS) { + INIT(default_value); + parse_punct(ARGS, '='); REQ + obj->val = parse_value(ARGS); REQ + EXIT; +} + +RET parse_value(PARAMS) { + INIT(value); + obj->var = parse_variable(ARGS); // FIXME: if not const + OR + obj->int_val = parse_int_value(ARGS); OR + obj->float_val = parse_float_value(ARGS); OR + obj->str_val = parse_string_value(ARGS); OR + obj->bool_val = parse_boolean_value(ARGS); OR + obj->null_val = parse_null_value(ARGS); OR + obj->enum_val = parse_enum_value(ARGS); OR + obj->list_val = parse_list_value(ARGS); OR + obj->obj_val = parse_object_value(ARGS); + EXIT; +} + +RET parse_type_condition(PARAMS) { + INIT(type_condition); + parse_keyword(ARGS, "on", "expected: 'on'"); REQ + obj->named_type = parse_named_type(ARGS); REQ + EXIT; +} + +RET parse_fragment_name(PARAMS) { + INIT(fragment_name); + obj->name = parse_name(ARGS); REQ + struct graphql_token *tok = list_top(used, struct graphql_token, node); + if (streq(tok->token_string, "on")) { + *err = "invalid fragment name"; + ROLLBACK(ARGS); + } + EXIT; +} + +RET parse_alias(PARAMS) { + INIT(alias); + obj->name = parse_name(ARGS); REQ + parse_punct(ARGS, ':'); REQ + EXIT; +} + +RET parse_argument(PARAMS) { + INIT(argument); + obj->name = parse_name(ARGS); REQ + parse_punct(ARGS, ':'); REQ + obj->val = parse_value(ARGS); REQ + EXIT; +} + +RET parse_arguments(PARAMS) { + INIT(arguments); + parse_punct(ARGS, '('); REQ + obj->first = parse_argument(ARGS); REQ + struct graphql_argument *p = obj->first; + parse_punct(ARGS, ')'); + while (*err) { + *err = NULL; + p->next = parse_argument(ARGS); MSG("expected: argument or ')'"); REQ; + p = p->next; + parse_punct(ARGS, ')'); + } + EXIT; +} + +RET parse_directive(PARAMS) { + INIT(directive); + parse_punct(ARGS, '@'); REQ + obj->name = parse_name(ARGS); REQ + obj->args = parse_arguments(ARGS); OPT + EXIT; +} + +RET parse_directives(PARAMS) { + INIT(directives); + obj->first = parse_directive(ARGS); REQ + struct graphql_directive *p = obj->first; + do { + p->next = parse_directive(ARGS); + p = p->next; + } WHILE_OPT; + EXIT; +} + +RET parse_fragment_spread(PARAMS) { + INIT(fragment_spread); + parse_punct(ARGS, PUNCT_SPREAD); REQ + obj->name = parse_fragment_name(ARGS); REQ + obj->directives = parse_directives(ARGS); OPT + EXIT; +} + +RET parse_variable_definition(PARAMS) { + INIT(variable_definition); + obj->var = parse_variable(ARGS); REQ + parse_punct(ARGS, ':'); REQ + obj->type = parse_type(ARGS); REQ + obj->default_val = parse_default_value(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + EXIT; +} + +RET parse_variable_definitions(PARAMS) { + INIT(variable_definitions); + parse_punct(ARGS, '('); REQ + obj->first = parse_variable_definition(ARGS); REQ + struct graphql_variable_definition *p = obj->first; + parse_punct(ARGS, ')'); + while (*err) { + *err = NULL; + p->next = parse_variable_definition(ARGS); MSG("expected: variable definition or ')'"); REQ + p = p->next; + parse_punct(ARGS, ')'); + } + EXIT; +} + +RET parse_selection_set(PARAMS); + +RET parse_fragment_definition(PARAMS) { + INIT(fragment_definition); + parse_keyword(ARGS, "fragment", "fragment expected"); REQ + obj->name = parse_fragment_name(ARGS); REQ + obj->type_cond = parse_type_condition(ARGS); REQ + obj->directives = parse_directives(ARGS); OPT + obj->sel_set = parse_selection_set(ARGS); REQ + EXIT; +} + +RET parse_inline_fragment(PARAMS) { + INIT(inline_fragment); + parse_punct(ARGS, PUNCT_SPREAD); REQ + obj->type_cond = parse_type_condition(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + obj->sel_set = parse_selection_set(ARGS); REQ + EXIT; +} + +RET parse_field(PARAMS) { + INIT(field); + obj->alias = parse_alias(ARGS); OPT + obj->name = parse_name(ARGS); REQ + obj->args = parse_arguments(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + obj->sel_set = parse_selection_set(ARGS); OPT + EXIT; +} + +RET parse_selection(PARAMS) { + INIT(selection); + obj->field = parse_field(ARGS); OR + obj->frag_spread = parse_fragment_spread(ARGS); OR + obj->inline_frag = parse_inline_fragment(ARGS); + MSG("expected: field, fragment spread, or inline fragment"); + EXIT; +} + +RET parse_selection_set(PARAMS) { + INIT(selection_set); + parse_punct(ARGS, '{'); REQ; + obj->first = parse_selection(ARGS); REQ; + struct graphql_selection *p = obj->first; + parse_punct(ARGS, '}'); + while (*err) { + *err = NULL; + p->next = parse_selection(ARGS); MSG("expected: selection or '}'"); REQ; + p = p->next; + parse_punct(ARGS, '}'); + } + EXIT; +} + +RET parse_operation_type(PARAMS) { + INIT(operation_type); + const char *errmsg = "expected: query, mutation, or subscription"; + obj->op_type = parse_keyword(ARGS, "query", errmsg); OR + obj->op_type = parse_keyword(ARGS, "mutation", errmsg); OR + obj->op_type = parse_keyword(ARGS, "subscription", errmsg); + EXIT; +} + +RET parse_operation_definition(PARAMS) { + INIT(operation_definition); + obj->op_type = parse_operation_type(ARGS); + if (!*err) { + obj->op_name = parse_name(ARGS); OPT + obj->vars = parse_variable_definitions(ARGS); OPT + obj->directives = parse_directives(ARGS); OPT + } else + *err = NULL; + obj->sel_set = parse_selection_set(ARGS); + if (*err) ROLLBACK(ARGS); + EXIT; +} + +RET parse_executable_definition(PARAMS) { + INIT(executable_definition); + obj->op_def = parse_operation_definition(ARGS); MSG("invalid operation or fragment definition"); OR + obj->frag_def = parse_fragment_definition(ARGS); MSG("invalid operation or fragment definition"); + EXIT; +} + +RET parse_executable_document(PARAMS) { + INIT(executable_document); + obj->first_def = parse_executable_definition(ARGS); REQ + struct graphql_executable_definition *p = obj->first_def; + do { + p->next_def = parse_executable_definition(ARGS); + p = p->next_def; + } WHILE_OPT; + EXIT; +} + +RET parse_definition(PARAMS) { + INIT(definition); + obj->executable_def = parse_executable_definition(ARGS); +/* OR + obj->type_system_def = parse_type_system_definition_or_extension(ARGS); + // NOTE: Optional type system is not (yet) implemented. +*/ + EXIT; +} + +RET parse_document(PARAMS) { + INIT(document); + obj->first_def = parse_definition(ARGS); REQ + struct graphql_definition *p = obj->first_def; + do { + p->next_def = parse_definition(ARGS); + p = p->next_def; + } WHILE_OPT; + EXIT; +} +void *currently_unused = parse_document; // to hide the warning till this is used + +/* Convert input string into tokens. + * + * All data (i.e. the list and the tokens it contains) are allocated to the + * specified tal context. + */ +const char *graphql_lex(const tal_t *ctx, const char *input, struct list_head **tokens) { + + unsigned int c; + const char *p, *line_beginning; + unsigned int line_num = 1; + struct list_head *tok_list; + struct graphql_token *tok; + + // Initialize token output list. + tok_list = tal(ctx, struct list_head); + if (tokens) + *tokens = tok_list; + list_head_init(tok_list); + + // Note: label and goto are used here like a continue statement except that + // it skips iteration, for when characters are fetched in the loop body. + p = input; + line_beginning = p; + do { + c = *p++; +newchar: + // Consume line terminators and increment line counter. + if (LINE_TERMINATOR(c)) { + unsigned int c0 = c; + c = *p++; + if (c0 == 10 || c0 == 13) + line_num++; + if (c0 == 13 && c == 10) + c = *p++; + line_beginning = p - 1; + goto newchar; + } + + // Consume other ignored tokens. + if (COMMA(c) || WHITE_SPACE(c)) { + c = *p++; + goto newchar; + } + if (COMMENT(c)) { + while (!EOF_CHAR(c) && COMMENT_CHAR(c)) + c = *p++; + goto newchar; + } + + // Return success when end is reached. + if (EOF_CHAR(c)) + return GRAPHQL_SUCCESS; + + // Punctuator tokens. + if (PUNCTUATOR(c)) { + + // Note beginning of token in input. + const char *start = p - 1; + + // Handle the ... multi-character case. + if (c == '.') { + c = *p++; + if (c != '.') + return "unrecognized punctuator"; + c = *p++; + if (c != '.') + return "unrecognized punctuator"; + c = PUNCT_SPREAD; + } + + tok = talz(tok_list, struct graphql_token); + list_add_tail(tok_list, &tok->node); + tok->token_type = c; + tok->token_string = NULL; + tok->source_line = line_num; + tok->source_column = start - line_beginning + 1; + tok->source_offset = start - input; + tok->source_len = p - start; + + } else if (NAME_START(c)) { + + // Name/identifier tokens. + tok = talz(tok_list, struct graphql_token); + list_add_tail(tok_list, &tok->node); + tok->token_type = 'a'; + // tok->token_string updated below. + tok->source_line = line_num; + tok->source_column = p - line_beginning; + // tok->source_len updated below. + + // Note the beginning of the name. + const char *name_begin = p - 1; + const char *name_end; + int name_len; + + // Consume the rest of the token. + do { + c = *p++; + } while (NAME_CONTINUE(c)); + + // Note the end of the name and calculate the length. + name_end = p - 1; + name_len = name_end - name_begin; + tok->source_offset = name_begin - input; + tok->source_len = name_len; + + // Copy the token string. + tok->token_string = tal_strndup(tok, name_begin, name_len); + + goto newchar; + + } else if (DIGIT(c) || c == '-') { + + // Number tokens. + const char *num_start = p - 1; + char type = 'i'; + + if (c == '-') { + c = *p++; + if (!DIGIT(c)) + return "negative sign must precede a number"; + } + + if (c == '0') { + c = *p++; + if (DIGIT(c)) + return "leading zeros are not allowed"; + } else { + do { + c = *p++; + } while(DIGIT(c)); + } + + if (c == '.') { + type = 'f'; + if (!DIGIT(*p)) + return "invalid float value fractional part"; + do { + c = *p++; + } while(DIGIT(c)); + } + + if (c == 'e' || c == 'E') { + type = 'f'; + c = *p++; + if (c == '+' || c == '-') + c = *p++; + if (!DIGIT(*p)) + return "invalid float value exponent part"; + do { + c = *p++; + } while(DIGIT(c)); + } + + if (c == '.' || NAME_START(c)) + return "invalid numeric value"; + + const char *num_end = p - 1; + int num_len = num_end - num_start; + + tok = talz(tok_list, struct graphql_token); + list_add_tail(tok_list, &tok->node); + tok->token_type = type; + tok->token_string = tal_strndup(tok, num_start, num_len); + tok->source_line = line_num; + tok->source_column = num_start - line_beginning + 1; + tok->source_offset = num_start - input; + tok->source_len = num_len; + + goto newchar; + + } else if (c == '"') { + + // String tokens. + c = *p++; + const char *str_begin = p - 1; + const char *str_end; + bool str_block = false; + if (c == '"') { + c = *p++; + if (c == '"') { + // block string + str_block = true; + str_begin += 2; + int quotes = 0; + do { + c = *p++; + if (c == '\"') quotes++; else quotes = 0; + if (quotes == 3 && *(p-4) == '\\') quotes = 0; + } while (BLOCK_STRING_CHAR(c) && quotes < 3); + if (quotes == 3) { + c = *--p; + c = *--p; + } + str_end = p - 1; + if (c != '"') + return "unterminated string or invalid character"; + c = *p++; + if (c != '"') + return "invalid string termination"; + c = *p++; + if (c != '"') + return "invalid string termination"; + } else { + // empty string + str_end = str_begin; + --p; + } + } else { + // normal string + --p; + do { + c = *p++; + if (c == '\\') { + c = *p++; + if (strchr("\"\\/bfnrtu", c)) { + if (c == 'u') { + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + c = *p++; + if (!HEX_DIGIT(c)) + return "invalid unicode escape sequence"; + } else { + c = 'a'; // anything besides a quote to let the loop continue + } + } else { + return "invalid string escape sequence"; + } + } + } while (STRING_CHAR(c)); + if (c != '"') + return "unterminated string or invalid character"; + str_end = p - 1; + } + int str_len = str_end - str_begin; + + tok = talz(tok_list, struct graphql_token); + list_add_tail(tok_list, &tok->node); + tok->token_type = 's'; + tok->token_string = tal_strndup(tok, str_begin, str_len); + tok->source_line = line_num; + tok->source_column = str_begin - line_beginning + 1; + tok->source_offset = str_begin - input; + tok->source_len = str_len; + + // Process escape sequences. These always shorten the string (so the memory allocation is always enough). + char d; + char *q = tok->token_string; + char *rewrite_dest; + int quotes = 0; + while ((d = *q++)) { + if (str_block) { + if (d == '\"') quotes++; else quotes = 0; + if (quotes == 3 && *(q-4) == '\\') { + quotes = 0; + rewrite_dest = q - 4; + cpystr(rewrite_dest, q - 3); + } + } else { + if (d == '\\') { + rewrite_dest = q - 1; + d = *q++; + switch (d) { + case '\"': + *rewrite_dest++ = '\"'; + cpystr(rewrite_dest, q--); + break; + case 'b': + *rewrite_dest++ = '\b'; + cpystr(rewrite_dest, q--); + break; + case 'f': + *rewrite_dest++ = '\f'; + cpystr(rewrite_dest, q--); + break; + case 'n': + *rewrite_dest++ = '\n'; + cpystr(rewrite_dest, q--); + break; + case 'r': + *rewrite_dest++ = '\r'; + cpystr(rewrite_dest, q--); + break; + case 't': + *rewrite_dest++ = '\t'; + cpystr(rewrite_dest, q--); + break; + case 'u': { + // Insert escaped character using UTF-8 multi-byte encoding. + char buf[5], *b = buf; + for (int i = 0; i < 4; i++) + *b++ = *q++; + *b = 0; + int code_point = strtol(buf, 0, 16); + int bytes = utf8_encode(code_point, rewrite_dest); + // note: if bytes == 0 + // due to encoding failure, + // the following will safely + // eliminate the invalid char. + rewrite_dest += bytes; + cpystr(rewrite_dest, q--); + } + break; + default: + cpystr(rewrite_dest, --q); + } + } + } + } + if (str_block) { + // Strip leading lines. + q = tok->token_string; + for (;;) { + d = *q++; + while (WHITE_SPACE(d)) + d = *q++; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *q++; + cpystr(tok->token_string, q - 1); + q = tok->token_string; + } else + break; + } + + // Strip trailing lines. + q = tok->token_string + strlen(tok->token_string); + for (;;) { + d = *--q; + while (WHITE_SPACE(d)) + d = *--q; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *--q; + *++q = 0; + } else + break; + } + + // Look for common indentation. + char *this_indent_start; + const char *this_indent_end; + const char *common_indent_start = NULL; + const char *common_indent_end = common_indent_start; + const char *r; + q = tok->token_string; + do { + d = *q++; + this_indent_start = q - 1; + while (WHITE_SPACE(d)) + d = *q++; + this_indent_end = q - 1; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *q++; + continue; + } + if (EOF_CHAR(d)) + continue; + + if (common_indent_start == NULL) { + common_indent_start = this_indent_start; + common_indent_end = this_indent_end; + } + for (r = this_indent_start; r < this_indent_end && (r - this_indent_start + common_indent_start < common_indent_end); r++) { + if (*r != *(r - this_indent_start + common_indent_start)) + break; + } + common_indent_end = r - this_indent_start + common_indent_start; + + while (!LINE_TERMINATOR(d) && !EOF_CHAR(d)) + d = *q++; + while (LINE_TERMINATOR(d)) + d = *q++; + --q; + + } while (d); + + // Remove common indentation. + int common_indent_len = common_indent_end - common_indent_start; + if (common_indent_len > 0) { + q = tok->token_string; + do { + d = *q++; + this_indent_start = q - 1; + while (WHITE_SPACE(d)) + d = *q++; + this_indent_end = q - 1; + if (LINE_TERMINATOR(d)) { + while (LINE_TERMINATOR(d)) + d = *q++; + continue; + } + if (EOF_CHAR(d)) + continue; + + while (!LINE_TERMINATOR(d) && !EOF_CHAR(d)) + d = *q++; + --q; + + cpystr(this_indent_start, this_indent_start + common_indent_len); + q -= common_indent_len; + d = *q++; + + while (LINE_TERMINATOR(d)) + d = *q++; + --q; + + } while (d); + } + } + c = *p++; + goto newchar; + + } else { + return "invalid source character encountered"; + } + + } while (!EOF_CHAR(c)); + + return "unexpected end-of-input encountered"; +} + +// Convert lexed tokens into AST. +const char *graphql_parse(struct list_head *tokens, struct graphql_executable_document **doc) { + struct list_head used = LIST_HEAD_INIT(used); + const char *err = NULL; + *doc = parse_executable_document(tokens, &used, &err); + return err; +} + +// Convert input string into AST. +const char *graphql_lexparse(const tal_t *ctx, const char *input, struct list_head **tokens, struct graphql_executable_document **doc) { + const char *err = graphql_lex(ctx, input, tokens); + if (!err) + err = graphql_parse(*tokens, doc); + return err; +} + + + diff --git a/ccan/ccan/graphql/graphql.h b/ccan/ccan/graphql/graphql.h new file mode 100644 index 000000000000..30116d460d59 --- /dev/null +++ b/ccan/ccan/graphql/graphql.h @@ -0,0 +1,291 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#ifndef __GRAPHQL_H__ +#define __GRAPHQL_H__ 1 + +#include + +#include +#include + +// Coding constants +#define GRAPHQL_SUCCESS ((const char *)NULL) + +// The following structures constitute the AST returned by the parser. + +struct graphql_directive { + struct graphql_directive *next; + struct graphql_token *name; + struct graphql_arguments *args; + void *data; // for application use +}; + +struct graphql_directives { + struct graphql_directive *first; + void *data; // for application use +}; + +struct graphql_named_type { + struct graphql_token *name; + void *data; // for application use +}; + +struct graphql_type { + struct graphql_named_type *named; +// struct graphql_list_type *list; +// struct graphql_non_null_type *non_null; + void *data; // for application use +}; + +struct graphql_default_value { + struct graphql_value *val; + void *data; // for application use +}; + +struct graphql_variable_definition { + struct graphql_variable_definition *next; + struct graphql_variable *var; + struct graphql_type *type; + struct graphql_default_value *default_val; + struct graphql_directives *directives; + void *data; // for application use +}; + +struct graphql_variable_definitions { + struct graphql_variable_definition *first; + void *data; // for application use +}; + +struct graphql_variable { + struct graphql_token *name; + void *data; // for application use +}; + +struct graphql_object_field { + struct graphql_object_field *next; + struct graphql_token *name; + struct graphql_value *val; + void *data; // for application use +}; + +struct graphql_object_value { + struct graphql_object_field *first; + void *data; // for application use +}; + +struct graphql_list_value { + struct graphql_token *val; + void *data; // for application use +}; + +struct graphql_enum_value { + struct graphql_token *val; + void *data; // for application use +}; + +struct graphql_null_value { + struct graphql_token *val; + void *data; // for application use +}; + +struct graphql_string_value { + struct graphql_token *val; + void *data; // for application use +}; + +struct graphql_boolean_value { + struct graphql_token *val; + void *data; // for application use +}; + +struct graphql_float_value { + struct graphql_token *val; + void *data; // for application use +}; + +struct graphql_int_value { + struct graphql_token *val; + void *data; // for application use +}; + +struct graphql_value { + struct graphql_variable *var; + struct graphql_int_value *int_val; + struct graphql_float_value *float_val; + struct graphql_boolean_value *bool_val; + struct graphql_string_value *str_val; + struct graphql_null_value *null_val; + struct graphql_enum_value *enum_val; + struct graphql_list_value *list_val; + struct graphql_object_value *obj_val; + void *data; // for application use +}; + +struct graphql_inline_fragment { + struct graphql_type_condition *type_cond; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; + void *data; // for application use +}; + +struct graphql_type_condition { + struct graphql_named_type *named_type; + void *data; // for application use +}; + +struct graphql_fragment_name { + struct graphql_token *name; + void *data; // for application use +}; + +struct graphql_fragment_definition { + struct graphql_fragment_name *name; + struct graphql_type_condition *type_cond; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; + void *data; // for application use +}; + +struct graphql_fragment_spread { + struct graphql_fragment_name *name; + struct graphql_directives *directives; + void *data; // for application use +}; + +struct graphql_alias { + struct graphql_token *name; + void *data; // for application use +}; + +struct graphql_argument { + struct graphql_argument *next; + struct graphql_token *name; + struct graphql_value *val; + void *data; // for application use +}; + +struct graphql_arguments { + struct graphql_argument *first; + void *data; // for application use +}; + +struct graphql_field { + struct graphql_alias *alias; + struct graphql_token *name; + struct graphql_arguments *args; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; + void *data; // for application use +}; + +struct graphql_selection { + struct graphql_selection *next; + struct graphql_field *field; + struct graphql_fragment_spread *frag_spread; + struct graphql_inline_fragment *inline_frag; + void *data; // for application use +}; + +struct graphql_selection_set { + struct graphql_selection *first; + void *data; // for application use +}; + +struct graphql_operation_type { + struct graphql_token *op_type; + void *data; // for application use +}; + +struct graphql_operation_definition { + struct graphql_operation_type *op_type; + struct graphql_token *op_name; + struct graphql_variable_definitions *vars; + struct graphql_directives *directives; + struct graphql_selection_set *sel_set; + void *data; // for application use +}; + +struct graphql_executable_definition { + struct graphql_executable_definition *next_def; + struct graphql_operation_definition *op_def; + struct graphql_fragment_definition *frag_def; + void *data; // for application use +}; + +struct graphql_executable_document { + struct graphql_executable_definition *first_def; + void *data; // for application use +}; + +struct graphql_definition { + struct graphql_definition *next_def; + struct graphql_executable_definition *executable_def; + struct graphql_type_system_definition_or_extension *type_system_def; + void *data; // for application use +}; + +struct graphql_document { + struct graphql_definition *first_def; + void *data; // for application use +}; + +enum token_type_enum { + NAME = 'a', + INTEGER = 'i', + FLOAT = 'f', + STRING = 's', + PUNCT_BANG = '!', + PUNCT_SH__ = '$', + PUNCT_AMP = '&', + PUNCT_LPAR = '(', + PUNCT_RPAR = ')', + PUNCT_COLON = ':', + PUNCT_EQ = '=', + PUNCT_AT = '@', + PUNCT_LBRACKET = '[', + PUNCT_RBRACKET = ']', + PUNCT_LBRACE = '{', + PUNCT_PIPE = '|', + PUNCT_RBRACE = '}', + PUNCT_SPREAD = 0x2026, // spread operator (triple dot) +}; + +struct graphql_token { + struct list_node node; + enum token_type_enum token_type; + char *token_string; + unsigned int source_line; + unsigned int source_column; + unsigned int source_offset; + unsigned int source_len; + void *data; // for application use +}; + +/* The lexer. + * INPUTS: + * input - string to parse + * ctx - parent tal context or NULL + * tokens - a variable to receive the resulting token list + * RETURN: + * GRAPHQL_SUCCESS or an error string. + */ +const char *graphql_lex(const tal_t *ctx, const char *input, struct list_head **tokens); + +/* The parser. + * INPUTS: + * tokens - the list produced by the lexer + * doc - a variable to receive the resulting abstract syntax tree (AST) + * OPERATION: + * The token list is emptied during parsing, so far as the parsing + * succeeds. This allows the caller to inspect the line/char position + * of the next token (where the error likely is) and report that hint to + * the user in the form of an error message. + * RETURN: + * GRAPHQL_SUCCESS or an error string. + */ +const char *graphql_parse(struct list_head *tokens, struct graphql_executable_document **doc); + +/* The lexer and parser in one function, for convenience. */ +const char *graphql_lexparse(const tal_t *ctx, const char *input, struct list_head **tokens, struct graphql_executable_document **doc); + +#endif + diff --git a/ccan/ccan/graphql/test/run.c b/ccan/ccan/graphql/test/run.c new file mode 100644 index 000000000000..7ae167841317 --- /dev/null +++ b/ccan/ccan/graphql/test/run.c @@ -0,0 +1,3177 @@ +/* Include the C files directly. */ +#include "ccan/graphql/graphql.c" +#include "ccan/str/str.h" + +/* TEST POINT MACROS + * + * The idea here is to run each test case silently, and if any test point + * fails, that test case is re-run verbosely to pinpoint the failure. + * + * RUN macros run a whole test case function. + * + * Different TEST_xxxx macros are provided for different test point types as + * follows: + * + * TEST_CONT - Test and continue testing regardless of failure (the failure + * will still be printed). + * TEST_ABRT - Test and abort the test case, used when testing pointer + * validity to avoid subsequent dereference errors. + * TEST_STRG - Test a string value, which includes the convenience of testing + * for a null pointer. + */ + +#define RUN(test) { prev_fail = fail; test(source); if (fail != prev_fail) { mute = false; test(source); mute = true; } } +#define RUN1(test,arg) { prev_fail = fail; test(source,arg); if (fail != prev_fail) { mute = false; test(source,arg); mute = true; } } +#define RUN2(test,arg1,arg2) { prev_fail = fail; test(source,arg1,arg2); if (fail != prev_fail) { mute = false; test(source,arg1,arg2); mute = true; } } + +#define TEST_CONT(expr) { bool c = (expr); if (mute) c? pass++ : fail++; else printf("%s: %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(expr)); } +#define TEST_ABRT(expr) { bool c = (expr); if (mute) c? pass++ : fail++; else printf("%s: %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(expr)); if (!c) return; } +#define TEST_STRG(str,expr) { bool c = ((str) && streq((str),(expr))); if (mute) c? pass++ : fail++; else printf("%s: %s == %s\033[0m\n", c? "passed" : "\033[91mfailed", stringify(str), stringify(expr)); if (!c) return; } + +// Global variables to track overall results. +int pass = 0, fail = 0; +bool mute = 1; + +// Helper function. +int listlen(struct list_head *tokens); +int listlen(struct list_head *tokens) { + struct graphql_token *tok; + int n=0; + list_for_each(tokens, tok, node) { + n++; + } + return n; +} + +/* Test case function prototypes */ + +void check_example_3(const char *source); +void check_example_5(char *source); +void check_example_6(char *source); +void check_example_7(char *source); +void check_example_8(char *source); +void check_example_9(char *source); +void check_example_10(char *source); +void check_example_11(char *source); +void check_example_12_and_13(const char *source); +void check_example_14(char *source); +void check_example_16(char *source); +void check_example_18(char *source); +void check_example_19(char *source); +void check_example_20(char *source); +void check_example_21(char *source); +void check_example_23(char *source); +void check_example_24(char *source); +void check_int_value(char *source, int int_value); +void check_invalid_int_values(char *source); +void check_float_value(char *source, float float_value, const char *format); +void check_valid_float_values(char *source); +void check_invalid_float_values(char *source); +void check_boolean_values(char *source); +void check_string_value(char *source, const char *test_value, const char *expected_result); +void check_example_25_and_26(const char *source); +void check_example_29(char *source); +void check_example_30_and_31(const char *source); +void check_example_32(char *source); +void check_example_34(char *source); +void check_example_35(char *source); + +/* Test case functions begin here, called by main(). + * Note: Memory should be freed correctly in the success case, but if there + * are errors, all bets are off. + */ + +void check_example_3(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 3\n"); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '{'); + TEST_CONT(tok->source_line == 1); + TEST_CONT(tok->source_column == 1); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 4); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '('); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 7); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 8); + TEST_CONT(tok->source_len == 2); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == ':'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 10); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 13); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '{'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 15); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + TEST_CONT(tok->source_line == 3); + TEST_CONT(tok->source_column == 5); + TEST_CONT(tok->source_len == 4); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '}'); + TEST_CONT(tok->source_line == 4); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '}'); + TEST_CONT(tok->source_line == 5); + TEST_CONT(tok->source_column == 1); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name"); + tokens = tal_free(tokens); +} + +void check_example_5(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 5\n"); + + sprintf(source, "\ +mutation {\n\ + likeStory(storyID: 12345) {\n\ + story {\n\ + likeCount\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 15); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "mutation"); + TEST_CONT(tok->source_line == 1); + TEST_CONT(tok->source_column == 1); + TEST_CONT(tok->source_len == 8); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "likeStory"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 9); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "storyID"); + TEST_CONT(tok->source_line == 2); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "12345"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 22); + TEST_CONT(tok->source_len == 5); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "story"); + TEST_CONT(tok->source_line == 3); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "likeCount"); + TEST_CONT(tok->source_line == 4); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_ABRT(doc->first_def->op_def->op_type != NULL); + TEST_ABRT(doc->first_def->op_def->op_type->op_type != NULL); + TEST_CONT(doc->first_def->op_def->op_type->op_type->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->op_type->op_type->token_string, "mutation"); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "likeStory"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "storyID"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "12345"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "story"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field->name->token_string, "likeCount"); + tokens = tal_free(tokens); +} + +void check_example_6(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 6\n"); + + sprintf(source, "\ +{\n\ + field\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 3); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "field"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 5); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "field"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + tokens = tal_free(tokens); +} + +void check_example_7(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 7\n"); + + sprintf(source, "\ +{\n\ + id\n\ + firstName\n\ + lastName\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 5); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 2); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "firstName"); + TEST_CONT(tok->source_line == 3); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 9); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "lastName"); + TEST_CONT(tok->source_line == 4); + TEST_CONT(tok->source_column == 3); + TEST_CONT(tok->source_len == 8); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); + TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->next->field->name->token_string, "firstName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->next->next->field->name->token_string, "lastName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->next->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_8(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 8\n"); + + sprintf(source, "\ +{\n\ + me {\n\ + id\n\ + firstName\n\ + lastName\n\ + birthday {\n\ + month\n\ + day\n\ + }\n\ + friends {\n\ + name\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 17); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "me"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "firstName"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "lastName"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "birthday"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "month"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "day"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friends"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "me"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "firstName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "lastName"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_string, "birthday"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->name->token_string, "month"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->name->token_string, "day"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set->first->next->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->name->token_string, "friends"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_9(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 9\n"); + + sprintf(source, "\ +# `me` could represent the currently logged in viewer.\n\ +{\n\ + me {\n\ + name\n\ + }\n\ +}\n\ +\n\ +# `user` represents one of many users in a graph of data, referred to by a\n\ +# unique identifier.\n\ +{\n\ + user(id: 4) {\n\ + name\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 17); + // NOTE: Comments are ignored. + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "me"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_ABRT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "me"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->frag_def == NULL); + TEST_ABRT(doc->first_def->next_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->next_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->next_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->next_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "name"); + tokens = tal_free(tokens); +} + +void check_example_10(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 10\n"); + + sprintf(source, "\ +{\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + profilePic(size: 100)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 18); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "size"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "100"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "100"); + tokens = tal_free(tokens); +} + +void check_example_11(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 11\n"); + + sprintf(source, "\ +{\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + profilePic(width: 100, height: 50)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 21); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "width"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "100"); + // NOTE: Comma is ignored. + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "height"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "50"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "width"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "100"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->name->token_string, "height"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next->val->int_val->val->token_string, "50"); + tokens = tal_free(tokens); +} + +void check_example_12_and_13(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example Nos. 12 and 13\n"); + + // Test the lexer. + const char *param; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "picture"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_ABRT(tok->token_string != NULL && (streq(tok->token_string, "width") || streq(tok->token_string, "height"))); + param = tok->token_string; + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_CONT(tok->token_string != NULL && ((streq(param, "width") && streq(tok->token_string, "200")) || (streq(param, "height") && streq(tok->token_string, "100")))); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_CONT(tok->token_string != NULL && (streq(tok->token_string, "width") || streq(tok->token_string, "height"))); + param = tok->token_string; + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_CONT(tok->token_string != NULL && ((streq(param, "width") && streq(tok->token_string, "200")) || (streq(param, "height") && streq(tok->token_string, "100")))); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_argument *arg; + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "picture"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next->next == NULL); + arg = doc->first_def->op_def->sel_set->first->field->args->first; + if (!streq(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "width")) arg = arg->next; + TEST_ABRT(arg->name != NULL); + TEST_CONT(arg->name->token_type == 'a'); + TEST_STRG(arg->name->token_string, "width"); + TEST_ABRT(arg->val != NULL); + TEST_ABRT(arg->val->int_val != NULL); + TEST_ABRT(arg->val->int_val->val != NULL); + TEST_CONT(arg->val->int_val->val->token_type == 'i'); + TEST_STRG(arg->val->int_val->val->token_string, "200"); + arg = doc->first_def->op_def->sel_set->first->field->args->first; + if (!streq(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "height")) arg = arg->next; + TEST_ABRT(arg->name != NULL); + TEST_CONT(arg->name->token_type == 'a'); + TEST_STRG(arg->name->token_string, "height"); + TEST_ABRT(arg->val != NULL); + TEST_ABRT(arg->val->int_val != NULL); + TEST_ABRT(arg->val->int_val->val != NULL); + TEST_CONT(arg->val->int_val->val->token_type == 'i'); + TEST_STRG(arg->val->int_val->val->token_string, "100"); + tokens = tal_free(tokens); +} + +void check_example_14(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 14\n"); + + sprintf(source, "\ +{\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + smallPic: profilePic(size: 64)\n\ + bigPic: profilePic(size: 1024)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 28); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "smallPic"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "size"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "64"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "bigPic"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profilePic"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "size"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "1024"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias->name->token_string, "smallPic"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->int_val->val->token_string, "64"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->alias->name->token_string, "bigPic"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->val->int_val->val->token_string, "1024"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next->field->args->first->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_16(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 16\n"); + + sprintf(source, "\ +{\n\ + zuck: user(id: 4) {\n\ + id\n\ + name\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 14); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, "4"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->alias != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->alias->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->alias->name->token_string, "zuck"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_18(char *source) { + struct list_head *tokens; + + if (!mute) printf("// Example No. 18\n"); + + sprintf(source, "\ +query noFragments {\n\ + user(id: 4) {\n\ + friends(first: 10) {\n\ + id\n\ + name\n\ + profilePic(size: 50)\n\ + }\n\ + mutualFriends(first: 10) {\n\ + id\n\ + name\n\ + profilePic(size: 50)\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 44); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_19(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 19\n"); + + sprintf(source, "\ +query withFragments {\n\ + user(id: 4) {\n\ + friends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + mutualFriends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + }\n\ +}\n\ +\n\ +fragment friendFields on User {\n\ + id\n\ + name\n\ + profilePic(size: 50)\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 46); + for (int i=0; i<17; i++) + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + for (int i=0; i<7; i++) + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_20(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 20\n"); + + sprintf(source, "\ +query withNestedFragments {\n\ + user(id: 4) {\n\ + friends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + mutualFriends(first: 10) {\n\ + ...friendFields\n\ + }\n\ + }\n\ +}\n\ +\n\ +fragment friendFields on User {\n\ + id\n\ + name\n\ + ...standardProfilePic\n\ +}\n\ +\n\ +fragment standardProfilePic on User {\n\ + profilePic(size: 50)\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 54); + for (int i=0; i<17; i++) + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + for (int i=0; i<7; i++) + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "friendFields"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set->first->frag_spread->name->name->token_string, "friendFields"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next == NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "friendFields"); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->next->field == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->frag_spread != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next->next->next == NULL); + TEST_ABRT(doc->first_def->next_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->name->name->token_string, "standardProfilePic"); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->sel_set->first->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_21(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 21\n"); + + sprintf(source, "\ +query FragmentTyping {\n\ + profiles(handles: [\"zuck\", \"coca-cola\"]) {\n\ + handle\n\ + ...userFragment\n\ + ...pageFragment\n\ + }\n\ +}\n\ +\n\ +fragment userFragment on User {\n\ + friends {\n\ + count\n\ + }\n\ +}\n\ +\n\ +fragment pageFragment on Page {\n\ + likers {\n\ + count\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 40); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "query"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "FragmentTyping"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profiles"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "handles"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '['); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "coca-cola"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ']'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread->name->name->token_string, "userFragment"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread->name->name->token_string, "pageFragment"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_ABRT(doc->first_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->name->name->token_string, "userFragment"); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->frag_def->type_cond->named_type->name->token_string, "User"); + TEST_CONT(doc->first_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first != NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->next_def->frag_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->next_def->frag_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->next_def->next_def != NULL); + TEST_CONT(doc->first_def->next_def->next_def->next_def == NULL); + TEST_CONT(doc->first_def->next_def->next_def->op_def == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->name->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->name->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->name->name->token_string, "pageFragment"); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->next_def->next_def->frag_def->type_cond->named_type->name->token_string, "Page"); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->directives == NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set != NULL); + TEST_ABRT(doc->first_def->next_def->next_def->frag_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->next_def->next_def->frag_def->sel_set->first->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_23(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 23\n"); + + sprintf(source, "\ +query inlineFragmentTyping {\n\ + profiles(handles: [\"zuck\", \"coca-cola\"]) {\n\ + handle\n\ + ... on User {\n\ + friends {\n\ + count\n\ + }\n\ + }\n\ + ... on Page {\n\ + likers {\n\ + count\n\ + }\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 34); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "query"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "inlineFragmentTyping"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "profiles"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "handles"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '['); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "coca-cola"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ']'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->type_cond->named_type->name->token_string, "User"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond->named_type->name->token_string, "Page"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->next_def == NULL); + tokens = tal_free(tokens); +} + +void check_example_24(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 24\n"); + + sprintf(source, "\ +query inlineFragmentNoType($expandedInfo: Boolean) {\n\ + user(handle: \"zuck\") {\n\ + id\n\ + name\n\ + ... @include(if: $expandedInfo) {\n\ + firstName\n\ + lastName\n\ + birthday\n\ + }\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 34); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "query"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "inlineFragmentNoType"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "expandedInfo"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "Boolean"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "user"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "handle"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, "zuck"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "id"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "name"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 0x2026); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "include"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "if"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "expandedInfo"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "firstName"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "lastName"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "birthday"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->type_cond == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->name->token_string, "include"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->name->token_string, "if"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->str_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->obj_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->directives->first->args->first->val->var->name->token_string, "expandedInfo"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->next_def == NULL); + tokens = tal_free(tokens); +} + +void check_int_value(char *source, int int_value) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Int Value Range Check on %d\n", int_value); + + sprintf(source, "\ +{\n\ + user(id: %d) {\n\ + name\n\ + }\n\ +}\n\ + ", int_value); + + char buf[20]; + sprintf(buf, "%d", int_value); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + TEST_STRG(tok->token_string, buf); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == strlen(buf)); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12 + strlen(buf)); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, buf); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); +} + +void check_invalid_int_values(char *source) { + struct list_head *tokens; + + if (!mute) printf("// Invalid Int Values\n"); + + const char *bad_values[] = {"00", "-00", "+1", "1.", "1a", "1e", "0x123", "123L", 0}; + + for (int i=0; bad_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", bad_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) != NULL); + TEST_ABRT(listlen(tokens) == 5); + tokens = tal_free(tokens); + + // No need to test parser when lexer fails. + } +} + +void check_float_value(char *source, float float_value, const char *format) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Float Value Range Check on %f\n", float_value); + + char buf[100]; + sprintf(buf, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", format); + sprintf(source, buf, float_value); + sprintf(buf, format, float_value); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, buf); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == strlen(buf)); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12 + strlen(buf)); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_type == 'f'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_string, buf); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); +} + +void check_valid_float_values(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Valid Float Values\n"); + + const char *good_values[] = {"1.0", "1e50", "6.0221413e23", "1.23", 0}; + + for (int i=0; good_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", good_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, good_values[i]); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12); + TEST_CONT(tok->source_len == strlen(good_values[i])); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 12 + strlen(good_values[i])); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_type == 'f'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val->val->token_string, good_values[i]); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } +} + +void check_invalid_float_values(char *source) { + struct list_head *tokens; + + if (!mute) printf("// Invalid Float Values\n"); + + const char *bad_values[] = {"00.0", "-00.0", "00e1", "1.23.4", "0x1.2p3", 0}; + + for (int i=0; bad_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", bad_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) != NULL); + TEST_ABRT(listlen(tokens) == 5); + tokens = tal_free(tokens); + + // No need to test parser when lexer fails. + } +} + +void check_boolean_values(char *source) { + struct list_head *tokens; + + if (!mute) printf("// Boolean Values\n"); + + const char *good_values[] = {"true", "false", 0}; + + for (int i=0; good_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", good_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val->val->token_string, good_values[i]); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } + + const char *bad_values[] = {"True", "False", "TRUE", "FALSE", 0}; + + for (int i=0; bad_values[i]; i++) { + sprintf(source, "\ +{\n\ + user(id: %s) {\n\ + name\n\ + }\n\ +}\n\ + ", bad_values[i]); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tokens = tal_free(tokens); + + // Test the parser (it will succeed in parsing the bad values as enum values, not boolean values). + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val->val->token_string, bad_values[i]); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } +} + +void check_string_value(char *source, const char *test_value, const char *expected_result) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// String Value Test: %s\n", test_value); + + sprintf(source, "\ +{\n\ + user(id:%s) {\n\ + name\n\ + }\n\ +}\n\ + ", test_value); + + bool block = (test_value[0]=='\"' && test_value[1]=='\"' && test_value[2]=='\"')? true: false; + if (expected_result) { + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 11); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + TEST_STRG(tok->token_string, expected_result); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 11 + (block? 3: 1)); + TEST_CONT(tok->source_len == strlen(test_value) - (block? 6: 2)); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + TEST_CONT(tok->source_line == 2); + TEST_CONT(tok->source_column == 11 + strlen(test_value)); + TEST_CONT(tok->source_len == 1); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser (it will succeed in parsing the bad values as enum values, not boolean values). + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val->token_type == 's'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val->val->token_string, expected_result); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + tokens = tal_free(tokens); + } else { + TEST_CONT(graphql_lex(NULL, source, &tokens) != NULL); + tokens = tal_free(tokens); + } +} + +void check_example_25_and_26(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 25 and 26\n"); + + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + while ((tok = list_pop(tokens, struct graphql_token, node)) && tok->token_type != 's') { } + if (tok) { + TEST_STRG(tok->token_string, "Hello,\n World!\n\nYours,\n GraphQL."); + } + + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + tokens = tal_free(tokens); +} + +void check_example_29(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 29\n"); + + sprintf(source, "\ +{\n\ + field(arg: null)\n\ + field\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 9); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "null"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "field"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "arg"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val->val->token_string, "null"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->next->field->name->token_string, "field"); + TEST_CONT(doc->first_def->op_def->sel_set->first->next->field->args == NULL); + tokens = tal_free(tokens); +} + +void check_example_30_and_31(const char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 30 and 31\n"); + + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 15); + while ((tok = list_pop(tokens, struct graphql_token, node)) && !(tok->token_type == 'a' && tok->token_string != NULL && streq(tok->token_string, "lat"))) { } + TEST_CONT(tok); + if (tok) { + TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, "-53.211"); + } + tokens = tal_free(tokens); + + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + while ((tok = list_pop(tokens, struct graphql_token, node)) && !(tok->token_type == 'a' && tok->token_string != NULL && streq(tok->token_string, "lon"))) { } + TEST_CONT(tok); + if (tok) { + TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'f'); + TEST_STRG(tok->token_string, "12.43"); + } + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + const char *e; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT((e = graphql_parse(tokens, &doc)) == NULL); + if (e) printf("%s\n", e); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_CONT(doc->first_def->op_def->op_type == NULL); + TEST_CONT(doc->first_def->op_def->op_name == NULL); + TEST_CONT(doc->first_def->op_def->vars == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "nearestThing"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "location"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->var == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->float_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->bool_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->enum_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->list_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->null_val == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->str_val == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->name->token_type == 'a'); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->val->float_val->val->token_type == 'f'); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->name->token_type == 'a'); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->val->float_val->val->token_type == 'f'); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->obj_val->first->next->next == NULL); + tokens = tal_free(tokens); +} + +void check_example_32(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 32\n"); + + sprintf(source, "\ +query getZuckProfile($devicePicSize: Int) {\n\ + user(id: 4) {\n\ + id\n\ + name\n\ + profilePic(size: $devicePicSize)\n\ + }\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 27); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'i'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '$'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // Test the parser. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(graphql_parse(tokens, &doc) == NULL); + TEST_ABRT(doc != NULL); + TEST_ABRT(doc->first_def != NULL); + TEST_CONT(doc->first_def->next_def == NULL); + TEST_CONT(doc->first_def->frag_def == NULL); + TEST_ABRT(doc->first_def->op_def != NULL); + TEST_ABRT(doc->first_def->op_def->op_type != NULL); + TEST_ABRT(doc->first_def->op_def->op_type->op_type != NULL); + TEST_CONT(doc->first_def->op_def->op_type->op_type->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->op_type->op_type->token_string, "query"); + TEST_CONT(doc->first_def->op_def->op_name != NULL); + TEST_CONT(doc->first_def->op_def->op_name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->op_name->token_string, "getZuckProfile"); + TEST_ABRT(doc->first_def->op_def->vars != NULL); + TEST_ABRT(doc->first_def->op_def->vars->first != NULL); + TEST_CONT(doc->first_def->op_def->vars->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->var != NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->var->name != NULL); + TEST_CONT(doc->first_def->op_def->vars->first->var->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->vars->first->var->name->token_string, "devicePicSize"); + TEST_ABRT(doc->first_def->op_def->vars->first->type != NULL); +// TEST_CONT(doc->first_def->op_def->vars->first->type->list == NULL); +// TEST_CONT(doc->first_def->op_def->vars->first->type->non_null == NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->type->named != NULL); + TEST_ABRT(doc->first_def->op_def->vars->first->type->named->name != NULL); + TEST_CONT(doc->first_def->op_def->vars->first->type->named->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->vars->first->type->named->name->token_string, "Int"); + TEST_CONT(doc->first_def->op_def->vars->first->default_val == NULL); + TEST_CONT(doc->first_def->op_def->vars->first->directives == NULL); + TEST_CONT(doc->first_def->op_def->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->name->token_string, "user"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->name->token_string, "id"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_type == 'i'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->args->first->val->int_val->val->token_string, "4"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->directives == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->name->token_string, "id"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->name->token_string, "name"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->args == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->next == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->frag_spread == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->inline_frag == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->alias == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->name->token_string, "profilePic"); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->directives == NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->sel_set == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->next == NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->name->token_string, "size"); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var != NULL); + TEST_ABRT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name != NULL); + TEST_CONT(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name->token_type == 'a'); + TEST_STRG(doc->first_def->op_def->sel_set->first->field->sel_set->first->next->next->field->args->first->val->var->name->token_string, "devicePicSize"); + tokens = tal_free(tokens); +} + +void check_example_34(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 34\n"); + + sprintf(source, "\ +type Person\n\ + @addExternalFields(source: \"profiles\")\n\ + @excludeField(name: \"photo\") {\n\ + name: String\n\ +}\n\ + "); + + // Test the lexer. + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 21); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "addExternalFields"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "excludeField"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // The type system is not yet implemented, so parsing will fail here. + // This could be "phase 2" of this project. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) != NULL); + tokens = tal_free(tokens); +} + +void check_example_35(char *source) { + struct list_head *tokens; + struct graphql_token *tok; + + if (!mute) printf("// Example No. 35\n"); + + sprintf(source, "\ +type Person\n\ + @excludeField(name: \"photo\")\n\ + @addExternalFields(source: \"profiles\") {\n\ + name: String\n\ +}\n\ + "); + + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_ABRT(listlen(tokens) == 21); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "excludeField"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '@'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + TEST_STRG(tok->token_string, "addExternalFields"); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '('); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 's'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ')'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '{'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == ':'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == 'a'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok->token_type == '}'); + tok = list_pop(tokens, struct graphql_token, node); TEST_CONT(tok == NULL); + tokens = tal_free(tokens); + + // The type system is not yet implemented, so parsing will fail here. + // This could be "phase 2" of this project. + struct graphql_executable_document *doc; + TEST_CONT(graphql_lex(NULL, source, &tokens) == NULL); + TEST_CONT(graphql_parse(tokens, &doc) != NULL); + tokens = tal_free(tokens); +} + +/* End of test case functions. */ + +/* Beginning of main() test run to run all test cases. */ + +int main(void) +{ + printf("\nTesting GraphQL lexer/parser...\n"); + + char source[1024]; + int prev_fail; // Used by RUNx macros. + + // Check the lexer with all valid line terminators. + const char *new_line = "\n"; + const char *carriage_return = "\r"; + const char *carriage_return_new_line = "\r\n"; + const char *line_terminators[] = { new_line, carriage_return, carriage_return_new_line }; + for (int i=0; i<3; i++) { + sprintf(source, "\ +{%s\ + user(id: 4) {%s\ + name%s\ + }%s\ +}%s\ + ", line_terminators[i], line_terminators[i], line_terminators[i], line_terminators[i], line_terminators[i]); + + RUN(check_example_3); + } + + RUN(check_example_5); // Parse a mutation operation and check results. + + RUN(check_example_6); // Parse an unnamed query and check results. + + RUN(check_example_7); // Parse multiple fields in a selection set and check results. + RUN(check_example_8); // Parse complex data structure and check results. + RUN(check_example_9); // Parse example of top-level fields and check results. + RUN(check_example_10); // Parse example with parameterized field and check results. + RUN(check_example_11); // Parse example with multiple field arguments and check results. + + // Parse examples of different parameter order and check for identical results. + sprintf(source, "\ +{\n\ + picture(width: 200, height: 100)\n\ +}\n\ + "); + RUN(check_example_12_and_13); + sprintf(source, "\ +{\n\ + picture(height: 100, width: 200)\n\ +}\n\ + "); + RUN(check_example_12_and_13); + + RUN(check_example_14); // Parse alias example and check results. + RUN(check_example_16); // Parse a top-level-field alias example and check results. + + RUN(check_example_18); // Parse example and check results. + + RUN(check_example_19); // Parse fragment example and check results. + RUN(check_example_20); // Parse another fragment example and check results. + RUN(check_example_21); // Parse fragment typing example and check results. + RUN(check_example_23); // Parse fragment typing example and check results. + RUN(check_example_24); // Parse fragment typing example and check results. + + // Parse various int values and check results. + for (int i= -15; i<= 15; i++) { RUN1(check_int_value, i); } + for (int i= -32770; i<= -32765; i++) { RUN1(check_int_value, i); } + for (int i= 32765; i<= 32770; i++) { RUN1(check_int_value, i); } + for (int i=-2147483648; i<=-2147483645; i++) { RUN1(check_int_value, i); } + for (int i= 2147483647; i>= 2147483645; i--) { RUN1(check_int_value, i); } + RUN(check_invalid_int_values); + + // Parse various float values and check results. + for (float i= -1.0; i<= 1.0; i+= 0.1) { RUN2(check_float_value, i, "%1.1f"); } + for (float i=-327.70; i<=-327.65; i+= 0.01) { RUN2(check_float_value, i, "%1.2f"); } + for (float i= 327.65; i<= 327.70; i+= 0.01) { RUN2(check_float_value, i, "%1.2f"); } + for (float i= -5e-20; i<= -1e-20; i+= 1e-20) { RUN2(check_float_value, i, "%1.0e"); } + for (float i= 5e-20; i>= 1e-20; i-= 1e-20) { RUN2(check_float_value, i, "%1.0e"); } + for (float i= 5E+20; i>= 1E+20; i-= 1E+20) { RUN2(check_float_value, i, "%1.2E"); } + for (float i=1.5E+20; i>=1.1E+20; i-=0.1E+20) { RUN2(check_float_value, i, "%1.1E"); } + RUN(check_valid_float_values); + RUN(check_invalid_float_values); + + RUN(check_boolean_values); // Parse boolean values and check results. + + // Parse various string values and check results. + RUN2(check_string_value, "te^st", NULL ); // Missing quotes (the caret makes it an invalid token for testing purposes). + RUN2(check_string_value, "\"te^st\"", "te^st" ); // A valid string. + RUN2(check_string_value, "\"\"", "" ); // An empty string is valid. + RUN2(check_string_value, "\"\"\"te^st\"\"\"", "te^st" ); // A block string. + RUN2(check_string_value, "\"te\\st\"", NULL ); // Backslashes are normally invalid. + RUN2(check_string_value, "\"te\nst\"", NULL ); // New-line characters are invalid except in block strings. + RUN2(check_string_value, "\"te\rst\"", NULL ); // New-line characters are invalid except in block strings. + RUN2(check_string_value, "\"\"\"te\nst\"\"\"", "te\nst" ); // New-line characters are valid in block strings. + RUN2(check_string_value, "\"\"\"te\rst\"\"\"", "te\rst" ); // New-line characters are valid in block strings. + RUN2(check_string_value, "\"te\"st\"", NULL ); // A quote in a string is invalid. + RUN2(check_string_value, "\"te\\\"st\"", "te\"st" ); // ...unless it is escaped. + RUN2(check_string_value, "\"\"\"te\"st\"\"\"", "te\"st" ); // A quote in a block string is valid. + RUN2(check_string_value, "\"\"\"te\"\"st\"\"\"", "te\"\"st" ); // It is even valid to have two quotes in a block string. + RUN2(check_string_value, "\"\"\"te\"\"\"st\"\"\"", NULL ); // Three quotes in a row are not allowed in a block string. + RUN2(check_string_value, "\"\"\"te\\\"\"\"st\"\"\"", "te\"\"\"st" ); // ...unless escaped. + RUN2(check_string_value, "\"te\\\"st\"", "te\"st" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\\\st\"", "te\\st" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\/st\"", "te/st" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\bst\"", "te\bst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\fst\"", "te\fst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\nst\"", "te\nst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\rst\"", "te\rst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\tst\"", "te\tst" ); // Check escape sequence. + RUN2(check_string_value, "\"te\\vst\"", NULL ); // Invalid escape sequence. + RUN2(check_string_value, "\"te\\033st\"", NULL ); // Invalid escape sequence. + // Note: Unicode excape sequence is tested below. + + // This block string and this string should result in identical tokens. + sprintf(source, "\ +mutation {\n\ + sendEmail(message: \"\"\"\n\ + Hello,\n\ + World!\n\ +\n\ + Yours,\n\ + GraphQL.\n\ + \"\"\")\n\ +}\n\ + "); + RUN(check_example_25_and_26); + sprintf(source, "\ +mutation {\n\ + sendEmail(message: \"Hello,\\n World!\\n\\nYours,\\n GraphQL.\")\n\ +}\n\ + "); + RUN(check_example_25_and_26); + + // Check block string example. + RUN2(check_string_value, +"\"\"\"\n\ +This starts with and ends with an empty line,\n\ +which makes it easier to read.\n\ +\"\"\"", + "This starts with and ends with an empty line,\nwhich makes it easier to read."); + + // Check block string counter example. + RUN2(check_string_value, +"\"\"\"This does not start with or end with any empty lines,\n\ +which makes it a little harder to read.\"\"\"", + "This does not start with or end with any empty lines,\nwhich makes it a little harder to read."); + + RUN2(check_string_value, "\"te\\u001bst\"", "te\033st" ); // Check unicode escape sequence. + RUN2(check_string_value, "\"te\\u001Bst\"", "te\033st" ); // Check again with other case. + RUN2(check_string_value, "\"\"\"te\\u001bst\"\"\"", "te\\u001bst" ); // Escape sequences are ignored in block strings (except for the triple quote). + RUN2(check_string_value, "\"\"\"te\\nst\"\"\"", "te\\nst" ); // Escape sequences are ignored in block strings (except for the triple quote). + RUN2(check_string_value, "\"te\\u2026st\"", "te\xe2\x80\xa6st"); // Check a unicode escape sequence. + + RUN(check_example_29); // Parse null value and check result. + + // These two input objects should have the same result. + sprintf(source, "\ +{\n\ + nearestThing(location: { lon: 12.43, lat: -53.211 })\n\ +}\n\ + "); + RUN(check_example_30_and_31); + sprintf(source, "\ +{\n\ + nearestThing(location: { lat: -53.211, lon: 12.43 })\n\ +}\n\ + "); + RUN(check_example_30_and_31); + + RUN(check_example_32); // Parse an example with a variable and check result. + + RUN(check_example_34); // Parse directives and check result. + RUN(check_example_35); // Parse directives and check result. + + RUN(check_example_35); // Parse directives and check result. + + printf("total passed: %d\n%stotal failed: %d\033[0m\n", pass, fail?"\033[91m":"", fail); + + return fail==0? 0: 1; +} + From 9a85b02c6f3031e93662c803741efc3c236de676 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 14 Dec 2021 10:11:03 +1030 Subject: [PATCH 0143/1530] wire: add missing spec patch. You have to manually run "make extract-bolt-csv" to recreate the spec files, so we didn't notice that I forgot to check this in :( Signed-off-by: Rusty Russell --- wire/extracted_onion_02_modernonion.patch | 40 +++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 wire/extracted_onion_02_modernonion.patch diff --git a/wire/extracted_onion_02_modernonion.patch b/wire/extracted_onion_02_modernonion.patch new file mode 100644 index 000000000000..653693be9f9a --- /dev/null +++ b/wire/extracted_onion_02_modernonion.patch @@ -0,0 +1,40 @@ +--- wire/onion_wire.csv 2021-11-16 15:17:39.446494580 +1030 ++++ wire/onion_wire.csv.raw 2021-11-16 15:36:00.046441058 +1030 +@@ -8,10 +8,36 @@ + tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, + tlvtype,obs2_encmsg_tlvs,self_id,14 + tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... ++tlvtype,tlv_payload,encrypted_recipient_data,10 ++tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... ++tlvtype,tlv_payload,blinding_point,12 ++tlvdata,tlv_payload,blinding_point,blinding,point, ++tlvtype,encrypted_data_tlv,padding,1 ++tlvdata,encrypted_data_tlv,padding,padding,byte,... ++tlvtype,encrypted_data_tlv,short_channel_id,2 ++tlvdata,encrypted_data_tlv,short_channel_id,short_channel_id,short_channel_id, ++tlvtype,encrypted_data_tlv,next_node_id,4 ++tlvdata,encrypted_data_tlv,next_node_id,node_id,point, ++tlvtype,encrypted_data_tlv,path_id,6 ++tlvdata,encrypted_data_tlv,path_id,data,byte,... ++tlvtype,encrypted_data_tlv,next_blinding_override,8 ++tlvdata,encrypted_data_tlv,next_blinding_override,blinding,point, ++tlvtype,onionmsg_payload,reply_path,2 ++tlvdata,onionmsg_payload,reply_path,first_node_id,point, ++tlvdata,onionmsg_payload,reply_path,blinding,point, ++tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... ++tlvtype,onionmsg_payload,encrypted_data_tlv,4 ++tlvdata,onionmsg_payload,encrypted_data_tlv,encrypted_data_tlv,byte,... ++tlvtype,onionmsg_payload,invoice_request,64 ++tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... ++tlvtype,onionmsg_payload,invoice,66 ++tlvdata,onionmsg_payload,invoice,invoice,byte,... ++tlvtype,onionmsg_payload,invoice_error,68 ++tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... + subtype,onionmsg_path + subtypedata,onionmsg_path,node_id,point, + subtypedata,onionmsg_path,enclen,u16, +-subtypedata,onionmsg_path,enctlv,byte,enclen ++subtypedata,onionmsg_path,encrypted_recipient_data,byte,enclen + msgtype,invalid_realm,PERM|1 + msgtype,temporary_node_failure,NODE|2 + msgtype,permanent_node_failure,PERM|NODE|2 From 43ff949ea7ae8b90474b51d0432a0f2bb0865d85 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 14 Dec 2021 22:57:21 +0100 Subject: [PATCH 0144/1530] lightningd: support hsm error code Suggested-by: Rusty Russell Signed-off-by: Vincenzo Palazzo Changelog-Changed: Support hsm specific error error code in lightning-cli --- common/errcode.h | 6 +++++ common/hsm_encryption.c | 37 ++++++++++++++++++++---------- common/hsm_encryption.h | 11 +++++---- lightningd/hsm_control.c | 13 ++++++----- lightningd/options.c | 40 ++++++++++++++++++++++---------- tests/test_wallet.py | 12 +++++++--- tools/hsmtool.c | 49 ++++++++++++++++++++++------------------ 7 files changed, 109 insertions(+), 59 deletions(-) diff --git a/common/errcode.h b/common/errcode.h index 2b24122626d4..a4ac49e17261 100644 --- a/common/errcode.h +++ b/common/errcode.h @@ -9,4 +9,10 @@ typedef s32 errcode_t; #define PRIerrcode PRId32 +// HSM errors code +#define HSM_GENERIC_ERROR 20 +#define HSM_ERROR_IS_ENCRYPT 21 +#define HSM_BAD_PASSWORD 22 +#define HSM_PASSWORD_INPUT_ERR 23 + #endif /* LIGHTNING_COMMON_ERRCODE_H */ diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index b66c3ea8784f..8a433998b113 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -1,21 +1,28 @@ #include "config.h" +#include #include #include #include -char *hsm_secret_encryption_key(const char *pass, struct secret *key) +int hsm_secret_encryption_key_with_exitcode(const char *pass, struct secret *key, + char **err_msg) { u8 salt[16] = "c-lightning\0\0\0\0\0"; /* Don't swap the encryption key ! */ - if (sodium_mlock(key->data, sizeof(key->data)) != 0) - return "Could not lock hsm_secret encryption key memory."; + if (sodium_mlock(key->data, sizeof(key->data)) != 0) { + *err_msg = "Could not lock hsm_secret encryption key memory."; + return HSM_GENERIC_ERROR; + } /* Check bounds. */ - if (strlen(pass) < crypto_pwhash_argon2id_PASSWD_MIN) - return "Password too short to be able to derive a key from it."; - if (strlen(pass) > crypto_pwhash_argon2id_PASSWD_MAX) - return "Password too long to be able to derive a key from it."; + if (strlen(pass) < crypto_pwhash_argon2id_PASSWD_MIN) { + *err_msg = "Password too short to be able to derive a key from it."; + return HSM_BAD_PASSWORD; + } else if (strlen(pass) > crypto_pwhash_argon2id_PASSWD_MAX) { + *err_msg = "Password too long to be able to derive a key from it."; + return HSM_BAD_PASSWORD; + } /* Now derive the key. */ if (crypto_pwhash(key->data, sizeof(key->data), pass, strlen(pass), salt, @@ -23,10 +30,12 @@ char *hsm_secret_encryption_key(const char *pass, struct secret *key) * and SENSITIVE needs 1024. */ crypto_pwhash_argon2id_OPSLIMIT_MODERATE, crypto_pwhash_argon2id_MEMLIMIT_MODERATE, - crypto_pwhash_ALG_ARGON2ID13) != 0) - return "Could not derive a key from the password."; + crypto_pwhash_ALG_ARGON2ID13) != 0) { + *err_msg = "Could not derive a key from the password."; + return HSM_BAD_PASSWORD; + } - return NULL; + return 0; } bool encrypt_hsm_secret(const struct secret *encryption_key, @@ -90,7 +99,7 @@ static bool getline_stdin_pass(char **passwd, size_t *passwd_size) return true; } -char *read_stdin_pass(char **reason) +char *read_stdin_pass_with_exit_code(char **reason, int *exit_code) { struct termios current_term, temp_term; char *passwd = NULL; @@ -100,17 +109,20 @@ char *read_stdin_pass(char **reason) /* 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."; + *exit_code = HSM_PASSWORD_INPUT_ERR; return NULL; } temp_term = current_term; temp_term.c_lflag &= ~ECHO; if (tcsetattr(fileno(stdin), TCSANOW, &temp_term) != 0) { *reason = "Could not disable pass echoing."; + *exit_code = HSM_PASSWORD_INPUT_ERR; return NULL; } if (!getline_stdin_pass(&passwd, &passwd_size)) { *reason = "Could not read pass from stdin."; + *exit_code = HSM_PASSWORD_INPUT_ERR; return NULL; } @@ -118,12 +130,13 @@ char *read_stdin_pass(char **reason) if (tcsetattr(fileno(stdin), TCSANOW, ¤t_term) != 0) { *reason = "Could not restore terminal options."; free(passwd); + *exit_code = HSM_PASSWORD_INPUT_ERR; return NULL; } } else if (!getline_stdin_pass(&passwd, &passwd_size)) { *reason = "Could not read pass from stdin."; + *exit_code = HSM_PASSWORD_INPUT_ERR; return NULL; } - return passwd; } diff --git a/common/hsm_encryption.h b/common/hsm_encryption.h index 1cfa92e25909..1534cb4ff6b4 100644 --- a/common/hsm_encryption.h +++ b/common/hsm_encryption.h @@ -21,10 +21,12 @@ struct encrypted_hsm_secret { /** Derive the hsm_secret encryption key from a passphrase. * @pass: the passphrase string. * @encryption_key: the output key derived from the passphrase. + * @err_msg: if not NULL the error message contains the reason of the failure. * - * On success, NULL is returned. On error, a human-readable error is. + * On success, 0 is returned, on error a value > 0 is returned and it can be used as exit code. */ -char *hsm_secret_encryption_key(const char *pass, struct secret *encryption_key); +int hsm_secret_encryption_key_with_exitcode(const char *pass, struct secret *key, + char **err_msg); /** Encrypt the hsm_secret using a previously derived encryption key. * @encryption_key: the key derived from the passphrase. @@ -54,10 +56,11 @@ bool decrypt_hsm_secret(const struct secret *encryption_key, void discard_key(struct secret *key TAKES); /** Read hsm_secret encryption pass from stdin, disabling echoing. - * @reason: if NULL is returned, will point to the human-readable error. + * @reason: if NULL is returned, will point to the human-readable error, + * and the correct exit code is returned by the exit_code parameter. * * Caller must free the string as it does tal-reallocate getline's output. */ -char *read_stdin_pass(char **reason); +char *read_stdin_pass_with_exit_code(char **reason, int *exit_code); #endif /* LIGHTNING_COMMON_HSM_ENCRYPTION_H */ diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 323ebaba2ee9..4efd57b195e8 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -82,14 +83,14 @@ struct ext_key *hsm_init(struct lightningd *ld) /* We actually send requests synchronously: only status is async. */ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) - err(1, "Could not create hsm socketpair"); + err(HSM_GENERIC_ERROR, "Could not create hsm socketpair"); ld->hsm = new_global_subd(ld, "lightning_hsmd", hsmd_wire_name, hsm_msg, take(&fds[1]), NULL); if (!ld->hsm) - err(1, "Could not subd hsm"); + err(HSM_GENERIC_ERROR, "Could not subd hsm"); /* If hsm_secret is encrypted and the --encrypted-hsm startup option is * not passed, don't let hsmd use the first 32 bytes of the cypher as the @@ -98,7 +99,7 @@ struct ext_key *hsm_init(struct lightningd *ld) struct stat st; if (stat("hsm_secret", &st) == 0 && st.st_size == ENCRYPTED_HSM_SECRET_LEN) - errx(1, "hsm_secret is encrypted, you need to pass the " + errx(HSM_ERROR_IS_ENCRYPT, "hsm_secret is encrypted, you need to pass the " "--encrypted-hsm startup option."); } @@ -111,7 +112,7 @@ struct ext_key *hsm_init(struct lightningd *ld) IFDEV(ld->dev_force_bip32_seed, NULL), IFDEV(ld->dev_force_channel_secrets, NULL), IFDEV(ld->dev_force_channel_secrets_shaseed, NULL)))) - err(1, "Writing init msg to hsm"); + err(HSM_GENERIC_ERROR, "Writing init msg to hsm"); bip32_base = tal(ld, struct ext_key); msg = wire_sync_read(tmpctx, ld->hsm_fd); @@ -120,8 +121,8 @@ struct ext_key *hsm_init(struct lightningd *ld) &ld->bolt12_base, &ld->onion_reply_secret)) { if (ld->config.keypass) - errx(1, "Wrong password for encrypted hsm_secret."); - errx(1, "HSM did not give init reply"); + errx(HSM_BAD_PASSWORD, "Wrong password for encrypted hsm_secret."); + errx(HSM_GENERIC_ERROR, "HSM did not give init reply"); } return bip32_base; diff --git a/lightningd/options.c b/lightningd/options.c index c335425c3e38..146fbee7e248 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -26,6 +26,20 @@ #include #include +/* Unless overridden, we exit with status 1 when option parsing fails */ +static int opt_exitcode = 1; + +static void opt_log_stderr_exitcode(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(opt_exitcode); +} + /* Declare opt_add_addr here, because we we call opt_add_addr * and opt_announce_addr vice versa */ @@ -461,7 +475,7 @@ static char *opt_important_plugin(const char *arg, struct lightningd *ld) */ static char *opt_set_hsm_password(struct lightningd *ld) { - char *passwd, *passwd_confirmation, *err; + char *passwd, *passwd_confirmation, *err_msg; printf("The hsm_secret is encrypted with a password. In order to " "decrypt it and start the node you must provide the password.\n"); @@ -469,20 +483,23 @@ static char *opt_set_hsm_password(struct lightningd *ld) /* If we don't flush we might end up being buffered and we might seem * to hang while we wait for the password. */ fflush(stdout); - passwd = read_stdin_pass(&err); + + passwd = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); if (!passwd) - return err; + return err_msg; printf("Confirm hsm_secret password:\n"); fflush(stdout); - passwd_confirmation = read_stdin_pass(&err); + passwd_confirmation = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); if (!passwd_confirmation) - return err; + return err_msg; printf("\n"); ld->config.keypass = tal(NULL, struct secret); - err = hsm_secret_encryption_key(passwd, ld->config.keypass); - if (err) - return err; + + opt_exitcode = hsm_secret_encryption_key_with_exitcode(passwd, ld->config.keypass, &err_msg); + if (opt_exitcode > 0) + return err_msg; + ld->encrypted_hsm = true; free(passwd); free(passwd_confirmation); @@ -1087,8 +1104,8 @@ static void register_opts(struct lightningd *ld) opt_hidden); opt_register_noarg("--encrypted-hsm", opt_set_hsm_password, ld, - "Set the password to encrypt hsm_secret with. If no password is passed through command line, " - "you will be prompted to enter it."); + "Set the password to encrypt hsm_secret with. If no password is passed through command line, " + "you will be prompted to enter it."); opt_register_arg("--rpc-file-mode", &opt_set_mode, &opt_show_mode, &ld->rpc_filemode, @@ -1315,10 +1332,9 @@ void handle_opts(struct lightningd *ld, int argc, char *argv[]) parse_config_files(ld->config_filename, ld->config_basedir, false); /* Now parse cmdline, which overrides config. */ - opt_parse(&argc, argv, opt_log_stderr_exit); + opt_parse(&argc, argv, opt_log_stderr_exitcode); if (argc != 1) errx(1, "no arguments accepted"); - /* We keep a separate variable rather than overriding always_use_proxy, * so listconfigs shows the correct thing. */ if (tal_count(ld->proposed_wireaddr) != 0 diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 5264a7093c92..695c8b37c40a 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -17,6 +17,11 @@ WAIT_TIMEOUT = 60 # Wait timeout for processes +# Errors codes +HSM_GENERIC_ERROR = 20 +HSM_ERROR_IS_ENCRYPT = 21 +HSM_BAD_PASSWORD = 22 + @unittest.skipIf(TEST_NETWORK != 'regtest', "Test relies on a number of example addresses valid only in regtest") def test_withdraw(node_factory, bitcoind): @@ -1018,7 +1023,7 @@ def test_hsm_secret_encryption(node_factory): # Test we cannot start the same wallet without specifying --encrypted-hsm l1.daemon.opts.pop("encrypted-hsm") - with pytest.raises(subprocess.CalledProcessError, match=r'returned non-zero exit status 1'): + with pytest.raises(subprocess.CalledProcessError, match=r'returned non-zero exit status {}'.format(HSM_ERROR_IS_ENCRYPT)): subprocess.check_call(l1.daemon.cmd_line) # Test we cannot restore the same wallet with another password @@ -1029,7 +1034,7 @@ def test_hsm_secret_encryption(node_factory): write_all(master_fd, password[2:].encode("utf-8")) l1.daemon.wait_for_log(r'Confirm hsm_secret password') write_all(master_fd, password[2:].encode("utf-8")) - assert(l1.daemon.proc.wait(WAIT_TIMEOUT) == 1) + assert(l1.daemon.proc.wait(WAIT_TIMEOUT) == HSM_BAD_PASSWORD) assert(l1.daemon.is_in_log("Wrong password for encrypted hsm_secret.")) # Test we can restore the same wallet with the same password @@ -1097,6 +1102,7 @@ def test_hsmtool_secret_decryption(node_factory): hsmtool.wait_for_log(r"Enter hsm_secret password:") write_all(master_fd, password.encode("utf-8")) assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 + # Then test we can now start it without password l1.daemon.opts.pop("encrypted-hsm") l1.daemon.start(stdin=slave_fd, wait_for_initialized=True) @@ -1115,7 +1121,7 @@ def test_hsmtool_secret_decryption(node_factory): assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 # Now we need to pass the encrypted-hsm startup option l1.stop() - with pytest.raises(subprocess.CalledProcessError, match=r'returned non-zero exit status 1'): + with pytest.raises(subprocess.CalledProcessError, match=r'returned non-zero exit status {}'.format(HSM_ERROR_IS_ENCRYPT)): subprocess.check_call(l1.daemon.cmd_line) l1.daemon.opts.update({"encrypted-hsm": None}) diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 5f5457a2e6d1..f6a426443f9a 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -90,6 +90,7 @@ static void get_encrypted_hsm_secret(struct secret *hsm_secret, struct secret key; struct encrypted_hsm_secret encrypted_secret; char *err; + int exit_code = 0; fd = open(hsm_secret_path, O_RDONLY); if (fd < 0) @@ -98,9 +99,9 @@ static void get_encrypted_hsm_secret(struct secret *hsm_secret, if (!read_all(fd, encrypted_secret.data, ENCRYPTED_HSM_SECRET_LEN)) errx(ERROR_HSM_FILE, "Could not read encrypted hsm_secret"); - err = hsm_secret_encryption_key(passwd, &key); - if (err) - errx(ERROR_LIBSODIUM, "%s", err); + exit_code = hsm_secret_encryption_key_with_exitcode(passwd, &key, &err); + if (exit_code > 0) + errx(exit_code, "%s", err); if (!decrypt_hsm_secret(&key, &encrypted_secret, hsm_secret)) errx(ERROR_LIBSODIUM, "Could not retrieve the seed. Wrong password ?"); @@ -164,15 +165,15 @@ static int decrypt_hsm(const char *hsm_secret_path) struct secret hsm_secret; char *passwd, *err; const char *dir, *backup; - + int exit_code = 0; /* This checks the file existence, too. */ if (!hsm_secret_is_encrypted(hsm_secret_path)) errx(ERROR_USAGE, "hsm_secret is not encrypted"); printf("Enter hsm_secret password:\n"); fflush(stdout); - passwd = read_stdin_pass(&err); + passwd = read_stdin_pass_with_exit_code(&err, &exit_code); if (!passwd) - errx(ERROR_TERM, "%s", err); + errx(exit_code, "%s", err); if (sodium_init() == -1) errx(ERROR_LIBSODIUM, @@ -221,6 +222,7 @@ static int encrypt_hsm(const char *hsm_secret_path) struct encrypted_hsm_secret encrypted_hsm_secret; char *passwd, *passwd_confirmation, *err; const char *dir, *backup; + int exit_code = 0; /* This checks the file existence, too. */ if (hsm_secret_is_encrypted(hsm_secret_path)) @@ -228,14 +230,14 @@ static int encrypt_hsm(const char *hsm_secret_path) printf("Enter hsm_secret password:\n"); fflush(stdout); - passwd = read_stdin_pass(&err); + passwd = read_stdin_pass_with_exit_code(&err, &exit_code); if (!passwd) - errx(ERROR_TERM, "%s", err); + errx(exit_code, "%s", err); printf("Confirm hsm_secret password:\n"); fflush(stdout); - passwd_confirmation = read_stdin_pass(&err); + passwd_confirmation = read_stdin_pass_with_exit_code(&err, &exit_code); if (!passwd_confirmation) - errx(ERROR_TERM, "%s", err); + errx(exit_code, "%s", err); if (!streq(passwd, passwd_confirmation)) errx(ERROR_USAGE, "Passwords confirmation mismatch."); get_hsm_secret(&hsm_secret, hsm_secret_path); @@ -249,9 +251,9 @@ static int encrypt_hsm(const char *hsm_secret_path) /* Derive the encryption key from the password provided, and try to encrypt * the seed. */ - err = hsm_secret_encryption_key(passwd, &key); - if (err) - errx(ERROR_LIBSODIUM, "%s", err); + exit_code = hsm_secret_encryption_key_with_exitcode(passwd, &key, &err); + if (exit_code > 0) + errx(exit_code, "%s", err); if (!encrypt_hsm_secret(&key, &hsm_secret, &encrypted_hsm_secret)) errx(ERROR_LIBSODIUM, "Could not encrypt the hsm_secret seed."); @@ -295,6 +297,7 @@ static int dump_commitments_infos(struct node_id *node_id, u64 channel_id, struct secret hsm_secret, channel_seed, per_commitment_secret; struct pubkey per_commitment_point; char *passwd, *err; + int exit_code = 0; secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); @@ -303,9 +306,9 @@ static int dump_commitments_infos(struct node_id *node_id, u64 channel_id, if (hsm_secret_is_encrypted(hsm_secret_path)) { printf("Enter hsm_secret password:\n"); fflush(stdout); - passwd = read_stdin_pass(&err); + passwd = read_stdin_pass_with_exit_code(&err, &exit_code); if (!passwd) - errx(ERROR_TERM, "%s", err); + errx(exit_code, "%s", err); get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); free(passwd); } else @@ -357,7 +360,7 @@ static int guess_to_remote(const char *address, struct node_id *node_id, u8 goal_pubkeyhash[20]; /* See common/bech32.h for buffer size. */ char hrp[strlen(address) - 6]; - int witver; + int witver, exit_code = 0; size_t witlen; /* Get the hrp to accept addresses from any network. */ @@ -373,9 +376,9 @@ static int guess_to_remote(const char *address, struct node_id *node_id, if (hsm_secret_is_encrypted(hsm_secret_path)) { printf("Enter hsm_secret password:\n"); fflush(stdout); - passwd = read_stdin_pass(&err); + passwd = read_stdin_pass_with_exit_code(&err, &exit_code); if (!passwd) - errx(ERROR_TERM, "%s", err); + errx(exit_code, "%s", err); get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); free(passwd); } else @@ -478,6 +481,7 @@ static int generate_hsm(const char *hsm_secret_path) { char mnemonic[BIP39_WORDLIST_LEN]; char *passphrase, *err; + int exit_code = 0; read_mnemonic(mnemonic); printf("Warning: remember that different passphrases yield different " @@ -485,9 +489,9 @@ static int generate_hsm(const char *hsm_secret_path) printf("If left empty, no password is used (echo is disabled).\n"); printf("Enter your passphrase: \n"); fflush(stdout); - passphrase = read_stdin_pass(&err); + passphrase = read_stdin_pass_with_exit_code(&err, &exit_code); if (!passphrase) - errx(ERROR_TERM, "%s", err); + errx(exit_code, "%s", err); if (strlen(passphrase) == 0) { free(passphrase); passphrase = NULL; @@ -534,14 +538,15 @@ static int dumponchaindescriptors(const char *hsm_secret_path, const char *old_p struct ext_key master_extkey; char *enc_xpub, *descriptor; struct descriptor_checksum checksum; + int exit_code = 0; /* This checks the file existence, too. */ if (hsm_secret_is_encrypted(hsm_secret_path)) { printf("Enter hsm_secret password:\n"); fflush(stdout); - passwd = read_stdin_pass(&err); + passwd = read_stdin_pass_with_exit_code(&err, &exit_code); if (!passwd) - errx(ERROR_TERM, "%s", err); + errx(exit_code, "%s", err); get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); free(passwd); } else From 5bbe3feee77b039cdb83c11420d36edbd4c5cc26 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 14 Dec 2021 22:58:01 +0100 Subject: [PATCH 0145/1530] doc: document the errors code Signed-off-by: Vincenzo Palazzo --- doc/lightningd.8.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/lightningd.8.md b/doc/lightningd.8.md index fa7b70244c90..2f3a68a7fbd0 100644 --- a/doc/lightningd.8.md +++ b/doc/lightningd.8.md @@ -152,6 +152,16 @@ merchant, and use lightning-pay(7) to pay it: $ lightning-cli pay $INVOICE +ERRORS CODE +--- + +- 1: Generic lightning-cli error +- 20: Generic error related to HSM secret +- 21: HSM secret is encrypted +- 22: Bad password used to decrypt the HSM secred +- 23: Error caused from the I/O operation during a HSM decryption/encryption operation + + BUGS ---- @@ -184,4 +194,3 @@ COPYING Note: the modules in the ccan/ directory have their own licenses, but the rest of the code is covered by the BSD-style MIT license. - From 0c487a61ba2d6e45d94bd7037173772c1c3b397c Mon Sep 17 00:00:00 2001 From: Anand Suresh Date: Wed, 15 Dec 2021 21:18:54 -0800 Subject: [PATCH 0146/1530] Fix comment to reflect the code The comment mentions a 100MB log book, but the code was modified 15 months ago to allocate a 10MB log book to preserve memory. --- lightningd/lightningd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index f406e68d04b9..9c0d120a6928 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -170,7 +170,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * in limbo until we get all the parts, or we time them out. */ htlc_set_map_init(&ld->htlc_sets); - /*~ We have a multi-entry log-book infrastructure: we define a 100MB log + /*~ We have a multi-entry log-book infrastructure: we define a 10MB log * book to hold all the entries (and trims as necessary), and multiple * log objects which each can write into it, each with a unique * prefix. */ From 6cee603f71ae70f9bc078bc6c0209ca755fea50a Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Sat, 18 Dec 2021 13:44:29 -0500 Subject: [PATCH 0147/1530] Add pip requirements install to Mac instructions --- doc/INSTALL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index bec63e75df54..836785eb2a43 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -264,6 +264,7 @@ Configure Python 3.x & get mako: Build lightning: + $ pip install -r requirements.txt $ ./configure $ make From 9b1a0f9b7bb8752a770eab3c52b50f5e8c4a308b Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 10 Nov 2021 15:43:23 -0600 Subject: [PATCH 0148/1530] coin-moves: remove penalty + chain fee types FIXME: still has coin tracking stuff! --- common/coin_mvt.c | 6 ++---- common/coin_mvt.h | 4 ++-- doc/PLUGINS.md | 5 +---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 537c1231f485..8851202b1b80 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -16,14 +16,12 @@ const char *mvt_type_str(enum mvt_type type) static const char *mvt_tags[] = { "deposit", "withdrawal", - "chain_fees", "penalty", "invoice", "routed", "journal_entry", "onchain_htlc", "pushed", - "spend_track", }; const char *mvt_tag_str(enum mvt_tag tag) { @@ -153,7 +151,7 @@ struct chain_coin_mvt *new_coin_chain_fees(const tal_t *ctx, { return new_chain_coin_mvt(ctx, account_name, tx_txid, NULL, NULL, blockheight, - CHAIN_FEES, amount, false); + 0, amount, false); } struct chain_coin_mvt *new_coin_chain_fees_sat(const tal_t *ctx, @@ -265,7 +263,7 @@ struct chain_coin_mvt *new_coin_spend_track(const tal_t *ctx, u32 blockheight) { return new_chain_coin_mvt_sat(ctx, "wallet", txid, outpoint, - NULL, blockheight, SPEND_TRACK, AMOUNT_SAT(0), + NULL, blockheight, 0, AMOUNT_SAT(0), false); } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 7c406c0f09ea..8225fb752937 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -17,14 +17,14 @@ enum mvt_type { enum mvt_tag { DEPOSIT = 0, WITHDRAWAL = 1, - CHAIN_FEES = 2, + /* 2, CHAIN_FEES has been removed */ PENALTY = 3, INVOICE = 4, ROUTED = 5, JOURNAL = 6, ONCHAIN_HTLC = 7, PUSHED = 8, - SPEND_TRACK = 9, + /* 9, SPEND_TRACK has been removed */ }; struct channel_coin_mvt { diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 07e04537494a..bb54cf95277f 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -734,8 +734,7 @@ all channel funds' account are the channel id. `txid` is the transaction id of the bitcoin transaction that triggered this ledger event. `utxo_txid` and `vout` identify the bitcoin output which triggered -this notification. (`chain_mvt` only) In most cases, the `utxo_txid` will be the -same as the `txid`, except for `spend_track` notficiations. Notifications tagged +this notification. (`chain_mvt` only). Notifications tagged `chain_fees` and `journal_entry` do not have a `utxo_txid` as they're not represented in the utxo set. @@ -755,7 +754,6 @@ multiple times. `channel_mvt` only `tag` is a movement descriptor. Current tags are as follows: - `deposit`: funds deposited - `withdrawal`: funds withdrawn - - `chain_fees`: funds paid for onchain fees. `chain_mvt` only - `penalty`: funds paid or gained from a penalty tx. `chain_mvt` only - `invoice`: funds paid to or recieved from an invoice. `channel_mvt` only - `routed`: funds routed through this node. `channel_mvt` only @@ -763,7 +761,6 @@ multiple times. `channel_mvt` only by a penalty tx onchain. `chain_mvt` only - `onchain_htlc`: funds moved via an htlc onchain. `chain_mvt` only - `pushed`: funds pushed to peer. `channel_mvt` only. - - `spend_track`: informational notification about a wallet utxo spend. `chain_mvt` only. `blockheight` is the block the txid is included in. `chain_mvt` only. In the case that an output is considered dust, c-lightning does not track its return to From e791efe23b02a9b0ae096dabbfb7061eab9b1183 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 10 Nov 2021 15:54:56 -0600 Subject: [PATCH 0149/1530] coin-mvt: remove all the spend-tracking We're not going to do 'spend tracks' any more; instead we'll emit an event whenever an output is included in a broadcast tx (even if the broadcast fails!!) --- common/coin_mvt.c | 10 --- common/coin_mvt.h | 4 -- lightningd/chaintopology.c | 128 +++++-------------------------------- 3 files changed, 17 insertions(+), 125 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 8851202b1b80..9de0cc907e85 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -257,16 +257,6 @@ struct chain_coin_mvt *new_coin_pushed(const tal_t *ctx, false); } -struct chain_coin_mvt *new_coin_spend_track(const tal_t *ctx, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight) -{ - return new_chain_coin_mvt_sat(ctx, "wallet", txid, outpoint, - NULL, blockheight, 0, AMOUNT_SAT(0), - false); -} - struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, const char *bip173_name, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 8225fb752937..944bc80884f2 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -170,10 +170,6 @@ struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx, u32 blockheight, struct amount_sat amount, bool is_credit); -struct chain_coin_mvt *new_coin_spend_track(const tal_t *ctx, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight); struct chain_coin_mvt *new_coin_pushed(const tal_t *ctx, const char *account_name, const struct bitcoin_txid *txid, diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index c7070c70caae..ed0e184d5428 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -652,102 +652,25 @@ static void updates_complete(struct chain_topology *topo) next_topology_timer(topo); } -static void record_utxo_spent(struct lightningd *ld, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat *input_amt) +static void record_wallet_spend(struct lightningd *ld, + struct bitcoin_outpoint *outpoint, + struct bitcoin_txid *txid, + u32 tx_blockheight) { struct utxo *utxo; - struct chain_coin_mvt *mvt; - u8 *ctx = tal(NULL, u8); - utxo = wallet_utxo_get(ctx, ld->wallet, outpoint); + /* Find the amount this was for */ + utxo = wallet_utxo_get(tmpctx, ld->wallet, outpoint); if (!utxo) { log_broken(ld->log, "No record of utxo %s", - type_to_string(tmpctx, struct bitcoin_outpoint, - outpoint)); + type_to_string(tmpctx, struct bitcoin_outpoint, + outpoint)); return; } - *input_amt = utxo->amount; - mvt = new_coin_spend_track(ctx, txid, outpoint, blockheight); - notify_chain_mvt(ld, mvt); - tal_free(ctx); -} - -static void record_outputs_as_withdraws(const tal_t *ctx, - struct lightningd *ld, - const struct bitcoin_tx *tx, - const struct bitcoin_txid *txid, - u32 blockheight) -{ - struct chain_coin_mvt *mvt; - struct bitcoin_outpoint outpoint; - - outpoint.txid = *txid; - for (outpoint.n = 0; outpoint.n < tx->wtx->num_outputs; outpoint.n++) { - struct amount_asset asset; - struct amount_sat outval; - if (elements_tx_output_is_fee(tx, outpoint.n)) - continue; - asset = bitcoin_tx_output_get_amount(tx, outpoint.n); - assert(amount_asset_is_main(&asset)); - outval = amount_asset_to_sat(&asset); - mvt = new_coin_withdrawal_sat(ctx, "wallet", txid, - &outpoint, blockheight, - outval); - notify_chain_mvt(ld, mvt); - } -} - -static void record_tx_outs_and_fees(struct lightningd *ld, - const struct bitcoin_tx *tx, - const struct bitcoin_txid *txid, - u32 blockheight, - struct amount_sat inputs_total, - bool our_tx) -{ - struct amount_sat fee, out_val; - struct chain_coin_mvt *mvt; - bool ok; - struct wally_psbt *psbt = NULL; - u8 *ctx = tal(NULL, u8); - - /* We own every input on this tx, so track withdrawals precisely */ - if (our_tx) { - record_outputs_as_withdraws(ctx, ld, tx, txid, blockheight); - fee = bitcoin_tx_compute_fee_w_inputs(tx, inputs_total); - goto log_fee; - } - - /* FIXME: look up stashed psbt! */ - if (!psbt) { - fee = bitcoin_tx_compute_fee_w_inputs(tx, inputs_total); - ok = amount_sat_sub(&out_val, inputs_total, fee); - assert(ok); - - /* We don't have detailed withdrawal info for this tx, - * so we log the wallet withdrawal as a single entry */ - mvt = new_coin_withdrawal_sat(ctx, "wallet", txid, NULL, - blockheight, out_val); - notify_chain_mvt(ld, mvt); - goto log_fee; - } - - fee = AMOUNT_SAT(0); - - /* Note that to figure out the *total* 'onchain' - * cost of a channel, you'll want to also include - * fees logged here, to the 'wallet' account (for funding tx). - * You can do this in post by accounting for any 'chain_fees' logged for - * the funding txid when looking at a channel. */ -log_fee: - notify_chain_mvt(ld, - new_coin_chain_fees_sat(ctx, "wallet", txid, - blockheight, fee)); - - tal_free(ctx); + notify_chain_mvt(ld, new_coin_wallet_withdraw(tmpctx, txid, outpoint, + tx_blockheight, + utxo->amount, WITHDRAWAL)); } /** @@ -758,37 +681,20 @@ static void topo_update_spends(struct chain_topology *topo, struct block *b) const struct short_channel_id *spent_scids; for (size_t i = 0; i < tal_count(b->full_txs); i++) { const struct bitcoin_tx *tx = b->full_txs[i]; - bool our_tx = true, includes_our_spend = false; - struct bitcoin_txid txid; - struct amount_sat inputs_total = AMOUNT_SAT(0); - - txid = b->txids[i]; for (size_t j = 0; j < tx->wtx->num_inputs; j++) { struct bitcoin_outpoint outpoint; - bool our_spend; bitcoin_tx_input_get_outpoint(tx, j, &outpoint); - our_spend = wallet_outpoint_spend( - topo->ld->wallet, tmpctx, b->height, &outpoint); - our_tx &= our_spend; - includes_our_spend |= our_spend; - if (our_spend) { - struct amount_sat input_amt; - bool ok; - - record_utxo_spent(topo->ld, &txid, &outpoint, - b->height, &input_amt); - ok = amount_sat_add(&inputs_total, inputs_total, input_amt); - assert(ok); - } - } + if (wallet_outpoint_spend(topo->ld->wallet, tmpctx, + b->height, &outpoint)) + record_wallet_spend(topo->ld, &outpoint, + &b->txids[i], b->height); - if (includes_our_spend) - record_tx_outs_and_fees(topo->ld, tx, &txid, - b->height, inputs_total, our_tx); + } } + /* Retrieve all potential channel closes from the UTXO set and * tell gossipd about them. */ spent_scids = From 737772f1cad517ded6ddfac87c84fed18c883f96 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 10 Nov 2021 16:05:57 -0600 Subject: [PATCH 0150/1530] coin-mvt: remove all the chain fees tracking this will be impllicit going forward. --- common/coin_mvt.c | 27 ---- common/coin_mvt.h | 10 -- doc/PLUGINS.md | 2 +- onchaind/onchaind.c | 180 ++------------------------ onchaind/test/run-grind_feerate-bug.c | 14 -- onchaind/test/run-grind_feerate.c | 14 -- 6 files changed, 12 insertions(+), 235 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 9de0cc907e85..b96e62ea9788 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -143,33 +143,6 @@ struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx, blockheight, amt_msat); } -struct chain_coin_mvt *new_coin_chain_fees(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - u32 blockheight, - struct amount_msat amount) -{ - return new_chain_coin_mvt(ctx, account_name, tx_txid, - NULL, NULL, blockheight, - 0, amount, false); -} - -struct chain_coin_mvt *new_coin_chain_fees_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - u32 blockheight, - struct amount_sat amount) -{ - struct amount_msat amt_msat; - bool ok; - - ok = amount_sat_to_msat(&amt_msat, amount); - assert(ok); - - return new_coin_chain_fees(ctx, account_name, tx_txid, - blockheight, amt_msat); -} - struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx, const char *account_name, const struct bitcoin_txid *txid, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 944bc80884f2..7049259d736d 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -128,16 +128,6 @@ struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount); -struct chain_coin_mvt *new_coin_chain_fees(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - u32 blockheight, - struct amount_msat amount); -struct chain_coin_mvt *new_coin_chain_fees_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - u32 blockheight, - struct amount_sat amount); struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx, const char *account_name, const struct bitcoin_txid *txid, diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index bb54cf95277f..b88d6140b595 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -735,7 +735,7 @@ all channel funds' account are the channel id. `txid` is the transaction id of the bitcoin transaction that triggered this ledger event. `utxo_txid` and `vout` identify the bitcoin output which triggered this notification. (`chain_mvt` only). Notifications tagged -`chain_fees` and `journal_entry` do not have a `utxo_txid` as they're not +`journal_entry` do not have a `utxo_txid` as they're not represented in the utxo set. `payment_hash` is the hash of the preimage used to move this payment. Only diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 47fa0d3d58a5..461fd0219c38 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -225,39 +225,6 @@ static void record_htlc_fulfilled(const struct bitcoin_txid *txid, send_coin_mvt(take(mvt)); } -static void update_ledger_chain_fees_msat(const struct bitcoin_txid *txid, - u32 blockheight, - struct amount_msat fees) -{ - send_coin_mvt(take(new_coin_chain_fees(NULL, NULL, txid, - blockheight, fees))); -} - -static void update_ledger_chain_fees(const struct bitcoin_txid *txid, - u32 blockheight, - struct amount_sat fees) -{ - struct chain_coin_mvt *mvt; - mvt = new_coin_chain_fees_sat(NULL, NULL, txid, blockheight, fees); - - send_coin_mvt(take(mvt)); -} - -/* Log the fees paid on this transaction as 'chain fees'. note that - * you *cannot* pass a chaintopology-originated tx to this method, - * as they don't have input amounts populated */ -static struct amount_sat record_chain_fees_tx(const struct bitcoin_txid *txid, - const struct bitcoin_tx *tx, - u32 blockheight) -{ - struct amount_sat fees; - fees = bitcoin_tx_compute_fee(tx); - status_debug("recording chain fees for tx %s", - type_to_string(tmpctx, struct bitcoin_txid, txid)); - update_ledger_chain_fees(txid, blockheight, fees); - return fees; -} - static void add_amt(struct amount_sat *sum, struct amount_sat amt) { if (!amount_sat_add(sum, *sum, amt)) @@ -273,7 +240,7 @@ static void record_mutual_closure(const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat our_out) { - struct amount_msat chain_fees, output_msat; + struct amount_msat output_msat; /* First figure out 'fees' we paid on this will include * - 'residue' that can't fit onchain (< 1 sat) @@ -286,17 +253,6 @@ static void record_mutual_closure(const struct bitcoin_outpoint *outpoint, type_to_string(tmpctx, struct amount_sat, &our_out)); - if (!amount_msat_sub(&chain_fees, our_msat, output_msat)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to subtract %s from %s", - type_to_string(tmpctx, struct amount_msat, - &output_msat), - type_to_string(tmpctx, struct amount_msat, - &our_msat)); - - if (!amount_msat_eq(AMOUNT_MSAT(0), chain_fees)) - update_ledger_chain_fees_msat(&outpoint->txid, blockheight, chain_fees); - /* If we have no output, we exit early */ if (amount_msat_eq(AMOUNT_MSAT(0), output_msat)) return; @@ -308,64 +264,6 @@ static void record_mutual_closure(const struct bitcoin_outpoint *outpoint, blockheight, output_msat))); } -static void record_chain_fees_unilateral(const struct bitcoin_txid *txid, - u32 blockheight, - struct amount_sat funding, - struct amount_sat their_outs, - struct amount_sat our_outs) -{ - struct amount_msat trimmed; - status_debug("chain_movements...recording chain fees for unilateral." - " our msat balance %s, funding %s," - " their_outs %s, our outs %s", - type_to_string(tmpctx, struct amount_msat, &our_msat), - type_to_string(tmpctx, struct amount_sat, &funding), - type_to_string(tmpctx, struct amount_sat, &their_outs), - type_to_string(tmpctx, struct amount_sat, &our_outs)); - - /* It's possible they published a commitment tx that - * paid us an htlc before we updated our balance. It's also - * possible that they fulfilled an htlc, but we'll just write - * that down in the chain fees :/ */ - if (!amount_msat_greater_eq_sat(our_msat, our_outs)) { - struct amount_msat missing; - - if (!amount_sat_sub_msat(&missing, our_outs, our_msat)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to subtract %s from %s", - type_to_string(tmpctx, struct amount_msat, - &our_msat), - type_to_string(tmpctx, struct amount_sat, - &our_outs)); - - /* Log the difference and update our_msat */ - send_coin_mvt(take(new_coin_journal_entry(NULL, NULL, txid, - NULL, blockheight, - missing, true))); - if (!amount_msat_add(&our_msat, our_msat, missing)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to add %s to %s", - type_to_string(tmpctx, struct amount_msat, - &missing), - type_to_string(tmpctx, struct amount_msat, - &our_msat)); - } - - /* we need to figure out what we paid in fees, total. - * this encompasses the actual chain fees + any trimmed outputs */ - if (!amount_msat_sub_sat(&trimmed, our_msat, our_outs)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to subtract %s from %s", - type_to_string(tmpctx, struct amount_sat, - &our_outs), - type_to_string(tmpctx, struct amount_msat, - &our_msat)); - - status_debug("logging 'chain fees' for unilateral (trimmed) %s", - type_to_string(tmpctx, struct amount_msat, &trimmed)); - update_ledger_chain_fees_msat(txid, blockheight, trimmed); -} - static void record_coin_loss(const struct bitcoin_txid *txid, u32 blockheight, struct tracked_output *out) @@ -379,32 +277,13 @@ static void record_coin_loss(const struct bitcoin_txid *txid, send_coin_mvt(take(mvt)); } -static void record_channel_withdrawal_minus_fees(const struct bitcoin_txid *tx_txid, - struct tracked_output *out, - u32 blockheight, - struct amount_sat fees) +static void record_channel_withdrawal(const struct bitcoin_txid *tx_txid, + struct tracked_output *out, + u32 blockheight) { - struct amount_sat emitted_amt; - - if (!amount_sat_sub(&emitted_amt, out->sat, fees)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to subtract %s from %s", - type_to_string(tmpctx, struct amount_sat, - &fees), - type_to_string(tmpctx, struct amount_sat, - &out->sat)); - send_coin_mvt(take(new_coin_withdrawal_sat( NULL, NULL, tx_txid, &out->outpoint, - blockheight, emitted_amt))); -} - - -static void record_channel_withdrawal(const struct bitcoin_txid *tx_txid, - u32 blockheight, - struct tracked_output *out) -{ - record_channel_withdrawal_minus_fees(tx_txid, out, blockheight, AMOUNT_SAT(0)); + blockheight, out->sat))); } static bool is_our_htlc_tx(struct tracked_output *out) @@ -426,23 +305,17 @@ static void record_coin_movements(struct tracked_output *out, const struct bitcoin_tx *tx, const struct bitcoin_txid *txid) { - struct amount_sat fees; /* there is a case where we've fulfilled an htlc onchain, * in which case we log a deposit to the channel */ if (is_channel_deposit(out)) record_htlc_fulfilled(txid, out, blockheight, true); - /* record fees paid for the tx here */ - /* FIXME: for now, every resolution generates its own tx, - * this will need to be updated if we switch to batching */ - fees = record_chain_fees_tx(txid, tx, blockheight); - /* we don't record a channel withdrawal until we get to * the 'exit' utxo, which for local commitment htlc txs * is the child htlc_tx's output */ if (!is_our_htlc_tx(out)) - record_channel_withdrawal_minus_fees(txid, out, blockheight, fees); + record_channel_withdrawal(txid, out, blockheight); } /* We vary feerate until signature they offered matches. */ @@ -1218,7 +1091,6 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay) /* Don't wait for this if we're ignoring the tiny payment. */ if (out->proposal->tx_type == IGNORING_TINY_PAYMENT) { struct bitcoin_txid txid; - struct amount_sat fees; ignore_output(out); @@ -1226,8 +1098,7 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay) /* log the coin movements here, since we're not * going to wait til we hear about it */ bitcoin_txid(out->proposal->tx, &txid); - fees = record_chain_fees_tx(&txid, out->proposal->tx, 0); - record_channel_withdrawal_minus_fees(&txid, out, 0, fees); + record_channel_withdrawal(&txid, out, 0); } } @@ -1715,7 +1586,7 @@ static void steal_htlc_tx(struct tracked_output *out, enum tx_type tx_type = OUR_PENALTY_TX; struct tracked_output *htlc_out; struct amount_asset asset; - struct amount_sat htlc_out_amt, fees; + struct amount_sat htlc_out_amt; u8 *wscript = bitcoin_wscript_htlc_tx(htlc_tx, to_self_delay[REMOTE], &keyset->self_revocation_key, @@ -1745,24 +1616,6 @@ static void steal_htlc_tx(struct tracked_output *out, /* mark commitment tx htlc output as 'resolved by them' */ resolved_by_other(out, &htlc_tx->txid, htlc_tx_type); - /* for penalties, we record *any* chain fees - * paid as coming from our channel balance, so - * that our balance ends up at zero */ - if (!amount_sat_sub(&fees, out->sat, htlc_out->sat)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to subtract %s from %s", - type_to_string(tmpctx, struct amount_sat, - &htlc_out->sat), - type_to_string(tmpctx, struct amount_sat, - &out->sat)); - - status_debug("recording chain fees for peer's htlc tx, that we're about to steal" - " the output of. fees: %s", - type_to_string(tmpctx, struct amount_sat, &fees)); - - if (!is_replay) - update_ledger_chain_fees(&htlc_tx->txid, htlc_tx_blockheight, fees); - /* annnd done! */ propose_resolution(htlc_out, tx, 0, tx_type, is_replay); } @@ -3023,10 +2876,6 @@ static void handle_our_unilateral(const struct tx_parts *tx, note_missing_htlcs(htlc_scripts, htlcs_info); tal_free(htlcs_info); - if (!is_replay) - record_chain_fees_unilateral(&tx->txid, tx_blockheight, - outs[0]->sat, - their_outs, our_outs); wait_for_resolved(outs); } @@ -3169,7 +3018,7 @@ static void their_unilateral_local(struct tracked_output ***outs, ignore_output(out); if (!is_replay) - record_channel_withdrawal(&tx->txid, tx_blockheight, out); + record_channel_withdrawal(&tx->txid, out, tx_blockheight); tell_wallet_to_remote(tx, outpoint, tx_blockheight, @@ -3536,9 +3385,6 @@ static void handle_their_cheat(const struct tx_parts *tx, status_debug("recording chain fees for their cheat %s", type_to_string(tmpctx, struct amount_sat, &fee_cost)); - if (!is_replay) - update_ledger_chain_fees(&tx->txid, tx_blockheight, fee_cost); - wait_for_resolved(outs); } @@ -3870,11 +3716,6 @@ static void handle_their_unilateral(const struct tx_parts *tx, note_missing_htlcs(htlc_scripts, htlcs_info); tal_free(htlcs_info); - if (!is_replay) - record_chain_fees_unilateral(&tx->txid, tx_blockheight, - outs[0]->sat, - their_outs, our_outs); - wait_for_resolved(outs); } @@ -3999,7 +3840,8 @@ static void handle_unknown_commitment(const struct tx_parts *tx, ignore_output(out); if (!is_replay) - record_channel_withdrawal(&tx->txid, tx_blockheight, out); + record_channel_withdrawal(&tx->txid, out, + tx_blockheight); add_amt(&amt_salvaged, amt); diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index cdcc6bbd8054..0ca11adadf90 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -113,20 +113,6 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx UNNEEDED, /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } -/* Generated stub for new_coin_chain_fees */ -struct chain_coin_mvt *new_coin_chain_fees(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_msat amount UNNEEDED) -{ fprintf(stderr, "new_coin_chain_fees called!\n"); abort(); } -/* Generated stub for new_coin_chain_fees_sat */ -struct chain_coin_mvt *new_coin_chain_fees_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED) -{ fprintf(stderr, "new_coin_chain_fees_sat called!\n"); abort(); } /* Generated stub for new_coin_journal_entry */ struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx UNNEEDED, const char *account_name UNNEEDED, diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index a59812b5d67c..5924edea56ef 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -136,20 +136,6 @@ struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, void memleak_remove_region(struct htable *memtable UNNEEDED, const void *p UNNEEDED, size_t bytelen UNNEEDED) { fprintf(stderr, "memleak_remove_region called!\n"); abort(); } -/* Generated stub for new_coin_chain_fees */ -struct chain_coin_mvt *new_coin_chain_fees(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_msat amount UNNEEDED) -{ fprintf(stderr, "new_coin_chain_fees called!\n"); abort(); } -/* Generated stub for new_coin_chain_fees_sat */ -struct chain_coin_mvt *new_coin_chain_fees_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED) -{ fprintf(stderr, "new_coin_chain_fees_sat called!\n"); abort(); } /* Generated stub for new_coin_journal_entry */ struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx UNNEEDED, const char *account_name UNNEEDED, From 511c0df63ab1e0c3c1a7876caf19448725b3b6b7 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 10 Nov 2021 16:24:34 -0600 Subject: [PATCH 0151/1530] coin-mvts: on close, record an 'end' of our channel balance For a mutual close, this is the only record for this that we need. We remove all of the other tracking around mutual closes. --- onchaind/onchaind.c | 71 ++++++++------------------------------------- 1 file changed, 12 insertions(+), 59 deletions(-) diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 461fd0219c38..204a42405dc8 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -236,34 +236,6 @@ static void add_amt(struct amount_sat *sum, struct amount_sat amt) sum)); } -static void record_mutual_closure(const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat our_out) -{ - struct amount_msat output_msat; - - /* First figure out 'fees' we paid on this will include - * - 'residue' that can't fit onchain (< 1 sat) - * - trimmed output, if our balance is < dust_limit - * - fees paid for getting this tx mined - */ - if (!amount_sat_to_msat(&output_msat, our_out)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to convert %s to msat", - type_to_string(tmpctx, struct amount_sat, - &our_out)); - - /* If we have no output, we exit early */ - if (amount_msat_eq(AMOUNT_MSAT(0), output_msat)) - return; - - /* Otherwise, we record the channel withdrawal */ - /* FIXME: WHy dup txid? */ - send_coin_mvt(take(new_coin_withdrawal(NULL, NULL, &outpoint->txid, - outpoint, - blockheight, output_msat))); -} - static void record_coin_loss(const struct bitcoin_txid *txid, u32 blockheight, struct tracked_output *out) @@ -1284,12 +1256,10 @@ static u64 unmask_commit_number(const struct tx_parts *tx, static bool is_mutual_close(const struct tx_parts *tx, const u8 *local_scriptpubkey, - const u8 *remote_scriptpubkey, - int *local_outnum) + const u8 *remote_scriptpubkey) { size_t i; bool local_matched = false, remote_matched = false; - *local_outnum = -1; for (i = 0; i < tal_count(tx->outputs); i++) { /* To be paranoid, we only let each one match once. */ @@ -1300,7 +1270,6 @@ static bool is_mutual_close(const struct tx_parts *tx, } else if (wally_tx_output_scripteq(tx->outputs[i], local_scriptpubkey) && !local_matched) { - *local_outnum = i; local_matched = true; } else if (wally_tx_output_scripteq(tx->outputs[i], remote_scriptpubkey) @@ -2145,13 +2114,8 @@ static struct htlcs_info *init_reply(const tal_t *ctx, const char *what) } static void handle_mutual_close(struct tracked_output **outs, - const struct tx_parts *tx, - u32 tx_blockheight, - int our_outnum, - bool is_replay) + const struct tx_parts *tx) { - struct amount_sat our_out; - /* In this case, we don't care about htlcs: there are none. */ init_reply(tmpctx, "Tracking mutual close transaction"); @@ -2167,23 +2131,6 @@ static void handle_mutual_close(struct tracked_output **outs, * already agreed to the output, which is sent to its specified `scriptpubkey` */ resolved_by_other(outs[0], &tx->txid, MUTUAL_CLOSE); - - if (!is_replay) { - struct bitcoin_outpoint outpoint; - /* It's possible there's no to_us output */ - if (our_outnum > -1) { - struct amount_asset asset; - asset = wally_tx_output_get_amount(tx->outputs[our_outnum]); - assert(amount_asset_is_main(&asset)); - our_out = amount_asset_to_sat(&asset); - } else - our_out = AMOUNT_SAT(0); - - outpoint.txid = tx->txid; - outpoint.n = our_outnum; - record_mutual_closure(&outpoint, tx_blockheight, our_out); - } - wait_for_resolved(outs); } @@ -3909,7 +3856,6 @@ int main(int argc, char *argv[]) u8 *scriptpubkey[NUM_SIDES]; u32 locktime, tx_blockheight; struct pubkey *possible_remote_per_commitment_point; - int mutual_outnum; bool open_is_replay; subdaemon_setup(argc, argv); @@ -3973,6 +3919,14 @@ int main(int argc, char *argv[]) funding_sats, FUNDING_OUTPUT, NULL, NULL, NULL); + /* Record the funding output being spent */ + /* FIXME: use channel_id as "account name" */ + send_coin_mvt(take(new_coin_withdrawal(NULL, NULL, &tx->txid, + &funding, tx_blockheight, + our_msat))); + + + status_debug("Remote per-commit point: %s", type_to_string(tmpctx, struct pubkey, &remote_per_commit_point)); @@ -3992,9 +3946,8 @@ int main(int argc, char *argv[]) * without any pending payments) and publish it on the blockchain (see * [BOLT #2: Channel Close](02-peer-protocol.md#channel-close)). */ - if (is_mutual_close(tx, scriptpubkey[LOCAL], scriptpubkey[REMOTE], &mutual_outnum)) - handle_mutual_close(outs, tx, - tx_blockheight, mutual_outnum, open_is_replay); + if (is_mutual_close(tx, scriptpubkey[LOCAL], scriptpubkey[REMOTE])) + handle_mutual_close(outs, tx); else { /* BOLT #5: * From 43ade318d9cffb71f391dffb17e1ac46cd91d75f Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 11 Nov 2021 15:59:29 -0600 Subject: [PATCH 0152/1530] coin-move: the txid is now optional but outpoints are not we're pivoting from a txid based world to a outpoint based world. every coin movement (onchain) will correspond with a outpoint; only the spend of an outpoint will have a tx_txid --- common/coin_mvt.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index b96e62ea9788..eaa118db905d 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -295,11 +295,13 @@ void towire_chain_coin_mvt(u8 **pptr, const struct chain_coin_mvt *mvt) towire_u8_array(pptr, (u8 *)mvt->account_name, strlen(mvt->account_name)); } else towire_u16(pptr, 0); - towire_bitcoin_txid(pptr, cast_const(struct bitcoin_txid *, mvt->tx_txid)); - if (mvt->outpoint) { + towire_bitcoin_outpoint(pptr, mvt->outpoint); + + if (mvt->tx_txid) { towire_bool(pptr, true); - towire_bitcoin_outpoint(pptr, mvt->outpoint); + towire_bitcoin_txid(pptr, cast_const(struct bitcoin_txid *, mvt->tx_txid)); + } else towire_bool(pptr, false); if (mvt->payment_hash) { @@ -324,17 +326,19 @@ void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_m } else mvt->account_name = NULL; - mvt->tx_txid = tal(mvt, struct bitcoin_txid); - fromwire_bitcoin_txid(cursor, max, - cast_const(struct bitcoin_txid *, mvt->tx_txid)); + /* Read into non-const version */ + struct bitcoin_outpoint *outpoint + = tal(mvt, struct bitcoin_outpoint); + fromwire_bitcoin_outpoint(cursor, max, outpoint); + mvt->outpoint = outpoint; + if (fromwire_bool(cursor, max)) { - /* Read into non-const version */ - struct bitcoin_outpoint *outpoint - = tal(mvt, struct bitcoin_outpoint); - fromwire_bitcoin_outpoint(cursor, max, outpoint); - mvt->outpoint = outpoint; + mvt->tx_txid = tal(mvt, struct bitcoin_txid); + fromwire_bitcoin_txid(cursor, max, + cast_const(struct bitcoin_txid *, mvt->tx_txid)); } else - mvt->outpoint = NULL; + mvt->tx_txid = NULL; + if (fromwire_bool(cursor, max)) { mvt->payment_hash = tal(mvt, struct sha256); fromwire_sha256(cursor, max, mvt->payment_hash); From 5e1d8d6ad6e7d56cd406f538faf06adc437094ed Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 11 Nov 2021 16:34:26 -0600 Subject: [PATCH 0153/1530] amount: helper for msat == 0 Shorthand for checking if value is zero --- common/amount.c | 5 +++++ common/amount.h | 1 + 2 files changed, 6 insertions(+) diff --git a/common/amount.c b/common/amount.c index bfe29e7a8deb..fa1870700e24 100644 --- a/common/amount.c +++ b/common/amount.c @@ -360,6 +360,11 @@ bool amount_sat_zero(struct amount_sat a) return a.satoshis == 0; } +bool amount_msat_zero(struct amount_msat a) +{ + return a.millisatoshis == 0; +} + bool amount_msat_eq(struct amount_msat a, struct amount_msat b) { return a.millisatoshis == b.millisatoshis; diff --git a/common/amount.h b/common/amount.h index 61631874022c..b7277d7ee536 100644 --- a/common/amount.h +++ b/common/amount.h @@ -94,6 +94,7 @@ bool amount_msat_eq(struct amount_msat a, struct amount_msat b); /* Is a zero? */ bool amount_sat_zero(struct amount_sat a); +bool amount_msat_zero(struct amount_msat a); /* Is a > b? */ bool amount_sat_greater(struct amount_sat a, struct amount_sat b); From b7ca45514c075156fbf0fe8258ac1da69badd03a Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 30 Nov 2021 14:07:44 -0600 Subject: [PATCH 0154/1530] coin_moves: fix account name serialization Switch over to a more robust method of serializing strings --- common/coin_mvt.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index eaa118db905d..f2d897b274b2 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -291,10 +291,10 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, void towire_chain_coin_mvt(u8 **pptr, const struct chain_coin_mvt *mvt) { if (mvt->account_name) { - towire_u16(pptr, strlen(mvt->account_name)); - towire_u8_array(pptr, (u8 *)mvt->account_name, strlen(mvt->account_name)); + towire_bool(pptr, true); + towire_wirestring(pptr, mvt->account_name); } else - towire_u16(pptr, 0); + towire_bool(pptr, false); towire_bitcoin_outpoint(pptr, mvt->outpoint); @@ -313,16 +313,13 @@ void towire_chain_coin_mvt(u8 **pptr, const struct chain_coin_mvt *mvt) towire_u8(pptr, mvt->tag); towire_amount_msat(pptr, mvt->credit); towire_amount_msat(pptr, mvt->debit); + towire_amount_sat(pptr, mvt->output_val); } void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_mvt *mvt) { - u16 account_name_len; - account_name_len = fromwire_u16(cursor, max); - - if (account_name_len) { - mvt->account_name = tal_arr(mvt, char, account_name_len); - fromwire_u8_array(cursor, max, (u8 *)mvt->account_name, account_name_len); + if (fromwire_bool(cursor, max)) { + mvt->account_name = fromwire_wirestring(mvt, cursor, max); } else mvt->account_name = NULL; From 3b3b8fb3ec91e34a4b136498b9ad704221e65d02 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 30 Nov 2021 14:15:03 -0600 Subject: [PATCH 0155/1530] coin_mvt: add "output value" to coin movement When we log "external" wallet events, it's helpful to know what the value of that output is, so let's log it --- common/coin_mvt.c | 10 +++++++++- common/coin_mvt.h | 7 +++++++ lightningd/notification.c | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index f2d897b274b2..fbb32624d77e 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -61,7 +61,8 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, const struct sha256 *payment_hash TAKES, u32 blockheight, enum mvt_tag tag, struct amount_msat amount, - bool is_credit) + bool is_credit, + struct amount_sat output_val) { struct chain_coin_mvt *mvt = tal(ctx, struct chain_coin_mvt); @@ -90,6 +91,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, mvt->debit = amount; mvt->credit = AMOUNT_MSAT(0); } + mvt->output_val = output_val; return mvt; } @@ -250,6 +252,10 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, mvt->tag = chain_mvt->tag; mvt->credit = chain_mvt->credit; mvt->debit = chain_mvt->debit; + + mvt->output_val = tal(mvt, struct amount_sat); + *mvt->output_val = chain_mvt->output_val; + mvt->timestamp = timestamp; mvt->blockheight = chain_mvt->blockheight; mvt->version = COIN_MVT_VERSION; @@ -278,6 +284,7 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->tag = chan_mvt->tag; mvt->credit = chan_mvt->credit; mvt->debit = chan_mvt->debit; + mvt->output_val = NULL; mvt->timestamp = timestamp; /* channel movements don't have a blockheight */ mvt->blockheight = 0; @@ -345,4 +352,5 @@ void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_m mvt->tag = fromwire_u8(cursor, max); mvt->credit = fromwire_amount_msat(cursor, max); mvt->debit = fromwire_amount_msat(cursor, max); + mvt->output_val = fromwire_amount_sat(cursor, max); } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 7049259d736d..77ba24e9097a 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -66,6 +66,9 @@ struct chain_coin_mvt { /* only one or the other */ struct amount_msat credit; struct amount_msat debit; + + /* total value of output (useful for tracking external outs) */ + struct amount_sat output_val; }; /* differs depending on type!? */ @@ -94,6 +97,10 @@ struct coin_mvt { struct amount_msat credit; struct amount_msat debit; + /* Value of the output. May be different than + * our credit/debit amount, eg channel opens */ + struct amount_sat *output_val; + u32 timestamp; u32 blockheight; diff --git a/lightningd/notification.c b/lightningd/notification.c index 82612e6a2753..de7d1e64e4ac 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -471,6 +471,10 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_mvt_id(stream, mvt->type, &mvt->id); json_add_amount_msat_only(stream, "credit", mvt->credit); json_add_amount_msat_only(stream, "debit", mvt->debit); + /* Only chain movements */ + if (mvt->output_val) + json_add_amount_sat_only(stream, "output_value", + *mvt->output_val); json_add_string(stream, "tag", mvt_tag_str(mvt->tag)); /* Only chain movements have blockheights. A blockheight From ade2242d14a29ee817fcfcf1a73ea0286805b440 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 1 Dec 2021 09:37:23 -0600 Subject: [PATCH 0156/1530] coin_mvts: don't overwrite account names we add in onchaind we assume that every event coming from onchaind is for that channel's account, but now that we sometimes track external + wallet events from onchaind, we should only add the channel account name if there's nothing set there already --- lightningd/onchain_control.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index da24e6c4c2db..dd9549c0a124 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -276,8 +276,10 @@ static void handle_onchain_log_coin_move(struct channel *channel, const u8 *msg) return; } - mvt->account_name = - type_to_string(mvt, struct channel_id, &channel->cid); + /* Any 'ignored' payments get registed to the wallet */ + if (!mvt->account_name) + mvt->account_name = type_to_string(mvt, struct channel_id, + &channel->cid); notify_chain_mvt(channel->peer->ld, mvt); tal_free(mvt); } From 07039fc2b4a47d7d5e8f0806ee35d491d92bda3b Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 1 Dec 2021 09:39:40 -0600 Subject: [PATCH 0157/1530] onchaind:move some stuff around so we can call it --- onchaind/onchaind.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 204a42405dc8..80e3fc429047 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -127,6 +127,26 @@ struct tracked_output { struct sha256 payment_hash; }; +static const char *tx_type_name(enum tx_type tx_type) +{ + size_t i; + + for (i = 0; enum_tx_type_names[i].name; i++) + if (enum_tx_type_names[i].v == tx_type) + return enum_tx_type_names[i].name; + return "unknown"; +} + +static const char *output_type_name(enum output_type output_type) +{ + size_t i; + + for (i = 0; enum_output_type_names[i].name; i++) + if (enum_output_type_names[i].v == output_type) + return enum_output_type_names[i].name; + return "unknown"; +} + /* helper to compare output script with our tal'd script */ static bool wally_tx_output_scripteq(const struct wally_tx_output *out, const u8 *script) @@ -456,26 +476,6 @@ static void set_htlc_success_fee(struct bitcoin_tx *tx, tal_hex(tmpctx, wscript)); } -static const char *tx_type_name(enum tx_type tx_type) -{ - size_t i; - - for (i = 0; enum_tx_type_names[i].name; i++) - if (enum_tx_type_names[i].v == tx_type) - return enum_tx_type_names[i].name; - return "unknown"; -} - -static const char *output_type_name(enum output_type output_type) -{ - size_t i; - - for (i = 0; enum_output_type_names[i].name; i++) - if (enum_output_type_names[i].v == output_type) - return enum_output_type_names[i].name; - return "unknown"; -} - static u8 *delayed_payment_to_us(const tal_t *ctx, struct bitcoin_tx *tx, const u8 *wscript) From d2c4d4aec2e0791b6460405dd355e90d7aa04160 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 1 Dec 2021 09:32:55 -0600 Subject: [PATCH 0158/1530] coin_mvts: rewrite how onchain events are recorded, update tests The old model of coin movements attempted to compute fees etc and log amounts, not utxos. This is not as robust, as multi-party opens and dual funded channels make it hard to account for fees etc correctly. Instead, we move towards a 'utxo' view of the onchain events. Every event is either the creation or 'destruction' of a utxo. For cases where the value of the utxo is not (fully) debited/credited to our account, we also record the output_value. E.g. channel closings spend a utxo who's entire value we may not own. Since we're now tracking UTXOs onchain, we can now do more complex assertions about the onchain footprint of them. The integration tests have been updated to now use more 'chain aware' assertions about the ending state. --- common/coin_mvt.c | 234 ++++++++++----- common/coin_mvt.h | 121 +++++--- lightningd/channel_control.c | 62 ++-- lightningd/onchain_control.c | 3 +- onchaind/onchaind.c | 417 ++++++++++++-------------- onchaind/test/run-grind_feerate-bug.c | 88 +++--- onchaind/test/run-grind_feerate.c | 88 +++--- tests/plugins/coin_movements.py | 16 +- tests/test_closing.py | 300 +++++++++++++++++- tests/test_connection.py | 5 +- tests/test_misc.py | 60 +--- tests/test_plugin.py | 108 +------ tests/test_wallet.py | 20 +- tests/utils.py | 153 ++++++++++ wallet/test/run-wallet.c | 14 +- wallet/wallet.c | 8 +- 16 files changed, 1043 insertions(+), 654 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index fbb32624d77e..1f93435e14d0 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -7,6 +7,9 @@ #include #include +#define WALLET "wallet" +#define EXTERNAL "external" + static const char *mvt_types[] = { "chain_mvt", "channel_mvt" }; const char *mvt_type_str(enum mvt_type type) { @@ -16,12 +19,27 @@ const char *mvt_type_str(enum mvt_type type) static const char *mvt_tags[] = { "deposit", "withdrawal", + NULL, "penalty", "invoice", "routed", "journal_entry", - "onchain_htlc", + NULL, "pushed", + NULL, + "channel_open", + "channel_close", + "delayed_to_us", + "htlc_timeout", + "htlc_fulfill", + "htlc_tx", + "to_wallet", + "ignored", + "anchor", + "to_them", + "penalized", + "stolen", + "to_miner", }; const char *mvt_tag_str(enum mvt_tag tag) { @@ -112,124 +130,176 @@ static struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx, return new_chain_coin_mvt(ctx, account_name, tx_txid, outpoint, payment_hash, - blockheight, tag, amt_msat, is_credit); + blockheight, tag, amt_msat, is_credit, + /* All amounts that are sat are + * on-chain output values */ + amt_sat); } -struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_msat amount) +struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *spend_txid, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag) { - assert(!amount_msat_eq(amount, AMOUNT_MSAT(7206000))); - return new_chain_coin_mvt(ctx, account_name, tx_txid, - outpoint, NULL, blockheight, - WITHDRAWAL, amount, false); + return new_chain_coin_mvt_sat(ctx, NULL, spend_txid, + outpoint, NULL, + blockheight, tag, + amount, false); } -struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat amount) +struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag) { - struct amount_msat amt_msat; - bool ok; - - ok = amount_sat_to_msat(&amt_msat, amount); - assert(ok); - - return new_coin_withdrawal(ctx, account_name, tx_txid, outpoint, - blockheight, amt_msat); + return new_chain_coin_mvt_sat(ctx, NULL, NULL, + outpoint, NULL, + blockheight, tag, + amount, true); } struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx, - const char *account_name, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_msat amount, bool is_credit) { - return new_chain_coin_mvt(ctx, account_name, txid, + return new_chain_coin_mvt(ctx, NULL, txid, outpoint, NULL, blockheight, JOURNAL, - amount, is_credit); + amount, is_credit, AMOUNT_SAT(0)); } -struct chain_coin_mvt *new_coin_deposit(const tal_t *ctx, - const char *account_name, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_msat amount) +struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, + const struct bitcoin_txid *txid, + const struct bitcoin_outpoint *out, + u32 blockheight, + const struct amount_msat amount, + const struct amount_sat output_val) { - /* FIXME: Why dup txid here? */ - return new_chain_coin_mvt(ctx, account_name, &outpoint->txid, outpoint, - NULL, blockheight, DEPOSIT, - amount, true); + return new_chain_coin_mvt(ctx, NULL, txid, + out, NULL, blockheight, + CHANNEL_CLOSE, amount, false, + output_val); } -struct chain_coin_mvt *new_coin_deposit_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat amount) +struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, + const struct channel_id *chan_id, + const struct bitcoin_outpoint *out, + u32 blockheight, + const struct amount_msat amount, + const struct amount_sat output_val) { - struct amount_msat amt_msat; - bool ok; - - ok = amount_sat_to_msat(&amt_msat, amount); - assert(ok); - - return new_coin_deposit(ctx, account_name, outpoint, - blockheight, amt_msat); + struct chain_coin_mvt *mvt; + mvt = new_chain_coin_mvt(ctx, NULL, NULL, out, NULL, blockheight, + CHANNEL_OPEN, amount, true, output_val); + mvt->account_name = type_to_string(mvt, struct channel_id, chan_id); + return mvt; } -struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat amount) + +struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + struct sha256 *payment_hash) { - struct amount_msat amt_msat; - bool ok; + return new_chain_coin_mvt_sat(ctx, NULL, NULL, + outpoint, payment_hash, + blockheight, HTLC_FULFILL, + amount, true); +} - ok = amount_sat_to_msat(&amt_msat, amount); - assert(ok); - return new_chain_coin_mvt(ctx, account_name, - txid, outpoint, NULL, - blockheight, PENALTY, - amt_msat, false); +struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + struct sha256 *payment_hash) +{ + /* An onchain htlc fulfillment to peer is a *deposit* of + * that output into their (external) account */ + return new_chain_coin_mvt_sat(ctx, EXTERNAL, NULL, + outpoint, payment_hash, + blockheight, HTLC_FULFILL, + amount, false); } -struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *txid, +struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *txid, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag) +{ + return new_chain_coin_mvt(ctx, EXTERNAL, txid, + outpoint, NULL, blockheight, + tag, AMOUNT_MSAT(0), true, amount); +} + +struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, - struct sha256 payment_hash, u32 blockheight, struct amount_sat amount, - bool is_credit) + enum mvt_tag tag) +{ + return new_chain_coin_mvt(ctx, EXTERNAL, NULL, + outpoint, NULL, + blockheight, tag, + AMOUNT_MSAT(0), true, amount); +} + +struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag) +{ + return new_chain_coin_mvt_sat(ctx, WALLET, NULL, + outpoint, NULL, + blockheight, tag, + amount, true); +} + +struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, + const struct bitcoin_txid *spend_txid, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag) +{ + return new_chain_coin_mvt_sat(ctx, WALLET, spend_txid, + outpoint, NULL, + blockheight, tag, + amount, false); +} + +struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, + const char *account_name, + const struct bitcoin_txid *txid, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount) { return new_chain_coin_mvt_sat(ctx, account_name, - txid, outpoint, - take(tal_dup(NULL, struct sha256, - &payment_hash)), blockheight, - ONCHAIN_HTLC, amount, is_credit); + txid, outpoint, NULL, + blockheight, PENALTY, + amount, false); } -struct chain_coin_mvt *new_coin_pushed(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *txid, - u32 blockheight, - struct amount_msat amount) +struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx, + const struct channel_id *cid, + struct amount_msat amount) { - return new_chain_coin_mvt(ctx, account_name, txid, NULL, - NULL, blockheight, PUSHED, amount, - false); + struct sha256 empty_hash; + /* Use a 0'd out payment hash */ + memset(&empty_hash, 0, sizeof(empty_hash)); + + return new_channel_coin_mvt(ctx, cid, empty_hash, + NULL, amount, PUSHED, false); } struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 77ba24e9097a..04383c6e6e90 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -22,9 +22,22 @@ enum mvt_tag { INVOICE = 4, ROUTED = 5, JOURNAL = 6, - ONCHAIN_HTLC = 7, + /* 7, ONCHAIN_HTLC has been removed */ PUSHED = 8, /* 9, SPEND_TRACK has been removed */ + CHANNEL_OPEN = 10, + CHANNEL_CLOSE = 11, + CHANNEL_TO_US = 12, + HTLC_TIMEOUT = 13, + HTLC_FULFILL = 14, + HTLC_TX = 15, + TO_WALLET = 16, + IGNORED = 17, + ANCHOR = 18, + TO_THEM = 19, + PENALIZED = 20, + STOLEN = 21, + TO_MINER = 22, }; struct channel_coin_mvt { @@ -123,35 +136,77 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, enum mvt_tag tag, bool is_credit); -struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_msat amount); -struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *tx_txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat amount); +struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *spend_txid, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag); + +struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag); + struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx, - const char *account_name, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_msat amount, bool is_credit); -struct chain_coin_mvt *new_coin_deposit(const tal_t *ctx, - const char *account_name, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_msat amount); -struct chain_coin_mvt *new_coin_deposit_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat amount); + +struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, + const struct bitcoin_txid *txid, + const struct bitcoin_outpoint *out, + u32 blockheight, + const struct amount_msat amount, + const struct amount_sat output_val); +struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, + const struct channel_id *chan_id, + const struct bitcoin_outpoint *out, + u32 blockheight, + const struct amount_msat amount, + const struct amount_sat output_val); + +struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + struct sha256 *payment_hash); + +struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + struct sha256 *payment_hash); + +struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag); + +struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, + const struct bitcoin_txid *spend_txid, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag); + +struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *txid, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag); + +struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag tag); + struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, const char *account_name, const struct bitcoin_txid *txid, @@ -159,25 +214,17 @@ struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, u32 blockheight, struct amount_sat amount); -struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - struct sha256 payment_hash, - u32 blockheight, - struct amount_sat amount, - bool is_credit); -struct chain_coin_mvt *new_coin_pushed(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *txid, - u32 blockheight, - struct amount_msat amount); +struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx, + const struct channel_id *cid, + struct amount_msat amount); + struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, const char *bip173_name, u32 timestamp, struct node_id *node_id, s64 mvt_count); + struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct channel_coin_mvt *chan_mvt, const char *bip173_name, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index e6fb0af00ab0..7262889b4f02 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -130,46 +130,40 @@ void notify_feerate_change(struct lightningd *ld) void channel_record_open(struct channel *channel) { struct chain_coin_mvt *mvt; - struct amount_msat channel_open_amt; u32 blockheight; - - u8 *ctx = tal(NULL, u8); + struct amount_msat start_balance; + bool we_pushed = channel->opener == LOCAL + && !amount_msat_zero(channel->push); blockheight = short_channel_id_blocknum(channel->scid); - /* FIXME: logic here will change for dual funded channels */ - if (channel->opener == LOCAL) { - if (!amount_sat_to_msat(&channel_open_amt, - channel->funding_sats)) - fatal("Unable to convert funding %s to msat", - type_to_string(tmpctx, struct amount_sat, - &channel->funding_sats)); - - /* if we pushed sats, we should decrement that - * from the channel balance */ - if (amount_msat_greater(channel->push, AMOUNT_MSAT(0))) { - mvt = new_coin_pushed(ctx, - type_to_string(tmpctx, - struct channel_id, - &channel->cid), - &channel->funding.txid, - blockheight, channel->push); - notify_chain_mvt(channel->peer->ld, mvt); - } - } else { - /* we're not the funder, we record our 'opening balance' - * anyway (there's a small chance we were pushed some - * satoshis, otherwise it's zero) */ - channel_open_amt = channel->our_msat; - } + /* If we pushed funds, add them back into the starting balance */ + if (we_pushed) { + if (!amount_msat_add(&start_balance, + channel->push, channel->our_msat)) + fatal("Unable to add push_msat (%s) + our_msat (%s)", + type_to_string(tmpctx, struct amount_msat, + &channel->push), + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat)); + + } else + start_balance = channel->our_msat; + + mvt = new_coin_channel_open(tmpctx, + &channel->cid, + &channel->funding, + blockheight, + start_balance, + channel->funding_sats); - mvt = new_coin_deposit(ctx, - type_to_string(tmpctx, struct channel_id, - &channel->cid), - &channel->funding, - blockheight, channel_open_amt); notify_chain_mvt(channel->peer->ld, mvt); - tal_free(ctx); + + /* If we pushed sats, *now* record them as a withdrawal */ + if (we_pushed) + notify_channel_mvt(channel->peer->ld, + new_coin_pushed(tmpctx, &channel->cid, + channel->push)); } static void lockin_complete(struct channel *channel) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index dd9549c0a124..ae263a7ba87c 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -479,7 +479,8 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg) commitment_point, csv_lock); - mvt = new_coin_deposit_sat(msg, "wallet", &outpoint, blockheight, amount); + mvt = new_coin_wallet_deposit(msg, &outpoint, blockheight, + amount, CHANNEL_CLOSE); notify_chain_mvt(channel->peer->ld, mvt); } diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 80e3fc429047..01b7aa7f6504 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -212,84 +212,104 @@ static void send_coin_mvt(struct chain_coin_mvt *mvt TAKES) tal_free(mvt); } -static void record_their_successful_cheat(const struct bitcoin_txid *txid, - u32 blockheight, - struct tracked_output *out) +static void record_channel_withdrawal(const struct bitcoin_txid *tx_txid, + struct tracked_output *out, + u32 blockheight, + enum mvt_tag tag) { - struct chain_coin_mvt *mvt; - /* They successfully spent a delayed_to_them output - * that we were expecting to revoke */ - mvt = new_coin_penalty_sat(NULL, NULL, - txid, &out->outpoint, - blockheight, out->sat); - - send_coin_mvt(take(mvt)); + send_coin_mvt(take(new_onchaind_withdraw(NULL, &out->outpoint, tx_txid, + blockheight, out->sat, tag))); } -static void record_htlc_fulfilled(const struct bitcoin_txid *txid, +static void record_external_spend(const struct bitcoin_txid *txid, struct tracked_output *out, u32 blockheight, - bool we_fulfilled) + enum mvt_tag tag) { - struct chain_coin_mvt *mvt; - - /* we're recording the *deposit* of a utxo which contained channel - * funds (htlc). - * - * since we really don't know if this was a 'routed' or 'destination' - * htlc here, we record it as a 'deposit/withdrawal' type */ - mvt = new_coin_onchain_htlc_sat(NULL, NULL, txid, &out->outpoint, - out->payment_hash, - blockheight, out->sat, we_fulfilled); + send_coin_mvt(take(new_coin_external_spend(NULL, &out->outpoint, + txid, blockheight, + out->sat, tag))); +} - send_coin_mvt(take(mvt)); +static void record_external_output(const struct bitcoin_outpoint *out, + struct amount_sat amount, + u32 blockheight, + enum mvt_tag tag) +{ + send_coin_mvt(take(new_coin_external_deposit(NULL, out, blockheight, + amount, tag))); } -static void add_amt(struct amount_sat *sum, struct amount_sat amt) +static void record_external_deposit(const struct tracked_output *out, + u32 blockheight, + enum mvt_tag tag) { - if (!amount_sat_add(sum, *sum, amt)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to add %s to %s", - type_to_string(tmpctx, struct amount_sat, - &amt), - type_to_string(tmpctx, struct amount_sat, - sum)); + record_external_output(&out->outpoint, out->sat, blockheight, tag); } -static void record_coin_loss(const struct bitcoin_txid *txid, - u32 blockheight, - struct tracked_output *out) +static void record_channel_deposit(struct tracked_output *out, + u32 blockheight, enum mvt_tag tag) { - struct chain_coin_mvt *mvt; - /* We don't for sure know that it's a 'penalty' - * but we write it as that anyway... */ - mvt = new_coin_penalty_sat(NULL, NULL, txid, &out->outpoint, - blockheight, out->sat); + send_coin_mvt(take(new_onchaind_deposit(NULL, + &out->outpoint, + blockheight, out->sat, + tag))); +} - send_coin_mvt(take(mvt)); +static void record_to_us_htlc_fulfilled(struct tracked_output *out, + u32 blockheight) +{ + send_coin_mvt(take(new_onchain_htlc_deposit(NULL, + &out->outpoint, + blockheight, + out->sat, + &out->payment_hash))); } -static void record_channel_withdrawal(const struct bitcoin_txid *tx_txid, - struct tracked_output *out, - u32 blockheight) +static void record_to_them_htlc_fulfilled(struct tracked_output *out, + u32 blockheight) { - send_coin_mvt(take(new_coin_withdrawal_sat( - NULL, NULL, tx_txid, &out->outpoint, - blockheight, out->sat))); + + send_coin_mvt(take(new_onchain_htlc_withdraw(NULL, + &out->outpoint, + blockheight, + out->sat, + &out->payment_hash))); } -static bool is_our_htlc_tx(struct tracked_output *out) +static void record_ignored_wallet_deposit(struct tracked_output *out) { - return out->resolved && - (out->resolved->tx_type == OUR_HTLC_TIMEOUT_TX - || out->resolved->tx_type == OUR_HTLC_SUCCESS_TX); + struct bitcoin_outpoint outpoint; + + /* Every spend tx we construct has a single output. */ + bitcoin_txid(out->proposal->tx, &outpoint.txid); + outpoint.n = 0; + + enum mvt_tag tag = TO_WALLET; + if (out->tx_type == OUR_HTLC_TIMEOUT_TX + || out->tx_type == OUR_HTLC_SUCCESS_TX) + tag = HTLC_TX; + else if (out->tx_type == THEIR_REVOKED_UNILATERAL) + tag = PENALTY; + else if (out->tx_type == OUR_UNILATERAL + || out->tx_type == THEIR_UNILATERAL) { + if (out->output_type == OUR_HTLC) + tag = HTLC_TIMEOUT; + } + if (out->output_type == DELAYED_OUTPUT_TO_US) + tag = CHANNEL_TO_US; + + /* Record the in/out through the channel */ + record_channel_deposit(out, out->tx_blockheight, tag); + record_channel_withdrawal(&outpoint.txid, out, 0, IGNORED); } -static bool is_channel_deposit(struct tracked_output *out) +static void record_anchor(struct tracked_output *out) { - return out->resolved && - (out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US - || out->resolved->tx_type == OUR_HTLC_SUCCESS_TX); + send_coin_mvt(take(new_coin_wallet_deposit(NULL, + &out->outpoint, + out->tx_blockheight, + out->sat, ANCHOR))); } static void record_coin_movements(struct tracked_output *out, @@ -297,17 +317,53 @@ static void record_coin_movements(struct tracked_output *out, const struct bitcoin_tx *tx, const struct bitcoin_txid *txid) { + /* For 'timeout' htlcs, we re-record them as a deposit + * before we withdraw them again. When the channel closed, + * we reported this as withdrawn (since we didn't know the + * total amount of pending htlcs that are to-them). So + * we have to "deposit" it again before we withdraw it. + * This is just to make the channel account close out nicely + * AND so we can accurately calculate our on-chain fee burden */ + if (out->tx_type == OUR_HTLC_TIMEOUT_TX + || out->tx_type == OUR_HTLC_SUCCESS_TX) + record_channel_deposit(out, blockheight, HTLC_TX); + + if (out->resolved->tx_type == OUR_HTLC_TIMEOUT_TO_US) + record_channel_deposit(out, blockheight, HTLC_TIMEOUT); + /* there is a case where we've fulfilled an htlc onchain, * in which case we log a deposit to the channel */ - if (is_channel_deposit(out)) - record_htlc_fulfilled(txid, out, blockheight, true); - + if (out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US + || out->resolved->tx_type == OUR_HTLC_SUCCESS_TX) + record_to_us_htlc_fulfilled(out, blockheight); + + /* If it's our to-us and our close, we publish *another* tx + * which spends the output when the timeout ends */ + if (out->tx_type == OUR_UNILATERAL) { + if (out->output_type == DELAYED_OUTPUT_TO_US) + record_channel_deposit(out, blockheight, CHANNEL_TO_US); + else if (out->output_type == OUR_HTLC) { + record_channel_deposit(out, blockheight, HTLC_TIMEOUT); + record_channel_withdrawal(txid, out, blockheight, HTLC_TIMEOUT); + } else if (out->output_type == THEIR_HTLC) + record_channel_withdrawal(txid, out, blockheight, HTLC_FULFILL); + } - /* we don't record a channel withdrawal until we get to - * the 'exit' utxo, which for local commitment htlc txs - * is the child htlc_tx's output */ - if (!is_our_htlc_tx(out)) - record_channel_withdrawal(txid, out, blockheight); + if (out->tx_type == THEIR_REVOKED_UNILATERAL + || out->resolved->tx_type == OUR_PENALTY_TX) + record_channel_deposit(out, blockheight, PENALTY); + + if (out->resolved->tx_type == OUR_DELAYED_RETURN_TO_WALLET + || out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US + || out->output_type == DELAYED_OUTPUT_TO_US + || out->resolved->tx_type == OUR_HTLC_TIMEOUT_TO_US + || out->resolved->tx_type == OUR_PENALTY_TX) { + /* penalty rbf cases, the amount might be zero */ + if (amount_sat_zero(out->sat)) + record_channel_withdrawal(txid, out, blockheight, TO_MINER); + else + record_channel_withdrawal(txid, out, blockheight, TO_WALLET); + } } /* We vary feerate until signature they offered matches. */ @@ -616,7 +672,7 @@ replace_penalty_tx_to_us(const tal_t *ctx, struct bitcoin_tx *tx, const u8 *wscript), const struct bitcoin_tx *penalty_tx, - struct amount_sat output_amount) + struct amount_sat *output_amount) { struct bitcoin_tx *tx; @@ -657,17 +713,19 @@ replace_penalty_tx_to_us(const tal_t *ctx, BITCOIN_TX_RBF_SEQUENCE, NULL, input_amount, NULL, input_wscript); /* Reconstruct the output with a smaller amount. */ - if (amount_sat_greater(output_amount, dust_limit)) + if (amount_sat_greater(*output_amount, dust_limit)) bitcoin_tx_add_output(tx, scriptpubkey_p2wpkh(tx, &our_wallet_pubkey), NULL, - output_amount); - else + *output_amount); + else { bitcoin_tx_add_output(tx, scriptpubkey_opreturn_padded(tx), NULL, AMOUNT_SAT(0)); + *output_amount = AMOUNT_SAT(0); + } /* Finalize the transaction. */ bitcoin_tx_finalize(tx); @@ -1001,7 +1059,11 @@ static void proposal_should_rbf(struct tracked_output *out, bool is_replay) /* Recreate the penalty tx. */ tx = replace_penalty_tx_to_us(tmpctx, &penalty_to_us, - out->proposal->tx, new_amount); + out->proposal->tx, &new_amount); + + /* We also update the output's value, so our accounting + * is correct. */ + out->sat = new_amount; status_debug("Created RBF OUR_PENALTY_TX with output %s " "(originally %s) for depth %"PRIu32"/%"PRIu32".", @@ -1040,6 +1102,11 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay) /* If we simply wanted to ignore it after some depth */ if (!out->proposal->tx) { ignore_output(out); + + if (out->proposal->tx_type == THEIR_HTLC_TIMEOUT_TO_THEM) + record_external_deposit(out, out->tx_blockheight, + HTLC_TIMEOUT); + return; } @@ -1062,16 +1129,8 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay) /* Don't wait for this if we're ignoring the tiny payment. */ if (out->proposal->tx_type == IGNORING_TINY_PAYMENT) { - struct bitcoin_txid txid; - ignore_output(out); - - if (!is_replay) { - /* log the coin movements here, since we're not - * going to wait til we hear about it */ - bitcoin_txid(out->proposal->tx, &txid); - record_channel_withdrawal(&txid, out, 0); - } + record_ignored_wallet_deposit(out); } /* Otherwise we will get a callback when it's in a block. */ @@ -1629,10 +1688,9 @@ static void output_spent(struct tracked_output ***outs, resolve_htlc_tx(outs, i, tx_parts, input_num, tx_blockheight, is_replay); - if (!is_replay) - record_coin_movements(out, tx_blockheight, - out->proposal->tx, - &tx_parts->txid); + record_coin_movements(out, tx_blockheight, + out->proposal->tx, + &tx_parts->txid); return; } @@ -1643,12 +1701,15 @@ static void output_spent(struct tracked_output ***outs, case OUTPUT_TO_US: case DELAYED_OUTPUT_TO_US: unknown_spend(out, tx_parts); - if (!is_replay) - record_coin_loss(&tx_parts->txid, - tx_blockheight, out); + record_external_deposit(out, tx_blockheight, PENALIZED); break; case THEIR_HTLC: + record_external_deposit(out, out->tx_blockheight, + HTLC_TIMEOUT); + record_external_spend(&tx_parts->txid, out, + tx_blockheight, HTLC_TIMEOUT); + if (out->tx_type == THEIR_REVOKED_UNILATERAL) { /* we've actually got a 'new' output here */ steal_htlc_tx(out, outs, tx_parts, @@ -1682,6 +1743,11 @@ static void output_spent(struct tracked_output ***outs, */ handle_htlc_onchain_fulfill(out, tx_parts, &htlc_outpoint); + + record_to_them_htlc_fulfilled(out, tx_blockheight); + record_external_spend(&tx_parts->txid, out, + tx_blockheight, HTLC_FULFILL); + if (out->tx_type == THEIR_REVOKED_UNILATERAL) { steal_htlc_tx(out, outs, tx_parts, tx_blockheight, @@ -1700,11 +1766,6 @@ static void output_spent(struct tracked_output ***outs, */ ignore_output(out); - if (!is_replay) - record_htlc_fulfilled(&tx_parts->txid, - out, - tx_blockheight, - false); onchain_annotate_txout( &htlc_outpoint, TX_CHANNEL_HTLC_SUCCESS | TX_THEIRS); @@ -1721,9 +1782,8 @@ static void output_spent(struct tracked_output ***outs, /* They successfully spent a delayed revoked output */ resolved_by_other(out, &tx_parts->txid, THEIR_DELAYED_CHEAT); - if (!is_replay) - record_their_successful_cheat(&tx_parts->txid, - tx_blockheight, out); + + record_external_deposit(out, tx_blockheight, STOLEN); break; /* Um, we don't track these! */ case OUTPUT_TO_THEM: @@ -1957,11 +2017,6 @@ static void handle_preimage(struct tracked_output **outs, htlc_feerate); propose_resolution(outs[i], tx, 0, tx_type, is_replay); - if (!is_replay && tx_type == IGNORING_TINY_PAYMENT) { - struct bitcoin_txid txid; - bitcoin_txid(tx, &txid); - record_htlc_fulfilled(&txid, outs[i], 0, true); - } } } @@ -2517,7 +2572,6 @@ static void handle_our_unilateral(const struct tx_parts *tx, struct pubkey local_per_commitment_point; struct keyset *ks; size_t i; - struct amount_sat their_outs = AMOUNT_SAT(0), our_outs = AMOUNT_SAT(0); struct htlcs_info *htlcs_info; htlcs_info = init_reply(tx, "Tracking our own unilateral close"); @@ -2631,7 +2685,6 @@ static void handle_our_unilateral(const struct tx_parts *tx, local_wscript, is_replay); script[LOCAL] = NULL; - add_amt(&our_outs, amt); continue; } if (script[REMOTE] @@ -2651,8 +2704,8 @@ static void handle_our_unilateral(const struct tx_parts *tx, OUTPUT_TO_THEM, NULL, NULL, NULL); ignore_output(out); + record_external_deposit(out, tx_blockheight, TO_THEM); script[REMOTE] = NULL; - add_amt(&their_outs, amt); continue; } if (anchor[LOCAL] @@ -2666,6 +2719,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, ANCHOR_TO_US, NULL, NULL, NULL); ignore_output(out); + record_anchor(out); anchor[LOCAL] = NULL; continue; } @@ -2679,6 +2733,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, ANCHOR_TO_THEM, NULL, NULL, NULL); ignore_output(out); + record_external_deposit(out, tx_blockheight, ANCHOR); anchor[REMOTE] = NULL; continue; } @@ -2718,7 +2773,6 @@ static void handle_our_unilateral(const struct tx_parts *tx, is_replay); script[LOCAL] = NULL; - add_amt(&our_outs, amt); found = true; break; } @@ -2752,8 +2806,10 @@ static void handle_our_unilateral(const struct tx_parts *tx, OUTPUT_TO_THEM, NULL, NULL, NULL); ignore_output(out); + record_external_deposit(out, + tx_blockheight, + TO_THEM); script[REMOTE] = NULL; - add_amt(&their_outs, amt); found = true; break; } @@ -2764,6 +2820,10 @@ static void handle_our_unilateral(const struct tx_parts *tx, continue; onchain_annotate_txout(&outpoint, TX_CHANNEL_PENALTY | TX_THEIRS); + + record_external_output(&outpoint, amt, + tx_blockheight, + PENALTY); status_failed(STATUS_FAIL_INTERNAL_ERROR, "Could not find resolution for output %zu", i); @@ -2788,7 +2848,6 @@ static void handle_our_unilateral(const struct tx_parts *tx, htlcs_info->htlcs, htlc_scripts, is_replay); - add_amt(&our_outs, amt); } else { out = new_tracked_output(&outs, &outpoint, tx_blockheight, @@ -2808,7 +2867,6 @@ static void handle_our_unilateral(const struct tx_parts *tx, htlcs_info->htlcs, htlc_scripts, is_replay); - add_amt(&their_outs, amt); } out->htlc = htlcs_info->htlcs[which_htlc]; out->wscript = tal_steal(out, htlc_scripts[which_htlc]); @@ -2904,37 +2962,6 @@ static void tell_wallet_to_remote(const struct tx_parts *tx, csv_lock))); } -/* When a 'cheat' transaction comes through, our accounting is - * going to be off, as it's publishing/finalizing old state. - * To compensate for this, we count *all* of the channel funds - * as ours; any subsequent handling of utxos on this tx - * will correctly mark the funds as a 'channel withdrawal' - */ -static void update_ledger_cheat(const struct bitcoin_txid *txid, - u32 blockheight, - const struct tracked_output *out) -{ - /* how much of a difference should we update the - * channel account ledger by? */ - struct amount_msat amt; - - if (amount_msat_eq_sat(our_msat, out->sat)) - return; - - if (!amount_sat_sub_msat(&amt, out->sat, our_msat)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "unable to subtract our balance %s from channel total %s", - type_to_string(tmpctx, struct amount_msat, - &our_msat), - type_to_string(tmpctx, struct amount_sat, - &out->sat)); - - /* add the difference to our ledger balance */ - send_coin_mvt(take(new_coin_journal_entry(NULL, NULL, txid, - &out->outpoint, - blockheight, amt, true))); -} - static void their_unilateral_local(struct tracked_output ***outs, const struct tx_parts *tx, const struct bitcoin_outpoint *outpoint, @@ -2964,9 +2991,6 @@ static void their_unilateral_local(struct tracked_output ***outs, NULL); ignore_output(out); - if (!is_replay) - record_channel_withdrawal(&tx->txid, out, tx_blockheight); - tell_wallet_to_remote(tx, outpoint, tx_blockheight, local_scriptpubkey, @@ -2996,10 +3020,6 @@ static void handle_their_cheat(const struct tx_parts *tx, struct keyset *ks; struct pubkey *k; size_t i; - /* We need to figure out what the 'chain fees' - * for this unilateral tx are */ - struct amount_sat total_outs = AMOUNT_SAT(0), fee_cost; - bool amt_ok; struct htlcs_info *htlcs_info; htlcs_info = init_reply(tx, @@ -3007,9 +3027,6 @@ static void handle_their_cheat(const struct tx_parts *tx, onchain_annotate_txin( &tx->txid, 0, TX_CHANNEL_UNILATERAL | TX_CHANNEL_CHEAT | TX_THEIRS); - if (!is_replay) - update_ledger_cheat(&tx->txid, tx_blockheight, outs[0]); - /* BOLT #5: * * Once a node discovers a commitment transaction for which *it* has a @@ -3157,7 +3174,6 @@ static void handle_their_cheat(const struct tx_parts *tx, THEIR_REVOKED_UNILATERAL, is_replay, 1); script[LOCAL] = NULL; - add_amt(&total_outs, amt); continue; } if (script[REMOTE] @@ -3176,7 +3192,6 @@ static void handle_their_cheat(const struct tx_parts *tx, NULL, NULL, NULL); steal_to_them_output(out, 1, is_replay); script[REMOTE] = NULL; - add_amt(&total_outs, amt); continue; } if (anchor[LOCAL] @@ -3190,6 +3205,7 @@ static void handle_their_cheat(const struct tx_parts *tx, ANCHOR_TO_US, NULL, NULL, NULL); ignore_output(out); + record_anchor(out); anchor[LOCAL] = NULL; continue; } @@ -3203,6 +3219,7 @@ static void handle_their_cheat(const struct tx_parts *tx, ANCHOR_TO_THEM, NULL, NULL, NULL); ignore_output(out); + record_external_deposit(out, tx_blockheight, ANCHOR); anchor[REMOTE] = NULL; continue; } @@ -3235,7 +3252,6 @@ static void handle_their_cheat(const struct tx_parts *tx, is_replay, csv); script[LOCAL] = NULL; - add_amt(&total_outs, amt); found = true; break; } @@ -3267,16 +3283,19 @@ static void handle_their_cheat(const struct tx_parts *tx, steal_to_them_output(out, csv, is_replay); script[REMOTE] = NULL; - add_amt(&total_outs, amt); found = true; break; } } - if (!found) + if (!found) { + record_external_output(&outpoint, amt, + tx_blockheight, + PENALTY); status_broken("Could not find resolution" " for output %zu: did" " *we* cheat?", i); + } continue; } @@ -3300,7 +3319,6 @@ static void handle_their_cheat(const struct tx_parts *tx, htlc_scripts[which_htlc], NULL); steal_htlc(out, is_replay); - add_amt(&total_outs, amt); } else { out = new_tracked_output(&outs, &outpoint, tx_blockheight, @@ -3318,7 +3336,6 @@ static void handle_their_cheat(const struct tx_parts *tx, * * spend the *HTLC-timeout tx*, if the remote node has published it. */ steal_htlc(out, is_replay); - add_amt(&total_outs, amt); } htlc_scripts[which_htlc] = NULL; } @@ -3326,12 +3343,6 @@ static void handle_their_cheat(const struct tx_parts *tx, note_missing_htlcs(htlc_scripts, htlcs_info); tal_free(htlcs_info); - /* Record the fee cost for this tx, deducting it from channel balance */ - amt_ok = amount_sat_sub(&fee_cost, outs[0]->sat, total_outs); - assert(amt_ok); - status_debug("recording chain fees for their cheat %s", - type_to_string(tmpctx, struct amount_sat, &fee_cost)); - wait_for_resolved(outs); } @@ -3347,7 +3358,6 @@ static void handle_their_unilateral(const struct tx_parts *tx, u8 *remote_wscript, *script[NUM_SIDES], *anchor[NUM_SIDES]; struct keyset *ks; size_t i; - struct amount_sat their_outs = AMOUNT_SAT(0), our_outs = AMOUNT_SAT(0); struct htlcs_info *htlcs_info; htlcs_info = init_reply(tx, "Tracking their unilateral close"); @@ -3490,7 +3500,6 @@ static void handle_their_unilateral(const struct tx_parts *tx, is_replay, 1); script[LOCAL] = NULL; - add_amt(&our_outs, amt); continue; } if (script[REMOTE] @@ -3511,7 +3520,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, DELAYED_OUTPUT_TO_THEM, NULL, NULL, NULL); ignore_output(out); - add_amt(&their_outs, amt); + record_external_deposit(out, tx_blockheight, TO_THEM); continue; } if (anchor[LOCAL] @@ -3524,7 +3533,9 @@ static void handle_their_unilateral(const struct tx_parts *tx, amt, ANCHOR_TO_US, NULL, NULL, NULL); + ignore_output(out); + record_anchor(out); anchor[LOCAL] = NULL; continue; } @@ -3539,6 +3550,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, NULL, NULL, NULL); ignore_output(out); anchor[REMOTE] = NULL; + record_external_deposit(out, tx_blockheight, ANCHOR); continue; } @@ -3571,7 +3583,6 @@ static void handle_their_unilateral(const struct tx_parts *tx, THEIR_UNILATERAL, is_replay, csv); script[LOCAL] = NULL; - add_amt(&our_outs, amt); found = true; break; } @@ -3602,7 +3613,9 @@ static void handle_their_unilateral(const struct tx_parts *tx, DELAYED_OUTPUT_TO_THEM, NULL, NULL, NULL); ignore_output(out); - add_amt(&their_outs, amt); + record_external_deposit(out, + tx_blockheight, + TO_THEM); found = true; break; } @@ -3611,6 +3624,9 @@ static void handle_their_unilateral(const struct tx_parts *tx, if (found) continue; + record_external_output(&outpoint, amt, + tx_blockheight, + PENALTY); status_failed(STATUS_FAIL_INTERNAL_ERROR, "Could not find resolution for output %zu", i); @@ -3635,7 +3651,6 @@ static void handle_their_unilateral(const struct tx_parts *tx, htlcs_info->htlcs, htlc_scripts, is_replay); - add_amt(&our_outs, amt); } else { out = new_tracked_output(&outs, &outpoint, tx_blockheight, @@ -3653,7 +3668,6 @@ static void handle_their_unilateral(const struct tx_parts *tx, which_htlc = resolve_their_htlc(out, matches, htlcs_info->htlcs, htlc_scripts, is_replay); - add_amt(&their_outs, amt); } out->htlc = htlcs_info->htlcs[which_htlc]; out->wscript = tal_steal(out, htlc_scripts[which_htlc]); @@ -3666,42 +3680,6 @@ static void handle_their_unilateral(const struct tx_parts *tx, wait_for_resolved(outs); } -static void update_ledger_unknown(const struct bitcoin_txid *txid, - u32 blockheight, - struct amount_sat amt_salvaged) -{ - /* ideally, we'd be able to capture the loss to fees (if we funded - * the channel) here separately, but given that we don't know the htlc - * set (and thus which outputs are trimmed), this is difficult. - * - * instead, we count the difference between any recoverable output - * and our current channel balance as a loss (or gain) */ - bool is_credit; - struct amount_msat diff; - - /* we do nothing if the amount withdrawn via 'salvage' is - * the same as our channel balance */ - if (amount_msat_eq_sat(our_msat, amt_salvaged)) - return; - - /* if we've withdrawn *less* in salvage than we have on the books - * as being ours, we record the difference as a debit */ - if (!amount_msat_sub_sat(&diff, our_msat, amt_salvaged)) { - is_credit = false; - if (!amount_sat_sub_msat(&diff, amt_salvaged, our_msat)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "overflow subtracting %s from %s", - type_to_string(tmpctx, struct amount_msat, - &our_msat), - type_to_string(tmpctx, struct amount_sat, - &amt_salvaged)); - } else - is_credit = true; - - send_coin_mvt(take(new_coin_journal_entry(NULL, NULL, txid, NULL, - blockheight, diff, is_credit))); -} - static void handle_unknown_commitment(const struct tx_parts *tx, u32 tx_blockheight, const struct pubkey *possible_remote_per_commitment_point, @@ -3712,7 +3690,6 @@ static void handle_unknown_commitment(const struct tx_parts *tx, int to_us_output = -1; /* We have two possible local scripts, depending on options */ u8 *local_scripts[2]; - struct amount_sat amt_salvaged = AMOUNT_SAT(0); struct htlcs_info *htlcs_info; onchain_annotate_txin(&tx->txid, 0, TX_CHANNEL_UNILATERAL | TX_THEIRS); @@ -3768,8 +3745,15 @@ static void handle_unknown_commitment(const struct tx_parts *tx, && wally_tx_output_scripteq(tx->outputs[i], local_scripts[1])) which_script = 1; - else + else { + /* Record every output on this tx as an + * external 'penalty' */ + record_external_output(&outpoint, amt, + tx_blockheight, + PENALTY); + continue; + } /* BOLT #5: * @@ -3786,12 +3770,6 @@ static void handle_unknown_commitment(const struct tx_parts *tx, OUTPUT_TO_US, NULL, NULL, NULL); ignore_output(out); - if (!is_replay) - record_channel_withdrawal(&tx->txid, out, - tx_blockheight); - - add_amt(&amt_salvaged, amt); - tell_wallet_to_remote(tx, &outpoint, tx_blockheight, local_scripts[which_script], @@ -3800,11 +3778,11 @@ static void handle_unknown_commitment(const struct tx_parts *tx, csv); local_scripts[0] = local_scripts[1] = NULL; to_us_output = i; - goto script_found; + /* Even though we're finished, we keep rolling + * so we log all the outputs */ } } -script_found: if (to_us_output == -1) { status_broken("FUNDS LOST. Unknown commitment #%"PRIu64"!", commit_num); @@ -3816,11 +3794,6 @@ static void handle_unknown_commitment(const struct tx_parts *tx, htlcs_info = init_reply(tx, "ERROR: Unknown commitment, recovering our funds!"); } - /* update our accounting notions for this channel. - * should result in a channel balance of zero */ - if (!is_replay) - update_ledger_unknown(&tx->txid, tx_blockheight, amt_salvaged); - /* Tell master to give up on HTLCs immediately. */ for (size_t i = 0; i < tal_count(htlcs_info->htlcs); i++) { u8 *msg; @@ -3919,13 +3892,11 @@ int main(int argc, char *argv[]) funding_sats, FUNDING_OUTPUT, NULL, NULL, NULL); - /* Record the funding output being spent */ - /* FIXME: use channel_id as "account name" */ - send_coin_mvt(take(new_coin_withdrawal(NULL, NULL, &tx->txid, - &funding, tx_blockheight, - our_msat))); - - + /* Record funding output spent */ + send_coin_mvt(take(new_coin_channel_close(NULL, &tx->txid, + &funding, tx_blockheight, + our_msat, + funding_sats))); status_debug("Remote per-commit point: %s", type_to_string(tmpctx, struct pubkey, diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 0ca11adadf90..325dd3d35cf4 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -113,49 +113,65 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx UNNEEDED, /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } -/* Generated stub for new_coin_journal_entry */ -struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, +/* Generated stub for new_coin_channel_close */ +struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, - struct amount_msat amount UNNEEDED, - bool is_credit UNNEEDED) -{ fprintf(stderr, "new_coin_journal_entry called!\n"); abort(); } -/* Generated stub for new_coin_onchain_htlc_sat */ -struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, + const struct amount_msat amount UNNEEDED, + const struct amount_sat output_val UNNEEDED) +{ fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } +/* Generated stub for new_coin_external_deposit */ +struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, - struct sha256 payment_hash UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - bool is_credit UNNEEDED) -{ fprintf(stderr, "new_coin_onchain_htlc_sat called!\n"); abort(); } -/* Generated stub for new_coin_penalty_sat */ -struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED) -{ fprintf(stderr, "new_coin_penalty_sat called!\n"); abort(); } -/* Generated stub for new_coin_withdrawal */ -struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_msat amount UNNEEDED) -{ fprintf(stderr, "new_coin_withdrawal called!\n"); abort(); } -/* Generated stub for new_coin_withdrawal_sat */ -struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); } +/* Generated stub for new_coin_external_spend */ +struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED) -{ fprintf(stderr, "new_coin_withdrawal_sat called!\n"); abort(); } + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } +/* Generated stub for new_coin_wallet_deposit */ +struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } +/* Generated stub for new_onchain_htlc_deposit */ +struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + struct sha256 *payment_hash UNNEEDED) +{ fprintf(stderr, "new_onchain_htlc_deposit called!\n"); abort(); } +/* Generated stub for new_onchain_htlc_withdraw */ +struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + struct sha256 *payment_hash UNNEEDED) +{ fprintf(stderr, "new_onchain_htlc_withdraw called!\n"); abort(); } +/* Generated stub for new_onchaind_deposit */ +struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_onchaind_deposit called!\n"); abort(); } +/* Generated stub for new_onchaind_withdraw */ +struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_txid *spend_txid UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 5924edea56ef..7762473503ea 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -136,49 +136,65 @@ struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, void memleak_remove_region(struct htable *memtable UNNEEDED, const void *p UNNEEDED, size_t bytelen UNNEEDED) { fprintf(stderr, "memleak_remove_region called!\n"); abort(); } -/* Generated stub for new_coin_journal_entry */ -struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, +/* Generated stub for new_coin_channel_close */ +struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, - struct amount_msat amount UNNEEDED, - bool is_credit UNNEEDED) -{ fprintf(stderr, "new_coin_journal_entry called!\n"); abort(); } -/* Generated stub for new_coin_onchain_htlc_sat */ -struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, + const struct amount_msat amount UNNEEDED, + const struct amount_sat output_val UNNEEDED) +{ fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } +/* Generated stub for new_coin_external_deposit */ +struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, - struct sha256 payment_hash UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - bool is_credit UNNEEDED) -{ fprintf(stderr, "new_coin_onchain_htlc_sat called!\n"); abort(); } -/* Generated stub for new_coin_penalty_sat */ -struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *txid UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED) -{ fprintf(stderr, "new_coin_penalty_sat called!\n"); abort(); } -/* Generated stub for new_coin_withdrawal */ -struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_msat amount UNNEEDED) -{ fprintf(stderr, "new_coin_withdrawal called!\n"); abort(); } -/* Generated stub for new_coin_withdrawal_sat */ -struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_txid *tx_txid UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); } +/* Generated stub for new_coin_external_spend */ +struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED) -{ fprintf(stderr, "new_coin_withdrawal_sat called!\n"); abort(); } + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } +/* Generated stub for new_coin_wallet_deposit */ +struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } +/* Generated stub for new_onchain_htlc_deposit */ +struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + struct sha256 *payment_hash UNNEEDED) +{ fprintf(stderr, "new_onchain_htlc_deposit called!\n"); abort(); } +/* Generated stub for new_onchain_htlc_withdraw */ +struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + struct sha256 *payment_hash UNNEEDED) +{ fprintf(stderr, "new_onchain_htlc_withdraw called!\n"); abort(); } +/* Generated stub for new_onchaind_deposit */ +struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_onchaind_deposit called!\n"); abort(); } +/* Generated stub for new_onchaind_withdraw */ +struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_txid *spend_txid UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } diff --git a/tests/plugins/coin_movements.py b/tests/plugins/coin_movements.py index dc92adeec018..ce617a770fed 100755 --- a/tests/plugins/coin_movements.py +++ b/tests/plugins/coin_movements.py @@ -22,21 +22,7 @@ def init(configuration, options, plugin): @plugin.subscribe("coin_movement") def notify_coin_movement(plugin, coin_movement, **kwargs): - idx = coin_movement['movement_idx'] - plugin.log("{} coins movement version: {}".format(idx, coin_movement['version'])) - plugin.log("{} coins node: {}".format(idx, coin_movement['node_id'])) - plugin.log("{} coins mvt_type: {}".format(idx, coin_movement['type'])) - plugin.log("{} coins account: {}".format(idx, coin_movement['account_id'])) - plugin.log("{} coins credit: {}".format(idx, coin_movement['credit'])) - plugin.log("{} coins debit: {}".format(idx, coin_movement['debit'])) - plugin.log("{} coins tag: {}".format(idx, coin_movement['tag'])) - plugin.log("{} coins timestamp: {}".format(idx, coin_movement['timestamp'])) - plugin.log("{} coins coin_type: {}".format(idx, coin_movement['coin_type'])) - - for f in ['payment_hash', 'utxo_txid', 'vout', 'txid', 'part_id', 'blockheight']: - if f in coin_movement: - plugin.log("{} coins {}: {}".format(idx, f, coin_movement[f])) - + plugin.log("coin movement: {}".format(coin_movement)) plugin.coin_moves.append(coin_movement) # we save to disk so that we don't get borked if the node restarts diff --git a/tests/test_closing.py b/tests/test_closing.py index ea0f448d45f1..b1d871d5e699 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -6,7 +6,8 @@ from utils import ( only_one, sync_blockheight, wait_for, TIMEOUT, account_balance, first_channel_id, closing_fee, TEST_NETWORK, - scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES + scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES, + check_utxos_channel, anchor_expected ) import os @@ -19,9 +20,11 @@ @pytest.mark.developer("Too slow without --dev-bitcoind-poll") -def test_closing(node_factory, bitcoind, chainparams): - l1, l2 = node_factory.line_graph(2) +def test_closing_simple(node_factory, bitcoind, chainparams): + coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') + l1, l2 = node_factory.line_graph(2, opts={'plugin': coin_mvt_plugin}) chan = l1.get_channel_scid(l2) + channel_id = first_channel_id(l1, l2) fee = closing_fee(3750, 2) if not chainparams['elements'] else 3603 l1.pay(l2, 200000000) @@ -98,6 +101,22 @@ def test_closing(node_factory, bitcoind, chainparams): assert l1.db_query("SELECT count(*) as c FROM channels;")[0]['c'] == 1 assert l2.db_query("SELECT count(*) as c FROM channels;")[0]['c'] == 1 + assert account_balance(l1, channel_id) == 0 + assert account_balance(l2, channel_id) == 0 + + expected_1 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('wallet', 'deposit', None, None)], + } + + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('wallet', 'deposit', None, None)], + } + tags = check_utxos_channel(l1, [channel_id], expected_1) + check_utxos_channel(l2, [channel_id], expected_2, tags) + def test_closing_while_disconnected(node_factory, bitcoind, executor): l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True}) @@ -604,8 +623,34 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): assert [o['status'] for o in outputs] == ['confirmed'] * 2 assert set([o['txid'] for o in outputs]) == txids + assert account_balance(l1, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + # l1 loses all of their channel balance to the peer, as penalties + expected_1 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'penalty', None, None), ('external', 'penalty', None, None)], + } + + # l2 sweeps all of l1's closing outputs + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')], + 'C': [('wallet', 'deposit', None, None)], + 'D': [('wallet', 'deposit', None, None)] + } + + if anchor_expected(): + expected_1['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('external', 'anchor', None, None)) + expected_1['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + # We use a subset of tags in expected_2 that are used in expected_1 + tags = check_utxos_channel(l1, [channel_id], expected_1) + check_utxos_channel(l2, [channel_id], expected_2, tags) + @pytest.mark.developer("needs DEVELOPER=1") def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): @@ -705,8 +750,34 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): assert [o['status'] for o in outputs] == ['confirmed'] * 3 assert set([o['txid'] for o in outputs]) == txids + assert account_balance(l1, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + # l1 loses all of their channel balance to the peer, as penalties + expected_1 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'penalty', None, None), ('external', 'penalty', None, None), ('external', 'penalty', None, None)], + } + + # l2 sweeps all of l1's closing outputs + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('wallet', 'channel_close', None, None), ('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')], + 'C': [('wallet', 'deposit', None, None)], + 'D': [('wallet', 'deposit', None, None)] + } + + if anchor_expected(): + expected_1['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('external', 'anchor', None, None)) + expected_1['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + # We use a subset of tags in expected_2 that are used in expected_1 + tags = check_utxos_channel(l1, [channel_id], expected_1) + check_utxos_channel(l2, [channel_id], expected_2, tags) + @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') @@ -1173,7 +1244,6 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): # reconnect with l1, which will fulfill the payment l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l2.daemon.wait_for_log('got commitsig .*: feerate 11000, blockheight: 0, 0 added, 1 fulfilled, 0 failed, 0 changed') - l2.daemon.wait_for_log('coins payment_hash: {}'.format(sticky_inv['payment_hash'])) # l2 moves on for closed l3 bitcoind.generate_block(1) @@ -1212,6 +1282,29 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): assert account_balance(l3, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'to_them', None, None), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('external', 'penalized', None, None)], + 'C': [('external', 'penalized', None, None)], + } + + expected_3 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('wallet', 'channel_close', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('cid1', 'penalty', 'to_wallet', 'E')], + 'C': [('cid1', 'penalty', 'to_wallet', 'D')], + 'D': [('wallet', 'deposit', None, None)], + 'E': [('wallet', 'deposit', None, None)] + } + + if anchor_expected(): + expected_2['B'].append(('external', 'anchor', None, None)) + expected_3['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_3['B'].append(('wallet', 'anchor', None, None)) + + tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) + check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) + @pytest.mark.developer("needs DEVELOPER=1") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") @@ -1329,7 +1422,6 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): # reconnect with l1, which will fulfill the payment l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l2.daemon.wait_for_log('got commitsig .*: feerate 11000, blockheight: 0, 0 added, 1 fulfilled, 0 failed, 0 changed') - l2.daemon.wait_for_log('coins payment_hash: {}'.format(sticky_inv_2['payment_hash'])) # l2 moves on for closed l3 bitcoind.generate_block(1, wait_for_mempool=1) @@ -1396,12 +1488,40 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): assert account_balance(l3, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'to_them', None, None), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'E'), ('cid1', 'delayed_to_us', 'to_wallet', 'F'), ('cid1', 'htlc_timeout', 'htlc_timeout', 'C')], + 'C': [('external', 'penalized', None, None)], + 'E': [('cid1', 'htlc_tx', 'to_wallet', 'G')], + 'F': [('wallet', 'deposit', None, None)], + 'G': [('wallet', 'deposit', None, None)] + } + + expected_3 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('wallet', 'channel_close', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'E'), ('external', 'stolen', None, None), ('external', 'htlc_timeout', 'htlc_timeout', 'C')], + 'C': [('cid1', 'penalty', 'to_wallet', 'D')], + 'D': [('wallet', 'deposit', None, None)], + 'E': [('external', 'stolen', None, None)] + } + + if anchor_expected(): + expected_2['B'].append(('external', 'anchor', None, None)) + expected_3['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_3['B'].append(('wallet', 'anchor', None, None)) + + tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) + check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) + @pytest.mark.developer("uses dev_sign_last_tx") def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams): ''' Test that penalty transactions are RBFed. ''' + # We track channel balances, to verify that accounting is ok. + coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') to_self_delay = 10 # l1 is the thief, which causes our honest upstanding lightningd # code to break, so l1 can fail. @@ -1409,10 +1529,12 @@ def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams): l1 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], may_fail=True, allow_broken_log=True) l2 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], - options={'watchtime-blocks': to_self_delay}) + options={'watchtime-blocks': to_self_delay, + 'plugin': coin_mvt_plugin}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, 10**7) + channel_id = first_channel_id(l1, l2) # Trigger an HTLC being added. t = executor.submit(l1.pay, l2, 1000000 * 1000) @@ -1501,6 +1623,21 @@ def get_rbf_tx(self, depth, name, resolve): # And l2 should consider it in its listfunds. assert(len(l2.rpc.listfunds()['outputs']) >= 1) + assert account_balance(l2, channel_id) == 0 + + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')], + 'C': [('wallet', 'deposit', None, None)], + 'D': [('wallet', 'deposit', None, None)] + } + + if anchor_expected(): + expected_2['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + check_utxos_channel(l2, [channel_id], expected_2) + @pytest.mark.developer("uses dev_sign_last_tx") def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams): @@ -1508,6 +1645,8 @@ def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams): Test that penalty transactions are RBFed and we are willing to burn it all up to spite the thief. ''' + # We track channel balances, to verify that accounting is ok. + coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') to_self_delay = 10 # l1 is the thief, which causes our honest upstanding lightningd # code to break, so l1 can fail. @@ -1515,10 +1654,12 @@ def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams): l1 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], may_fail=True, allow_broken_log=True) l2 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], - options={'watchtime-blocks': to_self_delay}) + options={'watchtime-blocks': to_self_delay, + 'plugin': coin_mvt_plugin}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, 10**7) + channel_id = first_channel_id(l1, l2) # Trigger an HTLC being added. t = executor.submit(l1.pay, l2, 1000000 * 1000) @@ -1605,6 +1746,18 @@ def get_rbf_tx(self, depth, name, resolve): # l2 donated it to the miners, so it owns nothing assert(len(l2.rpc.listfunds()['outputs']) == 0) + assert account_balance(l2, channel_id) == 0 + + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('cid1', 'penalty', 'to_miner', 'C'), ('cid1', 'penalty', 'to_miner', 'D')], + } + + if anchor_expected(): + expected_2['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + check_utxos_channel(l2, [channel_id], expected_2) @pytest.mark.developer("needs DEVELOPER=1") @@ -1912,9 +2065,37 @@ def test_onchain_timeout(node_factory, bitcoind, executor): assert account_balance(l1, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + # Graph of coin_move events we expect + expected_1 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('cid1', 'delayed_to_us', 'to_wallet', 'C'), ('cid1', 'htlc_timeout', 'htlc_timeout', 'D')], + 'C': [('wallet', 'deposit', None, None)], + 'D': [('cid1', 'htlc_tx', 'to_wallet', 'E')], + 'E': [('wallet', 'deposit', None, None)] + } + + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)] + } + + if anchor_expected(): + expected_1['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('external', 'anchor', None, None)) + expected_1['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + # We use a subset of tags in expected_2 that are used in expected_1 + tags = check_utxos_channel(l1, [channel_id], expected_1) + # Passing the same tags in to the check again will verify that the + # txids 'unify' across both event sets (in other words, we're talking + # about the same tx's when we say 'A' in each + check_utxos_channel(l2, [channel_id], expected_2, tags) + @pytest.mark.developer("needs DEVELOPER=1") -def test_onchain_middleman(node_factory, bitcoind): +def test_onchain_middleman_simple(node_factory, bitcoind): # We track channel balances, to verify that accounting is ok. coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') @@ -1999,6 +2180,35 @@ def try_pay(): assert account_balance(l1, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + # Graph of coin_move events we expect + expected_2 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + # This is ugly, but this wallet deposit is either unspent or used + # in the next channel open + 'A': [('wallet', 'deposit', [('withdrawal', 'F'), (None, None)]), ('cid1', 'channel_open', 'channel_close', 'B')], + '1': [('wallet', 'deposit', 'withdrawal', 'F')], + 'B': [('cid1', 'delayed_to_us', 'to_wallet', 'C'), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'D'), ('external', 'to_them', None, None)], + 'C': [('wallet', 'deposit', None, None)], + 'D': [('cid1', 'htlc_tx', 'to_wallet', 'E')], + 'E': [('wallet', 'deposit', None, None)], + 'F': [('wallet', 'deposit', None, None), ('cid2', 'channel_open', None, None)] + } + + expected_1 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'to_them', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'D'), ('wallet', 'channel_close', None, None)] + } + + if anchor_expected(): + expected_1['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('external', 'anchor', None, None)) + expected_1['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + chan2_id = first_channel_id(l2, l3) + tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2) + check_utxos_channel(l1, [channel_id, chan2_id], expected_1, tags) + @pytest.mark.developer("needs DEVELOPER=1") def test_onchain_middleman_their_unilateral_in(node_factory, bitcoind): @@ -2091,6 +2301,34 @@ def try_pay(): assert account_balance(l1, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + # Graph of coin_move events we expect + expected_2 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + # This is ugly, but this wallet deposit is either unspent or used + # in the next channel open + 'A': [('wallet', 'deposit', [('withdrawal', 'D'), (None, None)]), ('cid1', 'channel_open', 'channel_close', 'B')], + '1': [('wallet', 'deposit', 'withdrawal', 'D')], + 'B': [('external', 'to_them', None, None), ('wallet', 'channel_close', None, None), ('cid1', 'htlc_fulfill', 'to_wallet', 'C')], + 'C': [('wallet', 'deposit', None, None)], + 'D': [('wallet', 'deposit', None, None), ('cid2', 'channel_open', None, None)] + } + + expected_1 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'to_them', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('cid1', 'delayed_to_us', 'to_wallet', 'E')], + 'E': [('wallet', 'deposit', None, None)] + } + + if anchor_expected(): + expected_1['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('external', 'anchor', None, None)) + expected_1['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + chan2_id = first_channel_id(l2, l3) + tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2) + check_utxos_channel(l1, [channel_id, chan2_id], expected_1, tags) + @pytest.mark.developer("needs DEVELOPER=1") def test_onchain_their_unilateral_out(node_factory, bitcoind): @@ -2156,6 +2394,30 @@ def try_pay(): assert account_balance(l2, channel_id) == 0 assert account_balance(l1, channel_id) == 0 + # Graph of coin_move events we expect + expected_1 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + # This is ugly, but this wallet deposit is either unspent or used + # in the next channel open + 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('wallet', 'channel_close', None, None), ('cid1', 'htlc_timeout', 'to_wallet', 'C')], + 'C': [('wallet', 'deposit', None, None)], + } + + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)], + } + + if anchor_expected(): + expected_1['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('external', 'anchor', None, None)) + expected_1['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + tags = check_utxos_channel(l1, [channel_id], expected_1) + check_utxos_channel(l2, [channel_id], expected_2, tags) + def test_listfunds_after_their_unilateral(node_factory, bitcoind): """We keep spending info around for their unilateral closes. @@ -2336,6 +2598,28 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): assert account_balance(l1, channel_id) == 0 assert account_balance(l2, channel_id) == 0 + # Graph of coin_move events we expect + expected_1 = { + '0': [('wallet', 'deposit', 'withdrawal', 'A')], + 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('wallet', 'channel_close', None, None), ('cid1', 'htlc_timeout', 'ignored', 'C')], + 'C': [('wallet', 'deposit', None, None)], + } + + expected_2 = { + 'A': [('cid1', 'channel_open', 'channel_close', 'B')], + 'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)], + } + + if anchor_expected(): + expected_1['B'].append(('external', 'anchor', None, None)) + expected_2['B'].append(('external', 'anchor', None, None)) + expected_1['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('wallet', 'anchor', None, None)) + + tags = check_utxos_channel(l1, [channel_id], expected_1) + check_utxos_channel(l2, [channel_id], expected_2, tags) + @pytest.mark.developer("needs DEVELOPER=1 for dev_fail") def test_onchain_different_fees(node_factory, bitcoind, executor): diff --git a/tests/test_connection.py b/tests/test_connection.py index eb62b1b9322b..dd30de9ccfb9 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1101,12 +1101,11 @@ def test_funding_push(node_factory, bitcoind, chainparams): assert funds['channel_sat'] + push_sat == funds['channel_total_sat'] chanid = first_channel_id(l2, l1) - l1.daemon.wait_for_log('coins account: {}'.format(chanid)) # give the file write a second time.sleep(1) channel_mvts = [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 20000000, 'tag': 'pushed'}, - {'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tag': 'channel_open'}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tag': 'pushed'}, ] check_coin_moves(l1, chanid, channel_mvts, chainparams) assert account_balance(l1, chanid) == (amount - push_sat) * 1000 diff --git a/tests/test_misc.py b/tests/test_misc.py index b7e2120cd62a..feb83ae0f16b 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -10,7 +10,7 @@ wait_for, TailableProc, env ) from utils import ( - check_coin_moves, account_balance, scriptpubkey_addr, + account_balance, scriptpubkey_addr ) from ephemeral_port_reserve import reserve from utils import EXPERIMENTAL_FEATURES @@ -623,64 +623,6 @@ def dont_spend_outputs(n, txid): sync_blockheight(bitcoind, [l1]) assert account_balance(l1, 'wallet') == 0 - wallet_moves = [ - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1993760000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 6240000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 1993760000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1993760000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 6240000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 1993760000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1993760000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 6240000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 1993760000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 1993400000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 6600000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 1993400000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 11961240000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 13440000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 11961240000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 11957603000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 3637000, 'tag': 'chain_fees'}, - ] - check_coin_moves(l1, 'wallet', wallet_moves, chainparams) - def test_io_logging(node_factory, executor): l1 = node_factory.get_node(options={'log-level': 'io'}) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index d80c6febbec2..c4c24f73841e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -10,7 +10,7 @@ DEPRECATED_APIS, expected_peer_features, expected_node_features, expected_channel_features, account_balance, check_coin_moves, first_channel_id, check_coin_moves_idx, - EXPERIMENTAL_FEATURES, EXPERIMENTAL_DUAL_FUND + EXPERIMENTAL_DUAL_FUND ) import ast @@ -1885,84 +1885,23 @@ def test_plugin_fail(node_factory): @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_coin_movement_notices(node_factory, bitcoind, chainparams): - """Verify that coin movements are triggered correctly. - """ + """Verify that channel coin movements are triggered correctly. """ l1_l2_mvts = [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'deposit'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'channel_open'}, {'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tag': 'routed'}, {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tag': 'routed'}, {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tag': 'invoice'}, {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tag': 'invoice'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 100001000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 100001001, 'tag': 'channel_close'}, + ] + + l2_l3_mvts = [ + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'channel_open'}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'}, + {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tag': 'channel_close'}, ] - if chainparams['elements']: - l2_l3_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'}, - {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 4271501, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 945729000, 'tag': 'withdrawal'}, - ] - - l2_wallet_mvts = [ - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 991908000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 8092000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 991908000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 945729000, 'debit': 0, 'tag': 'deposit'}, - ] - elif EXPERIMENTAL_FEATURES: - # option_anchor_outputs - l2_l3_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'}, - {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 2520501, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 947480000, 'tag': 'withdrawal'}, - ] - - l2_wallet_mvts = [ - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - # Could go in either order - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 995433000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 4567000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 995433000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 947480000, 'debit': 0, 'tag': 'deposit'}, - ] - else: - l2_l3_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'}, - {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 2520501, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 947480000, 'tag': 'withdrawal'}, - ] - - l2_wallet_mvts = [ - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - # Could go in either order - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 995433000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 4567000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 995433000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 947480000, 'debit': 0, 'tag': 'deposit'}, - ] l1, l2, l3 = node_factory.line_graph(3, opts=[ {'may_reconnect': True}, @@ -1970,30 +1909,6 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): {'may_reconnect': True}, ], wait_for_announce=True) - # Special case for dual-funded channel opens - if l2.config('experimental-dual-fund'): - # option_anchor_outputs - l2_l3_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'}, - {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 2520501, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 947480000, 'tag': 'withdrawal'}, - ] - l2_wallet_mvts = [ - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - # Could go in either order - [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 995410000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - ], - {'type': 'chain_mvt', 'credit': 0, 'debit': 4590000, 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 995410000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 947480000, 'debit': 0, 'tag': 'deposit'}, - ] - bitcoind.generate_block(5) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 4) amount = 10**8 @@ -2071,7 +1986,6 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): # Verify we recorded all the movements we expect check_coin_moves(l2, chanid_1, l1_l2_mvts, chainparams) check_coin_moves(l2, chanid_3, l2_l3_mvts, chainparams) - check_coin_moves(l2, 'wallet', l2_wallet_mvts, chainparams) check_coin_moves_idx(l2) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 695c8b37c40a..812b8654fbb3 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -862,18 +862,14 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000 + int(out_1_ms), 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000 - int(out_1_ms), 'tag': 'chain_fees'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 4000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'chain_fees'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, ] check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams) diff --git a/tests/utils.py b/tests/utils.py index 2e1436987abd..960305e57f6d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,6 +8,10 @@ COMPAT = env("COMPAT", "1") == "1" +def anchor_expected(): + return EXPERIMENTAL_FEATURES or EXPERIMENTAL_DUAL_FUND + + def hex_bits(features): # We always to full bytes flen = (max(features + [0]) + 7) // 8 * 8 @@ -149,6 +153,155 @@ def account_balance(n, account_id): return m_sum +def extract_utxos(moves): + utxos = {} + for m in moves: + if 'utxo_txid' not in m: + continue + txid = m['utxo_txid'] + if txid not in utxos: + utxos[txid] = [] + + if 'txid' not in m: + utxos[txid].append([m, None]) + else: + evs = utxos[txid] + # it's a withdrawal, find the deposit and add to the pair + for ev in evs: + if ev[0]['vout'] == m['vout']: + ev[1] = m + assert ev[0]['output_value'] == m['output_value'] + break + return utxos + + +def print_utxos(utxos): + for k, us in utxos.items(): + print(k) + for u in us: + if u[1]: + print('\t', u[0]['account_id'], u[0]['tag'], u[1]['tag'], u[1]['txid']) + else: + print('\t', u[0]['account_id'], u[0]['tag'], None, None) + + +def utxos_for_channel(utxoset, channel_id): + relevant_txids = [] + chan_utxos = {} + + def _add_relevant(txid, utxo): + if txid not in chan_utxos: + chan_utxos[txid] = [] + chan_utxos[txid].append(utxo) + + for txid, utxo_list in utxoset.items(): + for utxo in utxo_list: + if utxo[0]['account_id'] == channel_id: + _add_relevant(txid, utxo) + relevant_txids.append(txid) + if utxo[1]: + relevant_txids.append(utxo[1]['txid']) + elif txid in relevant_txids: + _add_relevant(txid, utxo) + if utxo[1]: + relevant_txids.append(utxo[1]['txid']) + + # if they're not well ordered, we'll leave some txids out + for txid in relevant_txids: + if txid not in chan_utxos: + chan_utxos[txid] = utxoset[txid] + + return chan_utxos + + +def matchup_events(u_set, evs, chans, tag_list): + assert len(u_set) == len(evs) and len(u_set) > 0 + + txid = u_set[0][0]['utxo_txid'] + for ev in evs: + found = False + for u in u_set: + # We use 'cid' as a placeholder for the channel id, since it's + # dyanmic, but we need to sub it in. 'chans' is a list of cids, + # which are mapped to `cid` tags' suffixes. eg. 'cid1' is the + # first cid in the chans list + if ev[0][:3] == 'cid': + idx = int(ev[0][3:]) + acct = chans[idx - 1] + else: + acct = ev[0] + + if u[0]['account_id'] != acct or u[0]['tag'] != ev[1]: + continue + + if ev[2] is None: + assert u[1] is None + found = True + u_set.remove(u) + break + + # ugly hack to annotate two possible futures for a utxo + if type(ev[2]) is list: + tag = u[1]['tag'] if u[1] else u[1] + assert tag in [x[0] for x in ev[2]] + if not u[1]: + found = True + u_set.remove(u) + break + for x in ev[2]: + if x[0] == u[1]['tag'] and u[1]['tag'] != 'to_miner': + # Save the 'spent to' txid in the tag-list + tag_list[x[1]] = u[1]['txid'] + else: + assert ev[2] == u[1]['tag'] + # Save the 'spent to' txid in the tag-list + if u[1]['tag'] != 'to_miner': + tag_list[ev[3]] = u[1]['txid'] + + found = True + u_set.remove(u) + + assert found + + # Verify we used them all up + assert len(u_set) == 0 + return txid + + +def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=None): + tag_list = {} + moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] + utxos = extract_utxos(moves) + + if filter_channel: + utxos = utxos_for_channel(utxos, filter_channel) + + for tag, evs in expected.items(): + if tag not in tag_list: + u_set = list(utxos.values())[0] + elif tag in tag_list: + u_set = utxos[tag_list[tag]] + + txid = matchup_events(u_set, evs, chans, tag_list) + + if tag not in tag_list: + tag_list[tag] = txid + + # Remove checked set from utxos + del utxos[txid] + + # Verify that we went through all of the utxos + assert len(utxos) == 0 + + # Verify that the expected tags match the found tags + if exp_tag_list: + for tag, txid in tag_list.items(): + if tag in exp_tag_list: + assert exp_tag_list[tag] == txid + + return tag_list + + def first_channel_id(n1, n2): return only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['channel_id'] diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 2d65f6e7f0ed..6e9a14240b1d 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -441,13 +441,13 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx UNNEEDED, struct htlc_out *hout UNNEEDED, struct channel *channel UNNEEDED) { fprintf(stderr, "new_channel_mvt_routed_hout called!\n"); abort(); } -/* Generated stub for new_coin_deposit_sat */ -struct chain_coin_mvt *new_coin_deposit_sat(const tal_t *ctx UNNEEDED, - const char *account_name UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED) -{ fprintf(stderr, "new_coin_deposit_sat called!\n"); abort(); } +/* Generated stub for new_coin_wallet_deposit */ +struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) { fprintf(stderr, "notify_chain_mvt called!\n"); abort(); } diff --git a/wallet/wallet.c b/wallet/wallet.c index 4d13d01bb774..1c4b923b92bc 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2253,10 +2253,10 @@ int wallet_extract_owned_outputs(struct wallet *w, const struct wally_tx *wtx, /* We only record final ledger movements */ if (blockheight) { - mvt = new_coin_deposit_sat(utxo, "wallet", - &utxo->outpoint, - blockheight ? *blockheight : 0, - utxo->amount); + mvt = new_coin_wallet_deposit(tmpctx, &utxo->outpoint, + *blockheight, + utxo->amount, + DEPOSIT); notify_chain_mvt(w->ld, mvt); } From 27ff87c045db07f1391caa10e62c50c94ec2f318 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 1 Dec 2021 10:08:32 -0600 Subject: [PATCH 0159/1530] coin_moves: de-dupe moves as they come in test_onchain_dust_out restarts a node, which produces duplicate events. this is expected, but we need to de-duplicate the events stream to get accurate results --- tests/utils.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/utils.py b/tests/utils.py index 960305e57f6d..74bf7d2b3ff8 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -143,7 +143,7 @@ def check_coin_moves_idx(n): def account_balance(n, account_id): - moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] + moves = dedupe_moves(n.rpc.call('listcoinmoves_plugin')['coin_moves']) chan_moves = [m for m in moves if m['account_id'] == account_id] assert len(chan_moves) > 0 m_sum = 0 @@ -268,10 +268,27 @@ def matchup_events(u_set, evs, chans, tag_list): return txid +def dedupe_moves(moves): + move_set = {} + deduped_moves = [] + for move in moves: + # Dupes only pertain to onchain moves? + if 'utxo_txid' not in move: + deduped_moves.append(move) + continue + + outpoint = '{}:{};{}'.format(move['utxo_txid'], move['vout'], move['txid'] if 'txid' in move else 'xx') + if outpoint not in move_set: + deduped_moves.append(move) + move_set[outpoint] = move + return deduped_moves + + def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=None): tag_list = {} moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] - utxos = extract_utxos(moves) + + utxos = extract_utxos(dedupe_moves(moves)) if filter_channel: utxos = utxos_for_channel(utxos, filter_channel) From 8098a4cd212870be2e50ab6eb0efda28c2602b90 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 1 Dec 2021 10:24:31 -0600 Subject: [PATCH 0160/1530] onchaind: remove 'is_replay' logic we used this originally to suppress duplicate issuance of coin-move events; we're assuming that any plugin expects duplicate events though (and knows how to de-dupe them), so we no longer need this logic. --- lightningd/onchain_control.c | 41 ++--- lightningd/onchain_control.h | 3 +- lightningd/peer_control.c | 2 +- lightningd/peer_htlcs.c | 2 +- lightningd/test/run-invoice-select-inchan.c | 3 +- onchaind/onchaind.c | 158 ++++++++------------ onchaind/onchaind_wire.csv | 4 - onchaind/test/run-grind_feerate-bug.c | 12 +- onchaind/test/run-grind_feerate.c | 8 +- wallet/test/run-wallet.c | 5 +- 10 files changed, 87 insertions(+), 151 deletions(-) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index ae263a7ba87c..bd17246fbdfb 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -52,21 +52,7 @@ static void onchaind_tell_fulfill(struct channel *channel) if (!hin->preimage) continue; - /* Sooo these are *probably* replays since they're coming - * from the database but it's hard to be sure since we update - * the database before notifying onchaind about them. - * There's a *very* rare chance that we'll not log them, - * only in that we only make ledger records as a result of this call - * iff the output isn't deemed 'trackable'. So if we do miss a - * ledger record as a result of this decision, it's guaranteed to be - * impreceptibly tiny *and* not show up anywhere else in the node's - * utxo set. - * - * Aka a reconciliator's nightmare. - * The alternative is to double-count *every* ignored htlc output - * It's easier to delete than find a missing, but I'm banking on - * the rarity of failure here. (hahaha) */ - msg = towire_onchaind_known_preimage(channel, hin->preimage, false); + msg = towire_onchaind_known_preimage(channel, hin->preimage); subd_send_msg(channel->owner, take(msg)); } } @@ -152,11 +138,10 @@ static void handle_onchain_init_reply(struct channel *channel, const u8 *msg) */ static void onchain_tx_depth(struct channel *channel, const struct bitcoin_txid *txid, - unsigned int depth, - bool is_replay) + unsigned int depth) { u8 *msg; - msg = towire_onchaind_depth(channel, txid, depth, is_replay); + msg = towire_onchaind_depth(channel, txid, depth); subd_send_msg(channel->owner, take(msg)); } @@ -199,7 +184,7 @@ static enum watch_result onchain_tx_watched(struct lightningd *ld, wallet_channeltxs_add(ld->wallet, channel, WIRE_ONCHAIND_DEPTH, txid, 0, blockheight); - onchain_tx_depth(channel, txid, depth, false); + onchain_tx_depth(channel, txid, depth); return KEEP_WATCHING; } @@ -209,7 +194,7 @@ static void watch_tx_and_outputs(struct channel *channel, /** * Notify onchaind that an output was spent and register new watches. */ -static void onchain_txo_spent(struct channel *channel, const struct bitcoin_tx *tx, size_t input_num, u32 blockheight, bool is_replay) +static void onchain_txo_spent(struct channel *channel, const struct bitcoin_tx *tx, size_t input_num, u32 blockheight) { u8 *msg; /* Onchaind needs all inputs, since it uses those to compare @@ -219,7 +204,7 @@ static void onchain_txo_spent(struct channel *channel, const struct bitcoin_tx * watch_tx_and_outputs(channel, tx); - msg = towire_onchaind_spent(channel, parts, input_num, blockheight, is_replay); + msg = towire_onchaind_spent(channel, parts, input_num, blockheight); subd_send_msg(channel->owner, take(msg)); } @@ -240,7 +225,7 @@ static enum watch_result onchain_txo_watched(struct channel *channel, WIRE_ONCHAIND_SPENT, &txid, input_num, block->height); - onchain_txo_spent(channel, tx, input_num, block->height, false); + onchain_txo_spent(channel, tx, input_num, block->height); /* We don't need to keep watching: If this output is double-spent * (reorg), we'll get a zero depth cb to onchain_tx_watched, and @@ -590,8 +575,7 @@ static void onchain_error(struct channel *channel, * onchaind (like any other owner), and restart */ enum watch_result onchaind_funding_spent(struct channel *channel, const struct bitcoin_tx *tx, - u32 blockheight, - bool is_replay) + u32 blockheight) { u8 *msg; struct bitcoin_txid our_last_txid; @@ -727,7 +711,6 @@ enum watch_result onchaind_funding_spent(struct channel *channel, channel->static_remotekey_start[LOCAL], channel->static_remotekey_start[REMOTE], channel_has(channel, OPT_ANCHOR_OUTPUTS), - is_replay, feerate_min(ld, NULL)); subd_send_msg(channel->owner, take(msg)); @@ -757,18 +740,16 @@ void onchaind_replay_channels(struct lightningd *ld) for (size_t j = 0; j < tal_count(txs); j++) { if (txs[j].type == WIRE_ONCHAIND_INIT) { onchaind_funding_spent(chan, txs[j].tx, - txs[j].blockheight, - true); + txs[j].blockheight); } else if (txs[j].type == WIRE_ONCHAIND_SPENT) { onchain_txo_spent(chan, txs[j].tx, txs[j].input_num, - txs[j].blockheight, - true); + txs[j].blockheight); } else if (txs[j].type == WIRE_ONCHAIND_DEPTH) { onchain_tx_depth(chan, &txs[j].txid, - txs[j].depth, true); + txs[j].depth); } else { fatal("unknown message of type %d during " diff --git a/lightningd/onchain_control.h b/lightningd/onchain_control.h index dedd09e300f6..c0fe3d3b09dd 100644 --- a/lightningd/onchain_control.h +++ b/lightningd/onchain_control.h @@ -9,8 +9,7 @@ struct block; enum watch_result onchaind_funding_spent(struct channel *channel, const struct bitcoin_tx *tx, - u32 blockheight, - bool is_replay); + u32 blockheight); void onchaind_replay_channels(struct lightningd *ld); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 415b4d69f467..4fc9279fe2e4 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1365,7 +1365,7 @@ static enum watch_result funding_spent(struct channel *channel, wallet_channeltxs_add(channel->peer->ld->wallet, channel, WIRE_ONCHAIND_INIT, &txid, 0, block->height); - return onchaind_funding_spent(channel, tx, block->height, false); + return onchaind_funding_spent(channel, tx, block->height); } void channel_watch_wrong_funding(struct lightningd *ld, struct channel *channel) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 5914deb25829..43c0682438f8 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -431,7 +431,7 @@ void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage) } if (channel_on_chain(channel)) { - msg = towire_onchaind_known_preimage(hin, preimage, false); + msg = towire_onchaind_known_preimage(hin, preimage); } else { struct fulfilled_htlc fulfilled_htlc; fulfilled_htlc.id = hin->key.id; diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 242b8b689de5..8a8e54769709 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -476,8 +476,7 @@ void notify_invoice_payment(struct lightningd *ld UNNEEDED, struct amount_msat a /* Generated stub for onchaind_funding_spent */ enum watch_result onchaind_funding_spent(struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, - u32 blockheight UNNEEDED, - bool is_replay UNNEEDED) + u32 blockheight UNNEEDED) { fprintf(stderr, "onchaind_funding_spent called!\n"); abort(); } /* Generated stub for param */ bool param(struct command *cmd UNNEEDED, const char *buffer UNNEEDED, diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 01b7aa7f6504..24497197553f 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -1014,7 +1014,7 @@ static bool proposal_is_rbfable(const struct proposed_resolution *proposal) * @desc precondition: the given output must have an * rbfable proposal as per `proposal_is_rbfable`. */ -static void proposal_should_rbf(struct tracked_output *out, bool is_replay) +static void proposal_should_rbf(struct tracked_output *out) { struct bitcoin_tx *tx = NULL; u32 depth; @@ -1095,7 +1095,7 @@ static void proposal_should_rbf(struct tracked_output *out, bool is_replay) } } -static void proposal_meets_depth(struct tracked_output *out, bool is_replay) +static void proposal_meets_depth(struct tracked_output *out) { bool is_rbf = false; @@ -1139,8 +1139,7 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay) static void propose_resolution(struct tracked_output *out, const struct bitcoin_tx *tx, unsigned int depth_required, - enum tx_type tx_type, - bool is_replay) + enum tx_type tx_type) { status_debug("Propose handling %s/%s by %s (%s) after %u blocks", tx_type_name(out->tx_type), @@ -1155,14 +1154,13 @@ static void propose_resolution(struct tracked_output *out, out->proposal->tx_type = tx_type; if (depth_required == 0) - proposal_meets_depth(out, is_replay); + proposal_meets_depth(out); } static void propose_resolution_at_block(struct tracked_output *out, const struct bitcoin_tx *tx, unsigned int block_required, - enum tx_type tx_type, - bool is_replay) + enum tx_type tx_type) { u32 depth; @@ -1171,7 +1169,7 @@ static void propose_resolution_at_block(struct tracked_output *out, depth = 0; else /* Note that out->tx_blockheight is already at depth 1 */ depth = block_required - out->tx_blockheight + 1; - propose_resolution(out, tx, depth, tx_type, is_replay); + propose_resolution(out, tx, depth, tx_type); } static bool is_valid_sig(const u8 *e) @@ -1541,8 +1539,7 @@ static void resolve_htlc_tx(struct tracked_output ***outs, size_t out_index, const struct tx_parts *htlc_tx, size_t input_num, - u32 tx_blockheight, - bool is_replay) + u32 tx_blockheight) { struct tracked_output *out; struct bitcoin_tx *tx; @@ -1591,8 +1588,7 @@ static void resolve_htlc_tx(struct tracked_output ***outs, tx = tx_to_us(*outs, delayed_payment_to_us, out, to_self_delay[LOCAL], 0, NULL, 0, wscript, &tx_type, htlc_feerate); - propose_resolution(out, tx, to_self_delay[LOCAL], tx_type, - is_replay); + propose_resolution(out, tx, to_self_delay[LOCAL], tx_type); } /* BOLT #5: @@ -1607,8 +1603,7 @@ static void steal_htlc_tx(struct tracked_output *out, const struct tx_parts *htlc_tx, u32 htlc_tx_blockheight, enum tx_type htlc_tx_type, - const struct bitcoin_outpoint *htlc_outpoint, - bool is_replay) + const struct bitcoin_outpoint *htlc_outpoint) { struct bitcoin_tx *tx; enum tx_type tx_type = OUR_PENALTY_TX; @@ -1645,7 +1640,7 @@ static void steal_htlc_tx(struct tracked_output *out, resolved_by_other(out, &htlc_tx->txid, htlc_tx_type); /* annnd done! */ - propose_resolution(htlc_out, tx, 0, tx_type, is_replay); + propose_resolution(htlc_out, tx, 0, tx_type); } static void onchain_annotate_txout(const struct bitcoin_outpoint *outpoint, @@ -1666,8 +1661,7 @@ static void onchain_annotate_txin(const struct bitcoin_txid *txid, u32 innum, static void output_spent(struct tracked_output ***outs, const struct tx_parts *tx_parts, u32 input_num, - u32 tx_blockheight, - bool is_replay) + u32 tx_blockheight) { for (size_t i = 0; i < tal_count(*outs); i++) { struct tracked_output *out = (*outs)[i]; @@ -1686,7 +1680,7 @@ static void output_spent(struct tracked_output ***outs, if (out->resolved->tx_type == OUR_HTLC_SUCCESS_TX || out->resolved->tx_type == OUR_HTLC_TIMEOUT_TX) resolve_htlc_tx(outs, i, tx_parts, input_num, - tx_blockheight, is_replay); + tx_blockheight); record_coin_movements(out, tx_blockheight, out->proposal->tx, @@ -1715,8 +1709,7 @@ static void output_spent(struct tracked_output ***outs, steal_htlc_tx(out, outs, tx_parts, tx_blockheight, THEIR_HTLC_TIMEOUT_TO_THEM, - &htlc_outpoint, - is_replay); + &htlc_outpoint); } else { /* We ignore this timeout tx, since we should * resolve by ignoring once we reach depth. */ @@ -1752,8 +1745,7 @@ static void output_spent(struct tracked_output ***outs, steal_htlc_tx(out, outs, tx_parts, tx_blockheight, OUR_HTLC_FULFILL_TO_THEM, - &htlc_outpoint, - is_replay); + &htlc_outpoint); } else { /* BOLT #5: * @@ -1847,8 +1839,7 @@ static void update_resolution_depth(struct tracked_output *out, u32 depth) } static void tx_new_depth(struct tracked_output **outs, - const struct bitcoin_txid *txid, u32 depth, - bool is_replay) + const struct bitcoin_txid *txid, u32 depth) { size_t i; @@ -1882,7 +1873,7 @@ static void tx_new_depth(struct tracked_output **outs, if (outs[i]->proposal && bitcoin_txid_eq(&outs[i]->outpoint.txid, txid) && depth >= outs[i]->proposal->depth_required) { - proposal_meets_depth(outs[i], is_replay); + proposal_meets_depth(outs[i]); } /* Otherwise, is this an output whose proposed resolution @@ -1890,7 +1881,7 @@ static void tx_new_depth(struct tracked_output **outs, if (outs[i]->proposal && bitcoin_txid_eq(&outs[i]->outpoint.txid, txid) && proposal_is_rbfable(outs[i]->proposal)) - proposal_should_rbf(outs[i], is_replay); + proposal_should_rbf(outs[i]); } } @@ -1921,8 +1912,7 @@ static void tx_new_depth(struct tracked_output **outs, */ /* Master makes sure we only get told preimages once other node is committed. */ static void handle_preimage(struct tracked_output **outs, - const struct preimage *preimage, - bool is_replay) + const struct preimage *preimage) { size_t i; struct sha256 sha; @@ -1992,8 +1982,7 @@ static void handle_preimage(struct tracked_output **outs, tx, &sig, outs[i]->remote_htlc_sig, preimage, outs[i]->wscript); bitcoin_tx_input_set_witness(tx, 0, take(witness)); - propose_resolution(outs[i], tx, 0, OUR_HTLC_SUCCESS_TX, - is_replay); + propose_resolution(outs[i], tx, 0, OUR_HTLC_SUCCESS_TX); } else { enum tx_type tx_type = THEIR_HTLC_FULFILL_TO_US; @@ -2015,8 +2004,7 @@ static void handle_preimage(struct tracked_output **outs, 0, preimage, sizeof(*preimage), outs[i]->wscript, &tx_type, htlc_feerate); - propose_resolution(outs[i], tx, 0, tx_type, - is_replay); + propose_resolution(outs[i], tx, 0, tx_type); } } @@ -2080,7 +2068,6 @@ static void wait_for_resolved(struct tracked_output **outs) struct bitcoin_txid txid; u32 input_num, depth, tx_blockheight; struct preimage preimage; - bool is_replay; struct tx_parts *tx_parts; if (tal_count(queued_msgs)) { @@ -2092,13 +2079,13 @@ static void wait_for_resolved(struct tracked_output **outs) status_debug("Got new message %s", onchaind_wire_name(fromwire_peektype(msg))); - if (fromwire_onchaind_depth(msg, &txid, &depth, &is_replay)) - tx_new_depth(outs, &txid, depth, is_replay); + if (fromwire_onchaind_depth(msg, &txid, &depth)) + tx_new_depth(outs, &txid, depth); else if (fromwire_onchaind_spent(msg, msg, &tx_parts, &input_num, - &tx_blockheight, &is_replay)) { - output_spent(&outs, tx_parts, input_num, tx_blockheight, is_replay); - } else if (fromwire_onchaind_known_preimage(msg, &preimage, &is_replay)) - handle_preimage(outs, &preimage, is_replay); + &tx_blockheight)) { + output_spent(&outs, tx_parts, input_num, tx_blockheight); + } else if (fromwire_onchaind_known_preimage(msg, &preimage)) + handle_preimage(outs, &preimage); else if (!handle_dev_memleak(outs, msg)) master_badmsg(-1, msg); @@ -2221,8 +2208,7 @@ static u8 **derive_htlc_scripts(const struct htlc_stub *htlcs, enum side side) static size_t resolve_our_htlc_ourcommit(struct tracked_output *out, const size_t *matches, const struct htlc_stub *htlcs, - u8 **htlc_scripts, - bool is_replay) + u8 **htlc_scripts) { struct bitcoin_tx *tx = NULL; struct bitcoin_signature localsig; @@ -2311,7 +2297,7 @@ static size_t resolve_our_htlc_ourcommit(struct tracked_output *out, /* Steals tx onto out */ propose_resolution_at_block(out, tx, htlcs[matches[i]].cltv_expiry, - OUR_HTLC_TIMEOUT_TX, is_replay); + OUR_HTLC_TIMEOUT_TX); return matches[i]; } @@ -2333,8 +2319,7 @@ static u32 matches_cltv(const size_t *matches, static size_t resolve_our_htlc_theircommit(struct tracked_output *out, const size_t *matches, const struct htlc_stub *htlcs, - u8 **htlc_scripts, - bool is_replay) + u8 **htlc_scripts) { struct bitcoin_tx *tx; enum tx_type tx_type = OUR_HTLC_TIMEOUT_TO_US; @@ -2355,7 +2340,7 @@ static size_t resolve_our_htlc_theircommit(struct tracked_output *out, cltv_expiry, NULL, 0, htlc_scripts[matches[0]], &tx_type, htlc_feerate); - propose_resolution_at_block(out, tx, cltv_expiry, tx_type, is_replay); + propose_resolution_at_block(out, tx, cltv_expiry, tx_type); /* They're all equivalent: might as well use first one. */ return matches[0]; @@ -2365,8 +2350,7 @@ static size_t resolve_our_htlc_theircommit(struct tracked_output *out, static size_t resolve_their_htlc(struct tracked_output *out, const size_t *matches, const struct htlc_stub *htlcs, - u8 **htlc_scripts, - bool is_replay) + u8 **htlc_scripts) { size_t which_htlc; @@ -2403,7 +2387,7 @@ static size_t resolve_their_htlc(struct tracked_output *out, /* If we hit timeout depth, resolve by ignoring. */ propose_resolution_at_block(out, NULL, htlcs[which_htlc].cltv_expiry, - THEIR_HTLC_TIMEOUT_TO_THEM, is_replay); + THEIR_HTLC_TIMEOUT_TO_THEM); return which_htlc; } @@ -2513,8 +2497,7 @@ static void our_unilateral_to_us(struct tracked_output ***outs, struct amount_sat amt, u16 sequence, const u8 *local_scriptpubkey, - const u8 *local_wscript, - bool is_replay) + const u8 *local_wscript) { struct bitcoin_tx *to_us; struct tracked_output *out; @@ -2556,7 +2539,7 @@ static void our_unilateral_to_us(struct tracked_output ***outs, * Note: if the output is spent (as recommended), the * output is *resolved* by the spending transaction */ - propose_resolution(out, to_us, sequence, tx_type, is_replay); + propose_resolution(out, to_us, sequence, tx_type); } static void handle_our_unilateral(const struct tx_parts *tx, @@ -2564,8 +2547,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, const struct basepoints basepoints[NUM_SIDES], const enum side opener, const struct bitcoin_signature *remote_htlc_sigs, - struct tracked_output **outs, - bool is_replay) + struct tracked_output **outs) { u8 **htlc_scripts; u8 *local_wscript, *script[NUM_SIDES], *anchor[NUM_SIDES]; @@ -2682,7 +2664,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, our_unilateral_to_us(&outs, &outpoint, tx_blockheight, amt, to_self_delay[LOCAL], script[LOCAL], - local_wscript, is_replay); + local_wscript); script[LOCAL] = NULL; continue; @@ -2769,8 +2751,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, amt, max_unsigned(to_self_delay[LOCAL], csv), script[LOCAL], - local_wscript, - is_replay); + local_wscript); script[LOCAL] = NULL; found = true; @@ -2846,8 +2827,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, /* Tells us which htlc to use */ which_htlc = resolve_our_htlc_ourcommit(out, matches, htlcs_info->htlcs, - htlc_scripts, - is_replay); + htlc_scripts); } else { out = new_tracked_output(&outs, &outpoint, tx_blockheight, @@ -2865,8 +2845,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, /* Tells us which htlc to use */ which_htlc = resolve_their_htlc(out, matches, htlcs_info->htlcs, - htlc_scripts, - is_replay); + htlc_scripts); } out->htlc = htlcs_info->htlcs[which_htlc]; out->wscript = tal_steal(out, htlc_scripts[which_htlc]); @@ -2887,8 +2866,7 @@ static void handle_our_unilateral(const struct tx_parts *tx, /* We produce individual penalty txs. It's less efficient, but avoids them * using HTLC txs to block our penalties for long enough to pass the CSV * delay */ -static void steal_to_them_output(struct tracked_output *out, - u32 csv, bool is_replay) +static void steal_to_them_output(struct tracked_output *out, u32 csv) { u8 *wscript; struct bitcoin_tx *tx; @@ -2908,10 +2886,10 @@ static void steal_to_them_output(struct tracked_output *out, tx = tx_to_us(tmpctx, penalty_to_us, out, BITCOIN_TX_RBF_SEQUENCE, 0, &ONE, sizeof(ONE), wscript, &tx_type, penalty_feerate); - propose_resolution(out, tx, 0, tx_type, is_replay); + propose_resolution(out, tx, 0, tx_type); } -static void steal_htlc(struct tracked_output *out, bool is_replay) +static void steal_htlc(struct tracked_output *out) { struct bitcoin_tx *tx; enum tx_type tx_type = OUR_PENALTY_TX; @@ -2929,7 +2907,7 @@ static void steal_htlc(struct tracked_output *out, bool is_replay) der, sizeof(der), out->wscript, &tx_type, penalty_feerate); - propose_resolution(out, tx, 0, tx_type, is_replay); + propose_resolution(out, tx, 0, tx_type); } /* Tell wallet that we have discovered a UTXO from a to-remote output, @@ -2969,7 +2947,6 @@ static void their_unilateral_local(struct tracked_output ***outs, struct amount_sat amt, const u8 *local_scriptpubkey, enum tx_type tx_type, - bool is_replay, u32 csv_lock) { struct tracked_output *out; @@ -3012,8 +2989,7 @@ static void handle_their_cheat(const struct tx_parts *tx, const struct secret *revocation_preimage, const struct basepoints basepoints[NUM_SIDES], const enum side opener, - struct tracked_output **outs, - bool is_replay) + struct tracked_output **outs) { u8 **htlc_scripts; u8 *remote_wscript, *script[NUM_SIDES], *anchor[NUM_SIDES]; @@ -3171,8 +3147,7 @@ static void handle_their_cheat(const struct tx_parts *tx, their_unilateral_local(&outs, tx, &outpoint, tx_blockheight, amt, script[LOCAL], - THEIR_REVOKED_UNILATERAL, - is_replay, 1); + THEIR_REVOKED_UNILATERAL, 1); script[LOCAL] = NULL; continue; } @@ -3190,7 +3165,7 @@ static void handle_their_cheat(const struct tx_parts *tx, amt, DELAYED_CHEAT_OUTPUT_TO_THEM, NULL, NULL, NULL); - steal_to_them_output(out, 1, is_replay); + steal_to_them_output(out, 1); script[REMOTE] = NULL; continue; } @@ -3249,7 +3224,6 @@ static void handle_their_cheat(const struct tx_parts *tx, amt, script[LOCAL], THEIR_REVOKED_UNILATERAL, - is_replay, csv); script[LOCAL] = NULL; found = true; @@ -3280,8 +3254,7 @@ static void handle_their_cheat(const struct tx_parts *tx, amt, DELAYED_CHEAT_OUTPUT_TO_THEM, NULL, NULL, NULL); - steal_to_them_output(out, csv, - is_replay); + steal_to_them_output(out, csv); script[REMOTE] = NULL; found = true; break; @@ -3318,7 +3291,7 @@ static void handle_their_cheat(const struct tx_parts *tx, &htlcs_info->htlcs[which_htlc], htlc_scripts[which_htlc], NULL); - steal_htlc(out, is_replay); + steal_htlc(out); } else { out = new_tracked_output(&outs, &outpoint, tx_blockheight, @@ -3335,7 +3308,7 @@ static void handle_their_cheat(const struct tx_parts *tx, * * spend the *commitment tx* using the payment preimage (if known). * * spend the *HTLC-timeout tx*, if the remote node has published it. */ - steal_htlc(out, is_replay); + steal_htlc(out); } htlc_scripts[which_htlc] = NULL; } @@ -3351,8 +3324,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, const struct pubkey *this_remote_per_commitment_point, const struct basepoints basepoints[NUM_SIDES], const enum side opener, - struct tracked_output **outs, - bool is_replay) + struct tracked_output **outs) { u8 **htlc_scripts; u8 *remote_wscript, *script[NUM_SIDES], *anchor[NUM_SIDES]; @@ -3496,8 +3468,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, their_unilateral_local(&outs, tx, &outpoint, tx_blockheight, amt, script[LOCAL], - THEIR_UNILATERAL, - is_replay, 1); + THEIR_UNILATERAL, 1); script[LOCAL] = NULL; continue; @@ -3581,7 +3552,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, amt, script[LOCAL], THEIR_UNILATERAL, - is_replay, csv); + csv); script[LOCAL] = NULL; found = true; break; @@ -3649,8 +3620,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, which_htlc = resolve_our_htlc_theircommit(out, matches, htlcs_info->htlcs, - htlc_scripts, - is_replay); + htlc_scripts); } else { out = new_tracked_output(&outs, &outpoint, tx_blockheight, @@ -3667,7 +3637,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, */ which_htlc = resolve_their_htlc(out, matches, htlcs_info->htlcs, - htlc_scripts, is_replay); + htlc_scripts); } out->htlc = htlcs_info->htlcs[which_htlc]; out->wscript = tal_steal(out, htlc_scripts[which_htlc]); @@ -3684,8 +3654,7 @@ static void handle_unknown_commitment(const struct tx_parts *tx, u32 tx_blockheight, const struct pubkey *possible_remote_per_commitment_point, const struct basepoints basepoints[NUM_SIDES], - struct tracked_output **outs, - bool is_replay) + struct tracked_output **outs) { int to_us_output = -1; /* We have two possible local scripts, depending on options */ @@ -3829,7 +3798,6 @@ int main(int argc, char *argv[]) u8 *scriptpubkey[NUM_SIDES]; u32 locktime, tx_blockheight; struct pubkey *possible_remote_per_commitment_point; - bool open_is_replay; subdaemon_setup(argc, argv); @@ -3872,7 +3840,6 @@ int main(int argc, char *argv[]) &static_remotekey_start[LOCAL], &static_remotekey_start[REMOTE], &option_anchor_outputs, - &open_is_replay, &min_relay_feerate)) { master_badmsg(WIRE_ONCHAIND_INIT, msg); } @@ -3941,8 +3908,7 @@ int main(int argc, char *argv[]) basepoints, opener, remote_htlc_sigs, - outs, - open_is_replay); + outs); /* BOLT #5: * * 3. The ugly way (*revoked transaction close*): one of the @@ -3957,8 +3923,7 @@ int main(int argc, char *argv[]) &revocation_preimage, basepoints, opener, - outs, - open_is_replay); + outs); /* BOLT #5: * * There may be more than one valid, *unrevoked* commitment @@ -3974,22 +3939,19 @@ int main(int argc, char *argv[]) &old_remote_per_commit_point, basepoints, opener, - outs, - open_is_replay); + outs); } else if (commit_num == revocations_received(&shachain) + 1) { status_debug("Their unilateral tx, new commit point"); handle_their_unilateral(tx, tx_blockheight, &remote_per_commit_point, basepoints, opener, - outs, - open_is_replay); + outs); } else { handle_unknown_commitment(tx, tx_blockheight, possible_remote_per_commitment_point, basepoints, - outs, - open_is_replay); + outs); } } diff --git a/onchaind/onchaind_wire.csv b/onchaind/onchaind_wire.csv index 0193f4fbbe5b..a5204086c7db 100644 --- a/onchaind/onchaind_wire.csv +++ b/onchaind/onchaind_wire.csv @@ -48,7 +48,6 @@ msgdata,onchaind_init,remote_funding_pubkey,pubkey, msgdata,onchaind_init,local_static_remotekey_start,u64, msgdata,onchaind_init,remote_static_remotekey_start,u64, msgdata,onchaind_init,option_anchor_outputs,bool, -msgdata,onchaind_init,is_replay,bool, # We need this for BIP125 rule 4 msgdata,onchaind_init,min_relay_feerate,u32, @@ -78,13 +77,11 @@ msgtype,onchaind_spent,5004 msgdata,onchaind_spent,tx,tx_parts, msgdata,onchaind_spent,input_num,u32, msgdata,onchaind_spent,blockheight,u32, -msgdata,onchaind_spent,is_replay,bool, # master->onchaind: We will receive more than one of these, as depth changes. msgtype,onchaind_depth,5005 msgdata,onchaind_depth,txid,bitcoin_txid, msgdata,onchaind_depth,depth,u32, -msgdata,onchaind_depth,is_replay,bool, # onchaind->master: We don't want to watch this tx, or its outputs msgtype,onchaind_unwatch_tx,5006 @@ -93,7 +90,6 @@ msgdata,onchaind_unwatch_tx,txid,bitcoin_txid, # master->onchaind: We know HTLC preimage msgtype,onchaind_known_preimage,5007 msgdata,onchaind_known_preimage,preimage,preimage, -msgdata,onchaind_known_preimage,is_replay,bool, # onchaind->master: We discovered HTLC preimage msgtype,onchaind_extracted_preimage,5008 diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 325dd3d35cf4..76ae0744d5fb 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -40,7 +40,7 @@ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) bool fromwire_hsmd_get_per_commitment_point_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct pubkey *per_commitment_point UNNEEDED, struct secret **old_commitment_secret UNNEEDED) { fprintf(stderr, "fromwire_hsmd_get_per_commitment_point_reply called!\n"); abort(); } /* Generated stub for fromwire_onchaind_depth */ -bool fromwire_onchaind_depth(const void *p UNNEEDED, struct bitcoin_txid *txid UNNEEDED, u32 *depth UNNEEDED, bool *is_replay UNNEEDED) +bool fromwire_onchaind_depth(const void *p UNNEEDED, struct bitcoin_txid *txid UNNEEDED, u32 *depth UNNEEDED) { fprintf(stderr, "fromwire_onchaind_depth called!\n"); abort(); } /* Generated stub for fromwire_onchaind_dev_memleak */ bool fromwire_onchaind_dev_memleak(const void *p UNNEEDED) @@ -49,13 +49,13 @@ bool fromwire_onchaind_dev_memleak(const void *p UNNEEDED) bool fromwire_onchaind_htlcs(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct htlc_stub **htlc UNNEEDED, bool **tell_if_missing UNNEEDED, bool **tell_immediately UNNEEDED) { fprintf(stderr, "fromwire_onchaind_htlcs called!\n"); abort(); } /* Generated stub for fromwire_onchaind_init */ -bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, bool *is_replay UNNEEDED, u32 *min_relay_feerate UNNEEDED) +bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, u32 *min_relay_feerate UNNEEDED) { fprintf(stderr, "fromwire_onchaind_init called!\n"); abort(); } /* Generated stub for fromwire_onchaind_known_preimage */ -bool fromwire_onchaind_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED, bool *is_replay UNNEEDED) +bool fromwire_onchaind_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED) { fprintf(stderr, "fromwire_onchaind_known_preimage called!\n"); abort(); } /* Generated stub for fromwire_onchaind_spent */ -bool fromwire_onchaind_spent(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct tx_parts **tx UNNEEDED, u32 *input_num UNNEEDED, u32 *blockheight UNNEEDED, bool *is_replay UNNEEDED) +bool fromwire_onchaind_spent(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct tx_parts **tx UNNEEDED, u32 *input_num UNNEEDED, u32 *blockheight UNNEEDED) { fprintf(stderr, "fromwire_onchaind_spent called!\n"); abort(); } /* Generated stub for fromwire_peektype */ int fromwire_peektype(const u8 *cursor UNNEEDED) @@ -424,8 +424,8 @@ int main(int argc, char *argv[]) size_t ret = resolve_our_htlc_ourcommit(out, matches, htlcs, - htlc_scripts, - false); + htlc_scripts); + assert(ret == 2); common_shutdown(); } diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 7762473503ea..d25893b3d089 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -45,7 +45,7 @@ bool fromwire_hsmd_get_per_commitment_point_reply(const tal_t *ctx UNNEEDED, con bool fromwire_hsmd_sign_tx_reply(const void *p UNNEEDED, struct bitcoin_signature *sig UNNEEDED) { fprintf(stderr, "fromwire_hsmd_sign_tx_reply called!\n"); abort(); } /* Generated stub for fromwire_onchaind_depth */ -bool fromwire_onchaind_depth(const void *p UNNEEDED, struct bitcoin_txid *txid UNNEEDED, u32 *depth UNNEEDED, bool *is_replay UNNEEDED) +bool fromwire_onchaind_depth(const void *p UNNEEDED, struct bitcoin_txid *txid UNNEEDED, u32 *depth UNNEEDED) { fprintf(stderr, "fromwire_onchaind_depth called!\n"); abort(); } /* Generated stub for fromwire_onchaind_dev_memleak */ bool fromwire_onchaind_dev_memleak(const void *p UNNEEDED) @@ -54,13 +54,13 @@ bool fromwire_onchaind_dev_memleak(const void *p UNNEEDED) bool fromwire_onchaind_htlcs(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct htlc_stub **htlc UNNEEDED, bool **tell_if_missing UNNEEDED, bool **tell_immediately UNNEEDED) { fprintf(stderr, "fromwire_onchaind_htlcs called!\n"); abort(); } /* Generated stub for fromwire_onchaind_init */ -bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, bool *is_replay UNNEEDED, u32 *min_relay_feerate UNNEEDED) +bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, u32 *min_relay_feerate UNNEEDED) { fprintf(stderr, "fromwire_onchaind_init called!\n"); abort(); } /* Generated stub for fromwire_onchaind_known_preimage */ -bool fromwire_onchaind_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED, bool *is_replay UNNEEDED) +bool fromwire_onchaind_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED) { fprintf(stderr, "fromwire_onchaind_known_preimage called!\n"); abort(); } /* Generated stub for fromwire_onchaind_spent */ -bool fromwire_onchaind_spent(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct tx_parts **tx UNNEEDED, u32 *input_num UNNEEDED, u32 *blockheight UNNEEDED, bool *is_replay UNNEEDED) +bool fromwire_onchaind_spent(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct tx_parts **tx UNNEEDED, u32 *input_num UNNEEDED, u32 *blockheight UNNEEDED) { fprintf(stderr, "fromwire_onchaind_spent called!\n"); abort(); } /* Generated stub for fromwire_secp256k1_ecdsa_signature */ void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 6e9a14240b1d..3eed52759853 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -492,8 +492,7 @@ void notify_forward_event(struct lightningd *ld UNNEEDED, /* Generated stub for onchaind_funding_spent */ enum watch_result onchaind_funding_spent(struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, - u32 blockheight UNNEEDED, - bool is_replay UNNEEDED) + u32 blockheight UNNEEDED) { fprintf(stderr, "onchaind_funding_spent called!\n"); abort(); } /* Generated stub for onion_decode */ struct onion_payload *onion_decode(const tal_t *ctx UNNEEDED, @@ -770,7 +769,7 @@ u8 *towire_invalid_realm(const tal_t *ctx UNNEEDED) u8 *towire_onchaind_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_onchaind_dev_memleak called!\n"); abort(); } /* Generated stub for towire_onchaind_known_preimage */ -u8 *towire_onchaind_known_preimage(const tal_t *ctx UNNEEDED, const struct preimage *preimage UNNEEDED, bool is_replay UNNEEDED) +u8 *towire_onchaind_known_preimage(const tal_t *ctx UNNEEDED, const struct preimage *preimage UNNEEDED) { fprintf(stderr, "towire_onchaind_known_preimage called!\n"); abort(); } /* Generated stub for towire_permanent_channel_failure */ u8 *towire_permanent_channel_failure(const tal_t *ctx UNNEEDED) From 4506f639fa68e0b194017fcb9026877f4e5fe230 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 1 Dec 2021 10:34:58 -0600 Subject: [PATCH 0161/1530] coin_moves: remove 'index' for moves; bump version Get rid of the 'movement_idx', since we replay events now. Since we're removing a field from the 'coin_movement' event emission, we bump the version type. Changelog-Updated: `coin_movements` events have been revamped and are now on version 2. --- common/coin_mvt.c | 8 ++----- common/coin_mvt.h | 11 +++------ doc/PLUGINS.md | 5 +---- lightningd/coin_mvts.c | 32 +++------------------------ lightningd/coin_mvts.h | 2 -- lightningd/lightningd.c | 4 ---- lightningd/notification.c | 1 - lightningd/test/run-find_my_abspath.c | 3 --- tests/test_plugin.py | 4 +--- tests/utils.py | 15 +------------ 10 files changed, 11 insertions(+), 74 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 1f93435e14d0..a9bb5dee630f 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -306,8 +306,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, const char *bip173_name, u32 timestamp, - struct node_id *node_id, - s64 count) + struct node_id *node_id) { struct coin_mvt *mvt = tal(ctx, struct coin_mvt); @@ -330,7 +329,6 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, mvt->blockheight = chain_mvt->blockheight; mvt->version = COIN_MVT_VERSION; mvt->node_id = node_id; - mvt->counter = count; return mvt; } @@ -338,8 +336,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct channel_coin_mvt *chan_mvt, const char *bip173_name, - u32 timestamp, struct node_id *node_id, - s64 count) + u32 timestamp, struct node_id *node_id) { struct coin_mvt *mvt = tal(ctx, struct coin_mvt); @@ -360,7 +357,6 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->blockheight = 0; mvt->version = COIN_MVT_VERSION; mvt->node_id = node_id; - mvt->counter = count; return mvt; } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 04383c6e6e90..e76a6679bdd4 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -5,7 +5,7 @@ #include #include -#define COIN_MVT_VERSION 1 +#define COIN_MVT_VERSION 2 #define COIN_MVT_ACCT_WALLET "wallet" @@ -123,9 +123,6 @@ struct coin_mvt { /* node originating this movement */ struct node_id *node_id; - - /* id of this movement (on this node) */ - u64 counter; }; struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, @@ -222,14 +219,12 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, const char *bip173_name, u32 timestamp, - struct node_id *node_id, - s64 mvt_count); + struct node_id *node_id); struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct channel_coin_mvt *chan_mvt, const char *bip173_name, - u32 timestamp, struct node_id *node_id, - s64 mvt_count); + u32 timestamp, struct node_id *node_id); const char *mvt_type_str(enum mvt_type type); const char *mvt_tag_str(enum mvt_tag tag); diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index b88d6140b595..e925e319da29 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -697,9 +697,8 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. ```json { "coin_movement": { - "version":1, + "version":2, "node_id":"03a7103a2322b811f7369cbb27fb213d30bbc0b012082fed3cad7e4498da2dc56b", - "movement_idx":0, "type":"chain_mvt", "account_id":"wallet", "txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` type only, mandatory) @@ -722,8 +721,6 @@ notification adheres to. `node_id` specifies the node issuing the coin movement. -`movement_idx` is an increment-only counter for coin moves emitted by this node. - `type` marks the underlying mechanism which moved these coins. There are two 'types' of `coin_movements`: - `channel_mvt`s, which occur as a result of htlcs being resolved and, diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index b827310d9e89..d83550327c83 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -3,25 +3,15 @@ #include #include -static s64 update_count(struct lightningd *ld) -{ - s64 count; - count = ++ld->coin_moves_count; - db_set_intvar(ld->wallet->db, "coin_moves_count", count); - - return count; -} - void notify_channel_mvt(struct lightningd *ld, const struct channel_coin_mvt *mvt) { const struct coin_mvt *cm; u32 timestamp; - s64 count; timestamp = time_now().ts.tv_sec; - count = update_count(ld); cm = finalize_channel_mvt(mvt, mvt, chainparams->lightning_hrp, - timestamp, &ld->id, count); + timestamp, &ld->id); + notify_coin_mvt(ld, cm); } @@ -29,12 +19,10 @@ void notify_chain_mvt(struct lightningd *ld, const struct chain_coin_mvt *mvt) { const struct coin_mvt *cm; u32 timestamp; - s64 count; timestamp = time_now().ts.tv_sec; - count = update_count(ld); cm = finalize_chain_mvt(mvt, mvt, chainparams->onchain_hrp, - timestamp, &ld->id, count); + timestamp, &ld->id); notify_coin_mvt(ld, cm); } @@ -77,17 +65,3 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, hout->msat, ROUTED, false); } - -void coin_mvts_init_count(struct lightningd *ld) -{ - s64 count; - db_begin_transaction(ld->wallet->db); - count = db_get_intvar(ld->wallet->db, - "coin_moves_count", -1); - db_commit_transaction(ld->wallet->db); - if (count == -1) - fatal("Something went wrong attempting to fetch" - "the latest `coin_moves_count` from the intvars " - "table"); - ld->coin_moves_count = count; -} diff --git a/lightningd/coin_mvts.h b/lightningd/coin_mvts.h index 30f89c28ffdb..0ebce3d7d0d6 100644 --- a/lightningd/coin_mvts.h +++ b/lightningd/coin_mvts.h @@ -21,6 +21,4 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, struct htlc_out *hout, struct channel *channel); -/* Initialize the coin movement counter on lightningd */ -void coin_mvts_init_count(struct lightningd *ld); #endif /* LIGHTNING_LIGHTNINGD_COIN_MVTS_H */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 9c0d120a6928..4adb6632aa7f 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1000,10 +1000,6 @@ int main(int argc, char *argv[]) * states, invoices, payments, blocks and bitcoin transactions. */ ld->wallet = wallet_new(ld, ld->timers, bip32_base); - /*~ We keep track of how many 'coin moves' we've ever made. - * Initialize the starting value from the database here. */ - coin_mvts_init_count(ld); - /*~ We keep a filter of scriptpubkeys we're interested in. */ ld->owned_txfilter = txfilter_new(ld); diff --git a/lightningd/notification.c b/lightningd/notification.c index de7d1e64e4ac..3ca5deec2c32 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -465,7 +465,6 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_object_start(stream, "coin_movement"); json_add_num(stream, "version", mvt->version); json_add_node_id(stream, "node_id", mvt->node_id); - json_add_u64(stream, "movement_idx", mvt->counter); json_add_string(stream, "type", mvt_type_str(mvt->type)); json_add_string(stream, "account_id", mvt->account_id); json_mvt_id(stream, mvt->type, &mvt->id); diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 24e53c97abad..f7896a0c1724 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -19,9 +19,6 @@ void begin_topology(struct chain_topology *topo UNNEEDED) void channel_notify_new_block(struct lightningd *ld UNNEEDED, u32 block_height UNNEEDED) { fprintf(stderr, "channel_notify_new_block called!\n"); abort(); } -/* Generated stub for coin_mvts_init_count */ -void coin_mvts_init_count(struct lightningd *ld UNNEEDED) -{ fprintf(stderr, "coin_mvts_init_count called!\n"); abort(); } /* Generated stub for connectd_activate */ void connectd_activate(struct lightningd *ld UNNEEDED) { fprintf(stderr, "connectd_activate called!\n"); abort(); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index c4c24f73841e..b4cbf750811a 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -9,8 +9,7 @@ DEVELOPER, only_one, sync_blockheight, TIMEOUT, wait_for, TEST_NETWORK, DEPRECATED_APIS, expected_peer_features, expected_node_features, expected_channel_features, account_balance, - check_coin_moves, first_channel_id, check_coin_moves_idx, - EXPERIMENTAL_DUAL_FUND + check_coin_moves, first_channel_id, EXPERIMENTAL_DUAL_FUND ) import ast @@ -1986,7 +1985,6 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): # Verify we recorded all the movements we expect check_coin_moves(l2, chanid_1, l1_l2_mvts, chainparams) check_coin_moves(l2, chanid_3, l2_l3_mvts, chainparams) - check_coin_moves_idx(l2) def test_3847_repro(node_factory, bitcoind): diff --git a/tests/utils.py b/tests/utils.py index 74bf7d2b3ff8..4c5c83464c3e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -99,7 +99,7 @@ def check_coin_moves(n, account_id, expected_moves, chainparams): Millisatoshi(mv['credit']).millisatoshis, Millisatoshi(mv['debit']).millisatoshis, mv['tag'])) - assert mv['version'] == 1 + assert mv['version'] == 2 assert mv['node_id'] == node_id assert mv['timestamp'] > 0 assert mv['coin_type'] == chainparams['bip173_prefix'] @@ -129,19 +129,6 @@ def check_coin_moves(n, account_id, expected_moves, chainparams): assert acct_moves == [] -def check_coin_moves_idx(n): - """ Just check that the counter increments smoothly""" - moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] - idx = 0 - for m in moves: - c_idx = m['movement_idx'] - # verify that the index count increments smoothly here, also - if c_idx == 0 and idx == 0: - continue - assert c_idx == idx + 1 - idx = c_idx - - def account_balance(n, account_id): moves = dedupe_moves(n.rpc.call('listcoinmoves_plugin')['coin_moves']) chan_moves = [m for m in moves if m['account_id'] == account_id] From d3d6c09758da20931436bb0d2479465d06ea709b Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 1 Dec 2021 10:36:38 -0600 Subject: [PATCH 0162/1530] coin_moves: update some info about 'blockheight' --- doc/PLUGINS.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index e925e319da29..07fe7400761f 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -709,7 +709,7 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "credit":"2000000000msat", "debit":"0msat", "tag":"deposit", - "blockheight":102, // (`channel_mvt` type only. may be null) + "blockheight":102, // (May be null) "timestamp":1585948198, "coin_type":"bc" } @@ -759,10 +759,7 @@ multiple times. `channel_mvt` only - `onchain_htlc`: funds moved via an htlc onchain. `chain_mvt` only - `pushed`: funds pushed to peer. `channel_mvt` only. -`blockheight` is the block the txid is included in. `chain_mvt` only. In the -case that an output is considered dust, c-lightning does not track its return to -our wallet. In those cases, the blockheight will be `null`, as they're recorded -before confirmation. +`blockheight` is the block the txid is included in. The `timestamp` is seconds since Unix epoch of the node's machine time at the time lightningd broadcasts the notification. From b6463174d68bd8ad82511027d739fb79fc6303c0 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 6 Dec 2021 12:24:20 -0600 Subject: [PATCH 0163/1530] coin moves: turn 'tag' into 'tags' array, add OPENER tag Channels that the node has hopened will now be tagged with 'opener' in a list of tags. --- common/coin_mvt.c | 84 ++++++++---- common/coin_mvt.h | 24 ++-- doc/PLUGINS.md | 2 +- lightningd/channel_control.c | 3 +- lightningd/coin_mvts.c | 8 +- lightningd/notification.c | 6 +- lightningd/onchain_control.c | 2 + tests/test_closing.py | 246 +++++++++++++++++------------------ tests/test_connection.py | 4 +- tests/test_plugin.py | 20 +-- tests/utils.py | 22 ++-- 11 files changed, 234 insertions(+), 187 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index a9bb5dee630f..85f99905776e 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -40,18 +40,27 @@ static const char *mvt_tags[] = { "penalized", "stolen", "to_miner", + "opener", }; + const char *mvt_tag_str(enum mvt_tag tag) { return mvt_tags[tag]; } +enum mvt_tag *new_tag_arr(const tal_t *ctx, enum mvt_tag tag) +{ + enum mvt_tag *tags = tal_arr(ctx, enum mvt_tag, 1); + tags[0] = tag; + return tags; +} + struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, const struct channel_id *cid, struct sha256 payment_hash, u64 *part_id, struct amount_msat amount, - enum mvt_tag tag, + enum mvt_tag *tags STEALS, bool is_credit) { struct channel_coin_mvt *mvt = tal(ctx, struct channel_coin_mvt); @@ -59,7 +68,7 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, mvt->chan_id = *cid; mvt->payment_hash = tal_dup(mvt, struct sha256, &payment_hash); mvt->part_id = part_id; - mvt->tag = tag; + mvt->tags = tal_steal(mvt, tags); if (is_credit) { mvt->credit = amount; @@ -77,7 +86,8 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, const struct bitcoin_txid *tx_txid, const struct bitcoin_outpoint *outpoint, const struct sha256 *payment_hash TAKES, - u32 blockheight, enum mvt_tag tag, + u32 blockheight, + enum mvt_tag *tags STEALS, struct amount_msat amount, bool is_credit, struct amount_sat output_val) @@ -101,7 +111,8 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, mvt->payment_hash = NULL; mvt->blockheight = blockheight; - mvt->tag = tag; + mvt->tags = tal_steal(mvt, tags); + if (is_credit) { mvt->credit = amount; mvt->debit = AMOUNT_MSAT(0); @@ -119,7 +130,8 @@ static struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx, const struct bitcoin_txid *tx_txid, const struct bitcoin_outpoint *outpoint, const struct sha256 *payment_hash TAKES, - u32 blockheight, enum mvt_tag tag, + u32 blockheight, + enum mvt_tag *tags, struct amount_sat amt_sat, bool is_credit) { @@ -130,7 +142,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx, return new_chain_coin_mvt(ctx, account_name, tx_txid, outpoint, payment_hash, - blockheight, tag, amt_msat, is_credit, + blockheight, tags, amt_msat, is_credit, /* All amounts that are sat are * on-chain output values */ amt_sat); @@ -145,7 +157,7 @@ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, NULL, spend_txid, outpoint, NULL, - blockheight, tag, + blockheight, new_tag_arr(ctx, tag), amount, false); } @@ -157,7 +169,7 @@ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, NULL, NULL, outpoint, NULL, - blockheight, tag, + blockheight, new_tag_arr(ctx, tag), amount, true); } @@ -170,7 +182,7 @@ struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx, { return new_chain_coin_mvt(ctx, NULL, txid, outpoint, NULL, - blockheight, JOURNAL, + blockheight, new_tag_arr(ctx, JOURNAL), amount, is_credit, AMOUNT_SAT(0)); } @@ -183,7 +195,8 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, { return new_chain_coin_mvt(ctx, NULL, txid, out, NULL, blockheight, - CHANNEL_CLOSE, amount, false, + new_tag_arr(ctx, CHANNEL_CLOSE), + amount, false, output_val); } @@ -192,12 +205,20 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct bitcoin_outpoint *out, u32 blockheight, const struct amount_msat amount, - const struct amount_sat output_val) + const struct amount_sat output_val, + bool is_opener) { struct chain_coin_mvt *mvt; + mvt = new_chain_coin_mvt(ctx, NULL, NULL, out, NULL, blockheight, - CHANNEL_OPEN, amount, true, output_val); + new_tag_arr(ctx, CHANNEL_OPEN), amount, + true, output_val); mvt->account_name = type_to_string(mvt, struct channel_id, chan_id); + + /* If we're the opener, add to the tag list */ + if (is_opener) + tal_arr_expand(&mvt->tags, OPENER); + return mvt; } @@ -209,7 +230,8 @@ struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, NULL, NULL, outpoint, payment_hash, - blockheight, HTLC_FULFILL, + blockheight, + new_tag_arr(ctx, HTLC_FULFILL), amount, true); } @@ -224,7 +246,8 @@ struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx, * that output into their (external) account */ return new_chain_coin_mvt_sat(ctx, EXTERNAL, NULL, outpoint, payment_hash, - blockheight, HTLC_FULFILL, + blockheight, + new_tag_arr(ctx, HTLC_FULFILL), amount, false); } @@ -237,7 +260,8 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, { return new_chain_coin_mvt(ctx, EXTERNAL, txid, outpoint, NULL, blockheight, - tag, AMOUNT_MSAT(0), true, amount); + new_tag_arr(ctx, tag), + AMOUNT_MSAT(0), true, amount); } struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, @@ -248,7 +272,8 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, { return new_chain_coin_mvt(ctx, EXTERNAL, NULL, outpoint, NULL, - blockheight, tag, + blockheight, + new_tag_arr(ctx, tag), AMOUNT_MSAT(0), true, amount); } @@ -260,7 +285,7 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, WALLET, NULL, outpoint, NULL, - blockheight, tag, + blockheight, new_tag_arr(ctx, tag), amount, true); } @@ -273,7 +298,7 @@ struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, WALLET, spend_txid, outpoint, NULL, - blockheight, tag, + blockheight, new_tag_arr(ctx, tag), amount, false); } @@ -286,7 +311,8 @@ struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, account_name, txid, outpoint, NULL, - blockheight, PENALTY, + blockheight, + new_tag_arr(ctx, PENALTY), amount, false); } @@ -299,7 +325,8 @@ struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx, memset(&empty_hash, 0, sizeof(empty_hash)); return new_channel_coin_mvt(ctx, cid, empty_hash, - NULL, amount, PUSHED, false); + NULL, amount, + new_tag_arr(ctx, PUSHED), false); } struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, @@ -318,7 +345,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, mvt->id.tx_txid = chain_mvt->tx_txid; mvt->id.outpoint = chain_mvt->outpoint; mvt->id.payment_hash = chain_mvt->payment_hash; - mvt->tag = chain_mvt->tag; + mvt->tags = tal_steal(mvt, chain_mvt->tags); mvt->credit = chain_mvt->credit; mvt->debit = chain_mvt->debit; @@ -348,7 +375,7 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->id.part_id = chan_mvt->part_id; mvt->id.tx_txid = NULL; mvt->id.outpoint = NULL; - mvt->tag = chan_mvt->tag; + mvt->tags = tal_steal(mvt, chan_mvt->tags); mvt->credit = chan_mvt->credit; mvt->debit = chan_mvt->debit; mvt->output_val = NULL; @@ -383,7 +410,11 @@ void towire_chain_coin_mvt(u8 **pptr, const struct chain_coin_mvt *mvt) } else towire_bool(pptr, false); towire_u32(pptr, mvt->blockheight); - towire_u8(pptr, mvt->tag); + + towire_u32(pptr, tal_count(mvt->tags)); + for (size_t i = 0; i < tal_count(mvt->tags); i++) + towire_u8(pptr, mvt->tags[i]); + towire_amount_msat(pptr, mvt->credit); towire_amount_msat(pptr, mvt->debit); towire_amount_sat(pptr, mvt->output_val); @@ -415,7 +446,12 @@ void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_m } else mvt->payment_hash = NULL; mvt->blockheight = fromwire_u32(cursor, max); - mvt->tag = fromwire_u8(cursor, max); + + u32 tags_len = fromwire_u32(cursor, max); + mvt->tags = tal_arr(mvt, enum mvt_tag, tags_len); + for (size_t i = 0; i < tags_len; i++) + mvt->tags[i] = fromwire_u8(cursor, max); + mvt->credit = fromwire_amount_msat(cursor, max); mvt->debit = fromwire_amount_msat(cursor, max); mvt->output_val = fromwire_amount_sat(cursor, max); diff --git a/common/coin_mvt.h b/common/coin_mvt.h index e76a6679bdd4..e0f85df3adbd 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -4,11 +4,10 @@ #include #include +#include #define COIN_MVT_VERSION 2 -#define COIN_MVT_ACCT_WALLET "wallet" - enum mvt_type { CHAIN_MVT = 0, CHANNEL_MVT = 1, @@ -38,6 +37,7 @@ enum mvt_tag { PENALIZED = 20, STOLEN = 21, TO_MINER = 22, + OPENER = 23, }; struct channel_coin_mvt { @@ -51,8 +51,8 @@ struct channel_coin_mvt { * so we should also record a 'part-id' for them */ u64 *part_id; - /* label / tag */ - enum mvt_tag tag; + /* label / tag array */ + enum mvt_tag *tags; /* only one or the other */ struct amount_msat credit; @@ -69,8 +69,8 @@ struct chain_coin_mvt { /* some on-chain movements have a payment hash */ struct sha256 *payment_hash; - /* label / tag */ - enum mvt_tag tag; + /* label / tag array */ + enum mvt_tag *tags; /* block this transaction is confirmed in * zero means it's unknown/unconfirmed */ @@ -103,8 +103,8 @@ struct coin_mvt { /* identifier */ struct mvt_id id; - /* label / tag */ - enum mvt_tag tag; + /* label / tag array */ + enum mvt_tag *tags; /* only one or the other */ struct amount_msat credit; @@ -125,12 +125,14 @@ struct coin_mvt { struct node_id *node_id; }; +enum mvt_tag *new_tag_arr(const tal_t *ctx, enum mvt_tag tag); + struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, const struct channel_id *cid, struct sha256 payment_hash, u64 *part_id, struct amount_msat amount, - enum mvt_tag tag, + enum mvt_tag *tags STEALS, bool is_credit); struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, @@ -159,12 +161,14 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, u32 blockheight, const struct amount_msat amount, const struct amount_sat output_val); + struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct channel_id *chan_id, const struct bitcoin_outpoint *out, u32 blockheight, const struct amount_msat amount, - const struct amount_sat output_val); + const struct amount_sat output_val, + bool is_opener); struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 07fe7400761f..79dfc358f216 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -708,7 +708,7 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "part_id": 0, // (`channel_mvt` type only, mandatory) "credit":"2000000000msat", "debit":"0msat", - "tag":"deposit", + "tags": ["deposit"], "blockheight":102, // (May be null) "timestamp":1585948198, "coin_type":"bc" diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 7262889b4f02..d44dd87e99d2 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -155,7 +155,8 @@ void channel_record_open(struct channel *channel) &channel->funding, blockheight, start_balance, - channel->funding_sats); + channel->funding_sats, + channel->opener == LOCAL); notify_chain_mvt(channel->peer->ld, mvt); diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index d83550327c83..968a014db686 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -32,7 +32,7 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx, { return new_channel_coin_mvt(ctx, &channel->cid, hin->payment_hash, NULL, - hin->msat, INVOICE, + hin->msat, new_tag_arr(ctx, INVOICE), true); } @@ -42,7 +42,7 @@ struct channel_coin_mvt *new_channel_mvt_routed_hin(const tal_t *ctx, { return new_channel_coin_mvt(ctx, &channel->cid, hin->payment_hash, NULL, - hin->msat, ROUTED, + hin->msat, new_tag_arr(ctx, ROUTED), true); } @@ -52,7 +52,7 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hout(const tal_t *ctx, { return new_channel_coin_mvt(ctx, &channel->cid, hout->payment_hash, &hout->partid, - hout->msat, INVOICE, + hout->msat, new_tag_arr(ctx, INVOICE), false); } @@ -62,6 +62,6 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, { return new_channel_coin_mvt(ctx, &channel->cid, hout->payment_hash, NULL, - hout->msat, ROUTED, + hout->msat, new_tag_arr(ctx, ROUTED), false); } diff --git a/lightningd/notification.c b/lightningd/notification.c index 3ca5deec2c32..c5033eaf269a 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -474,7 +474,11 @@ static void coin_movement_notification_serialize(struct json_stream *stream, if (mvt->output_val) json_add_amount_sat_only(stream, "output_value", *mvt->output_val); - json_add_string(stream, "tag", mvt_tag_str(mvt->tag)); + + json_array_start(stream, "tags"); + for (size_t i = 0; i < tal_count(mvt->tags); i++) + json_add_string(stream, NULL, mvt_tag_str(mvt->tags[i])); + json_array_end(stream); /* Only chain movements have blockheights. A blockheight * of 'zero' means we haven't seen this tx confirmed yet. */ diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index bd17246fbdfb..6942162f7393 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -428,6 +428,7 @@ static void handle_irrevocably_resolved(struct channel *channel, const u8 *msg U delete_channel(channel); } + /** * onchain_add_utxo -- onchaind is telling us about an UTXO we own */ @@ -466,6 +467,7 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg) mvt = new_coin_wallet_deposit(msg, &outpoint, blockheight, amount, CHANNEL_CLOSE); + notify_chain_mvt(channel->peer->ld, mvt); } diff --git a/tests/test_closing.py b/tests/test_closing.py index b1d871d5e699..c5f420e9e25d 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -105,14 +105,14 @@ def test_closing_simple(node_factory, bitcoind, chainparams): assert account_balance(l2, channel_id) == 0 expected_1 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], - 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('wallet', 'deposit', None, None)], + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], + 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('wallet', ['deposit'], None, None)], } expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('wallet', 'deposit', None, None)], + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('wallet', ['deposit'], None, None)], } tags = check_utxos_channel(l1, [channel_id], expected_1) check_utxos_channel(l2, [channel_id], expected_2, tags) @@ -628,24 +628,24 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): # l1 loses all of their channel balance to the peer, as penalties expected_1 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], - 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'penalty', None, None), ('external', 'penalty', None, None)], + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], + 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('external', ['penalty'], None, None), ('external', ['penalty'], None, None)], } # l2 sweeps all of l1's closing outputs expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')], - 'C': [('wallet', 'deposit', None, None)], - 'D': [('wallet', 'deposit', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('cid1', ['penalty'], ['to_wallet'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'D')], + 'C': [('wallet', ['deposit'], None, None)], + 'D': [('wallet', ['deposit'], None, None)] } if anchor_expected(): - expected_1['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('external', 'anchor', None, None)) - expected_1['B'].append(('wallet', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_1['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) # We use a subset of tags in expected_2 that are used in expected_1 tags = check_utxos_channel(l1, [channel_id], expected_1) @@ -755,24 +755,24 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): # l1 loses all of their channel balance to the peer, as penalties expected_1 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], - 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'penalty', None, None), ('external', 'penalty', None, None), ('external', 'penalty', None, None)], + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], + 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('external', ['penalty'], None, None), ('external', ['penalty'], None, None), ('external', ['penalty'], None, None)], } # l2 sweeps all of l1's closing outputs expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('wallet', 'channel_close', None, None), ('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')], - 'C': [('wallet', 'deposit', None, None)], - 'D': [('wallet', 'deposit', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('wallet', ['channel_close'], None, None), ('cid1', ['penalty'], ['to_wallet'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'D')], + 'C': [('wallet', ['deposit'], None, None)], + 'D': [('wallet', ['deposit'], None, None)] } if anchor_expected(): - expected_1['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('external', 'anchor', None, None)) - expected_1['B'].append(('wallet', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_1['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) # We use a subset of tags in expected_2 that are used in expected_1 tags = check_utxos_channel(l1, [channel_id], expected_1) @@ -1283,24 +1283,24 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): assert account_balance(l2, channel_id) == 0 expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'to_them', None, None), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('external', 'penalized', None, None)], - 'C': [('external', 'penalized', None, None)], + 'A': [('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('external', ['to_them'], None, None), ('cid1', ['htlc_fulfill'], ['htlc_fulfill'], 'C'), ('external', ['penalized'], None, None)], + 'C': [('external', ['penalized'], None, None)], } expected_3 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('wallet', 'channel_close', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('cid1', 'penalty', 'to_wallet', 'E')], - 'C': [('cid1', 'penalty', 'to_wallet', 'D')], - 'D': [('wallet', 'deposit', None, None)], - 'E': [('wallet', 'deposit', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('wallet', ['channel_close'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'E')], + 'C': [('cid1', ['penalty'], ['to_wallet'], 'D')], + 'D': [('wallet', ['deposit'], None, None)], + 'E': [('wallet', ['deposit'], None, None)] } if anchor_expected(): - expected_2['B'].append(('external', 'anchor', None, None)) - expected_3['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) - expected_3['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_3['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_3['B'].append(('wallet', ['anchor'], None, None)) tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) @@ -1489,27 +1489,27 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): assert account_balance(l2, channel_id) == 0 expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'to_them', None, None), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'E'), ('cid1', 'delayed_to_us', 'to_wallet', 'F'), ('cid1', 'htlc_timeout', 'htlc_timeout', 'C')], - 'C': [('external', 'penalized', None, None)], - 'E': [('cid1', 'htlc_tx', 'to_wallet', 'G')], - 'F': [('wallet', 'deposit', None, None)], - 'G': [('wallet', 'deposit', None, None)] + 'A': [('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('external', ['to_them'], None, None), ('cid1', ['htlc_fulfill'], ['htlc_fulfill'], 'E'), ('cid1', ['delayed_to_us'], ['to_wallet'], 'F'), ('cid1', ['htlc_timeout'], ['htlc_timeout'], 'C')], + 'C': [('external', ['penalized'], None, None)], + 'E': [('cid1', ['htlc_tx'], ['to_wallet'], 'G')], + 'F': [('wallet', ['deposit'], None, None)], + 'G': [('wallet', ['deposit'], None, None)] } expected_3 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('wallet', 'channel_close', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'E'), ('external', 'stolen', None, None), ('external', 'htlc_timeout', 'htlc_timeout', 'C')], - 'C': [('cid1', 'penalty', 'to_wallet', 'D')], - 'D': [('wallet', 'deposit', None, None)], - 'E': [('external', 'stolen', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('wallet', ['channel_close'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'E'), ('external', ['stolen'], None, None), ('external', ['htlc_timeout'], ['htlc_timeout'], 'C')], + 'C': [('cid1', ['penalty'], ['to_wallet'], 'D')], + 'D': [('wallet', ['deposit'], None, None)], + 'E': [('external', ['stolen'], None, None)] } if anchor_expected(): - expected_2['B'].append(('external', 'anchor', None, None)) - expected_3['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) - expected_3['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_3['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_3['B'].append(('wallet', ['anchor'], None, None)) tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) @@ -1626,15 +1626,15 @@ def get_rbf_tx(self, depth, name, resolve): assert account_balance(l2, channel_id) == 0 expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')], - 'C': [('wallet', 'deposit', None, None)], - 'D': [('wallet', 'deposit', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('cid1', ['penalty'], ['to_wallet'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'D')], + 'C': [('wallet', ['deposit'], None, None)], + 'D': [('wallet', ['deposit'], None, None)] } if anchor_expected(): - expected_2['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) check_utxos_channel(l2, [channel_id], expected_2) @@ -1749,13 +1749,13 @@ def get_rbf_tx(self, depth, name, resolve): assert account_balance(l2, channel_id) == 0 expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('cid1', 'penalty', 'to_miner', 'C'), ('cid1', 'penalty', 'to_miner', 'D')], + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('cid1', ['penalty'], ['to_miner'], 'C'), ('cid1', ['penalty'], ['to_miner'], 'D')], } if anchor_expected(): - expected_2['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) check_utxos_channel(l2, [channel_id], expected_2) @@ -2067,24 +2067,24 @@ def test_onchain_timeout(node_factory, bitcoind, executor): # Graph of coin_move events we expect expected_1 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], - 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('cid1', 'delayed_to_us', 'to_wallet', 'C'), ('cid1', 'htlc_timeout', 'htlc_timeout', 'D')], - 'C': [('wallet', 'deposit', None, None)], - 'D': [('cid1', 'htlc_tx', 'to_wallet', 'E')], - 'E': [('wallet', 'deposit', None, None)] + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], + 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('cid1', ['delayed_to_us'], ['to_wallet'], 'C'), ('cid1', ['htlc_timeout'], ['htlc_timeout'], 'D')], + 'C': [('wallet', ['deposit'], None, None)], + 'D': [('cid1', ['htlc_tx'], ['to_wallet'], 'E')], + 'E': [('wallet', ['deposit'], None, None)] } expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('external', ['to_them'], None, None), ('external', ['htlc_timeout'], None, None)] } if anchor_expected(): - expected_1['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('external', 'anchor', None, None)) - expected_1['B'].append(('wallet', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_1['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) # We use a subset of tags in expected_2 that are used in expected_1 tags = check_utxos_channel(l1, [channel_id], expected_1) @@ -2182,28 +2182,28 @@ def try_pay(): # Graph of coin_move events we expect expected_2 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], # This is ugly, but this wallet deposit is either unspent or used # in the next channel open - 'A': [('wallet', 'deposit', [('withdrawal', 'F'), (None, None)]), ('cid1', 'channel_open', 'channel_close', 'B')], - '1': [('wallet', 'deposit', 'withdrawal', 'F')], - 'B': [('cid1', 'delayed_to_us', 'to_wallet', 'C'), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'D'), ('external', 'to_them', None, None)], - 'C': [('wallet', 'deposit', None, None)], - 'D': [('cid1', 'htlc_tx', 'to_wallet', 'E')], - 'E': [('wallet', 'deposit', None, None)], - 'F': [('wallet', 'deposit', None, None), ('cid2', 'channel_open', None, None)] + 'A': [('wallet', ['deposit'], ((['withdrawal'], 'F'), (None, None))), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + '1': [('wallet', ['deposit'], ['withdrawal'], 'F')], + 'B': [('cid1', ['delayed_to_us'], ['to_wallet'], 'C'), ('cid1', ['htlc_fulfill'], ['htlc_fulfill'], 'D'), ('external', ['to_them'], None, None)], + 'C': [('wallet', ['deposit'], None, None)], + 'D': [('cid1', ['htlc_tx'], ['to_wallet'], 'E')], + 'E': [('wallet', ['deposit'], None, None)], + 'F': [('wallet', ['deposit'], None, None), ('cid2', ['channel_open', 'opener'], None, None)] } expected_1 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'to_them', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'D'), ('wallet', 'channel_close', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('external', ['to_them'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'D'), ('wallet', ['channel_close'], None, None)] } if anchor_expected(): - expected_1['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('external', 'anchor', None, None)) - expected_1['B'].append(('wallet', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_1['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) chan2_id = first_channel_id(l2, l3) tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2) @@ -2303,27 +2303,27 @@ def try_pay(): # Graph of coin_move events we expect expected_2 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], # This is ugly, but this wallet deposit is either unspent or used # in the next channel open - 'A': [('wallet', 'deposit', [('withdrawal', 'D'), (None, None)]), ('cid1', 'channel_open', 'channel_close', 'B')], - '1': [('wallet', 'deposit', 'withdrawal', 'D')], - 'B': [('external', 'to_them', None, None), ('wallet', 'channel_close', None, None), ('cid1', 'htlc_fulfill', 'to_wallet', 'C')], - 'C': [('wallet', 'deposit', None, None)], - 'D': [('wallet', 'deposit', None, None), ('cid2', 'channel_open', None, None)] + 'A': [('wallet', ['deposit'], ((['withdrawal'], 'D'), (None, None))), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + '1': [('wallet', ['deposit'], ['withdrawal'], 'D')], + 'B': [('external', ['to_them'], None, None), ('wallet', ['channel_close'], None, None), ('cid1', ['htlc_fulfill'], ['to_wallet'], 'C')], + 'C': [('wallet', ['deposit'], None, None)], + 'D': [('wallet', ['deposit'], None, None), ('cid2', ['channel_open', 'opener'], None, None)] } expected_1 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'to_them', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('cid1', 'delayed_to_us', 'to_wallet', 'E')], - 'E': [('wallet', 'deposit', None, None)] + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('external', ['to_them'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'C'), ('cid1', ['delayed_to_us'], ['to_wallet'], 'E')], + 'E': [('wallet', ['deposit'], None, None)] } if anchor_expected(): - expected_1['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('external', 'anchor', None, None)) - expected_1['B'].append(('wallet', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_1['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) chan2_id = first_channel_id(l2, l3) tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2) @@ -2396,24 +2396,24 @@ def try_pay(): # Graph of coin_move events we expect expected_1 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], # This is ugly, but this wallet deposit is either unspent or used # in the next channel open - 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('wallet', 'channel_close', None, None), ('cid1', 'htlc_timeout', 'to_wallet', 'C')], - 'C': [('wallet', 'deposit', None, None)], + 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('wallet', ['channel_close'], None, None), ('cid1', ['htlc_timeout'], ['to_wallet'], 'C')], + 'C': [('wallet', ['deposit'], None, None)], } expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)], + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('external', ['to_them'], None, None), ('external', ['htlc_timeout'], None, None)], } if anchor_expected(): - expected_1['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('external', 'anchor', None, None)) - expected_1['B'].append(('wallet', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_1['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) tags = check_utxos_channel(l1, [channel_id], expected_1) check_utxos_channel(l2, [channel_id], expected_2, tags) @@ -2600,22 +2600,22 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): # Graph of coin_move events we expect expected_1 = { - '0': [('wallet', 'deposit', 'withdrawal', 'A')], - 'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('wallet', 'channel_close', None, None), ('cid1', 'htlc_timeout', 'ignored', 'C')], - 'C': [('wallet', 'deposit', None, None)], + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], + 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], + 'B': [('wallet', ['channel_close'], None, None), ('cid1', ['htlc_timeout'], ['ignored'], 'C')], + 'C': [('wallet', ['deposit'], None, None)], } expected_2 = { - 'A': [('cid1', 'channel_open', 'channel_close', 'B')], - 'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)], + 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], + 'B': [('external', ['to_them'], None, None), ('external', ['htlc_timeout'], None, None)], } if anchor_expected(): - expected_1['B'].append(('external', 'anchor', None, None)) - expected_2['B'].append(('external', 'anchor', None, None)) - expected_1['B'].append(('wallet', 'anchor', None, None)) - expected_2['B'].append(('wallet', 'anchor', None, None)) + expected_1['B'].append(('external', ['anchor'], None, None)) + expected_2['B'].append(('external', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor'], None, None)) tags = check_utxos_channel(l1, [channel_id], expected_1) check_utxos_channel(l2, [channel_id], expected_2, tags) diff --git a/tests/test_connection.py b/tests/test_connection.py index dd30de9ccfb9..738154b8f9d3 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1104,8 +1104,8 @@ def test_funding_push(node_factory, bitcoind, chainparams): # give the file write a second time.sleep(1) channel_mvts = [ - {'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tag': 'channel_open'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tag': 'pushed'}, + {'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tags': ['channel_open', 'opener']}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tags': ['pushed']}, ] check_coin_moves(l1, chanid, channel_mvts, chainparams) assert account_balance(l1, chanid) == (amount - push_sat) * 1000 diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b4cbf750811a..7de9d31b6f4a 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1887,19 +1887,19 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): """Verify that channel coin movements are triggered correctly. """ l1_l2_mvts = [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'channel_open'}, - {'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tag': 'routed'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tag': 'routed'}, - {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tag': 'invoice'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tag': 'invoice'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 100001001, 'tag': 'channel_close'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']}, + {'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tags': ['routed']}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['routed']}, + {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice']}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['invoice']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 100001001, 'tags': ['channel_close']}, ] l2_l3_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'channel_open'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'}, - {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tag': 'channel_close'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['channel_open', 'opener']}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tags': ['routed']}, + {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tags': ['routed']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tags': ['channel_close']}, ] l1, l2, l3 = node_factory.line_graph(3, opts=[ diff --git a/tests/utils.py b/tests/utils.py index 4c5c83464c3e..def472b0e870 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -77,7 +77,7 @@ def move_matches(exp, mv): return False if mv['debit'] != "{}msat".format(exp['debit']): return False - if mv['tag'] != exp['tag']: + if mv['tags'] != exp['tags']: return False return True @@ -94,11 +94,11 @@ def check_coin_moves(n, account_id, expected_moves, chainparams): node_id = n.info['id'] acct_moves = [m for m in moves if m['account_id'] == account_id] for mv in acct_moves: - print("{{'type': '{}', 'credit': {}, 'debit': {}, 'tag': '{}'}}," + print("{{'type': '{}', 'credit': {}, 'debit': {}, 'tags': '{}'}}," .format(mv['type'], Millisatoshi(mv['credit']).millisatoshis, Millisatoshi(mv['debit']).millisatoshis, - mv['tag'])) + mv['tags'])) assert mv['version'] == 2 assert mv['node_id'] == node_id assert mv['timestamp'] > 0 @@ -167,9 +167,9 @@ def print_utxos(utxos): print(k) for u in us: if u[1]: - print('\t', u[0]['account_id'], u[0]['tag'], u[1]['tag'], u[1]['txid']) + print('\t', u[0]['account_id'], u[0]['tags'], u[1]['tags'], u[1]['txid']) else: - print('\t', u[0]['account_id'], u[0]['tag'], None, None) + print('\t', u[0]['account_id'], u[0]['tags'], None, None) def utxos_for_channel(utxoset, channel_id): @@ -218,7 +218,7 @@ def matchup_events(u_set, evs, chans, tag_list): else: acct = ev[0] - if u[0]['account_id'] != acct or u[0]['tag'] != ev[1]: + if u[0]['account_id'] != acct or u[0]['tags'] != ev[1]: continue if ev[2] is None: @@ -228,21 +228,21 @@ def matchup_events(u_set, evs, chans, tag_list): break # ugly hack to annotate two possible futures for a utxo - if type(ev[2]) is list: - tag = u[1]['tag'] if u[1] else u[1] + if type(ev[2]) is tuple: + tag = u[1]['tags'] if u[1] else u[1] assert tag in [x[0] for x in ev[2]] if not u[1]: found = True u_set.remove(u) break for x in ev[2]: - if x[0] == u[1]['tag'] and u[1]['tag'] != 'to_miner': + if x[0] == u[1]['tags'] and 'to_miner' not in u[1]['tags']: # Save the 'spent to' txid in the tag-list tag_list[x[1]] = u[1]['txid'] else: - assert ev[2] == u[1]['tag'] + assert ev[2] == u[1]['tags'] # Save the 'spent to' txid in the tag-list - if u[1]['tag'] != 'to_miner': + if 'to_miner' not in u[1]['tags']: tag_list[ev[3]] = u[1]['txid'] found = True From 29c671829720a6816ddd5e51e9f82cdd00967890 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Dec 2021 10:05:29 -0600 Subject: [PATCH 0164/1530] coin_mvt: record new 'fees' field on htlc channel moves We record the amount of fees collected for a routed payment. For simplicity's sake on the data agg side, we record the fee payment on *BOTH* the incoming htlc and the outgoing htlc. Note that this results in double counting if you add up the fees from both an in-routed and out-routed payment. --- common/coin_mvt.c | 11 +++++++++-- common/coin_mvt.h | 8 +++++++- doc/PLUGINS.md | 12 ++++++++++++ lightningd/coin_mvts.c | 28 +++++++++++++++++++++++---- lightningd/notification.c | 3 +++ lightningd/peer_htlcs.c | 17 ++++++++++++++--- tests/test_plugin.py | 12 ++++++------ tests/test_wallet.py | 40 +++++++++++++++++++-------------------- tests/utils.py | 12 ++++++++++-- 9 files changed, 105 insertions(+), 38 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 85f99905776e..5949f1342bc2 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -61,7 +61,8 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, u64 *part_id, struct amount_msat amount, enum mvt_tag *tags STEALS, - bool is_credit) + bool is_credit, + struct amount_msat fees) { struct channel_coin_mvt *mvt = tal(ctx, struct channel_coin_mvt); @@ -78,6 +79,8 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, mvt->credit = AMOUNT_MSAT(0); } + mvt->fees = fees; + return mvt; } @@ -326,7 +329,8 @@ struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx, return new_channel_coin_mvt(ctx, cid, empty_hash, NULL, amount, - new_tag_arr(ctx, PUSHED), false); + new_tag_arr(ctx, PUSHED), false, + AMOUNT_MSAT(0)); } struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, @@ -351,6 +355,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, mvt->output_val = tal(mvt, struct amount_sat); *mvt->output_val = chain_mvt->output_val; + mvt->fees = NULL; mvt->timestamp = timestamp; mvt->blockheight = chain_mvt->blockheight; @@ -379,6 +384,8 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->credit = chan_mvt->credit; mvt->debit = chan_mvt->debit; mvt->output_val = NULL; + mvt->fees = tal(mvt, struct amount_msat); + *mvt->fees = chan_mvt->fees; mvt->timestamp = timestamp; /* channel movements don't have a blockheight */ mvt->blockheight = 0; diff --git a/common/coin_mvt.h b/common/coin_mvt.h index e0f85df3adbd..9dfcc80c1262 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -58,6 +58,8 @@ struct channel_coin_mvt { struct amount_msat credit; struct amount_msat debit; + /* Fees collected (or paid) on this mvt */ + struct amount_msat fees; }; struct chain_coin_mvt { @@ -114,6 +116,9 @@ struct coin_mvt { * our credit/debit amount, eg channel opens */ struct amount_sat *output_val; + /* Amount of fees collected/paid by channel mvt */ + struct amount_msat *fees; + u32 timestamp; u32 blockheight; @@ -133,7 +138,8 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, u64 *part_id, struct amount_msat amount, enum mvt_tag *tags STEALS, - bool is_credit); + bool is_credit, + struct amount_msat fees); struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 79dfc358f216..76ae30f9e9de 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -708,6 +708,8 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "part_id": 0, // (`channel_mvt` type only, mandatory) "credit":"2000000000msat", "debit":"0msat", + "output_value": "2000000000msat", // ('chain_mvt' only) + "fees": "382msat", // ('channel_mvt' only, optional) "tags": ["deposit"], "blockheight":102, // (May be null) "timestamp":1585948198, @@ -747,6 +749,16 @@ multiple times. `channel_mvt` only `credit` and `debit` are millisatoshi denominated amounts of the fund movement. A 'credit' is funds deposited into an account; a `debit` is funds withdrawn. +`output_value` is the total value of the on-chain UTXO. Note that for +channel opens/closes the total output value will not necessarily correspond +to the amount that's credited/debited. + +`fees` is an HTLC annotation for the amount of fees either paid or +earned. For "invoice" tagged events, the fees are the total fees +paid to send that payment. The end amount can be found by subtracting +the total fees from the `debited` amount. For "routed" tagged events, +both the debit/credit contain fees. Technically routed debits are the +'fee generating' event, however we include them on routed credits as well. `tag` is a movement descriptor. Current tags are as follows: - `deposit`: funds deposited diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 968a014db686..a29042cf68e8 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -33,17 +34,26 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx, return new_channel_coin_mvt(ctx, &channel->cid, hin->payment_hash, NULL, hin->msat, new_tag_arr(ctx, INVOICE), - true); + true, AMOUNT_MSAT(0)); } struct channel_coin_mvt *new_channel_mvt_routed_hin(const tal_t *ctx, struct htlc_in *hin, struct channel *channel) { + struct amount_msat fees_collected; + + if (!hin->payload) + return NULL; + + if (!amount_msat_sub(&fees_collected, hin->msat, + hin->payload->amt_to_forward)) + return NULL; + return new_channel_coin_mvt(ctx, &channel->cid, hin->payment_hash, NULL, hin->msat, new_tag_arr(ctx, ROUTED), - true); + true, fees_collected); } struct channel_coin_mvt *new_channel_mvt_invoice_hout(const tal_t *ctx, @@ -53,15 +63,25 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hout(const tal_t *ctx, return new_channel_coin_mvt(ctx, &channel->cid, hout->payment_hash, &hout->partid, hout->msat, new_tag_arr(ctx, INVOICE), - false); + false, AMOUNT_MSAT(0)); } struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, struct htlc_out *hout, struct channel *channel) { + struct amount_msat fees_collected; + + if (!hout->in) + return NULL; + + if (!amount_msat_sub(&fees_collected, hout->in->msat, + hout->msat)) + return NULL; + return new_channel_coin_mvt(ctx, &channel->cid, hout->payment_hash, NULL, hout->msat, new_tag_arr(ctx, ROUTED), - false); + false, + fees_collected); } diff --git a/lightningd/notification.c b/lightningd/notification.c index c5033eaf269a..239fbfabf9b6 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -474,6 +474,9 @@ static void coin_movement_notification_serialize(struct json_stream *stream, if (mvt->output_val) json_add_amount_sat_only(stream, "output_value", *mvt->output_val); + if (mvt->fees) + json_add_amount_msat_only(stream, "fees", + *mvt->fees); json_array_start(stream, "tags"); for (size_t i = 0; i < tal_count(mvt->tags); i++) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 43c0682438f8..70880689ad72 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1544,12 +1544,17 @@ static void remove_htlc_in(struct channel *channel, struct htlc_in *hin) channel->msat_to_us_max = channel->our_msat; /* Coins have definitively moved, log a movement */ - if (hin->we_filled) + if (hin->we_filled && *hin->we_filled) mvt = new_channel_mvt_invoice_hin(hin, hin, channel); else mvt = new_channel_mvt_routed_hin(hin, hin, channel); - notify_channel_mvt(channel->peer->ld, mvt); + if (!mvt) + log_broken(channel->log, + "Unable to calculate fees collected." + " Not logging an inbound HTLC"); + else + notify_channel_mvt(channel->peer->ld, mvt); } tal_free(hin); @@ -1597,7 +1602,13 @@ static void remove_htlc_out(struct channel *channel, struct htlc_out *hout) else mvt = new_channel_mvt_routed_hout(hout, hout, channel); - notify_channel_mvt(channel->peer->ld, mvt); + + if (!mvt) + log_broken(channel->log, + "Unable to calculate fees collected." + " Not logging an outbound HTLC"); + else + notify_channel_mvt(channel->peer->ld, mvt); } tal_free(hout); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 7de9d31b6f4a..58afbdbf5796 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1888,17 +1888,17 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): l1_l2_mvts = [ {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']}, - {'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tags': ['routed']}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['routed']}, - {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice']}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['invoice']}, + {'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tags': ['routed'], 'fees': '1001msat'}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['routed'], 'fees': '501msat'}, + {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['invoice'], 'fees': '0msat'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 100001001, 'tags': ['channel_close']}, ] l2_l3_mvts = [ {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['channel_open', 'opener']}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tags': ['routed']}, - {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tags': ['routed']}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tags': ['routed'], 'fees': '1001msat'}, + {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tags': ['routed'], 'fees': '501msat'}, {'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tags': ['channel_close']}, ] diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 812b8654fbb3..4aaf7557d613 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -850,26 +850,26 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): l1.rpc.signpsbt(invalid_psbt) wallet_coin_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, ] check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams) diff --git a/tests/utils.py b/tests/utils.py index def472b0e870..528090e67d6b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -79,6 +79,13 @@ def move_matches(exp, mv): return False if mv['tags'] != exp['tags']: return False + if 'fees' in exp: + if 'fees' not in mv: + return False + if mv['fees'] != exp['fees']: + return False + elif 'fees' in mv: + return False return True @@ -94,11 +101,12 @@ def check_coin_moves(n, account_id, expected_moves, chainparams): node_id = n.info['id'] acct_moves = [m for m in moves if m['account_id'] == account_id] for mv in acct_moves: - print("{{'type': '{}', 'credit': {}, 'debit': {}, 'tags': '{}'}}," + print("{{'type': '{}', 'credit': {}, 'debit': {}, 'tags': '{}' , ['fees'?: '{}']}}," .format(mv['type'], Millisatoshi(mv['credit']).millisatoshis, Millisatoshi(mv['debit']).millisatoshis, - mv['tags'])) + mv['tags'], + mv['fees'] if 'fees' in mv else '')) assert mv['version'] == 2 assert mv['node_id'] == node_id assert mv['timestamp'] > 0 From bddd3694fa35df20ba7497b48c8f4ffab2c24168 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 7 Dec 2021 14:09:28 -0600 Subject: [PATCH 0165/1530] coin_mvt: record fees for an outbound htlc If we initialized the payment, the fees are the entire fee-chain (final hop amount - starting hop amount) If it's a payment we routed, the fees are the diff between the inbound htlc and the outbound (net gain by this routing) Added to database so data persists nicely. --- lightningd/coin_mvts.c | 13 ++----------- lightningd/htlc_end.c | 27 ++++++++++++++++++++++++++- lightningd/htlc_end.h | 5 +++++ lightningd/pay.c | 10 +++++++--- lightningd/peer_htlcs.c | 7 +++++-- lightningd/peer_htlcs.h | 1 + tests/test_plugin.py | 14 ++++++++++++-- wallet/db.c | 1 + wallet/wallet.c | 9 +++++++-- 9 files changed, 66 insertions(+), 21 deletions(-) diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index a29042cf68e8..20c2eccacce6 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -63,25 +63,16 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hout(const tal_t *ctx, return new_channel_coin_mvt(ctx, &channel->cid, hout->payment_hash, &hout->partid, hout->msat, new_tag_arr(ctx, INVOICE), - false, AMOUNT_MSAT(0)); + false, hout->fees); } struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, struct htlc_out *hout, struct channel *channel) { - struct amount_msat fees_collected; - - if (!hout->in) - return NULL; - - if (!amount_msat_sub(&fees_collected, hout->in->msat, - hout->msat)) - return NULL; - return new_channel_coin_mvt(ctx, &channel->cid, hout->payment_hash, NULL, hout->msat, new_tag_arr(ctx, ROUTED), false, - fees_collected); + hout->fees); } diff --git a/lightningd/htlc_end.c b/lightningd/htlc_end.c index a5225f119c3d..d2ad10257ebf 100644 --- a/lightningd/htlc_end.c +++ b/lightningd/htlc_end.c @@ -279,6 +279,7 @@ struct htlc_out *new_htlc_out(const tal_t *ctx, const u8 *onion_routing_packet, const struct pubkey *blinding, bool am_origin, + struct amount_msat final_msat, u64 partid, u64 groupid, struct htlc_in *in) @@ -310,10 +311,34 @@ struct htlc_out *new_htlc_out(const tal_t *ctx, if (am_origin) { hout->partid = partid; hout->groupid = groupid; + + /* Stash the fees (for accounting) */ + if (!amount_msat_sub(&hout->fees, msat, final_msat)) + return corrupt("new_htlc_out", + "overflow subtract %s-%s", + type_to_string(tmpctx, + struct amount_msat, + &msat), + type_to_string(tmpctx, + struct amount_msat, + &final_msat)); + } hout->in = NULL; - if (in) + if (in) { htlc_out_connect_htlc_in(hout, in); + /* Stash the fees (for accounting) */ + if (!amount_msat_sub(&hout->fees, in->msat, msat)) + return corrupt("new_htlc_out", + "overflow subtract %s-%s", + type_to_string(tmpctx, + struct amount_msat, + &in->msat), + type_to_string(tmpctx, + struct amount_msat, + &msat)); + } + return htlc_out_check(hout, "new_htlc_out"); } diff --git a/lightningd/htlc_end.h b/lightningd/htlc_end.h index 91bd6315b3eb..da5f2ce18f74 100644 --- a/lightningd/htlc_end.h +++ b/lightningd/htlc_end.h @@ -90,6 +90,10 @@ struct htlc_out { /* Is this a locally-generated payment? Implies ->in is NULL. */ bool am_origin; + /* Amount of fees that this out htlc pays (if am_origin); + * otherwise fees collected by routing this out */ + struct amount_msat fees; + /* If am_origin, this is the partid of the payment. */ u64 partid; @@ -168,6 +172,7 @@ struct htlc_out *new_htlc_out(const tal_t *ctx, const u8 *onion_routing_packet, const struct pubkey *blinding, bool am_origin, + struct amount_msat final_msat, u64 partid, u64 groupid, struct htlc_in *in); diff --git a/lightningd/pay.c b/lightningd/pay.c index f4db4fc527f5..00fdafdf8a16 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -773,6 +773,7 @@ static bool should_use_tlv(enum route_hop_style style) static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld, const struct onionpacket *packet, const struct route_hop *first_hop, + const struct amount_msat final_amount, const struct sha256 *payment_hash, const struct pubkey *blinding, u64 partid, @@ -786,7 +787,8 @@ static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld, base_expiry = get_block_height(ld->topology) + 1; onion = serialize_onionpacket(tmpctx, packet); return send_htlc_out(ctx, channel, first_hop->amount, - base_expiry + first_hop->delay, payment_hash, + base_expiry + first_hop->delay, + final_amount, payment_hash, blinding, partid, groupid, onion, NULL, hout, &dont_care_about_channel_update); } @@ -1047,7 +1049,8 @@ send_payment_core(struct lightningd *ld, return command_failed(cmd, data); } - failmsg = send_onion(tmpctx, ld, packet, first_hop, rhash, NULL, partid, + failmsg = send_onion(tmpctx, ld, packet, first_hop, msat, + rhash, NULL, partid, group, channel, &hout); if (failmsg) { @@ -1211,7 +1214,8 @@ send_payment(struct lightningd *ld, n_hops, type_to_string(tmpctx, struct amount_msat, &msat)); packet = create_onionpacket(tmpctx, path, ROUTING_INFO_SIZE, &path_secrets); return send_payment_core(ld, cmd, rhash, partid, group, &route[0], - msat, total_msat, label, invstring, + msat, total_msat, + label, invstring, packet, &ids[n_hops - 1], ids, channels, path_secrets, local_offer_id); } diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 70880689ad72..8c60ed4e3219 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -638,6 +638,7 @@ static void htlc_offer_timeout(struct htlc_out *out) const u8 *send_htlc_out(const tal_t *ctx, struct channel *out, struct amount_msat amount, u32 cltv, + struct amount_msat final_msat, const struct sha256 *payment_hash, const struct pubkey *blinding, u64 partid, @@ -675,6 +676,7 @@ const u8 *send_htlc_out(const tal_t *ctx, *houtp = new_htlc_out(out->owner, out, amount, cltv, payment_hash, onion_routing_packet, blinding, in == NULL, + final_msat, partid, groupid, in); tal_add_destructor(*houtp, destroy_hout_subd_died); @@ -786,7 +788,8 @@ static void forward_htlc(struct htlc_in *hin, } failmsg = send_htlc_out(tmpctx, next, amt_to_forward, - outgoing_cltv_value, &hin->payment_hash, + outgoing_cltv_value, AMOUNT_MSAT(0), + &hin->payment_hash, next_blinding, 0 /* partid */, 0 /* groupid */, next_onion, hin, &hout, &needs_update_appended); if (!failmsg) @@ -1605,7 +1608,7 @@ static void remove_htlc_out(struct channel *channel, struct htlc_out *hout) if (!mvt) log_broken(channel->log, - "Unable to calculate fees collected." + "Unable to calculate fees." " Not logging an outbound HTLC"); else notify_channel_mvt(channel->peer->ld, mvt); diff --git a/lightningd/peer_htlcs.h b/lightningd/peer_htlcs.h index 50618a7499d1..d6c49983e613 100644 --- a/lightningd/peer_htlcs.h +++ b/lightningd/peer_htlcs.h @@ -43,6 +43,7 @@ void update_per_commit_point(struct channel *channel, const u8 *send_htlc_out(const tal_t *ctx, struct channel *out, struct amount_msat amount, u32 cltv, + struct amount_msat final_msat, const struct sha256 *payment_hash, const struct pubkey *blinding, u64 partid, diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 58afbdbf5796..e59861876281 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1902,10 +1902,18 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): {'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tags': ['channel_close']}, ] + l3_l2_mvts = [ + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']}, + {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 50000501, 'tags': ['invoice'], 'fees': '501msat'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 49999499, 'tags': ['channel_close']}, + ] + + coin_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') l1, l2, l3 = node_factory.line_graph(3, opts=[ {'may_reconnect': True}, - {'may_reconnect': True, 'plugin': os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')}, - {'may_reconnect': True}, + {'may_reconnect': True, 'plugin': coin_plugin}, + {'may_reconnect': True, 'plugin': coin_plugin}, ], wait_for_announce=True) bitcoind.generate_block(5) @@ -1977,12 +1985,14 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): bitcoind.generate_block(6) sync_blockheight(bitcoind, [l2]) l2.daemon.wait_for_log('{}.*FUNDING_TRANSACTION/FUNDING_OUTPUT->MUTUAL_CLOSE depth'.format(l3.info['id'])) + l3.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE') # Ending channel balance should be zero assert account_balance(l2, chanid_1) == 0 assert account_balance(l2, chanid_3) == 0 # Verify we recorded all the movements we expect + check_coin_moves(l3, chanid_3, l3_l2_mvts, chainparams) check_coin_moves(l2, chanid_1, l1_l2_mvts, chainparams) check_coin_moves(l2, chanid_3, l2_l3_mvts, chainparams) diff --git a/wallet/db.c b/wallet/db.c index bfbd9a7291a8..21806c2c206b 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -868,6 +868,7 @@ static struct migration dbmigrations[] = { [BUILD_ASSERT_OR_ZERO( 9 == RCVD_REMOVE_ACK_REVOCATION) + BUILD_ASSERT_OR_ZERO(19 == SENT_REMOVE_ACK_REVOCATION)], NULL}, + {SQL("ALTER TABLE channel_htlcs ADD fees_msat BIGINT DEFAULT 0"), NULL}, }; /* Leak tracking. */ diff --git a/wallet/wallet.c b/wallet/wallet.c index 1c4b923b92bc..62bae46bb4f5 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2372,8 +2372,9 @@ void wallet_htlc_save_out(struct wallet *wallet, " malformed_onion," " partid," " groupid," + " fees_msat," " min_commit_num" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?);")); + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, chan->dbid); db_bind_u64(stmt, 1, out->key.id); @@ -2403,7 +2404,9 @@ void wallet_htlc_save_out(struct wallet *wallet, db_bind_u64(stmt, 10, out->partid); db_bind_u64(stmt, 11, out->groupid); } - db_bind_u64(stmt, 12, min_u64(chan->next_index[LOCAL]-1, + + db_bind_amount_msat(stmt, 12, &out->fees); + db_bind_u64(stmt, 13, min_u64(chan->next_index[LOCAL]-1, chan->next_index[REMOTE]-1)); db_exec_prepared_v2(stmt); @@ -2598,6 +2601,7 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, out->failmsg = db_col_arr(out, stmt, "localfailmsg", u8); out->in = NULL; + db_col_amount_msat(stmt, "fees_msat", &out->fees); if (!db_col_is_null(stmt, "origin_htlc")) { u64 in_id = db_col_u64(stmt, "origin_htlc"); @@ -2737,6 +2741,7 @@ bool wallet_htlcs_load_out_for_channel(struct wallet *wallet, ", partid" ", localfailmsg" ", groupid" + ", fees_msat" " FROM channel_htlcs" " WHERE direction = ?" " AND channel_id = ?" From 8225a9decf29a987984b60cd948b5400dad95c4b Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 8 Dec 2021 11:42:07 -0600 Subject: [PATCH 0166/1530] coin_mvt: log events for pushes/lease_fees for leased channels We need to stash/save the amount of the lease fees on a leased channel, we do this by re-using the 'push' amount field on channel (which is technically correct, since we're essentially pushing the fee amount to the peer). Also updates a bit of how the pushes are accounted for (pushed to now has an event; their channel will open at zero but then they'll immediately register a push event). Leases fees are treated exactly the same as pushes, except labeled differently. Required adding a 'lease_fee' field to the inflights so we keep track of the fee for the lease until the open happens. --- common/coin_mvt.c | 18 ++++++++--- common/coin_mvt.h | 13 +++++--- lightningd/channel.c | 4 ++- lightningd/channel.h | 7 +++- lightningd/channel_control.c | 46 +++++++++++++++++---------- lightningd/dual_open_control.c | 29 ++++++++++++++--- lightningd/peer_control.c | 1 + openingd/dualopend.c | 58 ++++++++++++++++------------------ openingd/dualopend_wire.csv | 1 + tests/test_closing.py | 32 +++++++++++++++++-- tests/test_connection.py | 14 +++++--- wallet/db.c | 1 + wallet/test/run-wallet.c | 6 ++-- wallet/wallet.c | 14 ++++++-- 14 files changed, 170 insertions(+), 74 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 5949f1342bc2..37ae80093aa5 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -41,6 +41,8 @@ static const char *mvt_tags[] = { "stolen", "to_miner", "opener", + "lease_fee", + "leased", }; const char *mvt_tag_str(enum mvt_tag tag) @@ -209,7 +211,8 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, u32 blockheight, const struct amount_msat amount, const struct amount_sat output_val, - bool is_opener) + bool is_opener, + bool is_leased) { struct chain_coin_mvt *mvt; @@ -222,6 +225,9 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, if (is_opener) tal_arr_expand(&mvt->tags, OPENER); + if (is_leased) + tal_arr_expand(&mvt->tags, LEASED); + return mvt; } @@ -319,9 +325,11 @@ struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, amount, false); } -struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx, - const struct channel_id *cid, - struct amount_msat amount) +struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, + const struct channel_id *cid, + struct amount_msat amount, + enum mvt_tag tag, + bool is_credit) { struct sha256 empty_hash; /* Use a 0'd out payment hash */ @@ -329,7 +337,7 @@ struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx, return new_channel_coin_mvt(ctx, cid, empty_hash, NULL, amount, - new_tag_arr(ctx, PUSHED), false, + new_tag_arr(ctx, tag), is_credit, AMOUNT_MSAT(0)); } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 9dfcc80c1262..bac404974005 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -38,6 +38,8 @@ enum mvt_tag { STOLEN = 21, TO_MINER = 22, OPENER = 23, + LEASE_FEE = 24, + LEASED = 25, }; struct channel_coin_mvt { @@ -174,7 +176,8 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, u32 blockheight, const struct amount_msat amount, const struct amount_sat output_val, - bool is_opener); + bool is_opener, + bool is_leased); struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, @@ -221,9 +224,11 @@ struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, u32 blockheight, struct amount_sat amount); -struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx, - const struct channel_id *cid, - struct amount_msat amount); +struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, + const struct channel_id *cid, + struct amount_msat amount, + enum mvt_tag tag, + bool is_credit); struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, diff --git a/lightningd/channel.c b/lightningd/channel.c index 617b89ad9589..e0c1f8779ee7 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -155,7 +155,8 @@ new_inflight(struct channel *channel, const u32 lease_expiry, const secp256k1_ecdsa_signature *lease_commit_sig, const u32 lease_chan_max_msat, const u16 lease_chan_max_ppt, - const u32 lease_blockheight_start) + const u32 lease_blockheight_start, + const struct amount_msat lease_fee) { struct wally_psbt *last_tx_psbt_clone; struct channel_inflight *inflight @@ -191,6 +192,7 @@ new_inflight(struct channel *channel, inflight->lease_chan_max_msat = lease_chan_max_msat; inflight->lease_chan_max_ppt = lease_chan_max_ppt; + inflight->lease_fee = lease_fee; list_add_tail(&channel->inflights, &inflight->list); tal_add_destructor(inflight, destroy_inflight); diff --git a/lightningd/channel.h b/lightningd/channel.h index 76a73d5b47ae..4f3fb1d28d50 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -50,6 +50,10 @@ struct channel_inflight { u32 lease_chan_max_msat; u16 lease_chan_max_ppt; u32 lease_blockheight_start; + + /* We save this data so we can do nice accounting; + * on the channel we slot it into the 'push' field */ + struct amount_msat lease_fee; }; struct open_attempt { @@ -318,7 +322,8 @@ new_inflight(struct channel *channel, const secp256k1_ecdsa_signature *lease_commit_sig, const u32 lease_chan_max_msat, const u16 lease_chan_max_ppt, - const u32 lease_blockheight_start); + const u32 lease_blockheight_start, + const struct amount_msat lease_fee); /* Given a txid, find an inflight channel stub. Returns NULL if none found */ struct channel_inflight *channel_inflight_find(struct channel *channel, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index d44dd87e99d2..49940eff4fc8 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -132,21 +132,30 @@ void channel_record_open(struct channel *channel) struct chain_coin_mvt *mvt; u32 blockheight; struct amount_msat start_balance; - bool we_pushed = channel->opener == LOCAL - && !amount_msat_zero(channel->push); + bool is_pushed = !amount_msat_zero(channel->push); + bool is_leased = channel->lease_expiry > 0; blockheight = short_channel_id_blocknum(channel->scid); - /* If we pushed funds, add them back into the starting balance */ - if (we_pushed) { - if (!amount_msat_add(&start_balance, - channel->push, channel->our_msat)) - fatal("Unable to add push_msat (%s) + our_msat (%s)", - type_to_string(tmpctx, struct amount_msat, - &channel->push), - type_to_string(tmpctx, struct amount_msat, - &channel->our_msat)); - + /* If funds were pushed, add/sub them from the starting balance */ + if (is_pushed) { + if (channel->opener == LOCAL) { + if (!amount_msat_add(&start_balance, + channel->our_msat, channel->push)) + fatal("Unable to add push_msat (%s) + our_msat (%s)", + type_to_string(tmpctx, struct amount_msat, + &channel->push), + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat)); + } else { + if (!amount_msat_sub(&start_balance, + channel->our_msat, channel->push)) + fatal("Unable to sub our_msat (%s) - push (%s)", + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat), + type_to_string(tmpctx, struct amount_msat, + &channel->push)); + } } else start_balance = channel->our_msat; @@ -156,15 +165,18 @@ void channel_record_open(struct channel *channel) blockheight, start_balance, channel->funding_sats, - channel->opener == LOCAL); + channel->opener == LOCAL, + is_leased); notify_chain_mvt(channel->peer->ld, mvt); - /* If we pushed sats, *now* record them as a withdrawal */ - if (we_pushed) + /* If we pushed sats, *now* record them */ + if (is_pushed) notify_channel_mvt(channel->peer->ld, - new_coin_pushed(tmpctx, &channel->cid, - channel->push)); + new_coin_channel_push(tmpctx, &channel->cid, + channel->push, + is_leased ? LEASE_FEE : PUSHED, + channel->opener == REMOTE)); } static void lockin_complete(struct channel *channel) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index a53c38be9214..badaaf6f8df7 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1079,12 +1079,13 @@ wallet_update_channel(struct lightningd *ld, u32 funding_feerate, struct wally_psbt *psbt STEALS, const u32 lease_expiry, + struct amount_sat lease_fee, secp256k1_ecdsa_signature *lease_commit_sig STEALS, const u32 lease_chan_max_msat, const u16 lease_chan_max_ppt, const u32 lease_blockheight_start) { - struct amount_msat our_msat; + struct amount_msat our_msat, lease_fee_msat; struct channel_inflight *inflight; if (!amount_sat_to_msat(&our_msat, our_funding)) { @@ -1092,6 +1093,11 @@ wallet_update_channel(struct lightningd *ld, return NULL; } + if (!amount_sat_to_msat(&lease_fee_msat, lease_fee)) { + log_broken(channel->log, "Unable to convert 'lease_fee'"); + return NULL; + } + assert(channel->unsaved_dbid == 0); assert(channel->dbid != 0); @@ -1099,6 +1105,7 @@ wallet_update_channel(struct lightningd *ld, channel->funding_sats = total_funding; channel->our_funds = our_funding; channel->our_msat = our_msat; + channel->push = lease_fee_msat; channel->msat_to_us_min = our_msat; channel->msat_to_us_max = our_msat; channel->lease_expiry = lease_expiry; @@ -1134,7 +1141,8 @@ wallet_update_channel(struct lightningd *ld, channel->lease_commit_sig, channel->lease_chan_max_msat, channel->lease_chan_max_ppt, - lease_blockheight_start); + lease_blockheight_start, + channel->push); wallet_inflight_add(ld->wallet, inflight); return inflight; @@ -1157,11 +1165,12 @@ wallet_commit_channel(struct lightningd *ld, struct wally_psbt *psbt STEALS, const u32 lease_blockheight_start, const u32 lease_expiry, + const struct amount_sat lease_fee, secp256k1_ecdsa_signature *lease_commit_sig STEALS, const u32 lease_chan_max_msat, const u16 lease_chan_max_ppt) { - struct amount_msat our_msat; + struct amount_msat our_msat, lease_fee_msat; struct channel_inflight *inflight; if (!amount_sat_to_msat(&our_msat, our_funding)) { @@ -1169,6 +1178,11 @@ wallet_commit_channel(struct lightningd *ld, return NULL; } + if (!amount_sat_to_msat(&lease_fee_msat, lease_fee)) { + log_broken(channel->log, "Unable to convert lease fee"); + return NULL; + } + /* Get a key to use for closing outputs from this tx */ channel->final_key_idx = wallet_get_newindex(ld); if (channel->final_key_idx == -1) { @@ -1190,6 +1204,7 @@ wallet_commit_channel(struct lightningd *ld, channel->funding_sats = total_funding; channel->our_funds = our_funding; channel->our_msat = our_msat; + channel->push = lease_fee_msat; channel->msat_to_us_min = our_msat; channel->msat_to_us_max = our_msat; @@ -1253,7 +1268,8 @@ wallet_commit_channel(struct lightningd *ld, channel->lease_commit_sig, channel->lease_chan_max_msat, channel->lease_chan_max_ppt, - lease_blockheight_start); + lease_blockheight_start, + channel->push); wallet_inflight_add(ld->wallet, inflight); return inflight; @@ -2739,7 +2755,7 @@ static void handle_commit_received(struct subd *dualopend, u16 lease_chan_max_ppt; u32 feerate_funding, feerate_commitment, lease_expiry, lease_chan_max_msat, lease_blockheight_start; - struct amount_sat total_funding, funding_ours; + struct amount_sat total_funding, funding_ours, lease_fee; u8 *remote_upfront_shutdown_script, *local_upfront_shutdown_script; struct penalty_base *pbase; @@ -2772,6 +2788,7 @@ static void handle_commit_received(struct subd *dualopend, &remote_upfront_shutdown_script, &lease_blockheight_start, &lease_expiry, + &lease_fee, &lease_commit_sig, &lease_chan_max_msat, &lease_chan_max_ppt)) { @@ -2817,6 +2834,7 @@ static void handle_commit_received(struct subd *dualopend, psbt, lease_blockheight_start, lease_expiry, + lease_fee, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt))) { @@ -2849,6 +2867,7 @@ static void handle_commit_received(struct subd *dualopend, feerate_funding, psbt, lease_expiry, + lease_fee, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4fc9279fe2e4..fc81ee52bf68 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1230,6 +1230,7 @@ static void update_channel_from_inflight(struct lightningd *ld, /* Lease infos ! */ channel->lease_expiry = inflight->lease_expiry; + channel->push = inflight->lease_fee; tal_free(channel->lease_commit_sig); channel->lease_commit_sig = tal_steal(channel, inflight->lease_commit_sig); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 7711f6224da2..78c87e6517f7 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -113,6 +113,9 @@ struct tx_state { /* If delay til the channel funds lease expires */ u32 lease_expiry; + /* Total fee for lease */ + struct amount_sat lease_fee; + /* Lease's commit sig */ secp256k1_ecdsa_signature *lease_commit_sig; @@ -130,6 +133,7 @@ static struct tx_state *new_tx_state(const tal_t *ctx) tx_state->remote_funding_sigs_rcvd = false; tx_state->lease_expiry = 0; + tx_state->lease_fee = AMOUNT_SAT(0); tx_state->blockheight = 0; tx_state->lease_commit_sig = NULL; tx_state->lease_chan_max_msat = 0; @@ -567,7 +571,6 @@ static char *check_balances(const tal_t *ctx, struct state *state, struct tx_state *tx_state, struct wally_psbt *psbt, - struct amount_sat lease_fee, u32 feerate_per_kw_funding) { struct amount_sat initiator_inputs, initiator_outs, @@ -721,9 +724,11 @@ static char *check_balances(const tal_t *ctx, /* The lease_fee has been added to the accepter_funding, * but the opener_funding is responsible for covering it, * so we do a little switcheroo here */ - if (!amount_sat_add(&initiator_outs, initiator_outs, lease_fee)) + if (!amount_sat_add(&initiator_outs, initiator_outs, + tx_state->lease_fee)) return "overflow adding lease_fee to initiator's funding"; - if (!amount_sat_sub(&accepter_outs, accepter_outs, lease_fee)) + if (!amount_sat_sub(&accepter_outs, accepter_outs, + tx_state->lease_fee)) return "unable to subtract lease_fee from accepter's funding"; for (size_t i = 0; i < psbt->num_outputs; i++) { @@ -787,7 +792,7 @@ static char *check_balances(const tal_t *ctx, type_to_string(tmpctx, struct amount_sat, &initiator_outs), type_to_string(tmpctx, struct amount_sat, - &lease_fee)); + &tx_state->lease_fee)); } @@ -806,7 +811,8 @@ static char *check_balances(const tal_t *ctx, return tal_fmt(tmpctx, "accepter inputs %s less than outputs %s (lease fee %s)", type_to_string(tmpctx, struct amount_sat, &accepter_inputs), type_to_string(tmpctx, struct amount_sat, &accepter_outs), - type_to_string(tmpctx, struct amount_sat, &lease_fee)); + type_to_string(tmpctx, struct amount_sat, + &tx_state->lease_fee)); } if (!amount_sat_sub(&initiator_diff, initiator_inputs, @@ -1726,7 +1732,6 @@ static void revert_channel_state(struct state *state) static u8 *accepter_commits(struct state *state, struct tx_state *tx_state, struct amount_sat total, - struct amount_sat lease_fee, char **err_reason) { struct wally_tx_output *direct_outputs[NUM_SIDES]; @@ -1762,7 +1767,6 @@ static u8 *accepter_commits(struct state *state, /* Check tx funds are sane */ error = check_balances(tmpctx, state, tx_state, tx_state->psbt, - lease_fee, tx_state->feerate_per_kw_funding); if (error) { *err_reason = tal_fmt(tmpctx, "Insufficiently funded" @@ -1960,6 +1964,7 @@ static u8 *accepter_commits(struct state *state, state->upfront_shutdown_script[REMOTE], tx_state->blockheight, tx_state->lease_expiry, + tx_state->lease_fee, tx_state->lease_commit_sig, tx_state->lease_chan_max_msat, tx_state->lease_chan_max_ppt); @@ -2039,7 +2044,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) struct channel_id cid, full_cid; char *err_reason; u8 *msg; - struct amount_sat total, requested_amt, lease_fee, our_accept; + struct amount_sat total, requested_amt, our_accept; enum dualopend_wire msg_type; struct tx_state *tx_state = state->tx_state; @@ -2209,13 +2214,14 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) tx_state->accepter_funding, requested_amt, tx_state->feerate_per_kw_funding, - &lease_fee)) + &tx_state->lease_fee)) negotiation_failed(state, "Unable to calculate lease fee"); /* Add it to the accepter's total */ if (!amount_sat_add(&tx_state->accepter_funding, - tx_state->accepter_funding, lease_fee)) + tx_state->accepter_funding, + tx_state->lease_fee)) negotiation_failed(state, "Unable to add accepter's funding" @@ -2225,11 +2231,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) &tx_state->accepter_funding), type_to_string(tmpctx, struct amount_sat, - &lease_fee)); - - } else { - tx_state->lease_expiry = 0; - lease_fee = AMOUNT_SAT(0); + &tx_state->lease_fee)); } /* Check that total funding doesn't overflow */ @@ -2342,8 +2344,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) if (!run_tx_interactive(state, tx_state, &tx_state->psbt, TX_ACCEPTER)) return; - msg = accepter_commits(state, tx_state, total, - lease_fee, &err_reason); + msg = accepter_commits(state, tx_state, total, &err_reason); if (!msg) { if (err_reason) negotiation_failed(state, "%s", err_reason); @@ -2380,7 +2381,6 @@ static void add_funding_output(struct tx_state *tx_state, static u8 *opener_commits(struct state *state, struct tx_state *tx_state, struct amount_sat total, - struct amount_sat lease_fee, char **err_reason) { struct channel_id cid; @@ -2416,7 +2416,6 @@ static u8 *opener_commits(struct state *state, error = check_balances(tmpctx, state, tx_state, tx_state->psbt, - lease_fee, tx_state->feerate_per_kw_funding); if (error) { *err_reason = tal_fmt(tmpctx, "Insufficiently funded funding " @@ -2649,6 +2648,7 @@ static u8 *opener_commits(struct state *state, state->upfront_shutdown_script[REMOTE], tx_state->blockheight, tx_state->lease_expiry, + tx_state->lease_fee, tx_state->lease_commit_sig, tx_state->lease_chan_max_msat, tx_state->lease_chan_max_ppt); @@ -2661,7 +2661,7 @@ static void opener_start(struct state *state, u8 *msg) struct tlv_accept_tlvs *a_tlv; struct channel_id cid; char *err_reason; - struct amount_sat total, requested_sats, lease_fee; + struct amount_sat total, requested_sats; bool dry_run; struct lease_rates *expected_rates; struct tx_state *tx_state = state->tx_state; @@ -2882,13 +2882,14 @@ static void opener_start(struct state *state, u8 *msg) if (!lease_rates_calc_fee(rates, tx_state->accepter_funding, requested_sats, tx_state->feerate_per_kw_funding, - &lease_fee)) + &tx_state->lease_fee)) negotiation_failed(state, "Unable to calculate lease fee"); /* Add it to the accepter's total */ if (!amount_sat_add(&tx_state->accepter_funding, - tx_state->accepter_funding, lease_fee)) { + tx_state->accepter_funding, + tx_state->lease_fee)) { negotiation_failed(state, "Unable to add accepter's funding" @@ -2898,7 +2899,7 @@ static void opener_start(struct state *state, u8 *msg) &tx_state->accepter_funding), type_to_string(tmpctx, struct amount_sat, - &lease_fee)); + &tx_state->lease_fee)); return; } @@ -2909,8 +2910,7 @@ static void opener_start(struct state *state, u8 *msg) = rates->channel_fee_max_base_msat; tx_state->lease_chan_max_ppt = rates->channel_fee_max_proportional_thousandths; - } else - lease_fee = AMOUNT_SAT(0); + } /* Check that total funding doesn't overflow */ if (!amount_sat_add(&total, tx_state->opener_funding, @@ -2975,7 +2975,7 @@ static void opener_start(struct state *state, u8 *msg) if (!run_tx_interactive(state, tx_state, &tx_state->psbt, TX_INITIATOR)) return; - msg = opener_commits(state, tx_state, total, lease_fee, &err_reason); + msg = opener_commits(state, tx_state, total, &err_reason); if (!msg) { if (err_reason) open_err_warn(state, "%s", err_reason); @@ -3075,11 +3075,9 @@ static void rbf_wrap_up(struct state *state, if (state->our_role == TX_ACCEPTER) /* FIXME: lease fee rate !? */ - msg = accepter_commits(state, tx_state, total, - AMOUNT_SAT(0), &err_reason); + msg = accepter_commits(state, tx_state, total, &err_reason); else - msg = opener_commits(state, tx_state, total, - AMOUNT_SAT(0), &err_reason); + msg = opener_commits(state, tx_state, total, &err_reason); if (!msg) { if (err_reason) diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 3b3620c7472f..48177925bcc7 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -150,6 +150,7 @@ msgdata,dualopend_commit_rcvd,remote_shutdown_len,u16, msgdata,dualopend_commit_rcvd,remote_shutdown_scriptpubkey,u8,remote_shutdown_len msgdata,dualopend_commit_rcvd,lease_start_blockheight,u32, msgdata,dualopend_commit_rcvd,lease_expiry,u32, +msgdata,dualopend_commit_rcvd,lease_fee,amount_sat, msgdata,dualopend_commit_rcvd,lease_commit_sig,?secp256k1_ecdsa_signature, msgdata,dualopend_commit_rcvd,lease_chan_max_msat,u32, msgdata,dualopend_commit_rcvd,lease_chan_max_ppt,u16, diff --git a/tests/test_closing.py b/tests/test_closing.py index c5f420e9e25d..ea3fda115613 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -7,7 +7,7 @@ only_one, sync_blockheight, wait_for, TIMEOUT, account_balance, first_channel_id, closing_fee, TEST_NETWORK, scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES, - check_utxos_channel, anchor_expected + check_utxos_channel, anchor_expected, check_coin_moves ) import os @@ -824,11 +824,12 @@ def test_channel_lease_falls_behind(node_factory, bitcoind): @pytest.mark.openchannel('v2') @pytest.mark.developer("requres 'dev-queryrates'") @pytest.mark.slow_test -def test_channel_lease_post_expiry(node_factory, bitcoind): +def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): + coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') opts = {'funder-policy': 'match', 'funder-policy-mod': 100, 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, - 'may_reconnect': True} + 'may_reconnect': True, 'plugin': coin_mvt_plugin} l1, l2, = node_factory.get_nodes(2, opts=opts) @@ -854,6 +855,7 @@ def test_channel_lease_post_expiry(node_factory, bitcoind): bitcoind.generate_block(6) l1.daemon.wait_for_log('to CHANNELD_NORMAL') + channel_id = first_channel_id(l1, l2) wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(l1.get_channel_scid(l2))['channels']] == [True, True]) @@ -897,6 +899,30 @@ def test_channel_lease_post_expiry(node_factory, bitcoind): l1.rpc.close(chan) l2.daemon.wait_for_log('State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE') + bitcoind.generate_block(2) + sync_blockheight(bitcoind, [l1, l2]) + l1.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE') + l2.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE') + + channel_mvts_1 = [ + {'type': 'chain_mvt', 'credit': 506432000, 'debit': 0, 'tags': ['channel_open', 'opener', 'leased']}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 6432000, 'tags': ['lease_fee'], 'fees': '0msat'}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 10000, 'tags': ['invoice'], 'fees': '0msat'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 499990000, 'tags': ['channel_close']}, + ] + + channel_mvts_2 = [ + {'type': 'chain_mvt', 'credit': 500000000, 'debit': 0, 'tags': ['channel_open', 'leased']}, + {'type': 'channel_mvt', 'credit': 6432000, 'debit': 0, 'tags': ['lease_fee'], 'fees': '0msat'}, + {'type': 'channel_mvt', 'credit': 10000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'}, + {'type': 'chain_mvt', 'credit': 0, 'debit': 506442000, 'tags': ['channel_close']}, + ] + + check_coin_moves(l1, channel_id, channel_mvts_1, chainparams) + check_coin_moves(l2, channel_id, channel_mvts_2, chainparams) + assert account_balance(l1, channel_id) == 0 + assert account_balance(l2, channel_id) == 0 + @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') diff --git a/tests/test_connection.py b/tests/test_connection.py index 738154b8f9d3..1e717ebed519 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1074,7 +1074,7 @@ def test_funding_push(node_factory, bitcoind, chainparams): coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') l1 = node_factory.get_node(options={'plugin': coin_mvt_plugin}) - l2 = node_factory.get_node() + l2 = node_factory.get_node(options={'plugin': coin_mvt_plugin}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -1103,11 +1103,17 @@ def test_funding_push(node_factory, bitcoind, chainparams): chanid = first_channel_id(l2, l1) # give the file write a second time.sleep(1) - channel_mvts = [ + channel_mvts_1 = [ {'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tags': ['channel_open', 'opener']}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tags': ['pushed']}, + {'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tags': ['pushed'], 'fees': '0msat'}, + ] + channel_mvts_2 = [ + {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']}, + {'type': 'channel_mvt', 'credit': 20000000, 'debit': 0, 'tags': ['pushed'], 'fees': '0msat'}, ] - check_coin_moves(l1, chanid, channel_mvts, chainparams) + check_coin_moves(l1, chanid, channel_mvts_1, chainparams) + check_coin_moves(l2, chanid, channel_mvts_2, chainparams) + assert account_balance(l1, chanid) == (amount - push_sat) * 1000 diff --git a/wallet/db.c b/wallet/db.c index 21806c2c206b..23fa2866cbf0 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -869,6 +869,7 @@ static struct migration dbmigrations[] = { BUILD_ASSERT_OR_ZERO(19 == SENT_REMOVE_ACK_REVOCATION)], NULL}, {SQL("ALTER TABLE channel_htlcs ADD fees_msat BIGINT DEFAULT 0"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD lease_fee BIGINT DEFAULT 0"), NULL}, }; /* Leak tracking. */ diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 3eed52759853..01348b60ca1f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1614,7 +1614,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) funding_psbt, last_tx, sig, - 1, lease_commit_sig, 2, 4, 22); + 1, lease_commit_sig, 2, 4, 22, + AMOUNT_MSAT(10)); /* do inflights get correctly added to the channel? */ wallet_inflight_add(w, inflight); @@ -1636,7 +1637,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) funding_psbt, last_tx, sig, - 0, NULL, 0, 0, 0); + 0, NULL, 0, 0, 0, + AMOUNT_MSAT(0)); wallet_inflight_add(w, inflight); CHECK_MSG(c2 = wallet_channel_load(w, chan->dbid), tal_fmt(w, "Load from DB")); diff --git a/wallet/wallet.c b/wallet/wallet.c index 62bae46bb4f5..a08056d7bff9 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1003,8 +1003,9 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) ", lease_chan_max_ppt" ", lease_expiry" ", lease_blockheight_start" + ", lease_fee" ") VALUES (" - "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, inflight->channel->dbid); db_bind_txid(stmt, 1, &inflight->funding->outpoint.txid); @@ -1022,12 +1023,14 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) db_bind_int(stmt, 11, inflight->lease_chan_max_ppt); db_bind_int(stmt, 12, inflight->lease_expiry); db_bind_int(stmt, 13, inflight->lease_blockheight_start); + db_bind_amount_msat(stmt, 14, &inflight->lease_fee); } else { db_bind_null(stmt, 9); db_bind_null(stmt, 10); db_bind_null(stmt, 11); db_bind_int(stmt, 12, 0); db_bind_null(stmt, 13); + db_bind_null(stmt, 14); } db_exec_prepared_v2(stmt); @@ -1083,6 +1086,7 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, struct channel *chan) { struct amount_sat funding_sat, our_funding_sat; + struct amount_msat lease_fee; struct bitcoin_outpoint funding; struct bitcoin_signature last_sig; struct channel_inflight *inflight; @@ -1106,14 +1110,18 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, lease_chan_max_msat = db_col_int(stmt, "lease_chan_max_msat"); lease_chan_max_ppt = db_col_int(stmt, "lease_chan_max_ppt"); lease_blockheight_start = db_col_int(stmt, "lease_blockheight_start"); + db_col_amount_msat(stmt, "lease_fee", &lease_fee); } else { lease_commit_sig = NULL; lease_chan_max_msat = 0; lease_chan_max_ppt = 0; lease_blockheight_start = 0; + lease_fee = AMOUNT_MSAT(0); + db_col_ignore(stmt, "lease_chan_max_msat"); db_col_ignore(stmt, "lease_chan_max_ppt"); db_col_ignore(stmt, "lease_blockheight_start"); + db_col_ignore(stmt, "lease_fee"); } inflight = new_inflight(chan, &funding, @@ -1127,7 +1135,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, - lease_blockheight_start); + lease_blockheight_start, + lease_fee); /* Pull out the serialized tx-sigs-received-ness */ inflight->remote_tx_sigs = db_col_int(stmt, "funding_tx_remote_sigs_received"); @@ -1155,6 +1164,7 @@ static bool wallet_channel_load_inflights(struct wallet *w, ", lease_chan_max_msat" ", lease_chan_max_ppt" ", lease_blockheight_start" + ", lease_fee" " FROM channel_funding_inflights" " WHERE channel_id = ?" " ORDER BY funding_feerate")); From 5570c6da089c487f3e398eb5351afff42bd692e8 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 8 Dec 2021 12:37:35 -0600 Subject: [PATCH 0167/1530] coin_moves: cleanup tags list, update docs, remove unused method We don't use journal_entry any more; remove it and update all of the remaining tags (plus docs) --- common/coin_mvt.c | 17 ---------------- common/coin_mvt.h | 51 +++++++++++++++++++---------------------------- doc/PLUGINS.md | 37 +++++++++++++++++++++++----------- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 37ae80093aa5..8292991f6a85 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -19,14 +19,10 @@ const char *mvt_type_str(enum mvt_type type) static const char *mvt_tags[] = { "deposit", "withdrawal", - NULL, "penalty", "invoice", "routed", - "journal_entry", - NULL, "pushed", - NULL, "channel_open", "channel_close", "delayed_to_us", @@ -178,19 +174,6 @@ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, amount, true); } -struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_msat amount, - bool is_credit) -{ - return new_chain_coin_mvt(ctx, NULL, txid, - outpoint, NULL, - blockheight, new_tag_arr(ctx, JOURNAL), - amount, is_credit, AMOUNT_SAT(0)); -} - struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *out, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index bac404974005..026aead821cd 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -16,30 +16,26 @@ enum mvt_type { enum mvt_tag { DEPOSIT = 0, WITHDRAWAL = 1, - /* 2, CHAIN_FEES has been removed */ - PENALTY = 3, - INVOICE = 4, - ROUTED = 5, - JOURNAL = 6, - /* 7, ONCHAIN_HTLC has been removed */ - PUSHED = 8, - /* 9, SPEND_TRACK has been removed */ - CHANNEL_OPEN = 10, - CHANNEL_CLOSE = 11, - CHANNEL_TO_US = 12, - HTLC_TIMEOUT = 13, - HTLC_FULFILL = 14, - HTLC_TX = 15, - TO_WALLET = 16, - IGNORED = 17, - ANCHOR = 18, - TO_THEM = 19, - PENALIZED = 20, - STOLEN = 21, - TO_MINER = 22, - OPENER = 23, - LEASE_FEE = 24, - LEASED = 25, + PENALTY = 2, + INVOICE = 3, + ROUTED = 4, + PUSHED = 5, + CHANNEL_OPEN = 6, + CHANNEL_CLOSE = 7, + CHANNEL_TO_US = 8, + HTLC_TIMEOUT = 9, + HTLC_FULFILL = 10, + HTLC_TX = 11, + TO_WALLET = 12, + IGNORED = 13, + ANCHOR = 14, + TO_THEM = 15, + PENALIZED = 16, + STOLEN = 17, + TO_MINER = 18, + OPENER = 19, + LEASE_FEE = 20, + LEASED = 21, }; struct channel_coin_mvt { @@ -156,13 +152,6 @@ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, struct amount_sat amount, enum mvt_tag tag); -struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_msat amount, - bool is_credit); - struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *out, diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 76ae30f9e9de..1bc0b17118c2 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -701,15 +701,15 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "node_id":"03a7103a2322b811f7369cbb27fb213d30bbc0b012082fed3cad7e4498da2dc56b", "type":"chain_mvt", "account_id":"wallet", - "txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` type only, mandatory) - "utxo_txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` type only, optional) - "vout":1, // (`chain_mvt` type only, optional) + "txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only, optional) + "utxo_txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only) + "vout":1, // (`chain_mvt` only) "payment_hash": "xxx", // (either type, optional on `chain_mvt`) - "part_id": 0, // (`channel_mvt` type only, mandatory) + "part_id": 0, // (`channel_mvt` only, mandatory) "credit":"2000000000msat", "debit":"0msat", "output_value": "2000000000msat", // ('chain_mvt' only) - "fees": "382msat", // ('channel_mvt' only, optional) + "fees": "382msat", // ('channel_mvt' only) "tags": ["deposit"], "blockheight":102, // (May be null) "timestamp":1585948198, @@ -763,13 +763,26 @@ both the debit/credit contain fees. Technically routed debits are the `tag` is a movement descriptor. Current tags are as follows: - `deposit`: funds deposited - `withdrawal`: funds withdrawn - - `penalty`: funds paid or gained from a penalty tx. `chain_mvt` only - - `invoice`: funds paid to or recieved from an invoice. `channel_mvt` only - - `routed`: funds routed through this node. `channel_mvt` only - - `journal_entry`: a balance reconciliation event, typically triggered - by a penalty tx onchain. `chain_mvt` only - - `onchain_htlc`: funds moved via an htlc onchain. `chain_mvt` only - - `pushed`: funds pushed to peer. `channel_mvt` only. + - `penalty`: funds paid or gained from a penalty tx. + - `invoice`: funds paid to or recieved from an invoice. + - `routed`: funds routed through this node. + - `pushed`: funds pushed to peer. + - channel_open : channel is opened, initial channel balance + - channel_close: channel is closed, final channel balance + - delayed_to_us : on-chain output to us, spent back into our wallet + - htlc_timeout : on-chain htlc timeout output + - htlc_fulfill : on-chian htlc fulfill output + - htlc_tx : on-chain htlc tx has happened + - to_wallet : output being spent into our wallet + - ignored : output is being ignored + - anchor : an anchor output + - to_them : output intended to peer's wallet + - penalized : output we've 'lost' due to a penalty (failed cheat attempt) + - stolen : output we've 'lost' due to peer's cheat + - to_miner : output we've burned to miner (OP_RETURN) + - opener : tags channel_open, we are the channel opener + - lease_fee: amount paid as lease fee + - leased: tags channel_open, channel contains leased funds `blockheight` is the block the txid is included in. From ec991e3af7ee23988c02131460d61eb2610371aa Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 13 Dec 2021 16:44:32 -0600 Subject: [PATCH 0168/1530] balance snap: first pass --- common/coin_mvt.c | 1 - common/coin_mvt.h | 1 + lightningd/chaintopology.c | 4 +++ lightningd/coin_mvts.c | 56 ++++++++++++++++++++++++++++++++++++++ lightningd/coin_mvts.h | 15 ++++++++++ lightningd/notification.c | 38 ++++++++++++++++++++++++++ lightningd/notification.h | 4 +++ 7 files changed, 118 insertions(+), 1 deletion(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 8292991f6a85..9133c166bcf8 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -7,7 +7,6 @@ #include #include -#define WALLET "wallet" #define EXTERNAL "external" static const char *mvt_types[] = { "chain_mvt", "channel_mvt" }; diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 026aead821cd..84fedaff1167 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -7,6 +7,7 @@ #include #define COIN_MVT_VERSION 2 +#define WALLET "wallet" enum mvt_type { CHAIN_MVT = 0, diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index ed0e184d5428..9dcdc0124d8e 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -629,6 +629,10 @@ static void updates_complete(struct chain_topology *topo) "last_processed_block", topo->tip->height); topo->prev_tip = topo->tip->blkid; + + /* Send out an account balance snapshot */ + /* FIXME: only issue on first updates_complete call */ + send_account_balance_snapshot(topo->ld, topo->tip->height); } /* If bitcoind is synced, we're now synced. */ diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 20c2eccacce6..529360db1aa4 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -1,8 +1,12 @@ #include "config.h" +#include #include +#include #include #include #include +#include + void notify_channel_mvt(struct lightningd *ld, const struct channel_coin_mvt *mvt) { @@ -76,3 +80,55 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, false, hout->fees); } + +void send_account_balance_snapshot(struct lightningd *ld, u32 blockheight) +{ + struct balance_snapshot *snap = tal(NULL, struct balance_snapshot); + struct account_balance *bal; + struct utxo **utxos; + struct channel *chan; + struct peer *p; + /* Available + reserved utxos are A+, as reserved things have not yet + * been spent */ + enum output_status utxo_states[] = {OUTPUT_STATE_AVAILABLE, + OUTPUT_STATE_RESERVED}; + + snap->blockheight = blockheight; + snap->timestamp = time_now().ts.tv_sec; + snap->node_id = &ld->id; + + /* Add the 'wallet' account balance */ + snap->accts = tal_arr(snap, struct account_balance *, 1); + bal = tal(snap, struct account_balance); + bal->acct_id = WALLET; + bal->bip173_name = chainparams->lightning_hrp; + + for (size_t i = 0; i < ARRAY_SIZE(utxo_states); i++) { + utxos = wallet_get_utxos(NULL, ld->wallet, utxo_states[i]); + for (size_t j = 0; j < tal_count(utxos); j++) { + if (!amount_msat_add_sat(&bal->balance, + bal->balance, utxos[j]->amount)) + fatal("Overflow adding node balance"); + } + tal_free(utxos); + } + snap->accts[0] = bal; + + /* Add channel balances */ + list_for_each(&ld->peers, p, list) { + list_for_each(&p->channels, chan, list) { + if (channel_active(chan)) { + bal = tal(snap, struct account_balance); + bal->bip173_name = chainparams->lightning_hrp; + bal->acct_id = type_to_string(bal, + struct channel_id, + &chan->cid); + bal->balance = chan->our_msat; + tal_arr_expand(&snap->accts, bal); + } + } + } + + notify_balance_snapshot(ld, snap); + tal_free(snap); +} diff --git a/lightningd/coin_mvts.h b/lightningd/coin_mvts.h index 0ebce3d7d0d6..ac70f4096689 100644 --- a/lightningd/coin_mvts.h +++ b/lightningd/coin_mvts.h @@ -5,6 +5,20 @@ #include #include +struct account_balance { + const char *acct_id; + const char *bip173_name; + struct amount_msat balance; +}; + +struct balance_snapshot { + struct node_id *node_id; + u32 blockheight; + u32 timestamp; + + struct account_balance **accts; +}; + void notify_channel_mvt(struct lightningd *ld, const struct channel_coin_mvt *mvt); void notify_chain_mvt(struct lightningd *ld, const struct chain_coin_mvt *mvt); @@ -21,4 +35,5 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, struct htlc_out *hout, struct channel *channel); +void send_account_balance_snapshot(struct lightningd *ld, u32 blockheight); #endif /* LIGHTNING_LIGHTNINGD_COIN_MVTS_H */ diff --git a/lightningd/notification.c b/lightningd/notification.c index 239fbfabf9b6..8161aa83f42a 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -2,6 +2,7 @@ #include #include #include +#include #include static struct notification *find_notification_by_topic(const char* topic) @@ -513,6 +514,43 @@ void notify_coin_mvt(struct lightningd *ld, plugins_notify(ld->plugins, take(n)); } +static void balance_snapshot_notification_serialize(struct json_stream *stream, struct balance_snapshot *snap) +{ + json_object_start(stream, "balance_snapshot"); + json_add_node_id(stream, "node_id", snap->node_id); + json_add_u32(stream, "blockheight", snap->blockheight); + json_add_u32(stream, "timestamp", snap->timestamp); + + json_array_start(stream, "accounts"); + for (size_t i = 0; i < tal_count(snap->accts); i++) { + json_object_start(stream, NULL); + json_add_string(stream, "account_id", + snap->accts[i]->acct_id); + json_add_amount_msat_only(stream, "balance", + snap->accts[i]->balance); + json_add_string(stream, "coin_type", snap->accts[i]->bip173_name); + json_object_end(stream); + } + json_array_end(stream); + json_object_end(stream); +} + +REGISTER_NOTIFICATION(balance_snapshot, + balance_snapshot_notification_serialize); + +void notify_balance_snapshot(struct lightningd *ld, + const struct balance_snapshot *snap) +{ + void (*serialize)(struct json_stream *, + const struct balance_snapshot *) = balance_snapshot_notification_gen.serialize; + + struct jsonrpc_notification *n = + jsonrpc_notification_start(NULL, "balance_snapshot"); + serialize(n->stream, snap); + jsonrpc_notification_end(n); + plugins_notify(ld->plugins, take(n)); +} + static void openchannel_peer_sigs_serialize(struct json_stream *stream, const struct channel_id *cid, const struct wally_psbt *psbt) diff --git a/lightningd/notification.h b/lightningd/notification.h index 9313572766d5..87234ca2df60 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -5,6 +5,7 @@ #include #include +struct balance_snapshot; struct onionreply; struct wally_psbt; @@ -81,6 +82,9 @@ void notify_sendpay_failure(struct lightningd *ld, void notify_coin_mvt(struct lightningd *ld, const struct coin_mvt *mvt); +void notify_balance_snapshot(struct lightningd *ld, + const struct balance_snapshot *snap); + void notify_openchannel_peer_sigs(struct lightningd *ld, const struct channel_id *cid, const struct wally_psbt *psbt); From f169597a0235c03ffda53486bd16c602cace677f Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 9 Dec 2021 11:46:26 -0600 Subject: [PATCH 0169/1530] test plugins/coin_moves: clean up/tidy coin moves plugin logic removes unused in-memory stash of coin-moves; don't write the entire set of coinmoves out on every update. --- tests/plugins/coin_movements.py | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/tests/plugins/coin_movements.py b/tests/plugins/coin_movements.py index ce617a770fed..2c0f3983d501 100755 --- a/tests/plugins/coin_movements.py +++ b/tests/plugins/coin_movements.py @@ -9,36 +9,23 @@ plugin = Plugin() -@plugin.init() -def init(configuration, options, plugin): - if os.path.exists('moves.json'): - jd = {} - with open('moves.json', 'r') as f: - jd = f.read() - plugin.coin_moves = json.loads(jd) - else: - plugin.coin_moves = [] - - @plugin.subscribe("coin_movement") def notify_coin_movement(plugin, coin_movement, **kwargs): plugin.log("coin movement: {}".format(coin_movement)) - plugin.coin_moves.append(coin_movement) # we save to disk so that we don't get borked if the node restarts # assumes notification calls are synchronous (not thread safe) - with open('moves.json', 'w') as f: - f.write(json.dumps(plugin.coin_moves)) + with open('moves.json', 'a') as f: + f.write(json.dumps(coin_movement) + ',') @plugin.method('listcoinmoves_plugin') def return_moves(plugin): result = [] if os.path.exists('moves.json'): - jd = {} with open('moves.json', 'r') as f: jd = f.read() - result = json.loads(jd) + result = json.loads('[' + jd[:-1] + ']') return {'coin_moves': result} From 70a73928cb123f943253cf0b15c6334911fb3c4e Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 10 Dec 2021 09:46:42 -0600 Subject: [PATCH 0170/1530] balance-snaps: add a `balance_snapshot` event; fires after first catchup Fire off a snapshot of current account balances (node wallet + every 'active' channel) after we've caught up to the chain tip for the *first* time (in other words, on start). --- doc/PLUGINS.md | 41 ++++++++++++++++++++++++++++++++++ lightningd/chaintopology.c | 8 +++++-- lightningd/coin_mvts.c | 1 + tests/plugins/balance_snaps.py | 30 +++++++++++++++++++++++++ tests/test_closing.py | 26 ++++++++++++++++----- tests/utils.py | 10 +++++++++ 6 files changed, 109 insertions(+), 7 deletions(-) create mode 100755 tests/plugins/balance_snaps.py diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 1bc0b17118c2..f214080f6e7d 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -791,6 +791,47 @@ at the time lightningd broadcasts the notification. `coin_type` is the BIP173 name for the coin which moved. +### `balance_snapshot` + +Emitted after we've caught up to the chain head on first start. Lists all +current accounts (`account_id` matches the `account_id` emitted from +`coin_movement`). Useful for checkpointing account balances. + +```json +{ + "balance_snapshots": [ + { + 'node_id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', + 'blockheight': 101, + 'timestamp': 1639076327, + 'accounts': [ + { + 'account_id': 'wallet', + 'balance': '0msat', + 'coin_type': 'bcrt' + } + ] + }, + { + 'node_id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', + 'blockheight': 110, + 'timestamp': 1639076343, + 'accounts': [ + { + 'account_id': 'wallet', + 'balance': '995433000msat', + 'coin_type': 'bcrt' + }, { + 'account_id': '5b65c199ee862f49758603a5a29081912c8816a7c0243d1667489d244d3d055f', + 'balance': '500000000msat', + 'coin_type': 'bcrt' + } + ] + } + ] +} +``` + ### `openchannel_peer_sigs` When opening a channel with a peer using the collaborative transaction protocol diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 9dcdc0124d8e..a998065ce4b5 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -27,6 +27,8 @@ /* Mutual recursion via timer. */ static void try_extend_tip(struct chain_topology *topo); +static bool first_update_complete = false; + /* init_topo sets topo->root, start_fee_estimate clears * feerate_uninitialized (even if unsuccessful) */ static void maybe_completed_init(struct chain_topology *topo) @@ -631,8 +633,10 @@ static void updates_complete(struct chain_topology *topo) topo->prev_tip = topo->tip->blkid; /* Send out an account balance snapshot */ - /* FIXME: only issue on first updates_complete call */ - send_account_balance_snapshot(topo->ld, topo->tip->height); + if (!first_update_complete) { + send_account_balance_snapshot(topo->ld, topo->tip->height); + first_update_complete = true; + } } /* If bitcoind is synced, we're now synced. */ diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 529360db1aa4..a77de436688e 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -100,6 +100,7 @@ void send_account_balance_snapshot(struct lightningd *ld, u32 blockheight) /* Add the 'wallet' account balance */ snap->accts = tal_arr(snap, struct account_balance *, 1); bal = tal(snap, struct account_balance); + bal->balance = AMOUNT_MSAT(0); bal->acct_id = WALLET; bal->bip173_name = chainparams->lightning_hrp; diff --git a/tests/plugins/balance_snaps.py b/tests/plugins/balance_snaps.py new file mode 100755 index 000000000000..30850f3bc0c0 --- /dev/null +++ b/tests/plugins/balance_snaps.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +from pyln.client import Plugin + +import json +import os.path + + +plugin = Plugin() + + +@plugin.subscribe("balance_snapshot") +def notify_balance_snapshot(plugin, balance_snapshot, **kwargs): + # we save to disk so that we don't get borked if the node restarts + # assumes notification calls are synchronous (not thread safe) + with open('snaps.json', 'a') as f: + f.write(json.dumps(balance_snapshot) + ',') + + +@plugin.method('listsnapshots') +def return_moves(plugin): + result = [] + if os.path.exists('snaps.json'): + with open('snaps.json', 'r') as f: + jd = f.read() + result = json.loads('[' + jd[:-1] + ']') + return {'balance_snapshots': result} + + +plugin.run() diff --git a/tests/test_closing.py b/tests/test_closing.py index ea3fda115613..983064008216 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -7,7 +7,8 @@ only_one, sync_blockheight, wait_for, TIMEOUT, account_balance, first_channel_id, closing_fee, TEST_NETWORK, scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES, - check_utxos_channel, anchor_expected, check_coin_moves + check_utxos_channel, anchor_expected, check_coin_moves, + check_balance_snaps ) import os @@ -1038,12 +1039,15 @@ def test_channel_lease_lessor_cheat(node_factory, bitcoind, chainparams): ''' Check that lessee can recover funds if lessor cheats ''' + balance_snaps = os.path.join(os.getcwd(), 'tests/plugins/balance_snaps.py') opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, - 'may_reconnect': True, 'allow_warning': True}, + 'may_reconnect': True, 'allow_warning': True, + 'plugin': balance_snaps}, {'funder-policy': 'match', 'funder-policy-mod': 100, 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, - 'may_reconnect': True, 'allow_broken_log': True}] + 'may_reconnect': True, 'allow_broken_log': True, + 'plugin': balance_snaps}] l1, l2, = node_factory.get_nodes(2, opts=opts) amount = 500000 feerate = 2000 @@ -1203,17 +1207,18 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): # We track channel balances, to verify that accounting is ok. coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') + balance_snaps = os.path.join(os.getcwd(), 'tests/plugins/balance_snaps.py') l1, l2, l3, l4 = node_factory.line_graph(4, opts=[{'disconnect': ['-WIRE_UPDATE_FULFILL_HTLC'], 'may_reconnect': True, 'dev-no-reconnect': None}, - {'plugin': coin_mvt_plugin, + {'plugin': [coin_mvt_plugin, balance_snaps], 'disable-mpp': None, 'dev-no-reconnect': None, 'may_reconnect': True, 'allow_broken_log': True}, - {'plugin': coin_mvt_plugin, + {'plugin': [coin_mvt_plugin, balance_snaps], 'dev-no-reconnect': None, 'may_reconnect': True, 'allow_broken_log': True}, @@ -1331,6 +1336,17 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) + if not chainparams['elements']: + # Also check snapshots + expected_bals_2 = [ + {'blockheight': 101, 'accounts': [{'balance': '0msat'}]}, + {'blockheight': 108, 'accounts': [{'balance': '995433000msat'}, {'balance': '500000000msat'}, {'balance': '499994999msat'}]}, + # There's a duplicate because we stop and restart l2 twice + # (both times at block 108) + {'blockheight': 108, 'accounts': [{'balance': '995433000msat'}, {'balance': '500000000msat'}, {'balance': '499994999msat'}]}, + ] + check_balance_snaps(l2, expected_bals_2) + @pytest.mark.developer("needs DEVELOPER=1") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") diff --git a/tests/utils.py b/tests/utils.py index 528090e67d6b..8766ee53f117 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -96,6 +96,16 @@ def calc_lease_fee(amt, feerate, rates): return fee +def check_balance_snaps(n, expected_bals): + snaps = n.rpc.listsnapshots()['balance_snapshots'] + for snap, exp in zip(snaps, expected_bals): + assert snap['blockheight'] == exp['blockheight'] + for acct, exp_acct in zip(snap['accounts'], exp['accounts']): + # FIXME: also check 'account_id's (these change every run) + for item in ['balance']: + assert acct[item] == exp_acct[item] + + def check_coin_moves(n, account_id, expected_moves, chainparams): moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] node_id = n.info['id'] From 9f02b6eb59fcc56449e05069b9dc5d77feed7007 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 21 Dec 2021 15:28:04 +0100 Subject: [PATCH 0171/1530] external: update lnprototest dependeces Signed-off-by: Vincenzo Palazzo --- external/lnprototest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/lnprototest b/external/lnprototest index a68b35310d6c..4b9cc370c4d8 160000 --- a/external/lnprototest +++ b/external/lnprototest @@ -1 +1 @@ -Subproject commit a68b35310d6c45c7879089bc2b3a144a089caf14 +Subproject commit 4b9cc370c4d821c172d5e44ea6aeace93b1bdf32 From 18526a3a5b7ab1e38aa64d0edfd444d5f186f637 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:50:09 +1030 Subject: [PATCH 0172/1530] lightningd: close one more fd for subdaemons. Noticed by stracing for an unrelated problem. Signed-off-by: Rusty Russell --- lightningd/subd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/subd.c b/lightningd/subd.c index ee9d0d79d4b9..bebd5adc1282 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -241,7 +241,7 @@ static int subd(const char *path, const char *name, goto child_errno_fail; /* Make (fairly!) sure all other fds are closed. */ - closefrom(tal_count(fds) + 1); + closefrom(tal_count(fds)); num_args = 0; args[num_args++] = tal_strdup(NULL, path); From 90b669857e5bea26b2bd192386829a75b18a11f5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:51:09 +1030 Subject: [PATCH 0173/1530] lightningd: handle channel cleanups more explicitly. 1. Freeing an unconfirmed channel already releases the subd, so don't do that explicitly. 2. Use channel->owner to transfer ownership where possible, using channel_set_owner() which handles all the cases. This simplifies the code and makes it more readable, IMHO. Signed-off-by: Rusty Russell --- lightningd/channel.c | 2 +- lightningd/opening_common.c | 9 +++------ lightningd/opening_control.c | 10 ++-------- lightningd/subd.c | 2 +- lightningd/subd.h | 5 +++-- wallet/test/run-wallet.c | 2 +- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index e0c1f8779ee7..f8e7972dd15c 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -826,7 +826,7 @@ void channel_internal_error(struct channel *channel, const char *fmt, ...) channel_cleanup_commands(channel, why); if (channel_unsaved(channel)) { - subd_release_channel(channel->owner, channel); + channel_set_owner(channel, NULL); delete_channel(channel); tal_free(why); return; diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 635aaeef5975..0b02b41d5de7 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -17,8 +17,9 @@ static void destroy_uncommitted_channel(struct uncommitted_channel *uc) { - if (uc->open_daemon) { - struct subd *open_daemon= uc->open_daemon; + struct subd *open_daemon = uc->open_daemon; + + if (open_daemon) { uc->open_daemon = NULL; subd_release_channel(open_daemon, uc); } @@ -115,10 +116,6 @@ void kill_uncommitted_channel(struct uncommitted_channel *uc, { log_info(uc->log, "Killing opening daemon: %s", why); - /* Close opend daemon. */ - subd_release_channel(uc->open_daemon, uc); - uc->open_daemon = NULL; - uncommitted_channel_disconnect(uc, LOG_INFORM, why); tal_free(uc); } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index dd378219f90c..9e78a6963766 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -319,9 +319,7 @@ static void opening_funder_start_replied(struct subd *openingd, const u8 *resp, return; failed: - subd_release_channel(openingd, fc->uc); - fc->uc->open_daemon = NULL; - /* Frees fc too, and tmpctx */ + /* Frees fc too */ tal_free(fc->uc); } @@ -414,9 +412,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, peer_start_channeld(channel, pps, NULL, false, NULL); cleanup: - subd_release_channel(openingd, fc->uc); - fc->uc->open_daemon = NULL; - /* Frees fc too, and tmpctx */ + /* Frees fc too */ tal_free(fc->uc); } @@ -528,8 +524,6 @@ static void opening_fundee_finished(struct subd *openingd, /* On to normal operation! */ peer_start_channeld(channel, pps, fwd_msg, false, NULL); - subd_release_channel(openingd, uc); - uc->open_daemon = NULL; tal_free(uc); return; diff --git a/lightningd/subd.c b/lightningd/subd.c index bebd5adc1282..a42849b48805 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -888,7 +888,7 @@ struct subd *subd_shutdown(struct subd *sd, unsigned int seconds) return tal_free(sd); } -void subd_release_channel(struct subd *owner, void *channel) +void subd_release_channel(struct subd *owner, const void *channel) { /* If owner is a per-peer-daemon, and not already freeing itself... */ if (owner->channel) { diff --git a/lightningd/subd.h b/lightningd/subd.h index 94fabf9e3a32..73b9534bda2b 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -197,9 +197,10 @@ void subd_req_(const tal_t *ctx, * @channel: channel to release. * * If the subdaemon is not already shutting down, and it is a per-channel - * subdaemon, this shuts it down. + * subdaemon, this shuts it down. Don't call this directly, use + * channel_set_owner() or uncommitted_channel_release_subd(). */ -void subd_release_channel(struct subd *owner, void *channel); +void subd_release_channel(struct subd *owner, const void *channel); /** * subd_shutdown - try to politely shut down a subdaemon. diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 01348b60ca1f..52617a9e3f2f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -654,7 +654,7 @@ u8 *serialize_onionpacket( const struct onionpacket *packet UNNEEDED) { fprintf(stderr, "serialize_onionpacket called!\n"); abort(); } /* Generated stub for subd_release_channel */ -void subd_release_channel(struct subd *owner UNNEEDED, void *channel UNNEEDED) +void subd_release_channel(struct subd *owner UNNEEDED, const void *channel UNNEEDED) { fprintf(stderr, "subd_release_channel called!\n"); abort(); } /* Generated stub for subd_req_ */ void subd_req_(const tal_t *ctx UNNEEDED, From 6ce7e7ea60d10194c294617061156837776f18f6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:52:09 +1030 Subject: [PATCH 0174/1530] common: is_msg_gossip_broadcast() for putting things in filter. We don't need the other "gossip" messages in our echo-suppression filter. Signed-off-by: Rusty Russell --- common/read_peer_msg.c | 3 ++- wire/peer_wire.c | 54 ++++++++++++++++++++++++++++++++++++++++++ wire/peer_wire.h | 2 ++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index 7fc4b0f05f5b..dc58c4a8c5e8 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -181,7 +181,8 @@ bool handle_peer_gossip_or_error(struct per_peer_state *pps, sync_crypto_write(pps, take(pong)); return true; } else if (is_msg_for_gossipd(msg)) { - gossip_rcvd_filter_add(pps->grf, msg); + if (is_msg_gossip_broadcast(msg)) + gossip_rcvd_filter_add(pps->grf, msg); wire_sync_write(pps->gossip_fd, msg); /* wire_sync_write takes, so don't take again. */ return true; diff --git a/wire/peer_wire.c b/wire/peer_wire.c index 340085d282cc..33156f8be6c1 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -108,6 +108,60 @@ bool is_msg_for_gossipd(const u8 *cursor) return false; } +bool is_msg_gossip_broadcast(const u8 *cursor) +{ + switch ((enum peer_wire)fromwire_peektype(cursor)) { + case WIRE_CHANNEL_ANNOUNCEMENT: + case WIRE_NODE_ANNOUNCEMENT: + case WIRE_CHANNEL_UPDATE: + return true; + case WIRE_QUERY_SHORT_CHANNEL_IDS: + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: + case WIRE_QUERY_CHANNEL_RANGE: + case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_WARNING: + case WIRE_INIT: + case WIRE_PING: + case WIRE_PONG: + case WIRE_ERROR: + case WIRE_OPEN_CHANNEL: + case WIRE_ACCEPT_CHANNEL: + case WIRE_FUNDING_CREATED: + case WIRE_FUNDING_SIGNED: + case WIRE_FUNDING_LOCKED: + case WIRE_SHUTDOWN: + case WIRE_CLOSING_SIGNED: + case WIRE_UPDATE_ADD_HTLC: + case WIRE_UPDATE_FULFILL_HTLC: + case WIRE_UPDATE_FAIL_HTLC: + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + case WIRE_COMMITMENT_SIGNED: + case WIRE_REVOKE_AND_ACK: + case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: + case WIRE_CHANNEL_REESTABLISH: + case WIRE_ANNOUNCEMENT_SIGNATURES: + case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_TX_ADD_INPUT: + case WIRE_TX_REMOVE_INPUT: + case WIRE_TX_ADD_OUTPUT: + case WIRE_TX_REMOVE_OUTPUT: + case WIRE_TX_COMPLETE: + case WIRE_TX_SIGNATURES: + case WIRE_OPEN_CHANNEL2: + case WIRE_ACCEPT_CHANNEL2: + case WIRE_INIT_RBF: + case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif + break; + } + return false; +} + /* Return true if it's an unknown ODD message. cursor is a tal ptr. */ bool is_unknown_msg_discardable(const u8 *cursor) { diff --git a/wire/peer_wire.h b/wire/peer_wire.h index 12c951b8ff3e..d57a84d20bd7 100644 --- a/wire/peer_wire.h +++ b/wire/peer_wire.h @@ -23,6 +23,8 @@ bool is_unknown_msg_discardable(const u8 *cursor); /* Return true if it's a message for gossipd. */ bool is_msg_for_gossipd(const u8 *cursor); +/* Return true if it's a gossip update or announcement. */ +bool is_msg_gossip_broadcast(const u8 *cursor); /* Extract channel_id from various packets, return true if possible. */ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id); From 5111f39d2a05c1e75b3aa269ba9f97e1355692f6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:53:09 +1030 Subject: [PATCH 0175/1530] connectd: clean up lightningd connection handling. They all returned the same thing anyway. Signed-off-by: Rusty Russell --- connectd/connectd.c | 56 +++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index e855552f9c74..12adfdc97310 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1537,9 +1537,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, /*~ Parse the incoming connect init message from lightningd ("master") and * assign config variables to the daemon; it should be the first message we * get. */ -static struct io_plan *connect_init(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void connect_init(struct daemon *daemon, const u8 *msg) { struct wireaddr *proxyaddr; struct wireaddr_internal *binding; @@ -1606,15 +1604,10 @@ static struct io_plan *connect_init(struct io_conn *conn, take(towire_connectd_init_reply(NULL, binding, announcable))); - - /* Read the next message. */ - return daemon_conn_read_next(conn, daemon->master); } /*~ lightningd tells us to go! */ -static struct io_plan *connect_activate(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void connect_activate(struct daemon *daemon, const u8 *msg) { bool do_listen; @@ -1645,7 +1638,6 @@ static struct io_plan *connect_activate(struct io_conn *conn, /* OK, we're ready! */ daemon_conn_send(daemon->master, take(towire_connectd_activate_reply(NULL))); - return daemon_conn_read_next(conn, daemon->master); } /* BOLT #10: @@ -1861,8 +1853,7 @@ static void try_connect_peer(struct daemon *daemon, } /* lightningd tells us to connect to a peer by id, with optional addr hint. */ -static struct io_plan *connect_to_peer(struct io_conn *conn, - struct daemon *daemon, const u8 *msg) +static void connect_to_peer(struct daemon *daemon, const u8 *msg) { struct node_id id; u32 seconds_waited; @@ -1874,7 +1865,6 @@ static struct io_plan *connect_to_peer(struct io_conn *conn, master_badmsg(WIRE_CONNECTD_CONNECT_TO_PEER, msg); try_connect_peer(daemon, &id, seconds_waited, addrhint); - return daemon_conn_read_next(conn, daemon->master); } /* A peer is gone: clean things up. */ @@ -1900,8 +1890,7 @@ static void cleanup_dead_peer(struct daemon *daemon, const struct node_id *id) } /* lightningd tells us a peer has disconnected. */ -static struct io_plan *peer_disconnected(struct io_conn *conn, - struct daemon *daemon, const u8 *msg) +static void peer_disconnected(struct daemon *daemon, const u8 *msg) { struct node_id id; @@ -1909,9 +1898,6 @@ static struct io_plan *peer_disconnected(struct io_conn *conn, 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 @@ -1932,8 +1918,8 @@ static struct io_plan *send_final_msg(struct io_conn *conn, u8 *msg) } /* 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) +static void 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); @@ -1969,15 +1955,10 @@ static struct io_plan *peer_final_msg(struct io_conn *conn, /* 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); } #if DEVELOPER -static struct io_plan *dev_connect_memleak(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) { struct htable *memtable; bool found_leak; @@ -1991,7 +1972,6 @@ static struct io_plan *dev_connect_memleak(struct io_conn *conn, daemon_conn_send(daemon->master, take(towire_connectd_dev_memleak_reply(NULL, found_leak))); - return daemon_conn_read_next(conn, daemon->master); } #endif /* DEVELOPER */ @@ -2005,23 +1985,29 @@ static struct io_plan *recv_req(struct io_conn *conn, * connect requests and disconnected messages. */ switch (t) { case WIRE_CONNECTD_INIT: - return connect_init(conn, daemon, msg); + connect_init(daemon, msg); + goto out; case WIRE_CONNECTD_ACTIVATE: - return connect_activate(conn, daemon, msg); + connect_activate(daemon, msg); + goto out; case WIRE_CONNECTD_CONNECT_TO_PEER: - return connect_to_peer(conn, daemon, msg); + connect_to_peer(daemon, msg); + goto out; case WIRE_CONNECTD_PEER_DISCONNECTED: - return peer_disconnected(conn, daemon, msg); + peer_disconnected(daemon, msg); + goto out; case WIRE_CONNECTD_PEER_FINAL_MSG: - return peer_final_msg(conn, daemon, msg); + peer_final_msg(conn, daemon, msg); + goto out; case WIRE_CONNECTD_DEV_MEMLEAK: #if DEVELOPER - return dev_connect_memleak(conn, daemon, msg); + dev_connect_memleak(daemon, msg); + goto out; #endif /* We send these, we don't receive them */ case WIRE_CONNECTD_INIT_REPLY: @@ -2036,6 +2022,10 @@ static struct io_plan *recv_req(struct io_conn *conn, /* Master shouldn't give bad requests. */ status_failed(STATUS_FAIL_MASTER_IO, "%i: %s", t, tal_hex(tmpctx, msg)); + +out: + /* Read the next message. */ + return daemon_conn_read_next(conn, daemon->master); } /*~ UNUSED is defined to an __attribute__ for GCC; at one stage we tried to use From 35e3c1866e08b898ed4948a88e7fedbf07cc4c49 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:54:09 +1030 Subject: [PATCH 0176/1530] common: generalize extract_channel_id(). connectd is going to end up using this do demux; make it fast and complete. Fixing this reveals a problem in openingd: it now extracts the channel_id from funding_signed (which is where we transition off the temporary), and gets upset. So fix that. Signed-off-by: Rusty Russell --- channeld/test/run-commit_tx.c | 2 +- cli/test/run-human-mode.c | 2 +- cli/test/run-large-input.c | 2 +- cli/test/run-remove-hint.c | 2 +- common/channel_id.c | 4 +- common/channel_id.h | 2 +- common/read_peer_msg.c | 3 + common/test/run-blindedpath_enctlv.c | 2 +- common/test/run-blindedpath_onion.c | 2 +- common/test/run-bolt12_merkle-json.c | 2 +- common/test/run-bolt12_merkle.c | 2 +- common/test/run-gossmap_local.c | 2 +- common/test/run-route-specific.c | 2 +- common/test/run-route.c | 2 +- .../test/run-route_blinding_override_test.c | 2 +- common/test/run-route_blinding_test.c | 2 +- lightningd/test/run-find_my_abspath.c | 2 +- lightningd/test/run-invoice-select-inchan.c | 2 +- lightningd/test/run-jsonrpc.c | 2 +- lightningd/test/run-log-pruning.c | 2 +- lightningd/test/run-shuffle_fds.c | 2 +- openingd/openingd.c | 16 +- plugins/test/run-funder_policy.c | 2 +- plugins/test/run-route-overlong.c | 2 +- wire/peer_wire.c | 257 ++++++++++++++++-- wire/test/run-tlvstream.c | 2 +- 26 files changed, 264 insertions(+), 60 deletions(-) diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index 7610fbd9a3b7..f222e7ae3d59 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -24,7 +24,7 @@ static bool print_superverbose; bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index a3a122b1d0e8..bf7c4a25f82a 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -70,7 +70,7 @@ struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UN bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index 7bb64233326b..40c8d67f91e9 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -70,7 +70,7 @@ struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UN bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index 3311a9aec7d3..18e21e490e45 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -73,7 +73,7 @@ struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UN bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/common/channel_id.c b/common/channel_id.c index 517427b7469c..c2f4ce6e04dc 100644 --- a/common/channel_id.c +++ b/common/channel_id.c @@ -88,10 +88,10 @@ void towire_channel_id(u8 **pptr, const struct channel_id *channel_id) towire(pptr, channel_id, sizeof(*channel_id)); } -void fromwire_channel_id(const u8 **cursor, size_t *max, +bool fromwire_channel_id(const u8 **cursor, size_t *max, struct channel_id *channel_id) { - fromwire(cursor, max, channel_id, sizeof(*channel_id)); + return fromwire(cursor, max, channel_id, sizeof(*channel_id)) != NULL; } REGISTER_TYPE_TO_HEXSTR(channel_id); diff --git a/common/channel_id.h b/common/channel_id.h index 81b6ef0248ea..d6ce8687aaf7 100644 --- a/common/channel_id.h +++ b/common/channel_id.h @@ -38,6 +38,6 @@ void derive_tmp_channel_id(struct channel_id *channel_id, /* Marshalling/unmarshalling functions */ void towire_channel_id(u8 **pptr, const struct channel_id *channel_id); -void fromwire_channel_id(const u8 **cursor, size_t *max, +bool fromwire_channel_id(const u8 **cursor, size_t *max, struct channel_id *channel_id); #endif /* LIGHTNING_COMMON_CHANNEL_ID_H */ diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index dc58c4a8c5e8..f40c15c46829 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -103,6 +103,9 @@ bool is_peer_error(const tal_t *ctx, const u8 *msg, bool is_wrong_channel(const u8 *msg, const struct channel_id *expected, struct channel_id *actual) { + if (!expected) + return false; + if (!extract_channel_id(msg, actual)) return false; diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index a1d32e5a3af3..9c619b7b3fd9 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -50,7 +50,7 @@ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c index 191e157af697..4b51804ea175 100644 --- a/common/test/run-blindedpath_onion.c +++ b/common/test/run-blindedpath_onion.c @@ -56,7 +56,7 @@ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index eb62379cbc7c..ebdd325d5886 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -19,7 +19,7 @@ /* AUTOGENERATED MOCKS START */ /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/common/test/run-bolt12_merkle.c b/common/test/run-bolt12_merkle.c index 1610e3ea8737..83abb27d133a 100644 --- a/common/test/run-bolt12_merkle.c +++ b/common/test/run-bolt12_merkle.c @@ -22,7 +22,7 @@ int features_unsupported(const struct feature_set *our_features UNNEEDED, enum feature_place p UNNEEDED) { fprintf(stderr, "features_unsupported called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_onionmsg_path */ diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 605c2cb8d54b..1d8d19cc9cf0 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -16,7 +16,7 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for towire_bigsize */ diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index e58ef4689afc..a591f16cd5bb 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -25,7 +25,7 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_tlv */ diff --git a/common/test/run-route.c b/common/test/run-route.c index e1fac36ebfe4..ad6fadc8e60e 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -18,7 +18,7 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_tlv */ diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c index 5025c8ff7d2f..b8e2552cb5f2 100644 --- a/common/test/run-route_blinding_override_test.c +++ b/common/test/run-route_blinding_override_test.c @@ -63,7 +63,7 @@ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c index d9f141839536..22076d9568b7 100644 --- a/common/test/run-route_blinding_test.c +++ b/common/test/run-route_blinding_test.c @@ -63,7 +63,7 @@ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index f7896a0c1724..f64cd1d4f1a4 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -73,7 +73,7 @@ void free_htlcs(struct lightningd *ld UNNEEDED, const struct channel *channel UN bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 8a8e54769709..020921cbd682 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -203,7 +203,7 @@ void fixup_htlcs_out(struct lightningd *ld UNNEEDED) bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_channeld_dev_memleak_reply */ diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 1479612d1138..01806dadf442 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -22,7 +22,7 @@ const char *feerate_name(enum feerate feerate UNNEEDED) bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index 6bda5f337c18..3db0a81dc676 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -21,7 +21,7 @@ struct command_result *command_success(struct command *cmd UNNEEDED, bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/lightningd/test/run-shuffle_fds.c b/lightningd/test/run-shuffle_fds.c index 7f9a3806036c..c2a00d667439 100644 --- a/lightningd/test/run-shuffle_fds.c +++ b/lightningd/test/run-shuffle_fds.c @@ -57,7 +57,7 @@ void fatal(const char *fmt UNNEEDED, ...) bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/openingd/openingd.c b/openingd/openingd.c index 54f4be8462d9..fede6c8119d6 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -177,7 +177,8 @@ static void set_reserve(struct state *state, const struct amount_sat dust_limit) /*~ Handle random messages we might get during opening negotiation, (eg. gossip) * returning the first non-handled one, or NULL if we aborted negotiation. */ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, - bool am_opener) + bool am_opener, + const struct channel_id *alternate) { /* This is an event loop of its own. That's generally considered poor * form, but we use it in a very limited way. */ @@ -250,7 +251,8 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, * keeps things simple: if we wanted to change this, we would * probably be best with another daemon to de-multiplex them; * this could be connectd itself, in fact. */ - if (is_wrong_channel(msg, &state->channel_id, &actual)) { + if (is_wrong_channel(msg, &state->channel_id, &actual) + && is_wrong_channel(msg, alternate, &actual)) { status_debug("Rejecting %s for unknown channel_id %s", peer_wire_name(fromwire_peektype(msg)), type_to_string(tmpctx, struct channel_id, @@ -401,7 +403,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) "Funding channel start: offered, now waiting for accept_channel"); /* ... since their reply should be immediate. */ - msg = opening_negotiate_msg(tmpctx, state, true); + msg = opening_negotiate_msg(tmpctx, state, true, NULL); if (!msg) return NULL; @@ -652,8 +654,10 @@ static bool funder_finalize_channel_setup(struct state *state, "Funding channel: create first tx, now waiting for their signature"); /* Now they send us their signature for that first commitment - * transaction. */ - msg = opening_negotiate_msg(tmpctx, state, true); + * transaction. Note that errors may refer to the temporary channel + * id (state->channel_id), but success should refer to the new + * "cid" */ + msg = opening_negotiate_msg(tmpctx, state, true, &cid); if (!msg) goto fail; @@ -1052,7 +1056,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) "Incoming channel: accepted, now waiting for them to create funding tx"); /* This is a loop which handles gossip until we get a non-gossip msg */ - msg = opening_negotiate_msg(tmpctx, state, false); + msg = opening_negotiate_msg(tmpctx, state, false, NULL); if (!msg) return NULL; diff --git a/plugins/test/run-funder_policy.c b/plugins/test/run-funder_policy.c index 8efb543e5958..a91a5867615e 100644 --- a/plugins/test/run-funder_policy.c +++ b/plugins/test/run-funder_policy.c @@ -9,7 +9,7 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for fromwire_node_id */ diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 3999a31cfe87..2ca73fd9452e 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -21,7 +21,7 @@ bool feature_offered(const u8 *features UNNEEDED, size_t f UNNEEDED) bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for json_add_amount_msat_compat */ diff --git a/wire/peer_wire.c b/wire/peer_wire.c index 33156f8be6c1..1ed291db5c35 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include static bool unknown_type(enum peer_wire t) @@ -172,38 +173,234 @@ bool is_unknown_msg_discardable(const u8 *cursor) /* Extract channel_id from various packets, return true if possible. */ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id) { - struct amount_sat ignored_sat; - struct amount_msat ignored_msat; - u64 ignored_u64; - u32 ignored_u32; - u16 ignored_u16; - u8 ignored_u8; - struct pubkey ignored_pubkey; - struct bitcoin_blkid ignored_chainhash; - struct secret ignored_secret; - struct tlv_open_channel_tlvs *tlvs = tlv_open_channel_tlvs_new(tmpctx); -#if EXPERIMENTAL_FEATURES - struct tlv_channel_reestablish_tlvs *reestab_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); -#endif + const u8 *cursor = in_pkt; + size_t max = tal_bytelen(in_pkt); + enum peer_wire t; + + t = fromwire_u16(&cursor, &max); + + /* We carefully quote bolts here, in case anything changes! */ + switch (t) { + /* These ones don't have a channel_id */ + case WIRE_INIT: + case WIRE_PING: + case WIRE_PONG: + case WIRE_CHANNEL_ANNOUNCEMENT: + case WIRE_NODE_ANNOUNCEMENT: + case WIRE_CHANNEL_UPDATE: + case WIRE_QUERY_SHORT_CHANNEL_IDS: + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: + case WIRE_QUERY_CHANNEL_RANGE: + case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: + return false; + + /* Special cases: */ + case WIRE_ERROR: + /* BOLT #1: + * 1. type: 17 (`error`) + * 2. data: + * * [`channel_id`:`channel_id`] + *... + * The channel is referred to by `channel_id`, unless + * `channel_id` is 0 + */ + /* fall thru */ + case WIRE_WARNING: + /* BOLT-warning #1: + * 1. type: 1 (`warning`) + * 2. data: + * * [`channel_id`:`channel_id`] + *... + * The channel is referred to by `channel_id`, unless + * `channel_id` is 0 + */ + if (!fromwire_channel_id(&cursor, &max, channel_id)) + return false; + if (memeqzero(channel_id->id, sizeof(channel_id->id))) + return false; + return true; - if (fromwire_channel_reestablish(in_pkt, channel_id, - &ignored_u64, &ignored_u64, - &ignored_secret, &ignored_pubkey + case WIRE_OPEN_CHANNEL: + /* BOLT #2: + * 1. type: 32 (`open_channel`) + * 2. data: + * * [`chain_hash`:`chain_hash`] + * * [`32*byte`:`temporary_channel_id`] + */ + case WIRE_OPEN_CHANNEL2: + /* BOLT-dualfund #2: + * 1. type: 64 (`open_channel2`) + * 2. data: + * * [`chain_hash`:`chain_hash`] + * * [`channel_id`:`zerod_channel_id`] + */ + + /* Skip over chain_hash */ + fromwire_pad(&cursor, &max, sizeof(struct bitcoin_blkid)); + + /* These have them at the start */ + case WIRE_TX_ADD_INPUT: + /* BOLT-dualfund #2: + * 1. type: 66 (`tx_add_input`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_TX_ADD_OUTPUT: + /* BOLT-dualfund #2: + * 1. type: 67 (`tx_add_output`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_TX_REMOVE_INPUT: + /* BOLT-dualfund #2: + * 1. type: 68 (`tx_remove_input`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_TX_REMOVE_OUTPUT: + /* BOLT-dualfund #2: + * 1. type: 69 (`tx_remove_output`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_TX_COMPLETE: + /* BOLT-dualfund #2: + * 1. type: 70 (`tx_complete`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_TX_SIGNATURES: + /* BOLT-dualfund #2: + * 1. type: 71 (`tx_signatures`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_ACCEPT_CHANNEL: + /* BOLT #2: + * 1. type: 33 (`accept_channel`) + * 2. data: + * * [`32*byte`:`temporary_channel_id`] + */ + case WIRE_FUNDING_CREATED: + /* BOLT #2: + * 1. type: 34 (`funding_created`) + * 2. data: + * * [`32*byte`:`temporary_channel_id`] + */ + case WIRE_FUNDING_SIGNED: + /* BOLT #2: + * 1. type: 35 (`funding_signed`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_FUNDING_LOCKED: + /* BOLT #2: + * 1. type: 36 (`funding_locked`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_ACCEPT_CHANNEL2: + /* BOLT-dualfund #2: + * 1. type: 65 (`accept_channel2`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_INIT_RBF: + /* BOLT-dualfund #2: + * 1. type: 72 (`init_rbf`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_ACK_RBF: + /* BOLT-dualfund #2: + * 1. type: 73 (`ack_rbf`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_SHUTDOWN: + /* BOLT #2: + * 1. type: 38 (`shutdown`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_CLOSING_SIGNED: + /* BOLT #2: + * 1. type: 39 (`closing_signed`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_UPDATE_ADD_HTLC: + /* BOLT #2: + * 1. type: 128 (`update_add_htlc`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_UPDATE_FULFILL_HTLC: + /* BOLT #2: + * 1. type: 130 (`update_fulfill_htlc`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_UPDATE_FAIL_HTLC: + /* BOLT #2: + * 1. type: 131 (`update_fail_htlc`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + /* BOLT #2: + * 1. type: 135 (`update_fail_malformed_htlc`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_COMMITMENT_SIGNED: + /* BOLT #2: + * 1. type: 132 (`commitment_signed`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_REVOKE_AND_ACK: + /* BOLT #2: + * 1. type: 133 (`revoke_and_ack`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_UPDATE_FEE: + /* BOLT #2: + * 1. type: 134 (`update_fee`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_UPDATE_BLOCKHEIGHT: + /* BOLT-liquidity-ads #2: + * 1. type: 137 (`update_blockheight`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_CHANNEL_REESTABLISH: + /* BOLT #2: + * 1. type: 136 (`channel_reestablish`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ + case WIRE_ANNOUNCEMENT_SIGNATURES: + /* BOLT #7: + * 1. type: 259 (`announcement_signatures`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ #if EXPERIMENTAL_FEATURES - , reestab_tlvs + case WIRE_STFU: + /* BOLT-quiescent #2: + * 1. type: 2 (`stfu`) + * 2. data: + * * [`channel_id`:`channel_id`] + */ #endif - )) - return true; - if (fromwire_open_channel(in_pkt, &ignored_chainhash, - channel_id, &ignored_sat, - &ignored_msat, &ignored_sat, - &ignored_msat, &ignored_sat, - &ignored_msat, &ignored_u32, - &ignored_u16, &ignored_u16, - &ignored_pubkey, &ignored_pubkey, - &ignored_pubkey, &ignored_pubkey, - &ignored_pubkey, &ignored_pubkey, - &ignored_u8, tlvs)) - return true; + return fromwire_channel_id(&cursor, &max, channel_id); + } return false; } diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index b658d85f89d9..76b27b61252e 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -26,7 +26,7 @@ static const char *reason; const struct chainparams *chainparams_by_chainhash(const struct bitcoin_blkid *chain_hash UNNEEDED) { fprintf(stderr, "chainparams_by_chainhash called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ -void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } /* Generated stub for towire_channel_id */ From 2fb58772a49238e9655436c6a79f371109001ea8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:55:09 +1030 Subject: [PATCH 0177/1530] common/socket_close: don't wait forever. The "read until closed" trick doesn't work if the other end doesn't close (as found in the next patch, where we use DEV_DISCONNECT_DISABLE_AFTER). Signed-off-by: Rusty Russell --- common/socket_close.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/common/socket_close.c b/common/socket_close.c index 3c192e47e38d..7d19ec76814b 100644 --- a/common/socket_close.c +++ b/common/socket_close.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include @@ -18,9 +20,15 @@ Simplified (minus all the error checks): close(fd); */ +/* makes read() return EINTR */ +static void break_read(int signum) +{ +} + bool socket_close(int fd) { char unused[64]; + struct sigaction act, old_act; int sys_res; sys_res = shutdown(fd, SHUT_WR); @@ -29,20 +37,22 @@ bool socket_close(int fd) return false; } - for (;;) { - do { - sys_res = read(fd, unused, sizeof(unused)); - } while (sys_res < 0 && errno == EINTR); - if (sys_res < 0) { - close_noerr(fd); - return false; - } - if (sys_res == 0) - break; - } + /* Let's not get too enthusiastic about waiting. */ + memset(&act, 0, sizeof(act)); + act.sa_handler = break_read; + sigaction(SIGALRM, &act, &old_act); - if (close(fd) < 0) + alarm(5); + + while ((sys_res = read(fd, unused, sizeof(unused))) > 0); + + alarm(0); + sigaction(SIGALRM, &old_act, NULL); + + if (sys_res < 0) { + close_noerr(fd); return false; - else - return true; + } + + return close(fd) == 0; } From 560fa06f42cc657299a49a39e87622fba37f48c8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:56:09 +1030 Subject: [PATCH 0178/1530] pytest: disable dev-disconnect blackhole tests. These would have to be done by connectd, not the local daemon, once it's intermediating. Otherwise the remote peer won't see any change. Signed-off-by: Rusty Russell --- tests/test_connection.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 1e717ebed519..0b32b3527878 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -493,6 +493,7 @@ def test_reconnect_signed(node_factory): l2.daemon.wait_for_log(' to CHANNELD_NORMAL') +@pytest.mark.skip('needs blackhold support') @pytest.mark.developer @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @@ -532,6 +533,7 @@ def test_reconnect_openingd(node_factory): l2.daemon.wait_for_log(r'channeld-chan#[0-9]: pid [0-9]+, msgfd [0-9]+') +@pytest.mark.skip('needs blackhold support') @pytest.mark.developer def test_reconnect_gossiping(node_factory): # connectd thinks we're still gossiping; peer reconnects. @@ -3035,6 +3037,7 @@ def test_restart_many_payments(node_factory, bitcoind): wait_for(lambda: 'pending' not in [p['status'] for p in n.rpc.listsendpays()['payments']]) +@pytest.mark.skip('needs blackhold support') @pytest.mark.developer("need dev-disconnect") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @@ -3090,6 +3093,7 @@ def test_fail_unconfirmed(node_factory, bitcoind, executor): l1.fundchannel(l2, 200000, wait_for_active=True) +@pytest.mark.skip('needs blackhold support') @pytest.mark.developer("need dev-disconnect") @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') @@ -3396,6 +3400,7 @@ def test_nonstatic_channel(node_factory, bitcoind): l1.rpc.close(l2.info['id']) +@pytest.mark.skip('needs blackhold support') @pytest.mark.developer("need --dev-timeout-secs") @pytest.mark.openchannel('v1') def test_connection_timeout(node_factory): From 888745be163efe02345e944f7e7d501d64e3744c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:57:09 +1030 Subject: [PATCH 0179/1530] dev_disconnect: remove @ marker. Once connectd is doing this, we can't close as soon as we send, and in fact we can't do 'fail write' either. Signed-off-by: Rusty Russell --- common/crypto_sync.c | 2 -- common/dev_disconnect.h | 4 +-- connectd/peer_exchange_initmsg.c | 2 -- tests/test_closing.py | 22 +++++++------- tests/test_connection.py | 48 +++++++------------------------ tests/test_misc.py | 4 +-- tests/test_opening.py | 49 ++------------------------------ 7 files changed, 28 insertions(+), 103 deletions(-) diff --git a/common/crypto_sync.c b/common/crypto_sync.c index 93733884b073..29d8cd92e89f 100644 --- a/common/crypto_sync.c +++ b/common/crypto_sync.c @@ -29,8 +29,6 @@ void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) case DEV_DISCONNECT_BEFORE: 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; diff --git a/common/dev_disconnect.h b/common/dev_disconnect.h index 3c0f4b8973d4..739e82355b7f 100644 --- a/common/dev_disconnect.h +++ b/common/dev_disconnect.h @@ -7,12 +7,10 @@ enum dev_disconnect { /* Do nothing. */ DEV_DISCONNECT_NORMAL = '=', - /* Close connection before sending packet (and fail write). */ + /* Close connection before sending packet. */ DEV_DISCONNECT_BEFORE = '-', /* Close connection after sending packet. */ DEV_DISCONNECT_AFTER = '+', - /* Close connection after dropping packet. */ - 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. */ diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 9398bb75adad..d960a83d0587 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -204,8 +204,6 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, case DEV_DISCONNECT_BEFORE: dev_sabotage_fd(io_conn_fd(conn), true); break; - case DEV_DISCONNECT_DROPPKT: - peer->msg = tal_free(peer->msg); /* FALL THRU */ case DEV_DISCONNECT_AFTER: next = peer_write_postclose; break; diff --git a/tests/test_closing.py b/tests/test_closing.py index 983064008216..dc4367086fb5 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -312,7 +312,6 @@ def test_closing_different_fees(node_factory, bitcoind, executor): @pytest.mark.developer("needs DEVELOPER=1") def test_closing_negotiation_reconnect(node_factory, bitcoind): disconnects = ['-WIRE_CLOSING_SIGNED', - '@WIRE_CLOSING_SIGNED', '+WIRE_CLOSING_SIGNED'] l1, l2 = node_factory.line_graph(2, opts=[{'disconnect': disconnects, 'may_reconnect': True}, @@ -1956,7 +1955,7 @@ def test_onchain_dust_out(node_factory, bitcoind, executor): coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') # HTLC 1->2, 1 fails after it's irrevocably committed - disconnects = ['@WIRE_REVOKE_AND_ACK', 'permfail'] + disconnects = ['-WIRE_REVOKE_AND_ACK', 'permfail'] # Feerates identical so we don't get gratuitous commit to update them l1, l2 = node_factory.line_graph(2, opts=[{'disconnect': disconnects, @@ -3481,13 +3480,13 @@ def test_htlc_rexmit_while_closing(node_factory, executor): @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: simple""" - disconnects = ['@WIRE_CLOSING_SIGNED'] + disconnects = ['xWIRE_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}]) + 'disconnect': disconnects}, + {'may_reconnect': True, + 'dev-no-reconnect': None}]) l1.pay(l2, 200000) @@ -3498,6 +3497,7 @@ def test_you_forgot_closed_channel(node_factory, executor): assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' # l1 reconnects, it should succeed. + l1.rpc.disconnect(l2.info['id'], force=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) fut.result(TIMEOUT) @@ -3505,13 +3505,13 @@ def test_you_forgot_closed_channel(node_factory, executor): @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'] + disconnects = ['xWIRE_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}]) + 'disconnect': disconnects}, + {'may_reconnect': True, + 'dev-no-reconnect': None}]) l1.pay(l2, 200000) @@ -3533,6 +3533,8 @@ def no_new_blocks(req): wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'ONCHAIN') # l1 reconnects, it should succeed. + # l1 will disconnect once it sees block + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is False) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) fut.result(TIMEOUT) diff --git a/tests/test_connection.py b/tests/test_connection.py index 0b32b3527878..7faf5865a281 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -289,7 +289,6 @@ def test_channel_abandon(node_factory, bitcoind): def test_disconnect(node_factory): # These should all make us fail disconnects = ['-WIRE_INIT', - '@WIRE_INIT', '+WIRE_INIT'] l1 = node_factory.get_node(disconnect=disconnects) l2 = node_factory.get_node() @@ -298,8 +297,6 @@ def test_disconnect(node_factory): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) with pytest.raises(RpcError): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - with pytest.raises(RpcError): - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Should have 3 connect fails. for d in disconnects: @@ -317,22 +314,16 @@ def test_disconnect(node_factory): def test_disconnect_opener(node_factory): # Now error on opener side during channel open. disconnects = ['-WIRE_OPEN_CHANNEL', - '@WIRE_OPEN_CHANNEL', '+WIRE_OPEN_CHANNEL', - '-WIRE_FUNDING_CREATED', - '@WIRE_FUNDING_CREATED'] + '-WIRE_FUNDING_CREATED'] if EXPERIMENTAL_DUAL_FUND: disconnects = ['-WIRE_OPEN_CHANNEL2', - '@WIRE_OPEN_CHANNEL2', '+WIRE_OPEN_CHANNEL2', '-WIRE_TX_ADD_INPUT', - '@WIRE_TX_ADD_INPUT', '+WIRE_TX_ADD_INPUT', '-WIRE_TX_ADD_OUTPUT', - '@WIRE_TX_ADD_OUTPUT', '+WIRE_TX_ADD_OUTPUT', '-WIRE_TX_COMPLETE', - '@WIRE_TX_COMPLETE', '+WIRE_TX_COMPLETE'] l1 = node_factory.get_node(disconnect=disconnects) @@ -361,14 +352,11 @@ def test_disconnect_opener(node_factory): def test_disconnect_fundee(node_factory): # Now error on fundee side during channel open. disconnects = ['-WIRE_ACCEPT_CHANNEL', - '@WIRE_ACCEPT_CHANNEL', '+WIRE_ACCEPT_CHANNEL'] if EXPERIMENTAL_DUAL_FUND: disconnects = ['-WIRE_ACCEPT_CHANNEL2', - '@WIRE_ACCEPT_CHANNEL2', '+WIRE_ACCEPT_CHANNEL2', '-WIRE_TX_COMPLETE', - '@WIRE_TX_COMPLETE', '+WIRE_TX_COMPLETE'] l1 = node_factory.get_node() @@ -397,16 +385,12 @@ def test_disconnect_fundee(node_factory): def test_disconnect_fundee_v2(node_factory): # Now error on fundee side during channel open, with them funding disconnects = ['-WIRE_ACCEPT_CHANNEL2', - '@WIRE_ACCEPT_CHANNEL2', '+WIRE_ACCEPT_CHANNEL2', '-WIRE_TX_ADD_INPUT', - '@WIRE_TX_ADD_INPUT', '+WIRE_TX_ADD_INPUT', '-WIRE_TX_ADD_OUTPUT', - '@WIRE_TX_ADD_OUTPUT', '+WIRE_TX_ADD_OUTPUT', '-WIRE_TX_COMPLETE', - '@WIRE_TX_COMPLETE', '+WIRE_TX_COMPLETE'] l1 = node_factory.get_node() @@ -440,9 +424,9 @@ def test_disconnect_fundee_v2(node_factory): def test_disconnect_half_signed(node_factory): # Now, these are the corner cases. Fundee sends funding_signed, # but opener doesn't receive it. - disconnects = ['@WIRE_FUNDING_SIGNED'] + disconnects = ['-WIRE_FUNDING_SIGNED'] if EXPERIMENTAL_DUAL_FUND: - disconnects = ['@WIRE_COMMITMENT_SIGNED'] + disconnects = ['-WIRE_COMMITMENT_SIGNED'] l1 = node_factory.get_node() l2 = node_factory.get_node(disconnect=disconnects) @@ -567,7 +551,7 @@ def test_reconnect_no_update(node_factory, executor, bitcoind): reconnects. See comments for details. """ - disconnects = ["@WIRE_FUNDING_LOCKED", "@WIRE_SHUTDOWN"] + disconnects = ["-WIRE_FUNDING_LOCKED", "-WIRE_SHUTDOWN"] # Allow bad gossip because it might receive WIRE_CHANNEL_UPDATE before # announcement of the disconnection l1 = node_factory.get_node(may_reconnect=True, allow_bad_gossip=True) @@ -591,7 +575,7 @@ def test_reconnect_no_update(node_factory, executor, bitcoind): # For closingd reconnection l1.daemon.start() - # Close will trigger the @WIRE_SHUTDOWN and we then wait for the + # 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"channeld.* Retransmitting funding_locked for channel") @@ -645,7 +629,6 @@ def test_connect_stresstest(node_factory, executor): def test_reconnect_normal(node_factory): # Should reconnect fine even if locked message gets lost. disconnects = ['-WIRE_FUNDING_LOCKED', - '@WIRE_FUNDING_LOCKED', '+WIRE_FUNDING_LOCKED'] l1 = node_factory.get_node(disconnect=disconnects, may_reconnect=True) @@ -661,8 +644,7 @@ def test_reconnect_normal(node_factory): def test_reconnect_sender_add1(node_factory): # Fail after add is OK, will cause payment failure though. disconnects = ['-WIRE_UPDATE_ADD_HTLC-nocommit', - '+WIRE_UPDATE_ADD_HTLC-nocommit', - '@WIRE_UPDATE_ADD_HTLC-nocommit'] + '+WIRE_UPDATE_ADD_HTLC-nocommit'] # Feerates identical so we don't get gratuitous commit to update them l1 = node_factory.get_node(disconnect=disconnects, @@ -697,10 +679,8 @@ def test_reconnect_sender_add1(node_factory): @pytest.mark.openchannel('v2') def test_reconnect_sender_add(node_factory): disconnects = ['-WIRE_COMMITMENT_SIGNED', - '@WIRE_COMMITMENT_SIGNED', '+WIRE_COMMITMENT_SIGNED', '-WIRE_REVOKE_AND_ACK', - '@WIRE_REVOKE_AND_ACK', '+WIRE_REVOKE_AND_ACK'] if EXPERIMENTAL_DUAL_FUND: disconnects = ['=WIRE_COMMITMENT_SIGNED'] + disconnects @@ -733,10 +713,8 @@ def test_reconnect_sender_add(node_factory): @pytest.mark.openchannel('v2') def test_reconnect_receiver_add(node_factory): disconnects = ['-WIRE_COMMITMENT_SIGNED', - '@WIRE_COMMITMENT_SIGNED', '+WIRE_COMMITMENT_SIGNED', '-WIRE_REVOKE_AND_ACK', - '@WIRE_REVOKE_AND_ACK', '+WIRE_REVOKE_AND_ACK'] if EXPERIMENTAL_DUAL_FUND: @@ -767,14 +745,11 @@ def test_reconnect_receiver_fulfill(node_factory): # Ordering matters: after +WIRE_UPDATE_FULFILL_HTLC, channeld # will continue and try to send WIRE_COMMITMENT_SIGNED: if # that's the next failure, it will do two in one run. - disconnects = ['@WIRE_UPDATE_FULFILL_HTLC', - '+WIRE_UPDATE_FULFILL_HTLC', + disconnects = ['+WIRE_UPDATE_FULFILL_HTLC', '-WIRE_UPDATE_FULFILL_HTLC', '-WIRE_COMMITMENT_SIGNED', - '@WIRE_COMMITMENT_SIGNED', '+WIRE_COMMITMENT_SIGNED', '-WIRE_REVOKE_AND_ACK', - '@WIRE_REVOKE_AND_ACK', '+WIRE_REVOKE_AND_ACK'] l1 = node_factory.get_node(may_reconnect=True) l2 = node_factory.get_node(disconnect=disconnects, @@ -801,7 +776,6 @@ def test_reconnect_receiver_fulfill(node_factory): @pytest.mark.openchannel('v2') def test_shutdown_reconnect(node_factory): disconnects = ['-WIRE_SHUTDOWN', - '@WIRE_SHUTDOWN', '+WIRE_SHUTDOWN'] l1 = node_factory.get_node(disconnect=disconnects, may_reconnect=True) @@ -1802,7 +1776,7 @@ def test_multifunding_disconnect(node_factory): ''' Test disconnection during multifundchannel ''' - # TODO: Note that @WIRE_FUNDING_SIGNED does not + # TODO: Note that -WIRE_FUNDING_SIGNED does not # work. # See test_disconnect_half_signed. # If disconnected when the peer believes it sent @@ -1812,9 +1786,7 @@ def test_multifunding_disconnect(node_factory): # never send it. disconnects = ["-WIRE_INIT", "-WIRE_ACCEPT_CHANNEL", - "@WIRE_ACCEPT_CHANNEL", - "+WIRE_ACCEPT_CHANNEL", - "-WIRE_FUNDING_SIGNED"] + "+WIRE_ACCEPT_CHANNEL"] l1 = node_factory.get_node() l2 = node_factory.get_node(disconnect=disconnects) l3 = node_factory.get_node() @@ -1833,7 +1805,7 @@ def test_multifunding_disconnect(node_factory): l1.rpc.multifundchannel(destinations) # TODO: failing at the fundchannel_complete phase - # (@WIRE_FUNDING_SIGNED +@WIRE_FUNDING_SIGNED) + # (-WIRE_FUNDING_SIGNED +-WIRE_FUNDING_SIGNED) # leaves the peer (l2 in this case) in a state # where it is waiting for an incoming channel, # even though we no longer have a channel going to diff --git a/tests/test_misc.py b/tests/test_misc.py index feb83ae0f16b..3cde1fad72e7 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -316,7 +316,7 @@ def test_htlc_out_timeout(node_factory, bitcoind, executor): """Test that we drop onchain if the peer doesn't time out HTLC""" # HTLC 1->2, 1 fails after it's irrevocably committed, can't reconnect - disconnects = ['@WIRE_REVOKE_AND_ACK'] + disconnects = ['-WIRE_REVOKE_AND_ACK'] # Feerates identical so we don't get gratuitous commit to update them l1 = node_factory.get_node(disconnect=disconnects, options={'dev-no-reconnect': None}, @@ -336,7 +336,7 @@ def test_htlc_out_timeout(node_factory, bitcoind, executor): executor.submit(l1.rpc.dev_pay, inv, use_shadow=False) # l1 will disconnect, and not reconnect. - l1.daemon.wait_for_log('dev_disconnect: @WIRE_REVOKE_AND_ACK') + l1.daemon.wait_for_log('dev_disconnect: -WIRE_REVOKE_AND_ACK') # Takes 6 blocks to timeout (cltv-final + 1), but we also give grace period of 1 block. # shadow route can add extra blocks! diff --git a/tests/test_opening.py b/tests/test_opening.py index 31e74d3d3e91..c56537bf81a9 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -508,7 +508,6 @@ def test_v2_rbf_multi(node_factory, bitcoind, chainparams): @pytest.mark.openchannel('v2') def test_rbf_reconnect_init(node_factory, bitcoind, chainparams): disconnects = ['-WIRE_INIT_RBF', - '@WIRE_INIT_RBF', '+WIRE_INIT_RBF'] l1, l2 = node_factory.get_nodes(2, @@ -559,7 +558,6 @@ def test_rbf_reconnect_init(node_factory, bitcoind, chainparams): @pytest.mark.openchannel('v2') def test_rbf_reconnect_ack(node_factory, bitcoind, chainparams): disconnects = ['-WIRE_ACK_RBF', - '@WIRE_ACK_RBF', '+WIRE_ACK_RBF'] l1, l2 = node_factory.get_nodes(2, @@ -611,13 +609,10 @@ def test_rbf_reconnect_ack(node_factory, bitcoind, chainparams): def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): disconnects = ['=WIRE_TX_ADD_INPUT', # Initial funding succeeds '-WIRE_TX_ADD_INPUT', - '@WIRE_TX_ADD_INPUT', '+WIRE_TX_ADD_INPUT', '-WIRE_TX_ADD_OUTPUT', - '@WIRE_TX_ADD_OUTPUT', '+WIRE_TX_ADD_OUTPUT', '-WIRE_TX_COMPLETE', - '@WIRE_TX_COMPLETE', '+WIRE_TX_COMPLETE'] l1, l2 = node_factory.get_nodes(2, @@ -652,14 +647,14 @@ def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): excess_as_change=True) # Run through TX_ADD wires - for d in disconnects[1:-3]: + for d in disconnects[1:-2]: l1.rpc.connect(l2.info['id'], 'localhost', l2.port) with pytest.raises(RpcError): l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) assert l1.rpc.getpeer(l2.info['id']) is not None # Now we finish off the completes failure check - for d in disconnects[-3:]: + for d in disconnects[-2:]: l1.rpc.connect(l2.info['id'], 'localhost', l2.port) bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) with pytest.raises(RpcError): @@ -679,8 +674,6 @@ def test_rbf_reconnect_tx_sigs(node_factory, bitcoind, chainparams): disconnects = ['=WIRE_TX_SIGNATURES', # Initial funding succeeds '-WIRE_TX_SIGNATURES', # When we send tx-sigs, RBF '=WIRE_TX_SIGNATURES', # When we reconnect - '@WIRE_TX_SIGNATURES', # When we RBF again - '=WIRE_TX_SIGNATURES', # When we reconnect '+WIRE_TX_SIGNATURES'] # When we RBF again l1, l2 = node_factory.get_nodes(2, @@ -753,52 +746,16 @@ def test_rbf_reconnect_tx_sigs(node_factory, bitcoind, chainparams): l1.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') l1.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') - # Now we initiate the RBF + # 2nd RBF bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt'], funding_feerate=next_feerate) update = l1.rpc.openchannel_update(chan_id, bump['psbt']) - - # Sign our inputs, and continue signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] # Second time we error after we send our sigs with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): l1.rpc.openchannel_signed(chan_id, signed_psbt) - # We reconnect and try again. feerate should have bumped - rate = int(find_next_feerate(l1, l2)[:-5]) - # We bump the feerate to beat the min-relay fee - next_feerate = '{}perkw'.format(rate * 2) - - startweight = 42 + 172 # base weight, funding output - initpsbt = l1.rpc.utxopsbt(chan_amount, next_feerate, startweight, - prev_utxos, reservedok=True, - min_witness_weight=110, - excess_as_change=True) - - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - - # l2 gets our sigs and broadcasts them - l2.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') - l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') - l2.daemon.wait_for_log('sendrawtx exit 0') - - # Wait until we've done re-establish, if we try to - # RBF again too quickly, it'll fail since they haven't - # had time to process our sigs yet - l1.daemon.wait_for_log('peer_in WIRE_CHANNEL_REESTABLISH') - l1.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') - - # 3rd RBF - bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt'], - funding_feerate=next_feerate) - update = l1.rpc.openchannel_update(chan_id, bump['psbt']) - signed_psbt = l1.rpc.signpsbt(update['psbt'])['signed_psbt'] - - # Third time we error after we send our sigs - with pytest.raises(RpcError, match='Owning subdaemon dualopend died'): - l1.rpc.openchannel_signed(chan_id, signed_psbt) - # l2 gets our sigs l2.daemon.wait_for_log('peer_in WIRE_TX_SIGNATURES') l2.daemon.wait_for_log('sendrawtx exit 0') From 70ed47d77a3f7ac63e78f17d9fb9543564783ce7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 13:56:40 +1030 Subject: [PATCH 0180/1530] channeld: add dev-disable-commit-after instead of dev-disconnect -nocommit It was always a hack, but an impossible one once connectd will be interpreting dev-disconnect! Signed-off-by: Rusty Russell --- channeld/channeld.c | 137 ++++++++++++++++++++--------------- channeld/channeld_wire.csv | 1 + common/dev_disconnect.c | 14 +--- common/dev_disconnect.h | 2 - lightningd/channel_control.c | 120 +++++++++++++++--------------- lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 + lightningd/options.c | 4 + tests/test_closing.py | 44 +++++------ tests/test_connection.py | 37 +++++----- tests/test_pay.py | 16 ++-- 11 files changed, 198 insertions(+), 181 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 4b1dcf12052c..de1a4741bf59 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -158,6 +158,10 @@ struct peer { struct msg_queue *update_queue; #endif +#if DEVELOPER + /* If set, don't fire commit counter when this hits 0 */ + u32 *dev_disable_commit; +#endif /* Information used for reestablishment. */ bool last_was_revoke; struct changed_htlc *last_sent_commit; @@ -1248,8 +1252,7 @@ static void send_commit(struct peer *peer) u32 feerate_target; #if DEVELOPER - /* Hack to suppress all commit sends if dev_disconnect says to */ - if (dev_suppress_commit) { + if (peer->dev_disable_commit && !*peer->dev_disable_commit) { peer->commit_timer = NULL; return; } @@ -1384,6 +1387,14 @@ static void send_commit(struct peer *peer) } else pbase = NULL; +#if DEVELOPER + if (peer->dev_disable_commit) { + (*peer->dev_disable_commit)--; + if (*peer->dev_disable_commit == 0) + status_unusual("dev-disable-commit-after: disabling"); + } +#endif + status_debug("Telling master we're about to commit..."); /* Tell master to save this next commit to database, then wait. */ msg = sending_commitsig_msg(NULL, peer->next_index[REMOTE], @@ -3655,7 +3666,7 @@ static void handle_send_ping(struct peer *peer, const u8 *msg) #if DEVELOPER static void handle_dev_reenable_commit(struct peer *peer) { - dev_suppress_commit = false; + peer->dev_disable_commit = tal_free(peer->dev_disable_commit); start_commit_timer(peer); status_debug("dev_reenable_commit"); wire_sync_write(MASTER_FD, @@ -3828,6 +3839,7 @@ static void init_channel(struct peer *peer) struct penalty_base *pbases; u8 *reestablish_only; struct channel_type *channel_type; + u32 *dev_disable_commit; /* Always NULL */ #if !DEVELOPER bool dev_fail_process_onionpacket; /* Ignored */ #endif @@ -3836,66 +3848,71 @@ static void init_channel(struct peer *peer) msg = wire_sync_read(tmpctx, MASTER_FD); if (!fromwire_channeld_init(peer, msg, - &chainparams, - &peer->our_features, - &peer->channel_id, - &funding, - &funding_sats, - &minimum_depth, - &peer->our_blockheight, - &blockheight_states, - &lease_expiry, - &conf[LOCAL], &conf[REMOTE], - &fee_states, - &peer->feerate_min, - &peer->feerate_max, - &peer->feerate_penalty, - &peer->their_commit_sig, - &peer->pps, - &funding_pubkey[REMOTE], - &points[REMOTE], - &peer->remote_per_commit, - &peer->old_remote_per_commit, - &opener, - &peer->fee_base, - &peer->fee_per_satoshi, - &local_msat, - &points[LOCAL], - &funding_pubkey[LOCAL], - &peer->node_ids[LOCAL], - &peer->node_ids[REMOTE], - &peer->commit_msec, - &peer->cltv_delta, - &peer->last_was_revoke, - &peer->last_sent_commit, - &peer->next_index[LOCAL], - &peer->next_index[REMOTE], - &peer->revocations_received, - &peer->htlc_id, - &htlcs, - &peer->funding_locked[LOCAL], - &peer->funding_locked[REMOTE], - &peer->short_channel_ids[LOCAL], - &reconnected, - &peer->send_shutdown, - &peer->shutdown_sent[REMOTE], - &peer->final_scriptpubkey, - &peer->channel_flags, - &fwd_msg, - &peer->announce_depth_reached, - &last_remote_per_commit_secret, - &peer->their_features, - &peer->remote_upfront_shutdown_script, - &remote_ann_node_sig, - &remote_ann_bitcoin_sig, - &channel_type, - &dev_fast_gossip, - &dev_fail_process_onionpacket, - &pbases, - &reestablish_only)) { + &chainparams, + &peer->our_features, + &peer->channel_id, + &funding, + &funding_sats, + &minimum_depth, + &peer->our_blockheight, + &blockheight_states, + &lease_expiry, + &conf[LOCAL], &conf[REMOTE], + &fee_states, + &peer->feerate_min, + &peer->feerate_max, + &peer->feerate_penalty, + &peer->their_commit_sig, + &peer->pps, + &funding_pubkey[REMOTE], + &points[REMOTE], + &peer->remote_per_commit, + &peer->old_remote_per_commit, + &opener, + &peer->fee_base, + &peer->fee_per_satoshi, + &local_msat, + &points[LOCAL], + &funding_pubkey[LOCAL], + &peer->node_ids[LOCAL], + &peer->node_ids[REMOTE], + &peer->commit_msec, + &peer->cltv_delta, + &peer->last_was_revoke, + &peer->last_sent_commit, + &peer->next_index[LOCAL], + &peer->next_index[REMOTE], + &peer->revocations_received, + &peer->htlc_id, + &htlcs, + &peer->funding_locked[LOCAL], + &peer->funding_locked[REMOTE], + &peer->short_channel_ids[LOCAL], + &reconnected, + &peer->send_shutdown, + &peer->shutdown_sent[REMOTE], + &peer->final_scriptpubkey, + &peer->channel_flags, + &fwd_msg, + &peer->announce_depth_reached, + &last_remote_per_commit_secret, + &peer->their_features, + &peer->remote_upfront_shutdown_script, + &remote_ann_node_sig, + &remote_ann_bitcoin_sig, + &channel_type, + &dev_fast_gossip, + &dev_fail_process_onionpacket, + &dev_disable_commit, + &pbases, + &reestablish_only)) { master_badmsg(WIRE_CHANNELD_INIT, msg); } +#if DEVELOPER + peer->dev_disable_commit = dev_disable_commit; +#endif + status_debug("option_static_remotekey = %u, option_anchor_outputs = %u", channel_type_has(channel_type, OPT_STATIC_REMOTEKEY), channel_type_has(channel_type, OPT_ANCHOR_OUTPUTS)); diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 7888fb4d4ce6..f2eca498d7bb 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -74,6 +74,7 @@ msgdata,channeld_init,remote_ann_bitcoin_sig,?secp256k1_ecdsa_signature, msgdata,channeld_init,desired_type,channel_type, msgdata,channeld_init,dev_fast_gossip,bool, msgdata,channeld_init,dev_fail_process_onionpacket,bool, +msgdata,channeld_init,dev_disable_commit,?u32, msgdata,channeld_init,num_penalty_bases,u32, msgdata,channeld_init,pbases,penalty_base,num_penalty_bases msgdata,channeld_init,reestablish_only_len,u16, diff --git a/common/dev_disconnect.c b/common/dev_disconnect.c index bb2453c30f50..0dd088290541 100644 --- a/common/dev_disconnect.c +++ b/common/dev_disconnect.c @@ -15,9 +15,6 @@ static int dev_disconnect_fd = -1; static char dev_disconnect_line[200]; static int dev_disconnect_count, dev_disconnect_len; -static bool dev_disconnect_nocommit; - -bool dev_suppress_commit; static void next_dev_disconnect(void) { @@ -36,12 +33,6 @@ static void next_dev_disconnect(void) dev_disconnect_line[r] = '\n'; dev_disconnect_len = strcspn(dev_disconnect_line, "\n"); dev_disconnect_line[dev_disconnect_len] = '\0'; - if (strends(dev_disconnect_line, "-nocommit")) { - dev_disconnect_line[strlen(dev_disconnect_line) - - strlen("-nocommit")] = '\0'; - dev_disconnect_nocommit = true; - } else - dev_disconnect_nocommit = false; asterisk = strchr(dev_disconnect_line, '*'); if (asterisk) { @@ -79,10 +70,7 @@ enum dev_disconnect dev_disconnect(int pkt_type) err(1, "lseek failure"); } - status_debug("dev_disconnect: %s%s", dev_disconnect_line, - dev_disconnect_nocommit ? "-nocommit" : ""); - if (dev_disconnect_nocommit) - dev_suppress_commit = true; + status_debug("dev_disconnect: %s", dev_disconnect_line); return dev_disconnect_line[0]; } diff --git a/common/dev_disconnect.h b/common/dev_disconnect.h index 739e82355b7f..0f4ae524a4f3 100644 --- a/common/dev_disconnect.h +++ b/common/dev_disconnect.h @@ -29,8 +29,6 @@ void dev_blackhole_fd(int fd); /* For debug code to set in daemon. */ void dev_disconnect_init(int fd); -/* Hack for channeld to do DEV_DISCONNECT_SUPPRESS_COMMIT. */ -extern bool dev_suppress_commit; #endif /* DEVELOPER */ #endif /* LIGHTNING_COMMON_DEV_DISCONNECT_H */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 49940eff4fc8..9092970236d7 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -653,68 +653,72 @@ void peer_start_channeld(struct channel *channel, tmpctx, channel->peer->ld->wallet, channel->dbid); initmsg = towire_channeld_init(tmpctx, - chainparams, - ld->our_features, - &channel->cid, - &channel->funding, - channel->funding_sats, - channel->minimum_depth, - get_block_height(ld->topology), - channel->blockheight_states, - channel->lease_expiry, - &channel->our_config, - &channel->channel_info.their_config, - channel->fee_states, - feerate_min(ld, NULL), - feerate_max(ld, NULL), - try_get_feerate(ld->topology, FEERATE_PENALTY), - &channel->last_sig, - pps, - &channel->channel_info.remote_fundingkey, - &channel->channel_info.theirbase, - &channel->channel_info.remote_per_commit, - &channel->channel_info.old_remote_per_commit, - channel->opener, - channel->feerate_base, - channel->feerate_ppm, - channel->our_msat, - &channel->local_basepoints, - &channel->local_funding_pubkey, - &ld->id, - &channel->peer->id, - cfg->commit_time_ms, - cfg->cltv_expiry_delta, - channel->last_was_revoke, - channel->last_sent_commit, - channel->next_index[LOCAL], - channel->next_index[REMOTE], - num_revocations, - channel->next_htlc_id, - htlcs, - channel->scid != NULL, - channel->remote_funding_locked, - &scid, - reconnected, + chainparams, + ld->our_features, + &channel->cid, + &channel->funding, + channel->funding_sats, + channel->minimum_depth, + get_block_height(ld->topology), + channel->blockheight_states, + channel->lease_expiry, + &channel->our_config, + &channel->channel_info.their_config, + channel->fee_states, + feerate_min(ld, NULL), + feerate_max(ld, NULL), + try_get_feerate(ld->topology, FEERATE_PENALTY), + &channel->last_sig, + pps, + &channel->channel_info.remote_fundingkey, + &channel->channel_info.theirbase, + &channel->channel_info.remote_per_commit, + &channel->channel_info.old_remote_per_commit, + channel->opener, + channel->feerate_base, + channel->feerate_ppm, + channel->our_msat, + &channel->local_basepoints, + &channel->local_funding_pubkey, + &ld->id, + &channel->peer->id, + cfg->commit_time_ms, + cfg->cltv_expiry_delta, + channel->last_was_revoke, + channel->last_sent_commit, + channel->next_index[LOCAL], + channel->next_index[REMOTE], + num_revocations, + channel->next_htlc_id, + htlcs, + channel->scid != NULL, + channel->remote_funding_locked, + &scid, + reconnected, /* Anything that indicates we are or have * shut down */ - channel->state == CHANNELD_SHUTTING_DOWN + channel->state == CHANNELD_SHUTTING_DOWN || channel->state == CLOSINGD_SIGEXCHANGE || channel_closed(channel), - channel->shutdown_scriptpubkey[REMOTE] != NULL, - channel->shutdown_scriptpubkey[LOCAL], - channel->channel_flags, - fwd_msg, - reached_announce_depth, - &last_remote_per_commit_secret, - channel->peer->their_features, - channel->remote_upfront_shutdown_script, - remote_ann_node_sig, - remote_ann_bitcoin_sig, - channel->type, - IFDEV(ld->dev_fast_gossip, false), - IFDEV(dev_fail_process_onionpacket, false), - pbases, - reestablish_only); + channel->shutdown_scriptpubkey[REMOTE] != NULL, + channel->shutdown_scriptpubkey[LOCAL], + channel->channel_flags, + fwd_msg, + reached_announce_depth, + &last_remote_per_commit_secret, + channel->peer->their_features, + channel->remote_upfront_shutdown_script, + remote_ann_node_sig, + remote_ann_bitcoin_sig, + channel->type, + IFDEV(ld->dev_fast_gossip, false), + IFDEV(dev_fail_process_onionpacket, false), + IFDEV(ld->dev_disable_commit == -1 + ? NULL + : (u32 *)&ld->dev_disable_commit, + NULL), + 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/lightningd.c b/lightningd/lightningd.c index 4adb6632aa7f..b0ce617a3ec7 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -133,6 +133,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_max_funding_unconfirmed = 2016; ld->dev_ignore_modern_onion = false; ld->dev_ignore_obsolete_onion = false; + ld->dev_disable_commit = -1; #endif /*~ These are CCAN lists: an embedded double-linked list. It's not diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 8fabc615076a..884b2f921744 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -250,6 +250,9 @@ struct lightningd { /* Special switches to test onion compatibility */ bool dev_ignore_modern_onion, dev_ignore_obsolete_onion; + + /* Tell channeld to disable commits after this many. */ + int dev_disable_commit; #endif /* DEVELOPER */ /* tor support */ diff --git a/lightningd/options.c b/lightningd/options.c index 146fbee7e248..a1f263a0e139 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -697,6 +697,10 @@ static void dev_register_opts(struct lightningd *ld) opt_register_noarg("--dev-no-obsolete-onion", opt_set_bool, &ld->dev_ignore_obsolete_onion, "Ignore obsolete onion messages"); + opt_register_arg("--dev-disable-commit-after", + opt_set_intval, opt_show_intval, + &ld->dev_disable_commit, + "Disable commit timer after this many commits"); } #endif /* DEVELOPER */ diff --git a/tests/test_closing.py b/tests/test_closing.py index dc4367086fb5..7a4d2a339560 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -530,7 +530,7 @@ def test_closing_negotiation_step_700sat(node_factory, bitcoind, chainparams): closing_negotiation_step(node_factory, bitcoind, chainparams, opts) -@pytest.mark.developer("needs DEVELOPER=1") +@pytest.mark.developer("needs dev-disable-commit-after") def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): """Test penalty transaction with an incoming HTLC""" @@ -538,12 +538,12 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') # We suppress each one after first commit; HTLC gets added not fulfilled. # Feerates identical so we don't get gratuitous commit to update them - l1, l2 = node_factory.line_graph(2, opts=[{'disconnect': ['=WIRE_COMMITMENT_SIGNED-nocommit'], + l1, l2 = node_factory.line_graph(2, opts=[{'dev-disable-commit-after': 1, 'may_fail': True, 'feerates': (7500, 7500, 7500, 7500), 'allow_broken_log': True, 'plugin': coin_mvt_plugin}, - {'disconnect': ['=WIRE_COMMITMENT_SIGNED-nocommit'], + {'dev-disable-commit-after': 1, 'plugin': coin_mvt_plugin}]) channel_id = first_channel_id(l1, l2) @@ -555,8 +555,8 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): assert len(l2.getactivechannels()) == 2 # They should both have commitments blocked now. - l1.daemon.wait_for_log('=WIRE_COMMITMENT_SIGNED-nocommit') - l2.daemon.wait_for_log('=WIRE_COMMITMENT_SIGNED-nocommit') + l1.daemon.wait_for_log('dev-disable-commit-after: disabling') + l2.daemon.wait_for_log('dev-disable-commit-after: disabling') # Make sure l1 got l2's commitment to the HTLC, and sent to master. l1.daemon.wait_for_log('got commitsig') @@ -652,7 +652,7 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): check_utxos_channel(l2, [channel_id], expected_2, tags) -@pytest.mark.developer("needs DEVELOPER=1") +@pytest.mark.developer("needs dev-disable-commit-after") def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): """Test penalty transaction with an outgoing HTLC""" @@ -661,20 +661,20 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): # First we need to get funds to l2, so suppress after second. # Feerates identical so we don't get gratuitous commit to update them l1, l2 = node_factory.line_graph(2, - opts=[{'disconnect': ['=WIRE_COMMITMENT_SIGNED*3-nocommit'], + opts=[{'dev-disable-commit-after': 3, 'may_fail': True, 'feerates': (7500, 7500, 7500, 7500), 'allow_broken_log': True, 'plugin': coin_mvt_plugin}, - {'disconnect': ['=WIRE_COMMITMENT_SIGNED*3-nocommit'], + {'dev-disable-commit-after': 3, 'plugin': coin_mvt_plugin}]) channel_id = first_channel_id(l1, l2) # Move some across to l2. l1.pay(l2, 200000000) - assert not l1.daemon.is_in_log('=WIRE_COMMITMENT_SIGNED') - assert not l2.daemon.is_in_log('=WIRE_COMMITMENT_SIGNED') + assert not l1.daemon.is_in_log('dev-disable-commit-after: disabling') + assert not l2.daemon.is_in_log('dev-disable-commit-after: disabling') # Now, this will get stuck due to l1 commit being disabled.. t = executor.submit(l2.pay, l1, 100000000) @@ -684,8 +684,8 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): l1.daemon.wait_for_log('peer_in WIRE_COMMITMENT_SIGNED') # They should both have commitments blocked now. - l1.daemon.wait_for_log('dev_disconnect: =WIRE_COMMITMENT_SIGNED') - l2.daemon.wait_for_log('dev_disconnect: =WIRE_COMMITMENT_SIGNED') + l1.daemon.wait_for_log('dev-disable-commit-after: disabling') + l2.daemon.wait_for_log('dev-disable-commit-after: disabling') # Make sure both sides got revoke_and_ack for that commitment. l1.daemon.wait_for_log('peer_in WIRE_REVOKE_AND_ACK') @@ -1567,10 +1567,10 @@ def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams): # l1 is the thief, which causes our honest upstanding lightningd # code to break, so l1 can fail. # Initially, disconnect before the HTLC can be resolved. - l1 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], + l1 = node_factory.get_node(options={'dev-disable-commit-after': 1}, may_fail=True, allow_broken_log=True) - l2 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], - options={'watchtime-blocks': to_self_delay, + l2 = node_factory.get_node(options={'dev-disable-commit-after': 1, + 'watchtime-blocks': to_self_delay, 'plugin': coin_mvt_plugin}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -1585,8 +1585,8 @@ def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams): assert len(l2.getactivechannels()) == 2 # Wait for the disconnection. - l1.daemon.wait_for_log('=WIRE_COMMITMENT_SIGNED-nocommit') - l2.daemon.wait_for_log('=WIRE_COMMITMENT_SIGNED-nocommit') + l1.daemon.wait_for_log('dev-disable-commit-after: disabling') + l2.daemon.wait_for_log('dev-disable-commit-after: disabling') # Make sure l1 gets the new HTLC. l1.daemon.wait_for_log('got commitsig') @@ -1692,10 +1692,10 @@ def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams): # l1 is the thief, which causes our honest upstanding lightningd # code to break, so l1 can fail. # Initially, disconnect before the HTLC can be resolved. - l1 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], + l1 = node_factory.get_node(options={'dev-disable-commit-after': 1}, may_fail=True, allow_broken_log=True) - l2 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'], - options={'watchtime-blocks': to_self_delay, + l2 = node_factory.get_node(options={'dev-disable-commit-after': 1, + 'watchtime-blocks': to_self_delay, 'plugin': coin_mvt_plugin}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -1710,8 +1710,8 @@ def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams): assert len(l2.getactivechannels()) == 2 # Wait for the disconnection. - l1.daemon.wait_for_log('=WIRE_COMMITMENT_SIGNED-nocommit') - l2.daemon.wait_for_log('=WIRE_COMMITMENT_SIGNED-nocommit') + l1.daemon.wait_for_log('dev-disable-commit-after: disabling') + l2.daemon.wait_for_log('dev-disable-commit-after: disabling') # Make sure l1 gets the new HTLC. l1.daemon.wait_for_log('got commitsig') diff --git a/tests/test_connection.py b/tests/test_connection.py index 7faf5865a281..a7ad414a2910 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -643,8 +643,8 @@ def test_reconnect_normal(node_factory): @pytest.mark.openchannel('v2') def test_reconnect_sender_add1(node_factory): # Fail after add is OK, will cause payment failure though. - disconnects = ['-WIRE_UPDATE_ADD_HTLC-nocommit', - '+WIRE_UPDATE_ADD_HTLC-nocommit'] + disconnects = ['-WIRE_UPDATE_ADD_HTLC', + '+WIRE_UPDATE_ADD_HTLC'] # Feerates identical so we don't get gratuitous commit to update them l1 = node_factory.get_node(disconnect=disconnects, @@ -2085,14 +2085,14 @@ def test_channel_persistence(node_factory, bitcoind, executor): # mysteriously die while committing the first HTLC so we can # check that HTLCs reloaded from the DB work. # Feerates identical so we don't get gratuitous commit to update them - disconnect = ['=WIRE_COMMITMENT_SIGNED-nocommit'] - + disable_commit_after = 1 if EXPERIMENTAL_DUAL_FUND: - disconnect = ['=WIRE_COMMITMENT_SIGNED'] + disconnect + disable_commit_after = 2 l1 = node_factory.get_node(may_reconnect=True, feerates=(7500, 7500, 7500, 7500)) - l2 = node_factory.get_node(disconnect=disconnect, may_reconnect=True) + l2 = node_factory.get_node(options={'dev-disable-commit-after': disable_commit_after}, + may_reconnect=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # Neither node should have a channel open, they are just connected @@ -2119,7 +2119,7 @@ def test_channel_persistence(node_factory, bitcoind, executor): l2.daemon.kill() # Clear the disconnect and timer stop so we can proceed normally - del l2.daemon.opts['dev-disconnect'] + del l2.daemon.opts['dev-disable-commit-after'] # Wait for l1 to notice wait_for(lambda: 'connected' not in only_one(l1.rpc.listpeers()['peers'][0]['channels'])) @@ -2845,10 +2845,9 @@ def test_dataloss_protection(node_factory, bitcoind): @pytest.mark.developer("needs dev_disconnect") def test_restart_multi_htlc_rexmit(node_factory, bitcoind, executor): # l1 disables commit timer once we send first htlc, dies on commit - disconnects = ['=WIRE_UPDATE_ADD_HTLC-nocommit', - '-WIRE_COMMITMENT_SIGNED'] - l1, l2 = node_factory.line_graph(2, opts=[{'disconnect': disconnects, - 'may_reconnect': True}, + l1, l2 = node_factory.line_graph(2, opts=[{'disconnect': ['-WIRE_COMMITMENT_SIGNED'], + 'may_reconnect': True, + 'dev-disable-commit-after': 0}, {'may_reconnect': True}]) executor.submit(l1.pay, l2, 20000) @@ -3397,9 +3396,9 @@ def test_htlc_retransmit_order(node_factory, executor): l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, 'feerates': (7500, 7500, 7500, 7500), - 'disconnect': ['=WIRE_UPDATE_ADD_HTLC-nocommit', - '=WIRE_UPDATE_ADD_HTLC*' + str(NUM_HTLCS - 1), - '-WIRE_COMMITMENT_SIGNED']}, + 'disconnect': ['=WIRE_UPDATE_ADD_HTLC*' + str(NUM_HTLCS), + '-WIRE_COMMITMENT_SIGNED'], + 'dev-disable-commit-after': 0}, {'may_reconnect': True}]) invoices = [l2.rpc.invoice(1000, str(x), str(x)) for x in range(NUM_HTLCS)] @@ -3412,7 +3411,7 @@ def test_htlc_retransmit_order(node_factory, executor): for inv in invoices: executor.submit(l1.rpc.sendpay, [routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) - l1.daemon.wait_for_logs(['dev_disconnect'] * 2) + l1.daemon.wait_for_log('dev_disconnect') l1.rpc.call('dev-reenable-commit', [l2.info['id']]) l1.daemon.wait_for_log('dev_disconnect') @@ -3619,8 +3618,7 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): l1_disconnects = ['-WIRE_COMMITMENT_SIGNED', '-WIRE_REVOKE_AND_ACK'] l2_disconnects = ['-WIRE_REVOKE_AND_ACK', - '-WIRE_COMMITMENT_SIGNED', - '=WIRE_UPDATE_FAIL_HTLC-nocommit'] + '-WIRE_COMMITMENT_SIGNED'] l1, l2 = node_factory.line_graph(2, opts=[{'may_reconnect': True, 'dev-no-reconnect': None, @@ -3630,13 +3628,14 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): 'feerates': (7500, 7500, 7500, 7500)}, {'may_reconnect': True, 'dev-no-reconnect': None, - 'disconnect': l2_disconnects}]) + 'disconnect': l2_disconnects, + 'dev-disable-commit-after': 1}]) # This HTLC will fail l1.rpc.sendpay([{'msatoshi': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}], '00' * 32, payment_secret='00' * 32) # Each one should cause one disconnection, no upgrade. - for d in l1_disconnects + l2_disconnects[:-1]: + for d in l1_disconnects + l2_disconnects: l1.daemon.wait_for_log('Peer connection lost') l2.daemon.wait_for_log('Peer connection lost') assert not l1.daemon.is_in_log('option_static_remotekey enabled') diff --git a/tests/test_pay.py b/tests/test_pay.py index 96922a82f41a..5516099d90ac 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -450,7 +450,7 @@ def test_payment_failed_persistence(node_factory, executor): @pytest.mark.developer("needs DEVELOPER=1") def test_payment_duplicate_uncommitted(node_factory, executor): # We want to test two payments at the same time, before we send commit - l1 = node_factory.get_node(disconnect=['=WIRE_UPDATE_ADD_HTLC-nocommit']) + l1 = node_factory.get_node(options={'dev-disable-commit-after': 0}) l2 = node_factory.get_node() l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -463,7 +463,7 @@ def test_payment_duplicate_uncommitted(node_factory, executor): fut = executor.submit(l1.rpc.pay, inv1['bolt11']) # Make sure that's started... - l1.daemon.wait_for_log('dev_disconnect: =WIRE_UPDATE_ADD_HTLC-nocommit') + l1.daemon.wait_for_log('peer_out WIRE_UPDATE_ADD_HTLC') # We should see it in listsendpays payments = l1.rpc.listsendpays()['payments'] @@ -2809,12 +2809,12 @@ def truncate_encode(i: int): assert(e.error['data']['raw_message'] == "400f00000000000003e80000006c") -@pytest.mark.developer("needs dev-disconnect, dev-no-htlc-timeout") +@pytest.mark.developer("needs dev-disable-commit-after, dev-no-htlc-timeout") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_partial_payment(node_factory, bitcoind, executor): # We want to test two payments at the same time, before we send commit - l1, l2, l3, l4 = node_factory.get_nodes(4, [{}] + [{'disconnect': ['=WIRE_UPDATE_ADD_HTLC-nocommit'], 'dev-no-htlc-timeout': None}] * 2 + [{'plugin': os.path.join(os.getcwd(), 'tests/plugins/print_htlc_onion.py')}]) + l1, l2, l3, l4 = node_factory.get_nodes(4, [{}] + [{'dev-disable-commit-after': 0, 'dev-no-htlc-timeout': None}] * 2 + [{'plugin': os.path.join(os.getcwd(), 'tests/plugins/print_htlc_onion.py')}]) # Two routes to l4: one via l2, and one via l3. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -2925,13 +2925,15 @@ def test_partial_payment(node_factory, bitcoind, executor): groupid=1, ) - # Make sure they've done the suppress-commitment thing before we unsuppress - l2.daemon.wait_for_log(r'dev_disconnect') - l3.daemon.wait_for_log(r'dev_disconnect') + # Make sure they've got the HTLCs before we unsuppress + l2.daemon.wait_for_logs('peer_in WIRE_UPDATE_ADD_HTLC') + l3.daemon.wait_for_log('peer_in WIRE_UPDATE_ADD_HTLC') # Now continue, payments will succeed due to MPP. l2.rpc.dev_reenable_commit(l4.info['id']) l3.rpc.dev_reenable_commit(l4.info['id']) + l2.rpc.dev_reenable_commit(l1.info['id']) + l3.rpc.dev_reenable_commit(l1.info['id']) res = l1.rpc.waitsendpay(payment_hash=inv['payment_hash'], partid=1) assert res['partid'] == 1 From 8e37eb0028dcb7995b3f35b5a2a53decafc9caee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 13:56:43 +1030 Subject: [PATCH 0181/1530] gossipd: local_direction helper to generalize is_local_channel. Increasingly we want to know is it local, and get the direction: it's more efficient to do both at once. Signed-off-by: Rusty Russell --- gossipd/routing.c | 13 +++---------- gossipd/routing.h | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index d246b2ee53fc..560e1423932d 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -795,13 +795,6 @@ static void destroy_pending_cannouncement(struct pending_cannouncement *pending, pending_cannouncement_map_del(&rstate->pending_cannouncements, pending); } -static bool is_local_channel(const struct routing_state *rstate, - const struct chan *chan) -{ - return node_id_eq(&chan->nodes[0]->id, &rstate->local_id) - || node_id_eq(&chan->nodes[1]->id, &rstate->local_id); -} - static void add_channel_announce_to_broadcast(struct routing_state *rstate, struct chan *chan, const u8 *channel_announce, @@ -809,7 +802,7 @@ static void add_channel_announce_to_broadcast(struct routing_state *rstate, u32 index) { u8 *addendum = towire_gossip_store_channel_amount(tmpctx, chan->sat); - bool is_local = is_local_channel(rstate, chan); + bool is_local = local_direction(rstate, chan, NULL); chan->bcast.timestamp = timestamp; /* 0, unless we're loading from store */ @@ -1381,7 +1374,7 @@ bool routing_add_channel_update(struct routing_state *rstate, } else if (!is_chan_public(chan)) { /* For private channels, we get updates without an announce: don't * broadcast them! But save local ones to store anyway. */ - assert(is_local_channel(rstate, chan)); + assert(local_direction(rstate, chan, NULL)); /* Don't save if we're loading from store */ if (!index) { hc->bcast.index @@ -1399,7 +1392,7 @@ bool routing_add_channel_update(struct routing_state *rstate, hc->bcast.index = gossip_store_add(rstate->gs, update, hc->bcast.timestamp, - is_local_channel(rstate, chan), + local_direction(rstate, chan, NULL), NULL); if (hc->bcast.timestamp > rstate->last_timestamp && hc->bcast.timestamp < time_now().ts.tv_sec) diff --git a/gossipd/routing.h b/gossipd/routing.h index a0ec756f09fe..1a8a08d0cde0 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -273,6 +273,21 @@ struct routing_state { #endif }; +/* Which direction are we? False if neither. */ +static inline bool local_direction(struct routing_state *rstate, + const struct chan *chan, + int *direction) +{ + for (int dir = 0; dir <= 1; (dir)++) { + if (node_id_eq(&chan->nodes[dir]->id, &rstate->local_id)) { + if (direction) + *direction = dir; + return true; + } + } + return false; +} + static inline struct chan * get_channel(const struct routing_state *rstate, const struct short_channel_id *scid) From 0aad222c2d9659389ebdb44c3d4dd86dc8507bd0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 13:56:43 +1030 Subject: [PATCH 0182/1530] gossipd: rewrite update generation, remove local_chan. local_chan was mainly around so we could "soft" disable channels (and really disable them once we used the channel_update in an error message). Instead we introduce the idea of a "deferred_update": it's either deferred indefinitely (a peer goes offline, if we need to send it in an error we'll apply it immediatly), or simply delayed to avoid spamming everyone. The resulting rewrite is much clearer, IMHO. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 591 ++++++++++++++++++++++--------- gossipd/gossip_generation.h | 14 +- gossipd/gossipd.c | 128 ++++--- gossipd/gossipd.h | 3 + gossipd/routing.c | 35 -- gossipd/routing.h | 75 +--- gossipd/test/run-onion_message.c | 12 +- 7 files changed, 523 insertions(+), 335 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 7e71e5b81262..6ca0ff3e521e 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -307,48 +307,136 @@ void maybe_send_own_node_announce(struct daemon *daemon, bool startup) update_own_node_announcement(daemon, startup); } -/* Our timer callbacks take a single argument, so we marshall everything - * we need into this structure: */ -struct local_cupdate { - struct daemon *daemon; - struct local_chan *local_chan; +/* Fast accessors for channel_update fields */ +static u8 *channel_flags_access(const u8 *channel_update) +{ + /* BOLT #7: + * 1. type: 258 (`channel_update`) + * 2. data: + * * [`signature`:`signature`] + * * [`chain_hash`:`chain_hash`] + * * [`short_channel_id`:`short_channel_id`] + * * [`u32`:`timestamp`] + * * [`byte`:`message_flags`] + * * [`byte`:`channel_flags`] + */ + /* Note: 2 bytes for `type` field */ + return cast_const(u8 *, &channel_update[2 + 64 + 32 + 8 + 4 + 1]); +} - bool disable; - bool even_if_identical; - bool even_if_too_soon; +static u8 *timestamp_access(const u8 *channel_update) +{ + /* BOLT #7: + * 1. type: 258 (`channel_update`) + * 2. data: + * * [`signature`:`signature`] + * * [`chain_hash`:`chain_hash`] + * * [`short_channel_id`:`short_channel_id`] + * * [`u32`:`timestamp`] + * * [`byte`:`message_flags`] + * * [`byte`:`channel_flags`] + */ + /* Note: 2 bytes for `type` field */ + return cast_const(u8 *, &channel_update[2 + 64 + 32 + 8]); +} - u16 cltv_expiry_delta; - struct amount_msat htlc_minimum, htlc_maximum; - u32 fee_base_msat, fee_proportional_millionths; -}; +static bool is_disabled(const u8 *channel_update) +{ + return *channel_flags_access(channel_update) & ROUTING_FLAGS_DISABLED; +} -/* This generates a `channel_update` message for one of our channels. We do - * this here, rather than in `channeld` because we (may) need to do it - * ourselves anyway if channeld dies, or when we refresh it once a week, - * and so we can avoid creating redundant ones. */ -static void update_local_channel(struct local_cupdate *lc /* frees! */) +static bool is_enabled(const u8 *channel_update) { - struct daemon *daemon = lc->daemon; - secp256k1_ecdsa_signature dummy_sig; - u8 *update, *msg; - u32 timestamp = gossip_time_now(daemon->rstate).ts.tv_sec, next; - u8 message_flags, channel_flags; - struct chan *chan = lc->local_chan->chan; - struct half_chan *hc; - const int direction = lc->local_chan->direction; + return !is_disabled(channel_update); +} - /* Discard existing timer. */ - lc->local_chan->channel_update_timer - = tal_free(lc->local_chan->channel_update_timer); - /* So valgrind doesn't complain */ - memset(&dummy_sig, 0, sizeof(dummy_sig)); +static u32 timestamp_for_update(struct daemon *daemon, + const u32 *prev_timestamp, + bool disable) +{ + u32 timestamp = gossip_time_now(daemon->rstate).ts.tv_sec; /* Create an unsigned channel_update: we backdate enables, so * we can always send a disable in an emergency. */ - if (!lc->disable) + if (!disable) timestamp -= GOSSIP_MIN_INTERVAL(daemon->rstate->dev_fast_gossip); + if (prev_timestamp) { + /* Timestamps can't go backwards! */ + if (timestamp < *prev_timestamp) + timestamp = *prev_timestamp + 1; + + /* If we ever use set-based propagation, ensuring the toggle + * the lower bit in consecutive timestamps makes it more + * robust. */ + if ((timestamp & 1) == (*prev_timestamp & 1)) + timestamp++; + } + + return timestamp; +} + +static u8 *sign_and_timestamp_update(const tal_t *ctx, + struct daemon *daemon, + const struct chan *chan, + int direction, + u8 *unsigned_update TAKES) +{ + u8 *msg, *update; + be32 timestamp; + const u32 *prev_timestamp; + const struct half_chan *hc = &chan->half[direction]; + + if (is_halfchan_defined(hc)) + prev_timestamp = &hc->bcast.timestamp; + else + prev_timestamp = NULL; + + /* Get an appropriate timestamp */ + timestamp = cpu_to_be32(timestamp_for_update(daemon, + prev_timestamp, + is_disabled(unsigned_update))); + memcpy(timestamp_access(unsigned_update), ×tamp, sizeof(timestamp)); + + /* Note that we treat the hsmd as synchronous. This is simple (no + * callback hell)!, but may need to change to async if we ever want + * remote HSMs */ + if (!wire_sync_write(HSM_FD, + towire_hsmd_cupdate_sig_req(tmpctx, unsigned_update))) { + status_failed(STATUS_FAIL_HSM_IO, "Writing cupdate_sig_req: %s", + strerror(errno)); + } + + msg = wire_sync_read(tmpctx, HSM_FD); + if (!msg || !fromwire_hsmd_cupdate_sig_reply(ctx, msg, &update)) { + status_failed(STATUS_FAIL_HSM_IO, + "Reading cupdate_sig_req: %s", + strerror(errno)); + } + + if (taken(unsigned_update)) + tal_free(unsigned_update); + + return update; +} + +static u8 *create_unsigned_update(const tal_t *ctx, + const struct short_channel_id *scid, + int direction, + bool disable, + u16 cltv_expiry_delta, + struct amount_msat htlc_minimum, + struct amount_msat htlc_maximum, + u32 fee_base_msat, + u32 fee_proportional_millionths) +{ + secp256k1_ecdsa_signature dummy_sig; + u8 message_flags, channel_flags; + + /* So valgrind doesn't complain */ + memset(&dummy_sig, 0, sizeof(dummy_sig)); + /* BOLT #7: * * The `channel_flags` bitfield is used to indicate the direction of @@ -362,7 +450,7 @@ static void update_local_channel(struct local_cupdate *lc /* frees! */) * | 1 | `disable` | Disable the channel. | */ channel_flags = direction; - if (lc->disable) + if (disable) channel_flags |= ROUTING_FLAGS_DISABLED; /* BOLT #7: @@ -376,101 +464,43 @@ static void update_local_channel(struct local_cupdate *lc /* frees! */) */ message_flags = 0 | ROUTING_OPT_HTLC_MAX_MSAT; - /* Convenience variable. */ - hc = &chan->half[direction]; - - /* If we ever use set-based propagation, ensuring the toggle - * the lower bit in consecutive timestamps makes it more - * robust. */ - if (is_halfchan_defined(hc) - && (timestamp & 1) == (hc->bcast.timestamp & 1)) - timestamp++; - - /* We create an update with a dummy signature, and hand to hsmd to get - * it signed. */ - update = towire_channel_update_option_channel_htlc_max(tmpctx, &dummy_sig, + /* We create an update with a dummy signature and timestamp. */ + return towire_channel_update_option_channel_htlc_max(ctx, + &dummy_sig, /* sig set later */ &chainparams->genesis_blockhash, - &chan->scid, - timestamp, + scid, + 0, /* timestamp set later */ message_flags, channel_flags, - lc->cltv_expiry_delta, - lc->htlc_minimum, - lc->fee_base_msat, - lc->fee_proportional_millionths, - lc->htlc_maximum); - - if (is_halfchan_defined(hc)) { - /* Suppress duplicates. */ - if (!lc->even_if_identical - && !cupdate_different(daemon->rstate->gs, hc, update)) { - tal_free(lc); - return; - } - - /* Is it too soon to send another update? */ - next = hc->bcast.timestamp - + GOSSIP_MIN_INTERVAL(daemon->rstate->dev_fast_gossip); - - if (timestamp < next && !lc->even_if_too_soon) { - status_debug("channel_update %s/%u: delaying %u secs", - type_to_string(tmpctx, - struct short_channel_id, - &chan->scid), - direction, - next - timestamp); - lc->local_chan->channel_update_timer - = new_reltimer(&daemon->timers, lc, - time_from_sec(next - timestamp), - update_local_channel, - lc); - /* If local chan vanishes, so does update, and timer. */ - notleak(tal_steal(lc->local_chan, lc)); - return; - } - } - - /* Note that we treat the hsmd as synchronous. This is simple (no - * callback hell)!, but may need to change to async if we ever want - * remote HSMs */ - if (!wire_sync_write(HSM_FD, - towire_hsmd_cupdate_sig_req(tmpctx, update))) { - status_failed(STATUS_FAIL_HSM_IO, "Writing cupdate_sig_req: %s", - strerror(errno)); - } + cltv_expiry_delta, + htlc_minimum, + fee_base_msat, + fee_proportional_millionths, + htlc_maximum); +} - msg = wire_sync_read(tmpctx, HSM_FD); - if (!msg || !fromwire_hsmd_cupdate_sig_reply(tmpctx, msg, &update)) { - status_failed(STATUS_FAIL_HSM_IO, - "Reading cupdate_sig_req: %s", - strerror(errno)); - } +static void apply_update(struct daemon *daemon, + const struct chan *chan, + int direction, + u8 *update TAKES) +{ + u8 *msg; + struct peer *peer = find_peer(daemon, &chan->nodes[!direction]->id); - /* BOLT #7: - * - * The origin node: - *... - * - MAY create a `channel_update` to communicate the channel parameters to the - * channel peer, even though the channel has not yet been announced (i.e. the - * `announce_channel` bit was not set). - */ if (!is_chan_public(chan)) { + /* Save and restore taken state, for handle_channel_update */ + bool update_taken = taken(update); + /* handle_channel_update will not put private updates in the * broadcast list, but we send it direct to the peer (if we * have one connected) now */ - struct peer *peer = find_peer(daemon, - &chan->nodes[!direction]->id); if (peer) queue_peer_msg(peer, update); + + if (update_taken) + take(update); } - /* We feed it into routing.c like any other channel_update; it may - * discard it (eg. non-public channel), but it should not complain - * about it being invalid! __func__ is a magic C constant which - * expands to this function name. */ - msg = handle_channel_update(daemon->rstate, update, - find_peer(daemon, - &chan->nodes[!direction]->id), - NULL, true); + msg = handle_channel_update(daemon->rstate, update, peer, NULL, true); if (msg) status_failed(STATUS_FAIL_INTERNAL_ERROR, "%s: rejected local channel update %s: %s", @@ -481,72 +511,181 @@ static void update_local_channel(struct local_cupdate *lc /* frees! */) * tmpctx, so it's actually OK. */ tal_hex(tmpctx, update), tal_hex(tmpctx, msg)); - - tal_free(lc); } -/* This is a refresh of a local channel: sends an update if one is needed. */ -void refresh_local_channel(struct daemon *daemon, - struct local_chan *local_chan, - bool even_if_identical) +static void sign_timestamp_and_apply_update(struct daemon *daemon, + const struct chan *chan, + int direction, + u8 *update TAKES) { - const struct half_chan *hc; - struct local_cupdate *lc; - u8 *prev; - secp256k1_ecdsa_signature signature; - struct bitcoin_blkid chain_hash; - struct short_channel_id short_channel_id; - u32 timestamp; - u8 message_flags, channel_flags; + update = sign_and_timestamp_update(NULL, daemon, chan, direction, + update); + apply_update(daemon, chan, direction, take(update)); +} - hc = &local_chan->chan->half[local_chan->direction]; +/* We don't want to thrash the gossip network, so we often defer sending an + * update. We track them here. */ +struct deferred_update { + struct daemon *daemon; + /* Off daemon->deferred_updates */ + struct list_node list; + /* Channel it's for (and owner) */ + const struct chan *chan; + int direction; + /* Timer which will fire when it's time to apply. */ + struct oneshot *channel_update_timer; + /* The actual `update_channel` to apply */ + u8 *update; +}; - /* Don't generate a channel_update for an uninitialized channel. */ - if (!is_halfchan_defined(hc)) - return; +static struct deferred_update *find_deferred_update(struct daemon *daemon, + const struct chan *chan) +{ + struct deferred_update *du; - /* If there's an update pending already, force it to apply now. */ - if (local_chan->channel_update_timer) { - lc = reltimer_arg(local_chan->channel_update_timer); - lc->even_if_too_soon = true; - update_local_channel(lc); - /* Free timer */ - local_chan->channel_update_timer - = tal_free(local_chan->channel_update_timer); + list_for_each(&daemon->deferred_updates, du, list) { + if (du->chan == chan) + return du; } + return NULL; +} + +static void destroy_deferred_update(struct deferred_update *du) +{ + list_del(&du->list); +} + +static void apply_deferred_update(struct deferred_update *du) +{ + apply_update(du->daemon, du->chan, du->direction, take(du->update)); + tal_free(du); +} + +static void defer_update(struct daemon *daemon, + u32 delay, + const struct chan *chan, + int direction, + u8 *unsigned_update TAKES) +{ + struct deferred_update *du; + + /* Override any existing one */ + tal_free(find_deferred_update(daemon, chan)); + + /* If chan is gone, so are we. */ + du = tal(chan, struct deferred_update); + du->daemon = daemon; + du->chan = chan; + du->direction = direction; + du->update = sign_and_timestamp_update(du, daemon, chan, direction, + unsigned_update); + if (delay != 0xFFFFFFFF) + du->channel_update_timer = new_reltimer(&daemon->timers, du, + time_from_sec(delay), + apply_deferred_update, + du); + else + du->channel_update_timer = NULL; + list_add_tail(&daemon->deferred_updates, &du->list); + tal_add_destructor(du, destroy_deferred_update); +} + +/* If there is a pending update for this local channel, apply immediately. */ +bool local_channel_update_latest(struct daemon *daemon, struct chan *chan) +{ + struct deferred_update *du; + + du = find_deferred_update(daemon, chan); + if (!du) + return false; + + /* Frees itself */ + apply_deferred_update(du); + return true; +} + +/* Get previous update. */ +static u8 *prev_update(const tal_t *ctx, + struct daemon *daemon, const struct chan *chan, int direction) +{ + u8 *prev; - lc = tal(NULL, struct local_cupdate); - lc->daemon = daemon; - lc->local_chan = local_chan; - lc->even_if_identical = even_if_identical; - lc->even_if_too_soon = false; + if (!is_halfchan_defined(&chan->half[direction])) + return NULL; prev = cast_const(u8 *, gossip_store_get(tmpctx, daemon->rstate->gs, - local_chan->chan->half[local_chan->direction] - .bcast.index)); + chan->half[direction].bcast.index)); /* If it's a private update, unwrap */ - fromwire_gossip_store_private_update(tmpctx, prev, &prev); + if (!fromwire_gossip_store_private_update(ctx, prev, &prev)) + tal_steal(ctx, prev); + return prev; +} + +/* This is a refresh of a local channel (after 13 days). */ +void refresh_local_channel(struct daemon *daemon, + struct chan *chan, int direction) +{ + u16 cltv_expiry_delta; + struct amount_msat htlc_minimum, htlc_maximum; + u32 fee_base_msat, fee_proportional_millionths, timestamp; + u8 *prev, *update; + u8 message_flags, channel_flags; + secp256k1_ecdsa_signature signature; + struct bitcoin_blkid chain_hash; + struct short_channel_id short_channel_id; + + /* If there's a pending update, apply it and we're done. */ + if (local_channel_update_latest(daemon, chan)) + return; + + prev = prev_update(tmpctx, daemon, chan, direction); + if (!prev) + return; if (!fromwire_channel_update_option_channel_htlc_max(prev, &signature, &chain_hash, &short_channel_id, ×tamp, &message_flags, &channel_flags, - &lc->cltv_expiry_delta, - &lc->htlc_minimum, - &lc->fee_base_msat, - &lc->fee_proportional_millionths, - &lc->htlc_maximum)) { + &cltv_expiry_delta, + &htlc_minimum, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_maximum)) { status_broken("Could not decode local channel_update %s!", tal_hex(tmpctx, prev)); - tal_free(lc); return; } - lc->disable = (channel_flags & ROUTING_FLAGS_DISABLED) - || local_chan->local_disabled; - update_local_channel(lc); + /* BOLT #7: + * + * The `channel_flags` bitfield is used to indicate the direction of + * the channel: it identifies the node that this update originated + * from and signals various options concerning the channel. The + * following table specifies the meaning of its individual bits: + * + * | Bit Position | Name | Meaning | + * | ------------- | ----------- | -------------------------------- | + * | 0 | `direction` | Direction this update refers to. | + * | 1 | `disable` | Disable the channel. | + */ + if (direction != (channel_flags & ROUTING_FLAGS_DIRECTION)) { + status_broken("Wrong channel direction %s!", + tal_hex(tmpctx, prev)); + return; + } + + /* Don't refresh disabled channels. */ + if (channel_flags & ROUTING_FLAGS_DISABLED) + return; + + update = create_unsigned_update(NULL, &short_channel_id, direction, + false, cltv_expiry_delta, + htlc_minimum, htlc_maximum, + fee_base_msat, + fee_proportional_millionths); + sign_timestamp_and_apply_update(daemon, chan, direction, take(update)); } /* channeld asks us to update the local channel. */ @@ -555,43 +694,141 @@ bool handle_local_channel_update(struct daemon *daemon, const u8 *msg) { struct short_channel_id scid; - struct local_cupdate *lc = tal(tmpctx, struct local_cupdate); - - lc->daemon = daemon; - lc->even_if_identical = false; - lc->even_if_too_soon = false; + bool disable; + u16 cltv_expiry_delta; + struct amount_msat htlc_minimum, htlc_maximum; + u32 fee_base_msat, fee_proportional_millionths; + struct chan *chan; + int direction; + u8 *unsigned_update; + const struct half_chan *hc; /* FIXME: We should get scid from lightningd when setting up the * connection, so no per-peer daemon can mess with channels other than * its own! */ if (!fromwire_gossipd_local_channel_update(msg, &scid, - &lc->disable, - &lc->cltv_expiry_delta, - &lc->htlc_minimum, - &lc->fee_base_msat, - &lc->fee_proportional_millionths, - &lc->htlc_maximum)) { + &disable, + &cltv_expiry_delta, + &htlc_minimum, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_maximum)) { status_peer_broken(src, "bad local_channel_update %s", tal_hex(tmpctx, msg)); return false; } - lc->local_chan = local_chan_map_get(&daemon->rstate->local_chan_map, - &scid); + chan = get_channel(daemon->rstate, &scid); /* Can theoretically happen if channel just closed. */ - if (!lc->local_chan) { + if (!chan) { status_peer_debug(src, "local_channel_update for unknown %s", type_to_string(tmpctx, struct short_channel_id, &scid)); return true; } - /* Remove soft local_disabled flag, if they're marking it enabled. */ - if (!lc->disable) - local_enable_chan(daemon->rstate, lc->local_chan->chan); + if (!local_direction(daemon->rstate, chan, &direction)) { + status_peer_broken(src, "bad local_channel_update chan %s", + type_to_string(tmpctx, + struct short_channel_id, + &scid)); + return false; + } + + unsigned_update = create_unsigned_update(tmpctx, &scid, direction, + disable, cltv_expiry_delta, + htlc_minimum, htlc_maximum, + fee_base_msat, + fee_proportional_millionths); - /* Apply the update they told us */ - update_local_channel(tal_steal(NULL, lc)); + hc = &chan->half[direction]; + + /* Ignore duplicates. */ + if (is_halfchan_defined(hc) + && !cupdate_different(daemon->rstate->gs, hc, unsigned_update)) + return true; + + /* Too early? Defer (don't worry if it's unannounced). */ + if (hc && is_chan_public(chan)) { + u32 now = time_now().ts.tv_sec; + u32 next_time = hc->bcast.timestamp + + GOSSIP_MIN_INTERVAL(daemon->rstate->dev_fast_gossip); + if (now < next_time) { + defer_update(daemon, next_time - now, + chan, direction, take(unsigned_update)); + return true; + } + } + + sign_timestamp_and_apply_update(daemon, chan, direction, + take(unsigned_update)); return true; } + +/* Take update, set/unset disabled flag (and update timestamp). + */ +static void set_disable_flag(u8 *channel_update, bool disable) +{ + u8 *channel_flags = channel_flags_access(channel_update); + + if (disable) + *channel_flags |= ROUTING_FLAGS_DISABLED; + else + *channel_flags &= ~ROUTING_FLAGS_DISABLED; +} + +/* We don't immediately disable, to avoid flapping. */ +void local_disable_chan(struct daemon *daemon, const struct chan *chan, int direction) +{ + struct deferred_update *du; + u8 *update = prev_update(tmpctx, daemon, chan, direction); + if (!update) + return; + + du = find_deferred_update(daemon, chan); + + /* Will a deferred update disable it already? OK, nothing to do. */ + if (du && is_disabled(du->update)) + return; + + /* OK, we definitely don't want deferred update to re-enable! */ + tal_free(du); + + /* Is it already disabled? */ + if (is_disabled(update)) + return; + + /* This is deferred indefinitely (flushed if needed though) */ + set_disable_flag(update, true); + defer_update(daemon, 0xFFFFFFFF, chan, direction, take(update)); +} + +void local_enable_chan(struct daemon *daemon, const struct chan *chan, int direction) +{ + struct deferred_update *du; + u8 *update = prev_update(tmpctx, daemon, chan, direction); + + + if (!update) + return; + + du = find_deferred_update(daemon, chan); + + /* Will a deferred update enable it? If so, apply immediately. */ + if (du && is_enabled(du->update)) { + apply_deferred_update(du); + return; + } + + /* OK, we definitely don't want deferred update to disable! */ + tal_free(du); + + /* Is it already enabled? */ + if (is_enabled(update)) + return; + + /* Apply this enabling update immediately. */ + set_disable_flag(update, false); + sign_timestamp_and_apply_update(daemon, chan, direction, take(update)); +} diff --git a/gossipd/gossip_generation.h b/gossipd/gossip_generation.h index d58f5151e56b..2e7b396f547f 100644 --- a/gossipd/gossip_generation.h +++ b/gossipd/gossip_generation.h @@ -35,10 +35,18 @@ bool nannounce_different(struct gossip_store *gs, /* Should we announce our own node? Called at strategic places. */ void maybe_send_own_node_announce(struct daemon *daemon, bool startup); -/* This is a refresh of a local channel: sends an update if one is needed. */ +/* Flush any pending changes to this channel. */ +bool local_channel_update_latest(struct daemon *daemon, struct chan *chan); + +/* Disable this local channel (lazily) */ +void local_disable_chan(struct daemon *daemon, const struct chan *chan, int direction); + +/* Re-enable this local channel */ +void local_enable_chan(struct daemon *daemon, const struct chan *chan, int direction); + +/* This is a refresh of a local channel which is > 13 days old. */ void refresh_local_channel(struct daemon *daemon, - struct local_chan *local_chan, - bool even_if_identical); + struct chan *chan, int direction); /* channeld asks us to update the local channel. */ bool handle_local_channel_update(struct daemon *daemon, diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 3fc121b83480..098fe652f345 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -44,15 +44,32 @@ * whole-channel flag which indicates it's not available; we use this when a * peer disconnects, and generate a `channel_update` to tell the world lazily * when someone asks. */ -static void peer_disable_channels(struct daemon *daemon, struct node *node) +static void peer_disable_channels(struct daemon *daemon, const struct node *node) { /* If this peer had a channel with us, mark it disabled. */ struct chan_map_iter i; - struct chan *c; + const struct chan *c; + + for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { + int direction; + if (!local_direction(daemon->rstate, c, &direction)) + continue; + local_disable_chan(daemon, c, direction); + } +} + +/*~ This cancels the soft-disables when the peer reconnects. */ +static void peer_enable_channels(struct daemon *daemon, const struct node *node) +{ + /* If this peer had a channel with us, mark it disabled. */ + struct chan_map_iter i; + const struct chan *c; for (c = first_chan(node, &i); c; c = next_chan(node, &i)) { - if (node_id_eq(&other_node(node, c)->id, &daemon->id)) - local_disable_chan(daemon->rstate, c); + int direction; + if (!local_direction(daemon->rstate, c, &direction)) + continue; + local_enable_chan(daemon, c, direction); } } @@ -267,10 +284,10 @@ static u8 *handle_channel_update_msg(struct peer *peer, const u8 *msg) static bool handle_get_local_channel_update(struct peer *peer, const u8 *msg) { struct short_channel_id scid; - struct local_chan *local_chan; struct chan *chan; const u8 *update; struct routing_state *rstate = peer->daemon->rstate; + int direction; if (!fromwire_gossipd_get_update(msg, &scid)) { status_broken("peer %s sent bad gossip_get_update %s", @@ -280,8 +297,8 @@ static bool handle_get_local_channel_update(struct peer *peer, const u8 *msg) } /* It's possible that the channel has just closed (though v. unlikely) */ - local_chan = local_chan_map_get(&rstate->local_chan_map, &scid); - if (!local_chan) { + chan = get_channel(rstate, &scid); + if (!chan) { status_unusual("peer %s scid %s: unknown channel", type_to_string(tmpctx, struct node_id, &peer->id), type_to_string(tmpctx, struct short_channel_id, @@ -290,18 +307,24 @@ static bool handle_get_local_channel_update(struct peer *peer, const u8 *msg) goto out; } - chan = local_chan->chan; - /* Since we're going to send it out, make sure it's up-to-date. */ - refresh_local_channel(peer->daemon, local_chan, false); + local_channel_update_latest(peer->daemon, chan); + + if (!local_direction(rstate, chan, &direction)) { + status_peer_broken(&peer->id, "Chan %s is not local?", + type_to_string(tmpctx, struct short_channel_id, + &scid)); + update = NULL; + goto out; + } /* It's possible this is zero, if we've never sent a channel_update * for that channel. */ - if (!is_halfchan_defined(&chan->half[local_chan->direction])) + if (!is_halfchan_defined(&chan->half[direction])) update = NULL; else update = gossip_store_get(tmpctx, rstate->gs, - chan->half[local_chan->direction].bcast.index); + chan->half[direction].bcast.index); out: status_peer_debug(&peer->id, "schanid %s: %s update", type_to_string(tmpctx, struct short_channel_id, &scid), @@ -808,6 +831,7 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, const u8 *msg) { struct peer *peer = tal(conn, struct peer); + struct node *node; int fds[2]; int gossip_store_fd; struct gossip_state *gs; @@ -869,6 +893,10 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, /* Free peer if conn closed (destroy_peer closes conn if peer freed) */ tal_steal(peer->dc, peer); + node = get_node(daemon->rstate, &peer->id); + if (node) + peer_enable_channels(daemon, node); + /* This sends the initial timestamp filter. */ seeker_setup_peer_gossip(daemon->seeker, peer); @@ -967,23 +995,6 @@ static struct io_plan *connectd_req(struct io_conn *conn, return io_close(conn); } -/*~ This is our 13-day timer callback for refreshing our channels. This - * was added to the spec because people abandoned their channels without - * closing them. */ -static void gossip_send_keepalive_update(struct daemon *daemon, - struct local_chan *local_chan) -{ - status_debug("Sending keepalive channel_update for %s/%u", - type_to_string(tmpctx, struct short_channel_id, - &local_chan->chan->scid), - local_chan->direction); - - /* As a side-effect, this will create an update which matches the - * local_disabled state */ - refresh_local_channel(daemon, local_chan, true); -} - - /* BOLT #7: * * A node: @@ -1016,11 +1027,13 @@ static void gossip_refresh_network(struct daemon *daemon) struct chan *c; for (c = first_chan(n, &i); c; c = next_chan(n, &i)) { - struct local_chan *local_chan; struct half_chan *hc; + int direction; + + if (!local_direction(daemon->rstate, c, &direction)) + continue; - local_chan = is_local_chan(daemon->rstate, c); - hc = &c->half[local_chan->direction]; + hc = &c->half[direction]; if (!is_halfchan_defined(hc)) { /* Connection is not announced yet, so don't even @@ -1033,12 +1046,12 @@ static void gossip_refresh_network(struct daemon *daemon) continue; } - if (is_chan_local_disabled(daemon->rstate, c)) { - /* Only send keepalives for active connections */ - continue; - } - - gossip_send_keepalive_update(daemon, local_chan); + status_debug("Sending keepalive channel_update" + " for %s/%u", + type_to_string(tmpctx, + struct short_channel_id, + &c->scid), direction); + refresh_local_channel(daemon, c, direction); } } @@ -1060,7 +1073,7 @@ static void gossip_disable_local_channels(struct daemon *daemon) return; for (c = first_chan(local_node, &i); c; c = next_chan(local_node, &i)) - local_disable_chan(daemon->rstate, c); + local_disable_chan(daemon, c, half_chan_idx(local_node, c)); } struct peer *random_peer(struct daemon *daemon, @@ -1244,24 +1257,34 @@ static struct io_plan *get_stripped_cupdate(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { struct short_channel_id scid; - struct local_chan *local_chan; + struct chan *chan; const u8 *stripped_update; if (!fromwire_gossipd_get_stripped_cupdate(msg, &scid)) master_badmsg(WIRE_GOSSIPD_GET_STRIPPED_CUPDATE, msg); - local_chan = local_chan_map_get(&daemon->rstate->local_chan_map, &scid); - if (!local_chan) { + chan = get_channel(daemon->rstate, &scid); + if (!chan) { status_debug("Failed to resolve local channel %s", type_to_string(tmpctx, struct short_channel_id, &scid)); stripped_update = NULL; } else { + int direction; const struct half_chan *hc; + if (!local_direction(daemon->rstate, chan, &direction)) { + status_broken("%s is a non-local channel!", + type_to_string(tmpctx, + struct short_channel_id, + &scid)); + stripped_update = NULL; + goto out; + } + /* Since we're going to use it, make sure it's up-to-date. */ - refresh_local_channel(daemon, local_chan, false); + local_channel_update_latest(daemon, chan); - hc = &local_chan->chan->half[local_chan->direction]; + hc = &chan->half[direction]; if (is_halfchan_defined(hc)) { const u8 *update; @@ -1272,6 +1295,8 @@ static struct io_plan *get_stripped_cupdate(struct io_conn *conn, } else stripped_update = NULL; } + +out: daemon_conn_send(daemon->master, take(towire_gossipd_get_stripped_cupdate_reply(NULL, stripped_update))); @@ -1423,8 +1448,18 @@ static struct io_plan *handle_local_channel_close(struct io_conn *conn, master_badmsg(WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE, msg); chan = get_channel(rstate, &scid); - if (chan) - local_disable_chan(rstate, chan); + if (chan) { + int direction; + + if (!local_direction(rstate, chan, &direction)) { + status_broken("Non-local channel close %s", + type_to_string(tmpctx, + struct short_channel_id, + &scid)); + } else { + local_disable_chan(daemon, chan, direction); + } + } return daemon_conn_read_next(conn, daemon->master); } @@ -1522,6 +1557,7 @@ int main(int argc, char *argv[]) daemon->node_announce_timer = NULL; daemon->current_blockheight = 0; /* i.e. unknown */ daemon->rates = NULL; + list_head_init(&daemon->deferred_updates); /* Tell the ecdh() function how to talk to hsmd */ ecdh_hsmd_setup(HSM_FD, status_failed); diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index fe2082c74b26..af1d435ae3e7 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -60,6 +60,9 @@ struct daemon { /* The channel lease rates we're advertising */ const struct lease_rates *rates; + + /* Any of our channel_updates we're deferring. */ + struct list_head deferred_updates; }; struct range_query_reply { diff --git a/gossipd/routing.c b/gossipd/routing.c index 560e1423932d..58b1d5c789b8 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -198,7 +198,6 @@ static void destroy_routing_state(struct routing_state *rstate) /* Free up our htables */ pending_cannouncement_map_clear(&rstate->pending_cannouncements); - local_chan_map_clear(&rstate->local_chan_map); } /* We don't check this when loading from the gossip_store: that would break @@ -226,7 +225,6 @@ static void memleak_help_routing_tables(struct htable *memtable, memleak_remove_htable(memtable, &rstate->nodes->raw); memleak_remove_htable(memtable, &rstate->pending_node_map->raw); memleak_remove_htable(memtable, &rstate->pending_cannouncements.raw); - memleak_remove_htable(memtable, &rstate->local_chan_map.raw); memleak_remove_uintmap(memtable, &rstate->unupdated_chanmap); for (n = node_map_first(rstate->nodes, &nit); @@ -295,7 +293,6 @@ struct routing_state *new_routing_state(const tal_t *ctx, uintmap_init(&rstate->chanmap); uintmap_init(&rstate->unupdated_chanmap); - local_chan_map_init(&rstate->local_chan_map); rstate->num_txout_failures = 0; uintmap_init(&rstate->txout_failures); uintmap_init(&rstate->txout_failures_old); @@ -524,36 +521,6 @@ static void bad_gossip_order(const u8 *msg, details); } -static void destroy_local_chan(struct local_chan *local_chan, - struct routing_state *rstate) -{ - if (!local_chan_map_del(&rstate->local_chan_map, local_chan)) - abort(); -} - -static void maybe_add_local_chan(struct routing_state *rstate, - struct chan *chan) -{ - int direction; - struct local_chan *local_chan; - - if (node_id_eq(&chan->nodes[0]->id, &rstate->local_id)) - direction = 0; - else if (node_id_eq(&chan->nodes[1]->id, &rstate->local_id)) - direction = 1; - else - return; - - local_chan = tal(chan, struct local_chan); - local_chan->chan = chan; - local_chan->direction = direction; - local_chan->local_disabled = false; - local_chan->channel_update_timer = NULL; - - local_chan_map_add(&rstate->local_chan_map, local_chan); - tal_add_destructor2(local_chan, destroy_local_chan, rstate); -} - struct chan *new_chan(struct routing_state *rstate, const struct short_channel_id *scid, const struct node_id *id1, @@ -595,8 +562,6 @@ struct chan *new_chan(struct routing_state *rstate, uintmap_add(&rstate->chanmap, scid->u64, chan); - /* Initialize shadow structure if it's local */ - maybe_add_local_chan(rstate, chan); return chan; } diff --git a/gossipd/routing.h b/gossipd/routing.h index 1a8a08d0cde0..e33d6306116b 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -94,22 +94,6 @@ static inline bool chan_eq_scid(const struct chan *c, HTABLE_DEFINE_TYPE(struct chan, chan_map_scid, hash_scid, chan_eq_scid, chan_map); -/* Container for local channel pointers. */ -static inline const struct short_channel_id *local_chan_map_scid(const struct local_chan *local_chan) -{ - return &local_chan->chan->scid; -} - -static inline bool local_chan_eq_scid(const struct local_chan *local_chan, - const struct short_channel_id *scid) -{ - return short_channel_id_eq(scid, &local_chan->chan->scid); -} - -HTABLE_DEFINE_TYPE(struct local_chan, - local_chan_map_scid, hash_scid, local_chan_eq_scid, - local_chan_map); - /* For a small number of channels (by far the most common) we use a simple * array, with empty buckets NULL. For larger, we use a proper hash table, * with the extra allocation that implies. */ @@ -191,33 +175,13 @@ HTABLE_DEFINE_TYPE(struct pending_cannouncement, panding_cannouncement_map_scid, struct pending_node_map; struct unupdated_channel; -/* Fast versions: if you know n is one end of the channel */ -static inline struct node *other_node(const struct node *n, - const struct chan *chan) +/* If you know n is one end of the channel, get index of src == n */ +static inline int half_chan_idx(const struct node *n, const struct chan *chan) { int idx = (chan->nodes[1] == n); assert(chan->nodes[0] == n || chan->nodes[1] == n); - return chan->nodes[!idx]; -} - -/* If you know n is one end of the channel, get connection src == n */ -static inline struct half_chan *half_chan_from(const struct node *n, - struct chan *chan) -{ - int idx = (chan->nodes[1] == n); - - assert(chan->nodes[0] == n || chan->nodes[1] == n); - return &chan->half[idx]; -} - -/* If you know n is one end of the channel, get index dst == n */ -static inline int half_chan_to(const struct node *n, const struct chan *chan) -{ - int idx = (chan->nodes[1] == n); - - assert(chan->nodes[0] == n || chan->nodes[1] == n); - return !idx; + return idx; } struct routing_state { @@ -255,9 +219,6 @@ struct routing_state { UINTMAP(bool) txout_failures, txout_failures_old; struct oneshot *txout_failure_timer; - /* A map of local channels by short_channel_ids */ - struct local_chan_map local_chan_map; - /* Highest timestamp of gossip we accepted (before now) */ u32 last_timestamp; @@ -427,41 +388,11 @@ bool routing_add_private_channel(struct routing_state *rstate, */ struct timeabs gossip_time_now(const struct routing_state *rstate); -static inline struct local_chan *is_local_chan(struct routing_state *rstate, - const struct chan *chan) -{ - return local_chan_map_get(&rstate->local_chan_map, &chan->scid); -} - /* Would we ratelimit a channel_update with this timestamp? */ bool would_ratelimit_cupdate(struct routing_state *rstate, const struct half_chan *hc, u32 timestamp); -/* Because we can have millions of channels, and we only want a local_disable - * flag on ones connected to us, we keep a separate hashtable for that flag. - */ -static inline bool is_chan_local_disabled(struct routing_state *rstate, - const struct chan *chan) -{ - struct local_chan *local_chan = is_local_chan(rstate, chan); - return local_chan && local_chan->local_disabled; -} - -static inline void local_disable_chan(struct routing_state *rstate, - const struct chan *chan) -{ - struct local_chan *local_chan = is_local_chan(rstate, chan); - local_chan->local_disabled = true; -} - -static inline void local_enable_chan(struct routing_state *rstate, - const struct chan *chan) -{ - struct local_chan *local_chan = is_local_chan(rstate, chan); - local_chan->local_disabled = false; -} - /* Remove channel from store: announcement and any updates. */ void remove_channel_from_store(struct routing_state *rstate, struct chan *chan); diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 473554f3f720..b7bfd6a6896f 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -203,6 +203,15 @@ void json_object_end(struct json_stream *js UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for local_channel_update_latest */ +bool local_channel_update_latest(struct daemon *daemon UNNEEDED, struct chan *chan UNNEEDED) +{ fprintf(stderr, "local_channel_update_latest called!\n"); abort(); } +/* Generated stub for local_disable_chan */ +void local_disable_chan(struct daemon *daemon UNNEEDED, const struct chan *chan UNNEEDED, int direction UNNEEDED) +{ fprintf(stderr, "local_disable_chan called!\n"); abort(); } +/* Generated stub for local_enable_chan */ +void local_enable_chan(struct daemon *daemon UNNEEDED, const struct chan *chan UNNEEDED, int direction UNNEEDED) +{ fprintf(stderr, "local_enable_chan called!\n"); abort(); } /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } @@ -258,8 +267,7 @@ void query_unknown_node(struct seeker *seeker UNNEEDED, struct peer *peer UNNEED { fprintf(stderr, "query_unknown_node called!\n"); abort(); } /* Generated stub for refresh_local_channel */ void refresh_local_channel(struct daemon *daemon UNNEEDED, - struct local_chan *local_chan UNNEEDED, - bool even_if_identical UNNEEDED) + struct chan *chan UNNEEDED, int direction UNNEEDED) { fprintf(stderr, "refresh_local_channel called!\n"); abort(); } /* Generated stub for remove_channel_from_store */ void remove_channel_from_store(struct routing_state *rstate UNNEEDED, From d287d7d2298fa3d53205570cdc821fb5eb604517 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 13:56:43 +1030 Subject: [PATCH 0183/1530] gossipd: make request handlers return void. They all returned the next io_plan, but it was always the same. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 112 ++++++++++++------------------- gossipd/queries.c | 5 +- gossipd/queries.h | 4 +- gossipd/test/run-onion_message.c | 5 +- 4 files changed, 48 insertions(+), 78 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 098fe652f345..f1c57b9e8c1f 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -523,8 +523,7 @@ static u8 *handle_obs2_onion_message(struct peer *peer, const u8 *msg) return NULL; } -static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) +static void onionmsg_req(struct daemon *daemon, const u8 *msg) { struct node_id id; u8 *onionmsg; @@ -546,7 +545,6 @@ static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, omsg = towire_onion_message(NULL, &blinding, onionmsg); queue_peer_msg(peer, take(omsg)); } - return daemon_conn_read_next(conn, daemon->master); } /* Peer sends an onion msg. */ @@ -1099,9 +1097,7 @@ struct peer *random_peer(struct daemon *daemon, } /*~ Parse init message from lightningd: starts the daemon properly. */ -static struct io_plan *gossip_init(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void gossip_init(struct daemon *daemon, const u8 *msg) { u32 *dev_gossip_time; bool dev_fast_gossip, dev_fast_gossip_prune; @@ -1159,12 +1155,9 @@ static struct io_plan *gossip_init(struct io_conn *conn, /* OK, we are ready. */ daemon_conn_send(daemon->master, take(towire_gossipd_init_reply(NULL))); - return daemon_conn_read_next(conn, daemon->master); } -static struct io_plan *new_blockheight(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void new_blockheight(struct daemon *daemon, const u8 *msg) { if (!fromwire_gossipd_new_blockheight(msg, &daemon->current_blockheight)) master_badmsg(WIRE_GOSSIPD_NEW_BLOCKHEIGHT, msg); @@ -1185,27 +1178,20 @@ static struct io_plan *new_blockheight(struct io_conn *conn, tal_arr_remove(&daemon->deferred_txouts, i); i--; } - - return daemon_conn_read_next(conn, daemon->master); } #if DEVELOPER /* Another testing hack */ -static struct io_plan *dev_gossip_suppress(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void dev_gossip_suppress(struct daemon *daemon, const u8 *msg) { if (!fromwire_gossipd_dev_suppress(msg)) master_badmsg(WIRE_GOSSIPD_DEV_SUPPRESS, msg); status_unusual("Suppressing all gossip"); dev_suppress_gossip = true; - return daemon_conn_read_next(conn, daemon->master); } -static struct io_plan *dev_gossip_memleak(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) { struct htable *memtable; bool found_leak; @@ -1219,24 +1205,18 @@ static struct io_plan *dev_gossip_memleak(struct io_conn *conn, daemon_conn_send(daemon->master, take(towire_gossipd_dev_memleak_reply(NULL, found_leak))); - return daemon_conn_read_next(conn, daemon->master); } -static struct io_plan *dev_compact_store(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void dev_compact_store(struct daemon *daemon, const u8 *msg) { bool done = gossip_store_compact(daemon->rstate->gs); daemon_conn_send(daemon->master, take(towire_gossipd_dev_compact_store_reply(NULL, done))); - return daemon_conn_read_next(conn, daemon->master); } -static struct io_plan *dev_gossip_set_time(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void dev_gossip_set_time(struct daemon *daemon, const u8 *msg) { u32 time; @@ -1246,15 +1226,12 @@ static struct io_plan *dev_gossip_set_time(struct io_conn *conn, daemon->rstate->gossip_time = tal(daemon->rstate, struct timeabs); daemon->rstate->gossip_time->ts.tv_sec = time; daemon->rstate->gossip_time->ts.tv_nsec = 0; - - return daemon_conn_read_next(conn, daemon->master); } #endif /* DEVELOPER */ /*~ lightningd: so, get me the latest update for this local channel, * so I can include it in an error message. */ -static struct io_plan *get_stripped_cupdate(struct io_conn *conn, - struct daemon *daemon, const u8 *msg) +static void get_stripped_cupdate(struct daemon *daemon, const u8 *msg) { struct short_channel_id scid; struct chan *chan; @@ -1300,13 +1277,11 @@ static struct io_plan *get_stripped_cupdate(struct io_conn *conn, daemon_conn_send(daemon->master, take(towire_gossipd_get_stripped_cupdate_reply(NULL, stripped_update))); - return daemon_conn_read_next(conn, daemon->master); } /*~ We queue incoming channel_announcement pending confirmation from lightningd * that it really is an unspent output. Here's its reply. */ -static struct io_plan *handle_txout_reply(struct io_conn *conn, - struct daemon *daemon, const u8 *msg) +static void handle_txout_reply(struct daemon *daemon, const u8 *msg) { struct short_channel_id scid; u8 *outscript; @@ -1326,16 +1301,12 @@ static struct io_plan *handle_txout_reply(struct io_conn *conn, /* Anywhere we might have announced a channel, we check if it's time to * announce ourselves (ie. if we just announced our own first channel) */ maybe_send_own_node_announce(daemon, false); - - return daemon_conn_read_next(conn, daemon->master); } /*~ lightningd tells us when about a gossip message directly, when told to by * the addgossip RPC call. That's usually used when a plugin gets an update * returned in an payment error. */ -static struct io_plan *inject_gossip(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void inject_gossip(struct daemon *daemon, const u8 *msg) { u8 *goss; const u8 *errmsg; @@ -1374,12 +1345,9 @@ static struct io_plan *inject_gossip(struct io_conn *conn, err_extracted: daemon_conn_send(daemon->master, take(towire_gossipd_addgossip_reply(NULL, err))); - return daemon_conn_read_next(conn, daemon->master); } -static struct io_plan *handle_new_lease_rates(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void handle_new_lease_rates(struct daemon *daemon, const u8 *msg) { struct lease_rates *rates = tal(daemon, struct lease_rates); @@ -1394,15 +1362,11 @@ static struct io_plan *handle_new_lease_rates(struct io_conn *conn, /* Send the update over to the peer */ maybe_send_own_node_announce(daemon, false); - - return daemon_conn_read_next(conn, daemon->master); } /*~ This is where lightningd tells us that a channel's funding transaction has * been spent. */ -static struct io_plan *handle_outpoint_spent(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void handle_outpoint_spent(struct daemon *daemon, const u8 *msg) { struct short_channel_id scid; struct chan *chan; @@ -1424,8 +1388,6 @@ static struct io_plan *handle_outpoint_spent(struct io_conn *conn, * the channel */ free_chan(rstate, chan); } - - return daemon_conn_read_next(conn, daemon->master); } /*~ This is sent by lightningd when it kicks off 'closingd': we disable it @@ -1437,9 +1399,7 @@ static struct io_plan *handle_outpoint_spent(struct io_conn *conn, * channels. This does not send out updates since that's triggered by the peer * connection closing. */ -static struct io_plan *handle_local_channel_close(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +static void handle_local_channel_close(struct daemon *daemon, const u8 *msg) { struct short_channel_id scid; struct chan *chan; @@ -1460,7 +1420,6 @@ static struct io_plan *handle_local_channel_close(struct io_conn *conn, local_disable_chan(daemon, chan, direction); } } - return daemon_conn_read_next(conn, daemon->master); } /*~ This routine handles all the commands from lightningd. */ @@ -1472,40 +1431,53 @@ static struct io_plan *recv_req(struct io_conn *conn, switch (t) { case WIRE_GOSSIPD_INIT: - return gossip_init(conn, daemon, msg); + gossip_init(daemon, msg); + goto done; case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: - return get_stripped_cupdate(conn, daemon, msg); + get_stripped_cupdate(daemon, msg); + goto done; case WIRE_GOSSIPD_GET_TXOUT_REPLY: - return handle_txout_reply(conn, daemon, msg); + handle_txout_reply(daemon, msg); + goto done; case WIRE_GOSSIPD_OUTPOINT_SPENT: - return handle_outpoint_spent(conn, daemon, msg); + handle_outpoint_spent(daemon, msg); + goto done; case WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE: - return handle_local_channel_close(conn, daemon, msg); + handle_local_channel_close(daemon, msg); + goto done; case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: - return new_blockheight(conn, daemon, msg); + new_blockheight(daemon, msg); + goto done; case WIRE_GOSSIPD_ADDGOSSIP: - return inject_gossip(conn, daemon, msg); + inject_gossip(daemon, msg); + goto done; case WIRE_GOSSIPD_NEW_LEASE_RATES: - return handle_new_lease_rates(conn, daemon, msg); + handle_new_lease_rates(daemon, msg); + goto done; #if DEVELOPER case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: - return dev_set_max_scids_encode_size(conn, daemon, msg); + dev_set_max_scids_encode_size(daemon, msg); + goto done; case WIRE_GOSSIPD_DEV_SUPPRESS: - return dev_gossip_suppress(conn, daemon, msg); + dev_gossip_suppress(daemon, msg); + goto done; case WIRE_GOSSIPD_DEV_MEMLEAK: - return dev_gossip_memleak(conn, daemon, msg); + dev_gossip_memleak(daemon, msg); + goto done; case WIRE_GOSSIPD_DEV_COMPACT_STORE: - return dev_compact_store(conn, daemon, msg); + dev_compact_store(daemon, msg); + goto done; case WIRE_GOSSIPD_DEV_SET_TIME: - return dev_gossip_set_time(conn, daemon, msg); + dev_gossip_set_time(daemon, msg); + goto done; #else case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPD_DEV_SUPPRESS: @@ -1516,7 +1488,8 @@ static struct io_plan *recv_req(struct io_conn *conn, #endif /* !DEVELOPER */ case WIRE_GOSSIPD_SEND_ONIONMSG: - return onionmsg_req(conn, daemon, msg); + onionmsg_req(daemon, msg); + goto done; /* We send these, we don't receive them */ case WIRE_GOSSIPD_INIT_REPLY: @@ -1532,6 +1505,9 @@ static struct io_plan *recv_req(struct io_conn *conn, /* Master shouldn't give bad requests. */ status_failed(STATUS_FAIL_MASTER_IO, "%i: %s", t, tal_hex(tmpctx, msg)); + +done: + return daemon_conn_read_next(conn, daemon->master); } /* This is called when lightningd closes its connection to us. We simply diff --git a/gossipd/queries.c b/gossipd/queries.c index b46a683b9a8e..ccd8e2dc6cca 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -1163,15 +1163,12 @@ bool query_channel_range(struct daemon *daemon, #if DEVELOPER /* This is a testing hack to allow us to artificially lower the maximum bytes * of short_channel_ids we'll encode, using dev_set_max_scids_encode_size. */ -struct io_plan *dev_set_max_scids_encode_size(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +void dev_set_max_scids_encode_size(struct daemon *daemon, const u8 *msg) { if (!fromwire_gossipd_dev_set_max_scids_encode_size(msg, &dev_max_encoding_bytes)) master_badmsg(WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE, msg); status_debug("Set max_scids_encode_bytes to %u", dev_max_encoding_bytes); - return daemon_conn_read_next(conn, daemon->master); } #endif /* DEVELOPER */ diff --git a/gossipd/queries.h b/gossipd/queries.h index 67fbca0c30ee..14ea121ae64e 100644 --- a/gossipd/queries.h +++ b/gossipd/queries.h @@ -62,9 +62,7 @@ struct io_plan *dev_query_channel_range(struct io_conn *conn, /* This is a testing hack to allow us to artificially lower the maximum bytes * of short_channel_ids we'll encode, using dev_set_max_scids_encode_size. */ -struct io_plan *dev_set_max_scids_encode_size(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg); +void dev_set_max_scids_encode_size(struct daemon *daemon, const u8 *msg); #endif /* DEVELOPER */ #endif /* LIGHTNING_GOSSIPD_QUERIES_H */ diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index b7bfd6a6896f..1a4cfd59eb9d 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -18,9 +18,8 @@ int unused_main(int argc, char *argv[]); bool dev_suppress_gossip; /* Generated stub for dev_set_max_scids_encode_size */ -struct io_plan *dev_set_max_scids_encode_size(struct io_conn *conn UNNEEDED, - struct daemon *daemon UNNEEDED, - const u8 *msg UNNEEDED) +void dev_set_max_scids_encode_size(struct daemon *daemon UNNEEDED, + const u8 *msg UNNEEDED) { fprintf(stderr, "dev_set_max_scids_encode_size called!\n"); abort(); } /* Generated stub for dump_memleak */ bool dump_memleak(struct htable *memtable UNNEEDED, From 4ef2367c048b7fdcec8ee76e508359161144872b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 13:56:43 +1030 Subject: [PATCH 0184/1530] connectd: rename struct peer in peer_exchange_initmsg to early_peer. We want to have a real (persistent) struct peer eventually. Signed-off-by: Rusty Russell --- connectd/peer_exchange_initmsg.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index d960a83d0587..8110f5a2960f 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -10,7 +10,7 @@ #include /* Temporary structure for us to read peer message in */ -struct peer { +struct early_peer { struct daemon *daemon; /* The ID of the peer */ @@ -38,10 +38,10 @@ static bool contains_common_chain(struct bitcoin_blkid *chains) } /* Here in case we need to read another message. */ -static struct io_plan *read_init(struct io_conn *conn, struct peer *peer); +static struct io_plan *read_init(struct io_conn *conn, struct early_peer *peer); static struct io_plan *peer_init_received(struct io_conn *conn, - struct peer *peer) + struct early_peer *peer) { u8 *msg = cryptomsg_decrypt_body(tmpctx, &peer->cs, peer->msg); u8 *globalfeatures, *features; @@ -89,6 +89,9 @@ static struct io_plan *peer_init_received(struct io_conn *conn, * window where it was: combine the two. */ features = featurebits_or(tmpctx, take(features), globalfeatures); + /* We can dispose of peer after next call. */ + tal_steal(tmpctx, peer); + /* Usually return io_close_taken_fd, but may wait for old peer to * be disconnected if it's a reconnect. */ return peer_connected(conn, peer->daemon, &peer->id, @@ -98,7 +101,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, } static struct io_plan *peer_init_hdr_received(struct io_conn *conn, - struct peer *peer) + struct early_peer *peer) { u16 len; @@ -111,7 +114,7 @@ static struct io_plan *peer_init_hdr_received(struct io_conn *conn, peer_init_received, peer); } -static struct io_plan *read_init(struct io_conn *conn, struct peer *peer) +static struct io_plan *read_init(struct io_conn *conn, struct early_peer *peer) { /* Free our sent init msg. */ tal_free(peer->msg); @@ -128,14 +131,14 @@ static struct io_plan *read_init(struct io_conn *conn, struct peer *peer) #if DEVELOPER static struct io_plan *peer_write_postclose(struct io_conn *conn, - struct peer *peer) + struct early_peer *peer) { 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) + struct early_peer *peer) { dev_sabotage_fd(io_conn_fd(conn), false); return read_init(conn, peer); @@ -151,8 +154,8 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, bool incoming) { /* If conn is closed, forget peer */ - struct peer *peer = tal(conn, struct peer); - struct io_plan *(*next)(struct io_conn *, struct peer *); + struct early_peer *peer = tal(conn, struct early_peer); + struct io_plan *(*next)(struct io_conn *, struct early_peer *); struct tlv_init_tlvs *tlvs; peer->daemon = daemon; From 5aa8663c98dd9dfa9d206e34dd569a98d94f2997 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 13:56:43 +1030 Subject: [PATCH 0185/1530] common/socket_close.c: remove obsolete comment. Put more inline comments, and also preserve errno properly from read. Suggested-by: @cdecker Signed-off-by: Rusty Russell --- common/socket_close.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/common/socket_close.c b/common/socket_close.c index 7d19ec76814b..5abaf80d36da 100644 --- a/common/socket_close.c +++ b/common/socket_close.c @@ -7,20 +7,7 @@ #include #include -/* -Simplified (minus all the error checks): - - shutdown(fd, SHUT_WR); - for (;;) { - char unused[64] - sys_res = read(fd, unused, 64); - if (sys_res == 0) - break; - } - close(fd); -*/ - -/* makes read() return EINTR */ +/* makes read() return EINTR after 5 seconds */ static void break_read(int signum) { } @@ -29,8 +16,9 @@ bool socket_close(int fd) { char unused[64]; struct sigaction act, old_act; - int sys_res; + int sys_res, saved_errno; + /* We shutdown. Usually they then shutdown too, and read() gives 0 */ sys_res = shutdown(fd, SHUT_WR); if (sys_res < 0) { close_noerr(fd); @@ -45,12 +33,14 @@ bool socket_close(int fd) alarm(5); while ((sys_res = read(fd, unused, sizeof(unused))) > 0); + saved_errno = errno; alarm(0); sigaction(SIGALRM, &old_act, NULL); if (sys_res < 0) { - close_noerr(fd); + close(fd); + errno = saved_errno; return false; } From 8759641b0650eabebd528b3f3d637690a3870bdb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:50:09 +1030 Subject: [PATCH 0186/1530] ccan: update to get new helpers in ccan/tal tal_dup_talarr() is simply stolen from common/utils, but tal_dup_or_null is new. Signed-off-by: Rusty Russell --- ccan/README | 2 +- ccan/ccan/tal/tal.c | 13 ++++++++++++- ccan/ccan/tal/tal.h | 37 ++++++++++++++++++++++++++++++------- common/utils.c | 10 ---------- common/utils.h | 11 ----------- 5 files changed, 43 insertions(+), 30 deletions(-) diff --git a/ccan/README b/ccan/README index 2b5d2c37df82..503ba45676df 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2522-g21543f83 +CCAN version: init-2523-gb15b3673 diff --git a/ccan/ccan/tal/tal.c b/ccan/ccan/tal/tal.c index 05d52e1dbe5c..061fc2907aff 100644 --- a/ccan/ccan/tal/tal.c +++ b/ccan/ccan/tal/tal.c @@ -767,11 +767,17 @@ bool tal_expand_(tal_t **ctxp, const void *src, size_t size, size_t count) } void *tal_dup_(const tal_t *ctx, const void *p, size_t size, - size_t n, size_t extra, const char *label) + size_t n, size_t extra, bool nullok, const char *label) { void *ret; size_t nbytes = size; + if (nullok && p == NULL) { + /* take(NULL) works. */ + (void)taken(p); + return NULL; + } + if (!adjust_size(&nbytes, n)) { if (taken(p)) tal_free(p); @@ -802,6 +808,11 @@ void *tal_dup_(const tal_t *ctx, const void *p, size_t size, return ret; } +void *tal_dup_talarr_(const tal_t *ctx, const tal_t *src TAKES, const char *label) +{ + return tal_dup_(ctx, src, 1, tal_bytelen(src), 0, true, label); +} + void tal_set_backend(void *(*alloc_fn)(size_t size), void *(*resize_fn)(void *, size_t size), void (*free_fn)(void *), diff --git a/ccan/ccan/tal/tal.h b/ccan/ccan/tal/tal.h index a3a1549153fe..c486f9e8e194 100644 --- a/ccan/ccan/tal/tal.h +++ b/ccan/ccan/tal/tal.h @@ -352,10 +352,21 @@ tal_t *tal_parent(const tal_t *ctx); * tal_dup - duplicate an object. * @ctx: The tal allocated object to be parent of the result (may be NULL). * @type: the type (should match type of @p!) - * @p: the object to copy (or reparented if take()) + * @p: the object to copy (or reparented if take()). Must not be NULL. */ #define tal_dup(ctx, type, p) \ - tal_dup_label(ctx, type, p, TAL_LABEL(type, "")) + tal_dup_label(ctx, type, p, TAL_LABEL(type, ""), false) + +/** + * tal_dup_or_null - duplicate an object, or just pass NULL. + * @ctx: The tal allocated object to be parent of the result (may be NULL). + * @type: the type (should match type of @p!) + * @p: the object to copy (or reparented if take()) + * + * if @p is NULL, just return NULL, otherwise to tal_dup(). + */ +#define tal_dup_or_null(ctx, type, p) \ + tal_dup_label(ctx, type, p, TAL_LABEL(type, ""), true) /** * tal_dup_arr - duplicate an array. @@ -369,7 +380,17 @@ tal_t *tal_parent(const tal_t *ctx); tal_dup_arr_label(ctx, type, p, n, extra, TAL_LABEL(type, "[]")) - +/** + * tal_dup_arr - duplicate a tal array. + * @ctx: The tal allocated object to be parent of the result (may be NULL). + * @type: the type (should match type of @p!) + * @p: the tal array to copy (or resized & reparented if take()) + * + * The comon case of duplicating an entire tal array. + */ +#define tal_dup_talarr(ctx, type, p) \ + ((type *)tal_dup_talarr_((ctx), tal_typechk_(p, type *), \ + TAL_LABEL(type, "[]"))) /* Lower-level interfaces, where you want to supply your own label string. */ #define tal_label(ctx, type, label) \ ((type *)tal_alloc_((ctx), sizeof(type), false, label)) @@ -379,13 +400,13 @@ tal_t *tal_parent(const tal_t *ctx); ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), false, label)) #define tal_arrz_label(ctx, type, count, label) \ ((type *)tal_alloc_arr_((ctx), sizeof(type), (count), true, label)) -#define tal_dup_label(ctx, type, p, label) \ +#define tal_dup_label(ctx, type, p, label, nullok) \ ((type *)tal_dup_((ctx), tal_typechk_(p, type *), \ - sizeof(type), 1, 0, \ + sizeof(type), 1, 0, nullok, \ label)) #define tal_dup_arr_label(ctx, type, p, n, extra, label) \ ((type *)tal_dup_((ctx), tal_typechk_(p, type *), \ - sizeof(type), (n), (extra), \ + sizeof(type), (n), (extra), false, \ label)) /** @@ -505,7 +526,9 @@ void *tal_alloc_arr_(const tal_t *ctx, size_t bytes, size_t count, bool clear, const char *label); void *tal_dup_(const tal_t *ctx, const void *p TAKES, size_t size, - size_t n, size_t extra, const char *label); + size_t n, size_t extra, bool nullok, const char *label); +void *tal_dup_talarr_(const tal_t *ctx, const tal_t *src TAKES, + const char *label); tal_t *tal_steal_(const tal_t *new_parent, const tal_t *t); diff --git a/common/utils.c b/common/utils.c index b9fbded5b401..a11598fb1fb8 100644 --- a/common/utils.c +++ b/common/utils.c @@ -175,16 +175,6 @@ void tal_arr_remove_(void *p, size_t elemsize, size_t n) tal_resize((char **)p, len - elemsize); } -void *tal_dup_talarr_(const tal_t *ctx, const tal_t *src TAKES, const char *label) -{ - if (!src) { - /* Correctly handle TAKES on a NULL `src`. */ - (void) taken(src); - return NULL; - } - return tal_dup_(ctx, src, 1, tal_bytelen(src), 0, label); -} - /* Check for valid UTF-8 */ bool utf8_check(const void *vbuf, size_t buflen) { diff --git a/common/utils.h b/common/utils.h index 66ca58a4d84a..69314f8c2354 100644 --- a/common/utils.h +++ b/common/utils.h @@ -85,17 +85,6 @@ void clear_softref_(const tal_t *outer, size_t outersize, void **ptr); #define tal_arr_remove(p, n) tal_arr_remove_((p), sizeof(**p), (n)) void tal_arr_remove_(void *p, size_t elemsize, size_t n); -/** - * The comon case of duplicating an entire tal array. - * - * A macro because we must not double-evaluate p. - */ -#define tal_dup_talarr(ctx, type, p) \ - ((type *)tal_dup_talarr_((ctx), tal_typechk_(p, type *), \ - TAL_LABEL(type, "[]"))) -void *tal_dup_talarr_(const tal_t *ctx, const tal_t *src TAKES, - const char *label); - /* Check for valid UTF-8 */ bool utf8_check(const void *buf, size_t buflen); From 967ffbfbcbf4990838815c6553a08e194c52a05f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 28 Dec 2021 09:51:09 +1030 Subject: [PATCH 0187/1530] global: use tal_dup_or_null(). Signed-off-by: Rusty Russell --- channeld/full_channel.c | 8 +------- common/blockheight_states.c | 6 ++---- common/coin_mvt.c | 5 +---- common/fee_states.c | 7 +++---- common/htlc_wire.c | 18 +++++------------- common/onion.c | 5 +---- lightningd/channel.c | 7 ++----- lightningd/connect_control.c | 5 +---- lightningd/htlc_end.c | 10 ++-------- lightningd/log.c | 5 +---- lightningd/pay.c | 29 ++++++++--------------------- lightningd/subd.c | 5 +---- onchaind/onchaind.c | 5 +---- plugins/pay.c | 4 +--- 14 files changed, 30 insertions(+), 89 deletions(-) diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 6e1b39c87af3..70ddc15df87a 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -546,13 +546,7 @@ static enum channel_add_err add_htlc(struct channel *channel, htlc->fail_immediate = false; htlc->rhash = *payment_hash; - if (blinding) - htlc->blinding = tal_dup(htlc, struct pubkey, blinding); - else { - /* Can be taken, even if NULL. */ - taken(blinding); - htlc->blinding = NULL; - } + htlc->blinding = tal_dup_or_null(htlc, struct pubkey, blinding); htlc->failed = NULL; htlc->r = NULL; htlc->routing = tal_dup_arr(htlc, u8, routing, TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE), 0); diff --git a/common/blockheight_states.c b/common/blockheight_states.c index b8f1843d7b3b..d6060233427f 100644 --- a/common/blockheight_states.c +++ b/common/blockheight_states.c @@ -101,11 +101,9 @@ struct height_states *dup_height_states(const tal_t *ctx, tal_steal(ctx, states)); n = tal_dup(ctx, struct height_states, states); - for (size_t i = 0; i < ARRAY_SIZE(n->height); i++) { - if (n->height[i]) - n->height[i] = tal_dup(n, u32, n->height[i]); + for (size_t i = 0; i < ARRAY_SIZE(n->height); i++) + n->height[i] = tal_dup_or_null(n, u32, n->height[i]); - } return n; } diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 9133c166bcf8..6f981e7fe8b7 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -105,10 +105,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, /* for htlc's that are filled onchain, we also have a * preimage, NULL otherwise */ - if (payment_hash) - mvt->payment_hash = tal_dup(mvt, struct sha256, payment_hash); - else - mvt->payment_hash = NULL; + mvt->payment_hash = tal_dup_or_null(mvt, struct sha256, payment_hash); mvt->blockheight = blockheight; mvt->tags = tal_steal(mvt, tags); diff --git a/common/fee_states.c b/common/fee_states.c index 4176972f8833..adf6845d4e72 100644 --- a/common/fee_states.c +++ b/common/fee_states.c @@ -48,10 +48,9 @@ struct fee_states *dup_fee_states(const tal_t *ctx, return cast_const(struct fee_states *, tal_steal(ctx, fee_states)); n = tal_dup(ctx, struct fee_states, fee_states); - for (size_t i = 0; i < ARRAY_SIZE(n->feerate); i++) { - if (n->feerate[i]) - n->feerate[i] = tal_dup(n, u32, n->feerate[i]); - } + for (size_t i = 0; i < ARRAY_SIZE(n->feerate); i++) + n->feerate[i] = tal_dup_or_null(n, u32, n->feerate[i]); + return n; } diff --git a/common/htlc_wire.c b/common/htlc_wire.c index d0a024fe4639..4385658b3ce5 100644 --- a/common/htlc_wire.c +++ b/common/htlc_wire.c @@ -14,10 +14,8 @@ static struct failed_htlc *failed_htlc_dup(const tal_t *ctx, return cast_const(struct failed_htlc *, tal_steal(ctx, f)); newf = tal(ctx, struct failed_htlc); newf->id = f->id; - if (f->sha256_of_onion) - newf->sha256_of_onion = tal_dup(newf, struct sha256, f->sha256_of_onion); - else - newf->sha256_of_onion = NULL; + newf->sha256_of_onion = tal_dup_or_null(newf, struct sha256, + f->sha256_of_onion); newf->badonion = f->badonion; if (f->onion) newf->onion = dup_onionreply(newf, f->onion); @@ -46,15 +44,9 @@ struct existing_htlc *new_existing_htlc(const tal_t *ctx, existing->payment_hash = *payment_hash; memcpy(existing->onion_routing_packet, onion_routing_packet, sizeof(existing->onion_routing_packet)); - if (blinding) - existing->blinding = tal_dup(existing, struct pubkey, blinding); - else - existing->blinding = NULL; - if (preimage) - existing->payment_preimage - = tal_dup(existing, struct preimage, preimage); - else - existing->payment_preimage = NULL; + existing->blinding = tal_dup_or_null(existing, struct pubkey, blinding); + existing->payment_preimage + = tal_dup_or_null(existing, struct preimage, preimage); if (failed) existing->failed = failed_htlc_dup(existing, failed); else diff --git a/common/onion.c b/common/onion.c index 69e0e32db059..f8b09511eaad 100644 --- a/common/onion.c +++ b/common/onion.c @@ -355,10 +355,7 @@ struct onion_payload *onion_decode(const tal_t *ctx, } p->payment_secret = NULL; - if (blinding) - p->blinding = tal_dup(p, struct pubkey, blinding); - else - p->blinding = NULL; + p->blinding = tal_dup_or_null(p, struct pubkey, blinding); #if EXPERIMENTAL_FEATURES if (!p->blinding) { diff --git a/lightningd/channel.c b/lightningd/channel.c index f8e7972dd15c..eeb8aba9cf55 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -183,12 +183,9 @@ new_inflight(struct channel *channel, /* Channel lease infos */ inflight->lease_blockheight_start = lease_blockheight_start; inflight->lease_expiry = lease_expiry; - if (lease_commit_sig) - inflight->lease_commit_sig - = tal_dup(inflight, secp256k1_ecdsa_signature, + inflight->lease_commit_sig + = tal_dup_or_null(inflight, secp256k1_ecdsa_signature, lease_commit_sig); - else - inflight->lease_commit_sig = NULL; inflight->lease_chan_max_msat = lease_chan_max_msat; inflight->lease_chan_max_ppt = lease_chan_max_ppt; diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index a01366d5a1a8..3d51d0203a3f 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -214,10 +214,7 @@ void delay_then_reconnect(struct channel *channel, u32 seconds_delay, d = tal(channel, struct delayed_reconnect); d->channel = channel; d->seconds_delayed = seconds_delay; - if (addrhint) - d->addrhint = tal_dup(d, struct wireaddr_internal, addrhint); - else - d->addrhint = NULL; + d->addrhint = tal_dup_or_null(d, struct wireaddr_internal, addrhint); log_debug(channel->log, "Will try reconnect in %u seconds", seconds_delay); diff --git a/lightningd/htlc_end.c b/lightningd/htlc_end.c index d2ad10257ebf..129ba638c605 100644 --- a/lightningd/htlc_end.c +++ b/lightningd/htlc_end.c @@ -144,10 +144,7 @@ struct htlc_in *new_htlc_in(const tal_t *ctx, hin->payment_hash = *payment_hash; hin->status = NULL; hin->fail_immediate = fail_immediate; - if (shared_secret) - hin->shared_secret = tal_dup(hin, struct secret, shared_secret); - else - hin->shared_secret = NULL; + hin->shared_secret = tal_dup_or_null(hin, struct secret, shared_secret); if (blinding) { hin->blinding = tal_dup(hin, struct pubkey, blinding); hin->blinding_ss = *blinding_ss; @@ -303,10 +300,7 @@ struct htlc_out *new_htlc_out(const tal_t *ctx, hout->preimage = NULL; hout->timeout = NULL; - if (blinding) - hout->blinding = tal_dup(hout, struct pubkey, blinding); - else - hout->blinding = NULL; + hout->blinding = tal_dup_or_null(hout, struct pubkey, blinding); hout->am_origin = am_origin; if (am_origin) { hout->partid = partid; diff --git a/lightningd/log.c b/lightningd/log.c index 292eba39c66c..0d975934cf11 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -306,11 +306,8 @@ new_log(const tal_t *ctx, struct log_book *record, * by log entries, too */ log->prefix = log_prefix_new(log->lr, take(tal_vfmt(NULL, fmt, ap))); va_end(ap); - if (default_node_id) - log->default_node_id = tal_dup(log, struct node_id, + log->default_node_id = tal_dup_or_null(log, struct node_id, default_node_id); - else - log->default_node_id = NULL; /* Initialized on first use */ log->print_level = NULL; diff --git a/lightningd/pay.c b/lightningd/pay.c index 00fdafdf8a16..346cc8ecca3f 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -495,11 +495,8 @@ remote_routing_failure(const tal_t *ctx, routing_failure->failcode = failcode; routing_failure->msg = tal_dup_talarr(routing_failure, u8, failuremsg); - if (erring_node != NULL) - routing_failure->erring_node = - tal_dup(routing_failure, struct node_id, erring_node); - else - routing_failure->erring_node = NULL; + routing_failure->erring_node = + tal_dup_or_null(routing_failure, struct node_id, erring_node); if (erring_channel != NULL) { routing_failure->erring_channel = tal_dup( @@ -1081,10 +1078,8 @@ send_payment_core(struct lightningd *ld, payment->payment_hash = *rhash; payment->partid = partid; payment->groupid = group; - if (destination) - payment->destination = tal_dup(payment, struct node_id, destination); - else - payment->destination = NULL; + payment->destination = tal_dup_or_null(payment, struct node_id, + destination); payment->status = PAYMENT_PENDING; payment->msatoshi = msat; payment->msatoshi_sent = first_hop->amount; @@ -1092,14 +1087,8 @@ send_payment_core(struct lightningd *ld, payment->timestamp = time_now().ts.tv_sec; payment->payment_preimage = NULL; payment->path_secrets = tal_steal(payment, path_secrets); - if (route_nodes) - payment->route_nodes = tal_steal(payment, route_nodes); - else - payment->route_nodes = NULL; - if (route_channels) - payment->route_channels = tal_steal(payment, route_channels); - else - payment->route_channels = NULL; + payment->route_nodes = tal_steal(payment, route_nodes); + payment->route_channels = tal_steal(payment, route_channels); payment->failonion = NULL; if (label != NULL) payment->label = tal_strdup(payment, label); @@ -1109,10 +1098,8 @@ send_payment_core(struct lightningd *ld, payment->invstring = tal_strdup(payment, invstring); else payment->invstring = NULL; - if (local_offer_id) - payment->local_offer_id = tal_dup(payment, struct sha256, local_offer_id); - else - payment->local_offer_id = NULL; + payment->local_offer_id = tal_dup_or_null(payment, struct sha256, + local_offer_id); /* We write this into db when HTLC is actually sent. */ wallet_payment_setup(ld->wallet, payment); diff --git a/lightningd/subd.c b/lightningd/subd.c index a42849b48805..bbd04d40cf61 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -753,10 +753,7 @@ static struct subd *new_subd(struct lightningd *ld, list_head_init(&sd->reqs); sd->channel = channel; sd->rcvd_version = false; - if (node_id) - sd->node_id = tal_dup(sd, struct node_id, node_id); - else - sd->node_id = NULL; + sd->node_id = tal_dup_or_null(sd, struct node_id, node_id); /* conn actually owns daemon: we die when it does. */ sd->conn = io_new_conn(ld, msg_fd, msg_setup, sd); diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 24497197553f..869c772aec3c 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -929,11 +929,8 @@ new_tracked_output(struct tracked_output ***outs, if (htlc) out->htlc = *htlc; out->wscript = tal_steal(out, wscript); - if (remote_htlc_sig) - out->remote_htlc_sig = tal_dup(out, struct bitcoin_signature, + out->remote_htlc_sig = tal_dup_or_null(out, struct bitcoin_signature, remote_htlc_sig); - else - out->remote_htlc_sig = NULL; tal_arr_expand(outs, out); diff --git a/plugins/pay.c b/plugins/pay.c index b454d7a8c0a0..9c0b4dae7232 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -2361,9 +2361,7 @@ static struct command_result *json_paymod(struct command *cmd, feature_offered(b11->features, OPT_VAR_ONION); p->payment_hash = tal_dup(p, struct sha256, &b11->payment_hash); p->payment_secret = - b11->payment_secret - ? tal_dup(p, struct secret, b11->payment_secret) - : NULL; + tal_dup_or_null(p, struct secret, b11->payment_secret); p->routes = tal_steal(p, b11->routes); p->min_final_cltv_expiry = b11->min_final_cltv_expiry; p->features = tal_steal(p, b11->features); From 425a7af512eac9a1300564953599b303313cf5fa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 10:39:56 +1030 Subject: [PATCH 0188/1530] common/coin_mvt: clean up API a little. 1. tal_strndup(.., str, strlen(str)) == tal_strdup() 2. tal_strdup also takes(), so document that. 3. Avoid passing 'struct sha256' on the stack: use ptr. 4. Generally, structures shouldn't keep pointers to things they don't own. In this case, mvt->node_id. 5. Make payment_hash a pointer, since NULL is more natural than an all-zero hash. And add NON_NULL_ARGS() to the functions; it's cumbersome, but make it fairly clear what params are optional. Signed-off-by: Rusty Russell --- common/coin_mvt.c | 76 +++++++++++++-------------- common/coin_mvt.h | 52 +++++++++++------- lightningd/coin_mvts.c | 8 +-- lightningd/notification.c | 4 +- onchaind/test/run-grind_feerate-bug.c | 24 ++++++--- onchaind/test/run-grind_feerate.c | 24 ++++++--- wallet/test/run-wallet.c | 13 +++-- 7 files changed, 118 insertions(+), 83 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 6f981e7fe8b7..68dd50e659b0 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -54,19 +55,19 @@ enum mvt_tag *new_tag_arr(const tal_t *ctx, enum mvt_tag tag) struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, const struct channel_id *cid, - struct sha256 payment_hash, - u64 *part_id, + const struct sha256 *payment_hash TAKES, + u64 *part_id TAKES, struct amount_msat amount, - enum mvt_tag *tags STEALS, + const enum mvt_tag *tags TAKES, bool is_credit, struct amount_msat fees) { struct channel_coin_mvt *mvt = tal(ctx, struct channel_coin_mvt); mvt->chan_id = *cid; - mvt->payment_hash = tal_dup(mvt, struct sha256, &payment_hash); - mvt->part_id = part_id; - mvt->tags = tal_steal(mvt, tags); + mvt->payment_hash = tal_dup_or_null(mvt, struct sha256, payment_hash); + mvt->part_id = tal_dup_or_null(mvt, u64, part_id); + mvt->tags = tal_dup_talarr(mvt, enum mvt_tag, tags); if (is_credit) { mvt->credit = amount; @@ -82,12 +83,12 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, } static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, - const char *account_name, + const char *account_name TAKES, const struct bitcoin_txid *tx_txid, const struct bitcoin_outpoint *outpoint, const struct sha256 *payment_hash TAKES, u32 blockheight, - enum mvt_tag *tags STEALS, + enum mvt_tag *tags TAKES, struct amount_msat amount, bool is_credit, struct amount_sat output_val) @@ -95,8 +96,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, struct chain_coin_mvt *mvt = tal(ctx, struct chain_coin_mvt); if (account_name) - mvt->account_name = tal_strndup(mvt, account_name, - strlen(account_name)); + mvt->account_name = tal_strdup(mvt, account_name); else mvt->account_name = NULL; @@ -108,7 +108,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, mvt->payment_hash = tal_dup_or_null(mvt, struct sha256, payment_hash); mvt->blockheight = blockheight; - mvt->tags = tal_steal(mvt, tags); + mvt->tags = tal_dup_talarr(mvt, enum mvt_tag, tags); if (is_credit) { mvt->credit = amount; @@ -128,7 +128,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, const struct sha256 *payment_hash TAKES, u32 blockheight, - enum mvt_tag *tags, + enum mvt_tag *tags TAKES, struct amount_sat amt_sat, bool is_credit) { @@ -154,7 +154,8 @@ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, NULL, spend_txid, outpoint, NULL, - blockheight, new_tag_arr(ctx, tag), + blockheight, + take(new_tag_arr(NULL, tag)), amount, false); } @@ -166,7 +167,8 @@ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, NULL, NULL, outpoint, NULL, - blockheight, new_tag_arr(ctx, tag), + blockheight, + take(new_tag_arr(NULL, tag)), amount, true); } @@ -179,7 +181,7 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, { return new_chain_coin_mvt(ctx, NULL, txid, out, NULL, blockheight, - new_tag_arr(ctx, CHANNEL_CLOSE), + take(new_tag_arr(NULL, CHANNEL_CLOSE)), amount, false, output_val); } @@ -196,7 +198,7 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, struct chain_coin_mvt *mvt; mvt = new_chain_coin_mvt(ctx, NULL, NULL, out, NULL, blockheight, - new_tag_arr(ctx, CHANNEL_OPEN), amount, + take(new_tag_arr(NULL, CHANNEL_OPEN)), amount, true, output_val); mvt->account_name = type_to_string(mvt, struct channel_id, chan_id); @@ -214,12 +216,12 @@ struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - struct sha256 *payment_hash) + const struct sha256 *payment_hash) { return new_chain_coin_mvt_sat(ctx, NULL, NULL, outpoint, payment_hash, blockheight, - new_tag_arr(ctx, HTLC_FULFILL), + take(new_tag_arr(NULL, HTLC_FULFILL)), amount, true); } @@ -228,14 +230,14 @@ struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - struct sha256 *payment_hash) + const struct sha256 *payment_hash) { /* An onchain htlc fulfillment to peer is a *deposit* of * that output into their (external) account */ return new_chain_coin_mvt_sat(ctx, EXTERNAL, NULL, outpoint, payment_hash, blockheight, - new_tag_arr(ctx, HTLC_FULFILL), + take(new_tag_arr(NULL, HTLC_FULFILL)), amount, false); } @@ -248,7 +250,7 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, { return new_chain_coin_mvt(ctx, EXTERNAL, txid, outpoint, NULL, blockheight, - new_tag_arr(ctx, tag), + take(new_tag_arr(NULL, tag)), AMOUNT_MSAT(0), true, amount); } @@ -261,7 +263,7 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, return new_chain_coin_mvt(ctx, EXTERNAL, NULL, outpoint, NULL, blockheight, - new_tag_arr(ctx, tag), + take(new_tag_arr(NULL, tag)), AMOUNT_MSAT(0), true, amount); } @@ -273,7 +275,7 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, WALLET, NULL, outpoint, NULL, - blockheight, new_tag_arr(ctx, tag), + blockheight, take(new_tag_arr(NULL, tag)), amount, true); } @@ -286,7 +288,7 @@ struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, { return new_chain_coin_mvt_sat(ctx, WALLET, spend_txid, outpoint, NULL, - blockheight, new_tag_arr(ctx, tag), + blockheight, take(new_tag_arr(NULL, tag)), amount, false); } @@ -300,7 +302,7 @@ struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, return new_chain_coin_mvt_sat(ctx, account_name, txid, outpoint, NULL, blockheight, - new_tag_arr(ctx, PENALTY), + take(new_tag_arr(NULL, PENALTY)), amount, false); } @@ -310,27 +312,22 @@ struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, enum mvt_tag tag, bool is_credit) { - struct sha256 empty_hash; - /* Use a 0'd out payment hash */ - memset(&empty_hash, 0, sizeof(empty_hash)); - - return new_channel_coin_mvt(ctx, cid, empty_hash, + return new_channel_coin_mvt(ctx, cid, NULL, NULL, amount, - new_tag_arr(ctx, tag), is_credit, + take(new_tag_arr(NULL, tag)), is_credit, AMOUNT_MSAT(0)); } struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, - const char *bip173_name, + const char *bip173_name TAKES, u32 timestamp, struct node_id *node_id) { struct coin_mvt *mvt = tal(ctx, struct coin_mvt); - mvt->account_id = tal_strndup(mvt, chain_mvt->account_name, - strlen(chain_mvt->account_name)); - mvt->bip173_name = tal_strndup(mvt, bip173_name, strlen(bip173_name)); + mvt->account_id = tal_strdup(mvt, chain_mvt->account_name); + mvt->bip173_name = tal_strdup(mvt, bip173_name); mvt->type = CHAIN_MVT; mvt->id.tx_txid = chain_mvt->tx_txid; @@ -354,14 +351,15 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct channel_coin_mvt *chan_mvt, - const char *bip173_name, - u32 timestamp, struct node_id *node_id) + const char *bip173_name TAKES, + u32 timestamp, + const struct node_id *node_id TAKES) { struct coin_mvt *mvt = tal(ctx, struct coin_mvt); mvt->account_id = type_to_string(mvt, struct channel_id, &chan_mvt->chan_id); - mvt->bip173_name = tal_strndup(mvt, bip173_name, strlen(bip173_name)); + mvt->bip173_name = tal_strdup(mvt, bip173_name); mvt->type = CHANNEL_MVT; mvt->id.payment_hash = chan_mvt->payment_hash; mvt->id.part_id = chan_mvt->part_id; @@ -377,7 +375,7 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, /* channel movements don't have a blockheight */ mvt->blockheight = 0; mvt->version = COIN_MVT_VERSION; - mvt->node_id = node_id; + mvt->node_id = tal_dup(mvt, struct node_id, node_id); return mvt; } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 84fedaff1167..75459f773ff4 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -133,32 +133,36 @@ enum mvt_tag *new_tag_arr(const tal_t *ctx, enum mvt_tag tag); struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx, const struct channel_id *cid, - struct sha256 payment_hash, - u64 *part_id, + const struct sha256 *payment_hash TAKES, + u64 *part_id TAKES, struct amount_msat amount, - enum mvt_tag *tags STEALS, + const enum mvt_tag *tags TAKES, bool is_credit, - struct amount_msat fees); + struct amount_msat fees) + NON_NULL_ARGS(2); struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, const struct bitcoin_txid *spend_txid, u32 blockheight, struct amount_sat amount, - enum mvt_tag tag); + enum mvt_tag tag) + NON_NULL_ARGS(2, 3); struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - enum mvt_tag tag); + enum mvt_tag tag) + NON_NULL_ARGS(2); struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *out, u32 blockheight, const struct amount_msat amount, - const struct amount_sat output_val); + const struct amount_sat output_val) + NON_NULL_ARGS(2, 3); struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct channel_id *chan_id, @@ -167,69 +171,81 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct amount_msat amount, const struct amount_sat output_val, bool is_opener, - bool is_leased); + bool is_leased) + NON_NULL_ARGS(2, 3); struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - struct sha256 *payment_hash); + const struct sha256 *payment_hash) + NON_NULL_ARGS(2, 5); struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - struct sha256 *payment_hash); + const struct sha256 *payment_hash) + NON_NULL_ARGS(2, 5); struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - enum mvt_tag tag); + enum mvt_tag tag) + NON_NULL_ARGS(2); struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, const struct bitcoin_txid *spend_txid, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - enum mvt_tag tag); + enum mvt_tag tag) + NON_NULL_ARGS(2, 3); struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, const struct bitcoin_txid *txid, u32 blockheight, struct amount_sat amount, - enum mvt_tag tag); + enum mvt_tag tag) + NON_NULL_ARGS(2, 3); struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, struct amount_sat amount, - enum mvt_tag tag); + enum mvt_tag tag) + NON_NULL_ARGS(2); struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, const char *account_name, const struct bitcoin_txid *txid, const struct bitcoin_outpoint *outpoint, u32 blockheight, - struct amount_sat amount); + struct amount_sat amount) + NON_NULL_ARGS(3, 4); struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, const struct channel_id *cid, struct amount_msat amount, enum mvt_tag tag, - bool is_credit); + bool is_credit) + NON_NULL_ARGS(2); struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, const char *bip173_name, u32 timestamp, - struct node_id *node_id); + struct node_id *node_id) + NON_NULL_ARGS(2, 3); struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct channel_coin_mvt *chan_mvt, const char *bip173_name, - u32 timestamp, struct node_id *node_id); + u32 timestamp, + const struct node_id *node_id) + NON_NULL_ARGS(2, 3, 5); const char *mvt_type_str(enum mvt_type type); const char *mvt_tag_str(enum mvt_tag tag); diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index a77de436688e..098d2ee82bae 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -36,7 +36,7 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx, struct channel *channel) { return new_channel_coin_mvt(ctx, &channel->cid, - hin->payment_hash, NULL, + &hin->payment_hash, NULL, hin->msat, new_tag_arr(ctx, INVOICE), true, AMOUNT_MSAT(0)); } @@ -55,7 +55,7 @@ struct channel_coin_mvt *new_channel_mvt_routed_hin(const tal_t *ctx, return NULL; return new_channel_coin_mvt(ctx, &channel->cid, - hin->payment_hash, NULL, + &hin->payment_hash, NULL, hin->msat, new_tag_arr(ctx, ROUTED), true, fees_collected); } @@ -65,7 +65,7 @@ struct channel_coin_mvt *new_channel_mvt_invoice_hout(const tal_t *ctx, struct channel *channel) { return new_channel_coin_mvt(ctx, &channel->cid, - hout->payment_hash, &hout->partid, + &hout->payment_hash, &hout->partid, hout->msat, new_tag_arr(ctx, INVOICE), false, hout->fees); } @@ -75,7 +75,7 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, struct channel *channel) { return new_channel_coin_mvt(ctx, &channel->cid, - hout->payment_hash, NULL, + &hout->payment_hash, NULL, hout->msat, new_tag_arr(ctx, ROUTED), false, hout->fees); diff --git a/lightningd/notification.c b/lightningd/notification.c index 8161aa83f42a..1b85e3ed4ea1 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -452,7 +452,9 @@ static void json_mvt_id(struct json_stream *stream, enum mvt_type mvt_type, json_add_sha256(stream, "payment_hash", id->payment_hash); return; case CHANNEL_MVT: - json_add_sha256(stream, "payment_hash", id->payment_hash); + /* push funding / leases don't have a payment_hash */ + if (id->payment_hash) + json_add_sha256(stream, "payment_hash", id->payment_hash); if (id->part_id) json_add_u64(stream, "part_id", *id->part_id); return; diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 76ae0744d5fb..e14a2202b207 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -119,14 +119,16 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, const struct amount_msat amount UNNEEDED, - const struct amount_sat output_val UNNEEDED) + const struct amount_sat output_val) + { fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } /* Generated stub for new_coin_external_deposit */ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); } /* Generated stub for new_coin_external_spend */ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, @@ -134,35 +136,40 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit */ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } /* Generated stub for new_onchain_htlc_deposit */ struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - struct sha256 *payment_hash UNNEEDED) + const struct sha256 *payment_hash) + { fprintf(stderr, "new_onchain_htlc_deposit called!\n"); abort(); } /* Generated stub for new_onchain_htlc_withdraw */ struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - struct sha256 *payment_hash UNNEEDED) + const struct sha256 *payment_hash) + { fprintf(stderr, "new_onchain_htlc_withdraw called!\n"); abort(); } /* Generated stub for new_onchaind_deposit */ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_onchaind_deposit called!\n"); abort(); } /* Generated stub for new_onchaind_withdraw */ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, @@ -170,7 +177,8 @@ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *spend_txid UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index d25893b3d089..23454c0aac00 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -142,14 +142,16 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, const struct amount_msat amount UNNEEDED, - const struct amount_sat output_val UNNEEDED) + const struct amount_sat output_val) + { fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } /* Generated stub for new_coin_external_deposit */ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); } /* Generated stub for new_coin_external_spend */ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, @@ -157,35 +159,40 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit */ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } /* Generated stub for new_onchain_htlc_deposit */ struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - struct sha256 *payment_hash UNNEEDED) + const struct sha256 *payment_hash) + { fprintf(stderr, "new_onchain_htlc_deposit called!\n"); abort(); } /* Generated stub for new_onchain_htlc_withdraw */ struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - struct sha256 *payment_hash UNNEEDED) + const struct sha256 *payment_hash) + { fprintf(stderr, "new_onchain_htlc_withdraw called!\n"); abort(); } /* Generated stub for new_onchaind_deposit */ struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_onchaind_deposit called!\n"); abort(); } /* Generated stub for new_onchaind_withdraw */ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, @@ -193,7 +200,8 @@ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *spend_txid UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 52617a9e3f2f..e452202ee457 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -138,13 +138,12 @@ bool fromwire_custommsg_in(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 /* Generated stub for fromwire_gossipd_get_stripped_cupdate_reply */ bool fromwire_gossipd_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **stripped_update UNNEEDED) { fprintf(stderr, "fromwire_gossipd_get_stripped_cupdate_reply called!\n"); abort(); } -u8 *towire_hsmd_new_channel(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 dbid UNNEEDED) -{ fprintf(stderr, "towire_hsmd_new_channel called!\n"); abort(); } -bool fromwire_hsmd_new_channel_reply(const void *p UNNEEDED) -{ fprintf(stderr, "fromwire_hsmd_new_channel_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED) { fprintf(stderr, "fromwire_hsmd_get_output_scriptpubkey_reply called!\n"); abort(); } +/* Generated stub for fromwire_hsmd_new_channel_reply */ +bool fromwire_hsmd_new_channel_reply(const void *p UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_new_channel_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_sign_commitment_tx_reply */ bool fromwire_hsmd_sign_commitment_tx_reply(const void *p UNNEEDED, struct bitcoin_signature *sig UNNEEDED) { fprintf(stderr, "fromwire_hsmd_sign_commitment_tx_reply called!\n"); abort(); } @@ -446,7 +445,8 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, u32 blockheight UNNEEDED, struct amount_sat amount UNNEEDED, - enum mvt_tag tag UNNEEDED) + enum mvt_tag tag) + { fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) @@ -750,6 +750,9 @@ u8 *towire_gossipd_get_stripped_cupdate(const tal_t *ctx UNNEEDED, const struct /* Generated stub for towire_hsmd_get_output_scriptpubkey */ u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED) { fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); } +/* Generated stub for towire_hsmd_new_channel */ +u8 *towire_hsmd_new_channel(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 dbid UNNEEDED) +{ fprintf(stderr, "towire_hsmd_new_channel called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_commitment_tx */ u8 *towire_hsmd_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_commitment_tx called!\n"); abort(); } From ae4669f77f53d8c524843229066b10be109eba3c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 10:39:58 +1030 Subject: [PATCH 0189/1530] common/coin_mvt: make it clear we're using Lightning HRP not BIP 173. Signed-off-by: Rusty Russell --- common/coin_mvt.c | 8 ++++---- common/coin_mvt.h | 8 +++++--- lightningd/notification.c | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 68dd50e659b0..f523d1b26feb 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -320,14 +320,14 @@ struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, - const char *bip173_name TAKES, + const char *hrp_name TAKES, u32 timestamp, struct node_id *node_id) { struct coin_mvt *mvt = tal(ctx, struct coin_mvt); mvt->account_id = tal_strdup(mvt, chain_mvt->account_name); - mvt->bip173_name = tal_strdup(mvt, bip173_name); + mvt->hrp_name = tal_strdup(mvt, hrp_name); mvt->type = CHAIN_MVT; mvt->id.tx_txid = chain_mvt->tx_txid; @@ -351,7 +351,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct channel_coin_mvt *chan_mvt, - const char *bip173_name TAKES, + const char *hrp_name TAKES, u32 timestamp, const struct node_id *node_id TAKES) { @@ -359,7 +359,7 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->account_id = type_to_string(mvt, struct channel_id, &chan_mvt->chan_id); - mvt->bip173_name = tal_strdup(mvt, bip173_name); + mvt->hrp_name = tal_strdup(mvt, hrp_name); mvt->type = CHANNEL_MVT; mvt->id.payment_hash = chan_mvt->payment_hash; mvt->id.part_id = chan_mvt->part_id; diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 75459f773ff4..6905c3257461 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -96,7 +96,9 @@ struct mvt_id { struct coin_mvt { /* name of 'account': wallet, external, */ const char *account_id; - const char *bip173_name; + + /* Chain name: BIP 173, except signet lightning-style: tbs not tb */ + const char *hrp_name; /* type of movement: channel or chain */ enum mvt_type type; @@ -235,14 +237,14 @@ struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, const struct chain_coin_mvt *chain_mvt, - const char *bip173_name, + const char *hrp_name, u32 timestamp, struct node_id *node_id) NON_NULL_ARGS(2, 3); struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct channel_coin_mvt *chan_mvt, - const char *bip173_name, + const char *hrp_name, u32 timestamp, const struct node_id *node_id) NON_NULL_ARGS(2, 3, 5); diff --git a/lightningd/notification.c b/lightningd/notification.c index 1b85e3ed4ea1..a93a4d6e5202 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -495,7 +495,7 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_add_null(stream, "blockheight"); } json_add_u32(stream, "timestamp", mvt->timestamp); - json_add_string(stream, "coin_type", mvt->bip173_name); + json_add_string(stream, "coin_type", mvt->hrp_name); json_object_end(stream); } From b659fbbdf72092a7f9d9d5381d417a3c1a00c6c5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Dec 2021 14:11:15 +1030 Subject: [PATCH 0190/1530] pytest: further deflake test_funding_push. I also got an error under CI; it seems the sleep() was insufficient. So try adding a sleep inside the check_coin_moves, which should cover everyone. ``` acct_moves = acct_moves[number_moves:] else: > if not move_matches(m, acct_moves[0]): E IndexError: list index out of range ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 2 -- tests/utils.py | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index a7ad414a2910..b50dee021c47 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1077,8 +1077,6 @@ def test_funding_push(node_factory, bitcoind, chainparams): assert funds['channel_sat'] + push_sat == funds['channel_total_sat'] chanid = first_channel_id(l2, l1) - # give the file write a second - time.sleep(1) channel_mvts_1 = [ {'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tags': ['channel_open', 'opener']}, {'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tags': ['pushed'], 'fees': '0msat'}, diff --git a/tests/utils.py b/tests/utils.py index 8766ee53f117..d10fdada3b4c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -3,6 +3,7 @@ import bitstring from pyln.client import Millisatoshi from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND +import time EXPERIMENTAL_FEATURES = env("EXPERIMENTAL_FEATURES", "0") == "1" COMPAT = env("COMPAT", "1") == "1" @@ -108,6 +109,19 @@ def check_balance_snaps(n, expected_bals): def check_coin_moves(n, account_id, expected_moves, chainparams): moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] + # moves can lag; wait for a few seconds if we don't have correct number. + # then move on: we'll get details below. + expected_count = 0 + for m in enumerate(expected_moves): + if isinstance(m, list): + expected_count += len(m) + else: + expected_count += 1 + + if len(moves) != expected_count: + time.sleep(5) + moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] + node_id = n.info['id'] acct_moves = [m for m in moves if m['account_id'] == account_id] for mv in acct_moves: From 019b31c522d729c45931a904e827dab954eaaeaa Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sat, 1 Jan 2022 14:05:58 +0100 Subject: [PATCH 0191/1530] options: only allow one DNS announcement --- lightningd/options.c | 25 +++++++++++++++++++++++++ tests/test_gossip.py | 23 ++++++++++++++--------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index a1f263a0e139..e851bc67fc39 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -196,6 +196,23 @@ static char *opt_set_accept_extra_tlv_types(const char *arg, } #endif +#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ +/* Returns the number of wireaddr types already announced */ +static size_t num_announced_types(enum wire_addr_type type, struct lightningd *ld) +{ + size_t num = 0; + for (size_t i = 0; i < tal_count(ld->proposed_wireaddr); i++) { + if (ld->proposed_wireaddr[i].itype != ADDR_INTERNAL_WIREADDR) + continue; + if (ld->proposed_wireaddr[i].u.wireaddr.type != type) + continue; + if (ld->proposed_listen_announce[i] & ADDR_ANNOUNCE) + num++; + } + return num; +} +#endif + static char *opt_add_addr_withtype(const char *arg, struct lightningd *ld, enum addr_listen_announce ala, @@ -242,6 +259,14 @@ static char *opt_add_addr_withtype(const char *arg, #if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ /* Add ADDR_TYPE_DNS to announce DNS hostnames */ if (is_dnsaddr(address) && ala & ADDR_ANNOUNCE) { + /* BOLT-hostnames #7: + * The origin node: + * ... + * - MUST NOT announce more than one `type 5` DNS hostname. + */ + if (num_announced_types(ADDR_TYPE_DNS, ld) > 0) { + return tal_fmt(NULL, "Only one DNS can be announced"); + } memset(&wi, 0, sizeof(wi)); wi.itype = ADDR_INTERNAL_WIREADDR; wi.u.wireaddr.type = ADDR_TYPE_DNS; diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 7696b2642490..52ba9e120a72 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -114,7 +114,6 @@ def test_announce_address(node_factory, bitcoind): opts = {'disable-dns': None, 'announce-addr': ['4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion', '1.2.3.4:1234', - 'localhost:1235', 'example.com:1236', '::'], 'log-level': 'io', @@ -151,24 +150,21 @@ def test_announce_address(node_factory, bitcoind): # Also expect the address descriptor types to be sorted! # BOLT #7: # - MUST place address descriptors in ascending order. - l1.daemon.wait_for_log(r"\[OUT\] 0101.*0063" + l1.daemon.wait_for_log(r"\[OUT\] 0101.*0056" "010102030404d2" # IPv4 01 1.2.3.4:1234 "017f000001...." # IPv4 01 127.0.0.1:wxyz "0200000000000000000000000000000000...." # IPv6 02 ::: "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba5023003...." # TORv3 04 - "05096c6f63616c686f737404d3" # DNS 05 len localhost:1235 "050b6578616d706c652e636f6d04d4") # DNS 05 len example.com:1236 # Check other node can parse these (make sure it has digested msg) wait_for(lambda: 'addresses' in l2.rpc.listnodes(l1.info['id'])['nodes'][0]) addresses = l2.rpc.listnodes(l1.info['id'])['nodes'][0]['addresses'] addresses_dns = [address for address in addresses if address['type'] == 'dns'] - assert len(addresses) == 6 - assert len(addresses_dns) == 2 - assert addresses_dns[0]['address'] == 'localhost' - assert addresses_dns[0]['port'] == 1235 - assert addresses_dns[1]['address'] == 'example.com' - assert addresses_dns[1]['port'] == 1236 + assert len(addresses) == 5 + assert len(addresses_dns) == 1 + assert addresses_dns[0]['address'] == 'example.com' + assert addresses_dns[0]['port'] == 1236 @unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") @@ -236,6 +232,15 @@ def test_announce_and_connect_via_dns(node_factory, bitcoind): l4.rpc.connect(l1.info['id']) +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") +@pytest.mark.developer("gossip without DEVELOPER=1 is slow") +def test_only_announce_one_dns(node_factory, bitcoind): + # and test that we can't announce more than one DNS address + l1 = node_factory.get_node(may_fail=True, expect_fail=True, + options={'announce-addr': ['localhost.localdomain:12345', 'example.com:12345']}) + assert l1.daemon.is_in_stderr("Only one DNS can be announced") + + @pytest.mark.developer("needs DEVELOPER=1") def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): # Updates get backdated 5 seconds with --dev-fast-gossip. From dded47e33273f32b441fe2539307173055048252 Mon Sep 17 00:00:00 2001 From: manreo <35897350+manreo@users.noreply.github.com> Date: Tue, 4 Jan 2022 10:43:25 -0500 Subject: [PATCH 0192/1530] LightningRpc should accept pathlib Path Not sure why socket does not accept pathlib, but just added some code to transform the path to string. It is not the most pretty code, but better than these: https://stackoverflow.com/questions/58647584/how-to-test-if-object-is-a-pathlib-path --- contrib/pyln-client/pyln/client/lightning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 9d37fb86e9f6..b0829e852741 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -223,7 +223,7 @@ class UnixSocket(object): """ def __init__(self, path: str): - self.path = path + self.path = str(path) if 'pathlib' in str(type(path)) else path self.sock: Optional[socket.SocketType] = None self.connect() From 73bf4b2c2e0e173d0b3193e337dc9dbec7e0e16c Mon Sep 17 00:00:00 2001 From: manreo <35897350+manreo@users.noreply.github.com> Date: Tue, 4 Jan 2022 17:55:31 -0500 Subject: [PATCH 0193/1530] Update lightning.py --- contrib/pyln-client/pyln/client/lightning.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index b0829e852741..b76ad28437b7 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -223,14 +223,14 @@ class UnixSocket(object): """ def __init__(self, path: str): - self.path = str(path) if 'pathlib' in str(type(path)) else path + self.path = path self.sock: Optional[socket.SocketType] = None self.connect() def connect(self) -> None: try: self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - self.sock.connect(self.path) + self.sock.connect(str(self.path)) except OSError as e: self.close() From 52b1f5a8f454caf4b09ff14a432403ec94297609 Mon Sep 17 00:00:00 2001 From: johnongit Date: Sat, 11 Dec 2021 15:41:08 +0100 Subject: [PATCH 0194/1530] fix dockerfile --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 3f6713da7774..f6d570533b76 100644 --- a/Dockerfile +++ b/Dockerfile @@ -79,7 +79,9 @@ RUN git clone --recursive /tmp/lightning . && \ ARG DEVELOPER=0 ENV PYTHON_VERSION=3 -RUN pip3 install mrkd +RUN apt-get install -y --no-install-recommends python3-dev +RUN pip3 install -U pip && pip3 install -r requirements.lock + RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install FROM debian:buster-slim as final From 35eaaf8e63449e737c8a340070bfbcd57bd12540 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 12 Jan 2022 21:36:56 +0100 Subject: [PATCH 0195/1530] cli: change the redefine logic to read from the stdin Procedure redefinition is a very cool feature but when other libraries redefine the same function can be very tricky to avoid compilation error. This PR proposed a change of logic and use a customizzation function definition to read from the stdin, so we can avoid future error at compile time. However, we could be check also the os or compiler that cause the error and redefine the missing function. Changelog-None: Fixed compilation error due redefinition procedure. Signed-off-by: Vincenzo Palazzo --- cli/config_cli.h | 15 +++++++++++++++ cli/lightning-cli.c | 8 ++++---- cli/test/config_test.h | 8 ++++++++ cli/test/run-human-mode.c | 4 ++-- cli/test/run-large-input.c | 3 ++- cli/test/run-remove-hint.c | 3 ++- 6 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 cli/config_cli.h create mode 100644 cli/test/config_test.h diff --git a/cli/config_cli.h b/cli/config_cli.h new file mode 100644 index 000000000000..6faeb3606992 --- /dev/null +++ b/cli/config_cli.h @@ -0,0 +1,15 @@ +#ifndef LIGHTNING_CLI_CONFIG_CLI_H +#define LIGHTNING_CLI_CONFIG_CLI_H + +#include "config.h" +#include + +#ifndef CLN_TEST +/* Redefinition procedure is a very cool feature, but + if we try to redefine a procedure that is already + redefined somewhere (like read in alpine) we can have + tricky compilation error */ +#define cli_read read +#endif + +#endif /* LIGHTNING_CLI_CONFIG_CLI_H */ diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index 743d8b8f0a57..06e47f2b849c 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -2,6 +2,7 @@ * Helper to submit via JSON-RPC and get back response. */ #include "config.h" +#include "config_cli.h" #include #include #include @@ -15,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -386,7 +386,7 @@ static size_t read_nofail(int fd, void *buf, size_t len) ssize_t i; assert(len > 0); - i = read(fd, buf, len); + i = cli_read(fd, buf, len); if (i == 0) errx(ERROR_TALKING_TO_LIGHTNINGD, "reading response: socket closed"); @@ -572,7 +572,7 @@ static void enable_notifications(int fd) memset(rbuf, 0, sizeof(rbuf)); while (!strends(rbuf, "\n\n")) { size_t len = strlen(rbuf); - if (read(fd, rbuf + len, sizeof(rbuf) - len) < 0) + if (cli_read(fd, rbuf + len, sizeof(rbuf) - len) < 0) err(ERROR_TALKING_TO_LIGHTNINGD, "Reading enable response"); } @@ -756,7 +756,7 @@ int main(int argc, char *argv[]) while (parserr <= 0) { /* Read more if parser says, or we have 0 tokens. */ if (parserr == 0 || parserr == JSMN_ERROR_PART) { - ssize_t i = read(fd, resp + off, tal_bytelen(resp) - 1 - off); + ssize_t i = cli_read(fd, resp + off, tal_bytelen(resp) - 1 - off); if (i == 0) errx(ERROR_TALKING_TO_LIGHTNINGD, "reading response: socket closed"); diff --git a/cli/test/config_test.h b/cli/test/config_test.h new file mode 100644 index 000000000000..288751118bc9 --- /dev/null +++ b/cli/test/config_test.h @@ -0,0 +1,8 @@ +#ifndef LIGHTNING_CLI_TEST_CONFIG_TEST_H +#define LIGHTNING_CLI_TEST_CONFIG_TEST_H + +#include "config.h" + +#define CLN_TEST 1 + +#endif /* LIGHTNING_CLI_TEST_CONFIG_TEST_H */ diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index bf7c4a25f82a..1548aa404e44 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -1,4 +1,5 @@ #include "config.h" +#include "config_test.h" #include #include #include @@ -18,7 +19,7 @@ int test_printf(const char *format, ...); int test_chdir(const char *path); #define main test_main -#define read test_read +#define cli_read test_read #define socket test_socket #define connect test_connect #define getpid test_getpid @@ -174,4 +175,3 @@ int main(int argc UNUSED, char *argv[]) common_shutdown(); return 0; } - diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index 40c8d67f91e9..507b4e01dd6a 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -1,4 +1,5 @@ #include "config.h" +#include "config_test.h" #include #include #include @@ -18,7 +19,7 @@ int test_printf(const char *format, ...); int test_chdir(const char *path); #define main test_main -#define read test_read +#define cli_read test_read #define socket test_socket #define connect test_connect #define getpid test_getpid diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index 18e21e490e45..795488483f92 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -1,4 +1,5 @@ #include "config.h" +#include "config_test.h" #include #include #include @@ -20,7 +21,7 @@ int test_fputc(int c, FILE *stream); int test_chdir(const char *path); #define main test_main -#define read test_read +#define cli_read test_read #define socket test_socket #define connect test_connect #define getpid test_getpid From 562e6c4a931f7fc84ab7ef16566ff3996aac43af Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:46:29 +1030 Subject: [PATCH 0196/1530] update mocks Signed-off-by: Rusty Russell --- gossipd/test/run-check_node_announcement.c | 6 ------ gossipd/test/run-crc32_of_update.c | 10 ---------- gossipd/test/run-extended-info.c | 4 ---- 3 files changed, 20 deletions(-) diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index ca982558b22f..95b2bdc1930e 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -84,15 +84,9 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, struct timerel expire UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for queue_peer_msg */ void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "queue_peer_msg called!\n"); abort(); } -/* Generated stub for reltimer_arg */ -void *reltimer_arg(struct oneshot *t UNNEEDED) -{ fprintf(stderr, "reltimer_arg called!\n"); abort(); } /* Generated stub for status_failed */ void status_failed(enum status_failreason code UNNEEDED, const char *fmt UNNEEDED, ...) diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 87e6094b2aaa..3b7595f16a84 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -28,10 +28,6 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } -/* Generated stub for daemon_conn_read_next */ -struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, - struct daemon_conn *dc UNNEEDED) -{ fprintf(stderr, "daemon_conn_read_next called!\n"); abort(); } /* Generated stub for daemon_conn_wake */ void daemon_conn_wake(struct daemon_conn *dc UNNEEDED) { fprintf(stderr, "daemon_conn_wake called!\n"); abort(); } @@ -117,9 +113,6 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, struct timerel expire UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } @@ -130,9 +123,6 @@ void queue_peer_from_store(struct peer *peer UNNEEDED, /* Generated stub for queue_peer_msg */ void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "queue_peer_msg called!\n"); abort(); } -/* Generated stub for reltimer_arg */ -void *reltimer_arg(struct oneshot *t UNNEEDED) -{ fprintf(stderr, "reltimer_arg called!\n"); abort(); } /* Generated stub for status_failed */ void status_failed(enum status_failreason code UNNEEDED, const char *fmt UNNEEDED, ...) diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index bf0d460a82ed..8e5ad79cd38c 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -33,10 +33,6 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } -/* Generated stub for daemon_conn_read_next */ -struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, - struct daemon_conn *dc UNNEEDED) -{ fprintf(stderr, "daemon_conn_read_next called!\n"); abort(); } /* Generated stub for daemon_conn_wake */ void daemon_conn_wake(struct daemon_conn *dc UNNEEDED) { fprintf(stderr, "daemon_conn_wake called!\n"); abort(); } From 7e7a63a20de2237f5221645b58447d426756bf59 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:47:29 +1030 Subject: [PATCH 0197/1530] connectd: keep timeout timer around so we can disable it. connectd will be keeping the conn open, so it needs to free this "conn_timeout" timer. Pass it through, so we can do that. Signed-off-by: Rusty Russell --- connectd/connectd.c | 26 +++++++++++++++----------- connectd/handshake.c | 15 ++++++++++++++- connectd/handshake.h | 23 +++++++++++++++-------- connectd/peer_exchange_initmsg.c | 5 +++++ connectd/peer_exchange_initmsg.h | 2 ++ connectd/test/run-initiator-success.c | 3 ++- connectd/test/run-responder-success.c | 3 ++- devtools/gossipwith.c | 4 +++- 8 files changed, 58 insertions(+), 23 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 12adfdc97310..c2482df8261d 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -525,13 +525,14 @@ static struct io_plan *handshake_in_success(struct io_conn *conn, const struct pubkey *id_key, const struct wireaddr_internal *addr, struct crypto_state *cs, + struct oneshot *timeout, struct daemon *daemon) { struct node_id id; node_id_from_pubkey(&id, id_key); status_peer_debug(&id, "Connect IN"); return peer_exchange_initmsg(conn, daemon, daemon->our_features, - cs, &id, addr, true); + cs, &id, addr, timeout, true); } /*~ If the timer goes off, we simply free everything, which hangs up. */ @@ -591,11 +592,12 @@ static struct io_plan *conn_in(struct io_conn *conn, struct conn_in *conn_in_arg) { struct daemon *daemon = conn_in_arg->daemon; + struct oneshot *timeout; - /* If they don't complete handshake in reasonable time, hang up */ - notleak(new_reltimer(&daemon->timers, conn, - time_from_sec(daemon->timeout_secs), - conn_timeout, conn)); + /* If they don't complete handshake in reasonable time, we hang up */ + timeout = new_reltimer(&daemon->timers, conn, + time_from_sec(daemon->timeout_secs), + conn_timeout, conn); /*~ The crypto handshake differs depending on whether you received or * initiated the socket connection, so there are two entry points. @@ -603,7 +605,7 @@ static struct io_plan *conn_in(struct io_conn *conn, * code from thinking `conn` (which we don't keep a pointer to) is * leaked */ return responder_handshake(notleak(conn), &daemon->mykey, - &conn_in_arg->addr, + &conn_in_arg->addr, timeout, handshake_in_success, daemon); } @@ -723,6 +725,7 @@ static struct io_plan *handshake_out_success(struct io_conn *conn, const struct pubkey *key, const struct wireaddr_internal *addr, struct crypto_state *cs, + struct oneshot *timeout, struct connecting *connect) { struct node_id id; @@ -732,12 +735,13 @@ static struct io_plan *handshake_out_success(struct io_conn *conn, status_peer_debug(&id, "Connect OUT"); return peer_exchange_initmsg(conn, connect->daemon, connect->daemon->our_features, - cs, &id, addr, false); + cs, &id, addr, timeout, false); } struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect) { struct pubkey outkey; + struct oneshot *timeout; /* This shouldn't happen: lightningd should not give invalid ids! */ if (!pubkey_from_node_id(&outkey, &connect->id)) { @@ -748,15 +752,15 @@ struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect) } /* If they don't complete handshake in reasonable time, hang up */ - notleak(new_reltimer(&connect->daemon->timers, conn, - time_from_sec(connect->daemon->timeout_secs), - conn_timeout, conn)); + timeout = new_reltimer(&connect->daemon->timers, conn, + time_from_sec(connect->daemon->timeout_secs), + conn_timeout, conn); status_peer_debug(&connect->id, "Connected out, starting crypto"); connect->connstate = "Cryptographic handshake"; return initiator_handshake(conn, &connect->daemon->mykey, &outkey, &connect->addrs[connect->addrnum], - handshake_out_success, connect); + timeout, handshake_out_success, connect); } /*~ When we've exhausted all addresses without success, we come here. diff --git a/connectd/handshake.c b/connectd/handshake.c index 768b4a2a6a6b..1097f6f3d033 100644 --- a/connectd/handshake.c +++ b/connectd/handshake.c @@ -173,11 +173,15 @@ struct handshake { /* Are we initiator or responder. */ enum bolt8_side side; + /* Timeout timer if we take too long. */ + struct oneshot *timeout; + /* Function to call once handshake complete. */ struct io_plan *(*cb)(struct io_conn *conn, const struct pubkey *their_id, const struct wireaddr_internal *wireaddr, struct crypto_state *cs, + struct oneshot *timeout, void *cbarg); void *cbarg; }; @@ -348,10 +352,12 @@ static struct io_plan *handshake_succeeded(struct io_conn *conn, const struct pubkey *their_id, const struct wireaddr_internal *addr, struct crypto_state *cs, + struct oneshot *timeout, void *cbarg); void *cbarg; struct pubkey their_id; struct wireaddr_internal addr; + struct oneshot *timeout; /* BOLT #8: * @@ -377,9 +383,10 @@ static struct io_plan *handshake_succeeded(struct io_conn *conn, cbarg = h->cbarg; their_id = h->their_id; addr = h->addr; + timeout = h->timeout; tal_free(h); - return cb(conn, &their_id, &addr, &cs, cbarg); + return cb(conn, &their_id, &addr, &cs, timeout, cbarg); } static struct handshake *new_handshake(const tal_t *ctx, @@ -956,10 +963,12 @@ static struct io_plan *act_one_responder(struct io_conn *conn, struct io_plan *responder_handshake_(struct io_conn *conn, const struct pubkey *my_id, const struct wireaddr_internal *addr, + struct oneshot *timeout, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, struct crypto_state *, + struct oneshot *, void *cbarg), void *cbarg) { @@ -970,6 +979,7 @@ struct io_plan *responder_handshake_(struct io_conn *conn, h->addr = *addr; h->cbarg = cbarg; h->cb = cb; + h->timeout = timeout; return act_one_responder(conn, h); } @@ -978,10 +988,12 @@ struct io_plan *initiator_handshake_(struct io_conn *conn, const struct pubkey *my_id, const struct pubkey *their_id, const struct wireaddr_internal *addr, + struct oneshot *timeout, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, struct crypto_state *, + struct oneshot *timeout, void *cbarg), void *cbarg) { @@ -993,6 +1005,7 @@ struct io_plan *initiator_handshake_(struct io_conn *conn, h->addr = *addr; h->cbarg = cbarg; h->cb = cb; + h->timeout = timeout; return act_one_initiator(conn, h); } diff --git a/connectd/handshake.h b/connectd/handshake.h index 733178bee205..facb7f203250 100644 --- a/connectd/handshake.h +++ b/connectd/handshake.h @@ -6,15 +6,17 @@ struct crypto_state; struct io_conn; struct wireaddr_internal; struct pubkey; +struct oneshot; -#define initiator_handshake(conn, my_id, their_id, addr, cb, cbarg) \ - initiator_handshake_((conn), (my_id), (their_id), (addr), \ +#define initiator_handshake(conn, my_id, their_id, addr, timeout, cb, cbarg) \ + initiator_handshake_((conn), (my_id), (their_id), (addr), (timeout), \ typesafe_cb_preargs(struct io_plan *, void *, \ (cb), (cbarg), \ struct io_conn *, \ const struct pubkey *, \ - const struct wireaddr_internal *, \ - struct crypto_state *), \ + const struct wireaddr_internal *, \ + struct crypto_state *, \ + struct oneshot *), \ (cbarg)) @@ -22,31 +24,36 @@ struct io_plan *initiator_handshake_(struct io_conn *conn, const struct pubkey *my_id, const struct pubkey *their_id, const struct wireaddr_internal *addr, + struct oneshot *timeout, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, struct crypto_state *, + struct oneshot *timeout, void *cbarg), void *cbarg); -#define responder_handshake(conn, my_id, addr, cb, cbarg) \ - responder_handshake_((conn), (my_id), (addr), \ +#define responder_handshake(conn, my_id, addr, timeout, cb, cbarg) \ + responder_handshake_((conn), (my_id), (addr), (timeout), \ typesafe_cb_preargs(struct io_plan *, void *, \ (cb), (cbarg), \ struct io_conn *, \ const struct pubkey *, \ - const struct wireaddr_internal *, \ - struct crypto_state *), \ + const struct wireaddr_internal *, \ + struct crypto_state *, \ + struct oneshot *), \ (cbarg)) struct io_plan *responder_handshake_(struct io_conn *conn, const struct pubkey *my_id, const struct wireaddr_internal *addr, + struct oneshot *timeout, struct io_plan *(*cb)(struct io_conn *, const struct pubkey *, const struct wireaddr_internal *, struct crypto_state *, + struct oneshot *, void *cbarg), void *cbarg); #endif /* LIGHTNING_CONNECTD_HANDSHAKE_H */ diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 8110f5a2960f..8106ab92697d 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, const struct crypto_state *cs, const struct node_id *id, const struct wireaddr_internal *addr, + struct oneshot *timeout, bool incoming) { /* If conn is closed, forget peer */ @@ -164,6 +166,9 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, peer->cs = *cs; peer->incoming = incoming; + /* Attach timer to early peer, so it gets freed with it. */ + notleak(tal_steal(peer, timeout)); + /* BOLT #1: * * The sending node: diff --git a/connectd/peer_exchange_initmsg.h b/connectd/peer_exchange_initmsg.h index 6a3c8a24397d..eb654aaa73c0 100644 --- a/connectd/peer_exchange_initmsg.h +++ b/connectd/peer_exchange_initmsg.h @@ -8,6 +8,7 @@ struct daemon; struct io_conn; struct node_id; struct wireaddr_internal; +struct oneshot; /* If successful, calls peer_connected() */ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, @@ -16,6 +17,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, const struct crypto_state *cs, const struct node_id *id, const struct wireaddr_internal *addr, + struct oneshot *timeout, bool incoming); #endif /* LIGHTNING_CONNECTD_PEER_EXCHANGE_INITMSG_H */ diff --git a/connectd/test/run-initiator-success.c b/connectd/test/run-initiator-success.c index 27bb98bc9e03..73e35875f5ce 100644 --- a/connectd/test/run-initiator-success.c +++ b/connectd/test/run-initiator-success.c @@ -278,6 +278,7 @@ static struct io_plan *success(struct io_conn *conn UNUSED, const struct pubkey *them, const struct wireaddr_internal *addr UNUSED, struct crypto_state *cs, + struct oneshot *timeout UNUSED, void *unused UNUSED) { assert(pubkey_eq(them, &rs_pub)); @@ -320,7 +321,7 @@ int main(int argc, char *argv[]) dummy.itype = ADDR_INTERNAL_WIREADDR; dummy.u.wireaddr.addrlen = 0; - initiator_handshake((void *)tmpctx, &ls_pub, &rs_pub, &dummy, success, NULL); + initiator_handshake((void *)tmpctx, &ls_pub, &rs_pub, &dummy, NULL, success, NULL); /* Should not exit! */ abort(); } diff --git a/connectd/test/run-responder-success.c b/connectd/test/run-responder-success.c index 1bf317e3f328..b5de1da5a9e7 100644 --- a/connectd/test/run-responder-success.c +++ b/connectd/test/run-responder-success.c @@ -277,6 +277,7 @@ static struct io_plan *success(struct io_conn *conn UNUSED, const struct pubkey *them UNUSED, const struct wireaddr_internal *addr UNUSED, struct crypto_state *cs, + struct oneshot *timeout UNUSED, void *unused UNUSED) { assert(secret_eq_str(&cs->sk, expect_sk)); @@ -314,7 +315,7 @@ int main(int argc, char *argv[]) dummy.itype = ADDR_INTERNAL_WIREADDR; dummy.u.wireaddr.addrlen = 0; - responder_handshake((void *)tmpctx, &ls_pub, &dummy, success, NULL); + responder_handshake((void *)tmpctx, &ls_pub, &dummy, NULL, success, NULL); /* Should not exit! */ abort(); } diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 0e2093e6764c..66956ca662d3 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -144,6 +144,7 @@ static struct io_plan *handshake_success(struct io_conn *conn, const struct pubkey *them, const struct wireaddr_internal *addr, struct crypto_state *orig_cs, + struct oneshot *timer, char **args) { u8 *msg; @@ -351,7 +352,8 @@ int main(int argc, char *argv[]) if (connect(conn->fd, ai->ai_addr, ai->ai_addrlen) != 0) err(1, "Connecting to %s", at+1); - initiator_handshake(conn, &us, &them, &addr, handshake_success, argv+2); + initiator_handshake(conn, &us, &them, &addr, NULL, + handshake_success, argv+2); exit(0); } From 8f3b390356cb2646c65a0af608eb3b4fcd346e8a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:48:29 +1030 Subject: [PATCH 0198/1530] pytest: make test_channel_state_changed_unilateral more robust. This test started mostly failing (in non-DEVELOPER mode) after the next patch, due to timing issues. Handle both cases for now, and we'll add more enhancements later to things we should be handling more consistently. Signed-off-by: Rusty Russell --- tests/test_plugin.py | 65 +++++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index e59861876281..6f00b8d99bec 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -905,7 +905,8 @@ def wait_for_event(node): assert(event2['cause'] == "user") assert(event2['message'] == "Forcibly closed by `close` command timeout") - # restart l1 early, as the test gets flaky when done after generate_block(100) + # restart l1 now, it will reconnect and l2 will send it an error. + # FIXME: it should re-xmit shutdown, but it doesn't until it's mined :( l1.restart() wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 1) # check 'closer' on l2 while the peer is not yet forgotten @@ -932,21 +933,53 @@ def wait_for_event(node): event1 = wait_for_event(l1) assert(l1.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'remote') - # check if l1 sees ONCHAIN reasons for his channel - assert(event1['old_state'] == "CHANNELD_NORMAL") - assert(event1['new_state'] == "AWAITING_UNILATERAL") - assert(event1['cause'] == "onchain") - assert(event1['message'] == "Funding transaction spent") - event1 = wait_for_event(l1) - assert(event1['old_state'] == "AWAITING_UNILATERAL") - assert(event1['new_state'] == "FUNDING_SPEND_SEEN") - assert(event1['cause'] == "onchain") - assert(event1['message'] == "Onchain funding spend") - event1 = wait_for_event(l1) - assert(event1['old_state'] == "FUNDING_SPEND_SEEN") - assert(event1['new_state'] == "ONCHAIN") - assert(event1['cause'] == "onchain") - assert(event1['message'] == "Onchain init reply") + # If l1 saw onchain first, it goes: + # AWAITING_UNILATERAL + # FUNDING_SPEND_SEEN + # otherwise, it gets a shutdown from remote, and goes: + # CHANNELD_SHUTTING_DOWN + # AWAITING_UNILATERAL + # FUNDING_SPEND_SEEN + if event1['new_state'] == "CHANNELD_SHUTTING_DOWN": + # In this case, cause is always "remote". + assert(event1['old_state'] == "CHANNELD_NORMAL") + assert(event1['cause'] == "remote") + assert(event1['message'] == "Peer closes channel") + + event1 = wait_for_event(l1) + assert(event1['old_state'] == "CHANNELD_SHUTTING_DOWN") + assert(event1['new_state'] == "AWAITING_UNILATERAL") + assert(event1['message'] == "Funding transaction spent") + + event1 = wait_for_event(l1) + assert(event1['old_state'] == "AWAITING_UNILATERAL") + assert(event1['new_state'] == "FUNDING_SPEND_SEEN") + assert(event1['cause'] == "remote") + assert(event1['message'] == "Onchain funding spend") + + event1 = wait_for_event(l1) + assert(event1['old_state'] == "FUNDING_SPEND_SEEN") + assert(event1['new_state'] == "ONCHAIN") + assert(event1['cause'] == "remote") + assert(event1['message'] == "Onchain init reply") + else: + # In this case, cause is always "onchain". + assert(event1['old_state'] == "CHANNELD_NORMAL") + assert(event1['new_state'] == "AWAITING_UNILATERAL") + assert(event1['cause'] == "onchain") + assert(event1['message'] == "Funding transaction spent") + + event1 = wait_for_event(l1) + assert(event1['old_state'] == "AWAITING_UNILATERAL") + assert(event1['new_state'] == "FUNDING_SPEND_SEEN") + assert(event1['cause'] == "onchain") + assert(event1['message'] == "Onchain funding spend") + + event1 = wait_for_event(l1) + assert(event1['old_state'] == "FUNDING_SPEND_SEEN") + assert(event1['new_state'] == "ONCHAIN") + assert(event1['cause'] == "onchain") + assert(event1['message'] == "Onchain init reply") @pytest.mark.openchannel('v1') From e683649004f652ca16f57aee75304ac8e39c780d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:49:29 +1030 Subject: [PATCH 0199/1530] connectd: maintain connection with peer, shuffle data. Instead of passing the incoming socket to lightningd for the subdaemon, create a new one and simply shuffle data between them, keeping connectd in the loop. For the moment, we don't decrypt at all, just shuffle. This means our buffer code is kind of a hack, but that goes away once we start actually decrypting and understanding message boundaries. This implementation is naive: it closes the socket to the local daemon as soon as the peer closes the socket to us. This is fixed in a successive patch series (along with many other similar issues). Signed-off-by: Rusty Russell --- connectd/Makefile | 1 + connectd/connectd.c | 177 +++++++++++++++++++++--------------- connectd/multiplex.c | 212 +++++++++++++++++++++++++++++++++++++++++++ connectd/multiplex.h | 41 +++++++++ 4 files changed, 359 insertions(+), 72 deletions(-) create mode 100644 connectd/multiplex.c create mode 100644 connectd/multiplex.h diff --git a/connectd/Makefile b/connectd/Makefile index 76cb7079f7ce..343a3624c10f 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -5,6 +5,7 @@ CONNECTD_HEADERS := connectd/connectd_wiregen.h \ connectd/connectd.h \ connectd/peer_exchange_initmsg.h \ connectd/handshake.h \ + connectd/multiplex.h \ connectd/netaddress.h \ connectd/tor_autoservice.h \ connectd/tor.h diff --git a/connectd/connectd.c b/connectd/connectd.c index c2482df8261d..c538118628a0 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -59,13 +60,14 @@ #define INITIAL_WAIT_SECONDS 1 #define MAX_WAIT_SECONDS 300 -/*~ We keep a hash table (ccan/htable) of public keys, which tells us what - * peers are already connected. The HTABLE_DEFINE_TYPE() macro needs a - * keyof() function to extract the key. For this simple use case, that's the - * identity function: */ -static const struct node_id *node_id_keyof(const struct node_id *pc) +/*~ We keep a hash table (ccan/htable) of peers, which tells us what peers are + * already connected (by peer->id). */ + +/*~ The HTABLE_DEFINE_TYPE() macro needs a keyof() function to extract the key: + */ +static const struct node_id *peer_keyof(const struct peer *peer) { - return pc; + return &peer->id; } /*~ We also need to define a hashing function. siphash24 is a fast yet @@ -79,12 +81,19 @@ static size_t node_id_hash(const struct node_id *id) return siphash24(siphash_seed(), id->k, sizeof(id->k)); } -/*~ This defines 'struct node_set' which contains 'struct node_id' pointers. */ -HTABLE_DEFINE_TYPE(struct node_id, - node_id_keyof, +/*~ We also define an equality function: is this element equal to this key? */ +static bool peer_eq_node_id(const struct peer *peer, + const struct node_id *id) +{ + return node_id_eq(&peer->id, id); +} + +/*~ This defines 'struct peer_htable' which contains 'struct peer' pointers. */ +HTABLE_DEFINE_TYPE(struct peer, + peer_keyof, node_id_hash, - node_id_eq, - node_set); + peer_eq_node_id, + peer_htable); /*~ This is the global state, like `struct lightningd *ld` in lightningd. */ struct daemon { @@ -100,7 +109,7 @@ struct daemon { /* Peers that we've handed to `lightningd`, which it hasn't told us * have disconnected. */ - struct node_set peers; + struct peer_htable peers; /* Peers we are trying to reach */ struct list_head connecting; @@ -414,7 +423,7 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, /*~ ccan/io supports waiting on an address: in this case, the key in * the peer set. When someone calls `io_wake()` on that address, it * will call retry_peer_connected above. */ - return io_wait(conn, node_set_get(&daemon->peers, id), + return io_wait(conn, peer_htable_get(&daemon->peers, id), /*~ The notleak() wrapper is a DEVELOPER-mode hack so * that our memory leak detection doesn't consider 'pr' * (which is not referenced from our code) to be a @@ -422,6 +431,49 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, retry_peer_connected, notleak(pr)); } +/*~ When we free a peer, we remove it from the daemon's hashtable */ +static void destroy_peer(struct peer *peer, struct daemon *daemon) +{ + peer_htable_del(&daemon->peers, peer); +} + +/*~ This is where we create a new peer. */ +static struct peer *new_peer(struct daemon *daemon, + const struct node_id *id, + const struct crypto_state *cs, + const u8 *their_features, + struct io_conn *conn STEALS, + int *fd_for_subd) +{ + struct peer *peer = tal(daemon, struct peer); + + peer->id = *id; + peer->pps = new_per_peer_state(peer, cs); + peer->final_msg = NULL; + peer->subd_in = NULL; + peer->peer_in = NULL; + peer->sent_to_subd = NULL; + peer->sent_to_peer = NULL; + peer->peer_outq = msg_queue_new(peer); + peer->subd_outq = msg_queue_new(peer); + + /* Aim for connection to shuffle data back and forth: sets up + * peer->to_subd */ + if (!multiplex_subd_setup(peer, fd_for_subd)) + return tal_free(peer); + + /* If gossipd can't give us a file descriptor, we give up connecting. */ + if (!get_gossipfds(daemon, id, their_features, peer->pps)) { + close(*fd_for_subd); + return tal_free(peer); + } + + peer->to_peer = tal_steal(peer, conn); + peer_htable_add(&daemon->peers, peer); + tal_add_destructor2(peer, destroy_peer, daemon); + return peer; +} + /*~ Note the lack of static: this is called by peer_exchange_initmsg.c once the * INIT messages are exchanged, and also by the retry code above. */ struct io_plan *peer_connected(struct io_conn *conn, @@ -433,11 +485,13 @@ struct io_plan *peer_connected(struct io_conn *conn, bool incoming) { u8 *msg; - struct per_peer_state *pps; + struct peer *peer; int unsup; size_t depender, missing; + int subd_fd; - if (node_set_get(&daemon->peers, id)) + peer = peer_htable_get(&daemon->peers, id); + if (peer) return peer_reconnected(conn, daemon, id, addr, cs, their_features, incoming); @@ -487,35 +541,28 @@ struct io_plan *peer_connected(struct io_conn *conn, conn, find_connecting(daemon, id)->conn); /* This contains the per-peer state info; gossipd fills in pps->gs */ - pps = new_per_peer_state(tmpctx, cs); - - /* If gossipd can't give us a file descriptor, we give up connecting. */ - if (!get_gossipfds(daemon, id, their_features, pps)) + peer = new_peer(daemon, id, cs, their_features, conn, &subd_fd); + /* Only takes over conn if it succeeds. */ + if (!peer) return io_close(conn); /* Create message to tell master peer has connected. */ msg = towire_connectd_peer_connected(NULL, id, addr, incoming, - pps, their_features); + peer->pps, their_features); /*~ daemon_conn is a message queue for inter-daemon communication: we * queue up the `connect_peer_connected` message to tell lightningd * we have connected, and give the peer and gossip fds. */ daemon_conn_send(daemon->master, take(msg)); - /* io_conn_fd() extracts the fd from ccan/io's io_conn */ - daemon_conn_send_fd(daemon->master, io_conn_fd(conn)); - daemon_conn_send_fd(daemon->master, pps->gossip_fd); - daemon_conn_send_fd(daemon->master, pps->gossip_store_fd); + daemon_conn_send_fd(daemon->master, subd_fd); + daemon_conn_send_fd(daemon->master, peer->pps->gossip_fd); + daemon_conn_send_fd(daemon->master, peer->pps->gossip_store_fd); /* Don't try to close these on freeing. */ - pps->gossip_store_fd = pps->gossip_fd = -1; + peer->pps->gossip_store_fd = peer->pps->gossip_fd = -1; - /*~ Finally, we add it to the set of pubkeys: tal_dup will handle - * take() args for us, by simply tal_steal()ing it. */ - node_set_add(&daemon->peers, tal_dup(daemon, struct node_id, id)); - - /*~ We want to free the connection, but not close the fd (which is - * queued to go to lightningd), so use this variation on io_close: */ - return io_close_taken_fd(conn); + /*~ Now we set up this connection to read/write from subd */ + return multiplex_peer_setup(conn, peer); } /*~ handshake.c's handles setting up the crypto state once we get a connection @@ -1774,14 +1821,14 @@ static void add_gossip_addrs(struct wireaddr_internal **addrs, static void try_connect_peer(struct daemon *daemon, const struct node_id *id, u32 seconds_waited, - struct wireaddr_internal *addrhint) + struct wireaddr_internal *addrhint STEALS) { struct wireaddr_internal *addrs; bool use_proxy = daemon->always_use_proxy; struct connecting *connect; /* Already done? May happen with timer. */ - if (node_set_get(&daemon->peers, id)) + if (peer_htable_get(&daemon->peers, id)) return; /* If we're trying to connect it right now, that's OK. */ @@ -1874,23 +1921,24 @@ static void connect_to_peer(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 *node; + struct peer *peer; /* We should stay in sync with lightningd at all times. */ - node = node_set_get(&daemon->peers, id); - if (!node) + peer = peer_htable_get(&daemon->peers, id); + if (!peer) status_failed(STATUS_FAIL_INTERNAL_ERROR, "peer_disconnected unknown peer: %s", type_to_string(tmpctx, struct node_id, id)); - node_set_del(&daemon->peers, node); status_peer_debug(id, "disconnect"); /* Wake up in case there's a reconnecting peer waiting in io_wait. */ - io_wake(node); + io_wake(peer); /* 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); + * htable doesn't assume it's a tal object at all. That's why we have + * a destructor attached to peer (called destroy_peer by + * convention). */ + tal_free(peer); } /* lightningd tells us a peer has disconnected. */ @@ -1904,40 +1952,20 @@ static void peer_disconnected(struct daemon *daemon, const u8 *msg) cleanup_dead_peer(daemon, &id); } -/* 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 void peer_final_msg(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { + struct peer *peer; struct per_peer_state *pps; - struct final_msg_data *f = tal(NULL, struct final_msg_data); + struct node_id id; 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)) + if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &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. */ io_fd_block(io_conn_fd(conn), true); for (size_t i = 0; i < ARRAY_SIZE(fds); i++) { @@ -1949,16 +1977,20 @@ static void peer_final_msg(struct io_conn *conn, } io_fd_block(io_conn_fd(conn), false); + /* Close fd to ourselves. */ + close(fds[0]); + /* 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); + /* This can happen if peer hung up on us. */ + peer = peer_htable_get(&daemon->peers, &id); + if (peer) { + /* Log and encrypt message for peer. */ + status_peer_io(LOG_IO_OUT, &id, finalmsg); + finalmsg = cryptomsg_encrypt_msg(NULL, &pps->cs, take(finalmsg)); + multiplex_final_msg(peer, take(finalmsg)); + } } #if DEVELOPER @@ -1971,6 +2003,7 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) /* Now delete daemon and those which it has pointers to. */ memleak_remove_region(memtable, daemon, sizeof(daemon)); + memleak_remove_htable(memtable, &daemon->peers.raw); found_leak = dump_memleak(memtable, memleak_status_broken); daemon_conn_send(daemon->master, @@ -2064,7 +2097,7 @@ int main(int argc, char *argv[]) /* Allocate and set up our simple top-level structure. */ daemon = tal(NULL, struct daemon); - node_set_init(&daemon->peers); + peer_htable_init(&daemon->peers); memleak_add_helper(daemon, memleak_daemon_cb); list_head_init(&daemon->connecting); daemon->listen_fds = tal_arr(daemon, struct listen_fd, 0); diff --git a/connectd/multiplex.c b/connectd/multiplex.c new file mode 100644 index 000000000000..5ae912671fdd --- /dev/null +++ b/connectd/multiplex.c @@ -0,0 +1,212 @@ +/*~ This contains all the code to shuffle data between socket to the peer + * itself, and the subdaemons. */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* These four function handle subd->peer */ +static struct io_plan *after_final_msg(struct io_conn *peer_conn, + struct peer *peer) +{ + /* io_close will want to free this itself! */ + assert(peer->to_peer == peer_conn); + + /* Invert ownership, so io_close frees peer for us */ + tal_steal(NULL, peer_conn); + tal_steal(peer_conn, peer); + + return io_close(peer_conn); +} + +static struct io_plan *write_to_peer(struct io_conn *peer_conn, + struct peer *peer) +{ + assert(peer->to_peer == peer_conn); + + /* Free last sent one (if any) */ + tal_free(peer->sent_to_peer); + + /* Pop tail of send queue */ + peer->sent_to_peer = msg_dequeue(peer->peer_outq); + + /* Nothing to send? */ + if (!peer->sent_to_peer) { + /* Send final once subd is not longer connected */ + if (peer->final_msg && !peer->to_subd) { + return io_write(peer_conn, + peer->final_msg, + tal_bytelen(peer->final_msg), + after_final_msg, peer); + } + /* Tell them to read again, */ + io_wake(&peer->subd_in); + + /* Wait for them to wake us */ + return msg_queue_wait(peer_conn, peer->peer_outq, + write_to_peer, peer); + } + + return io_write(peer_conn, + peer->sent_to_peer, + tal_bytelen(peer->sent_to_peer), + write_to_peer, peer); +} + +static struct io_plan *read_from_subd(struct io_conn *subd_conn, + struct peer *peer); +static struct io_plan *read_from_subd_done(struct io_conn *subd_conn, + struct peer *peer) +{ + size_t len = ((size_t *)peer->subd_in)[1023]; + assert(peer->to_subd == subd_conn); + + /* Trim to length */ + tal_resize(&peer->subd_in, len); + + /* Tell them to write. */ + msg_enqueue(peer->peer_outq, take(peer->subd_in)); + peer->subd_in = NULL; + /* Wait for them to wake us */ + return io_wait(subd_conn, &peer->subd_in, read_from_subd, peer); +} + +static struct io_plan *read_from_subd(struct io_conn *subd_conn, + struct peer *peer) +{ + /* We stash the length at the end */ + size_t *buf = tal_arr(peer, size_t, 1024); + assert(peer->to_subd == subd_conn); + + peer->subd_in = (u8 *)buf; + return io_read_partial(subd_conn, peer->subd_in, + sizeof(size_t) * 1023, + &buf[1023], + read_from_subd_done, peer); +} + +/* These four function handle peer->subd */ +static struct io_plan *write_to_subd(struct io_conn *subd_conn, + struct peer *peer) +{ + assert(peer->to_subd == subd_conn); + + /* Free last sent one (if any) */ + tal_free(peer->sent_to_subd); + + /* Pop tail of send queue */ + peer->sent_to_subd = msg_dequeue(peer->subd_outq); + + /* Nothing to send? */ + if (!peer->sent_to_subd) { + /* Tell them to read again, */ + io_wake(&peer->peer_in); + + /* Wait for them to wake us */ + return msg_queue_wait(subd_conn, peer->subd_outq, + write_to_subd, peer); + } + + return io_write(subd_conn, + peer->sent_to_subd, + tal_bytelen(peer->sent_to_subd), + write_to_subd, peer); +} + +static struct io_plan *read_from_peer(struct io_conn *peer_conn, + struct peer *peer); +static struct io_plan *read_from_peer_done(struct io_conn *peer_conn, + struct peer *peer) +{ + size_t len = ((size_t *)peer->peer_in)[1023]; + assert(peer->to_peer == peer_conn); + + /* Trim to length */ + tal_resize(&peer->peer_in, len); + + /* Tell them to write. */ + msg_enqueue(peer->subd_outq, take(peer->peer_in)); + peer->peer_in = NULL; + /* Wait for them to wake us */ + return io_wait(peer_conn, &peer->peer_in, read_from_peer, peer); +} + +static struct io_plan *read_from_peer(struct io_conn *peer_conn, + struct peer *peer) +{ + /* We stash the length at the end */ + size_t *buf = tal_arr(peer, size_t, 1024); + assert(peer->to_peer == peer_conn); + + peer->peer_in = (u8 *)buf; + return io_read_partial(peer_conn, peer->peer_in, + sizeof(size_t) * 1023, + &buf[1023], + read_from_peer_done, peer); +} + +static struct io_plan *subd_conn_init(struct io_conn *subd_conn, struct peer *peer) +{ + peer->to_subd = subd_conn; + return io_duplex(subd_conn, + read_from_subd(subd_conn, peer), + write_to_subd(subd_conn, peer)); +} + +static void destroy_subd_conn(struct io_conn *subd_conn, struct peer *peer) +{ + assert(subd_conn == peer->to_subd); + peer->to_subd = NULL; + /* In case they were waiting for this to send final_msg */ + if (peer->final_msg) + msg_wake(peer->peer_outq); +} + +bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd) +{ + int fds[2]; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + status_broken("Failed to create socketpair: %s", + strerror(errno)); + return false; + } + peer->to_subd = io_new_conn(peer, fds[0], subd_conn_init, peer); + tal_add_destructor2(peer->to_subd, destroy_subd_conn, peer); + *fd_for_subd = fds[1]; + return true; +} + +static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) +{ + assert(peer->to_peer == peer_conn); + peer->to_peer = NULL; + + /* Close internal connections if not already. */ + if (peer->to_subd) + io_close(peer->to_subd); +} + +struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, + struct peer *peer) +{ + /*~ If conn closes, we close the subd connections and wait for + * lightningd to tell us to close with the peer */ + tal_add_destructor2(peer_conn, destroy_peer_conn, peer); + + return io_duplex(peer_conn, + read_from_peer(peer_conn, peer), + write_to_peer(peer_conn, peer)); +} + +void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) +{ + peer->final_msg = tal_dup_talarr(peer, u8, final_msg); + if (!peer->to_subd) + io_wake(peer->peer_outq); +} diff --git a/connectd/multiplex.h b/connectd/multiplex.h new file mode 100644 index 000000000000..84be1f008b19 --- /dev/null +++ b/connectd/multiplex.h @@ -0,0 +1,41 @@ +#ifndef LIGHTNING_CONNECTD_MULTIPLEX_H +#define LIGHTNING_CONNECTD_MULTIPLEX_H +#include "config.h" +#include +#include +#include + +struct peer { + struct node_id id; + struct per_peer_state *pps; + + /* Connection to the peer */ + struct io_conn *to_peer; + + /* Connection to the subdaemon */ + struct io_conn *to_subd; + + /* Final message to send to peer (and hangup) */ + u8 *final_msg; + + /* Input buffers. */ + u8 *subd_in, *peer_in; + + /* Output buffers. */ + struct msg_queue *subd_outq, *peer_outq; + + /* Sent buffers (for freeing after sending) */ + const u8 *sent_to_subd, *sent_to_peer; +}; + +/* Set up peer->to_subd; sets fd_for_subd to pass to lightningd. */ +bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd); + +/* Take over peer_conn as peer->to_peer */ +struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, + struct peer *peer); + +/* Send this message to peer and disconnect. */ +void multiplex_final_msg(struct peer *peer, + const u8 *final_msg TAKES); +#endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ From 6bfc3d8692fe61e0ac84bd36fb26e773ec828044 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:50:29 +1030 Subject: [PATCH 0200/1530] gossipwith: create our own internal sync_crypto functions. This avoids changes to crypto_sync which are coming in next patch. Signed-off-by: Rusty Russell --- devtools/Makefile | 2 +- devtools/gossipwith.c | 86 ++++++++++++++++++++++++++----------------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/devtools/Makefile b/devtools/Makefile index b4c23f03ac56..e61f928f1161 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -70,7 +70,7 @@ devtools/onion: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS devtools/blindedpath: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) common/blinding.o $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/blindedpath.o common/onion.o common/onionreply.o common/sphinx.o -devtools/gossipwith: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/peer$(EXP)_wiregen.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o common/crypto_sync.o +devtools/gossipwith: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/peer$(EXP)_wiregen.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS): wire/wire.h diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 66956ca662d3..3e7f0481a9fd 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -9,12 +9,12 @@ #include #include #include -#include -#include +#include #include #include #include #include +#include #include #include #include @@ -69,23 +69,6 @@ void status_fmt(enum log_level level, { } -#if DEVELOPER -void dev_sabotage_fd(int fd, bool close_fd) -{ - abort(); -} - -void dev_blackhole_fd(int fd) -{ - abort(); -} - -enum dev_disconnect dev_disconnect(int pkt_type) -{ - return DEV_DISCONNECT_NORMAL; -} -#endif - static char *opt_set_network(const char *arg, void *unused) { assert(arg != NULL); @@ -102,11 +85,6 @@ static void opt_show_network(char buf[OPT_SHOW_LEN], const void *unused) snprintf(buf, OPT_SHOW_LEN, "%s", chainparams->network_name); } -void peer_failed_connection_lost(void) -{ - exit(0); -} - void ecdh(const struct pubkey *point, struct secret *ss) { if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, @@ -140,18 +118,60 @@ static struct io_plan *simple_read(struct io_conn *conn, return next(conn, next_arg); } +static void sync_crypto_write(int peer_fd, struct crypto_state *cs, const void *msg TAKES) +{ + u8 *enc; + + enc = cryptomsg_encrypt_msg(NULL, cs, msg); + + if (!write_all(peer_fd, enc, tal_count(enc))) + exit(1); + tal_free(enc); +} + +static u8 *sync_crypto_read(const tal_t *ctx, int peer_fd, struct crypto_state *cs) +{ + u8 hdr[18], *enc, *dec; + u16 len; + + if (!read_all(peer_fd, hdr, sizeof(hdr))) { + status_debug("Failed reading header: %s", strerror(errno)); + exit(0); + } + + if (!cryptomsg_decrypt_header(cs, hdr, &len)) { + status_debug("Failed hdr decrypt with rn=%"PRIu64, + cs->rn-1); + exit(1); + } + + enc = tal_arr(ctx, u8, len + 16); + if (!read_all(peer_fd, enc, tal_count(enc))) { + status_debug("Failed reading body: %s", strerror(errno)); + exit(1); + } + + dec = cryptomsg_decrypt_body(ctx, cs, enc); + tal_free(enc); + if (!dec) + exit(1); + else + status_peer_io(LOG_IO_IN, NULL, dec); + + return dec; +} + static struct io_plan *handshake_success(struct io_conn *conn, const struct pubkey *them, const struct wireaddr_internal *addr, - struct crypto_state *orig_cs, + struct crypto_state *cs, struct oneshot *timer, char **args) { u8 *msg; - struct per_peer_state *pps = new_per_peer_state(conn, orig_cs); + int peer_fd = io_conn_fd(conn); struct pollfd pollfd[2]; - pps->peer_fd = io_conn_fd(conn); if (initial_sync) set_feature_bit(&features, OPTIONAL_FEATURE(OPT_INITIAL_ROUTING_SYNC)); @@ -165,9 +185,9 @@ static struct io_plan *handshake_success(struct io_conn *conn, } msg = towire_init(NULL, NULL, features, tlvs); - sync_crypto_write(pps, take(msg)); + sync_crypto_write(peer_fd, cs, take(msg)); /* Ignore their init message. */ - tal_free(sync_crypto_read(NULL, pps)); + tal_free(sync_crypto_read(NULL, peer_fd, cs)); tal_free(tlvs); } @@ -176,14 +196,14 @@ static struct io_plan *handshake_success(struct io_conn *conn, else pollfd[0].fd = -1; pollfd[0].events = POLLIN; - pollfd[1].fd = pps->peer_fd; + pollfd[1].fd = peer_fd; pollfd[1].events = POLLIN; while (*args) { u8 *m = tal_hexdata(NULL, *args, strlen(*args)); if (!m) errx(1, "Invalid hexdata '%s'", *args); - sync_crypto_write(pps, take(m)); + sync_crypto_write(peer_fd, cs, take(m)); args++; } @@ -204,10 +224,10 @@ static struct io_plan *handshake_success(struct io_conn *conn, if (!read_all(STDIN_FILENO, msg, tal_bytelen(msg))) err(1, "Only read partial message"); - sync_crypto_write(pps, take(msg)); + sync_crypto_write(peer_fd, cs, take(msg)); } } else if (pollfd[1].revents & POLLIN) { - msg = sync_crypto_read(NULL, pps); + msg = sync_crypto_read(NULL, peer_fd, cs); if (!msg) err(1, "Reading msg"); if (hex) { From 71f736678f6468a6c7266adc8f47b4cde2200e03 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:51:29 +1030 Subject: [PATCH 0201/1530] lightningd: make sure gossipd knows before we update blockheight. Without this, we can get spurious failures from lnprototest, which is waiting for lightningd to acknowledge the blockheight in getinfo: ``` 2021-12-21T00:56:21.460Z DEBUG lightningd: Adding block 109: 57a7bd3ade3a88a899e5b442075e9722ccec07e0205f9a913b304a3e2e038e26 2021-12-21T00:56:21.470Z DEBUG lightningd: Adding block 110: 11a280eb76f588e92e20c39999be9d2baff016c3c6bac1837b649a270570b7dd 2021-12-21T00:56:21.479Z DEBUG lightningd: Adding block 111: 02977fc9529b2ab4e0a805c4bc1bcfbff5a4e6577a8b31266341d22e204a1d27 2021-12-21T00:56:21.487Z DEBUG lightningd: Adding block 112: 2402f31c5ddfc9e847e8bbfb7df084d29a5d5d936a4358c109f2f4cf9ea8d828 2021-12-21T00:56:21.496Z DEBUG lightningd: Adding block 113: 5a561fe9423b4df33f004fc09985ee3ef38364d692a56a8b27ecbc6098a16d39 2021-12-21T00:56:21.505Z DEBUG lightningd: Adding block 114: 4502f5ec23c89177872846848848322e8fa6c3fb6f5eb361194e4cd47596dfe9 2021-12-21T00:56:21.511Z DEBUG 02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9-gossipd: Ignoring future channel_announcment for 109x1x0 (current block 108) ``` Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 4 ++++ gossipd/gossipd_wire.csv | 3 +++ gossipd/test/run-onion_message.c | 3 +++ lightningd/gossip_control.c | 24 ++++++++++++++++++++++-- lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 +++ lightningd/peer_control.c | 2 +- 7 files changed, 37 insertions(+), 3 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index f1c57b9e8c1f..0b723508606a 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1178,6 +1178,9 @@ static void new_blockheight(struct daemon *daemon, const u8 *msg) tal_arr_remove(&daemon->deferred_txouts, i); i--; } + + daemon_conn_send(daemon->master, + take(towire_gossipd_new_blockheight_reply(NULL))); } #if DEVELOPER @@ -1499,6 +1502,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: + case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: break; } diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 8f49421d0312..6525397b8810 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -74,6 +74,9 @@ msgdata,gossipd_dev_compact_store_reply,success,bool, msgtype,gossipd_new_blockheight,3026 msgdata,gossipd_new_blockheight,blockheight,u32, +# gossipd: got it! +msgtype,gossipd_new_blockheight_reply,3126 + msgtype,gossipd_got_onionmsg_to_us,3145 msgdata,gossipd_got_onionmsg_to_us,obs2,bool, msgdata,gossipd_got_onionmsg_to_us,node_alias,pubkey, diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 1a4cfd59eb9d..b306112c4392 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -338,6 +338,9 @@ u8 *towire_gossipd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEE /* Generated stub for towire_gossipd_init_reply */ u8 *towire_gossipd_init_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_gossipd_init_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_new_blockheight_reply */ +u8 *towire_gossipd_new_blockheight_reply(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "towire_gossipd_new_blockheight_reply called!\n"); abort(); } /* Generated stub for towire_gossipd_new_peer_reply */ u8 *towire_gossipd_new_peer_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED, const struct gossip_state *gs UNNEEDED) { fprintf(stderr, "towire_gossipd_new_peer_reply called!\n"); abort(); } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index d64575220a04..2acf5d15a51e 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -132,6 +133,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: + case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: break; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: @@ -144,14 +146,32 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) return 0; } +static void gossipd_new_blockheight_reply(struct subd *gossipd, + const u8 *reply, + const int *fds UNUSED, + void *blockheight) +{ + if (!fromwire_gossipd_new_blockheight_reply(reply)) { + /* Shouldn't happen! */ + log_broken(gossipd->ld->log, + "Invalid new_blockheight_reply from gossipd: %s", + tal_hex(tmpctx, reply)); + return; + } + + /* Now, finally update getinfo's blockheight */ + gossipd->ld->blockheight = ptr2int(blockheight); +} + void gossip_notify_new_block(struct lightningd *ld, u32 blockheight) { /* Only notify gossipd once we're synced. */ if (!topology_synced(ld->topology)) return; - subd_send_msg(ld->gossip, - take(towire_gossipd_new_blockheight(NULL, blockheight))); + subd_req(ld->gossip, ld->gossip, + take(towire_gossipd_new_blockheight(NULL, blockheight)), + -1, 0, gossipd_new_blockheight_reply, int2ptr(blockheight)); } static void gossip_topology_synced(struct chain_topology *topo, void *unused) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index b0ce617a3ec7..1824838c4f33 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -215,6 +215,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) /*~ This is detailed in chaintopology.c */ ld->topology = new_topology(ld, ld->log); + ld->blockheight = 0; ld->daemon_parent_fd = -1; ld->proxyaddr = NULL; ld->always_use_proxy = false; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 884b2f921744..8f870dbf7dfd 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -167,6 +167,9 @@ struct lightningd { /* Our chain topology. */ struct chain_topology *topology; + /* Blockheight (as acknowledged by gossipd) */ + u32 blockheight; + /* HTLCs in flight. */ struct htlc_in_map htlcs_in; struct htlc_out_map htlcs_out; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index fc81ee52bf68..1df75a738a1c 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1749,7 +1749,7 @@ static struct command_result *json_getinfo(struct command *cmd, json_array_end(response); } json_add_string(response, "version", version()); - json_add_num(response, "blockheight", get_block_height(cmd->ld->topology)); + json_add_num(response, "blockheight", cmd->ld->blockheight); json_add_string(response, "network", chainparams->network_name); json_add_amount_msat_compat(response, wallet_total_forward_fees(cmd->ld->wallet), From a2b3d335bb121a046b516af3f3fb02c8abb6785b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:52:29 +1030 Subject: [PATCH 0202/1530] connectd: do decryption for peers. We temporarily hack to sync_crypto_write/sync_crypto_read functions to not do any crypto, and do it all in connectd. Signed-off-by: Rusty Russell --- common/crypto_sync.c | 34 ++--------- connectd/connectd.c | 2 - connectd/multiplex.c | 142 +++++++++++++++++++++++++------------------ connectd/multiplex.h | 8 ++- wire/wire_io.h | 2 +- 5 files changed, 95 insertions(+), 93 deletions(-) diff --git a/common/crypto_sync.c b/common/crypto_sync.c index 29d8cd92e89f..58dc8b2a06be 100644 --- a/common/crypto_sync.c +++ b/common/crypto_sync.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) { @@ -19,10 +21,8 @@ void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) bool post_sabotage = false, post_close; int type = fromwire_peektype(msg); #endif - u8 *enc; status_peer_io(LOG_IO_OUT, NULL, msg); - enc = cryptomsg_encrypt_msg(NULL, &pps->cs, msg); #if DEVELOPER switch (dev_disconnect(type)) { @@ -44,9 +44,8 @@ void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) break; } #endif - if (!write_all(pps->peer_fd, enc, tal_count(enc))) + if (!wire_sync_write(pps->peer_fd, msg)) peer_failed_connection_lost(); - tal_free(enc); #if DEVELOPER if (post_sabotage) @@ -101,32 +100,11 @@ void sync_crypto_write_no_delay(struct per_peer_state *pps, u8 *sync_crypto_read(const tal_t *ctx, struct per_peer_state *pps) { - u8 hdr[18], *enc, *dec; - u16 len; - - if (!read_all(pps->peer_fd, hdr, sizeof(hdr))) { - status_debug("Failed reading header: %s", strerror(errno)); - peer_failed_connection_lost(); - } - - if (!cryptomsg_decrypt_header(&pps->cs, hdr, &len)) { - status_debug("Failed hdr decrypt with rn=%"PRIu64, - pps->cs.rn-1); - peer_failed_connection_lost(); - } - - enc = tal_arr(ctx, u8, len + 16); - if (!read_all(pps->peer_fd, enc, tal_count(enc))) { - status_debug("Failed reading body: %s", strerror(errno)); - peer_failed_connection_lost(); - } - - dec = cryptomsg_decrypt_body(ctx, &pps->cs, enc); - tal_free(enc); + u8 *dec = wire_sync_read(ctx, pps->peer_fd); if (!dec) peer_failed_connection_lost(); - else - status_peer_io(LOG_IO_IN, NULL, dec); + + status_peer_io(LOG_IO_IN, NULL, dec); return dec; } diff --git a/connectd/connectd.c b/connectd/connectd.c index c538118628a0..2dc053923cde 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -452,7 +452,6 @@ static struct peer *new_peer(struct daemon *daemon, peer->final_msg = NULL; peer->subd_in = NULL; peer->peer_in = NULL; - peer->sent_to_subd = NULL; peer->sent_to_peer = NULL; peer->peer_outq = msg_queue_new(peer); peer->subd_outq = msg_queue_new(peer); @@ -1988,7 +1987,6 @@ static void peer_final_msg(struct io_conn *conn, if (peer) { /* Log and encrypt message for peer. */ status_peer_io(LOG_IO_OUT, &id, finalmsg); - finalmsg = cryptomsg_encrypt_msg(NULL, &pps->cs, take(finalmsg)); multiplex_final_msg(peer, take(finalmsg)); } } diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 5ae912671fdd..fd7668b3bd35 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -3,12 +3,20 @@ #include "config.h" #include #include +#include +#include #include #include #include #include #include #include +#include + +void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) +{ + msg_enqueue(peer->peer_outq, msg); +} /* These four function handle subd->peer */ static struct io_plan *after_final_msg(struct io_conn *peer_conn, @@ -24,25 +32,39 @@ static struct io_plan *after_final_msg(struct io_conn *peer_conn, return io_close(peer_conn); } +static struct io_plan *encrypt_and_send(struct peer *peer, + const u8 *msg TAKES, + struct io_plan *(*next) + (struct io_conn *peer_conn, + struct peer *peer)) +{ + /* We free this and the encrypted version in next write_to_peer */ + peer->sent_to_peer = cryptomsg_encrypt_msg(peer, &peer->pps->cs, msg); + return io_write(peer->to_peer, + peer->sent_to_peer, + tal_bytelen(peer->sent_to_peer), + next, peer); +} + static struct io_plan *write_to_peer(struct io_conn *peer_conn, struct peer *peer) { + const u8 *msg; assert(peer->to_peer == peer_conn); /* Free last sent one (if any) */ - tal_free(peer->sent_to_peer); + peer->sent_to_peer = tal_free(peer->sent_to_peer); /* Pop tail of send queue */ - peer->sent_to_peer = msg_dequeue(peer->peer_outq); + msg = msg_dequeue(peer->peer_outq); /* Nothing to send? */ - if (!peer->sent_to_peer) { + if (!msg) { /* Send final once subd is not longer connected */ if (peer->final_msg && !peer->to_subd) { - return io_write(peer_conn, - peer->final_msg, - tal_bytelen(peer->final_msg), - after_final_msg, peer); + return encrypt_and_send(peer, + peer->final_msg, + after_final_msg); } /* Tell them to read again, */ io_wake(&peer->subd_in); @@ -52,10 +74,7 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, write_to_peer, peer); } - return io_write(peer_conn, - peer->sent_to_peer, - tal_bytelen(peer->sent_to_peer), - write_to_peer, peer); + return encrypt_and_send(peer, take(msg), write_to_peer); } static struct io_plan *read_from_subd(struct io_conn *subd_conn, @@ -63,15 +82,10 @@ static struct io_plan *read_from_subd(struct io_conn *subd_conn, static struct io_plan *read_from_subd_done(struct io_conn *subd_conn, struct peer *peer) { - size_t len = ((size_t *)peer->subd_in)[1023]; - assert(peer->to_subd == subd_conn); - - /* Trim to length */ - tal_resize(&peer->subd_in, len); - - /* Tell them to write. */ - msg_enqueue(peer->peer_outq, take(peer->subd_in)); + /* Tell them to encrypt & write. */ + queue_peer_msg(peer, take(peer->subd_in)); peer->subd_in = NULL; + /* Wait for them to wake us */ return io_wait(subd_conn, &peer->subd_in, read_from_subd, peer); } @@ -79,32 +93,23 @@ static struct io_plan *read_from_subd_done(struct io_conn *subd_conn, static struct io_plan *read_from_subd(struct io_conn *subd_conn, struct peer *peer) { - /* We stash the length at the end */ - size_t *buf = tal_arr(peer, size_t, 1024); - assert(peer->to_subd == subd_conn); - - peer->subd_in = (u8 *)buf; - return io_read_partial(subd_conn, peer->subd_in, - sizeof(size_t) * 1023, - &buf[1023], - read_from_subd_done, peer); + return io_read_wire(subd_conn, peer, &peer->subd_in, + read_from_subd_done, peer); } /* These four function handle peer->subd */ static struct io_plan *write_to_subd(struct io_conn *subd_conn, struct peer *peer) { + const u8 *msg; assert(peer->to_subd == subd_conn); - /* Free last sent one (if any) */ - tal_free(peer->sent_to_subd); - /* Pop tail of send queue */ - peer->sent_to_subd = msg_dequeue(peer->subd_outq); + msg = msg_dequeue(peer->subd_outq); /* Nothing to send? */ - if (!peer->sent_to_subd) { - /* Tell them to read again, */ + if (!msg) { + /* Tell them to read again. */ io_wake(&peer->peer_in); /* Wait for them to wake us */ @@ -112,42 +117,59 @@ static struct io_plan *write_to_subd(struct io_conn *subd_conn, write_to_subd, peer); } - return io_write(subd_conn, - peer->sent_to_subd, - tal_bytelen(peer->sent_to_subd), - write_to_subd, peer); + return io_write_wire(subd_conn, take(msg), write_to_subd, peer); } -static struct io_plan *read_from_peer(struct io_conn *peer_conn, - struct peer *peer); -static struct io_plan *read_from_peer_done(struct io_conn *peer_conn, +static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, + struct peer *peer); +static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, + struct peer *peer) +{ + u8 *decrypted; + + decrypted = cryptomsg_decrypt_body(NULL, &peer->pps->cs, + peer->peer_in); + if (!decrypted) + return io_close(peer_conn); + tal_free(peer->peer_in); + + /* Tell them to write. */ + msg_enqueue(peer->subd_outq, take(decrypted)); + + /* Wait for them to wake us */ + return io_wait(peer_conn, &peer->peer_in, read_hdr_from_peer, peer); +} + +static struct io_plan *read_body_from_peer(struct io_conn *peer_conn, struct peer *peer) { - size_t len = ((size_t *)peer->peer_in)[1023]; - assert(peer->to_peer == peer_conn); + u16 len; - /* Trim to length */ - tal_resize(&peer->peer_in, len); + if (!cryptomsg_decrypt_header(&peer->pps->cs, peer->peer_in, &len)) + return io_close(peer_conn); - /* Tell them to write. */ - msg_enqueue(peer->subd_outq, take(peer->peer_in)); - peer->peer_in = NULL; - /* Wait for them to wake us */ - return io_wait(peer_conn, &peer->peer_in, read_from_peer, peer); + tal_resize(&peer->peer_in, (u32)len + CRYPTOMSG_BODY_OVERHEAD); + return io_read(peer_conn, peer->peer_in, tal_count(peer->peer_in), + read_body_from_peer_done, peer); } -static struct io_plan *read_from_peer(struct io_conn *peer_conn, - struct peer *peer) +static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, + struct peer *peer) { - /* We stash the length at the end */ - size_t *buf = tal_arr(peer, size_t, 1024); assert(peer->to_peer == peer_conn); - peer->peer_in = (u8 *)buf; - return io_read_partial(peer_conn, peer->peer_in, - sizeof(size_t) * 1023, - &buf[1023], - read_from_peer_done, peer); + /* BOLT #8: + * + * ### Receiving and Decrypting Messages + * + * In order to decrypt the _next_ message in the network + * stream, the following steps are completed: + * + * 1. Read _exactly_ 18 bytes from the network buffer. + */ + peer->peer_in = tal_arr(peer, u8, CRYPTOMSG_HDR_SIZE); + return io_read(peer_conn, peer->peer_in, CRYPTOMSG_HDR_SIZE, + read_body_from_peer, peer); } static struct io_plan *subd_conn_init(struct io_conn *subd_conn, struct peer *peer) @@ -200,7 +222,7 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, tal_add_destructor2(peer_conn, destroy_peer_conn, peer); return io_duplex(peer_conn, - read_from_peer(peer_conn, peer), + read_hdr_from_peer(peer_conn, peer), write_to_peer(peer_conn, peer)); } diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 84be1f008b19..e96982bf8322 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -24,8 +24,8 @@ struct peer { /* Output buffers. */ struct msg_queue *subd_outq, *peer_outq; - /* Sent buffers (for freeing after sending) */ - const u8 *sent_to_subd, *sent_to_peer; + /* Peer sent buffer (for freeing after sending) */ + const u8 *sent_to_peer; }; /* Set up peer->to_subd; sets fd_for_subd to pass to lightningd. */ @@ -38,4 +38,8 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, /* Send this message to peer and disconnect. */ void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES); + +/* Inject a message into the output stream */ +void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); + #endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ diff --git a/wire/wire_io.h b/wire/wire_io.h index fd6d773c349e..3dd7e55a1194 100644 --- a/wire/wire_io.h +++ b/wire/wire_io.h @@ -27,7 +27,7 @@ struct io_plan *io_read_wire_(struct io_conn *conn, /* Write message from data (tal_count(data) gives length). data can be take() */ struct io_plan *io_write_wire_(struct io_conn *conn, - const u8 *data, + const u8 *data TAKES, struct io_plan *(*next)(struct io_conn *, void *), void *next_arg); From ce8b69401c197125396164b9f637f3295c6c291b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:53:29 +1030 Subject: [PATCH 0203/1530] peer_io: replace crypto_sync in daemons, use normal wire messages. Now connectd is doing the crypto, we can use normal wire io. We create helper functions to clearly differentiate between "peer" comms and intra-daemon comms though. Signed-off-by: Rusty Russell --- channeld/Makefile | 6 +-- channeld/channeld.c | 52 +++++++++++------------ closingd/Makefile | 8 ++-- closingd/closingd.c | 4 +- common/Makefile | 14 +++---- common/crypto_sync.h | 19 --------- common/peer_failed.c | 4 +- common/{crypto_sync.c => peer_io.c} | 11 +++-- common/peer_io.h | 18 ++++++++ common/read_peer_msg.c | 20 ++++----- openingd/Makefile | 10 ++--- openingd/dualopend.c | 64 ++++++++++++++--------------- openingd/openingd.c | 28 ++++++------- 13 files changed, 128 insertions(+), 130 deletions(-) delete mode 100644 common/crypto_sync.h rename common/{crypto_sync.c => peer_io.c} (89%) create mode 100644 common/peer_io.h diff --git a/channeld/Makefile b/channeld/Makefile index 6f26ef416219..4b660bb64325 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -41,7 +41,6 @@ CHANNELD_COMMON_OBJS := \ common/channel_id.o \ common/channel_type.o \ common/crypto_state.o \ - common/crypto_sync.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ @@ -50,9 +49,10 @@ CHANNELD_COMMON_OBJS := \ common/ecdh_hsmd.o \ common/features.o \ common/fee_states.o \ - common/status_wiregen.o \ - common/peer_status_wiregen.o \ common/gossip_rcvd_filter.o \ + common/peer_io.o \ + common/peer_status_wiregen.o \ + common/status_wiregen.o \ common/gossip_store.o \ common/hmac.o \ common/htlc_state.o \ diff --git a/channeld/channeld.c b/channeld/channeld.c index de1a4741bf59..86d5f015b656 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -29,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -270,7 +270,7 @@ static void maybe_send_stfu(struct peer *peer) if (!peer->stfu_sent[LOCAL] && !pending_updates(peer->channel, LOCAL, false)) { u8 *msg = towire_stfu(NULL, &peer->channel_id, peer->stfu_initiator == LOCAL); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); peer->stfu_sent[LOCAL] = true; } @@ -517,7 +517,7 @@ static void send_announcement_signatures(struct peer *peer) NULL, &peer->channel_id, &peer->short_channel_ids[LOCAL], &peer->announcement_node_sigs[LOCAL], &peer->announcement_bitcoin_sigs[LOCAL]); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } /* Tentatively create a channel_announcement, possibly with invalid @@ -971,7 +971,7 @@ static void maybe_send_shutdown(struct peer *peer) msg = towire_shutdown(NULL, &peer->channel_id, peer->final_scriptpubkey, tlvs); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); peer->send_shutdown = false; peer->shutdown_sent[LOCAL] = true; billboard_update(peer); @@ -1124,7 +1124,7 @@ static void send_ping(struct peer *peer) exit(0); } - sync_crypto_write_no_delay(peer->pps, take(make_ping(NULL, 1, 0))); + peer_write_no_delay(peer->pps, take(make_ping(NULL, 1, 0))); peer->expecting_pong = PONG_EXPECTED_PROBING; set_ping_timer(peer); } @@ -1326,7 +1326,7 @@ static void send_commit(struct peer *peer) msg = towire_update_fee(NULL, &peer->channel_id, feerate_target); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } } @@ -1342,7 +1342,7 @@ static void send_commit(struct peer *peer) &peer->channel_id, our_blockheight); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } } @@ -1416,7 +1416,7 @@ static void send_commit(struct peer *peer) msg = towire_commitment_signed(NULL, &peer->channel_id, &commit_sig.s, raw_sigs(tmpctx, htlc_sigs)); - sync_crypto_write_no_delay(peer->pps, take(msg)); + peer_write_no_delay(peer->pps, take(msg)); maybe_send_shutdown(peer); @@ -1584,7 +1584,7 @@ static void send_revocation(struct peer *peer, WIRE_CHANNELD_GOT_COMMITSIG_REPLY); /* Now we can finally send revoke_and_ack to peer */ - sync_crypto_write_no_delay(peer->pps, take(msg)); + peer_write_no_delay(peer->pps, take(msg)); } static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) @@ -2361,7 +2361,7 @@ static void resend_revoke(struct peer *peer) struct pubkey point; /* Current commit is peer->next_index[LOCAL]-1, revoke prior */ u8 *msg = make_revocation_msg(peer, peer->next_index[LOCAL]-2, &point); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } static void send_fail_or_fulfill(struct peer *peer, const struct htlc *h) @@ -2387,7 +2387,7 @@ static void send_fail_or_fulfill(struct peer *peer, const struct htlc *h) peer_failed_warn(peer->pps, &peer->channel_id, "HTLC %"PRIu64" state %s not failed/fulfilled", h->id, htlc_state_name(h->state)); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } static int cmp_changed_htlc_id(const struct changed_htlc *a, @@ -2490,7 +2490,7 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) , tlvs #endif ); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } } @@ -2498,12 +2498,12 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) if (peer->channel->opener == LOCAL) { msg = towire_update_fee(NULL, &peer->channel_id, channel_feerate(peer->channel, REMOTE)); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); if (peer->channel->lease_expiry > 0) { msg = towire_update_blockheight(NULL, &peer->channel_id, channel_blockheight(peer->channel, REMOTE)); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } } @@ -2517,7 +2517,7 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) msg = towire_commitment_signed(NULL, &peer->channel_id, &commit_sig.s, raw_sigs(tmpctx, htlc_sigs)); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); /* If we have already received the revocation for the previous, the * other side shouldn't be asking for a retransmit! */ @@ -2899,7 +2899,7 @@ static void peer_reconnect(struct peer *peer, ); } - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); peer_billboard(false, "Sent reestablish, waiting for theirs"); bool soft_error = peer->funding_locked[REMOTE] @@ -2917,7 +2917,7 @@ static void peer_reconnect(struct peer *peer, * before we've reestablished channel). */ do { clean_tmpctx(); - msg = sync_crypto_read(tmpctx, peer->pps); + msg = peer_read(tmpctx, peer->pps); } while (channeld_handle_custommsg(msg) || handle_peer_gossip_or_error(peer->pps, &peer->channel_id, soft_error, msg) || @@ -3003,7 +3003,7 @@ static void peer_reconnect(struct peer *peer, msg = towire_funding_locked(NULL, &peer->channel_id, &peer->next_local_per_commit); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } /* Note: next_index is the index of the current commit we're working @@ -3305,7 +3305,7 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) msg = towire_funding_locked(NULL, &peer->channel_id, &peer->next_local_per_commit); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); peer->funding_locked[LOCAL] = true; } @@ -3371,7 +3371,7 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) , tlvs #endif ); - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); start_commit_timer(peer); /* Tell the master. */ msg = towire_channeld_offer_htlc_reply(NULL, peer->htlc_id, @@ -3604,7 +3604,7 @@ static void handle_send_error(struct peer *peer, const u8 *msg) if (!fromwire_channeld_send_error(msg, msg, &reason)) master_badmsg(WIRE_CHANNELD_SEND_ERROR, msg); status_debug("Send error reason: %s", reason); - sync_crypto_write(peer->pps, + peer_write(peer->pps, take(towire_errorfmt(NULL, &peer->channel_id, "%s", reason))); @@ -3632,7 +3632,7 @@ static void handle_send_ping(struct peer *peer, const u8 *msg) if (tal_count(ping) > 65535) status_failed(STATUS_FAIL_MASTER_IO, "Oversize ping"); - sync_crypto_write_no_delay(peer->pps, take(ping)); + peer_write_no_delay(peer->pps, take(ping)); /* Since we're doing this manually, kill and restart timer. */ status_debug("sending ping expecting %sresponse", @@ -3714,7 +3714,7 @@ static void channeld_send_custommsg(struct peer *peer, const u8 *msg) u8 *inner; if (!fromwire_custommsg_out(tmpctx, msg, &inner)) master_badmsg(WIRE_CUSTOMMSG_OUT, msg); - sync_crypto_write(peer->pps, take(inner)); + peer_write(peer->pps, take(inner)); } static void req_in(struct peer *peer, const u8 *msg) @@ -4012,7 +4012,7 @@ static void init_channel(struct peer *peer) /* If we have a messages to send, send them immediately */ if (fwd_msg) - sync_crypto_write(peer->pps, take(fwd_msg)); + peer_write(peer->pps, take(fwd_msg)); /* Reenable channel */ channel_announcement_negotiate(peer); @@ -4025,7 +4025,7 @@ static void try_read_gossip_store(struct peer *peer) u8 *msg = gossip_store_next(tmpctx, peer->pps); if (msg) - sync_crypto_write(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } int main(int argc, char *argv[]) @@ -4146,7 +4146,7 @@ int main(int argc, char *argv[]) req_in(peer, msg); } else if (FD_ISSET(peer->pps->peer_fd, &rfds)) { /* This could take forever, but who cares? */ - msg = sync_crypto_read(tmpctx, peer->pps); + msg = peer_read(tmpctx, peer->pps); peer_in(peer, msg); } else if (FD_ISSET(peer->pps->gossip_fd, &rfds)) { msg = wire_sync_read(tmpctx, peer->pps->gossip_fd); diff --git a/closingd/Makefile b/closingd/Makefile index 6fca67f75303..d641d78574a6 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -27,14 +27,11 @@ CLOSINGD_COMMON_OBJS := \ common/channel_id.o \ common/close_tx.o \ common/crypto_state.o \ - common/crypto_sync.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ - common/dev_disconnect.o \ common/derive_basepoints.o \ - common/peer_status_wiregen.o \ - common/status_wiregen.o \ + common/dev_disconnect.o \ common/gossip_rcvd_filter.o \ common/gossip_store.o \ common/htlc_wire.o \ @@ -45,11 +42,14 @@ CLOSINGD_COMMON_OBJS := \ common/onionreply.o \ common/peer_billboard.o \ common/peer_failed.o \ + common/peer_io.o \ + common/peer_status_wiregen.o \ common/per_peer_state.o \ common/permute_tx.o \ common/ping.o \ common/psbt_open.o \ common/pseudorand.o \ + common/status_wiregen.o \ common/read_peer_msg.o \ common/setup.o \ common/socket_close.o \ diff --git a/closingd/closingd.c b/closingd/closingd.c index b3f54e3b7dfb..84dc438b5d8b 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -6,12 +6,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -214,7 +214,7 @@ static void send_offer(struct per_peer_state *pps, msg = towire_closing_signed(NULL, channel_id, fee_to_offer, &our_sig.s, close_tlvs); - sync_crypto_write(pps, take(msg)); + peer_write(pps, take(msg)); } static void tell_master_their_offer(const struct bitcoin_signature *their_sig, diff --git a/common/Makefile b/common/Makefile index 2f5368ad6f6a..d4f9ae8d372a 100644 --- a/common/Makefile +++ b/common/Makefile @@ -9,8 +9,8 @@ COMMON_SRC_NOGEN := \ common/bigsize.c \ common/billboard.c \ common/bip32.c \ - common/blinding.c \ common/blindedpath.c \ + common/blinding.c \ common/blockheight_states.c \ common/bolt11.c \ common/bolt11_json.c \ @@ -19,11 +19,10 @@ COMMON_SRC_NOGEN := \ common/channel_config.c \ common/channel_id.c \ common/channel_type.c \ - common/coin_mvt.c \ common/close_tx.c \ + common/coin_mvt.c \ common/configdir.c \ common/crypto_state.c \ - common/crypto_sync.c \ common/cryptomsg.c \ common/daemon.c \ common/daemon_conn.c \ @@ -38,6 +37,7 @@ COMMON_SRC_NOGEN := \ common/fp16.c \ common/gossip_rcvd_filter.c \ common/gossip_store.c \ + common/gossmap.c \ common/hash_u5.c \ common/hmac.c \ common/hsm_encryption.c \ @@ -54,7 +54,6 @@ COMMON_SRC_NOGEN := \ common/json_tok.c \ common/key_derive.c \ common/keyset.c \ - common/gossmap.c \ common/lease_rates.c \ common/memleak.c \ common/msg_queue.c \ @@ -62,15 +61,16 @@ COMMON_SRC_NOGEN := \ common/onion.c \ common/onionreply.c \ common/param.c \ - common/penalty_base.c \ - common/per_peer_state.c \ common/peer_billboard.c \ common/peer_failed.c \ + common/peer_io.c \ + common/penalty_base.c \ + common/per_peer_state.c \ common/permute_tx.c \ common/ping.c \ + common/private_channel_announcement.c \ common/psbt_internal.c \ common/psbt_open.c \ - common/private_channel_announcement.c \ common/pseudorand.c \ common/random_select.c \ common/read_peer_msg.c \ diff --git a/common/crypto_sync.h b/common/crypto_sync.h deleted file mode 100644 index 29816289d9df..000000000000 --- a/common/crypto_sync.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LIGHTNING_COMMON_CRYPTO_SYNC_H -#define LIGHTNING_COMMON_CRYPTO_SYNC_H -#include "config.h" -#include -#include - -struct per_peer_state; - -/* Exits with peer_failed_connection_lost() if write fails. */ -void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES); - -/* Same, but disabled nagle for this message. */ -void sync_crypto_write_no_delay(struct per_peer_state *pps, - const void *msg TAKES); - -/* Exits with peer_failed_connection_lost() if can't read packet. */ -u8 *sync_crypto_read(const tal_t *ctx, struct per_peer_state *pps); - -#endif /* LIGHTNING_COMMON_CRYPTO_SYNC_H */ diff --git a/common/peer_failed.c b/common/peer_failed.c index 97ec96cfab31..909161b650c3 100644 --- a/common/peer_failed.c +++ b/common/peer_failed.c @@ -2,9 +2,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -38,7 +38,7 @@ peer_failed(struct per_peer_state *pps, } else { msg = towire_errorfmt(desc, channel_id, "%s", desc); } - sync_crypto_write(pps, msg); + peer_write(pps, msg); /* Tell master the error so it can re-xmit. */ msg = towire_status_peer_error(NULL, channel_id, diff --git a/common/crypto_sync.c b/common/peer_io.c similarity index 89% rename from common/crypto_sync.c rename to common/peer_io.c index 58dc8b2a06be..43b9a027e18f 100644 --- a/common/crypto_sync.c +++ b/common/peer_io.c @@ -1,9 +1,9 @@ #include "config.h" #include -#include #include #include #include +#include #include #include #include @@ -15,7 +15,7 @@ #include #include -void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) +void peer_write(struct per_peer_state *pps, const void *msg TAKES) { #if DEVELOPER bool post_sabotage = false, post_close; @@ -64,8 +64,7 @@ void sync_crypto_write(struct per_peer_state *pps, const void *msg TAKES) * afterwards. Even if this is wrong on other non-Linux platforms, it * only means one extra packet. */ -void sync_crypto_write_no_delay(struct per_peer_state *pps, - const void *msg TAKES) +void peer_write_no_delay(struct per_peer_state *pps, const void *msg TAKES) { int val; int opt; @@ -92,13 +91,13 @@ void sync_crypto_write_no_delay(struct per_peer_state *pps, complained = true; } } - sync_crypto_write(pps, msg); + peer_write(pps, msg); val = 0; setsockopt(pps->peer_fd, IPPROTO_TCP, opt, &val, sizeof(val)); } -u8 *sync_crypto_read(const tal_t *ctx, struct per_peer_state *pps) +u8 *peer_read(const tal_t *ctx, struct per_peer_state *pps) { u8 *dec = wire_sync_read(ctx, pps->peer_fd); if (!dec) diff --git a/common/peer_io.h b/common/peer_io.h new file mode 100644 index 000000000000..0ca6670acde1 --- /dev/null +++ b/common/peer_io.h @@ -0,0 +1,18 @@ +#ifndef LIGHTNING_COMMON_PEER_IO_H +#define LIGHTNING_COMMON_PEER_IO_H +#include "config.h" +#include +#include + +struct per_peer_state; + +/* Exits with peer_failed_connection_lost() if write fails. */ +void peer_write(struct per_peer_state *pps, const void *msg TAKES); + +/* Same, but disabled nagle for this message. */ +void peer_write_no_delay(struct per_peer_state *pps, const void *msg TAKES); + +/* Exits with peer_failed_connection_lost() if can't read packet. */ +u8 *peer_read(const tal_t *ctx, struct per_peer_state *pps); + +#endif /* LIGHTNING_COMMON_PEER_IO_H */ diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index f40c15c46829..a1c0405e49a4 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -1,10 +1,10 @@ #include "config.h" #include #include -#include #include #include #include +#include #include #include #include @@ -49,7 +49,7 @@ u8 *peer_or_gossip_sync_read(const tal_t *ctx, } if (FD_ISSET(pps->peer_fd, &readfds)) { - msg = sync_crypto_read(ctx, pps); + msg = peer_read(ctx, pps); *from_gossipd = false; return msg; } @@ -121,10 +121,10 @@ void handle_gossip_msg(struct per_peer_state *pps, const u8 *msg TAKES) /* Gossipd can send us gossip messages, OR warnings */ if (fromwire_peektype(gossip) == WIRE_WARNING) { - sync_crypto_write(pps, gossip); + peer_write(pps, gossip); peer_failed_connection_lost(); } else { - sync_crypto_write(pps, gossip); + peer_write(pps, gossip); } } @@ -141,11 +141,11 @@ bool handle_timestamp_filter(struct per_peer_state *pps, const u8 *msg TAKES) } if (!bitcoin_blkid_eq(&chainparams->genesis_blockhash, &chain_hash)) { - sync_crypto_write(pps, - take(towire_warningfmt(NULL, NULL, - "gossip_timestamp_filter" - " for bad chain: %s", - tal_hex(tmpctx, take(msg))))); + peer_write(pps, + take(towire_warningfmt(NULL, NULL, + "gossip_timestamp_filter" + " for bad chain: %s", + tal_hex(tmpctx, take(msg))))); return true; } @@ -181,7 +181,7 @@ bool handle_peer_gossip_or_error(struct per_peer_state *pps, return true; else if (check_ping_make_pong(NULL, msg, &pong)) { if (pong) - sync_crypto_write(pps, take(pong)); + peer_write(pps, take(pong)); return true; } else if (is_msg_for_gossipd(msg)) { if (is_msg_gossip_broadcast(msg)) diff --git a/openingd/Makefile b/openingd/Makefile index 091f51ca1790..0ff843e47a7f 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -42,7 +42,6 @@ OPENINGD_COMMON_OBJS := \ common/channel_id.o \ common/channel_type.o \ common/crypto_state.o \ - common/crypto_sync.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ @@ -50,8 +49,6 @@ OPENINGD_COMMON_OBJS := \ common/dev_disconnect.o \ common/features.o \ common/fee_states.o \ - common/status_wiregen.o \ - common/peer_status_wiregen.o \ common/gossip_rcvd_filter.o \ common/gossip_store.o \ common/htlc_state.o \ @@ -65,10 +62,12 @@ OPENINGD_COMMON_OBJS := \ common/msg_queue.o \ common/node_id.o \ common/onionreply.o \ - common/penalty_base.o \ - common/per_peer_state.o \ common/peer_billboard.o \ common/peer_failed.o \ + common/peer_io.o \ + common/peer_status_wiregen.o \ + common/penalty_base.o \ + common/per_peer_state.o \ common/permute_tx.o \ common/ping.o \ common/psbt_internal.o \ @@ -79,6 +78,7 @@ OPENINGD_COMMON_OBJS := \ common/shutdown_scriptpubkey.o \ common/status.o \ common/status_wire.o \ + common/status_wiregen.o \ common/subdaemon.o \ common/type_to_string.o \ common/utils.o \ diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 78c87e6517f7..ed2eb2872211 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -396,7 +396,7 @@ static void send_shutdown(struct state *state, const u8 *final_scriptpubkey) /* FIXME: send wrong_funding */ msg = towire_shutdown(NULL, &state->channel_id, final_scriptpubkey, NULL); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); state->shutdown_sent[LOCAL] = true; } @@ -904,7 +904,7 @@ static void dualopend_send_custommsg(struct state *state, const u8 *msg) u8 *inner; if (!fromwire_custommsg_out(tmpctx, msg, &inner)) master_badmsg(WIRE_CUSTOMMSG_OUT, msg); - sync_crypto_write(state->pps, take(inner)); + peer_write(state->pps, take(inner)); } static u8 *psbt_to_tx_sigs_msg(const tal_t *ctx, @@ -1043,7 +1043,7 @@ static void handle_send_tx_sigs(struct state *state, const u8 *msg) /* Send our sigs to peer */ msg = psbt_to_tx_sigs_msg(tmpctx, state, tx_state->psbt); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); /* Notify lightningd that we've sent sigs */ wire_sync_write(REQ_FD, take(towire_dualopend_tx_sigs_sent(NULL))); @@ -1115,7 +1115,7 @@ static bool send_next(struct state *state, } sendmsg: - sync_crypto_write(state->pps, msg); + peer_write(state->pps, msg); return !finished; } @@ -1248,10 +1248,10 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) peer_wire_name(fromwire_peektype(msg)), type_to_string(tmpctx, struct channel_id, &actual)); - sync_crypto_write(state->pps, - take(towire_errorfmt(NULL, &actual, - "Multiple channels" - " unsupported"))); + peer_write(state->pps, + take(towire_errorfmt(NULL, &actual, + "Multiple channels" + " unsupported"))); tal_free(msg); continue; } @@ -1976,10 +1976,10 @@ static u8 *accepter_commits(struct state *state, master_badmsg(WIRE_DUALOPEND_SEND_TX_SIGS, msg); /* Send our commitment sigs over now */ - sync_crypto_write(state->pps, - take(towire_commitment_signed(NULL, - &state->channel_id, - &local_sig.s, NULL))); + peer_write(state->pps, + take(towire_commitment_signed(NULL, + &state->channel_id, + &local_sig.s, NULL))); return msg; } @@ -2333,7 +2333,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) &state->our_points.revocation, &state->their_points.revocation); - sync_crypto_write(state->pps, msg); + peer_write(state->pps, msg); peer_billboard(false, "channel open: accept sent, waiting for reply"); /* This is unused in this flow. We re-use @@ -2533,7 +2533,7 @@ static u8 *opener_commits(struct state *state, msg = towire_commitment_signed(tmpctx, &state->channel_id, &local_sig.s, NULL); - sync_crypto_write(state->pps, msg); + peer_write(state->pps, msg); peer_billboard(false, "channel open: commitment sent, waiting for reply"); /* Wait for the peer to send us our commitment tx signature */ @@ -2735,7 +2735,7 @@ static void opener_start(struct state *state, u8 *msg) state->channel_flags, open_tlv); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); /* This is usually a very transient state... */ peer_billboard(false, "channel open: offered, waiting for" @@ -3151,7 +3151,7 @@ static void rbf_local_start(struct state *state, u8 *msg) tx_state->tx_locktime, tx_state->feerate_per_kw_funding); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); /* ... since their reply should be immediate. */ msg = opening_negotiate_msg(tmpctx, state); @@ -3360,7 +3360,7 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) state->our_role == TX_INITIATOR ? tx_state->opener_funding : tx_state->accepter_funding); - sync_crypto_write(state->pps, msg); + peer_write(state->pps, msg); peer_billboard(false, "channel rbf: ack sent, waiting for reply"); /* We merge with RBF's we've initiated now */ @@ -3394,7 +3394,7 @@ static void send_funding_locked(struct state *state) msg = towire_funding_locked(NULL, &state->channel_id, &next_local_per_commit); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); state->funding_locked[LOCAL] = true; billboard_update(state); @@ -3440,7 +3440,7 @@ static void try_read_gossip_store(struct state *state) u8 *msg = gossip_store_next(tmpctx, state->pps); if (msg) - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); } /* Try to handle a custommsg Returns true if it was a custom message and has @@ -3550,7 +3550,7 @@ static void do_reconnect_dance(struct state *state) , tlvs #endif ); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); peer_billboard(false, "Sent reestablish, waiting for theirs"); bool soft_error = state->funding_locked[REMOTE] @@ -3561,7 +3561,7 @@ static void do_reconnect_dance(struct state *state) * before we've reestablished channel). */ do { clean_tmpctx(); - msg = sync_crypto_read(tmpctx, state->pps); + msg = peer_read(tmpctx, state->pps); } while (dualopend_handle_custommsg(msg) || handle_peer_gossip_or_error(state->pps, &state->channel_id, @@ -3616,7 +3616,7 @@ static void do_reconnect_dance(struct state *state) if (psbt_side_finalized(tx_state->psbt, state->our_role) && !state->funding_locked[REMOTE]) { msg = psbt_to_tx_sigs_msg(NULL, state, tx_state->psbt); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); } if (state->funding_locked[LOCAL]) { @@ -3701,7 +3701,7 @@ static u8 *handle_master_in(struct state *state) * surprise. */ static u8 *handle_peer_in(struct state *state) { - u8 *msg = sync_crypto_read(tmpctx, state->pps); + u8 *msg = peer_read(tmpctx, state->pps); enum peer_wire t = fromwire_peektype(msg); struct channel_id channel_id; @@ -3784,14 +3784,14 @@ static u8 *handle_peer_in(struct state *state) &state->channel_id, false, msg)) return NULL; - sync_crypto_write(state->pps, - take(towire_warningfmt(NULL, - extract_channel_id(msg, - &channel_id) ? - &channel_id : NULL, - "Unexpected message %s: %s", - peer_wire_name(t), - tal_hex(tmpctx, msg)))); + peer_write(state->pps, + take(towire_warningfmt(NULL, + extract_channel_id(msg, + &channel_id) ? + &channel_id : NULL, + "Unexpected message %s: %s", + peer_wire_name(t), + tal_hex(tmpctx, msg)))); /* FIXME: We don't actually want master to try to send an * error, since peer is transient. This is a hack. diff --git a/openingd/openingd.c b/openingd/openingd.c index fede6c8119d6..72471da3f6c5 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -21,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -150,7 +150,7 @@ static void negotiation_failed(struct state *state, bool am_opener, msg = towire_errorfmt(NULL, &state->channel_id, "You gave bad parameters: %s", errmsg); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); negotiation_aborted(state, am_opener, errmsg); } @@ -257,10 +257,10 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, peer_wire_name(fromwire_peektype(msg)), type_to_string(tmpctx, struct channel_id, &actual)); - sync_crypto_write(state->pps, - take(towire_errorfmt(NULL, &actual, - "Multiple channels" - " unsupported"))); + peer_write(state->pps, + take(towire_errorfmt(NULL, &actual, + "Multiple channels" + " unsupported"))); tal_free(msg); continue; } @@ -396,7 +396,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) &state->first_per_commitment_point[LOCAL], channel_flags, open_tlvs); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); /* This is usually a very transient state... */ peer_billboard(false, @@ -640,7 +640,7 @@ static bool funder_finalize_channel_setup(struct state *state, &state->funding.txid, state->funding.n, &sig->s); - sync_crypto_write(state->pps, msg); + peer_write(state->pps, msg); /* BOLT #2: * @@ -1050,7 +1050,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &state->first_per_commitment_point[LOCAL], accept_tlvs); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); peer_billboard(false, "Incoming channel: accepted, now waiting for them to create funding tx"); @@ -1258,7 +1258,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * surprise. */ static u8 *handle_peer_in(struct state *state) { - u8 *msg = sync_crypto_read(tmpctx, state->pps); + u8 *msg = peer_read(tmpctx, state->pps); enum peer_wire t = fromwire_peektype(msg); struct channel_id channel_id; bool extracted; @@ -1289,7 +1289,7 @@ static u8 *handle_peer_in(struct state *state) &channel_id, msg, state->pps); } - sync_crypto_write(state->pps, + peer_write(state->pps, take(towire_warningfmt(NULL, extracted ? &channel_id : NULL, "Unexpected message %s: %s", @@ -1349,7 +1349,7 @@ static void openingd_send_custommsg(struct state *state, const u8 *msg) u8 *inner; if (!fromwire_custommsg_out(tmpctx, msg, &inner)) master_badmsg(WIRE_CUSTOMMSG_OUT, msg); - sync_crypto_write(state->pps, take(inner)); + peer_write(state->pps, take(inner)); } /* Standard lightningd-fd-is-ready-to-read demux code. Again, we could hang @@ -1393,7 +1393,7 @@ static u8 *handle_master_in(struct state *state) master_badmsg(WIRE_OPENINGD_FUNDER_CANCEL, msg); msg = towire_errorfmt(NULL, &state->channel_id, "Channel open canceled by us"); - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); negotiation_aborted(state, true, "Channel open canceled by RPC"); return NULL; case WIRE_OPENINGD_DEV_MEMLEAK: @@ -1432,7 +1432,7 @@ static void try_read_gossip_store(struct state *state) u8 *msg = gossip_store_next(tmpctx, state->pps); if (msg) - sync_crypto_write(state->pps, take(msg)); + peer_write(state->pps, take(msg)); } int main(int argc, char *argv[]) From 9c0bb444b752ff9be3a5582e8fc4f496c9afa000 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:54:29 +1030 Subject: [PATCH 0204/1530] per_peer_state: remove struct crypto_state Now that connectd does the crypto, no need to hand around crypto_state. Signed-off-by: Rusty Russell --- channeld/Makefile | 1 - closingd/Makefile | 1 - common/Makefile | 2 +- common/crypto_state.c | 23 ----------------------- common/crypto_state.h | 3 --- common/per_peer_state.c | 9 ++------- common/per_peer_state.h | 6 +----- connectd/Makefile | 1 - connectd/connectd.c | 25 ++++++++++++++----------- connectd/multiplex.c | 6 +++--- connectd/multiplex.h | 4 +++- devtools/Makefile | 1 - gossipd/Makefile | 1 - lightningd/Makefile | 1 - openingd/Makefile | 1 - 15 files changed, 24 insertions(+), 61 deletions(-) delete mode 100644 common/crypto_state.c diff --git a/channeld/Makefile b/channeld/Makefile index 4b660bb64325..54b783856fba 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -40,7 +40,6 @@ CHANNELD_COMMON_OBJS := \ common/channel_config.o \ common/channel_id.o \ common/channel_type.o \ - common/crypto_state.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/closingd/Makefile b/closingd/Makefile index d641d78574a6..c9fc0570ce2e 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -26,7 +26,6 @@ CLOSINGD_COMMON_OBJS := \ common/bip32.o \ common/channel_id.o \ common/close_tx.o \ - common/crypto_state.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/common/Makefile b/common/Makefile index d4f9ae8d372a..9c8dbfe39893 100644 --- a/common/Makefile +++ b/common/Makefile @@ -22,7 +22,6 @@ COMMON_SRC_NOGEN := \ common/close_tx.c \ common/coin_mvt.c \ common/configdir.c \ - common/crypto_state.c \ common/cryptomsg.c \ common/daemon.c \ common/daemon_conn.c \ @@ -97,6 +96,7 @@ COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) \ common/closing_fee.h \ + common/crypto_state.h \ common/ecdh.h \ common/errcode.h \ common/gossip_constants.h \ diff --git a/common/crypto_state.c b/common/crypto_state.c deleted file mode 100644 index d592ddfd3500..000000000000 --- a/common/crypto_state.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "config.h" -#include -#include - -void towire_crypto_state(u8 **ptr, const struct crypto_state *cs) -{ - towire_u64(ptr, cs->rn); - towire_u64(ptr, cs->sn); - towire_secret(ptr, &cs->sk); - towire_secret(ptr, &cs->rk); - towire_secret(ptr, &cs->s_ck); - towire_secret(ptr, &cs->r_ck); -} - -void fromwire_crypto_state(const u8 **ptr, size_t *max, struct crypto_state *cs) -{ - cs->rn = fromwire_u64(ptr, max); - cs->sn = fromwire_u64(ptr, max); - fromwire_secret(ptr, max, &cs->sk); - fromwire_secret(ptr, max, &cs->rk); - fromwire_secret(ptr, max, &cs->s_ck); - fromwire_secret(ptr, max, &cs->r_ck); -} diff --git a/common/crypto_state.h b/common/crypto_state.h index c2fa658c8ce5..8a2674719961 100644 --- a/common/crypto_state.h +++ b/common/crypto_state.h @@ -12,7 +12,4 @@ struct crypto_state { struct secret s_ck, r_ck; }; -void towire_crypto_state(u8 **pptr, const struct crypto_state *cs); -void fromwire_crypto_state(const u8 **ptr, size_t *max, struct crypto_state *cs); - #endif /* LIGHTNING_COMMON_CRYPTO_STATE_H */ diff --git a/common/per_peer_state.c b/common/per_peer_state.c index 18b2472ff173..de11384e272e 100644 --- a/common/per_peer_state.c +++ b/common/per_peer_state.c @@ -19,12 +19,10 @@ static void destroy_per_peer_state(struct per_peer_state *pps) close(pps->gossip_store_fd); } -struct per_peer_state *new_per_peer_state(const tal_t *ctx, - const struct crypto_state *cs) +struct per_peer_state *new_per_peer_state(const tal_t *ctx) { struct per_peer_state *pps = tal(ctx, struct per_peer_state); - pps->cs = *cs; pps->gs = NULL; pps->peer_fd = pps->gossip_fd = pps->gossip_store_fd = -1; pps->grf = new_gossip_rcvd_filter(pps); @@ -69,7 +67,6 @@ void fromwire_gossip_state(const u8 **cursor, size_t *max, void towire_per_peer_state(u8 **pptr, const struct per_peer_state *pps) { - towire_crypto_state(pptr, &pps->cs); towire_bool(pptr, pps->gs != NULL); if (pps->gs) towire_gossip_state(pptr, pps->gs); @@ -89,11 +86,9 @@ void per_peer_state_fdpass_send(int fd, const struct per_peer_state *pps) struct per_peer_state *fromwire_per_peer_state(const tal_t *ctx, const u8 **cursor, size_t *max) { - struct crypto_state cs; struct per_peer_state *pps; - fromwire_crypto_state(cursor, max, &cs); - pps = new_per_peer_state(ctx, &cs); + pps = new_per_peer_state(ctx); if (fromwire_bool(cursor, max)) { pps->gs = tal(pps, struct gossip_state); fromwire_gossip_state(cursor, max, pps->gs); diff --git a/common/per_peer_state.h b/common/per_peer_state.h index 45bd1f172a82..e5e3b914cb6f 100644 --- a/common/per_peer_state.h +++ b/common/per_peer_state.h @@ -15,9 +15,6 @@ struct gossip_state { /* Things we hand between daemons to talk to peers. */ struct per_peer_state { - /* Cryptographic state needed to exchange messages with the peer (as - * featured in BOLT #8) */ - struct crypto_state cs; /* NULL if it's not initialized yet */ struct gossip_state *gs; /* Cache of msgs we have received, to avoid re-xmitting from store */ @@ -28,8 +25,7 @@ struct per_peer_state { /* Allocate a new per-peer state and add destructor to close fds if set; * sets fds to -1 and ->gs to NULL.. */ -struct per_peer_state *new_per_peer_state(const tal_t *ctx, - const struct crypto_state *cs); +struct per_peer_state *new_per_peer_state(const tal_t *ctx); /* Initialize the fds (must be -1 previous) */ void per_peer_state_set_fds(struct per_peer_state *pps, diff --git a/connectd/Makefile b/connectd/Makefile index 343a3624c10f..a6de259850f3 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -42,7 +42,6 @@ CONNECTD_COMMON_OBJS := \ common/bigsize.o \ common/bip32.o \ common/channel_id.o \ - common/crypto_state.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/connectd/connectd.c b/connectd/connectd.c index 2dc053923cde..fdbdefa26981 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -448,7 +448,7 @@ static struct peer *new_peer(struct daemon *daemon, struct peer *peer = tal(daemon, struct peer); peer->id = *id; - peer->pps = new_per_peer_state(peer, cs); + peer->cs = *cs; peer->final_msg = NULL; peer->subd_in = NULL; peer->peer_in = NULL; @@ -461,12 +461,6 @@ static struct peer *new_peer(struct daemon *daemon, if (!multiplex_subd_setup(peer, fd_for_subd)) return tal_free(peer); - /* If gossipd can't give us a file descriptor, we give up connecting. */ - if (!get_gossipfds(daemon, id, their_features, peer->pps)) { - close(*fd_for_subd); - return tal_free(peer); - } - peer->to_peer = tal_steal(peer, conn); peer_htable_add(&daemon->peers, peer); tal_add_destructor2(peer, destroy_peer, daemon); @@ -488,6 +482,7 @@ struct io_plan *peer_connected(struct io_conn *conn, int unsup; size_t depender, missing; int subd_fd; + struct per_peer_state *pps; peer = peer_htable_get(&daemon->peers, id); if (peer) @@ -545,20 +540,28 @@ struct io_plan *peer_connected(struct io_conn *conn, if (!peer) return io_close(conn); + pps = new_per_peer_state(tmpctx); + + /* If gossipd can't give us a file descriptor, we give up connecting. */ + if (!get_gossipfds(daemon, id, their_features, pps)) { + close(subd_fd); + return tal_free(peer); + } + /* Create message to tell master peer has connected. */ msg = towire_connectd_peer_connected(NULL, id, addr, incoming, - peer->pps, their_features); + pps, their_features); /*~ daemon_conn is a message queue for inter-daemon communication: we * queue up the `connect_peer_connected` message to tell lightningd * we have connected, and give the peer and gossip fds. */ daemon_conn_send(daemon->master, take(msg)); daemon_conn_send_fd(daemon->master, subd_fd); - daemon_conn_send_fd(daemon->master, peer->pps->gossip_fd); - daemon_conn_send_fd(daemon->master, peer->pps->gossip_store_fd); + daemon_conn_send_fd(daemon->master, pps->gossip_fd); + daemon_conn_send_fd(daemon->master, pps->gossip_store_fd); /* Don't try to close these on freeing. */ - peer->pps->gossip_store_fd = peer->pps->gossip_fd = -1; + pps->gossip_store_fd = pps->gossip_fd = -1; /*~ Now we set up this connection to read/write from subd */ return multiplex_peer_setup(conn, peer); diff --git a/connectd/multiplex.c b/connectd/multiplex.c index fd7668b3bd35..349f76111703 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -39,7 +39,7 @@ static struct io_plan *encrypt_and_send(struct peer *peer, struct peer *peer)) { /* We free this and the encrypted version in next write_to_peer */ - peer->sent_to_peer = cryptomsg_encrypt_msg(peer, &peer->pps->cs, msg); + peer->sent_to_peer = cryptomsg_encrypt_msg(peer, &peer->cs, msg); return io_write(peer->to_peer, peer->sent_to_peer, tal_bytelen(peer->sent_to_peer), @@ -127,7 +127,7 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, { u8 *decrypted; - decrypted = cryptomsg_decrypt_body(NULL, &peer->pps->cs, + decrypted = cryptomsg_decrypt_body(NULL, &peer->cs, peer->peer_in); if (!decrypted) return io_close(peer_conn); @@ -145,7 +145,7 @@ static struct io_plan *read_body_from_peer(struct io_conn *peer_conn, { u16 len; - if (!cryptomsg_decrypt_header(&peer->pps->cs, peer->peer_in, &len)) + if (!cryptomsg_decrypt_header(&peer->cs, peer->peer_in, &len)) return io_close(peer_conn); tal_resize(&peer->peer_in, (u32)len + CRYPTOMSG_BODY_OVERHEAD); diff --git a/connectd/multiplex.h b/connectd/multiplex.h index e96982bf8322..12ccfa082de4 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -2,12 +2,14 @@ #define LIGHTNING_CONNECTD_MULTIPLEX_H #include "config.h" #include +#include #include #include struct peer { struct node_id id; - struct per_peer_state *pps; + /* Counters and keys for symmetric crypto */ + struct crypto_state cs; /* Connection to the peer */ struct io_conn *to_peer; diff --git a/devtools/Makefile b/devtools/Makefile index e61f928f1161..5498db4684ce 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -21,7 +21,6 @@ DEVTOOLS_COMMON_OBJS := \ common/bolt11.o \ common/blockheight_states.o \ common/channel_id.o \ - common/crypto_state.o \ common/decode_array.o \ common/features.o \ common/fee_states.o \ diff --git a/gossipd/Makefile b/gossipd/Makefile index f31acfe92c0c..13c8eada0abb 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -37,7 +37,6 @@ GOSSIPD_COMMON_OBJS := \ common/blinding.o \ common/blindedpath.o \ common/channel_id.o \ - common/crypto_state.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/lightningd/Makefile b/lightningd/Makefile index 2cdb6b0d00b7..24c57250ae19 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -81,7 +81,6 @@ LIGHTNINGD_COMMON_OBJS := \ common/channel_type.o \ common/coin_mvt.o \ common/configdir.o \ - common/crypto_state.o \ common/daemon.o \ common/derive_basepoints.o \ common/ecdh_hsmd.o \ diff --git a/openingd/Makefile b/openingd/Makefile index 0ff843e47a7f..6cbf12546fd7 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -41,7 +41,6 @@ OPENINGD_COMMON_OBJS := \ common/channel_config.o \ common/channel_id.o \ common/channel_type.o \ - common/crypto_state.o \ common/cryptomsg.o \ common/daemon.o \ common/daemon_conn.o \ From 7a514112ec3f5f7b087529ffdd6d7b8aa111ea92 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:55:29 +1030 Subject: [PATCH 0205/1530] connectd: do dev_disconnect logic. As connectd handles more packets itself, or diverts them to/from gossipd, it's the only place we can implement the dev_disconnect logic. Signed-off-by: Rusty Russell --- channeld/Makefile | 1 - channeld/channeld.c | 1 - closingd/Makefile | 1 - common/dev_disconnect.c | 8 ++++--- common/dev_disconnect.h | 4 +++- common/peer_io.c | 31 -------------------------- common/subdaemon.c | 10 --------- connectd/connectd.c | 9 +++++++- connectd/connectd_wire.csv | 2 ++ connectd/multiplex.c | 38 ++++++++++++++++++++++++++++++++ connectd/peer_exchange_initmsg.c | 2 +- hsmd/hsmd.c | 4 ---- lightningd/connect_control.c | 11 +++++++-- lightningd/subd.c | 14 +++--------- onchaind/Makefile | 1 - openingd/Makefile | 1 - tests/test_gossip.py | 3 ++- 17 files changed, 71 insertions(+), 70 deletions(-) diff --git a/channeld/Makefile b/channeld/Makefile index 54b783856fba..2c9e89d758e2 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -44,7 +44,6 @@ CHANNELD_COMMON_OBJS := \ common/daemon.o \ common/daemon_conn.o \ common/derive_basepoints.o \ - common/dev_disconnect.o \ common/ecdh_hsmd.o \ common/features.o \ common/fee_states.o \ diff --git a/channeld/channeld.c b/channeld/channeld.c index 86d5f015b656..63f161ace784 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/closingd/Makefile b/closingd/Makefile index c9fc0570ce2e..53c732399c07 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -30,7 +30,6 @@ CLOSINGD_COMMON_OBJS := \ common/daemon.o \ common/daemon_conn.o \ common/derive_basepoints.o \ - common/dev_disconnect.o \ common/gossip_rcvd_filter.o \ common/gossip_store.o \ common/htlc_wire.o \ diff --git a/common/dev_disconnect.c b/common/dev_disconnect.c index 0dd088290541..608b2f7a842c 100644 --- a/common/dev_disconnect.c +++ b/common/dev_disconnect.c @@ -51,7 +51,7 @@ void dev_disconnect_init(int fd) dev_disconnect_fd = fd; } -enum dev_disconnect dev_disconnect(int pkt_type) +enum dev_disconnect dev_disconnect(const struct node_id *id, int pkt_type) { if (dev_disconnect_fd == -1) return DEV_DISCONNECT_NORMAL; @@ -59,7 +59,8 @@ enum dev_disconnect dev_disconnect(int pkt_type) if (!dev_disconnect_count) next_dev_disconnect(); - if (!streq(peer_wire_name(pkt_type), dev_disconnect_line+1)) + if (!dev_disconnect_line[0] + || !streq(peer_wire_name(pkt_type), dev_disconnect_line+1)) return DEV_DISCONNECT_NORMAL; if (--dev_disconnect_count != 0) { @@ -70,7 +71,8 @@ enum dev_disconnect dev_disconnect(int pkt_type) err(1, "lseek failure"); } - status_debug("dev_disconnect: %s", dev_disconnect_line); + status_peer_debug(id, "dev_disconnect: %s (%s)", dev_disconnect_line, + peer_wire_name(pkt_type)); return dev_disconnect_line[0]; } diff --git a/common/dev_disconnect.h b/common/dev_disconnect.h index 0f4ae524a4f3..e1351478dcae 100644 --- a/common/dev_disconnect.h +++ b/common/dev_disconnect.h @@ -4,6 +4,8 @@ #include #if DEVELOPER +struct node_id; + enum dev_disconnect { /* Do nothing. */ DEV_DISCONNECT_NORMAL = '=', @@ -18,7 +20,7 @@ enum dev_disconnect { }; /* Force a close fd before or after a certain packet type */ -enum dev_disconnect dev_disconnect(int pkt_type); +enum dev_disconnect dev_disconnect(const struct node_id *id, int pkt_type); /* Make next write on fd fail as if they'd disconnected. */ void dev_sabotage_fd(int fd, bool close_fd); diff --git a/common/peer_io.c b/common/peer_io.c index 43b9a027e18f..995920fd5c78 100644 --- a/common/peer_io.c +++ b/common/peer_io.c @@ -1,7 +1,6 @@ #include "config.h" #include #include -#include #include #include #include @@ -17,40 +16,10 @@ void peer_write(struct per_peer_state *pps, const void *msg TAKES) { -#if DEVELOPER - bool post_sabotage = false, post_close; - int type = fromwire_peektype(msg); -#endif - status_peer_io(LOG_IO_OUT, NULL, msg); -#if DEVELOPER - switch (dev_disconnect(type)) { - case DEV_DISCONNECT_BEFORE: - dev_sabotage_fd(pps->peer_fd, true); - peer_failed_connection_lost(); - 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 (!wire_sync_write(pps->peer_fd, msg)) peer_failed_connection_lost(); - -#if DEVELOPER - if (post_sabotage) - dev_sabotage_fd(pps->peer_fd, post_close); -#endif } /* We're happy for the kernel to batch update and gossip messages, but a diff --git a/common/subdaemon.c b/common/subdaemon.c index 6ed2e443815a..523a4e3ac3b1 100644 --- a/common/subdaemon.c +++ b/common/subdaemon.c @@ -1,5 +1,4 @@ #include "config.h" -#include #include #include #include @@ -33,14 +32,5 @@ void subdaemon_setup(int argc, char *argv[]) daemon_maybe_debug(argv); -#if DEVELOPER - for (int i = 1; i < argc; i++) { - if (strstarts(argv[i], "--dev-disconnect=")) { - dev_disconnect_init(atoi(argv[i] - + strlen("--dev-disconnect="))); - } - } -#endif - daemon_setup(argv[0], status_backtrace_print, status_backtrace_exit); } diff --git a/connectd/connectd.c b/connectd/connectd.c index fdbdefa26981..3789ec1283fc 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1598,6 +1599,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) enum addr_listen_announce *proposed_listen_announce; struct wireaddr *announcable; char *tor_password; + bool dev_disconnect; /* Fields which require allocation are allocated off daemon */ if (!fromwire_connectd_init( @@ -1613,7 +1615,8 @@ static void connect_init(struct daemon *daemon, const u8 *msg) &daemon->use_v3_autotor, &daemon->timeout_secs, &daemon->websocket_helper, - &daemon->websocket_port)) { + &daemon->websocket_port, + &dev_disconnect)) { /* This is a helper which prints the type expected and the actual * message, then exits (it should never be called!). */ master_badmsg(WIRE_CONNECTD_INIT, msg); @@ -1657,6 +1660,10 @@ static void connect_init(struct daemon *daemon, const u8 *msg) take(towire_connectd_init_reply(NULL, binding, announcable))); +#if DEVELOPER + if (dev_disconnect) + dev_disconnect_init(5); +#endif } /*~ lightningd tells us to go! */ diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index bddaf972af7e..351f78d31039 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -21,6 +21,8 @@ msgdata,connectd_init,use_v3_autotor,bool, msgdata,connectd_init,timeout_secs,u32, msgdata,connectd_init,websocket_helper,wirestring, msgdata,connectd_init,websocket_port,u16, +# If this is set, then fd 5 is dev_disconnect_fd. +msgdata,connectd_init,dev_disconnect,bool, # Connectd->master, here are the addresses I bound, can announce. msgtype,connectd_init_reply,2100 diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 349f76111703..52e43c7650aa 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,6 +12,7 @@ #include #include #include +#include #include void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) @@ -32,12 +34,48 @@ static struct io_plan *after_final_msg(struct io_conn *peer_conn, return io_close(peer_conn); } +#if DEVELOPER +static struct io_plan *write_to_peer(struct io_conn *peer_conn, + struct peer *peer); + +static struct io_plan *dev_leave_hanging(struct io_conn *peer_conn, + struct peer *peer) +{ + /* We don't tell the peer we're disconnecting, but from now on + * our writes go nowhere, and there's nothing to read. */ + dev_sabotage_fd(io_conn_fd(peer_conn), false); + return write_to_peer(peer_conn, peer); +} +#endif /* DEVELOPER */ + static struct io_plan *encrypt_and_send(struct peer *peer, const u8 *msg TAKES, struct io_plan *(*next) (struct io_conn *peer_conn, struct peer *peer)) { +#if DEVELOPER + int type = fromwire_peektype(msg); + + switch (dev_disconnect(&peer->id, type)) { + case DEV_DISCONNECT_BEFORE: + if (taken(msg)) + tal_free(msg); + return io_close(peer->to_peer); + case DEV_DISCONNECT_AFTER: + next = (void *)io_close_cb; + break; + case DEV_DISCONNECT_BLACKHOLE: + dev_blackhole_fd(io_conn_fd(peer->to_peer)); + break; + case DEV_DISCONNECT_NORMAL: + break; + case DEV_DISCONNECT_DISABLE_AFTER: + next = dev_leave_hanging; + break; + } +#endif + /* We free this and the encrypted version in next write_to_peer */ peer->sent_to_peer = cryptomsg_encrypt_msg(peer, &peer->cs, msg); return io_write(peer->to_peer, diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 8106ab92697d..aa6aa0da8f88 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -208,7 +208,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, next = read_init; #if DEVELOPER - switch (dev_disconnect(WIRE_INIT)) { + switch (dev_disconnect(&peer->id, WIRE_INIT)) { case DEV_DISCONNECT_BEFORE: dev_sabotage_fd(io_conn_fd(conn), true); break; diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 96c76ef4e8a3..77a09622341d 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -97,10 +97,6 @@ static bool is_lightningd(const struct client *client) return client == dbid_zero_clients[0]; } -/* FIXME: This is used by debug.c. Doesn't apply to us, but lets us link. */ -extern void dev_disconnect_init(int fd); -void dev_disconnect_init(int fd UNUSED) { } - /* Pre-declare this, due to mutual recursion */ static struct io_plan *handle_client(struct io_conn *conn, struct client *c); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 3d51d0203a3f..f4aae66492bf 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -360,7 +360,13 @@ int connectd_init(struct lightningd *ld) ld->connectd = new_global_subd(ld, "lightning_connectd", connectd_wire_name, connectd_msg, - take(&hsmfd), take(&fds[1]), NULL); + take(&hsmfd), take(&fds[1]), +#if DEVELOPER + /* Not take(): we share it */ + ld->dev_disconnect_fd >= 0 ? + &ld->dev_disconnect_fd : NULL, +#endif + NULL); if (!ld->connectd) err(1, "Could not subdaemon connectd"); @@ -385,7 +391,8 @@ int connectd_init(struct lightningd *ld) ld->config.use_v3_autotor, ld->config.connection_timeout_secs, websocket_helper_path, - ld->websocket_port); + ld->websocket_port, + IFDEV(ld->dev_disconnect_fd >= 0, false)); subd_req(ld->connectd, ld->connectd, take(msg), -1, 0, connect_init_done, NULL); diff --git a/lightningd/subd.c b/lightningd/subd.c index bbd04d40cf61..1ef25e9191a0 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -188,7 +188,7 @@ static void close_taken_fds(va_list *ap) /* We use sockets, not pipes, because fds are bidir. */ static int subd(const char *path, const char *name, const char *debug_subdaemon, - int *msgfd, int dev_disconnect_fd, + int *msgfd, bool io_logging, va_list *ap) { @@ -212,7 +212,7 @@ static int subd(const char *path, const char *name, if (childpid == 0) { size_t num_args; - char *args[] = { NULL, NULL, NULL, NULL, NULL }; + char *args[] = { NULL, NULL, NULL, NULL }; int **fds = tal_arr(tmpctx, int *, 3); int stdoutfd = STDOUT_FILENO, stderrfd = STDERR_FILENO; @@ -230,10 +230,6 @@ static int subd(const char *path, const char *name, tal_arr_expand(&fds, fd); } - /* If we have a dev_disconnect_fd, add it after. */ - if (dev_disconnect_fd != -1) - tal_arr_expand(&fds, &dev_disconnect_fd); - /* Finally, the fd to report exec errors on */ tal_arr_expand(&fds, &execfail[1]); @@ -248,8 +244,6 @@ static int subd(const char *path, const char *name, if (io_logging) args[num_args++] = "--log-io"; #if DEVELOPER - if (dev_disconnect_fd != -1) - args[num_args++] = tal_fmt(NULL, "--dev-disconnect=%i", dev_disconnect_fd); if (debug_subdaemon && strends(name, debug_subdaemon)) args[num_args++] = "--debugger"; #endif @@ -700,7 +694,6 @@ static struct subd *new_subd(struct lightningd *ld, struct subd *sd = tal(ld, struct subd); int msg_fd; const char *debug_subd = NULL; - int disconnect_fd = -1; const char *shortname; assert(name != NULL); @@ -720,13 +713,12 @@ static struct subd *new_subd(struct lightningd *ld, #if DEVELOPER debug_subd = ld->dev_debug_subprocess; - disconnect_fd = ld->dev_disconnect_fd; #endif /* DEVELOPER */ const char *path = subdaemon_path(tmpctx, ld, name); sd->pid = subd(path, name, debug_subd, - &msg_fd, disconnect_fd, + &msg_fd, /* We only turn on subdaemon io logging if we're going * to print it: too stressful otherwise! */ log_print_level(sd->log) < LOG_DBG, diff --git a/onchaind/Makefile b/onchaind/Makefile index a57e2bcf9ca2..1da287a5635b 100644 --- a/onchaind/Makefile +++ b/onchaind/Makefile @@ -41,7 +41,6 @@ ONCHAIND_COMMON_OBJS := \ common/daemon.o \ common/daemon_conn.o \ common/derive_basepoints.o \ - common/dev_disconnect.o \ common/status_wiregen.o \ common/htlc_tx.o \ common/htlc_wire.o \ diff --git a/openingd/Makefile b/openingd/Makefile index 6cbf12546fd7..608268399655 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -45,7 +45,6 @@ OPENINGD_COMMON_OBJS := \ common/daemon.o \ common/daemon_conn.o \ common/derive_basepoints.o \ - common/dev_disconnect.o \ common/features.o \ common/fee_states.o \ common/gossip_rcvd_filter.o \ diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 52ba9e120a72..af6f9858d223 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -602,7 +602,8 @@ def test_gossip_no_empty_announcements(node_factory, bitcoind): # l3 sends CHANNEL_ANNOUNCEMENT to l2, but not CHANNEL_UDPATE. l1, l2, l3, l4 = node_factory.line_graph(4, opts=[{'log-level': 'io'}, {'log-level': 'io'}, - {'disconnect': ['+WIRE_CHANNEL_ANNOUNCEMENT'], + # Writes to l4 first, then l2 + {'disconnect': ['+WIRE_CHANNEL_ANNOUNCEMENT*2'], 'may_reconnect': True}, {'may_reconnect': True}], fundchannel=False) From e37a638c0c1ebf18881ddf0f86c2c88a2286893d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:56:29 +1030 Subject: [PATCH 0206/1530] connectd: do nagle by packet type. channeld can't do it any more: it's using local sockets. Connectd can do it, and simply does it by type. Amazingly, on my machine the timing change *always* caused test_channel_receivable() to fail, due to a latent race. Includes feedback from @cdecker. Signed-off-by: Rusty Russell --- channeld/channeld.c | 8 ++-- common/peer_io.c | 44 ----------------- common/peer_io.h | 3 -- connectd/connectd.c | 1 + connectd/multiplex.c | 109 ++++++++++++++++++++++++++++++++++++++++++- connectd/multiplex.h | 3 ++ tests/test_pay.py | 3 +- 7 files changed, 118 insertions(+), 53 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 63f161ace784..b9a660524dc0 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1123,7 +1123,7 @@ static void send_ping(struct peer *peer) exit(0); } - peer_write_no_delay(peer->pps, take(make_ping(NULL, 1, 0))); + peer_write(peer->pps, take(make_ping(NULL, 1, 0))); peer->expecting_pong = PONG_EXPECTED_PROBING; set_ping_timer(peer); } @@ -1415,7 +1415,7 @@ static void send_commit(struct peer *peer) msg = towire_commitment_signed(NULL, &peer->channel_id, &commit_sig.s, raw_sigs(tmpctx, htlc_sigs)); - peer_write_no_delay(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); maybe_send_shutdown(peer); @@ -1583,7 +1583,7 @@ static void send_revocation(struct peer *peer, WIRE_CHANNELD_GOT_COMMITSIG_REPLY); /* Now we can finally send revoke_and_ack to peer */ - peer_write_no_delay(peer->pps, take(msg)); + peer_write(peer->pps, take(msg)); } static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) @@ -3631,7 +3631,7 @@ static void handle_send_ping(struct peer *peer, const u8 *msg) if (tal_count(ping) > 65535) status_failed(STATUS_FAIL_MASTER_IO, "Oversize ping"); - peer_write_no_delay(peer->pps, take(ping)); + peer_write(peer->pps, take(ping)); /* Since we're doing this manually, kill and restart timer. */ status_debug("sending ping expecting %sresponse", diff --git a/common/peer_io.c b/common/peer_io.c index 995920fd5c78..587a14901ba4 100644 --- a/common/peer_io.c +++ b/common/peer_io.c @@ -22,50 +22,6 @@ void peer_write(struct per_peer_state *pps, const void *msg TAKES) peer_failed_connection_lost(); } -/* We're happy for the kernel to batch update and gossip messages, but a - * commitment message, for example, should be instantly sent. There's no - * great way of doing this, unfortunately. - * - * Setting TCP_NODELAY on Linux flushes the socket, which really means - * we'd want to toggle on then off it *after* sending. But Linux has - * TCP_CORK. On FreeBSD, it seems (looking at source) not to, so - * there we'd want to set it before the send, and reenable it - * afterwards. Even if this is wrong on other non-Linux platforms, it - * only means one extra packet. - */ -void peer_write_no_delay(struct per_peer_state *pps, const void *msg TAKES) -{ - int val; - int opt; - const char *optname; - static bool complained = false; - -#ifdef TCP_CORK - opt = TCP_CORK; - optname = "TCP_CORK"; -#elif defined(TCP_NODELAY) - opt = TCP_NODELAY; - optname = "TCP_NODELAY"; -#else -#error "Please report platform with neither TCP_CORK nor TCP_NODELAY?" -#endif - - val = 1; - if (setsockopt(pps->peer_fd, IPPROTO_TCP, opt, &val, sizeof(val)) != 0) { - /* This actually happens in testing, where we blackhole the fd */ - if (!complained) { - status_unusual("setsockopt %s=1: %s", - optname, - strerror(errno)); - complained = true; - } - } - peer_write(pps, msg); - - val = 0; - setsockopt(pps->peer_fd, IPPROTO_TCP, opt, &val, sizeof(val)); -} - u8 *peer_read(const tal_t *ctx, struct per_peer_state *pps) { u8 *dec = wire_sync_read(ctx, pps->peer_fd); diff --git a/common/peer_io.h b/common/peer_io.h index 0ca6670acde1..621e71db35af 100644 --- a/common/peer_io.h +++ b/common/peer_io.h @@ -9,9 +9,6 @@ struct per_peer_state; /* Exits with peer_failed_connection_lost() if write fails. */ void peer_write(struct per_peer_state *pps, const void *msg TAKES); -/* Same, but disabled nagle for this message. */ -void peer_write_no_delay(struct per_peer_state *pps, const void *msg TAKES); - /* Exits with peer_failed_connection_lost() if can't read packet. */ u8 *peer_read(const tal_t *ctx, struct per_peer_state *pps); diff --git a/connectd/connectd.c b/connectd/connectd.c index 3789ec1283fc..2fe0953991a9 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -454,6 +454,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->subd_in = NULL; peer->peer_in = NULL; peer->sent_to_peer = NULL; + peer->urgent = false; peer->peer_outq = msg_queue_new(peer); peer->subd_outq = msg_queue_new(peer); diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 52e43c7650aa..ccbe5854bece 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -10,8 +10,11 @@ #include #include #include +#include +#include #include #include +#include #include #include @@ -48,15 +51,118 @@ static struct io_plan *dev_leave_hanging(struct io_conn *peer_conn, } #endif /* DEVELOPER */ +/* We're happy for the kernel to batch update and gossip messages, but a + * commitment message, for example, should be instantly sent. There's no + * great way of doing this, unfortunately. + * + * Setting TCP_NODELAY on Linux flushes the socket, which really means + * we'd want to toggle on then off it *after* sending. But Linux has + * TCP_CORK. On FreeBSD, it seems (looking at source) not to, so + * there we'd want to set it before the send, and reenable it + * afterwards. Even if this is wrong on other non-Linux platforms, it + * only means one extra packet. + */ +static void set_urgent_flag(struct peer *peer, bool urgent) +{ + int val; + int opt; + const char *optname; + static bool complained = false; + + if (urgent == peer->urgent) + return; + +#ifdef TCP_CORK + opt = TCP_CORK; + optname = "TCP_CORK"; +#elif defined(TCP_NODELAY) + opt = TCP_NODELAY; + optname = "TCP_NODELAY"; +#else +#error "Please report platform with neither TCP_CORK nor TCP_NODELAY?" +#endif + + val = urgent; + if (setsockopt(io_conn_fd(peer->to_peer), + IPPROTO_TCP, opt, &val, sizeof(val)) != 0) { + /* This actually happens in testing, where we blackhole the fd */ + if (!complained) { + status_unusual("setsockopt %s=1: %s", + optname, + strerror(errno)); + complained = true; + } + } + peer->urgent = urgent; +} + +static bool is_urgent(enum peer_wire type) +{ + switch (type) { + case WIRE_INIT: + case WIRE_ERROR: + case WIRE_WARNING: + case WIRE_TX_ADD_INPUT: + case WIRE_TX_ADD_OUTPUT: + case WIRE_TX_REMOVE_INPUT: + case WIRE_TX_REMOVE_OUTPUT: + case WIRE_TX_COMPLETE: + case WIRE_TX_SIGNATURES: + case WIRE_OPEN_CHANNEL: + case WIRE_ACCEPT_CHANNEL: + case WIRE_FUNDING_CREATED: + case WIRE_FUNDING_SIGNED: + case WIRE_FUNDING_LOCKED: + case WIRE_OPEN_CHANNEL2: + case WIRE_ACCEPT_CHANNEL2: + case WIRE_INIT_RBF: + case WIRE_ACK_RBF: + case WIRE_SHUTDOWN: + case WIRE_CLOSING_SIGNED: + case WIRE_UPDATE_ADD_HTLC: + case WIRE_UPDATE_FULFILL_HTLC: + case WIRE_UPDATE_FAIL_HTLC: + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: + case WIRE_CHANNEL_REESTABLISH: + case WIRE_ANNOUNCEMENT_SIGNATURES: + case WIRE_CHANNEL_ANNOUNCEMENT: + case WIRE_NODE_ANNOUNCEMENT: + case WIRE_CHANNEL_UPDATE: + case WIRE_QUERY_SHORT_CHANNEL_IDS: + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: + case WIRE_QUERY_CHANNEL_RANGE: + case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif + return false; + + /* These are time-sensitive, and so send without delay. */ + case WIRE_PING: + case WIRE_PONG: + case WIRE_COMMITMENT_SIGNED: + case WIRE_REVOKE_AND_ACK: + return true; + }; + + /* plugins can inject other messages; assume not urgent. */ + return false; +} + static struct io_plan *encrypt_and_send(struct peer *peer, const u8 *msg TAKES, struct io_plan *(*next) (struct io_conn *peer_conn, struct peer *peer)) { -#if DEVELOPER int type = fromwire_peektype(msg); +#if DEVELOPER switch (dev_disconnect(&peer->id, type)) { case DEV_DISCONNECT_BEFORE: if (taken(msg)) @@ -75,6 +181,7 @@ static struct io_plan *encrypt_and_send(struct peer *peer, break; } #endif + set_urgent_flag(peer, is_urgent(type)); /* We free this and the encrypted version in next write_to_peer */ peer->sent_to_peer = cryptomsg_encrypt_msg(peer, &peer->cs, msg); diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 12ccfa082de4..ccd037939fd0 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -20,6 +20,9 @@ struct peer { /* Final message to send to peer (and hangup) */ u8 *final_msg; + /* When we write something which wants Nagle overridden */ + bool urgent; + /* Input buffers. */ u8 *subd_in, *peer_in; diff --git a/tests/test_pay.py b/tests/test_pay.py index 5516099d90ac..569b56f7bd46 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2339,7 +2339,8 @@ def test_channel_receivable(node_factory, bitcoind): assert l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] == Millisatoshi(0) l1.rpc.waitsendpay(payment_hash, TIMEOUT) - # Make sure l2 thinks it's all over. + # Make sure both think it's all over. + wait_for(lambda: len(l1.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) # Now, reverse should work similarly. inv = l1.rpc.invoice('any', 'inv', 'for testing') From 1f608acd4e3c58e44b95506406994dad22763c28 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:57:29 +1030 Subject: [PATCH 0207/1530] common: add routine for absolute timeouts (vs. relative). Signed-off-by: Rusty Russell --- common/timeout.c | 19 ++++++++++++++++++- common/timeout.h | 10 +++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/common/timeout.c b/common/timeout.c index d4b4b480f1bf..475733e02a45 100644 --- a/common/timeout.c +++ b/common/timeout.c @@ -31,7 +31,24 @@ struct oneshot *new_reltimer_(struct timers *timers, return t; } -void *reltimer_arg(struct oneshot *t) +struct oneshot *new_abstimer_(struct timers *timers, + const tal_t *ctx, + struct timemono expiry, + void (*cb)(void *), void *arg) +{ + struct oneshot *t = tal(ctx, struct oneshot); + + t->cb = cb; + t->arg = arg; + t->timers = timers; + timer_init(&t->timer); + timer_addmono(timers, &t->timer, expiry); + tal_add_destructor(t, destroy_timer); + + return t; +} + +void *oneshot_arg(struct oneshot *t) { return t->arg; } diff --git a/common/timeout.h b/common/timeout.h index 55b1a4b633ac..0277a072323a 100644 --- a/common/timeout.h +++ b/common/timeout.h @@ -15,8 +15,16 @@ struct oneshot *new_reltimer_(struct timers *timers, new_reltimer_((timers), (ctx), (relexpire), \ typesafe_cb(void, void *, (func), (arg)), (arg)) +struct oneshot *new_abstimer_(struct timers *timers, + const tal_t *ctx, + struct timemono expiry, + void (*cb)(void *), void *arg); +#define new_abstimer(timers, ctx, expiry, func, arg) \ + new_abstimer_((timers), (ctx), (expiry), \ + typesafe_cb(void, void *, (func), (arg)), (arg)) + /* Get timer arg. */ -void *reltimer_arg(struct oneshot *t); +void *oneshot_arg(struct oneshot *t); void timer_expired(struct timer *timer); From 029d65cf2e93afb0bfeff9e92fd3e2124f3b9a90 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:58:29 +1030 Subject: [PATCH 0208/1530] connectd: serve gossip_store file for the peer. We actually intercept the gossip_timestamp_filter, so the gossip_store mechanism inside the per-peer daemon never kicks off for normal connections. The gossipwith tool doesn't set OPT_GOSSIP_QUERIES, so it gets both, but that only effects one place. Signed-off-by: Rusty Russell --- common/gossip_store.c | 112 ++++++++++++----- common/gossip_store.h | 21 ++++ connectd/Makefile | 2 + connectd/connectd.c | 112 +++-------------- connectd/connectd.h | 145 +++++++++++++++++++++- connectd/connectd_wire.csv | 1 + connectd/multiplex.c | 225 ++++++++++++++++++++++++++++++++++- connectd/multiplex.h | 32 +---- lightningd/connect_control.c | 1 + tests/test_gossip.py | 3 +- 10 files changed, 495 insertions(+), 159 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index bc66cf110811..1ab501044004 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -44,7 +44,7 @@ void gossip_setup_timestamp_filter(struct per_peer_state *pps, lseek(pps->gossip_store_fd, 1, SEEK_SET); } -static bool timestamp_filter(const struct per_peer_state *pps, u32 timestamp) +static bool timestamp_filter(const struct gossip_state *gs, u32 timestamp) { /* BOLT #7: * @@ -53,8 +53,8 @@ static bool timestamp_filter(const struct per_peer_state *pps, u32 timestamp) * `timestamp_range`. */ /* Note that we turn first_timestamp & timestamp_range into an inclusive range */ - return timestamp >= pps->gs->timestamp_min - && timestamp <= pps->gs->timestamp_max; + return timestamp >= gs->timestamp_min + && timestamp <= gs->timestamp_max; } /* Not all the data we expected was there: rewind file */ @@ -71,8 +71,7 @@ static void failed_read(int fd, int len) lseek(fd, -len, SEEK_CUR); } -static void reopen_gossip_store(struct per_peer_state *pps, - const u8 *msg) +static void reopen_gossip_store(int *gossip_store_fd, const u8 *msg) { u64 equivalent_offset; int newfd; @@ -93,53 +92,59 @@ static void reopen_gossip_store(struct per_peer_state *pps, equivalent_offset); lseek(newfd, equivalent_offset, SEEK_SET); - close(pps->gossip_store_fd); - pps->gossip_store_fd = newfd; + close(*gossip_store_fd); + *gossip_store_fd = newfd; } -u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps) +u8 *gossip_store_iter(const tal_t *ctx, + int *gossip_store_fd, + struct gossip_state *gs, + struct gossip_rcvd_filter *grf, + size_t *off) { u8 *msg = NULL; - /* Don't read until we're initialized. */ - if (!pps->gs) - return NULL; - while (!msg) { struct gossip_hdr hdr; u32 msglen, checksum, timestamp; bool push; int type, r; - r = read(pps->gossip_store_fd, &hdr, sizeof(hdr)); + if (off) + r = pread(*gossip_store_fd, &hdr, sizeof(hdr), *off); + else + r = read(*gossip_store_fd, &hdr, sizeof(hdr)); if (r != sizeof(hdr)) { /* We expect a 0 read here at EOF */ - if (r != 0) - failed_read(pps->gossip_store_fd, r); - per_peer_state_reset_gossip_timer(pps); + if (r != 0 && off) + failed_read(*gossip_store_fd, r); return NULL; } + msglen = be32_to_cpu(hdr.len); + push = (msglen & GOSSIP_STORE_LEN_PUSH_BIT); + msglen &= GOSSIP_STORE_LEN_MASK; + /* Skip any deleted entries. */ if (be32_to_cpu(hdr.len) & GOSSIP_STORE_LEN_DELETED_BIT) { /* Skip over it. */ - lseek(pps->gossip_store_fd, - be32_to_cpu(hdr.len) & GOSSIP_STORE_LEN_MASK, - SEEK_CUR); + if (off) + *off += r + msglen; + else + lseek(*gossip_store_fd, msglen, SEEK_CUR); continue; } - msglen = be32_to_cpu(hdr.len); - push = (msglen & GOSSIP_STORE_LEN_PUSH_BIT); - msglen &= GOSSIP_STORE_LEN_MASK; - checksum = be32_to_cpu(hdr.crc); timestamp = be32_to_cpu(hdr.timestamp); msg = tal_arr(ctx, u8, msglen); - r = read(pps->gossip_store_fd, msg, msglen); + if (off) + r = pread(*gossip_store_fd, msg, msglen, *off + r); + else + r = read(*gossip_store_fd, msg, msglen); if (r != msglen) { - failed_read(pps->gossip_store_fd, r); - per_peer_state_reset_gossip_timer(pps); + if (!off) + failed_read(*gossip_store_fd, r); return NULL; } @@ -147,27 +152,74 @@ u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps) status_failed(STATUS_FAIL_INTERNAL_ERROR, "gossip_store: bad checksum offset %" PRIi64": %s", - (s64)lseek(pps->gossip_store_fd, + off ? (s64)*off : + (s64)lseek(*gossip_store_fd, 0, SEEK_CUR) - msglen, tal_hex(tmpctx, msg)); + /* Definitely processing it now */ + if (off) + *off += sizeof(hdr) + msglen; + /* Don't send back gossip they sent to us! */ - if (gossip_rcvd_filter_del(pps->grf, msg)) { + if (gossip_rcvd_filter_del(grf, msg)) { msg = tal_free(msg); continue; } type = fromwire_peektype(msg); if (type == WIRE_GOSSIP_STORE_ENDED) - reopen_gossip_store(pps, msg); + reopen_gossip_store(gossip_store_fd, msg); /* Ignore gossipd internal messages. */ else if (type != WIRE_CHANNEL_ANNOUNCEMENT && type != WIRE_CHANNEL_UPDATE && type != WIRE_NODE_ANNOUNCEMENT) msg = tal_free(msg); - else if (!push && !timestamp_filter(pps, timestamp)) + else if (!push && !timestamp_filter(gs, timestamp)) msg = tal_free(msg); } return msg; } + +u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps) +{ + u8 *msg; + + /* Don't read until we're initialized. */ + if (!pps->gs) + return NULL; + + /* FIXME: We are only caller using off == NULL */ + msg = gossip_store_iter(ctx, &pps->gossip_store_fd, + pps->gs, pps->grf, NULL); + + if (!msg) + per_peer_state_reset_gossip_timer(pps); + + return msg; +} + +size_t find_gossip_store_end(int gossip_store_fd, size_t off) +{ + /* We cheat and read first two bytes of message too. */ + struct { + struct gossip_hdr hdr; + be16 type; + } buf; + int r; + + while ((r = read(gossip_store_fd, &buf, + sizeof(buf.hdr) + sizeof(buf.type))) + == sizeof(buf.hdr) + sizeof(buf.type)) { + u32 msglen = be32_to_cpu(buf.hdr.len) & GOSSIP_STORE_LEN_MASK; + + /* Don't swallow end marker! */ + if (buf.type == CPU_TO_BE16(WIRE_GOSSIP_STORE_ENDED)) + break; + + off += sizeof(buf.hdr) + msglen; + lseek(gossip_store_fd, off, SEEK_SET); + } + return off; +} diff --git a/common/gossip_store.h b/common/gossip_store.h index b23e99f43cf6..c109c8625817 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -6,6 +6,8 @@ #include struct per_peer_state; +struct gossip_state; +struct gossip_rcvd_filter; /** * gossip_store -- On-disk storage related information @@ -43,10 +45,29 @@ struct gossip_hdr { */ u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps); +/** + * Direct store accessor: loads gossip msg from store. + * + * Returns NULL if there are no more gossip msgs. + */ +u8 *gossip_store_iter(const tal_t *ctx, + int *gossip_store_fd, + struct gossip_state *gs, + struct gossip_rcvd_filter *grf, + size_t *off); + /** * Sets up the tiemstamp filter once they told us to set it.( */ void gossip_setup_timestamp_filter(struct per_peer_state *pps, u32 first_timestamp, u32 timestamp_range); + +/** + * Gossipd will be writing to this, and it's not atomic! Safest + * way to find the "end" is to walk through. + * @old_end: 1 if no previous end. + */ +size_t find_gossip_store_end(int gossip_store_fd, size_t old_end); + #endif /* LIGHTNING_COMMON_GOSSIP_STORE_H */ diff --git a/connectd/Makefile b/connectd/Makefile index a6de259850f3..fdf21e23e484 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -50,6 +50,7 @@ CONNECTD_COMMON_OBJS := \ common/ecdh_hsmd.o \ common/features.o \ common/status_wiregen.o \ + common/gossip_store.o \ common/gossip_rcvd_filter.o \ common/key_derive.o \ common/memleak.o \ @@ -71,6 +72,7 @@ CONNECTD_COMMON_OBJS := \ common/wireaddr.o \ common/wire_error.o \ gossipd/gossipd_wiregen.o \ + gossipd/gossip_store_wiregen.o \ wire/onion$(EXP)_wiregen.o lightningd/lightning_connectd: $(CONNECTD_OBJS) $(CONNECTD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(HSMD_CLIENT_OBJS) diff --git a/connectd/connectd.c b/connectd/connectd.c index 2fe0953991a9..f22f21d9937c 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -10,9 +10,7 @@ #include "config.h" #include #include -#include #include -#include #include #include #include @@ -20,9 +18,10 @@ #include #include #include +#include +#include #include #include -#include #include #include #include @@ -61,97 +60,6 @@ #define INITIAL_WAIT_SECONDS 1 #define MAX_WAIT_SECONDS 300 -/*~ We keep a hash table (ccan/htable) of peers, which tells us what peers are - * already connected (by peer->id). */ - -/*~ The HTABLE_DEFINE_TYPE() macro needs a keyof() function to extract the key: - */ -static const struct node_id *peer_keyof(const struct peer *peer) -{ - return &peer->id; -} - -/*~ We also need to define a hashing function. siphash24 is a fast yet - * cryptographic hash in ccan/crypto/siphash24; we might be able to get away - * with a slightly faster hash with fewer guarantees, but it's good hygiene to - * use this unless it's a proven bottleneck. siphash_seed() is a function in - * common/pseudorand which sets up a seed for our hashing; it's different - * every time the program is run. */ -static size_t node_id_hash(const struct node_id *id) -{ - return siphash24(siphash_seed(), id->k, sizeof(id->k)); -} - -/*~ We also define an equality function: is this element equal to this key? */ -static bool peer_eq_node_id(const struct peer *peer, - const struct node_id *id) -{ - return node_id_eq(&peer->id, id); -} - -/*~ This defines 'struct peer_htable' which contains 'struct peer' pointers. */ -HTABLE_DEFINE_TYPE(struct peer, - peer_keyof, - node_id_hash, - peer_eq_node_id, - peer_htable); - -/*~ This is the global state, like `struct lightningd *ld` in lightningd. */ -struct daemon { - /* Who am I? */ - struct node_id id; - - /* pubkey equivalent. */ - struct pubkey mykey; - - /* Base for timeout timers, and how long to wait for init msg */ - struct timers timers; - u32 timeout_secs; - - /* Peers that we've handed to `lightningd`, which it hasn't told us - * have disconnected. */ - struct peer_htable peers; - - /* Peers we are trying to reach */ - struct list_head connecting; - - /* Connection to main daemon. */ - struct daemon_conn *master; - - /* Allow localhost to be considered "public": DEVELOPER-only option, - * but for simplicity we don't #if DEVELOPER-wrap it here. */ - bool dev_allow_localhost; - - /* We support use of a SOCKS5 proxy (e.g. Tor) */ - struct addrinfo *proxyaddr; - - /* They can tell us we must use proxy even for non-Tor addresses. */ - bool always_use_proxy; - - /* There are DNS seeds we can use to look up node addresses as a last - * resort, but doing so leaks our address so can be disabled. */ - bool use_dns; - - /* The address that the broken response returns instead of - * NXDOMAIN. NULL if we have not detected a broken resolver. */ - struct sockaddr *broken_resolver_response; - - /* File descriptors to listen on once we're activated. */ - struct listen_fd *listen_fds; - - /* Allow to define the default behavior of tor services calls*/ - bool use_v3_autotor; - - /* Our features, as lightningd told us */ - struct feature_set *our_features; - - /* Subdaemon to proxy websocket requests. */ - char *websocket_helper; - - /* If non-zero, port to listen for websocket connections. */ - u16 websocket_port; -}; - /* Peers we're trying to reach: we iterate through addrs until we succeed * or fail. */ struct connecting { @@ -448,6 +356,7 @@ static struct peer *new_peer(struct daemon *daemon, { struct peer *peer = tal(daemon, struct peer); + peer->daemon = daemon; peer->id = *id; peer->cs = *cs; peer->final_msg = NULL; @@ -457,6 +366,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->urgent = false; peer->peer_outq = msg_queue_new(peer); peer->subd_outq = msg_queue_new(peer); + peer->grf = new_gossip_rcvd_filter(peer); /* Aim for connection to shuffle data back and forth: sets up * peer->to_subd */ @@ -466,6 +376,8 @@ static struct peer *new_peer(struct daemon *daemon, peer->to_peer = tal_steal(peer, conn); peer_htable_add(&daemon->peers, peer); tal_add_destructor2(peer, destroy_peer, daemon); + + peer->gs = NULL; return peer; } @@ -550,6 +462,9 @@ struct io_plan *peer_connected(struct io_conn *conn, return tal_free(peer); } + /* Get ready for streaming gossip from the store */ + setup_peer_gossip_store(peer, daemon->our_features, their_features); + /* Create message to tell master peer has connected. */ msg = towire_connectd_peer_connected(NULL, id, addr, incoming, pps, their_features); @@ -1600,6 +1515,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) enum addr_listen_announce *proposed_listen_announce; struct wireaddr *announcable; char *tor_password; + bool dev_fast_gossip; bool dev_disconnect; /* Fields which require allocation are allocated off daemon */ @@ -1617,12 +1533,18 @@ static void connect_init(struct daemon *daemon, const u8 *msg) &daemon->timeout_secs, &daemon->websocket_helper, &daemon->websocket_port, + &dev_fast_gossip, &dev_disconnect)) { /* This is a helper which prints the type expected and the actual * message, then exits (it should never be called!). */ master_badmsg(WIRE_CONNECTD_INIT, msg); } +#if DEVELOPER + /*~ Clearly mark this as a developer-only flag! */ + daemon->dev_fast_gossip = dev_fast_gossip; +#endif + if (!pubkey_from_node_id(&daemon->mykey, &daemon->id)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Invalid id for me %s", @@ -2111,6 +2033,8 @@ int main(int argc, char *argv[]) list_head_init(&daemon->connecting); daemon->listen_fds = tal_arr(daemon, struct listen_fd, 0); timers_init(&daemon->timers, time_mono()); + daemon->gossip_store_fd = -1; + /* stdin == control */ daemon->master = daemon_conn_new(daemon, STDIN_FILENO, recv_req, NULL, daemon); diff --git a/connectd/connectd.h b/connectd/connectd.h index d78fc15c6dbd..09c79bd18e3a 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -2,14 +2,155 @@ #define LIGHTNING_CONNECTD_CONNECTD_H #include "config.h" #include +#include +#include +#include #include +#include +#include struct io_conn; struct connecting; -struct daemon; -struct node_id; struct wireaddr_internal; +/*~ We keep a hash table (ccan/htable) of peers, which tells us what peers are + * already connected (by peer->id). */ +struct peer { + /* Main daemon */ + struct daemon *daemon; + + /* The pubkey of the node */ + struct node_id id; + /* Counters and keys for symmetric crypto */ + struct crypto_state cs; + + /* Connection to the peer */ + struct io_conn *to_peer; + + /* Connection to the subdaemon */ + struct io_conn *to_subd; + + /* Final message to send to peer (and hangup) */ + u8 *final_msg; + + /* When we write something which wants Nagle overridden */ + bool urgent; + + /* Input buffers. */ + u8 *subd_in, *peer_in; + + /* Output buffers. */ + struct msg_queue *subd_outq, *peer_outq; + + /* Peer sent buffer (for freeing after sending) */ + const u8 *sent_to_peer; + + /* Gossip store. */ + struct gossip_state *gs; + /* FIXME: move into gs. */ + struct gossip_rcvd_filter *grf; + size_t gossip_store_off; + + struct oneshot *gossip_timer; +}; + +/*~ The HTABLE_DEFINE_TYPE() macro needs a keyof() function to extract the key: + */ +static const struct node_id *peer_keyof(const struct peer *peer) +{ + return &peer->id; +} + +/*~ We also need to define a hashing function. siphash24 is a fast yet + * cryptographic hash in ccan/crypto/siphash24; we might be able to get away + * with a slightly faster hash with fewer guarantees, but it's good hygiene to + * use this unless it's a proven bottleneck. siphash_seed() is a function in + * common/pseudorand which sets up a seed for our hashing; it's different + * every time the program is run. */ +static size_t node_id_hash(const struct node_id *id) +{ + return siphash24(siphash_seed(), id->k, sizeof(id->k)); +} + +/*~ We also define an equality function: is this element equal to this key? */ +static bool peer_eq_node_id(const struct peer *peer, + const struct node_id *id) +{ + return node_id_eq(&peer->id, id); +} + +/*~ This defines 'struct peer_htable' which contains 'struct peer' pointers. */ +HTABLE_DEFINE_TYPE(struct peer, + peer_keyof, + node_id_hash, + peer_eq_node_id, + peer_htable); + +/*~ This is the global state, like `struct lightningd *ld` in lightningd. */ +struct daemon { + /* Who am I? */ + struct node_id id; + + /* pubkey equivalent. */ + struct pubkey mykey; + + /* Base for timeout timers, and how long to wait for init msg */ + struct timers timers; + u32 timeout_secs; + + /* Peers that we've handed to `lightningd`, which it hasn't told us + * have disconnected. */ + struct peer_htable peers; + + /* Peers we are trying to reach */ + struct list_head connecting; + + /* Connection to main daemon. */ + struct daemon_conn *master; + + /* Allow localhost to be considered "public": DEVELOPER-only option, + * but for simplicity we don't #if DEVELOPER-wrap it here. */ + bool dev_allow_localhost; + + /* We support use of a SOCKS5 proxy (e.g. Tor) */ + struct addrinfo *proxyaddr; + + /* They can tell us we must use proxy even for non-Tor addresses. */ + bool always_use_proxy; + + /* There are DNS seeds we can use to look up node addresses as a last + * resort, but doing so leaks our address so can be disabled. */ + bool use_dns; + + /* The address that the broken response returns instead of + * NXDOMAIN. NULL if we have not detected a broken resolver. */ + struct sockaddr *broken_resolver_response; + + /* File descriptors to listen on once we're activated. */ + struct listen_fd *listen_fds; + + /* Allow to define the default behavior of tor services calls*/ + bool use_v3_autotor; + + /* Our features, as lightningd told us */ + struct feature_set *our_features; + + /* Subdaemon to proxy websocket requests. */ + char *websocket_helper; + + /* If non-zero, port to listen for websocket connections. */ + u16 websocket_port; + + /* The gossip_store */ + int gossip_store_fd; + size_t gossip_store_end; + +#if DEVELOPER + /* Hack to speed up gossip timer */ + bool dev_fast_gossip; +#endif +}; + /* Called by io_tor_connect once it has a connection out. */ struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect); diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 351f78d31039..bb5d0296f78e 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -21,6 +21,7 @@ msgdata,connectd_init,use_v3_autotor,bool, msgdata,connectd_init,timeout_secs,u32, msgdata,connectd_init,websocket_helper,wirestring, msgdata,connectd_init,websocket_port,u16, +msgdata,connectd_init,dev_fast_gossip,bool, # If this is set, then fd 5 is dev_disconnect_fd. msgdata,connectd_init,dev_disconnect,bool, diff --git a/connectd/multiplex.c b/connectd/multiplex.c index ccbe5854bece..d14119f2e33e 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -2,17 +2,28 @@ * itself, and the subdaemons. */ #include "config.h" #include +#include +#include #include #include #include +#include +#include +#include +#include #include #include +#include #include +#include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -23,6 +34,117 @@ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) msg_enqueue(peer->peer_outq, msg); } +/* Send warning, close connection to peer */ +static void send_warning(struct peer *peer, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + status_vfmt(LOG_UNUSUAL, &peer->id, fmt, ap); + va_end(ap); + + /* Close locally, send msg as final warning */ + io_close(peer->to_subd); + + va_start(ap, fmt); + peer->final_msg = towire_warningfmtv(peer, NULL, fmt, ap); + va_end(ap); +} + +/* Either for initial setup, or when they ask by timestamp */ +static bool setup_gossip_filter(struct peer *peer, + u32 first_timestamp, + u32 timestamp_range) +{ + bool immediate_sync; + + /* If this is the first filter, we gossip sync immediately. */ + if (!peer->gs) { + peer->gs = tal(peer, struct gossip_state); + peer->gs->next_gossip = time_mono(); + immediate_sync = true; + } else + immediate_sync = false; + + /* BOLT #7: + * + * The receiver: + * - SHOULD send all gossip messages whose `timestamp` is greater or + * equal to `first_timestamp`, and less than `first_timestamp` plus + * `timestamp_range`. + * - MAY wait for the next outgoing gossip flush to send these. + * ... + * - SHOULD restrict future gossip messages to those whose `timestamp` + * is greater or equal to `first_timestamp`, and less than + * `first_timestamp` plus `timestamp_range`. + */ + peer->gs->timestamp_min = first_timestamp; + peer->gs->timestamp_max = first_timestamp + timestamp_range - 1; + /* Make sure we never leave it on an impossible value. */ + if (peer->gs->timestamp_max < peer->gs->timestamp_min) + peer->gs->timestamp_max = UINT32_MAX; + + peer->gossip_store_off = 1; + return immediate_sync; +} + +/* This is called once we need it: otherwise, the gossip_store may not exist, + * since we start at the same time as gossipd itself. */ +static void setup_gossip_store(struct daemon *daemon) +{ + daemon->gossip_store_fd = open(GOSSIP_STORE_FILENAME, O_RDONLY); + if (daemon->gossip_store_fd < 0) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Opening gossip_store %s: %s", + GOSSIP_STORE_FILENAME, strerror(errno)); + /* gossipd will be writing to this, and it's not atomic! Safest + * way to find the "end" is to walk through. */ + daemon->gossip_store_end + = find_gossip_store_end(daemon->gossip_store_fd, 1); +} + +void setup_peer_gossip_store(struct peer *peer, + const struct feature_set *our_features, + const u8 *their_features) +{ + /* Lazy setup */ + if (peer->daemon->gossip_store_fd == -1) + setup_gossip_store(peer->daemon); + + peer->gossip_timer = NULL; + + /* BOLT #7: + * + * A node: + * - if the `gossip_queries` feature is negotiated: + * - MUST NOT relay any gossip messages it did not generate itself, + * unless explicitly requested. + */ + if (feature_negotiated(our_features, their_features, OPT_GOSSIP_QUERIES)) + return; + + setup_gossip_filter(peer, 0, UINT32_MAX); + + /* BOLT #7: + * + * - upon receiving an `init` message with the + * `initial_routing_sync` flag set to 1: + * - SHOULD send gossip messages for all known channels and + * nodes, as if they were just received. + * - if the `initial_routing_sync` flag is set to 0, OR if the + * initial sync was completed: + * - SHOULD resume normal operation, as specified in the + * following [Rebroadcasting](#rebroadcasting) section. + */ + if (!feature_offered(their_features, OPT_INITIAL_ROUTING_SYNC)) { + /* During tests, particularly, we find that the gossip_store + * moves fast, so make sure it really does start at the end. */ + peer->gossip_store_off + = find_gossip_store_end(peer->daemon->gossip_store_fd, + peer->daemon->gossip_store_end); + } +} + /* These four function handle subd->peer */ static struct io_plan *after_final_msg(struct io_conn *peer_conn, struct peer *peer) @@ -191,6 +313,87 @@ static struct io_plan *encrypt_and_send(struct peer *peer, next, peer); } +/* Kicks off write_to_peer() to look for more gossip to send from store */ +static void wake_gossip(struct peer *peer) +{ + peer->gossip_timer = NULL; + io_wake(peer->peer_outq); +} + +/* If we are streaming gossip, get something from gossip store */ +static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) +{ + u8 *msg; + + /* Not streaming yet? */ + if (!peer->gs) + return NULL; + + /* Still waiting for timer? */ + if (peer->gossip_timer != NULL) + return NULL; + + msg = gossip_store_iter(ctx, &peer->daemon->gossip_store_fd, + peer->gs, peer->grf, &peer->gossip_store_off); + + /* Cache highest valid offset (FIXME: doesn't really work when + * gossip_store gets rewritten!) */ + if (peer->gossip_store_off > peer->daemon->gossip_store_end) + peer->daemon->gossip_store_end = peer->gossip_store_off; + + if (msg) { + status_peer_io(LOG_IO_OUT, &peer->id, msg); + return msg; + } + + /* BOLT #7: + * + * A node: + *... + * - SHOULD flush outgoing gossip messages once every 60 seconds, + * independently of the arrival times of the messages. + * - Note: this results in staggered announcements that are unique + * (not duplicated). + */ + /* We do 60 seconds from *start*, not from *now* */ + peer->gs->next_gossip + = timemono_add(time_mono(), + time_from_sec(GOSSIP_FLUSH_INTERVAL( + peer->daemon->dev_fast_gossip))); + peer->gossip_timer = new_abstimer(&peer->daemon->timers, peer, + peer->gs->next_gossip, + wake_gossip, peer); + return NULL; +} + +/* We only handle gossip_timestamp_filter for now */ +static bool handle_message_locally(struct peer *peer, const u8 *msg) +{ + struct bitcoin_blkid chain_hash; + u32 first_timestamp, timestamp_range; + + /* We remember these so we don't rexmit them */ + if (is_msg_gossip_broadcast(msg)) + gossip_rcvd_filter_add(peer->grf, msg); + + if (!fromwire_gossip_timestamp_filter(msg, &chain_hash, + &first_timestamp, + ×tamp_range)) { + return false; + } + + if (!bitcoin_blkid_eq(&chainparams->genesis_blockhash, &chain_hash)) { + send_warning(peer, "gossip_timestamp_filter for bad chain: %s", + tal_hex(tmpctx, msg)); + return true; + } + + /* Returns true the first time. */ + if (setup_gossip_filter(peer, first_timestamp, timestamp_range)) + wake_gossip(peer); + return true; +} + static struct io_plan *write_to_peer(struct io_conn *peer_conn, struct peer *peer) { @@ -211,12 +414,16 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, peer->final_msg, after_final_msg); } - /* Tell them to read again, */ - io_wake(&peer->subd_in); - - /* Wait for them to wake us */ - return msg_queue_wait(peer_conn, peer->peer_outq, - write_to_peer, peer); + /* If they want us to send gossip, do so now. */ + msg = maybe_from_gossip_store(NULL, peer); + if (!msg) { + /* Tell them to read again, */ + io_wake(&peer->subd_in); + + /* Wait for them to wake us */ + return msg_queue_wait(peer_conn, peer->peer_outq, + write_to_peer, peer); + } } return encrypt_and_send(peer, take(msg), write_to_peer); @@ -278,6 +485,12 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return io_close(peer_conn); tal_free(peer->peer_in); + /* If we swallow this, just try again. */ + if (handle_message_locally(peer, decrypted)) { + tal_free(decrypted); + return read_hdr_from_peer(peer_conn, peer); + } + /* Tell them to write. */ msg_enqueue(peer->subd_outq, take(decrypted)); diff --git a/connectd/multiplex.h b/connectd/multiplex.h index ccd037939fd0..524e4829966a 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -6,32 +6,9 @@ #include #include -struct peer { - struct node_id id; - /* Counters and keys for symmetric crypto */ - struct crypto_state cs; - - /* Connection to the peer */ - struct io_conn *to_peer; - - /* Connection to the subdaemon */ - struct io_conn *to_subd; - - /* Final message to send to peer (and hangup) */ - u8 *final_msg; - - /* When we write something which wants Nagle overridden */ - bool urgent; - - /* Input buffers. */ - u8 *subd_in, *peer_in; - - /* Output buffers. */ - struct msg_queue *subd_outq, *peer_outq; - - /* Peer sent buffer (for freeing after sending) */ - const u8 *sent_to_peer; -}; +struct peer; +struct io_conn; +struct feature_set; /* Set up peer->to_subd; sets fd_for_subd to pass to lightningd. */ bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd); @@ -47,4 +24,7 @@ void multiplex_final_msg(struct peer *peer, /* Inject a message into the output stream */ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); +void setup_peer_gossip_store(struct peer *peer, + const struct feature_set *our_features, + const u8 *their_features); #endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index f4aae66492bf..a1db898c4d52 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -392,6 +392,7 @@ int connectd_init(struct lightningd *ld) ld->config.connection_timeout_secs, websocket_helper_path, ld->websocket_port, + IFDEV(ld->dev_fast_gossip, false), IFDEV(ld->dev_disconnect_fd >= 0, false)); subd_req(ld->connectd, ld->connectd, take(msg), -1, 0, diff --git a/tests/test_gossip.py b/tests/test_gossip.py index af6f9858d223..75879bf95b5b 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1335,7 +1335,8 @@ def test_gossipwith(node_factory): num_msgs += 1 # one channel announcement, two channel_updates, two node announcements. - assert num_msgs == 5 + # FIXME: Currently gets double gossip! + assert num_msgs == 5 * 2 def test_gossip_notices_close(node_factory, bitcoind): From 6115ed02e8f9491c4f213899bde1e689561c5abe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 8 Jan 2022 23:59:29 +1030 Subject: [PATCH 0209/1530] subdaemons: don't stream gossip_store at all. We now let gossipd do it. This also means there's nothing left in 'struct per_peer_state' to send across the wire (the fds are sent separately), so that gets removed from wire messages too. Signed-off-by: Rusty Russell --- channeld/channeld.c | 39 +++------ channeld/channeld_wire.csv | 3 - closingd/closingd.c | 11 ++- closingd/closingd_wire.csv | 3 - common/gossip_store.c | 51 ----------- common/gossip_store.h | 16 ---- common/peer_failed.c | 4 +- common/peer_status_wire.csv | 1 - common/per_peer_state.c | 95 ++------------------- common/per_peer_state.h | 32 +------ common/read_peer_msg.c | 67 +++------------ connectd/connectd.c | 34 +++----- connectd/connectd_gossipd_wire.csv | 7 +- connectd/connectd_wire.csv | 3 - gossipd/gossipd.c | 55 +----------- gossipd/test/run-onion_message.c | 7 +- hsmd/hsmd.c | 3 + lightningd/channel_control.c | 10 +-- lightningd/closing_control.c | 3 - lightningd/connect_control.c | 6 +- lightningd/dual_open_control.c | 21 +++-- lightningd/opening_common.c | 5 +- lightningd/opening_control.c | 30 +++---- lightningd/peer_control.c | 11 +-- lightningd/peer_control.h | 2 +- lightningd/subd.c | 9 +- lightningd/test/run-find_my_abspath.c | 5 +- lightningd/test/run-invoice-select-inchan.c | 9 +- lightningd/test/run-shuffle_fds.c | 5 +- openingd/dualopend.c | 45 +++------- openingd/dualopend_wire.csv | 5 -- openingd/openingd.c | 43 ++-------- openingd/openingd_wire.csv | 6 -- tests/test_gossip.py | 3 +- wallet/test/run-wallet.c | 9 +- 35 files changed, 148 insertions(+), 510 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index b9a660524dc0..2cccbfefb32c 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -45,9 +46,9 @@ #include #include -/* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = HSM */ +/* stdin == requests, 3 == peer, 4 = gossip, 5 = HSM */ #define MASTER_FD STDIN_FILENO -#define HSM_FD 6 +#define HSM_FD 5 enum pong_expect_type { /* We weren't expecting a ping reply */ @@ -160,6 +161,9 @@ struct peer { #if DEVELOPER /* If set, don't fire commit counter when this hits 0 */ u32 *dev_disable_commit; + + /* If set, send channel_announcement after 1 second, not 30 */ + bool dev_fast_gossip; #endif /* Information used for reestablishment. */ bool last_was_revoke; @@ -638,7 +642,7 @@ static void channel_announcement_negotiate(struct peer *peer) /* Give other nodes time to notice new block. */ notleak(new_reltimer(&peer->timers, peer, - time_from_sec(GOSSIP_ANNOUNCE_DELAY(dev_fast_gossip)), + time_from_sec(GOSSIP_ANNOUNCE_DELAY(peer->dev_fast_gossip)), announce_channel, peer)); } } @@ -980,7 +984,7 @@ 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))); + take(towire_channeld_shutdown_complete(NULL))); per_peer_state_fdpass_send(MASTER_FD, peer->pps); close(MASTER_FD); } @@ -3839,6 +3843,7 @@ static void init_channel(struct peer *peer) u8 *reestablish_only; struct channel_type *channel_type; u32 *dev_disable_commit; /* Always NULL */ + bool dev_fast_gossip; #if !DEVELOPER bool dev_fail_process_onionpacket; /* Ignored */ #endif @@ -3862,7 +3867,6 @@ static void init_channel(struct peer *peer) &peer->feerate_max, &peer->feerate_penalty, &peer->their_commit_sig, - &peer->pps, &funding_pubkey[REMOTE], &points[REMOTE], &peer->remote_per_commit, @@ -3910,6 +3914,7 @@ static void init_channel(struct peer *peer) #if DEVELOPER peer->dev_disable_commit = dev_disable_commit; + peer->dev_fast_gossip = dev_fast_gossip; #endif status_debug("option_static_remotekey = %u, option_anchor_outputs = %u", @@ -3924,8 +3929,9 @@ static void init_channel(struct peer *peer) tal_dup(peer, struct penalty_base, &pbases[i])); tal_free(pbases); - /* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = HSM */ - per_peer_state_set_fds(peer->pps, 3, 4, 5); + /* stdin == requests, 3 == peer, 4 = gossip */ + peer->pps = new_per_peer_state(peer); + per_peer_state_set_fds(peer->pps, 3, 4); status_debug("init %s: remote_per_commit = %s, old_remote_per_commit = %s" " next_idx_local = %"PRIu64 @@ -4019,14 +4025,6 @@ static void init_channel(struct peer *peer) billboard_update(peer); } -static void try_read_gossip_store(struct peer *peer) -{ - u8 *msg = gossip_store_next(tmpctx, peer->pps); - - if (msg) - peer_write(peer->pps, take(msg)); -} - int main(int argc, char *argv[]) { setup_locale(); @@ -4088,7 +4086,6 @@ int main(int argc, char *argv[]) struct timeval timeout, *tptr; struct timer *expired; const u8 *msg; - struct timerel trel; struct timemono now = time_mono(); /* Free any temporary allocations */ @@ -4120,13 +4117,6 @@ int main(int argc, char *argv[]) tptr = &timeout; } - /* If timer to next gossip is sooner, use that instead. */ - if (time_to_next_gossip(peer->pps, &trel) - && (!tptr || time_less(trel, timeval_to_timerel(*tptr)))) { - timeout = timerel_to_timeval(trel); - tptr = &timeout; - } - if (select(nfds, &rfds, NULL, NULL, tptr) < 0) { /* Signals OK, eg. SIGUSR1 */ if (errno == EINTR) @@ -4154,8 +4144,7 @@ int main(int argc, char *argv[]) if (!msg) peer_failed_connection_lost(); handle_gossip_msg(peer->pps, take(msg)); - } else /* Lowest priority: stream from store. */ - try_read_gossip_store(peer); + } } /* We only exit when shutdown is complete. */ diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index f2eca498d7bb..f095b3cb5f61 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -8,7 +8,6 @@ #include #include #include -#include # Begin! (passes gossipd-client fd) msgtype,channeld_init,1000 @@ -28,7 +27,6 @@ msgdata,channeld_init,feerate_min,u32, msgdata,channeld_init,feerate_max,u32, msgdata,channeld_init,feerate_penalty,u32, msgdata,channeld_init,first_commit_sig,bitcoin_signature, -msgdata,channeld_init,per_peer_state,per_peer_state, msgdata,channeld_init,remote_fundingkey,pubkey, msgdata,channeld_init,remote_basepoints,basepoints, msgdata,channeld_init,remote_per_commit,pubkey, @@ -187,7 +185,6 @@ msgdata,channeld_got_shutdown,wrong_funding,?bitcoin_outpoint, # Shutdown is complete, ready for closing negotiation. + peer_fd & gossip_fd. msgtype,channeld_shutdown_complete,1025 -msgdata,channeld_shutdown_complete,per_peer_state,per_peer_state, # Re-enable commit timer. msgtype,channeld_dev_reenable_commit,1026 diff --git a/closingd/closingd.c b/closingd/closingd.c index 84dc438b5d8b..9fb74e4ba899 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -31,9 +31,9 @@ #include #include -/* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = hsmd */ +/* stdin == requests, 3 == peer, 4 = gossip, 5 = hsmd */ #define REQ_FD STDIN_FILENO -#define HSM_FD 6 +#define HSM_FD 5 static void notify(enum log_level level, const char *fmt, ...) { @@ -897,7 +897,6 @@ int main(int argc, char *argv[]) msg = wire_sync_read(tmpctx, REQ_FD); if (!fromwire_closingd_init(ctx, msg, &chainparams, - &pps, &channel_id, &funding, &funding_sats, @@ -914,12 +913,12 @@ int main(int argc, char *argv[]) &fee_negotiation_step, &fee_negotiation_step_unit, &use_quickclose, - &dev_fast_gossip, &wrong_funding)) master_badmsg(WIRE_CLOSINGD_INIT, msg); - /* stdin == requests, 3 == peer, 4 = gossip, 5 = gossip_store, 6 = hsmd */ - per_peer_state_set_fds(notleak(pps), 3, 4, 5); + /* stdin == requests, 3 == peer, 4 = gossip, 5 = hsmd */ + pps = notleak(new_per_peer_state(ctx)); + per_peer_state_set_fds(pps, 3, 4); funding_wscript = bitcoin_redeem_2of2(ctx, &funding_pubkey[LOCAL], diff --git a/closingd/closingd_wire.csv b/closingd/closingd_wire.csv index a6b066d739a3..94ab71c757e0 100644 --- a/closingd/closingd_wire.csv +++ b/closingd/closingd_wire.csv @@ -2,12 +2,10 @@ #include #include #include -#include #include # Begin! (passes peer fd, gossipd-client fd) msgtype,closingd_init,2001 msgdata,closingd_init,chainparams,chainparams, -msgdata,closingd_init,pps,per_peer_state, msgdata,closingd_init,channel_id,channel_id, msgdata,closingd_init,funding,bitcoin_outpoint, msgdata,closingd_init,funding_satoshi,amount_sat, @@ -28,7 +26,6 @@ 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,use_quickclose,bool, -msgdata,closingd_init,dev_fast_gossip,bool, msgdata,closingd_init,shutdown_wrong_funding,?bitcoin_outpoint, # Message for any commands waiting. diff --git a/common/gossip_store.c b/common/gossip_store.c index 1ab501044004..3456a483e003 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -11,39 +11,6 @@ #include #include -void gossip_setup_timestamp_filter(struct per_peer_state *pps, - u32 first_timestamp, - u32 timestamp_range) -{ - /* If this is the first filter, we gossip sync immediately. */ - if (!pps->gs) { - pps->gs = tal(pps, struct gossip_state); - pps->gs->next_gossip = time_mono(); - } - - pps->gs->timestamp_min = first_timestamp; - pps->gs->timestamp_max = first_timestamp + timestamp_range - 1; - /* Make sure we never leave it on an impossible value. */ - if (pps->gs->timestamp_max < pps->gs->timestamp_min) - pps->gs->timestamp_max = UINT32_MAX; - - /* BOLT #7: - * - * The receiver: - * - SHOULD send all gossip messages whose `timestamp` is greater or - * equal to `first_timestamp`, and less than `first_timestamp` plus - * `timestamp_range`. - * - MAY wait for the next outgoing gossip flush to send these. - * ... - * - SHOULD restrict future gossip messages to those whose `timestamp` - * is greater or equal to `first_timestamp`, and less than - * `first_timestamp` plus `timestamp_range`. - */ - - /* Restart just after header. */ - lseek(pps->gossip_store_fd, 1, SEEK_SET); -} - static bool timestamp_filter(const struct gossip_state *gs, u32 timestamp) { /* BOLT #7: @@ -182,24 +149,6 @@ u8 *gossip_store_iter(const tal_t *ctx, return msg; } -u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps) -{ - u8 *msg; - - /* Don't read until we're initialized. */ - if (!pps->gs) - return NULL; - - /* FIXME: We are only caller using off == NULL */ - msg = gossip_store_iter(ctx, &pps->gossip_store_fd, - pps->gs, pps->grf, NULL); - - if (!msg) - per_peer_state_reset_gossip_timer(pps); - - return msg; -} - size_t find_gossip_store_end(int gossip_store_fd, size_t off) { /* We cheat and read first two bytes of message too. */ diff --git a/common/gossip_store.h b/common/gossip_store.h index c109c8625817..e1d760860ae6 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -5,7 +5,6 @@ #include #include -struct per_peer_state; struct gossip_state; struct gossip_rcvd_filter; @@ -37,14 +36,6 @@ struct gossip_hdr { beint32_t timestamp; /* timestamp of msg. */ }; -/** - * Direct store accessor: loads gossip msg from store. - * - * Returns NULL and resets time_to_next_gossip(pps) if there are no - * more gossip msgs. - */ -u8 *gossip_store_next(const tal_t *ctx, struct per_peer_state *pps); - /** * Direct store accessor: loads gossip msg from store. * @@ -56,13 +47,6 @@ u8 *gossip_store_iter(const tal_t *ctx, struct gossip_rcvd_filter *grf, size_t *off); -/** - * Sets up the tiemstamp filter once they told us to set it.( - */ -void gossip_setup_timestamp_filter(struct per_peer_state *pps, - u32 first_timestamp, - u32 timestamp_range); - /** * Gossipd will be writing to this, and it's not atomic! Safest * way to find the "end" is to walk through. diff --git a/common/peer_failed.c b/common/peer_failed.c index 909161b650c3..e9f5487dafdc 100644 --- a/common/peer_failed.c +++ b/common/peer_failed.c @@ -20,7 +20,6 @@ peer_fatal_continue(const u8 *msg TAKES, const struct per_peer_state *pps) status_send_fd(pps->peer_fd); status_send_fd(pps->gossip_fd); - status_send_fd(pps->gossip_store_fd); exit(0x80 | (reason & 0xFF)); } @@ -44,7 +43,6 @@ peer_failed(struct per_peer_state *pps, msg = towire_status_peer_error(NULL, channel_id, desc, warn, - pps, msg); peer_billboard(true, desc); peer_fatal_continue(take(msg), pps); @@ -87,7 +85,7 @@ void peer_failed_received_errmsg(struct per_peer_state *pps, { u8 *msg; - msg = towire_status_peer_error(NULL, channel_id, desc, warning, pps, + msg = towire_status_peer_error(NULL, channel_id, desc, warning, NULL); peer_billboard(true, "Received %s", desc); peer_fatal_continue(take(msg), pps); diff --git a/common/peer_status_wire.csv b/common/peer_status_wire.csv index 8162443e2ffb..1fedb7ccc678 100644 --- a/common/peer_status_wire.csv +++ b/common/peer_status_wire.csv @@ -8,6 +8,5 @@ msgdata,status_peer_error,channel,channel_id, msgdata,status_peer_error,desc,wirestring, # Take a deep breath, then try reconnecting to the precious little snowflake. msgdata,status_peer_error,warning,bool, -msgdata,status_peer_error,pps,per_peer_state, msgdata,status_peer_error,len,u16, msgdata,status_peer_error,error_for_them,u8,len diff --git a/common/per_peer_state.c b/common/per_peer_state.c index de11384e272e..b33305538d27 100644 --- a/common/per_peer_state.c +++ b/common/per_peer_state.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -15,121 +14,37 @@ static void destroy_per_peer_state(struct per_peer_state *pps) close(pps->peer_fd); if (pps->gossip_fd != -1) close(pps->gossip_fd); - if (pps->gossip_store_fd != -1) - close(pps->gossip_store_fd); } struct per_peer_state *new_per_peer_state(const tal_t *ctx) { struct per_peer_state *pps = tal(ctx, struct per_peer_state); - pps->gs = NULL; - pps->peer_fd = pps->gossip_fd = pps->gossip_store_fd = -1; - pps->grf = new_gossip_rcvd_filter(pps); + pps->peer_fd = pps->gossip_fd = -1; tal_add_destructor(pps, destroy_per_peer_state); return pps; } void per_peer_state_set_fds(struct per_peer_state *pps, - int peer_fd, int gossip_fd, int gossip_store_fd) + int peer_fd, int gossip_fd) { assert(pps->peer_fd == -1); assert(pps->gossip_fd == -1); - assert(pps->gossip_store_fd == -1); pps->peer_fd = peer_fd; pps->gossip_fd = gossip_fd; - pps->gossip_store_fd = gossip_store_fd; } void per_peer_state_set_fds_arr(struct per_peer_state *pps, const int *fds) { - /* We expect 3 fds. */ - assert(tal_count(fds) == 3); - per_peer_state_set_fds(pps, fds[0], fds[1], fds[2]); -} - -void towire_gossip_state(u8 **pptr, const struct gossip_state *gs) -{ - towire_u64(pptr, gs->next_gossip.ts.tv_sec); - towire_u64(pptr, gs->next_gossip.ts.tv_nsec); - towire_u32(pptr, gs->timestamp_min); - towire_u32(pptr, gs->timestamp_max); -} - -void fromwire_gossip_state(const u8 **cursor, size_t *max, - struct gossip_state *gs) -{ - gs->next_gossip.ts.tv_sec = fromwire_u64(cursor, max); - gs->next_gossip.ts.tv_nsec = fromwire_u64(cursor, max); - gs->timestamp_min = fromwire_u32(cursor, max); - gs->timestamp_max = fromwire_u32(cursor, max); -} - -void towire_per_peer_state(u8 **pptr, const struct per_peer_state *pps) -{ - towire_bool(pptr, pps->gs != NULL); - if (pps->gs) - towire_gossip_state(pptr, pps->gs); - /* We don't pass the gossip_rcvd_filter: it's merely an optimization */ + /* We expect 2 fds. */ + assert(tal_count(fds) == 2); + per_peer_state_set_fds(pps, fds[0], fds[1]); } void per_peer_state_fdpass_send(int fd, const struct per_peer_state *pps) { assert(pps->peer_fd != -1); assert(pps->gossip_fd != -1); - assert(pps->gossip_store_fd != -1); fdpass_send(fd, pps->peer_fd); fdpass_send(fd, pps->gossip_fd); - fdpass_send(fd, pps->gossip_store_fd); -} - -struct per_peer_state *fromwire_per_peer_state(const tal_t *ctx, - const u8 **cursor, size_t *max) -{ - struct per_peer_state *pps; - - pps = new_per_peer_state(ctx); - if (fromwire_bool(cursor, max)) { - pps->gs = tal(pps, struct gossip_state); - fromwire_gossip_state(cursor, max, pps->gs); - } - return pps; -} - -/* FIXME: Put in ccan/time */ -/* Is a after b? */ -static inline bool timemono_after(struct timemono a, struct timemono b) -{ - return time_greater_(a.ts, b.ts); -} - -bool time_to_next_gossip(const struct per_peer_state *pps, - struct timerel *t) -{ - if (!pps->gs) - return false; - - struct timemono now = time_mono(); - if (timemono_after(now, pps->gs->next_gossip)) - *t = time_from_sec(0); - else - *t = timemono_between(pps->gs->next_gossip, now); - return true; -} - -/* BOLT #7: - * - * A node: - *... - * - SHOULD flush outgoing gossip messages once every 60 seconds, - * independently of the arrival times of the messages. - * - Note: this results in staggered announcements that are unique - * (not duplicated). - */ -void per_peer_state_reset_gossip_timer(struct per_peer_state *pps) -{ - struct timerel t = time_from_sec(GOSSIP_FLUSH_INTERVAL(dev_fast_gossip)); - - pps->gs->next_gossip = timemono_add(time_mono(), t); - gossip_rcvd_filter_age(pps->grf); } diff --git a/common/per_peer_state.h b/common/per_peer_state.h index e5e3b914cb6f..a81360ab3485 100644 --- a/common/per_peer_state.h +++ b/common/per_peer_state.h @@ -15,45 +15,21 @@ struct gossip_state { /* Things we hand between daemons to talk to peers. */ struct per_peer_state { - /* NULL if it's not initialized yet */ - struct gossip_state *gs; - /* Cache of msgs we have received, to avoid re-xmitting from store */ - struct gossip_rcvd_filter *grf; /* If not -1, closed on freeing */ - int peer_fd, gossip_fd, gossip_store_fd; + int peer_fd, gossip_fd; }; /* Allocate a new per-peer state and add destructor to close fds if set; - * sets fds to -1 and ->gs to NULL.. */ + * sets fds to -1. */ struct per_peer_state *new_per_peer_state(const tal_t *ctx); /* Initialize the fds (must be -1 previous) */ void per_peer_state_set_fds(struct per_peer_state *pps, - int peer_fd, int gossip_fd, int gossip_store_fd); + int peer_fd, int gossip_fd); -/* Array version of above: tal_count(fds) must be 3 */ +/* Array version of above: tal_count(fds) must be 2 */ void per_peer_state_set_fds_arr(struct per_peer_state *pps, const int *fds); -/* These routines do *part* of the work: you need to per_peer_state_fdpass_send - * or receive the three fds afterwards! */ -void towire_per_peer_state(u8 **pptr, const struct per_peer_state *pps); void per_peer_state_fdpass_send(int fd, const struct per_peer_state *pps); -struct per_peer_state *fromwire_per_peer_state(const tal_t *ctx, - const u8 **cursor, size_t *max); - -void towire_gossip_state(u8 **pptr, const struct gossip_state *gs); -void fromwire_gossip_state(const u8 **cursor, size_t *max, - struct gossip_state *gs); - -/* How long until we have to check gossip store, if any? */ -bool time_to_next_gossip(const struct per_peer_state *pps, - struct timerel *t); - -/* Reset pps->next_gossip now we've drained gossip_store */ -void per_peer_state_reset_gossip_timer(struct per_peer_state *pps); - -/* Used to speed up gossip iff DEVELOPER*/ -extern bool dev_fast_gossip; - #endif /* LIGHTNING_COMMON_PER_PEER_STATE_H */ diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index a1c0405e49a4..fe841108c6c4 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -1,8 +1,6 @@ #include "config.h" #include #include -#include -#include #include #include #include @@ -21,31 +19,15 @@ u8 *peer_or_gossip_sync_read(const tal_t *ctx, fd_set readfds; u8 *msg; - for (;;) { - struct timeval tv, *tptr; - struct timerel trel; - - if (time_to_next_gossip(pps, &trel)) { - tv = timerel_to_timeval(trel); - tptr = &tv; - } else - tptr = NULL; - - FD_ZERO(&readfds); - FD_SET(pps->peer_fd, &readfds); - FD_SET(pps->gossip_fd, &readfds); - - if (select(pps->peer_fd > pps->gossip_fd - ? pps->peer_fd + 1 : pps->gossip_fd + 1, - &readfds, NULL, NULL, tptr) != 0) - break; - - /* We timed out; look in gossip_store. Failure resets timer. */ - msg = gossip_store_next(tmpctx, pps); - if (msg) { - *from_gossipd = true; - return msg; - } + FD_ZERO(&readfds); + FD_SET(pps->peer_fd, &readfds); + FD_SET(pps->gossip_fd, &readfds); + + if (select(pps->peer_fd > pps->gossip_fd + ? pps->peer_fd + 1 : pps->gossip_fd + 1, + &readfds, NULL, NULL, NULL) <= 0) { + status_failed(STATUS_FAIL_GOSSIP_IO, + "select failed?: %s", strerror(errno)); } if (FD_ISSET(pps->peer_fd, &readfds)) { @@ -128,31 +110,6 @@ void handle_gossip_msg(struct per_peer_state *pps, const u8 *msg TAKES) } } -/* takes iff returns true */ -bool handle_timestamp_filter(struct per_peer_state *pps, const u8 *msg TAKES) -{ - struct bitcoin_blkid chain_hash; - u32 first_timestamp, timestamp_range; - - if (!fromwire_gossip_timestamp_filter(msg, &chain_hash, - &first_timestamp, - ×tamp_range)) { - return false; - } - - if (!bitcoin_blkid_eq(&chainparams->genesis_blockhash, &chain_hash)) { - peer_write(pps, - take(towire_warningfmt(NULL, NULL, - "gossip_timestamp_filter" - " for bad chain: %s", - tal_hex(tmpctx, take(msg))))); - return true; - } - - gossip_setup_timestamp_filter(pps, first_timestamp, timestamp_range); - return true; -} - bool handle_peer_gossip_or_error(struct per_peer_state *pps, const struct channel_id *channel_id, bool soft_error, @@ -177,15 +134,11 @@ bool handle_peer_gossip_or_error(struct per_peer_state *pps, goto handled; #endif - if (handle_timestamp_filter(pps, msg)) - return true; - else if (check_ping_make_pong(NULL, msg, &pong)) { + if (check_ping_make_pong(NULL, msg, &pong)) { if (pong) peer_write(pps, take(pong)); return true; } else if (is_msg_for_gossipd(msg)) { - if (is_msg_gossip_broadcast(msg)) - gossip_rcvd_filter_add(pps->grf, msg); wire_sync_write(pps->gossip_fd, msg); /* wire_sync_write takes, so don't take again. */ return true; diff --git a/connectd/connectd.c b/connectd/connectd.c index f22f21d9937c..a3c939acbbcd 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -223,7 +223,7 @@ static bool get_gossipfds(struct daemon *daemon, const u8 *their_features, struct per_peer_state *pps) { - bool gossip_queries_feature, initial_routing_sync, success; + bool gossip_queries_feature, success; u8 *msg; /*~ The way features generally work is that both sides need to offer it; @@ -232,23 +232,16 @@ static bool get_gossipfds(struct daemon *daemon, = feature_negotiated(daemon->our_features, their_features, OPT_GOSSIP_QUERIES); - /*~ `initial_routing_sync` is supported by every node, since it was in - * the initial lightning specification: it means the peer wants the - * backlog of existing gossip. */ - initial_routing_sync - = feature_offered(their_features, OPT_INITIAL_ROUTING_SYNC); - /*~ We do this communication sync, since gossipd is our friend and * it's easier. If gossipd fails, we fail. */ - msg = towire_gossipd_new_peer(NULL, id, gossip_queries_feature, - initial_routing_sync); + msg = towire_gossipd_new_peer(NULL, id, gossip_queries_feature); if (!wire_sync_write(GOSSIPCTL_FD, take(msg))) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Failed writing to gossipctl: %s", strerror(errno)); msg = wire_sync_read(tmpctx, GOSSIPCTL_FD); - if (!fromwire_gossipd_new_peer_reply(pps, msg, &success, &pps->gs)) + if (!fromwire_gossipd_new_peer_reply(msg, &success)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Failed parsing msg gossipctl: %s", tal_hex(tmpctx, msg)); @@ -261,10 +254,9 @@ static bool get_gossipfds(struct daemon *daemon, return false; } - /* Otherwise, the next thing in the socket will be the file descriptors + /* Otherwise, the next thing in the socket will be the file descriptor * for the per-peer daemon. */ pps->gossip_fd = fdpass_recv(GOSSIPCTL_FD); - pps->gossip_store_fd = fdpass_recv(GOSSIPCTL_FD); return true; } @@ -454,6 +446,7 @@ struct io_plan *peer_connected(struct io_conn *conn, if (!peer) return io_close(conn); + /* FIXME: Remove pps abstraction! */ pps = new_per_peer_state(tmpctx); /* If gossipd can't give us a file descriptor, we give up connecting. */ @@ -467,7 +460,7 @@ struct io_plan *peer_connected(struct io_conn *conn, /* Create message to tell master peer has connected. */ msg = towire_connectd_peer_connected(NULL, id, addr, incoming, - pps, their_features); + their_features); /*~ daemon_conn is a message queue for inter-daemon communication: we * queue up the `connect_peer_connected` message to tell lightningd @@ -475,10 +468,9 @@ struct io_plan *peer_connected(struct io_conn *conn, daemon_conn_send(daemon->master, take(msg)); daemon_conn_send_fd(daemon->master, subd_fd); daemon_conn_send_fd(daemon->master, pps->gossip_fd); - daemon_conn_send_fd(daemon->master, pps->gossip_store_fd); - /* Don't try to close these on freeing. */ - pps->gossip_store_fd = pps->gossip_fd = -1; + /* Don't try to close this on freeing. */ + pps->gossip_fd = -1; /*~ Now we set up this connection to read/write from subd */ return multiplex_peer_setup(conn, peer); @@ -1892,10 +1884,9 @@ static void peer_final_msg(struct io_conn *conn, struct per_peer_state *pps; struct node_id id; u8 *finalmsg; - int fds[3]; + int fds[2]; - /* pps is allocated off f, so fds are closed when f freed. */ - if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &pps, &finalmsg)) + if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &finalmsg)) master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); /* Get the fds for this peer. */ @@ -1912,8 +1903,9 @@ static void peer_final_msg(struct io_conn *conn, /* Close fd to ourselves. */ close(fds[0]); - /* We put peer fd into conn, but pps needs to free the rest */ - per_peer_state_set_fds(pps, -1, fds[1], fds[2]); + /* We put peer fd into conn, but pps needs to free the gossip_fd */ + pps = new_per_peer_state(tmpctx); + per_peer_state_set_fds(pps, -1, fds[1]); /* This can happen if peer hung up on us. */ peer = peer_htable_get(&daemon->peers, &id); diff --git a/connectd/connectd_gossipd_wire.csv b/connectd/connectd_gossipd_wire.csv index c0d9152c9957..11133197442f 100644 --- a/connectd/connectd_gossipd_wire.csv +++ b/connectd/connectd_gossipd_wire.csv @@ -5,15 +5,12 @@ # Communication between gossipd and connectd. msgtype,gossipd_new_peer,4000 msgdata,gossipd_new_peer,id,node_id, -# Did we negotiate LOCAL_GOSSIP_QUERIES? +# Did we negotiate OPT_GOSSIP_QUERIES? msgdata,gossipd_new_peer,gossip_queries_feature,bool, -# Did they offer LOCAL_INITIAL_ROUTING_SYNC? -msgdata,gossipd_new_peer,initial_routing_sync,bool, -# if success: + gossip fd and gossip_store fd +# if success: + gossip fd msgtype,gossipd_new_peer_reply,4100 msgdata,gossipd_new_peer_reply,success,bool, -msgdata,gossipd_new_peer_reply,gs,?gossip_state, # Connectd asks gossipd for any known addresses for that node. msgtype,gossipd_get_addrs,4001 diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index bb5d0296f78e..960f74f9f90a 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -2,7 +2,6 @@ #include #include #include -#include #include msgtype,connectd_init,2000 @@ -63,7 +62,6 @@ msgtype,connectd_peer_connected,2002 msgdata,connectd_peer_connected,id,node_id, msgdata,connectd_peer_connected,addr,wireaddr_internal, msgdata,connectd_peer_connected,incoming,bool, -msgdata,connectd_peer_connected,pps,per_peer_state, msgdata,connectd_peer_connected,flen,u16, msgdata,connectd_peer_connected,features,u8,flen @@ -74,7 +72,6 @@ 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 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 0b723508606a..9cfee53f4e6c 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -831,37 +831,21 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, struct peer *peer = tal(conn, struct peer); struct node *node; int fds[2]; - int gossip_store_fd; - struct gossip_state *gs; if (!fromwire_gossipd_new_peer(msg, &peer->id, - &peer->gossip_queries_feature, - &peer->initial_routing_sync_feature)) { + &peer->gossip_queries_feature)) { status_broken("Bad new_peer msg from connectd: %s", tal_hex(tmpctx, msg)); return io_close(conn); } - gossip_store_fd = gossip_store_readonly_fd(daemon->rstate->gs);; - if (gossip_store_fd < 0) { - status_broken("Failed to get readonly store fd: %s", - strerror(errno)); - daemon_conn_send(daemon->connectd, - take(towire_gossipd_new_peer_reply(NULL, - false, - NULL))); - goto done; - } - /* This can happen: we handle it gracefully, returning a `failed` msg. */ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { status_broken("Failed to create socketpair: %s", strerror(errno)); - close(gossip_store_fd); daemon_conn_send(daemon->connectd, take(towire_gossipd_new_peer_reply(NULL, - false, - NULL))); + false))); goto done; } @@ -898,43 +882,10 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, /* This sends the initial timestamp filter. */ seeker_setup_peer_gossip(daemon->seeker, peer); - /* BOLT #7: - * - * A node: - * - if the `gossip_queries` feature is negotiated: - * - MUST NOT relay any gossip messages it did not generate itself, - * unless explicitly requested. - */ - if (peer->gossip_queries_feature) { - gs = NULL; - } else { - /* BOLT #7: - * - * - upon receiving an `init` message with the - * `initial_routing_sync` flag set to 1: - * - SHOULD send gossip messages for all known channels and - * nodes, as if they were just received. - * - if the `initial_routing_sync` flag is set to 0, OR if the - * initial sync was completed: - * - SHOULD resume normal operation, as specified in the - * following [Rebroadcasting](#rebroadcasting) section. - */ - gs = tal(tmpctx, struct gossip_state); - gs->timestamp_min = 0; - gs->timestamp_max = UINT32_MAX; - - /* If they don't want initial sync, start at end of store */ - if (!peer->initial_routing_sync_feature) - lseek(gossip_store_fd, 0, SEEK_END); - - gs->next_gossip = time_mono(); - } - /* Reply with success, and the new fd and gossip_state. */ daemon_conn_send(daemon->connectd, - take(towire_gossipd_new_peer_reply(NULL, true, gs))); + take(towire_gossipd_new_peer_reply(NULL, true))); daemon_conn_send_fd(daemon->connectd, fds[1]); - daemon_conn_send_fd(daemon->connectd, gossip_store_fd); done: return daemon_conn_read_next(conn, daemon->connectd); diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index b306112c4392..66bad4d6941b 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -110,7 +110,7 @@ bool fromwire_gossipd_new_blockheight(const void *p UNNEEDED, u32 *blockheight U bool fromwire_gossipd_new_lease_rates(const void *p UNNEEDED, struct lease_rates *rates UNNEEDED) { fprintf(stderr, "fromwire_gossipd_new_lease_rates called!\n"); abort(); } /* Generated stub for fromwire_gossipd_new_peer */ -bool fromwire_gossipd_new_peer(const void *p UNNEEDED, struct node_id *id UNNEEDED, bool *gossip_queries_feature UNNEEDED, bool *initial_routing_sync UNNEEDED) +bool fromwire_gossipd_new_peer(const void *p UNNEEDED, struct node_id *id UNNEEDED, bool *gossip_queries_feature UNNEEDED) { fprintf(stderr, "fromwire_gossipd_new_peer called!\n"); abort(); } /* Generated stub for fromwire_gossipd_outpoint_spent */ bool fromwire_gossipd_outpoint_spent(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) @@ -136,9 +136,6 @@ const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, /* Generated stub for gossip_store_load */ u32 gossip_store_load(struct routing_state *rstate UNNEEDED, struct gossip_store *gs UNNEEDED) { fprintf(stderr, "gossip_store_load called!\n"); abort(); } -/* Generated stub for gossip_store_readonly_fd */ -int gossip_store_readonly_fd(struct gossip_store *gs UNNEEDED) -{ fprintf(stderr, "gossip_store_readonly_fd called!\n"); abort(); } /* Generated stub for gossip_time_now */ struct timeabs gossip_time_now(const struct routing_state *rstate UNNEEDED) { fprintf(stderr, "gossip_time_now called!\n"); abort(); } @@ -342,7 +339,7 @@ u8 *towire_gossipd_init_reply(const tal_t *ctx UNNEEDED) u8 *towire_gossipd_new_blockheight_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_gossipd_new_blockheight_reply called!\n"); abort(); } /* Generated stub for towire_gossipd_new_peer_reply */ -u8 *towire_gossipd_new_peer_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED, const struct gossip_state *gs UNNEEDED) +u8 *towire_gossipd_new_peer_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED) { fprintf(stderr, "towire_gossipd_new_peer_reply called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 77a09622341d..a7342a2e1a0e 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -614,6 +614,9 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) { enum hsmd_wire t = fromwire_peektype(c->msg_in); + if (!is_lightningd(c)) + status_peer_debug(&c->id, "Got %s", hsmd_wire_name(t)); + /* Before we do anything else, is this client allowed to do * what he asks for? */ if (!hsmd_check_client_capabilities(c->hsmd_client, t)) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 9092970236d7..fc2de4ee43dd 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -357,11 +358,12 @@ static void peer_start_closingd_after_shutdown(struct channel *channel, { struct per_peer_state *pps; - if (!fromwire_channeld_shutdown_complete(tmpctx, msg, &pps)) { + if (!fromwire_channeld_shutdown_complete(msg)) { channel_internal_error(channel, "bad shutdown_complete: %s", tal_hex(msg, msg)); return; } + pps = new_per_peer_state(msg); per_peer_state_set_fds_arr(pps, fds); /* This sets channel->owner, closes down channeld. */ @@ -489,9 +491,9 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) peer_got_shutdown(sd->channel, msg); break; case WIRE_CHANNELD_SHUTDOWN_COMPLETE: - /* We expect 3 fds. */ + /* We expect 2 fds. */ if (!fds) - return 3; + return 2; peer_start_closingd_after_shutdown(sd->channel, msg, fds); break; case WIRE_CHANNELD_FAIL_FALLEN_BEHIND: @@ -586,7 +588,6 @@ void peer_start_channeld(struct channel *channel, channel_set_billboard, take(&pps->peer_fd), take(&pps->gossip_fd), - take(&pps->gossip_store_fd), take(&hsmfd), NULL)); if (!channel->owner) { @@ -669,7 +670,6 @@ void peer_start_channeld(struct channel *channel, feerate_max(ld, NULL), try_get_feerate(ld->topology, FEERATE_PENALTY), &channel->last_sig, - pps, &channel->channel_info.remote_fundingkey, &channel->channel_info.theirbase, &channel->channel_info.remote_per_commit, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 96f600c7889a..4a42e1fea76f 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -380,7 +380,6 @@ void peer_start_closingd(struct channel *channel, channel_set_billboard, take(&pps->peer_fd), take(&pps->gossip_fd), - take(&pps->gossip_store_fd), take(&hsmfd), NULL)); @@ -455,7 +454,6 @@ void peer_start_closingd(struct channel *channel, initmsg = towire_closingd_init(tmpctx, chainparams, - pps, &channel->cid, &channel->funding, channel->funding_sats, @@ -476,7 +474,6 @@ void peer_start_closingd(struct channel *channel, && channel->closing_fee_negotiation_step_unit == CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE) /* Always use quickclose with anchors */ || option_anchor_outputs, - IFDEV(ld->dev_fast_gossip, false), channel->shutdown_wrong_funding); /* We don't expect a response: it will give us feedback on diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index a1db898c4d52..16940f44c0dd 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -312,9 +312,9 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd break; case WIRE_CONNECTD_PEER_CONNECTED: - if (tal_count(fds) != 3) - return 3; - peer_connected(connectd->ld, msg, fds[0], fds[1], fds[2]); + if (tal_count(fds) != 2) + return 2; + peer_connected(connectd->ld, msg, fds[0], fds[1]); break; case WIRE_CONNECTD_CONNECT_FAILED: diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index badaaf6f8df7..22051750e548 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1348,16 +1349,16 @@ static void handle_channel_closed(struct subd *dualopend, struct per_peer_state *pps; struct channel *channel = dualopend->channel; - if (!fromwire_dualopend_shutdown_complete(tmpctx, msg, &pps)) { + if (!fromwire_dualopend_shutdown_complete(msg)) { channel_internal_error(dualopend->channel, "Bad DUALOPEND_SHUTDOWN_COMPLETE: %s", tal_hex(msg, msg)); close(fds[0]); close(fds[1]); - close(fds[2]); return; } + pps = new_per_peer_state(tmpctx); per_peer_state_set_fds_arr(pps, fds); peer_start_closingd(channel, pps); @@ -1630,12 +1631,13 @@ static void handle_channel_locked(struct subd *dualopend, struct channel *channel = dualopend->channel; struct per_peer_state *pps; - if (!fromwire_dualopend_channel_locked(tmpctx, msg, &pps)) { + if (!fromwire_dualopend_channel_locked(msg)) { channel_internal_error(channel, "Bad WIRE_DUALOPEND_CHANNEL_LOCKED: %s", tal_hex(msg, msg)); return; } + pps = new_per_peer_state(tmpctx); per_peer_state_set_fds_arr(pps, fds); assert(channel->scid); @@ -2976,16 +2978,16 @@ static unsigned int dual_opend_msg(struct subd *dualopend, handle_dry_run_finished(dualopend, msg); return 0; case WIRE_DUALOPEND_CHANNEL_LOCKED: - if (tal_count(fds) != 3) - return 3; + if (tal_count(fds) != 2) + return 2; handle_channel_locked(dualopend, fds, msg); return 0; case WIRE_DUALOPEND_GOT_SHUTDOWN: handle_peer_wants_to_close(dualopend, msg); return 0; case WIRE_DUALOPEND_SHUTDOWN_COMPLETE: - if (tal_count(fds) != 3) - return 3; + if (tal_count(fds) != 2) + return 2; handle_channel_closed(dualopend, fds, msg); return 0; case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND: @@ -3220,7 +3222,6 @@ static void start_fresh_dualopend(struct peer *peer, channel_set_billboard, take(&pps->peer_fd), take(&pps->gossip_fd), - take(&pps->gossip_store_fd), take(&hsmfd), NULL); if (!channel->owner) { @@ -3249,7 +3250,7 @@ static void start_fresh_dualopend(struct peer *peer, &channel->our_config, max_to_self_delay, min_effective_htlc_capacity, - pps, &channel->local_basepoints, + &channel->local_basepoints, &channel->local_funding_pubkey, channel->minimum_depth); subd_send_msg(channel->owner, take(msg)); @@ -3288,7 +3289,6 @@ void peer_restart_dualopend(struct peer *peer, channel_set_billboard, take(&pps->peer_fd), take(&pps->gossip_fd), - take(&pps->gossip_store_fd), take(&hsmfd), NULL)); if (!channel->owner) { log_broken(channel->log, "Could not subdaemon channel: %s", @@ -3330,7 +3330,6 @@ void peer_restart_dualopend(struct peer *peer, &channel->cid, max_to_self_delay, min_effective_htlc_capacity, - pps, &channel->local_basepoints, &channel->local_funding_pubkey, &channel->channel_info.remote_fundingkey, diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 0b02b41d5de7..bcf6ba210719 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -192,14 +193,12 @@ void handle_reestablish(struct lightningd *ld, type_to_string(tmpctx, struct channel_id, channel_id)); subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, peer_id, - pps, err))); + err))); subd_send_fd(ld->connectd, pps->peer_fd); subd_send_fd(ld->connectd, pps->gossip_fd); - subd_send_fd(ld->connectd, pps->gossip_store_fd); /* Don't close those fds! */ pps->peer_fd = pps->gossip_fd - = pps->gossip_store_fd = -1; } } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 9e78a6963766..4cb58766e95f 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -348,7 +349,6 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, &remote_commit, &pbase, &remote_commit_sig, - &pps, &channel_info.theirbase.revocation, &channel_info.theirbase.payment, &channel_info.theirbase.htlc, @@ -370,6 +370,8 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, goto cleanup; } remote_commit->chainparams = chainparams; + + pps = new_per_peer_state(resp); per_peer_state_set_fds_arr(pps, fds); log_debug(ld->log, @@ -448,7 +450,6 @@ static void opening_fundee_finished(struct subd *openingd, &remote_commit, &pbase, &remote_commit_sig, - &pps, &channel_info.theirbase.revocation, &channel_info.theirbase.payment, &channel_info.theirbase.htlc, @@ -473,6 +474,7 @@ static void opening_fundee_finished(struct subd *openingd, } remote_commit->chainparams = chainparams; + pps = new_per_peer_state(tmpctx); per_peer_state_set_fds_arr(pps, fds); /* openingd should never accept them funding channel in this case. */ @@ -530,7 +532,6 @@ static void opening_fundee_finished(struct subd *openingd, failed: close(fds[0]); close(fds[1]); - close(fds[3]); tal_free(uc); } @@ -803,7 +804,7 @@ static void opening_got_offer(struct subd *openingd, } static void opening_got_reestablish(struct subd *openingd, const u8 *msg, - const int fds[3], + const int fds[2], struct uncommitted_channel *uc) { struct lightningd *ld = openingd->ld; @@ -813,12 +814,13 @@ static void opening_got_reestablish(struct subd *openingd, const u8 *msg, struct per_peer_state *pps; if (!fromwire_openingd_got_reestablish(tmpctx, msg, &channel_id, - &reestablish, &pps)) { + &reestablish)) { log_broken(openingd->log, "Malformed opening_got_reestablish %s", tal_hex(tmpctx, msg)); tal_free(openingd); return; } + pps = new_per_peer_state(tmpctx); per_peer_state_set_fds_arr(pps, fds); /* This could free peer */ @@ -841,8 +843,8 @@ static unsigned int openingd_msg(struct subd *openingd, tal_free(openingd); return 0; } - if (tal_count(fds) != 3) - return 3; + if (tal_count(fds) != 2) + return 2; opening_funder_finished(openingd, msg, fds, uc->fc); return 0; case WIRE_OPENINGD_FUNDER_START_REPLY: @@ -865,8 +867,8 @@ static unsigned int openingd_msg(struct subd *openingd, return 0; case WIRE_OPENINGD_FUNDEE: - if (tal_count(fds) != 3) - return 3; + if (tal_count(fds) != 2) + return 2; opening_fundee_finished(openingd, msg, fds, uc); return 0; @@ -875,8 +877,8 @@ static unsigned int openingd_msg(struct subd *openingd, return 0; case WIRE_OPENINGD_GOT_REESTABLISH: - if (tal_count(fds) != 3) - return 3; + if (tal_count(fds) != 2) + return 2; opening_got_reestablish(openingd, msg, fds, uc); return 0; @@ -932,7 +934,6 @@ void peer_start_openingd(struct peer *peer, struct per_peer_state *pps) opend_channel_set_billboard, take(&pps->peer_fd), take(&pps->gossip_fd), - take(&pps->gossip_store_fd), take(&hsmfd), NULL); if (!uc->open_daemon) { uncommitted_channel_disconnect(uc, LOG_BROKEN, @@ -962,13 +963,12 @@ void peer_start_openingd(struct peer *peer, struct per_peer_state *pps) &uc->our_config, max_to_self_delay, min_effective_htlc_capacity, - pps, &uc->local_basepoints, + &uc->local_basepoints, &uc->local_funding_pubkey, uc->minimum_depth, feerate_min(peer->ld, NULL), feerate_max(peer->ld, NULL), - IFDEV(peer->ld->dev_force_tmp_channel_id, NULL), - IFDEV(peer->ld->dev_fast_gossip, false)); + IFDEV(peer->ld->dev_force_tmp_channel_id, NULL)); subd_send_msg(uc->open_daemon, take(msg)); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 1df75a738a1c..2b3a1b3972b5 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1052,14 +1052,12 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa /* Get connectd to send error and close. */ subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, - payload->pps, error))); + 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; } @@ -1117,7 +1115,7 @@ REGISTER_PLUGIN_HOOK(peer_connected, /* Connectd tells us a peer has connected: it never hands us duplicates, since * it holds them until we say peer_died. */ void peer_connected(struct lightningd *ld, const u8 *msg, - int peer_fd, int gossip_fd, int gossip_store_fd) + int peer_fd, int gossip_fd) { struct node_id id; u8 *their_features; @@ -1130,13 +1128,12 @@ void peer_connected(struct lightningd *ld, const u8 *msg, if (!fromwire_connectd_peer_connected(hook_payload, msg, &id, &hook_payload->addr, &hook_payload->incoming, - &hook_payload->pps, &their_features)) fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", tal_hex(msg, msg)); - per_peer_state_set_fds(hook_payload->pps, - peer_fd, gossip_fd, gossip_store_fd); + hook_payload->pps = new_per_peer_state(hook_payload); + per_peer_state_set_fds(hook_payload->pps, peer_fd, gossip_fd); /* If we're already dealing with this peer, hand off to correct * subdaemon. Otherwise, we'll hand to openingd to wait there. */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 97d96753bad6..3ce98cd368bc 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -65,7 +65,7 @@ struct peer *peer_from_json(struct lightningd *ld, const jsmntok_t *peeridtok); void peer_connected(struct lightningd *ld, const u8 *msg, - int peer_fd, int gossip_fd, int gossip_store_fd); + int peer_fd, int gossip_fd); /* Could be configurable. */ #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL diff --git a/lightningd/subd.c b/lightningd/subd.c index 1ef25e9191a0..530706cd8027 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -404,7 +404,7 @@ static bool log_status_fail(struct subd *sd, const u8 *msg) return true; } -static bool handle_peer_error(struct subd *sd, const u8 *msg, int fds[3]) +static bool handle_peer_error(struct subd *sd, const u8 *msg, int fds[2]) { void *channel = sd->channel; struct channel_id channel_id; @@ -415,9 +415,10 @@ static bool handle_peer_error(struct subd *sd, const u8 *msg, int fds[3]) if (!fromwire_status_peer_error(msg, msg, &channel_id, &desc, &warning, - &pps, &err_for_them)) + &err_for_them)) return false; + pps = new_per_peer_state(msg); per_peer_state_set_fds_arr(pps, fds); /* Don't free sd; we may be about to free channel. */ @@ -523,11 +524,11 @@ static struct io_plan *sd_msg_read(struct io_conn *conn, struct subd *sd) if (sd->channel) { switch ((enum peer_status_wire)type) { case WIRE_STATUS_PEER_ERROR: - /* We expect 3 fds after this */ + /* We expect 2 fds after this */ if (!sd->fds_in) { /* Don't free msg_in: we go around again. */ tal_steal(sd, sd->msg_in); - plan = sd_collect_fds(conn, sd, 3); + plan = sd_collect_fds(conn, sd, 2); goto out; } if (!handle_peer_error(sd, sd->msg_in, sd->fds_in)) diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index f64cd1d4f1a4..9baf90cbf066 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -86,7 +86,7 @@ bool fromwire_status_fail(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, enu bool fromwire_status_peer_billboard(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *perm UNNEEDED, wirestring **happenings UNNEEDED) { fprintf(stderr, "fromwire_status_peer_billboard called!\n"); abort(); } /* Generated stub for fromwire_status_peer_error */ -bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct channel_id *channel UNNEEDED, wirestring **desc UNNEEDED, bool *warning UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **error_for_them UNNEEDED) +bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct channel_id *channel UNNEEDED, wirestring **desc UNNEEDED, bool *warning UNNEEDED, u8 **error_for_them UNNEEDED) { fprintf(stderr, "fromwire_status_peer_error called!\n"); abort(); } /* Generated stub for fromwire_status_version */ bool fromwire_status_version(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, wirestring **version UNNEEDED) @@ -167,6 +167,9 @@ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, /* Generated stub for new_log_book */ struct log_book *new_log_book(struct lightningd *ld UNNEEDED, size_t max_mem UNNEEDED) { fprintf(stderr, "new_log_book called!\n"); abort(); } +/* Generated stub for new_per_peer_state */ +struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } /* Generated stub for new_topology */ struct chain_topology *new_topology(struct lightningd *ld UNNEEDED, struct log *log UNNEEDED) { fprintf(stderr, "new_topology called!\n"); abort(); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 020921cbd682..aaaf1eaecbae 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -210,7 +210,7 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ -bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **features UNNEEDED) +bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_hsmd_sign_bolt12_reply */ bool fromwire_hsmd_sign_bolt12_reply(const void *p UNNEEDED, struct bip340sig *sig UNNEEDED) @@ -444,6 +444,9 @@ struct height_states *new_height_states(const tal_t *ctx UNNEEDED, enum side opener UNNEEDED, const u32 *blockheight UNNEEDED) { fprintf(stderr, "new_height_states called!\n"); abort(); } +/* Generated stub for new_per_peer_state */ +struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, @@ -598,7 +601,7 @@ struct channel *peer_unsaved_channel(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_unsaved_channel called!\n"); abort(); } /* Generated stub for per_peer_state_set_fds */ void per_peer_state_set_fds(struct per_peer_state *pps UNNEEDED, - int peer_fd UNNEEDED, int gossip_fd UNNEEDED, int gossip_store_fd UNNEEDED) + int peer_fd UNNEEDED, int gossip_fd UNNEEDED) { fprintf(stderr, "per_peer_state_set_fds called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, @@ -645,7 +648,7 @@ u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_bas 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) +u8 *towire_connectd_peer_final_msg(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } /* Generated stub for towire_errorfmt */ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, diff --git a/lightningd/test/run-shuffle_fds.c b/lightningd/test/run-shuffle_fds.c index c2a00d667439..3d7fb6e524d0 100644 --- a/lightningd/test/run-shuffle_fds.c +++ b/lightningd/test/run-shuffle_fds.c @@ -70,7 +70,7 @@ bool fromwire_status_fail(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, enu bool fromwire_status_peer_billboard(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *perm UNNEEDED, wirestring **happenings UNNEEDED) { fprintf(stderr, "fromwire_status_peer_billboard called!\n"); abort(); } /* Generated stub for fromwire_status_peer_error */ -bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct channel_id *channel UNNEEDED, wirestring **desc UNNEEDED, bool *warning UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **error_for_them UNNEEDED) +bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct channel_id *channel UNNEEDED, wirestring **desc UNNEEDED, bool *warning UNNEEDED, u8 **error_for_them UNNEEDED) { fprintf(stderr, "fromwire_status_peer_error called!\n"); abort(); } /* Generated stub for fromwire_status_version */ bool fromwire_status_version(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, wirestring **version UNNEEDED) @@ -108,6 +108,9 @@ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const struct node_id *default_node_id UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "new_log called!\n"); abort(); } +/* Generated stub for new_per_peer_state */ +struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } /* Generated stub for per_peer_state_set_fds_arr */ void per_peer_state_set_fds_arr(struct per_peer_state *pps UNNEEDED, const int *fds UNNEEDED) { fprintf(stderr, "per_peer_state_set_fds_arr called!\n"); abort(); } diff --git a/openingd/dualopend.c b/openingd/dualopend.c index ed2eb2872211..997525a853e2 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -44,9 +45,9 @@ #include #include -/* stdin == lightningd, 3 == peer, 4 == gossipd, 5 = gossip_store, 6 = hsmd */ +/* stdin == lightningd, 3 == peer, 4 == gossipd, 5 = hsmd */ #define REQ_FD STDIN_FILENO -#define HSM_FD 6 +#define HSM_FD 5 /* tx_add_input, tx_add_output, tx_rm_input, tx_rm_output */ #define NUM_TX_MSGS (TX_RM_OUTPUT + 1) @@ -297,7 +298,7 @@ static u8 *psbt_changeset_get_next(const tal_t *ctx, static void shutdown(struct state *state) { - u8 *msg = towire_dualopend_shutdown_complete(state, state->pps); + u8 *msg = towire_dualopend_shutdown_complete(state); wire_sync_write(REQ_FD, msg); per_peer_state_fdpass_send(REQ_FD, state->pps); @@ -1162,7 +1163,7 @@ static u8 *handle_funding_locked(struct state *state, u8 *msg) billboard_update(state); if (state->funding_locked[LOCAL]) - return towire_dualopend_channel_locked(state, state->pps); + return towire_dualopend_channel_locked(state); return NULL; } @@ -1197,7 +1198,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) /* Some messages go straight to gossipd. */ if (is_msg_for_gossipd(msg)) { - gossip_rcvd_filter_add(state->pps->grf, msg); wire_sync_write(state->pps->gossip_fd, take(msg)); continue; } @@ -1211,10 +1211,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) if (is_unknown_msg_discardable(msg)) continue; - /* Might be a timestamp filter request: handle. */ - if (handle_timestamp_filter(state->pps, msg)) - continue; - /* A helper which decodes an error. */ if (is_peer_error(tmpctx, msg, &state->channel_id, &err, &warning)) { @@ -3416,8 +3412,7 @@ static u8 *handle_funding_depth(struct state *state, u8 *msg) send_funding_locked(state); if (state->funding_locked[REMOTE]) - return towire_dualopend_channel_locked(state, - state->pps); + return towire_dualopend_channel_locked(state); return NULL; } @@ -3435,14 +3430,6 @@ static void handle_gossip_in(struct state *state) handle_gossip_msg(state->pps, take(msg)); } -static void try_read_gossip_store(struct state *state) -{ - u8 *msg = gossip_store_next(tmpctx, state->pps); - - if (msg) - peer_write(state->pps, take(msg)); -} - /* Try to handle a custommsg Returns true if it was a custom message and has * been handled, false if the message was not handled. */ @@ -3832,7 +3819,6 @@ int main(int argc, char *argv[]) &state->tx_state->localconf, &state->max_to_self_delay, &state->min_effective_htlc_capacity, - &state->pps, &state->our_points, &state->our_funding_pubkey, &state->minimum_depth)) { @@ -3864,7 +3850,6 @@ int main(int argc, char *argv[]) &state->channel_id, &state->max_to_self_delay, &state->min_effective_htlc_capacity, - &state->pps, &state->our_points, &state->our_funding_pubkey, &state->their_funding_pubkey, @@ -3932,8 +3917,9 @@ 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); + /* 3 == peer, 4 == gossipd, 5 = hsmd */ + state->pps = new_per_peer_state(state); + per_peer_state_set_fds(state->pps, 3, 4); /*~ We need an initial per-commitment point whether we're funding or * they are, and lightningd has reserved a unique dbid for us already, @@ -3972,19 +3958,13 @@ int main(int argc, char *argv[]) * opening_funder_reply or opening_fundee. */ msg = NULL; while (!msg) { - int t; - struct timerel trel; - if (time_to_next_gossip(state->pps, &trel)) - t = time_to_msec(trel); - else - t = -1; /*~ If we get a signal which aborts the poll() call, valgrind * complains about revents being uninitialized. I'm not sure * that's correct, but it's easy to be sure. */ pollfd[0].revents = pollfd[1].revents = pollfd[2].revents = 0; - poll(pollfd, ARRAY_SIZE(pollfd), t); + poll(pollfd, ARRAY_SIZE(pollfd), -1); /* Subtle: handle_master_in can do its own poll loop, so * don't try to service more than one fd per loop. */ /* First priority: messages from lightningd. */ @@ -3996,13 +3976,10 @@ int main(int argc, char *argv[]) /* Last priority: chit-chat from gossipd. */ else if (pollfd[1].revents & POLLIN) handle_gossip_in(state); - else - try_read_gossip_store(state); /* If we've shutdown, we're done */ if (shutdown_complete(state)) - msg = towire_dualopend_shutdown_complete(state, - state->pps); + msg = towire_dualopend_shutdown_complete(state); /* Since we're the top-level event loop, we clean up */ clean_tmpctx(); } diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 48177925bcc7..c87fbef2a30d 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -23,7 +22,6 @@ msgdata,dualopend_init,our_config,channel_config, # Minimum/maximum configuration values we'll accept msgdata,dualopend_init,max_to_self_delay,u32, msgdata,dualopend_init,min_effective_htlc_capacity_msat,amount_msat, -msgdata,dualopend_init,pps,per_peer_state, msgdata,dualopend_init,our_basepoints,basepoints, msgdata,dualopend_init,our_funding_pubkey,pubkey, # Constraints in case the other end tries to open a channel. @@ -40,7 +38,6 @@ msgdata,dualopend_reinit,their_config,channel_config, msgdata,dualopend_reinit,channel_id,channel_id, msgdata,dualopend_reinit,max_to_self_delay,u32, msgdata,dualopend_reinit,min_effective_htlc_capacity_msat,amount_msat, -msgdata,dualopend_reinit,pps,per_peer_state, msgdata,dualopend_reinit,our_basepoints,basepoints, msgdata,dualopend_reinit,our_funding_pubkey,pubkey, msgdata,dualopend_reinit,their_funding_pubkey,pubkey, @@ -202,7 +199,6 @@ msgdata,dualopend_peer_locked,remote_per_commit,pubkey, # dualopend->master this channel has been locked msgtype,dualopend_channel_locked,7019 -msgdata,dualopend_channel_locked,pps,per_peer_state, # master->dualopend funding reached depth; tell peer msgtype,dualopend_depth_reached,7020 @@ -223,7 +219,6 @@ msgtype,dualopend_fail_fallen_behind,1028 # Shutdown is complete, ready for closing negotiation. + peer_fd & gossip_fd. msgtype,dualopend_shutdown_complete,7025 -msgdata,dualopend_shutdown_complete,per_peer_state,per_peer_state, # dualopend -> master: this was a dry run, here's some info about this open msgtype,dualopend_dry_run,7026 diff --git a/openingd/openingd.c b/openingd/openingd.c index 72471da3f6c5..e94c31f74437 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -35,9 +36,9 @@ #include #include -/* stdin == lightningd, 3 == peer, 4 == gossipd, 5 = gossip_store, 6 = hsmd */ +/* stdin == lightningd, 3 == peer, 4 == gossipd, 5 = hsmd */ #define REQ_FD STDIN_FILENO -#define HSM_FD 6 +#define HSM_FD 5 #if DEVELOPER /* If --dev-force-tmp-channel-id is set, it ends up here */ @@ -205,7 +206,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, /* Some messages go straight to gossipd. */ if (is_msg_for_gossipd(msg)) { - gossip_rcvd_filter_add(state->pps->grf, msg); wire_sync_write(state->pps->gossip_fd, take(msg)); continue; } @@ -219,10 +219,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, if (is_unknown_msg_discardable(msg)) continue; - /* Might be a timestamp filter request: handle. */ - if (handle_timestamp_filter(state->pps, msg)) - continue; - /* A helper which decodes an error. */ if (is_peer_error(tmpctx, msg, &state->channel_id, &err, &warning)) { @@ -774,7 +770,6 @@ static u8 *funder_channel_complete(struct state *state) tx, pbase, &sig, - state->pps, &state->their_points.revocation, &state->their_points.payment, &state->their_points.htlc, @@ -1234,7 +1229,6 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) local_commit, pbase, &theirsig, - state->pps, &theirs.revocation, &theirs.payment, &theirs.htlc, @@ -1286,8 +1280,7 @@ static u8 *handle_peer_in(struct state *state) /* 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); + &channel_id, msg); } peer_write(state->pps, take(towire_warningfmt(NULL, @@ -1427,14 +1420,6 @@ static u8 *handle_master_in(struct state *state) "Unknown msg %s", tal_hex(tmpctx, msg)); } -static void try_read_gossip_store(struct state *state) -{ - u8 *msg = gossip_store_next(tmpctx, state->pps); - - if (msg) - peer_write(state->pps, take(msg)); -} - int main(int argc, char *argv[]) { setup_locale(); @@ -1460,21 +1445,20 @@ int main(int argc, char *argv[]) &state->localconf, &state->max_to_self_delay, &state->min_effective_htlc_capacity, - &state->pps, &state->our_points, &state->our_funding_pubkey, &state->minimum_depth, &state->min_feerate, &state->max_feerate, - &force_tmp_channel_id, - &dev_fast_gossip)) + &force_tmp_channel_id)) master_badmsg(WIRE_OPENINGD_INIT, msg); #if DEVELOPER dev_force_tmp_channel_id = force_tmp_channel_id; #endif - /* 3 == peer, 4 == gossipd, 5 = gossip_store, 6 = hsmd */ - per_peer_state_set_fds(state->pps, 3, 4, 5); + /* 3 == peer, 4 == gossipd, 5 = hsmd */ + state->pps = new_per_peer_state(state); + per_peer_state_set_fds(state->pps, 3, 4); /*~ Initially we're not associated with a channel, but * handle_peer_gossip_or_error compares this. */ @@ -1520,19 +1504,12 @@ int main(int argc, char *argv[]) * opening_funder_reply or opening_fundee. */ msg = NULL; while (!msg) { - int t; - struct timerel trel; - if (time_to_next_gossip(state->pps, &trel)) - t = time_to_msec(trel); - else - t = -1; - /*~ If we get a signal which aborts the poll() call, valgrind * complains about revents being uninitialized. I'm not sure * that's correct, but it's easy to be sure. */ pollfd[0].revents = pollfd[1].revents = pollfd[2].revents = 0; - poll(pollfd, ARRAY_SIZE(pollfd), t); + poll(pollfd, ARRAY_SIZE(pollfd), -1); /* Subtle: handle_master_in can do its own poll loop, so * don't try to service more than one fd per loop. */ /* First priority: messages from lightningd. */ @@ -1544,8 +1521,6 @@ int main(int argc, char *argv[]) /* Last priority: chit-chat from gossipd. */ else if (pollfd[1].revents & POLLIN) handle_gossip_in(state); - else - try_read_gossip_store(state); /* Since we're the top-level event loop, we clean up */ clean_tmpctx(); diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index 7a688c7f692f..b8f0fc5b9d6f 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -5,7 +5,6 @@ #include #include #include -#include msgtype,openingd_init,6000 # Which network are we configured for? @@ -18,7 +17,6 @@ msgdata,openingd_init,our_config,channel_config, # Minimum/maximum configuration values we'll accept msgdata,openingd_init,max_to_self_delay,u32, msgdata,openingd_init,min_effective_htlc_capacity_msat,amount_msat, -msgdata,openingd_init,pps,per_peer_state, msgdata,openingd_init,our_basepoints,basepoints, msgdata,openingd_init,our_funding_pubkey,pubkey, # Constraints in case the other end tries to open a channel. @@ -26,14 +24,12 @@ msgdata,openingd_init,minimum_depth,u32, msgdata,openingd_init,min_feerate,u32, msgdata,openingd_init,max_feerate,u32, 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 @@ -65,7 +61,6 @@ msgdata,openingd_funder_reply,their_config,channel_config, msgdata,openingd_funder_reply,first_commit,bitcoin_tx, msgdata,openingd_funder_reply,pbase,?penalty_base, msgdata,openingd_funder_reply,first_commit_sig,bitcoin_signature, -msgdata,openingd_funder_reply,pps,per_peer_state, msgdata,openingd_funder_reply,revocation_basepoint,pubkey, msgdata,openingd_funder_reply,payment_basepoint,pubkey, msgdata,openingd_funder_reply,htlc_basepoint,pubkey, @@ -119,7 +114,6 @@ msgdata,openingd_fundee,their_config,channel_config, msgdata,openingd_fundee,first_commit,bitcoin_tx, msgdata,openingd_fundee,pbase,?penalty_base, msgdata,openingd_fundee,first_commit_sig,bitcoin_signature, -msgdata,openingd_fundee,pps,per_peer_state, msgdata,openingd_fundee,revocation_basepoint,pubkey, msgdata,openingd_fundee,payment_basepoint,pubkey, msgdata,openingd_fundee,htlc_basepoint,pubkey, diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 75879bf95b5b..af6f9858d223 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1335,8 +1335,7 @@ def test_gossipwith(node_factory): num_msgs += 1 # one channel announcement, two channel_updates, two node announcements. - # FIXME: Currently gets double gossip! - assert num_msgs == 5 * 2 + assert num_msgs == 5 def test_gossip_notices_close(node_factory, bitcoind): diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index e452202ee457..f137766a802f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -130,7 +130,7 @@ bool fromwire_channeld_offer_htlc_reply(const tal_t *ctx UNNEEDED, const void *p bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_sending_commitsig called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ -bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **features UNNEEDED) +bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_custommsg_in */ bool fromwire_custommsg_in(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **msg UNNEEDED) @@ -448,6 +448,9 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } +/* Generated stub for new_per_peer_state */ +struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) { fprintf(stderr, "notify_chain_mvt called!\n"); abort(); } @@ -628,7 +631,7 @@ const char *peer_wire_name(int e UNNEEDED) { fprintf(stderr, "peer_wire_name called!\n"); abort(); } /* Generated stub for per_peer_state_set_fds */ void per_peer_state_set_fds(struct per_peer_state *pps UNNEEDED, - int peer_fd UNNEEDED, int gossip_fd UNNEEDED, int gossip_store_fd UNNEEDED) + int peer_fd UNNEEDED, int gossip_fd UNNEEDED) { fprintf(stderr, "per_peer_state_set_fds called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, @@ -719,7 +722,7 @@ u8 *towire_connectd_connect_to_peer(const tal_t *ctx UNNEEDED, const struct node 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) +u8 *towire_connectd_peer_final_msg(const tal_t *ctx UNNEEDED, const struct node_id *id 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) From 741f44725a1fa495529752f7e14959b9ab7f519e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:43:59 +1030 Subject: [PATCH 0210/1530] patch lightningd-peer-fds.patch --- lightningd/Makefile | 1 + lightningd/channel_control.c | 15 +++++---- lightningd/channel_control.h | 4 +-- lightningd/closing_control.c | 8 ++--- lightningd/closing_control.h | 4 +-- lightningd/dual_open_control.c | 34 ++++++++++----------- lightningd/dual_open_control.h | 6 ++-- lightningd/onchain_control.c | 2 +- lightningd/opening_common.c | 18 +++++------ lightningd/opening_common.h | 6 ++-- lightningd/opening_control.c | 32 +++++++++---------- lightningd/opening_control.h | 4 +-- lightningd/peer_control.c | 31 +++++++++---------- lightningd/peer_control.h | 4 +-- lightningd/peer_fd.c | 29 ++++++++++++++++++ lightningd/peer_fd.h | 21 +++++++++++++ lightningd/subd.c | 12 ++++---- lightningd/subd.h | 10 +++--- lightningd/test/run-find_my_abspath.c | 9 ++---- lightningd/test/run-invoice-select-inchan.c | 18 +++++------ lightningd/test/run-shuffle_fds.c | 9 ++---- wallet/test/run-wallet.c | 18 +++++------ 22 files changed, 160 insertions(+), 135 deletions(-) create mode 100644 lightningd/peer_fd.c create mode 100644 lightningd/peer_fd.h diff --git a/lightningd/Makefile b/lightningd/Makefile index 24c57250ae19..c1cbf9d4db57 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -29,6 +29,7 @@ LIGHTNINGD_SRC := \ lightningd/options.c \ lightningd/pay.c \ lightningd/peer_control.c \ + lightningd/peer_fd.c \ lightningd/peer_htlcs.c \ lightningd/ping.c \ lightningd/plugin.c \ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index fc2de4ee43dd..be3748e07e06 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -21,6 +20,7 @@ #include #include #include +#include #include #include @@ -356,18 +356,17 @@ static void peer_start_closingd_after_shutdown(struct channel *channel, const u8 *msg, const int *fds) { - struct per_peer_state *pps; + struct peer_fd *peer_fd; if (!fromwire_channeld_shutdown_complete(msg)) { channel_internal_error(channel, "bad shutdown_complete: %s", tal_hex(msg, msg)); return; } - pps = new_per_peer_state(msg); - per_peer_state_set_fds_arr(pps, fds); + peer_fd = new_peer_fd_arr(msg, fds); /* This sets channel->owner, closes down channeld. */ - peer_start_closingd(channel, pps); + peer_start_closingd(channel, peer_fd); /* We might have reconnected, so already be here. */ if (!channel_closed(channel) @@ -551,7 +550,7 @@ 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, + struct peer_fd *peer_fd, const u8 *fwd_msg, bool reconnected, const u8 *reestablish_only) @@ -586,8 +585,8 @@ void peer_start_channeld(struct channel *channel, channel_msg, channel_errmsg, channel_set_billboard, - take(&pps->peer_fd), - take(&pps->gossip_fd), + take(&peer_fd->fd), + take(&peer_fd->gossip_fd), take(&hsmfd), NULL)); if (!channel->owner) { diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index ce91cf68bcfc..2f80661b388f 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -7,11 +7,11 @@ struct channel; struct crypto_state; struct lightningd; -struct per_peer_state; +struct peer_fd; struct peer; void peer_start_channeld(struct channel *channel, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const u8 *fwd_msg, bool reconnected, const u8 *reestablish_only); diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 4a42e1fea76f..65f4bb909fad 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -349,7 +349,7 @@ 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) + struct peer_fd *peer_fd) { u8 *initmsg; u32 min_feerate, feerate, *max_feerate; @@ -378,8 +378,8 @@ void peer_start_closingd(struct channel *channel, closingd_wire_name, closing_msg, channel_errmsg, channel_set_billboard, - take(&pps->peer_fd), - take(&pps->gossip_fd), + take(&peer_fd->fd), + take(&peer_fd->gossip_fd), take(&hsmfd), NULL)); diff --git a/lightningd/closing_control.h b/lightningd/closing_control.h index 8991b44439bc..8e5cef6981b2 100644 --- a/lightningd/closing_control.h +++ b/lightningd/closing_control.h @@ -6,12 +6,12 @@ struct channel; struct lightningd; -struct per_peer_state; +struct peer_fd; void resolve_close_command(struct lightningd *ld, struct channel *channel, bool cooperative); void peer_start_closingd(struct channel *channel, - struct per_peer_state *pps); + struct peer_fd *peer_fd); #endif /* LIGHTNING_LIGHTNINGD_CLOSING_CONTROL_H */ diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 22051750e548..b3161cb8627a 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1346,7 +1346,7 @@ static void handle_channel_closed(struct subd *dualopend, const int *fds, const u8 *msg) { - struct per_peer_state *pps; + struct peer_fd *peer_fd; struct channel *channel = dualopend->channel; if (!fromwire_dualopend_shutdown_complete(msg)) { @@ -1358,10 +1358,9 @@ static void handle_channel_closed(struct subd *dualopend, return; } - pps = new_per_peer_state(tmpctx); - per_peer_state_set_fds_arr(pps, fds); + peer_fd = new_peer_fd_arr(tmpctx, fds); - peer_start_closingd(channel, pps); + peer_start_closingd(channel, peer_fd); channel_set_state(channel, CHANNELD_SHUTTING_DOWN, CLOSINGD_SIGEXCHANGE, @@ -1629,7 +1628,7 @@ static void handle_channel_locked(struct subd *dualopend, const u8 *msg) { struct channel *channel = dualopend->channel; - struct per_peer_state *pps; + struct peer_fd *peer_fd; if (!fromwire_dualopend_channel_locked(msg)) { channel_internal_error(channel, @@ -1637,8 +1636,7 @@ static void handle_channel_locked(struct subd *dualopend, tal_hex(msg, msg)); return; } - pps = new_per_peer_state(tmpctx); - per_peer_state_set_fds_arr(pps, fds); + peer_fd = new_peer_fd_arr(tmpctx, fds); assert(channel->scid); assert(channel->remote_funding_locked); @@ -1659,7 +1657,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, NULL); + peer_start_channeld(channel, peer_fd, NULL, false, NULL); return; } @@ -3198,7 +3196,7 @@ AUTODATA(json_command, &openchannel_bump_command); AUTODATA(json_command, &openchannel_abort_command); static void start_fresh_dualopend(struct peer *peer, - struct per_peer_state *pps, + struct peer_fd *peer_fd, struct channel *channel) { int hsmfd; @@ -3220,8 +3218,8 @@ static void start_fresh_dualopend(struct peer *peer, dual_opend_msg, channel_errmsg, channel_set_billboard, - take(&pps->peer_fd), - take(&pps->gossip_fd), + take(&peer_fd->fd), + take(&peer_fd->gossip_fd), take(&hsmfd), NULL); if (!channel->owner) { @@ -3258,7 +3256,7 @@ static void start_fresh_dualopend(struct peer *peer, } void peer_restart_dualopend(struct peer *peer, - struct per_peer_state *pps, + struct peer_fd *peer_fd, struct channel *channel) { u32 max_to_self_delay, blockheight; @@ -3270,7 +3268,7 @@ void peer_restart_dualopend(struct peer *peer, u8 *msg; if (channel_unsaved(channel)) { - start_fresh_dualopend(peer, pps, channel); + start_fresh_dualopend(peer, peer_fd, channel); return; } hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->dbid, @@ -3287,8 +3285,8 @@ void peer_restart_dualopend(struct peer *peer, dual_opend_msg, channel_errmsg, channel_set_billboard, - take(&pps->peer_fd), - take(&pps->gossip_fd), + take(&peer_fd->fd), + take(&peer_fd->gossip_fd), take(&hsmfd), NULL)); if (!channel->owner) { log_broken(channel->log, "Could not subdaemon channel: %s", @@ -3362,7 +3360,7 @@ void peer_restart_dualopend(struct peer *peer, subd_send_msg(channel->owner, take(msg)); } -void peer_start_dualopend(struct peer *peer, struct per_peer_state *pps) +void peer_start_dualopend(struct peer *peer, struct peer_fd *peer_fd) { struct channel *channel; @@ -3372,5 +3370,5 @@ void peer_start_dualopend(struct peer *peer, struct per_peer_state *pps) peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); - start_fresh_dualopend(peer, pps, channel); + start_fresh_dualopend(peer, peer_fd, channel); } diff --git a/lightningd/dual_open_control.h b/lightningd/dual_open_control.h index 8657a40a6b1b..741a65a0e29b 100644 --- a/lightningd/dual_open_control.h +++ b/lightningd/dual_open_control.h @@ -4,12 +4,12 @@ #include "config.h" #include -struct per_peer_state; +struct peer_fd; -void peer_start_dualopend(struct peer *peer, struct per_peer_state *pps); +void peer_start_dualopend(struct peer *peer, struct peer_fd *peer_fd); void peer_restart_dualopend(struct peer *peer, - struct per_peer_state *pps, + struct peer_fd *peer_fd, struct channel *channel); void dualopen_tell_depth(struct subd *dualopend, diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 6942162f7393..d4f81de27165 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -561,7 +561,7 @@ static unsigned int onchain_msg(struct subd *sd, const u8 *msg, const int *fds U /* Only error onchaind can get is if it dies. */ static void onchain_error(struct channel *channel, - struct per_peer_state *pps UNUSED, + struct peer_fd *pps UNUSED, const struct channel_id *channel_id UNUSED, const char *desc, bool warning UNUSED, diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index bcf6ba210719..7843267051bd 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -1,7 +1,6 @@ #include "config.h" #include #include -#include #include #include #include @@ -12,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -76,14 +76,14 @@ new_uncommitted_channel(struct peer *peer) } void opend_channel_errmsg(struct uncommitted_channel *uc, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id UNUSED, const char *desc, bool warning UNUSED, const u8 *err_for_them UNUSED) { /* Close fds, if any. */ - tal_free(pps); + tal_free(peer_fd); uncommitted_channel_disconnect(uc, LOG_INFORM, desc); tal_free(uc); } @@ -169,7 +169,7 @@ 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_fd *peer_fd) { struct peer *peer; struct channel *c; @@ -185,7 +185,7 @@ void handle_reestablish(struct lightningd *ld, 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); + peer_start_channeld(c, peer_fd, NULL, true, reestablish); } else { const u8 *err = towire_errorfmt(tmpctx, channel_id, "Unknown channel for reestablish"); @@ -194,12 +194,10 @@ void handle_reestablish(struct lightningd *ld, subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, peer_id, err))); - subd_send_fd(ld->connectd, pps->peer_fd); - subd_send_fd(ld->connectd, pps->gossip_fd); + subd_send_fd(ld->connectd, peer_fd->fd); + subd_send_fd(ld->connectd, peer_fd->gossip_fd); /* Don't close those fds! */ - pps->peer_fd - = pps->gossip_fd - = -1; + peer_fd->fd = peer_fd->gossip_fd = -1; } } diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index 8def23c573de..e550ce0190c9 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -92,14 +92,14 @@ struct funding_channel { /* Place to stash the per-peer-state while we wait * for them to get back to us with signatures */ - struct per_peer_state *pps; + struct peer_fd *peer_fd; }; struct uncommitted_channel * new_uncommitted_channel(struct peer *peer); void opend_channel_errmsg(struct uncommitted_channel *uc, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id UNUSED, const char *desc, bool warning UNUSED, @@ -125,7 +125,7 @@ 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_fd *peer_fd); #if DEVELOPER struct command; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 4cb58766e95f..acb53d292951 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -24,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -337,7 +337,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, struct channel *channel; struct lightningd *ld = openingd->ld; u8 *remote_upfront_shutdown_script; - struct per_peer_state *pps; + struct peer_fd *peer_fd; struct penalty_base *pbase; struct channel_type *type; @@ -371,8 +371,7 @@ static void opening_funder_finished(struct subd *openingd, const u8 *resp, } remote_commit->chainparams = chainparams; - pps = new_per_peer_state(resp); - per_peer_state_set_fds_arr(pps, fds); + peer_fd = new_peer_fd_arr(resp, fds); log_debug(ld->log, "%s", type_to_string(tmpctx, struct pubkey, @@ -411,7 +410,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, NULL); + peer_start_channeld(channel, peer_fd, NULL, false, NULL); cleanup: /* Frees fc too */ @@ -436,7 +435,7 @@ static void opening_fundee_finished(struct subd *openingd, u8 channel_flags; struct channel *channel; u8 *remote_upfront_shutdown_script, *local_upfront_shutdown_script; - struct per_peer_state *pps; + struct peer_fd *peer_fd; struct penalty_base *pbase; struct channel_type *type; @@ -445,6 +444,7 @@ static void opening_fundee_finished(struct subd *openingd, /* This is a new channel_info.their_config, set its ID to 0 */ channel_info.their_config.id = 0; + peer_fd = new_peer_fd_arr(tmpctx, fds); if (!fromwire_openingd_fundee(tmpctx, reply, &channel_info.their_config, &remote_commit, @@ -474,8 +474,6 @@ static void opening_fundee_finished(struct subd *openingd, } remote_commit->chainparams = chainparams; - pps = new_per_peer_state(tmpctx); - per_peer_state_set_fds_arr(pps, fds); /* openingd should never accept them funding channel in this case. */ if (peer_active_channel(uc->peer)) { @@ -524,14 +522,12 @@ 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, NULL); + peer_start_channeld(channel, peer_fd, fwd_msg, false, NULL); tal_free(uc); return; failed: - close(fds[0]); - close(fds[1]); tal_free(uc); } @@ -811,7 +807,9 @@ static void opening_got_reestablish(struct subd *openingd, const u8 *msg, struct node_id peer_id = uc->peer->id; struct channel_id channel_id; u8 *reestablish; - struct per_peer_state *pps; + struct peer_fd *peer_fd; + + peer_fd = new_peer_fd_arr(tmpctx, fds); if (!fromwire_openingd_got_reestablish(tmpctx, msg, &channel_id, &reestablish)) { @@ -820,13 +818,11 @@ static void opening_got_reestablish(struct subd *openingd, const u8 *msg, tal_free(openingd); return; } - pps = new_per_peer_state(tmpctx); - per_peer_state_set_fds_arr(pps, fds); /* This could free peer */ tal_free(uc); - handle_reestablish(ld, &peer_id, &channel_id, reestablish, pps); + handle_reestablish(ld, &peer_id, &channel_id, reestablish, peer_fd); } static unsigned int openingd_msg(struct subd *openingd, @@ -909,7 +905,7 @@ static unsigned int openingd_msg(struct subd *openingd, return 0; } -void peer_start_openingd(struct peer *peer, struct per_peer_state *pps) +void peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) { int hsmfd; u32 max_to_self_delay; @@ -932,8 +928,8 @@ void peer_start_openingd(struct peer *peer, struct per_peer_state *pps) openingd_msg, opend_channel_errmsg, opend_channel_set_billboard, - take(&pps->peer_fd), - take(&pps->gossip_fd), + take(&peer_fd->fd), + take(&peer_fd->gossip_fd), take(&hsmfd), NULL); if (!uc->open_daemon) { uncommitted_channel_disconnect(uc, LOG_BROKEN, diff --git a/lightningd/opening_control.h b/lightningd/opening_control.h index 3c5f3343afc1..f7860d9501e2 100644 --- a/lightningd/opening_control.h +++ b/lightningd/opening_control.h @@ -8,14 +8,14 @@ struct channel_id; struct crypto_state; struct json_stream; struct lightningd; -struct per_peer_state; +struct peer_fd; struct uncommitted_channel; 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); + struct peer_fd *peer_fd); struct subd *peer_get_owning_subd(struct peer *peer); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 2b3a1b3972b5..4365bfb9e484 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -57,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -281,7 +281,7 @@ void drop_to_chain(struct lightningd *ld, struct channel *channel, } void channel_errmsg(struct channel *channel, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id UNUSED, const char *desc, bool warning, @@ -299,8 +299,8 @@ void channel_errmsg(struct channel *channel, return; } - /* No per_peer_state means a subd crash or disconnection. */ - if (!pps) { + /* No peer_fd means a subd crash or disconnection. */ + if (!peer_fd) { /* If the channel is unsaved, we forget it */ channel_fail_reconnect(channel, "%s: %s", channel->owner->name, desc); @@ -931,7 +931,7 @@ struct peer_connected_hook_payload { struct wireaddr_internal addr; bool incoming; struct peer *peer; - struct per_peer_state *pps; + struct peer_fd *peer_fd; u8 *error; }; @@ -1011,7 +1011,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); + peer_restart_dualopend(peer, payload->peer_fd, channel); return; case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: @@ -1020,7 +1020,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_start_channeld(channel, payload->pps, NULL, true, + peer_start_channeld(channel, payload->peer_fd, NULL, true, NULL); return; } @@ -1039,11 +1039,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); + peer_restart_dualopend(peer, payload->peer_fd, channel); } else - peer_start_dualopend(peer, payload->pps); + peer_start_dualopend(peer, payload->peer_fd); } else - peer_start_openingd(peer, payload->pps); + peer_start_openingd(peer, payload->peer_fd); return; send_error: @@ -1053,12 +1053,10 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, 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->peer_fd->fd); + subd_send_fd(ld->connectd, payload->peer_fd->gossip_fd); /* Don't close those fds! */ - payload->pps->peer_fd - = payload->pps->gossip_fd - = -1; + payload->peer_fd->fd = payload->peer_fd->gossip_fd = -1; } static bool @@ -1132,8 +1130,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", tal_hex(msg, msg)); - hook_payload->pps = new_per_peer_state(hook_payload); - per_peer_state_set_fds(hook_payload->pps, peer_fd, gossip_fd); + hook_payload->peer_fd = new_peer_fd(hook_payload, peer_fd, gossip_fd); /* If we're already dealing with this peer, hand off to correct * subdaemon. Otherwise, we'll hand to openingd to wait there. */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 3ce98cd368bc..dff52d9d1840 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -11,7 +11,7 @@ #include #include -struct per_peer_state; +struct peer_fd; struct wally_psbt; struct peer { @@ -71,7 +71,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL void channel_errmsg(struct channel *channel, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id, const char *desc, bool warning, diff --git a/lightningd/peer_fd.c b/lightningd/peer_fd.c new file mode 100644 index 000000000000..5508dc720f92 --- /dev/null +++ b/lightningd/peer_fd.c @@ -0,0 +1,29 @@ +#include "config.h" +#include +#include +#include + +static void destroy_peer_fd(struct peer_fd *peer_fd) +{ + if (peer_fd->fd != -1) + close(peer_fd->fd); + if (peer_fd->gossip_fd != -1) + close(peer_fd->gossip_fd); +} + +struct peer_fd *new_peer_fd(const tal_t *ctx, int peer_fdnum, int gossip_fd) +{ + struct peer_fd *peer_fd = tal(ctx, struct peer_fd); + + peer_fd->fd = peer_fdnum; + peer_fd->gossip_fd = gossip_fd; + tal_add_destructor(peer_fd, destroy_peer_fd); + return peer_fd; +} + +struct peer_fd *new_peer_fd_arr(const tal_t *ctx, const int *fds) +{ + /* We expect 2 fds. */ + assert(tal_count(fds) == 2); + return new_peer_fd(ctx, fds[0], fds[1]); +} diff --git a/lightningd/peer_fd.h b/lightningd/peer_fd.h new file mode 100644 index 000000000000..705deb47684a --- /dev/null +++ b/lightningd/peer_fd.h @@ -0,0 +1,21 @@ +#ifndef LIGHTNING_LIGHTNINGD_PEER_FD_H +#define LIGHTNING_LIGHTNINGD_PEER_FD_H +#include "config.h" +#include + +/* This name is a little preemptive: it still contains the gossip_fd + * for now! */ +struct peer_fd { + /* If not -1, closed on freeing */ + int fd; + int gossip_fd; +}; + +/* Allocate a new per-peer state and add destructor to close fds if set; + * sets fds to -1. */ +struct peer_fd *new_peer_fd(const tal_t *ctx, int peer_fd, int gossip_fd); + +/* Array version of above: tal_count(fds) must be 2 */ +struct peer_fd *new_peer_fd_arr(const tal_t *ctx, const int *fds); + +#endif /* LIGHTNING_LIGHTNINGD_PEER_FD_H */ diff --git a/lightningd/subd.c b/lightningd/subd.c index 530706cd8027..17ba6dc09cf7 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -409,7 +410,7 @@ static bool handle_peer_error(struct subd *sd, const u8 *msg, int fds[2]) void *channel = sd->channel; struct channel_id channel_id; char *desc; - struct per_peer_state *pps; + struct peer_fd *peer_fd; u8 *err_for_them; bool warning; @@ -418,12 +419,11 @@ static bool handle_peer_error(struct subd *sd, const u8 *msg, int fds[2]) &err_for_them)) return false; - pps = new_per_peer_state(msg); - per_peer_state_set_fds_arr(pps, fds); + peer_fd = new_peer_fd_arr(msg, fds); /* Don't free sd; we may be about to free channel. */ sd->channel = NULL; - sd->errcb(channel, pps, &channel_id, desc, warning, err_for_them); + sd->errcb(channel, peer_fd, &channel_id, desc, warning, err_for_them); return true; } @@ -682,7 +682,7 @@ static struct subd *new_subd(struct lightningd *ld, unsigned int (*msgcb)(struct subd *, const u8 *, const int *fds), void (*errcb)(void *channel, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id, const char *desc, bool warning, @@ -789,7 +789,7 @@ struct subd *new_channel_subd_(struct lightningd *ld, unsigned int (*msgcb)(struct subd *, const u8 *, const int *fds), void (*errcb)(void *channel, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id, const char *desc, bool warning, diff --git a/lightningd/subd.h b/lightningd/subd.h index 73b9534bda2b..4be12e248181 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -11,7 +11,7 @@ struct crypto_state; struct io_conn; -struct per_peer_state; +struct peer_fd; /* By convention, replies are requests + 100 */ #define SUBD_REPLY_OFFSET 100 @@ -43,11 +43,11 @@ struct subd { unsigned (*msgcb)(struct subd *, const u8 *, const int *); const char *(*msgname)(int msgtype); - /* If per_peer_state == NULL, it was a disconnect/crash. Otherwise, + /* If peer_fd == NULL, it was a disconnect/crash. Otherwise, * sufficient information to hand back to gossipd, including the * error message we sent them if any. */ void (*errcb)(void *channel, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id, const char *desc, bool warning, @@ -124,7 +124,7 @@ struct subd *new_channel_subd_(struct lightningd *ld, unsigned int (*msgcb)(struct subd *, const u8 *, const int *fds), void (*errcb)(void *channel, - struct per_peer_state *pps, + struct peer_fd *peer_fd, const struct channel_id *channel_id, const char *desc, bool warning, @@ -141,7 +141,7 @@ struct subd *new_channel_subd_(struct lightningd *ld, (msgname), (msgcb), \ typesafe_cb_postargs(void, void *, (errcb), \ (channel), \ - struct per_peer_state *, \ + struct peer_fd *, \ const struct channel_id *, \ const char *, bool, const u8 *), \ typesafe_cb_postargs(void, void *, (billboardcb), \ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 9baf90cbf066..9cac29111ef9 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -167,18 +167,15 @@ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, /* Generated stub for new_log_book */ struct log_book *new_log_book(struct lightningd *ld UNNEEDED, size_t max_mem UNNEEDED) { fprintf(stderr, "new_log_book called!\n"); abort(); } -/* Generated stub for new_per_peer_state */ -struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } +/* Generated stub for new_peer_fd_arr */ +struct peer_fd *new_peer_fd_arr(const tal_t *ctx UNNEEDED, const int *fds UNNEEDED) +{ fprintf(stderr, "new_peer_fd_arr called!\n"); abort(); } /* Generated stub for new_topology */ struct chain_topology *new_topology(struct lightningd *ld UNNEEDED, struct log *log UNNEEDED) { fprintf(stderr, "new_topology called!\n"); abort(); } /* Generated stub for onchaind_replay_channels */ void onchaind_replay_channels(struct lightningd *ld UNNEEDED) { fprintf(stderr, "onchaind_replay_channels called!\n"); abort(); } -/* Generated stub for per_peer_state_set_fds_arr */ -void per_peer_state_set_fds_arr(struct per_peer_state *pps UNNEEDED, const int *fds UNNEEDED) -{ fprintf(stderr, "per_peer_state_set_fds_arr called!\n"); abort(); } /* Generated stub for plugins_config */ void plugins_config(struct plugins *plugins UNNEEDED) { fprintf(stderr, "plugins_config called!\n"); abort(); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index aaaf1eaecbae..7c1197cc7d2d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -444,9 +444,9 @@ struct height_states *new_height_states(const tal_t *ctx UNNEEDED, enum side opener UNNEEDED, const u32 *blockheight UNNEEDED) { fprintf(stderr, "new_height_states called!\n"); abort(); } -/* Generated stub for new_per_peer_state */ -struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } +/* Generated stub for new_peer_fd */ +struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED) +{ fprintf(stderr, "new_peer_fd called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, @@ -579,30 +579,26 @@ struct channel *peer_normal_channel(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_normal_channel called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ void peer_restart_dualopend(struct peer *peer UNNEEDED, - struct per_peer_state *pps UNNEEDED, + struct peer_fd *peer_fd 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, - struct per_peer_state *pps UNNEEDED, + struct peer_fd *peer_fd UNNEEDED, const u8 *fwd_msg 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) +void peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd 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) + struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } /* Generated stub for peer_unsaved_channel */ struct channel *peer_unsaved_channel(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_unsaved_channel called!\n"); abort(); } -/* Generated stub for per_peer_state_set_fds */ -void per_peer_state_set_fds(struct per_peer_state *pps UNNEEDED, - int peer_fd UNNEEDED, int gossip_fd UNNEEDED) -{ fprintf(stderr, "per_peer_state_set_fds called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) diff --git a/lightningd/test/run-shuffle_fds.c b/lightningd/test/run-shuffle_fds.c index 3d7fb6e524d0..3e1d5cd0b2ab 100644 --- a/lightningd/test/run-shuffle_fds.c +++ b/lightningd/test/run-shuffle_fds.c @@ -108,12 +108,9 @@ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const struct node_id *default_node_id UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "new_log called!\n"); abort(); } -/* Generated stub for new_per_peer_state */ -struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } -/* Generated stub for per_peer_state_set_fds_arr */ -void per_peer_state_set_fds_arr(struct per_peer_state *pps UNNEEDED, const int *fds UNNEEDED) -{ fprintf(stderr, "per_peer_state_set_fds_arr called!\n"); abort(); } +/* Generated stub for new_peer_fd_arr */ +struct peer_fd *new_peer_fd_arr(const tal_t *ctx UNNEEDED, const int *fds UNNEEDED) +{ fprintf(stderr, "new_peer_fd_arr called!\n"); abort(); } /* Generated stub for subdaemon_path */ const char *subdaemon_path(const tal_t *ctx UNNEEDED, const struct lightningd *ld UNNEEDED, const char *name UNNEEDED) { fprintf(stderr, "subdaemon_path called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index f137766a802f..9f726f2729e0 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -448,9 +448,9 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } -/* Generated stub for new_per_peer_state */ -struct per_peer_state *new_per_peer_state(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "new_per_peer_state called!\n"); abort(); } +/* Generated stub for new_peer_fd */ +struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED) +{ fprintf(stderr, "new_peer_fd called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) { fprintf(stderr, "notify_chain_mvt called!\n"); abort(); } @@ -606,22 +606,22 @@ void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDE { fprintf(stderr, "peer_memleak_done called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ void peer_restart_dualopend(struct peer *peer UNNEEDED, - struct per_peer_state *pps UNNEEDED, + struct peer_fd *peer_fd 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, - struct per_peer_state *pps UNNEEDED, + struct peer_fd *peer_fd UNNEEDED, const u8 *fwd_msg 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) +void peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd 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) + struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } /* Generated stub for peer_wire_is_defined */ bool peer_wire_is_defined(u16 type UNNEEDED) @@ -629,10 +629,6 @@ bool peer_wire_is_defined(u16 type UNNEEDED) /* Generated stub for peer_wire_name */ const char *peer_wire_name(int e UNNEEDED) { fprintf(stderr, "peer_wire_name called!\n"); abort(); } -/* Generated stub for per_peer_state_set_fds */ -void per_peer_state_set_fds(struct per_peer_state *pps UNNEEDED, - int peer_fd UNNEEDED, int gossip_fd UNNEEDED) -{ fprintf(stderr, "per_peer_state_set_fds called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) From 407a89a400a9f2623c67c7bc9c9a883cfd4315d0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:44:59 +1030 Subject: [PATCH 0211/1530] connectd: remove per_peer_state in favor of keeping gossip_fd directly. Signed-off-by: Rusty Russell --- connectd/connectd.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index a3c939acbbcd..fc02c3242b19 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -218,10 +218,9 @@ static void peer_connected_in(struct daemon *daemon, * 'features' is a field in the `init` message, indicating properties of the * node. */ -static bool get_gossipfds(struct daemon *daemon, - const struct node_id *id, - const u8 *their_features, - struct per_peer_state *pps) +static int get_gossipfd(struct daemon *daemon, + const struct node_id *id, + const u8 *their_features) { bool gossip_queries_feature, success; u8 *msg; @@ -251,13 +250,13 @@ static bool get_gossipfds(struct daemon *daemon, if (!success) { status_broken("Gossipd did not give us an fd: losing peer %s", type_to_string(tmpctx, struct node_id, id)); - return false; + return -1; } /* Otherwise, the next thing in the socket will be the file descriptor * for the per-peer daemon. */ - pps->gossip_fd = fdpass_recv(GOSSIPCTL_FD); - return true; + return fdpass_recv(GOSSIPCTL_FD); + } /*~ This is an ad-hoc marshalling structure where we store arguments so we @@ -387,8 +386,7 @@ struct io_plan *peer_connected(struct io_conn *conn, struct peer *peer; int unsup; size_t depender, missing; - int subd_fd; - struct per_peer_state *pps; + int subd_fd, gossip_fd; peer = peer_htable_get(&daemon->peers, id); if (peer) @@ -446,11 +444,9 @@ struct io_plan *peer_connected(struct io_conn *conn, if (!peer) return io_close(conn); - /* FIXME: Remove pps abstraction! */ - pps = new_per_peer_state(tmpctx); - /* If gossipd can't give us a file descriptor, we give up connecting. */ - if (!get_gossipfds(daemon, id, their_features, pps)) { + gossip_fd = get_gossipfd(daemon, id, their_features); + if (gossip_fd < 0) { close(subd_fd); return tal_free(peer); } @@ -467,10 +463,7 @@ struct io_plan *peer_connected(struct io_conn *conn, * we have connected, and give the peer and gossip fds. */ daemon_conn_send(daemon->master, take(msg)); daemon_conn_send_fd(daemon->master, subd_fd); - daemon_conn_send_fd(daemon->master, pps->gossip_fd); - - /* Don't try to close this on freeing. */ - pps->gossip_fd = -1; + daemon_conn_send_fd(daemon->master, gossip_fd); /*~ Now we set up this connection to read/write from subd */ return multiplex_peer_setup(conn, peer); @@ -1881,32 +1874,24 @@ static void peer_final_msg(struct io_conn *conn, struct daemon *daemon, const u8 *msg) { struct peer *peer; - struct per_peer_state *pps; struct node_id id; u8 *finalmsg; - int fds[2]; if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &finalmsg)) master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); - /* Get the fds for this peer. */ + /* Get the peer_fd and gossip_fd for this peer: we don't need them. */ 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) + for (size_t i = 0; i < 2; i++) { + int fd = fdpass_recv(io_conn_fd(conn)); + if (fd == -1) status_failed(STATUS_FAIL_MASTER_IO, "Getting fd %zu after peer_final_msg: %s", i, strerror(errno)); + close(fd); } io_fd_block(io_conn_fd(conn), false); - /* Close fd to ourselves. */ - close(fds[0]); - - /* We put peer fd into conn, but pps needs to free the gossip_fd */ - pps = new_per_peer_state(tmpctx); - per_peer_state_set_fds(pps, -1, fds[1]); - /* This can happen if peer hung up on us. */ peer = peer_htable_get(&daemon->peers, &id); if (peer) { From 6d4c56e8b6e81d89500e461470198536ed47f12b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:45:43 +1030 Subject: [PATCH 0212/1530] connectd: put more stuff into struct gossip_state. We're the only ones who use it now, so put our fields inside it and make it local. Signed-off-by: Rusty Russell --- common/gossip_store.c | 95 +++++++++------------------- common/gossip_store.h | 9 +-- common/per_peer_state.h | 7 --- connectd/connectd.c | 2 - connectd/connectd.h | 23 ++++--- connectd/multiplex.c | 134 ++++++++++++++++++++-------------------- 6 files changed, 118 insertions(+), 152 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index 3456a483e003..9056dee01158 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -11,7 +11,8 @@ #include #include -static bool timestamp_filter(const struct gossip_state *gs, u32 timestamp) +static bool timestamp_filter(u32 timestamp_min, u32 timestamp_max, + u32 timestamp) { /* BOLT #7: * @@ -20,25 +21,11 @@ static bool timestamp_filter(const struct gossip_state *gs, u32 timestamp) * `timestamp_range`. */ /* Note that we turn first_timestamp & timestamp_range into an inclusive range */ - return timestamp >= gs->timestamp_min - && timestamp <= gs->timestamp_max; + return timestamp >= timestamp_min + && timestamp <= timestamp_max; } -/* Not all the data we expected was there: rewind file */ -static void failed_read(int fd, int len) -{ - if (len < 0) { - /* Grab errno before lseek overrides it */ - const char *err = strerror(errno); - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: failed read @%"PRIu64": %s", - (u64)lseek(fd, 0, SEEK_CUR), err); - } - - lseek(fd, -len, SEEK_CUR); -} - -static void reopen_gossip_store(int *gossip_store_fd, const u8 *msg) +static size_t reopen_gossip_store(int *gossip_store_fd, const u8 *msg) { u64 equivalent_offset; int newfd; @@ -57,17 +44,16 @@ static void reopen_gossip_store(int *gossip_store_fd, const u8 *msg) status_debug("gossip_store at end, new fd moved to %"PRIu64, equivalent_offset); - lseek(newfd, equivalent_offset, SEEK_SET); close(*gossip_store_fd); *gossip_store_fd = newfd; + return equivalent_offset; } -u8 *gossip_store_iter(const tal_t *ctx, +u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, - struct gossip_state *gs, - struct gossip_rcvd_filter *grf, - size_t *off) + u32 timestamp_min, u32 timestamp_max, + size_t *off, size_t *end) { u8 *msg = NULL; @@ -77,16 +63,9 @@ u8 *gossip_store_iter(const tal_t *ctx, bool push; int type, r; - if (off) - r = pread(*gossip_store_fd, &hdr, sizeof(hdr), *off); - else - r = read(*gossip_store_fd, &hdr, sizeof(hdr)); - if (r != sizeof(hdr)) { - /* We expect a 0 read here at EOF */ - if (r != 0 && off) - failed_read(*gossip_store_fd, r); + r = pread(*gossip_store_fd, &hdr, sizeof(hdr), *off); + if (r != sizeof(hdr)) return NULL; - } msglen = be32_to_cpu(hdr.len); push = (msglen & GOSSIP_STORE_LEN_PUSH_BIT); @@ -94,56 +73,42 @@ u8 *gossip_store_iter(const tal_t *ctx, /* Skip any deleted entries. */ if (be32_to_cpu(hdr.len) & GOSSIP_STORE_LEN_DELETED_BIT) { - /* Skip over it. */ - if (off) - *off += r + msglen; - else - lseek(*gossip_store_fd, msglen, SEEK_CUR); + *off += r + msglen; continue; } checksum = be32_to_cpu(hdr.crc); timestamp = be32_to_cpu(hdr.timestamp); msg = tal_arr(ctx, u8, msglen); - if (off) - r = pread(*gossip_store_fd, msg, msglen, *off + r); - else - r = read(*gossip_store_fd, msg, msglen); - if (r != msglen) { - if (!off) - failed_read(*gossip_store_fd, r); + r = pread(*gossip_store_fd, msg, msglen, *off + r); + if (r != msglen) return NULL; - } if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: bad checksum offset %" - PRIi64": %s", - off ? (s64)*off : - (s64)lseek(*gossip_store_fd, - 0, SEEK_CUR) - msglen, - tal_hex(tmpctx, msg)); + "gossip_store: bad checksum offset %zu" + ": %s", + *off, tal_hex(tmpctx, msg)); /* Definitely processing it now */ - if (off) - *off += sizeof(hdr) + msglen; - - /* Don't send back gossip they sent to us! */ - if (gossip_rcvd_filter_del(grf, msg)) { - msg = tal_free(msg); - continue; - } + *off += sizeof(hdr) + msglen; + if (*off > *end) + *end = *off; type = fromwire_peektype(msg); - if (type == WIRE_GOSSIP_STORE_ENDED) - reopen_gossip_store(gossip_store_fd, msg); + /* end can go backwards in this case! */ + if (type == WIRE_GOSSIP_STORE_ENDED) { + *off = *end = reopen_gossip_store(gossip_store_fd, msg); /* Ignore gossipd internal messages. */ - else if (type != WIRE_CHANNEL_ANNOUNCEMENT - && type != WIRE_CHANNEL_UPDATE - && type != WIRE_NODE_ANNOUNCEMENT) + } else if (type != WIRE_CHANNEL_ANNOUNCEMENT + && type != WIRE_CHANNEL_UPDATE + && type != WIRE_NODE_ANNOUNCEMENT) { msg = tal_free(msg); - else if (!push && !timestamp_filter(gs, timestamp)) + } else if (!push && + !timestamp_filter(timestamp_min, timestamp_max, + timestamp)) { msg = tal_free(msg); + } } return msg; diff --git a/common/gossip_store.h b/common/gossip_store.h index e1d760860ae6..b5d8fad1905d 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -40,12 +40,13 @@ struct gossip_hdr { * Direct store accessor: loads gossip msg from store. * * Returns NULL if there are no more gossip msgs. + * Updates *end if the known end of file has moved. + * Updates *gossip_store_fd if file has been compacted. */ -u8 *gossip_store_iter(const tal_t *ctx, +u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, - struct gossip_state *gs, - struct gossip_rcvd_filter *grf, - size_t *off); + u32 timestamp_min, u32 timestamp_max, + size_t *off, size_t *end); /** * Gossipd will be writing to this, and it's not atomic! Safest diff --git a/common/per_peer_state.h b/common/per_peer_state.h index a81360ab3485..af41d95f0a8d 100644 --- a/common/per_peer_state.h +++ b/common/per_peer_state.h @@ -6,13 +6,6 @@ #include #include -struct gossip_state { - /* Time for next gossip burst. */ - struct timemono next_gossip; - /* Timestamp filtering for gossip. */ - u32 timestamp_min, timestamp_max; -}; - /* Things we hand between daemons to talk to peers. */ struct per_peer_state { /* If not -1, closed on freeing */ diff --git a/connectd/connectd.c b/connectd/connectd.c index fc02c3242b19..9aab4c9d2545 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -357,7 +357,6 @@ static struct peer *new_peer(struct daemon *daemon, peer->urgent = false; peer->peer_outq = msg_queue_new(peer); peer->subd_outq = msg_queue_new(peer); - peer->grf = new_gossip_rcvd_filter(peer); /* Aim for connection to shuffle data back and forth: sets up * peer->to_subd */ @@ -368,7 +367,6 @@ static struct peer *new_peer(struct daemon *daemon, peer_htable_add(&daemon->peers, peer); tal_add_destructor2(peer, destroy_peer, daemon); - peer->gs = NULL; return peer; } diff --git a/connectd/connectd.h b/connectd/connectd.h index 09c79bd18e3a..ff4057538cd7 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -13,6 +13,20 @@ struct io_conn; struct connecting; struct wireaddr_internal; +/*~ All the gossip_store related fields are kept together for convenience. */ +struct gossip_state { + /* Is it active right now? */ + bool active; + /* Except with dev override, this fires every 60 seconds */ + struct oneshot *gossip_timer; + /* Timestamp filtering for gossip. */ + u32 timestamp_min, timestamp_max; + /* I think this is called "echo cancellation" */ + struct gossip_rcvd_filter *grf; + /* Offset within the gossip_store file */ + size_t off; +}; + /*~ We keep a hash table (ccan/htable) of peers, which tells us what peers are * already connected (by peer->id). */ struct peer { @@ -45,13 +59,8 @@ struct peer { /* Peer sent buffer (for freeing after sending) */ const u8 *sent_to_peer; - /* Gossip store. */ - struct gossip_state *gs; - /* FIXME: move into gs. */ - struct gossip_rcvd_filter *grf; - size_t gossip_store_off; - - struct oneshot *gossip_timer; + /* We stream from the gossip_store for them, when idle */ + struct gossip_state gs; }; /*~ The HTABLE_DEFINE_TYPE() macro needs a keyof() function to extract the key: diff --git a/connectd/multiplex.c b/connectd/multiplex.c index d14119f2e33e..ed3b8eda0b99 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -51,41 +51,28 @@ static void send_warning(struct peer *peer, const char *fmt, ...) va_end(ap); } -/* Either for initial setup, or when they ask by timestamp */ -static bool setup_gossip_filter(struct peer *peer, - u32 first_timestamp, - u32 timestamp_range) -{ - bool immediate_sync; +/* Kicks off write_to_peer() to look for more gossip to send from store */ +static void wake_gossip(struct peer *peer); - /* If this is the first filter, we gossip sync immediately. */ - if (!peer->gs) { - peer->gs = tal(peer, struct gossip_state); - peer->gs->next_gossip = time_mono(); - immediate_sync = true; - } else - immediate_sync = false; +static struct oneshot *gossip_stream_timer(struct peer *peer) +{ + u32 next; /* BOLT #7: * - * The receiver: - * - SHOULD send all gossip messages whose `timestamp` is greater or - * equal to `first_timestamp`, and less than `first_timestamp` plus - * `timestamp_range`. - * - MAY wait for the next outgoing gossip flush to send these. - * ... - * - SHOULD restrict future gossip messages to those whose `timestamp` - * is greater or equal to `first_timestamp`, and less than - * `first_timestamp` plus `timestamp_range`. + * A node: + *... + * - SHOULD flush outgoing gossip messages once every 60 seconds, + * independently of the arrival times of the messages. + * - Note: this results in staggered announcements that are unique + * (not duplicated). */ - peer->gs->timestamp_min = first_timestamp; - peer->gs->timestamp_max = first_timestamp + timestamp_range - 1; - /* Make sure we never leave it on an impossible value. */ - if (peer->gs->timestamp_max < peer->gs->timestamp_min) - peer->gs->timestamp_max = UINT32_MAX; + /* We shorten this for dev_fast_gossip! */ + next = GOSSIP_FLUSH_INTERVAL(peer->daemon->dev_fast_gossip); - peer->gossip_store_off = 1; - return immediate_sync; + return new_reltimer(&peer->daemon->timers, + peer, time_from_sec(next), + wake_gossip, peer); } /* This is called once we need it: otherwise, the gossip_store may not exist, @@ -111,7 +98,7 @@ void setup_peer_gossip_store(struct peer *peer, if (peer->daemon->gossip_store_fd == -1) setup_gossip_store(peer->daemon); - peer->gossip_timer = NULL; + peer->gs.grf = new_gossip_rcvd_filter(peer); /* BOLT #7: * @@ -120,10 +107,17 @@ void setup_peer_gossip_store(struct peer *peer, * - MUST NOT relay any gossip messages it did not generate itself, * unless explicitly requested. */ - if (feature_negotiated(our_features, their_features, OPT_GOSSIP_QUERIES)) + if (feature_negotiated(our_features, their_features, OPT_GOSSIP_QUERIES)) { + peer->gs.gossip_timer = NULL; + peer->gs.active = false; + peer->gs.off = 1; return; + } - setup_gossip_filter(peer, 0, UINT32_MAX); + peer->gs.gossip_timer = gossip_stream_timer(peer); + peer->gs.active = true; + peer->gs.timestamp_min = 0; + peer->gs.timestamp_max = UINT32_MAX; /* BOLT #7: * @@ -136,10 +130,12 @@ void setup_peer_gossip_store(struct peer *peer, * - SHOULD resume normal operation, as specified in the * following [Rebroadcasting](#rebroadcasting) section. */ - if (!feature_offered(their_features, OPT_INITIAL_ROUTING_SYNC)) { + if (feature_offered(their_features, OPT_INITIAL_ROUTING_SYNC)) + peer->gs.off = 1; + else { /* During tests, particularly, we find that the gossip_store * moves fast, so make sure it really does start at the end. */ - peer->gossip_store_off + peer->gs.off = find_gossip_store_end(peer->daemon->gossip_store_fd, peer->daemon->gossip_store_end); } @@ -316,8 +312,11 @@ static struct io_plan *encrypt_and_send(struct peer *peer, /* Kicks off write_to_peer() to look for more gossip to send from store */ static void wake_gossip(struct peer *peer) { - peer->gossip_timer = NULL; + peer->gs.active = true; io_wake(peer->peer_outq); + + /* And go again in 60 seconds (from now, now when we finish!) */ + peer->gs.gossip_timer = gossip_stream_timer(peer); } /* If we are streaming gossip, get something from gossip store */ @@ -326,43 +325,32 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) u8 *msg; /* Not streaming yet? */ - if (!peer->gs) + if (!peer->gs.active) return NULL; - /* Still waiting for timer? */ - if (peer->gossip_timer != NULL) - return NULL; - - msg = gossip_store_iter(ctx, &peer->daemon->gossip_store_fd, - peer->gs, peer->grf, &peer->gossip_store_off); - - /* Cache highest valid offset (FIXME: doesn't really work when - * gossip_store gets rewritten!) */ - if (peer->gossip_store_off > peer->daemon->gossip_store_end) - peer->daemon->gossip_store_end = peer->gossip_store_off; + /* This should be around to kick us every 60 seconds */ + assert(peer->gs.gossip_timer); +again: + msg = gossip_store_next(ctx, &peer->daemon->gossip_store_fd, + peer->gs.timestamp_min, + peer->gs.timestamp_max, + &peer->gs.off, + &peer->daemon->gossip_store_end); + /* Don't send back gossip they sent to us! */ if (msg) { + status_peer_debug(&peer->id, + "Sending gossip %s", + peer_wire_name(fromwire_peektype(msg))); + if (gossip_rcvd_filter_del(peer->gs.grf, msg)) { + msg = tal_free(msg); + goto again; + } status_peer_io(LOG_IO_OUT, &peer->id, msg); return msg; } - /* BOLT #7: - * - * A node: - *... - * - SHOULD flush outgoing gossip messages once every 60 seconds, - * independently of the arrival times of the messages. - * - Note: this results in staggered announcements that are unique - * (not duplicated). - */ - /* We do 60 seconds from *start*, not from *now* */ - peer->gs->next_gossip - = timemono_add(time_mono(), - time_from_sec(GOSSIP_FLUSH_INTERVAL( - peer->daemon->dev_fast_gossip))); - peer->gossip_timer = new_abstimer(&peer->daemon->timers, peer, - peer->gs->next_gossip, - wake_gossip, peer); + peer->gs.active = false; return NULL; } @@ -374,7 +362,7 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) /* We remember these so we don't rexmit them */ if (is_msg_gossip_broadcast(msg)) - gossip_rcvd_filter_add(peer->grf, msg); + gossip_rcvd_filter_add(peer->gs.grf, msg); if (!fromwire_gossip_timestamp_filter(msg, &chain_hash, &first_timestamp, @@ -388,9 +376,21 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) return true; } - /* Returns true the first time. */ - if (setup_gossip_filter(peer, first_timestamp, timestamp_range)) + peer->gs.timestamp_min = first_timestamp; + peer->gs.timestamp_max = first_timestamp + timestamp_range - 1; + /* Make sure we never leave it on an impossible value. */ + if (peer->gs.timestamp_max < peer->gs.timestamp_min) + peer->gs.timestamp_max = UINT32_MAX; + + peer->gs.off = 1; + + /* BOLT #7: + * - MAY wait for the next outgoing gossip flush to send these. + */ + /* We send immediately the first time, after that we wait. */ + if (!peer->gs.gossip_timer) wake_gossip(peer); + return true; } From 39c93ee6e5a1b0975507d0dcd34449877c9b3836 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:45:48 +1030 Subject: [PATCH 0213/1530] connectd: get addresses from lightningd, not gossipd. It's weird to have connectd ask gossipd, when lightningd can just do it and hand all the addresses together. Signed-off-by: Rusty Russell --- connectd/connectd.c | 38 +++---- connectd/connectd_gossipd_wire.csv | 8 -- connectd/connectd_wire.csv | 2 + gossipd/gossipd.c | 27 +++-- gossipd/gossipd_wire.csv | 8 ++ lightningd/channel.c | 8 +- lightningd/connect_control.c | 104 ++++++++++++++++---- lightningd/connect_control.h | 4 +- lightningd/gossip_control.c | 2 + lightningd/peer_control.c | 25 +---- lightningd/test/run-invoice-select-inchan.c | 15 +-- wallet/test/run-wallet.c | 11 +-- 12 files changed, 134 insertions(+), 118 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 9aab4c9d2545..3cbfc3c7ffa6 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1669,37 +1669,19 @@ static void add_seed_addrs(struct wireaddr_internal **addrs, } } -static bool wireaddr_int_equals_wireaddr(struct wireaddr_internal *addr_a, - struct wireaddr *addr_b) +static bool wireaddr_int_equals_wireaddr(const struct wireaddr_internal *addr_a, + const struct wireaddr *addr_b) { if (!addr_a || !addr_b) return false; return wireaddr_eq(&addr_a->u.wireaddr, addr_b); } -/*~ This asks gossipd for any addresses advertized by the node. */ +/*~ Orders the addresses which lightningd gave us. */ static void add_gossip_addrs(struct wireaddr_internal **addrs, - const struct node_id *id, - struct wireaddr_internal *addrhint) + const struct wireaddr *normal_addrs, + const struct wireaddr_internal *addrhint) { - u8 *msg; - struct wireaddr *normal_addrs; - - /* For simplicity, we do this synchronous. */ - msg = towire_gossipd_get_addrs(NULL, id); - if (!wire_sync_write(GOSSIPCTL_FD, take(msg))) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed writing to gossipctl: %s", - strerror(errno)); - - /* This returns 'struct wireaddr's since that's what's supported by - * the BOLT #7 protocol. */ - msg = wire_sync_read(tmpctx, GOSSIPCTL_FD); - if (!fromwire_gossipd_get_addrs_reply(tmpctx, msg, &normal_addrs)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed parsing get_addrs_reply gossipctl: %s", - tal_hex(tmpctx, msg)); - /* Wrap each one in a wireaddr_internal and add to addrs. */ for (size_t i = 0; i < tal_count(normal_addrs); i++) { /* This is not supported, ignore. */ @@ -1736,6 +1718,7 @@ static void add_gossip_addrs(struct wireaddr_internal **addrs, static void try_connect_peer(struct daemon *daemon, const struct node_id *id, u32 seconds_waited, + struct wireaddr *gossip_addrs, struct wireaddr_internal *addrhint STEALS) { struct wireaddr_internal *addrs; @@ -1767,7 +1750,7 @@ static void try_connect_peer(struct daemon *daemon, if (addrhint) tal_arr_expand(&addrs, *addrhint); - add_gossip_addrs(&addrs, id, addrhint); + add_gossip_addrs(&addrs, gossip_addrs, addrhint); if (tal_count(addrs) == 0) { /* Don't resolve via DNS seed if we're supposed to use proxy. */ @@ -1824,13 +1807,14 @@ static void connect_to_peer(struct daemon *daemon, const u8 *msg) struct node_id id; u32 seconds_waited; struct wireaddr_internal *addrhint; + struct wireaddr *addrs; if (!fromwire_connectd_connect_to_peer(tmpctx, msg, - &id, &seconds_waited, - &addrhint)) + &id, &seconds_waited, + &addrs, &addrhint)) master_badmsg(WIRE_CONNECTD_CONNECT_TO_PEER, msg); - try_connect_peer(daemon, &id, seconds_waited, addrhint); + try_connect_peer(daemon, &id, seconds_waited, addrs, addrhint); } /* A peer is gone: clean things up. */ diff --git a/connectd/connectd_gossipd_wire.csv b/connectd/connectd_gossipd_wire.csv index 11133197442f..5517b450afde 100644 --- a/connectd/connectd_gossipd_wire.csv +++ b/connectd/connectd_gossipd_wire.csv @@ -11,11 +11,3 @@ msgdata,gossipd_new_peer,gossip_queries_feature,bool, # if success: + gossip fd msgtype,gossipd_new_peer_reply,4100 msgdata,gossipd_new_peer_reply,success,bool, - -# Connectd asks gossipd for any known addresses for that node. -msgtype,gossipd_get_addrs,4001 -msgdata,gossipd_get_addrs,id,node_id, - -msgtype,gossipd_get_addrs_reply,4101 -msgdata,gossipd_get_addrs_reply,num,u16, -msgdata,gossipd_get_addrs_reply,addrs,wireaddr,num diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 960f74f9f90a..deee01f4f4a3 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -47,6 +47,8 @@ msgdata,connectd_reconnected,id,node_id, msgtype,connectd_connect_to_peer,2001 msgdata,connectd_connect_to_peer,id,node_id, msgdata,connectd_connect_to_peer,seconds_waited,u32, +msgdata,connectd_connect_to_peer,len,u32, +msgdata,connectd_connect_to_peer,addrs,wireaddr,len msgdata,connectd_connect_to_peer,addrhint,?wireaddr_internal, # Connectd->master: connect failed. diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 9cfee53f4e6c..0b76ff8ff230 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -891,10 +891,10 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->connectd); } -/*~ connectd can also ask us if we know any addresses for a given id. */ -static struct io_plan *connectd_get_address(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +/*~ lightningd asks us if we know any addresses for a given id. */ +static struct io_plan *handle_get_address(struct io_conn *conn, + struct daemon *daemon, + const u8 *msg) { struct node_id id; u8 rgb_color[3]; @@ -903,20 +903,17 @@ static struct io_plan *connectd_get_address(struct io_conn *conn, struct wireaddr *addrs; struct lease_rates *rates; - if (!fromwire_gossipd_get_addrs(msg, &id)) { - status_broken("Bad gossipd_get_addrs msg from connectd: %s", - tal_hex(tmpctx, msg)); - return io_close(conn); - } + if (!fromwire_gossipd_get_addrs(msg, &id)) + master_badmsg(WIRE_GOSSIPD_GET_ADDRS, msg); if (!get_node_announcement_by_id(tmpctx, daemon, &id, rgb_color, alias, &features, &addrs, &rates)) addrs = NULL; - daemon_conn_send(daemon->connectd, + daemon_conn_send(daemon->master, take(towire_gossipd_get_addrs_reply(NULL, addrs))); - return daemon_conn_read_next(conn, daemon->connectd); + return daemon_conn_read_next(conn, daemon->master); } /*~ connectd's input handler is very simple. */ @@ -930,12 +927,8 @@ static struct io_plan *connectd_req(struct io_conn *conn, case WIRE_GOSSIPD_NEW_PEER: return connectd_new_peer(conn, daemon, msg); - case WIRE_GOSSIPD_GET_ADDRS: - return connectd_get_address(conn, daemon, msg); - /* We send these, don't receive them. */ case WIRE_GOSSIPD_NEW_PEER_REPLY: - case WIRE_GOSSIPD_GET_ADDRS_REPLY: break; } @@ -1416,6 +1409,9 @@ static struct io_plan *recv_req(struct io_conn *conn, handle_new_lease_rates(daemon, msg); goto done; + case WIRE_GOSSIPD_GET_ADDRS: + return handle_get_address(conn, daemon, msg); + #if DEVELOPER case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: dev_set_max_scids_encode_size(daemon, msg); @@ -1454,6 +1450,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: + case WIRE_GOSSIPD_GET_ADDRS_REPLY: break; } diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 6525397b8810..f0a6a9c898d1 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -108,3 +108,11 @@ msgdata,gossipd_addgossip_reply,err,wirestring, # Updated lease rates available msgtype,gossipd_new_lease_rates,3046 msgdata,gossipd_new_lease_rates,rates,lease_rates, + +# Lightningd asks gossipd for any known addresses for that node. +msgtype,gossipd_get_addrs,3050 +msgdata,gossipd_get_addrs,id,node_id, + +msgtype,gossipd_get_addrs_reply,3150 +msgdata,gossipd_get_addrs_reply,num,u16, +msgdata,gossipd_get_addrs_reply,addrs,wireaddr,num diff --git a/lightningd/channel.c b/lightningd/channel.c index eeb8aba9cf55..79eee3248933 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -874,10 +874,10 @@ static void err_and_reconnect(struct channel *channel, channel_set_owner(channel, NULL); /* Their address only useful if we connected to them */ - delay_then_reconnect(channel, seconds_before_reconnect, - channel->peer->connected_incoming - ? NULL - : &channel->peer->addr); + try_reconnect(channel, seconds_before_reconnect, + channel->peer->connected_incoming + ? NULL + : &channel->peer->addr); } void channel_fail_reconnect_later(struct channel *channel, const char *fmt, ...) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 16940f44c0dd..a8861aad1d4a 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,14 @@ static struct command_result *connect_cmd_succeed(struct command *cmd, return command_success(cmd, response); } +/* FIXME: Reorder! */ +static void try_connect(const tal_t *ctx, + struct lightningd *ld, + const struct node_id *id, + struct channel *channel, + u32 seconds_delay, + const struct wireaddr_internal *addrhint); + static struct command_result *json_connect(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -82,7 +91,6 @@ static struct command_result *json_connect(struct command *cmd, char *ataddr = NULL; const char *name; struct wireaddr_internal *addr; - u8 *msg; const char *err_msg; struct peer *peer; @@ -165,8 +173,7 @@ static struct command_result *json_connect(struct command *cmd, } else addr = NULL; - msg = towire_connectd_connect_to_peer(NULL, &id, 0, addr); - subd_send_msg(cmd->ld->connectd, take(msg)); + try_connect(cmd, cmd->ld, &id, NULL, 0, addr); /* Leave this here for peer_connected or connect_failed. */ new_connect(cmd->ld, &id, cmd); @@ -182,40 +189,80 @@ static const struct json_command connect_command = { }; AUTODATA(json_command, &connect_command); +/* We actually use this even if we don't need a delay, while we talk to + * gossipd to get the addresses. */ struct delayed_reconnect { + struct lightningd *ld; + struct node_id id; + /* May be unset if there's no associated channel */ struct channel *channel; u32 seconds_delayed; struct wireaddr_internal *addrhint; }; -static void maybe_reconnect(struct delayed_reconnect *d) +static void gossipd_got_addrs(struct subd *subd, + const u8 *msg, + const int *fds, + struct delayed_reconnect *d) { - struct peer *peer = d->channel->peer; - - /* Might have gone onchain since we started timer. */ - if (channel_active(d->channel)) { - u8 *msg = towire_connectd_connect_to_peer(NULL, &peer->id, - d->seconds_delayed, - d->addrhint); - subd_send_msg(peer->ld->connectd, take(msg)); + struct wireaddr *addrs; + u8 *connectmsg; + + if (!fromwire_gossipd_get_addrs_reply(tmpctx, msg, &addrs)) + fatal("Gossipd gave bad GOSSIPD_GET_ADDRS_REPLY %s", + tal_hex(msg, msg)); + + /* Might have gone onchain (if it was actually freed, we were too). */ + if (d->channel && !channel_active(d->channel)) { + tal_free(d); + return; } + + connectmsg = towire_connectd_connect_to_peer(NULL, + &d->id, + d->seconds_delayed, + addrs, + d->addrhint); + subd_send_msg(d->ld->connectd, take(connectmsg)); tal_free(d); } -void delay_then_reconnect(struct channel *channel, u32 seconds_delay, - const struct wireaddr_internal *addrhint) +/* We might be off a delay timer. Now ask gossipd about public addresses. */ +static void do_connect(struct delayed_reconnect *d) { - struct delayed_reconnect *d; - struct lightningd *ld = channel->peer->ld; + u8 *msg = towire_gossipd_get_addrs(NULL, &d->id); - if (!ld->reconnect) - return; + subd_req(d, d->ld->gossip, take(msg), -1, 0, gossipd_got_addrs, d); +} - d = tal(channel, struct delayed_reconnect); +/* channel may be NULL here */ +static void try_connect(const tal_t *ctx, + struct lightningd *ld, + const struct node_id *id, + struct channel *channel, + u32 seconds_delay, + const struct wireaddr_internal *addrhint) +{ + struct delayed_reconnect *d; + + d = tal(ctx, struct delayed_reconnect); + d->ld = ld; + d->id = *id; d->channel = channel; d->seconds_delayed = seconds_delay; d->addrhint = tal_dup_or_null(d, struct wireaddr_internal, addrhint); + if (!seconds_delay) { + do_connect(d); + return; + } + + /* We never have a delay when connecting without a channel */ + assert(channel); + channel_set_billboard(channel, false, + tal_fmt(tmpctx, + "Will attempt reconnect " + "in %u seconds", seconds_delay)); log_debug(channel->log, "Will try reconnect in %u seconds", seconds_delay); @@ -224,7 +271,22 @@ void delay_then_reconnect(struct channel *channel, u32 seconds_delay, notleak(new_reltimer(ld->timers, d, timerel_add(time_from_sec(seconds_delay), time_from_usec(pseudorand(1000000))), - maybe_reconnect, d)); + do_connect, d)); +} + +void try_reconnect(struct channel *channel, + u32 seconds_delay, + const struct wireaddr_internal *addrhint) +{ + if (!channel->peer->ld->reconnect) + return; + + try_connect(channel, + channel->peer->ld, + &channel->peer->id, + channel, + seconds_delay, + addrhint); } static void connect_failed(struct lightningd *ld, const u8 *msg) @@ -251,7 +313,7 @@ static void connect_failed(struct lightningd *ld, const u8 *msg) /* If we have an active channel, then reconnect. */ channel = active_channel_by_id(ld, &id, NULL); if (channel) - delay_then_reconnect(channel, seconds_to_delay, addrhint); + try_reconnect(channel, seconds_to_delay, addrhint); } void connect_succeeded(struct lightningd *ld, const struct peer *peer, diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 830332b3d37c..67beb003782b 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -10,8 +10,8 @@ struct wireaddr_internal; int connectd_init(struct lightningd *ld); void connectd_activate(struct lightningd *ld); -void delay_then_reconnect(struct channel *channel, u32 seconds_delay, - const struct wireaddr_internal *addrhint TAKES); +void try_reconnect(struct channel *channel, u32 seconds_delay, + const struct wireaddr_internal *addrhint TAKES); void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 2acf5d15a51e..9b82d9b793db 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -127,6 +127,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: + case WIRE_GOSSIPD_GET_ADDRS: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: @@ -134,6 +135,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: + case WIRE_GOSSIPD_GET_ADDRS_REPLY: break; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4365bfb9e484..e87d0ae8bcc2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1527,7 +1527,6 @@ command_find_channel(struct command *cmd, static void activate_peer(struct peer *peer, u32 delay) { - u8 *msg; struct channel *channel; struct channel_inflight *inflight; struct lightningd *ld = peer->ld; @@ -1535,28 +1534,8 @@ static void activate_peer(struct peer *peer, u32 delay) /* We can only have one active channel: make sure connectd * knows to try reconnecting. */ channel = peer_active_channel(peer); - if (channel && ld->reconnect) { - if (delay > 0) { - channel_set_billboard(channel, false, - tal_fmt(tmpctx, - "Will attempt reconnect " - "in %u seconds", - delay)); - delay_then_reconnect(channel, delay, - peer->connected_incoming - ? NULL - : &peer->addr); - } else { - msg = towire_connectd_connect_to_peer(NULL, - &peer->id, 0, - peer->connected_incoming - ? NULL - : &peer->addr); - subd_send_msg(ld->connectd, take(msg)); - channel_set_billboard(channel, false, - "Attempting to reconnect"); - } - } + if (channel) + try_reconnect(channel, delay, &peer->addr); list_for_each(&peer->channels, channel, list) { if (channel_unsaved(channel)) diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 7c1197cc7d2d..cdfcd9ee44cb 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -94,10 +94,6 @@ void channel_internal_error(struct channel *channel UNNEEDED, const char *fmt UN /* Generated stub for channel_last_funding_feerate */ u32 channel_last_funding_feerate(const struct channel *channel UNNEEDED) { fprintf(stderr, "channel_last_funding_feerate called!\n"); abort(); } -/* Generated stub for channel_set_billboard */ -void channel_set_billboard(struct channel *channel UNNEEDED, bool perm UNNEEDED, - const char *str TAKES UNNEEDED) -{ fprintf(stderr, "channel_set_billboard called!\n"); abort(); } /* Generated stub for channel_set_last_tx */ void channel_set_last_tx(struct channel *channel UNNEEDED, struct bitcoin_tx *tx UNNEEDED, @@ -164,10 +160,6 @@ void db_begin_transaction_(struct db *db UNNEEDED, const char *location UNNEEDED /* Generated stub for db_commit_transaction */ void db_commit_transaction(struct db *db UNNEEDED) { fprintf(stderr, "db_commit_transaction called!\n"); abort(); } -/* Generated stub for delay_then_reconnect */ -void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, - const struct wireaddr_internal *addrhint TAKES UNNEEDED) -{ fprintf(stderr, "delay_then_reconnect called!\n"); abort(); } /* Generated stub for delete_channel */ void delete_channel(struct channel *channel STEALS UNNEEDED) { fprintf(stderr, "delete_channel called!\n"); abort(); } @@ -640,9 +632,6 @@ u8 *towire_channeld_dev_reenable_commit(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channeld_specific_feerates */ u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_base UNNEEDED, u32 feerate_ppm UNNEEDED) { fprintf(stderr, "towire_channeld_specific_feerates called!\n"); abort(); } -/* 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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } @@ -671,6 +660,10 @@ 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 try_reconnect */ +void try_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, + const struct wireaddr_internal *addrhint TAKES UNNEEDED) +{ fprintf(stderr, "try_reconnect called!\n"); abort(); } /* Generated stub for version */ const char *version(void) { fprintf(stderr, "version called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 9f726f2729e0..695998e30b1c 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -95,10 +95,6 @@ struct onionreply *create_onionreply(const tal_t *ctx UNNEEDED, const struct secret *shared_secret UNNEEDED, const u8 *failure_msg UNNEEDED) { fprintf(stderr, "create_onionreply called!\n"); abort(); } -/* Generated stub for delay_then_reconnect */ -void delay_then_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, - const struct wireaddr_internal *addrhint TAKES UNNEEDED) -{ fprintf(stderr, "delay_then_reconnect called!\n"); abort(); } /* Generated stub for derive_channel_id */ void derive_channel_id(struct channel_id *channel_id UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED) @@ -711,9 +707,6 @@ u8 *towire_channeld_sending_commitsig_reply(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channeld_specific_feerates */ u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_base UNNEEDED, u32 feerate_ppm UNNEEDED) { fprintf(stderr, "towire_channeld_specific_feerates called!\n"); abort(); } -/* 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_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(); } @@ -799,6 +792,10 @@ 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 try_reconnect */ +void try_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, + const struct wireaddr_internal *addrhint TAKES UNNEEDED) +{ fprintf(stderr, "try_reconnect called!\n"); abort(); } /* Generated stub for watch_txid */ struct txwatch *watch_txid(const tal_t *ctx UNNEEDED, struct chain_topology *topo UNNEEDED, From 26b9384fd02a2280d0eacd23b3fc06ae89e2454f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:45:58 +1030 Subject: [PATCH 0214/1530] various: minor cleanups from Christian's review. More significant things have been folded. Signed-off-by: Rusty Russell --- common/gossip_store.c | 2 +- common/peer_io.c | 8 ++++---- connectd/connectd.h | 2 +- connectd/multiplex.c | 5 ++++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index 9056dee01158..e730d4220c59 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -86,7 +86,7 @@ u8 *gossip_store_next(const tal_t *ctx, if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) status_failed(STATUS_FAIL_INTERNAL_ERROR, - "gossip_store: bad checksum offset %zu" + "gossip_store: bad checksum at offset %zu" ": %s", *off, tal_hex(tmpctx, msg)); diff --git a/common/peer_io.c b/common/peer_io.c index 587a14901ba4..41c6caa97ca9 100644 --- a/common/peer_io.c +++ b/common/peer_io.c @@ -24,11 +24,11 @@ void peer_write(struct per_peer_state *pps, const void *msg TAKES) u8 *peer_read(const tal_t *ctx, struct per_peer_state *pps) { - u8 *dec = wire_sync_read(ctx, pps->peer_fd); - if (!dec) + u8 *msg = wire_sync_read(ctx, pps->peer_fd); + if (!msg) peer_failed_connection_lost(); - status_peer_io(LOG_IO_IN, NULL, dec); + status_peer_io(LOG_IO_IN, NULL, msg); - return dec; + return msg; } diff --git a/connectd/connectd.h b/connectd/connectd.h index ff4057538cd7..b29ff00376c4 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -47,7 +47,7 @@ struct peer { /* Final message to send to peer (and hangup) */ u8 *final_msg; - /* When we write something which wants Nagle overridden */ + /* When socket has Nagle overridden */ bool urgent; /* Input buffers. */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index ed3b8eda0b99..cef5cd630706 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -481,8 +481,11 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, decrypted = cryptomsg_decrypt_body(NULL, &peer->cs, peer->peer_in); - if (!decrypted) + if (!decrypted) { + status_peer_debug(&peer->id, "Bad encrypted packet len %zu", + tal_bytelen(peer->peer_in)); return io_close(peer_conn); + } tal_free(peer->peer_in); /* If we swallow this, just try again. */ From bb5beeddd7b56b283aac2873d433ec7ce0c18d25 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:46:04 +1030 Subject: [PATCH 0215/1530] connectd: drop support (unused) for @ during handshake. We could implement it, but we don't have to. Signed-off-by: Rusty Russell --- connectd/peer_exchange_initmsg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index aa6aa0da8f88..b8896629f2db 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -216,7 +216,8 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, next = peer_write_postclose; break; case DEV_DISCONNECT_BLACKHOLE: - dev_blackhole_fd(io_conn_fd(conn)); + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Blackhole not supported during handshake"); break; case DEV_DISCONNECT_NORMAL: break; From a93c49ca65319d345ac7a108d88d9a09049ab52e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:46:10 +1030 Subject: [PATCH 0216/1530] connectd: implement @ correctly. dev_blackhole_fd was a hack, and doesn't work well now we are async (it worked for sync comms in per-peer daemons, but now we could sneak through a read before we get to the next write). So, make explicit flags and use them. This is much easier now we have all peer comms in one place. Signed-off-by: Rusty Russell --- common/dev_disconnect.c | 52 ----------------------------------------- common/dev_disconnect.h | 3 --- connectd/connectd.c | 5 ++++ connectd/connectd.h | 6 +++++ connectd/multiplex.c | 42 ++++++++++++++++++++------------- 5 files changed, 37 insertions(+), 71 deletions(-) diff --git a/common/dev_disconnect.c b/common/dev_disconnect.c index 608b2f7a842c..8a6abe6bc20c 100644 --- a/common/dev_disconnect.c +++ b/common/dev_disconnect.c @@ -105,56 +105,4 @@ void dev_sabotage_fd(int fd, bool close_fd) dup2(fds[1], fd); close(fds[1]); } - -/* Replace fd with blackhole until dev_disconnect file is truncated. */ -void dev_blackhole_fd(int fd) -{ - int fds[2]; - int i; - struct stat st; - - int maxfd; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) - err(1, "dev_blackhole_fd: creating socketpair"); - - switch (fork()) { - case -1: - err(1, "dev_blackhole_fd: forking"); - case 0: - /* Close everything but the dev_disconnect_fd, the socket - * which is pretending to be the peer, and stderr. - * The "correct" way to do this would be to move the - * fds we want to preserve to the low end (0, 1, 2...) - * of the fd space and then just do a single closefrom - * call, but dup2 could fail with ENFILE (which is a - * *system*-level error, i.e. the entire system has too - * many processes with open files) and we have no - * convenient way to inform the parent of the error. - * So loop until we reach whichever is higher of fds[0] - * or dev_disconnect_fd, and *then* closefrom after that. - */ - maxfd = (fds[0] > dev_disconnect_fd) ? fds[0] : - dev_disconnect_fd ; - for (i = 0; i < maxfd; i++) - if (i != fds[0] - && i != dev_disconnect_fd - && i != STDERR_FILENO) - close(i); - closefrom(maxfd + 1); - - /* Close once dev_disconnect file is truncated. */ - for (;;) { - if (fstat(dev_disconnect_fd, &st) != 0) - err(1, "fstat of dev_disconnect_fd failed"); - if (st.st_size == 0) - _exit(0); - sleep(1); - } - } - - close(fds[0]); - dup2(fds[1], fd); - close(fds[1]); -} #endif diff --git a/common/dev_disconnect.h b/common/dev_disconnect.h index e1351478dcae..9cd11f990865 100644 --- a/common/dev_disconnect.h +++ b/common/dev_disconnect.h @@ -25,9 +25,6 @@ enum dev_disconnect dev_disconnect(const struct node_id *id, int pkt_type); /* Make next write on fd fail as if they'd disconnected. */ 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); - /* For debug code to set in daemon. */ void dev_disconnect_init(int fd); diff --git a/connectd/connectd.c b/connectd/connectd.c index 3cbfc3c7ffa6..59492daa1465 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -358,6 +358,11 @@ static struct peer *new_peer(struct daemon *daemon, peer->peer_outq = msg_queue_new(peer); peer->subd_outq = msg_queue_new(peer); +#if DEVELOPER + peer->dev_writes_enabled = NULL; + peer->dev_read_enabled = true; +#endif + /* Aim for connection to shuffle data back and forth: sets up * peer->to_subd */ if (!multiplex_subd_setup(peer, fd_for_subd)) diff --git a/connectd/connectd.h b/connectd/connectd.h index b29ff00376c4..d99e00b4c9d3 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -61,6 +61,12 @@ struct peer { /* We stream from the gossip_store for them, when idle */ struct gossip_state gs; + +#if DEVELOPER + bool dev_read_enabled; + /* If non-NULL, this counts down; 0 means disable */ + u32 *dev_writes_enabled; +#endif }; /*~ The HTABLE_DEFINE_TYPE() macro needs a keyof() function to extract the key: diff --git a/connectd/multiplex.c b/connectd/multiplex.c index cef5cd630706..cad5c1cbc47d 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -155,20 +155,6 @@ static struct io_plan *after_final_msg(struct io_conn *peer_conn, return io_close(peer_conn); } -#if DEVELOPER -static struct io_plan *write_to_peer(struct io_conn *peer_conn, - struct peer *peer); - -static struct io_plan *dev_leave_hanging(struct io_conn *peer_conn, - struct peer *peer) -{ - /* We don't tell the peer we're disconnecting, but from now on - * our writes go nowhere, and there's nothing to read. */ - dev_sabotage_fd(io_conn_fd(peer_conn), false); - return write_to_peer(peer_conn, peer); -} -#endif /* DEVELOPER */ - /* We're happy for the kernel to batch update and gossip messages, but a * commitment message, for example, should be instantly sent. There's no * great way of doing this, unfortunately. @@ -287,15 +273,21 @@ static struct io_plan *encrypt_and_send(struct peer *peer, tal_free(msg); return io_close(peer->to_peer); case DEV_DISCONNECT_AFTER: + /* Disallow reads from now on */ + peer->dev_read_enabled = false; next = (void *)io_close_cb; break; case DEV_DISCONNECT_BLACKHOLE: - dev_blackhole_fd(io_conn_fd(peer->to_peer)); + /* Disable both reads and writes from now on */ + peer->dev_read_enabled = false; + peer->dev_writes_enabled = talz(peer, u32); break; case DEV_DISCONNECT_NORMAL: break; case DEV_DISCONNECT_DISABLE_AFTER: - next = dev_leave_hanging; + peer->dev_read_enabled = false; + peer->dev_writes_enabled = tal(peer, u32); + *peer->dev_writes_enabled = 1; break; } #endif @@ -426,6 +418,18 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, } } + /* dev_disconnect can disable writes */ +#if DEVELOPER + if (peer->dev_writes_enabled) { + if (*peer->dev_writes_enabled == 0) { + tal_free(msg); + /* Continue, to drain queue */ + return write_to_peer(peer_conn, peer); + } + (*peer->dev_writes_enabled)--; + } +#endif + return encrypt_and_send(peer, take(msg), write_to_peer); } @@ -488,6 +492,12 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, } tal_free(peer->peer_in); + /* dev_disconnect can disable read */ + if (!IFDEV(peer->dev_read_enabled, true)) { + tal_free(decrypted); + return read_hdr_from_peer(peer_conn, peer); + } + /* If we swallow this, just try again. */ if (handle_message_locally(peer, decrypted)) { tal_free(decrypted); From d51fb5207a5918190285dc10bd5a9d4c63db8826 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:46:18 +1030 Subject: [PATCH 0217/1530] msg_queue: don't allow magic MSG_PASS_FD message for peers. msg_queue was originally designed for inter-daemon comms, and so it has a special mechanism to mark that we're trying to send an fd. Unfortunately, a peer could also send such a message, confusing us! Signed-off-by: Rusty Russell --- channeld/channeld.c | 4 ++-- common/daemon_conn.c | 6 +++--- common/msg_queue.c | 11 ++++++++--- common/msg_queue.h | 7 ++++--- connectd/connectd.c | 4 ++-- lightningd/subd.c | 4 ++-- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 2cccbfefb32c..980bb1033648 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -4045,7 +4045,7 @@ int main(int argc, char *argv[]) peer->have_sigs[LOCAL] = peer->have_sigs[REMOTE] = false; peer->announce_depth_reached = false; peer->channel_local_active = false; - peer->from_master = msg_queue_new(peer); + peer->from_master = msg_queue_new(peer, true); peer->shutdown_sent[LOCAL] = false; peer->shutdown_wrong_funding = NULL; peer->last_update_timestamp = 0; @@ -4053,7 +4053,7 @@ int main(int argc, char *argv[]) #if EXPERIMENTAL_FEATURES peer->stfu = false; peer->stfu_sent[LOCAL] = peer->stfu_sent[REMOTE] = false; - peer->update_queue = msg_queue_new(peer); + peer->update_queue = msg_queue_new(peer, false); #endif /* We send these to HSM to get real signatures; don't have valgrind diff --git a/common/daemon_conn.c b/common/daemon_conn.c index 0a9a1ef2582c..25bbac2bcd8d 100644 --- a/common/daemon_conn.c +++ b/common/daemon_conn.c @@ -54,7 +54,7 @@ static struct io_plan *daemon_conn_write_next(struct io_conn *conn, } if (msg) { - int fd = msg_extract_fd(msg); + int fd = msg_extract_fd(dc->out, msg); if (fd >= 0) { tal_free(msg); return io_send_fd(conn, fd, true, @@ -82,7 +82,7 @@ bool daemon_conn_sync_flush(struct daemon_conn *dc) /* Flush existing messages. */ while ((msg = msg_dequeue(dc->out)) != NULL) { - int fd = msg_extract_fd(msg); + int fd = msg_extract_fd(dc->out, msg); if (fd >= 0) { tal_free(msg); if (!fdpass_send(daemon_fd, fd)) @@ -125,7 +125,7 @@ struct daemon_conn *daemon_conn_new_(const tal_t *ctx, int fd, dc->outq_empty = outq_empty; dc->arg = arg; dc->msg_in = NULL; - dc->out = msg_queue_new(dc); + dc->out = msg_queue_new(dc, true); dc->conn = io_new_conn(dc, fd, daemon_conn_start, dc); tal_add_destructor2(dc->conn, destroy_dc_from_conn, dc); diff --git a/common/msg_queue.c b/common/msg_queue.c index 120a77a9f9fb..f3926ab67b5e 100644 --- a/common/msg_queue.c +++ b/common/msg_queue.c @@ -5,12 +5,14 @@ #include struct msg_queue { + bool fd_passing; const u8 **q; }; -struct msg_queue *msg_queue_new(const tal_t *ctx) +struct msg_queue *msg_queue_new(const tal_t *ctx, bool fd_passing) { struct msg_queue *q = tal(ctx, struct msg_queue); + q->fd_passing = fd_passing; q->q = tal_arr(q, const u8 *, 0); return q; } @@ -30,13 +32,15 @@ size_t msg_queue_length(const struct msg_queue *q) void msg_enqueue(struct msg_queue *q, const u8 *add) { - assert(fromwire_peektype(add) != MSG_PASS_FD); + if (q->fd_passing) + assert(fromwire_peektype(add) != MSG_PASS_FD); do_enqueue(q, add); } void msg_enqueue_fd(struct msg_queue *q, int fd) { u8 *fdmsg = tal_arr(q, u8, 0); + assert(q->fd_passing); towire_u16(&fdmsg, MSG_PASS_FD); towire_u32(&fdmsg, fd); do_enqueue(q, take(fdmsg)); @@ -56,11 +60,12 @@ const u8 *msg_dequeue(struct msg_queue *q) return msg; } -int msg_extract_fd(const u8 *msg) +int msg_extract_fd(const struct msg_queue *q, const u8 *msg) { const u8 *p = msg + sizeof(u16); size_t len = tal_count(msg) - sizeof(u16); + assert(q->fd_passing); if (fromwire_peektype(msg) != MSG_PASS_FD) return -1; diff --git a/common/msg_queue.h b/common/msg_queue.h index 15c9c66d431b..8bdbe15577c1 100644 --- a/common/msg_queue.h +++ b/common/msg_queue.h @@ -8,8 +8,9 @@ /* Reserved type used to indicate we're actually passing an fd. */ #define MSG_PASS_FD 0xFFFF -/* Allocate a new msg queue. */ -struct msg_queue *msg_queue_new(const tal_t *ctx); +/* Allocate a new msg queue; if we control all msgs we send/receive, + * we can pass fds. Otherwise, set @fd_passing to false. */ +struct msg_queue *msg_queue_new(const tal_t *ctx, bool fd_passing); /* If add is taken(), freed after sending. msg_wake() implied. */ void msg_enqueue(struct msg_queue *q, const u8 *add TAKES); @@ -27,7 +28,7 @@ void msg_wake(const struct msg_queue *q); const u8 *msg_dequeue(struct msg_queue *q); /* Returns -1 if not an fd: close after sending. */ -int msg_extract_fd(const u8 *msg); +int msg_extract_fd(const struct msg_queue *q, const u8 *msg); #define msg_queue_wait(conn, q, next, arg) \ io_out_wait((conn), (q), (next), (arg)) diff --git a/connectd/connectd.c b/connectd/connectd.c index 59492daa1465..287c4b5fc733 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -355,8 +355,8 @@ static struct peer *new_peer(struct daemon *daemon, peer->peer_in = NULL; peer->sent_to_peer = NULL; peer->urgent = false; - peer->peer_outq = msg_queue_new(peer); - peer->subd_outq = msg_queue_new(peer); + peer->peer_outq = msg_queue_new(peer, false); + peer->subd_outq = msg_queue_new(peer, false); #if DEVELOPER peer->dev_writes_enabled = NULL; diff --git a/lightningd/subd.c b/lightningd/subd.c index 17ba6dc09cf7..36c836308145 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -657,7 +657,7 @@ static struct io_plan *msg_send_next(struct io_conn *conn, struct subd *sd) if (!msg) return msg_queue_wait(conn, sd->outq, msg_send_next, sd); - fd = msg_extract_fd(msg); + fd = msg_extract_fd(sd->outq, msg); if (fd >= 0) { tal_free(msg); return io_send_fd(conn, fd, true, msg_send_next, sd); @@ -741,7 +741,7 @@ static struct subd *new_subd(struct lightningd *ld, sd->errcb = errcb; sd->billboardcb = billboardcb; sd->fds_in = NULL; - sd->outq = msg_queue_new(sd); + sd->outq = msg_queue_new(sd, true); tal_add_destructor(sd, destroy_subd); list_head_init(&sd->reqs); sd->channel = channel; From ea73e49f2ceaf41072c1e3c804d68e5500550824 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:46:35 +1030 Subject: [PATCH 0218/1530] ccan: update to get io_sock_shutdown Signed-off-by: Rusty Russell --- ccan/README | 2 +- ccan/ccan/io/io.c | 12 ++++++++++++ ccan/ccan/io/io.h | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/ccan/README b/ccan/README index 503ba45676df..8b86e68fd83a 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2523-gb15b3673 +CCAN version: init-2524-g609670cc diff --git a/ccan/ccan/io/io.c b/ccan/ccan/io/io.c index 36dcb81e720f..12df06d82cb6 100644 --- a/ccan/ccan/io/io.c +++ b/ccan/ccan/io/io.c @@ -529,6 +529,18 @@ bool io_plan_out_started(const struct io_conn *conn) return conn->plan[IO_OUT].status == IO_POLLING_STARTED; } +/* Despite being a TCP expert, I missed the full extent of this + * problem. The legendary ZmnSCPxj implemented it (with the URL + * pointing to the explanation), and I imitate that here. */ +struct io_plan *io_sock_shutdown(struct io_conn *conn) +{ + if (shutdown(io_conn_fd(conn), SHUT_WR) != 0) + return io_close(conn); + + /* And leave unset .*/ + return &conn->plan[IO_IN]; +} + bool io_flush_sync(struct io_conn *conn) { struct io_plan *plan = &conn->plan[IO_OUT]; diff --git a/ccan/ccan/io/io.h b/ccan/ccan/io/io.h index e6905fb9241a..1197626f1267 100644 --- a/ccan/ccan/io/io.h +++ b/ccan/ccan/io/io.h @@ -389,6 +389,40 @@ struct io_plan *io_out_always_(struct io_conn *conn, void *), void *arg); +/** + * io_sock_shutdown - start socket close process (flushes TCP sockets). + * @conn: the connection the plan is for + * + * Simply closing a TCP socket can lose data; unfortunately you should + * shutdown(SHUT_WR) and wait for the other side to see this and close. + * Of course, you also need to set a timer, in case it doesn't (you may + * already have some responsiveness timer, of course). + * + * On error, is equivalent to io_close(). + * + * Example: + * #include + * + * // Timer infra needs wrapper to contain extra data. + * struct timeout_timer { + * struct timer t; + * struct io_conn *conn; + * }; + * static struct timers timers; + * + * static struct io_plan *flush_and_close(struct io_conn *conn) + * { + * struct timeout_timer *timeout; + * // Freed if conn closes normally. + * timeout = tal(conn, struct timeout_timer); + * timeout->conn = conn; + * timeout->t = conn; + * timer_addrel(&timers, &timeout->t, time_from_sec(5)); + * return io_sock_shutdown(conn); + * } + */ +struct io_plan *io_sock_shutdown(struct io_conn *conn); + /** * io_connect - create an asynchronous connection to a listening socket. * @conn: the connection that plan is for. From d29795a19829e308e66daf87524420bd83889c7f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:46:49 +1030 Subject: [PATCH 0219/1530] connectd: don't just close to peer, but use shutdown(). We would lose packets sometimes due to this previously, but it doesn't happen over localhost so our tests didn't notice. However, now we have connectd being sole thing talking to peers, we can do a more elegant shutdown, which should fix closing. Signed-off-by: Rusty Russell Changelog-Fixed: Protocol: Always flush sockets to increase chance that final message get to peer (esp. error packets). --- connectd/connectd.c | 27 +++++++++++++++++++-------- connectd/connectd.h | 6 ++++++ connectd/multiplex.c | 42 ++++++++++++++++++++++++++++++++++++++++++ connectd/multiplex.h | 3 +++ 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 287c4b5fc733..10944467b60e 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -355,6 +355,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->peer_in = NULL; peer->sent_to_peer = NULL; peer->urgent = false; + peer->told_to_close = false; peer->peer_outq = msg_queue_new(peer, false); peer->subd_outq = msg_queue_new(peer, false); @@ -1822,6 +1823,18 @@ static void connect_to_peer(struct daemon *daemon, const u8 *msg) try_connect_peer(daemon, &id, seconds_waited, addrs, addrhint); } +void peer_conn_closed(struct peer *peer) +{ + /* Wake up in case there's a reconnecting peer waiting in io_wait. */ + io_wake(peer); + + /* 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. That's why we have + * a destructor attached to peer (called destroy_peer by + * convention). */ + tal_free(peer); +} + /* A peer is gone: clean things up. */ static void cleanup_dead_peer(struct daemon *daemon, const struct node_id *id) { @@ -1835,14 +1848,12 @@ static void cleanup_dead_peer(struct daemon *daemon, const struct node_id *id) type_to_string(tmpctx, struct node_id, id)); status_peer_debug(id, "disconnect"); - /* Wake up in case there's a reconnecting peer waiting in io_wait. */ - io_wake(peer); - - /* 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. That's why we have - * a destructor attached to peer (called destroy_peer by - * convention). */ - tal_free(peer); + /* Make sure we flush any outstanding writes! */ + if (peer->to_peer) { + close_peer_conn(peer); + /* It calls peer_conn_closed() when done */ + } else + peer_conn_closed(peer); } /* lightningd tells us a peer has disconnected. */ diff --git a/connectd/connectd.h b/connectd/connectd.h index d99e00b4c9d3..aa151ea891c3 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -47,6 +47,9 @@ struct peer { /* Final message to send to peer (and hangup) */ u8 *final_msg; + /* Set when we want to close. */ + bool told_to_close; + /* When socket has Nagle overridden */ bool urgent; @@ -181,4 +184,7 @@ struct io_plan *peer_connected(struct io_conn *conn, const u8 *their_features TAKES, bool incoming); +/* Called when peer->peer_conn is finally freed */ +void peer_conn_closed(struct peer *peer); + #endif /* LIGHTNING_CONNECTD_CONNECTD_H */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index cad5c1cbc47d..55f23f64ea49 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -386,6 +387,22 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) return true; } +static void close_timeout(struct peer *peer) +{ + /* BROKEN means we'll trigger CI if we see it, though it's possible */ + status_peer_broken(&peer->id, "Peer did not close, forcing close"); + tal_free(peer->to_peer); +} + +/* Close this in 5 seconds if it doesn't do so by itself. */ +static void set_closing_timer(struct peer *peer, + struct io_conn *peer_conn) +{ + notleak(new_reltimer(&peer->daemon->timers, + peer_conn, time_from_sec(5), + close_timeout, peer)); +} + static struct io_plan *write_to_peer(struct io_conn *peer_conn, struct peer *peer) { @@ -406,6 +423,13 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, peer->final_msg, after_final_msg); } + + /* We close once subds are all closed. */ + if (!peer->to_subd) { + set_closing_timer(peer, peer_conn); + return io_sock_shutdown(peer_conn); + } + /* If they want us to send gossip, do so now. */ msg = maybe_from_gossip_store(NULL, peer); if (!msg) { @@ -498,6 +522,12 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return read_hdr_from_peer(peer_conn, peer); } + /* Don't process packets while we're closing */ + if (peer->told_to_close) { + tal_free(decrypted); + return read_hdr_from_peer(peer_conn, peer); + } + /* If we swallow this, just try again. */ if (handle_message_locally(peer, decrypted)) { tal_free(decrypted); @@ -560,6 +590,15 @@ static void destroy_subd_conn(struct io_conn *subd_conn, struct peer *peer) msg_wake(peer->peer_outq); } +void close_peer_conn(struct peer *peer) +{ + /* Make write_to_peer do flush after writing */ + peer->told_to_close = true; + + /* In case it's not currently writing, wake write_to_peer */ + msg_wake(peer->peer_outq); +} + bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd) { int fds[2]; @@ -583,6 +622,9 @@ static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) /* Close internal connections if not already. */ if (peer->to_subd) io_close(peer->to_subd); + + if (peer->told_to_close) + peer_conn_closed(peer); } struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 524e4829966a..0b3ba146a1ff 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -27,4 +27,7 @@ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); void setup_peer_gossip_store(struct peer *peer, const struct feature_set *our_features, const u8 *their_features); + +/* Start the process of flushing and closing the peer_conn */ +void close_peer_conn(struct peer *peer); #endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ From 0841e4190b831c7f345ee53ae5b726978d01fb8e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:46:55 +1030 Subject: [PATCH 0220/1530] connectd: also do the shutdown()-close for final_msg sends. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 55f23f64ea49..a5cebf4abfa3 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -142,20 +142,6 @@ void setup_peer_gossip_store(struct peer *peer, } } -/* These four function handle subd->peer */ -static struct io_plan *after_final_msg(struct io_conn *peer_conn, - struct peer *peer) -{ - /* io_close will want to free this itself! */ - assert(peer->to_peer == peer_conn); - - /* Invert ownership, so io_close frees peer for us */ - tal_steal(NULL, peer_conn); - tal_steal(peer_conn, peer); - - return io_close(peer_conn); -} - /* We're happy for the kernel to batch update and gossip messages, but a * commitment message, for example, should be instantly sent. There's no * great way of doing this, unfortunately. @@ -415,15 +401,15 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, /* Pop tail of send queue */ msg = msg_dequeue(peer->peer_outq); - /* Nothing to send? */ - if (!msg) { - /* Send final once subd is not longer connected */ - if (peer->final_msg && !peer->to_subd) { - return encrypt_and_send(peer, - peer->final_msg, - after_final_msg); - } + /* Is it time to send final? */ + if (!msg && peer->final_msg && !peer->to_subd) { + /* OK, send this then close. */ + msg = peer->final_msg; + peer->final_msg = NULL; + } + /* Still nothing to send? */ + if (!msg) { /* We close once subds are all closed. */ if (!peer->to_subd) { set_closing_timer(peer, peer_conn); @@ -641,6 +627,7 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) { + peer->told_to_close = true; peer->final_msg = tal_dup_talarr(peer, u8, final_msg); if (!peer->to_subd) io_wake(peer->peer_outq); From 1ae31724090031cd549b5169ac2f3fb2aeb3537b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:47:01 +1030 Subject: [PATCH 0221/1530] connectd: flush queues before hanging up. This is critical in the common case where peer sends an error and hangs up: we almost never get to relay the error to the subd in time. This also applies in the other direction: we need to flush the queue to the peer when the subd closes. Note we only free the actual peer struct when lightningd reaps us with connectd_peer_disconnected(). Signed-off-by: Rusty Russell --- connectd/connectd.c | 20 ++++++++++++-------- connectd/multiplex.c | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 10944467b60e..e8897c7d3ad6 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -364,12 +364,15 @@ static struct peer *new_peer(struct daemon *daemon, peer->dev_read_enabled = true; #endif + peer->to_peer = conn; + /* Aim for connection to shuffle data back and forth: sets up * peer->to_subd */ if (!multiplex_subd_setup(peer, fd_for_subd)) return tal_free(peer); - peer->to_peer = tal_steal(peer, conn); + /* Now we own it */ + tal_steal(peer, peer->to_peer); peer_htable_add(&daemon->peers, peer); tal_add_destructor2(peer, destroy_peer, daemon); @@ -1825,6 +1828,11 @@ static void connect_to_peer(struct daemon *daemon, const u8 *msg) void peer_conn_closed(struct peer *peer) { + /* These should be closed already! */ + assert(!peer->to_subd); + assert(!peer->to_peer); + assert(peer->told_to_close); + /* Wake up in case there's a reconnecting peer waiting in io_wait. */ io_wake(peer); @@ -1848,12 +1856,8 @@ static void cleanup_dead_peer(struct daemon *daemon, const struct node_id *id) type_to_string(tmpctx, struct node_id, id)); status_peer_debug(id, "disconnect"); - /* Make sure we flush any outstanding writes! */ - if (peer->to_peer) { - close_peer_conn(peer); - /* It calls peer_conn_closed() when done */ - } else - peer_conn_closed(peer); + /* When it's finished, it will call peer_conn_closed() */ + close_peer_conn(peer); } /* lightningd tells us a peer has disconnected. */ @@ -1893,7 +1897,7 @@ static void peer_final_msg(struct io_conn *conn, /* This can happen if peer hung up on us. */ peer = peer_htable_get(&daemon->peers, &id); if (peer) { - /* Log and encrypt message for peer. */ + /* Log message for peer. */ status_peer_io(LOG_IO_OUT, &id, finalmsg); multiplex_final_msg(peer, take(finalmsg)); } diff --git a/connectd/multiplex.c b/connectd/multiplex.c index a5cebf4abfa3..8fc361408e5a 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -46,6 +46,7 @@ static void send_warning(struct peer *peer, const char *fmt, ...) /* Close locally, send msg as final warning */ io_close(peer->to_subd); + peer->to_subd = NULL; va_start(ap, fmt); peer->final_msg = towire_warningfmtv(peer, NULL, fmt, ap); @@ -475,6 +476,10 @@ static struct io_plan *write_to_subd(struct io_conn *subd_conn, /* Nothing to send? */ if (!msg) { + /* If peer is closed, close this. */ + if (!peer->to_peer) + return io_close(subd_conn); + /* Tell them to read again. */ io_wake(&peer->peer_in); @@ -520,6 +525,12 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return read_hdr_from_peer(peer_conn, peer); } + /* If there's no subd, discard and keep reading. */ + if (!peer->to_subd) { + tal_free(decrypted); + return read_hdr_from_peer(peer_conn, peer); + } + /* Tell them to write. */ msg_enqueue(peer->subd_outq, take(decrypted)); @@ -574,6 +585,14 @@ static void destroy_subd_conn(struct io_conn *subd_conn, struct peer *peer) /* In case they were waiting for this to send final_msg */ if (peer->final_msg) msg_wake(peer->peer_outq); + + /* Make sure we try to keep reading from peer, so we know if + * it hangs up! */ + io_wake(&peer->peer_in); + + /* If no peer, finally time to close */ + if (!peer->to_peer && peer->told_to_close) + peer_conn_closed(peer); } void close_peer_conn(struct peer *peer) @@ -581,6 +600,12 @@ void close_peer_conn(struct peer *peer) /* Make write_to_peer do flush after writing */ peer->told_to_close = true; + /* Already dead? */ + if (!peer->to_subd && !peer->to_peer) { + peer_conn_closed(peer); + return; + } + /* In case it's not currently writing, wake write_to_peer */ msg_wake(peer->peer_outq); } @@ -605,9 +630,11 @@ static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) assert(peer->to_peer == peer_conn); peer->to_peer = NULL; - /* Close internal connections if not already. */ - if (peer->to_subd) - io_close(peer->to_subd); + /* Flush internal connections if not already. */ + if (peer->to_subd) { + msg_wake(peer->subd_outq); + return; + } if (peer->told_to_close) peer_conn_closed(peer); From d042cbc3446dd5ce1124eb7fea34591420ee45c4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:47:06 +1030 Subject: [PATCH 0222/1530] patch dual-open-control-double-notify-fix.patch --- lightningd/dual_open_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index b3161cb8627a..1728b3a45d1c 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -66,9 +66,9 @@ void channel_unsaved_close_conn(struct channel *channel, const char *why) " Disconnecting and deleting channel. Reason: %s", why); - notify_disconnect(channel->peer->ld, &channel->peer->id); channel_cleanup_commands(channel, why); + assert(channel->owner); channel_set_owner(channel, NULL); delete_channel(channel); } From e366cb17f63787b8c7e90858969b555280afe9fe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:47:12 +1030 Subject: [PATCH 0223/1530] pytest: fix flake in test_reconnect_sender_add1 l1 might split in a commitment_signed before it notices the disconnect, and this test fails: ``` for i in range(0, len(disconnects)): with pytest.raises(RpcError): l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) > l1.rpc.waitsendpay(rhash) E Failed: DID NOT RAISE ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index b50dee021c47..afde7d9f9bc2 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -643,12 +643,14 @@ def test_reconnect_normal(node_factory): @pytest.mark.openchannel('v2') def test_reconnect_sender_add1(node_factory): # Fail after add is OK, will cause payment failure though. + # Make sure it doesn't send commit before it sees disconnect though. disconnects = ['-WIRE_UPDATE_ADD_HTLC', '+WIRE_UPDATE_ADD_HTLC'] # Feerates identical so we don't get gratuitous commit to update them l1 = node_factory.get_node(disconnect=disconnects, may_reconnect=True, + options={'commit-time': 2000}, feerates=(7500, 7500, 7500, 7500)) l2 = node_factory.get_node(may_reconnect=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) From 2ab5603624921dc9e6ea9949454e3c3d5afa5ec6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 11:47:30 +1030 Subject: [PATCH 0224/1530] peer subds: ignore failed writes. In the case where the peer sends an error (and hangs up) immediately after init, connectd *doesn't actually read the error* (even after all the previous fixes so it actually receives the error!). This is because to tried to first write WIRE_CHANNEL_REESTABLISH, and that fails, so it never tries to read. Generally, we should ignore write failures; we'll find out if the socket is closed when we read nothing. Signed-off-by: Rusty Russell --- common/peer_io.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/peer_io.c b/common/peer_io.c index 41c6caa97ca9..3f090a834ea4 100644 --- a/common/peer_io.c +++ b/common/peer_io.c @@ -18,8 +18,9 @@ void peer_write(struct per_peer_state *pps, const void *msg TAKES) { status_peer_io(LOG_IO_OUT, NULL, msg); - if (!wire_sync_write(pps->peer_fd, msg)) - peer_failed_connection_lost(); + /* We ignore write errors; we might still have something to read, + * so we'd rather fail there. */ + wire_sync_write(pps->peer_fd, msg); } u8 *peer_read(const tal_t *ctx, struct per_peer_state *pps) From 109b3e62e94d654d48aa910918f7f235454ee8cd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 15:03:37 +1030 Subject: [PATCH 0225/1530] pytest: disable tests/test_closing.py::test_onchain_all_dust Here's the "Normal Tet Config clang-fuzzing" setup where it fails: ``` CC=clang CONFIGURATOR_CC=clang CWARNFLAGS=-Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition -Werror CDEBUGFLAGS=-std=gnu11 -g -fstack-protector-strong COPTFLAGS= SQLITE3_CFLAGS= SQLITE3_LDLIBS=-lsqlite3 POSTGRES_INCLUDE=-I/usr/include/postgresql POSTGRES_LDLIBS=-L/usr/lib/x86_64-linux-gnu -lpq VALGRIND=0 DEVELOPER=1 EXPERIMENTAL_FEATURES=0 COMPAT=1 ``` Here's the truncated test output: ``` if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) expected_1['B'].append(('wallet', ['anchor'], None, None)) expected_2['B'].append(('wallet', ['anchor'], None, None)) tags = check_utxos_channel(l1, [channel_id], expected_1) > check_utxos_channel(l2, [channel_id], expected_2, tags) tests/test_closing.py:2662: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ tests/utils.py:321: in check_utxos_channel txid = matchup_events(u_set, evs, chans, tag_list) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ u_set = [[{'account_id': 'external', 'blockheight': 104, 'coin_type': 'bcrt', 'credit': '0msat', ...}, {'account_id': 'external', 'blockheight': 110, 'coin_type': 'bcrt', 'credit': '0msat', ...}]] evs = [('external', ['to_them'], None, None), ('external', ['htlc_timeout'], None, None)] chans = ['8ede62cea34c5196467c68175f70b8915f0edda421c5087a99584d4197cfb6c4'] tag_list = {'A': 'c5b6cf97414d58997a08c521a4dd0e5f91b8705f17687c4696514ca3ce62de8e', 'B': 'bb09b25d6653aeeec188961347ff80e90dca6f4a29cc017856f6585adb8cb468'} def matchup_events(u_set, evs, chans, tag_list): assert len(u_set) == len(evs) and len(u_set) > 0 txid = u_set[0][0]['utxo_txid'] for ev in evs: found = False for u in u_set: # We use 'cid' as a placeholder for the channel id, since it's # dyanmic, but we need to sub it in. 'chans' is a list of cids, # which are mapped to `cid` tags' suffixes. eg. 'cid1' is the # first cid in the chans list if ev[0][:3] == 'cid': idx = int(ev[0][3:]) acct = chans[idx - 1] else: acct = ev[0] if u[0]['account_id'] != acct or u[0]['tags'] != ev[1]: continue if ev[2] is None: > assert u[1] is None E AssertionError ``` --- tests/test_closing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index 7a4d2a339560..97f611bb5f3e 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2573,6 +2573,7 @@ def test_onchain_feechange(node_factory, bitcoind, executor): assert only_one(l2.rpc.listinvoices('onchain_timeout')['invoices'])['status'] == 'unpaid' +@pytest.mark.skip("Lisa, please fix this!") @pytest.mark.developer("needs DEVELOPER=1 for dev-set-fees") def test_onchain_all_dust(node_factory, bitcoind, executor): """Onchain handling when we reduce output to all dust""" From 3ccb3da2c551c0bc13e388bbfb6170032efb5c08 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 16:24:11 +1030 Subject: [PATCH 0226/1530] pytest: disable automatic reconnection. We seem to hit a race between manual reconnect (with address hint) and an automatic reconnection attempt which fails: ``` > l4.rpc.connect(l3.info['id'], 'localhost', l3.port) ... E pyln.client.lightning.RpcError: RPC call failed: method: connect, payload: {'id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', 'host': 'localhost', 'port': 41285}, error: {'code': 401, 'message': 'All addresses failed: 127.0.0.1:36678: Connection establishment: Connection refused. '} ``` See how it didn't even try the given address? Signed-off-by: Rusty Russell --- tests/test_pay.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_pay.py b/tests/test_pay.py index 569b56f7bd46..7dc898b452b9 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4348,7 +4348,8 @@ def test_offer(node_factory, bitcoind): def test_fetchinvoice_3hop(node_factory, bitcoind): l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True, opts={'experimental-offers': None, - 'may_reconnect': True}) + 'may_reconnect': True, + 'dev-no-reconnect': None}) offer1 = l4.rpc.call('offer', {'amount': '2msat', 'description': 'simple test'}) assert offer1['created'] is True @@ -4999,7 +5000,8 @@ def test_sendpay_grouping(node_factory, bitcoind): invoices = l3.rpc.listinvoices()['invoices'] assert(len(invoices) == 1) assert(invoices[0]['status'] == 'unpaid') - l3.connect(l2) + # Will reconnect automatically + wait_for(lambda: only_one(l3.rpc.listpeers()['peers'])['connected'] is True) scid = l3.rpc.listpeers()['peers'][0]['channels'][0]['short_channel_id'] wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid)['channels']] == [True, True]) l1.rpc.pay(inv, msatoshi='420000msat') From 4584066a1e27a796b7812215f457783aabdf8305 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 11 Jan 2022 18:05:48 +1030 Subject: [PATCH 0227/1530] connectd: make sure we io_log msgs doing to gossipd. test_gossip_no_empty_announcements relies on this! Signed-off-by: Rusty Russell --- connectd/multiplex.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 8fc361408e5a..cdcb17a0c7bc 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -350,6 +350,9 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) return false; } + /* gossipd doesn't log IO, so we log it here. */ + status_peer_io(LOG_IO_IN, &peer->id, msg); + if (!bitcoin_blkid_eq(&chainparams->genesis_blockhash, &chain_hash)) { send_warning(peer, "gossip_timestamp_filter for bad chain: %s", tal_hex(tmpctx, msg)); From c98734e0a4011cd1e5b6f93f00a175c06200fe10 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 12 Jan 2022 06:05:12 +1030 Subject: [PATCH 0228/1530] connectd: don't ignore requests to connect if we're shutting down. We used to shut down peers atomically, but now we flush the connections there's a delay. If we are asked to connect in that time, we ignore it, as we are already connected, but that's wrong: we need to remember that we were told to connect and reconnect. This should solve a few weird test failures where "connect" would hang indefinitely. Signed-off-by: Rusty Russell --- connectd/connectd.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index e8897c7d3ad6..01be81414106 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1733,10 +1733,17 @@ static void try_connect_peer(struct daemon *daemon, struct wireaddr_internal *addrs; bool use_proxy = daemon->always_use_proxy; struct connecting *connect; - - /* Already done? May happen with timer. */ - if (peer_htable_get(&daemon->peers, id)) - return; + struct peer *existing; + + /* Already existing? */ + existing = peer_htable_get(&daemon->peers, id); + if (existing) { + /* If it's exiting now, we've raced: reconnect after */ + if (existing->to_subd + && existing->to_peer + && !existing->told_to_close) + return; + } /* If we're trying to connect it right now, that's OK. */ if ((connect = find_connecting(daemon, id))) { @@ -1807,7 +1814,8 @@ static void try_connect_peer(struct daemon *daemon, tal_add_destructor(connect, destroy_connecting); /* Now we kick it off by recursively trying connect->addrs[connect->addrnum] */ - try_connect_one_addr(connect); + if (!existing) + try_connect_one_addr(connect); } /* lightningd tells us to connect to a peer by id, with optional addr hint. */ @@ -1828,6 +1836,8 @@ static void connect_to_peer(struct daemon *daemon, const u8 *msg) void peer_conn_closed(struct peer *peer) { + struct connecting *connect = find_connecting(peer->daemon, &peer->id); + /* These should be closed already! */ assert(!peer->to_subd); assert(!peer->to_peer); @@ -1841,6 +1851,10 @@ void peer_conn_closed(struct peer *peer) * a destructor attached to peer (called destroy_peer by * convention). */ tal_free(peer); + + /* If we wanted to connect to it, but found it was exiting, try again */ + if (connect) + try_connect_one_addr(connect); } /* A peer is gone: clean things up. */ From fc44846e10c51605f30374dfcf231faae4c7274e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 12 Jan 2022 10:57:17 +1030 Subject: [PATCH 0229/1530] pytest: disable test_closing_different_fees for elements temporarily. There's actually a bug in our closing tx size estimation; I'll do a separate patch for this, though. Seems this used to be flaky, now we always flush queues, so it's more reliably caught. Signed-off-by: Rusty Russell --- tests/test_closing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index 97f611bb5f3e..f5993a7b28b0 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -249,6 +249,7 @@ def test_closing_torture(node_factory, executor, bitcoind): wait_for(lambda: n.rpc.listpeers()['peers'] == []) +@unittest.skipIf(TEST_NETWORK != 'regtest', 'FIXME: broken under elements') @pytest.mark.slow_test def test_closing_different_fees(node_factory, bitcoind, executor): l1 = node_factory.get_node() From 96ff874bd1900a56dd805e3a27e6a3b9d5f78d5b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 12 Jan 2022 14:19:16 +1030 Subject: [PATCH 0230/1530] pytest: fix race when we mine blocks after pay(). This seems to trigger now, especially on PostgresQL (maybe it's faster to process blocks?). e.g. test_closing_simple() hangs in close(), because the close is unilateral because the HTLC timed out, so it's waiting for a block (other lines removed): ``` lightningd-1: 2022-01-12T00:33:46.258Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: peer_out WIRE_COMMITMENT_SIGNED lightningd-1: 2022-01-12T00:33:46.278Z DEBUG lightningd: close_command: timeout = 172800 2022-01-12T01:03:36.9757201Z lightningd-2: 2022-01-12T00:33:46.384Z DEBUG lightningd: Adding block 104: 73ffa19d27d048613b2731e1682b4efff0dc226807d8cc99d724523c2ea58204 2022-01-12T01:03:36.9759053Z lightningd-2: 2022-01-12T00:33:46.396Z DEBUG lightningd: Adding block 105: 44fd06ed053a0d0594abcfefcfa69089351fc89080826799fb4b278a68fe5c20 2022-01-12T01:03:36.9760865Z lightningd-2: 2022-01-12T00:33:46.406Z DEBUG lightningd: Adding block 106: 0fee2dcbd1376249832642079131275e195bba4fb49cc9968df3a899010bba0f 2022-01-12T01:03:36.9762632Z lightningd-2: 2022-01-12T00:33:46.418Z DEBUG lightningd: Adding block 107: 7f24f2d7d3e83fe3256298bd661e57cdf92b058440738fd4d7e1c8ef4a4ca073 2022-01-12T01:03:36.9773411Z lightningd-2: 2022-01-12T00:33:46.429Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-channeld-chan#1: peer_in WIRE_REVOKE_AND_ACK 2022-01-12T01:03:36.9794707Z lightningd-2: 2022-01-12T00:33:46.437Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-channeld-chan#1: Commits outstanding after recv revoke_and_ack 2022-01-12T01:03:36.9788197Z lightningd-2: 2022-01-12T00:33:46.433Z DEBUG lightningd: Adding block 108: 283b371fb5d1ef42980ea10ab9f5965a179af8e91ddf31c8176e79820e1ec54d 2022-01-12T01:03:36.9799347Z lightningd-2: 2022-01-12T00:33:46.439Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-channeld-chan#1: HTLC 0[REMOTE] => RCVD_REMOVE_REVOCATION 2022-01-12T01:03:36.9808057Z lightningd-2: 2022-01-12T00:33:46.447Z UNUSUAL 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-chan#1: Peer permanent failure in CHANNELD_NORMAL: Fulfilled HTLC 0 RCVD_REMOVE_REVOCATION cltv 109 hit deadline ``` This is because `pay` returns from l1 when it has the preimage, not when the HTLC is fully resolved. Add a helper for this, and call it at the end of the pay test helper. We might need this elsewhere though! Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 3d0723e866f3..a726e58439e0 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1021,6 +1021,9 @@ def pay(self, dst, amt, label=None): result = self.rpc.waitsendpay(rhash) assert(result.get('status') == 'complete') + # Make sure they're all settled, in case we quickly mine blocks! + dst.wait_for_htlcs() + # This helper sends all money to a peer until even 1 msat can't get through. def drain(self, peer): total = 0 From f6847f44f68882d56118bd9a4e7041fb99fd34c1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 20 Jan 2022 15:25:06 +1030 Subject: [PATCH 0231/1530] subds: remove "ignore error" from old LND nodes. This was put in late 2019, and @t-bast says Eclair doesn't ignore their errors and has had no issues. It also conflicts with https://github.com/lightning/bolts/pull/932 which suggests you *should* fail when you receive an error. Signed-off-by: Rusty Russell --- channeld/channeld.c | 14 ++------------ closingd/closingd.c | 2 +- common/read_peer_msg.c | 4 +--- common/read_peer_msg.h | 2 -- openingd/dualopend.c | 6 ++---- openingd/openingd.c | 2 +- 6 files changed, 7 insertions(+), 23 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 980bb1033648..a8d0aaaa0b76 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2231,17 +2231,10 @@ static void peer_in(struct peer *peer, const u8 *msg) { enum peer_wire type = fromwire_peektype(msg); - /* Only count soft errors if the channel has locked-in already; - * otherwise we can't cancel a channel before it has opened. - */ - bool soft_error = peer->funding_locked[REMOTE] || peer->funding_locked[LOCAL]; - if (channeld_handle_custommsg(msg)) return; - /* Since LND seems to send errors which aren't actually fatal events, - * we treat errors here as soft. */ - if (handle_peer_gossip_or_error(peer->pps, &peer->channel_id, soft_error, msg)) + if (handle_peer_gossip_or_error(peer->pps, &peer->channel_id, msg)) return; /* Must get funding_locked before almost anything. */ @@ -2905,8 +2898,6 @@ static void peer_reconnect(struct peer *peer, peer_write(peer->pps, take(msg)); peer_billboard(false, "Sent reestablish, waiting for theirs"); - 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! */ @@ -2922,8 +2913,7 @@ static void peer_reconnect(struct peer *peer, clean_tmpctx(); msg = peer_read(tmpctx, peer->pps); } while (channeld_handle_custommsg(msg) || - handle_peer_gossip_or_error(peer->pps, &peer->channel_id, soft_error, - msg) || + handle_peer_gossip_or_error(peer->pps, &peer->channel_id, msg) || capture_premature_msg(&premature_msgs, msg)); got_reestablish: diff --git a/closingd/closingd.c b/closingd/closingd.c index 9fb74e4ba899..19665dd8e7bf 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -136,7 +136,7 @@ static u8 *closing_read_peer_msg(const tal_t *ctx, wire_sync_write(REQ_FD, take(towire_custommsg_in(NULL, msg))); continue; } - if (!handle_peer_gossip_or_error(pps, channel_id, false, msg)) + if (!handle_peer_gossip_or_error(pps, channel_id, msg)) return msg; } } diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index fe841108c6c4..c2f87344cf6f 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -112,7 +112,6 @@ void handle_gossip_msg(struct per_peer_state *pps, const u8 *msg TAKES) bool handle_peer_gossip_or_error(struct per_peer_state *pps, const struct channel_id *channel_id, - bool soft_error, const u8 *msg TAKES) { char *err; @@ -150,8 +149,7 @@ bool handle_peer_gossip_or_error(struct per_peer_state *pps, goto handled; /* We hang up when a warning is received. */ - peer_failed_received_errmsg(pps, err, channel_id, - soft_error || warning); + peer_failed_received_errmsg(pps, err, channel_id, warning); goto handled; } diff --git a/common/read_peer_msg.h b/common/read_peer_msg.h index fbdff761a759..a3fecb83f1ac 100644 --- a/common/read_peer_msg.h +++ b/common/read_peer_msg.h @@ -58,7 +58,6 @@ bool is_wrong_channel(const u8 *msg, const struct channel_id *expected, * handle_peer_gossip_or_error - simple handler for all the above cases. * @pps: per-peer state. * @channel_id: the channel id of the current channel. - * @soft_error: tell lightningd that incoming error is non-fatal. * @msg: the peer message (only taken if returns true). * * This returns true if it handled the packet: a gossip packet (forwarded @@ -67,7 +66,6 @@ bool is_wrong_channel(const u8 *msg, const struct channel_id *expected, */ bool handle_peer_gossip_or_error(struct per_peer_state *pps, const struct channel_id *channel_id, - bool soft_error, const u8 *msg TAKES); /** diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 997525a853e2..28c9a407dfa8 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3540,8 +3540,6 @@ static void do_reconnect_dance(struct state *state) peer_write(state->pps, take(msg)); peer_billboard(false, "Sent reestablish, waiting for theirs"); - bool soft_error = state->funding_locked[REMOTE] - || state->funding_locked[LOCAL]; /* Read until they say something interesting (don't forward * gossip *to* them yet: we might try sending channel_update @@ -3552,7 +3550,7 @@ static void do_reconnect_dance(struct state *state) } while (dualopend_handle_custommsg(msg) || handle_peer_gossip_or_error(state->pps, &state->channel_id, - soft_error, msg)); + msg)); if (!fromwire_channel_reestablish (msg, &cid, @@ -3768,7 +3766,7 @@ static u8 *handle_peer_in(struct state *state) /* Handles standard cases, and legal unknown ones. */ if (handle_peer_gossip_or_error(state->pps, - &state->channel_id, false, msg)) + &state->channel_id, msg)) return NULL; peer_write(state->pps, diff --git a/openingd/openingd.c b/openingd/openingd.c index e94c31f74437..42257adfbed1 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1272,7 +1272,7 @@ static u8 *handle_peer_in(struct state *state) /* Handles standard cases, and legal unknown ones. */ if (handle_peer_gossip_or_error(state->pps, - &state->channel_id, false, msg)) + &state->channel_id, msg)) return NULL; extracted = extract_channel_id(msg, &channel_id); From 3281d72169bbfeac53c5807629eb58226a5d1ec9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 20 Jan 2022 15:26:06 +1030 Subject: [PATCH 0232/1530] pytest: clean up test_channel_state_changed_unilateral OK, now this test makes more sense! Now we don't ignore errors, we *will* drop to chain if we reconnect after one side has dropped to chain. Signed-off-by: Rusty Russell --- tests/test_plugin.py | 73 +++++++++++++------------------------------- 1 file changed, 21 insertions(+), 52 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 6f00b8d99bec..2889b820030f 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -848,12 +848,9 @@ def test_channel_state_changed_unilateral(node_factory, bitcoind): The misc_notifications.py plugin logs `channel_state_changed` events. """ - # FIXME: We can get warnings from unilteral changes, since we treat - # such errors a soft because LND. opts = {"plugin": os.path.join(os.getcwd(), "tests/plugins/misc_notifications.py"), - "allow_warning": True} - if EXPERIMENTAL_DUAL_FUND: - opts['may_reconnect'] = True + "allow_warning": True, + 'may_reconnect': True} l1, l2 = node_factory.line_graph(2, opts=opts) @@ -906,7 +903,6 @@ def wait_for_event(node): assert(event2['message'] == "Forcibly closed by `close` command timeout") # restart l1 now, it will reconnect and l2 will send it an error. - # FIXME: it should re-xmit shutdown, but it doesn't until it's mined :( l1.restart() wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 1) # check 'closer' on l2 while the peer is not yet forgotten @@ -915,6 +911,16 @@ def wait_for_event(node): l1.daemon.wait_for_log(r'Peer has reconnected, state') l2.daemon.wait_for_log(r'Peer has reconnected, state') + # l1 will receive error, and go into AWAITING_UNILATERAL + # FIXME: l2 should re-xmit shutdown, but it doesn't until it's mined :( + event1 = wait_for_event(l1) + # Doesn't have closer, since it blames the "protocol"? + assert 'closer' not in l1.rpc.listpeers()['peers'][0]['channels'][0] + assert(event1['old_state'] == "CHANNELD_NORMAL") + assert(event1['new_state'] == "AWAITING_UNILATERAL") + assert(event1['cause'] == "protocol") + assert(event1['message'] == "channeld: received ERROR error channel {}: Forcibly closed by `close` command timeout".format(cid)) + # settle the channel closure bitcoind.generate_block(100) @@ -933,53 +939,16 @@ def wait_for_event(node): event1 = wait_for_event(l1) assert(l1.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'remote') - # If l1 saw onchain first, it goes: - # AWAITING_UNILATERAL - # FUNDING_SPEND_SEEN - # otherwise, it gets a shutdown from remote, and goes: - # CHANNELD_SHUTTING_DOWN - # AWAITING_UNILATERAL - # FUNDING_SPEND_SEEN - if event1['new_state'] == "CHANNELD_SHUTTING_DOWN": - # In this case, cause is always "remote". - assert(event1['old_state'] == "CHANNELD_NORMAL") - assert(event1['cause'] == "remote") - assert(event1['message'] == "Peer closes channel") - - event1 = wait_for_event(l1) - assert(event1['old_state'] == "CHANNELD_SHUTTING_DOWN") - assert(event1['new_state'] == "AWAITING_UNILATERAL") - assert(event1['message'] == "Funding transaction spent") - - event1 = wait_for_event(l1) - assert(event1['old_state'] == "AWAITING_UNILATERAL") - assert(event1['new_state'] == "FUNDING_SPEND_SEEN") - assert(event1['cause'] == "remote") - assert(event1['message'] == "Onchain funding spend") - - event1 = wait_for_event(l1) - assert(event1['old_state'] == "FUNDING_SPEND_SEEN") - assert(event1['new_state'] == "ONCHAIN") - assert(event1['cause'] == "remote") - assert(event1['message'] == "Onchain init reply") - else: - # In this case, cause is always "onchain". - assert(event1['old_state'] == "CHANNELD_NORMAL") - assert(event1['new_state'] == "AWAITING_UNILATERAL") - assert(event1['cause'] == "onchain") - assert(event1['message'] == "Funding transaction spent") - - event1 = wait_for_event(l1) - assert(event1['old_state'] == "AWAITING_UNILATERAL") - assert(event1['new_state'] == "FUNDING_SPEND_SEEN") - assert(event1['cause'] == "onchain") - assert(event1['message'] == "Onchain funding spend") + assert(event1['old_state'] == "AWAITING_UNILATERAL") + assert(event1['new_state'] == "FUNDING_SPEND_SEEN") + assert(event1['cause'] == "onchain") + assert(event1['message'] == "Onchain funding spend") - event1 = wait_for_event(l1) - assert(event1['old_state'] == "FUNDING_SPEND_SEEN") - assert(event1['new_state'] == "ONCHAIN") - assert(event1['cause'] == "onchain") - assert(event1['message'] == "Onchain init reply") + event1 = wait_for_event(l1) + assert(event1['old_state'] == "FUNDING_SPEND_SEEN") + assert(event1['new_state'] == "ONCHAIN") + assert(event1['cause'] == "onchain") + assert(event1['message'] == "Onchain init reply") @pytest.mark.openchannel('v1') From be299c0d593fc1582782eb0a919073d20128d7b2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 20 Jan 2022 15:27:06 +1030 Subject: [PATCH 0233/1530] common/socket_close: remove now only connectd talks to peer. connectd does this internally now using ccan/io, with appropriate credit for ZmnSCPxj who wrote this code in the first place. Signed-off-by: Rusty Russell --- closingd/Makefile | 1 - closingd/closingd.c | 6 +----- common/Makefile | 1 - common/socket_close.c | 48 ------------------------------------------- common/socket_close.h | 23 --------------------- 5 files changed, 1 insertion(+), 78 deletions(-) delete mode 100644 common/socket_close.c delete mode 100644 common/socket_close.h diff --git a/closingd/Makefile b/closingd/Makefile index 53c732399c07..5c3373471137 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -50,7 +50,6 @@ CLOSINGD_COMMON_OBJS := \ common/status_wiregen.o \ common/read_peer_msg.o \ common/setup.o \ - common/socket_close.o \ common/status.o \ common/status_wire.o \ common/subdaemon.o \ diff --git a/closingd/closingd.c b/closingd/closingd.c index 19665dd8e7bf..2d228a462434 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -1097,10 +1096,7 @@ int main(int argc, char *argv[]) #endif /* We're done! */ - /* Properly close the channel first. */ - if (!socket_close(pps->peer_fd)) - status_unusual("Closing and draining peerfd gave error: %s", - strerror(errno)); + /* Sending the below will kill us! */ wire_sync_write(REQ_FD, take(towire_closingd_complete(NULL))); tal_free(ctx); diff --git a/common/Makefile b/common/Makefile index 9c8dbfe39893..a203a734a5f7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -76,7 +76,6 @@ COMMON_SRC_NOGEN := \ common/route.c \ common/setup.c \ common/shutdown_scriptpubkey.c \ - common/socket_close.c \ common/sphinx.c \ common/status.c \ common/status_levels.c \ diff --git a/common/socket_close.c b/common/socket_close.c deleted file mode 100644 index 5abaf80d36da..000000000000 --- a/common/socket_close.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include - -/* makes read() return EINTR after 5 seconds */ -static void break_read(int signum) -{ -} - -bool socket_close(int fd) -{ - char unused[64]; - struct sigaction act, old_act; - int sys_res, saved_errno; - - /* We shutdown. Usually they then shutdown too, and read() gives 0 */ - sys_res = shutdown(fd, SHUT_WR); - if (sys_res < 0) { - close_noerr(fd); - return false; - } - - /* Let's not get too enthusiastic about waiting. */ - memset(&act, 0, sizeof(act)); - act.sa_handler = break_read; - sigaction(SIGALRM, &act, &old_act); - - alarm(5); - - while ((sys_res = read(fd, unused, sizeof(unused))) > 0); - saved_errno = errno; - - alarm(0); - sigaction(SIGALRM, &old_act, NULL); - - if (sys_res < 0) { - close(fd); - errno = saved_errno; - return false; - } - - return close(fd) == 0; -} diff --git a/common/socket_close.h b/common/socket_close.h deleted file mode 100644 index 8fc8030f9511..000000000000 --- a/common/socket_close.h +++ /dev/null @@ -1,23 +0,0 @@ -/* common/socket_close - Properly close a socket, - * ensuring that any data we write just before - * the close has been transmitted to the other - * side, and ignoring any data the other side - * has sent at the time the close was started. - * - * Reference: - * - * http://ia800504.us.archive.org/3/items/TheUltimateSo_lingerPageOrWhyIsMyTcpNotReliable/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable.html - */ -#ifndef LIGHTNING_COMMON_SOCKET_CLOSE_H -#define LIGHTNING_COMMON_SOCKET_CLOSE_H -#include "config.h" -#include - -/* Return false if something failed, true if - * nothing failed. - * If something failed, error is stored in - * `errno. - */ -bool socket_close(int fd); - -#endif /* LIGHTNING_COMMON_SOCKET_CLOSE_H */ From 2b5d25c8515b22a299da2756fb8332063fec1142 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 20 Jan 2022 15:27:08 +1030 Subject: [PATCH 0234/1530] common: remove stderr debug in is_valid_witnessprog. Signed-off-by: Rusty Russell --- common/shutdown_scriptpubkey.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/common/shutdown_scriptpubkey.c b/common/shutdown_scriptpubkey.c index 866931ee506f..ad79ff20f502 100644 --- a/common/shutdown_scriptpubkey.c +++ b/common/shutdown_scriptpubkey.c @@ -2,8 +2,6 @@ #include #include -#include - /* BOLT #2: * 5. if (and only if) `option_shutdown_anysegwit` is negotiated: * * `OP_1` through `OP_16` inclusive, followed by a single @@ -36,20 +34,15 @@ static bool is_valid_witnessprog(const u8 *scriptpubkey) case OP_16: break; default: - fprintf(stderr, "op = %u (invalid)\n", scriptpubkey[0]); return false; } pushlen = scriptpubkey[1]; /* Must be all of the rest of scriptpubkey */ if (2 + pushlen != tal_bytelen(scriptpubkey)) { - fprintf(stderr, "2 + %zu != %zu\n", pushlen, tal_bytelen(scriptpubkey)); return false; } - if (!(pushlen >= 2 && pushlen <= 40)) - fprintf(stderr, "pushlen == %zu\n", pushlen); - return pushlen >= 2 && pushlen <= 40; } From 409b26916cdc5ad3323933dd5c3644c268234d91 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 20 Jan 2022 15:27:09 +1030 Subject: [PATCH 0235/1530] CI: actually check db statements. check-dbstmts was just running the normal pytest, AFAICT: ``` export TEST_CHECK_DBSTMTS=0 + TEST_CHECK_DBSTMTS=0 ``` Signed-off-by: Rusty Russell --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f71e5c72ce85..225c9cd9a7fb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -221,6 +221,7 @@ jobs: PYTEST_PAR: ${{ matrix.PYTEST_PAR }} PYTEST_OPTS: ${{ matrix.PYTEST_OPTS }} NETWORK: ${{ matrix.NETWORK }} + TEST_CHECK_DBSTMTS: ${{ matrix.TEST_CHECK_DBSTMTS }} TEST_CMD: ${{ matrix.TEST_CMD }} TEST_GROUP_COUNT: ${{ matrix.TEST_GROUP_COUNT }} TEST_GROUP: ${{ matrix.TEST_GROUP }} From beed4fcccc1bd1bb388d6e188d1df9db785ae11e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 20 Jan 2022 15:27:09 +1030 Subject: [PATCH 0236/1530] lightningd: fix backwards test in sigchld. This would never trigger, since test was backward. Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 1824838c4f33..4727e679a70d 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -713,7 +713,7 @@ static void on_sigchild(int _ UNUSED) * __attribute__((warn_unused_result)) means we have to * "catch" the return value. */ if (write(sigchld_wfd, "", 1) != 1) { - if (errno != EAGAIN && errno == EWOULDBLOCK) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { /* Should not call this in a signal handler, but we're * already messed up! */ fatal("on_sigchild: write errno %s", strerror(errno)); From 4a4f85dd3f8dad6a16b76654ee1d84d211a2c533 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 22 Jan 2022 15:19:22 +1030 Subject: [PATCH 0237/1530] subd: fix waitpid properly. lightningd would race with the subd destructor to do the waitpid(), resulting in UNUSUAL log messages, but also us missing if a plugin was killed via a signal. We can also get rid of the gratuitous waitpid() in test_subdaemons. Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 13 +++++++---- lightningd/lightningd.h | 3 +++ lightningd/subd.c | 51 ++++++++++++++++++++++++++++------------- lightningd/subd.h | 9 ++++++++ 4 files changed, 55 insertions(+), 21 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 4727e679a70d..a73017835c2f 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -151,6 +151,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * This method of manually declaring the list hooks avoids dynamic * allocations to put things into a list. */ list_head_init(&ld->peers); + list_head_init(&ld->subds); /*~ These are hash tables of incoming and outgoing HTLCs (contracts), * defined as `struct htlc_in` and `struct htlc_out` in htlc_end.h. @@ -409,10 +410,8 @@ void test_subdaemons(const struct lightningd *ld) || verstring[strlen(version())] != '\n') errx(1, "%s: bad version '%s'", subdaemons[i], verstring); - - /*~ finally reap the child process, freeing all OS - * resources that go with it */ - waitpid(pid, NULL, 0); + /*~ The child will be reaped by sigchld_rfd_in, so we don't + * need to waitpid() here. */ } } @@ -784,9 +783,13 @@ static struct io_plan *sigchld_rfd_in(struct io_conn *conn, /* We don't actually care what we read, so we stuff things here. */ static u8 ignorebuf; static size_t len; + pid_t childpid; + int wstatus; /* Reap the plugins, since we otherwise ignore them. */ - while (waitpid(-1, NULL, WNOHANG) != 0); + while ((childpid = waitpid(-1, &wstatus, WNOHANG)) != 0) { + maybe_subd_child(ld, childpid, wstatus); + } return io_read_partial(conn, &ignorebuf, 1, &len, sigchld_rfd_in, ld); } diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 8f870dbf7dfd..25a5db475a88 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -202,6 +202,9 @@ struct lightningd { /* RPC response to send once we've shut down. */ const char *stop_response; + /* All the subdaemons. */ + struct list_head subds; + /* Used these feerates instead of whatever bcli returns (up to * FEERATE_PENALTY). */ u32 *force_feerates; diff --git a/lightningd/subd.c b/lightningd/subd.c index 36c836308145..81614f8d1b78 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -21,6 +21,16 @@ #include #include +void maybe_subd_child(struct lightningd *ld, int childpid, int wstatus) +{ + struct subd *sd; + + list_for_each(&ld->subds, sd, list) { + if (sd->pid == childpid) + sd->wstatus = tal_dup(sd, int, &wstatus); + } +} + /* Carefully move fd *@from to @to: on success *from set to to */ static bool move_fd(int *from, int to) { @@ -580,24 +590,30 @@ static void destroy_subd(struct subd *sd) bool fail_if_subd_fails; fail_if_subd_fails = IFDEV(sd->ld->dev_subdaemon_fail, false); + list_del_from(&sd->ld->subds, &sd->list); - switch (waitpid(sd->pid, &status, WNOHANG)) { - case 0: - /* If it's an essential daemon, don't kill: we want the - * exit status */ - if (!sd->must_not_exit) { - log_debug(sd->log, - "Status closed, but not exited. Killing"); - kill(sd->pid, SIGKILL); + /* lightningd may have already done waitpid() */ + if (sd->wstatus != NULL) { + status = *sd->wstatus; + } else { + switch (waitpid(sd->pid, &status, WNOHANG)) { + case 0: + /* If it's an essential daemon, don't kill: we want the + * exit status */ + if (!sd->must_not_exit) { + log_debug(sd->log, + "Status closed, but not exited. Killing"); + kill(sd->pid, SIGKILL); + } + waitpid(sd->pid, &status, 0); + fail_if_subd_fails = false; + break; + case -1: + log_broken(sd->log, "Status closed, but waitpid %i says %s", + sd->pid, strerror(errno)); + status = -1; + break; } - waitpid(sd->pid, &status, 0); - fail_if_subd_fails = false; - break; - case -1: - log_unusual(sd->log, "Status closed, but waitpid %i says %s", - sd->pid, strerror(errno)); - status = -1; - break; } if (fail_if_subd_fails && WIFSIGNALED(status)) { @@ -742,6 +758,8 @@ static struct subd *new_subd(struct lightningd *ld, sd->billboardcb = billboardcb; sd->fds_in = NULL; sd->outq = msg_queue_new(sd, true); + sd->wstatus = NULL; + list_add(&ld->subds, &sd->list); tal_add_destructor(sd, destroy_subd); list_head_init(&sd->reqs); sd->channel = channel; @@ -868,6 +886,7 @@ struct subd *subd_shutdown(struct subd *sd, unsigned int seconds) if (waitpid(sd->pid, NULL, 0) > 0) { alarm(0); sigaction(SIGALRM, &old, NULL); + list_del_from(&sd->ld->subds, &sd->list); return tal_free(sd); } diff --git a/lightningd/subd.h b/lightningd/subd.h index 4be12e248181..564b2f35ff86 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -20,6 +20,9 @@ struct peer_fd; /* One of our subds. */ struct subd { + /* Inside ld->subds */ + struct list_node list; + /* Name, like John, or "lightning_hsmd" */ const char *name; /* The Big Cheese. */ @@ -74,6 +77,9 @@ struct subd { /* Callbacks for replies. */ struct list_head reqs; + + /* Did lightningd already wait for this pid? */ + int *wstatus; }; /** @@ -219,6 +225,9 @@ struct subd *subd_shutdown(struct subd *subd, unsigned int seconds); /* Ugly helper to get full pathname of the current binary. */ const char *find_my_abspath(const tal_t *ctx, const char *argv0); +/* lightningd captures SIGCHLD and waits, but so does subd. */ +void maybe_subd_child(struct lightningd *ld, int childpid, int wstatus); + #if DEVELOPER char *opt_subd_dev_disconnect(const char *optarg, struct lightningd *ld); From f6191c8ef9eced8871db2e3657b6e6c01509ebf9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 22 Jan 2022 15:19:32 +1030 Subject: [PATCH 0238/1530] gossipd: fix unknown channel_update recovery logic. An "err" is only returned if the channel_update is malformed: more common is that it's fine, but we don't know the scid. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 0b76ff8ff230..ac5580856c7c 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -261,11 +261,12 @@ static u8 *handle_channel_update_msg(struct peer *peer, const u8 *msg) unknown_scid.u64 = 0; err = handle_channel_update(peer->daemon->rstate, msg, peer, &unknown_scid, false); - if (err) { - if (unknown_scid.u64 != 0) - query_unknown_channel(peer->daemon, peer, &unknown_scid); + if (err) return err; - } + + /* If it's an unknown channel, ask someone about it */ + if (unknown_scid.u64 != 0) + query_unknown_channel(peer->daemon, peer, &unknown_scid); /*~ As a nasty compromise in the spec, we only forward `channel_announce` * once we have a `channel_update`; the channel isn't *usable* for From 02aa39a9d73be60d8420f98d4e5b365a3cad42b7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 22 Jan 2022 15:19:33 +1030 Subject: [PATCH 0239/1530] gossipd: use tal_dup_talarr helper. Signed-off-by: Rusty Russell --- gossipd/routing.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index 58b1d5c789b8..c52d68bc3e9f 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1450,11 +1450,10 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 fee_proportional_millionths; struct bitcoin_blkid chain_hash; u8 direction; - size_t len = tal_count(update); struct pending_cannouncement *pending; u8 *err; - serialized = tal_dup_arr(tmpctx, u8, update, len, 0); + serialized = tal_dup_talarr(tmpctx, u8, update); if (!fromwire_channel_update(serialized, &signature, &chain_hash, &short_channel_id, ×tamp, &message_flags, From 16dd091d8b56bd641e90a1b420ec7fa85c2a66b2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 22 Jan 2022 15:20:22 +1030 Subject: [PATCH 0240/1530] pytest: fix flake in test_upgrade_statickey_fail ``` l1.rpc.disconnect(l2.info['id'], force=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) > l1.daemon.wait_for_log('option_static_remotekey enabled at 2/2') tests/test_connection.py:3653: ``` If l2's channeld gets killed (due to reconnect) before it tells lightningd it got the revoke_and_ack it will need a retransmission *again*. This makes the test more robust, and does more checks too. Signed-off-by: Rusty Russell --- tests/test_connection.py | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index afde7d9f9bc2..3280ab27c693 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3629,7 +3629,9 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): {'may_reconnect': True, 'dev-no-reconnect': None, 'disconnect': l2_disconnects, - 'dev-disable-commit-after': 1}]) + 'plugin': os.path.join(os.getcwd(), 'tests/plugins/hold_htlcs.py'), + 'hold-time': 10000, + 'hold-result': 'fail'}]) # This HTLC will fail l1.rpc.sendpay([{'msatoshi': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}], '00' * 32, payment_secret='00' * 32) @@ -3641,17 +3643,38 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): assert not l1.daemon.is_in_log('option_static_remotekey enabled') assert not l2.daemon.is_in_log('option_static_remotekey enabled') l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + line1 = l1.daemon.wait_for_log('No upgrade') + line2 = l2.daemon.wait_for_log('No upgrade') # On the last reconnect, it retransmitted revoke_and_ack. - l1.daemon.wait_for_log('No upgrade: we retransmitted') - l2.daemon.wait_for_log('No upgrade: pending changes') + assert re.search('No upgrade: we retransmitted', line1) + assert re.search('No upgrade: pending changes', line2) - # Now when we reconnect, despite having an HTLC, we're quiescent. - l1.rpc.disconnect(l2.info['id'], force=True) - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # Make sure we already skip the first of these. + l1.daemon.wait_for_log('billboard perm: Reconnected, and reestablished.') + assert 'option_static_remotekey' not in only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['features'] + assert 'option_static_remotekey' not in only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['features'] + + sleeptime = 1 + while True: + # Now when we reconnect, despite having an HTLC, we're quiescent. + l1.rpc.disconnect(l2.info['id'], force=True) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + oldstart = l1.daemon.logsearch_start + l1.daemon.wait_for_log('billboard perm: Reconnected, and reestablished.') + if not l1.daemon.is_in_log('No upgrade:', start=oldstart): + break + + # Give it some processing time before reconnect... + time.sleep(sleeptime) + sleeptime += 1 - l1.daemon.wait_for_log('option_static_remotekey enabled at 2/2') - l2.daemon.wait_for_log('option_static_remotekey enabled at 2/2') + l1.daemon.logsearch_start = oldstart + assert l1.daemon.wait_for_log('option_static_remotekey enabled at 2/2') + assert l2.daemon.wait_for_log('option_static_remotekey enabled at 2/2') + assert 'option_static_remotekey' in only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['features'] + assert 'option_static_remotekey' in only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['features'] @unittest.skipIf(not EXPERIMENTAL_FEATURES, "quiescence is experimental") From cf2c4c5dde005f8f7499d45e0df98de859e548df Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 7 Jan 2022 13:58:40 -0800 Subject: [PATCH 0241/1530] make: add ctags target For us vim users :) Signed-off-by: William Casarin --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 5a768d1ed00c..fe17cd563443 100644 --- a/Makefile +++ b/Makefile @@ -547,6 +547,9 @@ 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 +tags: + $(RM) tags; find * -name test -type d -prune -o -name '*.[ch]' -print -o -name '*.py' -print | xargs ctags --append + 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 ff4ae8b5f454afb8c0113198af97322ee485e862 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 24 Jan 2022 12:58:39 -0600 Subject: [PATCH 0242/1530] coin_moves: add an 'originating_account' field If a coin move concerns an external account, it's really useful to know which 'internal' account initiated the transfer. We're about to add a notification for withdrawals, so we can use this to track wallet pushes to outside addresses Changelog-Added: JSONRPC: `coin_movement` to 'external' accounts now include an 'originating_account' field --- common/coin_mvt.c | 24 ++++++++++++++++++++++++ common/coin_mvt.h | 10 ++++++++++ doc/PLUGINS.md | 4 ++++ lightningd/notification.c | 3 +++ lightningd/onchain_control.c | 3 +++ 5 files changed, 44 insertions(+) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index f523d1b26feb..f77561e37fd3 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -102,6 +102,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, mvt->tx_txid = tx_txid; mvt->outpoint = outpoint; + mvt->originating_acct = NULL; /* for htlc's that are filled onchain, we also have a * preimage, NULL otherwise */ @@ -267,6 +268,11 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, AMOUNT_MSAT(0), true, amount); } +bool chain_mvt_is_external(const struct chain_coin_mvt *mvt) +{ + return streq(mvt->account_name, EXTERNAL); +} + struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, @@ -327,6 +333,11 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, struct coin_mvt *mvt = tal(ctx, struct coin_mvt); mvt->account_id = tal_strdup(mvt, chain_mvt->account_name); + if (chain_mvt->originating_acct) + mvt->originating_acct = + tal_strdup(mvt, chain_mvt->originating_acct); + else + mvt->originating_acct = NULL; mvt->hrp_name = tal_strdup(mvt, hrp_name); mvt->type = CHAIN_MVT; @@ -359,6 +370,8 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->account_id = type_to_string(mvt, struct channel_id, &chan_mvt->chan_id); + /* channel moves don't have external events! */ + mvt->originating_acct = NULL; mvt->hrp_name = tal_strdup(mvt, hrp_name); mvt->type = CHANNEL_MVT; mvt->id.payment_hash = chan_mvt->payment_hash; @@ -388,6 +401,12 @@ void towire_chain_coin_mvt(u8 **pptr, const struct chain_coin_mvt *mvt) } else towire_bool(pptr, false); + if (mvt->originating_acct) { + towire_bool(pptr, true); + towire_wirestring(pptr, mvt->originating_acct); + } else + towire_bool(pptr, false); + towire_bitcoin_outpoint(pptr, mvt->outpoint); if (mvt->tx_txid) { @@ -419,6 +438,11 @@ void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_m } else mvt->account_name = NULL; + if (fromwire_bool(cursor, max)) { + mvt->originating_acct = fromwire_wirestring(mvt, cursor, max); + } else + mvt->originating_acct = NULL; + /* Read into non-const version */ struct bitcoin_outpoint *outpoint = tal(mvt, struct bitcoin_outpoint); diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 6905c3257461..0f3b974755c2 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -83,6 +83,10 @@ struct chain_coin_mvt { /* total value of output (useful for tracking external outs) */ struct amount_sat output_val; + + /* When we pay to external accounts, it's useful + * to track which internal account it originated from */ + const char *originating_acct; }; /* differs depending on type!? */ @@ -97,6 +101,9 @@ struct coin_mvt { /* name of 'account': wallet, external, */ const char *account_id; + /* if account_id is external, the account this 'impacted' */ + const char *originating_acct; + /* Chain name: BIP 173, except signet lightning-style: tbs not tb */ const char *hrp_name; @@ -249,6 +256,9 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, const struct node_id *node_id) NON_NULL_ARGS(2, 3, 5); +/* Is this an xternal account? */ +bool chain_mvt_is_external(const struct chain_coin_mvt *mvt); + const char *mvt_type_str(enum mvt_type type); const char *mvt_tag_str(enum mvt_tag tag); diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index f214080f6e7d..c6e1cd9d2750 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -701,6 +701,7 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "node_id":"03a7103a2322b811f7369cbb27fb213d30bbc0b012082fed3cad7e4498da2dc56b", "type":"chain_mvt", "account_id":"wallet", + "originating_account": "wallet", // (`chain_mvt` only, optional) "txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only, optional) "utxo_txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only) "vout":1, // (`chain_mvt` only) @@ -731,6 +732,9 @@ notification adheres to. `account_id` is the name of this account. The node's wallet is named 'wallet', all channel funds' account are the channel id. +`originating_account` is the account that this movement originated from. +*Only* tagged on external events (deposits/withdrawals to an external party). + `txid` is the transaction id of the bitcoin transaction that triggered this ledger event. `utxo_txid` and `vout` identify the bitcoin output which triggered this notification. (`chain_mvt` only). Notifications tagged diff --git a/lightningd/notification.c b/lightningd/notification.c index a93a4d6e5202..13f9154b9779 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -470,6 +470,9 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_add_node_id(stream, "node_id", mvt->node_id); json_add_string(stream, "type", mvt_type_str(mvt->type)); json_add_string(stream, "account_id", mvt->account_id); + if (mvt->originating_acct) + json_add_string(stream, "originating_account", + mvt->originating_acct); json_mvt_id(stream, mvt->type, &mvt->id); json_add_amount_msat_only(stream, "credit", mvt->credit); json_add_amount_msat_only(stream, "debit", mvt->debit); diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index d4f81de27165..b1122a79ac18 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -265,6 +265,9 @@ static void handle_onchain_log_coin_move(struct channel *channel, const u8 *msg) if (!mvt->account_name) mvt->account_name = type_to_string(mvt, struct channel_id, &channel->cid); + else if (chain_mvt_is_external(mvt)) + mvt->originating_acct = type_to_string(mvt, struct channel_id, + &channel->cid); notify_chain_mvt(channel->peer->ld, mvt); tal_free(mvt); } From 75df20ace9bf973e83c49a9588e4a1b6834927b3 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 24 Jan 2022 15:36:46 -0600 Subject: [PATCH 0243/1530] doc nit: wrap rest of tags in backticks Requested in a comment on a previous PR --- doc/PLUGINS.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index c6e1cd9d2750..32ce9229a12c 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -771,22 +771,22 @@ both the debit/credit contain fees. Technically routed debits are the - `invoice`: funds paid to or recieved from an invoice. - `routed`: funds routed through this node. - `pushed`: funds pushed to peer. - - channel_open : channel is opened, initial channel balance - - channel_close: channel is closed, final channel balance - - delayed_to_us : on-chain output to us, spent back into our wallet - - htlc_timeout : on-chain htlc timeout output - - htlc_fulfill : on-chian htlc fulfill output - - htlc_tx : on-chain htlc tx has happened - - to_wallet : output being spent into our wallet - - ignored : output is being ignored - - anchor : an anchor output - - to_them : output intended to peer's wallet - - penalized : output we've 'lost' due to a penalty (failed cheat attempt) - - stolen : output we've 'lost' due to peer's cheat - - to_miner : output we've burned to miner (OP_RETURN) - - opener : tags channel_open, we are the channel opener - - lease_fee: amount paid as lease fee - - leased: tags channel_open, channel contains leased funds + - `channel_open` : channel is opened, initial channel balance + - `channel_close`: channel is closed, final channel balance + - `delayed_to_us`: on-chain output to us, spent back into our wallet + - `htlc_timeout`: on-chain htlc timeout output + - `htlc_fulfill`: on-chian htlc fulfill output + - `htlc_tx`: on-chain htlc tx has happened + - `to_wallet`: output being spent into our wallet + - `ignored`: output is being ignored + - `anchor`: an anchor output + - `to_them`: output intended to peer's wallet + - `penalized`: output we've 'lost' due to a penalty (failed cheat attempt) + - `stolen`: output we've 'lost' due to peer's cheat + - `to_miner`: output we've burned to miner (OP_RETURN) + - `opener`: tags channel_open, we are the channel opener + - `lease_fee`: amount paid as lease fee + - `leased`: tags channel_open, channel contains leased funds `blockheight` is the block the txid is included in. From 7f511b9ba95bd36a55b40abfdee741ea95fe0753 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 25 Jan 2022 14:03:23 -0600 Subject: [PATCH 0244/1530] coin moves: external deposits should be *deposits* Report how much the deposit was for. --- common/coin_mvt.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index f77561e37fd3..e6e698a3ac92 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -261,11 +261,9 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, struct amount_sat amount, enum mvt_tag tag) { - return new_chain_coin_mvt(ctx, EXTERNAL, NULL, - outpoint, NULL, - blockheight, - take(new_tag_arr(NULL, tag)), - AMOUNT_MSAT(0), true, amount); + return new_chain_coin_mvt_sat(ctx, EXTERNAL, NULL, outpoint, NULL, + blockheight, take(new_tag_arr(NULL, tag)), + amount, true); } bool chain_mvt_is_external(const struct chain_coin_mvt *mvt) From 4dafeede5c81d1ba7626480083ad4bb6716a5f70 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 25 Jan 2022 14:24:31 -0600 Subject: [PATCH 0245/1530] coin moves: notify when we make deposits to external accounts The blockheight is zero though, since these aren't included in a block yet. We also don't issue an 'external' deposit event if we can tell that the address you're sending to actually belongs to our wallet (we'll issue a deposit event when it gets included in a block) --- common/psbt_open.c | 17 +++++++++++ common/psbt_open.h | 12 ++++++++ doc/PLUGINS.md | 4 ++- plugins/spender/multiwithdraw.c | 16 +++++++--- plugins/txprepare.c | 12 ++++++++ tests/test_misc.py | 12 +++++++- tests/test_wallet.py | 28 ++++++++++++++++-- tests/utils.py | 2 +- wallet/walletrpc.c | 52 ++++++++++++++++++++++++++++++++- 9 files changed, 144 insertions(+), 11 deletions(-) diff --git a/common/psbt_open.c b/common/psbt_open.c index b7a6f330c9ee..646b4ea029a5 100644 --- a/common/psbt_open.c +++ b/common/psbt_open.c @@ -491,6 +491,23 @@ bool psbt_has_our_input(const struct wally_psbt *psbt) return false; } +void psbt_output_mark_as_external(const tal_t *ctx, + struct wally_psbt_output *output) +{ + u8 *key = psbt_make_key(tmpctx, PSBT_TYPE_OUTPUT_EXTERNAL, NULL); + beint16_t bev = cpu_to_be16(1); + + psbt_output_set_unknown(ctx, output, key, &bev, sizeof(bev)); +} + +bool psbt_output_to_external(const struct wally_psbt_output *output) +{ + size_t unused; + void *result = psbt_get_lightning(&output->unknowns, + PSBT_TYPE_OUTPUT_EXTERNAL, &unused); + return !(!result); +} + bool psbt_contribs_changed(struct wally_psbt *orig, struct wally_psbt *new) { diff --git a/common/psbt_open.h b/common/psbt_open.h index 8e41a4e13b58..134a5da65754 100644 --- a/common/psbt_open.h +++ b/common/psbt_open.h @@ -37,6 +37,7 @@ struct psbt_changeset { #define PSBT_TYPE_SERIAL_ID 0x01 #define PSBT_TYPE_INPUT_MARKER 0x02 +#define PSBT_TYPE_OUTPUT_EXTERNAL 0x04 /* psbt_get_serial_id - Returns the serial_id from an unknowns map * @@ -177,6 +178,17 @@ bool psbt_input_is_ours(const struct wally_psbt_input *input); */ bool psbt_has_our_input(const struct wally_psbt *psbt); +/* psbt_output_mark_external - Marks an output as a deposit to + * an external address. + * Used when withdrawing from internal + * wallet */ +void psbt_output_mark_as_external(const tal_t *ctx, + struct wally_psbt_output *output); + +/* psbt_output_to_external - Is this an output we're paying to an external + * party? */ +bool psbt_output_to_external(const struct wally_psbt_output *output); + /* psbt_contribs_changed - Returns true if the psbt's inputs/outputs * have changed. * diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 32ce9229a12c..c6e1af1ac0e1 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -788,7 +788,9 @@ both the debit/credit contain fees. Technically routed debits are the - `lease_fee`: amount paid as lease fee - `leased`: tags channel_open, channel contains leased funds -`blockheight` is the block the txid is included in. +`blockheight` is the block the txid is included in. `channel_mvt`s will be null, +so will the blockheight for withdrawals to external parties (we issue these events +when we send the tx containing them, before they're included in the chain). The `timestamp` is seconds since Unix epoch of the node's machine time at the time lightningd broadcasts the notification. diff --git a/plugins/spender/multiwithdraw.c b/plugins/spender/multiwithdraw.c index afe7a3534c7f..b89638c68536 100644 --- a/plugins/spender/multiwithdraw.c +++ b/plugins/spender/multiwithdraw.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ struct multiwithdraw_destination { struct amount_sat amount; /* Whether the amount was "all". */ bool all; + /* Whether this is to an external addr (all passed in are assumed) */ + bool is_to_external; }; struct multiwithdraw_command { @@ -93,6 +96,7 @@ param_outputs_array(struct command *cmd, enum address_parse_result res; dest = &(*outputs)[i]; + dest->is_to_external = true; if (e->type != JSMN_OBJECT) goto err; @@ -537,6 +541,7 @@ mw_after_newaddr(struct command *cmd, change.script = script; change.amount = mw->change_amount; change.all = false; + change.is_to_external = false; tal_arr_expand(&mw->outputs, change); @@ -560,15 +565,18 @@ mw_load_outputs(struct multiwithdraw_command *mw) { /* Insert outputs at random locations. */ for (size_t i = 0; i < tal_count(mw->outputs); ++i) { + struct wally_psbt_output *out; /* There are already `i` outputs at this point, * select from 0 to `i` inclusive, with 0 meaning * "before first output" and `i` meaning "after * last output". */ size_t point = pseudorand(i + 1); - psbt_insert_output(mw->psbt, - mw->outputs[i].script, - mw->outputs[i].amount, - point); + out = psbt_insert_output(mw->psbt, + mw->outputs[i].script, + mw->outputs[i].amount, + point); + if (mw->outputs[i].is_to_external) + psbt_output_mark_as_external(mw->psbt, out); } if (chainparams->is_elements) { diff --git a/plugins/txprepare.c b/plugins/txprepare.c index 82776adc0f19..4de70d44fb47 100644 --- a/plugins/txprepare.c +++ b/plugins/txprepare.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ struct tx_output { struct amount_sat amount; const u8 *script; + bool is_to_external; }; struct txprepare { @@ -79,6 +81,9 @@ static struct command_result *param_outputs(struct command *cmd, enum address_parse_result res; struct tx_output *out = &txp->outputs[i]; + /* We assume these are accounted for elsewhere */ + out->is_to_external = false; + /* output format: {destination: amount} */ if (t->type != JSMN_OBJECT) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, @@ -190,6 +195,11 @@ static struct command_result *finish_txprepare(struct command *cmd, struct amount_sat, &txp->outputs[i].amount)); psbt_add_output(txp->psbt, out, i); + + if (txp->outputs[i].is_to_external) + psbt_output_mark_as_external(txp->psbt, + &txp->psbt->outputs[i]); + wally_tx_output_free(out); } @@ -239,6 +249,7 @@ static struct command_result *newaddr_done(struct command *cmd, sizeof(txp->outputs[0]) * (num - pos)); txp->outputs[pos].amount = txp->change_amount; + txp->outputs[pos].is_to_external = false; if (json_to_address_scriptpubkey(txp, chainparams, buf, addr, &txp->outputs[pos].script) != ADDRESS_PARSE_SUCCESS) { @@ -516,6 +527,7 @@ static struct command_result *json_withdraw(struct command *cmd, } txp->outputs[0].amount = *amount; txp->outputs[0].script = scriptpubkey; + txp->outputs[0].is_to_external = true; txp->weight = bitcoin_tx_core_weight(1, tal_count(txp->outputs)) + bitcoin_tx_output_weight(tal_bytelen(scriptpubkey)); diff --git a/tests/test_misc.py b/tests/test_misc.py index 3cde1fad72e7..3f13b1cadbff 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -10,7 +10,7 @@ wait_for, TailableProc, env ) from utils import ( - account_balance, scriptpubkey_addr + account_balance, scriptpubkey_addr, check_coin_moves ) from ephemeral_port_reserve import reserve from utils import EXPERIMENTAL_FEATURES @@ -623,6 +623,16 @@ def dont_spend_outputs(n, txid): sync_blockheight(bitcoind, [l1]) assert account_balance(l1, 'wallet') == 0 + external_moves = [ + {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit': 11957603000, 'debit': 0, 'tags': ['deposit']}, + ] + + check_coin_moves(l1, 'external', external_moves, chainparams) + def test_io_logging(node_factory, executor): l1 = node_factory.get_node(options={'log-level': 'io'}) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 4aaf7557d613..f7c601b26e2e 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -7,6 +7,7 @@ from utils import ( only_one, wait_for, sync_blockheight, EXPERIMENTAL_FEATURES, VALGRIND, check_coin_moves, TailableProc, scriptpubkey_addr, + check_utxos_channel ) import os @@ -223,7 +224,8 @@ def test_addfunds_from_block(node_factory, bitcoind): """Send funds to the daemon without telling it explicitly """ # Previous runs with same bitcoind can leave funds! - l1 = node_factory.get_node(random_hsm=True) + coin_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') + l1 = node_factory.get_node(random_hsm=True, options={'plugin': coin_plugin}) addr = l1.rpc.newaddr()['bech32'] bitcoind.rpc.sendtoaddress(addr, 0.1) @@ -248,6 +250,15 @@ def test_addfunds_from_block(node_factory, bitcoind): output = only_one(l1.rpc.listfunds()['outputs']) assert output['address'] == addr + # We don't print a 'external deposit' event + # for funds that come back to our own wallet + expected_utxos = { + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], + 'A': [('wallet', ['deposit'], None, None)], + } + + check_utxos_channel(l1, [], expected_utxos) + def test_txprepare_multi(node_factory, bitcoind): amount = 10000000 @@ -1298,11 +1309,13 @@ def test_withdraw_nlocktime_fuzz(node_factory, bitcoind): raise Exception("No transaction with fuzzed nLockTime !") -def test_multiwithdraw_simple(node_factory, bitcoind): +def test_multiwithdraw_simple(node_factory, bitcoind, chainparams): """ Test simple multiwithdraw usage. """ - l1, l2, l3 = node_factory.get_nodes(3) + coin_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') + l1, l2, l3 = node_factory.get_nodes(3, opts=[{'plugin': coin_plugin}, + {}, {}]) l1.fundwallet(10**8) addr2 = l2.rpc.newaddr()['bech32'] @@ -1329,6 +1342,15 @@ def test_multiwithdraw_simple(node_factory, bitcoind): assert only_one(funds3)["status"] == "confirmed" assert only_one(funds3)["amount_msat"] == amount3 + expected_utxos = { + '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], + 'A': [('wallet', ['deposit'], None, None), + ('external', ['deposit'], None, None), + ('external', ['deposit'], None, None)], + } + + check_utxos_channel(l1, [], expected_utxos) + @unittest.skipIf( TEST_NETWORK == 'liquid-regtest', diff --git a/tests/utils.py b/tests/utils.py index d10fdada3b4c..1d417a2da77c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -136,7 +136,7 @@ def check_coin_moves(n, account_id, expected_moves, chainparams): assert mv['timestamp'] > 0 assert mv['coin_type'] == chainparams['bip173_prefix'] # chain moves should have blockheights - if mv['type'] == 'chain_mvt': + if mv['type'] == 'chain_mvt' and mv['account_id'] != 'external': assert mv['blockheight'] is not None for num, m in enumerate(expected_moves): diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 5aac2c19daa0..584dfa8158a0 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -10,11 +10,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -753,9 +755,48 @@ struct sending_psbt { struct command *cmd; struct utxo **utxos; struct wally_tx *wtx; + /* Hold onto b/c has data about + * which are to external addresses */ + struct wally_psbt *psbt; u32 reserve_blocks; }; +static void maybe_notify_new_external_send(struct lightningd *ld, + struct bitcoin_txid *txid, + u32 outnum, + struct wally_psbt *psbt) +{ + struct chain_coin_mvt *mvt; + struct bitcoin_outpoint outpoint; + struct amount_sat amount; + u32 index; + bool is_p2sh; + const u8 *script; + + /* If it's not going to an external address, ignore */ + if (!psbt_output_to_external(&psbt->outputs[outnum])) + return; + + /* If it's going to our wallet, ignore */ + script = wally_tx_output_get_script(tmpctx, + &psbt->tx->outputs[outnum]); + if (wallet_can_spend(ld->wallet, script, &index, &is_p2sh)) + return; + + outpoint.txid = *txid; + outpoint.n = outnum; + amount = psbt_output_get_amount(psbt, outnum); + + mvt = new_coin_external_deposit(NULL, &outpoint, + 0, amount, + DEPOSIT); + + mvt->originating_acct = WALLET; + notify_chain_mvt(ld, mvt); + tal_free(mvt); +} + + static void sendpsbt_done(struct bitcoind *bitcoind UNUSED, bool success, const char *msg, struct sending_psbt *sending) @@ -787,9 +828,12 @@ static void sendpsbt_done(struct bitcoind *bitcoind UNUSED, /* Extract the change output and add it to the DB */ wallet_extract_owned_outputs(ld->wallet, sending->wtx, NULL, &change); + wally_txid(sending->wtx, &txid); + + for (size_t i = 0; i < sending->psbt->num_outputs; i++) + maybe_notify_new_external_send(ld, &txid, i, sending->psbt); response = json_stream_success(sending->cmd); - wally_txid(sending->wtx, &txid); json_add_hex_talarr(response, "tx", linearize_wtx(tmpctx, sending->wtx)); json_add_txid(response, "txid", &txid); was_pending(command_success(sending->cmd, response)); @@ -818,6 +862,12 @@ static struct command_result *json_sendpsbt(struct command *cmd, psbt_finalize(psbt); sending->wtx = psbt_final_tx(sending, psbt); + + /* psbt contains info about which outputs are to external, + * and thus need a coin_move issued for them. We only + * notify if the transaction broadcasts */ + sending->psbt = tal_steal(sending, psbt); + if (!sending->wtx) return command_fail(cmd, LIGHTNINGD, "PSBT not finalizeable %s", From 59f174996765bf127f1c5ed96d3b77e95f21e19c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 12 Jan 2022 13:41:58 +1030 Subject: [PATCH 0246/1530] bitcoin: fix tx weight calculation when there are no witnesses, but will be. We had an out-by-two error when calculating weights, because we grab weights on unsigned txs. Signed-off-by: Rusty Russell --- bitcoin/tx.c | 13 ++++++++++++- bitcoin/tx.h | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 318a2ec94f2e..76d47b173b80 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -457,7 +457,18 @@ size_t wally_tx_weight(const struct wally_tx *wtx) size_t bitcoin_tx_weight(const struct bitcoin_tx *tx) { - return wally_tx_weight(tx->wtx); + size_t extra; + size_t num_witnesses; + + /* If we don't have witnesses *yet*, libwally doesn't encode + * in BIP 141 style, omitting the flag and marker bytes */ + wally_tx_get_witness_count(tx->wtx, &num_witnesses); + if (num_witnesses == 0) + extra = 2; + else + extra = 0; + + return extra + wally_tx_weight(tx->wtx); } void wally_txid(const struct wally_tx *wtx, struct bitcoin_txid *txid) diff --git a/bitcoin/tx.h b/bitcoin/tx.h index 85bb38444480..5bccaf7b6b3b 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -57,7 +57,7 @@ void wally_txid(const struct wally_tx *wtx, struct bitcoin_txid *txid); u8 *linearize_tx(const tal_t *ctx, const struct bitcoin_tx *tx); u8 *linearize_wtx(const tal_t *ctx, const struct wally_tx *wtx); -/* Get weight of tx in Sipa. */ +/* Get weight of tx in Sipa; assumes it will have witnesses! */ size_t bitcoin_tx_weight(const struct bitcoin_tx *tx); size_t wally_tx_weight(const struct wally_tx *wtx); From 169c113d150bac78181bf622acd80b77ef5bd4e1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 12 Jan 2022 14:22:43 +1030 Subject: [PATCH 0247/1530] pytest: test that our closingd tx weight estimate is correct. Compare against lightningd's, and the actual tx. Signed-off-by: Rusty Russell --- lightningd/closing_control.c | 11 ++++++----- tests/test_closing.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 65f4bb909fad..5f0aa53d16aa 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -201,14 +201,15 @@ static bool closing_fee_is_acceptable(struct lightningd *ld, fee = calc_tx_fee(channel->funding_sats, tx); last_fee = calc_tx_fee(channel->funding_sats, channel->last_tx); - log_debug(channel->log, "Their actual closing tx fee is %s" - " vs previous %s", - type_to_string(tmpctx, struct amount_sat, &fee), - type_to_string(tmpctx, struct amount_sat, &last_fee)); - /* Weight once we add in sigs. */ weight = bitcoin_tx_weight(tx) + bitcoin_tx_input_sig_weight() * 2; + log_debug(channel->log, "Their actual closing tx fee is %s" + " vs previous %s: weight is %"PRIu64, + type_to_string(tmpctx, struct amount_sat, &fee), + type_to_string(tmpctx, struct amount_sat, &last_fee), + weight); + /* If we don't have a feerate estimate, this gives feerate_floor */ min_feerate = feerate_min(ld, &feerate_unknown); diff --git a/tests/test_closing.py b/tests/test_closing.py index f5993a7b28b0..80dc81df070f 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3653,3 +3653,32 @@ def test_close_twice(node_factory, executor): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) assert fut.result(TIMEOUT)['type'] == 'mutual' assert fut2.result(TIMEOUT)['type'] == 'mutual' + + +@pytest.mark.xfail(strict=True) +def test_close_weight_estimate(node_factory, bitcoind): + """closingd uses the expected closing tx weight to constrain fees; make sure that lightningd agrees + once it has the actual agreed tx""" + l1, l2 = node_factory.line_graph(2) + l1.rpc.close(l2.info['id']) + + # Closingd gives this estimate before it begins + log = l1.daemon.wait_for_log('Expected closing weight = ') + expected_weight = int(re.match('.*Expected closing weight = ([0-9]*),.*', log).group(1)) + + # This is the actual weight: in theory this could use their + # actual sig, and thus vary, but we don't do that. + log = l1.daemon.wait_for_log('Their actual closing tx fee is') + actual_weight = int(re.match('.*: weight is ([0-9]*).*', log).group(1)) + + assert actual_weight == expected_weight + + log = l1.daemon.wait_for_log('sendrawtransaction: ') + tx = re.match('.*sendrawtransaction: ([0-9a-f]*).*', log).group(1) + + # This could actually be a bit shorter: 1 in 256 chance we get + # lucky with a sig and it's shorter. We have 2 sigs, so that's + # 1 in 128. Unlikely to do better than 2 bytes off though! + signed_weight = int(bitcoind.rpc.decoderawtransaction(tx)['weight']) + assert signed_weight <= actual_weight + assert signed_weight >= actual_weight - 2 From c88dc7883e887cdc96fb2eef6dd7c77e02668eaa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 12 Jan 2022 14:22:52 +1030 Subject: [PATCH 0248/1530] bitcoin: implement bitcoin_tx_2of2_input_witness_weight Saves us actually creating the witness to measure it. Signed-off-by: Rusty Russell --- ...-tx-bitcoin_tx_2of2_input_witness_weight.c | 157 ++++++++++++++++++ bitcoin/tx.c | 10 ++ bitcoin/tx.h | 3 + 3 files changed, 170 insertions(+) create mode 100644 bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c diff --git a/bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c b/bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c new file mode 100644 index 000000000000..af5483956300 --- /dev/null +++ b/bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c @@ -0,0 +1,157 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for fromwire_wally_psbt */ +struct wally_psbt *fromwire_wally_psbt(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_wally_psbt called!\n"); abort(); } +/* Generated stub for new_psbt */ +struct wally_psbt *new_psbt(const tal_t *ctx UNNEEDED, + const struct wally_tx *wtx UNNEEDED) +{ fprintf(stderr, "new_psbt called!\n"); abort(); } +/* Generated stub for psbt_add_output */ +struct wally_psbt_output *psbt_add_output(struct wally_psbt *psbt UNNEEDED, + struct wally_tx_output *output UNNEEDED, + size_t insert_at UNNEEDED) +{ fprintf(stderr, "psbt_add_output called!\n"); abort(); } +/* Generated stub for psbt_append_input */ +struct wally_psbt_input *psbt_append_input(struct wally_psbt *psbt UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 sequence UNNEEDED, + const u8 *scriptSig UNNEEDED, + const u8 *input_wscript UNNEEDED, + const u8 *redeemscript UNNEEDED) +{ fprintf(stderr, "psbt_append_input called!\n"); abort(); } +/* Generated stub for psbt_elements_input_set_asset */ +void psbt_elements_input_set_asset(struct wally_psbt *psbt UNNEEDED, size_t in UNNEEDED, + struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "psbt_elements_input_set_asset called!\n"); abort(); } +/* Generated stub for psbt_final_tx */ +struct wally_tx *psbt_final_tx(const tal_t *ctx UNNEEDED, const struct wally_psbt *psbt UNNEEDED) +{ fprintf(stderr, "psbt_final_tx called!\n"); abort(); } +/* Generated stub for psbt_finalize */ +bool psbt_finalize(struct wally_psbt *psbt UNNEEDED) +{ fprintf(stderr, "psbt_finalize called!\n"); abort(); } +/* Generated stub for psbt_input_get_amount */ +struct amount_sat psbt_input_get_amount(const struct wally_psbt *psbt UNNEEDED, + size_t in UNNEEDED) +{ fprintf(stderr, "psbt_input_get_amount called!\n"); abort(); } +/* Generated stub for psbt_input_set_wit_utxo */ +void psbt_input_set_wit_utxo(struct wally_psbt *psbt UNNEEDED, size_t in UNNEEDED, + const u8 *scriptPubkey UNNEEDED, struct amount_sat amt UNNEEDED) +{ fprintf(stderr, "psbt_input_set_wit_utxo called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for towire_wally_psbt */ +void towire_wally_psbt(u8 **pptr UNNEEDED, const struct wally_psbt *psbt UNNEEDED) +{ fprintf(stderr, "towire_wally_psbt called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +int main(int argc, const char *argv[]) +{ + struct privkey p; + struct pubkey pub; + struct bitcoin_signature sig; + struct sha256_double h; + u8 **wit; + size_t weight; + + common_setup(argv[0]); + chainparams = chainparams_for_network("bitcoin"); + + memset(&h, 0, sizeof(h)); + memset(&p, 1, sizeof(p)); + sign_hash(&p, &h, &sig.s); + sig.sighash_type = SIGHASH_ALL; + pubkey_from_privkey(&p, &pub); + + wit = bitcoin_witness_2of2(tmpctx, &sig, &sig, &pub, &pub); + + /* 1 byte for num witnesses, one per witness element */ + weight = 1; + for (size_t i = 0; i < tal_count(wit); i++) + weight += 1 + tal_bytelen(wit[i]); + assert(bitcoin_tx_2of2_input_witness_weight() == weight); + + common_shutdown(); + return 0; +} diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 76d47b173b80..215d2e25f1e2 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -884,6 +884,16 @@ size_t bitcoin_tx_simple_input_weight(bool p2sh) bitcoin_tx_simple_input_witness_weight()); } +size_t bitcoin_tx_2of2_input_witness_weight(void) +{ + /* witness[0] = "" + * witness[1] = sig + * witness[2] = sig + * witness[3] = 2 key key 2 CHECKMULTISIG + */ + return 1 + (1 + 0) + (1 + 72) + (1 + 72) + (1 + 1 + 33 + 33 + 1 + 1); +} + struct amount_sat change_amount(struct amount_sat excess, u32 feerate_perkw, size_t total_weight) { diff --git a/bitcoin/tx.h b/bitcoin/tx.h index 5bccaf7b6b3b..694cc4a75213 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -271,6 +271,9 @@ size_t bitcoin_tx_simple_input_witness_weight(void); /* We only do segwit inputs, and we assume witness is sig + key */ size_t bitcoin_tx_simple_input_weight(bool p2sh); +/* The witness for our 2of2 input (closing or commitment tx). */ +size_t bitcoin_tx_2of2_input_witness_weight(void); + /** * change_amount - Is it worth making a P2WPKH change output at this feerate? * @excess: input amount we have above the tx fee and other outputs. From 8a8d7c4243107d8698e5172669ba163f575c331e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 Jan 2022 12:42:28 +1030 Subject: [PATCH 0249/1530] elements: unify overhead calculation. And in particular, fix onchaind grinding code which used the actual number of inputs and outputs (which already includes the fee output); that breaks with the next patch which fixes other calculations. Signed-off-by: Rusty Russell --- bitcoin/tx.c | 1 - bitcoin/tx.h | 37 ++++++++++++++++++++++++++++++++++--- channeld/watchtower.c | 2 +- common/htlc_tx.h | 35 +++++------------------------------ common/initial_commit_tx.h | 23 +++-------------------- onchaind/onchaind.c | 12 ++++++------ 6 files changed, 49 insertions(+), 61 deletions(-) diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 215d2e25f1e2..9d89ec6964a0 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -1,6 +1,5 @@ #include "config.h" #include -#include #include #include #include diff --git a/bitcoin/tx.h b/bitcoin/tx.h index 694cc4a75213..170371094991 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -1,9 +1,10 @@ #ifndef LIGHTNING_BITCOIN_TX_H #define LIGHTNING_BITCOIN_TX_H #include "config.h" -#include "shadouble.h" -#include "signature.h" -#include "varint.h" +#include +#include +#include +#include #include #include #include @@ -230,6 +231,36 @@ bool elements_wtx_output_is_fee(const struct wally_tx *tx, int outnum); */ bool elements_tx_output_is_fee(const struct bitcoin_tx *tx, int outnum); +/** Attempt to compute the elements overhead given a base bitcoin size. + * + * The overhead consists of 2 empty proofs for the transaction, 6 bytes of + * proofs per input and 35 bytes per output. In addition the explicit fee + * output will add 9 bytes and the per output overhead as well. + */ +static inline size_t elements_tx_overhead(const struct chainparams *chainparams, + size_t incount, size_t outcount) +{ + size_t overhead; + + if (!chainparams->is_elements) + return 0; + + /* Each transaction has surjection and rangeproof (both empty + * for us as long as we use unblinded L-BTC transactions). */ + overhead = 2 * 4; + /* For elements we also need to add the fee output and the + * overhead for rangeproofs into the mix. */ + overhead += (8 + 1) * 4; /* Bitcoin style output */ + + /* All outputs have a bit of elements overhead (incl fee) */ + overhead += (32 + 1 + 1 + 1) * 4 * (outcount + 1); /* Elements added fields */ + + /* Inputs have 6 bytes of blank proofs attached. */ + overhead += 6 * incount; + + return overhead; +} + /** * Calculate the fees for this transaction */ diff --git a/channeld/watchtower.c b/channeld/watchtower.c index a895dfc1d31b..20d273d6510f 100644 --- a/channeld/watchtower.c +++ b/channeld/watchtower.c @@ -78,7 +78,7 @@ penalty_tx_create(const tal_t *ctx, /* Worst-case sig is 73 bytes */ weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); - weight = elements_add_overhead(weight, 1, 1); + weight += elements_tx_overhead(chainparams, 1, 1); fee = amount_tx_fee(penalty_feerate, weight); if (!amount_sat_add(&min_out, dust_limit, fee)) diff --git a/common/htlc_tx.h b/common/htlc_tx.h index f982757eaa1a..c658ca6bd9ea 100644 --- a/common/htlc_tx.h +++ b/common/htlc_tx.h @@ -2,6 +2,7 @@ #define LIGHTNING_COMMON_HTLC_TX_H #include "config.h" #include +#include #include #include @@ -12,34 +13,6 @@ struct preimage; struct pubkey; struct ripemd160; -/** Attempt to compute the elements overhead given a base bitcoin size. - * - * The overhead consists of 2 empty proofs for the transaction, 6 bytes of - * proofs per input and 35 bytes per output. In addition the explicit fee - * output will add 9 bytes and the per output overhead as well. - */ -/* FIXME: This seems to be a generalization of the logic in initial_commit_tx.h - * and should be in bitcoin/tx.h */ -static inline size_t elements_add_overhead(size_t weight, size_t incount, - size_t outcount) -{ - if (chainparams->is_elements) { - /* Each transaction has surjection and rangeproof (both empty - * for us as long as we use unblinded L-BTC transactions). */ - weight += 2 * 4; - /* For elements we also need to add the fee output and the - * overhead for rangeproofs into the mix. */ - weight += (8 + 1) * 4; /* Bitcoin style output */ - - /* All outputs have a bit of elements overhead */ - weight += (32 + 1 + 1 + 1) * 4 * (outcount + 1); /* Elements added fields */ - - /* Inputs have 6 bytes of blank proofs attached. */ - weight += 6 * incount; - } - return weight; -} - static inline struct amount_sat htlc_timeout_fee(u32 feerate_per_kw, bool option_anchor_outputs) { @@ -56,7 +29,8 @@ static inline struct amount_sat htlc_timeout_fee(u32 feerate_per_kw, base = 666; else base = 663; - return amount_tx_fee(elements_add_overhead(base, 1, 1), feerate_per_kw); + return amount_tx_fee(base + elements_tx_overhead(chainparams, 1, 1), + feerate_per_kw); } static inline struct amount_sat htlc_success_fee(u32 feerate_per_kw, @@ -75,7 +49,8 @@ static inline struct amount_sat htlc_success_fee(u32 feerate_per_kw, base = 706; else base = 703; - return amount_tx_fee(elements_add_overhead(base, 1, 1), feerate_per_kw); + return amount_tx_fee(base + elements_tx_overhead(chainparams, 1, 1), + feerate_per_kw); } /* Create HTLC-success tx to spend a received HTLC commitment tx diff --git a/common/initial_commit_tx.h b/common/initial_commit_tx.h index 798c5ac4a594..e0bc3c645784 100644 --- a/common/initial_commit_tx.h +++ b/common/initial_commit_tx.h @@ -4,6 +4,7 @@ #include "config.h" #include #include +#include #include #include @@ -47,26 +48,8 @@ static inline size_t commit_tx_base_weight(size_t num_untrimmed_htlcs, */ weight += 172 * num_untrimmed_htlcs; - if (chainparams->is_elements) { - /* Each transaction has surjection and rangeproof (both empty - * for us as long as we use unblinded L-BTC transactions). */ - weight += 2 * 4; - - /* Inputs have 6 bytes of blank proofs attached. This TX only - * has a single input. */ - weight += 6; - - /* Each direct output has a bit more weight to it */ - weight += (32 + 1 + 1 + 1) * 4 * 2; /* Elements added fields */ - - /* Each HTLC output also carries a bit more weight */ - weight += (32 + 1 + 1 + 1) * 4 * num_untrimmed_htlcs; - - /* For elements we also need to add the fee output and the - * overhead for rangeproofs into the mix. */ - weight += (8 + 1) * 4; /* Bitcoin style output */ - weight += (32 + 1 + 1 + 1) * 4; /* Elements added fields */ - } + /* Extra fields for Elements */ + weight += elements_tx_overhead(chainparams, 1, 1); return weight; } diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 869c772aec3c..ecd60166a0e5 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -441,8 +441,7 @@ static bool set_htlc_timeout_fee(struct bitcoin_tx *tx, weight = 666; else weight = 663; - weight = elements_add_overhead(weight, tx->wtx->num_inputs, - tx->wtx->num_outputs); + weight += elements_tx_overhead(chainparams, 1, 1); assert(amount_asset_is_main(&asset)); amount = amount_asset_to_sat(&asset); @@ -491,15 +490,16 @@ static void set_htlc_success_fee(struct bitcoin_tx *tx, else weight = 703; - weight = elements_add_overhead(weight, tx->wtx->num_inputs, - tx->wtx->num_outputs); + weight += elements_tx_overhead(chainparams, 1, 1); if (amount_sat_eq(fee, AMOUNT_SAT(UINT64_MAX))) { if (!grind_htlc_tx_fee(&fee, tx, remotesig, wscript, weight)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "htlc_success_fee can't be found " - "for tx %s, signature %s, wscript %s", + "for tx %s (weight %zu, feerate %u-%u), signature %s, wscript %s", type_to_string(tmpctx, struct bitcoin_tx, tx), + weight, + min_possible_feerate, max_possible_feerate, type_to_string(tmpctx, struct bitcoin_signature, remotesig), @@ -596,7 +596,7 @@ static struct bitcoin_tx *tx_to_us(const tal_t *ctx, /* Worst-case sig is 73 bytes */ weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); - weight = elements_add_overhead(weight, 1, 1); + weight += elements_tx_overhead(chainparams, 1, 1); fee = amount_tx_fee(feerate, weight); /* Result is trivial? Spend with small feerate, but don't wait From de28bbd792e029faf5b3deeedcb11ecbd4f6a45f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 Jan 2022 12:42:34 +1030 Subject: [PATCH 0250/1530] closingd, lightningd: use bitcoin_tx_2of2_input_witness_weight This fixes lightningd's chronic weight underestimate. Signed-off-by: Rusty Russell Changelog-Fixed: closingd: more accurate weight estimation helps mutual closing near min/max feerates. --- closingd/closingd.c | 17 ++--------------- lightningd/closing_control.c | 4 +++- tests/test_closing.py | 1 - 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/closingd/closingd.c b/closingd/closingd.c index 2d228a462434..ffcac22e3d7a 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -579,10 +579,6 @@ static size_t closing_tx_weight_estimate(u8 *scriptpubkey[NUM_SIDES], /* We create a dummy close */ struct bitcoin_tx *tx; struct bitcoin_outpoint dummy_funding; - struct bitcoin_signature dummy_sig; - struct privkey dummy_privkey; - struct pubkey dummy_pubkey; - u8 **witness; memset(&dummy_funding, 0, sizeof(dummy_funding)); tx = create_close_tx(tmpctx, chainparams, @@ -594,17 +590,8 @@ static size_t closing_tx_weight_estimate(u8 *scriptpubkey[NUM_SIDES], out[REMOTE], dust_limit); - /* Create a signature, any signature, so we can weigh fully "signed" - * tx. */ - dummy_sig.sighash_type = SIGHASH_ALL; - memset(&dummy_privkey, 1, sizeof(dummy_privkey)); - sign_hash(&dummy_privkey, &dummy_funding.txid.shad, &dummy_sig.s); - pubkey_from_privkey(&dummy_privkey, &dummy_pubkey); - witness = bitcoin_witness_2of2(NULL, &dummy_sig, &dummy_sig, - &dummy_pubkey, &dummy_pubkey); - bitcoin_tx_input_set_witness(tx, 0, take(witness)); - - return bitcoin_tx_weight(tx); + /* We will have to append the witness */ + return bitcoin_tx_weight(tx) + bitcoin_tx_2of2_input_witness_weight(); } /* Get the minimum and desired fees */ diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 5f0aa53d16aa..f7d88f2fdeb8 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -202,7 +202,9 @@ static bool closing_fee_is_acceptable(struct lightningd *ld, last_fee = calc_tx_fee(channel->funding_sats, channel->last_tx); /* Weight once we add in sigs. */ - weight = bitcoin_tx_weight(tx) + bitcoin_tx_input_sig_weight() * 2; + assert(!tx->wtx->inputs[0].witness + || tx->wtx->inputs[0].witness->num_items == 0); + weight = bitcoin_tx_weight(tx) + bitcoin_tx_2of2_input_witness_weight(); log_debug(channel->log, "Their actual closing tx fee is %s" " vs previous %s: weight is %"PRIu64, diff --git a/tests/test_closing.py b/tests/test_closing.py index 80dc81df070f..e362c8856aa9 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3655,7 +3655,6 @@ def test_close_twice(node_factory, executor): assert fut2.result(TIMEOUT)['type'] == 'mutual' -@pytest.mark.xfail(strict=True) def test_close_weight_estimate(node_factory, bitcoind): """closingd uses the expected closing tx weight to constrain fees; make sure that lightningd agrees once it has the actual agreed tx""" From a55cfab00dd8a29896962ab3306250b979080b44 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 Jan 2022 12:42:34 +1030 Subject: [PATCH 0251/1530] elements: fix gross weight differential. Firstly, we were not adding the extra fee output on our dummy tx, because the fee amount was 0. We probably should always do this, even if it's 0. Secondly, there are 6 witnesses, not 1, for elements txs. Signed-off-by: Rusty Russell --- bitcoin/tx.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 9d89ec6964a0..6a4fc788f890 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -162,7 +162,7 @@ static int elements_tx_add_fee_output(struct bitcoin_tx *tx) int pos; /* If we aren't using elements, we don't add explicit fee outputs */ - if (!chainparams->is_elements || amount_sat_eq(fee, AMOUNT_SAT(0))) + if (!chainparams->is_elements) return -1; /* Try to find any existing fee output */ @@ -462,9 +462,12 @@ size_t bitcoin_tx_weight(const struct bitcoin_tx *tx) /* If we don't have witnesses *yet*, libwally doesn't encode * in BIP 141 style, omitting the flag and marker bytes */ wally_tx_get_witness_count(tx->wtx, &num_witnesses); - if (num_witnesses == 0) - extra = 2; - else + if (num_witnesses == 0) { + if (chainparams->is_elements) + extra = 7; + else + extra = 2; + } else extra = 0; return extra + wally_tx_weight(tx->wtx); From c0e3155bb60421c4a37cc43150e2960e8325bfd1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 26 Jan 2022 12:42:34 +1030 Subject: [PATCH 0252/1530] pytest: update elements for new, more accurate feerate calc. And skip some tests: I'd simply be pasting in the results, which is not very useful. Signed-off-by: Rusty Russell --- tests/test_closing.py | 26 +++++++++++++++----------- tests/test_connection.py | 3 ++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index e362c8856aa9..0d0e1f7e2041 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -26,7 +26,7 @@ def test_closing_simple(node_factory, bitcoind, chainparams): l1, l2 = node_factory.line_graph(2, opts={'plugin': coin_mvt_plugin}) chan = l1.get_channel_scid(l2) channel_id = first_channel_id(l1, l2) - fee = closing_fee(3750, 2) if not chainparams['elements'] else 3603 + fee = closing_fee(3750, 2) if not chainparams['elements'] else 4263 l1.pay(l2, 200000000) @@ -467,28 +467,30 @@ def get_mempool_when_size_1(): @unittest.skipIf(EXPERIMENTAL_FEATURES, "anchors uses quick-close, not negotiation") +@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Different closing fees") def test_closing_negotiation_step_30pct(node_factory, bitcoind, chainparams): """Test that the closing fee negotiation step works, 30%""" opts = {} opts['fee_negotiation_step'] = '30%' opts['close_initiated_by'] = 'opener' - opts['expected_close_fee'] = 20537 if not chainparams['elements'] else 26046 + opts['expected_close_fee'] = 20537 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) opts['close_initiated_by'] = 'peer' - opts['expected_close_fee'] = 20233 if not chainparams['elements'] else 25657 + opts['expected_close_fee'] = 20233 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) @unittest.skipIf(EXPERIMENTAL_FEATURES, "anchors uses quick-close, not negotiation") +@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Different closing fees") def test_closing_negotiation_step_100pct(node_factory, bitcoind, chainparams): """Test that the closing fee negotiation step works, 100%""" opts = {} opts['fee_negotiation_step'] = '100%' opts['close_initiated_by'] = 'opener' - opts['expected_close_fee'] = 20001 if not chainparams['elements'] else 25366 + opts['expected_close_fee'] = 20001 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) # The close fee of 20499 looks strange in this case - one would expect @@ -497,37 +499,39 @@ def test_closing_negotiation_step_100pct(node_factory, bitcoind, chainparams): # * the opener is always first to propose, he uses 50% step, so he proposes 20500 # * the range is narrowed to [20001, 20499] and the peer proposes 20499 opts['close_initiated_by'] = 'peer' - opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 25998 + opts['expected_close_fee'] = 20499 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) @unittest.skipIf(EXPERIMENTAL_FEATURES, "anchors uses quick-close, not negotiation") +@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Different closing fees") def test_closing_negotiation_step_1sat(node_factory, bitcoind, chainparams): """Test that the closing fee negotiation step works, 1sat""" opts = {} opts['fee_negotiation_step'] = '1' opts['close_initiated_by'] = 'opener' - opts['expected_close_fee'] = 20989 if not chainparams['elements'] else 26624 + opts['expected_close_fee'] = 20989 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) opts['close_initiated_by'] = 'peer' - opts['expected_close_fee'] = 20010 if not chainparams['elements'] else 25373 + opts['expected_close_fee'] = 20010 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) @unittest.skipIf(EXPERIMENTAL_FEATURES, "anchors uses quick-close, not negotiation") +@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Different closing fees") def test_closing_negotiation_step_700sat(node_factory, bitcoind, chainparams): """Test that the closing fee negotiation step works, 700sat""" opts = {} opts['fee_negotiation_step'] = '700' opts['close_initiated_by'] = 'opener' - opts['expected_close_fee'] = 20151 if not chainparams['elements'] else 25650 + opts['expected_close_fee'] = 20151 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) opts['close_initiated_by'] = 'peer' - opts['expected_close_fee'] = 20499 if not chainparams['elements'] else 25998 + opts['expected_close_fee'] = 20499 closing_negotiation_step(node_factory, bitcoind, chainparams, opts) @@ -3615,8 +3619,8 @@ def save_notifications(message, progress, request, **kwargs): l2_range = [1027, 1000000] else: # That fee output is a little chunky. - l1_range = [175, 5212] - l2_range = [1303, 1000000] + l1_range = [220, 6547] + l2_range = [1636, 1000000] l1.daemon.wait_for_log('Negotiating closing fee between {}sat and {}sat satoshi'.format(l1_range[0], l1_range[1])) l2.daemon.wait_for_log('Negotiating closing fee between {}sat and {}sat satoshi'.format(l2_range[0], l2_range[1])) diff --git a/tests/test_connection.py b/tests/test_connection.py index 3280ab27c693..055551557173 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3140,13 +3140,14 @@ def test_change_chaining(node_factory, bitcoind): l1.rpc.fundchannel(l3.info['id'], 10**7, minconf=0) +@unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Fees on elements are different") def test_feerate_spam(node_factory, chainparams): l1, l2 = node_factory.line_graph(2) # We constrain the value the opener has at its disposal so we get the # REMOTE feerate we are looking for below. This may be fragile and depends # on the transactions we generate. - slack = 45000000 if not chainparams['elements'] else 68000000 + slack = 45000000 # Pay almost everything to l2. l1.pay(l2, 10**9 - slack) From 39572c1aaf1fecd3ad8e8eedc705490d8b621999 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 21 Jan 2022 13:56:28 -0600 Subject: [PATCH 0253/1530] coin_moves: remove unused method This wasn't used anywhere, so we remove it. --- common/coin_mvt.c | 14 -------------- common/coin_mvt.h | 8 -------- 2 files changed, 22 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index e6e698a3ac92..68c23c9892c4 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -296,20 +296,6 @@ struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, amount, false); } -struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat amount) -{ - return new_chain_coin_mvt_sat(ctx, account_name, - txid, outpoint, NULL, - blockheight, - take(new_tag_arr(NULL, PENALTY)), - amount, false); -} - struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, const struct channel_id *cid, struct amount_msat amount, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 0f3b974755c2..350945c00e2d 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -227,14 +227,6 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, enum mvt_tag tag) NON_NULL_ARGS(2); -struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx, - const char *account_name, - const struct bitcoin_txid *txid, - const struct bitcoin_outpoint *outpoint, - u32 blockheight, - struct amount_sat amount) - NON_NULL_ARGS(3, 4); - struct channel_coin_mvt *new_coin_channel_push(const tal_t *ctx, const struct channel_id *cid, struct amount_msat amount, From 36ca175ec72e98dfe1a5eebce77d9660eb3df5aa Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 3 Jan 2022 12:48:00 -0600 Subject: [PATCH 0254/1530] wallet: was erroring out, saving to null field Add missing field to first write --- wallet/wallet.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index a08056d7bff9..4fe3740c0f0d 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -996,6 +996,7 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) ", funding_satoshi" ", our_funding_satoshi" ", funding_psbt" + ", funding_tx_remote_sigs_received" ", last_tx" ", last_sig" ", lease_commit_sig" @@ -1005,7 +1006,7 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) ", lease_blockheight_start" ", lease_fee" ") VALUES (" - "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, inflight->channel->dbid); db_bind_txid(stmt, 1, &inflight->funding->outpoint.txid); @@ -1014,23 +1015,24 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) db_bind_amount_sat(stmt, 4, &inflight->funding->total_funds); db_bind_amount_sat(stmt, 5, &inflight->funding->our_funds); db_bind_psbt(stmt, 6, inflight->funding_psbt); - db_bind_psbt(stmt, 7, inflight->last_tx->psbt); - db_bind_signature(stmt, 8, &inflight->last_sig.s); + db_bind_int(stmt, 7, inflight->remote_tx_sigs ? 1 : 0); + db_bind_psbt(stmt, 8, inflight->last_tx->psbt); + db_bind_signature(stmt, 9, &inflight->last_sig.s); if (inflight->lease_expiry != 0) { - db_bind_signature(stmt, 9, inflight->lease_commit_sig); - db_bind_int(stmt, 10, inflight->lease_chan_max_msat); - db_bind_int(stmt, 11, inflight->lease_chan_max_ppt); - db_bind_int(stmt, 12, inflight->lease_expiry); - db_bind_int(stmt, 13, inflight->lease_blockheight_start); - db_bind_amount_msat(stmt, 14, &inflight->lease_fee); + db_bind_signature(stmt, 10, inflight->lease_commit_sig); + db_bind_int(stmt, 11, inflight->lease_chan_max_msat); + db_bind_int(stmt, 12, inflight->lease_chan_max_ppt); + db_bind_int(stmt, 13, inflight->lease_expiry); + db_bind_int(stmt, 14, inflight->lease_blockheight_start); + db_bind_amount_msat(stmt, 15, &inflight->lease_fee); } else { - db_bind_null(stmt, 9); db_bind_null(stmt, 10); db_bind_null(stmt, 11); - db_bind_int(stmt, 12, 0); - db_bind_null(stmt, 13); + db_bind_null(stmt, 12); + db_bind_int(stmt, 13, 0); db_bind_null(stmt, 14); + db_bind_null(stmt, 15); } db_exec_prepared_v2(stmt); From 081ef05dd1c889f6dd94b5fedd66bf29e74d60c9 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 13 Jan 2022 16:35:29 -0600 Subject: [PATCH 0255/1530] listchannels: add funding_outnum to listchannels Changelog-Added: JSONRPC: `listchannels` now includes the `funding_outnum` --- doc/lightning-listpeers.7.md | 3 ++- doc/schemas/listpeers.schema.json | 8 ++++++++ lightningd/peer_control.c | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index f3ff7e96bd64..17a48d81f3ae 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -55,6 +55,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **short_channel_id** (short_channel_id, optional): The short_channel_id (once locked in) - **channel_id** (hex, optional): The full channel_id (always 64 characters) - **funding_txid** (txid, optional): ID of the funding transaction + - **funding_outnum** (u32, optional): The 0-based output number of the funding transaction which opens the channel - **initial_feerate** (string, optional): For inflight opens, the first feerate used to initiate the channel open - **last_feerate** (string, optional): For inflight opens, the most recent feerate used on the channel open - **next_feerate** (string, optional): For inflight opens, the next feerate we'll use for the channel open @@ -376,4 +377,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:956a13291bebc808bf1505a5d2030280aca441c5ca9991a6baae70c8715429a4) +[comment]: # ( SHA256STAMP:444080f602bbce7d86bc7c38a4a2b1186a31c63d6b747d92c5aa4711dd9118c1) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index 1a37fd2bc6b4..74a7f782e2bc 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -235,6 +235,10 @@ "type": "txid", "description": "ID of the funding transaction" }, + "funding_outnum": { + "type": "u32", + "description": "The 0-based output number of the funding transaction which opens the channel" + }, "initial_feerate": { "type": "string", "description": "For inflight opens, the first feerate used to initiate the channel open" @@ -741,6 +745,7 @@ "short_channel_id": {}, "channel_id": {}, "funding_txid": {}, + "funding_outnum": {}, "close_to": {}, "private": {}, "opener": {}, @@ -826,6 +831,7 @@ "short_channel_id": {}, "channel_id": {}, "funding_txid": {}, + "funding_outnum": {}, "inflight": {}, "close_to": {}, "private": {}, @@ -913,6 +919,7 @@ "short_channel_id": {}, "channel_id": {}, "funding_txid": {}, + "funding_outnum": {}, "inflight": {}, "close_to": {}, "private": {}, @@ -1000,6 +1007,7 @@ "short_channel_id": {}, "channel_id": {}, "funding_txid": {}, + "funding_outnum": {}, "close_to": {}, "private": {}, "opener": {}, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e87d0ae8bcc2..ca309f119c12 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -656,6 +656,7 @@ static void json_add_channel(struct lightningd *ld, json_add_string(response, "channel_id", type_to_string(tmpctx, struct channel_id, &channel->cid)); json_add_txid(response, "funding_txid", &channel->funding.txid); + json_add_num(response, "funding_outnum", channel->funding.n); if (!list_empty(&channel->inflights)) { struct channel_inflight *initial, *inflight; From 43f1fb45616f81e722a2c0b29778cf5d61de3678 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 21 Jan 2022 13:58:24 -0600 Subject: [PATCH 0256/1530] coin_moves: an htlc_fulfill claimed by them is a credit to "external" The money moved into an external account here. --- common/coin_mvt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 68c23c9892c4..1d77e9b9b02a 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -239,7 +239,7 @@ struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx, outpoint, payment_hash, blockheight, take(new_tag_arr(NULL, HTLC_FULFILL)), - amount, false); + amount, true); } struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, From 6349d89326f8804a24942e357cb43292aee1be7c Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 3 Jan 2022 12:47:46 -0600 Subject: [PATCH 0257/1530] coin_mvt: tiny, dont import lightningd header --- lightningd/coin_mvts.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/coin_mvts.h b/lightningd/coin_mvts.h index ac70f4096689..70ce9aa6c4d6 100644 --- a/lightningd/coin_mvts.h +++ b/lightningd/coin_mvts.h @@ -3,7 +3,8 @@ #include "config.h" #include -#include + +struct lightningd; struct account_balance { const char *acct_id; From 8eecd6c9f9daa7fa17b743e1e62a23d209d95da4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 12 Jan 2022 12:40:49 -0600 Subject: [PATCH 0258/1530] doc: turns out these fields are optional --- doc/PLUGINS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index c6e1af1ac0e1..e8196ec726ea 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -705,8 +705,8 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only, optional) "utxo_txid":"0159693d8f3876b4def468b208712c630309381e9d106a9836fa0a9571a28722", // (`chain_mvt` only) "vout":1, // (`chain_mvt` only) - "payment_hash": "xxx", // (either type, optional on `chain_mvt`) - "part_id": 0, // (`channel_mvt` only, mandatory) + "payment_hash": "xxx", // (either type, optional on both) + "part_id": 0, // (`channel_mvt` only, optional) "credit":"2000000000msat", "debit":"0msat", "output_value": "2000000000msat", // ('chain_mvt' only) From 9ebdb71ec0a4e7e10c372c90a323a085d866ab59 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 16 Dec 2021 13:07:33 -0600 Subject: [PATCH 0259/1530] lightningd/Make: fixup reference to wallet headers There is no "wallet_lib_headers" variable in wallet/Makefile --- lightningd/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightningd/Makefile b/lightningd/Makefile index c1cbf9d4db57..557bfb9c2285 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -132,9 +132,9 @@ LIGHTNINGD_COMMON_OBJS := \ include wallet/Makefile # All together in one convenient var -LIGHTNINGD_HEADERS = $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) $(WALLET_LIB_HEADERS) +LIGHTNINGD_HEADERS = $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) $(WALLET_HDRS) -$(LIGHTNINGD_OBJS): $(LIGHTNINGD_HEADERS) $(WALLET_HDRS) +$(LIGHTNINGD_OBJS): $(LIGHTNINGD_HEADERS) # Only the plugin component needs to depend on this header. lightningd/plugin.o: plugins/list_of_builtin_plugins_gen.h From 8309a049eb7e6c0b622dfc7f0100f65a4c08ba40 Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Fri, 4 Feb 2022 21:16:23 +0100 Subject: [PATCH 0260/1530] db: enable SQLite extended result codes With this change, we get more fine-grained error messages if something goes wrong in the course of communicating with the SQLite database. To pick some random examples, the error codes SQLITE_IOERR_NOMEM, SQLITE_IOERR_CORRUPTFS or SQLITE_IOERR_FSYNC are way more specific than just a plain SQLITE_IOERR, and the corresponding error messages generated by sqlite3_errstr() will hence give a better hint to the user (or also to the developers, if an error report is sent) what the cause for a failure is. Changelog-None --- wallet/db_sqlite3.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wallet/db_sqlite3.c b/wallet/db_sqlite3.c index 4459e6b2e45c..ad81bc20b1a2 100644 --- a/wallet/db_sqlite3.c +++ b/wallet/db_sqlite3.c @@ -146,6 +146,12 @@ static bool db_sqlite3_setup(struct db *db) } wrapper->conn = sql; + err = sqlite3_extended_result_codes(wrapper->conn, 1); + if (err != SQLITE_OK) { + db_fatal("failed to enable extended result codes: %s", + sqlite3_errstr(err)); + } + if (!backup_filename) wrapper->backup_conn = NULL; else { From c4f882b8758842b53c89436b3d3200d2a4bc4d16 Mon Sep 17 00:00:00 2001 From: Tim W Date: Fri, 4 Feb 2022 10:01:00 +0000 Subject: [PATCH 0261/1530] fix UnicodeDecodeError in doc/lightning-listfunds.7.md --- doc/lightning-listfunds.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index a2a5ec3a7749..d4abdaef48c5 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -37,7 +37,7 @@ On success, an object is returned, containing: - **reserved_to_block** (u32): Block height where reservation will expire - **channels** (array of objects): - **peer_id** (pubkey): the peer with which the channel is opened - - **our_amount_msat** (msat): available satoshis on our node’s end of the channel + - **our_amount_msat** (msat): available satoshis on our node's end of the channel - **amount_msat** (msat): total channel value - **funding_txid** (txid): funding transaction id - **funding_output** (u32): the 0-based index of the output in the funding transaction From 1087f9aadd96e1ffefe6961b3b3a251773f31d0c Mon Sep 17 00:00:00 2001 From: Tim W Date: Fri, 4 Feb 2022 14:29:54 +0000 Subject: [PATCH 0262/1530] fix UnicodeDecodeError in doc/schemas/listfunds.schema.json --- doc/lightning-listfunds.7.md | 2 +- doc/schemas/listfunds.schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index d4abdaef48c5..e049eeaa1938 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -67,4 +67,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7e2ee47b9e35c222ee8b671745990800feaba771cf60fbe8390c2afd040e878f) +[comment]: # ( SHA256STAMP:959d54ed855f9f5d64148f5acf4a0bbb4bc1503e610ddae5ab4c04fd397af0b3) diff --git a/doc/schemas/listfunds.schema.json b/doc/schemas/listfunds.schema.json index a5d5b875129a..e7a9c0af9a84 100644 --- a/doc/schemas/listfunds.schema.json +++ b/doc/schemas/listfunds.schema.json @@ -151,7 +151,7 @@ }, "our_amount_msat": { "type": "msat", - "description": "available satoshis on our node’s end of the channel" + "description": "available satoshis on our node's end of the channel" }, "channel_sat": { "deprecated": true From a8bed542f76987ef4384a996a7e7e042de482c17 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:27:52 +1030 Subject: [PATCH 0263/1530] lightningd: start gossipd after channels are loaded. This is in preparation for gossipd feeding us the latest channel_updates, rather than having lightningd and channeld query gossipd when it wants to send an onion error with an update included. This means gossipd will start telling us the updates, so we need the channels loaded first. Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index a73017835c2f..06da2389714d 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1027,11 +1027,6 @@ int main(int argc, char *argv[]) * socket pair, and gives us the other */ connectd_gossipd_fd = connectd_init(ld); - /*~ The gossip daemon looks after the routing gossip; - * channel_announcement, channel_update, node_announcement and gossip - * queries. */ - gossip_init(ld, connectd_gossipd_fd); - /*~ We do every database operation within a transaction; usually this * is covered by the infrastructure (eg. opening a transaction before * handling a message or expiring a timer), but for startup we do this @@ -1078,6 +1073,12 @@ int main(int argc, char *argv[]) unconnected_htlcs_in = load_channels_from_wallet(ld); db_commit_transaction(ld->wallet->db); + /*~ The gossip daemon looks after the routing gossip; + * channel_announcement, channel_update, node_announcement and gossip + * queries. It also hands us the latest channel_updates for our + * channels. */ + gossip_init(ld, connectd_gossipd_fd); + /*~ Create RPC socket: now lightning-cli can send us JSON RPC commands * over a UNIX domain socket specified by `ld->rpc_filename`. */ jsonrpc_listen(ld->jsonrpc, ld); From 51d23ffcd3a87a933e6e432b9b115360b63bc22c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:28:52 +1030 Subject: [PATCH 0264/1530] gossipd: infrastructure to tell lightningd about local channel updates. We want it to keep the latest, so it can make its own error msgs without asking us. This installs (but does not use!) the message handler. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 1 + gossipd/gossipd_wire.csv | 6 ++++++ lightningd/channel.c | 2 ++ lightningd/channel.h | 3 +++ lightningd/gossip_control.c | 31 +++++++++++++++++++++++++++++++ 5 files changed, 43 insertions(+) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index ac5580856c7c..fcde98ba1871 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1452,6 +1452,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: + case WIRE_GOSSIPD_GOT_LOCAL_CHANNEL_UPDATE: break; } diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index f0a6a9c898d1..6df53358a6c1 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -116,3 +116,9 @@ msgdata,gossipd_get_addrs,id,node_id, msgtype,gossipd_get_addrs_reply,3150 msgdata,gossipd_get_addrs_reply,num,u16, msgdata,gossipd_get_addrs_reply,addrs,wireaddr,num + +# Tell master a local channel update (so it can serve errors). +msgtype,gossipd_got_local_channel_update,3151 +msgdata,gossipd_got_local_channel_update,scid,short_channel_id, +msgdata,gossipd_got_local_channel_update,len,u16, +msgdata,gossipd_got_local_channel_update,channel_update,u8,len diff --git a/lightningd/channel.c b/lightningd/channel.c index 79eee3248933..10f388672ea6 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -253,6 +253,7 @@ struct channel *new_unsaved_channel(struct peer *peer, = CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE; channel->shutdown_wrong_funding = NULL; channel->closing_feerate_range = NULL; + channel->channel_update = NULL; /* Channel is connected! */ channel->connected = true; @@ -461,6 +462,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->lease_chan_max_msat = lease_chan_max_msat; channel->lease_chan_max_ppt = lease_chan_max_ppt; channel->blockheight_states = dup_height_states(channel, height_states); + channel->channel_update = NULL; list_add_tail(&peer->channels, &channel->list); channel->rr_number = peer->ld->rr_counter++; diff --git a/lightningd/channel.h b/lightningd/channel.h index 4f3fb1d28d50..729ac8599dd1 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -237,6 +237,9 @@ struct channel { u32 lease_chan_max_msat; /* Lease commited max part per thousandth channel fee (ppm * 1000) */ u16 lease_chan_max_ppt; + + /* Latest channel_update, for use in error messages. */ + u8 *channel_update; }; /* For v2 opens, a channel that has not yet been committed/saved to disk */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 9b82d9b793db..11b2463e537e 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -5,10 +5,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -107,6 +109,32 @@ static void get_txout(struct subd *gossip, const u8 *msg) } } +static void handle_local_channel_update(struct lightningd *ld, const u8 *msg) +{ + struct short_channel_id scid; + u8 *update; + struct channel *channel; + + if (!fromwire_gossipd_got_local_channel_update(msg, msg, + &scid, &update)) { + fatal("Gossip gave bad GOSSIP_GOT_LOCAL_CHANNEL_UPDATE %s", + tal_hex(msg, msg)); + } + + /* In theory this could vanish before gossipd gets around to telling + * us. */ + channel = any_channel_by_scid(ld, &scid); + if (!channel) { + log_broken(ld->log, "Local update for bad scid %s", + type_to_string(tmpctx, struct short_channel_id, + &scid)); + return; + } + + tal_free(channel->channel_update); + channel->channel_update = tal_steal(channel, update); +} + static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) { enum gossipd_wire t = fromwire_peektype(msg); @@ -144,6 +172,9 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_GET_TXOUT: get_txout(gossip, msg); break; + case WIRE_GOSSIPD_GOT_LOCAL_CHANNEL_UPDATE: + handle_local_channel_update(gossip->ld, msg); + break; } return 0; } From 430a380e35dd9d1c8cbecf330302d08eb39d1526 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:29:52 +1030 Subject: [PATCH 0265/1530] gossipd: feed lightningd the channel_updates as soon as we make them. Even if we're deferring putting them in the store and broadcasting them, we tell lightningd so it will use it in any error messages. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 7 +++++++ gossipd/test/run-check_node_announcement.c | 6 ++++++ gossipd/test/run-crc32_of_update.c | 6 ++++++ tests/test_gossip.py | 11 +++++++++-- tests/test_misc.py | 4 +++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 6ca0ff3e521e..22497709eb6c 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include @@ -418,6 +420,11 @@ static u8 *sign_and_timestamp_update(const tal_t *ctx, if (taken(unsigned_update)) tal_free(unsigned_update); + /* Tell lightningd about this immediately (even if we're not actually + * applying it now) */ + msg = towire_gossipd_got_local_channel_update(NULL, &chan->scid, update); + daemon_conn_send(daemon->master, take(msg)); + return update; } diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 95b2bdc1930e..ac5c80f27b89 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -24,6 +24,9 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } @@ -97,6 +100,9 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_got_local_channel_update */ +u8 *towire_gossipd_got_local_channel_update(const tal_t *ctx UNNEEDED, const struct short_channel_id *scid UNNEEDED, const u8 *channel_update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_got_local_channel_update called!\n"); abort(); } /* Generated stub for towire_hsmd_cupdate_sig_req */ u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED) { fprintf(stderr, "towire_hsmd_cupdate_sig_req called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 3b7595f16a84..9c0fef29d132 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -28,6 +28,9 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } /* Generated stub for daemon_conn_wake */ void daemon_conn_wake(struct daemon_conn *dc UNNEEDED) { fprintf(stderr, "daemon_conn_wake called!\n"); abort(); } @@ -133,6 +136,9 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_got_local_channel_update */ +u8 *towire_gossipd_got_local_channel_update(const tal_t *ctx UNNEEDED, const struct short_channel_id *scid UNNEEDED, const u8 *channel_update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_got_local_channel_update called!\n"); abort(); } /* Generated stub for towire_hsmd_cupdate_sig_req */ u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED) { fprintf(stderr, "towire_hsmd_cupdate_sig_req called!\n"); abort(); } diff --git a/tests/test_gossip.py b/tests/test_gossip.py index af6f9858d223..c0210f7f28ea 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1799,10 +1799,13 @@ def test_gossip_ratelimit(node_factory, bitcoind): canned gossip to the other partition consisting of l3. l3 should ratelimit the incoming gossip. + We get BROKEN logs because gossipd talks about non-existent channels to + lightningd ("**BROKEN** lightningd: Local update for bad scid 103x1x1"). """ l3, = node_factory.get_nodes( 1, - opts=[{'dev-gossip-time': 1568096251}] + opts=[{'dev-gossip-time': 1568096251, + 'allow_broken_log': True}] ) # Bump to block 102, so the following tx ends up in 103x1: @@ -1958,7 +1961,11 @@ def test_torport_onions(node_factory): @unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete gossip_store") def test_gossip_store_upgrade_v7_v8(node_factory): """Version 8 added feature bits to local channel announcements""" - l1 = node_factory.get_node(start=False) + + # We get BROKEN logs because gossipd talks about non-existent channels to + # lightningd ("**BROKEN** lightningd: Local update for bad scid 103x1x1"). + l1 = node_factory.get_node(start=False, + allow_broken_log=True) # A channel announcement with no channel_update. with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: diff --git a/tests/test_misc.py b/tests/test_misc.py index 3f13b1cadbff..8fb538c5a257 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1091,7 +1091,9 @@ def test_funding_reorg_private(node_factory, bitcoind): # Rescan to detect reorg at restart and may_reconnect so channeld # will restart. Reorg can cause bad gossip msg. opts = {'funding-confirms': 2, 'rescan': 10, 'may_reconnect': True, - 'allow_bad_gossip': True} + 'allow_bad_gossip': True, + # gossipd send lightning update for original channel. + 'allow_broken_log': True} l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=opts) l1.fundwallet(10000000) sync_blockheight(bitcoind, [l1]) # height 102 From c34639252b4386f6dbf321351cf6ee838225bd9c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:30:52 +1030 Subject: [PATCH 0266/1530] lightningd: tell gossipd when we use the channel_update. This way it can flush it if it was pending. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 20 ++++++++++++++++++++ gossipd/gossip_generation.h | 3 +++ gossipd/gossipd.c | 4 ++++ gossipd/gossipd_wire.csv | 4 ++++ gossipd/test/run-check_node_announcement.c | 6 ++++++ gossipd/test/run-crc32_of_update.c | 3 +++ gossipd/test/run-onion_message.c | 3 +++ lightningd/channel.h | 2 ++ lightningd/gossip_control.c | 12 ++++++++++++ 9 files changed, 57 insertions(+) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 22497709eb6c..2653f479a57b 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -811,6 +811,26 @@ void local_disable_chan(struct daemon *daemon, const struct chan *chan, int dire defer_update(daemon, 0xFFFFFFFF, chan, direction, take(update)); } +/* lightningd tells us it used the local channel update. */ +void handle_used_local_channel_update(struct daemon *daemon, const u8 *msg) +{ + struct short_channel_id scid; + struct chan *chan; + + if (!fromwire_gossipd_used_local_channel_update(msg, &scid)) + master_badmsg(WIRE_GOSSIPD_USED_LOCAL_CHANNEL_UPDATE, msg); + + chan = get_channel(daemon->rstate, &scid); + /* Might have closed in meantime, but v unlikely! */ + if (!chan) { + status_broken("used_local_channel_update on unknown %s", + type_to_string(tmpctx, struct short_channel_id, + &scid)); + return; + } + local_channel_update_latest(daemon, chan); +} + void local_enable_chan(struct daemon *daemon, const struct chan *chan, int direction) { struct deferred_update *du; diff --git a/gossipd/gossip_generation.h b/gossipd/gossip_generation.h index 2e7b396f547f..adc3f81db51d 100644 --- a/gossipd/gossip_generation.h +++ b/gossipd/gossip_generation.h @@ -53,4 +53,7 @@ bool handle_local_channel_update(struct daemon *daemon, const struct node_id *src, const u8 *msg); +/* lightningd tells us it used the last channel_update we sent. */ +void handle_used_local_channel_update(struct daemon *daemon, const u8 *msg); + #endif /* LIGHTNING_GOSSIPD_GOSSIP_GENERATION_H */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index fcde98ba1871..06ff2deb489f 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1413,6 +1413,10 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_GET_ADDRS: return handle_get_address(conn, daemon, msg); + case WIRE_GOSSIPD_USED_LOCAL_CHANNEL_UPDATE: + handle_used_local_channel_update(daemon, msg); + goto done; + #if DEVELOPER case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: dev_set_max_scids_encode_size(daemon, msg); diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 6df53358a6c1..96d42fd1bba5 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -122,3 +122,7 @@ msgtype,gossipd_got_local_channel_update,3151 msgdata,gossipd_got_local_channel_update,scid,short_channel_id, msgdata,gossipd_got_local_channel_update,len,u16, msgdata,gossipd_got_local_channel_update,channel_update,u8,len + +# Tell gossipd we used the channel update (in case it was deferred) +msgtype,gossipd_used_local_channel_update,3052 +msgdata,gossipd_used_local_channel_update,scid,short_channel_id, diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index ac5c80f27b89..4104556fade7 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -39,6 +39,9 @@ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr /* Generated stub for fromwire_gossipd_local_channel_update */ bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_used_local_channel_update */ +bool fromwire_gossipd_used_local_channel_update(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_used_local_channel_update called!\n"); abort(); } /* Generated stub for fromwire_hsmd_cupdate_sig_reply */ bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **cu UNNEEDED) { fprintf(stderr, "fromwire_hsmd_cupdate_sig_reply called!\n"); abort(); } @@ -78,6 +81,9 @@ void json_object_end(struct json_stream *js UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for master_badmsg */ +void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) +{ fprintf(stderr, "master_badmsg called!\n"); abort(); } /* Generated stub for new_onionreply */ struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) { fprintf(stderr, "new_onionreply called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 9c0fef29d132..7d9b64037473 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -60,6 +60,9 @@ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 /* Generated stub for fromwire_gossipd_local_channel_update */ bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_used_local_channel_update */ +bool fromwire_gossipd_used_local_channel_update(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_used_local_channel_update called!\n"); abort(); } /* Generated stub for fromwire_hsmd_cupdate_sig_reply */ bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **cu UNNEEDED) { fprintf(stderr, "fromwire_hsmd_cupdate_sig_reply called!\n"); abort(); } diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 66bad4d6941b..13b610fe104c 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -183,6 +183,9 @@ const u8 *handle_reply_channel_range(struct peer *peer UNNEEDED, const u8 *msg U /* Generated stub for handle_reply_short_channel_ids_end */ const u8 *handle_reply_short_channel_ids_end(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "handle_reply_short_channel_ids_end called!\n"); abort(); } +/* Generated stub for handle_used_local_channel_update */ +void handle_used_local_channel_update(struct daemon *daemon UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_used_local_channel_update called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, diff --git a/lightningd/channel.h b/lightningd/channel.h index 729ac8599dd1..091346f2cc2e 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -474,4 +474,6 @@ void channel_set_billboard(struct channel *channel, bool perm, struct htlc_in *channel_has_htlc_in(struct channel *channel); struct htlc_out *channel_has_htlc_out(struct channel *channel); +const u8 *get_channel_update(struct channel *channel); + #endif /* LIGHTNING_LIGHTNINGD_CHANNEL_H */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 11b2463e537e..090f4ad41858 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -135,6 +135,17 @@ static void handle_local_channel_update(struct lightningd *ld, const u8 *msg) channel->channel_update = tal_steal(channel, update); } +const u8 *get_channel_update(struct channel *channel) +{ + /* Tell gossipd we're using it (if shutting down, might be NULL) */ + if (channel->channel_update && channel->peer->ld->gossip) { + subd_send_msg(channel->peer->ld->gossip, + take(towire_gossipd_used_local_channel_update + (NULL, channel->scid))); + } + return channel->channel_update; +} + static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) { enum gossipd_wire t = fromwire_peektype(msg); @@ -156,6 +167,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: case WIRE_GOSSIPD_GET_ADDRS: + case WIRE_GOSSIPD_USED_LOCAL_CHANNEL_UPDATE: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: From 5065bd6fc219fd3e628f0172c924269e0d7580ef Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:31:52 +1030 Subject: [PATCH 0267/1530] lightningd: use our cached channel_update for errors instead of asking gossipd. We also no longer strip the type off: everyone handles both forms, and Eclair doesn't strip (and it's easier!). Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 55 ---------- gossipd/gossipd_wire.csv | 8 -- gossipd/test/run-onion_message.c | 6 -- lightningd/gossip_control.c | 2 - lightningd/lightningd.c | 7 +- lightningd/pay.c | 12 +-- lightningd/pay.h | 5 +- lightningd/peer_htlcs.c | 167 +++++++------------------------ lightningd/peer_htlcs.h | 5 +- tests/test_pay.py | 6 +- wallet/test/run-wallet.c | 11 +- 11 files changed, 55 insertions(+), 229 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 06ff2deb489f..6e12096124ae 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1177,56 +1177,6 @@ static void dev_gossip_set_time(struct daemon *daemon, const u8 *msg) } #endif /* DEVELOPER */ -/*~ lightningd: so, get me the latest update for this local channel, - * so I can include it in an error message. */ -static void get_stripped_cupdate(struct daemon *daemon, const u8 *msg) -{ - struct short_channel_id scid; - struct chan *chan; - const u8 *stripped_update; - - if (!fromwire_gossipd_get_stripped_cupdate(msg, &scid)) - master_badmsg(WIRE_GOSSIPD_GET_STRIPPED_CUPDATE, msg); - - chan = get_channel(daemon->rstate, &scid); - if (!chan) { - status_debug("Failed to resolve local channel %s", - type_to_string(tmpctx, struct short_channel_id, &scid)); - stripped_update = NULL; - } else { - int direction; - const struct half_chan *hc; - - if (!local_direction(daemon->rstate, chan, &direction)) { - status_broken("%s is a non-local channel!", - type_to_string(tmpctx, - struct short_channel_id, - &scid)); - stripped_update = NULL; - goto out; - } - - /* Since we're going to use it, make sure it's up-to-date. */ - local_channel_update_latest(daemon, chan); - - hc = &chan->half[direction]; - if (is_halfchan_defined(hc)) { - const u8 *update; - - update = gossip_store_get(tmpctx, daemon->rstate->gs, - hc->bcast.index); - stripped_update = tal_dup_arr(tmpctx, u8, update + 2, - tal_count(update) - 2, 0); - } else - stripped_update = NULL; - } - -out: - daemon_conn_send(daemon->master, - take(towire_gossipd_get_stripped_cupdate_reply(NULL, - stripped_update))); -} - /*~ We queue incoming channel_announcement pending confirmation from lightningd * that it really is an unspent output. Here's its reply. */ static void handle_txout_reply(struct daemon *daemon, const u8 *msg) @@ -1382,10 +1332,6 @@ static struct io_plan *recv_req(struct io_conn *conn, gossip_init(daemon, msg); goto done; - case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: - get_stripped_cupdate(daemon, msg); - goto done; - case WIRE_GOSSIPD_GET_TXOUT_REPLY: handle_txout_reply(daemon, msg); goto done; @@ -1448,7 +1394,6 @@ static struct io_plan *recv_req(struct io_conn *conn, /* We send these, we don't receive them */ case WIRE_GOSSIPD_INIT_REPLY: - case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 96d42fd1bba5..7dba19620276 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -27,14 +27,6 @@ msgdata,gossipd_dev_set_time,dev_gossip_time,u32, msgtype,gossipd_dev_set_max_scids_encode_size,3030 msgdata,gossipd_dev_set_max_scids_encode_size,max,u32, -# Given a short_channel_id, return the latest (stripped) update for error msg. -msgtype,gossipd_get_stripped_cupdate,3010 -msgdata,gossipd_get_stripped_cupdate,channel_id,short_channel_id, - -msgtype,gossipd_get_stripped_cupdate_reply,3110 -msgdata,gossipd_get_stripped_cupdate_reply,stripped_update_len,u16, -msgdata,gossipd_get_stripped_cupdate_reply,stripped_update,u8,stripped_update_len - # gossipd->master: we're closing this channel. msgtype,gossipd_local_channel_close,3027 msgdata,gossipd_local_channel_close,short_channel_id,short_channel_id, diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 13b610fe104c..5ccc425f6f28 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -85,9 +85,6 @@ bool fromwire_gossipd_dev_suppress(const void *p UNNEEDED) /* Generated stub for fromwire_gossipd_get_addrs */ bool fromwire_gossipd_get_addrs(const void *p UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_gossipd_get_addrs called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_get_stripped_cupdate */ -bool fromwire_gossipd_get_stripped_cupdate(const void *p UNNEEDED, struct short_channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_get_stripped_cupdate called!\n"); abort(); } /* Generated stub for fromwire_gossipd_get_txout_reply */ bool fromwire_gossipd_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct amount_sat *satoshis UNNEEDED, u8 **outscript UNNEEDED) { fprintf(stderr, "fromwire_gossipd_get_txout_reply called!\n"); abort(); } @@ -323,9 +320,6 @@ u8 *towire_gossipd_dev_memleak_reply(const tal_t *ctx UNNEEDED, bool leak UNNEED /* Generated stub for towire_gossipd_get_addrs_reply */ u8 *towire_gossipd_get_addrs_reply(const tal_t *ctx UNNEEDED, const struct wireaddr *addrs UNNEEDED) { fprintf(stderr, "towire_gossipd_get_addrs_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_get_stripped_cupdate_reply */ -u8 *towire_gossipd_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const u8 *stripped_update UNNEEDED) -{ fprintf(stderr, "towire_gossipd_get_stripped_cupdate_reply called!\n"); abort(); } /* Generated stub for towire_gossipd_get_txout */ u8 *towire_gossipd_get_txout(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) { fprintf(stderr, "towire_gossipd_get_txout called!\n"); abort(); } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 090f4ad41858..5859fd675f85 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -153,7 +153,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) switch (t) { /* These are messages we send, not them. */ case WIRE_GOSSIPD_INIT: - case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: case WIRE_GOSSIPD_GET_TXOUT_REPLY: case WIRE_GOSSIPD_OUTPOINT_SPENT: case WIRE_GOSSIPD_NEW_LEASE_RATES: @@ -172,7 +171,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: - case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE_REPLY: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 06da2389714d..cedc20e77d8d 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -526,6 +526,8 @@ static void shutdown_subdaemons(struct lightningd *ld) /*~ The three "global" daemons, which we shutdown explicitly: we * give them 10 seconds to exit gracefully before killing them. */ ld->connectd = subd_shutdown(ld->connectd, 10); + ld->gossip = subd_shutdown(ld->gossip, 10); + ld->hsm = subd_shutdown(ld->hsm, 10); /* Now we free all the HTLCs */ free_htlcs(ld, NULL); @@ -558,11 +560,6 @@ static void shutdown_subdaemons(struct lightningd *ld) tal_free(p); } - /*~ Now they're all dead, we can stop gossipd: doing it before HTLCs is - * problematic because local_fail_in_htlc_needs_update() asks gossipd */ - ld->gossip = subd_shutdown(ld->gossip, 10); - ld->hsm = subd_shutdown(ld->hsm, 10); - /*~ Commit the transaction. Note that the db is actually * single-threaded, so commits never fail and we don't need * spin-and-retry logic everywhere. */ diff --git a/lightningd/pay.c b/lightningd/pay.c index 346cc8ecca3f..6889945c50bb 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -533,7 +533,7 @@ void payment_store(struct lightningd *ld, struct wallet_payment *payment TAKES) } void payment_failed(struct lightningd *ld, const struct htlc_out *hout, - const char *localfail, const u8 *failmsg_needs_update) + const char *localfail) { struct wallet_payment *payment; struct routing_failure* fail = NULL; @@ -566,10 +566,7 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, if (localfail) { /* Use temporary_channel_failure if failmsg has it */ enum onion_wire failcode; - if (failmsg_needs_update) - failcode = fromwire_peektype(failmsg_needs_update); - else - failcode = fromwire_peektype(hout->failmsg); + failcode = fromwire_peektype(hout->failmsg); fail = local_routing_failure(tmpctx, ld, hout, failcode, payment); @@ -780,14 +777,13 @@ static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld, { const u8 *onion; unsigned int base_expiry; - bool dont_care_about_channel_update; + base_expiry = get_block_height(ld->topology) + 1; onion = serialize_onionpacket(tmpctx, packet); return send_htlc_out(ctx, channel, first_hop->amount, base_expiry + first_hop->delay, final_amount, payment_hash, - blinding, partid, groupid, onion, NULL, hout, - &dont_care_about_channel_update); + blinding, partid, groupid, onion, NULL, hout); } static struct command_result *check_offer_usage(struct command *cmd, diff --git a/lightningd/pay.h b/lightningd/pay.h index 1aace68bbc99..7c933877418d 100644 --- a/lightningd/pay.h +++ b/lightningd/pay.h @@ -15,10 +15,9 @@ struct routing_failure; void payment_succeeded(struct lightningd *ld, struct htlc_out *hout, const struct preimage *rval); -/* failmsg_needs_update is if we actually wanted to temporary_channel_failure - * but we haven't got the update msg yet */ +/* hout->failmsg or hout->failonion must be set. */ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, - const char *localfail, const u8 *failmsg_needs_update); + const char *localfail); /* Inform payment system to save the payment. */ void payment_store(struct lightningd *ld, struct wallet_payment *payment); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 8c60ed4e3219..20932c567433 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -138,58 +138,6 @@ static void tell_channeld_htlc_failed(const struct htlc_in *hin, take(towire_channeld_fail_htlc(NULL, failed_htlc))); } -struct failmsg_update_cbdata { - struct htlc_in *hin; - const u8 *failmsg_needs_update; -}; - -static void failmsg_update_reply(struct subd *gossipd, - const u8 *msg, - const int *unused, - struct failmsg_update_cbdata *cbdata) -{ - u8 *failmsg; - u8 *stripped_update; - struct failed_htlc *failed_htlc; - - /* This can happen because channel never got properly announced.*/ - if (!fromwire_gossipd_get_stripped_cupdate_reply(msg, msg, - &stripped_update) - || !tal_count(stripped_update)) { - failmsg = towire_temporary_node_failure(NULL); - } else { - /* End of failmsg is two zero bytes (empty update). */ - assert(tal_count(cbdata->failmsg_needs_update) >= 2); - failmsg = tal_dup_arr(msg, u8, - cbdata->failmsg_needs_update, - tal_count(cbdata->failmsg_needs_update)-2, - 0); - towire_u16(&failmsg, tal_count(stripped_update)); - towire_u8_array(&failmsg, - stripped_update, tal_count(stripped_update)); - } - - /* Now we replace dummy failonion with this real one */ - tal_free(cbdata->hin->failonion); - cbdata->hin->failonion - = create_onionreply(cbdata->hin, - cbdata->hin->shared_secret, - failmsg); - - bool we_filled = false; - wallet_htlc_update(gossipd->ld->wallet, - cbdata->hin->dbid, cbdata->hin->hstate, - cbdata->hin->preimage, - max_unsigned(cbdata->hin->key.channel->next_index[LOCAL], - cbdata->hin->key.channel->next_index[REMOTE]), - cbdata->hin->badonion, - cbdata->hin->failonion, NULL, &we_filled); - - failed_htlc = mk_failed_htlc(tmpctx, - cbdata->hin, cbdata->hin->failonion); - tell_channeld_htlc_failed(cbdata->hin, failed_htlc); -} - static void fail_in_htlc(struct htlc_in *hin, const struct onionreply *failonion TAKES) { @@ -211,6 +159,15 @@ static void fail_in_htlc(struct htlc_in *hin, #endif failed_htlc = mk_failed_htlc(tmpctx, hin, hin->failonion); + bool we_filled = false; + wallet_htlc_update(hin->key.channel->peer->ld->wallet, + hin->dbid, hin->hstate, + hin->preimage, + max_unsigned(hin->key.channel->next_index[LOCAL], + hin->key.channel->next_index[REMOTE]), + hin->badonion, + hin->failonion, NULL, &we_filled); + tell_channeld_htlc_failed(hin, failed_htlc); } @@ -244,34 +201,6 @@ void local_fail_in_htlc(struct htlc_in *hin, const u8 *failmsg TAKES) fail_in_htlc(hin, take(failonion)); } -/* This is used for cases where we can immediately fail the HTLC, but - * need to append a channel_update. */ -void local_fail_in_htlc_needs_update(struct htlc_in *hin, - const u8 *failmsg_needs_update TAKES, - const struct short_channel_id *failmsg_scid) -{ - struct failmsg_update_cbdata *cbdata; - - /* To avoid the state where we have no failonion, we use a temporary - * one, and update once we get the reply from gossipd. */ - assert(!hin->preimage); - - hin->failonion = create_onionreply(hin, - hin->shared_secret, - towire_temporary_node_failure(tmpctx)); - /* We update state now to signal it's in progress, for persistence. */ - htlc_in_update_state(hin->key.channel, hin, SENT_REMOVE_HTLC); - htlc_in_check(hin, __func__); - - cbdata = tal(hin, struct failmsg_update_cbdata); - cbdata->hin = hin; - cbdata->failmsg_needs_update - = tal_dup_talarr(cbdata, u8, failmsg_needs_update); - subd_req(cbdata, hin->key.channel->peer->ld->gossip, - take(towire_gossipd_get_stripped_cupdate(NULL, failmsg_scid)), - -1, 0, failmsg_update_reply, cbdata); -} - /* Helper to create (common) WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS */ const u8 *failmsg_incorrect_or_unknown_(const tal_t *ctx, struct lightningd *ld, @@ -286,39 +215,27 @@ const u8 *failmsg_incorrect_or_unknown_(const tal_t *ctx, } /* localfail are for handing to the local payer if it's local. */ -static void fail_out_htlc(struct htlc_out *hout, - const char *localfail, - const u8 *failmsg_needs_update TAKES) +static void fail_out_htlc(struct htlc_out *hout, const char *localfail) { htlc_out_check(hout, __func__); assert(hout->failmsg || hout->failonion); if (hout->am_origin) { - payment_failed(hout->key.channel->peer->ld, hout, localfail, - failmsg_needs_update); - if (taken(failmsg_needs_update)) - tal_free(failmsg_needs_update); + payment_failed(hout->key.channel->peer->ld, hout, localfail); } else if (hout->in) { - if (failmsg_needs_update) { - local_fail_in_htlc_needs_update(hout->in, - failmsg_needs_update, - hout->key.channel->scid); - } else { - const struct onionreply *failonion; + const struct onionreply *failonion; - /* If we have an onion, simply copy it. */ - if (hout->failonion) - failonion = hout->failonion; - /* Otherwise, we need to onionize this local error. */ - else - failonion = create_onionreply(hout, - hout->in->shared_secret, - hout->failmsg); - fail_in_htlc(hout->in, failonion); - } + /* If we have an onion, simply copy it. */ + if (hout->failonion) + failonion = hout->failonion; + /* Otherwise, we need to onionize this local error. */ + else + failonion = create_onionreply(hout, + hout->in->shared_secret, + hout->failmsg); + fail_in_htlc(hout->in, failonion); } else { - if (taken(failmsg_needs_update)) - tal_free(failmsg_needs_update); + log_broken(hout->key.channel->log, "Neither origin nor in?"); } } @@ -530,8 +447,8 @@ static void destroy_hout_subd_died(struct htlc_out *hout) "Failing HTLC %"PRIu64" due to peer death", hout->key.id); - /* This isn't really used, except as sanity check */ - hout->failmsg = towire_temporary_node_failure(hout); + hout->failmsg = towire_temporary_channel_failure(hout, + get_channel_update(hout->key.channel)); /* Assign a temporary state (we're about to free it!) so checks * are happy that it has a failure message */ @@ -541,8 +458,7 @@ static void destroy_hout_subd_died(struct htlc_out *hout) if (!have_tx) db_begin_transaction(db); - fail_out_htlc(hout, "Outgoing subdaemon died", - take(towire_temporary_channel_failure(NULL, NULL))); + fail_out_htlc(hout, "Outgoing subdaemon died"); if (!have_tx) db_commit_transaction(db); @@ -573,7 +489,7 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds UNU char *localfail = tal_fmt(msg, "%s: %s", onion_wire_name(fromwire_peektype(failmsg)), failurestr); - payment_failed(ld, hout, localfail, NULL); + payment_failed(ld, hout, localfail); } else if (hout->in) { struct onionreply *failonion; @@ -645,13 +561,11 @@ const u8 *send_htlc_out(const tal_t *ctx, u64 groupid, const u8 *onion_routing_packet, struct htlc_in *in, - struct htlc_out **houtp, - bool *needs_update_appended) + struct htlc_out **houtp) { u8 *msg; *houtp = NULL; - *needs_update_appended = false; if (!channel_can_add_htlc(out)) { log_info(out->log, "Attempt to send HTLC but not ready (%s)", @@ -662,8 +576,8 @@ const u8 *send_htlc_out(const tal_t *ctx, if (!out->owner) { log_info(out->log, "Attempt to send HTLC but unowned (%s)", channel_state_name(out)); - *needs_update_appended = true; - return towire_temporary_channel_failure(ctx, NULL); + return towire_temporary_channel_failure(ctx, + get_channel_update(out)); } if (!topology_synced(out->peer->ld->topology)) { @@ -708,7 +622,6 @@ static void forward_htlc(struct htlc_in *hin, struct lightningd *ld = hin->key.channel->peer->ld; struct channel *next = active_channel_by_scid(ld, scid); struct htlc_out *hout = NULL; - bool needs_update_appended; /* Unknown peer, or peer not ready. */ if (!next || !next->scid) { @@ -734,9 +647,8 @@ static void forward_htlc(struct htlc_in *hin, || !check_fwd_amount(hin, amt_to_forward, hin->msat, next->old_feerate_base, next->old_feerate_ppm)) { - needs_update_appended = true; failmsg = towire_fee_insufficient(tmpctx, hin->msat, - NULL); + get_channel_update(next)); goto fail; } log_info(hin->key.channel->log, @@ -745,9 +657,8 @@ static void forward_htlc(struct htlc_in *hin, if (!check_cltv(hin, cltv_expiry, outgoing_cltv_value, ld->config.cltv_expiry_delta)) { - needs_update_appended = true; failmsg = towire_incorrect_cltv_expiry(tmpctx, cltv_expiry, - NULL); + get_channel_update(next)); goto fail; } @@ -765,8 +676,8 @@ static void forward_htlc(struct htlc_in *hin, "Expiry cltv %u too close to current %u", outgoing_cltv_value, get_block_height(ld->topology)); - needs_update_appended = true; - failmsg = towire_expiry_too_soon(tmpctx, NULL); + failmsg = towire_expiry_too_soon(tmpctx, + get_channel_update(next)); goto fail; } @@ -782,7 +693,6 @@ static void forward_htlc(struct htlc_in *hin, outgoing_cltv_value, get_block_height(ld->topology), ld->config.locktime_max); - needs_update_appended = false; failmsg = towire_expiry_too_far(tmpctx); goto fail; } @@ -791,15 +701,12 @@ static void forward_htlc(struct htlc_in *hin, outgoing_cltv_value, AMOUNT_MSAT(0), &hin->payment_hash, next_blinding, 0 /* partid */, 0 /* groupid */, - next_onion, hin, &hout, &needs_update_appended); + next_onion, hin, &hout); if (!failmsg) return; fail: - if (needs_update_appended) - local_fail_in_htlc_needs_update(hin, failmsg, next->scid); - else - local_fail_in_htlc(hin, failmsg); + local_fail_in_htlc(hin, failmsg); wallet_forwarded_payment_add(ld->wallet, hin, next->scid, hout, FORWARD_LOCAL_FAILED, @@ -1497,7 +1404,7 @@ void onchain_failed_our_htlc(const struct channel *channel, char *localfail = tal_fmt(channel, "%s: %s", onion_wire_name(WIRE_PERMANENT_CHANNEL_FAILURE), why); - payment_failed(ld, hout, localfail, NULL); + payment_failed(ld, hout, localfail); tal_free(localfail); } else if (hout->in) { local_fail_in_htlc(hout->in, @@ -1575,7 +1482,7 @@ static void remove_htlc_out(struct channel *channel, struct htlc_out *hout) /* If it's failed, now we can forward since it's completely locked-in */ if (!hout->preimage) { - fail_out_htlc(hout, NULL, NULL); + fail_out_htlc(hout, NULL); } else { const struct channel_coin_mvt *mvt; struct amount_msat oldamt = channel->our_msat; diff --git a/lightningd/peer_htlcs.h b/lightningd/peer_htlcs.h index d6c49983e613..3cdf6476ecdf 100644 --- a/lightningd/peer_htlcs.h +++ b/lightningd/peer_htlcs.h @@ -39,7 +39,7 @@ void peer_got_revoke(struct channel *channel, const u8 *msg); void update_per_commit_point(struct channel *channel, const struct pubkey *per_commitment_point); -/* Returns NULL on success, otherwise failmsg (and sets *needs_update_appended)*/ +/* Returns NULL on success, otherwise failmsg*/ const u8 *send_htlc_out(const tal_t *ctx, struct channel *out, struct amount_msat amount, u32 cltv, @@ -50,8 +50,7 @@ const u8 *send_htlc_out(const tal_t *ctx, u64 groupid, const u8 *onion_routing_packet, struct htlc_in *in, - struct htlc_out **houtp, - bool *needs_update_appended); + struct htlc_out **houtp); void onchain_failed_our_htlc(const struct channel *channel, const struct htlc_stub *htlc, diff --git a/tests/test_pay.py b/tests/test_pay.py index 7dc898b452b9..a5a68b4ae954 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -307,7 +307,7 @@ def test_pay_get_error_with_update(node_factory): # channel_update, and it should patch it to include a type prefix. The # prefix 0x0102 should be in the channel_update, but not in the # onionreply (negation of 0x0102 in the RE) - l1.daemon.wait_for_log(r'Extracted channel_update 0102.*from onionreply 10070088[0-9a-fA-F]{88}') + l1.daemon.wait_for_log(r'Extracted channel_update 0102.*from onionreply 1007008a[0-9a-fA-F]{276}$') # And now monitor for l1 to apply the channel_update we just extracted wait_for(lambda: not l1.is_channel_active(chanid2)) @@ -1748,7 +1748,9 @@ def listpays_nofail(b11): def test_pay_routeboost(node_factory, bitcoind, compat): """Make sure we can use routeboost information. """ # l1->l2->l3--private-->l4 - l1, l2 = node_factory.line_graph(2, announce_channels=True, wait_for_announce=True) + # Note: l1 gets upset because it extracts update for private channel. + l1, l2 = node_factory.line_graph(2, announce_channels=True, wait_for_announce=True, + opts=[{'allow_bad_gossip': True}, {}]) l3, l4, l5 = node_factory.line_graph(3, announce_channels=False, wait_for_announce=False) # This should a "could not find a route" because that's true. diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 695998e30b1c..1ca2fd25de15 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -131,9 +131,6 @@ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p U /* Generated stub for fromwire_custommsg_in */ bool fromwire_custommsg_in(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **msg UNNEEDED) { fprintf(stderr, "fromwire_custommsg_in called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_get_stripped_cupdate_reply */ -bool fromwire_gossipd_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **stripped_update UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_get_stripped_cupdate_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED) { fprintf(stderr, "fromwire_hsmd_get_output_scriptpubkey_reply called!\n"); abort(); } @@ -149,6 +146,9 @@ bool fromwire_onchaind_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNE /* Generated stub for get_block_height */ u32 get_block_height(const struct chain_topology *topo UNNEEDED) { fprintf(stderr, "get_block_height called!\n"); abort(); } +/* Generated stub for get_channel_update */ +const u8 *get_channel_update(struct channel *channel UNNEEDED) +{ fprintf(stderr, "get_channel_update called!\n"); abort(); } /* Generated stub for htlc_is_trimmed */ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED, struct amount_msat htlc_amount UNNEEDED, @@ -585,7 +585,7 @@ struct onionpacket *parse_onionpacket(const tal_t *ctx UNNEEDED, { fprintf(stderr, "parse_onionpacket called!\n"); abort(); } /* Generated stub for payment_failed */ void payment_failed(struct lightningd *ld UNNEEDED, const struct htlc_out *hout UNNEEDED, - const char *localfail UNNEEDED, const u8 *failmsg_needs_update UNNEEDED) + const char *localfail UNNEEDED) { fprintf(stderr, "payment_failed called!\n"); abort(); } /* Generated stub for payment_store */ void payment_store(struct lightningd *ld UNNEEDED, struct wallet_payment *payment UNNEEDED) @@ -736,9 +736,6 @@ u8 *towire_final_incorrect_cltv_expiry(const tal_t *ctx UNNEEDED, u32 cltv_expir /* Generated stub for towire_final_incorrect_htlc_amount */ u8 *towire_final_incorrect_htlc_amount(const tal_t *ctx UNNEEDED, struct amount_msat incoming_htlc_amt UNNEEDED) { fprintf(stderr, "towire_final_incorrect_htlc_amount called!\n"); abort(); } -/* Generated stub for towire_gossipd_get_stripped_cupdate */ -u8 *towire_gossipd_get_stripped_cupdate(const tal_t *ctx UNNEEDED, const struct short_channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "towire_gossipd_get_stripped_cupdate called!\n"); abort(); } /* Generated stub for towire_hsmd_get_output_scriptpubkey */ u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED) { fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); } From e8554c862a40b1b90b3db9f7c01626cf5fa467f0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:32:52 +1030 Subject: [PATCH 0268/1530] channeld: keep local copy of latest channel_update for errors. Now we don't ask gossipd, but lightningd keeps channeld up-to-date. Signed-off-by: Rusty Russell --- channeld/channeld.c | 59 ++++++++++++++-------------- channeld/channeld_wire.csv | 10 +++++ gossipd/gossip_generation.c | 2 +- gossipd/gossip_generation.h | 3 -- gossipd/gossipd.c | 66 -------------------------------- gossipd/gossipd_peerd_wire.csv | 10 ----- gossipd/test/run-onion_message.c | 9 ----- lightningd/channel_control.c | 22 ++++++++++- lightningd/channel_control.h | 4 ++ lightningd/gossip_control.c | 4 +- 10 files changed, 69 insertions(+), 120 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index a8d0aaaa0b76..0c272791529a 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -191,6 +191,9 @@ struct peer { /* We allow a 'tx-sigs' message between reconnect + funding_locked */ bool tx_sigs_allowed; + + /* Most recent channel_update message. */ + u8 *channel_update; }; static u8 *create_channel_announcement(const tal_t *ctx, struct peer *peer); @@ -414,28 +417,6 @@ static void send_channel_update(struct peer *peer, int disable_flag) wire_sync_write(peer->pps->gossip_fd, take(msg)); } -/* Get the latest channel update for this channel from gossipd */ -static const u8 *get_local_channel_update(const tal_t *ctx, struct peer *peer) -{ - const u8 *msg; - - msg = towire_gossipd_get_update(NULL, &peer->short_channel_ids[LOCAL]); - wire_sync_write(peer->pps->gossip_fd, take(msg)); - - /* Wait for reply to come back; handle other gossipd msgs meanwhile */ - while ((msg = wire_sync_read(tmpctx, peer->pps->gossip_fd)) != NULL) { - u8 *update; - if (fromwire_gossipd_get_update_reply(ctx, msg, &update)) - return update; - - handle_gossip_msg(peer->pps, take(msg)); - } - - /* Gossipd hangs up on us to kill us when a new - * connection comes in. */ - peer_failed_connection_lost(); -} - /** * Add a channel locally and send a channel update to the peer * @@ -3312,6 +3293,15 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) billboard_update(peer); } +static const u8 *get_cupdate(const struct peer *peer) +{ + /* Technically we only need to tell it the first time (unless it's + * changed). But it's not that common. */ + wire_sync_write(MASTER_FD, + take(towire_channeld_used_channel_update(NULL))); + return peer->channel_update; +} + static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) { u8 *msg; @@ -3373,7 +3363,7 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) peer->htlc_id++; return; case CHANNEL_ERR_INVALID_EXPIRY: - failwiremsg = towire_incorrect_cltv_expiry(inmsg, cltv_expiry, get_local_channel_update(tmpctx, peer)); + failwiremsg = towire_incorrect_cltv_expiry(inmsg, cltv_expiry, get_cupdate(peer)); failstr = tal_fmt(inmsg, "Invalid cltv_expiry %u", cltv_expiry); goto failed; case CHANNEL_ERR_DUPLICATE: @@ -3387,18 +3377,18 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) goto failed; /* FIXME: Fuzz the boundaries a bit to avoid probing? */ case CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED: - failwiremsg = towire_temporary_channel_failure(inmsg, get_local_channel_update(inmsg, peer)); + failwiremsg = towire_temporary_channel_failure(inmsg, get_cupdate(peer)); failstr = tal_fmt(inmsg, "Capacity exceeded - HTLC fee: %s", fmt_amount_sat(inmsg, htlc_fee)); goto failed; case CHANNEL_ERR_HTLC_BELOW_MINIMUM: - failwiremsg = towire_amount_below_minimum(inmsg, amount, get_local_channel_update(inmsg, peer)); + failwiremsg = towire_amount_below_minimum(inmsg, amount, get_cupdate(peer)); failstr = tal_fmt(inmsg, "HTLC too small (%s minimum)", type_to_string(tmpctx, struct amount_msat, &peer->channel->config[REMOTE].htlc_minimum)); goto failed; case CHANNEL_ERR_TOO_MANY_HTLCS: - failwiremsg = towire_temporary_channel_failure(inmsg, get_local_channel_update(inmsg, peer)); + failwiremsg = towire_temporary_channel_failure(inmsg, get_cupdate(peer)); failstr = "Too many HTLCs"; goto failed; case CHANNEL_ERR_DUST_FAILURE: @@ -3408,7 +3398,7 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) * - SHOULD NOT send this HTLC * - SHOULD fail this HTLC if it's forwarded */ - failwiremsg = towire_temporary_channel_failure(inmsg, get_local_channel_update(inmsg, peer)); + failwiremsg = towire_temporary_channel_failure(inmsg, get_cupdate(peer)); failstr = "HTLC too dusty, allowed dust limit reached"; goto failed; } @@ -3591,6 +3581,14 @@ static void handle_shutdown_cmd(struct peer *peer, const u8 *inmsg) start_commit_timer(peer); } +/* Lightningd tells us when channel_update has changed. */ +static void handle_channel_update(struct peer *peer, const u8 *msg) +{ + peer->channel_update = tal_free(peer->channel_update); + if (!fromwire_channeld_channel_update(peer, msg, &peer->channel_update)) + master_badmsg(WIRE_CHANNELD_CHANNEL_UPDATE, msg); +} + static void handle_send_error(struct peer *peer, const u8 *msg) { char *reason; @@ -3757,6 +3755,9 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_PING: handle_send_ping(peer, msg); return; + case WIRE_CHANNELD_CHANNEL_UPDATE: + handle_channel_update(peer, msg); + return; #if DEVELOPER case WIRE_CHANNELD_DEV_REENABLE_COMMIT: handle_dev_reenable_commit(peer); @@ -3793,6 +3794,7 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_DEV_QUIESCE_REPLY: case WIRE_CHANNELD_UPGRADED: case WIRE_CHANNELD_PING_REPLY: + case WIRE_CHANNELD_USED_CHANNEL_UPDATE: break; } @@ -3898,7 +3900,8 @@ static void init_channel(struct peer *peer) &dev_fail_process_onionpacket, &dev_disable_commit, &pbases, - &reestablish_only)) { + &reestablish_only, + &peer->channel_update)) { master_badmsg(WIRE_CHANNELD_INIT, msg); } diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index f095b3cb5f61..00d9784b7616 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -77,6 +77,8 @@ 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 +msgdata,channeld_init,channel_update_len,u16, +msgdata,channeld_init,channel_update,u8,channel_update_len # master->channeld funding hit new depth(funding locked if >= lock depth) msgtype,channeld_funding_depth,1002 @@ -224,6 +226,14 @@ msgdata,channeld_send_error,reason,wirestring, # Tell master channeld has sent the error message. msgtype,channeld_send_error_reply,1108 +# Tell channeld about the latest channel_update +msgtype,channeld_channel_update,1001 +msgdata,channeld_channel_update,len,u16, +msgdata,channeld_channel_update,msg,u8,len + +# Tell lightningd we used the latest channel_update for an error. +msgtype,channeld_used_channel_update,1102 + # Ask channeld to quiesce. msgtype,channeld_dev_quiesce,1009 msgtype,channeld_dev_quiesce_reply,1109 diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 2653f479a57b..adef74e5e262 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -598,7 +598,7 @@ static void defer_update(struct daemon *daemon, } /* If there is a pending update for this local channel, apply immediately. */ -bool local_channel_update_latest(struct daemon *daemon, struct chan *chan) +static bool local_channel_update_latest(struct daemon *daemon, struct chan *chan) { struct deferred_update *du; diff --git a/gossipd/gossip_generation.h b/gossipd/gossip_generation.h index adc3f81db51d..3d8f257aba54 100644 --- a/gossipd/gossip_generation.h +++ b/gossipd/gossip_generation.h @@ -35,9 +35,6 @@ bool nannounce_different(struct gossip_store *gs, /* Should we announce our own node? Called at strategic places. */ void maybe_send_own_node_announce(struct daemon *daemon, bool startup); -/* Flush any pending changes to this channel. */ -bool local_channel_update_latest(struct daemon *daemon, struct chan *chan); - /* Disable this local channel (lazily) */ void local_disable_chan(struct daemon *daemon, const struct chan *chan, int direction); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 6e12096124ae..77c484e8052e 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -277,65 +277,6 @@ static u8 *handle_channel_update_msg(struct peer *peer, const u8 *msg) return NULL; } -/*~ This is when channeld asks us for a channel_update for a local channel. - * It does that to fill in the error field when lightningd fails an HTLC and - * sets the UPDATE bit in the error type. lightningd is too important to - * fetch this itself, so channeld does it (channeld has to talk to us for - * other things anyway, so why not?). */ -static bool handle_get_local_channel_update(struct peer *peer, const u8 *msg) -{ - struct short_channel_id scid; - struct chan *chan; - const u8 *update; - struct routing_state *rstate = peer->daemon->rstate; - int direction; - - if (!fromwire_gossipd_get_update(msg, &scid)) { - status_broken("peer %s sent bad gossip_get_update %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, msg)); - return false; - } - - /* It's possible that the channel has just closed (though v. unlikely) */ - chan = get_channel(rstate, &scid); - if (!chan) { - status_unusual("peer %s scid %s: unknown channel", - type_to_string(tmpctx, struct node_id, &peer->id), - type_to_string(tmpctx, struct short_channel_id, - &scid)); - update = NULL; - goto out; - } - - /* Since we're going to send it out, make sure it's up-to-date. */ - local_channel_update_latest(peer->daemon, chan); - - if (!local_direction(rstate, chan, &direction)) { - status_peer_broken(&peer->id, "Chan %s is not local?", - type_to_string(tmpctx, struct short_channel_id, - &scid)); - update = NULL; - goto out; - } - - /* It's possible this is zero, if we've never sent a channel_update - * for that channel. */ - if (!is_halfchan_defined(&chan->half[direction])) - update = NULL; - else - update = gossip_store_get(tmpctx, rstate->gs, - chan->half[direction].bcast.index); -out: - status_peer_debug(&peer->id, "schanid %s: %s update", - type_to_string(tmpctx, struct short_channel_id, &scid), - update ? "got" : "no"); - - msg = towire_gossipd_get_update_reply(NULL, update); - daemon_conn_send(peer->dc, take(msg)); - return true; -} - static u8 *handle_node_announce(struct peer *peer, const u8 *msg) { bool was_unknown = false; @@ -781,19 +722,12 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, /* Must be a gossipd_peerd_wire_type asking us to do something. */ switch ((enum gossipd_peerd_wire)fromwire_peektype(msg)) { - case WIRE_GOSSIPD_GET_UPDATE: - ok = handle_get_local_channel_update(peer, msg); - goto handled_cmd; case WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE: ok = handle_local_channel_update(peer->daemon, &peer->id, msg); goto handled_cmd; case WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT: ok = handle_local_channel_announcement(peer->daemon, peer, msg); goto handled_cmd; - - /* These are the ones we send, not them */ - case WIRE_GOSSIPD_GET_UPDATE_REPLY: - break; } if (fromwire_peektype(msg) == WIRE_GOSSIP_STORE_PRIVATE_CHANNEL) { diff --git a/gossipd/gossipd_peerd_wire.csv b/gossipd/gossipd_peerd_wire.csv index 10c48adeaf89..9b86cbf27cc8 100644 --- a/gossipd/gossipd_peerd_wire.csv +++ b/gossipd/gossipd_peerd_wire.csv @@ -2,16 +2,6 @@ #include #include -# Channel daemon can ask for updates for a specific channel, for sending -# errors. -msgtype,gossipd_get_update,3501 -msgdata,gossipd_get_update,short_channel_id,short_channel_id, - -# If channel isn't known, update will be empty. -msgtype,gossipd_get_update_reply,3601 -msgdata,gossipd_get_update_reply,len,u16, -msgdata,gossipd_get_update_reply,update,u8,len - # Send this channel_update. msgtype,gossipd_local_channel_update,3504 msgdata,gossipd_local_channel_update,short_channel_id,short_channel_id, diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 5ccc425f6f28..5babc51c9fb7 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -88,9 +88,6 @@ bool fromwire_gossipd_get_addrs(const void *p UNNEEDED, struct node_id *id UNNEE /* Generated stub for fromwire_gossipd_get_txout_reply */ bool fromwire_gossipd_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct amount_sat *satoshis UNNEEDED, u8 **outscript UNNEEDED) { fprintf(stderr, "fromwire_gossipd_get_txout_reply called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_get_update */ -bool fromwire_gossipd_get_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_get_update called!\n"); abort(); } /* Generated stub for fromwire_gossipd_init */ bool fromwire_gossipd_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct feature_set **our_features UNNEEDED, struct node_id *id UNNEEDED, u8 rgb[3] UNNEEDED, u8 alias[32] UNNEEDED, struct wireaddr **announcable UNNEEDED, u32 **dev_gossip_time UNNEEDED, bool *dev_fast_gossip UNNEEDED, bool *dev_fast_gossip_prune UNNEEDED) { fprintf(stderr, "fromwire_gossipd_init called!\n"); abort(); } @@ -199,9 +196,6 @@ void json_object_end(struct json_stream *js UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for local_channel_update_latest */ -bool local_channel_update_latest(struct daemon *daemon UNNEEDED, struct chan *chan UNNEEDED) -{ fprintf(stderr, "local_channel_update_latest called!\n"); abort(); } /* Generated stub for local_disable_chan */ void local_disable_chan(struct daemon *daemon UNNEEDED, const struct chan *chan UNNEEDED, int direction UNNEEDED) { fprintf(stderr, "local_disable_chan called!\n"); abort(); } @@ -323,9 +317,6 @@ u8 *towire_gossipd_get_addrs_reply(const tal_t *ctx UNNEEDED, const struct wirea /* Generated stub for towire_gossipd_get_txout */ u8 *towire_gossipd_get_txout(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) { fprintf(stderr, "towire_gossipd_get_txout called!\n"); abort(); } -/* Generated stub for towire_gossipd_get_update_reply */ -u8 *towire_gossipd_get_update_reply(const tal_t *ctx UNNEEDED, const u8 *update UNNEEDED) -{ fprintf(stderr, "towire_gossipd_get_update_reply called!\n"); abort(); } /* Generated stub for towire_gossipd_got_onionmsg_to_us */ u8 *towire_gossipd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEEDED, const struct pubkey *node_alias UNNEEDED, const struct secret *self_id UNNEEDED, const struct pubkey *reply_blinding UNNEEDED, const struct pubkey *reply_first_node UNNEEDED, const struct onionmsg_path **reply_path UNNEEDED, const u8 *rawmsg UNNEEDED) { fprintf(stderr, "towire_gossipd_got_onionmsg_to_us called!\n"); abort(); } diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index be3748e07e06..d8333673fb0a 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -504,6 +504,10 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_PING_REPLY: ping_reply(sd, msg); break; + case WIRE_CHANNELD_USED_CHANNEL_UPDATE: + /* This tells gossipd we used it. */ + get_channel_update(sd->channel); + break; #if EXPERIMENTAL_FEATURES case WIRE_CHANNELD_UPGRADED: handle_channel_upgrade(sd->channel, msg); @@ -525,6 +529,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_FEERATES: case WIRE_CHANNELD_BLOCKHEIGHT: case WIRE_CHANNELD_SPECIFIC_FEERATES: + case WIRE_CHANNELD_CHANNEL_UPDATE: case WIRE_CHANNELD_DEV_MEMLEAK: case WIRE_CHANNELD_DEV_QUIESCE: /* Replies go to requests. */ @@ -717,7 +722,8 @@ void peer_start_channeld(struct channel *channel, : (u32 *)&ld->dev_disable_commit, NULL), pbases, - reestablish_only); + reestablish_only, + channel->channel_update); /* We don't expect a response: we are triggered by funding_depth_cb. */ subd_send_msg(channel->owner, take(initmsg)); @@ -1008,6 +1014,20 @@ struct command_result *cancel_channel_before_broadcast(struct command *cmd, return command_still_pending(cmd); } +void channel_replace_update(struct channel *channel, u8 *update TAKES) +{ + tal_free(channel->channel_update); + channel->channel_update = tal_dup_talarr(channel, u8, update); + + /* Keep channeld up-to-date */ + if (!channel->owner || !streq(channel->owner->name, "channeld")) + return; + + subd_send_msg(channel->owner, + take(towire_channeld_channel_update(NULL, + channel->channel_update))); +} + #if DEVELOPER static struct command_result *json_dev_feerate(struct command *cmd, const char *buffer, diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index 2f80661b388f..db8fa838b5c9 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -39,4 +39,8 @@ void channel_record_open(struct channel *channel); /* A channel has unrecoverably fallen behind */ void channel_fallen_behind(struct channel *channel, const u8 *msg); + +/* Fresh channel_update for this channel. */ +void channel_replace_update(struct channel *channel, u8 *update TAKES); + #endif /* LIGHTNING_LIGHTNINGD_CHANNEL_CONTROL_H */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 5859fd675f85..33038d3e3d7e 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -131,8 +132,7 @@ static void handle_local_channel_update(struct lightningd *ld, const u8 *msg) return; } - tal_free(channel->channel_update); - channel->channel_update = tal_steal(channel, update); + channel_replace_update(channel, take(update)); } const u8 *get_channel_update(struct channel *channel) From e841e69b1b4ba77d97595143e8eef3d5af78c61b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:33:52 +1030 Subject: [PATCH 0269/1530] channeld: send channel updates and announcements via lightningd. We're weaning per-peer daemons off having a direct gossipd connection. Signed-off-by: Rusty Russell --- channeld/channeld.c | 34 ++++--- channeld/channeld_wire.csv | 21 ++++ gossipd/gossip_generation.c | 29 +++--- gossipd/gossip_generation.h | 6 +- gossipd/gossip_store.c | 15 ++- gossipd/gossipd.c | 95 +++++++++++-------- gossipd/gossipd_peerd_wire.csv | 14 --- gossipd/gossipd_wire.csv | 26 +++++ gossipd/routing.c | 58 +++++++---- gossipd/routing.h | 5 +- gossipd/test/run-check_channel_announcement.c | 4 + gossipd/test/run-check_node_announcement.c | 2 +- gossipd/test/run-crc32_of_update.c | 2 +- gossipd/test/run-onion_message.c | 24 +++-- gossipd/test/run-txout_failure.c | 4 + lightningd/channel_control.c | 27 ++++++ lightningd/gossip_control.c | 83 ++++++++++++++++ lightningd/gossip_control.h | 13 +++ 18 files changed, 335 insertions(+), 127 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 0c272791529a..a3d9fa113cdf 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -392,7 +392,9 @@ static void maybe_send_stfu(struct peer *peer) } #endif -/* Create and send channel_update to gossipd (and maybe peer) */ +/* Tell gossipd to create channel_update (then it goes into + * gossip_store, then streams out to peers, or sends it directly if + * it's a private channel) */ static void send_channel_update(struct peer *peer, int disable_flag) { u8 *msg; @@ -405,7 +407,7 @@ static void send_channel_update(struct peer *peer, int disable_flag) assert(peer->short_channel_ids[LOCAL].u64); - msg = towire_gossipd_local_channel_update(NULL, + msg = towire_channeld_local_channel_update(NULL, &peer->short_channel_ids[LOCAL], disable_flag == ROUTING_FLAGS_DISABLED, @@ -414,7 +416,7 @@ static void send_channel_update(struct peer *peer, int disable_flag) peer->fee_base, peer->fee_per_satoshi, advertized_htlc_max(peer->channel)); - wire_sync_write(peer->pps->gossip_fd, take(msg)); + wire_sync_write(MASTER_FD, take(msg)); } /** @@ -430,22 +432,15 @@ static void send_channel_update(struct peer *peer, int disable_flag) static void make_channel_local_active(struct peer *peer) { u8 *msg; - const u8 *ann; const u8 *annfeatures = get_agreed_channelfeatures(tmpctx, peer->our_features, peer->their_features); - ann = private_channel_announcement(tmpctx, - &peer->short_channel_ids[LOCAL], - &peer->node_ids[LOCAL], - &peer->node_ids[REMOTE], - annfeatures); - - /* Tell gossipd about local channel. */ - msg = towire_gossip_store_private_channel(NULL, - peer->channel->funding_sats, - ann); - wire_sync_write(peer->pps->gossip_fd, take(msg)); + /* Tell lightningd to tell gossipd about local channel. */ + msg = towire_channeld_local_private_channel(NULL, + peer->channel->funding_sats, + annfeatures); + wire_sync_write(MASTER_FD, take(msg)); /* Tell gossipd and the other side what parameters we expect should * they route through us */ @@ -560,9 +555,9 @@ static void announce_channel(struct peer *peer) cannounce = create_channel_announcement(tmpctx, peer); - wire_sync_write(peer->pps->gossip_fd, - take(towire_gossipd_local_channel_announcement(NULL, - cannounce))); + wire_sync_write(MASTER_FD, + take(towire_channeld_local_channel_announcement(NULL, + cannounce))); send_channel_update(peer, 0); } @@ -3795,6 +3790,9 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_UPGRADED: case WIRE_CHANNELD_PING_REPLY: case WIRE_CHANNELD_USED_CHANNEL_UPDATE: + case WIRE_CHANNELD_LOCAL_CHANNEL_UPDATE: + case WIRE_CHANNELD_LOCAL_CHANNEL_ANNOUNCEMENT: + case WIRE_CHANNELD_LOCAL_PRIVATE_CHANNEL: break; } diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 00d9784b7616..4770aea3d542 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -234,6 +234,27 @@ msgdata,channeld_channel_update,msg,u8,len # Tell lightningd we used the latest channel_update for an error. msgtype,channeld_used_channel_update,1102 +# Channeld: tell gossipd to make this channel_update. +msgtype,channeld_local_channel_update,1013 +msgdata,channeld_local_channel_update,short_channel_id,short_channel_id, +msgdata,channeld_local_channel_update,disable,bool, +msgdata,channeld_local_channel_update,cltv_expiry_delta,u16, +msgdata,channeld_local_channel_update,htlc_minimum_msat,amount_msat, +msgdata,channeld_local_channel_update,fee_base_msat,u32, +msgdata,channeld_local_channel_update,fee_proportional_millionths,u32, +msgdata,channeld_local_channel_update,htlc_maximum_msat,amount_msat, + +# Channeld: tell gossipd about our channel_announcement +msgtype,channeld_local_channel_announcement,1014 +msgdata,channeld_local_channel_announcement,len,u16, +msgdata,channeld_local_channel_announcement,cannounce,u8,len + +# Channeld: tell gossipd about this (as-yet) unannounced channel +msgtype,channeld_local_private_channel,1015 +msgdata,channeld_local_private_channel,capacity,amount_sat, +msgdata,channeld_local_private_channel,len,u16, +msgdata,channeld_local_private_channel,features,u8,len + # Ask channeld to quiesce. msgtype,channeld_dev_quiesce,1009 msgtype,channeld_dev_quiesce_reply,1109 diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index adef74e5e262..faf01bb7254f 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -695,11 +694,10 @@ void refresh_local_channel(struct daemon *daemon, sign_timestamp_and_apply_update(daemon, chan, direction, take(update)); } -/* channeld asks us to update the local channel. */ -bool handle_local_channel_update(struct daemon *daemon, - const struct node_id *src, - const u8 *msg) +/* channeld (via lightningd) asks us to update the local channel. */ +void handle_local_channel_update(struct daemon *daemon, const u8 *msg) { + struct node_id id; struct short_channel_id scid; bool disable; u16 cltv_expiry_delta; @@ -710,10 +708,8 @@ bool handle_local_channel_update(struct daemon *daemon, u8 *unsigned_update; const struct half_chan *hc; - /* FIXME: We should get scid from lightningd when setting up the - * connection, so no per-peer daemon can mess with channels other than - * its own! */ if (!fromwire_gossipd_local_channel_update(msg, + &id, &scid, &disable, &cltv_expiry_delta, @@ -721,26 +717,24 @@ bool handle_local_channel_update(struct daemon *daemon, &fee_base_msat, &fee_proportional_millionths, &htlc_maximum)) { - status_peer_broken(src, "bad local_channel_update %s", - tal_hex(tmpctx, msg)); - return false; + master_badmsg(WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE, msg); } chan = get_channel(daemon->rstate, &scid); /* Can theoretically happen if channel just closed. */ if (!chan) { - status_peer_debug(src, "local_channel_update for unknown %s", + status_peer_debug(&id, "local_channel_update for unknown %s", type_to_string(tmpctx, struct short_channel_id, &scid)); - return true; + return; } if (!local_direction(daemon->rstate, chan, &direction)) { - status_peer_broken(src, "bad local_channel_update chan %s", + status_peer_broken(&id, "bad local_channel_update chan %s", type_to_string(tmpctx, struct short_channel_id, &scid)); - return false; + return; } unsigned_update = create_unsigned_update(tmpctx, &scid, direction, @@ -754,7 +748,7 @@ bool handle_local_channel_update(struct daemon *daemon, /* Ignore duplicates. */ if (is_halfchan_defined(hc) && !cupdate_different(daemon->rstate->gs, hc, unsigned_update)) - return true; + return; /* Too early? Defer (don't worry if it's unannounced). */ if (hc && is_chan_public(chan)) { @@ -764,13 +758,12 @@ bool handle_local_channel_update(struct daemon *daemon, if (now < next_time) { defer_update(daemon, next_time - now, chan, direction, take(unsigned_update)); - return true; + return; } } sign_timestamp_and_apply_update(daemon, chan, direction, take(unsigned_update)); - return true; } /* Take update, set/unset disabled flag (and update timestamp). diff --git a/gossipd/gossip_generation.h b/gossipd/gossip_generation.h index 3d8f257aba54..b39cc158b47f 100644 --- a/gossipd/gossip_generation.h +++ b/gossipd/gossip_generation.h @@ -45,10 +45,8 @@ void local_enable_chan(struct daemon *daemon, const struct chan *chan, int direc void refresh_local_channel(struct daemon *daemon, struct chan *chan, int direction); -/* channeld asks us to update the local channel. */ -bool handle_local_channel_update(struct daemon *daemon, - const struct node_id *src, - const u8 *msg); +/* channeld (via lightningd) asks us to update the local channel. */ +void handle_local_channel_update(struct daemon *daemon, const u8 *msg); /* lightningd tells us it used the last channel_update we sent. */ void handle_used_local_channel_update(struct daemon *daemon, const u8 *msg); diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 408e79adfd36..f06c5a876678 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -782,14 +782,25 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) } switch (fromwire_peektype(msg)) { - case WIRE_GOSSIP_STORE_PRIVATE_CHANNEL: - if (!routing_add_private_channel(rstate, NULL, msg, + case WIRE_GOSSIP_STORE_PRIVATE_CHANNEL: { + u8 *chan_ann; + struct amount_sat sat; + if (!fromwire_gossip_store_private_channel(msg, msg, + &sat, + &chan_ann)) { + bad = "Bad private_channel"; + goto badmsg; + } + + if (!routing_add_private_channel(rstate, NULL, + sat, chan_ann, gs->len)) { bad = "Bad add_private_channel"; goto badmsg; } stats[0]++; break; + } case WIRE_GOSSIP_STORE_CHANNEL_AMOUNT: if (!fromwire_gossip_store_channel_amount(msg, &satoshis)) { diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 77c484e8052e..a27e6ccb81ef 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -289,31 +290,60 @@ static u8 *handle_node_announce(struct peer *peer, const u8 *msg) return err; } -static bool handle_local_channel_announcement(struct daemon *daemon, - struct peer *peer, - const u8 *msg) +static void handle_local_channel_announcement(struct daemon *daemon, const u8 *msg) { u8 *cannouncement; const u8 *err; + struct node_id id; + struct peer *peer; if (!fromwire_gossipd_local_channel_announcement(msg, msg, - &cannouncement)) { - status_broken("peer %s bad local_channel_announcement %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, msg)); - return false; - } + &id, + &cannouncement)) + master_badmsg(WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT, msg); + + /* We treat it OK even if peer has disconnected since (unlikely though!) */ + peer = find_peer(daemon, &id); + if (!peer) + status_broken("Unknown peer %s for local_channel_announcement", + type_to_string(tmpctx, struct node_id, &id)); err = handle_channel_announcement_msg(daemon, peer, cannouncement); if (err) { status_broken("peer %s invalid local_channel_announcement %s (%s)", - type_to_string(tmpctx, struct node_id, &peer->id), + type_to_string(tmpctx, struct node_id, &id), tal_hex(tmpctx, msg), tal_hex(tmpctx, err)); - return false; } +} - return true; + +/* channeld (via lightningd) tells us about (as-yet?) unannounce channel. + * It needs us to put it in gossip_store. */ +static void handle_local_private_channel(struct daemon *daemon, const u8 *msg) +{ + struct node_id id; + struct amount_sat capacity; + u8 *features; + struct short_channel_id scid; + const u8 *cannounce; + + if (!fromwire_gossipd_local_private_channel(msg, msg, + &id, &capacity, &scid, + &features)) + master_badmsg(WIRE_GOSSIPD_LOCAL_PRIVATE_CHANNEL, msg); + + cannounce = private_channel_announcement(tmpctx, + &scid, + &daemon->id, + &id, + features); + + if (!routing_add_private_channel(daemon->rstate, &id, capacity, + cannounce, 0)) { + status_peer_broken(&id, "bad add_private_channel %s", + tal_hex(tmpctx, cannounce)); + } } /* Peer sends obsolete onion msg. */ @@ -645,7 +675,6 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, struct peer *peer) { const u8 *err; - bool ok; /* These are messages relayed from peer */ switch ((enum peer_wire)fromwire_peektype(msg)) { @@ -720,40 +749,17 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, return io_close(conn); } - /* Must be a gossipd_peerd_wire_type asking us to do something. */ - switch ((enum gossipd_peerd_wire)fromwire_peektype(msg)) { - case WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE: - ok = handle_local_channel_update(peer->daemon, &peer->id, msg); - goto handled_cmd; - case WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT: - ok = handle_local_channel_announcement(peer->daemon, peer, msg); - goto handled_cmd; - } - - if (fromwire_peektype(msg) == WIRE_GOSSIP_STORE_PRIVATE_CHANNEL) { - ok = routing_add_private_channel(peer->daemon->rstate, peer, - msg, 0); - goto handled_cmd; - } - /* Anything else should not have been sent to us: close on it */ - status_peer_broken(&peer->id, "unexpected cmd of type %i %s", - fromwire_peektype(msg), - gossipd_peerd_wire_name(fromwire_peektype(msg))); + status_peer_broken(&peer->id, "unexpected cmd of type %i", + fromwire_peektype(msg)); return io_close(conn); - /* Commands should always be OK. */ -handled_cmd: - if (!ok) - return io_close(conn); - goto done; - /* Forwarded messages may be bad, so we have error which the per-peer * daemon will forward to the peer. */ handled_relay: if (err) queue_peer_msg(peer, take(err)); -done: + return daemon_conn_read_next(conn, peer->dc); } @@ -1297,6 +1303,17 @@ static struct io_plan *recv_req(struct io_conn *conn, handle_used_local_channel_update(daemon, msg); goto done; + case WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE: + handle_local_channel_update(daemon, msg); + goto done; + + case WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT: + handle_local_channel_announcement(daemon, msg); + goto done; + + case WIRE_GOSSIPD_LOCAL_PRIVATE_CHANNEL: + handle_local_private_channel(daemon, msg); + goto done; #if DEVELOPER case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: dev_set_max_scids_encode_size(daemon, msg); diff --git a/gossipd/gossipd_peerd_wire.csv b/gossipd/gossipd_peerd_wire.csv index 9b86cbf27cc8..282712beb4bc 100644 --- a/gossipd/gossipd_peerd_wire.csv +++ b/gossipd/gossipd_peerd_wire.csv @@ -2,17 +2,3 @@ #include #include -# Send this channel_update. -msgtype,gossipd_local_channel_update,3504 -msgdata,gossipd_local_channel_update,short_channel_id,short_channel_id, -msgdata,gossipd_local_channel_update,disable,bool, -msgdata,gossipd_local_channel_update,cltv_expiry_delta,u16, -msgdata,gossipd_local_channel_update,htlc_minimum_msat,amount_msat, -msgdata,gossipd_local_channel_update,fee_base_msat,u32, -msgdata,gossipd_local_channel_update,fee_proportional_millionths,u32, -msgdata,gossipd_local_channel_update,htlc_maximum_msat,amount_msat, - -# Send this channel_announcement -msgtype,gossipd_local_channel_announcement,3506 -msgdata,gossipd_local_channel_announcement,len,u16, -msgdata,gossipd_local_channel_announcement,cannount,u8,len diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 7dba19620276..eaec7643caa9 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -115,6 +115,32 @@ msgdata,gossipd_got_local_channel_update,scid,short_channel_id, msgdata,gossipd_got_local_channel_update,len,u16, msgdata,gossipd_got_local_channel_update,channel_update,u8,len +# Send this channel_update. +msgtype,gossipd_local_channel_update,3004 +msgdata,gossipd_local_channel_update,id,node_id, +msgdata,gossipd_local_channel_update,short_channel_id,short_channel_id, +msgdata,gossipd_local_channel_update,disable,bool, +msgdata,gossipd_local_channel_update,cltv_expiry_delta,u16, +msgdata,gossipd_local_channel_update,htlc_minimum_msat,amount_msat, +msgdata,gossipd_local_channel_update,fee_base_msat,u32, +msgdata,gossipd_local_channel_update,fee_proportional_millionths,u32, +msgdata,gossipd_local_channel_update,htlc_maximum_msat,amount_msat, + +# Send this channel_announcement +msgtype,gossipd_local_channel_announcement,3006 +msgdata,gossipd_local_channel_announcement,id,node_id, +msgdata,gossipd_local_channel_announcement,len,u16, +msgdata,gossipd_local_channel_announcement,cannounce,u8,len + +# Tell gossipd about a private channel (to put it in the store) +# cannounce has same structure, dummy sigs. +msgtype,gossipd_local_private_channel,3008 +msgdata,gossipd_local_private_channel,id,node_id, +msgdata,gossipd_local_private_channel,capacity,amount_sat, +msgdata,gossipd_local_private_channel,scid,short_channel_id, +msgdata,gossipd_local_private_channel,len,u16, +msgdata,gossipd_local_private_channel,features,u8,len + # Tell gossipd we used the channel update (in case it was deferred) msgtype,gossipd_used_local_channel_update,3052 msgdata,gossipd_used_local_channel_update,scid,short_channel_id, diff --git a/gossipd/routing.c b/gossipd/routing.c index c52d68bc3e9f..c95834d02785 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1820,23 +1820,18 @@ void route_prune(struct routing_state *rstate) } bool routing_add_private_channel(struct routing_state *rstate, - const struct peer *peer, - const u8 *msg, u64 index) + const struct node_id *id, + struct amount_sat capacity, + const u8 *chan_ann, u64 index) { struct short_channel_id scid; struct node_id node_id[2]; struct pubkey ignorekey; - struct amount_sat sat; struct chan *chan; - u8 *features, *chan_ann; + u8 *features; secp256k1_ecdsa_signature ignoresig; struct bitcoin_blkid chain_hash; - if (!fromwire_gossip_store_private_channel(tmpctx, msg, - &sat, &chan_ann)) - return false; - - if (!fromwire_channel_announcement(tmpctx, chan_ann, &ignoresig, &ignoresig, @@ -1851,21 +1846,46 @@ bool routing_add_private_channel(struct routing_state *rstate, &ignorekey)) return false; - /* Can happen on channeld restart. */ - if (get_channel(rstate, &scid)) { - status_peer_debug(peer ? &peer->id : NULL, - "Attempted to local_add_channel a known channel"); + /* Happens on channeld restart. */ + if (get_channel(rstate, &scid)) return true; - } - status_peer_debug(peer ? &peer->id : NULL, - "local_add_channel %s", - type_to_string(tmpctx, struct short_channel_id, &scid)); + /* Make sure this id (if any) was allowed to create this */ + if (id) { + struct node_id expected[2]; + int cmp = node_id_cmp(&rstate->local_id, id); + + if (cmp < 0) { + expected[0] = rstate->local_id; + expected[1] = *id; + } else if (cmp > 0) { + expected[0] = *id; + expected[1] = rstate->local_id; + } else { + /* lightningd sets id, so this is fatal */ + status_failed(STATUS_FAIL_MASTER_IO, + "private_channel peer was us?"); + } + + if (!node_id_eq(&node_id[0], &expected[0]) + || !node_id_eq(&node_id[1], &expected[1])) { + status_peer_broken(id, "private channel %s<->%s invalid", + type_to_string(tmpctx, struct node_id, + &node_id[0]), + type_to_string(tmpctx, struct node_id, + &node_id[1])); + return false; + } + } /* Create new (unannounced) channel */ - chan = new_chan(rstate, &scid, &node_id[0], &node_id[1], sat); - if (!index) + chan = new_chan(rstate, &scid, &node_id[0], &node_id[1], capacity); + if (!index) { + u8 *msg = towire_gossip_store_private_channel(tmpctx, + capacity, + chan_ann); index = gossip_store_add(rstate->gs, msg, 0, false, NULL); + } chan->bcast.index = index; return true; } diff --git a/gossipd/routing.h b/gossipd/routing.h index e33d6306116b..1641284bb667 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -378,8 +378,9 @@ bool routing_add_node_announcement(struct routing_state *rstate, * `announce_depth`. */ bool routing_add_private_channel(struct routing_state *rstate, - const struct peer *peer, - const u8 *msg, u64 index); + const struct node_id *id, + struct amount_sat sat, + const u8 *chan_ann, u64 index); /** * Get the local time. diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 28b29a341031..827bbdd63ebd 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -144,6 +144,10 @@ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } +/* Generated stub for status_failed */ +void status_failed(enum status_failreason code UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "status_failed called!\n"); abort(); } /* Generated stub for status_fmt */ void status_fmt(enum log_level level UNNEEDED, const struct node_id *peer UNNEEDED, diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 4104556fade7..25fcdda7abd2 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -37,7 +37,7 @@ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_update */ -bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) +bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } /* Generated stub for fromwire_gossipd_used_local_channel_update */ bool fromwire_gossipd_used_local_channel_update(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED) diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 7d9b64037473..47a1250a35c9 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -58,7 +58,7 @@ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_update */ -bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) +bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } /* Generated stub for fromwire_gossipd_used_local_channel_update */ bool fromwire_gossipd_used_local_channel_update(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED) diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 5babc51c9fb7..2ad7a2f3e7c8 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -92,11 +92,14 @@ bool fromwire_gossipd_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p U bool fromwire_gossipd_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct feature_set **our_features UNNEEDED, struct node_id *id UNNEEDED, u8 rgb[3] UNNEEDED, u8 alias[32] UNNEEDED, struct wireaddr **announcable UNNEEDED, u32 **dev_gossip_time UNNEEDED, bool *dev_fast_gossip UNNEEDED, bool *dev_fast_gossip_prune UNNEEDED) { fprintf(stderr, "fromwire_gossipd_init called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_announcement */ -bool fromwire_gossipd_local_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **cannount UNNEEDED) +bool fromwire_gossipd_local_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u8 **cannounce UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_announcement called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_close */ bool fromwire_gossipd_local_channel_close(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_close called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_local_private_channel */ +bool fromwire_gossipd_local_private_channel(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct amount_sat *capacity UNNEEDED, struct short_channel_id *scid UNNEEDED, u8 **features UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_local_private_channel called!\n"); abort(); } /* Generated stub for fromwire_gossipd_new_blockheight */ bool fromwire_gossipd_new_blockheight(const void *p UNNEEDED, u32 *blockheight UNNEEDED) { fprintf(stderr, "fromwire_gossipd_new_blockheight called!\n"); abort(); } @@ -133,9 +136,6 @@ u32 gossip_store_load(struct routing_state *rstate UNNEEDED, struct gossip_store /* Generated stub for gossip_time_now */ struct timeabs gossip_time_now(const struct routing_state *rstate UNNEEDED) { fprintf(stderr, "gossip_time_now called!\n"); abort(); } -/* Generated stub for gossipd_peerd_wire_name */ -const char *gossipd_peerd_wire_name(int e UNNEEDED) -{ fprintf(stderr, "gossipd_peerd_wire_name called!\n"); abort(); } /* Generated stub for handle_channel_announcement */ u8 *handle_channel_announcement(struct routing_state *rstate UNNEEDED, const u8 *announce TAKES UNNEEDED, @@ -150,9 +150,7 @@ u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *updat bool force UNNEEDED) { fprintf(stderr, "handle_channel_update called!\n"); abort(); } /* Generated stub for handle_local_channel_update */ -bool handle_local_channel_update(struct daemon *daemon UNNEEDED, - const struct node_id *src UNNEEDED, - const u8 *msg UNNEEDED) +void handle_local_channel_update(struct daemon *daemon UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "handle_local_channel_update called!\n"); abort(); } /* Generated stub for handle_node_announcement */ u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, @@ -247,6 +245,13 @@ struct chan *next_chan(const struct node *node UNNEEDED, struct chan_map_iter *i /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } +/* Generated stub for private_channel_announcement */ +const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, + const struct short_channel_id *scid UNNEEDED, + const struct node_id *local_node_id UNNEEDED, + const struct node_id *remote_node_id UNNEEDED, + const u8 *features UNNEEDED) +{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } /* Generated stub for query_unknown_channel */ void query_unknown_channel(struct daemon *daemon UNNEEDED, struct peer *peer UNNEEDED, @@ -273,8 +278,9 @@ void route_prune(struct routing_state *rstate UNNEEDED) { fprintf(stderr, "route_prune called!\n"); abort(); } /* Generated stub for routing_add_private_channel */ bool routing_add_private_channel(struct routing_state *rstate UNNEEDED, - const struct peer *peer UNNEEDED, - const u8 *msg UNNEEDED, u64 index UNNEEDED) + const struct node_id *id UNNEEDED, + struct amount_sat sat UNNEEDED, + const u8 *chan_ann UNNEEDED, u64 index UNNEEDED) { fprintf(stderr, "routing_add_private_channel called!\n"); abort(); } /* Generated stub for sanitize_error */ char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index ba7791c329a3..1acfd534d210 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -109,6 +109,10 @@ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDE char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "sanitize_error called!\n"); abort(); } +/* Generated stub for status_failed */ +void status_failed(enum status_failreason code UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "status_failed called!\n"); abort(); } /* Generated stub for status_fmt */ void status_fmt(enum log_level level UNNEEDED, const struct node_id *peer UNNEEDED, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index d8333673fb0a..030b15b5eb2a 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -412,6 +413,23 @@ static void handle_error_channel(struct channel *channel, forget(channel); } +static void handle_local_private_channel(struct channel *channel, const u8 *msg) +{ + struct amount_sat capacity; + u8 *features; + + if (!fromwire_channeld_local_private_channel(msg, msg, &capacity, + &features)) { + channel_internal_error(channel, + "bad channeld_local_private_channel %s", + tal_hex(channel, msg)); + return; + } + + tell_gossipd_local_private_channel(channel->peer->ld, channel, + capacity, features); +} + static void forget_channel(struct channel *channel, const char *why) { channel->error = towire_errorfmt(channel, &channel->cid, "%s", why); @@ -508,6 +526,15 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) /* This tells gossipd we used it. */ get_channel_update(sd->channel); break; + case WIRE_CHANNELD_LOCAL_CHANNEL_UPDATE: + tell_gossipd_local_channel_update(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_LOCAL_CHANNEL_ANNOUNCEMENT: + tell_gossipd_local_channel_announce(sd->ld, sd->channel, msg); + break; + case WIRE_CHANNELD_LOCAL_PRIVATE_CHANNEL: + handle_local_private_channel(sd->channel, msg); + break; #if EXPERIMENTAL_FEATURES case WIRE_CHANNELD_UPGRADED: handle_channel_upgrade(sd->channel, msg); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 33038d3e3d7e..3b13a102119d 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -167,6 +168,9 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_ADDGOSSIP: case WIRE_GOSSIPD_GET_ADDRS: case WIRE_GOSSIPD_USED_LOCAL_CHANNEL_UPDATE: + case WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE: + case WIRE_GOSSIPD_LOCAL_CHANNEL_ANNOUNCEMENT: + case WIRE_GOSSIPD_LOCAL_PRIVATE_CHANNEL: /* This is a reply, so never gets through to here. */ case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: @@ -278,6 +282,85 @@ void gossipd_notify_spend(struct lightningd *ld, subd_send_msg(ld->gossip, msg); } +/* We unwrap, add the peer id, and send to gossipd. */ +void tell_gossipd_local_channel_update(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + struct short_channel_id scid; + bool disable; + u16 cltv_expiry_delta; + struct amount_msat htlc_minimum_msat; + u32 fee_base_msat, fee_proportional_millionths; + struct amount_msat htlc_maximum_msat; + + if (!fromwire_channeld_local_channel_update(msg, &scid, &disable, + &cltv_expiry_delta, + &htlc_minimum_msat, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_maximum_msat)) { + channel_internal_error(channel, + "bad channeld_local_channel_update %s", + tal_hex(channel, msg)); + return; + } + + /* As we're shutting down, ignore */ + if (!ld->gossip) + return; + + subd_send_msg(ld->gossip, + take(towire_gossipd_local_channel_update + (NULL, + &channel->peer->id, + &scid, + disable, + cltv_expiry_delta, + htlc_minimum_msat, + fee_base_msat, + fee_proportional_millionths, htlc_maximum_msat))); +} + +void tell_gossipd_local_channel_announce(struct lightningd *ld, + struct channel *channel, + const u8 *msg) +{ + u8 *ann; + if (!fromwire_channeld_local_channel_announcement(msg, msg, &ann)) { + channel_internal_error(channel, + "bad channeld_local_channel_announcement" + " %s", + tal_hex(channel, msg)); + return; + } + + /* As we're shutting down, ignore */ + if (!ld->gossip) + return; + + subd_send_msg(ld->gossip, + take(towire_gossipd_local_channel_announcement + (NULL, &channel->peer->id, ann))); +} + +void tell_gossipd_local_private_channel(struct lightningd *ld, + struct channel *channel, + struct amount_sat capacity, + const u8 *features) +{ + /* As we're shutting down, ignore */ + if (!ld->gossip) + return; + + subd_send_msg(ld->gossip, + take(towire_gossipd_local_private_channel + (NULL, &channel->peer->id, + capacity, + channel->scid, + features))); +} + static struct command_result *json_setleaserates(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, diff --git a/lightningd/gossip_control.h b/lightningd/gossip_control.h index 7d6254e291bb..c1d587902158 100644 --- a/lightningd/gossip_control.h +++ b/lightningd/gossip_control.h @@ -5,6 +5,7 @@ #include #include +struct channel; struct lightningd; void gossip_init(struct lightningd *ld, int connectd_fd); @@ -14,4 +15,16 @@ void gossipd_notify_spend(struct lightningd *ld, void gossip_notify_new_block(struct lightningd *ld, u32 blockheight); +/* channeld tells us stuff, we tell gossipd. */ +void tell_gossipd_local_channel_update(struct lightningd *ld, + struct channel *channel, + const u8 *msg); +void tell_gossipd_local_channel_announce(struct lightningd *ld, + struct channel *channel, + const u8 *msg); +void tell_gossipd_local_private_channel(struct lightningd *ld, + struct channel *channel, + struct amount_sat capacity, + const u8 *features); + #endif /* LIGHTNING_LIGHTNINGD_GOSSIP_CONTROL_H */ From 88a4502a51133c23dd9a3106924223c48644564f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:34:52 +1030 Subject: [PATCH 0270/1530] channeld: pause before sending initial channel_update. The last change exposed a race: the peer sends funding_locked then immediately sends an update_channel. channeld used to process the funding_locked from the peer, tell gossipd about the new channel, then finally forward the channel_update. We can have the channel_update hit gossipd before we've told it about the channel. It ignores the channel_update for the currently-unknown channel: we get a 'bad gossip' message, but the immediate symptom is a timeout in tests/test_closing.py::test_onchain_multihtlc_their_unilateral: ``` node_factory = bitcoind = @pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs") @pytest.mark.slow_test def test_onchain_multihtlc_their_unilateral(node_factory, bitcoind): """Node pushes a channel onchain with multiple HTLCs with same payment_hash """ > h, nodes = setup_multihtlc_test(node_factory, bitcoind) tests/test_closing.py:2938: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ tests/test_closing.py:2780: in setup_multihtlc_test nodes = node_factory.line_graph(7, wait_for_announce=True, /usr/local/lib/python3.8/dist-packages/pyln/testing/utils.py:1416: in line_graph self.join_nodes(nodes, fundchannel, fundamount, wait_for_announce, announce_channels) /usr/local/lib/python3.8/dist-packages/pyln/testing/utils.py:1394: in join_nodes nodes[i + 1].wait_channel_active(scids[i]) /usr/local/lib/python3.8/dist-packages/pyln/testing/utils.py:958: in wait_channel_active wait_for(lambda: self.is_channel_active(chanid)) ``` Note that we are usually much faster to send between subds than we are between peers, but during CI this is common, as we're all running on the same machine. Signed-off-by: Rusty Russell --- channeld/channeld.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index a3d9fa113cdf..c0a2d46857bb 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -419,6 +419,13 @@ static void send_channel_update(struct peer *peer, int disable_flag) wire_sync_write(MASTER_FD, take(msg)); } +/* Tell gossipd and the other side what parameters we expect should + * they route through us */ +static void send_channel_initial_update(struct peer *peer) +{ + send_channel_update(peer, 0); +} + /** * Add a channel locally and send a channel update to the peer * @@ -442,9 +449,12 @@ static void make_channel_local_active(struct peer *peer) annfeatures); wire_sync_write(MASTER_FD, take(msg)); - /* Tell gossipd and the other side what parameters we expect should - * they route through us */ - send_channel_update(peer, 0); + /* Under CI, because blocks come so fast, we often find that the + * peer sends its first channel_update before the above message has + * reached it. */ + notleak(new_reltimer(&peer->timers, peer, + time_from_sec(5), + send_channel_initial_update, peer)); } static void send_announcement_signatures(struct peer *peer) From 4dc72dd829e67eb4d92135ae73992eb9c6120c1e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:35:52 +1030 Subject: [PATCH 0271/1530] dualopend: tell lightningd about new channel as soon as it's locked in. Once we send funding_locked, gossipd could start seeing channel_updates from the peer (which get sent so we can use the channel in routehints even before it's announcable). Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 23 +++++++++++++++++++++- openingd/dualopend.c | 36 ++++++++++++++++++++++++++++++++++ openingd/dualopend_wire.csv | 6 ++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 1728b3a45d1c..ba9219f76923 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1368,6 +1369,24 @@ static void handle_channel_closed(struct subd *dualopend, "Start closingd"); } +static void handle_local_private_channel(struct subd *dualopend, + const u8 *msg) +{ + struct amount_sat capacity; + u8 *features; + + if (!fromwire_dualopend_local_private_channel(msg, msg, &capacity, + &features)) { + channel_internal_error(dualopend->channel, + "bad dualopend_local_private_channel %s", + tal_hex(msg, msg)); + return; + } + + tell_gossipd_local_private_channel(dualopend->ld, dualopend->channel, + capacity, features); +} + struct channel_send { const struct wally_tx *wtx; struct channel *channel; @@ -2991,7 +3010,9 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND: channel_fail_fallen_behind(dualopend, msg); return 0; - + case WIRE_DUALOPEND_LOCAL_PRIVATE_CHANNEL: + handle_local_private_channel(dualopend, msg); + return 0; /* Messages we send */ case WIRE_DUALOPEND_INIT: case WIRE_DUALOPEND_REINIT: diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 28c9a407dfa8..87a38d3520db 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3396,6 +3396,38 @@ static void send_funding_locked(struct state *state) billboard_update(state); } +/* FIXME: Maybe cache this? */ +static struct amount_sat channel_size(struct state *state) +{ + u32 funding_outnum; + const u8 *funding_wscript = + bitcoin_redeem_2of2(tmpctx, + &state->our_funding_pubkey, + &state->their_funding_pubkey); + + if (!find_txout(state->tx_state->psbt, + scriptpubkey_p2wsh(tmpctx, funding_wscript), + &funding_outnum)) { + open_err_fatal(state, "Cannot fund txout"); + } + + return psbt_output_get_amount(state->tx_state->psbt, funding_outnum); +} + +static void tell_gossipd_new_channel(struct state *state) +{ + u8 *msg; + const u8 *annfeatures = get_agreed_channelfeatures(tmpctx, + state->our_features, + state->their_features); + + /* Tell lightningd about local channel. */ + msg = towire_dualopend_local_private_channel(NULL, + channel_size(state), + annfeatures); + wire_sync_write(REQ_FD, take(msg)); +} + static u8 *handle_funding_depth(struct state *state, u8 *msg) { u32 depth; @@ -3410,6 +3442,9 @@ static u8 *handle_funding_depth(struct state *state, u8 *msg) /* We check this before we arrive here, but for sanity */ assert(state->minimum_depth <= depth); + /* Tell gossipd the new channel exists before we tell peer. */ + tell_gossipd_new_channel(state); + send_funding_locked(state); if (state->funding_locked[REMOTE]) return towire_dualopend_channel_locked(state); @@ -3665,6 +3700,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND: case WIRE_DUALOPEND_DRY_RUN: case WIRE_DUALOPEND_VALIDATE_LEASE: + case WIRE_DUALOPEND_LOCAL_PRIVATE_CHANNEL: break; } diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index c87fbef2a30d..6c6543487d4e 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -238,3 +238,9 @@ msgdata,dualopend_validate_lease,their_pubkey,pubkey, msgtype,dualopend_validate_lease_reply,7127 msgdata,dualopend_validate_lease_reply,err_msg,?wirestring, + +# Tell gossipd about this (as-yet) unannounced channel +msgtype,dualopend_local_private_channel,7015 +msgdata,dualopend_local_private_channel,capacity,amount_sat, +msgdata,dualopend_local_private_channel,len,u16, +msgdata,dualopend_local_private_channel,features,u8,len From bba468a51c4faafec63f6a95f0858162d9c0823a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:36:52 +1030 Subject: [PATCH 0272/1530] connectd: temporarily have two fds to gossipd. We want to stream gossip through this, but currently connectd treats the fd as synchronous. While we work on getting rid of that, it's easiest to have two fds. Signed-off-by: Rusty Russell --- connectd/connectd.c | 17 ++++++++++++++++- connectd/connectd.h | 3 +++ lightningd/connect_control.c | 12 +++++++++--- lightningd/connect_control.h | 2 +- lightningd/gossip_control.c | 7 +++++-- lightningd/gossip_control.h | 2 +- lightningd/lightningd.c | 6 +++--- lightningd/test/run-find_my_abspath.c | 4 ++-- 8 files changed, 40 insertions(+), 13 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 01be81414106..64dac103535d 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -52,6 +52,7 @@ * thus may know how to reach certain peers. */ #define HSM_FD 3 #define GOSSIPCTL_FD 4 +#define GOSSIPCTL2_FD 5 /*~ In C convention, constants are UPPERCASE macros. Not everything needs to * be a constant, but it soothes the programmer's conscience to encapsulate @@ -1577,7 +1578,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) announcable))); #if DEVELOPER if (dev_disconnect) - dev_disconnect_init(5); + dev_disconnect_init(6); #endif } @@ -2001,6 +2002,15 @@ static void master_gone(struct daemon_conn *master UNUSED) exit(2); } +/*~ gossipd sends us gossip to send to the peers. */ +static struct io_plan *recv_gossip(struct io_conn *conn, + const u8 *msg, + struct daemon *daemon) +{ + /* FIXME! */ + return daemon_conn_read_next(conn, daemon->gossipd); +} + /*~ This is a hook used by the memleak code (if DEVELOPER=1): it can't see * pointers inside hash tables, so we give it a hint here. */ #if DEVELOPER @@ -2037,6 +2047,11 @@ int main(int argc, char *argv[]) * our status_ and failed messages. */ status_setup_async(daemon->master); + /* This streams gossip to and from gossipd */ + daemon->gossipd = daemon_conn_new(daemon, GOSSIPCTL2_FD, + recv_gossip, NULL, + daemon); + /* Set up ecdh() function so it uses our HSM fd, and calls * status_failed on error. */ ecdh_hsmd_setup(HSM_FD, status_failed); diff --git a/connectd/connectd.h b/connectd/connectd.h index aa151ea891c3..86800f1e93ec 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -126,6 +126,9 @@ struct daemon { /* Connection to main daemon. */ struct daemon_conn *master; + /* Connection to gossip daemon. */ + struct daemon_conn *gossipd; + /* Allow localhost to be considered "public": DEVELOPER-only option, * but for simplicity we don't #if DEVELOPER-wrap it here. */ bool dev_allow_localhost; diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index a8861aad1d4a..e46305595653 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -403,9 +403,9 @@ static void connect_init_done(struct subd *connectd, io_break(connectd); } -int connectd_init(struct lightningd *ld) +int connectd_init(struct lightningd *ld, int *gossipd_fd2) { - int fds[2]; + int fds[2], fds2[2]; u8 *msg; int hsmfd; struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr; @@ -418,11 +418,16 @@ int connectd_init(struct lightningd *ld) if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) fatal("Could not socketpair for connectd<->gossipd"); + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds2) != 0) + fatal("Could not socketpair for connectd<->gossipd 2"); + hsmfd = hsm_get_global_fd(ld, HSM_CAP_ECDH); ld->connectd = new_global_subd(ld, "lightning_connectd", connectd_wire_name, connectd_msg, - take(&hsmfd), take(&fds[1]), + take(&hsmfd), + take(&fds[1]), + take(&fds2[1]), #if DEVELOPER /* Not take(): we share it */ ld->dev_disconnect_fd >= 0 ? @@ -463,6 +468,7 @@ int connectd_init(struct lightningd *ld) /* Wait for init_reply */ io_loop(NULL, NULL); + *gossipd_fd2 = fds2[0]; return fds[0]; } diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 67beb003782b..681c5c7a1ed5 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -7,7 +7,7 @@ struct pubkey; struct wireaddr_internal; /* Returns fd for gossipd to talk to connectd */ -int connectd_init(struct lightningd *ld); +int connectd_init(struct lightningd *ld, int *gossipd_fd2); void connectd_activate(struct lightningd *ld); void try_reconnect(struct channel *channel, u32 seconds_delay, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 3b13a102119d..f6895fd69d00 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -239,7 +239,7 @@ static void gossipd_init_done(struct subd *gossipd, /* Create the `gossipd` subdaemon and send the initialization * message */ -void gossip_init(struct lightningd *ld, int connectd_fd) +void gossip_init(struct lightningd *ld, int connectd_fd, int connectd_fd2) { u8 *msg; int hsmfd; @@ -248,7 +248,10 @@ void gossip_init(struct lightningd *ld, int connectd_fd) ld->gossip = new_global_subd(ld, "lightning_gossipd", gossipd_wire_name, gossip_msg, - take(&hsmfd), take(&connectd_fd), NULL); + take(&hsmfd), + take(&connectd_fd), + take(&connectd_fd2), + NULL); if (!ld->gossip) err(1, "Could not subdaemon gossip"); diff --git a/lightningd/gossip_control.h b/lightningd/gossip_control.h index c1d587902158..869f53930aaf 100644 --- a/lightningd/gossip_control.h +++ b/lightningd/gossip_control.h @@ -8,7 +8,7 @@ struct channel; struct lightningd; -void gossip_init(struct lightningd *ld, int connectd_fd); +void gossip_init(struct lightningd *ld, int connectd_fd, int connectd_fd2); void gossipd_notify_spend(struct lightningd *ld, const struct short_channel_id *scid); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index cedc20e77d8d..b8ec5851fc69 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -850,7 +850,7 @@ int main(int argc, char *argv[]) { struct lightningd *ld; u32 min_blockheight, max_blockheight; - int connectd_gossipd_fd; + int connectd_gossipd_fd, connectd_gossipd_fd2; int stop_fd; struct timers *timers; const char *stop_response; @@ -1022,7 +1022,7 @@ int main(int argc, char *argv[]) * which knows (via node_announcement messages) the public * addresses of nodes, so connectd_init hands it one end of a * socket pair, and gives us the other */ - connectd_gossipd_fd = connectd_init(ld); + connectd_gossipd_fd = connectd_init(ld, &connectd_gossipd_fd2); /*~ We do every database operation within a transaction; usually this * is covered by the infrastructure (eg. opening a transaction before @@ -1074,7 +1074,7 @@ int main(int argc, char *argv[]) * channel_announcement, channel_update, node_announcement and gossip * queries. It also hands us the latest channel_updates for our * channels. */ - gossip_init(ld, connectd_gossipd_fd); + gossip_init(ld, connectd_gossipd_fd, connectd_gossipd_fd2); /*~ Create RPC socket: now lightning-cli can send us JSON RPC commands * over a UNIX domain socket specified by `ld->rpc_filename`. */ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 9cac29111ef9..ac79fffe3f7a 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -23,7 +23,7 @@ void channel_notify_new_block(struct lightningd *ld UNNEEDED, void connectd_activate(struct lightningd *ld UNNEEDED) { fprintf(stderr, "connectd_activate called!\n"); abort(); } /* Generated stub for connectd_init */ -int connectd_init(struct lightningd *ld UNNEEDED) +int connectd_init(struct lightningd *ld UNNEEDED, int *gossipd_fd2 UNNEEDED) { fprintf(stderr, "connectd_init called!\n"); abort(); } /* Generated stub for daemon_poll */ int daemon_poll(struct pollfd *fds UNNEEDED, nfds_t nfds UNNEEDED, int timeout UNNEEDED) @@ -92,7 +92,7 @@ bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDE bool fromwire_status_version(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, wirestring **version UNNEEDED) { fprintf(stderr, "fromwire_status_version called!\n"); abort(); } /* Generated stub for gossip_init */ -void gossip_init(struct lightningd *ld UNNEEDED, int connectd_fd UNNEEDED) +void gossip_init(struct lightningd *ld UNNEEDED, int connectd_fd UNNEEDED, int connectd_fd2 UNNEEDED) { fprintf(stderr, "gossip_init called!\n"); abort(); } /* Generated stub for gossip_notify_new_block */ void gossip_notify_new_block(struct lightningd *ld UNNEEDED, u32 blockheight UNNEEDED) From 9983c2fd8e88239ae7eb5108522c6161792bf55b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:37:52 +1030 Subject: [PATCH 0273/1530] gossipd: add routines to send gossip messages to and from connectd. Signed-off-by: Rusty Russell --- connectd/connectd.c | 13 ++- connectd/connectd_gossipd_wire.csv | 12 +++ gossipd/gossipd.c | 143 ++++++++++++++++++++++++++++- gossipd/gossipd.h | 2 + gossipd/test/run-onion_message.c | 6 ++ 5 files changed, 173 insertions(+), 3 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 64dac103535d..babdd5c00ed8 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -2007,7 +2007,18 @@ static struct io_plan *recv_gossip(struct io_conn *conn, const u8 *msg, struct daemon *daemon) { - /* FIXME! */ + struct node_id dst; + u8 *gossip_msg; + struct peer *peer; + + if (!fromwire_gossipd_send_gossip(msg, msg, &dst, &gossip_msg)) + status_failed(STATUS_FAIL_GOSSIP_IO, "Unknown msg %i", + fromwire_peektype(msg)); + + peer = peer_htable_get(&daemon->peers, &dst); + if (peer) + queue_peer_msg(peer, take(gossip_msg)); + return daemon_conn_read_next(conn, daemon->gossipd); } diff --git a/connectd/connectd_gossipd_wire.csv b/connectd/connectd_gossipd_wire.csv index 5517b450afde..bdb9a75f5292 100644 --- a/connectd/connectd_gossipd_wire.csv +++ b/connectd/connectd_gossipd_wire.csv @@ -11,3 +11,15 @@ msgdata,gossipd_new_peer,gossip_queries_feature,bool, # if success: + gossip fd msgtype,gossipd_new_peer_reply,4100 msgdata,gossipd_new_peer_reply,success,bool, + +# connectd tells gossipd a gossip msg it received for peer. +msgtype,gossipd_recv_gossip,4002 +msgdata,gossipd_recv_gossip,id,node_id, +msgdata,gossipd_recv_gossip,len,u16, +msgdata,gossipd_recv_gossip,msg,byte,len + +# Gossipd asks connectd to send a gossip msg for peer. +msgtype,gossipd_send_gossip,4102 +msgdata,gossipd_send_gossip,id,node_id, +msgdata,gossipd_send_gossip,len,u16, +msgdata,gossipd_send_gossip,msg,byte,len diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index a27e6ccb81ef..3e90a2dae9b0 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -118,10 +118,14 @@ void peer_supplied_good_gossip(struct peer *peer, size_t amount) peer->gossip_counter += amount; } -/* Queue a gossip message for the peer: the subdaemon on the other end simply - * forwards it to the peer. */ +/* Queue a gossip message for the peer: connectd simply forwards it to + * the peer. */ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) { + u8 *outermsg = towire_gossipd_send_gossip(NULL, &peer->id, msg); + daemon_conn_send(peer->daemon->connectd2, take(outermsg)); + + /* FIXME: backwards compat! */ daemon_conn_send(peer->dc, msg); } @@ -857,6 +861,108 @@ static struct io_plan *handle_get_address(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->master); } +static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) +{ + struct node_id id; + u8 *msg; + const u8 *err; + struct peer *peer; + + if (!fromwire_gossipd_recv_gossip(outermsg, outermsg, &id, &msg)) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Bad gossipd_recv_gossip msg from connectd: %s", + tal_hex(tmpctx, outermsg)); + } + + /* FIXME: happens when peer closes! */ + peer = find_peer(daemon, &id); + if (!peer) { + status_debug("connectd sent gossip msg %s for unknown peer %s", + peer_wire_name(fromwire_peektype(msg)), + type_to_string(tmpctx, struct node_id, &id)); + return; + } + + /* These are messages relayed from peer */ + switch ((enum peer_wire)fromwire_peektype(msg)) { + case WIRE_CHANNEL_ANNOUNCEMENT: + err = handle_channel_announcement_msg(peer->daemon, peer, msg); + goto handled_msg; + case WIRE_CHANNEL_UPDATE: + err = handle_channel_update_msg(peer, msg); + goto handled_msg; + case WIRE_NODE_ANNOUNCEMENT: + err = handle_node_announce(peer, msg); + goto handled_msg; + case WIRE_QUERY_CHANNEL_RANGE: + err = handle_query_channel_range(peer, msg); + goto handled_msg; + case WIRE_REPLY_CHANNEL_RANGE: + err = handle_reply_channel_range(peer, msg); + goto handled_msg; + case WIRE_QUERY_SHORT_CHANNEL_IDS: + err = handle_query_short_channel_ids(peer, msg); + goto handled_msg; + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: + err = handle_reply_short_channel_ids_end(peer, msg); + goto handled_msg; + case WIRE_OBS2_ONION_MESSAGE: + err = handle_obs2_onion_message(peer, msg); + goto handled_msg; + case WIRE_ONION_MESSAGE: + err = handle_onion_message(peer, msg); + goto handled_msg; + + /* These are non-gossip messages (!is_msg_for_gossipd()) */ + case WIRE_WARNING: + case WIRE_INIT: + case WIRE_ERROR: + case WIRE_PING: + case WIRE_PONG: + case WIRE_OPEN_CHANNEL: + case WIRE_ACCEPT_CHANNEL: + case WIRE_FUNDING_CREATED: + case WIRE_FUNDING_SIGNED: + case WIRE_FUNDING_LOCKED: + case WIRE_SHUTDOWN: + case WIRE_CLOSING_SIGNED: + case WIRE_UPDATE_ADD_HTLC: + case WIRE_UPDATE_FULFILL_HTLC: + case WIRE_UPDATE_FAIL_HTLC: + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + case WIRE_COMMITMENT_SIGNED: + case WIRE_REVOKE_AND_ACK: + case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: + case WIRE_CHANNEL_REESTABLISH: + case WIRE_ANNOUNCEMENT_SIGNATURES: + case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_TX_ADD_INPUT: + case WIRE_TX_REMOVE_INPUT: + case WIRE_TX_ADD_OUTPUT: + case WIRE_TX_REMOVE_OUTPUT: + case WIRE_TX_COMPLETE: + case WIRE_TX_SIGNATURES: + case WIRE_OPEN_CHANNEL2: + case WIRE_ACCEPT_CHANNEL2: + case WIRE_INIT_RBF: + case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif + break; + } + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "connectd sent unexpected gossip msg %s for peer %s", + peer_wire_name(fromwire_peektype(msg)), + type_to_string(tmpctx, struct node_id, &peer->id)); + +handled_msg: + if (err) + queue_peer_msg(peer, take(err)); +} + /*~ connectd's input handler is very simple. */ static struct io_plan *connectd_req(struct io_conn *conn, const u8 *msg, @@ -868,8 +974,11 @@ static struct io_plan *connectd_req(struct io_conn *conn, case WIRE_GOSSIPD_NEW_PEER: return connectd_new_peer(conn, daemon, msg); + /* This is not for this fd! */ + case WIRE_GOSSIPD_RECV_GOSSIP: /* We send these, don't receive them. */ case WIRE_GOSSIPD_NEW_PEER_REPLY: + case WIRE_GOSSIPD_SEND_GOSSIP: break; } @@ -878,6 +987,33 @@ static struct io_plan *connectd_req(struct io_conn *conn, return io_close(conn); } +/*~ connectd's input handler is very simple. */ +static struct io_plan *connectd_gossip_req(struct io_conn *conn, + const u8 *msg, + struct daemon *daemon) +{ + enum connectd_gossipd_wire t = fromwire_peektype(msg); + + switch (t) { + case WIRE_GOSSIPD_RECV_GOSSIP: + handle_recv_gossip(daemon, msg); + goto handled; + + /* This is not for this fd! */ + case WIRE_GOSSIPD_NEW_PEER: + /* We send these, don't receive them. */ + case WIRE_GOSSIPD_NEW_PEER_REPLY: + case WIRE_GOSSIPD_SEND_GOSSIP: + break; + } + + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Bad msg from connectd2: %s", tal_hex(tmpctx, msg)); + +handled: + return daemon_conn_read_next(conn, daemon->connectd2); +} + /* BOLT #7: * * A node: @@ -1037,6 +1173,9 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) daemon->connectd = daemon_conn_new(daemon, CONNECTD_FD, connectd_req, NULL, daemon); + daemon->connectd2 = daemon_conn_new(daemon, CONNECTD2_FD, + connectd_gossip_req, NULL, daemon); + /* OK, we are ready. */ daemon_conn_send(daemon->master, take(towire_gossipd_init_reply(NULL))); diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index af1d435ae3e7..75b39b8c67a1 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -9,6 +9,7 @@ #define HSM_FD 3 /* connectd asks us for help finding nodes, and gossip fds for new peers */ #define CONNECTD_FD 4 +#define CONNECTD2_FD 5 struct chan; struct channel_update_timestamps; @@ -32,6 +33,7 @@ struct daemon { /* Connection to connect daemon. */ struct daemon_conn *connectd; + struct daemon_conn *connectd2; /* Routing information */ struct routing_state *rstate; diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index 2ad7a2f3e7c8..f1bedb5c7071 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -112,6 +112,9 @@ bool fromwire_gossipd_new_peer(const void *p UNNEEDED, struct node_id *id UNNEED /* Generated stub for fromwire_gossipd_outpoint_spent */ bool fromwire_gossipd_outpoint_spent(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) { fprintf(stderr, "fromwire_gossipd_outpoint_spent called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_recv_gossip */ +bool fromwire_gossipd_recv_gossip(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u8 **msg UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_recv_gossip called!\n"); abort(); } /* Generated stub for fromwire_gossipd_send_onionmsg */ bool fromwire_gossipd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *obs2 UNNEEDED, struct node_id *id UNNEEDED, u8 **onion UNNEEDED, struct pubkey *blinding UNNEEDED) { fprintf(stderr, "fromwire_gossipd_send_onionmsg called!\n"); abort(); } @@ -335,6 +338,9 @@ u8 *towire_gossipd_new_blockheight_reply(const tal_t *ctx UNNEEDED) /* Generated stub for towire_gossipd_new_peer_reply */ u8 *towire_gossipd_new_peer_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED) { fprintf(stderr, "towire_gossipd_new_peer_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_send_gossip */ +u8 *towire_gossipd_send_gossip(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "towire_gossipd_send_gossip called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, From d7cf38a80aa878d68ef0681fe334f84a11acb0f6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:38:52 +1030 Subject: [PATCH 0274/1530] connectd: divert gossip messages directly to gossipd. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 17 ++++++++++++++++- gossipd/gossipd.c | 30 ++++++++---------------------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index cdcb17a0c7bc..2d0ee88b2cbf 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +31,7 @@ #include #include #include +#include void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) { @@ -334,7 +337,7 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) return NULL; } -/* We only handle gossip_timestamp_filter for now */ +/* We handle gossip_timestamp_filter, and divert other gossip msgs to gossipd */ static bool handle_message_locally(struct peer *peer, const u8 *msg) { struct bitcoin_blkid chain_hash; @@ -347,6 +350,18 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) if (!fromwire_gossip_timestamp_filter(msg, &chain_hash, &first_timestamp, ×tamp_range)) { + /* Do we want to divert to gossipd? */ + if (is_msg_for_gossipd(msg)) { + u8 *gmsg = towire_gossipd_recv_gossip(NULL, + &peer->id, msg); + + /* gossipd doesn't log IO, so we log it here. */ + status_peer_io(LOG_IO_IN, &peer->id, msg); + + daemon_conn_send(peer->daemon->gossipd, take(gmsg)); + return true; + } + return false; } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 3e90a2dae9b0..88ab7bab3698 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -682,27 +682,6 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, /* These are messages relayed from peer */ switch ((enum peer_wire)fromwire_peektype(msg)) { - case WIRE_CHANNEL_ANNOUNCEMENT: - err = handle_channel_announcement_msg(peer->daemon, peer, msg); - goto handled_relay; - case WIRE_CHANNEL_UPDATE: - err = handle_channel_update_msg(peer, msg); - goto handled_relay; - case WIRE_NODE_ANNOUNCEMENT: - err = handle_node_announce(peer, msg); - goto handled_relay; - case WIRE_QUERY_CHANNEL_RANGE: - err = handle_query_channel_range(peer, msg); - goto handled_relay; - case WIRE_REPLY_CHANNEL_RANGE: - err = handle_reply_channel_range(peer, msg); - goto handled_relay; - case WIRE_QUERY_SHORT_CHANNEL_IDS: - err = handle_query_short_channel_ids(peer, msg); - goto handled_relay; - case WIRE_REPLY_SHORT_CHANNEL_IDS_END: - err = handle_reply_short_channel_ids_end(peer, msg); - goto handled_relay; case WIRE_OBS2_ONION_MESSAGE: err = handle_obs2_onion_message(peer, msg); goto handled_relay; @@ -710,7 +689,14 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, err = handle_onion_message(peer, msg); goto handled_relay; - /* These are non-gossip messages (!is_msg_for_gossipd()) */ + /* These are not sent by peer (connectd sends us gossip msgs) */ + case WIRE_CHANNEL_ANNOUNCEMENT: + case WIRE_CHANNEL_UPDATE: + case WIRE_NODE_ANNOUNCEMENT: + case WIRE_QUERY_CHANNEL_RANGE: + case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_QUERY_SHORT_CHANNEL_IDS: + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_WARNING: case WIRE_INIT: case WIRE_ERROR: From 797da4805f1a7512119ffb102c2818356156c843 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 25 Jan 2022 06:39:52 +1030 Subject: [PATCH 0275/1530] gossipd: send all gossip msgs directly to connectd, not peer. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 12 ++++++------ gossipd/queries.c | 22 +++++++++++++++++++--- gossipd/queries.h | 4 ++-- gossipd/test/run-onion_message.c | 2 +- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 88ab7bab3698..0e8153ffab6c 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -125,8 +125,8 @@ void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) u8 *outermsg = towire_gossipd_send_gossip(NULL, &peer->id, msg); daemon_conn_send(peer->daemon->connectd2, take(outermsg)); - /* FIXME: backwards compat! */ - daemon_conn_send(peer->dc, msg); + if (taken(msg)) + tal_free(msg); } /*~ We have a helper for messages from the store. */ @@ -798,11 +798,10 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, list_add_tail(&peer->daemon->peers, &peer->list); tal_add_destructor(peer, destroy_peer); - /* This is the new connection: calls maybe_send_query_responses when - * nothing else to send. */ + /* This is the new connection. */ peer->dc = daemon_conn_new(daemon, fds[0], peer_msg_in, - maybe_send_query_responses, peer); + NULL, peer); /* Free peer if conn closed (destroy_peer closes conn if peer freed) */ tal_steal(peer->dc, peer); @@ -1157,7 +1156,8 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) /* connectd is already started, and uses this fd to ask us things. */ daemon->connectd = daemon_conn_new(daemon, CONNECTD_FD, - connectd_req, NULL, daemon); + connectd_req, + maybe_send_query_responses, daemon); daemon->connectd2 = daemon_conn_new(daemon, CONNECTD2_FD, connectd_gossip_req, NULL, daemon); diff --git a/gossipd/queries.c b/gossipd/queries.c index ccd8e2dc6cca..436ee9b2c4b4 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -335,8 +335,8 @@ const u8 *handle_query_short_channel_ids(struct peer *peer, const u8 *msg) peer->scid_query_idx = 0; peer->scid_query_nodes = tal_arr(peer, struct node_id, 0); - /* Notify the daemon_conn-write loop to invoke create_next_scid_reply */ - daemon_conn_wake(peer->dc); + /* Notify the daemon_conn-write loop to invoke maybe_send_query_responses_peer */ + daemon_conn_wake(peer->daemon->connectd); return NULL; } @@ -985,7 +985,7 @@ static void uniquify_node_ids(struct node_id **ids) /* We are fairly careful to avoid the peer DoSing us with channel queries: * this routine sends information about a single short_channel_id, unless * it's finished all of them. */ -void maybe_send_query_responses(struct peer *peer) +static bool maybe_send_query_responses_peer(struct peer *peer) { struct routing_state *rstate = peer->daemon->rstate; size_t i, num; @@ -1119,6 +1119,22 @@ void maybe_send_query_responses(struct peer *peer) peer->scid_query_nodes = tal_free(peer->scid_query_nodes); peer->scid_query_nodes_idx = 0; } + return sent; +} + +void maybe_send_query_responses(struct daemon *daemon) +{ + /* Rotate through, so we don't favor a single peer. */ + struct list_head used; + struct peer *p; + + list_head_init(&used); + while ((p = list_pop(&daemon->peers, struct peer, list)) != NULL) { + list_add(&used, &p->list); + if (maybe_send_query_responses_peer(p)) + break; + } + list_append_list(&daemon->peers, &used); } bool query_channel_range(struct daemon *daemon, diff --git a/gossipd/queries.h b/gossipd/queries.h index 14ea121ae64e..1e4019f11115 100644 --- a/gossipd/queries.h +++ b/gossipd/queries.h @@ -16,8 +16,8 @@ const u8 *handle_reply_short_channel_ids_end(struct peer *peer, const u8 *msg); const u8 *handle_query_channel_range(struct peer *peer, const u8 *msg); const u8 *handle_reply_channel_range(struct peer *peer, const u8 *msg); -/* This called when the peer is idle. */ -void maybe_send_query_responses(struct peer *peer); +/* This called when the connectd is idle. */ +void maybe_send_query_responses(struct daemon *daemon); /* BOLT #7: * diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c index f1bedb5c7071..13e47d554e91 100644 --- a/gossipd/test/run-onion_message.c +++ b/gossipd/test/run-onion_message.c @@ -210,7 +210,7 @@ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) void maybe_send_own_node_announce(struct daemon *daemon UNNEEDED, bool startup UNNEEDED) { fprintf(stderr, "maybe_send_own_node_announce called!\n"); abort(); } /* Generated stub for maybe_send_query_responses */ -void maybe_send_query_responses(struct peer *peer UNNEEDED) +void maybe_send_query_responses(struct daemon *daemon UNNEEDED) { fprintf(stderr, "maybe_send_query_responses called!\n"); abort(); } /* Generated stub for memleak_find_allocations */ struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, From 0ac5c4f8dff4b2b9194dbb452f90cc74fa171378 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:01:19 +1030 Subject: [PATCH 0276/1530] pytest: ignore pings when doing query_gossip. Next patch starts a timeout ping, which can interfere with results. In theory, we should reply, but in practice (so far!) we seem to get enough time that it doesn't hang up on us. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 54 ++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index c0210f7f28ea..9224375954b6 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -271,7 +271,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, '0', '0xFFFFFFFF', - filters=['0109']) + filters=['0109', '0012']) # 0x0100 = channel_announcement # 0x0102 = channel_update @@ -284,7 +284,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, '0', before_anything - backdate, - filters=['0109']) + filters=['0109', '0012']) assert msgs == [] # Now choose range which will only give first update. @@ -292,7 +292,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): genesis_blockhash, before_anything - backdate, after_12 - before_anything + 1, - filters=['0109']) + filters=['0109', '0012']) # 0x0100 = channel_announcement # 0x0102 = channel_update @@ -306,7 +306,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): genesis_blockhash, after_12 - backdate, after_23 - after_12 + 1, - filters=['0109']) + filters=['0109', '0012']) # 0x0100 = channel_announcement # 0x0102 = channel_update @@ -703,7 +703,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', chainparams['chain_hash'], 0, 1000000, - filters=['0109']) + filters=['0109', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12, scid23], check=True, timeout=TIMEOUT, @@ -722,7 +722,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, 0, block12, - filters=['0109']) + filters=['0109', '0012']) # reply_channel_range == 264 assert msgs == ['0108' # blockhash @@ -736,7 +736,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, 0, block12 + 1, - filters=['0109']) + filters=['0109', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12], check=True, timeout=TIMEOUT, @@ -755,7 +755,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, 0, block23, - filters=['0109']) + filters=['0109', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12], check=True, timeout=TIMEOUT, @@ -774,7 +774,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, block12, block23 - block12 + 1, - filters=['0109']) + filters=['0109', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12, scid23], check=True, timeout=TIMEOUT, @@ -793,7 +793,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, block23, 1, - filters=['0109']) + filters=['0109', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid23], check=True, timeout=TIMEOUT, @@ -812,7 +812,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, block23 + 1, 1000000, - filters=['0109']) + filters=['0109', '0012']) # reply_channel_range == 264 assert msgs == ['0108' # blockhash @@ -829,7 +829,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, 0, 1000000, - filters=['0109']) + filters=['0109', '0012']) # It should definitely have split l2.daemon.wait_for_log('reply_channel_range: splitting 0-1 of 2') @@ -854,7 +854,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, 1, 429496000, - filters=['0109']) + filters=['0109', '0012']) assert len(msgs) == 2 # This should actually be large enough for zlib to kick in! @@ -870,7 +870,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l2.query_gossip('query_channel_range', genesis_blockhash, 0, 65535, - filters=['0109']) + filters=['0109', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '01', scid12, scid23, scid34], check=True, timeout=TIMEOUT, @@ -957,7 +957,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): msgs = l1.query_gossip('query_short_channel_ids', chain_hash, encoded, - filters=['0109']) + filters=['0109', '0012']) # Should just get the WIRE_REPLY_SHORT_CHANNEL_IDS_END = 262 # (with chainhash and completeflag = 1) @@ -980,7 +980,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): msgs = l1.query_gossip('query_short_channel_ids', chain_hash, encoded, - filters=['0109']) + filters=['0109', '0012']) assert len(msgs) == 6 # 0x0100 = channel_announcement @@ -1000,7 +1000,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): msgs = l1.query_gossip('query_short_channel_ids', chain_hash, encoded, - filters=['0109']) + filters=['0109', '0012']) # Technically, this order could be different, but this matches code. assert len(msgs) == 10 @@ -1273,7 +1273,8 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): '0', '0xFFFFFFFF', # Filter out gossip_timestamp_filter, # channel_announcement and channel_updates. - filters=['0109', '0102', '0100']) + # And pings. + filters=['0109', '0102', '0100', '0012']) assert len(msgs) == 2 assert (bytes("SENIORBEAM", encoding="utf8").hex() in msgs[0] @@ -1287,7 +1288,8 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): '0', '0xFFFFFFFF', # Filter out gossip_timestamp_filter, # channel_announcement and channel_updates. - filters=['0109', '0102', '0100']) + # And pings. + filters=['0109', '0102', '0100', '0012']) assert msgs == msgs2 # Won't have queued up another one, either. assert not l1.daemon.is_in_log('node_announcement: delaying') @@ -1311,7 +1313,8 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): '0', '0xFFFFFFFF', # Filter out gossip_timestamp_filter, # channel_announcement and channel_updates. - filters=['0109', '0102', '0100']) + # And pings. + filters=['0109', '0102', '0100', '0012']) assert msgs != msgs2 @@ -1328,11 +1331,14 @@ def test_gossipwith(node_factory): num_msgs = 0 while len(out): l, t = struct.unpack('>HH', out[0:4]) - # channel_announcement node_announcement, channel_update or timestamp_filter - assert t == 256 or t == 257 or t == 258 or t == 265 out = out[2 + l:] - if t != 265: - num_msgs += 1 + + # Ignore pings, timestamp_filter + if t == 265 or t == 18: + continue + # channel_announcement node_announcement or channel_update + assert t == 256 or t == 257 or t == 258 + num_msgs += 1 # one channel announcement, two channel_updates, two node announcements. assert num_msgs == 5 From 50eccb6a12e49b9ae331a81823bb793659363e4a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:01:32 +1030 Subject: [PATCH 0277/1530] connectd: handle pings and pongs. Signed-off-by: Rusty Russell Changelog-Changed: JSON_RPC: `ping` now works with connected peers, even without a channel. --- channeld/channeld.c | 133 +--------------------- channeld/channeld_wire.csv | 11 -- connectd/Makefile | 1 + connectd/connectd.c | 5 + connectd/connectd.h | 16 +++ connectd/connectd_wire.csv | 12 ++ connectd/multiplex.c | 215 +++++++++++++++++++++++++++++++---- connectd/multiplex.h | 3 + lightningd/Makefile | 2 +- lightningd/channel_control.c | 5 - lightningd/connect_control.c | 2 + lightningd/ping.c | 82 +++---------- lightningd/ping.h | 9 -- 13 files changed, 249 insertions(+), 247 deletions(-) delete mode 100644 lightningd/ping.h diff --git a/channeld/channeld.c b/channeld/channeld.c index c0a2d46857bb..01736f526331 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -50,15 +49,6 @@ #define MASTER_FD STDIN_FILENO #define HSM_FD 5 -enum pong_expect_type { - /* We weren't expecting a ping reply */ - PONG_UNEXPECTED = 0, - /* We were expecting a ping reply due to ping command */ - PONG_EXPECTED_COMMAND = 1, - /* We were expecting a ping reply due to ping timer */ - PONG_EXPECTED_PROBING = 2, -}; - struct peer { struct per_peer_state *pps; bool funding_locked[NUM_SIDES]; @@ -110,12 +100,6 @@ struct peer { u64 commit_timer_attempts; u32 commit_msec; - /* Random ping timer, to detect dead connections. */ - struct oneshot *ping_timer; - - /* Are we expecting a pong? */ - enum pong_expect_type expecting_pong; - /* The feerate we want. */ u32 desired_feerate; @@ -1095,29 +1079,6 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, return htlc_sigs; } -/* Mutual recursion */ -static void send_ping(struct peer *peer); - -static void set_ping_timer(struct peer *peer) -{ - peer->ping_timer = new_reltimer(&peer->timers, peer, - time_from_sec(15 + pseudorand(30)), - send_ping, peer); -} - -static void send_ping(struct peer *peer) -{ - /* Already have a ping in flight? */ - if (peer->expecting_pong != PONG_UNEXPECTED) { - status_debug("Last ping unreturned: hanging up"); - exit(0); - } - - peer_write(peer->pps, take(make_ping(NULL, 1, 0))); - peer->expecting_pong = PONG_EXPECTED_PROBING; - set_ping_timer(peer); -} - /* Peer protocol doesn't want sighash flags. */ static secp256k1_ecdsa_signature *raw_sigs(const tal_t *ctx, const struct bitcoin_signature *sigs) @@ -2190,29 +2151,6 @@ static void handle_unexpected_reestablish(struct peer *peer, const u8 *msg) &channel_id)); } -static void handle_ping_reply(struct peer *peer, const u8 *msg) -{ - u8 *ignored; - size_t i; - - /* We print this out because we asked for pong, so can't spam us... */ - if (!fromwire_pong(msg, msg, &ignored)) - status_unusual("Got malformed ping reply %s", - tal_hex(tmpctx, msg)); - - /* We print this because dev versions of c-lightning embed - * version here: see check_ping_make_pong! */ - for (i = 0; i < tal_count(ignored); i++) { - if (ignored[i] < ' ' || ignored[i] == 127) - break; - } - status_debug("Got pong %zu bytes (%.*s...)", - tal_count(ignored), (int)i, (char *)ignored); - wire_sync_write(MASTER_FD, - take(towire_channeld_ping_reply(NULL, true, - tal_bytelen(msg)))); -} - static void peer_in(struct peer *peer, const u8 *msg) { enum peer_wire type = fromwire_peektype(msg); @@ -2298,19 +2236,6 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_INIT_RBF: case WIRE_ACK_RBF: break; - case WIRE_PONG: - switch (peer->expecting_pong) { - case PONG_EXPECTED_COMMAND: - handle_ping_reply(peer, msg); - /* fall thru */ - case PONG_EXPECTED_PROBING: - peer->expecting_pong = PONG_UNEXPECTED; - return; - case PONG_UNEXPECTED: - status_debug("Unexpected pong?"); - return; - } - abort(); case WIRE_CHANNEL_REESTABLISH: handle_unexpected_reestablish(peer, msg); @@ -2326,6 +2251,7 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_GOSSIP_TIMESTAMP_FILTER: case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_PING: + case WIRE_PONG: case WIRE_WARNING: case WIRE_ERROR: case WIRE_OBS2_ONION_MESSAGE: @@ -3608,57 +3534,6 @@ static void handle_send_error(struct peer *peer, const u8 *msg) take(towire_channeld_send_error_reply(NULL))); } -static void handle_send_ping(struct peer *peer, const u8 *msg) -{ - u8 *ping; - u16 len, num_pong_bytes; - - if (!fromwire_channeld_ping(msg, &num_pong_bytes, &len)) - master_badmsg(WIRE_CHANNELD_PING, msg); - - /* We're not supposed to send another ping until previous replied */ - if (peer->expecting_pong != PONG_UNEXPECTED) { - wire_sync_write(MASTER_FD, - take(towire_channeld_ping_reply(NULL, false, 0))); - return; - } - - /* It should never ask for an oversize ping. */ - ping = make_ping(NULL, num_pong_bytes, len); - if (tal_count(ping) > 65535) - status_failed(STATUS_FAIL_MASTER_IO, "Oversize ping"); - - peer_write(peer->pps, take(ping)); - - /* Since we're doing this manually, kill and restart timer. */ - status_debug("sending ping expecting %sresponse", - num_pong_bytes >= 65532 ? "no " : ""); - - /* BOLT #1: - * - * A node receiving a `ping` message: - *... - * - if `num_pong_bytes` is less than 65532: - * - MUST respond by sending a `pong` message, with `byteslen` equal - * to `num_pong_bytes`. - * - otherwise (`num_pong_bytes` is **not** less than 65532): - * - MUST ignore the `ping`. - */ - if (num_pong_bytes >= 65532) { - wire_sync_write(MASTER_FD, - take(towire_channeld_ping_reply(NULL, - true, 0))); - return; - } - - /* We'll respond to lightningd once the pong comes in */ - peer->expecting_pong = PONG_EXPECTED_COMMAND; - - /* Restart our timed pings now. */ - tal_free(peer->ping_timer); - set_ping_timer(peer); -} - #if DEVELOPER static void handle_dev_reenable_commit(struct peer *peer) { @@ -3757,9 +3632,6 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_SEND_ERROR: handle_send_error(peer, msg); return; - case WIRE_CHANNELD_PING: - handle_send_ping(peer, msg); - return; case WIRE_CHANNELD_CHANNEL_UPDATE: handle_channel_update(peer, msg); return; @@ -3798,7 +3670,6 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_SEND_ERROR_REPLY: case WIRE_CHANNELD_DEV_QUIESCE_REPLY: case WIRE_CHANNELD_UPGRADED: - case WIRE_CHANNELD_PING_REPLY: case WIRE_CHANNELD_USED_CHANNEL_UPDATE: case WIRE_CHANNELD_LOCAL_CHANNEL_UPDATE: case WIRE_CHANNELD_LOCAL_CHANNEL_ANNOUNCEMENT: @@ -4039,10 +3910,8 @@ int main(int argc, char *argv[]) status_setup_sync(MASTER_FD); peer = tal(NULL, struct peer); - peer->expecting_pong = PONG_UNEXPECTED; timers_init(&peer->timers, time_mono()); peer->commit_timer = NULL; - set_ping_timer(peer); peer->have_sigs[LOCAL] = peer->have_sigs[REMOTE] = false; peer->announce_depth_reached = false; peer->channel_local_active = false; diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 4770aea3d542..a444125ce069 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -266,14 +266,3 @@ msgdata,channeld_upgraded,new_type,channel_type, # Tell peer about our latest and greatest blockheight. msgtype,channeld_blockheight,1012 msgdata,channeld_blockheight,blockheight,u32, - -# Ping/pong test. Waits for a reply if it expects one. -msgtype,channeld_ping,1030 -msgdata,channeld_ping,num_pong_bytes,u16, -msgdata,channeld_ping,len,u16, - -msgtype,channeld_ping_reply,1130 -# False if we there was already a ping in progress. -msgdata,channeld_ping_reply,sent,bool, -# 0 == no pong expected, otherwise length of pong. -msgdata,channeld_ping_reply,totlen,u16, diff --git a/connectd/Makefile b/connectd/Makefile index fdf21e23e484..779a354a8c51 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -57,6 +57,7 @@ CONNECTD_COMMON_OBJS := \ common/msg_queue.o \ common/node_id.o \ common/onionreply.o \ + common/ping.o \ common/per_peer_state.o \ common/psbt_open.o \ common/pseudorand.o \ diff --git a/connectd/connectd.c b/connectd/connectd.c index babdd5c00ed8..1d9bdfe29882 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1966,6 +1966,10 @@ static struct io_plan *recv_req(struct io_conn *conn, peer_final_msg(conn, daemon, msg); goto out; + case WIRE_CONNECTD_PING: + send_manual_ping(daemon, msg); + goto out; + case WIRE_CONNECTD_DEV_MEMLEAK: #if DEVELOPER dev_connect_memleak(daemon, msg); @@ -1978,6 +1982,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_RECONNECTED: case WIRE_CONNECTD_CONNECT_FAILED: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: + case WIRE_CONNECTD_PING_REPLY: break; } diff --git a/connectd/connectd.h b/connectd/connectd.h index 86800f1e93ec..7b2eaa98da3d 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -27,6 +27,16 @@ struct gossip_state { size_t off; }; +/*~ We need to know if we were expecting a pong, and why */ +enum pong_expect_type { + /* We weren't expecting a ping reply */ + PONG_UNEXPECTED = 0, + /* We were expecting a ping reply due to ping command */ + PONG_EXPECTED_COMMAND = 1, + /* We were expecting a ping reply due to ping timer */ + PONG_EXPECTED_PROBING = 2, +}; + /*~ We keep a hash table (ccan/htable) of peers, which tells us what peers are * already connected (by peer->id). */ struct peer { @@ -65,6 +75,12 @@ struct peer { /* We stream from the gossip_store for them, when idle */ struct gossip_state gs; + /* Are we expecting a pong? */ + enum pong_expect_type expecting_pong; + + /* Random ping timer, to detect dead connections. */ + struct oneshot *ping_timer; + #if DEVELOPER bool dev_read_enabled; /* If non-NULL, this counts down; 0 means disable */ diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index deee01f4f4a3..b3f8f5b175e1 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -82,3 +82,15 @@ msgtype,connectd_dev_memleak,2033 msgtype,connectd_dev_memleak_reply,2133 msgdata,connectd_dev_memleak_reply,leak,bool, + +# Ping/pong test. Waits for a reply if it expects one. +msgtype,connectd_ping,2030 +msgdata,connectd_ping,id,node_id, +msgdata,connectd_ping,num_pong_bytes,u16, +msgdata,connectd_ping,len,u16, + +msgtype,connectd_ping_reply,2130 +# False if we there was already a ping in progress. +msgdata,connectd_ping_reply,sent,bool, +# 0 == no pong expected, otherwise length of pong. +msgdata,connectd_ping_reply,totlen,u16, diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 2d0ee88b2cbf..18010471a158 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -14,12 +14,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -337,32 +339,110 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) return NULL; } -/* We handle gossip_timestamp_filter, and divert other gossip msgs to gossipd */ -static bool handle_message_locally(struct peer *peer, const u8 *msg) +/* Mutual recursion */ +static void send_ping(struct peer *peer); + +static void set_ping_timer(struct peer *peer) +{ + peer->ping_timer = new_reltimer(&peer->daemon->timers, peer, + time_from_sec(15 + pseudorand(30)), + send_ping, peer); +} + +static void send_ping(struct peer *peer) +{ + /* Already have a ping in flight? */ + if (peer->expecting_pong != PONG_UNEXPECTED) { + status_peer_debug(&peer->id, "Last ping unreturned: hanging up"); + if (peer->to_peer) + io_close(peer->to_peer); + return; + } + + queue_peer_msg(peer, take(make_ping(NULL, 1, 0))); + peer->expecting_pong = PONG_EXPECTED_PROBING; + set_ping_timer(peer); +} + +static void handle_ping_in(struct peer *peer, const u8 *msg) +{ + u8 *pong; + + /* gossipd doesn't log IO, so we log it here. */ + status_peer_io(LOG_IO_IN, &peer->id, msg); + + if (!check_ping_make_pong(NULL, msg, &pong)) { + send_warning(peer, "Invalid ping %s", tal_hex(msg, msg)); + return; + } + + if (pong) + queue_peer_msg(peer, take(pong)); +} + +static void handle_ping_reply(struct peer *peer, const u8 *msg) +{ + u8 *ignored; + size_t i; + + /* We print this out because we asked for pong, so can't spam us... */ + if (!fromwire_pong(msg, msg, &ignored)) + status_peer_unusual(&peer->id, "Got malformed ping reply %s", + tal_hex(tmpctx, msg)); + + /* We print this because dev versions of c-lightning embed + * version here: see check_ping_make_pong! */ + for (i = 0; i < tal_count(ignored); i++) { + if (ignored[i] < ' ' || ignored[i] == 127) + break; + } + status_debug("Got pong %zu bytes (%.*s...)", + tal_count(ignored), (int)i, (char *)ignored); + daemon_conn_send(peer->daemon->master, + take(towire_connectd_ping_reply(NULL, true, + tal_bytelen(msg)))); +} + +static void handle_pong_in(struct peer *peer, const u8 *msg) +{ + /* gossipd doesn't log IO, so we log it here. */ + status_peer_io(LOG_IO_IN, &peer->id, msg); + + switch (peer->expecting_pong) { + case PONG_EXPECTED_COMMAND: + handle_ping_reply(peer, msg); + /* fall thru */ + case PONG_EXPECTED_PROBING: + peer->expecting_pong = PONG_UNEXPECTED; + return; + case PONG_UNEXPECTED: + status_debug("Unexpected pong?"); + return; + } + abort(); +} + +/* Forward to gossipd */ +static void handle_gossip_in(struct peer *peer, const u8 *msg) +{ + u8 *gmsg = towire_gossipd_recv_gossip(NULL, &peer->id, msg); + + /* gossipd doesn't log IO, so we log it here. */ + status_peer_io(LOG_IO_IN, &peer->id, msg); + daemon_conn_send(peer->daemon->gossipd, take(gmsg)); +} + +static void handle_gossip_timetamp_filter_in(struct peer *peer, const u8 *msg) { struct bitcoin_blkid chain_hash; u32 first_timestamp, timestamp_range; - /* We remember these so we don't rexmit them */ - if (is_msg_gossip_broadcast(msg)) - gossip_rcvd_filter_add(peer->gs.grf, msg); - if (!fromwire_gossip_timestamp_filter(msg, &chain_hash, &first_timestamp, ×tamp_range)) { - /* Do we want to divert to gossipd? */ - if (is_msg_for_gossipd(msg)) { - u8 *gmsg = towire_gossipd_recv_gossip(NULL, - &peer->id, msg); - - /* gossipd doesn't log IO, so we log it here. */ - status_peer_io(LOG_IO_IN, &peer->id, msg); - - daemon_conn_send(peer->daemon->gossipd, take(gmsg)); - return true; - } - - return false; + send_warning(peer, "gossip_timestamp_filter invalid: %s", + tal_hex(tmpctx, msg)); + return; } /* gossipd doesn't log IO, so we log it here. */ @@ -371,7 +451,7 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) if (!bitcoin_blkid_eq(&chainparams->genesis_blockhash, &chain_hash)) { send_warning(peer, "gossip_timestamp_filter for bad chain: %s", tal_hex(tmpctx, msg)); - return true; + return; } peer->gs.timestamp_min = first_timestamp; @@ -388,8 +468,35 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) /* We send immediately the first time, after that we wait. */ if (!peer->gs.gossip_timer) wake_gossip(peer); +} - return true; +/* We handle pings and gossip messages. */ +static bool handle_message_locally(struct peer *peer, const u8 *msg) +{ + enum peer_wire type = fromwire_peektype(msg); + + /* We remember these so we don't rexmit them */ + if (is_msg_gossip_broadcast(msg)) + gossip_rcvd_filter_add(peer->gs.grf, msg); + + if (type == WIRE_GOSSIP_TIMESTAMP_FILTER) { + handle_gossip_timetamp_filter_in(peer, msg); + return true; + } else if (type == WIRE_PING) { + handle_ping_in(peer, msg); + return true; + } else if (type == WIRE_PONG) { + handle_pong_in(peer, msg); + return true; + } + + /* Do we want to divert to gossipd? */ + if (is_msg_for_gossipd(msg)) { + handle_gossip_in(peer, msg); + return true; + } + + return false; } static void close_timeout(struct peer *peer) @@ -665,6 +772,10 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, * lightningd to tell us to close with the peer */ tal_add_destructor2(peer_conn, destroy_peer_conn, peer); + /* Start keepalives */ + peer->expecting_pong = PONG_UNEXPECTED; + set_ping_timer(peer); + return io_duplex(peer_conn, read_hdr_from_peer(peer_conn, peer), write_to_peer(peer_conn, peer)); @@ -677,3 +788,65 @@ void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) if (!peer->to_subd) io_wake(peer->peer_outq); } + +/* Lightningd says to send a ping */ +void send_manual_ping(struct daemon *daemon, const u8 *msg) +{ + u8 *ping; + struct node_id id; + u16 len, num_pong_bytes; + struct peer *peer; + + if (!fromwire_connectd_ping(msg, &id, &num_pong_bytes, &len)) + master_badmsg(WIRE_CONNECTD_PING, msg); + + peer = peer_htable_get(&daemon->peers, &id); + if (!peer) { + daemon_conn_send(daemon->master, + take(towire_connectd_ping_reply(NULL, + false, 0))); + return; + } + + /* We're not supposed to send another ping until previous replied */ + if (peer->expecting_pong != PONG_UNEXPECTED) { + daemon_conn_send(daemon->master, + take(towire_connectd_ping_reply(NULL, + false, 0))); + return; + } + + /* It should never ask for an oversize ping. */ + ping = make_ping(NULL, num_pong_bytes, len); + if (tal_count(ping) > 65535) + status_failed(STATUS_FAIL_MASTER_IO, "Oversize ping"); + + queue_peer_msg(peer, take(ping)); + + status_debug("sending ping expecting %sresponse", + num_pong_bytes >= 65532 ? "no " : ""); + + /* BOLT #1: + * + * A node receiving a `ping` message: + *... + * - if `num_pong_bytes` is less than 65532: + * - MUST respond by sending a `pong` message, with `byteslen` equal + * to `num_pong_bytes`. + * - otherwise (`num_pong_bytes` is **not** less than 65532): + * - MUST ignore the `ping`. + */ + if (num_pong_bytes >= 65532) { + daemon_conn_send(daemon->master, + take(towire_connectd_ping_reply(NULL, + true, 0))); + return; + } + + /* We'll respond to lightningd once the pong comes in */ + peer->expecting_pong = PONG_EXPECTED_COMMAND; + + /* Since we're doing this manually, kill and restart timer. */ + tal_free(peer->ping_timer); + set_ping_timer(peer); +} diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 0b3ba146a1ff..bbb67e75501e 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -30,4 +30,7 @@ void setup_peer_gossip_store(struct peer *peer, /* Start the process of flushing and closing the peer_conn */ void close_peer_conn(struct peer *peer); + +/* When lightningd says to send a ping */ +void send_manual_ping(struct daemon *daemon, const u8 *msg); #endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ diff --git a/lightningd/Makefile b/lightningd/Makefile index 557bfb9c2285..c6827849ed6b 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -31,7 +31,6 @@ LIGHTNINGD_SRC := \ lightningd/peer_control.c \ lightningd/peer_fd.c \ lightningd/peer_htlcs.c \ - lightningd/ping.c \ lightningd/plugin.c \ lightningd/plugin_control.c \ lightningd/plugin_hook.c \ @@ -42,6 +41,7 @@ LIGHTNINGD_SRC := \ LIGHTNINGD_SRC_NOHDR := \ lightningd/datastore.c \ + lightningd/ping.c \ lightningd/offer.c \ lightningd/signmessage.c diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 030b15b5eb2a..2e7792dc88a6 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -22,7 +22,6 @@ #include #include #include -#include #include static void update_feerates(struct lightningd *ld, struct channel *channel) @@ -519,9 +518,6 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_SEND_ERROR_REPLY: handle_error_channel(sd->channel, msg); break; - case WIRE_CHANNELD_PING_REPLY: - ping_reply(sd, msg); - break; case WIRE_CHANNELD_USED_CHANNEL_UPDATE: /* This tells gossipd we used it. */ get_channel_update(sd->channel); @@ -565,7 +561,6 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_DEV_MEMLEAK_REPLY: case WIRE_CHANNELD_SEND_ERROR: case WIRE_CHANNELD_DEV_QUIESCE_REPLY: - case WIRE_CHANNELD_PING: break; } diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index e46305595653..ed037461a03b 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -363,10 +363,12 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_PEER_DISCONNECTED: case WIRE_CONNECTD_DEV_MEMLEAK: case WIRE_CONNECTD_PEER_FINAL_MSG: + case WIRE_CONNECTD_PING: /* This is a reply, so never gets through to here. */ case WIRE_CONNECTD_INIT_REPLY: case WIRE_CONNECTD_ACTIVATE_REPLY: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: + case WIRE_CONNECTD_PING_REPLY: break; case WIRE_CONNECTD_RECONNECTED: diff --git a/lightningd/ping.c b/lightningd/ping.c index e97351595ae7..144dbe8fac47 100644 --- a/lightningd/ping.c +++ b/lightningd/ping.c @@ -1,83 +1,39 @@ #include "config.h" -#include #include #include #include +#include #include #include #include #include -#include #include -struct ping_command { - struct list_node list; - struct node_id id; - struct command *cmd; -}; - -static struct ping_command *find_ping_cmd(struct lightningd *ld, - const struct node_id *id) -{ - struct ping_command *i; - - list_for_each(&ld->ping_commands, i, list) { - if (node_id_eq(id, &i->id)) - return i; - } - return NULL; -} - -static void destroy_ping_command(struct ping_command *pc) -{ - list_del(&pc->list); -} - -static struct ping_command *new_ping_command(const tal_t *ctx, - struct lightningd *ld, - const struct node_id *peer_id, - struct command *cmd) -{ - struct ping_command *pc = tal(ctx, struct ping_command); - - pc->id = *peer_id; - pc->cmd = cmd; - list_add_tail(&ld->ping_commands, &pc->list); - tal_add_destructor(pc, destroy_ping_command); - - return pc; -} - -void ping_reply(struct subd *channeld, const u8 *msg) +static void ping_reply(struct subd *connectd, + const u8 *msg, const int *fds, + struct command *cmd) { u16 totlen; bool sent; - struct ping_command *pc; - struct channel *c = channeld->channel; - log_debug(channeld->log, "Got ping reply!"); - pc = find_ping_cmd(channeld->ld, &c->peer->id); - if (!pc) { - log_broken(channeld->log, "Unexpected ping reply?"); - return; - } + log_debug(connectd->log, "Got ping reply!"); - if (!fromwire_channeld_ping_reply(msg, &sent, &totlen)) { - log_broken(channeld->log, "Malformed ping reply %s", + if (!fromwire_connectd_ping_reply(msg, &sent, &totlen)) { + log_broken(connectd->log, "Malformed ping reply %s", tal_hex(tmpctx, msg)); - was_pending(command_fail(pc->cmd, LIGHTNINGD, + was_pending(command_fail(cmd, LIGHTNINGD, "Bad reply message")); return; } if (!sent) - was_pending(command_fail(pc->cmd, LIGHTNINGD, + was_pending(command_fail(cmd, LIGHTNINGD, "Ping already pending")); else { - struct json_stream *response = json_stream_success(pc->cmd); + struct json_stream *response = json_stream_success(cmd); json_add_num(response, "totlen", totlen); - was_pending(command_success(pc->cmd, response)); + was_pending(command_success(cmd, response)); } } @@ -88,8 +44,6 @@ static struct command_result *json_ping(struct command *cmd, { unsigned int *len, *pongbytes; struct node_id *id; - struct peer *peer; - struct channel *channel; u8 *msg; if (!param(cmd, buffer, params, @@ -124,19 +78,11 @@ static struct command_result *json_ping(struct command *cmd, "pongbytes %u > 65535", *pongbytes); } - peer = peer_by_id(cmd->ld, id); - if (!peer) + if (!peer_by_id(cmd->ld, id)) 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"); - - /* parent is cmd, so when we complete cmd, we free this. */ - new_ping_command(cmd, cmd->ld, id, cmd); - - msg = towire_channeld_ping(NULL, *pongbytes, *len); - subd_send_msg(channel->owner, take(msg)); + msg = towire_connectd_ping(NULL, id, *pongbytes, *len); + subd_req(cmd, cmd->ld->connectd, take(msg), -1, 0, ping_reply, cmd); return command_still_pending(cmd); } diff --git a/lightningd/ping.h b/lightningd/ping.h deleted file mode 100644 index fec5c90ee242..000000000000 --- a/lightningd/ping.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef LIGHTNING_LIGHTNINGD_PING_H -#define LIGHTNING_LIGHTNINGD_PING_H -#include "config.h" -#include - -struct subd; -void ping_reply(struct subd *subd, const u8 *msg); - -#endif /* LIGHTNING_LIGHTNINGD_PING_H */ From 8782d3947689d6391a7b4ccc6ad8d185a0906715 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:02:32 +1030 Subject: [PATCH 0278/1530] connectd: handle onion messages. Signed-off-by: Rusty Russell --- connectd/Makefile | 6 + connectd/connectd.c | 6 + connectd/connectd_wire.csv | 22 ++ connectd/multiplex.c | 7 + connectd/onion_message.c | 345 ++++++++++++++++++ connectd/onion_message.h | 15 + connectd/test/run-onion_message.c | 408 +++++++++++++++++++++ gossipd/Makefile | 6 - gossipd/gossipd.c | 356 +----------------- gossipd/gossipd_wire.csv | 19 - gossipd/test/run-onion_message.c | 588 ------------------------------ lightningd/connect_control.c | 6 + lightningd/gossip_control.c | 5 - lightningd/onion_message.c | 12 +- wire/peer_wire.c | 4 +- 15 files changed, 827 insertions(+), 978 deletions(-) create mode 100644 connectd/onion_message.c create mode 100644 connectd/onion_message.h create mode 100644 connectd/test/run-onion_message.c delete mode 100644 gossipd/test/run-onion_message.c diff --git a/connectd/Makefile b/connectd/Makefile index 779a354a8c51..8b1ea8907606 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -7,6 +7,7 @@ CONNECTD_HEADERS := connectd/connectd_wiregen.h \ connectd/handshake.h \ connectd/multiplex.h \ connectd/netaddress.h \ + connectd/onion_message.h \ connectd/tor_autoservice.h \ connectd/tor.h @@ -41,6 +42,8 @@ CONNECTD_COMMON_OBJS := \ common/bech32_util.o \ common/bigsize.o \ common/bip32.o \ + common/blinding.o \ + common/blindedpath.o \ common/channel_id.o \ common/cryptomsg.o \ common/daemon.o \ @@ -49,6 +52,7 @@ CONNECTD_COMMON_OBJS := \ common/dev_disconnect.o \ common/ecdh_hsmd.o \ common/features.o \ + common/hmac.o \ common/status_wiregen.o \ common/gossip_store.o \ common/gossip_rcvd_filter.o \ @@ -56,12 +60,14 @@ CONNECTD_COMMON_OBJS := \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ + common/onion.o \ common/onionreply.o \ common/ping.o \ common/per_peer_state.o \ common/psbt_open.o \ common/pseudorand.o \ common/setup.o \ + common/sphinx.o \ common/status.o \ common/status_wire.o \ common/subdaemon.o \ diff --git a/connectd/connectd.c b/connectd/connectd.c index 1d9bdfe29882..b8343f6c69c9 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -1970,6 +1971,10 @@ static struct io_plan *recv_req(struct io_conn *conn, send_manual_ping(daemon, msg); goto out; + case WIRE_CONNECTD_SEND_ONIONMSG: + onionmsg_req(daemon, msg); + goto out; + case WIRE_CONNECTD_DEV_MEMLEAK: #if DEVELOPER dev_connect_memleak(daemon, msg); @@ -1983,6 +1988,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_CONNECT_FAILED: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: case WIRE_CONNECTD_PING_REPLY: + case WIRE_CONNECTD_GOT_ONIONMSG_TO_US: break; } diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index b3f8f5b175e1..52dae09d0c54 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -3,6 +3,7 @@ #include #include #include +#include msgtype,connectd_init,2000 msgdata,connectd_init,chainparams,chainparams, @@ -94,3 +95,24 @@ msgtype,connectd_ping_reply,2130 msgdata,connectd_ping_reply,sent,bool, # 0 == no pong expected, otherwise length of pong. msgdata,connectd_ping_reply,totlen,u16, + +# We tell lightningd we got an onionmsg +msgtype,connectd_got_onionmsg_to_us,2145 +msgdata,connectd_got_onionmsg_to_us,obs2,bool, +msgdata,connectd_got_onionmsg_to_us,node_alias,pubkey, +msgdata,connectd_got_onionmsg_to_us,self_id,?secret, +msgdata,connectd_got_onionmsg_to_us,reply_blinding,?pubkey, +msgdata,connectd_got_onionmsg_to_us,reply_first_node,?pubkey, +msgdata,connectd_got_onionmsg_to_us,reply_path_len,u16, +msgdata,connectd_got_onionmsg_to_us,reply_path,onionmsg_path,reply_path_len +msgdata,connectd_got_onionmsg_to_us,rawmsg_len,u16, +msgdata,connectd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len + +# Lightningd tells us to send an onion message. +msgtype,connectd_send_onionmsg,2041 +msgdata,connectd_send_onionmsg,obs2,bool, +msgdata,connectd_send_onionmsg,id,node_id, +msgdata,connectd_send_onionmsg,onion_len,u16, +msgdata,connectd_send_onionmsg,onion,u8,onion_len +msgdata,connectd_send_onionmsg,blinding,pubkey, + diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 18010471a158..8637d01ff322 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -488,6 +489,12 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) } else if (type == WIRE_PONG) { handle_pong_in(peer, msg); return true; + } else if (type == WIRE_OBS2_ONION_MESSAGE) { + handle_obs2_onion_message(peer->daemon, peer, msg); + return true; + } else if (type == WIRE_ONION_MESSAGE) { + handle_onion_message(peer->daemon, peer, msg); + return true; } /* Do we want to divert to gossipd? */ diff --git a/connectd/onion_message.c b/connectd/onion_message.c new file mode 100644 index 000000000000..f19594899172 --- /dev/null +++ b/connectd/onion_message.c @@ -0,0 +1,345 @@ +/*~ This contains all the code to handle onion messages. */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Peer sends obsolete onion msg. */ +void handle_obs2_onion_message(struct daemon *daemon, + struct peer *peer, const u8 *msg) +{ + enum onion_wire badreason; + struct onionpacket *op; + struct pubkey blinding, ephemeral; + struct route_step *rs; + u8 *onion; + struct tlv_obs2_onionmsg_payload *om; + struct secret ss, onion_ss; + const u8 *cursor; + size_t max, maxlen; + + /* Ignore unless explicitly turned on. */ + if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], + OPT_ONION_MESSAGES)) + return; + + /* FIXME: ratelimit! */ + if (!fromwire_obs2_onion_message(msg, msg, &blinding, &onion)) { + queue_peer_msg(peer, + towire_warningfmt(NULL, NULL, + "Bad onion_message")); + return; + } + + /* We unwrap the onion now. */ + op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); + if (!op) { + status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", + onion_wire_name(badreason)); + return; + } + + ephemeral = op->ephemeralkey; + if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { + status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); + return; + } + + /* Now get onion shared secret and parse it. */ + ecdh(&ephemeral, &onion_ss); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + if (!rs) { + status_peer_debug(&peer->id, + "onion msg: can't process onionpacket ss=%s", + type_to_string(tmpctx, struct secret, &onion_ss)); + return; + } + + /* The raw payload is prepended with length in the modern onion. */ + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + if (!cursor) { + status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + if (maxlen > max) { + status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + om = tlv_obs2_onionmsg_payload_new(msg); + if (!fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)) { + status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + if (rs->nextcase == ONION_END) { + struct pubkey *reply_blinding, *first_node_id, me, alias; + const struct onionmsg_path **reply_path; + struct secret *self_id; + u8 *omsg; + + if (!pubkey_from_node_id(&me, &daemon->id)) { + status_broken("Failed to convert own id"); + return; + } + + /* Final enctlv is actually optional */ + if (!om->enctlv) { + alias = me; + self_id = NULL; + } else if (!decrypt_obs2_final_enctlv(tmpctx, &blinding, &ss, + om->enctlv, &me, &alias, + &self_id)) { + status_peer_debug(&peer->id, + "onion msg: failed to decrypt enctlv" + " %s", tal_hex(tmpctx, om->enctlv)); + return; + } + + if (om->reply_path) { + first_node_id = &om->reply_path->first_node_id; + reply_blinding = &om->reply_path->blinding; + reply_path = cast_const2(const struct onionmsg_path **, + om->reply_path->path); + } else { + first_node_id = NULL; + reply_blinding = NULL; + reply_path = NULL; + } + + /* We re-marshall here by policy, before handing to lightningd */ + omsg = tal_arr(tmpctx, u8, 0); + towire_tlvstream_raw(&omsg, om->fields); + daemon_conn_send(daemon->master, + take(towire_connectd_got_onionmsg_to_us(NULL, + true, /* obs2 */ + &alias, self_id, + reply_blinding, + first_node_id, + reply_path, + omsg))); + } else { + struct pubkey next_node, next_blinding; + struct peer *next_peer; + struct node_id next_node_id; + + /* This fails as expected if no enctlv. */ + if (!decrypt_obs2_enctlv(&blinding, &ss, om->enctlv, &next_node, + &next_blinding)) { + status_peer_debug(&peer->id, + "onion msg: invalid enctlv %s", + tal_hex(tmpctx, om->enctlv)); + return; + } + + /* Even though lightningd checks for valid ids, there's a race + * where it might vanish before we read this command. */ + node_id_from_pubkey(&next_node_id, &next_node); + next_peer = peer_htable_get(&daemon->peers, &next_node_id); + if (!next_peer) { + status_peer_debug(&peer->id, + "onion msg: unknown next peer %s", + type_to_string(tmpctx, + struct pubkey, + &next_node)); + return; + } + queue_peer_msg(next_peer, + take(towire_obs2_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); + } +} + +void onionmsg_req(struct daemon *daemon, const u8 *msg) +{ + struct node_id id; + u8 *onionmsg; + struct pubkey blinding; + struct peer *peer; + bool obs2; + + if (!fromwire_connectd_send_onionmsg(msg, msg, &obs2, &id, &onionmsg, &blinding)) + master_badmsg(WIRE_CONNECTD_SEND_ONIONMSG, msg); + + /* Even though lightningd checks for valid ids, there's a race + * where it might vanish before we read this command. */ + peer = peer_htable_get(&daemon->peers, &id); + if (peer) { + u8 *omsg; + if (obs2) + omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg); + else + omsg = towire_onion_message(NULL, &blinding, onionmsg); + queue_peer_msg(peer, take(omsg)); + } +} + +/* Peer sends an onion msg. */ +void handle_onion_message(struct daemon *daemon, + struct peer *peer, const u8 *msg) +{ + enum onion_wire badreason; + struct onionpacket *op; + struct pubkey blinding, ephemeral; + struct route_step *rs; + u8 *onion; + struct tlv_onionmsg_payload *om; + struct secret ss, onion_ss; + const u8 *cursor; + size_t max, maxlen; + + /* Ignore unless explicitly turned on. */ + if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], + OPT_ONION_MESSAGES)) + return; + + /* FIXME: ratelimit! */ + if (!fromwire_onion_message(msg, msg, &blinding, &onion)) { + queue_peer_msg(peer, + towire_warningfmt(NULL, NULL, + "Bad onion_message")); + return; + } + + /* We unwrap the onion now. */ + op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); + if (!op) { + status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", + onion_wire_name(badreason)); + return; + } + + ephemeral = op->ephemeralkey; + if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { + status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); + return; + } + + /* Now get onion shared secret and parse it. */ + ecdh(&ephemeral, &onion_ss); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + if (!rs) { + status_peer_debug(&peer->id, + "onion msg: can't process onionpacket ss=%s", + type_to_string(tmpctx, struct secret, &onion_ss)); + return; + } + + /* The raw payload is prepended with length in the modern onion. */ + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + if (!cursor) { + status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + if (maxlen > max) { + status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + om = tlv_onionmsg_payload_new(msg); + if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { + status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + if (rs->nextcase == ONION_END) { + struct pubkey *reply_blinding, *first_node_id, me, alias; + const struct onionmsg_path **reply_path; + struct secret *self_id; + u8 *omsg; + + if (!pubkey_from_node_id(&me, &daemon->id)) { + status_broken("Failed to convert own id"); + return; + } + + /* Final enctlv is actually optional */ + if (!om->encrypted_data_tlv) { + alias = me; + self_id = NULL; + } else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss, + om->encrypted_data_tlv, &me, &alias, + &self_id)) { + status_peer_debug(&peer->id, + "onion msg: failed to decrypt enctlv" + " %s", tal_hex(tmpctx, om->encrypted_data_tlv)); + return; + } + + if (om->reply_path) { + first_node_id = &om->reply_path->first_node_id; + reply_blinding = &om->reply_path->blinding; + reply_path = cast_const2(const struct onionmsg_path **, + om->reply_path->path); + } else { + first_node_id = NULL; + reply_blinding = NULL; + reply_path = NULL; + } + + /* We re-marshall here by policy, before handing to lightningd */ + omsg = tal_arr(tmpctx, u8, 0); + towire_tlvstream_raw(&omsg, om->fields); + daemon_conn_send(daemon->master, + take(towire_connectd_got_onionmsg_to_us(NULL, + false, /* !obs2 */ + &alias, self_id, + reply_blinding, + first_node_id, + reply_path, + omsg))); + } else { + struct pubkey next_node, next_blinding; + struct peer *next_peer; + struct node_id next_node_id; + + /* This fails as expected if no enctlv. */ + if (!decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, + &next_blinding)) { + status_peer_debug(&peer->id, + "onion msg: invalid enctlv %s", + tal_hex(tmpctx, om->encrypted_data_tlv)); + return; + } + + /* FIXME: Handle short_channel_id! */ + node_id_from_pubkey(&next_node_id, &next_node); + next_peer = peer_htable_get(&daemon->peers, &next_node_id); + if (!next_peer) { + status_peer_debug(&peer->id, + "onion msg: unknown next peer %s", + type_to_string(tmpctx, + struct pubkey, + &next_node)); + return; + } + queue_peer_msg(next_peer, + take(towire_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); + } +} + diff --git a/connectd/onion_message.h b/connectd/onion_message.h new file mode 100644 index 000000000000..22fc9bb8b4a4 --- /dev/null +++ b/connectd/onion_message.h @@ -0,0 +1,15 @@ +#ifndef LIGHTNING_CONNECTD_ONION_MESSAGE_H +#define LIGHTNING_CONNECTD_ONION_MESSAGE_H +#include "config.h" +#include + +/* Various messages come in from peer */ +void handle_obs2_onion_message(struct daemon *daemon, + struct peer *peer, const u8 *msg); +void handle_onion_message(struct daemon *daemon, + struct peer *peer, const u8 *msg); + +/* Lightningd tells us to send an onion message */ +void onionmsg_req(struct daemon *daemon, const u8 *msg); + +#endif /* LIGHTNING_CONNECTD_ONION_MESSAGE_H */ diff --git a/connectd/test/run-onion_message.c b/connectd/test/run-onion_message.c new file mode 100644 index 000000000000..8533d718f703 --- /dev/null +++ b/connectd/test/run-onion_message.c @@ -0,0 +1,408 @@ +#include "config.h" +#include "../onion_message.c" +#include "common/blindedpath.c" +#include "common/blinding.c" +#include "common/bigsize.c" +#include "common/hmac.c" +#include "common/onion.c" +#include "common/sphinx.c" +#include "wire/fromwire.c" +#if EXPERIMENTAL_FEATURES +#include "wire/peer_exp_wiregen.c" +#include "wire/onion_exp_wiregen.c" +#else +#include "wire/peer_wiregen.c" +#include "wire/onion_wiregen.c" +#endif +#include "wire/tlvstream.c" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_msat_eq */ +bool amount_msat_eq(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED) +{ fprintf(stderr, "amount_msat_eq called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_connectd_send_onionmsg */ +bool fromwire_connectd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *obs2 UNNEEDED, struct node_id *id UNNEEDED, u8 **onion UNNEEDED, struct pubkey *blinding UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_send_onionmsg called!\n"); abort(); } +/* Generated stub for fromwire_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for master_badmsg */ +void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) +{ fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } +/* Generated stub for node_id_from_pubkey */ +void node_id_from_pubkey(struct node_id *id UNNEEDED, const struct pubkey *key UNNEEDED) +{ fprintf(stderr, "node_id_from_pubkey called!\n"); abort(); } +/* Generated stub for pubkey_from_node_id */ +bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } +/* Generated stub for queue_peer_msg */ +void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +{ fprintf(stderr, "queue_peer_msg called!\n"); abort(); } +/* Generated stub for status_fmt */ +void status_fmt(enum log_level level UNNEEDED, + const struct node_id *peer UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_connectd_got_onionmsg_to_us */ +u8 *towire_connectd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEEDED, const struct pubkey *node_alias UNNEEDED, const struct secret *self_id UNNEEDED, const struct pubkey *reply_blinding UNNEEDED, const struct pubkey *reply_first_node UNNEEDED, const struct onionmsg_path **reply_path UNNEEDED, const u8 *rawmsg UNNEEDED) +{ fprintf(stderr, "towire_connectd_got_onionmsg_to_us called!\n"); abort(); } +/* Generated stub for towire_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_pad */ +void towire_pad(u8 **pptr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_pad called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_tu32 */ +void towire_tu32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_tu32 called!\n"); abort(); } +/* Generated stub for towire_tu64 */ +void towire_tu64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_tu64 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for towire_warningfmt */ +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(); } +/* AUTOGENERATED MOCKS END */ + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey->secret.data, NULL, NULL) != 1) + abort(); +} + +static void json_strfield(const char *name, const char *val) +{ + printf("\t\"%s\": \"%s\",\n", name, val); +} + +static void json_onionmsg_payload(const struct tlv_obs2_onionmsg_payload *om) +{ + if (om->reply_path) { + printf("\t\"reply_path\": {\n"); + json_strfield("first_node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->first_node_id)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->blinding)); + printf("\t\"path\": [\n"); + for (size_t i = 0; i < tal_count(om->reply_path->path); i++) { + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->path[i]->node_id)); + json_strfield("encrypted_recipient_data", + tal_hex(tmpctx, + om->reply_path->path[i]->encrypted_recipient_data)); + } + printf("]}\n"); + } + if (om->invoice) + json_strfield("invoice", tal_hex(tmpctx, om->invoice)); + if (om->invoice_request) + json_strfield("invoice_request", + tal_hex(tmpctx, om->invoice_request)); + if (om->invoice_error) + json_strfield("invoice_error", + tal_hex(tmpctx, om->invoice_error)); +} + +/* Return next onion (and updates blinding), or NULL */ +static u8 *json_test(const char *testname, + const u8 *data, + const struct privkey *me, + const struct privkey *blinding_priv, + struct pubkey *blinding) +{ + struct pubkey my_id, next_node; + struct secret ss, onion_ss; + struct pubkey ephemeral; + struct route_step *rs; + const u8 *cursor; + size_t max, maxlen; + struct onionpacket *op; + struct tlv_obs2_onionmsg_payload *om; + + op = parse_onionpacket(tmpctx, data, tal_bytelen(data), NULL); + assert(op); + + pubkey_from_privkey(me, &my_id); + printf("{"); + json_strfield("test name", testname); + json_strfield("reader_privkey", + type_to_string(tmpctx, struct privkey, me)); + json_strfield("reader_id", + type_to_string(tmpctx, struct pubkey, &my_id)); + + if (blinding_priv) + json_strfield("blinding_privkey", + type_to_string(tmpctx, struct privkey, + blinding_priv)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, blinding)); + printf("\"onionmsg\": {\n"); + json_strfield("raw", tal_hex(tmpctx, data)); + json_strfield("version", tal_fmt(tmpctx, "%i", op->version)); + json_strfield("public_key", + type_to_string(tmpctx, struct pubkey, &op->ephemeralkey)); + json_strfield("hop_payloads", + tal_hex(tmpctx, op->routinginfo)); + json_strfield("hmac", + tal_hexstr(tmpctx, &op->hmac, sizeof(op->hmac))); + printf("},\n"); + + ephemeral = op->ephemeralkey; + + /* Set this for test_ecdh */ + mykey = me; + assert(unblind_onion(blinding, test_ecdh, &ephemeral, &ss)); + json_strfield("ECDH shared secret", + type_to_string(tmpctx, struct secret, &ss)); + /* Reproduce internal calc from unblind_onion */ + { + struct secret hmac; + subkey_from_hmac("blinded_node_id", &ss, &hmac); + json_strfield("HMAC256(\\\"blinded_node_id\\\", ss(i)) * k(i)", + type_to_string(tmpctx, struct secret, &hmac)); + } + json_strfield("Tweaked onion pubkey", + type_to_string(tmpctx, struct pubkey, &ephemeral)); + + /* Now get onion shared secret and parse it. */ + test_ecdh(&ephemeral, &onion_ss); + json_strfield("onion shared secret", + type_to_string(tmpctx, struct secret, &onion_ss)); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + assert(rs); + + printf("\"onion contents\": {\n"); + json_strfield("raw", tal_hex(tmpctx, rs->raw_payload)); + + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + json_strfield("length", tal_fmt(tmpctx, "%zu", maxlen)); + json_strfield("rawtlv", tal_hexstr(tmpctx, cursor, maxlen)); + json_strfield("hmac", tal_hexstr(tmpctx, rs->next->hmac.bytes, + sizeof(rs->next->hmac.bytes))); + om = tlv_obs2_onionmsg_payload_new(tmpctx); + assert(fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)); + + json_onionmsg_payload(om); + + /* We expect one of these. */ + assert(om->enctlv); + + printf("\t\"encrypted_data_tlv\": {\n"); + json_strfield("raw", tal_hex(tmpctx, om->enctlv)); + + if (rs->nextcase == ONION_END) { + struct secret *self_id; + struct pubkey alias; + assert(decrypt_obs2_final_enctlv(tmpctx, + blinding, &ss, + om->enctlv, + &my_id, &alias, &self_id)); + if (self_id) { + json_strfield("self_id", + type_to_string(tmpctx, struct secret, + self_id)); + } + printf("}\n"); + return NULL; + } else { + assert(decrypt_obs2_enctlv(blinding, &ss, om->enctlv, &next_node, + blinding)); + json_strfield("next_node", + type_to_string(tmpctx, struct pubkey, &next_node)); + json_strfield("next_blinding", + type_to_string(tmpctx, struct pubkey, + blinding)); + printf("}"); + printf("},\n"); + return serialize_onionpacket(tmpctx, rs->next); + } +} + +int main(int argc, char *argv[]) +{ + struct onionpacket *op; + u8 *data; + struct privkey alice, bob, carol, dave, blinding_priv; + struct pubkey alice_id, bob_id, carol_id, dave_id; + struct pubkey blinding; + + common_setup(argv[0]); + + memset(&alice, 'A', sizeof(alice)); + memset(&bob, 'B', sizeof(bob)); + memset(&carol, 'C', sizeof(carol)); + memset(&dave, 'D', sizeof(dave)); + pubkey_from_privkey(&alice, &alice_id); + pubkey_from_privkey(&bob, &bob_id); + pubkey_from_privkey(&carol, &carol_id); + pubkey_from_privkey(&dave, &dave_id); + + /* ThomasH sends via email: + * + * { + * "version":0, + * "public_key": + * "0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", + * "hop_payloads": + * "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + * "hmac": "564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac" + * } + */ + op = tal(tmpctx, struct onionpacket); + op->version = 0; + assert(pubkey_from_hexstr("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", strlen("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967"), &op->ephemeralkey)); + assert(hex_decode("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac", + strlen("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac"), + &op->hmac, sizeof(op->hmac))); + op->routinginfo = tal_hexdata(op, "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + strlen("37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a")); + + data = serialize_onionpacket(tmpctx, op); + printf("[\n"); + + memset(&blinding_priv, 5, sizeof(blinding_priv)); + pubkey_from_privkey(&blinding_priv, &blinding); + + data = json_test("onion message for Alice", + data, + &alice, + &blinding_priv, + &blinding); + + data = json_test("onion message for Bob", + data, + &bob, + NULL, + &blinding); + + data = json_test("onion message for Carol", + data, + &carol, + NULL, + &blinding); + + data = json_test("onion message for Dave", + data, + &dave, + NULL, + &blinding); + + assert(!data); + printf("]\n"); + + common_shutdown(); + return 0; +} diff --git a/gossipd/Makefile b/gossipd/Makefile index 13c8eada0abb..6fbb6a7e450c 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -34,8 +34,6 @@ GOSSIPD_COMMON_OBJS := \ common/bech32_util.o \ common/bigsize.o \ common/bip32.o \ - common/blinding.o \ - common/blindedpath.o \ common/channel_id.o \ common/cryptomsg.o \ common/daemon.o \ @@ -45,7 +43,6 @@ GOSSIPD_COMMON_OBJS := \ common/dev_disconnect.o \ common/ecdh_hsmd.o \ common/features.o \ - common/hmac.o \ common/status_wiregen.o \ common/gossip_rcvd_filter.o \ common/key_derive.o \ @@ -53,8 +50,6 @@ GOSSIPD_COMMON_OBJS := \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ - common/onion.o \ - common/onionreply.o \ common/per_peer_state.o \ common/ping.o \ common/psbt_open.o \ @@ -62,7 +57,6 @@ GOSSIPD_COMMON_OBJS := \ common/private_channel_announcement.o \ common/random_select.o \ common/setup.o \ - common/sphinx.o \ common/status.o \ common/status_wire.o \ common/subdaemon.o \ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 0e8153ffab6c..d7797c068e0b 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -13,15 +13,12 @@ #include "config.h" #include #include -#include -#include #include #include #include #include #include #include -#include #include #include #include @@ -350,327 +347,6 @@ static void handle_local_private_channel(struct daemon *daemon, const u8 *msg) } } -/* Peer sends obsolete onion msg. */ -static u8 *handle_obs2_onion_message(struct peer *peer, const u8 *msg) -{ - enum onion_wire badreason; - struct onionpacket *op; - struct pubkey blinding, ephemeral; - struct route_step *rs; - u8 *onion; - struct tlv_obs2_onionmsg_payload *om; - struct secret ss, onion_ss; - const u8 *cursor; - size_t max, maxlen; - - /* Ignore unless explicitly turned on. */ - if (!feature_offered(peer->daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], - OPT_ONION_MESSAGES)) - return NULL; - - /* FIXME: ratelimit! */ - if (!fromwire_obs2_onion_message(msg, msg, &blinding, &onion)) - return towire_warningfmt(peer, NULL, "Bad onion_message"); - - /* We unwrap the onion now. */ - op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); - if (!op) { - status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", - onion_wire_name(badreason)); - return NULL; - } - - ephemeral = op->ephemeralkey; - if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { - status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); - return NULL; - } - - /* Now get onion shared secret and parse it. */ - ecdh(&ephemeral, &onion_ss); - rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); - if (!rs) { - status_peer_debug(&peer->id, - "onion msg: can't process onionpacket ss=%s", - type_to_string(tmpctx, struct secret, &onion_ss)); - return NULL; - } - - /* The raw payload is prepended with length in the modern onion. */ - cursor = rs->raw_payload; - max = tal_bytelen(rs->raw_payload); - maxlen = fromwire_bigsize(&cursor, &max); - if (!cursor) { - status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - if (maxlen > max) { - status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - - om = tlv_obs2_onionmsg_payload_new(msg); - if (!fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)) { - status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - - if (rs->nextcase == ONION_END) { - struct pubkey *reply_blinding, *first_node_id, me, alias; - const struct onionmsg_path **reply_path; - struct secret *self_id; - u8 *omsg; - - if (!pubkey_from_node_id(&me, &peer->daemon->id)) { - status_broken("Failed to convert own id"); - return NULL; - } - - /* Final enctlv is actually optional */ - if (!om->enctlv) { - alias = me; - self_id = NULL; - } else if (!decrypt_obs2_final_enctlv(tmpctx, &blinding, &ss, - om->enctlv, &me, &alias, - &self_id)) { - status_peer_debug(&peer->id, - "onion msg: failed to decrypt enctlv" - " %s", tal_hex(tmpctx, om->enctlv)); - return NULL; - } - - if (om->reply_path) { - first_node_id = &om->reply_path->first_node_id; - reply_blinding = &om->reply_path->blinding; - reply_path = cast_const2(const struct onionmsg_path **, - om->reply_path->path); - } else { - first_node_id = NULL; - reply_blinding = NULL; - reply_path = NULL; - } - - /* We re-marshall here by policy, before handing to lightningd */ - omsg = tal_arr(tmpctx, u8, 0); - towire_tlvstream_raw(&omsg, om->fields); - daemon_conn_send(peer->daemon->master, - take(towire_gossipd_got_onionmsg_to_us(NULL, - true, /* obs2 */ - &alias, self_id, - reply_blinding, - first_node_id, - reply_path, - omsg))); - } else { - struct pubkey next_node, next_blinding; - struct peer *next_peer; - struct node_id next_node_id; - - /* This fails as expected if no enctlv. */ - if (!decrypt_obs2_enctlv(&blinding, &ss, om->enctlv, &next_node, - &next_blinding)) { - status_peer_debug(&peer->id, - "onion msg: invalid enctlv %s", - tal_hex(tmpctx, om->enctlv)); - return NULL; - } - - /* Even though lightningd checks for valid ids, there's a race - * where it might vanish before we read this command. */ - node_id_from_pubkey(&next_node_id, &next_node); - next_peer = find_peer(peer->daemon, &next_node_id); - if (!next_peer) { - status_peer_debug(&peer->id, - "onion msg: unknown next peer %s", - type_to_string(tmpctx, - struct pubkey, - &next_node)); - return NULL; - } - queue_peer_msg(next_peer, - take(towire_obs2_onion_message(NULL, - &next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); - } - - return NULL; -} - -static void onionmsg_req(struct daemon *daemon, const u8 *msg) -{ - struct node_id id; - u8 *onionmsg; - struct pubkey blinding; - struct peer *peer; - bool obs2; - - if (!fromwire_gossipd_send_onionmsg(msg, msg, &obs2, &id, &onionmsg, &blinding)) - master_badmsg(WIRE_GOSSIPD_SEND_ONIONMSG, msg); - - /* Even though lightningd checks for valid ids, there's a race - * where it might vanish before we read this command. */ - peer = find_peer(daemon, &id); - if (peer) { - u8 *omsg; - if (obs2) - omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg); - else - omsg = towire_onion_message(NULL, &blinding, onionmsg); - queue_peer_msg(peer, take(omsg)); - } -} - -/* Peer sends an onion msg. */ -static u8 *handle_onion_message(struct peer *peer, const u8 *msg) -{ - enum onion_wire badreason; - struct onionpacket *op; - struct pubkey blinding, ephemeral; - struct route_step *rs; - u8 *onion; - struct tlv_onionmsg_payload *om; - struct secret ss, onion_ss; - const u8 *cursor; - size_t max, maxlen; - - /* Ignore unless explicitly turned on. */ - if (!feature_offered(peer->daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], - OPT_ONION_MESSAGES)) - return NULL; - - /* FIXME: ratelimit! */ - if (!fromwire_onion_message(msg, msg, &blinding, &onion)) - return towire_warningfmt(peer, NULL, "Bad onion_message"); - - /* We unwrap the onion now. */ - op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); - if (!op) { - status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", - onion_wire_name(badreason)); - return NULL; - } - - ephemeral = op->ephemeralkey; - if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { - status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); - return NULL; - } - - /* Now get onion shared secret and parse it. */ - ecdh(&ephemeral, &onion_ss); - rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); - if (!rs) { - status_peer_debug(&peer->id, - "onion msg: can't process onionpacket ss=%s", - type_to_string(tmpctx, struct secret, &onion_ss)); - return NULL; - } - - /* The raw payload is prepended with length in the modern onion. */ - cursor = rs->raw_payload; - max = tal_bytelen(rs->raw_payload); - maxlen = fromwire_bigsize(&cursor, &max); - if (!cursor) { - status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - if (maxlen > max) { - status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - - om = tlv_onionmsg_payload_new(msg); - if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { - status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return NULL; - } - - if (rs->nextcase == ONION_END) { - struct pubkey *reply_blinding, *first_node_id, me, alias; - const struct onionmsg_path **reply_path; - struct secret *self_id; - u8 *omsg; - - if (!pubkey_from_node_id(&me, &peer->daemon->id)) { - status_broken("Failed to convert own id"); - return NULL; - } - - /* Final enctlv is actually optional */ - if (!om->encrypted_data_tlv) { - alias = me; - self_id = NULL; - } else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss, - om->encrypted_data_tlv, &me, &alias, - &self_id)) { - status_peer_debug(&peer->id, - "onion msg: failed to decrypt enctlv" - " %s", tal_hex(tmpctx, om->encrypted_data_tlv)); - return NULL; - } - - if (om->reply_path) { - first_node_id = &om->reply_path->first_node_id; - reply_blinding = &om->reply_path->blinding; - reply_path = cast_const2(const struct onionmsg_path **, - om->reply_path->path); - } else { - first_node_id = NULL; - reply_blinding = NULL; - reply_path = NULL; - } - - /* We re-marshall here by policy, before handing to lightningd */ - omsg = tal_arr(tmpctx, u8, 0); - towire_tlvstream_raw(&omsg, om->fields); - daemon_conn_send(peer->daemon->master, - take(towire_gossipd_got_onionmsg_to_us(NULL, - false, /* !obs2 */ - &alias, self_id, - reply_blinding, - first_node_id, - reply_path, - omsg))); - } else { - struct pubkey next_node, next_blinding; - struct peer *next_peer; - struct node_id next_node_id; - - /* This fails as expected if no enctlv. */ - if (!decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, - &next_blinding)) { - status_peer_debug(&peer->id, - "onion msg: invalid enctlv %s", - tal_hex(tmpctx, om->encrypted_data_tlv)); - return NULL; - } - - /* FIXME: Handle short_channel_id! */ - node_id_from_pubkey(&next_node_id, &next_node); - next_peer = find_peer(peer->daemon, &next_node_id); - if (!next_peer) { - status_peer_debug(&peer->id, - "onion msg: unknown next peer %s", - type_to_string(tmpctx, - struct pubkey, - &next_node)); - return NULL; - } - queue_peer_msg(next_peer, - take(towire_onion_message(NULL, - &next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); - } - - return NULL; -} - /*~ This is where the per-peer daemons send us messages. It's either forwarded * gossip, or a request for information. We deliberately use non-overlapping * message types so we can distinguish them. */ @@ -678,17 +354,8 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, const u8 *msg, struct peer *peer) { - const u8 *err; - /* These are messages relayed from peer */ switch ((enum peer_wire)fromwire_peektype(msg)) { - case WIRE_OBS2_ONION_MESSAGE: - err = handle_obs2_onion_message(peer, msg); - goto handled_relay; - case WIRE_ONION_MESSAGE: - err = handle_onion_message(peer, msg); - goto handled_relay; - /* These are not sent by peer (connectd sends us gossip msgs) */ case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_CHANNEL_UPDATE: @@ -730,6 +397,8 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: case WIRE_ACK_RBF: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: #if EXPERIMENTAL_FEATURES case WIRE_STFU: #endif @@ -743,14 +412,6 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, status_peer_broken(&peer->id, "unexpected cmd of type %i", fromwire_peektype(msg)); return io_close(conn); - - /* Forwarded messages may be bad, so we have error which the per-peer - * daemon will forward to the peer. */ -handled_relay: - if (err) - queue_peer_msg(peer, take(err)); - - return daemon_conn_read_next(conn, peer->dc); } /*~ This is where connectd tells us about a new peer, and we hand back an fd for @@ -891,12 +552,6 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) case WIRE_REPLY_SHORT_CHANNEL_IDS_END: err = handle_reply_short_channel_ids_end(peer, msg); goto handled_msg; - case WIRE_OBS2_ONION_MESSAGE: - err = handle_obs2_onion_message(peer, msg); - goto handled_msg; - case WIRE_ONION_MESSAGE: - err = handle_onion_message(peer, msg); - goto handled_msg; /* These are non-gossip messages (!is_msg_for_gossipd()) */ case WIRE_WARNING: @@ -932,6 +587,8 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: case WIRE_ACK_RBF: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: #if EXPERIMENTAL_FEATURES case WIRE_STFU: #endif @@ -1464,16 +1121,11 @@ static struct io_plan *recv_req(struct io_conn *conn, break; #endif /* !DEVELOPER */ - case WIRE_GOSSIPD_SEND_ONIONMSG: - onionmsg_req(daemon, msg); - goto done; - /* We send these, we don't receive them */ case WIRE_GOSSIPD_INIT_REPLY: case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: - case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index eaec7643caa9..e5369751ed89 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -69,25 +69,6 @@ msgdata,gossipd_new_blockheight,blockheight,u32, # gossipd: got it! msgtype,gossipd_new_blockheight_reply,3126 -msgtype,gossipd_got_onionmsg_to_us,3145 -msgdata,gossipd_got_onionmsg_to_us,obs2,bool, -msgdata,gossipd_got_onionmsg_to_us,node_alias,pubkey, -msgdata,gossipd_got_onionmsg_to_us,self_id,?secret, -msgdata,gossipd_got_onionmsg_to_us,reply_blinding,?pubkey, -msgdata,gossipd_got_onionmsg_to_us,reply_first_node,?pubkey, -msgdata,gossipd_got_onionmsg_to_us,reply_path_len,u16, -msgdata,gossipd_got_onionmsg_to_us,reply_path,onionmsg_path,reply_path_len -msgdata,gossipd_got_onionmsg_to_us,rawmsg_len,u16, -msgdata,gossipd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len - -# Lightningd tells us to send an onion message. -msgtype,gossipd_send_onionmsg,3041 -msgdata,gossipd_send_onionmsg,obs2,bool, -msgdata,gossipd_send_onionmsg,id,node_id, -msgdata,gossipd_send_onionmsg,onion_len,u16, -msgdata,gossipd_send_onionmsg,onion,u8,onion_len -msgdata,gossipd_send_onionmsg,blinding,pubkey, - # Lightningd tells us to inject a gossip message (for addgossip RPC) msgtype,gossipd_addgossip,3044 msgdata,gossipd_addgossip,len,u16, diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c deleted file mode 100644 index 13e47d554e91..000000000000 --- a/gossipd/test/run-onion_message.c +++ /dev/null @@ -1,588 +0,0 @@ -#include "config.h" -int unused_main(int argc, char *argv[]); -#define main unused_main -#include "../gossipd.c" -#undef main -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if DEVELOPER -bool dev_suppress_gossip; - -/* Generated stub for dev_set_max_scids_encode_size */ -void dev_set_max_scids_encode_size(struct daemon *daemon UNNEEDED, - const u8 *msg UNNEEDED) -{ fprintf(stderr, "dev_set_max_scids_encode_size called!\n"); abort(); } -/* Generated stub for dump_memleak */ -bool dump_memleak(struct htable *memtable UNNEEDED, - void (*print)(const char *fmt UNNEEDED, ...)) -{ fprintf(stderr, "dump_memleak called!\n"); abort(); } -/* Generated stub for memleak_status_broken */ -void memleak_status_broken(const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "memleak_status_broken called!\n"); abort(); } -#endif - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for add_to_txout_failures */ -void add_to_txout_failures(struct routing_state *rstate UNNEEDED, - const struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "add_to_txout_failures called!\n"); abort(); } -/* Generated stub for daemon_conn_new_ */ -struct daemon_conn *daemon_conn_new_(const tal_t *ctx UNNEEDED, int fd UNNEEDED, - struct io_plan *(*recv)(struct io_conn * UNNEEDED, - const u8 * UNNEEDED, - void *) UNNEEDED, - void (*outq_empty)(void *) UNNEEDED, - void *arg UNNEEDED) -{ fprintf(stderr, "daemon_conn_new_ called!\n"); abort(); } -/* Generated stub for daemon_conn_read_next */ -struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, - struct daemon_conn *dc UNNEEDED) -{ fprintf(stderr, "daemon_conn_read_next called!\n"); abort(); } -/* Generated stub for daemon_conn_send */ -void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } -/* Generated stub for daemon_conn_send_fd */ -void daemon_conn_send_fd(struct daemon_conn *dc UNNEEDED, int fd UNNEEDED) -{ fprintf(stderr, "daemon_conn_send_fd called!\n"); abort(); } -/* Generated stub for daemon_shutdown */ -void daemon_shutdown(void) -{ fprintf(stderr, "daemon_shutdown called!\n"); abort(); } -/* Generated stub for ecdh */ -void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) -{ fprintf(stderr, "ecdh called!\n"); abort(); } -/* Generated stub for ecdh_hsmd_setup */ -void ecdh_hsmd_setup(int hsm_fd UNNEEDED, - void (*failed)(enum status_failreason UNNEEDED, - const char *fmt UNNEEDED, ...)) -{ fprintf(stderr, "ecdh_hsmd_setup called!\n"); abort(); } -/* Generated stub for first_chan */ -struct chan *first_chan(const struct node *node UNNEEDED, struct chan_map_iter *i UNNEEDED) -{ fprintf(stderr, "first_chan called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for free_chan */ -void free_chan(struct routing_state *rstate UNNEEDED, struct chan *chan UNNEEDED) -{ fprintf(stderr, "free_chan called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_addgossip */ -bool fromwire_gossipd_addgossip(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **msg UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_addgossip called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_dev_set_time */ -bool fromwire_gossipd_dev_set_time(const void *p UNNEEDED, u32 *dev_gossip_time UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_dev_set_time called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_dev_suppress */ -bool fromwire_gossipd_dev_suppress(const void *p UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_dev_suppress called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_get_addrs */ -bool fromwire_gossipd_get_addrs(const void *p UNNEEDED, struct node_id *id UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_get_addrs called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_get_txout_reply */ -bool fromwire_gossipd_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct amount_sat *satoshis UNNEEDED, u8 **outscript UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_get_txout_reply called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_init */ -bool fromwire_gossipd_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct feature_set **our_features UNNEEDED, struct node_id *id UNNEEDED, u8 rgb[3] UNNEEDED, u8 alias[32] UNNEEDED, struct wireaddr **announcable UNNEEDED, u32 **dev_gossip_time UNNEEDED, bool *dev_fast_gossip UNNEEDED, bool *dev_fast_gossip_prune UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_init called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_local_channel_announcement */ -bool fromwire_gossipd_local_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u8 **cannounce UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_local_channel_announcement called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_local_channel_close */ -bool fromwire_gossipd_local_channel_close(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_local_channel_close called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_local_private_channel */ -bool fromwire_gossipd_local_private_channel(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct amount_sat *capacity UNNEEDED, struct short_channel_id *scid UNNEEDED, u8 **features UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_local_private_channel called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_new_blockheight */ -bool fromwire_gossipd_new_blockheight(const void *p UNNEEDED, u32 *blockheight UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_new_blockheight called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_new_lease_rates */ -bool fromwire_gossipd_new_lease_rates(const void *p UNNEEDED, struct lease_rates *rates UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_new_lease_rates called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_new_peer */ -bool fromwire_gossipd_new_peer(const void *p UNNEEDED, struct node_id *id UNNEEDED, bool *gossip_queries_feature UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_new_peer called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_outpoint_spent */ -bool fromwire_gossipd_outpoint_spent(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_outpoint_spent called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_recv_gossip */ -bool fromwire_gossipd_recv_gossip(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u8 **msg UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_recv_gossip called!\n"); abort(); } -/* Generated stub for fromwire_gossipd_send_onionmsg */ -bool fromwire_gossipd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *obs2 UNNEEDED, struct node_id *id UNNEEDED, u8 **onion UNNEEDED, struct pubkey *blinding UNNEEDED) -{ fprintf(stderr, "fromwire_gossipd_send_onionmsg called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr_array */ -struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } -/* Generated stub for get_node */ -struct node *get_node(struct routing_state *rstate UNNEEDED, - const struct node_id *id UNNEEDED) -{ fprintf(stderr, "get_node called!\n"); abort(); } -/* Generated stub for gossip_store_compact */ -bool gossip_store_compact(struct gossip_store *gs UNNEEDED) -{ fprintf(stderr, "gossip_store_compact called!\n"); abort(); } -/* Generated stub for gossip_store_get */ -const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, - struct gossip_store *gs UNNEEDED, - u64 offset UNNEEDED) -{ fprintf(stderr, "gossip_store_get called!\n"); abort(); } -/* Generated stub for gossip_store_load */ -u32 gossip_store_load(struct routing_state *rstate UNNEEDED, struct gossip_store *gs UNNEEDED) -{ fprintf(stderr, "gossip_store_load called!\n"); abort(); } -/* Generated stub for gossip_time_now */ -struct timeabs gossip_time_now(const struct routing_state *rstate UNNEEDED) -{ fprintf(stderr, "gossip_time_now called!\n"); abort(); } -/* Generated stub for handle_channel_announcement */ -u8 *handle_channel_announcement(struct routing_state *rstate UNNEEDED, - const u8 *announce TAKES UNNEEDED, - u32 current_blockheight UNNEEDED, - const struct short_channel_id **scid UNNEEDED, - struct peer *peer UNNEEDED) -{ fprintf(stderr, "handle_channel_announcement called!\n"); abort(); } -/* Generated stub for handle_channel_update */ -u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *update TAKES UNNEEDED, - struct peer *peer UNNEEDED, - struct short_channel_id *unknown_scid UNNEEDED, - bool force UNNEEDED) -{ fprintf(stderr, "handle_channel_update called!\n"); abort(); } -/* Generated stub for handle_local_channel_update */ -void handle_local_channel_update(struct daemon *daemon UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "handle_local_channel_update called!\n"); abort(); } -/* Generated stub for handle_node_announcement */ -u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, - struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED) -{ fprintf(stderr, "handle_node_announcement called!\n"); abort(); } -/* Generated stub for handle_pending_cannouncement */ -bool handle_pending_cannouncement(struct daemon *daemon UNNEEDED, - struct routing_state *rstate UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct amount_sat sat UNNEEDED, - const u8 *txscript UNNEEDED) -{ fprintf(stderr, "handle_pending_cannouncement called!\n"); abort(); } -/* Generated stub for handle_query_channel_range */ -const u8 *handle_query_channel_range(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "handle_query_channel_range called!\n"); abort(); } -/* Generated stub for handle_query_short_channel_ids */ -const u8 *handle_query_short_channel_ids(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "handle_query_short_channel_ids called!\n"); abort(); } -/* Generated stub for handle_reply_channel_range */ -const u8 *handle_reply_channel_range(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "handle_reply_channel_range called!\n"); abort(); } -/* Generated stub for handle_reply_short_channel_ids_end */ -const u8 *handle_reply_short_channel_ids_end(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "handle_reply_short_channel_ids_end called!\n"); abort(); } -/* Generated stub for handle_used_local_channel_update */ -void handle_used_local_channel_update(struct daemon *daemon UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "handle_used_local_channel_update called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for local_disable_chan */ -void local_disable_chan(struct daemon *daemon UNNEEDED, const struct chan *chan UNNEEDED, int direction UNNEEDED) -{ fprintf(stderr, "local_disable_chan called!\n"); abort(); } -/* Generated stub for local_enable_chan */ -void local_enable_chan(struct daemon *daemon UNNEEDED, const struct chan *chan UNNEEDED, int direction UNNEEDED) -{ fprintf(stderr, "local_enable_chan called!\n"); abort(); } -/* Generated stub for master_badmsg */ -void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) -{ fprintf(stderr, "master_badmsg called!\n"); abort(); } -/* Generated stub for maybe_send_own_node_announce */ -void maybe_send_own_node_announce(struct daemon *daemon UNNEEDED, bool startup UNNEEDED) -{ fprintf(stderr, "maybe_send_own_node_announce called!\n"); abort(); } -/* Generated stub for maybe_send_query_responses */ -void maybe_send_query_responses(struct daemon *daemon UNNEEDED) -{ fprintf(stderr, "maybe_send_query_responses called!\n"); abort(); } -/* Generated stub for memleak_find_allocations */ -struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, - const void *exclude1 UNNEEDED, - const void *exclude2 UNNEEDED) -{ fprintf(stderr, "memleak_find_allocations called!\n"); abort(); } -/* Generated stub for memleak_remove_region */ -void memleak_remove_region(struct htable *memtable UNNEEDED, - const void *p UNNEEDED, size_t bytelen UNNEEDED) -{ fprintf(stderr, "memleak_remove_region called!\n"); abort(); } -/* Generated stub for new_onionreply */ -struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) -{ fprintf(stderr, "new_onionreply called!\n"); abort(); } -/* Generated stub for new_reltimer_ */ -struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, - const tal_t *ctx UNNEEDED, - struct timerel expire UNNEEDED, - void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) -{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); } -/* Generated stub for new_routing_state */ -struct routing_state *new_routing_state(const tal_t *ctx UNNEEDED, - const struct node_id *local_id UNNEEDED, - struct list_head *peers UNNEEDED, - struct timers *timers UNNEEDED, - const u32 *dev_gossip_time TAKES UNNEEDED, - bool dev_fast_gossip UNNEEDED, - bool dev_fast_gossip_prune UNNEEDED) -{ fprintf(stderr, "new_routing_state called!\n"); abort(); } -/* Generated stub for new_seeker */ -struct seeker *new_seeker(struct daemon *daemon UNNEEDED) -{ fprintf(stderr, "new_seeker called!\n"); abort(); } -/* Generated stub for next_chan */ -struct chan *next_chan(const struct node *node UNNEEDED, struct chan_map_iter *i UNNEEDED) -{ fprintf(stderr, "next_chan called!\n"); abort(); } -/* Generated stub for notleak_ */ -void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) -{ fprintf(stderr, "notleak_ called!\n"); abort(); } -/* Generated stub for private_channel_announcement */ -const u8 *private_channel_announcement(const tal_t *ctx UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - const struct node_id *local_node_id UNNEEDED, - const struct node_id *remote_node_id UNNEEDED, - const u8 *features UNNEEDED) -{ fprintf(stderr, "private_channel_announcement called!\n"); abort(); } -/* Generated stub for query_unknown_channel */ -void query_unknown_channel(struct daemon *daemon UNNEEDED, - struct peer *peer UNNEEDED, - const struct short_channel_id *id UNNEEDED) -{ fprintf(stderr, "query_unknown_channel called!\n"); abort(); } -/* Generated stub for query_unknown_node */ -void query_unknown_node(struct seeker *seeker UNNEEDED, struct peer *peer UNNEEDED) -{ fprintf(stderr, "query_unknown_node called!\n"); abort(); } -/* Generated stub for refresh_local_channel */ -void refresh_local_channel(struct daemon *daemon UNNEEDED, - struct chan *chan UNNEEDED, int direction UNNEEDED) -{ fprintf(stderr, "refresh_local_channel called!\n"); abort(); } -/* Generated stub for remove_channel_from_store */ -void remove_channel_from_store(struct routing_state *rstate UNNEEDED, - struct chan *chan UNNEEDED) -{ fprintf(stderr, "remove_channel_from_store called!\n"); abort(); } -/* Generated stub for remove_unknown_scid */ -bool remove_unknown_scid(struct seeker *seeker UNNEEDED, - const struct short_channel_id *scid UNNEEDED, - bool found UNNEEDED) -{ fprintf(stderr, "remove_unknown_scid called!\n"); abort(); } -/* Generated stub for route_prune */ -void route_prune(struct routing_state *rstate UNNEEDED) -{ fprintf(stderr, "route_prune called!\n"); abort(); } -/* Generated stub for routing_add_private_channel */ -bool routing_add_private_channel(struct routing_state *rstate UNNEEDED, - const struct node_id *id UNNEEDED, - struct amount_sat sat UNNEEDED, - const u8 *chan_ann UNNEEDED, u64 index UNNEEDED) -{ fprintf(stderr, "routing_add_private_channel called!\n"); abort(); } -/* Generated stub for sanitize_error */ -char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "sanitize_error called!\n"); abort(); } -/* Generated stub for seeker_setup_peer_gossip */ -void seeker_setup_peer_gossip(struct seeker *seeker UNNEEDED, struct peer *peer UNNEEDED) -{ fprintf(stderr, "seeker_setup_peer_gossip called!\n"); abort(); } -/* Generated stub for status_failed */ -void status_failed(enum status_failreason code UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "status_failed called!\n"); abort(); } -/* Generated stub for status_fmt */ -void status_fmt(enum log_level level UNNEEDED, - const struct node_id *peer UNNEEDED, - const char *fmt UNNEEDED, ...) - -{ fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for status_setup_async */ -void status_setup_async(struct daemon_conn *master UNNEEDED) -{ fprintf(stderr, "status_setup_async called!\n"); abort(); } -/* Generated stub for subdaemon_setup */ -void subdaemon_setup(int argc UNNEEDED, char *argv[]) -{ fprintf(stderr, "subdaemon_setup called!\n"); abort(); } -/* Generated stub for timer_expired */ -void timer_expired(struct timer *timer UNNEEDED) -{ fprintf(stderr, "timer_expired called!\n"); abort(); } -/* Generated stub for towire_gossipd_addgossip_reply */ -u8 *towire_gossipd_addgossip_reply(const tal_t *ctx UNNEEDED, const wirestring *err UNNEEDED) -{ fprintf(stderr, "towire_gossipd_addgossip_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_dev_compact_store_reply */ -u8 *towire_gossipd_dev_compact_store_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED) -{ fprintf(stderr, "towire_gossipd_dev_compact_store_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_dev_memleak_reply */ -u8 *towire_gossipd_dev_memleak_reply(const tal_t *ctx UNNEEDED, bool leak UNNEEDED) -{ fprintf(stderr, "towire_gossipd_dev_memleak_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_get_addrs_reply */ -u8 *towire_gossipd_get_addrs_reply(const tal_t *ctx UNNEEDED, const struct wireaddr *addrs UNNEEDED) -{ fprintf(stderr, "towire_gossipd_get_addrs_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_get_txout */ -u8 *towire_gossipd_get_txout(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) -{ fprintf(stderr, "towire_gossipd_get_txout called!\n"); abort(); } -/* Generated stub for towire_gossipd_got_onionmsg_to_us */ -u8 *towire_gossipd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEEDED, const struct pubkey *node_alias UNNEEDED, const struct secret *self_id UNNEEDED, const struct pubkey *reply_blinding UNNEEDED, const struct pubkey *reply_first_node UNNEEDED, const struct onionmsg_path **reply_path UNNEEDED, const u8 *rawmsg UNNEEDED) -{ fprintf(stderr, "towire_gossipd_got_onionmsg_to_us called!\n"); abort(); } -/* Generated stub for towire_gossipd_init_reply */ -u8 *towire_gossipd_init_reply(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "towire_gossipd_init_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_new_blockheight_reply */ -u8 *towire_gossipd_new_blockheight_reply(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "towire_gossipd_new_blockheight_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_new_peer_reply */ -u8 *towire_gossipd_new_peer_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED) -{ fprintf(stderr, "towire_gossipd_new_peer_reply called!\n"); abort(); } -/* Generated stub for towire_gossipd_send_gossip */ -u8 *towire_gossipd_send_gossip(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "towire_gossipd_send_gossip called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -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(); } -/* AUTOGENERATED MOCKS END */ - -/* Updated each time, as we pretend to be Alice, Bob, Carol */ -static const struct privkey *mykey; - -static void test_ecdh(const struct pubkey *point, struct secret *ss) -{ - if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, - mykey->secret.data, NULL, NULL) != 1) - abort(); -} - -static void json_strfield(const char *name, const char *val) -{ - printf("\t\"%s\": \"%s\",\n", name, val); -} - -static void json_onionmsg_payload(const struct tlv_obs2_onionmsg_payload *om) -{ - if (om->reply_path) { - printf("\t\"reply_path\": {\n"); - json_strfield("first_node_id", - type_to_string(tmpctx, struct pubkey, - &om->reply_path->first_node_id)); - json_strfield("blinding", - type_to_string(tmpctx, struct pubkey, - &om->reply_path->blinding)); - printf("\t\"path\": [\n"); - for (size_t i = 0; i < tal_count(om->reply_path->path); i++) { - json_strfield("node_id", - type_to_string(tmpctx, struct pubkey, - &om->reply_path->path[i]->node_id)); - json_strfield("encrypted_recipient_data", - tal_hex(tmpctx, - om->reply_path->path[i]->encrypted_recipient_data)); - } - printf("]}\n"); - } - if (om->invoice) - json_strfield("invoice", tal_hex(tmpctx, om->invoice)); - if (om->invoice_request) - json_strfield("invoice_request", - tal_hex(tmpctx, om->invoice_request)); - if (om->invoice_error) - json_strfield("invoice_error", - tal_hex(tmpctx, om->invoice_error)); -} - -/* Return next onion (and updates blinding), or NULL */ -static u8 *json_test(const char *testname, - const u8 *data, - const struct privkey *me, - const struct privkey *blinding_priv, - struct pubkey *blinding) -{ - struct pubkey my_id, next_node; - struct secret ss, onion_ss; - struct pubkey ephemeral; - struct route_step *rs; - const u8 *cursor; - size_t max, maxlen; - struct onionpacket *op; - struct tlv_obs2_onionmsg_payload *om; - - op = parse_onionpacket(tmpctx, data, tal_bytelen(data), NULL); - assert(op); - - pubkey_from_privkey(me, &my_id); - printf("{"); - json_strfield("test name", testname); - json_strfield("reader_privkey", - type_to_string(tmpctx, struct privkey, me)); - json_strfield("reader_id", - type_to_string(tmpctx, struct pubkey, &my_id)); - - if (blinding_priv) - json_strfield("blinding_privkey", - type_to_string(tmpctx, struct privkey, - blinding_priv)); - json_strfield("blinding", - type_to_string(tmpctx, struct pubkey, blinding)); - printf("\"onionmsg\": {\n"); - json_strfield("raw", tal_hex(tmpctx, data)); - json_strfield("version", tal_fmt(tmpctx, "%i", op->version)); - json_strfield("public_key", - type_to_string(tmpctx, struct pubkey, &op->ephemeralkey)); - json_strfield("hop_payloads", - tal_hex(tmpctx, op->routinginfo)); - json_strfield("hmac", - tal_hexstr(tmpctx, &op->hmac, sizeof(op->hmac))); - printf("},\n"); - - ephemeral = op->ephemeralkey; - - /* Set this for test_ecdh */ - mykey = me; - assert(unblind_onion(blinding, test_ecdh, &ephemeral, &ss)); - json_strfield("ECDH shared secret", - type_to_string(tmpctx, struct secret, &ss)); - /* Reproduce internal calc from unblind_onion */ - { - struct secret hmac; - subkey_from_hmac("blinded_node_id", &ss, &hmac); - json_strfield("HMAC256(\\\"blinded_node_id\\\", ss(i)) * k(i)", - type_to_string(tmpctx, struct secret, &hmac)); - } - json_strfield("Tweaked onion pubkey", - type_to_string(tmpctx, struct pubkey, &ephemeral)); - - /* Now get onion shared secret and parse it. */ - test_ecdh(&ephemeral, &onion_ss); - json_strfield("onion shared secret", - type_to_string(tmpctx, struct secret, &onion_ss)); - rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); - assert(rs); - - printf("\"onion contents\": {\n"); - json_strfield("raw", tal_hex(tmpctx, rs->raw_payload)); - - cursor = rs->raw_payload; - max = tal_bytelen(rs->raw_payload); - maxlen = fromwire_bigsize(&cursor, &max); - json_strfield("length", tal_fmt(tmpctx, "%zu", maxlen)); - json_strfield("rawtlv", tal_hexstr(tmpctx, cursor, maxlen)); - json_strfield("hmac", tal_hexstr(tmpctx, rs->next->hmac.bytes, - sizeof(rs->next->hmac.bytes))); - om = tlv_obs2_onionmsg_payload_new(tmpctx); - assert(fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)); - - json_onionmsg_payload(om); - - /* We expect one of these. */ - assert(om->enctlv); - - printf("\t\"encrypted_data_tlv\": {\n"); - json_strfield("raw", tal_hex(tmpctx, om->enctlv)); - - if (rs->nextcase == ONION_END) { - struct secret *self_id; - struct pubkey alias; - assert(decrypt_obs2_final_enctlv(tmpctx, - blinding, &ss, - om->enctlv, - &my_id, &alias, &self_id)); - if (self_id) { - json_strfield("self_id", - type_to_string(tmpctx, struct secret, - self_id)); - } - printf("}\n"); - return NULL; - } else { - assert(decrypt_obs2_enctlv(blinding, &ss, om->enctlv, &next_node, - blinding)); - json_strfield("next_node", - type_to_string(tmpctx, struct pubkey, &next_node)); - json_strfield("next_blinding", - type_to_string(tmpctx, struct pubkey, - blinding)); - printf("}"); - printf("},\n"); - return serialize_onionpacket(tmpctx, rs->next); - } -} - -int main(int argc, char *argv[]) -{ - struct onionpacket *op; - u8 *data; - struct privkey alice, bob, carol, dave, blinding_priv; - struct pubkey alice_id, bob_id, carol_id, dave_id; - struct pubkey blinding; - - common_setup(argv[0]); - - memset(&alice, 'A', sizeof(alice)); - memset(&bob, 'B', sizeof(bob)); - memset(&carol, 'C', sizeof(carol)); - memset(&dave, 'D', sizeof(dave)); - pubkey_from_privkey(&alice, &alice_id); - pubkey_from_privkey(&bob, &bob_id); - pubkey_from_privkey(&carol, &carol_id); - pubkey_from_privkey(&dave, &dave_id); - - /* ThomasH sends via email: - * - * { - * "version":0, - * "public_key": - * "0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", - * "hop_payloads": - * "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", - * "hmac": "564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac" - * } - */ - op = tal(tmpctx, struct onionpacket); - op->version = 0; - assert(pubkey_from_hexstr("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", strlen("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967"), &op->ephemeralkey)); - assert(hex_decode("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac", - strlen("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac"), - &op->hmac, sizeof(op->hmac))); - op->routinginfo = tal_hexdata(op, "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", - strlen("37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a")); - - data = serialize_onionpacket(tmpctx, op); - printf("[\n"); - - memset(&blinding_priv, 5, sizeof(blinding_priv)); - pubkey_from_privkey(&blinding_priv, &blinding); - - data = json_test("onion message for Alice", - data, - &alice, - &blinding_priv, - &blinding); - - data = json_test("onion message for Bob", - data, - &bob, - NULL, - &blinding); - - data = json_test("onion message for Carol", - data, - &carol, - NULL, - &blinding); - - data = json_test("onion message for Dave", - data, - &dave, - NULL, - &blinding); - - assert(!data); - printf("]\n"); - - common_shutdown(); - return 0; -} diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index ed037461a03b..bd56b82c166e 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -364,6 +365,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_DEV_MEMLEAK: case WIRE_CONNECTD_PEER_FINAL_MSG: case WIRE_CONNECTD_PING: + case WIRE_CONNECTD_SEND_ONIONMSG: /* This is a reply, so never gets through to here. */ case WIRE_CONNECTD_INIT_REPLY: case WIRE_CONNECTD_ACTIVATE_REPLY: @@ -384,6 +386,10 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_CONNECT_FAILED: connect_failed(connectd->ld, msg); break; + + case WIRE_CONNECTD_GOT_ONIONMSG_TO_US: + handle_onionmsg_to_us(connectd->ld, msg); + break; } return 0; } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index f6895fd69d00..7429d4db3a89 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -164,7 +163,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_DEV_COMPACT_STORE: case WIRE_GOSSIPD_DEV_SET_TIME: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: - case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: case WIRE_GOSSIPD_GET_ADDRS: case WIRE_GOSSIPD_USED_LOCAL_CHANNEL_UPDATE: @@ -180,9 +178,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_GET_ADDRS_REPLY: break; - case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: - handle_onionmsg_to_us(gossip->ld, msg); - break; case WIRE_GOSSIPD_GET_TXOUT: get_txout(gossip, msg); break; diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index e6e51f5ebccd..fec95b7ca547 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -150,7 +150,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) payload = tal(tmpctx, struct onion_message_hook_payload); payload->our_alias = tal(payload, struct pubkey); - if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg, + if (!fromwire_connectd_got_onionmsg_to_us(payload, msg, &obs2, payload->our_alias, &self_id, @@ -198,7 +198,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) } tal_free(submsg); - /* Make sure gossipd gets this right. */ + /* Make sure connectd gets this right. */ if (payload->reply_path && (!payload->reply_blinding || !payload->reply_first_node)) { log_broken(ld->log, @@ -277,7 +277,7 @@ static struct command_result *json_sendonionmessage2(struct command *cmd, return command_fail(cmd, LIGHTNINGD, "experimental-onion-messages not enabled"); - /* Sanity check first; gossipd doesn't bother telling us if peer + /* Sanity check first; connectd doesn't bother telling us if peer * can't be reached. */ if (!peer_by_id(cmd->ld, first_id)) return command_fail(cmd, LIGHTNINGD, "Unknown first peer"); @@ -300,8 +300,8 @@ static struct command_result *json_sendonionmessage2(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Creating onion failed (tlvs too long?)"); - subd_send_msg(cmd->ld->gossip, - take(towire_gossipd_send_onionmsg(NULL, obs2, first_id, + subd_send_msg(cmd->ld->connectd, + take(towire_connectd_send_onionmsg(NULL, obs2, first_id, serialize_onionpacket(tmpctx, op), blinding))); diff --git a/wire/peer_wire.c b/wire/peer_wire.c index 1ed291db5c35..35089e48f1d7 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -65,8 +65,6 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_QUERY_CHANNEL_RANGE: case WIRE_REPLY_CHANNEL_RANGE: - case WIRE_OBS2_ONION_MESSAGE: - case WIRE_ONION_MESSAGE: return true; case WIRE_WARNING: case WIRE_INIT: @@ -101,6 +99,8 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: case WIRE_ACK_RBF: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: #if EXPERIMENTAL_FEATURES case WIRE_STFU: #endif From 960e911986b5f4ed3edded8cccb5c65127aa8f43 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0279/1530] connectd: do io logging properly for msgs we make. We don't need to log msgs from subds, but we do our own, and we weren't. 1. Rename queue_peer_msg to inject_peer_msg for clarity, make it do logging 2. In the one place where we're relaying, call msg_queue() directly. Signed-off-by: Rusty Russell --- connectd/connectd.c | 2 +- connectd/multiplex.c | 11 ++++++----- connectd/multiplex.h | 5 +++-- connectd/onion_message.c | 30 +++++++++++++++--------------- connectd/test/run-onion_message.c | 6 +++--- 5 files changed, 28 insertions(+), 26 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index b8343f6c69c9..aa24cd0999f3 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -2028,7 +2028,7 @@ static struct io_plan *recv_gossip(struct io_conn *conn, peer = peer_htable_get(&daemon->peers, &dst); if (peer) - queue_peer_msg(peer, take(gossip_msg)); + inject_peer_msg(peer, take(gossip_msg)); return daemon_conn_read_next(conn, daemon->gossipd); } diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 8637d01ff322..384f12f62225 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -36,8 +36,9 @@ #include #include -void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) +void inject_peer_msg(struct peer *peer, const u8 *msg TAKES) { + status_peer_io(LOG_IO_OUT, &peer->id, msg); msg_enqueue(peer->peer_outq, msg); } @@ -360,7 +361,7 @@ static void send_ping(struct peer *peer) return; } - queue_peer_msg(peer, take(make_ping(NULL, 1, 0))); + inject_peer_msg(peer, take(make_ping(NULL, 1, 0))); peer->expecting_pong = PONG_EXPECTED_PROBING; set_ping_timer(peer); } @@ -378,7 +379,7 @@ static void handle_ping_in(struct peer *peer, const u8 *msg) } if (pong) - queue_peer_msg(peer, take(pong)); + inject_peer_msg(peer, take(pong)); } static void handle_ping_reply(struct peer *peer, const u8 *msg) @@ -582,7 +583,7 @@ static struct io_plan *read_from_subd_done(struct io_conn *subd_conn, struct peer *peer) { /* Tell them to encrypt & write. */ - queue_peer_msg(peer, take(peer->subd_in)); + msg_enqueue(peer->peer_outq, take(peer->subd_in)); peer->subd_in = NULL; /* Wait for them to wake us */ @@ -828,7 +829,7 @@ void send_manual_ping(struct daemon *daemon, const u8 *msg) if (tal_count(ping) > 65535) status_failed(STATUS_FAIL_MASTER_IO, "Oversize ping"); - queue_peer_msg(peer, take(ping)); + inject_peer_msg(peer, take(ping)); status_debug("sending ping expecting %sresponse", num_pong_bytes >= 65532 ? "no " : ""); diff --git a/connectd/multiplex.h b/connectd/multiplex.h index bbb67e75501e..fa0a032fb100 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -21,8 +21,9 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES); -/* Inject a message into the output stream */ -void queue_peer_msg(struct peer *peer, const u8 *msg TAKES); +/* Inject a message into the output stream. Unlike a raw msg_enqueue, + * this does io logging if required. */ +void inject_peer_msg(struct peer *peer, const u8 *msg TAKES); void setup_peer_gossip_store(struct peer *peer, const struct feature_set *our_features, diff --git a/connectd/onion_message.c b/connectd/onion_message.c index f19594899172..5459fd57b181 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -37,9 +37,9 @@ void handle_obs2_onion_message(struct daemon *daemon, /* FIXME: ratelimit! */ if (!fromwire_obs2_onion_message(msg, msg, &blinding, &onion)) { - queue_peer_msg(peer, - towire_warningfmt(NULL, NULL, - "Bad onion_message")); + inject_peer_msg(peer, + towire_warningfmt(NULL, NULL, + "Bad onion_message")); return; } @@ -161,10 +161,10 @@ void handle_obs2_onion_message(struct daemon *daemon, &next_node)); return; } - queue_peer_msg(next_peer, - take(towire_obs2_onion_message(NULL, - &next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); + inject_peer_msg(next_peer, + take(towire_obs2_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); } } @@ -188,7 +188,7 @@ void onionmsg_req(struct daemon *daemon, const u8 *msg) omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg); else omsg = towire_onion_message(NULL, &blinding, onionmsg); - queue_peer_msg(peer, take(omsg)); + inject_peer_msg(peer, take(omsg)); } } @@ -213,9 +213,9 @@ void handle_onion_message(struct daemon *daemon, /* FIXME: ratelimit! */ if (!fromwire_onion_message(msg, msg, &blinding, &onion)) { - queue_peer_msg(peer, - towire_warningfmt(NULL, NULL, - "Bad onion_message")); + inject_peer_msg(peer, + towire_warningfmt(NULL, NULL, + "Bad onion_message")); return; } @@ -336,10 +336,10 @@ void handle_onion_message(struct daemon *daemon, &next_node)); return; } - queue_peer_msg(next_peer, - take(towire_onion_message(NULL, - &next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); + inject_peer_msg(next_peer, + take(towire_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); } } diff --git a/connectd/test/run-onion_message.c b/connectd/test/run-onion_message.c index 8533d718f703..56ea1e268a05 100644 --- a/connectd/test/run-onion_message.c +++ b/connectd/test/run-onion_message.c @@ -88,6 +88,9 @@ bool fromwire_connectd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UN /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for inject_peer_msg */ +void inject_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +{ fprintf(stderr, "inject_peer_msg called!\n"); abort(); } /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } @@ -100,9 +103,6 @@ void node_id_from_pubkey(struct node_id *id UNNEEDED, const struct pubkey *key U /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } -/* Generated stub for queue_peer_msg */ -void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) -{ fprintf(stderr, "queue_peer_msg called!\n"); abort(); } /* Generated stub for status_fmt */ void status_fmt(enum log_level level UNNEEDED, const struct node_id *peer UNNEEDED, From f14c0ef76a821b139b96d72a22a56800aa9b4c28 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0280/1530] common: make sure we hand through peer for io logging. This is mainly useful for connectd. Signed-off-by: Rusty Russell --- common/status.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/status.c b/common/status.c index 499484c39f61..d2d770523c3e 100644 --- a/common/status.c +++ b/common/status.c @@ -125,7 +125,7 @@ void status_peer_io(enum log_level iodir, { report_logging_io("SIGUSR1"); if (logging_io) - status_io_full(iodir, NULL, "", p); + status_io_full(iodir, peer, "", p); /* We get a huge amount of gossip; don't log it */ else if (!is_msg_for_gossipd(p)) status_peer_io_short(iodir, peer, p); From 1c71c9849bd2a59562876398ee99dc004f7b0202 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0281/1530] connectd: handle custom messages. This is neater than what we had before, and slightly more general. Signed-off-by: Rusty Russell Changelog-Changed: JSON_RPC: `sendcustommsg` now works with any connected peer, even when shutting down a channel. --- channeld/channeld.c | 46 +----- closingd/closingd.c | 10 -- connectd/connectd.c | 5 + connectd/connectd_wire.csv | 12 ++ connectd/multiplex.c | 37 +++++ connectd/multiplex.h | 3 + lightningd/channel_control.c | 10 -- lightningd/closing_control.c | 10 -- lightningd/connect_control.c | 149 ++++++++++++++++++ lightningd/dual_open_control.c | 10 -- lightningd/opening_control.c | 10 -- lightningd/peer_control.c | 164 -------------------- lightningd/peer_control.h | 3 - lightningd/subd.c | 4 +- lightningd/test/run-invoice-select-inchan.c | 8 - openingd/dualopend.c | 56 +------ openingd/openingd.c | 32 ---- tests/test_misc.py | 8 +- wallet/test/run-wallet.c | 20 --- wire/Makefile | 2 - wire/common_wire.csv | 10 -- 21 files changed, 215 insertions(+), 394 deletions(-) delete mode 100644 wire/common_wire.csv diff --git a/channeld/channeld.c b/channeld/channeld.c index 01736f526331..dfc3923181d2 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include @@ -2049,23 +2048,6 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) billboard_update(peer); } -/* Try to handle a custommsg Returns true if it was a custom message and has - * been handled, false if the message was not handled. - */ -static bool channeld_handle_custommsg(const u8 *msg) -{ - enum peer_wire type = fromwire_peektype(msg); - if (type % 2 == 1 && !peer_wire_is_defined(type)) { - /* The message is not part of the messages we know how to - * handle. Assuming this is a custommsg, we just forward it to the - * master. */ - wire_sync_write(MASTER_FD, take(towire_custommsg_in(NULL, msg))); - return true; - } else { - return false; - } -} - static void handle_unexpected_tx_sigs(struct peer *peer, const u8 *msg) { const struct witness_stack **ws; @@ -2155,9 +2137,6 @@ static void peer_in(struct peer *peer, const u8 *msg) { enum peer_wire type = fromwire_peektype(msg); - if (channeld_handle_custommsg(msg)) - return; - if (handle_peer_gossip_or_error(peer->pps, &peer->channel_id, msg)) return; @@ -2824,8 +2803,7 @@ static void peer_reconnect(struct peer *peer, do { clean_tmpctx(); msg = peer_read(tmpctx, peer->pps); - } while (channeld_handle_custommsg(msg) || - handle_peer_gossip_or_error(peer->pps, &peer->channel_id, msg) || + } while (handle_peer_gossip_or_error(peer->pps, &peer->channel_id, msg) || capture_premature_msg(&premature_msgs, msg)); got_reestablish: @@ -3577,17 +3555,6 @@ static void handle_dev_quiesce(struct peer *peer, const u8 *msg) #endif /* EXPERIMENTAL_FEATURES */ #endif /* DEVELOPER */ -/* We were told to send a custommsg to the peer by `lightningd`. All the - * verification is done on the side of `lightningd` so we should be good to - * just forward it here. */ -static void channeld_send_custommsg(struct peer *peer, const u8 *msg) -{ - u8 *inner; - if (!fromwire_custommsg_out(tmpctx, msg, &inner)) - master_badmsg(WIRE_CUSTOMMSG_OUT, msg); - peer_write(peer->pps, take(inner)); -} - static void req_in(struct peer *peer, const u8 *msg) { enum channeld_wire t = fromwire_peektype(msg); @@ -3676,17 +3643,6 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_LOCAL_PRIVATE_CHANNEL: break; } - - /* Now handle common messages. */ - switch ((enum common_wire)t) { - case WIRE_CUSTOMMSG_OUT: - channeld_send_custommsg(peer, msg); - return; - /* We send these. */ - case WIRE_CUSTOMMSG_IN: - break; - } - master_badmsg(-1, msg); } diff --git a/closingd/closingd.c b/closingd/closingd.c index ffcac22e3d7a..87db15cda9c6 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -126,15 +125,6 @@ static u8 *closing_read_peer_msg(const tal_t *ctx, handle_gossip_msg(pps, take(msg)); continue; } - /* Handle custommsgs */ - enum peer_wire type = fromwire_peektype(msg); - if (type % 2 == 1 && !peer_wire_is_defined(type)) { - /* The message is not part of the messages we know - * how to handle. Assume is custommsg, forward it - * to master. */ - wire_sync_write(REQ_FD, take(towire_custommsg_in(NULL, msg))); - continue; - } if (!handle_peer_gossip_or_error(pps, channel_id, msg)) return msg; } diff --git a/connectd/connectd.c b/connectd/connectd.c index aa24cd0999f3..3016ceb37419 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1975,6 +1975,10 @@ static struct io_plan *recv_req(struct io_conn *conn, onionmsg_req(daemon, msg); goto out; + case WIRE_CONNECTD_CUSTOMMSG_OUT: + send_custommsg(daemon, msg); + goto out; + case WIRE_CONNECTD_DEV_MEMLEAK: #if DEVELOPER dev_connect_memleak(daemon, msg); @@ -1989,6 +1993,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: case WIRE_CONNECTD_PING_REPLY: case WIRE_CONNECTD_GOT_ONIONMSG_TO_US: + case WIRE_CONNECTD_CUSTOMMSG_IN: break; } diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 52dae09d0c54..01c03335cb32 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -116,3 +116,15 @@ msgdata,connectd_send_onionmsg,onion_len,u16, msgdata,connectd_send_onionmsg,onion,u8,onion_len msgdata,connectd_send_onionmsg,blinding,pubkey, +# A custom message that we got from a peer and don't know how to handle, so we +# forward it to the master for further handling. +msgtype,connectd_custommsg_in,2110 +msgdata,connectd_custommsg_in,id,node_id, +msgdata,connectd_custommsg_in,msg_len,u16, +msgdata,connectd_custommsg_in,msg,u8,msg_len + +# A custom message that the lightningd tells us to send to the peer. +msgtype,connectd_custommsg_out,2011 +msgdata,connectd_custommsg_out,id,node_id, +msgdata,connectd_custommsg_out,msg_len,u16, +msgdata,connectd_custommsg_out,msg,u8,msg_len diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 384f12f62225..f94175b32ec0 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -366,6 +366,21 @@ static void send_ping(struct peer *peer) set_ping_timer(peer); } +void send_custommsg(struct daemon *daemon, const u8 *msg) +{ + struct node_id id; + u8 *custommsg; + struct peer *peer; + + if (!fromwire_connectd_custommsg_out(tmpctx, msg, &id, &custommsg)) + master_badmsg(WIRE_CONNECTD_CUSTOMMSG_OUT, msg); + + /* Races can happen: this might be gone by now. */ + peer = peer_htable_get(&daemon->peers, &id); + if (peer) + inject_peer_msg(peer, take(custommsg)); +} + static void handle_ping_in(struct peer *peer, const u8 *msg) { u8 *pong; @@ -472,6 +487,26 @@ static void handle_gossip_timetamp_filter_in(struct peer *peer, const u8 *msg) wake_gossip(peer); } +static bool handle_custommsg(struct daemon *daemon, + struct peer *peer, + const u8 *msg) +{ + enum peer_wire type = fromwire_peektype(msg); + if (type % 2 == 1 && !peer_wire_is_defined(type)) { + /* The message is not part of the messages we know how to + * handle. Assuming this is a custommsg, we just forward it to the + * master. */ + status_peer_io(LOG_IO_IN, &peer->id, msg); + daemon_conn_send(daemon->master, + take(towire_connectd_custommsg_in(NULL, + &peer->id, + msg))); + return true; + } else { + return false; + } +} + /* We handle pings and gossip messages. */ static bool handle_message_locally(struct peer *peer, const u8 *msg) { @@ -496,6 +531,8 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) } else if (type == WIRE_ONION_MESSAGE) { handle_onion_message(peer->daemon, peer, msg); return true; + } else if (handle_custommsg(peer->daemon, peer, msg)) { + return true; } /* Do we want to divert to gossipd? */ diff --git a/connectd/multiplex.h b/connectd/multiplex.h index fa0a032fb100..13c37e298221 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -34,4 +34,7 @@ void close_peer_conn(struct peer *peer); /* When lightningd says to send a ping */ void send_manual_ping(struct daemon *daemon, const u8 *msg); + +/* When lightningd says to send a custom message (from a plugin) */ +void send_custommsg(struct daemon *daemon, const u8 *msg); #endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 2e7792dc88a6..dcfb4536686b 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -22,7 +22,6 @@ #include #include #include -#include static void update_feerates(struct lightningd *ld, struct channel *channel) { @@ -564,15 +563,6 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) break; } - switch ((enum common_wire)t) { - case WIRE_CUSTOMMSG_IN: - handle_custommsg_in(sd->ld, sd->node_id, msg); - break; - /* We send these. */ - case WIRE_CUSTOMMSG_OUT: - break; - } - return 0; } diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index f7d88f2fdeb8..489e1acc2a65 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -38,7 +38,6 @@ #include #include #include -#include struct close_command { /* Inside struct lightningd close_commands. */ @@ -339,15 +338,6 @@ static unsigned closing_msg(struct subd *sd, const u8 *msg, const int *fds UNUSE break; } - switch ((enum common_wire)t) { - case WIRE_CUSTOMMSG_IN: - handle_custommsg_in(sd->ld, sd->node_id, msg); - break; - /* We send these. */ - case WIRE_CUSTOMMSG_OUT: - break; - } - return 0; } diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index bd56b82c166e..292114810244 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include struct connect { struct list_node list; @@ -352,6 +354,63 @@ static void peer_please_disconnect(struct lightningd *ld, const u8 *msg) } } +struct custommsg_payload { + struct node_id peer_id; + u8 *msg; +}; + +static bool custommsg_cb(struct custommsg_payload *payload, + const char *buffer, const jsmntok_t *toks) +{ + const jsmntok_t *t_res; + + if (!toks || !buffer) + return true; + + t_res = json_get_member(buffer, toks, "result"); + + /* fail */ + if (!t_res || !json_tok_streq(buffer, t_res, "continue")) + fatal("Plugin returned an invalid response to the " + "custommsg hook: %s", buffer); + + /* call next hook */ + return true; +} + +static void custommsg_final(struct custommsg_payload *payload STEALS) +{ + tal_steal(tmpctx, payload); +} + +static void custommsg_payload_serialize(struct custommsg_payload *payload, + struct json_stream *stream, + struct plugin *plugin) +{ + json_add_hex_talarr(stream, "payload", payload->msg); + json_add_node_id(stream, "peer_id", &payload->peer_id); +} + +REGISTER_PLUGIN_HOOK(custommsg, + custommsg_cb, + custommsg_final, + custommsg_payload_serialize, + struct custommsg_payload *); + +static void handle_custommsg_in(struct lightningd *ld, const u8 *msg) +{ + struct custommsg_payload *p = tal(NULL, struct custommsg_payload); + + if (!fromwire_connectd_custommsg_in(p, msg, &p->peer_id, &p->msg)) { + log_broken(ld->log, "Malformed custommsg: %s", + tal_hex(tmpctx, msg)); + tal_free(p); + return; + } + + plugin_hook_call_custommsg(ld, p); +} + static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fds) { enum connectd_wire t = fromwire_peektype(msg); @@ -366,6 +425,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_PEER_FINAL_MSG: case WIRE_CONNECTD_PING: case WIRE_CONNECTD_SEND_ONIONMSG: + case WIRE_CONNECTD_CUSTOMMSG_OUT: /* This is a reply, so never gets through to here. */ case WIRE_CONNECTD_INIT_REPLY: case WIRE_CONNECTD_ACTIVATE_REPLY: @@ -390,6 +450,10 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_GOT_ONIONMSG_TO_US: handle_onionmsg_to_us(connectd->ld, msg); break; + + case WIRE_CONNECTD_CUSTOMMSG_IN: + handle_custommsg_in(connectd->ld, msg); + break; } return 0; } @@ -500,3 +564,88 @@ void connectd_activate(struct lightningd *ld) io_loop(NULL, NULL); } +static struct command_result *json_sendcustommsg(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + struct node_id *dest; + struct peer *peer; + u8 *msg; + int type; + + if (!param(cmd, buffer, params, + p_req("node_id", param_node_id, &dest), + p_req("msg", param_bin_from_hex, &msg), + NULL)) + return command_param_failed(); + + type = fromwire_peektype(msg); + if (peer_wire_is_defined(type)) { + return command_fail( + cmd, JSONRPC2_INVALID_REQUEST, + "Cannot send messages of type %d (%s). It is not possible " + "to send messages that have a type managed internally " + "since that might cause issues with the internal state " + "tracking.", + type, peer_wire_name(type)); + } + + if (type % 2 == 0) { + return command_fail( + cmd, JSONRPC2_INVALID_REQUEST, + "Cannot send even-typed %d custom message. Currently " + "custom messages are limited to odd-numbered message " + "types, as even-numbered types might result in " + "disconnections.", + type); + } + + peer = peer_by_id(cmd->ld, dest); + if (!peer) { + return command_fail(cmd, JSONRPC2_INVALID_REQUEST, + "No such peer: %s", + type_to_string(cmd, struct node_id, dest)); + } + + /* FIXME: This won't work once connectd keeps peers */ + if (!peer_get_owning_subd(peer)) { + return command_fail(cmd, JSONRPC2_INVALID_REQUEST, + "Peer is not connected: %s", + type_to_string(cmd, struct node_id, dest)); + } + + subd_send_msg(cmd->ld->connectd, + take(towire_connectd_custommsg_out(cmd, dest, msg))); + + response = json_stream_success(cmd); + json_add_string(response, "status", + "Message sent to connectd for delivery"); + + return command_success(cmd, response); +} + +static const struct json_command sendcustommsg_command = { + "sendcustommsg", + "utility", + json_sendcustommsg, + "Send a custom message to the peer with the given {node_id}", + .verbose = "sendcustommsg node_id hexcustommsg", +}; + +AUTODATA(json_command, &sendcustommsg_command); + +#ifdef COMPAT_V0100 +#ifdef DEVELOPER +static const struct json_command dev_sendcustommsg_command = { + "dev-sendcustommsg", + "utility", + json_sendcustommsg, + "Send a custom message to the peer with the given {node_id}", + .verbose = "dev-sendcustommsg node_id hexcustommsg", +}; + +AUTODATA(json_command, &dev_sendcustommsg_command); +#endif /* DEVELOPER */ +#endif /* COMPAT_V0100 */ diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index ba9219f76923..b65f6a6c5966 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -31,7 +31,6 @@ #include #include #include -#include struct commit_rcvd { struct channel *channel; @@ -3030,15 +3029,6 @@ static unsigned int dual_opend_msg(struct subd *dualopend, break; } - switch ((enum common_wire)t) { - case WIRE_CUSTOMMSG_IN: - handle_custommsg_in(dualopend->ld, dualopend->node_id, msg); - return 0; - /* We send these. */ - case WIRE_CUSTOMMSG_OUT: - break; - } - log_broken(dualopend->log, "Unexpected msg %s: %s", dualopend_wire_name(t), tal_hex(tmpctx, msg)); tal_free(dualopend); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index acb53d292951..ad88b934aa2a 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -28,7 +28,6 @@ #include #include #include -#include void json_add_uncommitted_channel(struct json_stream *response, const struct uncommitted_channel *uc) @@ -890,15 +889,6 @@ static unsigned int openingd_msg(struct subd *openingd, break; } - switch ((enum common_wire)t) { - case WIRE_CUSTOMMSG_IN: - handle_custommsg_in(openingd->ld, openingd->node_id, msg); - return 0; - /* We send these. */ - case WIRE_CUSTOMMSG_OUT: - break; - } - log_broken(openingd->log, "Unexpected msg %s: %s", openingd_wire_name(t), tal_hex(tmpctx, msg)); tal_free(openingd); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index ca309f119c12..40cd63ae0429 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -65,7 +65,6 @@ #include #include #include -#include #include #include @@ -2404,167 +2403,4 @@ void peer_dev_memleak(struct command *cmd) { peer_memleak_req_next(cmd, NULL); } - #endif /* DEVELOPER */ - -struct custommsg_payload { - struct node_id peer_id; - const u8 *msg; -}; - -static bool custommsg_cb(struct custommsg_payload *payload, - const char *buffer, const jsmntok_t *toks) -{ - const jsmntok_t *t_res; - - if (!toks || !buffer) - return true; - - t_res = json_get_member(buffer, toks, "result"); - - /* fail */ - if (!t_res || !json_tok_streq(buffer, t_res, "continue")) - fatal("Plugin returned an invalid response to the " - "custommsg hook: %s", buffer); - - /* call next hook */ - return true; -} - -static void custommsg_final(struct custommsg_payload *payload STEALS) -{ - tal_steal(tmpctx, payload); -} - -static void custommsg_payload_serialize(struct custommsg_payload *payload, - struct json_stream *stream, - struct plugin *plugin) -{ - json_add_hex_talarr(stream, "payload", payload->msg); - json_add_node_id(stream, "peer_id", &payload->peer_id); -} - -REGISTER_PLUGIN_HOOK(custommsg, - custommsg_cb, - custommsg_final, - custommsg_payload_serialize, - struct custommsg_payload *); - -void handle_custommsg_in(struct lightningd *ld, const struct node_id *peer_id, - const u8 *msg) -{ - struct custommsg_payload *p = tal(NULL, struct custommsg_payload); - u8 *custommsg; - - if (!fromwire_custommsg_in(NULL, msg, &custommsg)) { - log_broken(ld->log, "Malformed custommsg from peer %s: %s", - type_to_string(tmpctx, struct node_id, peer_id), - tal_hex(tmpctx, msg)); - return; - } - - p->peer_id = *peer_id; - p->msg = tal_steal(p, custommsg); - plugin_hook_call_custommsg(ld, p); -} - -static struct command_result *json_sendcustommsg(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - struct json_stream *response; - struct node_id *dest; - struct peer *peer; - struct subd *owner; - u8 *msg; - int type; - - if (!param(cmd, buffer, params, - p_req("node_id", param_node_id, &dest), - p_req("msg", param_bin_from_hex, &msg), - NULL)) - return command_param_failed(); - - type = fromwire_peektype(msg); - if (peer_wire_is_defined(type)) { - return command_fail( - cmd, JSONRPC2_INVALID_REQUEST, - "Cannot send messages of type %d (%s). It is not possible " - "to send messages that have a type managed internally " - "since that might cause issues with the internal state " - "tracking.", - type, peer_wire_name(type)); - } - - if (type % 2 == 0) { - return command_fail( - cmd, JSONRPC2_INVALID_REQUEST, - "Cannot send even-typed %d custom message. Currently " - "custom messages are limited to odd-numbered message " - "types, as even-numbered types might result in " - "disconnections.", - type); - } - - peer = peer_by_id(cmd->ld, dest); - if (!peer) { - return command_fail(cmd, JSONRPC2_INVALID_REQUEST, - "No such peer: %s", - type_to_string(cmd, struct node_id, dest)); - } - - owner = peer_get_owning_subd(peer); - if (owner == NULL) { - return command_fail(cmd, JSONRPC2_INVALID_REQUEST, - "Peer is not connected: %s", - type_to_string(cmd, struct node_id, dest)); - } - - /* Only a couple of subdaemons have the ability to send custom - * messages. We whitelist those, and error if the current owner is not - * in the whitelist. The reason is that some subdaemons do not handle - * spontaneous messages from the master well (I'm looking at you - * `closingd`...). */ - if (!streq(owner->name, "channeld") && - !streq(owner->name, "openingd")) { - return command_fail(cmd, JSONRPC2_INVALID_REQUEST, - "Peer is currently owned by %s which does " - "not support injecting custom messages.", - owner->name); - } - - subd_send_msg(owner, take(towire_custommsg_out(cmd, msg))); - - response = json_stream_success(cmd); - json_add_string(response, "status", - tal_fmt(cmd, - "Message sent to subdaemon %s for delivery", - owner->name)); - - return command_success(cmd, response); -} - -static const struct json_command sendcustommsg_command = { - "sendcustommsg", - "utility", - json_sendcustommsg, - "Send a custom message to the peer with the given {node_id}", - .verbose = "sendcustommsg node_id hexcustommsg", -}; - -AUTODATA(json_command, &sendcustommsg_command); - -#ifdef COMPAT_V0100 -#ifdef DEVELOPER -static const struct json_command dev_sendcustommsg_command = { - "dev-sendcustommsg", - "utility", - json_sendcustommsg, - "Send a custom message to the peer with the given {node_id}", - .verbose = "dev-sendcustommsg node_id hexcustommsg", -}; - -AUTODATA(json_command, &dev_sendcustommsg_command); -#endif /* DEVELOPER */ -#endif /* COMPAT_V0100 */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index dff52d9d1840..e16e80ba054b 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -98,9 +98,6 @@ struct htlc_in_map *load_channels_from_wallet(struct lightningd *ld); void peer_dev_memleak(struct command *cmd); #endif /* DEVELOPER */ -void handle_custommsg_in(struct lightningd *ld, const struct node_id *peer_id, - const u8 *msg); - /* Triggered at each new block. */ void waitblockheight_notify_new_block(struct lightningd *ld, u32 block_height); diff --git a/lightningd/subd.c b/lightningd/subd.c index 81614f8d1b78..60bb5318beac 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -18,7 +18,6 @@ #include #include #include -#include #include void maybe_subd_child(struct lightningd *ld, int childpid, int wstatus) @@ -831,8 +830,7 @@ void subd_send_msg(struct subd *sd, const u8 *msg_out) u16 type = fromwire_peektype(msg_out); /* FIXME: We should use unique upper bits for each daemon, then * have generate-wire.py add them, just assert here. */ - assert(!strstarts(common_wire_name(type), "INVALID") || - !strstarts(sd->msgname(type), "INVALID")); + assert(!strstarts(sd->msgname(type), "INVALID")); msg_enqueue(sd->outq, msg_out); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index cdfcd9ee44cb..2868e952c84c 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -482,11 +482,6 @@ struct command_result *param_array(struct command *cmd UNNEEDED, const char *nam const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const jsmntok_t **arr UNNEEDED) { fprintf(stderr, "param_array called!\n"); abort(); } -/* Generated stub for param_bin_from_hex */ -struct command_result *param_bin_from_hex(struct command *cmd UNNEEDED, const char *name UNNEEDED, - const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - u8 **bin UNNEEDED) -{ fprintf(stderr, "param_bin_from_hex called!\n"); abort(); } /* Generated stub for param_bool */ struct command_result *param_bool(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -560,9 +555,6 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name /* Generated stub for peer_active_channel */ struct channel *peer_active_channel(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_active_channel called!\n"); abort(); } -/* Generated stub for peer_get_owning_subd */ -struct subd *peer_get_owning_subd(struct peer *peer UNNEEDED) -{ fprintf(stderr, "peer_get_owning_subd called!\n"); abort(); } /* Generated stub for peer_memleak_done */ void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED) { fprintf(stderr, "peer_memleak_done called!\n"); abort(); } diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 87a38d3520db..bd870c95a119 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -42,7 +42,6 @@ #include #include #include -#include #include /* stdin == lightningd, 3 == peer, 4 == gossipd, 5 = hsmd */ @@ -897,17 +896,6 @@ static void dualopend_dev_memleak(struct state *state) } #endif /* DEVELOPER */ -/* We were told to send a custommsg to the peer by `lightningd`. All the - * verification is done on the side of `lightningd` so we should be good to - * just forward it here. */ -static void dualopend_send_custommsg(struct state *state, const u8 *msg) -{ - u8 *inner; - if (!fromwire_custommsg_out(tmpctx, msg, &inner)) - master_badmsg(WIRE_CUSTOMMSG_OUT, msg); - peer_write(state->pps, take(inner)); -} - static u8 *psbt_to_tx_sigs_msg(const tal_t *ctx, struct state *state, const struct wally_psbt *psbt) @@ -3465,23 +3453,6 @@ static void handle_gossip_in(struct state *state) handle_gossip_msg(state->pps, take(msg)); } -/* Try to handle a custommsg Returns true if it was a custom message and has - * been handled, false if the message was not handled. - */ -static bool dualopend_handle_custommsg(const u8 *msg) -{ - enum peer_wire type = fromwire_peektype(msg); - if (type % 2 == 1 && !peer_wire_is_defined(type)) { - /* The message is not part of the messages we know how to - * handle. Assuming this is a custommsg, we just forward it to the - * master. */ - wire_sync_write(REQ_FD, take(towire_custommsg_in(NULL, msg))); - return true; - } else { - return false; - } -} - /* BOLT #2: * * A receiving node: @@ -3582,10 +3553,9 @@ static void do_reconnect_dance(struct state *state) do { clean_tmpctx(); msg = peer_read(tmpctx, state->pps); - } while (dualopend_handle_custommsg(msg) - || handle_peer_gossip_or_error(state->pps, - &state->channel_id, - msg)); + } while (handle_peer_gossip_or_error(state->pps, + &state->channel_id, + msg)); if (!fromwire_channel_reestablish (msg, &cid, @@ -3703,16 +3673,6 @@ static u8 *handle_master_in(struct state *state) case WIRE_DUALOPEND_LOCAL_PRIVATE_CHANNEL: break; } - - /* Now handle common messages. */ - switch ((enum common_wire)t) { - case WIRE_CUSTOMMSG_OUT: - dualopend_send_custommsg(state, msg); - /* We send these. */ - case WIRE_CUSTOMMSG_IN: - break; - } - status_failed(STATUS_FAIL_MASTER_IO, "Unknown msg %s", tal_hex(tmpctx, msg)); } @@ -3790,16 +3750,6 @@ static u8 *handle_peer_in(struct state *state) break; } - /* Handle custommsgs */ - enum peer_wire type = fromwire_peektype(msg); - if (type % 2 == 1 && !peer_wire_is_defined(type)) { - /* The message is not part of the messages we know how to - * handle. Assuming this is a custommsg, we just - * forward it to master. */ - wire_sync_write(REQ_FD, take(towire_custommsg_in(NULL, msg))); - return NULL; - } - /* Handles standard cases, and legal unknown ones. */ if (handle_peer_gossip_or_error(state->pps, &state->channel_id, msg)) diff --git a/openingd/openingd.c b/openingd/openingd.c index 42257adfbed1..854c5cd088fc 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include @@ -1260,16 +1259,6 @@ static u8 *handle_peer_in(struct state *state) if (t == WIRE_OPEN_CHANNEL) return fundee_channel(state, msg); - /* Handle custommsgs */ - enum peer_wire type = fromwire_peektype(msg); - if (type % 2 == 1 && !peer_wire_is_defined(type)) { - /* The message is not part of the messages we know how to - * handle. Assuming this is a custommsg, we just forward it to the - * master. */ - wire_sync_write(REQ_FD, take(towire_custommsg_in(NULL, msg))); - return NULL; - } - /* Handles standard cases, and legal unknown ones. */ if (handle_peer_gossip_or_error(state->pps, &state->channel_id, msg)) @@ -1334,17 +1323,6 @@ static void handle_dev_memleak(struct state *state, const u8 *msg) } #endif /* DEVELOPER */ -/* We were told to send a custommsg to the peer by `lightningd`. All the - * verification is done on the side of `lightningd` so we should be good to - * just forward it here. */ -static void openingd_send_custommsg(struct state *state, const u8 *msg) -{ - u8 *inner; - if (!fromwire_custommsg_out(tmpctx, msg, &inner)) - master_badmsg(WIRE_CUSTOMMSG_OUT, msg); - peer_write(state->pps, take(inner)); -} - /* 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) @@ -1406,16 +1384,6 @@ static u8 *handle_master_in(struct state *state) break; } - /* Now handle common messages. */ - switch ((enum common_wire)t) { - case WIRE_CUSTOMMSG_OUT: - openingd_send_custommsg(state, msg); - return NULL; - /* We send these. */ - case WIRE_CUSTOMMSG_IN: - break; - } - status_failed(STATUS_FAIL_MASTER_IO, "Unknown msg %s", tal_hex(tmpctx, msg)); } diff --git a/tests/test_misc.py b/tests/test_misc.py index 8fb538c5a257..e5827b719241 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2178,8 +2178,8 @@ def test_sendcustommsg(node_factory): # This should work since the peer is currently owned by `channeld` l2.rpc.sendcustommsg(l1.info['id'], msg) l2.daemon.wait_for_log( - r'{peer_id}-{owner}-chan#[0-9]: \[OUT\] {msg}'.format( - owner='channeld', msg=msg, peer_id=l1.info['id'] + r'{peer_id}-{owner}: \[OUT\] {msg}'.format( + owner='connectd', msg=msg, peer_id=l1.info['id'] ) ) l1.daemon.wait_for_log(r'\[IN\] {}'.format(msg)) @@ -2193,8 +2193,8 @@ def test_sendcustommsg(node_factory): # This should work since the peer is currently owned by `openingd` l2.rpc.sendcustommsg(l4.info['id'], msg) l2.daemon.wait_for_log( - r'{peer_id}-{owner}-chan#[0-9]: \[OUT\] {msg}'.format( - owner='openingd', msg=msg, peer_id=l4.info['id'] + r'{peer_id}-{owner}: \[OUT\] {msg}'.format( + owner='connectd', msg=msg, peer_id=l4.info['id'] ) ) l4.daemon.wait_for_log(r'\[IN\] {}'.format(msg)) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 1ca2fd25de15..7778c252ce66 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -128,9 +128,6 @@ bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void * /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } -/* Generated stub for fromwire_custommsg_in */ -bool fromwire_custommsg_in(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **msg UNNEEDED) -{ fprintf(stderr, "fromwire_custommsg_in called!\n"); abort(); } /* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED) { fprintf(stderr, "fromwire_hsmd_get_output_scriptpubkey_reply called!\n"); abort(); } @@ -524,11 +521,6 @@ void outpointfilter_remove(struct outpointfilter *of UNNEEDED, bool param(struct command *cmd UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t params[] UNNEEDED, ...) { fprintf(stderr, "param called!\n"); abort(); } -/* Generated stub for param_bin_from_hex */ -struct command_result *param_bin_from_hex(struct command *cmd UNNEEDED, const char *name UNNEEDED, - const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - u8 **bin UNNEEDED) -{ fprintf(stderr, "param_bin_from_hex called!\n"); abort(); } /* Generated stub for param_bool */ struct command_result *param_bool(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -594,9 +586,6 @@ void payment_store(struct lightningd *ld UNNEEDED, struct wallet_payment *paymen void payment_succeeded(struct lightningd *ld UNNEEDED, struct htlc_out *hout UNNEEDED, const struct preimage *rval UNNEEDED) { fprintf(stderr, "payment_succeeded called!\n"); abort(); } -/* Generated stub for peer_get_owning_subd */ -struct subd *peer_get_owning_subd(struct peer *peer UNNEEDED) -{ fprintf(stderr, "peer_get_owning_subd called!\n"); abort(); } /* Generated stub for peer_memleak_done */ void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED) { fprintf(stderr, "peer_memleak_done called!\n"); abort(); } @@ -619,12 +608,6 @@ void peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UN void peer_start_openingd(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } -/* Generated stub for peer_wire_is_defined */ -bool peer_wire_is_defined(u16 type UNNEEDED) -{ fprintf(stderr, "peer_wire_is_defined called!\n"); abort(); } -/* Generated stub for peer_wire_name */ -const char *peer_wire_name(int e UNNEEDED) -{ fprintf(stderr, "peer_wire_name called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) @@ -713,9 +696,6 @@ u8 *towire_connectd_peer_disconnected(const tal_t *ctx UNNEEDED, const struct no /* 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 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(); } /* Generated stub for towire_errorfmt */ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, diff --git a/wire/Makefile b/wire/Makefile index 9651eaf1e895..44380df67b91 100644 --- a/wire/Makefile +++ b/wire/Makefile @@ -10,7 +10,6 @@ WIRE_HEADERS := wire/onion_defs.h \ wire/peer$(EXP)_wiregen.h \ wire/onion$(EXP)_wiregen.h \ wire/bolt12$(EXP)_wiregen.h \ - wire/common_wiregen.h \ wire/channel_type_wiregen.h \ wire/peer$(EXP)_printgen.h \ wire/onion$(EXP)_printgen.h @@ -22,7 +21,6 @@ WIRE_SRC := wire/wire_sync.c \ wire/peer_wire.c \ wire/tlvstream.c \ wire/towire.c \ - wire/common_wiregen.c \ wire/bolt12$(EXP)_wiregen.c \ wire/peer$(EXP)_wiregen.c \ wire/channel_type_wiregen.c \ diff --git a/wire/common_wire.csv b/wire/common_wire.csv deleted file mode 100644 index 7e607c806ccc..000000000000 --- a/wire/common_wire.csv +++ /dev/null @@ -1,10 +0,0 @@ -# A custom message that we got from a peer and don't know how to handle, so we -# forward it to the master for further handling. -msgtype,custommsg_in,1030 -msgdata,custommsg_in,msg_len,u16, -msgdata,custommsg_in,msg,u8,msg_len - -# A custom message that the master tells us to send to the peer. -msgtype,custommsg_out,1031 -msgdata,custommsg_out,msg_len,u16, -msgdata,custommsg_out,msg,u8,msg_len From 1abbc3d06a106933b9a00ba5b3388f29391fd25d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0282/1530] channeld: simply exit if hsmd vanishes. We currently die when gossipd vanishes, but our direct connection will go away. We then complain if the node is shutting down while we're talking to hsmd. Signed-off-by: Rusty Russell --- channeld/Makefile | 1 + channeld/channeld.c | 15 +++++---------- channeld/channeld.h | 10 ++++++++++ channeld/watchtower.c | 15 ++++++--------- 4 files changed, 22 insertions(+), 19 deletions(-) create mode 100644 channeld/channeld.h diff --git a/channeld/Makefile b/channeld/Makefile index 2c9e89d758e2..bef41c69a92a 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -4,6 +4,7 @@ CHANNELD_HEADERS := \ channeld/full_channel_error_names_gen.h \ channeld/channeld_wiregen.h \ channeld/channeld_htlc.h \ + channeld/channeld.h \ channeld/commit_tx.h \ channeld/full_channel.h \ channeld/full_channel_error.h \ diff --git a/channeld/channeld.c b/channeld/channeld.c index dfc3923181d2..fb90922d2c21 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -193,23 +194,17 @@ static void billboard_update(const struct peer *peer) peer_billboard(false, update); } -static const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) +const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) { u8 *msg; - int type = fromwire_peektype(req); + /* hsmd goes away at shutdown. That's OK. */ if (!wire_sync_write(HSM_FD, req)) - status_failed(STATUS_FAIL_HSM_IO, - "Writing %s to HSM: %s", - hsmd_wire_name(type), - strerror(errno)); + exit(0); msg = wire_sync_read(ctx, HSM_FD); if (!msg) - status_failed(STATUS_FAIL_HSM_IO, - "Reading resp to %s: %s", - hsmd_wire_name(type), - strerror(errno)); + exit(0); return msg; } diff --git a/channeld/channeld.h b/channeld/channeld.h new file mode 100644 index 000000000000..a2b26b08246a --- /dev/null +++ b/channeld/channeld.h @@ -0,0 +1,10 @@ +#ifndef LIGHTNING_CHANNELD_CHANNELD_H +#define LIGHTNING_CHANNELD_CHANNELD_H +#include "config.h" +#include +#include +#include + +const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES); + +#endif /* LIGHTNING_CHANNELD_CHANNELD_H */ diff --git a/channeld/watchtower.c b/channeld/watchtower.c index 20d273d6510f..e91dc4f89699 100644 --- a/channeld/watchtower.c +++ b/channeld/watchtower.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -26,7 +27,7 @@ penalty_tx_create(const tal_t *ctx, struct bitcoin_tx *tx; struct keyset keyset; size_t weight; - u8 *msg; + const u8 *msg; struct amount_sat fee, min_out, amt; struct bitcoin_signature sig; u32 locktime = 0; @@ -105,16 +106,12 @@ penalty_tx_create(const tal_t *ctx, bitcoin_tx_finalize(tx); u8 *hsm_sign_msg = - towire_hsmd_sign_penalty_to_us(ctx, &remote_per_commitment_secret, + towire_hsmd_sign_penalty_to_us(tmpctx, &remote_per_commitment_secret, tx, wscript); - if (!wire_sync_write(hsm_fd, take(hsm_sign_msg))) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Writing sign request to hsm"); - - msg = wire_sync_read(tmpctx, hsm_fd); - if (!msg || !fromwire_hsmd_sign_tx_reply(msg, &sig)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, + msg = hsm_req(tmpctx, hsm_sign_msg); + if (!fromwire_hsmd_sign_tx_reply(msg, &sig)) + status_failed(STATUS_FAIL_HSM_IO, "Reading sign_tx_reply: %s", tal_hex(tmpctx, msg)); witness = bitcoin_witness_sig_and_element(tx, &sig, &ONE, sizeof(ONE), From 3c5d27e3e99cff8a042bde298339cfee153f7454 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0283/1530] subdaemons: remove gossipd fd from per-peer daemons. Signed-off-by: Rusty Russell --- channeld/channeld.c | 25 ++---- closingd/closingd.c | 17 ++-- common/peer_failed.c | 1 - common/per_peer_state.c | 18 +--- common/per_peer_state.h | 10 +-- common/read_peer_msg.c | 96 ++------------------- common/read_peer_msg.h | 40 ++------- connectd/connectd.c | 28 +++--- connectd/connectd.h | 3 + connectd/connectd_wire.csv | 4 +- lightningd/channel_control.c | 5 +- lightningd/closing_control.c | 4 +- lightningd/connect_control.c | 6 +- lightningd/dual_open_control.c | 10 +-- lightningd/opening_common.c | 5 +- lightningd/opening_control.c | 13 ++- lightningd/peer_control.c | 10 +-- lightningd/peer_control.h | 3 +- lightningd/peer_fd.c | 13 ++- lightningd/peer_fd.h | 13 ++- lightningd/subd.c | 6 +- lightningd/test/run-find_my_abspath.c | 2 +- lightningd/test/run-invoice-select-inchan.c | 2 +- lightningd/test/run-shuffle_fds.c | 2 +- openingd/dualopend.c | 61 +++---------- openingd/openingd.c | 63 ++++---------- wallet/test/run-wallet.c | 2 +- 27 files changed, 120 insertions(+), 342 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index fb90922d2c21..e491a52e1ba5 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -45,9 +45,9 @@ #include #include -/* stdin == requests, 3 == peer, 4 = gossip, 5 = HSM */ +/* stdin == requests, 3 == peer, 4 = HSM */ #define MASTER_FD STDIN_FILENO -#define HSM_FD 5 +#define HSM_FD 4 struct peer { struct per_peer_state *pps; @@ -2132,13 +2132,12 @@ static void peer_in(struct peer *peer, const u8 *msg) { enum peer_wire type = fromwire_peektype(msg); - if (handle_peer_gossip_or_error(peer->pps, &peer->channel_id, msg)) + if (handle_peer_error(peer->pps, &peer->channel_id, msg)) return; /* Must get funding_locked before almost anything. */ if (!peer->funding_locked[REMOTE]) { if (type != WIRE_FUNDING_LOCKED - && type != WIRE_PONG && type != WIRE_SHUTDOWN /* We expect these for v2 !! */ && type != WIRE_TX_SIGNATURES @@ -2215,7 +2214,7 @@ static void peer_in(struct peer *peer, const u8 *msg) handle_unexpected_reestablish(peer, msg); return; - /* These are all swallowed by handle_peer_gossip_or_error */ + /* These are all swallowed by connectd */ case WIRE_CHANNEL_ANNOUNCEMENT: case WIRE_CHANNEL_UPDATE: case WIRE_NODE_ANNOUNCEMENT: @@ -2798,7 +2797,7 @@ static void peer_reconnect(struct peer *peer, do { clean_tmpctx(); msg = peer_read(tmpctx, peer->pps); - } while (handle_peer_gossip_or_error(peer->pps, &peer->channel_id, msg) || + } while (handle_peer_error(peer->pps, &peer->channel_id, msg) || capture_premature_msg(&premature_msgs, msg)); got_reestablish: @@ -3752,9 +3751,9 @@ static void init_channel(struct peer *peer) tal_dup(peer, struct penalty_base, &pbases[i])); tal_free(pbases); - /* stdin == requests, 3 == peer, 4 = gossip */ + /* stdin == requests, 3 == peer */ peer->pps = new_per_peer_state(peer); - per_peer_state_set_fds(peer->pps, 3, 4); + per_peer_state_set_fd(peer->pps, 3); status_debug("init %s: remote_per_commit = %s, old_remote_per_commit = %s" " next_idx_local = %"PRIu64 @@ -3895,11 +3894,10 @@ int main(int argc, char *argv[]) FD_ZERO(&fds_in); FD_SET(MASTER_FD, &fds_in); FD_SET(peer->pps->peer_fd, &fds_in); - FD_SET(peer->pps->gossip_fd, &fds_in); FD_ZERO(&fds_out); FD_SET(peer->pps->peer_fd, &fds_out); - nfds = peer->pps->gossip_fd+1; + nfds = peer->pps->peer_fd+1; while (!shutdown_complete(peer)) { struct timemono first; @@ -3958,13 +3956,6 @@ int main(int argc, char *argv[]) /* This could take forever, but who cares? */ msg = peer_read(tmpctx, peer->pps); peer_in(peer, msg); - } else if (FD_ISSET(peer->pps->gossip_fd, &rfds)) { - msg = wire_sync_read(tmpctx, peer->pps->gossip_fd); - /* Gossipd hangs up on us to kill us when a new - * connection comes in. */ - if (!msg) - peer_failed_connection_lost(); - handle_gossip_msg(peer->pps, take(msg)); } } diff --git a/closingd/closingd.c b/closingd/closingd.c index 87db15cda9c6..05c45cfc2074 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -29,9 +29,9 @@ #include #include -/* stdin == requests, 3 == peer, 4 = gossip, 5 = hsmd */ +/* stdin == requests, 3 == peer, 4 = hsmd */ #define REQ_FD STDIN_FILENO -#define HSM_FD 5 +#define HSM_FD 4 static void notify(enum log_level level, const char *fmt, ...) { @@ -117,15 +117,10 @@ static u8 *closing_read_peer_msg(const tal_t *ctx, { for (;;) { u8 *msg; - bool from_gossipd; clean_tmpctx(); - msg = peer_or_gossip_sync_read(ctx, pps, &from_gossipd); - if (from_gossipd) { - handle_gossip_msg(pps, take(msg)); - continue; - } - if (!handle_peer_gossip_or_error(pps, channel_id, msg)) + msg = peer_read(ctx, pps); + if (!handle_peer_error(pps, channel_id, msg)) return msg; } } @@ -892,9 +887,9 @@ int main(int argc, char *argv[]) &wrong_funding)) master_badmsg(WIRE_CLOSINGD_INIT, msg); - /* stdin == requests, 3 == peer, 4 = gossip, 5 = hsmd */ + /* stdin == requests, 3 == peer, 4 = hsmd */ pps = notleak(new_per_peer_state(ctx)); - per_peer_state_set_fds(pps, 3, 4); + per_peer_state_set_fd(pps, 3); funding_wscript = bitcoin_redeem_2of2(ctx, &funding_pubkey[LOCAL], diff --git a/common/peer_failed.c b/common/peer_failed.c index e9f5487dafdc..c9c18d84dbe7 100644 --- a/common/peer_failed.c +++ b/common/peer_failed.c @@ -19,7 +19,6 @@ peer_fatal_continue(const u8 *msg TAKES, const struct per_peer_state *pps) status_send(msg); status_send_fd(pps->peer_fd); - status_send_fd(pps->gossip_fd); exit(0x80 | (reason & 0xFF)); } diff --git a/common/per_peer_state.c b/common/per_peer_state.c index b33305538d27..981e06c07126 100644 --- a/common/per_peer_state.c +++ b/common/per_peer_state.c @@ -12,39 +12,25 @@ static void destroy_per_peer_state(struct per_peer_state *pps) { if (pps->peer_fd != -1) close(pps->peer_fd); - if (pps->gossip_fd != -1) - close(pps->gossip_fd); } struct per_peer_state *new_per_peer_state(const tal_t *ctx) { struct per_peer_state *pps = tal(ctx, struct per_peer_state); - pps->peer_fd = pps->gossip_fd = -1; + pps->peer_fd = -1; tal_add_destructor(pps, destroy_per_peer_state); return pps; } -void per_peer_state_set_fds(struct per_peer_state *pps, - int peer_fd, int gossip_fd) +void per_peer_state_set_fd(struct per_peer_state *pps, int peer_fd) { assert(pps->peer_fd == -1); - assert(pps->gossip_fd == -1); pps->peer_fd = peer_fd; - pps->gossip_fd = gossip_fd; -} - -void per_peer_state_set_fds_arr(struct per_peer_state *pps, const int *fds) -{ - /* We expect 2 fds. */ - assert(tal_count(fds) == 2); - per_peer_state_set_fds(pps, fds[0], fds[1]); } void per_peer_state_fdpass_send(int fd, const struct per_peer_state *pps) { assert(pps->peer_fd != -1); - assert(pps->gossip_fd != -1); fdpass_send(fd, pps->peer_fd); - fdpass_send(fd, pps->gossip_fd); } diff --git a/common/per_peer_state.h b/common/per_peer_state.h index af41d95f0a8d..0561d8ce2870 100644 --- a/common/per_peer_state.h +++ b/common/per_peer_state.h @@ -9,19 +9,15 @@ /* Things we hand between daemons to talk to peers. */ struct per_peer_state { /* If not -1, closed on freeing */ - int peer_fd, gossip_fd; + int peer_fd; }; /* Allocate a new per-peer state and add destructor to close fds if set; - * sets fds to -1. */ + * sets peer_fd to -1. */ struct per_peer_state *new_per_peer_state(const tal_t *ctx); /* Initialize the fds (must be -1 previous) */ -void per_peer_state_set_fds(struct per_peer_state *pps, - int peer_fd, int gossip_fd); - -/* Array version of above: tal_count(fds) must be 2 */ -void per_peer_state_set_fds_arr(struct per_peer_state *pps, const int *fds); +void per_peer_state_set_fd(struct per_peer_state *pps, int peer_fd); void per_peer_state_fdpass_send(int fd, const struct per_peer_state *pps); diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index c2f87344cf6f..fcf403f91799 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -12,39 +12,6 @@ #include #include -u8 *peer_or_gossip_sync_read(const tal_t *ctx, - struct per_peer_state *pps, - bool *from_gossipd) -{ - fd_set readfds; - u8 *msg; - - FD_ZERO(&readfds); - FD_SET(pps->peer_fd, &readfds); - FD_SET(pps->gossip_fd, &readfds); - - if (select(pps->peer_fd > pps->gossip_fd - ? pps->peer_fd + 1 : pps->gossip_fd + 1, - &readfds, NULL, NULL, NULL) <= 0) { - status_failed(STATUS_FAIL_GOSSIP_IO, - "select failed?: %s", strerror(errno)); - } - - if (FD_ISSET(pps->peer_fd, &readfds)) { - msg = peer_read(ctx, pps); - *from_gossipd = false; - return msg; - } - - msg = wire_sync_read(ctx, pps->gossip_fd); - if (!msg) - status_failed(STATUS_FAIL_GOSSIP_IO, - "Error reading gossip msg: %s", - strerror(errno)); - *from_gossipd = true; - return msg; -} - bool is_peer_error(const tal_t *ctx, const u8 *msg, const struct channel_id *channel_id, char **desc, bool *warning) @@ -94,70 +61,23 @@ bool is_wrong_channel(const u8 *msg, const struct channel_id *expected, return !channel_id_eq(expected, actual); } -void handle_gossip_msg(struct per_peer_state *pps, const u8 *msg TAKES) -{ - u8 *gossip; - - /* It's a raw gossip msg: this copies or takes() */ - gossip = tal_dup_talarr(tmpctx, u8, msg); - - /* Gossipd can send us gossip messages, OR warnings */ - if (fromwire_peektype(gossip) == WIRE_WARNING) { - peer_write(pps, gossip); - peer_failed_connection_lost(); - } else { - peer_write(pps, gossip); - } -} - -bool handle_peer_gossip_or_error(struct per_peer_state *pps, - const struct channel_id *channel_id, - const u8 *msg TAKES) +bool handle_peer_error(struct per_peer_state *pps, + const struct channel_id *channel_id, + const u8 *msg TAKES) { char *err; bool warning; - u8 *pong; - -#if DEVELOPER - /* Any odd-typed unknown message is handled by the caller, so if we - * find one here it's an error. */ - assert(!is_unknown_msg_discardable(msg)); -#else - /* BOLT #1: - * - * A receiving node: - * - upon receiving a message of _odd_, unknown type: - * - MUST ignore the received message. - */ - if (is_unknown_msg_discardable(msg)) - goto handled; -#endif - - if (check_ping_make_pong(NULL, msg, &pong)) { - if (pong) - peer_write(pps, take(pong)); - return true; - } else if (is_msg_for_gossipd(msg)) { - wire_sync_write(pps->gossip_fd, msg); - /* wire_sync_write takes, so don't take again. */ - return true; - } - if (is_peer_error(tmpctx, msg, channel_id, &err, &warning)) { /* Ignore unknown channel errors. */ - if (!err) - goto handled; + if (!err) { + if (taken(msg)) + tal_free(msg); + return true; + } /* We hang up when a warning is received. */ peer_failed_received_errmsg(pps, err, channel_id, warning); - - goto handled; } return false; - -handled: - if (taken(msg)) - tal_free(msg); - return true; } diff --git a/common/read_peer_msg.h b/common/read_peer_msg.h index a3fecb83f1ac..2719eb2a3c64 100644 --- a/common/read_peer_msg.h +++ b/common/read_peer_msg.h @@ -8,25 +8,6 @@ struct crypto_state; struct channel_id; struct per_peer_state; -/** - * peer_or_gossip_sync_read - read a peer message, or maybe a gossip msg. - * @ctx: context to allocate return packet from. - * @pps: the per-peer peer state and fds - * @from_gossipd: true if the msg was from gossipd, otherwise false. - * - * Will call peer_failed_connection_lost() or - * status_failed(STATUS_FAIL_GOSSIP_IO) or return a message. - * - * Usually, you should call handle_gossip_msg if *@from_gossipd is - * true, otherwise if is_peer_error() handle the error, otherwise if - * is_msg_for_gossipd() then send to gossipd, otherwise if is - * is_wrong_channel() send that as a reply. Otherwise it should be - * a valid message. - */ -u8 *peer_or_gossip_sync_read(const tal_t *ctx, - struct per_peer_state *pps, - bool *from_gossipd); - /** * is_peer_error - if it's an error, describe if it applies to this channel. * @ctx: context to allocate return from. @@ -55,26 +36,15 @@ bool is_wrong_channel(const u8 *msg, const struct channel_id *expected, /** - * handle_peer_gossip_or_error - simple handler for all the above cases. + * handle_peer_error - simple handler for errors * @pps: per-peer state. * @channel_id: the channel id of the current channel. * @msg: the peer message (only taken if returns true). * - * This returns true if it handled the packet: a gossip packet (forwarded - * to gossipd), or an error packet (causes peer_failed_received_errmsg or - * ignored), or a ping (may reply with pong). - */ -bool handle_peer_gossip_or_error(struct per_peer_state *pps, - const struct channel_id *channel_id, - const u8 *msg TAKES); - -/** - * handle_timestamp_filter - deal with timestamp filter requests. - * @pps: per-peer state. - * @msg: the peer message (only taken if returns true). + * This returns true if it handled the packet. */ -bool handle_timestamp_filter(struct per_peer_state *pps, const u8 *msg TAKES); +bool handle_peer_error(struct per_peer_state *pps, + const struct channel_id *channel_id, + const u8 *msg TAKES); -/* We got this message from gossipd: forward/quit as it asks. */ -void handle_gossip_msg(struct per_peer_state *pps, const u8 *msg TAKES); #endif /* LIGHTNING_COMMON_READ_PEER_MSG_H */ diff --git a/connectd/connectd.c b/connectd/connectd.c index 3016ceb37419..b70f9bafc3dc 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -336,6 +336,8 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, /*~ When we free a peer, we remove it from the daemon's hashtable */ static void destroy_peer(struct peer *peer, struct daemon *daemon) { + if (peer->gossip_fd >= 0) + close(peer->gossip_fd); peer_htable_del(&daemon->peers, peer); } @@ -395,7 +397,7 @@ struct io_plan *peer_connected(struct io_conn *conn, struct peer *peer; int unsup; size_t depender, missing; - int subd_fd, gossip_fd; + int subd_fd; peer = peer_htable_get(&daemon->peers, id); if (peer) @@ -454,8 +456,8 @@ struct io_plan *peer_connected(struct io_conn *conn, return io_close(conn); /* If gossipd can't give us a file descriptor, we give up connecting. */ - gossip_fd = get_gossipfd(daemon, id, their_features); - if (gossip_fd < 0) { + peer->gossip_fd = get_gossipfd(daemon, id, their_features); + if (peer->gossip_fd < 0) { close(subd_fd); return tal_free(peer); } @@ -469,10 +471,9 @@ struct io_plan *peer_connected(struct io_conn *conn, /*~ daemon_conn is a message queue for inter-daemon communication: we * queue up the `connect_peer_connected` message to tell lightningd - * we have connected, and give the peer and gossip fds. */ + * we have connected, and give the peer fd. */ daemon_conn_send(daemon->master, take(msg)); daemon_conn_send_fd(daemon->master, subd_fd); - daemon_conn_send_fd(daemon->master, gossip_fd); /*~ Now we set up this connection to read/write from subd */ return multiplex_peer_setup(conn, peer); @@ -1894,20 +1895,19 @@ static void peer_final_msg(struct io_conn *conn, struct peer *peer; struct node_id id; u8 *finalmsg; + int peer_fd; if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &finalmsg)) master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); - /* Get the peer_fd and gossip_fd for this peer: we don't need them. */ + /* Get the peer_fd for this peer: we don't need it though! */ io_fd_block(io_conn_fd(conn), true); - for (size_t i = 0; i < 2; i++) { - int fd = fdpass_recv(io_conn_fd(conn)); - if (fd == -1) - status_failed(STATUS_FAIL_MASTER_IO, - "Getting fd %zu after peer_final_msg: %s", - i, strerror(errno)); - close(fd); - } + peer_fd = fdpass_recv(io_conn_fd(conn)); + if (peer_fd == -1) + status_failed(STATUS_FAIL_MASTER_IO, + "Getting peer fd after peer_final_msg: %s", + strerror(errno)); + close(peer_fd); io_fd_block(io_conn_fd(conn), false); /* This can happen if peer hung up on us. */ diff --git a/connectd/connectd.h b/connectd/connectd.h index 7b2eaa98da3d..a630ff64968e 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -81,6 +81,9 @@ struct peer { /* Random ping timer, to detect dead connections. */ struct oneshot *ping_timer; + /* FIXME: remove! */ + int gossip_fd; + #if DEVELOPER bool dev_read_enabled; /* If non-NULL, this counts down; 0 means disable */ diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 01c03335cb32..c735f3c93173 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -60,7 +60,7 @@ msgdata,connectd_connect_failed,failreason,wirestring, msgdata,connectd_connect_failed,seconds_to_delay,u32, msgdata,connectd_connect_failed,addrhint,?wireaddr_internal, -# Connectd -> master: we got a peer. Three fds: peer, gossip and gossip_store +# Connectd -> master: we got a peer. Plus fd for peer daemon msgtype,connectd_peer_connected,2002 msgdata,connectd_peer_connected,id,node_id, msgdata,connectd_peer_connected,addr,wireaddr_internal, @@ -72,7 +72,7 @@ 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 +# master -> connectd: give message to peer and disconnect. Plus fd for peer msgtype,connectd_peer_final_msg,2003 msgdata,connectd_peer_final_msg,id,node_id, msgdata,connectd_peer_final_msg,len,u16, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index dcfb4536686b..d36baacd8749 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -506,9 +506,9 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) peer_got_shutdown(sd->channel, msg); break; case WIRE_CHANNELD_SHUTDOWN_COMPLETE: - /* We expect 2 fds. */ + /* We expect 1 fd. */ if (!fds) - return 2; + return 1; peer_start_closingd_after_shutdown(sd->channel, msg, fds); break; case WIRE_CHANNELD_FAIL_FALLEN_BEHIND: @@ -603,7 +603,6 @@ void peer_start_channeld(struct channel *channel, channel_errmsg, channel_set_billboard, take(&peer_fd->fd), - take(&peer_fd->gossip_fd), take(&hsmfd), NULL)); if (!channel->owner) { diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 489e1acc2a65..0590e154c057 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -341,8 +341,7 @@ static unsigned closing_msg(struct subd *sd, const u8 *msg, const int *fds UNUSE return 0; } -void peer_start_closingd(struct channel *channel, - struct peer_fd *peer_fd) +void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd) { u8 *initmsg; u32 min_feerate, feerate, *max_feerate; @@ -372,7 +371,6 @@ void peer_start_closingd(struct channel *channel, channel_errmsg, channel_set_billboard, take(&peer_fd->fd), - take(&peer_fd->gossip_fd), take(&hsmfd), NULL)); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 292114810244..f3da8ff8042f 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -438,9 +438,9 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd break; case WIRE_CONNECTD_PEER_CONNECTED: - if (tal_count(fds) != 2) - return 2; - peer_connected(connectd->ld, msg, fds[0], fds[1]); + if (tal_count(fds) != 1) + return 1; + peer_connected(connectd->ld, msg, fds[0]); break; case WIRE_CONNECTD_CONNECT_FAILED: diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index b65f6a6c5966..9b3a05ad8cf9 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -2994,16 +2994,16 @@ static unsigned int dual_opend_msg(struct subd *dualopend, handle_dry_run_finished(dualopend, msg); return 0; case WIRE_DUALOPEND_CHANNEL_LOCKED: - if (tal_count(fds) != 2) - return 2; + if (tal_count(fds) != 1) + return 1; handle_channel_locked(dualopend, fds, msg); return 0; case WIRE_DUALOPEND_GOT_SHUTDOWN: handle_peer_wants_to_close(dualopend, msg); return 0; case WIRE_DUALOPEND_SHUTDOWN_COMPLETE: - if (tal_count(fds) != 2) - return 2; + if (tal_count(fds) != 1) + return 1; handle_channel_closed(dualopend, fds, msg); return 0; case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND: @@ -3230,7 +3230,6 @@ static void start_fresh_dualopend(struct peer *peer, channel_errmsg, channel_set_billboard, take(&peer_fd->fd), - take(&peer_fd->gossip_fd), take(&hsmfd), NULL); if (!channel->owner) { @@ -3297,7 +3296,6 @@ void peer_restart_dualopend(struct peer *peer, channel_errmsg, channel_set_billboard, take(&peer_fd->fd), - take(&peer_fd->gossip_fd), take(&hsmfd), NULL)); if (!channel->owner) { log_broken(channel->log, "Could not subdaemon channel: %s", diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 7843267051bd..7afc8b4b4996 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -195,9 +195,8 @@ void handle_reestablish(struct lightningd *ld, take(towire_connectd_peer_final_msg(NULL, peer_id, err))); subd_send_fd(ld->connectd, peer_fd->fd); - subd_send_fd(ld->connectd, peer_fd->gossip_fd); - /* Don't close those fds! */ - peer_fd->fd = peer_fd->gossip_fd = -1; + /* Don't close this fd! */ + peer_fd->fd = -1; } } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index ad88b934aa2a..4c91a43f2751 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -838,8 +838,8 @@ static unsigned int openingd_msg(struct subd *openingd, tal_free(openingd); return 0; } - if (tal_count(fds) != 2) - return 2; + if (tal_count(fds) != 1) + return 1; opening_funder_finished(openingd, msg, fds, uc->fc); return 0; case WIRE_OPENINGD_FUNDER_START_REPLY: @@ -862,8 +862,8 @@ static unsigned int openingd_msg(struct subd *openingd, return 0; case WIRE_OPENINGD_FUNDEE: - if (tal_count(fds) != 2) - return 2; + if (tal_count(fds) != 1) + return 1; opening_fundee_finished(openingd, msg, fds, uc); return 0; @@ -872,8 +872,8 @@ static unsigned int openingd_msg(struct subd *openingd, return 0; case WIRE_OPENINGD_GOT_REESTABLISH: - if (tal_count(fds) != 2) - return 2; + if (tal_count(fds) != 1) + return 1; opening_got_reestablish(openingd, msg, fds, uc); return 0; @@ -919,7 +919,6 @@ void peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) opend_channel_errmsg, opend_channel_set_billboard, take(&peer_fd->fd), - take(&peer_fd->gossip_fd), take(&hsmfd), NULL); if (!uc->open_daemon) { uncommitted_channel_disconnect(uc, LOG_BROKEN, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 40cd63ae0429..6e40ebb44f89 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1054,9 +1054,8 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa take(towire_connectd_peer_final_msg(NULL, &peer->id, error))); subd_send_fd(ld->connectd, payload->peer_fd->fd); - subd_send_fd(ld->connectd, payload->peer_fd->gossip_fd); - /* Don't close those fds! */ - payload->peer_fd->fd = payload->peer_fd->gossip_fd = -1; + /* Don't close the fd! */ + payload->peer_fd->fd = -1; } static bool @@ -1112,8 +1111,7 @@ REGISTER_PLUGIN_HOOK(peer_connected, /* Connectd tells us a peer has connected: it never hands us duplicates, since * it holds them until we say peer_died. */ -void peer_connected(struct lightningd *ld, const u8 *msg, - int peer_fd, int gossip_fd) +void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) { struct node_id id; u8 *their_features; @@ -1130,7 +1128,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", tal_hex(msg, msg)); - hook_payload->peer_fd = new_peer_fd(hook_payload, peer_fd, gossip_fd); + hook_payload->peer_fd = new_peer_fd(hook_payload, peer_fd); /* If we're already dealing with this peer, hand off to correct * subdaemon. Otherwise, we'll hand to openingd to wait there. */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index e16e80ba054b..fc06b3d0c222 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -64,8 +64,7 @@ struct peer *peer_from_json(struct lightningd *ld, const char *buffer, const jsmntok_t *peeridtok); -void peer_connected(struct lightningd *ld, const u8 *msg, - int peer_fd, int gossip_fd); +void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd); /* Could be configurable. */ #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL diff --git a/lightningd/peer_fd.c b/lightningd/peer_fd.c index 5508dc720f92..9a63f55be2df 100644 --- a/lightningd/peer_fd.c +++ b/lightningd/peer_fd.c @@ -7,23 +7,20 @@ static void destroy_peer_fd(struct peer_fd *peer_fd) { if (peer_fd->fd != -1) close(peer_fd->fd); - if (peer_fd->gossip_fd != -1) - close(peer_fd->gossip_fd); } -struct peer_fd *new_peer_fd(const tal_t *ctx, int peer_fdnum, int gossip_fd) +struct peer_fd *new_peer_fd(const tal_t *ctx, int peer_fdnum) { struct peer_fd *peer_fd = tal(ctx, struct peer_fd); peer_fd->fd = peer_fdnum; - peer_fd->gossip_fd = gossip_fd; tal_add_destructor(peer_fd, destroy_peer_fd); return peer_fd; } -struct peer_fd *new_peer_fd_arr(const tal_t *ctx, const int *fds) +struct peer_fd *new_peer_fd_arr(const tal_t *ctx, const int *fd) { - /* We expect 2 fds. */ - assert(tal_count(fds) == 2); - return new_peer_fd(ctx, fds[0], fds[1]); + /* We expect 1 fd. */ + assert(tal_count(fd) == 1); + return new_peer_fd(ctx, fd[0]); } diff --git a/lightningd/peer_fd.h b/lightningd/peer_fd.h index 705deb47684a..fc9ea027bda3 100644 --- a/lightningd/peer_fd.h +++ b/lightningd/peer_fd.h @@ -3,19 +3,16 @@ #include "config.h" #include -/* This name is a little preemptive: it still contains the gossip_fd - * for now! */ +/* Tal wrapper for fd connecting subd to connectd */ struct peer_fd { /* If not -1, closed on freeing */ int fd; - int gossip_fd; }; -/* Allocate a new per-peer state and add destructor to close fds if set; - * sets fds to -1. */ -struct peer_fd *new_peer_fd(const tal_t *ctx, int peer_fd, int gossip_fd); +/* Allocate a new per-peer state and add destructor to close fd if set. */ +struct peer_fd *new_peer_fd(const tal_t *ctx, int peer_fd); -/* Array version of above: tal_count(fds) must be 2 */ -struct peer_fd *new_peer_fd_arr(const tal_t *ctx, const int *fds); +/* Array version of above: tal_count(fds) must be 1 */ +struct peer_fd *new_peer_fd_arr(const tal_t *ctx, const int *fd); #endif /* LIGHTNING_LIGHTNINGD_PEER_FD_H */ diff --git a/lightningd/subd.c b/lightningd/subd.c index 60bb5318beac..f563a32f1f79 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -414,7 +414,7 @@ static bool log_status_fail(struct subd *sd, const u8 *msg) return true; } -static bool handle_peer_error(struct subd *sd, const u8 *msg, int fds[2]) +static bool handle_peer_error(struct subd *sd, const u8 *msg, int fds[1]) { void *channel = sd->channel; struct channel_id channel_id; @@ -533,11 +533,11 @@ static struct io_plan *sd_msg_read(struct io_conn *conn, struct subd *sd) if (sd->channel) { switch ((enum peer_status_wire)type) { case WIRE_STATUS_PEER_ERROR: - /* We expect 2 fds after this */ + /* We expect 1 fd after this */ if (!sd->fds_in) { /* Don't free msg_in: we go around again. */ tal_steal(sd, sd->msg_in); - plan = sd_collect_fds(conn, sd, 2); + plan = sd_collect_fds(conn, sd, 1); goto out; } if (!handle_peer_error(sd, sd->msg_in, sd->fds_in)) diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index ac79fffe3f7a..8c1e42e022d0 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -168,7 +168,7 @@ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, struct log_book *new_log_book(struct lightningd *ld UNNEEDED, size_t max_mem UNNEEDED) { fprintf(stderr, "new_log_book called!\n"); abort(); } /* Generated stub for new_peer_fd_arr */ -struct peer_fd *new_peer_fd_arr(const tal_t *ctx UNNEEDED, const int *fds UNNEEDED) +struct peer_fd *new_peer_fd_arr(const tal_t *ctx UNNEEDED, const int *fd UNNEEDED) { fprintf(stderr, "new_peer_fd_arr called!\n"); abort(); } /* Generated stub for new_topology */ struct chain_topology *new_topology(struct lightningd *ld UNNEEDED, struct log *log UNNEEDED) diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 2868e952c84c..8d787bbdca2c 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -437,7 +437,7 @@ struct height_states *new_height_states(const tal_t *ctx UNNEEDED, const u32 *blockheight UNNEEDED) { fprintf(stderr, "new_height_states called!\n"); abort(); } /* Generated stub for new_peer_fd */ -struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED) +struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED) { fprintf(stderr, "new_peer_fd called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, diff --git a/lightningd/test/run-shuffle_fds.c b/lightningd/test/run-shuffle_fds.c index 3e1d5cd0b2ab..f26907cb66f8 100644 --- a/lightningd/test/run-shuffle_fds.c +++ b/lightningd/test/run-shuffle_fds.c @@ -109,7 +109,7 @@ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "new_log called!\n"); abort(); } /* Generated stub for new_peer_fd_arr */ -struct peer_fd *new_peer_fd_arr(const tal_t *ctx UNNEEDED, const int *fds UNNEEDED) +struct peer_fd *new_peer_fd_arr(const tal_t *ctx UNNEEDED, const int *fd UNNEEDED) { fprintf(stderr, "new_peer_fd_arr called!\n"); abort(); } /* Generated stub for subdaemon_path */ const char *subdaemon_path(const tal_t *ctx UNNEEDED, const struct lightningd *ld UNNEEDED, const char *name UNNEEDED) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index bd870c95a119..cc49e20ac86f 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -44,9 +44,9 @@ #include #include -/* stdin == lightningd, 3 == peer, 4 == gossipd, 5 = hsmd */ +/* stdin == lightningd, 3 == peer, 4 = hsmd */ #define REQ_FD STDIN_FILENO -#define HSM_FD 5 +#define HSM_FD 4 /* tx_add_input, tx_add_output, tx_rm_input, tx_rm_output */ #define NUM_TX_MSGS (TX_RM_OUTPUT + 1) @@ -1164,7 +1164,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) * form, but we use it in a very limited way. */ for (;;) { u8 *msg; - bool from_gossipd; char *err; bool warning; struct channel_id actual; @@ -1175,20 +1174,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) clean_tmpctx(); /* This helper routine polls both the peer and gossipd. */ - msg = peer_or_gossip_sync_read(ctx, state->pps, &from_gossipd); - - /* Use standard helper for gossip msgs (forwards, if it's an - * error, exits). */ - if (from_gossipd) { - handle_gossip_msg(state->pps, take(msg)); - continue; - } - - /* Some messages go straight to gossipd. */ - if (is_msg_for_gossipd(msg)) { - wire_sync_write(state->pps->gossip_fd, take(msg)); - continue; - } + msg = peer_read(ctx, state->pps); /* BOLT #1: * @@ -3440,19 +3426,6 @@ static u8 *handle_funding_depth(struct state *state, u8 *msg) return NULL; } -/*~ If we see the gossip_fd readable, we read a whole message. Sure, we might - * block, but we trust gossipd. */ -static void handle_gossip_in(struct state *state) -{ - u8 *msg = wire_sync_read(NULL, state->pps->gossip_fd); - - if (!msg) - status_failed(STATUS_FAIL_GOSSIP_IO, - "Reading gossip: %s", strerror(errno)); - - handle_gossip_msg(state->pps, take(msg)); -} - /* BOLT #2: * * A receiving node: @@ -3553,9 +3526,9 @@ static void do_reconnect_dance(struct state *state) do { clean_tmpctx(); msg = peer_read(tmpctx, state->pps); - } while (handle_peer_gossip_or_error(state->pps, - &state->channel_id, - msg)); + } while (handle_peer_error(state->pps, + &state->channel_id, + msg)); if (!fromwire_channel_reestablish (msg, &cid, @@ -3750,9 +3723,8 @@ static u8 *handle_peer_in(struct state *state) break; } - /* Handles standard cases, and legal unknown ones. */ - if (handle_peer_gossip_or_error(state->pps, - &state->channel_id, msg)) + /* Handles errors. */ + if (handle_peer_error(state->pps, &state->channel_id, msg)) return NULL; peer_write(state->pps, @@ -3775,7 +3747,7 @@ int main(int argc, char *argv[]) { common_setup(argv[0]); - struct pollfd pollfd[3]; + struct pollfd pollfd[2]; struct state *state = tal(NULL, struct state); struct secret *none; struct fee_states *fee_states; @@ -3901,9 +3873,9 @@ int main(int argc, char *argv[]) - /* 3 == peer, 4 == gossipd, 5 = hsmd */ + /* 3 == peer, 4 = hsmd */ state->pps = new_per_peer_state(state); - per_peer_state_set_fds(state->pps, 3, 4); + per_peer_state_set_fd(state->pps, 3); /*~ We need an initial per-commitment point whether we're funding or * they are, and lightningd has reserved a unique dbid for us already, @@ -3927,10 +3899,8 @@ int main(int argc, char *argv[]) /*~ We manually run a little poll() loop here. With only three fds */ pollfd[0].fd = REQ_FD; pollfd[0].events = POLLIN; - pollfd[1].fd = state->pps->gossip_fd; + pollfd[1].fd = state->pps->peer_fd; pollfd[1].events = POLLIN; - pollfd[2].fd = state->pps->peer_fd; - pollfd[2].events = POLLIN; /* Do reconnect, if need be */ if (state->channel) { @@ -3946,7 +3916,7 @@ int main(int argc, char *argv[]) /*~ If we get a signal which aborts the poll() call, valgrind * complains about revents being uninitialized. I'm not sure * that's correct, but it's easy to be sure. */ - pollfd[0].revents = pollfd[1].revents = pollfd[2].revents = 0; + pollfd[0].revents = pollfd[1].revents = 0; poll(pollfd, ARRAY_SIZE(pollfd), -1); /* Subtle: handle_master_in can do its own poll loop, so @@ -3955,11 +3925,8 @@ int main(int argc, char *argv[]) if (pollfd[0].revents & POLLIN) msg = handle_master_in(state); /* Second priority: messages from peer. */ - else if (pollfd[2].revents & POLLIN) - msg = handle_peer_in(state); - /* Last priority: chit-chat from gossipd. */ else if (pollfd[1].revents & POLLIN) - handle_gossip_in(state); + msg = handle_peer_in(state); /* If we've shutdown, we're done */ if (shutdown_complete(state)) diff --git a/openingd/openingd.c b/openingd/openingd.c index 854c5cd088fc..282213c88322 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -35,9 +35,9 @@ #include #include -/* stdin == lightningd, 3 == peer, 4 == gossipd, 5 = hsmd */ +/* stdin == lightningd, 3 == peer, 4 = hsmd */ #define REQ_FD STDIN_FILENO -#define HSM_FD 5 +#define HSM_FD 4 #if DEVELOPER /* If --dev-force-tmp-channel-id is set, it ends up here */ @@ -184,7 +184,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, * form, but we use it in a very limited way. */ for (;;) { u8 *msg; - bool from_gossipd; char *err; bool warning; struct channel_id actual; @@ -194,20 +193,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, clean_tmpctx(); /* This helper routine polls both the peer and gossipd. */ - msg = peer_or_gossip_sync_read(ctx, state->pps, &from_gossipd); - - /* Use standard helper for gossip msgs (forwards, if it's an - * error, exits). */ - if (from_gossipd) { - handle_gossip_msg(state->pps, take(msg)); - continue; - } - - /* Some messages go straight to gossipd. */ - if (is_msg_for_gossipd(msg)) { - wire_sync_write(state->pps->gossip_fd, take(msg)); - continue; - } + msg = peer_read(ctx, state->pps); /* BOLT #1: * @@ -1259,9 +1245,8 @@ static u8 *handle_peer_in(struct state *state) if (t == WIRE_OPEN_CHANNEL) return fundee_channel(state, msg); - /* Handles standard cases, and legal unknown ones. */ - if (handle_peer_gossip_or_error(state->pps, - &state->channel_id, msg)) + /* Handles error cases. */ + if (handle_peer_error(state->pps, &state->channel_id, msg)) return NULL; extracted = extract_channel_id(msg, &channel_id); @@ -1285,19 +1270,6 @@ static u8 *handle_peer_in(struct state *state) peer_failed_connection_lost(); } -/*~ If we see the gossip_fd readable, we read a whole message. Sure, we might - * block, but we trust gossipd. */ -static void handle_gossip_in(struct state *state) -{ - u8 *msg = wire_sync_read(NULL, state->pps->gossip_fd); - - if (!msg) - status_failed(STATUS_FAIL_GOSSIP_IO, - "Reading gossip: %s", strerror(errno)); - - handle_gossip_msg(state->pps, take(msg)); -} - /* 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 @@ -1393,7 +1365,7 @@ int main(int argc, char *argv[]) setup_locale(); u8 *msg; - struct pollfd pollfd[3]; + struct pollfd pollfd[2]; struct state *state = tal(NULL, struct state); struct secret *none; struct channel_id *force_tmp_channel_id; @@ -1424,9 +1396,9 @@ int main(int argc, char *argv[]) dev_force_tmp_channel_id = force_tmp_channel_id; #endif - /* 3 == peer, 4 == gossipd, 5 = hsmd */ + /* 3 == peer, 4 = hsmd */ state->pps = new_per_peer_state(state); - per_peer_state_set_fds(state->pps, 3, 4); + per_peer_state_set_fd(state->pps, 3); /*~ Initially we're not associated with a channel, but * handle_peer_gossip_or_error compares this. */ @@ -1463,10 +1435,8 @@ int main(int argc, char *argv[]) /*~ We manually run a little poll() loop here. With only three fds */ pollfd[0].fd = REQ_FD; pollfd[0].events = POLLIN; - pollfd[1].fd = state->pps->gossip_fd; + pollfd[1].fd = state->pps->peer_fd; pollfd[1].events = POLLIN; - pollfd[2].fd = state->pps->peer_fd; - pollfd[2].events = POLLIN; /* We exit when we get a conclusion to write to lightningd: either * opening_funder_reply or opening_fundee. */ @@ -1475,7 +1445,7 @@ int main(int argc, char *argv[]) /*~ If we get a signal which aborts the poll() call, valgrind * complains about revents being uninitialized. I'm not sure * that's correct, but it's easy to be sure. */ - pollfd[0].revents = pollfd[1].revents = pollfd[2].revents = 0; + pollfd[0].revents = pollfd[1].revents = 0; poll(pollfd, ARRAY_SIZE(pollfd), -1); /* Subtle: handle_master_in can do its own poll loop, so @@ -1484,22 +1454,19 @@ int main(int argc, char *argv[]) if (pollfd[0].revents & POLLIN) msg = handle_master_in(state); /* Second priority: messages from peer. */ - else if (pollfd[2].revents & POLLIN) - msg = handle_peer_in(state); - /* Last priority: chit-chat from gossipd. */ else if (pollfd[1].revents & POLLIN) - handle_gossip_in(state); + msg = handle_peer_in(state); /* Since we're the top-level event loop, we clean up */ clean_tmpctx(); } - /*~ Write message and hand back the peer fd and gossipd fd. This also - * means that if the peer or gossipd wrote us any messages we didn't - * read yet, it will simply be read by the next daemon. */ + /*~ Write message and hand back the peer fd. This also means that if + * the peer wrote us any messages we didn't read yet, it will simply + * be read by the next daemon. */ wire_sync_write(REQ_FD, msg); per_peer_state_fdpass_send(REQ_FD, state->pps); - status_debug("Sent %s with fds", + status_debug("Sent %s with fd", openingd_wire_name(fromwire_peektype(msg))); /* This frees the entire tal tree. */ diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 7778c252ce66..eac5f34d5a49 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -442,7 +442,7 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, { fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } /* Generated stub for new_peer_fd */ -struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED, int gossip_fd UNNEEDED) +struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED) { fprintf(stderr, "new_peer_fd called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) From 3121cebf4c29a975bd59defe530133187ddde3ed Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0284/1530] gossipd: don't hand out fds. Gossipd now simply gets told by channeld when peers arrive or leave. (it only needs to know for the seeker). Signed-off-by: Rusty Russell --- connectd/connectd.c | 70 ++----------- connectd/connectd.h | 3 - connectd/connectd_gossipd_wire.csv | 6 +- gossipd/gossipd.c | 159 ++++++++--------------------- gossipd/gossipd.h | 3 - 5 files changed, 54 insertions(+), 187 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index b70f9bafc3dc..f3cb14afbeec 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -210,57 +210,6 @@ static void peer_connected_in(struct daemon *daemon, tal_free(connect); } -/*~ Every per-peer daemon needs a connection to the gossip daemon; this allows - * it to forward gossip to/from the peer. The gossip daemon needs to know a - * few of the features of the peer and its id (for reporting). - * - * Every peer also has read-only access to the gossip_store, which is handed - * out by gossipd too, and also a "gossip_state" indicating where we're up to. - * - * 'features' is a field in the `init` message, indicating properties of the - * node. - */ -static int get_gossipfd(struct daemon *daemon, - const struct node_id *id, - const u8 *their_features) -{ - bool gossip_queries_feature, success; - u8 *msg; - - /*~ The way features generally work is that both sides need to offer it; - * we always offer `gossip_queries`, but this check is explicit. */ - gossip_queries_feature - = feature_negotiated(daemon->our_features, their_features, - OPT_GOSSIP_QUERIES); - - /*~ We do this communication sync, since gossipd is our friend and - * it's easier. If gossipd fails, we fail. */ - msg = towire_gossipd_new_peer(NULL, id, gossip_queries_feature); - if (!wire_sync_write(GOSSIPCTL_FD, take(msg))) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed writing to gossipctl: %s", - strerror(errno)); - - msg = wire_sync_read(tmpctx, GOSSIPCTL_FD); - if (!fromwire_gossipd_new_peer_reply(msg, &success)) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed parsing msg gossipctl: %s", - tal_hex(tmpctx, msg)); - - /* Gossipd might run out of file descriptors, so it tells us, and we - * give up on connecting this peer. */ - if (!success) { - status_broken("Gossipd did not give us an fd: losing peer %s", - type_to_string(tmpctx, struct node_id, id)); - return -1; - } - - /* Otherwise, the next thing in the socket will be the file descriptor - * for the per-peer daemon. */ - return fdpass_recv(GOSSIPCTL_FD); - -} - /*~ This is an ad-hoc marshalling structure where we store arguments so we * can call peer_connected again. */ struct peer_reconnected { @@ -336,8 +285,6 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, /*~ When we free a peer, we remove it from the daemon's hashtable */ static void destroy_peer(struct peer *peer, struct daemon *daemon) { - if (peer->gossip_fd >= 0) - close(peer->gossip_fd); peer_htable_del(&daemon->peers, peer); } @@ -398,6 +345,7 @@ struct io_plan *peer_connected(struct io_conn *conn, int unsup; size_t depender, missing; int subd_fd; + bool option_gossip_queries; peer = peer_htable_get(&daemon->peers, id); if (peer) @@ -455,12 +403,12 @@ struct io_plan *peer_connected(struct io_conn *conn, if (!peer) return io_close(conn); - /* If gossipd can't give us a file descriptor, we give up connecting. */ - peer->gossip_fd = get_gossipfd(daemon, id, their_features); - if (peer->gossip_fd < 0) { - close(subd_fd); - return tal_free(peer); - } + /* Tell gossipd it can ask query this new peer for gossip */ + option_gossip_queries = feature_negotiated(daemon->our_features, + their_features, + OPT_GOSSIP_QUERIES); + msg = towire_gossipd_new_peer(NULL, id, option_gossip_queries); + daemon_conn_send(daemon->gossipd, take(msg)); /* Get ready for streaming gossip from the store */ setup_peer_gossip_store(peer, daemon->our_features, their_features); @@ -1846,6 +1794,10 @@ void peer_conn_closed(struct peer *peer) assert(!peer->to_peer); assert(peer->told_to_close); + /* Tell gossipd to stop asking this peer gossip queries */ + daemon_conn_send(peer->daemon->gossipd, + take(towire_gossipd_peer_gone(NULL, &peer->id))); + /* Wake up in case there's a reconnecting peer waiting in io_wait. */ io_wake(peer); diff --git a/connectd/connectd.h b/connectd/connectd.h index a630ff64968e..7b2eaa98da3d 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -81,9 +81,6 @@ struct peer { /* Random ping timer, to detect dead connections. */ struct oneshot *ping_timer; - /* FIXME: remove! */ - int gossip_fd; - #if DEVELOPER bool dev_read_enabled; /* If non-NULL, this counts down; 0 means disable */ diff --git a/connectd/connectd_gossipd_wire.csv b/connectd/connectd_gossipd_wire.csv index bdb9a75f5292..195a73421bb8 100644 --- a/connectd/connectd_gossipd_wire.csv +++ b/connectd/connectd_gossipd_wire.csv @@ -8,9 +8,9 @@ msgdata,gossipd_new_peer,id,node_id, # Did we negotiate OPT_GOSSIP_QUERIES? msgdata,gossipd_new_peer,gossip_queries_feature,bool, -# if success: + gossip fd -msgtype,gossipd_new_peer_reply,4100 -msgdata,gossipd_new_peer_reply,success,bool, +# peer is done +msgtype,gossipd_peer_gone,4101 +msgdata,gossipd_peer_gone,id,node_id, # connectd tells gossipd a gossip msg it received for peer. msgtype,gossipd_recv_gossip,4002 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index d7797c068e0b..5fef825a36f7 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -89,12 +89,6 @@ static void destroy_peer(struct peer *peer) node = get_node(peer->daemon->rstate, &peer->id); if (node) peer_disable_channels(peer->daemon, node); - - /* This is tricky: our lifetime is tied to the daemon_conn; it's our - * parent, so we are freed if it is, but we need to free it if we're - * freed manually. tal_free() treats this as a noop if it's already - * being freed */ - tal_free(peer->dc); } /* Search for a peer. */ @@ -347,103 +341,26 @@ static void handle_local_private_channel(struct daemon *daemon, const u8 *msg) } } -/*~ This is where the per-peer daemons send us messages. It's either forwarded - * gossip, or a request for information. We deliberately use non-overlapping - * message types so we can distinguish them. */ -static struct io_plan *peer_msg_in(struct io_conn *conn, - const u8 *msg, - struct peer *peer) -{ - /* These are messages relayed from peer */ - switch ((enum peer_wire)fromwire_peektype(msg)) { - /* These are not sent by peer (connectd sends us gossip msgs) */ - case WIRE_CHANNEL_ANNOUNCEMENT: - case WIRE_CHANNEL_UPDATE: - case WIRE_NODE_ANNOUNCEMENT: - case WIRE_QUERY_CHANNEL_RANGE: - case WIRE_REPLY_CHANNEL_RANGE: - case WIRE_QUERY_SHORT_CHANNEL_IDS: - case WIRE_REPLY_SHORT_CHANNEL_IDS_END: - case WIRE_WARNING: - case WIRE_INIT: - case WIRE_ERROR: - case WIRE_PING: - case WIRE_PONG: - case WIRE_OPEN_CHANNEL: - case WIRE_ACCEPT_CHANNEL: - case WIRE_FUNDING_CREATED: - case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: - case WIRE_SHUTDOWN: - case WIRE_CLOSING_SIGNED: - case WIRE_UPDATE_ADD_HTLC: - case WIRE_UPDATE_FULFILL_HTLC: - case WIRE_UPDATE_FAIL_HTLC: - case WIRE_UPDATE_FAIL_MALFORMED_HTLC: - case WIRE_COMMITMENT_SIGNED: - case WIRE_REVOKE_AND_ACK: - case WIRE_UPDATE_FEE: - case WIRE_UPDATE_BLOCKHEIGHT: - case WIRE_CHANNEL_REESTABLISH: - case WIRE_ANNOUNCEMENT_SIGNATURES: - case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_TX_ADD_INPUT: - case WIRE_TX_REMOVE_INPUT: - case WIRE_TX_ADD_OUTPUT: - case WIRE_TX_REMOVE_OUTPUT: - case WIRE_TX_COMPLETE: - case WIRE_TX_SIGNATURES: - case WIRE_OPEN_CHANNEL2: - case WIRE_ACCEPT_CHANNEL2: - case WIRE_INIT_RBF: - case WIRE_ACK_RBF: - case WIRE_OBS2_ONION_MESSAGE: - case WIRE_ONION_MESSAGE: -#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))); - return io_close(conn); - } - - /* Anything else should not have been sent to us: close on it */ - status_peer_broken(&peer->id, "unexpected cmd of type %i", - fromwire_peektype(msg)); - return io_close(conn); -} - -/*~ This is where connectd tells us about a new peer, and we hand back an fd for - * it to send us messages via peer_msg_in above */ -static struct io_plan *connectd_new_peer(struct io_conn *conn, - struct daemon *daemon, - const u8 *msg) +/*~ This is where connectd tells us about a new peer we might want to + * gossip with. */ +static void connectd_new_peer(struct daemon *daemon, const u8 *msg) { - struct peer *peer = tal(conn, struct peer); + struct peer *peer = tal(daemon, struct peer); struct node *node; - int fds[2]; if (!fromwire_gossipd_new_peer(msg, &peer->id, &peer->gossip_queries_feature)) { - status_broken("Bad new_peer msg from connectd: %s", + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Bad new_peer msg from connectd: %s", tal_hex(tmpctx, msg)); - return io_close(conn); } - /* This can happen: we handle it gracefully, returning a `failed` msg. */ - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { - status_broken("Failed to create socketpair: %s", - strerror(errno)); - daemon_conn_send(daemon->connectd, - take(towire_gossipd_new_peer_reply(NULL, - false))); - goto done; + if (find_peer(daemon, &peer->id)) { + status_broken("Peer %s already here?", + type_to_string(tmpctx, struct node_id, &peer->id)); + tal_free(find_peer(daemon, &peer->id)); } - /* We might not have noticed old peer is dead; kill it now. */ - tal_free(find_peer(daemon, &peer->id)); - /* Populate the rest of the peer info. */ peer->daemon = daemon; peer->gossip_counter = 0; @@ -459,27 +376,30 @@ static struct io_plan *connectd_new_peer(struct io_conn *conn, list_add_tail(&peer->daemon->peers, &peer->list); tal_add_destructor(peer, destroy_peer); - /* This is the new connection. */ - peer->dc = daemon_conn_new(daemon, fds[0], - peer_msg_in, - NULL, peer); - /* Free peer if conn closed (destroy_peer closes conn if peer freed) */ - tal_steal(peer->dc, peer); - node = get_node(daemon->rstate, &peer->id); if (node) peer_enable_channels(daemon, node); /* This sends the initial timestamp filter. */ seeker_setup_peer_gossip(daemon->seeker, peer); +} + +static void connectd_peer_gone(struct daemon *daemon, const u8 *msg) +{ + struct node_id id; + struct peer *peer; - /* Reply with success, and the new fd and gossip_state. */ - daemon_conn_send(daemon->connectd, - take(towire_gossipd_new_peer_reply(NULL, true))); - daemon_conn_send_fd(daemon->connectd, fds[1]); + if (!fromwire_gossipd_peer_gone(msg, &id)) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Bad peer_gone msg from connectd: %s", + tal_hex(tmpctx, msg)); + } -done: - return daemon_conn_read_next(conn, daemon->connectd); + peer = find_peer(daemon, &id); + if (!peer) + status_broken("Peer %s already gone?", + type_to_string(tmpctx, struct node_id, &id)); + tal_free(peer); } /*~ lightningd asks us if we know any addresses for a given id. */ @@ -520,12 +440,11 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) tal_hex(tmpctx, outermsg)); } - /* FIXME: happens when peer closes! */ peer = find_peer(daemon, &id); if (!peer) { - status_debug("connectd sent gossip msg %s for unknown peer %s", - peer_wire_name(fromwire_peektype(msg)), - type_to_string(tmpctx, struct node_id, &id)); + status_broken("connectd sent gossip msg %s for unknown peer %s", + peer_wire_name(fromwire_peektype(msg)), + type_to_string(tmpctx, struct node_id, &id)); return; } @@ -613,20 +532,17 @@ static struct io_plan *connectd_req(struct io_conn *conn, enum connectd_gossipd_wire t = fromwire_peektype(msg); switch (t) { - case WIRE_GOSSIPD_NEW_PEER: - return connectd_new_peer(conn, daemon, msg); - /* This is not for this fd! */ case WIRE_GOSSIPD_RECV_GOSSIP: + case WIRE_GOSSIPD_NEW_PEER: + case WIRE_GOSSIPD_PEER_GONE: /* We send these, don't receive them. */ - case WIRE_GOSSIPD_NEW_PEER_REPLY: case WIRE_GOSSIPD_SEND_GOSSIP: break; } - status_broken("Bad msg from connectd: %s", - tal_hex(tmpctx, msg)); - return io_close(conn); + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Bad msg from connectd: %s", tal_hex(tmpctx, msg)); } /*~ connectd's input handler is very simple. */ @@ -641,10 +557,15 @@ static struct io_plan *connectd_gossip_req(struct io_conn *conn, handle_recv_gossip(daemon, msg); goto handled; - /* This is not for this fd! */ case WIRE_GOSSIPD_NEW_PEER: + connectd_new_peer(daemon, msg); + goto handled; + + case WIRE_GOSSIPD_PEER_GONE: + connectd_peer_gone(daemon, msg); + goto handled; + /* We send these, don't receive them. */ - case WIRE_GOSSIPD_NEW_PEER_REPLY: case WIRE_GOSSIPD_SEND_GOSSIP: break; } diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 75b39b8c67a1..9a73c8974305 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -109,9 +109,6 @@ struct peer { void (*query_channel_range_cb)(struct peer *peer, u32 first_blocknum, u32 number_of_blocks, const struct range_query_reply *replies); - - /* The daemon_conn used to queue messages to/from the peer. */ - struct daemon_conn *dc; }; /* Search for a peer. */ From ca08f27d5460f3ad2a47566aaab6b399dbac0d05 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0285/1530] connectd: remove second gossip fd. Now we only send and receive gossip messages on this fd. Signed-off-by: Rusty Russell --- connectd/connectd.c | 5 ++--- gossipd/gossipd.c | 30 +++------------------------ gossipd/gossipd.h | 1 - lightningd/connect_control.c | 12 +++-------- lightningd/connect_control.h | 2 +- lightningd/gossip_control.c | 7 ++----- lightningd/gossip_control.h | 2 +- lightningd/lightningd.c | 6 +++--- lightningd/test/run-find_my_abspath.c | 4 ++-- 9 files changed, 17 insertions(+), 52 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index f3cb14afbeec..70950924ed34 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -53,7 +53,6 @@ * thus may know how to reach certain peers. */ #define HSM_FD 3 #define GOSSIPCTL_FD 4 -#define GOSSIPCTL2_FD 5 /*~ In C convention, constants are UPPERCASE macros. Not everything needs to * be a constant, but it soothes the programmer's conscience to encapsulate @@ -1528,7 +1527,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) announcable))); #if DEVELOPER if (dev_disconnect) - dev_disconnect_init(6); + dev_disconnect_init(5); #endif } @@ -2027,7 +2026,7 @@ int main(int argc, char *argv[]) status_setup_async(daemon->master); /* This streams gossip to and from gossipd */ - daemon->gossipd = daemon_conn_new(daemon, GOSSIPCTL2_FD, + daemon->gossipd = daemon_conn_new(daemon, GOSSIPCTL_FD, recv_gossip, NULL, daemon); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 5fef825a36f7..6113dc8cae87 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -114,7 +114,7 @@ void peer_supplied_good_gossip(struct peer *peer, size_t amount) void queue_peer_msg(struct peer *peer, const u8 *msg TAKES) { u8 *outermsg = towire_gossipd_send_gossip(NULL, &peer->id, msg); - daemon_conn_send(peer->daemon->connectd2, take(outermsg)); + daemon_conn_send(peer->daemon->connectd, take(outermsg)); if (taken(msg)) tal_free(msg); @@ -531,27 +531,6 @@ static struct io_plan *connectd_req(struct io_conn *conn, { enum connectd_gossipd_wire t = fromwire_peektype(msg); - switch (t) { - /* This is not for this fd! */ - case WIRE_GOSSIPD_RECV_GOSSIP: - case WIRE_GOSSIPD_NEW_PEER: - case WIRE_GOSSIPD_PEER_GONE: - /* We send these, don't receive them. */ - case WIRE_GOSSIPD_SEND_GOSSIP: - break; - } - - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Bad msg from connectd: %s", tal_hex(tmpctx, msg)); -} - -/*~ connectd's input handler is very simple. */ -static struct io_plan *connectd_gossip_req(struct io_conn *conn, - const u8 *msg, - struct daemon *daemon) -{ - enum connectd_gossipd_wire t = fromwire_peektype(msg); - switch (t) { case WIRE_GOSSIPD_RECV_GOSSIP: handle_recv_gossip(daemon, msg); @@ -574,7 +553,7 @@ static struct io_plan *connectd_gossip_req(struct io_conn *conn, "Bad msg from connectd2: %s", tal_hex(tmpctx, msg)); handled: - return daemon_conn_read_next(conn, daemon->connectd2); + return daemon_conn_read_next(conn, daemon->connectd); } /* BOLT #7: @@ -732,14 +711,11 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) /* Fire up the seeker! */ daemon->seeker = new_seeker(daemon); - /* connectd is already started, and uses this fd to ask us things. */ + /* connectd is already started, and uses this fd to feed/recv gossip. */ daemon->connectd = daemon_conn_new(daemon, CONNECTD_FD, connectd_req, maybe_send_query_responses, daemon); - daemon->connectd2 = daemon_conn_new(daemon, CONNECTD2_FD, - connectd_gossip_req, NULL, daemon); - /* OK, we are ready. */ daemon_conn_send(daemon->master, take(towire_gossipd_init_reply(NULL))); diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 9a73c8974305..a84d5765eb71 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -33,7 +33,6 @@ struct daemon { /* Connection to connect daemon. */ struct daemon_conn *connectd; - struct daemon_conn *connectd2; /* Routing information */ struct routing_state *rstate; diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index f3da8ff8042f..57b0db804189 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -475,9 +475,9 @@ static void connect_init_done(struct subd *connectd, io_break(connectd); } -int connectd_init(struct lightningd *ld, int *gossipd_fd2) +int connectd_init(struct lightningd *ld) { - int fds[2], fds2[2]; + int fds[2]; u8 *msg; int hsmfd; struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr; @@ -490,16 +490,11 @@ int connectd_init(struct lightningd *ld, int *gossipd_fd2) if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) fatal("Could not socketpair for connectd<->gossipd"); - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds2) != 0) - fatal("Could not socketpair for connectd<->gossipd 2"); - hsmfd = hsm_get_global_fd(ld, HSM_CAP_ECDH); ld->connectd = new_global_subd(ld, "lightning_connectd", connectd_wire_name, connectd_msg, - take(&hsmfd), - take(&fds[1]), - take(&fds2[1]), + take(&hsmfd), take(&fds[1]), #if DEVELOPER /* Not take(): we share it */ ld->dev_disconnect_fd >= 0 ? @@ -540,7 +535,6 @@ int connectd_init(struct lightningd *ld, int *gossipd_fd2) /* Wait for init_reply */ io_loop(NULL, NULL); - *gossipd_fd2 = fds2[0]; return fds[0]; } diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 681c5c7a1ed5..67beb003782b 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -7,7 +7,7 @@ struct pubkey; struct wireaddr_internal; /* Returns fd for gossipd to talk to connectd */ -int connectd_init(struct lightningd *ld, int *gossipd_fd2); +int connectd_init(struct lightningd *ld); void connectd_activate(struct lightningd *ld); void try_reconnect(struct channel *channel, u32 seconds_delay, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 7429d4db3a89..e16607345b24 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -234,7 +234,7 @@ static void gossipd_init_done(struct subd *gossipd, /* Create the `gossipd` subdaemon and send the initialization * message */ -void gossip_init(struct lightningd *ld, int connectd_fd, int connectd_fd2) +void gossip_init(struct lightningd *ld, int connectd_fd) { u8 *msg; int hsmfd; @@ -243,10 +243,7 @@ void gossip_init(struct lightningd *ld, int connectd_fd, int connectd_fd2) ld->gossip = new_global_subd(ld, "lightning_gossipd", gossipd_wire_name, gossip_msg, - take(&hsmfd), - take(&connectd_fd), - take(&connectd_fd2), - NULL); + take(&hsmfd), take(&connectd_fd), NULL); if (!ld->gossip) err(1, "Could not subdaemon gossip"); diff --git a/lightningd/gossip_control.h b/lightningd/gossip_control.h index 869f53930aaf..c1d587902158 100644 --- a/lightningd/gossip_control.h +++ b/lightningd/gossip_control.h @@ -8,7 +8,7 @@ struct channel; struct lightningd; -void gossip_init(struct lightningd *ld, int connectd_fd, int connectd_fd2); +void gossip_init(struct lightningd *ld, int connectd_fd); void gossipd_notify_spend(struct lightningd *ld, const struct short_channel_id *scid); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index b8ec5851fc69..cedc20e77d8d 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -850,7 +850,7 @@ int main(int argc, char *argv[]) { struct lightningd *ld; u32 min_blockheight, max_blockheight; - int connectd_gossipd_fd, connectd_gossipd_fd2; + int connectd_gossipd_fd; int stop_fd; struct timers *timers; const char *stop_response; @@ -1022,7 +1022,7 @@ int main(int argc, char *argv[]) * which knows (via node_announcement messages) the public * addresses of nodes, so connectd_init hands it one end of a * socket pair, and gives us the other */ - connectd_gossipd_fd = connectd_init(ld, &connectd_gossipd_fd2); + connectd_gossipd_fd = connectd_init(ld); /*~ We do every database operation within a transaction; usually this * is covered by the infrastructure (eg. opening a transaction before @@ -1074,7 +1074,7 @@ int main(int argc, char *argv[]) * channel_announcement, channel_update, node_announcement and gossip * queries. It also hands us the latest channel_updates for our * channels. */ - gossip_init(ld, connectd_gossipd_fd, connectd_gossipd_fd2); + gossip_init(ld, connectd_gossipd_fd); /*~ Create RPC socket: now lightning-cli can send us JSON RPC commands * over a UNIX domain socket specified by `ld->rpc_filename`. */ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 8c1e42e022d0..dbe411903ba9 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -23,7 +23,7 @@ void channel_notify_new_block(struct lightningd *ld UNNEEDED, void connectd_activate(struct lightningd *ld UNNEEDED) { fprintf(stderr, "connectd_activate called!\n"); abort(); } /* Generated stub for connectd_init */ -int connectd_init(struct lightningd *ld UNNEEDED, int *gossipd_fd2 UNNEEDED) +int connectd_init(struct lightningd *ld UNNEEDED) { fprintf(stderr, "connectd_init called!\n"); abort(); } /* Generated stub for daemon_poll */ int daemon_poll(struct pollfd *fds UNNEEDED, nfds_t nfds UNNEEDED, int timeout UNNEEDED) @@ -92,7 +92,7 @@ bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDE bool fromwire_status_version(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, wirestring **version UNNEEDED) { fprintf(stderr, "fromwire_status_version called!\n"); abort(); } /* Generated stub for gossip_init */ -void gossip_init(struct lightningd *ld UNNEEDED, int connectd_fd UNNEEDED, int connectd_fd2 UNNEEDED) +void gossip_init(struct lightningd *ld UNNEEDED, int connectd_fd UNNEEDED) { fprintf(stderr, "gossip_init called!\n"); abort(); } /* Generated stub for gossip_notify_new_block */ void gossip_notify_new_block(struct lightningd *ld UNNEEDED, u32 blockheight UNNEEDED) From 727b486d49ca4b6bfeee23a78ef25f1da0c9e243 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0286/1530] connectd: don't received useless peer fd if we're told to send final msg. We don't need the connection to ourselves, just to free it. Signed-off-by: Rusty Russell --- connectd/connectd.c | 11 ----------- connectd/connectd_wire.csv | 2 +- lightningd/opening_common.c | 4 +--- lightningd/peer_control.c | 4 +--- lightningd/test/run-invoice-select-inchan.c | 3 --- wallet/test/run-wallet.c | 3 --- 6 files changed, 3 insertions(+), 24 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 70950924ed34..d161e695feb4 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1846,21 +1846,10 @@ static void peer_final_msg(struct io_conn *conn, struct peer *peer; struct node_id id; u8 *finalmsg; - int peer_fd; if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &finalmsg)) master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); - /* Get the peer_fd for this peer: we don't need it though! */ - io_fd_block(io_conn_fd(conn), true); - peer_fd = fdpass_recv(io_conn_fd(conn)); - if (peer_fd == -1) - status_failed(STATUS_FAIL_MASTER_IO, - "Getting peer fd after peer_final_msg: %s", - strerror(errno)); - close(peer_fd); - io_fd_block(io_conn_fd(conn), false); - /* This can happen if peer hung up on us. */ peer = peer_htable_get(&daemon->peers, &id); if (peer) { diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index c735f3c93173..7632a2840e43 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -72,7 +72,7 @@ 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. Plus fd for peer +# master -> connectd: give message to peer and disconnect. msgtype,connectd_peer_final_msg,2003 msgdata,connectd_peer_final_msg,id,node_id, msgdata,connectd_peer_final_msg,len,u16, diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 7afc8b4b4996..46087c13819e 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -194,9 +194,7 @@ void handle_reestablish(struct lightningd *ld, subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, peer_id, err))); - subd_send_fd(ld->connectd, peer_fd->fd); - /* Don't close this fd! */ - peer_fd->fd = -1; + tal_free(peer_fd); } } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 6e40ebb44f89..ebcfcf4edef7 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1053,9 +1053,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, error))); - subd_send_fd(ld->connectd, payload->peer_fd->fd); - /* Don't close the fd! */ - payload->peer_fd->fd = -1; + tal_free(payload->peer_fd); } static bool diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 8d787bbdca2c..d55ec8384eb7 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -603,9 +603,6 @@ 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(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index eac5f34d5a49..a6605b25af3f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -642,9 +642,6 @@ 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(); } From b47930f395039b7561bf0e3c633ff01164f37c99 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:05 +1030 Subject: [PATCH 0287/1530] gossip_store: handle compacted gossip_store correctly. Don't send EOF marker to peer, e.g. in tests/test_gossip.py::test_gossip_store_compact: ``` lightningd-2: 2022-01-24T03:34:22.925Z DEBUG connectd: gossip_store at end, new fd moved to 1875 lightningd-2: 2022-01-24T03:34:22.933Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-connectd: Sending gossip INVALID 4105 lightningd-2: 2022-01-24T03:34:22.933Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-channeld-chan#2: peer_in WIRE_WARNING lightningd-2: 2022-01-24T03:34:22.941Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-connectd: peer_out INVALID 4105 lightningd-2: 2022-01-24T03:34:22.949Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-channeld-chan#2: billboard perm: Received warning channel 2c7cf1dc9dada7ed14f10c78ade8f0de907c1b70e736c12ff6f7472dc69c3db3: Peer sent unknown message 4105 (INVALID 4105) ``` Signed-off-by: Rusty Russell --- common/gossip_store.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/gossip_store.c b/common/gossip_store.c index e730d4220c59..686b1f1773a7 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -99,6 +99,7 @@ u8 *gossip_store_next(const tal_t *ctx, /* end can go backwards in this case! */ if (type == WIRE_GOSSIP_STORE_ENDED) { *off = *end = reopen_gossip_store(gossip_store_fd, msg); + msg = tal_free(msg); /* Ignore gossipd internal messages. */ } else if (type != WIRE_CHANNEL_ANNOUNCEMENT && type != WIRE_CHANNEL_UPDATE From f7f9f35f2aad4d4e139ddd4fca1e8bce1d31a527 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:06 +1030 Subject: [PATCH 0288/1530] pytest: remove flake in test_upgrade_statickey_onchaind We were relying on the fee update to create an additional tx. That's ugly; do an actual payment and make sure we definitely complete a new tx by waiting for that *then* both revoke_and_ack. (Without this, we could get a unilateral close instead of a penalty). Signed-off-by: Rusty Russell --- tests/test_connection.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 055551557173..18f15559224e 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3522,8 +3522,21 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_log('option_static_remotekey enabled at 1/1') + # Make sure another commitment happens, sending failed payment. + routestep = { + 'msatoshi': 1, + 'id': l2.info['id'], + 'delay': 5, + 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments + } + l1.rpc.sendpay([routestep], '00' * 32, payment_secret='00' * 32) + with pytest.raises(RpcError, match=r'WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): + l1.rpc.waitsendpay('00' * 32) + # Make sure l2 gets REVOKE_AND_ACK from previous. + l2.daemon.wait_for_log('peer_in WIRE_UPDATE_ADD_HTLC') l2.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') + l2.daemon.wait_for_log('peer_in WIRE_REVOKE_AND_ACK') # Pre-statickey penalty works. bitcoind.rpc.sendrawtransaction(tx) From 6d9f6ffd6777e7124504adc2420bb98df7a46148 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 29 Jan 2022 14:03:06 +1030 Subject: [PATCH 0289/1530] pytest: make test_gossip_no_empty_announcements more robust. Don't assume gossip send order: explicitly disconnect and reconnect. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 9224375954b6..6a17319c0997 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -596,32 +596,47 @@ def test_routing_gossip_reconnect(node_factory): wait_for(lambda: len(n.rpc.listchannels()['channels']) == 4) -@pytest.mark.developer("needs DEVELOPER=1") -def test_gossip_no_empty_announcements(node_factory, bitcoind): +@pytest.mark.developer("needs fast gossip, dev-no-reconnect") +def test_gossip_no_empty_announcements(node_factory, bitcoind, chainparams): # Need full IO logging so we can see gossip - # l3 sends CHANNEL_ANNOUNCEMENT to l2, but not CHANNEL_UDPATE. - l1, l2, l3, l4 = node_factory.line_graph(4, opts=[{'log-level': 'io'}, - {'log-level': 'io'}, - # Writes to l4 first, then l2 - {'disconnect': ['+WIRE_CHANNEL_ANNOUNCEMENT*2'], + # l2 sends CHANNEL_ANNOUNCEMENT to l1, but not CHANNEL_UDPATE. + l1, l2, l3, l4 = node_factory.line_graph(4, opts=[{'log-level': 'io', + 'dev-no-reconnect': None}, + {'log-level': 'io', + 'disconnect': ['+WIRE_CHANNEL_ANNOUNCEMENT'], 'may_reconnect': True}, + {'may_reconnect': True}, {'may_reconnect': True}], fundchannel=False) - # Make an announced-but-not-updated channel. l3.fundchannel(l4, 10**5) bitcoind.generate_block(5) - # 0x0100 = channel_announcement, which goes to l2 before l3 dies. - l2.daemon.wait_for_log(r'\[IN\] 0100') + # l2 sends CHANNEL_ANNOUNCEMENT to l1, then disconnects/ + l2.daemon.wait_for_log('dev_disconnect') + l1.daemon.wait_for_log(r'\[IN\] 0100') - # But it never goes to l1, as there's no channel_update. + # l1 won't relay it (make sure it has time to digest though) time.sleep(2) - assert not l1.daemon.is_in_log(r'\[IN\] 0100') - assert len(l1.rpc.listchannels()['channels']) == 0 + assert l1.rpc.listchannels()['channels'] == [] + encoded = subprocess.run(['devtools/mkencoded', '--scids', '00'], + check=True, + timeout=TIMEOUT, + stdout=subprocess.PIPE).stdout.strip().decode() + assert l1.query_gossip('query_channel_range', + chainparams['chain_hash'], + 0, 1000000, + filters=['0109', '0012']) == ['0108' + # blockhash + + chainparams['chain_hash'] + # first_blocknum, number_of_blocks, complete + + format(0, '08x') + format(1000000, '08x') + '01' + # encoded_short_ids + + format(len(encoded) // 2, '04x') + + encoded] # If we reconnect, gossip will now flow. - l3.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 2) From e8d2176e6bb89c9825da24e68bfb43d0a6a408bd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 30 Jan 2022 14:07:23 +1030 Subject: [PATCH 0290/1530] pytest: protect against bad gossip messages from mining confirms too fast. If we fund a channel between two nodes, then mine all the blocks to announce it, any other nodes may see the announcement before the blocks, causing CI to complain about "bad gossip": ``` lightningd-4: 2022-01-25T22:33:25.468Z DEBUG 032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e-gossipd: Ignoring future channel_announcment for 113x1x1 (current block 112) lightningd-4: 2022-01-25T22:33:25.468Z DEBUG 032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e-gossipd: Bad gossip order: WIRE_CHANNEL_UPDATE before announcement 113x1x1/0 lightningd-4: 2022-01-25T22:33:25.468Z DEBUG 032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e-gossipd: Bad gossip order: WIRE_CHANNEL_UPDATE before announcement 113x1x1/1 lightningd-4: 2022-01-25T22:33:25.468Z DEBUG 032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e-gossipd: Bad gossip order: WIRE_NODE_ANNOUNCEMENT before announcement 032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e ``` Add a new helper for this case, and use it where there are more than 2 nodes. Cleans up test_routing_gossip and a few other places which did this manually. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 11 ++++ tests/test_closing.py | 11 ++-- tests/test_connection.py | 12 ++-- tests/test_gossip.py | 65 +++++++++------------- tests/test_invoices.py | 6 +- tests/test_misc.py | 4 +- tests/test_pay.py | 39 ++++++------- tests/test_plugin.py | 9 ++- tests/utils.py | 2 +- 9 files changed, 77 insertions(+), 82 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index a726e58439e0..f5117e81c6a2 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -118,6 +118,17 @@ def sync_blockheight(bitcoind, nodes): wait_for(lambda: n.rpc.getinfo()['blockheight'] == height) +def mine_funding_to_announce(bitcoind, nodes, num_blocks=5, wait_for_mempool=0): + """Mine blocks so a channel can be announced (5, if it's already +mined), but make sure we don't leave nodes behind who will reject the +announcement. Not needed if there are only two nodes. + + """ + bitcoind.generate_block(num_blocks - 1, wait_for_mempool) + sync_blockheight(bitcoind, nodes) + bitcoind.generate_block(1) + + def wait_channel_quiescent(n1, n2): wait_for(lambda: only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['htlcs'] == []) wait_for(lambda: only_one(only_one(n2.rpc.listpeers(n1.info['id'])['peers'])['channels'])['htlcs'] == []) diff --git a/tests/test_closing.py b/tests/test_closing.py index 0d0e1f7e2041..481ac860ad07 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -8,7 +8,7 @@ account_balance, first_channel_id, closing_fee, TEST_NETWORK, scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES, check_utxos_channel, anchor_expected, check_coin_moves, - check_balance_snaps + check_balance_snaps, mine_funding_to_announce ) import os @@ -281,7 +281,7 @@ def test_closing_different_fees(node_factory, bitcoind, executor): # Technically, this is async to fundchannel returning. l1.daemon.wait_for_log('sendrawtx exit 0') - bitcoind.generate_block(6) + mine_funding_to_announce(bitcoind, peers, num_blocks=6) # Now wait for them all to hit normal state, do payments l1.daemon.wait_for_logs(['update for channel .* now ACTIVE'] * num_peers @@ -349,7 +349,8 @@ def test_closing_specified_destination(node_factory, bitcoind, chainparams): l1.pay(l3, 100000000) l1.pay(l4, 100000000) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) + addr = chainparams['example_addr'] l1.rpc.close(chan12, None, addr) l1.rpc.call('close', {'id': chan13, 'destination': addr}) @@ -2160,7 +2161,7 @@ def test_onchain_middleman_simple(node_factory, bitcoind): channel_id = first_channel_id(l1, l2) # Make sure routes finalized. - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) l1.wait_channel_active(c23) # Give l1 some money to play with. @@ -2280,7 +2281,7 @@ def test_onchain_middleman_their_unilateral_in(node_factory, bitcoind): channel_id = first_channel_id(l1, l2) # Make sure routes finalized. - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) l1.wait_channel_active(c23) # Make sure l3 sees gossip for channel now; it can get upset diff --git a/tests/test_connection.py b/tests/test_connection.py index 18f15559224e..701507c7d39b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -11,7 +11,7 @@ expected_channel_features, check_coin_moves, first_channel_id, account_balance, basic_fee, scriptpubkey_addr, - EXPERIMENTAL_FEATURES + EXPERIMENTAL_FEATURES, mine_funding_to_announce ) from pyln.testing.utils import SLOW_MACHINE, VALGRIND, EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT @@ -2556,7 +2556,7 @@ def test_disconnectpeer(node_factory, bitcoind): # Fund channel l1 -> l3 l1.fundchannel(l3, 10**6) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # disconnecting a non gossiping peer results in error with pytest.raises(RpcError, match=r'Peer is in state CHANNELD_NORMAL'): @@ -2945,11 +2945,9 @@ def test_restart_many_payments(node_factory, bitcoind): # OK to use change from previous fundings l1.rpc.fundchannel(n.info['id'], 10**6, minconf=0) - # Now mine them, get scids; make sure they all see the first block - # otherwise they may complain about channel_announcement from the future. - bitcoind.generate_block(1, wait_for_mempool=num * 2) - sync_blockheight(bitcoind, [l1] + nodes) - bitcoind.generate_block(5) + # Now mine them, get scids + mine_funding_to_announce(bitcoind, [l1] + nodes, + num_blocks=6, wait_for_mempool=num * 2) wait_for(lambda: [only_one(n.rpc.listpeers()['peers'])['channels'][0]['state'] for n in nodes] == ['CHANNELD_NORMAL'] * len(nodes)) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 6a17319c0997..142bb9dd1809 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -5,7 +5,8 @@ from pyln.client import RpcError, Millisatoshi from utils import ( DEVELOPER, wait_for, TIMEOUT, only_one, sync_blockheight, - expected_node_features, COMPAT, EXPERIMENTAL_FEATURES + expected_node_features, COMPAT, EXPERIMENTAL_FEATURES, + mine_funding_to_announce ) import json @@ -37,7 +38,7 @@ def test_gossip_pruning(node_factory, bitcoind): scid1, _ = l1.fundchannel(l2, 10**6) scid2, _ = l2.fundchannel(l3, 10**6) - bitcoind.generate_block(6) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Channels should be activated locally wait_for(lambda: [c['active'] for c in l1.rpc.listchannels()['channels']] == [True] * 4) @@ -201,7 +202,7 @@ def test_announce_and_connect_via_dns(node_factory, bitcoind): l3.rpc.connect(l2.info['id'], 'localhost', l2.port) l4.rpc.connect(l2.info['id'], 'localhost', l2.port) scid, _ = l1.fundchannel(l2, 10**6) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # wait until l3 and l4 see l1 via gossip with announced addresses wait_for(lambda: len(l3.rpc.listnodes(l1.info['id'])['nodes']) == 1) @@ -252,7 +253,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): # Make a public channel. chan12, _ = l1.fundchannel(l2, 10**5) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) l3.wait_for_channel_updates([chan12]) after_12 = int(time.time()) @@ -334,7 +335,7 @@ def test_connect_by_gossip(node_factory, bitcoind): # Nodes are gossiped only if they have channels chanid, _ = l2.fundchannel(l3, 10**6) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Let channel reach announcement depth l2.wait_channel_active(chanid) @@ -442,7 +443,7 @@ def test_gossip_jsonrpc(node_factory): @pytest.mark.developer("Too slow without --dev-fast-gossip") -def test_gossip_badsig(node_factory): +def test_gossip_badsig(node_factory, bitcoind): """Make sure node announcement signatures are ok. This is a smoke test to see if signatures fail. This used to be the case @@ -460,7 +461,7 @@ def test_gossip_badsig(node_factory): l2.fundchannel(l3, 10**6) # Wait for route propagation. - l1.bitcoin.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) l1.daemon.wait_for_log('Received node_announcement for node {}' .format(l3.info['id'])) assert not l1.daemon.is_in_log('signature verification failed') @@ -517,7 +518,7 @@ def test_gossip_persistence(node_factory, bitcoind): scid23, _ = l2.fundchannel(l3, 10**6) # Make channels public, except for l3 -> l4, which is kept local-only for now - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) scid34, _ = l3.fundchannel(l4, 10**6) bitcoind.generate_block(1) @@ -610,7 +611,7 @@ def test_gossip_no_empty_announcements(node_factory, bitcoind, chainparams): fundchannel=False) l3.fundchannel(l4, 10**5) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) # l2 sends CHANNEL_ANNOUNCEMENT to l1, then disconnects/ l2.daemon.wait_for_log('dev_disconnect') @@ -644,20 +645,16 @@ def test_gossip_no_empty_announcements(node_factory, bitcoind, chainparams): def test_routing_gossip(node_factory, bitcoind): nodes = node_factory.get_nodes(5) - sync_blockheight(bitcoind, nodes) for i in range(len(nodes) - 1): src, dst = nodes[i], nodes[i + 1] src.rpc.connect(dst.info['id'], 'localhost', dst.port) src.openchannel(dst, 25000, confirm=False, wait_for_announce=False) - sync_blockheight(bitcoind, nodes) - # Avoid "bad gossip" caused by future announcements (a node below - # confirmation height receiving and ignoring the announcement, - # thus marking followup messages as bad). - sync_blockheight(bitcoind, nodes) + # openchannel calls fundwallet which mines a block; so first channel + # is 4 deep, last is unconfirmed. # Allow announce messages. - bitcoind.generate_block(6) + mine_funding_to_announce(bitcoind, nodes, num_blocks=6, wait_for_mempool=1) # Deep check that all channels are in there comb = [] @@ -691,17 +688,15 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): l2.fundwallet(10**6) num_tx = len(bitcoind.rpc.getrawmempool()) + # We want these one block apart. l1.rpc.fundchannel(l2.info['id'], 10**5)['tx'] - wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == num_tx + 1) - bitcoind.generate_block(1) - - num_tx = len(bitcoind.rpc.getrawmempool()) + bitcoind.generate_block(wait_for_mempool=num_tx + 1) + sync_blockheight(bitcoind, [l1, l2, l3, l4]) l2.rpc.fundchannel(l3.info['id'], 10**5)['tx'] - wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == num_tx + 1) - bitcoind.generate_block(1) - # Get them both to gossip depth. - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4], + num_blocks=6, + wait_for_mempool=1) # Make sure l2 has received all the gossip. l2.daemon.wait_for_logs(['Received node_announcement for node ' + l1.info['id'], @@ -874,7 +869,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): # This should actually be large enough for zlib to kick in! scid34, _ = l3.fundchannel(l4, 10**5) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) l2.daemon.wait_for_log('Received node_announcement for node ' + l4.info['id']) # Restore infinite encode size. @@ -924,12 +919,6 @@ def test_report_routing_failure(node_factory, bitcoind): # Finally the only possible path is # l1-l2-l3-l4. - def fund_from_to_payer(lsrc, ldst, lpayer): - lsrc.rpc.connect(ldst.info['id'], 'localhost', ldst.port) - c, _ = lsrc.fundchannel(ldst, 10000000) - bitcoind.generate_block(5) - lpayer.wait_for_channel_updates([c]) - # Setup # Construct lightningd l1, l2, l3, l4 = node_factory.get_nodes(4) @@ -946,7 +935,7 @@ def fund_from_to_payer(lsrc, ldst, lpayer): dst.daemon.lightning_dir)) c, _ = src.fundchannel(dst, 10**6) channels.append(c) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) for c in channels: l1.wait_channel_active(c) @@ -982,7 +971,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): # Make channels public. scid12, _ = l1.fundchannel(l2, 10**5) scid23, _ = l2.fundchannel(l3, 10**5) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # It will know about everything. l1.daemon.wait_for_log('Received node_announcement for node {}'.format(l3.info['id'])) @@ -1369,7 +1358,7 @@ def test_gossip_notices_close(node_factory, bitcoind): node_factory.join_nodes([l2, l3]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Make sure l1 learns about channel and nodes. wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 2) @@ -1468,7 +1457,7 @@ def test_getroute_exclude(node_factory, bitcoind): # Now, create an alternate (better) route. l2.rpc.connect(l4.info['id'], 'localhost', l4.port) scid, _ = l2.fundchannel(l4, 1000000, wait_for_active=False) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5]) # We don't wait above, because we care about it hitting l1. l1.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE' @@ -1505,7 +1494,7 @@ def test_getroute_exclude(node_factory, bitcoind): scid15, _ = l1.fundchannel(l5, 1000000, wait_for_active=False) l5.rpc.connect(l4.info['id'], 'localhost', l4.port) scid54, _ = l5.fundchannel(l4, 1000000, wait_for_active=False) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5]) # We don't wait above, because we care about it hitting l1. l1.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE' @@ -1589,7 +1578,7 @@ def setup_gossip_store_test(node_factory, bitcoind): scid23, _ = l2.fundchannel(l3, 10**6) # Have that channel announced. - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Make sure we've got node_announcements wait_for(lambda: ['alias' in n for n in l2.rpc.listnodes()['nodes']] == [True, True]) @@ -2115,7 +2104,7 @@ def test_topology_leak(node_factory, bitcoind): l1, l2, l3 = node_factory.line_graph(3) l1.rpc.listchannels() - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Wait until l1 sees all the channels. wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 4) diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 386f5a99e533..d31fade615b0 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -1,7 +1,7 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from pyln.client import RpcError, Millisatoshi -from utils import only_one, wait_for, wait_channel_quiescent +from utils import only_one, wait_for, wait_channel_quiescent, mine_funding_to_announce import pytest @@ -221,7 +221,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): l0 = node_factory.get_node() l0.rpc.connect(l1.info['id'], 'localhost', l1.port) scid_dummy, _ = l0.fundchannel(l1, 2 * (10**5)) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l0, l1, l2, l3]) # Make sure channel is totally public. wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid_dummy)['channels']] == [True, True]) @@ -285,7 +285,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # the exposure of private channels. l3.rpc.connect(l2.info['id'], 'localhost', l2.port) scid2, _ = l3.fundchannel(l2, (10**5)) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l0, l1, l2, l3]) # Make sure channel is totally public. wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid2)['channels']] == [True, True]) diff --git a/tests/test_misc.py b/tests/test_misc.py index e5827b719241..f65b88fd2f64 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -7,7 +7,7 @@ from threading import Event from pyln.testing.utils import ( DEVELOPER, TIMEOUT, VALGRIND, DEPRECATED_APIS, sync_blockheight, only_one, - wait_for, TailableProc, env + wait_for, TailableProc, env, mine_funding_to_announce ) from utils import ( account_balance, scriptpubkey_addr, check_coin_moves @@ -2291,7 +2291,7 @@ def test_listforwards(node_factory, bitcoind): c24, _ = l2.fundchannel(l4, 10**5) # Wait until channels are active - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) l1.wait_channel_active(c23) # successful payments diff --git a/tests/test_pay.py b/tests/test_pay.py index a5a68b4ae954..50eb721f857a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -8,7 +8,7 @@ from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT from utils import ( DEVELOPER, wait_for, only_one, sync_blockheight, TIMEOUT, - EXPERIMENTAL_FEATURES, env, VALGRIND + EXPERIMENTAL_FEATURES, env, VALGRIND, mine_funding_to_announce ) import copy import os @@ -182,7 +182,7 @@ def test_pay_exclude_node(node_factory, bitcoind): scid14, _ = l1.fundchannel(l4, 10**6, wait_for_active=False) scid45, _ = l4.fundchannel(l5, 10**6, wait_for_active=False) scid53, _ = l5.fundchannel(l3, 10**6, wait_for_active=False) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5]) l1.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE' .format(scid14), @@ -1137,7 +1137,7 @@ def test_forward_different_fees_and_cltv(node_factory, bitcoind): c1, _ = l1.fundchannel(l2, 10**6) c2, _ = l2.fundchannel(l3, 10**6) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Make sure l1 has seen announce for all channels. l1.wait_channel_active(c1) @@ -1244,7 +1244,7 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): c1, _ = l1.fundchannel(l2, 10**6) c2, _ = l2.fundchannel(l3, 10**6) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Make sure l1 has seen announce for all channels. l1.wait_channel_active(c1) @@ -1290,9 +1290,7 @@ def test_forward_stats(node_factory, bitcoind): l2.openchannel(l4, 10**6, wait_for_announce=False) l2.openchannel(l5, 10**6, wait_for_announce=False) - bitcoind.generate_block(1) - sync_blockheight(bitcoind, [l1, l2, l3, l4, l5]) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5]) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8) @@ -1419,7 +1417,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): l6.fundchannel(l1, 10**6) # Make sure routes finalized. - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5, l6]) l1.wait_channel_active(c23) l1.wait_channel_active(c24) l1.wait_channel_active(c25) @@ -1702,7 +1700,7 @@ def exhaust_channel(opener, peer, scid, already_spent=0): scid35, _ = l3.fundchannel(l5, 10**6, wait_for_active=False) # Make sure l1 sees all 7 channels - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5]) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 14) # Exhaust shortcut channels one at a time, to force retries. @@ -1763,7 +1761,7 @@ def test_pay_routeboost(node_factory, bitcoind, compat): scidl2l3, _ = l2.fundchannel(l3, 10**6) # Make sure l1 knows about the 2->3 channel. - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5]) l1.daemon.wait_for_logs([r'update for channel {}/0 now ACTIVE' .format(scidl2l3), r'update for channel {}/1 now ACTIVE' @@ -2030,8 +2028,7 @@ def test_setchannelfee_state(node_factory, bitcoind): # cid = result['channels'][0]['channel_id'] # test routing correct new fees once routing is established - bitcoind.generate_block(6) - sync_blockheight(bitcoind, [l0, l1, l2]) + mine_funding_to_announce(bitcoind, [l0, l1, l2]) l0.wait_for_route(l2) inv = l2.rpc.invoice(100000, 'test_setchannelfee_state', 'desc')['bolt11'] @@ -2595,7 +2592,7 @@ def test_tlv_or_legacy(node_factory, bitcoind): l3.daemon.wait_for_log("Got onion.*'type': 'tlv'") # We need 5 more blocks to announce l1->l2 channel. - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Make sure l1 knows about l2 wait_for(lambda: 'alias' in l1.rpc.listnodes(l2.info['id'])['nodes'][0]) @@ -2828,7 +2825,7 @@ def test_partial_payment(node_factory, bitcoind, executor): scid24, _ = l2.fundchannel(l4, 100000) l3.rpc.connect(l4.info['id'], 'localhost', l4.port) scid34, _ = l3.fundchannel(l4, 100000) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) # Wait until l1 knows about all channels. wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8) @@ -3633,7 +3630,7 @@ def test_mpp_adaptive(node_factory, bitcoind): assert(c12['spendable_msat'].millisatoshis < amt) assert(c34['spendable_msat'].millisatoshis < amt) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8) inv = l4.rpc.invoice( @@ -3728,8 +3725,7 @@ def test_mpp_presplit_routehint_conflict(node_factory, bitcoind): l2.rpc.connect(l3.info['id'], 'localhost', l3.port) l2.fundchannel(l3, 10**7, announce_channel=False) - bitcoind.generate_block(6) - sync_blockheight(bitcoind, [l1, l2, l3]) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Wait for l3 to learn about l1->l2, otherwise it will think # l2 is a deadend and not add it to the routehint. @@ -3894,8 +3890,7 @@ def test_mpp_waitblockheight_routehint_conflict(node_factory, bitcoind, executor l2.rpc.connect(l3.info['id'], 'localhost', l3.port) l2.fundchannel(l3, 10**7, announce_channel=False) - bitcoind.generate_block(6) - sync_blockheight(bitcoind, [l1, l2, l3]) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) # Wait for l3 to learn about l1->l2, otherwise it will think # l2 is a deadend and not add it to the routehint. @@ -4017,8 +4012,7 @@ def test_mpp_interference_2(node_factory, bitcoind, executor): l3.fundchannel(l6, int((unit * 6).to_satoshi()), announce_channel=False) # Now wait for the buyers to learn the entire public network. - bitcoind.generate_block(5) - sync_blockheight(bitcoind, [l1, l2, l3, l4, l5, l6, l7]) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5, l6, l7]) for channel in public_network: wait_for(lambda: len(l2.rpc.listchannels(channel)['channels']) == 2) wait_for(lambda: len(l3.rpc.listchannels(channel)['channels']) == 2) @@ -4120,8 +4114,7 @@ def test_mpp_overload_payee(node_factory, bitcoind): l5.fundbalancedchannel(l6, amt)] # Ensure l1 knows the entire public network. - bitcoind.generate_block(5) - sync_blockheight(bitcoind, [l1, l2, l3, l4, l5, l6]) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4, l5, l6]) for c in public_network: wait_for(lambda: len(l1.rpc.listchannels(c)['channels']) >= 2) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 2889b820030f..cec3b8ec369a 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -9,7 +9,8 @@ DEVELOPER, only_one, sync_blockheight, TIMEOUT, wait_for, TEST_NETWORK, DEPRECATED_APIS, expected_peer_features, expected_node_features, expected_channel_features, account_balance, - check_coin_moves, first_channel_id, EXPERIMENTAL_DUAL_FUND + check_coin_moves, first_channel_id, EXPERIMENTAL_DUAL_FUND, + mine_funding_to_announce ) import ast @@ -1723,7 +1724,9 @@ def test_hook_crash(node_factory, executor, bitcoind): n.rpc.plugin_start(p) l1.openchannel(n, 10**6, confirm=False, wait_for_announce=False) - bitcoind.generate_block(6) + # Mine final openchannel tx first. + sync_blockheight(bitcoind, [l1] + nodes) + mine_funding_to_announce(bitcoind, [l1] + nodes, wait_for_mempool=1) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 2 * len(perm)) @@ -1918,7 +1921,7 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): {'may_reconnect': True, 'plugin': coin_plugin}, ], wait_for_announce=True) - bitcoind.generate_block(5) + mine_funding_to_announce(bitcoind, [l1, l2, l3]) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 4) amount = 10**8 diff --git a/tests/utils.py b/tests/utils.py index 1d417a2da77c..141a27b50289 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,5 +1,5 @@ from pyln.testing.utils import TEST_NETWORK, TIMEOUT, VALGRIND, DEVELOPER, DEPRECATED_APIS # noqa: F401 -from pyln.testing.utils import env, only_one, wait_for, write_config, TailableProc, sync_blockheight, wait_channel_quiescent, get_tx_p2wsh_outnum # noqa: F401 +from pyln.testing.utils import env, only_one, wait_for, write_config, TailableProc, sync_blockheight, wait_channel_quiescent, get_tx_p2wsh_outnum, mine_funding_to_announce # noqa: F401 import bitstring from pyln.client import Millisatoshi from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND From d370aac020d3a0c8ce851e324fb52edccfae3f07 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 30 Jan 2022 14:07:30 +1030 Subject: [PATCH 0291/1530] gossipd: fix longstanding logic error in gossip_generation. `hc` is never NULL, since it's `hc = &chan->half[direction];`; we really meant "is it initialized", and valgrind under CI finally caught it: ``` ==69243== Conditional jump or move depends on uninitialised value(s) ==69243== at 0x11C595: handle_local_channel_update (gossip_generation.c:758) ==69243== by 0x115254: recv_req (gossipd.c:986) ==69243== by 0x128F8D: handle_read (daemon_conn.c:31) ==69243== by 0x16BEE1: next_plan (io.c:59) ==69243== by 0x16CAE9: do_plan (io.c:407) ==69243== by 0x16CB2B: io_ready (io.c:417) ==69243== by 0x16EE1E: io_loop (poll.c:453) ==69243== by 0x1154DA: main (gossipd.c:1089) ==69243== ``` Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index faf01bb7254f..70b8f854b15f 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -751,7 +751,7 @@ void handle_local_channel_update(struct daemon *daemon, const u8 *msg) return; /* Too early? Defer (don't worry if it's unannounced). */ - if (hc && is_chan_public(chan)) { + if (is_halfchan_defined(hc) && is_chan_public(chan)) { u32 now = time_now().ts.tv_sec; u32 next_time = hc->bcast.timestamp + GOSSIP_MIN_INTERVAL(daemon->rstate->dev_fast_gossip); From 0c243347388a7f40fa33fa417ace8a8615c48da6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 30 Jan 2022 14:07:30 +1030 Subject: [PATCH 0292/1530] lightningd: clean up subds before freeing HTLCs. Otherwise we get weird effects, as htlcs are being freed: ``` 2022-01-26T05:07:37.8774610Z lightningd-1: 2022-01-26T04:47:48.770Z DEBUG 030eeb52087b9dbb27b7aec79ca5249369f6ce7b20a5684ce38d9f4595a21c2fda-chan#8: Failing HTLC 18446744073709551615 due to peer death 2022-01-26T05:07:37.8775287Z lightningd-1: 2022-01-26T04:47:48.770Z **BROKEN** 030eeb52087b9dbb27b7aec79ca5249369f6ce7b20a5684ce38d9f4595a21c2fda-chan#8: Neither origin nor in? ``` Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 5 +++++ lightningd/onchain_control.c | 7 ++++++- lightningd/opening_common.c | 12 ++++++++---- lightningd/subd.c | 14 ++++++++++++++ lightningd/subd.h | 9 +++++++++ 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index cedc20e77d8d..113abcdb6ecb 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -529,6 +529,11 @@ static void shutdown_subdaemons(struct lightningd *ld) ld->gossip = subd_shutdown(ld->gossip, 10); ld->hsm = subd_shutdown(ld->hsm, 10); + /*~ Closing the hsmd means all other subdaemons should be exiting; + * deal with that cleanly before we start freeing internal + * structures. */ + subd_shutdown_remaining(ld); + /* Now we free all the HTLCs */ free_htlcs(ld, NULL); diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index b1122a79ac18..17e32f204be9 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -570,10 +570,15 @@ static void onchain_error(struct channel *channel, bool warning UNUSED, const u8 *err_for_them UNUSED) { + channel_set_owner(channel, NULL); + + /* This happens on shutdown: fine */ + if (channel->peer->ld->state == LD_STATE_SHUTDOWN) + return; + /* FIXME: re-launch? */ log_broken(channel->log, "%s", desc); channel_set_billboard(channel, true, desc); - channel_set_owner(channel, NULL); } /* With a reorg, this can get called multiple times; each time we'll kill diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 46087c13819e..d8d39c20816d 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -105,7 +105,9 @@ void uncommitted_channel_disconnect(struct uncommitted_channel *uc, { u8 *msg = towire_connectd_peer_disconnected(tmpctx, &uc->peer->id); log_(uc->log, level, NULL, false, "%s", desc); - subd_send_msg(uc->peer->ld->connectd, msg); + /* NULL when we're shutting down */ + if (uc->peer->ld->connectd) + subd_send_msg(uc->peer->ld->connectd, msg); if (uc->fc && uc->fc->cmd) was_pending(command_fail(uc->fc->cmd, LIGHTNINGD, "%s", desc)); notify_disconnect(uc->peer->ld, &uc->peer->id); @@ -191,9 +193,11 @@ void handle_reestablish(struct lightningd *ld, "Unknown channel for reestablish"); log_debug(ld->log, "Reestablish on UNKNOWN channel %s", type_to_string(tmpctx, struct channel_id, channel_id)); - subd_send_msg(ld->connectd, - take(towire_connectd_peer_final_msg(NULL, peer_id, - err))); + /* Unless we're shutting down */ + if (ld->connectd) + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, peer_id, + err))); tal_free(peer_fd); } } diff --git a/lightningd/subd.c b/lightningd/subd.c index f563a32f1f79..633976191db4 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -895,6 +895,20 @@ struct subd *subd_shutdown(struct subd *sd, unsigned int seconds) return tal_free(sd); } +void subd_shutdown_remaining(struct lightningd *ld) +{ + struct subd *subd; + + /* We give them a second to finish exiting, before we kill + * them in destroy_subd() */ + sleep(1); + + while ((subd = list_top(&ld->subds, struct subd, list)) != NULL) { + /* Destructor removes from list */ + io_close(subd->conn); + } +} + void subd_release_channel(struct subd *owner, const void *channel) { /* If owner is a per-peer-daemon, and not already freeing itself... */ diff --git a/lightningd/subd.h b/lightningd/subd.h index 564b2f35ff86..d721b14a1440 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -222,6 +222,15 @@ void subd_release_channel(struct subd *owner, const void *channel); */ struct subd *subd_shutdown(struct subd *subd, unsigned int seconds); +/** + * subd_shutdown_remaining - kill all remaining (per-peer) subds + * @ld: lightningd + * + * They should already be exiting (since we shutdown hsmd), but + * make sure they have. + */ +void subd_shutdown_remaining(struct lightningd *ld); + /* Ugly helper to get full pathname of the current binary. */ const char *find_my_abspath(const tal_t *ctx, const char *argv0); From d9b1e6924376ef77733c9b3a1e4151cc5b193d52 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 30 Jan 2022 14:07:30 +1030 Subject: [PATCH 0293/1530] dual_funding: don't steal inflight field in update_channel_from_inflight. If we call update_channel_from_inflight *twice* with the same inflight, we will get bad results. Using tal_steal() here was a premature optimization: ``` Valgrind error file: valgrind-errors.496395 ==496395== Invalid read of size 8 ==496395== at 0x22A9D3: to_tal_hdr (tal.c:174) ==496395== by 0x22B4B5: tal_steal_ (tal.c:498) ==496395== by 0x16A13D: update_channel_from_inflight (peer_control.c:1225) ==496395== by 0x16A4C7: funding_depth_cb (peer_control.c:1299) ==496395== by 0x182807: txw_fire (watch.c:232) ==496395== by 0x182AA9: watch_topology_changed (watch.c:300) ==496395== by 0x1290ED: updates_complete (chaintopology.c:624) ==496395== by 0x129BF4: get_new_block (chaintopology.c:835) ==496395== by 0x125EEF: getrawblockbyheight_callback (bitcoind.c:362) ==496395== by 0x176ECC: plugin_response_handle (plugin.c:584) ==496395== by 0x1770F5: plugin_read_json_one (plugin.c:690) ==496395== by 0x1772D9: plugin_read_json (plugin.c:735) ==496395== Address 0x89fbb08 is 24 bytes inside a block of size 104 free'd ==496395== at 0x483CA3F: free (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==496395== by 0x22B193: del_tree (tal.c:421) ==496395== by 0x22B461: tal_free (tal.c:486) ==496395== by 0x16A123: update_channel_from_inflight (peer_control.c:1223) ==496395== by 0x16A4C7: funding_depth_cb (peer_control.c:1299) ==496395== by 0x182807: txw_fire (watch.c:232) ==496395== by 0x182AA9: watch_topology_changed (watch.c:300) ==496395== by 0x1290ED: updates_complete (chaintopology.c:624) ==496395== by 0x129BF4: get_new_block (chaintopology.c:835) ==496395== by 0x125EEF: getrawblockbyheight_callback (bitcoind.c:362) ==496395== by 0x176ECC: plugin_response_handle (plugin.c:584) ==496395== by 0x1770F5: plugin_read_json_one (plugin.c:690) ==496395== Block was alloc'd at ==496395== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==496395== by 0x22AC1C: allocate (tal.c:250) ==496395== by 0x22B1DD: tal_alloc_ (tal.c:428) ==496395== by 0x22B3A6: tal_alloc_arr_ (tal.c:471) ==496395== by 0x22C094: tal_dup_ (tal.c:805) ==496395== by 0x12B274: new_inflight (channel.c:187) ==496395== by 0x136D4C: wallet_commit_channel (dual_open_control.c:1260) ==496395== by 0x13B084: handle_commit_received (dual_open_control.c:2839) ==496395== by 0x13B6AF: dual_opend_msg (dual_open_control.c:2976) ==496395== by 0x1809FF: sd_msg_read (subd.c:553) ==496395== by 0x218F5D: next_plan (io.c:59) ==496395== by 0x219B65: do_plan (io.c:407) ``` Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 4 ++-- tests/test_closing.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index ebcfcf4edef7..7adec90f32cc 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1210,7 +1210,7 @@ static bool check_funding_tx(const struct bitcoin_tx *tx, static void update_channel_from_inflight(struct lightningd *ld, struct channel *channel, - struct channel_inflight *inflight) + const struct channel_inflight *inflight) { struct wally_psbt *psbt_copy; @@ -1223,7 +1223,7 @@ static void update_channel_from_inflight(struct lightningd *ld, channel->push = inflight->lease_fee; tal_free(channel->lease_commit_sig); channel->lease_commit_sig - = tal_steal(channel, inflight->lease_commit_sig); + = tal_dup_or_null(channel, secp256k1_ecdsa_signature, inflight->lease_commit_sig); channel->lease_chan_max_msat = inflight->lease_chan_max_msat; channel->lease_chan_max_ppt = inflight->lease_chan_max_ppt; diff --git a/tests/test_closing.py b/tests/test_closing.py index 481ac860ad07..61264648b7dd 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -814,7 +814,7 @@ def test_channel_lease_falls_behind(node_factory, bitcoind): compact_lease=rates['compact_lease']) # sink the funding transaction - bitcoind.generate_block(1) + bitcoind.generate_block(1, wait_for_mempool=1) # stop l1 l1.stop() From fc2401c1acb0d26b1c9e47a76f5ee7106d891d25 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 Jan 2022 13:09:07 +1030 Subject: [PATCH 0294/1530] pytest: make test_mpp_adaptive more reliable If the HTLCs are completely negotiated, we can get a channel break when we mine a pile of blocks. This is mainly seen with Postgres, due to the db speed. Signed-off-by: Rusty Russell --- tests/test_pay.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index 50eb721f857a..33910f28802a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3630,6 +3630,16 @@ def test_mpp_adaptive(node_factory, bitcoind): assert(c12['spendable_msat'].millisatoshis < amt) assert(c34['spendable_msat'].millisatoshis < amt) + # Make sure all HTLCs entirely resolved before we mine more blocks! + def all_htlcs(n): + htlcs = [] + for p in n.rpc.listpeers()['peers']: + for c in p['channels']: + htlcs += c['htlcs'] + return htlcs + + wait_for(lambda: all([all_htlcs(n) == [] for n in [l1, l2, l3, l4]])) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8) From bba9525cc3b9316aaa4efc6d71eb246e97c28425 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 31 Jan 2022 13:11:46 +1030 Subject: [PATCH 0295/1530] pytest: note unfixable test_htlc_rexmit_while_closing test. We really need our own lnprototest tests for packet-based stuff; these message-based tests are inherently delicate and awkward. In particular, connectd now does dev-disconnect, so the socket is not immediately closed after a dev-disconnect command. In this case, the WIRE_SHUTDOWN has often already been written from connectd to channeld. But it sometimes works, too. Signed-off-by: Rusty Russell --- tests/test_closing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index 61264648b7dd..b273e2d6a7ff 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3442,6 +3442,7 @@ def test_closing_higherfee(node_factory, bitcoind, executor): @pytest.mark.developer("needs dev_disconnect") def test_htlc_rexmit_while_closing(node_factory, executor): """Retranmitting an HTLC revocation while shutting down should work""" + # FIXME: This should be in lnprototest! UNRELIABLE. # l1 disconnects after sending second COMMITMENT_SIGNED. # Then it stops receiving after sending WIRE_SHUTDOWN (which is before it # reads the revoke_and_ack). From d4fee837c227babcd667c05c4d313bf68b325637 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 7 Feb 2022 14:02:32 +1030 Subject: [PATCH 0296/1530] misc: clarifications from cdecker review. Signed-off-by: Rusty Russell --- connectd/multiplex.h | 2 +- gossipd/gossip_generation.c | 6 ++++++ gossipd/gossipd.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 13c37e298221..1c87ccb59a35 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -22,7 +22,7 @@ void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES); /* Inject a message into the output stream. Unlike a raw msg_enqueue, - * this does io logging if required. */ + * this does io logging. */ void inject_peer_msg(struct peer *peer, const u8 *msg TAKES); void setup_peer_gossip_store(struct peer *peer, diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 70b8f854b15f..e7c9e8e01e42 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -821,6 +821,12 @@ void handle_used_local_channel_update(struct daemon *daemon, const u8 *msg) &scid)); return; } + + /* This whole idea is racy: they might have used a *previous* update. + * But that's OK: the notification is an optimization to avoid + * broadcasting updates we never use (route flapping). In this case, + * we might broadcast a more recent update than the one we sent to a + * peer. */ local_channel_update_latest(daemon, chan); } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 6113dc8cae87..4b607afb7f50 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -442,7 +442,7 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) peer = find_peer(daemon, &id); if (!peer) { - status_broken("connectd sent gossip msg %s for unknown peer %s", + status_broken("connectd sent gossip msg %s from unknown peer %s", peer_wire_name(fromwire_peektype(msg)), type_to_string(tmpctx, struct node_id, &id)); return; From c63d9e60f8948fa53a625a59bc1b9a296060afc5 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Mon, 7 Feb 2022 20:07:27 +0100 Subject: [PATCH 0297/1530] Add short_channel_id to listchannels.schema.json This was previously marked as required, but not actually defined in properties. [ Regenerated listchannels manpage -- RR ] --- doc/lightning-listchannels.7.md | 3 ++- doc/lightning-listpeers.7.md | 2 +- doc/schemas/listchannels.schema.json | 4 ++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index 1cc762ec100c..668b060fb770 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -34,6 +34,7 @@ RETURN VALUE On success, an object containing **channels** is returned. It is an array of objects, where each object contains: - **source** (pubkey): the source node - **destination** (pubkey): the destination node +- **short_channel_id** (short_channel_id): short channel id of channel - **public** (boolean): true if this is announced (otherwise it must be our channel) - **amount_msat** (msat): the total capacity of this channel (always a whole number of satoshis) - **message_flags** (u8): as defined by BOLT #7 @@ -77,4 +78,4 @@ Lightning RFC site - BOLT \#7: -[comment]: # ( SHA256STAMP:c2ebd6407a66ad5f67b5fd933552a468e306b1fed7868f92985c24e321861fae) +[comment]: # ( SHA256STAMP:e27d51a95411739ee10b082beaca55e33de4b2177b0e39df2223700c3141bc02) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 17a48d81f3ae..fc37753e231a 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -377,4 +377,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:444080f602bbce7d86bc7c38a4a2b1186a31c63d6b747d92c5aa4711dd9118c1) +[comment]: # ( SHA256STAMP:d45de73a968bcc3e7fb699f0acd7a27a4c70d9e9fded8af8c684a71fe012f1ce) diff --git a/doc/schemas/listchannels.schema.json b/doc/schemas/listchannels.schema.json index a187e6196394..8d95dd87f3e4 100644 --- a/doc/schemas/listchannels.schema.json +++ b/doc/schemas/listchannels.schema.json @@ -36,6 +36,10 @@ "type": "pubkey", "description": "the destination node" }, + "short_channel_id": { + "type": "short_channel_id", + "description": "short channel id of channel" + }, "public": { "type": "boolean", "description": "true if this is announced (otherwise it must be our channel)" From 849f92f4b0397be3c657ec53549045e56e447664 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 8 Feb 2022 11:59:13 +1030 Subject: [PATCH 0298/1530] doc/schemas: disallow unknown fields in listchannels. Would have caught the previous bug! Signed-off-by: Rusty Russell --- doc/schemas/listchannels.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/schemas/listchannels.schema.json b/doc/schemas/listchannels.schema.json index 8d95dd87f3e4..feea2028531d 100644 --- a/doc/schemas/listchannels.schema.json +++ b/doc/schemas/listchannels.schema.json @@ -10,7 +10,7 @@ "type": "array", "items": { "type": "object", - "additionalProperties": true, + "additionalProperties": false, "required": [ "source", "destination", From 74071c95a2a515d2b5290bcfeab6715e720bd1d9 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sun, 23 Jan 2022 18:23:26 +0100 Subject: [PATCH 0299/1530] Remove some formatting inconsistencies I've tried automatically parsing the docs, and these inconsistencies made it harder to do that. (I tried to do that for a project which I can't share yet, I'm not sure if it'll even work). --- doc/lightning-connect.7.md | 2 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-listconfigs.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listnodes.7.md | 2 +- doc/lightning-listpays.7.md | 2 +- doc/lightning-ping.7.md | 2 +- doc/lightning-plugin.7.md | 2 +- doc/lightning-reserveinputs.7.md | 2 +- doc/lightning-sendpsbt.7.md | 2 +- doc/lightning-signpsbt.7.md | 2 +- doc/lightning-unreserveinputs.7.md | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index 2209f67ec8fa..d2f5a17e4b31 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -4,7 +4,7 @@ lightning-connect -- Command for connecting to another lightning node SYNOPSIS -------- -**connect** *id* \[*host* *port*\] +**connect** *id* \[*host*\] \[*port*\] DESCRIPTION ----------- diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index f94e706aa9ea..27704c00f01e 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -4,7 +4,7 @@ lightning-datastore -- Command for storing (plugin) data SYNOPSIS -------- -**datastore** *key* [*string*] [*hex*] [*mode*] [*generation*] +**datastore** *key* \[*string*\] \[*hex*\] \[*mode*\] \[*generation*\] DESCRIPTION ----------- diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index 477887641b1c..af63fc80534b 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -4,7 +4,7 @@ lightning-deldatastore -- Command for removing (plugin) data SYNOPSIS -------- -**deldatastore** *key* [*generation*] +**deldatastore** *key* \[*generation*\] DESCRIPTION ----------- diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index b61f7d01fc1d..26a80c0e5c43 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -4,7 +4,7 @@ lightning-listconfigs -- Command to list all configuration options. SYNOPSIS -------- -**listconfigs** \[config\] +**listconfigs** \[*config*\] DESCRIPTION ----------- diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index 316e719e90ad..09f365b5f0e4 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -4,7 +4,7 @@ lightning-listdatastore -- Command for listing (plugin) data SYNOPSIS -------- -**listdatastore** [*key*] +**listdatastore** \[*key*\] DESCRIPTION ----------- diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index 3f36ce07f9cf..c2f040600faf 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -4,7 +4,7 @@ lightning-listnodes -- Command to get the list of nodes in the known network. SYNOPSIS -------- -**listnodes** \[id\] +**listnodes** \[*id*\] DESCRIPTION ----------- diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index a862b51e1267..0a0805784908 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -4,7 +4,7 @@ lightning-listpays -- Command for querying payment status SYNOPSIS -------- -**listpays** \[bolt11\] \[payment_hash\] \[status\] +**listpays** \[*bolt11*\] \[*payment_hash*\] \[*status*\] DESCRIPTION ----------- diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index 225b48bf7383..7d0918598c86 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -4,7 +4,7 @@ lightning-ping -- Command to check if a node is up. SYNOPSIS -------- -**ping** *id* \[len\] \[pongbytes\] +**ping** *id* \[*len*\] \[*pongbytes*\] DESCRIPTION ----------- diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index 3b4f455c15af..88003f1c06e8 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -4,7 +4,7 @@ lightning-plugin -- Manage plugins with RPC SYNOPSIS -------- -**plugin** command \[parameter\] \[second\_parameter\] +**plugin** command \[*parameter*\] \[*second\_parameter*\] DESCRIPTION ----------- diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index 2cdc30649b10..1b2f4768c831 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -4,7 +4,7 @@ lightning-reserveinputs -- Construct a transaction and reserve the UTXOs it spen SYNOPSIS -------- -**reserveinputs** *psbt* [*exclusive*] [*reserve*] +**reserveinputs** *psbt* \[*exclusive*\] \[*reserve*\] DESCRIPTION ----------- diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index fd150b9d38fa..056e68e62fd0 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -4,7 +4,7 @@ lightning-sendpsbt -- Command to finalize, extract and send a partially signed b SYNOPSIS -------- -**sendpsbt** *psbt* [*reserve*] +**sendpsbt** *psbt* \[*reserve*\] DESCRIPTION ----------- diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index cef87f311044..883f9120c912 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -4,7 +4,7 @@ lightning-signpsbt -- Command to sign a wallet's inputs on a provided bitcoin tr SYNOPSIS -------- -**signpsbt** *psbt* [*signonly*] +**signpsbt** *psbt* \[*signonly*\] DESCRIPTION ----------- diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index 869adbb3f576..e84b8f6a016b 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -4,7 +4,7 @@ lightning-unreserveinputs -- Release reserved UTXOs SYNOPSIS -------- -**unreserveinputs** *psbt* [*reserve*] +**unreserveinputs** *psbt* \[*reserve*\] DESCRIPTION ----------- From 0924b477b282ff302adf6d22af0d86787542483e Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Wed, 26 Jan 2022 18:18:49 +0100 Subject: [PATCH 0300/1530] Remove useless \ --- doc/lightning-autocleaninvoice.7.md | 2 +- doc/lightning-check.7.md | 2 +- doc/lightning-checkmessage.7.md | 2 +- doc/lightning-close.7.md | 2 +- doc/lightning-connect.7.md | 2 +- doc/lightning-createonion.7.md | 2 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-decodepay.7.md | 2 +- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-delexpiredinvoice.7.md | 2 +- doc/lightning-disconnect.7.md | 2 +- doc/lightning-fetchinvoice.7.md | 2 +- doc/lightning-fundchannel.7.md | 4 ++-- doc/lightning-fundchannel_start.7.md | 2 +- doc/lightning-funderupdate.7.md | 2 +- doc/lightning-fundpsbt.7.md | 2 +- doc/lightning-getlog.7.md | 2 +- doc/lightning-getroute.7.md | 8 ++++---- doc/lightning-help.7.md | 2 +- doc/lightning-invoice.7.md | 4 ++-- doc/lightning-keysend.7.md | 2 +- doc/lightning-listchannels.7.md | 2 +- doc/lightning-listconfigs.7 | 4 ++-- doc/lightning-listconfigs.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listforwards.7.md | 2 +- doc/lightning-listfunds.7.md | 2 +- doc/lightning-listinvoices.7.md | 2 +- doc/lightning-listnodes.7.md | 2 +- doc/lightning-listoffers.7.md | 2 +- doc/lightning-listpays.7.md | 2 +- doc/lightning-listpeers.7.md | 2 +- doc/lightning-listsendpays.7.md | 2 +- doc/lightning-multifundchannel.7.md | 2 +- doc/lightning-multiwithdraw.7.md | 2 +- doc/lightning-newaddr.7.md | 2 +- doc/lightning-offer.7.md | 4 ++-- doc/lightning-offerout.7.md | 2 +- doc/lightning-openchannel_bump.7.md | 2 +- doc/lightning-openchannel_init.7.md | 2 +- doc/lightning-pay.7.md | 10 +++++----- doc/lightning-ping.7.md | 2 +- doc/lightning-plugin.7.md | 2 +- doc/lightning-reserveinputs.7.md | 2 +- doc/lightning-sendinvoice.7.md | 2 +- doc/lightning-sendonion.7.md | 4 ++-- doc/lightning-sendonionmessage.7.md | 2 +- doc/lightning-sendpay.7.md | 4 ++-- doc/lightning-sendpsbt.7.md | 2 +- doc/lightning-setchannelfee.7.md | 2 +- doc/lightning-signpsbt.7.md | 2 +- doc/lightning-txprepare.7.md | 6 +++--- doc/lightning-unreserveinputs.7.md | 2 +- doc/lightning-utxopsbt.7.md | 2 +- doc/lightning-waitanyinvoice.7.md | 2 +- doc/lightning-waitblockheight.7.md | 2 +- doc/lightning-waitsendpay.7.md | 2 +- doc/lightning-withdraw.7.md | 2 +- 58 files changed, 73 insertions(+), 73 deletions(-) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index 270f63b876b8..9cb826748fe6 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -4,7 +4,7 @@ lightning-autocleaninvoice -- Set up auto-delete of expired invoice SYNOPSIS -------- -**autocleaninvoice** \[*cycle\_seconds*\] \[*expired\_by*\] +**autocleaninvoice** [*cycle\_seconds*] [*expired\_by*] DESCRIPTION ----------- diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index a15bba93e88a..500e2aa43692 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -4,7 +4,7 @@ lightning-check -- Command for verifying parameters SYNOPSIS -------- -**check** *command\_to\_check* \[*parameters*\] +**check** *command\_to\_check* [*parameters*] DESCRIPTION ----------- diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index da056b3fc89a..9ccf3c13e23a 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -4,7 +4,7 @@ lightning-checkmessage -- Command to check if a signature is from a node SYNOPSIS -------- -**checkmessage** *message* *zbase* \[*pubkey*\] +**checkmessage** *message* *zbase* [*pubkey*] DESCRIPTION ----------- diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index c88b6b87f794..5f05818d5bd7 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -4,7 +4,7 @@ lightning-close -- Command for closing channels with direct peers SYNOPSIS -------- -**close** *id* \[*unilateraltimeout*\] \[*destination*\] \[*fee_negotiation_step*\] \[*wrong_funding*\] \[*force_lease_closed*\] [\*feerange\*] +**close** *id* [*unilateraltimeout*] [*destination*] [*fee_negotiation_step*] [*wrong_funding*] [*force_lease_closed*] [\*feerange\*] DESCRIPTION ----------- diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index d2f5a17e4b31..02a85f1c068b 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -4,7 +4,7 @@ lightning-connect -- Command for connecting to another lightning node SYNOPSIS -------- -**connect** *id* \[*host*\] \[*port*\] +**connect** *id* [*host*] [*port*] DESCRIPTION ----------- diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index b35d33836aa6..9eed31890c2a 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -4,7 +4,7 @@ lightning-createonion -- Low-level command to create a custom onion SYNOPSIS -------- -**createonion** *hops* *assocdata* \[*session_key*\] \[*onion_size*\] +**createonion** *hops* *assocdata* [*session_key*] [*onion_size*] DESCRIPTION ----------- diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index 27704c00f01e..f94e706aa9ea 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -4,7 +4,7 @@ lightning-datastore -- Command for storing (plugin) data SYNOPSIS -------- -**datastore** *key* \[*string*\] \[*hex*\] \[*mode*\] \[*generation*\] +**datastore** *key* [*string*] [*hex*] [*mode*] [*generation*] DESCRIPTION ----------- diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index e74da14f6858..bbdc75c04892 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -4,7 +4,7 @@ lightning-decodepay -- Command for decoding a bolt11 string (low-level) SYNOPSIS -------- -**decodepay** *bolt11* \[*description*\] +**decodepay** *bolt11* [*description*] DESCRIPTION ----------- diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index af63fc80534b..477887641b1c 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -4,7 +4,7 @@ lightning-deldatastore -- Command for removing (plugin) data SYNOPSIS -------- -**deldatastore** *key* \[*generation*\] +**deldatastore** *key* [*generation*] DESCRIPTION ----------- diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index 664661ac603c..45cf12cbcede 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -4,7 +4,7 @@ lightning-delexpiredinvoice -- Command for removing expired invoices SYNOPSIS -------- -**delexpiredinvoice** \[*maxexpirytime*\] +**delexpiredinvoice** [*maxexpirytime*] DESCRIPTION ----------- diff --git a/doc/lightning-disconnect.7.md b/doc/lightning-disconnect.7.md index 19424cdef826..d1ec7f708e43 100644 --- a/doc/lightning-disconnect.7.md +++ b/doc/lightning-disconnect.7.md @@ -4,7 +4,7 @@ lightning-disconnect -- Command for disconnecting from another lightning node SYNOPSIS -------- -**disconnect** *id* \[*force*\] +**disconnect** *id* [*force*] DESCRIPTION ----------- diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index bb7c3f34c4c6..799bde5f65ec 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -6,7 +6,7 @@ SYNOPSIS **(WARNING: experimental-offers only)** -**fetchinvoice** *offer* \[*msatoshi*\] \[*quantity*\] \[*recurrence_counter*\] \[*recurrence_start*\] \[*recurrence_label*\] \[*timeout*\] \[*payer_note*\] +**fetchinvoice** *offer* [*msatoshi*] [*quantity*] [*recurrence_counter*] [*recurrence_start*] [*recurrence_label*] [*timeout*] [*payer_note*] DESCRIPTION ----------- diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index 1861d3b571d3..229e484240aa 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -4,8 +4,8 @@ lightning-fundchannel -- Command for establishing a lightning channel SYNOPSIS -------- -**fundchannel** *id* *amount* \[*feerate*\] \[*announce*\] \[*minconf*\] -\[*utxos*\] \[*push_msat*\] \[*close_to*\] \[*request_amt*\] \[*compact_lease*\] +**fundchannel** *id* *amount* [*feerate*] [*announce*] [*minconf*] +[*utxos*] [*push_msat*] [*close_to*] [*request_amt*] [*compact_lease*] DESCRIPTION ----------- diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index af9694d4b925..1a0da9c74ddc 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -4,7 +4,7 @@ lightning-fundchannel\_start -- Command for initiating channel establishment for SYNOPSIS -------- -**fundchannel\_start** *id* *amount* \[*feerate* *announce* *close_to* *push_msat*\] +**fundchannel\_start** *id* *amount* [*feerate* *announce* *close_to* *push_msat*] DESCRIPTION ----------- diff --git a/doc/lightning-funderupdate.7.md b/doc/lightning-funderupdate.7.md index cec36163b816..700ecb9bfc98 100644 --- a/doc/lightning-funderupdate.7.md +++ b/doc/lightning-funderupdate.7.md @@ -4,7 +4,7 @@ lightning-funderupdate -- Command for adjusting node funding v2 channels SYNOPSIS -------- -**funderupdate** \[*policy*\] \[*policy_mod*\] \[*leases_only*\] \[*min_their_funding_msat*\] \[*max_their_funding_msat*\] \[*per_channel_min_msat*\] \[*per_channel_max_msat*\] \[*reserve_tank_msat*\] \[*fuzz_percent*\] \[*fund_probability*\] \[*lease_fee_base_msat*\] \[*lease_fee_basis*\] \[*funding_weight*\] \[*channel_fee_max_base_msat*\] \[*channel_fee_max_proportional_thousandths*\] \[*compact_lease*\] +**funderupdate** [*policy*] [*policy_mod*] [*leases_only*] [*min_their_funding_msat*] [*max_their_funding_msat*] [*per_channel_min_msat*] [*per_channel_max_msat*] [*reserve_tank_msat*] [*fuzz_percent*] [*fund_probability*] [*lease_fee_base_msat*] [*lease_fee_basis*] [*funding_weight*] [*channel_fee_max_base_msat*] [*channel_fee_max_proportional_thousandths*] [*compact_lease*] NOTE: Must have --experimental-dual-fund enabled for these settings to take effect. diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 4dfca65eb19c..553efdec3dd8 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -4,7 +4,7 @@ lightning-fundpsbt -- Command to populate PSBT inputs from the wallet SYNOPSIS -------- -**fundpsbt** *satoshi* *feerate* *startweight* \[*minconf*\] \[*reserve*\] \[*locktime*\] \[*min_witness_weight*\] \[*excess_as_change*\] +**fundpsbt** *satoshi* *feerate* *startweight* [*minconf*] [*reserve*] [*locktime*] [*min_witness_weight*] [*excess_as_change*] DESCRIPTION ----------- diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index 3637fe010d94..8007f70ae2a0 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -4,7 +4,7 @@ lightning-getlog -- Command to show logs. SYNOPSIS -------- -**getlog** \[*level*\] +**getlog** [*level*] DESCRIPTION ----------- diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index f597678cd3dd..e490542a3e4c 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -4,8 +4,8 @@ lightning-getroute -- Command for routing a payment (low-level) SYNOPSIS -------- -**getroute** *id* *msatoshi* *riskfactor* \[*cltv*\] \[*fromid*\] -\[*fuzzpercent*\] \[*exclude*\] \[*maxhops*\] +**getroute** *id* *msatoshi* *riskfactor* [*cltv*] [*fromid*] +[*fuzzpercent*] [*exclude*] [*maxhops*] DESCRIPTION ----------- @@ -39,8 +39,8 @@ route generated. 0.0 means the exact fee of that channel is used, while 100.0 means the fee used might be from 0 to twice the actual fee. The default is 5.0, or up to 5% fee distortion. -*exclude* is a JSON array of short-channel-id/direction (e.g. \[ -"564334x877x1/0", "564195x1292x0/1" \]) or node-id which should be excluded +*exclude* is a JSON array of short-channel-id/direction (e.g. [ +"564334x877x1/0", "564195x1292x0/1" ]) or node-id which should be excluded from consideration for routing. The default is not to exclude any channels or nodes. Note if the source or destination is excluded, the command result is undefined. diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index 2bccd6cc7ca7..24fdf73bb093 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -4,7 +4,7 @@ lightning-help -- Command to return all information about RPC commands. SYNOPSIS -------- -**help** \[*command\*] +**help** [*command\*] DESCRIPTION ----------- diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 692eea43170b..5347625acebe 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -4,8 +4,8 @@ lightning-invoice -- Command for accepting payments SYNOPSIS -------- -**invoice** *msatoshi* *label* *description* \[*expiry*\] -\[*fallbacks*\] \[*preimage*\] \[*exposeprivatechannels*\] \[*cltv*\] +**invoice** *msatoshi* *label* *description* [*expiry*] +[*fallbacks*] [*preimage*] [*exposeprivatechannels*] [*cltv*] DESCRIPTION ----------- diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index 559d9813f82e..f5c613202fe7 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -4,7 +4,7 @@ lightning-keysend -- Send funds to a node without an invoice SYNOPSIS -------- -**keysend** *destination* *msatoshi* \[*label*\] \[*maxfeepercent*\] \[*retry\_for*\] \[*maxdelay*\] \[*exemptfee*\] +**keysend** *destination* *msatoshi* [*label*] [*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*] DESCRIPTION ----------- diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index 668b060fb770..7c9063840bbd 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -4,7 +4,7 @@ lightning-listchannels -- Command to query active lightning channels in the enti SYNOPSIS -------- -**listchannels** \[*short\_channel\_id*\] \[*source*\] \[*destination*\] +**listchannels** [*short\_channel\_id*] [*source*] [*destination*] DESCRIPTION ----------- diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index 5818f32f41d7..d946d1e03a7c 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -3,7 +3,7 @@ lightning-listconfigs - Command to list all configuration options\. .SH SYNOPSIS -\fBlistconfigs\fR [config] +\fBlistconfigs\fR [\fIconfig\fR] .SH DESCRIPTION @@ -276,4 +276,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:37b3fd1b2c5b2c903c25579f96d0bb4116955f3aebbf6bbb97f8a62e352cf440 +\" SHA256STAMP:759090c2619cd40cba89b670ea16d4885f9aea520588d30d0ee8041369d5b91e diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 26a80c0e5c43..c6cdf6290720 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -4,7 +4,7 @@ lightning-listconfigs -- Command to list all configuration options. SYNOPSIS -------- -**listconfigs** \[*config*\] +**listconfigs** [*config*] DESCRIPTION ----------- diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index 09f365b5f0e4..316e719e90ad 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -4,7 +4,7 @@ lightning-listdatastore -- Command for listing (plugin) data SYNOPSIS -------- -**listdatastore** \[*key*\] +**listdatastore** [*key*] DESCRIPTION ----------- diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 1ff8b2b70829..590150a56e2c 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -4,7 +4,7 @@ lightning-listforwards -- Command showing all htlcs and their information SYNOPSIS -------- -**listforwards** \[*status*\] \[*in_channel*\] \[*out_channel*\] +**listforwards** [*status*] [*in_channel*] [*out_channel*] DESCRIPTION ----------- diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index e049eeaa1938..0712aafdeef0 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -4,7 +4,7 @@ lightning-listfunds -- Command showing all funds currently managed by the c-ligh SYNOPSIS -------- -**listfunds** \[*spent*\] +**listfunds** [*spent*] DESCRIPTION ----------- diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index 763a8d2329c2..059d460fd978 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -4,7 +4,7 @@ lightning-listinvoices -- Command for querying invoice status SYNOPSIS -------- -**listinvoices** \[*label*\] \[*invstring*\] \[*payment_hash*\] \[*offer_id*\] +**listinvoices** [*label*] [*invstring*] [*payment_hash*] [*offer_id*] DESCRIPTION ----------- diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index c2f040600faf..b386c56696b7 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -4,7 +4,7 @@ lightning-listnodes -- Command to get the list of nodes in the known network. SYNOPSIS -------- -**listnodes** \[*id*\] +**listnodes** [*id*] DESCRIPTION ----------- diff --git a/doc/lightning-listoffers.7.md b/doc/lightning-listoffers.7.md index baca636d23eb..7a5b76965cc9 100644 --- a/doc/lightning-listoffers.7.md +++ b/doc/lightning-listoffers.7.md @@ -5,7 +5,7 @@ SYNOPSIS -------- **(WARNING: experimental-offers only)** -**listoffers** \[*offer_id*\] \[*active_only*\] +**listoffers** [*offer_id*] [*active_only*] DESCRIPTION ----------- diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index 0a0805784908..f4c2edf82bb9 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -4,7 +4,7 @@ lightning-listpays -- Command for querying payment status SYNOPSIS -------- -**listpays** \[*bolt11*\] \[*payment_hash*\] \[*status*\] +**listpays** [*bolt11*] [*payment_hash*] [*status*] DESCRIPTION ----------- diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index fc37753e231a..8e4862b1e32f 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -4,7 +4,7 @@ lightning-listpeers -- Command returning data on connected lightning nodes SYNOPSIS -------- -**listpeers** \[*id*\] \[*level*\] +**listpeers** [*id*] [*level*] DESCRIPTION ----------- diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 941078994535..9cd0034cbda2 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -4,7 +4,7 @@ lightning-listsendpays -- Low-level command for querying sendpay status SYNOPSIS -------- -**listsendpays** \[*bolt11*\] \[*payment\_hash*\] \[*status*\] +**listsendpays** [*bolt11*] [*payment\_hash*] [*status*] DESCRIPTION ----------- diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index e42b0a228972..d61fceac5d7e 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -4,7 +4,7 @@ lightning-multifundchannel -- Command for establishing many lightning channels SYNOPSIS -------- -**multifundchannel** *destinations* \[*feerate*\] \[*minconf*\] \[*utxos*\] \[*minchannels*\] \[*commitment_feerate*\] +**multifundchannel** *destinations* [*feerate*] [*minconf*] [*utxos*] [*minchannels*] [*commitment_feerate*] DESCRIPTION ----------- diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index 52b5379d8bcb..28be3855bb22 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -4,7 +4,7 @@ lightning-multiwithdraw -- Command for withdrawing to multiple addresses SYNOPSIS -------- -**multiwithdraw** *outputs* \[*feerate*\] \[*minconf*\] \[*utxos*\] +**multiwithdraw** *outputs* [*feerate*] [*minconf*] [*utxos*] DESCRIPTION ----------- diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index e2dda2a4c457..9d0a95532d1d 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -4,7 +4,7 @@ lightning-newaddr -- Command for generating a new address to be used by c-lightn SYNOPSIS -------- -**newaddr** \[ *addresstype* \] +**newaddr** [ *addresstype* ] DESCRIPTION ----------- diff --git a/doc/lightning-offer.7.md b/doc/lightning-offer.7.md index 0fa3a4218039..22b903efbbf3 100644 --- a/doc/lightning-offer.7.md +++ b/doc/lightning-offer.7.md @@ -6,7 +6,7 @@ SYNOPSIS **(WARNING: experimental-offers only)** -**offer** *amount* *description* \[*issuer*\] \[*label*\] \[*quantity_min*\] \[*quantity_max*\] \[*absolute_expiry*\] \[*recurrence*\] \[*recurrence_base*\] \[*recurrence_paywindow*\] \[*recurrence_limit*\] \[*single_use*\] +**offer** *amount* *description* [*issuer*] [*label*] [*quantity_min*] [*quantity_max*] [*absolute_expiry*] [*recurrence*] [*recurrence_base*] [*recurrence_paywindow*] [*recurrence_limit*] [*single_use*] DESCRIPTION ----------- @@ -69,7 +69,7 @@ period. This is encoded in the offer. e.g. "@1609459200" indicates you must start paying on the 1st January 2021. *recurrence_paywindow* is an optional argument of form -'-time+time\[%\]'. The first time is the number of seconds before the +'-time+time[%]'. The first time is the number of seconds before the start of a period in which an invoice and payment is valid, the second time is the number of seconds after the start of the period. For example *-604800+86400* means you can fetch an pay the invoice 4 weeks diff --git a/doc/lightning-offerout.7.md b/doc/lightning-offerout.7.md index a70db8707595..e74589c02193 100644 --- a/doc/lightning-offerout.7.md +++ b/doc/lightning-offerout.7.md @@ -7,7 +7,7 @@ SYNOPSIS **(WARNING: experimental-offers only)** -**offerout** *amount* *description* \[*issuer*\] \[*label*\] \[*absolute_expiry*\] \[*refund_for*\] +**offerout** *amount* *description* [*issuer*] [*label*] [*absolute_expiry*] [*refund_for*] DESCRIPTION ----------- diff --git a/doc/lightning-openchannel_bump.7.md b/doc/lightning-openchannel_bump.7.md index 2eb748c4c5f6..a863eb0fcf31 100644 --- a/doc/lightning-openchannel_bump.7.md +++ b/doc/lightning-openchannel_bump.7.md @@ -4,7 +4,7 @@ lightning-openchannel\_bump -- Command to initiate a channel RBF SYNOPSIS -------- -**openchannel_bump** *channel_id* *amount* *initalpsbt* \[*funding_feerate*\] +**openchannel_bump** *channel_id* *amount* *initalpsbt* [*funding_feerate*] DESCRIPTION ----------- diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index bd043a91ced6..e67a14150800 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -4,7 +4,7 @@ lightning-openchannel\_init -- Command to initiate a channel to a peer SYNOPSIS -------- -**openchannel_init** *id* *amount* *initalpsbt* \[*commitment_feerate*\] \[*funding_feerate*\] \[*announce*\] \[*close_to*\] \[*request_amt*\] \[*compact_lease*\] +**openchannel_init** *id* *amount* *initalpsbt* [*commitment_feerate*] [*funding_feerate*] [*announce*] [*close_to*] [*request_amt*] [*compact_lease*] DESCRIPTION ----------- diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index 988a3cce9a9b..ed815601591f 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -4,9 +4,9 @@ lightning-pay -- Command for sending a payment to a BOLT11 invoice SYNOPSIS -------- -**pay** *bolt11* \[*msatoshi*\] \[*label*\] \[*riskfactor*\] -\[*maxfeepercent*\] \[*retry\_for*\] \[*maxdelay*\] \[*exemptfee*\] -\[*exclude*\] +**pay** *bolt11* [*msatoshi*] [*label*] [*riskfactor*] +[*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*] +[*exclude*] DESCRIPTION ----------- @@ -41,8 +41,8 @@ finding routes and retrying the payment. However, a payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case. -*exclude* is a JSON array of short-channel-id/direction (e.g. \[ -"564334x877x1/0", "564195x1292x0/1" \]) or node-id which should be excluded +*exclude* is a JSON array of short-channel-id/direction (e.g. [ +"564334x877x1/0", "564195x1292x0/1" ]) or node-id which should be excluded from consideration for routing. The default is not to exclude any channels or nodes. diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index 7d0918598c86..0088748d9024 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -4,7 +4,7 @@ lightning-ping -- Command to check if a node is up. SYNOPSIS -------- -**ping** *id* \[*len*\] \[*pongbytes*\] +**ping** *id* [*len*] [*pongbytes*] DESCRIPTION ----------- diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index 88003f1c06e8..7c3032075a54 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -4,7 +4,7 @@ lightning-plugin -- Manage plugins with RPC SYNOPSIS -------- -**plugin** command \[*parameter*\] \[*second\_parameter*\] +**plugin** command [*parameter*] [*second\_parameter*] DESCRIPTION ----------- diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index 1b2f4768c831..2cdc30649b10 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -4,7 +4,7 @@ lightning-reserveinputs -- Construct a transaction and reserve the UTXOs it spen SYNOPSIS -------- -**reserveinputs** *psbt* \[*exclusive*\] \[*reserve*\] +**reserveinputs** *psbt* [*exclusive*] [*reserve*] DESCRIPTION ----------- diff --git a/doc/lightning-sendinvoice.7.md b/doc/lightning-sendinvoice.7.md index 6ca11f5dc6ea..5fc675ed1f6b 100644 --- a/doc/lightning-sendinvoice.7.md +++ b/doc/lightning-sendinvoice.7.md @@ -6,7 +6,7 @@ SYNOPSIS **(WARNING: experimental-offers only)** -**sendinvoice** *offer* *label* \[*msatoshi*\] \[*timeout*\] \[*quantity*\] +**sendinvoice** *offer* *label* [*msatoshi*] [*timeout*] [*quantity*] DESCRIPTION ----------- diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index ad88418c6e1f..927e779a668b 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -4,8 +4,8 @@ lightning-sendonion -- Send a payment with a custom onion packet SYNOPSIS -------- -**sendonion** *onion* *first_hop* *payment_hash* \[*label*\] \[*shared_secrets*\] \[*partid*\] \[*bolt11*\] -\[*msatoshi*\] \[*destination*\] +**sendonion** *onion* *first_hop* *payment_hash* [*label*] [*shared_secrets*] [*partid*] [*bolt11*] +[*msatoshi*] [*destination*] DESCRIPTION ----------- diff --git a/doc/lightning-sendonionmessage.7.md b/doc/lightning-sendonionmessage.7.md index 03627a56ab33..13a04af7cfeb 100644 --- a/doc/lightning-sendonionmessage.7.md +++ b/doc/lightning-sendonionmessage.7.md @@ -6,7 +6,7 @@ SYNOPSIS **(WARNING: experimental-onion-messages only)** -**sendonionmessage** *hops* \[*reply_path*\] +**sendonionmessage** *hops* [*reply_path*] DESCRIPTION ----------- diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 41e4ec2c30d3..c8d782154490 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -4,8 +4,8 @@ lightning-sendpay -- Low-level command for sending a payment via a route SYNOPSIS -------- -**sendpay** *route* *payment\_hash* \[*label*\] \[*msatoshi*\] -\[*bolt11*\] \[*payment_secret*\] \[*partid*\] +**sendpay** *route* *payment\_hash* [*label*] [*msatoshi*] +[*bolt11*] [*payment_secret*] [*partid*] DESCRIPTION ----------- diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index 056e68e62fd0..fd150b9d38fa 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -4,7 +4,7 @@ lightning-sendpsbt -- Command to finalize, extract and send a partially signed b SYNOPSIS -------- -**sendpsbt** *psbt* \[*reserve*\] +**sendpsbt** *psbt* [*reserve*] DESCRIPTION ----------- diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index cefcf6f4d9b0..7f94d4872725 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -4,7 +4,7 @@ lightning-setchannelfee -- Command for setting specific routing fees on a lightn SYNOPSIS -------- -**setchannelfee** *id* \[*base*\] \[*ppm*\] \[*enforcedelay*\] +**setchannelfee** *id* [*base*] [*ppm*] [*enforcedelay*] DESCRIPTION ----------- diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index 883f9120c912..cef87f311044 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -4,7 +4,7 @@ lightning-signpsbt -- Command to sign a wallet's inputs on a provided bitcoin tr SYNOPSIS -------- -**signpsbt** *psbt* \[*signonly*\] +**signpsbt** *psbt* [*signonly*] DESCRIPTION ----------- diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index dcc46344aa3e..83efc718672e 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -4,7 +4,7 @@ lightning-txprepare -- Command to prepare to withdraw funds from the internal wa SYNOPSIS -------- -**txprepare** *outputs* \[*feerate*\] \[*minconf*\] \[*utxos*\] +**txprepare** *outputs* [*feerate*] [*minconf*] [*utxos*] DESCRIPTION ----------- @@ -15,9 +15,9 @@ in *outputs*. The *outputs* is the array of output that include *destination* and *amount*(\{*destination*: *amount*\}). Its format is like: -\[\{address1: amount1\}, \{address2: amount2\}\] +[\{address1: amount1\}, \{address2: amount2\}] or -\[\{address: *all*\}\]. +[\{address: *all*\}]. It supports any number of **confirmed** outputs. The *destination* of output is the address which can be of any Bitcoin accepted diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index e84b8f6a016b..869adbb3f576 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -4,7 +4,7 @@ lightning-unreserveinputs -- Release reserved UTXOs SYNOPSIS -------- -**unreserveinputs** *psbt* \[*reserve*\] +**unreserveinputs** *psbt* [*reserve*] DESCRIPTION ----------- diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index 01db6aede6ba..090d66961318 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -4,7 +4,7 @@ lightning-utxopsbt -- Command to populate PSBT inputs from given UTXOs SYNOPSIS -------- -**utxopsbt** *satoshi* *feerate* *startweight* *utxos* \[*reserve*\] \[*reservedok*\] \[*locktime*\] \[*min_witness_weight*\] \[*excess_as_change*\] +**utxopsbt** *satoshi* *feerate* *startweight* *utxos* [*reserve*] [*reservedok*] [*locktime*] [*min_witness_weight*] [*excess_as_change*] DESCRIPTION ----------- diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index 6dafab1ad074..725068fad716 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -4,7 +4,7 @@ lightning-waitanyinvoice -- Command for waiting for payments SYNOPSIS -------- -**waitanyinvoice** \[*lastpay\_index*\] \[*timeout*\] +**waitanyinvoice** [*lastpay\_index*] [*timeout*] DESCRIPTION ----------- diff --git a/doc/lightning-waitblockheight.7.md b/doc/lightning-waitblockheight.7.md index a84561bacd98..7eae32216db4 100644 --- a/doc/lightning-waitblockheight.7.md +++ b/doc/lightning-waitblockheight.7.md @@ -4,7 +4,7 @@ lightning-waitblockheight -- Command for waiting for blocks on the blockchain SYNOPSIS -------- -**waitblockheight** *blockheight* \[*timeout*\] +**waitblockheight** *blockheight* [*timeout*] DESCRIPTION ----------- diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index abca8ea0ed31..ff87bea3efc7 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -4,7 +4,7 @@ lightning-waitsendpay -- Command for sending a payment via a route SYNOPSIS -------- -**waitsendpay** *payment\_hash* \[*timeout*\] \[*partid*\] +**waitsendpay** *payment\_hash* [*timeout*] [*partid*] DESCRIPTION ----------- diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index 204f2131c64d..007a361f52ae 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -4,7 +4,7 @@ lightning-withdraw -- Command for withdrawing funds from the internal wallet SYNOPSIS -------- -**withdraw** *destination* *satoshi* \[*feerate*\] \[*minconf*\] \[*utxos*\] +**withdraw** *destination* *satoshi* [*feerate*] [*minconf*] [*utxos*] DESCRIPTION ----------- From 95eb868047aad17e4ab2b1f546f40c49c0a9247c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 28 Jan 2022 22:53:10 +0100 Subject: [PATCH 0301/1530] pyln: Delete psql DBs after testing --- contrib/pyln-testing/pyln/testing/db.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/contrib/pyln-testing/pyln/testing/db.py b/contrib/pyln-testing/pyln/testing/db.py index efcd345dc03b..5ee90ce7944d 100644 --- a/contrib/pyln-testing/pyln/testing/db.py +++ b/contrib/pyln-testing/pyln/testing/db.py @@ -51,6 +51,9 @@ def execute(self, query: str) -> None: c.close() db.close() + def stop(self): + pass + class PostgresDb(object): def __init__(self, dbname, port): @@ -88,6 +91,15 @@ def execute(self, query): with self.conn, self.conn.cursor() as cur: cur.execute(query) + def stop(self): + """Clean up the database. + """ + self.conn.close() + conn = psycopg2.connect("dbname=postgres user=postgres host=localhost port={self.port}") + cur = conn.cursor() + cur.execute("DROP DATABASE {};".format(self.dbname)) + cur.close() + class SqliteDbProvider(object): def __init__(self, directory: str) -> None: @@ -208,3 +220,4 @@ def stop(self): # [1] https://www.postgresql.org/docs/9.1/server-shutdown.html self.proc.send_signal(signal.SIGINT) self.proc.wait() + shutil.rmtree(self.pgdir) From 0fc0ffc96142cd39f993334c817d46958b0b5484 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 13:49:56 +0100 Subject: [PATCH 0302/1530] msggen: Parse JSON-RPC schemas and build the in-memory model We build an in-memory model of what the API should look like, which will later be used to generate a variety of bindings. In this PR we will use the model to build structs corresponding to the requests and responses for the various methods. The JSON-RPC schemas serve as ground-truth, however they are missing a bit of context: methods, and the request-response matching (as well as a higher level grouping we'll call a Service). I'm tempted to create a new document that describes this behavior and we could even generate the rather repetitive JSON schemas from that document. Furthermore it'd allow us to add some required metadata such as grpc field numbering once we generate those bindings. Changelog-Added: JSON-RPC: A new `msggen` library allows easy generation of language bindings for the JSON-RPC from the JSON schemas --- contrib/msggen/README.md | 23 +++ contrib/msggen/msggen/__init__.py | 0 contrib/msggen/msggen/model.py | 300 ++++++++++++++++++++++++++++++ contrib/msggen/pyproject.toml | 19 ++ 4 files changed, 342 insertions(+) create mode 100644 contrib/msggen/README.md create mode 100644 contrib/msggen/msggen/__init__.py create mode 100644 contrib/msggen/msggen/model.py create mode 100644 contrib/msggen/pyproject.toml diff --git a/contrib/msggen/README.md b/contrib/msggen/README.md new file mode 100644 index 000000000000..2c90f9f94ee0 --- /dev/null +++ b/contrib/msggen/README.md @@ -0,0 +1,23 @@ +# MsgGen - Generating language bindings and docs from schemas and wire descriptions + +MsgGen is a collection of tools that are used to parse schemas and +(eventually) protocol wire CSVs into an intermediate representation in +memory, and then generate language specific bindings and +documentation from it. + + +The dependency graph looks like this: + + +```dot +digraph { + "JSON-RPC Schemas" -> "msggen model"; + "msggen model" -> "grpc proto file"; + "msggen model" -> "Rust From Converters"; + "grpc proto file" -> "Rust grpc bindings" + "Rust grpc bindings" -> "cln-grpc"; + "Rust From Converters" -> "cln-grpc"; + "msggen model" -> "Rust JSON-RPC structs"; + "Rust JSON-RPC structs" -> "cln-rpc"; +} +``` diff --git a/contrib/msggen/msggen/__init__.py b/contrib/msggen/msggen/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py new file mode 100644 index 000000000000..f1a703a1edcc --- /dev/null +++ b/contrib/msggen/msggen/model.py @@ -0,0 +1,300 @@ +from typing import List, Union, Optional +import logging + +logger = logging.getLogger(__name__) + + +def path2type(path): + typename = "".join([s.capitalize() for s in path.replace("[]", "").split(".")]) + return typename + + +class FieldName: + def __init__(self, name): + self.name = name + + def normalized(self): + name = { + "type": "item_type" + }.get(self.name, self.name) + + name = name.replace(' ', '_').replace('-', '_') + return name + + def __str__(self): + return self.name + + +class Field: + def __init__(self, path, description): + self.path = path + self.description = description + self.required = False + + @property + def name(self): + return FieldName(self.path.split(".")[-1]) + + def __str__(self): + return f"Field[path={self.path}, required={self.required}]" + + def __repr__(self): + return str(self) + + def normalized(self): + return self.name.normalized() + + +class Service: + """Top level class that wraps all the RPC methods. + """ + def __init__(self, name: str, methods=None): + self.name = name + self.methods = [] if methods is None else methods + + # If we require linking with some external files we'll add + # them here so the generator can use them. + self.includes: List[str] = [] + + def gather_types(self): + """Gather all types that might need to be defined. + """ + + def gather_subfields(field: Field) -> List[Field]: + fields = [field] + + if isinstance(field, CompositeField): + for f in field.fields: + fields.extend(gather_subfields(f)) + elif isinstance(field, ArrayField): + fields = [] + fields.extend(gather_subfields(field.itemtype)) + + return fields + + types = [] + for method in self.methods: + types.extend([method.request, method.response]) + for field in method.request.fields: + types.extend(gather_subfields(field)) + for field in method.response.fields: + types.extend(gather_subfields(field)) + return types + + +class Method: + def __init__(self, name: str, request: Field, response: Field): + self.name = name + self.request = request + self.response = response + + +class CompositeField(Field): + def __init__(self, typename, fields, path, description): + Field.__init__(self, path, description) + self.typename = typename + self.fields = fields + + @classmethod + def from_js(cls, js, path): + typename = path2type(path) + + properties = js["properties"] + # Ok, let's flatten the conditional properties. We do this by + # reformatting the outer conditions into the `allOf` format. + top = { + 'then': {'properties': js.get('then', {}).get('properties', [])}, + 'else': {'properties': js.get('else', {}).get('properties', [])}, + } + # Yes, this is ugly, but walking nested dicts always is. + for a in [top] + js.get('allOf', []): + var = a.get('then', {}) + props = var.get('properties', None) + if isinstance(props, dict): + for k, v in props.items(): + if k not in properties: + properties[k] = v + var = a.get('else', {}) + props = var.get('properties', None) + if isinstance(props, dict): + for k, v in props.items(): + if k not in properties: + properties[k] = v + + # Identify required fields + required = js.get("required", []) + fields = [] + for fname, ftype in properties.items(): + field = None + desc = ftype["description"] if "description" in ftype else "" + fpath = f"{path}.{fname}" + + if ftype.get("deprecated", False): + logger.warning(f"Unmanaged {fpath}, it is deprecated") + continue + + if "type" not in ftype: + logger.warning(f"Unmanaged {fpath}, it doesn't have a type") + continue + + # TODO Remove the `['string', 'null']` match once + # `listpeers.peers[].channels[].closer` no longer has this + # type + if ftype["type"] == ["string", "null"]: + ftype["type"] = "string" + + # Peek into the type so we know how to decode it + if ftype["type"] in ["string", ["string", "null"]] and "enum" in ftype: + field = EnumField.from_js(ftype, fpath) + + elif ftype["type"] == "object": + field = CompositeField.from_js(ftype, fpath) + + elif ftype["type"] == "array": + field = ArrayField.from_js(fpath, ftype) + + elif ftype["type"] in PrimitiveField.types: + field = PrimitiveField(ftype["type"], fpath, desc) + + else: + logger.warning( + f"Unmanaged {path}, type {ftype} is not mapped in the object model" + ) + + if field is not None: + field.required = fname in required + fields.append(field) + logger.debug(field) + + return CompositeField( + typename, fields, path, js["description"] if "description" in js else "" + ) + + def __str__(self): + fieldnames = ",".join([f.path.split(".")[-1] for f in self.fields]) + return f"CompositeField[name={self.path}, fields=[{fieldnames}]]" + + +class EnumVariant(Field): + """A variant of an enum with helpers for normalization of the display. + """ + def __init__(self, variant: Optional[str]): + self.variant = variant + + def __str__(self): + return self.variant + + def normalized(self): + return self.variant.replace(' ', '_').replace('-', '_').upper() + + +class EnumField(Field): + def __init__(self, typename, values, path, description): + Field.__init__(self, path, description) + self.typename = typename + self.values = values + self.variants = [EnumVariant(v) for v in self.values] + + @classmethod + def from_js(cls, js, path): + # Transform the path into something that is a valid TypeName + typename = path2type(path) + return EnumField( + typename, + values=filter(lambda i: i is not None, js["enum"]), + path=path, + description=js["description"] if "description" in js else "", + ) + + def __str__(self): + values = ",".join([v for v in self.values if v is not None]) + return f"Enum[path={self.path}, required={self.required}, values=[{values}]]" + + +class PrimitiveField(Field): + # Leaf types that we expect the binding languages to provide + types = [ + "boolean", + "u32", + "u64", + "u8", + "string", + "pubkey", + "signature", + "msat", + "hex", + "short_channel_id", + "txid", + "integer", + "u16", + "number", + ] + + def __init__(self, typename, path, description): + Field.__init__(self, path, description) + self.typename = typename + + def __str__(self): + return f"Primitive[path={self.path}, required={self.required}, type={self.typename}]" + + +class ArrayField(Field): + def __init__(self, itemtype, dims, path, description): + Field.__init__(self, path, description) + self.itemtype = itemtype + self.dims = dims + self.path = path + + @classmethod + def from_js(cls, path, js): + # Determine how nested we are + dims = 1 + child_js = js["items"] + while child_js["type"] == "array": + dims += 1 + child_js = child_js["items"] + + path += "[]" * dims + if child_js["type"] == "object": + itemtype = CompositeField.from_js(child_js, path) + + elif child_js["type"] == "string" and "enum" in child_js: + itemtype = EnumField.from_js(child_js, path) + + elif child_js["type"] in PrimitiveField.types: + itemtype = PrimitiveField( + child_js["type"], path, child_js.get("description", "") + ) + + logger.debug(f"Array path={path} dims={dims}, type={itemtype}") + return ArrayField( + itemtype, dims=dims, path=path, description=js.get("description", "") + ) + + def normalized(self): + # Strip the '[]' that we use to signal an array. The name + # itself doesn't need this. + return Field.normalized(self)[:-2] + + +class Command: + def __init__(self, name, fields): + self.name = name + self.fields = fields + + def __str__(self): + fieldnames = ",".join([f.path.split(".")[-1] for f in self.fields]) + return f"Command[name={self.name}, fields=[{fieldnames}]]" + + +def parse_doc(command, js) -> Union[CompositeField, Command]: + """Given a command name and its schema, generate the IR model""" + path = command + + # All our top-level wrappers are objects, right? + assert js["type"] in ["object", "string"] + if js["type"] == "string": + # Special case: stop just returns a string + return Command(path.capitalize(), []) + else: + return CompositeField.from_js(js, path) diff --git a/contrib/msggen/pyproject.toml b/contrib/msggen/pyproject.toml new file mode 100644 index 000000000000..a17c300cb34e --- /dev/null +++ b/contrib/msggen/pyproject.toml @@ -0,0 +1,19 @@ +[tool.poetry] +name = "msggen" +version = "0.1.0" +description = "A utility to transform wire messages and JSON-RPC messages to arbitrary target languages." +authors = ["Christian Decker "] +license = "BSD-MIT" + +[tool.poetry.dependencies] +python = "^3.6" + +[tool.poetry.dev-dependencies] +pytest = "^6.2.5" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +msggen = 'msggen.__main__:run' From b0053e2ca2d87d45c7ac5f4148c1d4cdce2f5909 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 13:54:23 +0100 Subject: [PATCH 0303/1530] msggen: Generate the cln-rpc Rust structs We're generating these structs so we can parse them directly into native objects. --- contrib/msggen/msggen/__main__.py | 140 +++++++++++++++++ contrib/msggen/msggen/rust.py | 243 ++++++++++++++++++++++++++++++ 2 files changed, 383 insertions(+) create mode 100644 contrib/msggen/msggen/__main__.py create mode 100644 contrib/msggen/msggen/rust.py diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py new file mode 100644 index 000000000000..982607aba820 --- /dev/null +++ b/contrib/msggen/msggen/__main__.py @@ -0,0 +1,140 @@ +from msggen.model import Method, CompositeField, Service +from msggen.rust import RustGenerator +from pathlib import Path +import subprocess +import json + + +def repo_root(): + path = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) + return Path(path.strip().decode('UTF-8')) + + +def load_jsonrpc_method(name): + """Load a method based on the file naming conventions for the JSON-RPC. + """ + base_path = (repo_root() / "doc" / "schemas").resolve() + req_file = base_path / f"{name.lower()}.request.json" + resp_file = base_path / f"{name.lower()}.schema.json" + request = CompositeField.from_js(json.load(open(req_file)), path=name) + response = CompositeField.from_js(json.load(open(resp_file)), path=name) + + # Normalize the method request and response typename so they no + # longer conflict. + request.typename += "Request" + response.typename += "Response" + + return Method( + name=name, + request=request, + response=response, + ) + + +def load_jsonrpc_service(): + method_names = [ + "Getinfo", + # "ListPeers", + "ListFunds", + # "ListConfigs", + "ListChannels", + "AddGossip", + "AutoCleanInvoice", + "CheckMessage", + # "check", # No point in mapping this one + "Close", + # "connect", + # "createinvoice", + # "createonion", + # "datastore", + # "decodepay", + # "decode", + # "deldatastore", + # "delexpiredinvoice", + # "delinvoice", + # "delpay", + # "disableoffer", + # "disconnect", + # "feerates", + # "fetchinvoice", + # "fundchannel_cancel", + # "fundchannel_complete", + # "fundchannel", + # "fundchannel_start", + # "funderupdate", + # "fundpsbt", + # "getinfo", + # "getlog", + # "getroute", + # "getsharedsecret", + # "help", + # "invoice", + # "keysend", + # "listchannels", + # "listconfigs", + # "listdatastore", + # "listforwards", + # "listfunds", + # "listinvoices", + # "listnodes", + # "listoffers", + # "listpays", + # "listpeers", + # "listsendpays", + # "listtransactions", + # "multifundchannel", + # "multiwithdraw", + # "newaddr", + # "notifications", + # "offerout", + # "offer", + # "openchannel_abort", + # "openchannel_bump", + # "openchannel_init", + # "openchannel_signed", + # "openchannel_update", + # "parsefeerate", + # "pay", + # "ping", + # "plugin", + # "reserveinputs", + # "sendcustommsg", + # "sendinvoice", + # "sendonionmessage", + # "sendonion", + # "sendpay", + # "sendpsbt", + # "setchannelfee", + # "signmessage", + # "signpsbt", + # "stop", + # "txdiscard", + # "txprepare", + # "txsend", + # "unreserveinputs", + # "utxopsbt", + # "waitanyinvoice", + # "waitblockheight", + # "waitinvoice", + # "waitsendpay", + # "withdraw", + ] + methods = [load_jsonrpc_method(name) for name in method_names] + service = Service(name="Node", methods=methods) + service.includes = ['primitives.proto'] # Make sure we have the primitives included. + return service + + +def genrustjsonrpc(service): + fname = repo_root() / "cln-rpc" / "src" / "model.rs" + dest = open(fname, "w") + RustGenerator(dest).generate(service) + + +def run(): + service = load_jsonrpc_service() + genrustjsonrpc(service) + + +if __name__ == "__main__": + run() diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py new file mode 100644 index 000000000000..a5e99cc3404a --- /dev/null +++ b/contrib/msggen/msggen/rust.py @@ -0,0 +1,243 @@ +from typing import TextIO +from typing import Tuple +from textwrap import dedent, indent +import logging +import sys + +from .model import (ArrayField, CompositeField, EnumField, + PrimitiveField, Service) + +logger = logging.getLogger(__name__) + +# The following words need to be changed, otherwise they'd clash with +# built-in keywords. +keywords = ["in", "type"] + +# Manual overrides for some of the auto-generated types for paths +# Manual overrides for some of the auto-generated types for paths +overrides = { + 'ListPeers.peers[].channels[].state_changes[].old_state': "ChannelState", + 'ListPeers.peers[].channels[].state_changes[].new_state': "ChannelState", + 'ListPeers.peers[].channels[].state_changes[].cause': "ChannelStateChangeCause", + 'ListPeers.peers[].channels[].opener': "ChannelSide", + 'ListPeers.peers[].channels[].closer': "ChannelSide", + 'ListPeers.peers[].channels[].features[]': "string", + 'ListFunds.channels[].state': 'ChannelState', +} + +# A map of schema type to rust primitive types. +typemap = { + 'boolean': 'bool', + 'hex': 'String', + 'msat': 'Amount', + 'number': 'i64', + 'pubkey': 'String', + 'short_channel_id': 'String', + 'signature': 'String', + 'string': 'String', + 'txid': 'String', +} + +header = f"""#![allow(non_camel_case_types)] +//! This file was automatically generated using the following command: +//! +//! ```bash +//! {" ".join(sys.argv)} +//! ``` +//! +//! Do not edit this file, it'll be overwritten. Rather edit the schema that +//! this file was generated from + +""" + + +def normalize_varname(field): + """Make sure that the variable name of this field is valid. + """ + # Dashes are not valid names + field.path = field.path.replace("-", "_") + return field + + +def gen_field(field): + if isinstance(field, CompositeField): + return gen_composite(field) + elif isinstance(field, EnumField): + return gen_enum(field) + elif isinstance(field, ArrayField): + return gen_array(field) + elif isinstance(field, PrimitiveField): + return gen_primitive(field) + else: + raise ValueError(f"Unmanaged type {field}") + + +def gen_enum(e): + defi, decl = "", "" + + if e.description != "": + decl += f"/// {e.description}\n" + + decl += f"#[derive(Copy, Clone, Debug, Deserialize, Serialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum {e.typename} {{\n" + for v in e.variants: + if v is None: + continue + norm = v.normalized() + # decl += f" #[serde(rename = \"{v}\")]\n" + decl += f" {norm},\n" + decl += "}\n\n" + + typename = e.typename + + if e.path in overrides: + decl = "" # No declaration if we have an override + typename = overrides[e.path] + + if e.required: + defi = f" // Path `{e.path}`\n #[serde(rename = \"{e.name}\")]\n pub {e.name.normalized()}: {typename},\n" + else: + defi = f' #[serde(skip_serializing_if = "Option::is_none")]' + defi = f" pub {e.name.normalized()}: Option<{typename}>,\n" + + return defi, decl + + +def gen_primitive(p): + defi, decl = "", "" + org = p.name.name + typename = typemap.get(p.typename, p.typename) + normalize_varname(p) + + if p.required: + defi = f" #[serde(alias = \"{org}\")]\n pub {p.name}: {typename},\n" + else: + defi = f" #[serde(alias = \"{org}\", skip_serializing_if = \"Option::is_none\")]\n pub {p.name}: Option<{typename}>,\n" + + return defi, decl + + +def gen_array(a): + name = a.name.normalized().replace("[]", "") + logger.debug(f"Generating array field {a.name} -> {name} ({a.path})") + + _, decl = gen_field(a.itemtype) + + if isinstance(a.itemtype, PrimitiveField): + itemtype = a.itemtype.typename + elif isinstance(a.itemtype, CompositeField): + itemtype = a.itemtype.typename + elif isinstance(a.itemtype, EnumField): + itemtype = a.itemtype.typename + + if a.path in overrides: + decl = "" # No declaration if we have an override + itemtype = overrides[a.path] + + itemtype = typemap.get(itemtype, itemtype) + alias = a.name.normalized()[:-2] # Strip the `[]` suffix for arrays. + defi = f" #[serde(alias = \"{alias}\")]\n pub {name}: {'Vec<'*a.dims}{itemtype}{'>'*a.dims},\n" + + return (defi, decl) + + +def gen_composite(c) -> Tuple[str, str]: + logger.debug(f"Generating composite field {c.name} ({c.path})") + fields = [] + for f in c.fields: + fields.append(gen_field(f)) + + r = "".join([f[1] for f in fields]) + + r += f"""#[derive(Clone, Debug, Deserialize, Serialize)]\npub struct {c.typename} {{\n""" + + r += "".join([f[0] for f in fields]) + + r += "}\n\n" + return ("", r) + + +class RustGenerator: + def __init__(self, dest: TextIO): + self.dest = dest + + def write(self, text: str, numindent: int = 0) -> None: + raw = dedent(text) + if numindent > 0: + raw = indent(text, "\t" * numindent) + self.dest.write(raw) + + def generate_requests(self, service: Service): + self.write("""\ + pub mod requests { + #[allow(unused_imports)] + use crate::primitives::*; + #[allow(unused_imports)] + use serde::{{Deserialize, Serialize}}; + + """) + + for meth in service.methods: + req = meth.request + _, decl = gen_composite(req) + self.write(decl, numindent=1) + + self.write("}\n\n") + + def generate_responses(self, service: Service): + self.write(""" + pub mod responses { + #[allow(unused_imports)] + use crate::primitives::*; + #[allow(unused_imports)] + use serde::{{Deserialize, Serialize}}; + + """) + + for meth in service.methods: + res = meth.response + _, decl = gen_composite(res) + self.write(decl, numindent=1) + + self.write("}\n\n") + + def generate_enums(self, service: Service): + """The Request and Response enums serve as parsing primitives. + """ + self.write(f"""\ + use serde::{{Deserialize, Serialize}}; + pub use requests::*; + pub use responses::*; + + #[derive(Clone, Debug, Serialize, Deserialize)] + #[serde(tag = "method", content = "params")] + #[serde(rename_all = "lowercase")] + pub enum Request {{ + """) + + for method in service.methods: + self.write(f"{method.name}(requests::{method.request.typename}),\n", numindent=1) + + self.write(f"""\ + }} + + #[derive(Clone, Debug, Serialize, Deserialize)] + #[serde(tag = "method", content = "result")] + #[serde(rename_all = "lowercase")] + pub enum Response {{ + """) + + for method in service.methods: + self.write(f"{method.name}(responses::{method.response.typename}),\n", numindent=1) + + self.write(f"""\ + }} + + """) + + def generate(self, service: Service) -> None: + self.write(header) + + self.generate_enums(service) + + self.generate_requests(service) + self.generate_responses(service) From 3c32f7d8f6c5e605591be02171398286c10a6d8e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 14:03:19 +0100 Subject: [PATCH 0304/1530] json-rpc: Add request stubs for a couple of calls These are required so we can generate the requests, not just the responses. We'll add more as we test compatibility with our generation code. --- doc/schemas/addgossip.request.json | 14 ++++++++ doc/schemas/autocleaninvoice.request.json | 16 +++++++++ doc/schemas/checkmessage.request.json | 23 +++++++++++++ doc/schemas/close.request.json | 40 +++++++++++++++++++++++ doc/schemas/getinfo.request.json | 7 ++++ doc/schemas/listchannels.request.json | 20 ++++++++++++ doc/schemas/listfunds.request.json | 12 +++++++ doc/schemas/listpeers.request.json | 16 +++++++++ 8 files changed, 148 insertions(+) create mode 100644 doc/schemas/addgossip.request.json create mode 100644 doc/schemas/autocleaninvoice.request.json create mode 100644 doc/schemas/checkmessage.request.json create mode 100644 doc/schemas/close.request.json create mode 100644 doc/schemas/getinfo.request.json create mode 100644 doc/schemas/listchannels.request.json create mode 100644 doc/schemas/listfunds.request.json create mode 100644 doc/schemas/listpeers.request.json diff --git a/doc/schemas/addgossip.request.json b/doc/schemas/addgossip.request.json new file mode 100644 index 000000000000..5729e2fa2e96 --- /dev/null +++ b/doc/schemas/addgossip.request.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "message" + ], + "properties": { + "message": { + "type": "hex", + "description": "The raw, hex-encoded, gossip message to add to the local gossip view." + } + } +} diff --git a/doc/schemas/autocleaninvoice.request.json b/doc/schemas/autocleaninvoice.request.json new file mode 100644 index 000000000000..229363d504ee --- /dev/null +++ b/doc/schemas/autocleaninvoice.request.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": { + "expired_by": { + "type": "u64", + "description": "How long an invoice must be expired (seconds) before we delete it." + }, + "cycle_seconds": { + "type": "u64", + "description": "The interval (in seconds) between cleaning expired invoices" + } + } +} diff --git a/doc/schemas/checkmessage.request.json b/doc/schemas/checkmessage.request.json new file mode 100644 index 000000000000..df896bbf43ef --- /dev/null +++ b/doc/schemas/checkmessage.request.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "message", + "zbase" + ], + "properties": { + "message": { + "type": "string", + "description": "Message to be checked against the signature." + }, + "zbase": { + "type": "string", + "description": "The Zbase32 encoded signature to verify." + }, + "pubkey": { + "type": "pubkey", + "description": "The Zbase32 encoded signature to verify." + } + } +} diff --git a/doc/schemas/close.request.json b/doc/schemas/close.request.json new file mode 100644 index 000000000000..c91f41a13ac4 --- /dev/null +++ b/doc/schemas/close.request.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "" + }, + "unilateraltimeout": { + "type": "u32", + "description": "" + }, + "destination": { + "type": "string", + "description": "" + }, + "fee_negotiation_step": { + "type": "string", + "description": "" + }, + "wrong_funding": { + "type": "txid", + "description": "" + }, + "force_lease_closed": { + "type": "boolean", + "description": "" + }, + "feerange": { + "type": { + "type": "array", + "items": "u32" + }, + "description": "" + } + } +} diff --git a/doc/schemas/getinfo.request.json b/doc/schemas/getinfo.request.json new file mode 100644 index 000000000000..f99496c5ac84 --- /dev/null +++ b/doc/schemas/getinfo.request.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": {} +} diff --git a/doc/schemas/listchannels.request.json b/doc/schemas/listchannels.request.json new file mode 100644 index 000000000000..80c7344900ee --- /dev/null +++ b/doc/schemas/listchannels.request.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": { + "short_channel_id": { + "type": "short_channel_id", + "description": "If short_channel_id is a short channel id, then only known channels with a matching short_channel_id are returned. Otherwise, it must be null." + }, + "source": { + "type": "pubkey", + "description": "If source is a node id, then only channels leading from that node id are returned." + }, + "destination": { + "type": "pubkey", + "description": "If destination is a node id, then only channels leading to that node id are returned." + } + } +} diff --git a/doc/schemas/listfunds.request.json b/doc/schemas/listfunds.request.json new file mode 100644 index 000000000000..00faf6c658e8 --- /dev/null +++ b/doc/schemas/listfunds.request.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": { + "spent": { + "type": "boolean", + "description": "Should outputs that are already spent be included in the result?" + } + } +} diff --git a/doc/schemas/listpeers.request.json b/doc/schemas/listpeers.request.json new file mode 100644 index 000000000000..f2b02304acd3 --- /dev/null +++ b/doc/schemas/listpeers.request.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": { + "id": { + "type": "pubkey", + "description": "If supplied, limits the result to just the peer with the given ID, if it exists." + }, + "level": { + "type": "string", + "description": "Supplying level will show log entries related to that peer at the given log level. Valid log levels are “io”, “debug”, “info”, and “unusual”." + } + } +} From 7fdad0a60cf5417628f3ba48670df82aaacba212 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 15:34:41 +0100 Subject: [PATCH 0305/1530] rust: Add rust detection to configure and a target to add binaries We detect whether we have the rust tooling available (mainly `cargo`) and enable or disable the rust libraries, plugins and examples when it is enabled. Since the rest of the Makefiles assumes that executables have an associated header and C source file, we also needed to add a target that we can add non-C binaries to. --- .gitignore | 4 ++++ Makefile | 16 +++++++++++++++- configure | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 99d8703b2af8..fa8e86ef94c5 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,7 @@ doc/lightning*.[1578] # Ignore unrelated stuff .DS_Store .gdb_history + +# Rust targets +target +Cargo.lock \ No newline at end of file diff --git a/Makefile b/Makefile index fe17cd563443..5f88fe01c8d9 100644 --- a/Makefile +++ b/Makefile @@ -230,6 +230,8 @@ ALL_TEST_PROGRAMS := ALL_FUZZ_TARGETS := ALL_C_SOURCES := ALL_C_HEADERS := header_versions_gen.h version_gen.h +# Extra (non C) targets that should be built by default. +DEFAULT_TARGETS := CPPFLAGS += -DBINTOPKGLIBEXECDIR="\"$(shell sh tools/rel.sh $(bindir) $(pkglibexecdir))\"" CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) $(COPTFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I/usr/local/include $(SQLITE3_CFLAGS) $(POSTGRES_INCLUDE) $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) -DBUILD_ELEMENTS=1 @@ -260,7 +262,7 @@ ifeq ($(HAVE_POSTGRES),1) LDLIBS += $(POSTGRES_LDLIBS) endif -default: show-flags all-programs all-test-programs doc-all +default: show-flags all-programs all-test-programs doc-all default-targets ifneq ($(SUPPRESS_GENERATION),1) FORCE = FORCE @@ -323,6 +325,13 @@ endif $(call VERBOSE,"printgen $@",tools/generate-wire.py -s -P --page impl $($@_args) ${@:.c=.h} `basename $< .csv | sed 's/_exp_/_/'` < $< > $@ && $(call SHA256STAMP,//,)); \ fi +RUST_PROFILE ?= debug +ifneq ($(RUST_PROFILE),debug) +CARGO_OPTS := --profile=$(RUST_PROFILE) --quiet +else +CARGO_OPTS := --quiet +endif + include external/Makefile include bitcoin/Makefile include common/Makefile @@ -345,6 +354,9 @@ include contrib/libhsmd_python/Makefile ifneq ($(FUZZING),0) include tests/fuzz/Makefile endif +ifneq ($(RUST),0) +# Add Rust Makefiles here +endif # We make pretty much everything depend on these. ALL_GEN_HEADERS := $(filter %gen.h,$(ALL_C_HEADERS)) @@ -607,6 +619,7 @@ update-ccan: # Now ALL_PROGRAMS is fully populated, we can expand it. all-programs: $(ALL_PROGRAMS) all-test-programs: $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS) +default-targets: $(DEFAULT_TARGETS) distclean: clean $(RM) ccan/config.h config.vars @@ -630,6 +643,7 @@ clean: obsclean find . -name '*gcda' -delete find . -name '*gcno' -delete find . -name '*.nccout' -delete + if [ "${RUST}" -eq "1" ]; then cargo clean; fi # These must both be enabled for update-mocks ifeq ($(DEVELOPER)$(EXPERIMENTAL_FEATURES),11) diff --git a/configure b/configure index 94ac97236e40..57bca469c15d 100755 --- a/configure +++ b/configure @@ -109,6 +109,15 @@ default_valgrind_setting() fi } +default_rust_setting() +{ + if cargoa --version > /dev/null 2>&1; then + echo 1 + else + echo 0 + fi +} + set_defaults() { # Default values, loaded from environment or canned. @@ -129,6 +138,7 @@ set_defaults() VALGRIND=${VALGRIND:-$(default_valgrind_setting)} TEST_NETWORK=${TEST_NETWORK:-regtest} FUZZING=${FUZZING:-0} + RUST=${RUST:-$(default_rust_setting)} } usage() @@ -167,6 +177,8 @@ usage() usage_with_default "--enable/disable-ub-sanitizer" "$UBSAN" "enable" "disable" echo " Compile with undefined behaviour sanitizer" usage_with_default "--enable/disable-fuzzing" "$FUZZING" "enable" "disable" + echo " Compile with Rust support" + usage_with_default "--enable/disable-rust" "$RUST" "enable" "disable" exit 1 } @@ -222,6 +234,8 @@ for opt in "$@"; do --disable-ub-sanitize) UBSAN=0;; --enable-fuzzing) FUZZING=1;; --disable-fuzzing) FUZZING=0;; + --enable-rust) RUST=1;; + --disable-rust) RUST=0;; --help|-h) usage;; *) echo "Unknown option '$opt'" >&2 @@ -430,6 +444,7 @@ add_var TEST_NETWORK "$TEST_NETWORK" add_var HAVE_PYTHON3_MAKO "$HAVE_PYTHON3_MAKO" add_var SHA256SUM "$SHA256SUM" add_var FUZZING "$FUZZING" +add_var RUST "$RUST" # Hack to avoid sha256 name clash with libwally: will be fixed when that # becomes a standalone shared lib. From faa383517767d311d3ece8e29630863b8953b3a7 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 16:11:28 +0100 Subject: [PATCH 0306/1530] cln-rpc: Scaffolding for the cln-rpc crate Changelog-Added: cln-rpc: A new Rust library called `cln-rpc` can be used to interact with the JSON-RPC --- Cargo.toml | 4 + Makefile | 2 +- cln-rpc/Cargo.toml | 19 ++ cln-rpc/Makefile | 16 ++ cln-rpc/README.md | 3 + cln-rpc/src/codec.rs | 210 ++++++++++++++++++++++ cln-rpc/src/jsonrpc.rs | 88 +++++++++ cln-rpc/src/lib.rs | 108 +++++++++++ cln-rpc/src/model.rs | 338 +++++++++++++++++++++++++++++++++++ cln-rpc/src/notifications.rs | 4 + cln-rpc/src/primitives.rs | 142 +++++++++++++++ 11 files changed, 933 insertions(+), 1 deletion(-) create mode 100644 Cargo.toml create mode 100644 cln-rpc/Cargo.toml create mode 100644 cln-rpc/Makefile create mode 100644 cln-rpc/README.md create mode 100644 cln-rpc/src/codec.rs create mode 100644 cln-rpc/src/jsonrpc.rs create mode 100644 cln-rpc/src/lib.rs create mode 100644 cln-rpc/src/model.rs create mode 100644 cln-rpc/src/notifications.rs create mode 100644 cln-rpc/src/primitives.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000000..e670e44ec858 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,4 @@ +[workspace] +members = [ + "cln-rpc", +] diff --git a/Makefile b/Makefile index 5f88fe01c8d9..618cc2d67f68 100644 --- a/Makefile +++ b/Makefile @@ -355,7 +355,7 @@ ifneq ($(FUZZING),0) include tests/fuzz/Makefile endif ifneq ($(RUST),0) -# Add Rust Makefiles here + include cln-rpc/Makefile endif # We make pretty much everything depend on these. diff --git a/cln-rpc/Cargo.toml b/cln-rpc/Cargo.toml new file mode 100644 index 000000000000..4145d20a96b9 --- /dev/null +++ b/cln-rpc/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "cln-rpc" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.51" +bytes = "1.1.0" +log = "0.4.14" +serde = { version = "1.0.131", features = ["derive"] } +serde_json = "1.0.72" +tokio-util = { version = "0.6.9", features = ["codec"] } +tokio = { version = "1", features = ["net"]} +native-tls = { version = "*", features = ["vendored"] } +futures-util = { version = "*", features = [ "sink" ] } + +[dev-dependencies] +tokio = { version = "1", features = ["net", "macros", "rt-multi-thread"]} +env_logger = "*" diff --git a/cln-rpc/Makefile b/cln-rpc/Makefile new file mode 100644 index 000000000000..a1812982c09a --- /dev/null +++ b/cln-rpc/Makefile @@ -0,0 +1,16 @@ +cln-rpc-wrongdir: + $(MAKE) -C .. cln-rpc-all + +CLN_RPC_EXAMPLES := +CLN_RPC_GENALL = cln-rpc/src/model.rs +CLN_RPC_SOURCES = $(shell find cln-rpc -name *.rs) ${CLN_RPC_GENALL} +JSON_SCHEMA = doc/schemas/*.schema.json +DEFAULT_TARGETS += $(CLN_RPC_EXAMPLES) $(CLN_RPC_GENALL) + +$(CLN_RPC_GENALL): $(JSON_SCHEMA) + PYTHONPATH=contrib/msggen python3 contrib/msggen/msggen/__main__.py + +target/debug/examples/cln-rpc-getinfo: $(shell find cln-rpc -name *.rs) + cargo build --example cln-rpc-getinfo + +cln-rpc-all: ${CLN_RPC_GEN_ALL} ${CLN_RPC_EXAMPLES} diff --git a/cln-rpc/README.md b/cln-rpc/README.md new file mode 100644 index 000000000000..d637e040cf37 --- /dev/null +++ b/cln-rpc/README.md @@ -0,0 +1,3 @@ +# `cln-rpc`: Talk to c-lightning + + diff --git a/cln-rpc/src/codec.rs b/cln-rpc/src/codec.rs new file mode 100644 index 000000000000..1e8adbbf353a --- /dev/null +++ b/cln-rpc/src/codec.rs @@ -0,0 +1,210 @@ +//! The codec is used to encode and decode messages received from and +//! sent to the main daemon. The protocol uses `stdout` and `stdin` to +//! exchange JSON formatted messages. Each message is separated by an +//! empty line and we're guaranteed that no other empty line is +//! present in the messages. +use crate::Error; +use anyhow::anyhow; +use bytes::{BufMut, BytesMut}; +use serde_json::value::Value; +use std::str::FromStr; +use std::{io, str}; +use tokio_util::codec::{Decoder, Encoder}; + +pub use crate::jsonrpc::JsonRpc; +use crate::{ + model::{Request}, + notifications::Notification, +}; + +/// A simple codec that parses messages separated by two successive +/// `\n` newlines. +#[derive(Default)] +pub struct MultiLineCodec {} + +/// Find two consecutive newlines, i.e., an empty line, signalling the +/// end of one message and the start of the next message. +fn find_separator(buf: &mut BytesMut) -> Option { + buf.iter() + .zip(buf.iter().skip(1)) + .position(|b| *b.0 == b'\n' && *b.1 == b'\n') +} + +fn utf8(buf: &[u8]) -> Result<&str, io::Error> { + str::from_utf8(buf) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Unable to decode input as UTF8")) +} + +impl Decoder for MultiLineCodec { + type Item = String; + type Error = Error; + fn decode(&mut self, buf: &mut BytesMut) -> Result, Error> { + if let Some(newline_offset) = find_separator(buf) { + let line = buf.split_to(newline_offset + 2); + let line = &line[..line.len() - 2]; + let line = utf8(line)?; + Ok(Some(line.to_string())) + } else { + Ok(None) + } + } +} + +impl Encoder for MultiLineCodec +where + T: AsRef, +{ + type Error = Error; + fn encode(&mut self, line: T, buf: &mut BytesMut) -> Result<(), Self::Error> { + let line = line.as_ref(); + buf.reserve(line.len() + 2); + buf.put(line.as_bytes()); + buf.put_u8(b'\n'); + buf.put_u8(b'\n'); + Ok(()) + } +} + +#[derive(Default)] +pub struct JsonCodec { + /// Sub-codec used to split the input into chunks that can then be + /// parsed by the JSON parser. + inner: MultiLineCodec, +} + +impl Encoder for JsonCodec +where + T: Into, +{ + type Error = Error; + fn encode(&mut self, msg: T, buf: &mut BytesMut) -> Result<(), Self::Error> { + let s = msg.into().to_string(); + self.inner.encode(s, buf) + } +} + +impl Decoder for JsonCodec { + type Item = Value; + type Error = Error; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, Error> { + match self.inner.decode(buf) { + Ok(None) => Ok(None), + Err(e) => Err(e), + Ok(Some(s)) => { + if let Ok(v) = Value::from_str(&s) { + Ok(Some(v)) + } else { + Err(anyhow!("failed to parse JSON")) + } + } + } + } +} + +/// A codec that reads fully formed [crate::messages::JsonRpc] +/// messages. Internally it uses the [JsonCodec] which itself is built +/// on the [MultiLineCodec]. +#[derive(Default)] +pub(crate) struct JsonRpcCodec { + inner: JsonCodec, +} + +impl Decoder for JsonRpcCodec { + type Item = JsonRpc; + type Error = Error; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, Error> { + match self.inner.decode(buf) { + Ok(None) => Ok(None), + Err(e) => Err(e), + Ok(Some(s)) => { + let req: Self::Item = serde_json::from_value(s)?; + Ok(Some(req)) + } + } + } +} + +#[cfg(test)] +mod test { + use super::{find_separator, JsonCodec, MultiLineCodec}; + use bytes::{BufMut, BytesMut}; + use serde_json::json; + use tokio_util::codec::{Decoder, Encoder}; + + #[test] + fn test_separator() { + struct Test(String, Option); + let tests = vec![ + Test("".to_string(), None), + Test("}\n\n".to_string(), Some(1)), + Test("\"hello\"},\n\"world\"}\n\n".to_string(), Some(18)), + ]; + + for t in tests.iter() { + let mut buf = BytesMut::new(); + buf.put_slice(t.0.as_bytes()); + assert_eq!(find_separator(&mut buf), t.1); + } + } + + #[test] + fn test_ml_decoder() { + struct Test(String, Option, String); + let tests = vec![ + Test("".to_string(), None, "".to_string()), + Test( + "{\"hello\":\"world\"}\n\nremainder".to_string(), + Some("{\"hello\":\"world\"}".to_string()), + "remainder".to_string(), + ), + Test( + "{\"hello\":\"world\"}\n\n{}\n\nremainder".to_string(), + Some("{\"hello\":\"world\"}".to_string()), + "{}\n\nremainder".to_string(), + ), + ]; + + for t in tests.iter() { + let mut buf = BytesMut::new(); + buf.put_slice(t.0.as_bytes()); + + let mut codec = MultiLineCodec::default(); + let mut remainder = BytesMut::new(); + remainder.put_slice(t.2.as_bytes()); + + assert_eq!(codec.decode(&mut buf).unwrap(), t.1); + assert_eq!(buf, remainder); + } + } + + #[test] + fn test_ml_encoder() { + let tests = vec!["test"]; + + for t in tests.iter() { + let mut buf = BytesMut::new(); + let mut codec = MultiLineCodec::default(); + let mut expected = BytesMut::new(); + expected.put_slice(t.as_bytes()); + expected.put_u8(b'\n'); + expected.put_u8(b'\n'); + codec.encode(t, &mut buf).unwrap(); + assert_eq!(buf, expected); + } + } + + #[test] + fn test_json_codec() { + let tests = vec![json!({"hello": "world"})]; + + for t in tests.iter() { + let mut codec = JsonCodec::default(); + let mut buf = BytesMut::new(); + codec.encode(t.clone(), &mut buf).unwrap(); + let decoded = codec.decode(&mut buf).unwrap().unwrap(); + assert_eq!(&decoded, t); + } + } +} diff --git a/cln-rpc/src/jsonrpc.rs b/cln-rpc/src/jsonrpc.rs new file mode 100644 index 000000000000..99a5469b13ac --- /dev/null +++ b/cln-rpc/src/jsonrpc.rs @@ -0,0 +1,88 @@ +//! Common structs to handle JSON-RPC decoding and encoding. They are +//! generic over the Notification and Request types. + +use serde::ser::{SerializeStruct, Serializer}; +use serde::de::{self, Deserializer}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fmt::Debug; + +#[derive(Debug)] +pub enum JsonRpc { + Request(usize, R), + Notification(N), +} + +/// This function disentangles the various cases: +/// +/// 1) If we have an `id` then it is a request +/// +/// 2) Otherwise it's a notification that doesn't require a +/// response. +/// +/// Furthermore we distinguish between the built-in types and the +/// custom user notifications/methods: +/// +/// 1) We either match a built-in type above, +/// +/// 2) Or it's a custom one, so we pass it around just as a +/// `serde_json::Value` +impl<'de, N, R> Deserialize<'de> for JsonRpc +where + N: Deserialize<'de> + Debug, + R: Deserialize<'de> + Debug, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize, Debug)] + struct IdHelper { + id: Option, + } + + let v = Value::deserialize(deserializer)?; + let helper = IdHelper::deserialize(&v).map_err(de::Error::custom)?; + match helper.id { + Some(id) => { + let r = R::deserialize(v).map_err(de::Error::custom)?; + Ok(JsonRpc::Request(id, r)) + } + None => { + let n = N::deserialize(v).map_err(de::Error::custom)?; + Ok(JsonRpc::Notification(n)) + } + } + } +} + +impl Serialize for JsonRpc +where + N: Serialize + Debug, + R: Serialize + Debug, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + JsonRpc::Notification(r) => { + let r = serde_json::to_value(r).unwrap(); + let mut s = serializer.serialize_struct("Notification", 3)?; + s.serialize_field("jsonrpc", "2.0")?; + s.serialize_field("method", &r["method"])?; + s.serialize_field("params", &r["params"])?; + s.end() + } + JsonRpc::Request(id, r) => { + let r = serde_json::to_value(r).unwrap(); + let mut s = serializer.serialize_struct("Request", 4)?; + s.serialize_field("jsonrpc", "2.0")?; + s.serialize_field("id", id)?; + s.serialize_field("method", &r["method"])?; + s.serialize_field("params", &r["params"])?; + s.end() + } + } + } +} diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs new file mode 100644 index 000000000000..0197d4b3993f --- /dev/null +++ b/cln-rpc/src/lib.rs @@ -0,0 +1,108 @@ +use crate::codec::JsonCodec; +use crate::codec::JsonRpc; +use anyhow::{Context, Error, Result}; +use futures_util::sink::SinkExt; +use futures_util::StreamExt; +use log::{debug, trace}; +use std::path::Path; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; +use tokio::net::unix::{OwnedReadHalf, OwnedWriteHalf}; +use tokio::net::UnixStream; +use tokio_util::codec::{FramedRead, FramedWrite}; + +pub mod codec; +pub mod jsonrpc; +pub mod model; +pub mod notifications; +pub mod primitives; + +pub use crate::{ + model::{Request, Response}, + notifications::Notification, +}; + +/// +pub struct ClnRpc { + next_id: AtomicUsize, + + #[allow(dead_code)] + read: FramedRead, + write: FramedWrite, +} + +impl ClnRpc { + pub async fn new

(path: P) -> Result + where + P: AsRef, + { + debug!( + "Connecting to socket at {}", + path.as_ref().to_string_lossy() + ); + ClnRpc::from_stream(UnixStream::connect(path).await?) + } + + fn from_stream(stream: UnixStream) -> Result { + let (read, write) = stream.into_split(); + + Ok(ClnRpc { + next_id: AtomicUsize::new(1), + read: FramedRead::new(read, JsonCodec::default()), + write: FramedWrite::new(write, JsonCodec::default()), + }) + } + + pub async fn call(&mut self, req: Request) -> Result { + trace!("Sending request {:?}", req); + + // Wrap the raw request in a well-formed JSON-RPC outer dict. + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + let req: JsonRpc = JsonRpc::Request(id, req); + let req = serde_json::to_value(req)?; + let req2 = req.clone(); + self.write.send(req).await?; + + let mut response = self + .read + .next() + .await + .context("no response from lightningd")? + .context("reading response from socket")?; + trace!("Read response {:?}", response); + + // Annotate the response with the method from the request, so + // serde_json knows which variant of [`Request`] should be + // used. + response["method"] = req2["method"].clone(); + + serde_json::from_value(response).context("converting response into enum") + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::model::*; + use futures_util::StreamExt; + use serde_json::json; + + #[tokio::test] + async fn test_call() { + let req = Request::Getinfo(requests::GetinfoRequest {}); + let (uds1, uds2) = UnixStream::pair().unwrap(); + let mut cln = ClnRpc::from_stream(uds1).unwrap(); + + let mut read = FramedRead::new(uds2, JsonCodec::default()); + tokio::task::spawn(async move { + cln.call(req).await.unwrap(); + }); + + let read_req = dbg!(read.next().await.unwrap().unwrap()); + + assert_eq!( + json!({"id": 1, "method": "getinfo", "params": {}, "jsonrpc": "2.0"}), + read_req + ); + } +} diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs new file mode 100644 index 000000000000..ebc697023bfd --- /dev/null +++ b/cln-rpc/src/model.rs @@ -0,0 +1,338 @@ +#![allow(non_camel_case_types)] +//! This file was automatically generated using the following command: +//! +//! ```bash +//! msggen +//! ``` +//! +//! Do not edit this file, it'll be overwritten. Rather edit the schema that +//! this file was generated from + +use serde::{Deserialize, Serialize}; +pub use requests::*; +pub use responses::*; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "method", content = "params")] +#[serde(rename_all = "lowercase")] +pub enum Request { + Getinfo(requests::GetinfoRequest), + ListFunds(requests::ListfundsRequest), + ListChannels(requests::ListchannelsRequest), + AddGossip(requests::AddgossipRequest), + AutoCleanInvoice(requests::AutocleaninvoiceRequest), + CheckMessage(requests::CheckmessageRequest), + Close(requests::CloseRequest), +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "method", content = "result")] +#[serde(rename_all = "lowercase")] +pub enum Response { + Getinfo(responses::GetinfoResponse), + ListFunds(responses::ListfundsResponse), + ListChannels(responses::ListchannelsResponse), + AddGossip(responses::AddgossipResponse), + AutoCleanInvoice(responses::AutocleaninvoiceResponse), + CheckMessage(responses::CheckmessageResponse), + Close(responses::CloseResponse), +} + +pub mod requests { + #[allow(unused_imports)] + use crate::primitives::*; + #[allow(unused_imports)] + use serde::{{Deserialize, Serialize}}; + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetinfoRequest { + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListfundsRequest { + #[serde(alias = "spent", skip_serializing_if = "Option::is_none")] + pub spent: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListchannelsRequest { + #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] + pub short_channel_id: Option, + #[serde(alias = "source", skip_serializing_if = "Option::is_none")] + pub source: Option, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AddgossipRequest { + #[serde(alias = "message")] + pub message: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AutocleaninvoiceRequest { + #[serde(alias = "expired_by", skip_serializing_if = "Option::is_none")] + pub expired_by: Option, + #[serde(alias = "cycle_seconds", skip_serializing_if = "Option::is_none")] + pub cycle_seconds: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CheckmessageRequest { + #[serde(alias = "message")] + pub message: String, + #[serde(alias = "zbase")] + pub zbase: String, + #[serde(alias = "pubkey", skip_serializing_if = "Option::is_none")] + pub pubkey: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CloseRequest { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "unilateraltimeout", skip_serializing_if = "Option::is_none")] + pub unilateraltimeout: Option, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "fee_negotiation_step", skip_serializing_if = "Option::is_none")] + pub fee_negotiation_step: Option, + #[serde(alias = "wrong_funding", skip_serializing_if = "Option::is_none")] + pub wrong_funding: Option, + #[serde(alias = "force_lease_closed", skip_serializing_if = "Option::is_none")] + pub force_lease_closed: Option, + } + +} + + +pub mod responses { + #[allow(unused_imports)] + use crate::primitives::*; + #[allow(unused_imports)] + use serde::{{Deserialize, Serialize}}; + + /// Type of connection + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum GetinfoAddressType { + DNS, + IPV4, + IPV6, + TORV2, + TORV3, + WEBSOCKET, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetinfoAddress { + // Path `Getinfo.address[].type` + #[serde(rename = "type")] + pub item_type: GetinfoAddressType, + #[serde(alias = "port")] + pub port: u16, + #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + pub address: Option, + } + + /// Type of connection + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum GetinfoBindingType { + LOCAL_SOCKET, + IPV4, + IPV6, + TORV2, + TORV3, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetinfoBinding { + // Path `Getinfo.binding[].type` + #[serde(rename = "type")] + pub item_type: GetinfoBindingType, + #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + pub address: Option, + #[serde(alias = "port", skip_serializing_if = "Option::is_none")] + pub port: Option, + #[serde(alias = "socket", skip_serializing_if = "Option::is_none")] + pub socket: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetinfoResponse { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "alias")] + pub alias: String, + #[serde(alias = "color")] + pub color: String, + #[serde(alias = "num_peers")] + pub num_peers: u32, + #[serde(alias = "num_pending_channels")] + pub num_pending_channels: u32, + #[serde(alias = "num_active_channels")] + pub num_active_channels: u32, + #[serde(alias = "num_inactive_channels")] + pub num_inactive_channels: u32, + #[serde(alias = "version")] + pub version: String, + #[serde(alias = "lightning-dir")] + pub lightning_dir: String, + #[serde(alias = "blockheight")] + pub blockheight: u32, + #[serde(alias = "network")] + pub network: String, + #[serde(alias = "fees_collected_msat")] + pub fees_collected_msat: Amount, + #[serde(alias = "address")] + pub address: Vec, + #[serde(alias = "binding")] + pub binding: Vec, + #[serde(alias = "warning_bitcoind_sync", skip_serializing_if = "Option::is_none")] + pub warning_bitcoind_sync: Option, + #[serde(alias = "warning_lightningd_sync", skip_serializing_if = "Option::is_none")] + pub warning_lightningd_sync: Option, + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListfundsOutputsStatus { + UNCONFIRMED, + CONFIRMED, + SPENT, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListfundsOutputs { + #[serde(alias = "txid")] + pub txid: String, + #[serde(alias = "output")] + pub output: u32, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "scriptpubkey")] + pub scriptpubkey: String, + #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + pub address: Option, + #[serde(alias = "redeemscript", skip_serializing_if = "Option::is_none")] + pub redeemscript: Option, + // Path `ListFunds.outputs[].status` + #[serde(rename = "status")] + pub status: ListfundsOutputsStatus, + #[serde(alias = "blockheight", skip_serializing_if = "Option::is_none")] + pub blockheight: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListfundsChannels { + #[serde(alias = "peer_id")] + pub peer_id: String, + #[serde(alias = "our_amount_msat")] + pub our_amount_msat: Amount, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "funding_txid")] + pub funding_txid: String, + #[serde(alias = "funding_output")] + pub funding_output: u32, + #[serde(alias = "connected")] + pub connected: bool, + // Path `ListFunds.channels[].state` + #[serde(rename = "state")] + pub state: ChannelState, + #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] + pub short_channel_id: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListfundsResponse { + #[serde(alias = "outputs")] + pub outputs: Vec, + #[serde(alias = "channels")] + pub channels: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListchannelsChannels { + #[serde(alias = "source")] + pub source: String, + #[serde(alias = "destination")] + pub destination: String, + #[serde(alias = "public")] + pub public: bool, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "message_flags")] + pub message_flags: u8, + #[serde(alias = "channel_flags")] + pub channel_flags: u8, + #[serde(alias = "active")] + pub active: bool, + #[serde(alias = "last_update")] + pub last_update: u32, + #[serde(alias = "base_fee_millisatoshi")] + pub base_fee_millisatoshi: u32, + #[serde(alias = "fee_per_millionth")] + pub fee_per_millionth: u32, + #[serde(alias = "delay")] + pub delay: u32, + #[serde(alias = "htlc_minimum_msat")] + pub htlc_minimum_msat: Amount, + #[serde(alias = "htlc_maximum_msat", skip_serializing_if = "Option::is_none")] + pub htlc_maximum_msat: Option, + #[serde(alias = "features")] + pub features: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListchannelsResponse { + #[serde(alias = "channels")] + pub channels: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AddgossipResponse { + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct AutocleaninvoiceResponse { + #[serde(alias = "enabled")] + pub enabled: bool, + #[serde(alias = "expired_by", skip_serializing_if = "Option::is_none")] + pub expired_by: Option, + #[serde(alias = "cycle_seconds", skip_serializing_if = "Option::is_none")] + pub cycle_seconds: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CheckmessageResponse { + #[serde(alias = "verified")] + pub verified: bool, + #[serde(alias = "pubkey", skip_serializing_if = "Option::is_none")] + pub pubkey: Option, + } + + /// Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum CloseType { + MUTUAL, + UNILATERAL, + UNOPENED, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CloseResponse { + // Path `Close.type` + #[serde(rename = "type")] + pub item_type: CloseType, + #[serde(alias = "tx", skip_serializing_if = "Option::is_none")] + pub tx: Option, + #[serde(alias = "txid", skip_serializing_if = "Option::is_none")] + pub txid: Option, + } + +} + diff --git a/cln-rpc/src/notifications.rs b/cln-rpc/src/notifications.rs new file mode 100644 index 000000000000..4256350f9b47 --- /dev/null +++ b/cln-rpc/src/notifications.rs @@ -0,0 +1,4 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize)] +pub enum Notification {} diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs new file mode 100644 index 000000000000..0c1faa31ac7e --- /dev/null +++ b/cln-rpc/src/primitives.rs @@ -0,0 +1,142 @@ +use anyhow::{anyhow, Error, Result}; +use serde::{Deserialize, Serialize}; +use serde::{Deserializer, Serializer}; +#[derive(Copy, Clone, Serialize, Deserialize, Debug)] +#[allow(non_camel_case_types)] +pub enum ChannelState { + OPENINGD, + CHANNELD_AWAITING_LOCKIN, + CHANNELD_NORMAL, + CHANNELD_SHUTTING_DOWN, + CLOSINGD_SIGEXCHANGE, + CLOSINGD_COMPLETE, + AWAITING_UNILATERAL, + FUNDING_SPEND_SEEN, + ONCHAIN, + DUALOPEND_OPEN_INIT, + DUALOPEND_AWAITING_LOCKIN, +} + +#[derive(Copy, Clone, Serialize, Deserialize, Debug)] +#[allow(non_camel_case_types)] +pub enum ChannelStateChangeCause { + UNKNOWN, + LOCAL, + USER, + REMOTE, + PROTOCOL, + ONCHAIN, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Amount { + msat: u64, +} + +impl Amount { + pub fn from_msat(msat: u64) -> Amount { + Amount { msat: msat } + } + pub fn from_sat(sat: u64) -> Amount { + Amount { msat: 1_000 * sat } + } + pub fn from_btc(btc: u64) -> Amount { + Amount { + msat: 100_000_000_000 * btc, + } + } +} + +#[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)] +pub enum ChannelSide { + LOCAL, + REMOTE, +} + +impl<'de> Deserialize<'de> for Amount { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let s: String = Deserialize::deserialize(deserializer)?; + let ss: &str = &s; + ss.try_into() + .map_err(|_e| Error::custom("could not parse amount")) + } +} + +impl Serialize for Amount { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}msat", self.msat)) + } +} + +impl TryFrom<&str> for Amount { + type Error = Error; + fn try_from(s: &str) -> Result { + let number: u64 = s + .chars() + .map(|c| c.to_digit(10)) + .take_while(|opt| opt.is_some()) + .fold(0, |acc, digit| acc * 10 + (digit.unwrap() as u64)); + + let s = s.to_lowercase(); + if s.ends_with("msat") { + Ok(Amount::from_msat(number)) + } else if s.ends_with("sat") { + Ok(Amount::from_sat(number)) + } else if s.ends_with("btc") { + Ok(Amount::from_btc(number)) + } else { + Err(anyhow!("Unable to parse amount from string: {}", s)) + } + } +} + +impl From for String { + fn from(a: Amount) -> String { + format!("{}msat", a.msat) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_amount_serde() { + #[derive(Serialize, PartialEq, Debug, Deserialize)] + struct T { + amount: Amount, + } + + let tests = vec![ + ("{\"amount\": \"10msat\"}", Amount { msat: 10 }, "10msat"), + ( + "{\"amount\": \"42sat\"}", + Amount { msat: 42_000 }, + "42000msat", + ), + ( + "{\"amount\": \"31337btc\"}", + Amount { + msat: 3_133_700_000_000_000, + }, + "3133700000000000msat", + ), + ]; + + for (req, res, s) in tests.into_iter() { + println!("{:?} {:?}", req, res); + let parsed: T = serde_json::from_str(req).unwrap(); + assert_eq!(res, parsed.amount); + + let serialized: String = parsed.amount.into(); + assert_eq!(s, serialized); + } + } +} From 787350eaa9d2604ee80715fa19e199d542fa80b3 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 18:56:00 +0100 Subject: [PATCH 0307/1530] pytest: Test the rust bindings from cln-rpc --- Makefile | 2 +- cln-rpc/Cargo.toml | 4 ++++ cln-rpc/Makefile | 2 +- cln-rpc/examples/getinfo.rs | 18 ++++++++++++++++++ tests/test_cln_rs.py | 23 +++++++++++++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 cln-rpc/examples/getinfo.rs create mode 100644 tests/test_cln_rs.py diff --git a/Makefile b/Makefile index 618cc2d67f68..35c5b34ae245 100644 --- a/Makefile +++ b/Makefile @@ -422,7 +422,7 @@ else endif endif -pytest: $(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS) +pytest: $(ALL_PROGRAMS) $(DEFAULT_TARGETS) $(ALL_TEST_PROGRAMS) ifeq ($(PYTEST),) @echo "py.test is required to run the integration tests, please install using 'pip3 install -r requirements.txt', and rerun 'configure'." exit 1 diff --git a/cln-rpc/Cargo.toml b/cln-rpc/Cargo.toml index 4145d20a96b9..3c8f7f6e4d8c 100644 --- a/cln-rpc/Cargo.toml +++ b/cln-rpc/Cargo.toml @@ -3,6 +3,10 @@ name = "cln-rpc" version = "0.1.0" edition = "2021" +[[example]] +name = "cln-rpc-getinfo" +path = "examples/getinfo.rs" + [dependencies] anyhow = "1.0.51" bytes = "1.1.0" diff --git a/cln-rpc/Makefile b/cln-rpc/Makefile index a1812982c09a..646d268d25bd 100644 --- a/cln-rpc/Makefile +++ b/cln-rpc/Makefile @@ -1,7 +1,7 @@ cln-rpc-wrongdir: $(MAKE) -C .. cln-rpc-all -CLN_RPC_EXAMPLES := +CLN_RPC_EXAMPLES := target/debug/examples/cln-rpc-getinfo CLN_RPC_GENALL = cln-rpc/src/model.rs CLN_RPC_SOURCES = $(shell find cln-rpc -name *.rs) ${CLN_RPC_GENALL} JSON_SCHEMA = doc/schemas/*.schema.json diff --git a/cln-rpc/examples/getinfo.rs b/cln-rpc/examples/getinfo.rs new file mode 100644 index 000000000000..2ca02e76b958 --- /dev/null +++ b/cln-rpc/examples/getinfo.rs @@ -0,0 +1,18 @@ +use anyhow::Context; +use cln_rpc::{model::GetinfoRequest, ClnRpc, Request}; +use log::info; +use std::env::args; +use std::path::Path; +use tokio; + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + env_logger::init(); + let rpc_path = args().nth(1).context("missing argument: socket path")?; + let p = Path::new(&rpc_path); + + let mut rpc = ClnRpc::new(p).await?; + let response = rpc.call(Request::Getinfo(GetinfoRequest {})).await?; + info!("{}", serde_json::to_string_pretty(&response)?); + Ok(()) +} diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py new file mode 100644 index 000000000000..af01642a4fe9 --- /dev/null +++ b/tests/test_cln_rs.py @@ -0,0 +1,23 @@ +from fixtures import * # noqa: F401,F403 +from pathlib import Path +from pyln.testing.utils import env, TEST_NETWORK +import subprocess +import os +import pytest + + +# Skip the entire module if we don't have Rust. +pytestmark = pytest.mark.skipif( + env('RUST') != '1', + reason='RUST is not enabled, skipping rust-dependent tests' +) + +os.environ['RUST_LOG'] = "trace" + + +def test_rpc_client(node_factory): + l1 = node_factory.get_node() + bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-rpc-getinfo" + rpc_path = Path(l1.daemon.lightning_dir) / TEST_NETWORK / "lightning-rpc" + out = subprocess.check_output([bin_path, rpc_path], stderr=subprocess.STDOUT) + assert(b'0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518' in out) From 3eced14e38f75b74577ad78e68d19fa1b99d9125 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 25 Jan 2022 13:44:34 +0100 Subject: [PATCH 0308/1530] gci: Add rust configuration to Github actions --- .github/scripts/setup.sh | 7 +++++++ .github/workflows/ci.yaml | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/.github/scripts/setup.sh b/.github/scripts/setup.sh index 3833cdd80144..928c6e2ec95c 100755 --- a/.github/scripts/setup.sh +++ b/.github/scripts/setup.sh @@ -3,6 +3,8 @@ export DEBIAN_FRONTEND=noninteractive export BITCOIN_VERSION=0.20.1 export ELEMENTS_VERSION=0.18.1.8 +export RUST_VERSION=nightly + sudo useradd -ms /bin/bash tester sudo apt-get update -qq @@ -66,3 +68,8 @@ sudo chmod 0440 /etc/sudoers.d/tester elements-$ELEMENTS_VERSION-x86_64-linux-gnu.tar.bz2 \ elements-$ELEMENTS_VERSION ) + +if [ "$RUST" == "1" ]; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ + -y --default-toolchain ${RUST_VERSION} +fi diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 225c9cd9a7fb..d388208d8047 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -338,3 +338,35 @@ jobs: with: name: Junit Report ${{ github.run_number }}.${{ matrix.cfg }} path: report.* + + rust-test: + name: Rust Test Config + runs-on: ubuntu-20.04 + needs: [smoke-test] + env: + DEVELOPER: 1 + RUST: 1 + VALGRIND: 0 + steps: + - name: Checkout + uses: actions/checkout@v2.0.0 + + - name: Set up Python 3.6 + uses: actions/setup-python@v2 + with: + python-version: 3.6 + + - name: Install dependencies + run: | + bash -x .github/scripts/setup.sh + + - name: Build + run: | + bash -x .github/scripts/build.sh + + - name: Upload Unit Test Results + if: always() + uses: actions/upload-artifact@v2 + with: + name: Junit Report ${{ github.run_number }}.${{ matrix.cfg }} + path: report.* From b320337a60189368c8d5dbbe25d2304a1d6334a1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 26 Jan 2022 11:59:24 +0100 Subject: [PATCH 0309/1530] gci: Limit the RUST=1 config to test rust-related functionality No point in retesting yet again, just test what wasn't tested elsewhere. --- .github/workflows/ci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d388208d8047..69d95b6bac25 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -347,6 +347,8 @@ jobs: DEVELOPER: 1 RUST: 1 VALGRIND: 0 + # Run only the rust tests, others are not impacted. + TEST_CMD: "make -j 8 && pytest -vvv tests/test_cln_rs.py" steps: - name: Checkout uses: actions/checkout@v2.0.0 From 6d256fdbf9e8fe571e6338ab7abd1183546a1a81 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 30 Jan 2022 15:46:07 +0100 Subject: [PATCH 0310/1530] cln-rpc: Add type for AmountOrAll and AmountOrAny --- cln-rpc/src/primitives.rs | 102 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 0c1faa31ac7e..0a719620210b 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -28,6 +28,23 @@ pub enum ChannelStateChangeCause { ONCHAIN, } +/// An `Amount` that can also be `any`. Useful for cases in which you +/// want to delegate the Amount selection so someone else, e.g., an +/// amountless invoice. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum AmountOrAny { + Amount(Amount), + Any, +} + +/// An amount that can also be `all`. Useful for cases where you want +/// to delegate the amount computation to the cln node. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum AmountOrAll { + Amount(Amount), + All, +} + #[derive(Copy, Clone, Debug, PartialEq)] pub struct Amount { msat: u64, @@ -75,6 +92,62 @@ impl Serialize for Amount { } } +impl Serialize for AmountOrAll { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + AmountOrAll::Amount(a) => serializer.serialize_str(&format!("{}msat", a.msat)), + AmountOrAll::All => serializer.serialize_str("all"), + } + } +} + +impl Serialize for AmountOrAny { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + AmountOrAny::Amount(a) => serializer.serialize_str(&format!("{}msat", a.msat)), + AmountOrAny::Any => serializer.serialize_str("any"), + } + } +} + +impl<'de> Deserialize<'de> for AmountOrAny { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + Ok(match s.to_lowercase().as_ref() { + "any" => AmountOrAny::Any, + v => AmountOrAny::Amount( + v.try_into() + .map_err(|_e| serde::de::Error::custom("could not parse amount"))?, + ), + }) + } +} + +impl<'de> Deserialize<'de> for AmountOrAll { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + Ok(match s.to_lowercase().as_ref() { + "all" => AmountOrAll::All, + v => AmountOrAll::Amount( + v.try_into() + .map_err(|_e| serde::de::Error::custom("could not parse amount"))?, + ), + }) + } +} + impl TryFrom<&str> for Amount { type Error = Error; fn try_from(s: &str) -> Result { @@ -139,4 +212,33 @@ mod test { assert_eq!(s, serialized); } } + + #[test] + fn test_amount_all_any() { + let t = r#"{"any": "any", "all": "all", "not_any": "42msat", "not_all": "31337msat"}"#; + + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct T { + all: AmountOrAll, + not_all: AmountOrAll, + any: AmountOrAny, + not_any: AmountOrAny, + } + + let parsed: T = serde_json::from_str(t).unwrap(); + + let expected = T { + all: AmountOrAll::All, + any: AmountOrAny::Any, + not_all: AmountOrAll::Amount(Amount { msat: 31337 }), + not_any: AmountOrAny::Amount(Amount { msat: 42 }), + }; + assert_eq!(expected, parsed); + + let serialized: String = serde_json::to_string(&parsed).unwrap(); + assert_eq!( + serialized, + r#"{"all":"all","not_all":"31337msat","any":"any","not_any":"42msat"}"# + ); + } } From 53d4e9d2b82d92080cf90f5e7b755f4dbaac5729 Mon Sep 17 00:00:00 2001 From: Sebastian Falbesoner Date: Mon, 7 Feb 2022 21:08:17 +0100 Subject: [PATCH 0311/1530] bitcoin/tx: remove unused SEGREGATED_WITNESS_FLAG This define is unused since commit 509bb2c7aee5887a96e8966869c39bb4d5aba766. Changelog-None --- bitcoin/tx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 6a4fc788f890..60e2f3246b46 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -9,8 +9,6 @@ #include #include -#define SEGREGATED_WITNESS_FLAG 0x1 - struct bitcoin_tx_output *new_tx_output(const tal_t *ctx, struct amount_sat amount, const u8 *script) From 00cbe6959bc540a372ebab22d5f14dc9510676ef Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 16 Feb 2022 12:08:33 +1030 Subject: [PATCH 0312/1530] pytest: YA bitcoind API break. Signed-off-by: Rusty Russell --- tests/test_closing.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index b273e2d6a7ff..25021872e8bb 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -462,7 +462,11 @@ def get_mempool_when_size_1(): wait_for(get_mempool_when_size_1) close_tx_id = mempool_tx_ids[0] - fee_mempool = round(mempool[close_tx_id]['fee'] * 10**8) + # v22.99.0-8fe6f5a6fbcd at least doesn't have 'fee', it has 'fees'. + if 'fees' in mempool[close_tx_id]: + fee_mempool = round(mempool[close_tx_id]['fees']['base'] * 10**8) + else: + fee_mempool = round(mempool[close_tx_id]['fee'] * 10**8) assert opts['expected_close_fee'] == fee_mempool From c4ec1d576eda757a411a951bab69f53975fac743 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 16 Feb 2022 13:39:24 +1030 Subject: [PATCH 0313/1530] pytest: fix test_statictor_onions It wasn't binding to .onion:, but .onion:9735. Test both cases. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 142bb9dd1809..e23d9caaf631 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1933,11 +1933,13 @@ def test_statictor_onions(node_factory): }) l2 = node_factory.get_node(may_fail=True, options={ 'bind-addr': '127.0.0.1:{}'.format(portB), - 'addr': ['statictor:{}/torblob=11234567890123456789012345678901'.format(torips)] + 'addr': ['statictor:{}/torblob=11234567890123456789012345678901/torport={}'.format(torips, 9736)] }) assert l1.daemon.is_in_log('127.0.0.1:{}'.format(l1.port)) - assert l2.daemon.is_in_log('x2y4zvh4fn5q3eouuh7nxnc7zeawrqoutljrup2xjtiyxgx3emgkemad.onion:{},127.0.0.1:{}'.format(l2.port, l2.port)) + # Did not specify torport, so it's the default. + assert l1.daemon.is_in_log('.onion:{}'.format(9735)) + assert l2.daemon.is_in_log('x2y4zvh4fn5q3eouuh7nxnc7zeawrqoutljrup2xjtiyxgx3emgkemad.onion:{},127.0.0.1:{}'.format(9736, l2.port)) @pytest.mark.developer("needs a running Tor service instance at port 9151 or 9051") From d0c7e189958f77ccbc8f6ed85c1c2c7c13984a43 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 18 Feb 2022 10:14:11 +1030 Subject: [PATCH 0314/1530] bitcoind: importmulti fails (bitcoin master), use importdescriptors But this requires a watch-only wallet, and python-bitcoinlib doesn't support multiple wallets, so we need to unload the original one, but then we need to generate a block, so that can't generate a new address, so we need an address arg to generate_block. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 6 ++-- tests/test_wallet.py | 36 ++++++++++++++++------ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index f5117e81c6a2..28ed6be7bf14 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -422,7 +422,7 @@ def get_proxy(self): # int > 0 := wait for at least N transactions # 'tx_id' := wait for one transaction id given as a string # ['tx_id1', 'tx_id2'] := wait until all of the specified transaction IDs - def generate_block(self, numblocks=1, wait_for_mempool=0): + def generate_block(self, numblocks=1, wait_for_mempool=0, to_addr=None): if wait_for_mempool: if isinstance(wait_for_mempool, str): wait_for_mempool = [wait_for_mempool] @@ -439,7 +439,9 @@ def generate_block(self, numblocks=1, wait_for_mempool=0): )) # As of 0.16, generate() is removed; use generatetoaddress. - return self.rpc.generatetoaddress(numblocks, self.rpc.getnewaddress()) + if to_addr is None: + to_addr = self.rpc.getnewaddress() + return self.rpc.generatetoaddress(numblocks, to_addr) def simple_reorg(self, height, shift=0): """ diff --git a/tests/test_wallet.py b/tests/test_wallet.py index f7c601b26e2e..ef5e8184ef45 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1189,21 +1189,37 @@ def test_hsmtool_dump_descriptors(node_factory, bitcoind): out = subprocess.check_output(cmd_line).decode("utf8").split("\n") descriptor = [l for l in out if l.startswith("wpkh(tpub")][0] + # If we switch wallet, we can't generate address: do so now. + mine_to_addr = bitcoind.rpc.getnewaddress() + # Import the descriptor to bitcoind - # FIXME: if we update the testsuite to use the upcoming 0.21 we could use - # importdescriptors instead. - bitcoind.rpc.importmulti([{ - "desc": descriptor, - # No need to rescan, we'll transact afterward - "timestamp": "now", - # The default - "range": [0, 99] - }]) + try: + bitcoind.rpc.importmulti([{ + "desc": descriptor, + # No need to rescan, we'll transact afterward + "timestamp": "now", + # The default + "range": [0, 99] + }]) + except JSONRPCError: + # Oh look, a new API! + # Need watch-only wallet, since descriptor has no privkeys. + bitcoind.rpc.createwallet("lightningd-ro", True) + + # FIXME: No way to access non-default wallet in python-bitcoinlib + bitcoind.rpc.unloadwallet("lightningd-tests", True) + bitcoind.rpc.importdescriptors([{ + "desc": descriptor, + # No need to rescan, we'll transact afterward + "timestamp": "now", + # The default + "range": [0, 99] + }]) # Funds sent to lightningd can be retrieved by bitcoind addr = l1.rpc.newaddr()["bech32"] txid = l1.rpc.withdraw(addr, 10**3)["txid"] - bitcoind.generate_block(1, txid) + bitcoind.generate_block(1, txid, mine_to_addr) assert len(bitcoind.rpc.listunspent(1, 1, [addr])) == 1 From 7c8b12db0bb0cb0b81e8c60ea1918c1b85743bb5 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 12 Oct 2021 13:22:42 +0200 Subject: [PATCH 0315/1530] wiregen: adds note about what can be --- tools/generate-wire.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 9698eb09c3ae..6a74a34a3ba1 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -16,6 +16,9 @@ # Subtypes: # subtype, # subtypedata,,,,[] +# +# Note: can be a fixed value, a named value read before, +# or '...' to read until the end of the current structure. from argparse import ArgumentParser, REMAINDER from collections import OrderedDict From 03a1df074a6f36c298cb82caa25f2590fec74631 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 12 Oct 2021 13:13:18 +0200 Subject: [PATCH 0316/1530] peer_wire: add remote_addr to init_tlv Unfortunately we can't do any smart parsing here since wiregen does not support switch/type cases for different substructure unions yet. So just give us a pointer we can use. --- contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py | 2 ++ wire/peer_wire.csv | 2 ++ 2 files changed, 4 insertions(+) diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py index 4c82899920b9..eda2a5862644 100644 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py @@ -7,6 +7,8 @@ "msgdata,init,tlvs,init_tlvs,", "tlvtype,init_tlvs,networks,1", "tlvdata,init_tlvs,networks,chains,chain_hash,...", + "tlvtype,init_tlvs,remote_addr,3", + "tlvdata,init_tlvs,remote_addr,data,byte,...", "msgtype,error,17", "msgdata,error,channel_id,channel_id,", "msgdata,error,len,u16,", diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 4043c63509bf..497d43b52fe8 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -6,6 +6,8 @@ msgdata,init,features,byte,flen msgdata,init,tlvs,init_tlvs, tlvtype,init_tlvs,networks,1 tlvdata,init_tlvs,networks,chains,chain_hash,... +tlvtype,init_tlvs,remote_addr,3 +tlvdata,init_tlvs,remote_addr,data,byte,... msgtype,error,17 msgdata,error,channel_id,channel_id, msgdata,error,len,u16, From 38e2abf68a4b634ae2fafc564f39327eb1e9071e Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 12 Oct 2021 13:16:37 +0200 Subject: [PATCH 0317/1530] peer_exchange: set, read and log remote_addr Changelog-Added: Protocol: set remote_addr on init tlvs --- channeld/test/run-commit_tx.c | 6 +++ channeld/test/run-full_channel.c | 6 +++ cli/test/run-human-mode.c | 7 +++ cli/test/run-large-input.c | 7 +++ cli/test/run-remove-hint.c | 7 +++ common/test/Makefile | 13 +++-- common/test/run-route-specific.c | 6 +++ common/test/run-route.c | 6 +++ .../test/run-route_blinding_override_test.c | 3 -- common/test/run-route_blinding_test.c | 6 +++ common/wireaddr.c | 5 ++ common/wireaddr.h | 1 + connectd/connectd.c | 15 ++++-- connectd/connectd.h | 2 + connectd/connectd_wire.csv | 1 + connectd/peer_exchange_initmsg.c | 54 ++++++++++++++++++- connectd/test/run-onion_message.c | 6 +++ gossipd/test/run-check_channel_announcement.c | 6 +++ gossipd/test/run-check_node_announcement.c | 3 ++ gossipd/test/run-crc32_of_update.c | 3 ++ gossipd/test/run-extended-info.c | 6 +++ gossipd/test/run-next_block_range.c | 6 +++ gossipd/test/run-txout_failure.c | 6 +++ hsmd/Makefile | 4 +- lightningd/peer_control.c | 14 +++++ lightningd/test/Makefile | 4 +- lightningd/test/run-find_my_abspath.c | 2 - lightningd/test/run-invoice-select-inchan.c | 2 +- onchaind/Makefile | 4 +- openingd/dualopend.c | 4 +- plugins/test/run-funder_policy.c | 6 +++ plugins/test/run-route-overlong.c | 6 +++ wallet/test/run-wallet.c | 2 +- wire/peer_wire.csv | 2 +- wire/test/Makefile | 4 +- wire/test/run-peer-wire.c | 1 + wire/wire.h | 1 + 37 files changed, 214 insertions(+), 23 deletions(-) diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index f222e7ae3d59..07fa485af69d 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -30,6 +30,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for status_fmt */ void status_fmt(enum log_level level UNNEEDED, const struct node_id *peer UNNEEDED, @@ -45,6 +48,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* bitcoind loves its backwards txids! */ diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index 2a5112dee2d0..ae8da9cd0a51 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -16,6 +16,9 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, const tal_t *)){ } @@ -32,6 +35,9 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ void status_fmt(enum log_level level UNUSED, diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index 1548aa404e44..a9d038bb04c1 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -77,6 +78,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -109,6 +113,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for version_and_exit */ char *version_and_exit(const void *unused UNNEEDED) { fprintf(stderr, "version_and_exit called!\n"); abort(); } diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index 507b4e01dd6a..7f04e68cc360 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -77,6 +78,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -109,6 +113,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for version_and_exit */ char *version_and_exit(const void *unused UNNEEDED) { fprintf(stderr, "version_and_exit called!\n"); abort(); } diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index 795488483f92..00dd49acbcd6 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -80,6 +81,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -112,6 +116,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for version_and_exit */ char *version_and_exit(const void *unused UNNEEDED) { fprintf(stderr, "version_and_exit called!\n"); abort(); } diff --git a/common/test/Makefile b/common/test/Makefile index d441bfaa74e2..c3959b52af9d 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -20,9 +20,9 @@ ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) # Sphinx test wants to decode TLVs. common/test/run-sphinx: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o -common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o +common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o -common/test/run-route_blinding_override_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o +common/test/run-route_blinding_override_test: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o common/test/run-param \ common/test/run-json: \ @@ -35,8 +35,8 @@ common/test/run-json: \ common/lease_rates.o \ common/node_id.o \ common/pseudorand.o \ - common/wireaddr.o \ common/type_to_string.o \ + common/wireaddr.o \ wire/fromwire.o \ wire/onion$(EXP)_wiregen.o \ wire/peer$(EXP)_wiregen.o \ @@ -55,6 +55,8 @@ common/test/run-route common/test/run-route-specific: \ wire/towire.o common/test/run-gossmap_local: \ + common/base32.o \ + common/wireaddr.o \ wire/fromwire.o \ wire/peer$(EXP)_wiregen.o \ wire/tlvstream.o \ @@ -63,16 +65,21 @@ common/test/run-gossmap_local: \ common/test/run-bolt12_merkle: \ common/amount.o \ common/bigsize.o \ + common/base32.o \ common/bech32.o \ common/bech32_util.o \ common/bolt12.o \ common/node_id.o \ common/type_to_string.o \ + common/wireaddr.o \ wire/bolt12$(EXP)_wiregen.o \ wire/fromwire.o \ wire/tlvstream.o \ wire/peer$(EXP)_wiregen.o \ wire/towire.o +common/test/run-bolt12_merkle-json: \ + common/base32.o \ + common/wireaddr.o check-units: $(COMMON_TEST_PROGRAMS:%=unittest/%) diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index a591f16cd5bb..22db6f5eccee 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -33,6 +33,9 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for tlv_fields_valid */ bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, size_t *err_index UNNEEDED) @@ -48,6 +51,9 @@ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, const void *record UNNEEDED) { fprintf(stderr, "towire_tlv called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for type_to_string_ */ const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, union printable_types u UNNEEDED) diff --git a/common/test/run-route.c b/common/test/run-route.c index ad6fadc8e60e..31887236ac06 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -26,6 +26,9 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for tlv_fields_valid */ bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, size_t *err_index UNNEEDED) @@ -41,6 +44,9 @@ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, const void *record UNNEEDED) { fprintf(stderr, "towire_tlv called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for type_to_string_ */ const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, union printable_types u UNNEEDED) diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c index b8e2552cb5f2..bd8af933ffd0 100644 --- a/common/test/run-route_blinding_override_test.c +++ b/common/test/run-route_blinding_override_test.c @@ -53,9 +53,6 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } /* Generated stub for fromwire_amount_msat */ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c index 22076d9568b7..2250e2ded02e 100644 --- a/common/test/run-route_blinding_test.c +++ b/common/test/run-route_blinding_test.c @@ -69,6 +69,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -106,6 +109,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static u8 *json_to_enctlvs(const tal_t *ctx, diff --git a/common/wireaddr.c b/common/wireaddr.c index 48a1b3c37992..8fd901784f9c 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -287,6 +287,11 @@ char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a) } REGISTER_TYPE_TO_STRING(wireaddr, fmt_wireaddr); +char *printwire_wireaddr(const tal_t *ctx, const struct wireaddr *a) +{ + return fmt_wireaddr(ctx, a); +} + /* Valid forms: * * [anything]: diff --git a/common/wireaddr.h b/common/wireaddr.h index ed83194d1af5..feb6bfe0b215 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -92,6 +92,7 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 port, char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a); char *fmt_wireaddr_without_port(const tal_t *ctx, const struct wireaddr *a); +char *printwire_wireaddr(const tal_t *ctx, const struct wireaddr *a); /* If no_dns is non-NULL, we will set it to true and return NULL if * we wanted to do a DNS lookup. */ diff --git a/connectd/connectd.c b/connectd/connectd.c index d161e695feb4..090cfd4324be 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -215,6 +215,7 @@ struct peer_reconnected { struct daemon *daemon; struct node_id id; struct wireaddr_internal addr; + const struct wireaddr *remote_addr; struct crypto_state cs; const u8 *their_features; bool incoming; @@ -233,8 +234,9 @@ static struct io_plan *retry_peer_connected(struct io_conn *conn, /*~ Usually the pattern is to return this directly, but we have to free * our temporary structure. */ - plan = peer_connected(conn, pr->daemon, &pr->id, &pr->addr, &pr->cs, - take(pr->their_features), pr->incoming); + plan = peer_connected(conn, pr->daemon, &pr->id, &pr->addr, + pr->remote_addr, + &pr->cs, take(pr->their_features), pr->incoming); tal_free(pr); return plan; } @@ -245,6 +247,7 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, struct daemon *daemon, const struct node_id *id, const struct wireaddr_internal *addr, + const struct wireaddr *remote_addr, const struct crypto_state *cs, const u8 *their_features TAKES, bool incoming) @@ -264,6 +267,7 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, pr->id = *id; pr->cs = *cs; pr->addr = *addr; + pr->remote_addr = remote_addr; pr->incoming = incoming; /*~ Note that tal_dup_talarr() will do handle the take() of features @@ -335,6 +339,7 @@ struct io_plan *peer_connected(struct io_conn *conn, struct daemon *daemon, const struct node_id *id, const struct wireaddr_internal *addr, + const struct wireaddr *remote_addr, struct crypto_state *cs, const u8 *their_features TAKES, bool incoming) @@ -348,7 +353,7 @@ struct io_plan *peer_connected(struct io_conn *conn, peer = peer_htable_get(&daemon->peers, id); if (peer) - return peer_reconnected(conn, daemon, id, addr, cs, + return peer_reconnected(conn, daemon, id, addr, remote_addr, cs, their_features, incoming); /* We promised we'd take it by marking it TAKEN above; prepare to free it. */ @@ -413,8 +418,8 @@ struct io_plan *peer_connected(struct io_conn *conn, setup_peer_gossip_store(peer, daemon->our_features, their_features); /* Create message to tell master peer has connected. */ - msg = towire_connectd_peer_connected(NULL, id, addr, incoming, - their_features); + msg = towire_connectd_peer_connected(NULL, id, addr, remote_addr, + incoming, their_features); /*~ daemon_conn is a message queue for inter-daemon communication: we * queue up the `connect_peer_connected` message to tell lightningd diff --git a/connectd/connectd.h b/connectd/connectd.h index 7b2eaa98da3d..dd6637c66eea 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -8,6 +8,7 @@ #include #include #include +#include struct io_conn; struct connecting; @@ -199,6 +200,7 @@ struct io_plan *peer_connected(struct io_conn *conn, struct daemon *daemon, const struct node_id *id, const struct wireaddr_internal *addr, + const struct wireaddr *remote_addr, struct crypto_state *cs, const u8 *their_features TAKES, bool incoming); diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 7632a2840e43..e297aaeee2db 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -64,6 +64,7 @@ msgdata,connectd_connect_failed,addrhint,?wireaddr_internal, msgtype,connectd_peer_connected,2002 msgdata,connectd_peer_connected,id,node_id, msgdata,connectd_peer_connected,addr,wireaddr_internal, +msgdata,connectd_peer_connected,remote_addr,?wireaddr, msgdata,connectd_peer_connected,incoming,bool, msgdata,connectd_peer_connected,flen,u16, msgdata,connectd_peer_connected,features,u8,flen diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index b8896629f2db..bf506d391065 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, u8 *msg = cryptomsg_decrypt_body(tmpctx, &peer->cs, peer->msg); u8 *globalfeatures, *features; struct tlv_init_tlvs *tlvs = tlv_init_tlvs_new(msg); + struct wireaddr *remote_addr; if (!msg) return io_close(conn); @@ -86,6 +88,28 @@ static struct io_plan *peer_init_received(struct io_conn *conn, } } + /* fetch optional tlv `remote_addr` */ + remote_addr = NULL; + /* BOLT-remote-address #1: + * The receiving node: + * ... + * - MAY use the `remote_addr` to update its `node_annoucement` + */ + if (tlvs->remote_addr) { + switch (tlvs->remote_addr->type) { + case ADDR_TYPE_IPV4: + case ADDR_TYPE_IPV6: + remote_addr = tal_steal(peer, tlvs->remote_addr); + break; + /* We are only interested in IP addresses */ + case ADDR_TYPE_TOR_V2_REMOVED: + case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: + case ADDR_TYPE_WEBSOCKET: + break; + } + } + /* The globalfeatures field is now unused, but there was a * window where it was: combine the two. */ features = featurebits_or(tmpctx, take(features), globalfeatures); @@ -96,7 +120,9 @@ static struct io_plan *peer_init_received(struct io_conn *conn, /* Usually return io_close_taken_fd, but may wait for old peer to * be disconnected if it's a reconnect. */ return peer_connected(conn, peer->daemon, &peer->id, - &peer->addr, &peer->cs, + &peer->addr, + remote_addr, + &peer->cs, take(features), peer->incoming); } @@ -182,6 +208,32 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, tlvs->networks = tal_dup_arr(tlvs, struct bitcoin_blkid, &chainparams->genesis_blockhash, 1, 0); + /* set optional tlv `remote_addr` on incoming IP connections */ + tlvs->remote_addr = NULL; + /* BOLT-remote-address #1: + * The sending node: + * ... + * - SHOULD set `remote_addr` to reflect the remote IP address (and port) of an + * incoming connection, if the node is the receiver and the connection was done + * via IP. + */ + if (incoming && addr->itype == ADDR_INTERNAL_WIREADDR && + address_routable(&addr->u.wireaddr, true)) { + switch (addr->u.wireaddr.type) { + case ADDR_TYPE_IPV4: + case ADDR_TYPE_IPV6: + tlvs->remote_addr = tal(tlvs, struct wireaddr); + *tlvs->remote_addr = addr->u.wireaddr; + break; + /* Only report IP addresses back for now */ + case ADDR_TYPE_TOR_V2_REMOVED: + case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: + case ADDR_TYPE_WEBSOCKET: + break; + } + } + /* Initially, there were two sets of feature bits: global and local. * Local affected peer nodes only, global affected everyone. Both were * sent in the `init` message, but node_announcement only advertized diff --git a/connectd/test/run-onion_message.c b/connectd/test/run-onion_message.c index 56ea1e268a05..8d69fabee873 100644 --- a/connectd/test/run-onion_message.c +++ b/connectd/test/run-onion_message.c @@ -88,6 +88,9 @@ bool fromwire_connectd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UN /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for inject_peer_msg */ void inject_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "inject_peer_msg called!\n"); abort(); } @@ -166,6 +169,9 @@ 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 towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* Updated each time, as we pretend to be Alice, Bob, Carol */ diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 827bbdd63ebd..4db6c943bb6d 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -65,6 +65,9 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for fromwire_wireaddr_array */ struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) { fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } @@ -154,6 +157,9 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int main(int argc, char *argv[]) diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 25fcdda7abd2..ae6958abe558 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -48,6 +48,9 @@ bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UN /* Generated stub for fromwire_hsmd_node_announcement_sig_reply */ bool fromwire_hsmd_node_announcement_sig_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) { fprintf(stderr, "fromwire_hsmd_node_announcement_sig_reply called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for get_node */ struct node *get_node(struct routing_state *rstate UNNEEDED, const struct node_id *id UNNEEDED) diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 47a1250a35c9..635fc4f1c525 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -69,6 +69,9 @@ bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UN /* Generated stub for fromwire_hsmd_node_announcement_sig_reply */ bool fromwire_hsmd_node_announcement_sig_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) { fprintf(stderr, "fromwire_hsmd_node_announcement_sig_reply called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for get_node */ struct node *get_node(struct routing_state *rstate UNNEEDED, const struct node_id *id UNNEEDED) diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 8e5ad79cd38c..9b5e12a1be51 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -56,6 +56,9 @@ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for get_cupdate_parts */ void get_cupdate_parts(const u8 *channel_update UNNEEDED, const u8 *parts[2] UNNEEDED, @@ -107,6 +110,9 @@ 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 towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ void status_fmt(enum log_level level UNNEEDED, diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index e298e35f9c95..a47c295bea77 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -32,6 +32,9 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -93,6 +96,9 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for would_ratelimit_cupdate */ bool would_ratelimit_cupdate(struct routing_state *rstate UNNEEDED, const struct half_chan *hc UNNEEDED, diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 1acfd534d210..a648b0d87042 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -36,6 +36,9 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for fromwire_wireaddr_array */ struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) { fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } @@ -124,6 +127,9 @@ 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 towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* NOOP stub for gossip_store_new */ diff --git a/hsmd/Makefile b/hsmd/Makefile index 448833a1c122..91376292d051 100644 --- a/hsmd/Makefile +++ b/hsmd/Makefile @@ -21,6 +21,7 @@ ALL_PROGRAMS += lightningd/lightning_hsmd HSMD_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ + common/base32.o \ common/bigsize.o \ common/bip32.o \ common/bolt12_merkle.o \ @@ -46,7 +47,8 @@ HSMD_COMMON_OBJS := \ common/type_to_string.o \ common/utils.o \ common/utxo.o \ - common/version.o + common/version.o \ + common/wireaddr.o lightningd/lightning_hsmd: $(HSMD_OBJS) $(HSMD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 7adec90f32cc..c3d7908c96c2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -929,6 +929,7 @@ struct peer_connected_hook_payload { struct lightningd *ld; struct channel *channel; struct wireaddr_internal addr; + struct wireaddr *remote_addr; bool incoming; struct peer *peer; struct peer_fd *peer_fd; @@ -946,6 +947,10 @@ peer_connected_serialize(struct peer_connected_hook_payload *payload, json_add_string( stream, "addr", type_to_string(stream, struct wireaddr_internal, &payload->addr)); + if (payload->remote_addr) + json_add_string( + stream, "remote_addr", + type_to_string(stream, struct wireaddr, payload->remote_addr)); json_add_hex_talarr(stream, "features", p->their_features); json_object_end(stream); /* .peer */ } @@ -1121,6 +1126,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) hook_payload->error = NULL; if (!fromwire_connectd_peer_connected(hook_payload, msg, &id, &hook_payload->addr, + &hook_payload->remote_addr, &hook_payload->incoming, &their_features)) fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", @@ -1151,6 +1157,14 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) if (!hook_payload->channel) hook_payload->channel = peer_unsaved_channel(peer); + /* Log remote_addr for now */ + if (hook_payload->remote_addr && ( + hook_payload->remote_addr->type == ADDR_TYPE_IPV4 || + hook_payload->remote_addr->type == ADDR_TYPE_IPV6)) { + log_info(ld->log, "Peer says it sees our address as: %s", + fmt_wireaddr(peer, hook_payload->remote_addr)); + } + plugin_hook_call_peer_connected(ld, hook_payload); } diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index 500e76df130b..d0c3aa677868 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -10,6 +10,7 @@ ALL_TEST_PROGRAMS += $(LIGHTNINGD_TEST_PROGRAMS) LIGHTNINGD_TEST_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ + common/base32.o \ common/bech32.o \ common/daemon_conn.o \ common/htlc_state.o \ @@ -23,7 +24,8 @@ LIGHTNINGD_TEST_COMMON_OBJS := \ common/utils.o \ common/utxo.o \ common/type_to_string.o \ - common/permute_tx.o + common/permute_tx.o \ + common/wireaddr.o \ $(LIGHTNINGD_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(LIGHTNINGD_TEST_COMMON_OBJS) diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index dbe411903ba9..43c2593ff987 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -1,8 +1,6 @@ #include "config.h" #define main unused_main int unused_main(int argc, char *argv[]); -#include "../../common/base32.c" -#include "../../common/wireaddr.c" #include "../io_loop_with_timers.c" #include "../lightningd.c" #include "../subd.c" diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index d55ec8384eb7..f9e65d420abb 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -202,7 +202,7 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ -bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) +bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_hsmd_sign_bolt12_reply */ bool fromwire_hsmd_sign_bolt12_reply(const void *p UNNEEDED, struct bip340sig *sig UNNEEDED) diff --git a/onchaind/Makefile b/onchaind/Makefile index 1da287a5635b..1db28e425082 100644 --- a/onchaind/Makefile +++ b/onchaind/Makefile @@ -34,6 +34,7 @@ LIGHTNINGD_CONTROL_OBJS += \ ONCHAIND_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ + common/base32.o \ common/bigsize.o \ common/bip32.o \ common/coin_mvt.o \ @@ -63,7 +64,8 @@ ONCHAIND_COMMON_OBJS := \ common/utils.o \ common/utxo.o \ common/version.o \ - common/wallet.o + common/wallet.o \ + common/wireaddr.o lightningd/lightning_onchaind: $(ONCHAIND_OBJS) $(WIRE_ONION_OBJS) $(ONCHAIND_COMMON_OBJS) $(WIRE_OBJS) $(BITCOIN_OBJS) $(HSMD_CLIENT_OBJS) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index cc49e20ac86f..6c2cf736f105 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -295,7 +295,7 @@ static u8 *psbt_changeset_get_next(const tal_t *ctx, return NULL; } -static void shutdown(struct state *state) +static void dualopen_shutdown(struct state *state) { u8 *msg = towire_dualopend_shutdown_complete(state); @@ -1243,7 +1243,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) handle_peer_shutdown(state, msg); /* If we're done, exit */ if (shutdown_complete(state)) - shutdown(state); + dualopen_shutdown(state); return NULL; case WIRE_INIT_RBF: case WIRE_OPEN_CHANNEL2: diff --git a/plugins/test/run-funder_policy.c b/plugins/test/run-funder_policy.c index a91a5867615e..0a48176bcae0 100644 --- a/plugins/test/run-funder_policy.c +++ b/plugins/test/run-funder_policy.c @@ -15,6 +15,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } @@ -24,6 +27,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ struct test_case { diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 2ca73fd9452e..773db648f2c8 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -24,6 +24,9 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_amount_msat_compat */ void json_add_amount_msat_compat(struct json_stream *result UNNEEDED, struct amount_msat msat UNNEEDED, @@ -224,6 +227,9 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ #ifndef SUPERVERBOSE diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a6605b25af3f..d97b722969fb 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -126,7 +126,7 @@ bool fromwire_channeld_offer_htlc_reply(const tal_t *ctx UNNEEDED, const void *p bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_sending_commitsig called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ -bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) +bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED) diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 497d43b52fe8..a028ddc66d92 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -7,7 +7,7 @@ msgdata,init,tlvs,init_tlvs, tlvtype,init_tlvs,networks,1 tlvdata,init_tlvs,networks,chains,chain_hash,... tlvtype,init_tlvs,remote_addr,3 -tlvdata,init_tlvs,remote_addr,data,byte,... +tlvdata,init_tlvs,remote_addr,remote_addr,wireaddr, msgtype,error,17 msgdata,error,channel_id,channel_id, msgdata,error,len,u16, diff --git a/wire/test/Makefile b/wire/test/Makefile index adf452dbdb06..b52b00b374b8 100644 --- a/wire/test/Makefile +++ b/wire/test/Makefile @@ -11,9 +11,11 @@ ALL_TEST_PROGRAMS += $(WIRE_TEST_PROGRAMS) WIRE_TEST_COMMON_OBJS := \ common/autodata.o \ + common/base32.o \ common/pseudorand.o \ common/setup.o \ - common/utils.o + common/utils.o \ + common/wireaddr.o # run-tlvstream.c needs to reach into bitcoin/pubkey for SUPERVERBOSE $(WIRE_TEST_PROGRAMS): $(WIRE_TEST_COMMON_OBJS) $(filter-out bitcoin/pubkey.o bitcoin/chainparams.o,$(BITCOIN_OBJS)) diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 8eb92aeb6d50..05769bbef18a 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -13,6 +13,7 @@ #include #include #include +#include #include extern secp256k1_context *secp256k1_ctx; diff --git a/wire/wire.h b/wire/wire.h index c6c5287846cc..1b3d6502e0ad 100644 --- a/wire/wire.h +++ b/wire/wire.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include From 6db97b423523437d4286ca640db4d75c4eced716 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sat, 1 Jan 2022 13:02:08 +0100 Subject: [PATCH 0318/1530] pytest: check for remote_addr --- tests/plugins/peer_connected_logger_a.py | 2 +- tests/plugins/peer_connected_logger_b.py | 2 +- tests/test_connection.py | 3 +++ tests/test_plugin.py | 27 +++++++++++++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/tests/plugins/peer_connected_logger_a.py b/tests/plugins/peer_connected_logger_a.py index ae31b998a3a7..5ded23068203 100755 --- a/tests/plugins/peer_connected_logger_a.py +++ b/tests/plugins/peer_connected_logger_a.py @@ -10,7 +10,7 @@ @plugin.hook('peer_connected') def on_connected(peer, plugin, **kwargs): - print("peer_connected_logger_a {}".format(peer['id'])) + print(f"peer_connected_logger_a {peer['id']} {peer}") return {'result': 'continue'} diff --git a/tests/plugins/peer_connected_logger_b.py b/tests/plugins/peer_connected_logger_b.py index 1f0b5f6a260c..8b92076b1e1c 100755 --- a/tests/plugins/peer_connected_logger_b.py +++ b/tests/plugins/peer_connected_logger_b.py @@ -10,7 +10,7 @@ @plugin.hook('peer_connected') def on_connected(peer, plugin, **kwargs): - print("peer_connected_logger_b {}".format(peer['id'])) + print(f"peer_connected_logger_b {peer['id']} {peer}") return {'result': 'continue'} diff --git a/tests/test_connection.py b/tests/test_connection.py index 701507c7d39b..2ba68c955b12 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -48,6 +48,9 @@ def test_connect(node_factory): assert len(l1.rpc.listpeers()) == 1 assert len(l2.rpc.listpeers()) == 1 + if EXPERIMENTAL_FEATURES: + l1.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + # Should get reasonable error if unknown addr for peer. with pytest.raises(RpcError, match=r'Unable to connect, no address known'): l1.rpc.connect('032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e') diff --git a/tests/test_plugin.py b/tests/test_plugin.py index cec3b8ec369a..2a54c92c289f 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -10,7 +10,7 @@ DEPRECATED_APIS, expected_peer_features, expected_node_features, expected_channel_features, account_balance, check_coin_moves, first_channel_id, EXPERIMENTAL_DUAL_FUND, - mine_funding_to_announce + mine_funding_to_announce, EXPERIMENTAL_FEATURES ) import ast @@ -451,6 +451,31 @@ def check_disconnect(): assert not l1.daemon.is_in_log(f"peer_connected_logger_b {l3id}") +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT1 remote_addr #917") +def test_peer_connected_remote_addr(node_factory): + """This tests the optional tlv `remote_addr` being passed to a plugin. + + The optional tlv `remote_addr` should only be visible to the initiator l1. + """ + l1, l2 = node_factory.get_nodes(2, opts={'plugin': os.path.join(os.getcwd(), 'tests/plugins/peer_connected_logger_a.py')}) + l1id = l1.info['id'] + l2id = l2.info['id'] + + l1.connect(l2) + l1log = l1.daemon.wait_for_log(f"peer_connected_logger_a {l2id}") + l2log = l2.daemon.wait_for_log(f"peer_connected_logger_a {l1id}") + + # the log entries are followed by the peer_connected payload as dict {} like: + # {'id': '022d223...', 'direction': 'out', 'addr': '127.0.0.1:35289', + # 'remote_addr': '127.0.0.1:59582', 'features': '8808226aa2'} + l1payload = eval(l1log[l1log.find('{'):]) + l2payload = eval(l2log[l2log.find('{'):]) + + # check that l1 sees its remote_addr as l2 sees l1 + assert(l1payload['remote_addr'] == l2payload['addr']) + assert(not l2payload.get('remote_addr')) # l2 can't see a remote_addr + + def test_async_rpcmethod(node_factory, executor): """This tests the async rpcmethods. From df9a34b81e662e2f986fa85782bf74301de005fd Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 19 Oct 2021 11:41:25 +0200 Subject: [PATCH 0319/1530] chore: use EXPERIMENTAL for BOLT1 remote_addr #917 --- connectd/peer_exchange_initmsg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index bf506d391065..551de97677ed 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -90,6 +90,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, /* fetch optional tlv `remote_addr` */ remote_addr = NULL; +#if EXPERIMENTAL_FEATURES /* BOLT1 remote_addr #917 */ /* BOLT-remote-address #1: * The receiving node: * ... @@ -109,6 +110,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, break; } } +#endif /* The globalfeatures field is now unused, but there was a * window where it was: combine the two. */ @@ -210,6 +212,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, /* set optional tlv `remote_addr` on incoming IP connections */ tlvs->remote_addr = NULL; +#if EXPERIMENTAL_FEATURES /* BOLT1 remote_addr #917 */ /* BOLT-remote-address #1: * The sending node: * ... @@ -233,6 +236,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, break; } } +#endif /* Initially, there were two sets of feature bits: global and local. * Local affected peer nodes only, global affected everyone. Both were From 1ef77504b1b8c00fe41e82b205177463924dea6c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 26 Jan 2022 19:11:29 +0100 Subject: [PATCH 0320/1530] misc: Add build targets for the tarball and debian packages The tarball needs to materialze all submodules, and git needs to be removed as a build dependency. --- Dockerfile | 19 ++++++++- configure | 2 +- tools/build-release.sh | 92 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index f6d570533b76..6da0f36a06f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,7 +48,24 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:buster-slim as builder ENV LIGHTNINGD_VERSION=master -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git gettext +RUN apt-get update -qq && \ + apt-get install -qq -y --no-install-recommends \ + autoconf \ + automake \ + build-essential \ + ca-certificates \ + dirmngr \ + gettext \ + git \ + gnupg \ + libtool \ + python3 \ + python3-mako \ + python3-pip \ + python3-setuptools \ + wget + +RUN pip3 install -U setuptools mrkd mako RUN wget -q https://zlib.net/zlib-1.2.11.tar.gz \ && tar xvf zlib-1.2.11.tar.gz \ diff --git a/configure b/configure index 57bca469c15d..73a08f6b918e 100755 --- a/configure +++ b/configure @@ -35,7 +35,7 @@ usage_with_default() default_coptflags() { if [ "$1" = 0 ]; then - echo "-Og" + echo "" fi } diff --git a/tools/build-release.sh b/tools/build-release.sh index 86a968f77257..cb3231b4896b 100755 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -14,7 +14,7 @@ if [ "$1" = "--inside-docker" ]; then fi # bin-Ubuntu-16.04-amd64 was superceded by the reproducible built 18.04 version. -ALL_TARGETS="bin-Fedora-28-amd64 zipfile" +ALL_TARGETS="bin-Fedora-28-amd64 zipfile tarball deb" FORCE_VERSION= FORCE_UNCLEAN=false @@ -74,8 +74,6 @@ fi # If it's a completely clean directory, we need submodules! make submodcheck - -rm -rf release mkdir -p release for target in $TARGETS; do platform=${target#bin-} @@ -118,6 +116,94 @@ if [ -z "${TARGETS##* zipfile *}" ]; then rm -r "release/clightning-$VERSION" fi +RELEASEDIR="$(pwd)/release" +BARE_VERSION="$(echo "${VERSION}" | sed 's/^v//g')" +TARBALL="${RELEASEDIR}/lightningd_${BARE_VERSION}.orig.tar.bz2" +DATE=$(date +%Y%m%d%H%M%S) + + +if [ -z "${TARGETS##* tarball *}" ]; then + TMPDIR="$(mktemp -d /tmp/lightningd-tarball.XXXXXX)" + DIR="${TMPDIR}/lightningd_${BARE_VERSION}" + DESTINATION="${RELEASEDIR}/lightningd_${BARE_VERSION}.orig.tar.bz2" + echo "Bundling tarball in ${DIR}" + git clone --recursive . "${DIR}" + ( + cd "${DIR}" + # Materialize the version in the Makefile, allows us to skip + # the git dependency + sed -i "/^VERSION=/c\VERSION=v${BARE_VERSION}" "Makefile" + ./configure --disable-valgrind --enable-developer --enable-experimental-features + make doc-all check-gen-updated clean + find . -name .git -type d -print0 | xargs -0 /bin/rm -rf + ) + + ( + cd "$TMPDIR" + tar -cjvf "${DESTINATION}" "lightningd_${BARE_VERSION}" + ) + + rm -rf "${TMPDIR}" +fi + +if [ -z "${TARGETS##* deb *}" ]; then + TMPDIR="$(mktemp -d /tmp/lightningd-deb.XXXXXX)" + SRCDIR="$(pwd)" + BLDDIR="${TMPDIR}/clightning-${VERSION}" + ARCH="$(dpkg-architecture -q DEB_BUILD_ARCH)" + + for SUITE in bionic focal hirsute xenial hirsute impish; do + + mkdir -p "${BLDDIR}" + echo "Building ${BARE_VERSION} in ${TMPDIR}" + + # Stage the source directory, with the debian directory bolted on + # until we add it to contrib + tar --directory="$BLDDIR" --strip-components=1 -xjf "${TARBALL}" + cp -R "${SRCDIR}/debian" "${BLDDIR}" + + # Stage the tarball so `debuild` can find it in the parent of the + # source directory. + cp "${TARBALL}" "${TMPDIR}" + cp "${TARBALL}" "${TMPDIR}/lightningd_${BARE_VERSION}~${DATE}~${SUITE}.orig.tar.bz2" + + # Now actually build all the artifacts. + #(cd "${BLDDIR}" && debuild -i -us -uc -b) + ( + cd "${BLDDIR}" + # Add a dummy changelog entry + dch -D "${SUITE}" -v "${BARE_VERSION}~${DATE}~${SUITE}" "Upstream release $BARE_VERSION" + head debian/changelog + debuild -k5B7EE09E54473A764A23515B25B5BC531246001A -S + debuild -k5B7EE09E54473A764A23515B25B5BC531246001A -b + ) + rm -rf "${BLDDIR}" + + # Save the debs locally + cp -v "${TMPDIR}/lightningd_${BARE_VERSION}~${DATE}~${SUITE}_${ARCH}.deb" "${RELEASEDIR}" + cp -v "${TMPDIR}/lightningd-dbgsym_${BARE_VERSION}~${DATE}~${SUITE}_${ARCH}.ddeb" "${RELEASEDIR}" + + # Send to PPA + dput ppa:cdecker/clightning "${TMPDIR}/lightningd_${BARE_VERSION}~${DATE}~${SUITE}_source.changes" + done + rm -rf "${TMPDIR}" +fi + +if [ -z "${TARGETS##* docker *}" ]; then + TMPDIR="$(mktemp -d /tmp/lightningd-docker.XXXXXX)" + SRCDIR="$(pwd)" + echo "Bundling tarball in ${TMPDIR}" + git clone --recursive . "${TMPDIR}" + ( + cd "${TMPDIR}" + git checkout "v${BARE_VERSION}" + cp "${SRCDIR}/Dockerfile" "${TMPDIR}/" + sudo docker build -t elementsproject/lightningd:latest . + sudo docker tag "elementsproject/lightningd:latest" "elementsproject/lightningd:v${BARE_VERSION}" + ) + rm -rf "${TMPDIR}" +fi + if [ -z "${TARGETS##* sign *}" ]; then sha256sum release/clightning-"$VERSION"* > release/SHA256SUMS gpg -sb --armor -o release/SHA256SUMS.asc-"$(gpgconf --list-options gpg | awk -F: '$1 == "default-key" {print $10}' | tr -d '"')" release/SHA256SUMS From 11c94528b3cfa06a7b77cda5deb00f4169b60f6d Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sat, 5 Feb 2022 14:38:18 +0100 Subject: [PATCH 0321/1530] doc: reintroduce the fmt command to fmt the schema Signed-off-by: Vincenzo Palazzo --- doc/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/Makefile b/doc/Makefile index 0c90b195e10f..75e5fbdb1c65 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -93,10 +93,14 @@ doc-all: $(MANPAGES) doc/index.rst SCHEMAS := $(wildcard doc/schemas/*.json) check-fmt-schemas: $(SCHEMAS:%=check-fmt-schema/%) +fmt-schemas: $(SCHEMAS:%=fmt-schema/%) check-fmt-schema/%: % @jq . < "$*" > "$*".fmt && diff -u "$*" "$*.fmt" && rm "$*.fmt" +fmt-schema/%: % + @jq . < "$*" > "$*".fmt && cat "$*".fmt > "$*" && rm "$*.fmt" + check-doc: check-config-docs check-manpages check-fmt-schemas # Some manpages use a schema, so need that added. From 167fade0fa441ad7fd495baa211214b7253f85d2 Mon Sep 17 00:00:00 2001 From: azuchi Date: Sun, 31 Oct 2021 15:41:56 +0900 Subject: [PATCH 0322/1530] Add LIGHTNINGD_NETWORK env variable to Dockerfile for ARM Changelog-Added: Docker build for ARM defaults to `bitcoin`, but can be overridden with the `LIGHTNINGD_NETWORK` envvar. --- contrib/linuxarm32v7.Dockerfile | 1 + contrib/linuxarm64v8.Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/contrib/linuxarm32v7.Dockerfile b/contrib/linuxarm32v7.Dockerfile index e3064b5b3e26..fe843a51da2c 100644 --- a/contrib/linuxarm32v7.Dockerfile +++ b/contrib/linuxarm32v7.Dockerfile @@ -104,6 +104,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends socat inotify-t ENV LIGHTNINGD_DATA=/root/.lightning ENV LIGHTNINGD_RPC_PORT=9835 ENV LIGHTNINGD_PORT=9735 +ENV LIGHTNINGD_NETWORK=bitcoin RUN mkdir $LIGHTNINGD_DATA && \ touch $LIGHTNINGD_DATA/config diff --git a/contrib/linuxarm64v8.Dockerfile b/contrib/linuxarm64v8.Dockerfile index bb29aead50fe..4c461b688afc 100644 --- a/contrib/linuxarm64v8.Dockerfile +++ b/contrib/linuxarm64v8.Dockerfile @@ -103,6 +103,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends socat inotify-t ENV LIGHTNINGD_DATA=/root/.lightning ENV LIGHTNINGD_RPC_PORT=9835 ENV LIGHTNINGD_PORT=9735 +ENV LIGHTNINGD_NETWORK=bitcoin RUN mkdir $LIGHTNINGD_DATA && \ touch $LIGHTNINGD_DATA/config From ff84b3f77327c1ea6a733261256f17587d66d440 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Tue, 18 Jan 2022 06:13:44 -0800 Subject: [PATCH 0323/1530] json_add_invoice: fix crash if missing invstring If this field is missing for whatever reason (weird db state?) clightning will crash when listing invoices. Signed-off-by: William Casarin --- lightningd/invoice.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 9c37ca30a76c..f462a4e291b5 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -39,7 +39,8 @@ static void json_add_invoice(struct json_stream *response, const struct invoice_details *inv) { json_add_escaped_string(response, "label", inv->label); - json_add_invstring(response, inv->invstring); + if (inv->invstring) + json_add_invstring(response, inv->invstring); json_add_sha256(response, "payment_hash", &inv->rhash); if (inv->msat) json_add_amount_msat_compat(response, *inv->msat, From af16b9b9f411e13c5aff452ec9f7ecb334f8cf7c Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 10 Feb 2022 09:14:04 -0800 Subject: [PATCH 0324/1530] bolt11: mark when we decode min_final_cltv_expiry It looks like decode_c doesn't set have_c unlike the other decode_ methods. At the start of the function, decode_c checks have_c to see if it's set, but it is never set. It seems like this could allow for duplicate c tags, which is probably not intended. Signed-off-by: William Casarin --- common/bolt11.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/bolt11.c b/common/bolt11.c index cad30717f2a1..190e433f39b4 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -245,7 +245,7 @@ static char *decode_x(struct bolt11 *b11, static char *decode_c(struct bolt11 *b11, struct hash_u5 *hu5, u5 **data, size_t *data_len, - size_t data_length, const bool *have_c) + size_t data_length, bool *have_c) { u64 c; if (*have_c) @@ -261,6 +261,7 @@ static char *decode_c(struct bolt11 *b11, if (b11->min_final_cltv_expiry != c) return tal_fmt(b11, "c: %"PRIu64" is too large", c); + *have_c = true; return NULL; } From 2b92ac423601413f740ca645ba21f394c0e202dc Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 10 Feb 2022 09:22:17 -0800 Subject: [PATCH 0325/1530] bolt11: mark when expiry is decoded It looks like the x tag isn't marked when parsed either? --- common/bolt11.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/bolt11.c b/common/bolt11.c index 190e433f39b4..490c7b9ef4f2 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -224,7 +224,7 @@ static void decode_h(struct bolt11 *b11, static char *decode_x(struct bolt11 *b11, struct hash_u5 *hu5, u5 **data, size_t *data_len, - size_t data_length, const bool *have_x) + size_t data_length, bool *have_x) { if (*have_x) return unknown_field(b11, hu5, data, data_len, 'x', @@ -234,6 +234,8 @@ static char *decode_x(struct bolt11 *b11, if (!pull_uint(hu5, data, data_len, &b11->expiry, data_length * 5)) return tal_fmt(b11, "x: length %zu chars is excessive", *data_len); + + *have_x = true; return NULL; } From bdeeaab6316d90b08280d393d023cd7edb8acbc5 Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Tue, 15 Feb 2022 15:30:05 -0600 Subject: [PATCH 0326/1530] Clean up outdated comments about gossipd --- openingd/dualopend.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 6c2cf736f105..18e20d5617b5 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1173,7 +1173,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) * temporary allocations don't grow unbounded. */ clean_tmpctx(); - /* This helper routine polls both the peer and gossipd. */ + /* This helper routine polls the peer. */ msg = peer_read(ctx, state->pps); /* BOLT #1: @@ -3896,7 +3896,7 @@ int main(int argc, char *argv[]) /*~ Turns out this is useful for testing, to make sure we're ready. */ status_debug("Handed peer, entering loop"); - /*~ We manually run a little poll() loop here. With only three fds */ + /*~ We manually run a little poll() loop here. With only two fds */ pollfd[0].fd = REQ_FD; pollfd[0].events = POLLIN; pollfd[1].fd = state->pps->peer_fd; @@ -3935,8 +3935,8 @@ int main(int argc, char *argv[]) clean_tmpctx(); } - /*~ Write message and hand back the peer fd and gossipd fd. This also - * means that if the peer or gossipd wrote us any messages we didn't + /*~ Write message and hand back the peer fd. This also + * means that if the peer wrote us any messages we didn't * read yet, it will simply be read by the next daemon. */ wire_sync_write(REQ_FD, msg); per_peer_state_fdpass_send(REQ_FD, state->pps); From 01f2ca4fe7693284d735c57b237182966685a972 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Feb 2022 10:17:24 +1030 Subject: [PATCH 0327/1530] pytest: remove test_htlc_rexmit_while_closing as too flaky. Signed-off-by: Rusty Russell --- tests/test_closing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index 25021872e8bb..52f4eb745208 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3443,6 +3443,7 @@ def test_closing_higherfee(node_factory, bitcoind, executor): wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'][0]['state'] == 'CLOSINGD_COMPLETE') +@unittest.skipIf(True, "Test is extremely flaky") @pytest.mark.developer("needs dev_disconnect") def test_htlc_rexmit_while_closing(node_factory, executor): """Retranmitting an HTLC revocation while shutting down should work""" From d56904db8039d4f49fed4a24a9740011f651fa81 Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Tue, 22 Feb 2022 17:15:41 -0600 Subject: [PATCH 0328/1530] channeld: Comment update for channeld Receiving gossip is now handled globally by connectd --- channeld/channeld.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index e491a52e1ba5..f47dc404e1b4 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1,6 +1,6 @@ /* Main channel operation daemon: runs from funding_locked to shutdown_complete. * - * We're fairly synchronous: our main loop looks for gossip, master or + * We're fairly synchronous: our main loop looks for master or * peer requests and services them synchronously. * * The exceptions are: From 1da9b30b9abd26e9861ae199c2754f3d9cf7249f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 8 Feb 2022 11:16:52 +1030 Subject: [PATCH 0329/1530] gossipd: don't send updates in error messages for unannounced channels. This restores the behaviour prior to `lightningd: use our cached channel_update for errors instead of asking gossipd.`, where gossipd would refuse to give us channel_updates for unannounced channels. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 10 +++++++--- tests/test_pay.py | 4 +--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index e7c9e8e01e42..d641063ac962 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -420,9 +420,13 @@ static u8 *sign_and_timestamp_update(const tal_t *ctx, tal_free(unsigned_update); /* Tell lightningd about this immediately (even if we're not actually - * applying it now) */ - msg = towire_gossipd_got_local_channel_update(NULL, &chan->scid, update); - daemon_conn_send(daemon->master, take(msg)); + * applying it now). We choose not to send info about private + * channels, even in errors. */ + if (is_chan_public(chan)) { + msg = towire_gossipd_got_local_channel_update(NULL, &chan->scid, + update); + daemon_conn_send(daemon->master, take(msg)); + } return update; } diff --git a/tests/test_pay.py b/tests/test_pay.py index 33910f28802a..75ce58a2bd3f 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1746,9 +1746,7 @@ def listpays_nofail(b11): def test_pay_routeboost(node_factory, bitcoind, compat): """Make sure we can use routeboost information. """ # l1->l2->l3--private-->l4 - # Note: l1 gets upset because it extracts update for private channel. - l1, l2 = node_factory.line_graph(2, announce_channels=True, wait_for_announce=True, - opts=[{'allow_bad_gossip': True}, {}]) + l1, l2 = node_factory.line_graph(2, announce_channels=True, wait_for_announce=True) l3, l4, l5 = node_factory.line_graph(3, announce_channels=False, wait_for_announce=False) # This should a "could not find a route" because that's true. From ec6d91fd549df85a383c3ef63a8d13cf803b935c Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Wed, 9 Feb 2022 04:21:53 -0500 Subject: [PATCH 0330/1530] hsmd: don't leak message buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit client_read_next(…) calls io_read_wire(…), passing &c->msg_in as the address of a pointer that will be set to the address of the buffer that io_read_wire_(…) will allocate, and passing c (a pointer to the struct client instance) as the parent for the new allocation. As long as the struct client instance eventually gets freed, the allocated message buffer will be freed too, so there is no "leak" in the strict sense of the term, but the freeing of the buffer may not occur for an arbitrarily long time after the buffer has become disused, and indeed many millions of message buffers may be allocated within the lifetime of one struct client instance. handle_client(…) ultimately hands off the c->msg_in to one of several message-type-specific handler functions, and those functions are not TAKES or STEALS on their message buffer parameters and do not free their message buffer arguments. Consequently, each successive call to client_read_next(…) will cause io_read_wire_(…) to overwrite the c->msg_in pointer with the address of a newly allocated message buffer, and the old buffer will be left dangling off of the struct client instance indefinitely. Fix this by initializing c->msg_in to NULL in new_client(…) and then having client_read_next(…) do `c->msg_in = tal_free(c->msg_in)` prior to calling io_read_wire(…). That way, the previous message buffer will be freed just before beginning to read the next message. The same strategy is already employed in common/daemon_conn.c, albeit without nulling out dc->msg_in after freeing it. Fixes: #5035 Changelog-Fixed: hsmd: Fixed a significant memory leak --- hsmd/hsmd.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index a7342a2e1a0e..a236131f1307 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -165,6 +165,7 @@ static struct io_plan *bad_req(struct io_conn *conn, * and then call handle_client with argument 'c' */ static struct io_plan *client_read_next(struct io_conn *conn, struct client *c) { + c->msg_in = tal_free(c->msg_in); return io_read_wire(conn, c, &c->msg_in, handle_client, c); } @@ -187,6 +188,8 @@ static struct client *new_client(const tal_t *ctx, { struct client *c = tal(ctx, struct client); + c->msg_in = NULL; + /*~ All-zero pubkey is used for the initial master connection */ if (id) { c->id = *id; From 86b83e473bfedd401eade6ee5dbe1639f8b22a55 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 25 Feb 2022 17:38:54 +0100 Subject: [PATCH 0331/1530] pytest: Remove 3 stress-tests These tests have proven to be: a) very expensive, as they spin up many nodes, and perform long setup b) are not testing anything specific, they just fuzz functionality that is already tested otherwise c) have not helped pinpoint any issues in living memory d) are very flaky, making for really bad signal-to-noise, so much that devs usually just restart without even looking at the logs e) even if we were to look at the logs, we'd be unable to reproduce due to the inherent randomness involved in these tests f) are really noisy neighbors, causing other tests to flake as well, further muddying the water All in all, these tests are a waste of time, and source of frustration. [ Cleaned up python unused imports --RR ] Changelog-None --- tests/test_closing.py | 62 ------------------ tests/test_connection.py | 138 --------------------------------------- 2 files changed, 200 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 52f4eb745208..d449154d549a 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -187,68 +187,6 @@ def test_closing_id(node_factory): wait_for(lambda: not only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected']) -@pytest.mark.slow_test -def test_closing_torture(node_factory, executor, bitcoind): - # We set up a fully-connected mesh of N nodes, then try - # closing them all at once. - amount = 10**6 - - num_nodes = 10 # => 45 channels (36 seconds on my laptop) - if node_factory.valgrind: - num_nodes -= 4 # => 15 (135 seconds) - - nodes = node_factory.get_nodes(num_nodes) - - # Make sure bitcoind has plenty of utxos - bitcoind.generate_block(num_nodes) - - # Give them all plenty of UTXOs, make sure they see them - for i in range(len(nodes)): - for j in range(i + 1, len(nodes)): - addr = nodes[i].rpc.newaddr()['bech32'] - bitcoind.rpc.sendtoaddress(addr, (amount + 1000000) / 10**8) - bitcoind.generate_block(1) - sync_blockheight(bitcoind, nodes) - - txs = [] - for i in range(len(nodes)): - for j in range(i + 1, len(nodes)): - nodes[i].rpc.connect(nodes[j].info['id'], 'localhost', nodes[j].port) - txs.append(nodes[i].rpc.fundchannel(nodes[j].info['id'], amount)['txid']) - - # Make sure they're all in, then lock them in. - bitcoind.generate_block(1, wait_for_mempool=txs) - - # Wait for them all to be CHANNELD_NORMAL - for n in nodes: - wait_for(lambda: all(p['channels'][0]['state'] == 'CHANNELD_NORMAL' for p in n.rpc.listpeers()['peers'])) - - # Start closers: can take a long time under valgrind! - futures = [] - for i in range(len(nodes)): - for j in range(i + 1, len(nodes)): - futures.append(executor.submit(nodes[i].rpc.close, nodes[j].info['id'])) - futures.append(executor.submit(nodes[j].rpc.close, nodes[i].info['id'])) - - # Wait for close to finish - close_txs = set() - for f in futures: - # If one side completes closing, we'll get an error here 'Peer has no active channel' - try: - close_txs.add(f.result(TIMEOUT)['txid']) - except RpcError as err: - assert err.error['message'] == 'Peer has no active channel' - - # Should have one close for each open. - assert len(close_txs) == len(txs) - # Get closes confirmed - bitcoind.generate_block(100, wait_for_mempool=list(close_txs)) - - # And make sure they hangup. - for n in nodes: - wait_for(lambda: n.rpc.listpeers()['peers'] == []) - - @unittest.skipIf(TEST_NETWORK != 'regtest', 'FIXME: broken under elements') @pytest.mark.slow_test def test_closing_different_fees(node_factory, bitcoind, executor): diff --git a/tests/test_connection.py b/tests/test_connection.py index 2ba68c955b12..e19159d157e1 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,4 +1,3 @@ -from collections import namedtuple from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from flaky import flaky # noqa: F401 @@ -19,7 +18,6 @@ import pytest import random import re -import shutil import time import unittest import websocket @@ -585,47 +583,6 @@ def test_reconnect_no_update(node_factory, executor, bitcoind): l1.daemon.wait_for_log(r"CLOSINGD_COMPLETE") -def test_connect_stresstest(node_factory, executor): - # This test is unreliable, but it's better than nothing. - l1, l2, l3 = node_factory.get_nodes(3, opts={'may_reconnect': True}) - - # Hack l3 into a clone of l2, to stress reconnect code. - l3.stop() - shutil.copyfile(os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, 'hsm_secret'), - os.path.join(l3.daemon.lightning_dir, TEST_NETWORK, 'hsm_secret')) - l3.start() - l3.info = l3.rpc.getinfo() - - assert l3.info['id'] == l2.info['id'] - - # We fire off random connect/disconnect commands. - actions = [ - (l2.rpc.connect, l1.info['id'], 'localhost', l1.port), - (l3.rpc.connect, l1.info['id'], 'localhost', l1.port), - (l1.rpc.connect, l2.info['id'], 'localhost', l2.port), - (l1.rpc.connect, l3.info['id'], 'localhost', l3.port), - (l1.rpc.disconnect, l2.info['id']) - ] - args = [random.choice(actions) for _ in range(1000)] - - # We get them all to connect to each other. - futs = [] - for a in args: - futs.append(executor.submit(*a)) - - # We don't actually care if they fail, since some will. - successes = 0 - failures = 0 - for f in futs: - if f.exception(): - failures += 1 - else: - f.result() - successes += 1 - - assert successes > failures - - @pytest.mark.developer @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @@ -2914,101 +2871,6 @@ def test_fulfill_incoming_first(node_factory, bitcoind): l3.daemon.wait_for_log('onchaind complete, forgetting peer') -@pytest.mark.developer("gossip without DEVELOPER=1 is slow") -@pytest.mark.slow_test -def test_restart_many_payments(node_factory, bitcoind): - l1 = node_factory.get_node(may_reconnect=True) - - # On my laptop, these take 89 seconds and 12 seconds - if node_factory.valgrind: - num = 2 - else: - num = 5 - - nodes = node_factory.get_nodes(num * 2, opts={'may_reconnect': True}) - innodes = nodes[:num] - outnodes = nodes[num:] - - # Fund up-front to save some time. - dests = {l1.rpc.newaddr()['bech32']: (10**6 + 1000000) / 10**8 * num} - for n in innodes: - dests[n.rpc.newaddr()['bech32']] = (10**6 + 1000000) / 10**8 - bitcoind.rpc.sendmany("", dests) - bitcoind.generate_block(1) - sync_blockheight(bitcoind, [l1] + innodes) - - # Nodes with channels into the main node - for n in innodes: - n.rpc.connect(l1.info['id'], 'localhost', l1.port) - n.rpc.fundchannel(l1.info['id'], 10**6) - - # Nodes with channels out of the main node - for n in outnodes: - l1.rpc.connect(n.info['id'], 'localhost', n.port) - # OK to use change from previous fundings - l1.rpc.fundchannel(n.info['id'], 10**6, minconf=0) - - # Now mine them, get scids - mine_funding_to_announce(bitcoind, [l1] + nodes, - num_blocks=6, wait_for_mempool=num * 2) - - wait_for(lambda: [only_one(n.rpc.listpeers()['peers'])['channels'][0]['state'] for n in nodes] == ['CHANNELD_NORMAL'] * len(nodes)) - - inchans = [] - for n in innodes: - inchans.append(only_one(n.rpc.listpeers()['peers'])['channels'][0]['short_channel_id']) - - outchans = [] - for n in outnodes: - outchans.append(only_one(n.rpc.listpeers()['peers'])['channels'][0]['short_channel_id']) - - # Now make sure every node sees every channel. - for n in nodes + [l1]: - wait_for(lambda: [c['public'] for c in n.rpc.listchannels()['channels']] == [True] * len(nodes) * 2) - - # Manually create routes, get invoices - Payment = namedtuple('Payment', ['innode', 'route', 'payment_hash', 'payment_secret']) - - to_pay = [] - for i in range(len(innodes)): - # This one will cause WIRE_INCORRECT_CLTV_EXPIRY from l1. - route = [{'msatoshi': 100001001, - 'id': l1.info['id'], - 'delay': 10, - 'channel': inchans[i]}, - {'msatoshi': 100000000, - 'id': outnodes[i].info['id'], - 'delay': 5, - 'channel': outchans[i]}] - inv = outnodes[i].rpc.invoice(100000000, "invoice", "invoice") - payment_hash = inv['payment_hash'] - to_pay.append(Payment(innodes[i], route, payment_hash, inv['payment_secret'])) - - # This one should be routed through to the outnode. - route = [{'msatoshi': 100001001, - 'id': l1.info['id'], - 'delay': 11, - 'channel': inchans[i]}, - {'msatoshi': 100000000, - 'id': outnodes[i].info['id'], - 'delay': 5, - 'channel': outchans[i]}] - inv = outnodes[i].rpc.invoice(100000000, "invoice2", "invoice2") - payment_hash = inv['payment_hash'] - to_pay.append(Payment(innodes[i], route, payment_hash, inv['payment_secret'])) - - # sendpay is async. - for p in to_pay: - p.innode.rpc.sendpay(p.route, p.payment_hash, p.payment_secret) - - # Now restart l1 while traffic is flowing... - l1.restart() - - # Wait for them to finish. - for n in innodes: - wait_for(lambda: 'pending' not in [p['status'] for p in n.rpc.listsendpays()['payments']]) - - @pytest.mark.skip('needs blackhold support') @pytest.mark.developer("need dev-disconnect") @pytest.mark.openchannel('v1') From 2a6d3cad01f5372b63921ba18f9896f4a803649d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 18 Feb 2022 14:32:49 +1030 Subject: [PATCH 0332/1530] libbacktrace: update to latest. Seeing if this helps my build box which previously gives: ``` /home/rusty/lightning-ltest/lightningd/lightning_connectd: libbacktrace: unrecognized DWARF version in .debug_info at 6 /home/rusty/lightning-ltest/lightningd/lightning_connectd: libbacktrace: no debug info in ELF executable ``` Signed-off-by: Rusty Russell --- external/libbacktrace | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/libbacktrace b/external/libbacktrace index 5a99ff7fed66..2446c6607648 160000 --- a/external/libbacktrace +++ b/external/libbacktrace @@ -1 +1 @@ -Subproject commit 5a99ff7fed66b8ea8f09c9805c138524a7035ece +Subproject commit 2446c66076480ce07a6bd868badcbceb3eeecc2e From a901402e666854d5fb4d57b43ccf257514b80c8d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0333/1530] plugins/pay: fix unnecessary code. 1. p is a child of cmd, so it's freed by command_failed. 2. cltv_budget is set a few lines up to the same thing already. Signed-off-by: Rusty Russell --- plugins/pay.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 9c0b4dae7232..bf5fc83f0916 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -2476,12 +2476,10 @@ static struct command_result *json_paymod(struct command *cmd, if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0, *maxfee_pct_millionths / 100)) { - tal_free(p); return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "Overflow when computing fee budget, fee rate too high."); } - p->constraints.cltv_budget = *maxdelay; payment_mod_exemptfee_get_data(p)->amount = *exemptfee; shadow_route = payment_mod_shadowroute_get_data(p); From 899f54894fcb5810f004a33160e4901fce5c8d20 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0334/1530] gossipd: move deferred_update list ptr to head to ease memleak checks. They can only follow pointers (without help) if they point to the *start* of a tal object. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index d641063ac962..eb25ce8801a9 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -536,9 +536,11 @@ static void sign_timestamp_and_apply_update(struct daemon *daemon, /* We don't want to thrash the gossip network, so we often defer sending an * update. We track them here. */ struct deferred_update { - struct daemon *daemon; - /* Off daemon->deferred_updates */ + /* Off daemon->deferred_updates (our leak detection needs this as + * first element in struct, because it's dumb!) */ struct list_node list; + /* The daemon */ + struct daemon *daemon; /* Channel it's for (and owner) */ const struct chan *chan; int direction; From b67329bec27944dbee271bf707f7500837246403 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0335/1530] lightningd: don't leak address for printing. Not really a leak, but it would last as long as the peer. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index c3d7908c96c2..a08134fc0262 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1162,7 +1162,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) hook_payload->remote_addr->type == ADDR_TYPE_IPV4 || hook_payload->remote_addr->type == ADDR_TYPE_IPV6)) { log_info(ld->log, "Peer says it sees our address as: %s", - fmt_wireaddr(peer, hook_payload->remote_addr)); + fmt_wireaddr(tmpctx, hook_payload->remote_addr)); } plugin_hook_call_peer_connected(ld, hook_payload); From 6c8ea95f6e72af739c23166fd36c938bed62ee05 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0336/1530] hsmd: remove unnecessary memleak_remove_region. The current client is in dbid_zero_clients (i.e. it's lightningd). Signed-off-by: Rusty Russell --- hsmd/hsmd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index a236131f1307..ff1b889e48bf 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -558,7 +558,6 @@ static struct io_plan *handle_memleak(struct io_conn *conn, memtable = memleak_find_allocations(tmpctx, msg_in, msg_in); /* Now delete clients and anything they point to. */ - memleak_remove_region(memtable, c, tal_bytelen(c)); memleak_remove_region(memtable, dbid_zero_clients, sizeof(dbid_zero_clients)); memleak_remove_uintmap(memtable, &clients); From b3338a174e3a825d480fda669b449b39a8b325ce Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0337/1530] plugins/keysend: prevent leak detecting getting upset. 1. We don't keep a pointer to payments (unlike pay, which keeps a linked list), so mark it notleak. 2. plugin->our_features is overloaded for "features we want to set" (used by keysend) and then "features we have". Create a new field, which is cleaner. Signed-off-by: Rusty Russell --- plugins/keysend.c | 3 ++- plugins/libplugin.c | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/keysend.c b/plugins/keysend.c index 4c762c3dbe46..6fe37e76f3a7 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -223,7 +224,7 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, p->label = tal_steal(p, label); payment_start(p); /* We're keeping this around now */ - tal_steal(cmd->plugin, p); + tal_steal(cmd->plugin, notleak(p)); return command_still_pending(cmd); } diff --git a/plugins/libplugin.c b/plugins/libplugin.c index e77fecb7b4b2..b441146180b8 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -87,6 +87,8 @@ struct plugin { /* Feature set for lightningd */ struct feature_set *our_features; + /* Features we want to add to lightningd */ + const struct feature_set *desired_features; /* Location of the RPC filename in case we need to defer RPC * initialization or need to recover from a disconnect. */ @@ -663,10 +665,10 @@ handle_getmanifest(struct command *getmanifest_cmd, } json_array_end(params); - if (p->our_features != NULL) { + if (p->desired_features != NULL) { json_object_start(params, "featurebits"); for (size_t i = 0; i < NUM_FEATURE_PLACE; i++) { - u8 *f = p->our_features->bits[i]; + u8 *f = p->desired_features->bits[i]; const char *fieldname = feature_place_names[i]; if (fieldname == NULL) continue; @@ -1464,7 +1466,7 @@ static struct plugin *new_plugin(const tal_t *ctx, p->next_outreq_id = 0; uintmap_init(&p->out_reqs); - p->our_features = tal_steal(p, features); + p->desired_features = tal_steal(p, features); if (init_rpc) { /* Sync RPC FIXME: maybe go full async ? */ p->rpc_conn = tal(p, struct rpc_conn); From 4b7d2dc5b8610dedd822d61ebf126acecf335ab0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0338/1530] common/bolt11: make decoded routes hang off routes arr, not b11. This causes weirdness in pay, which tal_steal's b11->routes and expects to get it all. Signed-off-by: Rusty Russell --- common/bolt11.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/bolt11.c b/common/bolt11.c index 490c7b9ef4f2..5ec8f8f717c5 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -442,7 +442,7 @@ static char *decode_r(struct bolt11 *b11, size_t rlen = data_length * 5 / 8; u8 *r8 = tal_arr(tmpctx, u8, rlen); size_t n = 0; - struct route_info *r = tal_arr(tmpctx, struct route_info, n); + struct route_info *r = tal_arr(b11->routes, struct route_info, n); const u8 *cursor = r8; /* Route hops don't split in 5 bit boundaries, so convert whole thing */ @@ -457,7 +457,7 @@ static char *decode_r(struct bolt11 *b11, } while (rlen); /* Append route */ - tal_arr_expand(&b11->routes, tal_steal(b11, r)); + tal_arr_expand(&b11->routes, r); return NULL; } From 246b724dbeb4782f75cf5f51b9eee335192975d6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0339/1530] libplugin-pay: fix spurious leak reports. 1. The dijkstra can be temporary, doesn't need to last as long as pay cmd. 2. We fail multiple times in several places, so don't leak old failreason. 3. Make payments findable by our memleak detector. Signed-off-by: Rusty Russell --- plugins/libplugin-pay.c | 6 +++++- plugins/libplugin-pay.h | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index ae82c1497ce1..9595dc6092cb 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -766,7 +766,7 @@ static struct route_hop *route(const tal_t *ctx, /* Try using disabled channels too */ /* FIXME: is there somewhere we can annotate this for paystatus? */ can_carry = payment_route_can_carry_even_disabled; - dij = dijkstra(ctx, gossmap, dst, amount, riskfactor, + dij = dijkstra(tmpctx, gossmap, dst, amount, riskfactor, can_carry, route_score, p); r = route_from_dijkstra(ctx, gossmap, dij, src, amount, final_delay); @@ -2122,6 +2122,8 @@ void payment_abort(struct payment *p, const char *fmt, ...) { payment_set_step(p, PAYMENT_STEP_FAILED); p->end_time = time_now(); + /* We can fail twice, it seems. */ + tal_free(p->failreason); va_start(ap, fmt); p->failreason = tal_vfmt(p, fmt, ap); va_end(ap); @@ -2147,6 +2149,8 @@ void payment_fail(struct payment *p, const char *fmt, ...) va_list ap; p->end_time = time_now(); payment_set_step(p, PAYMENT_STEP_FAILED); + /* We can fail twice, it seems. */ + tal_free(p->failreason); va_start(ap, fmt); p->failreason = tal_vfmt(p, fmt, ap); va_end(ap); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index b2b4ce7feb40..4f045e252488 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -160,11 +160,12 @@ struct payment_constraints { }; struct payment { + /* Usually in global payments list */ + struct list_node list; /* The command that triggered this payment. Only set for the root * payment. */ struct command *cmd; struct plugin *plugin; - struct list_node list; struct node_id *local_id; const char *json_buffer; From c5b424b6ce44c2750f67d0a454d24d7b3c745fcf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0340/1530] plugins/pay: make memleak happy. 1. Tell memleak about our linked-list of current payments. 2. Don't remove them from list until we actually free them (in destructor, naturally). 3. Decode invoices into tmpctx (we steal / copy what we want anyway). 4. Free params after we've used them. Signed-off-by: Rusty Russell --- plugins/pay.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index bf5fc83f0916..95a3ee2ae596 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -1928,6 +1929,13 @@ static struct command_result *json_listpays(struct command *cmd, return send_outreq(cmd->plugin, req); } +#if DEVELOPER +static void memleak_mark_payments(struct plugin *p, struct htable *memtable) +{ + memleak_remove_region(memtable, &payments, sizeof(payments)); +} +#endif + static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { @@ -1940,6 +1948,9 @@ static const char *init(struct plugin *p, JSON_SCAN(json_to_number, &maxdelay_default), JSON_SCAN(json_to_bool, &exp_offers)); +#if DEVELOPER + plugin_set_memleak_handler(p, memleak_mark_payments); +#endif return NULL; } @@ -1987,7 +1998,6 @@ static void on_payment_success(struct payment *payment) json_add_string(ret, "status", "complete"); if (command_finished(p->cmd, ret)) {/* Ignore result. */} p->cmd = NULL; - list_del(&p->list); } } @@ -2139,7 +2149,6 @@ static void on_payment_failure(struct payment *payment) if (command_finished(cmd, ret)) { /* Ignore result. */} } p->cmd = NULL; - list_del(&p->list); } } @@ -2293,6 +2302,11 @@ struct payment_modifier *paymod_mods[] = { NULL, }; +static void destroy_payment(struct payment *p) +{ + list_del(&p->list); +} + static struct command_result *json_paymod(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -2347,7 +2361,7 @@ static struct command_result *json_paymod(struct command *cmd, if (!bolt12_has_prefix(b11str)) { b11 = - bolt11_decode(p, b11str, plugin_feature_set(cmd->plugin), + bolt11_decode(tmpctx, b11str, plugin_feature_set(cmd->plugin), NULL, chainparams, &b11_fail); if (b11 == NULL) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, @@ -2362,7 +2376,9 @@ static struct command_result *json_paymod(struct command *cmd, p->payment_hash = tal_dup(p, struct sha256, &b11->payment_hash); p->payment_secret = tal_dup_or_null(p, struct secret, b11->payment_secret); - p->routes = tal_steal(p, b11->routes); + /* FIXME: libplugin-pay plays with this array, and there are many FIXMEs + * about it. But it looks like a leak, so we suppress it here. */ + p->routes = notleak_with_children(tal_steal(p, b11->routes)); p->min_final_cltv_expiry = b11->min_final_cltv_expiry; p->features = tal_steal(p, b11->features); /* Sanity check */ @@ -2373,7 +2389,7 @@ static struct command_result *json_paymod(struct command *cmd, "Invalid bolt11:" " sets feature var_onion with no secret"); } else { - b12 = invoice_decode(p, b11str, strlen(b11str), + b12 = invoice_decode(tmpctx, b11str, strlen(b11str), plugin_feature_set(cmd->plugin), chainparams, &b12_fail); if (b12 == NULL) @@ -2452,7 +2468,7 @@ static struct command_result *json_paymod(struct command *cmd, "msatoshi parameter unnecessary"); } p->amount = *invmsat; - + tal_free(invmsat); } else { if (!msat) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, @@ -2471,17 +2487,23 @@ static struct command_result *json_paymod(struct command *cmd, p->json_toks = params; p->why = "Initial attempt"; p->constraints.cltv_budget = *maxdelay; + tal_free(maxdelay); p->deadline = timeabs_add(time_now(), time_from_sec(*retryfor)); + tal_free(retryfor); p->getroute->riskfactorppm = *riskfactor_millionths; + tal_free(riskfactor_millionths); + /* We free unneeded params as we use them, to keep memleak happy. */ if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0, *maxfee_pct_millionths / 100)) { return command_fail( cmd, JSONRPC2_INVALID_PARAMS, "Overflow when computing fee budget, fee rate too high."); } + tal_free(maxfee_pct_millionths); payment_mod_exemptfee_get_data(p)->amount = *exemptfee; + tal_free(exemptfee); shadow_route = payment_mod_shadowroute_get_data(p); payment_mod_presplit_get_data(p)->disable = disablempp; payment_mod_adaptive_splitter_get_data(p)->disable = disablempp; @@ -2491,9 +2513,11 @@ static struct command_result *json_paymod(struct command *cmd, shadow_route->fuzz_amount = false; #if DEVELOPER shadow_route->use_shadow = *use_shadow; + tal_free(use_shadow); #endif p->label = tal_steal(p, label); list_add_tail(&payments, &p->list); + tal_add_destructor(p, destroy_payment); /* We're keeping this around now */ tal_steal(cmd->plugin, p); From f0ea4d60b97feb9ecdb87691ee3c11fcc88dc09a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0341/1530] onchaind: fix minor leaks. Not actually leaks, but they do live longer than they need. Signed-off-by: Rusty Russell --- bitcoin/tx.c | 4 ++-- bitcoin/tx.h | 2 +- common/htlc_tx.c | 2 +- onchaind/onchaind.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 60e2f3246b46..15c8698620b2 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -59,7 +59,7 @@ struct wally_tx_output *wally_tx_output(const tal_t *ctx, } int bitcoin_tx_add_output(struct bitcoin_tx *tx, const u8 *script, - u8 *wscript, struct amount_sat amount) + const u8 *wscript, struct amount_sat amount) { size_t i = tx->wtx->num_outputs; struct wally_tx_output *output; @@ -197,7 +197,7 @@ int bitcoin_tx_add_input(struct bitcoin_tx *tx, input_wscript, NULL); if (input_wscript) { - scriptPubkey = scriptpubkey_p2wsh(tx->psbt, input_wscript); + scriptPubkey = scriptpubkey_p2wsh(tmpctx, input_wscript); } assert(scriptPubkey); diff --git a/bitcoin/tx.h b/bitcoin/tx.h index 170371094991..0d0e7e414ed4 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -97,7 +97,7 @@ struct wally_tx_output *wally_tx_output(const tal_t *ctx, /* Add one output to tx. */ int bitcoin_tx_add_output(struct bitcoin_tx *tx, const u8 *script, - u8 *wscript, + const u8 *wscript, struct amount_sat amount); /* Set the locktime for a transaction */ diff --git a/common/htlc_tx.c b/common/htlc_tx.c index 50552128c449..e80ea1375813 100644 --- a/common/htlc_tx.c +++ b/common/htlc_tx.c @@ -62,7 +62,7 @@ static struct bitcoin_tx *htlc_tx(const tal_t *ctx, wscript = bitcoin_wscript_htlc_tx(tx, to_self_delay, revocation_pubkey, local_delayedkey); - bitcoin_tx_add_output(tx, scriptpubkey_p2wsh(tx, wscript), + bitcoin_tx_add_output(tx, scriptpubkey_p2wsh(tmpctx, wscript), wscript, amount); bitcoin_tx_finalize(tx); diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index ecd60166a0e5..ae8eb2a5b127 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -592,7 +592,7 @@ static struct bitcoin_tx *tx_to_us(const tal_t *ctx, NULL, out->sat, NULL, wscript); bitcoin_tx_add_output( - tx, scriptpubkey_p2wpkh(tx, &our_wallet_pubkey), NULL, out->sat); + tx, scriptpubkey_p2wpkh(tmpctx, &our_wallet_pubkey), NULL, out->sat); /* Worst-case sig is 73 bytes */ weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); From 3218e5332f79a9463d085ab1fb5614526bcce809 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0342/1530] common/param: fix leak with opt_param_def. We allocate the default, then callback allocates over the top. Mark params with a default, so we can free that when it's called. (We can't do this generally, since not all param args are actually pointers to pointers, though opt_param_def has to be). Signed-off-by: Rusty Russell --- common/param.c | 23 ++++++++++++++--------- common/param.h | 14 ++++++++++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/common/param.c b/common/param.c index 5c3b30ad5cab..5c6139ee8231 100644 --- a/common/param.c +++ b/common/param.c @@ -8,13 +8,14 @@ struct param { const char *name; bool is_set; - bool required; + enum param_style style; param_cbx cbx; void *arg; }; static bool param_add(struct param **params, - const char *name, bool required, + const char *name, + enum param_style style, param_cbx cbx, void *arg) { #if DEVELOPER @@ -25,7 +26,7 @@ static bool param_add(struct param **params, last.is_set = false; last.name = name; - last.required = required; + last.style = style; last.cbx = cbx; last.arg = arg; @@ -38,6 +39,10 @@ static struct command_result *make_callback(struct command *cmd, const char *buffer, const jsmntok_t *tok) { + /* If it had a default, free that now to avoid leak */ + if (def->style == PARAM_OPTIONAL_WITH_DEFAULT && !def->is_set) + tal_free(*(void **)def->arg); + def->is_set = true; return def->cbx(cmd, def->name, buffer, tok, def->arg); @@ -50,7 +55,7 @@ static struct command_result *post_check(struct command *cmd, struct param *last = first + tal_count(params); /* Make sure required params were provided. */ - while (first != last && first->required) { + while (first != last && first->style == PARAM_REQUIRED) { if (!first->is_set) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "missing required parameter: %s", @@ -169,7 +174,7 @@ static int comp_by_arg(const struct param *a, const struct param *b, static int comp_req_order(const struct param *a, const struct param *b, void *unused) { - if (!a->required && b->required) + if (a->style != PARAM_REQUIRED && b->style == PARAM_REQUIRED) return 0; return 1; } @@ -234,7 +239,7 @@ static char *param_usage(const tal_t *ctx, for (size_t i = 0; i < tal_count(params); i++) { if (i != 0) tal_append_fmt(&usage, " "); - if (params[i].required) + if (params[i].style == PARAM_REQUIRED) tal_append_fmt(&usage, "%s", params[i].name); else tal_append_fmt(&usage, "[%s]", params[i].name); @@ -271,7 +276,7 @@ const char *param_subcommand(struct command *cmd, const char *buffer, const char *arg, **names = tal_arr(tmpctx, const char *, 1); const char *subcmd; - param_add(¶ms, "subcommand", true, (void *)param_string, &subcmd); + param_add(¶ms, "subcommand", PARAM_REQUIRED, (void *)param_string, &subcmd); names[0] = name; va_start(ap, name); while ((arg = va_arg(ap, const char *)) != NULL) @@ -315,14 +320,14 @@ bool param(struct command *cmd, const char *buffer, va_start(ap, tokens); while ((name = va_arg(ap, const char *)) != NULL) { - bool required = va_arg(ap, int); + enum param_style style = va_arg(ap, enum param_style); param_cbx cbx = va_arg(ap, param_cbx); void *arg = va_arg(ap, void *); if (streq(name, "")) { allow_extra = true; continue; } - if (!param_add(¶ms, name, required, cbx, arg)) { + if (!param_add(¶ms, name, style, cbx, arg)) { /* We really do ignore this return! */ struct command_result *ignore; ignore = command_fail(cmd, PARAM_DEV_ERROR, diff --git a/common/param.h b/common/param.h index 2dd04232a675..dcaa9f25d419 100644 --- a/common/param.h +++ b/common/param.h @@ -68,12 +68,18 @@ const char *param_subcommand(struct command *cmd, const char *buffer, const jsmntok_t tokens[], const char *name, ...) LAST_ARG_NULL; +enum param_style { + PARAM_REQUIRED, + PARAM_OPTIONAL, + PARAM_OPTIONAL_WITH_DEFAULT, +}; + /* * Add a required parameter. */ #define p_req(name, cbx, arg) \ name"", \ - true, \ + PARAM_REQUIRED, \ (param_cbx)(cbx), \ (arg) + 0*sizeof((cbx)((struct command *)NULL, \ (const char *)NULL, \ @@ -86,7 +92,7 @@ const char *param_subcommand(struct command *cmd, const char *buffer, */ #define p_opt(name, cbx, arg) \ name"", \ - false, \ + PARAM_OPTIONAL, \ (param_cbx)(cbx), \ ({ *arg = NULL; \ (arg) + 0*sizeof((cbx)((struct command *)NULL, \ @@ -100,7 +106,7 @@ const char *param_subcommand(struct command *cmd, const char *buffer, */ #define p_opt_def(name, cbx, arg, def) \ name"", \ - false, \ + PARAM_OPTIONAL_WITH_DEFAULT, \ (param_cbx)(cbx), \ ({ (*arg) = tal((cmd), typeof(**arg)); \ (**arg) = (def); \ @@ -111,5 +117,5 @@ const char *param_subcommand(struct command *cmd, const char *buffer, (arg)) == (struct command_result *)NULL); }) /* Special flag for 'check' which allows any parameters. */ -#define p_opt_any() "", false, NULL, NULL +#define p_opt_any() "", PARAM_OPTIONAL, NULL, NULL #endif /* LIGHTNING_COMMON_PARAM_H */ From 9bf2aa65cd43a75795c5fe095acd129056caa7a3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:33 +1030 Subject: [PATCH 0343/1530] common/param: don't keep around the temporary array off cmd. Signed-off-by: Rusty Russell --- common/param.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/param.c b/common/param.c index 5c6139ee8231..9326465c9ff1 100644 --- a/common/param.c +++ b/common/param.c @@ -313,7 +313,7 @@ const char *param_subcommand(struct command *cmd, const char *buffer, bool param(struct command *cmd, const char *buffer, const jsmntok_t tokens[], ...) { - struct param *params = tal_arr(cmd, struct param, 0); + struct param *params = tal_arr(tmpctx, struct param, 0); const char *name; va_list ap; bool allow_extra = false; From d5064ff56c54cb1bb6c0ccdcfd82873b9bb7b670 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 11:20:34 +1030 Subject: [PATCH 0344/1530] libplugin: make memleak see current requests. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index b441146180b8..942c91822786 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1195,6 +1195,9 @@ static void memleak_check(struct plugin *plugin, struct command *cmd) /* Now delete plugin and anything it has pointers to. */ memleak_remove_region(memtable, plugin, sizeof(*plugin)); + /* Memleak needs some help to see into intmaps */ + memleak_remove_uintmap(memtable, &plugin->out_reqs); + /* We know usage strings are referred to. */ memleak_remove_strmap(memtable, &cmd->plugin->usagemap); From 7081413e20757d95d9f295f6635d1ef4dcd593d9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 14:36:30 +1030 Subject: [PATCH 0345/1530] plugins/funder: actually free the pending_opens. It's a small leak, but real. Signed-off-by: Rusty Russell --- plugins/funder.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/plugins/funder.c b/plugins/funder.c index d0a41d8ddb0f..bbe3bbebc12f 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -84,9 +84,11 @@ unreserve_done(struct command *cmd UNUSED, json_tok_full_len(result), json_tok_full(buf, result)); + tal_free(open); return command_done(); } +/* Frees open (eventually, in unreserve_done callback) */ static void unreserve_psbt(struct pending_open *open) { struct out_req *req; @@ -102,6 +104,11 @@ static void unreserve_psbt(struct pending_open *open) open); json_add_psbt(req->js, "psbt", open->psbt); send_outreq(open->p, req); + + /* We will free this in callback, but remove from list *now* + * to avoid calling twice! */ + list_del_from(&pending_opens, &open->list); + notleak(open); } static void cleanup_peer_pending_opens(const struct node_id *id) @@ -110,24 +117,10 @@ static void cleanup_peer_pending_opens(const struct node_id *id) list_for_each_safe(&pending_opens, i, next, list) { if (node_id_eq(&i->peer_id, id)) { unreserve_psbt(i); - list_del(&i->list); } } } -static struct pending_open * -cleanup_channel_pending_open(const struct channel_id *cid) -{ - struct pending_open *open; - open = find_channel_pending_open(cid); - - if (!open) - return NULL; - - list_del(&open->list); - return open; -} - static struct command_result * command_hook_cont_psbt(struct command *cmd, struct wally_psbt *psbt) { @@ -162,7 +155,10 @@ signpsbt_done(struct command *cmd, err, json_tok_full_len(result), json_tok_full(buf, result)); - cleanup_channel_pending_open(&open->channel_id); + /* This finishes the open (successfully!) */ + list_del_from(&pending_opens, &open->list); + tal_free(open); + return command_hook_cont_psbt(cmd, signed_psbt); } @@ -757,7 +753,7 @@ static struct command_result *json_channel_open_failed(struct command *cmd, "Cleaning up inflight for channel_id %s", type_to_string(tmpctx, struct channel_id, &cid)); - open = cleanup_channel_pending_open(&cid); + open = find_channel_pending_open(&cid); if (open) unreserve_psbt(open); From fe86395e7cfed32e224c44b13c819d9fb8824853 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 14:36:35 +1030 Subject: [PATCH 0346/1530] memleak: restore functionality accidentally removed. Accidentally removed in 6c9b75275191f0efdf1036ec5b26aa334662462b. Signed-off-by: Rusty Russell --- common/memleak.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/memleak.c b/common/memleak.c index c436cdc7ded5..bdff9db08ef6 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -106,6 +106,7 @@ static void children_into_htable(const void *exclude1, const void *exclude2, if (streq(name, "tmpctx")) continue; } + htable_add(memtable, hash_ptr(i, NULL), i); children_into_htable(exclude1, exclude2, memtable, i); } } From ea71e9fe5bbb61f6fb9ba40a83680f1e02dffc84 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 14:36:36 +1030 Subject: [PATCH 0347/1530] memleak: fix handling of notleak pointers. We were ignoring the *parent* of the notleak pointers, not the notleak pointer itself! Signed-off-by: Rusty Russell --- common/memleak.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/memleak.c b/common/memleak.c index bdff9db08ef6..0c698e747a54 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -271,11 +271,11 @@ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) const struct memleak_helper *mh = i; mh->cb(memtable, p); } else if (name && strends(name, " **NOTLEAK**")) { - pointer_referenced(memtable, p); - memleak_remove_region(memtable, p, tal_bytelen(p)); + pointer_referenced(memtable, i); + memleak_remove_region(memtable, i, tal_bytelen(i)); } else if (name && strends(name, " **NOTLEAK_IGNORE_CHILDREN**")) { - remove_with_children(memtable, p); - memleak_remove_region(memtable, p, tal_bytelen(p)); + remove_with_children(memtable, i); + memleak_remove_region(memtable, i, tal_bytelen(i)); } else if (name && strends(name, "_notleak")) { pointer_referenced(memtable, i); call_memleak_helpers(memtable, i); From dd495b2b21dbf8d1511a2735568ed4be61251542 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 26 Feb 2022 14:36:36 +1030 Subject: [PATCH 0348/1530] memleak: fix handling of excluded pointers. We often hand an exclude pointer (usually the current command) to memleak. But when we encountered this we would stop iterating, rather than just ignore it: this means we would often ignore significant siblings. In particular, fixing this (which has always been there) reveals many previously-undetected leaks. Signed-off-by: Rusty Russell --- common/memleak.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/memleak.c b/common/memleak.c index 0c698e747a54..68fe64f74892 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -79,7 +79,7 @@ static void children_into_htable(const void *exclude1, const void *exclude2, const char *name = tal_name(i); if (i == exclude1 || i == exclude2) - return; + continue; if (name) { /* Don't add backtrace objects. */ From 05bd62fee4e122cafbbfeb18d4da09bbdf8d6a26 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Feb 2022 09:45:53 +1030 Subject: [PATCH 0349/1530] configure: restore -Og default on non-developer builds. Reverts change in 1ef77504b1b8c00fe41e82b205177463924dea6c. Signed-off-by: Rusty Russell --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 73a08f6b918e..57bca469c15d 100755 --- a/configure +++ b/configure @@ -35,7 +35,7 @@ usage_with_default() default_coptflags() { if [ "$1" = 0 ]; then - echo "" + echo "-Og" fi } From 760c271381f87907d200ebcda1aae72867f1cf20 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Fri, 14 Jan 2022 20:01:34 +0200 Subject: [PATCH 0350/1530] devtools: in decodemsg, add newline and fix decoding a node_announcement with tlvs Terminal prompt got messed up because missing newline in case of empty fields. printwire_addresses expected it to be the last field, which is not the case of a node_announcement with tlv --- devtools/decodemsg.c | 1 + devtools/print_wire.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/devtools/decodemsg.c b/devtools/decodemsg.c index 8512b4793ea7..fc943999818e 100644 --- a/devtools/decodemsg.c +++ b/devtools/decodemsg.c @@ -78,5 +78,6 @@ int main(int argc, char *argv[]) tal_free(m); } } + printf("\n"); return 0; } diff --git a/devtools/print_wire.c b/devtools/print_wire.c index 08bfc7cdbcb3..d73e7cb07a70 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -85,14 +85,18 @@ static void printwire_alias(const u8 **cursor, size_t *plen, size_t len) static void printwire_addresses(const u8 **cursor, size_t *plen, size_t len) { struct wireaddr addr; + size_t to_go = len; + const size_t len_ref = *plen; printf("["); - while (*plen && fromwire_wireaddr(cursor, plen, &addr)) + while (to_go && fromwire_wireaddr(cursor, plen, &addr)) { + to_go = len - (len_ref - *plen); printf(" %s", fmt_wireaddr(NULL, &addr)); + } if (!*cursor) return; - if (*plen != 0) { + if (to_go) { printf(" UNKNOWN:"); if (!print_hexstring(cursor, plen, len)) return; From 32fb8a42fd4d00e0e860fe70dcb5897dcded52be Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Tue, 14 Dec 2021 14:13:28 +0200 Subject: [PATCH 0351/1530] testing: cleanup in test_plugin_shutdown --- tests/test_plugin.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 2a54c92c289f..73452636ac8a 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2537,9 +2537,7 @@ def test_plugin_shutdown(node_factory): # Now, should also shutdown or timeout on finish, RPC calls then fail with error code -5 l1.rpc.plugin_start(p, dont_shutdown=True) - needle = l1.daemon.logsearch_start l1.rpc.stop() - l1.daemon.wait_for_log(r"test_libplugin: shutdown called") - l1.daemon.logsearch_start = needle # we don't know what comes first - l1.daemon.is_in_log(r'misc_notifications.py: via lightningd shutdown, datastore failed') - l1.daemon.is_in_log(r'test_libplugin: failed to self-terminate in time, killing.') + l1.daemon.wait_for_logs(['test_libplugin: shutdown called', + 'misc_notifications.py: via lightningd shutdown, datastore failed', + 'test_libplugin: failed to self-terminate in time, killing.']) From 905a85dc99b95c2e24fb2c03b5cbf65268037428 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Fri, 18 Feb 2022 17:34:05 +0200 Subject: [PATCH 0352/1530] JSON-RPC: getinfo, add field `our_features` All build flags and (experimental) options make it hard to find out what features are supported or enabled. And the undocumented `--list-features-only`, does not account for all our featurebits, for example bit 55 (keysend). Changelog-Added: JSON-RPC: `getinfo` result now includes `our_features` (bits) for various Bolt #9 contexts --- ...-tx-bitcoin_tx_2of2_input_witness_weight.c | 3 -- doc/lightning-getinfo.7.md | 17 +++++++++-- doc/schemas/getinfo.schema.json | 29 +++++++++++++++++++ lightningd/peer_control.c | 9 ++++++ lightningd/test/run-invoice-select-inchan.c | 3 ++ 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c b/bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c index af5483956300..d1c6a3c25d98 100644 --- a/bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c +++ b/bitcoin/test/run-tx-bitcoin_tx_2of2_input_witness_weight.c @@ -24,9 +24,6 @@ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_add called!\n"); abort(); } -/* Generated stub for amount_sat_eq */ -bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } /* Generated stub for amount_sat_greater_eq */ bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) { fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index a3d4b5b118b1..c7150e41b895 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -39,6 +39,11 @@ On success, an object is returned, containing: - **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 +- **our_features** (object, optional): Our BOLT #9 feature bits (as hexstring) for various contexts: + - **init** (hex): features (incl. globalfeatures) in our init message, these also restrict what we offer in open_channel or accept in accept_channel + - **node** (hex): features in our node_announcement message + - **channel** (hex): negotiated channel features we — as channel initiator — publish in the channel_announcement message + - **invoice** (hex): features in our BOLT11 invoices - **address** (array of objects, optional): The addresses we announce to the world: - **type** (string): Type of connection (one of "dns", "ipv4", "ipv6", "torv2", "torv3", "websocket") - **port** (u16): port number @@ -91,12 +96,18 @@ EXAMPLE JSON RESPONSE "port": 9736 } ], - "version": "0.9.0", - "blockheight": 644297, + "version": "v0.10.2", + "blockheight": 724302, "network": "bitcoin", "msatoshi_fees_collected": 0, "fees_collected_msat": "0msat", "lightning-dir": "/media/vincent/Maxtor/C-lightning/node/bitcoin" + "our_features": { + "init": "8828226aa2", + "node": "80008828226aa2", + "channel": "", + "invoice": "20024200" + } } ``` @@ -117,4 +128,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:90a3bacb6cb4456119afee8e60677c29bf5f46c4cd950e660a9f9c8e0433b473) +[comment]: # ( SHA256STAMP:041768347542d7cf4260739ad8934c77d52682d089d9fe07499e22f7331b53f5) diff --git a/doc/schemas/getinfo.schema.json b/doc/schemas/getinfo.schema.json index fc07c5de9bb5..2ddbfc3553aa 100644 --- a/doc/schemas/getinfo.schema.json +++ b/doc/schemas/getinfo.schema.json @@ -56,6 +56,35 @@ "type": "string", "description": "Identifies where you can find the configuration and other related files" }, + "our_features": { + "type": "object", + "description": "Our BOLT #9 feature bits (as hexstring) for various contexts", + "additionalProperties": true, + "required": [ + "init", + "node", + "channel", + "invoice" + ], + "properties": { + "init": { + "type": "hex", + "description": "features (incl. globalfeatures) in our init message, these also restrict what we offer in open_channel or accept in accept_channel" + }, + "node": { + "type": "hex", + "description": "features in our node_announcement message" + }, + "channel": { + "type": "hex", + "description": "negotiated channel features we — as channel initiator — publish in the channel_announcement message" + }, + "invoice": { + "type": "hex", + "description": "features in our BOLT11 invoices" + } + } + }, "blockheight": { "type": "u32", "description": "The highest block height we've learned" diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index a08134fc0262..212208467166 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1747,6 +1747,15 @@ static struct command_result *json_getinfo(struct command *cmd, json_add_string(response, "warning_lightningd_sync", "Still loading latest blocks from bitcoind."); + u8 **bits = cmd->ld->our_features->bits; + json_object_start(response, "our_features"); + json_add_hex_talarr(response, "init", + featurebits_or(cmd, bits[INIT_FEATURE], bits[GLOBAL_INIT_FEATURE])); + json_add_hex_talarr(response, "node", bits[NODE_ANNOUNCE_FEATURE]); + json_add_hex_talarr(response, "channel", bits[CHANNEL_FEATURE]); + json_add_hex_talarr(response, "invoice", bits[BOLT11_FEATURE]); + json_object_end(response); + return command_success(cmd, response); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index f9e65d420abb..1a8ddc3d8c9d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -184,6 +184,9 @@ bool feature_is_set(const u8 *features UNNEEDED, size_t bit UNNEEDED) bool feature_negotiated(const struct feature_set *our_features UNNEEDED, const u8 *their_features UNNEEDED, size_t f UNNEEDED) { fprintf(stderr, "feature_negotiated called!\n"); abort(); } +/* Generated stub for featurebits_or */ +u8 *featurebits_or(const tal_t *ctx UNNEEDED, const u8 *f1 TAKES UNNEEDED, const u8 *f2 TAKES UNNEEDED) +{ fprintf(stderr, "featurebits_or called!\n"); abort(); } /* Generated stub for find_plugin_for_command */ struct plugin *find_plugin_for_command(struct lightningd *ld UNNEEDED, const char *cmd_name UNNEEDED) From 9bbb52097e3525d5c90949f41b1332ce355075a3 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Fri, 18 Feb 2022 17:41:05 +0200 Subject: [PATCH 0353/1530] lightningd: fix parsing of option dev-force-features It did not pick-up the last (bolt11) bits set. --- lightningd/options.c | 10 +++++----- tests/test_connection.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index e851bc67fc39..d14b917fd31f 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -611,11 +611,11 @@ static char *opt_force_featureset(const char *optarg, struct lightningd *ld) { char **parts = tal_strsplit(tmpctx, optarg, "/", STR_EMPTY_OK); - if (tal_count(parts) != NUM_FEATURE_PLACE) { + if (tal_count(parts) != NUM_FEATURE_PLACE + 1) { if (!strstarts(optarg, "-") && !strstarts(optarg, "+")) - return "Expected 5 feature sets (init, globalinit," - " node_announce, channel, bolt11) separated" - " by / OR +/-"; + return "Expected 5 feature sets (init/globalinit/" + " node_announce/channel/bolt11) each terminated by /" + " OR +/-"; char *endp; long int n = strtol(optarg + 1, &endp, 10); @@ -712,7 +712,7 @@ static void dev_register_opts(struct lightningd *ld) &ld->plugins->dev_builtin_plugins_unimportant, "Make builtin plugins unimportant so you can plugin stop them."); opt_register_arg("--dev-force-features", opt_force_featureset, NULL, ld, - "Force the init/globalinit/node_announce/channel/bolt11 features, each comma-separated bitnumbers"); + "Force the init/globalinit/node_announce/channel/bolt11/ features, each comma-separated bitnumbers OR a single +/-"); opt_register_arg("--dev-timeout-secs", opt_set_u32, opt_show_u32, &ld->config.connection_timeout_secs, "Seconds to timeout if we don't receive INIT from peer"); diff --git a/tests/test_connection.py b/tests/test_connection.py index e19159d157e1..9e8179a11f0d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3226,7 +3226,7 @@ def test_nonstatic_channel(node_factory, bitcoind): opts=[{}, # needs at least 15 to connect # (and 9 is a dependent) - {'dev-force-features': '9,15////'}]) + {'dev-force-features': '9,15/////'}]) chan = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) assert 'option_static_remotekey' not in chan['features'] assert 'option_anchor_outputs' not in chan['features'] From 0173717b6ade9d7e0d760897b73318b8b1275b52 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Sat, 25 Dec 2021 21:06:31 +0200 Subject: [PATCH 0354/1530] doc: add details about: log-level, experimental options and `features` field returned by listnodes, listchannels and connect. --- doc/lightning-connect.7.md | 2 +- doc/lightning-getinfo.7.md | 2 +- doc/lightning-listchannels.7.md | 2 +- doc/lightning-listnodes.7.md | 2 +- doc/lightningd-config.5.md | 10 +++++++--- gossipd/gossip_generation.c | 3 +-- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index 02a85f1c068b..e596f7a76c81 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -42,7 +42,7 @@ RETURN VALUE [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 +- **features** (hex): BOLT 9 features bitmap offered by peer in init message (globalfeatures and features combined) - **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") diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index c7150e41b895..20f8be1d80e7 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -128,4 +128,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:041768347542d7cf4260739ad8934c77d52682d089d9fe07499e22f7331b53f5) +[comment]: # ( SHA256STAMP:f7c85915ae8da7e9cabd2c72a31157801524b53f6bf8463da1743a0ee71e7c88) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index 7c9063840bbd..5113ba6f5199 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -45,7 +45,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **fee_per_millionth** (u32): Proportional fee changed by *source* to use this channel, in parts-per-million - **delay** (u32): The number of blocks delay required by *source* to use this channel - **htlc_minimum_msat** (msat): The smallest payment *source* will allow via this channel -- **features** (hex): BOLT #9 features bitmap for this channel +- **features** (hex): BOLT #9 features bitmap for this channel in channel_announcement message - **htlc_maximum_msat** (msat, optional): The largest payment *source* will allow via this channel [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index b386c56696b7..314530684752 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -34,7 +34,7 @@ On success, an object containing **nodes** is returned. It is an array of objec If **last_timestamp** is present: - **alias** (string): The fun alias this node advertized (up to 32 characters) - **color** (hex): The favorite RGB color this node advertized (always 6 characters) - - **features** (hex): BOLT #9 features bitmap this node advertized + - **features** (hex): BOLT #9 features bitmap this node advertized in node_announcement message - **addresses** (array of objects): The addresses this node advertized: - **type** (string): Type of connection (one of "dns", "ipv4", "ipv6", "torv2", "torv3", "websocket") - **port** (u16): port number diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 80e633071c0b..879606f2ea29 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -124,7 +124,8 @@ Specify pid file to write to. **log-level**=*LEVEL*\[:*SUBSYSTEM*\] What log level to print out: options are io, debug, info, unusual, broken. If *SUBSYSTEM* is supplied, this sets the logging level -for any subsystem containing that string. Subsystems include: +for any subsystem containing that string. This option may be specified multiple times. +Subsystems include: * *lightningd*: The main lightning daemon * *database*: The database subsystem @@ -497,8 +498,11 @@ considered important. Experimental options are subject to breakage between releases: they are made available for advanced users who want to test proposed -features. If lightningd is built configured with -`--enable-experimental-features` these are on by default. +features. When the build is configured _without_ `--enable-experimental-features`, +below options are available but disabled by default. +A build _with_ `--enable-experimental-features` enables some of below options +by default and also adds support for even more features. Supported features can +be listed with `lightningd --list-features-only`. **experimental-onion-messages** diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index eb25ce8801a9..ae1d7d8516c2 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -46,8 +46,7 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, announcement = towire_node_announcement(ctx, sig, - daemon->our_features->bits - [NODE_ANNOUNCE_FEATURE], + daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], timestamp, &daemon->id, daemon->rgb, daemon->alias, addresses, From 2d06c389975d9bf31e836ccb33547fbdd7da4678 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 17 Jan 2022 15:35:40 +0200 Subject: [PATCH 0355/1530] plugin-funder: fix typos in option lease-fee-base-msat and funder-fuzz-percent Changelog-Experimental: option `--lease-fee-base-msat` renamed to `--lease-fee-base-sat` Changelog-Experimental: option `--lease-fee-base-msat` deprecated and will be removed next release --- doc/undoc-flags.json | 1 + plugins/funder.c | 9 +++++++-- tests/test_closing.py | 16 ++++++++-------- tests/test_gossip.py | 2 +- tests/test_opening.py | 2 +- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/doc/undoc-flags.json b/doc/undoc-flags.json index 123b7b313568..ef8b8fe23694 100644 --- a/doc/undoc-flags.json +++ b/doc/undoc-flags.json @@ -13,6 +13,7 @@ "funder-policy", "funder-policy-mod", "funder-reserve-tank", + "lease-fee-base-sat", "lease-fee-base-msat", "lease-fee-basis", "lease-funding-weight" diff --git a/plugins/funder.c b/plugins/funder.c index bbe3bbebc12f..d2c572a63055 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -1259,7 +1259,7 @@ int main(int argc, char **argv) plugin_option("funder-fuzz-percent", "int", "Percent to fuzz the policy contribution by." - " Defaults to 5%. Max is 100%", + " Defaults to 0%. Max is 100%", u32_option, ¤t_policy->fuzz_factor), plugin_option("funder-fund-probability", @@ -1276,7 +1276,12 @@ int main(int argc, char **argv) " being advertised", bool_option, ¤t_policy->leases_only), - plugin_option("lease-fee-base-msat", + plugin_option("lease-fee-base-sat", + "string", + "Channel lease rates, base fee for leased" + " funds, in satoshi.", + option_lease_fee_base, current_policy), + plugin_option_deprecated("lease-fee-base-msat", "string", "Channel lease rates, base fee for leased" " funds, in satoshi.", diff --git a/tests/test_closing.py b/tests/test_closing.py index d449154d549a..4643c8e8b6ea 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -737,9 +737,9 @@ def test_channel_lease_falls_behind(node_factory, bitcoind): their blockheight, the lessor fails the channel ''' opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100}, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100}, {'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100}] + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100}] l1, l2, = node_factory.get_nodes(2, opts=opts) amount = 500000 feerate = 2000 @@ -776,7 +776,7 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') opts = {'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, 'may_reconnect': True, 'plugin': coin_mvt_plugin} l1, l2, = node_factory.get_nodes(2, opts=opts) @@ -884,7 +884,7 @@ def test_channel_lease_unilat_closes(node_factory, bitcoind): l2-l3: l2 leases funds from l3; l3 goes to chain unilaterally ''' opts = {'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, 'funder-lease-requests-only': False} l1, l2, l3 = node_factory.get_nodes(3, opts=opts) @@ -988,11 +988,11 @@ def test_channel_lease_lessor_cheat(node_factory, bitcoind, chainparams): ''' balance_snaps = os.path.join(os.getcwd(), 'tests/plugins/balance_snaps.py') opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, 'may_reconnect': True, 'allow_warning': True, 'plugin': balance_snaps}, {'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, 'may_reconnect': True, 'allow_broken_log': True, 'plugin': balance_snaps}] l1, l2, = node_factory.get_nodes(2, opts=opts) @@ -1063,10 +1063,10 @@ def test_channel_lease_lessee_cheat(node_factory, bitcoind, chainparams): Check that lessor can recover funds if lessee cheats ''' opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, 'may_reconnect': True, 'allow_broken_log': True}, {'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, 'may_reconnect': True}] l1, l2, = node_factory.get_nodes(2, opts=opts) amount = 500000 diff --git a/tests/test_gossip.py b/tests/test_gossip.py index e23d9caaf631..4899f5971397 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1066,7 +1066,7 @@ def test_gossip_addresses(node_factory, bitcoind): @pytest.mark.openchannel('v2') def test_gossip_lease_rates(node_factory, bitcoind): lease_opts = {'lease-fee-basis': 50, - 'lease-fee-base-msat': '2000msat', + 'lease-fee-base-sat': '2000msat', 'channel-fee-max-base-msat': '500sat', 'channel-fee-max-proportional-thousandths': 200} l1, l2 = node_factory.get_nodes(2, opts=[lease_opts, {}]) diff --git a/tests/test_opening.py b/tests/test_opening.py index c56537bf81a9..c1c3dfa573a4 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -339,7 +339,7 @@ def test_v2_rbf_single(node_factory, bitcoind, chainparams): def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): opts = {'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, 'may_reconnect': True} l1, l2 = node_factory.get_nodes(2, opts=opts) From f84e1a05362faa7e3753aa584d3cb2619eead46d Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Fri, 14 Jan 2022 13:38:54 +0200 Subject: [PATCH 0356/1530] pyln-testing: in utils, update method fundbalancedchannel, now it also works with dualfund and some cleanup, fundchannel has been returning tx, txid and outnum for years --- contrib/pyln-testing/pyln/testing/utils.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 28ed6be7bf14..6656c41f9949 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -781,22 +781,15 @@ def fundbalancedchannel(self, remote_node, total_capacity, announce=True): self.rpc.connect(remote_node.info['id'], 'localhost', remote_node.port) - # Make sure the fundchannel is confirmed. - num_tx = len(self.bitcoin.rpc.getrawmempool()) res = self.rpc.fundchannel(remote_node.info['id'], chan_capacity, feerate='slow', minconf=0, announce=announce, push_msat=Millisatoshi(chan_capacity * 500)) - wait_for(lambda: len(self.bitcoin.rpc.getrawmempool()) == num_tx + 1) - blockid = self.bitcoin.generate_block(1)[0] + blockid = self.bitcoin.generate_block(1, wait_for_mempool=res['txid'])[0] # Generate the scid. - outnum = get_tx_p2wsh_outnum(self.bitcoin, res['tx'], total_capacity) - if outnum is None: - raise ValueError("no outnum found. capacity {} tx {}".format(total_capacity, res['tx'])) - for i, txid in enumerate(self.bitcoin.rpc.getblock(blockid)['tx']): if txid == res['txid']: txnum = i - return '{}x{}x{}'.format(self.bitcoin.rpc.getblockcount(), txnum, outnum) + return '{}x{}x{}'.format(self.bitcoin.rpc.getblockcount(), txnum, res['outnum']) def getactivechannels(self): return [c for c in self.rpc.listchannels()['channels'] if c['active']] @@ -897,8 +890,7 @@ def has_funds_on_addr(addr): res = self.rpc.fundchannel(l2.info['id'], amount, announce=announce_channel, **kwargs) - wait_for(lambda: res['txid'] in self.bitcoin.rpc.getrawmempool()) - blockid = self.bitcoin.generate_block(1)[0] + blockid = self.bitcoin.generate_block(1, wait_for_mempool=res['txid'])[0] for i, txid in enumerate(self.bitcoin.rpc.getblock(blockid)['tx']): if txid == res['txid']: From 84bead93967993fb26072fc1e608a70062d5d356 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Sun, 16 Jan 2022 11:56:15 +0200 Subject: [PATCH 0357/1530] pyln-testing: in LightningNode.openchannel, make wait_for_announce more reliable it now waits for 'alias' in node_announcement, not just block confirms. more cleanup --- contrib/pyln-testing/pyln/testing/utils.py | 19 ++++++------------- tests/test_misc.py | 2 +- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 6656c41f9949..bd6b84196f81 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -731,21 +731,16 @@ def openchannel(self, remote_node, capacity=FUNDAMOUNT, addrtype="p2sh-segwit", if connect and not self.is_connected(remote_node): self.connect(remote_node) - fundingtx = self.rpc.fundchannel(remote_node.info['id'], capacity) - - # Wait for the funding transaction to be in bitcoind's mempool - wait_for(lambda: fundingtx['txid'] in self.bitcoin.rpc.getrawmempool()) + res = self.rpc.fundchannel(remote_node.info['id'], capacity) if confirm or wait_for_announce: - self.bitcoin.generate_block(1) + self.bitcoin.generate_block(1, wait_for_mempool=res['txid']) if wait_for_announce: self.bitcoin.generate_block(5) + wait_for(lambda: ['alias' in e for e in self.rpc.listnodes(remote_node.info['id'])['nodes']]) - if confirm or wait_for_announce: - self.daemon.wait_for_log( - r'Funding tx {} depth'.format(fundingtx['txid'])) - return {'address': addr, 'wallettxid': wallettxid, 'fundingtx': fundingtx} + return {'address': addr, 'wallettxid': wallettxid, 'fundingtx': res['tx']} def fundwallet(self, sats, addrtype="p2sh-segwit", mine_block=True): addr = self.rpc.newaddr(addrtype)[addrtype] @@ -1360,7 +1355,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, return node def join_nodes(self, nodes, fundchannel=True, fundamount=FUNDAMOUNT, wait_for_announce=False, announce_channels=True) -> None: - """Given nodes, connect them in a line, optionally funding a channel""" + """Given nodes, connect them in a line, optionally funding a channel, wait_for_announce waits for channel and node announcements""" assert not (wait_for_announce and not announce_channels), "You've asked to wait for an announcement that's not coming. (wait_for_announce=True,announce_channels=False)" connections = [(nodes[i], nodes[i + 1]) for i in range(len(nodes) - 1)] @@ -1386,10 +1381,8 @@ def join_nodes(self, nodes, fundchannel=True, fundamount=FUNDAMOUNT, wait_for_an for src, dst in connections: txids.append(src.rpc.fundchannel(dst.info['id'], fundamount, announce=announce_channels)['txid']) - wait_for(lambda: set(txids).issubset(set(bitcoind.rpc.getrawmempool()))) - # Confirm all channels and wait for them to become usable - bitcoind.generate_block(1) + bitcoind.generate_block(1, wait_for_mempool=txids) scids = [] for src, dst in connections: wait_for(lambda: src.channel_state(dst) == 'CHANNELD_NORMAL') diff --git a/tests/test_misc.py b/tests/test_misc.py index f65b88fd2f64..083306d258a5 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -452,7 +452,7 @@ def test_bech32_funding(node_factory, chainparams): wallettxid = res['wallettxid'] wallettx = l1.bitcoin.rpc.getrawtransaction(wallettxid, True) - fundingtx = l1.bitcoin.rpc.decoderawtransaction(res['fundingtx']['tx']) + fundingtx = l1.bitcoin.rpc.decoderawtransaction(res['fundingtx']) def is_p2wpkh(output): return output['type'] == 'witness_v0_keyhash' and \ From ad22535d345fed5c934229cfda2d73b80f2b4f50 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Tue, 14 Dec 2021 13:47:50 +0200 Subject: [PATCH 0358/1530] pyln-testing: improve description of wait_for_logs --- contrib/pyln-testing/pyln/testing/utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index bd6b84196f81..c47fffd98a22 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -277,9 +277,12 @@ def is_in_stderr(self, regex): def wait_for_logs(self, regexs, timeout=TIMEOUT): """Look for `regexs` in the logs. - We tail the stdout of the process and look for each regex in `regexs`, - starting from last of the previous waited-for log entries (if any). We - fail if the timeout is exceeded or if the underlying process + The logs contain tailed stdout of the process. We look for each regex + in `regexs`, starting from `logsearch_start` which normally is the + position of the last found entry of a previous wait-for logs call. + The ordering inside `regexs` doesn't matter. + + We fail if the timeout is exceeded or if the underlying process exits before all the `regexs` were found. If timeout is None, no time-out is applied. From c286eb053fa8c873d309c8351559886373ea6aab Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Sat, 4 Sep 2021 12:51:50 +0300 Subject: [PATCH 0359/1530] pyln-client: Plugin, improve background comment --- contrib/pyln-client/pyln/client/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/pyln-client/pyln/client/plugin.py b/contrib/pyln-client/pyln/client/plugin.py index 35b8c2346818..efc9b2ac2b69 100644 --- a/contrib/pyln-client/pyln/client/plugin.py +++ b/contrib/pyln-client/pyln/client/plugin.py @@ -630,8 +630,8 @@ def _dispatch_request(self, request: Request) -> None: try: result = self._exec_func(method.func, request) if not method.background: - # Only if this is not an async (background) call do we need to - # return the result, otherwise the callee will eventually need + # Only if this is a synchronous (background=False) call do we need to + # return the result. Otherwise the callee (method) will eventually need # to call request.set_result or request.set_exception to # return a result or raise an exception. request.set_result(result) From 175e0b54b36630443680a4e264a28ef42c3680ef Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Sat, 26 Feb 2022 19:36:46 +0200 Subject: [PATCH 0360/1530] testing: fix flake in test_ping_timeout [ Folded 2 fixups --RR ] --- tests/test_connection.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 9e8179a11f0d..5e4cfbe15dc5 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3702,6 +3702,7 @@ def test_ping_timeout(node_factory): 'disconnect': l1_disconnects}, {}]) # Takes 15-45 seconds, then another to try second ping - l1.daemon.wait_for_log('Last ping unreturned: hanging up', - timeout=45 + 45 + 5) - wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected'] is False) + # Because of ping timer randomness we don't know which side hangs up first + wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected'] is False, timeout=45 + 45 + 5) + wait_for(lambda: (l1.daemon.is_in_log('Last ping unreturned: hanging up') + or l2.daemon.is_in_log('Last ping unreturned: hanging up'))) From edf6832f8fb1d0578a9a96c47781bf8d60730a2c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 16 Feb 2022 12:11:35 +0100 Subject: [PATCH 0361/1530] rust: Actually check if we have cargo Seems a temporary edit to check the detection slipped in during the previous PR. This fixes that :-) --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 57bca469c15d..e07d52069675 100755 --- a/configure +++ b/configure @@ -111,7 +111,7 @@ default_valgrind_setting() default_rust_setting() { - if cargoa --version > /dev/null 2>&1; then + if cargo --version > /dev/null 2>&1; then echo 1 else echo 0 From d01b2c21a7f5b0190eece0628edee40e8929b678 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 19:45:30 +0100 Subject: [PATCH 0362/1530] cln-grpc: Add generation of grpc protobuf file from schema --- Makefile | 1 + cln-grpc/Makefile | 11 ++ cln-grpc/proto/node.proto | 184 ++++++++++++++++++++++++++++++ cln-grpc/proto/primitives.proto | 33 ++++++ contrib/msggen/msggen/__main__.py | 8 ++ contrib/msggen/msggen/grpc.py | 153 +++++++++++++++++++++++++ requirements.lock | 71 +++++++----- 7 files changed, 431 insertions(+), 30 deletions(-) create mode 100644 cln-grpc/Makefile create mode 100644 cln-grpc/proto/node.proto create mode 100644 cln-grpc/proto/primitives.proto create mode 100644 contrib/msggen/msggen/grpc.py diff --git a/Makefile b/Makefile index 35c5b34ae245..62fec37c111d 100644 --- a/Makefile +++ b/Makefile @@ -356,6 +356,7 @@ ifneq ($(FUZZING),0) endif ifneq ($(RUST),0) include cln-rpc/Makefile + include cln-grpc/Makefile endif # We make pretty much everything depend on these. diff --git a/cln-grpc/Makefile b/cln-grpc/Makefile new file mode 100644 index 000000000000..9eb6bc26e4de --- /dev/null +++ b/cln-grpc/Makefile @@ -0,0 +1,11 @@ +cln-grpc-wrongdir: + $(MAKE) -C .. cln-grpc-all + +CLN_GRPC_EXAMPLES := +CLN_GRPC_GENALL = cln-grpc/proto/node.proto +DEFAULT_TARGETS += $(CLN_GRPC_EXAMPLES) $(CLN_GRPC_GENALL) + +$(CLN_GRPC_GENALL): $(JSON_SCHEMA) + PYTHONPATH=contrib/msggen python3 contrib/msggen/msggen/__main__.py + +cln-grpc-all: ${CLN_GRPC_GENALL} ${CLN_GRPC_EXAMPLES} diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto new file mode 100644 index 000000000000..e7d7f5267565 --- /dev/null +++ b/cln-grpc/proto/node.proto @@ -0,0 +1,184 @@ +syntax = "proto3"; +package cln; + +// This file was automatically derived from the JSON-RPC schemas in +// `doc/schemas`. Do not edit this file manually as it would get +// overwritten. + +import "primitives.proto"; + +service Node { + rpc Getinfo(GetinfoRequest) returns (GetinfoResponse) {} + rpc ListFunds(ListfundsRequest) returns (ListfundsResponse) {} + rpc ListChannels(ListchannelsRequest) returns (ListchannelsResponse) {} + rpc AddGossip(AddgossipRequest) returns (AddgossipResponse) {} + rpc AutoCleanInvoice(AutocleaninvoiceRequest) returns (AutocleaninvoiceResponse) {} + rpc CheckMessage(CheckmessageRequest) returns (CheckmessageResponse) {} + rpc Close(CloseRequest) returns (CloseResponse) {} +} + +message GetinfoRequest { +} + +message GetinfoResponse { + bytes id = 1; + string alias = 2; + bytes color = 3; + uint32 num_peers = 4; + uint32 num_pending_channels = 5; + uint32 num_active_channels = 6; + uint32 num_inactive_channels = 7; + string version = 8; + string lightning_dir = 9; + uint32 blockheight = 10; + string network = 11; + Amount fees_collected_msat = 12; + repeated GetinfoAddress address = 13; + repeated GetinfoBinding binding = 14; + optional string warning_bitcoind_sync = 15; + optional string warning_lightningd_sync = 16; +} + +message GetinfoAddress { + // Getinfo.address[].type + enum GetinfoAddressType { + DNS = 0; + IPV4 = 1; + IPV6 = 2; + TORV2 = 3; + TORV3 = 4; + WEBSOCKET = 5; + } + GetinfoAddressType item_type = 1; + uint32 port = 2; + optional string address = 3; +} + +message GetinfoBinding { + // Getinfo.binding[].type + enum GetinfoBindingType { + LOCAL_SOCKET = 0; + IPV4 = 1; + IPV6 = 2; + TORV2 = 3; + TORV3 = 4; + } + GetinfoBindingType item_type = 1; + optional string address = 2; + optional uint32 port = 3; + optional string socket = 4; +} + +message ListfundsRequest { + optional bool spent = 1; +} + +message ListfundsResponse { + repeated ListfundsOutputs outputs = 1; + repeated ListfundsChannels channels = 2; +} + +message ListfundsOutputs { + // ListFunds.outputs[].status + enum ListfundsOutputsStatus { + UNCONFIRMED = 0; + CONFIRMED = 1; + SPENT = 2; + } + bytes txid = 1; + uint32 output = 2; + Amount amount_msat = 3; + bytes scriptpubkey = 4; + optional string address = 5; + optional bytes redeemscript = 6; + ListfundsOutputsStatus status = 7; + optional uint32 blockheight = 8; +} + +message ListfundsChannels { + bytes peer_id = 1; + Amount our_amount_msat = 2; + Amount amount_msat = 3; + bytes funding_txid = 4; + uint32 funding_output = 5; + bool connected = 6; + ChannelState state = 7; + optional string short_channel_id = 8; +} + +message ListchannelsRequest { + optional string short_channel_id = 1; + optional bytes source = 2; + optional bytes destination = 3; +} + +message ListchannelsResponse { + repeated ListchannelsChannels channels = 1; +} + +message ListchannelsChannels { + bytes source = 1; + bytes destination = 2; + bool public = 3; + Amount amount_msat = 4; + uint32 message_flags = 5; + uint32 channel_flags = 6; + bool active = 7; + uint32 last_update = 8; + uint32 base_fee_millisatoshi = 9; + uint32 fee_per_millionth = 10; + uint32 delay = 11; + Amount htlc_minimum_msat = 12; + optional Amount htlc_maximum_msat = 13; + bytes features = 14; +} + +message AddgossipRequest { + bytes message = 1; +} + +message AddgossipResponse { +} + +message AutocleaninvoiceRequest { + optional uint64 expired_by = 1; + optional uint64 cycle_seconds = 2; +} + +message AutocleaninvoiceResponse { + bool enabled = 1; + optional uint64 expired_by = 2; + optional uint64 cycle_seconds = 3; +} + +message CheckmessageRequest { + string message = 1; + string zbase = 2; + optional bytes pubkey = 3; +} + +message CheckmessageResponse { + bool verified = 1; + optional bytes pubkey = 2; +} + +message CloseRequest { + bytes id = 1; + optional uint32 unilateraltimeout = 2; + optional string destination = 3; + optional string fee_negotiation_step = 4; + optional bytes wrong_funding = 5; + optional bool force_lease_closed = 6; +} + +message CloseResponse { + // Close.type + enum CloseType { + MUTUAL = 0; + UNILATERAL = 1; + UNOPENED = 2; + } + CloseType item_type = 1; + optional bytes tx = 2; + optional bytes txid = 3; +} diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto new file mode 100644 index 000000000000..d08e10bf76b0 --- /dev/null +++ b/cln-grpc/proto/primitives.proto @@ -0,0 +1,33 @@ +syntax = "proto3"; +package cln; + +message Amount { + oneof unit { + uint64 millisatoshi = 1; + uint64 satoshi = 2; + uint64 bitcoin = 3; + bool all = 4; + bool any = 5; + } +} + +enum ChannelSide { + IN = 0; + OUT = 1; +} + +enum ChannelState { + Openingd = 0; + ChanneldAwaitingLockin = 1; + ChanneldNormal = 2; + ChanneldShuttingDown = 3; + ClosingdSigexchange = 4; + ClosingdComplete = 5; + AwaitingUnilateral = 6; + FundingSpendSeen = 7; + Onchain = 8; + DualopendOpenInit = 9; + DualopendAwaitingLockin = 10; +} + +message ChannelStateChangeCause {} \ No newline at end of file diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 982607aba820..219ddf4a5a07 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,4 +1,5 @@ from msggen.model import Method, CompositeField, Service +from msggen.grpc import GrpcGenerator from msggen.rust import RustGenerator from pathlib import Path import subprocess @@ -125,6 +126,12 @@ def load_jsonrpc_service(): return service +def gengrpc(service): + """Load all mapped RPC methods, wrap them in a Service, and split them into messages. + """ + fname = repo_root() / "cln-grpc" / "proto" / "node.proto" + dest = open(fname, "w") + GrpcGenerator(dest).generate(service) def genrustjsonrpc(service): fname = repo_root() / "cln-rpc" / "src" / "model.rs" dest = open(fname, "w") @@ -133,6 +140,7 @@ def genrustjsonrpc(service): def run(): service = load_jsonrpc_service() + gengrpc(service) genrustjsonrpc(service) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py new file mode 100644 index 000000000000..d90b14f4d318 --- /dev/null +++ b/contrib/msggen/msggen/grpc.py @@ -0,0 +1,153 @@ +# A grpc model +from .model import ArrayField, Field, CompositeField, EnumField, PrimitiveField, Service +from typing import TextIO, List +from textwrap import indent, dedent +import re +import logging + + +typemap = { + 'boolean': 'bool', + 'hex': 'bytes', + 'msat': 'Amount', + 'number': 'i64', + 'pubkey': 'bytes', + 'short_channel_id': 'string', + 'signature': 'bytes', + 'string': 'string', + 'txid': 'bytes', + 'u8': 'uint32', # Yep, this is the smallest integer type in grpc... + 'u32': 'uint32', + 'u64': 'uint64', + 'u16': 'uint32', # Yeah, I know... +} + + +# Manual overrides for some of the auto-generated types for paths +overrides = { + 'ListPeers.peers[].channels[].state_changes[].old_state': "ChannelState", + 'ListPeers.peers[].channels[].state_changes[].new_state': "ChannelState", + 'ListPeers.peers[].channels[].state_changes[].cause': "ChannelStateChangeCause", + 'ListPeers.peers[].channels[].opener': "ChannelSide", + 'ListPeers.peers[].channels[].closer': "ChannelSide", + 'ListPeers.peers[].channels[].features[]': "string", + 'ListFunds.channels[].state': 'ChannelState', +} + + +class GrpcGenerator: + """A generator that generates protobuf files. + """ + + def __init__(self, dest: TextIO): + self.dest = dest + self.logger = logging.getLogger("msggen.grpc.GrpcGenerator") + + def write(self, text: str, cleanup: bool = True) -> None: + if cleanup: + self.dest.write(dedent(text)) + else: + self.dest.write(text) + + def gather_types(self, service): + """Gather all types that might need to be defined. + """ + + def gather_subfields(field: Field) -> List[Field]: + fields = [field] + + if isinstance(field, CompositeField): + for f in field.fields: + fields.extend(gather_subfields(f)) + elif isinstance(field, ArrayField): + fields = [] + fields.extend(gather_subfields(field.itemtype)) + + return fields + + types = [] + for method in service.methods: + types.extend([method.request, method.response]) + for field in method.request.fields: + types.extend(gather_subfields(field)) + for field in method.response.fields: + types.extend(gather_subfields(field)) + return types + + def generate_service(self, service: Service) -> None: + self.write(f""" + service {service.name} {{ + """) + + for method in service.methods: + self.write( + f" rpc {method.name}({method.request.typename}) returns ({method.response.typename}) {{}}\n", + cleanup=False, + ) + + self.write(f"""}} + """) + + def generate_enum(self, e: EnumField, indent=0): + self.logger.debug(f"Generating enum {e}") + prefix = "\t" * indent + self.write(f"{prefix}// {e.path}\n", False) + self.write(f"{prefix}enum {e.typename} {{\n", False) + + for i, v in enumerate(e.variants): + self.logger.debug(f"Generating enum variant {v}") + self.write(f"{prefix}\t{v.normalized()} = {i};\n", False) + + self.write(f"""{prefix}}}\n""", False) + + def generate_message(self, message: CompositeField): + self.write(f""" + message {message.typename} {{ + """) + + # Declare enums inline so they are scoped correctly in C++ + for i, f in enumerate(message.fields): + if isinstance(f, EnumField) and f.path not in overrides.keys(): + self.generate_enum(f, indent=1) + + for i, f in enumerate(message.fields): + opt = "optional " if not f.required else "" + if isinstance(f, ArrayField): + typename = typemap.get(f.itemtype.typename, f.itemtype.typename) + if f.path in overrides: + typename = overrides[f.path] + self.write(f"\trepeated {typename} {f.normalized()} = {i+1};\n", False) + elif isinstance(f, PrimitiveField): + typename = typemap.get(f.typename, f.typename) + if f.path in overrides: + typename = overrides[f.path] + self.write(f"\t{opt}{typename} {f.normalized()} = {i+1};\n", False) + elif isinstance(f, EnumField): + typename = f.typename + if f.path in overrides: + typename = overrides[f.path] + self.write(f"\t{opt}{typename} {f.normalized()} = {i+1};\n", False) + + self.write(f"""}} + """) + + def generate(self, service: Service) -> None: + """Generate the GRPC protobuf file and write to `dest` + """ + self.write(f"""syntax = "proto3";\npackage cln;\n""") + self.write(""" + // This file was automatically derived from the JSON-RPC schemas in + // `doc/schemas`. Do not edit this file manually as it would get + // overwritten. + + """) + + for i in service.includes: + self.write(f"import \"{i}\";\n") + + self.generate_service(service) + + fields = self.gather_types(service) + + for message in [f for f in fields if isinstance(f, CompositeField)]: + self.generate_message(message) diff --git a/requirements.lock b/requirements.lock index cc193eea8c3d..a6434caa4193 100644 --- a/requirements.lock +++ b/requirements.lock @@ -1,8 +1,8 @@ # -# This file is autogenerated by pip-compile with python 3.8 +# This file is autogenerated by pip-compile with python 3.9 # To update, run: # -# pip-compile --output-file=requirements.lock requirements.txt +# pip-compile --output-file=requirements.lock requirements.in # alabaster==0.7.12 # via sphinx @@ -15,9 +15,9 @@ attrs==21.2.0 babel==2.9.1 # via sphinx base58==2.0.1 - # via pyln.proto + # via -r requirements.in bitstring==3.1.9 - # via pyln.proto + # via -r requirements.in certifi==2021.5.30 # via requests cffi==1.14.6 @@ -27,17 +27,17 @@ cffi==1.14.6 charset-normalizer==2.0.6 # via requests cheroot==8.5.2 - # via pyln-testing + # via -r requirements.in click==7.1.2 # via flask coincurve==13.0.0 - # via pyln.proto + # via -r requirements.in commonmark==0.9.1 # via recommonmark crc32c==2.2.post0 - # via -r requirements.txt + # via -r requirements.in cryptography==3.4.8 - # via pyln.proto + # via -r requirements.in docutils==0.17.1 # via # recommonmark @@ -45,15 +45,21 @@ docutils==0.17.1 entrypoints==0.3 # via flake8 ephemeral-port-reserve==1.1.1 - # via pyln-testing + # via -r requirements.in execnet==1.9.0 # via pytest-xdist flake8==3.7.9 - # via -r requirements.txt + # via -r requirements.in flaky==3.7.0 - # via pyln-testing + # via -r requirements.in flask==1.1.4 - # via pyln-testing + # via -r requirements.in +grpcio==1.34.0 + # via + # -r requirements.in + # grpcio-tools +grpcio-tools==1.34.0 + # via -r requirements.in idna==3.2 # via requests imagesize==1.2.0 @@ -70,9 +76,9 @@ jinja2==2.11.3 # mrkd # sphinx jsonschema==3.2.0 - # via pyln-testing + # via -r requirements.in mako==1.1.5 - # via -r requirements.txt + # via -r requirements.in markupsafe==2.0.1 # via # jinja2 @@ -88,9 +94,9 @@ more-itertools==8.10.0 # cheroot # jaraco.functools mrkd==0.1.6 - # via -r requirements.txt + # via -r requirements.in mypy==0.910 - # via pyln.proto + # via -r requirements.in mypy-extensions==0.4.3 # via mypy packaging==21.0 @@ -101,10 +107,12 @@ plac==1.3.3 # via mrkd pluggy==0.13.1 # via pytest +protobuf==3.19.3 + # via grpcio-tools psutil==5.7.3 - # via pyln-testing + # via -r requirements.in psycopg2-binary==2.8.6 - # via pyln-testing + # via -r requirements.in py==1.10.0 # via # pytest @@ -112,7 +120,9 @@ py==1.10.0 pycodestyle==2.5.0 # via flake8 pycparser==2.20 - # via cffi + # via + # -r requirements.in + # cffi pyflakes==2.1.1 # via flake8 pygments==2.10.0 @@ -124,10 +134,10 @@ pyparsing==2.4.7 pyrsistent==0.18.0 # via jsonschema pysocks==1.7.1 - # via pyln.proto + # via -r requirements.in pytest==6.1.2 # via - # pyln-testing + # -r requirements.in # pytest-forked # pytest-rerunfailures # pytest-timeout @@ -135,22 +145,23 @@ pytest==6.1.2 pytest-forked==1.3.0 # via pytest-xdist pytest-rerunfailures==9.1.1 - # via pyln-testing + # via -r requirements.in pytest-timeout==1.4.2 - # via pyln-testing + # via -r requirements.in pytest-xdist==2.2.1 - # via pyln-testing + # via -r requirements.in python-bitcoinlib==0.11.0 - # via pyln-testing + # via -r requirements.in pytz==2021.1 # via babel recommonmark==0.7.1 - # via pyln-client + # via -r requirements.in requests==2.26.0 # via sphinx six==1.16.0 # via # cheroot + # grpcio # jsonschema snowballstemmer==2.1.0 # via sphinx @@ -169,15 +180,15 @@ sphinxcontrib-qthelp==1.0.3 sphinxcontrib-serializinghtml==1.1.5 # via sphinx toml==0.10.2 - # via pytest -typed-ast==1.4.3 - # via mypy + # via + # mypy + # pytest typing-extensions==3.10.0.2 # via mypy urllib3==1.26.7 # via requests websocket-client==1.2.1 - # via -r requirements.txt + # via -r requirements.in werkzeug==1.0.1 # via flask From 4c105d2424accbe30ecac487ab968c2ed28d0502 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 14 Jan 2022 20:01:34 +0100 Subject: [PATCH 0363/1530] cln-grpc: Generate grpc bindings from proto This is the easy way, since there's already tooling for this. --- cln-grpc/Cargo.toml | 15 +++++++++++++++ cln-grpc/build.rs | 3 +++ 2 files changed, 18 insertions(+) create mode 100644 cln-grpc/Cargo.toml create mode 100644 cln-grpc/build.rs diff --git a/cln-grpc/Cargo.toml b/cln-grpc/Cargo.toml new file mode 100644 index 000000000000..0204e4237b48 --- /dev/null +++ b/cln-grpc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "cln-grpc" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0" +log = "0.4" +cln-rpc = { path="../cln-rpc" } +tonic = { version = "^0.5", features = ["tls", "transport"] } +prost = "0.8" +hex = "0.4.3" + +[build-dependencies] +tonic-build = "^0.5" diff --git a/cln-grpc/build.rs b/cln-grpc/build.rs new file mode 100644 index 000000000000..17f86d001bdf --- /dev/null +++ b/cln-grpc/build.rs @@ -0,0 +1,3 @@ +fn main() { + tonic_build::compile_protos("proto/node.proto").unwrap(); +} From 8d3871d791c69af50038e2562051e41b4a44108d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sat, 15 Jan 2022 13:56:49 +0100 Subject: [PATCH 0364/1530] cln-grpc: Add result conversion generator to `msggen` This takes the Rust bindings and converts them into the generated protobuf bindings: > JSON-RPC -> Rust bindings -> grpc bindings -> protobuf --- cln-grpc/Makefile | 4 +- cln-grpc/src/convert.rs | 170 ++++++++++++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 6 +- contrib/msggen/msggen/grpc.py | 98 +++++++++++++++++ 4 files changed, 276 insertions(+), 2 deletions(-) create mode 100644 cln-grpc/src/convert.rs diff --git a/cln-grpc/Makefile b/cln-grpc/Makefile index 9eb6bc26e4de..f7a42b85f071 100644 --- a/cln-grpc/Makefile +++ b/cln-grpc/Makefile @@ -2,7 +2,9 @@ cln-grpc-wrongdir: $(MAKE) -C .. cln-grpc-all CLN_GRPC_EXAMPLES := -CLN_GRPC_GENALL = cln-grpc/proto/node.proto +CLN_GRPC_GENALL = cln-grpc/proto/node.proto \ + cln-grpc/src/convert.rs + DEFAULT_TARGETS += $(CLN_GRPC_EXAMPLES) $(CLN_GRPC_GENALL) $(CLN_GRPC_GENALL): $(JSON_SCHEMA) diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs new file mode 100644 index 000000000000..c5c41f81b354 --- /dev/null +++ b/cln-grpc/src/convert.rs @@ -0,0 +1,170 @@ + +// This file was automatically derived from the JSON-RPC schemas in +// `doc/schemas`. Do not edit this file manually as it would get +// overwritten. + +use std::convert::From; +#[allow(unused_imports)] +use cln_rpc::model::{responses,requests}; +use crate::pb; + +#[allow(unused_variables)] +impl From<&responses::GetinfoAddress> for pb::GetinfoAddress { + fn from(c: &responses::GetinfoAddress) -> Self { + Self { + item_type: c.item_type as i32, + port: c.port.into(), + address: c.address.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::GetinfoBinding> for pb::GetinfoBinding { + fn from(c: &responses::GetinfoBinding) -> Self { + Self { + item_type: c.item_type as i32, + address: c.address.clone(), + port: c.port.map(|v| v.into()), + socket: c.socket.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::GetinfoResponse> for pb::GetinfoResponse { + fn from(c: &responses::GetinfoResponse) -> Self { + Self { + id: hex::decode(&c.id).unwrap(), + alias: c.alias.clone(), + color: hex::decode(&c.color).unwrap(), + num_peers: c.num_peers.clone(), + num_pending_channels: c.num_pending_channels.clone(), + num_active_channels: c.num_active_channels.clone(), + num_inactive_channels: c.num_inactive_channels.clone(), + version: c.version.clone(), + lightning_dir: c.lightning_dir.clone(), + blockheight: c.blockheight.clone(), + network: c.network.clone(), + fees_collected_msat: Some(c.fees_collected_msat.into()), + address: c.address.iter().map(|s| s.into()).collect(), + binding: c.binding.iter().map(|s| s.into()).collect(), + warning_bitcoind_sync: c.warning_bitcoind_sync.clone(), + warning_lightningd_sync: c.warning_lightningd_sync.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListfundsOutputs> for pb::ListfundsOutputs { + fn from(c: &responses::ListfundsOutputs) -> Self { + Self { + txid: hex::decode(&c.txid).unwrap(), + output: c.output.clone(), + amount_msat: Some(c.amount_msat.into()), + scriptpubkey: hex::decode(&c.scriptpubkey).unwrap(), + address: c.address.clone(), + redeemscript: c.redeemscript.as_ref().map(|v| hex::decode(&v).unwrap()), + status: c.status as i32, + blockheight: c.blockheight.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListfundsChannels> for pb::ListfundsChannels { + fn from(c: &responses::ListfundsChannels) -> Self { + Self { + peer_id: hex::decode(&c.peer_id).unwrap(), + our_amount_msat: Some(c.our_amount_msat.into()), + amount_msat: Some(c.amount_msat.into()), + funding_txid: hex::decode(&c.funding_txid).unwrap(), + funding_output: c.funding_output.clone(), + connected: c.connected.clone(), + state: c.state as i32, + short_channel_id: c.short_channel_id.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListfundsResponse> for pb::ListfundsResponse { + fn from(c: &responses::ListfundsResponse) -> Self { + Self { + outputs: c.outputs.iter().map(|s| s.into()).collect(), + channels: c.channels.iter().map(|s| s.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { + fn from(c: &responses::ListchannelsChannels) -> Self { + Self { + source: hex::decode(&c.source).unwrap(), + destination: hex::decode(&c.destination).unwrap(), + public: c.public.clone(), + amount_msat: Some(c.amount_msat.into()), + message_flags: c.message_flags.into(), + channel_flags: c.channel_flags.into(), + active: c.active.clone(), + last_update: c.last_update.clone(), + base_fee_millisatoshi: c.base_fee_millisatoshi.clone(), + fee_per_millionth: c.fee_per_millionth.clone(), + delay: c.delay.clone(), + htlc_minimum_msat: Some(c.htlc_minimum_msat.into()), + htlc_maximum_msat: c.htlc_maximum_msat.map(|f| f.into()), + features: hex::decode(&c.features).unwrap(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListchannelsResponse> for pb::ListchannelsResponse { + fn from(c: &responses::ListchannelsResponse) -> Self { + Self { + channels: c.channels.iter().map(|s| s.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::AddgossipResponse> for pb::AddgossipResponse { + fn from(c: &responses::AddgossipResponse) -> Self { + Self { + } + } +} + +#[allow(unused_variables)] +impl From<&responses::AutocleaninvoiceResponse> for pb::AutocleaninvoiceResponse { + fn from(c: &responses::AutocleaninvoiceResponse) -> Self { + Self { + enabled: c.enabled.clone(), + expired_by: c.expired_by.clone(), + cycle_seconds: c.cycle_seconds.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::CheckmessageResponse> for pb::CheckmessageResponse { + fn from(c: &responses::CheckmessageResponse) -> Self { + Self { + verified: c.verified.clone(), + pubkey: c.pubkey.as_ref().map(|v| hex::decode(&v).unwrap()), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::CloseResponse> for pb::CloseResponse { + fn from(c: &responses::CloseResponse) -> Self { + Self { + item_type: c.item_type as i32, + tx: c.tx.as_ref().map(|v| hex::decode(&v).unwrap()), + txid: c.txid.as_ref().map(|v| hex::decode(&v).unwrap()), + } + } +} + diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 219ddf4a5a07..01585c83a96d 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,5 +1,5 @@ from msggen.model import Method, CompositeField, Service -from msggen.grpc import GrpcGenerator +from msggen.grpc import GrpcGenerator, GrpcConverterGenerator from msggen.rust import RustGenerator from pathlib import Path import subprocess @@ -132,6 +132,10 @@ def gengrpc(service): fname = repo_root() / "cln-grpc" / "proto" / "node.proto" dest = open(fname, "w") GrpcGenerator(dest).generate(service) + + fname = repo_root() / "cln-grpc" / "src" / "convert.rs" + dest = open(fname, "w") + GrpcConverterGenerator(dest).generate(service) def genrustjsonrpc(service): fname = repo_root() / "cln-rpc" / "src" / "model.rs" dest = open(fname, "w") diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index d90b14f4d318..54d4976c89e7 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -151,3 +151,101 @@ def generate(self, service: Service) -> None: for message in [f for f in fields if isinstance(f, CompositeField)]: self.generate_message(message) + + +class GrpcConverterGenerator: + def __init__(self, dest: TextIO): + self.dest = dest + self.logger = logging.getLogger("msggen.grpc.GrpcConversionGenerator") + + def generate_array(self, prefix, field: ArrayField): + if isinstance(field.itemtype, CompositeField): + self.generate_composite(prefix, field.itemtype) + + def generate_composite(self, prefix, field: CompositeField): + """Generates the conversions from JSON-RPC to GRPC. + """ + # First pass: generate any sub-fields before we generate the + # top-level field itself. + for f in field.fields: + if isinstance(f, ArrayField): + self.generate_array(prefix, f) + + # And now we can convert the current field: + self.write(f"""\ + #[allow(unused_variables)] + impl From<&{prefix}::{field.typename}> for pb::{field.typename} {{ + fn from(c: &{prefix}::{field.typename}) -> Self {{ + Self {{ + """) + + for f in field.fields: + name = f.normalized() + if isinstance(f, ArrayField): + self.write(f"{name}: c.{name}.iter().map(|s| s.into()).collect(),\n", numindent=3) + + elif isinstance(f, EnumField): + self.write(f"{name}: c.{name} as i32,\n", numindent=3) + + elif isinstance(f, PrimitiveField): + typ = f.typename + ("?" if not f.required else "") + # We may need to reduce or increase the size of some + # types, or have some conversion such as + # hex-decoding. Also includes the `Some()` that grpc + # requires for non-native types. + rhs = { + 'u8': f'c.{name}.into()', + 'u16': f'c.{name}.into()', + 'u16?': f'c.{name}.map(|v| v.into())', + 'msat': f'Some(c.{name}.into())', + 'msat?': f'c.{name}.map(|f| f.into())', + 'pubkey': f'hex::decode(&c.{name}).unwrap()', + 'pubkey?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', + 'hex': f'hex::decode(&c.{name}).unwrap()', + 'hex?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', + 'txid': f'hex::decode(&c.{name}).unwrap()', + 'txid?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', + }.get( + typ, + f'c.{name}.clone()' # default to just assignment + ) + self.write(f"{name}: {rhs},\n", numindent=3) + + self.write(f"""\ + }} + }} + }} + + """) + + def generate_requests(self, service): + for meth in service.methods: + req = meth.request + self.generate_composite("requests", req) + + def generate_responses(self, service): + for meth in service.methods: + res = meth.response + self.generate_composite("responses", res) + + def generate(self, service: Service) -> None: + self.write(""" + // This file was automatically derived from the JSON-RPC schemas in + // `doc/schemas`. Do not edit this file manually as it would get + // overwritten. + + use std::convert::From; + #[allow(unused_imports)] + use cln_rpc::model::{responses,requests}; + use crate::pb; + + """) + + self.generate_responses(service) + + def write(self, text: str, numindent: int = 0) -> None: + raw = dedent(text) + if numindent > 0: + raw = indent(text, " " * numindent) + + self.dest.write(raw) From 5d6e9d6daec2e324ff1695ad7cdcc4b7d4387533 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 16 Jan 2022 16:12:54 +0100 Subject: [PATCH 0365/1530] cln-grpc: Add generation of request conversion This is taking protobuf requests on one side and converting them into the JSON-RPC requests. --- cln-grpc/src/convert.rs | 72 +++++++++++++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 5 ++- contrib/msggen/msggen/grpc.py | 55 +++++++++++++++++++++++ 3 files changed, 131 insertions(+), 1 deletion(-) diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index c5c41f81b354..a7c5caa25e2e 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -168,3 +168,75 @@ impl From<&responses::CloseResponse> for pb::CloseResponse { } } +#[allow(unused_variables)] +impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { + fn from(c: &pb::GetinfoRequest) -> Self { + Self { + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListfundsRequest> for requests::ListfundsRequest { + fn from(c: &pb::ListfundsRequest) -> Self { + Self { + spent: c.spent.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListchannelsRequest> for requests::ListchannelsRequest { + fn from(c: &pb::ListchannelsRequest) -> Self { + Self { + short_channel_id: c.short_channel_id.clone(), + source: c.source.clone().map(|v| hex::encode(v)), + destination: c.destination.clone().map(|v| hex::encode(v)), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::AddgossipRequest> for requests::AddgossipRequest { + fn from(c: &pb::AddgossipRequest) -> Self { + Self { + message: hex::encode(&c.message), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::AutocleaninvoiceRequest> for requests::AutocleaninvoiceRequest { + fn from(c: &pb::AutocleaninvoiceRequest) -> Self { + Self { + expired_by: c.expired_by.clone(), + cycle_seconds: c.cycle_seconds.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::CheckmessageRequest> for requests::CheckmessageRequest { + fn from(c: &pb::CheckmessageRequest) -> Self { + Self { + message: c.message.clone(), + zbase: c.zbase.clone(), + pubkey: c.pubkey.clone().map(|v| hex::encode(v)), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::CloseRequest> for requests::CloseRequest { + fn from(c: &pb::CloseRequest) -> Self { + Self { + id: hex::encode(&c.id), + unilateraltimeout: c.unilateraltimeout.clone(), + destination: c.destination.clone(), + fee_negotiation_step: c.fee_negotiation_step.clone(), + wrong_funding: c.wrong_funding.clone().map(|v| hex::encode(v)), + force_lease_closed: c.force_lease_closed.clone(), + } + } +} + diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 01585c83a96d..98747a18bec4 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,5 +1,5 @@ from msggen.model import Method, CompositeField, Service -from msggen.grpc import GrpcGenerator, GrpcConverterGenerator +from msggen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator from msggen.rust import RustGenerator from pathlib import Path import subprocess @@ -136,6 +136,9 @@ def gengrpc(service): fname = repo_root() / "cln-grpc" / "src" / "convert.rs" dest = open(fname, "w") GrpcConverterGenerator(dest).generate(service) + GrpcUnconverterGenerator(dest).generate(service) + + def genrustjsonrpc(service): fname = repo_root() / "cln-rpc" / "src" / "model.rs" dest = open(fname, "w") diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 54d4976c89e7..aed92378b5b6 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -249,3 +249,58 @@ def write(self, text: str, numindent: int = 0) -> None: raw = indent(text, " " * numindent) self.dest.write(raw) + + +class GrpcUnconverterGenerator(GrpcConverterGenerator): + """Generator to generate the conversions from GRPC to JSON-RPC (for requests). + """ + def generate(self, service: Service): + self.generate_requests(service) + + def generate_composite(self, prefix, field: CompositeField) -> None: + # First pass: generate any sub-fields before we generate the + # top-level field itself. + for f in field.fields: + if isinstance(f, ArrayField): + self.generate_array(prefix, f) + + # And now we can convert the current field: + self.write(f"""\ + #[allow(unused_variables)] + impl From<&pb::{field.typename}> for {prefix}::{field.typename} {{ + fn from(c: &pb::{field.typename}) -> Self {{ + Self {{ + """) + + for f in field.fields: + name = f.normalized() + if isinstance(f, ArrayField): + self.write(f"{name}: c.{name}.iter().map(|s| s.into()).collect(),\n", numindent=3) + + elif isinstance(f, EnumField): + raise ValueError("enums from i32 are not implemented yet") + + elif isinstance(f, PrimitiveField): + typ = f.typename + ("?" if not f.required else "") + # We may need to reduce or increase the size of some + # types, or have some conversion such as + # hex-decoding. Also includes the `Some()` that grpc + # requires for non-native types. + rhs = { + 'hex': f'hex::encode(&c.{name})', + 'txid?': f'c.{name}.clone().map(|v| hex::encode(v))', + 'pubkey': f'hex::encode(&c.{name})', + 'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))', + }.get( + typ, + f'c.{name}.clone()' # default to just assignment + ) + self.write(f"{name}: {rhs},\n", numindent=3) + + self.write(f"""\ + }} + }} + }} + + """) + From 62dc0782718a0cfec26dba725cc1391f162a8576 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 16 Jan 2022 16:38:21 +0100 Subject: [PATCH 0366/1530] cln-grpc: Generate server dispatcher The server doesn't do much more than unwrapping the request from its grpc envelope, convert it into the matching JSON-RPC binding struct, initiate the RPC connection (until we have connection pooling), and then forwards the converted request. The inverse then happens for the result. --- cln-grpc/Makefile | 3 +- cln-grpc/src/server.rs | 240 ++++++++++++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 6 +- contrib/msggen/msggen/grpc.py | 72 +++++++++ 4 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 cln-grpc/src/server.rs diff --git a/cln-grpc/Makefile b/cln-grpc/Makefile index f7a42b85f071..993286be7fbb 100644 --- a/cln-grpc/Makefile +++ b/cln-grpc/Makefile @@ -3,7 +3,8 @@ cln-grpc-wrongdir: CLN_GRPC_EXAMPLES := CLN_GRPC_GENALL = cln-grpc/proto/node.proto \ - cln-grpc/src/convert.rs + cln-grpc/src/convert.rs \ + cln-grpc/src/server.rs DEFAULT_TARGETS += $(CLN_GRPC_EXAMPLES) $(CLN_GRPC_GENALL) diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs new file mode 100644 index 000000000000..b1bb9d8c1b30 --- /dev/null +++ b/cln-grpc/src/server.rs @@ -0,0 +1,240 @@ +use crate::pb::node_server::Node; +use crate::pb; +use cln_rpc::{Request, Response, ClnRpc}; +use anyhow::Result; +use std::path::{Path, PathBuf}; +use cln_rpc::model::requests; +use log::debug; +use crate::convert::*; +use tonic::{Code, Status}; + +#[derive(Clone)] +pub struct Server +{ + rpc_path: PathBuf, +} + +impl Server +{ + pub async fn new(path: &Path) -> Result + { + Ok(Self { + rpc_path: path.to_path_buf(), + }) + } +} + +#[tonic::async_trait] +impl Node for Server +{ +async fn getinfo( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::GetinfoRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Getinfo(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Getinfo: {:?}", e)))?; + match result { + Response::Getinfo(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Getinfo", + r + ) + )), + } + +} + +async fn list_funds( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListfundsRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListFunds(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListFunds: {:?}", e)))?; + match result { + Response::ListFunds(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListFunds", + r + ) + )), + } + +} + +async fn list_channels( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListchannelsRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListChannels(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListChannels: {:?}", e)))?; + match result { + Response::ListChannels(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListChannels", + r + ) + )), + } + +} + +async fn add_gossip( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::AddgossipRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::AddGossip(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method AddGossip: {:?}", e)))?; + match result { + Response::AddGossip(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call AddGossip", + r + ) + )), + } + +} + +async fn auto_clean_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::AutocleaninvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::AutoCleanInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method AutoCleanInvoice: {:?}", e)))?; + match result { + Response::AutoCleanInvoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call AutoCleanInvoice", + r + ) + )), + } + +} + +async fn check_message( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::CheckmessageRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::CheckMessage(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method CheckMessage: {:?}", e)))?; + match result { + Response::CheckMessage(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call CheckMessage", + r + ) + )), + } + +} + +async fn close( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::CloseRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Close(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Close: {:?}", e)))?; + match result { + Response::Close(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Close", + r + ) + )), + } + +} + +} diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 98747a18bec4..df4addea0be2 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,5 +1,5 @@ from msggen.model import Method, CompositeField, Service -from msggen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator +from msggen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator from msggen.rust import RustGenerator from pathlib import Path import subprocess @@ -138,6 +138,10 @@ def gengrpc(service): GrpcConverterGenerator(dest).generate(service) GrpcUnconverterGenerator(dest).generate(service) + fname = repo_root() / "cln-grpc" / "src" / "server.rs" + dest = open(fname, "w") + GrpcServerGenerator(dest).generate(service) + def genrustjsonrpc(service): fname = repo_root() / "cln-rpc" / "src" / "model.rs" diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index aed92378b5b6..07347c3d79e5 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -304,3 +304,75 @@ def generate_composite(self, prefix, field: CompositeField) -> None: """) + +class GrpcServerGenerator(GrpcConverterGenerator): + def generate(self, service: Service) -> None: + self.write(f"""\ + use crate::pb::node_server::Node; + use crate::pb; + use cln_rpc::{{Request, Response, ClnRpc}}; + use anyhow::Result; + use std::path::{{Path, PathBuf}}; + use cln_rpc::model::requests; + use log::debug; + use crate::convert::*; + use tonic::{{Code, Status}}; + + #[derive(Clone)] + pub struct Server + {{ + rpc_path: PathBuf, + }} + + impl Server + {{ + pub async fn new(path: &Path) -> Result + {{ + Ok(Self {{ + rpc_path: path.to_path_buf(), + }}) + }} + }} + + #[tonic::async_trait] + impl Node for Server + {{ + """) + + for method in service.methods: + # Tonic will convert to snake-case, so we have to do it here too + name = re.sub(r'(?, + ) -> Result, tonic::Status> {{ + let req = request.into_inner(); + let req: requests::{method.request.typename} = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::{method.name}(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method {method.name}: {{:?}}", e)))?; + match result {{ + Response::{method.name}(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {{:?}} to method call {method.name}", + r + ) + )), + }} + + }}\n\n""", numindent=0) + + self.write(f"""\ + }} + """, numindent=0) From 24e44ecbb616704ceeb42a630f7bd5b177062e9b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 16 Jan 2022 16:39:47 +0100 Subject: [PATCH 0367/1530] cln-grpc: Add glue to get all pieces to work together --- Cargo.toml | 1 + cln-grpc/proto/primitives.proto | 8 +------- cln-grpc/src/lib.rs | 5 +++++ cln-grpc/src/pb.rs | 15 +++++++++++++++ cln-rpc/src/primitives.rs | 4 ++++ 5 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 cln-grpc/src/lib.rs create mode 100644 cln-grpc/src/pb.rs diff --git a/Cargo.toml b/Cargo.toml index e670e44ec858..288c893473c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] members = [ "cln-rpc", + "cln-grpc", ] diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index d08e10bf76b0..0b1064c55977 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -2,13 +2,7 @@ syntax = "proto3"; package cln; message Amount { - oneof unit { - uint64 millisatoshi = 1; - uint64 satoshi = 2; - uint64 bitcoin = 3; - bool all = 4; - bool any = 5; - } + uint64 msat = 1; } enum ChannelSide { diff --git a/cln-grpc/src/lib.rs b/cln-grpc/src/lib.rs new file mode 100644 index 000000000000..b25b91261242 --- /dev/null +++ b/cln-grpc/src/lib.rs @@ -0,0 +1,5 @@ +mod convert; +pub mod pb; +mod server; + +pub use crate::server::Server; diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs new file mode 100644 index 000000000000..6d72730cf864 --- /dev/null +++ b/cln-grpc/src/pb.rs @@ -0,0 +1,15 @@ +tonic::include_proto!("cln"); + +use cln_rpc::primitives::Amount as JAmount; + +impl From for Amount { + fn from(a: JAmount) -> Self { + Amount { msat: a.msat() } + } +} + +impl From for JAmount { + fn from(a: Amount) -> Self { + JAmount::from_msat(a.msat) + } +} diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 0a719620210b..a3317d415660 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -62,6 +62,10 @@ impl Amount { msat: 100_000_000_000 * btc, } } + + pub fn msat(&self) -> u64 { + self.msat + } } #[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)] From 494243d41c9a95b15240df15793c5b0b87db7e72 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 27 Jan 2022 14:07:26 +0100 Subject: [PATCH 0368/1530] msggen: Handle some more types in request conversions --- contrib/msggen/msggen/grpc.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 07347c3d79e5..ee8b0eeb5515 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -10,7 +10,7 @@ 'boolean': 'bool', 'hex': 'bytes', 'msat': 'Amount', - 'number': 'i64', + 'number': 'sint64', 'pubkey': 'bytes', 'short_channel_id': 'string', 'signature': 'bytes', @@ -20,6 +20,7 @@ 'u32': 'uint32', 'u64': 'uint64', 'u16': 'uint32', # Yeah, I know... + 'integer': 'sint64', } @@ -287,10 +288,14 @@ def generate_composite(self, prefix, field: CompositeField) -> None: # hex-decoding. Also includes the `Some()` that grpc # requires for non-native types. rhs = { + 'u16': f'c.{name} as u16', + 'u16?': f'c.{name}.map(|v| v as u16)', 'hex': f'hex::encode(&c.{name})', + 'hex?': f'c.{name}.clone().map(|v| hex::encode(v))', 'txid?': f'c.{name}.clone().map(|v| hex::encode(v))', 'pubkey': f'hex::encode(&c.{name})', 'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))', + 'msat': f'c.{name}.as_ref().unwrap().into()' }.get( typ, f'c.{name}.clone()' # default to just assignment From efed7d86174d1ff9b4d3f9b654e60be85bf7e168 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 27 Jan 2022 14:08:26 +0100 Subject: [PATCH 0369/1530] msggen: Support enums in requests too They are sent as i32 over protobuf, so we need to convert them into their enum representation. --- contrib/msggen/msggen/grpc.py | 7 +++++-- contrib/msggen/msggen/rust.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index ee8b0eeb5515..d79a1ea2e3d6 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -279,8 +279,11 @@ def generate_composite(self, prefix, field: CompositeField) -> None: self.write(f"{name}: c.{name}.iter().map(|s| s.into()).collect(),\n", numindent=3) elif isinstance(f, EnumField): - raise ValueError("enums from i32 are not implemented yet") - + if f.required: + self.write(f"{name}: c.{name}.into(),\n", numindent=3) + else: + self.write(f"{name}: c.{name}.map(|v| v.try_into().unwrap()),\n", numindent=3) + pass elif isinstance(f, PrimitiveField): typ = f.typename + ("?" if not f.required else "") # We may need to reduce or increase the size of some diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index a5e99cc3404a..2d9d586f87ee 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -87,6 +87,25 @@ def gen_enum(e): decl += f" {norm},\n" decl += "}\n\n" + # Implement From so we can convert from the numerical + # representation + decl += dedent(f"""\ + impl TryFrom for {e.typename} {{ + type Error = anyhow::Error; + fn try_from(c: i32) -> Result<{e.typename}, anyhow::Error> {{ + match c {{ + """) + for i, v in enumerate(e.variants): + norm = v.normalized() + # decl += f" #[serde(rename = \"{v}\")]\n" + decl += f" {i} => Ok({e.typename}::{norm}),\n" + decl += dedent(f"""\ + o => Err(anyhow::anyhow!("Unknown variant {{}} for enum {e.typename}", o)), + }} + }} + }} + """) + typename = e.typename if e.path in overrides: From bb4946a6e60c7c987daa1dda30912591d0279f1c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 27 Jan 2022 14:19:48 +0100 Subject: [PATCH 0370/1530] msggen: Support renaming methods in GRPC There is at least one clash with a built-in for the grpc server trait, namely `connect` so we add support for renaming a method when generating the scaffolding --- contrib/msggen/msggen/grpc.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index d79a1ea2e3d6..c622c5368b02 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -35,6 +35,10 @@ 'ListFunds.channels[].state': 'ChannelState', } +method_name_overrides = { + "Connect": "ConnectPeer", +} + class GrpcGenerator: """A generator that generates protobuf files. @@ -81,8 +85,9 @@ def generate_service(self, service: Service) -> None: """) for method in service.methods: + mname = method_name_overrides.get(method.name, method.name) self.write( - f" rpc {method.name}({method.request.typename}) returns ({method.response.typename}) {{}}\n", + f" rpc {mname}({method.request.typename}) returns ({method.response.typename}) {{}}\n", cleanup=False, ) @@ -348,8 +353,9 @@ def generate(self, service: Service) -> None: """) for method in service.methods: + mname = method_name_overrides.get(method.name, method.name) # Tonic will convert to snake-case, so we have to do it here too - name = re.sub(r'(? Date: Thu, 27 Jan 2022 15:23:46 +0100 Subject: [PATCH 0371/1530] cln-grpc: Add conversion from pb Amount to json Amount --- cln-grpc/src/pb.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 6d72730cf864..c027103612f6 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -13,3 +13,20 @@ impl From for JAmount { JAmount::from_msat(a.msat) } } + +impl From<&Amount> for JAmount { + fn from(a: &Amount) -> Self { + match a { + Amount { + unit: Some(amount::Unit::Millisatoshi(v)), + } => JAmount::Millisatoshi(*v), + Amount { + unit: Some(amount::Unit::Satoshi(v)), + } => JAmount::Satoshi(*v), + Amount { + unit: Some(amount::Unit::Bitcoin(v)), + } => JAmount::Bitcoin(*v), + o => panic!("Unhandled conversion from pb:Amount to json:Amount: {:?}", o), + } + } +} From fd2d126ec472de87967abca65497ffdfe6d69153 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 30 Jan 2022 16:28:49 +0100 Subject: [PATCH 0372/1530] cln-grpc: Add conversion from pb Amount to json Amount --- cln-grpc/src/pb.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index c027103612f6..6d72730cf864 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -13,20 +13,3 @@ impl From for JAmount { JAmount::from_msat(a.msat) } } - -impl From<&Amount> for JAmount { - fn from(a: &Amount) -> Self { - match a { - Amount { - unit: Some(amount::Unit::Millisatoshi(v)), - } => JAmount::Millisatoshi(*v), - Amount { - unit: Some(amount::Unit::Satoshi(v)), - } => JAmount::Satoshi(*v), - Amount { - unit: Some(amount::Unit::Bitcoin(v)), - } => JAmount::Bitcoin(*v), - o => panic!("Unhandled conversion from pb:Amount to json:Amount: {:?}", o), - } - } -} From b73405a4c2e8f176810bacd728b34be283aa88db Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 27 Jan 2022 18:14:17 +0100 Subject: [PATCH 0373/1530] cln-grpc: Allow fields to be mapped to None to ignore them `listpeers` is a rather deeply nested structure which has a couple of caveats, namely that we use the same enum multiple times, which causes naming clashes. So we truncate the state_changes[]. We can later map them if needed, but it'll get much easier once we have an abstract model description that isn't JSON schema, which unrolls all types, causing us to generate those enums multiple times. --- contrib/msggen/msggen/grpc.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index c622c5368b02..5f0421b92ceb 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -26,9 +26,9 @@ # Manual overrides for some of the auto-generated types for paths overrides = { - 'ListPeers.peers[].channels[].state_changes[].old_state': "ChannelState", - 'ListPeers.peers[].channels[].state_changes[].new_state': "ChannelState", - 'ListPeers.peers[].channels[].state_changes[].cause': "ChannelStateChangeCause", + # Truncate the tree here, it's a complex structure with identitcal + # types + 'ListPeers.peers[].channels[].state_changes[]': None, 'ListPeers.peers[].channels[].opener': "ChannelSide", 'ListPeers.peers[].channels[].closer': "ChannelSide", 'ListPeers.peers[].channels[].features[]': "string", @@ -107,6 +107,9 @@ def generate_enum(self, e: EnumField, indent=0): self.write(f"""{prefix}}}\n""", False) def generate_message(self, message: CompositeField): + if overrides.get(message.path, "") is None: + return + self.write(f""" message {message.typename} {{ """) @@ -117,6 +120,9 @@ def generate_message(self, message: CompositeField): self.generate_enum(f, indent=1) for i, f in enumerate(message.fields): + if overrides.get(f.path, "") is None: + continue + opt = "optional " if not f.required else "" if isinstance(f, ArrayField): typename = typemap.get(f.itemtype.typename, f.itemtype.typename) @@ -171,6 +177,9 @@ def generate_array(self, prefix, field: ArrayField): def generate_composite(self, prefix, field: CompositeField): """Generates the conversions from JSON-RPC to GRPC. """ + if overrides.get(field.path, "") is None: + return + # First pass: generate any sub-fields before we generate the # top-level field itself. for f in field.fields: @@ -186,12 +195,18 @@ def generate_composite(self, prefix, field: CompositeField): """) for f in field.fields: + if overrides.get(f.path, "") is None: + continue + name = f.normalized() if isinstance(f, ArrayField): self.write(f"{name}: c.{name}.iter().map(|s| s.into()).collect(),\n", numindent=3) elif isinstance(f, EnumField): - self.write(f"{name}: c.{name} as i32,\n", numindent=3) + if f.required: + self.write(f"{name}: c.{name} as i32,\n", numindent=3) + else: + self.write(f"{name}: c.{name}.map(|v| v as i32),\n", numindent=3) elif isinstance(f, PrimitiveField): typ = f.typename + ("?" if not f.required else "") @@ -266,6 +281,9 @@ def generate(self, service: Service): def generate_composite(self, prefix, field: CompositeField) -> None: # First pass: generate any sub-fields before we generate the # top-level field itself. + if overrides.get(field.path, "") is None: + return + for f in field.fields: if isinstance(f, ArrayField): self.generate_array(prefix, f) From 75f0b8e916862dd524ce7607a3fe91a541dba022 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 27 Jan 2022 18:17:10 +0100 Subject: [PATCH 0374/1530] cln-grpc: Add `listpeers` RPC method This is pretty much the hardest to map, but we map it correctly, with the exception of the state_changes[] array we truncated out in the last commit. --- cln-grpc/proto/node.proto | 147 ++++++++++++ cln-grpc/src/convert.rs | 129 ++++++++++ cln-grpc/src/server.rs | 30 +++ cln-rpc/src/model.rs | 381 +++++++++++++++++++++++++++++- contrib/msggen/msggen/__main__.py | 2 +- 5 files changed, 687 insertions(+), 2 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index e7d7f5267565..76610e9f6209 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -9,6 +9,7 @@ import "primitives.proto"; service Node { rpc Getinfo(GetinfoRequest) returns (GetinfoResponse) {} + rpc ListPeers(ListpeersRequest) returns (ListpeersResponse) {} rpc ListFunds(ListfundsRequest) returns (ListfundsResponse) {} rpc ListChannels(ListchannelsRequest) returns (ListchannelsResponse) {} rpc AddGossip(AddgossipRequest) returns (AddgossipResponse) {} @@ -69,6 +70,152 @@ message GetinfoBinding { optional string socket = 4; } +message ListpeersRequest { + optional bytes id = 1; + optional string level = 2; +} + +message ListpeersResponse { + repeated ListpeersPeers peers = 1; +} + +message ListpeersPeers { + bytes id = 1; + bool connected = 2; + repeated ListpeersPeersLog log = 3; + repeated ListpeersPeersChannels channels = 4; + repeated string netaddr = 5; + optional bytes features = 6; +} + +message ListpeersPeersLog { + // ListPeers.peers[].log[].type + enum ListpeersPeersLogType { + SKIPPED = 0; + BROKEN = 1; + UNUSUAL = 2; + INFO = 3; + DEBUG = 4; + IO_IN = 5; + IO_OUT = 6; + } + ListpeersPeersLogType item_type = 1; + optional uint32 num_skipped = 2; + optional string time = 3; + optional string source = 4; + optional string log = 5; + optional bytes node_id = 6; + optional bytes data = 7; +} + +message ListpeersPeersChannels { + // ListPeers.peers[].channels[].state + enum ListpeersPeersChannelsState { + OPENINGD = 0; + CHANNELD_AWAITING_LOCKIN = 1; + CHANNELD_NORMAL = 2; + CHANNELD_SHUTTING_DOWN = 3; + CLOSINGD_SIGEXCHANGE = 4; + CLOSINGD_COMPLETE = 5; + AWAITING_UNILATERAL = 6; + FUNDING_SPEND_SEEN = 7; + ONCHAIN = 8; + DUALOPEND_OPEN_INIT = 9; + DUALOPEND_AWAITING_LOCKIN = 10; + } + ListpeersPeersChannelsState state = 1; + optional bytes scratch_txid = 2; + optional string owner = 4; + optional string short_channel_id = 5; + optional bytes channel_id = 6; + optional bytes funding_txid = 7; + optional string initial_feerate = 8; + optional string last_feerate = 9; + optional string next_feerate = 10; + optional uint32 next_fee_step = 11; + repeated ListpeersPeersChannelsInflight inflight = 12; + optional bytes close_to = 13; + optional bool private = 14; + ChannelSide opener = 15; + optional ChannelSide closer = 16; + repeated string features = 17; + optional Amount to_us_msat = 19; + optional Amount min_to_us_msat = 20; + optional Amount max_to_us_msat = 21; + optional Amount total_msat = 22; + optional Amount fee_base_msat = 23; + optional uint32 fee_proportional_millionths = 24; + optional Amount dust_limit_msat = 25; + optional Amount max_total_htlc_in_msat = 26; + optional Amount their_reserve_msat = 27; + optional Amount our_reserve_msat = 28; + optional Amount spendable_msat = 29; + optional Amount receivable_msat = 30; + optional Amount minimum_htlc_in_msat = 31; + optional uint32 their_to_self_delay = 32; + optional uint32 our_to_self_delay = 33; + optional uint32 max_accepted_htlcs = 34; + repeated string status = 36; + optional uint64 in_payments_offered = 37; + optional Amount in_offered_msat = 38; + optional uint64 in_payments_fulfilled = 39; + optional Amount in_fulfilled_msat = 40; + optional uint64 out_payments_offered = 41; + optional Amount out_offered_msat = 42; + optional uint64 out_payments_fulfilled = 43; + optional Amount out_fulfilled_msat = 44; + repeated ListpeersPeersChannelsHtlcs htlcs = 45; + optional string close_to_addr = 46; +} + +message ListpeersPeersChannelsFeerate { + uint32 perkw = 1; + uint32 perkb = 2; +} + +message ListpeersPeersChannelsInflight { + bytes funding_txid = 1; + uint32 funding_outnum = 2; + string feerate = 3; + Amount total_funding_msat = 4; + Amount our_funding_msat = 5; + bytes scratch_txid = 6; +} + +message ListpeersPeersChannelsFunding { + Amount local_msat = 1; + Amount remote_msat = 2; +} + +message ListpeersPeersChannelsHtlcs { + // ListPeers.peers[].channels[].htlcs[].direction + enum ListpeersPeersChannelsHtlcsDirection { + IN = 0; + OUT = 1; + } + // ListPeers.peers[].channels[].htlcs[].state + enum ListpeersPeersChannelsHtlcsState { + SENT_ADD_HTLC = 0; + SENT_ADD_COMMIT = 1; + RCVD_ADD_REVOCATION = 2; + RCVD_ADD_ACK_COMMIT = 3; + SENT_ADD_ACK_REVOCATION = 4; + RCVD_REMOVE_HTLC = 5; + RCVD_REMOVE_COMMIT = 6; + SENT_REMOVE_REVOCATION = 7; + SENT_REMOVE_ACK_COMMIT = 8; + RCVD_REMOVE_ACK_REVOCATION = 9; + } + ListpeersPeersChannelsHtlcsDirection direction = 1; + uint64 id = 2; + Amount amount_msat = 3; + uint32 expiry = 4; + bytes payment_hash = 5; + optional bool local_trimmed = 6; + optional string status = 7; + ListpeersPeersChannelsHtlcsState state = 8; +} + message ListfundsRequest { optional bool spent = 1; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index a7c5caa25e2e..3efce232d4b3 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -55,6 +55,125 @@ impl From<&responses::GetinfoResponse> for pb::GetinfoResponse { } } +#[allow(unused_variables)] +impl From<&responses::ListpeersPeersLog> for pb::ListpeersPeersLog { + fn from(c: &responses::ListpeersPeersLog) -> Self { + Self { + item_type: c.item_type as i32, + num_skipped: c.num_skipped.clone(), + time: c.time.clone(), + source: c.source.clone(), + log: c.log.clone(), + node_id: c.node_id.as_ref().map(|v| hex::decode(&v).unwrap()), + data: c.data.as_ref().map(|v| hex::decode(&v).unwrap()), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListpeersPeersChannelsInflight> for pb::ListpeersPeersChannelsInflight { + fn from(c: &responses::ListpeersPeersChannelsInflight) -> Self { + Self { + funding_txid: hex::decode(&c.funding_txid).unwrap(), + funding_outnum: c.funding_outnum.clone(), + feerate: c.feerate.clone(), + total_funding_msat: Some(c.total_funding_msat.into()), + our_funding_msat: Some(c.our_funding_msat.into()), + scratch_txid: hex::decode(&c.scratch_txid).unwrap(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListpeersPeersChannelsHtlcs> for pb::ListpeersPeersChannelsHtlcs { + fn from(c: &responses::ListpeersPeersChannelsHtlcs) -> Self { + Self { + direction: c.direction as i32, + id: c.id.clone(), + amount_msat: Some(c.amount_msat.into()), + expiry: c.expiry.clone(), + payment_hash: hex::decode(&c.payment_hash).unwrap(), + local_trimmed: c.local_trimmed.clone(), + status: c.status.clone(), + state: c.state as i32, + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { + fn from(c: &responses::ListpeersPeersChannels) -> Self { + Self { + state: c.state as i32, + scratch_txid: c.scratch_txid.as_ref().map(|v| hex::decode(&v).unwrap()), + owner: c.owner.clone(), + short_channel_id: c.short_channel_id.clone(), + channel_id: c.channel_id.as_ref().map(|v| hex::decode(&v).unwrap()), + funding_txid: c.funding_txid.as_ref().map(|v| hex::decode(&v).unwrap()), + initial_feerate: c.initial_feerate.clone(), + last_feerate: c.last_feerate.clone(), + next_feerate: c.next_feerate.clone(), + next_fee_step: c.next_fee_step.clone(), + inflight: c.inflight.iter().map(|s| s.into()).collect(), + close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), + private: c.private.clone(), + opener: c.opener as i32, + closer: c.closer.map(|v| v as i32), + features: c.features.iter().map(|s| s.into()).collect(), + to_us_msat: c.to_us_msat.map(|f| f.into()), + min_to_us_msat: c.min_to_us_msat.map(|f| f.into()), + max_to_us_msat: c.max_to_us_msat.map(|f| f.into()), + total_msat: c.total_msat.map(|f| f.into()), + fee_base_msat: c.fee_base_msat.map(|f| f.into()), + fee_proportional_millionths: c.fee_proportional_millionths.clone(), + dust_limit_msat: c.dust_limit_msat.map(|f| f.into()), + max_total_htlc_in_msat: c.max_total_htlc_in_msat.map(|f| f.into()), + their_reserve_msat: c.their_reserve_msat.map(|f| f.into()), + our_reserve_msat: c.our_reserve_msat.map(|f| f.into()), + spendable_msat: c.spendable_msat.map(|f| f.into()), + receivable_msat: c.receivable_msat.map(|f| f.into()), + minimum_htlc_in_msat: c.minimum_htlc_in_msat.map(|f| f.into()), + their_to_self_delay: c.their_to_self_delay.clone(), + our_to_self_delay: c.our_to_self_delay.clone(), + max_accepted_htlcs: c.max_accepted_htlcs.clone(), + status: c.status.iter().map(|s| s.into()).collect(), + in_payments_offered: c.in_payments_offered.clone(), + in_offered_msat: c.in_offered_msat.map(|f| f.into()), + in_payments_fulfilled: c.in_payments_fulfilled.clone(), + in_fulfilled_msat: c.in_fulfilled_msat.map(|f| f.into()), + out_payments_offered: c.out_payments_offered.clone(), + out_offered_msat: c.out_offered_msat.map(|f| f.into()), + out_payments_fulfilled: c.out_payments_fulfilled.clone(), + out_fulfilled_msat: c.out_fulfilled_msat.map(|f| f.into()), + htlcs: c.htlcs.iter().map(|s| s.into()).collect(), + close_to_addr: c.close_to_addr.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { + fn from(c: &responses::ListpeersPeers) -> Self { + Self { + id: hex::decode(&c.id).unwrap(), + connected: c.connected.clone(), + log: c.log.iter().map(|s| s.into()).collect(), + channels: c.channels.iter().map(|s| s.into()).collect(), + netaddr: c.netaddr.iter().map(|s| s.into()).collect(), + features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListpeersResponse> for pb::ListpeersResponse { + fn from(c: &responses::ListpeersResponse) -> Self { + Self { + peers: c.peers.iter().map(|s| s.into()).collect(), + } + } +} + #[allow(unused_variables)] impl From<&responses::ListfundsOutputs> for pb::ListfundsOutputs { fn from(c: &responses::ListfundsOutputs) -> Self { @@ -176,6 +295,16 @@ impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { } } +#[allow(unused_variables)] +impl From<&pb::ListpeersRequest> for requests::ListpeersRequest { + fn from(c: &pb::ListpeersRequest) -> Self { + Self { + id: c.id.clone().map(|v| hex::encode(v)), + level: c.level.clone(), + } + } +} + #[allow(unused_variables)] impl From<&pb::ListfundsRequest> for requests::ListfundsRequest { fn from(c: &pb::ListfundsRequest) -> Self { diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index b1bb9d8c1b30..21b06a67c066 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -57,6 +57,36 @@ async fn getinfo( } +async fn list_peers( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListpeersRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListPeers(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListPeers: {:?}", e)))?; + match result { + Response::ListPeers(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListPeers", + r + ) + )), + } + +} + async fn list_funds( &self, request: tonic::Request, diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index ebc697023bfd..d7c6e699532b 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -2,7 +2,7 @@ //! This file was automatically generated using the following command: //! //! ```bash -//! msggen +//! contrib/msggen/msggen/__main__.py //! ``` //! //! Do not edit this file, it'll be overwritten. Rather edit the schema that @@ -17,6 +17,7 @@ pub use responses::*; #[serde(rename_all = "lowercase")] pub enum Request { Getinfo(requests::GetinfoRequest), + ListPeers(requests::ListpeersRequest), ListFunds(requests::ListfundsRequest), ListChannels(requests::ListchannelsRequest), AddGossip(requests::AddgossipRequest), @@ -30,6 +31,7 @@ pub enum Request { #[serde(rename_all = "lowercase")] pub enum Response { Getinfo(responses::GetinfoResponse), + ListPeers(responses::ListpeersResponse), ListFunds(responses::ListfundsResponse), ListChannels(responses::ListchannelsResponse), AddGossip(responses::AddgossipResponse), @@ -48,6 +50,14 @@ pub mod requests { pub struct GetinfoRequest { } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersRequest { + #[serde(alias = "id", skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(alias = "level", skip_serializing_if = "Option::is_none")] + pub level: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListfundsRequest { #[serde(alias = "spent", skip_serializing_if = "Option::is_none")] @@ -125,6 +135,20 @@ pub mod responses { WEBSOCKET, } + impl TryFrom for GetinfoAddressType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(GetinfoAddressType::DNS), + 1 => Ok(GetinfoAddressType::IPV4), + 2 => Ok(GetinfoAddressType::IPV6), + 3 => Ok(GetinfoAddressType::TORV2), + 4 => Ok(GetinfoAddressType::TORV3), + 5 => Ok(GetinfoAddressType::WEBSOCKET), + o => Err(anyhow::anyhow!("Unknown variant {} for enum GetinfoAddressType", o)), + } + } + } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoAddress { // Path `Getinfo.address[].type` @@ -147,6 +171,19 @@ pub mod responses { TORV3, } + impl TryFrom for GetinfoBindingType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(GetinfoBindingType::LOCAL_SOCKET), + 1 => Ok(GetinfoBindingType::IPV4), + 2 => Ok(GetinfoBindingType::IPV6), + 3 => Ok(GetinfoBindingType::TORV2), + 4 => Ok(GetinfoBindingType::TORV3), + o => Err(anyhow::anyhow!("Unknown variant {} for enum GetinfoBindingType", o)), + } + } + } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoBinding { // Path `Getinfo.binding[].type` @@ -196,6 +233,326 @@ pub mod responses { pub warning_lightningd_sync: Option, } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListpeersPeersLogType { + SKIPPED, + BROKEN, + UNUSUAL, + INFO, + DEBUG, + IO_IN, + IO_OUT, + } + + impl TryFrom for ListpeersPeersLogType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListpeersPeersLogType::SKIPPED), + 1 => Ok(ListpeersPeersLogType::BROKEN), + 2 => Ok(ListpeersPeersLogType::UNUSUAL), + 3 => Ok(ListpeersPeersLogType::INFO), + 4 => Ok(ListpeersPeersLogType::DEBUG), + 5 => Ok(ListpeersPeersLogType::IO_IN), + 6 => Ok(ListpeersPeersLogType::IO_OUT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeersPeersLogType", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersLog { + // Path `ListPeers.peers[].log[].type` + #[serde(rename = "type")] + pub item_type: ListpeersPeersLogType, + #[serde(alias = "num_skipped", skip_serializing_if = "Option::is_none")] + pub num_skipped: Option, + #[serde(alias = "time", skip_serializing_if = "Option::is_none")] + pub time: Option, + #[serde(alias = "source", skip_serializing_if = "Option::is_none")] + pub source: Option, + #[serde(alias = "log", skip_serializing_if = "Option::is_none")] + pub log: Option, + #[serde(alias = "node_id", skip_serializing_if = "Option::is_none")] + pub node_id: Option, + #[serde(alias = "data", skip_serializing_if = "Option::is_none")] + pub data: Option, + } + + /// the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListpeersPeersChannelsState { + OPENINGD, + CHANNELD_AWAITING_LOCKIN, + CHANNELD_NORMAL, + CHANNELD_SHUTTING_DOWN, + CLOSINGD_SIGEXCHANGE, + CLOSINGD_COMPLETE, + AWAITING_UNILATERAL, + FUNDING_SPEND_SEEN, + ONCHAIN, + DUALOPEND_OPEN_INIT, + DUALOPEND_AWAITING_LOCKIN, + } + + impl TryFrom for ListpeersPeersChannelsState { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListpeersPeersChannelsState::OPENINGD), + 1 => Ok(ListpeersPeersChannelsState::CHANNELD_AWAITING_LOCKIN), + 2 => Ok(ListpeersPeersChannelsState::CHANNELD_NORMAL), + 3 => Ok(ListpeersPeersChannelsState::CHANNELD_SHUTTING_DOWN), + 4 => Ok(ListpeersPeersChannelsState::CLOSINGD_SIGEXCHANGE), + 5 => Ok(ListpeersPeersChannelsState::CLOSINGD_COMPLETE), + 6 => Ok(ListpeersPeersChannelsState::AWAITING_UNILATERAL), + 7 => Ok(ListpeersPeersChannelsState::FUNDING_SPEND_SEEN), + 8 => Ok(ListpeersPeersChannelsState::ONCHAIN), + 9 => Ok(ListpeersPeersChannelsState::DUALOPEND_OPEN_INIT), + 10 => Ok(ListpeersPeersChannelsState::DUALOPEND_AWAITING_LOCKIN), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeersPeersChannelsState", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersChannelsFeerate { + #[serde(alias = "perkw")] + pub perkw: u32, + #[serde(alias = "perkb")] + pub perkb: u32, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersChannelsInflight { + #[serde(alias = "funding_txid")] + pub funding_txid: String, + #[serde(alias = "funding_outnum")] + pub funding_outnum: u32, + #[serde(alias = "feerate")] + pub feerate: String, + #[serde(alias = "total_funding_msat")] + pub total_funding_msat: Amount, + #[serde(alias = "our_funding_msat")] + pub our_funding_msat: Amount, + #[serde(alias = "scratch_txid")] + pub scratch_txid: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersChannelsFunding { + #[serde(alias = "local_msat")] + pub local_msat: Amount, + #[serde(alias = "remote_msat")] + pub remote_msat: Amount, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersChannelsState_changes { + #[serde(alias = "timestamp")] + pub timestamp: String, + // Path `ListPeers.peers[].channels[].state_changes[].old_state` + #[serde(rename = "old_state")] + pub old_state: ChannelState, + // Path `ListPeers.peers[].channels[].state_changes[].new_state` + #[serde(rename = "new_state")] + pub new_state: ChannelState, + // Path `ListPeers.peers[].channels[].state_changes[].cause` + #[serde(rename = "cause")] + pub cause: ChannelStateChangeCause, + #[serde(alias = "message")] + pub message: String, + } + + /// Whether it came from peer, or is going to peer + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListpeersPeersChannelsHtlcsDirection { + IN, + OUT, + } + + impl TryFrom for ListpeersPeersChannelsHtlcsDirection { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListpeersPeersChannelsHtlcsDirection::IN), + 1 => Ok(ListpeersPeersChannelsHtlcsDirection::OUT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeersPeersChannelsHtlcsDirection", o)), + } + } + } + /// Status of the HTLC + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListpeersPeersChannelsHtlcsState { + SENT_ADD_HTLC, + SENT_ADD_COMMIT, + RCVD_ADD_REVOCATION, + RCVD_ADD_ACK_COMMIT, + SENT_ADD_ACK_REVOCATION, + RCVD_REMOVE_HTLC, + RCVD_REMOVE_COMMIT, + SENT_REMOVE_REVOCATION, + SENT_REMOVE_ACK_COMMIT, + RCVD_REMOVE_ACK_REVOCATION, + } + + impl TryFrom for ListpeersPeersChannelsHtlcsState { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListpeersPeersChannelsHtlcsState::SENT_ADD_HTLC), + 1 => Ok(ListpeersPeersChannelsHtlcsState::SENT_ADD_COMMIT), + 2 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_ADD_REVOCATION), + 3 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_ADD_ACK_COMMIT), + 4 => Ok(ListpeersPeersChannelsHtlcsState::SENT_ADD_ACK_REVOCATION), + 5 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_REMOVE_HTLC), + 6 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_REMOVE_COMMIT), + 7 => Ok(ListpeersPeersChannelsHtlcsState::SENT_REMOVE_REVOCATION), + 8 => Ok(ListpeersPeersChannelsHtlcsState::SENT_REMOVE_ACK_COMMIT), + 9 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_REMOVE_ACK_REVOCATION), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeersPeersChannelsHtlcsState", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersChannelsHtlcs { + // Path `ListPeers.peers[].channels[].htlcs[].direction` + #[serde(rename = "direction")] + pub direction: ListpeersPeersChannelsHtlcsDirection, + #[serde(alias = "id")] + pub id: u64, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "expiry")] + pub expiry: u32, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + #[serde(alias = "local_trimmed", skip_serializing_if = "Option::is_none")] + pub local_trimmed: Option, + #[serde(alias = "status", skip_serializing_if = "Option::is_none")] + pub status: Option, + // Path `ListPeers.peers[].channels[].htlcs[].state` + #[serde(rename = "state")] + pub state: ListpeersPeersChannelsHtlcsState, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersChannels { + // Path `ListPeers.peers[].channels[].state` + #[serde(rename = "state")] + pub state: ListpeersPeersChannelsState, + #[serde(alias = "scratch_txid", skip_serializing_if = "Option::is_none")] + pub scratch_txid: Option, + #[serde(alias = "owner", skip_serializing_if = "Option::is_none")] + pub owner: Option, + #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] + pub short_channel_id: Option, + #[serde(alias = "channel_id", skip_serializing_if = "Option::is_none")] + pub channel_id: Option, + #[serde(alias = "funding_txid", skip_serializing_if = "Option::is_none")] + pub funding_txid: Option, + #[serde(alias = "initial_feerate", skip_serializing_if = "Option::is_none")] + pub initial_feerate: Option, + #[serde(alias = "last_feerate", skip_serializing_if = "Option::is_none")] + pub last_feerate: Option, + #[serde(alias = "next_feerate", skip_serializing_if = "Option::is_none")] + pub next_feerate: Option, + #[serde(alias = "next_fee_step", skip_serializing_if = "Option::is_none")] + pub next_fee_step: Option, + #[serde(alias = "inflight[]")] + pub inflight: Vec, + #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] + pub close_to: Option, + #[serde(alias = "private", skip_serializing_if = "Option::is_none")] + pub private: Option, + // Path `ListPeers.peers[].channels[].opener` + #[serde(rename = "opener")] + pub opener: ChannelSide, + pub closer: Option, + #[serde(alias = "features[]")] + pub features: Vec, + #[serde(alias = "to_us_msat", skip_serializing_if = "Option::is_none")] + pub to_us_msat: Option, + #[serde(alias = "min_to_us_msat", skip_serializing_if = "Option::is_none")] + pub min_to_us_msat: Option, + #[serde(alias = "max_to_us_msat", skip_serializing_if = "Option::is_none")] + pub max_to_us_msat: Option, + #[serde(alias = "total_msat", skip_serializing_if = "Option::is_none")] + pub total_msat: Option, + #[serde(alias = "fee_base_msat", skip_serializing_if = "Option::is_none")] + pub fee_base_msat: Option, + #[serde(alias = "fee_proportional_millionths", skip_serializing_if = "Option::is_none")] + pub fee_proportional_millionths: Option, + #[serde(alias = "dust_limit_msat", skip_serializing_if = "Option::is_none")] + pub dust_limit_msat: Option, + #[serde(alias = "max_total_htlc_in_msat", skip_serializing_if = "Option::is_none")] + pub max_total_htlc_in_msat: Option, + #[serde(alias = "their_reserve_msat", skip_serializing_if = "Option::is_none")] + pub their_reserve_msat: Option, + #[serde(alias = "our_reserve_msat", skip_serializing_if = "Option::is_none")] + pub our_reserve_msat: Option, + #[serde(alias = "spendable_msat", skip_serializing_if = "Option::is_none")] + pub spendable_msat: Option, + #[serde(alias = "receivable_msat", skip_serializing_if = "Option::is_none")] + pub receivable_msat: Option, + #[serde(alias = "minimum_htlc_in_msat", skip_serializing_if = "Option::is_none")] + pub minimum_htlc_in_msat: Option, + #[serde(alias = "their_to_self_delay", skip_serializing_if = "Option::is_none")] + pub their_to_self_delay: Option, + #[serde(alias = "our_to_self_delay", skip_serializing_if = "Option::is_none")] + pub our_to_self_delay: Option, + #[serde(alias = "max_accepted_htlcs", skip_serializing_if = "Option::is_none")] + pub max_accepted_htlcs: Option, + #[serde(alias = "state_changes[]")] + pub state_changes: Vec, + #[serde(alias = "status[]")] + pub status: Vec, + #[serde(alias = "in_payments_offered", skip_serializing_if = "Option::is_none")] + pub in_payments_offered: Option, + #[serde(alias = "in_offered_msat", skip_serializing_if = "Option::is_none")] + pub in_offered_msat: Option, + #[serde(alias = "in_payments_fulfilled", skip_serializing_if = "Option::is_none")] + pub in_payments_fulfilled: Option, + #[serde(alias = "in_fulfilled_msat", skip_serializing_if = "Option::is_none")] + pub in_fulfilled_msat: Option, + #[serde(alias = "out_payments_offered", skip_serializing_if = "Option::is_none")] + pub out_payments_offered: Option, + #[serde(alias = "out_offered_msat", skip_serializing_if = "Option::is_none")] + pub out_offered_msat: Option, + #[serde(alias = "out_payments_fulfilled", skip_serializing_if = "Option::is_none")] + pub out_payments_fulfilled: Option, + #[serde(alias = "out_fulfilled_msat", skip_serializing_if = "Option::is_none")] + pub out_fulfilled_msat: Option, + #[serde(alias = "htlcs[]")] + pub htlcs: Vec, + #[serde(alias = "close_to_addr", skip_serializing_if = "Option::is_none")] + pub close_to_addr: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeers { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "connected")] + pub connected: bool, + #[serde(alias = "log[]")] + pub log: Vec, + #[serde(alias = "channels[]")] + pub channels: Vec, + #[serde(alias = "netaddr[]")] + pub netaddr: Vec, + #[serde(alias = "features", skip_serializing_if = "Option::is_none")] + pub features: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersResponse { + #[serde(alias = "peers[]")] + pub peers: Vec, + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum ListfundsOutputsStatus { @@ -204,6 +561,17 @@ pub mod responses { SPENT, } + impl TryFrom for ListfundsOutputsStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListfundsOutputsStatus::UNCONFIRMED), + 1 => Ok(ListfundsOutputsStatus::CONFIRMED), + 2 => Ok(ListfundsOutputsStatus::SPENT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListfundsOutputsStatus", o)), + } + } + } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListfundsOutputs { #[serde(alias = "txid")] @@ -323,6 +691,17 @@ pub mod responses { UNOPENED, } + impl TryFrom for CloseType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(CloseType::MUTUAL), + 1 => Ok(CloseType::UNILATERAL), + 2 => Ok(CloseType::UNOPENED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum CloseType", o)), + } + } + } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CloseResponse { // Path `Close.type` diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index df4addea0be2..6b4069e17727 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -35,7 +35,7 @@ def load_jsonrpc_method(name): def load_jsonrpc_service(): method_names = [ "Getinfo", - # "ListPeers", + "ListPeers", "ListFunds", # "ListConfigs", "ListChannels", From 6332578070edb2b9e81dcf85daa50d2c4638b903 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 25 Feb 2022 12:06:13 +0100 Subject: [PATCH 0375/1530] rust: Use $CARGO_OPTS when building the example plugin --- cln-rpc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cln-rpc/Makefile b/cln-rpc/Makefile index 646d268d25bd..267b7eadf383 100644 --- a/cln-rpc/Makefile +++ b/cln-rpc/Makefile @@ -11,6 +11,6 @@ $(CLN_RPC_GENALL): $(JSON_SCHEMA) PYTHONPATH=contrib/msggen python3 contrib/msggen/msggen/__main__.py target/debug/examples/cln-rpc-getinfo: $(shell find cln-rpc -name *.rs) - cargo build --example cln-rpc-getinfo + cargo build ${CARGO_OPTS} --example cln-rpc-getinfo cln-rpc-all: ${CLN_RPC_GEN_ALL} ${CLN_RPC_EXAMPLES} From 7bfbe1492a77fd609fb183bc30696c9f060948b1 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 1 Mar 2022 12:55:41 -0600 Subject: [PATCH 0376/1530] test_close: sync l1 to the blockchain, so that the events are there Flake on CI coming from `penalty_in/outhtlc` due to the fact that all the events haven't arrived for the check. If we wait to sync `l1` as well as `l2`, this should resolve the flake. # We use a subset of tags in expected_2 that are used in expected_1 > tags = check_utxos_channel(l1, [channel_id], expected_1) tests/test_closing.py:726: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ tests/utils.py:321: in check_utxos_channel txid = matchup_events(u_set, evs, chans, tag_list) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ u_set = [[{'account_id': 'external', 'blockheight': 104, 'coin_type': 'bcrt', 'credit': '100000000msat', ...}, None]] evs = [('external', ['penalty'], None, None), ('external', ['penalty'], None, None), ('external', ['penalty'], None, None)] chans = ['2722a5fe49a8b5fa4004c19828f3e903632ac02712c6fe78ebea418daad2691f'] tag_list = {'0': '892c64c7d8c8f15d7bdcdcde34b615817d273d2e33d9d775cc9ff38e8e3deeb2', 'A': '1e69d2aa8d41eaeb78fec61227c02a6303e9f32898c10440fab5a849fea52227', 'B': '0e11e2ca01bf5f30b4c54522af172af055dcff8e3810f80a5069a2394cad74b5'} def matchup_events(u_set, evs, chans, tag_list): > assert len(u_set) == len(evs) and len(u_set) > 0 E AssertionError --- tests/test_closing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 4643c8e8b6ea..99da6709dc78 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -548,7 +548,7 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): bitcoind.generate_block(100) - sync_blockheight(bitcoind, [l2]) + sync_blockheight(bitcoind, [l1, l2]) wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) # Do one last pass over the logs to extract the reactions l2 sent @@ -677,7 +677,7 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): # 100 blocks later, all resolved. bitcoind.generate_block(100) - sync_blockheight(bitcoind, [l2]) + sync_blockheight(bitcoind, [l1, l2]) wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) # Do one last pass over the logs to extract the reactions l2 sent From 4386ccd842cd97f1e500eeeadb41d133cd0cc906 Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Wed, 2 Mar 2022 16:34:21 -0600 Subject: [PATCH 0377/1530] bitcoin: Comment typo fix --- bitcoin/tx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitcoin/tx.h b/bitcoin/tx.h index 0d0e7e414ed4..cb0903ccf40d 100644 --- a/bitcoin/tx.h +++ b/bitcoin/tx.h @@ -115,7 +115,7 @@ int bitcoin_tx_add_input(struct bitcoin_tx *tx, struct amount_sat amount, const u8 *scriptPubkey, const u8 *input_wscript); -/* This helps is useful because wally uses a raw byte array for txids */ +/* This is useful because wally uses a raw byte array for txids */ bool wally_tx_input_spends(const struct wally_tx_input *input, const struct bitcoin_outpoint *outpoint); From cd37753849ccc9399954d24d47c604701899a7e3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Mar 2022 08:58:29 +1030 Subject: [PATCH 0378/1530] plugins/bcli: neaten to fix spurious leak report. ``` plugin-bcli: MEMLEAK: 0x1d2a118 plugin-bcli: label=plugins/bcli.c:636:const char *[] plugin-bcli: backtrace: plugin-bcli: ccan/ccan/tal/tal.c:442 (tal_alloc_) plugin-bcli: ccan/ccan/tal/tal.c:471 (tal_alloc_arr_) plugin-bcli: plugins/bcli.c:636 (estimatefees_next) plugin-bcli: plugins/bcli.c:684 (estimatefees) plugin-bcli: plugins/libplugin.c:1307 (ld_command_handle) plugin-bcli: plugins/libplugin.c:1348 (ld_read_json_one) plugin-bcli: plugins/libplugin.c:1368 (ld_read_json) plugin-bcli: ccan/ccan/io/io.c:59 (next_plan) plugin-bcli: ccan/ccan/io/io.c:407 (do_plan) plugin-bcli: ccan/ccan/io/io.c:417 (io_ready) plugin-bcli: ccan/ccan/io/poll.c:453 (io_loop) plugin-bcli: plugins/libplugin.c:1565 (plugin_main) plugin-bcli: plugins/bcli.c:965 (main) plugin-bcli: parents: plugin-bcli: plugins/libplugin.c:1235:struct command ``` It's neater to convert start_bitcoin_cli() to take varargs, and handle take: the above "leak" is because we don't keep a pointer to the params[] array which we use to pass to start_bitcoin_cli(). Signed-off-by: Rusty Russell --- plugins/bcli.c | 124 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 83 insertions(+), 41 deletions(-) diff --git a/plugins/bcli.c b/plugins/bcli.c index d4c1bb4213c5..d65e16f192f6 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -94,14 +94,17 @@ struct bitcoin_cli { }; /* Add the n'th arg to *args, incrementing n and keeping args of size n+1 */ -static void add_arg(const char ***args, const char *arg) +static void add_arg(const char ***args, const char *arg TAKES) { + if (taken(arg)) + tal_steal(*args, arg); tal_arr_expand(args, arg); } -static const char **gather_args(const tal_t *ctx, const char *cmd, const char **cmd_args) +static const char **gather_argsv(const tal_t *ctx, const char *cmd, va_list ap) { const char **args = tal_arr(ctx, const char *, 1); + const char *arg; args[0] = bitcoind->cli ? bitcoind->cli : chainparams->cli; if (chainparams->cli_args) @@ -121,13 +124,26 @@ static const char **gather_args(const tal_t *ctx, const char *cmd, const char ** tal_fmt(args, "-rpcpassword=%s", bitcoind->rpcpass)); add_arg(&args, cmd); - for (size_t i = 0; i < tal_count(cmd_args); i++) - add_arg(&args, cmd_args[i]); + while ((arg = va_arg(ap, char *)) != NULL) + add_arg(&args, arg); add_arg(&args, NULL); return args; } +static LAST_ARG_NULL const char ** +gather_args(const tal_t *ctx, const char *cmd, ...) +{ + va_list ap; + const char **ret; + + va_start(ap, cmd); + ret = gather_argsv(ctx, cmd, ap); + va_end(ap); + + return ret; +} + static struct io_plan *read_more(struct io_conn *conn, struct bitcoin_cli *bcli) { bcli->output_bytes += bcli->new_output; @@ -299,16 +315,15 @@ static void next_bcli(enum bitcoind_prio prio) tal_add_destructor(bcli, destroy_bcli); } -/* If ctx is non-NULL, and is freed before we return, we don't call process(). - * process returns false() if it's a spurious error, and we should retry. */ static void -start_bitcoin_cli(const tal_t *ctx, - struct command *cmd, - struct command_result *(*process)(struct bitcoin_cli *), - bool nonzero_exit_ok, - enum bitcoind_prio prio, - char *method, const char **method_args, - void *stash) +start_bitcoin_cliv(const tal_t *ctx, + struct command *cmd, + struct command_result *(*process)(struct bitcoin_cli *), + bool nonzero_exit_ok, + enum bitcoind_prio prio, + void *stash, + const char *method, + va_list ap) { struct bitcoin_cli *bcli = tal(bitcoind, struct bitcoin_cli); @@ -321,13 +336,33 @@ start_bitcoin_cli(const tal_t *ctx, else bcli->exitstatus = NULL; - bcli->args = gather_args(bcli, method, method_args); + bcli->args = gather_argsv(bcli, method, ap); bcli->stash = stash; list_add_tail(&bitcoind->pending[bcli->prio], &bcli->list); next_bcli(bcli->prio); } +/* If ctx is non-NULL, and is freed before we return, we don't call process(). + * process returns false() if it's a spurious error, and we should retry. */ +static void LAST_ARG_NULL +start_bitcoin_cli(const tal_t *ctx, + struct command *cmd, + struct command_result *(*process)(struct bitcoin_cli *), + bool nonzero_exit_ok, + enum bitcoind_prio prio, + void *stash, + const char *method, + ...) +{ + va_list ap; + + va_start(ap, method); + start_bitcoin_cliv(ctx, cmd, process, nonzero_exit_ok, prio, stash, method, + ap); + va_end(ap); +} + static void strip_trailing_whitespace(char *str, size_t len) { size_t stripped_len = len; @@ -539,7 +574,6 @@ getrawblockbyheight_notfound(struct bitcoin_cli *bcli) static struct command_result *process_getblockhash(struct bitcoin_cli *bcli) { - const char **params; struct getrawblock_stash *stash = bcli->stash; /* If it failed with error 8, give an empty response. */ @@ -556,12 +590,13 @@ static struct command_result *process_getblockhash(struct bitcoin_cli *bcli) return command_err_bcli_badjson(bcli, "bad blockhash"); } - params = tal_arr(bcli->cmd, const char *, 2); - params[0] = stash->block_hash; - /* Non-verbose: raw block. */ - params[1] = "0"; start_bitcoin_cli(NULL, bcli->cmd, process_getrawblock, false, - BITCOIND_HIGH_PRIO, "getblock", params, stash); + BITCOIND_HIGH_PRIO, stash, + "getblock", + stash->block_hash, + /* Non-verbose: raw block. */ + "0", + NULL); return command_still_pending(bcli->cmd); } @@ -576,7 +611,6 @@ static struct command_result *getrawblockbyheight(struct command *cmd, { struct getrawblock_stash *stash; u32 *height; - const char **params; /* bitcoin-cli wants a string. */ if (!param(cmd, buf, toks, @@ -586,11 +620,13 @@ static struct command_result *getrawblockbyheight(struct command *cmd, stash = tal(cmd, struct getrawblock_stash); stash->block_height = *height; + tal_free(height); - params = tal_arr(cmd, const char *, 1); - params[0] = tal_fmt(params, "%u", *height); start_bitcoin_cli(NULL, cmd, process_getblockhash, true, - BITCOIND_LOW_PRIO, "getblockhash", params, stash); + BITCOIND_LOW_PRIO, stash, + "getblockhash", + take(tal_fmt(NULL, "%u", stash->block_height)), + NULL); return command_still_pending(cmd); } @@ -604,10 +640,11 @@ static struct command_result *getchaininfo(struct command *cmd, const jsmntok_t *toks UNUSED) { if (!param(cmd, buf, toks, NULL)) - return command_param_failed(); + return command_param_failed(); start_bitcoin_cli(NULL, cmd, process_getblockchaininfo, false, - BITCOIND_HIGH_PRIO, "getblockchaininfo", NULL, NULL); + BITCOIND_HIGH_PRIO, NULL, + "getblockchaininfo", NULL); return command_still_pending(cmd); } @@ -633,12 +670,13 @@ static struct command_result *estimatefees_next(struct command *cmd, struct json_stream *response; if (stash->cursor < ARRAY_SIZE(stash->perkb)) { - const char **params = tal_arr(cmd, const char *, 2); - - params[0] = tal_fmt(params, "%u", estimatefee_params[stash->cursor].blocks); - params[1] = estimatefee_params[stash->cursor].style; start_bitcoin_cli(NULL, cmd, estimatefees_done, true, - BITCOIND_LOW_PRIO, "estimatesmartfee", params, stash); + BITCOIND_LOW_PRIO, stash, + "estimatesmartfee", + take(tal_fmt(NULL, "%u", + estimatefee_params[stash->cursor].blocks)), + estimatefee_params[stash->cursor].style, + NULL); return command_still_pending(cmd); } @@ -708,12 +746,12 @@ static struct command_result *sendrawtransaction(struct command *cmd, const char *buf, const jsmntok_t *toks) { - const char **params = tal_arr(cmd, const char *, 1); + const char *tx, *highfeesarg; bool *allowhighfees; /* bitcoin-cli wants strings. */ if (!param(cmd, buf, toks, - p_req("tx", param_string, ¶ms[0]), + p_req("tx", param_string, &tx), p_req("allowhighfees", param_bool, &allowhighfees), NULL)) return command_param_failed(); @@ -724,16 +762,19 @@ static struct command_result *sendrawtransaction(struct command *cmd, * maxfeerate, which when set to 0 means * no max feerate. */ - tal_arr_expand(¶ms, "0"); + highfeesarg = "0"; else /* in older versions, second arg is allowhighfees, * set to true to allow high fees. */ - tal_arr_expand(¶ms, "true"); - } + highfeesarg = "true"; + } else + highfeesarg = NULL; start_bitcoin_cli(NULL, cmd, process_sendrawtransaction, true, - BITCOIND_HIGH_PRIO, "sendrawtransaction", params, NULL); + BITCOIND_HIGH_PRIO, NULL, + "sendrawtransaction", + tx, highfeesarg, NULL); return command_still_pending(cmd); } @@ -742,17 +783,18 @@ static struct command_result *getutxout(struct command *cmd, const char *buf, const jsmntok_t *toks) { - const char **params = tal_arr(cmd, const char *, 2); + const char *txid, *vout; /* bitcoin-cli wants strings. */ if (!param(cmd, buf, toks, - p_req("txid", param_string, ¶ms[0]), - p_req("vout", param_string, ¶ms[1]), + p_req("txid", param_string, &txid), + p_req("vout", param_string, &vout), NULL)) return command_param_failed(); start_bitcoin_cli(NULL, cmd, process_getutxout, true, - BITCOIND_HIGH_PRIO, "gettxout", params, NULL); + BITCOIND_HIGH_PRIO, NULL, + "gettxout", txid, vout, NULL); return command_still_pending(cmd); } From b0829fc52a54f6463aa41035027d7b9373b87c98 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 16 Dec 2021 13:07:33 -0600 Subject: [PATCH 0379/1530] lightningd/Make: cleanup lightningd+wallet headers There is no "wallet_lib_headers" variable in wallet/Makefile Likewise, there were two "lightningd_headers", a couple of unused variables and some other nonsene in lightningd/Makefile --- lightningd/Makefile | 20 +++++++++----------- lightningd/test/Makefile | 2 +- wallet/Makefile | 1 - 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/lightningd/Makefile b/lightningd/Makefile index c6827849ed6b..3a9b58bb768c 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -38,25 +38,27 @@ LIGHTNINGD_SRC := \ lightningd/subd.c \ lightningd/watch.c - LIGHTNINGD_SRC_NOHDR := \ lightningd/datastore.c \ lightningd/ping.c \ lightningd/offer.c \ lightningd/signmessage.c -LIGHTNINGD_HEADERS := \ +include wallet/Makefile + +LIGHTNINGD_HDRS := \ $(LIGHTNINGD_SRC:.c=.h) \ lightningd/channel_state.h \ - lightningd/channel_state_names_gen.h + lightningd/channel_state_names_gen.h \ + $(WALLET_HDRS) LIGHTNINGD_OBJS := $(LIGHTNINGD_SRC:.c=.o) $(LIGHTNINGD_SRC_NOHDR:.c=.o) -$(LIGHTNINGD_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_CONTROL_HEADERS) +$(LIGHTNINGD_OBJS): $(LIGHTNINGD_HDRS) $(LIGHTNINGD_CONTROL_HEADERS) # Make sure these depend on everything. ALL_C_SOURCES += $(LIGHTNINGD_SRC) $(LIGHTNINGD_SRC_NOHDR) -ALL_C_HEADERS += $(LIGHTNINGD_HEADERS) +ALL_C_HEADERS += $(LIGHTNINGD_HDRS) ALL_PROGRAMS += lightningd/lightningd # Common source we use. @@ -129,12 +131,8 @@ LIGHTNINGD_COMMON_OBJS := \ common/wire_error.o \ common/wireaddr.o \ -include wallet/Makefile - -# All together in one convenient var -LIGHTNINGD_HEADERS = $(LIGHTNINGD_HEADERS_NOGEN) $(LIGHTNINGD_HEADERS_GEN) $(WALLET_HDRS) - -$(LIGHTNINGD_OBJS): $(LIGHTNINGD_HEADERS) +$(LIGHTNINGD_OBJS): $(LIGHTNINGD_HDRS) +$(WALLET_OBJS): $(LIGHTNINGD_HDRS) # Only the plugin component needs to depend on this header. lightningd/plugin.o: plugins/list_of_builtin_plugins_gen.h diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index d0c3aa677868..e513a6fc2db6 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -29,6 +29,6 @@ LIGHTNINGD_TEST_COMMON_OBJS := \ $(LIGHTNINGD_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(LIGHTNINGD_TEST_COMMON_OBJS) -$(LIGHTNINGD_TEST_OBJS): $(LIGHTNINGD_HEADERS) $(LIGHTNINGD_SRC) $(LIGHTNINGD_SRC_NOHDR) +$(LIGHTNINGD_TEST_OBJS): $(LIGHTNINGD_HDRS) $(LIGHTNINGD_SRC) $(LIGHTNINGD_SRC_NOHDR) check-units: $(LIGHTNINGD_TEST_PROGRAMS:%=unittest/%) diff --git a/wallet/Makefile b/wallet/Makefile index 007872f4c151..2697febf142b 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -18,7 +18,6 @@ WALLET_SRC := $(WALLET_LIB_SRC) $(WALLET_LIB_SRC_NOHDR) $(WALLET_DB_DRIVERS) WALLET_HDRS := $(WALLET_LIB_SRC:.c=.h) WALLET_OBJS := $(WALLET_SRC:.c=.o) -$(WALLET_OBJS): $(WALLET_HDRS) $(LIGHTNINGD_HEADERS) # Make sure these depend on everything. ALL_C_SOURCES += $(WALLET_SRC) wallet/db_sqlite3_sqlgen.c wallet/db_postgres_sqlgen.c From 03c950bae810e92fc80d25b2ccd7e1e35282fad6 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 1 Mar 2022 11:22:15 -0600 Subject: [PATCH 0380/1530] db: decouple `fatal` reliance, have as impl defined function `fatal` is defined in lightningd and has logfile dependencies etc. Make it more generic by allowing declaration in the use file (wallet.c) --- lightningd/log.c | 17 ++++++++++------- lightningd/log.h | 1 + wallet/db_common.h | 8 +++----- wallet/test/run-db.c | 5 +---- wallet/test/run-wallet.c | 39 +++++++++++++++++++++------------------ wallet/wallet.c | 15 +++++++++++++++ 6 files changed, 51 insertions(+), 34 deletions(-) diff --git a/lightningd/log.c b/lightningd/log.c index 0d975934cf11..3b4df6a1c779 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -809,24 +809,27 @@ void log_backtrace_exit(void) } } -void fatal(const char *fmt, ...) +void fatal_vfmt(const char *fmt, va_list ap) { - va_list ap; - - va_start(ap, fmt); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); - va_end(ap); if (!crashlog) exit(1); - va_start(ap, fmt); logv(crashlog, LOG_BROKEN, NULL, true, fmt, ap); - va_end(ap); abort(); } +void fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fatal_vfmt(fmt, ap); + va_end(ap); +} + struct log_info { enum log_level level; struct json_stream *response; diff --git a/lightningd/log.h b/lightningd/log.h index daf7896e3763..95dc2ada261b 100644 --- a/lightningd/log.h +++ b/lightningd/log.h @@ -53,6 +53,7 @@ char *arg_log_to_file(const char *arg, struct lightningd *ld); /* Once this is set, we dump fatal with a backtrace to this log */ extern struct log *crashlog; void NORETURN PRINTF_FMT(1,2) fatal(const char *fmt, ...); +void NORETURN fatal_vfmt(const char *fmt, va_list ap); void log_backtrace_print(const char *fmt, ...); void log_backtrace_exit(void); diff --git a/wallet/db_common.h b/wallet/db_common.h index 0ab196c29081..233d7b93a926 100644 --- a/wallet/db_common.h +++ b/wallet/db_common.h @@ -6,11 +6,6 @@ #include #include -/* For testing, we want to catch fatal messages. */ -#ifndef db_fatal -#define db_fatal fatal -#endif - struct db { char *filename; const char *in_transaction; @@ -163,6 +158,9 @@ struct db_config { /* Provide a way for DB backends to register themselves */ AUTODATA_TYPE(db_backends, struct db_config); +void db_fatal(const char *fmt, ...) + PRINTF_FMT(1, 2); + /** * Report a statement that changes the wallet * diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 1766613246ae..63765852626d 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -1,9 +1,6 @@ #include "config.h" #include -static void db_test_fatal(const char *fmt, ...); -#define db_fatal db_test_fatal - static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const struct node_id *node_id UNUSED, bool call_notifier UNUSED, const char *fmt UNUSED, ...) { } @@ -59,7 +56,7 @@ bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) /* AUTOGENERATED MOCKS END */ static char *db_err; -static void db_test_fatal(const char *fmt, ...) +void db_fatal(const char *fmt, ...) { va_list ap; diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index d97b722969fb..fa12dcf621d0 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1,15 +1,34 @@ #include "config.h" #include -static void wallet_test_fatal(const char *fmt, ...); -#define db_fatal wallet_test_fatal #include "test_utils.h" +#include +#include static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const struct node_id *node_id UNUSED, bool call_notifier UNUSED, const char *fmt UNUSED, ...) { } #define log_ db_log_ +#ifndef DB_FATAL +#define DB_FATAL +static char *wallet_err; +void db_fatal(const char *fmt, ...) +{ + va_list ap; + + /* Fail hard if we're complaining about not being in transaction */ + assert(!strstarts(fmt, "No longer in transaction")); + + /* Fail hard if we're complaining about not being in transaction */ + assert(!strstarts(fmt, "No longer in transaction")); + + va_start(ap, fmt); + wallet_err = tal_vfmt(NULL, fmt, ap); + va_end(ap); +} +#endif /* DB_FATAL */ + #include "wallet/wallet.c" #include "lightningd/htlc_end.c" #include "lightningd/peer_control.c" @@ -837,22 +856,6 @@ bool fromwire_hsmd_get_channel_basepoints_reply(const void *p UNNEEDED, return true; } -static char *wallet_err; -static void wallet_test_fatal(const char *fmt, ...) -{ - va_list ap; - - /* Fail hard if we're complaining about not being in transaction */ - assert(!strstarts(fmt, "No longer in transaction")); - - /* Fail hard if we're complaining about not being in transaction */ - assert(!strstarts(fmt, "No longer in transaction")); - - va_start(ap, fmt); - wallet_err = tal_vfmt(NULL, fmt, ap); - va_end(ap); -} - #define transaction_wrap(db, ...) \ (db_begin_transaction(db), __VA_ARGS__, db_commit_transaction(db), wallet_err == NULL) diff --git a/wallet/wallet.c b/wallet/wallet.c index 4fe3740c0f0d..3cbe424ecf59 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -45,6 +45,21 @@ struct channel_state_param { const enum channel_state_bucket state; }; +/* Implement db_fatal, as a wrapper around fatal. + * We use a ifndef block so that it can get be + * implemented in a test file first, if necessary */ +#ifndef DB_FATAL +#define DB_FATAL +void db_fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fatal_vfmt(fmt, ap); + va_end(ap); +} +#endif /* DB_FATAL */ + static void outpointfilters_init(struct wallet *w) { struct db_stmt *stmt; From ce12d2b8a994e30376acbaa6af8a161df1c93047 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 3 Jan 2022 12:45:35 -0600 Subject: [PATCH 0381/1530] database: pull out database code into a new module We're going to reuse the database controllers for the accounting plugin --- .gitattributes | 4 +- Makefile | 1 + db/Makefile | 23 + db/bindings.c | 554 ++++++++++++++++ db/bindings.h | 118 ++++ wallet/db_common.h => db/common.h | 60 +- {wallet => db}/db_postgres.c | 9 +- {wallet => db}/db_sqlite3.c | 8 +- db/exec.c | 162 +++++ db/exec.h | 52 ++ db/utils.c | 324 +++++++++ db/utils.h | 100 +++ devtools/sql-rewrite.py | 3 +- lightningd/Makefile | 6 +- lightningd/bitcoind.c | 1 + lightningd/chaintopology.c | 1 + lightningd/invoice.c | 2 + lightningd/io_loop_with_timers.c | 1 + lightningd/jsonrpc.c | 2 + lightningd/lightningd.c | 2 + lightningd/offer.c | 2 + lightningd/onchain_control.c | 1 + lightningd/options.c | 1 + lightningd/peer_htlcs.c | 1 + lightningd/plugin_hook.c | 2 + lightningd/subd.c | 1 + wallet/Makefile | 32 +- wallet/db.c | 1011 +---------------------------- wallet/db.h | 235 ------- wallet/db_queries_postgres.c | 13 + wallet/db_queries_sqlite3.c | 12 + wallet/invoices.c | 5 +- wallet/test/Makefile | 2 +- wallet/test/run-db.c | 6 + wallet/test/run-wallet.c | 7 +- wallet/wallet.c | 5 +- wallet/walletrpc.c | 1 + 37 files changed, 1478 insertions(+), 1292 deletions(-) create mode 100644 db/Makefile create mode 100644 db/bindings.c create mode 100644 db/bindings.h rename wallet/db_common.h => db/common.h (77%) rename {wallet => db}/db_postgres.c (97%) rename {wallet => db}/db_sqlite3.c (99%) create mode 100644 db/exec.c create mode 100644 db/exec.h create mode 100644 db/utils.c create mode 100644 db/utils.h create mode 100644 wallet/db_queries_postgres.c create mode 100644 wallet/db_queries_sqlite3.c diff --git a/.gitattributes b/.gitattributes index c40e14769e9a..ddec20639f42 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,8 +7,8 @@ configure text eol=lf # The following files are generated and should not be shown in the # diffs by default on Github. doc/lightning*.7 linguist-generated=true -wallet/db_*_sqlgen.c linguist-generated=true -wallet/statements_gettextgen.po linguist-generated=true +db_*_sqlgen.c linguist-generated=true +statements_gettextgen.po linguist-generated=true *_wiregen.? linguist-generated=true *_printgen.? linguist-generated=true diff --git a/Makefile b/Makefile index 62fec37c111d..66232988b464 100644 --- a/Makefile +++ b/Makefile @@ -336,6 +336,7 @@ include external/Makefile include bitcoin/Makefile include common/Makefile include wire/Makefile +include db/Makefile include hsmd/Makefile include gossipd/Makefile include openingd/Makefile diff --git a/db/Makefile b/db/Makefile new file mode 100644 index 000000000000..43380acd6245 --- /dev/null +++ b/db/Makefile @@ -0,0 +1,23 @@ +#! /usr/bin/make + +DB_LIB_SRC := \ + db/bindings.c \ + db/exec.c \ + db/utils.c + +DB_DRIVERS := \ + db/db_postgres.c \ + db/db_sqlite3.c + +DB_SRC := $(DB_LIB_SRC) $(DB_DRIVERS) +DB_HEADERS := $(DB_LIB_SRC:.c=.h) db/common.h + +DB_OBJS := $(DB_LIB_SRC:.c=.o) $(DB_DRIVERS:.c=.o) +$(DB_OBJS): $(DB_HEADERS) + +# Make sure these depend on everything. +ALL_C_SOURCES += $(DB_SRC) +ALL_C_HEADERS += $(DB_HEADERS) + +# DB_SQL_FILES is the list of database files +DB_SQL_FILES := db/exec.c diff --git a/db/bindings.c b/db/bindings.c new file mode 100644 index 000000000000..df8b3827c2df --- /dev/null +++ b/db/bindings.c @@ -0,0 +1,554 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NSEC_IN_SEC 1000000000 + +/* Local helpers once you have column number */ +static bool db_column_is_null(struct db_stmt *stmt, int col) +{ + return stmt->db->config->column_is_null_fn(stmt, col); +} + +/* Returns true (and warns) if it's nul */ +static bool db_column_null_warn(struct db_stmt *stmt, const char *colname, + int col) +{ + if (!db_column_is_null(stmt, col)) + return false; + + /* FIXME: log broken? */ +#if DEVELOPER + db_fatal("Accessing a null column %s/%i in query %s", + colname, col, stmt->query->query); +#endif /* DEVELOPER */ + return true; +} + +void db_bind_int(struct db_stmt *stmt, int pos, int val) +{ + assert(pos < tal_count(stmt->bindings)); + memcheck(&val, sizeof(val)); + stmt->bindings[pos].type = DB_BINDING_INT; + stmt->bindings[pos].v.i = val; +} + +int db_col_int(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_null_warn(stmt, colname, col)) + return 0; + + return stmt->db->config->column_int_fn(stmt, col); +} + +int db_col_is_null(struct db_stmt *stmt, const char *colname) +{ + return db_column_is_null(stmt, db_query_colnum(stmt, colname)); +} + +void db_bind_null(struct db_stmt *stmt, int pos) +{ + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_NULL; +} + +void db_bind_u64(struct db_stmt *stmt, int pos, u64 val) +{ + memcheck(&val, sizeof(val)); + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_UINT64; + stmt->bindings[pos].v.u64 = val; +} + +void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len) +{ + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_BLOB; + stmt->bindings[pos].v.blob = memcheck(val, len); + stmt->bindings[pos].len = len; +} + +void db_bind_text(struct db_stmt *stmt, int pos, const char *val) +{ + assert(pos < tal_count(stmt->bindings)); + stmt->bindings[pos].type = DB_BINDING_TEXT; + stmt->bindings[pos].v.text = val; + stmt->bindings[pos].len = strlen(val); +} + +void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p) +{ + db_bind_blob(stmt, pos, p->r, sizeof(struct preimage)); +} + +void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s) +{ + db_bind_blob(stmt, pos, s->u.u8, sizeof(struct sha256)); +} + +void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s) +{ + db_bind_sha256(stmt, pos, &s->sha); +} + +void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s) +{ + assert(sizeof(s->data) == 32); + db_bind_blob(stmt, pos, s->data, sizeof(s->data)); +} + +void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s) +{ + size_t num = tal_count(s), elsize = sizeof(s->data); + u8 *ser = tal_arr(stmt, u8, num * elsize); + + for (size_t i = 0; i < num; ++i) + memcpy(ser + i * elsize, &s[i], elsize); + + db_bind_blob(stmt, col, ser, tal_count(ser)); +} + +void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t) +{ + db_bind_sha256d(stmt, pos, &t->shad); +} + +void db_bind_channel_id(struct db_stmt *stmt, int pos, const struct channel_id *id) +{ + db_bind_blob(stmt, pos, id->id, sizeof(id->id)); +} + +void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *id) +{ + db_bind_blob(stmt, pos, id->k, sizeof(id->k)); +} + +void db_bind_node_id_arr(struct db_stmt *stmt, int col, + const struct node_id *ids) +{ + /* Copy into contiguous array: ARM will add padding to struct node_id! */ + size_t n = tal_count(ids); + u8 *arr = tal_arr(stmt, u8, n * sizeof(ids[0].k)); + + for (size_t i = 0; i < n; ++i) { + assert(node_id_valid(&ids[i])); + memcpy(arr + sizeof(ids[i].k) * i, + ids[i].k, + sizeof(ids[i].k)); + } + db_bind_blob(stmt, col, arr, tal_count(arr)); +} + +void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *pk) +{ + u8 *der = tal_arr(stmt, u8, PUBKEY_CMPR_LEN); + pubkey_to_der(der, pk); + db_bind_blob(stmt, pos, der, PUBKEY_CMPR_LEN); +} + +void db_bind_short_channel_id(struct db_stmt *stmt, int col, + const struct short_channel_id *id) +{ + char *ser = short_channel_id_to_str(stmt, id); + db_bind_text(stmt, col, ser); +} + +void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, + const struct short_channel_id *id) +{ + u8 *ser = tal_arr(stmt, u8, 0); + size_t num = tal_count(id); + + for (size_t i = 0; i < num; ++i) + towire_short_channel_id(&ser, &id[i]); + + db_bind_talarr(stmt, col, ser); +} + +void db_bind_signature(struct db_stmt *stmt, int col, + const secp256k1_ecdsa_signature *sig) +{ + u8 *buf = tal_arr(stmt, u8, 64); + int ret = secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, + buf, sig); + assert(ret == 1); + db_bind_blob(stmt, col, buf, 64); +} + +void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t) +{ + u64 timestamp = t.ts.tv_nsec + (((u64) t.ts.tv_sec) * ((u64) NSEC_IN_SEC)); + db_bind_u64(stmt, col, timestamp); +} + +void db_bind_tx(struct db_stmt *stmt, int col, const struct wally_tx *tx) +{ + u8 *ser = linearize_wtx(stmt, tx); + assert(ser); + db_bind_talarr(stmt, col, ser); +} + +void db_bind_psbt(struct db_stmt *stmt, int col, const struct wally_psbt *psbt) +{ + size_t bytes_written; + const u8 *ser = psbt_get_bytes(stmt, psbt, &bytes_written); + assert(ser); + db_bind_blob(stmt, col, ser, bytes_written); +} + +void db_bind_amount_msat(struct db_stmt *stmt, int pos, + const struct amount_msat *msat) +{ + db_bind_u64(stmt, pos, msat->millisatoshis); /* Raw: low level function */ +} + +void db_bind_amount_sat(struct db_stmt *stmt, int pos, + const struct amount_sat *sat) +{ + db_bind_u64(stmt, pos, sat->satoshis); /* Raw: low level function */ +} + +void db_bind_json_escape(struct db_stmt *stmt, int pos, + const struct json_escape *esc) +{ + db_bind_text(stmt, pos, esc->s); +} + +void db_bind_onionreply(struct db_stmt *stmt, int pos, const struct onionreply *r) +{ + db_bind_talarr(stmt, pos, r->contents); +} + +void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr) +{ + if (!arr) + db_bind_null(stmt, col); + else + db_bind_blob(stmt, col, arr, tal_bytelen(arr)); +} + +static size_t db_column_bytes(struct db_stmt *stmt, int col) +{ + if (db_column_is_null(stmt, col)) + return 0; + return stmt->db->config->column_bytes_fn(stmt, col); +} + +static const void *db_column_blob(struct db_stmt *stmt, int col) +{ + if (db_column_is_null(stmt, col)) + return NULL; + return stmt->db->config->column_blob_fn(stmt, col); +} + + +u64 db_col_u64(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_null_warn(stmt, colname, col)) + return 0; + + return stmt->db->config->column_u64_fn(stmt, col); +} + +int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) + return def; + else + return stmt->db->config->column_int_fn(stmt, col); +} + +size_t db_col_bytes(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_null_warn(stmt, colname, col)) + return 0; + + return stmt->db->config->column_bytes_fn(stmt, col); +} + +const void *db_col_blob(struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_null_warn(stmt, colname, col)) + return NULL; + + return stmt->db->config->column_blob_fn(stmt, col); +} + +char *db_col_strdup(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_null_warn(stmt, colname, col)) + return NULL; + + return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); +} + +void db_col_preimage(struct db_stmt *stmt, const char *colname, + struct preimage *preimage) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + size_t size = sizeof(struct preimage); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(preimage, raw, size); +} + +void db_col_channel_id(struct db_stmt *stmt, const char *colname, struct channel_id *dest) +{ + size_t col = db_query_colnum(stmt, colname); + + assert(db_column_bytes(stmt, col) == sizeof(dest->id)); + memcpy(dest->id, db_column_blob(stmt, col), sizeof(dest->id)); +} + +void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *dest) +{ + size_t col = db_query_colnum(stmt, colname); + + assert(db_column_bytes(stmt, col) == sizeof(dest->k)); + memcpy(dest->k, db_column_blob(stmt, col), sizeof(dest->k)); +} + +struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, + const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + struct node_id *ret; + size_t n = db_column_bytes(stmt, col) / sizeof(ret->k); + const u8 *arr = db_column_blob(stmt, col); + assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); + ret = tal_arr(ctx, struct node_id, n); + + db_column_null_warn(stmt, colname, col); + for (size_t i = 0; i < n; i++) + memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); + + return ret; +} + +void db_col_pubkey(struct db_stmt *stmt, + const char *colname, + struct pubkey *dest) +{ + size_t col = db_query_colnum(stmt, colname); + bool ok; + assert(db_column_bytes(stmt, col) == PUBKEY_CMPR_LEN); + ok = pubkey_from_der(db_column_blob(stmt, col), PUBKEY_CMPR_LEN, dest); + assert(ok); +} + +/* Yes, we put this in as a string. Past mistakes; do not use! */ +bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, + struct short_channel_id *dest) +{ + size_t col = db_query_colnum(stmt, colname); + const char *source = db_column_blob(stmt, col); + size_t sourcelen = db_column_bytes(stmt, col); + db_column_null_warn(stmt, colname, col); + return short_channel_id_from_str(source, sourcelen, dest); +} + +struct short_channel_id * +db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *ser; + size_t len; + struct short_channel_id *ret; + + db_column_null_warn(stmt, colname, col); + ser = db_column_blob(stmt, col); + len = db_column_bytes(stmt, col); + ret = tal_arr(ctx, struct short_channel_id, 0); + + while (len != 0) { + struct short_channel_id scid; + fromwire_short_channel_id(&ser, &len, &scid); + tal_arr_expand(&ret, scid); + } + + return ret; +} + +bool db_col_signature(struct db_stmt *stmt, const char *colname, + secp256k1_ecdsa_signature *sig) +{ + size_t col = db_query_colnum(stmt, colname); + assert(db_column_bytes(stmt, col) == 64); + return secp256k1_ecdsa_signature_parse_compact( + secp256k1_ctx, sig, db_column_blob(stmt, col)) == 1; +} + +struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname) +{ + struct timeabs t; + u64 timestamp = db_col_u64(stmt, colname); + t.ts.tv_sec = timestamp / NSEC_IN_SEC; + t.ts.tv_nsec = timestamp % NSEC_IN_SEC; + return t; + +} + +struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *src = db_column_blob(stmt, col); + size_t len = db_column_bytes(stmt, col); + + db_column_null_warn(stmt, colname, col); + return pull_bitcoin_tx(ctx, &src, &len); +} + +struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *src = db_column_blob(stmt, col); + size_t len = db_column_bytes(stmt, col); + + db_column_null_warn(stmt, colname, col); + return psbt_from_bytes(ctx, src, len); +} + +struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) +{ + struct wally_psbt *psbt = db_col_psbt(ctx, stmt, colname); + if (!psbt) + return NULL; + return bitcoin_tx_with_psbt(ctx, psbt); +} + +void *db_col_arr_(const tal_t *ctx, struct db_stmt *stmt, const char *colname, + size_t bytes, const char *label, const char *caller) +{ + size_t col = db_query_colnum(stmt, colname); + size_t sourcelen; + void *p; + + if (db_column_is_null(stmt, col)) + return NULL; + + sourcelen = db_column_bytes(stmt, col); + + if (sourcelen % bytes != 0) + db_fatal("%s: %s/%zu column size for %zu not a multiple of %s (%zu)", + caller, colname, col, sourcelen, label, bytes); + + p = tal_arr_label(ctx, char, sourcelen, label); + memcpy(p, db_column_blob(stmt, col), sourcelen); + return p; +} + +void db_col_amount_msat_or_default(struct db_stmt *stmt, + const char *colname, + struct amount_msat *msat, + struct amount_msat def) +{ + size_t col = db_query_colnum(stmt, colname); + + if (db_column_is_null(stmt, col)) + *msat = def; + else + msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ +} + +void db_col_amount_msat(struct db_stmt *stmt, const char *colname, + struct amount_msat *msat) +{ + msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ +} + +void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat) +{ + sat->satoshis = db_col_u64(stmt, colname); /* Raw: low level function */ +} + +struct json_escape *db_col_json_escape(const tal_t *ctx, + struct db_stmt *stmt, const char *colname) +{ + size_t col = db_query_colnum(stmt, colname); + + return json_escape_string_(ctx, db_column_blob(stmt, col), + db_column_bytes(stmt, col)); +} + +void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + size_t size = sizeof(struct sha256); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(sha, raw, size); +} + +void db_col_sha256d(struct db_stmt *stmt, const char *colname, + struct sha256_double *shad) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + size_t size = sizeof(struct sha256_double); + assert(db_column_bytes(stmt, col) == size); + raw = db_column_blob(stmt, col); + memcpy(shad, raw, size); +} + +void db_col_secret(struct db_stmt *stmt, const char *colname, struct secret *s) +{ + size_t col = db_query_colnum(stmt, colname); + const u8 *raw; + assert(db_column_bytes(stmt, col) == sizeof(struct secret)); + raw = db_column_blob(stmt, col); + memcpy(s, raw, sizeof(struct secret)); +} + +struct secret *db_col_secret_arr(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) +{ + return db_col_arr(ctx, stmt, colname, struct secret); +} + +void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t) +{ + db_col_sha256d(stmt, colname, &t->shad); +} + +struct onionreply *db_col_onionreply(const tal_t *ctx, + struct db_stmt *stmt, const char *colname) +{ + struct onionreply *r = tal(ctx, struct onionreply); + r->contents = db_col_arr(ctx, stmt, colname, u8); + return r; +} + +void db_col_ignore(struct db_stmt *stmt, const char *colname) +{ +#if DEVELOPER + db_query_colnum(stmt, colname); +#endif +} diff --git a/db/bindings.h b/db/bindings.h new file mode 100644 index 000000000000..298dc6d4838d --- /dev/null +++ b/db/bindings.h @@ -0,0 +1,118 @@ +#ifndef LIGHTNING_DB_BINDINGS_H +#define LIGHTNING_DB_BINDINGS_H +#include "config.h" + +#include +#include +#include +#include +#include +#include + +struct channel_id; +struct db_stmt; +struct node_id; +struct onionreply; +struct wally_psbt; +struct wally_tx; + +int db_col_is_null(struct db_stmt *stmt, const char *colname); + +void db_bind_int(struct db_stmt *stmt, int pos, int val); +int db_col_int(struct db_stmt *stmt, const char *colname); + +void db_bind_null(struct db_stmt *stmt, int pos); +void db_bind_int(struct db_stmt *stmt, int pos, int val); +void db_bind_u64(struct db_stmt *stmt, int pos, u64 val); +void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len); +void db_bind_text(struct db_stmt *stmt, int pos, const char *val); +void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p); +void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s); +void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s); +void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s); +void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s); +void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t); +void db_bind_channel_id(struct db_stmt *stmt, int pos, const struct channel_id *id); +void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *ni); +void db_bind_node_id_arr(struct db_stmt *stmt, int col, + const struct node_id *ids); +void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *p); +void db_bind_short_channel_id(struct db_stmt *stmt, int col, + const struct short_channel_id *id); +void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, + const struct short_channel_id *id); +void db_bind_signature(struct db_stmt *stmt, int col, + const secp256k1_ecdsa_signature *sig); +void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t); +void db_bind_tx(struct db_stmt *stmt, int col, const struct wally_tx *tx); +void db_bind_psbt(struct db_stmt *stmt, int col, const struct wally_psbt *psbt); +void db_bind_amount_msat(struct db_stmt *stmt, int pos, + const struct amount_msat *msat); +void db_bind_amount_sat(struct db_stmt *stmt, int pos, + const struct amount_sat *sat); +void db_bind_json_escape(struct db_stmt *stmt, int pos, + const struct json_escape *esc); +void db_bind_onionreply(struct db_stmt *stmt, int col, + const struct onionreply *r); +void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr); + +/* Modern variants: get columns by name from SELECT */ +/* Bridge function to get column number from SELECT + (must exist) */ +size_t db_query_colnum(const struct db_stmt *stmt, const char *colname); + +u64 db_col_u64(struct db_stmt *stmt, const char *colname); +size_t db_col_bytes(struct db_stmt *stmt, const char *colname); +const void* db_col_blob(struct db_stmt *stmt, const char *colname); +char *db_col_strdup(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname); +void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage); +void db_col_amount_msat(struct db_stmt *stmt, const char *colname, struct amount_msat *msat); +void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat); +struct json_escape *db_col_json_escape(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha); +void db_col_sha256d(struct db_stmt *stmt, const char *colname, struct sha256_double *shad); +void db_col_secret(struct db_stmt *stmt, const char *colname, struct secret *s); +struct secret *db_col_secret_arr(const tal_t *ctx, struct db_stmt *stmt, + const char *colname); +void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t); +void db_col_channel_id(struct db_stmt *stmt, const char *colname, struct channel_id *dest); +void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *ni); +struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, + const char *colname); +void db_col_pubkey(struct db_stmt *stmt, const char *colname, + struct pubkey *p); +bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, + struct short_channel_id *dest); +struct short_channel_id * +db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +bool db_col_signature(struct db_stmt *stmt, const char *colname, + secp256k1_ecdsa_signature *sig); +struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname); +struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); + +struct onionreply *db_col_onionreply(const tal_t *ctx, + struct db_stmt *stmt, const char *colname); + +#define db_col_arr(ctx, stmt, colname, type) \ + ((type *)db_col_arr_((ctx), (stmt), (colname), \ + sizeof(type), TAL_LABEL(type, "[]"), \ + __func__)) +void *db_col_arr_(const tal_t *ctx, struct db_stmt *stmt, const char *colname, + size_t bytes, const char *label, const char *caller); + + +/* Some useful default variants */ +int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def); +void db_col_amount_msat_or_default(struct db_stmt *stmt, const char *colname, + struct amount_msat *msat, + struct amount_msat def); + + +/* Explicitly ignore a column (so we don't complain you didn't use it!) */ +void db_col_ignore(struct db_stmt *stmt, const char *colname); + +#endif /* LIGHTNING_DB_BINDINGS_H */ diff --git a/wallet/db_common.h b/db/common.h similarity index 77% rename from wallet/db_common.h rename to db/common.h index 233d7b93a926..4ced5132a231 100644 --- a/wallet/db_common.h +++ b/db/common.h @@ -1,10 +1,32 @@ -#ifndef LIGHTNING_WALLET_DB_COMMON_H -#define LIGHTNING_WALLET_DB_COMMON_H +#ifndef LIGHTNING_DB_COMMON_H +#define LIGHTNING_DB_COMMON_H #include "config.h" #include #include #include #include +#include + +/** + * Macro to annotate a named SQL query. + * + * This macro is used to annotate SQL queries that might need rewriting for + * different SQL dialects. It is used both as a marker for the query + * extraction logic in devtools/sql-rewrite.py to identify queries, as well as + * a way to swap out the query text with it's name so that the query execution + * engine can then look up the rewritten query using its name. + * + */ +#define NAMED_SQL(name,x) x + +/** + * Simple annotation macro that auto-generates names for NAMED_SQL + * + * If this macro is changed it is likely that the extraction logic in + * devtools/sql-rewrite.py needs to change as well, since they need to + * generate identical names to work correctly. + */ +#define SQL(x) NAMED_SQL( __FILE__ ":" stringify(__COUNTER__), x) struct db { char *filename; @@ -13,18 +35,18 @@ struct db { /* DB-specific context */ void *conn; - /* The configuration, including translated queries for the current - * instance. */ + /* The configuration for the current database driver */ const struct db_config *config; + /* Translated queries for the current database domain + driver */ + const struct db_query_set *queries; + const char **changes; /* List of statements that have been created but not executed yet. */ struct list_head pending_statements; char *error; - struct log *log; - /* Were there any modifying statements in the current transaction? * Used to bump the data_version in the DB.*/ bool dirty; @@ -32,6 +54,8 @@ struct db { /* The current DB version we expect to update if changes are * committed. */ u32 data_version; + + void (*report_changes_fn)(struct db *); }; struct db_query { @@ -102,10 +126,14 @@ struct db_stmt { #endif }; -struct db_config { +struct db_query_set { const char *name; const struct db_query *query_table; size_t query_table_size; +}; + +struct db_config { + const char *name; /* Function used to execute a statement that doesn't result in a * response. */ @@ -155,20 +183,14 @@ struct db_config { const char **colnames, size_t num_cols); }; -/* Provide a way for DB backends to register themselves */ -AUTODATA_TYPE(db_backends, struct db_config); - void db_fatal(const char *fmt, ...) PRINTF_FMT(1, 2); -/** - * Report a statement that changes the wallet - * - * Allows the DB driver to report an expanded statement during - * execution. Changes are queued up and reported to the `db_write` plugin hook - * upon committing. - */ -void db_changes_add(struct db_stmt *db_stmt, const char * expanded); +/* Provide a way for DB backends to register themselves */ +AUTODATA_TYPE(db_backends, struct db_config); + +/* Provide a way for DB query sets to register themselves */ +AUTODATA_TYPE(db_queries, struct db_query_set); /* devtools/sql-rewrite.py generates this simple htable */ struct sqlname_map { @@ -176,4 +198,4 @@ struct sqlname_map { int val; }; -#endif /* LIGHTNING_WALLET_DB_COMMON_H */ +#endif /* LIGHTNING_DB_COMMON_H */ diff --git a/wallet/db_postgres.c b/db/db_postgres.c similarity index 97% rename from wallet/db_postgres.c rename to db/db_postgres.c index 91db5beffa32..7f6f143bc750 100644 --- a/wallet/db_postgres.c +++ b/db/db_postgres.c @@ -1,9 +1,8 @@ #include "config.h" #include #include -#include -#include -#include +#include +#include #if HAVE_POSTGRES /* Indented in order not to trigger the inclusion order check */ @@ -321,8 +320,6 @@ static bool db_postgres_delete_columns(struct db *db, struct db_config db_postgres_config = { .name = "postgres", - .query_table = db_postgres_queries, - .query_table_size = ARRAY_SIZE(db_postgres_queries), .exec_fn = db_postgres_exec, .query_fn = db_postgres_query, .step_fn = db_postgres_step, @@ -348,4 +345,4 @@ struct db_config db_postgres_config = { AUTODATA(db_backends, &db_postgres_config); -#endif +#endif /* HAVE_POSTGRES */ diff --git a/wallet/db_sqlite3.c b/db/db_sqlite3.c similarity index 99% rename from wallet/db_sqlite3.c rename to db/db_sqlite3.c index ad81bc20b1a2..90f0a4aa28e3 100644 --- a/wallet/db_sqlite3.c +++ b/db/db_sqlite3.c @@ -1,8 +1,8 @@ #include "config.h" -#include "db_sqlite3_sqlgen.c" #include #include -#include +#include +#include #if HAVE_SQLITE3 #include @@ -682,8 +682,6 @@ static bool db_sqlite3_delete_columns(struct db *db, struct db_config db_sqlite3_config = { .name = "sqlite3", - .query_table = db_sqlite3_queries, - .query_table_size = ARRAY_SIZE(db_sqlite3_queries), .exec_fn = &db_sqlite3_exec, .query_fn = &db_sqlite3_query, .step_fn = &db_sqlite3_step, @@ -710,4 +708,4 @@ struct db_config db_sqlite3_config = { AUTODATA(db_backends, &db_sqlite3_config); -#endif +#endif /* HAVE_SQLITE3 */ diff --git a/db/exec.c b/db/exec.c new file mode 100644 index 000000000000..77c9597ab841 --- /dev/null +++ b/db/exec.c @@ -0,0 +1,162 @@ +#include "config.h" +#include +#include +#include +#include +#include + +/** + * db_get_version - Determine the current DB schema version + * + * Will attempt to determine the current schema version of the + * database @db by querying the `version` table. If the table does not + * exist it'll return schema version -1, so that migration 0 is + * applied, which should create the `version` table. + */ +int db_get_version(struct db *db) +{ + int res = -1; + struct db_stmt *stmt = db_prepare_v2(db, SQL("SELECT version FROM version LIMIT 1")); + + /* + * Tentatively execute a query, but allow failures. Some databases + * like postgres will terminate the DB transaction if there is an + * error during the execution of a query, e.g., trying to access a + * table that doesn't exist yet, so we need to terminate and restart + * the DB transaction. + */ + if (!db_query_prepared(stmt)) { + db_commit_transaction(stmt->db); + db_begin_transaction(stmt->db); + tal_free(stmt); + return res; + } + + if (db_step(stmt)) + res = db_col_int(stmt, "version"); + + tal_free(stmt); + return res; +} + +u32 db_data_version_get(struct db *db) +{ + struct db_stmt *stmt; + u32 version; + stmt = db_prepare_v2(db, SQL("SELECT intval FROM vars WHERE name = 'data_version'")); + db_query_prepared(stmt); + db_step(stmt); + version = db_col_int(stmt, "intval"); + tal_free(stmt); + return version; +} + +void db_set_intvar(struct db *db, char *varname, s64 val) +{ + size_t changes; + struct db_stmt *stmt = db_prepare_v2(db, SQL("UPDATE vars SET intval=? WHERE name=?;")); + db_bind_int(stmt, 0, val); + db_bind_text(stmt, 1, varname); + if (!db_exec_prepared_v2(stmt)) + db_fatal("Error executing update: %s", stmt->error); + changes = db_count_changes(stmt); + tal_free(stmt); + + if (changes == 0) { + stmt = db_prepare_v2(db, SQL("INSERT INTO vars (name, intval) VALUES (?, ?);")); + db_bind_text(stmt, 0, varname); + db_bind_int(stmt, 1, val); + if (!db_exec_prepared_v2(stmt)) + db_fatal("Error executing insert: %s", stmt->error); + tal_free(stmt); + } +} + +s64 db_get_intvar(struct db *db, char *varname, s64 defval) +{ + s64 res = defval; + struct db_stmt *stmt = db_prepare_v2( + db, SQL("SELECT intval FROM vars WHERE name= ? LIMIT 1")); + db_bind_text(stmt, 0, varname); + if (!db_query_prepared(stmt)) + goto done; + + if (db_step(stmt)) + res = db_col_int(stmt, "intval"); + +done: + tal_free(stmt); + return res; +} + +/* Leak tracking. */ + +/* By making the update conditional on the current value we expect we + * are implementing an optimistic lock: if the update results in + * changes on the DB we know that the data_version did not change + * under our feet and no other transaction ran in the meantime. + * + * Notice that this update effectively locks the row, so that other + * operations attempting to change this outside the transaction will + * wait for this transaction to complete. The external change will + * ultimately fail the changes test below, it'll just delay its abort + * until our transaction is committed. + */ +static void db_data_version_incr(struct db *db) +{ + struct db_stmt *stmt = db_prepare_v2( + db, SQL("UPDATE vars " + "SET intval = intval + 1 " + "WHERE name = 'data_version'" + " AND intval = ?")); + db_bind_int(stmt, 0, db->data_version); + db_exec_prepared_v2(stmt); + if (db_count_changes(stmt) != 1) + db_fatal("Optimistic lock on the database failed. There" + " may be a concurrent access to the database." + " Aborting since concurrent access is unsafe."); + tal_free(stmt); + db->data_version++; +} + +void db_begin_transaction_(struct db *db, const char *location) +{ + bool ok; + if (db->in_transaction) + db_fatal("Already in transaction from %s", db->in_transaction); + + /* No writes yet. */ + db->dirty = false; + + db_prepare_for_changes(db); + ok = db->config->begin_tx_fn(db); + if (!ok) + db_fatal("Failed to start DB transaction: %s", db->error); + + db->in_transaction = location; +} + +bool db_in_transaction(struct db *db) +{ + return db->in_transaction; +} + +void db_commit_transaction(struct db *db) +{ + bool ok; + assert(db->in_transaction); + db_assert_no_outstanding_statements(db); + + /* Increment before reporting changes to an eventual plugin. */ + if (db->dirty) + db_data_version_incr(db); + + db_report_changes(db, NULL, 0); + ok = db->config->commit_tx_fn(db); + + if (!ok) + db_fatal("Failed to commit DB transaction: %s", db->error); + + db->in_transaction = NULL; + db->dirty = false; +} diff --git a/db/exec.h b/db/exec.h new file mode 100644 index 000000000000..70799532a55a --- /dev/null +++ b/db/exec.h @@ -0,0 +1,52 @@ +#ifndef LIGHTNING_DB_EXEC_H +#define LIGHTNING_DB_EXEC_H +#include "config.h" + +#include +#include + +struct db; + +/** + * db_set_intvar - Set an integer variable in the database + * + * Utility function to store generic integer values in the + * database. + */ +void db_set_intvar(struct db *db, char *varname, s64 val); + +/** + * db_get_intvar - Retrieve an integer variable from the database + * + * Either returns the value in the database, or @defval if + * the query failed or no such variable exists. + */ +s64 db_get_intvar(struct db *db, char *varname, s64 defval); + +/* Get the current data version (entries). */ +u32 db_data_version_get(struct db *db); + +/* Get the current database version (migrations). */ +int db_get_version(struct db *db); + +/** + * db_begin_transaction - Begin a transaction + * + * Begin a new DB transaction. fatal() on database error. + */ +#define db_begin_transaction(db) \ + db_begin_transaction_((db), __FILE__ ":" stringify(__LINE__)) +void db_begin_transaction_(struct db *db, const char *location); + +bool db_in_transaction(struct db *db); + +/** + * db_commit_transaction - Commit a running transaction + * + * Requires that we are currently in a transaction. fatal() if we + * fail to commit. + */ +void db_commit_transaction(struct db *db); + + +#endif /* LIGHTNING_DB_EXEC_H */ diff --git a/db/utils.c b/db/utils.c new file mode 100644 index 000000000000..11d2654f655d --- /dev/null +++ b/db/utils.c @@ -0,0 +1,324 @@ +#include "config.h" +#include +#include +#include +#include + +/* Matches the hash function used in devtools/sql-rewrite.py */ +static u32 hash_djb2(const char *str) +{ + u32 hash = 5381; + for (size_t i = 0; str[i]; i++) + hash = ((hash << 5) + hash) ^ str[i]; + return hash; +} + +size_t db_query_colnum(const struct db_stmt *stmt, + const char *colname) +{ + u32 col; + + assert(stmt->query->colnames != NULL); + + col = hash_djb2(colname) % stmt->query->num_colnames; + /* Will crash on NULL, which is the Right Thing */ + while (!streq(stmt->query->colnames[col].sqlname, + colname)) { + col = (col + 1) % stmt->query->num_colnames; + } + +#if DEVELOPER + strset_add(stmt->cols_used, colname); +#endif + + return stmt->query->colnames[col].val; +} + +static void db_stmt_free(struct db_stmt *stmt) +{ + if (!stmt->executed) + db_fatal("Freeing an un-executed statement from %s: %s", + stmt->location, stmt->query->query); +#if DEVELOPER + /* If they never got a db_step, we don't track */ + if (stmt->cols_used) { + for (size_t i = 0; i < stmt->query->num_colnames; i++) { + if (!stmt->query->colnames[i].sqlname) + continue; + if (!strset_get(stmt->cols_used, + stmt->query->colnames[i].sqlname)) { + db_fatal("Never accessed column %s in query %s", + stmt->query->colnames[i].sqlname, + stmt->query->query); + } + } + strset_clear(stmt->cols_used); + } +#endif + if (stmt->inner_stmt) + stmt->db->config->stmt_free_fn(stmt); + assert(stmt->inner_stmt == NULL); +} + + +struct db_stmt *db_prepare_v2_(const char *location, struct db *db, + const char *query_id) +{ + struct db_stmt *stmt = tal(db, struct db_stmt); + size_t num_slots, pos; + + /* Normalize query_id paths, because unit tests are compiled with this + * prefix. */ + if (strncmp(query_id, "./", 2) == 0) + query_id += 2; + + if (!db->in_transaction) + db_fatal("Attempting to prepare a db_stmt outside of a " + "transaction: %s", location); + + /* Look up the query by its ID */ + pos = hash_djb2(query_id) % db->queries->query_table_size; + for (;;) { + if (!db->queries->query_table[pos].name) + db_fatal("Could not resolve query %s", query_id); + if (streq(query_id, db->queries->query_table[pos].name)) { + stmt->query = &db->queries->query_table[pos]; + break; + } + pos = (pos + 1) % db->queries->query_table_size; + } + + num_slots = stmt->query->placeholders; + /* Allocate the slots for placeholders/bindings, zeroed next since + * that sets the type to DB_BINDING_UNINITIALIZED for later checks. */ + stmt->bindings = tal_arr(stmt, struct db_binding, num_slots); + for (size_t i=0; ibindings[i].type = DB_BINDING_UNINITIALIZED; + + stmt->location = location; + stmt->error = NULL; + stmt->db = db; + stmt->executed = false; + stmt->inner_stmt = NULL; + + tal_add_destructor(stmt, db_stmt_free); + + list_add(&db->pending_statements, &stmt->list); + +#if DEVELOPER + stmt->cols_used = NULL; +#endif /* DEVELOPER */ + + return stmt; +} + +#define db_prepare_v2(db,query) \ + db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) + +bool db_query_prepared(struct db_stmt *stmt) +{ + /* Make sure we don't accidentally execute a modifying query using a + * read-only path. */ + bool ret; + assert(stmt->query->readonly); + ret = stmt->db->config->query_fn(stmt); + stmt->executed = true; + list_del_from(&stmt->db->pending_statements, &stmt->list); + return ret; +} + +bool db_step(struct db_stmt *stmt) +{ + bool ret; + + assert(stmt->executed); + ret = stmt->db->config->step_fn(stmt); + +#if DEVELOPER + /* We only track cols_used if we return a result! */ + if (ret && !stmt->cols_used) { + stmt->cols_used = tal(stmt, struct strset); + strset_init(stmt->cols_used); + } +#endif + return ret; +} + +bool db_exec_prepared_v2(struct db_stmt *stmt TAKES) +{ + bool ret = stmt->db->config->exec_fn(stmt); + + /* If this was a write we need to bump the data_version upon commit. */ + stmt->db->dirty = stmt->db->dirty || !stmt->query->readonly; + + stmt->executed = true; + list_del_from(&stmt->db->pending_statements, &stmt->list); + + /* The driver itself doesn't call `fatal` since we want to override it + * for testing. Instead we check here that the error message is set if + * we report an error. */ + if (!ret) { + assert(stmt->error); + db_fatal("Error executing statement: %s", stmt->error); + } + + if (taken(stmt)) + tal_free(stmt); + + return ret; +} + +size_t db_count_changes(struct db_stmt *stmt) +{ + assert(stmt->executed); + return stmt->db->config->count_changes_fn(stmt); +} + +const char **db_changes(struct db *db) +{ + return db->changes; +} + +u64 db_last_insert_id_v2(struct db_stmt *stmt TAKES) +{ + u64 id; + assert(stmt->executed); + id = stmt->db->config->last_insert_id_fn(stmt); + + if (taken(stmt)) + tal_free(stmt); + + return id; +} + +/* We expect min changes (ie. BEGIN TRANSACTION): report if more. + * Optionally add "final" at the end (ie. COMMIT). */ +void db_report_changes(struct db *db, const char *final, size_t min) +{ + assert(db->changes); + assert(tal_count(db->changes) >= min); + + /* Having changes implies that we have a dirty TX. The opposite is + * currently not true, e.g., the postgres driver doesn't record + * changes yet. */ + assert(!tal_count(db->changes) || db->dirty); + + if (tal_count(db->changes) > min && db->report_changes_fn) + db->report_changes_fn(db); + db->changes = tal_free(db->changes); +} + +void db_changes_add(struct db_stmt *stmt, const char * expanded) +{ + struct db *db = stmt->db; + + if (stmt->query->readonly) { + return; + } + /* We get a "COMMIT;" after we've sent our changes. */ + if (!db->changes) { + assert(streq(expanded, "COMMIT;")); + return; + } + + tal_arr_expand(&db->changes, tal_strdup(db->changes, expanded)); +} + +#if DEVELOPER +void db_assert_no_outstanding_statements(struct db *db) +{ + struct db_stmt *stmt; + + stmt = list_top(&db->pending_statements, struct db_stmt, list); + if (stmt) + db_fatal("Unfinalized statement %s", stmt->location); +} +#else +void db_assert_no_outstanding_statements(struct db *db) +{ +} +#endif + + +static void destroy_db(struct db *db) +{ + db_assert_no_outstanding_statements(db); + + if (db->config->teardown_fn) + db->config->teardown_fn(db); +} + +static struct db_config *db_config_find(const char *dsn) +{ + size_t num_configs; + struct db_config **configs = autodata_get(db_backends, &num_configs); + const char *sep, *driver_name; + sep = strstr(dsn, "://"); + + if (!sep) + db_fatal("%s doesn't look like a valid data-source name (missing \"://\" separator.", dsn); + + driver_name = tal_strndup(tmpctx, dsn, sep - dsn); + + for (size_t i=0; iname)) { + tal_free(driver_name); + return configs[i]; + } + } + + tal_free(driver_name); + return NULL; +} + +static struct db_query_set *db_queries_find(const struct db_config *config) +{ + size_t num_queries; + struct db_query_set **queries = autodata_get(db_queries, &num_queries); + + for (size_t i = 0; i < num_queries; i++) { + if (streq(config->name, queries[i]->name)) { + return queries[i]; + } + } + return NULL; +} + +void db_prepare_for_changes(struct db *db) +{ + assert(!db->changes); + db->changes = tal_arr(db, const char *, 0); +} + +struct db *db_open(const tal_t *ctx, char *filename) +{ + struct db *db; + + db = tal(ctx, struct db); + db->filename = tal_strdup(db, filename); + list_head_init(&db->pending_statements); + if (!strstr(db->filename, "://")) + db_fatal("Could not extract driver name from \"%s\"", db->filename); + + db->config = db_config_find(db->filename); + if (!db->config) + db_fatal("Unable to find DB driver for %s", db->filename); + + db->queries = db_queries_find(db->config); + if (!db->queries) + db_fatal("Unable to find DB queries for %s", db->config->name); + + tal_add_destructor(db, destroy_db); + db->in_transaction = NULL; + db->changes = NULL; + + /* This must be outside a transaction, so catch it */ + assert(!db->in_transaction); + + db_prepare_for_changes(db); + if (db->config->setup_fn && !db->config->setup_fn(db)) + db_fatal("Error calling DB setup: %s", db->error); + db_report_changes(db, NULL, 0); + + return db; +} diff --git a/db/utils.h b/db/utils.h new file mode 100644 index 000000000000..2b5a21dd7571 --- /dev/null +++ b/db/utils.h @@ -0,0 +1,100 @@ +#ifndef LIGHTNING_DB_UTILS_H +#define LIGHTNING_DB_UTILS_H +#include "config.h" +#include +#include + +struct db; +struct db_stmt; + +size_t db_query_colnum(const struct db_stmt *stmt, + const char *colname); + +/* Return next 'row' result of statement */ +bool db_step(struct db_stmt *stmt); + +/* TODO(cdecker) Remove the v2 suffix after finishing the migration */ +#define db_prepare_v2(db,query) \ + db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) + + +/** + * db_exec_prepared -- Execute a prepared statement + * + * After preparing a statement using `db_prepare`, and after binding all + * non-null variables using the `db_bind_*` functions, it can be executed with + * this function. It is a small, transaction-aware, wrapper around `db_step`, + * that calls fatal() if the execution fails. This may take ownership of + * `stmt` if annotated with `take()`and will free it before returning. + * + * If you'd like to issue a query and access the rows returned by the query + * please use `db_query_prepared` instead, since this function will not expose + * returned results, and the `stmt` can only be used for calls to + * `db_count_changes` and `db_last_insert_id` after executing. + * + * @stmt: The prepared statement to execute + */ +bool db_exec_prepared_v2(struct db_stmt *stmt TAKES); + +/** + * db_query_prepared -- Execute a prepared query + * + * After preparing a query using `db_prepare`, and after binding all non-null + * variables using the `db_bind_*` functions, it can be executed with this + * function. This function must be called before calling `db_step` or any of + * the `db_col_*` column access functions. + * + * If you are not executing a read-only statement, please use + * `db_exec_prepared` instead. + * + * @stmt: The prepared statement to execute + */ +bool db_query_prepared(struct db_stmt *stmt); + +size_t db_count_changes(struct db_stmt *stmt); +void db_report_changes(struct db *db, const char *final, size_t min); +void db_prepare_for_changes(struct db *db); + +u64 db_last_insert_id_v2(struct db_stmt *stmt); + +/** + * db_prepare -- Prepare a DB query/command + * + * Create an instance of `struct db_stmt` that encapsulates a SQL query or command. + * + * @query MUST be wrapped in a `SQL()` macro call, since that allows the + * extraction and translation of the query into the target SQL dialect. + * + * It does not execute the query and does not check its validity, but + * allocates the placeholders detected in the query. The placeholders in the + * `stmt` can then be bound using the `db_bind_*` functions, and executed + * using `db_exec_prepared` for write-only statements and `db_query_prepared` + * for read-only statements. + * + * @db: Database to query/exec + * @query: The SQL statement to compile + */ +struct db_stmt *db_prepare_v2_(const char *location, struct db *db, + const char *query_id); + +/** + * db_open - Open or create a database + */ +struct db *db_open(const tal_t *ctx, char *filename); + +/** + * Report a statement that changes the wallet + * + * Allows the DB driver to report an expanded statement during + * execution. Changes are queued up and reported to the `db_write` plugin hook + * upon committing. + */ +void db_changes_add(struct db_stmt *db_stmt, const char * expanded); +void db_assert_no_outstanding_statements(struct db *db); + +/** + * Access pending changes that have been added to the current transaction. + */ +const char **db_changes(struct db *db); + +#endif /* LIGHTNING_DB_UTILS_H */ diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py index e6f70689c260..1aa9c446906d 100755 --- a/devtools/sql-rewrite.py +++ b/devtools/sql-rewrite.py @@ -126,7 +126,8 @@ def colname_htable(query): #include #include -#include +#include +#include #if HAVE_${f.upper()} % for colname, table in colhtables.items(): diff --git a/lightningd/Makefile b/lightningd/Makefile index 3a9b58bb768c..844d2748f333 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -130,9 +130,11 @@ LIGHTNINGD_COMMON_OBJS := \ common/wallet.o \ common/wire_error.o \ common/wireaddr.o \ + db/bindings.o \ + db/exec.o \ $(LIGHTNINGD_OBJS): $(LIGHTNINGD_HDRS) -$(WALLET_OBJS): $(LIGHTNINGD_HDRS) +$(WALLET_OBJS): $(LIGHTNINGD_HDRS) $(DB_HEADERS) # Only the plugin component needs to depend on this header. lightningd/plugin.o: plugins/list_of_builtin_plugins_gen.h @@ -140,6 +142,6 @@ lightningd/plugin.o: plugins/list_of_builtin_plugins_gen.h lightningd/channel_state_names_gen.h: lightningd/channel_state.h ccan/ccan/cdump/tools/cdump-enumstr ccan/ccan/cdump/tools/cdump-enumstr lightningd/channel_state.h > $@ -lightningd/lightningd: $(LIGHTNINGD_OBJS) $(WALLET_OBJS) $(LIGHTNINGD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(WIRE_ONION_OBJS) $(LIGHTNINGD_CONTROL_OBJS) $(HSMD_CLIENT_OBJS) +lightningd/lightningd: $(LIGHTNINGD_OBJS) $(WALLET_OBJS) $(LIGHTNINGD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(WIRE_ONION_OBJS) $(LIGHTNINGD_CONTROL_OBJS) $(HSMD_CLIENT_OBJS) $(DB_OBJS) include lightningd/test/Makefile diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 91711b62e072..251e3f11a03c 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index a998065ce4b5..f965ae90c587 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/invoice.c b/lightningd/invoice.c index f462a4e291b5..365826521799 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/io_loop_with_timers.c b/lightningd/io_loop_with_timers.c index 246ca9979f4b..9d52d6a4c789 100644 --- a/lightningd/io_loop_with_timers.c +++ b/lightningd/io_loop_with_timers.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index e80d3edbc07c..367e3ab4e9c5 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 113abcdb6ecb..440a34694c12 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -37,6 +37,7 @@ */ #include #include +#include #include #include #include @@ -53,6 +54,7 @@ #include #include #include +#include #include #include diff --git a/lightningd/offer.c b/lightningd/offer.c index e8118a493e70..48739c85f037 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -1,5 +1,7 @@ #include "config.h" #include +#include +#include #include #include #include diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 17e32f204be9..fa8c2139c813 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/options.c b/lightningd/options.c index d14b917fd31f..0577b0a1eda2 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 20932c567433..cf910dfa2ba3 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index 29a5de2aa16a..4d9d82b43877 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -1,6 +1,8 @@ #include "config.h" #include #include +#include +#include #include /* Struct containing all the information needed to deserialize and diff --git a/lightningd/subd.c b/lightningd/subd.c index 633976191db4..44bd85f3a2ea 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/wallet/Makefile b/wallet/Makefile index 2697febf142b..91b10246be29 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -7,37 +7,40 @@ WALLET_LIB_SRC := \ wallet/wallet.c \ wallet/walletrpc.c -WALLET_LIB_SRC_NOHDR := \ - wallet/reservation.c +WALLET_LIB_SRC_NOHDR := \ + wallet/reservation.c \ + wallet/db_queries_postgres.c \ + wallet/db_queries_sqlite3.c -WALLET_DB_DRIVERS := \ - wallet/db_postgres.c \ - wallet/db_sqlite3.c +WALLET_DB_QUERIES := \ + wallet/db_sqlite3_sqlgen.c \ + wallet/db_postgres_sqlgen.c -WALLET_SRC := $(WALLET_LIB_SRC) $(WALLET_LIB_SRC_NOHDR) $(WALLET_DB_DRIVERS) +WALLET_SRC := $(WALLET_LIB_SRC) $(WALLET_LIB_SRC_NOHDR) WALLET_HDRS := $(WALLET_LIB_SRC:.c=.h) WALLET_OBJS := $(WALLET_SRC:.c=.o) # Make sure these depend on everything. -ALL_C_SOURCES += $(WALLET_SRC) wallet/db_sqlite3_sqlgen.c wallet/db_postgres_sqlgen.c +ALL_C_SOURCES += $(WALLET_SRC) $(WALLET_DB_QUERIES) ALL_C_HEADERS += $(WALLET_HDRS) -# Each database driver depends on its rewritten statements. -wallet/db_sqlite3.o: wallet/db_sqlite3_sqlgen.c -wallet/db_postgres.o: wallet/db_postgres_sqlgen.c +# Query sets depend on the rewritten queries +wallet/db_queries_sqlite3.o: $(WALLET_DB_QUERIES) +wallet/db_queries_postgres.o: $(WALLET_DB_QUERIES) # The following files contain SQL-annotated statements that we need to extact -SQL_FILES := \ +WALLET_SQL_FILES := \ + $(DB_SQL_FILES) \ wallet/db.c \ wallet/invoices.c \ wallet/wallet.c \ wallet/test/run-db.c \ wallet/test/run-wallet.c \ -wallet/statements_gettextgen.po: $(SQL_FILES) $(FORCE) +wallet/statements_gettextgen.po: $(WALLET_SQL_FILES) $(FORCE) @if $(call SHA256STAMP_CHANGED); then \ - $(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 $@ $(WALLET_SQL_FILES) && $(call SHA256STAMP,# ,)); \ fi wallet/db_%_sqlgen.c: wallet/statements_gettextgen.po devtools/sql-rewrite.py $(FORCE) @@ -50,7 +53,6 @@ clean: wallet-maintainer-clean wallet-maintainer-clean: $(RM) wallet/statements.po $(RM) wallet/statements_gettextgen.po - $(RM) wallet/db_sqlite3_sqlgen.c - $(RM) wallet/db_postgres_sqlgen.c + $(RM) $(WALLET_DB_QUERIES) include wallet/test/Makefile diff --git a/wallet/db.c b/wallet/db.c index 23fa2866cbf0..3225579ebd43 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -4,20 +4,19 @@ #include #include #include -#include #include -#include #include +#include +#include +#include +#include #include #include #include #include #include -#include #include -#define NSEC_IN_SEC 1000000000 - /* Small container for things that are needed by migrations. The * fields are guaranteed to be initialized and can be relied upon when * migrating. @@ -872,337 +871,6 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE channel_funding_inflights ADD lease_fee BIGINT DEFAULT 0"), NULL}, }; -/* Leak tracking. */ -#if DEVELOPER -static void db_assert_no_outstanding_statements(struct db *db) -{ - struct db_stmt *stmt; - - stmt = list_top(&db->pending_statements, struct db_stmt, list); - if (stmt) - db_fatal("Unfinalized statement %s", stmt->location); -} -#else -static void db_assert_no_outstanding_statements(struct db *db) -{ -} -#endif - -static void db_stmt_free(struct db_stmt *stmt) -{ - if (!stmt->executed) - fatal("Freeing an un-executed statement from %s: %s", - stmt->location, stmt->query->query); -#if DEVELOPER - /* If they never got a db_step, we don't track */ - if (stmt->cols_used) { - for (size_t i = 0; i < stmt->query->num_colnames; i++) { - if (!stmt->query->colnames[i].sqlname) - continue; - if (!strset_get(stmt->cols_used, - stmt->query->colnames[i].sqlname)) { - log_broken(stmt->db->log, - "Never accessed column %s in query %s", - stmt->query->colnames[i].sqlname, - stmt->query->query); - } - } - strset_clear(stmt->cols_used); - } -#endif - if (stmt->inner_stmt) - stmt->db->config->stmt_free_fn(stmt); - assert(stmt->inner_stmt == NULL); -} - -/* Matches the hash function used in devtools/sql-rewrite.py */ -static u32 hash_djb2(const char *str) -{ - u32 hash = 5381; - for (size_t i = 0; str[i]; i++) - hash = ((hash << 5) + hash) ^ str[i]; - return hash; -} - -struct db_stmt *db_prepare_v2_(const char *location, struct db *db, - const char *query_id) -{ - struct db_stmt *stmt = tal(db, struct db_stmt); - size_t num_slots, pos; - - /* Normalize query_id paths, because unit tests are compiled with this - * prefix. */ - if (strncmp(query_id, "./", 2) == 0) - query_id += 2; - - if (!db->in_transaction) - db_fatal("Attempting to prepare a db_stmt outside of a " - "transaction: %s", location); - - /* Look up the query by its ID */ - pos = hash_djb2(query_id) % db->config->query_table_size; - for (;;) { - if (!db->config->query_table[pos].name) - fatal("Could not resolve query %s", query_id); - if (streq(query_id, db->config->query_table[pos].name)) { - stmt->query = &db->config->query_table[pos]; - break; - } - pos = (pos + 1) % db->config->query_table_size; - } - - num_slots = stmt->query->placeholders; - /* Allocate the slots for placeholders/bindings, zeroed next since - * that sets the type to DB_BINDING_UNINITIALIZED for later checks. */ - stmt->bindings = tal_arr(stmt, struct db_binding, num_slots); - for (size_t i=0; ibindings[i].type = DB_BINDING_UNINITIALIZED; - - stmt->location = location; - stmt->error = NULL; - stmt->db = db; - stmt->executed = false; - stmt->inner_stmt = NULL; - - tal_add_destructor(stmt, db_stmt_free); - - list_add(&db->pending_statements, &stmt->list); - -#if DEVELOPER - stmt->cols_used = NULL; -#endif /* DEVELOPER */ - - return stmt; -} - -#define db_prepare_v2(db,query) \ - db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) - -bool db_step(struct db_stmt *stmt) -{ - bool ret; - - assert(stmt->executed); - ret = stmt->db->config->step_fn(stmt); - -#if DEVELOPER - /* We only track cols_used if we return a result! */ - if (ret && !stmt->cols_used) { - stmt->cols_used = tal(stmt, struct strset); - strset_init(stmt->cols_used); - } -#endif - return ret; -} - -size_t db_count_changes(struct db_stmt *stmt) -{ - assert(stmt->executed); - return stmt->db->config->count_changes_fn(stmt); -} - -u64 db_last_insert_id_v2(struct db_stmt *stmt TAKES) -{ - u64 id; - assert(stmt->executed); - id = stmt->db->config->last_insert_id_fn(stmt); - - if (taken(stmt)) - tal_free(stmt); - - return id; -} - -static void destroy_db(struct db *db) -{ - db_assert_no_outstanding_statements(db); - - if (db->config->teardown_fn) - db->config->teardown_fn(db); -} - -/* We expect min changes (ie. BEGIN TRANSACTION): report if more. - * Optionally add "final" at the end (ie. COMMIT). */ -static void db_report_changes(struct db *db, const char *final, size_t min) -{ - assert(db->changes); - assert(tal_count(db->changes) >= min); - - /* Having changes implies that we have a dirty TX. The opposite is - * currently not true, e.g., the postgres driver doesn't record - * changes yet. */ - assert(!tal_count(db->changes) || db->dirty); - - if (tal_count(db->changes) > min) - plugin_hook_db_sync(db); - db->changes = tal_free(db->changes); -} - -static void db_prepare_for_changes(struct db *db) -{ - assert(!db->changes); - db->changes = tal_arr(db, const char *, 0); -} - -bool db_in_transaction(struct db *db) -{ - return db->in_transaction; -} - -void db_begin_transaction_(struct db *db, const char *location) -{ - bool ok; - if (db->in_transaction) - db_fatal("Already in transaction from %s", db->in_transaction); - - /* No writes yet. */ - db->dirty = false; - - db_prepare_for_changes(db); - ok = db->config->begin_tx_fn(db); - if (!ok) - db_fatal("Failed to start DB transaction: %s", db->error); - - db->in_transaction = location; -} - -/* By making the update conditional on the current value we expect we - * are implementing an optimistic lock: if the update results in - * changes on the DB we know that the data_version did not change - * under our feet and no other transaction ran in the meantime. - * - * Notice that this update effectively locks the row, so that other - * operations attempting to change this outside the transaction will - * wait for this transaction to complete. The external change will - * ultimately fail the changes test below, it'll just delay its abort - * until our transaction is committed. - */ -static void db_data_version_incr(struct db *db) -{ - struct db_stmt *stmt = db_prepare_v2( - db, SQL("UPDATE vars " - "SET intval = intval + 1 " - "WHERE name = 'data_version'" - " AND intval = ?")); - db_bind_int(stmt, 0, db->data_version); - db_exec_prepared_v2(stmt); - if (db_count_changes(stmt) != 1) - fatal("Optimistic lock on the database failed. There may be a " - "concurrent access to the database. Aborting since " - "concurrent access is unsafe."); - tal_free(stmt); - db->data_version++; -} - -void db_commit_transaction(struct db *db) -{ - bool ok; - assert(db->in_transaction); - db_assert_no_outstanding_statements(db); - - /* Increment before reporting changes to an eventual plugin. */ - if (db->dirty) - db_data_version_incr(db); - - db_report_changes(db, NULL, 0); - ok = db->config->commit_tx_fn(db); - - if (!ok) - db_fatal("Failed to commit DB transaction: %s", db->error); - - db->in_transaction = NULL; - db->dirty = false; -} - -static struct db_config *db_config_find(const char *dsn) -{ - size_t num_configs; - struct db_config **configs = autodata_get(db_backends, &num_configs); - const char *sep, *driver_name; - sep = strstr(dsn, "://"); - - if (!sep) - db_fatal("%s doesn't look like a valid data-source name (missing \"://\" separator.", dsn); - - driver_name = tal_strndup(tmpctx, dsn, sep - dsn); - - for (size_t i=0; iname)) { - tal_free(driver_name); - return configs[i]; - } - } - - tal_free(driver_name); - return NULL; -} - -/** - * db_open - Open or create a sqlite3 database - */ -static struct db *db_open(const tal_t *ctx, char *filename) -{ - struct db *db; - - db = tal(ctx, struct db); - db->filename = tal_strdup(db, filename); - list_head_init(&db->pending_statements); - if (!strstr(db->filename, "://")) - db_fatal("Could not extract driver name from \"%s\"", db->filename); - - db->config = db_config_find(db->filename); - if (!db->config) - db_fatal("Unable to find DB driver for %s", db->filename); - - tal_add_destructor(db, destroy_db); - db->in_transaction = NULL; - db->changes = NULL; - - /* This must be outside a transaction, so catch it */ - assert(!db->in_transaction); - - db_prepare_for_changes(db); - if (db->config->setup_fn && !db->config->setup_fn(db)) - fatal("Error calling DB setup: %s", db->error); - db_report_changes(db, NULL, 0); - - return db; -} - -/** - * db_get_version - Determine the current DB schema version - * - * Will attempt to determine the current schema version of the - * database @db by querying the `version` table. If the table does not - * exist it'll return schema version -1, so that migration 0 is - * applied, which should create the `version` table. - */ -static int db_get_version(struct db *db) -{ - int res = -1; - struct db_stmt *stmt = db_prepare_v2(db, SQL("SELECT version FROM version LIMIT 1")); - - /* - * Tentatively execute a query, but allow failures. Some databases - * like postgres will terminate the DB transaction if there is an - * error during the execution of a query, e.g., trying to access a - * table that doesn't exist yet, so we need to terminate and restart - * the DB transaction. - */ - if (!db_query_prepared(stmt)) { - db_commit_transaction(stmt->db); - db_begin_transaction(stmt->db); - tal_free(stmt); - return res; - } - - if (db_step(stmt)) - res = db_col_int(stmt, "version"); - - tal_free(stmt); - return res; -} - /** * db_migrate - Apply all remaining migrations from the current version */ @@ -1221,12 +889,12 @@ static bool db_migrate(struct lightningd *ld, struct db *db, available = ARRAY_SIZE(dbmigrations) - 1; if (current == -1) - log_info(db->log, "Creating database"); + log_info(ld->log, "Creating database"); else if (available < current) db_fatal("Refusing to migrate down from version %u to %u", current, available); else if (current != available) - log_info(db->log, "Updating database from version %u to %u", + log_info(ld->log, "Updating database from version %u to %u", current, available); while (current < available) { @@ -1260,24 +928,13 @@ static bool db_migrate(struct lightningd *ld, struct db *db, return current != orig; } -u32 db_data_version_get(struct db *db) -{ - struct db_stmt *stmt; - u32 version; - stmt = db_prepare_v2(db, SQL("SELECT intval FROM vars WHERE name = 'data_version'")); - db_query_prepared(stmt); - db_step(stmt); - version = db_col_int(stmt, "intval"); - tal_free(stmt); - return version; -} - struct db *db_setup(const tal_t *ctx, struct lightningd *ld, const struct ext_key *bip32_base) { struct db *db = db_open(ctx, ld->wallet_dsn); bool migrated; - db->log = new_log(db, ld->log_book, NULL, "database"); + + db->report_changes_fn = plugin_hook_db_sync; db_begin_transaction(db); @@ -1295,44 +952,6 @@ struct db *db_setup(const tal_t *ctx, struct lightningd *ld, return db; } -s64 db_get_intvar(struct db *db, char *varname, s64 defval) -{ - s64 res = defval; - struct db_stmt *stmt = db_prepare_v2( - db, SQL("SELECT intval FROM vars WHERE name= ? LIMIT 1")); - db_bind_text(stmt, 0, varname); - if (!db_query_prepared(stmt)) - goto done; - - if (db_step(stmt)) - res = db_col_int(stmt, "intval"); - -done: - tal_free(stmt); - return res; -} - -void db_set_intvar(struct db *db, char *varname, s64 val) -{ - size_t changes; - struct db_stmt *stmt = db_prepare_v2(db, SQL("UPDATE vars SET intval=? WHERE name=?;")); - db_bind_int(stmt, 0, val); - db_bind_text(stmt, 1, varname); - if (!db_exec_prepared_v2(stmt)) - db_fatal("Error executing update: %s", stmt->error); - changes = db_count_changes(stmt); - tal_free(stmt); - - if (changes == 0) { - stmt = db_prepare_v2(db, SQL("INSERT INTO vars (name, intval) VALUES (?, ?);")); - db_bind_text(stmt, 0, varname); - db_bind_int(stmt, 1, val); - if (!db_exec_prepared_v2(stmt)) - db_fatal("Error executing insert: %s", stmt->error); - tal_free(stmt); - } -} - /* Will apply the current config fee settings to all channels */ static void migrate_pr2342_feerate_per_channel(struct lightningd *ld, struct db *db, const struct migration_context *mc) @@ -1770,617 +1389,3 @@ void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db, tal_free(stmt); } - -void db_bind_null(struct db_stmt *stmt, int pos) -{ - assert(pos < tal_count(stmt->bindings)); - stmt->bindings[pos].type = DB_BINDING_NULL; -} - -void db_bind_int(struct db_stmt *stmt, int pos, int val) -{ - assert(pos < tal_count(stmt->bindings)); - memcheck(&val, sizeof(val)); - stmt->bindings[pos].type = DB_BINDING_INT; - stmt->bindings[pos].v.i = val; -} - -void db_bind_u64(struct db_stmt *stmt, int pos, u64 val) -{ - memcheck(&val, sizeof(val)); - assert(pos < tal_count(stmt->bindings)); - stmt->bindings[pos].type = DB_BINDING_UINT64; - stmt->bindings[pos].v.u64 = val; -} - -void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len) -{ - assert(pos < tal_count(stmt->bindings)); - stmt->bindings[pos].type = DB_BINDING_BLOB; - stmt->bindings[pos].v.blob = memcheck(val, len); - stmt->bindings[pos].len = len; -} - -void db_bind_text(struct db_stmt *stmt, int pos, const char *val) -{ - assert(pos < tal_count(stmt->bindings)); - stmt->bindings[pos].type = DB_BINDING_TEXT; - stmt->bindings[pos].v.text = val; - stmt->bindings[pos].len = strlen(val); -} - -void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p) -{ - db_bind_blob(stmt, pos, p->r, sizeof(struct preimage)); -} - -void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s) -{ - db_bind_blob(stmt, pos, s->u.u8, sizeof(struct sha256)); -} - -void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s) -{ - db_bind_sha256(stmt, pos, &s->sha); -} - -void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s) -{ - assert(sizeof(s->data) == 32); - db_bind_blob(stmt, pos, s->data, sizeof(s->data)); -} - -void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s) -{ - size_t num = tal_count(s), elsize = sizeof(s->data); - u8 *ser = tal_arr(stmt, u8, num * elsize); - - for (size_t i = 0; i < num; ++i) - memcpy(ser + i * elsize, &s[i], elsize); - - db_bind_blob(stmt, col, ser, tal_count(ser)); -} - -void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t) -{ - db_bind_sha256d(stmt, pos, &t->shad); -} - -void db_bind_channel_id(struct db_stmt *stmt, int pos, const struct channel_id *id) -{ - db_bind_blob(stmt, pos, id->id, sizeof(id->id)); -} - -void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *id) -{ - db_bind_blob(stmt, pos, id->k, sizeof(id->k)); -} - -void db_bind_node_id_arr(struct db_stmt *stmt, int col, - const struct node_id *ids) -{ - /* Copy into contiguous array: ARM will add padding to struct node_id! */ - size_t n = tal_count(ids); - u8 *arr = tal_arr(stmt, u8, n * sizeof(ids[0].k)); - - for (size_t i = 0; i < n; ++i) { - assert(node_id_valid(&ids[i])); - memcpy(arr + sizeof(ids[i].k) * i, - ids[i].k, - sizeof(ids[i].k)); - } - db_bind_blob(stmt, col, arr, tal_count(arr)); -} - -void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *pk) -{ - u8 *der = tal_arr(stmt, u8, PUBKEY_CMPR_LEN); - pubkey_to_der(der, pk); - db_bind_blob(stmt, pos, der, PUBKEY_CMPR_LEN); -} - -void db_bind_short_channel_id(struct db_stmt *stmt, int col, - const struct short_channel_id *id) -{ - char *ser = short_channel_id_to_str(stmt, id); - db_bind_text(stmt, col, ser); -} - -void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, - const struct short_channel_id *id) -{ - u8 *ser = tal_arr(stmt, u8, 0); - size_t num = tal_count(id); - - for (size_t i = 0; i < num; ++i) - towire_short_channel_id(&ser, &id[i]); - - db_bind_talarr(stmt, col, ser); -} - -void db_bind_signature(struct db_stmt *stmt, int col, - const secp256k1_ecdsa_signature *sig) -{ - u8 *buf = tal_arr(stmt, u8, 64); - int ret = secp256k1_ecdsa_signature_serialize_compact(secp256k1_ctx, - buf, sig); - assert(ret == 1); - db_bind_blob(stmt, col, buf, 64); -} - -void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t) -{ - u64 timestamp = t.ts.tv_nsec + (((u64) t.ts.tv_sec) * ((u64) NSEC_IN_SEC)); - db_bind_u64(stmt, col, timestamp); -} - -void db_bind_tx(struct db_stmt *stmt, int col, const struct wally_tx *tx) -{ - u8 *ser = linearize_wtx(stmt, tx); - assert(ser); - db_bind_talarr(stmt, col, ser); -} - -void db_bind_psbt(struct db_stmt *stmt, int col, const struct wally_psbt *psbt) -{ - size_t bytes_written; - const u8 *ser = psbt_get_bytes(stmt, psbt, &bytes_written); - assert(ser); - db_bind_blob(stmt, col, ser, bytes_written); -} - -void db_bind_amount_msat(struct db_stmt *stmt, int pos, - const struct amount_msat *msat) -{ - db_bind_u64(stmt, pos, msat->millisatoshis); /* Raw: low level function */ -} - -void db_bind_amount_sat(struct db_stmt *stmt, int pos, - const struct amount_sat *sat) -{ - db_bind_u64(stmt, pos, sat->satoshis); /* Raw: low level function */ -} - -void db_bind_json_escape(struct db_stmt *stmt, int pos, - const struct json_escape *esc) -{ - db_bind_text(stmt, pos, esc->s); -} - -void db_bind_onionreply(struct db_stmt *stmt, int pos, const struct onionreply *r) -{ - db_bind_talarr(stmt, pos, r->contents); -} - -void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr) -{ - if (!arr) - db_bind_null(stmt, col); - else - db_bind_blob(stmt, col, arr, tal_bytelen(arr)); -} - -/* Local helpers once you have column number */ -static bool db_column_is_null(struct db_stmt *stmt, int col) -{ - return stmt->db->config->column_is_null_fn(stmt, col); -} - -/* Returns true (and warns) if it's nul */ -static bool db_column_null_warn(struct db_stmt *stmt, const char *colname, - int col) -{ - if (!db_column_is_null(stmt, col)) - return false; - - log_broken(stmt->db->log, "Accessing a null column %s/%i in query %s", - colname, col, - stmt->query->query); - return true; -} - -static size_t db_column_bytes(struct db_stmt *stmt, int col) -{ - if (db_column_is_null(stmt, col)) - return 0; - return stmt->db->config->column_bytes_fn(stmt, col); -} - -static const void *db_column_blob(struct db_stmt *stmt, int col) -{ - if (db_column_is_null(stmt, col)) - return NULL; - return stmt->db->config->column_blob_fn(stmt, col); -} - - -u64 db_col_u64(struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - - if (db_column_null_warn(stmt, colname, col)) - return 0; - - return stmt->db->config->column_u64_fn(stmt, col); -} - -int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def) -{ - size_t col = db_query_colnum(stmt, colname); - - if (db_column_is_null(stmt, col)) - return def; - else - return stmt->db->config->column_int_fn(stmt, col); -} - -int db_col_int(struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - - if (db_column_null_warn(stmt, colname, col)) - return 0; - - return stmt->db->config->column_int_fn(stmt, col); -} - -size_t db_col_bytes(struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - - if (db_column_null_warn(stmt, colname, col)) - return 0; - - return stmt->db->config->column_bytes_fn(stmt, col); -} - -int db_col_is_null(struct db_stmt *stmt, const char *colname) -{ - return db_column_is_null(stmt, db_query_colnum(stmt, colname)); -} - -const void *db_col_blob(struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - - if (db_column_null_warn(stmt, colname, col)) - return NULL; - - return stmt->db->config->column_blob_fn(stmt, col); -} - -char *db_col_strdup(const tal_t *ctx, - struct db_stmt *stmt, - const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - - if (db_column_null_warn(stmt, colname, col)) - return NULL; - - return tal_strdup(ctx, (char *)stmt->db->config->column_text_fn(stmt, col)); -} - -void db_col_preimage(struct db_stmt *stmt, const char *colname, - struct preimage *preimage) -{ - size_t col = db_query_colnum(stmt, colname); - const u8 *raw; - size_t size = sizeof(struct preimage); - assert(db_column_bytes(stmt, col) == size); - raw = db_column_blob(stmt, col); - memcpy(preimage, raw, size); -} - -void db_col_channel_id(struct db_stmt *stmt, const char *colname, struct channel_id *dest) -{ - size_t col = db_query_colnum(stmt, colname); - - assert(db_column_bytes(stmt, col) == sizeof(dest->id)); - memcpy(dest->id, db_column_blob(stmt, col), sizeof(dest->id)); -} - -void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *dest) -{ - size_t col = db_query_colnum(stmt, colname); - - assert(db_column_bytes(stmt, col) == sizeof(dest->k)); - memcpy(dest->k, db_column_blob(stmt, col), sizeof(dest->k)); -} - -struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, - const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - struct node_id *ret; - size_t n = db_column_bytes(stmt, col) / sizeof(ret->k); - const u8 *arr = db_column_blob(stmt, col); - assert(n * sizeof(ret->k) == (size_t)db_column_bytes(stmt, col)); - ret = tal_arr(ctx, struct node_id, n); - - db_column_null_warn(stmt, colname, col); - for (size_t i = 0; i < n; i++) - memcpy(ret[i].k, arr + i * sizeof(ret[i].k), sizeof(ret[i].k)); - - return ret; -} - -void db_col_pubkey(struct db_stmt *stmt, - const char *colname, - struct pubkey *dest) -{ - size_t col = db_query_colnum(stmt, colname); - bool ok; - assert(db_column_bytes(stmt, col) == PUBKEY_CMPR_LEN); - ok = pubkey_from_der(db_column_blob(stmt, col), PUBKEY_CMPR_LEN, dest); - assert(ok); -} - -/* Yes, we put this in as a string. Past mistakes; do not use! */ -bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, - struct short_channel_id *dest) -{ - size_t col = db_query_colnum(stmt, colname); - const char *source = db_column_blob(stmt, col); - size_t sourcelen = db_column_bytes(stmt, col); - db_column_null_warn(stmt, colname, col); - return short_channel_id_from_str(source, sourcelen, dest); -} - -struct short_channel_id * -db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - const u8 *ser; - size_t len; - struct short_channel_id *ret; - - db_column_null_warn(stmt, colname, col); - ser = db_column_blob(stmt, col); - len = db_column_bytes(stmt, col); - ret = tal_arr(ctx, struct short_channel_id, 0); - - while (len != 0) { - struct short_channel_id scid; - fromwire_short_channel_id(&ser, &len, &scid); - tal_arr_expand(&ret, scid); - } - - return ret; -} - -bool db_col_signature(struct db_stmt *stmt, const char *colname, - secp256k1_ecdsa_signature *sig) -{ - size_t col = db_query_colnum(stmt, colname); - assert(db_column_bytes(stmt, col) == 64); - return secp256k1_ecdsa_signature_parse_compact( - secp256k1_ctx, sig, db_column_blob(stmt, col)) == 1; -} - -struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname) -{ - struct timeabs t; - u64 timestamp = db_col_u64(stmt, colname); - t.ts.tv_sec = timestamp / NSEC_IN_SEC; - t.ts.tv_nsec = timestamp % NSEC_IN_SEC; - return t; - -} - -struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - const u8 *src = db_column_blob(stmt, col); - size_t len = db_column_bytes(stmt, col); - - db_column_null_warn(stmt, colname, col); - return pull_bitcoin_tx(ctx, &src, &len); -} - -struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - const u8 *src = db_column_blob(stmt, col); - size_t len = db_column_bytes(stmt, col); - - db_column_null_warn(stmt, colname, col); - return psbt_from_bytes(ctx, src, len); -} - -struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname) -{ - struct wally_psbt *psbt = db_col_psbt(ctx, stmt, colname); - if (!psbt) - return NULL; - return bitcoin_tx_with_psbt(ctx, psbt); -} - -void *db_col_arr_(const tal_t *ctx, struct db_stmt *stmt, const char *colname, - size_t bytes, const char *label, const char *caller) -{ - size_t col = db_query_colnum(stmt, colname); - size_t sourcelen; - void *p; - - if (db_column_is_null(stmt, col)) - return NULL; - - sourcelen = db_column_bytes(stmt, col); - - if (sourcelen % bytes != 0) - db_fatal("%s: %s/%zu column size for %zu not a multiple of %s (%zu)", - caller, colname, col, sourcelen, label, bytes); - - p = tal_arr_label(ctx, char, sourcelen, label); - memcpy(p, db_column_blob(stmt, col), sourcelen); - return p; -} - -void db_col_amount_msat_or_default(struct db_stmt *stmt, - const char *colname, - struct amount_msat *msat, - struct amount_msat def) -{ - size_t col = db_query_colnum(stmt, colname); - - if (db_column_is_null(stmt, col)) - *msat = def; - else - msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ -} - -void db_col_amount_msat(struct db_stmt *stmt, const char *colname, - struct amount_msat *msat) -{ - msat->millisatoshis = db_col_u64(stmt, colname); /* Raw: low level function */ -} - -void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat) -{ - sat->satoshis = db_col_u64(stmt, colname); /* Raw: low level function */ -} - -struct json_escape *db_col_json_escape(const tal_t *ctx, - struct db_stmt *stmt, const char *colname) -{ - size_t col = db_query_colnum(stmt, colname); - - return json_escape_string_(ctx, db_column_blob(stmt, col), - db_column_bytes(stmt, col)); -} - -void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha) -{ - size_t col = db_query_colnum(stmt, colname); - const u8 *raw; - size_t size = sizeof(struct sha256); - assert(db_column_bytes(stmt, col) == size); - raw = db_column_blob(stmt, col); - memcpy(sha, raw, size); -} - -void db_col_sha256d(struct db_stmt *stmt, const char *colname, - struct sha256_double *shad) -{ - size_t col = db_query_colnum(stmt, colname); - const u8 *raw; - size_t size = sizeof(struct sha256_double); - assert(db_column_bytes(stmt, col) == size); - raw = db_column_blob(stmt, col); - memcpy(shad, raw, size); -} - -void db_col_secret(struct db_stmt *stmt, const char *colname, struct secret *s) -{ - size_t col = db_query_colnum(stmt, colname); - const u8 *raw; - assert(db_column_bytes(stmt, col) == sizeof(struct secret)); - raw = db_column_blob(stmt, col); - memcpy(s, raw, sizeof(struct secret)); -} - -struct secret *db_col_secret_arr(const tal_t *ctx, - struct db_stmt *stmt, - const char *colname) -{ - return db_col_arr(ctx, stmt, colname, struct secret); -} - -void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t) -{ - db_col_sha256d(stmt, colname, &t->shad); -} - -struct onionreply *db_col_onionreply(const tal_t *ctx, - struct db_stmt *stmt, const char *colname) -{ - struct onionreply *r = tal(ctx, struct onionreply); - r->contents = db_col_arr(ctx, stmt, colname, u8); - return r; -} - -bool db_exec_prepared_v2(struct db_stmt *stmt TAKES) -{ - bool ret = stmt->db->config->exec_fn(stmt); - - /* If this was a write we need to bump the data_version upon commit. */ - stmt->db->dirty = stmt->db->dirty || !stmt->query->readonly; - - stmt->executed = true; - list_del_from(&stmt->db->pending_statements, &stmt->list); - - /* The driver itself doesn't call `fatal` since we want to override it - * for testing. Instead we check here that the error message is set if - * we report an error. */ - if (!ret) { - assert(stmt->error); - db_fatal("Error executing statement: %s", stmt->error); - } - - if (taken(stmt)) - tal_free(stmt); - - return ret; -} - -bool db_query_prepared(struct db_stmt *stmt) -{ - /* Make sure we don't accidentally execute a modifying query using a - * read-only path. */ - bool ret; - assert(stmt->query->readonly); - ret = stmt->db->config->query_fn(stmt); - stmt->executed = true; - list_del_from(&stmt->db->pending_statements, &stmt->list); - return ret; -} - -void db_changes_add(struct db_stmt *stmt, const char * expanded) -{ - struct db *db = stmt->db; - - if (stmt->query->readonly) { - return; - } - /* We get a "COMMIT;" after we've sent our changes. */ - if (!db->changes) { - assert(streq(expanded, "COMMIT;")); - return; - } - - tal_arr_expand(&db->changes, tal_strdup(db->changes, expanded)); -} - -const char **db_changes(struct db *db) -{ - return db->changes; -} - -size_t db_query_colnum(const struct db_stmt *stmt, - const char *colname) -{ - u32 col; - - assert(stmt->query->colnames != NULL); - - col = hash_djb2(colname) % stmt->query->num_colnames; - /* Will crash on NULL, which is the Right Thing */ - while (!streq(stmt->query->colnames[col].sqlname, - colname)) { - col = (col + 1) % stmt->query->num_colnames; - } - -#if DEVELOPER - strset_add(stmt->cols_used, colname); -#endif - - return stmt->query->colnames[col].val; -} - -void db_col_ignore(struct db_stmt *stmt, const char *colname) -{ -#if DEVELOPER - db_query_colnum(stmt, colname); -#endif -} diff --git a/wallet/db.h b/wallet/db.h index e51e75fd6b71..3d76d097a5df 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -2,45 +2,10 @@ #define LIGHTNING_WALLET_DB_H #include "config.h" -#include -#include -#include -#include -#include -#include - -struct channel_id; struct ext_key; struct lightningd; -struct log; -struct node_id; -struct onionreply; struct db_stmt; struct db; -struct wally_psbt; -struct wally_tx; - -/** - * Macro to annotate a named SQL query. - * - * This macro is used to annotate SQL queries that might need rewriting for - * different SQL dialects. It is used both as a marker for the query - * extraction logic in devtools/sql-rewrite.py to identify queries, as well as - * a way to swap out the query text with it's name so that the query execution - * engine can then look up the rewritten query using its name. - * - */ -#define NAMED_SQL(name,x) x - -/** - * Simple annotation macro that auto-generates names for NAMED_SQL - * - * If this macro is changed it is likely that the extraction logic in - * devtools/sql-rewrite.py needs to change as well, since they need to - * generate identical names to work correctly. - */ -#define SQL(x) NAMED_SQL( __FILE__ ":" stringify(__COUNTER__), x) - /** * db_setup - Open a the lightningd database and update the schema @@ -57,204 +22,4 @@ struct wally_tx; struct db *db_setup(const tal_t *ctx, struct lightningd *ld, const struct ext_key *bip32_base); -/** - * db_begin_transaction - Begin a transaction - * - * Begin a new DB transaction. fatal() on database error. - */ -#define db_begin_transaction(db) \ - db_begin_transaction_((db), __FILE__ ":" stringify(__LINE__)) -void db_begin_transaction_(struct db *db, const char *location); - -bool db_in_transaction(struct db *db); - -/** - * db_commit_transaction - Commit a running transaction - * - * Requires that we are currently in a transaction. fatal() if we - * fail to commit. - */ -void db_commit_transaction(struct db *db); - -/** - * db_set_intvar - Set an integer variable in the database - * - * Utility function to store generic integer values in the - * database. - */ -void db_set_intvar(struct db *db, char *varname, s64 val); - -/** - * db_get_intvar - Retrieve an integer variable from the database - * - * Either returns the value in the database, or @defval if - * the query failed or no such variable exists. - */ -s64 db_get_intvar(struct db *db, char *varname, s64 defval); - -void db_bind_null(struct db_stmt *stmt, int pos); -void db_bind_int(struct db_stmt *stmt, int pos, int val); -void db_bind_u64(struct db_stmt *stmt, int pos, u64 val); -void db_bind_blob(struct db_stmt *stmt, int pos, const u8 *val, size_t len); -void db_bind_text(struct db_stmt *stmt, int pos, const char *val); -void db_bind_preimage(struct db_stmt *stmt, int pos, const struct preimage *p); -void db_bind_sha256(struct db_stmt *stmt, int pos, const struct sha256 *s); -void db_bind_sha256d(struct db_stmt *stmt, int pos, const struct sha256_double *s); -void db_bind_secret(struct db_stmt *stmt, int pos, const struct secret *s); -void db_bind_secret_arr(struct db_stmt *stmt, int col, const struct secret *s); -void db_bind_txid(struct db_stmt *stmt, int pos, const struct bitcoin_txid *t); -void db_bind_channel_id(struct db_stmt *stmt, int pos, const struct channel_id *id); -void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *ni); -void db_bind_node_id_arr(struct db_stmt *stmt, int col, - const struct node_id *ids); -void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *p); -void db_bind_short_channel_id(struct db_stmt *stmt, int col, - const struct short_channel_id *id); -void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, - const struct short_channel_id *id); -void db_bind_signature(struct db_stmt *stmt, int col, - const secp256k1_ecdsa_signature *sig); -void db_bind_timeabs(struct db_stmt *stmt, int col, struct timeabs t); -void db_bind_tx(struct db_stmt *stmt, int col, const struct wally_tx *tx); -void db_bind_psbt(struct db_stmt *stmt, int col, const struct wally_psbt *psbt); -void db_bind_amount_msat(struct db_stmt *stmt, int pos, - const struct amount_msat *msat); -void db_bind_amount_sat(struct db_stmt *stmt, int pos, - const struct amount_sat *sat); -void db_bind_json_escape(struct db_stmt *stmt, int pos, - const struct json_escape *esc); -void db_bind_onionreply(struct db_stmt *stmt, int col, - const struct onionreply *r); -void db_bind_talarr(struct db_stmt *stmt, int col, const u8 *arr); - -bool db_step(struct db_stmt *stmt); - -/* Modern variants: get columns by name from SELECT */ -/* Bridge function to get column number from SELECT - (must exist) */ -size_t db_query_colnum(const struct db_stmt *stmt, const char *colname); - -u64 db_col_u64(struct db_stmt *stmt, const char *colname); -int db_col_int(struct db_stmt *stmt, const char *colname); -size_t db_col_bytes(struct db_stmt *stmt, const char *colname); -int db_col_is_null(struct db_stmt *stmt, const char *colname); -const void* db_col_blob(struct db_stmt *stmt, const char *colname); -char *db_col_strdup(const tal_t *ctx, - struct db_stmt *stmt, - const char *colname); -void db_col_preimage(struct db_stmt *stmt, const char *colname, struct preimage *preimage); -void db_col_amount_msat(struct db_stmt *stmt, const char *colname, struct amount_msat *msat); -void db_col_amount_sat(struct db_stmt *stmt, const char *colname, struct amount_sat *sat); -struct json_escape *db_col_json_escape(const tal_t *ctx, struct db_stmt *stmt, const char *colname); -void db_col_sha256(struct db_stmt *stmt, const char *colname, struct sha256 *sha); -void db_col_sha256d(struct db_stmt *stmt, const char *colname, struct sha256_double *shad); -void db_col_secret(struct db_stmt *stmt, const char *colname, struct secret *s); -struct secret *db_col_secret_arr(const tal_t *ctx, struct db_stmt *stmt, - const char *colname); -void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t); -void db_col_channel_id(struct db_stmt *stmt, const char *colname, struct channel_id *dest); -void db_col_node_id(struct db_stmt *stmt, const char *colname, struct node_id *ni); -struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, - const char *colname); -void db_col_pubkey(struct db_stmt *stmt, const char *colname, - struct pubkey *p); -bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, - struct short_channel_id *dest); -struct short_channel_id * -db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); -bool db_col_signature(struct db_stmt *stmt, const char *colname, - secp256k1_ecdsa_signature *sig); -struct timeabs db_col_timeabs(struct db_stmt *stmt, const char *colname); -struct bitcoin_tx *db_col_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); -struct wally_psbt *db_col_psbt(const tal_t *ctx, struct db_stmt *stmt, const char *colname); -struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, const char *colname); - -struct onionreply *db_col_onionreply(const tal_t *ctx, - struct db_stmt *stmt, const char *colname); - -#define db_col_arr(ctx, stmt, colname, type) \ - ((type *)db_col_arr_((ctx), (stmt), (colname), \ - sizeof(type), TAL_LABEL(type, "[]"), \ - __func__)) -void *db_col_arr_(const tal_t *ctx, struct db_stmt *stmt, const char *colname, - size_t bytes, const char *label, const char *caller); - - -/* Some useful default variants */ -int db_col_int_or_default(struct db_stmt *stmt, const char *colname, int def); -void db_col_amount_msat_or_default(struct db_stmt *stmt, const char *colname, - struct amount_msat *msat, - struct amount_msat def); - - -/* Explicitly ignore a column (so we don't complain you didn't use it!) */ -void db_col_ignore(struct db_stmt *stmt, const char *colname); - -/** - * db_exec_prepared -- Execute a prepared statement - * - * After preparing a statement using `db_prepare`, and after binding all - * non-null variables using the `db_bind_*` functions, it can be executed with - * this function. It is a small, transaction-aware, wrapper around `db_step`, - * that calls fatal() if the execution fails. This may take ownership of - * `stmt` if annotated with `take()`and will free it before returning. - * - * If you'd like to issue a query and access the rows returned by the query - * please use `db_query_prepared` instead, since this function will not expose - * returned results, and the `stmt` can only be used for calls to - * `db_count_changes` and `db_last_insert_id` after executing. - * - * @stmt: The prepared statement to execute - */ -bool db_exec_prepared_v2(struct db_stmt *stmt TAKES); - -/** - * db_query_prepared -- Execute a prepared query - * - * After preparing a query using `db_prepare`, and after binding all non-null - * variables using the `db_bind_*` functions, it can be executed with this - * function. This function must be called before calling `db_step` or any of - * the `db_col_*` column access functions. - * - * If you are not executing a read-only statement, please use - * `db_exec_prepared` instead. - * - * @stmt: The prepared statement to execute - */ -bool db_query_prepared(struct db_stmt *stmt); -size_t db_count_changes(struct db_stmt *stmt); -u64 db_last_insert_id_v2(struct db_stmt *stmt); - -/** - * db_prepare -- Prepare a DB query/command - * - * Create an instance of `struct db_stmt` that encapsulates a SQL query or command. - * - * @query MUST be wrapped in a `SQL()` macro call, since that allows the - * extraction and translation of the query into the target SQL dialect. - * - * It does not execute the query and does not check its validity, but - * allocates the placeholders detected in the query. The placeholders in the - * `stmt` can then be bound using the `db_bind_*` functions, and executed - * using `db_exec_prepared` for write-only statements and `db_query_prepared` - * for read-only statements. - * - * @db: Database to query/exec - * @query: The SQL statement to compile - */ -struct db_stmt *db_prepare_v2_(const char *location, struct db *db, - const char *query_id); - -/* TODO(cdecker) Remove the v2 suffix after finishing the migration */ -#define db_prepare_v2(db,query) \ - db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) - -/** - * Access pending changes that have been added to the current transaction. - */ -const char **db_changes(struct db *db); - -/* Get the current data version. */ -u32 db_data_version_get(struct db *db); - #endif /* LIGHTNING_WALLET_DB_H */ diff --git a/wallet/db_queries_postgres.c b/wallet/db_queries_postgres.c new file mode 100644 index 000000000000..687f9e4d55ac --- /dev/null +++ b/wallet/db_queries_postgres.c @@ -0,0 +1,13 @@ +#include "config.h" +#include "db_postgres_sqlgen.c" + +#if HAVE_POSTGRES + +struct db_query_set postgres_query_set = { + .name = "postgres", + .query_table = db_postgres_queries, + .query_table_size = ARRAY_SIZE(db_postgres_queries), +}; + +AUTODATA(db_queries, &postgres_query_set); +#endif /* HAVE_POSTGRES */ diff --git a/wallet/db_queries_sqlite3.c b/wallet/db_queries_sqlite3.c new file mode 100644 index 000000000000..9411170fd6b2 --- /dev/null +++ b/wallet/db_queries_sqlite3.c @@ -0,0 +1,12 @@ +#include "config.h" +#include "db_sqlite3_sqlgen.c" + +#if HAVE_SQLITE3 +struct db_query_set sqlite3_query_set = { + .name = "sqlite3", + .query_table = db_sqlite3_queries, + .query_table_size = ARRAY_SIZE(db_sqlite3_queries), +}; + +AUTODATA(db_queries, &sqlite3_query_set); +#endif /* HAVE_SQLITE3 */ diff --git a/wallet/invoices.c b/wallet/invoices.c index 1d6e268ec0df..f8d46d8d6b99 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -1,7 +1,10 @@ #include "config.h" #include #include -#include +#include +#include +#include +#include #include #include diff --git a/wallet/test/Makefile b/wallet/test/Makefile index 46335b0636d8..b8406b16c9b8 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -27,7 +27,7 @@ WALLET_TEST_COMMON_OBJS := \ common/utils.o \ common/wireaddr.o \ common/version.o \ - wallet/db_sqlite3.o \ + wallet/db_queries_sqlite3.o \ wire/towire.o \ wire/fromwire.o diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 63765852626d..966a0a9c8142 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -6,6 +6,10 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const s } #define log_ db_log_ +#include "db/bindings.c" +#include "db/db_sqlite3.c" +#include "db/exec.c" +#include "db/utils.c" #include "wallet/db.c" #include "test_utils.h" @@ -86,6 +90,8 @@ static struct db *create_test_db(void) tal_free(filename); db = db_open(NULL, dsn); db->data_version = 0; + db->report_changes_fn = NULL; + tal_free(dsn); return db; } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index fa12dcf621d0..7da3e5e411ee 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -3,7 +3,7 @@ #include "test_utils.h" #include -#include +#include static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const struct node_id *node_id UNUSED, bool call_notifier UNUSED, const char *fmt UNUSED, ...) { @@ -35,6 +35,10 @@ void db_fatal(const char *fmt, ...) #include "lightningd/peer_htlcs.c" #include "lightningd/channel.c" +#include "db/bindings.c" +#include "db/db_sqlite3.c" +#include "db/exec.c" +#include "db/utils.c" #include "wallet/db.c" #include @@ -908,6 +912,7 @@ static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx dsn = tal_fmt(NULL, "sqlite3://%s", filename); w->db = db_open(w, dsn); + w->db->report_changes_fn = NULL; tal_free(dsn); tal_add_destructor2(w, cleanup_test_wallet, filename); diff --git a/wallet/wallet.c b/wallet/wallet.c index 3cbe424ecf59..16444bf981af 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -8,13 +8,16 @@ #include #include #include +#include +#include +#include +#include #include #include #include #include #include #include -#include #include #include #include diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 584dfa8158a0..1d32d89deb67 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include From 453ef7f0ce50398df3ebad650dcab8892badc110 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 31 Jan 2022 13:29:31 -0600 Subject: [PATCH 0382/1530] db: mark that column is unused/ignored 2022-01-25T23:41:45.2994844Z ----------------------------- Captured stderr call ----------------------------- 2022-01-25T23:41:45.2995230Z lightningd: Never accessed column 1 in query SELECT 1 FROM offers WHERE offer_id = ?; --- wallet/wallet.c | 1 + 1 file changed, 1 insertion(+) diff --git a/wallet/wallet.c b/wallet/wallet.c index 16444bf981af..fa44b869cafd 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4692,6 +4692,7 @@ bool wallet_offer_create(struct wallet *w, db_query_prepared(stmt); if (db_step(stmt)) { + db_col_ignore(stmt, "1"); tal_free(stmt); return false; } From ea36c3a938c6cc67971a127631d3003134f962dc Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 1 Mar 2022 12:24:18 -0600 Subject: [PATCH 0383/1530] db: collapse the db_queries files into the generated ones There's no reason to have these be independent of the generated files, all the data contained within them is 'formulaic' --- devtools/sql-rewrite.py | 7 +++++++ wallet/Makefile | 10 ++-------- wallet/db_queries_postgres.c | 13 ------------- wallet/db_queries_sqlite3.c | 12 ------------ wallet/test/Makefile | 2 +- 5 files changed, 10 insertions(+), 34 deletions(-) delete mode 100644 wallet/db_queries_postgres.c delete mode 100644 wallet/db_queries_sqlite3.c diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py index 1aa9c446906d..476cdc147d4d 100755 --- a/devtools/sql-rewrite.py +++ b/devtools/sql-rewrite.py @@ -157,6 +157,13 @@ def colname_htable(query): % endfor }; +struct db_query_set ${f}_query_set = { + .name = "${f}", + .query_table = db_${f}_queries, + .query_table_size = ARRAY_SIZE(db_${f}_queries), +}; + +AUTODATA(db_queries, &${f}_query_set); #endif /* HAVE_${f.upper()} */ #endif /* LIGHTNINGD_WALLET_GEN_DB_${f.upper()} */ diff --git a/wallet/Makefile b/wallet/Makefile index 91b10246be29..05ce68de4593 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -8,15 +8,13 @@ WALLET_LIB_SRC := \ wallet/walletrpc.c WALLET_LIB_SRC_NOHDR := \ - wallet/reservation.c \ - wallet/db_queries_postgres.c \ - wallet/db_queries_sqlite3.c + wallet/reservation.c WALLET_DB_QUERIES := \ wallet/db_sqlite3_sqlgen.c \ wallet/db_postgres_sqlgen.c -WALLET_SRC := $(WALLET_LIB_SRC) $(WALLET_LIB_SRC_NOHDR) +WALLET_SRC := $(WALLET_LIB_SRC) $(WALLET_LIB_SRC_NOHDR) $(WALLET_DB_QUERIES) WALLET_HDRS := $(WALLET_LIB_SRC:.c=.h) WALLET_OBJS := $(WALLET_SRC:.c=.o) @@ -25,10 +23,6 @@ WALLET_OBJS := $(WALLET_SRC:.c=.o) ALL_C_SOURCES += $(WALLET_SRC) $(WALLET_DB_QUERIES) ALL_C_HEADERS += $(WALLET_HDRS) -# Query sets depend on the rewritten queries -wallet/db_queries_sqlite3.o: $(WALLET_DB_QUERIES) -wallet/db_queries_postgres.o: $(WALLET_DB_QUERIES) - # The following files contain SQL-annotated statements that we need to extact WALLET_SQL_FILES := \ $(DB_SQL_FILES) \ diff --git a/wallet/db_queries_postgres.c b/wallet/db_queries_postgres.c deleted file mode 100644 index 687f9e4d55ac..000000000000 --- a/wallet/db_queries_postgres.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "config.h" -#include "db_postgres_sqlgen.c" - -#if HAVE_POSTGRES - -struct db_query_set postgres_query_set = { - .name = "postgres", - .query_table = db_postgres_queries, - .query_table_size = ARRAY_SIZE(db_postgres_queries), -}; - -AUTODATA(db_queries, &postgres_query_set); -#endif /* HAVE_POSTGRES */ diff --git a/wallet/db_queries_sqlite3.c b/wallet/db_queries_sqlite3.c deleted file mode 100644 index 9411170fd6b2..000000000000 --- a/wallet/db_queries_sqlite3.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "config.h" -#include "db_sqlite3_sqlgen.c" - -#if HAVE_SQLITE3 -struct db_query_set sqlite3_query_set = { - .name = "sqlite3", - .query_table = db_sqlite3_queries, - .query_table_size = ARRAY_SIZE(db_sqlite3_queries), -}; - -AUTODATA(db_queries, &sqlite3_query_set); -#endif /* HAVE_SQLITE3 */ diff --git a/wallet/test/Makefile b/wallet/test/Makefile index b8406b16c9b8..278287cfb346 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -27,7 +27,7 @@ WALLET_TEST_COMMON_OBJS := \ common/utils.o \ common/wireaddr.o \ common/version.o \ - wallet/db_queries_sqlite3.o \ + wallet/db_sqlite3_sqlgen.o \ wire/towire.o \ wire/fromwire.o From b30328a91f6b86f463c56f38566a23278fcfa720 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 8 Feb 2022 14:39:50 -0600 Subject: [PATCH 0384/1530] coin_mvt: rm unncessary if statement We don't need to switch on this; if it's zero the below ops will be effectiely no-ops. --- lightningd/channel_control.c | 37 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index d36baacd8749..0d70aa0393bf 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -138,26 +138,23 @@ void channel_record_open(struct channel *channel) blockheight = short_channel_id_blocknum(channel->scid); /* If funds were pushed, add/sub them from the starting balance */ - if (is_pushed) { - if (channel->opener == LOCAL) { - if (!amount_msat_add(&start_balance, - channel->our_msat, channel->push)) - fatal("Unable to add push_msat (%s) + our_msat (%s)", - type_to_string(tmpctx, struct amount_msat, - &channel->push), - type_to_string(tmpctx, struct amount_msat, - &channel->our_msat)); - } else { - if (!amount_msat_sub(&start_balance, - channel->our_msat, channel->push)) - fatal("Unable to sub our_msat (%s) - push (%s)", - type_to_string(tmpctx, struct amount_msat, - &channel->our_msat), - type_to_string(tmpctx, struct amount_msat, - &channel->push)); - } - } else - start_balance = channel->our_msat; + if (channel->opener == LOCAL) { + if (!amount_msat_add(&start_balance, + channel->our_msat, channel->push)) + fatal("Unable to add push_msat (%s) + our_msat (%s)", + type_to_string(tmpctx, struct amount_msat, + &channel->push), + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat)); + } else { + if (!amount_msat_sub(&start_balance, + channel->our_msat, channel->push)) + fatal("Unable to sub our_msat (%s) - push (%s)", + type_to_string(tmpctx, struct amount_msat, + &channel->our_msat), + type_to_string(tmpctx, struct amount_msat, + &channel->push)); + } mvt = new_coin_channel_open(tmpctx, &channel->cid, From 12cbf614cd5e967c86ac71172d5ee7bedfb34316 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 8 Feb 2022 14:40:55 -0600 Subject: [PATCH 0385/1530] coin_mvt: mark every event that already has a destination Every event that's coming out of here that specifies a different place to be deposited should be marked as originating from this channel's account. --- lightningd/onchain_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index fa8c2139c813..14784aa2b27f 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -266,7 +266,7 @@ static void handle_onchain_log_coin_move(struct channel *channel, const u8 *msg) if (!mvt->account_name) mvt->account_name = type_to_string(mvt, struct channel_id, &channel->cid); - else if (chain_mvt_is_external(mvt)) + else mvt->originating_acct = type_to_string(mvt, struct channel_id, &channel->cid); notify_chain_mvt(channel->peer->ld, mvt); From 24a1c91045f5a3af9202d7ce0d756a0a421b7ec4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 16 Feb 2022 12:16:39 -0600 Subject: [PATCH 0386/1530] coin_mvt: report the number of outputs on a channel close tx The bookkeeper needs to know how many outputs to expect before we can consider a channel resolved onchain. --- common/coin_mvt.c | 21 +++++++++++++++------ common/coin_mvt.h | 9 ++++++++- doc/PLUGINS.md | 4 ++++ lightningd/notification.c | 4 ++++ onchaind/onchaind.c | 3 ++- onchaind/test/run-grind_feerate-bug.c | 3 ++- onchaind/test/run-grind_feerate.c | 3 ++- 7 files changed, 37 insertions(+), 10 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 1d77e9b9b02a..c0ca08777d4f 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -91,7 +91,8 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, enum mvt_tag *tags TAKES, struct amount_msat amount, bool is_credit, - struct amount_sat output_val) + struct amount_sat output_val, + u32 out_count) { struct chain_coin_mvt *mvt = tal(ctx, struct chain_coin_mvt); @@ -118,7 +119,9 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, mvt->debit = amount; mvt->credit = AMOUNT_MSAT(0); } + mvt->output_val = output_val; + mvt->output_count = out_count; return mvt; } @@ -143,7 +146,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx, blockheight, tags, amt_msat, is_credit, /* All amounts that are sat are * on-chain output values */ - amt_sat); + amt_sat, 0); } struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx, @@ -178,13 +181,15 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, const struct bitcoin_outpoint *out, u32 blockheight, const struct amount_msat amount, - const struct amount_sat output_val) + const struct amount_sat output_val, + u32 output_count) { return new_chain_coin_mvt(ctx, NULL, txid, out, NULL, blockheight, take(new_tag_arr(NULL, CHANNEL_CLOSE)), amount, false, - output_val); + output_val, + output_count); } struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, @@ -200,7 +205,7 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, mvt = new_chain_coin_mvt(ctx, NULL, NULL, out, NULL, blockheight, take(new_tag_arr(NULL, CHANNEL_OPEN)), amount, - true, output_val); + true, output_val, 0); mvt->account_name = type_to_string(mvt, struct channel_id, chan_id); /* If we're the opener, add to the tag list */ @@ -252,7 +257,7 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, return new_chain_coin_mvt(ctx, EXTERNAL, txid, outpoint, NULL, blockheight, take(new_tag_arr(NULL, tag)), - AMOUNT_MSAT(0), true, amount); + AMOUNT_MSAT(0), true, amount, 0); } struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, @@ -334,6 +339,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, mvt->output_val = tal(mvt, struct amount_sat); *mvt->output_val = chain_mvt->output_val; + mvt->output_count = chain_mvt->output_count; mvt->fees = NULL; mvt->timestamp = timestamp; @@ -366,6 +372,7 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->credit = chan_mvt->credit; mvt->debit = chan_mvt->debit; mvt->output_val = NULL; + mvt->output_count = 0; mvt->fees = tal(mvt, struct amount_msat); *mvt->fees = chan_mvt->fees; mvt->timestamp = timestamp; @@ -413,6 +420,7 @@ void towire_chain_coin_mvt(u8 **pptr, const struct chain_coin_mvt *mvt) towire_amount_msat(pptr, mvt->credit); towire_amount_msat(pptr, mvt->debit); towire_amount_sat(pptr, mvt->output_val); + towire_u32(pptr, mvt->output_count); } void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_mvt *mvt) @@ -455,4 +463,5 @@ void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_m mvt->credit = fromwire_amount_msat(cursor, max); mvt->debit = fromwire_amount_msat(cursor, max); mvt->output_val = fromwire_amount_sat(cursor, max); + mvt->output_count = fromwire_u32(cursor, max); } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 350945c00e2d..1f0aa9a581d0 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -87,6 +87,10 @@ struct chain_coin_mvt { /* When we pay to external accounts, it's useful * to track which internal account it originated from */ const char *originating_acct; + + /* Number of outputs in spending tx; used by the + * `channel_close` event */ + u32 output_count; }; /* differs depending on type!? */ @@ -123,6 +127,8 @@ struct coin_mvt { /* Value of the output. May be different than * our credit/debit amount, eg channel opens */ struct amount_sat *output_val; + /* Really only needed for channel closes */ + size_t output_count; /* Amount of fees collected/paid by channel mvt */ struct amount_msat *fees; @@ -170,7 +176,8 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, const struct bitcoin_outpoint *out, u32 blockheight, const struct amount_msat amount, - const struct amount_sat output_val) + const struct amount_sat output_val, + u32 output_count) NON_NULL_ARGS(2, 3); struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index e8196ec726ea..cc6d1ac0f57a 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -710,6 +710,7 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "credit":"2000000000msat", "debit":"0msat", "output_value": "2000000000msat", // ('chain_mvt' only) + "output_count": 2, // ('chain_mvt' only, typically only channel closes) "fees": "382msat", // ('channel_mvt' only) "tags": ["deposit"], "blockheight":102, // (May be null) @@ -757,6 +758,9 @@ multiple times. `channel_mvt` only channel opens/closes the total output value will not necessarily correspond to the amount that's credited/debited. +`output_count` is the total outputs to expect for a channel close. Useful +for figuring out when every onchain output for a close has been resolved. + `fees` is an HTLC annotation for the amount of fees either paid or earned. For "invoice" tagged events, the fees are the total fees paid to send that payment. The end amount can be found by subtracting diff --git a/lightningd/notification.c b/lightningd/notification.c index 13f9154b9779..c77fa24c411d 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -480,6 +480,10 @@ static void coin_movement_notification_serialize(struct json_stream *stream, if (mvt->output_val) json_add_amount_sat_only(stream, "output_value", *mvt->output_val); + if (mvt->output_count > 0) + json_add_num(stream, "output_count", + mvt->output_count); + if (mvt->fees) json_add_amount_msat_only(stream, "fees", *mvt->fees); diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index ae8eb2a5b127..1a7ee749956a 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -3860,7 +3860,8 @@ int main(int argc, char *argv[]) send_coin_mvt(take(new_coin_channel_close(NULL, &tx->txid, &funding, tx_blockheight, our_msat, - funding_sats))); + funding_sats, + tal_count(tx->outputs)))); status_debug("Remote per-commit point: %s", type_to_string(tmpctx, struct pubkey, diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index e14a2202b207..0d17accd7d62 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -119,7 +119,8 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, const struct amount_msat amount UNNEEDED, - const struct amount_sat output_val) + const struct amount_sat output_val UNNEEDED, + u32 output_count) { fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } /* Generated stub for new_coin_external_deposit */ diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 23454c0aac00..9e3be09040fb 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -142,7 +142,8 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *out UNNEEDED, u32 blockheight UNNEEDED, const struct amount_msat amount UNNEEDED, - const struct amount_sat output_val) + const struct amount_sat output_val UNNEEDED, + u32 output_count) { fprintf(stderr, "new_coin_channel_close called!\n"); abort(); } /* Generated stub for new_coin_external_deposit */ From 7a00277b43189c065406a6d2556ee7dcc30577b4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 16 Feb 2022 12:17:53 -0600 Subject: [PATCH 0387/1530] coin_mvt: mark coins destined for wallet w/ originating acct Only shows up on delayed to us outputs, but nice to have anyway. It's missing for channel index destined deposits, maybe nice to add at some point in the future; right now you can figure out which close a wallet deposit comes from via the channel close txid --- lightningd/onchain_control.c | 4 +++- tests/test_closing.py | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 14784aa2b27f..f980565c64b3 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -470,7 +470,9 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg) csv_lock); mvt = new_coin_wallet_deposit(msg, &outpoint, blockheight, - amount, CHANNEL_CLOSE); + amount, DEPOSIT); + mvt->originating_acct = type_to_string(mvt, struct channel_id, + &channel->cid); notify_chain_mvt(channel->peer->ld, mvt); } diff --git a/tests/test_closing.py b/tests/test_closing.py index 99da6709dc78..2975701acb13 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -711,7 +711,7 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): # l2 sweeps all of l1's closing outputs expected_2 = { 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], - 'B': [('wallet', ['channel_close'], None, None), ('cid1', ['penalty'], ['to_wallet'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'D')], + 'B': [('wallet', ['deposit'], None, None), ('cid1', ['penalty'], ['to_wallet'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'D')], 'C': [('wallet', ['deposit'], None, None)], 'D': [('wallet', ['deposit'], None, None)] } @@ -1268,7 +1268,7 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): expected_3 = { 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], - 'B': [('wallet', ['channel_close'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'E')], + 'B': [('wallet', ['deposit'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'E')], 'C': [('cid1', ['penalty'], ['to_wallet'], 'D')], 'D': [('wallet', ['deposit'], None, None)], 'E': [('wallet', ['deposit'], None, None)] @@ -1488,7 +1488,7 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): expected_3 = { 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], - 'B': [('wallet', ['channel_close'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'E'), ('external', ['stolen'], None, None), ('external', ['htlc_timeout'], ['htlc_timeout'], 'C')], + 'B': [('wallet', ['deposit'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'E'), ('external', ['stolen'], None, None), ('external', ['htlc_timeout'], ['htlc_timeout'], 'C')], 'C': [('cid1', ['penalty'], ['to_wallet'], 'D')], 'D': [('wallet', ['deposit'], None, None)], 'E': [('external', ['stolen'], None, None)] @@ -2185,7 +2185,7 @@ def try_pay(): expected_1 = { 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], - 'B': [('external', ['to_them'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'D'), ('wallet', ['channel_close'], None, None)] + 'B': [('external', ['to_them'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'D'), ('wallet', ['deposit'], None, None)] } if anchor_expected(): @@ -2297,7 +2297,7 @@ def try_pay(): # in the next channel open 'A': [('wallet', ['deposit'], ((['withdrawal'], 'D'), (None, None))), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], '1': [('wallet', ['deposit'], ['withdrawal'], 'D')], - 'B': [('external', ['to_them'], None, None), ('wallet', ['channel_close'], None, None), ('cid1', ['htlc_fulfill'], ['to_wallet'], 'C')], + 'B': [('external', ['to_them'], None, None), ('wallet', ['deposit'], None, None), ('cid1', ['htlc_fulfill'], ['to_wallet'], 'C')], 'C': [('wallet', ['deposit'], None, None)], 'D': [('wallet', ['deposit'], None, None), ('cid2', ['channel_open', 'opener'], None, None)] } @@ -2389,7 +2389,7 @@ def try_pay(): # This is ugly, but this wallet deposit is either unspent or used # in the next channel open 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], - 'B': [('wallet', ['channel_close'], None, None), ('cid1', ['htlc_timeout'], ['to_wallet'], 'C')], + 'B': [('wallet', ['deposit'], None, None), ('cid1', ['htlc_timeout'], ['to_wallet'], 'C')], 'C': [('wallet', ['deposit'], None, None)], } @@ -2592,7 +2592,7 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): expected_1 = { '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], - 'B': [('wallet', ['channel_close'], None, None), ('cid1', ['htlc_timeout'], ['ignored'], 'C')], + 'B': [('wallet', ['deposit'], None, None), ('cid1', ['htlc_timeout'], ['ignored'], 'C')], 'C': [('wallet', ['deposit'], None, None)], } From d246e9c1a2ec5a3c7ce6bcdd36742b3b4f0758f9 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 3 Mar 2022 16:54:01 -0600 Subject: [PATCH 0388/1530] tx_parts: as a convenience, copy over the value as the 'satoshi' Reporting coin movements was crashing for liquid-regtest tests because we were using an un-initialized field (the tx_part output's satoshi field). We fill this in 'as a convenience' for other wally_tx_outputs that are on liquid elsewhere, here we do the same for tx_parts sent over the wire to onchaind. --- bitcoin/tx_parts.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/bitcoin/tx_parts.c b/bitcoin/tx_parts.c index e74c3f393b7f..e61a388be163 100644 --- a/bitcoin/tx_parts.c +++ b/bitcoin/tx_parts.c @@ -94,6 +94,19 @@ struct tx_parts *tx_parts_from_wally_tx(const tal_t *ctx, if (output != -1 && output != i) continue; txp->outputs[i] = clone_output(&wtx->outputs[i]); + + /* Cheat a bit by also setting the numeric satoshi + * value, otherwise we end up converting a + * number of times */ + if (chainparams->is_elements) { + struct amount_asset asset; + struct amount_sat sats; + asset = wally_tx_output_get_amount(txp->outputs[i]); + /* FIXME: non l-btc assets */ + assert(amount_asset_is_main(&asset)); + sats = amount_asset_to_sat(&asset); + txp->outputs[i]->satoshi = sats.satoshis; /* Raw: wally conversion */ + } } tal_wally_end(txp); @@ -281,6 +294,9 @@ static struct wally_tx_output *fromwire_wally_tx_output(const tal_t *ctx, surjectionproof, tal_bytelen(surjectionproof), rangeproof, tal_bytelen(rangeproof), &out); + + /* As a convenience, we sent the value over as satoshis */ + out->satoshi = fromwire_u64(cursor, max); } else { u64 satoshi; satoshi = fromwire_u64(cursor, max); @@ -348,6 +364,8 @@ static void towire_wally_tx_output(u8 **pptr, const struct wally_tx_output *out) out->surjectionproof_len); towire_u32(pptr, out->rangeproof_len); towire_u8_array(pptr, out->rangeproof, out->rangeproof_len); + /* Copy the value over, as a convenience */ + towire_u64(pptr, out->satoshi); } else { towire_u64(pptr, out->satoshi); } From ecb19ba6f2cfe06efe4934a2b26722959838db9c Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 16 Feb 2022 15:16:07 -0600 Subject: [PATCH 0389/1530] coin_mvt: report mutual close outputs also It's better to report every single utxo on close so we know when to mark a channel account as definitively closed. --- onchaind/onchaind.c | 31 +++++++++++++++++++++++++++++-- tests/test_closing.py | 4 ++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 1a7ee749956a..d210590ada1a 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -247,6 +247,31 @@ static void record_external_deposit(const struct tracked_output *out, record_external_output(&out->outpoint, out->sat, blockheight, tag); } +static void record_mutual_close(const struct tx_parts *tx, + const u8 *remote_scriptpubkey, + u32 blockheight) +{ + /* FIXME: if we ever change how closes happen, this will + * need to be updated as there's no longer 1 output + * per peer */ + for (size_t i = 0; i < tal_count(tx->outputs); i++) { + struct bitcoin_outpoint out; + + if (!wally_tx_output_scripteq(tx->outputs[i], + remote_scriptpubkey)) + continue; + + out.n = i; + out.txid = tx->txid; + record_external_output(&out, + amount_sat(tx->outputs[i]->satoshi), + blockheight, + TO_THEM); + break; + } +} + + static void record_channel_deposit(struct tracked_output *out, u32 blockheight, enum mvt_tag tag) { @@ -3882,9 +3907,11 @@ int main(int argc, char *argv[]) * without any pending payments) and publish it on the blockchain (see * [BOLT #2: Channel Close](02-peer-protocol.md#channel-close)). */ - if (is_mutual_close(tx, scriptpubkey[LOCAL], scriptpubkey[REMOTE])) + if (is_mutual_close(tx, scriptpubkey[LOCAL], scriptpubkey[REMOTE])) { + record_mutual_close(tx, scriptpubkey[REMOTE], + tx_blockheight); handle_mutual_close(outs, tx); - else { + } else { /* BOLT #5: * * 2. The bad way (*unilateral close*): something goes wrong, diff --git a/tests/test_closing.py b/tests/test_closing.py index 2975701acb13..705cac755c57 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -108,12 +108,12 @@ def test_closing_simple(node_factory, bitcoind, chainparams): expected_1 = { '0': [('wallet', ['deposit'], ['withdrawal'], 'A')], 'A': [('wallet', ['deposit'], None, None), ('cid1', ['channel_open', 'opener'], ['channel_close'], 'B')], - 'B': [('wallet', ['deposit'], None, None)], + 'B': [('wallet', ['deposit'], None, None), ('external', ['to_them'], None, None)], } expected_2 = { 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], - 'B': [('wallet', ['deposit'], None, None)], + 'B': [('wallet', ['deposit'], None, None), ('external', ['to_them'], None, None)], } tags = check_utxos_channel(l1, [channel_id], expected_1) check_utxos_channel(l2, [channel_id], expected_2, tags) From 590f12145b058a3224ed1fee1be70d767dde4217 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 8 Feb 2022 14:43:51 -0600 Subject: [PATCH 0390/1530] listpeers: include the `pushed_msat` amount Useful for accounting for missed/historical channel opens, to figure out what the actual sat contribution from each peer is at a utxo level Changelog-Added: JSONRPC: `listpeers` now includes a `pushed_msat` value. For leased channels, is the total lease_fee. --- doc/lightning-listpeers.7.md | 3 ++- doc/schemas/listpeers.schema.json | 7 ++++++- lightningd/peer_control.c | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 8e4862b1e32f..b90d112e7dd3 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -73,6 +73,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **funding** (object, optional): - **local_msat** (msat): Amount of channel we funded - **remote_msat** (msat): Amount of channel they funded + - **pushed_msat** (msat): Amount pushed from opener to peer - **to_us_msat** (msat, optional): how much of channel is owed to us - **min_to_us_msat** (msat, optional): least amount owed to us ever - **max_to_us_msat** (msat, optional): most amount owed to us ever @@ -377,4 +378,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:d45de73a968bcc3e7fb699f0acd7a27a4c70d9e9fded8af8c684a71fe012f1ce) +[comment]: # ( SHA256STAMP:001e3cf495571bb09fe29f74adde8a6e40e69ddb1169934924eaf901a1e5f3c0) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index 74a7f782e2bc..e25ef39acc48 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -342,7 +342,8 @@ "additionalProperties": false, "required": [ "local_msat", - "remote_msat" + "remote_msat", + "pushed_msat" ], "properties": { "local_msat": { @@ -352,6 +353,10 @@ "remote_msat": { "type": "msat", "description": "Amount of channel they funded" + }, + "pushed_msat": { + "type": "msat", + "description": "Amount pushed from opener to peer" } } }, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 212208467166..20d4c238efe2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -791,6 +791,7 @@ static void json_add_channel(struct lightningd *ld, json_object_start(response, "funding"); json_add_sat_only(response, "local_msat", channel->our_funds); json_add_sat_only(response, "remote_msat", peer_funded_sats); + json_add_amount_msat_only(response, "pushed_msat", channel->push); json_object_end(response); if (!amount_sat_to_msat(&funding_msat, channel->funding_sats)) { From 7add7ca199e19a486884f3fd6d80c562586e1749 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 16 Feb 2022 15:36:47 -0600 Subject: [PATCH 0391/1530] json: reverse parse a coin_mvt tag back into an enum --- common/coin_mvt.h | 1 + common/json_helpers.c | 16 ++++++++++++++++ common/json_helpers.h | 5 +++++ common/test/Makefile | 5 +++-- devtools/Makefile | 1 + gossipd/test/Makefile | 1 + plugins/Makefile | 1 + 7 files changed, 28 insertions(+), 2 deletions(-) diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 1f0aa9a581d0..0995c9edaff0 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -14,6 +14,7 @@ enum mvt_type { CHANNEL_MVT = 1, }; +#define NUM_MVT_TAGS (LEASED + 1) enum mvt_tag { DEPOSIT = 0, WITHDRAWAL = 1, diff --git a/common/json_helpers.c b/common/json_helpers.c index 8d9cb35224c4..af06c2036a2e 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -108,6 +108,22 @@ bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, cid, sizeof(*cid)); } + +bool json_to_coin_mvt_tag(const char *buffer, const jsmntok_t *tok, + enum mvt_tag *tag) +{ + enum mvt_tag i_tag; + for (size_t i = 0; i < NUM_MVT_TAGS; i++) { + i_tag = (enum mvt_tag) i; + if (json_tok_streq(buffer, tok, mvt_tag_str(i_tag))) { + *tag = i_tag; + return true; + } + } + + return false; +} + bool split_tok(const char *buffer, const jsmntok_t *tok, char split, jsmntok_t *a, diff --git a/common/json_helpers.h b/common/json_helpers.h index e37cb95e835e..1f5fe7fe3239 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -3,6 +3,7 @@ #define LIGHTNING_COMMON_JSON_HELPERS_H #include "config.h" #include +#include #include #include @@ -73,6 +74,10 @@ bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, struct channel_id *cid); +/* Extract a coin movement 'tag' from this */ +bool json_to_coin_mvt_tag(const char *buffer, const jsmntok_t *tok, + enum mvt_tag *tag); + /* Split a json token into 2 tokens given a splitting character */ bool split_tok(const char *buffer, const jsmntok_t *tok, char split, diff --git a/common/test/Makefile b/common/test/Makefile index c3959b52af9d..ae2803cf5efa 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -21,8 +21,8 @@ ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) # Sphinx test wants to decode TLVs. common/test/run-sphinx: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o -common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o -common/test/run-route_blinding_override_test: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o +common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o common/coin_mvt.o +common/test/run-route_blinding_override_test: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o common/coin_mvt.o common/test/run-param \ common/test/run-json: \ @@ -30,6 +30,7 @@ common/test/run-json: \ common/base32.o \ common/bigsize.o \ common/channel_id.o \ + common/coin_mvt.o \ common/json.o \ common/json_stream.o \ common/lease_rates.o \ diff --git a/devtools/Makefile b/devtools/Makefile index 5498db4684ce..90dd7c7b45a5 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -14,6 +14,7 @@ ALL_PROGRAMS += $(DEVTOOLS) DEVTOOLS_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ + common/coin_mvt.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index 6c4f150b894f..e91b5c2ce322 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -9,6 +9,7 @@ GOSSIPD_TEST_PROGRAMS := $(GOSSIPD_TEST_OBJS:.o=) GOSSIPD_TEST_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ + common/coin_mvt.o \ common/bigsize.o \ common/blindedpath.o \ common/channel_id.o \ diff --git a/plugins/Makefile b/plugins/Makefile index 3562243cd8c4..3707e7688c69 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -108,6 +108,7 @@ PLUGIN_COMMON_OBJS := \ bitcoin/varint.o \ common/amount.o \ common/autodata.o \ + common/coin_mvt.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ From b33868f7c2c134887a65e70c324a7a68bf0b8782 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 16 Feb 2022 15:36:20 -0600 Subject: [PATCH 0392/1530] coin_mvt: only chain moves have a blockheight channel moves don't have blockheights --- common/coin_mvt.c | 2 -- common/coin_mvt.h | 3 +-- doc/PLUGINS.md | 2 +- lightningd/notification.c | 11 +++-------- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index c0ca08777d4f..12d758f4d846 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -376,8 +376,6 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->fees = tal(mvt, struct amount_msat); *mvt->fees = chan_mvt->fees; mvt->timestamp = timestamp; - /* channel movements don't have a blockheight */ - mvt->blockheight = 0; mvt->version = COIN_MVT_VERSION; mvt->node_id = tal_dup(mvt, struct node_id, node_id); diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 0995c9edaff0..c34f03792d7a 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -74,8 +74,7 @@ struct chain_coin_mvt { /* label / tag array */ enum mvt_tag *tags; - /* block this transaction is confirmed in - * zero means it's unknown/unconfirmed */ + /* block this transaction is confirmed in */ u32 blockheight; /* only one or the other */ diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index cc6d1ac0f57a..487a653498c4 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -713,7 +713,7 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "output_count": 2, // ('chain_mvt' only, typically only channel closes) "fees": "382msat", // ('channel_mvt' only) "tags": ["deposit"], - "blockheight":102, // (May be null) + "blockheight":102, // 'chain_mvt' only "timestamp":1585948198, "coin_type":"bc" } diff --git a/lightningd/notification.c b/lightningd/notification.c index c77fa24c411d..983e9fc746ff 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -493,14 +493,9 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_add_string(stream, NULL, mvt_tag_str(mvt->tags[i])); json_array_end(stream); - /* Only chain movements have blockheights. A blockheight - * of 'zero' means we haven't seen this tx confirmed yet. */ - if (mvt->type == CHAIN_MVT) { - if (mvt->blockheight) - json_add_u32(stream, "blockheight", mvt->blockheight); - else - json_add_null(stream, "blockheight"); - } + if (mvt->type == CHAIN_MVT) + json_add_u32(stream, "blockheight", mvt->blockheight); + json_add_u32(stream, "timestamp", mvt->timestamp); json_add_string(stream, "coin_type", mvt->hrp_name); From 9075c74e3d4f40cc6903a45b829e76a50cd8ac5b Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 22 Feb 2022 14:00:00 -0600 Subject: [PATCH 0393/1530] balance_snapshot: don't count unconfirmed utxos This was causing journal_entries to show up in the accountant plugin, since we don't emit events for unconfirmed events until they're actually confirmed onchain. --- lightningd/coin_mvts.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 098d2ee82bae..4ba2d9e58339 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -107,6 +107,9 @@ void send_account_balance_snapshot(struct lightningd *ld, u32 blockheight) for (size_t i = 0; i < ARRAY_SIZE(utxo_states); i++) { utxos = wallet_get_utxos(NULL, ld->wallet, utxo_states[i]); for (size_t j = 0; j < tal_count(utxos); j++) { + /* Don't count unconfirmed utxos! */ + if (!utxos[j]->spendheight && !utxos[j]->blockheight) + continue; if (!amount_msat_add_sat(&bal->balance, bal->balance, utxos[j]->amount)) fatal("Overflow adding node balance"); From f1ed373c97fdc61d2d68b33507091f630b2da187 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Mar 2022 20:54:11 +1030 Subject: [PATCH 0394/1530] connectd: be more graceful when we an address is in use. Aditya had this issue due to a config line, and the result was hard to diagnose even for me. It's now: ``` $ ./lightningd/lightningd --network=regtest --addr=:18444 2022-02-26T05:01:28.705Z **BROKEN** connectd: Failed to bind socket for 0.0.0.0:18444: Address already in use ``` Whereas before it doesn't even give the address it's trying to bind: ``` rusty@rusty-XPS-13-9370:~/devel/cvs/lightning (master)$ ./lightningd/lightningd --network=regtest --addr=:18444 lightning_connectd: Failed to bind on 2 socket: Address already in use (version v0.10.2-331-g86b83e4) 0x558a8b8d9a12 send_backtrace common/daemon.c:33 0x558a8b8e91e1 status_failed common/status.c:221 0x558a8b8c8e4f make_listen_fd connectd/connectd.c:1090 0x558a8b8c8f55 handle_wireaddr_listen connectd/connectd.c:1129 0x558a8b8c993d setup_listeners connectd/connectd.c:1312 0x558a8b8ca344 connect_init connectd/connectd.c:1517 0x558a8b8cbb57 recv_req connectd/connectd.c:1896 0x558a8b8d9f9f handle_read common/daemon_conn.c:31 0x558a8b9247c1 next_plan ccan/ccan/io/io.c:59 0x558a8b9253c9 do_plan ccan/ccan/io/io.c:407 0x558a8b92540b io_ready ccan/ccan/io/io.c:417 0x558a8b9276fe io_loop ccan/ccan/io/poll.c:453 0x558a8b8cbf36 main connectd/connectd.c:2033 0x7fe4d02940b2 ??? ???:0 0x558a8b8c285d ??? ???:0 0xffffffffffffffff ??? ???:0 2022-02-26T05:02:27.547Z **BROKEN** connectd: Failed to bind on 2 socket: Address already in use (version v0.10.2-331-g86b83e4) 2022-02-26T05:02:27.547Z **BROKEN** connectd: backtrace: common/daemon.c:38 (send_backtrace) 0x558a8b8d9a68 2022-02-26T05:02:27.547Z **BROKEN** connectd: backtrace: common/status.c:221 (status_failed) 0x558a8b8e91e1 2022-02-26T05:02:27.547Z **BROKEN** connectd: backtrace: connectd/connectd.c:1090 (make_listen_fd) 0x558a8b8c8e4f 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: connectd/connectd.c:1129 (handle_wireaddr_listen) 0x558a8b8c8f55 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: connectd/connectd.c:1312 (setup_listeners) 0x558a8b8c993d 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: connectd/connectd.c:1517 (connect_init) 0x558a8b8ca344 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: connectd/connectd.c:1896 (recv_req) 0x558a8b8cbb57 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: common/daemon_conn.c:31 (handle_read) 0x558a8b8d9f9f 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: ccan/ccan/io/io.c:59 (next_plan) 0x558a8b9247c1 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: ccan/ccan/io/io.c:407 (do_plan) 0x558a8b9253c9 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: ccan/ccan/io/io.c:417 (io_ready) 0x558a8b92540b 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: ccan/ccan/io/poll.c:453 (io_loop) 0x558a8b9276fe 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: connectd/connectd.c:2033 (main) 0x558a8b8cbf36 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: (null):0 ((null)) 0x7fe4d02940b2 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: (null):0 ((null)) 0x558a8b8c285d 2022-02-26T05:02:27.548Z **BROKEN** connectd: backtrace: (null):0 ((null)) 0xffffffffffffffff 2022-02-26T05:02:27.548Z **BROKEN** connectd: STATUS_FAIL_INTERNAL_ERROR: Failed to bind on 2 socket: Address already in use lightningd: connectd failed (exit status 242), exiting. ``` Signed-off-by: Rusty Russell --- connectd/connectd.c | 104 ++++++++++++++++++++++------------- connectd/connectd_wire.csv | 1 + lightningd/connect_control.c | 14 ++++- 3 files changed, 79 insertions(+), 40 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 090cfd4324be..dd695b9ef6e3 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1063,38 +1063,47 @@ static void add_listen_fd(struct daemon *daemon, int fd, bool mayfail, * I generally avoid "return -1 on error", but for file-descriptors it's the * UNIX standard, so it's not as offensive here as it would be in other * contexts. + * + * Note that it's generally an antipattern to have a function which + * returns an allocated object (here, errstr) without an explicit tal ctx so the + * caller is aware. */ -static int make_listen_fd(int domain, void *addr, socklen_t len, bool mayfail) +static int make_listen_fd(const tal_t *ctx, + struct daemon *daemon, + const struct wireaddr_internal *wi, + int domain, void *addr, socklen_t len, + char **errstr) { int fd = socket(domain, SOCK_STREAM, 0); int on = 1; if (fd < 0) { - if (!mayfail) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed to create %u socket: %s", - domain, strerror(errno)); + if (errstr) + *errstr = tal_fmt(ctx, "Failed to create socket for %s: %s", + type_to_string(tmpctx, struct wireaddr_internal, wi), + strerror(errno)); status_debug("Failed to create %u socket: %s", domain, strerror(errno)); return -1; } - /* Re-use, please.. */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) status_unusual("Failed setting socket reuse: %s", strerror(errno)); if (bind(fd, addr, len) != 0) { - if (!mayfail) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed to bind on %u socket: %s", - domain, strerror(errno)); + if (errstr) + *errstr = tal_fmt(ctx, "Failed to bind socket for %s: %s", + type_to_string(tmpctx, struct wireaddr_internal, wi), + strerror(errno)); status_debug("Failed to create %u socket: %s", domain, strerror(errno)); goto fail; } + if (errstr) + *errstr = NULL; return fd; fail: @@ -1104,15 +1113,18 @@ static int make_listen_fd(int domain, void *addr, socklen_t len, bool mayfail) return -1; } -/* Return true if it created socket successfully. */ -static bool handle_wireaddr_listen(struct daemon *daemon, - const struct wireaddr *wireaddr, - bool mayfail, - bool websocket) +/* Return true if it created socket successfully. If errstr is non-NULL, + * allocate off ctx if return false, otherwise it implies it's OK to fail. */ +static bool handle_wireaddr_listen(const tal_t *ctx, + struct daemon *daemon, + const struct wireaddr_internal *wi, + bool websocket, + char **errstr) { int fd; struct sockaddr_in addr; struct sockaddr_in6 addr6; + const struct wireaddr *wireaddr; struct io_plan *(*in_cb)(struct io_conn *, struct daemon *); if (websocket) @@ -1120,29 +1132,32 @@ static bool handle_wireaddr_listen(struct daemon *daemon, else in_cb = connection_in; + assert(wi->itype == ADDR_INTERNAL_WIREADDR); + wireaddr = &wi->u.wireaddr; + /* Note the use of a switch() over enum here, even though it must be * IPv4 or IPv6 here; that will catch future changes. */ switch (wireaddr->type) { case ADDR_TYPE_IPV4: wireaddr_to_ipv4(wireaddr, &addr); /* We might fail if IPv6 bound to port first */ - fd = make_listen_fd(AF_INET, &addr, sizeof(addr), mayfail); + fd = make_listen_fd(ctx, daemon, wi, AF_INET, &addr, sizeof(addr), errstr); if (fd >= 0) { status_debug("Created IPv4 %slistener on port %u", websocket ? "websocket ": "", wireaddr->port); - add_listen_fd(daemon, fd, mayfail, in_cb); + add_listen_fd(daemon, fd, errstr == NULL, in_cb); return true; } return false; case ADDR_TYPE_IPV6: wireaddr_to_ipv6(wireaddr, &addr6); - fd = make_listen_fd(AF_INET6, &addr6, sizeof(addr6), mayfail); + fd = make_listen_fd(ctx, daemon, wi, AF_INET6, &addr6, sizeof(addr6), errstr); if (fd >= 0) { status_debug("Created IPv6 %slistener on port %u", websocket ? "websocket ": "", wireaddr->port); - add_listen_fd(daemon, fd, mayfail, in_cb); + add_listen_fd(daemon, fd, errstr == NULL, in_cb); return true; } return false; @@ -1220,7 +1235,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, announce or both */ const enum addr_listen_announce *proposed_listen_announce, const char *tor_password, - struct wireaddr **announcable) + struct wireaddr **announcable, + char **errstr) { struct sockaddr_un addrun; int fd; @@ -1265,8 +1281,11 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, sizeof(addrun.sun_path)); /* Remove any existing one. */ unlink(wa.u.sockname); - fd = make_listen_fd(AF_UNIX, &addrun, sizeof(addrun), - false); + fd = make_listen_fd(ctx, daemon, &wa, AF_UNIX, &addrun, sizeof(addrun), + errstr); + /* Don't bother freeing here; we'll exit */ + if (fd < 0) + return NULL; status_debug("Created socket listener on file %s", addrun.sun_path); add_listen_fd(daemon, fd, false, connection_in); @@ -1293,8 +1312,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, memset(wa.u.wireaddr.addr, 0, sizeof(wa.u.wireaddr.addr)); - ipv6_ok = handle_wireaddr_listen(daemon, &wa.u.wireaddr, - true, false); + ipv6_ok = handle_wireaddr_listen(ctx, daemon, &wa, false, NULL); if (ipv6_ok) { add_binding(&binding, &wa); if (announce @@ -1309,20 +1327,22 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, memset(wa.u.wireaddr.addr, 0, sizeof(wa.u.wireaddr.addr)); /* OK if this fails, as long as one succeeds! */ - if (handle_wireaddr_listen(daemon, &wa.u.wireaddr, - ipv6_ok, false)) { + if (handle_wireaddr_listen(ctx, daemon, &wa, false, ipv6_ok ? NULL : errstr)) { add_binding(&binding, &wa); if (announce && public_address(daemon, &wa.u.wireaddr)) add_announcable(announcable, &wa.u.wireaddr); + } else if (!ipv6_ok) { + /* Both failed, return now, errstr set. */ + return NULL; } continue; } /* This is a vanilla wireaddr as per BOLT #7 */ case ADDR_INTERNAL_WIREADDR: - handle_wireaddr_listen(daemon, &wa.u.wireaddr, - false, false); + if (!handle_wireaddr_listen(ctx, daemon, &wa, false, errstr)) + return NULL; add_binding(&binding, &wa); if (announce && public_address(daemon, &wa.u.wireaddr)) add_announcable(announcable, &wa.u.wireaddr); @@ -1339,17 +1359,20 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, /* If we want websockets to match IPv4/v6, set it up now. */ if (daemon->websocket_port) { bool announced_some = false; - struct wireaddr addr; + struct wireaddr_internal addr; + /* If not overriden below, this is the default. */ + *errstr = "Cannot advertize websocket: no IPv4/6 addresses advertized"; for (size_t i = 0; i < tal_count(binding); i++) { /* Ignore UNIX sockets */ if (binding[i].itype != ADDR_INTERNAL_WIREADDR) continue; /* Override with websocket port */ - addr = binding[i].u.wireaddr; - addr.port = daemon->websocket_port; - if (handle_wireaddr_listen(daemon, &addr, true, true)) + addr = binding[i]; + addr.u.wireaddr.port = daemon->websocket_port; + /* FIXME: catch errors here! */ + if (handle_wireaddr_listen(ctx, daemon, &addr, true, NULL)) announced_some = true; /* FIXME: We don't report these bindings to * lightningd, so they don't appear in @@ -1363,9 +1386,10 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, * least one address of different type. */ if (announced_some && tal_count(*announcable) != 0) { - wireaddr_from_websocket(&addr, daemon->websocket_port); - add_announcable(announcable, &addr); - } + wireaddr_from_websocket(&addr.u.wireaddr, daemon->websocket_port); + add_announcable(announcable, &addr.u.wireaddr); + } else + return NULL; } /* FIXME: Websocket over Tor (difficult for autotor, since we need @@ -1447,6 +1471,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, */ asort(*announcable, tal_count(*announcable), wireaddr_cmp_type, NULL); + *errstr = NULL; return binding; } @@ -1464,6 +1489,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) char *tor_password; bool dev_fast_gossip; bool dev_disconnect; + char *errstr; /* Fields which require allocation are allocated off daemon */ if (!fromwire_connectd_init( @@ -1518,7 +1544,8 @@ static void connect_init(struct daemon *daemon, const u8 *msg) proposed_wireaddr, proposed_listen_announce, tor_password, - &announcable); + &announcable, + &errstr); /* Free up old allocations */ tal_free(proposed_wireaddr); @@ -1528,8 +1555,9 @@ static void connect_init(struct daemon *daemon, const u8 *msg) /* Tell it we're ready, handing it the addresses we have. */ daemon_conn_send(daemon->master, take(towire_connectd_init_reply(NULL, - binding, - announcable))); + binding, + announcable, + errstr))); #if DEVELOPER if (dev_disconnect) dev_disconnect_init(5); diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index e297aaeee2db..5f3409042de1 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -31,6 +31,7 @@ msgdata,connectd_init_reply,num_bindings,u16, msgdata,connectd_init_reply,bindings,wireaddr_internal,num_bindings msgdata,connectd_init_reply,num_announcable,u16, msgdata,connectd_init_reply,announcable,wireaddr,num_announcable +msgdata,connectd_init_reply,failmsg,?wirestring, # Activate the connect daemon, so others can connect. msgtype,connectd_activate,2025 diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 57b0db804189..cbde80d114e5 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -464,13 +464,23 @@ static void connect_init_done(struct subd *connectd, void *unused UNUSED) { struct lightningd *ld = connectd->ld; + char *errmsg; + log_debug(connectd->log, "connectd_init_done"); if (!fromwire_connectd_init_reply(ld, reply, - &ld->binding, - &ld->announcable)) + &ld->binding, + &ld->announcable, + &errmsg)) fatal("Bad connectd_activate_reply: %s", tal_hex(reply, reply)); + /* connectd can fail in *informative* ways: don't use fatal() here and + * confuse things with a backtrace! */ + if (errmsg) { + log_broken(connectd->log, "%s", errmsg); + exit(1); + } + /* Break out of loop, so we can begin */ io_break(connectd); } From 200a8a985b1c5d39cadef50df4ff531d35b5a01f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Mar 2022 20:55:11 +1030 Subject: [PATCH 0395/1530] connectd: add is_websocket and wireaddr to struct listen_fd. This lets us give a better error message if listen fails, and also moved the callback closer to where it's needed. Signed-off-by: Rusty Russell --- connectd/connectd.c | 85 +++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index dd695b9ef6e3..751a8cdac60d 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1023,6 +1023,15 @@ static void try_connect_one_addr(struct connecting *connect) try_connect_one_addr(connect); } +/*~ Sometimes it's nice to have an explicit enum instead of a bool to make + * arguments clearer: it kind of hacks around C's lack of naming formal + * arguments in callers (e.g. in Python we'd simply call func(websocket=False)). + */ +enum is_websocket { + NORMAL_SOCKET, + WEBSOCKET, +}; + /*~ connectd is responsible for incoming connections, but it's the process of * setting up the listening ports which gives us information we need for startup * (such as our own address). So we perform setup in two phases: first we bind @@ -1031,28 +1040,33 @@ static void try_connect_one_addr(struct connecting *connect) * * This stores the fds we're going to listen on: */ struct listen_fd { + /* This is usually an IPv4/v6 address, but we also support local + * domain sockets (i.e. filesystem) */ + struct wireaddr_internal wi; + /* The actual fd, ready to listen() on */ int fd; /* If we bind() IPv6 then IPv4 to same port, we *may* fail to listen() * on the IPv4 socket: under Linux, by default, the IPv6 listen() * covers IPv4 too. Normally we'd consider failing to listen on a * port to be fatal, so we note this when setting up addresses. */ bool mayfail; - /* Callback to use for the listening: either connection_in, or for - * our much-derided WebSocket ability, websocket_connection_in! */ - struct io_plan *(*in_cb)(struct io_conn *conn, struct daemon *daemon); + /* Is this a websocket? */ + enum is_websocket is_websocket; }; -static void add_listen_fd(struct daemon *daemon, int fd, bool mayfail, - struct io_plan *(*in_cb)(struct io_conn *, - struct daemon *)) +static void add_listen_fd(struct daemon *daemon, + const struct wireaddr_internal *wi, + int fd, bool mayfail, + enum is_websocket is_websocket) { /*~ utils.h contains a convenience macro tal_arr_expand which * reallocates a tal_arr to make it one longer, then returns a pointer * to the (new) last element. */ struct listen_fd l; + l.wi = *wi; l.fd = fd; l.mayfail = mayfail; - l.in_cb = in_cb; + l.is_websocket = is_websocket; tal_arr_expand(&daemon->listen_fds, l); } @@ -1118,19 +1132,13 @@ static int make_listen_fd(const tal_t *ctx, static bool handle_wireaddr_listen(const tal_t *ctx, struct daemon *daemon, const struct wireaddr_internal *wi, - bool websocket, + enum is_websocket is_websocket, char **errstr) { int fd; struct sockaddr_in addr; struct sockaddr_in6 addr6; const struct wireaddr *wireaddr; - struct io_plan *(*in_cb)(struct io_conn *, struct daemon *); - - if (websocket) - in_cb = websocket_connection_in; - else - in_cb = connection_in; assert(wi->itype == ADDR_INTERNAL_WIREADDR); wireaddr = &wi->u.wireaddr; @@ -1144,9 +1152,9 @@ static bool handle_wireaddr_listen(const tal_t *ctx, fd = make_listen_fd(ctx, daemon, wi, AF_INET, &addr, sizeof(addr), errstr); if (fd >= 0) { status_debug("Created IPv4 %slistener on port %u", - websocket ? "websocket ": "", + is_websocket ? "websocket ": "", wireaddr->port); - add_listen_fd(daemon, fd, errstr == NULL, in_cb); + add_listen_fd(daemon, wi, fd, errstr == NULL, is_websocket); return true; } return false; @@ -1155,9 +1163,9 @@ static bool handle_wireaddr_listen(const tal_t *ctx, fd = make_listen_fd(ctx, daemon, wi, AF_INET6, &addr6, sizeof(addr6), errstr); if (fd >= 0) { status_debug("Created IPv6 %slistener on port %u", - websocket ? "websocket ": "", + is_websocket ? "websocket ": "", wireaddr->port); - add_listen_fd(daemon, fd, errstr == NULL, in_cb); + add_listen_fd(daemon, wi, fd, errstr == NULL, is_websocket); return true; } return false; @@ -1288,7 +1296,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, return NULL; status_debug("Created socket listener on file %s", addrun.sun_path); - add_listen_fd(daemon, fd, false, connection_in); + add_listen_fd(daemon, &wa, fd, false, NORMAL_SOCKET); /* We don't announce socket names, though we allow * them to lazily specify --addr=/socket. */ add_binding(&binding, &wa); @@ -1312,7 +1320,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, memset(wa.u.wireaddr.addr, 0, sizeof(wa.u.wireaddr.addr)); - ipv6_ok = handle_wireaddr_listen(ctx, daemon, &wa, false, NULL); + ipv6_ok = handle_wireaddr_listen(ctx, daemon, &wa, + NORMAL_SOCKET, NULL); if (ipv6_ok) { add_binding(&binding, &wa); if (announce @@ -1327,7 +1336,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, memset(wa.u.wireaddr.addr, 0, sizeof(wa.u.wireaddr.addr)); /* OK if this fails, as long as one succeeds! */ - if (handle_wireaddr_listen(ctx, daemon, &wa, false, ipv6_ok ? NULL : errstr)) { + if (handle_wireaddr_listen(ctx, daemon, &wa, + NORMAL_SOCKET, ipv6_ok ? NULL : errstr)) { add_binding(&binding, &wa); if (announce && public_address(daemon, &wa.u.wireaddr)) @@ -1341,7 +1351,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, } /* This is a vanilla wireaddr as per BOLT #7 */ case ADDR_INTERNAL_WIREADDR: - if (!handle_wireaddr_listen(ctx, daemon, &wa, false, errstr)) + if (!handle_wireaddr_listen(ctx, daemon, &wa, + NORMAL_SOCKET, errstr)) return NULL; add_binding(&binding, &wa); if (announce && public_address(daemon, &wa.u.wireaddr)) @@ -1372,7 +1383,8 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, addr = binding[i]; addr.u.wireaddr.port = daemon->websocket_port; /* FIXME: catch errors here! */ - if (handle_wireaddr_listen(ctx, daemon, &addr, true, NULL)) + if (handle_wireaddr_listen(ctx, daemon, &addr, + WEBSOCKET, NULL)) announced_some = true; /* FIXME: We don't report these bindings to * lightningd, so they don't appear in @@ -1564,6 +1576,22 @@ static void connect_init(struct daemon *daemon, const u8 *msg) #endif } +/* Returning functions in C is ugly! */ +static struct io_plan *(*get_in_cb(enum is_websocket is_websocket))(struct io_conn *, struct daemon *) + +{ + /*~ This switch and fall pattern serves a specific purpose: + * gcc will warn if we don't handle every case! */ + switch (is_websocket) { + case WEBSOCKET: + return websocket_connection_in; + case NORMAL_SOCKET: + return connection_in; + } + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Invalid is_websocket %u", is_websocket); +} + /*~ lightningd tells us to go! */ static void connect_activate(struct daemon *daemon, const u8 *msg) { @@ -1575,21 +1603,24 @@ static void connect_activate(struct daemon *daemon, const u8 *msg) /* If we're --offline, lightningd tells us not to actually listen. */ if (do_listen) { for (size_t i = 0; i < tal_count(daemon->listen_fds); i++) { - /* On Linux, at least, we may bind to all addresses - * for IPv4 and IPv6, but we'll fail to listen. */ if (listen(daemon->listen_fds[i].fd, 64) != 0) { if (daemon->listen_fds[i].mayfail) continue; status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed to listen on socket: %s", + "Failed to listen on socket %s: %s", + type_to_string(tmpctx, + struct wireaddr_internal, + &daemon->listen_fds[i].wi), strerror(errno)); } notleak(io_new_listener(daemon, daemon->listen_fds[i].fd, - daemon->listen_fds[i].in_cb, + get_in_cb(daemon->listen_fds[i] + .is_websocket), daemon)); } } + /* Free, with NULL assignment just as an extra sanity check. */ daemon->listen_fds = tal_free(daemon->listen_fds); From a62f5e5d827a577a94d842912ed5eb71979ee1cd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Mar 2022 20:56:11 +1030 Subject: [PATCH 0396/1530] connectd: hoist find_local_address so we can give more graceful Tor erros. Signed-off-by: Rusty Russell --- connectd/connectd.c | 39 ++++++++++++++++++++++++++++++++++++-- connectd/tor_autoservice.c | 20 +------------------ connectd/tor_autoservice.h | 6 +----- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 751a8cdac60d..bd4c7a0eee92 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1227,6 +1227,31 @@ static int wireaddr_cmp_type(const struct wireaddr *a, return cmp; } +/* We need to have a bound address we can tell Tor to connect to */ +static const struct wireaddr * +find_local_address(const struct wireaddr_internal *bindings) +{ + for (size_t i = 0; i < tal_count(bindings); i++) { + if (bindings[i].itype != ADDR_INTERNAL_WIREADDR) + continue; + if (bindings[i].u.wireaddr.type != ADDR_TYPE_IPV4 + && bindings[i].u.wireaddr.type != ADDR_TYPE_IPV6) + continue; + return &bindings[i].u.wireaddr; + } + return NULL; +} + +static bool want_tor(const struct wireaddr_internal *proposed_wireaddr) +{ + for (size_t i = 0; i < tal_count(proposed_wireaddr); i++) { + if (proposed_wireaddr[i].itype == ADDR_INTERNAL_STATICTOR + || proposed_wireaddr[i].itype == ADDR_INTERNAL_AUTOTOR) + return true; + } + return false; +} + /*~ The user can specify three kinds of addresses: ones we bind to but don't * announce, ones we announce but don't bind to, and ones we bind to and * announce if they seem to be public addresses. @@ -1249,6 +1274,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, struct sockaddr_un addrun; int fd; struct wireaddr_internal *binding; + const struct wireaddr *localaddr; const char *blob = NULL; struct secret random; struct pubkey pb; @@ -1367,6 +1393,15 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, proposed_wireaddr[i].itype); } + /* Make sure we have at least one non-websocket address to send to, + * for Tor */ + localaddr = find_local_address(binding); + if (want_tor(proposed_wireaddr) && !localaddr) { + *errstr = "Need to bind at least one local address," + " to send Tor connections to"; + return NULL; + } + /* If we want websockets to match IPv4/v6, set it up now. */ if (daemon->websocket_port) { bool announced_some = false; @@ -1417,7 +1452,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, toraddr = tor_autoservice(tmpctx, &proposed_wireaddr[i], tor_password, - binding, + localaddr, daemon->use_v3_autotor); if (!(proposed_listen_announce[i] & ADDR_ANNOUNCE)) { @@ -1462,7 +1497,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, &proposed_wireaddr[i], tor_password, blob, - find_local_address(binding), + localaddr, 0); /* get rid of blob data on our side of tor and add jitter */ randombytes_buf((void * const)proposed_wireaddr[i].u.torservice.blob, TOR_V3_BLOBLEN); diff --git a/connectd/tor_autoservice.c b/connectd/tor_autoservice.c index e471231f5324..a790c8615815 100644 --- a/connectd/tor_autoservice.c +++ b/connectd/tor_autoservice.c @@ -265,36 +265,18 @@ static void negotiate_auth(struct rbuf *rbuf, const char *tor_password) "Tor protocolinfo did not give auth"); } -/* We need to have a bound address we can tell Tor to connect to */ -const struct wireaddr * -find_local_address(const struct wireaddr_internal *bindings) -{ - for (size_t i = 0; i < tal_count(bindings); i++) { - if (bindings[i].itype != ADDR_INTERNAL_WIREADDR) - continue; - if (bindings[i].u.wireaddr.type != ADDR_TYPE_IPV4 - && bindings[i].u.wireaddr.type != ADDR_TYPE_IPV6) - continue; - return &bindings[i].u.wireaddr; - } - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "No local address found to tell Tor to connect to"); -} - struct wireaddr *tor_autoservice(const tal_t *ctx, const struct wireaddr_internal *tor_serviceaddr, const char *tor_password, - const struct wireaddr_internal *bindings, + const struct wireaddr *laddr, const bool use_v3_autotor) { int fd; - const struct wireaddr *laddr; struct wireaddr *onion; struct addrinfo *ai_tor; struct rbuf rbuf; char *buffer; - laddr = find_local_address(bindings); ai_tor = wireaddr_to_addrinfo(tmpctx, &tor_serviceaddr->u.torservice.address); fd = socket(ai_tor->ai_family, SOCK_STREAM, 0); diff --git a/connectd/tor_autoservice.h b/connectd/tor_autoservice.h index e88731ac9de4..8bdb8bb9d435 100644 --- a/connectd/tor_autoservice.h +++ b/connectd/tor_autoservice.h @@ -9,7 +9,7 @@ struct wireaddr *tor_autoservice(const tal_t *ctx, const struct wireaddr_internal *tor_serviceaddr, const char *tor_password, - const struct wireaddr_internal *bindings, + const struct wireaddr *localaddr, const bool use_v3_autotor); struct wireaddr *tor_fixed_service(const tal_t *ctx, @@ -19,8 +19,4 @@ struct wireaddr *tor_fixed_service(const tal_t *ctx, const struct wireaddr *bind, const u8 index); -const struct wireaddr * -find_local_address(const struct wireaddr_internal *bindings); - - #endif /* LIGHTNING_CONNECTD_TOR_AUTOSERVICE_H */ From c075d78431f33ba2e359e16bdac14e3b166104b1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Mar 2022 16:40:53 +1030 Subject: [PATCH 0397/1530] connectd: use listen_fd array directly, rather than returning binding arr. We always added to both arrays, might as well just keep one. We make mayfail an explicit flag, rather than relying on the presence of errstr, which is never NULL now. Signed-off-by: Rusty Russell --- connectd/connectd.c | 275 +++++++++++++++++++++++--------------------- connectd/connectd.h | 2 +- 2 files changed, 144 insertions(+), 133 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index bd4c7a0eee92..38d98c28f047 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1054,51 +1054,47 @@ struct listen_fd { enum is_websocket is_websocket; }; -static void add_listen_fd(struct daemon *daemon, - const struct wireaddr_internal *wi, - int fd, bool mayfail, - enum is_websocket is_websocket) +static struct listen_fd *listen_fd_new(const tal_t *ctx, + const struct wireaddr_internal *wi, + int fd, bool mayfail, + enum is_websocket is_websocket) { - /*~ utils.h contains a convenience macro tal_arr_expand which - * reallocates a tal_arr to make it one longer, then returns a pointer - * to the (new) last element. */ - struct listen_fd l; - l.wi = *wi; - l.fd = fd; - l.mayfail = mayfail; - l.is_websocket = is_websocket; - tal_arr_expand(&daemon->listen_fds, l); + struct listen_fd *l = tal(ctx, struct listen_fd); + + l->wi = *wi; + l->fd = fd; + l->mayfail = mayfail; + l->is_websocket = is_websocket; + return l; } /*~ Helper routine to create and bind a socket of a given type; like many * daemons we set it SO_REUSEADDR so we won't have to wait 2 minutes to reuse * it on restart. * - * I generally avoid "return -1 on error", but for file-descriptors it's the - * UNIX standard, so it's not as offensive here as it would be in other - * contexts. - * * Note that it's generally an antipattern to have a function which - * returns an allocated object (here, errstr) without an explicit tal ctx so the - * caller is aware. - */ -static int make_listen_fd(const tal_t *ctx, - struct daemon *daemon, - const struct wireaddr_internal *wi, - int domain, void *addr, socklen_t len, - char **errstr) + * returns an allocated object without an explicit tal ctx so the + * caller is aware. */ +static struct listen_fd *make_listen_fd(const tal_t *ctx, + const struct wireaddr_internal *wi, + int domain, void *addr, socklen_t len, + bool listen_mayfail, + enum is_websocket is_websocket, + char **errstr) { int fd = socket(domain, SOCK_STREAM, 0); int on = 1; if (fd < 0) { - if (errstr) - *errstr = tal_fmt(ctx, "Failed to create socket for %s: %s", - type_to_string(tmpctx, struct wireaddr_internal, wi), - strerror(errno)); + *errstr = tal_fmt(ctx, "Failed to create socket for %s%s: %s", + is_websocket ? "websocket " : "", + type_to_string(tmpctx, + struct wireaddr_internal, + wi), + strerror(errno)); status_debug("Failed to create %u socket: %s", domain, strerror(errno)); - return -1; + return NULL; } /* Re-use, please.. */ @@ -1107,35 +1103,38 @@ static int make_listen_fd(const tal_t *ctx, strerror(errno)); if (bind(fd, addr, len) != 0) { - if (errstr) - *errstr = tal_fmt(ctx, "Failed to bind socket for %s: %s", - type_to_string(tmpctx, struct wireaddr_internal, wi), - strerror(errno)); + *errstr = tal_fmt(ctx, "Failed to bind socket for %s%s: %s", + is_websocket ? "websocket " : "", + type_to_string(tmpctx, + struct wireaddr_internal, + wi), + strerror(errno)); status_debug("Failed to create %u socket: %s", domain, strerror(errno)); goto fail; } - if (errstr) - *errstr = NULL; - return fd; + *errstr = NULL; + status_debug("Created %slistener on %s", + is_websocket ? "websocket ": "", + type_to_string(tmpctx, struct wireaddr_internal, wi)); + return listen_fd_new(ctx, wi, fd, listen_mayfail, is_websocket); fail: /*~ ccan/noerr contains convenient routines which don't clobber the * errno global; in this case, the caller can report errno. */ close_noerr(fd); - return -1; + return NULL; } /* Return true if it created socket successfully. If errstr is non-NULL, * allocate off ctx if return false, otherwise it implies it's OK to fail. */ -static bool handle_wireaddr_listen(const tal_t *ctx, - struct daemon *daemon, - const struct wireaddr_internal *wi, - enum is_websocket is_websocket, - char **errstr) +static struct listen_fd *handle_wireaddr_listen(const tal_t *ctx, + const struct wireaddr_internal *wi, + bool listen_mayfail, + enum is_websocket is_websocket, + char **errstr) { - int fd; struct sockaddr_in addr; struct sockaddr_in6 addr6; const struct wireaddr *wireaddr; @@ -1149,26 +1148,12 @@ static bool handle_wireaddr_listen(const tal_t *ctx, case ADDR_TYPE_IPV4: wireaddr_to_ipv4(wireaddr, &addr); /* We might fail if IPv6 bound to port first */ - fd = make_listen_fd(ctx, daemon, wi, AF_INET, &addr, sizeof(addr), errstr); - if (fd >= 0) { - status_debug("Created IPv4 %slistener on port %u", - is_websocket ? "websocket ": "", - wireaddr->port); - add_listen_fd(daemon, wi, fd, errstr == NULL, is_websocket); - return true; - } - return false; + return make_listen_fd(ctx, wi, AF_INET, &addr, sizeof(addr), + listen_mayfail, is_websocket, errstr); case ADDR_TYPE_IPV6: wireaddr_to_ipv6(wireaddr, &addr6); - fd = make_listen_fd(ctx, daemon, wi, AF_INET6, &addr6, sizeof(addr6), errstr); - if (fd >= 0) { - status_debug("Created IPv6 %slistener on port %u", - is_websocket ? "websocket ": "", - wireaddr->port); - add_listen_fd(daemon, wi, fd, errstr == NULL, is_websocket); - return true; - } - return false; + return make_listen_fd(ctx, wi, AF_INET6, &addr6, sizeof(addr6), + listen_mayfail, is_websocket, errstr); /* Handle specially by callers. */ case ADDR_TYPE_WEBSOCKET: case ADDR_TYPE_TOR_V2_REMOVED: @@ -1195,15 +1180,12 @@ static bool public_address(struct daemon *daemon, struct wireaddr *wireaddr) static void add_announcable(struct wireaddr **announcable, const struct wireaddr *addr) { + /*~ utils.h contains a convenience macro tal_arr_expand which + * reallocates a tal_arr to make it one longer, then returns a pointer + * to the (new) last element. */ tal_arr_expand(announcable, *addr); } -static void add_binding(struct wireaddr_internal **binding, - const struct wireaddr_internal *addr) -{ - tal_arr_expand(binding, *addr); -} - /*~ ccan/asort provides a type-safe sorting function; it requires a comparison * function, which takes an optional extra argument which is usually unused as * here, but deeply painful if you need it and don't have it! */ @@ -1229,15 +1211,15 @@ static int wireaddr_cmp_type(const struct wireaddr *a, /* We need to have a bound address we can tell Tor to connect to */ static const struct wireaddr * -find_local_address(const struct wireaddr_internal *bindings) +find_local_address(const struct listen_fd **listen_fds) { - for (size_t i = 0; i < tal_count(bindings); i++) { - if (bindings[i].itype != ADDR_INTERNAL_WIREADDR) + for (size_t i = 0; i < tal_count(listen_fds); i++) { + if (listen_fds[i]->wi.itype != ADDR_INTERNAL_WIREADDR) continue; - if (bindings[i].u.wireaddr.type != ADDR_TYPE_IPV4 - && bindings[i].u.wireaddr.type != ADDR_TYPE_IPV6) + if (listen_fds[i]->wi.u.wireaddr.type != ADDR_TYPE_IPV4 + && listen_fds[i]->wi.u.wireaddr.type != ADDR_TYPE_IPV6) continue; - return &bindings[i].u.wireaddr; + return &listen_fds[i]->wi.u.wireaddr; } return NULL; } @@ -1256,32 +1238,35 @@ static bool want_tor(const struct wireaddr_internal *proposed_wireaddr) * announce, ones we announce but don't bind to, and ones we bind to and * announce if they seem to be public addresses. * - * This routine sorts out the mess: it populates the daemon->announcable array, + * This routine sorts out the mess: it populates the *announcable array, * and returns the addresses we bound to (by convention, return is allocated * off `ctx` argument). + * + * Note the important difference between returning a zero-element array, and + * returning NULL! The latter means failure here, the former simply means + * we don't want to listen to anything. */ -static struct wireaddr_internal *setup_listeners(const tal_t *ctx, - struct daemon *daemon, - /* The proposed address. */ - const struct wireaddr_internal *proposed_wireaddr, - /* For each one, listen, - announce or both */ - const enum addr_listen_announce *proposed_listen_announce, - const char *tor_password, - struct wireaddr **announcable, - char **errstr) +static const struct listen_fd ** +setup_listeners(const tal_t *ctx, + struct daemon *daemon, + /* The proposed address. */ + const struct wireaddr_internal *proposed_wireaddr, + /* For each one, listen, announce or both */ + const enum addr_listen_announce *proposed_listen_announce, + const char *tor_password, + struct wireaddr **announcable, + char **errstr) { struct sockaddr_un addrun; - int fd; - struct wireaddr_internal *binding; - const struct wireaddr *localaddr; + const struct listen_fd **listen_fds, *lfd; const char *blob = NULL; struct secret random; struct pubkey pb; struct wireaddr *toraddr; + const struct wireaddr *localaddr; /* Start with empty arrays, for tal_arr_expand() */ - binding = tal_arr(ctx, struct wireaddr_internal, 0); + listen_fds = tal_arr(ctx, const struct listen_fd *, 0); *announcable = tal_arr(ctx, struct wireaddr, 0); /* Add addresses we've explicitly been told to *first*: implicit @@ -1315,17 +1300,16 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, sizeof(addrun.sun_path)); /* Remove any existing one. */ unlink(wa.u.sockname); - fd = make_listen_fd(ctx, daemon, &wa, AF_UNIX, &addrun, sizeof(addrun), - errstr); + lfd = make_listen_fd(ctx, &wa, AF_UNIX, + &addrun, sizeof(addrun), + false, NORMAL_SOCKET, + errstr); /* Don't bother freeing here; we'll exit */ - if (fd < 0) + if (!lfd) return NULL; - status_debug("Created socket listener on file %s", - addrun.sun_path); - add_listen_fd(daemon, &wa, fd, false, NORMAL_SOCKET); /* We don't announce socket names, though we allow * them to lazily specify --addr=/socket. */ - add_binding(&binding, &wa); + tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); continue; case ADDR_INTERNAL_AUTOTOR: /* We handle these after we have all bindings. */ @@ -1346,25 +1330,30 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, memset(wa.u.wireaddr.addr, 0, sizeof(wa.u.wireaddr.addr)); - ipv6_ok = handle_wireaddr_listen(ctx, daemon, &wa, - NORMAL_SOCKET, NULL); - if (ipv6_ok) { - add_binding(&binding, &wa); + /* This may fail due to no IPv6 support. */ + lfd = handle_wireaddr_listen(ctx, &wa, false, + NORMAL_SOCKET, errstr); + if (lfd) { + tal_arr_expand(&listen_fds, + tal_steal(listen_fds, lfd)); if (announce && public_address(daemon, &wa.u.wireaddr)) add_announcable(announcable, &wa.u.wireaddr); } + ipv6_ok = (lfd != NULL); /* Now, create wildcard IPv4 address. */ wa.u.wireaddr.type = ADDR_TYPE_IPV4; wa.u.wireaddr.addrlen = 4; memset(wa.u.wireaddr.addr, 0, sizeof(wa.u.wireaddr.addr)); - /* OK if this fails, as long as one succeeds! */ - if (handle_wireaddr_listen(ctx, daemon, &wa, - NORMAL_SOCKET, ipv6_ok ? NULL : errstr)) { - add_binding(&binding, &wa); + /* This listen *may* fail, as long as IPv6 succeeds! */ + lfd = handle_wireaddr_listen(ctx, &wa, ipv6_ok, + NORMAL_SOCKET, errstr); + if (lfd) { + tal_arr_expand(&listen_fds, + tal_steal(listen_fds, lfd)); if (announce && public_address(daemon, &wa.u.wireaddr)) add_announcable(announcable, @@ -1377,10 +1366,11 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, } /* This is a vanilla wireaddr as per BOLT #7 */ case ADDR_INTERNAL_WIREADDR: - if (!handle_wireaddr_listen(ctx, daemon, &wa, - NORMAL_SOCKET, errstr)) + lfd = handle_wireaddr_listen(ctx, &wa, false, + NORMAL_SOCKET, errstr); + if (!lfd) return NULL; - add_binding(&binding, &wa); + tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); if (announce && public_address(daemon, &wa.u.wireaddr)) add_announcable(announcable, &wa.u.wireaddr); continue; @@ -1395,7 +1385,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, /* Make sure we have at least one non-websocket address to send to, * for Tor */ - localaddr = find_local_address(binding); + localaddr = find_local_address(listen_fds); if (want_tor(proposed_wireaddr) && !localaddr) { *errstr = "Need to bind at least one local address," " to send Tor connections to"; @@ -1406,24 +1396,31 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, if (daemon->websocket_port) { bool announced_some = false; struct wireaddr_internal addr; + /* Only consider bindings added before this! */ + size_t num_nonws_listens = tal_count(listen_fds); /* If not overriden below, this is the default. */ - *errstr = "Cannot advertize websocket: no IPv4/6 addresses advertized"; - for (size_t i = 0; i < tal_count(binding); i++) { + *errstr = "Cannot listen on websocket: not listening on any IPv4/6 addresses"; + for (size_t i = 0; i < num_nonws_listens; i++) { /* Ignore UNIX sockets */ - if (binding[i].itype != ADDR_INTERNAL_WIREADDR) + if (listen_fds[i]->wi.itype != ADDR_INTERNAL_WIREADDR) continue; /* Override with websocket port */ - addr = binding[i]; + addr = listen_fds[i]->wi; addr.u.wireaddr.port = daemon->websocket_port; - /* FIXME: catch errors here! */ - if (handle_wireaddr_listen(ctx, daemon, &addr, - WEBSOCKET, NULL)) - announced_some = true; - /* FIXME: We don't report these bindings to - * lightningd, so they don't appear in - * getinfo. */ + + /* We set mayfail on all but the first websocket; + * it's quite common to have multple overlapping + * addresses. */ + lfd = handle_wireaddr_listen(ctx, &addr, + announced_some, + WEBSOCKET, errstr); + if (!lfd) + continue; + + announced_some = true; + tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); } /* We add the websocket port to the announcement if we made one @@ -1519,7 +1516,7 @@ static struct wireaddr_internal *setup_listeners(const tal_t *ctx, asort(*announcable, tal_count(*announcable), wireaddr_cmp_type, NULL); *errstr = NULL; - return binding; + return listen_fds; } @@ -1587,24 +1584,39 @@ static void connect_init(struct daemon *daemon, const u8 *msg) } /* Figure out our addresses. */ - binding = setup_listeners(tmpctx, daemon, - proposed_wireaddr, - proposed_listen_announce, - tor_password, - &announcable, - &errstr); + daemon->listen_fds = setup_listeners(daemon, daemon, + proposed_wireaddr, + proposed_listen_announce, + tor_password, + &announcable, + &errstr); /* Free up old allocations */ tal_free(proposed_wireaddr); tal_free(proposed_listen_announce); tal_free(tor_password); + /* Create binding array to send to lightningd */ + binding = tal_arr(tmpctx, struct wireaddr_internal, 0); + for (size_t i = 0; i < tal_count(daemon->listen_fds); i++) { + /* FIXME: Tell it about websockets! */ + if (daemon->listen_fds[i]->is_websocket) + continue; + tal_arr_expand(&binding, daemon->listen_fds[i]->wi); + } + /* Tell it we're ready, handing it the addresses we have. */ daemon_conn_send(daemon->master, take(towire_connectd_init_reply(NULL, binding, announcable, errstr))); + /*~ Who cares about a little once-off memory leak? Turns out we do! + * We have a memory leak checker which scans for allocated memory + * with no pointers to it (a tell-tale leak sign, though with tal it's + * not always a real problem), and this would (did!) trigger it. */ + tal_free(announcable); + #if DEVELOPER if (dev_disconnect) dev_disconnect_init(5); @@ -1638,20 +1650,20 @@ static void connect_activate(struct daemon *daemon, const u8 *msg) /* If we're --offline, lightningd tells us not to actually listen. */ if (do_listen) { for (size_t i = 0; i < tal_count(daemon->listen_fds); i++) { - if (listen(daemon->listen_fds[i].fd, 64) != 0) { - if (daemon->listen_fds[i].mayfail) + if (listen(daemon->listen_fds[i]->fd, 64) != 0) { + if (daemon->listen_fds[i]->mayfail) continue; status_failed(STATUS_FAIL_INTERNAL_ERROR, "Failed to listen on socket %s: %s", type_to_string(tmpctx, struct wireaddr_internal, - &daemon->listen_fds[i].wi), + &daemon->listen_fds[i]->wi), strerror(errno)); } notleak(io_new_listener(daemon, - daemon->listen_fds[i].fd, + daemon->listen_fds[i]->fd, get_in_cb(daemon->listen_fds[i] - .is_websocket), + ->is_websocket), daemon)); } } @@ -2100,7 +2112,6 @@ int main(int argc, char *argv[]) peer_htable_init(&daemon->peers); memleak_add_helper(daemon, memleak_daemon_cb); list_head_init(&daemon->connecting); - daemon->listen_fds = tal_arr(daemon, struct listen_fd, 0); timers_init(&daemon->timers, time_mono()); daemon->gossip_store_fd = -1; diff --git a/connectd/connectd.h b/connectd/connectd.h index dd6637c66eea..9cecc61949e0 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -165,7 +165,7 @@ struct daemon { struct sockaddr *broken_resolver_response; /* File descriptors to listen on once we're activated. */ - struct listen_fd *listen_fds; + const struct listen_fd **listen_fds; /* Allow to define the default behavior of tor services calls*/ bool use_v3_autotor; From 885a6f50aecf0debebdd62397caf539eb5910a2e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Mar 2022 16:40:57 +1030 Subject: [PATCH 0398/1530] connectd: make sure we announce websocket addr which succeeded. By accessing `addr` after the loop, it's possible that it's one which failed, in complex scenarios. Also gives us a chance to warn if they specify a websocket but don't actually end up advertizing it (you *must* advertize a normal addr as well). Signed-off-by: Rusty Russell --- connectd/connectd.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 38d98c28f047..703660913e84 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1419,20 +1419,34 @@ setup_listeners(const tal_t *ctx, if (!lfd) continue; - announced_some = true; + if (!announced_some) { + /* BOLT-websocket #7: + * - MUST NOT add a `type 6` address unless + * there is also at least one address of + * different type. + */ + if (tal_count(*announcable) != 0) { + wireaddr_from_websocket(&addr.u.wireaddr, + daemon->websocket_port); + add_announcable(announcable, + &addr.u.wireaddr); + } else { + status_unusual("Bound to websocket %s," + " but we cannot announce" + " the websocket as we don't" + " announce anything else!", + type_to_string(tmpctx, + struct wireaddr_internal, + &addr)); + } + announced_some = true; + } + tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); } - /* We add the websocket port to the announcement if we made one - * *and* we have other announced addresses. */ - /* BOLT-websocket #7: - * - MUST NOT add a `type 6` address unless there is also at - * least one address of different type. - */ - if (announced_some && tal_count(*announcable) != 0) { - wireaddr_from_websocket(&addr.u.wireaddr, daemon->websocket_port); - add_announcable(announcable, &addr.u.wireaddr); - } else + /* If none of those was possible, it's a configuration error? */ + if (tal_count(listen_fds) == num_nonws_listens) return NULL; } From b5a1715c2b4efbe2814149056ed2370094874468 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Mar 2022 16:40:57 +1030 Subject: [PATCH 0399/1530] connectd: also fail without a scary backtrace when listen fails. For example, if you do: ``` ./lightningd/lightningd --network=regtest --experimental-websocket-port=19846 ``` Then you're trying to reuse the normal port as the websocket port, but this only fails at *listen* time, when we activate connectd. Catch this too. Fixes incorrect fatal() message, too. Signed-off-by: Rusty Russell --- connectd/connectd.c | 16 +++++++++------- connectd/connectd_wire.csv | 1 + lightningd/connect_control.c | 16 ++++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 703660913e84..68ea866b3aed 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1657,6 +1657,7 @@ static struct io_plan *(*get_in_cb(enum is_websocket is_websocket))(struct io_co static void connect_activate(struct daemon *daemon, const u8 *msg) { bool do_listen; + char *errmsg = NULL; if (!fromwire_connectd_activate(msg, &do_listen)) master_badmsg(WIRE_CONNECTD_ACTIVATE, msg); @@ -1667,12 +1668,13 @@ static void connect_activate(struct daemon *daemon, const u8 *msg) if (listen(daemon->listen_fds[i]->fd, 64) != 0) { if (daemon->listen_fds[i]->mayfail) continue; - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "Failed to listen on socket %s: %s", - type_to_string(tmpctx, - struct wireaddr_internal, - &daemon->listen_fds[i]->wi), - strerror(errno)); + errmsg = tal_fmt(tmpctx, + "Failed to listen on socket %s: %s", + type_to_string(tmpctx, + struct wireaddr_internal, + &daemon->listen_fds[i]->wi), + strerror(errno)); + break; } notleak(io_new_listener(daemon, daemon->listen_fds[i]->fd, @@ -1687,7 +1689,7 @@ static void connect_activate(struct daemon *daemon, const u8 *msg) /* OK, we're ready! */ daemon_conn_send(daemon->master, - take(towire_connectd_activate_reply(NULL))); + take(towire_connectd_activate_reply(NULL, errmsg))); } /* BOLT #10: diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 5f3409042de1..63ef72f3efc6 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -40,6 +40,7 @@ msgdata,connectd_activate,listen,bool, # Connectd->master, I am ready. msgtype,connectd_activate_reply,2125 +msgdata,connectd_activate_reply,failmsg,?wirestring, # connectd->master: disconnect this peer please (due to reconnect). msgtype,connectd_reconnected,2112 diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index cbde80d114e5..322ceb69fb09 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -471,7 +471,7 @@ static void connect_init_done(struct subd *connectd, &ld->binding, &ld->announcable, &errmsg)) - fatal("Bad connectd_activate_reply: %s", + fatal("Bad connectd_init_reply: %s", tal_hex(reply, reply)); /* connectd can fail in *informative* ways: don't use fatal() here and @@ -549,10 +549,22 @@ int connectd_init(struct lightningd *ld) } static void connect_activate_done(struct subd *connectd, - const u8 *reply UNUSED, + const u8 *reply, const int *fds UNUSED, void *unused UNUSED) { + char *errmsg; + if (!fromwire_connectd_activate_reply(reply, reply, &errmsg)) + fatal("Bad connectd_activate_reply: %s", + tal_hex(reply, reply)); + + /* connectd can fail in *informative* ways: don't use fatal() here and + * confuse things with a backtrace! */ + if (errmsg) { + log_broken(connectd->log, "%s", errmsg); + exit(1); + } + /* Break out of loop, so we can begin */ io_break(connectd); } From d991f135161e1e504e5b6d7609d01c5e6eb069f8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Mar 2022 14:29:28 +1030 Subject: [PATCH 0400/1530] gossipd: make sure we set the `urgent` bit if we move our own node_announcement. node_announcement has to follow at least one channel_announcement. When channels close, if this isn't the case, we remove the old node_announcement and put it at the end of the gossip_store. But we lose the "send even if they don't want it" bit in the case it's our own node_announceent, so keep it. This only happens if you don't change your node configuration at all since you opened your first channel, but still worth fixing. We expose the force_node_announce_rexmit() for later use. Signed-off-by: Rusty Russell --- gossipd/routing.c | 33 ++++++++++++++++++++------------- gossipd/routing.h | 4 ++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index c95834d02785..e6a3786ae65e 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -421,6 +421,25 @@ static bool node_announce_predates_channels(const struct node *node) return true; } +/* Move this node's announcement to the tail of the gossip_store, to + * make everyone send it again. */ +void force_node_announce_rexmit(struct routing_state *rstate, + struct node *node) +{ + const u8 *announce; + bool is_local = node_id_eq(&node->id, &rstate->local_id); + announce = gossip_store_get(tmpctx, rstate->gs, node->bcast.index); + + gossip_store_delete(rstate->gs, + &node->bcast, + WIRE_NODE_ANNOUNCEMENT); + node->bcast.index = gossip_store_add(rstate->gs, + announce, + node->bcast.timestamp, + is_local, + NULL); +} + static void remove_chan_from_node(struct routing_state *rstate, struct node *node, const struct chan *chan) { @@ -458,23 +477,11 @@ static void remove_chan_from_node(struct routing_state *rstate, &node->bcast, WIRE_NODE_ANNOUNCEMENT); } else if (node_announce_predates_channels(node)) { - const u8 *announce; - - announce = gossip_store_get(tmpctx, rstate->gs, - node->bcast.index); - /* node announcement predates all channel announcements? * Move to end (we could, in theory, move to just past next * channel_announce, but we don't care that much about spurious * retransmissions in this corner case */ - gossip_store_delete(rstate->gs, - &node->bcast, - WIRE_NODE_ANNOUNCEMENT); - node->bcast.index = gossip_store_add(rstate->gs, - announce, - node->bcast.timestamp, - false, - NULL); + force_node_announce_rexmit(rstate, node); } } diff --git a/gossipd/routing.h b/gossipd/routing.h index 1641284bb667..35abe5eb35dd 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -406,4 +406,8 @@ void remove_all_gossip(struct routing_state *rstate); /* This scid is dead to us. */ void add_to_txout_failures(struct routing_state *rstate, const struct short_channel_id *scid); + +/* Move this node's announcement to the tail of the gossip_store, to + * make everyone send it again. */ +void force_node_announce_rexmit(struct routing_state *rstate, struct node *node); #endif /* LIGHTNING_GOSSIPD_ROUTING_H */ From bf8cb640b71e15f935ced19934150c921c025981 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 3 Mar 2022 14:30:22 +1030 Subject: [PATCH 0401/1530] gossipd: always re-sent our own node_announcement on startup. Even if it hasn't changed, re-send it to everyone. That should help propagation a little. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 31 +++++++++++++++++----- gossipd/test/run-check_node_announcement.c | 3 +++ gossipd/test/run-crc32_of_update.c | 3 +++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index ae1d7d8516c2..6b466252253d 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -219,7 +219,8 @@ static void update_own_node_announcement_after_startup(struct daemon *daemon); * the routing.c code like any other `node_announcement`. Such announcements * are only accepted if there is an announced channel associated with that node * (to prevent spam), so we only call this once we've announced a channel. */ -static void update_own_node_announcement(struct daemon *daemon, bool startup) +/* Returns true if this sent one, or has arranged to send one in future. */ +static bool update_own_node_announcement(struct daemon *daemon, bool startup) { u32 timestamp = gossip_time_now(daemon->rstate).ts.tv_sec; u8 *nannounce; @@ -245,8 +246,9 @@ static void update_own_node_announcement(struct daemon *daemon, bool startup) bool only_missing_tlv; if (!nannounce_different(daemon->rstate->gs, self, nannounce, - &only_missing_tlv)) - return; + &only_missing_tlv)) { + return false; + } /* Missing liquidity_ad, maybe we'll get plugin callback */ if (startup && only_missing_tlv) { @@ -260,7 +262,7 @@ static void update_own_node_announcement(struct daemon *daemon, bool startup) time_from_sec(delay), update_own_node_announcement_after_startup, daemon); - return; + return true; } /* BOLT #7: * @@ -282,16 +284,27 @@ static void update_own_node_announcement(struct daemon *daemon, bool startup) time_from_sec(next - timestamp), update_own_node_announcement_after_startup, daemon); - return; + return true; } } sign_and_send_nannounce(daemon, nannounce, timestamp); + return true; +} + +/* This retransmits the existing node announcement */ +static void force_self_nannounce_rexmit(struct daemon *daemon) +{ + struct node *self = get_node(daemon->rstate, &daemon->id); + + force_node_announce_rexmit(daemon->rstate, self); } static void update_own_node_announcement_after_startup(struct daemon *daemon) { - update_own_node_announcement(daemon, false); + /* If that doesn't send one, arrange rexmit anyway */ + if (!update_own_node_announcement(daemon, false)) + force_self_nannounce_rexmit(daemon); } /* Should we announce our own node? Called at strategic places. */ @@ -304,7 +317,11 @@ void maybe_send_own_node_announce(struct daemon *daemon, bool startup) if (!daemon->rstate->local_channel_announced) return; - update_own_node_announcement(daemon, startup); + /* If we didn't send one, arrange rexmit of existing at startup */ + if (!update_own_node_announcement(daemon, startup)) { + if (startup) + force_self_nannounce_rexmit(daemon); + } } /* Fast accessors for channel_update fields */ diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index ae6958abe558..304ec891b481 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -36,6 +36,9 @@ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for force_node_announce_rexmit */ +void force_node_announce_rexmit(struct routing_state *rstate UNNEEDED, struct node *node UNNEEDED) +{ fprintf(stderr, "force_node_announce_rexmit called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_update */ bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 635fc4f1c525..05f90e4f508f 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -54,6 +54,9 @@ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for force_node_announce_rexmit */ +void force_node_announce_rexmit(struct routing_state *rstate UNNEEDED, struct node *node UNNEEDED) +{ fprintf(stderr, "force_node_announce_rexmit called!\n"); abort(); } /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } From 93c00cc07dd216e3ad619435b97ecfc55377c987 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Mar 2022 11:18:15 +1030 Subject: [PATCH 0402/1530] gossipd: force a fresh node_announcement every 24 hours. Even if nothing has changed. Note that this is different from simply re-xmitting the old one, in that it has a different timestamp, so others will re-xmit it too. This is a side-effect of reports that node_announcement propagation through the network is poor: https://github.com/ElementsProject/lightning/issues/5037 Signed-off-by: Rusty Russell Changelog-Protocol: We now refresh our ndoe_announcement every 24 hours, due to propagation issues. --- gossipd/gossip_generation.c | 42 ++++++++++++++++++++++++++++++++++--- gossipd/gossipd.c | 1 + gossipd/gossipd.h | 5 ++++- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 6b466252253d..3180d52be69b 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -214,13 +214,16 @@ static void sign_and_send_nannounce(struct daemon *daemon, /* Mutual recursion via timer */ static void update_own_node_announcement_after_startup(struct daemon *daemon); +static void setup_force_nannounce_regen_timer(struct daemon *daemon); /* This routine created a `node_announcement` for our node, and hands it to * the routing.c code like any other `node_announcement`. Such announcements * are only accepted if there is an announced channel associated with that node * (to prevent spam), so we only call this once we've announced a channel. */ /* Returns true if this sent one, or has arranged to send one in future. */ -static bool update_own_node_announcement(struct daemon *daemon, bool startup) +static bool update_own_node_announcement(struct daemon *daemon, + bool startup, + bool always_refresh) { u32 timestamp = gossip_time_now(daemon->rstate).ts.tv_sec; u8 *nannounce; @@ -247,6 +250,8 @@ static bool update_own_node_announcement(struct daemon *daemon, bool startup) if (!nannounce_different(daemon->rstate->gs, self, nannounce, &only_missing_tlv)) { + if (always_refresh) + goto send; return false; } @@ -288,7 +293,12 @@ static bool update_own_node_announcement(struct daemon *daemon, bool startup) } } +send: sign_and_send_nannounce(daemon, nannounce, timestamp); + + /* Generate another one in 24 hours. */ + setup_force_nannounce_regen_timer(daemon); + return true; } @@ -303,10 +313,36 @@ static void force_self_nannounce_rexmit(struct daemon *daemon) static void update_own_node_announcement_after_startup(struct daemon *daemon) { /* If that doesn't send one, arrange rexmit anyway */ - if (!update_own_node_announcement(daemon, false)) + if (!update_own_node_announcement(daemon, false, false)) force_self_nannounce_rexmit(daemon); } +/* This creates and transmits a *new* node announcement */ +static void force_self_nannounce_regen(struct daemon *daemon) +{ + struct node *self = get_node(daemon->rstate, &daemon->id); + + /* No channels left? We'll restart timer once we have one. */ + if (!self || !self->bcast.index) + return; + + update_own_node_announcement(daemon, false, true); +} + +/* Because node_announcement propagation is spotty, we rexmit this every + * 24 hours. */ +static void setup_force_nannounce_regen_timer(struct daemon *daemon) +{ + tal_free(daemon->node_announce_regen_timer); + daemon->node_announce_regen_timer + = new_reltimer(&daemon->timers, + daemon, + time_from_sec(24 * 3600), + force_self_nannounce_regen, + daemon); +} + + /* Should we announce our own node? Called at strategic places. */ void maybe_send_own_node_announce(struct daemon *daemon, bool startup) { @@ -318,7 +354,7 @@ void maybe_send_own_node_announce(struct daemon *daemon, bool startup) return; /* If we didn't send one, arrange rexmit of existing at startup */ - if (!update_own_node_announcement(daemon, startup)) { + if (!update_own_node_announcement(daemon, startup, false)) { if (startup) force_self_nannounce_rexmit(daemon); } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 4b607afb7f50..eaec43f7b764 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1059,6 +1059,7 @@ int main(int argc, char *argv[]) list_head_init(&daemon->peers); daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0); daemon->node_announce_timer = NULL; + daemon->node_announce_regen_timer = NULL; daemon->current_blockheight = 0; /* i.e. unknown */ daemon->rates = NULL; list_head_init(&daemon->deferred_updates); diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index a84d5765eb71..70c3ca485d78 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -47,9 +47,12 @@ struct daemon { /* What addresses we can actually announce. */ struct wireaddr *announcable; - /* Timer until we can send a new node_announcement */ + /* Timer until we can send an updated node_announcement */ struct oneshot *node_announce_timer; + /* Timer until we should force a new new node_announcement */ + struct oneshot *node_announce_regen_timer; + /* Channels we have an announce for, but aren't deep enough. */ struct short_channel_id *deferred_txouts; From a3696f04695a3f726ab61af7ad8d3778c73fd44f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 4 Mar 2022 11:18:18 +1030 Subject: [PATCH 0403/1530] gossipd: allow faster updating, 2 per day, before ratelimiting. This limit applies to both node_announcements (which we now send 1 per day), and channel_updates; I've had reports of LND nodes going down daily for database compation, so they end up ratelimited. Changelog-Protocol: We now allow two channel_updates or node_announcements per day, up from 1. Signed-off-by: Rusty Russell --- gossipd/routing.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index e6a3786ae65e..d0ce5388a12a 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -33,10 +33,10 @@ struct pending_node_announce { struct peer *peer_softref; }; -/* We consider a reasonable gossip rate to be 1 per day, with burst of +/* We consider a reasonable gossip rate to be 2 per day, with burst of * 4 per day. So we use a granularity of one hour. */ -#define TOKENS_PER_MSG 24 -#define TOKEN_MAX (24 * 4) +#define TOKENS_PER_MSG 12 +#define TOKEN_MAX (12 * 4) static u8 update_tokens(const struct routing_state *rstate, u8 tokens, u32 prev_timestamp, u32 new_timestamp) From 74fd685219a30fa05165df08c13fc4eb02e44f90 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 21 Feb 2022 19:27:33 +0100 Subject: [PATCH 0404/1530] pyln: Fix the pyln-proto version and migrate to PEP 517 (poetry) --- contrib/pyln-proto/.gitignore | 1 + contrib/pyln-proto/pyln/proto/__init__.py | 3 +- contrib/pyln-proto/pyproject.toml | 25 +++++++++++++ contrib/pyln-proto/requirements.txt | 7 ---- contrib/pyln-proto/setup.py | 43 ----------------------- 5 files changed, 28 insertions(+), 51 deletions(-) create mode 100644 contrib/pyln-proto/.gitignore create mode 100644 contrib/pyln-proto/pyproject.toml delete mode 100644 contrib/pyln-proto/requirements.txt delete mode 100644 contrib/pyln-proto/setup.py diff --git a/contrib/pyln-proto/.gitignore b/contrib/pyln-proto/.gitignore new file mode 100644 index 000000000000..c04bc49f76fa --- /dev/null +++ b/contrib/pyln-proto/.gitignore @@ -0,0 +1 @@ +poetry.lock diff --git a/contrib/pyln-proto/pyln/proto/__init__.py b/contrib/pyln-proto/pyln/proto/__init__.py index d9fea85d2d8a..2a54c4f3c64e 100644 --- a/contrib/pyln-proto/pyln/proto/__init__.py +++ b/contrib/pyln-proto/pyln/proto/__init__.py @@ -3,7 +3,8 @@ from .invoice import Invoice from .onion import OnionPayload, TlvPayload, LegacyOnionPayload from .wire import LightningConnection, LightningServerSocket -from .__version__ import __version__ + +__version__ = "0.10.2" __all__ = [ "Invoice", diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml new file mode 100644 index 000000000000..6bb2af431e2c --- /dev/null +++ b/contrib/pyln-proto/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "pyln-proto" +version = "0.10.2" +description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." +authors = ["Christian Decker "] +license = "BSD-MIT" + +packages = [ + { include = "pyln/proto" }, +] + +[tool.poetry.dependencies] +python = "^3.7" +base58 = "^2.1.1" +bitstring = "^3.1.9" +coincurve = "^17.0.0" +cryptography = "^36.0.1" +PySocks = "^1.7.1" + +[tool.poetry.dev-dependencies] +pytest = "^7" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-proto/requirements.txt b/contrib/pyln-proto/requirements.txt deleted file mode 100644 index 0c81a83bc734..000000000000 --- a/contrib/pyln-proto/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -base58 ~= 2.0.1 -bitstring ~= 3.1.6 -coincurve ~= 13.0 -cryptography ~= 3.2 -mypy>=0.790 -pysocks ~= 1.7.1 -pycparser==2.20 diff --git a/contrib/pyln-proto/setup.py b/contrib/pyln-proto/setup.py deleted file mode 100644 index 9530d8ceed5d..000000000000 --- a/contrib/pyln-proto/setup.py +++ /dev/null @@ -1,43 +0,0 @@ - -from setuptools import setup -import codecs -import io -import os.path - - -with io.open('README.md', encoding='utf-8') as f: - long_description = f.read() - - -with io.open('requirements.txt', encoding='utf-8') as f: - requirements = [r for r in f.read().split('\n') if len(r)] - - -def read(rel_path): - here = os.path.abspath(os.path.dirname(__file__)) - with codecs.open(os.path.join(here, rel_path), 'r') as fp: - return fp.read() - - -setup(name='pyln-proto', - description='Pure python implementation of the Lightning Network protocol', - long_description=long_description, - long_description_content_type='text/markdown', - url='http://github.com/ElementsProject/lightning', - author='Christian Decker', - author_email='decker.christian@gmail.com', - license='MIT', - packages=['pyln.proto', 'pyln.proto.message'], - package_data={'pyln.proto.message': ['py.typed']}, - scripts=[], - zip_safe=True, - use_scm_version={ - "root": "../..", - "relative_to": __file__, - "write_to": "contrib/pyln-proto/pyln/proto/__version__.py", - "write_to_template": "__version__ = \"{version}\"\n", - "version_scheme": "post-release", - "local_scheme": "no-local-version", - }, - setup_requires=["setuptools_scm"], - install_requires=requirements) From b0f8a99310dba666d53c04fff7267dbc7b67de3f Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 21 Feb 2022 19:43:40 +0100 Subject: [PATCH 0405/1530] pyln: Migrate pyln-bolt7 to PEP517 (poetry) --- contrib/pyln-spec/bolt7/.gitignore | 1 + .../bolt7/pyln/spec/bolt7/__init__.py | 26 ++++++++- .../pyln-spec/bolt7/pyln/spec/bolt7/bolt.py | 6 ++- contrib/pyln-spec/bolt7/pyproject.toml | 20 +++++++ contrib/pyln-spec/bolt7/requirements.txt | 1 - contrib/pyln-spec/bolt7/setup.py | 53 ------------------- 6 files changed, 51 insertions(+), 56 deletions(-) create mode 100644 contrib/pyln-spec/bolt7/.gitignore mode change 120000 => 100644 contrib/pyln-spec/bolt7/pyln/spec/bolt7/__init__.py mode change 120000 => 100644 contrib/pyln-spec/bolt7/pyln/spec/bolt7/bolt.py create mode 100644 contrib/pyln-spec/bolt7/pyproject.toml delete mode 100644 contrib/pyln-spec/bolt7/requirements.txt delete mode 100644 contrib/pyln-spec/bolt7/setup.py diff --git a/contrib/pyln-spec/bolt7/.gitignore b/contrib/pyln-spec/bolt7/.gitignore new file mode 100644 index 000000000000..c04bc49f76fa --- /dev/null +++ b/contrib/pyln-spec/bolt7/.gitignore @@ -0,0 +1 @@ +poetry.lock diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/__init__.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/__init__.py deleted file mode 120000 index 52f300f8a0ed..000000000000 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../subinit.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/__init__.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/__init__.py new file mode 100644 index 000000000000..749979a1e431 --- /dev/null +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/__init__.py @@ -0,0 +1,25 @@ +# This is the same __init__.py for all bolt dirs. +from .csv import csv +from .text import text, desc +from .gen_csv_version import __csv_version__ +from .gen_version import __base_version__, __post_version__, __gitversion__ +from .bolt import namespace +import sys + +# eg. 1.0.1.137. +__version__ = '{}.{}.{}'.format(__base_version__, __csv_version__, __post_version__) + +__all__ = [ + 'csv', + 'text', + 'desc', + 'namespace', + '__version__', + '__gitversion__', +] + +mod = sys.modules[__name__] +for d in namespace.subtypes, namespace.tlvtypes, namespace.messagetypes: + for name in d: + setattr(mod, name, d[name]) + __all__.append(name) diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/bolt.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/bolt.py deleted file mode 120000 index c22b879fc58c..000000000000 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/bolt.py +++ /dev/null @@ -1 +0,0 @@ -../../../../bolt.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/bolt.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/bolt.py new file mode 100644 index 000000000000..565c41228744 --- /dev/null +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/bolt.py @@ -0,0 +1,5 @@ +from pyln.proto.message import MessageNamespace +from .csv import csv + + +namespace = MessageNamespace(csv_lines=csv) diff --git a/contrib/pyln-spec/bolt7/pyproject.toml b/contrib/pyln-spec/bolt7/pyproject.toml new file mode 100644 index 000000000000..1062ae710565 --- /dev/null +++ b/contrib/pyln-spec/bolt7/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "pyln-bolt7" +version = "1.0.186" +description = "BOLT7" +authors = ["Rusty Russell"] +license = "BSD-MIT" + +packages = [ + { include = "pyln/spec/bolt7" }, +] + +[tool.poetry.dependencies] +python = "^3.7" +pyln-proto = { path = "../../pyln-proto" } + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-spec/bolt7/requirements.txt b/contrib/pyln-spec/bolt7/requirements.txt deleted file mode 100644 index 16f3090944f2..000000000000 --- a/contrib/pyln-spec/bolt7/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pyln-proto diff --git a/contrib/pyln-spec/bolt7/setup.py b/contrib/pyln-spec/bolt7/setup.py deleted file mode 100644 index c30d18d22ed0..000000000000 --- a/contrib/pyln-spec/bolt7/setup.py +++ /dev/null @@ -1,53 +0,0 @@ -from setuptools import setup -import io -import os - -base = os.path.dirname(__file__) -with io.open(os.path.join(base, 'requirements.txt'), encoding='utf-8') as f: - requirements = [r for r in f.read().split('\n') if len(r)] - - -def bolt_meta(bolt_num): - ctx = {} - pkg_dir = os.path.join(base, 'pyln', 'spec', 'bolt{}'.format(bolt_num)) - - files = ['gen_version.py', 'gen_csv_version.py', 'text.py'] - - for f in files: - f = os.path.join(pkg_dir, f) - with open(f, 'r') as fd: - exec(fd.read(), ctx) - - __version__ = '{__base_version__}.{__csv_version__}.{__post_version__}'.format(**ctx) - return { - 'description': ctx['desc'], - 'version': __version__, - } - - -def bolt_num(): - """Look into the pyln/spec/ directory to see which subpackages we provide. - """ - dirlist = os.listdir(os.path.join('pyln', 'spec')) - assert(len(dirlist) == 1) # Should only be the boltX directory - b = dirlist[0] - assert(b[:4] == 'bolt') - return int(b[4]) - - -boltnum = bolt_num() -meta = bolt_meta(boltnum) - -setup( - **meta, - name='pyln-bolt{}'.format(boltnum), - url='http://github.com/ElementsProject/lightning', - author='Rusty Russell', - author_email='rusty@rustcorp.com.au', - license='MIT', - packages=['pyln.spec.bolt{}'.format(boltnum)], - package_data={'pyln.proto.message': ['py.typed']}, - scripts=[], - zip_safe=True, - install_requires=requirements -) From dd8d2c138c16f79c52d50238b49a5c3692a8efed Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 21 Feb 2022 19:46:15 +0100 Subject: [PATCH 0406/1530] pyln: Migrate pyln-client to PEP 517 (poetry) --- contrib/pyln-client/.gitignore | 2 +- contrib/pyln-client/pyln/client/__init__.py | 2 +- contrib/pyln-client/pyproject.toml | 22 ++++++++++++++ contrib/pyln-client/requirements.txt | 3 -- contrib/pyln-client/setup.py | 32 --------------------- 5 files changed, 24 insertions(+), 37 deletions(-) create mode 100644 contrib/pyln-client/pyproject.toml delete mode 100644 contrib/pyln-client/requirements.txt delete mode 100644 contrib/pyln-client/setup.py diff --git a/contrib/pyln-client/.gitignore b/contrib/pyln-client/.gitignore index 3122c62bfc94..c04bc49f76fa 100644 --- a/contrib/pyln-client/.gitignore +++ b/contrib/pyln-client/.gitignore @@ -1 +1 @@ -pyln/client/__version__.py \ No newline at end of file +poetry.lock diff --git a/contrib/pyln-client/pyln/client/__init__.py b/contrib/pyln-client/pyln/client/__init__.py index da1d9ae8bfb5..c50292f892de 100644 --- a/contrib/pyln-client/pyln/client/__init__.py +++ b/contrib/pyln-client/pyln/client/__init__.py @@ -1,8 +1,8 @@ from .lightning import LightningRpc, RpcError, Millisatoshi from .plugin import Plugin, monkey_patch, RpcException from .gossmap import Gossmap, GossmapNode, GossmapChannel, GossmapNodeId -from .__version__ import __version__ +__version__ = "0.10.2" __all__ = [ "LightningRpc", diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml new file mode 100644 index 000000000000..b4c883b9a44e --- /dev/null +++ b/contrib/pyln-client/pyproject.toml @@ -0,0 +1,22 @@ +[tool.poetry] +name = "pyln-client" +version = "0.10.2" +description = "Client library and plugin library for c-lightning" +authors = ["Christian Decker "] +license = "BSD-MIT" + +packages = [ + { include = "pyln/client" }, +] + +[tool.poetry.dependencies] +python = "^3.7" +pyln-proto = { path = "../pyln-proto" } +pyln-bolt7 = { path = "../pyln-spec/bolt7" } + +[tool.poetry.dev-dependencies] +pytest = "^7.0.1" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-client/requirements.txt b/contrib/pyln-client/requirements.txt deleted file mode 100644 index e7776fb68ac9..000000000000 --- a/contrib/pyln-client/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -recommonmark~=0.7 -pyln-bolt7 -pyln-proto diff --git a/contrib/pyln-client/setup.py b/contrib/pyln-client/setup.py deleted file mode 100644 index 77858c687074..000000000000 --- a/contrib/pyln-client/setup.py +++ /dev/null @@ -1,32 +0,0 @@ -from setuptools import setup -import io - - -with io.open('README.md', encoding='utf-8') as f: - long_description = f.read() - -with io.open('requirements.txt', encoding='utf-8') as f: - requirements = [r for r in f.read().split('\n') if len(r)] - - -setup(name='pyln-client', - description='Client library for lightningd', - long_description=long_description, - long_description_content_type='text/markdown', - url='http://github.com/ElementsProject/lightning', - author='Christian Decker', - author_email='decker.christian@gmail.com', - license='MIT', - packages=['pyln.client'], - scripts=[], - zip_safe=True, - use_scm_version={ - "root": "../..", - "relative_to": __file__, - "write_to": "contrib/pyln-client/pyln/client/__version__.py", - "write_to_template": "__version__ = \"{version}\"\n", - "version_scheme": "post-release", - "local_scheme": "no-local-version", - }, - setup_requires=["setuptools_scm"], - install_requires=requirements) From 4b9bf22193783a82d2885e5a9c2f6e09c1158231 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 21 Feb 2022 19:56:54 +0100 Subject: [PATCH 0407/1530] pyln: Migrate pyln-testing to PEP 517 (poetry) --- contrib/pyln-testing/.gitignore | 1 + contrib/pyln-testing/pyln/testing/__init__.py | 2 +- contrib/pyln-testing/pyproject.toml | 27 +++++++++++++++++ contrib/pyln-testing/requirements.txt | 12 -------- contrib/pyln-testing/setup.py | 29 ------------------- 5 files changed, 29 insertions(+), 42 deletions(-) create mode 100644 contrib/pyln-testing/.gitignore create mode 100644 contrib/pyln-testing/pyproject.toml delete mode 100644 contrib/pyln-testing/requirements.txt delete mode 100644 contrib/pyln-testing/setup.py diff --git a/contrib/pyln-testing/.gitignore b/contrib/pyln-testing/.gitignore new file mode 100644 index 000000000000..c04bc49f76fa --- /dev/null +++ b/contrib/pyln-testing/.gitignore @@ -0,0 +1 @@ +poetry.lock diff --git a/contrib/pyln-testing/pyln/testing/__init__.py b/contrib/pyln-testing/pyln/testing/__init__.py index 6225cf0ce456..8891f5e2285e 100644 --- a/contrib/pyln-testing/pyln/testing/__init__.py +++ b/contrib/pyln-testing/pyln/testing/__init__.py @@ -1,4 +1,4 @@ -from .__version__ import __version__ +__version__ = "0.10.2" __all__ = [ "__version__", diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml new file mode 100644 index 000000000000..e7159d9808c6 --- /dev/null +++ b/contrib/pyln-testing/pyproject.toml @@ -0,0 +1,27 @@ +[tool.poetry] +name = "pyln-testing" +version = "0.10.2" +description = "Test your c-lightning integration, plugins or whatever you want" +authors = ["Christian Decker "] +license = "BSD-MIT" + +packages = [ + { include = "pyln/testing" }, +] + +[tool.poetry.dependencies] +python = "^3.7" +pytest = "^7.0.1" +ephemeral-port-reserve = "^1.1.4" +psycopg2 = "^2.9.3" +python-bitcoinlib = "^0.11.0" +jsonschema = "^4.4.0" +pyln-client = { path = "../pyln-client" } +Flask = "^2.0.3" +cheroot = "^8.6.0" +psutil = "^5.9.0" +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-testing/requirements.txt b/contrib/pyln-testing/requirements.txt deleted file mode 100644 index 1b9fcc647929..000000000000 --- a/contrib/pyln-testing/requirements.txt +++ /dev/null @@ -1,12 +0,0 @@ -Flask==1.1.* -cheroot==8.5.* -ephemeral-port-reserve==1.1.1 -flaky ~= 3.7.0 -psutil==5.7.* -psycopg2-binary==2.8.* -pytest-rerunfailures==9.1.1 -pytest-timeout ~= 1.4.2 -pytest-xdist ~= 2.2.0 -pytest==6.1.* -python-bitcoinlib==0.11.* -jsonschema==3.2.* diff --git a/contrib/pyln-testing/setup.py b/contrib/pyln-testing/setup.py deleted file mode 100644 index d20aaa2cfa6d..000000000000 --- a/contrib/pyln-testing/setup.py +++ /dev/null @@ -1,29 +0,0 @@ -from setuptools import setup - - -with open('README.md', encoding='utf-8') as f: - long_description = f.read() - -with open('requirements.txt', 'r') as f: - requirements = [l.strip() for l in f] - -setup(name='pyln-testing', - description='Library to facilitate writing tests for for lightningd', - long_description=long_description, - long_description_content_type='text/markdown', - url='http://github.com/ElementsProject/lightning', - author='Christian Decker', - author_email='decker.christian@gmail.com', - install_requires=requirements, - license='MIT', - packages=['pyln.testing'], - use_scm_version={ - "root": "../..", - "relative_to": __file__, - "write_to": "contrib/pyln-testing/pyln/testing/__version__.py", - "write_to_template": "__version__ = \"{version}\"\n", - "version_scheme": "post-release", - "local_scheme": "no-local-version", - }, - setup_requires=["setuptools_scm"], - zip_safe=True) From 49d27790789aeba3d36a87251da76988106565f8 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 22 Feb 2022 14:33:41 +0100 Subject: [PATCH 0408/1530] pyln: Use poetry and PEP 517 locks for the root project Allows us to just rely on `poetry` to manage our dependencies, should finally solve all the `mrkd`, `mako` and `mistune` issues we've had for a while. --- poetry.lock | 1254 ++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 31 ++ requirements.txt | 15 - 3 files changed, 1285 insertions(+), 15 deletions(-) create mode 100644 poetry.lock create mode 100644 pyproject.toml delete mode 100644 requirements.txt diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 000000000000..d02e45334dd5 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1254 @@ +[[package]] +name = "asn1crypto" +version = "1.4.0" +description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "base58" +version = "2.1.1" +description = "Base58 and Base58Check implementation." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +tests = ["mypy", "PyHamcrest (>=2.0.2)", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"] + +[[package]] +name = "bitstring" +version = "3.1.9" +description = "Simple construction, analysis and modification of binary data." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "cffi" +version = "1.15.0" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "cheroot" +version = "8.6.0" +description = "Highly-optimized, pure-python HTTP server" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +"jaraco.functools" = "*" +more-itertools = {version = ">=2.6", markers = "python_version >= \"3.6\""} +six = ">=1.11.0" + +[package.extras] +docs = ["sphinx (>=1.8.2)", "jaraco.packaging (>=3.2)", "sphinx-tabs (>=1.1.0)", "furo", "python-dateutil", "sphinxcontrib-apidoc (>=0.3.0)"] + +[[package]] +name = "click" +version = "8.0.4" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "coincurve" +version = "17.0.0" +description = "Cross-platform Python CFFI bindings for libsecp256k1" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +asn1crypto = "*" +cffi = ">=1.3.0" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "crc32c" +version = "2.2.post0" +description = "A python package implementing the crc32c algorithmin hardware and software" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "cryptography" +version = "36.0.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools_rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + +[[package]] +name = "ephemeral-port-reserve" +version = "1.1.4" +description = "Bind to an ephemeral port, force it into the TIME_WAIT state, and unbind it." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "execnet" +version = "1.9.0" +description = "execnet: rapid multi-Python deployment" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +testing = ["pre-commit"] + +[[package]] +name = "flake8" +version = "4.0.1" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" + +[[package]] +name = "flaky" +version = "3.7.0" +description = "Plugin for nose or pytest that automatically reruns flaky tests." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "flask" +version = "2.0.3" +description = "A simple framework for building complex web applications." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +click = ">=7.1.2" +itsdangerous = ">=2.0" +Jinja2 = ">=3.0" +Werkzeug = ">=2.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "importlib-metadata" +version = "4.2.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "importlib-resources" +version = "5.4.0" +description = "Read resources from Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "itsdangerous" +version = "2.1.0" +description = "Safely pass data to untrusted environments and back." +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "jaraco.functools" +version = "3.5.0" +description = "Functools like those found in stdlib" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +more-itertools = "*" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.classes", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[[package]] +name = "jinja2" +version = "3.0.3" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonschema" +version = "4.4.0" +description = "An implementation of JSON Schema validation for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +attrs = ">=17.4.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] + +[[package]] +name = "mako" +version = "1.1.6" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["babel"] +lingua = ["lingua"] + +[[package]] +name = "markupsafe" +version = "2.1.0" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mistune" +version = "0.8.4" +description = "The fastest markdown parser in pure Python" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "more-itertools" +version = "8.12.0" +description = "More routines for operating on iterables, beyond itertools" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "mrkd" +version = "0.2.0" +description = "Write man pages using Markdown, and convert them to Roff or HTML" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Jinja2 = "*" +mistune = "*" +pygments = "*" + +[[package]] +name = "mypy" +version = "0.931" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = ">=1.1.0" +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "psutil" +version = "5.9.0" +description = "Cross-platform lib for process and system monitoring in Python." +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] + +[[package]] +name = "psycopg2" +version = "2.9.3" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pycodestyle" +version = "2.8.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pyflakes" +version = "2.4.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pygments" +version = "2.11.2" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "pyln-bolt7" +version = "1.0.186" +description = "BOLT7" +category = "main" +optional = false +python-versions = "^3.7" +develop = false + +[package.dependencies] +pyln-proto = {path = "../../pyln-proto"} + +[package.source] +type = "directory" +url = "contrib/pyln-spec/bolt7" + +[[package]] +name = "pyln-client" +version = "0.10.2" +description = "Client library and plugin library for c-lightning" +category = "main" +optional = false +python-versions = "^3.7" +develop = false + +[package.dependencies] +pyln-bolt7 = {path = "../pyln-spec/bolt7"} +pyln-proto = {path = "../pyln-proto"} + +[package.source] +type = "directory" +url = "contrib/pyln-client" + +[[package]] +name = "pyln-proto" +version = "0.10.2" +description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." +category = "main" +optional = false +python-versions = "^3.7" +develop = false + +[package.dependencies] +base58 = "^2.1.1" +bitstring = "^3.1.9" +coincurve = "^17.0.0" +cryptography = "^36.0.1" +PySocks = "^1.7.1" + +[package.source] +type = "directory" +url = "contrib/pyln-proto" + +[[package]] +name = "pyln-testing" +version = "0.10.2" +description = "Test your c-lightning integration, plugins or whatever you want" +category = "dev" +optional = false +python-versions = "^3.7" +develop = false + +[package.dependencies] +cheroot = "^8.6.0" +ephemeral-port-reserve = "^1.1.4" +Flask = "^2.0.3" +jsonschema = "^4.4.0" +psutil = "^5.9.0" +psycopg2 = "^2.9.3" +pyln-client = {path = "../pyln-client"} +pytest = "^7.0.1" +python-bitcoinlib = "^0.11.0" + +[package.source] +type = "directory" +url = "contrib/pyln-testing" + +[[package]] +name = "pyparsing" +version = "3.0.7" +description = "Python parsing module" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "pyrsistent" +version = "0.18.1" +description = "Persistent/Functional/Immutable data structures" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pysocks" +version = "1.7.1" +description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pytest" +version = "7.0.1" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-custom-exit-code" +version = "0.3.0" +description = "Exit pytest test session with custom exit code in different scenarios" +category = "dev" +optional = false +python-versions = ">2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pytest = ">=4.0.2" + +[[package]] +name = "pytest-forked" +version = "1.4.0" +description = "run tests in isolated forked subprocesses" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +py = "*" +pytest = ">=3.10" + +[[package]] +name = "pytest-test-groups" +version = "1.0.3" +description = "A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +pytest = ">=2.5" + +[[package]] +name = "pytest-timeout" +version = "2.1.0" +description = "pytest plugin to abort hanging tests" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pytest = ">=5.0.0" + +[[package]] +name = "pytest-xdist" +version = "2.5.0" +description = "pytest xdist plugin for distributed testing and loop-on-failing modes" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +execnet = ">=1.1" +pytest = ">=6.2.0" +pytest-forked = "*" + +[package.extras] +psutil = ["psutil (>=3.0)"] +setproctitle = ["setproctitle"] +testing = ["filelock"] + +[[package]] +name = "python-bitcoinlib" +version = "0.11.0" +description = "The Swiss Army Knife of the Bitcoin protocol." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "typed-ast" +version = "1.5.2" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "typing-extensions" +version = "4.1.1" +description = "Backported and Experimental Type Hints for Python 3.6+" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "websocket-client" +version = "1.2.3" +description = "WebSocket client for Python with low level API options" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] +optional = ["python-socks", "wsaccel"] +test = ["websockets"] + +[[package]] +name = "werkzeug" +version = "2.0.3" +description = "The comprehensive WSGI web application library." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "zipp" +version = "3.7.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.7" +content-hash = "ca73a2bafa306cda115372944c4bd9a62e8ac63916a101d004162223123ce01c" + +[metadata.files] +asn1crypto = [ + {file = "asn1crypto-1.4.0-py2.py3-none-any.whl", hash = "sha256:4bcdf33c861c7d40bdcd74d8e4dd7661aac320fcdf40b9a3f95b4ee12fde2fa8"}, + {file = "asn1crypto-1.4.0.tar.gz", hash = "sha256:f4f6e119474e58e04a2b1af817eb585b4fd72bdd89b998624712b5c99be7641c"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +base58 = [ + {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, + {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, +] +bitstring = [ + {file = "bitstring-3.1.9-py2-none-any.whl", hash = "sha256:e3e340e58900a948787a05e8c08772f1ccbe133f6f41fe3f0fa19a18a22bbf4f"}, + {file = "bitstring-3.1.9-py3-none-any.whl", hash = "sha256:0de167daa6a00c9386255a7cac931b45e6e24e0ad7ea64f1f92a64ac23ad4578"}, + {file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"}, +] +cffi = [ + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, +] +cheroot = [ + {file = "cheroot-8.6.0-py2.py3-none-any.whl", hash = "sha256:62cbced16f07e8aaf512673987cd6b1fc5ad00073345e9ed6c4e2a5cc2a3a22d"}, + {file = "cheroot-8.6.0.tar.gz", hash = "sha256:366adf6e7cac9555486c2d1be6297993022eff6f8c4655c1443268cca3f08e25"}, +] +click = [ + {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, + {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, +] +coincurve = [ + {file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"}, + {file = "coincurve-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25dfa105beba24c8de886f8ed654bb1133866e4e22cfd7ea5ad8438cae6ed924"}, + {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:698efdd53e4fe1bbebaee9b75cbc851be617974c1c60098e9145cb7198ae97fb"}, + {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30dd44d1039f1d237aaa2da6d14a455ca88df3bcb00610b41f3253fdca1be97b"}, + {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d154e2eb5711db8c5ef52fcd80935b5a0e751c057bc6ffb215a7bb409aedef03"}, + {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c71caffb97dd3d0c243beb62352669b1e5dafa3a4bccdbb27d36bd82f5e65d20"}, + {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:747215254e51dd4dfbe6dded9235491263da5d88fe372d66541ca16b51ea078f"}, + {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad2f6df39ba1e2b7b14bb984505ffa7d0a0ecdd697e8d7dbd19e04bc245c87ed"}, + {file = "coincurve-17.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0503326963916c85b61d16f611ea0545f03c9e418fa8007c233c815429e381e8"}, + {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1013c1597b65684ae1c3e42497f9ef5a04527fa6136a84a16b34602606428c74"}, + {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4beef321fd6434448aab03a0c245f31c4e77f43b54b82108c0948d29852ac7e"}, + {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f47806527d3184da3e8b146fac92a8ed567bbd225194f4517943d8cdc85f9542"}, + {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51e56373ac79f4ec1cfc5da53d72c55f5e5ac28d848b0849ef5e687ace857888"}, + {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d694ad194bee9e8792e2e75879dc5238d8a184010cde36c5ad518fcfe2cd8f2"}, + {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74cedb3d3a1dc5abe0c9c2396e1b82cc64496babc5b42e007e72e185cb1edad8"}, + {file = "coincurve-17.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:db874c5c1dcb1f3a19379773b5e8cffc777625a7a7a60dd9a67206e31e62e2e9"}, + {file = "coincurve-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:896b01941254f0a218cf331a9bddfe2d43892f7f1ba10d6e372e2eb744a744c2"}, + {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6aec70238dbe7a5d66b5f9438ff45b08eb5e0990d49c32ebb65247c5d5b89d7a"}, + {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24284d17162569df917a640f19d9654ba3b43cf560ced8864f270da903f73a5"}, + {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ea057f777842396d387103c606babeb3a1b4c6126769cc0a12044312fc6c465"}, + {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b88642edf7f281649b0c0b6ffade051945ccceae4b885e40445634877d0b3049"}, + {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a80a207131813b038351c5bdae8f20f5f774bbf53622081f208d040dd2b7528f"}, + {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1ef72574aa423bc33665ef4be859164a478bad24d48442da874ef3dc39a474d"}, + {file = "coincurve-17.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfd4fab857bcd975edc39111cb5f5c104f138dac2e9ace35ea8434d37bcea3be"}, + {file = "coincurve-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73f39579dd651a9fc29da5a8fc0d8153d872bcbc166f876457baced1a1c01501"}, + {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8852dc01af4f0fe941ffd04069f7e4fecdce9b867a016f823a02286a1a1f07b5"}, + {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bef812da1da202cdd601a256825abcf26d86e8634fac3ec3e615e3bb3ff08c"}, + {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abbefc9ccb170cb255a31df32457c2e43084b9f37589d0694dacc2dea6ddaf7c"}, + {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:abbd9d017a7638dc38a3b9bb4851f8801b7818d4e5ac22e0c75e373b3c1dbff0"}, + {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e2c2e8a1f0b1f8e48049c891af4ae3cad65d115d358bde72f6b8abdbb8a23170"}, + {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c571445b166c714af4f8155e38a894376c16c0431e88963f2fff474a9985d87"}, + {file = "coincurve-17.0.0-py3-none-win32.whl", hash = "sha256:b956b0b2c85e25a7d00099970ff5d8338254b45e46f0a940f4a2379438ce0dde"}, + {file = "coincurve-17.0.0-py3-none-win_amd64.whl", hash = "sha256:630388080da3026e0b0176cc6762eaabecba857ee3fc85767577dea063ea7c6e"}, + {file = "coincurve-17.0.0.tar.gz", hash = "sha256:68da55aff898702952fda3ee04fd6ed60bb6b91f919c69270786ed766b548b93"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +crc32c = [ + {file = "crc32c-2.2.post0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:67f45df0a21d3efeea2875098466c23248bf2f9ed1eefd487a74c44f87f8f542"}, + {file = "crc32c-2.2.post0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1a1b91448eed19d210eb47e3b2a5f25a05c0c1880ad5a4a8b73bc2d74b597869"}, + {file = "crc32c-2.2.post0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:762e0f149d722d544d097dd43e00c0040116130d49345daa9d923c899bcea2b0"}, + {file = "crc32c-2.2.post0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:364d3c7a5d3e6ad592c01d781ad74d810303892a730f2e071a0abb7953c35f68"}, + {file = "crc32c-2.2.post0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:28eb6732020e3eb498702ea350159bebf79a96f1ebde12e4489cb087b408781d"}, + {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:69fed9a5781c931c43613cf1e011217cb3ac53a728d41bf557f368e007cfebac"}, + {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:945399dca5df68db90a5a63d3b05527c9058435720f213beabd805b4447d6f4e"}, + {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ad737c928f01361ff29a9ab23adce6da109093a334e47c9a507caaee0c953f65"}, + {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ea98089bb706d4e070ac841abd0555dc741ac331c1c779e7d89e3061f8dc0d15"}, + {file = "crc32c-2.2.post0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:e07c8324908c258820bd38c4b05fc3f4e58b45edd4e71b2341b4eef8cfa0a2fd"}, + {file = "crc32c-2.2.post0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:b5d332f5825920074868407ab3b9751521fc160716f47e92b93680931f235650"}, + {file = "crc32c-2.2.post0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:4498fb95c48b2a2ab61974b65ec67730085fd58baf379e8373a50d8cf8042f6f"}, + {file = "crc32c-2.2.post0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:3cb208d6a1541cdfadd6ff9c719e51fb9a4800289fd901718d0dee2f6da4eb54"}, + {file = "crc32c-2.2.post0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3fec7da0d62d9e7461a73dbfd1064fdb42094cceba793d81d20cb568b7f33445"}, + {file = "crc32c-2.2.post0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:5eb70b6d91b2c4cbaf5b61e4951d04967703704e247bf42b2eba6c79c6cb3eac"}, + {file = "crc32c-2.2.post0-cp35-cp35m-win32.whl", hash = "sha256:e3327496b1f671357f7d8d7637cd73c0e48770b9228694109ae844a6c67efdb9"}, + {file = "crc32c-2.2.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:966e069d7d9e5d87d00e6b0a14b6e5715940002a55839e0272df10e53f9be5f2"}, + {file = "crc32c-2.2.post0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:71cf48bd0461d35465a79491058abdf5243453046375cee1fce62dc87c12da18"}, + {file = "crc32c-2.2.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6bdcfefc43406afe27e2015fe1b959378eb44a7b498b12bfa0d1e41436e1fe42"}, + {file = "crc32c-2.2.post0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:a1b0c8edb20f0d2a942341cb591826360991cbcc53364d7083595386adeeba04"}, + {file = "crc32c-2.2.post0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:4048370db1890245fa601dfa1b41f6c4a06c02b95d15e1e20ccd2c4b41c7e3e7"}, + {file = "crc32c-2.2.post0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:b36a1cb1de66efc0211cc41bfd0e95410e333f3ca167eb0fff4db599a597dd6c"}, + {file = "crc32c-2.2.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:de97cc65a6765741194e5f26055dbe4976498fd0082783d1c5e80bd7ce6c5a1b"}, + {file = "crc32c-2.2.post0-cp36-cp36m-win32.whl", hash = "sha256:36786a4482c14e1f3b073e31cf50031009088c115e5bb3022eea4ef2506bbdbb"}, + {file = "crc32c-2.2.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:3c430d64da293bf4cd00b8c6252f40944050c6581150e1a2e483546e95bb87d9"}, + {file = "crc32c-2.2.post0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a45e1a35d664a730f166a7e634385c3be0126aba2366c94f5960790ae21f10cb"}, + {file = "crc32c-2.2.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1c409c147e0bee644ae0586540cc1332d9b3421f196f3d57361ab3dd61318724"}, + {file = "crc32c-2.2.post0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c14f2aa3ee5c3ec746731392781826537730c351fdd769d814cd86dd9577bda"}, + {file = "crc32c-2.2.post0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:da84bdf61a9302854c939cb7c9b3885980cf1f98a789048f55fd00aea2acb156"}, + {file = "crc32c-2.2.post0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1e149d6c44e04bf2edb5c677cf294b12fea431df0c37e9d4d90ad9acc2358864"}, + {file = "crc32c-2.2.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:fbb4afa4d6c6ee5205c8a5d9a4722c27a47e1035b34562f7aefc4408c0a94088"}, + {file = "crc32c-2.2.post0-cp37-cp37m-win32.whl", hash = "sha256:04804688634dce8691e7e0b2aecead339f2be15f4c0997fdb92c2a1d79ade826"}, + {file = "crc32c-2.2.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:5faa1c72c5688581ebd71946f34dd937777cdcb59c4e5500a7f61e038b9e7cdc"}, + {file = "crc32c-2.2.post0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:98e1a8907797519e187e5461bcc97b3180977439f77c5d416b51dbfa5fe7bb6b"}, + {file = "crc32c-2.2.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:f82b1848956796fdfbfd9296f858f109aebacaea0b344cbd5708a0a54a668759"}, + {file = "crc32c-2.2.post0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ed5b2097257213bb34a9d9c9160e86980a0c378cd0e70febadec9841d7f93380"}, + {file = "crc32c-2.2.post0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:32cbaea35b37bc4a88f4c4d84ecc3d1c6cfa04f12d49130d36c6e3b11b13f2f7"}, + {file = "crc32c-2.2.post0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0ecb5be3eaaa6c6f14d1f7584b6e828a68f5f633c46e2ea30ab0307922ac496a"}, + {file = "crc32c-2.2.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3ea5714e6bbb3d03adec83c68f85b2ac67a01461b8131ff3065c6312c52e17b0"}, + {file = "crc32c-2.2.post0-cp38-cp38-win32.whl", hash = "sha256:213bdf9e982a287c067f5542a6024e576bfa116b136c3d26cbc10aa133fb4f9c"}, + {file = "crc32c-2.2.post0-cp38-cp38-win_amd64.whl", hash = "sha256:b6828dab82609659bf3bcef9e0cbf6aac11c5d243e7ebcff6516df965ee889cf"}, + {file = "crc32c-2.2.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9cf12a0eaeb151a569670065374b318f1af355c15261c9a74f03a19306ac677"}, + {file = "crc32c-2.2.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0f6e7ce03fd6104b1f9b9a427eb26fa0eb88cafd7faf5ae0d7f4d2fa35d34242"}, + {file = "crc32c-2.2.post0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b47e710c1a35f785323a73f28a91e4da312f6f806d1e3bce62f60a35937af05a"}, + {file = "crc32c-2.2.post0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:fa812a49462e26a600877e0af4ca719a3bc4e85e986d34336d7abc1d0641b37c"}, + {file = "crc32c-2.2.post0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9b3cf77fde6bd2e88423c248f0d35d2549115e9548b0e5a04ee53306a0a39296"}, + {file = "crc32c-2.2.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:fa7392a2c159805ef9d6711dd3090628d567a8fb349e81019cff48f4caeeaa36"}, + {file = "crc32c-2.2.post0-cp39-cp39-win32.whl", hash = "sha256:078cbe11f48e3ace4bb01fe9fa6ef00becb4016ff71b0de3fe794ad0511cff8a"}, + {file = "crc32c-2.2.post0-cp39-cp39-win_amd64.whl", hash = "sha256:0f5284371a0a8eb178ee8dbc157ae1e40cddce2c04719ad8bc2e162a1c37831c"}, + {file = "crc32c-2.2.post0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:0a7bbffaebb8bd91b5039965ef0bc23383dee5078e98e5799de88c75d94f88ca"}, + {file = "crc32c-2.2.post0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:7dad7f0ad3ea71e1e74be29b3312d6902774ef3e91510b0b2bdcd4b612b7797e"}, + {file = "crc32c-2.2.post0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:ad4de5af5494892ba40c73d32337eebf407760dcd25cb94d949dd9b98b662f8c"}, + {file = "crc32c-2.2.post0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9ecf707b09c1f3cb3d4a082ad8912e14175b99b4644fd1069106b4727c179903"}, + {file = "crc32c-2.2.post0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:c69f1e0c6e70927395fdcbfda5f7bed9408cf453c57b609e4b40b04079d86f1f"}, + {file = "crc32c-2.2.post0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:cae9c893ec0eb67f09923a37b40c07eec3f0adfb70bc795276d9fff03b21488e"}, + {file = "crc32c-2.2.post0-pp36-pypy36_pp73-win32.whl", hash = "sha256:43b50ca2ab32267136e5b9e92cb756abb327480d0d477583d0322f10b86dd630"}, + {file = "crc32c-2.2.post0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b3da5d99a8adf07bab4366a94830bad08cb0f4177e483d8bb9e61b157607341d"}, + {file = "crc32c-2.2.post0-pp37-pypy37_pp73-manylinux1_x86_64.whl", hash = "sha256:0a250a257292ed0eb1156a6e0898409f5fdf723314261b8d74fbe52302d232af"}, + {file = "crc32c-2.2.post0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:b6e99e9a93c968b7aedca3e1486c9e6cf57e14516c8316af46e9327f003d66f2"}, + {file = "crc32c-2.2.post0-pp37-pypy37_pp73-win32.whl", hash = "sha256:262b24d51b28bb81e35d6d120d0b440d5a55fda730ced2aff5a9bdd564091397"}, + {file = "crc32c-2.2.post0.tar.gz", hash = "sha256:3d058e7a5e37e4985d1a7ad4cb702bca56b490daa658d4851377d13ead8b435e"}, +] +cryptography = [ + {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"}, + {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"}, + {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"}, + {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"}, + {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"}, + {file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"}, + {file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"}, + {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"}, + {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"}, + {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"}, + {file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"}, + {file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"}, +] +ephemeral-port-reserve = [ + {file = "ephemeral_port_reserve-1.1.4-py2.py3-none-any.whl", hash = "sha256:dae8da99422c643bb52478ed55d5a8428099092391656ba3726ff30c801600c8"}, + {file = "ephemeral_port_reserve-1.1.4.tar.gz", hash = "sha256:b8f7da2c97090cb0801949dec1d6d40c97220505b742a70935ffbd43234c14b2"}, +] +execnet = [ + {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, + {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, +] +flake8 = [ + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, +] +flaky = [ + {file = "flaky-3.7.0-py2.py3-none-any.whl", hash = "sha256:d6eda73cab5ae7364504b7c44670f70abed9e75f77dd116352f662817592ec9c"}, + {file = "flaky-3.7.0.tar.gz", hash = "sha256:3ad100780721a1911f57a165809b7ea265a7863305acb66708220820caf8aa0d"}, +] +flask = [ + {file = "Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, + {file = "Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, +] +importlib-resources = [ + {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, + {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +itsdangerous = [ + {file = "itsdangerous-2.1.0-py3-none-any.whl", hash = "sha256:29285842166554469a56d427addc0843914172343784cb909695fdbe90a3e129"}, + {file = "itsdangerous-2.1.0.tar.gz", hash = "sha256:d848fcb8bc7d507c4546b448574e8a44fc4ea2ba84ebf8d783290d53e81992f5"}, +] +"jaraco.functools" = [ + {file = "jaraco.functools-3.5.0-py3-none-any.whl", hash = "sha256:141f95c490a18eb8aab86caf7a2728f02f604988a26dc36652e3d9fa9e4c49fa"}, + {file = "jaraco.functools-3.5.0.tar.gz", hash = "sha256:31e0e93d1027592b7b0bec6ad468db850338981ebee76ba5e212e235f4c7dda0"}, +] +jinja2 = [ + {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, + {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, +] +jsonschema = [ + {file = "jsonschema-4.4.0-py3-none-any.whl", hash = "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823"}, + {file = "jsonschema-4.4.0.tar.gz", hash = "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83"}, +] +mako = [ + {file = "Mako-1.1.6-py2.py3-none-any.whl", hash = "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57"}, + {file = "Mako-1.1.6.tar.gz", hash = "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-win32.whl", hash = "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454"}, + {file = "MarkupSafe-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-win32.whl", hash = "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a"}, + {file = "MarkupSafe-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-win32.whl", hash = "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8"}, + {file = "MarkupSafe-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-win32.whl", hash = "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05"}, + {file = "MarkupSafe-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7"}, + {file = "MarkupSafe-2.1.0.tar.gz", hash = "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mistune = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] +more-itertools = [ + {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"}, + {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"}, +] +mrkd = [ + {file = "mrkd-0.2.0-py3-none-any.whl", hash = "sha256:4cb47bb9eb8933a34ea2f2110529f33a07abcf1da43b7e77328538b7ee6164a0"}, + {file = "mrkd-0.2.0.tar.gz", hash = "sha256:456f8c1be99da268554b29c6b5383532e58119def5a65d85270bc6a0ecc26aaf"}, +] +mypy = [ + {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, + {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, + {file = "mypy-0.931-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714"}, + {file = "mypy-0.931-cp310-cp310-win_amd64.whl", hash = "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc"}, + {file = "mypy-0.931-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d"}, + {file = "mypy-0.931-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d"}, + {file = "mypy-0.931-cp36-cp36m-win_amd64.whl", hash = "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c"}, + {file = "mypy-0.931-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0"}, + {file = "mypy-0.931-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05"}, + {file = "mypy-0.931-cp37-cp37m-win_amd64.whl", hash = "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7"}, + {file = "mypy-0.931-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0"}, + {file = "mypy-0.931-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069"}, + {file = "mypy-0.931-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799"}, + {file = "mypy-0.931-cp38-cp38-win_amd64.whl", hash = "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a"}, + {file = "mypy-0.931-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166"}, + {file = "mypy-0.931-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266"}, + {file = "mypy-0.931-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd"}, + {file = "mypy-0.931-cp39-cp39-win_amd64.whl", hash = "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697"}, + {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, + {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +psutil = [ + {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"}, + {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"}, + {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"}, + {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"}, + {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"}, + {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"}, + {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"}, + {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"}, + {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"}, + {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"}, + {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"}, + {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"}, + {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"}, + {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"}, + {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"}, + {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"}, + {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"}, + {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"}, + {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"}, + {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"}, + {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"}, + {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"}, + {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"}, + {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"}, + {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"}, + {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"}, + {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"}, + {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"}, + {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"}, + {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"}, + {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"}, + {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"}, +] +psycopg2 = [ + {file = "psycopg2-2.9.3-cp310-cp310-win32.whl", hash = "sha256:083707a696e5e1c330af2508d8fab36f9700b26621ccbcb538abe22e15485362"}, + {file = "psycopg2-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:d3ca6421b942f60c008f81a3541e8faf6865a28d5a9b48544b0ee4f40cac7fca"}, + {file = "psycopg2-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:9572e08b50aed176ef6d66f15a21d823bb6f6d23152d35e8451d7d2d18fdac56"}, + {file = "psycopg2-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:a81e3866f99382dfe8c15a151f1ca5fde5815fde879348fe5a9884a7c092a305"}, + {file = "psycopg2-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:cb10d44e6694d763fa1078a26f7f6137d69f555a78ec85dc2ef716c37447e4b2"}, + {file = "psycopg2-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4295093a6ae3434d33ec6baab4ca5512a5082cc43c0505293087b8a46d108461"}, + {file = "psycopg2-2.9.3-cp38-cp38-win32.whl", hash = "sha256:34b33e0162cfcaad151f249c2649fd1030010c16f4bbc40a604c1cb77173dcf7"}, + {file = "psycopg2-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:0762c27d018edbcb2d34d51596e4346c983bd27c330218c56c4dc25ef7e819bf"}, + {file = "psycopg2-2.9.3-cp39-cp39-win32.whl", hash = "sha256:8cf3878353cc04b053822896bc4922b194792df9df2f1ad8da01fb3043602126"}, + {file = "psycopg2-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c"}, + {file = "psycopg2-2.9.3.tar.gz", hash = "sha256:8e841d1bf3434da985cc5ef13e6f75c8981ced601fd70cc6bf33351b91562981"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pycodestyle = [ + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, +] +pycparser = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] +pyflakes = [ + {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, + {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, +] +pygments = [ + {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, + {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, +] +pyln-bolt7 = [] +pyln-client = [] +pyln-proto = [] +pyln-testing = [] +pyparsing = [ + {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, + {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, +] +pyrsistent = [ + {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, + {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, + {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, + {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"}, + {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"}, + {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"}, + {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"}, + {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"}, + {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"}, + {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"}, + {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"}, + {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"}, + {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"}, + {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"}, + {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"}, + {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, + {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, +] +pysocks = [ + {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, + {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, + {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, +] +pytest = [ + {file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"}, + {file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"}, +] +pytest-custom-exit-code = [ + {file = "pytest-custom_exit_code-0.3.0.tar.gz", hash = "sha256:51ffff0ee2c1ddcc1242e2ddb2a5fd02482717e33a2326ef330e3aa430244635"}, + {file = "pytest_custom_exit_code-0.3.0-py3-none-any.whl", hash = "sha256:6e0ce6e57ce3a583cb7e5023f7d1021e19dfec22be41d9ad345bae2fc61caf3b"}, +] +pytest-forked = [ + {file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"}, + {file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"}, +] +pytest-xdist = [ + {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, + {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, +] +python-bitcoinlib = [ + {file = "python-bitcoinlib-0.11.0.tar.gz", hash = "sha256:3daafd63cb755f6e2067b7c9c514053856034c9f9363c80c37007744d54a2e06"}, + {file = "python_bitcoinlib-0.11.0-py3-none-any.whl", hash = "sha256:6e7982734637135599e2136d3c88d622f147e3b29201636665f799365784cd9e"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +typed-ast = [ + {file = "typed_ast-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:183b183b7771a508395d2cbffd6db67d6ad52958a5fdc99f450d954003900266"}, + {file = "typed_ast-1.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:676d051b1da67a852c0447621fdd11c4e104827417bf216092ec3e286f7da596"}, + {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc2542e83ac8399752bc16e0b35e038bdb659ba237f4222616b4e83fb9654985"}, + {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74cac86cc586db8dfda0ce65d8bcd2bf17b58668dfcc3652762f3ef0e6677e76"}, + {file = "typed_ast-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:18fe320f354d6f9ad3147859b6e16649a0781425268c4dde596093177660e71a"}, + {file = "typed_ast-1.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:31d8c6b2df19a777bc8826770b872a45a1f30cfefcfd729491baa5237faae837"}, + {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:963a0ccc9a4188524e6e6d39b12c9ca24cc2d45a71cfdd04a26d883c922b4b78"}, + {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e"}, + {file = "typed_ast-1.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:294a6903a4d087db805a7656989f613371915fc45c8cc0ddc5c5a0a8ad9bea4d"}, + {file = "typed_ast-1.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:26a432dc219c6b6f38be20a958cbe1abffcc5492821d7e27f08606ef99e0dffd"}, + {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7407cfcad702f0b6c0e0f3e7ab876cd1d2c13b14ce770e412c0c4b9728a0f88"}, + {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f30ddd110634c2d7534b2d4e0e22967e88366b0d356b24de87419cc4410c41b7"}, + {file = "typed_ast-1.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8c08d6625bb258179b6e512f55ad20f9dfef019bbfbe3095247401e053a3ea30"}, + {file = "typed_ast-1.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:90904d889ab8e81a956f2c0935a523cc4e077c7847a836abee832f868d5c26a4"}, + {file = "typed_ast-1.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bbebc31bf11762b63bf61aaae232becb41c5bf6b3461b80a4df7e791fabb3aca"}, + {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29dd9a3a9d259c9fa19d19738d021632d673f6ed9b35a739f48e5f807f264fb"}, + {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:58ae097a325e9bb7a684572d20eb3e1809802c5c9ec7108e85da1eb6c1a3331b"}, + {file = "typed_ast-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:da0a98d458010bf4fe535f2d1e367a2e2060e105978873c04c04212fb20543f7"}, + {file = "typed_ast-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33b4a19ddc9fc551ebabca9765d54d04600c4a50eda13893dadf67ed81d9a098"}, + {file = "typed_ast-1.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1098df9a0592dd4c8c0ccfc2e98931278a6c6c53cb3a3e2cf7e9ee3b06153344"}, + {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c47c3b43fe3a39ddf8de1d40dbbfca60ac8530a36c9b198ea5b9efac75c09e"}, + {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f290617f74a610849bd8f5514e34ae3d09eafd521dceaa6cf68b3f4414266d4e"}, + {file = "typed_ast-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:df05aa5b241e2e8045f5f4367a9f6187b09c4cdf8578bb219861c4e27c443db5"}, + {file = "typed_ast-1.5.2.tar.gz", hash = "sha256:525a2d4088e70a9f75b08b3f87a51acc9cde640e19cc523c7e41aa355564ae27"}, +] +typing-extensions = [ + {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, + {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, +] +websocket-client = [ + {file = "websocket-client-1.2.3.tar.gz", hash = "sha256:1315816c0acc508997eb3ae03b9d3ff619c9d12d544c9a9b553704b1cc4f6af5"}, + {file = "websocket_client-1.2.3-py3-none-any.whl", hash = "sha256:2eed4cc58e4d65613ed6114af2f380f7910ff416fc8c46947f6e76b6815f56c0"}, +] +werkzeug = [ + {file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"}, + {file = "Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"}, +] +zipp = [ + {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, + {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000000..b3e578372450 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,31 @@ +[tool.poetry] +name = "cln-meta-project" +version = "0.1.0" +description = "Just a helper to get our python dependencies under control" +authors = ["Christian Decker "] + +[tool.poetry.dependencies] +# Build dependencies belong here +python = "^3.7" +pyln-client = { path = "contrib/pyln-client" } +pyln-proto = { path = "contrib/pyln-proto" } +Mako = "^1.1.6" +mrkd = "^0.2.0" +websocket-client = "^1.2.3" + +[tool.poetry.dev-dependencies] +# Test dependencies and inherited dependencies belong here +crc32c = "^2.2.post0" # Belongs to lnprototest +pytest-xdist = "^2.5.0" +mistune = "0.8.4" # Belongs to pyln-proto +pytest-test-groups = "^1.0.3" +pytest-timeout = "^2.1.0" +flake8 = "^4.0.1" +mypy = "^0.931" +pytest-custom-exit-code = "0.3.0" +pyln-testing = { path = "contrib/pyln-testing" } +flaky = "^3.7.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 4986279a274f..000000000000 --- a/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Dependencies required to build and test c-lightning -mrkd ~= 0.1.6 -Mako ~= 1.1.3 -flake8 ~= 3.7.8 -websocket-client - -./contrib/pyln-client -./contrib/pyln-proto -./contrib/pyln-testing - -# Dependencies from pyln-spec -# None - -# Dependencies from lnprototest -crc32c From d51f97a9e4be372e8605734a2e02724df269277e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 22 Feb 2022 14:34:58 +0100 Subject: [PATCH 0409/1530] gci: Do not generate JSON reports for test runs We've not been using them, so let's not generate them. --- .github/scripts/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index f74f2f646a9e..076ab18a76bb 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -52,7 +52,7 @@ cat config.vars cat << EOF > pytest.ini [pytest] -addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 --junitxml=report.xml --json-report --json-report-file=report.json --json-report-indent=2 +addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 markers = slow_test: marks tests as slow (deselect with '-m "not slow_test"') EOF From 70840ef066bf463c1e7d096db2f9b85e00962346 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 22 Feb 2022 14:42:40 +0100 Subject: [PATCH 0410/1530] gci: Switch docs and CI builder to use `poetry` --- .github/scripts/build.sh | 25 +++---------------------- .github/workflows/ci.yaml | 5 +---- .github/workflows/macos.yaml | 8 ++++---- doc/INSTALL.md | 15 +++++++-------- poetry.lock | 7 +++++++ 5 files changed, 22 insertions(+), 38 deletions(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 076ab18a76bb..05cece10fa49 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -21,28 +21,9 @@ export VALGRIND=${VALGRIND:-0} export FUZZING=${FUZZING:-0} export LIGHTNINGD_POSTGRES_NO_VACUUM=1 -pip3 install --user -U \ - -r requirements.lock - -timeout 60 pip3 install --user \ - --use-feature=in-tree-build \ - ./contrib/pyln-client \ - ./contrib/pyln-proto \ - ./contrib/pyln-testing - -# Install utilities that aren't dependencies, but make -# running tests easier/feasible on CI (and pytest which -# keeps breaking the rerunfailures plugin). -pip3 install --user \ - blinker \ - flake8 \ - flaky \ - mako \ - pytest-sentry \ - pytest-test-groups==1.0.3 \ - pytest-custom-exit-code==0.3.0 \ - pytest-timeout \ - pytest-json-report +pip3 install --user poetry +poetry config virtualenvs.create false --local +poetry install git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc git submodule update --init --recursive diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 69d95b6bac25..b9fc9ffcdcfd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -30,10 +30,7 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Fetch tags for auto versioning - run: git fetch --prune --unshallow --tags -f - - - name: Set up Python 3.6 + - name: Set up Python 3.7 uses: actions/setup-python@v2 with: python-version: 3.6 diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index 027a537f84d5..adbbf74faa46 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -16,9 +16,6 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Fetch tags for auto versioning - run: git fetch --prune --unshallow --tags - - name: Set up Python 3.6 uses: actions/setup-python@v2 with: @@ -36,7 +33,10 @@ jobs: sudo mv bitcoin-$BITCOIN_VERSION/bin/* /usr/local/bin ) - pip install --upgrade mako pip + pip3 install --user poetry + poetry config virtualenvs.create false --local + poetry install + ln -s /usr/local/Cellar/gettext/0.20.1/bin/xgettext /usr/local/opt export PATH="/usr/local/opt:$PATH" diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 836785eb2a43..02c203a595c5 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -38,9 +38,9 @@ Get dependencies: sudo apt-get update sudo apt-get install -y \ autoconf automake build-essential git libtool libgmp-dev libsqlite3-dev \ - python3 python3-mako python3-pip net-tools zlib1g-dev libsodium-dev \ - gettext - pip3 install --user mrkd mistune==0.8.4 + python3 python3-pip net-tools zlib1g-dev libsodium-dev gettext + pip install --user poetry + poetry install If you don't have Bitcoin installed locally you'll need to install that as well. It's now available via [snapd](https://snapcraft.io/bitcoin-core). @@ -60,8 +60,6 @@ For development or running tests, get additional dependencies: sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ libsecp256k1-dev jq - pip3 install --upgrade pip - pip3 install --user -r requirements.txt Build lightning: @@ -150,7 +148,8 @@ fiddle with compile time options: mrkd is required to build man pages from markdown files (not done by the port): # cd /usr/ports/devel/py-pip && make install - $ pip install --user mrkd + $ pip install --user poetry + $ poetry install See `/usr/ports/net-p2p/c-lightning/Makefile` for instructions on how to build from an arbitrary git commit, instead of the latest release tag. @@ -183,8 +182,8 @@ pkg_add autoconf # (select highest version, autoconf-2.69p2 at time of writing) ``` Install `mako` and `mrkd` otherwise we run into build errors: ``` -pip3.7 install --user mako -pip3.7 install --user mrkd +pip3.7 install --user poetry +poetry install ``` Add `/home//.local/bin` to your path: diff --git a/poetry.lock b/poetry.lock index d02e45334dd5..7e0c7f2934b4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1194,6 +1194,13 @@ pytest-forked = [ {file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"}, {file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"}, ] +pytest-test-groups = [ + {file = "pytest-test-groups-1.0.3.tar.gz", hash = "sha256:a93ee8ae8605ad290965508d13efc975de64f80429465837af5f3dd5bc93fd96"}, +] +pytest-timeout = [ + {file = "pytest-timeout-2.1.0.tar.gz", hash = "sha256:c07ca07404c612f8abbe22294b23c368e2e5104b521c1790195561f37e1ac3d9"}, + {file = "pytest_timeout-2.1.0-py3-none-any.whl", hash = "sha256:f6f50101443ce70ad325ceb4473c4255e9d74e3c7cd0ef827309dfa4c0d975c6"}, +] pytest-xdist = [ {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, From 978711699a600706bdd3d8702016bca9bf64770d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 22 Feb 2022 15:37:30 +0100 Subject: [PATCH 0411/1530] gci: Switch to python 3.7 Since the minimum requirement for pyln-testing is 3.7 we better test with that version at least. --- .github/workflows/ci.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b9fc9ffcdcfd..90fde47b52ee 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -33,7 +33,7 @@ jobs: - name: Set up Python 3.7 uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: Install dependencies run: | @@ -77,10 +77,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Set up Python 3.6 + - name: Set up Python 3.7 uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: Install dependencies run: bash -x .github/scripts/setup.sh @@ -113,10 +113,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Setup Python 3.6 + - name: Setup Python 3.7 uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: Install dependencies run: | @@ -196,10 +196,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Set up Python 3.6 + - name: Set up Python 3.7 uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: Install dependencies run: | @@ -302,10 +302,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Set up Python 3.6 + - name: Set up Python 3.7 uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: Install dependencies run: | @@ -350,10 +350,10 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Set up Python 3.6 + - name: Set up Python 3.7 uses: actions/setup-python@v2 with: - python-version: 3.6 + python-version: 3.7 - name: Install dependencies run: | From f4fda04fb6f7c8a996136aa359bb0af2f423d317 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 23 Feb 2022 19:37:54 +0100 Subject: [PATCH 0412/1530] misc: Make the spell checker just a tiny bit less trigger happy --- tools/check-spelling.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/check-spelling.sh b/tools/check-spelling.sh index 5090352a726e..900fd87e4af8 100755 --- a/tools/check-spelling.sh +++ b/tools/check-spelling.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -if git --no-pager grep -nHiE 'l[ightn]{6}g|l[ightn]{8}g|ilghtning|lgihtning|lihgtning|ligthning|lighnting|lightinng|lightnnig|lightnign' -- . ':!tools/check-spelling.sh'; then +if git --no-pager grep -nHiE 'l[ightn]{6}g|l[ightn]{8}g|ilghtning|lgihtning|lihgtning|ligthning|lighnting|lightinng|lightnnig|lightnign' -- . ':!tools/check-spelling.sh' | grep -vE "highlighting"; then echo "Identified a likely misspelling of the word \"lightning\" (see above). Please fix." echo "Is this warning incorrect? Please teach tools/check-spelling.sh about the exciting new word." exit 1 From fa183b81193cce95eb4ce6a15b47880e5f69fa76 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 23 Feb 2022 19:50:34 +0100 Subject: [PATCH 0413/1530] python: Exclude newly detected f-string flake --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 66232988b464..6f541df029bc 100644 --- a/Makefile +++ b/Makefile @@ -496,7 +496,7 @@ check-python-flake8: @# E731 do not assign a lambda expression, use a def @# W503: line break before binary operator @# E741: ambiguous variable name - @flake8 --ignore=E501,E731,E741,W503 --exclude $(shell echo ${PYTHON_GENERATED} | sed 's/ \+/,/g') ${PYSRC} + @flake8 --ignore=E501,E731,E741,W503,F541 --exclude $(shell echo ${PYTHON_GENERATED} | sed 's/ \+/,/g') ${PYSRC} check-pytest-pyln-proto: PATH=$(PYLN_PATH) PYTHONPATH=$(MY_CHECK_PYTHONPATH) $(PYTEST) contrib/pyln-proto/tests/ From 941f217778fd78046fa9c137c8f40a695603f369 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 24 Feb 2022 14:32:37 +0100 Subject: [PATCH 0414/1530] gci: Fix up the MacOS test with poetry --- .github/workflows/macos.yaml | 39 ++---------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index adbbf74faa46..27d779ff0944 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -16,13 +16,9 @@ jobs: - name: Checkout uses: actions/checkout@v2.0.0 - - name: Set up Python 3.6 - uses: actions/setup-python@v2 - with: - python-version: 3.6 - - name: Install dependencies run: | + export PATH="/usr/local/opt:/Users/runner/.local/bin:/Users/runner/Library/Python/3.9/bin:$PATH" export BITCOIN_VERSION=0.20.1 brew install wget python autoconf automake libtool python3 gmp gnu-sed gettext libsodium @@ -56,33 +52,10 @@ jobs: TEST_GROUP_COUNT: ${{ matrix.TEST_GROUP_COUNT }} TEST_GROUP: ${{ matrix.TEST_GROUP }} run: | - export PATH="/usr/local/opt:/Users/runner/.local/bin:$PATH" + export PATH="/usr/local/opt:/Users/runner/.local/bin:/Users/runner/Library/Python/3.9/bin:$PATH" export LDFLAGS="-L/usr/local/opt/sqlite/lib" export CPPFLAGS="-I/usr/local/opt/sqlite/include" - pip3 install --user -U \ - -r requirements.lock - - pip3 install --user --no-index \ - --use-feature=in-tree-build \ - ./contrib/pyln-spec/bolt7 \ - ./contrib/pyln-client \ - ./contrib/pyln-proto \ - ./contrib/pyln-testing - - # Install utilities that aren't dependencies, but make - # running tests easier/feasible on CI (and pytest which - # keeps breaking the rerunfailures plugin). - pip3 install --user -U \ - blinker \ - flake8 \ - flaky \ - mako \ - pytest-sentry \ - pytest-test-groups==1.0.3 \ - pytest-custom-exit-code==0.3.0 \ - pytest-json-report - cat << EOF > pytest.ini [pytest] addopts=-p no:logging --color=yes --timeout=600 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 --junitxml=report.xml --json-report --json-report-file=report.json --json-report-indent=2 @@ -92,11 +65,3 @@ jobs: ./configure make - - - name: Upload Unit Test Results - if: always() - uses: actions/upload-artifact@v2 - with: - name: Junit Report ${{ github.run_number }}.${{ matrix.cfg }} - path: report.* - if-no-files-found: ignore From 8857789a54708e13f78240c42239f7cf20f80bb4 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 24 Feb 2022 14:56:09 +0100 Subject: [PATCH 0415/1530] gci: Only test pushes if it's `master` Should avoid many of the duplicate test runs --- .github/workflows/bsd.yml | 6 +++++- .github/workflows/ci.yaml | 6 +++++- .github/workflows/macos.yaml | 3 ++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index bea605fef489..360016ff0074 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -1,6 +1,10 @@ name: FreeBSD Test -on: [push, pull_request] +on: + push: + branches: + - "master" + pull_request: jobs: testfreebsd: diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 90fde47b52ee..8ccc22d7ea18 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,10 @@ --- name: Continuous Integration -on: [push, pull_request] +on: + push: + branches: + - "master" + pull_request: jobs: smoke-test: name: Smoke Test ${{ matrix.cfg }} diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index 27d779ff0944..331389f0c4ac 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -1,6 +1,7 @@ --- name: Mac OS pytest -on: [pull_request] +on: + pull_request: jobs: smoke-test: name: Smoke Test macOS From 60135cf9ba6525e0932670f00c7a2f752b20e381 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sat, 26 Feb 2022 11:31:59 +0100 Subject: [PATCH 0416/1530] pyln: MIgrate the BOLT packages to poetry / PEP 517 --- contrib/pyln-spec/bolt.py | 5 -- contrib/pyln-spec/bolt1/.gitignore | 1 + .../bolt1/pyln/spec/bolt1/__init__.py | 26 ++++++++- .../pyln-spec/bolt1/pyln/spec/bolt1/bolt.py | 6 ++- contrib/pyln-spec/bolt1/pyproject.toml | 20 +++++++ contrib/pyln-spec/bolt1/requirements.txt | 1 - contrib/pyln-spec/bolt1/setup.py | 53 ------------------- contrib/pyln-spec/bolt2/.gitignore | 1 + .../bolt2/pyln/spec/bolt2/__init__.py | 26 ++++++++- .../pyln-spec/bolt2/pyln/spec/bolt2/bolt.py | 6 ++- contrib/pyln-spec/bolt2/pyproject.toml | 20 +++++++ contrib/pyln-spec/bolt2/requirements.txt | 1 - contrib/pyln-spec/bolt2/setup.py | 53 ------------------- contrib/pyln-spec/bolt4/.gitignore | 1 + .../bolt4/pyln/spec/bolt4/__init__.py | 26 ++++++++- .../pyln-spec/bolt4/pyln/spec/bolt4/bolt.py | 6 ++- contrib/pyln-spec/bolt4/pyproject.toml | 20 +++++++ contrib/pyln-spec/bolt4/requirements.txt | 1 - contrib/pyln-spec/bolt4/setup.py | 53 ------------------- contrib/pyln-spec/subinit.py | 25 --------- 20 files changed, 153 insertions(+), 198 deletions(-) delete mode 100644 contrib/pyln-spec/bolt.py create mode 100644 contrib/pyln-spec/bolt1/.gitignore mode change 120000 => 100644 contrib/pyln-spec/bolt1/pyln/spec/bolt1/__init__.py mode change 120000 => 100644 contrib/pyln-spec/bolt1/pyln/spec/bolt1/bolt.py create mode 100644 contrib/pyln-spec/bolt1/pyproject.toml delete mode 100644 contrib/pyln-spec/bolt1/requirements.txt delete mode 100644 contrib/pyln-spec/bolt1/setup.py create mode 100644 contrib/pyln-spec/bolt2/.gitignore mode change 120000 => 100644 contrib/pyln-spec/bolt2/pyln/spec/bolt2/__init__.py mode change 120000 => 100644 contrib/pyln-spec/bolt2/pyln/spec/bolt2/bolt.py create mode 100644 contrib/pyln-spec/bolt2/pyproject.toml delete mode 100644 contrib/pyln-spec/bolt2/requirements.txt delete mode 100644 contrib/pyln-spec/bolt2/setup.py create mode 100644 contrib/pyln-spec/bolt4/.gitignore mode change 120000 => 100644 contrib/pyln-spec/bolt4/pyln/spec/bolt4/__init__.py mode change 120000 => 100644 contrib/pyln-spec/bolt4/pyln/spec/bolt4/bolt.py create mode 100644 contrib/pyln-spec/bolt4/pyproject.toml delete mode 100644 contrib/pyln-spec/bolt4/requirements.txt delete mode 100644 contrib/pyln-spec/bolt4/setup.py delete mode 100644 contrib/pyln-spec/subinit.py diff --git a/contrib/pyln-spec/bolt.py b/contrib/pyln-spec/bolt.py deleted file mode 100644 index 565c41228744..000000000000 --- a/contrib/pyln-spec/bolt.py +++ /dev/null @@ -1,5 +0,0 @@ -from pyln.proto.message import MessageNamespace -from .csv import csv - - -namespace = MessageNamespace(csv_lines=csv) diff --git a/contrib/pyln-spec/bolt1/.gitignore b/contrib/pyln-spec/bolt1/.gitignore new file mode 100644 index 000000000000..c04bc49f76fa --- /dev/null +++ b/contrib/pyln-spec/bolt1/.gitignore @@ -0,0 +1 @@ +poetry.lock diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/__init__.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/__init__.py deleted file mode 120000 index 52f300f8a0ed..000000000000 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../subinit.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/__init__.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/__init__.py new file mode 100644 index 000000000000..749979a1e431 --- /dev/null +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/__init__.py @@ -0,0 +1,25 @@ +# This is the same __init__.py for all bolt dirs. +from .csv import csv +from .text import text, desc +from .gen_csv_version import __csv_version__ +from .gen_version import __base_version__, __post_version__, __gitversion__ +from .bolt import namespace +import sys + +# eg. 1.0.1.137. +__version__ = '{}.{}.{}'.format(__base_version__, __csv_version__, __post_version__) + +__all__ = [ + 'csv', + 'text', + 'desc', + 'namespace', + '__version__', + '__gitversion__', +] + +mod = sys.modules[__name__] +for d in namespace.subtypes, namespace.tlvtypes, namespace.messagetypes: + for name in d: + setattr(mod, name, d[name]) + __all__.append(name) diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/bolt.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/bolt.py deleted file mode 120000 index c22b879fc58c..000000000000 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/bolt.py +++ /dev/null @@ -1 +0,0 @@ -../../../../bolt.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/bolt.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/bolt.py new file mode 100644 index 000000000000..565c41228744 --- /dev/null +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/bolt.py @@ -0,0 +1,5 @@ +from pyln.proto.message import MessageNamespace +from .csv import csv + + +namespace = MessageNamespace(csv_lines=csv) diff --git a/contrib/pyln-spec/bolt1/pyproject.toml b/contrib/pyln-spec/bolt1/pyproject.toml new file mode 100644 index 000000000000..903c33114377 --- /dev/null +++ b/contrib/pyln-spec/bolt1/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "pyln-bolt1" +version = "1.0.1.187" +description = "" +authors = ["Rusty Russell <@rustyrussell>"] +license = "MIT" + +packages = [ + { include = "pyln/spec/bolt1" }, +] + +[tool.poetry.dependencies] +python = "^3.7" +pyln-proto = {path = "../../pyln-proto"} + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-spec/bolt1/requirements.txt b/contrib/pyln-spec/bolt1/requirements.txt deleted file mode 100644 index c863b024f737..000000000000 --- a/contrib/pyln-spec/bolt1/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pyln-proto >= 0.9.3 diff --git a/contrib/pyln-spec/bolt1/setup.py b/contrib/pyln-spec/bolt1/setup.py deleted file mode 100644 index c30d18d22ed0..000000000000 --- a/contrib/pyln-spec/bolt1/setup.py +++ /dev/null @@ -1,53 +0,0 @@ -from setuptools import setup -import io -import os - -base = os.path.dirname(__file__) -with io.open(os.path.join(base, 'requirements.txt'), encoding='utf-8') as f: - requirements = [r for r in f.read().split('\n') if len(r)] - - -def bolt_meta(bolt_num): - ctx = {} - pkg_dir = os.path.join(base, 'pyln', 'spec', 'bolt{}'.format(bolt_num)) - - files = ['gen_version.py', 'gen_csv_version.py', 'text.py'] - - for f in files: - f = os.path.join(pkg_dir, f) - with open(f, 'r') as fd: - exec(fd.read(), ctx) - - __version__ = '{__base_version__}.{__csv_version__}.{__post_version__}'.format(**ctx) - return { - 'description': ctx['desc'], - 'version': __version__, - } - - -def bolt_num(): - """Look into the pyln/spec/ directory to see which subpackages we provide. - """ - dirlist = os.listdir(os.path.join('pyln', 'spec')) - assert(len(dirlist) == 1) # Should only be the boltX directory - b = dirlist[0] - assert(b[:4] == 'bolt') - return int(b[4]) - - -boltnum = bolt_num() -meta = bolt_meta(boltnum) - -setup( - **meta, - name='pyln-bolt{}'.format(boltnum), - url='http://github.com/ElementsProject/lightning', - author='Rusty Russell', - author_email='rusty@rustcorp.com.au', - license='MIT', - packages=['pyln.spec.bolt{}'.format(boltnum)], - package_data={'pyln.proto.message': ['py.typed']}, - scripts=[], - zip_safe=True, - install_requires=requirements -) diff --git a/contrib/pyln-spec/bolt2/.gitignore b/contrib/pyln-spec/bolt2/.gitignore new file mode 100644 index 000000000000..c04bc49f76fa --- /dev/null +++ b/contrib/pyln-spec/bolt2/.gitignore @@ -0,0 +1 @@ +poetry.lock diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/__init__.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/__init__.py deleted file mode 120000 index 52f300f8a0ed..000000000000 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../subinit.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/__init__.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/__init__.py new file mode 100644 index 000000000000..749979a1e431 --- /dev/null +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/__init__.py @@ -0,0 +1,25 @@ +# This is the same __init__.py for all bolt dirs. +from .csv import csv +from .text import text, desc +from .gen_csv_version import __csv_version__ +from .gen_version import __base_version__, __post_version__, __gitversion__ +from .bolt import namespace +import sys + +# eg. 1.0.1.137. +__version__ = '{}.{}.{}'.format(__base_version__, __csv_version__, __post_version__) + +__all__ = [ + 'csv', + 'text', + 'desc', + 'namespace', + '__version__', + '__gitversion__', +] + +mod = sys.modules[__name__] +for d in namespace.subtypes, namespace.tlvtypes, namespace.messagetypes: + for name in d: + setattr(mod, name, d[name]) + __all__.append(name) diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/bolt.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/bolt.py deleted file mode 120000 index c22b879fc58c..000000000000 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/bolt.py +++ /dev/null @@ -1 +0,0 @@ -../../../../bolt.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/bolt.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/bolt.py new file mode 100644 index 000000000000..565c41228744 --- /dev/null +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/bolt.py @@ -0,0 +1,5 @@ +from pyln.proto.message import MessageNamespace +from .csv import csv + + +namespace = MessageNamespace(csv_lines=csv) diff --git a/contrib/pyln-spec/bolt2/pyproject.toml b/contrib/pyln-spec/bolt2/pyproject.toml new file mode 100644 index 000000000000..14895c1704f7 --- /dev/null +++ b/contrib/pyln-spec/bolt2/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "pyln-bolt2" +version = "1.0.1.187" +description = "A pure python implementation of BOLT2" +authors = ["Rusty Russell <@rustyrussell>"] +license = "MIT" + +packages = [ + { include = "pyln/spec/bolt2" }, +] + +[tool.poetry.dependencies] +python = "^3.7" +pyln-proto = {path = "../../pyln-proto"} + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-spec/bolt2/requirements.txt b/contrib/pyln-spec/bolt2/requirements.txt deleted file mode 100644 index c863b024f737..000000000000 --- a/contrib/pyln-spec/bolt2/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pyln-proto >= 0.9.3 diff --git a/contrib/pyln-spec/bolt2/setup.py b/contrib/pyln-spec/bolt2/setup.py deleted file mode 100644 index c30d18d22ed0..000000000000 --- a/contrib/pyln-spec/bolt2/setup.py +++ /dev/null @@ -1,53 +0,0 @@ -from setuptools import setup -import io -import os - -base = os.path.dirname(__file__) -with io.open(os.path.join(base, 'requirements.txt'), encoding='utf-8') as f: - requirements = [r for r in f.read().split('\n') if len(r)] - - -def bolt_meta(bolt_num): - ctx = {} - pkg_dir = os.path.join(base, 'pyln', 'spec', 'bolt{}'.format(bolt_num)) - - files = ['gen_version.py', 'gen_csv_version.py', 'text.py'] - - for f in files: - f = os.path.join(pkg_dir, f) - with open(f, 'r') as fd: - exec(fd.read(), ctx) - - __version__ = '{__base_version__}.{__csv_version__}.{__post_version__}'.format(**ctx) - return { - 'description': ctx['desc'], - 'version': __version__, - } - - -def bolt_num(): - """Look into the pyln/spec/ directory to see which subpackages we provide. - """ - dirlist = os.listdir(os.path.join('pyln', 'spec')) - assert(len(dirlist) == 1) # Should only be the boltX directory - b = dirlist[0] - assert(b[:4] == 'bolt') - return int(b[4]) - - -boltnum = bolt_num() -meta = bolt_meta(boltnum) - -setup( - **meta, - name='pyln-bolt{}'.format(boltnum), - url='http://github.com/ElementsProject/lightning', - author='Rusty Russell', - author_email='rusty@rustcorp.com.au', - license='MIT', - packages=['pyln.spec.bolt{}'.format(boltnum)], - package_data={'pyln.proto.message': ['py.typed']}, - scripts=[], - zip_safe=True, - install_requires=requirements -) diff --git a/contrib/pyln-spec/bolt4/.gitignore b/contrib/pyln-spec/bolt4/.gitignore new file mode 100644 index 000000000000..c04bc49f76fa --- /dev/null +++ b/contrib/pyln-spec/bolt4/.gitignore @@ -0,0 +1 @@ +poetry.lock diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/__init__.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/__init__.py deleted file mode 120000 index 52f300f8a0ed..000000000000 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/__init__.py +++ /dev/null @@ -1 +0,0 @@ -../../../../subinit.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/__init__.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/__init__.py new file mode 100644 index 000000000000..749979a1e431 --- /dev/null +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/__init__.py @@ -0,0 +1,25 @@ +# This is the same __init__.py for all bolt dirs. +from .csv import csv +from .text import text, desc +from .gen_csv_version import __csv_version__ +from .gen_version import __base_version__, __post_version__, __gitversion__ +from .bolt import namespace +import sys + +# eg. 1.0.1.137. +__version__ = '{}.{}.{}'.format(__base_version__, __csv_version__, __post_version__) + +__all__ = [ + 'csv', + 'text', + 'desc', + 'namespace', + '__version__', + '__gitversion__', +] + +mod = sys.modules[__name__] +for d in namespace.subtypes, namespace.tlvtypes, namespace.messagetypes: + for name in d: + setattr(mod, name, d[name]) + __all__.append(name) diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/bolt.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/bolt.py deleted file mode 120000 index c22b879fc58c..000000000000 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/bolt.py +++ /dev/null @@ -1 +0,0 @@ -../../../../bolt.py \ No newline at end of file diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/bolt.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/bolt.py new file mode 100644 index 000000000000..565c41228744 --- /dev/null +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/bolt.py @@ -0,0 +1,5 @@ +from pyln.proto.message import MessageNamespace +from .csv import csv + + +namespace = MessageNamespace(csv_lines=csv) diff --git a/contrib/pyln-spec/bolt4/pyproject.toml b/contrib/pyln-spec/bolt4/pyproject.toml new file mode 100644 index 000000000000..33afd320cfcb --- /dev/null +++ b/contrib/pyln-spec/bolt4/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "pyln-bolt4" +version = "1.0.1.187" +description = "A pure python implementation of BOLT4" +authors = ["Rusty Russell <@rustyrussell>"] +license = "MIT" + +packages = [ + { include = "pyln/spec/bolt4" }, +] + +[tool.poetry.dependencies] +python = "^3.7" +pyln-proto = {path = "../../pyln-proto"} + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/contrib/pyln-spec/bolt4/requirements.txt b/contrib/pyln-spec/bolt4/requirements.txt deleted file mode 100644 index c863b024f737..000000000000 --- a/contrib/pyln-spec/bolt4/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pyln-proto >= 0.9.3 diff --git a/contrib/pyln-spec/bolt4/setup.py b/contrib/pyln-spec/bolt4/setup.py deleted file mode 100644 index c30d18d22ed0..000000000000 --- a/contrib/pyln-spec/bolt4/setup.py +++ /dev/null @@ -1,53 +0,0 @@ -from setuptools import setup -import io -import os - -base = os.path.dirname(__file__) -with io.open(os.path.join(base, 'requirements.txt'), encoding='utf-8') as f: - requirements = [r for r in f.read().split('\n') if len(r)] - - -def bolt_meta(bolt_num): - ctx = {} - pkg_dir = os.path.join(base, 'pyln', 'spec', 'bolt{}'.format(bolt_num)) - - files = ['gen_version.py', 'gen_csv_version.py', 'text.py'] - - for f in files: - f = os.path.join(pkg_dir, f) - with open(f, 'r') as fd: - exec(fd.read(), ctx) - - __version__ = '{__base_version__}.{__csv_version__}.{__post_version__}'.format(**ctx) - return { - 'description': ctx['desc'], - 'version': __version__, - } - - -def bolt_num(): - """Look into the pyln/spec/ directory to see which subpackages we provide. - """ - dirlist = os.listdir(os.path.join('pyln', 'spec')) - assert(len(dirlist) == 1) # Should only be the boltX directory - b = dirlist[0] - assert(b[:4] == 'bolt') - return int(b[4]) - - -boltnum = bolt_num() -meta = bolt_meta(boltnum) - -setup( - **meta, - name='pyln-bolt{}'.format(boltnum), - url='http://github.com/ElementsProject/lightning', - author='Rusty Russell', - author_email='rusty@rustcorp.com.au', - license='MIT', - packages=['pyln.spec.bolt{}'.format(boltnum)], - package_data={'pyln.proto.message': ['py.typed']}, - scripts=[], - zip_safe=True, - install_requires=requirements -) diff --git a/contrib/pyln-spec/subinit.py b/contrib/pyln-spec/subinit.py deleted file mode 100644 index 749979a1e431..000000000000 --- a/contrib/pyln-spec/subinit.py +++ /dev/null @@ -1,25 +0,0 @@ -# This is the same __init__.py for all bolt dirs. -from .csv import csv -from .text import text, desc -from .gen_csv_version import __csv_version__ -from .gen_version import __base_version__, __post_version__, __gitversion__ -from .bolt import namespace -import sys - -# eg. 1.0.1.137. -__version__ = '{}.{}.{}'.format(__base_version__, __csv_version__, __post_version__) - -__all__ = [ - 'csv', - 'text', - 'desc', - 'namespace', - '__version__', - '__gitversion__', -] - -mod = sys.modules[__name__] -for d in namespace.subtypes, namespace.tlvtypes, namespace.messagetypes: - for name in d: - setattr(mod, name, d[name]) - __all__.append(name) From ad22becb266ea28e18db6185a8f94918f8ec8807 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 1 Mar 2022 13:01:55 +0100 Subject: [PATCH 0417/1530] pyln: Switch to binary psycopg2 --- contrib/pyln-testing/pyproject.toml | 2 +- poetry.lock | 79 ++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index e7159d9808c6..ab8a59b35ecf 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -13,7 +13,7 @@ packages = [ python = "^3.7" pytest = "^7.0.1" ephemeral-port-reserve = "^1.1.4" -psycopg2 = "^2.9.3" +psycopg2-binary = "^2.9.3" python-bitcoinlib = "^0.11.0" jsonschema = "^4.4.0" pyln-client = { path = "../pyln-client" } diff --git a/poetry.lock b/poetry.lock index 7e0c7f2934b4..6e15b0ad6df4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -411,7 +411,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] [[package]] -name = "psycopg2" +name = "psycopg2-binary" version = "2.9.3" description = "psycopg2 - Python-PostgreSQL Database Adapter" category = "dev" @@ -526,7 +526,7 @@ ephemeral-port-reserve = "^1.1.4" Flask = "^2.0.3" jsonschema = "^4.4.0" psutil = "^5.9.0" -psycopg2 = "^2.9.3" +psycopg2-binary = "^2.9.3" pyln-client = {path = "../pyln-client"} pytest = "^7.0.1" python-bitcoinlib = "^0.11.0" @@ -689,7 +689,7 @@ python-versions = ">=3.6" [[package]] name = "websocket-client" -version = "1.2.3" +version = "1.3.1" description = "WebSocket client for Python with low level API options" category = "main" optional = false @@ -1113,18 +1113,63 @@ psutil = [ {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"}, {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"}, ] -psycopg2 = [ - {file = "psycopg2-2.9.3-cp310-cp310-win32.whl", hash = "sha256:083707a696e5e1c330af2508d8fab36f9700b26621ccbcb538abe22e15485362"}, - {file = "psycopg2-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:d3ca6421b942f60c008f81a3541e8faf6865a28d5a9b48544b0ee4f40cac7fca"}, - {file = "psycopg2-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:9572e08b50aed176ef6d66f15a21d823bb6f6d23152d35e8451d7d2d18fdac56"}, - {file = "psycopg2-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:a81e3866f99382dfe8c15a151f1ca5fde5815fde879348fe5a9884a7c092a305"}, - {file = "psycopg2-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:cb10d44e6694d763fa1078a26f7f6137d69f555a78ec85dc2ef716c37447e4b2"}, - {file = "psycopg2-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4295093a6ae3434d33ec6baab4ca5512a5082cc43c0505293087b8a46d108461"}, - {file = "psycopg2-2.9.3-cp38-cp38-win32.whl", hash = "sha256:34b33e0162cfcaad151f249c2649fd1030010c16f4bbc40a604c1cb77173dcf7"}, - {file = "psycopg2-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:0762c27d018edbcb2d34d51596e4346c983bd27c330218c56c4dc25ef7e819bf"}, - {file = "psycopg2-2.9.3-cp39-cp39-win32.whl", hash = "sha256:8cf3878353cc04b053822896bc4922b194792df9df2f1ad8da01fb3043602126"}, - {file = "psycopg2-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c"}, - {file = "psycopg2-2.9.3.tar.gz", hash = "sha256:8e841d1bf3434da985cc5ef13e6f75c8981ced601fd70cc6bf33351b91562981"}, +psycopg2-binary = [ + {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"}, + {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"}, + {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"}, + {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"}, + {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"}, + {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"}, ] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, @@ -1248,8 +1293,8 @@ typing-extensions = [ {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] websocket-client = [ - {file = "websocket-client-1.2.3.tar.gz", hash = "sha256:1315816c0acc508997eb3ae03b9d3ff619c9d12d544c9a9b553704b1cc4f6af5"}, - {file = "websocket_client-1.2.3-py3-none-any.whl", hash = "sha256:2eed4cc58e4d65613ed6114af2f380f7910ff416fc8c46947f6e76b6815f56c0"}, + {file = "websocket-client-1.3.1.tar.gz", hash = "sha256:6278a75065395418283f887de7c3beafb3aa68dada5cacbe4b214e8d26da499b"}, + {file = "websocket_client-1.3.1-py3-none-any.whl", hash = "sha256:074e2ed575e7c822fc0940d31c3ac9bb2b1142c303eafcf3e304e6ce035522e8"}, ] werkzeug = [ {file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"}, From 81259bbd672fcc9507c8dd42bc2358765e256b34 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 1 Mar 2022 13:19:19 +0100 Subject: [PATCH 0418/1530] py: Update mrkd --- Dockerfile | 5 ++++- poetry.lock | 18 +++++++++++------- pyproject.toml | 3 +-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6da0f36a06f4..3ee42e173333 100644 --- a/Dockerfile +++ b/Dockerfile @@ -54,11 +54,13 @@ RUN apt-get update -qq && \ automake \ build-essential \ ca-certificates \ + curl \ dirmngr \ gettext \ git \ gnupg \ libtool \ + libffi-dev \ python3 \ python3-mako \ python3-pip \ @@ -97,7 +99,8 @@ RUN git clone --recursive /tmp/lightning . && \ ARG DEVELOPER=0 ENV PYTHON_VERSION=3 RUN apt-get install -y --no-install-recommends python3-dev -RUN pip3 install -U pip && pip3 install -r requirements.lock +RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 - +RUN /root/.poetry/bin/poetry install RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install diff --git a/poetry.lock b/poetry.lock index 6e15b0ad6df4..f91d61c15904 100644 --- a/poetry.lock +++ b/poetry.lock @@ -337,16 +337,23 @@ python-versions = ">=3.5" [[package]] name = "mrkd" version = "0.2.0" -description = "Write man pages using Markdown, and convert them to Roff or HTML" +description = "" category = "main" optional = false python-versions = "*" +develop = false [package.dependencies] Jinja2 = "*" -mistune = "*" +mistune = "0.8.4" pygments = "*" +[package.source] +type = "git" +url = "https://github.com/refi64/mrkd.git" +reference = "781f05eb9898ca652f18eed29b3c956389e6a2a7" +resolved_reference = "781f05eb9898ca652f18eed29b3c956389e6a2a7" + [[package]] name = "mypy" version = "0.931" @@ -726,7 +733,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "ca73a2bafa306cda115372944c4bd9a62e8ac63916a101d004162223123ce01c" +content-hash = "279c769b5a15faf494173d6b71dca50d90493534646f9c5e3506b8c52abfe3b5" [metadata.files] asn1crypto = [ @@ -1041,10 +1048,7 @@ more-itertools = [ {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"}, {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"}, ] -mrkd = [ - {file = "mrkd-0.2.0-py3-none-any.whl", hash = "sha256:4cb47bb9eb8933a34ea2f2110529f33a07abcf1da43b7e77328538b7ee6164a0"}, - {file = "mrkd-0.2.0.tar.gz", hash = "sha256:456f8c1be99da268554b29c6b5383532e58119def5a65d85270bc6a0ecc26aaf"}, -] +mrkd = [] mypy = [ {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, diff --git a/pyproject.toml b/pyproject.toml index b3e578372450..3d8214f7ce70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,14 +10,13 @@ python = "^3.7" pyln-client = { path = "contrib/pyln-client" } pyln-proto = { path = "contrib/pyln-proto" } Mako = "^1.1.6" -mrkd = "^0.2.0" +mrkd = { git = "https://github.com/refi64/mrkd.git", rev = "781f05eb9898ca652f18eed29b3c956389e6a2a7" } websocket-client = "^1.2.3" [tool.poetry.dev-dependencies] # Test dependencies and inherited dependencies belong here crc32c = "^2.2.post0" # Belongs to lnprototest pytest-xdist = "^2.5.0" -mistune = "0.8.4" # Belongs to pyln-proto pytest-test-groups = "^1.0.3" pytest-timeout = "^2.1.0" flake8 = "^4.0.1" From 0c4cc782df017eaca62306100fd269d31d8ee705 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 1 Mar 2022 14:28:58 +0100 Subject: [PATCH 0419/1530] docker: Clean up the dockerfile to use poetry --- Dockerfile | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3ee42e173333..c79d34400dad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,13 +62,13 @@ RUN apt-get update -qq && \ libtool \ libffi-dev \ python3 \ + python3-dev \ python3-mako \ python3-pip \ + python3-venv \ python3-setuptools \ wget -RUN pip3 install -U setuptools mrkd mako - RUN wget -q https://zlib.net/zlib-1.2.11.tar.gz \ && tar xvf zlib-1.2.11.tar.gz \ && cd zlib-1.2.11 \ @@ -95,12 +95,13 @@ WORKDIR /opt/lightningd COPY . /tmp/lightning RUN git clone --recursive /tmp/lightning . && \ git checkout $(git --work-tree=/tmp/lightning --git-dir=/tmp/lightning/.git rev-parse HEAD) - ARG DEVELOPER=0 ENV PYTHON_VERSION=3 -RUN apt-get install -y --no-install-recommends python3-dev -RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python3 - -RUN /root/.poetry/bin/poetry install +RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | python3 - \ + && pip3 install -U pip \ + && pip3 install -U wheel \ + && /root/.local/bin/poetry config virtualenvs.create false \ + && /root/.local/bin/poetry install RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install From 00bb6f07d7fc68441a48166e10b2b746863a3631 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 8 Mar 2022 10:44:41 +1030 Subject: [PATCH 0420/1530] lightningd: simplify memleak code. Instead of doing this weird chaining, just call them all at once and use a reference counter. To make it simpler, we return the subd_req so we can hang a destructor off it which decrements after the request is complete. Signed-off-by: Rusty Russell --- lightningd/memdump.c | 167 ++++++++------------ lightningd/memdump.h | 17 +- lightningd/opening_common.c | 86 ---------- lightningd/opening_common.h | 6 - lightningd/peer_control.c | 109 +++++-------- lightningd/peer_control.h | 3 +- lightningd/subd.c | 26 +-- lightningd/subd.h | 2 +- lightningd/test/run-invoice-select-inchan.c | 18 ++- wallet/test/run-wallet.c | 18 ++- 10 files changed, 166 insertions(+), 286 deletions(-) diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 9503decbe39e..02ffe2102ce0 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -124,14 +124,14 @@ static void json_add_backtrace(struct json_stream *response, json_array_end(response); } -static void scan_mem(struct command *cmd, - struct json_stream *response, - struct lightningd *ld, - const struct subd *leaking_subd) +static void finish_report(const struct leak_detect *leaks) { struct htable *memtable; const tal_t *i; const uintptr_t *backtrace; + struct command *cmd = leaks->cmd; + struct lightningd *ld = cmd->ld; + struct json_stream *response = json_stream_success(cmd); /* Enter everything, except this cmd and its jcon */ memtable = memleak_find_allocations(cmd, cmd, cmd->jcon); @@ -165,126 +165,67 @@ static void scan_mem(struct command *cmd, json_object_end(response); } - if (leaking_subd) { + for (size_t i = 0; i < tal_count(leaks->leakers); i++) { json_object_start(response, NULL); - json_add_string(response, "subdaemon", leaking_subd->name); + json_add_string(response, "subdaemon", leaks->leakers[i]); json_object_end(response); } json_array_end(response); -} -struct leak_info { - struct command *cmd; - struct subd *leaker; -}; + /* Command is now done. */ + was_pending(command_success(cmd, response)); +} -static void report_leak_info2(struct leak_info *leak_info) +static void leak_detect_req_done(const struct subd_req *req, + struct leak_detect *leak_detect) { - struct json_stream *response = json_stream_success(leak_info->cmd); - - scan_mem(leak_info->cmd, response, leak_info->cmd->ld, leak_info->leaker); - - was_pending(command_success(leak_info->cmd, response)); + leak_detect->num_outstanding_requests--; + if (leak_detect->num_outstanding_requests == 0) + finish_report(leak_detect); } -static void report_leak_info(struct command *cmd, struct subd *leaker) +/* Start a leak request: decrements num_outstanding_requests when freed. */ +void start_leak_request(const struct subd_req *req, + struct leak_detect *leak_detect) { - struct leak_info *leak_info = tal(cmd, struct leak_info); - - leak_info->cmd = cmd; - leak_info->leaker = leaker; + leak_detect->num_outstanding_requests++; + /* When req is freed, request finished. */ + tal_add_destructor2(req, leak_detect_req_done, leak_detect); +} - /* Leak detection in a reply handler thinks we're leaking conn. */ - notleak(new_reltimer(leak_info->cmd->ld->timers, leak_info->cmd, - time_from_sec(0), - report_leak_info2, leak_info)); +/* Yep, found a leak in this subd. */ +void report_subd_memleak(struct leak_detect *leak_detect, struct subd *leaker) +{ + tal_arr_expand(&leak_detect->leakers, + tal_strdup(leak_detect, leaker->name)); } static void gossip_dev_memleak_done(struct subd *gossipd, const u8 *reply, const int *fds UNUSED, - struct command *cmd) + struct leak_detect *leaks) { bool found_leak; - if (!fromwire_gossipd_dev_memleak_reply(reply, &found_leak)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Bad gossip_dev_memleak")); - return; - } + if (!fromwire_gossipd_dev_memleak_reply(reply, &found_leak)) + fatal("Bad gossip_dev_memleak"); - report_leak_info(cmd, found_leak ? gossipd : NULL); + if (found_leak) + report_subd_memleak(leaks, gossipd); } static void connect_dev_memleak_done(struct subd *connectd, const u8 *reply, const int *fds UNUSED, - struct command *cmd) -{ - bool found_leak; - - if (!fromwire_connectd_dev_memleak_reply(reply, &found_leak)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Bad connect_dev_memleak")); - return; - } - - if (found_leak) { - report_leak_info(cmd, connectd); - return; - } - - /* No leak? Ask openingd. */ - opening_dev_memleak(cmd); -} - -static void hsm_dev_memleak_done(struct subd *hsmd, - const u8 *reply, - struct command *cmd) + struct leak_detect *leaks) { - struct lightningd *ld = cmd->ld; bool found_leak; - if (!fromwire_hsmd_dev_memleak_reply(reply, &found_leak)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Bad hsm_dev_memleak")); - return; - } - - if (found_leak) { - report_leak_info(cmd, hsmd); - return; - } + if (!fromwire_connectd_dev_memleak_reply(reply, &found_leak)) + fatal("Bad connect_dev_memleak"); - /* No leak? Ask gossipd. */ - subd_req(ld->gossip, ld->gossip, take(towire_gossipd_dev_memleak(NULL)), - -1, 0, gossip_dev_memleak_done, cmd); -} - -void peer_memleak_done(struct command *cmd, struct subd *leaker) -{ - if (leaker) - report_leak_info(cmd, leaker); - else { - /* No leak there, try hsmd (we talk to hsm sync) */ - u8 *msg = towire_hsmd_dev_memleak(NULL); - if (!wire_sync_write(cmd->ld->hsm_fd, take(msg))) - fatal("Could not write to HSM: %s", strerror(errno)); - - hsm_dev_memleak_done(cmd->ld->hsm, - wire_sync_read(tmpctx, cmd->ld->hsm_fd), - cmd); - } -} - -void opening_memleak_done(struct command *cmd, struct subd *leaker) -{ - if (leaker) - report_leak_info(cmd, leaker); - else { - /* No leak there, try normal peers. */ - peer_dev_memleak(cmd); - } + if (found_leak) + report_subd_memleak(leaks, connectd); } static struct command_result *json_memleak(struct command *cmd, @@ -293,6 +234,9 @@ static struct command_result *json_memleak(struct command *cmd, const jsmntok_t *params) { struct lightningd *ld = cmd->ld; + u8 *msg; + bool found_leak; + struct leak_detect *leaks; if (!param(cmd, buffer, params, NULL)) return command_param_failed(); @@ -302,11 +246,34 @@ static struct command_result *json_memleak(struct command *cmd, "Leak detection needs $LIGHTNINGD_DEV_MEMLEAK"); } - /* Start by asking connectd, which is always async. */ - subd_req(ld->connectd, ld->connectd, - take(towire_connectd_dev_memleak(NULL)), - -1, 0, connect_dev_memleak_done, cmd); - + leaks = tal(cmd, struct leak_detect); + leaks->cmd = cmd; + leaks->num_outstanding_requests = 0; + leaks->leakers = tal_arr(leaks, const char *, 0); + + /* hsmd is sync, so do that first. */ + if (!wire_sync_write(ld->hsm_fd, + take(towire_hsmd_dev_memleak(NULL)))) + fatal("Could not write to HSM: %s", strerror(errno)); + msg = wire_sync_read(tmpctx, ld->hsm_fd); + if (!fromwire_hsmd_dev_memleak_reply(msg, &found_leak)) + fatal("Bad HSMD_DEV_MEMLEAK_REPLY: %s", tal_hex(tmpctx, msg)); + + if (found_leak) + report_subd_memleak(leaks, ld->hsm); + + /* Now do all the async ones. */ + start_leak_request(subd_req(ld->connectd, ld->connectd, + take(towire_connectd_dev_memleak(NULL)), + -1, 0, connect_dev_memleak_done, leaks), + leaks); + start_leak_request(subd_req(ld->gossip, ld->gossip, + take(towire_gossipd_dev_memleak(NULL)), + -1, 0, gossip_dev_memleak_done, leaks), + leaks); + + /* Ask all per-peer daemons */ + peer_dev_memleak(ld, leaks); return command_still_pending(cmd); } diff --git a/lightningd/memdump.h b/lightningd/memdump.h index 3f9f6b37704d..8ce39fd29f57 100644 --- a/lightningd/memdump.h +++ b/lightningd/memdump.h @@ -3,11 +3,20 @@ #include "config.h" struct command; -struct htable; -struct strmap; struct subd; +struct subd_req; -void opening_memleak_done(struct command *cmd, struct subd *leaker); -void peer_memleak_done(struct command *cmd, struct subd *leaker); +struct leak_detect { + struct command *cmd; + size_t num_outstanding_requests; + const char **leakers; +}; + +/* Start a leak request: decrements num_outstanding_requests when freed. */ +void start_leak_request(const struct subd_req *req, + struct leak_detect *leak_detect); + +/* Yep, found a leak in this subd. */ +void report_subd_memleak(struct leak_detect *leak_detect, struct subd *leaker); #endif /* LIGHTNING_LIGHTNINGD_MEMDUMP_H */ diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index d8d39c20816d..5a6ac26f3078 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -201,89 +201,3 @@ void handle_reestablish(struct lightningd *ld, tal_free(peer_fd); } } - -#if DEVELOPER - /* Indented to avoid include ordering check */ - #include - -static void opening_died_forget_memleak(struct subd *open_daemon, - struct command *cmd) -{ - /* FIXME: We ignore the remaining opening daemons in this case. */ - opening_memleak_done(cmd, NULL); -} - -/* Mutual recursion */ -static void opening_memleak_req_next(struct command *cmd, struct peer *prev); -static void opening_memleak_req_done(struct subd *open_daemon, - const u8 *msg, const int *fds UNUSED, - struct command *cmd) -{ - bool found_leak; - struct peer *p; - - p = ((struct uncommitted_channel *)open_daemon->channel)->peer; - - tal_del_destructor2(open_daemon, opening_died_forget_memleak, cmd); - if (!fromwire_openingd_dev_memleak_reply(msg, &found_leak)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Bad opening_dev_memleak")); - return; - } - - if (found_leak) { - opening_memleak_done(cmd, open_daemon); - return; - } - opening_memleak_req_next(cmd, p); -} - -static void opening_memleak_req_next(struct command *cmd, struct peer *prev) -{ - struct peer *p; - struct channel *c; - u8 *msg; - - list_for_each(&cmd->ld->peers, p, list) { - struct subd *open_daemon; - c = NULL; - - if (!p->uncommitted_channel - && !(c = peer_unsaved_channel(p))) - continue; - - if (p == prev) { - prev = NULL; - continue; - } - if (prev != NULL) - continue; - - if (c) - open_daemon = c->owner; - else - open_daemon = p->uncommitted_channel->open_daemon; - - if (!open_daemon) - continue; - - /* FIXME: dualopend doesn't support memleak when we ask */ - if (streq(open_daemon->name, "dualopend")) - continue; - - msg = towire_openingd_dev_memleak(NULL); - subd_req(p, open_daemon, take(msg), -1, 0, - opening_memleak_req_done, cmd); - /* Just in case it dies before replying! */ - tal_add_destructor2(open_daemon, - opening_died_forget_memleak, cmd); - return; - } - opening_memleak_done(cmd, NULL); -} - -void opening_dev_memleak(struct command *cmd) -{ - opening_memleak_req_next(cmd, NULL); -} -#endif /* DEVELOPER */ diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index e550ce0190c9..021532493678 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -127,10 +127,4 @@ void handle_reestablish(struct lightningd *ld, const u8 *reestablish, struct peer_fd *peer_fd); -#if DEVELOPER -struct command; -/* Calls report_leak_info() async. */ -void opening_dev_memleak(struct command *cmd); -#endif - #endif /* LIGHTNING_LIGHTNINGD_OPENING_COMMON_H */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 20d4c238efe2..a742b5a4b719 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -2323,104 +2324,76 @@ static const struct json_command dev_forget_channel_command = { }; AUTODATA(json_command, &dev_forget_channel_command); -static void subd_died_forget_memleak(struct subd *openingd, struct command *cmd) +static void channeld_memleak_req_done(struct subd *channeld, + const u8 *msg, const int *fds UNUSED, + struct leak_detect *leaks) { - /* FIXME: We ignore the remaining per-peer daemons in this case. */ - peer_memleak_done(cmd, NULL); -} + bool found_leak; -/* Mutual recursion */ -static void peer_memleak_req_next(struct command *cmd, struct channel *prev); -static void peer_memleak_req_done(struct subd *subd, bool found_leak, - struct command *cmd) -{ - struct channel *c = subd->channel; + if (!fromwire_channeld_dev_memleak_reply(msg, &found_leak)) + fatal("Bad channel_dev_memleak"); if (found_leak) - peer_memleak_done(cmd, subd); - else - peer_memleak_req_next(cmd, c); + report_subd_memleak(leaks, channeld); } -static void channeld_memleak_req_done(struct subd *channeld, +static void onchaind_memleak_req_done(struct subd *onchaind, const u8 *msg, const int *fds UNUSED, - struct command *cmd) + struct leak_detect *leaks) { bool found_leak; - tal_del_destructor2(channeld, subd_died_forget_memleak, cmd); - if (!fromwire_channeld_dev_memleak_reply(msg, &found_leak)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Bad channel_dev_memleak")); - return; - } - peer_memleak_req_done(channeld, found_leak, cmd); + if (!fromwire_onchaind_dev_memleak_reply(msg, &found_leak)) + fatal("Bad onchaind_dev_memleak"); + + if (found_leak) + report_subd_memleak(leaks, onchaind); } -static void onchaind_memleak_req_done(struct subd *onchaind, - const u8 *msg, const int *fds UNUSED, - struct command *cmd) +static void openingd_memleak_req_done(struct subd *open_daemon, + const u8 *msg, const int *fds UNUSED, + struct leak_detect *leaks) { bool found_leak; - tal_del_destructor2(onchaind, subd_died_forget_memleak, cmd); - if (!fromwire_onchaind_dev_memleak_reply(msg, &found_leak)) { - was_pending(command_fail(cmd, LIGHTNINGD, - "Bad onchain_dev_memleak")); - return; - } - peer_memleak_req_done(onchaind, found_leak, cmd); + if (!fromwire_openingd_dev_memleak_reply(msg, &found_leak)) + fatal("Bad opening_dev_memleak"); + + if (found_leak) + report_subd_memleak(leaks, open_daemon); } -static void peer_memleak_req_next(struct command *cmd, struct channel *prev) +void peer_dev_memleak(struct lightningd *ld, struct leak_detect *leaks) { struct peer *p; - list_for_each(&cmd->ld->peers, p, list) { + list_for_each(&ld->peers, p, list) { struct channel *c; + if (p->uncommitted_channel) { + struct subd *openingd = p->uncommitted_channel->open_daemon; + start_leak_request(subd_req(openingd, openingd, + take(towire_openingd_dev_memleak(NULL)), + -1, 0, openingd_memleak_req_done, leaks), + leaks); + } list_for_each(&p->channels, c, list) { - if (c == prev) { - prev = NULL; - continue; - } - if (!c->owner) continue; - - if (prev != NULL) - continue; - - /* Note: closingd and dualopend do their own - * checking automatically */ - if (channel_unsaved(c)) - continue; - if (streq(c->owner->name, "channeld")) { - subd_req(c, c->owner, + start_leak_request(subd_req(c, c->owner, take(towire_channeld_dev_memleak(NULL)), - -1, 0, channeld_memleak_req_done, cmd); - tal_add_destructor2(c->owner, - subd_died_forget_memleak, - cmd); - return; - } - if (streq(c->owner->name, "onchaind")) { - subd_req(c, c->owner, + -1, 0, channeld_memleak_req_done, leaks), + leaks); + } else if (streq(c->owner->name, "onchaind")) { + start_leak_request(subd_req(c, c->owner, take(towire_onchaind_dev_memleak(NULL)), - -1, 0, onchaind_memleak_req_done, cmd); - tal_add_destructor2(c->owner, - subd_died_forget_memleak, - cmd); - return; + -1, 0, onchaind_memleak_req_done, leaks), + leaks); } + /* FIXME: dualopend doesn't support memleak + * when we ask */ } } - peer_memleak_done(cmd, NULL); -} - -void peer_dev_memleak(struct command *cmd) -{ - peer_memleak_req_next(cmd, NULL); } #endif /* DEVELOPER */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index fc06b3d0c222..1da2abcc6e50 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -94,7 +94,8 @@ struct amount_msat channel_amount_receivable(const struct channel *channel); struct htlc_in_map *load_channels_from_wallet(struct lightningd *ld); #if DEVELOPER -void peer_dev_memleak(struct command *cmd); +struct leak_detect; +void peer_dev_memleak(struct lightningd *ld, struct leak_detect *leaks); #endif /* DEVELOPER */ /* Triggered at each new block. */ diff --git a/lightningd/subd.c b/lightningd/subd.c index 44bd85f3a2ea..308f18aa3a0e 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -138,11 +138,11 @@ static void disable_cb(void *disabler UNUSED, struct subd_req *sr) sr->disabler = NULL; } -static void add_req(const tal_t *ctx, - struct subd *sd, int type, size_t num_fds_in, - void (*replycb)(struct subd *, const u8 *, const int *, - void *), - void *replycb_data) +static struct subd_req *add_req(const tal_t *ctx, + struct subd *sd, int type, size_t num_fds_in, + void (*replycb)(struct subd *, const u8 *, const int *, + void *), + void *replycb_data) { struct subd_req *sr = tal(sd, struct subd_req); @@ -164,6 +164,8 @@ static void add_req(const tal_t *ctx, /* Keep in FIFO order: we sent in order, so replies will be too. */ list_add_tail(&sd->reqs, &sr->list); tal_add_destructor(sr, destroy_subd_req); + + return sr; } /* Caller must free. */ @@ -840,12 +842,12 @@ void subd_send_fd(struct subd *sd, int fd) msg_enqueue_fd(sd->outq, fd); } -void subd_req_(const tal_t *ctx, - struct subd *sd, - const u8 *msg_out, - int fd_out, size_t num_fds_in, - void (*replycb)(struct subd *, const u8 *, const int *, void *), - void *replycb_data) +struct subd_req *subd_req_(const tal_t *ctx, + struct subd *sd, + const u8 *msg_out, + int fd_out, size_t num_fds_in, + void (*replycb)(struct subd *, const u8 *, const int *, void *), + void *replycb_data) { /* Grab type now in case msg_out is taken() */ int type = fromwire_peektype(msg_out); @@ -854,7 +856,7 @@ void subd_req_(const tal_t *ctx, if (fd_out >= 0) subd_send_fd(sd, fd_out); - add_req(ctx, sd, type, num_fds_in, replycb, replycb_data); + return add_req(ctx, sd, type, num_fds_in, replycb, replycb_data); } /* SIGALRM terminates by default: we just want it to interrupt waitpid(), diff --git a/lightningd/subd.h b/lightningd/subd.h index d721b14a1440..e3f087579dbe 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -190,7 +190,7 @@ void subd_send_fd(struct subd *sd, int fd); struct subd *, \ const u8 *, const int *), \ (replycb_data)) -void subd_req_(const tal_t *ctx, +struct subd_req *subd_req_(const tal_t *ctx, struct subd *sd, const u8 *msg_out, int fd_out, size_t num_fds_in, diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 1a8ddc3d8c9d..51cc8a4202d2 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -222,6 +222,9 @@ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct n /* Generated stub for fromwire_onchaind_dev_memleak_reply */ bool fromwire_onchaind_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_onchaind_dev_memleak_reply called!\n"); abort(); } +/* Generated stub for fromwire_openingd_dev_memleak_reply */ +bool fromwire_openingd_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) +{ fprintf(stderr, "fromwire_openingd_dev_memleak_reply called!\n"); abort(); } /* Generated stub for get_block_height */ u32 get_block_height(const struct chain_topology *topo UNNEEDED) { fprintf(stderr, "get_block_height called!\n"); abort(); } @@ -558,9 +561,6 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name /* Generated stub for peer_active_channel */ struct channel *peer_active_channel(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_active_channel called!\n"); abort(); } -/* Generated stub for peer_memleak_done */ -void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED) -{ fprintf(stderr, "peer_memleak_done called!\n"); abort(); } /* Generated stub for peer_normal_channel */ struct channel *peer_normal_channel(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_normal_channel called!\n"); abort(); } @@ -594,12 +594,19 @@ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook void plugin_request_send(struct plugin *plugin UNNEEDED, struct jsonrpc_request *req TAKES UNNEEDED) { fprintf(stderr, "plugin_request_send called!\n"); abort(); } +/* Generated stub for report_subd_memleak */ +void report_subd_memleak(struct leak_detect *leak_detect UNNEEDED, struct subd *leaker UNNEEDED) +{ fprintf(stderr, "report_subd_memleak called!\n"); abort(); } /* Generated stub for resolve_close_command */ void resolve_close_command(struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, bool cooperative UNNEEDED) { fprintf(stderr, "resolve_close_command called!\n"); abort(); } +/* Generated stub for start_leak_request */ +void start_leak_request(const struct subd_req *req UNNEEDED, + struct leak_detect *leak_detect UNNEEDED) +{ fprintf(stderr, "start_leak_request called!\n"); abort(); } /* Generated stub for subd_req_ */ -void subd_req_(const tal_t *ctx UNNEEDED, +struct subd_req *subd_req_(const tal_t *ctx UNNEEDED, struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED, int fd_out UNNEEDED, size_t num_fds_in UNNEEDED, @@ -647,6 +654,9 @@ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) /* Generated stub for towire_onchaind_dev_memleak */ u8 *towire_onchaind_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_onchaind_dev_memleak called!\n"); abort(); } +/* Generated stub for towire_openingd_dev_memleak */ +u8 *towire_openingd_dev_memleak(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "towire_openingd_dev_memleak called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 7da3e5e411ee..7b2e30ec6371 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -163,6 +163,9 @@ bool fromwire_hsmd_sign_commitment_tx_reply(const void *p UNNEEDED, struct bitco /* Generated stub for fromwire_onchaind_dev_memleak_reply */ bool fromwire_onchaind_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_onchaind_dev_memleak_reply called!\n"); abort(); } +/* Generated stub for fromwire_openingd_dev_memleak_reply */ +bool fromwire_openingd_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) +{ fprintf(stderr, "fromwire_openingd_dev_memleak_reply called!\n"); abort(); } /* Generated stub for get_block_height */ u32 get_block_height(const struct chain_topology *topo UNNEEDED) { fprintf(stderr, "get_block_height called!\n"); abort(); } @@ -609,9 +612,6 @@ void payment_store(struct lightningd *ld UNNEEDED, struct wallet_payment *paymen void payment_succeeded(struct lightningd *ld UNNEEDED, struct htlc_out *hout UNNEEDED, const struct preimage *rval UNNEEDED) { fprintf(stderr, "payment_succeeded called!\n"); abort(); } -/* Generated stub for peer_memleak_done */ -void peer_memleak_done(struct command *cmd UNNEEDED, struct subd *leaker UNNEEDED) -{ fprintf(stderr, "peer_memleak_done called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ void peer_restart_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED, @@ -645,6 +645,9 @@ struct route_step *process_onionpacket( bool has_realm ) { fprintf(stderr, "process_onionpacket called!\n"); abort(); } +/* Generated stub for report_subd_memleak */ +void report_subd_memleak(struct leak_detect *leak_detect UNNEEDED, struct subd *leaker UNNEEDED) +{ fprintf(stderr, "report_subd_memleak called!\n"); abort(); } /* Generated stub for resolve_close_command */ void resolve_close_command(struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, bool cooperative UNNEEDED) @@ -654,11 +657,15 @@ u8 *serialize_onionpacket( const tal_t *ctx UNNEEDED, const struct onionpacket *packet UNNEEDED) { fprintf(stderr, "serialize_onionpacket called!\n"); abort(); } +/* Generated stub for start_leak_request */ +void start_leak_request(const struct subd_req *req UNNEEDED, + struct leak_detect *leak_detect UNNEEDED) +{ fprintf(stderr, "start_leak_request called!\n"); abort(); } /* Generated stub for subd_release_channel */ void subd_release_channel(struct subd *owner UNNEEDED, const void *channel UNNEEDED) { fprintf(stderr, "subd_release_channel called!\n"); abort(); } /* Generated stub for subd_req_ */ -void subd_req_(const tal_t *ctx UNNEEDED, +struct subd_req *subd_req_(const tal_t *ctx UNNEEDED, struct subd *sd UNNEEDED, const u8 *msg_out UNNEEDED, int fd_out UNNEEDED, size_t num_fds_in UNNEEDED, @@ -763,6 +770,9 @@ u8 *towire_onchaind_dev_memleak(const tal_t *ctx UNNEEDED) /* Generated stub for towire_onchaind_known_preimage */ u8 *towire_onchaind_known_preimage(const tal_t *ctx UNNEEDED, const struct preimage *preimage UNNEEDED) { fprintf(stderr, "towire_onchaind_known_preimage called!\n"); abort(); } +/* Generated stub for towire_openingd_dev_memleak */ +u8 *towire_openingd_dev_memleak(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "towire_openingd_dev_memleak called!\n"); abort(); } /* Generated stub for towire_permanent_channel_failure */ u8 *towire_permanent_channel_failure(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_permanent_channel_failure called!\n"); abort(); } From 84e0e743eb00b0414f12008689bd466f37925aa0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 8 Mar 2022 11:01:17 +1030 Subject: [PATCH 0421/1530] dualopend: fix memleak reports. Next patch re-enables runtime leak checking for dualopend, so fix those leak reports. In some cases, this menas allocating off tmpctx or state->channel (which gets reset on failure), not state. The problem with tmpctx is that there are event loops in the *middle* of some functions, which free it. So for RBF functions we use a rbf_ctx temporary (with leak detection suppressed, like it is for tmpctx), then be careful to free it on all exits! Signed-off-by: Rusty Russell --- openingd/dualopend.c | 99 +++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 18e20d5617b5..00017d4c00ea 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1091,7 +1091,7 @@ static bool send_next(struct state *state, tx_state->changeset = psbt_get_changeset(tx_state, *psbt, updated_psbt); /* We want this old psbt to be cleaned up when the changeset is freed */ - tal_steal(tx_state->changeset, *psbt); + tal_steal(tx_state->changeset, notleak(*psbt)); *psbt = tal_steal(tx_state, updated_psbt); msg = psbt_changeset_get_next(tmpctx, &state->channel_id, tx_state->changeset); @@ -1373,7 +1373,7 @@ static bool run_tx_interactive(struct state *state, /* Convert tx_bytes to a tx! */ len = tal_bytelen(tx_bytes); - tx = pull_bitcoin_tx(state, &tx_bytes, &len); + tx = pull_bitcoin_tx(tmpctx, &tx_bytes, &len); if (!tx || len != 0) open_err_warn(state, "%s", "Invalid tx sent."); @@ -1448,7 +1448,6 @@ static bool run_tx_interactive(struct state *state, outpoint.n, &asset); } - psbt_input_set_serial_id(psbt, in, serial_id); break; @@ -1719,7 +1718,7 @@ static u8 *accepter_commits(struct state *state, /* Find the funding transaction txid */ psbt_txid(NULL, tx_state->psbt, &tx_state->funding.txid, NULL); - wscript = bitcoin_redeem_2of2(state, + wscript = bitcoin_redeem_2of2(tmpctx, &state->our_funding_pubkey, &state->their_funding_pubkey); @@ -1771,9 +1770,6 @@ static u8 *accepter_commits(struct state *state, "Overflow converting accepter_funding " "to msats"); - if (state->channel) - state->channel = tal_free(state->channel); - type = default_channel_type(NULL, state->our_features, state->their_features); @@ -1798,6 +1794,7 @@ static u8 *accepter_commits(struct state *state, status_failed(STATUS_FAIL_HSM_IO, "Bad ready_channel_reply %s", tal_hex(tmpctx, msg)); + tal_free(state->channel); state->channel = new_initial_channel(state, &state->channel_id, &tx_state->funding, @@ -1822,7 +1819,7 @@ static u8 *accepter_commits(struct state *state, OPT_LARGE_CHANNELS), REMOTE); - local_commit = initial_channel_tx(state, &wscript, state->channel, + local_commit = initial_channel_tx(tmpctx, &wscript, state->channel, &state->first_per_commitment_point[LOCAL], LOCAL, NULL, &error); @@ -1880,7 +1877,7 @@ static u8 *accepter_commits(struct state *state, } /* Create commitment tx signatures for remote */ - remote_commit = initial_channel_tx(state, &wscript, state->channel, + remote_commit = initial_channel_tx(tmpctx, &wscript, state->channel, &state->first_per_commitment_point[REMOTE], REMOTE, direct_outputs, &error); @@ -1950,6 +1947,7 @@ static u8 *accepter_commits(struct state *state, take(towire_commitment_signed(NULL, &state->channel_id, &local_sig.s, NULL))); + tal_free(local_commit); return msg; } @@ -2251,7 +2249,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) } /* If we have an upfront shutdown script, send it to our peer */ - struct tlv_accept_tlvs *a_tlv = tlv_accept_tlvs_new(state); + struct tlv_accept_tlvs *a_tlv = tlv_accept_tlvs_new(tmpctx); if (!state->upfront_shutdown_script[LOCAL]) state->upfront_shutdown_script[LOCAL] = no_upfront_shutdown_script(state, @@ -2439,6 +2437,7 @@ static u8 *opener_commits(struct state *state, status_failed(STATUS_FAIL_HSM_IO, "Bad ready_channel_reply %s", tal_hex(tmpctx, msg)); + tal_free(state->channel); state->channel = new_initial_channel(state, &cid, &tx_state->funding, @@ -2462,7 +2461,7 @@ static u8 *opener_commits(struct state *state, /* Opener is local */ LOCAL); - remote_commit = initial_channel_tx(state, &wscript, + remote_commit = initial_channel_tx(state->channel, &wscript, state->channel, &state->first_per_commitment_point[REMOTE], REMOTE, direct_outputs, &error); @@ -2473,7 +2472,10 @@ static u8 *opener_commits(struct state *state, revert_channel_state(state); return NULL; } - + /* These can look like a leak, but we free them in revert_channel_state + * or manually below */ + notleak_with_children(remote_commit); + tal_steal(remote_commit, wscript); /* We ask the HSM to sign their commitment transaction for us: it knows * our funding key, it just needs the remote funding key to create the @@ -2528,7 +2530,7 @@ static u8 *opener_commits(struct state *state, return NULL; } - local_commit = initial_channel_tx(state, &wscript, state->channel, + local_commit = initial_channel_tx(tmpctx, &wscript, state->channel, &state->first_per_commitment_point[LOCAL], LOCAL, NULL, &error); @@ -2588,11 +2590,13 @@ static u8 *opener_commits(struct state *state, } if (direct_outputs[LOCAL]) - pbase = penalty_base_new(state, 0, remote_commit, + pbase = penalty_base_new(tmpctx, 0, remote_commit, direct_outputs[LOCAL]); else pbase = NULL; + tal_free(remote_commit); + peer_billboard(false, "channel open: commitment received, " "sending to lightningd to save"); @@ -2652,7 +2656,7 @@ static void opener_start(struct state *state, u8 *msg) state->our_role = TX_INITIATOR; tx_state->tx_locktime = tx_state->psbt->tx->locktime; - open_tlv = tlv_opening_tlvs_new(state); + open_tlv = tlv_opening_tlvs_new(tmpctx); /* BOLT-* #2 * If the peer's revocation basepoint is unknown (e.g. @@ -2788,7 +2792,7 @@ static void opener_start(struct state *state, u8 *msg) /* If we've requested funds and they've failed to provide * to lease us (or give them to us for free?!) then we fail. * This isn't spec'd but it makes the UX predictable */ - if (open_tlv->request_funds + if (!amount_sat_zero(requested_sats) && amount_sat_less(tx_state->accepter_funding, requested_sats)) negotiation_failed(state, "We requested %s, which is more" @@ -2807,7 +2811,7 @@ static void opener_start(struct state *state, u8 *msg) * - if they decide to accept the offer: * - MUST include a `will_fund` tlv */ - if (open_tlv->request_funds && a_tlv->will_fund) { + if (!amount_sat_zero(requested_sats) && a_tlv->will_fund) { char *err_msg; struct lease_rates *rates = &a_tlv->will_fund->lease_rates; @@ -2985,6 +2989,7 @@ static bool check_funding_feerate(u32 proposed_next_feerate, return next_min <= proposed_next_feerate; } +/* Takes ownership of tx_state if successful */ static void rbf_wrap_up(struct state *state, struct tx_state *tx_state, struct amount_sat total) @@ -3012,7 +3017,6 @@ static void rbf_wrap_up(struct state *state, if (!send_next(state, tx_state, &tx_state->psbt)) { open_err_warn(state, "Peer error, has no tx updates."); - tal_free(tx_state); return; } } @@ -3020,7 +3024,6 @@ static void rbf_wrap_up(struct state *state, if (!run_tx_interactive(state, tx_state, &tx_state->psbt, state->our_role)) { - tal_free(tx_state); return; } @@ -3033,7 +3036,6 @@ static void rbf_wrap_up(struct state *state, if (!fromwire_dualopend_fail(msg, msg, &err_reason)) master_badmsg(msg_type, msg); open_err_warn(state, "%s", err_reason); - tal_free(tx_state); return; } @@ -3056,14 +3058,12 @@ static void rbf_wrap_up(struct state *state, open_err_warn(state, "%s", "Unable to commit"); /* We need to 'reset' the channel to what it * was before we did this. */ - - tal_free(tx_state); return; } /* Promote tx_state */ tal_free(state->tx_state); - state->tx_state = tx_state; + state->tx_state = tal_steal(state, tx_state); if (state->our_role == TX_ACCEPTER) handle_send_tx_sigs(state, msg); @@ -3077,15 +3077,17 @@ static void rbf_local_start(struct state *state, u8 *msg) struct channel_id cid; struct amount_sat total; char *err_reason; + /* tmpctx gets cleaned midway, so we have a context for this fn */ + char *rbf_ctx = notleak_with_children(tal(state, char)); /* We need a new tx_state! */ - tx_state = new_tx_state(state); + tx_state = new_tx_state(rbf_ctx); /* Copy over the channel config info -- everything except * the reserve will be the same */ tx_state->localconf = state->tx_state->localconf; tx_state->remoteconf = state->tx_state->remoteconf; - if (!fromwire_dualopend_rbf_init(state, msg, + if (!fromwire_dualopend_rbf_init(tx_state, msg, state->our_role == TX_INITIATOR ? &tx_state->opener_funding : &tx_state->accepter_funding, @@ -3099,7 +3101,7 @@ static void rbf_local_start(struct state *state, u8 *msg) state->tx_state->feerate_per_kw_funding)) { open_err_warn(state, "Proposed funding feerate (%u) invalid", tx_state->feerate_per_kw_funding); - return; + goto free_rbf_ctx; } /* Have you sent us everything we need yet ? */ @@ -3109,8 +3111,7 @@ static void rbf_local_start(struct state *state, u8 *msg) open_err_warn(state, "%s", "Still waiting for remote funding sigs" " for last open attempt"); - tal_free(tx_state); - return; + goto free_rbf_ctx; } tx_state->tx_locktime = tx_state->psbt->tx->locktime; @@ -3127,8 +3128,7 @@ static void rbf_local_start(struct state *state, u8 *msg) msg = opening_negotiate_msg(tmpctx, state); if (!msg) { open_err_warn(state, "%s", "Unable to init rbf"); - tal_free(tx_state); - return; + goto free_rbf_ctx; } if (!fromwire_ack_rbf(msg, &cid, @@ -3150,8 +3150,7 @@ static void rbf_local_start(struct state *state, u8 *msg) &tx_state->accepter_funding), type_to_string(tmpctx, struct amount_sat, &tx_state->opener_funding)); - tal_free(tx_state); - return; + goto free_rbf_ctx; } /* Check that total funding doesn't exceed allowed channel capacity */ /* BOLT #2: @@ -3168,8 +3167,7 @@ static void rbf_local_start(struct state *state, u8 *msg) type_to_string(tmpctx, struct amount_sat, &total)); - tal_free(tx_state); - return; + goto free_rbf_ctx; } /* Now that we know the total of the channel, we can set the reserve */ @@ -3185,12 +3183,14 @@ static void rbf_local_start(struct state *state, u8 *msg) true, /* v2 means we use anchor outputs */ &err_reason)) { open_err_warn(state, "%s", err_reason); - tal_free(tx_state); - return; + goto free_rbf_ctx; } /* We merge with RBF's we've initiated now */ rbf_wrap_up(state, tx_state, total); + +free_rbf_ctx: + tal_free(rbf_ctx); } static void rbf_remote_start(struct state *state, const u8 *rbf_msg) @@ -3201,9 +3201,11 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) struct amount_sat total; enum dualopend_wire msg_type; u8 *msg; + /* tmpctx gets cleaned midway, so we have a context for this fn */ + char *rbf_ctx = notleak_with_children(tal(state, char)); /* We need a new tx_state! */ - tx_state = new_tx_state(state); + tx_state = new_tx_state(rbf_ctx); if (!fromwire_init_rbf(rbf_msg, &cid, state->our_role == TX_INITIATOR ? @@ -3242,8 +3244,7 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) "Proposed %u, last feerate %u", tx_state->feerate_per_kw_funding, state->tx_state->feerate_per_kw_funding); - tal_free(tx_state); - return; + goto free_rbf_ctx; } /* We ask master if this is ok */ @@ -3262,8 +3263,7 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) if (!fromwire_dualopend_fail(msg, msg, &err_reason)) master_badmsg(msg_type, msg); open_err_warn(state, "%s", err_reason); - tal_free(tx_state); - return; + goto free_rbf_ctx; } if (!fromwire_dualopend_got_rbf_offer_reply(state, msg, @@ -3286,8 +3286,7 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) &tx_state->accepter_funding), type_to_string(tmpctx, struct amount_sat, &tx_state->opener_funding)); - tal_free(tx_state); - return; + goto free_rbf_ctx; } /* Check that total funding doesn't exceed allowed channel capacity */ @@ -3305,8 +3304,7 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) type_to_string(tmpctx, struct amount_sat, &total)); - tal_free(tx_state); - return; + goto free_rbf_ctx; } /* Now that we know the total of the channel, we can set the reserve */ @@ -3322,8 +3320,7 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) true, /* v2 means we use anchor outputs */ &err_reason)) { open_err_warn(state, "%s", err_reason); - tal_free(tx_state); - return; + goto free_rbf_ctx; } msg = towire_ack_rbf(tmpctx, &state->channel_id, @@ -3335,6 +3332,9 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) /* We merge with RBF's we've initiated now */ rbf_wrap_up(state, tx_state, total); + +free_rbf_ctx: + tal_free(rbf_ctx); } static void hsm_per_commitment_point(u64 index, struct pubkey *point) @@ -3868,6 +3868,11 @@ int main(int argc, char *argv[]) /* We can pull the commitment feerate out of the feestates */ state->feerate_per_kw_commitment = get_feerate(fee_states, opener, LOCAL); + + /* No longer need this */ + tal_free(fee_states); + /* This psbt belongs to tx_state, not state! */ + tal_steal(state->tx_state, state->tx_state->psbt); } else master_badmsg(fromwire_peektype(msg), msg); From d7ffb712e5f50f7e14b8b6533119c02eadfd4a49 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 8 Mar 2022 11:01:26 +1030 Subject: [PATCH 0422/1530] dualopend: restore memleak calls. And implement a timeout (20 seconds) just in case it's not listening. Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 2 ++ lightningd/memdump.c | 27 ++++++++++++++++-- lightningd/peer_control.c | 21 ++++++++++++-- lightningd/test/run-invoice-select-inchan.c | 6 ++++ openingd/dualopend.c | 31 +++++++++++++++------ openingd/dualopend_wire.csv | 6 ++++ wallet/test/run-wallet.c | 6 ++++ 7 files changed, 85 insertions(+), 14 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 9b3a05ad8cf9..fb7df13c7bd5 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -3026,6 +3026,8 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_SEND_TX_SIGS: case WIRE_DUALOPEND_SEND_SHUTDOWN: case WIRE_DUALOPEND_DEPTH_REACHED: + case WIRE_DUALOPEND_DEV_MEMLEAK: + case WIRE_DUALOPEND_DEV_MEMLEAK_REPLY: break; } diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 02ffe2102ce0..226341a90896 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -129,9 +129,19 @@ static void finish_report(const struct leak_detect *leaks) struct htable *memtable; const tal_t *i; const uintptr_t *backtrace; - struct command *cmd = leaks->cmd; - struct lightningd *ld = cmd->ld; - struct json_stream *response = json_stream_success(cmd); + struct command *cmd; + struct lightningd *ld; + struct json_stream *response; + + /* If it timed out, we free ourselved and exit! */ + if (!leaks->cmd) { + tal_free(leaks); + return; + } + + /* Convenience variables */ + cmd = leaks->cmd; + ld = cmd->ld; /* Enter everything, except this cmd and its jcon */ memtable = memleak_find_allocations(cmd, cmd, cmd->jcon); @@ -146,6 +156,7 @@ static void finish_report(const struct leak_detect *leaks) /* Now delete ld and those which it has pointers to. */ memleak_remove_region(memtable, ld, sizeof(*ld)); + response = json_stream_success(cmd); json_array_start(response, "leaks"); while ((i = memleak_get(memtable, &backtrace)) != NULL) { const tal_t *p; @@ -176,6 +187,12 @@ static void finish_report(const struct leak_detect *leaks) was_pending(command_success(cmd, response)); } +static void leak_detect_timeout(struct leak_detect *leak_detect) +{ + finish_report(leak_detect); + leak_detect->cmd = NULL; +} + static void leak_detect_req_done(const struct subd_req *req, struct leak_detect *leak_detect) { @@ -274,6 +291,10 @@ static struct command_result *json_memleak(struct command *cmd, /* Ask all per-peer daemons */ peer_dev_memleak(ld, leaks); + + /* Set timer: dualopend doesn't always listen! */ + notleak(new_reltimer(ld->timers, leaks, time_from_sec(20), + leak_detect_timeout, leaks)); return command_still_pending(cmd); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index a742b5a4b719..5c4d43a34b1b 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -2363,6 +2364,19 @@ static void openingd_memleak_req_done(struct subd *open_daemon, report_subd_memleak(leaks, open_daemon); } +static void dualopend_memleak_req_done(struct subd *dualopend, + const u8 *msg, const int *fds UNUSED, + struct leak_detect *leaks) +{ + bool found_leak; + + if (!fromwire_dualopend_dev_memleak_reply(msg, &found_leak)) + fatal("Bad dualopend_dev_memleak"); + + if (found_leak) + report_subd_memleak(leaks, dualopend); +} + void peer_dev_memleak(struct lightningd *ld, struct leak_detect *leaks) { struct peer *p; @@ -2390,9 +2404,12 @@ void peer_dev_memleak(struct lightningd *ld, struct leak_detect *leaks) take(towire_onchaind_dev_memleak(NULL)), -1, 0, onchaind_memleak_req_done, leaks), leaks); + } else if (streq(c->owner->name, "dualopend")) { + start_leak_request(subd_req(c, c->owner, + take(towire_dualopend_dev_memleak(NULL)), + -1, 0, dualopend_memleak_req_done, leaks), + leaks); } - /* FIXME: dualopend doesn't support memleak - * when we ask */ } } } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 51cc8a4202d2..7796b2f87f3b 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -207,6 +207,9 @@ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNE /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } +/* Generated stub for fromwire_dualopend_dev_memleak_reply */ +bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) +{ fprintf(stderr, "fromwire_dualopend_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_sign_bolt12_reply */ bool fromwire_hsmd_sign_bolt12_reply(const void *p UNNEEDED, struct bip340sig *sig UNNEEDED) { fprintf(stderr, "fromwire_hsmd_sign_bolt12_reply called!\n"); abort(); } @@ -634,6 +637,9 @@ u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_bas /* 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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } +/* Generated stub for towire_dualopend_dev_memleak */ +u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "towire_dualopend_dev_memleak called!\n"); abort(); } /* Generated stub for towire_errorfmt */ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 00017d4c00ea..d5ab273b15f6 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -881,18 +881,23 @@ static bool is_segwit_output(struct wally_tx_output *output, * at closing time, rather than when it askes. */ #if DEVELOPER -static void dualopend_dev_memleak(struct state *state) +static void handle_dev_memleak(struct state *state, const u8 *msg) { struct htable *memtable; + bool found_leak; - /* Populate a hash table with all our allocations. */ - memtable = memleak_find_allocations(tmpctx, NULL, NULL); + /* Populate a hash table with all our allocations (except msg, which + * is in use right now). */ + memtable = memleak_find_allocations(tmpctx, msg, msg); /* Now delete state and things it has pointers to. */ memleak_remove_region(memtable, state, tal_bytelen(state)); /* If there's anything left, dump it to logs, and return true. */ - dump_memleak(memtable, memleak_status_broken); + found_leak = dump_memleak(memtable, memleak_status_broken); + wire_sync_write(REQ_FD, + take(towire_dualopend_dev_memleak_reply(NULL, + found_leak))); } #endif /* DEVELOPER */ @@ -1053,7 +1058,14 @@ fetch_psbt_changes(struct state *state, psbt); wire_sync_write(REQ_FD, take(msg)); + msg = wire_sync_read(tmpctx, REQ_FD); +#if DEVELOPER + while (fromwire_dualopend_dev_memleak(msg)) { + handle_dev_memleak(state, msg); + msg = wire_sync_read(tmpctx, REQ_FD); + } +#endif if (fromwire_dualopend_fail(msg, msg, &err)) { open_err_warn(state, "%s", err); @@ -3601,6 +3613,11 @@ static u8 *handle_master_in(struct state *state) enum dualopend_wire t = fromwire_peektype(msg); switch (t) { + case WIRE_DUALOPEND_DEV_MEMLEAK: +#if DEVELOPER + handle_dev_memleak(state, msg); +#endif + return NULL; case WIRE_DUALOPEND_OPENER_INIT: opener_start(state, msg); return NULL; @@ -3627,6 +3644,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY: case WIRE_DUALOPEND_RBF_VALID: case WIRE_DUALOPEND_VALIDATE_LEASE_REPLY: + case WIRE_DUALOPEND_DEV_MEMLEAK_REPLY: /* Messages we send */ case WIRE_DUALOPEND_GOT_OFFER: @@ -3949,11 +3967,6 @@ int main(int argc, char *argv[]) dualopend_wire_name(fromwire_peektype(msg))); tal_free(msg); -#if DEVELOPER - /* Now look for memory leaks. */ - dualopend_dev_memleak(state); -#endif /* DEVELOPER */ - /* This frees the entire tal tree. */ tal_free(state); daemon_shutdown(); diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 6c6543487d4e..7f49f37fe1ea 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -220,6 +220,12 @@ msgtype,dualopend_fail_fallen_behind,1028 # Shutdown is complete, ready for closing negotiation. + peer_fd & gossip_fd. msgtype,dualopend_shutdown_complete,7025 +# master -> dualopend: do you have a memleak? +msgtype,dualopend_dev_memleak,7033 + +msgtype,dualopend_dev_memleak_reply,7133 +msgdata,dualopend_dev_memleak_reply,leak,bool, + # dualopend -> master: this was a dry run, here's some info about this open msgtype,dualopend_dry_run,7026 msgdata,dualopend_dry_run,channel_id,channel_id, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 7b2e30ec6371..ef7c21d65666 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -151,6 +151,9 @@ bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void * /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } +/* Generated stub for fromwire_dualopend_dev_memleak_reply */ +bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) +{ fprintf(stderr, "fromwire_dualopend_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_hsmd_get_output_scriptpubkey_reply */ bool fromwire_hsmd_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED) { fprintf(stderr, "fromwire_hsmd_get_output_scriptpubkey_reply called!\n"); abort(); } @@ -723,6 +726,9 @@ u8 *towire_connectd_peer_disconnected(const tal_t *ctx UNNEEDED, const struct no /* 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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } +/* Generated stub for towire_dualopend_dev_memleak */ +u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "towire_dualopend_dev_memleak called!\n"); abort(); } /* Generated stub for towire_errorfmt */ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, From c0c826d2ee106a1e450f7b89ad163ebe629c2662 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 4 Jan 2022 16:39:46 +0100 Subject: [PATCH 0423/1530] ci: introduce in ci the compilation testing on different os Changelog-None: introduce in ci the compilation testing on different os Signed-off-by: Vincenzo Palazzo --- .github/workflows/ci_build.yml | 17 ++++++++++++++ contrib/docker/Dockerfile.alpine | 23 +++++++++++++++++++ contrib/{ => docker}/Dockerfile.builder | 0 .../{ => docker}/Dockerfile.builder.fedora | 0 contrib/{ => docker}/Dockerfile.tester | 0 contrib/{ => docker}/linuxarm32v7.Dockerfile | 0 contrib/{ => docker}/linuxarm64v8.Dockerfile | 0 7 files changed, 40 insertions(+) create mode 100644 .github/workflows/ci_build.yml create mode 100644 contrib/docker/Dockerfile.alpine rename contrib/{ => docker}/Dockerfile.builder (100%) rename contrib/{ => docker}/Dockerfile.builder.fedora (100%) rename contrib/{ => docker}/Dockerfile.tester (100%) rename contrib/{ => docker}/linuxarm32v7.Dockerfile (100%) rename contrib/{ => docker}/linuxarm64v8.Dockerfile (100%) diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml new file mode 100644 index 000000000000..7683c539d4ae --- /dev/null +++ b/.github/workflows/ci_build.yml @@ -0,0 +1,17 @@ +name: CI Compilation testing + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - { OS: alpine } + steps: + - uses: actions/checkout@v2 + - name: Integration testing + run: | + docker build -f contrib/docker/Dockerfile.${{matrix.OS}} -t clightning-${{matrix.OS}} . diff --git a/contrib/docker/Dockerfile.alpine b/contrib/docker/Dockerfile.alpine new file mode 100644 index 000000000000..d1ab045f9f05 --- /dev/null +++ b/contrib/docker/Dockerfile.alpine @@ -0,0 +1,23 @@ +FROM alpine:3.14.3 +LABEL org.opencontainers.image.authors="Vincenzo Palazzo (@vincenzopalazzo) vincenzopalazzodev@gmail.com" + +WORKDIR /build + +RUN apk update && \ + apk add ca-certificates alpine-sdk autoconf automake git libtool \ + gmp-dev sqlite-dev python3 py3-mako net-tools zlib-dev libsodium gettext su-exec \ + python3 py3-pip #&& \ + #apk add --upgrade fortify-headers + +RUN mkdir lightning +COPY . lightning + +RUN cd lightning && \ + git submodule update --init --recursive && \ + ./configure && \ + pip3 install mrkd mistune==0.8.4 && \ + make -j$(nproc) && \ + make install + +# TODO: review entry point here, to make this availale for the user +CMD ["lightningd", "--version"] diff --git a/contrib/Dockerfile.builder b/contrib/docker/Dockerfile.builder similarity index 100% rename from contrib/Dockerfile.builder rename to contrib/docker/Dockerfile.builder diff --git a/contrib/Dockerfile.builder.fedora b/contrib/docker/Dockerfile.builder.fedora similarity index 100% rename from contrib/Dockerfile.builder.fedora rename to contrib/docker/Dockerfile.builder.fedora diff --git a/contrib/Dockerfile.tester b/contrib/docker/Dockerfile.tester similarity index 100% rename from contrib/Dockerfile.tester rename to contrib/docker/Dockerfile.tester diff --git a/contrib/linuxarm32v7.Dockerfile b/contrib/docker/linuxarm32v7.Dockerfile similarity index 100% rename from contrib/linuxarm32v7.Dockerfile rename to contrib/docker/linuxarm32v7.Dockerfile diff --git a/contrib/linuxarm64v8.Dockerfile b/contrib/docker/linuxarm64v8.Dockerfile similarity index 100% rename from contrib/linuxarm64v8.Dockerfile rename to contrib/docker/linuxarm64v8.Dockerfile From 9ae1f33992a3cdadbe1d4c8acb223adb87bf24b7 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Jan 2022 12:59:09 +0100 Subject: [PATCH 0424/1530] cln-plugin: Get started with the plugin interface --- Cargo.toml | 1 + plugins/Cargo.toml | 25 +++ plugins/Makefile | 10 ++ plugins/examples/cln-plugin-startup.rs | 14 ++ plugins/src/codec.rs | 207 +++++++++++++++++++++++++ plugins/src/lib.rs | 191 +++++++++++++++++++++++ plugins/src/messages.rs | 160 +++++++++++++++++++ 7 files changed, 608 insertions(+) create mode 100644 plugins/Cargo.toml create mode 100644 plugins/examples/cln-plugin-startup.rs create mode 100644 plugins/src/codec.rs create mode 100644 plugins/src/lib.rs create mode 100644 plugins/src/messages.rs diff --git a/Cargo.toml b/Cargo.toml index 288c893473c7..b597a2cef789 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,5 @@ members = [ "cln-rpc", "cln-grpc", + "plugins", ] diff --git a/plugins/Cargo.toml b/plugins/Cargo.toml new file mode 100644 index 000000000000..49fbc6db08c1 --- /dev/null +++ b/plugins/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "cln-plugin" +version = "0.1.0" +edition = "2021" + +[[example]] +name = "cln-plugin-startup" +path = "examples/cln-plugin-startup.rs" + +[dependencies] +anyhow = "1.0.51" +bytes = "1.1.0" +log = "0.4.14" +serde = { version = "1.0.131", features = ["derive"] } +serde_json = "1.0.72" +tokio-util = { version = "0.6.9", features = ["codec"] } +tokio = { version="1", features = ['io-std', 'rt'] } +tokio-stream = "*" +futures = "0.3" +cln-rpc = { path = "../cln-rpc" } + +[dev-dependencies] +tokio = { version = "1", features = ["macros", "rt-multi-thread", ] } +env_logger = "*" +cln-grpc = { path = "../cln-grpc" } diff --git a/plugins/Makefile b/plugins/Makefile index 3707e7688c69..70302311bd42 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -173,4 +173,14 @@ ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile @$(call VERBOSE,GEN $@,echo "static const char *list_of_builtin_plugins[] = { $(foreach d,$(notdir $(PLUGINS)),\"$d\",) NULL };" > $@) +CLN_PLUGIN_EXAMPLES := target/${RUST_PROFILE}/examples/cln-plugin-startup +CLN_PLUGIN_SRC = $(shell find plugins/src -name "*.rs") + +${CLN_PLUGIN_EXAMPLES}: ${CLN_PLUGIN_SRC} + (cd plugins; cargo build ${CARGO_OPTS} --examples) + +ifneq ($(RUST),0) +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) +endif + include plugins/test/Makefile diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs new file mode 100644 index 000000000000..f1c6ed26be1a --- /dev/null +++ b/plugins/examples/cln-plugin-startup.rs @@ -0,0 +1,14 @@ +//! This is a test plugin used to verify that we can compile and run +//! plugins using the Rust API against c-lightning. + +use cln_plugin::Builder; +use tokio; + +#[tokio::main] +async fn main() -> Result<(), anyhow::Error> { + env_logger::init(); + + let (plugin, stdin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()).build(); + plugin.run(stdin).await; + Ok(()) +} diff --git a/plugins/src/codec.rs b/plugins/src/codec.rs new file mode 100644 index 000000000000..e3d1a5fefd78 --- /dev/null +++ b/plugins/src/codec.rs @@ -0,0 +1,207 @@ +/// The codec is used to encode and decode messages received from and +/// sent to the main daemon. The protocol uses `stdout` and `stdin` to +/// exchange JSON formatted messages. Each message is separated by an +/// empty line and we're guaranteed that no other empty line is +/// present in the messages. +use crate::Error; +use anyhow::anyhow; +use bytes::{BufMut, BytesMut}; +use serde_json::value::Value; +use std::str::FromStr; +use std::{io, str}; +use tokio_util::codec::{Decoder, Encoder}; + +use crate::messages::{Notification, Request}; +pub use crate::messages::JsonRpc; + +/// A simple codec that parses messages separated by two successive +/// `\n` newlines. +#[derive(Default)] +pub struct MultiLineCodec {} + +/// Find two consecutive newlines, i.e., an empty line, signalling the +/// end of one message and the start of the next message. +fn find_separator(buf: &mut BytesMut) -> Option { + buf.iter() + .zip(buf.iter().skip(1)) + .position(|b| *b.0 == b'\n' && *b.1 == b'\n') +} + +fn utf8(buf: &[u8]) -> Result<&str, io::Error> { + str::from_utf8(buf) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Unable to decode input as UTF8")) +} + +impl Decoder for MultiLineCodec { + type Item = String; + type Error = Error; + fn decode(&mut self, buf: &mut BytesMut) -> Result, Error> { + if let Some(newline_offset) = find_separator(buf) { + let line = buf.split_to(newline_offset + 2); + let line = &line[..line.len() - 2]; + let line = utf8(line)?; + Ok(Some(line.to_string())) + } else { + Ok(None) + } + } +} + +impl Encoder for MultiLineCodec +where + T: AsRef, +{ + type Error = Error; + fn encode(&mut self, line: T, buf: &mut BytesMut) -> Result<(), Self::Error> { + let line = line.as_ref(); + buf.reserve(line.len() + 2); + buf.put(line.as_bytes()); + buf.put_u8(b'\n'); + buf.put_u8(b'\n'); + Ok(()) + } +} + +#[derive(Default)] +pub struct JsonCodec { + /// Sub-codec used to split the input into chunks that can then be + /// parsed by the JSON parser. + inner: MultiLineCodec, +} + +impl Encoder for JsonCodec +where + T: Into, +{ + type Error = Error; + fn encode(&mut self, msg: T, buf: &mut BytesMut) -> Result<(), Self::Error> { + let s = msg.into().to_string(); + self.inner.encode(s, buf) + } +} + +impl Decoder for JsonCodec { + type Item = Value; + type Error = Error; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, Error> { + match self.inner.decode(buf) { + Ok(None) => Ok(None), + Err(e) => Err(e), + Ok(Some(s)) => { + if let Ok(v) = Value::from_str(&s) { + Ok(Some(v)) + } else { + Err(anyhow!("failed to parse JSON")) + } + } + } + } +} + +/// A codec that reads fully formed [crate::messages::JsonRpc] +/// messages. Internally it uses the [JsonCodec] which itself is built +/// on the [MultiLineCodec]. +#[derive(Default)] +pub(crate) struct JsonRpcCodec { + inner: JsonCodec, +} + +impl Decoder for JsonRpcCodec { + type Item = JsonRpc; + type Error = Error; + + fn decode(&mut self, buf: &mut BytesMut) -> Result, Error> { + match self.inner.decode(buf) { + Ok(None) => Ok(None), + Err(e) => Err(e), + Ok(Some(s)) => { + let req: Self::Item = serde_json::from_value(s)?; + Ok(Some(req)) + } + } + } +} + +#[cfg(test)] +mod test { + use super::{find_separator, JsonCodec, MultiLineCodec}; + use bytes::{BufMut, BytesMut}; + use serde_json::json; + use tokio_util::codec::{Decoder, Encoder}; + + #[test] + fn test_separator() { + struct Test(String, Option); + let tests = vec![ + Test("".to_string(), None), + Test("}\n\n".to_string(), Some(1)), + Test("\"hello\"},\n\"world\"}\n\n".to_string(), Some(18)), + ]; + + for t in tests.iter() { + let mut buf = BytesMut::new(); + buf.put_slice(t.0.as_bytes()); + assert_eq!(find_separator(&mut buf), t.1); + } + } + + #[test] + fn test_ml_decoder() { + struct Test(String, Option, String); + let tests = vec![ + Test("".to_string(), None, "".to_string()), + Test( + "{\"hello\":\"world\"}\n\nremainder".to_string(), + Some("{\"hello\":\"world\"}".to_string()), + "remainder".to_string(), + ), + Test( + "{\"hello\":\"world\"}\n\n{}\n\nremainder".to_string(), + Some("{\"hello\":\"world\"}".to_string()), + "{}\n\nremainder".to_string(), + ), + ]; + + for t in tests.iter() { + let mut buf = BytesMut::new(); + buf.put_slice(t.0.as_bytes()); + + let mut codec = MultiLineCodec::default(); + let mut remainder = BytesMut::new(); + remainder.put_slice(t.2.as_bytes()); + + assert_eq!(codec.decode(&mut buf).unwrap(), t.1); + assert_eq!(buf, remainder); + } + } + + #[test] + fn test_ml_encoder() { + let tests = vec!["test"]; + + for t in tests.iter() { + let mut buf = BytesMut::new(); + let mut codec = MultiLineCodec::default(); + let mut expected = BytesMut::new(); + expected.put_slice(t.as_bytes()); + expected.put_u8(b'\n'); + expected.put_u8(b'\n'); + codec.encode(t, &mut buf).unwrap(); + assert_eq!(buf, expected); + } + } + + #[test] + fn test_json_codec() { + let tests = vec![json!({"hello": "world"})]; + + for t in tests.iter() { + let mut codec = JsonCodec::default(); + let mut buf = BytesMut::new(); + codec.encode(t.clone(), &mut buf).unwrap(); + let decoded = codec.decode(&mut buf).unwrap().unwrap(); + assert_eq!(&decoded, t); + } + } +} diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs new file mode 100644 index 000000000000..ca43e567bdab --- /dev/null +++ b/plugins/src/lib.rs @@ -0,0 +1,191 @@ +use crate::codec::{JsonCodec, JsonRpcCodec}; +use futures::sink::SinkExt; +use std::sync::Arc; +use tokio::sync::Mutex; +use tokio_util::codec::FramedWrite; +pub mod codec; +mod messages; +pub use anyhow::Error; +use log::{trace, warn}; +use std::marker::PhantomData; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_stream::StreamExt; +use tokio_util::codec::FramedRead; + +#[macro_use] +extern crate serde_json; + +/// Builder for a new plugin. +pub struct Builder +where + S: Clone + Send, + I: AsyncRead + Unpin, + O: Send + AsyncWrite + Unpin, +{ + state: S, + + input: I, + output: O, + + #[allow(dead_code)] + hooks: Hooks, + + #[allow(dead_code)] + subscriptions: Subscriptions, +} + +impl Builder +where + O: Send + AsyncWrite + Unpin + 'static, + S: Clone + Send + 'static, + I: AsyncRead + Send + Unpin + 'static, +{ + pub fn new(state: S, input: I, output: O) -> Self { + Self { + state, + input, + output, + hooks: Hooks::default(), + subscriptions: Subscriptions::default(), + } + } + + pub async fn run(self) -> Result<(), Error> { + let (plugin, input) = self.build(); + plugin.run(input).await + } + + pub fn build(self) -> (Plugin, I) { + ( + Plugin { + state: Arc::new(Mutex::new(self.state)), + output: Arc::new(Mutex::new(FramedWrite::new( + self.output, + JsonCodec::default(), + ))), + input_type: PhantomData, + }, + self.input, + ) + } +} + +pub struct Plugin +where + S: Clone + Send, + I: AsyncRead, + O: Send + AsyncWrite, +{ + //input: FramedRead, + output: Arc>>, + + /// The state gets cloned for each request + state: Arc>, + input_type: PhantomData, +} +impl Plugin +where + S: Clone + Send, + I: AsyncRead + Send + Unpin, + O: Send + AsyncWrite + Unpin, +{ + /// Read incoming requests from `c-lightning and dispatch their handling. + #[allow(unused_mut)] + pub async fn run(mut self, input: I) -> Result<(), Error> { + let mut input = FramedRead::new(input, JsonRpcCodec::default()); + loop { + match input.next().await { + Some(Ok(msg)) => { + trace!("Received a message: {:?}", msg); + match msg { + messages::JsonRpc::Request(id, p) => { + self.dispatch_request(id, p).await? + // Use a match to detect Ok / Error and return an error if we failed. + } + messages::JsonRpc::Notification(n) => self.dispatch_notification(n).await?, + } + } + Some(Err(e)) => { + warn!("Error reading command: {}", e); + break; + } + None => break, + } + } + Ok(()) + } + + async fn dispatch_request( + &mut self, + id: usize, + request: messages::Request, + ) -> Result<(), Error> { + trace!("Dispatching request {:?}", request); + let state = self.state.clone(); + let res: serde_json::Value = match request { + messages::Request::Getmanifest(c) => { + serde_json::to_value(Plugin::::handle_get_manifest(c, state).await?) + .unwrap() + } + messages::Request::Init(c) => { + serde_json::to_value(Plugin::::handle_init(c, state).await?).unwrap() + } + o => panic!("Request {:?} is currently unhandled", o), + }; + trace!("Sending respone {:?}", res); + + let mut out = self.output.lock().await; + out.send(json!({ + "jsonrpc": "2.0", + "result": res, + "id": id, + })) + .await + .unwrap(); + Ok(()) + } + + async fn dispatch_notification( + &mut self, + notification: messages::Notification, + ) -> Result<(), Error> { + trace!("Dispatching notification {:?}", notification); + unimplemented!() + } + + async fn handle_get_manifest( + _call: messages::GetManifestCall, + _state: Arc>, + ) -> Result { + Ok(messages::GetManifestResponse::default()) + } + + async fn handle_init( + _call: messages::InitCall, + _state: Arc>, + ) -> Result { + Ok(messages::InitResponse::default()) + } +} + +/// A container for all the configure hooks. It is just a collection +/// of callbacks that can be registered by the users of the +/// library. Based on this configuration we can then generate the +/// [`messages::GetManifestResponse`] from, populating our subscriptions +#[derive(Debug, Default)] +struct Hooks {} + +/// A container for all the configured notifications. +#[derive(Debug, Default)] +struct Subscriptions {} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn init() { + let builder = Builder::new((), tokio::io::stdin(), tokio::io::stdout()); + let plugin = builder.build(); + } +} diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs new file mode 100644 index 000000000000..fcba6ceaec8d --- /dev/null +++ b/plugins/src/messages.rs @@ -0,0 +1,160 @@ +use serde::de::{self, Deserializer}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashMap; +use std::fmt::Debug; + +#[derive(Deserialize, Debug)] +#[serde(tag = "method", content = "params")] +#[serde(rename_all = "snake_case")] +pub(crate) enum Request { + // Builtin + Getmanifest(GetManifestCall), + Init(InitCall), + + // Hooks + PeerConnected, + CommitmentRevocation, + DbWrite, + InvoicePayment, + Openchannel, + Openchannel2, + Openchannel2Changed, + Openchannel2Sign, + RbfChannel, + HtlcAccepted, + RpcCommand, + Custommsg, + OnionMessage, + OnionMessageBlinded, + OnionMessageOurpath, + + // Bitcoin backend + Getchaininfo, + Estimatefees, + Getrawblockbyheight, + Getutxout, + Sendrawtransaction, +} + +#[derive(Deserialize, Debug)] +#[serde(tag = "method", content = "params")] +#[serde(rename_all = "snake_case")] +pub(crate) enum Notification { + ChannelOpened, + ChannelOpenFailed, + ChannelStateChanged, + Connect, + Disconnect, + InvoicePayment, + InvoiceCreation, + Warning, + ForwardEvent, + SendpaySuccess, + SendpayFailure, + CoinMovement, + OpenchannelPeerSigs, + Shutdown, +} + +#[derive(Deserialize, Debug)] +pub struct GetManifestCall {} + +#[derive(Deserialize, Debug)] +pub struct InitCall { + pub options: Value, + pub configuration: HashMap, +} + +#[derive(Debug)] +pub enum JsonRpc { + Request(usize, R), + Notification(N), +} + +/// This function disentangles the various cases: +/// +/// 1) If we have an `id` then it is a request +/// +/// 2) Otherwise it's a notification that doesn't require a +/// response. +/// +/// Furthermore we distinguish between the built-in types and the +/// custom user notifications/methods: +/// +/// 1) We either match a built-in type above, +/// +/// 2) Or it's a custom one, so we pass it around just as a +/// `serde_json::Value` +impl<'de, N, R> Deserialize<'de> for JsonRpc +where + N: Deserialize<'de> + Debug, + R: Deserialize<'de> + Debug, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize, Debug)] + struct IdHelper { + id: Option, + } + + let v = Value::deserialize(deserializer)?; + let helper = IdHelper::deserialize(&v).map_err(de::Error::custom)?; + match helper.id { + Some(id) => { + let r = R::deserialize(v).map_err(de::Error::custom)?; + Ok(JsonRpc::Request(id, r)) + } + None => { + let n = N::deserialize(v).map_err(de::Error::custom)?; + Ok(JsonRpc::Notification(n)) + } + } + } +} + +use serde::ser::{SerializeStruct, Serializer}; + +impl Serialize for JsonRpc +where + N: Serialize + Debug, + R: Serialize + Debug, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + JsonRpc::Notification(r) => { + let r = serde_json::to_value(r).unwrap(); + let mut s = serializer.serialize_struct("Notification", 3)?; + s.serialize_field("jsonrpc", "2.0")?; + s.serialize_field("method", &r["method"])?; + s.serialize_field("params", &r["params"])?; + s.end() + } + JsonRpc::Request(id, r) => { + let r = serde_json::to_value(r).unwrap(); + let mut s = serializer.serialize_struct("Request", 4)?; + s.serialize_field("jsonrpc", "2.0")?; + s.serialize_field("id", id)?; + s.serialize_field("method", &r["method"])?; + s.serialize_field("params", &r["params"])?; + s.end() + } + } + } +} + +#[derive(Serialize, Default, Debug)] +pub struct GetManifestResponse { + options: Vec<()>, + rpcmethods: Vec<()>, +} + +#[derive(Serialize, Default, Debug)] +pub struct InitResponse {} + +pub trait Response: Serialize + Debug {} From f5e1829117668619cfdc5856255a5a60c4aff801 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 21 Jan 2022 14:45:16 +0100 Subject: [PATCH 0425/1530] cln-plugin: Implement logging facade adapter for cln plugins We wrap emitted messages into a JSON-RPC notification envelope and write them to stdout. We use an indirection over an mpsc channel in order to avoid deadlocks if we emit logs while holding the writer lock on stdout. --- cln-rpc/examples/getinfo.rs | 2 +- plugins/Cargo.toml | 4 +- plugins/src/lib.rs | 40 +++++++++------- plugins/src/logging.rs | 92 +++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 plugins/src/logging.rs diff --git a/cln-rpc/examples/getinfo.rs b/cln-rpc/examples/getinfo.rs index 2ca02e76b958..a6dd279894e2 100644 --- a/cln-rpc/examples/getinfo.rs +++ b/cln-rpc/examples/getinfo.rs @@ -13,6 +13,6 @@ async fn main() -> Result<(), anyhow::Error> { let mut rpc = ClnRpc::new(p).await?; let response = rpc.call(Request::Getinfo(GetinfoRequest {})).await?; - info!("{}", serde_json::to_string_pretty(&response)?); + println!("{}", serde_json::to_string_pretty(&response)?); Ok(()) } diff --git a/plugins/Cargo.toml b/plugins/Cargo.toml index 49fbc6db08c1..8acda04350fc 100644 --- a/plugins/Cargo.toml +++ b/plugins/Cargo.toml @@ -10,11 +10,11 @@ path = "examples/cln-plugin-startup.rs" [dependencies] anyhow = "1.0.51" bytes = "1.1.0" -log = "0.4.14" +log = { version = "0.4.14", features = ['std'] } serde = { version = "1.0.131", features = ["derive"] } serde_json = "1.0.72" tokio-util = { version = "0.6.9", features = ["codec"] } -tokio = { version="1", features = ['io-std', 'rt'] } +tokio = { version="1", features = ['io-std', 'rt', 'sync'] } tokio-stream = "*" futures = "0.3" cln-rpc = { path = "../cln-rpc" } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index ca43e567bdab..4568baccdf00 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -1,16 +1,19 @@ use crate::codec::{JsonCodec, JsonRpcCodec}; -use futures::sink::SinkExt; -use std::sync::Arc; -use tokio::sync::Mutex; -use tokio_util::codec::FramedWrite; -pub mod codec; -mod messages; pub use anyhow::Error; +use futures::sink::SinkExt; +extern crate log; use log::{trace, warn}; use std::marker::PhantomData; +use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::sync::Mutex; use tokio_stream::StreamExt; use tokio_util::codec::FramedRead; +use tokio_util::codec::FramedWrite; + +pub mod codec; +pub mod logging; +mod messages; #[macro_use] extern crate serde_json; @@ -50,19 +53,19 @@ where } } - pub async fn run(self) -> Result<(), Error> { - let (plugin, input) = self.build(); - plugin.run(input).await - } - pub fn build(self) -> (Plugin, I) { + let output = Arc::new(Mutex::new(FramedWrite::new( + self.output, + JsonCodec::default(), + ))); + + // Now configure the logging, so any `log` call is wrapped + // in a JSON-RPC notification and sent to c-lightning + tokio::spawn(async move {}); ( Plugin { state: Arc::new(Mutex::new(self.state)), - output: Arc::new(Mutex::new(FramedWrite::new( - self.output, - JsonCodec::default(), - ))), + output, input_type: PhantomData, }, self.input, @@ -74,7 +77,7 @@ pub struct Plugin where S: Clone + Send, I: AsyncRead, - O: Send + AsyncWrite, + O: Send + AsyncWrite + 'static, { //input: FramedRead, output: Arc>>, @@ -87,11 +90,14 @@ impl Plugin where S: Clone + Send, I: AsyncRead + Send + Unpin, - O: Send + AsyncWrite + Unpin, + O: Send + AsyncWrite + Unpin + 'static, { /// Read incoming requests from `c-lightning and dispatch their handling. #[allow(unused_mut)] pub async fn run(mut self, input: I) -> Result<(), Error> { + crate::logging::init(self.output.clone()).await?; + trace!("Plugin logging initialized"); + let mut input = FramedRead::new(input, JsonRpcCodec::default()); loop { match input.next().await { diff --git a/plugins/src/logging.rs b/plugins/src/logging.rs new file mode 100644 index 000000000000..7a90b84aa1ca --- /dev/null +++ b/plugins/src/logging.rs @@ -0,0 +1,92 @@ +use crate::codec::JsonCodec; +use futures::SinkExt; +use log::{Level, Metadata, Record}; +use serde::Serialize; +use std::sync::Arc; +use tokio::io::AsyncWrite; +use tokio::sync::Mutex; +use tokio_util::codec::FramedWrite; + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "lowercase")] +struct LogEntry { + level: LogLevel, + message: String, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(rename_all = "lowercase")] +enum LogLevel { + Debug, + Info, + Warn, + Error, +} + +impl From for LogLevel { + fn from(lvl: log::Level) -> Self { + match lvl { + log::Level::Error => LogLevel::Error, + log::Level::Warn => LogLevel::Warn, + log::Level::Info => LogLevel::Info, + log::Level::Debug | log::Level::Trace => LogLevel::Debug, + } + } +} + +/// A simple logger that just wraps log entries in a JSON-RPC +/// notification and delivers it to `lightningd`. +struct PluginLogger { + // An unbounded mpsc channel we can use to talk to the + // flusher. This avoids having circular locking dependencies if we + // happen to emit a log record while holding the lock on the + // plugin connection. + sender: tokio::sync::mpsc::UnboundedSender, +} + +/// Initialize the logger starting a flusher to the passed in sink. +pub async fn init(out: Arc>>) -> Result<(), log::SetLoggerError> +where + O: AsyncWrite + Send + Unpin + 'static, +{ + let out = out.clone(); + let (sender, mut receiver) = tokio::sync::mpsc::unbounded_channel::(); + tokio::spawn(async move { + while let Some(i) = receiver.recv().await { + // We continue draining the queue, even if we get some + // errors when forwarding. Forwarding could break due to + // an interrupted connection or stdout being closed, but + // keeping the messages in the queue is a memory leak. + let _ = out + .lock() + .await + .send(json!({ + "jsonrpc": "2.0", + "method": "log", + "params": i + })) + .await; + } + }); + log::set_boxed_logger(Box::new(PluginLogger { sender })) + .map(|()| log::set_max_level(log::LevelFilter::Debug)) +} + +impl log::Log for PluginLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= Level::Debug + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + self.sender + .send(LogEntry { + level: record.level().into(), + message: record.args().to_string(), + }) + .unwrap(); + } + } + + fn flush(&self) {} +} From fe21b89b561f5a964709115783a272f21e55a03e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 24 Jan 2022 13:19:45 +0100 Subject: [PATCH 0426/1530] pytest: Add a test for the cln-plugin logging integration --- plugins/examples/cln-plugin-startup.rs | 9 +++++---- tests/test_cln_rs.py | 17 ++++++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index f1c6ed26be1a..828605e3c7f3 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -6,9 +6,10 @@ use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - env_logger::init(); - let (plugin, stdin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()).build(); - plugin.run(stdin).await; - Ok(()) + tokio::spawn(async { + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + log::info!("Hello world"); + }); + plugin.run(stdin).await } diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index af01642a4fe9..ccccca15aa9d 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -2,7 +2,6 @@ from pathlib import Path from pyln.testing.utils import env, TEST_NETWORK import subprocess -import os import pytest @@ -12,8 +11,6 @@ reason='RUST is not enabled, skipping rust-dependent tests' ) -os.environ['RUST_LOG'] = "trace" - def test_rpc_client(node_factory): l1 = node_factory.get_node() @@ -21,3 +18,17 @@ def test_rpc_client(node_factory): rpc_path = Path(l1.daemon.lightning_dir) / TEST_NETWORK / "lightning-rpc" out = subprocess.check_output([bin_path, rpc_path], stderr=subprocess.STDOUT) assert(b'0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518' in out) + + +def test_plugin_start(node_factory): + """Start a minimal plugin and ensure it is well-behaved + """ + bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-plugin-startup" + l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + + # The plugin should be in the list of active plugins + plugins = l1.rpc.plugin('list')['plugins'] + assert len([p for p in plugins if 'cln-plugin-startup' in p['name'] and p['active']]) == 1 + + # Logging should also work through the log integration + l1.daemon.wait_for_log(r'Hello world') From 249fa8675ae4765f630b56b1c2816fd937465f3d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 7 Feb 2022 11:00:08 +0100 Subject: [PATCH 0427/1530] cln-plugin: Add options to the `getmanifest` call --- plugins/examples/cln-plugin-startup.rs | 11 ++- plugins/src/lib.rs | 37 +++++++- plugins/src/messages.rs | 7 +- plugins/src/options.rs | 122 +++++++++++++++++++++++++ tests/test_cln_rs.py | 26 ++++-- 5 files changed, 184 insertions(+), 19 deletions(-) create mode 100644 plugins/src/options.rs diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index 828605e3c7f3..df71ae39c96b 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -1,12 +1,19 @@ //! This is a test plugin used to verify that we can compile and run //! plugins using the Rust API against c-lightning. -use cln_plugin::Builder; +use cln_plugin::{options, Builder}; use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - let (plugin, stdin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()).build(); + let (plugin, stdin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) + .option(options::ConfigOption::new( + "test-option", + options::Value::Integer(42), + "a test-option with default 42", + )) + .build(); + tokio::spawn(async { tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; log::info!("Hello world"); diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 4568baccdf00..feb1833f71db 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -18,6 +18,10 @@ mod messages; #[macro_use] extern crate serde_json; +pub mod options; + +use options::ConfigOption; + /// Builder for a new plugin. pub struct Builder where @@ -35,6 +39,8 @@ where #[allow(dead_code)] subscriptions: Subscriptions, + + options: Vec, } impl Builder @@ -50,9 +56,15 @@ where output, hooks: Hooks::default(), subscriptions: Subscriptions::default(), + options: vec![], } } + pub fn option(mut self, opt: options::ConfigOption) -> Builder { + self.options.push(opt); + self + } + pub fn build(self) -> (Plugin, I) { let output = Arc::new(Mutex::new(FramedWrite::new( self.output, @@ -67,6 +79,7 @@ where state: Arc::new(Mutex::new(self.state)), output, input_type: PhantomData, + options: self.options, }, self.input, ) @@ -85,7 +98,20 @@ where /// The state gets cloned for each request state: Arc>, input_type: PhantomData, + options: Vec, +} + +impl Plugin +where + S: Clone + Send, + I: AsyncRead + Send + Unpin, + O: Send + AsyncWrite + Unpin, +{ + pub fn options(&self) -> Vec { + self.options.clone() + } } + impl Plugin where S: Clone + Send, @@ -130,8 +156,7 @@ where let state = self.state.clone(); let res: serde_json::Value = match request { messages::Request::Getmanifest(c) => { - serde_json::to_value(Plugin::::handle_get_manifest(c, state).await?) - .unwrap() + serde_json::to_value(self.handle_get_manifest(c, state).await?).unwrap() } messages::Request::Init(c) => { serde_json::to_value(Plugin::::handle_init(c, state).await?).unwrap() @@ -160,10 +185,14 @@ where } async fn handle_get_manifest( + &mut self, _call: messages::GetManifestCall, _state: Arc>, ) -> Result { - Ok(messages::GetManifestResponse::default()) + Ok(messages::GetManifestResponse { + options: self.options.clone(), + rpcmethods: vec![], + }) } async fn handle_init( @@ -192,6 +221,6 @@ mod test { #[test] fn init() { let builder = Builder::new((), tokio::io::stdin(), tokio::io::stdout()); - let plugin = builder.build(); + builder.build(); } } diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index fcba6ceaec8d..540019b6d4e3 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -1,3 +1,4 @@ +use crate::options::ConfigOption; use serde::de::{self, Deserializer}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -149,9 +150,9 @@ where } #[derive(Serialize, Default, Debug)] -pub struct GetManifestResponse { - options: Vec<()>, - rpcmethods: Vec<()>, +pub(crate) struct GetManifestResponse { + pub(crate) options: Vec, + pub(crate) rpcmethods: Vec<()>, } #[derive(Serialize, Default, Debug)] diff --git a/plugins/src/options.rs b/plugins/src/options.rs new file mode 100644 index 000000000000..04e11509f7ba --- /dev/null +++ b/plugins/src/options.rs @@ -0,0 +1,122 @@ +use serde::ser::{SerializeStruct, Serializer}; +use serde::{Serialize}; + +#[derive(Clone, Debug)] +pub enum Value { + String(String), + Integer(i64), + Boolean(bool), +} + +/// An stringly typed option that is passed to +#[derive(Clone, Debug)] +pub struct ConfigOption { + name: String, + pub(crate) value: Option, + default: Value, + description: String, +} + +impl ConfigOption { + pub fn name(&self) -> &str { + &self.name + } + pub fn default(&self) -> &Value { + &self.default + } +} + +// When we serialize we don't add the value. This is because we only +// ever serialize when we pass the option back to lightningd during +// the getmanifest call. +impl Serialize for ConfigOption { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut s = serializer.serialize_struct("ConfigOption", 4)?; + s.serialize_field("name", &self.name)?; + match &self.default { + Value::String(ss) => { + s.serialize_field("type", "string")?; + s.serialize_field("default", ss)?; + } + Value::Integer(i) => { + s.serialize_field("type", "int")?; + s.serialize_field("default", i)?; + } + + Value::Boolean(b) => { + s.serialize_field("type", "bool")?; + s.serialize_field("default", b)?; + } + } + + s.serialize_field("description", &self.description)?; + s.end() + } +} +impl ConfigOption { + pub fn new(name: &str, default: Value, description: &str) -> Self { + Self { + name: name.to_string(), + default, + description: description.to_string(), + value: None, + } + } + + pub fn value(&self) -> Value { + match &self.value { + None => self.default.clone(), + Some(v) => v.clone(), + } + } + + pub fn description(&self) -> String { + self.description.clone() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_option_serialize() { + let tests = vec![ + ( + ConfigOption::new("name", Value::String("default".to_string()), "description"), + json!({ + "name": "name", + "description":"description", + "default": "default", + "type": "string", + }), + ), + ( + ConfigOption::new("name", Value::Integer(42), "description"), + json!({ + "name": "name", + "description":"description", + "default": 42, + "type": "int", + }), + ), + ( + ConfigOption::new("name", Value::Boolean(true), "description" + json!({ + "name": "name", + "description":"description", + "default": true, + "type": "booltes", + }), + )), + ]; + + for (input, expected) in tests.iter() { + let res = serde_json::to_value(input).unwrap(); + assert_eq!(&res, expected); + } + } +} diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index ccccca15aa9d..86fc022596a5 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -21,14 +21,20 @@ def test_rpc_client(node_factory): def test_plugin_start(node_factory): - """Start a minimal plugin and ensure it is well-behaved - """ - bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-plugin-startup" - l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + """Start a minimal plugin and ensure it is well-behaved + """ + bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-plugin-startup" + l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + + cfg = l1.rpc.listconfigs() + p = cfg['plugins'][0] + p['path'] = None # The path is host-specific, so blank it. + expected = { + 'name': 'cln-plugin-startup', + 'options': { + 'test-option': 42 + }, + 'path': None + } + assert expected == p - # The plugin should be in the list of active plugins - plugins = l1.rpc.plugin('list')['plugins'] - assert len([p for p in plugins if 'cln-plugin-startup' in p['name'] and p['active']]) == 1 - - # Logging should also work through the log integration - l1.daemon.wait_for_log(r'Hello world') From fbcb4c33ad9147615067dbf10da83a1f2e3476a5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 21 Jan 2022 18:09:16 +0100 Subject: [PATCH 0428/1530] cln-plugin: Populate the options when we get an `init` call --- plugins/src/lib.rs | 25 +++++++++++++++++++++++-- plugins/src/messages.rs | 5 ++--- tests/test_cln_rs.py | 5 ++--- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index feb1833f71db..2d6382454809 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -159,7 +159,7 @@ where serde_json::to_value(self.handle_get_manifest(c, state).await?).unwrap() } messages::Request::Init(c) => { - serde_json::to_value(Plugin::::handle_init(c, state).await?).unwrap() + serde_json::to_value(self.handle_init(c, state).await?).unwrap() } o => panic!("Request {:?} is currently unhandled", o), }; @@ -196,9 +196,30 @@ where } async fn handle_init( - _call: messages::InitCall, + &mut self, + call: messages::InitCall, _state: Arc>, ) -> Result { + use options::Value as OValue; + use serde_json::Value as JValue; + + // Match up the ConfigOptions and fill in their values if we + // have a matching entry. + + for opt in self.options.iter_mut() { + if let Some(val) = call.options.get(opt.name()) { + opt.value = Some(match (opt.default(), &val) { + (OValue::String(_), JValue::String(s)) => OValue::String(s.clone()), + (OValue::Integer(_), JValue::Number(n)) => OValue::Integer(n.as_i64().unwrap()), + (OValue::Boolean(_), JValue::Bool(n)) => OValue::Boolean(*n), + + // It's ok to panic, if we get here c-lightning + // has not enforced the option type. + (_, _) => panic!("Mismatching types in options: {:?} != {:?}", opt, val), + }); + } + } + Ok(messages::InitResponse::default()) } } diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index 540019b6d4e3..b966446dc680 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -62,9 +62,8 @@ pub(crate) enum Notification { pub struct GetManifestCall {} #[derive(Deserialize, Debug)] -pub struct InitCall { - pub options: Value, - pub configuration: HashMap, +pub(crate) struct InitCall { + pub(crate) options: HashMap, } #[derive(Debug)] diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 86fc022596a5..f1f04842adfe 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -24,7 +24,7 @@ def test_plugin_start(node_factory): """Start a minimal plugin and ensure it is well-behaved """ bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-plugin-startup" - l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + l1 = node_factory.get_node(options={"plugin": str(bin_path), 'test-option': 31337}) cfg = l1.rpc.listconfigs() p = cfg['plugins'][0] @@ -32,9 +32,8 @@ def test_plugin_start(node_factory): expected = { 'name': 'cln-plugin-startup', 'options': { - 'test-option': 42 + 'test-option': 31337 }, 'path': None } assert expected == p - From a1555623bc0d04a743cc47c229b7f46a7ff934a9 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 7 Feb 2022 19:49:57 +0100 Subject: [PATCH 0429/1530] pytest: Mark Rust-dependent tests as skipped with VALGRIND `valgrind` reports seems to flag some memory accesses that are ok in the Rust standard library, which we can consider false positives for our purposes: ```Valgrind error file: valgrind-errors.69147 ==69147== Syscall param statx(file_name) points to unaddressable byte(s) ==69147== at 0x4B049FE: statx (statx.c:29) ==69147== by 0x2E2DA0: std::sys::unix::fs::try_statx (weak.rs:139) ==69147== by 0x2D7BD5: ::read_to_string (fs.rs:784) ==69147== by 0x2632CE: num_cpus::linux::Cgroup::param (linux.rs:214) ==69147== by 0x263179: num_cpus::linux::Cgroup::quota_us (linux.rs:203) ==69147== by 0x263002: num_cpus::linux::Cgroup::cpu_quota (linux.rs:188) ==69147== by 0x262C01: num_cpus::linux::load_cgroups (linux.rs:149) ==69147== by 0x26289D: num_cpus::linux::init_cgroups (linux.rs:129) ==69147== by 0x26BD88: core::ops::function::FnOnce::call_once (function.rs:227) ==69147== by 0x26B749: std::sync::once::Once::call_once::{{closure}} (once.rs:262) ==69147== by 0x139717: std::sync::once::Once::call_inner (once.rs:419) ==69147== by 0x26B6D5: std::sync::once::Once::call_once (once.rs:262) ==69147== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==69147== ==69147== Syscall param statx(buf) points to unaddressable byte(s) ==69147== at 0x4B049FE: statx (statx.c:29) ==69147== by 0x2E2DA0: std::sys::unix::fs::try_statx (weak.rs:139) ==69147== by 0x2D7BD5: ::read_to_string (fs.rs:784) ==69147== by 0x2632CE: num_cpus::linux::Cgroup::param (linux.rs:214) ==69147== by 0x263179: num_cpus::linux::Cgroup::quota_us (linux.rs:203) ==69147== by 0x263002: num_cpus::linux::Cgroup::cpu_quota (linux.rs:188) ==69147== by 0x262C01: num_cpus::linux::load_cgroups (linux.rs:149) ==69147== by 0x26289D: num_cpus::linux::init_cgroups (linux.rs:129) ==69147== by 0x26BD88: core::ops::function::FnOnce::call_once (function.rs:227) ==69147== by 0x26B749: std::sync::once::Once::call_once::{{closure}} (once.rs:262) ==69147== by 0x139717: std::sync::once::Once::call_inner (once.rs:419) ==69147== by 0x26B6D5: std::sync::once::Once::call_once (once.rs:262) ==69147== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==69147== ``` --- tests/test_cln_rs.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index f1f04842adfe..678f03c48109 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -5,11 +5,19 @@ import pytest -# Skip the entire module if we don't have Rust. -pytestmark = pytest.mark.skipif( - env('RUST') != '1', - reason='RUST is not enabled, skipping rust-dependent tests' -) +# Skip the entire module if we don't have Rust. The same is true for +# VALGRIND, since it sometimes causes false positives in +# `std::sync::Once` +pytestmark = [ + pytest.mark.skipif( + env('RUST') != '1', + reason='RUST is not enabled skipping rust-dependent tests' + ), + pytest.mark.skipif( + env('VALGRIND') == '1', + reason='VALGRIND is enabled skipping rust-dependent tests, as they may report false positives.' + ), +] def test_rpc_client(node_factory): From 4aba119733358662b11c7ac435ecfb59f40d32e3 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 9 Feb 2022 17:10:09 +0100 Subject: [PATCH 0430/1530] pytest: Use valgrind target suppressions instead of skipping tests Having a list of very targeted suppressions allows us to still run the majority of tests with valgrind checking, and not fail when Rust does some trickery. This is for example the case with `std::sync::Once` which uses `num_procs` calling out to the cgroups subsystem, sometimes with a null path. Suggested-by: Rusty Russell <@rustyrussell> --- doc/HACKING.md | 31 +++++++++++++++++++++++++++++++ tests/fixtures.py | 15 ++++++++++++++- tests/test_cln_rs.py | 18 +++++------------- tests/valgrind-suppressions.txt | 14 ++++++++++++++ 4 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 tests/valgrind-suppressions.txt diff --git a/doc/HACKING.md b/doc/HACKING.md index 9d3237864679..6003dbd724ba 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -242,6 +242,37 @@ TEST_DB_PROVIDER=[sqlite3|postgres] - Selects the database to use when running EXPERIMENTAL_DUAL_FUND=[0|1] - Enable dual-funding tests. ``` +#### Troubleshooting + +##### Valgrind complains about code we don't control + +Sometimes `valgrind` will complain about code we do not control +ourselves, either because it's in a library we use or it's a false +positive. There are generally three ways to address these issues +(in descending order of preference): + + 1. Add a suppression for the one specific call that is causing the + issue. Upon finding an issue `valgrind` is instructed in the + testing framework to print filters that'd match the issue. These + can be added to the suppressions file under + `tests/valgrind-suppressions.txt` in order to explicitly skip + reporting these in future. This is preferred over the other + solutions since it only disables reporting selectively for things + that were manually checked. See the [valgrind docs][vg-supp] for + details. + 2. Add the process that `valgrind` is complaining about to the + `--trace-children-skip` argument in `pyln-testing`. This is used + in cases of full binaries not being under our control, such as the + `python3` interpreter used in tests that run plugins. Do not use + this for binaries that are compiled from our code, as it tends to + mask real issues. + 3. Mark the test as skipped if running under `valgrind`. It's mostly + used to skip tests that otherwise would take considerably too long + to test on CI. We discourage this for suppressions, since it is a + very blunt tool. + +[vg-supp]: https://valgrind.org/docs/manual/manual-core.html#manual-core.suppress + Making BOLT Modifications ------------------------- diff --git a/tests/fixtures.py b/tests/fixtures.py index e1fdc12388bf..84258367d445 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,7 +1,8 @@ -from utils import DEVELOPER, TEST_NETWORK # noqa: F401,F403 +from utils import DEVELOPER, TEST_NETWORK, VALGRIND # 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 pathlib import Path import os import pytest @@ -17,6 +18,18 @@ class LightningNode(utils.LightningNode): def __init__(self, *args, **kwargs): utils.LightningNode.__init__(self, *args, **kwargs) + # We have some valgrind suppressions in the `tests/` + # directory, so we can add these to the valgrind configuration + # (not generally true when running pyln-testing, hence why + # it's being done in this specialization, and not in the + # library). + if self.daemon.cmd_line[0] == 'valgrind': + suppressions_path = Path(__file__).parent / "valgrind-suppressions.txt" + self.daemon.cmd_prefix += [ + f"--suppressions={suppressions_path}", + "--gen-suppressions=all" + ] + # If we opted into checking the DB statements we will attach the dblog # plugin before starting the node check_dblog = os.environ.get("TEST_CHECK_DBSTMTS", None) == "1" diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 678f03c48109..08dee44c98cf 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -5,19 +5,11 @@ import pytest -# Skip the entire module if we don't have Rust. The same is true for -# VALGRIND, since it sometimes causes false positives in -# `std::sync::Once` -pytestmark = [ - pytest.mark.skipif( - env('RUST') != '1', - reason='RUST is not enabled skipping rust-dependent tests' - ), - pytest.mark.skipif( - env('VALGRIND') == '1', - reason='VALGRIND is enabled skipping rust-dependent tests, as they may report false positives.' - ), -] +# Skip the entire module if we don't have Rust. +pytestmark = pytest.mark.skipif( + env('RUST') != '1', + reason='RUST is not enabled skipping rust-dependent tests' +) def test_rpc_client(node_factory): diff --git a/tests/valgrind-suppressions.txt b/tests/valgrind-suppressions.txt new file mode 100644 index 000000000000..c0053e40eda6 --- /dev/null +++ b/tests/valgrind-suppressions.txt @@ -0,0 +1,14 @@ +{ + rust__std_sync_once__try_statx_01 + Memcheck:Param + statx(buf) + fun:statx + ... +} +{ + rust__std_sync_once__try_statx_02 + Memcheck:Param + statx(file_name) + fun:statx + ... +} From 22618a2f94f8ff121b7d67afb874a7aa2bd432dd Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 15 Feb 2022 16:24:15 +0100 Subject: [PATCH 0431/1530] cln-plugin: Rework the plugin library using a Builder --- plugins/examples/cln-plugin-startup.rs | 12 +- plugins/src/lib.rs | 316 +++++++++++++++---------- plugins/src/options.rs | 4 +- 3 files changed, 197 insertions(+), 135 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index df71ae39c96b..fc3b208760c8 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -6,17 +6,13 @@ use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - let (plugin, stdin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) + let plugin = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "test-option", options::Value::Integer(42), "a test-option with default 42", )) - .build(); - - tokio::spawn(async { - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - log::info!("Hello world"); - }); - plugin.run(stdin).await + .start() + .await?; + plugin.join().await } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 2d6382454809..f53ff5b02b99 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -1,9 +1,8 @@ use crate::codec::{JsonCodec, JsonRpcCodec}; -pub use anyhow::Error; +pub use anyhow::{anyhow, Context, Error}; use futures::sink::SinkExt; extern crate log; -use log::{trace, warn}; -use std::marker::PhantomData; +use log::trace; use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::sync::Mutex; @@ -25,14 +24,13 @@ use options::ConfigOption; /// Builder for a new plugin. pub struct Builder where - S: Clone + Send, I: AsyncRead + Unpin, O: Send + AsyncWrite + Unpin, { state: S, - input: I, - output: O, + input: Option, + output: Option, #[allow(dead_code)] hooks: Hooks, @@ -46,14 +44,14 @@ where impl Builder where O: Send + AsyncWrite + Unpin + 'static, - S: Clone + Send + 'static, + S: Clone + Sync + Send + Clone + 'static, I: AsyncRead + Send + Unpin + 'static, { pub fn new(state: S, input: I, output: O) -> Self { Self { state, - input, - output, + input: Some(input), + output: Some(output), hooks: Hooks::default(), subscriptions: Subscriptions::default(), options: vec![], @@ -65,162 +63,230 @@ where self } - pub fn build(self) -> (Plugin, I) { + /// Build and start the plugin loop. This performs the handshake + /// and spawns a new task that accepts incoming messages from + /// c-lightning and dispatches them to the handlers. It only + /// returns after completing the handshake to ensure that the + /// configuration and initialization was successfull. + pub async fn start(mut self) -> Result, anyhow::Error> { + let mut input = FramedRead::new(self.input.take().unwrap(), JsonRpcCodec::default()); + + // Sadly we need to wrap the output in a mutex in order to + // enable early logging, i.e., logging that is done before the + // PluginDriver is processing events during the + // handshake. Otherwise we could just write the log events to + // the event queue and have the PluginDriver be the sole owner + // of `Stdout`. let output = Arc::new(Mutex::new(FramedWrite::new( - self.output, + self.output.take().unwrap(), JsonCodec::default(), ))); // Now configure the logging, so any `log` call is wrapped // in a JSON-RPC notification and sent to c-lightning - tokio::spawn(async move {}); - ( - Plugin { - state: Arc::new(Mutex::new(self.state)), - output, - input_type: PhantomData, - options: self.options, - }, - self.input, - ) + crate::logging::init(output.clone()).await?; + trace!("Plugin logging initialized"); + + // Read the `getmanifest` message: + match input.next().await { + Some(Ok(messages::JsonRpc::Request(id, messages::Request::Getmanifest(m)))) => { + output + .lock() + .await + .send(json!({ + "jsonrpc": "2.0", + "result": self.handle_get_manifest(m), + "id": id, + })) + .await? + } + o => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), + }; + + match input.next().await { + Some(Ok(messages::JsonRpc::Request(id, messages::Request::Init(m)))) => { + output + .lock() + .await + .send(json!({ + "jsonrpc": "2.0", + "result": self.handle_init(m)?, + "id": id, + })) + .await? + } + + o => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), + }; + + let (tx, _) = tokio::sync::broadcast::channel(1); + let plugin = Plugin { + state: self.state, + options: self.options, + wait_handle: tx, + }; + + // Start the PluginDriver to handle plugin IO + tokio::spawn( + PluginDriver { + plugin: plugin.clone(), + } + .run(input, output), + ); + + Ok(plugin) + } + + fn handle_get_manifest( + &mut self, + _call: messages::GetManifestCall, + ) -> messages::GetManifestResponse { + messages::GetManifestResponse { + options: self.options.clone(), + rpcmethods: vec![], + } + } + + fn handle_init(&mut self, call: messages::InitCall) -> Result { + use options::Value as OValue; + use serde_json::Value as JValue; + + // Match up the ConfigOptions and fill in their values if we + // have a matching entry. + for opt in self.options.iter_mut() { + if let Some(val) = call.options.get(opt.name()) { + opt.value = Some(match (opt.default(), &val) { + (OValue::String(_), JValue::String(s)) => OValue::String(s.clone()), + (OValue::Integer(_), JValue::Number(n)) => OValue::Integer(n.as_i64().unwrap()), + (OValue::Boolean(_), JValue::Bool(n)) => OValue::Boolean(*n), + + // It's ok to panic, if we get here c-lightning + // has not enforced the option type. + (_, _) => panic!("Mismatching types in options: {:?} != {:?}", opt, val), + }); + } + } + + Ok(messages::InitResponse::default()) } } -pub struct Plugin +#[derive(Clone)] +pub struct Plugin where S: Clone + Send, - I: AsyncRead, - O: Send + AsyncWrite + 'static, { - //input: FramedRead, - output: Arc>>, - /// The state gets cloned for each request - state: Arc>, - input_type: PhantomData, + state: S, options: Vec, + + /// A signal that allows us to wait on the plugin's shutdown. + wait_handle: tokio::sync::broadcast::Sender<()>, } -impl Plugin +/// The [PluginDriver] is used to run the IO loop, reading messages +/// from the Lightning daemon, dispatching calls and notifications to +/// the plugin, and returning responses to the the daemon. We also use +/// it to handle spontaneous messages like Notifications and logging +/// events. +struct PluginDriver where - S: Clone + Send, - I: AsyncRead + Send + Unpin, - O: Send + AsyncWrite + Unpin, + S: Send + Clone, { - pub fn options(&self) -> Vec { - self.options.clone() - } + #[allow(dead_code)] + plugin: Plugin, } -impl Plugin +use tokio::io::AsyncReadExt; +impl PluginDriver where - S: Clone + Send, - I: AsyncRead + Send + Unpin, - O: Send + AsyncWrite + Unpin + 'static, + S: Send + Clone, { - /// Read incoming requests from `c-lightning and dispatch their handling. - #[allow(unused_mut)] - pub async fn run(mut self, input: I) -> Result<(), Error> { - crate::logging::init(self.output.clone()).await?; - trace!("Plugin logging initialized"); - - let mut input = FramedRead::new(input, JsonRpcCodec::default()); + /// Run the plugin until we get a shutdown command. + async fn run( + self, + mut input: FramedRead, + _output: Arc>>, + ) -> Result<(), Error> + where + I: Send + AsyncReadExt + Unpin, + O: Send, + { loop { - match input.next().await { - Some(Ok(msg)) => { - trace!("Received a message: {:?}", msg); - match msg { - messages::JsonRpc::Request(id, p) => { - self.dispatch_request(id, p).await? - // Use a match to detect Ok / Error and return an error if we failed. - } - messages::JsonRpc::Notification(n) => self.dispatch_notification(n).await?, + tokio::select! { + _ = PluginDriver::dispatch_one(&mut input, &self.plugin) => {}, + } + } + } + + /// Dispatch one server-side event and then return. Just so we + /// have a nicer looking `select` statement in `run` :-) + async fn dispatch_one( + input: &mut FramedRead, + plugin: &Plugin, + ) -> Result<(), Error> + where + I: Send + AsyncReadExt + Unpin, + { + match input.next().await { + Some(Ok(msg)) => { + trace!("Received a message: {:?}", msg); + match msg { + messages::JsonRpc::Request(id, p) => { + PluginDriver::::dispatch_request(id, p, plugin).await + } + messages::JsonRpc::Notification(n) => { + PluginDriver::::dispatch_notification(n, plugin).await } } - Some(Err(e)) => { - warn!("Error reading command: {}", e); - break; - } - None => break, } + Some(Err(e)) => Err(anyhow!("Error reading command: {}", e)), + None => Ok(()), } - Ok(()) } async fn dispatch_request( - &mut self, id: usize, request: messages::Request, + _plugin: &Plugin, ) -> Result<(), Error> { - trace!("Dispatching request {:?}", request); - let state = self.state.clone(); - let res: serde_json::Value = match request { - messages::Request::Getmanifest(c) => { - serde_json::to_value(self.handle_get_manifest(c, state).await?).unwrap() - } - messages::Request::Init(c) => { - serde_json::to_value(self.handle_init(c, state).await?).unwrap() - } - o => panic!("Request {:?} is currently unhandled", o), - }; - trace!("Sending respone {:?}", res); - - let mut out = self.output.lock().await; - out.send(json!({ - "jsonrpc": "2.0", - "result": res, - "id": id, - })) - .await - .unwrap(); - Ok(()) + panic!("Unexpected request {:?} with id {}", request, id); } async fn dispatch_notification( - &mut self, notification: messages::Notification, - ) -> Result<(), Error> { + _plugin: &Plugin, + ) -> Result<(), Error> + where + S: Send + Clone, + { trace!("Dispatching notification {:?}", notification); unimplemented!() } +} - async fn handle_get_manifest( - &mut self, - _call: messages::GetManifestCall, - _state: Arc>, - ) -> Result { - Ok(messages::GetManifestResponse { - options: self.options.clone(), - rpcmethods: vec![], - }) +impl Plugin +where + S: Clone + Send, +{ + pub fn options(&self) -> Vec { + self.options.clone() } + pub fn state(&self) -> &S { + &self.state + } +} - async fn handle_init( - &mut self, - call: messages::InitCall, - _state: Arc>, - ) -> Result { - use options::Value as OValue; - use serde_json::Value as JValue; - - // Match up the ConfigOptions and fill in their values if we - // have a matching entry. - - for opt in self.options.iter_mut() { - if let Some(val) = call.options.get(opt.name()) { - opt.value = Some(match (opt.default(), &val) { - (OValue::String(_), JValue::String(s)) => OValue::String(s.clone()), - (OValue::Integer(_), JValue::Number(n)) => OValue::Integer(n.as_i64().unwrap()), - (OValue::Boolean(_), JValue::Bool(n)) => OValue::Boolean(*n), - - // It's ok to panic, if we get here c-lightning - // has not enforced the option type. - (_, _) => panic!("Mismatching types in options: {:?} != {:?}", opt, val), - }); - } - } - - Ok(messages::InitResponse::default()) +impl Plugin +where + S: Send + Clone, +{ + pub async fn join(&self) -> Result<(), Error> { + self.wait_handle + .subscribe() + .recv() + .await + .context("error waiting for shutdown") } } @@ -239,9 +305,9 @@ struct Subscriptions {} mod test { use super::*; - #[test] - fn init() { + #[tokio::test] + async fn init() { let builder = Builder::new((), tokio::io::stdin(), tokio::io::stdout()); - builder.build(); + let _ = builder.start(); } } diff --git a/plugins/src/options.rs b/plugins/src/options.rs index 04e11509f7ba..7ac6f3d7f9b1 100644 --- a/plugins/src/options.rs +++ b/plugins/src/options.rs @@ -104,14 +104,14 @@ mod test { }), ), ( - ConfigOption::new("name", Value::Boolean(true), "description" + ConfigOption::new("name", Value::Boolean(true), "description"), json!({ "name": "name", "description":"description", "default": true, "type": "booltes", }), - )), + ), ]; for (input, expected) in tests.iter() { From 8c6af21169a5d783c0196b201554ee8475438c84 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 21 Feb 2022 18:31:29 +0100 Subject: [PATCH 0432/1530] cln-plugin: Add support for synchronous RPC methods Changelog-Experimental: cln-plugin: Added support for non-async RPC method passthrough (async support coming soon) --- plugins/examples/cln-plugin-startup.rs | 12 +- plugins/src/lib.rs | 150 +++++++++++++++++++++++-- plugins/src/messages.rs | 56 +++------ plugins/src/options.rs | 2 +- tests/test_cln_rs.py | 15 +++ 5 files changed, 182 insertions(+), 53 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index fc3b208760c8..506f8fbef545 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -1,9 +1,10 @@ //! This is a test plugin used to verify that we can compile and run //! plugins using the Rust API against c-lightning. - -use cln_plugin::{options, Builder}; +#[macro_use] +extern crate serde_json; +use cln_plugin::{options, Builder, Error, Plugin}; +use std::pin::Pin; use tokio; - #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let plugin = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) @@ -12,7 +13,12 @@ async fn main() -> Result<(), anyhow::Error> { options::Value::Integer(42), "a test-option with default 42", )) + .rpcmethod("testmethod", "This is a test", Box::new(testmethod)) .start() .await?; plugin.join().await } + +fn testmethod(_p: Plugin<()>, _v: &serde_json::Value) -> Result { + Ok(json!("Hello")) +} diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index f53ff5b02b99..faf929b25e8a 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -1,8 +1,9 @@ use crate::codec::{JsonCodec, JsonRpcCodec}; -pub use anyhow::{anyhow, Context, Error}; +pub use anyhow::{anyhow, Context}; use futures::sink::SinkExt; extern crate log; use log::trace; +use std::collections::HashMap; use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::sync::Mutex; @@ -21,11 +22,18 @@ pub mod options; use options::ConfigOption; +/// Need to tell us about something that went wrong? Use this error +/// type to do that. Use this alias to be safe from future changes in +/// our internal error handling, since we'll implement any necessary +/// conversions for you :-) +pub type Error = anyhow::Error; + /// Builder for a new plugin. pub struct Builder where I: AsyncRead + Unpin, O: Send + AsyncWrite + Unpin, + S: Clone + Send, { state: S, @@ -39,6 +47,7 @@ where subscriptions: Subscriptions, options: Vec, + rpcmethods: HashMap>, } impl Builder @@ -55,6 +64,7 @@ where hooks: Hooks::default(), subscriptions: Subscriptions::default(), options: vec![], + rpcmethods: HashMap::new(), } } @@ -63,6 +73,23 @@ where self } + pub fn rpcmethod( + mut self, + name: &str, + description: &str, + callback: Callback, + ) -> Builder { + self.rpcmethods.insert( + name.to_string(), + RpcMethod { + name: name.to_string(), + description: description.to_string(), + callback, + }, + ); + self + } + /// Build and start the plugin loop. This performs the handshake /// and spawns a new task that accepts incoming messages from /// c-lightning and dispatches them to the handlers. It only @@ -119,19 +146,31 @@ where o => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), }; - let (tx, _) = tokio::sync::broadcast::channel(1); + let (wait_handle, _) = tokio::sync::broadcast::channel(1); + + // Collect the callbacks and create the hashmap for the dispatcher. + let mut rpcmethods = HashMap::new(); + for (name, callback) in self.rpcmethods.drain().map(|(k, v)| (k, v.callback)) { + rpcmethods.insert(name, callback); + } + + // An MPSC pair used by anything that needs to send messages + // to the main daemon. + let (sender, receiver) = tokio::sync::mpsc::channel(4); let plugin = Plugin { state: self.state, options: self.options, - wait_handle: tx, + wait_handle, + sender, }; // Start the PluginDriver to handle plugin IO tokio::spawn( PluginDriver { plugin: plugin.clone(), + rpcmethods, } - .run(input, output), + .run(receiver, input, output), ); Ok(plugin) @@ -141,9 +180,19 @@ where &mut self, _call: messages::GetManifestCall, ) -> messages::GetManifestResponse { + let rpcmethods: Vec<_> = self + .rpcmethods + .values() + .map(|v| messages::RpcMethod { + name: v.name.clone(), + description: v.description.clone(), + usage: String::new(), + }) + .collect(); + messages::GetManifestResponse { options: self.options.clone(), - rpcmethods: vec![], + rpcmethods, } } @@ -171,6 +220,20 @@ where } } +type Callback = Box, &serde_json::Value) -> Result>; + +/// A struct collecting the metadata required to register a custom +/// rpcmethod with the main daemon upon init. It'll get deconstructed +/// into just the callback after the init. +struct RpcMethod +where + S: Clone + Send, +{ + callback: Callback, + description: String, + name: String, +} + #[derive(Clone)] pub struct Plugin where @@ -182,6 +245,8 @@ where /// A signal that allows us to wait on the plugin's shutdown. wait_handle: tokio::sync::broadcast::Sender<()>, + + sender: tokio::sync::mpsc::Sender, } /// The [PluginDriver] is used to run the IO loop, reading messages @@ -195,9 +260,10 @@ where { #[allow(dead_code)] plugin: Plugin, + rpcmethods: HashMap>, } -use tokio::io::AsyncReadExt; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; impl PluginDriver where S: Send + Clone, @@ -205,16 +271,18 @@ where /// Run the plugin until we get a shutdown command. async fn run( self, + mut receiver: tokio::sync::mpsc::Receiver, mut input: FramedRead, - _output: Arc>>, + output: Arc>>, ) -> Result<(), Error> where I: Send + AsyncReadExt + Unpin, - O: Send, + O: Send + AsyncWriteExt + Unpin, { loop { tokio::select! { - _ = PluginDriver::dispatch_one(&mut input, &self.plugin) => {}, + _ = self.dispatch_one(&mut input, &self.plugin) => {}, + v = receiver.recv() => {output.lock().await.send(v.unwrap()).await?}, } } } @@ -222,6 +290,7 @@ where /// Dispatch one server-side event and then return. Just so we /// have a nicer looking `select` statement in `run` :-) async fn dispatch_one( + &self, input: &mut FramedRead, plugin: &Plugin, ) -> Result<(), Error> @@ -238,6 +307,31 @@ where messages::JsonRpc::Notification(n) => { PluginDriver::::dispatch_notification(n, plugin).await } + messages::JsonRpc::CustomRequest(id, p) => { + match self.dispatch_custom_request(id, p, plugin).await { + Ok(v) => plugin + .sender + .send(json!({ + "jsonrpc": "2.0", + "id": id, + "result": v + })) + .await + .context("returning custom result"), + Err(e) => plugin + .sender + .send(json!({ + "jsonrpc": "2.0", + "id": id, + "error": e.to_string(), + })) + .await + .context("returning custom error"), + } + } + messages::JsonRpc::CustomNotification(n) => { + PluginDriver::::dispatch_custom_notification(n, plugin).await + } } } Some(Err(e)) => Err(anyhow!("Error reading command: {}", e)), @@ -263,6 +357,44 @@ where trace!("Dispatching notification {:?}", notification); unimplemented!() } + async fn dispatch_custom_request( + &self, + _id: usize, + request: serde_json::Value, + plugin: &Plugin, + ) -> Result { + let method = request + .get("method") + .context("Missing 'method' in request")? + .as_str() + .context("'method' is not a string")?; + + let params = request + .get("params") + .context("Missing 'params' field in request")?; + let callback = self + .rpcmethods + .get(method) + .with_context(|| anyhow!("No handler for method '{}' registered", method))?; + + trace!( + "Dispatching custom request: method={}, params={}", + method, + params + ); + callback(plugin.clone(), params) + } + + async fn dispatch_custom_notification( + notification: serde_json::Value, + _plugin: &Plugin, + ) -> Result<(), Error> + where + S: Send + Clone, + { + trace!("Dispatching notification {:?}", notification); + unimplemented!() + } } impl Plugin diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index b966446dc680..f32cb2086cfe 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -70,6 +70,8 @@ pub(crate) struct InitCall { pub enum JsonRpc { Request(usize, R), Notification(N), + CustomRequest(usize, Value), + CustomNotification(Value), } /// This function disentangles the various cases: @@ -103,55 +105,29 @@ where let v = Value::deserialize(deserializer)?; let helper = IdHelper::deserialize(&v).map_err(de::Error::custom)?; match helper.id { - Some(id) => { - let r = R::deserialize(v).map_err(de::Error::custom)?; - Ok(JsonRpc::Request(id, r)) - } - None => { - let n = N::deserialize(v).map_err(de::Error::custom)?; - Ok(JsonRpc::Notification(n)) - } + Some(id) => match R::deserialize(v.clone()) { + Ok(r) => Ok(JsonRpc::Request(id, r)), + Err(_) => Ok(JsonRpc::CustomRequest(id, v)), + }, + None => match N::deserialize(v.clone()) { + Ok(n) => Ok(JsonRpc::Notification(n)), + Err(_) => Ok(JsonRpc::CustomNotification(v)), + }, } } } -use serde::ser::{SerializeStruct, Serializer}; - -impl Serialize for JsonRpc -where - N: Serialize + Debug, - R: Serialize + Debug, -{ - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match self { - JsonRpc::Notification(r) => { - let r = serde_json::to_value(r).unwrap(); - let mut s = serializer.serialize_struct("Notification", 3)?; - s.serialize_field("jsonrpc", "2.0")?; - s.serialize_field("method", &r["method"])?; - s.serialize_field("params", &r["params"])?; - s.end() - } - JsonRpc::Request(id, r) => { - let r = serde_json::to_value(r).unwrap(); - let mut s = serializer.serialize_struct("Request", 4)?; - s.serialize_field("jsonrpc", "2.0")?; - s.serialize_field("id", id)?; - s.serialize_field("method", &r["method"])?; - s.serialize_field("params", &r["params"])?; - s.end() - } - } - } +#[derive(Serialize, Default, Debug)] +pub(crate) struct RpcMethod { + pub(crate) name: String, + pub(crate) description: String, + pub(crate) usage: String, } #[derive(Serialize, Default, Debug)] pub(crate) struct GetManifestResponse { pub(crate) options: Vec, - pub(crate) rpcmethods: Vec<()>, + pub(crate) rpcmethods: Vec, } #[derive(Serialize, Default, Debug)] diff --git a/plugins/src/options.rs b/plugins/src/options.rs index 7ac6f3d7f9b1..587686c241df 100644 --- a/plugins/src/options.rs +++ b/plugins/src/options.rs @@ -109,7 +109,7 @@ mod test { "name": "name", "description":"description", "default": true, - "type": "booltes", + "type": "bool", }), ), ]; diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 08dee44c98cf..c336c294d2df 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -37,3 +37,18 @@ def test_plugin_start(node_factory): 'path': None } assert expected == p + + # Now check that the `testmethod was registered ok + l1.rpc.help("testmethod") == { + 'help': [ + { + 'command': 'testmethod ', + 'category': 'plugin', + 'description': 'This is a test', + 'verbose': 'This is a test' + } + ], + 'format-hint': 'simple' + } + + assert l1.rpc.testmethod() == "Hello" From 60e773239c39a42dd93cd70ee7255e56b27fe907 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 23 Feb 2022 19:00:25 +0100 Subject: [PATCH 0433/1530] cln-plugin: Add notification subscriptions and hooks to the plugins For now hooks are treated identically to rpcmethods, with the exception of not being returned in the `getmanifest` call. Later on we can add typed handlers as well. --- plugins/examples/cln-plugin-startup.rs | 12 +++ plugins/src/lib.rs | 128 +++++++++++++++++-------- plugins/src/messages.rs | 71 +++++++------- tests/test_cln_rs.py | 5 + 4 files changed, 141 insertions(+), 75 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index 506f8fbef545..a377d81d6ea4 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -14,6 +14,8 @@ async fn main() -> Result<(), anyhow::Error> { "a test-option with default 42", )) .rpcmethod("testmethod", "This is a test", Box::new(testmethod)) + .subscribe("connect", Box::new(connect_handler)) + .hook("peer_connected", Box::new(peer_connected_handler)) .start() .await?; plugin.join().await @@ -22,3 +24,13 @@ async fn main() -> Result<(), anyhow::Error> { fn testmethod(_p: Plugin<()>, _v: &serde_json::Value) -> Result { Ok(json!("Hello")) } + +fn connect_handler(_p: Plugin<()>, v: &serde_json::Value) -> Result<(), Error> { + log::info!("Got a connect notification: {}", v); + Ok(()) +} + +fn peer_connected_handler(_p: Plugin<()>, v: &serde_json::Value) -> Result { + log::info!("Got a connect hook call: {}", v); + Ok(json!({"result": "continue"})) +} diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index faf929b25e8a..b77064995b07 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -40,14 +40,10 @@ where input: Option, output: Option, - #[allow(dead_code)] - hooks: Hooks, - - #[allow(dead_code)] - subscriptions: Subscriptions, - + hooks: HashMap>, options: Vec, rpcmethods: HashMap>, + subscriptions: HashMap>, } impl Builder @@ -61,8 +57,8 @@ where state, input: Some(input), output: Some(output), - hooks: Hooks::default(), - subscriptions: Subscriptions::default(), + hooks: HashMap::new(), + subscriptions: HashMap::new(), options: vec![], rpcmethods: HashMap::new(), } @@ -73,6 +69,21 @@ where self } + /// Subscribe to notifications for the given `topic`. + pub fn subscribe(mut self, topic: &str, callback: NotificationCallback) -> Builder { + self.subscriptions + .insert(topic.to_string(), Subscription { callback }); + self + } + + /// Add a subscription to a given `hookname` + pub fn hook(mut self, hookname: &str, callback: Callback) -> Self { + self.hooks.insert(hookname.to_string(), Hook { callback }); + self + } + + /// Register a custom RPC method for the RPC passthrough from the + /// main daemon pub fn rpcmethod( mut self, name: &str, @@ -148,12 +159,6 @@ where let (wait_handle, _) = tokio::sync::broadcast::channel(1); - // Collect the callbacks and create the hashmap for the dispatcher. - let mut rpcmethods = HashMap::new(); - for (name, callback) in self.rpcmethods.drain().map(|(k, v)| (k, v.callback)) { - rpcmethods.insert(name, callback); - } - // An MPSC pair used by anything that needs to send messages // to the main daemon. let (sender, receiver) = tokio::sync::mpsc::channel(4); @@ -164,11 +169,21 @@ where sender, }; + // TODO Split the two hashmaps once we fill in the hook + // payload structs in messages.rs + let mut rpcmethods: HashMap> = + HashMap::from_iter(self.rpcmethods.drain().map(|(k, v)| (k, v.callback))); + rpcmethods.extend(self.hooks.clone().drain().map(|(k, v)| (k, v.callback))); + // Start the PluginDriver to handle plugin IO tokio::spawn( PluginDriver { plugin: plugin.clone(), rpcmethods, + hooks: HashMap::from_iter(self.hooks.drain().map(|(k, v)| (k, v.callback))), + subscriptions: HashMap::from_iter( + self.subscriptions.drain().map(|(k, v)| (k, v.callback)), + ), } .run(receiver, input, output), ); @@ -192,6 +207,8 @@ where messages::GetManifestResponse { options: self.options.clone(), + subscriptions: self.subscriptions.keys().map(|s| s.clone()).collect(), + hooks: self.hooks.keys().map(|s| s.clone()).collect(), rpcmethods, } } @@ -221,6 +238,7 @@ where } type Callback = Box, &serde_json::Value) -> Result>; +type NotificationCallback = Box, &serde_json::Value) -> Result<(), Error>>; /// A struct collecting the metadata required to register a custom /// rpcmethod with the main daemon upon init. It'll get deconstructed @@ -234,6 +252,21 @@ where name: String, } +struct Subscription +where + S: Clone + Send, +{ + callback: NotificationCallback, +} + +#[derive(Clone)] +struct Hook +where + S: Clone + Send, +{ + callback: Callback, +} + #[derive(Clone)] pub struct Plugin where @@ -258,9 +291,13 @@ struct PluginDriver where S: Send + Clone, { - #[allow(dead_code)] + plugin: Plugin, rpcmethods: HashMap>, + + #[allow(dead_code)] // Unused until we fill in the Hook structs. + hooks: HashMap>, + subscriptions: HashMap>, } use tokio::io::{AsyncReadExt, AsyncWriteExt}; @@ -281,9 +318,9 @@ where { loop { tokio::select! { - _ = self.dispatch_one(&mut input, &self.plugin) => {}, - v = receiver.recv() => {output.lock().await.send(v.unwrap()).await?}, - } + _ = self.dispatch_one(&mut input, &self.plugin) => {}, + v = receiver.recv() => {output.lock().await.send(v.unwrap()).await?}, + } } } @@ -305,7 +342,7 @@ where PluginDriver::::dispatch_request(id, p, plugin).await } messages::JsonRpc::Notification(n) => { - PluginDriver::::dispatch_notification(n, plugin).await + self.dispatch_notification(n, plugin).await } messages::JsonRpc::CustomRequest(id, p) => { match self.dispatch_custom_request(id, p, plugin).await { @@ -330,7 +367,7 @@ where } } messages::JsonRpc::CustomNotification(n) => { - PluginDriver::::dispatch_custom_notification(n, plugin).await + self.dispatch_custom_notification(n, plugin).await } } } @@ -340,23 +377,24 @@ where } async fn dispatch_request( - id: usize, - request: messages::Request, + _id: usize, + _request: messages::Request, _plugin: &Plugin, ) -> Result<(), Error> { - panic!("Unexpected request {:?} with id {}", request, id); + todo!("This is unreachable until we start filling in messages:Request. Until then the custom dispatcher below is used exclusively.") } async fn dispatch_notification( - notification: messages::Notification, + &self, + _notification: messages::Notification, _plugin: &Plugin, ) -> Result<(), Error> where S: Send + Clone, { - trace!("Dispatching notification {:?}", notification); - unimplemented!() + todo!("As soon as we define the full structure of the messages::Notification we'll get here. Until then the custom dispatcher below is used.") } + async fn dispatch_custom_request( &self, _id: usize, @@ -386,14 +424,35 @@ where } async fn dispatch_custom_notification( + &self, notification: serde_json::Value, - _plugin: &Plugin, + plugin: &Plugin, ) -> Result<(), Error> where S: Send + Clone, { - trace!("Dispatching notification {:?}", notification); - unimplemented!() + trace!("Dispatching custom notification {:?}", notification); + let method = notification + .get("method") + .context("Missing 'method' in notification")? + .as_str() + .context("'method' is not a string")?; + let params = notification + .get("params") + .context("Missing 'params' field in notification")?; + let callback = self + .subscriptions + .get(method) + .with_context(|| anyhow!("No handler for method '{}' registered", method))?; + trace!( + "Dispatching custom request: method={}, params={}", + method, + params + ); + if let Err(e) = callback(plugin.clone(), params) { + log::error!("Error in notification handler '{}': {}", method, e); + } + Ok(()) } } @@ -422,17 +481,6 @@ where } } -/// A container for all the configure hooks. It is just a collection -/// of callbacks that can be registered by the users of the -/// library. Based on this configuration we can then generate the -/// [`messages::GetManifestResponse`] from, populating our subscriptions -#[derive(Debug, Default)] -struct Hooks {} - -/// A container for all the configured notifications. -#[derive(Debug, Default)] -struct Subscriptions {} - #[cfg(test)] mod test { use super::*; diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index f32cb2086cfe..ec11cb2eb633 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -12,50 +12,49 @@ pub(crate) enum Request { // Builtin Getmanifest(GetManifestCall), Init(InitCall), - // Hooks - PeerConnected, - CommitmentRevocation, - DbWrite, - InvoicePayment, - Openchannel, - Openchannel2, - Openchannel2Changed, - Openchannel2Sign, - RbfChannel, - HtlcAccepted, - RpcCommand, - Custommsg, - OnionMessage, - OnionMessageBlinded, - OnionMessageOurpath, + // PeerConnected, + // CommitmentRevocation, + // DbWrite, + // InvoicePayment, + // Openchannel, + // Openchannel2, + // Openchannel2Changed, + // Openchannel2Sign, + // RbfChannel, + // HtlcAccepted, + // RpcCommand, + // Custommsg, + // OnionMessage, + // OnionMessageBlinded, + // OnionMessageOurpath, // Bitcoin backend - Getchaininfo, - Estimatefees, - Getrawblockbyheight, - Getutxout, - Sendrawtransaction, + // Getchaininfo, + // Estimatefees, + // Getrawblockbyheight, + // Getutxout, + // Sendrawtransaction, } #[derive(Deserialize, Debug)] #[serde(tag = "method", content = "params")] #[serde(rename_all = "snake_case")] pub(crate) enum Notification { - ChannelOpened, - ChannelOpenFailed, - ChannelStateChanged, - Connect, - Disconnect, - InvoicePayment, - InvoiceCreation, - Warning, - ForwardEvent, - SendpaySuccess, - SendpayFailure, - CoinMovement, - OpenchannelPeerSigs, - Shutdown, +// ChannelOpened, +// ChannelOpenFailed, +// ChannelStateChanged, +// Connect, +// Disconnect, +// InvoicePayment, +// InvoiceCreation, +// Warning, +// ForwardEvent, +// SendpaySuccess, +// SendpayFailure, +// CoinMovement, +// OpenchannelPeerSigs, +// Shutdown, } #[derive(Deserialize, Debug)] @@ -128,6 +127,8 @@ pub(crate) struct RpcMethod { pub(crate) struct GetManifestResponse { pub(crate) options: Vec, pub(crate) rpcmethods: Vec, + pub(crate) subscriptions: Vec, + pub(crate) hooks: Vec, } #[derive(Serialize, Default, Debug)] diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index c336c294d2df..5c2d891089fc 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -25,6 +25,7 @@ def test_plugin_start(node_factory): """ bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-plugin-startup" l1 = node_factory.get_node(options={"plugin": str(bin_path), 'test-option': 31337}) + l2 = node_factory.get_node() cfg = l1.rpc.listconfigs() p = cfg['plugins'][0] @@ -52,3 +53,7 @@ def test_plugin_start(node_factory): } assert l1.rpc.testmethod() == "Hello" + + l1.connect(l2) + l1.daemon.wait_for_log(r'Got a connect hook call') + l1.daemon.wait_for_log(r'Got a connect notification') From a7ef38732f145ac4c70212d97fe23216f1c85049 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 25 Feb 2022 13:59:24 +0100 Subject: [PATCH 0434/1530] cln-plugin: Make rpcmethod handlers async --- plugins/examples/cln-plugin-startup.rs | 6 ++-- plugins/src/lib.rs | 38 ++++++++++++++++---------- tests/test_cln_rs.py | 2 +- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index a377d81d6ea4..a1b2f2a4f0eb 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -13,15 +13,15 @@ async fn main() -> Result<(), anyhow::Error> { options::Value::Integer(42), "a test-option with default 42", )) - .rpcmethod("testmethod", "This is a test", Box::new(testmethod)) + .rpcmethod("testmethod", "This is a test", testmethod) .subscribe("connect", Box::new(connect_handler)) - .hook("peer_connected", Box::new(peer_connected_handler)) + //.hook("peer_connected", Box::new(peer_connected_handler)) .start() .await?; plugin.join().await } -fn testmethod(_p: Plugin<()>, _v: &serde_json::Value) -> Result { +async fn testmethod(_p: Plugin<()>, _v: serde_json::Value) -> Result { Ok(json!("Hello")) } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index b77064995b07..9f8bb777a21a 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -4,6 +4,8 @@ use futures::sink::SinkExt; extern crate log; use log::trace; use std::collections::HashMap; +use std::future::Future; +use std::pin::Pin; use std::sync::Arc; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::sync::Mutex; @@ -84,18 +86,18 @@ where /// Register a custom RPC method for the RPC passthrough from the /// main daemon - pub fn rpcmethod( - mut self, - name: &str, - description: &str, - callback: Callback, - ) -> Builder { + pub fn rpcmethod(mut self, name: &str, description: &str, callback: C) -> Builder + where + C: Send + Sync + 'static, + C: Fn(Plugin, Request) -> F + 'static, + F: Future + Send + Sync + 'static, + { self.rpcmethods.insert( name.to_string(), RpcMethod { name: name.to_string(), description: description.to_string(), - callback, + callback: Box::new(move |p, r| Box::pin(callback(p, r))), }, ); self @@ -171,9 +173,10 @@ where // TODO Split the two hashmaps once we fill in the hook // payload structs in messages.rs - let mut rpcmethods: HashMap> = + let mut rpcmethods: HashMap> = HashMap::from_iter(self.rpcmethods.drain().map(|(k, v)| (k, v.callback))); - rpcmethods.extend(self.hooks.clone().drain().map(|(k, v)| (k, v.callback))); + // TODO re-enable once hooks are async again + //rpcmethods.extend(self.hooks.clone().drain().map(|(k, v)| (k, v.callback))); // Start the PluginDriver to handle plugin IO tokio::spawn( @@ -237,8 +240,14 @@ where } } -type Callback = Box, &serde_json::Value) -> Result>; -type NotificationCallback = Box, &serde_json::Value) -> Result<(), Error>>; +// Just some type aliases so we don't get confused in a lisp-like sea +// of parentheses. +type Request = serde_json::Value; +type Response = Result; +type Callback = Box, &Request) -> Response>; +type AsyncCallback = + Box, Request) -> Pin + Send>> + Send + Sync>; +type NotificationCallback = Box, &Request) -> Result<(), Error>>; /// A struct collecting the metadata required to register a custom /// rpcmethod with the main daemon upon init. It'll get deconstructed @@ -247,7 +256,7 @@ struct RpcMethod where S: Clone + Send, { - callback: Callback, + callback: AsyncCallback, description: String, name: String, } @@ -291,9 +300,8 @@ struct PluginDriver where S: Send + Clone, { - plugin: Plugin, - rpcmethods: HashMap>, + rpcmethods: HashMap>, #[allow(dead_code)] // Unused until we fill in the Hook structs. hooks: HashMap>, @@ -420,7 +428,7 @@ where method, params ); - callback(plugin.clone(), params) + callback(plugin.clone(), params.clone()).await } async fn dispatch_custom_notification( diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 5c2d891089fc..fa8840f1f9fa 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -55,5 +55,5 @@ def test_plugin_start(node_factory): assert l1.rpc.testmethod() == "Hello" l1.connect(l2) - l1.daemon.wait_for_log(r'Got a connect hook call') + #l1.daemon.wait_for_log(r'Got a connect hook call') l1.daemon.wait_for_log(r'Got a connect notification') From af4eed3787835eab90d9500b0dac5e39516f9e9e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 25 Feb 2022 14:36:39 +0100 Subject: [PATCH 0435/1530] cln-plugin: Make hooks asynchronous --- plugins/examples/cln-plugin-startup.rs | 4 ++-- plugins/src/lib.rs | 25 ++++++++++++++++--------- tests/test_cln_rs.py | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index a1b2f2a4f0eb..c06c83c40e61 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -15,7 +15,7 @@ async fn main() -> Result<(), anyhow::Error> { )) .rpcmethod("testmethod", "This is a test", testmethod) .subscribe("connect", Box::new(connect_handler)) - //.hook("peer_connected", Box::new(peer_connected_handler)) + .hook("peer_connected", peer_connected_handler) .start() .await?; plugin.join().await @@ -30,7 +30,7 @@ fn connect_handler(_p: Plugin<()>, v: &serde_json::Value) -> Result<(), Error> { Ok(()) } -fn peer_connected_handler(_p: Plugin<()>, v: &serde_json::Value) -> Result { +async fn peer_connected_handler(_p: Plugin<()>, v: serde_json::Value) -> Result { log::info!("Got a connect hook call: {}", v); Ok(json!({"result": "continue"})) } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 9f8bb777a21a..28752ac29302 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -79,8 +79,18 @@ where } /// Add a subscription to a given `hookname` - pub fn hook(mut self, hookname: &str, callback: Callback) -> Self { - self.hooks.insert(hookname.to_string(), Hook { callback }); + pub fn hook(mut self, hookname: &str, callback: C) -> Self + where + C: Send + Sync + 'static, + C: Fn(Plugin, Request) -> F + 'static, + F: Future + Send + Sync + 'static, + { + self.hooks.insert( + hookname.to_string(), + Hook { + callback: Box::new(move |p, r| Box::pin(callback(p, r))), + }, + ); self } @@ -175,15 +185,14 @@ where // payload structs in messages.rs let mut rpcmethods: HashMap> = HashMap::from_iter(self.rpcmethods.drain().map(|(k, v)| (k, v.callback))); - // TODO re-enable once hooks are async again - //rpcmethods.extend(self.hooks.clone().drain().map(|(k, v)| (k, v.callback))); + rpcmethods.extend(self.hooks.drain().map(|(k, v)| (k, v.callback))); // Start the PluginDriver to handle plugin IO tokio::spawn( PluginDriver { plugin: plugin.clone(), rpcmethods, - hooks: HashMap::from_iter(self.hooks.drain().map(|(k, v)| (k, v.callback))), + hooks: HashMap::new(), subscriptions: HashMap::from_iter( self.subscriptions.drain().map(|(k, v)| (k, v.callback)), ), @@ -244,7 +253,6 @@ where // of parentheses. type Request = serde_json::Value; type Response = Result; -type Callback = Box, &Request) -> Response>; type AsyncCallback = Box, Request) -> Pin + Send>> + Send + Sync>; type NotificationCallback = Box, &Request) -> Result<(), Error>>; @@ -268,12 +276,11 @@ where callback: NotificationCallback, } -#[derive(Clone)] struct Hook where S: Clone + Send, { - callback: Callback, + callback: AsyncCallback, } #[derive(Clone)] @@ -304,7 +311,7 @@ where rpcmethods: HashMap>, #[allow(dead_code)] // Unused until we fill in the Hook structs. - hooks: HashMap>, + hooks: HashMap>, subscriptions: HashMap>, } diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index fa8840f1f9fa..5c2d891089fc 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -55,5 +55,5 @@ def test_plugin_start(node_factory): assert l1.rpc.testmethod() == "Hello" l1.connect(l2) - #l1.daemon.wait_for_log(r'Got a connect hook call') + l1.daemon.wait_for_log(r'Got a connect hook call') l1.daemon.wait_for_log(r'Got a connect notification') From 1bd2b8c9f74d693e0e7354f1714439cd1c1fcc54 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 25 Feb 2022 14:48:32 +0100 Subject: [PATCH 0436/1530] cln-plugin: Make notification handlers asynchronous --- plugins/examples/cln-plugin-startup.rs | 5 ++- plugins/src/lib.rs | 46 +++++++++++++++++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index c06c83c40e61..822083e47c15 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -3,7 +3,6 @@ #[macro_use] extern crate serde_json; use cln_plugin::{options, Builder, Error, Plugin}; -use std::pin::Pin; use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { @@ -14,7 +13,7 @@ async fn main() -> Result<(), anyhow::Error> { "a test-option with default 42", )) .rpcmethod("testmethod", "This is a test", testmethod) - .subscribe("connect", Box::new(connect_handler)) + .subscribe("connect", connect_handler) .hook("peer_connected", peer_connected_handler) .start() .await?; @@ -25,7 +24,7 @@ async fn testmethod(_p: Plugin<()>, _v: serde_json::Value) -> Result, v: &serde_json::Value) -> Result<(), Error> { +async fn connect_handler(_p: Plugin<()>, v: serde_json::Value) -> Result<(), Error> { log::info!("Got a connect notification: {}", v); Ok(()) } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 28752ac29302..1318d893e53f 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -71,10 +71,36 @@ where self } - /// Subscribe to notifications for the given `topic`. - pub fn subscribe(mut self, topic: &str, callback: NotificationCallback) -> Builder { - self.subscriptions - .insert(topic.to_string(), Subscription { callback }); + /// Subscribe to notifications for the given `topic`. The handler + /// is an async function that takes a `Plugin` and the + /// notification as a `serde_json::Value` as inputs. Since + /// notifications do not expect a result the handler should only + /// report errors while processing. Any error reported while + /// processing the notification will be logged in the cln logs. + /// + /// ``` + /// use cln_plugin::{options, Builder, Error, Plugin}; + /// + /// async fn connect_handler(_p: Plugin<()>, v: serde_json::Value) -> Result<(), Error> { + /// println!("Got a connect notification: {}", v); + /// Ok(()) + /// } + /// + /// let b = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) + /// .subscribe("connect", connect_handler); + /// ``` + pub fn subscribe(mut self, topic: &str, callback: C) -> Builder + where + C: Send + Sync + 'static, + C: Fn(Plugin, Request) -> F + 'static, + F: Future> + Send + Sync + 'static, + { + self.subscriptions.insert( + topic.to_string(), + Subscription { + callback: Box::new(move |p, r| Box::pin(callback(p, r))), + }, + ); self } @@ -255,7 +281,11 @@ type Request = serde_json::Value; type Response = Result; type AsyncCallback = Box, Request) -> Pin + Send>> + Send + Sync>; -type NotificationCallback = Box, &Request) -> Result<(), Error>>; +type AsyncNotificationCallback = Box< + dyn Fn(Plugin, Request) -> Pin> + Send>> + + Send + + Sync, +>; /// A struct collecting the metadata required to register a custom /// rpcmethod with the main daemon upon init. It'll get deconstructed @@ -273,7 +303,7 @@ struct Subscription where S: Clone + Send, { - callback: NotificationCallback, + callback: AsyncNotificationCallback, } struct Hook @@ -312,7 +342,7 @@ where #[allow(dead_code)] // Unused until we fill in the Hook structs. hooks: HashMap>, - subscriptions: HashMap>, + subscriptions: HashMap>, } use tokio::io::{AsyncReadExt, AsyncWriteExt}; @@ -464,7 +494,7 @@ where method, params ); - if let Err(e) = callback(plugin.clone(), params) { + if let Err(e) = callback(plugin.clone(), params.clone()).await { log::error!("Error in notification handler '{}': {}", method, e); } Ok(()) From 6706384c509232db14090e647984871c95a2708b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 25 Feb 2022 14:55:49 +0100 Subject: [PATCH 0437/1530] cln-plugin: Ensure we cleanly shut down when we lose the master conn --- plugins/src/lib.rs | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 1318d893e53f..dfa996210f57 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -224,6 +224,9 @@ where ), } .run(receiver, input, output), + // TODO Use the broadcast to distribute any error that we + // might receive here to anyone listening. (Shutdown + // signal) ); Ok(plugin) @@ -362,10 +365,21 @@ where O: Send + AsyncWriteExt + Unpin, { loop { + // If we encounter any error reading or writing from/to + // the master we hand them up, so we can return control to + // the user-code, which may require some cleanups or + // similar. tokio::select! { - _ = self.dispatch_one(&mut input, &self.plugin) => {}, - v = receiver.recv() => {output.lock().await.send(v.unwrap()).await?}, - } + e = self.dispatch_one(&mut input, &self.plugin) => { + //Hand any error up. + e?; + }, + v = receiver.recv() => { + output.lock().await.send( + v.context("internal communication error")? + ).await?; + }, + } } } @@ -417,7 +431,7 @@ where } } Some(Err(e)) => Err(anyhow!("Error reading command: {}", e)), - None => Ok(()), + None => Err(anyhow!("Error reading from master")), } } From 1e1948bfd21a59e55d82147666516180340d470e Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Tue, 21 Dec 2021 20:07:24 -0800 Subject: [PATCH 0438/1530] hsmd: Add redeemscripts to anchor outputs --- channeld/commit_tx.c | 17 ++++++++++------- common/initial_commit_tx.c | 7 ++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index 1e87ae6ee908..2fdf0df1ac62 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -285,6 +285,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * `dust_limit_satoshis`, add a [`to_remote` * output](#to_remote-output). */ + u8 *redeem; if (amount_msat_greater_eq_sat(other_pay, dust_limit)) { struct amount_sat amount = amount_msat_to_sat_round_down(other_pay); u8 *scriptpubkey; @@ -303,11 +304,10 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * Otherwise, this output is a simple P2WPKH to `remotepubkey`. */ if (option_anchor_outputs) { - const u8 *redeem - = anchor_to_remote_redeem(tmpctx, - &keyset->other_payment_key, - (!side) == lessor ? - csv_lock : 1); + redeem = anchor_to_remote_redeem(tmpctx, + &keyset->other_payment_key, + (!side) == lessor ? + csv_lock : 1); /* BOLT- #3: * ##### Leased channel (`option_will_fund`) * @@ -322,10 +322,11 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, */ scriptpubkey = scriptpubkey_p2wsh(tmpctx, redeem); } else { + redeem = NULL; scriptpubkey = scriptpubkey_p2wpkh(tmpctx, &keyset->other_payment_key); } - pos = bitcoin_tx_add_output(tx, scriptpubkey, NULL, amount); + pos = bitcoin_tx_add_output(tx, scriptpubkey, redeem, amount); assert(pos == n); (*htlcmap)[n] = direct_outputs ? dummy_to_remote : NULL; /* We don't assign cltvs[n]: if we use it, order doesn't matter. @@ -337,8 +338,10 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, n++; to_remote = true; - } else + } else { to_remote = false; + redeem = NULL; + } /* BOLT #3: * diff --git a/common/initial_commit_tx.c b/common/initial_commit_tx.c index 6da3fa3a0115..44203f7f4bd6 100644 --- a/common/initial_commit_tx.c +++ b/common/initial_commit_tx.c @@ -243,19 +243,20 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, */ u8 *scriptpubkey; int pos; + u8 *redeem; amount = amount_msat_to_sat_round_down(other_pay); if (option_anchor_outputs) { - const u8 *redeem - = anchor_to_remote_redeem(tmpctx, + redeem = anchor_to_remote_redeem(tmpctx, &keyset->other_payment_key, (!side) == lessor ? csv_lock : 1); scriptpubkey = scriptpubkey_p2wsh(tmpctx, redeem); } else { + redeem = NULL; scriptpubkey = scriptpubkey_p2wpkh(tmpctx, &keyset->other_payment_key); } - pos = bitcoin_tx_add_output(tx, scriptpubkey, NULL, amount); + pos = bitcoin_tx_add_output(tx, scriptpubkey, redeem, amount); assert(pos == n); output_order[n] = dummy_remote; n++; From 8876c03791db7119606a362e7b887bc642bf4592 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 23 Dec 2021 11:57:33 -0800 Subject: [PATCH 0439/1530] hsmd: Add PSBT keypath utility functions --- common/Makefile | 1 + common/psbt_keypath.c | 33 +++++++++++++++++++++++++++++++++ common/psbt_keypath.h | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 common/psbt_keypath.c create mode 100644 common/psbt_keypath.h diff --git a/common/Makefile b/common/Makefile index a203a734a5f7..86f1588b421b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -69,6 +69,7 @@ COMMON_SRC_NOGEN := \ common/ping.c \ common/private_channel_announcement.c \ common/psbt_internal.c \ + common/psbt_keypath.c \ common/psbt_open.c \ common/pseudorand.c \ common/random_select.c \ diff --git a/common/psbt_keypath.c b/common/psbt_keypath.c new file mode 100644 index 000000000000..5037a0a405fc --- /dev/null +++ b/common/psbt_keypath.c @@ -0,0 +1,33 @@ +#include "config.h" +#include +#include +#include +#include +#include + +void psbt_set_keypath(u32 index, const struct ext_key *ext, struct wally_map *map_in) { + u8 fingerprint[BIP32_KEY_FINGERPRINT_LEN]; + if (bip32_key_get_fingerprint( + (struct ext_key *) ext, fingerprint, sizeof(fingerprint)) != WALLY_OK) + abort(); + + u32 path[1]; + path[0] = index; + + if (wally_map_add_keypath_item(map_in, + ext->pub_key, sizeof(ext->pub_key), + fingerprint, sizeof(fingerprint), + path, 1) != WALLY_OK) + abort(); +} + +void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx, + u32 key_index, + const struct ext_key *ext) { + size_t outndx = tx->psbt->num_outputs - 1; + struct wally_map *map_in = &tx->psbt->outputs[outndx].keypaths; + + tal_wally_start(); + psbt_set_keypath(key_index, ext, map_in); + tal_wally_end(tx->psbt); +} diff --git a/common/psbt_keypath.h b/common/psbt_keypath.h new file mode 100644 index 000000000000..f19d85761cbd --- /dev/null +++ b/common/psbt_keypath.h @@ -0,0 +1,32 @@ +#ifndef LIGHTNING_COMMON_PSBT_KEYPATH_H +#define LIGHTNING_COMMON_PSBT_KEYPATH_H + +#include "config.h" +#include + +struct bitcoin_tx; +struct ext_key; +struct wally_map; + +/* psbt_set_keypath - Set the keypath of a PSBT output. + * + * @index - child index of the wallet key + * @ext - extended public key of the immediate parent of the wallet key + * @map_in - wally keypaths map + */ +void psbt_set_keypath(u32 index, + const struct ext_key *ext, + struct wally_map *map_in); + +/* psbt_add_keypath_to_last_output - augment the last output with the + * given wallet keypath + * + * @tx - transaction to modify + * @index - child index of the wallet key + * @ext - extended public key of the immediate parent of the wallet key + */ +void psbt_add_keypath_to_last_output(struct bitcoin_tx *tx, + u32 index, + const struct ext_key *ext); + +#endif /* LIGHTNING_COMMON_PSBT_KEYPATH_H */ From 3abe22213c5605d875679690b3d0a2c4a889703b Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 23 Dec 2021 12:03:56 -0800 Subject: [PATCH 0440/1530] hsmd: Augment call to hsmd_sign_withdrawal_tx with wallet index metadata --- lightningd/Makefile | 1 + wallet/test/Makefile | 1 + wallet/walletrpc.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/lightningd/Makefile b/lightningd/Makefile index 844d2748f333..8876dc55c929 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -115,6 +115,7 @@ LIGHTNINGD_COMMON_OBJS := \ common/penalty_base.o \ common/per_peer_state.o \ common/permute_tx.o \ + common/psbt_keypath.o \ common/psbt_open.o \ common/pseudorand.o \ common/random_select.o \ diff --git a/wallet/test/Makefile b/wallet/test/Makefile index 278287cfb346..c6c26ac914d2 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -21,6 +21,7 @@ WALLET_TEST_COMMON_OBJS := \ common/node_id.o \ common/onionreply.o \ common/key_derive.o \ + common/psbt_keypath.o \ common/pseudorand.o \ common/setup.o \ common/timeout.o \ diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 1d32d89deb67..fb2d3815b22c 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -658,6 +659,35 @@ static struct command_result *match_psbt_inputs_to_utxos(struct command *cmd, return NULL; } +static void match_psbt_outputs_to_wallet(struct wally_psbt *psbt, + struct wallet *w) +{ + assert(psbt->tx->num_outputs == psbt->num_outputs); + tal_wally_start(); + for (size_t outndx = 0; outndx < psbt->num_outputs; ++outndx) { + u32 index; + bool is_p2sh; + const u8 *script; + struct ext_key ext; + + script = wally_tx_output_get_script(tmpctx, + &psbt->tx->outputs[outndx]); + if (!script) + continue; + + if (!wallet_can_spend(w, script, &index, &is_p2sh)) + continue; + + if (bip32_key_from_parent( + w->bip32_base, index, BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) { + abort(); + } + + psbt_set_keypath(index, &ext, &psbt->outputs[outndx].keypaths); + } + tal_wally_end(psbt); +} + static struct command_result *param_input_numbers(struct command *cmd, const char *name, const char *buffer, @@ -721,6 +751,9 @@ static struct command_result *json_signpsbt(struct command *cmd, return command_fail(cmd, LIGHTNINGD, "No wallet inputs to sign"); + /* Update the keypaths on any outputs that are in our wallet (change addresses). */ + match_psbt_outputs_to_wallet(psbt, cmd->ld->wallet); + /* FIXME: hsm will sign almost anything, but it should really * fail cleanly (not abort!) and let us report the error here. */ u8 *msg = towire_hsmd_sign_withdrawal(cmd, From 8f56f9680159765c41f53a915061f16f68de0e99 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Thu, 23 Dec 2021 12:16:35 -0800 Subject: [PATCH 0441/1530] hsmd: Add wallet index metadata to existing messages --- channeld/Makefile | 1 + channeld/channeld.c | 24 +++++++++- channeld/channeld_wire.csv | 6 +++ channeld/watchtower.c | 6 +++ channeld/watchtower.h | 4 ++ closingd/Makefile | 1 + closingd/closingd.c | 42 ++++++++++++++++- closingd/closingd_wire.csv | 4 ++ common/close_tx.c | 7 +++ common/close_tx.h | 4 ++ lightningd/channel_control.c | 15 ++++++ lightningd/closing_control.c | 66 ++++++++++++++++++++++++++- lightningd/onchain_control.c | 13 ++++++ onchaind/Makefile | 1 + onchaind/onchaind.c | 12 ++++- onchaind/onchaind_wire.csv | 3 ++ onchaind/test/Makefile | 1 + onchaind/test/run-grind_feerate-bug.c | 2 +- onchaind/test/run-grind_feerate.c | 2 +- 19 files changed, 206 insertions(+), 8 deletions(-) diff --git a/channeld/Makefile b/channeld/Makefile index bef41c69a92a..bdab8ffa0c13 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -73,6 +73,7 @@ CHANNELD_COMMON_OBJS := \ common/per_peer_state.o \ common/permute_tx.o \ common/ping.o \ + common/psbt_keypath.o \ common/psbt_open.o \ common/private_channel_announcement.o \ common/pseudorand.o \ diff --git a/channeld/channeld.c b/channeld/channeld.c index f47dc404e1b4..1d85ff2ed71a 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -122,6 +123,8 @@ struct peer { u32 fee_per_satoshi; /* The scriptpubkey to use for shutting down. */ + u32 *final_index; + struct ext_key *final_ext_key; u8 *final_scriptpubkey; /* If master told us to shut down */ @@ -1722,6 +1725,7 @@ static u8 *got_revoke_msg(struct peer *peer, u64 revoke_num, if (pbase) { ptx = penalty_tx_create( NULL, peer->channel, peer->feerate_penalty, + peer->final_index, peer->final_ext_key, peer->final_scriptpubkey, per_commitment_secret, &pbase->txid, pbase->outnum, pbase->amount, HSM_FD); @@ -3470,12 +3474,23 @@ static void handle_fail(struct peer *peer, const u8 *inmsg) static void handle_shutdown_cmd(struct peer *peer, const u8 *inmsg) { + u32 *final_index; + struct ext_key *final_ext_key; u8 *local_shutdown_script; - if (!fromwire_channeld_send_shutdown(peer, inmsg, &local_shutdown_script, + if (!fromwire_channeld_send_shutdown(peer, inmsg, + &final_index, + &final_ext_key, + &local_shutdown_script, &peer->shutdown_wrong_funding)) master_badmsg(WIRE_CHANNELD_SEND_SHUTDOWN, inmsg); + tal_free(peer->final_index); + peer->final_index = final_index; + + tal_free(peer->final_ext_key); + peer->final_ext_key = final_ext_key; + tal_free(peer->final_scriptpubkey); peer->final_scriptpubkey = local_shutdown_script; @@ -3652,6 +3667,8 @@ static void init_channel(struct peer *peer) enum side opener; struct existing_htlc **htlcs; bool reconnected; + u32 final_index; + struct ext_key final_ext_key; u8 *fwd_msg; const u8 *msg; struct fee_states *fee_states; @@ -3715,6 +3732,8 @@ static void init_channel(struct peer *peer) &reconnected, &peer->send_shutdown, &peer->shutdown_sent[REMOTE], + &final_index, + &final_ext_key, &peer->final_scriptpubkey, &peer->channel_flags, &fwd_msg, @@ -3734,6 +3753,9 @@ static void init_channel(struct peer *peer) master_badmsg(WIRE_CHANNELD_INIT, msg); } + peer->final_index = tal_dup(peer, u32, &final_index); + peer->final_ext_key = tal_dup(peer, struct ext_key, &final_ext_key); + #if DEVELOPER peer->dev_disable_commit = dev_disable_commit; peer->dev_fast_gossip = dev_fast_gossip; diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index a444125ce069..b84ca73c428e 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -56,6 +57,8 @@ msgdata,channeld_init,funding_short_id,short_channel_id, msgdata,channeld_init,reestablish,bool, msgdata,channeld_init,send_shutdown,bool, msgdata,channeld_init,remote_shutdown_received,bool, +msgdata,channeld_init,final_index,u32, +msgdata,channeld_init,final_ext_key,ext_key, msgdata,channeld_init,final_scriptpubkey_len,u16, msgdata,channeld_init,final_scriptpubkey,u8,final_scriptpubkey_len msgdata,channeld_init,flags,u8, @@ -173,8 +176,11 @@ msgdata,channeld_got_revoke,penalty_tx,?bitcoin_tx, # (eg. if we sent another commitment_signed, that would implicitly ack). msgtype,channeld_got_revoke_reply,1122 +#include # Tell peer to shut down channel. msgtype,channeld_send_shutdown,1023 +msgdata,channeld_send_shutdown,final_index,?u32, +msgdata,channeld_send_shutdown,final_ext_key,?ext_key, msgdata,channeld_send_shutdown,shutdown_len,u16, msgdata,channeld_send_shutdown,shutdown_scriptpubkey,u8,shutdown_len msgdata,channeld_send_shutdown,wrong_funding,?bitcoin_outpoint, diff --git a/channeld/watchtower.c b/channeld/watchtower.c index e91dc4f89699..269b4f405ad0 100644 --- a/channeld/watchtower.c +++ b/channeld/watchtower.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,8 @@ const struct bitcoin_tx * penalty_tx_create(const tal_t *ctx, const struct channel *channel, u32 penalty_feerate, + u32 *final_index, + struct ext_key *final_ext_key, u8 *final_scriptpubkey, const struct secret *revocation_preimage, const struct bitcoin_txid *commitment_txid, @@ -76,6 +79,9 @@ penalty_tx_create(const tal_t *ctx, NULL, to_them_sats, NULL, wscript); bitcoin_tx_add_output(tx, final_scriptpubkey, NULL, to_them_sats); + assert((final_index == NULL) == (final_ext_key == NULL)); + if (final_index) + psbt_add_keypath_to_last_output(tx, *final_index, final_ext_key); /* Worst-case sig is 73 bytes */ weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); diff --git a/channeld/watchtower.h b/channeld/watchtower.h index 90fe01e90b49..e4e339b7aba4 100644 --- a/channeld/watchtower.h +++ b/channeld/watchtower.h @@ -3,10 +3,14 @@ #include "config.h" #include +struct ext_key; + const struct bitcoin_tx * penalty_tx_create(const tal_t *ctx, const struct channel *channel, u32 penalty_feerate, + u32 *final_index, + struct ext_key *final_ext_key, u8 *final_scriptpubkey, const struct secret *revocation_preimage, const struct bitcoin_txid *commitment_txid, diff --git a/closingd/Makefile b/closingd/Makefile index 5c3373471137..7ee284fbb98e 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -45,6 +45,7 @@ CLOSINGD_COMMON_OBJS := \ common/per_peer_state.o \ common/permute_tx.o \ common/ping.o \ + common/psbt_keypath.o \ common/psbt_open.o \ common/pseudorand.o \ common/status_wiregen.o \ diff --git a/closingd/closingd.c b/closingd/closingd.c index 05c45cfc2074..345f2c26c26e 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,8 @@ static struct bitcoin_tx *close_tx(const tal_t *ctx, const struct chainparams *chainparams, struct per_peer_state *pps, const struct channel_id *channel_id, + u32 *local_wallet_index, + const struct ext_key *local_wallet_ext_key, u8 *scriptpubkey[NUM_SIDES], const struct bitcoin_outpoint *funding, struct amount_sat funding_sats, @@ -83,6 +86,7 @@ static struct bitcoin_tx *close_tx(const tal_t *ctx, /* FIXME: We need to allow this! */ tx = create_close_tx(ctx, chainparams, + local_wallet_index, local_wallet_ext_key, scriptpubkey[LOCAL], scriptpubkey[REMOTE], funding_wscript, funding, @@ -130,6 +134,8 @@ static void send_offer(struct per_peer_state *pps, const struct channel_id *channel_id, const struct pubkey funding_pubkey[NUM_SIDES], const u8 *funding_wscript, + u32 *local_wallet_index, + const struct ext_key *local_wallet_ext_key, u8 *scriptpubkey[NUM_SIDES], const struct bitcoin_outpoint *funding, struct amount_sat funding_sats, @@ -152,6 +158,8 @@ static void send_offer(struct per_peer_state *pps, * #3](03-transactions.md#closing-transaction). */ tx = close_tx(tmpctx, chainparams, pps, channel_id, + local_wallet_index, + local_wallet_ext_key, scriptpubkey, funding, funding_sats, @@ -225,6 +233,8 @@ receive_offer(struct per_peer_state *pps, const struct channel_id *channel_id, const struct pubkey funding_pubkey[NUM_SIDES], const u8 *funding_wscript, + u32 *local_wallet_index, + const struct ext_key *local_wallet_ext_key, u8 *scriptpubkey[NUM_SIDES], const struct bitcoin_outpoint *funding, struct amount_sat funding_sats, @@ -285,6 +295,8 @@ receive_offer(struct per_peer_state *pps, * - MUST fail the connection. */ tx = close_tx(tmpctx, chainparams, pps, channel_id, + local_wallet_index, + local_wallet_ext_key, scriptpubkey, funding, funding_sats, @@ -315,6 +327,8 @@ receive_offer(struct per_peer_state *pps, * - MAY eliminate its own output. */ trimmed = close_tx(tmpctx, chainparams, pps, channel_id, + local_wallet_index, + local_wallet_ext_key, scriptpubkey, funding, funding_sats, @@ -559,7 +573,9 @@ static size_t closing_tx_weight_estimate(u8 *scriptpubkey[NUM_SIDES], const u8 *funding_wscript, const struct amount_sat *out, struct amount_sat funding_sats, - struct amount_sat dust_limit) + struct amount_sat dust_limit, + u32 *local_wallet_index, + const struct ext_key *local_wallet_ext_key) { /* We create a dummy close */ struct bitcoin_tx *tx; @@ -567,6 +583,7 @@ static size_t closing_tx_weight_estimate(u8 *scriptpubkey[NUM_SIDES], memset(&dummy_funding, 0, sizeof(dummy_funding)); tx = create_close_tx(tmpctx, chainparams, + local_wallet_index, local_wallet_ext_key, scriptpubkey[LOCAL], scriptpubkey[REMOTE], funding_wscript, &dummy_funding, @@ -671,6 +688,8 @@ static void do_quickclose(struct amount_sat offer[NUM_SIDES], const struct channel_id *channel_id, const struct pubkey funding_pubkey[NUM_SIDES], const u8 *funding_wscript, + u32 *local_wallet_index, + const struct ext_key *local_wallet_ext_key, u8 *scriptpubkey[NUM_SIDES], const struct bitcoin_outpoint *funding, struct amount_sat funding_sats, @@ -750,6 +769,7 @@ static void do_quickclose(struct amount_sat offer[NUM_SIDES], offer[LOCAL] = offer[REMOTE]; send_offer(pps, chainparams, channel_id, funding_pubkey, funding_wscript, + local_wallet_index, local_wallet_ext_key, scriptpubkey, funding, funding_sats, out, opener, our_dust_limit, @@ -789,6 +809,7 @@ static void do_quickclose(struct amount_sat offer[NUM_SIDES], } send_offer(pps, chainparams, channel_id, funding_pubkey, funding_wscript, + local_wallet_index, local_wallet_ext_key, scriptpubkey, funding, funding_sats, out, opener, our_dust_limit, @@ -802,6 +823,7 @@ static void do_quickclose(struct amount_sat offer[NUM_SIDES], = receive_offer(pps, chainparams, channel_id, funding_pubkey, funding_wscript, + local_wallet_index, local_wallet_ext_key, scriptpubkey, funding, funding_sats, out, opener, @@ -851,6 +873,8 @@ int main(int argc, char *argv[]) u32 min_feerate, initial_feerate, *max_feerate; struct feerange feerange; enum side opener; + u32 *local_wallet_index; + struct ext_key *local_wallet_ext_key; u8 *scriptpubkey[NUM_SIDES], *funding_wscript; u64 fee_negotiation_step; u8 fee_negotiation_step_unit; @@ -879,6 +903,8 @@ int main(int argc, char *argv[]) &our_dust_limit, &min_feerate, &initial_feerate, &max_feerate, &commitment_fee, + &local_wallet_index, + &local_wallet_ext_key, &scriptpubkey[LOCAL], &scriptpubkey[REMOTE], &fee_negotiation_step, @@ -899,7 +925,9 @@ int main(int argc, char *argv[]) calc_fee_bounds(closing_tx_weight_estimate(scriptpubkey, funding_wscript, out, funding_sats, - our_dust_limit), + our_dust_limit, + local_wallet_index, + local_wallet_ext_key), min_feerate, initial_feerate, max_feerate, commitment_fee, funding_sats, opener, &min_fee_to_accept, &offer[LOCAL], &max_fee_to_accept); @@ -957,6 +985,7 @@ int main(int argc, char *argv[]) if (whose_turn == LOCAL) { send_offer(pps, chainparams, &channel_id, funding_pubkey, funding_wscript, + local_wallet_index, local_wallet_ext_key, scriptpubkey, &funding, funding_sats, out, opener, our_dust_limit, @@ -978,6 +1007,8 @@ int main(int argc, char *argv[]) = receive_offer(pps, chainparams, &channel_id, funding_pubkey, funding_wscript, + local_wallet_index, + local_wallet_ext_key, scriptpubkey, &funding, funding_sats, out, opener, @@ -991,6 +1022,7 @@ int main(int argc, char *argv[]) do_quickclose(offer, pps, &channel_id, funding_pubkey, funding_wscript, + local_wallet_index, local_wallet_ext_key, scriptpubkey, &funding, funding_sats, out, opener, @@ -1024,6 +1056,8 @@ int main(int argc, char *argv[]) fee_negotiation_step_unit); send_offer(pps, chainparams, &channel_id, funding_pubkey, funding_wscript, + local_wallet_index, + local_wallet_ext_key, scriptpubkey, &funding, funding_sats, out, opener, our_dust_limit, @@ -1040,6 +1074,8 @@ int main(int argc, char *argv[]) = receive_offer(pps, chainparams, &channel_id, funding_pubkey, funding_wscript, + local_wallet_index, + local_wallet_ext_key, scriptpubkey, &funding, funding_sats, out, opener, @@ -1064,6 +1100,8 @@ int main(int argc, char *argv[]) tal_free(our_feerange); tal_free(their_feerange); tal_free(max_feerate); + tal_free(local_wallet_index); + tal_free(local_wallet_ext_key); closing_dev_memleak(ctx, scriptpubkey, funding_wscript); #endif diff --git a/closingd/closingd_wire.csv b/closingd/closingd_wire.csv index 94ab71c757e0..29f6eccabbc1 100644 --- a/closingd/closingd_wire.csv +++ b/closingd/closingd_wire.csv @@ -1,8 +1,10 @@ #include +#include #include #include #include #include +#include # Begin! (passes peer fd, gossipd-client fd) msgtype,closingd_init,2001 msgdata,closingd_init,chainparams,chainparams, @@ -19,6 +21,8 @@ msgdata,closingd_init,min_feerate_perksipa,u32, msgdata,closingd_init,preferred_feerate_perksipa,u32, msgdata,closingd_init,max_feerate_perksipa,?u32, msgdata,closingd_init,fee_limit_satoshi,amount_sat, +msgdata,closingd_init,local_wallet_index,?u32, +msgdata,closingd_init,local_wallet_ext_key,?ext_key, msgdata,closingd_init,local_scriptpubkey_len,u16, msgdata,closingd_init,local_scriptpubkey,u8,local_scriptpubkey_len msgdata,closingd_init,remote_scriptpubkey_len,u16, diff --git a/common/close_tx.c b/common/close_tx.c index e1eb16ba6838..48e54d1e9c02 100644 --- a/common/close_tx.c +++ b/common/close_tx.c @@ -3,10 +3,13 @@ #include #include #include +#include #include struct bitcoin_tx *create_close_tx(const tal_t *ctx, const struct chainparams *chainparams, + u32 *local_wallet_index, + const struct ext_key *local_wallet_ext_key, const u8 *our_script, const u8 *their_script, const u8 *funding_wscript, @@ -46,6 +49,10 @@ struct bitcoin_tx *create_close_tx(const tal_t *ctx, script = tal_dup_talarr(tx, u8, our_script); /* One output is to us. */ bitcoin_tx_add_output(tx, script, NULL, to_us); + assert((local_wallet_index == NULL) == (local_wallet_ext_key == NULL)); + if (local_wallet_index) + psbt_add_keypath_to_last_output( + tx, *local_wallet_index, local_wallet_ext_key); num_outputs++; } diff --git a/common/close_tx.h b/common/close_tx.h index 6f9886556fe5..2c3aad61d4b6 100644 --- a/common/close_tx.h +++ b/common/close_tx.h @@ -3,10 +3,14 @@ #include "config.h" #include +struct ext_key; + /* Create close tx to spend the anchor tx output; doesn't fill in * input scriptsig. */ struct bitcoin_tx *create_close_tx(const tal_t *ctx, const struct chainparams *chainparams, + u32 *local_wallet_index, + const struct ext_key *local_wallet_ext_key, const u8 *our_script, const u8 *their_script, const u8 *funding_wscript, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 0d70aa0393bf..e07db69d09da 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -22,6 +22,7 @@ #include #include #include +#include static void update_feerates(struct lightningd *ld, struct channel *channel) { @@ -665,6 +666,18 @@ void peer_start_channeld(struct channel *channel, pbases = wallet_penalty_base_load_for_channel( tmpctx, channel->peer->ld->wallet, channel->dbid); + struct ext_key final_ext_key; + if (bip32_key_from_parent( + ld->wallet->bip32_base, + channel->final_key_idx, + BIP32_FLAG_KEY_PUBLIC, + &final_ext_key) != WALLY_OK) { + channel_internal_error(channel, + "Could not derive final_ext_key %"PRIu64, + channel->final_key_idx); + return; + } + initmsg = towire_channeld_init(tmpctx, chainparams, ld->our_features, @@ -713,6 +726,8 @@ void peer_start_channeld(struct channel *channel, || channel->state == CLOSINGD_SIGEXCHANGE || channel_closed(channel), channel->shutdown_scriptpubkey[REMOTE] != NULL, + channel->final_key_idx, + &final_ext_key, channel->shutdown_scriptpubkey[LOCAL], channel->channel_flags, fwd_msg, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 0590e154c057..ef85657f77de 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -38,6 +38,7 @@ #include #include #include +#include struct close_command { /* Inside struct lightningd close_commands. */ @@ -443,6 +444,29 @@ void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd) return; } + // Determine the wallet index for our output or NULL if not found. + u32 *local_wallet_index = NULL; + struct ext_key *local_wallet_ext_key = NULL; + u32 index_val; + struct ext_key ext_key_val; + bool is_p2sh; + if (wallet_can_spend( + ld->wallet, + channel->shutdown_scriptpubkey[LOCAL], + &index_val, + &is_p2sh)) { + if (bip32_key_from_parent( + ld->wallet->bip32_base, + index_val, + BIP32_FLAG_KEY_PUBLIC, + &ext_key_val) != WALLY_OK) { + channel_internal_error(channel, "Could not derive ext public key"); + return; + } + local_wallet_index = &index_val; + local_wallet_ext_key = &ext_key_val; + } + initmsg = towire_closingd_init(tmpctx, chainparams, &channel->cid, @@ -456,6 +480,8 @@ void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd) channel->our_config.dust_limit, min_feerate, feerate, max_feerate, feelimit, + local_wallet_index, + local_wallet_ext_key, channel->shutdown_scriptpubkey[LOCAL], channel->shutdown_scriptpubkey[REMOTE], channel->closing_fee_negotiation_step, @@ -527,6 +553,8 @@ static struct command_result *json_close(struct command *cmd, struct channel *channel; unsigned int *timeout; const u8 *close_to_script = NULL; + u32 *final_index; + u32 index_val; bool close_script_set, wrong_funding_changed, *force_lease_close; const char *fee_negotiation_step_str; struct bitcoin_outpoint *wrong_funding; @@ -584,6 +612,14 @@ static struct command_result *json_close(struct command *cmd, channel->lease_expiry, get_block_height(cmd->ld->topology)); + /* Set the wallet index to the default value; it is updated + * below if the close_to_script is found to be in the + * wallet. If the close_to_script is not in the wallet + * final_index will be set to NULL instead.*/ + assert(channel->final_key_idx <= UINT32_MAX); + index_val = (u32) channel->final_key_idx; + final_index = &index_val; + /* If we've set a local shutdown script for this peer, and it's not the * default upfront script, try to close to a different channel. * Error is an operator error */ @@ -613,6 +649,17 @@ static struct command_result *json_close(struct command *cmd, channel->shutdown_scriptpubkey[LOCAL] = tal_steal(channel, cast_const(u8 *, close_to_script)); close_script_set = true; + /* Is the close script in our wallet? */ + bool is_p2sh; + if (wallet_can_spend( + cmd->ld->wallet, + channel->shutdown_scriptpubkey[LOCAL], + &index_val, + &is_p2sh)) { + /* index_val has been set to the discovered wallet index */ + } else { + final_index = NULL; + } } else if (!channel->shutdown_scriptpubkey[LOCAL]) { channel->shutdown_scriptpubkey[LOCAL] = p2wpkh_for_keyidx(channel, cmd->ld, channel->final_key_idx); @@ -735,11 +782,28 @@ static struct command_result *json_close(struct command *cmd, msg = towire_dualopend_send_shutdown( NULL, channel->shutdown_scriptpubkey[LOCAL]); - } else + } else { + struct ext_key ext_key_val; + struct ext_key *final_ext_key = NULL; + if (final_index) { + if (bip32_key_from_parent( + channel->peer->ld->wallet->bip32_base, + *final_index, + BIP32_FLAG_KEY_PUBLIC, + &ext_key_val) != WALLY_OK) { + return command_fail( + cmd, LIGHTNINGD, + "Could not derive final_ext_key"); + } + final_ext_key = &ext_key_val; + } msg = towire_channeld_send_shutdown( NULL, + final_index, + final_ext_key, channel->shutdown_scriptpubkey[LOCAL], channel->shutdown_wrong_funding); + } subd_send_msg(channel->owner, take(msg)); } diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index f980565c64b3..850ae19133f5 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -15,6 +15,7 @@ #include #include #include +#include /* We dump all the known preimages when onchaind starts up. */ static void onchaind_tell_fulfill(struct channel *channel) @@ -642,6 +643,16 @@ enum watch_result onchaind_funding_spent(struct channel *channel, channel->final_key_idx); return KEEP_WATCHING; } + struct ext_key final_wallet_ext_key; + if (bip32_key_from_parent( + ld->wallet->bip32_base, + channel->final_key_idx, + BIP32_FLAG_KEY_PUBLIC, + &final_wallet_ext_key) != WALLY_OK) { + log_broken(channel->log, "Could not derive final_wallet_ext_key %"PRIu64, + channel->final_key_idx); + return KEEP_WATCHING; + } /* This could be a mutual close, but it doesn't matter. */ bitcoin_txid(channel->last_tx, &our_last_txid); @@ -706,6 +717,8 @@ enum watch_result onchaind_funding_spent(struct channel *channel, &our_last_txid, channel->shutdown_scriptpubkey[LOCAL], channel->shutdown_scriptpubkey[REMOTE], + channel->final_key_idx, + &final_wallet_ext_key, &final_key, channel->opener, &channel->local_basepoints, diff --git a/onchaind/Makefile b/onchaind/Makefile index 1db28e425082..28c8b6a32f10 100644 --- a/onchaind/Makefile +++ b/onchaind/Makefile @@ -54,6 +54,7 @@ ONCHAIND_COMMON_OBJS := \ common/onionreply.o \ common/peer_billboard.o \ common/permute_tx.o \ + common/psbt_keypath.o \ common/psbt_open.o \ common/pseudorand.o \ common/setup.o \ diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index d210590ada1a..df8902cbc106 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include "onchain_types_names_gen.h" @@ -53,6 +55,8 @@ static struct amount_sat dust_limit; static u32 to_self_delay[NUM_SIDES]; /* Where we send money to (our wallet) */ +static u32 our_wallet_index; +static struct ext_key our_wallet_ext_key; static struct pubkey our_wallet_pubkey; /* Their revocation secret (only if they cheated). */ @@ -618,6 +622,7 @@ static struct bitcoin_tx *tx_to_us(const tal_t *ctx, bitcoin_tx_add_output( tx, scriptpubkey_p2wpkh(tmpctx, &our_wallet_pubkey), NULL, out->sat); + psbt_add_keypath_to_last_output(tx, our_wallet_index, &our_wallet_ext_key); /* Worst-case sig is 73 bytes */ weight = bitcoin_tx_weight(tx) + 1 + 3 + 73 + 0 + tal_count(wscript); @@ -738,13 +743,14 @@ replace_penalty_tx_to_us(const tal_t *ctx, BITCOIN_TX_RBF_SEQUENCE, NULL, input_amount, NULL, input_wscript); /* Reconstruct the output with a smaller amount. */ - if (amount_sat_greater(*output_amount, dust_limit)) + if (amount_sat_greater(*output_amount, dust_limit)) { bitcoin_tx_add_output(tx, scriptpubkey_p2wpkh(tx, &our_wallet_pubkey), NULL, *output_amount); - else { + psbt_add_keypath_to_last_output(tx, our_wallet_index, &our_wallet_ext_key); + } else { bitcoin_tx_add_output(tx, scriptpubkey_opreturn_padded(tx), NULL, @@ -3845,6 +3851,8 @@ int main(int argc, char *argv[]) &our_broadcast_txid, &scriptpubkey[LOCAL], &scriptpubkey[REMOTE], + &our_wallet_index, + &our_wallet_ext_key, &our_wallet_pubkey, &opener, &basepoints[LOCAL], diff --git a/onchaind/onchaind_wire.csv b/onchaind/onchaind_wire.csv index a5204086c7db..e2dfd031620a 100644 --- a/onchaind/onchaind_wire.csv +++ b/onchaind/onchaind_wire.csv @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -29,6 +30,8 @@ msgdata,onchaind_init,local_scriptpubkey_len,u16, msgdata,onchaind_init,local_scriptpubkey,u8,local_scriptpubkey_len msgdata,onchaind_init,remote_scriptpubkey_len,u16, msgdata,onchaind_init,remote_scriptpubkey,u8,remote_scriptpubkey_len +msgdata,onchaind_init,ourwallet_index,u32, +msgdata,onchaind_init,ourwallet_ext_key,ext_key, msgdata,onchaind_init,ourwallet_pubkey,pubkey, # We need these two for commit number obscurer msgdata,onchaind_init,opener,enum side, diff --git a/onchaind/test/Makefile b/onchaind/test/Makefile index 96fd2b86537a..2bb2269e044a 100644 --- a/onchaind/test/Makefile +++ b/onchaind/test/Makefile @@ -13,6 +13,7 @@ ONCHAIND_TEST_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ common/features.o \ + common/psbt_keypath.o \ common/pseudorand.o \ common/setup.o \ common/type_to_string.o \ diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 0d17accd7d62..8726dbaf3782 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -49,7 +49,7 @@ bool fromwire_onchaind_dev_memleak(const void *p UNNEEDED) bool fromwire_onchaind_htlcs(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct htlc_stub **htlc UNNEEDED, bool **tell_if_missing UNNEEDED, bool **tell_immediately UNNEEDED) { fprintf(stderr, "fromwire_onchaind_htlcs called!\n"); abort(); } /* Generated stub for fromwire_onchaind_init */ -bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, u32 *min_relay_feerate UNNEEDED) +bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, u32 *ourwallet_index UNNEEDED, struct ext_key *ourwallet_ext_key UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, u32 *min_relay_feerate UNNEEDED) { fprintf(stderr, "fromwire_onchaind_init called!\n"); abort(); } /* Generated stub for fromwire_onchaind_known_preimage */ bool fromwire_onchaind_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED) diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 9e3be09040fb..e800b371cefa 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -54,7 +54,7 @@ bool fromwire_onchaind_dev_memleak(const void *p UNNEEDED) bool fromwire_onchaind_htlcs(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct htlc_stub **htlc UNNEEDED, bool **tell_if_missing UNNEEDED, bool **tell_immediately UNNEEDED) { fprintf(stderr, "fromwire_onchaind_htlcs called!\n"); abort(); } /* Generated stub for fromwire_onchaind_init */ -bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, u32 *min_relay_feerate UNNEEDED) +bool fromwire_onchaind_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct shachain *shachain UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct amount_sat *funding_amount_satoshi UNNEEDED, struct amount_msat *our_msat UNNEEDED, struct pubkey *old_remote_per_commitment_point UNNEEDED, struct pubkey *remote_per_commitment_point UNNEEDED, u32 *local_to_self_delay UNNEEDED, u32 *remote_to_self_delay UNNEEDED, u32 *delayed_to_us_feerate UNNEEDED, u32 *htlc_feerate UNNEEDED, u32 *penalty_feerate UNNEEDED, struct amount_sat *local_dust_limit_satoshi UNNEEDED, struct bitcoin_txid *our_broadcast_txid UNNEEDED, u8 **local_scriptpubkey UNNEEDED, u8 **remote_scriptpubkey UNNEEDED, u32 *ourwallet_index UNNEEDED, struct ext_key *ourwallet_ext_key UNNEEDED, struct pubkey *ourwallet_pubkey UNNEEDED, enum side *opener UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct basepoints *remote_basepoints UNNEEDED, struct tx_parts **tx_parts UNNEEDED, u32 *locktime UNNEEDED, u32 *tx_blockheight UNNEEDED, u32 *reasonable_depth UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, u32 *min_possible_feerate UNNEEDED, u32 *max_possible_feerate UNNEEDED, struct pubkey **possible_remote_per_commit_point UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED, struct pubkey *remote_funding_pubkey UNNEEDED, u64 *local_static_remotekey_start UNNEEDED, u64 *remote_static_remotekey_start UNNEEDED, bool *option_anchor_outputs UNNEEDED, u32 *min_relay_feerate UNNEEDED) { fprintf(stderr, "fromwire_onchaind_init called!\n"); abort(); } /* Generated stub for fromwire_onchaind_known_preimage */ bool fromwire_onchaind_known_preimage(const void *p UNNEEDED, struct preimage *preimage UNNEEDED) From c357411337010d2c1f4a5c3b8e0c3a28b6b278c9 Mon Sep 17 00:00:00 2001 From: Marnix <93143998+MarnixCroes@users.noreply.github.com> Date: Mon, 31 Jan 2022 11:43:39 +0000 Subject: [PATCH 0442/1530] Update LICENSE Update LICENSE to 2022 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 582800a0a8c2..15ef029efec9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ Note: the modules in the ccan/ directory have their own licenses, but the rest of the code is covered by the following (BSD-MIT) license: - Copyright Rusty Russell (Blockstream) 2015. + Copyright Rusty Russell (Blockstream) 2015-2022. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2c55c27ad482487861759cd45c44ca472343ad4f Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 3 Feb 2022 09:21:41 +0100 Subject: [PATCH 0443/1530] doc: remove ambiguity for functionality enabled after a release version Changelog-None: remove ambiguity for functionality enabled after a release version Signed-off-by: Vincenzo Palazzo --- doc/BACKUP.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/BACKUP.md b/doc/BACKUP.md index c6346e7002c7..d0c76c1fefe3 100644 --- a/doc/BACKUP.md +++ b/doc/BACKUP.md @@ -86,7 +86,7 @@ backup strategies below. `/!\` WHO SHOULD DO THIS: Casual users. -`/!\` **CAUTION** `/!\` This technique is only supported on 0.10.3 +`/!\` **CAUTION** `/!\` This technique is only supported after the version v0.10.2 (not included) or later. On earlier versions, the `:` character is not special and will be considered part of the path of the database file. @@ -234,7 +234,7 @@ such as (not an exhaustive list!): of multiple physical media. * BTRFS RAID-1 or RAID-10, a filesystem built into Linux. * ZFS RAID-Z, a filesystem that cannot be legally distributed with the Linux - kernel, but can be distributed in a BSD system, and can be installed + kernel, but can be distributed in a BSD system, and can be installed on Linux with some extra effort, see [ZFSonLinux](https://zfsonlinux.org). @@ -430,7 +430,7 @@ Consider using `--wallet=sqlite3://${main}:${backup}` above, instead. [issue 4857]: https://github.com/ElementsProject/lightning/issues/4857 One of the simpler things on any system is to use Litestream to replicate the SQLite database. -It continuously streams SQLite changes to file or external storage - the cloud storage option +It continuously streams SQLite changes to file or external storage - the cloud storage option should not be used. Backups/replication should not be on the same disk as the original SQLite DB. From e92176248e9430609d4352ef80a3bd94c2d48af7 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 23 Feb 2022 17:58:41 +0100 Subject: [PATCH 0444/1530] chore: fix typo announcable -> announceable "announcable" is a common misspelling of "announceable", see: https://en.wiktionary.org/wiki/announcable --- connectd/connectd.c | 40 ++++++++++++++++++------------------ connectd/connectd_wire.csv | 6 +++--- gossipd/gossip_generation.c | 4 ++-- gossipd/gossipd.c | 2 +- gossipd/gossipd.h | 2 +- gossipd/gossipd_wire.csv | 4 ++-- lightningd/connect_control.c | 2 +- lightningd/gossip_control.c | 2 +- lightningd/lightningd.h | 4 ++-- lightningd/options.c | 2 +- lightningd/peer_control.c | 4 ++-- tests/test_misc.py | 2 +- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 68ea866b3aed..c47e11b9e120 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1177,13 +1177,13 @@ static bool public_address(struct daemon *daemon, struct wireaddr *wireaddr) return address_routable(wireaddr, daemon->dev_allow_localhost); } -static void add_announcable(struct wireaddr **announcable, - const struct wireaddr *addr) +static void add_announceable(struct wireaddr **announceable, + const struct wireaddr *addr) { /*~ utils.h contains a convenience macro tal_arr_expand which * reallocates a tal_arr to make it one longer, then returns a pointer * to the (new) last element. */ - tal_arr_expand(announcable, *addr); + tal_arr_expand(announceable, *addr); } /*~ ccan/asort provides a type-safe sorting function; it requires a comparison @@ -1238,7 +1238,7 @@ static bool want_tor(const struct wireaddr_internal *proposed_wireaddr) * announce, ones we announce but don't bind to, and ones we bind to and * announce if they seem to be public addresses. * - * This routine sorts out the mess: it populates the *announcable array, + * This routine sorts out the mess: it populates the *announceable array, * and returns the addresses we bound to (by convention, return is allocated * off `ctx` argument). * @@ -1254,7 +1254,7 @@ setup_listeners(const tal_t *ctx, /* For each one, listen, announce or both */ const enum addr_listen_announce *proposed_listen_announce, const char *tor_password, - struct wireaddr **announcable, + struct wireaddr **announceable, char **errstr) { struct sockaddr_un addrun; @@ -1267,7 +1267,7 @@ setup_listeners(const tal_t *ctx, /* Start with empty arrays, for tal_arr_expand() */ listen_fds = tal_arr(ctx, const struct listen_fd *, 0); - *announcable = tal_arr(ctx, struct wireaddr, 0); + *announceable = tal_arr(ctx, struct wireaddr, 0); /* Add addresses we've explicitly been told to *first*: implicit * addresses will be discarded then if we have multiple. */ @@ -1282,7 +1282,7 @@ setup_listeners(const tal_t *ctx, /* You can only announce wiretypes, not internal formats! */ assert(proposed_wireaddr[i].itype == ADDR_INTERNAL_WIREADDR); - add_announcable(announcable, &wa.u.wireaddr); + add_announceable(announceable, &wa.u.wireaddr); } /* Now look for listening addresses. */ @@ -1338,8 +1338,8 @@ setup_listeners(const tal_t *ctx, tal_steal(listen_fds, lfd)); if (announce && public_address(daemon, &wa.u.wireaddr)) - add_announcable(announcable, - &wa.u.wireaddr); + add_announceable(announceable, + &wa.u.wireaddr); } ipv6_ok = (lfd != NULL); @@ -1356,7 +1356,7 @@ setup_listeners(const tal_t *ctx, tal_steal(listen_fds, lfd)); if (announce && public_address(daemon, &wa.u.wireaddr)) - add_announcable(announcable, + add_announceable(announceable, &wa.u.wireaddr); } else if (!ipv6_ok) { /* Both failed, return now, errstr set. */ @@ -1372,7 +1372,7 @@ setup_listeners(const tal_t *ctx, return NULL; tal_arr_expand(&listen_fds, tal_steal(listen_fds, lfd)); if (announce && public_address(daemon, &wa.u.wireaddr)) - add_announcable(announcable, &wa.u.wireaddr); + add_announceable(announceable, &wa.u.wireaddr); continue; case ADDR_INTERNAL_FORPROXY: break; @@ -1425,10 +1425,10 @@ setup_listeners(const tal_t *ctx, * there is also at least one address of * different type. */ - if (tal_count(*announcable) != 0) { + if (tal_count(*announceable) != 0) { wireaddr_from_websocket(&addr.u.wireaddr, daemon->websocket_port); - add_announcable(announcable, + add_announceable(announceable, &addr.u.wireaddr); } else { status_unusual("Bound to websocket %s," @@ -1469,7 +1469,7 @@ setup_listeners(const tal_t *ctx, if (!(proposed_listen_announce[i] & ADDR_ANNOUNCE)) { continue; }; - add_announcable(announcable, toraddr); + add_announceable(announceable, toraddr); } /* Now we have bindings, set up any Tor static addresses: we will point @@ -1516,7 +1516,7 @@ setup_listeners(const tal_t *ctx, if (!(proposed_listen_announce[i] & ADDR_ANNOUNCE)) { continue; }; - add_announcable(announcable, toraddr); + add_announceable(announceable, toraddr); } /*~ The spec used to ban more than one address of each type, but @@ -1527,7 +1527,7 @@ setup_listeners(const tal_t *ctx, *... * - MUST place address descriptors in ascending order. */ - asort(*announcable, tal_count(*announcable), wireaddr_cmp_type, NULL); + asort(*announceable, tal_count(*announceable), wireaddr_cmp_type, NULL); *errstr = NULL; return listen_fds; @@ -1543,7 +1543,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) struct wireaddr_internal *binding; struct wireaddr_internal *proposed_wireaddr; enum addr_listen_announce *proposed_listen_announce; - struct wireaddr *announcable; + struct wireaddr *announceable; char *tor_password; bool dev_fast_gossip; bool dev_disconnect; @@ -1602,7 +1602,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) proposed_wireaddr, proposed_listen_announce, tor_password, - &announcable, + &announceable, &errstr); /* Free up old allocations */ @@ -1623,13 +1623,13 @@ static void connect_init(struct daemon *daemon, const u8 *msg) daemon_conn_send(daemon->master, take(towire_connectd_init_reply(NULL, binding, - announcable, + announceable, errstr))); /*~ Who cares about a little once-off memory leak? Turns out we do! * We have a memory leak checker which scans for allocated memory * with no pointers to it (a tell-tale leak sign, though with tal it's * not always a real problem), and this would (did!) trigger it. */ - tal_free(announcable); + tal_free(announceable); #if DEVELOPER if (dev_disconnect) diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 63ef72f3efc6..de580c0e3620 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -29,8 +29,8 @@ msgdata,connectd_init,dev_disconnect,bool, msgtype,connectd_init_reply,2100 msgdata,connectd_init_reply,num_bindings,u16, msgdata,connectd_init_reply,bindings,wireaddr_internal,num_bindings -msgdata,connectd_init_reply,num_announcable,u16, -msgdata,connectd_init_reply,announcable,wireaddr,num_announcable +msgdata,connectd_init_reply,num_announceable,u16, +msgdata,connectd_init_reply,announceable,wireaddr,num_announceable msgdata,connectd_init_reply,failmsg,?wirestring, # Activate the connect daemon, so others can connect. @@ -75,7 +75,7 @@ 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. +# master -> connectd: give message to peer and disconnect. msgtype,connectd_peer_final_msg,2003 msgdata,connectd_peer_final_msg,id,node_id, msgdata,connectd_peer_final_msg,len,u16, diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 3180d52be69b..b0596bdfe54c 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -38,8 +38,8 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, if (!sig) sig = talz(tmpctx, secp256k1_ecdsa_signature); - for (i = 0; i < tal_count(daemon->announcable); i++) - towire_wireaddr(&addresses, &daemon->announcable[i]); + for (i = 0; i < tal_count(daemon->announceable); i++) + towire_wireaddr(&addresses, &daemon->announceable[i]); na_tlv = tlv_node_ann_tlvs_new(tmpctx); na_tlv->option_will_fund = cast_const(struct lease_rates *, rates); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index eaec43f7b764..0b660daf82d8 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -672,7 +672,7 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) &daemon->id, daemon->rgb, daemon->alias, - &daemon->announcable, + &daemon->announceable, &dev_gossip_time, &dev_fast_gossip, &dev_fast_gossip_prune)) { diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 70c3ca485d78..a108f38660f7 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -45,7 +45,7 @@ struct daemon { u8 rgb[3]; /* What addresses we can actually announce. */ - struct wireaddr *announcable; + struct wireaddr *announceable; /* Timer until we can send an updated node_announcement */ struct oneshot *node_announce_timer; diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index e5369751ed89..30022048493c 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -11,8 +11,8 @@ msgdata,gossipd_init,our_features,feature_set, msgdata,gossipd_init,id,node_id, msgdata,gossipd_init,rgb,u8,3 msgdata,gossipd_init,alias,u8,32 -msgdata,gossipd_init,num_announcable,u16, -msgdata,gossipd_init,announcable,wireaddr,num_announcable +msgdata,gossipd_init,num_announceable,u16, +msgdata,gossipd_init,announceable,wireaddr,num_announceable msgdata,gossipd_init,dev_gossip_time,?u32, msgdata,gossipd_init,dev_fast_gossip,bool, msgdata,gossipd_init,dev_fast_gossip_prune,bool, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 322ceb69fb09..f62d83c1a8f1 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -469,7 +469,7 @@ static void connect_init_done(struct subd *connectd, log_debug(connectd->log, "connectd_init_done"); if (!fromwire_connectd_init_reply(ld, reply, &ld->binding, - &ld->announcable, + &ld->announceable, &errmsg)) fatal("Bad connectd_init_reply: %s", tal_hex(reply, reply)); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index e16607345b24..b1dfbe8e4bc6 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -258,7 +258,7 @@ void gossip_init(struct lightningd *ld, int connectd_fd) &ld->id, ld->rgb, ld->alias, - ld->announcable, + ld->announceable, IFDEV(ld->dev_gossip_time ? &ld->dev_gossip_time: NULL, NULL), IFDEV(ld->dev_fast_gossip, false), IFDEV(ld->dev_fast_gossip_prune, false)); diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 25a5db475a88..5573a9470c2a 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -144,9 +144,9 @@ struct lightningd { /* Setup: And the bitset for each, whether to listen, announce or both */ enum addr_listen_announce *proposed_listen_announce; - /* Actual bindings and announcables from gossipd */ + /* Actual bindings and announceables from gossipd */ struct wireaddr_internal *binding; - struct wireaddr *announcable; + struct wireaddr *announceable; /* Bearer of all my secrets. */ int hsm_fd; diff --git a/lightningd/options.c b/lightningd/options.c index 0577b0a1eda2..4828180d6e49 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -304,7 +304,7 @@ static char *opt_add_announce_addr(const char *arg, struct lightningd *ld) /* Can't announce anything that's not a normal wireaddr. */ if (ld->proposed_wireaddr[n].itype != ADDR_INTERNAL_WIREADDR) - return tal_fmt(NULL, "address '%s' is not announcable", + return tal_fmt(NULL, "address '%s' is not announceable", arg); return NULL; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 5c4d43a34b1b..45fa36decc79 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1723,8 +1723,8 @@ static struct command_result *json_getinfo(struct command *cmd, if (cmd->ld->listen) { /* These are the addresses we're announcing */ json_array_start(response, "address"); - for (size_t i = 0; i < tal_count(cmd->ld->announcable); i++) - json_add_address(response, NULL, cmd->ld->announcable+i); + for (size_t i = 0; i < tal_count(cmd->ld->announceable); i++) + json_add_address(response, NULL, cmd->ld->announceable+i); json_array_end(response); /* This is what we're actually bound to. */ diff --git a/tests/test_misc.py b/tests/test_misc.py index 083306d258a5..219739b48857 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1155,7 +1155,7 @@ def no_more_blocks(req): # Reorg changes short_channel_id 103x1x0 to 104x1x0, l1 sees it, restarts channeld bitcoind.simple_reorg(103, 1) # heights 103 - 108 - # But now it's height 104, we need another block to make it announcable. + # But now it's height 104, we need another block to make it announceable. bitcoind.generate_block(1) l1.daemon.wait_for_log(r'Peer transient failure .* short_channel_id changed to 104x1x0 \(was 103x1x0\)') From a01e2740ef4c7d681225f29a40cb18ad55d9bfb1 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Thu, 24 Feb 2022 14:49:19 +0100 Subject: [PATCH 0445/1530] pyln-proto: fix port typo in example script Changelog-None --- contrib/pyln-proto/examples/connect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-proto/examples/connect.py b/contrib/pyln-proto/examples/connect.py index 8f7b639d406b..fa2d4f635d22 100644 --- a/contrib/pyln-proto/examples/connect.py +++ b/contrib/pyln-proto/examples/connect.py @@ -16,7 +16,7 @@ b'03b31e5bbf2cdbe115b485a2b480e70a1ef3951a0dc6df4b1232e0e56f3dce18d6' )) -lc = connect(ls_privkey, remote_pubkey, '127.0.0.1', 9375) +lc = connect(ls_privkey, remote_pubkey, '127.0.0.1', 9735) # Send an init message, with no global features, and 0b10101010 as local # features. From f1981461efb43aee63c4d62df9b8698b55accfb5 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Thu, 24 Feb 2022 16:48:16 +0100 Subject: [PATCH 0446/1530] connectd: ignore private remote_addr on non-DEVELOPER builds When compiled without DEVELOPER this will now filter out `remote_addr` that come from localhost. The testcase checks for DEVELOPER to test for correct function of `remote_addr`. Also, I renamed "test_connect" to "test_connect_basic" so it can be started without all the other tests in that file that start with "test_connect..." --- connectd/peer_exchange_initmsg.c | 7 ++++++- tests/test_connection.py | 9 +++++---- tests/test_plugin.py | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 551de97677ed..8223bd751cf7 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -100,7 +100,12 @@ static struct io_plan *peer_init_received(struct io_conn *conn, switch (tlvs->remote_addr->type) { case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: - remote_addr = tal_steal(peer, tlvs->remote_addr); +#if DEVELOPER /* ignore private addresses (non-DEVELOPER builds) */ + if (address_routable(tlvs->remote_addr, true)) +#else + if (address_routable(tlvs->remote_addr, false)) +#endif /* DEVELOPER */ + remote_addr = tal_steal(peer, tlvs->remote_addr); break; /* We are only interested in IP addresses */ case ADDR_TYPE_TOR_V2_REMOVED: diff --git a/tests/test_connection.py b/tests/test_connection.py index 5e4cfbe15dc5..68572770cb5d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -10,7 +10,7 @@ expected_channel_features, check_coin_moves, first_channel_id, account_balance, basic_fee, scriptpubkey_addr, - EXPERIMENTAL_FEATURES, mine_funding_to_announce + DEVELOPER, EXPERIMENTAL_FEATURES, mine_funding_to_announce ) from pyln.testing.utils import SLOW_MACHINE, VALGRIND, EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT @@ -23,7 +23,7 @@ import websocket -def test_connect(node_factory): +def test_connect_basic(node_factory): l1, l2 = node_factory.line_graph(2, fundchannel=False) # These should be in openingd. @@ -46,8 +46,9 @@ def test_connect(node_factory): assert len(l1.rpc.listpeers()) == 1 assert len(l2.rpc.listpeers()) == 1 - if EXPERIMENTAL_FEATURES: - l1.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + if EXPERIMENTAL_FEATURES: # BOLT1 remote_addr #917 + if DEVELOPER: + print(l1.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}")) # Should get reasonable error if unknown addr for peer. with pytest.raises(RpcError, match=r'Unable to connect, no address known'): diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 73452636ac8a..979d8c41a44c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -452,6 +452,7 @@ def check_disconnect(): @unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT1 remote_addr #917") +@pytest.mark.developer("localhost remote_addr will be filtered without DEVELOEPR") def test_peer_connected_remote_addr(node_factory): """This tests the optional tlv `remote_addr` being passed to a plugin. From b930b8c548adc00a8dcf846847406d4519f6669e Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 23 Feb 2022 18:06:48 +0100 Subject: [PATCH 0447/1530] wireaddr: adds wireaddr_eq_without_port and wireaddr_cmp_type Adds wireaddr_eq_without_port so it can be used later. Moves wireaddr_cmp_type from connectd.c to this file, so it can be reused later. --- common/wireaddr.c | 30 ++++++++++++++++++++++++++++++ common/wireaddr.h | 4 ++++ connectd/connectd.c | 23 ----------------------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 8fd901784f9c..2873fe764c12 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -19,6 +19,13 @@ bool wireaddr_eq(const struct wireaddr *a, const struct wireaddr *b) return memeq(a->addr, a->addrlen, b->addr, b->addrlen); } +bool wireaddr_eq_without_port(const struct wireaddr *a, const struct wireaddr *b) +{ + if (a->type != b->type) + return false; + return memeq(a->addr, a->addrlen, b->addr, b->addrlen); +} + /* Returns false if we didn't parse it, and *cursor == NULL if malformed. */ bool fromwire_wireaddr(const u8 **cursor, size_t *max, struct wireaddr *addr) { @@ -874,3 +881,26 @@ bool all_tor_addresses(const struct wireaddr_internal *wireaddr) } return true; } + +/*~ ccan/asort provides a type-safe sorting function; it requires a comparison + * function, which takes an optional extra argument which is usually unused as + * here, but deeply painful if you need it and don't have it! */ +int wireaddr_cmp_type(const struct wireaddr *a, + const struct wireaddr *b, void *unused) +{ + /* This works, but of course it's inefficient. We don't + * really care, since it's called only once at startup. */ + u8 *a_wire = tal_arr(tmpctx, u8, 0), *b_wire = tal_arr(tmpctx, u8, 0); + int cmp, minlen; + + towire_wireaddr(&a_wire, a); + towire_wireaddr(&b_wire, b); + + minlen = tal_bytelen(a_wire) < tal_bytelen(b_wire) + ? tal_bytelen(a_wire) : tal_bytelen(b_wire); + cmp = memcmp(a_wire, b_wire, minlen); + /* On a tie, shorter one goes first. */ + if (cmp == 0) + return tal_bytelen(a_wire) - tal_bytelen(b_wire); + return cmp; +} diff --git a/common/wireaddr.h b/common/wireaddr.h index feb6bfe0b215..74e02fc065a0 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -69,6 +69,7 @@ struct wireaddr { }; bool wireaddr_eq(const struct wireaddr *a, const struct wireaddr *b); +bool wireaddr_eq_without_port(const struct wireaddr *a, const struct wireaddr *b); /* We use wireaddr to tell gossipd both what to listen on, and what to * announce */ @@ -197,4 +198,7 @@ bool all_tor_addresses(const struct wireaddr_internal *wireaddr); /* Decode an array of serialized addresses from node_announcement */ struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx, const u8 *ser); +int wireaddr_cmp_type(const struct wireaddr *a, + const struct wireaddr *b, void *unused); + #endif /* LIGHTNING_COMMON_WIREADDR_H */ diff --git a/connectd/connectd.c b/connectd/connectd.c index c47e11b9e120..f3c1e16056e4 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1186,29 +1186,6 @@ static void add_announceable(struct wireaddr **announceable, tal_arr_expand(announceable, *addr); } -/*~ ccan/asort provides a type-safe sorting function; it requires a comparison - * function, which takes an optional extra argument which is usually unused as - * here, but deeply painful if you need it and don't have it! */ -static int wireaddr_cmp_type(const struct wireaddr *a, - const struct wireaddr *b, void *unused) -{ - /* This works, but of course it's inefficient. We don't - * really care, since it's called only once at startup. */ - u8 *a_wire = tal_arr(tmpctx, u8, 0), *b_wire = tal_arr(tmpctx, u8, 0); - int cmp, minlen; - - towire_wireaddr(&a_wire, a); - towire_wireaddr(&b_wire, b); - - minlen = tal_bytelen(a_wire) < tal_bytelen(b_wire) - ? tal_bytelen(a_wire) : tal_bytelen(b_wire); - cmp = memcmp(a_wire, b_wire, minlen); - /* On a tie, shorter one goes first. */ - if (cmp == 0) - return tal_bytelen(a_wire) - tal_bytelen(b_wire); - return cmp; -} - /* We need to have a bound address we can tell Tor to connect to */ static const struct wireaddr * find_local_address(const struct listen_fd **listen_fds) From 28b4e57974f210fe7ccd10aecef594f56104fe05 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 23 Feb 2022 18:40:10 +0100 Subject: [PATCH 0448/1530] lightningd: store recently reported remote_addr --- lightningd/lightningd.c | 3 +++ lightningd/lightningd.h | 6 +++++ lightningd/peer_control.c | 57 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 440a34694c12..d047650e4f55 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -203,6 +203,9 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * NULL. So we start with a zero-length array. */ ld->proposed_wireaddr = tal_arr(ld, struct wireaddr_internal, 0); ld->proposed_listen_announce = tal_arr(ld, enum addr_listen_announce, 0); + + ld->remote_addr_v4 = NULL; + ld->remote_addr_v6 = NULL; ld->listen = true; ld->autolisten = true; ld->reconnect = true; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 5573a9470c2a..c5150146c4c2 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -148,6 +148,12 @@ struct lightningd { struct wireaddr_internal *binding; struct wireaddr *announceable; + /* unverified remote_addr as reported by recent peers */ + struct wireaddr *remote_addr_v4; + struct wireaddr *remote_addr_v6; + struct node_id remote_addr_v4_peer; + struct node_id remote_addr_v6_peer; + /* Bearer of all my secrets. */ int hsm_fd; struct subd *hsm; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 45fa36decc79..e53c6db5693a 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1109,6 +1109,53 @@ peer_connected_hook_deserialize(struct peer_connected_hook_payload *payload, return true; } +/* Compare and store `remote_addr` and the `peer_id` that reported it. + * If new address was reported by at least one other, do node_announcement */ +static void update_remote_addr(struct lightningd *ld, + const struct wireaddr *remote_addr, + const struct node_id peer_id) +{ + switch (remote_addr->type) { + case ADDR_TYPE_IPV4: + /* init pointers first time */ + if (ld->remote_addr_v4 == NULL) { + ld->remote_addr_v4 = tal_dup(ld, struct wireaddr, + remote_addr); + ld->remote_addr_v4_peer = peer_id; + } + /* if updated by the same peer just remember the latest addr */ + if (node_id_eq(&ld->remote_addr_v4_peer, &peer_id)) { + *ld->remote_addr_v4 = *remote_addr; + break; + } + /* store latest values */ + *ld->remote_addr_v4 = *remote_addr; + ld->remote_addr_v4_peer = peer_id; + break; + case ADDR_TYPE_IPV6: + /* same code :s/4/6/ without the comments ;) */ + if (ld->remote_addr_v6 == NULL) { + ld->remote_addr_v6 = tal_dup(ld, struct wireaddr, + remote_addr); + ld->remote_addr_v6_peer = peer_id; + } + if (node_id_eq(&ld->remote_addr_v6_peer, &peer_id)) { + *ld->remote_addr_v6 = *remote_addr; + break; + } + *ld->remote_addr_v6 = *remote_addr; + ld->remote_addr_v6_peer = peer_id; + break; + + /* ignore all other cases */ + case ADDR_TYPE_TOR_V2_REMOVED: + case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: + case ADDR_TYPE_WEBSOCKET: + break; + } +} + REGISTER_PLUGIN_HOOK(peer_connected, peer_connected_hook_deserialize, peer_connected_hook_final, @@ -1160,12 +1207,14 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) if (!hook_payload->channel) hook_payload->channel = peer_unsaved_channel(peer); - /* Log remote_addr for now */ - if (hook_payload->remote_addr && ( - hook_payload->remote_addr->type == ADDR_TYPE_IPV4 || - hook_payload->remote_addr->type == ADDR_TYPE_IPV6)) { + /* Log and update remote_addr for Nat/IP discovery. */ + if (hook_payload->remote_addr) { log_info(ld->log, "Peer says it sees our address as: %s", fmt_wireaddr(tmpctx, hook_payload->remote_addr)); + /* Currently only from peers we have a channel with, until we + * do stuff like probing for remote_addr to a random node. */ + if (hook_payload->channel) + update_remote_addr(ld, hook_payload->remote_addr, id); } plugin_hook_call_peer_connected(ld, hook_payload); From 67fdc6f8ad052c12826d8b82888aaad039b893cd Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 23 Feb 2022 19:11:03 +0100 Subject: [PATCH 0449/1530] gossipd: send updated node_annoucement remote_addr This is the cheapest algo I came up with that simply checks that the same `remote_addr` has been report by two different peers. Can be improved in many ways: - Check by connecting to a radonm peers in the network - Check for more than two confirmations or a certain fraction - ... Changelog-Added: Send updated node_annoucement when two peers report the same remote_addr. --- gossipd/gossip_generation.c | 37 ++++++++++++- gossipd/gossipd.c | 54 +++++++++++++++++++ gossipd/gossipd.h | 4 ++ gossipd/gossipd_wire.csv | 4 ++ gossipd/test/Makefile | 2 + gossipd/test/run-check_channel_announcement.c | 12 ----- gossipd/test/run-check_node_announcement.c | 9 ---- gossipd/test/run-crc32_of_update.c | 9 ---- gossipd/test/run-extended-info.c | 9 ---- gossipd/test/run-next_block_range.c | 9 ---- gossipd/test/run-txout_failure.c | 12 ----- lightningd/gossip_control.c | 1 + lightningd/peer_control.c | 10 ++++ lightningd/test/run-invoice-select-inchan.c | 3 ++ wallet/test/run-wallet.c | 3 ++ 15 files changed, 116 insertions(+), 62 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index b0596bdfe54c..1a561ac3d418 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -1,6 +1,7 @@ /* Routines to make our own gossip messages. Not as in "we're the gossip * generation, man!" */ #include "config.h" +#include #include #include #include @@ -19,6 +20,15 @@ #include #include +static bool wireaddr_arr_contains(const struct wireaddr *was, + const struct wireaddr *wa) +{ + for (size_t i = 0; i < tal_count(was); i++) + if (wireaddr_eq(&was[i], wa)) + return true; + return false; +} + /* Create a node_announcement with the given signature. It may be NULL in the * case we need to create a provisional announcement for the HSM to sign. * This is called twice: once with the dummy signature to get it signed and a @@ -30,16 +40,39 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, u32 timestamp, const struct lease_rates *rates) { + struct wireaddr *was; u8 *addresses = tal_arr(tmpctx, u8, 0); u8 *announcement; struct tlv_node_ann_tlvs *na_tlv; size_t i; + /* add all announceable addresses */ + was = tal_arr(tmpctx, struct wireaddr, 0); + for (i = 0; i < tal_count(daemon->announceable); i++) + tal_arr_expand(&was, daemon->announceable[i]); + + /* add reported `remote_addr` v4 and v6 of our self */ + if (daemon->remote_addr_v4 != NULL && + !wireaddr_arr_contains(was, daemon->remote_addr_v4)) + tal_arr_expand(&was, *daemon->remote_addr_v4); + if (daemon->remote_addr_v6 != NULL && + !wireaddr_arr_contains(was, daemon->remote_addr_v6)) + tal_arr_expand(&was, *daemon->remote_addr_v6); + + /* Sort by address type again, as we added dynamic remote_addr v4/v6. */ + /* BOLT #7: + * + * The origin node: + *... + * - MUST place address descriptors in ascending order. + */ + asort(was, tal_count(was), wireaddr_cmp_type, NULL); + if (!sig) sig = talz(tmpctx, secp256k1_ecdsa_signature); - for (i = 0; i < tal_count(daemon->announceable); i++) - towire_wireaddr(&addresses, &daemon->announceable[i]); + for (i = 0; i < tal_count(was); i++) + towire_wireaddr(&addresses, &was[i]); na_tlv = tlv_node_ann_tlvs_new(tmpctx); na_tlv->option_will_fund = cast_const(struct lease_rates *, rates); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 0b660daf82d8..674f646cbbce 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -341,6 +342,53 @@ static void handle_local_private_channel(struct daemon *daemon, const u8 *msg) } } +/* lightningd tells us it has dicovered and verified new `remote_addr`. + * We can use this to update our node announcement. */ +static void handle_remote_addr(struct daemon *daemon, const u8 *msg) +{ + struct wireaddr remote_addr; + + if (!fromwire_gossipd_remote_addr(msg, &remote_addr)) + master_badmsg(WIRE_GOSSIPD_REMOTE_ADDR, msg); + + /* current best guess is that we use DEFAULT_PORT on public internet */ + remote_addr.port = DEFAULT_PORT; + + switch (remote_addr.type) { + case ADDR_TYPE_IPV4: + if (daemon->remote_addr_v4 != NULL && + wireaddr_eq_without_port(daemon->remote_addr_v4, + &remote_addr)) + break; + tal_free(daemon->remote_addr_v4); + daemon->remote_addr_v4 = tal_dup(daemon, struct wireaddr, + &remote_addr); + goto update_node_annoucement; + case ADDR_TYPE_IPV6: + if (daemon->remote_addr_v6 != NULL && + wireaddr_eq_without_port(daemon->remote_addr_v6, + &remote_addr)) + break; + tal_free(daemon->remote_addr_v6); + daemon->remote_addr_v6 = tal_dup(daemon, struct wireaddr, + &remote_addr); + goto update_node_annoucement; + + /* ignore all other cases */ + case ADDR_TYPE_TOR_V2_REMOVED: + case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: + case ADDR_TYPE_WEBSOCKET: + break; + } + return; + +update_node_annoucement: + status_debug("Update our node_announcement for discovered address: %s", + fmt_wireaddr(tmpctx, &remote_addr)); + maybe_send_own_node_announce(daemon, false); +} + /*~ This is where connectd tells us about a new peer we might want to * gossip with. */ static void connectd_new_peer(struct daemon *daemon, const u8 *msg) @@ -993,6 +1041,10 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_LOCAL_PRIVATE_CHANNEL: handle_local_private_channel(daemon, msg); goto done; + + case WIRE_GOSSIPD_REMOTE_ADDR: + handle_remote_addr(daemon, msg); + goto done; #if DEVELOPER case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: dev_set_max_scids_encode_size(daemon, msg); @@ -1062,6 +1114,8 @@ int main(int argc, char *argv[]) daemon->node_announce_regen_timer = NULL; daemon->current_blockheight = 0; /* i.e. unknown */ daemon->rates = NULL; + daemon->remote_addr_v4 = NULL; + daemon->remote_addr_v6 = NULL; list_head_init(&daemon->deferred_updates); /* Tell the ecdh() function how to talk to hsmd */ diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index a108f38660f7..7ed700afa028 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -47,6 +47,10 @@ struct daemon { /* What addresses we can actually announce. */ struct wireaddr *announceable; + /* verified remote_addr as reported by recent peers */ + struct wireaddr *remote_addr_v4; + struct wireaddr *remote_addr_v6; + /* Timer until we can send an updated node_announcement */ struct oneshot *node_announce_timer; diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 30022048493c..19e4a01f209e 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -125,3 +125,7 @@ msgdata,gossipd_local_private_channel,features,u8,len # Tell gossipd we used the channel update (in case it was deferred) msgtype,gossipd_used_local_channel_update,3052 msgdata,gossipd_used_local_channel_update,scid,short_channel_id, + +# Tell gossipd we have discovered a new remote_addr +msgtype,gossipd_remote_addr,3009 +msgdata,gossipd_remote_addr,remote_addr,wireaddr, diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index e91b5c2ce322..796e1a07e11d 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -9,6 +9,7 @@ GOSSIPD_TEST_PROGRAMS := $(GOSSIPD_TEST_OBJS:.o=) GOSSIPD_TEST_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ + common/base32.o \ common/coin_mvt.o \ common/bigsize.o \ common/blindedpath.o \ @@ -25,6 +26,7 @@ GOSSIPD_TEST_COMMON_OBJS := \ common/sphinx.o \ common/type_to_string.o \ common/utils.o \ + common/wireaddr.o \ gossipd/gossip_store_wiregen.o \ wire/peer$(EXP)_wiregen.o \ wire/onion$(EXP)_wiregen.o \ diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 4db6c943bb6d..ded89931d24e 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -62,15 +62,6 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr_array */ -struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) @@ -157,9 +148,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int main(int argc, char *argv[]) diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 304ec891b481..e2d36b59dcbe 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -33,9 +33,6 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } /* Generated stub for force_node_announce_rexmit */ void force_node_announce_rexmit(struct routing_state *rstate UNNEEDED, struct node *node UNNEEDED) { fprintf(stderr, "force_node_announce_rexmit called!\n"); abort(); } @@ -51,9 +48,6 @@ bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UN /* Generated stub for fromwire_hsmd_node_announcement_sig_reply */ bool fromwire_hsmd_node_announcement_sig_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) { fprintf(stderr, "fromwire_hsmd_node_announcement_sig_reply called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for get_node */ struct node *get_node(struct routing_state *rstate UNNEEDED, const struct node_id *id UNNEEDED) @@ -121,9 +115,6 @@ u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED /* Generated stub for towire_hsmd_node_announcement_sig_req */ u8 *towire_hsmd_node_announcement_sig_req(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED) { fprintf(stderr, "towire_hsmd_node_announcement_sig_req called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for wire_sync_read */ u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) { fprintf(stderr, "wire_sync_read called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 05f90e4f508f..ea1104c7bc69 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -51,9 +51,6 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } /* Generated stub for force_node_announce_rexmit */ void force_node_announce_rexmit(struct routing_state *rstate UNNEEDED, struct node *node UNNEEDED) { fprintf(stderr, "force_node_announce_rexmit called!\n"); abort(); } @@ -72,9 +69,6 @@ bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UN /* Generated stub for fromwire_hsmd_node_announcement_sig_reply */ bool fromwire_hsmd_node_announcement_sig_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) { fprintf(stderr, "fromwire_hsmd_node_announcement_sig_reply called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for get_node */ struct node *get_node(struct routing_state *rstate UNNEEDED, const struct node_id *id UNNEEDED) @@ -159,9 +153,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 towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for wire_sync_read */ u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) { fprintf(stderr, "wire_sync_read called!\n"); abort(); } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 9b5e12a1be51..83debe2643c2 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -50,15 +50,9 @@ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *e /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for get_cupdate_parts */ void get_cupdate_parts(const u8 *channel_update UNNEEDED, const u8 *parts[2] UNNEEDED, @@ -110,9 +104,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 towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ void status_fmt(enum log_level level UNNEEDED, diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index a47c295bea77..bad28d36720a 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -29,12 +29,6 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -96,9 +90,6 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for would_ratelimit_cupdate */ bool would_ratelimit_cupdate(struct routing_state *rstate UNNEEDED, const struct half_chan *hc UNNEEDED, diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index a648b0d87042..f6687c5bd8fd 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -33,15 +33,6 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr_array */ -struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) @@ -127,9 +118,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 towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* NOOP stub for gossip_store_new */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index b1dfbe8e4bc6..90f7352bda16 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -176,6 +176,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: + case WIRE_GOSSIPD_REMOTE_ADDR: break; case WIRE_GOSSIPD_GET_TXOUT: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e53c6db5693a..b7b09aeca191 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -1128,6 +1129,11 @@ static void update_remote_addr(struct lightningd *ld, *ld->remote_addr_v4 = *remote_addr; break; } + /* tell gossip we have a valid update */ + if (wireaddr_eq_without_port(ld->remote_addr_v4, remote_addr)) + subd_send_msg(ld->gossip, towire_gossipd_remote_addr( + tmpctx, + ld->remote_addr_v4)); /* store latest values */ *ld->remote_addr_v4 = *remote_addr; ld->remote_addr_v4_peer = peer_id; @@ -1143,6 +1149,10 @@ static void update_remote_addr(struct lightningd *ld, *ld->remote_addr_v6 = *remote_addr; break; } + if (wireaddr_eq_without_port(ld->remote_addr_v6, remote_addr)) + subd_send_msg(ld->gossip, towire_gossipd_remote_addr( + tmpctx, + ld->remote_addr_v6)); *ld->remote_addr_v6 = *remote_addr; ld->remote_addr_v6_peer = peer_id; break; diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 7796b2f87f3b..6df4809eb53d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -645,6 +645,9 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_remote_addr */ +u8 *towire_gossipd_remote_addr(const tal_t *ctx UNNEEDED, const struct wireaddr *remote_addr UNNEEDED) +{ fprintf(stderr, "towire_gossipd_remote_addr called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_bolt12 */ u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED, const u8 *publictweak UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index ef7c21d65666..890ed17b57fe 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -749,6 +749,9 @@ u8 *towire_final_incorrect_cltv_expiry(const tal_t *ctx UNNEEDED, u32 cltv_expir /* Generated stub for towire_final_incorrect_htlc_amount */ u8 *towire_final_incorrect_htlc_amount(const tal_t *ctx UNNEEDED, struct amount_msat incoming_htlc_amt UNNEEDED) { fprintf(stderr, "towire_final_incorrect_htlc_amount called!\n"); abort(); } +/* Generated stub for towire_gossipd_remote_addr */ +u8 *towire_gossipd_remote_addr(const tal_t *ctx UNNEEDED, const struct wireaddr *remote_addr UNNEEDED) +{ fprintf(stderr, "towire_gossipd_remote_addr called!\n"); abort(); } /* Generated stub for towire_hsmd_get_output_scriptpubkey */ u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED) { fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); } From 07fbc7ef13e2f0bc1b5cc6d6ce941d932d942a40 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Thu, 24 Feb 2022 15:17:17 +0100 Subject: [PATCH 0450/1530] pytest: IP address discovery updates node_announcement --- tests/test_connection.py | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 68572770cb5d..20e1eec00807 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -63,6 +63,65 @@ def test_connect_basic(node_factory): l1.rpc.connect('032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e', 'localhost', l2.port) +@pytest.mark.developer("needs DEVELOPER=1 for having localhost remote_addr and fast gossip") +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT1 remote_addr #917") +def test_remote_addr(node_factory, bitcoind): + """Check address discovery (BOLT1 #917) init remote_addr works as designed: + + `node_announcement` update must only be send out when: + - at least two peers + - we have a channel with + - report the same `remote_addr` + """ + # don't announce anything per se + opts = {'announce-addr': [], 'may_reconnect': True} + l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + + # Fund first channel so initial node_announcement is send + l1.fundchannel(l2) + bitcoind.generate_block(5) + l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") + + # when we restart l1 with a channel and reconnect, node_annoucement update + # must not yet be send as we need the same `remote_addr` confirmed from a + # another peer we have a channel with. + # Note: In this state l2 stores remote_addr as reported by l1 + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + l1.restart() + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + + # now only l1 sees l2 without announced addresses (disabled by opts) + assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) + assert(len(l3.rpc.listnodes(l2.info['id'])['nodes']) == 0) + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + + # connect second node. This will not yet trigger `node_annoucement` update, + # as we again do not have a channel at the time we connected. + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + + # fund channel and check we didn't send Update earlier already + l2.fundchannel(l3, wait_for_active=True) + bitcoind.generate_block(5) + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + + # restart, reconnect and re-check for updated node_annoucement. This time + # l2 sees that two different peers with channel reported the same `remote_addr`. + l3.restart() + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + l2.daemon.wait_for_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") + + address = l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses'][0] + assert address['type'] == "ipv4" + assert address['address'] == "127.0.0.1" + assert address['port'] == 9735 + + def test_connect_standard_addr(node_factory): """Test standard node@host:port address """ From 57fb34ed06d00b2532c5272f21ff56bbb55530f4 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 1 Mar 2022 19:28:25 +0100 Subject: [PATCH 0451/1530] test: connectd netaddress Increases test coverage by adding a testcase for connectd/netaddress.c Changelog-None --- connectd/test/run-netaddress.c | 232 +++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 connectd/test/run-netaddress.c diff --git a/connectd/test/run-netaddress.c b/connectd/test/run-netaddress.c new file mode 100644 index 000000000000..8f6482cb18d7 --- /dev/null +++ b/connectd/test/run-netaddress.c @@ -0,0 +1,232 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for b32_decode */ +u8 *b32_decode(const tal_t *ctx UNNEEDED, const char *str UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "b32_decode called!\n"); abort(); } +/* Generated stub for b32_encode */ +char *b32_encode(const tal_t *ctx UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "b32_encode called!\n"); abort(); } +/* Generated stub for daemon_conn_queue_length */ +size_t daemon_conn_queue_length(const struct daemon_conn *dc UNNEEDED) +{ fprintf(stderr, "daemon_conn_queue_length called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } +/* Generated stub for daemon_conn_sync_flush */ +bool daemon_conn_sync_flush(struct daemon_conn *dc UNNEEDED) +{ fprintf(stderr, "daemon_conn_sync_flush called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_peektype */ +int fromwire_peektype(const u8 *cursor UNNEEDED) +{ fprintf(stderr, "fromwire_peektype called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for is_msg_for_gossipd */ +bool is_msg_for_gossipd(const u8 *cursor UNNEEDED) +{ fprintf(stderr, "is_msg_for_gossipd called!\n"); abort(); } +/* Generated stub for peer_wire_name */ +const char *peer_wire_name(int e UNNEEDED) +{ fprintf(stderr, "peer_wire_name called!\n"); abort(); } +/* Generated stub for send_backtrace */ +void send_backtrace(const char *why UNNEEDED) +{ fprintf(stderr, "send_backtrace called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_status_fail */ +u8 *towire_status_fail(const tal_t *ctx UNNEEDED, enum status_failreason failreason UNNEEDED, const wirestring *desc UNNEEDED) +{ fprintf(stderr, "towire_status_fail called!\n"); abort(); } +/* Generated stub for towire_status_io */ +u8 *towire_status_io(const tal_t *ctx UNNEEDED, enum log_level iodir UNNEEDED, const struct node_id *peer UNNEEDED, const wirestring *who UNNEEDED, const u8 *data UNNEEDED) +{ fprintf(stderr, "towire_status_io called!\n"); abort(); } +/* Generated stub for towire_status_log */ +u8 *towire_status_log(const tal_t *ctx UNNEEDED, enum log_level level UNNEEDED, const struct node_id *peer UNNEEDED, const wirestring *entry UNNEEDED) +{ fprintf(stderr, "towire_status_log called!\n"); abort(); } +/* Generated stub for towire_status_version */ +u8 *towire_status_version(const tal_t *ctx UNNEEDED, const wirestring *version UNNEEDED) +{ fprintf(stderr, "towire_status_version called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for version */ +const char *version(void) +{ fprintf(stderr, "version called!\n"); abort(); } +/* Generated stub for wire_sync_write */ +bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) +{ fprintf(stderr, "wire_sync_write called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +int main(int argc, char *argv[]) +{ + struct wireaddr wa; + const char *err; + + common_setup(argv[0]); + + // start with IPv4 + parse_wireaddr("0.0.0.0", &wa, DEFAULT_PORT, NULL, &err); + assert(!IsValid(&wa)); + assert(IsIPv4(&wa)); + assert(!IsIPv6(&wa)); + parse_wireaddr("255.255.255.255", &wa, DEFAULT_PORT, NULL, &err); + assert(!IsValid(&wa)); + assert(IsIPv4(&wa)); + assert(!IsIPv6(&wa)); + + parse_wireaddr("127.0.0.1", &wa, DEFAULT_PORT, NULL, &err); + assert(IsValid(&wa)); + assert(IsIPv4(&wa)); + assert(!IsIPv6(&wa)); + assert(IsLocal(&wa)); + assert(address_routable(&wa, true)); + assert(!address_routable(&wa, false)); + + parse_wireaddr("0.1.2.3", &wa, DEFAULT_PORT, NULL, &err); + assert(IsValid(&wa)); + assert(IsIPv4(&wa)); + assert(!IsIPv6(&wa)); + assert(IsLocal(&wa)); + assert(address_routable(&wa, true)); + assert(!address_routable(&wa, false)); + + parse_wireaddr("10.0.21.42", &wa, DEFAULT_PORT, NULL, &err); + assert(IsValid(&wa)); + assert(IsIPv4(&wa)); + assert(!IsIPv6(&wa)); + assert(!IsLocal(&wa)); + assert(IsRFC1918(&wa)); + assert(!address_routable(&wa, true)); + assert(!address_routable(&wa, false)); + + parse_wireaddr("192.168.123.4", &wa, DEFAULT_PORT, NULL, &err); + assert(IsValid(&wa)); + assert(IsIPv4(&wa)); + assert(!IsIPv6(&wa)); + assert(!IsLocal(&wa)); + assert(IsRFC1918(&wa)); + assert(!address_routable(&wa, true)); + assert(!address_routable(&wa, false)); + + parse_wireaddr("1.2.3.4", &wa, DEFAULT_PORT, NULL, &err); + assert(IsValid(&wa)); + assert(IsIPv4(&wa)); + assert(!IsIPv6(&wa)); + assert(!IsLocal(&wa)); + assert(address_routable(&wa, true)); + assert(address_routable(&wa, false)); + + // now IPv6 stuff + parse_wireaddr("2142:DEAD:beef::1", &wa, DEFAULT_PORT, NULL, &err); + assert(IsValid(&wa)); + assert(!IsIPv4(&wa)); + assert(IsIPv6(&wa)); + assert(!IsLocal(&wa)); + assert(address_routable(&wa, true)); + assert(address_routable(&wa, false)); + + parse_wireaddr("0::0", &wa, DEFAULT_PORT, NULL, &err); + assert(!IsValid(&wa)); + assert(!IsIPv4(&wa)); + assert(IsIPv6(&wa)); + + parse_wireaddr("2001:db8::1", &wa, DEFAULT_PORT, NULL, &err); + assert(!IsValid(&wa)); + assert(!IsIPv4(&wa)); + assert(IsIPv6(&wa)); +} From ef84d6eec505390aed9623a11a92f6b5372df804 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 8 Mar 2022 15:31:26 +0100 Subject: [PATCH 0452/1530] chore: remove EXPERIMENTAL for rfc #917 remote_addr --- connectd/peer_exchange_initmsg.c | 6 ++---- tests/test_connection.py | 6 ++---- tests/test_plugin.py | 3 +-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 8223bd751cf7..6dc36d5349b3 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -90,7 +90,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, /* fetch optional tlv `remote_addr` */ remote_addr = NULL; -#if EXPERIMENTAL_FEATURES /* BOLT1 remote_addr #917 */ + /* BOLT-remote-address #1: * The receiving node: * ... @@ -115,7 +115,6 @@ static struct io_plan *peer_init_received(struct io_conn *conn, break; } } -#endif /* The globalfeatures field is now unused, but there was a * window where it was: combine the two. */ @@ -217,7 +216,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, /* set optional tlv `remote_addr` on incoming IP connections */ tlvs->remote_addr = NULL; -#if EXPERIMENTAL_FEATURES /* BOLT1 remote_addr #917 */ + /* BOLT-remote-address #1: * The sending node: * ... @@ -241,7 +240,6 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, break; } } -#endif /* Initially, there were two sets of feature bits: global and local. * Local affected peer nodes only, global affected everyone. Both were diff --git a/tests/test_connection.py b/tests/test_connection.py index 20e1eec00807..23aaabafecae 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -46,9 +46,8 @@ def test_connect_basic(node_factory): assert len(l1.rpc.listpeers()) == 1 assert len(l2.rpc.listpeers()) == 1 - if EXPERIMENTAL_FEATURES: # BOLT1 remote_addr #917 - if DEVELOPER: - print(l1.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}")) + if DEVELOPER: + print(l1.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}")) # Should get reasonable error if unknown addr for peer. with pytest.raises(RpcError, match=r'Unable to connect, no address known'): @@ -64,7 +63,6 @@ def test_connect_basic(node_factory): @pytest.mark.developer("needs DEVELOPER=1 for having localhost remote_addr and fast gossip") -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT1 remote_addr #917") def test_remote_addr(node_factory, bitcoind): """Check address discovery (BOLT1 #917) init remote_addr works as designed: diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 979d8c41a44c..bde2da4e74b1 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -10,7 +10,7 @@ DEPRECATED_APIS, expected_peer_features, expected_node_features, expected_channel_features, account_balance, check_coin_moves, first_channel_id, EXPERIMENTAL_DUAL_FUND, - mine_funding_to_announce, EXPERIMENTAL_FEATURES + mine_funding_to_announce ) import ast @@ -451,7 +451,6 @@ def check_disconnect(): assert not l1.daemon.is_in_log(f"peer_connected_logger_b {l3id}") -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT1 remote_addr #917") @pytest.mark.developer("localhost remote_addr will be filtered without DEVELOEPR") def test_peer_connected_remote_addr(node_factory): """This tests the optional tlv `remote_addr` being passed to a plugin. From db95893aecc678a6675de3d497694a7635955c3f Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 9 Mar 2022 14:28:20 +0100 Subject: [PATCH 0453/1530] lightningd: do not use remote_addr for always_use_proxy --- lightningd/peer_control.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index b7b09aeca191..d7607535ad09 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1116,6 +1116,10 @@ static void update_remote_addr(struct lightningd *ld, const struct wireaddr *remote_addr, const struct node_id peer_id) { + /* failsafe to prevent privacy leakage. */ + if (ld->always_use_proxy) + return; + switch (remote_addr->type) { case ADDR_TYPE_IPV4: /* init pointers first time */ From 6a146bed26610340ad085b69a3b9a038623ebe9d Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 10 Mar 2022 20:31:37 +0100 Subject: [PATCH 0454/1530] db: small code cleanup Changelog-None: db: small code cleanup Signed-off-by: Vincenzo Palazzo --- db/exec.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/db/exec.c b/db/exec.c index 77c9597ab841..13341c1e7d64 100644 --- a/db/exec.c +++ b/db/exec.c @@ -78,13 +78,9 @@ s64 db_get_intvar(struct db *db, char *varname, s64 defval) struct db_stmt *stmt = db_prepare_v2( db, SQL("SELECT intval FROM vars WHERE name= ? LIMIT 1")); db_bind_text(stmt, 0, varname); - if (!db_query_prepared(stmt)) - goto done; - - if (db_step(stmt)) + if (db_query_prepared(stmt) && db_step(stmt)) res = db_col_int(stmt, "intval"); -done: tal_free(stmt); return res; } From 9703ee05bffc1162ce60a5fce8d051e3138fe1e0 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Fri, 11 Mar 2022 19:14:14 +0100 Subject: [PATCH 0455/1530] doc: update faq and docs for IP discovery --- doc/FAQ.md | 9 +++++++-- doc/TOR.md | 6 ++++++ doc/lightningd-config.5.md | 4 ++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index 371b5efcf9b8..6ec1a9f0b89c 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -70,8 +70,13 @@ Note that if you already have a channel open to them, you'll need to close it be ### Are there any issues if my node changes its IP address? What happens to the channels if it does? There is no risk to your channels if your IP address changes. -However, be sure to change your announced address (or [setup a TOR hidden service](TOR.md)) -in your config so that others can establish connections at your new address ! +Other nodes might not be able to connect to you, but your node can still connect to them. +But c-lightning also has an integrated IPv4/6 address discovery mechanism. +If your node detects an new public address, it will update its announcement. +For this to work binhind a NAT router you need to forward the TCP port 9735 to your node. + +Alternatively, you can [setup a TOR hidden service](TOR.md) for your node that +will also work well behind NAT firewalls. ### Can I have two hosts with the same public key and different IP addresses, both online and operating at the same time? diff --git a/doc/TOR.md b/doc/TOR.md index 781af0cd70cf..0eb11babb2f6 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -47,6 +47,12 @@ Tor provides NAT-traversal for free, so even if you or your ISP has a complex network between you and the Internet, as long as you can use Tor you can be connected to. +Note: c-lightning also support IPv4/6 address discovery behind NAT routers. +For this to work you need to forward the TCP port 9735 to your node. +In this case you don't need TOR to punch through your firewall. +This usually has the benefit of quicker and more stable connections but does not +offer additional privacy. + On most Linux distributions, making a standard installation of `tor` will automatically set it up to have a SOCKS5 proxy at port 9050. As well, you have to set up the Tor Control Port. diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 879606f2ea29..bc45c789a06b 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -350,6 +350,10 @@ right thing: for the mainnet (bitcoin) network it will try to bind to port 9735 on IPv4 and IPv6, and will announce it to peers if it seems like a public address. +c-lightning also support IPv4/6 address discovery behind NAT routers. +If your node detects an new public address, it will update its announcement. +For this to work you need to forward the TCP port 9735 to your node. + You can instead use *addr* to override this (eg. to change the port), or precisely control where to bind and what to announce with the *bind-addr* and *announce-addr* options. These will **disable** the From a2a6b8c3ff128c4fc19ae2b48f811552d47fc90f Mon Sep 17 00:00:00 2001 From: GoofyAF <91838665+GoofyAF@users.noreply.github.com> Date: Sat, 12 Mar 2022 16:00:11 -0800 Subject: [PATCH 0456/1530] Goofy af tor.md systemd syntax update (#5059) Update TOR.md Update tor install to represent current systemd syntax for enabling and starting a new system service Corrected a typo. After tor is installed to refresh /etc/tor/torrc config changes the command should be 'sudo systemctl restart tor' not 'sudo systemctl start tor' --- doc/TOR.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/TOR.md b/doc/TOR.md index 0eb11babb2f6..083a939d6b94 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -9,7 +9,7 @@ If Tor is not installed you can install it on Debian based Linux systems (Ubuntu ```bash sudo apt install tor ``` -then `/etc/init.d/tor start` or `sudo systemctl start tor` depending +then `/etc/init.d/tor start` or `sudo systemctl enable --now tor` depending on your system configuration. Most default setting should be sufficient. @@ -210,7 +210,7 @@ Tor service. Both types of addresses can coexist on the same node. Save the file and restart the Tor service. In linux: -`/etc/init.d/tor restart` or `sudo systemctl start tor` depending +`/etc/init.d/tor restart` or `sudo systemctl restart tor` depending on the configuration of your system. You will find the newly created address (myaddress.onion) with: From f72a08c80261296f2649bb04c2f3b2552636d404 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 11 Mar 2022 12:08:59 -0800 Subject: [PATCH 0457/1530] websocketd: fix random failures by blocking stdin reads Example request that is dying: NEW REQUEST! lightning_websocketd:main [1955685] <-- bad request from safari read 507 write_all 1 -> websocket_to_lightningd -> read_payload_header read 2 read_all 1 read -11 <--- This tried to read a part of the header, is this -EAGAIN? read_all 0 should we be blocking on these reads? *dies* Fixes #5089 Changelog-Fixed: `experimental-websocket` intermittent read errors fixed Signed-off-by: William Casarin --- connectd/websocketd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/connectd/websocketd.c b/connectd/websocketd.c index 324a17f7922b..9dc65bec2fcc 100644 --- a/connectd/websocketd.c +++ b/connectd/websocketd.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -326,6 +327,7 @@ int main(int argc, char *argv[]) errx(1, "Usage: %s", argv[0]); /* Do HTTP-style negotiation to get into websocket frames. */ + io_fd_block(STDIN_FILENO, true); http_upgrade(STDIN_FILENO); pfds[0].fd = STDIN_FILENO; From e99b11565c9c465d594339349486cf68bdf47e59 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 15 Mar 2022 12:38:39 +0100 Subject: [PATCH 0458/1530] pyln: Change the default argument to `exclude` in `pay` We must use `None` for default arguments since otherwise they aren't filtered out when serializing the request. In addition default arguments to functions are initialized once and, if mutable, could persist internal changes across function calls. Changelog-None --- contrib/pyln-client/pyln/client/lightning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index b76ad28437b7..12759d694e5a 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -990,7 +990,7 @@ def newaddr(self, addresstype=None): def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, - maxdelay=None, exemptfee=None, exclude=[]): + maxdelay=None, exemptfee=None, exclude=None): """ Send payment specified by {bolt11} with {msatoshi} (ignored if {bolt11} has an amount), optional {label} From 487b5e616937faa73976d7d5f4babd7b28ff12d1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 15 Mar 2022 13:34:32 +0100 Subject: [PATCH 0459/1530] msggen: Add meta file to keep the field numbers stable We are inferring the field numbers on the fly, which isn't really compatible with the way GRPC field numbers work, i.e., they must be stable while the IDL file evolves. So far when a field was added in the middle of a struct or removed all subsequent fields would get renumbered, essentially breaking any client that was using the old scheme. We now add a meta file `.msggen.json` that keeps track of the numbers assigned so far, so they can be reused, and new ones can be generated not to conflict with existing ones. This file is intentionally kept generic, so other generators can add more information that has to be managed across runs. Changelog-None --- .msggen.json | 234 ++++++++++++++++++++++++++++++ cln-grpc/proto/node.proto | 146 ++++++++++--------- cln-grpc/src/convert.rs | 2 + cln-rpc/src/model.rs | 36 +++-- contrib/msggen/msggen/__main__.py | 18 ++- contrib/msggen/msggen/grpc.py | 67 ++++++++- 6 files changed, 415 insertions(+), 88 deletions(-) create mode 100644 .msggen.json diff --git a/.msggen.json b/.msggen.json new file mode 100644 index 000000000000..db8ab9dd7e1b --- /dev/null +++ b/.msggen.json @@ -0,0 +1,234 @@ +{ + "grpc-enum-map": { + "CloseType": { + "mutual": 0, + "unilateral": 1, + "unopened": 2 + }, + "GetinfoAddressType": { + "dns": 0, + "ipv4": 1, + "ipv6": 2, + "torv2": 3, + "torv3": 4, + "websocket": 5 + }, + "GetinfoBindingType": { + "ipv4": 1, + "ipv6": 2, + "local socket": 0, + "torv2": 3, + "torv3": 4 + }, + "ListfundsOutputsStatus": { + "confirmed": 1, + "spent": 2, + "unconfirmed": 0 + }, + "ListpeersPeersChannelsHtlcsDirection": { + "in": 0, + "out": 1 + }, + "ListpeersPeersChannelsHtlcsState": { + "RCVD_ADD_ACK_COMMIT": 3, + "RCVD_ADD_REVOCATION": 2, + "RCVD_REMOVE_ACK_REVOCATION": 9, + "RCVD_REMOVE_COMMIT": 6, + "RCVD_REMOVE_HTLC": 5, + "SENT_ADD_ACK_REVOCATION": 4, + "SENT_ADD_COMMIT": 1, + "SENT_ADD_HTLC": 0, + "SENT_REMOVE_ACK_COMMIT": 8, + "SENT_REMOVE_REVOCATION": 7 + }, + "ListpeersPeersChannelsState": { + "AWAITING_UNILATERAL": 6, + "CHANNELD_AWAITING_LOCKIN": 1, + "CHANNELD_NORMAL": 2, + "CHANNELD_SHUTTING_DOWN": 3, + "CLOSINGD_COMPLETE": 5, + "CLOSINGD_SIGEXCHANGE": 4, + "DUALOPEND_AWAITING_LOCKIN": 10, + "DUALOPEND_OPEN_INIT": 9, + "FUNDING_SPEND_SEEN": 7, + "ONCHAIN": 8, + "OPENINGD": 0 + }, + "ListpeersPeersLogType": { + "BROKEN": 1, + "DEBUG": 4, + "INFO": 3, + "IO_IN": 5, + "IO_OUT": 6, + "SKIPPED": 0, + "UNUSUAL": 2 + } + }, + "grpc-field-map": { + "AddGossip.message": 1, + "AutoCleanInvoice.cycle_seconds": 2, + "AutoCleanInvoice.enabled": 3, + "AutoCleanInvoice.expired_by": 1, + "CheckMessage.message": 1, + "CheckMessage.pubkey": 3, + "CheckMessage.verified": 4, + "CheckMessage.zbase": 2, + "Close.destination": 3, + "Close.fee_negotiation_step": 4, + "Close.force_lease_closed": 6, + "Close.id": 1, + "Close.tx": 8, + "Close.txid": 9, + "Close.type": 7, + "Close.unilateraltimeout": 2, + "Close.wrong_funding": 5, + "Getinfo.address[]": 14, + "Getinfo.address[].address": 3, + "Getinfo.address[].port": 2, + "Getinfo.address[].type": 1, + "Getinfo.alias": 2, + "Getinfo.binding[]": 15, + "Getinfo.binding[].address": 2, + "Getinfo.binding[].port": 3, + "Getinfo.binding[].socket": 4, + "Getinfo.binding[].type": 1, + "Getinfo.blockheight": 11, + "Getinfo.color": 3, + "Getinfo.fees_collected_msat": 13, + "Getinfo.id": 1, + "Getinfo.lightning-dir": 9, + "Getinfo.network": 12, + "Getinfo.num_active_channels": 6, + "Getinfo.num_inactive_channels": 7, + "Getinfo.num_peers": 4, + "Getinfo.num_pending_channels": 5, + "Getinfo.our_features": 10, + "Getinfo.our_features.channel": 3, + "Getinfo.our_features.init": 1, + "Getinfo.our_features.invoice": 4, + "Getinfo.our_features.node": 2, + "Getinfo.version": 8, + "Getinfo.warning_bitcoind_sync": 16, + "Getinfo.warning_lightningd_sync": 17, + "ListChannels.channels[]": 4, + "ListChannels.channels[].active": 8, + "ListChannels.channels[].amount_msat": 5, + "ListChannels.channels[].base_fee_millisatoshi": 10, + "ListChannels.channels[].channel_flags": 7, + "ListChannels.channels[].delay": 12, + "ListChannels.channels[].destination": 2, + "ListChannels.channels[].features": 15, + "ListChannels.channels[].fee_per_millionth": 11, + "ListChannels.channels[].htlc_maximum_msat": 14, + "ListChannels.channels[].htlc_minimum_msat": 13, + "ListChannels.channels[].last_update": 9, + "ListChannels.channels[].message_flags": 6, + "ListChannels.channels[].public": 4, + "ListChannels.channels[].short_channel_id": 3, + "ListChannels.channels[].source": 1, + "ListChannels.destination": 3, + "ListChannels.short_channel_id": 1, + "ListChannels.source": 2, + "ListFunds.channels[]": 3, + "ListFunds.channels[].amount_msat": 3, + "ListFunds.channels[].connected": 6, + "ListFunds.channels[].funding_output": 5, + "ListFunds.channels[].funding_txid": 4, + "ListFunds.channels[].our_amount_msat": 2, + "ListFunds.channels[].peer_id": 1, + "ListFunds.channels[].short_channel_id": 8, + "ListFunds.channels[].state": 7, + "ListFunds.outputs[]": 2, + "ListFunds.outputs[].address": 5, + "ListFunds.outputs[].amount_msat": 3, + "ListFunds.outputs[].blockheight": 8, + "ListFunds.outputs[].output": 2, + "ListFunds.outputs[].redeemscript": 6, + "ListFunds.outputs[].scriptpubkey": 4, + "ListFunds.outputs[].status": 7, + "ListFunds.outputs[].txid": 1, + "ListFunds.spent": 1, + "ListPeers.id": 1, + "ListPeers.level": 2, + "ListPeers.peers[]": 3, + "ListPeers.peers[].channels[]": 4, + "ListPeers.peers[].channels[].channel_id": 6, + "ListPeers.peers[].channels[].close_to": 14, + "ListPeers.peers[].channels[].close_to_addr": 47, + "ListPeers.peers[].channels[].closer": 17, + "ListPeers.peers[].channels[].dust_limit_msat": 26, + "ListPeers.peers[].channels[].features[]": 18, + "ListPeers.peers[].channels[].fee_base_msat": 24, + "ListPeers.peers[].channels[].fee_proportional_millionths": 25, + "ListPeers.peers[].channels[].feerate": 3, + "ListPeers.peers[].channels[].feerate.perkb": 2, + "ListPeers.peers[].channels[].feerate.perkw": 1, + "ListPeers.peers[].channels[].funding": 19, + "ListPeers.peers[].channels[].funding.local_msat": 1, + "ListPeers.peers[].channels[].funding.pushed_msat": 3, + "ListPeers.peers[].channels[].funding.remote_msat": 2, + "ListPeers.peers[].channels[].funding_outnum": 8, + "ListPeers.peers[].channels[].funding_txid": 7, + "ListPeers.peers[].channels[].htlcs[]": 46, + "ListPeers.peers[].channels[].htlcs[].amount_msat": 3, + "ListPeers.peers[].channels[].htlcs[].direction": 1, + "ListPeers.peers[].channels[].htlcs[].expiry": 4, + "ListPeers.peers[].channels[].htlcs[].id": 2, + "ListPeers.peers[].channels[].htlcs[].local_trimmed": 6, + "ListPeers.peers[].channels[].htlcs[].payment_hash": 5, + "ListPeers.peers[].channels[].htlcs[].state": 8, + "ListPeers.peers[].channels[].htlcs[].status": 7, + "ListPeers.peers[].channels[].in_fulfilled_msat": 41, + "ListPeers.peers[].channels[].in_offered_msat": 39, + "ListPeers.peers[].channels[].in_payments_fulfilled": 40, + "ListPeers.peers[].channels[].in_payments_offered": 38, + "ListPeers.peers[].channels[].inflight[]": 13, + "ListPeers.peers[].channels[].inflight[].feerate": 3, + "ListPeers.peers[].channels[].inflight[].funding_outnum": 2, + "ListPeers.peers[].channels[].inflight[].funding_txid": 1, + "ListPeers.peers[].channels[].inflight[].our_funding_msat": 5, + "ListPeers.peers[].channels[].inflight[].scratch_txid": 6, + "ListPeers.peers[].channels[].inflight[].total_funding_msat": 4, + "ListPeers.peers[].channels[].initial_feerate": 9, + "ListPeers.peers[].channels[].last_feerate": 10, + "ListPeers.peers[].channels[].max_accepted_htlcs": 35, + "ListPeers.peers[].channels[].max_to_us_msat": 22, + "ListPeers.peers[].channels[].max_total_htlc_in_msat": 27, + "ListPeers.peers[].channels[].min_to_us_msat": 21, + "ListPeers.peers[].channels[].minimum_htlc_in_msat": 32, + "ListPeers.peers[].channels[].next_fee_step": 12, + "ListPeers.peers[].channels[].next_feerate": 11, + "ListPeers.peers[].channels[].opener": 16, + "ListPeers.peers[].channels[].our_reserve_msat": 29, + "ListPeers.peers[].channels[].our_to_self_delay": 34, + "ListPeers.peers[].channels[].out_fulfilled_msat": 45, + "ListPeers.peers[].channels[].out_offered_msat": 43, + "ListPeers.peers[].channels[].out_payments_fulfilled": 44, + "ListPeers.peers[].channels[].out_payments_offered": 42, + "ListPeers.peers[].channels[].owner": 4, + "ListPeers.peers[].channels[].private": 15, + "ListPeers.peers[].channels[].receivable_msat": 31, + "ListPeers.peers[].channels[].scratch_txid": 2, + "ListPeers.peers[].channels[].short_channel_id": 5, + "ListPeers.peers[].channels[].spendable_msat": 30, + "ListPeers.peers[].channels[].state": 1, + "ListPeers.peers[].channels[].state_changes[]": 36, + "ListPeers.peers[].channels[].status[]": 37, + "ListPeers.peers[].channels[].their_reserve_msat": 28, + "ListPeers.peers[].channels[].their_to_self_delay": 33, + "ListPeers.peers[].channels[].to_us_msat": 20, + "ListPeers.peers[].channels[].total_msat": 23, + "ListPeers.peers[].connected": 2, + "ListPeers.peers[].features": 6, + "ListPeers.peers[].id": 1, + "ListPeers.peers[].log[]": 3, + "ListPeers.peers[].log[].data": 7, + "ListPeers.peers[].log[].log": 5, + "ListPeers.peers[].log[].node_id": 6, + "ListPeers.peers[].log[].num_skipped": 2, + "ListPeers.peers[].log[].source": 4, + "ListPeers.peers[].log[].time": 3, + "ListPeers.peers[].log[].type": 1, + "ListPeers.peers[].netaddr[]": 5 + } +} \ No newline at end of file diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 76610e9f6209..b834c9c34329 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -31,13 +31,20 @@ message GetinfoResponse { uint32 num_inactive_channels = 7; string version = 8; string lightning_dir = 9; - uint32 blockheight = 10; - string network = 11; - Amount fees_collected_msat = 12; - repeated GetinfoAddress address = 13; - repeated GetinfoBinding binding = 14; - optional string warning_bitcoind_sync = 15; - optional string warning_lightningd_sync = 16; + uint32 blockheight = 11; + string network = 12; + Amount fees_collected_msat = 13; + repeated GetinfoAddress address = 14; + repeated GetinfoBinding binding = 15; + optional string warning_bitcoind_sync = 16; + optional string warning_lightningd_sync = 17; +} + +message GetinfoOur_features { + bytes init = 1; + bytes node = 2; + bytes channel = 3; + bytes invoice = 4; } message GetinfoAddress { @@ -76,7 +83,7 @@ message ListpeersRequest { } message ListpeersResponse { - repeated ListpeersPeers peers = 1; + repeated ListpeersPeers peers = 3; } message ListpeersPeers { @@ -129,43 +136,44 @@ message ListpeersPeersChannels { optional string short_channel_id = 5; optional bytes channel_id = 6; optional bytes funding_txid = 7; - optional string initial_feerate = 8; - optional string last_feerate = 9; - optional string next_feerate = 10; - optional uint32 next_fee_step = 11; - repeated ListpeersPeersChannelsInflight inflight = 12; - optional bytes close_to = 13; - optional bool private = 14; - ChannelSide opener = 15; - optional ChannelSide closer = 16; - repeated string features = 17; - optional Amount to_us_msat = 19; - optional Amount min_to_us_msat = 20; - optional Amount max_to_us_msat = 21; - optional Amount total_msat = 22; - optional Amount fee_base_msat = 23; - optional uint32 fee_proportional_millionths = 24; - optional Amount dust_limit_msat = 25; - optional Amount max_total_htlc_in_msat = 26; - optional Amount their_reserve_msat = 27; - optional Amount our_reserve_msat = 28; - optional Amount spendable_msat = 29; - optional Amount receivable_msat = 30; - optional Amount minimum_htlc_in_msat = 31; - optional uint32 their_to_self_delay = 32; - optional uint32 our_to_self_delay = 33; - optional uint32 max_accepted_htlcs = 34; - repeated string status = 36; - optional uint64 in_payments_offered = 37; - optional Amount in_offered_msat = 38; - optional uint64 in_payments_fulfilled = 39; - optional Amount in_fulfilled_msat = 40; - optional uint64 out_payments_offered = 41; - optional Amount out_offered_msat = 42; - optional uint64 out_payments_fulfilled = 43; - optional Amount out_fulfilled_msat = 44; - repeated ListpeersPeersChannelsHtlcs htlcs = 45; - optional string close_to_addr = 46; + optional uint32 funding_outnum = 8; + optional string initial_feerate = 9; + optional string last_feerate = 10; + optional string next_feerate = 11; + optional uint32 next_fee_step = 12; + repeated ListpeersPeersChannelsInflight inflight = 13; + optional bytes close_to = 14; + optional bool private = 15; + ChannelSide opener = 16; + optional ChannelSide closer = 17; + repeated string features = 18; + optional Amount to_us_msat = 20; + optional Amount min_to_us_msat = 21; + optional Amount max_to_us_msat = 22; + optional Amount total_msat = 23; + optional Amount fee_base_msat = 24; + optional uint32 fee_proportional_millionths = 25; + optional Amount dust_limit_msat = 26; + optional Amount max_total_htlc_in_msat = 27; + optional Amount their_reserve_msat = 28; + optional Amount our_reserve_msat = 29; + optional Amount spendable_msat = 30; + optional Amount receivable_msat = 31; + optional Amount minimum_htlc_in_msat = 32; + optional uint32 their_to_self_delay = 33; + optional uint32 our_to_self_delay = 34; + optional uint32 max_accepted_htlcs = 35; + repeated string status = 37; + optional uint64 in_payments_offered = 38; + optional Amount in_offered_msat = 39; + optional uint64 in_payments_fulfilled = 40; + optional Amount in_fulfilled_msat = 41; + optional uint64 out_payments_offered = 42; + optional Amount out_offered_msat = 43; + optional uint64 out_payments_fulfilled = 44; + optional Amount out_fulfilled_msat = 45; + repeated ListpeersPeersChannelsHtlcs htlcs = 46; + optional string close_to_addr = 47; } message ListpeersPeersChannelsFeerate { @@ -185,6 +193,7 @@ message ListpeersPeersChannelsInflight { message ListpeersPeersChannelsFunding { Amount local_msat = 1; Amount remote_msat = 2; + Amount pushed_msat = 3; } message ListpeersPeersChannelsHtlcs { @@ -221,8 +230,8 @@ message ListfundsRequest { } message ListfundsResponse { - repeated ListfundsOutputs outputs = 1; - repeated ListfundsChannels channels = 2; + repeated ListfundsOutputs outputs = 2; + repeated ListfundsChannels channels = 3; } message ListfundsOutputs { @@ -260,24 +269,25 @@ message ListchannelsRequest { } message ListchannelsResponse { - repeated ListchannelsChannels channels = 1; + repeated ListchannelsChannels channels = 4; } message ListchannelsChannels { bytes source = 1; bytes destination = 2; - bool public = 3; - Amount amount_msat = 4; - uint32 message_flags = 5; - uint32 channel_flags = 6; - bool active = 7; - uint32 last_update = 8; - uint32 base_fee_millisatoshi = 9; - uint32 fee_per_millionth = 10; - uint32 delay = 11; - Amount htlc_minimum_msat = 12; - optional Amount htlc_maximum_msat = 13; - bytes features = 14; + string short_channel_id = 3; + bool public = 4; + Amount amount_msat = 5; + uint32 message_flags = 6; + uint32 channel_flags = 7; + bool active = 8; + uint32 last_update = 9; + uint32 base_fee_millisatoshi = 10; + uint32 fee_per_millionth = 11; + uint32 delay = 12; + Amount htlc_minimum_msat = 13; + optional Amount htlc_maximum_msat = 14; + bytes features = 15; } message AddgossipRequest { @@ -293,9 +303,9 @@ message AutocleaninvoiceRequest { } message AutocleaninvoiceResponse { - bool enabled = 1; - optional uint64 expired_by = 2; - optional uint64 cycle_seconds = 3; + bool enabled = 3; + optional uint64 expired_by = 1; + optional uint64 cycle_seconds = 2; } message CheckmessageRequest { @@ -305,8 +315,8 @@ message CheckmessageRequest { } message CheckmessageResponse { - bool verified = 1; - optional bytes pubkey = 2; + bool verified = 4; + optional bytes pubkey = 3; } message CloseRequest { @@ -325,7 +335,7 @@ message CloseResponse { UNILATERAL = 1; UNOPENED = 2; } - CloseType item_type = 1; - optional bytes tx = 2; - optional bytes txid = 3; + CloseType item_type = 7; + optional bytes tx = 8; + optional bytes txid = 9; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 3efce232d4b3..5dbfd54eef97 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -110,6 +110,7 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { short_channel_id: c.short_channel_id.clone(), channel_id: c.channel_id.as_ref().map(|v| hex::decode(&v).unwrap()), funding_txid: c.funding_txid.as_ref().map(|v| hex::decode(&v).unwrap()), + funding_outnum: c.funding_outnum.clone(), initial_feerate: c.initial_feerate.clone(), last_feerate: c.last_feerate.clone(), next_feerate: c.next_feerate.clone(), @@ -222,6 +223,7 @@ impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { Self { source: hex::decode(&c.source).unwrap(), destination: hex::decode(&c.destination).unwrap(), + short_channel_id: c.short_channel_id.clone(), public: c.public.clone(), amount_msat: Some(c.amount_msat.into()), message_flags: c.message_flags.into(), diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index d7c6e699532b..3e6a0fc76f93 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -123,6 +123,18 @@ pub mod responses { #[allow(unused_imports)] use serde::{{Deserialize, Serialize}}; + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetinfoOur_features { + #[serde(alias = "init")] + pub init: String, + #[serde(alias = "node")] + pub node: String, + #[serde(alias = "channel")] + pub channel: String, + #[serde(alias = "invoice")] + pub invoice: String, + } + /// Type of connection #[derive(Copy, Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] @@ -345,6 +357,8 @@ pub mod responses { pub local_msat: Amount, #[serde(alias = "remote_msat")] pub remote_msat: Amount, + #[serde(alias = "pushed_msat")] + pub pushed_msat: Amount, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -453,6 +467,8 @@ pub mod responses { pub channel_id: Option, #[serde(alias = "funding_txid", skip_serializing_if = "Option::is_none")] pub funding_txid: Option, + #[serde(alias = "funding_outnum", skip_serializing_if = "Option::is_none")] + pub funding_outnum: Option, #[serde(alias = "initial_feerate", skip_serializing_if = "Option::is_none")] pub initial_feerate: Option, #[serde(alias = "last_feerate", skip_serializing_if = "Option::is_none")] @@ -461,7 +477,7 @@ pub mod responses { pub next_feerate: Option, #[serde(alias = "next_fee_step", skip_serializing_if = "Option::is_none")] pub next_fee_step: Option, - #[serde(alias = "inflight[]")] + #[serde(alias = "inflight")] pub inflight: Vec, #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] pub close_to: Option, @@ -471,7 +487,7 @@ pub mod responses { #[serde(rename = "opener")] pub opener: ChannelSide, pub closer: Option, - #[serde(alias = "features[]")] + #[serde(alias = "features")] pub features: Vec, #[serde(alias = "to_us_msat", skip_serializing_if = "Option::is_none")] pub to_us_msat: Option, @@ -505,9 +521,9 @@ pub mod responses { pub our_to_self_delay: Option, #[serde(alias = "max_accepted_htlcs", skip_serializing_if = "Option::is_none")] pub max_accepted_htlcs: Option, - #[serde(alias = "state_changes[]")] + #[serde(alias = "state_changes")] pub state_changes: Vec, - #[serde(alias = "status[]")] + #[serde(alias = "status")] pub status: Vec, #[serde(alias = "in_payments_offered", skip_serializing_if = "Option::is_none")] pub in_payments_offered: Option, @@ -525,7 +541,7 @@ pub mod responses { pub out_payments_fulfilled: Option, #[serde(alias = "out_fulfilled_msat", skip_serializing_if = "Option::is_none")] pub out_fulfilled_msat: Option, - #[serde(alias = "htlcs[]")] + #[serde(alias = "htlcs")] pub htlcs: Vec, #[serde(alias = "close_to_addr", skip_serializing_if = "Option::is_none")] pub close_to_addr: Option, @@ -537,11 +553,11 @@ pub mod responses { pub id: String, #[serde(alias = "connected")] pub connected: bool, - #[serde(alias = "log[]")] + #[serde(alias = "log")] pub log: Vec, - #[serde(alias = "channels[]")] + #[serde(alias = "channels")] pub channels: Vec, - #[serde(alias = "netaddr[]")] + #[serde(alias = "netaddr")] pub netaddr: Vec, #[serde(alias = "features", skip_serializing_if = "Option::is_none")] pub features: Option, @@ -549,7 +565,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersResponse { - #[serde(alias = "peers[]")] + #[serde(alias = "peers")] pub peers: Vec, } @@ -628,6 +644,8 @@ pub mod responses { pub source: String, #[serde(alias = "destination")] pub destination: String, + #[serde(alias = "short_channel_id")] + pub short_channel_id: String, #[serde(alias = "public")] pub public: bool, #[serde(alias = "amount_msat")] diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 6b4069e17727..5700b9764a28 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -126,12 +126,12 @@ def load_jsonrpc_service(): return service -def gengrpc(service): +def gengrpc(service, meta): """Load all mapped RPC methods, wrap them in a Service, and split them into messages. """ fname = repo_root() / "cln-grpc" / "proto" / "node.proto" dest = open(fname, "w") - GrpcGenerator(dest).generate(service) + GrpcGenerator(dest, meta).generate(service) fname = repo_root() / "cln-grpc" / "src" / "convert.rs" dest = open(fname, "w") @@ -149,10 +149,22 @@ def genrustjsonrpc(service): RustGenerator(dest).generate(service) +def load_msggen_meta(): + meta = json.load(open('.msggen.json', 'r')) + return meta + + +def write_msggen_meta(meta): + with open('.msggen.json', 'w') as f: + json.dump(meta, f, sort_keys=True, indent=4) + + def run(): service = load_jsonrpc_service() - gengrpc(service) + meta = load_msggen_meta() + gengrpc(service, meta) genrustjsonrpc(service) + write_msggen_meta(meta) if __name__ == "__main__": diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 5f0421b92ceb..73e1fe2aee52 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -1,6 +1,6 @@ # A grpc model from .model import ArrayField, Field, CompositeField, EnumField, PrimitiveField, Service -from typing import TextIO, List +from typing import TextIO, List, Dict, Any from textwrap import indent, dedent import re import logging @@ -44,9 +44,10 @@ class GrpcGenerator: """A generator that generates protobuf files. """ - def __init__(self, dest: TextIO): + def __init__(self, dest: TextIO, meta: Dict[str, Any]): self.dest = dest self.logger = logging.getLogger("msggen.grpc.GrpcGenerator") + self.meta = meta def write(self, text: str, cleanup: bool = True) -> None: if cleanup: @@ -54,6 +55,56 @@ def write(self, text: str, cleanup: bool = True) -> None: else: self.dest.write(text) + def field2number(self, field): + m = self.meta['grpc-field-map'] + # Simple case first: if we've already assigned a number let's reuse that + if field.path in m: + return m[field.path] + + # Now let's find the highest number we have in the parent + # context + parent = '.'.join(field.path.split('.')[:-1]) + maxnum = 0 + for k, v in m.items(): + parent2 = '.'.join(k.split('.')[:-1]) + if parent2 == parent: + maxnum = max(maxnum, v) + + self.meta['grpc-field-map'][field.path] = maxnum + 1 + self.logger.warn(f"Assigning new field number to {field.path} => {m[field.path]}") + + return self.meta['grpc-field-map'][field.path] + + def enumerate_fields(self, fields): + """Use the meta map to identify which number this field will get. + """ + for f in fields: + yield (self.field2number(f), f) + + def enumvar2number(self, typename, variant): + """Find an existing variant number of generate a new one. + + If we don't have a variant number yet we'll just take the + largest one assigned so far and increment it by 1. """ + m = self.meta['grpc-enum-map'] + variant = str(variant) + if typename not in m: + m[typename] = {} + + variants = m[typename] + if variant in variants: + return variants[variant] + + # Now find the maximum and increment once + n = max(variants.values()) if len(variants) else -1 + + m[typename][variant] = n + 1 + return m[typename][variant] + + def enumerate_enum(self, typename, variants): + for v in variants: + yield (self.enumvar2number(typename, v), v) + def gather_types(self, service): """Gather all types that might need to be defined. """ @@ -100,7 +151,7 @@ def generate_enum(self, e: EnumField, indent=0): self.write(f"{prefix}// {e.path}\n", False) self.write(f"{prefix}enum {e.typename} {{\n", False) - for i, v in enumerate(e.variants): + for i, v in self.enumerate_enum(e.typename, e.variants): self.logger.debug(f"Generating enum variant {v}") self.write(f"{prefix}\t{v.normalized()} = {i};\n", False) @@ -115,11 +166,11 @@ def generate_message(self, message: CompositeField): """) # Declare enums inline so they are scoped correctly in C++ - for i, f in enumerate(message.fields): + for _, f in enumerate(message.fields): if isinstance(f, EnumField) and f.path not in overrides.keys(): self.generate_enum(f, indent=1) - for i, f in enumerate(message.fields): + for i, f in self.enumerate_fields(message.fields): if overrides.get(f.path, "") is None: continue @@ -128,17 +179,17 @@ def generate_message(self, message: CompositeField): typename = typemap.get(f.itemtype.typename, f.itemtype.typename) if f.path in overrides: typename = overrides[f.path] - self.write(f"\trepeated {typename} {f.normalized()} = {i+1};\n", False) + self.write(f"\trepeated {typename} {f.normalized()} = {i};\n", False) elif isinstance(f, PrimitiveField): typename = typemap.get(f.typename, f.typename) if f.path in overrides: typename = overrides[f.path] - self.write(f"\t{opt}{typename} {f.normalized()} = {i+1};\n", False) + self.write(f"\t{opt}{typename} {f.normalized()} = {i};\n", False) elif isinstance(f, EnumField): typename = f.typename if f.path in overrides: typename = overrides[f.path] - self.write(f"\t{opt}{typename} {f.normalized()} = {i+1};\n", False) + self.write(f"\t{opt}{typename} {f.normalized()} = {i};\n", False) self.write(f"""}} """) From a8aa9bd5ae2be86ec364176b2dfe12655c661f14 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 15 Mar 2022 13:37:46 +0100 Subject: [PATCH 0460/1530] cln-rpc: Fixed two minor compiler warnings --- cln-grpc/src/server.rs | 1 - cln-rpc/examples/getinfo.rs | 1 - contrib/msggen/msggen/grpc.py | 1 - 3 files changed, 3 deletions(-) diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 21b06a67c066..3576bac86436 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -5,7 +5,6 @@ use anyhow::Result; use std::path::{Path, PathBuf}; use cln_rpc::model::requests; use log::debug; -use crate::convert::*; use tonic::{Code, Status}; #[derive(Clone)] diff --git a/cln-rpc/examples/getinfo.rs b/cln-rpc/examples/getinfo.rs index a6dd279894e2..5cbebdfaf1a4 100644 --- a/cln-rpc/examples/getinfo.rs +++ b/cln-rpc/examples/getinfo.rs @@ -1,6 +1,5 @@ use anyhow::Context; use cln_rpc::{model::GetinfoRequest, ClnRpc, Request}; -use log::info; use std::env::args; use std::path::Path; use tokio; diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 73e1fe2aee52..5b9e6763c269 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -397,7 +397,6 @@ def generate(self, service: Service) -> None: use std::path::{{Path, PathBuf}}; use cln_rpc::model::requests; use log::debug; - use crate::convert::*; use tonic::{{Code, Status}}; #[derive(Clone)] From 508b8cb54aaa73e7c57c5c6251a3adfc3821f3b0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 15 Mar 2022 19:18:30 +1030 Subject: [PATCH 0461/1530] doc: fix sendonionmessage documentation. It's a low-level interface now, expecting you to build your own TLVs. Signed-off-by: Rusty Russell Reported-by: @valentinewallace --- doc/lightning-sendonionmessage.7.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/doc/lightning-sendonionmessage.7.md b/doc/lightning-sendonionmessage.7.md index 13a04af7cfeb..7da3b9d96b0b 100644 --- a/doc/lightning-sendonionmessage.7.md +++ b/doc/lightning-sendonionmessage.7.md @@ -6,7 +6,7 @@ SYNOPSIS **(WARNING: experimental-onion-messages only)** -**sendonionmessage** *hops* [*reply_path*] +**sendonionmessage** *first_id* *blinding* *hops* DESCRIPTION ----------- @@ -16,14 +16,7 @@ the lightning network. These are currently used by *offers* to request and receive invoices. *hops* is an array of json objects: *id* as a public key of the node, -and either *rawtlv* containing a hexidecimal TLV to include, or any of -the fields *short_channel_id*, *blinding*, *enctlv*, *invoice*, -*invoice_request* and *invoice_error* to construct the onionmessage -TLV with. - -*reply_path* is a json object, containing a pubkey *blinding*, and an -array *path* of objects containing *id* (a pubkey) and *enctlv* (a hex -value, optional for final element). +and *tlv* contains a hexidecimal TLV to include. RETURN VALUE ------------ From 704162f24a312bb902a5670656f5ee7eb3877289 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 28 Feb 2022 18:53:13 +0100 Subject: [PATCH 0462/1530] docker: Build the docker image with psql support Changelog-Fixed: docker: The docker image is now built with postgresql support --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index c79d34400dad..5e4efddf4b6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,6 +59,7 @@ RUN apt-get update -qq && \ gettext \ git \ gnupg \ + libpq-dev \ libtool \ libffi-dev \ python3 \ @@ -108,7 +109,7 @@ RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVE FROM debian:buster-slim as final COPY --from=downloader /opt/tini /usr/bin/tini -RUN apt-get update && apt-get install -y --no-install-recommends socat inotify-tools python3 python3-pip \ +RUN apt-get update && apt-get install -y --no-install-recommends socat inotify-tools python3 python3-pip libpq5\ && rm -rf /var/lib/apt/lists/* ENV LIGHTNINGD_DATA=/root/.lightning From 36466af3eb848fde37d53d422c73fa887ea34335 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Tue, 14 Dec 2021 17:37:35 -0800 Subject: [PATCH 0463/1530] hsmd: Add fields to hsmd_sign_{,remote_}commitment_tx for validating signers --- channeld/channeld.c | 21 ++++++++++++- common/htlc_wire.c | 34 +++++++++++++++++++++ common/htlc_wire.h | 17 +++++++++++ gossipd/Makefile | 1 + hsmd/Makefile | 6 +++- hsmd/hsmd_wire.csv | 6 ++++ hsmd/libhsmd.c | 11 +++++-- lightningd/peer_control.c | 5 ++- lightningd/test/Makefile | 2 ++ lightningd/test/run-invoice-select-inchan.c | 2 +- openingd/dualopend.c | 16 ++++++++-- openingd/openingd.c | 16 ++++++++-- tools/generate-wire.py | 1 + wallet/test/run-wallet.c | 2 +- 14 files changed, 129 insertions(+), 11 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 1d85ff2ed71a..98cff384f17b 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1008,11 +1008,30 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, const u8 *msg; struct bitcoin_signature *htlc_sigs; + /* Collect the htlcs for call to hsmd. */ + struct simple_htlc **htlcs = tal_arr(tmpctx, struct simple_htlc *, 0); + size_t num_entries = tal_count(htlc_map); + for (size_t ndx = 0; ndx < num_entries; ++ndx) { + struct htlc const *hh = htlc_map[ndx]; + if (hh) { + struct simple_htlc *simple = + new_simple_htlc(htlcs, + htlc_state_owner(hh->state), + hh->amount, + &hh->rhash, + hh->expiry.locktime); + tal_arr_expand(&htlcs, simple); + } + } + msg = towire_hsmd_sign_remote_commitment_tx(NULL, txs[0], &peer->channel->funding_pubkey[REMOTE], &peer->remote_per_commit, channel_has(peer->channel, - OPT_STATIC_REMOTEKEY)); + OPT_STATIC_REMOTEKEY), + commit_index, + (const struct simple_htlc **) htlcs, + channel_feerate(peer->channel, REMOTE)); msg = hsm_req(tmpctx, take(msg)); if (!fromwire_hsmd_sign_tx_reply(msg, commit_sig)) diff --git a/common/htlc_wire.c b/common/htlc_wire.c index 4385658b3ce5..72ce1ac3de39 100644 --- a/common/htlc_wire.c +++ b/common/htlc_wire.c @@ -24,6 +24,20 @@ static struct failed_htlc *failed_htlc_dup(const tal_t *ctx, return newf; } +struct simple_htlc *new_simple_htlc(const tal_t *ctx, + enum side side, + struct amount_msat amount, + const struct sha256 *payment_hash, + u32 cltv_expiry) +{ + struct simple_htlc *simple = tal(ctx, struct simple_htlc); + simple->side = side; + simple->amount = amount; + simple->payment_hash = *payment_hash; + simple->cltv_expiry = cltv_expiry; + return simple; +} + struct existing_htlc *new_existing_htlc(const tal_t *ctx, u64 id, enum htlc_state state, @@ -100,6 +114,14 @@ void towire_existing_htlc(u8 **pptr, const struct existing_htlc *existing) towire_bool(pptr, false); } +void towire_simple_htlc(u8 **pptr, const struct simple_htlc *simple) +{ + towire_side(pptr, simple->side); + towire_amount_msat(pptr, simple->amount); + towire_sha256(pptr, &simple->payment_hash); + towire_u32(pptr, simple->cltv_expiry); +} + void towire_fulfilled_htlc(u8 **pptr, const struct fulfilled_htlc *fulfilled) { towire_u64(pptr, fulfilled->id); @@ -197,6 +219,18 @@ struct existing_htlc *fromwire_existing_htlc(const tal_t *ctx, return existing; } +struct simple_htlc *fromwire_simple_htlc(const tal_t *ctx, + const u8 **cursor, size_t *max) +{ + struct simple_htlc *simple = tal(ctx, struct simple_htlc); + + simple->side = fromwire_side(cursor, max); + simple->amount = fromwire_amount_msat(cursor, max); + fromwire_sha256(cursor, max, &simple->payment_hash); + simple->cltv_expiry = fromwire_u32(cursor, max); + return simple; +} + void fromwire_fulfilled_htlc(const u8 **cursor, size_t *max, struct fulfilled_htlc *fulfilled) { diff --git a/common/htlc_wire.h b/common/htlc_wire.h index 80fe232172b6..b89b5961a90b 100644 --- a/common/htlc_wire.h +++ b/common/htlc_wire.h @@ -63,6 +63,14 @@ struct changed_htlc { u64 id; }; +/* For signing interfaces */ +struct simple_htlc { + enum side side; + struct amount_msat amount; + struct sha256 payment_hash; + u32 cltv_expiry; +}; + struct existing_htlc *new_existing_htlc(const tal_t *ctx, u64 id, enum htlc_state state, @@ -74,8 +82,15 @@ struct existing_htlc *new_existing_htlc(const tal_t *ctx, const struct preimage *preimage TAKES, const struct failed_htlc *failed TAKES); +struct simple_htlc *new_simple_htlc(const tal_t *ctx, + enum side side, + struct amount_msat amount, + const struct sha256 *payment_hash, + u32 cltv_expiry); + void towire_added_htlc(u8 **pptr, const struct added_htlc *added); void towire_existing_htlc(u8 **pptr, const struct existing_htlc *existing); +void towire_simple_htlc(u8 **pptr, const struct simple_htlc *simple); void towire_fulfilled_htlc(u8 **pptr, const struct fulfilled_htlc *fulfilled); void towire_failed_htlc(u8 **pptr, const struct failed_htlc *failed); void towire_changed_htlc(u8 **pptr, const struct changed_htlc *changed); @@ -86,6 +101,8 @@ void fromwire_added_htlc(const u8 **cursor, size_t *max, struct added_htlc *added); struct existing_htlc *fromwire_existing_htlc(const tal_t *ctx, const u8 **cursor, size_t *max); +struct simple_htlc *fromwire_simple_htlc(const tal_t *ctx, + const u8 **cursor, size_t *max); void fromwire_fulfilled_htlc(const u8 **cursor, size_t *max, struct fulfilled_htlc *fulfilled); struct failed_htlc *fromwire_failed_htlc(const tal_t *ctx, const u8 **cursor, diff --git a/gossipd/Makefile b/gossipd/Makefile index 6fbb6a7e450c..c31f3486c6d1 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -50,6 +50,7 @@ GOSSIPD_COMMON_OBJS := \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ + common/onionreply.o \ common/per_peer_state.o \ common/ping.o \ common/psbt_open.o \ diff --git a/hsmd/Makefile b/hsmd/Makefile index 91376292d051..bc396cd07021 100644 --- a/hsmd/Makefile +++ b/hsmd/Makefile @@ -10,7 +10,9 @@ HSMD_OBJS := $(HSMD_SRC:.c=.o) $(HSMD_OBJS): $(HSMD_HEADERS) # Other programs which use the hsm need this. -HSMD_CLIENT_OBJS := hsmd/hsmd_wiregen.o +HSMD_CLIENT_OBJS := \ + hsmd/hsmd_wiregen.o \ + common/htlc_wire.o # Make sure these depend on everything. ALL_C_SOURCES += $(HSMD_SRC) @@ -32,11 +34,13 @@ HSMD_COMMON_OBJS := \ common/status_wiregen.o \ common/hash_u5.o \ common/hsm_encryption.o \ + common/htlc_wire.o \ common/key_derive.o \ common/lease_rates.o \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ + common/onionreply.o \ common/permute_tx.o \ common/psbt_open.o \ common/pseudorand.o \ diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 2359341a41da..a92402eb54d1 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -133,6 +133,7 @@ msgdata,hsmd_sign_commitment_tx,peer_id,node_id, msgdata,hsmd_sign_commitment_tx,channel_dbid,u64, msgdata,hsmd_sign_commitment_tx,tx,bitcoin_tx, msgdata,hsmd_sign_commitment_tx,remote_funding_key,pubkey, +msgdata,hsmd_sign_commitment_tx,commit_num,u64, msgtype,hsmd_sign_commitment_tx_reply,105 msgdata,hsmd_sign_commitment_tx_reply,sig,bitcoin_signature, @@ -176,11 +177,16 @@ msgdata,hsmd_sign_local_htlc_tx,wscript,u8,wscript_len msgdata,hsmd_sign_local_htlc_tx,option_anchor_outputs,bool, # Openingd/channeld asks HSM to sign the other sides' commitment tx. +#include msgtype,hsmd_sign_remote_commitment_tx,19 msgdata,hsmd_sign_remote_commitment_tx,tx,bitcoin_tx, msgdata,hsmd_sign_remote_commitment_tx,remote_funding_key,pubkey, msgdata,hsmd_sign_remote_commitment_tx,remote_per_commit,pubkey, msgdata,hsmd_sign_remote_commitment_tx,option_static_remotekey,bool, +msgdata,hsmd_sign_remote_commitment_tx,commit_num,u64, +msgdata,hsmd_sign_remote_commitment_tx,num_htlcs,u16, +msgdata,hsmd_sign_remote_commitment_tx,htlcs,simple_htlc,num_htlcs +msgdata,hsmd_sign_remote_commitment_tx,feerate,u32, # channeld asks HSM to sign remote HTLC tx. msgtype,hsmd_sign_remote_htlc_tx,20 diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 13b0fe03aa59..fa488724bea2 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -1203,12 +1203,17 @@ static u8 *handle_sign_remote_commitment_tx(struct hsmd_client *c, const u8 *msg const u8 *funding_wscript; struct pubkey remote_per_commit; bool option_static_remotekey; + u64 commit_num; + struct simple_htlc **htlc; + u32 feerate; if (!fromwire_hsmd_sign_remote_commitment_tx(tmpctx, msg_in, &tx, &remote_funding_pubkey, &remote_per_commit, - &option_static_remotekey)) + &option_static_remotekey, + &commit_num, + &htlc, &feerate)) return hsmd_status_malformed_request(c, msg_in); tx->chainparams = c->chainparams; @@ -1293,13 +1298,15 @@ static u8 *handle_sign_commitment_tx(struct hsmd_client *c, const u8 *msg_in) struct secret channel_seed; struct bitcoin_tx *tx; struct bitcoin_signature sig; + u64 commit_num; struct secrets secrets; const u8 *funding_wscript; if (!fromwire_hsmd_sign_commitment_tx(tmpctx, msg_in, &peer_id, &dbid, &tx, - &remote_funding_pubkey)) + &remote_funding_pubkey, + &commit_num)) return hsmd_status_malformed_request(c, msg_in); tx->chainparams = c->chainparams; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index d7607535ad09..0d776ba0510e 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -186,13 +186,16 @@ static void sign_last_tx(struct channel *channel, struct bitcoin_signature sig; u8 *msg, **witness; + u64 commit_index = channel->next_index[LOCAL] - 1; + assert(!last_tx->wtx->inputs[0].witness); msg = towire_hsmd_sign_commitment_tx(tmpctx, &channel->peer->id, channel->dbid, last_tx, &channel->channel_info - .remote_fundingkey); + .remote_fundingkey, + commit_index); if (!wire_sync_write(ld->hsm_fd, take(msg))) fatal("Could not write to HSM: %s", strerror(errno)); diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index e513a6fc2db6..660a7aeee067 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -14,12 +14,14 @@ LIGHTNINGD_TEST_COMMON_OBJS := \ common/bech32.o \ common/daemon_conn.o \ common/htlc_state.o \ + common/htlc_wire.o \ common/json.o \ common/key_derive.o \ common/pseudorand.o \ common/random_select.o \ common/memleak.o \ common/msg_queue.o \ + common/onionreply.o \ common/setup.o \ common/utils.o \ common/utxo.o \ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 6df4809eb53d..9e95ca3e0430 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -652,7 +652,7 @@ u8 *towire_gossipd_remote_addr(const tal_t *ctx UNNEEDED, const struct wireaddr u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED, const u8 *publictweak UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_commitment_tx */ -u8 *towire_hsmd_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED) +u8 *towire_hsmd_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED, u64 commit_num UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_commitment_tx called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_invoice */ u8 *towire_hsmd_sign_invoice(const tal_t *ctx UNNEEDED, const u8 *u5bytes UNNEEDED, const u8 *hrp UNNEEDED) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index d5ab273b15f6..261b3d9c17dd 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1901,11 +1901,17 @@ static u8 *accepter_commits(struct state *state, } /* Make HSM sign it */ + struct simple_htlc **htlcs = tal_arr(tmpctx, struct simple_htlc *, 0); + u32 feerate = 0; // unused since there are no htlcs + u64 commit_num = 0; msg = towire_hsmd_sign_remote_commitment_tx(NULL, remote_commit, &state->channel->funding_pubkey[REMOTE], &state->first_per_commitment_point[REMOTE], - true); + true, + commit_num, + (const struct simple_htlc **) htlcs, + feerate); wire_sync_write(HSM_FD, take(msg)); msg = wire_sync_read(tmpctx, HSM_FD); if (!fromwire_hsmd_sign_tx_reply(msg, &local_sig)) @@ -2494,11 +2500,17 @@ static u8 *opener_commits(struct state *state, * witness script. It also needs the amount of the funding output, * as segwit signatures commit to that as well, even though it doesn't * explicitly appear in the transaction itself. */ + struct simple_htlc **htlcs = tal_arr(tmpctx, struct simple_htlc *, 0); + u32 feerate = 0; // unused since there are no htlcs + u64 commit_num = 0; msg = towire_hsmd_sign_remote_commitment_tx(NULL, remote_commit, &state->channel->funding_pubkey[REMOTE], &state->first_per_commitment_point[REMOTE], - true); + true, + commit_num, + (const struct simple_htlc **) htlcs, + feerate); wire_sync_write(HSM_FD, take(msg)); msg = wire_sync_read(tmpctx, HSM_FD); if (!fromwire_hsmd_sign_tx_reply(msg, &local_sig)) diff --git a/openingd/openingd.c b/openingd/openingd.c index 282213c88322..82c097249a4b 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -594,12 +594,18 @@ static bool funder_finalize_channel_setup(struct state *state, * witness script. It also needs the amount of the funding output, * as segwit signatures commit to that as well, even though it doesn't * explicitly appear in the transaction itself. */ + struct simple_htlc **htlcs = tal_arr(tmpctx, struct simple_htlc *, 0); + u32 feerate = 0; // unused since there are no htlcs + u64 commit_num = 0; msg = towire_hsmd_sign_remote_commitment_tx(NULL, *tx, &state->channel->funding_pubkey[REMOTE], &state->first_per_commitment_point[REMOTE], channel_has(state->channel, - OPT_STATIC_REMOTEKEY)); + OPT_STATIC_REMOTEKEY), + commit_num, + (const struct simple_htlc **) htlcs, + feerate); wire_sync_write(HSM_FD, take(msg)); msg = wire_sync_read(tmpctx, HSM_FD); @@ -1185,12 +1191,18 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) } /* Make HSM sign it */ + struct simple_htlc **htlcs = tal_arr(tmpctx, struct simple_htlc *, 0); + u32 feerate = 0; // unused since there are no htlcs + u64 commit_num = 0; msg = towire_hsmd_sign_remote_commitment_tx(NULL, remote_commit, &state->channel->funding_pubkey[REMOTE], &state->first_per_commitment_point[REMOTE], channel_has(state->channel, - OPT_STATIC_REMOTEKEY)); + OPT_STATIC_REMOTEKEY), + commit_num, + (const struct simple_htlc **) htlcs, + feerate); wire_sync_write(HSM_FD, take(msg)); msg = wire_sync_read(tmpctx, HSM_FD); diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 6a74a34a3ba1..d5498ae67469 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -228,6 +228,7 @@ class Type(FieldSet): 'gossip_getchannels_entry', 'failed_htlc', 'existing_htlc', + 'simple_htlc', 'utxo', 'bitcoin_tx', 'wirestring', diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 890ed17b57fe..5c28d58f620d 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -759,7 +759,7 @@ u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_i u8 *towire_hsmd_new_channel(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 dbid UNNEEDED) { fprintf(stderr, "towire_hsmd_new_channel called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_commitment_tx */ -u8 *towire_hsmd_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED) +u8 *towire_hsmd_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED, u64 commit_num UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_commitment_tx called!\n"); abort(); } /* Generated stub for towire_incorrect_cltv_expiry */ u8 *towire_incorrect_cltv_expiry(const tal_t *ctx UNNEEDED, u32 cltv_expiry UNNEEDED, const u8 *channel_update UNNEEDED) From 0db05f6e9c54438c3ab5e61090fac2bbb516e019 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 8 Mar 2022 05:01:36 +1030 Subject: [PATCH 0464/1530] lightningd: opt_var_onion is now a compulsory feature. We're about to drop support for legacy. Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 2 +- tests/test_misc.py | 2 +- tests/test_plugin.py | 2 +- tests/utils.py | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index d047650e4f55..241797405d0e 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -815,7 +815,7 @@ static struct feature_set *default_features(const tal_t *ctx) OPTIONAL_FEATURE(OPT_DATA_LOSS_PROTECT), OPTIONAL_FEATURE(OPT_UPFRONT_SHUTDOWN_SCRIPT), OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES), - OPTIONAL_FEATURE(OPT_VAR_ONION), + COMPULSORY_FEATURE(OPT_VAR_ONION), COMPULSORY_FEATURE(OPT_PAYMENT_SECRET), OPTIONAL_FEATURE(OPT_BASIC_MPP), OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX), diff --git a/tests/test_misc.py b/tests/test_misc.py index 219739b48857..376a68a1705a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1784,7 +1784,7 @@ def test_list_features_only(node_factory): expected = ['option_data_loss_protect/odd', 'option_upfront_shutdown_script/odd', 'option_gossip_queries/odd', - 'option_var_onion_optin/odd', + 'option_var_onion_optin/even', 'option_gossip_queries_ex/odd', 'option_static_remotekey/odd', 'option_payment_secret/even', diff --git a/tests/test_plugin.py b/tests/test_plugin.py index bde2da4e74b1..c6b0b15bbf78 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1548,7 +1548,7 @@ def test_plugin_feature_announce(node_factory): # Check the featurebits we've set in the `init` message from # feature-test.py. - assert l1.daemon.is_in_log(r'\[OUT\] 001000022200....{}' + assert l1.daemon.is_in_log(r'\[OUT\] 001000022100....{}' .format(expected_peer_features(extra=[201] + extra))) # Check the invoice featurebit we set in feature-test.py diff --git a/tests/utils.py b/tests/utils.py index 141a27b50289..e56b7d6acd5f 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -25,7 +25,7 @@ def hex_bits(features): def expected_peer_features(wumbo_channels=False, extra=[]): """Return the expected peer features hexstring for this configuration""" - features = [1, 5, 7, 9, 11, 13, 14, 17, 27] + features = [1, 5, 7, 8, 11, 13, 14, 17, 27] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39] @@ -47,7 +47,7 @@ def expected_peer_features(wumbo_channels=False, extra=[]): # features for the 'node' and the 'peer' feature sets def expected_node_features(wumbo_channels=False, extra=[]): """Return the expected node features hexstring for this configuration""" - features = [1, 5, 7, 9, 11, 13, 14, 17, 27, 55] + features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 55] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39] From 45143cc731f89523c905bca1fb152111a0d25641 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 9 Mar 2022 05:30:06 +1030 Subject: [PATCH 0465/1530] pytest: Remove onion test vectors containing legacy onions. I thought about fixing them up, but really these should be in lnprototest anyway. Turns out they're from the spec, so we should actually fix them up there. I moved the vector files into contrib/pyln-proto, since that still needs them. Signed-off-by: Rusty Russell --- contrib/pyln-proto/tests/test_onion.py | 4 ++-- .../tests}/vectors/onion-test-multi-frame.json | 0 .../pyln-proto/tests}/vectors/onion-test-v0.json | 0 tests/test_onion.py | 10 ---------- 4 files changed, 2 insertions(+), 12 deletions(-) rename {tests => contrib/pyln-proto/tests}/vectors/onion-test-multi-frame.json (100%) rename {tests => contrib/pyln-proto/tests}/vectors/onion-test-v0.json (100%) diff --git a/contrib/pyln-proto/tests/test_onion.py b/contrib/pyln-proto/tests/test_onion.py index e4b02c89bff2..f48878cc9db8 100644 --- a/contrib/pyln-proto/tests/test_onion.py +++ b/contrib/pyln-proto/tests/test_onion.py @@ -64,7 +64,7 @@ def test_tu_fields(): dirname = os.path.dirname(__file__) -vector_base = os.path.join(dirname, '..', '..', '..', 'tests', 'vectors') +vector_base = os.path.join(dirname, '..', 'vectors') have_vectors = os.path.exists(os.path.join(vector_base, 'onion-test-v0.json')) @@ -181,7 +181,7 @@ def sphinx_path_from_test_vector(filename: str) -> Tuple[onion.SphinxPath, dict] """Loads a sphinx test vector from the repo root. """ path = os.path.dirname(__file__) - root = os.path.join(path, '..', '..', '..') + root = os.path.join(path, '..') filename = os.path.join(root, filename) v = json.load(open(filename, 'r')) session_key = onion.Secret(bytes.fromhex(v['generate']['session_key'])) diff --git a/tests/vectors/onion-test-multi-frame.json b/contrib/pyln-proto/tests/vectors/onion-test-multi-frame.json similarity index 100% rename from tests/vectors/onion-test-multi-frame.json rename to contrib/pyln-proto/tests/vectors/onion-test-multi-frame.json diff --git a/tests/vectors/onion-test-v0.json b/contrib/pyln-proto/tests/vectors/onion-test-v0.json similarity index 100% rename from tests/vectors/onion-test-v0.json rename to contrib/pyln-proto/tests/vectors/onion-test-v0.json diff --git a/tests/test_onion.py b/tests/test_onion.py index fb1142c9afed..dcafbbacdc16 100644 --- a/tests/test_onion.py +++ b/tests/test_onion.py @@ -83,13 +83,3 @@ def store_onion(o): store_onion(out[-1][5:]) assert(out == ['payload=000000000000000000000000000000000400000004000000000000000000000000']) - - -def test_onion_vectors(oniontool): - tests = [ - 'onion-test-multi-frame.json', - 'onion-test-v0.json'] - - for t in tests: - p = os.path.join(os.path.dirname(__file__), 'vectors', t) - print(subprocess.check_output([oniontool, 'runtest', p]).decode('ASCII')) From 072c4711ec1aff467eba921d12a7d96a89a675a7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 9 Mar 2022 05:30:11 +1030 Subject: [PATCH 0466/1530] pytest: convert test_sendonion_rpc to modern TLV onion. Signed-off-by: Rusty Russell --- tests/test_pay.py | 53 ++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/tests/test_pay.py b/tests/test_pay.py index 75ce58a2bd3f..ea450a8ebe68 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1,4 +1,3 @@ -from binascii import hexlify from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from flaky import flaky # noqa: F401 @@ -2697,25 +2696,41 @@ def test_sendonion_rpc(node_factory): first_hop = route[0] blockheight = l1.rpc.getinfo()['blockheight'] - def serialize_payload(n): + def truncate_encode(i: int): + """Encode a tu64 (or tu32 etc) value""" + ret = struct.pack("!Q", i) + while ret.startswith(b'\0'): + ret = ret[1:] + return ret + + def serialize_payload_tlv(n): block, tx, out = n['channel'].split('x') - payload = hexlify(struct.pack( - "!BQQL", - 0, - int(block) << 40 | int(tx) << 16 | int(out), - int(n['amount_msat']), - blockheight + n['delay'])).decode('ASCII') - payload += "00" * 12 - return payload - def serialize_payload_final_tlv(n, payment_secret: str): - def truncate_encode(i: int): - """Encode a tu64 (or tu32 etc) value""" - ret = struct.pack("!Q", i) - while ret.startswith(b'\0'): - ret = ret[1:] - return ret + payload = TlvPayload() + # BOLT #4: + # 1. type: 2 (`amt_to_forward`) + # 2. data: + # * [`tu64`:`amt_to_forward`] + b = BytesIO() + b.write(truncate_encode(int(n['amount_msat']))) + payload.add_field(2, b.getvalue()) + # BOLT #4: + # 1. type: 4 (`outgoing_cltv_value`) + # 2. data: + # * [`tu32`:`outgoing_cltv_value`] + b = BytesIO() + b.write(truncate_encode(blockheight + n['delay'])) + payload.add_field(4, b.getvalue()) + # BOLT #4: + # 1. type: 6 (`short_channel_id`) + # 2. data: + # * [`short_channel_id`:`short_channel_id`] + b = BytesIO() + b.write(struct.pack("!Q", int(block) << 40 | int(tx) << 16 | int(out))) + payload.add_field(6, b.getvalue()) + return payload.to_bytes().hex() + def serialize_payload_final_tlv(n, payment_secret: str): payload = TlvPayload() # BOLT #4: # 1. type: 2 (`amt_to_forward`) @@ -2729,7 +2744,7 @@ def truncate_encode(i: int): # 2. data: # * [`tu32`:`outgoing_cltv_value`] b = BytesIO() - b.write(truncate_encode(n['delay'])) + b.write(truncate_encode(blockheight + n['delay'])) payload.add_field(4, b.getvalue()) # BOLT #4: # 1. type: 8 (`payment_data`) @@ -2748,7 +2763,7 @@ def truncate_encode(i: int): # We tell the node h about the parameters to use for n (a.k.a. h + 1) hops.append({ "pubkey": h['id'], - "payload": serialize_payload(n) + "payload": serialize_payload_tlv(n) }) # The last hop has a special payload: From 43a833e4052d6439902b8368e309d61f6c421944 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 9 Mar 2022 06:52:22 +1030 Subject: [PATCH 0467/1530] lightningd: remove support for legacy onion format. As per proposal in https://github.com/lightning/bolts/pull/962 Signed-off-by: Rusty Russell Changelog-Removed: protocol: support for legacy onion format removed, since everyone supports the new one. --- common/onion.c | 357 ++++++++------------- common/onion.h | 13 +- common/route.c | 6 - common/route.h | 7 - common/sphinx.c | 2 +- common/test/run-sphinx-xor_cipher_stream.c | 3 +- devtools/onion.c | 7 +- lightningd/invoice.c | 4 +- lightningd/pay.c | 52 +-- lightningd/peer_htlcs.c | 10 +- plugins/keysend.c | 10 +- plugins/libplugin-pay.c | 103 ++---- plugins/libplugin-pay.h | 5 - plugins/libplugin.c | 3 - plugins/pay.c | 3 - plugins/topology.c | 17 +- tests/test_onion.py | 8 +- wallet/test/run-db.c | 5 - 18 files changed, 182 insertions(+), 433 deletions(-) diff --git a/common/onion.c b/common/onion.c index f8b09511eaad..37afa9ab69c8 100644 --- a/common/onion.c +++ b/common/onion.c @@ -9,34 +9,13 @@ /* BOLT #4: * - * ## Legacy `hop_data` payload format + * ### `tlv_payload` format * - * The `hop_data` format is identified by a single `0x00`-byte length, - * for backward compatibility. Its payload is defined as: - * - * 1. type: `hop_data` (for `realm` 0) - * 2. data: - * * [`short_channel_id`:`short_channel_id`] - * * [`u64`:`amt_to_forward`] - * * [`u32`:`outgoing_cltv_value`] - * * [`12*byte`:`padding`] + * This is a more flexible format, which avoids the redundant + * `short_channel_id` field for the final node. It is formatted + * according to the Type-Length-Value format defined in [BOLT + * #1](01-messaging.md#type-length-value-format). */ -static u8 *make_v0_hop(const tal_t *ctx, - const struct short_channel_id *scid, - struct amount_msat forward, u32 outgoing_cltv) -{ - const u8 padding[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - /* Prepend 0 byte for realm */ - u8 *buf = tal_arrz(ctx, u8, 1); - towire_short_channel_id(&buf, scid); - towire_amount_msat(&buf, forward); - towire_u32(&buf, outgoing_cltv); - towire(&buf, padding, ARRAY_SIZE(padding)); - assert(tal_bytelen(buf) == 1 + 32); - return buf; -} - static u8 *make_tlv_hop(const tal_t *ctx, const struct tlv_tlv_payload *tlv) { @@ -58,46 +37,35 @@ static u8 *make_tlv_hop(const tal_t *ctx, } u8 *onion_nonfinal_hop(const tal_t *ctx, - bool use_tlv, const struct short_channel_id *scid, struct amount_msat forward, u32 outgoing_cltv, const struct pubkey *blinding, const u8 *enctlv) { - if (use_tlv) { - struct tlv_tlv_payload *tlv = tlv_tlv_payload_new(tmpctx); + struct tlv_tlv_payload *tlv = tlv_tlv_payload_new(tmpctx); - /* BOLT #4: - * - * The writer: - *... - * - For every node: - * - MUST include `amt_to_forward` and `outgoing_cltv_value`. - * - For every non-final node: - * - MUST include `short_channel_id` - * - MUST NOT include `payment_data` - */ - tlv->amt_to_forward = &forward.millisatoshis; /* Raw: TLV convert */ - tlv->outgoing_cltv_value = &outgoing_cltv; - tlv->short_channel_id = cast_const(struct short_channel_id *, - scid); -#if EXPERIMENTAL_FEATURES - tlv->blinding_point = cast_const(struct pubkey *, blinding); - tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); -#endif - return make_tlv_hop(ctx, tlv); - } else { + /* BOLT #4: + * + * The writer: + *... + * - For every node: + * - MUST include `amt_to_forward` and `outgoing_cltv_value`. + * - For every non-final node: + * - MUST include `short_channel_id` + * - MUST NOT include `payment_data` + */ + tlv->amt_to_forward = &forward.millisatoshis; /* Raw: TLV convert */ + tlv->outgoing_cltv_value = &outgoing_cltv; + tlv->short_channel_id = cast_const(struct short_channel_id *, scid); #if EXPERIMENTAL_FEATURES - if (blinding || enctlv) - return NULL; + tlv->blinding_point = cast_const(struct pubkey *, blinding); + tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); #endif - return make_v0_hop(ctx, scid, forward, outgoing_cltv); - } + return make_tlv_hop(ctx, tlv); } u8 *onion_final_hop(const tal_t *ctx, - bool use_tlv, struct amount_msat forward, u32 outgoing_cltv, struct amount_msat total_msat, @@ -105,59 +73,46 @@ u8 *onion_final_hop(const tal_t *ctx, const u8 *enctlv, const struct secret *payment_secret) { + struct tlv_tlv_payload *tlv = tlv_tlv_payload_new(tmpctx); + struct tlv_tlv_payload_payment_data tlv_pdata; + /* These go together! */ if (!payment_secret) assert(amount_msat_eq(total_msat, forward)); - if (use_tlv) { - struct tlv_tlv_payload *tlv = tlv_tlv_payload_new(tmpctx); - struct tlv_tlv_payload_payment_data tlv_pdata; + /* BOLT #4: + * + * The writer: + *... + * - For every node: + * - MUST include `amt_to_forward` and `outgoing_cltv_value`. + *... + * - For the final node: + * - MUST NOT include `short_channel_id` + * - if the recipient provided `payment_secret`: + * - MUST include `payment_data` + * - MUST set `payment_secret` to the one provided + * - MUST set `total_msat` to the total amount it will send + */ + tlv->amt_to_forward = &forward.millisatoshis; /* Raw: TLV convert */ + tlv->outgoing_cltv_value = &outgoing_cltv; - /* BOLT #4: - * - * The writer: - *... - * - For every node: - * - MUST include `amt_to_forward` and `outgoing_cltv_value`. - *... - * - For the final node: - * - MUST NOT include `short_channel_id` - * - if the recipient provided `payment_secret`: - * - MUST include `payment_data` - * - MUST set `payment_secret` to the one provided - * - MUST set `total_msat` to the total amount it will send - */ - tlv->amt_to_forward = &forward.millisatoshis; /* Raw: TLV convert */ - tlv->outgoing_cltv_value = &outgoing_cltv; - - if (payment_secret) { - tlv_pdata.payment_secret = *payment_secret; - tlv_pdata.total_msat = total_msat.millisatoshis; /* Raw: TLV convert */ - tlv->payment_data = &tlv_pdata; - } -#if EXPERIMENTAL_FEATURES - tlv->blinding_point = cast_const(struct pubkey *, blinding); - tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); -#endif - return make_tlv_hop(ctx, tlv); - } else { - static struct short_channel_id all_zero_scid; - /* No payment secrets in legacy format. */ - if (payment_secret) - return NULL; + if (payment_secret) { + tlv_pdata.payment_secret = *payment_secret; + tlv_pdata.total_msat = total_msat.millisatoshis; /* Raw: TLV convert */ + tlv->payment_data = &tlv_pdata; + } #if EXPERIMENTAL_FEATURES - if (blinding || enctlv) - return NULL; + tlv->blinding_point = cast_const(struct pubkey *, blinding); + tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); #endif - return make_v0_hop(ctx, &all_zero_scid, forward, outgoing_cltv); - } + return make_tlv_hop(ctx, tlv); } -/* Returns true if valid, and fills in type. */ +/* Returns true if valid, and fills in len. */ static bool pull_payload_length(const u8 **cursor, size_t *max, bool has_realm, - enum onion_payload_type *type, size_t *len) { /* *len will incorporate bytes we read from cursor */ @@ -172,19 +127,6 @@ static bool pull_payload_length(const u8 **cursor, if (!cursor) return false; - /* BOLT #4: - * - Legacy `hop_data` format, identified by a single `0x00` byte for - * length. In this case the `hop_payload_length` is defined to be 32 - * bytes. - */ - if (has_realm && *len == 0) { - if (type) - *type = ONION_V0_PAYLOAD; - assert(*cursor - start == 1); - *len = 1 + 32; - return true; - } - /* BOLT #4: * - `tlv_payload` format, identified by any length over `1`. In this * case the `hop_payload_length` is equal to the numeric value of @@ -200,8 +142,6 @@ static bool pull_payload_length(const u8 **cursor, return false; } - if (type) - *type = ONION_TLV_PAYLOAD; *len += (*cursor - start); return true; } @@ -210,11 +150,10 @@ static bool pull_payload_length(const u8 **cursor, } size_t onion_payload_length(const u8 *raw_payload, size_t len, bool has_realm, - bool *valid, - enum onion_payload_type *type) + bool *valid) { size_t max = len, payload_len; - *valid = pull_payload_length(&raw_payload, &max, has_realm, type, &payload_len); + *valid = pull_payload_length(&raw_payload, &max, has_realm, &payload_len); /* If it's not valid, copy the entire thing. */ if (!*valid) @@ -276,138 +215,104 @@ struct onion_payload *onion_decode(const tal_t *ctx, size_t max = tal_bytelen(cursor), len; struct tlv_tlv_payload *tlv; - if (!pull_payload_length(&cursor, &max, true, &p->type, &len)) + if (!pull_payload_length(&cursor, &max, true, &len)) return tal_free(p); - switch (p->type) { - case ONION_V0_PAYLOAD: - p->type = ONION_V0_PAYLOAD; - p->forward_channel = tal(p, struct short_channel_id); - fromwire_short_channel_id(&cursor, &max, p->forward_channel); - p->amt_to_forward = fromwire_amount_msat(&cursor, &max); - p->outgoing_cltv = fromwire_u32(&cursor, &max); - p->payment_secret = NULL; - p->blinding = NULL; - /* We can't handle blinding with a legacy payload */ - if (blinding) - return tal_free(p); + tlv = tlv_tlv_payload_new(p); + if (!fromwire_tlv_payload(&cursor, &max, tlv)) + goto fail; + + if (!tlv_fields_valid(tlv->fields, accepted_extra_tlvs, failtlvpos)) { + *failtlvtype = tlv->fields[*failtlvpos].numtype; + goto fail; + } - if (rs->nextcase == ONION_FORWARD) { - p->total_msat = NULL; - } else { - /* BOLT #4: - * - if it is the final node: - * - MUST treat `total_msat` as if it were equal to - * `amt_to_forward` if it is not present. */ - p->total_msat = tal_dup(p, struct amount_msat, - &p->amt_to_forward); - } + /* BOLT #4: + * + * The reader: + * - MUST return an error if `amt_to_forward` or + * `outgoing_cltv_value` are not present. + */ + if (!tlv->amt_to_forward || !tlv->outgoing_cltv_value) + goto fail; - /* If they somehow got an invalid onion this far, fail. */ - if (!cursor) - return tal_free(p); - p->tlv = NULL; - return p; + p->amt_to_forward = amount_msat(*tlv->amt_to_forward); + p->outgoing_cltv = *tlv->outgoing_cltv_value; - case ONION_TLV_PAYLOAD: - tlv = tlv_tlv_payload_new(p); - if (!fromwire_tlv_payload(&cursor, &max, tlv)) + /* BOLT #4: + * + * The writer: + *... + * - For every non-final node: + * - MUST include `short_channel_id` + */ + if (rs->nextcase == ONION_FORWARD) { + if (!tlv->short_channel_id) goto fail; + p->forward_channel = tal_dup(p, struct short_channel_id, + tlv->short_channel_id); + p->total_msat = NULL; + } else { + p->forward_channel = NULL; + /* BOLT #4: + * - if it is the final node: + * - MUST treat `total_msat` as if it were equal to + * `amt_to_forward` if it is not present. */ + p->total_msat = tal_dup(p, struct amount_msat, + &p->amt_to_forward); + } - if (!tlv_fields_valid(tlv->fields, accepted_extra_tlvs, failtlvpos)) { - *failtlvtype = tlv->fields[*failtlvpos].numtype; - goto fail; + p->payment_secret = NULL; + p->blinding = tal_dup_or_null(p, struct pubkey, blinding); + +#if EXPERIMENTAL_FEATURES + if (!p->blinding) { + /* If we have no blinding, it could be in TLV. */ + if (tlv->blinding_point) { + p->blinding = + tal_dup(p, struct pubkey, + tlv->blinding_point); + ecdh(p->blinding, &p->blinding_ss); } + } else + p->blinding_ss = *blinding_ss; - /* BOLT #4: - * - * The reader: - * - MUST return an error if `amt_to_forward` or - * `outgoing_cltv_value` are not present. - */ - if (!tlv->amt_to_forward || !tlv->outgoing_cltv_value) - goto fail; + if (p->blinding) { + /* If they give us a blinding and we're not terminal, + * we must have an enctlv. */ + if (rs->nextcase == ONION_FORWARD) { + struct tlv_tlv_payload *ntlv; - p->amt_to_forward = amount_msat(*tlv->amt_to_forward); - p->outgoing_cltv = *tlv->outgoing_cltv_value; + if (!tlv->encrypted_recipient_data) + goto fail; - /* BOLT #4: - * - * The writer: - *... - * - For every non-final node: - * - MUST include `short_channel_id` - */ - if (rs->nextcase == ONION_FORWARD) { - if (!tlv->short_channel_id) + ntlv = decrypt_tlv(tmpctx, + &p->blinding_ss, + tlv->encrypted_recipient_data); + if (!ntlv) goto fail; - p->forward_channel = tal_dup(p, struct short_channel_id, - tlv->short_channel_id); - p->total_msat = NULL; - } else { - p->forward_channel = NULL; - /* BOLT #4: - * - if it is the final node: - * - MUST treat `total_msat` as if it were equal to - * `amt_to_forward` if it is not present. */ - p->total_msat = tal_dup(p, struct amount_msat, - &p->amt_to_forward); - } - p->payment_secret = NULL; - p->blinding = tal_dup_or_null(p, struct pubkey, blinding); + /* Must override short_channel_id */ + if (!ntlv->short_channel_id) + goto fail; -#if EXPERIMENTAL_FEATURES - if (!p->blinding) { - /* If we have no blinding, it could be in TLV. */ - if (tlv->blinding_point) { - p->blinding = - tal_dup(p, struct pubkey, - tlv->blinding_point); - ecdh(p->blinding, &p->blinding_ss); - } - } else - p->blinding_ss = *blinding_ss; - - if (p->blinding) { - /* If they give us a blinding and we're not terminal, - * we must have an enctlv. */ - if (rs->nextcase == ONION_FORWARD) { - struct tlv_tlv_payload *ntlv; - - if (!tlv->encrypted_recipient_data) - goto fail; - - ntlv = decrypt_tlv(tmpctx, - &p->blinding_ss, - tlv->encrypted_recipient_data); - if (!ntlv) - goto fail; - - /* Must override short_channel_id */ - if (!ntlv->short_channel_id) - goto fail; - - *p->forward_channel - = *ntlv->short_channel_id; - } + *p->forward_channel + = *ntlv->short_channel_id; } + } #endif /* EXPERIMENTAL_FEATURES */ - if (tlv->payment_data) { - p->payment_secret = tal_dup(p, struct secret, - &tlv->payment_data->payment_secret); - tal_free(p->total_msat); - p->total_msat = tal(p, struct amount_msat); - *p->total_msat - = amount_msat(tlv->payment_data->total_msat); - } - p->tlv = tal_steal(p, tlv); - return p; + if (tlv->payment_data) { + p->payment_secret = tal_dup(p, struct secret, + &tlv->payment_data->payment_secret); + tal_free(p->total_msat); + p->total_msat = tal(p, struct amount_msat); + *p->total_msat + = amount_msat(tlv->payment_data->total_msat); } + p->tlv = tal_steal(p, tlv); + return p; - /* You said it was a valid type! */ - abort(); fail: tal_free(tlv); tal_free(p); diff --git a/common/onion.h b/common/onion.h index 44b10ab268bb..bf8c3c430b78 100644 --- a/common/onion.h +++ b/common/onion.h @@ -6,14 +6,7 @@ struct route_step; -enum onion_payload_type { - ONION_V0_PAYLOAD = 0, - ONION_TLV_PAYLOAD = 1, -}; - struct onion_payload { - enum onion_payload_type type; - struct amount_msat amt_to_forward; u32 outgoing_cltv; struct amount_msat *total_msat; @@ -29,7 +22,6 @@ struct onion_payload { }; u8 *onion_nonfinal_hop(const tal_t *ctx, - bool use_tlv, const struct short_channel_id *scid, struct amount_msat forward, u32 outgoing_cltv, @@ -38,7 +30,6 @@ u8 *onion_nonfinal_hop(const tal_t *ctx, /* Note that this can fail if we supply payment_secret and !use_tlv! */ u8 *onion_final_hop(const tal_t *ctx, - bool use_tlv, struct amount_msat forward, u32 outgoing_cltv, struct amount_msat total_msat, @@ -52,7 +43,6 @@ u8 *onion_final_hop(const tal_t *ctx, * @len: length of @raw_payload in bytes. * @has_realm: used for HTLCs, where first byte 0 is magical. * @valid: set to true if it is valid, false otherwise. - * @type: if non-NULL, set to type of payload if *@valid is true. * * If @valid is set, there is room for the HMAC immediately following, * as the return value is <= ROUTING_INFO_SIZE - HMAC_SIZE. Otherwise, @@ -60,8 +50,7 @@ u8 *onion_final_hop(const tal_t *ctx, */ size_t onion_payload_length(const u8 *raw_payload, size_t len, bool has_realm, - bool *valid, - enum onion_payload_type *type); + bool *valid); /** * onion_decode: decode payload from a decrypted onion. diff --git a/common/route.c b/common/route.c index f204518b7e3c..04d590643882 100644 --- a/common/route.c +++ b/common/route.c @@ -101,12 +101,6 @@ static bool dijkstra_to_hops(struct route_hop **hops, /* Find other end of channel. */ next = gossmap_nth_node(gossmap, c, !(*hops)[num_hops].direction); gossmap_node_get_id(gossmap, next, &(*hops)[num_hops].node_id); - /* If we don't have a node_announcement, we *assume* modern */ - if (next->nann_off == 0 - || gossmap_node_get_feature(gossmap, next, OPT_VAR_ONION) != -1) - (*hops)[num_hops].style = ROUTE_HOP_TLV; - else - (*hops)[num_hops].style = ROUTE_HOP_LEGACY; /* These are (ab)used by others. */ (*hops)[num_hops].blinding = NULL; diff --git a/common/route.h b/common/route.h index 11963fdab231..f28b0d0730be 100644 --- a/common/route.h +++ b/common/route.h @@ -11,11 +11,6 @@ struct gossmap; struct gossmap_chan; struct gossmap_node; -enum route_hop_style { - ROUTE_HOP_LEGACY = 1, - ROUTE_HOP_TLV = 2, -}; - /** * struct route_hop: a hop in a route. * @@ -26,7 +21,6 @@ enum route_hop_style { * @delay: total cltv delay at this hop. * @blinding: blinding key for this hop (if any) * @enctlv: encrypted TLV for this hop (if any) - * @style: onion encoding style for this hop. */ struct route_hop { struct short_channel_id scid; @@ -36,7 +30,6 @@ struct route_hop { u32 delay; struct pubkey *blinding; u8 *enctlv; - enum route_hop_style style; }; /* Can c carry amount in dir? */ diff --git a/common/sphinx.c b/common/sphinx.c index 1eb4496ea77e..9c0064db48c6 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -627,7 +627,7 @@ struct route_step *process_onionpacket( payload_size = onion_payload_length(paddedheader, tal_bytelen(msg->routinginfo), has_realm, - &valid, NULL); + &valid); /* Can't decode? Treat it as terminal. */ if (!valid) { diff --git a/common/test/run-sphinx-xor_cipher_stream.c b/common/test/run-sphinx-xor_cipher_stream.c index dc1bd585c504..26781985b255 100644 --- a/common/test/run-sphinx-xor_cipher_stream.c +++ b/common/test/run-sphinx-xor_cipher_stream.c @@ -91,8 +91,7 @@ struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents /* Generated stub for onion_payload_length */ size_t onion_payload_length(const u8 *raw_payload UNNEEDED, size_t len UNNEEDED, bool has_realm UNNEEDED, - bool *valid UNNEEDED, - enum onion_payload_type *type UNNEEDED) + bool *valid UNNEEDED) { fprintf(stderr, "onion_payload_length called!\n"); abort(); } /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) diff --git a/devtools/onion.c b/devtools/onion.c index d486ccbb4588..7b81a4f9ac57 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -71,7 +71,6 @@ static void do_generate(int argc, char **argv, } else { struct short_channel_id scid; struct amount_msat amt; - bool use_tlv = streq(argv[1 + i] + klen, "/tlv"); /* FIXME: support secret and and total_msat */ memset(&scid, i, sizeof(scid)); @@ -79,14 +78,12 @@ static void do_generate(int argc, char **argv, if (i == num_hops - 1) sphinx_add_hop(sp, &path[i], take(onion_final_hop(NULL, - use_tlv, amt, i, amt, NULL, NULL, NULL))); else sphinx_add_hop(sp, &path[i], take(onion_nonfinal_hop(NULL, - use_tlv, &scid, amt, i, NULL, @@ -273,7 +270,6 @@ static void runtest(const char *filename) decodetok = json_get_member(buffer, toks, "decode"); json_for_each_arr(i, hop, decodetok) { - enum onion_payload_type type; bool valid; hexprivkey = json_strdup(ctx, buffer, hop); @@ -284,9 +280,8 @@ static void runtest(const char *filename) errx(1, "Error serializing message."); onion_payload_length(step->raw_payload, tal_bytelen(step->raw_payload), - true, &valid, &type); + true, &valid); assert(valid); - printf(" Type: %d\n", type); printf(" Payload: %s\n", tal_hex(ctx, step->raw_payload)); printf(" Next onion: %s\n", tal_hex(ctx, serialized)); printf(" Next HMAC: %s\n", diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 365826521799..45cc4922a813 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -175,14 +175,12 @@ static void invoice_payment_add_tlvs(struct json_stream *stream, struct htlc_set *hset) { struct htlc_in *hin; - struct tlv_tlv_payload *tlvs; + const struct tlv_tlv_payload *tlvs; assert(tal_count(hset->htlcs) > 0); /* Pick the first HTLC as representative for the entire set. */ hin = hset->htlcs[0]; - if (hin->payload->type != ONION_TLV_PAYLOAD) - return; tlvs = hin->payload->tlv; json_array_start(stream, "extratlvs"); diff --git a/lightningd/pay.c b/lightningd/pay.c index 6889945c50bb..41f47f42c828 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -751,18 +751,6 @@ static struct command_result *wait_payment(struct lightningd *ld, abort(); } -static bool should_use_tlv(enum route_hop_style style) -{ - switch (style) { - case ROUTE_HOP_TLV: - return true; - /* Otherwise fall thru */ - case ROUTE_HOP_LEGACY: - return false; - } - abort(); -} - /* Returns failmsg on failure, tallocated off ctx */ static const u8 *send_onion(const tal_t *ctx, struct lightningd *ld, const struct onionpacket *packet, @@ -1126,7 +1114,7 @@ send_payment(struct lightningd *ld, struct short_channel_id *channels; struct sphinx_path *path; struct pubkey pubkey; - bool final_tlv, ret; + bool ret; u8 *onion; /* Expiry for HTLCs is absolute. And add one to give some margin. */ @@ -1144,7 +1132,6 @@ send_payment(struct lightningd *ld, sphinx_add_hop(path, &pubkey, take(onion_nonfinal_hop(NULL, - should_use_tlv(route[i].style), &route[i + 1].scid, route[i + 1].amount, base_expiry + route[i + 1].delay, @@ -1157,25 +1144,15 @@ send_payment(struct lightningd *ld, ret = pubkey_from_node_id(&pubkey, &ids[i]); assert(ret); - final_tlv = should_use_tlv(route[i].style); /* BOLT #4: * - Unless `node_announcement`, `init` message or the * [BOLT #11](11-payment-encoding.md#tagged-fields) offers feature * `var_onion_optin`: * - MUST use the legacy payload format instead. */ - /* In our case, we don't use it unless we also have a payment_secret; - * everyone should support this eventually */ - if (!final_tlv && payment_secret) - final_tlv = true; - - /* Parallel payments are invalid for legacy. */ - if (partid && !final_tlv) - return command_fail(cmd, PAY_DESTINATION_PERM_FAIL, - "Cannot do parallel payments to legacy node"); + /* FIXME: This requirement is now obsolete, and we should remove it! */ onion = onion_final_hop(cmd, - final_tlv, route[i].amount, base_expiry + route[i].delay, total_msat, route[i].blinding, route[i].enctlv, @@ -1326,23 +1303,19 @@ AUTODATA(json_command, &sendonion_command); JSON-RPC sendpay interface -----------------------------------------------------------------------------*/ +/* FIXME: We accept his parameter for now, will deprecate eventually */ static struct command_result *param_route_hop_style(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, - enum route_hop_style **style) + int **unused) { - *style = tal(cmd, enum route_hop_style); - if (json_tok_streq(buffer, tok, "legacy")) { - **style = ROUTE_HOP_LEGACY; - return NULL; - } else if (json_tok_streq(buffer, tok, "tlv")) { - **style = ROUTE_HOP_TLV; + if (json_tok_streq(buffer, tok, "tlv")) { return NULL; } return command_fail_badparam(cmd, name, buffer, tok, - "should be 'legacy' or 'tlv'"); + "should be 'tlv' ('legacy' not supported)"); } static struct command_result *param_route_hops(struct command *cmd, @@ -1366,7 +1339,7 @@ static struct command_result *param_route_hops(struct command *cmd, unsigned *delay, *direction; struct pubkey *blinding; u8 *enctlv; - enum route_hop_style *style, default_style; + int *ignored; if (!param(cmd, buffer, t, /* Only *one* of these is required */ @@ -1378,7 +1351,7 @@ static struct command_result *param_route_hops(struct command *cmd, 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("style", param_route_hop_style, &ignored), p_opt("blinding", param_pubkey, &blinding), p_opt("encrypted_recipient_data", param_bin_from_hex, &enctlv), NULL)) @@ -1405,21 +1378,12 @@ static struct command_result *param_route_hops(struct command *cmd, if (!msat) msat = amount_msat; - if (blinding || enctlv) { - if (style && *style == ROUTE_HOP_LEGACY) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zi]: Can't have blinding or enctlv with legacy", name, i); - default_style = ROUTE_HOP_TLV; - } else - default_style = ROUTE_HOP_LEGACY; - (*hops)[i].amount = *msat; (*hops)[i].node_id = *id; (*hops)[i].delay = *delay; (*hops)[i].scid = *channel; (*hops)[i].blinding = blinding; (*hops)[i].enctlv = enctlv; - (*hops)[i].style = style ? *style : default_style; } return NULL; diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index cf910dfa2ba3..b95746590267 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -945,15 +945,7 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_add_hex_talarr(s, "payload", rs->raw_payload); if (p->payload) { - switch (p->payload->type) { - case ONION_V0_PAYLOAD: - json_add_string(s, "type", "legacy"); - break; - - case ONION_TLV_PAYLOAD: - json_add_string(s, "type", "tlv"); - break; - } + json_add_string(s, "type", "tlv"); if (p->payload->forward_channel) json_add_short_channel_id(s, "short_channel_id", diff --git a/plugins/keysend.c b/plugins/keysend.c index 6fe37e76f3a7..5f1f80ad896b 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -63,14 +63,7 @@ static void keysend_cb(struct keysend_data *d, struct payment *p) { size_t hopcount; /* On the root payment we perform the featurebit check. */ - if (p->parent == NULL && p->step == PAYMENT_STEP_INITIALIZED) { - if (!payment_root(p)->destination_has_tlv) - return payment_fail( - p, - "Recipient %s does not support keysend payments " - "(no TLV support)", - node_id_to_hexstr(tmpctx, p->destination)); - } else if (p->step == PAYMENT_STEP_FAILED) { + if (p->step == PAYMENT_STEP_FAILED) { /* Now we can look at the error, and the failing node, and determine whether they didn't like our attempt. This is required since most nodes don't @@ -184,7 +177,6 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, p->json_buffer = tal_dup_talarr(p, const char, buf); p->json_toks = params; p->destination = tal_steal(p, destination); - p->destination_has_tlv = true; p->payment_secret = NULL; p->amount = *msat; p->routes = tal_steal(p, hints); diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 9595dc6092cb..dafa5b7669bd 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -76,7 +76,6 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, assert(cmd == NULL); tal_arr_expand(&parent->children, p); p->destination = parent->destination; - p->destination_has_tlv = parent->destination_has_tlv; p->amount = parent->amount; p->label = parent->label; p->payment_hash = parent->payment_hash; @@ -885,20 +884,6 @@ static struct command_result *payment_getroute(struct payment *p) return command_still_pending(p->cmd); } -static u8 *tal_towire_legacy_payload(const tal_t *ctx, const struct legacy_payload *payload) -{ - const u8 padding[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - /* Prepend 0 byte for realm */ - u8 *buf = tal_arrz(ctx, u8, 1); - towire_short_channel_id(&buf, &payload->scid); - towire_amount_msat(&buf, payload->forward_amt); - towire_u32(&buf, payload->outgoing_cltv); - towire(&buf, padding, ARRAY_SIZE(padding)); - assert(tal_bytelen(buf) == 1 + 32); - return buf; -} - static struct payment_result *tal_sendpay_result_from_json(const tal_t *ctx, const char *buffer, const jsmntok_t *toks) @@ -1611,7 +1596,6 @@ static void payment_add_hop_onion_payload(struct payment *p, struct route_hop *node, struct route_hop *next, bool final, - bool force_tlv, struct secret *payment_secret) { struct createonion_request *cr = p->createonion_request; @@ -1619,48 +1603,29 @@ static void payment_add_hop_onion_payload(struct payment *p, u64 msat = next->amount.millisatoshis; /* Raw: TLV payload generation*/ struct tlv_field **fields; struct payment *root = payment_root(p); - static struct short_channel_id all_zero_scid = {.u64 = 0}; /* This is the information of the node processing this payload, while * `next` are the instructions to include in the payload, which is * basically the channel going to the next node. */ - dst->style = node->style; - if (force_tlv) - dst->style = ROUTE_HOP_TLV; dst->pubkey = node->node_id; - switch (dst->style) { - case ROUTE_HOP_LEGACY: - dst->legacy_payload = tal(cr->hops, struct legacy_payload); - dst->legacy_payload->forward_amt = next->amount; + dst->tlv_payload = tlv_tlv_payload_new(cr->hops); + fields = &dst->tlv_payload->fields; + tlvstream_set_tu64(fields, TLV_TLV_PAYLOAD_AMT_TO_FORWARD, + msat); + tlvstream_set_tu32(fields, TLV_TLV_PAYLOAD_OUTGOING_CLTV_VALUE, + cltv); - if (!final) - dst->legacy_payload->scid = next->scid; - else - dst->legacy_payload->scid = all_zero_scid; + if (!final) + tlvstream_set_short_channel_id(fields, + TLV_TLV_PAYLOAD_SHORT_CHANNEL_ID, + &next->scid); - dst->legacy_payload->outgoing_cltv = cltv; - break; - case ROUTE_HOP_TLV: - dst->tlv_payload = tlv_tlv_payload_new(cr->hops); - fields = &dst->tlv_payload->fields; - tlvstream_set_tu64(fields, TLV_TLV_PAYLOAD_AMT_TO_FORWARD, - msat); - tlvstream_set_tu32(fields, TLV_TLV_PAYLOAD_OUTGOING_CLTV_VALUE, - cltv); - - if (!final) - tlvstream_set_short_channel_id(fields, - TLV_TLV_PAYLOAD_SHORT_CHANNEL_ID, - &next->scid); - - if (payment_secret != NULL) { - assert(final); - tlvstream_set_tlv_payload_data( - fields, payment_secret, - root->amount.millisatoshis); /* Raw: TLV payload generation*/ - } - break; + if (payment_secret != NULL) { + assert(final); + tlvstream_set_tlv_payload_data( + fields, payment_secret, + root->amount.millisatoshis); /* Raw: TLV payload generation*/ } } @@ -1699,7 +1664,7 @@ static void payment_compute_onion_payloads(struct payment *p) /* The message is destined for hop i, but contains fields for * i+1 */ payment_add_hop_onion_payload(p, &cr->hops[i], &p->route[i], - &p->route[i + 1], false, false, + &p->route[i + 1], false, NULL); tal_append_fmt(&routetxt, "%s -> ", type_to_string(tmpctx, struct short_channel_id, @@ -1709,7 +1674,7 @@ static void payment_compute_onion_payloads(struct payment *p) /* Final hop */ payment_add_hop_onion_payload( p, &cr->hops[hopcount - 1], &p->route[hopcount - 1], - &p->route[hopcount - 1], true, root->destination_has_tlv, + &p->route[hopcount - 1], true, root->payment_secret); tal_append_fmt(&routetxt, "%s", type_to_string(tmpctx, struct short_channel_id, @@ -1738,18 +1703,14 @@ static void payment_sendonion(struct payment *p) json_object_start(req->js, NULL); struct createonion_hop *hop = &p->createonion_request->hops[i]; json_add_node_id(req->js, "pubkey", &hop->pubkey); - if (hop->style == ROUTE_HOP_LEGACY) { - payload = tal_towire_legacy_payload(tmpctx, hop->legacy_payload); - json_add_hex_talarr(req->js, "payload", payload); - }else { - tlv = tal_arr(tmpctx, u8, 0); - towire_tlvstream_raw(&tlv, hop->tlv_payload->fields); - payload = tal_arr(tmpctx, u8, 0); - towire_bigsize(&payload, tal_bytelen(tlv)); - towire(&payload, tlv, tal_bytelen(tlv)); - json_add_hex_talarr(req->js, "payload", payload); - tal_free(tlv); - } + + tlv = tal_arr(tmpctx, u8, 0); + towire_tlvstream_raw(&tlv, hop->tlv_payload->fields); + payload = tal_arr(tmpctx, u8, 0); + towire_bigsize(&payload, tal_bytelen(tlv)); + towire(&payload, tlv, tal_bytelen(tlv)); + json_add_hex_talarr(req->js, "payload", payload); + tal_free(tlv); tal_free(payload); json_object_end(req->js); } @@ -2832,7 +2793,6 @@ static void routehint_step_cb(struct routehints_data *d, struct payment *p) } hop.node_id = *route_pubkey(p, routehint, i + 1); - hop.style = ROUTE_HOP_LEGACY; hop.scid = routehint[i].short_channel_id; hop.amount = dest_amount; hop.delay = route_cltv(d->final_cltv, routehint + i + 1, @@ -3216,7 +3176,6 @@ static void direct_pay_override(struct payment *p) { p->route[0].scid = hint->scid.scid; p->route[0].direction = hint->scid.dir; p->route[0].node_id = *p->destination; - p->route[0].style = p->destination_has_tlv ? ROUTE_HOP_TLV : ROUTE_HOP_LEGACY; paymod_log(p, LOG_DBG, "Found a direct channel (%s) with sufficient " "capacity, skipping route computation.", @@ -3523,12 +3482,10 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) * get the exact value through. */ size_t lastidx = tal_count(p->createonion_request->hops) - 1; struct createonion_hop *hop = &p->createonion_request->hops[lastidx]; - if (hop->style == ROUTE_HOP_TLV) { - struct tlv_field **fields = &hop->tlv_payload->fields; - tlvstream_set_tlv_payload_data( + struct tlv_field **fields = &hop->tlv_payload->fields; + tlvstream_set_tlv_payload_data( fields, root->payment_secret, root->amount.millisatoshis); /* Raw: onion payload */ - } } else if (p->step == PAYMENT_STEP_INITIALIZED) { /* The presplitter only acts on the root and only in the first * step. */ @@ -3708,12 +3665,10 @@ static void adaptive_splitter_cb(struct adaptive_split_mod_data *d, struct payme * get the exact value through. */ size_t lastidx = tal_count(p->createonion_request->hops) - 1; struct createonion_hop *hop = &p->createonion_request->hops[lastidx]; - if (hop->style == ROUTE_HOP_TLV) { - struct tlv_field **fields = &hop->tlv_payload->fields; - tlvstream_set_tlv_payload_data( + struct tlv_field **fields = &hop->tlv_payload->fields; + tlvstream_set_tlv_payload_data( fields, root->payment_secret, root->amount.millisatoshis); /* Raw: onion payload */ - } } else if (p->step == PAYMENT_STEP_FAILED && !p->abort) { if (amount_msat_greater(p->amount, MPP_ADAPTIVE_LOWER_LIMIT)) { struct payment *a, *b; diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 4f045e252488..e1f14da44f73 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -16,10 +16,7 @@ struct legacy_payload { /* struct holding the information necessary to call createonion */ struct createonion_hop { struct node_id pubkey; - - enum route_hop_style style; struct tlv_tlv_payload *tlv_payload; - struct legacy_payload *legacy_payload; }; struct createonion_request { @@ -176,8 +173,6 @@ struct payment { /* Real destination we want to route to */ struct node_id *destination; - /* Do we know for sure that this supports OPT_VAR_ONION? */ - bool destination_has_tlv; /* Payment hash extracted from the invoice if any. */ struct sha256 *payment_hash; diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 942c91822786..2e728f159c67 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1735,9 +1735,6 @@ static bool json_to_route_hop_inplace(struct route_hop *dst, const char *buffer, json_to_int(buffer, directiontok, &dst->direction); json_to_msat(buffer, amounttok, &dst->amount); json_to_number(buffer, delaytok, &dst->delay); - dst->style = json_tok_streq(buffer, styletok, "legacy") - ? ROUTE_HOP_LEGACY - : ROUTE_HOP_TLV; return true; } diff --git a/plugins/pay.c b/plugins/pay.c index 95a3ee2ae596..56e05792de0d 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -2371,8 +2371,6 @@ static struct command_result *json_paymod(struct command *cmd, invexpiry = b11->timestamp + b11->expiry; p->destination = tal_dup(p, struct node_id, &b11->receiver_id); - p->destination_has_tlv = - feature_offered(b11->features, OPT_VAR_ONION); p->payment_hash = tal_dup(p, struct sha256, &b11->payment_hash); p->payment_secret = tal_dup_or_null(p, struct secret, b11->payment_secret); @@ -2420,7 +2418,6 @@ static struct command_result *json_paymod(struct command *cmd, p->destination = tal(p, struct node_id); gossmap_guess_node_id(get_gossmap(cmd->plugin), b12->node_id, p->destination); - p->destination_has_tlv = true; p->payment_hash = tal_dup(p, struct sha256, b12->payment_hash); if (b12->recurrence_counter && !label) return command_fail( diff --git a/plugins/topology.c b/plugins/topology.c index cca6f0cd9c16..12be328e57b1 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -94,21 +94,6 @@ static bool can_carry(const struct gossmap *map, return true; } -static void json_add_route_hop_style(struct json_stream *response, - const char *fieldname, - enum route_hop_style style) -{ - switch (style) { - case ROUTE_HOP_LEGACY: - json_add_string(response, fieldname, "legacy"); - return; - case ROUTE_HOP_TLV: - json_add_string(response, fieldname, "tlv"); - return; - } - abort(); -} - /* Output a route hop */ static void json_add_route_hop(struct json_stream *js, const char *fieldname, @@ -121,7 +106,7 @@ static void json_add_route_hop(struct json_stream *js, json_add_num(js, "direction", r->direction); json_add_amount_msat_compat(js, r->amount, "msatoshi", "amount_msat"); json_add_num(js, "delay", r->delay); - json_add_route_hop_style(js, "style", r->style); + json_add_string(js, "style", "tlv"); json_object_end(js); } diff --git a/tests/test_onion.py b/tests/test_onion.py index dcafbbacdc16..9abff56698f0 100644 --- a/tests/test_onion.py +++ b/tests/test_onion.py @@ -46,7 +46,9 @@ def store_onion(o): out = subprocess.check_output([oniontool, 'decode', tempfile, pk]).decode('ASCII').strip().split('\n') store_onion(out[-1][5:]) - assert(out == ['payload=000000000000000000000000000000000400000004000000000000000000000000']) + # Final payload: + # amt_to_forward=4,outgoing_cltv_value=4 + assert(out == ['payload=06020104040104']) def test_rendezvous_onion(directory, oniontool): @@ -82,4 +84,6 @@ def store_onion(o): out = subprocess.check_output([oniontool, 'decode', tempfile, pk]).decode('ASCII').strip().split('\n') store_onion(out[-1][5:]) - assert(out == ['payload=000000000000000000000000000000000400000004000000000000000000000000']) + # Final payload: + # amt_to_forward=4,outgoing_cltv_value=4 + assert(out == ['payload=06020104040104']) diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 966a0a9c8142..54d1a107b92e 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -40,11 +40,6 @@ void get_channel_basepoints(struct lightningd *ld UNNEEDED, struct basepoints *local_basepoints UNNEEDED, struct pubkey *local_funding_pubkey UNNEEDED) { fprintf(stderr, "get_channel_basepoints called!\n"); abort(); } -/* Generated stub for new_log */ -struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, - const struct node_id *default_node_id UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "new_log called!\n"); abort(); } /* Generated stub for towire_hsmd_get_channel_basepoints */ u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct node_id *peerid UNNEEDED, u64 dbid UNNEEDED) { fprintf(stderr, "towire_hsmd_get_channel_basepoints called!\n"); abort(); } From 8f0aab70617f33a6b48d7321eab2506c14d5c505 Mon Sep 17 00:00:00 2001 From: Jules Comte Date: Wed, 16 Mar 2022 23:20:21 -0400 Subject: [PATCH 0468/1530] doc: update ubuntu install instructions Changelog-None --- doc/HACKING.md | 6 +- doc/INSTALL.md | 3 +- requirements.lock | 196 ---------------------------------------------- 3 files changed, 4 insertions(+), 201 deletions(-) delete mode 100644 requirements.lock diff --git a/doc/HACKING.md b/doc/HACKING.md index 6003dbd724ba..22a6406c4fa5 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -160,16 +160,14 @@ lightning funds. Build and Development --------------------- -Install `valgrind` and the python dependencies for best results: +Install the following dependencies for best results: ``` sudo apt update sudo apt install valgrind cppcheck shellcheck libsecp256k1-dev libpq-dev -pip3 install --upgrade pip -pip3 install --user -r requirements.txt ``` -Re-run `configure` for the python dependencies and build using `make`. +Re-run `configure` and build using `make`: ``` ./configure --enable-developer diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 02c203a595c5..b01c88e97562 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -39,7 +39,8 @@ Get dependencies: sudo apt-get install -y \ autoconf automake build-essential git libtool libgmp-dev libsqlite3-dev \ python3 python3-pip net-tools zlib1g-dev libsodium-dev gettext - pip install --user poetry + pip3 install --upgrade pip + pip3 install --user poetry poetry install If you don't have Bitcoin installed locally you'll need to install that diff --git a/requirements.lock b/requirements.lock deleted file mode 100644 index a6434caa4193..000000000000 --- a/requirements.lock +++ /dev/null @@ -1,196 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile --output-file=requirements.lock requirements.in -# -alabaster==0.7.12 - # via sphinx -asn1crypto==1.4.0 - # via coincurve -attrs==21.2.0 - # via - # jsonschema - # pytest -babel==2.9.1 - # via sphinx -base58==2.0.1 - # via -r requirements.in -bitstring==3.1.9 - # via -r requirements.in -certifi==2021.5.30 - # via requests -cffi==1.14.6 - # via - # coincurve - # cryptography -charset-normalizer==2.0.6 - # via requests -cheroot==8.5.2 - # via -r requirements.in -click==7.1.2 - # via flask -coincurve==13.0.0 - # via -r requirements.in -commonmark==0.9.1 - # via recommonmark -crc32c==2.2.post0 - # via -r requirements.in -cryptography==3.4.8 - # via -r requirements.in -docutils==0.17.1 - # via - # recommonmark - # sphinx -entrypoints==0.3 - # via flake8 -ephemeral-port-reserve==1.1.1 - # via -r requirements.in -execnet==1.9.0 - # via pytest-xdist -flake8==3.7.9 - # via -r requirements.in -flaky==3.7.0 - # via -r requirements.in -flask==1.1.4 - # via -r requirements.in -grpcio==1.34.0 - # via - # -r requirements.in - # grpcio-tools -grpcio-tools==1.34.0 - # via -r requirements.in -idna==3.2 - # via requests -imagesize==1.2.0 - # via sphinx -iniconfig==1.1.1 - # via pytest -itsdangerous==1.1.0 - # via flask -jaraco.functools==3.3.0 - # via cheroot -jinja2==2.11.3 - # via - # flask - # mrkd - # sphinx -jsonschema==3.2.0 - # via -r requirements.in -mako==1.1.5 - # via -r requirements.in -markupsafe==2.0.1 - # via - # jinja2 - # mako -mccabe==0.6.1 - # via flake8 -mistune==0.8.4 - # via mrkd -mistune-contrib==0.1 - # via mrkd -more-itertools==8.10.0 - # via - # cheroot - # jaraco.functools -mrkd==0.1.6 - # via -r requirements.in -mypy==0.910 - # via -r requirements.in -mypy-extensions==0.4.3 - # via mypy -packaging==21.0 - # via - # pytest - # sphinx -plac==1.3.3 - # via mrkd -pluggy==0.13.1 - # via pytest -protobuf==3.19.3 - # via grpcio-tools -psutil==5.7.3 - # via -r requirements.in -psycopg2-binary==2.8.6 - # via -r requirements.in -py==1.10.0 - # via - # pytest - # pytest-forked -pycodestyle==2.5.0 - # via flake8 -pycparser==2.20 - # via - # -r requirements.in - # cffi -pyflakes==2.1.1 - # via flake8 -pygments==2.10.0 - # via - # mrkd - # sphinx -pyparsing==2.4.7 - # via packaging -pyrsistent==0.18.0 - # via jsonschema -pysocks==1.7.1 - # via -r requirements.in -pytest==6.1.2 - # via - # -r requirements.in - # pytest-forked - # pytest-rerunfailures - # pytest-timeout - # pytest-xdist -pytest-forked==1.3.0 - # via pytest-xdist -pytest-rerunfailures==9.1.1 - # via -r requirements.in -pytest-timeout==1.4.2 - # via -r requirements.in -pytest-xdist==2.2.1 - # via -r requirements.in -python-bitcoinlib==0.11.0 - # via -r requirements.in -pytz==2021.1 - # via babel -recommonmark==0.7.1 - # via -r requirements.in -requests==2.26.0 - # via sphinx -six==1.16.0 - # via - # cheroot - # grpcio - # jsonschema -snowballstemmer==2.1.0 - # via sphinx -sphinx==4.2.0 - # via recommonmark -sphinxcontrib-applehelp==1.0.2 - # via sphinx -sphinxcontrib-devhelp==1.0.2 - # via sphinx -sphinxcontrib-htmlhelp==2.0.0 - # via sphinx -sphinxcontrib-jsmath==1.0.1 - # via sphinx -sphinxcontrib-qthelp==1.0.3 - # via sphinx -sphinxcontrib-serializinghtml==1.1.5 - # via sphinx -toml==0.10.2 - # via - # mypy - # pytest -typing-extensions==3.10.0.2 - # via mypy -urllib3==1.26.7 - # via requests -websocket-client==1.2.1 - # via -r requirements.in -werkzeug==1.0.1 - # via flask - -# The following packages are considered to be unsafe in a requirements file: -# setuptools From 92fd97627de8bbbfabb891d8fb669a5a2f943b89 Mon Sep 17 00:00:00 2001 From: Aaron Dewes Date: Sat, 19 Mar 2022 14:46:24 +0100 Subject: [PATCH 0469/1530] fix: Remove \ from help documentation --- doc/lightning-help.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index 24fdf73bb093..061e97771d1a 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -4,7 +4,7 @@ lightning-help -- Command to return all information about RPC commands. SYNOPSIS -------- -**help** [*command\*] +**help** [*command*] DESCRIPTION ----------- From 278ea6c8ac1b0b388618adac7e28a94e2d7b71e6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 16 Mar 2022 09:39:19 +1030 Subject: [PATCH 0470/1530] memleak: make sure we catch children which are also "notleak". By not recursing into "notleak" children, we could miss other "notleak" pointers: ``` 2022-03-15T05:25:33.9759500Z lightningd-1: 2022-03-15T05:00:15.998Z **BROKEN** connectd: MEMLEAK: 0x55b29c3d0cc8 2022-03-15T05:25:33.9759901Z lightningd-1: 2022-03-15T05:00:16.003Z **BROKEN** connectd: label=common/timeout.c:22:struct oneshot **NOTLEAK** 2022-03-15T05:25:33.9760195Z lightningd-1: 2022-03-15T05:00:16.003Z **BROKEN** connectd: backtrace: 2022-03-15T05:25:33.9760555Z lightningd-1: 2022-03-15T05:00:16.003Z **BROKEN** connectd: ccan/ccan/tal/tal.c:442 (tal_alloc_) 2022-03-15T05:25:33.9760905Z lightningd-1: 2022-03-15T05:00:16.003Z **BROKEN** connectd: common/timeout.c:22 (new_reltimer_) 2022-03-15T05:25:33.9761272Z lightningd-1: 2022-03-15T05:00:16.003Z **BROKEN** connectd: connectd/multiplex.c:558 (set_closing_timer) 2022-03-15T05:25:33.9761647Z lightningd-1: 2022-03-15T05:00:16.003Z **BROKEN** connectd: connectd/multiplex.c:586 (write_to_peer) 2022-03-15T05:25:33.9761986Z lightningd-1: 2022-03-15T05:00:16.003Z **BROKEN** connectd: ccan/ccan/io/io.c:59 (next_plan) 2022-03-15T05:25:33.9762335Z lightningd-1: 2022-03-15T05:00:16.004Z **BROKEN** connectd: ccan/ccan/io/io.c:435 (io_do_always) 2022-03-15T05:25:33.9762692Z lightningd-1: 2022-03-15T05:00:16.004Z **BROKEN** connectd: ccan/ccan/io/poll.c:304 (handle_always) 2022-03-15T05:25:33.9763035Z lightningd-1: 2022-03-15T05:00:16.004Z **BROKEN** connectd: ccan/ccan/io/poll.c:385 (io_loop) 2022-03-15T05:25:33.9763376Z lightningd-1: 2022-03-15T05:00:16.004Z **BROKEN** connectd: connectd/connectd.c:2131 (main) 2022-03-15T05:25:33.9763645Z lightningd-1: 2022-03-15T05:00:16.004Z **BROKEN** connectd: parents: 2022-03-15T05:25:33.9764020Z lightningd-1: 2022-03-15T05:00:16.004Z **BROKEN** connectd: ccan/ccan/io/io.c:91:struct io_conn **NOTLEAK** 2022-03-15T05:25:33.9764471Z lightningd-1: 2022-03-15T05:00:16.004Z **BROKEN** connectd: connectd/connectd.c:2104:struct daemon ``` Signed-off-by: Rusty Russell --- common/memleak.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/common/memleak.c b/common/memleak.c index 68fe64f74892..8f03324808ab 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -267,21 +267,25 @@ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) for (i = tal_first(p); i; i = tal_next(i)) { const char *name = tal_name(i); - if (name && strends(name, "struct memleak_helper")) { - const struct memleak_helper *mh = i; - mh->cb(memtable, p); - } else if (name && strends(name, " **NOTLEAK**")) { - pointer_referenced(memtable, i); - memleak_remove_region(memtable, i, tal_bytelen(i)); - } else if (name && strends(name, " **NOTLEAK_IGNORE_CHILDREN**")) { - remove_with_children(memtable, i); - memleak_remove_region(memtable, i, tal_bytelen(i)); - } else if (name && strends(name, "_notleak")) { - pointer_referenced(memtable, i); - call_memleak_helpers(memtable, i); - } else { - call_memleak_helpers(memtable, i); + if (name) { + if (strends(name, "struct memleak_helper")) { + const struct memleak_helper *mh = i; + mh->cb(memtable, p); + } else if (strends(name, " **NOTLEAK**") + || strends(name, "_notleak")) { + pointer_referenced(memtable, i); + memleak_remove_region(memtable, i, + tal_bytelen(i)); + } else if (strends(name, + " **NOTLEAK_IGNORE_CHILDREN**")) { + remove_with_children(memtable, i); + memleak_remove_region(memtable, i, + tal_bytelen(i)); + } } + + /* Recurse down looking for "notleak" children */ + call_memleak_helpers(memtable, i); } } From 8b04bf1357403fffd512f9258790f462110f992b Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 15 Mar 2022 17:49:21 +0100 Subject: [PATCH 0471/1530] lnprototest: upgrade the lnprototest version Changelog-None: upgrade the lnprototest version Signed-off-by: Vincenzo Palazzo --- external/lnprototest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/lnprototest b/external/lnprototest index 4b9cc370c4d8..ceeb8667f745 160000 --- a/external/lnprototest +++ b/external/lnprototest @@ -1 +1 @@ -Subproject commit 4b9cc370c4d821c172d5e44ea6aeace93b1bdf32 +Subproject commit ceeb8667f745fce80ba81e4b23c6717d72e22bbc From 8994b8dade01c11b73ab452651b562ae477e82ca Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Wed, 16 Mar 2022 12:45:29 -0700 Subject: [PATCH 0472/1530] hsmd: Add validate_commitment_tx --- channeld/channeld.c | 81 +++++++++++++++++++++++++++++++++----------- hsmd/Makefile | 2 +- hsmd/hsmd.c | 2 ++ hsmd/hsmd_wire.csv | 15 ++++++++ hsmd/libhsmd.c | 55 ++++++++++++++++++++++++++++++ openingd/common.c | 38 +++++++++++++++++++++ openingd/common.h | 6 ++++ openingd/dualopend.c | 4 +++ openingd/openingd.c | 4 +++ 9 files changed, 187 insertions(+), 20 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 98cff384f17b..edf4c7215cc4 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -994,22 +994,12 @@ static u8 *master_wait_sync_reply(const tal_t *ctx, return reply; } -/* Returns HTLC sigs, sets commit_sig */ -static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, - const struct peer *peer, - struct bitcoin_tx **txs, - const u8 *funding_wscript, - const struct htlc **htlc_map, - u64 commit_index, - struct bitcoin_signature *commit_sig) +/* Collect the htlcs for call to hsmd. */ +static struct simple_htlc **collect_htlcs(const tal_t *ctx, const struct htlc **htlc_map) { - size_t i; - struct pubkey local_htlckey; - const u8 *msg; - struct bitcoin_signature *htlc_sigs; + struct simple_htlc **htlcs; - /* Collect the htlcs for call to hsmd. */ - struct simple_htlc **htlcs = tal_arr(tmpctx, struct simple_htlc *, 0); + htlcs = tal_arr(ctx, struct simple_htlc *, 0); size_t num_entries = tal_count(htlc_map); for (size_t ndx = 0; ndx < num_entries; ++ndx) { struct htlc const *hh = htlc_map[ndx]; @@ -1023,7 +1013,25 @@ static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, tal_arr_expand(&htlcs, simple); } } + return htlcs; +} + +/* Returns HTLC sigs, sets commit_sig */ +static struct bitcoin_signature *calc_commitsigs(const tal_t *ctx, + const struct peer *peer, + struct bitcoin_tx **txs, + const u8 *funding_wscript, + const struct htlc **htlc_map, + u64 commit_index, + struct bitcoin_signature *commit_sig) +{ + struct simple_htlc **htlcs; + size_t i; + struct pubkey local_htlckey; + const u8 *msg; + struct bitcoin_signature *htlc_sigs; + htlcs = collect_htlcs(tmpctx, htlc_map); msg = towire_hsmd_sign_remote_commitment_tx(NULL, txs[0], &peer->channel->funding_pubkey[REMOTE], &peer->remote_per_commit, @@ -1442,6 +1450,17 @@ static u8 *make_revocation_msg(const struct peer *peer, u64 revoke_index, point); } +static u8 *make_revocation_msg_from_secret(const struct peer *peer, + u64 revoke_index, + struct pubkey *point, + const struct secret *old_commit_secret, + const struct pubkey *next_point) +{ + *point = *next_point; + return towire_revoke_and_ack(peer, &peer->channel_id, + old_commit_secret, next_point); +} + /* Convert changed htlcs into parts which lightningd expects. */ static void marshall_htlc_info(const tal_t *ctx, const struct htlc **changed_htlcs, @@ -1501,12 +1520,15 @@ static void send_revocation(struct peer *peer, const struct bitcoin_signature *commit_sig, const struct bitcoin_signature *htlc_sigs, const struct htlc **changed_htlcs, - const struct bitcoin_tx *committx) + const struct bitcoin_tx *committx, + const struct secret *old_secret, + const struct pubkey *next_point) { struct changed_htlc *changed; struct fulfilled_htlc *fulfilled; const struct failed_htlc **failed; struct added_htlc *added; + const u8 *msg; const u8 *msg_for_master; /* Marshall it now before channel_sending_revoke_and_ack changes htlcs */ @@ -1519,8 +1541,9 @@ static void send_revocation(struct peer *peer, &added); /* Revoke previous commit, get new point. */ - u8 *msg = make_revocation_msg(peer, peer->next_index[LOCAL]-1, - &peer->next_local_per_commit); + msg = make_revocation_msg_from_secret(peer, peer->next_index[LOCAL]-1, + &peer->next_local_per_commit, + old_secret, next_point); /* From now on we apply changes to the next commitment */ peer->next_index[LOCAL]++; @@ -1564,6 +1587,8 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) const struct htlc **htlc_map, **changed_htlcs; const u8 *funding_wscript; size_t i; + struct simple_htlc **htlcs; + const u8 * msg2; changed_htlcs = tal_arr(msg, const struct htlc *, 0); if (!channel_rcvd_commit(peer->channel, &changed_htlcs)) { @@ -1685,8 +1710,26 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) status_debug("Received commit_sig with %zu htlc sigs", tal_count(htlc_sigs)); - send_revocation(peer, - &commit_sig, htlc_sigs, changed_htlcs, txs[0]); + /* Validate the counterparty's signatures, returns prior per_commitment_secret. */ + htlcs = collect_htlcs(NULL, htlc_map); + msg2 = towire_hsmd_validate_commitment_tx(NULL, + txs[0], + (const struct simple_htlc **) htlcs, + peer->next_index[LOCAL], + channel_feerate(peer->channel, LOCAL), + &commit_sig, + htlc_sigs); + tal_free(htlcs); + msg2 = hsm_req(tmpctx, take(msg2)); + struct secret *old_secret; + struct pubkey next_point; + if (!fromwire_hsmd_validate_commitment_tx_reply(tmpctx, msg2, &old_secret, &next_point)) + status_failed(STATUS_FAIL_HSM_IO, + "Reading validate_commitment_tx reply: %s", + tal_hex(tmpctx, msg2)); + + send_revocation(peer, &commit_sig, htlc_sigs, changed_htlcs, txs[0], + old_secret, &next_point); /* We may now be quiescent on our side. */ maybe_send_stfu(peer); diff --git a/hsmd/Makefile b/hsmd/Makefile index bc396cd07021..6fd4dac59704 100644 --- a/hsmd/Makefile +++ b/hsmd/Makefile @@ -31,7 +31,6 @@ HSMD_COMMON_OBJS := \ common/daemon.o \ common/daemon_conn.o \ common/derive_basepoints.o \ - common/status_wiregen.o \ common/hash_u5.o \ common/hsm_encryption.o \ common/htlc_wire.o \ @@ -47,6 +46,7 @@ HSMD_COMMON_OBJS := \ common/setup.o \ common/status.o \ common/status_wire.o \ + common/status_wiregen.o \ common/subdaemon.o \ common/type_to_string.o \ common/utils.o \ diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index ff1b889e48bf..d5dd898f8a02 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -644,6 +644,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_NEW_CHANNEL: case WIRE_HSMD_READY_CHANNEL: case WIRE_HSMD_SIGN_COMMITMENT_TX: + case WIRE_HSMD_VALIDATE_COMMITMENT_TX: case WIRE_HSMD_VALIDATE_REVOCATION: case WIRE_HSMD_SIGN_PENALTY_TO_US: case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX: @@ -682,6 +683,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSMD_VALIDATE_COMMITMENT_TX_REPLY: case WIRE_HSMD_VALIDATE_REVOCATION_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index a92402eb54d1..a62edce8c520 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -138,6 +138,21 @@ msgdata,hsmd_sign_commitment_tx,commit_num,u64, msgtype,hsmd_sign_commitment_tx_reply,105 msgdata,hsmd_sign_commitment_tx_reply,sig,bitcoin_signature, +# Validate the counterparty's commitment signatures. +msgtype,hsmd_validate_commitment_tx,35 +msgdata,hsmd_validate_commitment_tx,tx,bitcoin_tx, +msgdata,hsmd_validate_commitment_tx,num_htlcs,u16, +msgdata,hsmd_validate_commitment_tx,htlcs,simple_htlc,num_htlcs +msgdata,hsmd_validate_commitment_tx,commit_num,u64, +msgdata,hsmd_validate_commitment_tx,feerate,u32, +msgdata,hsmd_validate_commitment_tx,sig,bitcoin_signature, +msgdata,hsmd_validate_commitment_tx,num_htlc_sigs,u16, +msgdata,hsmd_validate_commitment_tx,htlc_sigs,bitcoin_signature,num_htlc_sigs + +msgtype,hsmd_validate_commitment_tx_reply,135 +msgdata,hsmd_validate_commitment_tx_reply,old_commitment_secret,?secret, +msgdata,hsmd_validate_commitment_tx_reply,next_per_commitment_point,pubkey, + # Vaidate the counterparty's revocation secret msgtype,hsmd_validate_revocation,36 msgdata,hsmd_validate_revocation,revoke_num,u64, diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index fa488724bea2..b8e230718bab 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -96,6 +96,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_REMOTE_COMMITMENT_TX: case WIRE_HSMD_SIGN_REMOTE_HTLC_TX: + case WIRE_HSMD_VALIDATE_COMMITMENT_TX: case WIRE_HSMD_VALIDATE_REVOCATION: return (client->capabilities & HSM_CAP_SIGN_REMOTE_TX) != 0; @@ -133,6 +134,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSMD_VALIDATE_COMMITMENT_TX_REPLY: case WIRE_HSMD_VALIDATE_REVOCATION_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: @@ -1339,6 +1341,56 @@ static u8 *handle_sign_commitment_tx(struct hsmd_client *c, const u8 *msg_in) return towire_hsmd_sign_commitment_tx_reply(NULL, &sig); } +/* ~This stub implementation is overriden by fully validating signers + * that need to independently verify the peer's signatures. */ +static u8 *handle_validate_commitment_tx(struct hsmd_client *c, const u8 *msg_in) +{ + struct bitcoin_tx *tx; + struct simple_htlc **htlc; + u64 commit_num; + u32 feerate; + struct bitcoin_signature sig; + struct bitcoin_signature *htlc_sigs; + struct secret channel_seed; + struct sha256 shaseed; + struct secret *old_secret; + struct pubkey next_per_commitment_point; + + if (!fromwire_hsmd_validate_commitment_tx(tmpctx, msg_in, + &tx, &htlc, + &commit_num, &feerate, + &sig, &htlc_sigs)) + return hsmd_status_malformed_request(c, msg_in); + + /* Stub implementation */ + + /* The signatures are not checked in this stub because they + * are already checked by the caller. However, the returned + * old_secret and next_per_commitment_point are used. + */ + + get_channel_seed(&c->id, c->dbid, &channel_seed); + if (!derive_shaseed(&channel_seed, &shaseed)) + return hsmd_status_bad_request(c, msg_in, "bad derive_shaseed"); + + if (!per_commit_point(&shaseed, &next_per_commitment_point, commit_num + 1)) + return hsmd_status_bad_request_fmt( + c, msg_in, "bad per_commit_point %" PRIu64, commit_num + 1); + + if (commit_num >= 1) { + old_secret = tal(tmpctx, struct secret); + if (!per_commit_secret(&shaseed, old_secret, commit_num - 1)) { + return hsmd_status_bad_request_fmt( + c, msg_in, "Cannot derive secret %" PRIu64, commit_num - 1); + } + } else { + old_secret = NULL; + } + + return towire_hsmd_validate_commitment_tx_reply( + NULL, old_secret, &next_per_commitment_point); +} + /* This stub implementation is overriden by fully validating signers * that need to independently verify that the latest state is * commited. */ @@ -1533,6 +1585,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_sign_penalty_to_us(client, msg); case WIRE_HSMD_SIGN_COMMITMENT_TX: return handle_sign_commitment_tx(client, msg); + case WIRE_HSMD_VALIDATE_COMMITMENT_TX: + return handle_validate_commitment_tx(client, msg); case WIRE_HSMD_VALIDATE_REVOCATION: return handle_validate_revocation(client, msg); case WIRE_HSMD_SIGN_REMOTE_HTLC_TO_US: @@ -1553,6 +1607,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMD_INIT_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: + case WIRE_HSMD_VALIDATE_COMMITMENT_TX_REPLY: case WIRE_HSMD_VALIDATE_REVOCATION_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: diff --git a/openingd/common.c b/openingd/common.c index a5d6ff41285a..c31bb7774d72 100644 --- a/openingd/common.c +++ b/openingd/common.c @@ -3,8 +3,11 @@ #include #include #include +#include #include +#include #include +#include /*~ This is the key function that checks that their configuration is reasonable: * it applied for both the case where they're trying to open a channel, and when @@ -225,3 +228,38 @@ u8 *no_upfront_shutdown_script(const tal_t *ctx, return NULL; } + +void validate_initial_commitment_signature(int hsm_fd, + struct bitcoin_tx *tx, + struct bitcoin_signature *sig) +{ + struct existing_htlc **htlcs; + struct bitcoin_signature *htlc_sigs; + u32 feerate; + u64 commit_num; + const u8 *msg; + struct secret *old_secret; + struct pubkey next_point; + + /* Validate the counterparty's signature. */ + htlcs = tal_arr(NULL, struct existing_htlc *, 0); + htlc_sigs = tal_arr(NULL, struct bitcoin_signature, 0); + feerate = 0; /* unused since there are no htlcs */ + commit_num = 0; + msg = towire_hsmd_validate_commitment_tx(NULL, + tx, + (const struct simple_htlc **) htlcs, + commit_num, + feerate, + sig, + htlc_sigs); + tal_free(htlc_sigs); + tal_free(htlcs); + wire_sync_write(hsm_fd, take(msg)); + msg = wire_sync_read(tmpctx, hsm_fd); + if (!fromwire_hsmd_validate_commitment_tx_reply(tmpctx, msg, &old_secret, &next_point)) + status_failed(STATUS_FAIL_HSM_IO, + "Reading validate_commitment_tx reply: %s", + tal_hex(tmpctx, msg)); +} + diff --git a/openingd/common.h b/openingd/common.h index 8edbd9905f10..838321867dda 100644 --- a/openingd/common.h +++ b/openingd/common.h @@ -4,6 +4,8 @@ #include "config.h" struct amount_sat; +struct bitcoin_tx; +struct bitcoin_signature; struct channel_config; @@ -21,4 +23,8 @@ bool check_config_bounds(const tal_t *ctx, u8 *no_upfront_shutdown_script(const tal_t *ctx, struct feature_set *our_features, const u8 *their_features); + +void validate_initial_commitment_signature(int hsm_fd, + struct bitcoin_tx *tx, + struct bitcoin_signature *sig); #endif /* LIGHTNING_OPENINGD_COMMON_H */ diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 261b3d9c17dd..afc0d68fa61b 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1843,6 +1843,8 @@ static u8 *accepter_commits(struct state *state, return NULL; } + validate_initial_commitment_signature(HSM_FD, local_commit, &remote_sig); + /* BOLT #2: * * The recipient: @@ -2567,6 +2569,8 @@ static u8 *opener_commits(struct state *state, return NULL; } + validate_initial_commitment_signature(HSM_FD, local_commit, &remote_sig); + /* BOLT #2: * * The recipient: diff --git a/openingd/openingd.c b/openingd/openingd.c index 82c097249a4b..ce6db7eee0ba 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -701,6 +701,8 @@ static bool funder_finalize_channel_setup(struct state *state, goto fail; } + validate_initial_commitment_signature(HSM_FD, *tx, sig); + if (!check_tx_sig(*tx, 0, NULL, wscript, &state->their_funding_pubkey, sig)) { peer_failed_err(state->pps, &state->channel_id, "Bad signature %s on tx %s using key %s (channel_type=%s)", @@ -1133,6 +1135,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) return NULL; } + validate_initial_commitment_signature(HSM_FD, local_commit, &theirsig); + if (!check_tx_sig(local_commit, 0, NULL, wscript, &their_funding_pubkey, &theirsig)) { /* BOLT #1: From 65be18d355c544c22f00fef4e8c4d46d2efceacc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 20 Mar 2022 12:59:27 +1030 Subject: [PATCH 0473/1530] memleak: handle libwally allocations better. Things allocated by libwally all get the tal_name "wally_tal", which cost me a few hours trying to find a leak. In the case where we're making one of the allocations the parent of the others (e.g. a wally_psbt), we can do better: supply a name for the tal_wally_end(). So I add a new tal_wally_end_onto() which does the standard tal_steal() trick, and also changes the (typechecked!) name. Signed-off-by: Rusty Russell --- bitcoin/base58.c | 2 +- bitcoin/psbt.c | 16 ++++++++-------- bitcoin/tx.c | 8 ++++---- bitcoin/tx_parts.c | 6 +++--- common/utils.c | 25 +++++++++++++++++-------- common/utils.h | 15 +++++++++++++-- lightningd/dual_open_control.c | 6 ++++-- plugins/spender/multifundchannel.c | 2 +- plugins/spender/openchannel.c | 8 ++++---- 9 files changed, 55 insertions(+), 33 deletions(-) diff --git a/bitcoin/base58.c b/bitcoin/base58.c index 0af71d8f3785..d1368289d0b9 100644 --- a/bitcoin/base58.c +++ b/bitcoin/base58.c @@ -26,7 +26,7 @@ static char *to_base58(const tal_t *ctx, u8 version, total_length, BASE58_FLAG_CHECKSUM, &out) != WALLY_OK) out = NULL; - tal_wally_end(tal_steal(ctx, out)); + tal_wally_end_onto(ctx, out, char); return out; } diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index f3fda99edecc..990621104228 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -29,7 +29,7 @@ static struct wally_psbt *init_psbt(const tal_t *ctx, size_t num_inputs, size_t wally_err = wally_psbt_init_alloc(0, num_inputs, num_outputs, 0, &psbt); assert(wally_err == WALLY_OK); tal_add_destructor(psbt, psbt_destroy); - tal_wally_end(tal_steal(ctx, psbt)); + tal_wally_end_onto(ctx, psbt, struct wally_psbt); return psbt; } @@ -63,7 +63,7 @@ struct wally_psbt *clone_psbt(const tal_t *ctx, struct wally_psbt *psbt) tal_wally_start(); if (wally_psbt_clone_alloc(psbt, 0, &clone) != WALLY_OK) abort(); - tal_wally_end(tal_steal(ctx, clone)); + tal_wally_end_onto(ctx, clone, struct wally_psbt); return clone; } @@ -634,7 +634,7 @@ bool psbt_finalize(struct wally_psbt *psbt) wally_tx_witness_stack_add(stack, input->witness_script, input->witness_script_len); - input->final_witness = stack; + wally_psbt_input_set_final_witness(input, stack); } ok = (wally_psbt_finalize(psbt) == WALLY_OK); @@ -656,7 +656,7 @@ struct wally_tx *psbt_final_tx(const tal_t *ctx, const struct wally_psbt *psbt) else wtx = NULL; - tal_wally_end(tal_steal(ctx, wtx)); + tal_wally_end_onto(ctx, wtx, struct wally_tx); return wtx; } @@ -672,7 +672,7 @@ struct wally_psbt *psbt_from_b64(const tal_t *ctx, tal_add_destructor(psbt, psbt_destroy); else psbt = NULL; - tal_wally_end(tal_steal(ctx, psbt)); + tal_wally_end_onto(ctx, psbt, struct wally_psbt); return psbt; } @@ -685,7 +685,7 @@ char *psbt_to_b64(const tal_t *ctx, const struct wally_psbt *psbt) tal_wally_start(); ret = wally_psbt_to_base64(psbt, 0, &serialized_psbt); assert(ret == WALLY_OK); - tal_wally_end(tal_steal(ctx, serialized_psbt)); + tal_wally_end_onto(ctx, serialized_psbt, char); return serialized_psbt; } @@ -723,7 +723,7 @@ struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes, tal_add_destructor(psbt, psbt_destroy); else psbt = NULL; - tal_wally_end(tal_steal(ctx, psbt)); + tal_wally_end_onto(ctx, psbt, struct wally_psbt); return psbt; } @@ -798,7 +798,7 @@ void psbt_txid(const tal_t *ctx, wally_tx_set_input_script(tx, i, script, tal_bytelen(script)); } } - tal_wally_end(tal_steal(ctx, tx)); + tal_wally_end_onto(ctx, tx, struct wally_tx); wally_txid(tx, txid); if (wtx) diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 15c8698620b2..29d0ed8ab0f7 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -54,7 +54,7 @@ struct wally_tx_output *wally_tx_output(const tal_t *ctx, } done: - tal_wally_end(tal_steal(ctx, output)); + tal_wally_end_onto(ctx, output, struct wally_tx_output); return output; } @@ -520,7 +520,7 @@ struct bitcoin_tx *bitcoin_tx(const tal_t *ctx, wally_tx_init_alloc(WALLY_TX_VERSION_2, nlocktime, input_count, output_count, &tx->wtx); tal_add_destructor(tx, bitcoin_tx_destroy); - tal_wally_end(tal_steal(tx, tx->wtx)); + tal_wally_end_onto(tx, tx->wtx, struct wally_tx); tx->chainparams = chainparams; tx->psbt = new_psbt(tx, tx->wtx); @@ -548,7 +548,7 @@ struct bitcoin_tx *bitcoin_tx_with_psbt(const tal_t *ctx, struct wally_psbt *psb tal_wally_start(); if (wally_tx_clone_alloc(psbt->tx, 0, &tx->wtx) != WALLY_OK) tx->wtx = NULL; - tal_wally_end(tal_steal(tx, tx->wtx)); + tal_wally_end_onto(tx, tx->wtx, struct wally_tx); if (!tx->wtx) return tal_free(tx); } @@ -574,7 +574,7 @@ static struct wally_tx *pull_wtx(const tal_t *ctx, fromwire_fail(cursor, max); wtx = tal_free(wtx); } - tal_wally_end(tal_steal(ctx, wtx)); + tal_wally_end_onto(ctx, wtx, struct wally_tx); if (wtx) { size_t wsize; diff --git a/bitcoin/tx_parts.c b/bitcoin/tx_parts.c index e61a388be163..970e489f8c72 100644 --- a/bitcoin/tx_parts.c +++ b/bitcoin/tx_parts.c @@ -154,7 +154,7 @@ fromwire_wally_tx_witness_stack(const tal_t *ctx, tal_add_destructor(ws, destroy_wally_tx_witness_stack); out: - tal_wally_end(tal_steal(ctx, ws)); + tal_wally_end_onto(ctx, ws, struct wally_tx_witness_stack); return ws; } @@ -252,7 +252,7 @@ static struct wally_tx_input *fromwire_wally_tx_input(const tal_t *ctx, tal_add_destructor(in, destroy_wally_tx_input); } - tal_wally_end(tal_steal(ctx, in)); + tal_wally_end_onto(ctx, in, struct wally_tx_input); return in; } @@ -310,7 +310,7 @@ static struct wally_tx_output *fromwire_wally_tx_output(const tal_t *ctx, } else { tal_add_destructor(out, destroy_wally_tx_output); } - tal_wally_end(tal_steal(ctx, out)); + tal_wally_end_onto(ctx, out, struct wally_tx_output); return out; } diff --git a/common/utils.c b/common/utils.c index a11598fb1fb8..c3c5b0fb9f2d 100644 --- a/common/utils.c +++ b/common/utils.c @@ -34,20 +34,29 @@ void tal_wally_end(const tal_t *parent) { tal_t *p; while ((p = tal_first(wally_tal_ctx)) != NULL) { - if (p != parent) { + /* Refuse to make a loop! */ + assert(p != parent); #if DEVELOPER - /* Don't steal backtrace from wally_tal_ctx! */ - if (tal_name(p) && streq(tal_name(p), "backtrace")) { - tal_free(p); - continue; - } -#endif /* DEVELOPER */ - tal_steal(parent, p); + /* Don't steal backtrace from wally_tal_ctx! */ + if (tal_name(p) && streq(tal_name(p), "backtrace")) { + tal_free(p); + continue; } +#endif /* DEVELOPER */ + tal_steal(parent, p); } wally_tal_ctx = tal_free(wally_tal_ctx); } +void tal_wally_end_onto_(const tal_t *parent, + tal_t *from_wally, + const char *from_wally_name) +{ + if (from_wally) + tal_set_name_(from_wally, from_wally_name, 1); + tal_wally_end(tal_steal(parent, from_wally)); +} + #if DEVELOPER /* If you've got a softref, we assume no reallocs. */ static void dont_move_softref(tal_t *ctx, enum tal_notify_type ntype, void *info) diff --git a/common/utils.h b/common/utils.h index 69314f8c2354..dcfa111cbc9e 100644 --- a/common/utils.h +++ b/common/utils.h @@ -105,10 +105,21 @@ void clean_tmpctx(void); /* Call this before any libwally function which allocates. */ void tal_wally_start(void); -/* Then call this to reparent everything onto this parent (which must - * have been tal_steal() if it was allocated by libwally here) */ + +/* Then call this to reparent everything onto this parent */ void tal_wally_end(const tal_t *parent); +/* ... or this if you want to reparent onto something which is + * allocated by libwally here. Fixes up this from_wally obj to have a + * proper tal_name, too! */ +#define tal_wally_end_onto(parent, from_wally, type) \ + tal_wally_end_onto_((parent), \ + (from_wally) + 0*sizeof((from_wally) == (type *)0), \ + stringify(type)) +void tal_wally_end_onto_(const tal_t *parent, + tal_t *from_wally, + const char *from_wally_name); + /* Define sha256_eq. */ STRUCTEQ_DEF(sha256, 0, u); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index fb7df13c7bd5..3aa4f98adefe 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1470,7 +1470,9 @@ static void send_funding_tx(struct channel *channel, wally_tx_clone_alloc(wtx, 0, cast_const2(struct wally_tx **, &cs->wtx)); - tal_wally_end(tal_steal(cs, cs->wtx)); + tal_wally_end_onto(cs, + cast_const(struct wally_tx *, + cs->wtx), struct wally_tx); } wally_txid(wtx, &txid); @@ -2405,7 +2407,7 @@ json_openchannel_signed(struct command *cmd, /* Make memleak happy, (otherwise cleaned up with `cmd`) */ tal_free(psbt); - tal_wally_end(tal_steal(inflight, inflight->funding_psbt)); + tal_wally_end_onto(inflight, inflight->funding_psbt, struct wally_psbt); /* Update the PSBT on disk */ wallet_inflight_save(cmd->ld->wallet, inflight); diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index e2ada2bd1714..758524e1fdc4 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -179,7 +179,7 @@ mfc_cleanup_psbt(struct command *cmd, tal_wally_start(); if (wally_psbt_clone_alloc(psbt, 0, &pruned_psbt) != WALLY_OK) abort(); - tal_wally_end(tal_steal(NULL, pruned_psbt)); + tal_wally_end_onto(NULL, pruned_psbt, struct wally_psbt); for (size_t i = pruned_psbt->num_inputs - 1; i < pruned_psbt->num_inputs; diff --git a/plugins/spender/openchannel.c b/plugins/spender/openchannel.c index 501f28ca7a42..3202eb5d3212 100644 --- a/plugins/spender/openchannel.c +++ b/plugins/spender/openchannel.c @@ -76,7 +76,7 @@ static bool update_parent_psbt(const tal_t *ctx, tal_wally_start(); if (wally_psbt_clone_alloc(*parent_psbt, 0, &clone) != WALLY_OK) abort(); - tal_wally_end(tal_steal(ctx, clone)); + tal_wally_end_onto(ctx, clone, struct wally_psbt); /* This makes it such that we can reparent/steal added * inputs/outputs without impacting the 'original'. We @@ -261,7 +261,7 @@ static bool update_node_psbt(const tal_t *ctx, /* Only failure is alloc */ if (wally_psbt_clone_alloc(parent_psbt, 0, &clone) != WALLY_OK) abort(); - tal_wally_end(tal_steal(ctx, clone)); + tal_wally_end_onto(ctx, clone, struct wally_psbt); /* For every peer's input/output, flip the serial id * on the clone. They should all be present. */ @@ -958,7 +958,7 @@ openchannel_init_ok(struct command *cmd, * logic in `perform_openchannel_update` the same. */ tal_wally_start(); wally_psbt_clone_alloc(dest->updated_psbt, 0, &dest->psbt); - tal_wally_end(tal_steal(mfc, dest->updated_psbt)); + tal_wally_end_onto(mfc, dest->updated_psbt, struct wally_psbt); return openchannel_init_done(dest); } @@ -998,7 +998,7 @@ openchannel_init_dest(struct multifundchannel_destination *dest) /* Copy the original parent down */ tal_wally_start(); wally_psbt_clone_alloc(mfc->psbt, 0, &dest->psbt); - tal_wally_end(tal_steal(mfc, dest->psbt)); + tal_wally_end_onto(mfc, dest->psbt, struct wally_psbt); json_add_psbt(req->js, "initialpsbt", dest->psbt); if (mfc->cmtmt_feerate_str) From cf55d40eca950bbc4b5dfb2fefb6b16ad69392d0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 20 Mar 2022 12:59:29 +1030 Subject: [PATCH 0474/1530] psbt: fix dual-funding memleak. This happened occasionally in tests/test_opening.py::test_rbf_fails_to_broadcast: we overwrote the witness stack without freeing the old one. ``` lightningd-2: 2022-03-18T02:23:32.113Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: MEMLEAK: 0x559c375d9df8 lightningd-2: 2022-03-18T02:23:32.113Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: label=wally_tal lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: backtrace: lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: ccan/ccan/tal/tal.c:442 (tal_alloc_) lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: ccan/ccan/tal/tal.c:471 (tal_alloc_arr_) lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: common/setup.c:13 (wally_tal) lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: ../../../libwally-core/src/internal.c:285 (wally_calloc) lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: ../../../libwally-core/src/transaction.c:234 (wally_tx_witness_stack_init_alloc) lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: ../../../libwally-core/src/psbt.c:1119 (pull_psbt_input) lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: ../../../libwally-core/src/psbt.c:1411 (wally_psbt_from_bytes) lightningd-2: 2022-03-18T02:23:32.114Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: bitcoin/psbt.c:722 (psbt_from_bytes) lightningd-2: 2022-03-18T02:23:32.115Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: bitcoin/psbt.c:753 (fromwire_wally_psbt) lightningd-2: 2022-03-18T02:23:32.115Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: openingd/dualopend_wiregen.c:246 (fromwire_dualopend_reinit) lightningd-2: 2022-03-18T02:23:32.115Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: openingd/dualopend.c:3855 (main) lightningd-2: 2022-03-18T02:23:32.115Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: parents: lightningd-2: 2022-03-18T02:23:32.115Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: struct wally_psbt lightningd-2: 2022-03-18T02:23:32.115Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: openingd/dualopend.c:3804:struct state lightningd-2: 2022-03-18T02:23:32.115Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba932 ``` Signed-off-by: Rusty Russell --- common/psbt_internal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/psbt_internal.c b/common/psbt_internal.c index 19460f01e58f..46bb0d134fd2 100644 --- a/common/psbt_internal.c +++ b/common/psbt_internal.c @@ -9,6 +9,8 @@ psbt_input_set_final_witness_stack(const tal_t *ctx, struct wally_psbt_input *in, const struct witness_element **elements) { + wally_tx_witness_stack_free(in->final_witness); + tal_wally_start(); wally_tx_witness_stack_init_alloc(tal_count(elements), &in->final_witness); From 953f238bd2e2da0c83056801f4d17668ec309b3a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 16 Mar 2022 11:56:41 +1030 Subject: [PATCH 0475/1530] connectd: use closefrom for faster forking, and ignore children Zombie sighting fom jb55. Fixes: #5092 Changelog-EXPERIMENTAL: Fixed `experimental-websocket-port` not to leave zombie processes. Signed-off-by: Rusty Russell --- connectd/connectd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index f3c1e16056e4..367253f030cf 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -10,6 +10,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -569,7 +571,6 @@ static struct io_plan *websocket_connection_in(struct io_conn *conn, goto close_execfail_fail; if (childpid == 0) { - size_t max; close(childmsg[0]); close(execfail[0]); @@ -582,9 +583,7 @@ static struct io_plan *websocket_connection_in(struct io_conn *conn, goto child_errno_fail; /* Make (fairly!) sure all other fds are closed. */ - max = sysconf(_SC_OPEN_MAX); - for (size_t i = STDERR_FILENO + 1; i < max; i++) - close(i); + closefrom(STDERR_FILENO + 1); /* Tell websocket helper what we read so far. */ execlp(daemon->websocket_helper, daemon->websocket_helper, @@ -2117,6 +2116,10 @@ int main(int argc, char *argv[]) * our status_ and failed messages. */ status_setup_async(daemon->master); + /* Don't leave around websocketd zombies. Technically not portable, + * but OK for Linux and BSD, so... */ + signal(SIGCHLD, SIG_IGN); + /* This streams gossip to and from gossipd */ daemon->gossipd = daemon_conn_new(daemon, GOSSIPCTL_FD, recv_gossip, NULL, From 6fdcc86f9d2ce9637cf40da5f4f025c4eca08930 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:23 +1030 Subject: [PATCH 0476/1530] lightningd: store htlc_maximum_msat for channel in the db. We currently don't allow setting it, but it's been requested. Signed-off-by: Rusty Russell --- lightningd/channel.c | 55 +++++++++++++++++++++++++++++++++++- lightningd/channel.h | 7 ++++- lightningd/opening_control.c | 3 +- wallet/db.c | 2 ++ wallet/test/run-wallet.c | 3 +- wallet/wallet.c | 15 ++++++---- 6 files changed, 76 insertions(+), 9 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 10f388672ea6..ff68376ee764 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -306,6 +307,48 @@ struct channel *new_unsaved_channel(struct peer *peer, return channel; } +/* + * The maximum msat that this node could possibly accept for an htlc. + * It's the default htlc_maximum_msat in channel_updates, if none is + * explicitly set (and the cap on what can be set!). + * + * We advertize the maximum value possible, defined as the smaller + * of the remote's maximum in-flight HTLC or the total channel + * capacity the reserve we have to keep. + * FIXME: does this need fuzz? + */ +static struct amount_msat htlc_max_possible_send(const struct channel *channel) +{ + struct amount_sat lower_bound; + struct amount_msat lower_bound_msat; + + /* These shouldn't fail */ + if (!amount_sat_sub(&lower_bound, channel->funding_sats, + channel->channel_info.their_config.channel_reserve)) { + log_broken(channel->log, "%s: their reserve %s > funding %s!", + __func__, + type_to_string(tmpctx, struct amount_sat, + &channel->funding_sats), + type_to_string(tmpctx, struct amount_sat, + &channel->channel_info.their_config.channel_reserve)); + return AMOUNT_MSAT(0); + } + + if (!amount_sat_to_msat(&lower_bound_msat, lower_bound)) { + log_broken(channel->log, "%s: impossible size channel %s!", + __func__, + type_to_string(tmpctx, struct amount_sat, + &lower_bound)); + return AMOUNT_MSAT(0); + } + + if (amount_msat_less(channel->channel_info.their_config.max_htlc_value_in_flight, + lower_bound_msat)) + lower_bound_msat = channel->channel_info.their_config.max_htlc_value_in_flight; + + return lower_bound_msat; +} + struct channel *new_channel(struct peer *peer, u64 dbid, /* NULL or stolen */ struct wallet_shachain *their_shachain, @@ -366,9 +409,11 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u32 lease_expiry, secp256k1_ecdsa_signature *lease_commit_sig STEALS, u32 lease_chan_max_msat, - u16 lease_chan_max_ppt) + u16 lease_chan_max_ppt, + struct amount_msat htlc_maximum_msat) { struct channel *channel = tal(peer->ld, struct channel); + struct amount_msat htlc_max; assert(dbid != 0); channel->peer = peer; @@ -464,6 +509,14 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->blockheight_states = dup_height_states(channel, height_states); channel->channel_update = NULL; + /* DB migration, for example, sets this to bignum; correct + * here */ + htlc_max = htlc_max_possible_send(channel); + if (amount_msat_less(htlc_max, htlc_maximum_msat)) + channel->htlc_maximum_msat = htlc_max; + else + channel->htlc_maximum_msat = htlc_maximum_msat; + list_add_tail(&peer->channels, &channel->list); channel->rr_number = peer->ld->rr_counter++; tal_add_destructor(channel, destroy_channel); diff --git a/lightningd/channel.h b/lightningd/channel.h index 091346f2cc2e..98953fbe7947 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -196,8 +196,12 @@ struct channel { * peer via option_data_loss_protect? */ const struct pubkey *future_per_commitment_point; + /* Max htlc amount allowed in channel. */ + struct amount_msat htlc_maximum_msat; + /* Feerate per channel */ u32 feerate_base, feerate_ppm; + /* But allow these feerates up until this time. */ struct timeabs old_feerate_timeout; u32 old_feerate_base, old_feerate_ppm; @@ -309,7 +313,8 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u32 lease_expiry, secp256k1_ecdsa_signature *lease_commit_sig STEALS, u32 lease_chan_max_msat, - u16 lease_chan_max_ppt); + u16 lease_chan_max_ppt, + struct amount_msat htlc_maximum_msat); /* new_inflight - Create a new channel_inflight for a channel */ struct channel_inflight * diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 4c91a43f2751..be576e75a268 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -209,7 +209,8 @@ wallet_commit_channel(struct lightningd *ld, NULL, take(new_height_states(NULL, uc->fc ? LOCAL : REMOTE, &lease_start_blockheight)), - 0, NULL, 0, 0); /* No leases on v1s */ + 0, NULL, 0, 0, /* No leases on v1s */ + AMOUNT_MSAT(-1ULL)); /* No htlc_maximum_msat */ /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); diff --git a/wallet/db.c b/wallet/db.c index 3225579ebd43..09721db2ce2a 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -869,6 +869,8 @@ static struct migration dbmigrations[] = { NULL}, {SQL("ALTER TABLE channel_htlcs ADD fees_msat BIGINT DEFAULT 0"), NULL}, {SQL("ALTER TABLE channel_funding_inflights ADD lease_fee BIGINT DEFAULT 0"), NULL}, + /* Default is too big; we set to max after loading */ + {SQL("ALTER TABLE channels ADD htlc_maximum_msat BIGINT DEFAULT 2100000000000000"), NULL}, }; /** diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 5c28d58f620d..c1ce3a94b489 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1597,7 +1597,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) &lease_blockheight_start), 100, lease_commit_sig, - 7777, 22); + 7777, 22, + AMOUNT_MSAT(-1ULL)); db_begin_transaction(w->db); CHECK(!wallet_err); wallet_channel_insert(w, chan); diff --git a/wallet/wallet.c b/wallet/wallet.c index fa44b869cafd..7cc5676f0893 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1263,7 +1263,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm struct pubkey local_funding_pubkey; struct pubkey *future_per_commitment_point; struct amount_sat funding_sat, our_funding_sat; - struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max; + struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max, htlc_maximum_msat; struct channel_type *type; secp256k1_ecdsa_signature *lease_commit_sig; u32 lease_chan_max_msat; @@ -1403,6 +1403,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm db_col_amount_msat(stmt, "msatoshi_local", &our_msat); db_col_amount_msat(stmt, "msatoshi_to_us_min", &msat_to_us_min); db_col_amount_msat(stmt, "msatoshi_to_us_max", &msat_to_us_max); + db_col_amount_msat(stmt, "htlc_maximum_msat", &htlc_maximum_msat); if (!db_col_is_null(stmt, "lease_commit_sig")) { lease_commit_sig = tal(w, secp256k1_ecdsa_signature); @@ -1478,7 +1479,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm db_col_int(stmt, "lease_expiry"), lease_commit_sig, lease_chan_max_msat, - lease_chan_max_ppt); + lease_chan_max_ppt, + htlc_maximum_msat); if (!wallet_channel_load_inflights(w, chan)) { tal_free(chan); @@ -1572,6 +1574,7 @@ static bool wallet_channels_load_active(struct wallet *w) ", lease_commit_sig" ", lease_chan_max_msat" ", lease_chan_max_ppt" + ", htlc_maximum_msat" " FROM channels" " WHERE state != ?;")); //? 0 db_bind_int(stmt, 0, CLOSED); @@ -1851,8 +1854,9 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " lease_expiry=?," // 38 " lease_commit_sig=?," // 39 " lease_chan_max_msat=?," // 40 - " lease_chan_max_ppt=?" // 41 - " WHERE id=?")); // 42 + " lease_chan_max_ppt=?," // 41 + " htlc_maximum_msat=?" // 42 + " WHERE id=?")); // 43 db_bind_u64(stmt, 0, chan->their_shachain.id); if (chan->scid) db_bind_short_channel_id(stmt, 1, chan->scid); @@ -1915,7 +1919,8 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt, 40); db_bind_null(stmt, 41); } - db_bind_u64(stmt, 42, chan->dbid); + db_bind_amount_msat(stmt, 42, &chan->htlc_maximum_msat); + db_bind_u64(stmt, 43, chan->dbid); db_exec_prepared_v2(take(stmt)); wallet_channel_config_save(w, &chan->channel_info.their_config); From 3217dbe17f5d8977bf97eecca4f39bdff2ac1586 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:27 +1030 Subject: [PATCH 0477/1530] channeld: get htlc_maximum_msat from lightningd. We used to calculate it ourselves. Unfortunately this needs to be done in several places, since new_channel() isn't used to fully create a channel in the case of dual funding :( Signed-off-by: Rusty Russell --- channeld/channeld.c | 43 +++++----------------------------- channeld/channeld_wire.csv | 1 + lightningd/channel.c | 2 +- lightningd/channel.h | 1 + lightningd/channel_control.c | 1 + lightningd/dual_open_control.c | 2 ++ 6 files changed, 12 insertions(+), 38 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index edf4c7215cc4..65cf9d5e17a2 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -119,8 +119,12 @@ struct peer { /* CLTV delta to announce to peers */ u16 cltv_delta; + + /* We only really know these because we're the ones who create + * the channel_updates. */ u32 fee_base; u32 fee_per_satoshi; + struct amount_msat htlc_maximum_msat; /* The scriptpubkey to use for shutting down. */ u32 *final_index; @@ -212,42 +216,6 @@ const u8 *hsm_req(const tal_t *ctx, const u8 *req TAKES) return msg; } -/* - * The maximum msat that this node will accept for an htlc. - * It's flagged as an optional field in `channel_update`. - * - * We advertize the maximum value possible, defined as the smaller - * of the remote's maximum in-flight HTLC or the total channel - * capacity the reserve we have to keep. - * FIXME: does this need fuzz? - */ -static struct amount_msat advertized_htlc_max(const struct channel *channel) -{ - struct amount_sat lower_bound; - struct amount_msat lower_bound_msat; - - /* This shouldn't fail */ - if (!amount_sat_sub(&lower_bound, channel->funding_sats, - channel->config[REMOTE].channel_reserve)) { - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "funding %s - remote reserve %s?", - type_to_string(tmpctx, struct amount_sat, - &channel->funding_sats), - type_to_string(tmpctx, struct amount_sat, - &channel->config[REMOTE] - .channel_reserve)); - } - - if (!amount_sat_to_msat(&lower_bound_msat, lower_bound)) { - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "lower_bound %s invalid?", - type_to_string(tmpctx, struct amount_sat, - &lower_bound)); - } - - return lower_bound_msat; -} - #if EXPERIMENTAL_FEATURES static void maybe_send_stfu(struct peer *peer) { @@ -396,7 +364,7 @@ static void send_channel_update(struct peer *peer, int disable_flag) peer->channel->config[REMOTE].htlc_minimum, peer->fee_base, peer->fee_per_satoshi, - advertized_htlc_max(peer->channel)); + peer->htlc_maximum_msat); wire_sync_write(MASTER_FD, take(msg)); } @@ -3774,6 +3742,7 @@ static void init_channel(struct peer *peer) &opener, &peer->fee_base, &peer->fee_per_satoshi, + &peer->htlc_maximum_msat, &local_msat, &points[LOCAL], &funding_pubkey[LOCAL], diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index b84ca73c428e..f0d9f9e9c2ce 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -35,6 +35,7 @@ msgdata,channeld_init,old_remote_per_commit,pubkey, msgdata,channeld_init,opener,enum side, msgdata,channeld_init,fee_base,u32, msgdata,channeld_init,fee_proportional,u32, +msgdata,channeld_init,htlc_maximum_msat,amount_msat, msgdata,channeld_init,local_msatoshi,amount_msat, msgdata,channeld_init,our_basepoints,basepoints, msgdata,channeld_init,our_funding_pubkey,pubkey, diff --git a/lightningd/channel.c b/lightningd/channel.c index ff68376ee764..3bb5ad0272bf 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -317,7 +317,7 @@ struct channel *new_unsaved_channel(struct peer *peer, * capacity the reserve we have to keep. * FIXME: does this need fuzz? */ -static struct amount_msat htlc_max_possible_send(const struct channel *channel) +struct amount_msat htlc_max_possible_send(const struct channel *channel) { struct amount_sat lower_bound; struct amount_msat lower_bound_msat; diff --git a/lightningd/channel.h b/lightningd/channel.h index 98953fbe7947..867f8208d4ae 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -481,4 +481,5 @@ struct htlc_out *channel_has_htlc_out(struct channel *channel); const u8 *get_channel_update(struct channel *channel); +struct amount_msat htlc_max_possible_send(const struct channel *channel); #endif /* LIGHTNING_LIGHTNINGD_CHANNEL_H */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index e07db69d09da..390320e72a0e 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -702,6 +702,7 @@ void peer_start_channeld(struct channel *channel, channel->opener, channel->feerate_base, channel->feerate_ppm, + channel->htlc_maximum_msat, channel->our_msat, &channel->local_basepoints, &channel->local_funding_pubkey, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 3aa4f98adefe..d2fa86f4adc6 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1110,6 +1110,7 @@ wallet_update_channel(struct lightningd *ld, channel->msat_to_us_min = our_msat; channel->msat_to_us_max = our_msat; channel->lease_expiry = lease_expiry; + channel->htlc_maximum_msat = htlc_max_possible_send(channel); tal_free(channel->lease_commit_sig); channel->lease_commit_sig = tal_steal(channel, lease_commit_sig); @@ -1252,6 +1253,7 @@ wallet_commit_channel(struct lightningd *ld, channel->lease_chan_max_msat = lease_chan_max_msat; channel->lease_chan_max_ppt = lease_chan_max_ppt; + channel->htlc_maximum_msat = htlc_max_possible_send(channel); /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); From f078a916e72b900c7d6f78654885155c1cc2dc65 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:27 +1030 Subject: [PATCH 0478/1530] lightningd: enforce htlc_maximum_msat. When we let them set it, this matters! Signed-off-by: Rusty Russell --- lightningd/channel.h | 3 ++- lightningd/peer_control.c | 1 + lightningd/peer_htlcs.c | 12 ++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lightningd/channel.h b/lightningd/channel.h index 867f8208d4ae..5390ad74ae66 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -202,9 +202,10 @@ struct channel { /* Feerate per channel */ u32 feerate_base, feerate_ppm; - /* But allow these feerates up until this time. */ + /* But allow these feerates/htlcs up until this time. */ struct timeabs old_feerate_timeout; u32 old_feerate_base, old_feerate_ppm; + struct amount_msat old_htlc_maximum_msat; /* If they used option_upfront_shutdown_script. */ const u8 *remote_upfront_shutdown_script; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 0d776ba0510e..aae61a8c293d 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2015,6 +2015,7 @@ static void set_channel_fees(struct command *cmd, struct channel *channel, = timeabs_add(time_now(), time_from_sec(delaysecs)); channel->old_feerate_base = channel->feerate_base; channel->old_feerate_ppm = channel->feerate_ppm; + channel->old_htlc_maximum_msat = channel->htlc_maximum_msat; } /* set new values */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index b95746590267..fc55f2b27c16 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -656,6 +656,18 @@ static void forward_htlc(struct htlc_in *hin, "Allowing payment using older feerate"); } + if (amount_msat_greater(amt_to_forward, next->htlc_maximum_msat)) { + /* Are we in old-max grace-period? */ + if (!time_before(time_now(), next->old_feerate_timeout) + || amount_msat_greater(amt_to_forward, next->old_htlc_maximum_msat)) { + failmsg = towire_temporary_channel_failure(tmpctx, + get_channel_update(next)); + goto fail; + } + log_info(hin->key.channel->log, + "Allowing large htlc using older htlc_maximum_msat"); + } + if (!check_cltv(hin, cltv_expiry, outgoing_cltv_value, ld->config.cltv_expiry_delta)) { failmsg = towire_incorrect_cltv_expiry(tmpctx, cltv_expiry, From 33bd2512966110bd4f3882ff686bb5572cb77798 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:27 +1030 Subject: [PATCH 0479/1530] listpeers: show maximum_htlc_out_msat. This is htlc_maximum_msat in BOLT 7 speak, but this name matches our existing fields and is clearer in this context. Signed-off-by: Rusty Russell --- doc/lightning-listpeers.7.md | 3 ++- doc/schemas/listpeers.schema.json | 8 ++++++++ lightningd/peer_control.c | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index b90d112e7dd3..569f53e58ccb 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -87,6 +87,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **spendable_msat** (msat, optional): total we could send through channel - **receivable_msat** (msat, optional): total peer could send through channel - **minimum_htlc_in_msat** (msat, optional): the minimum amount HTLC we accept + - **maximum_htlc_out_msat** (msat, optional): the maximum amount HTLC we will send - **their_to_self_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close - **our_to_self_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close - **max_accepted_htlcs** (u32, optional): Maximum number of incoming HTLC we will accept at once @@ -378,4 +379,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:001e3cf495571bb09fe29f74adde8a6e40e69ddb1169934924eaf901a1e5f3c0) +[comment]: # ( SHA256STAMP:8e30caf48aed46acc7c053a355867dc8b8624035dba4ea7668d30d86b8d827cd) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index e25ef39acc48..cada820f1fcc 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -418,6 +418,10 @@ "type": "msat", "description": "the minimum amount HTLC we accept" }, + "maximum_htlc_out_msat": { + "type": "msat", + "description": "the maximum amount HTLC we will send" + }, "their_to_self_delay": { "type": "u32", "description": "the number of blocks before they can take their funds if they unilateral close" @@ -772,6 +776,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, "their_to_self_delay": {}, @@ -859,6 +864,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, "their_to_self_delay": {}, @@ -947,6 +953,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, "their_to_self_delay": {}, @@ -1034,6 +1041,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, "their_to_self_delay": {}, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index aae61a8c293d..e3aff640821c 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -861,6 +861,9 @@ static void json_add_channel(struct lightningd *ld, channel->our_config.htlc_minimum, "htlc_minimum_msat", "minimum_htlc_in_msat"); + json_add_amount_msat_only(response, + "maximum_htlc_out_msat", + channel->htlc_maximum_msat); /* The `to_self_delay` is imposed on the *other* * side, so our configuration `to_self_delay` is From 4fdcee9a115203794f1b9c2dce857d5794b267e6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:28 +1030 Subject: [PATCH 0480/1530] channeld: generalize specific_feerates mesage into config_channel. 1. Add the htlc_max param. 2. Allow parameters to be unset, meaning "don't change". Signed-off-by: Rusty Russell --- channeld/channeld.c | 33 ++++++++++++++------- channeld/channeld_wire.csv | 9 +++--- lightningd/channel_control.c | 2 +- lightningd/peer_control.c | 3 +- lightningd/test/run-invoice-select-inchan.c | 6 ++-- wallet/test/run-wallet.c | 6 ++-- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 65cf9d5e17a2..262cb387f3be 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -3426,18 +3426,31 @@ static void handle_blockheight(struct peer *peer, const u8 *inmsg) } } -static void handle_specific_feerates(struct peer *peer, const u8 *inmsg) +static void handle_config_channel(struct peer *peer, const u8 *inmsg) { - u32 base_old = peer->fee_base; - u32 per_satoshi_old = peer->fee_per_satoshi; + u32 *base, *ppm; + struct amount_msat *htlc_max; + bool changed; - if (!fromwire_channeld_specific_feerates(inmsg, - &peer->fee_base, - &peer->fee_per_satoshi)) - master_badmsg(WIRE_CHANNELD_SPECIFIC_FEERATES, inmsg); + if (!fromwire_channeld_config_channel(inmsg, inmsg, &base, &ppm, &htlc_max)) + master_badmsg(WIRE_CHANNELD_CONFIG_CHANNEL, inmsg); /* only send channel updates if values actually changed */ - if (peer->fee_base != base_old || peer->fee_per_satoshi != per_satoshi_old) + changed = false; + if (base && *base != peer->fee_base) { + peer->fee_base = *base; + changed = true; + } + if (ppm && *ppm != peer->fee_per_satoshi) { + peer->fee_per_satoshi = *ppm; + changed = true; + } + if (htlc_max && !amount_msat_eq(*htlc_max, peer->htlc_maximum_msat)) { + peer->htlc_maximum_msat = *htlc_max; + changed = true; + } + + if (changed) send_channel_update(peer, 0); } @@ -3627,10 +3640,10 @@ static void req_in(struct peer *peer, const u8 *msg) return; handle_fail(peer, msg); return; - case WIRE_CHANNELD_SPECIFIC_FEERATES: + case WIRE_CHANNELD_CONFIG_CHANNEL: if (handle_master_request_later(peer, msg)) return; - handle_specific_feerates(peer, msg); + handle_config_channel(peer, msg); return; case WIRE_CHANNELD_SEND_SHUTDOWN: handle_shutdown_cmd(peer, msg); diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index f0d9f9e9c2ce..df54a26b7f0d 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -216,10 +216,11 @@ msgtype,channeld_fail_fallen_behind,1028 # This is NULL if option_static_remotekey. msgdata,channeld_fail_fallen_behind,remote_per_commitment_point,?pubkey, -# Handle a channel specific feerate base ppm configuration -msgtype,channeld_specific_feerates,1029 -msgdata,channeld_specific_feerates,feerate_base,u32, -msgdata,channeld_specific_feerates,feerate_ppm,u32, +# Handle a channel-specific configuration change +msgtype,channeld_config_channel,1029 +msgdata,channeld_config_channel,feerate_base,?u32, +msgdata,channeld_config_channel,feerate_ppm,?u32, +msgdata,channeld_config_channel,htlc_maximum,?amount_msat, # When we receive announcement_signatures for channel announce msgtype,channeld_got_announcement,1017 diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 390320e72a0e..2b21d524190a 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -548,7 +548,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_DEV_REENABLE_COMMIT: case WIRE_CHANNELD_FEERATES: case WIRE_CHANNELD_BLOCKHEIGHT: - case WIRE_CHANNELD_SPECIFIC_FEERATES: + case WIRE_CHANNELD_CONFIG_CHANNEL: case WIRE_CHANNELD_CHANNEL_UPDATE: case WIRE_CHANNELD_DEV_MEMLEAK: case WIRE_CHANNELD_DEV_QUIESCE: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e3aff640821c..38809d433a64 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2028,7 +2028,8 @@ static void set_channel_fees(struct command *cmd, struct channel *channel, /* tell channeld to make a send_channel_update */ if (channel->owner && streq(channel->owner->name, "channeld")) subd_send_msg(channel->owner, - take(towire_channeld_specific_feerates(NULL, base, ppm))); + take(towire_channeld_config_channel(NULL, &base, &ppm, + NULL))); /* save values to database */ wallet_channel_save(cmd->ld->wallet, channel); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 9e95ca3e0430..1f7db910b6dc 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -625,15 +625,15 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_channeld_config_channel */ +u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED) +{ fprintf(stderr, "towire_channeld_config_channel called!\n"); abort(); } /* Generated stub for towire_channeld_dev_memleak */ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_memleak called!\n"); abort(); } /* Generated stub for towire_channeld_dev_reenable_commit */ u8 *towire_channeld_dev_reenable_commit(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_reenable_commit called!\n"); abort(); } -/* Generated stub for towire_channeld_specific_feerates */ -u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_base UNNEEDED, u32 feerate_ppm UNNEEDED) -{ fprintf(stderr, "towire_channeld_specific_feerates 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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index c1ce3a94b489..d6573857ed29 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -693,6 +693,9 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, /* Generated stub for towire_channel_disabled */ u8 *towire_channel_disabled(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channel_disabled called!\n"); abort(); } +/* Generated stub for towire_channeld_config_channel */ +u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED) +{ fprintf(stderr, "towire_channeld_config_channel called!\n"); abort(); } /* Generated stub for towire_channeld_dev_memleak */ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_memleak called!\n"); abort(); } @@ -717,9 +720,6 @@ u8 *towire_channeld_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amo /* Generated stub for towire_channeld_sending_commitsig_reply */ u8 *towire_channeld_sending_commitsig_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_sending_commitsig_reply called!\n"); abort(); } -/* Generated stub for towire_channeld_specific_feerates */ -u8 *towire_channeld_specific_feerates(const tal_t *ctx UNNEEDED, u32 feerate_base UNNEEDED, u32 feerate_ppm UNNEEDED) -{ fprintf(stderr, "towire_channeld_specific_feerates called!\n"); abort(); } /* 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(); } From 66e264d6b366ae80d91985697c17acb09a1748a9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:28 +1030 Subject: [PATCH 0481/1530] lightningd: new setchannel command. Based on setchannelfee, but expanded to allow setting max htlc amount (and others in future?). The main differences: 1. It doesn't change values which are not specified (that would be hard to add fields to!) 2. It says exactly what all values are in any potentially changed channels. Changelog-Added: JSON-RPC: new `setchannel` command generalizes `setchannelfee`. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 25 ++++ doc/Makefile | 1 + doc/index.rst | 1 + doc/lightning-setchannel.7.md | 89 ++++++++++++++ doc/schemas/setchannel.schema.json | 53 ++++++++ lightningd/peer_control.c | 121 ++++++++++++++++--- 6 files changed, 275 insertions(+), 15 deletions(-) create mode 100644 doc/lightning-setchannel.7.md create mode 100644 doc/schemas/setchannel.schema.json diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 12759d694e5a..92185ec8a3f8 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -1176,6 +1176,31 @@ def setchannelfee(self, id, base=None, ppm=None, enforcedelay=None): } return self.call("setchannelfee", payload) + def setchannel(self, id, feebase=None, feeppm=None, htlcmax=None, enforcedelay=None): + """Set configuration a channel/peer {id} (or 'all'). + + {feebase} is a value in millisatoshi that is added as base fee + to any routed payment. + + {feeppm} is a value added proportionally per-millionths to any + routed payment volume in satoshi. + + {htlcmax} is the maximum (outgoing) htlc amount to allow and + advertize. + + {enforcedelay} is the number of seconds before enforcing this + change. + + """ + payload = { + "id": id, + "feebase": feebase, + "feeppm": feeppm, + "htlcmax": htlcmax, + "enforcedelay": enforcedelay, + } + return self.call("setchannel", payload) + def stop(self): """ Shut down the lightningd process. diff --git a/doc/Makefile b/doc/Makefile index 75e5fbdb1c65..68d4eb842106 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -65,6 +65,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-sendonion.7 \ doc/lightning-sendonionmessage.7 \ doc/lightning-sendpay.7 \ + doc/lightning-setchannel.7 \ doc/lightning-setchannelfee.7 \ doc/lightning-sendcustommsg.7 \ doc/lightning-signmessage.7 \ diff --git a/doc/index.rst b/doc/index.rst index 349f32086462..4671ccae850a 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -97,6 +97,7 @@ c-lightning Documentation lightning-sendonionmessage lightning-sendpay lightning-sendpsbt + lightning-setchannel lightning-setchannelfee lightning-signmessage lightning-signpsbt diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md new file mode 100644 index 000000000000..658511a18188 --- /dev/null +++ b/doc/lightning-setchannel.7.md @@ -0,0 +1,89 @@ +lightning-setchannel -- Command for configuring fees / maximum htlc on a lightning channel +=========================================================================================== + +SYNOPSIS +-------- + +**setchannel** *id* [*feebase*] [*feeppm*] [*htlcmax*] [*enforcedelay*] + +DESCRIPTION +----------- + +The **setchannel** RPC command sets channel specific routing fees, and +`htlc_maximum_msat` as defined in BOLT \#7. The channel has to be in +normal or awaiting state. This can be checked by **listpeers** +reporting a *state* of CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN +for the channel. + +*id* is required and should contain a scid (short channel ID), channel +id or peerid (pubkey) of the channel to be modified. If *id* is set to +"all", the updates are applied to all channels in states +CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN. + +*feebase* is an optional value in millisatoshi that is added as base fee to +any routed payment: if omitted, it is unchanged. It can be a whole number, or a whole +number ending in *msat* or *sat*, or a number with three decimal places +ending in *sat*, or a number with 1 to 11 decimal places ending in +*btc*. + +*feeppm* is an optional value that is added proportionally per-millionths +to any routed payment volume in satoshi. For example, if ppm is 1,000 +and 1,000,000 satoshi is being routed through the channel, an +proportional fee of 1,000 satoshi is added, resulting in a 0.1% fee. + +*htlcmax* is an optional value that limits how large an HTLC we will +send: if omitted, it is unchanged (the default is no effective +limit). It can be a whole number, or a whole number ending in *msat* +or *sat*, or a number with three decimal places ending in *sat*, or a +number with 1 to 11 decimal places ending in *btc*. + +*enforcedelay* is the number of seconds to delay before enforcing the +new fees/htlc max (default 600, which is ten minutes). This gives the +network a chance to catch up with the new rates and avoids rejecting +HTLCs before they do. This only has an effect if rates are increased +(we always allow users to overpay fees) or *htlcmax* is decreased, and +only applied to a single rate increase per channel (we don't remember +an arbitrary number of prior feerates) and if the node is restarted +the updated configuration is enforced immediately. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **channels** is returned. It is an array of objects, where each object contains: +- **peer_id** (pubkey): The node_id of the peer +- **channel_id** (hex): The channel_id of the channel (always 64 characters) +- **fee_base_msat** (msat): The resulting feebase (this is the BOLT #7 name) +- **fee_proportional_millionths** (u32): The resulting feeppm (this is the BOLT #7 name) +- **maximum_htlc_out_msat** (msat): The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat) +- **short_channel_id** (short_channel_id, optional): the short_channel_id (if locked in) + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +ERRORS +------ + +The following error codes may occur: +- -1: Channel is in incorrect state, i.e. Catchall nonspecific error. +- -32602: JSONRPC2\_INVALID\_PARAMS, i.e. Given id is not a channel ID +or short channel ID. + +AUTHOR +------ + +Michael Schmoock <> is the author of this +feature. Rusty Russell <> is mainly +responsible for the c-lightning project. + +SEE ALSO +-------- + +lightningd-config(5), lightning-fundchannel(7), +lightning-listchannels(7), lightning-listpeers(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:25c6733af784e8a21a8eed4bcb0f12767ae49d16fe623187ae5313b5bb5cdd80) diff --git a/doc/schemas/setchannel.schema.json b/doc/schemas/setchannel.schema.json new file mode 100644 index 000000000000..fcd896a51b33 --- /dev/null +++ b/doc/schemas/setchannel.schema.json @@ -0,0 +1,53 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channels" + ], + "properties": { + "channels": { + "type": "array", + "description": "channel(s) set, and their resulting configuration", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "peer_id", + "channel_id", + "fee_base_msat", + "fee_proportional_millionths", + "maximum_htlc_out_msat" + ], + "properties": { + "peer_id": { + "type": "pubkey", + "description": "The node_id of the peer" + }, + "channel_id": { + "type": "hex", + "description": "The channel_id of the channel", + "minLength": 64, + "maxLength": 64 + }, + "short_channel_id": { + "type": "short_channel_id", + "description": "the short_channel_id (if locked in)" + }, + "fee_base_msat": { + "type": "msat", + "description": "The resulting feebase (this is the BOLT #7 name)" + }, + "fee_proportional_millionths": { + "type": "u32", + "description": "The resulting feeppm (this is the BOLT #7 name)" + }, + "maximum_htlc_out_msat": { + "type": "msat", + "description": "The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat)" + } + } + } + } + } +} diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 38809d433a64..675b00b773b0 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2007,13 +2007,20 @@ static struct command_result *param_msat_u32(struct command *cmd, return NULL; } -static void set_channel_fees(struct command *cmd, struct channel *channel, - u32 base, u32 ppm, u32 delaysecs, - struct json_stream *response) -{ - /* We only need to defer values if we *increase* them; we always - * allow users to overpay fees. */ - if (base > channel->feerate_base || ppm > channel->feerate_ppm) { +static void set_channel_config(struct command *cmd, struct channel *channel, + u32 *base, + u32 *ppm, + struct amount_msat *htlc_max, + u32 delaysecs, + struct json_stream *response, + bool add_details) +{ + /* We only need to defer values if we *increase* fees (or drop + * max); we always allow users to overpay fees. */ + if ((base && *base > channel->feerate_base) + || (ppm && *ppm > channel->feerate_ppm) + || (htlc_max + && amount_msat_less(*htlc_max, channel->htlc_maximum_msat))) { channel->old_feerate_timeout = timeabs_add(time_now(), time_from_sec(delaysecs)); channel->old_feerate_base = channel->feerate_base; @@ -2022,14 +2029,18 @@ static void set_channel_fees(struct command *cmd, struct channel *channel, } /* set new values */ - channel->feerate_base = base; - channel->feerate_ppm = ppm; + if (base) + channel->feerate_base = *base; + if (ppm) + channel->feerate_ppm = *ppm; + if (htlc_max) + channel->htlc_maximum_msat = *htlc_max; /* tell channeld to make a send_channel_update */ if (channel->owner && streq(channel->owner->name, "channeld")) subd_send_msg(channel->owner, - take(towire_channeld_config_channel(NULL, &base, &ppm, - NULL))); + take(towire_channeld_config_channel(NULL, base, ppm, + htlc_max))); /* save values to database */ wallet_channel_save(cmd->ld->wallet, channel); @@ -2041,6 +2052,17 @@ static void set_channel_fees(struct command *cmd, struct channel *channel, type_to_string(tmpctx, struct channel_id, &channel->cid)); if (channel->scid) json_add_short_channel_id(response, "short_channel_id", channel->scid); + + /* setchannel lists these explicitly */ + if (add_details) { + json_add_amount_msat_only(response, "fee_base_msat", + amount_msat(channel->feerate_base)); + json_add_u32(response, "fee_proportional_millionths", + channel->feerate_ppm); + json_add_amount_msat_only(response, + "maximum_htlc_out_msat", + channel->htlc_maximum_msat); + } json_object_end(response); } @@ -2088,14 +2110,14 @@ static struct command_result *json_setchannelfee(struct command *cmd, channel->state != CHANNELD_AWAITING_LOCKIN && channel->state != DUALOPEND_AWAITING_LOCKIN) continue; - set_channel_fees(cmd, channel, *base, *ppm, *delaysecs, - response); + set_channel_config(cmd, channel, base, ppm, NULL, + *delaysecs, response, false); } /* single channel should be updated */ } else { - set_channel_fees(cmd, channel, *base, *ppm, *delaysecs, - response); + set_channel_config(cmd, channel, base, ppm, NULL, + *delaysecs, response, false); } /* Close and return response */ @@ -2117,6 +2139,75 @@ static const struct json_command setchannelfee_command = { }; AUTODATA(json_command, &setchannelfee_command); +static struct command_result *json_setchannel(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + struct peer *peer; + struct channel *channel; + u32 *base, *ppm, *delaysecs; + struct amount_msat *htlc_max; + + /* Parse the JSON command */ + if (!param(cmd, buffer, params, + p_req("id", param_channel_or_all, &channel), + p_opt("feebase", param_msat_u32, &base), + p_opt("feeppm", param_number, &ppm), + p_opt("htlcmax", param_msat, &htlc_max), + p_opt_def("enforcedelay", param_number, &delaysecs, 600), + NULL)) + return command_param_failed(); + + if (channel + && channel->state != CHANNELD_NORMAL + && channel->state != CHANNELD_AWAITING_LOCKIN + && channel->state != DUALOPEND_AWAITING_LOCKIN) + return command_fail(cmd, LIGHTNINGD, + "Channel is in state %s", channel_state_name(channel)); + + /* Open JSON response object for later iteration */ + response = json_stream_success(cmd); + json_array_start(response, "channels"); + + /* If the users requested 'all' channels we need to iterate */ + if (channel == NULL) { + list_for_each(&cmd->ld->peers, peer, list) { + channel = peer_active_channel(peer); + if (!channel) + continue; + if (channel->state != CHANNELD_NORMAL && + channel->state != CHANNELD_AWAITING_LOCKIN && + channel->state != DUALOPEND_AWAITING_LOCKIN) + continue; + set_channel_config(cmd, channel, base, ppm, htlc_max, + *delaysecs, response, true); + } + + /* single channel should be updated */ + } else { + set_channel_config(cmd, channel, base, ppm, htlc_max, + *delaysecs, response, true); + } + + /* Close and return response */ + json_array_end(response); + return command_success(cmd, response); +} + +static const struct json_command setchannel_command = { + "setchannel", + "channels", + json_setchannel, + "Sets fees and/or htlc_max for channel with {id} " + "(either peer ID, channel ID, short channel ID or 'all'). " + "If {feebase}, {feeppm} or {htlcmax} is missing, it is unchanged." + "{base} can also be defined in other units, for example '1sat'. " + "If {id} is 'all', the fees will be applied for all channels. " +}; +AUTODATA(json_command, &setchannel_command); + #if DEVELOPER static struct command_result *json_sign_last_tx(struct command *cmd, const char *buffer, From 42f91ff2fae8275d3d6bd9deefba981de2eef88e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:28 +1030 Subject: [PATCH 0482/1530] lightningd: deprecate setchannelfee, use setchannel in tests. Signed-off-by: Rusty Russell Changelog-Deprecated: JSON-RPC: `setchannelfee` (use `setchannel`). --- doc/lightning-listpeers.7.md | 2 +- doc/lightning-setchannelfee.7.md | 5 +- doc/lightningd-config.5.md | 6 +- lightningd/peer_control.c | 3 +- tests/test_gossip.py | 8 +- tests/test_pay.py | 231 +++++++++++++++++-------------- 6 files changed, 137 insertions(+), 118 deletions(-) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 569f53e58ccb..9835e0e25f1c 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -370,7 +370,7 @@ SEE ALSO -------- lightning-connect(7), lightning-fundchannel\_start(7), -lightning-setchannelfee(7) +lightning-setchannel(7) RESOURCES --------- diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index 7f94d4872725..b0f97ca3e18e 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -4,7 +4,7 @@ lightning-setchannelfee -- Command for setting specific routing fees on a lightn SYNOPSIS -------- -**setchannelfee** *id* [*base*] [*ppm*] [*enforcedelay*] +(DEPRECATED) **setchannelfee** *id* [*base*] [*ppm*] [*enforcedelay*] DESCRIPTION ----------- @@ -73,8 +73,7 @@ responsible for the c-lightning project. SEE ALSO -------- -lightningd-config(5), lightning-fundchannel(7), -lightning-listchannels(7), lightning-listpeers(7) +lightningd-setchannel(7) RESOURCES --------- diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index bc45c789a06b..9fe83e46fb05 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -236,14 +236,14 @@ Default: 1000. The base fee to charge for every payment which passes through. Note that millisatoshis are a very, very small unit! Changing this value will only affect new channels and not existing ones. If you want to change fees for existing channels, use the RPC call -lightning-setchannelfee(7). +lightning-setchannel(7). **fee-per-satoshi**=*MILLIONTHS* Default: 10 (0.001%). This is the proportional fee to charge for every payment which passes through. As percentages are too coarse, it's in millionths, so 10000 is 1%, 1000 is 0.1%. Changing this value will only affect new channels and not existing ones. If you want to change fees -for existing channels, use the RPC call lightning-setchannelfee(7). +for existing channels, use the RPC call lightning-setchannel(7). **min-capacity-sat**=*SATOSHI* Default: 10000. This value defines the minimal effective channel @@ -563,7 +563,7 @@ actually implementing these options. SEE ALSO -------- -lightning-listconfigs(7) lightning-setchannelfee(7) lightningd(8) +lightning-listconfigs(7) lightning-setchannel(7) lightningd(8) lightning-hsmtool(8) RESOURCES diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 675b00b773b0..f25dc0006343 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2135,7 +2135,8 @@ static const struct json_command setchannelfee_command = { "and a {ppm} (proportional per millionth) value. " "If values for {base} or {ppm} are left out, defaults will be used. " "{base} can also be defined in other units, for example '1sat'. " - "If {id} is 'all', the fees will be applied for all channels. " + "If {id} is 'all', the fees will be applied for all channels. ", + true /* deprecated */ }; AUTODATA(json_command, &setchannelfee_command); diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 4899f5971397..9e6b463ce3f3 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1583,7 +1583,7 @@ def setup_gossip_store_test(node_factory, bitcoind): wait_for(lambda: ['alias' in n for n in l2.rpc.listnodes()['nodes']] == [True, True]) # Now, replace the one channel_update, so it's past the node announcements. - l2.rpc.setchannelfee(l3.info['id'], 20, 1000) + l2.rpc.setchannel(l3.info['id'], 20, 1000) # Old base feerate is 1. wait_for(lambda: sum([c['base_fee_millisatoshi'] for c in l2.rpc.listchannels()['channels']]) == 21) @@ -1592,12 +1592,12 @@ def setup_gossip_store_test(node_factory, bitcoind): # Now insert channel_update for previous channel; now they're both past the # node announcements. - l3.rpc.setchannelfee(l2.info['id'], 20, 1000) + l3.rpc.setchannel(l2.info['id'], feebase=20, feeppm=1000) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l2.rpc.listchannels(scid23)['channels']] == [20, 20]) # Replace both (private) updates for scid12. - l1.rpc.setchannelfee(l2.info['id'], 20, 1000) - l2.rpc.setchannelfee(l1.info['id'], 20, 1000) + l1.rpc.setchannel(l2.info['id'], feebase=20, feeppm=1000) + l2.rpc.setchannel(l1.info['id'], feebase=20, feeppm=1000) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l2.rpc.listchannels(scid12)['channels']] == [20, 20]) # Records in store now looks (something) like: diff --git a/tests/test_pay.py b/tests/test_pay.py index ea450a8ebe68..1716f4d584a4 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1854,7 +1854,7 @@ def test_pay_routeboost(node_factory, bitcoind, compat): @pytest.mark.developer("updates are delayed without --dev-fast-gossip") -def test_setchannelfee_usage(node_factory, bitcoind): +def test_setchannel_usage(node_factory, bitcoind): # TEST SETUP # # [l1] ---> [l2] (channel funded) @@ -1862,7 +1862,7 @@ def test_setchannelfee_usage(node_factory, bitcoind): # o - - > [l3] (only connected) # # - check initial SQL values - # - check setchannelfee can be used + # - check setchannel can be used # - checks command's return object format # - check custom SQL fee values # - check values in local nodes listchannels output @@ -1872,77 +1872,99 @@ def test_setchannelfee_usage(node_factory, bitcoind): DEF_BASE = 10 DEF_BASE_MSAT = Millisatoshi(DEF_BASE) DEF_PPM = 100 + # Minus reserve + MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99)) l1, l2, l3 = node_factory.get_nodes(3, opts={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM}) node_factory.join_nodes([l1, l2]) l1.rpc.connect(l3.info['id'], 'localhost', l3.port) - def channel_get_fees(scid): + def channel_get_config(scid): return l1.db.query( - 'SELECT feerate_base, feerate_ppm FROM channels ' + 'SELECT feerate_base, feerate_ppm, htlc_maximum_msat FROM channels ' 'WHERE short_channel_id=\'{}\';'.format(scid)) # get short channel id scid = l1.get_channel_scid(l2) # feerates should be init with global config - db_fees = l1.db_query('SELECT feerate_base, feerate_ppm FROM channels;') + db_fees = l1.db_query('SELECT feerate_base, feerate_ppm, htlc_maximum_msat FROM channels;') assert(db_fees[0]['feerate_base'] == DEF_BASE) assert(db_fees[0]['feerate_ppm'] == DEF_PPM) + # This will be the capacity - reserves: + assert(db_fees[0]['htlc_maximum_msat'] == MAX_HTLC) # this is also what listpeers should return peers = l1.rpc.listpeers()['peers'] assert peers[0]['channels'][0]['fee_base_msat'] == DEF_BASE_MSAT assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM + assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == MAX_HTLC - # custom setchannelfee scid - result = l1.rpc.setchannelfee(scid, 1337, 137) + # custom setchannel scid + result = l1.rpc.setchannel(scid, 1337, 137, 133337) # check result format - assert(result['base'] == 1337) - assert(result['ppm'] == 137) assert(len(result['channels']) == 1) assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) assert(result['channels'][0]['peer_id'] == l2.info['id']) assert(result['channels'][0]['short_channel_id'] == scid) + assert(result['channels'][0]['fee_base_msat'] == 1337) + assert(result['channels'][0]['fee_proportional_millionths'] == 137) + assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337) # check if custom values made it into the database - db_fees = channel_get_fees(scid) + db_fees = channel_get_config(scid) assert(db_fees[0]['feerate_base'] == 1337) assert(db_fees[0]['feerate_ppm'] == 137) + assert(db_fees[0]['htlc_maximum_msat'] == 133337) # also check for updated values in `listpeers` peers = l1.rpc.listpeers()['peers'] assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(1337) assert peers[0]['channels'][0]['fee_proportional_millionths'] == 137 + assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == 133337 # wait for gossip and check if l1 sees new fees in listchannels wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, 1337]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, 137]) + wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [MAX_HTLC, 133337]) # also test with named and missing parameters - result = l1.rpc.setchannelfee(ppm=42, id=scid) - assert(result['base'] == DEF_BASE) - assert(result['ppm'] == 42) + result = l1.rpc.setchannel(feeppm=42, id=scid) assert(len(result['channels']) == 1) assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) assert(result['channels'][0]['short_channel_id'] == scid) - result = l1.rpc.setchannelfee(base=42, id=scid) - assert(result['base'] == 42) - assert(result['ppm'] == DEF_PPM) + assert(result['channels'][0]['fee_base_msat'] == 1337) + assert(result['channels'][0]['fee_proportional_millionths'] == 42) + assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337) + + result = l1.rpc.setchannel(feebase=43, id=scid) + assert(len(result['channels']) == 1) + assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) + assert(result['channels'][0]['short_channel_id'] == scid) + assert(result['channels'][0]['fee_base_msat'] == 43) + assert(result['channels'][0]['fee_proportional_millionths'] == 42) + assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337) + + result = l1.rpc.setchannel(htlcmax=43333, id=scid) assert(len(result['channels']) == 1) assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) assert(result['channels'][0]['short_channel_id'] == scid) + assert(result['channels'][0]['fee_base_msat'] == 43) + assert(result['channels'][0]['fee_proportional_millionths'] == 42) + assert(result['channels'][0]['maximum_htlc_out_msat'] == 43333) # check if negative fees raise error and DB keeps values # JSONRPC2_INVALID_PARAMS := -32602 with pytest.raises(RpcError, match=r'-32602'): - l1.rpc.setchannelfee(scid, -1, -1) + l1.rpc.setchannel(scid, -1, -1) # test if zero fees is possible - result = l1.rpc.setchannelfee(scid, 0, 0) - assert(result['base'] == 0) - assert(result['ppm'] == 0) - db_fees = channel_get_fees(scid) + result = l1.rpc.setchannel(scid, 0, 0) + assert(result['channels'][0]['short_channel_id'] == scid) + assert(result['channels'][0]['fee_base_msat'] == 0) + assert(result['channels'][0]['fee_proportional_millionths'] == 0) + + db_fees = channel_get_config(scid) assert(db_fees[0]['feerate_base'] == 0) assert(db_fees[0]['feerate_ppm'] == 0) # also check for updated values in `listpeers` @@ -1950,53 +1972,44 @@ def channel_get_fees(scid): assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(0) assert peers[0]['channels'][0]['fee_proportional_millionths'] == 0 - # disable and check for global values to be returned - result = l1.rpc.setchannelfee(scid) - assert(result['base'] == DEF_BASE) - assert(result['ppm'] == DEF_PPM) - # check default values in DB - db_fees = channel_get_fees(scid) - assert(db_fees[0]['feerate_base'] == DEF_BASE) - assert(db_fees[0]['feerate_ppm'] == DEF_PPM) - # also check for updated values in `listpeers` - peers = l1.rpc.listpeers()['peers'] - assert peers[0]['channels'][0]['fee_base_msat'] == DEF_BASE_MSAT - assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM - # check also peer id can be used - result = l1.rpc.setchannelfee(l2.info['id'], 42, 43) - assert(result['base'] == 42) - assert(result['ppm'] == 43) + result = l1.rpc.setchannel(l2.info['id'], 142, 143) assert(len(result['channels']) == 1) assert(result['channels'][0]['peer_id'] == l2.info['id']) assert(result['channels'][0]['short_channel_id'] == scid) - db_fees = channel_get_fees(scid) - assert(db_fees[0]['feerate_base'] == 42) - assert(db_fees[0]['feerate_ppm'] == 43) + assert(result['channels'][0]['fee_base_msat'] == 142) + assert(result['channels'][0]['fee_proportional_millionths'] == 143) + + db_fees = channel_get_config(scid) + assert(db_fees[0]['feerate_base'] == 142) + assert(db_fees[0]['feerate_ppm'] == 143) # check if invalid scid raises proper error with pytest.raises(RpcError, match=r'-1.*Could not find active channel of peer with that id'): - result = l1.rpc.setchannelfee(l3.info['id'], 42, 43) + result = l1.rpc.setchannel(l3.info['id'], 42, 43) with pytest.raises(RpcError, match=r'-32602.*id: should be a channel ID or short channel ID: invalid token'): - result = l1.rpc.setchannelfee('f42' + scid[3:], 42, 43) + result = l1.rpc.setchannel('f42' + scid[3:], 42, 43) # check if 'base' unit can be modified to satoshi - result = l1.rpc.setchannelfee(scid, '1sat') - assert(result['base'] == 1000) - db_fees = channel_get_fees(scid) + result = l1.rpc.setchannel(scid, '1sat') + assert(len(result['channels']) == 1) + assert(result['channels'][0]['peer_id'] == l2.info['id']) + assert(result['channels'][0]['short_channel_id'] == scid) + assert(result['channels'][0]['fee_base_msat'] == 1000) + db_fees = channel_get_config(scid) assert(db_fees[0]['feerate_base'] == 1000) # check if 'ppm' values greater than u32_max fail with pytest.raises(RpcError, match=r'-32602.*ppm: should be an integer: invalid token'): - l1.rpc.setchannelfee(scid, 0, 2**32) + l1.rpc.setchannel(scid, 0, 2**32) # check if 'base' values greater than u32_max fail with pytest.raises(RpcError, match=r'-32602.*base: exceeds u32 max: invalid token'): - l1.rpc.setchannelfee(scid, 2**32) + l1.rpc.setchannel(scid, 2**32) @pytest.mark.developer("gossip without DEVELOPER=1 is slow") -def test_setchannelfee_state(node_factory, bitcoind): +def test_setchannel_state(node_factory, bitcoind): # TEST SETUP # # [l0] --> [l1] --> [l2] @@ -2020,7 +2033,7 @@ def test_setchannelfee_state(node_factory, bitcoind): # try setting the fee in state AWAITING_LOCKIN should be possible # assert(l1.channel_state(l2) == "CHANNELD_AWAITING_LOCKIN") - result = l1.rpc.setchannelfee(l2.info['id'], 42, 0) + result = l1.rpc.setchannel(l2.info['id'], 42, 0) assert(result['channels'][0]['peer_id'] == l2.info['id']) # cid = result['channels'][0]['channel_id'] @@ -2028,7 +2041,7 @@ def test_setchannelfee_state(node_factory, bitcoind): mine_funding_to_announce(bitcoind, [l0, l1, l2]) l0.wait_for_route(l2) - inv = l2.rpc.invoice(100000, 'test_setchannelfee_state', 'desc')['bolt11'] + inv = l2.rpc.invoice(100000, 'test_setchannel_state', 'desc')['bolt11'] result = l0.rpc.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' assert result['msatoshi_sent'] == 100042 @@ -2044,15 +2057,15 @@ def test_setchannelfee_state(node_factory, bitcoind): bitcoind.generate_block(1) # assert l1.channel_state(l2) == "FUNDING_SPEND_SEEN" - # Try to setchannelfee in order to raise expected error. + # Try to setchannel in order to raise expected error. # To reduce false positive flakes, only test if state is not NORMAL anymore. with pytest.raises(RpcError, match=r'-1.*'): - # l1.rpc.setchannelfee(l2.info['id'], 10, 1) - l1.rpc.setchannelfee(l2.info['id'], 10, 1) + # l1.rpc.setchannel(l2.info['id'], 10, 1) + l1.rpc.setchannel(l2.info['id'], 10, 1) @pytest.mark.developer("gossip without DEVELOPER=1 is slow") -def test_setchannelfee_routing(node_factory, bitcoind): +def test_setchannel_routing(node_factory, bitcoind): # TEST SETUP # # [l1] <--default_fees--> [l2] <--specific_fees--> [l3] @@ -2062,8 +2075,10 @@ def test_setchannelfee_routing(node_factory, bitcoind): # - payment can be done using specific fees # - channel specific fees can be disabled again # - payment can be done using global fees + # - htlc max is honored DEF_BASE = 1 DEF_PPM = 10 + MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99)) l1, l2, l3 = node_factory.line_graph( 3, announce_channels=True, wait_for_announce=True, @@ -2073,11 +2088,12 @@ def test_setchannelfee_routing(node_factory, bitcoind): scid = l2.get_channel_scid(l3) # TEST CUSTOM VALUES - l2.rpc.setchannelfee(scid, 1337, 137) + l2.rpc.setchannel(scid, 1337, 137, 4000000, enforcedelay=0) # wait for l1 to see updated channel via gossip wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [1337, DEF_BASE]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [137, DEF_PPM]) + wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [4000000, MAX_HTLC]) # test fees are applied to HTLC forwards # @@ -2085,50 +2101,48 @@ def test_setchannelfee_routing(node_factory, bitcoind): # If l1 were to send 4,999,999 millisatoshi to l3 via l2, it needs to # pay l2 the fee it specified in the l2->l3 `channel_update`, calculated as # per [HTLC Fees](#htlc_fees): base + amt * pm / 10**6 - # - # 1337 + 4999999 * 137 / 1000000 = 2021.999 (2021) - route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"] - assert len(route) == 2 - assert route[0]['msatoshi'] == 5002020 - assert route[1]['msatoshi'] == 4999999 - - # In case l3 includes a routehint, we need to make sure they also know - # about the new fees, otherwise we may end up with the old feerate - wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, True), (DEF_BASE, DEF_PPM, True)]) - - # do and check actual payment - inv = l3.rpc.invoice(4999999, 'test_setchannelfee_1', 'desc')['bolt11'] - result = l1.rpc.dev_pay(inv, use_shadow=False) - assert result['status'] == 'complete' - assert result['msatoshi_sent'] == 5002020 - - # TEST DISABLE and check global fee routing - l2.rpc.setchannelfee(scid) - # wait for l1 to see default values again via gossip - wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, DEF_BASE]) - wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, DEF_PPM]) - - # test if global fees are applied again (base 1 ppm 10) - # 1 + 4999999 * 10 / 1000000 = 50.999 (50) - route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"] - assert len(route) == 2 - assert route[0]['msatoshi'] == 5000049 - assert route[1]['msatoshi'] == 4999999 + # FIXME! +# # Should refuse to route this! +# with pytest.raises(RpcError, match=r'Could not find a route'): +# l1.rpc.getroute(l3.info['id'], 4000001, 1, fuzzpercent=0)["route"] + + # 1337 + 4000000 * 137 / 1000000 = 1885 + route_ok = l1.rpc.getroute(l3.info['id'], 4000000, 1)["route"] + assert len(route_ok) == 2 + assert route_ok[0]['msatoshi'] == 4001885 + assert route_ok[1]['msatoshi'] == 4000000 + + # Make variant that tries to pay more than allowed htlc! + route_bad = copy.deepcopy(route_ok) + route_bad[0]['msatoshi'] = 4001887 + route_bad[1]['msatoshi'] = 4000001 + route_bad[0]['amount_msat'] = Millisatoshi(4001887) + route_bad[1]['amount_msat'] = Millisatoshi(4000001) + assert route_bad != route_ok # In case l3 includes a routehint, we need to make sure they also know # about the new fees, otherwise we may end up with the old feerate - wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(DEF_BASE, DEF_PPM, True), (DEF_BASE, DEF_PPM, True)]) + wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, 4000000, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)]) # do and check actual payment - inv = l3.rpc.invoice(4999999, 'test_setchannelfee_2', 'desc')['bolt11'] - result = l1.rpc.dev_pay(inv, use_shadow=False) - assert result['status'] == 'complete' - assert result['msatoshi_sent'] == 5000049 + inv = l3.rpc.invoice(4000000, 'test_setchannel_1', 'desc') + # Check that routehint from l3 incorporated new feerate! + decoded = l1.rpc.decodepay(inv['bolt11']) + assert decoded['routes'] == [[{'pubkey': l2.info['id'], 'short_channel_id': scid, 'fee_base_msat': 1337, 'fee_proportional_millionths': 137, 'cltv_expiry_delta': 6}]] + + # This will fail. + l1.rpc.sendpay(route_bad, inv['payment_hash'], payment_secret=inv['payment_secret']) + with pytest.raises(RpcError, match='WIRE_TEMPORARY_CHANNEL_FAILURE'): + l1.rpc.waitsendpay(inv['payment_hash']) + + # This will succeed + l1.rpc.sendpay(route_ok, inv['payment_hash'], payment_secret=inv['payment_secret']) + l1.rpc.waitsendpay(inv['payment_hash']) @pytest.mark.developer("gossip without DEVELOPER=1 is slow") -def test_setchannelfee_zero(node_factory, bitcoind): +def test_setchannel_zero(node_factory, bitcoind): # TEST SETUP # # [l1] <--default_fees--> [l2] <--specific_fees--> [l3] @@ -2147,7 +2161,7 @@ def test_setchannelfee_zero(node_factory, bitcoind): scid = l2.get_channel_scid(l3) # TEST ZERO fees possible - l2.rpc.setchannelfee(scid, 0, 0) + l2.rpc.setchannel(scid, 0, 0) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [0, DEF_BASE]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [0, DEF_PPM]) @@ -2162,14 +2176,14 @@ def test_setchannelfee_zero(node_factory, bitcoind): wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(0, 0, True), (DEF_BASE, DEF_PPM, True)]) # do and check actual payment - inv = l3.rpc.invoice(4999999, 'test_setchannelfee_3', 'desc')['bolt11'] + inv = l3.rpc.invoice(4999999, 'test_setchannel_3', 'desc')['bolt11'] result = l1.rpc.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' assert result['msatoshi_sent'] == 4999999 @pytest.mark.developer("gossip without DEVELOPER=1 is slow") -def test_setchannelfee_restart(node_factory, bitcoind): +def test_setchannel_restart(node_factory, bitcoind): # TEST SETUP # # [l1] <--default_fees--> [l2] <--specific_fees--> [l3] @@ -2180,6 +2194,7 @@ def test_setchannelfee_restart(node_factory, bitcoind): # - l1 routing can be made to l3 and global (1 10) fees are applied DEF_BASE = 1 DEF_PPM = 10 + MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99)) OPTS = {'may_reconnect': True, 'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM} l1, l2, l3 = node_factory.line_graph(3, announce_channels=True, wait_for_announce=True, opts=OPTS) @@ -2189,7 +2204,7 @@ def test_setchannelfee_restart(node_factory, bitcoind): scid23 = l2.get_channel_scid(l3) # l2 set custom fees - l2.rpc.setchannelfee(scid23, 1337, 137) + l2.rpc.setchannel(scid23, 1337, 137, 500001) # restart l2 and reconnect l2.restart() @@ -2200,22 +2215,22 @@ def test_setchannelfee_restart(node_factory, bitcoind): wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid12)['channels']] == [True, True]) # l1 wait for channel update from l2 - wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, True), (DEF_BASE, DEF_PPM, True)]) + wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)]) # In case l3 includes a routehint, we need to make sure they also know # about the new fees, otherwise we may end up with the old feerate - wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, True), (DEF_BASE, DEF_PPM, True)]) + wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)]) # l1 can make payment to l3 with custom fees being applied - # Note: BOLT #7 math works out to 2021 msat fees - inv = l3.rpc.invoice(4999999, 'test_setchannelfee_1', 'desc')['bolt11'] + # Note: BOLT #7 math works out to 1405 msat fees + inv = l3.rpc.invoice(499999, 'test_setchannel_1', 'desc')['bolt11'] result = l1.rpc.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' - assert result['msatoshi_sent'] == 5002020 + assert result['msatoshi_sent'] == 501404 @pytest.mark.developer("updates are delayed without --dev-fast-gossip") -def test_setchannelfee_all(node_factory, bitcoind): +def test_setchannel_all(node_factory, bitcoind): # TEST SETUP # # [l1]----> [l2] @@ -2235,7 +2250,7 @@ def test_setchannelfee_all(node_factory, bitcoind): scid3 = l1.get_channel_scid(l3) # now try to set all (two) channels using wildcard syntax - result = l1.rpc.setchannelfee("all", 0xDEAD, 0xBEEF) + result = l1.rpc.setchannel("all", 0xDEAD, 0xBEEF, 0xCAFE) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_BASE, 0xDEAD]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_PPM, 0xBEEF]) @@ -2243,12 +2258,16 @@ def test_setchannelfee_all(node_factory, bitcoind): wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid3)['channels']] == [0xBEEF, DEF_PPM]) assert len(result['channels']) == 2 - assert result['base'] == 0xDEAD - assert result['ppm'] == 0xBEEF assert result['channels'][0]['peer_id'] == l2.info['id'] assert result['channels'][0]['short_channel_id'] == scid2 + assert result['channels'][0]['fee_base_msat'] == 0xDEAD + assert result['channels'][0]['fee_proportional_millionths'] == 0xBEEF + assert result['channels'][0]['maximum_htlc_out_msat'] == 0xCAFE assert result['channels'][1]['peer_id'] == l3.info['id'] assert result['channels'][1]['short_channel_id'] == scid3 + assert result['channels'][1]['fee_base_msat'] == 0xDEAD + assert result['channels'][1]['fee_proportional_millionths'] == 0xBEEF + assert result['channels'][1]['maximum_htlc_out_msat'] == 0xCAFE @pytest.mark.developer("gossip without DEVELOPER=1 is slow") @@ -4898,7 +4917,7 @@ def test_pay_low_max_htlcs(node_factory): ) -def test_setchannelfee_enforcement_delay(node_factory, bitcoind): +def test_setchannel_enforcement_delay(node_factory, bitcoind): # Fees start at 1msat + 1% l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, opts={'fee-base': 1, @@ -4924,7 +4943,7 @@ def test_setchannelfee_enforcement_delay(node_factory, bitcoind): l1.rpc.waitsendpay(inv['payment_hash']) # Increase fee immediately; l1 payment rejected. - l2.rpc.setchannelfee("all", 2, 10000, 0) + l2.rpc.setchannel("all", 2, 10000, enforcedelay=0) inv = l3.rpc.invoice(1000, "test2", "test2") l1.rpc.sendpay(route, @@ -4942,7 +4961,7 @@ def test_setchannelfee_enforcement_delay(node_factory, bitcoind): l1.rpc.waitsendpay(inv['payment_hash']) # Now, give us 30 seconds please. - l2.rpc.setchannelfee("all", 3, 10000, 30) + l2.rpc.setchannel("all", 3, 10000, enforcedelay=30) inv = l3.rpc.invoice(1000, "test4", "test4") l1.rpc.sendpay(route, payment_hash=inv['payment_hash'], From 35cca73d2fe67a8ca2914fa56d428b570aff3189 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:28 +1030 Subject: [PATCH 0483/1530] devtools/fp16: conversion tool for fp16 format. We use this in gossmap to represent htlc max/min approximations. Signed-off-by: Rusty Russell --- devtools/Makefile | 4 +++- devtools/fp16.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 devtools/fp16.c diff --git a/devtools/Makefile b/devtools/Makefile index 90dd7c7b45a5..181c1eae5acc 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -1,4 +1,4 @@ -DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/blindedpath devtools/bolt12-cli devtools/encodeaddr devtools/features +DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/blindedpath devtools/bolt12-cli devtools/encodeaddr devtools/features devtools/fp16 ifeq ($(HAVE_SQLITE3),1) DEVTOOLS += devtools/checkchannels endif @@ -49,6 +49,8 @@ DEVTOOLS_COMMON_OBJS := \ devtools/features: $(CCAN_OBJS) common/features.o common/utils.o wire/fromwire.o wire/towire.o devtools/features.o +devtools/fp16: $(CCAN_OBJS) common/fp16.o common/utils.o common/setup.o common/autodata.o devtools/fp16.o + devtools/bolt11-cli: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bolt11-cli.o devtools/encodeaddr: common/utils.o common/bech32.o devtools/encodeaddr.o diff --git a/devtools/fp16.c b/devtools/fp16.c new file mode 100644 index 000000000000..4f541e863d2e --- /dev/null +++ b/devtools/fp16.c @@ -0,0 +1,45 @@ +/* Simple tool to convert to/from our internal fp16 representation */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + fp16_t fp16val; + u64 u64min, u64max; + char *endp; + + common_setup(argv[0]); + + opt_register_noarg("-h|--help", opt_usage_and_exit, + "[fp16:val|value]\n" + "Converts to/from fp16, indicates ranges", + "Get usage information"); + opt_parse(&argc, argv, opt_log_stderr_exit); + if (argc != 2) + opt_usage_exit_fail("Expect 1 argument"); + + if (strstarts(argv[1], "fp16:")) + fp16val = strtoul(argv[1] + 5, &endp, 0); + else + fp16val = u64_to_fp16(strtoul(argv[1], &endp, 0), false); + + if (*endp) + opt_usage_exit_fail("Expect valid number"); + + u64min = u64max = fp16_to_u64(fp16val); + while (u64_to_fp16(u64min-1, false) == fp16val) + u64min--; + while (u64_to_fp16(u64max+1, false) == fp16val) + u64max++; + printf("fp16:0x%x\n" + "min %"PRIu64"\n" + "max %"PRIu64"\n", + fp16val, u64min, u64max); + common_shutdown(); +} From 1751b1becc7015d527ef6728b769d5d17b26948f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:28 +1030 Subject: [PATCH 0484/1530] pytest: add checks that pay and getroute respect htlc_maximum_msat. We need to add some, since our internal representations of htlc_maximum_msat round up, and we need to disable mpp which succeeds in getting a payment through by splitting. We also allow dev_routes to suppress invoice routehints altogether. Signed-off-by: Rusty Russell --- lightningd/invoice.c | 2 +- tests/test_pay.py | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 45cc4922a813..9e205d9de9e4 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -708,7 +708,7 @@ add_routehints(struct invoice_info *info, struct amount_msat total, needed; /* Dev code can force routes. */ - if (tal_count(info->b11->routes) != 0) { + if (info->b11->routes) { *warning_mpp = *warning_capacity = *warning_deadends = *warning_offline = *warning_private_unused = false; diff --git a/tests/test_pay.py b/tests/test_pay.py index 1716f4d584a4..25c03eb522a2 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2082,7 +2082,8 @@ def test_setchannel_routing(node_factory, bitcoind): l1, l2, l3 = node_factory.line_graph( 3, announce_channels=True, wait_for_announce=True, - opts={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM}) + opts={'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM, + 'disable-mpp': None}) # get short channel id for 2->3 scid = l2.get_channel_scid(l3) @@ -2102,10 +2103,22 @@ def test_setchannel_routing(node_factory, bitcoind): # pay l2 the fee it specified in the l2->l3 `channel_update`, calculated as # per [HTLC Fees](#htlc_fees): base + amt * pm / 10**6 - # FIXME! -# # Should refuse to route this! -# with pytest.raises(RpcError, match=r'Could not find a route'): -# l1.rpc.getroute(l3.info['id'], 4000001, 1, fuzzpercent=0)["route"] + # Note: we use fp16 internally for channel max, so we overestimate: + # from devtools/fp16 4000000: fp16:5fa1 min 3999744 max 4001791 + # Since it rounds up, it will use 4001792 as max capacity. + + # Should refuse to route this! + with pytest.raises(RpcError, match=r'Could not find a route'): + l1.rpc.getroute(l3.info['id'], 4001793, 1, fuzzpercent=0)["route"] + + # We should consider this unroutable! (MPP is disabled!) + inv = l3.rpc.call('invoice', {'msatoshi': 4001793, + 'label': 'test_setchannel_1', + 'description': 'desc', + 'dev-routes': []}) + with pytest.raises(RpcError) as routefail: + l1.rpc.dev_pay(inv['bolt11'], use_shadow=False) + assert routefail.value.error['attempts'][0]['failreason'] == 'No path found' # 1337 + 4000000 * 137 / 1000000 = 1885 route_ok = l1.rpc.getroute(l3.info['id'], 4000000, 1)["route"] @@ -2126,7 +2139,7 @@ def test_setchannel_routing(node_factory, bitcoind): wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, 4000000, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)]) # do and check actual payment - inv = l3.rpc.invoice(4000000, 'test_setchannel_1', 'desc') + inv = l3.rpc.invoice(4000000, 'test_setchannel_2', 'desc') # Check that routehint from l3 incorporated new feerate! decoded = l1.rpc.decodepay(inv['bolt11']) assert decoded['routes'] == [[{'pubkey': l2.info['id'], 'short_channel_id': scid, 'fee_base_msat': 1337, 'fee_proportional_millionths': 137, 'cltv_expiry_delta': 6}]] From f29890ed66e3ac112e7ac3312268607c03e3279d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:28 +1030 Subject: [PATCH 0485/1530] lightningd: check htlc_maximum_msat of channels for routehints. We still use the channel hint here (as it's the only option), we just warn about lack of capacity. Signed-off-by: Rusty Russell --- lightningd/routehint.c | 10 +++++++++- plugins/topology.c | 3 +++ tests/test_pay.py | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lightningd/routehint.c b/lightningd/routehint.c index 4d4ebd6adb0b..b5f37776aaec 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -61,7 +61,7 @@ routehint_candidates(const tal_t *ctx, struct amount_msat capacity; const char *err; struct routehint_candidate candidate; - struct amount_msat fee_base; + struct amount_msat fee_base, htlc_max; struct route_info *r; struct peer *peer; bool is_public; @@ -74,6 +74,7 @@ routehint_candidates(const tal_t *ctx, ",fee_base_msat:%" ",fee_proportional_millionths:%" ",cltv_expiry_delta:%" + ",htlc_max_msat:%" ",incoming_capacity_msat:%" "}", JSON_SCAN(json_to_node_id, &r->pubkey), @@ -83,6 +84,7 @@ routehint_candidates(const tal_t *ctx, JSON_SCAN(json_to_u32, &r->fee_proportional_millionths), JSON_SCAN(json_to_u16, &r->cltv_expiry_delta), + JSON_SCAN(json_to_msat, &htlc_max), JSON_SCAN(json_to_msat, &capacity)); if (err) { @@ -112,7 +114,13 @@ routehint_candidates(const tal_t *ctx, continue; } + /* If they set an htlc_maximum_msat, consider that the + * capacity ceiling. We *could* do multiple HTLCs, + * but presumably that would defeat the spirit of the + * limit anyway */ candidate.capacity = channel_amount_receivable(candidate.c); + if (amount_msat_greater(candidate.capacity, htlc_max)) + candidate.capacity = htlc_max; /* Now we can tell if it's public. If so (even if it's otherwise * unusable), we *don't* expose private channels! */ diff --git a/plugins/topology.c b/plugins/topology.c index 12be328e57b1..b6f733704fcc 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -607,6 +607,9 @@ static struct command_result *json_listincoming(struct command *cmd, json_add_amount_msat_only(js, "fee_base_msat", amount_msat(ourchan->half[!dir] .base_fee)); + json_add_amount_msat_only(js, "htlc_max_msat", + amount_msat(fp16_to_u64(ourchan->half[!dir] + .htlc_max))); json_add_u32(js, "fee_proportional_millionths", ourchan->half[!dir].proportional_fee); json_add_u32(js, "cltv_expiry_delta", ourchan->half[!dir].delay); diff --git a/tests/test_pay.py b/tests/test_pay.py index 25c03eb522a2..0e532248d79a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2153,6 +2153,12 @@ def test_setchannel_routing(node_factory, bitcoind): l1.rpc.sendpay(route_ok, inv['payment_hash'], payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(inv['payment_hash']) + # Check that this one warns about capacity! + inv = l3.rpc.call('invoice', {'msatoshi': 4001793, + 'label': 'test_setchannel_3', + 'description': 'desc'}) + assert 'warning_capacity' in inv + @pytest.mark.developer("gossip without DEVELOPER=1 is slow") def test_setchannel_zero(node_factory, bitcoind): From 999c734bb5be2073979d63bfbc35074effbc8ce3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:54 +1030 Subject: [PATCH 0486/1530] setchannel: add minhtlc Suggested by @m-schmook, I realized that if we append it later I'll never get it right: I expect parameters min and max, not max and min! Signed-off-by: Rusty Russell Changelog-Added: Protocol: you can now alter the `htlc_minimum_msat` and `htlc_maximum_msat` your node advertizes. --- channeld/channeld.c | 18 ++++-- channeld/channeld_wire.csv | 2 + contrib/pyln-client/pyln/client/lightning.py | 6 +- doc/lightning-listpeers.7.md | 3 +- doc/lightning-setchannel.7.md | 22 +++++-- doc/schemas/listpeers.schema.json | 8 +++ doc/schemas/setchannel.schema.json | 9 +++ lightningd/channel.c | 11 +++- lightningd/channel.h | 7 ++- lightningd/channel_control.c | 1 + lightningd/dual_open_control.c | 2 + lightningd/opening_control.c | 1 + lightningd/peer_control.c | 43 ++++++++++--- lightningd/peer_htlcs.c | 8 ++- lightningd/routehint.c | 2 + lightningd/test/run-invoice-select-inchan.c | 2 +- tests/test_pay.py | 65 +++++++++++++++++--- wallet/db.c | 1 + wallet/test/run-wallet.c | 3 +- wallet/wallet.c | 15 +++-- 20 files changed, 186 insertions(+), 43 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 262cb387f3be..d1b898754fe5 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -124,7 +124,9 @@ struct peer { * the channel_updates. */ u32 fee_base; u32 fee_per_satoshi; - struct amount_msat htlc_maximum_msat; + /* Note: the real min constraint is channel->config[REMOTE].htlc_minimum: + * they could kill the channel if we violate that! */ + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; /* The scriptpubkey to use for shutting down. */ u32 *final_index; @@ -361,7 +363,7 @@ static void send_channel_update(struct peer *peer, int disable_flag) disable_flag == ROUTING_FLAGS_DISABLED, peer->cltv_delta, - peer->channel->config[REMOTE].htlc_minimum, + peer->htlc_minimum_msat, peer->fee_base, peer->fee_per_satoshi, peer->htlc_maximum_msat); @@ -3429,10 +3431,13 @@ static void handle_blockheight(struct peer *peer, const u8 *inmsg) static void handle_config_channel(struct peer *peer, const u8 *inmsg) { u32 *base, *ppm; - struct amount_msat *htlc_max; + struct amount_msat *htlc_min, *htlc_max; bool changed; - if (!fromwire_channeld_config_channel(inmsg, inmsg, &base, &ppm, &htlc_max)) + if (!fromwire_channeld_config_channel(inmsg, inmsg, + &base, &ppm, + &htlc_min, + &htlc_max)) master_badmsg(WIRE_CHANNELD_CONFIG_CHANNEL, inmsg); /* only send channel updates if values actually changed */ @@ -3445,6 +3450,10 @@ static void handle_config_channel(struct peer *peer, const u8 *inmsg) peer->fee_per_satoshi = *ppm; changed = true; } + if (htlc_min && !amount_msat_eq(*htlc_min, peer->htlc_minimum_msat)) { + peer->htlc_minimum_msat = *htlc_min; + changed = true; + } if (htlc_max && !amount_msat_eq(*htlc_max, peer->htlc_maximum_msat)) { peer->htlc_maximum_msat = *htlc_max; changed = true; @@ -3755,6 +3764,7 @@ static void init_channel(struct peer *peer) &opener, &peer->fee_base, &peer->fee_per_satoshi, + &peer->htlc_minimum_msat, &peer->htlc_maximum_msat, &local_msat, &points[LOCAL], diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index df54a26b7f0d..7100e5c56249 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -35,6 +35,7 @@ msgdata,channeld_init,old_remote_per_commit,pubkey, msgdata,channeld_init,opener,enum side, msgdata,channeld_init,fee_base,u32, msgdata,channeld_init,fee_proportional,u32, +msgdata,channeld_init,htlc_minimum_msat,amount_msat, msgdata,channeld_init,htlc_maximum_msat,amount_msat, msgdata,channeld_init,local_msatoshi,amount_msat, msgdata,channeld_init,our_basepoints,basepoints, @@ -220,6 +221,7 @@ msgdata,channeld_fail_fallen_behind,remote_per_commitment_point,?pubkey, msgtype,channeld_config_channel,1029 msgdata,channeld_config_channel,feerate_base,?u32, msgdata,channeld_config_channel,feerate_ppm,?u32, +msgdata,channeld_config_channel,htlc_minimum,?amount_msat, msgdata,channeld_config_channel,htlc_maximum,?amount_msat, # When we receive announcement_signatures for channel announce diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 92185ec8a3f8..1c17b1c5a131 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -1176,7 +1176,7 @@ def setchannelfee(self, id, base=None, ppm=None, enforcedelay=None): } return self.call("setchannelfee", payload) - def setchannel(self, id, feebase=None, feeppm=None, htlcmax=None, enforcedelay=None): + def setchannel(self, id, feebase=None, feeppm=None, htlcmin=None, htlcmax=None, enforcedelay=None): """Set configuration a channel/peer {id} (or 'all'). {feebase} is a value in millisatoshi that is added as base fee @@ -1185,6 +1185,9 @@ def setchannel(self, id, feebase=None, feeppm=None, htlcmax=None, enforcedelay=N {feeppm} is a value added proportionally per-millionths to any routed payment volume in satoshi. + {htlcmin} is the minimum (outgoing) htlc amount to allow and + advertize. + {htlcmax} is the maximum (outgoing) htlc amount to allow and advertize. @@ -1196,6 +1199,7 @@ def setchannel(self, id, feebase=None, feeppm=None, htlcmax=None, enforcedelay=N "id": id, "feebase": feebase, "feeppm": feeppm, + "htlcmin": htlcmin, "htlcmax": htlcmax, "enforcedelay": enforcedelay, } diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 9835e0e25f1c..88905de2c3b5 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -87,6 +87,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **spendable_msat** (msat, optional): total we could send through channel - **receivable_msat** (msat, optional): total peer could send through channel - **minimum_htlc_in_msat** (msat, optional): the minimum amount HTLC we accept + - **minimum_htlc_out_msat** (msat, optional): the minimum amount HTLC we will send - **maximum_htlc_out_msat** (msat, optional): the maximum amount HTLC we will send - **their_to_self_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close - **our_to_self_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close @@ -379,4 +380,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:8e30caf48aed46acc7c053a355867dc8b8624035dba4ea7668d30d86b8d827cd) +[comment]: # ( SHA256STAMP:147b7008c8f4acb031df625e0731614339a75ee5861cb9f40cd542b1017e3660) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 658511a18188..f915b7968853 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -1,20 +1,24 @@ -lightning-setchannel -- Command for configuring fees / maximum htlc on a lightning channel +lightning-setchannel -- Command for configuring fees / htlc range advertized for a channel =========================================================================================== SYNOPSIS -------- -**setchannel** *id* [*feebase*] [*feeppm*] [*htlcmax*] [*enforcedelay*] +**setchannel** *id* [*feebase*] [*feeppm*] [*htlcmin*] [*htlcmax*] [*enforcedelay*] DESCRIPTION ----------- The **setchannel** RPC command sets channel specific routing fees, and -`htlc_maximum_msat` as defined in BOLT \#7. The channel has to be in +`htlc_minimum_msat` or `htlc_maximum_msat` as defined in BOLT \#7. The channel has to be in normal or awaiting state. This can be checked by **listpeers** reporting a *state* of CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN for the channel. +These changes (for a public channel) will be broadcast to the rest of +the network (though many nodes limit the rate of such changes they +will accept: we allow 2 a day, with a few extra occasionally). + *id* is required and should contain a scid (short channel ID), channel id or peerid (pubkey) of the channel to be modified. If *id* is set to "all", the updates are applied to all channels in states @@ -31,6 +35,13 @@ to any routed payment volume in satoshi. For example, if ppm is 1,000 and 1,000,000 satoshi is being routed through the channel, an proportional fee of 1,000 satoshi is added, resulting in a 0.1% fee. +*htlcmin* is an optional value that limits how small an HTLC we will +send: if omitted, it is unchanged (the default is no lower limit). It +can be a whole number, or a whole number ending in *msat* or *sat*, or +a number with three decimal places ending in *sat*, or a number with 1 +to 11 decimal places ending in *btc*. The peer also enforces a +minimum for the channel: setting it below will be ignored. + *htlcmax* is an optional value that limits how large an HTLC we will send: if omitted, it is unchanged (the default is no effective limit). It can be a whole number, or a whole number ending in *msat* @@ -55,8 +66,11 @@ On success, an object containing **channels** is returned. It is an array of ob - **channel_id** (hex): The channel_id of the channel (always 64 characters) - **fee_base_msat** (msat): The resulting feebase (this is the BOLT #7 name) - **fee_proportional_millionths** (u32): The resulting feeppm (this is the BOLT #7 name) +- **minimum_htlc_out_msat** (msat): The resulting htlcmin we will advertize (the BOLT #7 name is htlc_minimum_msat) - **maximum_htlc_out_msat** (msat): The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat) - **short_channel_id** (short_channel_id, optional): the short_channel_id (if locked in) +- the following warnings are possible: + - **warning_htlcmin_too_low**: The requested htlcmin was too low for this peer, so we set it to the minimum they will allow [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -86,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:25c6733af784e8a21a8eed4bcb0f12767ae49d16fe623187ae5313b5bb5cdd80) +[comment]: # ( SHA256STAMP:0f153e7dddce61bc921b3743472f11316c5984b9b1459cac1b201d6f51ec1be1) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index cada820f1fcc..f68d5580996c 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -418,6 +418,10 @@ "type": "msat", "description": "the minimum amount HTLC we accept" }, + "minimum_htlc_out_msat": { + "type": "msat", + "description": "the minimum amount HTLC we will send" + }, "maximum_htlc_out_msat": { "type": "msat", "description": "the maximum amount HTLC we will send" @@ -776,6 +780,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, @@ -864,6 +869,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, @@ -953,6 +959,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, @@ -1041,6 +1048,7 @@ "spendable_msat": {}, "receivable_msat": {}, "minimum_htlc_in_msat": {}, + "minimum_htlc_out_msat": {}, "maximum_htlc_out_msat": {}, "spendable_msatoshi": {}, "receivable_msatoshi": {}, diff --git a/doc/schemas/setchannel.schema.json b/doc/schemas/setchannel.schema.json index fcd896a51b33..512e4dbdfa80 100644 --- a/doc/schemas/setchannel.schema.json +++ b/doc/schemas/setchannel.schema.json @@ -17,6 +17,7 @@ "channel_id", "fee_base_msat", "fee_proportional_millionths", + "minimum_htlc_out_msat", "maximum_htlc_out_msat" ], "properties": { @@ -42,6 +43,14 @@ "type": "u32", "description": "The resulting feeppm (this is the BOLT #7 name)" }, + "minimum_htlc_out_msat": { + "type": "msat", + "description": "The resulting htlcmin we will advertize (the BOLT #7 name is htlc_minimum_msat)" + }, + "warning_htlcmin_too_low": { + "type": "string", + "description": "The requested htlcmin was too low for this peer, so we set it to the minimum they will allow" + }, "maximum_htlc_out_msat": { "type": "msat", "description": "The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat)" diff --git a/lightningd/channel.c b/lightningd/channel.c index 3bb5ad0272bf..a6555fc76cb3 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -410,10 +410,11 @@ struct channel *new_channel(struct peer *peer, u64 dbid, secp256k1_ecdsa_signature *lease_commit_sig STEALS, u32 lease_chan_max_msat, u16 lease_chan_max_ppt, + struct amount_msat htlc_minimum_msat, struct amount_msat htlc_maximum_msat) { struct channel *channel = tal(peer->ld, struct channel); - struct amount_msat htlc_max; + struct amount_msat htlc_min, htlc_max; assert(dbid != 0); channel->peer = peer; @@ -509,8 +510,12 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->blockheight_states = dup_height_states(channel, height_states); channel->channel_update = NULL; - /* DB migration, for example, sets this to bignum; correct - * here */ + /* DB migration, for example, sets min to 0, max to large: fixup */ + htlc_min = channel->channel_info.their_config.htlc_minimum; + if (amount_msat_greater(htlc_min, htlc_minimum_msat)) + channel->htlc_minimum_msat = htlc_min; + else + channel->htlc_minimum_msat = htlc_minimum_msat; htlc_max = htlc_max_possible_send(channel); if (amount_msat_less(htlc_max, htlc_maximum_msat)) channel->htlc_maximum_msat = htlc_max; diff --git a/lightningd/channel.h b/lightningd/channel.h index 5390ad74ae66..cd0113317251 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -196,8 +196,8 @@ struct channel { * peer via option_data_loss_protect? */ const struct pubkey *future_per_commitment_point; - /* Max htlc amount allowed in channel. */ - struct amount_msat htlc_maximum_msat; + /* Min/max htlc amount allowed in channel. */ + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; /* Feerate per channel */ u32 feerate_base, feerate_ppm; @@ -205,7 +205,7 @@ struct channel { /* But allow these feerates/htlcs up until this time. */ struct timeabs old_feerate_timeout; u32 old_feerate_base, old_feerate_ppm; - struct amount_msat old_htlc_maximum_msat; + struct amount_msat old_htlc_minimum_msat, old_htlc_maximum_msat; /* If they used option_upfront_shutdown_script. */ const u8 *remote_upfront_shutdown_script; @@ -315,6 +315,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, secp256k1_ecdsa_signature *lease_commit_sig STEALS, u32 lease_chan_max_msat, u16 lease_chan_max_ppt, + struct amount_msat htlc_minimum_msat, struct amount_msat htlc_maximum_msat); /* new_inflight - Create a new channel_inflight for a channel */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 2b21d524190a..53b433967ed8 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -702,6 +702,7 @@ void peer_start_channeld(struct channel *channel, channel->opener, channel->feerate_base, channel->feerate_ppm, + channel->htlc_minimum_msat, channel->htlc_maximum_msat, channel->our_msat, &channel->local_basepoints, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index d2fa86f4adc6..f10fffe9b6b3 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1110,6 +1110,7 @@ wallet_update_channel(struct lightningd *ld, channel->msat_to_us_min = our_msat; channel->msat_to_us_max = our_msat; channel->lease_expiry = lease_expiry; + channel->htlc_minimum_msat = channel->channel_info.their_config.htlc_minimum; channel->htlc_maximum_msat = htlc_max_possible_send(channel); tal_free(channel->lease_commit_sig); @@ -1253,6 +1254,7 @@ wallet_commit_channel(struct lightningd *ld, channel->lease_chan_max_msat = lease_chan_max_msat; channel->lease_chan_max_ppt = lease_chan_max_ppt; + channel->htlc_minimum_msat = channel_info->their_config.htlc_minimum; channel->htlc_maximum_msat = htlc_max_possible_send(channel); /* Now we finally put it in the database. */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index be576e75a268..ed23a5ae0307 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -210,6 +210,7 @@ wallet_commit_channel(struct lightningd *ld, take(new_height_states(NULL, uc->fc ? LOCAL : REMOTE, &lease_start_blockheight)), 0, NULL, 0, 0, /* No leases on v1s */ + AMOUNT_MSAT(0), /* No htlc_minimum_msat */ AMOUNT_MSAT(-1ULL)); /* No htlc_maximum_msat */ /* Now we finally put it in the database. */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index f25dc0006343..39c6b4a31e70 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -861,6 +861,9 @@ static void json_add_channel(struct lightningd *ld, channel->our_config.htlc_minimum, "htlc_minimum_msat", "minimum_htlc_in_msat"); + json_add_amount_msat_only(response, + "minimum_htlc_out_msat", + channel->htlc_minimum_msat); json_add_amount_msat_only(response, "maximum_htlc_out_msat", channel->htlc_maximum_msat); @@ -2010,21 +2013,27 @@ static struct command_result *param_msat_u32(struct command *cmd, static void set_channel_config(struct command *cmd, struct channel *channel, u32 *base, u32 *ppm, + struct amount_msat *htlc_min, struct amount_msat *htlc_max, u32 delaysecs, struct json_stream *response, bool add_details) { + bool warn_cannot_set_min = false; + /* We only need to defer values if we *increase* fees (or drop - * max); we always allow users to overpay fees. */ + * max, increase min); we always allow users to overpay fees. */ if ((base && *base > channel->feerate_base) || (ppm && *ppm > channel->feerate_ppm) + || (htlc_min + && amount_msat_greater(*htlc_min, channel->htlc_minimum_msat)) || (htlc_max && amount_msat_less(*htlc_max, channel->htlc_maximum_msat))) { channel->old_feerate_timeout = timeabs_add(time_now(), time_from_sec(delaysecs)); channel->old_feerate_base = channel->feerate_base; channel->old_feerate_ppm = channel->feerate_ppm; + channel->old_htlc_minimum_msat = channel->htlc_minimum_msat; channel->old_htlc_maximum_msat = channel->htlc_maximum_msat; } @@ -2033,6 +2042,17 @@ static void set_channel_config(struct command *cmd, struct channel *channel, channel->feerate_base = *base; if (ppm) channel->feerate_ppm = *ppm; + if (htlc_min) { + struct amount_msat actual_min; + + /* We can't send something they'll refuse: check that here. */ + actual_min = channel->channel_info.their_config.htlc_minimum; + if (amount_msat_less(*htlc_min, actual_min)) { + warn_cannot_set_min = true; + channel->htlc_minimum_msat = actual_min; + } else + channel->htlc_minimum_msat = *htlc_min; + } if (htlc_max) channel->htlc_maximum_msat = *htlc_max; @@ -2040,7 +2060,7 @@ static void set_channel_config(struct command *cmd, struct channel *channel, if (channel->owner && streq(channel->owner->name, "channeld")) subd_send_msg(channel->owner, take(towire_channeld_config_channel(NULL, base, ppm, - htlc_max))); + htlc_min, htlc_max))); /* save values to database */ wallet_channel_save(cmd->ld->wallet, channel); @@ -2059,6 +2079,12 @@ static void set_channel_config(struct command *cmd, struct channel *channel, amount_msat(channel->feerate_base)); json_add_u32(response, "fee_proportional_millionths", channel->feerate_ppm); + json_add_amount_msat_only(response, + "minimum_htlc_out_msat", + channel->htlc_minimum_msat); + if (warn_cannot_set_min) + json_add_string(response, "warning_htlcmin_too_low", + "Set minimum_htlc_out_msat to minimum allowed by peer"); json_add_amount_msat_only(response, "maximum_htlc_out_msat", channel->htlc_maximum_msat); @@ -2110,13 +2136,13 @@ static struct command_result *json_setchannelfee(struct command *cmd, channel->state != CHANNELD_AWAITING_LOCKIN && channel->state != DUALOPEND_AWAITING_LOCKIN) continue; - set_channel_config(cmd, channel, base, ppm, NULL, + set_channel_config(cmd, channel, base, ppm, NULL, NULL, *delaysecs, response, false); } /* single channel should be updated */ } else { - set_channel_config(cmd, channel, base, ppm, NULL, + set_channel_config(cmd, channel, base, ppm, NULL, NULL, *delaysecs, response, false); } @@ -2149,13 +2175,14 @@ static struct command_result *json_setchannel(struct command *cmd, struct peer *peer; struct channel *channel; u32 *base, *ppm, *delaysecs; - struct amount_msat *htlc_max; + struct amount_msat *htlc_min, *htlc_max; /* Parse the JSON command */ if (!param(cmd, buffer, params, p_req("id", param_channel_or_all, &channel), p_opt("feebase", param_msat_u32, &base), p_opt("feeppm", param_number, &ppm), + p_opt("htlcmin", param_msat, &htlc_min), p_opt("htlcmax", param_msat, &htlc_max), p_opt_def("enforcedelay", param_number, &delaysecs, 600), NULL)) @@ -2182,13 +2209,15 @@ static struct command_result *json_setchannel(struct command *cmd, channel->state != CHANNELD_AWAITING_LOCKIN && channel->state != DUALOPEND_AWAITING_LOCKIN) continue; - set_channel_config(cmd, channel, base, ppm, htlc_max, + set_channel_config(cmd, channel, base, ppm, + htlc_min, htlc_max, *delaysecs, response, true); } /* single channel should be updated */ } else { - set_channel_config(cmd, channel, base, ppm, htlc_max, + set_channel_config(cmd, channel, base, ppm, + htlc_min, htlc_max, *delaysecs, response, true); } diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index fc55f2b27c16..e0d902c715a1 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -656,16 +656,18 @@ static void forward_htlc(struct htlc_in *hin, "Allowing payment using older feerate"); } - if (amount_msat_greater(amt_to_forward, next->htlc_maximum_msat)) { - /* Are we in old-max grace-period? */ + if (amount_msat_greater(amt_to_forward, next->htlc_maximum_msat) + || amount_msat_less(amt_to_forward, next->htlc_minimum_msat)) { + /* Are we in old-range grace-period? */ if (!time_before(time_now(), next->old_feerate_timeout) + || amount_msat_less(amt_to_forward, next->old_htlc_minimum_msat) || amount_msat_greater(amt_to_forward, next->old_htlc_maximum_msat)) { failmsg = towire_temporary_channel_failure(tmpctx, get_channel_update(next)); goto fail; } log_info(hin->key.channel->log, - "Allowing large htlc using older htlc_maximum_msat"); + "Allowing htlc using older htlc_minimum/maximum_msat"); } if (!check_cltv(hin, cltv_expiry, outgoing_cltv_value, diff --git a/lightningd/routehint.c b/lightningd/routehint.c index b5f37776aaec..a9879e3a38a2 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -114,6 +114,8 @@ routehint_candidates(const tal_t *ctx, continue; } + /* FIXME: we don't actually check htlc_minimum_msat! */ + /* If they set an htlc_maximum_msat, consider that the * capacity ceiling. We *could* do multiple HTLCs, * but presumably that would defeat the spirit of the diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 1f7db910b6dc..08bd6eef936a 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -626,7 +626,7 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } /* Generated stub for towire_channeld_config_channel */ -u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED) +u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_minimum UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED) { fprintf(stderr, "towire_channeld_config_channel called!\n"); abort(); } /* Generated stub for towire_channeld_dev_memleak */ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) diff --git a/tests/test_pay.py b/tests/test_pay.py index 0e532248d79a..fee9e3f302a4 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1882,7 +1882,7 @@ def test_setchannel_usage(node_factory, bitcoind): def channel_get_config(scid): return l1.db.query( - 'SELECT feerate_base, feerate_ppm, htlc_maximum_msat FROM channels ' + 'SELECT feerate_base, feerate_ppm, htlc_minimum_msat, htlc_maximum_msat FROM channels ' 'WHERE short_channel_id=\'{}\';'.format(scid)) # get short channel id @@ -1900,8 +1900,8 @@ def channel_get_config(scid): assert peers[0]['channels'][0]['fee_proportional_millionths'] == DEF_PPM assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == MAX_HTLC - # custom setchannel scid - result = l1.rpc.setchannel(scid, 1337, 137, 133337) + # custom setchannel scid + result = l1.rpc.setchannel(scid, 1337, 137, 17, 133337) # check result format assert(len(result['channels']) == 1) @@ -1910,22 +1910,26 @@ def channel_get_config(scid): assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['fee_base_msat'] == 1337) assert(result['channels'][0]['fee_proportional_millionths'] == 137) + assert(result['channels'][0]['minimum_htlc_out_msat'] == 17) assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337) # check if custom values made it into the database db_fees = channel_get_config(scid) assert(db_fees[0]['feerate_base'] == 1337) assert(db_fees[0]['feerate_ppm'] == 137) + assert(db_fees[0]['htlc_minimum_msat'] == 17) assert(db_fees[0]['htlc_maximum_msat'] == 133337) # also check for updated values in `listpeers` peers = l1.rpc.listpeers()['peers'] assert peers[0]['channels'][0]['fee_base_msat'] == Millisatoshi(1337) assert peers[0]['channels'][0]['fee_proportional_millionths'] == 137 + assert peers[0]['channels'][0]['minimum_htlc_out_msat'] == 17 assert peers[0]['channels'][0]['maximum_htlc_out_msat'] == 133337 # wait for gossip and check if l1 sees new fees in listchannels wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_BASE, 1337]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [DEF_PPM, 137]) + wait_for(lambda: [c['htlc_minimum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [0, 17]) wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [MAX_HTLC, 133337]) # also test with named and missing parameters @@ -1935,6 +1939,7 @@ def channel_get_config(scid): assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['fee_base_msat'] == 1337) assert(result['channels'][0]['fee_proportional_millionths'] == 42) + assert result['channels'][0]['minimum_htlc_out_msat'] == 17 assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337) result = l1.rpc.setchannel(feebase=43, id=scid) @@ -1943,6 +1948,16 @@ def channel_get_config(scid): assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['fee_base_msat'] == 43) assert(result['channels'][0]['fee_proportional_millionths'] == 42) + assert result['channels'][0]['minimum_htlc_out_msat'] == 17 + assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337) + + result = l1.rpc.setchannel(htlcmin=45, id=scid) + assert(len(result['channels']) == 1) + assert(re.match('^[0-9a-f]{64}$', result['channels'][0]['channel_id'])) + assert(result['channels'][0]['short_channel_id'] == scid) + assert(result['channels'][0]['fee_base_msat'] == 43) + assert(result['channels'][0]['fee_proportional_millionths'] == 42) + assert result['channels'][0]['minimum_htlc_out_msat'] == 45 assert(result['channels'][0]['maximum_htlc_out_msat'] == 133337) result = l1.rpc.setchannel(htlcmax=43333, id=scid) @@ -1951,6 +1966,7 @@ def channel_get_config(scid): assert(result['channels'][0]['short_channel_id'] == scid) assert(result['channels'][0]['fee_base_msat'] == 43) assert(result['channels'][0]['fee_proportional_millionths'] == 42) + assert result['channels'][0]['minimum_htlc_out_msat'] == 45 assert(result['channels'][0]['maximum_htlc_out_msat'] == 43333) # check if negative fees raise error and DB keeps values @@ -2079,6 +2095,7 @@ def test_setchannel_routing(node_factory, bitcoind): DEF_BASE = 1 DEF_PPM = 10 MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99)) + MIN_HTLC = Millisatoshi(0) l1, l2, l3 = node_factory.line_graph( 3, announce_channels=True, wait_for_announce=True, @@ -2089,11 +2106,12 @@ def test_setchannel_routing(node_factory, bitcoind): scid = l2.get_channel_scid(l3) # TEST CUSTOM VALUES - l2.rpc.setchannel(scid, 1337, 137, 4000000, enforcedelay=0) + l2.rpc.setchannel(scid, 1337, 137, 17, 4000000, enforcedelay=0) # wait for l1 to see updated channel via gossip wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid)['channels']] == [1337, DEF_BASE]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid)['channels']] == [137, DEF_PPM]) + wait_for(lambda: [c['htlc_minimum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [17, MIN_HTLC]) wait_for(lambda: [c['htlc_maximum_msat'] for c in l1.rpc.listchannels(scid)['channels']] == [4000000, MAX_HTLC]) # test fees are applied to HTLC forwards @@ -2136,7 +2154,7 @@ def test_setchannel_routing(node_factory, bitcoind): # In case l3 includes a routehint, we need to make sure they also know # about the new fees, otherwise we may end up with the old feerate - wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, 4000000, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)]) + wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_minimum_msat'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid)['channels']] == [(1337, 137, 17, 4000000, True), (DEF_BASE, DEF_PPM, MIN_HTLC, MAX_HTLC, True)]) # do and check actual payment inv = l3.rpc.invoice(4000000, 'test_setchannel_2', 'desc') @@ -2153,9 +2171,33 @@ def test_setchannel_routing(node_factory, bitcoind): l1.rpc.sendpay(route_ok, inv['payment_hash'], payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(inv['payment_hash']) + # Now try below minimum + route_ok = l1.rpc.getroute(l3.info['id'], 17, 1)["route"] + assert len(route_ok) == 2 + assert route_ok[0]['msatoshi'] == 1337 + 17 + assert route_ok[1]['msatoshi'] == 17 + + route_bad = copy.deepcopy(route_ok) + route_bad[0]['msatoshi'] = 1337 + 16 + route_bad[1]['msatoshi'] = 16 + route_bad[0]['amount_msat'] = Millisatoshi(1337 + 16) + route_bad[1]['amount_msat'] = Millisatoshi(16) + assert route_bad != route_ok + + inv = l3.rpc.invoice(17, 'test_setchannel_3', 'desc') + + # This will fail. + l1.rpc.sendpay(route_bad, inv['payment_hash'], payment_secret=inv['payment_secret']) + with pytest.raises(RpcError, match='WIRE_TEMPORARY_CHANNEL_FAILURE'): + l1.rpc.waitsendpay(inv['payment_hash']) + + # This will succeed + l1.rpc.sendpay(route_ok, inv['payment_hash'], payment_secret=inv['payment_secret']) + l1.rpc.waitsendpay(inv['payment_hash']) + # Check that this one warns about capacity! inv = l3.rpc.call('invoice', {'msatoshi': 4001793, - 'label': 'test_setchannel_3', + 'label': 'test_setchannel_4', 'description': 'desc'}) assert 'warning_capacity' in inv @@ -2213,6 +2255,7 @@ def test_setchannel_restart(node_factory, bitcoind): # - l1 routing can be made to l3 and global (1 10) fees are applied DEF_BASE = 1 DEF_PPM = 10 + MIN_HTLC = Millisatoshi(0) MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99)) OPTS = {'may_reconnect': True, 'fee-base': DEF_BASE, 'fee-per-satoshi': DEF_PPM} @@ -2223,7 +2266,7 @@ def test_setchannel_restart(node_factory, bitcoind): scid23 = l2.get_channel_scid(l3) # l2 set custom fees - l2.rpc.setchannel(scid23, 1337, 137, 500001) + l2.rpc.setchannel(scid23, 1337, 137, 17, 500001) # restart l2 and reconnect l2.restart() @@ -2234,11 +2277,11 @@ def test_setchannel_restart(node_factory, bitcoind): wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid12)['channels']] == [True, True]) # l1 wait for channel update from l2 - wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)]) + wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_minimum_msat'], c['htlc_maximum_msat'], c['active']) for c in l1.rpc.listchannels(scid23)['channels']] == [(1337, 137, 17, 500001, True), (DEF_BASE, DEF_PPM, MIN_HTLC, MAX_HTLC, True)]) # In case l3 includes a routehint, we need to make sure they also know # about the new fees, otherwise we may end up with the old feerate - wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, 500001, True), (DEF_BASE, DEF_PPM, MAX_HTLC, True)]) + wait_for(lambda: [(c['base_fee_millisatoshi'], c['fee_per_millionth'], c['htlc_minimum_msat'], c['htlc_maximum_msat'], c['active']) for c in l3.rpc.listchannels(scid23)['channels']] == [(1337, 137, 17, 500001, True), (DEF_BASE, DEF_PPM, MIN_HTLC, MAX_HTLC, True)]) # l1 can make payment to l3 with custom fees being applied # Note: BOLT #7 math works out to 1405 msat fees @@ -2269,7 +2312,7 @@ def test_setchannel_all(node_factory, bitcoind): scid3 = l1.get_channel_scid(l3) # now try to set all (two) channels using wildcard syntax - result = l1.rpc.setchannel("all", 0xDEAD, 0xBEEF, 0xCAFE) + result = l1.rpc.setchannel("all", 0xDEAD, 0xBEEF, 0xBAD, 0xCAFE) wait_for(lambda: [c['base_fee_millisatoshi'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_BASE, 0xDEAD]) wait_for(lambda: [c['fee_per_millionth'] for c in l1.rpc.listchannels(scid2)['channels']] == [DEF_PPM, 0xBEEF]) @@ -2281,11 +2324,13 @@ def test_setchannel_all(node_factory, bitcoind): assert result['channels'][0]['short_channel_id'] == scid2 assert result['channels'][0]['fee_base_msat'] == 0xDEAD assert result['channels'][0]['fee_proportional_millionths'] == 0xBEEF + assert result['channels'][0]['minimum_htlc_out_msat'] == 0xBAD assert result['channels'][0]['maximum_htlc_out_msat'] == 0xCAFE assert result['channels'][1]['peer_id'] == l3.info['id'] assert result['channels'][1]['short_channel_id'] == scid3 assert result['channels'][1]['fee_base_msat'] == 0xDEAD assert result['channels'][1]['fee_proportional_millionths'] == 0xBEEF + assert result['channels'][1]['minimum_htlc_out_msat'] == 0xBAD assert result['channels'][1]['maximum_htlc_out_msat'] == 0xCAFE diff --git a/wallet/db.c b/wallet/db.c index 09721db2ce2a..79576b9a6a9c 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -871,6 +871,7 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE channel_funding_inflights ADD lease_fee BIGINT DEFAULT 0"), NULL}, /* Default is too big; we set to max after loading */ {SQL("ALTER TABLE channels ADD htlc_maximum_msat BIGINT DEFAULT 2100000000000000"), NULL}, + {SQL("ALTER TABLE channels ADD htlc_minimum_msat BIGINT DEFAULT 0"), NULL}, }; /** diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index d6573857ed29..71280026ae01 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -694,7 +694,7 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, u8 *towire_channel_disabled(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channel_disabled called!\n"); abort(); } /* Generated stub for towire_channeld_config_channel */ -u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED) +u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_minimum UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED) { fprintf(stderr, "towire_channeld_config_channel called!\n"); abort(); } /* Generated stub for towire_channeld_dev_memleak */ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) @@ -1598,6 +1598,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) 100, lease_commit_sig, 7777, 22, + AMOUNT_MSAT(0), AMOUNT_MSAT(-1ULL)); db_begin_transaction(w->db); CHECK(!wallet_err); diff --git a/wallet/wallet.c b/wallet/wallet.c index 7cc5676f0893..47e43bfabaa7 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1263,7 +1263,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm struct pubkey local_funding_pubkey; struct pubkey *future_per_commitment_point; struct amount_sat funding_sat, our_funding_sat; - struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max, htlc_maximum_msat; + struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max, htlc_minimum_msat, htlc_maximum_msat; struct channel_type *type; secp256k1_ecdsa_signature *lease_commit_sig; u32 lease_chan_max_msat; @@ -1403,6 +1403,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm db_col_amount_msat(stmt, "msatoshi_local", &our_msat); db_col_amount_msat(stmt, "msatoshi_to_us_min", &msat_to_us_min); db_col_amount_msat(stmt, "msatoshi_to_us_max", &msat_to_us_max); + db_col_amount_msat(stmt, "htlc_minimum_msat", &htlc_minimum_msat); db_col_amount_msat(stmt, "htlc_maximum_msat", &htlc_maximum_msat); if (!db_col_is_null(stmt, "lease_commit_sig")) { @@ -1480,6 +1481,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, + htlc_minimum_msat, htlc_maximum_msat); if (!wallet_channel_load_inflights(w, chan)) { @@ -1574,6 +1576,7 @@ static bool wallet_channels_load_active(struct wallet *w) ", lease_commit_sig" ", lease_chan_max_msat" ", lease_chan_max_ppt" + ", htlc_minimum_msat" ", htlc_maximum_msat" " FROM channels" " WHERE state != ?;")); //? 0 @@ -1855,8 +1858,9 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " lease_commit_sig=?," // 39 " lease_chan_max_msat=?," // 40 " lease_chan_max_ppt=?," // 41 - " htlc_maximum_msat=?" // 42 - " WHERE id=?")); // 43 + " htlc_minimum_msat=?," // 42 + " htlc_maximum_msat=?" // 43 + " WHERE id=?")); // 44 db_bind_u64(stmt, 0, chan->their_shachain.id); if (chan->scid) db_bind_short_channel_id(stmt, 1, chan->scid); @@ -1919,8 +1923,9 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt, 40); db_bind_null(stmt, 41); } - db_bind_amount_msat(stmt, 42, &chan->htlc_maximum_msat); - db_bind_u64(stmt, 43, chan->dbid); + db_bind_amount_msat(stmt, 42, &chan->htlc_minimum_msat); + db_bind_amount_msat(stmt, 43, &chan->htlc_maximum_msat); + db_bind_u64(stmt, 44, chan->dbid); db_exec_prepared_v2(take(stmt)); wallet_channel_config_save(w, &chan->channel_info.their_config); From 5704653d4c0e9761194fd51f631cd692e3700091 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 21 Mar 2022 11:28:57 +1030 Subject: [PATCH 0487/1530] setchannel: don't let them advertize htlc_maximum_msat larger than capacity. And check for the obvious setting min > max. Signed-off-by: Rusty Russell --- doc/lightning-setchannel.7.md | 3 ++- doc/schemas/setchannel.schema.json | 4 ++++ lightningd/peer_control.c | 25 ++++++++++++++++++--- lightningd/test/run-invoice-select-inchan.c | 3 +++ tests/test_pay.py | 9 ++++++++ 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index f915b7968853..65755634ce6b 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -71,6 +71,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **short_channel_id** (short_channel_id, optional): the short_channel_id (if locked in) - the following warnings are possible: - **warning_htlcmin_too_low**: The requested htlcmin was too low for this peer, so we set it to the minimum they will allow + - **warning_htlcmax_too_high**: The requested htlcmax was greater than the channel capacity, so we set it to the channel capacity [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -100,4 +101,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:0f153e7dddce61bc921b3743472f11316c5984b9b1459cac1b201d6f51ec1be1) +[comment]: # ( SHA256STAMP:a38b5ea12566d9e40eab07b95a90007bf66373ac1189f458d1678634522575b3) diff --git a/doc/schemas/setchannel.schema.json b/doc/schemas/setchannel.schema.json index 512e4dbdfa80..54a507cb899a 100644 --- a/doc/schemas/setchannel.schema.json +++ b/doc/schemas/setchannel.schema.json @@ -54,6 +54,10 @@ "maximum_htlc_out_msat": { "type": "msat", "description": "The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat)" + }, + "warning_htlcmax_too_high": { + "type": "string", + "description": "The requested htlcmax was greater than the channel capacity, so we set it to the channel capacity" } } } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 39c6b4a31e70..8b90cd657f55 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2019,7 +2019,7 @@ static void set_channel_config(struct command *cmd, struct channel *channel, struct json_stream *response, bool add_details) { - bool warn_cannot_set_min = false; + bool warn_cannot_set_min = false, warn_cannot_set_max = false; /* We only need to defer values if we *increase* fees (or drop * max, increase min); we always allow users to overpay fees. */ @@ -2053,8 +2053,17 @@ static void set_channel_config(struct command *cmd, struct channel *channel, } else channel->htlc_minimum_msat = *htlc_min; } - if (htlc_max) - channel->htlc_maximum_msat = *htlc_max; + if (htlc_max) { + struct amount_msat actual_max; + + /* Can't set it greater than actual capacity. */ + actual_max = htlc_max_possible_send(channel); + if (amount_msat_greater(*htlc_max, actual_max)) { + warn_cannot_set_max = true; + channel->htlc_maximum_msat = actual_max; + } else + channel->htlc_maximum_msat = *htlc_max; + } /* tell channeld to make a send_channel_update */ if (channel->owner && streq(channel->owner->name, "channeld")) @@ -2088,6 +2097,9 @@ static void set_channel_config(struct command *cmd, struct channel *channel, json_add_amount_msat_only(response, "maximum_htlc_out_msat", channel->htlc_maximum_msat); + if (warn_cannot_set_max) + json_add_string(response, "warning_htlcmax_too_high", + "Set maximum_htlc_out_msat to maximum possible in channel"); } json_object_end(response); } @@ -2188,6 +2200,13 @@ static struct command_result *json_setchannel(struct command *cmd, NULL)) return command_param_failed(); + /* Prevent obviously incorrect things! */ + if (htlc_min && htlc_max + && amount_msat_less(*htlc_max, *htlc_min)) { + return command_fail(cmd, LIGHTNINGD, + "htlcmax cannot be less than htlcmin"); + } + if (channel && channel->state != CHANNELD_NORMAL && channel->state != CHANNELD_AWAITING_LOCKIN diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 08bd6eef936a..09da34f9516b 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -247,6 +247,9 @@ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED, enum side side UNNEEDED, bool option_anchor_outputs UNNEEDED) { fprintf(stderr, "htlc_is_trimmed called!\n"); abort(); } +/* Generated stub for htlc_max_possible_send */ +struct amount_msat htlc_max_possible_send(const struct channel *channel UNNEEDED) +{ fprintf(stderr, "htlc_max_possible_send called!\n"); abort(); } /* Generated stub for htlc_set_fail */ void htlc_set_fail(struct htlc_set *set UNNEEDED, const u8 *failmsg TAKES UNNEEDED) { fprintf(stderr, "htlc_set_fail called!\n"); abort(); } diff --git a/tests/test_pay.py b/tests/test_pay.py index fee9e3f302a4..867fec63af40 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2213,6 +2213,7 @@ def test_setchannel_zero(node_factory, bitcoind): # - payment can be done using zero fees DEF_BASE = 1 DEF_PPM = 10 + MAX_HTLC = Millisatoshi(int(FUNDAMOUNT * 1000 * 0.99)) l1, l2, l3 = node_factory.line_graph( 3, announce_channels=True, wait_for_announce=True, @@ -2242,6 +2243,14 @@ def test_setchannel_zero(node_factory, bitcoind): assert result['status'] == 'complete' assert result['msatoshi_sent'] == 4999999 + # FIXME: hack something up to advertize min_htlc > 0, then test mintoolow. + with pytest.raises(RpcError, match="htlcmax cannot be less than htlcmin"): + l2.rpc.setchannel(scid, htlcmin=100000, htlcmax=99999) + + ret = l2.rpc.setchannel(scid, htlcmax=FUNDAMOUNT * 1000) + assert 'warning_htlcmax_too_high' in only_one(ret['channels']) + assert only_one(ret['channels'])['maximum_htlc_out_msat'] == MAX_HTLC + @pytest.mark.developer("gossip without DEVELOPER=1 is slow") def test_setchannel_restart(node_factory, bitcoind): From dbc77bcbc485a62dc74a26951608f268a8255833 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 09:55:15 +1030 Subject: [PATCH 0488/1530] pay: fix leak detect on shadow route. Short term leak, but leak-detect is right: it's dumb code! Signed-off-by: Rusty Russell ``` plugin-pay: MEMLEAK: 0x2505318 plugin-pay: label=common/utils.c:134:char[] plugin-pay: backtrace: plugin-pay: ccan/ccan/tal/tal.c:442 (tal_alloc_) plugin-pay: ccan/ccan/tal/tal.c:471 (tal_alloc_arr_) plugin-pay: common/utils.c:134 (tal_hexstr) plugin-pay: common/node_id.c:48 (node_id_to_hexstr) plugin-pay: common/node_id.c:50 (fmt_node_id_) plugin-pay: common/type_to_string.c:32 (type_to_string_) plugin-pay: plugins/libplugin-pay.c:2970 (shadow_route_extend) plugin-pay: plugins/libplugin-pay.c:3113 (shadow_route_listchannels) plugin-pay: plugins/libplugin.c:563 (handle_rpc_reply) plugin-pay: plugins/libplugin.c:731 (rpc_read_response_one) plugin-pay: plugins/libplugin.c:751 (rpc_conn_read_response) plugin-pay: ccan/ccan/io/io.c:59 (next_plan) plugin-pay: ccan/ccan/io/io.c:407 (do_plan) plugin-pay: ccan/ccan/io/io.c:417 (io_ready) plugin-pay: ccan/ccan/io/poll.c:453 (io_loop) plugin-pay: plugins/libplugin.c:1565 (plugin_main) plugin-pay: plugins/pay.c:2569 (main) plugin-pay: parents: plugin-pay: plugins/libplugin.c:155:struct out_req ``` --- plugins/libplugin-pay.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index dafa5b7669bd..f3f9d496d696 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -2966,8 +2966,7 @@ static struct command_result *shadow_route_extend(struct shadow_route_data *d, req = jsonrpc_request_start(p->plugin, NULL, "listchannels", shadow_route_listchannels, payment_rpc_failure, p); - json_add_string(req->js, "source", - type_to_string(req, struct node_id, &d->destination)); + json_add_node_id(req->js, "source", &d->destination); return send_outreq(p->plugin, req); } From 722f2911df37dcf95fc1b7b761cec46156194e47 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 15 Mar 2022 10:58:38 +0100 Subject: [PATCH 0489/1530] py: Disentangle pyln dependencies Turns out that the pyln-proto dependency in the bolt packages is only needed for testing, not for production. Making it a dev-dependency means it isn't considered in resolution anymore. Since the bolt, testing and client packages are to be used outside from the project we can't use relative dependencies either, so make then dependent on the version on PyPI. This also means we had to push a couple of updated to PyPI. Changelog-None --- contrib/pyln-client/pyproject.toml | 6 +++--- contrib/pyln-proto/pyproject.toml | 2 +- contrib/pyln-spec/bolt1/pyproject.toml | 6 +++--- contrib/pyln-spec/bolt2/pyproject.toml | 6 +++--- contrib/pyln-spec/bolt4/pyproject.toml | 6 +++--- contrib/pyln-spec/bolt7/pyproject.toml | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index b4c883b9a44e..3014e19d783d 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-client" -version = "0.10.2" +version = "0.10.2.post1" description = "Client library and plugin library for c-lightning" authors = ["Christian Decker "] license = "BSD-MIT" @@ -11,8 +11,8 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -pyln-proto = { path = "../pyln-proto" } -pyln-bolt7 = { path = "../pyln-spec/bolt7" } +pyln-bolt7 = "^1.0.186" +pyln-proto = "^0.10.2" [tool.poetry.dev-dependencies] pytest = "^7.0.1" diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml index 6bb2af431e2c..cdc074d85afd 100644 --- a/contrib/pyln-proto/pyproject.toml +++ b/contrib/pyln-proto/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-proto" -version = "0.10.2" +version = "0.10.2.post1" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." authors = ["Christian Decker "] license = "BSD-MIT" diff --git a/contrib/pyln-spec/bolt1/pyproject.toml b/contrib/pyln-spec/bolt1/pyproject.toml index 903c33114377..c6ee8219eb12 100644 --- a/contrib/pyln-spec/bolt1/pyproject.toml +++ b/contrib/pyln-spec/bolt1/pyproject.toml @@ -1,8 +1,8 @@ [tool.poetry] name = "pyln-bolt1" -version = "1.0.1.187" +version = "1.0.1.187.post0" description = "" -authors = ["Rusty Russell <@rustyrussell>"] +authors = ["Rusty Russell "] license = "MIT" packages = [ @@ -11,9 +11,9 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -pyln-proto = {path = "../../pyln-proto"} [tool.poetry.dev-dependencies] +pyln-proto = "^0.10.2" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/contrib/pyln-spec/bolt2/pyproject.toml b/contrib/pyln-spec/bolt2/pyproject.toml index 14895c1704f7..10367daa99b8 100644 --- a/contrib/pyln-spec/bolt2/pyproject.toml +++ b/contrib/pyln-spec/bolt2/pyproject.toml @@ -1,8 +1,8 @@ [tool.poetry] name = "pyln-bolt2" -version = "1.0.1.187" +version = "1.0.2.187.post0" description = "A pure python implementation of BOLT2" -authors = ["Rusty Russell <@rustyrussell>"] +authors = ["Rusty Russell "] license = "MIT" packages = [ @@ -11,9 +11,9 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -pyln-proto = {path = "../../pyln-proto"} [tool.poetry.dev-dependencies] +pyln-proto = "^0.10.2" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/contrib/pyln-spec/bolt4/pyproject.toml b/contrib/pyln-spec/bolt4/pyproject.toml index 33afd320cfcb..1d3361de1b91 100644 --- a/contrib/pyln-spec/bolt4/pyproject.toml +++ b/contrib/pyln-spec/bolt4/pyproject.toml @@ -1,8 +1,8 @@ [tool.poetry] name = "pyln-bolt4" -version = "1.0.1.187" +version = "1.0.2.187.post0" description = "A pure python implementation of BOLT4" -authors = ["Rusty Russell <@rustyrussell>"] +authors = ["Rusty Russell "] license = "MIT" packages = [ @@ -11,9 +11,9 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -pyln-proto = {path = "../../pyln-proto"} [tool.poetry.dev-dependencies] +pyln-proto = "^0.10.2" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/contrib/pyln-spec/bolt7/pyproject.toml b/contrib/pyln-spec/bolt7/pyproject.toml index 1062ae710565..bc6b128ea567 100644 --- a/contrib/pyln-spec/bolt7/pyproject.toml +++ b/contrib/pyln-spec/bolt7/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-bolt7" -version = "1.0.186" +version = "1.0.2.186.post0" description = "BOLT7" authors = ["Rusty Russell"] license = "BSD-MIT" @@ -11,9 +11,9 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -pyln-proto = { path = "../../pyln-proto" } [tool.poetry.dev-dependencies] +pyln-proto = "^0.10.2" [build-system] requires = ["poetry-core>=1.0.0"] From ac322e1e5bd7acf35e925ba6dad68f08ee0ebced Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 16 Mar 2022 10:47:10 +0100 Subject: [PATCH 0490/1530] py: Make the pyln dependencies editable This allows us to edit the pyln packages in-tree without having to reinstall them after each change. --- poetry.lock | 197 ++++++++++++++++++++++++------------------------- pyproject.toml | 6 +- 2 files changed, 100 insertions(+), 103 deletions(-) diff --git a/poetry.lock b/poetry.lock index f91d61c15904..f6e2cbe80066 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,6 +1,6 @@ [[package]] name = "asn1crypto" -version = "1.4.0" +version = "1.5.1" description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP" category = "main" optional = false @@ -116,7 +116,7 @@ python-versions = "*" [[package]] name = "cryptography" -version = "36.0.1" +version = "36.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -196,7 +196,7 @@ dotenv = ["python-dotenv"] name = "importlib-metadata" version = "4.2.0" description = "Read metadata from Python packages" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -233,7 +233,7 @@ python-versions = "*" [[package]] name = "itsdangerous" -version = "2.1.0" +version = "2.1.1" description = "Safely pass data to untrusted environments and back." category = "dev" optional = false @@ -289,22 +289,24 @@ format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "mako" -version = "1.1.6" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +version = "1.2.0" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.7" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} MarkupSafe = ">=0.9.2" [package.extras] babel = ["babel"] lingua = ["lingua"] +testing = ["pytest"] [[package]] name = "markupsafe" -version = "2.1.0" +version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false @@ -467,32 +469,24 @@ python-versions = ">=3.5" [[package]] name = "pyln-bolt7" -version = "1.0.186" +version = "1.0.186.post0" description = "BOLT7" category = "main" optional = false -python-versions = "^3.7" -develop = false - -[package.dependencies] -pyln-proto = {path = "../../pyln-proto"} - -[package.source] -type = "directory" -url = "contrib/pyln-spec/bolt7" +python-versions = ">=3.7,<4.0" [[package]] name = "pyln-client" -version = "0.10.2" +version = "0.10.2.post1" description = "Client library and plugin library for c-lightning" category = "main" optional = false python-versions = "^3.7" -develop = false +develop = true [package.dependencies] -pyln-bolt7 = {path = "../pyln-spec/bolt7"} -pyln-proto = {path = "../pyln-proto"} +pyln-bolt7 = "^1.0.186" +pyln-proto = "^0.10.2" [package.source] type = "directory" @@ -500,12 +494,12 @@ url = "contrib/pyln-client" [[package]] name = "pyln-proto" -version = "0.10.2" +version = "0.10.2.post1" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." category = "main" optional = false python-versions = "^3.7" -develop = false +develop = true [package.dependencies] base58 = "^2.1.1" @@ -525,7 +519,7 @@ description = "Test your c-lightning integration, plugins or whatever you want" category = "dev" optional = false python-versions = "^3.7" -develop = false +develop = true [package.dependencies] cheroot = "^8.6.0" @@ -534,7 +528,7 @@ Flask = "^2.0.3" jsonschema = "^4.4.0" psutil = "^5.9.0" psycopg2-binary = "^2.9.3" -pyln-client = {path = "../pyln-client"} +pyln-client = "^0.10.2" pytest = "^7.0.1" python-bitcoinlib = "^0.11.0" @@ -571,11 +565,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pytest" -version = "7.0.1" +version = "7.1.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} @@ -690,7 +684,7 @@ python-versions = ">=3.6" name = "typing-extensions" version = "4.1.1" description = "Backported and Experimental Type Hints for Python 3.6+" -category = "dev" +category = "main" optional = false python-versions = ">=3.6" @@ -722,7 +716,7 @@ watchdog = ["watchdog"] name = "zipp" version = "3.7.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -733,12 +727,12 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "279c769b5a15faf494173d6b71dca50d90493534646f9c5e3506b8c52abfe3b5" +content-hash = "25c2960761882dd99f4a0ed3394354054a45905d811d80f6984a56495df9ac22" [metadata.files] asn1crypto = [ - {file = "asn1crypto-1.4.0-py2.py3-none-any.whl", hash = "sha256:4bcdf33c861c7d40bdcd74d8e4dd7661aac320fcdf40b9a3f95b4ee12fde2fa8"}, - {file = "asn1crypto-1.4.0.tar.gz", hash = "sha256:f4f6e119474e58e04a2b1af817eb585b4fd72bdd89b998624712b5c99be7641c"}, + {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, + {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, ] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, @@ -921,26 +915,26 @@ crc32c = [ {file = "crc32c-2.2.post0.tar.gz", hash = "sha256:3d058e7a5e37e4985d1a7ad4cb702bca56b490daa658d4851377d13ead8b435e"}, ] cryptography = [ - {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"}, - {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"}, - {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"}, - {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"}, - {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"}, - {file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"}, - {file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"}, - {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"}, - {file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"}, - {file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"}, + {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"}, + {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"}, + {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"}, + {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"}, + {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"}, + {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"}, + {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"}, + {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"}, + {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"}, + {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"}, + {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"}, + {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"}, ] ephemeral-port-reserve = [ {file = "ephemeral_port_reserve-1.1.4-py2.py3-none-any.whl", hash = "sha256:dae8da99422c643bb52478ed55d5a8428099092391656ba3726ff30c801600c8"}, @@ -975,8 +969,8 @@ iniconfig = [ {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] itsdangerous = [ - {file = "itsdangerous-2.1.0-py3-none-any.whl", hash = "sha256:29285842166554469a56d427addc0843914172343784cb909695fdbe90a3e129"}, - {file = "itsdangerous-2.1.0.tar.gz", hash = "sha256:d848fcb8bc7d507c4546b448574e8a44fc4ea2ba84ebf8d783290d53e81992f5"}, + {file = "itsdangerous-2.1.1-py3-none-any.whl", hash = "sha256:935642cd4b987cdbee7210080004033af76306757ff8b4c0a506a4b6e06f02cf"}, + {file = "itsdangerous-2.1.1.tar.gz", hash = "sha256:7b7d3023cd35d9cb0c1fd91392f8c95c6fa02c59bf8ad64b8849be3401b95afb"}, ] "jaraco.functools" = [ {file = "jaraco.functools-3.5.0-py3-none-any.whl", hash = "sha256:141f95c490a18eb8aab86caf7a2728f02f604988a26dc36652e3d9fa9e4c49fa"}, @@ -991,50 +985,50 @@ jsonschema = [ {file = "jsonschema-4.4.0.tar.gz", hash = "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83"}, ] mako = [ - {file = "Mako-1.1.6-py2.py3-none-any.whl", hash = "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57"}, - {file = "Mako-1.1.6.tar.gz", hash = "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2"}, + {file = "Mako-1.2.0-py3-none-any.whl", hash = "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba"}, + {file = "Mako-1.2.0.tar.gz", hash = "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39"}, ] markupsafe = [ - {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-win32.whl", hash = "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454"}, - {file = "MarkupSafe-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-win32.whl", hash = "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a"}, - {file = "MarkupSafe-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-win32.whl", hash = "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8"}, - {file = "MarkupSafe-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-win32.whl", hash = "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05"}, - {file = "MarkupSafe-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7"}, - {file = "MarkupSafe-2.1.0.tar.gz", hash = "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, @@ -1195,7 +1189,10 @@ pygments = [ {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, ] -pyln-bolt7 = [] +pyln-bolt7 = [ + {file = "pyln-bolt7-1.0.186.post0.tar.gz", hash = "sha256:950f788869df138599abea7643b752c16ae8ddfa91c3a31b64647c45d08c0407"}, + {file = "pyln_bolt7-1.0.186.post0-py3-none-any.whl", hash = "sha256:9b65cbaa4fd9db19f30ead7bb5eb61793d2157038237430ff9759e3abc84d739"}, +] pyln-client = [] pyln-proto = [] pyln-testing = [] @@ -1232,8 +1229,8 @@ pysocks = [ {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, ] pytest = [ - {file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"}, - {file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"}, + {file = "pytest-7.1.1-py3-none-any.whl", hash = "sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea"}, + {file = "pytest-7.1.1.tar.gz", hash = "sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63"}, ] pytest-custom-exit-code = [ {file = "pytest-custom_exit_code-0.3.0.tar.gz", hash = "sha256:51ffff0ee2c1ddcc1242e2ddb2a5fd02482717e33a2326ef330e3aa430244635"}, diff --git a/pyproject.toml b/pyproject.toml index 3d8214f7ce70..2e29984de8d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,8 @@ authors = ["Christian Decker "] [tool.poetry.dependencies] # Build dependencies belong here python = "^3.7" -pyln-client = { path = "contrib/pyln-client" } -pyln-proto = { path = "contrib/pyln-proto" } +pyln-client = { path = "./contrib/pyln-client", develop = true } +pyln-proto = { path = "./contrib/pyln-proto", develop = true } Mako = "^1.1.6" mrkd = { git = "https://github.com/refi64/mrkd.git", rev = "781f05eb9898ca652f18eed29b3c956389e6a2a7" } websocket-client = "^1.2.3" @@ -22,7 +22,7 @@ pytest-timeout = "^2.1.0" flake8 = "^4.0.1" mypy = "^0.931" pytest-custom-exit-code = "0.3.0" -pyln-testing = { path = "contrib/pyln-testing" } +pyln-testing = { path = "./contrib/pyln-testing", develop = true } flaky = "^3.7.0" [build-system] From fdd7c6b192e118f131e9c9ffdeb661b19b6b945b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 18 Mar 2022 13:01:17 +0100 Subject: [PATCH 0491/1530] pyln: Remove two more occurences of non-None default args --- contrib/pyln-client/pyln/client/lightning.py | 15 ++++++++++----- contrib/pyln-testing/pyproject.toml | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 1c17b1c5a131..e6d1dc5840fe 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -616,7 +616,7 @@ def dev_memleak(self): def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, - maxdelay=None, exemptfee=None, use_shadow=True, exclude=[]): + maxdelay=None, exemptfee=None, use_shadow=True, exclude=None): """ A developer version of `pay`, with the possibility to deactivate shadow routing (used for testing). @@ -701,7 +701,9 @@ def feerates(self, style, urgent=None, normal=None, slow=None): } return self.call("feerates", payload) - def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None, utxos=None, push_msat=None, close_to=None, request_amt=None, compact_lease=None): + def fundchannel(self, node_id, amount, feerate=None, announce=True, + minconf=None, utxos=None, push_msat=None, close_to=None, + request_amt=None, compact_lease=None): """ Fund channel with {id} using {amount} satoshis with feerate of {feerate} (uses default feerate if unset). @@ -729,7 +731,8 @@ def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None } return self.call("fundchannel", payload) - def fundchannel_start(self, node_id, amount, feerate=None, announce=True, close_to=None): + def fundchannel_start(self, node_id, amount, feerate=None, announce=True, + close_to=None): """ Start channel funding with {id} for {amount} satoshis with feerate of {feerate} (uses default feerate if unset). @@ -793,7 +796,8 @@ def getpeer(self, peer_id, level=None): res = self.call("listpeers", payload) return res.get("peers") and res["peers"][0] or None - def getroute(self, node_id, msatoshi, riskfactor, cltv=9, fromid=None, fuzzpercent=None, exclude=[], maxhops=20): + def getroute(self, node_id, msatoshi, riskfactor, cltv=9, fromid=None, + fuzzpercent=None, exclude=None, maxhops=None): """ Show route to {id} for {msatoshi}, using {riskfactor} and optional {cltv} (default 9). If specified search from {fromid} otherwise use @@ -823,7 +827,8 @@ def help(self, command=None): } return self.call("help", payload) - def invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, preimage=None, exposeprivatechannels=None, cltv=None): + def invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, + preimage=None, exposeprivatechannels=None, cltv=None): """ Create an invoice for {msatoshi} with {label} and {description} with optional {expiry} seconds (default 1 week). diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index ab8a59b35ecf..c9a319455b46 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -16,7 +16,7 @@ ephemeral-port-reserve = "^1.1.4" psycopg2-binary = "^2.9.3" python-bitcoinlib = "^0.11.0" jsonschema = "^4.4.0" -pyln-client = { path = "../pyln-client" } +pyln-client = "^0.10.2" Flask = "^2.0.3" cheroot = "^8.6.0" psutil = "^5.9.0" From 915a591873e7f0dff04a511087751fdf69f50afd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:18:13 +1030 Subject: [PATCH 0492/1530] spender: free up vars to avoid transient false leak reports. Signed-off-by: Rusty Russell --- plugins/spender/fundchannel.c | 3 +++ plugins/spender/multifundchannel.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index f0b2b1a4e247..91f6b72ff2b0 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -93,6 +93,9 @@ json_fundchannel(struct command *cmd, if (utxos) json_add_tok(req->js, "utxos", utxos, buf); + /* Stop memleak from complaining */ + tal_free(id); + return send_outreq(cmd->plugin, req); } diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 758524e1fdc4..41b55a17b9c9 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1931,6 +1931,13 @@ param_destinations_array(struct command *cmd, const char *name, dest->request_amt = *request_amt; dest->rates = tal_steal(*dests, rates); + /* Stop leak detection from complaining. */ + tal_free(id); + tal_free(amount); + tal_free(push_msat); + tal_free(request_amt); + tal_free(announce); + /* Only one destination can have "all" indicator. */ if (dest->all) { if (has_all) @@ -2028,6 +2035,9 @@ json_multifundchannel(struct command *cmd, mfc->sigs_collected = false; + /* Stop memleak from complaining */ + tal_free(minconf); + return perform_multifundchannel(mfc); } From b99c04e605c4e1bd6ade98b66800bc63fb02977b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:19:13 +1030 Subject: [PATCH 0493/1530] lightningd: add explicit "connected" flag. We currently intuit this by whether there's a subdaemon owning it. But we're about to change the rules and allow connectd to hold idle connections, so we need an explicit flag. Signed-off-by: Rusty Russell --- lightningd/channel.c | 2 ++ lightningd/connect_control.c | 21 +++++++-------------- lightningd/opening_common.c | 2 ++ lightningd/peer_control.c | 17 +++++------------ lightningd/peer_control.h | 3 +++ 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index a6555fc76cb3..6976a83cdb98 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -38,6 +38,8 @@ void channel_set_owner(struct channel *channel, struct subd *owner) * Only transfer to connectd if connectd is * there to be transferred to. */ + assert(channel->peer->connected); + channel->peer->connected = false; if (channel->peer->ld->connectd) { u8 *msg; msg = towire_connectd_peer_disconnected( diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index f62d83c1a8f1..a45a1cd3e6b0 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -139,20 +139,13 @@ static struct command_result *json_connect(struct command *cmd, /* If we know about peer, see if it's already connected. */ peer = peer_by_id(cmd->ld, &id); - if (peer) { - struct channel *channel = peer_active_channel(peer); - - if (!channel) - channel = peer_unsaved_channel(peer); - - if (peer->uncommitted_channel - || (channel && channel->connected)) { - log_debug(cmd->ld->log, "Already connected via %s", - type_to_string(tmpctx, struct wireaddr_internal, &peer->addr)); - return connect_cmd_succeed(cmd, peer, - peer->connected_incoming, - &peer->addr); - } + if (peer && peer->connected) { + log_debug(cmd->ld->log, "Already connected via %s", + type_to_string(tmpctx, struct wireaddr_internal, + &peer->addr)); + return connect_cmd_succeed(cmd, peer, + peer->connected_incoming, + &peer->addr); } /* Was there parseable host name? */ diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 5a6ac26f3078..542dfe2d2809 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -193,6 +193,8 @@ void handle_reestablish(struct lightningd *ld, "Unknown channel for reestablish"); log_debug(ld->log, "Reestablish on UNKNOWN channel %s", type_to_string(tmpctx, struct channel_id, channel_id)); + if (peer) + peer->connected = false; /* Unless we're shutting down */ if (ld->connectd) subd_send_msg(ld->connectd, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8b90cd657f55..f678ccecf472 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -100,6 +100,7 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, peer->their_features = NULL; list_head_init(&peer->channels); peer->direction = node_id_idx(&peer->ld->id, &peer->id); + peer->connected = false; #if DEVELOPER peer->ignore_htlcs = false; #endif @@ -1067,6 +1068,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa send_error: log_debug(ld->log, "Telling connectd to send error %s", tal_hex(tmpctx, error)); + peer->connected = false; /* Get connectd to send error and close. */ subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, @@ -1213,6 +1215,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) if (!peer) peer = new_peer(ld, 0, &id, &hook_payload->addr, hook_payload->incoming); + peer->connected = true; tal_steal(peer, hook_payload); hook_payload->peer = peer; @@ -1487,27 +1490,17 @@ static void json_add_peer(struct lightningd *ld, struct peer *p, const enum log_level *ll) { - bool connected; struct channel *channel; json_object_start(response, NULL); json_add_node_id(response, "id", &p->id); - /* Channel is also connected if uncommitted channel */ - if (p->uncommitted_channel) - connected = true; - else { - channel = peer_active_channel(p); - if (!channel) - channel = peer_unsaved_channel(p); - connected = channel && channel->connected; - } - json_add_bool(response, "connected", connected); + json_add_bool(response, "connected", p->connected); /* If it's not connected, features are unreliable: we don't * store them in the database, and they would only reflect * their features *last* time they connected. */ - if (connected) { + if (p->connected) { json_array_start(response, "netaddr"); json_add_string(response, NULL, type_to_string(tmpctx, diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 1da2abcc6e50..c6afe8edd318 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -30,6 +30,9 @@ struct peer { /* Our channels */ struct list_head channels; + /* Are we connected? */ + bool connected; + /* Our (only) uncommitted channel, still opening. */ struct uncommitted_channel *uncommitted_channel; From 9bbb32433e114cf12a76313b8cc9c400c96621fa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:20:13 +1030 Subject: [PATCH 0494/1530] connectd: make sure we do IO logging on final_msg output. This happens when we send a warning or lightningd tells us to send a final message then close. Normally io logging is done by the subdaemon that creates it, but this is a special case. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index f94175b32ec0..3a918500b844 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -577,6 +577,8 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, /* OK, send this then close. */ msg = peer->final_msg; peer->final_msg = NULL; + /* Wasn't logged earlier, so do it now */ + status_peer_io(LOG_IO_OUT, &peer->id, msg); } /* Still nothing to send? */ From 005d69c4634cbd3989f5d384c64229b6a89e2380 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:21:13 +1030 Subject: [PATCH 0495/1530] connectd: clean up decrypted packet memory handling. Use tmpctx, rather than freeing manually everywhere (proof: next patch added a branch and forgot to free it!). Signed-off-by: Rusty Russell --- connectd/multiplex.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 3a918500b844..c6a3e47ea3e3 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -670,7 +670,7 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, { u8 *decrypted; - decrypted = cryptomsg_decrypt_body(NULL, &peer->cs, + decrypted = cryptomsg_decrypt_body(tmpctx, &peer->cs, peer->peer_in); if (!decrypted) { status_peer_debug(&peer->id, "Bad encrypted packet len %zu", @@ -680,28 +680,20 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, tal_free(peer->peer_in); /* dev_disconnect can disable read */ - if (!IFDEV(peer->dev_read_enabled, true)) { - tal_free(decrypted); + if (!IFDEV(peer->dev_read_enabled, true)) return read_hdr_from_peer(peer_conn, peer); - } /* Don't process packets while we're closing */ - if (peer->told_to_close) { - tal_free(decrypted); + if (peer->told_to_close) return read_hdr_from_peer(peer_conn, peer); - } /* If we swallow this, just try again. */ - if (handle_message_locally(peer, decrypted)) { - tal_free(decrypted); + if (handle_message_locally(peer, decrypted)) return read_hdr_from_peer(peer_conn, peer); - } /* If there's no subd, discard and keep reading. */ - if (!peer->to_subd) { - tal_free(decrypted); + if (!peer->to_subd) return read_hdr_from_peer(peer_conn, peer); - } /* Tell them to write. */ msg_enqueue(peer->subd_outq, take(decrypted)); From fcd0b2eb42cc5e5477fdd3789b221c855b04b9a0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:22:13 +1030 Subject: [PATCH 0496/1530] connectd: prepare for multiple subd connections. We still always have 1, but the infrastructure is now in place. Signed-off-by: Rusty Russell --- connectd/connectd.c | 9 ++- connectd/connectd.h | 13 ++-- connectd/multiplex.c | 165 ++++++++++++++++++++++++++++++++----------- tests/test_plugin.py | 2 +- 4 files changed, 136 insertions(+), 53 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 367253f030cf..dc0c2f798207 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -307,13 +307,12 @@ static struct peer *new_peer(struct daemon *daemon, peer->id = *id; peer->cs = *cs; peer->final_msg = NULL; - peer->subd_in = NULL; + peer->subds = tal_arr(peer, struct subd *, 0); peer->peer_in = NULL; peer->sent_to_peer = NULL; peer->urgent = false; peer->told_to_close = false; peer->peer_outq = msg_queue_new(peer, false); - peer->subd_outq = msg_queue_new(peer, false); #if DEVELOPER peer->dev_writes_enabled = NULL; @@ -323,7 +322,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->to_peer = conn; /* Aim for connection to shuffle data back and forth: sets up - * peer->to_subd */ + * peer->subds[0] */ if (!multiplex_subd_setup(peer, fd_for_subd)) return tal_free(peer); @@ -1792,7 +1791,7 @@ static void try_connect_peer(struct daemon *daemon, existing = peer_htable_get(&daemon->peers, id); if (existing) { /* If it's exiting now, we've raced: reconnect after */ - if (existing->to_subd + if (tal_count(existing->subds) != 0 && existing->to_peer && !existing->told_to_close) return; @@ -1892,7 +1891,7 @@ void peer_conn_closed(struct peer *peer) struct connecting *connect = find_connecting(peer->daemon, &peer->id); /* These should be closed already! */ - assert(!peer->to_subd); + assert(!peer->subds); assert(!peer->to_peer); assert(peer->told_to_close); diff --git a/connectd/connectd.h b/connectd/connectd.h index 9cecc61949e0..e75337f34af4 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -52,8 +53,8 @@ struct peer { /* Connection to the peer */ struct io_conn *to_peer; - /* Connection to the subdaemon */ - struct io_conn *to_subd; + /* Connections to the subdaemons */ + struct subd **subds; /* Final message to send to peer (and hangup) */ u8 *final_msg; @@ -64,11 +65,11 @@ struct peer { /* When socket has Nagle overridden */ bool urgent; - /* Input buffers. */ - u8 *subd_in, *peer_in; + /* Input buffer. */ + u8 *peer_in; - /* Output buffers. */ - struct msg_queue *subd_outq, *peer_outq; + /* Output buffer. */ + struct msg_queue *peer_outq; /* Peer sent buffer (for freeing after sending) */ const u8 *sent_to_peer; diff --git a/connectd/multiplex.c b/connectd/multiplex.c index c6a3e47ea3e3..91f221b56440 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -36,6 +36,26 @@ #include #include +struct subd { + /* Owner: we are in peer->subds[] */ + struct peer *peer; + + /* The temporary or permanant channel_id */ + struct channel_id channel_id; + + /* In passing, we can have a temporary one, too. */ + struct channel_id *temporary_channel_id; + + /* The actual connection to talk to it */ + struct io_conn *conn; + + /* Input buffer */ + u8 *in; + + /* Output buffer */ + struct msg_queue *outq; +}; + void inject_peer_msg(struct peer *peer, const u8 *msg TAKES) { status_peer_io(LOG_IO_OUT, &peer->id, msg); @@ -51,10 +71,10 @@ static void send_warning(struct peer *peer, const char *fmt, ...) status_vfmt(LOG_UNUSUAL, &peer->id, fmt, ap); va_end(ap); - /* Close locally, send msg as final warning */ - io_close(peer->to_subd); - peer->to_subd = NULL; + /* Close to any subdaemons. */ + peer->subds = tal_free(peer->subds); + /* Send warning as final message. */ va_start(ap, fmt); peer->final_msg = towire_warningfmtv(peer, NULL, fmt, ap); va_end(ap); @@ -573,7 +593,7 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, msg = msg_dequeue(peer->peer_outq); /* Is it time to send final? */ - if (!msg && peer->final_msg && !peer->to_subd) { + if (!msg && peer->final_msg && !peer->subds) { /* OK, send this then close. */ msg = peer->final_msg; peer->final_msg = NULL; @@ -584,7 +604,7 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, /* Still nothing to send? */ if (!msg) { /* We close once subds are all closed. */ - if (!peer->to_subd) { + if (!peer->subds) { set_closing_timer(peer, peer_conn); return io_sock_shutdown(peer_conn); } @@ -593,7 +613,7 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, msg = maybe_from_gossip_store(NULL, peer); if (!msg) { /* Tell them to read again, */ - io_wake(&peer->subd_in); + io_wake(&peer->subds); /* Wait for them to wake us */ return msg_queue_wait(peer_conn, peer->peer_outq, @@ -617,50 +637,59 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, } static struct io_plan *read_from_subd(struct io_conn *subd_conn, - struct peer *peer); + struct subd *subd); static struct io_plan *read_from_subd_done(struct io_conn *subd_conn, - struct peer *peer) + struct subd *subd) { /* Tell them to encrypt & write. */ - msg_enqueue(peer->peer_outq, take(peer->subd_in)); - peer->subd_in = NULL; + msg_enqueue(subd->peer->peer_outq, take(subd->in)); + subd->in = NULL; /* Wait for them to wake us */ - return io_wait(subd_conn, &peer->subd_in, read_from_subd, peer); + return io_wait(subd_conn, &subd->peer->subds, read_from_subd, subd); } static struct io_plan *read_from_subd(struct io_conn *subd_conn, - struct peer *peer) + struct subd *subd) { - return io_read_wire(subd_conn, peer, &peer->subd_in, - read_from_subd_done, peer); + return io_read_wire(subd_conn, subd, &subd->in, + read_from_subd_done, subd); } /* These four function handle peer->subd */ static struct io_plan *write_to_subd(struct io_conn *subd_conn, - struct peer *peer) + struct subd *subd) { const u8 *msg; - assert(peer->to_subd == subd_conn); + assert(subd->conn == subd_conn); /* Pop tail of send queue */ - msg = msg_dequeue(peer->subd_outq); + msg = msg_dequeue(subd->outq); /* Nothing to send? */ if (!msg) { /* If peer is closed, close this. */ - if (!peer->to_peer) + if (!subd->peer->to_peer) return io_close(subd_conn); /* Tell them to read again. */ - io_wake(&peer->peer_in); + io_wake(&subd->peer->peer_in); /* Wait for them to wake us */ - return msg_queue_wait(subd_conn, peer->subd_outq, - write_to_subd, peer); + return msg_queue_wait(subd_conn, subd->outq, + write_to_subd, subd); } - return io_write_wire(subd_conn, take(msg), write_to_subd, peer); + return io_write_wire(subd_conn, take(msg), write_to_subd, subd); +} + +/* FIXME: We only currently have one subd */ +static struct subd *find_subd(struct peer *peer, + const struct channel_id *channel_id) +{ + if (tal_count(peer->subds) == 0) + return NULL; + return peer->subds[0]; } static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, @@ -669,6 +698,8 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, struct peer *peer) { u8 *decrypted; + struct channel_id channel_id; + struct subd *subd; decrypted = cryptomsg_decrypt_body(tmpctx, &peer->cs, peer->peer_in); @@ -691,12 +722,39 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, if (handle_message_locally(peer, decrypted)) return read_hdr_from_peer(peer_conn, peer); - /* If there's no subd, discard and keep reading. */ - if (!peer->to_subd) + /* After this we should be able to match to subd by channel_id */ + if (!extract_channel_id(decrypted, &channel_id)) { + enum peer_wire type = fromwire_peektype(decrypted); + + /* We won't log this anywhere else, so do it here. */ + status_peer_io(LOG_IO_IN, &peer->id, decrypted); + + /* Could be a all-channel error or warning? Log it + * more verbose, and hang up. */ + if (type == WIRE_ERROR || type == WIRE_WARNING) { + char *desc = sanitize_error(tmpctx, decrypted, NULL); + status_peer_info(&peer->id, + "Received %s: %s", + peer_wire_name(type), desc); + return io_close(peer_conn); + } + + /* This sets final_msg: will close after sending warning */ + send_warning(peer, "Unexpected message %s: %s", + peer_wire_name(type), + tal_hex(tmpctx, decrypted)); + io_wake(peer->peer_outq); + + return read_hdr_from_peer(peer_conn, peer); + } + + /* If we don't find a subdaemon for this, discard and keep reading. */ + subd = find_subd(peer, &channel_id); + if (!subd) return read_hdr_from_peer(peer_conn, peer); /* Tell them to write. */ - msg_enqueue(peer->subd_outq, take(decrypted)); + msg_enqueue(subd->outq, take(decrypted)); /* Wait for them to wake us */ return io_wait(peer_conn, &peer->peer_in, read_hdr_from_peer, peer); @@ -734,21 +792,33 @@ static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, read_body_from_peer, peer); } -static struct io_plan *subd_conn_init(struct io_conn *subd_conn, struct peer *peer) +static struct io_plan *subd_conn_init(struct io_conn *subd_conn, + struct subd *subd) { - peer->to_subd = subd_conn; + subd->conn = subd_conn; return io_duplex(subd_conn, - read_from_subd(subd_conn, peer), - write_to_subd(subd_conn, peer)); + read_from_subd(subd_conn, subd), + write_to_subd(subd_conn, subd)); } -static void destroy_subd_conn(struct io_conn *subd_conn, struct peer *peer) +static void destroy_subd(struct subd *subd) { - assert(subd_conn == peer->to_subd); - peer->to_subd = NULL; - /* In case they were waiting for this to send final_msg */ - if (peer->final_msg) - msg_wake(peer->peer_outq); + struct peer *peer = subd->peer; + size_t pos; + + for (pos = 0; peer->subds[pos] != subd; pos++) + assert(pos < tal_count(peer->subds)); + + tal_arr_remove(&peer->subds, pos); + + /* Last one out frees array, sets to NULL as an indicator */ + if (tal_count(peer->subds) == 0) { + peer->subds = tal_free(peer->subds); + + /* In case they were waiting for this to send final_msg */ + if (peer->final_msg) + msg_wake(peer->peer_outq); + } /* Make sure we try to keep reading from peer, so we know if * it hangs up! */ @@ -765,7 +835,7 @@ void close_peer_conn(struct peer *peer) peer->told_to_close = true; /* Already dead? */ - if (!peer->to_subd && !peer->to_peer) { + if (!peer->subds && !peer->to_peer) { peer_conn_closed(peer); return; } @@ -777,14 +847,26 @@ void close_peer_conn(struct peer *peer) bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd) { int fds[2]; + struct subd *subd; if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { status_broken("Failed to create socketpair: %s", strerror(errno)); return false; } - peer->to_subd = io_new_conn(peer, fds[0], subd_conn_init, peer); - tal_add_destructor2(peer->to_subd, destroy_subd_conn, peer); + + subd = tal(peer->subds, struct subd); + subd->peer = peer; + subd->outq = msg_queue_new(subd, false); + /* This sets subd->conn inside subd_conn_init */ + io_new_conn(peer, fds[0], subd_conn_init, subd); + /* When conn dies, subd is freed. */ + tal_steal(subd->conn, subd); + + /* Connect it to the peer */ + tal_arr_expand(&peer->subds, subd); + tal_add_destructor(subd, destroy_subd); + *fd_for_subd = fds[1]; return true; } @@ -795,8 +877,9 @@ static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) peer->to_peer = NULL; /* Flush internal connections if not already. */ - if (peer->to_subd) { - msg_wake(peer->subd_outq); + if (peer->subds) { + for (size_t i = 0; i < tal_count(peer->subds); i++) + msg_wake(peer->subds[i]->outq); return; } @@ -824,7 +907,7 @@ void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) { peer->told_to_close = true; peer->final_msg = tal_dup_talarr(peer, u8, final_msg); - if (!peer->to_subd) + if (!peer->subds) io_wake(peer->peer_outq); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index c6b0b15bbf78..3c020bd81e8a 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -440,7 +440,7 @@ def test_plugin_connected_hook_chaining(node_factory): ]) # FIXME: this error occurs *after* connection, so we connect then drop. - l3.daemon.wait_for_log(r"chan#1: peer_in WIRE_WARNING") + l3.daemon.wait_for_log(r"-connectd: peer_in WIRE_WARNING") l3.daemon.wait_for_log(r"You are in reject list") def check_disconnect(): From eb203bf71e8bda621a41135c2428e3a0b43fd214 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:23:13 +1030 Subject: [PATCH 0497/1530] lightningd: clean up connect code. 1. The notification should be called every time. 2. channel can never be NULL, since it's tested above. Signed-off-by: Rusty Russell Changelog-Fixed: JSON-RPC: `connect` notification now called even if we already have a live channel. --- lightningd/peer_control.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index f678ccecf472..baa8f8d6fab3 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -982,6 +982,9 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa * subd). */ tal_steal(tmpctx, payload); + /* Notify anyone who cares */ + notify_connect(ld, &peer->id, payload->incoming, &addr); + /* Check for specific errors of a hook */ if (payload->error) { error = payload->error; @@ -1046,21 +1049,12 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa abort(); } - notify_connect(ld, &peer->id, payload->incoming, &addr); - + /* If we get here, it means we have no channel */ + assert(!channel); if (feature_negotiated(ld->our_features, peer->their_features, OPT_DUAL_FUND)) { - if (channel && !list_empty(&channel->inflights)) { - assert(!channel->owner); - assert(channel->state == DUALOPEND_OPEN_INIT - || channel->state == DUALOPEND_AWAITING_LOCKIN - || channel->state == AWAITING_UNILATERAL); - channel->peer->addr = addr; - channel->peer->connected_incoming = payload->incoming; - peer_restart_dualopend(peer, payload->peer_fd, channel); - } else - peer_start_dualopend(peer, payload->peer_fd); + peer_start_dualopend(peer, payload->peer_fd); } else peer_start_openingd(peer, payload->peer_fd); return; From 0cba0623188fa07bdb76e251ef74e2a849e47259 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:24:13 +1030 Subject: [PATCH 0498/1530] pytest: add test to check we notice remote disconnects. Signed-off-by: Rusty Russell --- tests/test_connection.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 23aaabafecae..0af389818511 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -401,8 +401,19 @@ def test_disconnect_opener(node_factory): l1.rpc.fundchannel(l2.info['id'], 25000) # Should still only have one peer! - assert len(l1.rpc.listpeers()) == 1 - assert len(l2.rpc.listpeers()) == 1 + assert len(l1.rpc.listpeers()['peers']) == 1 + assert len(l2.rpc.listpeers()['peers']) == 1 + + +def test_remote_disconnect(node_factory): + l1, l2 = node_factory.get_nodes(2) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + assert l2.rpc.listpeers()['peers'] != [] + l2.rpc.disconnect(l1.info['id']) + + # l1 should notice! + wait_for(lambda: l1.rpc.listpeers()['peers'] == []) @pytest.mark.developer From 16e9ba03614cfd9390bf1ef242e6694362f34858 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:56:16 +1030 Subject: [PATCH 0499/1530] connectd: fix confusing names. The message from lightningd simply acknowleges that we are allowed to discard the peer (because no subdaemons are talking to it anymore). This difference becomes more stark once connectd holds on to idle peers. Signed-off-by: Rusty Russell --- connectd/connectd.c | 18 +++++++++--------- connectd/connectd.h | 5 +++-- connectd/connectd_wire.csv | 6 +++--- connectd/multiplex.c | 10 +++++----- lightningd/channel.c | 2 +- lightningd/connect_control.c | 2 +- lightningd/opening_common.c | 2 +- wallet/test/run-wallet.c | 6 +++--- 8 files changed, 26 insertions(+), 25 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index dc0c2f798207..cb19a78fa824 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -311,7 +311,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->peer_in = NULL; peer->sent_to_peer = NULL; peer->urgent = false; - peer->told_to_close = false; + peer->ready_to_die = false; peer->peer_outq = msg_queue_new(peer, false); #if DEVELOPER @@ -1793,7 +1793,7 @@ static void try_connect_peer(struct daemon *daemon, /* If it's exiting now, we've raced: reconnect after */ if (tal_count(existing->subds) != 0 && existing->to_peer - && !existing->told_to_close) + && !existing->ready_to_die) return; } @@ -1893,7 +1893,7 @@ void peer_conn_closed(struct peer *peer) /* These should be closed already! */ assert(!peer->subds); assert(!peer->to_peer); - assert(peer->told_to_close); + assert(peer->ready_to_die); /* Tell gossipd to stop asking this peer gossip queries */ daemon_conn_send(peer->daemon->gossipd, @@ -1930,13 +1930,13 @@ static void cleanup_dead_peer(struct daemon *daemon, const struct node_id *id) close_peer_conn(peer); } -/* lightningd tells us a peer has disconnected. */ -static void peer_disconnected(struct daemon *daemon, const u8 *msg) +/* lightningd tells us a peer should be disconnected. */ +static void peer_discard(struct daemon *daemon, const u8 *msg) { struct node_id id; - if (!fromwire_connectd_peer_disconnected(msg, &id)) - master_badmsg(WIRE_CONNECTD_PEER_DISCONNECTED, msg); + if (!fromwire_connectd_discard_peer(msg, &id)) + master_badmsg(WIRE_CONNECTD_DISCARD_PEER, msg); cleanup_dead_peer(daemon, &id); } @@ -2001,8 +2001,8 @@ static struct io_plan *recv_req(struct io_conn *conn, connect_to_peer(daemon, msg); goto out; - case WIRE_CONNECTD_PEER_DISCONNECTED: - peer_disconnected(daemon, msg); + case WIRE_CONNECTD_DISCARD_PEER: + peer_discard(daemon, msg); goto out; case WIRE_CONNECTD_PEER_FINAL_MSG: diff --git a/connectd/connectd.h b/connectd/connectd.h index e75337f34af4..ce488a907433 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -59,8 +59,9 @@ struct peer { /* Final message to send to peer (and hangup) */ u8 *final_msg; - /* Set when we want to close. */ - bool told_to_close; + /* Set once lightningd says it's OK to close (subd tells it + * it's done). */ + bool ready_to_die; /* When socket has Nagle overridden */ bool urgent; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index de580c0e3620..577c882cd30b 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -71,9 +71,9 @@ msgdata,connectd_peer_connected,incoming,bool, msgdata,connectd_peer_connected,flen,u16, msgdata,connectd_peer_connected,features,u8,flen -# master -> connectd: peer has disconnected. -msgtype,connectd_peer_disconnected,2015 -msgdata,connectd_peer_disconnected,id,node_id, +# master -> connectd: peer no longer wanted, you can disconnect. +msgtype,connectd_discard_peer,2015 +msgdata,connectd_discard_peer,id,node_id, # master -> connectd: give message to peer and disconnect. msgtype,connectd_peer_final_msg,2003 diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 91f221b56440..49198176ddda 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -715,7 +715,7 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return read_hdr_from_peer(peer_conn, peer); /* Don't process packets while we're closing */ - if (peer->told_to_close) + if (peer->ready_to_die) return read_hdr_from_peer(peer_conn, peer); /* If we swallow this, just try again. */ @@ -825,14 +825,14 @@ static void destroy_subd(struct subd *subd) io_wake(&peer->peer_in); /* If no peer, finally time to close */ - if (!peer->to_peer && peer->told_to_close) + if (!peer->to_peer && peer->ready_to_die) peer_conn_closed(peer); } void close_peer_conn(struct peer *peer) { /* Make write_to_peer do flush after writing */ - peer->told_to_close = true; + peer->ready_to_die = true; /* Already dead? */ if (!peer->subds && !peer->to_peer) { @@ -883,7 +883,7 @@ static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) return; } - if (peer->told_to_close) + if (peer->ready_to_die) peer_conn_closed(peer); } @@ -905,7 +905,7 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) { - peer->told_to_close = true; + peer->ready_to_die = true; peer->final_msg = tal_dup_talarr(peer, u8, final_msg); if (!peer->subds) io_wake(peer->peer_outq); diff --git a/lightningd/channel.c b/lightningd/channel.c index 6976a83cdb98..b5b1fa2ad647 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -42,7 +42,7 @@ void channel_set_owner(struct channel *channel, struct subd *owner) channel->peer->connected = false; if (channel->peer->ld->connectd) { u8 *msg; - msg = towire_connectd_peer_disconnected( + msg = towire_connectd_discard_peer( NULL, &channel->peer->id); subd_send_msg(channel->peer->ld->connectd, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index a45a1cd3e6b0..321f9c2a2883 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -413,7 +413,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_INIT: case WIRE_CONNECTD_ACTIVATE: case WIRE_CONNECTD_CONNECT_TO_PEER: - case WIRE_CONNECTD_PEER_DISCONNECTED: + case WIRE_CONNECTD_DISCARD_PEER: case WIRE_CONNECTD_DEV_MEMLEAK: case WIRE_CONNECTD_PEER_FINAL_MSG: case WIRE_CONNECTD_PING: diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 542dfe2d2809..f57c35572e94 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -103,7 +103,7 @@ void uncommitted_channel_disconnect(struct uncommitted_channel *uc, enum log_level level, const char *desc) { - u8 *msg = towire_connectd_peer_disconnected(tmpctx, &uc->peer->id); + u8 *msg = towire_connectd_discard_peer(tmpctx, &uc->peer->id); log_(uc->log, level, NULL, false, "%s", desc); /* NULL when we're shutting down */ if (uc->peer->ld->connectd) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 71280026ae01..dd3d5ce66d5a 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -720,9 +720,9 @@ u8 *towire_channeld_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amo /* Generated stub for towire_channeld_sending_commitsig_reply */ u8 *towire_channeld_sending_commitsig_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_sending_commitsig_reply called!\n"); abort(); } -/* 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_discard_peer */ +u8 *towire_connectd_discard_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_connectd_discard_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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } From 10e36e073cb4c3bdb271d13e7fffe00359c3a00b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:56:29 +1030 Subject: [PATCH 0500/1530] openingd: disconnect from peer when an error occurs. openingd currently holds the connection to idle peers, but we're about to change that: it will only look after peers which are actively opening a connection. We can start this process by disconnecting whenever we have a negotiation failure. We could stay connected if we wanted to, but that would be up to connectd to decide. Right now it's easier if we disconnect from any idle peer once it's been active. Signed-off-by: Rusty Russell --- lightningd/opening_control.c | 27 ++++++------ openingd/openingd.c | 79 ++++++++++++++---------------------- openingd/openingd_wire.csv | 4 +- tests/test_connection.py | 14 +++---- 4 files changed, 51 insertions(+), 73 deletions(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index ed23a5ae0307..957f399b0cc6 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -561,22 +561,25 @@ opening_funder_failed_cancel_commands(struct uncommitted_channel *uc, */ uc->fc = tal_free(uc->fc); } -static void opening_funder_failed(struct subd *openingd, const u8 *msg, - struct uncommitted_channel *uc) + +static void openingd_failed(struct subd *openingd, const u8 *msg, + struct uncommitted_channel *uc) { char *desc; - if (!fromwire_openingd_funder_failed(msg, msg, &desc)) { + if (!fromwire_openingd_failed(msg, msg, &desc)) { log_broken(uc->log, - "bad OPENING_FUNDER_FAILED %s", + "bad OPENINGD_FAILED %s", tal_hex(tmpctx, msg)); - was_pending(command_fail(uc->fc->cmd, LIGHTNINGD, - "bad OPENING_FUNDER_FAILED %s", - tal_hex(uc->fc->cmd, msg))); + if (uc->fc) + was_pending(command_fail(uc->fc->cmd, LIGHTNINGD, + "bad OPENINGD_FAILED %s", + tal_hex(uc->fc->cmd, msg))); tal_free(uc); return; } + /* Noop if we're not funder. */ opening_funder_failed_cancel_commands(uc, desc); } @@ -853,14 +856,8 @@ static unsigned int openingd_msg(struct subd *openingd, } opening_funder_start_replied(openingd, msg, fds, uc->fc); return 0; - case WIRE_OPENINGD_FUNDER_FAILED: - if (!uc->fc) { - log_unusual(openingd->log, "Unexpected FUNDER_FAILED %s", - tal_hex(tmpctx, msg)); - tal_free(openingd); - return 0; - } - opening_funder_failed(openingd, msg, uc); + case WIRE_OPENINGD_FAILED: + openingd_failed(openingd, msg, uc); return 0; case WIRE_OPENINGD_FUNDEE: diff --git a/openingd/openingd.c b/openingd/openingd.c index ce6db7eee0ba..0370d186b76d 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -102,10 +102,9 @@ struct state { struct feature_set *our_features; }; -/*~ If we can't agree on parameters, we fail to open the channel. If we're - * the opener, we need to tell lightningd, otherwise it never really notices. */ -static void negotiation_aborted(struct state *state, bool am_opener, - const char *why) +/*~ If we can't agree on parameters, we fail to open the channel. + * Tell lightningd why. */ +static void NORETURN negotiation_aborted(struct state *state, const char *why) { status_debug("aborted opening negotiation: %s", why); /*~ The "billboard" (exposed as "status" in the JSON listpeers RPC @@ -116,29 +115,14 @@ static void negotiation_aborted(struct state *state, bool am_opener, * status. */ peer_billboard(true, why); - /* If necessary, tell master that funding failed. */ - if (am_opener) { - u8 *msg = towire_openingd_funder_failed(NULL, why); - wire_sync_write(REQ_FD, take(msg)); - } - - /* Default is no shutdown_scriptpubkey: free any leftover ones. */ - state->upfront_shutdown_script[LOCAL] - = tal_free(state->upfront_shutdown_script[LOCAL]); - state->upfront_shutdown_script[REMOTE] - = tal_free(state->upfront_shutdown_script[REMOTE]); - - /*~ Reset state. We keep gossipping with them, even though this open - * failed. */ - memset(&state->channel_id, 0, sizeof(state->channel_id)); - state->channel = tal_free(state->channel); - - state->channel_type = tal_free(state->channel_type); + /* Tell master that funding failed. */ + wire_sync_write(REQ_FD, take(towire_openingd_failed(NULL, why))); + exit(0); } /*~ For negotiation failures: we tell them the parameter we didn't like. */ -static void negotiation_failed(struct state *state, bool am_opener, - const char *fmt, ...) +static void NORETURN negotiation_failed(struct state *state, + const char *fmt, ...) { va_list ap; const char *errmsg; @@ -152,7 +136,7 @@ static void negotiation_failed(struct state *state, bool am_opener, "You gave bad parameters: %s", errmsg); peer_write(state->pps, take(msg)); - negotiation_aborted(state, am_opener, errmsg); + negotiation_aborted(state, errmsg); } /* We always set channel_reserve_satoshis to 1%, rounded down. */ @@ -177,7 +161,6 @@ static void set_reserve(struct state *state, const struct amount_sat dust_limit) /*~ Handle random messages we might get during opening negotiation, (eg. gossip) * returning the first non-handled one, or NULL if we aborted negotiation. */ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, - bool am_opener, const struct channel_id *alternate) { /* This is an event loop of its own. That's generally considered poor @@ -219,7 +202,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, tal_free(msg); continue; } - negotiation_aborted(state, am_opener, + negotiation_aborted(state, tal_fmt(tmpctx, "They sent %s", err)); /* Return NULL so caller knows to stop negotiating. */ @@ -384,7 +367,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) "Funding channel start: offered, now waiting for accept_channel"); /* ... since their reply should be immediate. */ - msg = opening_negotiate_msg(tmpctx, state, true, NULL); + msg = opening_negotiate_msg(tmpctx, state, NULL); if (!msg) return NULL; @@ -426,7 +409,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) if (accept_tlvs->channel_type && !featurebits_eq(accept_tlvs->channel_type, state->channel_type->features)) { - negotiation_failed(state, true, + negotiation_failed(state, "Return unoffered channel_type: %s", fmt_featurebits(tmpctx, accept_tlvs->channel_type)); @@ -447,7 +430,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) if (amount_sat_greater(state->remoteconf.dust_limit, state->localconf.channel_reserve)) { - negotiation_failed(state, true, + negotiation_failed(state, "dust limit %s" " would be above our reserve %s", type_to_string(tmpctx, struct amount_sat, @@ -468,7 +451,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) state->their_features, OPT_ANCHOR_OUTPUTS), &err_reason)) { - negotiation_failed(state, true, "%s", err_reason); + negotiation_failed(state, "%s", err_reason); return NULL; } @@ -579,7 +562,7 @@ static bool funder_finalize_channel_setup(struct state *state, if (!*tx) { /* This should not happen: we should never create channels we * can't afford the fees for after reserve. */ - negotiation_failed(state, true, + negotiation_failed(state, "Could not meet their fees and reserve: %s", err_reason); goto fail; } @@ -644,7 +627,7 @@ static bool funder_finalize_channel_setup(struct state *state, * transaction. Note that errors may refer to the temporary channel * id (state->channel_id), but success should refer to the new * "cid" */ - msg = opening_negotiate_msg(tmpctx, state, true, &cid); + msg = opening_negotiate_msg(tmpctx, state, &cid); if (!msg) goto fail; @@ -696,7 +679,7 @@ static bool funder_finalize_channel_setup(struct state *state, &state->first_per_commitment_point[LOCAL], LOCAL, direct_outputs, &err_reason); if (!*tx) { - negotiation_failed(state, true, + negotiation_failed(state, "Could not meet our fees and reserve: %s", err_reason); goto fail; } @@ -842,7 +825,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) state->our_features, state->their_features); if (!state->channel_type) { - negotiation_failed(state, false, + negotiation_failed(state, "Did not support channel_type %s", fmt_featurebits(tmpctx, open_tlvs->channel_type)); @@ -861,7 +844,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * that is unknown to the receiver. */ if (!bitcoin_blkid_eq(&chain_hash, &chainparams->genesis_blockhash)) { - negotiation_failed(state, false, + negotiation_failed(state, "Unknown chain-hash %s", type_to_string(tmpctx, struct bitcoin_blkid, @@ -879,7 +862,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) if (!feature_negotiated(state->our_features, state->their_features, OPT_LARGE_CHANNELS) && amount_sat_greater(state->funding_sats, chainparams->max_funding)) { - negotiation_failed(state, false, + negotiation_failed(state, "funding_satoshis %s too large", type_to_string(tmpctx, struct amount_sat, &state->funding_sats)); @@ -911,14 +894,14 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * unreasonably large. */ if (state->feerate_per_kw < state->min_feerate) { - negotiation_failed(state, false, + negotiation_failed(state, "feerate_per_kw %u below minimum %u", state->feerate_per_kw, state->min_feerate); return NULL; } if (state->feerate_per_kw > state->max_feerate) { - negotiation_failed(state, false, + negotiation_failed(state, "feerate_per_kw %u above maximum %u", state->feerate_per_kw, state->max_feerate); return NULL; @@ -938,7 +921,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) */ if (amount_sat_greater(state->remoteconf.dust_limit, state->localconf.channel_reserve)) { - negotiation_failed(state, false, + negotiation_failed(state, "Our channel reserve %s" " would be below their dust %s", type_to_string(tmpctx, struct amount_sat, @@ -949,7 +932,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) } if (amount_sat_greater(state->localconf.dust_limit, state->remoteconf.channel_reserve)) { - negotiation_failed(state, false, + negotiation_failed(state, "Our dust limit %s" " would be above their reserve %s", type_to_string(tmpctx, struct amount_sat, @@ -971,7 +954,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) state->their_features, OPT_ANCHOR_OUTPUTS), &err_reason)) { - negotiation_failed(state, false, "%s", err_reason); + negotiation_failed(state, "%s", err_reason); return NULL; } @@ -1001,7 +984,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) /* If they give us a reason to reject, do so. */ if (err_reason) { - negotiation_failed(state, false, "%s", err_reason); + negotiation_failed(state, "%s", err_reason); tal_free(err_reason); return NULL; } @@ -1044,7 +1027,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) "Incoming channel: accepted, now waiting for them to create funding tx"); /* This is a loop which handles gossip until we get a non-gossip msg */ - msg = opening_negotiate_msg(tmpctx, state, false, NULL); + msg = opening_negotiate_msg(tmpctx, state, NULL); if (!msg) return NULL; @@ -1130,7 +1113,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) LOCAL, NULL, &err_reason); /* This shouldn't happen either, AFAICT. */ if (!local_commit) { - negotiation_failed(state, false, + negotiation_failed(state, "Could not meet our fees and reserve: %s", err_reason); return NULL; } @@ -1189,7 +1172,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &state->first_per_commitment_point[REMOTE], REMOTE, direct_outputs, &err_reason); if (!remote_commit) { - negotiation_failed(state, false, + negotiation_failed(state, "Could not meet their fees and reserve: %s", err_reason); return NULL; } @@ -1353,7 +1336,7 @@ static u8 *handle_master_in(struct state *state) msg = towire_errorfmt(NULL, &state->channel_id, "Channel open canceled by us"); peer_write(state->pps, take(msg)); - negotiation_aborted(state, true, "Channel open canceled by RPC"); + negotiation_aborted(state, "Channel open canceled by RPC"); return NULL; case WIRE_OPENINGD_DEV_MEMLEAK: #if DEVELOPER @@ -1365,7 +1348,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_OPENINGD_FUNDER_REPLY: case WIRE_OPENINGD_FUNDER_START_REPLY: case WIRE_OPENINGD_FUNDEE: - case WIRE_OPENINGD_FUNDER_FAILED: + case WIRE_OPENINGD_FAILED: case WIRE_OPENINGD_GOT_OFFER: case WIRE_OPENINGD_GOT_OFFER_REPLY: case WIRE_OPENINGD_GOT_REESTABLISH: diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index b8f0fc5b9d6f..aa014e0104ab 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -104,8 +104,8 @@ msgdata,openingd_funder_complete,channel_type,channel_type, msgtype,openingd_funder_cancel,6013 # Openingd->master: we failed to negotiation channel -msgtype,openingd_funder_failed,6004 -msgdata,openingd_funder_failed,reason,wirestring, +msgtype,openingd_failed,6004 +msgdata,openingd_failed,reason,wirestring, # Openingd->master: they offered channel. # This gives their txid and info, means we can send funding_signed: we're done. diff --git a/tests/test_connection.py b/tests/test_connection.py index 0af389818511..9e1e2105c383 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -998,13 +998,9 @@ def test_funding_fail(node_factory, bitcoind): with pytest.raises(RpcError, match=r'to_self_delay \d+ larger than \d+'): l1.rpc.fundchannel(l2.info['id'], int(funds / 10)) - # dual-funded channels disconnect on failure - if not l1.config('experimental-dual-fund'): - assert only_one(l1.rpc.listpeers()['peers'])['connected'] - assert only_one(l2.rpc.listpeers()['peers'])['connected'] - else: - assert len(l1.rpc.listpeers()['peers']) == 0 - assert len(l2.rpc.listpeers()['peers']) == 0 + # channels disconnect on failure + assert len(l1.rpc.listpeers()['peers']) == 0 + assert len(l2.rpc.listpeers()['peers']) == 0 # Restart l2 without ridiculous locktime. del l2.daemon.opts['watchtime-blocks'] @@ -1217,7 +1213,9 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): l1.rpc.txdiscard(wrongaddr['txid']) l1.rpc.fundchannel_cancel(l2.info['id']) - # Should be able to 'restart' after canceling + + # Cancelling causes disconnection. + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount2 = 1000000 funding_addr = l1.rpc.fundchannel_start(l2.info['id'], amount2)['funding_address'] From 6cc9f37cab26a01bcc93ccda2fcc048a55e869a1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:56:29 +1030 Subject: [PATCH 0501/1530] connectd: handle connect vs closing race better. We would return success from connect even though the peer was closing; this is technically correct but fairly undesirable. Better is to pass every connect attempt to connectd, and have it block if the peer is exiting (and retry), otherwise tell us it's already connected. Signed-off-by: Rusty Russell --- connectd/connectd.c | 9 ++++++++- connectd/connectd_wire.csv | 4 ++++ lightningd/connect_control.c | 31 +++++++++++++++++++------------ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index cb19a78fa824..0ca58c4a23ae 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1793,8 +1793,14 @@ static void try_connect_peer(struct daemon *daemon, /* If it's exiting now, we've raced: reconnect after */ if (tal_count(existing->subds) != 0 && existing->to_peer - && !existing->ready_to_die) + && !existing->ready_to_die) { + /* Tell it it's already connected so it doesn't + * wait forever. */ + daemon_conn_send(daemon->master, + take(towire_connectd_peer_already_connected + (NULL, id))); return; + } } /* If we're trying to connect it right now, that's OK. */ @@ -2030,6 +2036,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_INIT_REPLY: case WIRE_CONNECTD_ACTIVATE_REPLY: case WIRE_CONNECTD_PEER_CONNECTED: + case WIRE_CONNECTD_PEER_ALREADY_CONNECTED: case WIRE_CONNECTD_RECONNECTED: case WIRE_CONNECTD_CONNECT_FAILED: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 577c882cd30b..a247252f6f93 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -81,6 +81,10 @@ msgdata,connectd_peer_final_msg,id,node_id, msgdata,connectd_peer_final_msg,len,u16, msgdata,connectd_peer_final_msg,msg,u8,len +# connectd->master: You said to connect, but we already were. +msgtype,connectd_peer_already_connected,2007 +msgdata,connectd_peer_already_connected,id,node_id, + # master -> connectd: do you have a memleak? msgtype,connectd_dev_memleak,2033 diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 321f9c2a2883..0c031c491db9 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -95,7 +95,6 @@ static struct command_result *json_connect(struct command *cmd, const char *name; struct wireaddr_internal *addr; const char *err_msg; - struct peer *peer; if (!param(cmd, buffer, params, p_req("id", param_tok, (const jsmntok_t **) &idtok), @@ -137,17 +136,6 @@ static struct command_result *json_connect(struct command *cmd, "Can't specify port without host"); } - /* If we know about peer, see if it's already connected. */ - peer = peer_by_id(cmd->ld, &id); - if (peer && peer->connected) { - log_debug(cmd->ld->log, "Already connected via %s", - type_to_string(tmpctx, struct wireaddr_internal, - &peer->addr)); - return connect_cmd_succeed(cmd, peer, - peer->connected_incoming, - &peer->addr); - } - /* Was there parseable host name? */ if (name) { /* Is there a port? */ @@ -325,6 +313,21 @@ void connect_succeeded(struct lightningd *ld, const struct peer *peer, } } +static void peer_already_connected(struct lightningd *ld, const u8 *msg) +{ + struct node_id id; + struct peer *peer; + + if (!fromwire_connectd_peer_already_connected(msg, &id)) + fatal("Bad msg %s from connectd", tal_hex(tmpctx, msg)); + + peer = peer_by_id(ld, &id); + if (peer) + connect_succeeded(ld, peer, + peer->connected_incoming, + &peer->addr); +} + static void peer_please_disconnect(struct lightningd *ld, const u8 *msg) { struct node_id id; @@ -436,6 +439,10 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd peer_connected(connectd->ld, msg, fds[0]); break; + case WIRE_CONNECTD_PEER_ALREADY_CONNECTED: + peer_already_connected(connectd->ld, msg); + break; + case WIRE_CONNECTD_CONNECT_FAILED: connect_failed(connectd->ld, msg); break; From deecedb0335bec03eb6ec4f7546d5a9bc36f193d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:56:30 +1030 Subject: [PATCH 0502/1530] connectd: tell lightningd when disconnect is complete. This avoids races in our tests where we assume it's sync (and is kind of nicer). Signed-off-by: Rusty Russell --- connectd/connectd.c | 5 ++ connectd/connectd_wire.csv | 4 ++ lightningd/channel.c | 11 +--- lightningd/connect_control.c | 4 ++ lightningd/lightningd.c | 1 + lightningd/lightningd.h | 2 + lightningd/opening_common.c | 2 - lightningd/peer_control.c | 63 ++++++++++++++++++--- lightningd/peer_control.h | 3 +- lightningd/test/run-invoice-select-inchan.c | 3 + tests/test_misc.py | 2 +- wallet/test/run-wallet.c | 3 + 12 files changed, 83 insertions(+), 20 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 0ca58c4a23ae..8a49ad35baa2 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1905,6 +1905,10 @@ void peer_conn_closed(struct peer *peer) daemon_conn_send(peer->daemon->gossipd, take(towire_gossipd_peer_gone(NULL, &peer->id))); + /* Tell lightningd it's really disconnected */ + daemon_conn_send(peer->daemon->master, + take(towire_connectd_peer_disconnect_done(NULL, + &peer->id))); /* Wake up in case there's a reconnecting peer waiting in io_wait. */ io_wake(peer); @@ -2043,6 +2047,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_PING_REPLY: case WIRE_CONNECTD_GOT_ONIONMSG_TO_US: case WIRE_CONNECTD_CUSTOMMSG_IN: + case WIRE_CONNECTD_PEER_DISCONNECT_DONE: break; } diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index a247252f6f93..e891e63f064a 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -71,6 +71,10 @@ msgdata,connectd_peer_connected,incoming,bool, msgdata,connectd_peer_connected,flen,u16, msgdata,connectd_peer_connected,features,u8,flen +# connectd -> master: peer disconnected. +msgtype,connectd_peer_disconnect_done,2006 +msgdata,connectd_peer_disconnect_done,id,node_id, + # master -> connectd: peer no longer wanted, you can disconnect. msgtype,connectd_discard_peer,2015 msgdata,connectd_discard_peer,id,node_id, diff --git a/lightningd/channel.c b/lightningd/channel.c index b5b1fa2ad647..94b9fc0e7d84 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -33,13 +33,7 @@ void channel_set_owner(struct channel *channel, struct subd *owner) if (old_owner) { subd_release_channel(old_owner, channel); if (channel->connected && !connects_to_peer(owner)) { - /* If shutting down, connectd no longer exists, - * and we should not transfer peer to connectd. - * Only transfer to connectd if connectd is - * there to be transferred to. - */ - assert(channel->peer->connected); - channel->peer->connected = false; + /* If shutting down, connectd no longer exists */ if (channel->peer->ld->connectd) { u8 *msg; msg = towire_connectd_discard_peer( @@ -47,7 +41,8 @@ void channel_set_owner(struct channel *channel, struct subd *owner) &channel->peer->id); subd_send_msg(channel->peer->ld->connectd, take(msg)); - } + } else + channel->peer->is_connected = false; } } channel->connected = connects_to_peer(owner); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 0c031c491db9..e7e6cdb80fc4 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -439,6 +439,10 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd peer_connected(connectd->ld, msg, fds[0]); break; + case WIRE_CONNECTD_PEER_DISCONNECT_DONE: + peer_disconnect_done(connectd->ld, msg); + break; + case WIRE_CONNECTD_PEER_ALREADY_CONNECTED: peer_already_connected(connectd->ld, msg); break; diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 241797405d0e..0702a024f9d9 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -194,6 +194,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) list_head_init(&ld->sendpay_commands); list_head_init(&ld->close_commands); list_head_init(&ld->ping_commands); + list_head_init(&ld->disconnect_commands); list_head_init(&ld->waitblockheight_commands); /*~ Tal also explicitly supports arrays: it stores the number of diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index c5150146c4c2..973db2a7c863 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -193,6 +193,8 @@ struct lightningd { struct list_head close_commands; /* Outstanding ping commands. */ struct list_head ping_commands; + /* Outstanding disconnect commands. */ + struct list_head disconnect_commands; /* Maintained by invoices.c */ struct invoices *invoices; diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index f57c35572e94..1b5551a2a5e6 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -193,8 +193,6 @@ void handle_reestablish(struct lightningd *ld, "Unknown channel for reestablish"); log_debug(ld->log, "Reestablish on UNKNOWN channel %s", type_to_string(tmpctx, struct channel_id, channel_id)); - if (peer) - peer->connected = false; /* Unless we're shutting down */ if (ld->connectd) subd_send_msg(ld->connectd, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index baa8f8d6fab3..35a90791fcf0 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -100,7 +100,7 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, peer->their_features = NULL; list_head_init(&peer->channels); peer->direction = node_id_idx(&peer->ld->id, &peer->id); - peer->connected = false; + peer->is_connected = false; #if DEVELOPER peer->ignore_htlcs = false; #endif @@ -1062,7 +1062,6 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa send_error: log_debug(ld->log, "Telling connectd to send error %s", tal_hex(tmpctx, error)); - peer->connected = false; /* Get connectd to send error and close. */ subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, @@ -1209,7 +1208,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) if (!peer) peer = new_peer(ld, 0, &id, &hook_payload->addr, hook_payload->incoming); - peer->connected = true; + peer->is_connected = true; tal_steal(peer, hook_payload); hook_payload->peer = peer; @@ -1240,6 +1239,44 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) plugin_hook_call_peer_connected(ld, hook_payload); } +struct disconnect_command { + struct list_node list; + /* Command structure. This is the parent of the close command. */ + struct command *cmd; + /* node being disconnected. */ + struct node_id id; +}; + +static void destroy_disconnect_command(struct disconnect_command *dc) +{ + list_del(&dc->list); +} + +void peer_disconnect_done(struct lightningd *ld, const u8 *msg) +{ + struct node_id id; + struct disconnect_command *i, *next; + struct peer *p; + + if (!fromwire_connectd_peer_disconnect_done(msg, &id)) + fatal("Connectd gave bad PEER_DISCONNECT_DONE message %s", + tal_hex(msg, msg)); + + /* If we still have peer, it's disconnected now */ + p = peer_by_id(ld, &id); + if (p) + p->is_connected = false; + + /* Wake any disconnect commands (removes self from list) */ + list_for_each_safe(&ld->disconnect_commands, i, next, list) { + if (!node_id_eq(&i->id, &id)) + continue; + + was_pending(command_success(i->cmd, + json_stream_success(i->cmd))); + } +} + static bool check_funding_details(const struct bitcoin_tx *tx, const u8 *wscript, struct amount_sat funding, @@ -1489,12 +1526,12 @@ static void json_add_peer(struct lightningd *ld, json_object_start(response, NULL); json_add_node_id(response, "id", &p->id); - json_add_bool(response, "connected", p->connected); + json_add_bool(response, "connected", p->is_connected); /* If it's not connected, features are unreliable: we don't * store them in the database, and they would only reflect * their features *last* time they connected. */ - if (p->connected) { + if (p->is_connected) { json_array_start(response, "netaddr"); json_add_string(response, NULL, type_to_string(tmpctx, @@ -1693,6 +1730,7 @@ static struct command_result *json_disconnect(struct command *cmd, const jsmntok_t *params) { struct node_id *id; + struct disconnect_command *dc; struct peer *peer; struct channel *channel; bool *force; @@ -1712,7 +1750,7 @@ static struct command_result *json_disconnect(struct command *cmd, if (*force) { channel_fail_reconnect(channel, "disconnect command force=true"); - return command_success(cmd, json_stream_success(cmd)); + goto wait_for_connectd; } return command_fail(cmd, LIGHTNINGD, "Peer is in state %s", channel_state_name(channel)); @@ -1720,14 +1758,23 @@ static struct command_result *json_disconnect(struct command *cmd, channel = peer_unsaved_channel(peer); if (channel) { channel_unsaved_close_conn(channel, "disconnect command"); - return command_success(cmd, json_stream_success(cmd)); + goto wait_for_connectd; } if (!peer->uncommitted_channel) { return command_fail(cmd, LIGHTNINGD, "Peer not connected"); } kill_uncommitted_channel(peer->uncommitted_channel, "disconnect command"); - return command_success(cmd, json_stream_success(cmd)); + +wait_for_connectd: + /* Connectd tells us when it's finally disconnected */ + dc = tal(cmd, struct disconnect_command); + dc->cmd = cmd; + dc->id = *id; + list_add_tail(&cmd->ld->disconnect_commands, &dc->list); + tal_add_destructor(dc, destroy_disconnect_command); + + return command_still_pending(cmd); } static const struct json_command disconnect_command = { diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index c6afe8edd318..a407d6c5b564 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -31,7 +31,7 @@ struct peer { struct list_head channels; /* Are we connected? */ - bool connected; + bool is_connected; /* Our (only) uncommitted channel, still opening. */ struct uncommitted_channel *uncommitted_channel; @@ -68,6 +68,7 @@ struct peer *peer_from_json(struct lightningd *ld, const jsmntok_t *peeridtok); void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd); +void peer_disconnect_done(struct lightningd *ld, const u8 *msg); /* Could be configurable. */ #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 09da34f9516b..a7ebce3d8311 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -207,6 +207,9 @@ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNE /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } +/* Generated stub for fromwire_connectd_peer_disconnect_done */ +bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_peer_disconnect_done called!\n"); abort(); } /* Generated stub for fromwire_dualopend_dev_memleak_reply */ bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_dualopend_dev_memleak_reply called!\n"); abort(); } diff --git a/tests/test_misc.py b/tests/test_misc.py index 376a68a1705a..a19b076b2963 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1303,7 +1303,7 @@ def test_reserve_enforcement(node_factory, executor): 'Peer transient failure in CHANNELD_NORMAL: channeld.*' ' CHANNEL_ERR_CHANNEL_CAPACITY_EXCEEDED' ) - assert only_one(l1.rpc.listpeers()['peers'])['connected'] is False + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is False) def test_ipv4_and_ipv6(node_factory): diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index dd3d5ce66d5a..259ebf03dada 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -151,6 +151,9 @@ bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void * /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } +/* Generated stub for fromwire_connectd_peer_disconnect_done */ +bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_peer_disconnect_done called!\n"); abort(); } /* Generated stub for fromwire_dualopend_dev_memleak_reply */ bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_dualopend_dev_memleak_reply called!\n"); abort(); } From 77b1087cdfaadd5b957a58e1bc13d964a71d28ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:56:30 +1030 Subject: [PATCH 0503/1530] lightningd: move notification of disconnect into when we hear from connectd. Simpler, and closes a potential race. Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 2 -- lightningd/opening_common.c | 1 - lightningd/peer_control.c | 5 +++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index f10fffe9b6b3..8ccfd06343d0 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -46,8 +46,6 @@ static void channel_disconnect(struct channel *channel, log_(channel->log, level, NULL, false, "%s", desc); channel_cleanup_commands(channel, desc); - notify_disconnect(channel->peer->ld, &channel->peer->id); - if (!reconnect) channel_set_owner(channel, NULL); else diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 1b5551a2a5e6..21bc0fb84182 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -110,7 +110,6 @@ void uncommitted_channel_disconnect(struct uncommitted_channel *uc, subd_send_msg(uc->peer->ld->connectd, msg); if (uc->fc && uc->fc->cmd) was_pending(command_fail(uc->fc->cmd, LIGHTNINGD, "%s", desc)); - notify_disconnect(uc->peer->ld, &uc->peer->id); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 35a90791fcf0..e3bfd1ca01d9 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -293,8 +293,6 @@ void channel_errmsg(struct channel *channel, bool warning, const u8 *err_for_them) { - notify_disconnect(channel->peer->ld, &channel->peer->id); - /* Clean up any in-progress open attempts */ channel_cleanup_commands(channel, desc); @@ -1267,6 +1265,9 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) if (p) p->is_connected = false; + /* Fire off plugin notifications */ + notify_disconnect(ld, &id); + /* Wake any disconnect commands (removes self from list) */ list_for_each_safe(&ld->disconnect_commands, i, next, list) { if (!node_id_eq(&i->id, &id)) From 2424b7dea899e11b33ee4da9f95836e058db4a0c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:57:29 +1030 Subject: [PATCH 0504/1530] connectd: hold peer until we're interested. Either because lightningd tells us it wants to talk, or because the peer says something about a channel. We also introduce a behavior change: we disconnect after a failed open. We might want to modify this later, but we it's a side-effect of openingd not holding onto idle connections. Signed-off-by: Rusty Russell --- connectd/connectd.c | 51 +++--- connectd/connectd.h | 3 + connectd/connectd_wire.csv | 15 +- connectd/multiplex.c | 119 +++++++++--- connectd/multiplex.h | 6 +- contrib/pyln-testing/pyln/testing/utils.py | 2 +- lightningd/channel.c | 1 + lightningd/channel.h | 3 + lightningd/connect_control.c | 10 +- lightningd/dual_open_control.c | 64 +++---- lightningd/dual_open_control.h | 3 +- lightningd/opening_common.c | 1 + lightningd/opening_common.h | 6 +- lightningd/opening_control.c | 41 +++-- lightningd/opening_control.h | 2 +- lightningd/peer_control.c | 189 +++++++++++++++++--- lightningd/peer_control.h | 3 +- lightningd/test/run-invoice-select-inchan.c | 22 ++- openingd/dualopend.c | 3 - openingd/openingd.c | 4 +- tests/test_closing.py | 3 +- tests/test_connection.py | 6 +- tests/test_misc.py | 6 + wallet/test/run-wallet.c | 14 +- 24 files changed, 429 insertions(+), 148 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 8a49ad35baa2..b2b07eaab3c4 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -312,6 +312,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->sent_to_peer = NULL; peer->urgent = false; peer->ready_to_die = false; + peer->active = false; peer->peer_outq = msg_queue_new(peer, false); #if DEVELOPER @@ -321,11 +322,6 @@ static struct peer *new_peer(struct daemon *daemon, peer->to_peer = conn; - /* Aim for connection to shuffle data back and forth: sets up - * peer->subds[0] */ - if (!multiplex_subd_setup(peer, fd_for_subd)) - return tal_free(peer); - /* Now we own it */ tal_steal(peer, peer->to_peer); peer_htable_add(&daemon->peers, peer); @@ -424,9 +420,9 @@ struct io_plan *peer_connected(struct io_conn *conn, /*~ daemon_conn is a message queue for inter-daemon communication: we * queue up the `connect_peer_connected` message to tell lightningd - * we have connected, and give the peer fd. */ + * we have connected. Once it says something interesting, we tell + * it that, too. */ daemon_conn_send(daemon->master, take(msg)); - daemon_conn_send_fd(daemon->master, subd_fd); /*~ Now we set up this connection to read/write from subd */ return multiplex_peer_setup(conn, peer); @@ -1791,7 +1787,7 @@ static void try_connect_peer(struct daemon *daemon, existing = peer_htable_get(&daemon->peers, id); if (existing) { /* If it's exiting now, we've raced: reconnect after */ - if (tal_count(existing->subds) != 0 + if ((tal_count(existing->subds) != 0 || !existing->active) && existing->to_peer && !existing->ready_to_die) { /* Tell it it's already connected so it doesn't @@ -1897,9 +1893,11 @@ void peer_conn_closed(struct peer *peer) struct connecting *connect = find_connecting(peer->daemon, &peer->id); /* These should be closed already! */ - assert(!peer->subds); + assert(tal_count(peer->subds) == 0); assert(!peer->to_peer); - assert(peer->ready_to_die); + assert(peer->ready_to_die || !peer->active); + + status_peer_debug(&peer->id, "peer_conn_closed"); /* Tell gossipd to stop asking this peer gossip queries */ daemon_conn_send(peer->daemon->gossipd, @@ -1923,32 +1921,24 @@ void peer_conn_closed(struct peer *peer) try_connect_one_addr(connect); } -/* A peer is gone: clean things up. */ -static void cleanup_dead_peer(struct daemon *daemon, const struct node_id *id) -{ - struct peer *peer; - - /* We should stay in sync with lightningd at all times. */ - peer = peer_htable_get(&daemon->peers, id); - if (!peer) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "peer_disconnected unknown peer: %s", - type_to_string(tmpctx, struct node_id, id)); - status_peer_debug(id, "disconnect"); - - /* When it's finished, it will call peer_conn_closed() */ - close_peer_conn(peer); -} - /* lightningd tells us a peer should be disconnected. */ static void peer_discard(struct daemon *daemon, const u8 *msg) { struct node_id id; + struct peer *peer; if (!fromwire_connectd_discard_peer(msg, &id)) master_badmsg(WIRE_CONNECTD_DISCARD_PEER, msg); - cleanup_dead_peer(daemon, &id); + /* We should stay in sync with lightningd, but this can happen + * under stress. */ + peer = peer_htable_get(&daemon->peers, &id); + if (!peer) + return; + status_peer_debug(&id, "disconnect"); + + /* When it's finished, it will call peer_conn_closed() */ + close_peer_conn(peer); } /* lightningd tells us to send a msg and disconnect. */ @@ -2031,6 +2021,10 @@ static struct io_plan *recv_req(struct io_conn *conn, send_custommsg(daemon, msg); goto out; + case WIRE_CONNECTD_PEER_MAKE_ACTIVE: + peer_make_active(daemon, msg); + goto out; + case WIRE_CONNECTD_DEV_MEMLEAK: #if DEVELOPER dev_connect_memleak(daemon, msg); @@ -2041,6 +2035,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_ACTIVATE_REPLY: case WIRE_CONNECTD_PEER_CONNECTED: case WIRE_CONNECTD_PEER_ALREADY_CONNECTED: + case WIRE_CONNECTD_PEER_ACTIVE: case WIRE_CONNECTD_RECONNECTED: case WIRE_CONNECTD_CONNECT_FAILED: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: diff --git a/connectd/connectd.h b/connectd/connectd.h index ce488a907433..825a21891b71 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -63,6 +63,9 @@ struct peer { * it's done). */ bool ready_to_die; + /* Has this ever been active? (i.e. ever had a subd attached?) */ + bool active; + /* When socket has Nagle overridden */ bool urgent; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index e891e63f064a..7af157794667 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -62,7 +63,7 @@ msgdata,connectd_connect_failed,failreason,wirestring, msgdata,connectd_connect_failed,seconds_to_delay,u32, msgdata,connectd_connect_failed,addrhint,?wireaddr_internal, -# Connectd -> master: we got a peer. Plus fd for peer daemon +# Connectd -> master: we got a peer. msgtype,connectd_peer_connected,2002 msgdata,connectd_peer_connected,id,node_id, msgdata,connectd_peer_connected,addr,wireaddr_internal, @@ -75,6 +76,18 @@ msgdata,connectd_peer_connected,features,u8,flen msgtype,connectd_peer_disconnect_done,2006 msgdata,connectd_peer_disconnect_done,id,node_id, +# Master -> connectd: make peer active immediately (we want to talk) +msgtype,connectd_peer_make_active,2004 +msgdata,connectd_peer_make_active,id,node_id, +msgdata,connectd_peer_make_active,channel_id,?channel_id, + +# Connectd -> master: peer said something interesting (or you said make_active) +# Plus fd for peer daemon. +msgtype,connectd_peer_active,2005 +msgdata,connectd_peer_active,id,node_id, +msgdata,connectd_peer_active,msgtype,?u16, +msgdata,connectd_peer_active,channel_id,?channel_id, + # master -> connectd: peer no longer wanted, you can disconnect. msgtype,connectd_discard_peer,2015 msgdata,connectd_discard_peer,id,node_id, diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 49198176ddda..eb384acee6e1 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -401,6 +401,67 @@ void send_custommsg(struct daemon *daemon, const u8 *msg) inject_peer_msg(peer, take(custommsg)); } +/* FIXME: fwd decl */ +static struct subd *multiplex_subd_setup(struct peer *peer, int *fd_for_subd); + +static struct subd *activate_peer(struct peer *peer, + const enum peer_wire *type, + const struct channel_id *channel_id) +{ + int fd_for_subd; + u16 t, *tp; + struct subd *subd; + + /* If it wasn't active before, it is now! */ + peer->active = true; + + subd = multiplex_subd_setup(peer, &fd_for_subd); + if (!subd) + return NULL; + + /* wire routines want a u16, not an enum */ + if (type) { + t = *type; + tp = &t; + } else { + tp = NULL; + } + + /* We tell lightningd to fire up a subdaemon to handle this! */ + daemon_conn_send(peer->daemon->master, + take(towire_connectd_peer_active(NULL, &peer->id, + tp, + channel_id))); + daemon_conn_send_fd(peer->daemon->master, fd_for_subd); + return subd; +} + +void peer_make_active(struct daemon *daemon, const u8 *msg) +{ + struct node_id id; + struct peer *peer; + struct channel_id *channel_id; + + if (!fromwire_connectd_peer_make_active(msg, msg, &id, &channel_id)) + master_badmsg(WIRE_CONNECTD_PEER_MAKE_ACTIVE, msg); + + /* Races can happen: this might be gone by now. */ + peer = peer_htable_get(&daemon->peers, &id); + if (!peer) + return; + + /* Could be disconnecting now */ + if (!peer->to_peer) + return; + + /* Could be made active already by receiving a message (esp reestablish!) */ + if (tal_count(peer->subds) != 0) + return; + + if (!activate_peer(peer, NULL, channel_id)) + tal_free(peer); +} + static void handle_ping_in(struct peer *peer, const u8 *msg) { u8 *pong; @@ -593,7 +654,7 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, msg = msg_dequeue(peer->peer_outq); /* Is it time to send final? */ - if (!msg && peer->final_msg && !peer->subds) { + if (!msg && peer->final_msg && tal_count(peer->subds) == 0) { /* OK, send this then close. */ msg = peer->final_msg; peer->final_msg = NULL; @@ -603,8 +664,10 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, /* Still nothing to send? */ if (!msg) { - /* We close once subds are all closed. */ - if (!peer->subds) { + /* We close once subds are all closed; or if we're not + active, when told to die. */ + if ((peer->active || peer->ready_to_die) + && tal_count(peer->subds) == 0) { set_closing_timer(peer, peer_conn); return io_sock_shutdown(peer_conn); } @@ -748,10 +811,19 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return read_hdr_from_peer(peer_conn, peer); } - /* If we don't find a subdaemon for this, discard and keep reading. */ + /* If we don't find a subdaemon for this, activate a new one. */ subd = find_subd(peer, &channel_id); - if (!subd) - return read_hdr_from_peer(peer_conn, peer); + if (!subd) { + struct channel_id channel_id; + enum peer_wire t = fromwire_peektype(decrypted); + bool has_channel_id = extract_channel_id(decrypted, &channel_id); + status_peer_debug(&peer->id, "Activating for message %s", + peer_wire_name(t)); + subd = activate_peer(peer, &t, + has_channel_id ? &channel_id : NULL); + if (!subd) + return io_close(peer_conn); + } /* Tell them to write. */ msg_enqueue(subd->outq, take(decrypted)); @@ -806,19 +878,18 @@ static void destroy_subd(struct subd *subd) struct peer *peer = subd->peer; size_t pos; + status_peer_debug(&peer->id, + "destroy_subd: %zu subds, to_peer conn %p, read_to_die = %u", + tal_count(peer->subds), peer->to_peer, + peer->ready_to_die); for (pos = 0; peer->subds[pos] != subd; pos++) assert(pos < tal_count(peer->subds)); tal_arr_remove(&peer->subds, pos); - /* Last one out frees array, sets to NULL as an indicator */ - if (tal_count(peer->subds) == 0) { - peer->subds = tal_free(peer->subds); - - /* In case they were waiting for this to send final_msg */ - if (peer->final_msg) - msg_wake(peer->peer_outq); - } + /* In case they were waiting for this to send final_msg */ + if (tal_count(peer->subds) == 0 && peer->final_msg) + msg_wake(peer->peer_outq); /* Make sure we try to keep reading from peer, so we know if * it hangs up! */ @@ -835,7 +906,7 @@ void close_peer_conn(struct peer *peer) peer->ready_to_die = true; /* Already dead? */ - if (!peer->subds && !peer->to_peer) { + if (tal_count(peer->subds) == 0 && !peer->to_peer) { peer_conn_closed(peer); return; } @@ -844,7 +915,7 @@ void close_peer_conn(struct peer *peer) msg_wake(peer->peer_outq); } -bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd) +static struct subd *multiplex_subd_setup(struct peer *peer, int *fd_for_subd) { int fds[2]; struct subd *subd; @@ -852,7 +923,7 @@ bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd) if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { status_broken("Failed to create socketpair: %s", strerror(errno)); - return false; + return NULL; } subd = tal(peer->subds, struct subd); @@ -868,7 +939,7 @@ bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd) tal_add_destructor(subd, destroy_subd); *fd_for_subd = fds[1]; - return true; + return subd; } static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) @@ -876,14 +947,15 @@ static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) assert(peer->to_peer == peer_conn); peer->to_peer = NULL; - /* Flush internal connections if not already. */ - if (peer->subds) { + /* Flush internal connections if any. */ + if (tal_count(peer->subds) != 0) { for (size_t i = 0; i < tal_count(peer->subds); i++) msg_wake(peer->subds[i]->outq); return; } - if (peer->ready_to_die) + /* If lightningd says we're ready, or we were never had a subd, finish */ + if (peer->ready_to_die || !peer->active) peer_conn_closed(peer); } @@ -898,6 +970,9 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, peer->expecting_pong = PONG_UNEXPECTED; set_ping_timer(peer); + /* This used to be in openingd; don't break tests. */ + status_peer_debug(&peer->id, "Handed peer, entering loop"); + return io_duplex(peer_conn, read_hdr_from_peer(peer_conn, peer), write_to_peer(peer_conn, peer)); @@ -907,7 +982,7 @@ void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) { peer->ready_to_die = true; peer->final_msg = tal_dup_talarr(peer, u8, final_msg); - if (!peer->subds) + if (tal_count(peer->subds) == 0) io_wake(peer->peer_outq); } diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 1c87ccb59a35..f389d88da77f 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -10,9 +10,6 @@ struct peer; struct io_conn; struct feature_set; -/* Set up peer->to_subd; sets fd_for_subd to pass to lightningd. */ -bool multiplex_subd_setup(struct peer *peer, int *fd_for_subd); - /* Take over peer_conn as peer->to_peer */ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, struct peer *peer); @@ -37,4 +34,7 @@ void send_manual_ping(struct daemon *daemon, const u8 *msg); /* When lightningd says to send a custom message (from a plugin) */ void send_custommsg(struct daemon *daemon, const u8 *msg); + +/* Lightningd wants to talk to you. */ +void peer_make_active(struct daemon *daemon, const u8 *msg); #endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index c47fffd98a22..29d8c2a31cd3 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1369,7 +1369,7 @@ def join_nodes(self, nodes, fundchannel=True, fundamount=FUNDAMOUNT, wait_for_an # getpeers. if not fundchannel: for src, dst in connections: - dst.daemon.wait_for_log(r'{}-.*-chan#[0-9]*: Handed peer, entering loop'.format(src.info['id'])) + dst.daemon.wait_for_log(r'{}-connectd: Handed peer, entering loop'.format(src.info['id'])) return bitcoind = nodes[0].bitcoin diff --git a/lightningd/channel.c b/lightningd/channel.c index 94b9fc0e7d84..23bd3ae1704a 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -205,6 +205,7 @@ struct open_attempt *new_channel_open_attempt(struct channel *channel) oa->our_upfront_shutdown_script = NULL; oa->cmd = NULL; oa->aborted = false; + oa->open_msg = NULL; return oa; } diff --git a/lightningd/channel.h b/lightningd/channel.h index cd0113317251..676b14a75990 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -67,6 +67,9 @@ struct open_attempt { struct command *cmd; struct amount_sat funding; const u8 *our_upfront_shutdown_script; + + /* First msg to send to dualopend (to make it create channel) */ + const u8 *open_msg; }; struct channel { diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index e7e6cdb80fc4..d08e52d1829b 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -419,6 +419,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_DISCARD_PEER: case WIRE_CONNECTD_DEV_MEMLEAK: case WIRE_CONNECTD_PEER_FINAL_MSG: + case WIRE_CONNECTD_PEER_MAKE_ACTIVE: case WIRE_CONNECTD_PING: case WIRE_CONNECTD_SEND_ONIONMSG: case WIRE_CONNECTD_CUSTOMMSG_OUT: @@ -434,9 +435,13 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd break; case WIRE_CONNECTD_PEER_CONNECTED: + peer_connected(connectd->ld, msg); + break; + + case WIRE_CONNECTD_PEER_ACTIVE: if (tal_count(fds) != 1) return 1; - peer_connected(connectd->ld, msg, fds[0]); + peer_active(connectd->ld, msg, fds[0]); break; case WIRE_CONNECTD_PEER_DISCONNECT_DONE: @@ -629,8 +634,7 @@ static struct command_result *json_sendcustommsg(struct command *cmd, type_to_string(cmd, struct node_id, dest)); } - /* FIXME: This won't work once connectd keeps peers */ - if (!peer_get_owning_subd(peer)) { + if (!peer->is_connected) { return command_fail(cmd, JSONRPC2_INVALID_REQUEST, "Peer is not connected: %s", type_to_string(cmd, struct node_id, dest)); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 8ccfd06343d0..c2a49845131f 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -2525,7 +2526,6 @@ static struct command_result *json_openchannel_init(struct command *cmd, struct open_attempt *oa; struct lease_rates *rates; struct command_result *res; - u8 *msg; if (!param(cmd, buffer, params, p_req("id", param_node_id, &id), @@ -2591,9 +2591,12 @@ static struct command_result *json_openchannel_init(struct command *cmd, } channel = peer_unsaved_channel(peer); - if (!channel || !channel->owner) - return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, - "Peer not connected"); + if (!channel) { + channel = new_unsaved_channel(peer, + peer->ld->config.fee_base, + peer->ld->config.fee_per_satoshi); + } + if (channel->open_attempt || !list_empty(&channel->inflights)) return command_fail(cmd, FUNDING_STATE_INVALID, @@ -2670,7 +2673,7 @@ static struct command_result *json_openchannel_init(struct command *cmd, } else our_upfront_shutdown_script_wallet_index = NULL; - msg = towire_dualopend_opener_init(NULL, + oa->open_msg = towire_dualopend_opener_init(oa, psbt, *amount, oa->our_upfront_shutdown_script, our_upfront_shutdown_script_wallet_index, @@ -2682,7 +2685,10 @@ static struct command_result *json_openchannel_init(struct command *cmd, false, rates); - subd_send_msg(channel->owner, take(msg)); + /* Tell connectd to hand us this so we can start dualopend */ + subd_send_msg(peer->ld->connectd, + take(towire_connectd_peer_make_active(NULL, &peer->id, + NULL))); return command_still_pending(cmd); } @@ -3056,7 +3062,6 @@ static struct command_result *json_queryrates(struct command *cmd, struct wally_psbt *psbt; struct open_attempt *oa; u32 *our_upfront_shutdown_script_wallet_index; - u8 *msg; struct command_result *res; if (!param(cmd, buffer, params, @@ -3077,6 +3082,10 @@ static struct command_result *json_queryrates(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } + if (!peer->is_connected) + return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, + "Peer not connected"); + /* We can't query rates for a peer we have a channel with */ channel = peer_active_channel(peer); if (channel) @@ -3086,9 +3095,12 @@ static struct command_result *json_queryrates(struct command *cmd, channel_state_name(channel)); channel = peer_unsaved_channel(peer); - if (!channel || !channel->owner) - return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, - "Peer not connected"); + if (!channel) { + channel = new_unsaved_channel(peer, + peer->ld->config.fee_base, + peer->ld->config.fee_per_satoshi); + } + if (channel->open_attempt || !list_empty(&channel->inflights)) return command_fail(cmd, FUNDING_STATE_INVALID, @@ -3140,7 +3152,7 @@ static struct command_result *json_queryrates(struct command *cmd, } else our_upfront_shutdown_script_wallet_index = NULL; - msg = towire_dualopend_opener_init(NULL, + oa->open_msg = towire_dualopend_opener_init(oa, psbt, *amount, oa->our_upfront_shutdown_script, our_upfront_shutdown_script_wallet_index, @@ -3152,7 +3164,10 @@ static struct command_result *json_queryrates(struct command *cmd, true, NULL); - subd_send_msg(channel->owner, take(msg)); + /* Tell connectd to hand us this so we can start dualopend */ + subd_send_msg(peer->ld->connectd, + take(towire_connectd_peer_make_active(NULL, &peer->id, + NULL))); return command_still_pending(cmd); } @@ -3212,9 +3227,9 @@ AUTODATA(json_command, &openchannel_signed_command); AUTODATA(json_command, &openchannel_bump_command); AUTODATA(json_command, &openchannel_abort_command); -static void start_fresh_dualopend(struct peer *peer, - struct peer_fd *peer_fd, - struct channel *channel) +bool peer_start_dualopend(struct peer *peer, + struct peer_fd *peer_fd, + struct channel *channel) { int hsmfd; u32 max_to_self_delay; @@ -3242,7 +3257,7 @@ static void start_fresh_dualopend(struct peer *peer, channel_internal_error(channel, "Running lightningd_dualopend: %s", strerror(errno)); - return; + return false; } channel_config(peer->ld, &channel->our_config, @@ -3268,7 +3283,7 @@ static void start_fresh_dualopend(struct peer *peer, &channel->local_funding_pubkey, channel->minimum_depth); subd_send_msg(channel->owner, take(msg)); - + return true; } void peer_restart_dualopend(struct peer *peer, @@ -3284,7 +3299,7 @@ void peer_restart_dualopend(struct peer *peer, u8 *msg; if (channel_unsaved(channel)) { - start_fresh_dualopend(peer, peer_fd, channel); + peer_start_dualopend(peer, peer_fd, channel); return; } hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->dbid, @@ -3374,16 +3389,3 @@ void peer_restart_dualopend(struct peer *peer, subd_send_msg(channel->owner, take(msg)); } - -void peer_start_dualopend(struct peer *peer, struct peer_fd *peer_fd) -{ - struct channel *channel; - - /* And we never touch this. */ - assert(!peer_unsaved_channel(peer)); - channel = new_unsaved_channel(peer, - peer->ld->config.fee_base, - peer->ld->config.fee_per_satoshi); - - start_fresh_dualopend(peer, peer_fd, channel); -} diff --git a/lightningd/dual_open_control.h b/lightningd/dual_open_control.h index 741a65a0e29b..aa623659fa8c 100644 --- a/lightningd/dual_open_control.h +++ b/lightningd/dual_open_control.h @@ -6,7 +6,8 @@ struct peer_fd; -void peer_start_dualopend(struct peer *peer, struct peer_fd *peer_fd); +bool peer_start_dualopend(struct peer *peer, struct peer_fd *peer_fd, + struct channel *channel); void peer_restart_dualopend(struct peer *peer, struct peer_fd *peer_fd, diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 21bc0fb84182..81da4583737b 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -71,6 +71,7 @@ new_uncommitted_channel(struct peer *peer) tal_add_destructor(uc, destroy_uncommitted_channel); uc->got_offer = false; + uc->open_daemon = NULL; return uc; } diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index 021532493678..4dc609f4df9c 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -87,6 +87,9 @@ struct funding_channel { /* Whether or not this is in the middle of getting funded */ bool inflight; + /* Initial openingd_funder_start msg */ + const u8 *open_msg; + /* Any commands trying to cancel us. */ struct command **cancels; @@ -95,8 +98,7 @@ struct funding_channel { struct peer_fd *peer_fd; }; -struct uncommitted_channel * -new_uncommitted_channel(struct peer *peer); +struct uncommitted_channel *new_uncommitted_channel(struct peer *peer); void opend_channel_errmsg(struct uncommitted_channel *uc, struct peer_fd *peer_fd, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 957f399b0cc6..10f5563255e1 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -894,7 +895,7 @@ static unsigned int openingd_msg(struct subd *openingd, return 0; } -void peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) +bool peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) { int hsmfd; u32 max_to_self_delay; @@ -902,9 +903,9 @@ void peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) struct uncommitted_channel *uc; const u8 *msg; - assert(!peer->uncommitted_channel); - - uc = peer->uncommitted_channel = new_uncommitted_channel(peer); + assert(peer->uncommitted_channel); + uc = peer->uncommitted_channel; + assert(!uc->open_daemon); hsmfd = hsm_get_client_fd(peer->ld, &uc->peer->id, uc->dbid, HSM_CAP_COMMITMENT_POINT @@ -925,7 +926,7 @@ void peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) "Running lightning_openingd: %s", strerror(errno))); tal_free(uc); - return; + return false; } channel_config(peer->ld, &uc->our_config, @@ -954,6 +955,7 @@ void peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) feerate_max(peer->ld, NULL), IFDEV(peer->ld->dev_force_tmp_channel_id, NULL)); subd_send_msg(uc->open_daemon, take(msg)); + return true; } static struct command_result *json_fundchannel_complete(struct command *cmd, @@ -986,12 +988,15 @@ static struct command_result *json_fundchannel_complete(struct command *cmd, return command_fail(cmd, LIGHTNINGD, "Peer already %s", channel_state_name(channel)); - if (!peer->uncommitted_channel) + if (!peer->is_connected) return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, "Peer not connected"); - if (!peer->uncommitted_channel->fc || !peer->uncommitted_channel->fc->inflight) + if (!peer->uncommitted_channel + || !peer->uncommitted_channel->fc + || !peer->uncommitted_channel->fc->inflight) return command_fail(cmd, LIGHTNINGD, "No channel funding in progress."); + if (peer->uncommitted_channel->fc->cmd) return command_fail(cmd, LIGHTNINGD, "Channel funding in progress."); @@ -1082,6 +1087,8 @@ static struct command_result *json_fundchannel_cancel(struct command *cmd, return command_still_pending(cmd); } + log_debug(cmd->ld->log, "fundchannel_cancel no uncommitted_channel!"); + /* Handle `fundchannel_cancel` after `fundchannel_complete`. */ return cancel_channel_before_broadcast(cmd, peer); } @@ -1101,7 +1108,6 @@ static struct command_result *json_fundchannel_start(struct command *cmd, bool *announce_channel; u32 *feerate_per_kw; - u8 *msg = NULL; struct amount_sat *amount; struct amount_msat *push_msat; u32 *upfront_shutdown_script_wallet_index; @@ -1154,6 +1160,10 @@ static struct command_result *json_fundchannel_start(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } + if (!peer->is_connected) + return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, + "Peer not connected"); + channel = peer_active_channel(peer); if (channel) { return command_fail(cmd, LIGHTNINGD, "Peer already %s", @@ -1170,9 +1180,10 @@ static struct command_result *json_fundchannel_start(struct command *cmd, " must use `openchannel_init` not" " `fundchannel_start`."); - return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, - "Peer not connected"); - } + log_debug(cmd->ld->log, "fundchannel_start: allocating uncommitted_channel"); + peer->uncommitted_channel = new_uncommitted_channel(peer); + } else + log_debug(cmd->ld->log, "fundchannel_start: reusing uncommitted_channel"); if (peer->uncommitted_channel->fc) { return command_fail(cmd, LIGHTNINGD, "Already funding channel"); @@ -1228,7 +1239,8 @@ static struct command_result *json_fundchannel_start(struct command *cmd, } else upfront_shutdown_script_wallet_index = NULL; - msg = towire_openingd_funder_start(NULL, + fc->open_msg + = towire_openingd_funder_start(fc, *amount, fc->push, fc->our_upfront_shutdown_script, @@ -1236,7 +1248,10 @@ static struct command_result *json_fundchannel_start(struct command *cmd, *feerate_per_kw, fc->channel_flags); - subd_send_msg(peer->uncommitted_channel->open_daemon, take(msg)); + /* Tell connectd to make this active; when it does, we can continue */ + subd_send_msg(peer->ld->connectd, + take(towire_connectd_peer_make_active(NULL, &peer->id, + NULL))); return command_still_pending(cmd); } diff --git a/lightningd/opening_control.h b/lightningd/opening_control.h index f7860d9501e2..a8f8d982a73a 100644 --- a/lightningd/opening_control.h +++ b/lightningd/opening_control.h @@ -14,7 +14,7 @@ struct uncommitted_channel; void json_add_uncommitted_channel(struct json_stream *response, const struct uncommitted_channel *uc); -void peer_start_openingd(struct peer *peer, +bool peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd); struct subd *peer_get_owning_subd(struct peer *peer); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e3bfd1ca01d9..28dc35acc896 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -944,7 +944,6 @@ struct peer_connected_hook_payload { struct wireaddr *remote_addr; bool incoming; struct peer *peer; - struct peer_fd *peer_fd; u8 *error; }; @@ -1028,11 +1027,6 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa } case DUALOPEND_OPEN_INIT: case DUALOPEND_AWAITING_LOCKIN: - assert(!channel->owner); - channel->peer->addr = addr; - channel->peer->connected_incoming = payload->incoming; - peer_restart_dualopend(peer, payload->peer_fd, channel); - return; case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: case CHANNELD_SHUTTING_DOWN: @@ -1040,31 +1034,31 @@ 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->peer_fd, NULL, true, - NULL); - return; + goto make_active; } abort(); } /* If we get here, it means we have no channel */ assert(!channel); - if (feature_negotiated(ld->our_features, - peer->their_features, - OPT_DUAL_FUND)) { - peer_start_dualopend(peer, payload->peer_fd); - } else - peer_start_openingd(peer, payload->peer_fd); return; send_error: - log_debug(ld->log, "Telling connectd to send error %s", - tal_hex(tmpctx, error)); + log_peer_debug(ld->log, &peer->id, "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, error))); - tal_free(payload->peer_fd); + return; + +make_active: + log_peer_debug(ld->log, &peer->id, + "Telling connectd to make active, state %s", + channel_state_name(channel)); + subd_send_msg(ld->connectd, + take(towire_connectd_peer_make_active(NULL, &peer->id, + &channel->cid))); } static bool @@ -1179,8 +1173,8 @@ REGISTER_PLUGIN_HOOK(peer_connected, struct peer_connected_hook_payload *); /* Connectd tells us a peer has connected: it never hands us duplicates, since - * it holds them until we say peer_died. */ -void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) + * it holds them until we say peer_disconnected. */ +void peer_connected(struct lightningd *ld, const u8 *msg) { struct node_id id; u8 *their_features; @@ -1198,8 +1192,6 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", tal_hex(msg, msg)); - hook_payload->peer_fd = new_peer_fd(hook_payload, peer_fd); - /* If we're already dealing with this peer, hand off to correct * subdaemon. Otherwise, we'll hand to openingd to wait there. */ peer = peer_by_id(ld, &id); @@ -1237,6 +1229,137 @@ void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd) plugin_hook_call_peer_connected(ld, hook_payload); } +/* connectd tells us a peer has an interesting message, and hands us an + * fd to give to the correct subdaemon. Unlike peer_connected, this is racy: + * we might have just told it to disconnect peer. */ +void peer_active(struct lightningd *ld, const u8 *msg, int fd) +{ + struct node_id id; + u16 *msgtype; + struct channel *channel; + struct channel_id *channel_id; + struct peer *peer; + bool dual_fund; + u8 *error; + struct peer_fd *peer_fd = new_peer_fd(tmpctx, fd); + + /* FIXME: Use msgtype to determine what to do! */ + if (!fromwire_connectd_peer_active(msg, msg, &id, &msgtype, &channel_id)) + fatal("Connectd gave bad CONNECTD_PEER_ACTIVE message %s", + tal_hex(msg, msg)); + + peer = peer_by_id(ld, &id); + if (!peer) { + /* This race is possible, but I want to see it in CI. */ + log_broken(ld->log, "Unknown active peer %s", + type_to_string(tmpctx, struct node_id, &id)); + return; + } + + channel = peer_active_channel(peer); + + /* It might be v2 opening, though, since we hang onto these */ + if (!channel) + channel = peer_unsaved_channel(peer); + + if (channel) { + switch (channel->state) { + case ONCHAIN: + case FUNDING_SPEND_SEEN: + case CLOSINGD_COMPLETE: + /* Channel is supposed to be active!*/ + abort(); + case CLOSED: + /* Channel should not have been loaded */ + abort(); + + /* We consider this "active" but we only send an error */ + case AWAITING_UNILATERAL: { + /* channel->error is not saved in db, so this can + * happen if we restart. */ + error = towire_errorfmt(tmpctx, &channel->cid, + "Awaiting unilateral close"); + goto send_error; + } + case DUALOPEND_OPEN_INIT: + /* We asked for this, to open? */ + if (!msgtype + && channel->open_attempt + && channel->open_attempt->open_msg) { + if (peer_start_dualopend(peer, peer_fd, channel)) + subd_send_msg(channel->owner, channel->open_attempt->open_msg); + return; + } + /* Fall through. */ + case DUALOPEND_AWAITING_LOCKIN: + assert(!channel->owner); + peer_restart_dualopend(peer, peer_fd, channel); + return; + case CHANNELD_AWAITING_LOCKIN: + case CHANNELD_NORMAL: + case CHANNELD_SHUTTING_DOWN: + case CLOSINGD_SIGEXCHANGE: + assert(!channel->owner); + peer_start_channeld(channel, + peer_fd, + NULL, true, + NULL); + return; + } + abort(); + } + + dual_fund = feature_negotiated(ld->our_features, + peer->their_features, + OPT_DUAL_FUND); + + /* Did we ask for this? */ + if (!msgtype) { + /* If it was dual_fund, it will have peer_unsaved_channel above */ + if (dual_fund) { + log_broken(ld->log, "Unsolicited active df peer %s?", + type_to_string(tmpctx, struct node_id, + &peer->id)); + } else { + const struct uncommitted_channel *uc + = peer->uncommitted_channel; + + if (!uc->open_daemon + && uc->fc + && uc->fc->open_msg) { + if (peer_start_openingd(peer, peer_fd)) { + subd_send_msg(uc->open_daemon, + uc->fc->open_msg); + } + } else { + log_broken(ld->log, "Unsolicited active peer %s?", + type_to_string(tmpctx, struct node_id, + &peer->id)); + } + } + } else { + /* OK, it's unsolicited. What kind of open do they want? */ + if (dual_fund) { + channel = new_unsaved_channel(peer, + peer->ld->config.fee_base, + peer->ld->config.fee_per_satoshi); + peer_start_dualopend(peer, peer_fd, channel); + } else { + peer->uncommitted_channel = new_uncommitted_channel(peer); + peer_start_openingd(peer, peer_fd); + } + } + return; + +send_error: + log_peer_debug(ld->log, &peer->id, "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, + error))); +} + struct disconnect_command { struct list_node list; /* Command structure. This is the parent of the close command. */ @@ -1262,8 +1385,13 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) /* If we still have peer, it's disconnected now */ p = peer_by_id(ld, &id); - if (p) + if (p) { p->is_connected = false; + /* If we only cared about peer because of connectd, free it. */ + if (list_empty(&p->channels) && !p->uncommitted_channel) { + tal_free(p); + } + } /* Fire off plugin notifications */ notify_disconnect(ld, &id); @@ -1744,6 +1872,9 @@ static struct command_result *json_disconnect(struct command *cmd, peer = peer_by_id(cmd->ld, id); if (!peer) { + return command_fail(cmd, LIGHTNINGD, "Unknown peer"); + } + if (!peer->is_connected) { return command_fail(cmd, LIGHTNINGD, "Peer not connected"); } channel = peer_active_channel(peer); @@ -1761,11 +1892,15 @@ static struct command_result *json_disconnect(struct command *cmd, channel_unsaved_close_conn(channel, "disconnect command"); goto wait_for_connectd; } - if (!peer->uncommitted_channel) { - return command_fail(cmd, LIGHTNINGD, "Peer not connected"); + if (peer->uncommitted_channel) { + kill_uncommitted_channel(peer->uncommitted_channel, + "disconnect command"); + goto wait_for_connectd; } - kill_uncommitted_channel(peer->uncommitted_channel, - "disconnect command"); + + /* It's just sitting in connectd. */ + subd_send_msg(cmd->ld->connectd, + take(towire_connectd_discard_peer(NULL, id))); wait_for_connectd: /* Connectd tells us when it's finally disconnected */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index a407d6c5b564..5f006466a888 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -67,8 +67,9 @@ struct peer *peer_from_json(struct lightningd *ld, const char *buffer, const jsmntok_t *peeridtok); -void peer_connected(struct lightningd *ld, const u8 *msg, int peer_fd); +void peer_connected(struct lightningd *ld, const u8 *msg); void peer_disconnect_done(struct lightningd *ld, const u8 *msg); +void peer_active(struct lightningd *ld, const u8 *msg, int peer_fd); /* Could be configurable. */ #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index a7ebce3d8311..02d4d4964eac 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -204,6 +204,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_channeld_dev_memleak_reply */ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } +/* Generated stub for fromwire_connectd_peer_active */ +bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id **channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_peer_active called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } @@ -460,6 +463,14 @@ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, struct timerel expire UNNEEDED, void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "new_reltimer_ called!\n"); abort(); } +/* Generated stub for new_uncommitted_channel */ +struct uncommitted_channel *new_uncommitted_channel(struct peer *peer UNNEEDED) +{ fprintf(stderr, "new_uncommitted_channel called!\n"); abort(); } +/* Generated stub for new_unsaved_channel */ +struct channel *new_unsaved_channel(struct peer *peer UNNEEDED, + u32 feerate_base UNNEEDED, + u32 feerate_ppm UNNEEDED) +{ fprintf(stderr, "new_unsaved_channel called!\n"); abort(); } /* Generated stub for node_id_cmp */ int node_id_cmp(const struct node_id *a UNNEEDED, const struct node_id *b UNNEEDED) { fprintf(stderr, "node_id_cmp called!\n"); abort(); } @@ -586,10 +597,11 @@ void peer_start_channeld(struct channel *channel 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 peer_fd *peer_fd UNNEEDED) +bool peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED, + struct channel *channel UNNEEDED) { fprintf(stderr, "peer_start_dualopend called!\n"); abort(); } /* Generated stub for peer_start_openingd */ -void peer_start_openingd(struct peer *peer UNNEEDED, +bool peer_start_openingd(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } /* Generated stub for peer_unsaved_channel */ @@ -640,9 +652,15 @@ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channeld_dev_reenable_commit */ u8 *towire_channeld_dev_reenable_commit(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_reenable_commit called!\n"); abort(); } +/* Generated stub for towire_connectd_discard_peer */ +u8 *towire_connectd_discard_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_connectd_discard_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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_make_active */ +u8 *towire_connectd_peer_make_active(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_make_active called!\n"); abort(); } /* Generated stub for towire_dualopend_dev_memleak */ u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_dualopend_dev_memleak called!\n"); abort(); } diff --git a/openingd/dualopend.c b/openingd/dualopend.c index afc0d68fa61b..b8ac657fc358 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3932,9 +3932,6 @@ int main(int argc, char *argv[]) * N'th per-commitment point. But since N=0, it won't give us one. */ assert(none == NULL); - /*~ Turns out this is useful for testing, to make sure we're ready. */ - status_debug("Handed peer, entering loop"); - /*~ We manually run a little poll() loop here. With only two fds */ pollfd[0].fd = REQ_FD; pollfd[0].events = POLLIN; diff --git a/openingd/openingd.c b/openingd/openingd.c index 0370d186b76d..7bcff4de893d 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -313,6 +313,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) struct tlv_accept_channel_tlvs *accept_tlvs; char *err_reason; + status_debug("funder_channel_start"); if (!setup_channel_funder(state)) return NULL; @@ -1428,9 +1429,6 @@ int main(int argc, char *argv[]) * N'th per-commitment point. But since N=0, it won't give us one. */ assert(none == NULL); - /*~ Turns out this is useful for testing, to make sure we're ready. */ - status_debug("Handed peer, entering loop"); - /*~ We manually run a little poll() loop here. With only three fds */ pollfd[0].fd = REQ_FD; pollfd[0].events = POLLIN; diff --git a/tests/test_closing.py b/tests/test_closing.py index 705cac755c57..69db8c3f1cab 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3448,7 +3448,8 @@ def test_you_forgot_closed_channel(node_factory, executor): assert only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CLOSINGD_SIGEXCHANGE' # l1 reconnects, it should succeed. - l1.rpc.disconnect(l2.info['id'], force=True) + if only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']: + l1.rpc.disconnect(l2.info['id'], force=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) fut.result(TIMEOUT) diff --git a/tests/test_connection.py b/tests/test_connection.py index 9e1e2105c383..3c9117cdc4cf 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -409,7 +409,7 @@ def test_remote_disconnect(node_factory): l1, l2 = node_factory.get_nodes(2) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - assert l2.rpc.listpeers()['peers'] != [] + wait_for(lambda: l2.rpc.listpeers()['peers'] != []) l2.rpc.disconnect(l1.info['id']) # l1 should notice! @@ -2576,9 +2576,9 @@ def test_disconnectpeer(node_factory, bitcoind): wait_for(lambda: l2.rpc.getpeer(l1.info['id']) is None) # Make sure you cannot disconnect after disconnecting - with pytest.raises(RpcError, match=r'Peer not connected'): + with pytest.raises(RpcError, match=r'Unknown peer'): l1.rpc.disconnect(l2.info['id']) - with pytest.raises(RpcError, match=r'Peer not connected'): + with pytest.raises(RpcError, match=r'Unknown peer'): l2.rpc.disconnect(l1.info['id']) # Fund channel l1 -> l3 diff --git a/tests/test_misc.py b/tests/test_misc.py index a19b076b2963..43a318b938fa 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2337,6 +2337,7 @@ def test_listforwards(node_factory, bitcoind): assert len(c24_forwards) == 1 +@pytest.mark.openchannel('v1') def test_version_reexec(node_factory, bitcoind): badopeningd = os.path.join(os.path.dirname(__file__), "plugins", "badopeningd.sh") version = subprocess.check_output(['lightningd/lightningd', @@ -2358,7 +2359,12 @@ def test_version_reexec(node_factory, bitcoind): 'fff6')) # type f.write(bytes('badversion\0', encoding='utf8')) + # Opening a channel will fire subd. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + try: + l1.fundchannel(l2) + except RpcError: + pass l1.daemon.wait_for_log("openingd.*version 'badversion' not '{}': restarting".format(version)) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 259ebf03dada..a38fb1808466 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -148,6 +148,9 @@ bool fromwire_channeld_offer_htlc_reply(const tal_t *ctx UNNEEDED, const void *p /* Generated stub for fromwire_channeld_sending_commitsig */ bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_sending_commitsig called!\n"); abort(); } +/* Generated stub for fromwire_connectd_peer_active */ +bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id **channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_peer_active called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } @@ -476,6 +479,9 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, /* Generated stub for new_peer_fd */ struct peer_fd *new_peer_fd(const tal_t *ctx UNNEEDED, int peer_fd UNNEEDED) { fprintf(stderr, "new_peer_fd called!\n"); abort(); } +/* Generated stub for new_uncommitted_channel */ +struct uncommitted_channel *new_uncommitted_channel(struct peer *peer UNNEEDED) +{ fprintf(stderr, "new_uncommitted_channel called!\n"); abort(); } /* Generated stub for notify_chain_mvt */ void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) { fprintf(stderr, "notify_chain_mvt called!\n"); abort(); } @@ -631,10 +637,11 @@ void peer_start_channeld(struct channel *channel 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 peer_fd *peer_fd UNNEEDED) +bool peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED, + struct channel *channel UNNEEDED) { fprintf(stderr, "peer_start_dualopend called!\n"); abort(); } /* Generated stub for peer_start_openingd */ -void peer_start_openingd(struct peer *peer UNNEEDED, +bool peer_start_openingd(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ @@ -729,6 +736,9 @@ u8 *towire_connectd_discard_peer(const tal_t *ctx UNNEEDED, const struct node_id /* 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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_make_active */ +u8 *towire_connectd_peer_make_active(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_make_active called!\n"); abort(); } /* Generated stub for towire_dualopend_dev_memleak */ u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_dualopend_dev_memleak called!\n"); abort(); } From 57263a3eb22fd58a31801d7e9a3b043c456bcf55 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:57:30 +1030 Subject: [PATCH 0505/1530] lightningd: handle reestablish directly from connectd. We don't need to hand it to channeld: it will read it! We simply need to tell it to expect it. Similarly, openingd/dualopend will never see it, so remove that logic. Signed-off-by: Rusty Russell --- channeld/channeld.c | 20 ++++----- channeld/channeld_wire.csv | 3 +- lightningd/channel_control.c | 2 +- lightningd/channel_control.h | 2 +- lightningd/opening_common.c | 35 --------------- lightningd/opening_common.h | 6 --- lightningd/opening_control.c | 32 -------------- lightningd/peer_control.c | 48 +++++++++++++++++---- lightningd/test/run-invoice-select-inchan.c | 6 ++- openingd/openingd.c | 6 --- openingd/openingd_wire.csv | 6 --- wallet/test/run-wallet.c | 6 ++- 12 files changed, 62 insertions(+), 110 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index d1b898754fe5..cbf4c1742406 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2682,7 +2682,7 @@ static bool fromwire_channel_reestablish_notlvs(const void *p, struct channel_id static void peer_reconnect(struct peer *peer, const struct secret *last_remote_per_commit_secret, - u8 *reestablish_only) + bool reestablish_only) { struct channel_id channel_id; /* Note: BOLT #2 uses these names! */ @@ -2820,23 +2820,23 @@ static void peer_reconnect(struct peer *peer, peer_billboard(false, "Sent reestablish, waiting for theirs"); - /* 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). */ do { clean_tmpctx(); msg = peer_read(tmpctx, peer->pps); + + /* connectd promised us the msg was reestablish? */ + if (reestablish_only) { + if (fromwire_peektype(msg) != WIRE_CHANNEL_REESTABLISH) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Expected reestablish, got: %s", + tal_hex(tmpctx, msg)); + } } while (handle_peer_error(peer->pps, &peer->channel_id, msg) || capture_premature_msg(&premature_msgs, msg)); -got_reestablish: #if EXPERIMENTAL_FEATURES recv_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); @@ -3730,7 +3730,7 @@ static void init_channel(struct peer *peer) secp256k1_ecdsa_signature *remote_ann_node_sig; secp256k1_ecdsa_signature *remote_ann_bitcoin_sig; struct penalty_base *pbases; - u8 *reestablish_only; + bool reestablish_only; struct channel_type *channel_type; u32 *dev_disable_commit; /* Always NULL */ bool dev_fast_gossip; diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 7100e5c56249..b2179908b0d4 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -80,8 +80,7 @@ msgdata,channeld_init,dev_fail_process_onionpacket,bool, msgdata,channeld_init,dev_disable_commit,?u32, 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 +msgdata,channeld_init,reestablish_only,bool, msgdata,channeld_init,channel_update_len,u16, msgdata,channeld_init,channel_update,u8,channel_update_len diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 53b433967ed8..7e28b7814c2f 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -568,7 +568,7 @@ void peer_start_channeld(struct channel *channel, struct peer_fd *peer_fd, const u8 *fwd_msg, bool reconnected, - const u8 *reestablish_only) + bool reestablish_only) { u8 *initmsg; int hsmfd; diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index db8fa838b5c9..365e87655e2c 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -14,7 +14,7 @@ void peer_start_channeld(struct channel *channel, struct peer_fd *peer_fd, const u8 *fwd_msg, bool reconnected, - const u8 *reestablish_only); + bool reestablish_only); /* Returns true if subd told, otherwise false. */ bool channel_tell_depth(struct lightningd *ld, diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 81da4583737b..f018b1c9159a 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -166,38 +166,3 @@ void channel_config(struct lightningd *ld, /* This is filled in by lightning_openingd, for consistency. */ 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 peer_fd *peer_fd) -{ - 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, peer_fd, NULL, true, reestablish); - } else { - const u8 *err = towire_errorfmt(tmpctx, channel_id, - "Unknown channel for reestablish"); - log_debug(ld->log, "Reestablish on UNKNOWN channel %s", - type_to_string(tmpctx, struct channel_id, channel_id)); - /* Unless we're shutting down */ - if (ld->connectd) - subd_send_msg(ld->connectd, - take(towire_connectd_peer_final_msg(NULL, peer_id, - err))); - tal_free(peer_fd); - } -} diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index 4dc609f4df9c..8d1b1c490e6f 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -123,10 +123,4 @@ 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 peer_fd *peer_fd); - #endif /* LIGHTNING_LIGHTNINGD_OPENING_COMMON_H */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 10f5563255e1..6c99ada1f38b 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -804,32 +804,6 @@ 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[2], - 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 peer_fd *peer_fd; - - peer_fd = new_peer_fd_arr(tmpctx, fds); - - if (!fromwire_openingd_got_reestablish(tmpctx, msg, &channel_id, - &reestablish)) { - log_broken(openingd->log, "Malformed opening_got_reestablish %s", - tal_hex(tmpctx, msg)); - tal_free(openingd); - return; - } - - /* This could free peer */ - tal_free(uc); - - handle_reestablish(ld, &peer_id, &channel_id, reestablish, peer_fd); -} - static unsigned int openingd_msg(struct subd *openingd, const u8 *msg, const int *fds) { @@ -871,12 +845,6 @@ 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) != 1) - return 1; - opening_got_reestablish(openingd, msg, fds, uc); - return 0; - /* We send these! */ case WIRE_OPENINGD_INIT: case WIRE_OPENINGD_FUNDER_START: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 28dc35acc896..4a8d20654340 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1337,18 +1337,48 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) &peer->id)); } } - } else { - /* OK, it's unsolicited. What kind of open do they want? */ - if (dual_fund) { - channel = new_unsaved_channel(peer, - peer->ld->config.fee_base, - peer->ld->config.fee_per_satoshi); - peer_start_dualopend(peer, peer_fd, channel); + return; + } + + /* It's possible that they want to reestablish a channel, but + * it's closed? */ + if (*msgtype == WIRE_CHANNEL_REESTABLISH && channel_id) { + channel = find_channel_by_id(peer, channel_id); + if (channel && channel_closed(channel)) { + log_debug(channel->log, + "Reestablish on %s channel: using channeld to reply", + channel_state_name(channel)); + peer_start_channeld(channel, peer_fd, NULL, true, true); + return; } else { - peer->uncommitted_channel = new_uncommitted_channel(peer); - peer_start_openingd(peer, peer_fd); + const u8 *err = towire_errorfmt(tmpctx, channel_id, + "Unknown channel for reestablish"); + log_peer_debug(ld->log, &peer->id, + "Reestablish on UNKNOWN channel %s", + type_to_string(tmpctx, struct channel_id, + channel_id)); + /* Unless we're shutting down, tell connectd to send err */ + if (ld->connectd) + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, + &peer->id, + err))); + else + peer->is_connected = false; + return; } } + + /* OK, it's unsolicited. What kind of open do they want? */ + if (dual_fund) { + channel = new_unsaved_channel(peer, + peer->ld->config.fee_base, + peer->ld->config.fee_per_satoshi); + peer_start_dualopend(peer, peer_fd, channel); + } else { + peer->uncommitted_channel = new_uncommitted_channel(peer); + peer_start_openingd(peer, peer_fd); + } return; send_error: diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 02d4d4964eac..8240e771b2d5 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -187,6 +187,10 @@ bool feature_negotiated(const struct feature_set *our_features UNNEEDED, /* Generated stub for featurebits_or */ u8 *featurebits_or(const tal_t *ctx UNNEEDED, const u8 *f1 TAKES UNNEEDED, const u8 *f2 TAKES UNNEEDED) { fprintf(stderr, "featurebits_or called!\n"); abort(); } +/* Generated stub for find_channel_by_id */ +struct channel *find_channel_by_id(const struct peer *peer UNNEEDED, + const struct channel_id *cid UNNEEDED) +{ fprintf(stderr, "find_channel_by_id called!\n"); abort(); } /* Generated stub for find_plugin_for_command */ struct plugin *find_plugin_for_command(struct lightningd *ld UNNEEDED, const char *cmd_name UNNEEDED) @@ -594,7 +598,7 @@ void peer_start_channeld(struct channel *channel UNNEEDED, struct peer_fd *peer_fd UNNEEDED, const u8 *fwd_msg UNNEEDED, bool reconnected UNNEEDED, - const u8 *reestablish_only UNNEEDED) + bool reestablish_only UNNEEDED) { fprintf(stderr, "peer_start_channeld called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ bool peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED, diff --git a/openingd/openingd.c b/openingd/openingd.c index 7bcff4de893d..0401680833f7 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1251,11 +1251,6 @@ static u8 *handle_peer_in(struct state *state) 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); - } peer_write(state->pps, take(towire_warningfmt(NULL, extracted ? &channel_id : NULL, @@ -1352,7 +1347,6 @@ static u8 *handle_master_in(struct state *state) case WIRE_OPENINGD_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 aa014e0104ab..ee90e0894e91 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -25,12 +25,6 @@ msgdata,openingd_init,min_feerate,u32, msgdata,openingd_init,max_feerate,u32, msgdata,openingd_init,dev_temporary_channel_id,?byte,32 -# 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 - # Openingd->master: they offered channel, should we continue? msgtype,openingd_got_offer,6005 msgdata,openingd_got_offer,funding_satoshis,amount_sat, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a38fb1808466..62b6c2296e25 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -133,6 +133,10 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED, /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } +/* Generated stub for find_channel_by_id */ +struct channel *find_channel_by_id(const struct peer *peer UNNEEDED, + const struct channel_id *cid UNNEEDED) +{ fprintf(stderr, "find_channel_by_id called!\n"); abort(); } /* Generated stub for fromwire_channeld_dev_memleak_reply */ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } @@ -634,7 +638,7 @@ void peer_start_channeld(struct channel *channel UNNEEDED, struct peer_fd *peer_fd UNNEEDED, const u8 *fwd_msg UNNEEDED, bool reconnected UNNEEDED, - const u8 *reestablish_only UNNEEDED) + bool reestablish_only UNNEEDED) { fprintf(stderr, "peer_start_channeld called!\n"); abort(); } /* Generated stub for peer_start_dualopend */ bool peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED, From 2bc58e2327d1b0d9bb8c0ceb38d6a08adb0e4917 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:57:30 +1030 Subject: [PATCH 0506/1530] lightningd: always tell connectd the channel id. This means lightningd needs to create the temporary one and tell it to openingd/dualopend, rather than the other way around. Signed-off-by: Rusty Russell --- connectd/connectd_wire.csv | 2 +- connectd/multiplex.c | 6 +++--- lightningd/channel.c | 1 - lightningd/dual_open_control.c | 14 ++++++++++++-- lightningd/opening_control.c | 6 +++++- lightningd/peer_control.c | 1 + openingd/openingd.c | 5 +---- openingd/openingd_wire.csv | 1 + 8 files changed, 24 insertions(+), 12 deletions(-) diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 7af157794667..39506d8aea3b 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -79,7 +79,7 @@ msgdata,connectd_peer_disconnect_done,id,node_id, # Master -> connectd: make peer active immediately (we want to talk) msgtype,connectd_peer_make_active,2004 msgdata,connectd_peer_make_active,id,node_id, -msgdata,connectd_peer_make_active,channel_id,?channel_id, +msgdata,connectd_peer_make_active,channel_id,channel_id, # Connectd -> master: peer said something interesting (or you said make_active) # Plus fd for peer daemon. diff --git a/connectd/multiplex.c b/connectd/multiplex.c index eb384acee6e1..dbc6014e6dbd 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -440,9 +440,9 @@ void peer_make_active(struct daemon *daemon, const u8 *msg) { struct node_id id; struct peer *peer; - struct channel_id *channel_id; + struct channel_id channel_id; - if (!fromwire_connectd_peer_make_active(msg, msg, &id, &channel_id)) + if (!fromwire_connectd_peer_make_active(msg, &id, &channel_id)) master_badmsg(WIRE_CONNECTD_PEER_MAKE_ACTIVE, msg); /* Races can happen: this might be gone by now. */ @@ -458,7 +458,7 @@ void peer_make_active(struct daemon *daemon, const u8 *msg) if (tal_count(peer->subds) != 0) return; - if (!activate_peer(peer, NULL, channel_id)) + if (!activate_peer(peer, NULL, &channel_id)) tal_free(peer); } diff --git a/lightningd/channel.c b/lightningd/channel.c index 23bd3ae1704a..640c3f390822 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -235,7 +235,6 @@ struct channel *new_unsaved_channel(struct peer *peer, "chan#%"PRIu64, channel->unsaved_dbid); - memset(&channel->cid, 0xFF, sizeof(channel->cid)); channel->our_config.id = 0; channel->open_attempt = NULL; diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index c2a49845131f..a968409f72f7 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -2595,6 +2595,11 @@ static struct command_result *json_openchannel_init(struct command *cmd, channel = new_unsaved_channel(peer, peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); + + /* We derive initial channel_id *now*, so we can tell it to + * connectd. */ + derive_tmp_channel_id(&channel->cid, + &channel->local_basepoints.revocation); } if (channel->open_attempt @@ -2688,7 +2693,7 @@ static struct command_result *json_openchannel_init(struct command *cmd, /* Tell connectd to hand us this so we can start dualopend */ subd_send_msg(peer->ld->connectd, take(towire_connectd_peer_make_active(NULL, &peer->id, - NULL))); + &channel->cid))); return command_still_pending(cmd); } @@ -3099,6 +3104,11 @@ static struct command_result *json_queryrates(struct command *cmd, channel = new_unsaved_channel(peer, peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); + + /* We derive initial channel_id *now*, so we can tell it to + * connectd. */ + derive_tmp_channel_id(&channel->cid, + &channel->local_basepoints.revocation); } if (channel->open_attempt @@ -3167,7 +3177,7 @@ static struct command_result *json_queryrates(struct command *cmd, /* Tell connectd to hand us this so we can start dualopend */ subd_send_msg(peer->ld->connectd, take(towire_connectd_peer_make_active(NULL, &peer->id, - NULL))); + &channel->cid))); return command_still_pending(cmd); } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 6c99ada1f38b..832009e9cad3 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1079,6 +1079,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, struct amount_sat *amount; struct amount_msat *push_msat; u32 *upfront_shutdown_script_wallet_index; + struct channel_id tmp_channel_id; fc->cmd = cmd; fc->cancels = tal_arr(fc, struct command *, 0); @@ -1207,6 +1208,8 @@ static struct command_result *json_fundchannel_start(struct command *cmd, } else upfront_shutdown_script_wallet_index = NULL; + temporary_channel_id(&tmp_channel_id); + fc->open_msg = towire_openingd_funder_start(fc, *amount, @@ -1214,12 +1217,13 @@ static struct command_result *json_fundchannel_start(struct command *cmd, fc->our_upfront_shutdown_script, upfront_shutdown_script_wallet_index, *feerate_per_kw, + &tmp_channel_id, fc->channel_flags); /* Tell connectd to make this active; when it does, we can continue */ subd_send_msg(peer->ld->connectd, take(towire_connectd_peer_make_active(NULL, &peer->id, - NULL))); + &tmp_channel_id))); return command_still_pending(cmd); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4a8d20654340..99a6e84be5b6 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1374,6 +1374,7 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) channel = new_unsaved_channel(peer, peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); + channel->cid = *channel_id; peer_start_dualopend(peer, peer_fd, channel); } else { peer->uncommitted_channel = new_uncommitted_channel(peer); diff --git a/openingd/openingd.c b/openingd/openingd.c index 0401680833f7..0b4ca5f1353b 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -240,10 +240,6 @@ static bool setup_channel_funder(struct state *state) * could do it for the we-are-funding case. */ set_reserve(state, state->localconf.dust_limit); - /*~ Grab a random ID until the funding tx is created (we can't do that - * until we know their funding_pubkey) */ - temporary_channel_id(&state->channel_id); - #if DEVELOPER /* --dev-force-tmp-channel-id specified */ if (dev_force_tmp_channel_id) @@ -1308,6 +1304,7 @@ static u8 *handle_master_in(struct state *state) &state->upfront_shutdown_script[LOCAL], &state->local_upfront_shutdown_wallet_index, &state->feerate_per_kw, + &state->channel_id, &channel_flags)) master_badmsg(WIRE_OPENINGD_FUNDER_START, msg); msg = funder_channel_start(state, channel_flags); diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index ee90e0894e91..a759e1a4fddb 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -77,6 +77,7 @@ msgdata,openingd_funder_start,len_upfront,u16, msgdata,openingd_funder_start,upfront_shutdown_script,u8,len_upfront msgdata,openingd_funder_start,upfront_shutdown_wallet_index,?u32, msgdata,openingd_funder_start,feerate_per_kw,u32, +msgdata,openingd_funder_start,temporary_channel_id,channel_id, msgdata,openingd_funder_start,channel_flags,u8, # openingd->master: send back output script for 2-of-2 funding output From fe9f391a932cc587e29f8a1c2ab643fc61af6ea4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:57:30 +1030 Subject: [PATCH 0507/1530] connectd: tell lightningd the channel_id when we give it the active peer. Now we always have it (either extracted from an unsolicited message, or told to us by lightningd when it tells us it wants to talk), we can always send it. Signed-off-by: Rusty Russell --- connectd/connectd_wire.csv | 2 +- connectd/multiplex.c | 25 ++++++++++++++++----- lightningd/peer_control.c | 12 +++++----- lightningd/test/run-invoice-select-inchan.c | 2 +- wallet/test/run-wallet.c | 2 +- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 39506d8aea3b..5b0950f9376e 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -86,7 +86,7 @@ msgdata,connectd_peer_make_active,channel_id,channel_id, msgtype,connectd_peer_active,2005 msgdata,connectd_peer_active,id,node_id, msgdata,connectd_peer_active,msgtype,?u16, -msgdata,connectd_peer_active,channel_id,?channel_id, +msgdata,connectd_peer_active,channel_id,channel_id, # master -> connectd: peer no longer wanted, you can disconnect. msgtype,connectd_discard_peer,2015 diff --git a/connectd/multiplex.c b/connectd/multiplex.c index dbc6014e6dbd..da6f5f346813 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -402,7 +402,9 @@ void send_custommsg(struct daemon *daemon, const u8 *msg) } /* FIXME: fwd decl */ -static struct subd *multiplex_subd_setup(struct peer *peer, int *fd_for_subd); +static struct subd *multiplex_subd_setup(struct peer *peer, + const struct channel_id *channel_id, + int *fd_for_subd); static struct subd *activate_peer(struct peer *peer, const enum peer_wire *type, @@ -415,7 +417,7 @@ static struct subd *activate_peer(struct peer *peer, /* If it wasn't active before, it is now! */ peer->active = true; - subd = multiplex_subd_setup(peer, &fd_for_subd); + subd = multiplex_subd_setup(peer, channel_id, &fd_for_subd); if (!subd) return NULL; @@ -816,11 +818,18 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, if (!subd) { struct channel_id channel_id; enum peer_wire t = fromwire_peektype(decrypted); - bool has_channel_id = extract_channel_id(decrypted, &channel_id); + + if (!extract_channel_id(decrypted, &channel_id)) { + send_warning(peer, "Unrecognized message %s: %s", + peer_wire_name(t), + tal_hex(tmpctx, decrypted)); + tal_free(decrypted); + io_wake(peer->peer_outq); + return read_hdr_from_peer(peer_conn, peer); + } status_peer_debug(&peer->id, "Activating for message %s", peer_wire_name(t)); - subd = activate_peer(peer, &t, - has_channel_id ? &channel_id : NULL); + subd = activate_peer(peer, &t, &channel_id); if (!subd) return io_close(peer_conn); } @@ -915,7 +924,9 @@ void close_peer_conn(struct peer *peer) msg_wake(peer->peer_outq); } -static struct subd *multiplex_subd_setup(struct peer *peer, int *fd_for_subd) +static struct subd *multiplex_subd_setup(struct peer *peer, + const struct channel_id *channel_id, + int *fd_for_subd) { int fds[2]; struct subd *subd; @@ -929,6 +940,8 @@ static struct subd *multiplex_subd_setup(struct peer *peer, int *fd_for_subd) subd = tal(peer->subds, struct subd); subd->peer = peer; subd->outq = msg_queue_new(subd, false); + subd->channel_id = *channel_id; + subd->temporary_channel_id = NULL; /* This sets subd->conn inside subd_conn_init */ io_new_conn(peer, fds[0], subd_conn_init, subd); /* When conn dies, subd is freed. */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 99a6e84be5b6..2237d9f36d1b 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1237,7 +1237,7 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) struct node_id id; u16 *msgtype; struct channel *channel; - struct channel_id *channel_id; + struct channel_id channel_id; struct peer *peer; bool dual_fund; u8 *error; @@ -1342,8 +1342,8 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) /* It's possible that they want to reestablish a channel, but * it's closed? */ - if (*msgtype == WIRE_CHANNEL_REESTABLISH && channel_id) { - channel = find_channel_by_id(peer, channel_id); + if (*msgtype == WIRE_CHANNEL_REESTABLISH) { + channel = find_channel_by_id(peer, &channel_id); if (channel && channel_closed(channel)) { log_debug(channel->log, "Reestablish on %s channel: using channeld to reply", @@ -1351,12 +1351,12 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) peer_start_channeld(channel, peer_fd, NULL, true, true); return; } else { - const u8 *err = towire_errorfmt(tmpctx, channel_id, + const u8 *err = towire_errorfmt(tmpctx, &channel_id, "Unknown channel for reestablish"); log_peer_debug(ld->log, &peer->id, "Reestablish on UNKNOWN channel %s", type_to_string(tmpctx, struct channel_id, - channel_id)); + &channel_id)); /* Unless we're shutting down, tell connectd to send err */ if (ld->connectd) subd_send_msg(ld->connectd, @@ -1374,7 +1374,7 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) channel = new_unsaved_channel(peer, peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); - channel->cid = *channel_id; + channel->cid = channel_id; peer_start_dualopend(peer, peer_fd, channel); } else { peer->uncommitted_channel = new_uncommitted_channel(peer); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 8240e771b2d5..7e7a0f73849a 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -209,7 +209,7 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_active */ -bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id **channel_id UNNEEDED) +bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_active called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 62b6c2296e25..1f05d6fff02f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -153,7 +153,7 @@ bool fromwire_channeld_offer_htlc_reply(const tal_t *ctx UNNEEDED, const void *p bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_sending_commitsig called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_active */ -bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id **channel_id UNNEEDED) +bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_active called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) From 395051cdf85319a15d1dac0552dfcbeb226a3814 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:57:30 +1030 Subject: [PATCH 0508/1530] connectd: track the channel_id of each stream to/from peer. This means doing some wire interpretation, and handling the transient case where we switch from temporary to permenant channel_id, but it's not that bad (and required for accurate demux when multiple channels are involved for a single peer). Signed-off-by: Rusty Russell --- connectd/multiplex.c | 204 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index da6f5f346813..d086cf3a97bf 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,9 @@ struct subd { /* In passing, we can have a temporary one, too. */ struct channel_id *temporary_channel_id; + /* The opening revocation basepoint, for v2 channel_id. */ + struct pubkey *opener_revocation_basepoint; + /* The actual connection to talk to it */ struct io_conn *conn; @@ -627,6 +631,176 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) return false; } +/* Move "channel_id" to temporary. */ +static void move_channel_id_to_temp(struct subd *subd) +{ + tal_free(subd->temporary_channel_id); + subd->temporary_channel_id + = tal_dup(subd, struct channel_id, &subd->channel_id); +} + +/* Only works for open_channel2 and accept_channel2 */ +static struct pubkey *extract_revocation_basepoint(const tal_t *ctx, + const u8 *msg) +{ + const u8 *cursor = msg; + size_t max = tal_bytelen(msg); + enum peer_wire t; + struct pubkey pubkey; + + t = fromwire_u16(&cursor, &max); + + switch (t) { + case WIRE_OPEN_CHANNEL2: + /* BOLT-dualfund #2: + * 1. type: 64 (`open_channel2`) + * 2. data: + * * [`chain_hash`:`chain_hash`] + * * [`channel_id`:`zerod_channel_id`] + * * [`u32`:`funding_feerate_perkw`] + * * [`u32`:`commitment_feerate_perkw`] + * * [`u64`:`funding_satoshis`] + * * [`u64`:`dust_limit_satoshis`] + * * [`u64`:`max_htlc_value_in_flight_msat`] + * * [`u64`:`htlc_minimum_msat`] + * * [`u16`:`to_self_delay`] + * * [`u16`:`max_accepted_htlcs`] + * * [`u32`:`locktime`] + * * [`point`:`funding_pubkey`] + * * [`point`:`revocation_basepoint`] + */ + fromwire_pad(&cursor, &max, + sizeof(struct bitcoin_blkid) + + sizeof(struct channel_id) + + sizeof(u32) + + sizeof(u32) + + sizeof(u64) + + sizeof(u64) + + sizeof(u64) + + sizeof(u64) + + sizeof(u16) + + sizeof(u16) + + sizeof(u32) + + PUBKEY_CMPR_LEN); + break; + case WIRE_ACCEPT_CHANNEL2: + /* BOLT-dualfund #2: + * 1. type: 65 (`accept_channel2`) + * 2. data: + * * [`channel_id`:`zerod_channel_id`] + * * [`u64`:`funding_satoshis`] + * * [`u64`:`dust_limit_satoshis`] + * * [`u64`:`max_htlc_value_in_flight_msat`] + * * [`u64`:`htlc_minimum_msat`] + * * [`u32`:`minimum_depth`] + * * [`u16`:`to_self_delay`] + * * [`u16`:`max_accepted_htlcs`] + * * [`point`:`funding_pubkey`] + * * [`point`:`revocation_basepoint`] + */ + fromwire_pad(&cursor, &max, + sizeof(struct channel_id) + + sizeof(u64) + + sizeof(u64) + + sizeof(u64) + + sizeof(u64) + + sizeof(u32) + + sizeof(u16) + + sizeof(u16) + + PUBKEY_CMPR_LEN); + break; + default: + abort(); + } + + fromwire_pubkey(&cursor, &max, &pubkey); + if (!cursor) + return NULL; + return tal_dup(ctx, struct pubkey, &pubkey); +} + +/* Only works for funding_created */ +static bool extract_funding_created_funding(const u8 *funding_created, + struct bitcoin_outpoint *outp) +{ + const u8 *cursor = funding_created; + size_t max = tal_bytelen(funding_created); + enum peer_wire t; + + t = fromwire_u16(&cursor, &max); + + switch (t) { + case WIRE_FUNDING_CREATED: + /* BOLT #2: + * 1. type: 34 (`funding_created`) + * 2. data: + * * [`32*byte`:`temporary_channel_id`] + * * [`sha256`:`funding_txid`] + * * [`u16`:`funding_output_index`] + */ + fromwire_pad(&cursor, &max, 32); + fromwire_bitcoin_txid(&cursor, &max, &outp->txid); + outp->n = fromwire_u16(&cursor, &max); + break; + default: + abort(); + } + + return cursor != NULL; +} + +static void update_v1_channelid(struct subd *subd, const u8 *funding_created) +{ + struct bitcoin_outpoint outp; + + if (!extract_funding_created_funding(funding_created, &outp)) { + status_peer_unusual(&subd->peer->id, "WARNING: funding_created no tx info?"); + return; + } + move_channel_id_to_temp(subd); + derive_channel_id(&subd->channel_id, &outp); +} + +static void update_v2_channelid(struct subd *subd, const u8 *accept_channel2) +{ + struct pubkey *acc_basepoint; + + acc_basepoint = extract_revocation_basepoint(tmpctx, accept_channel2); + if (!acc_basepoint) { + status_peer_unusual(&subd->peer->id, "WARNING: accept_channel2 no revocation_basepoint?"); + return; + } + if (!subd->opener_revocation_basepoint) { + status_peer_unusual(&subd->peer->id, "WARNING: accept_channel2 without open_channel2?"); + return; + } + + move_channel_id_to_temp(subd); + derive_channel_id_v2(&subd->channel_id, + subd->opener_revocation_basepoint, acc_basepoint); +} + +/* We maintain channel_id matching for subds by snooping: we set it manually + * for first packet (open_channel or open_channel2). */ +static void maybe_update_channelid(struct subd *subd, const u8 *msg) +{ + switch (fromwire_peektype(msg)) { + case WIRE_OPEN_CHANNEL: + extract_channel_id(msg, &subd->channel_id); + break; + case WIRE_OPEN_CHANNEL2: + subd->opener_revocation_basepoint + = extract_revocation_basepoint(subd, msg); + break; + case WIRE_ACCEPT_CHANNEL2: + update_v2_channelid(subd, msg); + break; + case WIRE_FUNDING_CREATED: + update_v1_channelid(subd, msg); + break; + } +} + static void close_timeout(struct peer *peer) { /* BROKEN means we'll trigger CI if we see it, though it's possible */ @@ -706,6 +880,8 @@ static struct io_plan *read_from_subd(struct io_conn *subd_conn, static struct io_plan *read_from_subd_done(struct io_conn *subd_conn, struct subd *subd) { + maybe_update_channelid(subd, subd->in); + /* Tell them to encrypt & write. */ msg_enqueue(subd->peer->peer_outq, take(subd->in)); subd->in = NULL; @@ -748,12 +924,34 @@ static struct io_plan *write_to_subd(struct io_conn *subd_conn, return io_write_wire(subd_conn, take(msg), write_to_subd, subd); } -/* FIXME: We only currently have one subd */ static struct subd *find_subd(struct peer *peer, const struct channel_id *channel_id) { if (tal_count(peer->subds) == 0) return NULL; + + for (size_t i = 0; i < tal_count(peer->subds); i++) { + struct subd *subd = peer->subds[i]; + + /* Once we see a message using the real channel_id, we + * clear the temporary_channel_id */ + if (channel_id_eq(&subd->channel_id, channel_id)) { + subd->temporary_channel_id + = tal_free(subd->temporary_channel_id); + return subd; + } + if (subd->temporary_channel_id + && channel_id_eq(subd->temporary_channel_id, channel_id)) { + return subd; + } + } + + status_peer_broken(&peer->id, "channel_id %s does not match peer %s (temp=%s)", + type_to_string(tmpctx, struct channel_id, channel_id), + type_to_string(tmpctx, struct channel_id, &peer->subds[0]->channel_id), + peer->subds[0]->temporary_channel_id + ? type_to_string(tmpctx, struct channel_id, peer->subds[0]->temporary_channel_id) + : "none"); return peer->subds[0]; } @@ -834,6 +1032,9 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return io_close(peer_conn); } + /* Even if we just created it, call this to catch open_channel2 */ + maybe_update_channelid(subd, decrypted); + /* Tell them to write. */ msg_enqueue(subd->outq, take(decrypted)); @@ -942,6 +1143,7 @@ static struct subd *multiplex_subd_setup(struct peer *peer, subd->outq = msg_queue_new(subd, false); subd->channel_id = *channel_id; subd->temporary_channel_id = NULL; + subd->opener_revocation_basepoint = NULL; /* This sets subd->conn inside subd_conn_init */ io_new_conn(peer, fds[0], subd_conn_init, subd); /* When conn dies, subd is freed. */ From 32cd7ae39872434b3a8e8bd0527b080810a06a95 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:57:30 +1030 Subject: [PATCH 0509/1530] connectd: key multiple subds by channel_id, use for lookup. We still don't *have* multiple subds per peer, but now we could! Signed-off-by: Rusty Russell --- connectd/multiplex.c | 70 ++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 45 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index d086cf3a97bf..c39c35ea094c 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -60,6 +60,27 @@ struct subd { struct msg_queue *outq; }; +static struct subd *find_subd(struct peer *peer, + const struct channel_id *channel_id) +{ + for (size_t i = 0; i < tal_count(peer->subds); i++) { + struct subd *subd = peer->subds[i]; + + /* Once we see a message using the real channel_id, we + * clear the temporary_channel_id */ + if (channel_id_eq(&subd->channel_id, channel_id)) { + subd->temporary_channel_id + = tal_free(subd->temporary_channel_id); + return subd; + } + if (subd->temporary_channel_id + && channel_id_eq(subd->temporary_channel_id, channel_id)) { + return subd; + } + } + return NULL; +} + void inject_peer_msg(struct peer *peer, const u8 *msg TAKES) { status_peer_io(LOG_IO_OUT, &peer->id, msg); @@ -410,7 +431,7 @@ static struct subd *multiplex_subd_setup(struct peer *peer, const struct channel_id *channel_id, int *fd_for_subd); -static struct subd *activate_peer(struct peer *peer, +static struct subd *activate_subd(struct peer *peer, const enum peer_wire *type, const struct channel_id *channel_id) { @@ -461,10 +482,10 @@ void peer_make_active(struct daemon *daemon, const u8 *msg) return; /* Could be made active already by receiving a message (esp reestablish!) */ - if (tal_count(peer->subds) != 0) + if (find_subd(peer, &channel_id)) return; - if (!activate_peer(peer, NULL, &channel_id)) + if (!activate_subd(peer, NULL, &channel_id)) tal_free(peer); } @@ -924,37 +945,6 @@ static struct io_plan *write_to_subd(struct io_conn *subd_conn, return io_write_wire(subd_conn, take(msg), write_to_subd, subd); } -static struct subd *find_subd(struct peer *peer, - const struct channel_id *channel_id) -{ - if (tal_count(peer->subds) == 0) - return NULL; - - for (size_t i = 0; i < tal_count(peer->subds); i++) { - struct subd *subd = peer->subds[i]; - - /* Once we see a message using the real channel_id, we - * clear the temporary_channel_id */ - if (channel_id_eq(&subd->channel_id, channel_id)) { - subd->temporary_channel_id - = tal_free(subd->temporary_channel_id); - return subd; - } - if (subd->temporary_channel_id - && channel_id_eq(subd->temporary_channel_id, channel_id)) { - return subd; - } - } - - status_peer_broken(&peer->id, "channel_id %s does not match peer %s (temp=%s)", - type_to_string(tmpctx, struct channel_id, channel_id), - type_to_string(tmpctx, struct channel_id, &peer->subds[0]->channel_id), - peer->subds[0]->temporary_channel_id - ? type_to_string(tmpctx, struct channel_id, peer->subds[0]->temporary_channel_id) - : "none"); - return peer->subds[0]; -} - static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, struct peer *peer); static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, @@ -1014,20 +1004,10 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, /* If we don't find a subdaemon for this, activate a new one. */ subd = find_subd(peer, &channel_id); if (!subd) { - struct channel_id channel_id; enum peer_wire t = fromwire_peektype(decrypted); - - if (!extract_channel_id(decrypted, &channel_id)) { - send_warning(peer, "Unrecognized message %s: %s", - peer_wire_name(t), - tal_hex(tmpctx, decrypted)); - tal_free(decrypted); - io_wake(peer->peer_outq); - return read_hdr_from_peer(peer_conn, peer); - } status_peer_debug(&peer->id, "Activating for message %s", peer_wire_name(t)); - subd = activate_peer(peer, &t, &channel_id); + subd = activate_subd(peer, &t, &channel_id); if (!subd) return io_close(peer_conn); } From 7de7b7be61f273d188716cfe160350a6e54f07c5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 06:57:30 +1030 Subject: [PATCH 0510/1530] lightningd: use channel_id when a peer is activated. Rather than intuiting whether this is a new channel / active channel, use the channel_id. This simplifies things and makes them explicit, and prepares for multiple live channels per peer. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 94 +++++++++++++++++++++------------------ tests/test_connection.py | 2 +- wallet/test/run-wallet.c | 3 ++ 3 files changed, 55 insertions(+), 44 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 2237d9f36d1b..e91e7561cff0 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1256,24 +1256,17 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) return; } - channel = peer_active_channel(peer); - - /* It might be v2 opening, though, since we hang onto these */ - if (!channel) - channel = peer_unsaved_channel(peer); - + /* Do we know what channel they're talking about? */ + channel = find_channel_by_id(peer, &channel_id); if (channel) { switch (channel->state) { case ONCHAIN: case FUNDING_SPEND_SEEN: case CLOSINGD_COMPLETE: - /* Channel is supposed to be active!*/ - abort(); + goto channel_is_closed; case CLOSED: /* Channel should not have been loaded */ abort(); - - /* We consider this "active" but we only send an error */ case AWAITING_UNILATERAL: { /* channel->error is not saved in db, so this can * happen if we restart. */ @@ -1340,48 +1333,63 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) return; } - /* It's possible that they want to reestablish a channel, but - * it's closed? */ - if (*msgtype == WIRE_CHANNEL_REESTABLISH) { - channel = find_channel_by_id(peer, &channel_id); - if (channel && channel_closed(channel)) { - log_debug(channel->log, - "Reestablish on %s channel: using channeld to reply", - channel_state_name(channel)); - peer_start_channeld(channel, peer_fd, NULL, true, true); - return; - } else { - const u8 *err = towire_errorfmt(tmpctx, &channel_id, - "Unknown channel for reestablish"); - log_peer_debug(ld->log, &peer->id, - "Reestablish on UNKNOWN channel %s", - type_to_string(tmpctx, struct channel_id, - &channel_id)); - /* Unless we're shutting down, tell connectd to send err */ - if (ld->connectd) - subd_send_msg(ld->connectd, - take(towire_connectd_peer_final_msg(NULL, - &peer->id, - err))); - else - peer->is_connected = false; - return; + /* OK, it's an unknown channel. Create a new one if they're trying. */ + switch (*msgtype) { + case WIRE_OPEN_CHANNEL: + if (dual_fund) { + error = towire_errorfmt(tmpctx, &channel_id, + "OPT_DUAL_FUND: cannot use open_channel"); + goto send_error; + } + if (peer->uncommitted_channel) { + error = towire_errorfmt(tmpctx, &channel_id, + "Multiple simulteneous opens not supported"); + goto send_error; + } + peer->uncommitted_channel = new_uncommitted_channel(peer); + peer_start_openingd(peer, peer_fd); + break; + case WIRE_OPEN_CHANNEL2: + if (!dual_fund) { + error = towire_errorfmt(tmpctx, &channel_id, + "Didn't negotiate OPT_DUAL_FUND: cannot use open_channel2"); + goto send_error; } - } - - /* OK, it's unsolicited. What kind of open do they want? */ - if (dual_fund) { channel = new_unsaved_channel(peer, peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); channel->cid = channel_id; peer_start_dualopend(peer, peer_fd, channel); - } else { - peer->uncommitted_channel = new_uncommitted_channel(peer); - peer_start_openingd(peer, peer_fd); + break; + default: + log_peer_unusual(ld->log, &peer->id, + "Unknown channel %s for %s", + type_to_string(tmpctx, struct channel_id, + &channel_id), + peer_wire_name(*msgtype)); + error = towire_errorfmt(tmpctx, &channel_id, + "Unknown channel for %s", peer_wire_name(*msgtype)); + goto send_error; + break; } return; +channel_is_closed: + if (msgtype && *msgtype == WIRE_CHANNEL_REESTABLISH) { + log_debug(channel->log, + "Reestablish on %s channel: using channeld to reply", + channel_state_name(channel)); + peer_start_channeld(channel, peer_fd, NULL, true, true); + return; + } + + /* Retransmit error if we have one. Otherwise generic error. */ + error = channel->error; + if (!error) + error = towire_errorfmt(tmpctx, &channel_id, + "channel in state %s", + channel_state_name(channel)); + send_error: log_peer_debug(ld->log, &peer->id, "Telling connectd to send error %s", tal_hex(tmpctx, error)); diff --git a/tests/test_connection.py b/tests/test_connection.py index 3c9117cdc4cf..97e9f91e5a9d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1258,7 +1258,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): # on reconnect, channel should get destroyed l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - l1.daemon.wait_for_log('Reestablish on UNKNOWN channel') + l1.daemon.wait_for_log('Unknown channel .* for WIRE_CHANNEL_REESTABLISH') wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 0) wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 1f05d6fff02f..f2e958053b9f 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -648,6 +648,9 @@ bool peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UN bool peer_start_openingd(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } +/* Generated stub for peer_wire_name */ +const char *peer_wire_name(int e UNNEEDED) +{ fprintf(stderr, "peer_wire_name called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) From 90be2cc10402a20c44eb54f689461ae2732a7b03 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 07:00:54 +1030 Subject: [PATCH 0511/1530] lightningd: remove some "single active channel" assumptions. Generally this means converting a lazy "peer_active_channel(peer)" call into an explicit iteration. 1. notify_feerate_change: call all channels (ignores non-active ones anyway). 2. peer_get_owning_subd remove unused function. 3. peer_connected hook: don't save channel, do lookup and iterate channels. 4. In json_setchannelfee "all" remove useless call to peer_active_channel since we check state anyway, and iterate. Signed-off-by: Rusty Russell --- lightningd/channel_control.c | 13 +++-- lightningd/opening_control.c | 13 ----- lightningd/peer_control.c | 100 ++++++++++++++--------------------- tests/test_plugin.py | 2 +- 4 files changed, 47 insertions(+), 81 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 7e28b7814c2f..a16d6ee57cf6 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -117,15 +117,14 @@ void notify_feerate_change(struct lightningd *ld) /* FIXME: We should notify onchaind about NORMAL fee change in case * it's going to generate more txs. */ list_for_each(&ld->peers, peer, list) { - struct channel *channel = peer_active_channel(peer); + struct channel *channel; - if (!channel) - continue; - - /* FIXME: We choose not to drop to chain if we can't contact - * peer. We *could* do so, however. */ - try_update_feerates(ld, channel); + list_for_each(&peer->channels, channel, list) + try_update_feerates(ld, channel); } + + /* FIXME: We choose not to drop to chain if we can't contact + * peer. We *could* do so, however. */ } void channel_record_open(struct channel *channel) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 832009e9cad3..23c351ce0985 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1252,16 +1252,3 @@ static const struct json_command fundchannel_complete_command = { "with {psbt}. Returns true on success, false otherwise." }; AUTODATA(json_command, &fundchannel_complete_command); - -struct subd *peer_get_owning_subd(struct peer *peer) -{ - struct channel *channel; - channel = peer_active_channel(peer); - - if (channel != NULL) { - return channel->owner; - } else if (peer->uncommitted_channel != NULL) { - return peer->uncommitted_channel->open_daemon; - } - return NULL; -} diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e91e7561cff0..157be841ad87 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -939,7 +939,6 @@ static void json_add_channel(struct lightningd *ld, struct peer_connected_hook_payload { struct lightningd *ld; - struct channel *channel; struct wireaddr_internal addr; struct wireaddr *remote_addr; bool incoming; @@ -969,7 +968,7 @@ peer_connected_serialize(struct peer_connected_hook_payload *payload, static void peer_connected_hook_final(struct peer_connected_hook_payload *payload STEALS) { struct lightningd *ld = payload->ld; - struct channel *channel = payload->channel; + struct channel *channel; struct wireaddr_internal addr = payload->addr; struct peer *peer = payload->peer; u8 *error; @@ -988,16 +987,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa goto send_error; } - if (channel) { - log_debug(channel->log, "Peer has reconnected, state %s", - channel_state_name(channel)); - - /* If we have a canned error, deliver it now. */ - if (channel->error) { - error = channel->error; - goto send_error; - } - + list_for_each(&peer->channels, channel, list) { #if DEVELOPER if (dev_disconnect_permanent(ld)) { channel_fail_permanent(channel, REASON_LOCAL, @@ -1008,39 +998,38 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa #endif switch (channel->state) { - case ONCHAIN: - case FUNDING_SPEND_SEEN: - case CLOSINGD_COMPLETE: - /* Channel is supposed to be active!*/ - abort(); case CLOSED: /* Channel should not have been loaded */ abort(); - - /* We consider this "active" but we only send an error */ - case AWAITING_UNILATERAL: { - /* channel->error is not saved in db, so this can - * happen if we restart. */ - error = towire_errorfmt(tmpctx, &channel->cid, - "Awaiting unilateral close"); - goto send_error; - } + case ONCHAIN: + case FUNDING_SPEND_SEEN: + case CLOSINGD_COMPLETE: + case AWAITING_UNILATERAL: + /* We don't send anything here; if they talk about + * this channel they'll get an error. */ + continue; case DUALOPEND_OPEN_INIT: case DUALOPEND_AWAITING_LOCKIN: case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: case CHANNELD_SHUTTING_DOWN: case CLOSINGD_SIGEXCHANGE: + log_debug(channel->log, "Peer has reconnected, state %s: telling connectd to make active", + channel_state_name(channel)); + assert(!channel->owner); channel->peer->addr = addr; channel->peer->connected_incoming = payload->incoming; - goto make_active; + + subd_send_msg(ld->connectd, + take(towire_connectd_peer_make_active(NULL, &peer->id, + &channel->cid))); + continue; } + + /* Oops, channel->state is corrupted? */ abort(); } - - /* If we get here, it means we have no channel */ - assert(!channel); return; send_error: @@ -1050,15 +1039,6 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, error))); - return; - -make_active: - log_peer_debug(ld->log, &peer->id, - "Telling connectd to make active, state %s", - channel_state_name(channel)); - subd_send_msg(ld->connectd, - take(towire_connectd_peer_make_active(NULL, &peer->id, - &channel->cid))); } static bool @@ -1210,11 +1190,6 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* Can't be opening, since we wouldn't have sent peer_disconnected. */ assert(!peer->uncommitted_channel); - hook_payload->channel = peer_active_channel(peer); - - /* It might be v2 opening, though, since we hang onto these */ - if (!hook_payload->channel) - hook_payload->channel = peer_unsaved_channel(peer); /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { @@ -1222,7 +1197,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) fmt_wireaddr(tmpctx, hook_payload->remote_addr)); /* Currently only from peers we have a channel with, until we * do stuff like probing for remote_addr to a random node. */ - if (hook_payload->channel) + if (!list_empty(&peer->channels)) update_remote_addr(ld, hook_payload->remote_addr, id); } @@ -1259,6 +1234,12 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) /* Do we know what channel they're talking about? */ channel = find_channel_by_id(peer, &channel_id); if (channel) { + /* If we have a canned error for this channel, send it now */ + if (channel->error) { + error = channel->error; + goto send_error; + } + switch (channel->state) { case ONCHAIN: case FUNDING_SPEND_SEEN: @@ -1777,11 +1758,12 @@ command_find_channel(struct command *cmd, if (json_tok_channel_id(buffer, tok, &cid)) { list_for_each(&ld->peers, peer, list) { - *channel = peer_active_channel(peer); - if (!*channel) - continue; - if (channel_id_eq(&(*channel)->cid, &cid)) - return NULL; + list_for_each(&peer->channels, (*channel), list) { + if (!channel_active(*channel)) + continue; + if (channel_id_eq(&(*channel)->cid, &cid)) + return NULL; + } } return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Channel ID not found: '%.*s'", @@ -2350,17 +2332,15 @@ static struct command_result *json_setchannelfee(struct command *cmd, /* If the users requested 'all' channels we need to iterate */ if (channel == NULL) { list_for_each(&cmd->ld->peers, peer, list) { - channel = peer_active_channel(peer); - if (!channel) - continue; - if (channel->state != CHANNELD_NORMAL && - channel->state != CHANNELD_AWAITING_LOCKIN && - channel->state != DUALOPEND_AWAITING_LOCKIN) - continue; - set_channel_config(cmd, channel, base, ppm, NULL, NULL, - *delaysecs, response, false); + list_for_each(&peer->channels, channel, list) { + if (channel->state != CHANNELD_NORMAL && + channel->state != CHANNELD_AWAITING_LOCKIN && + channel->state != DUALOPEND_AWAITING_LOCKIN) + continue; + set_channel_config(cmd, channel, base, ppm, NULL, NULL, + *delaysecs, response, false); + } } - /* single channel should be updated */ } else { set_channel_config(cmd, channel, base, ppm, NULL, NULL, diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3c020bd81e8a..19523613aa60 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -935,7 +935,7 @@ def wait_for_event(node): assert(l2.rpc.listpeers()['peers'][0]['channels'][0]['closer'] == 'local') if EXPERIMENTAL_DUAL_FUND: l1.daemon.wait_for_log(r'Peer has reconnected, state') - l2.daemon.wait_for_log(r'Peer has reconnected, state') + l2.daemon.wait_for_log(r'Telling connectd to send error') # l1 will receive error, and go into AWAITING_UNILATERAL # FIXME: l2 should re-xmit shutdown, but it doesn't until it's mined :( From 33abf93ec19fe25d1bc4ba76afc807f75e1cd2f0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 07:00:59 +1030 Subject: [PATCH 0512/1530] lightningd: rename activate_peers() to setup_peers(). Activate means a specific thing now (connectd said something), so avoid confusing it with this function. Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 2 +- lightningd/peer_control.c | 6 +++--- lightningd/peer_control.h | 2 +- lightningd/test/run-find_my_abspath.c | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 0702a024f9d9..f7ffa99eb97d 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1151,7 +1151,7 @@ int main(int argc, char *argv[]) /*~ This is where we ask connectd to reconnect to any peers who have * live channels with us, and makes sure we're watching the funding * tx. */ - activate_peers(ld); + setup_peers(ld); /*~ Now that all the notifications for transactions are in place, we * can start the poll loop which queries bitcoind for new blocks. */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 157be841ad87..263222993fac 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1784,7 +1784,7 @@ command_find_channel(struct command *cmd, } } -static void activate_peer(struct peer *peer, u32 delay) +static void setup_peer(struct peer *peer, u32 delay) { struct channel *channel; struct channel_inflight *inflight; @@ -1815,14 +1815,14 @@ static void activate_peer(struct peer *peer, u32 delay) } } -void activate_peers(struct lightningd *ld) +void setup_peers(struct lightningd *ld) { struct peer *p; /* Avoid thundering herd: after first five, delay by 1 second. */ int delay = -5; list_for_each(&ld->peers, p, list) { - activate_peer(p, delay > 0 ? delay : 0); + setup_peer(p, delay > 0 ? delay : 0); delay++; } } diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 5f006466a888..4e3207609573 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -84,7 +84,7 @@ void channel_errmsg(struct channel *channel, u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx); /* We've loaded peers from database, set them going. */ -void activate_peers(struct lightningd *ld); +void setup_peers(struct lightningd *ld); void drop_to_chain(struct lightningd *ld, struct channel *channel, bool cooperative); diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 43c2593ff987..a8805bce7cfa 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -7,9 +7,6 @@ int unused_main(int argc, char *argv[]); #include /* AUTOGENERATED MOCKS START */ -/* Generated stub for activate_peers */ -void activate_peers(struct lightningd *ld UNNEEDED) -{ fprintf(stderr, "activate_peers called!\n"); abort(); } /* Generated stub for begin_topology */ void begin_topology(struct chain_topology *topo UNNEEDED) { fprintf(stderr, "begin_topology called!\n"); abort(); } @@ -191,6 +188,9 @@ void plugins_set_builtin_plugins_dir(struct plugins *plugins UNNEEDED, /* Generated stub for setup_color_and_alias */ void setup_color_and_alias(struct lightningd *ld UNNEEDED) { fprintf(stderr, "setup_color_and_alias called!\n"); abort(); } +/* Generated stub for setup_peers */ +void setup_peers(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "setup_peers called!\n"); abort(); } /* Generated stub for setup_topology */ void setup_topology(struct chain_topology *topology UNNEEDED, u32 min_blockheight UNNEEDED, u32 max_blockheight UNNEEDED) From b3438e9bba5aa98bbd6cdd8996332f42acaf9e1b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 07:00:59 +1030 Subject: [PATCH 0513/1530] lightningd: associate connect commands with peer, not channel. Sure, we want to connect (usually) because of an active channel, but it's not specific to the channel itself. Signed-off-by: Rusty Russell --- lightningd/channel.c | 2 +- lightningd/connect_control.c | 63 +++++++++++---------- lightningd/connect_control.h | 9 ++- lightningd/peer_control.c | 14 +++-- lightningd/test/run-invoice-select-inchan.c | 6 +- wallet/test/run-wallet.c | 6 +- 6 files changed, 56 insertions(+), 44 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 640c3f390822..c25a163d5d55 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -931,7 +931,7 @@ static void err_and_reconnect(struct channel *channel, channel_set_owner(channel, NULL); /* Their address only useful if we connected to them */ - try_reconnect(channel, seconds_before_reconnect, + try_reconnect(channel, channel->peer, seconds_before_reconnect, channel->peer->connected_incoming ? NULL : &channel->peer->addr); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index d08e52d1829b..8407c1ff0a04 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -77,7 +77,6 @@ static struct command_result *connect_cmd_succeed(struct command *cmd, static void try_connect(const tal_t *ctx, struct lightningd *ld, const struct node_id *id, - struct channel *channel, u32 seconds_delay, const struct wireaddr_internal *addrhint); @@ -157,7 +156,7 @@ static struct command_result *json_connect(struct command *cmd, } else addr = NULL; - try_connect(cmd, cmd->ld, &id, NULL, 0, addr); + try_connect(cmd, cmd->ld, &id, 0, addr); /* Leave this here for peer_connected or connect_failed. */ new_connect(cmd->ld, &id, cmd); @@ -178,8 +177,6 @@ AUTODATA(json_command, &connect_command); struct delayed_reconnect { struct lightningd *ld; struct node_id id; - /* May be unset if there's no associated channel */ - struct channel *channel; u32 seconds_delayed; struct wireaddr_internal *addrhint; }; @@ -196,12 +193,6 @@ static void gossipd_got_addrs(struct subd *subd, fatal("Gossipd gave bad GOSSIPD_GET_ADDRS_REPLY %s", tal_hex(msg, msg)); - /* Might have gone onchain (if it was actually freed, we were too). */ - if (d->channel && !channel_active(d->channel)) { - tal_free(d); - return; - } - connectmsg = towire_connectd_connect_to_peer(NULL, &d->id, d->seconds_delayed, @@ -219,20 +210,19 @@ static void do_connect(struct delayed_reconnect *d) subd_req(d, d->ld->gossip, take(msg), -1, 0, gossipd_got_addrs, d); } -/* channel may be NULL here */ +/* peer may be NULL here */ static void try_connect(const tal_t *ctx, struct lightningd *ld, const struct node_id *id, - struct channel *channel, u32 seconds_delay, const struct wireaddr_internal *addrhint) { struct delayed_reconnect *d; + struct peer *peer; d = tal(ctx, struct delayed_reconnect); d->ld = ld; d->id = *id; - d->channel = channel; d->seconds_delayed = seconds_delay; d->addrhint = tal_dup_or_null(d, struct wireaddr_internal, addrhint); @@ -241,14 +231,22 @@ static void try_connect(const tal_t *ctx, return; } - /* We never have a delay when connecting without a channel */ - assert(channel); - channel_set_billboard(channel, false, - tal_fmt(tmpctx, - "Will attempt reconnect " - "in %u seconds", seconds_delay)); - log_debug(channel->log, "Will try reconnect in %u seconds", - seconds_delay); + log_peer_debug(ld->log, id, "Will try reconnect in %u seconds", + seconds_delay); + /* Update any channel billboards */ + peer = peer_by_id(ld, id); + if (peer) { + struct channel *channel; + list_for_each(&peer->channels, channel, list) { + if (!channel_active(channel)) + continue; + channel_set_billboard(channel, false, + tal_fmt(tmpctx, + "Will attempt reconnect " + "in %u seconds", + seconds_delay)); + } + } /* We fuzz the timer by up to 1 second, to avoid getting into * simultanous-reconnect deadlocks with peer. */ @@ -258,17 +256,17 @@ static void try_connect(const tal_t *ctx, do_connect, d)); } -void try_reconnect(struct channel *channel, +void try_reconnect(const tal_t *ctx, + struct peer *peer, u32 seconds_delay, const struct wireaddr_internal *addrhint) { - if (!channel->peer->ld->reconnect) + if (!peer->ld->reconnect) return; - try_connect(channel, - channel->peer->ld, - &channel->peer->id, - channel, + try_connect(ctx, + peer->ld, + &peer->id, seconds_delay, addrhint); } @@ -281,7 +279,7 @@ static void connect_failed(struct lightningd *ld, const u8 *msg) struct connect *c; u32 seconds_to_delay; struct wireaddr_internal *addrhint; - struct channel *channel; + struct peer *peer; if (!fromwire_connectd_connect_failed(tmpctx, msg, &id, &errcode, &errmsg, &seconds_to_delay, &addrhint)) @@ -295,9 +293,12 @@ static void connect_failed(struct lightningd *ld, const u8 *msg) } /* If we have an active channel, then reconnect. */ - channel = active_channel_by_id(ld, &id, NULL); - if (channel) - try_reconnect(channel, seconds_to_delay, addrhint); + peer = peer_by_id(ld, &id); + if (peer) { + struct channel *channel = peer_active_channel(peer); + if (channel) + try_reconnect(peer, peer, seconds_to_delay, addrhint); + } } void connect_succeeded(struct lightningd *ld, const struct peer *peer, diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 67beb003782b..50ffb45bf668 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -1,8 +1,11 @@ #ifndef LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H #define LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H #include "config.h" +#include +#include struct lightningd; +struct peer; struct pubkey; struct wireaddr_internal; @@ -10,8 +13,10 @@ struct wireaddr_internal; int connectd_init(struct lightningd *ld); void connectd_activate(struct lightningd *ld); -void try_reconnect(struct channel *channel, u32 seconds_delay, - const struct wireaddr_internal *addrhint TAKES); +void try_reconnect(const tal_t *ctx, + struct peer *peer, + u32 seconds_delay, + const struct wireaddr_internal *addrhint); void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 263222993fac..8268993c4d17 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1789,12 +1789,7 @@ static void setup_peer(struct peer *peer, u32 delay) struct channel *channel; struct channel_inflight *inflight; struct lightningd *ld = peer->ld; - - /* We can only have one active channel: make sure connectd - * knows to try reconnecting. */ - channel = peer_active_channel(peer); - if (channel) - try_reconnect(channel, delay, &peer->addr); + bool connect = false; list_for_each(&peer->channels, channel, list) { if (channel_unsaved(channel)) @@ -1812,7 +1807,14 @@ static void setup_peer(struct peer *peer, u32 delay) channel_watch_inflight(ld, channel, inflight); } + if (channel_active(channel)) + connect = true; } + + /* Make sure connectd knows to try reconnecting. */ + if (connect) + try_reconnect(peer, peer, delay, &peer->addr); + } void setup_peers(struct lightningd *ld) diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 7e7a0f73849a..dec7c3a72396 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -700,8 +700,10 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_warningfmt called!\n"); abort(); } /* Generated stub for try_reconnect */ -void try_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, - const struct wireaddr_internal *addrhint TAKES UNNEEDED) +void try_reconnect(const tal_t *ctx UNNEEDED, + struct peer *peer UNNEEDED, + u32 seconds_delay UNNEEDED, + const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "try_reconnect called!\n"); abort(); } /* Generated stub for version */ const char *version(void) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index f2e958053b9f..c067c987d9d4 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -829,8 +829,10 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_warningfmt called!\n"); abort(); } /* Generated stub for try_reconnect */ -void try_reconnect(struct channel *channel UNNEEDED, u32 seconds_delay UNNEEDED, - const struct wireaddr_internal *addrhint TAKES UNNEEDED) +void try_reconnect(const tal_t *ctx UNNEEDED, + struct peer *peer UNNEEDED, + u32 seconds_delay UNNEEDED, + const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "try_reconnect called!\n"); abort(); } /* Generated stub for watch_txid */ struct txwatch *watch_txid(const tal_t *ctx UNNEEDED, From cb5dc48cab222cadf426d72e430ee17e9eada733 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:29:18 +1030 Subject: [PATCH 0514/1530] lightningd: make setchannelfee handle multiple channels per peer. Signed-off-by: Rusty Russell --- doc/lightning-setchannel.7.md | 4 +- doc/lightning-setchannelfee.7.md | 6 ++- lightningd/peer_control.c | 93 ++++++++++++++++---------------- tests/test_pay.py | 2 +- 4 files changed, 55 insertions(+), 50 deletions(-) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 65755634ce6b..3f0260f0a0f6 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -22,7 +22,9 @@ will accept: we allow 2 a day, with a few extra occasionally). *id* is required and should contain a scid (short channel ID), channel id or peerid (pubkey) of the channel to be modified. If *id* is set to "all", the updates are applied to all channels in states -CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN. +CHANNELD\_NORMAL CHANNELD\_AWAITING\_LOCKIN or DUALOPEND_AWAITING_LOCKIN. +If *id* is a peerid, all channels with the +peer in those states are +changed. *feebase* is an optional value in millisatoshi that is added as base fee to any routed payment: if omitted, it is unchanged. It can be a whole number, or a whole diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index b0f97ca3e18e..2db2f2dbcd8e 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -12,12 +12,14 @@ DESCRIPTION The **setchannelfee** RPC command sets channel specific routing fees as defined in BOLT \#7. The channel has to be in normal or awaiting state. This can be checked by **listpeers** reporting a *state* of -CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN for the channel. +CHANNELD\_NORMAL, CHANNELD\_AWAITING\_LOCKIN or DUALOPEND_AWAITING_LOCKIN for the channel. *id* is required and should contain a scid (short channel ID), channel id or peerid (pubkey) of the channel to be modified. If *id* is set to "all", the fees for all channels are updated that are in state -CHANNELD\_NORMAL or CHANNELD\_AWAITING\_LOCKIN. +CHANNELD\_NORMAL, CHANNELD\_AWAITING\_LOCKIN or +DUALOPEND_AWAITING_LOCKIN. If *id* is a peerid, all channels with the +peer in those states are changed. *base* is an optional value in millisatoshi that is added as base fee to any routed payment. If the parameter is left out, the global config diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8268993c4d17..3deece449c14 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2145,35 +2145,46 @@ static struct command_result *param_channel_or_all(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, - struct channel **channel) + struct channel ***channels) { struct command_result *res; struct peer *peer; /* early return the easy case */ if (json_tok_streq(buffer, tok, "all")) { - *channel = NULL; + *channels = NULL; return NULL; } - /* Find channel by peer_id */ + /* Find channels by peer_id */ peer = peer_from_json(cmd->ld, buffer, tok); if (peer) { - *channel = peer_active_channel(peer); - if (!*channel) + struct channel *channel; + *channels = tal_arr(cmd, struct channel *, 0); + list_for_each(&peer->channels, channel, list) { + if (channel->state != CHANNELD_NORMAL + && channel->state != CHANNELD_AWAITING_LOCKIN + && channel->state != DUALOPEND_AWAITING_LOCKIN) + continue; + + tal_arr_expand(channels, channel); + } + if (tal_count(*channels) == 0) return command_fail(cmd, LIGHTNINGD, - "Could not find active channel of peer with that id"); + "Could not find any active channels of peer with that id"); return NULL; - /* Find channel by id or scid */ } else { - res = command_find_channel(cmd, buffer, tok, channel); + struct channel *channel; + res = command_find_channel(cmd, buffer, tok, &channel); if (res) return res; /* check channel is found and in valid state */ - if (!*channel) + if (!channel) return command_fail(cmd, LIGHTNINGD, "Could not find channel with that id"); + *channels = tal_arr(cmd, struct channel *, 1); + (*channels)[0] = channel; return NULL; } } @@ -2304,12 +2315,12 @@ static struct command_result *json_setchannelfee(struct command *cmd, { struct json_stream *response; struct peer *peer; - struct channel *channel; + struct channel **channels; u32 *base, *ppm, *delaysecs; /* Parse the JSON command */ if (!param(cmd, buffer, params, - p_req("id", param_channel_or_all, &channel), + p_req("id", param_channel_or_all, &channels), p_opt_def("base", param_msat_u32, &base, cmd->ld->config.fee_base), p_opt_def("ppm", param_number, &ppm, @@ -2318,13 +2329,6 @@ static struct command_result *json_setchannelfee(struct command *cmd, NULL)) return command_param_failed(); - if (channel - && channel->state != CHANNELD_NORMAL - && channel->state != CHANNELD_AWAITING_LOCKIN - && channel->state != DUALOPEND_AWAITING_LOCKIN) - return command_fail(cmd, LIGHTNINGD, - "Channel is in state %s", channel_state_name(channel)); - /* Open JSON response object for later iteration */ response = json_stream_success(cmd); json_add_num(response, "base", *base); @@ -2332,8 +2336,9 @@ static struct command_result *json_setchannelfee(struct command *cmd, json_array_start(response, "channels"); /* If the users requested 'all' channels we need to iterate */ - if (channel == NULL) { + if (channels == NULL) { list_for_each(&cmd->ld->peers, peer, list) { + struct channel *channel; list_for_each(&peer->channels, channel, list) { if (channel->state != CHANNELD_NORMAL && channel->state != CHANNELD_AWAITING_LOCKIN && @@ -2343,10 +2348,12 @@ static struct command_result *json_setchannelfee(struct command *cmd, *delaysecs, response, false); } } - /* single channel should be updated */ + /* single peer should be updated */ } else { - set_channel_config(cmd, channel, base, ppm, NULL, NULL, - *delaysecs, response, false); + for (size_t i = 0; i < tal_count(channels); i++) { + set_channel_config(cmd, channels[i], base, ppm, NULL, NULL, + *delaysecs, response, false); + } } /* Close and return response */ @@ -2376,13 +2383,13 @@ static struct command_result *json_setchannel(struct command *cmd, { struct json_stream *response; struct peer *peer; - struct channel *channel; + struct channel **channels; u32 *base, *ppm, *delaysecs; struct amount_msat *htlc_min, *htlc_max; /* Parse the JSON command */ if (!param(cmd, buffer, params, - p_req("id", param_channel_or_all, &channel), + p_req("id", param_channel_or_all, &channels), p_opt("feebase", param_msat_u32, &base), p_opt("feeppm", param_number, &ppm), p_opt("htlcmin", param_msat, &htlc_min), @@ -2398,37 +2405,31 @@ static struct command_result *json_setchannel(struct command *cmd, "htlcmax cannot be less than htlcmin"); } - if (channel - && channel->state != CHANNELD_NORMAL - && channel->state != CHANNELD_AWAITING_LOCKIN - && channel->state != DUALOPEND_AWAITING_LOCKIN) - return command_fail(cmd, LIGHTNINGD, - "Channel is in state %s", channel_state_name(channel)); - /* Open JSON response object for later iteration */ response = json_stream_success(cmd); json_array_start(response, "channels"); /* If the users requested 'all' channels we need to iterate */ - if (channel == NULL) { + if (channels == NULL) { list_for_each(&cmd->ld->peers, peer, list) { - channel = peer_active_channel(peer); - if (!channel) - continue; - if (channel->state != CHANNELD_NORMAL && - channel->state != CHANNELD_AWAITING_LOCKIN && - channel->state != DUALOPEND_AWAITING_LOCKIN) - continue; - set_channel_config(cmd, channel, base, ppm, + struct channel *channel; + list_for_each(&peer->channels, channel, list) { + if (channel->state != CHANNELD_NORMAL && + channel->state != CHANNELD_AWAITING_LOCKIN && + channel->state != DUALOPEND_AWAITING_LOCKIN) + continue; + set_channel_config(cmd, channel, base, ppm, + htlc_min, htlc_max, + *delaysecs, response, true); + } + } + /* single peer should be updated */ + } else { + for (size_t i = 0; i < tal_count(channels); i++) { + set_channel_config(cmd, channels[i], base, ppm, htlc_min, htlc_max, *delaysecs, response, true); } - - /* single channel should be updated */ - } else { - set_channel_config(cmd, channel, base, ppm, - htlc_min, htlc_max, - *delaysecs, response, true); } /* Close and return response */ diff --git a/tests/test_pay.py b/tests/test_pay.py index 867fec63af40..8942c6ccf961 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2001,7 +2001,7 @@ def channel_get_config(scid): assert(db_fees[0]['feerate_ppm'] == 143) # check if invalid scid raises proper error - with pytest.raises(RpcError, match=r'-1.*Could not find active channel of peer with that id'): + with pytest.raises(RpcError, match=r'-1.*Could not find any active channels of peer with that id'): result = l1.rpc.setchannel(l3.info['id'], 42, 43) with pytest.raises(RpcError, match=r'-32602.*id: should be a channel ID or short channel ID: invalid token'): result = l1.rpc.setchannel('f42' + scid[3:], 42, 43) From ba1242af3e6fe8c651188b4d289106c6252d9609 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:29:20 +1030 Subject: [PATCH 0515/1530] lightningd: add find_channel_by_scid More efficient to search a known peer than the whole set. Also, move find_channel_by_id() from channel_control.c into channel.c where we'd expect it. Signed-off-by: Rusty Russell --- lightningd/channel.c | 23 +++++++++++++++++++++++ lightningd/channel.h | 4 ++++ lightningd/channel_control.c | 12 ------------ wallet/test/run-wallet.c | 4 ---- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index c25a163d5d55..e0535212bfd5 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -678,6 +678,29 @@ struct channel *channel_by_cid(struct lightningd *ld, return NULL; } +struct channel *find_channel_by_id(const struct peer *peer, + const struct channel_id *cid) +{ + struct channel *c; + + list_for_each(&peer->channels, c, list) { + if (channel_id_eq(&c->cid, cid)) + return c; + } + return NULL; +} + +struct channel *find_channel_by_scid(const struct peer *peer, + const struct short_channel_id *scid) +{ + struct channel *c; + + list_for_each(&peer->channels, c, list) { + if (c->scid && short_channel_id_eq(c->scid, scid)) + return c; + } + return NULL; +} void channel_set_last_tx(struct channel *channel, struct bitcoin_tx *tx, diff --git a/lightningd/channel.h b/lightningd/channel.h index 676b14a75990..2583070c12e5 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -418,6 +418,10 @@ struct channel *channel_by_cid(struct lightningd *ld, struct channel *find_channel_by_id(const struct peer *peer, const struct channel_id *cid); +/* Find this channel within peer */ +struct channel *find_channel_by_scid(const struct peer *peer, + const struct short_channel_id *scid); + void channel_set_last_tx(struct channel *channel, struct bitcoin_tx *tx, const struct bitcoin_signature *sig, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index a16d6ee57cf6..c3721ef10a2a 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -907,18 +907,6 @@ void channel_notify_new_block(struct lightningd *ld, tal_free(to_forget); } -struct channel *find_channel_by_id(const struct peer *peer, - const struct channel_id *cid) -{ - struct channel *c; - - list_for_each(&peer->channels, c, list) { - if (channel_id_eq(&c->cid, cid)) - return c; - } - return NULL; -} - /* Since this could vanish while we're checking with bitcoind, we need to save * the details and re-lookup. * diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index c067c987d9d4..1f8b3617631b 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -133,10 +133,6 @@ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED, /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } -/* Generated stub for find_channel_by_id */ -struct channel *find_channel_by_id(const struct peer *peer UNNEEDED, - const struct channel_id *cid UNNEEDED) -{ fprintf(stderr, "find_channel_by_id called!\n"); abort(); } /* Generated stub for fromwire_channeld_dev_memleak_reply */ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } From f85425d1066ff3450cf63916e8240cd90b6c301f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:29:20 +1030 Subject: [PATCH 0516/1530] lightningd: don't assume a single channel per peer. Signed-off-by: Rusty Russell --- lightningd/routehint.c | 20 ++++++++++++++++---- lightningd/test/run-invoice-select-inchan.c | 7 ++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/lightningd/routehint.c b/lightningd/routehint.c index a9879e3a38a2..29a1439bc978 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -99,14 +99,25 @@ routehint_candidates(const tal_t *ctx, if (!peer) { log_debug(ld->log, "%s: unknown peer", type_to_string(tmpctx, - struct short_channel_id, - &r->short_channel_id)); + struct node_id, + &r->pubkey)); continue; } - /* Does it have a channel in state CHANNELD_NORMAL */ - candidate.c = peer_normal_channel(peer); + /* Check channel is in CHANNELD_NORMAL */ + candidate.c = find_channel_by_scid(peer, &r->short_channel_id); if (!candidate.c) { + log_debug(ld->log, "%s: channel not found in peer %s", + type_to_string(tmpctx, + struct short_channel_id, + &r->short_channel_id), + type_to_string(tmpctx, + struct node_id, + &r->pubkey)); + continue; + } + + if (candidate.c->state != CHANNELD_NORMAL) { log_debug(ld->log, "%s: abnormal channel", type_to_string(tmpctx, struct short_channel_id, @@ -120,6 +131,7 @@ routehint_candidates(const tal_t *ctx, * capacity ceiling. We *could* do multiple HTLCs, * but presumably that would defeat the spirit of the * limit anyway */ + /* FIXME: Present max capacity of multiple channels? */ candidate.capacity = channel_amount_receivable(candidate.c); if (amount_msat_greater(candidate.capacity, htlc_max)) candidate.capacity = htlc_max; diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index dec7c3a72396..17e3c9d1ba29 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -191,6 +191,10 @@ u8 *featurebits_or(const tal_t *ctx UNNEEDED, const u8 *f1 TAKES UNNEEDED, const struct channel *find_channel_by_id(const struct peer *peer UNNEEDED, const struct channel_id *cid UNNEEDED) { fprintf(stderr, "find_channel_by_id called!\n"); abort(); } +/* Generated stub for find_channel_by_scid */ +struct channel *find_channel_by_scid(const struct peer *peer UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "find_channel_by_scid called!\n"); abort(); } /* Generated stub for find_plugin_for_command */ struct plugin *find_plugin_for_command(struct lightningd *ld UNNEEDED, const char *cmd_name UNNEEDED) @@ -585,9 +589,6 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name /* Generated stub for peer_active_channel */ struct channel *peer_active_channel(struct peer *peer UNNEEDED) { fprintf(stderr, "peer_active_channel called!\n"); abort(); } -/* Generated stub for peer_normal_channel */ -struct channel *peer_normal_channel(struct peer *peer UNNEEDED) -{ fprintf(stderr, "peer_normal_channel called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ void peer_restart_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED, From 21e1d68e3be77caca8da3ec225f48ebf5ff5a509 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:29:20 +1030 Subject: [PATCH 0517/1530] lightningd: remove (most) functions to search channels by status. This is generally verboten now, since there can be multiple. There are a few exceptions: 1. We sometimes want to know if there are *any* active channels. 2. Some dev commands still take peer id when they mean channel_id. 3. We still allow peer id when it's fully determined. Signed-off-by: Rusty Russell Changelog-Changed: JSON-RPC: `close` by peer id will fail if there is more than one live channel (use `channel_id` or `short_channel_id` as id arg). --- lightningd/channel.c | 74 +++++++--------- lightningd/channel.h | 21 ++--- lightningd/channel_control.c | 12 ++- lightningd/closing_control.c | 19 ++++- lightningd/connect_control.c | 39 +++++---- lightningd/dual_open_control.c | 29 ++----- lightningd/opening_control.c | 12 +-- lightningd/pay.c | 21 ++++- lightningd/peer_control.c | 93 +++++++++++++++------ lightningd/test/run-invoice-select-inchan.c | 9 +- tests/test_connection.py | 2 +- 11 files changed, 181 insertions(+), 150 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index e0535212bfd5..49ea854d464f 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -549,26 +549,44 @@ const char *channel_state_str(enum channel_state state) return "unknown"; } -struct channel *peer_unsaved_channel(struct peer *peer) +struct channel *peer_any_active_channel(struct peer *peer, bool *others) { - struct channel *channel; + struct channel *channel, *ret = NULL; list_for_each(&peer->channels, channel, list) { - if (channel_unsaved(channel)) - return channel; + if (!channel_active(channel)) + continue; + /* Already found one? */ + if (ret) { + if (others) + *others = true; + } else { + if (others) + *others = false; + ret = channel; + } } - return NULL; + return ret; } -struct channel *peer_active_channel(struct peer *peer) +struct channel *peer_any_unsaved_channel(struct peer *peer, bool *others) { - struct channel *channel; + struct channel *channel, *ret = NULL; list_for_each(&peer->channels, channel, list) { - if (channel_active(channel)) - return channel; + if (!channel_unsaved(channel)) + continue; + /* Already found one? */ + if (ret) { + if (others) + *others = true; + } else { + if (others) + *others = false; + ret = channel; + } } - return NULL; + return ret; } struct channel_inflight *channel_inflight_find(struct channel *channel, @@ -583,42 +601,6 @@ struct channel_inflight *channel_inflight_find(struct channel *channel, return NULL; } -struct channel *peer_normal_channel(struct peer *peer) -{ - struct channel *channel; - - list_for_each(&peer->channels, channel, list) { - if (channel->state == CHANNELD_NORMAL) - return channel; - } - return NULL; -} - -struct channel *active_channel_by_id(struct lightningd *ld, - const struct node_id *id, - struct uncommitted_channel **uc) -{ - struct peer *peer = peer_by_id(ld, id); - if (!peer) { - if (uc) - *uc = NULL; - return NULL; - } - - if (uc) - *uc = peer->uncommitted_channel; - return peer_active_channel(peer); -} - -struct channel *unsaved_channel_by_id(struct lightningd *ld, - const struct node_id *id) -{ - struct peer *peer = peer_by_id(ld, id); - if (!peer) - return NULL; - return peer_unsaved_channel(peer); -} - struct channel *active_channel_by_scid(struct lightningd *ld, const struct short_channel_id *scid) { diff --git a/lightningd/channel.h b/lightningd/channel.h index 2583070c12e5..7c42752bfbf4 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -385,23 +385,12 @@ void channel_set_state(struct channel *channel, const char *channel_change_state_reason_str(enum state_change reason); -/* Find a channel which is not yet saved to disk */ -struct channel *peer_unsaved_channel(struct peer *peer); - -/* Find a channel which is not onchain, if any */ -struct channel *peer_active_channel(struct peer *peer); - -/* Find a channel which is in state CHANNELD_NORMAL, if any */ -struct channel *peer_normal_channel(struct peer *peer); +/* Find a channel which is not onchain, if any: sets *others if there + * is more than one. */ +struct channel *peer_any_active_channel(struct peer *peer, bool *others); -/* Get active channel for peer, optionally any uncommitted_channel. */ -struct channel *active_channel_by_id(struct lightningd *ld, - const struct node_id *id, - struct uncommitted_channel **uc); - -/* Get unsaved channel for peer */ -struct channel *unsaved_channel_by_id(struct lightningd *ld, - const struct node_id *id); +/* Find a channel which is not yet saved to disk */ +struct channel *peer_any_unsaved_channel(struct peer *peer, bool *others); struct channel *channel_by_dbid(struct lightningd *ld, const u64 dbid); diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index c3721ef10a2a..f8f369733733 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1052,6 +1052,7 @@ static struct command_result *json_dev_feerate(struct command *cmd, struct json_stream *response; struct channel *channel; const u8 *msg; + bool more_than_one; if (!param(cmd, buffer, params, p_req("id", param_node_id, &id), @@ -1063,9 +1064,12 @@ static struct command_result *json_dev_feerate(struct command *cmd, if (!peer) return command_fail(cmd, LIGHTNINGD, "Peer not connected"); - channel = peer_active_channel(peer); + channel = peer_any_active_channel(peer, &more_than_one); if (!channel || !channel->owner || channel->state != CHANNELD_NORMAL) return command_fail(cmd, LIGHTNINGD, "Peer bad state"); + /* This is a dev command: fix the api if you need this! */ + if (more_than_one) + return command_fail(cmd, LIGHTNINGD, "More than one channel"); msg = towire_channeld_feerates(NULL, *feerate, feerate_min(cmd->ld, NULL), @@ -1110,6 +1114,7 @@ static struct command_result *json_dev_quiesce(struct command *cmd, struct peer *peer; struct channel *channel; const u8 *msg; + bool more_than_one; if (!param(cmd, buffer, params, p_req("id", param_node_id, &id), @@ -1120,9 +1125,12 @@ static struct command_result *json_dev_quiesce(struct command *cmd, if (!peer) return command_fail(cmd, LIGHTNINGD, "Peer not connected"); - channel = peer_active_channel(peer); + channel = peer_any_active_channel(peer, &more_than_one); if (!channel || !channel->owner || channel->state != CHANNELD_NORMAL) return command_fail(cmd, LIGHTNINGD, "Peer bad state"); + /* This is a dev command: fix the api if you need this! */ + if (more_than_one) + return command_fail(cmd, LIGHTNINGD, "More than one channel"); msg = towire_channeld_dev_quiesce(NULL); subd_req(channel->owner, channel->owner, take(msg), -1, 0, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index ef85657f77de..285c5dd26485 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -577,9 +577,14 @@ static struct command_result *json_close(struct command *cmd, return command_param_failed(); peer = peer_from_json(cmd->ld, buffer, idtok); - if (peer) - channel = peer_active_channel(peer); - else { + if (peer) { + bool more_than_one; + channel = peer_any_active_channel(peer, &more_than_one); + if (channel && more_than_one) { + return command_fail(cmd, LIGHTNINGD, + "Peer has multiple channels: use channel_id or short_channel_id"); + } + } else { struct command_result *res; res = command_find_channel(cmd, buffer, idtok, &channel); if (res) @@ -587,13 +592,19 @@ static struct command_result *json_close(struct command *cmd, } if (!channel && peer) { + bool more_than_one; struct uncommitted_channel *uc = peer->uncommitted_channel; if (uc) { /* Easy case: peer can simply be forgotten. */ kill_uncommitted_channel(uc, "close command called"); goto discard_unopened; } - if ((channel = peer_unsaved_channel(peer))) { + channel = peer_any_unsaved_channel(peer, &more_than_one); + if (channel) { + if (more_than_one) { + return command_fail(cmd, LIGHTNINGD, + "Peer has multiple channels: use channel_id or short_channel_id"); + } channel_unsaved_close_conn(channel, "close command called"); goto discard_unopened; diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 8407c1ff0a04..a31a611a6fbc 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -295,8 +295,7 @@ static void connect_failed(struct lightningd *ld, const u8 *msg) /* If we have an active channel, then reconnect. */ peer = peer_by_id(ld, &id); if (peer) { - struct channel *channel = peer_active_channel(peer); - if (channel) + if (peer_any_active_channel(peer, NULL)) try_reconnect(peer, peer, seconds_to_delay, addrhint); } } @@ -332,22 +331,34 @@ static void peer_already_connected(struct lightningd *ld, const u8 *msg) static void peer_please_disconnect(struct lightningd *ld, const u8 *msg) { struct node_id id; - struct channel *c; - struct uncommitted_channel *uc; + struct peer *peer; + struct channel *c, **channels; if (!fromwire_connectd_reconnected(msg, &id)) fatal("Bad msg %s from connectd", tal_hex(tmpctx, msg)); - c = active_channel_by_id(ld, &id, &uc); - if (uc) - kill_uncommitted_channel(uc, "Reconnected"); - else if (c) { - channel_cleanup_commands(c, "Reconnected"); - channel_fail_reconnect(c, "Reconnected"); - } - else if ((c = unsaved_channel_by_id(ld, &id))) { - log_info(c->log, "Killing opening daemon: Reconnected"); - channel_unsaved_close_conn(c, "Reconnected"); + peer = peer_by_id(ld, &id); + if (!peer) + return; + + /* Freeing channels can free peer, so gather first. */ + channels = tal_arr(tmpctx, struct channel *, 0); + list_for_each(&peer->channels, c, list) + tal_arr_expand(&channels, c); + + if (peer->uncommitted_channel) + kill_uncommitted_channel(peer->uncommitted_channel, + "Reconnected"); + + for (size_t i = 0; i < tal_count(channels); i++) { + c = channels[i]; + if (channel_active(c)) { + channel_cleanup_commands(c, "Reconnected"); + channel_fail_reconnect(c, "Reconnected"); + } else if (channel_unsaved(c)) { + log_info(c->log, "Killing opening daemon: Reconnected"); + channel_unsaved_close_conn(c, "Reconnected"); + } } } diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index a968409f72f7..bab3e3fd40ea 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1782,7 +1782,7 @@ static void accepter_got_offer(struct subd *dualopend, { struct openchannel2_payload *payload; - if (peer_active_channel(channel->peer)) { + if (peer_any_active_channel(channel->peer, NULL)) { subd_send_msg(dualopend, take(towire_dualopend_fail(NULL, "Already have active channel"))); @@ -2584,13 +2584,13 @@ static struct command_result *json_openchannel_init(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } - channel = peer_active_channel(peer); + channel = peer_any_active_channel(peer, NULL); if (channel) { return command_fail(cmd, LIGHTNINGD, "Peer already %s", channel_state_name(channel)); } - channel = peer_unsaved_channel(peer); + channel = peer_any_unsaved_channel(peer, NULL); if (!channel) { channel = new_unsaved_channel(peer, peer->ld->config.fee_base, @@ -2839,18 +2839,6 @@ static void handle_commit_received(struct subd *dualopend, total_funding); if (channel->state == DUALOPEND_OPEN_INIT) { - if (peer_active_channel(channel->peer)) { - channel_saved_err_broken_reconn(channel, - "Already have active" - " channel with %s", - type_to_string(tmpctx, - struct node_id, - &channel->peer->id)); - channel->open_attempt - = tal_free(channel->open_attempt); - return; - } - if (!(inflight = wallet_commit_channel(ld, channel, remote_commit, &remote_commit_sig, @@ -3091,15 +3079,8 @@ static struct command_result *json_queryrates(struct command *cmd, return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, "Peer not connected"); - /* We can't query rates for a peer we have a channel with */ - channel = peer_active_channel(peer); - if (channel) - return command_fail(cmd, LIGHTNINGD, "Peer in state %s," - " can't query peer's rates if already" - " have a channel", - channel_state_name(channel)); - - channel = peer_unsaved_channel(peer); + /* FIXME: This is wrong: we should always create a new channel? */ + channel = peer_any_unsaved_channel(peer, NULL); if (!channel) { channel = new_unsaved_channel(peer, peer->ld->config.fee_base, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 23c351ce0985..f2e68186ecd2 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -478,7 +478,7 @@ static void opening_fundee_finished(struct subd *openingd, remote_commit->chainparams = chainparams; /* openingd should never accept them funding channel in this case. */ - if (peer_active_channel(uc->peer)) { + if (peer_any_active_channel(uc->peer, NULL)) { uncommitted_channel_disconnect(uc, LOG_BROKEN, "already have active channel"); @@ -770,7 +770,7 @@ static void opening_got_offer(struct subd *openingd, struct openchannel_hook_payload *payload; /* Tell them they can't open, if we already have open channel. */ - if (peer_active_channel(uc->peer)) { + if (peer_any_active_channel(uc->peer, NULL)) { subd_send_msg(openingd, take(towire_openingd_got_offer_reply( NULL, "Already have active channel", NULL, NULL))); @@ -935,7 +935,6 @@ static struct command_result *json_fundchannel_complete(struct command *cmd, struct node_id *id; struct bitcoin_txid *funding_txid; struct peer *peer; - struct channel *channel; struct wally_psbt *funding_psbt; u32 *funding_txout_num = NULL; struct funding_channel *fc; @@ -951,11 +950,6 @@ static struct command_result *json_fundchannel_complete(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } - channel = peer_active_channel(peer); - if (channel) - return command_fail(cmd, LIGHTNINGD, "Peer already %s", - channel_state_name(channel)); - if (!peer->is_connected) return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, "Peer not connected"); @@ -1133,7 +1127,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, "Peer not connected"); - channel = peer_active_channel(peer); + channel = peer_any_active_channel(peer, NULL); if (channel) { return command_fail(cmd, LIGHTNINGD, "Peer already %s", channel_state_name(channel)); diff --git a/lightningd/pay.c b/lightningd/pay.c index 41f47f42c828..00acdece7815 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -826,6 +826,23 @@ static struct command_result *check_offer_usage(struct command *cmd, return NULL; } +static struct channel *find_channel_for_htlc_add(struct lightningd *ld, + const struct node_id *node) +{ + struct channel *channel; + struct peer *peer = peer_by_id(ld, node); + if (!peer) + return NULL; + + list_for_each(&peer->channels, channel, list) { + if (channel_can_add_htlc(channel)) { + return channel; + } + } + + return NULL; +} + /* destination/route_channels/route_nodes are NULL (and path_secrets may be NULL) * if we're sending a raw onion. */ static struct command_result * @@ -1014,8 +1031,8 @@ send_payment_core(struct lightningd *ld, if (offer_err) return offer_err; - channel = active_channel_by_id(ld, &first_hop->node_id, NULL); - if (!channel || !channel_can_add_htlc(channel)) { + channel = find_channel_for_htlc_add(ld, &first_hop->node_id); + if (!channel) { struct json_stream *data = json_stream_fail(cmd, PAY_TRY_OTHER_ROUTE, "No connection to first " diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 3deece449c14..759f58208e3a 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1884,8 +1884,9 @@ static struct command_result *json_disconnect(struct command *cmd, struct node_id *id; struct disconnect_command *dc; struct peer *peer; - struct channel *channel; + struct channel *channel, **channels; bool *force; + bool disconnected = false; if (!param(cmd, buffer, params, p_req("id", param_node_id, &id), @@ -1900,32 +1901,69 @@ static struct command_result *json_disconnect(struct command *cmd, if (!peer->is_connected) { return command_fail(cmd, LIGHTNINGD, "Peer not connected"); } - channel = peer_active_channel(peer); - if (channel) { - if (*force) { - channel_fail_reconnect(channel, - "disconnect command force=true"); - goto wait_for_connectd; - } - return command_fail(cmd, LIGHTNINGD, "Peer is in state %s", + + channel = peer_any_active_channel(peer, NULL); + if (channel && !*force) { + return command_fail(cmd, LIGHTNINGD, + "Peer has (at least one) channel in state %s", channel_state_name(channel)); } - channel = peer_unsaved_channel(peer); - if (channel) { - channel_unsaved_close_conn(channel, "disconnect command"); - goto wait_for_connectd; + + /* Careful here! Disconnecting can free peer! */ + channels = tal_arr(cmd, struct channel *, 0); + list_for_each(&peer->channels, channel, list) { + if (!channel->owner) + continue; + if (!channel->owner->talks_to_peer) + continue; + + switch (channel->state) { + case DUALOPEND_OPEN_INIT: + case CHANNELD_AWAITING_LOCKIN: + case CHANNELD_NORMAL: + case CHANNELD_SHUTTING_DOWN: + case DUALOPEND_AWAITING_LOCKIN: + case CLOSINGD_SIGEXCHANGE: + tal_arr_expand(&channels, channel); + continue; + case CLOSINGD_COMPLETE: + case AWAITING_UNILATERAL: + case FUNDING_SPEND_SEEN: + case ONCHAIN: + case CLOSED: + /* We don't expect these to have owners who connect! */ + log_broken(channel->log, + "Don't expect owner %s in state %s", + channel->owner->name, + channel_state_name(channel)); + continue; + } + abort(); } + + /* This can free peer too! */ if (peer->uncommitted_channel) { kill_uncommitted_channel(peer->uncommitted_channel, "disconnect command"); - goto wait_for_connectd; + disconnected = true; } - /* It's just sitting in connectd. */ - subd_send_msg(cmd->ld->connectd, - take(towire_connectd_discard_peer(NULL, id))); + for (size_t i = 0; i < tal_count(channels); i++) { + if (channel_unsaved(channels[i])) + channel_unsaved_close_conn(channels[i], + "disconnect command"); + else + channel_fail_reconnect(channels[i], + "disconnect command"); + disconnected = true; + } + + if (!disconnected) { + /* It's just sitting in connectd. */ + subd_send_msg(cmd->ld->connectd, + take(towire_connectd_discard_peer(NULL, id))); + } -wait_for_connectd: /* Connectd tells us when it's finally disconnected */ dc = tal(cmd, struct disconnect_command); dc->cmd = cmd; @@ -2459,6 +2497,7 @@ static struct command_result *json_sign_last_tx(struct command *cmd, struct peer *peer; struct json_stream *response; struct channel *channel; + bool more_than_one; if (!param(cmd, buffer, params, p_req("id", param_node_id, &peerid), @@ -2470,10 +2509,10 @@ static struct command_result *json_sign_last_tx(struct command *cmd, return command_fail(cmd, LIGHTNINGD, "Could not find peer with that id"); } - channel = peer_active_channel(peer); - if (!channel) { + channel = peer_any_active_channel(peer, &more_than_one); + if (!channel || more_than_one) { return command_fail(cmd, LIGHTNINGD, - "Could not find active channel"); + "Could not find single active channel"); } response = json_stream_success(cmd); @@ -2521,6 +2560,7 @@ static struct command_result *json_dev_fail(struct command *cmd, struct node_id *peerid; struct peer *peer; struct channel *channel; + bool more_than_one; if (!param(cmd, buffer, params, p_req("id", param_node_id, &peerid), @@ -2533,10 +2573,10 @@ static struct command_result *json_dev_fail(struct command *cmd, "Could not find peer with that id"); } - channel = peer_active_channel(peer); - if (!channel) { + channel = peer_any_active_channel(peer, &more_than_one); + if (!channel || more_than_one) { return command_fail(cmd, LIGHTNINGD, - "Could not find active channel with peer"); + "Could not find single active channel with peer"); } channel_fail_permanent(channel, @@ -2570,6 +2610,7 @@ static struct command_result *json_dev_reenable_commit(struct command *cmd, struct peer *peer; u8 *msg; struct channel *channel; + bool more_than_one; if (!param(cmd, buffer, params, p_req("id", param_node_id, &peerid), @@ -2582,8 +2623,8 @@ static struct command_result *json_dev_reenable_commit(struct command *cmd, "Could not find peer with that id"); } - channel = peer_active_channel(peer); - if (!channel) { + channel = peer_any_active_channel(peer, &more_than_one); + if (!channel || more_than_one) { return command_fail(cmd, LIGHTNINGD, "Peer has no active channel"); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 17e3c9d1ba29..ec4c220d223e 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -586,9 +586,9 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint64_t **num UNNEEDED) { fprintf(stderr, "param_u64 called!\n"); abort(); } -/* Generated stub for peer_active_channel */ -struct channel *peer_active_channel(struct peer *peer UNNEEDED) -{ fprintf(stderr, "peer_active_channel called!\n"); abort(); } +/* Generated stub for peer_any_active_channel */ +struct channel *peer_any_active_channel(struct peer *peer UNNEEDED, bool *others UNNEEDED) +{ fprintf(stderr, "peer_any_active_channel called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ void peer_restart_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED, @@ -609,9 +609,6 @@ bool peer_start_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UN bool peer_start_openingd(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } -/* Generated stub for peer_unsaved_channel */ -struct channel *peer_unsaved_channel(struct peer *peer UNNEEDED) -{ fprintf(stderr, "peer_unsaved_channel called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) diff --git a/tests/test_connection.py b/tests/test_connection.py index 97e9f91e5a9d..0af12c9cfb23 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2586,7 +2586,7 @@ def test_disconnectpeer(node_factory, bitcoind): mine_funding_to_announce(bitcoind, [l1, l2, l3]) # disconnecting a non gossiping peer results in error - with pytest.raises(RpcError, match=r'Peer is in state CHANNELD_NORMAL'): + with pytest.raises(RpcError, match=r'Peer has \(at least one\) channel in state CHANNELD_NORMAL'): l1.rpc.disconnect(l3.info['id']) From debc1b90d3058374bf4b6d7bc54a7d2fa2ee99f8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:29:20 +1030 Subject: [PATCH 0518/1530] lightningd: remove checks which prevent us from opening multiple channels. Signed-off-by: Rusty Russell Changelog-Added: Protocol: we now support opening multiple channels with the same peer. --- lightningd/dual_open_control.c | 13 ----- lightningd/opening_control.c | 23 --------- tests/test_connection.py | 91 ++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 36 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index bab3e3fd40ea..20cf2337d82b 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1782,13 +1782,6 @@ static void accepter_got_offer(struct subd *dualopend, { struct openchannel2_payload *payload; - if (peer_any_active_channel(channel->peer, NULL)) { - subd_send_msg(dualopend, - take(towire_dualopend_fail(NULL, - "Already have active channel"))); - return; - } - if (channel->open_attempt) { subd_send_msg(dualopend, take(towire_dualopend_fail(NULL, @@ -2584,12 +2577,6 @@ static struct command_result *json_openchannel_init(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } - channel = peer_any_active_channel(peer, NULL); - if (channel) { - return command_fail(cmd, LIGHTNINGD, "Peer already %s", - channel_state_name(channel)); - } - channel = peer_any_unsaved_channel(peer, NULL); if (!channel) { channel = new_unsaved_channel(peer, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index f2e68186ecd2..bc9aac4bd9fb 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -477,14 +477,6 @@ static void opening_fundee_finished(struct subd *openingd, remote_commit->chainparams = chainparams; - /* openingd should never accept them funding channel in this case. */ - if (peer_any_active_channel(uc->peer, NULL)) { - uncommitted_channel_disconnect(uc, - LOG_BROKEN, - "already have active channel"); - goto failed; - } - derive_channel_id(&cid, &funding); /* old_remote_per_commit not valid yet, copy valid one. */ @@ -769,14 +761,6 @@ static void opening_got_offer(struct subd *openingd, { struct openchannel_hook_payload *payload; - /* Tell them they can't open, if we already have open channel. */ - if (peer_any_active_channel(uc->peer, NULL)) { - subd_send_msg(openingd, - take(towire_openingd_got_offer_reply( - NULL, "Already have active channel", NULL, NULL))); - return; - } - payload = tal(openingd, struct openchannel_hook_payload); payload->openingd = openingd; payload->uc = uc; @@ -1066,7 +1050,6 @@ static struct command_result *json_fundchannel_start(struct command *cmd, struct funding_channel * fc = tal(cmd, struct funding_channel); struct node_id *id; struct peer *peer; - struct channel *channel; bool *announce_channel; u32 *feerate_per_kw; @@ -1127,12 +1110,6 @@ static struct command_result *json_fundchannel_start(struct command *cmd, return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, "Peer not connected"); - channel = peer_any_active_channel(peer, NULL); - if (channel) { - return command_fail(cmd, LIGHTNINGD, "Peer already %s", - channel_state_name(channel)); - } - if (!peer->uncommitted_channel) { if (feature_negotiated(cmd->ld->our_features, peer->their_features, diff --git a/tests/test_connection.py b/tests/test_connection.py index 0af12c9cfb23..5224c429cb9b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3773,3 +3773,94 @@ def test_ping_timeout(node_factory): wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected'] is False, timeout=45 + 45 + 5) wait_for(lambda: (l1.daemon.is_in_log('Last ping unreturned: hanging up') or l2.daemon.is_in_log('Last ping unreturned: hanging up'))) + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +def test_multichan(node_factory, executor, bitcoind): + """Test multiple channels between same nodes""" + l1, l2, l3 = node_factory.line_graph(3) + + scid12 = l1.get_channel_scid(l2) + scid23a = l2.get_channel_scid(l3) + + # Now fund *second* channel l2->l3. + bitcoind.rpc.sendtoaddress(l2.rpc.newaddr()['bech32'], 0.1) + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l2]) + l2.rpc.fundchannel(l3.info['id'], '0.05btc') + assert(len(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) == 2) + assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2) + + bitcoind.generate_block(1, wait_for_mempool=1) + + # Dance around to get the *other* scid. + wait_for(lambda: all(['short_channel_id' in c for c in l3.rpc.listpeers()['peers'][0]['channels']])) + scids = [c['short_channel_id'] for c in l3.rpc.listpeers()['peers'][0]['channels']] + assert len(scids) == 2 + + if scids[0] == scid23a: + scid23b = scids[1] + else: + assert scids[1] == scid23a + scid23b = scids[0] + + # Test paying by each, + route = [{'msatoshi': 100001001, + 'id': l2.info['id'], + 'delay': 11, + # Unneeded + 'channel': scid12}, + {'msatoshi': 100000000, + 'id': l3.info['id'], + 'delay': 5, + 'channel': scid23a}] + before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + inv = l3.rpc.invoice(100000000, "invoice", "invoice") + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) + l1.rpc.waitsendpay(inv['payment_hash']) + # Wait until HTLCs fully settled + wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []]) + after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + + if before[0]['short_channel_id'] == scid23a: + chan23a_idx = 0 + chan23b_idx = 1 + else: + chan23a_idx = 1 + chan23b_idx = 0 + + # Check it used the right scid! + assert before[chan23a_idx]['to_us_msat'] != after[chan23a_idx]['to_us_msat'] + assert before[chan23b_idx]['to_us_msat'] == after[chan23b_idx]['to_us_msat'] + + before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + route[1]['channel'] = scid23b + inv = l3.rpc.invoice(100000000, "invoice2", "invoice2") + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) + l1.rpc.waitsendpay(inv['payment_hash']) + # Wait until HTLCs fully settled + wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []]) + after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] + + # Check it used the right scid! + assert before[chan23a_idx]['to_us_msat'] == after[chan23a_idx]['to_us_msat'] + assert before[chan23b_idx]['to_us_msat'] != after[chan23b_idx]['to_us_msat'] + + # Make sure gossip works. + bitcoind.generate_block(5) + + wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 2) + + chans = l1.rpc.listchannels(source=l3.info['id'])['channels'] + if chans[0]['short_channel_id'] == scid23a: + chan23a = chans[0] + chan23b = chans[1] + else: + chan23a = chans[1] + chan23b = chans[0] + + assert chan23a['amount_msat'] == Millisatoshi(1000000000) + assert chan23a['short_channel_id'] == scid23a + assert chan23b['amount_msat'] == Millisatoshi(5000000000) + assert chan23b['short_channel_id'] == scid23b From 75596b3e0f8a18bef0a0c084d70408de076cb9bb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:29:20 +1030 Subject: [PATCH 0519/1530] lightningd: use a better channel if available to next hop. Signed-off-by: Rusty Russell --- lightningd/channel.c | 9 ----- lightningd/channel.h | 2 -- lightningd/peer_control.c | 22 +++++++----- lightningd/peer_control.h | 4 +++ lightningd/peer_htlcs.c | 38 +++++++++++++++++++-- lightningd/test/run-invoice-select-inchan.c | 8 ++--- tests/test_connection.py | 18 +++++----- 7 files changed, 66 insertions(+), 35 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 49ea854d464f..d0b90da19a9f 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -601,15 +601,6 @@ struct channel_inflight *channel_inflight_find(struct channel *channel, return NULL; } -struct channel *active_channel_by_scid(struct lightningd *ld, - const struct short_channel_id *scid) -{ - struct channel *chan = any_channel_by_scid(ld, scid); - if (chan && !channel_active(chan)) - chan = NULL; - return chan; -} - struct channel *any_channel_by_scid(struct lightningd *ld, const struct short_channel_id *scid) { diff --git a/lightningd/channel.h b/lightningd/channel.h index 7c42752bfbf4..db81ce8c484c 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -394,8 +394,6 @@ struct channel *peer_any_unsaved_channel(struct peer *peer, bool *others); struct channel *channel_by_dbid(struct lightningd *ld, const u64 dbid); -struct channel *active_channel_by_scid(struct lightningd *ld, - const struct short_channel_id *scid); struct channel *any_channel_by_scid(struct lightningd *ld, const struct short_channel_id *scid); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 759f58208e3a..fdedc84f5553 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -546,7 +546,7 @@ static void subtract_received_htlcs(const struct channel *channel, } } -static struct amount_msat channel_amount_spendable(const struct channel *channel) +struct amount_msat channel_amount_spendable(const struct channel *channel) { struct amount_msat spendable; @@ -1770,14 +1770,18 @@ command_find_channel(struct command *cmd, tok->end - tok->start, buffer + tok->start); } else if (json_to_short_channel_id(buffer, tok, &scid)) { - *channel = active_channel_by_scid(ld, &scid); - if (*channel) - return NULL; - - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Short channel ID not found: '%.*s'", - tok->end - tok->start, - buffer + tok->start); + *channel = any_channel_by_scid(ld, &scid); + if (!*channel) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Short channel ID not found: '%.*s'", + tok->end - tok->start, + buffer + tok->start); + if (!channel_active(*channel)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Short channel ID not active: '%.*s'", + tok->end - tok->start, + buffer + tok->start); + return NULL; } else { return command_fail_badparam(cmd, "id", buffer, tok, "should be a channel ID or short channel ID"); diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 4e3207609573..77037e17d74a 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -92,6 +92,10 @@ void channel_watch_funding(struct lightningd *ld, struct channel *channel); /* If this channel has a "wrong funding" shutdown, watch that too. */ void channel_watch_wrong_funding(struct lightningd *ld, struct channel *channel); +/* How much can we spend in this channel? */ +struct amount_msat channel_amount_spendable(const struct channel *channel); + +/* How much can we receive in this channel? */ struct amount_msat channel_amount_receivable(const struct channel *channel); /* Pull peers, channels and HTLCs from db, and wire them up. diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index e0d902c715a1..7a434bbd3a3b 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -621,11 +621,45 @@ static void forward_htlc(struct htlc_in *hin, { const u8 *failmsg; struct lightningd *ld = hin->key.channel->peer->ld; - struct channel *next = active_channel_by_scid(ld, scid); + struct channel *next; struct htlc_out *hout = NULL; + /* This is a shortcut for specifying next peer; doesn't mean + * the actual channel! */ + next = any_channel_by_scid(ld, scid); + if (next) { + struct peer *peer = next->peer; + struct channel *channel; + struct amount_msat best_spendable = channel_amount_spendable(next); + + /* Seek channel with largest spendable! */ + list_for_each(&peer->channels, channel, list) { + struct amount_msat spendable; + if (!channel_can_add_htlc(channel)) + continue; + spendable = channel_amount_spendable(channel); + if (!amount_msat_greater(spendable, best_spendable)) + continue; + + /* Don't override if fees differ... */ + if (channel->feerate_base != next->feerate_base + || channel->feerate_ppm != next->feerate_ppm) + continue; + /* Or if this would be below min for channel! */ + if (amount_msat_less(amt_to_forward, + channel->channel_info.their_config.htlc_minimum)) + continue; + + /* OK, it's better! */ + log_debug(next->log, "Chose a better channel: %s", + type_to_string(tmpctx, struct short_channel_id, + channel->scid)); + next = channel; + } + } + /* Unknown peer, or peer not ready. */ - if (!next || !next->scid) { + if (!next || !channel_active(next)) { local_fail_in_htlc(hin, take(towire_unknown_next_peer(NULL))); wallet_forwarded_payment_add(hin->key.channel->peer->ld->wallet, hin, next ? next->scid : NULL, NULL, diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index ec4c220d223e..81e5c1bae023 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -10,10 +10,10 @@ bool deprecated_apis = false; /* AUTOGENERATED MOCKS START */ -/* Generated stub for active_channel_by_scid */ -struct channel *active_channel_by_scid(struct lightningd *ld UNNEEDED, - const struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "active_channel_by_scid called!\n"); abort(); } +/* Generated stub for any_channel_by_scid */ +struct channel *any_channel_by_scid(struct lightningd *ld UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "any_channel_by_scid called!\n"); abort(); } /* Generated stub for bitcoind_getutxout_ */ void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, diff --git a/tests/test_connection.py b/tests/test_connection.py index 5224c429cb9b..0707c53caac4 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3784,11 +3784,11 @@ def test_multichan(node_factory, executor, bitcoind): scid12 = l1.get_channel_scid(l2) scid23a = l2.get_channel_scid(l3) - # Now fund *second* channel l2->l3. + # Now fund *second* channel l2->l3 (slightly larger) bitcoind.rpc.sendtoaddress(l2.rpc.newaddr()['bech32'], 0.1) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l2]) - l2.rpc.fundchannel(l3.info['id'], '0.05btc') + l2.rpc.fundchannel(l3.info['id'], '0.01001btc') assert(len(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) == 2) assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2) @@ -3830,9 +3830,9 @@ def test_multichan(node_factory, executor, bitcoind): chan23a_idx = 1 chan23b_idx = 0 - # Check it used the right scid! - assert before[chan23a_idx]['to_us_msat'] != after[chan23a_idx]['to_us_msat'] - assert before[chan23b_idx]['to_us_msat'] == after[chan23b_idx]['to_us_msat'] + # Check it used the larger channel! + assert before[chan23a_idx]['to_us_msat'] == after[chan23a_idx]['to_us_msat'] + assert before[chan23b_idx]['to_us_msat'] != after[chan23b_idx]['to_us_msat'] before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] route[1]['channel'] = scid23b @@ -3843,9 +3843,9 @@ def test_multichan(node_factory, executor, bitcoind): wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []]) after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] - # Check it used the right scid! - assert before[chan23a_idx]['to_us_msat'] == after[chan23a_idx]['to_us_msat'] - assert before[chan23b_idx]['to_us_msat'] != after[chan23b_idx]['to_us_msat'] + # Now the first channel is larger! + assert before[chan23a_idx]['to_us_msat'] != after[chan23a_idx]['to_us_msat'] + assert before[chan23b_idx]['to_us_msat'] == after[chan23b_idx]['to_us_msat'] # Make sure gossip works. bitcoind.generate_block(5) @@ -3862,5 +3862,5 @@ def test_multichan(node_factory, executor, bitcoind): assert chan23a['amount_msat'] == Millisatoshi(1000000000) assert chan23a['short_channel_id'] == scid23a - assert chan23b['amount_msat'] == Millisatoshi(5000000000) + assert chan23b['amount_msat'] == Millisatoshi(1001000000) assert chan23b['short_channel_id'] == scid23b From 4e8239fcfe8ad9e6311057df4beff67bf0a67c42 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:33 +1030 Subject: [PATCH 0520/1530] lightningd: don't tell connectd to discard peer unless no subds left. Otherwise it waits for subds to exit, but they don't. Plus, the others may still be talking! Signed-off-by: Rusty Russell --- lightningd/channel.c | 21 +++-------------- lightningd/connect_control.c | 25 +++++++++++++++++++++ lightningd/connect_control.h | 3 +++ lightningd/opening_common.c | 7 ++---- lightningd/peer_control.c | 8 +++---- lightningd/test/run-invoice-select-inchan.c | 6 ++--- wallet/test/run-wallet.c | 6 ++--- 7 files changed, 42 insertions(+), 34 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index d0b90da19a9f..789d75554161 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -20,11 +20,6 @@ #include #include -static bool connects_to_peer(struct subd *owner) -{ - return owner && owner->talks_to_peer; -} - void channel_set_owner(struct channel *channel, struct subd *owner) { struct subd *old_owner = channel->owner; @@ -32,20 +27,10 @@ void channel_set_owner(struct channel *channel, struct subd *owner) if (old_owner) { subd_release_channel(old_owner, channel); - if (channel->connected && !connects_to_peer(owner)) { - /* If shutting down, connectd no longer exists */ - if (channel->peer->ld->connectd) { - u8 *msg; - msg = towire_connectd_discard_peer( - NULL, - &channel->peer->id); - subd_send_msg(channel->peer->ld->connectd, - take(msg)); - } else - channel->peer->is_connected = false; - } + if (channel->connected) + maybe_disconnect_peer(channel->peer->ld, channel->peer); } - channel->connected = connects_to_peer(owner); + channel->connected = (owner && owner->talks_to_peer); } struct htlc_out *channel_has_htlc_out(struct channel *channel) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index a31a611a6fbc..df6993c811d7 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -601,6 +601,31 @@ void connectd_activate(struct lightningd *ld) io_loop(NULL, NULL); } +void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer) +{ + struct channel *channel; + + /* Any channels left which want to talk? */ + if (peer->uncommitted_channel) + return; + + list_for_each(&peer->channels, channel, list) { + if (!channel->owner) + continue; + if (channel->owner->talks_to_peer) + return; + } + + /* If shutting down, connectd no longer exists */ + if (!ld->connectd) { + peer->is_connected = false; + return; + } + + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, &peer->id))); +} + static struct command_result *json_sendcustommsg(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 50ffb45bf668..748ecb8eb9d7 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -21,4 +21,7 @@ void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr); +/* Disconnect a peer (if no subds want to talk any more) */ +void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer); + #endif /* LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H */ diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index f018b1c9159a..7442cea19a93 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -3,11 +3,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include @@ -31,6 +31,7 @@ static void destroy_uncommitted_channel(struct uncommitted_channel *uc) uc->peer->uncommitted_channel = NULL; + maybe_disconnect_peer(uc->peer->ld, uc->peer); maybe_delete_peer(uc->peer); } @@ -104,11 +105,7 @@ void uncommitted_channel_disconnect(struct uncommitted_channel *uc, enum log_level level, const char *desc) { - u8 *msg = towire_connectd_discard_peer(tmpctx, &uc->peer->id); log_(uc->log, level, NULL, false, "%s", desc); - /* NULL when we're shutting down */ - if (uc->peer->ld->connectd) - subd_send_msg(uc->peer->ld->connectd, msg); if (uc->fc && uc->fc->cmd) was_pending(command_fail(uc->fc->cmd, LIGHTNINGD, "%s", desc)); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index fdedc84f5553..2e70af6d86a2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1962,11 +1962,9 @@ static struct command_result *json_disconnect(struct command *cmd, disconnected = true; } - if (!disconnected) { - /* It's just sitting in connectd. */ - subd_send_msg(cmd->ld->connectd, - take(towire_connectd_discard_peer(NULL, id))); - } + /* It's just sitting in connectd? */ + if (!disconnected) + maybe_disconnect_peer(cmd->ld, peer); /* Connectd tells us when it's finally disconnected */ dc = tal(cmd, struct disconnect_command); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 81e5c1bae023..ba9193ef079a 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -450,6 +450,9 @@ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "log_ called!\n"); abort(); } +/* Generated stub for maybe_disconnect_peer */ +void maybe_disconnect_peer(struct lightningd *ld UNNEEDED, struct peer *peer UNNEEDED) +{ fprintf(stderr, "maybe_disconnect_peer called!\n"); abort(); } /* Generated stub for merkle_tlv */ void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle UNNEEDED) { fprintf(stderr, "merkle_tlv called!\n"); abort(); } @@ -654,9 +657,6 @@ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channeld_dev_reenable_commit */ u8 *towire_channeld_dev_reenable_commit(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_reenable_commit called!\n"); abort(); } -/* Generated stub for towire_connectd_discard_peer */ -u8 *towire_connectd_discard_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) -{ fprintf(stderr, "towire_connectd_discard_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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 1f8b3617631b..6a74d2bab0c6 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -448,6 +448,9 @@ bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, const char *why UNNEEDED) { fprintf(stderr, "kill_uncommitted_channel called!\n"); abort(); } +/* Generated stub for maybe_disconnect_peer */ +void maybe_disconnect_peer(struct lightningd *ld UNNEEDED, struct peer *peer UNNEEDED) +{ fprintf(stderr, "maybe_disconnect_peer called!\n"); abort(); } /* Generated stub for new_channel_mvt_invoice_hin */ struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx UNNEEDED, struct htlc_in *hin UNNEEDED, @@ -733,9 +736,6 @@ u8 *towire_channeld_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amo /* Generated stub for towire_channeld_sending_commitsig_reply */ u8 *towire_channeld_sending_commitsig_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_sending_commitsig_reply called!\n"); abort(); } -/* Generated stub for towire_connectd_discard_peer */ -u8 *towire_connectd_discard_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) -{ fprintf(stderr, "towire_connectd_discard_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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } From 8458994a7fcce321dfd5e03842fc1cc715c94ca2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:35 +1030 Subject: [PATCH 0521/1530] pytest: test closing one multichannel works as expected. Signed-off-by: Rusty Russell --- tests/test_connection.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 0707c53caac4..952b792c0b38 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3864,3 +3864,20 @@ def test_multichan(node_factory, executor, bitcoind): assert chan23a['short_channel_id'] == scid23a assert chan23b['amount_msat'] == Millisatoshi(1001000000) assert chan23b['short_channel_id'] == scid23b + + # We can close one, other one is still fine. + with pytest.raises(RpcError, match="Peer has multiple channels"): + l2.rpc.close(l3.info['id']) + + l2.rpc.close(scid23b) + bitcoind.generate_block(1, wait_for_mempool=1) + + # Gossip works as expected. + wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 1) + assert only_one(l1.rpc.listchannels(source=l3.info['id'])['channels'])['short_channel_id'] == scid23a + + # We can actually pay by *closed* scid (at least until it's completely forgotten) + route[1]['channel'] = scid23a + inv = l3.rpc.invoice(100000000, "invoice3", "invoice3") + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) + l1.rpc.waitsendpay(inv['payment_hash']) From 1e4149f18aa2206d8cb4b6be0cc9a1751da649e2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0522/1530] lightningd: clean up peer connection handling a little. Update the address and direction as soon as it connects not just when we're about to make it active: we want this even if we don't have an active channel, or if the connect hook rejects it. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 2e70af6d86a2..022c0cf8c539 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1018,9 +1018,6 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa channel_state_name(channel)); assert(!channel->owner); - channel->peer->addr = addr; - channel->peer->connected_incoming = payload->incoming; - subd_send_msg(ld->connectd, take(towire_connectd_peer_make_active(NULL, &peer->id, &channel->cid))); @@ -1179,12 +1176,14 @@ void peer_connected(struct lightningd *ld, const u8 *msg) peer = new_peer(ld, 0, &id, &hook_payload->addr, hook_payload->incoming); peer->is_connected = true; + /* Update peer address and direction */ + peer->addr = hook_payload->addr; + peer->connected_incoming = hook_payload->incoming; + peer_update_features(peer, their_features); tal_steal(peer, hook_payload); hook_payload->peer = peer; - peer_update_features(peer, their_features); - /* Complete any outstanding connect commands. */ connect_succeeded(ld, peer, hook_payload->incoming, &hook_payload->addr); From 293cf3c2b21940f4425f4f6e664b0c6f03cd44ff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0523/1530] connect: delay return until all subds ready. We had some flakes because we returned from `connect`, but we hadn't started subds yet. Signed-off-by: Rusty Russell --- doc/lightning-connect.7.md | 4 +++ lightningd/peer_control.c | 57 ++++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index e596f7a76c81..c2e9851f765e 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -36,6 +36,10 @@ Connecting to a node is just the first step in opening a channel with another node. Once the peer is connected a channel can be opened with lightning-fundchannel(7). +If there are active channels with the peer, **connect** returns once +all the subdaemons are in place to handle the channels, not just once +it's connected. + RETURN VALUE ------------ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 022c0cf8c539..b1968cfa23e0 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -997,23 +997,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa } #endif - switch (channel->state) { - case CLOSED: - /* Channel should not have been loaded */ - abort(); - case ONCHAIN: - case FUNDING_SPEND_SEEN: - case CLOSINGD_COMPLETE: - case AWAITING_UNILATERAL: - /* We don't send anything here; if they talk about - * this channel they'll get an error. */ - continue; - case DUALOPEND_OPEN_INIT: - case DUALOPEND_AWAITING_LOCKIN: - case CHANNELD_AWAITING_LOCKIN: - case CHANNELD_NORMAL: - case CHANNELD_SHUTTING_DOWN: - case CLOSINGD_SIGEXCHANGE: + if (channel_active(channel)) { log_debug(channel->log, "Peer has reconnected, state %s: telling connectd to make active", channel_state_name(channel)); @@ -1021,11 +1005,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa subd_send_msg(ld->connectd, take(towire_connectd_peer_make_active(NULL, &peer->id, &channel->cid))); - continue; } - - /* Oops, channel->state is corrupted? */ - abort(); } return; @@ -1149,6 +1129,20 @@ REGISTER_PLUGIN_HOOK(peer_connected, peer_connected_serialize, struct peer_connected_hook_payload *); +/* Returns true if we're still waiting for subds for active channels */ +static bool peer_subds_pending(const struct peer *peer) +{ + struct channel *channel; + + list_for_each(&peer->channels, channel, list) { + if (!channel_active(channel)) + continue; + if (!channel->owner) + return true; + } + return false; +} + /* Connectd tells us a peer has connected: it never hands us duplicates, since * it holds them until we say peer_disconnected. */ void peer_connected(struct lightningd *ld, const u8 *msg) @@ -1184,8 +1178,11 @@ void peer_connected(struct lightningd *ld, const u8 *msg) tal_steal(peer, hook_payload); hook_payload->peer = peer; - /* Complete any outstanding connect commands. */ - connect_succeeded(ld, peer, hook_payload->incoming, &hook_payload->addr); + /* Complete any outstanding connect commands: as a hack, we delay here if we + * are going to make them active (so when connect returns, the channels are ready). + * So we also wake these up if the connection dies before that! */ + if (!peer_subds_pending(peer)) + connect_succeeded(ld, peer, hook_payload->incoming, &hook_payload->addr); /* Can't be opening, since we wouldn't have sent peer_disconnected. */ assert(!peer->uncommitted_channel); @@ -1217,7 +1214,6 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) u8 *error; struct peer_fd *peer_fd = new_peer_fd(tmpctx, fd); - /* FIXME: Use msgtype to determine what to do! */ if (!fromwire_connectd_peer_active(msg, msg, &id, &msgtype, &channel_id)) fatal("Connectd gave bad CONNECTD_PEER_ACTIVE message %s", tal_hex(msg, msg)); @@ -1261,13 +1257,13 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) && channel->open_attempt->open_msg) { if (peer_start_dualopend(peer, peer_fd, channel)) subd_send_msg(channel->owner, channel->open_attempt->open_msg); - return; + goto subd_setup_done; } /* Fall through. */ case DUALOPEND_AWAITING_LOCKIN: assert(!channel->owner); peer_restart_dualopend(peer, peer_fd, channel); - return; + goto subd_setup_done; case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: case CHANNELD_SHUTTING_DOWN: @@ -1277,7 +1273,7 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) peer_fd, NULL, true, NULL); - return; + goto subd_setup_done; } abort(); } @@ -1377,6 +1373,13 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, error))); + return; + +subd_setup_done: + /* We deferred connect_succeeded to here, so subd would be ready once + * `connect` returns. */ + if (!peer_subds_pending(peer)) + connect_succeeded(ld, peer, peer->connected_incoming, &peer->addr); } struct disconnect_command { From aeeaa1443065b2057e8a94b1c3c0f1ae19f9a0ad Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0524/1530] pytest: add reconnect and restart to test_multichan. Signed-off-by: Rusty Russell --- tests/test_connection.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 952b792c0b38..60fe6485090b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3779,7 +3779,7 @@ def test_ping_timeout(node_factory): @pytest.mark.openchannel('v2') def test_multichan(node_factory, executor, bitcoind): """Test multiple channels between same nodes""" - l1, l2, l3 = node_factory.line_graph(3) + l1, l2, l3 = node_factory.line_graph(3, opts={'may_reconnect': True}) scid12 = l1.get_channel_scid(l2) scid23a = l2.get_channel_scid(l3) @@ -3830,6 +3830,12 @@ def test_multichan(node_factory, executor, bitcoind): chan23a_idx = 1 chan23b_idx = 0 + # Gratuitous reconnect + with pytest.raises(RpcError, match=r"Peer has \(at least one\) channel"): + l3.rpc.disconnect(l2.info['id']) + l3.rpc.disconnect(l2.info['id'], force=True) + l3.rpc.connect(l2.info['id'], 'localhost', l2.port) + # Check it used the larger channel! assert before[chan23a_idx]['to_us_msat'] == after[chan23a_idx]['to_us_msat'] assert before[chan23b_idx]['to_us_msat'] != after[chan23b_idx]['to_us_msat'] @@ -3881,3 +3887,10 @@ def test_multichan(node_factory, executor, bitcoind): inv = l3.rpc.invoice(100000000, "invoice3", "invoice3") l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(inv['payment_hash']) + + # Restart with multiple channels works. + l3.restart() + l3.rpc.connect(l2.info['id'], 'localhost', l2.port) + + inv = l3.rpc.invoice(100000000, "invoice4", "invoice4") + l1.rpc.pay(inv['bolt11']) From 486b1b24818e696795fd28f2d686bb22c478ddcb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0525/1530] pytest: fix flake in test_funding_fail We may not see a disconnect instantly: ``` > assert len(l2.rpc.listpeers()['peers']) == 0 E assert 1 == 0 E +1 E -0 ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 60fe6485090b..9e2b284ab5fd 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -999,8 +999,8 @@ def test_funding_fail(node_factory, bitcoind): l1.rpc.fundchannel(l2.info['id'], int(funds / 10)) # channels disconnect on failure - assert len(l1.rpc.listpeers()['peers']) == 0 - assert len(l2.rpc.listpeers()['peers']) == 0 + wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 0) + wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) # Restart l2 without ridiculous locktime. del l2.daemon.opts['watchtime-blocks'] From 07c4d39b7597177d79dba9f799414425506d893b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0526/1530] memleak: fix double-free if we timeout. ... and then dualopend returns, and we access the fread leak_detect struct. ``` lightningd: FATAL SIGNAL 6 (version 065ca1e) 0x55ecd4be8145 send_backtrace common/daemon.c:33 0x55ecd4be81f1 crashdump common/daemon.c:46 0x7f200acab51f ??? ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 0x7f200acff828 __pthread_kill_implementation ./nptl/pthread_kill.c:44 0x7f200acff828 __pthread_kill_internal ./nptl/pthread_kill.c:80 0x7f200acff828 __GI___pthread_kill ./nptl/pthread_kill.c:91 0x7f200acab475 __GI_raise ../sysdeps/posix/raise.c:26 0x7f200ac917b6 __GI_abort ./stdlib/abort.c:79 0x55ecd4c6827f call_error ccan/ccan/tal/tal.c:93 0x55ecd4c68470 check_bounds ccan/ccan/tal/tal.c:165 0x55ecd4c684c2 to_tal_hdr ccan/ccan/tal/tal.c:175 0x55ecd4c68eb8 tal_free ccan/ccan/tal/tal.c:479 0x55ecd4b8bdd0 finish_report lightningd/memdump.c:138 0x55ecd4b8c115 leak_detect_req_done lightningd/memdump.c:201 0x55ecd4c68664 notify ccan/ccan/tal/tal.c:237 0x55ecd4c68b9e del_tree ccan/ccan/tal/tal.c:402 0x55ecd4c68bf3 del_tree ccan/ccan/tal/tal.c:412 0x55ecd4c68bf3 del_tree ccan/ccan/tal/tal.c:412 0x55ecd4c68f43 tal_free ccan/ccan/tal/tal.c:486 0x55ecd4c5751f io_close ccan/ccan/io/io.c:450 0x55ecd4bbce68 subd_shutdown_remaining lightningd/subd.c:911 0x55ecd4b8724a shutdown_subdaemons lightningd/lightningd.c:541 0x55ecd4b883cc main lightningd/lightningd.c:1207 0x7f200ac92fcf __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 0x7f200ac9307c __libc_start_main_impl ../csu/libc-start.c:409 0x55ecd4b5cc54 ??? ``` Signed-off-by: Rusty Russell --- lightningd/memdump.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 226341a90896..952355bf8d0e 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -189,6 +189,9 @@ static void finish_report(const struct leak_detect *leaks) static void leak_detect_timeout(struct leak_detect *leak_detect) { + /* We actually *do* leak the leak_detect, but cmd is about + * to exit. */ + notleak(tal_steal(NULL, leak_detect)); finish_report(leak_detect); leak_detect->cmd = NULL; } From d2cf2a3f51330d0accee39b8f4998679c660ced4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0527/1530] gossipd: don't use connectd daemon_conn after it shuts down. Simply exit, like we do when master daemon_conn exits. ``` Valgrind error file: valgrind-errors.2211908 ==2211908== Invalid read of size 8 ==2211908== at 0x12AC13: daemon_conn_send (daemon_conn.c:137) ==2211908== by 0x113CD9: queue_peer_msg (gossipd.c:118) ==2211908== by 0x11B806: query_channel_range (queries.c:1169) ==2211908== by 0x1250DD: peer_gossip_probe_scids (seeker.c:706) ==2211908== by 0x1253B1: check_firstpeer (seeker.c:788) ==2211908== by 0x1256CA: seeker_check (seeker.c:884) ==2211908== by 0x1366AC: timer_expired (timeout.c:62) ==2211908== by 0x1163D1: main (gossipd.c:1146) ==2211908== Address 0x4cafdf0 is 48 bytes inside a block of size 88 free'd ==2211908== at 0x48460C4: free (vg_replace_malloc.c:872) ==2211908== by 0x1805EA: del_tree (tal.c:421) ==2211908== by 0x1808BE: tal_free (tal.c:486) ==2211908== by 0x12AB25: destroy_dc_from_conn (daemon_conn.c:112) ==2211908== by 0x17FFDF: notify (tal.c:237) ==2211908== by 0x180519: del_tree (tal.c:402) ==2211908== by 0x1808BE: tal_free (tal.c:486) ==2211908== by 0x16EE9A: io_close (io.c:450) ==2211908== by 0x16ECA9: do_plan (io.c:401) ==2211908== by 0x16ED16: io_ready (io.c:417) ==2211908== by 0x1710B2: io_loop (poll.c:453) ==2211908== by 0x1163C5: main (gossipd.c:1144) ==2211908== Block was alloc'd at ==2211908== at 0x484384F: malloc (vg_replace_malloc.c:381) ==2211908== by 0x180064: allocate (tal.c:250) ==2211908== by 0x180634: tal_alloc_ (tal.c:428) ==2211908== by 0x12AB65: daemon_conn_new_ (daemon_conn.c:122) ==2211908== by 0x1155F4: gossip_init (gossipd.c:763) ==2211908== by 0x116014: recv_req (gossipd.c:999) ==2211908== by 0x12A828: handle_read (daemon_conn.c:31) ==2211908== by 0x16E09F: next_plan (io.c:59) ==2211908== by 0x16ECD4: do_plan (io.c:407) ==2211908== by 0x16ED16: io_ready (io.c:417) ==2211908== by 0x1710B2: io_loop (poll.c:453) ==2211908== by 0x1163C5: main (gossipd.c:1144) ==2211908== ``` --- gossipd/gossipd.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 674f646cbbce..45cbe4918cc0 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -707,6 +707,15 @@ struct peer *random_peer(struct daemon *daemon, return best; } +/* This is called when lightningd or connectd closes its connection to + * us. We simply exit. */ +static void master_or_connectd_gone(struct daemon_conn *dc UNUSED) +{ + daemon_shutdown(); + /* Can't tell master, it's gone. */ + exit(2); +} + /*~ Parse init message from lightningd: starts the daemon properly. */ static void gossip_init(struct daemon *daemon, const u8 *msg) { @@ -763,6 +772,7 @@ static void gossip_init(struct daemon *daemon, const u8 *msg) daemon->connectd = daemon_conn_new(daemon, CONNECTD_FD, connectd_req, maybe_send_query_responses, daemon); + tal_add_destructor(daemon->connectd, master_or_connectd_gone); /* OK, we are ready. */ daemon_conn_send(daemon->master, @@ -1090,15 +1100,6 @@ static struct io_plan *recv_req(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->master); } -/* This is called when lightningd closes its connection to us. We simply - * exit. */ -static void master_gone(struct daemon_conn *master UNUSED) -{ - daemon_shutdown(); - /* Can't tell master, it's gone. */ - exit(2); -} - int main(int argc, char *argv[]) { setup_locale(); @@ -1131,7 +1132,7 @@ int main(int argc, char *argv[]) /* Our daemons always use STDIN for commands from lightningd. */ daemon->master = daemon_conn_new(daemon, STDIN_FILENO, recv_req, NULL, daemon); - tal_add_destructor(daemon->master, master_gone); + tal_add_destructor(daemon->master, master_or_connectd_gone); status_setup_async(daemon->master); From 2a80400a0f7610f3f8aad471f526b697d25f0cd8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0528/1530] pytest: fix test_connection.py::test_funding_close_upfront fake Breaks when there are no peers: ``` > _fundchannel(l1, l2, amt_normal, None) tests/test_connection.py:1564: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ tests/test_connection.py:1535: in _fundchannel wait_for(lambda: not has_normal_channels(l2, l1)) contrib/pyln-testing/pyln/testing/utils.py:88: in wait_for while not success(): tests/test_connection.py:1535: in wait_for(lambda: not has_normal_channels(l2, l1)) tests/test_connection.py:1527: in has_normal_channels for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']]) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ arr = [] def only_one(arr): """Many JSON RPC calls return an array; often we only expect a single entry """ > assert len(arr) == 1 E AssertionError ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 9e2b284ab5fd..2c956380a37f 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1522,6 +1522,8 @@ def test_funding_close_upfront(node_factory, bitcoind): remote_valid_addr = 'bcrt1q7gtnxmlaly9vklvmfj06amfdef3rtnrdazdsvw' def has_normal_channels(l1, l2): + if l1.rpc.listpeers(l2.info['id'])['peers'] == []: + return False return any([c['state'] == 'CHANNELD_AWAITING_LOCKIN' or c['state'] == 'CHANNELD_NORMAL' for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']]) From bcf3cef96c2837aaa9bb3b99b3d23ace4c3183dc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0529/1530] pytest: fix flake in test_connection.py::test_opener_feerate_reconnect Make sure it sees disconnect before reconnect, otherwise the next command fails since we're now disconnected. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 1 + tests/test_connection.py | 1 + 2 files changed, 2 insertions(+) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index b1968cfa23e0..6bfd09f689dd 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1408,6 +1408,7 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) /* If we still have peer, it's disconnected now */ p = peer_by_id(ld, &id); if (p) { + log_peer_debug(ld->log, &id, "peer_disconnect_done"); p->is_connected = false; /* If we only cared about peer because of connectd, free it. */ if (list_empty(&p->channels) && !p->uncommitted_channel) { diff --git a/tests/test_connection.py b/tests/test_connection.py index 2c956380a37f..ede5b8716bef 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2748,6 +2748,7 @@ def test_opener_feerate_reconnect(node_factory, bitcoind): # Wait until they reconnect. l1.daemon.wait_for_log('Peer transient failure in CHANNELD_NORMAL') + l1.daemon.wait_for_log('peer_disconnect_done') wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected']) # Should work normally. From 9c978f2c632e396d2e14e16fb370c9e061e56da0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0530/1530] lightningd: free peer->uc when openingd fails. This avoids reuse which can cause confusion; the long-term fix is to rewrite this to use real channels like dualfunding does. Signed-off-by: Rusty Russell --- lightningd/opening_control.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index bc9aac4bd9fb..8f898ee16112 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -574,6 +574,8 @@ static void openingd_failed(struct subd *openingd, const u8 *msg, /* Noop if we're not funder. */ opening_funder_failed_cancel_commands(uc, desc); + /* Detaches from ->peer */ + tal_free(uc); } struct openchannel_hook_payload { From be75518559d41e262d10f4eee830bcfca1907f0b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 09:31:36 +1030 Subject: [PATCH 0531/1530] pytest: fix bad gossip flake in test_multifunding_v1_v2_mixed This was missed in e8d2176e6bb89c9825da24e68bfb43d0a6a408bd. ``` > raise ValueError(str(errors)) E ValueError: E Node errors: E - lightningd-2: had bad gossip messages E - lightningd-3: had bad gossip messages E Global errors: contrib/pyln-testing/pyln/testing/fixtures.py:201: ValueError ... 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-gossipd: Ignoring future channel_announcment for 105x1x2 (current block 104) 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-gossipd: Bad gossip order: WIRE_CHANNEL_UPDATE before announcement 105x1x2/0 ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index ede5b8716bef..4e5eb8be1753 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1681,7 +1681,7 @@ def test_multifunding_v1_v2_mixed(node_factory, bitcoind): "amount": 50000}] l1.rpc.multifundchannel(destinations) - bitcoind.generate_block(6, wait_for_mempool=1) + mine_funding_to_announce(bitcoind, [l1, l2, l3, l4], wait_for_mempool=1) for node in [l1, l2, l3, l4]: node.daemon.wait_for_log(r'to CHANNELD_NORMAL') From 58ce4cf71ca9b5054406ea3e732d9c268ea4aab2 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 10 Mar 2022 21:30:39 +0100 Subject: [PATCH 0532/1530] cli: check if the pass and the confirmation pass match Changelog-Fixed: check if the pass and the confirmation pass match from the command line Signed-off-by: Vincenzo Palazzo --- lightningd/options.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lightningd/options.c b/lightningd/options.c index 4828180d6e49..66c7ca951116 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -518,6 +518,12 @@ static char *opt_set_hsm_password(struct lightningd *ld) passwd_confirmation = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); if (!passwd_confirmation) return err_msg; + + if (!streq(passwd, passwd_confirmation)) { + opt_exitcode = HSM_BAD_PASSWORD; + return "Passwords confirmation mismatch."; + } + printf("\n"); ld->config.keypass = tal(NULL, struct secret); From 53806d1abdc0859795176a94ac59a9fbf61520c0 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 10 Mar 2022 22:26:20 +0100 Subject: [PATCH 0533/1530] cli: make the command line more user friendly. Also has to fix up tests. Changelog-Fixed: cli doesn't required anymore to confirm the password if the `hsm_secret` is already encrypted. Signed-off-by: Vincenzo Palazzo --- common/errcode.h | 1 + common/hsm_encryption.c | 13 +++++++++++++ common/hsm_encryption.h | 3 +++ lightningd/hsm_control.c | 6 ++---- lightningd/options.c | 29 ++++++++++++++++++----------- tests/test_wallet.py | 7 ------- tools/hsmtool.c | 29 ++++++++++++++++++----------- 7 files changed, 55 insertions(+), 33 deletions(-) diff --git a/common/errcode.h b/common/errcode.h index a4ac49e17261..d9784e962fd6 100644 --- a/common/errcode.h +++ b/common/errcode.h @@ -14,5 +14,6 @@ typedef s32 errcode_t; #define HSM_ERROR_IS_ENCRYPT 21 #define HSM_BAD_PASSWORD 22 #define HSM_PASSWORD_INPUT_ERR 23 +#define ERROR_HSM_FILE 24 #endif /* LIGHTNING_COMMON_ERRCODE_H */ diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index 8a433998b113..b2f7c813f63d 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -1,6 +1,8 @@ #include "config.h" +#include #include #include +#include #include #include @@ -79,6 +81,17 @@ bool decrypt_hsm_secret(const struct secret *encryption_key, return true; } +/* Returns -1 on error (and sets errno), 0 if not encrypted, 1 if it is */ +int is_hsm_secret_encrypted(const char *path) +{ + struct stat st; + + if (stat(path, &st) != 0) + return -1; + + return st.st_size == ENCRYPTED_HSM_SECRET_LEN; +} + void discard_key(struct secret *key TAKES) { /* sodium_munlock() also zeroes the memory. */ diff --git a/common/hsm_encryption.h b/common/hsm_encryption.h index 1534cb4ff6b4..867202661330 100644 --- a/common/hsm_encryption.h +++ b/common/hsm_encryption.h @@ -4,6 +4,7 @@ #include #include #include +#include /* Length of the encrypted hsm secret header. */ #define HS_HEADER_LEN crypto_secretstream_xchacha20poly1305_HEADERBYTES @@ -63,4 +64,6 @@ void discard_key(struct secret *key TAKES); */ char *read_stdin_pass_with_exit_code(char **reason, int *exit_code); +/** Returns -1 on error (and sets errno), 0 if not encrypted, 1 if it is */ +int is_hsm_secret_encrypted(const char *path); #endif /* LIGHTNING_COMMON_HSM_ENCRYPTION_H */ diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 4efd57b195e8..9a49c03df79d 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -96,11 +96,9 @@ struct ext_key *hsm_init(struct lightningd *ld) * not passed, don't let hsmd use the first 32 bytes of the cypher as the * actual secret. */ if (!ld->config.keypass) { - struct stat st; - if (stat("hsm_secret", &st) == 0 && - st.st_size == ENCRYPTED_HSM_SECRET_LEN) + if (is_hsm_secret_encrypted("hsm_secret") == 1) errx(HSM_ERROR_IS_ENCRYPT, "hsm_secret is encrypted, you need to pass the " - "--encrypted-hsm startup option."); + "--encrypted-hsm startup option."); } ld->hsm_fd = fds[0]; diff --git a/lightningd/options.c b/lightningd/options.c index 66c7ca951116..e756854929ec 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -502,6 +502,12 @@ static char *opt_important_plugin(const char *arg, struct lightningd *ld) static char *opt_set_hsm_password(struct lightningd *ld) { char *passwd, *passwd_confirmation, *err_msg; + int is_encrypted; + + is_encrypted = is_hsm_secret_encrypted("hsm_secret"); + if (is_encrypted == -1) + return tal_fmt(NULL, "Could not access 'hsm_secret': %s", + strerror(errno)); printf("The hsm_secret is encrypted with a password. In order to " "decrypt it and start the node you must provide the password.\n"); @@ -513,17 +519,19 @@ static char *opt_set_hsm_password(struct lightningd *ld) passwd = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); if (!passwd) return err_msg; - printf("Confirm hsm_secret password:\n"); - fflush(stdout); - passwd_confirmation = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); - if (!passwd_confirmation) - return err_msg; - - if (!streq(passwd, passwd_confirmation)) { - opt_exitcode = HSM_BAD_PASSWORD; - return "Passwords confirmation mismatch."; + if (!is_encrypted) { + printf("Confirm hsm_secret password:\n"); + fflush(stdout); + passwd_confirmation = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); + if (!passwd_confirmation) + return err_msg; + + if (!streq(passwd, passwd_confirmation)) { + opt_exitcode = HSM_BAD_PASSWORD; + return "Passwords confirmation mismatch."; + } + free(passwd_confirmation); } - printf("\n"); ld->config.keypass = tal(NULL, struct secret); @@ -534,7 +542,6 @@ static char *opt_set_hsm_password(struct lightningd *ld) ld->encrypted_hsm = true; free(passwd); - free(passwd_confirmation); return NULL; } diff --git a/tests/test_wallet.py b/tests/test_wallet.py index ef5e8184ef45..05cf54319bc3 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1039,8 +1039,6 @@ def test_hsm_secret_encryption(node_factory): wait_for_initialized=False) l1.daemon.wait_for_log(r'Enter hsm_secret password') write_all(master_fd, password[2:].encode("utf-8")) - l1.daemon.wait_for_log(r'Confirm hsm_secret password') - write_all(master_fd, password[2:].encode("utf-8")) assert(l1.daemon.proc.wait(WAIT_TIMEOUT) == HSM_BAD_PASSWORD) assert(l1.daemon.is_in_log("Wrong password for encrypted hsm_secret.")) @@ -1048,8 +1046,6 @@ def test_hsm_secret_encryption(node_factory): l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) l1.daemon.wait_for_log(r'The hsm_secret is encrypted') write_all(master_fd, password.encode("utf-8")) - l1.daemon.wait_for_log(r'Confirm hsm_secret password') - write_all(master_fd, password.encode("utf-8")) l1.daemon.wait_for_log("Server started with public key") assert id == l1.rpc.getinfo()["id"] l1.stop() @@ -1057,7 +1053,6 @@ def test_hsm_secret_encryption(node_factory): # We can restore the same wallet with the same password provided through stdin l1.daemon.start(stdin=subprocess.PIPE, wait_for_initialized=False) l1.daemon.proc.stdin.write(password.encode("utf-8")) - l1.daemon.proc.stdin.write(password.encode("utf-8")) l1.daemon.proc.stdin.flush() l1.daemon.wait_for_log("Server started with public key") assert id == l1.rpc.getinfo()["id"] @@ -1138,8 +1133,6 @@ def test_hsmtool_secret_decryption(node_factory): l1.daemon.wait_for_log(r'The hsm_secret is encrypted') write_all(master_fd, password.encode("utf-8")) - l1.daemon.wait_for_log(r'Confirm hsm_secret password') - write_all(master_fd, password.encode("utf-8")) l1.daemon.wait_for_log("Server started with public key") print(node_id, l1.rpc.getinfo()["id"]) assert node_id == l1.rpc.getinfo()["id"] diff --git a/tools/hsmtool.c b/tools/hsmtool.c index f6a426443f9a..41eb514aba2e 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -10,18 +10,17 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include -#define ERROR_HSM_FILE errno #define ERROR_USAGE 2 #define ERROR_LIBSODIUM 3 #define ERROR_LIBWALLY 4 @@ -147,16 +146,24 @@ static void get_channel_seed(struct secret *channel_seed, struct node_id *peer_i /* We detect an encrypted hsm_secret as a hsm_secret which is 73-bytes long. */ static bool hsm_secret_is_encrypted(const char *hsm_secret_path) { - struct stat st; - - if (stat(hsm_secret_path, &st) != 0) - errx(ERROR_HSM_FILE, "Could not stat hsm_secret"); - - if (st.st_size != 32 && st.st_size != ENCRYPTED_HSM_SECRET_LEN) - errx(ERROR_HSM_FILE, "Invalid hsm_secret (neither plaintext " - "nor encrypted)."); + switch (is_hsm_secret_encrypted(hsm_secret_path)) { + case -1: + err(ERROR_HSM_FILE, "Cannot open '%s'", hsm_secret_path); + case 1: + return true; + case 0: { + /* Extra sanity check on HSM file! */ + struct stat st; + stat(hsm_secret_path, &st); + if (st.st_size != 32) + errx(ERROR_HSM_FILE, + "Invalid hsm_secret '%s' (neither plaintext " + "nor encrypted).", hsm_secret_path); + return false; + } + } - return st.st_size == ENCRYPTED_HSM_SECRET_LEN; + abort(); } static int decrypt_hsm(const char *hsm_secret_path) From 5aa108f17679a70db60ca91c3e7a8b41d9cd72cf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 21:13:44 +1030 Subject: [PATCH 0534/1530] pytest: fix flake in test_pay.py::test_setchannel_state The second disconnect could fail, if slow enough: ``` > l1.rpc.disconnect(l2.info['id'], force=True) tests/test_pay.py:2067: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-client/pyln/client/lightning.py:690: in disconnect return self.call("disconnect", payload) contrib/pyln-testing/pyln/testing/utils.py:639: in call res = LightningRpc.call(self, method, payload) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = method = 'disconnect' payload = {'force': True, 'id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d'} ... > raise RpcError(method, payload, resp['error']) E pyln.client.lightning.RpcError: RPC call failed: method: disconnect, payload: {'id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', 'force': True}, error: {'code': -1, 'message': 'Peer not connected'} ``` Signed-off-by: Rusty Russell --- tests/test_pay.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_pay.py b/tests/test_pay.py index 8942c6ccf961..12a5c8b6226b 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2064,7 +2064,6 @@ def test_setchannel_state(node_factory, bitcoind): # Disconnect and unilaterally close from l2 to l1 l2.rpc.disconnect(l1.info['id'], force=True) - l1.rpc.disconnect(l2.info['id'], force=True) result = l2.rpc.close(scid, 1) assert result['type'] == 'unilateral' From f7aba314482e6aaeedd954e073e92bcd26b7455c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:30:06 +1030 Subject: [PATCH 0535/1530] lightningd: allow 'style' 'legacy' for clboss compat (with deprecated_apis). We keep the parameter around for future use (e.g. PTLC support?), but clboss master still sets 'style' to "legacy". Accept, but ignore it. Reported-by: grubman on #c-lightning (IRC) Changelog-Deprecated: JSON-RPC: `sendpay` `route` argument `style` "legacy" (don't use it at all, we ignore it now and always use "tlv" anyway). Signed-off-by: Rusty Russell --- doc/PLUGINS.md | 2 -- doc/lightning-createonion.7.md | 3 --- doc/lightning-getroute.7.md | 4 ++-- doc/schemas/getroute.schema.json | 1 - lightningd/pay.c | 5 +++++ 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 487a653498c4..bd38a7eb49c9 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1358,7 +1358,6 @@ The payload of the hook call has the following format: { "onion": { "payload": "", - "type": "legacy", "short_channel_id": "1x2x3", "forward_amount": "42msat", "outgoing_cltv_value": 500014, @@ -1379,7 +1378,6 @@ For detailed information about each field please refer to [BOLT 04 of the specif - `onion`: - `payload` contains the unparsed payload that was sent to us from the sender of the payment. - - `type` is `legacy` for realm 0 payments, `tlv` for realm > 1. - `short_channel_id` determines the channel that the sender is hinting should be used next. Not present if we're the final destination. - `forward_amount` is the amount we should be forwarding to the next hop, diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 9eed31890c2a..f401fe5cc596 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -45,7 +45,6 @@ which the above *hops* parameter was generated: "msatoshi": 1002, "amount_msat": "1002msat", "delay": 21, - "style": "legacy" }, { "id": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", "channel": "103x1x1", @@ -53,7 +52,6 @@ which the above *hops* parameter was generated: "msatoshi": 1001, "amount_msat": "1001msat", "delay": 15, - "style": "legacy" }, { "id": "0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199", "channel": "103x3x1", @@ -61,7 +59,6 @@ which the above *hops* parameter was generated: "msatoshi": 1000, "amount_msat": "1000msat", "delay": 9, - "style": "legacy" } ] ``` diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index e490542a3e4c..7a6ec76c831f 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -284,7 +284,7 @@ On success, an object containing **route** is returned. It is an array of objec - **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") +- **style** (string): The features understood by the destination node (always "tlv") [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -309,4 +309,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:0c1f92ff24ae0277fed3cf3fd41f2f45e4a57558a4b61fc51a1a698b4f3d8f01) +[comment]: # ( SHA256STAMP:3494cb4003abfe32e8942ec5d92d0c464815d5e65edf29087cd2193eb414d694) diff --git a/doc/schemas/getroute.schema.json b/doc/schemas/getroute.schema.json index d6878da411ac..586e4d556975 100644 --- a/doc/schemas/getroute.schema.json +++ b/doc/schemas/getroute.schema.json @@ -48,7 +48,6 @@ "type": "string", "description": "The features understood by the destination node", "enum": [ - "legacy", "tlv" ] } diff --git a/lightningd/pay.c b/lightningd/pay.c index 00acdece7815..9c2c05157ca2 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -1331,6 +1332,10 @@ static struct command_result *param_route_hop_style(struct command *cmd, return NULL; } + /* We still let you *specify* this, but we ignore it! */ + if (deprecated_apis && json_tok_streq(buffer, tok, "legacy")) + return NULL; + return command_fail_badparam(cmd, name, buffer, tok, "should be 'tlv' ('legacy' not supported)"); } From f95c9522fd3747908fe9f4209bcada24deef3b5d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 13:06:52 +1030 Subject: [PATCH 0536/1530] plugins/bcli: fix false memleak detection. Well, it is leaking a bool for the command duration, but that's probably OK. Signed-off-by: Rusty Russell --- plugins/bcli.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/bcli.c b/plugins/bcli.c index d65e16f192f6..5f0922c77812 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -771,6 +771,9 @@ static struct command_result *sendrawtransaction(struct command *cmd, } else highfeesarg = NULL; + /* Keep memleak happy! */ + tal_free(allowhighfees); + start_bitcoin_cli(NULL, cmd, process_sendrawtransaction, true, BITCOIND_HIGH_PRIO, NULL, "sendrawtransaction", From 560b090bd8ea316ad1805e2202335f0f4fb01de9 Mon Sep 17 00:00:00 2001 From: Sjors Provoost Date: Sun, 20 Mar 2022 16:07:48 +0100 Subject: [PATCH 0537/1530] Add more warnings to fundchannel_start [ Slight modifications to fix schema -- RR ] --- doc/lightning-fundchannel_start.7.md | 7 +++++-- doc/schemas/fundchannel_start.schema.json | 9 +++++++-- lightningd/opening_control.c | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index 1a0da9c74ddc..7b1ba60d07c0 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -43,10 +43,13 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **funding_address** (string): The address to send funding to for the channel +- **funding_address** (string): The address to send funding to for the channel. DO NOT SEND COINS TO THIS ADDRESS YET. - **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` +The following warnings may also be returned: +- **warning_usage**: A warning not to prematurely broadcast the funding transaction (always present!) + [comment]: # (GENERATE-FROM-SCHEMA-END) On error the returned object will contain `code` and `message` properties, @@ -79,4 +82,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:eab533b02f2bffecef27724078461ed25f3a9b729c2432b80bbdc35aea670ca2) +[comment]: # ( SHA256STAMP:acbd9ffb07219bc10e06e8f8bc6772d12ec62650bd30b6ee1084a24f53da2ac2) diff --git a/doc/schemas/fundchannel_start.schema.json b/doc/schemas/fundchannel_start.schema.json index b7cc51b859ff..a2c9795dc3e1 100644 --- a/doc/schemas/fundchannel_start.schema.json +++ b/doc/schemas/fundchannel_start.schema.json @@ -4,12 +4,13 @@ "additionalProperties": false, "required": [ "funding_address", - "scriptpubkey" + "scriptpubkey", + "warning_usage" ], "properties": { "funding_address": { "type": "string", - "description": "The address to send funding to for the channel" + "description": "The address to send funding to for the channel. DO NOT SEND COINS TO THIS ADDRESS YET." }, "scriptpubkey": { "type": "hex", @@ -18,6 +19,10 @@ "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`" + }, + "warning_usage": { + "type": "string", + "description": "A warning not to prematurely broadcast the funding transaction (always present!)" } } } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 8f898ee16112..694256b732bf 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -284,6 +284,8 @@ static void funding_started_success(struct funding_channel *fc) fc->funding_scriptpubkey); if (fc->our_upfront_shutdown_script) json_add_hex_talarr(response, "close_to", fc->our_upfront_shutdown_script); + json_add_string(response, "warning_usage", + "The funding transaction MUST NOT be broadcast until after channel establishment has been successfully completed by running `fundchannel_complete`"); } /* Clear this so cancel doesn't think it's still in progress */ From 613a21693d884e60cd7722cb97972f2989fc7456 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 24 Mar 2022 14:47:52 +1030 Subject: [PATCH 0538/1530] test: avoid testing for secret_eq under CI. It's failing under CI, but loadavg is under 1. Simply remove it. Signed-off-by: Rusty Russell --- bitcoin/test/run-secret_eq_consttime.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/bitcoin/test/run-secret_eq_consttime.c b/bitcoin/test/run-secret_eq_consttime.c index a06cc4c034e1..4f21ee191f11 100644 --- a/bitcoin/test/run-secret_eq_consttime.c +++ b/bitcoin/test/run-secret_eq_consttime.c @@ -112,7 +112,6 @@ int main(int argc, char *argv[]) { const char *v; int const_success, nonconst_success = ITERATIONS, i; - double load; common_setup(argv[0]); @@ -121,6 +120,11 @@ int main(int argc, char *argv[]) if (v && atoi(v) == 1) goto exit; + /* this sometimes trips under CI */ + v = getenv("SLOW_MACHINE"); + if (v && atoi(v) == 1) + goto exit; + s1 = calloc(RUNS, sizeof(*s1)); s2 = calloc(RUNS, sizeof(*s2)); @@ -144,17 +148,12 @@ int main(int argc, char *argv[]) } /* Now, check loadavg: if we weren't alone, that could explain results */ - getloadavg(&load, 1); - if (load > 1.0) { - warnx("Load %.2f: too high, ignoring", load); - } else { - if (const_success < ITERATIONS / 2) - errx(1, "Only const time %u/%u?", const_success, i); - - if (nonconst_success < ITERATIONS / 2) - errx(1, "memcmp seemed const time %u/%u?", - nonconst_success, i); - } + if (const_success < ITERATIONS / 2) + errx(1, "Only const time %u/%u?", const_success, i); + + if (nonconst_success < ITERATIONS / 2) + errx(1, "memcmp seemed const time %u/%u?", + nonconst_success, i); free(s1); free(s2); From 6f7d51ee684e2e8f0b50b1a940854ce000e867b1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:18:13 +1030 Subject: [PATCH 0539/1530] common/onion: always fill in failtlvtype and failtlvpos parameters. When we did fill them in, we filled them in wrong: the offset should be the offset in the message, not the field number! Signed-off-by: Rusty Russell --- common/onion.c | 64 ++++++++++++++++++++++++++++++---------- common/test/run-sphinx.c | 3 ++ wire/tlvstream.c | 28 ++++++++++++++++++ wire/tlvstream.h | 4 +++ 4 files changed, 84 insertions(+), 15 deletions(-) diff --git a/common/onion.c b/common/onion.c index 37afa9ab69c8..584f67af1f17 100644 --- a/common/onion.c +++ b/common/onion.c @@ -214,17 +214,22 @@ struct onion_payload *onion_decode(const tal_t *ctx, const u8 *cursor = rs->raw_payload; size_t max = tal_bytelen(cursor), len; struct tlv_tlv_payload *tlv; + size_t badfield; if (!pull_payload_length(&cursor, &max, true, &len)) - return tal_free(p); + goto general_fail; tlv = tlv_tlv_payload_new(p); - if (!fromwire_tlv_payload(&cursor, &max, tlv)) - goto fail; + if (!fromwire_tlv_payload(&cursor, &max, tlv)) { + /* FIXME: Fill in correct thing here! */ + goto general_fail; + } - if (!tlv_fields_valid(tlv->fields, accepted_extra_tlvs, failtlvpos)) { - *failtlvtype = tlv->fields[*failtlvpos].numtype; - goto fail; + /* FIXME: This API makes it really hard to get the actual + * offset of field. */ + if (!tlv_fields_valid(tlv->fields, accepted_extra_tlvs, &badfield)) { + *failtlvtype = tlv->fields[badfield].numtype; + goto field_bad; } /* BOLT #4: @@ -233,8 +238,14 @@ struct onion_payload *onion_decode(const tal_t *ctx, * - MUST return an error if `amt_to_forward` or * `outgoing_cltv_value` are not present. */ - if (!tlv->amt_to_forward || !tlv->outgoing_cltv_value) - goto fail; + if (!tlv->amt_to_forward) { + *failtlvtype = TLV_TLV_PAYLOAD_AMT_TO_FORWARD; + goto field_bad; + } + if (!tlv->outgoing_cltv_value) { + *failtlvtype = TLV_TLV_PAYLOAD_OUTGOING_CLTV_VALUE; + goto field_bad; + } p->amt_to_forward = amount_msat(*tlv->amt_to_forward); p->outgoing_cltv = *tlv->outgoing_cltv_value; @@ -247,8 +258,10 @@ struct onion_payload *onion_decode(const tal_t *ctx, * - MUST include `short_channel_id` */ if (rs->nextcase == ONION_FORWARD) { - if (!tlv->short_channel_id) - goto fail; + if (!tlv->short_channel_id) { + *failtlvtype = TLV_TLV_PAYLOAD_SHORT_CHANNEL_ID; + goto field_bad; + } p->forward_channel = tal_dup(p, struct short_channel_id, tlv->short_channel_id); p->total_msat = NULL; @@ -283,18 +296,30 @@ struct onion_payload *onion_decode(const tal_t *ctx, if (rs->nextcase == ONION_FORWARD) { struct tlv_tlv_payload *ntlv; - if (!tlv->encrypted_recipient_data) - goto fail; + if (!tlv->encrypted_recipient_data) { + *failtlvtype = TLV_TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA; + goto field_bad; + } ntlv = decrypt_tlv(tmpctx, &p->blinding_ss, tlv->encrypted_recipient_data); - if (!ntlv) - goto fail; + if (!ntlv) { + *failtlvtype = TLV_TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA; + goto field_bad; + } /* Must override short_channel_id */ - if (!ntlv->short_channel_id) + if (!ntlv->short_channel_id) { + *failtlvtype = TLV_TLV_PAYLOAD_ENCRYPTED_RECIPIENT_DATA; + /* Place error at *end* of enctlv, + * indicating missing field. */ + *failtlvpos = tlv_field_offset(rs->raw_payload, + tal_bytelen(rs->raw_payload), + *failtlvtype) + + tal_bytelen(tlv->encrypted_recipient_data); goto fail; + } *p->forward_channel = *ntlv->short_channel_id; @@ -313,6 +338,15 @@ struct onion_payload *onion_decode(const tal_t *ctx, p->tlv = tal_steal(p, tlv); return p; +field_bad: + *failtlvpos = tlv_field_offset(rs->raw_payload, tal_bytelen(rs->raw_payload), + *failtlvtype); + goto fail; + +general_fail: + *failtlvtype = 0; + *failtlvpos = tal_bytelen(rs->raw_payload); + goto fail; fail: tal_free(tlv); tal_free(p); diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index 7cb201a8e7b5..36248e44aefb 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -62,6 +62,9 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } +/* Generated stub for tlv_field_offset */ +size_t tlv_field_offset(const u8 *tlvstream UNNEEDED, size_t tlvlen UNNEEDED, u64 fieldtype UNNEEDED) +{ fprintf(stderr, "tlv_field_offset called!\n"); abort(); } /* Generated stub for tlv_fields_valid */ bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, size_t *err_index UNNEEDED) diff --git a/wire/tlvstream.c b/wire/tlvstream.c index 9bf10a77431a..aadd04460706 100644 --- a/wire/tlvstream.c +++ b/wire/tlvstream.c @@ -82,6 +82,34 @@ void tlvstream_set_tu32(struct tlv_field **stream, u64 type, u32 value) tlvstream_set_raw(stream, type, take(ser), tal_bytelen(ser)); } +/* Get the offset of this field: returns size of msg if not found (or + * tlv malformed) */ +size_t tlv_field_offset(const u8 *tlvstream, size_t tlvlen, u64 fieldtype) +{ + size_t max = tlvlen; + while (max > 0) { + u64 type, length; + size_t field_off = tlvlen - max; + + type = fromwire_bigsize(&tlvstream, &max); + length = fromwire_bigsize(&tlvstream, &max); + + if (!tlvstream) + break; + + /* Found it! */ + if (type == fieldtype) + return field_off; + + if (length > max) + break; + + max -= length; + tlvstream += length; + } + return tlvlen; +} + bool fromwire_tlv(const u8 **cursor, size_t *max, const struct tlv_record_type *types, size_t num_types, void *record, struct tlv_field **fields) diff --git a/wire/tlvstream.h b/wire/tlvstream.h index e289513e60be..61addfea8a9f 100644 --- a/wire/tlvstream.h +++ b/wire/tlvstream.h @@ -48,6 +48,10 @@ void towire_tlv(u8 **pptr, bool tlv_fields_valid(const struct tlv_field *fields, u64 *allow_extra, size_t *err_index); +/* Get the offset of this field: returns size of msg if not found (or + * tlv malformed) */ +size_t tlv_field_offset(const u8 *tlvstream, size_t tlvlen, u64 fieldtype); + /* Generic primitive setters for tlvstreams. */ void tlvstream_set_raw(struct tlv_field **stream, u64 type, void *value TAKES, size_t valuelen); void tlvstream_set_short_channel_id(struct tlv_field **stream, u64 type, From e36d4d1143d2008ff64bdcff873adb2b0e4a701b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:19:13 +1030 Subject: [PATCH 0540/1530] devtools/decodemsg: fix printing of wireaddr. printwire_ routines are supposed to print! And they're only needed inside devtools/. Signed-off-by: Rusty Russell --- common/wireaddr.c | 5 ----- common/wireaddr.h | 1 - devtools/print_wire.c | 5 +++++ devtools/print_wire.h | 2 ++ 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 2873fe764c12..7d5354713397 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -294,11 +294,6 @@ char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a) } REGISTER_TYPE_TO_STRING(wireaddr, fmt_wireaddr); -char *printwire_wireaddr(const tal_t *ctx, const struct wireaddr *a) -{ - return fmt_wireaddr(ctx, a); -} - /* Valid forms: * * [anything]: diff --git a/common/wireaddr.h b/common/wireaddr.h index 74e02fc065a0..acdb2e4feda0 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -93,7 +93,6 @@ bool parse_wireaddr(const char *arg, struct wireaddr *addr, u16 port, char *fmt_wireaddr(const tal_t *ctx, const struct wireaddr *a); char *fmt_wireaddr_without_port(const tal_t *ctx, const struct wireaddr *a); -char *printwire_wireaddr(const tal_t *ctx, const struct wireaddr *a); /* If no_dns is non-NULL, we will set it to true and return NULL if * we wanted to do a DNS lookup. */ diff --git a/devtools/print_wire.c b/devtools/print_wire.c index d73e7cb07a70..8fbab06d7904 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -27,6 +27,11 @@ void printwire_u64(const char *fieldname, const u64 *v) printf("%"PRIu64"\n", *v); } +void printwire_wireaddr(const char *fieldname, const struct wireaddr *wireaddr) +{ + printf("%s\n", fmt_wireaddr(tmpctx, wireaddr)); +} + /* Returns false if we ran out of data. */ static bool print_hexstring(const u8 **cursor, size_t *plen, size_t len) { diff --git a/devtools/print_wire.h b/devtools/print_wire.h index 5f9f2aff3833..081cd85d7586 100644 --- a/devtools/print_wire.h +++ b/devtools/print_wire.h @@ -14,6 +14,7 @@ struct tlv_print_record_type { typedef u64 bigsize; #define printwire_bigsize printwire_u64 +struct wireaddr; void printwire_u8(const char *fieldname, const u8 *v); void printwire_u16(const char *fieldname, const u16 *v); @@ -24,6 +25,7 @@ void printwire_tlvs(const char *tlv_name, const u8 **cursor, size_t *plen, const struct tlv_print_record_type types[], size_t num_types); void printwire_bitcoin_blkid(const char *fieldname, const struct bitcoin_blkid *bitcoin_blkid); +void printwire_wireaddr(const char *fieldname, const struct wireaddr *wireaddr); void printwire_bitcoin_txid(const char *fieldname, const struct bitcoin_txid *bitcoin_txid); void printwire_channel_id(const char *fieldname, const struct channel_id *channel_id); void printwire_amount_sat(const char *fieldname, const struct amount_sat *sat); From 726b6878d123a13fa27de64d4bf92f8e3187f2aa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:20:13 +1030 Subject: [PATCH 0541/1530] offers: import latest variant from draft. In particular, this changes the name of a field in invoice_request: `payer_signature` becomes simply `signature`. So we allow both for now, and send the old one unless deprecated_apis is disabled. Signed-off-by: Rusty Russell --- common/bolt12_merkle.c | 2 +- common/test/run-bolt12_merkle-json.c | 3 ++ devtools/bolt12-cli.c | 28 ++++++++++++------- lightningd/offer.c | 16 +++++++---- plugins/fetchinvoice.c | 9 ++++-- plugins/offers.c | 41 +++++++++++++++++++--------- plugins/offers_invreq_hook.c | 21 ++++++++++---- plugins/offers_invreq_hook.h | 2 +- wire/bolt12_exp_wire.csv | 13 ++++----- wire/bolt12_wire.csv | 13 ++++----- 10 files changed, 96 insertions(+), 52 deletions(-) diff --git a/common/bolt12_merkle.c b/common/bolt12_merkle.c index 575529f6180c..591685e4e700 100644 --- a/common/bolt12_merkle.c +++ b/common/bolt12_merkle.c @@ -205,7 +205,7 @@ void merkle_tlv(const struct tlv_field *fields, struct sha256 *merkle) * Merkle-root; "lightning" is the literal 9-byte ASCII string, * `messagename` is the name of the TLV stream being signed (i.e. "offer", * "invoice_request" or "invoice") and the `fieldname` is the TLV field - * containing the signature (e.g. "signature" or "payer_signature"). + * containing the signature (e.g. "signature" or "refund_signature"). */ void sighash_from_merkle(const char *messagename, const char *fieldname, diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index ebdd325d5886..cc653aa9d81b 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -60,6 +60,9 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_tu16 */ +void towire_tu16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_tu16 called!\n"); abort(); } /* Generated stub for towire_tu32 */ void towire_tu32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_tu32 called!\n"); abort(); } diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index cd8f4878f9de..de7dedf1be10 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -422,13 +422,13 @@ static void print_relative_expiry(u64 *created_at, u32 *relative) fmt_time(tmpctx, *created_at + *relative)); } -static void print_fallbacks(const struct tlv_invoice_fallbacks *fallbacks) +static void print_fallbacks(struct fallback_address **fallbacks) { - for (size_t i = 0; i < tal_count(fallbacks->fallbacks); i++) { + for (size_t i = 0; i < tal_count(fallbacks); i++) { /* FIXME: format properly! */ printf("fallback: %u %s\n", - fallbacks->fallbacks[i]->version, - tal_hex(tmpctx, fallbacks->fallbacks[i]->address)); + fallbacks[i]->version, + tal_hex(tmpctx, fallbacks[i]->address)); } } @@ -556,12 +556,20 @@ int main(int argc, char *argv[]) print_features(invreq->features); if (invreq->quantity) print_quantity(*invreq->quantity); - if (must_have(invreq, payer_signature)) - well_formed &= print_signature("invoice_request", - "payer_signature", - invreq->fields, - invreq->payer_key, - invreq->payer_signature); + if (must_have(invreq, signature)) { + if (!print_signature("invoice_request", + "signature", + invreq->fields, + invreq->payer_key, + invreq->signature)) { + /* FIXME: We temporarily allow the old "payer_signature" name */ + well_formed &= print_signature("invoice_request", + "payer_signature", + invreq->fields, + invreq->payer_key, + invreq->signature); + } + } if (invreq->recurrence_counter) { print_recurrence_counter(invreq->recurrence_counter, invreq->recurrence_start); diff --git a/lightningd/offer.c b/lightningd/offer.c index 48739c85f037..e251f3b623f0 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -466,16 +467,21 @@ static struct command_result *json_createinvoicerequest(struct command *cmd, } /* BOLT-offers #12: - * - MUST set `payer_signature` `sig` as detailed in + * - MUST set `signature` `sig` as detailed in * [Signature Calculation](#signature-calculation) using the `payer_key`. */ /* This populates the ->fields from our entries */ invreq->fields = tlv_make_fields(invreq, invoice_request); merkle_tlv(invreq->fields, &merkle); - invreq->payer_signature = tal(invreq, struct bip340sig); - hsm_sign_b12(cmd->ld, "invoice_request", "payer_signature", - &merkle, invreq->payer_info, invreq->payer_key, - invreq->payer_signature); + invreq->signature = tal(invreq, struct bip340sig); + if (deprecated_apis) + hsm_sign_b12(cmd->ld, "invoice_request", "payer_signature", + &merkle, invreq->payer_info, invreq->payer_key, + invreq->signature); + else + hsm_sign_b12(cmd->ld, "invoice_request", "signature", + &merkle, invreq->payer_info, invreq->payer_key, + invreq->signature); response = json_stream_success(cmd); json_add_string(response, "bolt12", invrequest_encode(tmpctx, invreq)); diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 91f8cc9a8851..d63d3680d7a7 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -1192,11 +1192,14 @@ force_payer_secret(struct command *cmd, "Could not remarshall invreq %s", tal_hex(tmpctx, msg)); merkle_tlv(sent->invreq->fields, &merkle); - sighash_from_merkle("invoice_request", "payer_signature", &merkle, &sha); + if (deprecated_apis) + sighash_from_merkle("invoice_request", "payer_signature", &merkle, &sha); + else + sighash_from_merkle("invoice_request", "signature", &merkle, &sha); - sent->invreq->payer_signature = tal(invreq, struct bip340sig); + sent->invreq->signature = tal(invreq, struct bip340sig); if (!secp256k1_schnorrsig_sign(secp256k1_ctx, - sent->invreq->payer_signature->u8, + sent->invreq->signature->u8, sha.u.u8, &kp, NULL, NULL)) { diff --git a/plugins/offers.c b/plugins/offers.c index 36bd20a85632..4093a7094328 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -16,7 +16,7 @@ #include struct point32 id; -u32 cltv_final; +u16 cltv_final; bool offers_enabled; static struct command_result *finished(struct command *cmd, @@ -679,7 +679,7 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->fallbacks) valid &= json_add_fallbacks(js, invoice->chain, - invoice->fallbacks->fallbacks); + invoice->fallbacks); /* BOLT-offers #12: * - if the offer contained `refund_for`: @@ -769,23 +769,38 @@ static void json_add_invoice_request(struct json_stream *js, tal_bytelen(invreq->payer_note)); /* BOLT-offers #12: - * - MUST fail the request if there is no `payer_signature` field. - * - MUST fail the request if `payer_signature` is not correct. + * - MUST fail the request if there is no `signature` field. + * - MUST fail the request if `signature` is not correct. */ - if (invreq->payer_signature) { + if (invreq->signature) { if (invreq->payer_key && !bolt12_check_signature(invreq->fields, "invoice_request", - "payer_signature", + "signature", invreq->payer_key, - invreq->payer_signature)) { - json_add_string(js, "warning_invoice_request_invalid_payer_signature", - "Bad payer_signature"); - valid = false; + invreq->signature)) { + bool sig_valid; + + if (deprecated_apis) { + /* The old name? */ + sig_valid = bolt12_check_signature(invreq->fields, + "invoice_request", + "payer_signature", + invreq->payer_key, + invreq->signature); + } else { + sig_valid = false; + } + + if (!sig_valid) { + json_add_string(js, "warning_invoice_request_invalid_signature", + "Bad signature"); + valid = false; + } } } else { - json_add_string(js, "warning_invoice_request_missing_payer_signature", - "Missing payer_signature"); + json_add_string(js, "warning_invoice_request_missing_signature", + "Missing signature"); valid = false; } @@ -836,7 +851,7 @@ static const char *init(struct plugin *p, rpc_scan(p, "listconfigs", take(json_out_obj(NULL, NULL, NULL)), "{cltv-final:%,experimental-offers:%}", - JSON_SCAN(json_to_number, &cltv_final), + JSON_SCAN(json_to_u16, &cltv_final), JSON_SCAN(json_to_bool, &offers_enabled)); return NULL; diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 0b7866603b6a..c5fd0bb5bcdd 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -417,7 +417,7 @@ static struct command_result *check_previous_invoice(struct command *cmd, } /* BOLT-offers #12: - * - MUST fail the request if `payer_signature` is not correct. + * - MUST fail the request if `signature` is not correct. */ static bool check_payer_sig(const struct tlv_invoice_request *invreq, const struct point32 *payer_key, @@ -425,6 +425,17 @@ static bool check_payer_sig(const struct tlv_invoice_request *invreq, { struct sha256 merkle, sighash; merkle_tlv(invreq->fields, &merkle); + sighash_from_merkle("invoice_request", "signature", &merkle, &sighash); + + if (secp256k1_schnorrsig_verify(secp256k1_ctx, + sig->u8, + sighash.u.u8, &payer_key->pubkey) == 1) + return true; + + if (!deprecated_apis) + return false; + + /* Try old name */ sighash_from_merkle("invoice_request", "payer_signature", &merkle, &sighash); @@ -714,13 +725,13 @@ static struct command_result *listoffers_done(struct command *cmd, return err; } - err = invreq_must_have(cmd, ir, payer_signature); + err = invreq_must_have(cmd, ir, signature); if (err) return err; if (!check_payer_sig(ir->invreq, ir->invreq->payer_key, - ir->invreq->payer_signature)) { - return fail_invreq(cmd, ir, "bad payer_signature"); + ir->invreq->signature)) { + return fail_invreq(cmd, ir, "bad signature"); } if (ir->offer->recurrence) { @@ -806,7 +817,7 @@ static struct command_result *listoffers_done(struct command *cmd, ir->inv->payment_hash = tal(ir->inv, struct sha256); sha256(ir->inv->payment_hash, &ir->preimage, sizeof(ir->preimage)); - ir->inv->cltv = tal_dup(ir->inv, u32, &cltv_final); + ir->inv->cltv = tal_dup(ir->inv, u16, &cltv_final); ir->inv->created_at = tal(ir->inv, u64); *ir->inv->created_at = time_now().ts.tv_sec; diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index b33c8d86015e..da59e52b5da0 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -3,7 +3,7 @@ #include "config.h" #include -extern u32 cltv_final; +extern u16 cltv_final; /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, diff --git a/wire/bolt12_exp_wire.csv b/wire/bolt12_exp_wire.csv index 4077edb8d222..4b0ce8519d04 100644 --- a/wire/bolt12_exp_wire.csv +++ b/wire/bolt12_exp_wire.csv @@ -64,8 +64,8 @@ tlvtype,invoice_request,payer_info,50 tlvdata,invoice_request,payer_info,blob,byte,... tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, -tlvtype,invoice_request,payer_signature,240 -tlvdata,invoice_request,payer_signature,sig,bip340sig, +tlvtype,invoice_request,signature,240 +tlvdata,invoice_request,signature,sig,bip340sig, tlvtype,invoice,chain,3 tlvdata,invoice,chain,chain,chain_hash, tlvtype,invoice,offer_id,4 @@ -101,8 +101,6 @@ tlvtype,invoice,payer_key,38 tlvdata,invoice,payer_key,key,point32, tlvtype,invoice,payer_note,39 tlvdata,invoice,payer_note,note,utf8,... -tlvtype,invoice,payer_info,50 -tlvdata,invoice,payer_info,blob,byte,... tlvtype,invoice,created_at,40 tlvdata,invoice,created_at,timestamp,tu64, tlvtype,invoice,payment_hash,42 @@ -110,10 +108,11 @@ tlvdata,invoice,payment_hash,payment_hash,sha256, tlvtype,invoice,relative_expiry,44 tlvdata,invoice,relative_expiry,seconds_from_creation,tu32, tlvtype,invoice,cltv,46 -tlvdata,invoice,cltv,min_final_cltv_expiry,tu32, +tlvdata,invoice,cltv,min_final_cltv_expiry,tu16, tlvtype,invoice,fallbacks,48 -tlvdata,invoice,fallbacks,num,byte, -tlvdata,invoice,fallbacks,fallbacks,fallback_address,num +tlvdata,invoice,fallbacks,fallbacks,fallback_address,... +tlvtype,invoice,payer_info,50 +tlvdata,invoice,payer_info,blob,byte,... tlvtype,invoice,refund_signature,52 tlvdata,invoice,refund_signature,payer_signature,bip340sig, tlvtype,invoice,replace_invoice,56 diff --git a/wire/bolt12_wire.csv b/wire/bolt12_wire.csv index 4077edb8d222..4b0ce8519d04 100644 --- a/wire/bolt12_wire.csv +++ b/wire/bolt12_wire.csv @@ -64,8 +64,8 @@ tlvtype,invoice_request,payer_info,50 tlvdata,invoice_request,payer_info,blob,byte,... tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, -tlvtype,invoice_request,payer_signature,240 -tlvdata,invoice_request,payer_signature,sig,bip340sig, +tlvtype,invoice_request,signature,240 +tlvdata,invoice_request,signature,sig,bip340sig, tlvtype,invoice,chain,3 tlvdata,invoice,chain,chain,chain_hash, tlvtype,invoice,offer_id,4 @@ -101,8 +101,6 @@ tlvtype,invoice,payer_key,38 tlvdata,invoice,payer_key,key,point32, tlvtype,invoice,payer_note,39 tlvdata,invoice,payer_note,note,utf8,... -tlvtype,invoice,payer_info,50 -tlvdata,invoice,payer_info,blob,byte,... tlvtype,invoice,created_at,40 tlvdata,invoice,created_at,timestamp,tu64, tlvtype,invoice,payment_hash,42 @@ -110,10 +108,11 @@ tlvdata,invoice,payment_hash,payment_hash,sha256, tlvtype,invoice,relative_expiry,44 tlvdata,invoice,relative_expiry,seconds_from_creation,tu32, tlvtype,invoice,cltv,46 -tlvdata,invoice,cltv,min_final_cltv_expiry,tu32, +tlvdata,invoice,cltv,min_final_cltv_expiry,tu16, tlvtype,invoice,fallbacks,48 -tlvdata,invoice,fallbacks,num,byte, -tlvdata,invoice,fallbacks,fallbacks,fallback_address,num +tlvdata,invoice,fallbacks,fallbacks,fallback_address,... +tlvtype,invoice,payer_info,50 +tlvdata,invoice,payer_info,blob,byte,... tlvtype,invoice,refund_signature,52 tlvdata,invoice,refund_signature,payer_signature,bip340sig, tlvtype,invoice,replace_invoice,56 From 4d0a97e1bd756c78c060d2f16c1cc171a6a83122 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:21:13 +1030 Subject: [PATCH 0542/1530] offers: import latest variant from draft, part 2. Notably, the latest draft uses the correct tlv types inside the onion, but we don't want to (we'd prefer to demarshal those as a separate step, for better diagnostics), so we change it, then add a spec patch to change it back. Signed-off-by: Rusty Russell --- wire/extracted_onion_02_modernonion.patch | 6 +++--- ...onion_03_onionmsg-payload-as-bytearr.patch | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 wire/extracted_onion_03_onionmsg-payload-as-bytearr.patch diff --git a/wire/extracted_onion_02_modernonion.patch b/wire/extracted_onion_02_modernonion.patch index 653693be9f9a..4d1bc69ca110 100644 --- a/wire/extracted_onion_02_modernonion.patch +++ b/wire/extracted_onion_02_modernonion.patch @@ -25,11 +25,11 @@ +tlvtype,onionmsg_payload,encrypted_data_tlv,4 +tlvdata,onionmsg_payload,encrypted_data_tlv,encrypted_data_tlv,byte,... +tlvtype,onionmsg_payload,invoice_request,64 -+tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... ++tlvdata,onionmsg_payload,invoice_request,invoice_request,tlv_invoice_request, +tlvtype,onionmsg_payload,invoice,66 -+tlvdata,onionmsg_payload,invoice,invoice,byte,... ++tlvdata,onionmsg_payload,invoice,invoice,tlv_invoice, +tlvtype,onionmsg_payload,invoice_error,68 -+tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... ++tlvdata,onionmsg_payload,invoice_error,invoice_error,tlv_invoice_error, subtype,onionmsg_path subtypedata,onionmsg_path,node_id,point, subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_onion_03_onionmsg-payload-as-bytearr.patch b/wire/extracted_onion_03_onionmsg-payload-as-bytearr.patch new file mode 100644 index 000000000000..a71a3069b742 --- /dev/null +++ b/wire/extracted_onion_03_onionmsg-payload-as-bytearr.patch @@ -0,0 +1,19 @@ +diff --git b/wire/onion_wire.csv a/wire/onion_wire.csv +index 5c52fe9a1..2ac0c4cff 100644 +--- b/wire/onion_wire.csv ++++ a/wire/onion_wire.csv +@@ -49,11 +49,11 @@ tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... + tlvtype,onionmsg_payload,encrypted_data_tlv,4 + tlvdata,onionmsg_payload,encrypted_data_tlv,encrypted_data_tlv,byte,... + tlvtype,onionmsg_payload,invoice_request,64 +-tlvdata,onionmsg_payload,invoice_request,invoice_request,tlv_invoice_request, ++tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... + tlvtype,onionmsg_payload,invoice,66 +-tlvdata,onionmsg_payload,invoice,invoice,tlv_invoice, ++tlvdata,onionmsg_payload,invoice,invoice,byte,... + tlvtype,onionmsg_payload,invoice_error,68 +-tlvdata,onionmsg_payload,invoice_error,invoice_error,tlv_invoice_error, ++tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... + subtype,onionmsg_path + subtypedata,onionmsg_path,node_id,point, + subtypedata,onionmsg_path,enclen,u16, From 65f5bb263824e939fae37e85dc22448cc7541441 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 22 Mar 2022 19:22:13 +1030 Subject: [PATCH 0543/1530] pytest: test for compat code. Signed-off-by: Rusty Russell --- plugins/offers_invreq_hook.c | 7 +++++-- tests/test_pay.py | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index c5fd0bb5bcdd..13cb896a1f16 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -419,7 +419,8 @@ static struct command_result *check_previous_invoice(struct command *cmd, /* BOLT-offers #12: * - MUST fail the request if `signature` is not correct. */ -static bool check_payer_sig(const struct tlv_invoice_request *invreq, +static bool check_payer_sig(struct command *cmd, + const struct tlv_invoice_request *invreq, const struct point32 *payer_key, const struct bip340sig *sig) { @@ -436,6 +437,8 @@ static bool check_payer_sig(const struct tlv_invoice_request *invreq, return false; /* Try old name */ + plugin_log(cmd->plugin, LOG_DBG, + "Testing invoice_request with old name 'payer_signature'"); sighash_from_merkle("invoice_request", "payer_signature", &merkle, &sighash); @@ -728,7 +731,7 @@ static struct command_result *listoffers_done(struct command *cmd, err = invreq_must_have(cmd, ir, signature); if (err) return err; - if (!check_payer_sig(ir->invreq, + if (!check_payer_sig(cmd, ir->invreq, ir->invreq->payer_key, ir->invreq->signature)) { return fail_invreq(cmd, ir, "bad signature"); diff --git a/tests/test_pay.py b/tests/test_pay.py index 12a5c8b6226b..d55dade1de5e 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4453,6 +4453,20 @@ def test_offer(node_factory, bitcoind): assert 'recurrence: every 600 seconds paywindow -10 to +600 (pay proportional)\n' in output +def test_deprecated_offer(node_factory, bitcoind): + """Test that we allow old invreq name `payer_signature` with deprecated_apis""" + l1, l2 = node_factory.line_graph(2, opts={'experimental-offers': None, + 'allow-deprecated-apis': True}) + + offer = l2.rpc.call('offer', {'amount': 10000, + 'description': 'test'})['bolt12'] + + inv = l1.rpc.call('fetchinvoice', {'offer': offer})['invoice'] + l2.daemon.wait_for_log("Testing invoice_request with old name 'payer_signature'") + + l1.rpc.pay(inv) + + @pytest.mark.developer("dev-no-modern-onion is DEVELOPER-only") def test_fetchinvoice_3hop(node_factory, bitcoind): l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True, From 1cb93ffc27554dd445cf9cd160a1eb18bb5edea4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:12 +1030 Subject: [PATCH 0544/1530] devtools: remove blindedpath tool. It only made obsolete onions anyway. Signed-off-by: Rusty Russell --- devtools/Makefile | 4 +- devtools/blindedpath.c | 303 ----------------------------------------- 2 files changed, 1 insertion(+), 306 deletions(-) delete mode 100644 devtools/blindedpath.c diff --git a/devtools/Makefile b/devtools/Makefile index 181c1eae5acc..ba529da0f66f 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -1,4 +1,4 @@ -DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/blindedpath devtools/bolt12-cli devtools/encodeaddr devtools/features devtools/fp16 +DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/bolt12-cli devtools/encodeaddr devtools/features devtools/fp16 ifeq ($(HAVE_SQLITE3),1) DEVTOOLS += devtools/checkchannels endif @@ -70,8 +70,6 @@ devtools/onion.c: ccan/config.h devtools/onion: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) common/onion.o common/onionreply.o wire/fromwire.o wire/towire.o devtools/onion.o common/sphinx.o -devtools/blindedpath: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) common/blinding.o $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/blindedpath.o common/onion.o common/onionreply.o common/sphinx.o - devtools/gossipwith: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/peer$(EXP)_wiregen.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS): wire/wire.h diff --git a/devtools/blindedpath.c b/devtools/blindedpath.c deleted file mode 100644 index 3e46662ab2a8..000000000000 --- a/devtools/blindedpath.c +++ /dev/null @@ -1,303 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static bool simpleout = false; - -/* Tal wrappers for opt. */ -static void *opt_allocfn(size_t size) -{ - return tal_arr_label(NULL, char, size, TAL_LABEL("opt_allocfn", "")); -} - -static void *tal_reallocfn(void *ptr, size_t size) -{ - if (!ptr) - return opt_allocfn(size); - tal_resize_(&ptr, 1, size, false); - return ptr; -} - -static void tal_freefn(void *ptr) -{ - tal_free(ptr); -} - -/* We don't actually use this, but common/onion needs it */ -void ecdh(const struct pubkey *point, struct secret *ss) -{ - abort(); -} - -int main(int argc, char **argv) -{ - bool first = false; - - common_setup(argv[0]); - - opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); - opt_register_noarg("--help|-h", opt_usage_and_exit, - "\n\n\tcreate [/]...\n" - "\tunwrap \n", - "Show this message"); - opt_register_noarg("--first-node", opt_set_bool, &first, - "Don't try to tweak key to unwrap onion"); - opt_register_noarg("--simple-output", opt_set_bool, &simpleout, - "Output values without prefixes, one per line"); - opt_register_version(); - - opt_parse(&argc, argv, opt_log_stderr_exit); - - if (argc < 2) - errx(1, "You must specify create or unwrap"); - if (streq(argv[1], "create")) { - struct privkey e; - struct pubkey *pk_e, *b, *nodes; - struct short_channel_id **scids; - struct secret *rho; - size_t num = argc - 2; - - if (argc < 3) - errx(1, "create requires at least one nodeid"); - - /* P(i) */ - nodes = tal_arr(tmpctx, struct pubkey, num); - /* E(i) */ - pk_e = tal_arr(tmpctx, struct pubkey, num); - /* B(i) */ - b = tal_arr(tmpctx, struct pubkey, num); - /* rho(i) */ - rho = tal_arr(tmpctx, struct secret, num); - - scids = tal_arr(tmpctx, struct short_channel_id *, num); - /* Randomness, chosen with a fair dice roll! */ - memset(&e, 6, sizeof(e)); - if (!pubkey_from_privkey(&e, &pk_e[0])) - abort(); - - for (size_t i = 0; i < num; i++) { - struct secret ss; - struct secret hmac; - struct sha256 h; - const char *slash; - - if (!pubkey_from_hexstr(argv[2+i], - strcspn(argv[2+i], "/"), - &nodes[i])) - errx(1, "%s not a valid pubkey", argv[2+i]); - - slash = strchr(argv[2+i], '/'); - if (slash) { - scids[i] = tal(scids, struct short_channel_id); - if (!short_channel_id_from_str(slash+1, - strlen(slash+1), - scids[i])) - errx(1, "%s is not a valid scids", - slash + 1); - } else - scids[i] = NULL; - if (secp256k1_ecdh(secp256k1_ctx, ss.data, - &nodes[i].pubkey, e.secret.data, NULL, NULL) != 1) - abort(); - - subkey_from_hmac("blinded_node_id", &ss, &hmac); - b[i] = nodes[i]; - if (i != 0) { - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &b[i].pubkey, hmac.data) != 1) - abort(); - } - subkey_from_hmac("rho", &ss, &rho[i]); - blinding_hash_e_and_ss(&pk_e[i], &ss, &h); - if (i != num-1) - blinding_next_pubkey(&pk_e[i], &h, - &pk_e[i+1]); - blinding_next_privkey(&e, &h, &e); - } - - /* Print initial blinding factor */ - if (simpleout) - printf("%s\n", - type_to_string(tmpctx, struct pubkey, &pk_e[0])); - else - printf("Blinding: %s\n", - type_to_string(tmpctx, struct pubkey, &pk_e[0])); - - for (size_t i = 0; i < num - 1; i++) { - u8 *p; - u8 buf[BIGSIZE_MAX_LEN]; - const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - struct tlv_obs2_onionmsg_payload *outer; - struct tlv_obs2_encmsg_tlvs *inner; - int ret; - - /* Inner is encrypted */ - inner = tlv_obs2_encmsg_tlvs_new(tmpctx); - inner->next_node_id = tal_dup(inner, struct pubkey, &nodes[i+1]); - p = tal_arr(tmpctx, u8, 0); - towire_obs2_encmsg_tlvs(&p, inner); - - outer = tlv_obs2_onionmsg_payload_new(tmpctx); - outer->enctlv = tal_arr(outer, u8, tal_count(p) - + crypto_aead_chacha20poly1305_ietf_ABYTES); - ret = crypto_aead_chacha20poly1305_ietf_encrypt(outer->enctlv, NULL, - p, - tal_bytelen(p), - NULL, 0, - NULL, npub, - rho[i].data); - assert(ret == 0); - - p = tal_arr(tmpctx, u8, 0); - towire_obs2_onionmsg_payload(&p, outer); - ret = bigsize_put(buf, tal_bytelen(p)); - - if (simpleout) { - printf("%s\n%s\n", - type_to_string(tmpctx, struct pubkey, - &b[i]), - tal_hex(tmpctx, outer->enctlv)); - } else { - /* devtools/onion wants length explicitly prepended */ - printf("%s/%.*s%s ", - type_to_string(tmpctx, struct pubkey, - &b[i]), - ret * 2, - tal_hexstr(tmpctx, buf, ret), - tal_hex(tmpctx, p)); - } - } - /* No payload for last node */ - if (simpleout) - printf("%s\n", - type_to_string(tmpctx, struct pubkey, &b[num-1])); - else - printf("%s/00\n", - type_to_string(tmpctx, struct pubkey, &b[num-1])); - } else if (streq(argv[1], "unwrap")) { - struct privkey privkey; - struct pubkey blinding; - u8 onion[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)], *dec; - struct onionpacket *op; - struct secret ss, onion_ss; - struct secret hmac, rho; - struct route_step *rs; - const u8 *cursor; - struct tlv_obs2_onionmsg_payload *outer; - size_t max, len; - struct pubkey res; - struct sha256 h; - int ret; - enum onion_wire failcode; - const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - if (argc != 5) - errx(1, "unwrap requires privkey, onion and blinding"); - - if (!hex_decode(argv[2], strlen(argv[2]), &privkey, - sizeof(privkey))) - errx(1, "Invalid private key hex '%s'", argv[2]); - - if (!hex_decode(argv[3], strlen(argv[3]), onion, - sizeof(onion))) - errx(1, "Invalid onion %s", argv[3]); - - if (!pubkey_from_hexstr(argv[4], strlen(argv[4]), &blinding)) - errx(1, "Invalid blinding %s", argv[4]); - - op = parse_onionpacket(tmpctx, onion, sizeof(onion), &failcode); - if (!op) - errx(1, "Unparsable onion"); - - /* ss(r) = H(k(r) * E(r)) */ - if (secp256k1_ecdh(secp256k1_ctx, ss.data, &blinding.pubkey, - privkey.secret.data, NULL, NULL) != 1) - abort(); - - subkey_from_hmac("rho", &ss, &rho); - - /* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */ - subkey_from_hmac("blinded_node_id", &ss, &hmac); - - /* We instead tweak the *ephemeral* key from the onion - * and use our raw privkey: this models how lightningd - * will do it, since hsmd knows only how to ECDH with - * our real key */ - res = op->ephemeralkey; - if (!first) { - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &res.pubkey, - hmac.data) != 1) - abort(); - } - - if (secp256k1_ecdh(secp256k1_ctx, onion_ss.data, - &res.pubkey, - privkey.secret.data, NULL, NULL) != 1) - abort(); - - rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); - if (!rs) - errx(1, "Could not process onionpacket"); - - cursor = rs->raw_payload; - max = tal_bytelen(cursor); - len = fromwire_bigsize(&cursor, &max); - - /* Always true since we're non-legacy */ - assert(len == max); - outer = tlv_obs2_onionmsg_payload_new(tmpctx); - if (!fromwire_obs2_onionmsg_payload(&cursor, &max, outer)) - errx(1, "Invalid payload %s", - tal_hex(tmpctx, rs->raw_payload)); - - if (rs->nextcase == ONION_END) { - printf("TERMINAL\n"); - return 0; - } - - /* Look for enctlv */ - if (!outer->enctlv) - errx(1, "No encrypted_recipient_data field"); - - if (tal_bytelen(outer->enctlv) - < crypto_aead_chacha20poly1305_ietf_ABYTES) - errx(1, "encrypted_recipient_data field too short"); - - dec = tal_arr(tmpctx, u8, - tal_bytelen(outer->enctlv) - - crypto_aead_chacha20poly1305_ietf_ABYTES); - ret = crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL, - NULL, - outer->enctlv, - tal_bytelen(outer->enctlv), - NULL, 0, - npub, - rho.data); - if (ret != 0) - errx(1, "Failed to decrypt encrypted_recipient_data field"); - - printf("Contents: %s\n", tal_hex(tmpctx, dec)); - - /* E(i-1) = H(E(i) || ss(i)) * E(i) */ - blinding_hash_e_and_ss(&blinding, &ss, &h); - blinding_next_pubkey(&blinding, &h, &res); - printf("Next blinding: %s\n", - type_to_string(tmpctx, struct pubkey, &res)); - printf("Next onion: %s\n", tal_hex(tmpctx, serialize_onionpacket(tmpctx, rs->next))); - } else - errx(1, "Either create or unwrap!"); - - common_shutdown(); -} From 7829f2eb0602baf195caa9d0af3487a02475ffe8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0545/1530] onion_messages: remove obs2 support. Signed-off-by: Rusty Russell Changelog-EXPERIMENTAL: Removed backwards compat with onion messages from v0.10.1. --- common/blindedpath.c | 163 --------- common/blindedpath.h | 32 -- common/json_helpers.c | 36 -- common/json_helpers.h | 4 - connectd/connectd_wire.csv | 2 - connectd/multiplex.c | 3 - connectd/onion_message.c | 162 +-------- connectd/onion_message.h | 4 +- connectd/test/run-onion_message.c | 414 ---------------------- lightningd/lightningd.c | 1 - lightningd/lightningd.h | 2 +- lightningd/onion_message.c | 165 ++------- lightningd/options.c | 3 - plugins/fetchinvoice.c | 96 +---- plugins/offers.c | 77 +--- plugins/offers.h | 1 - plugins/offers_inv_hook.c | 10 +- plugins/offers_inv_hook.h | 3 +- plugins/offers_invreq_hook.c | 10 +- plugins/offers_invreq_hook.h | 3 +- tests/test_pay.py | 39 +- wire/extracted_onion_01_offers.patch | 22 +- wire/extracted_onion_02_modernonion.patch | 6 +- wire/extracted_onion_exp_enctlv.patch | 7 +- wire/onion_wire.csv | 20 -- 25 files changed, 67 insertions(+), 1218 deletions(-) delete mode 100644 connectd/test/run-onion_message.c diff --git a/common/blindedpath.c b/common/blindedpath.c index 7b60c0f0ccea..b931cd45ec3d 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -114,19 +114,6 @@ static u8 *enctlv_from_encmsg_raw(const tal_t *ctx, return ret; } -static u8 *enctlv_from_obs2_encmsg(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *node, - const struct tlv_obs2_encmsg_tlvs *encmsg, - struct privkey *next_blinding, - struct pubkey *node_alias) -{ - u8 *encmsg_raw = tal_arr(NULL, u8, 0); - towire_obs2_encmsg_tlvs(&encmsg_raw, encmsg); - return enctlv_from_encmsg_raw(ctx, blinding, node, take(encmsg_raw), - next_blinding, node_alias); -} - static u8 *enctlv_from_encmsg(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *node, @@ -196,28 +183,6 @@ static u8 *decrypt_encmsg_raw(const tal_t *ctx, return dec; } -static struct tlv_obs2_encmsg_tlvs *decrypt_obs2_encmsg(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv) -{ - struct tlv_obs2_encmsg_tlvs *encmsg; - const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); - size_t maxlen = tal_bytelen(cursor); - - /* BOLT-onion-message #4: - * - * - if the `enctlv` is not a valid TLV... - * - MUST drop the message. - */ - encmsg = tlv_obs2_encmsg_tlvs_new(ctx); - if (!fromwire_obs2_encmsg_tlvs(&cursor, &maxlen, encmsg) - || !tlv_fields_valid(encmsg->fields, NULL, NULL)) - return tal_free(encmsg); - - return encmsg; -} - static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, const struct pubkey *blinding, const struct secret *ss, @@ -366,131 +331,3 @@ u8 *create_final_enctlv(const tal_t *ctx, return enctlv_from_encmsg(ctx, blinding, final_node, encmsg, &unused_next_blinding, node_alias); } - -/* Obsolete variants */ -bool decrypt_obs2_enctlv(const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - struct pubkey *next_node, - struct pubkey *next_blinding) -{ - struct tlv_obs2_encmsg_tlvs *encmsg; - - encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); - if (!encmsg) - return false; - - /* BOLT-onion-message #4: - * - * The reader: - * - if it is not the final node according to the onion encryption: - *... - * - if the `enctlv` ... does not contain - * `next_node_id`: - * - MUST drop the message. - */ - if (!encmsg->next_node_id) - return false; - - /* BOLT-onion-message #4: - * The reader: - * - if it is not the final node according to the onion encryption: - *... - * - if the `enctlv` contains `self_id`: - * - MUST drop the message. - */ - if (encmsg->self_id) - return false; - - /* BOLT-onion-message #4: - * The reader: - * - if it is not the final node according to the onion encryption: - *... - * - if `blinding` is specified in the `enctlv`: - * - MUST pass that as `blinding` in the `onion_message` - * - otherwise: - * - MUST pass `blinding` derived as in - * [Route Blinding][route-blinding] (i.e. - * `E(i+1) = H(E(i) || ss(i)) * E(i)`). - */ - *next_node = *encmsg->next_node_id; - if (encmsg->next_blinding) - *next_blinding = *encmsg->next_blinding; - else { - /* E(i-1) = H(E(i) || ss(i)) * E(i) */ - struct sha256 h; - blinding_hash_e_and_ss(blinding, ss, &h); - blinding_next_pubkey(blinding, &h, next_blinding); - } - return true; -} - -bool decrypt_obs2_final_enctlv(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - const struct pubkey *my_id, - struct pubkey *alias, - struct secret **self_id) -{ - struct tlv_obs2_encmsg_tlvs *encmsg; - struct secret node_id_blinding; - - /* Repeat the tweak to get the alias it was using for us */ - subkey_from_hmac("blinded_node_id", ss, &node_id_blinding); - *alias = *my_id; - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &alias->pubkey, - node_id_blinding.data) != 1) - return false; - - encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); - if (!encmsg) - return false; - - if (tal_bytelen(encmsg->self_id) == sizeof(**self_id)) { - *self_id = tal(ctx, struct secret); - memcpy(*self_id, encmsg->self_id, sizeof(**self_id)); - } else - *self_id = NULL; - - return true; -} - -u8 *create_obs2_enctlv(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *node, - const struct pubkey *next_node, - size_t padlen, - const struct pubkey *override_blinding, - struct privkey *next_blinding, - struct pubkey *node_alias) -{ - struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); - if (padlen) - encmsg->padding = tal_arrz(encmsg, u8, padlen); - encmsg->next_node_id = cast_const(struct pubkey *, next_node); - encmsg->next_blinding = cast_const(struct pubkey *, override_blinding); - - return enctlv_from_obs2_encmsg(ctx, blinding, node, encmsg, - next_blinding, node_alias); -} - -u8 *create_obs2_final_enctlv(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *final_node, - size_t padlen, - const struct secret *self_id, - struct pubkey *node_alias) -{ - struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); - struct privkey unused_next_blinding; - - if (padlen) - encmsg->padding = tal_arrz(encmsg, u8, padlen); - if (self_id) - encmsg->self_id = (u8 *)tal_dup(encmsg, struct secret, self_id); - - return enctlv_from_obs2_encmsg(ctx, blinding, final_node, encmsg, - &unused_next_blinding, node_alias); -} diff --git a/common/blindedpath.h b/common/blindedpath.h index f3415939551f..285ec189d972 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -105,36 +105,4 @@ bool decrypt_final_enctlv(const tal_t *ctx, struct secret **path_id) NON_NULL_ARGS(1, 2, 4, 5); -/* Obsolete variants */ -u8 *create_obs2_enctlv(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *node, - const struct pubkey *next_node, - size_t padlen, - const struct pubkey *override_blinding, - struct privkey *next_blinding, - struct pubkey *node_alias) - NON_NULL_ARGS(2, 3, 4, 7, 8); -u8 *create_obs2_final_enctlv(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *final_node, - size_t padlen, - const struct secret *self_id, - struct pubkey *node_alias) - NON_NULL_ARGS(2, 3, 6); -bool decrypt_obs2_enctlv(const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - struct pubkey *next_node, - struct pubkey *next_blinding) - NON_NULL_ARGS(1, 2, 4, 5); -bool decrypt_obs2_final_enctlv(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv, - const struct pubkey *my_id, - struct pubkey *alias, - struct secret **self_id) - NON_NULL_ARGS(1, 2, 4, 5); - #endif /* LIGHTNING_COMMON_BLINDEDPATH_H */ diff --git a/common/json_helpers.c b/common/json_helpers.c index af06c2036a2e..90e570b5c6e4 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -158,42 +158,6 @@ struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, return psbt_from_b64(ctx, buffer + tok->start, tok->end - tok->start); } -struct tlv_obs2_onionmsg_payload_reply_path * -json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) -{ - struct tlv_obs2_onionmsg_payload_reply_path *rpath; - const jsmntok_t *hops, *t; - size_t i; - const char *err; - - rpath = tal(ctx, struct tlv_obs2_onionmsg_payload_reply_path); - err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", - JSON_SCAN(json_to_pubkey, &rpath->blinding), - JSON_SCAN(json_to_pubkey, &rpath->first_node_id), - NULL); - if (err) - return tal_free(rpath); - - hops = json_get_member(buffer, tok, "hops"); - if (!hops || hops->size < 1) - return tal_free(rpath); - - rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); - json_for_each_arr(i, t, hops) { - rpath->path[i] = tal(rpath->path, struct onionmsg_path); - err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", - JSON_SCAN(json_to_pubkey, - &rpath->path[i]->node_id), - JSON_SCAN_TAL(rpath->path[i], - json_tok_bin_from_hex, - &rpath->path[i]->encrypted_recipient_data)); - if (err) - return tal_free(rpath); - } - - return rpath; -} - struct tlv_onionmsg_payload_reply_path * json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) { diff --git a/common/json_helpers.h b/common/json_helpers.h index 1f5fe7fe3239..d8edee866956 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -88,10 +88,6 @@ bool split_tok(const char *buffer, const jsmntok_t *tok, struct tlv_onionmsg_payload_reply_path * json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); -/* Obsolete version! */ -struct tlv_obs2_onionmsg_payload_reply_path * -json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); - /* Helpers for outputting JSON results */ /* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 5b0950f9376e..b10e668a14ba 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -122,7 +122,6 @@ msgdata,connectd_ping_reply,totlen,u16, # We tell lightningd we got an onionmsg msgtype,connectd_got_onionmsg_to_us,2145 -msgdata,connectd_got_onionmsg_to_us,obs2,bool, msgdata,connectd_got_onionmsg_to_us,node_alias,pubkey, msgdata,connectd_got_onionmsg_to_us,self_id,?secret, msgdata,connectd_got_onionmsg_to_us,reply_blinding,?pubkey, @@ -134,7 +133,6 @@ msgdata,connectd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len # Lightningd tells us to send an onion message. msgtype,connectd_send_onionmsg,2041 -msgdata,connectd_send_onionmsg,obs2,bool, msgdata,connectd_send_onionmsg,id,node_id, msgdata,connectd_send_onionmsg,onion_len,u16, msgdata,connectd_send_onionmsg,onion,u8,onion_len diff --git a/connectd/multiplex.c b/connectd/multiplex.c index c39c35ea094c..03af2a0fb844 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -633,9 +633,6 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) } else if (type == WIRE_PONG) { handle_pong_in(peer, msg); return true; - } else if (type == WIRE_OBS2_ONION_MESSAGE) { - handle_obs2_onion_message(peer->daemon, peer, msg); - return true; } else if (type == WIRE_ONION_MESSAGE) { handle_onion_message(peer->daemon, peer, msg); return true; diff --git a/connectd/onion_message.c b/connectd/onion_message.c index 5459fd57b181..e3fd7fbdc98a 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -16,178 +16,21 @@ #include #include -/* Peer sends obsolete onion msg. */ -void handle_obs2_onion_message(struct daemon *daemon, - struct peer *peer, const u8 *msg) -{ - enum onion_wire badreason; - struct onionpacket *op; - struct pubkey blinding, ephemeral; - struct route_step *rs; - u8 *onion; - struct tlv_obs2_onionmsg_payload *om; - struct secret ss, onion_ss; - const u8 *cursor; - size_t max, maxlen; - - /* Ignore unless explicitly turned on. */ - if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], - OPT_ONION_MESSAGES)) - return; - - /* FIXME: ratelimit! */ - if (!fromwire_obs2_onion_message(msg, msg, &blinding, &onion)) { - inject_peer_msg(peer, - towire_warningfmt(NULL, NULL, - "Bad onion_message")); - return; - } - - /* We unwrap the onion now. */ - op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); - if (!op) { - status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", - onion_wire_name(badreason)); - return; - } - - ephemeral = op->ephemeralkey; - if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { - status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); - return; - } - - /* Now get onion shared secret and parse it. */ - ecdh(&ephemeral, &onion_ss); - rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); - if (!rs) { - status_peer_debug(&peer->id, - "onion msg: can't process onionpacket ss=%s", - type_to_string(tmpctx, struct secret, &onion_ss)); - return; - } - - /* The raw payload is prepended with length in the modern onion. */ - cursor = rs->raw_payload; - max = tal_bytelen(rs->raw_payload); - maxlen = fromwire_bigsize(&cursor, &max); - if (!cursor) { - status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return; - } - if (maxlen > max) { - status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return; - } - - om = tlv_obs2_onionmsg_payload_new(msg); - if (!fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)) { - status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", - tal_hex(tmpctx, rs->raw_payload)); - return; - } - - if (rs->nextcase == ONION_END) { - struct pubkey *reply_blinding, *first_node_id, me, alias; - const struct onionmsg_path **reply_path; - struct secret *self_id; - u8 *omsg; - - if (!pubkey_from_node_id(&me, &daemon->id)) { - status_broken("Failed to convert own id"); - return; - } - - /* Final enctlv is actually optional */ - if (!om->enctlv) { - alias = me; - self_id = NULL; - } else if (!decrypt_obs2_final_enctlv(tmpctx, &blinding, &ss, - om->enctlv, &me, &alias, - &self_id)) { - status_peer_debug(&peer->id, - "onion msg: failed to decrypt enctlv" - " %s", tal_hex(tmpctx, om->enctlv)); - return; - } - - if (om->reply_path) { - first_node_id = &om->reply_path->first_node_id; - reply_blinding = &om->reply_path->blinding; - reply_path = cast_const2(const struct onionmsg_path **, - om->reply_path->path); - } else { - first_node_id = NULL; - reply_blinding = NULL; - reply_path = NULL; - } - - /* We re-marshall here by policy, before handing to lightningd */ - omsg = tal_arr(tmpctx, u8, 0); - towire_tlvstream_raw(&omsg, om->fields); - daemon_conn_send(daemon->master, - take(towire_connectd_got_onionmsg_to_us(NULL, - true, /* obs2 */ - &alias, self_id, - reply_blinding, - first_node_id, - reply_path, - omsg))); - } else { - struct pubkey next_node, next_blinding; - struct peer *next_peer; - struct node_id next_node_id; - - /* This fails as expected if no enctlv. */ - if (!decrypt_obs2_enctlv(&blinding, &ss, om->enctlv, &next_node, - &next_blinding)) { - status_peer_debug(&peer->id, - "onion msg: invalid enctlv %s", - tal_hex(tmpctx, om->enctlv)); - return; - } - - /* Even though lightningd checks for valid ids, there's a race - * where it might vanish before we read this command. */ - node_id_from_pubkey(&next_node_id, &next_node); - next_peer = peer_htable_get(&daemon->peers, &next_node_id); - if (!next_peer) { - status_peer_debug(&peer->id, - "onion msg: unknown next peer %s", - type_to_string(tmpctx, - struct pubkey, - &next_node)); - return; - } - inject_peer_msg(next_peer, - take(towire_obs2_onion_message(NULL, - &next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); - } -} - void onionmsg_req(struct daemon *daemon, const u8 *msg) { struct node_id id; u8 *onionmsg; struct pubkey blinding; struct peer *peer; - bool obs2; - if (!fromwire_connectd_send_onionmsg(msg, msg, &obs2, &id, &onionmsg, &blinding)) + if (!fromwire_connectd_send_onionmsg(msg, msg, &id, &onionmsg, &blinding)) master_badmsg(WIRE_CONNECTD_SEND_ONIONMSG, msg); /* Even though lightningd checks for valid ids, there's a race * where it might vanish before we read this command. */ peer = peer_htable_get(&daemon->peers, &id); if (peer) { - u8 *omsg; - if (obs2) - omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg); - else - omsg = towire_onion_message(NULL, &blinding, onionmsg); + u8 *omsg = towire_onion_message(NULL, &blinding, onionmsg); inject_peer_msg(peer, take(omsg)); } } @@ -305,7 +148,6 @@ void handle_onion_message(struct daemon *daemon, towire_tlvstream_raw(&omsg, om->fields); daemon_conn_send(daemon->master, take(towire_connectd_got_onionmsg_to_us(NULL, - false, /* !obs2 */ &alias, self_id, reply_blinding, first_node_id, diff --git a/connectd/onion_message.h b/connectd/onion_message.h index 22fc9bb8b4a4..41b25982ce7e 100644 --- a/connectd/onion_message.h +++ b/connectd/onion_message.h @@ -3,9 +3,7 @@ #include "config.h" #include -/* Various messages come in from peer */ -void handle_obs2_onion_message(struct daemon *daemon, - struct peer *peer, const u8 *msg); +/* Onion message comes in from peer */ void handle_onion_message(struct daemon *daemon, struct peer *peer, const u8 *msg); diff --git a/connectd/test/run-onion_message.c b/connectd/test/run-onion_message.c deleted file mode 100644 index 8d69fabee873..000000000000 --- a/connectd/test/run-onion_message.c +++ /dev/null @@ -1,414 +0,0 @@ -#include "config.h" -#include "../onion_message.c" -#include "common/blindedpath.c" -#include "common/blinding.c" -#include "common/bigsize.c" -#include "common/hmac.c" -#include "common/onion.c" -#include "common/sphinx.c" -#include "wire/fromwire.c" -#if EXPERIMENTAL_FEATURES -#include "wire/peer_exp_wiregen.c" -#include "wire/onion_exp_wiregen.c" -#else -#include "wire/peer_wiregen.c" -#include "wire/onion_wiregen.c" -#endif -#include "wire/tlvstream.c" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AUTOGENERATED MOCKS START */ -/* Generated stub for amount_asset_is_main */ -bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) -{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } -/* Generated stub for amount_asset_to_sat */ -struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) -{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } -/* Generated stub for amount_msat */ -struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) -{ fprintf(stderr, "amount_msat called!\n"); abort(); } -/* Generated stub for amount_msat_eq */ -bool amount_msat_eq(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED) -{ fprintf(stderr, "amount_msat_eq called!\n"); abort(); } -/* Generated stub for amount_sat */ -struct amount_sat amount_sat(u64 satoshis UNNEEDED) -{ fprintf(stderr, "amount_sat called!\n"); abort(); } -/* Generated stub for amount_sat_add */ - bool amount_sat_add(struct amount_sat *val UNNEEDED, - struct amount_sat a UNNEEDED, - struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } -/* Generated stub for amount_sat_eq */ -bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } -/* Generated stub for amount_sat_greater_eq */ -bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } -/* Generated stub for amount_sat_sub */ - bool amount_sat_sub(struct amount_sat *val UNNEEDED, - struct amount_sat a UNNEEDED, - struct amount_sat b UNNEEDED) -{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } -/* Generated stub for amount_sat_to_asset */ -struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) -{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } -/* Generated stub for amount_tx_fee */ -struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) -{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for daemon_conn_send */ -void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) -{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } -/* Generated stub for ecdh */ -void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) -{ fprintf(stderr, "ecdh called!\n"); abort(); } -/* Generated stub for fromwire_amount_msat */ -struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } -/* Generated stub for fromwire_amount_sat */ -struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) -{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } -/* Generated stub for fromwire_channel_id */ -bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_connectd_send_onionmsg */ -bool fromwire_connectd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *obs2 UNNEEDED, struct node_id *id UNNEEDED, u8 **onion UNNEEDED, struct pubkey *blinding UNNEEDED) -{ fprintf(stderr, "fromwire_connectd_send_onionmsg called!\n"); abort(); } -/* Generated stub for fromwire_node_id */ -void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) -{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for inject_peer_msg */ -void inject_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) -{ fprintf(stderr, "inject_peer_msg called!\n"); abort(); } -/* Generated stub for master_badmsg */ -void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) -{ fprintf(stderr, "master_badmsg called!\n"); abort(); } -/* Generated stub for new_onionreply */ -struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) -{ fprintf(stderr, "new_onionreply called!\n"); abort(); } -/* Generated stub for node_id_from_pubkey */ -void node_id_from_pubkey(struct node_id *id UNNEEDED, const struct pubkey *key UNNEEDED) -{ fprintf(stderr, "node_id_from_pubkey called!\n"); abort(); } -/* Generated stub for pubkey_from_node_id */ -bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) -{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } -/* Generated stub for status_fmt */ -void status_fmt(enum log_level level UNNEEDED, - const struct node_id *peer UNNEEDED, - const char *fmt UNNEEDED, ...) - -{ fprintf(stderr, "status_fmt called!\n"); abort(); } -/* Generated stub for towire */ -void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "towire called!\n"); abort(); } -/* Generated stub for towire_amount_msat */ -void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) -{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } -/* Generated stub for towire_amount_sat */ -void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } -/* Generated stub for towire_bool */ -void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) -{ fprintf(stderr, "towire_bool called!\n"); abort(); } -/* Generated stub for towire_channel_id */ -void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_connectd_got_onionmsg_to_us */ -u8 *towire_connectd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEEDED, const struct pubkey *node_alias UNNEEDED, const struct secret *self_id UNNEEDED, const struct pubkey *reply_blinding UNNEEDED, const struct pubkey *reply_first_node UNNEEDED, const struct onionmsg_path **reply_path UNNEEDED, const u8 *rawmsg UNNEEDED) -{ fprintf(stderr, "towire_connectd_got_onionmsg_to_us called!\n"); abort(); } -/* Generated stub for towire_node_id */ -void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) -{ fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_pad */ -void towire_pad(u8 **pptr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "towire_pad called!\n"); abort(); } -/* Generated stub for towire_secp256k1_ecdsa_signature */ -void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, - const secp256k1_ecdsa_signature *signature UNNEEDED) -{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } -/* Generated stub for towire_sha256 */ -void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) -{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } -/* Generated stub for towire_tu32 */ -void towire_tu32(u8 **pptr UNNEEDED, u32 v UNNEEDED) -{ fprintf(stderr, "towire_tu32 called!\n"); abort(); } -/* Generated stub for towire_tu64 */ -void towire_tu64(u8 **pptr UNNEEDED, u64 v UNNEEDED) -{ fprintf(stderr, "towire_tu64 called!\n"); abort(); } -/* Generated stub for towire_u16 */ -void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) -{ fprintf(stderr, "towire_u16 called!\n"); abort(); } -/* Generated stub for towire_u32 */ -void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) -{ fprintf(stderr, "towire_u32 called!\n"); abort(); } -/* Generated stub for towire_u64 */ -void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) -{ fprintf(stderr, "towire_u64 called!\n"); abort(); } -/* Generated stub for towire_u8 */ -void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) -{ fprintf(stderr, "towire_u8 called!\n"); abort(); } -/* Generated stub for towire_u8_array */ -void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) -{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } -/* Generated stub for towire_warningfmt */ -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 towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } -/* AUTOGENERATED MOCKS END */ - -/* Updated each time, as we pretend to be Alice, Bob, Carol */ -static const struct privkey *mykey; - -static void test_ecdh(const struct pubkey *point, struct secret *ss) -{ - if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, - mykey->secret.data, NULL, NULL) != 1) - abort(); -} - -static void json_strfield(const char *name, const char *val) -{ - printf("\t\"%s\": \"%s\",\n", name, val); -} - -static void json_onionmsg_payload(const struct tlv_obs2_onionmsg_payload *om) -{ - if (om->reply_path) { - printf("\t\"reply_path\": {\n"); - json_strfield("first_node_id", - type_to_string(tmpctx, struct pubkey, - &om->reply_path->first_node_id)); - json_strfield("blinding", - type_to_string(tmpctx, struct pubkey, - &om->reply_path->blinding)); - printf("\t\"path\": [\n"); - for (size_t i = 0; i < tal_count(om->reply_path->path); i++) { - json_strfield("node_id", - type_to_string(tmpctx, struct pubkey, - &om->reply_path->path[i]->node_id)); - json_strfield("encrypted_recipient_data", - tal_hex(tmpctx, - om->reply_path->path[i]->encrypted_recipient_data)); - } - printf("]}\n"); - } - if (om->invoice) - json_strfield("invoice", tal_hex(tmpctx, om->invoice)); - if (om->invoice_request) - json_strfield("invoice_request", - tal_hex(tmpctx, om->invoice_request)); - if (om->invoice_error) - json_strfield("invoice_error", - tal_hex(tmpctx, om->invoice_error)); -} - -/* Return next onion (and updates blinding), or NULL */ -static u8 *json_test(const char *testname, - const u8 *data, - const struct privkey *me, - const struct privkey *blinding_priv, - struct pubkey *blinding) -{ - struct pubkey my_id, next_node; - struct secret ss, onion_ss; - struct pubkey ephemeral; - struct route_step *rs; - const u8 *cursor; - size_t max, maxlen; - struct onionpacket *op; - struct tlv_obs2_onionmsg_payload *om; - - op = parse_onionpacket(tmpctx, data, tal_bytelen(data), NULL); - assert(op); - - pubkey_from_privkey(me, &my_id); - printf("{"); - json_strfield("test name", testname); - json_strfield("reader_privkey", - type_to_string(tmpctx, struct privkey, me)); - json_strfield("reader_id", - type_to_string(tmpctx, struct pubkey, &my_id)); - - if (blinding_priv) - json_strfield("blinding_privkey", - type_to_string(tmpctx, struct privkey, - blinding_priv)); - json_strfield("blinding", - type_to_string(tmpctx, struct pubkey, blinding)); - printf("\"onionmsg\": {\n"); - json_strfield("raw", tal_hex(tmpctx, data)); - json_strfield("version", tal_fmt(tmpctx, "%i", op->version)); - json_strfield("public_key", - type_to_string(tmpctx, struct pubkey, &op->ephemeralkey)); - json_strfield("hop_payloads", - tal_hex(tmpctx, op->routinginfo)); - json_strfield("hmac", - tal_hexstr(tmpctx, &op->hmac, sizeof(op->hmac))); - printf("},\n"); - - ephemeral = op->ephemeralkey; - - /* Set this for test_ecdh */ - mykey = me; - assert(unblind_onion(blinding, test_ecdh, &ephemeral, &ss)); - json_strfield("ECDH shared secret", - type_to_string(tmpctx, struct secret, &ss)); - /* Reproduce internal calc from unblind_onion */ - { - struct secret hmac; - subkey_from_hmac("blinded_node_id", &ss, &hmac); - json_strfield("HMAC256(\\\"blinded_node_id\\\", ss(i)) * k(i)", - type_to_string(tmpctx, struct secret, &hmac)); - } - json_strfield("Tweaked onion pubkey", - type_to_string(tmpctx, struct pubkey, &ephemeral)); - - /* Now get onion shared secret and parse it. */ - test_ecdh(&ephemeral, &onion_ss); - json_strfield("onion shared secret", - type_to_string(tmpctx, struct secret, &onion_ss)); - rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); - assert(rs); - - printf("\"onion contents\": {\n"); - json_strfield("raw", tal_hex(tmpctx, rs->raw_payload)); - - cursor = rs->raw_payload; - max = tal_bytelen(rs->raw_payload); - maxlen = fromwire_bigsize(&cursor, &max); - json_strfield("length", tal_fmt(tmpctx, "%zu", maxlen)); - json_strfield("rawtlv", tal_hexstr(tmpctx, cursor, maxlen)); - json_strfield("hmac", tal_hexstr(tmpctx, rs->next->hmac.bytes, - sizeof(rs->next->hmac.bytes))); - om = tlv_obs2_onionmsg_payload_new(tmpctx); - assert(fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)); - - json_onionmsg_payload(om); - - /* We expect one of these. */ - assert(om->enctlv); - - printf("\t\"encrypted_data_tlv\": {\n"); - json_strfield("raw", tal_hex(tmpctx, om->enctlv)); - - if (rs->nextcase == ONION_END) { - struct secret *self_id; - struct pubkey alias; - assert(decrypt_obs2_final_enctlv(tmpctx, - blinding, &ss, - om->enctlv, - &my_id, &alias, &self_id)); - if (self_id) { - json_strfield("self_id", - type_to_string(tmpctx, struct secret, - self_id)); - } - printf("}\n"); - return NULL; - } else { - assert(decrypt_obs2_enctlv(blinding, &ss, om->enctlv, &next_node, - blinding)); - json_strfield("next_node", - type_to_string(tmpctx, struct pubkey, &next_node)); - json_strfield("next_blinding", - type_to_string(tmpctx, struct pubkey, - blinding)); - printf("}"); - printf("},\n"); - return serialize_onionpacket(tmpctx, rs->next); - } -} - -int main(int argc, char *argv[]) -{ - struct onionpacket *op; - u8 *data; - struct privkey alice, bob, carol, dave, blinding_priv; - struct pubkey alice_id, bob_id, carol_id, dave_id; - struct pubkey blinding; - - common_setup(argv[0]); - - memset(&alice, 'A', sizeof(alice)); - memset(&bob, 'B', sizeof(bob)); - memset(&carol, 'C', sizeof(carol)); - memset(&dave, 'D', sizeof(dave)); - pubkey_from_privkey(&alice, &alice_id); - pubkey_from_privkey(&bob, &bob_id); - pubkey_from_privkey(&carol, &carol_id); - pubkey_from_privkey(&dave, &dave_id); - - /* ThomasH sends via email: - * - * { - * "version":0, - * "public_key": - * "0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", - * "hop_payloads": - * "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", - * "hmac": "564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac" - * } - */ - op = tal(tmpctx, struct onionpacket); - op->version = 0; - assert(pubkey_from_hexstr("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", strlen("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967"), &op->ephemeralkey)); - assert(hex_decode("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac", - strlen("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac"), - &op->hmac, sizeof(op->hmac))); - op->routinginfo = tal_hexdata(op, "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", - strlen("37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a")); - - data = serialize_onionpacket(tmpctx, op); - printf("[\n"); - - memset(&blinding_priv, 5, sizeof(blinding_priv)); - pubkey_from_privkey(&blinding_priv, &blinding); - - data = json_test("onion message for Alice", - data, - &alice, - &blinding_priv, - &blinding); - - data = json_test("onion message for Bob", - data, - &bob, - NULL, - &blinding); - - data = json_test("onion message for Carol", - data, - &carol, - NULL, - &blinding); - - data = json_test("onion message for Dave", - data, - &dave, - NULL, - &blinding); - - assert(!data); - printf("]\n"); - - common_shutdown(); - return 0; -} diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index f7ffa99eb97d..2d7fab1449a5 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -134,7 +134,6 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_no_version_checks = false; ld->dev_max_funding_unconfirmed = 2016; ld->dev_ignore_modern_onion = false; - ld->dev_ignore_obsolete_onion = false; ld->dev_disable_commit = -1; #endif diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 973db2a7c863..6025a31e2b42 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -263,7 +263,7 @@ struct lightningd { u32 dev_max_funding_unconfirmed; /* Special switches to test onion compatibility */ - bool dev_ignore_modern_onion, dev_ignore_obsolete_onion; + bool dev_ignore_modern_onion; /* Tell channeld to disable commits after this many. */ int dev_disable_commit; diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index fec95b7ca547..aa8d89821383 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -21,10 +22,7 @@ struct onion_message_hook_payload { struct onionmsg_path **reply_path; struct pubkey *reply_first_node; struct pubkey *our_alias; - - /* Exactly one of these is set! */ struct tlv_onionmsg_payload *om; - struct tlv_obs2_onionmsg_payload *obs2_om; }; static void json_add_blindedpath(struct json_stream *stream, @@ -63,56 +61,32 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload, payload->reply_path); } - /* Common convenience fields */ - if (payload->obs2_om) { - json_add_bool(stream, "obs2", true); - if (payload->obs2_om->invoice_request) - json_add_hex_talarr(stream, "invoice_request", - payload->obs2_om->invoice_request); - if (payload->obs2_om->invoice) - json_add_hex_talarr(stream, "invoice", payload->obs2_om->invoice); - - if (payload->obs2_om->invoice_error) - json_add_hex_talarr(stream, "invoice_error", - payload->obs2_om->invoice_error); - - json_array_start(stream, "unknown_fields"); - for (size_t i = 0; i < tal_count(payload->obs2_om->fields); i++) { - if (payload->obs2_om->fields[i].meta) - continue; - json_object_start(stream, NULL); - json_add_u64(stream, "number", payload->obs2_om->fields[i].numtype); - json_add_hex(stream, "value", - payload->obs2_om->fields[i].value, - payload->obs2_om->fields[i].length); - json_object_end(stream); - } - json_array_end(stream); - } else { + if (deprecated_apis) json_add_bool(stream, "obs2", false); - if (payload->om->invoice_request) - json_add_hex_talarr(stream, "invoice_request", - payload->om->invoice_request); - if (payload->om->invoice) - json_add_hex_talarr(stream, "invoice", payload->om->invoice); - - if (payload->om->invoice_error) - json_add_hex_talarr(stream, "invoice_error", - payload->om->invoice_error); - - json_array_start(stream, "unknown_fields"); - for (size_t i = 0; i < tal_count(payload->om->fields); i++) { - if (payload->om->fields[i].meta) - continue; - json_object_start(stream, NULL); - json_add_u64(stream, "number", payload->om->fields[i].numtype); - json_add_hex(stream, "value", - payload->om->fields[i].value, - payload->om->fields[i].length); - json_object_end(stream); - } - json_array_end(stream); + + if (payload->om->invoice_request) + json_add_hex_talarr(stream, "invoice_request", + payload->om->invoice_request); + if (payload->om->invoice) + json_add_hex_talarr(stream, "invoice", payload->om->invoice); + + if (payload->om->invoice_error) + json_add_hex_talarr(stream, "invoice_error", + payload->om->invoice_error); + + json_array_start(stream, "unknown_fields"); + for (size_t i = 0; i < tal_count(payload->om->fields); i++) { + if (payload->om->fields[i].meta) + continue; + json_object_start(stream, NULL); + json_add_u64(stream, "number", payload->om->fields[i].numtype); + json_add_hex(stream, "value", + payload->om->fields[i].value, + payload->om->fields[i].length); + json_object_end(stream); } + json_array_end(stream); + json_object_end(stream); } @@ -143,7 +117,6 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) struct onion_message_hook_payload *payload; u8 *submsg; struct secret *self_id; - bool obs2; size_t submsglen; const u8 *subptr; @@ -151,7 +124,6 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) payload->our_alias = tal(payload, struct pubkey); if (!fromwire_connectd_got_onionmsg_to_us(payload, msg, - &obs2, payload->our_alias, &self_id, &payload->reply_blinding, @@ -164,9 +136,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) } #if DEVELOPER - if (!obs2 && ld->dev_ignore_modern_onion) - return; - if (obs2 && ld->dev_ignore_obsolete_onion) + if (ld->dev_ignore_modern_onion) return; #endif @@ -178,23 +148,11 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) submsglen = tal_bytelen(submsg); subptr = submsg; - if (obs2) { - payload->om = NULL; - payload->obs2_om = tlv_obs2_onionmsg_payload_new(payload); - if (!fromwire_obs2_onionmsg_payload(&subptr, - &submsglen, payload->obs2_om)) { - log_broken(ld->log, "bad got_onionmsg_tous obs2 om: %s", - tal_hex(tmpctx, msg)); - return; - } - } else { - payload->obs2_om = NULL; - payload->om = tlv_onionmsg_payload_new(payload); - if (!fromwire_onionmsg_payload(&subptr, &submsglen, payload->om)) { - log_broken(ld->log, "bad got_onionmsg_tous om: %s", - tal_hex(tmpctx, msg)); - return; - } + payload->om = tlv_onionmsg_payload_new(payload); + if (!fromwire_onionmsg_payload(&subptr, &submsglen, payload->om)) { + log_broken(ld->log, "bad got_onionmsg_tous om: %s", + tal_hex(tmpctx, msg)); + return; } tal_free(submsg); @@ -251,11 +209,10 @@ static struct command_result *param_onion_hops(struct command *cmd, return NULL; } -static struct command_result *json_sendonionmessage2(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params, - bool obs2) +static struct command_result *json_sendonionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct onion_hop *hops; struct node_id *first_id; @@ -301,29 +258,13 @@ static struct command_result *json_sendonionmessage2(struct command *cmd, "Creating onion failed (tlvs too long?)"); subd_send_msg(cmd->ld->connectd, - take(towire_connectd_send_onionmsg(NULL, obs2, first_id, + take(towire_connectd_send_onionmsg(NULL, first_id, serialize_onionpacket(tmpctx, op), blinding))); return command_success(cmd, json_stream_success(cmd)); } -static struct command_result *json_sendonionmessage(struct command *cmd, - const char *buffer, - const jsmntok_t *obj, - const jsmntok_t *params) -{ - return json_sendonionmessage2(cmd, buffer, obj, params, false); -} - -static struct command_result *json_sendobs2onionmessage(struct command *cmd, - const char *buffer, - const jsmntok_t *obj, - const jsmntok_t *params) -{ - return json_sendonionmessage2(cmd, buffer, obj, params, true); -} - static const struct json_command sendonionmessage_command = { "sendonionmessage", "utility", @@ -332,14 +273,6 @@ static const struct json_command sendonionmessage_command = { }; AUTODATA(json_command, &sendonionmessage_command); -static const struct json_command sendobs2onionmessage_command = { - "sendobs2onionmessage", - "utility", - json_sendobs2onionmessage, - "Send obsolete message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)" -}; -AUTODATA(json_command, &sendobs2onionmessage_command); - static struct command_result *param_pubkeys(struct command *cmd, const char *name, const char *buffer, @@ -431,32 +364,6 @@ static struct command_result *json_blindedpath(struct command *cmd, json_add_blindedpath(response, "blindedpath", &first_blinding_pubkey, &first_node, path); - /* Now create obsolete one! */ - blinding_iter = first_blinding; - for (size_t i = 0; i < nhops - 1; i++) { - path[i] = tal(path, struct onionmsg_path); - path[i]->encrypted_recipient_data = create_obs2_enctlv(path[i], - &blinding_iter, - &ids[i], - &ids[i+1], - /* FIXME: Pad? */ - 0, - NULL, - &blinding_iter, - &path[i]->node_id); - } - - /* FIXME: Add padding! */ - path[nhops-1] = tal(path, struct onionmsg_path); - path[nhops-1]->encrypted_recipient_data = create_obs2_final_enctlv(path[nhops-1], - &blinding_iter, - &ids[nhops-1], - /* FIXME: Pad? */ - 0, - &cmd->ld->onion_reply_secret, - &path[nhops-1]->node_id); - json_add_blindedpath(response, "obs2blindedpath", - &first_blinding_pubkey, &first_node, path); return command_success(cmd, response); } diff --git a/lightningd/options.c b/lightningd/options.c index e756854929ec..cb190223f9ea 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -733,9 +733,6 @@ static void dev_register_opts(struct lightningd *ld) opt_register_noarg("--dev-no-modern-onion", opt_set_bool, &ld->dev_ignore_modern_onion, "Ignore modern onion messages"); - opt_register_noarg("--dev-no-obsolete-onion", opt_set_bool, - &ld->dev_ignore_obsolete_onion, - "Ignore obsolete onion messages"); opt_register_arg("--dev-disable-commit-after", opt_set_intval, opt_show_intval, &ld->dev_disable_commit, diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index d63d3680d7a7..3c267ebe6e90 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -641,94 +641,12 @@ struct sending { struct sent *sent; const char *msgfield; const u8 *msgval; - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; struct command_result *(*done)(struct command *cmd, const char *buf UNUSED, const jsmntok_t *result UNUSED, struct sent *sent); }; -static struct command_result * -send_obs2_message(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct sending *sending) -{ - struct sent *sent = sending->sent; - struct privkey blinding_iter; - struct pubkey fwd_blinding, *node_alias; - size_t nhops = tal_count(sent->path); - struct tlv_obs2_onionmsg_payload **payloads; - struct out_req *req; - - /* Now create enctlvs for *forward* path. */ - randombytes_buf(&blinding_iter, sizeof(blinding_iter)); - if (!pubkey_from_privkey(&blinding_iter, &fwd_blinding)) - return command_fail(cmd, LIGHTNINGD, - "Could not convert blinding %s to pubkey!", - type_to_string(tmpctx, struct privkey, - &blinding_iter)); - - /* We overallocate: this node (0) doesn't have payload or alias */ - payloads = tal_arr(cmd, struct tlv_obs2_onionmsg_payload *, nhops); - node_alias = tal_arr(cmd, struct pubkey, nhops); - - for (size_t i = 1; i < nhops - 1; i++) { - payloads[i] = tlv_obs2_onionmsg_payload_new(payloads); - payloads[i]->enctlv = create_obs2_enctlv(payloads[i], - &blinding_iter, - &sent->path[i], - &sent->path[i+1], - /* FIXME: Pad? */ - 0, - NULL, - &blinding_iter, - &node_alias[i]); - } - /* Final payload contains the actual data. */ - payloads[nhops-1] = tlv_obs2_onionmsg_payload_new(payloads); - - /* We don't include enctlv in final, but it gives us final alias */ - if (!create_obs2_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], - /* FIXME: Pad? */ 0, - NULL, - &node_alias[nhops-1])) { - /* Should not happen! */ - return command_fail(cmd, LIGHTNINGD, - "Could create final enctlv"); - } - - /* FIXME: This interface is a string for sendobsonionmessage! */ - if (streq(sending->msgfield, "invoice_request")) { - payloads[nhops-1]->invoice_request - = cast_const(u8 *, sending->msgval); - } else { - assert(streq(sending->msgfield, "invoice")); - payloads[nhops-1]->invoice - = cast_const(u8 *, sending->msgval); - } - payloads[nhops-1]->reply_path = sending->obs2_reply_path; - - req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", - sending->done, - forward_error, - sending->sent); - json_add_pubkey(req->js, "first_id", &sent->path[1]); - json_add_pubkey(req->js, "blinding", &fwd_blinding); - json_array_start(req->js, "hops"); - for (size_t i = 1; i < nhops; i++) { - u8 *tlv; - json_object_start(req->js, NULL); - json_add_pubkey(req->js, "id", &node_alias[i]); - tlv = tal_arr(tmpctx, u8, 0); - towire_obs2_onionmsg_payload(&tlv, payloads[i]); - json_add_hex_talarr(req->js, "tlv", tlv); - json_object_end(req->js); - } - json_array_end(req->js); - return send_outreq(cmd->plugin, req); -} - static struct command_result * send_modern_message(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, @@ -790,10 +708,9 @@ send_modern_message(struct command *cmd, payloads[nhops-1]->reply_path = reply_path; req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", - /* Try sending older version next */ - send_obs2_message, + sending->done, forward_error, - sending); + sending->sent); json_add_pubkey(req->js, "first_id", &sent->path[1]); json_add_pubkey(req->js, "blinding", &fwd_blinding); json_array_start(req->js, "hops"); @@ -827,15 +744,6 @@ static struct command_result *use_reply_path(struct command *cmd, json_tok_full_len(result), json_tok_full(buf, result)); - sending->obs2_reply_path = json_to_obs2_reply_path(cmd, buf, - json_get_member(buf, result, - "obs2blindedpath")); - if (!sending->obs2_reply_path) - plugin_err(cmd->plugin, - "could not parse obs2 reply path %.*s?", - json_tok_full_len(result), - json_tok_full(buf, result)); - /* Remember our alias we used so we can recognize reply */ sending->sent->reply_alias = tal_dup(sending->sent, struct pubkey, diff --git a/plugins/offers.c b/plugins/offers.c index 4093a7094328..19b36b517f0b 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -42,64 +42,15 @@ static struct command_result *sendonionmessage_error(struct command *cmd, } /* FIXME: replyfield string interface is to accomodate obsolete API */ -static struct command_result * -send_obs2_onion_reply(struct command *cmd, - struct tlv_obs2_onionmsg_payload_reply_path *reply_path, - const char *replyfield, - const u8 *replydata) -{ - struct out_req *req; - size_t nhops = tal_count(reply_path->path); - - req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", - finished, sendonionmessage_error, NULL); - - json_add_pubkey(req->js, "first_id", &reply_path->first_node_id); - json_add_pubkey(req->js, "blinding", &reply_path->blinding); - json_array_start(req->js, "hops"); - for (size_t i = 0; i < nhops; i++) { - struct tlv_obs2_onionmsg_payload *omp; - u8 *tlv; - - json_object_start(req->js, NULL); - json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); - - omp = tlv_obs2_onionmsg_payload_new(tmpctx); - omp->enctlv = reply_path->path[i]->encrypted_recipient_data; - - /* Put payload in last hop. */ - if (i == nhops - 1) { - if (streq(replyfield, "invoice")) { - omp->invoice = cast_const(u8 *, replydata); - } else { - assert(streq(replyfield, "invoice_error")); - omp->invoice_error = cast_const(u8 *, replydata); - } - } - tlv = tal_arr(tmpctx, u8, 0); - towire_obs2_onionmsg_payload(&tlv, omp); - json_add_hex_talarr(req->js, "tlv", tlv); - json_object_end(req->js); - } - json_array_end(req->js); - return send_outreq(cmd->plugin, req); -} - struct command_result * send_onion_reply(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, const char *replyfield, const u8 *replydata) { struct out_req *req; size_t nhops; - /* Exactly one must be set! */ - assert(!reply_path != !obs2_reply_path); - if (obs2_reply_path) - return send_obs2_onion_reply(cmd, obs2_reply_path, replyfield, replydata); - req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", finished, sendonionmessage_error, NULL); @@ -141,7 +92,6 @@ static struct command_result *onion_message_modern_call(struct command *cmd, const jsmntok_t *params) { const jsmntok_t *om, *replytok, *invreqtok, *invtok; - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path = NULL; struct tlv_onionmsg_payload_reply_path *reply_path = NULL; if (!offers_enabled) @@ -150,31 +100,20 @@ static struct command_result *onion_message_modern_call(struct command *cmd, om = json_get_member(buf, params, "onion_message"); replytok = json_get_member(buf, om, "reply_blindedpath"); if (replytok) { - bool obs2; - json_to_bool(buf, json_get_member(buf, om, "obs2"), &obs2); - if (obs2) { - obs2_reply_path = json_to_obs2_reply_path(cmd, buf, replytok); - if (!obs2_reply_path) - plugin_err(cmd->plugin, "Invalid obs2 reply path %.*s?", - json_tok_full_len(replytok), - json_tok_full(buf, replytok)); - } else { - reply_path = json_to_reply_path(cmd, buf, replytok); - if (!reply_path) - plugin_err(cmd->plugin, "Invalid reply path %.*s?", - json_tok_full_len(replytok), - json_tok_full(buf, replytok)); - } + reply_path = json_to_reply_path(cmd, buf, replytok); + if (!reply_path) + plugin_err(cmd->plugin, "Invalid reply path %.*s?", + json_tok_full_len(replytok), + json_tok_full(buf, replytok)); } invreqtok = json_get_member(buf, om, "invoice_request"); if (invreqtok) { const u8 *invreqbin = json_tok_bin_from_hex(tmpctx, buf, invreqtok); - if (reply_path || obs2_reply_path) + if (reply_path) return handle_invoice_request(cmd, invreqbin, - reply_path, - obs2_reply_path); + reply_path); else plugin_log(cmd->plugin, LOG_DBG, "invoice_request without reply_path"); @@ -184,7 +123,7 @@ static struct command_result *onion_message_modern_call(struct command *cmd, if (invtok) { const u8 *invbin = json_tok_bin_from_hex(tmpctx, buf, invtok); if (invbin) - return handle_invoice(cmd, invbin, reply_path, obs2_reply_path); + return handle_invoice(cmd, invbin, reply_path); } return command_hook_success(cmd); diff --git a/plugins/offers.h b/plugins/offers.h index 68feefa12d69..dab0c6216f4d 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -10,7 +10,6 @@ struct command; struct command_result *WARN_UNUSED_RESULT send_onion_reply(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, const char *replyfield, const u8 *replydata); #endif /* LIGHTNING_PLUGINS_OFFERS_H */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index cc8731ae6c0c..0e3128ae68b5 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -12,7 +12,6 @@ struct inv { struct tlv_invoice *inv; /* May be NULL */ - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; struct tlv_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ @@ -41,7 +40,7 @@ fail_inv_level(struct command *cmd, plugin_log(cmd->plugin, l, "%s", msg); /* Only reply if they gave us a path */ - if (!inv->reply_path && !inv->obs2_reply_path) + if (!inv->reply_path) return command_hook_success(cmd); /* Don't send back internal error details. */ @@ -55,8 +54,7 @@ fail_inv_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, inv->reply_path, inv->obs2_reply_path, - "invoice_error", errdata); + return send_onion_reply(cmd, inv->reply_path, "invoice_error", errdata); } static struct command_result *WARN_UNUSED_RESULT @@ -316,8 +314,7 @@ static struct command_result *listoffers_error(struct command *cmd, struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS, - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS) + struct tlv_onionmsg_payload_reply_path *reply_path STEALS) { size_t len = tal_count(invbin); struct inv *inv = tal(cmd, struct inv); @@ -326,7 +323,6 @@ struct command_result *handle_invoice(struct command *cmd, int bad_feature; struct sha256 m, shash; - inv->obs2_reply_path = tal_steal(inv, obs2_reply_path); inv->reply_path = tal_steal(inv, reply_path); inv->inv = tlv_invoice_new(cmd); diff --git a/plugins/offers_inv_hook.h b/plugins/offers_inv_hook.h index a733d6c4626e..fbc12ace6815 100644 --- a/plugins/offers_inv_hook.h +++ b/plugins/offers_inv_hook.h @@ -6,6 +6,5 @@ /* We got an onionmessage with an invoice! reply_path could be NULL. */ struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS, - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); + struct tlv_onionmsg_payload_reply_path *reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */ diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 13cb896a1f16..1092c7c06fb6 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -17,7 +17,6 @@ struct invreq { struct tlv_invoice_request *invreq; struct tlv_onionmsg_payload_reply_path *reply_path; - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; /* The offer, once we've looked it up. */ struct tlv_offer *offer; @@ -61,7 +60,7 @@ fail_invreq_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, invreq->reply_path, invreq->obs2_reply_path, + return send_onion_reply(cmd, invreq->reply_path, "invoice_error", errdata); } @@ -181,8 +180,7 @@ static struct command_result *createinvoice_done(struct command *cmd, json_tok_full(buf, t)); } - return send_onion_reply(cmd, ir->reply_path, ir->obs2_reply_path, - "invoice", rawinv); + return send_onion_reply(cmd, ir->reply_path, "invoice", rawinv); } static struct command_result *createinvoice_error(struct command *cmd, @@ -844,15 +842,13 @@ static struct command_result *handle_offerless_request(struct command *cmd, struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_onionmsg_payload_reply_path *reply_path, - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path) + struct tlv_onionmsg_payload_reply_path *reply_path) { size_t len = tal_count(invreqbin); struct invreq *ir = tal(cmd, struct invreq); struct out_req *req; int bad_feature; - ir->obs2_reply_path = tal_steal(ir, obs2_reply_path); ir->reply_path = tal_steal(ir, reply_path); ir->invreq = tlv_invoice_request_new(cmd); diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index da59e52b5da0..c9dc5f37c64d 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -8,6 +8,5 @@ extern u16 cltv_final; /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS, - struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); + struct tlv_onionmsg_payload_reply_path *reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */ diff --git a/tests/test_pay.py b/tests/test_pay.py index d55dade1de5e..63cf2331e0ad 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4479,27 +4479,6 @@ def test_fetchinvoice_3hop(node_factory, bitcoind): l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - # Make sure l4 handled both onions before shutting down - l1.daemon.wait_for_log(r'plugin-fetchinvoice: Received modern onion .*obs2\\":false') - l1.daemon.wait_for_log(r'plugin-fetchinvoice: No match for modern onion.*obs2\\":true') - - # Test with obsolete onion. - l4.stop() - l4.daemon.opts['dev-no-modern-onion'] = None - l4.start() - l4.rpc.connect(l3.info['id'], 'localhost', l3.port) - - l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - - # Test with modern onion. - l4.stop() - del l4.daemon.opts['dev-no-modern-onion'] - l4.daemon.opts['dev-no-obsolete-onion'] = None - l4.start() - l4.rpc.connect(l3.info['id'], 'localhost', l3.port) - - l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) - def test_fetchinvoice(node_factory, bitcoind): # We remove the conversion plugin on l3, causing it to get upset. @@ -4796,10 +4775,8 @@ def test_dev_rawrequest(node_factory): assert 'invoice' in ret -def do_test_sendinvoice(node_factory, bitcoind, disable): +def test_sendinvoice(node_factory, bitcoind): l2opts = {'experimental-offers': None} - if disable: - l2opts[disable] = None l1, l2 = node_factory.line_graph(2, wait_for_announce=True, opts=[{'experimental-offers': None}, l2opts]) @@ -4881,20 +4858,6 @@ def do_test_sendinvoice(node_factory, bitcoind, disable): assert out['amount_received_msat'] == Millisatoshi(10000000) -def test_sendinvoice(node_factory, bitcoind): - do_test_sendinvoice(node_factory, bitcoind, None) - - -@pytest.mark.developer("needs to --dev-no-obsolete-onion") -def test_sendinvoice_modern(node_factory, bitcoind): - do_test_sendinvoice(node_factory, bitcoind, 'dev-no-obsolete-onion') - - -@pytest.mark.developer("needs to --dev-no-modern-onion") -def test_sendinvoice_obsolete(node_factory, bitcoind): - do_test_sendinvoice(node_factory, bitcoind, 'dev-no-modern-onion') - - def test_self_pay(node_factory): """Repro test for issue 4345: pay ourselves via the pay plugin. diff --git a/wire/extracted_onion_01_offers.patch b/wire/extracted_onion_01_offers.patch index 90c0cb6c41ac..263d9b73682a 100644 --- a/wire/extracted_onion_01_offers.patch +++ b/wire/extracted_onion_01_offers.patch @@ -1,29 +1,9 @@ --- wire/extracted_onion_wire_csv 2020-03-25 10:24:12.861645774 +1030 +++ - 2020-03-26 13:47:13.498294435 +1030 -@@ -8,6 +8,30 @@ +@@ -8,6 +8,10 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,obs2_onionmsg_payload,reply_path,2 -+tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, -+tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, -+tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... -+tlvtype,obs2_onionmsg_payload,enctlv,10 -+tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... -+tlvtype,obs2_onionmsg_payload,invoice_request,64 -+tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... -+tlvtype,obs2_onionmsg_payload,invoice,66 -+tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... -+tlvtype,obs2_onionmsg_payload,invoice_error,68 -+tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... -+tlvtype,obs2_encmsg_tlvs,padding,1 -+tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... -+tlvtype,obs2_encmsg_tlvs,next_node_id,4 -+tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, -+tlvtype,obs2_encmsg_tlvs,next_blinding,12 -+tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, -+tlvtype,obs2_encmsg_tlvs,self_id,14 -+tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... +subtype,onionmsg_path +subtypedata,onionmsg_path,node_id,point, +subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_onion_02_modernonion.patch b/wire/extracted_onion_02_modernonion.patch index 4d1bc69ca110..bed4ec9cc231 100644 --- a/wire/extracted_onion_02_modernonion.patch +++ b/wire/extracted_onion_02_modernonion.patch @@ -1,9 +1,9 @@ --- wire/onion_wire.csv 2021-11-16 15:17:39.446494580 +1030 +++ wire/onion_wire.csv.raw 2021-11-16 15:36:00.046441058 +1030 @@ -8,10 +8,36 @@ - tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, - tlvtype,obs2_encmsg_tlvs,self_id,14 - tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... + tlvtype,tlv_payload,payment_data,8 + tlvdata,tlv_payload,payment_data,payment_secret,byte,32 + tlvdata,tlv_payload,payment_data,total_msat,tu64, +tlvtype,tlv_payload,encrypted_recipient_data,10 +tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... +tlvtype,tlv_payload,blinding_point,12 diff --git a/wire/extracted_onion_exp_enctlv.patch b/wire/extracted_onion_exp_enctlv.patch index 1897193b8c8b..458bc318e503 100644 --- a/wire/extracted_onion_exp_enctlv.patch +++ b/wire/extracted_onion_exp_enctlv.patch @@ -10,6 +10,7 @@ +tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... +tlvtype,tlv_payload,blinding_point,12 +tlvdata,tlv_payload,blinding_point,blinding,point, - tlvtype,obs2_onionmsg_payload,reply_path,2 - tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, - tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, + tlvtype,tlv_payload,encrypted_recipient_data,10 + tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... + tlvtype,tlv_payload,blinding_point,12 + diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index 2ac0c4cff516..87c2cc8f0de6 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -8,26 +8,6 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -tlvtype,obs2_onionmsg_payload,reply_path,2 -tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, -tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, -tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... -tlvtype,obs2_onionmsg_payload,enctlv,10 -tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... -tlvtype,obs2_onionmsg_payload,invoice_request,64 -tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... -tlvtype,obs2_onionmsg_payload,invoice,66 -tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... -tlvtype,obs2_onionmsg_payload,invoice_error,68 -tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... -tlvtype,obs2_encmsg_tlvs,padding,1 -tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... -tlvtype,obs2_encmsg_tlvs,next_node_id,4 -tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, -tlvtype,obs2_encmsg_tlvs,next_blinding,12 -tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, -tlvtype,obs2_encmsg_tlvs,self_id,14 -tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... tlvtype,tlv_payload,encrypted_recipient_data,10 tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... tlvtype,tlv_payload,blinding_point,12 From f447e39d2d9af7e0885a5586aee1fb0c153d6d0c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0546/1530] offers: neater response on malformed invoice. Signed-off-by: Rusty Russell --- plugins/offers_inv_hook.c | 15 +++++++++------ plugins/offers_invreq_hook.c | 15 +++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index 0e3128ae68b5..bfeb5b87f0e0 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -28,12 +28,15 @@ fail_inv_level(struct command *cmd, struct tlv_invoice_error *err; u8 *errdata; - full_fmt = tal_fmt(tmpctx, "Failed invoice %s", - invoice_encode(tmpctx, inv->inv)); - if (inv->inv->offer_id) - tal_append_fmt(&full_fmt, " for offer %s", - type_to_string(tmpctx, struct sha256, - inv->inv->offer_id)); + full_fmt = tal_fmt(tmpctx, "Failed invoice"); + if (inv->inv) { + tal_append_fmt(&full_fmt, " %s", + invoice_encode(tmpctx, inv->inv)); + if (inv->inv->offer_id) + tal_append_fmt(&full_fmt, " for offer %s", + type_to_string(tmpctx, struct sha256, + inv->inv->offer_id)); + } tal_append_fmt(&full_fmt, ": %s", fmt); msg = tal_vfmt(tmpctx, full_fmt, ap); diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 1092c7c06fb6..f4b131ff6156 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -38,12 +38,15 @@ fail_invreq_level(struct command *cmd, struct tlv_invoice_error *err; u8 *errdata; - full_fmt = tal_fmt(tmpctx, "Failed invoice_request %s", - invrequest_encode(tmpctx, invreq->invreq)); - if (invreq->invreq->offer_id) - tal_append_fmt(&full_fmt, " for offer %s", - type_to_string(tmpctx, struct sha256, - invreq->invreq->offer_id)); + full_fmt = tal_fmt(tmpctx, "Failed invoice_request"); + if (invreq->invreq) { + tal_append_fmt(&full_fmt, " %s", + invrequest_encode(tmpctx, invreq->invreq)); + if (invreq->invreq->offer_id) + tal_append_fmt(&full_fmt, " for offer %s", + type_to_string(tmpctx, struct sha256, + invreq->invreq->offer_id)); + } tal_append_fmt(&full_fmt, ": %s", fmt); msg = tal_vfmt(tmpctx, full_fmt, ap); From fa0c29f9598b0b9e4c8c53356c160e8495933314 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0547/1530] tools/generate_wire.py: tlvs should start with tlv_ No more "towire_offer", but "towire_tlv_offer". This means we double-up on the unfortunately-named `tlv_payload` inside the onion, but we should rename that in the spec when we remove old payloads. Signed-off-by: Rusty Russell --- common/blindedpath.c | 4 +- common/bolt12.c | 12 ++--- common/onion.c | 6 +-- common/test/run-blindedpath_onion.c | 4 +- common/test/run-bolt12_decode.c | 42 ++++++++-------- common/test/run-bolt12_merkle-json.c | 4 +- common/test/run-bolt12_merkle.c | 4 +- common/test/run-bolt12_period.c | 42 ++++++++-------- .../test/run-route_blinding_override_test.c | 2 +- common/test/run-route_blinding_test.c | 2 +- connectd/onion_message.c | 2 +- lightningd/offer.c | 2 +- lightningd/onion_message.c | 2 +- plugins/fetchinvoice.c | 18 +++---- plugins/keysend.c | 2 +- plugins/offers.c | 2 +- plugins/offers_inv_hook.c | 4 +- plugins/offers_invreq_hook.c | 4 +- tools/gen/print_impl_template | 6 +-- tools/generate-wire.py | 4 +- wire/Makefile | 12 ++--- wire/test/run-tlvstream.c | 48 +++++++++---------- 22 files changed, 114 insertions(+), 114 deletions(-) diff --git a/common/blindedpath.c b/common/blindedpath.c index b931cd45ec3d..8c5b257616b3 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -122,7 +122,7 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx, struct pubkey *node_alias) { u8 *encmsg_raw = tal_arr(NULL, u8, 0); - towire_encrypted_data_tlv(&encmsg_raw, encmsg); + towire_tlv_encrypted_data_tlv(&encmsg_raw, encmsg); return enctlv_from_encmsg_raw(ctx, blinding, node, take(encmsg_raw), next_blinding, node_alias); } @@ -198,7 +198,7 @@ static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, * - MUST drop the message. */ encmsg = tlv_encrypted_data_tlv_new(ctx); - if (!fromwire_encrypted_data_tlv(&cursor, &maxlen, encmsg) + if (!fromwire_tlv_encrypted_data_tlv(&cursor, &maxlen, encmsg) || !tlv_fields_valid(encmsg->fields, NULL, NULL)) return tal_free(encmsg); diff --git a/common/bolt12.c b/common/bolt12.c index a57a166277c7..fd4a729d673c 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -155,7 +155,7 @@ char *offer_encode(const tal_t *ctx, const struct tlv_offer *offer_tlv) u8 *wire; wire = tal_arr(tmpctx, u8, 0); - towire_offer(&wire, offer_tlv); + towire_tlv_offer(&wire, offer_tlv); return to_bech32_charset(ctx, "lno", wire); } @@ -174,7 +174,7 @@ struct tlv_offer *offer_decode(const tal_t *ctx, if (!data) return tal_free(offer); - if (!fromwire_offer(&data, &dlen, offer)) { + if (!fromwire_tlv_offer(&data, &dlen, offer)) { *fail = tal_fmt(ctx, "invalid offer data"); return tal_free(offer); } @@ -208,7 +208,7 @@ char *invrequest_encode(const tal_t *ctx, const struct tlv_invoice_request *invr u8 *wire; wire = tal_arr(tmpctx, u8, 0); - towire_invoice_request(&wire, invrequest_tlv); + towire_tlv_invoice_request(&wire, invrequest_tlv); return to_bech32_charset(ctx, "lnr", wire); } @@ -227,7 +227,7 @@ struct tlv_invoice_request *invrequest_decode(const tal_t *ctx, if (!data) return tal_free(invrequest); - if (!fromwire_invoice_request(&data, &dlen, invrequest)) { + if (!fromwire_tlv_invoice_request(&data, &dlen, invrequest)) { *fail = tal_fmt(ctx, "invalid invoice_request data"); return tal_free(invrequest); } @@ -247,7 +247,7 @@ char *invoice_encode(const tal_t *ctx, const struct tlv_invoice *invoice_tlv) u8 *wire; wire = tal_arr(tmpctx, u8, 0); - towire_invoice(&wire, invoice_tlv); + towire_tlv_invoice(&wire, invoice_tlv); return to_bech32_charset(ctx, "lni", wire); } @@ -266,7 +266,7 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx, if (!data) return tal_free(invoice); - if (!fromwire_invoice(&data, &dlen, invoice)) { + if (!fromwire_tlv_invoice(&data, &dlen, invoice)) { *fail = tal_fmt(ctx, "invalid invoice data"); return tal_free(invoice); } diff --git a/common/onion.c b/common/onion.c index 584f67af1f17..e0c36ad8ebab 100644 --- a/common/onion.c +++ b/common/onion.c @@ -22,7 +22,7 @@ static u8 *make_tlv_hop(const tal_t *ctx, /* We can't have over 64k anyway */ u8 *tlvs = tal_arr(ctx, u8, 3); - towire_tlv_payload(&tlvs, tlv); + towire_tlv_tlv_payload(&tlvs, tlv); switch (bigsize_put(tlvs, tal_bytelen(tlvs) - 3)) { case 1: @@ -195,7 +195,7 @@ static struct tlv_tlv_payload *decrypt_tlv(const tal_t *ctx, tlv = tlv_tlv_payload_new(ctx); cursor = dec; max = tal_bytelen(dec); - if (!fromwire_tlv_payload(&cursor, &max, tlv)) + if (!fromwire_tlv_tlv_payload(&cursor, &max, tlv)) return tal_free(tlv); return tlv; @@ -220,7 +220,7 @@ struct onion_payload *onion_decode(const tal_t *ctx, goto general_fail; tlv = tlv_tlv_payload_new(p); - if (!fromwire_tlv_payload(&cursor, &max, tlv)) { + if (!fromwire_tlv_tlv_payload(&cursor, &max, tlv)) { /* FIXME: Fill in correct thing here! */ goto general_fail; } diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c index 4b51804ea175..267ecc2ea5bb 100644 --- a/common/test/run-blindedpath_onion.c +++ b/common/test/run-blindedpath_onion.c @@ -133,7 +133,7 @@ static u8 *next_onion(const tal_t *ctx, u8 *omsg, max = tal_bytelen(rs->raw_payload); maxlen = fromwire_bigsize(&cursor, &max); om = tlv_onionmsg_payload_new(tmpctx); - assert(fromwire_onionmsg_payload(&cursor, &maxlen, om)); + assert(fromwire_tlv_onionmsg_payload(&cursor, &maxlen, om)); if (rs->nextcase == ONION_END) return NULL; @@ -204,7 +204,7 @@ int main(int argc, char *argv[]) = tlv_onionmsg_payload_new(tmpctx); payload->encrypted_data_tlv = enctlv[i]; onionmsg_payload[i] = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&onionmsg_payload[i], payload); + towire_tlv_onionmsg_payload(&onionmsg_payload[i], payload); sphinx_add_modern_hop(sphinx_path, &alias[i], onionmsg_payload[i]); } diff --git a/common/test/run-bolt12_decode.c b/common/test/run-bolt12_decode.c index 68732b035243..ff623e17e801 100644 --- a/common/test/run-bolt12_decode.c +++ b/common/test/run-bolt12_decode.c @@ -56,18 +56,6 @@ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_fail */ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } -/* Generated stub for fromwire_invoice */ -bool fromwire_invoice(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice * record UNNEEDED) -{ fprintf(stderr, "fromwire_invoice called!\n"); abort(); } -/* Generated stub for fromwire_invoice_request */ -bool fromwire_invoice_request(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice_request * record UNNEEDED) -{ fprintf(stderr, "fromwire_invoice_request called!\n"); abort(); } -/* Generated stub for fromwire_offer */ -bool fromwire_offer(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_offer * record UNNEEDED) -{ fprintf(stderr, "fromwire_offer called!\n"); abort(); } /* Generated stub for fromwire_secp256k1_ecdsa_signature */ void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) @@ -79,6 +67,18 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_tlv_invoice */ +bool fromwire_tlv_invoice(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct tlv_invoice * record UNNEEDED) +{ fprintf(stderr, "fromwire_tlv_invoice called!\n"); abort(); } +/* Generated stub for fromwire_tlv_invoice_request */ +bool fromwire_tlv_invoice_request(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct tlv_invoice_request * record UNNEEDED) +{ fprintf(stderr, "fromwire_tlv_invoice_request called!\n"); abort(); } +/* Generated stub for fromwire_tlv_offer */ +bool fromwire_tlv_offer(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct tlv_offer * record UNNEEDED) +{ fprintf(stderr, "fromwire_tlv_offer called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -125,15 +125,6 @@ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } -/* Generated stub for towire_invoice */ -void towire_invoice(u8 **pptr UNNEEDED, const struct tlv_invoice *record UNNEEDED) -{ fprintf(stderr, "towire_invoice called!\n"); abort(); } -/* Generated stub for towire_invoice_request */ -void towire_invoice_request(u8 **pptr UNNEEDED, const struct tlv_invoice_request *record UNNEEDED) -{ fprintf(stderr, "towire_invoice_request called!\n"); abort(); } -/* Generated stub for towire_offer */ -void towire_offer(u8 **pptr UNNEEDED, const struct tlv_offer *record UNNEEDED) -{ fprintf(stderr, "towire_offer called!\n"); abort(); } /* Generated stub for towire_secp256k1_ecdsa_signature */ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, const secp256k1_ecdsa_signature *signature UNNEEDED) @@ -141,6 +132,15 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_tlv_invoice */ +void towire_tlv_invoice(u8 **pptr UNNEEDED, const struct tlv_invoice *record UNNEEDED) +{ fprintf(stderr, "towire_tlv_invoice called!\n"); abort(); } +/* Generated stub for towire_tlv_invoice_request */ +void towire_tlv_invoice_request(u8 **pptr UNNEEDED, const struct tlv_invoice_request *record UNNEEDED) +{ fprintf(stderr, "towire_tlv_invoice_request called!\n"); abort(); } +/* Generated stub for towire_tlv_offer */ +void towire_tlv_offer(u8 **pptr UNNEEDED, const struct tlv_offer *record UNNEEDED) +{ fprintf(stderr, "towire_tlv_offer called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index cc653aa9d81b..82ea1e43156d 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -160,14 +160,14 @@ int main(int argc, char *argv[]) if (streq(tlvtype, "n1")) { struct tlv_n1 *n1 = tlv_n1_new(tmpctx); size_t len = tal_bytelen(tlv); - assert(fromwire_n1(&tlv, &len, n1)); + assert(fromwire_tlv_n1(&tlv, &len, n1)); assert(len == 0); merkle_tlv(n1->fields, &merkle); assert(sha256_eq(&merkle, &expected_merkle)); } else if (streq(tlvtype, "offer")) { struct tlv_offer *offer = tlv_offer_new(tmpctx); size_t len = tal_bytelen(tlv); - assert(fromwire_offer(&tlv, &len, offer)); + assert(fromwire_tlv_offer(&tlv, &len, offer)); assert(len == 0); merkle_tlv(offer->fields, &merkle); assert(sha256_eq(&merkle, &expected_merkle)); diff --git a/common/test/run-bolt12_merkle.c b/common/test/run-bolt12_merkle.c index 83abb27d133a..f6df0f05bcfa 100644 --- a/common/test/run-bolt12_merkle.c +++ b/common/test/run-bolt12_merkle.c @@ -114,11 +114,11 @@ static void merkle_n1(const struct tlv_n1 *n1, struct sha256 *test_m) /* Linearize to populate ->fields */ v = tal_arr(tmpctx, u8, 0); - towire_n1(&v, n1); + towire_tlv_n1(&v, n1); len = tal_bytelen(v); tmp = tlv_n1_new(tmpctx); - if (!fromwire_n1(cast_const2(const u8 **, &v), &len, tmp)) + if (!fromwire_tlv_n1(cast_const2(const u8 **, &v), &len, tmp)) abort(); assert(len == 0); diff --git a/common/test/run-bolt12_period.c b/common/test/run-bolt12_period.c index 933e8c0d70d9..086177a260cc 100644 --- a/common/test/run-bolt12_period.c +++ b/common/test/run-bolt12_period.c @@ -59,18 +59,6 @@ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_fail */ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } -/* Generated stub for fromwire_invoice */ -bool fromwire_invoice(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice * record UNNEEDED) -{ fprintf(stderr, "fromwire_invoice called!\n"); abort(); } -/* Generated stub for fromwire_invoice_request */ -bool fromwire_invoice_request(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice_request * record UNNEEDED) -{ fprintf(stderr, "fromwire_invoice_request called!\n"); abort(); } -/* Generated stub for fromwire_offer */ -bool fromwire_offer(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_offer * record UNNEEDED) -{ fprintf(stderr, "fromwire_offer called!\n"); abort(); } /* Generated stub for fromwire_secp256k1_ecdsa_signature */ void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) @@ -82,6 +70,18 @@ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sh u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_tlv_invoice */ +bool fromwire_tlv_invoice(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct tlv_invoice * record UNNEEDED) +{ fprintf(stderr, "fromwire_tlv_invoice called!\n"); abort(); } +/* Generated stub for fromwire_tlv_invoice_request */ +bool fromwire_tlv_invoice_request(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct tlv_invoice_request * record UNNEEDED) +{ fprintf(stderr, "fromwire_tlv_invoice_request called!\n"); abort(); } +/* Generated stub for fromwire_tlv_offer */ +bool fromwire_tlv_offer(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct tlv_offer * record UNNEEDED) +{ fprintf(stderr, "fromwire_tlv_offer called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } @@ -132,15 +132,6 @@ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) /* Generated stub for towire_bool */ void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) { fprintf(stderr, "towire_bool called!\n"); abort(); } -/* Generated stub for towire_invoice */ -void towire_invoice(u8 **pptr UNNEEDED, const struct tlv_invoice *record UNNEEDED) -{ fprintf(stderr, "towire_invoice called!\n"); abort(); } -/* Generated stub for towire_invoice_request */ -void towire_invoice_request(u8 **pptr UNNEEDED, const struct tlv_invoice_request *record UNNEEDED) -{ fprintf(stderr, "towire_invoice_request called!\n"); abort(); } -/* Generated stub for towire_offer */ -void towire_offer(u8 **pptr UNNEEDED, const struct tlv_offer *record UNNEEDED) -{ fprintf(stderr, "towire_offer called!\n"); abort(); } /* Generated stub for towire_secp256k1_ecdsa_signature */ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, const secp256k1_ecdsa_signature *signature UNNEEDED) @@ -148,6 +139,15 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_tlv_invoice */ +void towire_tlv_invoice(u8 **pptr UNNEEDED, const struct tlv_invoice *record UNNEEDED) +{ fprintf(stderr, "towire_tlv_invoice called!\n"); abort(); } +/* Generated stub for towire_tlv_invoice_request */ +void towire_tlv_invoice_request(u8 **pptr UNNEEDED, const struct tlv_invoice_request *record UNNEEDED) +{ fprintf(stderr, "towire_tlv_invoice_request called!\n"); abort(); } +/* Generated stub for towire_tlv_offer */ +void towire_tlv_offer(u8 **pptr UNNEEDED, const struct tlv_offer *record UNNEEDED) +{ fprintf(stderr, "towire_tlv_offer called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c index bd8af933ffd0..c54af6078c33 100644 --- a/common/test/run-route_blinding_override_test.c +++ b/common/test/run-route_blinding_override_test.c @@ -151,7 +151,7 @@ static u8 *json_to_enctlvs(const tal_t *ctx, } } ret = tal_arr(ctx, u8, 0); - towire_encrypted_data_tlv(&ret, enctlv); + towire_tlv_encrypted_data_tlv(&ret, enctlv); towire_u8_array(&ret, appended, tal_bytelen(appended)); return ret; } diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c index 2250e2ded02e..b798cd0c55b9 100644 --- a/common/test/run-route_blinding_test.c +++ b/common/test/run-route_blinding_test.c @@ -156,7 +156,7 @@ static u8 *json_to_enctlvs(const tal_t *ctx, } } ret = tal_arr(ctx, u8, 0); - towire_encrypted_data_tlv(&ret, enctlv); + towire_tlv_encrypted_data_tlv(&ret, enctlv); towire_u8_array(&ret, appended, tal_bytelen(appended)); return ret; } diff --git a/connectd/onion_message.c b/connectd/onion_message.c index e3fd7fbdc98a..779ab969c7ea 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -102,7 +102,7 @@ void handle_onion_message(struct daemon *daemon, } om = tlv_onionmsg_payload_new(msg); - if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { + if (!fromwire_tlv_onionmsg_payload(&cursor, &maxlen, om)) { status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", tal_hex(tmpctx, rs->raw_payload)); return; diff --git a/lightningd/offer.c b/lightningd/offer.c index e251f3b623f0..511e5a225a4e 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -471,7 +471,7 @@ static struct command_result *json_createinvoicerequest(struct command *cmd, * [Signature Calculation](#signature-calculation) using the `payer_key`. */ /* This populates the ->fields from our entries */ - invreq->fields = tlv_make_fields(invreq, invoice_request); + invreq->fields = tlv_make_fields(invreq, tlv_invoice_request); merkle_tlv(invreq->fields, &merkle); invreq->signature = tal(invreq, struct bip340sig); if (deprecated_apis) diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index aa8d89821383..0316296263e1 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -149,7 +149,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) submsglen = tal_bytelen(submsg); subptr = submsg; payload->om = tlv_onionmsg_payload_new(payload); - if (!fromwire_onionmsg_payload(&subptr, &submsglen, payload->om)) { + if (!fromwire_tlv_onionmsg_payload(&subptr, &submsglen, payload->om)) { log_broken(ld->log, "bad got_onionmsg_tous om: %s", tal_hex(tmpctx, msg)); return; diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 3c267ebe6e90..4e410f83e622 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -120,7 +120,7 @@ static struct command_result *handle_error(struct command *cmd, json_tok_full_len(errtok), json_tok_full(buf, errtok)); json_out_start(details, NULL, '{'); - if (!fromwire_invoice_error(&data, &dlen, err)) { + if (!fromwire_tlv_invoice_error(&data, &dlen, err)) { plugin_log(cmd->plugin, LOG_DBG, "Invalid invoice_error %.*s", json_tok_full_len(errtok), @@ -186,7 +186,7 @@ static struct command_result *handle_invreq_response(struct command *cmd, invbin = json_tok_bin_from_hex(cmd, buf, invtok); len = tal_bytelen(invbin); inv = tlv_invoice_new(cmd); - if (!fromwire_invoice(&invbin, &len, inv)) { + if (!fromwire_tlv_invoice(&invbin, &len, inv)) { badfield = "invoice"; goto badinv; } @@ -719,7 +719,7 @@ send_modern_message(struct command *cmd, json_object_start(req->js, NULL); json_add_pubkey(req->js, "id", &node_alias[i]); tlv = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&tlv, payloads[i]); + towire_tlv_onionmsg_payload(&tlv, payloads[i]); json_add_hex_talarr(req->js, "tlv", tlv); json_object_end(req->js); } @@ -830,7 +830,7 @@ sendinvreq_after_connect(struct command *cmd, struct sent *sent) { u8 *rawinvreq = tal_arr(tmpctx, u8, 0); - towire_invoice_request(&rawinvreq, sent->invreq); + towire_tlv_invoice_request(&rawinvreq, sent->invreq); return send_message(cmd, sent, "invoice_request", rawinvreq, sendonionmsg_done); @@ -1091,11 +1091,11 @@ force_payer_secret(struct command *cmd, /* Linearize populates ->fields */ msg = tal_arr(tmpctx, u8, 0); - towire_invoice_request(&msg, invreq); + towire_tlv_invoice_request(&msg, invreq); p = msg; len = tal_bytelen(msg); sent->invreq = tlv_invoice_request_new(cmd); - if (!fromwire_invoice_request(&p, &len, sent->invreq)) + if (!fromwire_tlv_invoice_request(&p, &len, sent->invreq)) plugin_err(cmd->plugin, "Could not remarshall invreq %s", tal_hex(tmpctx, msg)); @@ -1379,7 +1379,7 @@ sendinvoice_after_connect(struct command *cmd, struct sent *sent) { u8 *rawinv = tal_arr(tmpctx, u8, 0); - towire_invoice(&rawinv, sent->inv); + towire_tlv_invoice(&rawinv, sent->inv); return send_message(cmd, sent, "invoice", rawinv, prepare_inv_timeout); } @@ -1474,11 +1474,11 @@ static struct command_result *listsendpays_done(struct command *cmd, /* Linearize populates ->fields */ msg = tal_arr(tmpctx, u8, 0); - towire_invoice(&msg, sent->inv); + towire_tlv_invoice(&msg, sent->inv); p = msg; len = tal_bytelen(msg); sent->inv = tlv_invoice_new(cmd); - if (!fromwire_invoice(&p, &len, sent->inv)) + if (!fromwire_tlv_invoice(&p, &len, sent->inv)) plugin_err(cmd->plugin, "Could not remarshall %s", tal_hex(tmpctx, msg)); diff --git a/plugins/keysend.c b/plugins/keysend.c index 5f1f80ad896b..90ed23e48aa8 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -357,7 +357,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd, if (s != max) { return htlc_accepted_continue(cmd, NULL); } - if (!fromwire_tlv_payload(&rawpayload, &max, payload)) { + if (!fromwire_tlv_tlv_payload(&rawpayload, &max, payload)) { plugin_log( cmd->plugin, LOG_UNUSUAL, "Malformed TLV payload %.*s", json_tok_full_len(params), diff --git a/plugins/offers.c b/plugins/offers.c index 19b36b517f0b..8e4399e2d635 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -79,7 +79,7 @@ send_onion_reply(struct command *cmd, } } tlv = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&tlv, omp); + towire_tlv_onionmsg_payload(&tlv, omp); json_add_hex_talarr(req->js, "tlv", tlv); json_object_end(req->js); } diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index bfeb5b87f0e0..d998a3f5a47b 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -56,7 +56,7 @@ fail_inv_level(struct command *cmd, /* FIXME: Add suggested_value / erroneous_field! */ errdata = tal_arr(cmd, u8, 0); - towire_invoice_error(&errdata, err); + towire_tlv_invoice_error(&errdata, err); return send_onion_reply(cmd, inv->reply_path, "invoice_error", errdata); } @@ -329,7 +329,7 @@ struct command_result *handle_invoice(struct command *cmd, inv->reply_path = tal_steal(inv, reply_path); inv->inv = tlv_invoice_new(cmd); - if (!fromwire_invoice(&invbin, &len, inv->inv)) { + if (!fromwire_tlv_invoice(&invbin, &len, inv->inv)) { return fail_inv(cmd, inv, "Invalid invoice %s", tal_hex(tmpctx, invbin)); diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index f4b131ff6156..d2192801f6e5 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -62,7 +62,7 @@ fail_invreq_level(struct command *cmd, /* FIXME: Add suggested_value / erroneous_field! */ errdata = tal_arr(cmd, u8, 0); - towire_invoice_error(&errdata, err); + towire_tlv_invoice_error(&errdata, err); return send_onion_reply(cmd, invreq->reply_path, "invoice_error", errdata); } @@ -855,7 +855,7 @@ struct command_result *handle_invoice_request(struct command *cmd, ir->reply_path = tal_steal(ir, reply_path); ir->invreq = tlv_invoice_request_new(cmd); - if (!fromwire_invoice_request(&invreqbin, &len, ir->invreq)) { + if (!fromwire_tlv_invoice_request(&invreqbin, &len, ir->invreq)) { return fail_invreq(cmd, ir, "Invalid invreq %s", tal_hex(tmpctx, invreqbin)); diff --git a/tools/gen/print_impl_template b/tools/gen/print_impl_template index 742d194656d8..9ee825b83a8b 100644 --- a/tools/gen/print_impl_template +++ b/tools/gen/print_impl_template @@ -141,9 +141,9 @@ ${print_fieldset(msg.fields.values(), False, '&cursor', '&plen')} % if bool(tlvs): void print${options.enum_name}_tlv_message(const char *tlv_name, const u8 *msg) { size_t plen = tal_count(msg); - % for tlv_name in tlvs: - if (strcmp(tlv_name, "${tlv_name}") == 0) { - printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_${tlv_name}, ARRAY_SIZE(print_tlvs_${tlv_name})); + % for tlv in tlvs.values(): + if (strcmp(tlv_name, "${tlv.name}") == 0) { + printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_${tlv.name}, ARRAY_SIZE(print_tlvs_${tlv.name})); } % endfor } diff --git a/tools/generate-wire.py b/tools/generate-wire.py index d5498ae67469..0e4ad19c0869 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -401,7 +401,7 @@ def add_if(self, if_token): class Tlv(object): def __init__(self, name): - self.name = name + self.name = 'tlv_' + name self.messages = {} def add_message(self, tokens, comments=[]): @@ -415,7 +415,7 @@ def type_name(self): return 'struct ' + self.struct_name() def struct_name(self): - return "tlv_{}".format(self.name) + return self.name def find_message(self, name): return self.messages[name] diff --git a/wire/Makefile b/wire/Makefile index 44380df67b91..a1c022eebfda 100644 --- a/wire/Makefile +++ b/wire/Makefile @@ -117,14 +117,14 @@ generate-bolt-csv-patch: bolt-precheck # tlvs_n1 and n2 are used for test vectors, thus not referenced: expose them # for testing and to prevent compile error about them being unused. # This will be easier if test vectors are moved to separate files. -wire/peer_wiregen.h_args := --include='common/channel_id.h' --include='bitcoin/tx.h' --include='bitcoin/preimage.h' --include='bitcoin/short_channel_id.h' --include='common/node_id.h' --include='common/bigsize.h' --include='bitcoin/block.h' --include='bitcoin/privkey.h' -s --expose-tlv-type=n1 --expose-tlv-type=n2 +wire/peer_wiregen.h_args := --include='common/channel_id.h' --include='bitcoin/tx.h' --include='bitcoin/preimage.h' --include='bitcoin/short_channel_id.h' --include='common/node_id.h' --include='common/bigsize.h' --include='bitcoin/block.h' --include='bitcoin/privkey.h' -s --expose-tlv-type=tlv_n1 --expose-tlv-type=tlv_n2 -wire/peer_wiregen.c_args := -s --expose-tlv-type=n1 --expose-tlv-type=n2 +wire/peer_wiregen.c_args := -s --expose-tlv-type=tlv_n1 --expose-tlv-type=tlv_n2 # The tlv_payload isn't parsed in a fromwire, so we need to expose it. -wire/onion_wiregen.h_args := --include='bitcoin/short_channel_id.h' --include='bitcoin/privkey.h' --include='common/bigsize.h' --include='common/amount.h' --include='common/node_id.h' --include='bitcoin/block.h' -s --expose-tlv-type=tlv_payload +wire/onion_wiregen.h_args := --include='bitcoin/short_channel_id.h' --include='bitcoin/privkey.h' --include='common/bigsize.h' --include='common/amount.h' --include='common/node_id.h' --include='bitcoin/block.h' -s --expose-tlv-type=tlv_tlv_payload -wire/onion_wiregen.c_args := -s --expose-tlv-type=tlv_payload +wire/onion_wiregen.c_args := -s --expose-tlv-type=tlv_tlv_payload # Same for _exp versions wire/peer_exp_wiregen.h_args := $(wire/peer_wiregen.h_args) --include='wire/channel_type_wiregen.h' @@ -133,13 +133,13 @@ wire/peer_exp_printgen.h_args := --include='wire/channel_type_printgen.h' wire/onion_exp_wiregen.h_args := $(wire/onion_wiregen.h_args) wire/onion_exp_wiregen.c_args := $(wire/onion_wiregen.c_args) -wire/bolt12_wiregen.c_args := -s --expose-tlv-type=blinded_path --expose-tlv-type=invoice_request +wire/bolt12_wiregen.c_args := -s --expose-tlv-type=tlv_blinded_path --expose-tlv-type=tlv_invoice_request wire/bolt12_wiregen.h_args := --include='bitcoin/short_channel_id.h' --include='bitcoin/signature.h' --include='bitcoin/privkey.h' --include='common/bigsize.h' --include='common/amount.h' --include='common/node_id.h' --include='bitcoin/block.h' --include='wire/onion_wire.h' $(wire/bolt12_wiregen.c_args) # Same for _exp versions wire/bolt12_exp_wiregen.h_args := $(wire/bolt12_wiregen.h_args) wire/bolt12_exp_wiregen.c_args := $(wire/bolt12_wiregen.c_args) -wire/peer_wiregen.h_args := --include='common/channel_id.h' --include='bitcoin/tx.h' --include='bitcoin/preimage.h' --include='bitcoin/short_channel_id.h' --include='common/node_id.h' --include='common/bigsize.h' --include='bitcoin/block.h' --include='bitcoin/privkey.h' -s --expose-tlv-type=n1 --expose-tlv-type=n2 +wire/peer_wiregen.h_args := --include='common/channel_id.h' --include='bitcoin/tx.h' --include='bitcoin/preimage.h' --include='bitcoin/short_channel_id.h' --include='common/node_id.h' --include='common/bigsize.h' --include='bitcoin/block.h' --include='bitcoin/privkey.h' -s --expose-tlv-type=tlv_n1 --expose-tlv-type=tlv_n2 wire/channel_type_wiregen.h_args := -s wire/channel_type_wiregen.c_args := $(wire/channel_type_wiregen.h_args) diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index 76b27b61252e..0e1a467361aa 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -467,13 +467,13 @@ int main(int argc, char *argv[]) orig_p = stream(tmpctx, invalid_streams_either[i].hex); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_n1(&p, &max, tlv_n1) && !p) || - !n1_is_valid(tlv_n1, NULL)); + assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + !tlv_n1_is_valid(tlv_n1, NULL)); assert(strstr(invalid_streams_either[i].reason, reason)); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_n2(&p, &max, tlv_n2) && !p) || - !n2_is_valid(tlv_n2, NULL)); + assert((!fromwire_tlv_n2(&p, &max, tlv_n2) && !p) || + !tlv_n2_is_valid(tlv_n2, NULL)); assert(strstr(invalid_streams_either[i].reason, reason)); } @@ -484,8 +484,8 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n1[i].hex); max = tal_count(p); - assert((!fromwire_n1(&p, &max, tlv_n1) && !p) || - !n1_is_valid(tlv_n1, NULL)); + assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + !tlv_n1_is_valid(tlv_n1, NULL)); assert(strstr(invalid_streams_n1[i].reason, reason)); } @@ -496,8 +496,8 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n1_combo[i].hex); max = tal_count(p); - assert((!fromwire_n1(&p, &max, tlv_n1) && !p) || - !n1_is_valid(tlv_n1, NULL)); + assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + !tlv_n1_is_valid(tlv_n1, NULL)); assert(strstr(invalid_streams_n1_combo[i].reason, reason)); } @@ -508,8 +508,8 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n2_combo[i].hex); max = tal_count(p); - assert((!fromwire_n2(&p, &max, tlv_n2) && !p) || - !n2_is_valid(tlv_n2, NULL)); + assert((!fromwire_tlv_n2(&p, &max, tlv_n2) && !p) || + !tlv_n2_is_valid(tlv_n2, NULL)); assert(strstr(invalid_streams_n2_combo[i].reason, reason)); } @@ -523,8 +523,8 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; - assert(fromwire_n1(&p, &max, tlv_n1) && - n1_is_valid(tlv_n1, NULL)); + assert(fromwire_tlv_n1(&p, &max, tlv_n1) && + tlv_n1_is_valid(tlv_n1, NULL)); assert(max == 0); assert(tlv_n1_eq(tlv_n1, &valid_streams[i].expect)); @@ -534,7 +534,7 @@ int main(int argc, char *argv[]) continue; p2 = tal_arr(tmpctx, u8, 0); - towire_n1(&p2, tlv_n1); + towire_tlv_n1(&p2, tlv_n1); assert(memeq(p2, tal_count(p2), orig_p, tal_count(orig_p))); } @@ -555,12 +555,12 @@ int main(int argc, char *argv[]) invalid_streams_either[i].hex); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_n1(&p, &max, tlv_n1) && !p) || - !n1_is_valid(tlv_n1, NULL)); + assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + !tlv_n1_is_valid(tlv_n1, NULL)); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_n2(&p, &max, tlv_n2) && !p) || - !n2_is_valid(tlv_n2, NULL)); + assert((!fromwire_tlv_n2(&p, &max, tlv_n2) && !p) || + !tlv_n2_is_valid(tlv_n2, NULL)); } } @@ -573,8 +573,8 @@ int main(int argc, char *argv[]) p = stream2(tmpctx, valid_streams[j].hex, invalid_streams_n1[i].hex); max = tal_count(p); - assert((!fromwire_n1(&p, &max, tlv_n1) && !p) || - !n1_is_valid(tlv_n1, NULL)); + assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + !tlv_n1_is_valid(tlv_n1, NULL)); } } @@ -587,8 +587,8 @@ int main(int argc, char *argv[]) p = stream2(tmpctx, valid_streams[j].hex, invalid_streams_n1_combo[i].hex); max = tal_count(p); - assert((!fromwire_n1(&p, &max, tlv_n1) && !p) || - !n1_is_valid(tlv_n1, NULL)); + assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + !tlv_n1_is_valid(tlv_n1, NULL)); } } @@ -617,8 +617,8 @@ int main(int argc, char *argv[]) expect_success = pull_type(valid_streams[i].hex) < pull_type(valid_streams[j].hex); - assert(fromwire_n1(&p, &max, tlv_n1) && - n1_is_valid(tlv_n1, NULL) == expect_success); + assert(fromwire_tlv_n1(&p, &max, tlv_n1) && + tlv_n1_is_valid(tlv_n1, NULL) == expect_success); if (!expect_success) continue; @@ -630,7 +630,7 @@ int main(int argc, char *argv[]) continue; u8 *p2 = tal_arr(tmpctx, u8, 0); - towire_n1(&p2, tlv_n1); + towire_tlv_n1(&p2, tlv_n1); assert(memeq(orig_p, tal_count(orig_p), p2, tal_count(p2))); } From 12c7b156c578b7d719a669dbc94527aaf0549cf9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0548/1530] tools/generate_wire.py: allow generated C files to have includes added. We previously ignored --include= for these, but onion is about to start needing bolt12. Signed-off-by: Rusty Russell --- tools/gen/impl_template | 3 +++ tools/gen/print_impl_template | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tools/gen/impl_template b/tools/gen/impl_template index 61f643598f1d..e88a13fdd248 100644 --- a/tools/gen/impl_template +++ b/tools/gen/impl_template @@ -9,6 +9,9 @@ #include #include #include +% for i in includes: +${i} +% endfor #ifndef SUPERVERBOSE #define SUPERVERBOSE(...) diff --git a/tools/gen/print_impl_template b/tools/gen/print_impl_template index 9ee825b83a8b..100e02ac1306 100644 --- a/tools/gen/print_impl_template +++ b/tools/gen/print_impl_template @@ -8,6 +8,9 @@ #include #include #include +% for i in includes: +${i} +% endfor % if enum_sets: void print${options.enum_name}_message(const u8 *msg) From 88de64a5802e56aca9504d60c9e40f7f1d88a63c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0549/1530] tools/generate-wire.py: simplify printwire routines, fix ... handling. We make them return bool, and always use names `cursor` and `plen` in callers, for simplicity. Also, `...` means "loop until finished" not "loop this many bytes". Signed-off-by: Rusty Russell --- devtools/print_wire.c | 5 +- devtools/print_wire.h | 4 +- tools/gen/print_header_template | 8 ++-- tools/gen/print_impl_template | 82 +++++++++++++++++---------------- tools/test/run-test-wire.c | 2 +- 5 files changed, 53 insertions(+), 48 deletions(-) diff --git a/devtools/print_wire.c b/devtools/print_wire.c index 8fbab06d7904..4523ecd7e015 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -180,7 +180,7 @@ find_print_record_type(u64 type, return NULL; } -void printwire_tlvs(const char *fieldname, const u8 **cursor, size_t *plen, +bool printwire_tlvs(const char *fieldname, const u8 **cursor, size_t *plen, const struct tlv_print_record_type types[], size_t num_types) { @@ -212,10 +212,11 @@ void printwire_tlvs(const char *fieldname, const u8 **cursor, size_t *plen, printf("**TYPE #%"PRIu64" UNKNOWN for TLV %s**\n", type, fieldname); *plen -= length; } - return; + return true; fail: printf("**TRUNCATED TLV %s**\n", fieldname); + return false; } #define PRINTWIRE_TYPE_TO_STRING(T, N) \ diff --git a/devtools/print_wire.h b/devtools/print_wire.h index 081cd85d7586..59f054dde732 100644 --- a/devtools/print_wire.h +++ b/devtools/print_wire.h @@ -9,7 +9,7 @@ struct tlv_print_record_type { u64 type; - void (*print)(const char *tlv_name, const u8 **cursor, size_t *plen); + bool (*print)(const char *tlv_name, const u8 **cursor, size_t *plen); }; typedef u64 bigsize; @@ -21,7 +21,7 @@ void printwire_u16(const char *fieldname, const u16 *v); void printwire_u32(const char *fieldname, const u32 *v); void printwire_u64(const char *fieldname, const u64 *v); void printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len); -void printwire_tlvs(const char *tlv_name, const u8 **cursor, size_t *plen, +bool printwire_tlvs(const char *tlv_name, const u8 **cursor, size_t *plen, const struct tlv_print_record_type types[], size_t num_types); void printwire_bitcoin_blkid(const char *fieldname, const struct bitcoin_blkid *bitcoin_blkid); diff --git a/tools/gen/print_header_template b/tools/gen/print_header_template index dc01e9c47d2f..f8c4bb3388af 100644 --- a/tools/gen/print_header_template +++ b/tools/gen/print_header_template @@ -9,18 +9,18 @@ ${i} % endfor -void print${options.enum_name}_message(const u8 *msg); +bool print${options.enum_name}_message(const u8 *msg); -void print${options.enum_name}_tlv_message(const char *tlv_name, const u8 *msg); +bool print${options.enum_name}_tlv_message(const char *tlv_name, const u8 *msg); % for msg in messages: -void printwire_${msg.name}(const char *fieldname, const u8 *cursor); +bool printwire_${msg.name}(const char *fieldname, const u8 *cursor); % endfor % if options.expose_subtypes: % for subtype in subtypes: -void printwire_${subtype.name}(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_${subtype.name}(const char *fieldname, const u8 **cursor, size_t *plen); % endfor % endif #endif /* LIGHTNING_${idem} */ diff --git a/tools/gen/print_impl_template b/tools/gen/print_impl_template index 100e02ac1306..996d44b1cab2 100644 --- a/tools/gen/print_impl_template +++ b/tools/gen/print_impl_template @@ -13,87 +13,85 @@ ${i} % endfor % if enum_sets: -void print${options.enum_name}_message(const u8 *msg) +bool print${options.enum_name}_message(const u8 *msg) { switch ((enum ${options.enum_name})fromwire_peektype(msg)) { % for msg in enum_sets[0]['set']: case ${msg.enum_name()}: printf("${msg.enum_name()}:\n"); - printwire_${msg.name}("${msg.name}", msg); - return; + return printwire_${msg.name}("${msg.name}", msg); % endfor } printf("UNKNOWN: %s\\n", tal_hex(msg, msg)); + return false; } % endif ## 'component' for 'truncate check -<%def name="truncate_check(nested=False)"> - if (!${ '*' if nested else '' }cursor) { +<%def name="truncate_check()"> + if (!*cursor) { printf("**TRUNCATED**\n"); - return; + return false; } \ ## definition for printing field sets -<%def name="print_fieldset(fields, nested, cursor, plen)"> +<%def name="print_fieldset(fields)"> % for f in fields: % if f.is_extension(): - if (plen <= 0) - return; + if (*plen == 0) + return true; printf("(${','.join(f.extension_names)}):"); % endif % if f.len_field_of: - ${f.type_obj.type_name()} ${f.name} = fromwire_${f.type_obj.name}(${cursor}, ${plen});${truncate_check(nested)} <% continue %> \ + ${f.type_obj.type_name()} ${f.name} = fromwire_${f.type_obj.name}(cursor, plen);${truncate_check()} <% continue %> \ % endif printf("${f.name}="); % if f.type_obj.is_tlv(): - printwire_tlvs(tal_fmt(NULL, "%s.${f.name}", fieldname), ${cursor}, ${plen}, print_tlvs_${f.type_obj.tlv.name}, ARRAY_SIZE(print_tlvs_${f.type_obj.tlv.name})); + printwire_tlvs(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen, print_tlvs_${f.type_obj.tlv.name}, ARRAY_SIZE(print_tlvs_${f.type_obj.tlv.name})); % elif f.is_array() or f.is_varlen(): % if f.type_obj.has_array_helper(): - printwire_${f.type_obj.name}_array(tal_fmt(NULL, "%s.${f.name}", fieldname), ${cursor}, ${plen}, ${f.size('*' + plen)}); + printwire_${f.type_obj.name}_array(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen, ${f.size('*plen')}); % else: printf("["); % if f.is_implicit_len(): - for (size_t i = 0; i < *${plen}; i++) { + while (*plen) { % else: for (size_t i = 0; i < ${f.size()}; i++) { % endif + printf("{\n"); % if f.type_obj.is_subtype() or f.type_obj.is_varsize(): printf("{\n"); - printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), ${cursor}, ${plen}); + printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen); printf("}\n"); % else: ${f.type_obj.type_name()} v; % if f.type_obj.is_assignable(): - v = fromwire_${f.type_obj.name}(${cursor}, ${plen}); + v = fromwire_${f.type_obj.name}(cursor, plen); % else: - fromwire_${f.type_obj.name}(${cursor}, ${plen}, &v); + fromwire_${f.type_obj.name}(cursor, plen, &v); % endif - if (!*cursor) { - printf("**TRUNCATED**\n"); - return; - } + ${truncate_check()} <% typename = f.type_obj.name if not f.type_obj.is_truncated() else f.type_obj.name[1:] %>\ printwire_${typename}(tal_fmt(NULL, "%s.${f.name}", fieldname), &v); % endif } printf("]"); % endif -${truncate_check(nested)} \ +${truncate_check()} \ % elif f.type_obj.is_subtype() or f.type_obj.is_varsize(): printf("{\n"); - printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), ${cursor}, ${plen}); + printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen); printf("}\n"); % else: % if f.type_obj.is_assignable(): - ${f.type_obj.type_name()} ${f.name} = fromwire_${f.type_obj.name}(${cursor}, ${plen}); + ${f.type_obj.type_name()} ${f.name} = fromwire_${f.type_obj.name}(cursor, plen); % else: ${f.type_obj.type_name()} ${f.name}; - fromwire_${f.type_obj.name}(${cursor}, ${plen}, &${f.name}); + fromwire_${f.type_obj.name}(cursor, plen, &${f.name}); % endif <% typename = f.type_obj.name if not f.type_obj.is_truncated() else f.type_obj.name[1:] %> - printwire_${typename}(tal_fmt(NULL, "%s.${f.name}", fieldname), &${f.name}); ${truncate_check(nested)} \ + printwire_${typename}(tal_fmt(NULL, "%s.${f.name}", fieldname), &${f.name}); ${truncate_check()} \ % endif % endfor \ @@ -103,18 +101,20 @@ ${truncate_check(nested)} \ <% static = '' if options.expose_subtypes else 'static ' %>\ -${static}void printwire_${subtype.name}(const char *fieldname, const u8 **cursor, size_t *plen) +${static}bool printwire_${subtype.name}(const char *fieldname, const u8 **cursor, size_t *plen) { - ${print_fieldset(subtype.fields.values(), True, 'cursor', 'plen')} + ${print_fieldset(subtype.fields.values())} + return true; } % endfor % for tlv in tlvs.values(): % for msg in tlv.messages.values(): -static void printwire_${msg.struct_name()}(const char *fieldname, const u8 **cursor, size_t *plen) +static bool printwire_${msg.struct_name()}(const char *fieldname, const u8 **cursor, size_t *plen) { printf("(msg_name=%s)\n", "${msg.name}"); - ${print_fieldset(msg.fields.values(), True, 'cursor', 'plen')} + ${print_fieldset(msg.fields.values())} + return true; } % endfor @@ -125,29 +125,33 @@ static const struct tlv_print_record_type print_tlvs_${tlv.name}[] = { }; % endfor % for msg in messages: -void printwire_${msg.name}(const char *fieldname, const u8 *cursor) +bool printwire_${msg.name}(const char *fieldname, const u8 *msg) { + size_t msglen = tal_count(msg); + const u8 **cursor = &msg; + size_t *plen = &msglen; - size_t plen = tal_count(cursor); - if (fromwire_u16(&cursor, &plen) != ${msg.enum_name()}) { + if (fromwire_u16(cursor, plen) != ${msg.enum_name()}) { printf("WRONG TYPE?!\n"); - return; + return false; } -${print_fieldset(msg.fields.values(), False, '&cursor', '&plen')} +${print_fieldset(msg.fields.values())} ## Length check - if (plen != 0) - printf("EXTRA: %s\n", tal_hexstr(NULL, cursor, plen)); + if (*plen != 0) + printf("EXTRA: %s\n", tal_hexstr(NULL, *cursor, *plen)); + return *cursor != NULL; } % endfor % if bool(tlvs): -void print${options.enum_name}_tlv_message(const char *tlv_name, const u8 *msg) { - size_t plen = tal_count(msg); +bool print${options.enum_name}_tlv_message(const char *tlv_name, const u8 *msg) { + size_t len = tal_count(msg); % for tlv in tlvs.values(): if (strcmp(tlv_name, "${tlv.name}") == 0) { - printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_${tlv.name}, ARRAY_SIZE(print_tlvs_${tlv.name})); + return printwire_tlvs(tlv_name, &msg, &len, print_tlvs_${tlv.name}, ARRAY_SIZE(print_tlvs_${tlv.name})); } % endfor + return false; } % endif diff --git a/tools/test/run-test-wire.c b/tools/test/run-test-wire.c index 28ae95df0085..1898d8d93c99 100644 --- a/tools/test/run-test-wire.c +++ b/tools/test/run-test-wire.c @@ -14,7 +14,7 @@ int fromwire_peektype(const u8 *cursor UNNEEDED) void printwire_amount_msat(const char *fieldname UNNEEDED, const struct amount_msat *msat UNNEEDED) { fprintf(stderr, "printwire_amount_msat called!\n"); abort(); } /* Generated stub for printwire_tlvs */ -void printwire_tlvs(const char *tlv_name UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, +bool printwire_tlvs(const char *tlv_name UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, const struct tlv_print_record_type types[] UNNEEDED, size_t num_types UNNEEDED) { fprintf(stderr, "printwire_tlvs called!\n"); abort(); } /* Generated stub for printwire_u16 */ From 8f390027155325dd0043d6430868e8f05a4ed314 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0550/1530] tools/generate-wire.py: have printwire routines do demarshal. This works better in general: let printwire_x do the work of figuring out how to demarshal x. This is particularly important for TLVs, which require a call to tlv_x_new() first. Signed-off-by: Rusty Russell --- devtools/print_wire.c | 180 +++++++++++++++++++++++++--------- devtools/print_wire.h | 39 ++++---- tools/gen/print_impl_template | 33 +------ tools/test/enum.c | 8 +- tools/test/enum.h | 3 +- tools/test/run-test-wire.c | 17 +++- 6 files changed, 178 insertions(+), 102 deletions(-) diff --git a/devtools/print_wire.c b/devtools/print_wire.c index 4523ecd7e015..482a1afe097f 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -7,29 +7,90 @@ #include #include -void printwire_u8(const char *fieldname, const u8 *v) +bool printwire_u8(const char *fieldname, const u8 **cursor, size_t *plen) { - printf("%u\n", *v); + u8 v = fromwire_u8(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED u8 %s**\n", fieldname); + return false; + } + printf("%u\n", v); + return true; } -void printwire_u16(const char *fieldname, const u16 *v) +bool printwire_u16(const char *fieldname, const u8 **cursor, size_t *plen) { - printf("%u\n", *v); + u16 v = fromwire_u16(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED u16 %s**\n", fieldname); + return false; + } + printf("%u\n", v); + return true; } -void printwire_u32(const char *fieldname, const u32 *v) +bool printwire_u32(const char *fieldname, const u8 **cursor, size_t *plen) { - printf("%u\n", *v); + u32 v = fromwire_u32(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED u32 %s**\n", fieldname); + return false; + } + printf("%u\n", v); + return true; } -void printwire_u64(const char *fieldname, const u64 *v) +bool printwire_u64(const char *fieldname, const u8 **cursor, size_t *plen) { - printf("%"PRIu64"\n", *v); + u64 v = fromwire_u64(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED u64 %s**\n", fieldname); + return false; + } + printf("%"PRIu64"\n", v); + return true; } -void printwire_wireaddr(const char *fieldname, const struct wireaddr *wireaddr) +bool printwire_tu16(const char *fieldname, const u8 **cursor, size_t *plen) { - printf("%s\n", fmt_wireaddr(tmpctx, wireaddr)); + u16 v = fromwire_tu16(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED tu16 %s**\n", fieldname); + return false; + } + printf("%u\n", v); + return true; +} + +bool printwire_tu32(const char *fieldname, const u8 **cursor, size_t *plen) +{ + u32 v = fromwire_tu32(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED tu32 %s**\n", fieldname); + return false; + } + printf("%u\n", v); + return true; +} + +bool printwire_tu64(const char *fieldname, const u8 **cursor, size_t *plen) +{ + u64 v = fromwire_tu64(cursor, plen); + if (!*cursor) { + printf("**TRUNCATED tu64 %s**\n", fieldname); + return false; + } + printf("%"PRIu64"\n", v); + return true; +} + +bool printwire_wireaddr(const char *fieldname, const u8 **cursor, size_t *plen) +{ + struct wireaddr w; + if (!fromwire_wireaddr(cursor, plen, &w)) + return false; + printf("%s\n", fmt_wireaddr(tmpctx, &w)); + return true; } /* Returns false if we ran out of data. */ @@ -37,15 +98,17 @@ static bool print_hexstring(const u8 **cursor, size_t *plen, size_t len) { while (len) { u8 v = fromwire_u8(cursor, plen); - if (!*cursor) + if (!*cursor) { + printf("**TRUNCATED**\n"); return false; + } printf("%02x", v); len--; } return true; } -static void printwire_alias(const u8 **cursor, size_t *plen, size_t len) +static bool printwire_alias(const u8 **cursor, size_t *plen, size_t len) { struct utf8_state utf8 = UTF8_STATE_INIT; const char *p = (const char *)*cursor; @@ -83,11 +146,12 @@ static void printwire_alias(const u8 **cursor, size_t *plen, size_t len) hexdump: if (!print_hexstring(cursor, plen, len)) - return; + return false; printf(" ]\n"); + return true; } -static void printwire_addresses(const u8 **cursor, size_t *plen, size_t len) +static bool printwire_addresses(const u8 **cursor, size_t *plen, size_t len) { struct wireaddr addr; size_t to_go = len; @@ -99,23 +163,24 @@ static void printwire_addresses(const u8 **cursor, size_t *plen, size_t len) printf(" %s", fmt_wireaddr(NULL, &addr)); } if (!*cursor) - return; + return false; if (to_go) { printf(" UNKNOWN:"); if (!print_hexstring(cursor, plen, len)) - return; + return false; } printf(" ]\n"); + return true; } -static void printwire_encoded_short_ids(const u8 **cursor, size_t *plen, size_t len) +static bool printwire_encoded_short_ids(const u8 **cursor, size_t *plen, size_t len) { struct short_channel_id *scids; u8 *arr = fromwire_tal_arrn(tmpctx, cursor, plen, len); if (!arr) - return; + return false; printf("["); scids = decode_short_ids(tmpctx, arr); @@ -139,34 +204,32 @@ static void printwire_encoded_short_ids(const u8 **cursor, size_t *plen, size_t || arr[0] == ARR_UNCOMPRESSED || arr[0] == ARR_ZLIB) { printf(" **CORRUPT**"); - return; + return true; } else { printf(" UNKNOWN:"); print_hexstring(cursor, plen, len); } } printf(" ]\n"); + return true; } -void printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len) +bool printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len) { - if (streq(fieldname, "node_announcement.alias")) { - printwire_alias(cursor, plen, len); - return; - } - if (streq(fieldname, "node_announcement.addresses")) { - printwire_addresses(cursor, plen, len); - return; - } - if (strends(fieldname, ".encoded_short_ids")) { - printwire_encoded_short_ids(cursor, plen, len); - return; - } + if (streq(fieldname, "node_announcement.alias")) + return printwire_alias(cursor, plen, len); + + if (streq(fieldname, "node_announcement.addresses")) + return printwire_addresses(cursor, plen, len); + + if (strends(fieldname, ".encoded_short_ids")) + return printwire_encoded_short_ids(cursor, plen, len); printf("["); if (!print_hexstring(cursor, plen, len)) - return; + return false; printf("]\n"); + return true; } static const struct tlv_print_record_type * @@ -220,25 +283,48 @@ bool printwire_tlvs(const char *fieldname, const u8 **cursor, size_t *plen, } #define PRINTWIRE_TYPE_TO_STRING(T, N) \ - void printwire_##N(const char *fieldname, const T *v) \ + bool printwire_##N(const char *fieldname, const u8 **cursor, \ + size_t *plen) \ + { \ + T v; \ + fromwire_##N(cursor, plen, &v); \ + if (!*cursor) { \ + printf("**TRUNCATED " stringify(N) "\n"); \ + return false; \ + } \ + const char *s = type_to_string(NULL, T, &v); \ + printf("%s\n", s); \ + tal_free(s); \ + return true; \ + } + +#define PRINTWIRE_ASSIGNABLE_STRUCT_TO_STRING(N) \ + bool printwire_##N(const char *fieldname, const u8 **cursor, \ + size_t *plen) \ { \ - const char *s = type_to_string(NULL, T, v); \ + struct N v = fromwire_##N(cursor, plen); \ + if (!*cursor) { \ + printf("**TRUNCATED " stringify(N) "\n"); \ + return false; \ + } \ + const char *s = type_to_string(NULL, struct N, &v); \ printf("%s\n", s); \ tal_free(s); \ + return true; \ } #define PRINTWIRE_STRUCT_TYPE_TO_STRING(T) \ PRINTWIRE_TYPE_TO_STRING(struct T, T) -PRINTWIRE_STRUCT_TYPE_TO_STRING(bitcoin_blkid); -PRINTWIRE_STRUCT_TYPE_TO_STRING(bitcoin_txid); -PRINTWIRE_STRUCT_TYPE_TO_STRING(channel_id); -PRINTWIRE_STRUCT_TYPE_TO_STRING(node_id); -PRINTWIRE_STRUCT_TYPE_TO_STRING(preimage); -PRINTWIRE_STRUCT_TYPE_TO_STRING(pubkey); -PRINTWIRE_STRUCT_TYPE_TO_STRING(sha256); -PRINTWIRE_STRUCT_TYPE_TO_STRING(secret); -PRINTWIRE_STRUCT_TYPE_TO_STRING(short_channel_id); -PRINTWIRE_STRUCT_TYPE_TO_STRING(amount_sat); -PRINTWIRE_STRUCT_TYPE_TO_STRING(amount_msat); -PRINTWIRE_TYPE_TO_STRING(secp256k1_ecdsa_signature, secp256k1_ecdsa_signature); +PRINTWIRE_STRUCT_TYPE_TO_STRING(bitcoin_blkid) +PRINTWIRE_STRUCT_TYPE_TO_STRING(bitcoin_txid) +PRINTWIRE_STRUCT_TYPE_TO_STRING(channel_id) +PRINTWIRE_STRUCT_TYPE_TO_STRING(node_id) +PRINTWIRE_STRUCT_TYPE_TO_STRING(preimage) +PRINTWIRE_STRUCT_TYPE_TO_STRING(pubkey) +PRINTWIRE_STRUCT_TYPE_TO_STRING(sha256) +PRINTWIRE_STRUCT_TYPE_TO_STRING(secret) +PRINTWIRE_STRUCT_TYPE_TO_STRING(short_channel_id) +PRINTWIRE_ASSIGNABLE_STRUCT_TO_STRING(amount_sat) +PRINTWIRE_ASSIGNABLE_STRUCT_TO_STRING(amount_msat) +PRINTWIRE_TYPE_TO_STRING(secp256k1_ecdsa_signature, secp256k1_ecdsa_signature) diff --git a/devtools/print_wire.h b/devtools/print_wire.h index 59f054dde732..060ef8a29743 100644 --- a/devtools/print_wire.h +++ b/devtools/print_wire.h @@ -16,26 +16,29 @@ typedef u64 bigsize; #define printwire_bigsize printwire_u64 struct wireaddr; -void printwire_u8(const char *fieldname, const u8 *v); -void printwire_u16(const char *fieldname, const u16 *v); -void printwire_u32(const char *fieldname, const u32 *v); -void printwire_u64(const char *fieldname, const u64 *v); -void printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len); +bool printwire_u8(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_u16(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_u32(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_u64(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_tu16(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_tu32(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_tu64(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len); bool printwire_tlvs(const char *tlv_name, const u8 **cursor, size_t *plen, const struct tlv_print_record_type types[], size_t num_types); -void printwire_bitcoin_blkid(const char *fieldname, const struct bitcoin_blkid *bitcoin_blkid); -void printwire_wireaddr(const char *fieldname, const struct wireaddr *wireaddr); -void printwire_bitcoin_txid(const char *fieldname, const struct bitcoin_txid *bitcoin_txid); -void printwire_channel_id(const char *fieldname, const struct channel_id *channel_id); -void printwire_amount_sat(const char *fieldname, const struct amount_sat *sat); -void printwire_amount_msat(const char *fieldname, const struct amount_msat *msat); -void printwire_preimage(const char *fieldname, const struct preimage *preimage); -void printwire_pubkey(const char *fieldname, const struct pubkey *pubkey); -void printwire_node_id(const char *fieldname, const struct node_id *id); -void printwire_secp256k1_ecdsa_signature(const char *fieldname, const secp256k1_ecdsa_signature *); -void printwire_sha256(const char *fieldname, const struct sha256 *sha256); -void printwire_secret(const char *fieldname, const struct secret *secret); -void printwire_short_channel_id(const char *fieldname, const struct short_channel_id *short_channel_id); +bool printwire_bitcoin_blkid(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_wireaddr(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_bitcoin_txid(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_channel_id(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_amount_sat(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_amount_msat(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_preimage(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_pubkey(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_node_id(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_secp256k1_ecdsa_signature(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_sha256(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_secret(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_short_channel_id(const char *fieldname, const u8 **cursor, size_t *plen); #endif /* LIGHTNING_DEVTOOLS_PRINT_WIRE_H */ diff --git a/tools/gen/print_impl_template b/tools/gen/print_impl_template index 996d44b1cab2..8b4d24653259 100644 --- a/tools/gen/print_impl_template +++ b/tools/gen/print_impl_template @@ -59,39 +59,14 @@ bool print${options.enum_name}_message(const u8 *msg) % else: for (size_t i = 0; i < ${f.size()}; i++) { % endif - printf("{\n"); - % if f.type_obj.is_subtype() or f.type_obj.is_varsize(): - printf("{\n"); - printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen); - printf("}\n"); - % else: - ${f.type_obj.type_name()} v; - % if f.type_obj.is_assignable(): - v = fromwire_${f.type_obj.name}(cursor, plen); - % else: - fromwire_${f.type_obj.name}(cursor, plen, &v); - % endif - ${truncate_check()} -<% typename = f.type_obj.name if not f.type_obj.is_truncated() else f.type_obj.name[1:] %>\ - printwire_${typename}(tal_fmt(NULL, "%s.${f.name}", fieldname), &v); - % endif + if (!printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen)) + return false; } printf("]"); % endif -${truncate_check()} \ - % elif f.type_obj.is_subtype() or f.type_obj.is_varsize(): - printf("{\n"); - printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen); - printf("}\n"); % else: - % if f.type_obj.is_assignable(): - ${f.type_obj.type_name()} ${f.name} = fromwire_${f.type_obj.name}(cursor, plen); - % else: - ${f.type_obj.type_name()} ${f.name}; - fromwire_${f.type_obj.name}(cursor, plen, &${f.name}); - % endif - <% typename = f.type_obj.name if not f.type_obj.is_truncated() else f.type_obj.name[1:] %> - printwire_${typename}(tal_fmt(NULL, "%s.${f.name}", fieldname), &${f.name}); ${truncate_check()} \ + if (!printwire_${f.type_obj.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen)) + return false; % endif % endfor \ diff --git a/tools/test/enum.c b/tools/test/enum.c index b8ad28ee5b73..3d1c501803ee 100644 --- a/tools/test/enum.c +++ b/tools/test/enum.c @@ -13,7 +13,11 @@ enum test_enum fromwire_test_enum(const u8 **cursor, size_t *max) return TEST_ONE; } -void printwire_test_enum(const char *fieldname, const enum test_enum *test_enum) +bool printwire_test_enum(const char *fieldname, const u8 **cursor, size_t *max) { - printf("%u\n", *test_enum); + enum test_enum e = fromwire_test_enum(cursor, max); + if (!*cursor) + return false; + printf("%u\n", e); + return true; } diff --git a/tools/test/enum.h b/tools/test/enum.h index 702105e94531..bc75e721109c 100644 --- a/tools/test/enum.h +++ b/tools/test/enum.h @@ -2,6 +2,7 @@ #define LIGHTNING_TOOLS_TEST_ENUM_H #include "config.h" #include +#include #include #define TEST_IFDEF 0 @@ -13,6 +14,6 @@ enum test_enum { void towire_test_enum(u8 **pptr, const enum test_enum test_enum); enum test_enum fromwire_test_enum(const u8 **cursor, size_t *max); -void printwire_test_enum(const char *fieldname, const enum test_enum *test_enum); +bool printwire_test_enum(const char *fieldname, const u8 **cursor, size_t *max); #endif /* LIGHTNING_TOOLS_TEST_ENUM_H */ diff --git a/tools/test/run-test-wire.c b/tools/test/run-test-wire.c index 1898d8d93c99..b409715e14a0 100644 --- a/tools/test/run-test-wire.c +++ b/tools/test/run-test-wire.c @@ -10,24 +10,31 @@ /* Generated stub for fromwire_peektype */ int fromwire_peektype(const u8 *cursor UNNEEDED) { fprintf(stderr, "fromwire_peektype called!\n"); abort(); } +/* Could not find declaration for fromwire_test_enum */ /* Generated stub for printwire_amount_msat */ -void printwire_amount_msat(const char *fieldname UNNEEDED, const struct amount_msat *msat UNNEEDED) +bool printwire_amount_msat(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "printwire_amount_msat called!\n"); abort(); } /* Generated stub for printwire_tlvs */ bool printwire_tlvs(const char *tlv_name UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, const struct tlv_print_record_type types[] UNNEEDED, size_t num_types UNNEEDED) { fprintf(stderr, "printwire_tlvs called!\n"); abort(); } +/* Generated stub for printwire_tu32 */ +bool printwire_tu32(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) +{ fprintf(stderr, "printwire_tu32 called!\n"); abort(); } +/* Generated stub for printwire_tu64 */ +bool printwire_tu64(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) +{ fprintf(stderr, "printwire_tu64 called!\n"); abort(); } /* Generated stub for printwire_u16 */ -void printwire_u16(const char *fieldname UNNEEDED, const u16 *v UNNEEDED) +bool printwire_u16(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "printwire_u16 called!\n"); abort(); } /* Generated stub for printwire_u32 */ -void printwire_u32(const char *fieldname UNNEEDED, const u32 *v UNNEEDED) +bool printwire_u32(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "printwire_u32 called!\n"); abort(); } /* Generated stub for printwire_u64 */ -void printwire_u64(const char *fieldname UNNEEDED, const u64 *v UNNEEDED) +bool printwire_u64(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "printwire_u64 called!\n"); abort(); } /* Generated stub for printwire_u8_array */ -void printwire_u8_array(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, size_t len UNNEEDED) +bool printwire_u8_array(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "printwire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ From b3a2666a3efaee925e86ec27972041916def3d95 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0551/1530] decodemsg: if decode fails, status 1. Signed-off-by: Rusty Russell --- devtools/decodemsg.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/devtools/decodemsg.c b/devtools/decodemsg.c index fc943999818e..1824c7f7edc3 100644 --- a/devtools/decodemsg.c +++ b/devtools/decodemsg.c @@ -16,6 +16,8 @@ int main(int argc, char *argv[]) const u8 *m; bool onion = false; char *tlv_name = NULL; + bool ok = true; + setup_locale(); opt_register_noarg("--onion", opt_set_bool, &onion, @@ -40,13 +42,13 @@ int main(int argc, char *argv[]) if (onion) if (tlv_name) - printonion_wire_tlv_message(tlv_name, m); + ok &= printonion_wire_tlv_message(tlv_name, m); else - printonion_wire_message(m); + ok &= printonion_wire_message(m); else if (tlv_name) - printpeer_wire_tlv_message(tlv_name, m); + ok &= printpeer_wire_tlv_message(tlv_name, m); else - printpeer_wire_message(m); + ok &= printpeer_wire_message(m); } else { u8 *f = grab_fd(NULL, STDIN_FILENO); size_t off = 0; @@ -56,28 +58,31 @@ int main(int argc, char *argv[]) if (off + sizeof(len) > tal_count(f)) { warnx("Truncated file"); + ok = false; break; } memcpy(&len, f + off, sizeof(len)); off += sizeof(len); if (off + be16_to_cpu(len) > tal_count(f)) { warnx("Truncated file"); + ok = false; break; } m = tal_dup_arr(f, u8, f + off, be16_to_cpu(len), 0); if (onion) if (tlv_name) - printonion_wire_tlv_message(tlv_name, m); + ok &= printonion_wire_tlv_message(tlv_name, m); else - printonion_wire_message(m); + ok &= printonion_wire_message(m); else if (tlv_name) - printpeer_wire_tlv_message(tlv_name, m); + ok &= printpeer_wire_tlv_message(tlv_name, m); else - printpeer_wire_message(m); + ok &= printpeer_wire_message(m); off += be16_to_cpu(len); tal_free(m); } } printf("\n"); - return 0; + + return ok ? 0 : 1; } From 04a152fbcf66f39a0a54e949befcfeaa8df91360 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0552/1530] tools/generate-wire.py: make (and expose) individual TLV print functions. When we actually put bolt12 fields (.e.g tlv_invoice) in onion messages, that code will try to call printwire_tlv_invoice(), so expose it. Signed-off-by: Rusty Russell --- tools/gen/print_header_template | 4 ++++ tools/gen/print_impl_template | 12 +++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/gen/print_header_template b/tools/gen/print_header_template index f8c4bb3388af..ba09bc127e11 100644 --- a/tools/gen/print_header_template +++ b/tools/gen/print_header_template @@ -18,6 +18,10 @@ bool printwire_${msg.name}(const char *fieldname, const u8 *cursor); % endfor +% for tlv in tlvs.values(): +bool printwire_${tlv.name}(const char *fieldname, const u8 **cursor, size_t *plen); +% endfor + % if options.expose_subtypes: % for subtype in subtypes: bool printwire_${subtype.name}(const char *fieldname, const u8 **cursor, size_t *plen); diff --git a/tools/gen/print_impl_template b/tools/gen/print_impl_template index 8b4d24653259..a38bda40ae73 100644 --- a/tools/gen/print_impl_template +++ b/tools/gen/print_impl_template @@ -119,13 +119,19 @@ ${print_fieldset(msg.fields.values())} } % endfor +% for tlv in tlvs.values(): +bool printwire_${tlv.name}(const char *fieldname, const u8 **cursor, size_t *plen) +{ + return printwire_tlvs(fieldname, cursor, plen, print_tlvs_${tlv.name}, ARRAY_SIZE(print_tlvs_${tlv.name})); +} +% endfor + % if bool(tlvs): bool print${options.enum_name}_tlv_message(const char *tlv_name, const u8 *msg) { size_t len = tal_count(msg); % for tlv in tlvs.values(): - if (strcmp(tlv_name, "${tlv.name}") == 0) { - return printwire_tlvs(tlv_name, &msg, &len, print_tlvs_${tlv.name}, ARRAY_SIZE(print_tlvs_${tlv.name})); - } + if (strcmp(tlv_name, "${tlv.name}") == 0) + return printwire_${tlv.name}(tlv_name, &msg, &len); % endfor return false; } From d15d629b8bbbf3e36fc69f75991de18cc6a6c93e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0553/1530] plugins/fetchinvoice: remove obsolete string-based API. Generate the payload in the callers. Signed-off-by: Rusty Russell --- plugins/fetchinvoice.c | 38 ++++++++++++++---------------------- plugins/offers.c | 20 +++++++------------ plugins/offers.h | 3 +-- plugins/offers_inv_hook.c | 9 +++++---- plugins/offers_invreq_hook.c | 15 ++++++++------ 5 files changed, 37 insertions(+), 48 deletions(-) diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 4e410f83e622..67edb3121629 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -639,8 +639,7 @@ static struct pubkey *path_to_node(const tal_t *ctx, /* Marshal arguments for sending onion messages */ struct sending { struct sent *sent; - const char *msgfield; - const u8 *msgval; + struct tlv_onionmsg_payload *payload; struct command_result *(*done)(struct command *cmd, const char *buf UNUSED, const jsmntok_t *result UNUSED, @@ -684,7 +683,7 @@ send_modern_message(struct command *cmd, &node_alias[i]); } /* Final payload contains the actual data. */ - payloads[nhops-1] = tlv_onionmsg_payload_new(payloads); + payloads[nhops-1] = sending->payload; /* We don't include enctlv in final, but it gives us final alias */ if (!create_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], @@ -696,15 +695,6 @@ send_modern_message(struct command *cmd, "Could create final enctlv"); } - /* FIXME: This interface is a string for sendobsonionmessage! */ - if (streq(sending->msgfield, "invoice_request")) { - payloads[nhops-1]->invoice_request - = cast_const(u8 *, sending->msgval); - } else { - assert(streq(sending->msgfield, "invoice")); - payloads[nhops-1]->invoice - = cast_const(u8 *, sending->msgval); - } payloads[nhops-1]->reply_path = reply_path; req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", @@ -779,8 +769,7 @@ static struct command_result *make_reply_path(struct command *cmd, static struct command_result *send_message(struct command *cmd, struct sent *sent, - const char *msgfield TAKES, - const u8 *msgval TAKES, + struct tlv_onionmsg_payload *payload STEALS, struct command_result *(*done) (struct command *cmd, const char *buf UNUSED, @@ -789,8 +778,7 @@ static struct command_result *send_message(struct command *cmd, { struct sending *sending = tal(cmd, struct sending); sending->sent = sent; - sending->msgfield = tal_strdup(sending, msgfield); - sending->msgval = tal_dup_talarr(sending, u8, msgval); + sending->payload = tal_steal(sending, payload); sending->done = done; return make_reply_path(cmd, sending); @@ -829,11 +817,12 @@ sendinvreq_after_connect(struct command *cmd, const jsmntok_t *result UNUSED, struct sent *sent) { - u8 *rawinvreq = tal_arr(tmpctx, u8, 0); - towire_tlv_invoice_request(&rawinvreq, sent->invreq); + struct tlv_onionmsg_payload *payload = tlv_onionmsg_payload_new(sent); + + payload->invoice_request = tal_arr(payload, u8, 0); + towire_tlv_invoice_request(&payload->invoice_request, sent->invreq); - return send_message(cmd, sent, "invoice_request", rawinvreq, - sendonionmsg_done); + return send_message(cmd, sent, payload, sendonionmsg_done); } struct connect_attempt { @@ -1378,9 +1367,12 @@ sendinvoice_after_connect(struct command *cmd, const jsmntok_t *result UNUSED, struct sent *sent) { - u8 *rawinv = tal_arr(tmpctx, u8, 0); - towire_tlv_invoice(&rawinv, sent->inv); - return send_message(cmd, sent, "invoice", rawinv, prepare_inv_timeout); + struct tlv_onionmsg_payload *payload = tlv_onionmsg_payload_new(sent); + + payload->invoice = tal_arr(payload, u8, 0); + towire_tlv_invoice(&payload->invoice, sent->inv); + + return send_message(cmd, sent, payload, prepare_inv_timeout); } static struct command_result *createinvoice_done(struct command *cmd, diff --git a/plugins/offers.c b/plugins/offers.c index 8e4399e2d635..08b08d52ed3b 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -41,12 +41,10 @@ static struct command_result *sendonionmessage_error(struct command *cmd, return command_hook_success(cmd); } -/* FIXME: replyfield string interface is to accomodate obsolete API */ struct command_result * send_onion_reply(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, - const char *replyfield, - const u8 *replydata) + struct tlv_onionmsg_payload *payload) { struct out_req *req; size_t nhops; @@ -66,18 +64,14 @@ send_onion_reply(struct command *cmd, json_object_start(req->js, NULL); json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); - omp = tlv_onionmsg_payload_new(tmpctx); + /* Put payload in last hop. */ + if (i == nhops - 1) + omp = payload; + else + omp = tlv_onionmsg_payload_new(tmpctx); + omp->encrypted_data_tlv = reply_path->path[i]->encrypted_recipient_data; - /* Put payload in last hop. */ - if (i == nhops - 1) { - if (streq(replyfield, "invoice")) { - omp->invoice = cast_const(u8 *, replydata); - } else { - assert(streq(replyfield, "invoice_error")); - omp->invoice_error = cast_const(u8 *, replydata); - } - } tlv = tal_arr(tmpctx, u8, 0); towire_tlv_onionmsg_payload(&tlv, omp); json_add_hex_talarr(req->js, "tlv", tlv); diff --git a/plugins/offers.h b/plugins/offers.h index dab0c6216f4d..73baca4ee585 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -10,6 +10,5 @@ struct command; struct command_result *WARN_UNUSED_RESULT send_onion_reply(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, - const char *replyfield, - const u8 *replydata); + struct tlv_onionmsg_payload *payload); #endif /* LIGHTNING_PLUGINS_OFFERS_H */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index d998a3f5a47b..60051a0fdbe4 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -25,8 +25,8 @@ fail_inv_level(struct command *cmd, const char *fmt, va_list ap) { char *full_fmt, *msg; + struct tlv_onionmsg_payload *payload; struct tlv_invoice_error *err; - u8 *errdata; full_fmt = tal_fmt(tmpctx, "Failed invoice"); if (inv->inv) { @@ -55,9 +55,10 @@ fail_inv_level(struct command *cmd, err->error = tal_dup_arr(err, char, msg, strlen(msg), 0); /* FIXME: Add suggested_value / erroneous_field! */ - errdata = tal_arr(cmd, u8, 0); - towire_tlv_invoice_error(&errdata, err); - return send_onion_reply(cmd, inv->reply_path, "invoice_error", errdata); + payload = tlv_onionmsg_payload_new(tmpctx); + payload->invoice_error = tal_arr(payload, u8, 0); + towire_tlv_invoice_error(&payload->invoice_error, err); + return send_onion_reply(cmd, inv->reply_path, payload); } static struct command_result *WARN_UNUSED_RESULT diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index d2192801f6e5..ae00aafe5915 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -35,8 +35,8 @@ fail_invreq_level(struct command *cmd, const char *fmt, va_list ap) { char *full_fmt, *msg; + struct tlv_onionmsg_payload *payload; struct tlv_invoice_error *err; - u8 *errdata; full_fmt = tal_fmt(tmpctx, "Failed invoice_request"); if (invreq->invreq) { @@ -61,10 +61,10 @@ fail_invreq_level(struct command *cmd, err->error = tal_dup_arr(err, char, msg, strlen(msg), 0); /* FIXME: Add suggested_value / erroneous_field! */ - errdata = tal_arr(cmd, u8, 0); - towire_tlv_invoice_error(&errdata, err); - return send_onion_reply(cmd, invreq->reply_path, - "invoice_error", errdata); + payload = tlv_onionmsg_payload_new(tmpctx); + payload->invoice_error = tal_arr(payload, u8, 0); + towire_tlv_invoice_error(&payload->invoice_error, err); + return send_onion_reply(cmd, invreq->reply_path, payload); } static struct command_result *WARN_UNUSED_RESULT PRINTF_FMT(3,4) @@ -171,6 +171,7 @@ static struct command_result *createinvoice_done(struct command *cmd, { char *hrp; u8 *rawinv; + struct tlv_onionmsg_payload *payload; const jsmntok_t *t; /* We have a signed invoice, use it as a reply. */ @@ -183,7 +184,9 @@ static struct command_result *createinvoice_done(struct command *cmd, json_tok_full(buf, t)); } - return send_onion_reply(cmd, ir->reply_path, "invoice", rawinv); + payload = tlv_onionmsg_payload_new(tmpctx); + payload->invoice = rawinv; + return send_onion_reply(cmd, ir->reply_path, payload); } static struct command_result *createinvoice_error(struct command *cmd, From 2b422272e5849e408ad872f98d2c4ec9b530e03a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0554/1530] wire: generate printwire_ routines for bolt12 CSV. Good exercise, and we need it when bolt12 messages appear in onion messages anyway. Signed-off-by: Rusty Russell --- devtools/print_wire.c | 6 ++++-- devtools/print_wire.h | 3 +++ wire/Makefile | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/devtools/print_wire.c b/devtools/print_wire.c index 482a1afe097f..f8b4acc1feaa 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -108,7 +108,7 @@ static bool print_hexstring(const u8 **cursor, size_t *plen, size_t len) return true; } -static bool printwire_alias(const u8 **cursor, size_t *plen, size_t len) +bool printwire_utf8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len) { struct utf8_state utf8 = UTF8_STATE_INIT; const char *p = (const char *)*cursor; @@ -217,7 +217,7 @@ static bool printwire_encoded_short_ids(const u8 **cursor, size_t *plen, size_t bool printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len) { if (streq(fieldname, "node_announcement.alias")) - return printwire_alias(cursor, plen, len); + return printwire_utf8_array(fieldname, cursor, plen, len); if (streq(fieldname, "node_announcement.addresses")) return printwire_addresses(cursor, plen, len); @@ -316,10 +316,12 @@ bool printwire_tlvs(const char *fieldname, const u8 **cursor, size_t *plen, #define PRINTWIRE_STRUCT_TYPE_TO_STRING(T) \ PRINTWIRE_TYPE_TO_STRING(struct T, T) +PRINTWIRE_STRUCT_TYPE_TO_STRING(bip340sig) PRINTWIRE_STRUCT_TYPE_TO_STRING(bitcoin_blkid) PRINTWIRE_STRUCT_TYPE_TO_STRING(bitcoin_txid) PRINTWIRE_STRUCT_TYPE_TO_STRING(channel_id) PRINTWIRE_STRUCT_TYPE_TO_STRING(node_id) +PRINTWIRE_STRUCT_TYPE_TO_STRING(point32) PRINTWIRE_STRUCT_TYPE_TO_STRING(preimage) PRINTWIRE_STRUCT_TYPE_TO_STRING(pubkey) PRINTWIRE_STRUCT_TYPE_TO_STRING(sha256) diff --git a/devtools/print_wire.h b/devtools/print_wire.h index 060ef8a29743..3165e0e03abc 100644 --- a/devtools/print_wire.h +++ b/devtools/print_wire.h @@ -24,15 +24,18 @@ bool printwire_tu16(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_tu32(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_tu64(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_u8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len); +bool printwire_utf8_array(const char *fieldname, const u8 **cursor, size_t *plen, size_t len); bool printwire_tlvs(const char *tlv_name, const u8 **cursor, size_t *plen, const struct tlv_print_record_type types[], size_t num_types); +bool printwire_bip340sig(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_bitcoin_blkid(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_wireaddr(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_bitcoin_txid(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_channel_id(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_amount_sat(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_amount_msat(const char *fieldname, const u8 **cursor, size_t *plen); +bool printwire_point32(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_preimage(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_pubkey(const char *fieldname, const u8 **cursor, size_t *plen); bool printwire_node_id(const char *fieldname, const u8 **cursor, size_t *plen); diff --git a/wire/Makefile b/wire/Makefile index a1c022eebfda..d6f1bf5f593b 100644 --- a/wire/Makefile +++ b/wire/Makefile @@ -11,6 +11,7 @@ WIRE_HEADERS := wire/onion_defs.h \ wire/onion$(EXP)_wiregen.h \ wire/bolt12$(EXP)_wiregen.h \ wire/channel_type_wiregen.h \ + wire/bolt12$(EXP)_printgen.h \ wire/peer$(EXP)_printgen.h \ wire/onion$(EXP)_printgen.h @@ -29,6 +30,7 @@ WIRE_SRC := wire/wire_sync.c \ WIRE_PRINT_SRC := \ wire/onion$(EXP)_printgen.c \ wire/peer$(EXP)_printgen.c \ + wire/bolt12$(EXP)_printgen.c \ wire/channel_type_printgen.c WIRE_OBJS := $(WIRE_SRC:.c=.o) @@ -135,9 +137,15 @@ wire/onion_exp_wiregen.c_args := $(wire/onion_wiregen.c_args) wire/bolt12_wiregen.c_args := -s --expose-tlv-type=tlv_blinded_path --expose-tlv-type=tlv_invoice_request wire/bolt12_wiregen.h_args := --include='bitcoin/short_channel_id.h' --include='bitcoin/signature.h' --include='bitcoin/privkey.h' --include='common/bigsize.h' --include='common/amount.h' --include='common/node_id.h' --include='bitcoin/block.h' --include='wire/onion_wire.h' $(wire/bolt12_wiregen.c_args) + +wire/bolt12_printgen.c_args := --expose-tlv-type=tlv_blinded_path --expose-tlv-type=tlv_invoice_request --include='wire/onion$(EXP)_wiregen.h' --include='wire/onion$(EXP)_printgen.h' +wire/bolt12_printgen.h_args := --include='wire/bolt12$(EXP)_wiregen.h' --expose-tlv-type=tlv_blinded_path --expose-tlv-type=tlv_invoice_request + # Same for _exp versions wire/bolt12_exp_wiregen.h_args := $(wire/bolt12_wiregen.h_args) wire/bolt12_exp_wiregen.c_args := $(wire/bolt12_wiregen.c_args) +wire/bolt12_exp_printgen.h_args := $(wire/bolt12_printgen.h_args) +wire/bolt12_exp_printgen.c_args := $(wire/bolt12_printgen.c_args) wire/peer_wiregen.h_args := --include='common/channel_id.h' --include='bitcoin/tx.h' --include='bitcoin/preimage.h' --include='bitcoin/short_channel_id.h' --include='common/node_id.h' --include='common/bigsize.h' --include='bitcoin/block.h' --include='bitcoin/privkey.h' -s --expose-tlv-type=tlv_n1 --expose-tlv-type=tlv_n2 From a770f51d0e1684df90473f00b924a810128c3dd2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 10:01:14 +1030 Subject: [PATCH 0555/1530] tools/generate_wire.py: make functions allocate the TLV. Requiring the caller to allocate them is ugly, and differs from other types. This means we need a context arg if we don't have one already. Signed-off-by: Rusty Russell --- channeld/channeld.c | 49 ++++++++++++++--------- closingd/closingd.c | 5 +-- common/blindedpath.c | 4 +- common/bolt12.c | 27 +++++++------ common/onion.c | 11 ++---- common/test/run-blindedpath_onion.c | 3 +- common/test/run-bolt12_decode.c | 21 +++------- common/test/run-bolt12_merkle-json.c | 10 +++-- common/test/run-bolt12_merkle.c | 4 +- common/test/run-bolt12_period.c | 21 +++------- common/test/run-gossmap_local.c | 5 +-- connectd/onion_message.c | 4 +- connectd/peer_exchange_initmsg.c | 4 +- devtools/create-gossipstore.c | 3 +- gossipd/gossipd.c | 3 +- gossipd/queries.c | 17 ++++---- gossipd/routing.c | 6 +-- lightningd/onion_message.c | 4 +- openingd/dualopend.c | 33 +++++++++------- openingd/openingd.c | 12 +++--- plugins/fetchinvoice.c | 16 ++++---- plugins/keysend.c | 4 +- plugins/offers_inv_hook.c | 4 +- plugins/offers_invreq_hook.c | 4 +- plugins/topology.c | 3 +- tools/gen/header_template | 4 +- tools/gen/impl_template | 13 ++++-- tools/gen/print_impl_template | 2 +- tools/generate-wire.py | 2 +- tools/test/run-test-wire.c | 1 - wire/test/run-peer-wire.c | 40 +++++++++++-------- wire/test/run-tlvstream.c | 59 ++++++++++++++++------------ 32 files changed, 199 insertions(+), 199 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index cbf4c1742406..ddce25310914 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -664,17 +664,21 @@ static void handle_peer_add_htlc(struct peer *peer, const u8 *msg) enum channel_add_err add_err; struct htlc *htlc; #if EXPERIMENTAL_FEATURES - struct tlv_update_add_tlvs *tlvs = tlv_update_add_tlvs_new(msg); + struct tlv_update_add_tlvs *tlvs; #endif struct pubkey *blinding = NULL; - if (!fromwire_update_add_htlc(msg, &channel_id, &id, &amount, - &payment_hash, &cltv_expiry, - onion_routing_packet + if (!fromwire_update_add_htlc #if EXPERIMENTAL_FEATURES - , tlvs + (msg, msg, &channel_id, &id, &amount, + &payment_hash, &cltv_expiry, + onion_routing_packet, &tlvs) +#else + (msg, &channel_id, &id, &amount, + &payment_hash, &cltv_expiry, + onion_routing_packet) #endif - )) + ) peer_failed_warn(peer->pps, &peer->channel_id, "Bad peer_add_htlc %s", tal_hex(msg, msg)); @@ -1987,14 +1991,14 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) { struct channel_id channel_id; u8 *scriptpubkey; - struct tlv_shutdown_tlvs *tlvs = tlv_shutdown_tlvs_new(tmpctx); + struct tlv_shutdown_tlvs *tlvs; struct bitcoin_outpoint *wrong_funding; /* Disable the channel. */ send_channel_update(peer, ROUTING_FLAGS_DISABLED); if (!fromwire_shutdown(tmpctx, shutdown, &channel_id, &scriptpubkey, - tlvs)) + &tlvs)) peer_failed_warn(peer->pps, &peer->channel_id, "Bad shutdown %s", tal_hex(peer, shutdown)); @@ -2112,18 +2116,26 @@ static void handle_unexpected_reestablish(struct peer *peer, const u8 *msg) struct secret your_last_per_commitment_secret; struct pubkey my_current_per_commitment_point; #if EXPERIMENTAL_FEATURES - struct tlv_channel_reestablish_tlvs *tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); + struct tlv_channel_reestablish_tlvs *tlvs; #endif - if (!fromwire_channel_reestablish(msg, &channel_id, - &next_commitment_number, - &next_revocation_number, - &your_last_per_commitment_secret, - &my_current_per_commitment_point + + if (!fromwire_channel_reestablish #if EXPERIMENTAL_FEATURES - , tlvs + (tmpctx, msg, &channel_id, + &next_commitment_number, + &next_revocation_number, + &your_last_per_commitment_secret, + &my_current_per_commitment_point, + &tlvs) +#else + (msg, &channel_id, + &next_commitment_number, + &next_revocation_number, + &your_last_per_commitment_secret, + &my_current_per_commitment_point) #endif - )) + ) peer_failed_warn(peer->pps, &peer->channel_id, "Bad channel_reestablish %s", tal_hex(peer, msg)); @@ -2838,6 +2850,7 @@ static void peer_reconnect(struct peer *peer, capture_premature_msg(&premature_msgs, msg)); #if EXPERIMENTAL_FEATURES + /* Initialize here in case we don't read it below! */ recv_tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); /* FIXME: v0.10.1 would send a different tlv set, due to older spec. @@ -2856,13 +2869,13 @@ static void peer_reconnect(struct peer *peer, "bad reestablish msg: %s %s", peer_wire_name(fromwire_peektype(msg)), tal_hex(msg, msg)); - } else if (!fromwire_channel_reestablish(msg, + } else if (!fromwire_channel_reestablish(tmpctx, msg, &channel_id, &next_commitment_number, &next_revocation_number, &last_local_per_commitment_secret, &remote_current_per_commitment_point, - recv_tlvs)) { + &recv_tlvs)) { peer_failed_warn(peer->pps, &peer->channel_id, "bad reestablish msg: %s %s", diff --git a/closingd/closingd.c b/closingd/closingd.c index 345f2c26c26e..41349d80df97 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -278,10 +278,9 @@ receive_offer(struct per_peer_state *pps, } while (!msg); their_sig.sighash_type = SIGHASH_ALL; - close_tlvs = tlv_closing_signed_tlvs_new(msg); - if (!fromwire_closing_signed(msg, &their_channel_id, + if (!fromwire_closing_signed(msg, msg, &their_channel_id, &received_fee, &their_sig.s, - close_tlvs)) + &close_tlvs)) peer_failed_warn(pps, channel_id, "Expected closing_signed: %s", tal_hex(tmpctx, msg)); diff --git a/common/blindedpath.c b/common/blindedpath.c index 8c5b257616b3..d65a0693806d 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -197,8 +197,8 @@ static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, * - if the `enctlv` is not a valid TLV... * - MUST drop the message. */ - encmsg = tlv_encrypted_data_tlv_new(ctx); - if (!fromwire_tlv_encrypted_data_tlv(&cursor, &maxlen, encmsg) + encmsg = fromwire_tlv_encrypted_data_tlv(ctx, &cursor, &maxlen); + if (!encmsg || !tlv_fields_valid(encmsg->fields, NULL, NULL)) return tal_free(encmsg); diff --git a/common/bolt12.c b/common/bolt12.c index fd4a729d673c..a0ce4c3ad4a8 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -166,17 +166,18 @@ struct tlv_offer *offer_decode(const tal_t *ctx, const struct chainparams *must_be_chain, char **fail) { - struct tlv_offer *offer = tlv_offer_new(ctx); + struct tlv_offer *offer; const u8 *data; size_t dlen; data = string_to_data(tmpctx, b12, b12len, "lno", &dlen, fail); if (!data) - return tal_free(offer); + return NULL;; - if (!fromwire_tlv_offer(&data, &dlen, offer)) { + offer = fromwire_tlv_offer(ctx, &data, &dlen); + if (!offer) { *fail = tal_fmt(ctx, "invalid offer data"); - return tal_free(offer); + return NULL; } *fail = check_features_and_chain(ctx, @@ -219,17 +220,18 @@ struct tlv_invoice_request *invrequest_decode(const tal_t *ctx, const struct chainparams *must_be_chain, char **fail) { - struct tlv_invoice_request *invrequest = tlv_invoice_request_new(ctx); + struct tlv_invoice_request *invrequest; const u8 *data; size_t dlen; data = string_to_data(tmpctx, b12, b12len, "lnr", &dlen, fail); if (!data) - return tal_free(invrequest); + return NULL; - if (!fromwire_tlv_invoice_request(&data, &dlen, invrequest)) { + invrequest = fromwire_tlv_invoice_request(ctx, &data, &dlen); + if (!invrequest) { *fail = tal_fmt(ctx, "invalid invoice_request data"); - return tal_free(invrequest); + return NULL; } *fail = check_features_and_chain(ctx, @@ -258,17 +260,18 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx, const struct chainparams *must_be_chain, char **fail) { - struct tlv_invoice *invoice = tlv_invoice_new(ctx); + struct tlv_invoice *invoice; const u8 *data; size_t dlen; data = string_to_data(tmpctx, b12, b12len, "lni", &dlen, fail); if (!data) - return tal_free(invoice); + return NULL; - if (!fromwire_tlv_invoice(&data, &dlen, invoice)) { + invoice = fromwire_tlv_invoice(ctx, &data, &dlen); + if (!invoice) { *fail = tal_fmt(ctx, "invalid invoice data"); - return tal_free(invoice); + return NULL; } *fail = check_features_and_chain(ctx, diff --git a/common/onion.c b/common/onion.c index e0c36ad8ebab..827ac476ea60 100644 --- a/common/onion.c +++ b/common/onion.c @@ -173,7 +173,6 @@ static struct tlv_tlv_payload *decrypt_tlv(const tal_t *ctx, const u8 *cursor; size_t max; int ret; - struct tlv_tlv_payload *tlv; subkey_from_hmac("rho", blinding_ss, &rho); if (tal_bytelen(enc) < crypto_aead_chacha20poly1305_ietf_ABYTES) @@ -192,13 +191,9 @@ static struct tlv_tlv_payload *decrypt_tlv(const tal_t *ctx, if (ret != 0) return NULL; - tlv = tlv_tlv_payload_new(ctx); cursor = dec; max = tal_bytelen(dec); - if (!fromwire_tlv_tlv_payload(&cursor, &max, tlv)) - return tal_free(tlv); - - return tlv; + return fromwire_tlv_tlv_payload(ctx, &cursor, &max); } #endif /* EXPERIMENTAL_FEATURES */ @@ -219,8 +214,8 @@ struct onion_payload *onion_decode(const tal_t *ctx, if (!pull_payload_length(&cursor, &max, true, &len)) goto general_fail; - tlv = tlv_tlv_payload_new(p); - if (!fromwire_tlv_tlv_payload(&cursor, &max, tlv)) { + tlv = fromwire_tlv_tlv_payload(p, &cursor, &max); + if (!tlv) { /* FIXME: Fill in correct thing here! */ goto general_fail; } diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c index 267ecc2ea5bb..3c3f44841c40 100644 --- a/common/test/run-blindedpath_onion.c +++ b/common/test/run-blindedpath_onion.c @@ -132,8 +132,7 @@ static u8 *next_onion(const tal_t *ctx, u8 *omsg, cursor = rs->raw_payload; max = tal_bytelen(rs->raw_payload); maxlen = fromwire_bigsize(&cursor, &max); - om = tlv_onionmsg_payload_new(tmpctx); - assert(fromwire_tlv_onionmsg_payload(&cursor, &maxlen, om)); + om = fromwire_tlv_onionmsg_payload(tmpctx, &cursor, &maxlen); if (rs->nextcase == ONION_END) return NULL; diff --git a/common/test/run-bolt12_decode.c b/common/test/run-bolt12_decode.c index ff623e17e801..f3adb2622c58 100644 --- a/common/test/run-bolt12_decode.c +++ b/common/test/run-bolt12_decode.c @@ -68,16 +68,16 @@ u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } /* Generated stub for fromwire_tlv_invoice */ -bool fromwire_tlv_invoice(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice * record UNNEEDED) +struct tlv_invoice *fromwire_tlv_invoice(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_tlv_invoice called!\n"); abort(); } /* Generated stub for fromwire_tlv_invoice_request */ -bool fromwire_tlv_invoice_request(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice_request * record UNNEEDED) +struct tlv_invoice_request *fromwire_tlv_invoice_request(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_tlv_invoice_request called!\n"); abort(); } /* Generated stub for fromwire_tlv_offer */ -bool fromwire_tlv_offer(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_offer * record UNNEEDED) +struct tlv_offer *fromwire_tlv_offer(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_tlv_offer called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) @@ -110,15 +110,6 @@ void sighash_from_merkle(const char *messagename UNNEEDED, const struct sha256 *merkle UNNEEDED, struct sha256 *sighash UNNEEDED) { fprintf(stderr, "sighash_from_merkle called!\n"); abort(); } -/* Generated stub for tlv_invoice_new */ -struct tlv_invoice *tlv_invoice_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "tlv_invoice_new called!\n"); abort(); } -/* Generated stub for tlv_invoice_request_new */ -struct tlv_invoice_request *tlv_invoice_request_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "tlv_invoice_request_new called!\n"); abort(); } -/* Generated stub for tlv_offer_new */ -struct tlv_offer *tlv_offer_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "tlv_offer_new called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index 82ea1e43156d..ef594f2c2132 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -158,16 +158,18 @@ int main(int argc, char *argv[]) /* Now do it via type-specific fromwire. */ if (streq(tlvtype, "n1")) { - struct tlv_n1 *n1 = tlv_n1_new(tmpctx); + struct tlv_n1 *n1; size_t len = tal_bytelen(tlv); - assert(fromwire_tlv_n1(&tlv, &len, n1)); + n1 = fromwire_tlv_n1(tmpctx, &tlv, &len); + assert(n1); assert(len == 0); merkle_tlv(n1->fields, &merkle); assert(sha256_eq(&merkle, &expected_merkle)); } else if (streq(tlvtype, "offer")) { - struct tlv_offer *offer = tlv_offer_new(tmpctx); + struct tlv_offer *offer; size_t len = tal_bytelen(tlv); - assert(fromwire_tlv_offer(&tlv, &len, offer)); + offer = fromwire_tlv_offer(tmpctx, &tlv, &len); + assert(offer); assert(len == 0); merkle_tlv(offer->fields, &merkle); assert(sha256_eq(&merkle, &expected_merkle)); diff --git a/common/test/run-bolt12_merkle.c b/common/test/run-bolt12_merkle.c index f6df0f05bcfa..b21420e55f7b 100644 --- a/common/test/run-bolt12_merkle.c +++ b/common/test/run-bolt12_merkle.c @@ -117,9 +117,7 @@ static void merkle_n1(const struct tlv_n1 *n1, struct sha256 *test_m) towire_tlv_n1(&v, n1); len = tal_bytelen(v); - tmp = tlv_n1_new(tmpctx); - if (!fromwire_tlv_n1(cast_const2(const u8 **, &v), &len, tmp)) - abort(); + tmp = fromwire_tlv_n1(tmpctx, cast_const2(const u8 **, &v), &len); assert(len == 0); merkle_tlv(tmp->fields, test_m); diff --git a/common/test/run-bolt12_period.c b/common/test/run-bolt12_period.c index 086177a260cc..218cea67d391 100644 --- a/common/test/run-bolt12_period.c +++ b/common/test/run-bolt12_period.c @@ -71,16 +71,16 @@ u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } /* Generated stub for fromwire_tlv_invoice */ -bool fromwire_tlv_invoice(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice * record UNNEEDED) +struct tlv_invoice *fromwire_tlv_invoice(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_tlv_invoice called!\n"); abort(); } /* Generated stub for fromwire_tlv_invoice_request */ -bool fromwire_tlv_invoice_request(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_invoice_request * record UNNEEDED) +struct tlv_invoice_request *fromwire_tlv_invoice_request(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_tlv_invoice_request called!\n"); abort(); } /* Generated stub for fromwire_tlv_offer */ -bool fromwire_tlv_offer(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct tlv_offer * record UNNEEDED) +struct tlv_offer *fromwire_tlv_offer(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_tlv_offer called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) @@ -113,15 +113,6 @@ void sighash_from_merkle(const char *messagename UNNEEDED, const struct sha256 *merkle UNNEEDED, struct sha256 *sighash UNNEEDED) { fprintf(stderr, "sighash_from_merkle called!\n"); abort(); } -/* Generated stub for tlv_invoice_new */ -struct tlv_invoice *tlv_invoice_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "tlv_invoice_new called!\n"); abort(); } -/* Generated stub for tlv_invoice_request_new */ -struct tlv_invoice_request *tlv_invoice_request_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "tlv_invoice_request_new called!\n"); abort(); } -/* Generated stub for tlv_offer_new */ -struct tlv_offer *tlv_offer_new(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "tlv_offer_new called!\n"); abort(); } /* Generated stub for to_bech32_charset */ char *to_bech32_charset(const tal_t *ctx UNNEEDED, const char *hrp UNNEEDED, const u8 *data UNNEEDED) diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 1d8d19cc9cf0..85e522448e5b 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -278,8 +278,7 @@ static void check_nannounce(const u8 *nannounce, u32 timestamp; u8 rgb_color[3], alias[32]; struct node_id node_id; - struct tlv_node_ann_tlvs *na_tlvs - = tlv_node_ann_tlvs_new(tmpctx); + struct tlv_node_ann_tlvs *na_tlvs; assert(fromwire_node_announcement(nannounce, nannounce, &sig, &features, ×tamp, @@ -287,7 +286,7 @@ static void check_nannounce(const u8 *nannounce, rgb_color, alias, &addresses, - na_tlvs)); + &na_tlvs)); assert(node_id_eq(&node_id, n)); } diff --git a/connectd/onion_message.c b/connectd/onion_message.c index 779ab969c7ea..d84349d53a4b 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -101,8 +101,8 @@ void handle_onion_message(struct daemon *daemon, return; } - om = tlv_onionmsg_payload_new(msg); - if (!fromwire_tlv_onionmsg_payload(&cursor, &maxlen, om)) { + om = fromwire_tlv_onionmsg_payload(msg, &cursor, &maxlen); + if (!om) { status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", tal_hex(tmpctx, rs->raw_payload)); return; diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 6dc36d5349b3..3f0df9946458 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -47,7 +47,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, { u8 *msg = cryptomsg_decrypt_body(tmpctx, &peer->cs, peer->msg); u8 *globalfeatures, *features; - struct tlv_init_tlvs *tlvs = tlv_init_tlvs_new(msg); + struct tlv_init_tlvs *tlvs; struct wireaddr *remote_addr; if (!msg) @@ -64,7 +64,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, if (unlikely(is_unknown_msg_discardable(msg))) return read_init(conn, peer); - if (!fromwire_init(tmpctx, msg, &globalfeatures, &features, tlvs)) { + if (!fromwire_init(tmpctx, msg, &globalfeatures, &features, &tlvs)) { status_peer_debug(&peer->id, "bad fromwire_init '%s', closing", tal_hex(tmpctx, msg)); diff --git a/devtools/create-gossipstore.c b/devtools/create-gossipstore.c index b4f6980f0842..28d6f0843e98 100644 --- a/devtools/create-gossipstore.c +++ b/devtools/create-gossipstore.c @@ -83,10 +83,9 @@ static u32 get_node_announce_timestamp(const u8 *msg) u8 *features, *addresses; struct tlv_node_ann_tlvs *na_tlvs; - na_tlvs = tlv_node_ann_tlvs_new(tmpctx); if (fromwire_node_announcement(tmpctx, msg, &sig, &features, ×tamp, &id, rgb_color, alias, &addresses, - na_tlvs)) + &na_tlvs)) return timestamp; errx(1, "Invalid node_announcement"); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 45cbe4918cc0..606c5a38adcc 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -154,13 +154,12 @@ static bool get_node_announcement(const tal_t *ctx, msg = gossip_store_get(tmpctx, daemon->rstate->gs, n->bcast.index); /* Note: validity of node_id is already checked. */ - na_tlvs = tlv_node_ann_tlvs_new(ctx); if (!fromwire_node_announcement(ctx, msg, &signature, features, ×tamp, &id, rgb_color, alias, &addresses, - na_tlvs)) { + &na_tlvs)) { status_broken("Bad local node_announcement @%u: %s", n->bcast.index, tal_hex(tmpctx, msg)); return false; diff --git a/gossipd/queries.c b/gossipd/queries.c index 436ee9b2c4b4..0ddb96bd29e0 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -242,11 +242,10 @@ const u8 *handle_query_short_channel_ids(struct peer *peer, const u8 *msg) u8 *encoded; struct short_channel_id *scids; bigsize_t *flags; - struct tlv_query_short_channel_ids_tlvs *tlvs - = tlv_query_short_channel_ids_tlvs_new(tmpctx); + struct tlv_query_short_channel_ids_tlvs *tlvs; if (!fromwire_query_short_channel_ids(tmpctx, msg, &chain, &encoded, - tlvs)) { + &tlvs)) { return towire_warningfmt(peer, NULL, "Bad query_short_channel_ids w/tlvs %s", tal_hex(tmpctx, msg)); @@ -645,12 +644,11 @@ const u8 *handle_query_channel_range(struct peer *peer, const u8 *msg) struct bitcoin_blkid chain_hash; u32 first_blocknum, number_of_blocks; enum query_option_flags query_option_flags; - struct tlv_query_channel_range_tlvs *tlvs - = tlv_query_channel_range_tlvs_new(msg); + struct tlv_query_channel_range_tlvs *tlvs; - if (!fromwire_query_channel_range(msg, &chain_hash, + if (!fromwire_query_channel_range(msg, msg, &chain_hash, &first_blocknum, &number_of_blocks, - tlvs)) { + &tlvs)) { return towire_warningfmt(peer, NULL, "Bad query_channel_range w/tlvs %s", tal_hex(tmpctx, msg)); @@ -741,12 +739,11 @@ const u8 *handle_reply_channel_range(struct peer *peer, const u8 *msg) void (*cb)(struct peer *peer, u32 first_blocknum, u32 number_of_blocks, const struct range_query_reply *replies); - struct tlv_reply_channel_range_tlvs *tlvs - = tlv_reply_channel_range_tlvs_new(tmpctx); + struct tlv_reply_channel_range_tlvs *tlvs; if (!fromwire_reply_channel_range(tmpctx, msg, &chain, &first_blocknum, &number_of_blocks, &sync_complete, - &encoded, tlvs)) { + &encoded, &tlvs)) { return towire_warningfmt(peer, NULL, "Bad reply_channel_range w/tlvs %s", tal_hex(tmpctx, msg)); diff --git a/gossipd/routing.c b/gossipd/routing.c index d0ce5388a12a..09d866cf5756 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1564,12 +1564,11 @@ bool routing_add_node_announcement(struct routing_state *rstate, tal_steal(tmpctx, msg); /* Note: validity of node_id is already checked. */ - na_tlv = tlv_node_ann_tlvs_new(tmpctx); if (!fromwire_node_announcement(tmpctx, msg, &signature, &features, ×tamp, &node_id, rgb_color, alias, &addresses, - na_tlv)) { + &na_tlv)) { return false; } @@ -1705,12 +1704,11 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, *was_unknown = false; serialized = tal_dup_arr(tmpctx, u8, node_ann, len, 0); - na_tlv = tlv_node_ann_tlvs_new(tmpctx); if (!fromwire_node_announcement(tmpctx, serialized, &signature, &features, ×tamp, &node_id, rgb_color, alias, &addresses, - na_tlv)) { + &na_tlv)) { /* BOLT #7: * * - if `node_id` is NOT a valid compressed public key: diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 0316296263e1..b940202ea191 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -148,8 +148,8 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) submsglen = tal_bytelen(submsg); subptr = submsg; - payload->om = tlv_onionmsg_payload_new(payload); - if (!fromwire_tlv_onionmsg_payload(&subptr, &submsglen, payload->om)) { + payload->om = fromwire_tlv_onionmsg_payload(payload, &subptr, &submsglen); + if (!payload->om) { log_broken(ld->log, "bad got_onionmsg_tous om: %s", tal_hex(tmpctx, msg)); return; diff --git a/openingd/dualopend.c b/openingd/dualopend.c index b8ac657fc358..0f95e3645dc3 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -404,9 +404,9 @@ static void handle_peer_shutdown(struct state *state, u8 *msg) { u8 *scriptpubkey; struct channel_id cid; - struct tlv_shutdown_tlvs *tlvs = tlv_shutdown_tlvs_new(msg); + struct tlv_shutdown_tlvs *tlvs; - if (!fromwire_shutdown(tmpctx, msg, &cid, &scriptpubkey, tlvs)) + if (!fromwire_shutdown(tmpctx, msg, &cid, &scriptpubkey, &tlvs)) open_err_warn(state, "Bad shutdown %s", tal_hex(msg, msg)); if (tal_count(state->upfront_shutdown_script[REMOTE]) @@ -2037,9 +2037,8 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) struct tx_state *tx_state = state->tx_state; state->our_role = TX_ACCEPTER; - open_tlv = tlv_opening_tlvs_new(tmpctx); - if (!fromwire_open_channel2(oc2_msg, &chain_hash, + if (!fromwire_open_channel2(tmpctx, oc2_msg, &chain_hash, &cid, &tx_state->feerate_per_kw_funding, &state->feerate_per_kw_commitment, @@ -2057,7 +2056,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) &state->their_points.htlc, &state->first_per_commitment_point[REMOTE], &state->channel_flags, - open_tlv)) + &open_tlv)) open_err_fatal(state, "Parsing open_channel2 %s", tal_hex(tmpctx, oc2_msg)); @@ -2748,8 +2747,7 @@ static void opener_start(struct state *state, u8 *msg) if (!msg) return; - a_tlv = notleak(tlv_accept_tlvs_new(state)); - if (!fromwire_accept_channel2(msg, &cid, + if (!fromwire_accept_channel2(state, msg, &cid, &tx_state->accepter_funding, &tx_state->remoteconf.dust_limit, &tx_state->remoteconf.max_htlc_value_in_flight, @@ -2763,9 +2761,11 @@ static void opener_start(struct state *state, u8 *msg) &state->their_points.delayed_payment, &state->their_points.htlc, &state->first_per_commitment_point[REMOTE], - a_tlv)) + &a_tlv)) open_err_fatal(state, "Parsing accept_channel2 %s", tal_hex(msg, msg)); + /* FIXME: why? */ + notleak(a_tlv); if (!channel_id_eq(&cid, &state->channel_id)) { struct channel_id future_chan_id; @@ -3559,22 +3559,25 @@ static void do_reconnect_dance(struct state *state) msg)); if (!fromwire_channel_reestablish +#if EXPERIMENTAL_FEATURES + (tmpctx, msg, &cid, + &next_commitment_number, + &next_revocation_number, + &last_local_per_commit_secret, + &remote_current_per_commit_point, + &tlvs) +#else (msg, &cid, &next_commitment_number, &next_revocation_number, &last_local_per_commit_secret, - &remote_current_per_commit_point -#if EXPERIMENTAL_FEATURES - , tlvs + &remote_current_per_commit_point) #endif - )) + ) open_err_fatal(state, "Bad reestablish msg: %s %s", peer_wire_name(fromwire_peektype(msg)), tal_hex(msg, msg)); -#if EXPERIMENTAL_FEATURES - tal_free(tlvs); -#endif /* EXPERIMENTAL_FEATURES */ check_channel_id(state, &cid, &state->channel_id); status_debug("Got dualopend reestablish commit=%"PRIu64 diff --git a/openingd/openingd.c b/openingd/openingd.c index 0b4ca5f1353b..87bdbf25d56a 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -376,8 +376,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) * `payment_basepoint`, or `delayed_payment_basepoint` are not * valid secp256k1 pubkeys in compressed format. */ - accept_tlvs = tlv_accept_channel_tlvs_new(tmpctx); - if (!fromwire_accept_channel(msg, &id_in, + if (!fromwire_accept_channel(tmpctx, msg, &id_in, &state->remoteconf.dust_limit, &state->remoteconf.max_htlc_value_in_flight, &state->remoteconf.channel_reserve, @@ -391,7 +390,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) &state->their_points.delayed_payment, &state->their_points.htlc, &state->first_per_commitment_point[REMOTE], - accept_tlvs)) { + &accept_tlvs)) { peer_failed_err(state->pps, &state->channel_id, "Parsing accept_channel %s", tal_hex(msg, msg)); @@ -774,8 +773,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) struct wally_tx_output *direct_outputs[NUM_SIDES]; struct penalty_base *pbase; struct tlv_accept_channel_tlvs *accept_tlvs; - struct tlv_open_channel_tlvs *open_tlvs - = tlv_open_channel_tlvs_new(tmpctx); + struct tlv_open_channel_tlvs *open_tlvs; /* BOLT #2: * @@ -785,7 +783,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * `payment_basepoint`, or `delayed_payment_basepoint` are not valid * secp256k1 pubkeys in compressed format. */ - if (!fromwire_open_channel(open_channel_msg, &chain_hash, + if (!fromwire_open_channel(tmpctx, open_channel_msg, &chain_hash, &state->channel_id, &state->funding_sats, &state->push_msat, @@ -803,7 +801,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &theirs.htlc, &state->first_per_commitment_point[REMOTE], &channel_flags, - open_tlvs)) + &open_tlvs)) peer_failed_err(state->pps, &state->channel_id, "Parsing open_channel %s", tal_hex(tmpctx, open_channel_msg)); diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 67edb3121629..7a3c0b06cc61 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -113,14 +113,14 @@ static struct command_result *handle_error(struct command *cmd, data = json_tok_bin_from_hex(cmd, buf, errtok); dlen = tal_bytelen(data); - err = tlv_invoice_error_new(cmd); details = json_out_new(cmd); plugin_log(cmd->plugin, LOG_DBG, "errtok = %.*s", json_tok_full_len(errtok), json_tok_full(buf, errtok)); json_out_start(details, NULL, '{'); - if (!fromwire_tlv_invoice_error(&data, &dlen, err)) { + err = fromwire_tlv_invoice_error(cmd, &data, &dlen); + if (!err) { plugin_log(cmd->plugin, LOG_DBG, "Invalid invoice_error %.*s", json_tok_full_len(errtok), @@ -185,8 +185,8 @@ static struct command_result *handle_invreq_response(struct command *cmd, invbin = json_tok_bin_from_hex(cmd, buf, invtok); len = tal_bytelen(invbin); - inv = tlv_invoice_new(cmd); - if (!fromwire_tlv_invoice(&invbin, &len, inv)) { + inv = fromwire_tlv_invoice(cmd, &invbin, &len); + if (!inv) { badfield = "invoice"; goto badinv; } @@ -1083,8 +1083,8 @@ force_payer_secret(struct command *cmd, towire_tlv_invoice_request(&msg, invreq); p = msg; len = tal_bytelen(msg); - sent->invreq = tlv_invoice_request_new(cmd); - if (!fromwire_tlv_invoice_request(&p, &len, sent->invreq)) + sent->invreq = fromwire_tlv_invoice_request(cmd, &p, &len); + if (!sent->invreq) plugin_err(cmd->plugin, "Could not remarshall invreq %s", tal_hex(tmpctx, msg)); @@ -1469,8 +1469,8 @@ static struct command_result *listsendpays_done(struct command *cmd, towire_tlv_invoice(&msg, sent->inv); p = msg; len = tal_bytelen(msg); - sent->inv = tlv_invoice_new(cmd); - if (!fromwire_tlv_invoice(&p, &len, sent->inv)) + sent->inv = fromwire_tlv_invoice(cmd, &p, &len); + if (!sent->inv) plugin_err(cmd->plugin, "Could not remarshall %s", tal_hex(tmpctx, msg)); diff --git a/plugins/keysend.c b/plugins/keysend.c index 90ed23e48aa8..4e47fbab4c08 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -351,13 +351,13 @@ static struct command_result *htlc_accepted_call(struct command *cmd, return htlc_accepted_continue(cmd, NULL); max = tal_bytelen(rawpayload); - payload = tlv_tlv_payload_new(cmd); s = fromwire_bigsize(&rawpayload, &max); if (s != max) { return htlc_accepted_continue(cmd, NULL); } - if (!fromwire_tlv_tlv_payload(&rawpayload, &max, payload)) { + payload = fromwire_tlv_tlv_payload(cmd, &rawpayload, &max); + if (!payload) { plugin_log( cmd->plugin, LOG_UNUSUAL, "Malformed TLV payload %.*s", json_tok_full_len(params), diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index 60051a0fdbe4..bd92c64b7e49 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -329,8 +329,8 @@ struct command_result *handle_invoice(struct command *cmd, inv->reply_path = tal_steal(inv, reply_path); - inv->inv = tlv_invoice_new(cmd); - if (!fromwire_tlv_invoice(&invbin, &len, inv->inv)) { + inv->inv = fromwire_tlv_invoice(cmd, &invbin, &len); + if (!inv->inv) { return fail_inv(cmd, inv, "Invalid invoice %s", tal_hex(tmpctx, invbin)); diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index ae00aafe5915..851bc556982e 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -857,8 +857,8 @@ struct command_result *handle_invoice_request(struct command *cmd, ir->reply_path = tal_steal(ir, reply_path); - ir->invreq = tlv_invoice_request_new(cmd); - if (!fromwire_tlv_invoice_request(&invreqbin, &len, ir->invreq)) { + ir->invreq = fromwire_tlv_invoice_request(cmd, &invreqbin, &len); + if (!ir->invreq) { return fail_invreq(cmd, ir, "Invalid invreq %s", tal_hex(tmpctx, invreqbin)); diff --git a/plugins/topology.c b/plugins/topology.c index b6f733704fcc..0d8851319656 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -460,7 +460,6 @@ static void json_add_node(struct json_stream *js, struct json_escape *esc; struct tlv_node_ann_tlvs *na_tlvs; - na_tlvs = tlv_node_ann_tlvs_new(tmpctx); if (!fromwire_node_announcement(nannounce, nannounce, &signature, &features, @@ -469,7 +468,7 @@ static void json_add_node(struct json_stream *js, rgb_color, alias, &addresses, - na_tlvs)) { + &na_tlvs)) { plugin_log(plugin, LOG_BROKEN, "Cannot parse stored node_announcement" " for %s at %u: %s", diff --git a/tools/gen/header_template b/tools/gen/header_template index 8b6203592891..35e62e60aeea 100644 --- a/tools/gen/header_template +++ b/tools/gen/header_template @@ -93,8 +93,8 @@ struct ${tlv.struct_name()} *${tlv.struct_name()}_new(const tal_t *ctx); * current namespace are stored in the `fields` member. Validity can be * checked using ${tlv.name}_is_valid. */ -bool fromwire_${tlv.name}(const u8 **cursor, size_t *max, - struct ${tlv.struct_name()} * record); +struct ${tlv.struct_name()} *fromwire_${tlv.name}(const tal_t *ctx, + const u8 **cursor, size_t *max); /** * Serialize a TLV stream for the ${tlv.name} namespace. diff --git a/tools/gen/impl_template b/tools/gen/impl_template index e88a13fdd248..0903cc0f78b6 100644 --- a/tools/gen/impl_template +++ b/tools/gen/impl_template @@ -266,9 +266,12 @@ void towire_${tlv.name}(u8 **pptr, const struct ${tlv.struct_name()} *record) } -bool fromwire_${tlv.name}(const u8 **cursor, size_t *max, struct ${tlv.struct_name()} *record) +struct ${tlv.name} *fromwire_${tlv.name}(const tal_t *ctx, const u8 **cursor, size_t *max) { - return fromwire_tlv(cursor, max, tlvs_${tlv.name}, ${len(tlv.messages)}, record, &record->fields); + struct ${tlv.name} *record = ${tlv.name}_new(ctx); + if (!fromwire_tlv(cursor, max, tlvs_${tlv.name}, ${len(tlv.messages)}, record, &record->fields)) + return tal_free(record); + return record; } bool ${tlv.name}_is_valid(const struct ${tlv.struct_name()} *record, size_t *err_index) @@ -345,7 +348,7 @@ bool fromwire_${msg.name}(${'const tal_t *ctx, ' if msg.needs_context() else ''} if f.type_obj.is_varsize(): typename = typename + ' *' type_ = f.type_obj.name - varsized = f.type_obj.is_varsize() + varsized = f.type_obj.is_varsize() or f.type_obj.is_tlv() %> \ % for c in f.field_comments: /*${c} */ @@ -357,7 +360,9 @@ bool fromwire_${msg.name}(${'const tal_t *ctx, ' if msg.needs_context() else ''} % if f.len_field_of: ${f.name} = fromwire_${type_}(&cursor, &plen); % elif f.type_obj.is_tlv(): - fromwire_${f.type_obj.tlv.name}(&cursor, &plen, ${f.name}); + *${f.name} = fromwire_${f.type_obj.tlv.name}(ctx, &cursor, &plen); + if (!*${f.name}) + return false; % elif f.is_array() or f.is_varlen(): % if f.type_obj.has_array_helper(): fromwire_${type_}_array(&cursor, &plen, ${'*' if f.is_varlen() else ''}${f.name}, ${f.size('plen')}); diff --git a/tools/gen/print_impl_template b/tools/gen/print_impl_template index a38bda40ae73..b4ad9d9306a6 100644 --- a/tools/gen/print_impl_template +++ b/tools/gen/print_impl_template @@ -48,7 +48,7 @@ bool print${options.enum_name}_message(const u8 *msg) % endif printf("${f.name}="); % if f.type_obj.is_tlv(): - printwire_tlvs(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen, print_tlvs_${f.type_obj.tlv.name}, ARRAY_SIZE(print_tlvs_${f.type_obj.tlv.name})); + printwire_${f.type_obj.tlv.name}(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen); % elif f.is_array() or f.is_varlen(): % if f.type_obj.has_array_helper(): printwire_${f.type_obj.name}_array(tal_fmt(NULL, "%s.${f.name}", fieldname), cursor, plen, ${f.size('*plen')}); diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 0e4ad19c0869..e0476b11f4d2 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -358,7 +358,7 @@ def is_assignable(self): def is_varsize(self): """ A type is variably sized if it's marked as such (in varsize_types) or it contains a field of variable length """ - return self.name in self.varsize_types or self.has_len_fields() + return self.name in self.varsize_types or self.has_len_fields() or self.is_tlv() def add_comments(self, comments): self.type_comments = comments diff --git a/tools/test/run-test-wire.c b/tools/test/run-test-wire.c index b409715e14a0..3e447a7eb4e3 100644 --- a/tools/test/run-test-wire.c +++ b/tools/test/run-test-wire.c @@ -10,7 +10,6 @@ /* Generated stub for fromwire_peektype */ int fromwire_peektype(const u8 *cursor UNNEEDED) { fprintf(stderr, "fromwire_peektype called!\n"); abort(); } -/* Could not find declaration for fromwire_test_enum */ /* Generated stub for printwire_amount_msat */ bool printwire_amount_msat(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "printwire_amount_msat called!\n"); abort(); } diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 05769bbef18a..29c71fd484f5 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -107,6 +107,7 @@ struct msg_update_fulfill_htlc { struct msg_shutdown { struct channel_id channel_id; u8 *scriptpubkey; + struct tlv_shutdown_tlvs *tlvs; }; struct msg_funding_signed { struct channel_id temporary_channel_id; @@ -218,6 +219,7 @@ struct msg_update_add_htlc { u32 expiry; struct sha256 payment_hash; u8 onion_routing_packet[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)]; + struct tlv_update_add_tlvs *tlvs; }; struct msg_update_fee { struct channel_id channel_id; @@ -288,9 +290,8 @@ static void *towire_struct_open_channel(const tal_t *ctx, static struct msg_open_channel *fromwire_struct_open_channel(const tal_t *ctx, const void *p) { struct msg_open_channel *s = tal(ctx, struct msg_open_channel); - s->tlvs = tlv_open_channel_tlvs_new(s); - if (fromwire_open_channel(p, + if (fromwire_open_channel(s, p, &s->chain_hash, &s->temporary_channel_id, &s->funding_satoshis, @@ -309,7 +310,7 @@ static struct msg_open_channel *fromwire_struct_open_channel(const tal_t *ctx, c &s->htlc_basepoint, &s->first_per_commitment_point, &s->channel_flags, - s->tlvs)) + &s->tlvs)) return s; return tal_free(s); } @@ -338,9 +339,8 @@ static void *towire_struct_accept_channel(const tal_t *ctx, static struct msg_accept_channel *fromwire_struct_accept_channel(const tal_t *ctx, const void *p) { struct msg_accept_channel *s = tal(ctx, struct msg_accept_channel); - s->tlvs = tlv_accept_channel_tlvs_new(s); - if (fromwire_accept_channel(p, + if (fromwire_accept_channel(s, p, &s->temporary_channel_id, &s->dust_limit_satoshis, &s->max_htlc_value_in_flight_msat, @@ -355,7 +355,7 @@ static struct msg_accept_channel *fromwire_struct_accept_channel(const tal_t *ct &s->htlc_basepoint, &s->delayed_payment_basepoint, &s->first_per_commitment_point, - s->tlvs)) + &s->tlvs)) return s; return tal_free(s); } @@ -377,7 +377,6 @@ static void *towire_struct_node_announcement(const tal_t *ctx, static struct msg_node_announcement *fromwire_struct_node_announcement(const tal_t *ctx, const void *p) { struct msg_node_announcement *s = tal(ctx, struct msg_node_announcement); - s->tlvs = tlv_node_ann_tlvs_new(s); if (!fromwire_node_announcement(s, p, &s->signature, &s->features, @@ -386,7 +385,7 @@ static struct msg_node_announcement *fromwire_struct_node_announcement(const tal s->rgb_color, s->alias, &s->addresses, - s->tlvs)) + &s->tlvs)) return tal_free(s); return s; } @@ -628,12 +627,11 @@ static struct msg_closing_signed *fromwire_struct_closing_signed(const tal_t *ct struct msg_closing_signed *s = tal(ctx, struct msg_closing_signed); struct tlv_closing_signed_tlvs *close_tlvs; - close_tlvs = tlv_closing_signed_tlvs_new(ctx); - if (fromwire_closing_signed(p, + if (fromwire_closing_signed(ctx, p, &s->channel_id, &s->fee_satoshis, &s->signature, - close_tlvs)) + &close_tlvs)) return s; return tal_free(s); } @@ -654,7 +652,7 @@ static struct msg_shutdown *fromwire_struct_shutdown(const tal_t *ctx, const voi if (!fromwire_shutdown(s, p, &s->channel_id, &s->scriptpubkey, - NULL)) + &s->tlvs)) return tal_free(s); return s; } @@ -721,15 +719,24 @@ static struct msg_update_add_htlc *fromwire_struct_update_add_htlc(const tal_t * { struct msg_update_add_htlc *s = tal(ctx, struct msg_update_add_htlc); - if (fromwire_update_add_htlc(p, + if (fromwire_update_add_htlc +#if EXPERIMENTAL_FEATURES + (s, p, + &s->channel_id, + &s->id, + &s->amount_msat, + &s->payment_hash, + &s->expiry, + s->onion_routing_packet, + &s->tlvs +#else + (p, &s->channel_id, &s->id, &s->amount_msat, &s->payment_hash, &s->expiry, s->onion_routing_packet -#if EXPERIMENTAL_FEATURES - ,NULL #endif )) return s; @@ -768,12 +775,11 @@ static void *towire_struct_init(const tal_t *ctx, static struct msg_init *fromwire_struct_init(const tal_t *ctx, const void *p) { struct msg_init *s = tal(ctx, struct msg_init); - s->tlvs = tlv_init_tlvs_new(s); if (!fromwire_init(s, p, &s->globalfeatures, &s->localfeatures, - s->tlvs)) + &s->tlvs)) return tal_free(s); return s; diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index 0e1a467361aa..3397c8db61c2 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -459,62 +459,63 @@ int main(int argc, char *argv[]) tlv3_node_id.amount_msat_2 = AMOUNT_MSAT(2); for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_either); i++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); - struct tlv_n2 *tlv_n2 = tlv_n2_new(tmpctx); + struct tlv_n1 *tlv_n1; + struct tlv_n2 *tlv_n2; const u8 *p, *orig_p; size_t max; orig_p = stream(tmpctx, invalid_streams_either[i].hex); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); assert(strstr(invalid_streams_either[i].reason, reason)); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_tlv_n2(&p, &max, tlv_n2) && !p) || - !tlv_n2_is_valid(tlv_n2, NULL)); + tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); + assert((!tlv_n2 && !p) || !tlv_n2_is_valid(tlv_n2, NULL)); assert(strstr(invalid_streams_either[i].reason, reason)); } for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1); i++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n1 *tlv_n1; const u8 *p; size_t max; p = stream(tmpctx, invalid_streams_n1[i].hex); max = tal_count(p); - assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); assert(strstr(invalid_streams_n1[i].reason, reason)); } for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1_combo); i++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n1 *tlv_n1; const u8 *p; size_t max; p = stream(tmpctx, invalid_streams_n1_combo[i].hex); max = tal_count(p); - assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); assert(strstr(invalid_streams_n1_combo[i].reason, reason)); } for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n2_combo); i++) { - struct tlv_n2 *tlv_n2 = tlv_n2_new(tmpctx); + struct tlv_n2 *tlv_n2; const u8 *p; size_t max; p = stream(tmpctx, invalid_streams_n2_combo[i].hex); max = tal_count(p); - assert((!fromwire_tlv_n2(&p, &max, tlv_n2) && !p) || + tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); + assert((!tlv_n2 && !p) || !tlv_n2_is_valid(tlv_n2, NULL)); assert(strstr(invalid_streams_n2_combo[i].reason, reason)); } for (size_t i = 0; i < ARRAY_SIZE(valid_streams); i++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n1 *tlv_n1; const u8 *orig_p, *p; u8 *p2; size_t max; @@ -523,7 +524,8 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; - assert(fromwire_tlv_n1(&p, &max, tlv_n1) && + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert(tlv_n1 && tlv_n1_is_valid(tlv_n1, NULL)); assert(max == 0); assert(tlv_n1_eq(tlv_n1, &valid_streams[i].expect)); @@ -545,8 +547,8 @@ int main(int argc, char *argv[]) */ for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_either); i++) { for (size_t j = 0; j < ARRAY_SIZE(valid_streams); j++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); - struct tlv_n2 *tlv_n2 = tlv_n2_new(tmpctx); + struct tlv_n1 *tlv_n1; + struct tlv_n2 *tlv_n2; const u8 *orig_p, *p; size_t max; @@ -555,39 +557,43 @@ int main(int argc, char *argv[]) invalid_streams_either[i].hex); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); max = tal_count(orig_p); p = orig_p; - assert((!fromwire_tlv_n2(&p, &max, tlv_n2) && !p) || + tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); + assert((!tlv_n2 && !p) || !tlv_n2_is_valid(tlv_n2, NULL)); } } for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1); i++) { for (size_t j = 0; j < ARRAY_SIZE(valid_streams); j++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n1 *tlv_n1; const u8 *p; size_t max; p = stream2(tmpctx, valid_streams[j].hex, invalid_streams_n1[i].hex); max = tal_count(p); - assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); } } for (size_t i = 0; i < ARRAY_SIZE(invalid_streams_n1_combo); i++) { for (size_t j = 0; j < ARRAY_SIZE(valid_streams); j++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n1 *tlv_n1; const u8 *p; size_t max; p = stream2(tmpctx, valid_streams[j].hex, invalid_streams_n1_combo[i].hex); max = tal_count(p); - assert((!fromwire_tlv_n1(&p, &max, tlv_n1) && !p) || + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); } } @@ -599,7 +605,7 @@ int main(int argc, char *argv[]) */ for (size_t i = 0; i < ARRAY_SIZE(valid_streams); i++) { for (size_t j = i+1; j < ARRAY_SIZE(valid_streams); j++) { - struct tlv_n1 *tlv_n1 = tlv_n1_new(tmpctx); + struct tlv_n1 *tlv_n1; const u8 *orig_p, *p; size_t max; bool expect_success; @@ -617,7 +623,8 @@ int main(int argc, char *argv[]) expect_success = pull_type(valid_streams[i].hex) < pull_type(valid_streams[j].hex); - assert(fromwire_tlv_n1(&p, &max, tlv_n1) && + tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); + assert(tlv_n1 && tlv_n1_is_valid(tlv_n1, NULL) == expect_success); if (!expect_success) From 83ee68ab065baff4357550f4585936f3c001c499 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 13:14:36 +1030 Subject: [PATCH 0556/1530] common/tlvstream: put TLV checking back in the generic function. Callers were supposed to call "tlv_fields_valid" after fromwire_tlv, but few did. Make this the default, and call the underlying function directly where we want to be more flexible (one place). This loses the ability to allow misordered fields, or to pass through *any* even fields. We restore that for special cases in the next patch. Signed-off-by: Rusty Russell --- common/blindedpath.c | 8 +- common/onion.c | 33 +++--- common/test/run-json.c | 7 +- common/test/run-param.c | 7 +- common/test/run-route-specific.c | 7 +- common/test/run-route.c | 7 +- common/test/run-sphinx.c | 7 +- plugins/keysend.c | 14 ++- tools/gen/impl_template | 8 +- wire/test/run-tlvstream.c | 35 +++---- wire/tlvstream.c | 170 ++++++++++++++++++------------- wire/tlvstream.h | 17 +++- 12 files changed, 165 insertions(+), 155 deletions(-) diff --git a/common/blindedpath.c b/common/blindedpath.c index d65a0693806d..1976bca46e10 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -188,7 +188,6 @@ static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, const struct secret *ss, const u8 *enctlv) { - struct tlv_encrypted_data_tlv *encmsg; const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); size_t maxlen = tal_bytelen(cursor); @@ -197,12 +196,7 @@ static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, * - if the `enctlv` is not a valid TLV... * - MUST drop the message. */ - encmsg = fromwire_tlv_encrypted_data_tlv(ctx, &cursor, &maxlen); - if (!encmsg - || !tlv_fields_valid(encmsg->fields, NULL, NULL)) - return tal_free(encmsg); - - return encmsg; + return fromwire_tlv_encrypted_data_tlv(ctx, &cursor, &maxlen); } bool decrypt_enctlv(const struct pubkey *blinding, diff --git a/common/onion.c b/common/onion.c index 827ac476ea60..3fb4600f6822 100644 --- a/common/onion.c +++ b/common/onion.c @@ -209,22 +209,21 @@ struct onion_payload *onion_decode(const tal_t *ctx, const u8 *cursor = rs->raw_payload; size_t max = tal_bytelen(cursor), len; struct tlv_tlv_payload *tlv; - size_t badfield; - if (!pull_payload_length(&cursor, &max, true, &len)) - goto general_fail; - - tlv = fromwire_tlv_tlv_payload(p, &cursor, &max); - if (!tlv) { - /* FIXME: Fill in correct thing here! */ - goto general_fail; + if (!pull_payload_length(&cursor, &max, true, &len)) { + *failtlvtype = 0; + *failtlvpos = tal_bytelen(rs->raw_payload); + goto fail_no_tlv; } - /* FIXME: This API makes it really hard to get the actual - * offset of field. */ - if (!tlv_fields_valid(tlv->fields, accepted_extra_tlvs, &badfield)) { - *failtlvtype = tlv->fields[badfield].numtype; - goto field_bad; + /* We do this manually so we can accept extra types, and get + * error off and type. */ + tlv = tlv_tlv_payload_new(p); + if (!fromwire_tlv(&cursor, &max, tlvs_tlv_tlv_payload, + TLVS_ARRAY_SIZE_tlv_tlv_payload, + tlv, &tlv->fields, accepted_extra_tlvs, + failtlvpos, failtlvtype)) { + goto fail; } /* BOLT #4: @@ -336,14 +335,10 @@ struct onion_payload *onion_decode(const tal_t *ctx, field_bad: *failtlvpos = tlv_field_offset(rs->raw_payload, tal_bytelen(rs->raw_payload), *failtlvtype); - goto fail; - -general_fail: - *failtlvtype = 0; - *failtlvpos = tal_bytelen(rs->raw_payload); - goto fail; fail: tal_free(tlv); + +fail_no_tlv: tal_free(p); return NULL; } diff --git a/common/test/run-json.c b/common/test/run-json.c index 711368786daf..d5511bce4da8 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -11,12 +11,9 @@ /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-param.c b/common/test/run-param.c index 71bdd277d2ce..edda9895ddd4 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -37,7 +37,8 @@ struct command_result *command_fail(struct command *cmd, /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for json_to_channel_id */ bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -76,10 +77,6 @@ int segwit_addr_decode( const char* addr ) { fprintf(stderr, "segwit_addr_decode called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_tlv */ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 22db6f5eccee..c3b375aeae8a 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -31,15 +31,12 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } diff --git a/common/test/run-route.c b/common/test/run-route.c index 31887236ac06..5c575c6fd14d 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -24,15 +24,12 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for fromwire_wireaddr */ bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index 36248e44aefb..7af35453bf12 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -57,7 +57,8 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) @@ -65,10 +66,6 @@ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id U /* Generated stub for tlv_field_offset */ size_t tlv_field_offset(const u8 *tlvstream UNNEEDED, size_t tlvlen UNNEEDED, u64 fieldtype UNNEEDED) { fprintf(stderr, "tlv_field_offset called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, u64 *allow_extra UNNEEDED, - size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_amount_msat */ void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) { fprintf(stderr, "towire_amount_msat called!\n"); abort(); } diff --git a/plugins/keysend.c b/plugins/keysend.c index 4e47fbab4c08..44d99a5f8f3e 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -342,6 +342,9 @@ static struct command_result *htlc_accepted_call(struct command *cmd, struct out_req *req; struct timeabs now = time_now(); const char *err; + u64 *allowed = tal_arr(cmd, u64, 1); + size_t err_off; + u64 err_type; err = json_scan(tmpctx, buf, params, "{onion:{payload:%},htlc:{payment_hash:%}}", @@ -356,10 +359,15 @@ static struct command_result *htlc_accepted_call(struct command *cmd, if (s != max) { return htlc_accepted_continue(cmd, NULL); } - payload = fromwire_tlv_tlv_payload(cmd, &rawpayload, &max); - if (!payload) { + + /* We explicitly allow our type. */ + allowed[0] = 5482373484; + payload = tlv_tlv_payload_new(cmd); + if (!fromwire_tlv(&rawpayload, &max, tlvs_tlv_tlv_payload, TLVS_ARRAY_SIZE_tlv_tlv_payload, + payload, &payload->fields, allowed, &err_off, &err_type)) { plugin_log( - cmd->plugin, LOG_UNUSUAL, "Malformed TLV payload %.*s", + cmd->plugin, LOG_UNUSUAL, "Malformed TLV payload type %"PRIu64" at off %zu %.*s", + err_type, err_off, json_tok_full_len(params), json_tok_full(buf, params)); return htlc_accepted_continue(cmd, NULL); diff --git a/tools/gen/impl_template b/tools/gen/impl_template index 0903cc0f78b6..3d013f7f144d 100644 --- a/tools/gen/impl_template +++ b/tools/gen/impl_template @@ -269,16 +269,10 @@ void towire_${tlv.name}(u8 **pptr, const struct ${tlv.struct_name()} *record) struct ${tlv.name} *fromwire_${tlv.name}(const tal_t *ctx, const u8 **cursor, size_t *max) { struct ${tlv.name} *record = ${tlv.name}_new(ctx); - if (!fromwire_tlv(cursor, max, tlvs_${tlv.name}, ${len(tlv.messages)}, record, &record->fields)) + if (!fromwire_tlv(cursor, max, tlvs_${tlv.name}, ${len(tlv.messages)}, record, &record->fields, NULL, NULL, NULL)) return tal_free(record); return record; } - -bool ${tlv.name}_is_valid(const struct ${tlv.struct_name()} *record, size_t *err_index) -{ - return tlv_fields_valid(record->fields, NULL, err_index); -} - % endfor ## END TLV's % for msg in messages: ## START Wire Messages diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index 3397c8db61c2..dacdb3d2e1c1 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -468,12 +468,12 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); assert(strstr(invalid_streams_either[i].reason, reason)); max = tal_count(orig_p); p = orig_p; tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); - assert((!tlv_n2 && !p) || !tlv_n2_is_valid(tlv_n2, NULL)); + assert(!tlv_n2 && !p); assert(strstr(invalid_streams_either[i].reason, reason)); } @@ -485,7 +485,7 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n1[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); assert(strstr(invalid_streams_n1[i].reason, reason)); } @@ -497,7 +497,7 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n1_combo[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); assert(strstr(invalid_streams_n1_combo[i].reason, reason)); } @@ -509,8 +509,7 @@ int main(int argc, char *argv[]) p = stream(tmpctx, invalid_streams_n2_combo[i].hex); max = tal_count(p); tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); - assert((!tlv_n2 && !p) || - !tlv_n2_is_valid(tlv_n2, NULL)); + assert(!tlv_n2 && !p); assert(strstr(invalid_streams_n2_combo[i].reason, reason)); } @@ -525,8 +524,7 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert(tlv_n1 && - tlv_n1_is_valid(tlv_n1, NULL)); + assert(tlv_n1); assert(max == 0); assert(tlv_n1_eq(tlv_n1, &valid_streams[i].expect)); @@ -558,13 +556,11 @@ int main(int argc, char *argv[]) max = tal_count(orig_p); p = orig_p; tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); max = tal_count(orig_p); p = orig_p; tlv_n2 = fromwire_tlv_n2(tmpctx, &p, &max); - assert((!tlv_n2 && !p) || - !tlv_n2_is_valid(tlv_n2, NULL)); + assert(!tlv_n2 && !p); } } @@ -578,8 +574,7 @@ int main(int argc, char *argv[]) invalid_streams_n1[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); } } @@ -593,8 +588,7 @@ int main(int argc, char *argv[]) invalid_streams_n1_combo[i].hex); max = tal_count(p); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert((!tlv_n1 && !p) || - !tlv_n1_is_valid(tlv_n1, NULL)); + assert(!tlv_n1 && !p); } } @@ -624,11 +618,12 @@ int main(int argc, char *argv[]) < pull_type(valid_streams[j].hex); tlv_n1 = fromwire_tlv_n1(tmpctx, &p, &max); - assert(tlv_n1 && - tlv_n1_is_valid(tlv_n1, NULL) == expect_success); - - if (!expect_success) + if (!expect_success) { + assert(!tlv_n1); continue; + } + + assert(tlv_n1); /* Re-encoding should give the same results (except * ignored fields tests!) */ diff --git a/wire/tlvstream.c b/wire/tlvstream.c index aadd04460706..1fff8415a49b 100644 --- a/wire/tlvstream.c +++ b/wire/tlvstream.c @@ -110,10 +110,39 @@ size_t tlv_field_offset(const u8 *tlvstream, size_t tlvlen, u64 fieldtype) return tlvlen; } +static bool tlv_type_is_allowed(const struct tlv_field *f, + const u64 *extra_types) +{ + /* Simple case: it's an odd field. */ + if (f->numtype % 2 != 0) + return true; + + /* Now iterate through the extras and see if we should make an + * exception. */ + for (size_t i = 0; i < tal_count(extra_types); i++) + if (extra_types[i] == f->numtype) + return true; + return false; +} + +/* Update err_off to point to current offset. */ +static void update_err_off(size_t *err_off, size_t initial_len, size_t max) +{ + if (err_off) + *err_off = initial_len - max; +} + bool fromwire_tlv(const u8 **cursor, size_t *max, const struct tlv_record_type *types, size_t num_types, - void *record, struct tlv_field **fields) + void *record, struct tlv_field **fields, + const u64 *extra_types, + size_t *err_off, u64 *err_type) { + bool first = true; + u64 prev_type = 0; + size_t initial_len = *max; + + update_err_off(err_off, initial_len, *max); while (*max > 0) { struct tlv_field field; @@ -129,8 +158,52 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, */ if (!*cursor) { SUPERVERBOSE("type"); + if (err_type) + *err_type = 0; + goto fail; + } + + /* BOLT #1: + * - if decoded `type`s are not strictly-increasing + * (including situations when two or more occurrences + * of the same `type` are met): + * - MUST fail to parse the `tlv_stream`. + */ + if (!first && field.numtype <= prev_type) { + if (field.numtype == prev_type) + SUPERVERBOSE("duplicate tlv type"); + else + SUPERVERBOSE("invalid ordering"); + if (err_type) + *err_type = field.numtype; goto fail; } + first = false; + prev_type = field.numtype; + + /* BOLT #1: + * - if `type` is known: + * - MUST decode the next `length` bytes using the known + * encoding for `type`. + */ + field.meta = NULL; + for (size_t i = 0; i < num_types; i++) { + if (types[i].type == field.numtype) { + field.meta = &types[i]; + break; + } + } + + if (!field.meta && !tlv_type_is_allowed(&field, extra_types)) { + SUPERVERBOSE("unknown even"); + if (err_type != NULL) + *err_type = field.numtype; + goto fail; + } + + /* We're happy with type field. Move on. */ + update_err_off(err_off, initial_len, *max); + field.length = fromwire_bigsize(cursor, max); /* BOLT #1: @@ -139,6 +212,8 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, */ if (!*cursor) { SUPERVERBOSE("length"); + if (err_type) + *err_type = field.numtype; goto fail; } @@ -149,28 +224,31 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, */ if (field.length > *max) { SUPERVERBOSE("value"); + if (err_type) + *err_type = field.numtype; goto fail; } - field.value = tal_dup_arr(record, u8, *cursor, field.length, 0); - /* BOLT #1: - * - if `type` is known: - * - MUST decode the next `length` bytes using the known - * encoding for `type`. - */ - field.meta = NULL; - for (size_t i = 0; i < num_types; i++) { - if (types[i].type == field.numtype) - field.meta = &types[i]; - } + /* We're happy with length field. Move on. */ + update_err_off(err_off, initial_len, *max); + + field.value = tal_dup_arr(record, u8, *cursor, field.length, 0); if (field.meta) { /* Length of message can't exceed 16 bits anyway. */ size_t tlvlen = field.length; + + /* We're happy with type field. Move on. */ + update_err_off(err_off, initial_len, *max); + + /* FIXME: We could add an err_off in here for more accuracy. */ field.meta->fromwire(cursor, &tlvlen, record); - if (!*cursor) + if (!*cursor) { + if (err_type != NULL) + *err_type = field.numtype; goto fail; + } /* BOLT #1: * - if `length` is not exactly equal to that required @@ -178,16 +256,25 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, * - MUST fail to parse the `tlv_stream`. */ if (tlvlen != 0) { + if (err_type != NULL) + *err_type = field.numtype; SUPERVERBOSE("greater than encoding length"); goto fail; } } else { + /* We're happy with type field. Move on. */ + update_err_off(err_off, initial_len, *max); + /* We didn't read from *cursor through a fromwire, so * update manually. */ *cursor += field.length; } /* We've read bytes in ->fromwire, so update max */ *max -= field.length; + + /* We're happy with contents. Move on. */ + update_err_off(err_off, initial_len, *max); + tal_arr_expand(fields, field); } return true; @@ -196,63 +283,6 @@ bool fromwire_tlv(const u8 **cursor, size_t *max, return false; } -static bool tlv_type_is_allowed(const struct tlv_field *f, u64 *extra_types) { - /* Simple case: we have internal meta fields or it's an odd field. */ - if (f->numtype % 2 != 0 || f->meta != NULL) - return true; - - if (extra_types == NULL) - return false; - - /* Now iterate through the extras and see if we should make an - * exception. */ - for (size_t i = 0; i < tal_count(extra_types); i++) - if (extra_types[i] == f->numtype) - return true; - return false; -} - -bool tlv_fields_valid(const struct tlv_field *fields, u64 *allow_extra, - size_t *err_index) -{ - size_t numfields = tal_count(fields); - bool first = true; - u64 prev_type = 0; - for (int i=0; inumtype <= prev_type) { - /* BOLT #1: - * - if decoded `type`s are not strictly-increasing - * (including situations when two or more occurrences - * of the same `type` are met): - * - MUST fail to parse the `tlv_stream`. - */ - if (f->numtype == prev_type) - SUPERVERBOSE("duplicate tlv type"); - else - SUPERVERBOSE("invalid ordering"); - if (err_index != NULL) - *err_index = i; - return false; - } - first = false; - prev_type = f->numtype; - } - return true; -} - void towire_tlv(u8 **pptr, const struct tlv_record_type *types, size_t num_types, const void *record) diff --git a/wire/tlvstream.h b/wire/tlvstream.h index 61addfea8a9f..e87862630523 100644 --- a/wire/tlvstream.h +++ b/wire/tlvstream.h @@ -38,15 +38,24 @@ struct tlv_field *tlv_make_fields_(const struct tlv_record_type *types, size_t num_types, const void *record); -/* Generic TLV decode/encode */ +/** + * fromwire_tlv: generic TLV decode engine + * @cursor: cursor to update (set to NULL if we fail). + * @max: max len to update (always set to 0 if we succeed). + * @types / @num_types: table of known tlv types + * @record: the tlv to hand to @type-specific decode + * @fields: the fields array to populate + * @extra_types: tal_arr or NULL of unknown types to allow + * @err_off: NULL, or set to offset in tlv stream which failed. + * @err_type: NULL, or set to tlv type which failed (or 0 if malformed) + */ bool fromwire_tlv(const u8 **cursor, size_t *max, const struct tlv_record_type *types, size_t num_types, - void *record, struct tlv_field **fields); + void *record, struct tlv_field **fields, + const u64 *extra_types, size_t *err_off, u64 *err_type); void towire_tlv(u8 **pptr, const struct tlv_record_type *types, size_t num_types, const void *record); -bool tlv_fields_valid(const struct tlv_field *fields, u64 *allow_extra, - size_t *err_index); /* Get the offset of this field: returns size of msg if not found (or * tlv malformed) */ From 93caef35842d4d20065e99faf24cba45349e352f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 13:14:38 +1030 Subject: [PATCH 0557/1530] common/tlvstream: allow fromwire_tlv to accept *any* unknown type. This is used for keysend if EXPERIMENTAL_FEATURES is set. Signed-off-by: Rusty Russell --- plugins/keysend.c | 22 +++++++++++----------- wire/tlvstream.c | 8 ++++++++ wire/tlvstream.h | 5 ++++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/plugins/keysend.c b/plugins/keysend.c index 44d99a5f8f3e..7dff550b289a 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -342,7 +343,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd, struct out_req *req; struct timeabs now = time_now(); const char *err; - u64 *allowed = tal_arr(cmd, u64, 1); + u64 *allowed; size_t err_off; u64 err_type; @@ -360,8 +361,15 @@ static struct command_result *htlc_accepted_call(struct command *cmd, return htlc_accepted_continue(cmd, NULL); } +#if EXPERIMENTAL_FEATURES + /* Note: This is a magic pointer value, not an actual array */ + allowed = cast_const(u64 *, FROMWIRE_TLV_ANY_TYPE); +#else /* We explicitly allow our type. */ - allowed[0] = 5482373484; + allowed = tal_arr(cmd, u64, 1); + allowed[0] = PREIMAGE_TLV_TYPE; +#endif + payload = tlv_tlv_payload_new(cmd); if (!fromwire_tlv(&rawpayload, &max, tlvs_tlv_tlv_payload, TLVS_ARRAY_SIZE_tlv_tlv_payload, payload, &payload->fields, allowed, &err_off, &err_type)) { @@ -380,6 +388,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd, preimage_field = field; break; } else if (field->numtype % 2 == 0 && field->meta == NULL) { + /* This can only happen with FROMWIRE_TLV_ANY_TYPE! */ unknown_field = field; } } @@ -390,19 +399,10 @@ static struct command_result *htlc_accepted_call(struct command *cmd, return htlc_accepted_continue(cmd, NULL); if (unknown_field != NULL) { -#if !EXPERIMENTAL_FEATURES - plugin_log(cmd->plugin, LOG_UNUSUAL, - "Payload contains unknown even TLV-type %" PRIu64 - ", can't safely accept the keysend. Deferring to " - "other plugins.", - unknown_field->numtype); - return htlc_accepted_continue(cmd, NULL); -#else plugin_log(cmd->plugin, LOG_INFORM, "Experimental: Accepting the keysend payment " "despite having unknown even TLV type %" PRIu64 ".", unknown_field->numtype); -#endif } /* If malformed (amt is compulsory), let lightningd handle it. */ diff --git a/wire/tlvstream.c b/wire/tlvstream.c index 1fff8415a49b..6e08981a5e1e 100644 --- a/wire/tlvstream.c +++ b/wire/tlvstream.c @@ -8,6 +8,11 @@ #define SUPERVERBOSE(...) #endif +/* This simply needs to be an address which is neither NULL nor a + * tal_arr return */ +static const u64 dummy; +const u64 *FROMWIRE_TLV_ANY_TYPE = &dummy; + static int tlv_field_cmp(const struct tlv_field *a, const struct tlv_field *b, void *x) { @@ -117,6 +122,9 @@ static bool tlv_type_is_allowed(const struct tlv_field *f, if (f->numtype % 2 != 0) return true; + if (extra_types == FROMWIRE_TLV_ANY_TYPE) + return true; + /* Now iterate through the extras and see if we should make an * exception. */ for (size_t i = 0; i < tal_count(extra_types); i++) diff --git a/wire/tlvstream.h b/wire/tlvstream.h index e87862630523..014d11c556f0 100644 --- a/wire/tlvstream.h +++ b/wire/tlvstream.h @@ -45,7 +45,7 @@ struct tlv_field *tlv_make_fields_(const struct tlv_record_type *types, * @types / @num_types: table of known tlv types * @record: the tlv to hand to @type-specific decode * @fields: the fields array to populate - * @extra_types: tal_arr or NULL of unknown types to allow + * @extra_types: tal_arr of unknown types to allow, or NULL, or FROMWIRE_TLV_ANY_TYPE. * @err_off: NULL, or set to offset in tlv stream which failed. * @err_type: NULL, or set to tlv type which failed (or 0 if malformed) */ @@ -61,6 +61,9 @@ void towire_tlv(u8 **pptr, * tlv malformed) */ size_t tlv_field_offset(const u8 *tlvstream, size_t tlvlen, u64 fieldtype); +/* Constant for fromwire_tlv to allow absolutely any unknown type. */ +extern const u64 *FROMWIRE_TLV_ANY_TYPE; + /* Generic primitive setters for tlvstreams. */ void tlvstream_set_raw(struct tlv_field **stream, u64 type, void *value TAKES, size_t valuelen); void tlvstream_set_short_channel_id(struct tlv_field **stream, u64 type, From 7147fea2fff249539788db47d4e44406417ef8b5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 13:14:38 +1030 Subject: [PATCH 0558/1530] common: add const to accepted_extra_tlvs arg. It was tlv_fields_valid that wanted a non-const: now that's gone, we can make this correctly const. Signed-off-by: Rusty Russell --- common/onion.c | 2 +- common/onion.h | 2 +- wallet/test/run-wallet.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/onion.c b/common/onion.c index 3fb4600f6822..18ba5e8066ec 100644 --- a/common/onion.c +++ b/common/onion.c @@ -201,7 +201,7 @@ struct onion_payload *onion_decode(const tal_t *ctx, const struct route_step *rs, const struct pubkey *blinding, const struct secret *blinding_ss, - u64 *accepted_extra_tlvs, + const u64 *accepted_extra_tlvs, u64 *failtlvtype, size_t *failtlvpos) { diff --git a/common/onion.h b/common/onion.h index bf8c3c430b78..46ccacb27dd5 100644 --- a/common/onion.h +++ b/common/onion.h @@ -69,7 +69,7 @@ struct onion_payload *onion_decode(const tal_t *ctx, const struct route_step *rs, const struct pubkey *blinding, const struct secret *blinding_ss, - u64 *accepted_extra_tlvs, + const u64 *accepted_extra_tlvs, u64 *failtlvtype, size_t *failtlvpos); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 6a74d2bab0c6..1bbdaf84f0fb 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -536,7 +536,7 @@ struct onion_payload *onion_decode(const tal_t *ctx UNNEEDED, const struct route_step *rs UNNEEDED, const struct pubkey *blinding UNNEEDED, const struct secret *blinding_ss UNNEEDED, - u64 *accepted_extra_tlvs UNNEEDED, + const u64 *accepted_extra_tlvs UNNEEDED, u64 *failtlvtype UNNEEDED, size_t *failtlvpos UNNEEDED) { fprintf(stderr, "onion_decode called!\n"); abort(); } From b19f3a5e7f4c1748dbd2ffcf738c65c937721408 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 13:14:38 +1030 Subject: [PATCH 0559/1530] devtools/decodemsg: don't require --onion for onion tlvs, fail if unknown tlvname. Generate a table, let decodemsg sort it out. Do more up-front work in argparsing too. Signed-off-by: Rusty Russell --- devtools/decodemsg.c | 68 +++++++++++++++++++++------------ tools/gen/print_header_template | 8 ++++ tools/gen/print_impl_template | 12 ++---- 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/devtools/decodemsg.c b/devtools/decodemsg.c index 1824c7f7edc3..b1e7205ad420 100644 --- a/devtools/decodemsg.c +++ b/devtools/decodemsg.c @@ -11,20 +11,46 @@ #include #endif +static char *opt_set_tlvname(const char *arg, + bool (**printwire)(const char *fieldname, + const u8 **cursor, + size_t *plen)) +{ + for (size_t i = 0; tlvs_printpeer_wire_byname[i].name; i++) { + if (streq(arg, tlvs_printpeer_wire_byname[i].name)) { + *printwire = tlvs_printpeer_wire_byname[i].print; + return NULL; + } + } + + for (size_t i = 0; tlvs_printonion_wire_byname[i].name; i++) { + if (streq(arg, tlvs_printonion_wire_byname[i].name)) { + *printwire = tlvs_printonion_wire_byname[i].print; + return NULL; + } + } + return "Unknown tlv name"; +} + +static char *opt_set_onionprint(bool (**printwire)(const u8 *msg)) +{ + *printwire = printonion_wire_message; + return NULL; +} + int main(int argc, char *argv[]) { const u8 *m; - bool onion = false; - char *tlv_name = NULL; + bool (*printtlv)(const char *fieldname, const u8 **cursor, size_t *plen) = NULL; + bool (*printwire)(const u8 *msg) = printpeer_wire_message; bool ok = true; setup_locale(); - opt_register_noarg("--onion", opt_set_bool, &onion, + opt_register_noarg("--onion", opt_set_onionprint, &printwire, "Decode an error message instead of a peer message"); - opt_register_arg("--tlv", opt_set_charp, opt_show_charp, - &tlv_name, - "Deocde a TLV of this type instead of a peer message"); + opt_register_arg("--tlv", opt_set_tlvname, NULL, &printtlv, + "Decode a TLV of this type instead of a peer message"); opt_register_noarg("--help|-h", opt_usage_and_exit, "[]" "Decode a lightning spec wire message from hex, or a series of messages from stdin", @@ -40,15 +66,12 @@ int main(int argc, char *argv[]) if (!m) errx(1, "'%s' is not valid hex", argv[1]); - if (onion) - if (tlv_name) - ok &= printonion_wire_tlv_message(tlv_name, m); - else - ok &= printonion_wire_message(m); - else if (tlv_name) - ok &= printpeer_wire_tlv_message(tlv_name, m); - else - ok &= printpeer_wire_message(m); + if (printtlv) { + size_t len = tal_bytelen(m); + ok &= printtlv("", &m, &len); + } else { + ok &= printwire(m); + } } else { u8 *f = grab_fd(NULL, STDIN_FILENO); size_t off = 0; @@ -69,15 +92,12 @@ int main(int argc, char *argv[]) break; } m = tal_dup_arr(f, u8, f + off, be16_to_cpu(len), 0); - if (onion) - if (tlv_name) - ok &= printonion_wire_tlv_message(tlv_name, m); - else - ok &= printonion_wire_message(m); - else if (tlv_name) - ok &= printpeer_wire_tlv_message(tlv_name, m); - else - ok &= printpeer_wire_message(m); + if (printtlv) { + size_t len = tal_bytelen(m); + ok &= printtlv("", &m, &len); + } else { + ok &= printwire(m); + } off += be16_to_cpu(len); tal_free(m); } diff --git a/tools/gen/print_header_template b/tools/gen/print_header_template index ba09bc127e11..4b2f87a261de 100644 --- a/tools/gen/print_header_template +++ b/tools/gen/print_header_template @@ -27,4 +27,12 @@ bool printwire_${tlv.name}(const char *fieldname, const u8 **cursor, size_t *ple bool printwire_${subtype.name}(const char *fieldname, const u8 **cursor, size_t *plen); % endfor % endif + +/* NULL-terminated map of TLV names -> print functions */ +struct tlv_print${options.enum_name}_byname { + const char *name; + bool (*print)(const char *fieldname, const u8 **cursor, size_t *plen); +}; +extern const struct tlv_print${options.enum_name}_byname tlvs_print${options.enum_name}_byname[]; + #endif /* LIGHTNING_${idem} */ diff --git a/tools/gen/print_impl_template b/tools/gen/print_impl_template index b4ad9d9306a6..3da32dd53ce6 100644 --- a/tools/gen/print_impl_template +++ b/tools/gen/print_impl_template @@ -126,13 +126,9 @@ bool printwire_${tlv.name}(const char *fieldname, const u8 **cursor, size_t *ple } % endfor -% if bool(tlvs): -bool print${options.enum_name}_tlv_message(const char *tlv_name, const u8 *msg) { - size_t len = tal_count(msg); +const struct tlv_print${options.enum_name}_byname tlvs_print${options.enum_name}_byname[] = { % for tlv in tlvs.values(): - if (strcmp(tlv_name, "${tlv.name}") == 0) - return printwire_${tlv.name}(tlv_name, &msg, &len); + { "${tlv.name}", printwire_${tlv.name} }, % endfor - return false; -} -% endif + { NULL, NULL } +}; From 5c20b8b1a33edf209dec4faa4f653c3a03973141 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Mar 2022 13:14:38 +1030 Subject: [PATCH 0560/1530] devtools/decodemsg: add --list-tlvs Signed-off-by: Rusty Russell --- devtools/decodemsg.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devtools/decodemsg.c b/devtools/decodemsg.c index b1e7205ad420..6559ca290015 100644 --- a/devtools/decodemsg.c +++ b/devtools/decodemsg.c @@ -38,6 +38,16 @@ static char *opt_set_onionprint(bool (**printwire)(const u8 *msg)) return NULL; } +static char *opt_list_tlvnames(void *unused) +{ + for (size_t i = 0; tlvs_printpeer_wire_byname[i].name; i++) + printf(" %s\n", tlvs_printpeer_wire_byname[i].name); + + for (size_t i = 0; tlvs_printonion_wire_byname[i].name; i++) + printf(" %s\n", tlvs_printonion_wire_byname[i].name); + exit(0); +} + int main(int argc, char *argv[]) { const u8 *m; @@ -51,6 +61,8 @@ int main(int argc, char *argv[]) "Decode an error message instead of a peer message"); opt_register_arg("--tlv", opt_set_tlvname, NULL, &printtlv, "Decode a TLV of this type instead of a peer message"); + opt_register_noarg("--list-tlvs", opt_list_tlvnames, NULL, + "List all --tlv names supported"); opt_register_noarg("--help|-h", opt_usage_and_exit, "[]" "Decode a lightning spec wire message from hex, or a series of messages from stdin", From 7abc491f4c214f2bcf9e8f2b9066ca05dee0342b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 24 Mar 2022 12:49:25 +1030 Subject: [PATCH 0561/1530] pay: fix crash on invalid onion in reply in some cases. fail can be NULL here. Reported-by: https://github.com/shafemtol Fixes: #5053 Signed-off-by: Rusty Russell --- lightningd/pay.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index 9c2c05157ca2..58463ee10da7 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -212,7 +212,7 @@ void json_sendpay_fail_fields(struct json_stream *js, json_add_payment_fields(js, payment); if (pay_errcode == PAY_UNPARSEABLE_ONION && onionreply) json_add_hex_talarr(js, "onionreply", onionreply->contents); - else + else if (fail) json_add_routefail_info(js, fail->erring_index, fail->failcode, From 290dfd2b815221e4409b608678518d9f47d6defe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 24 Mar 2022 09:54:38 +1030 Subject: [PATCH 0562/1530] doc/schemas: handle delinvoice of paid invoice. Signed-off-by: Rusty Russell --- doc/schemas/delinvoice.schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/schemas/delinvoice.schema.json b/doc/schemas/delinvoice.schema.json index 1803a7fbdfe8..2a86ad594906 100644 --- a/doc/schemas/delinvoice.schema.json +++ b/doc/schemas/delinvoice.schema.json @@ -101,6 +101,7 @@ "payment_hash": {}, "pay_index": {}, "amount_received_msat": {}, + "msatoshi_received": {}, "paid_at": {}, "payment_preimage": {} } @@ -145,6 +146,9 @@ "type": "msat", "description": "how much was actually received" }, + "msatoshi_received": { + "deprecated": "true" + }, "paid_at": { "type": "u64", "description": "UNIX timestamp of when payment was received" From ccaf04d268b786918bc912b72ccb742cea22a3ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 24 Mar 2022 10:27:20 +1030 Subject: [PATCH 0563/1530] invoice: add deschashonly parameter. LNURL wants this so they can include images etc in descriptions. Replaces: #4892 Changelog-Added: JSON-RPC: `invoice` has a new parameter `deschashonly` to put hash of description in bolt11. Signed-off-by: Rusty Russell --- common/bolt11.c | 11 +++++++--- contrib/pyln-client/pyln/client/lightning.py | 3 ++- doc/lightning-invoice.7.md | 12 ++++++++--- lightningd/invoice.c | 17 ++++++++++++++-- tests/test_invoices.py | 21 ++++++++++++++++++++ 5 files changed, 55 insertions(+), 9 deletions(-) diff --git a/common/bolt11.c b/common/bolt11.c index 5ec8f8f717c5..b33ac132ec1c 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -1119,11 +1119,16 @@ char *bolt11_encode_(const tal_t *ctx, /* Thus we do built-in fields, then extras last. */ encode_p(&data, &b11->payment_hash); - if (b11->description) - encode_d(&data, b11->description); - + /* BOLT #11: + * A writer: + *... + * - MUST include either exactly one `d` or exactly one `h` field. + */ + /* We sometimes keep description around (to put in db), so prefer hash */ if (b11->description_hash) encode_h(&data, b11->description_hash); + else if (b11->description) + encode_d(&data, b11->description); if (n_field) encode_n(&data, &b11->receiver_id); diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index e6d1dc5840fe..ed05203f5331 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -828,7 +828,7 @@ def help(self, command=None): return self.call("help", payload) def invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, - preimage=None, exposeprivatechannels=None, cltv=None): + preimage=None, exposeprivatechannels=None, cltv=None, deschashonly=None): """ Create an invoice for {msatoshi} with {label} and {description} with optional {expiry} seconds (default 1 week). @@ -842,6 +842,7 @@ def invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, "preimage": preimage, "exposeprivatechannels": exposeprivatechannels, "cltv": cltv, + "deschashonly": deschashonly, } return self.call("invoice", payload) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 5347625acebe..b52ed45b4ded 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -5,7 +5,7 @@ SYNOPSIS -------- **invoice** *msatoshi* *label* *description* [*expiry*] -[*fallbacks*] [*preimage*] [*exposeprivatechannels*] [*cltv*] +[*fallbacks*] [*preimage*] [*exposeprivatechannels*] [*cltv*] [*deschashonly*] DESCRIPTION ----------- @@ -29,8 +29,9 @@ of this invoice. The *description* is a short description of purpose of payment, e.g. *1 cup of coffee*. This value is encoded into the BOLT11 invoice and is -viewable by any node you send this invoice to. It must be UTF-8, and -cannot use *\\u* JSON escape codes. +viewable by any node you send this invoice to (unless *deschashonly* is +true as described below). It must be UTF-8, and cannot use *\\u* JSON +escape codes. The *expiry* is optionally the time the invoice is valid for; without a suffix it is interpreted as seconds, otherwise suffixes *s*, *m*, *h*, @@ -68,6 +69,11 @@ payment. If specified, *cltv* sets the *min_final_cltv_expiry* for the invoice. Otherwise, it's set to the parameter **cltv-final**. +If *deschash* is true (default false), then the bolt11 returned +contains a hash of the *description*, rather than the *description* +itself: this allows much longer descriptions, but they must be +communicated via some other mechanism. + RETURN VALUE ------------ diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 9e205d9de9e4..fadbb4c6f62b 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1135,6 +1135,7 @@ static struct command_result *json_invoice(struct command *cmd, u32 *cltv; struct jsonrpc_request *req; struct plugin *plugin; + bool *hashonly; #if DEVELOPER const jsmntok_t *routes; #endif @@ -1153,6 +1154,7 @@ static struct command_result *json_invoice(struct command *cmd, &info->chanhints), p_opt_def("cltv", param_number, &cltv, cmd->ld->config.cltv_final), + p_opt_def("deschashonly", param_bool, &hashonly, false), #if DEVELOPER p_opt("dev-routes", param_array, &routes), #endif @@ -1165,7 +1167,7 @@ static struct command_result *json_invoice(struct command *cmd, INVOICE_MAX_LABEL_LEN); } - if (strlen(desc_val) > BOLT11_FIELD_BYTE_LIMIT) { + if (strlen(desc_val) > BOLT11_FIELD_BYTE_LIMIT && !*hashonly) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Descriptions greater than %d bytes " "not yet supported " @@ -1207,7 +1209,18 @@ static struct command_result *json_invoice(struct command *cmd, info->b11->min_final_cltv_expiry = *cltv; info->b11->expiry = *expiry; info->b11->description = tal_steal(info->b11, desc_val); - info->b11->description_hash = NULL; + /* BOLT #11: + * * `h` (23): `data_length` 52. 256-bit description of purpose of payment (SHA256). + *... + * A writer: + *... + * - MUST include either exactly one `d` or exactly one `h` field. + */ + if (*hashonly) { + info->b11->description_hash = tal(info->b11, struct sha256); + sha256(info->b11->description_hash, desc_val, strlen(desc_val)); + } else + info->b11->description_hash = NULL; info->b11->payment_secret = tal_dup(info->b11, struct secret, &payment_secret); info->b11->features = tal_dup_talarr(info->b11, u8, diff --git a/tests/test_invoices.py b/tests/test_invoices.py index d31fade615b0..6cbf960d61f6 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -712,3 +712,24 @@ def match(node, query, invoice): for q in queries: r = l1.rpc.listinvoices(**q) assert len(r['invoices']) == 0 + + +def test_invoice_deschash(node_factory, chainparams): + l1, l2 = node_factory.line_graph(2) + + # BOLT #11: + # * `h`: tagged field: hash of description + # * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52) + # * `8yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs`: SHA256 of 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon' + inv = l2.rpc.invoice(42, 'label', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon', deschashonly=True) + assert '8yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs' in inv['bolt11'] + + b11 = l2.rpc.decodepay(inv['bolt11']) + assert 'description' not in b11 + assert b11['description_hash'] == '3925b6f67e2c340036ed12093dd44e0368df1b6ea26c53dbe4811f58fd5db8c1' + + listinv = only_one(l2.rpc.listinvoices()['invoices']) + assert listinv['description'] == 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon' + + # Make sure we can pay it! + l1.rpc.pay(inv['bolt11']) From aad4495f564fb61405b42a6aa1c91797915843bc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 24 Mar 2022 10:27:28 +1030 Subject: [PATCH 0564/1530] delinvoice: allow desconly arg to only remove the description. Means that field is now optional in JSON output. Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `delinvoice` has a new parameter `desconly` to remove description. --- common/jsonrpc_errors.h | 1 + contrib/pyln-client/pyln/client/lightning.py | 7 +++-- doc/lightning-delinvoice.7.md | 15 ++++++---- doc/lightning-listinvoices.7.md | 4 +-- doc/schemas/listinvoices.schema.json | 1 - lightningd/invoice.c | 31 +++++++++++++++----- lightningd/test/run-invoice-select-inchan.c | 10 +++++-- tests/test_invoices.py | 7 +++++ wallet/invoices.c | 23 +++++++++++++-- wallet/invoices.h | 17 +++++++++-- wallet/test/run-wallet.c | 10 +++++-- wallet/wallet.c | 11 +++++-- wallet/wallet.h | 9 ++++-- 13 files changed, 110 insertions(+), 36 deletions(-) diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index f7cb09776738..47a56e69e8a8 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -77,6 +77,7 @@ static const errcode_t INVOICE_WAIT_TIMED_OUT = 904; static const errcode_t INVOICE_NOT_FOUND = 905; static const errcode_t INVOICE_STATUS_UNEXPECTED = 906; static const errcode_t INVOICE_OFFER_INACTIVE = 907; +static const errcode_t INVOICE_NO_DESCRIPTION = 908; /* Errors from HSM crypto operations. */ static const errcode_t HSM_ECDH_FAILED = 800; diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index ed05203f5331..f7567fcf5c4b 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -566,13 +566,14 @@ def delexpiredinvoice(self, maxexpirytime=None): } return self.call("delexpiredinvoice", payload) - def delinvoice(self, label, status): + def delinvoice(self, label, status, desconly=None): """ - Delete unpaid invoice {label} with {status}. + Delete unpaid invoice {label} with {status} (or, with {desconly} true, remove its description). """ payload = { "label": label, - "status": status + "status": status, + "desconly": desconly, } return self.call("delinvoice", payload) diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 935c3e80d2cb..fe446756a6e0 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -1,20 +1,24 @@ -lightning-delinvoice -- Command for removing an invoice -======================================================= +lightning-delinvoice -- Command for removing an invoice (or just its description) +================================================================================= SYNOPSIS -------- -**delinvoice** *label* *status* +**delinvoice** *label* *status* [*desconly*] DESCRIPTION ----------- The **delinvoice** RPC command removes an invoice with *status* as given -in **listinvoices**. +in **listinvoices**, or with *desconly* set, removes its description. The caller should be particularly aware of the error case caused by the *status* changing just before this command is invoked! +If *desconly* is set, the invoice is not deleted, but has its +description removed (this can save space with very large descriptions, +as would be used with lightning-invoice(7) *deschashonly*. + RETURN VALUE ------------ @@ -55,6 +59,7 @@ The following errors may be reported: *current_status* and *expected_status* fields. This is most likely due to the *status* of the invoice changing just before this command is invoked. +- 908: The invoice already has no description, and *desconly* was set. AUTHOR ------ @@ -73,4 +78,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cd3b009a6ef0c220ca21c6d8e3a5716ca2080997016cf00a2e26defc03cfac73) +[comment]: # ( SHA256STAMP:73d9097734e85d438de90844ab78bdd737e6df53620542887170c7564a86b90b) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index 059d460fd978..63cfe33d1de0 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -23,10 +23,10 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **invoices** is returned. It is an array of objects, where each object contains: - **label** (string): unique label supplied at invoice creation -- **description** (string): description used in the invoice - **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid, unpaid or unpayable (one of "unpaid", "paid", "expired") - **expires_at** (u64): UNIX timestamp of when it will become / became unpayable +- **description** (string, optional): description used in the invoice - **amount_msat** (msat, optional): the amount required to pay this invoice - **bolt11** (string, optional): the BOLT11 string (always present unless *bolt12* is) - **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is) @@ -56,4 +56,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3dc5d5b8f7796d29e0d174d96e93915cbc7131b173a1547de022e021c55e8db6) +[comment]: # ( SHA256STAMP:d1328ecc2a4e76ede8c9adc3a63d18ce36be305ddcee7cf717039f79642cfd41) diff --git a/doc/schemas/listinvoices.schema.json b/doc/schemas/listinvoices.schema.json index bbd505684058..9afd91299215 100644 --- a/doc/schemas/listinvoices.schema.json +++ b/doc/schemas/listinvoices.schema.json @@ -13,7 +13,6 @@ "additionalProperties": true, "required": [ "label", - "description", "payment_hash", "status", "expires_at" diff --git a/lightningd/invoice.c b/lightningd/invoice.c index fadbb4c6f62b..1c95e48d4abb 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1382,15 +1382,17 @@ static struct command_result *json_delinvoice(struct command *cmd, const jsmntok_t *params) { struct invoice i; - const struct invoice_details *details; + struct invoice_details *details; struct json_stream *response; const char *status, *actual_status; struct json_escape *label; struct wallet *wallet = cmd->ld->wallet; + bool *deldesc; if (!param(cmd, buffer, params, p_req("label", param_label, &label), p_req("status", param_string, &status), + p_opt_def("desconly", param_bool, &deldesc, false), NULL)) return command_param_failed(); @@ -1415,12 +1417,27 @@ static struct command_result *json_delinvoice(struct command *cmd, return command_failed(cmd, js); } - if (!wallet_invoice_delete(wallet, i)) { - log_broken(cmd->ld->log, - "Error attempting to remove invoice %"PRIu64, - i.id); - /* FIXME: allocate a generic DATABASE_ERROR code. */ - return command_fail(cmd, LIGHTNINGD, "Database error"); + if (*deldesc) { + if (!details->description) + return command_fail(cmd, INVOICE_NO_DESCRIPTION, + "Invoice description already removed"); + + if (!wallet_invoice_delete_description(wallet, i)) { + log_broken(cmd->ld->log, + "Error attempting to delete description of invoice %"PRIu64, + i.id); + /* FIXME: allocate a generic DATABASE_ERROR code. */ + return command_fail(cmd, LIGHTNINGD, "Database error"); + } + details->description = tal_free(details->description); + } else { + if (!wallet_invoice_delete(wallet, i)) { + log_broken(cmd->ld->log, + "Error attempting to remove invoice %"PRIu64, + i.id); + /* FIXME: allocate a generic DATABASE_ERROR code. */ + return command_fail(cmd, LIGHTNINGD, "Database error"); + } } response = json_stream_success(cmd); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index ba9193ef079a..976af4bb8f54 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -753,14 +753,18 @@ bool wallet_invoice_create(struct wallet *wallet UNNEEDED, bool wallet_invoice_delete(struct wallet *wallet UNNEEDED, struct invoice invoice UNNEEDED) { fprintf(stderr, "wallet_invoice_delete called!\n"); abort(); } +/* Generated stub for wallet_invoice_delete_description */ +bool wallet_invoice_delete_description(struct wallet *wallet UNNEEDED, + struct invoice invoice UNNEEDED) +{ fprintf(stderr, "wallet_invoice_delete_description called!\n"); abort(); } /* Generated stub for wallet_invoice_delete_expired */ void wallet_invoice_delete_expired(struct wallet *wallet UNNEEDED, u64 max_expiry_time UNNEEDED) { fprintf(stderr, "wallet_invoice_delete_expired called!\n"); abort(); } /* Generated stub for wallet_invoice_details */ -const struct invoice_details *wallet_invoice_details(const tal_t *ctx UNNEEDED, - struct wallet *wallet UNNEEDED, - struct invoice invoice UNNEEDED) +struct invoice_details *wallet_invoice_details(const tal_t *ctx UNNEEDED, + struct wallet *wallet UNNEEDED, + struct invoice invoice UNNEEDED) { fprintf(stderr, "wallet_invoice_details called!\n"); abort(); } /* Generated stub for wallet_invoice_find_by_label */ bool wallet_invoice_find_by_label(struct wallet *wallet UNNEEDED, diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 6cbf960d61f6..e02dd2561926 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -733,3 +733,10 @@ def test_invoice_deschash(node_factory, chainparams): # Make sure we can pay it! l1.rpc.pay(inv['bolt11']) + + # Try removing description. + l2.rpc.delinvoice('label', "paid", desconly=True) + assert 'description' not in only_one(l2.rpc.listinvoices()['invoices']) + + with pytest.raises(RpcError, match=r'description already removed'): + l2.rpc.delinvoice('label', "paid", desconly=True) diff --git a/wallet/invoices.c b/wallet/invoices.c index f8d46d8d6b99..2727429a38e9 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -421,6 +421,23 @@ bool invoices_delete(struct invoices *invoices, struct invoice invoice) return true; } +bool invoices_delete_description(struct invoices *invoices, struct invoice invoice) +{ + struct db_stmt *stmt; + int changes; + + stmt = db_prepare_v2(invoices->db, SQL("UPDATE invoices" + " SET description = NULL" + " WHERE ID = ?;")); + db_bind_u64(stmt, 0, invoice.id); + db_exec_prepared_v2(stmt); + + changes = db_count_changes(stmt); + tal_free(stmt); + + return changes == 1; +} + void invoices_delete_expired(struct invoices *invoices, u64 max_expiry_time) { @@ -648,9 +665,9 @@ void invoices_waitone(const tal_t *ctx, false, invoice.id, cb, cbarg); } -const struct invoice_details *invoices_get_details(const tal_t *ctx, - struct invoices *invoices, - struct invoice invoice) +struct invoice_details *invoices_get_details(const tal_t *ctx, + struct invoices *invoices, + struct invoice invoice) { struct db_stmt *stmt; bool res; diff --git a/wallet/invoices.h b/wallet/invoices.h index 00acf384c772..7aa584452b21 100644 --- a/wallet/invoices.h +++ b/wallet/invoices.h @@ -108,6 +108,17 @@ bool invoices_find_unpaid(struct invoices *invoices, bool invoices_delete(struct invoices *invoices, struct invoice invoice); +/** + * invoices_delete_description - Remove description from an invoice + * + * @invoices - the invoice handler. + * @invoice - the invoice to remove description from. + * + * Return false on failure. + */ +bool invoices_delete_description(struct invoices *invoices, + struct invoice invoice); + /** * invoices_delete_expired - Delete all expired invoices * with expiration time less than or equal to the given. @@ -213,8 +224,8 @@ void invoices_waitone(const tal_t *ctx, * @invoice - the invoice to get details on. * @return pointer to the invoice details allocated off of `ctx`. */ -const struct invoice_details *invoices_get_details(const tal_t *ctx, - struct invoices *invoices, - struct invoice invoice); +struct invoice_details *invoices_get_details(const tal_t *ctx, + struct invoices *invoices, + struct invoice invoice); #endif /* LIGHTNING_WALLET_INVOICES_H */ diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 1bbdaf84f0fb..f095d170c796 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -212,6 +212,10 @@ bool invoices_create(struct invoices *invoices UNNEEDED, bool invoices_delete(struct invoices *invoices UNNEEDED, struct invoice invoice UNNEEDED) { fprintf(stderr, "invoices_delete called!\n"); abort(); } +/* Generated stub for invoices_delete_description */ +bool invoices_delete_description(struct invoices *invoices UNNEEDED, + struct invoice invoice UNNEEDED) +{ fprintf(stderr, "invoices_delete_description called!\n"); abort(); } /* Generated stub for invoices_delete_expired */ void invoices_delete_expired(struct invoices *invoices UNNEEDED, u64 max_expiry_time UNNEEDED) @@ -232,9 +236,9 @@ bool invoices_find_unpaid(struct invoices *invoices UNNEEDED, const struct sha256 *rhash UNNEEDED) { fprintf(stderr, "invoices_find_unpaid called!\n"); abort(); } /* Generated stub for invoices_get_details */ -const struct invoice_details *invoices_get_details(const tal_t *ctx UNNEEDED, - struct invoices *invoices UNNEEDED, - struct invoice invoice UNNEEDED) +struct invoice_details *invoices_get_details(const tal_t *ctx UNNEEDED, + struct invoices *invoices UNNEEDED, + struct invoice invoice UNNEEDED) { fprintf(stderr, "invoices_get_details called!\n"); abort(); } /* Generated stub for invoices_iterate */ bool invoices_iterate(struct invoices *invoices UNNEEDED, diff --git a/wallet/wallet.c b/wallet/wallet.c index 47e43bfabaa7..8a71c4113015 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2850,6 +2850,11 @@ bool wallet_invoice_delete(struct wallet *wallet, { return invoices_delete(wallet->invoices, invoice); } +bool wallet_invoice_delete_description(struct wallet *wallet, + struct invoice invoice) +{ + return invoices_delete_description(wallet->invoices, invoice); +} void wallet_invoice_delete_expired(struct wallet *wallet, u64 e) { invoices_delete_expired(wallet->invoices, e); @@ -2888,9 +2893,9 @@ void wallet_invoice_waitone(const tal_t *ctx, invoices_waitone(ctx, wallet->invoices, invoice, cb, cbarg); } -const struct invoice_details *wallet_invoice_details(const tal_t *ctx, - struct wallet *wallet, - struct invoice invoice) +struct invoice_details *wallet_invoice_details(const tal_t *ctx, + struct wallet *wallet, + struct invoice invoice) { return invoices_get_details(ctx, wallet->invoices, invoice); } diff --git a/wallet/wallet.h b/wallet/wallet.h index ad73893ca46c..6eea26b67e8e 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -893,6 +893,9 @@ bool wallet_invoice_find_unpaid(struct wallet *wallet, bool wallet_invoice_delete(struct wallet *wallet, struct invoice invoice); +bool wallet_invoice_delete_description(struct wallet *wallet, + struct invoice invoice); + /** * wallet_invoice_delete_expired - Delete all expired invoices * with expiration time less than or equal to the given. @@ -999,9 +1002,9 @@ void wallet_invoice_waitone(const tal_t *ctx, * @invoice - the invoice to get details on. * @return pointer to the invoice details allocated off of `ctx`. */ -const struct invoice_details *wallet_invoice_details(const tal_t *ctx, - struct wallet *wallet, - struct invoice invoice); +struct invoice_details *wallet_invoice_details(const tal_t *ctx, + struct wallet *wallet, + struct invoice invoice); /** * wallet_htlc_stubs - Retrieve HTLC stubs for the given channel From 7e6893af9ee5424bbab0ab1ee8def39e3d1f9da9 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 11 Mar 2022 14:09:26 +0100 Subject: [PATCH 0565/1530] rust: fixed compiler warning in the example Changelog-None: rust: fixed compiler warning in the example Signed-off-by: Vincenzo Palazzo --- cln-rpc/examples/getinfo.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cln-rpc/examples/getinfo.rs b/cln-rpc/examples/getinfo.rs index 5cbebdfaf1a4..9e61e22b4315 100644 --- a/cln-rpc/examples/getinfo.rs +++ b/cln-rpc/examples/getinfo.rs @@ -6,7 +6,9 @@ use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { + // initialize the log inside the library env_logger::init(); + let rpc_path = args().nth(1).context("missing argument: socket path")?; let p = Path::new(&rpc_path); From 9e11ae1a0b4ae7a0879e3b09c1be97d09c9d4c5d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Mar 2022 13:03:03 +1030 Subject: [PATCH 0566/1530] plugins/topology: don't get upset if we hit deleted channel. There's a race under CI, where a channel is deleted then we see the channel_update in the gossip store. We assumed this wouldn't happen, but it can! ``` [gw1] [ 95%] FAILED tests/test_connection.py::test_multichan [gw1] [ 95%] ERROR tests/test_connection.py::test_multichan ... > raise ValueError(str(errors)) E ValueError: E Node errors: E - lightningd-3: had BROKEN messages E - lightningd-3: Node exited with return code 1 E Global errors: ... lightningd-3: 2022-03-28T00:11:42.160Z DEBUG wallet: Owning output 0 100000sat (SEGWIT) txid 30616903feba1839a3834e2b3b6123759ce1fe0d76414ca77e2dbc17414772e0 CONFIRMED lightningd-3: 2022-03-28T00:11:42.392Z DEBUG hsmd: Client: Received message 5 from client lightningd-3: 2022-03-28T00:11:42.393Z DEBUG hsmd: new_client: 2 lightningd-3: 2022-03-28T00:11:42.398Z INFO plugin-topology: Killing plugin: exited during normal operation lightningd-3: 2022-03-28T00:11:42.400Z **BROKEN** plugin-topology: Plugin marked as important, shutting down lightningd! ... ----------------------------- Captured stderr call ----------------------------- topology: update for channel 105x1x1 not found! ``` Signed-off-by: Rusty Russell --- common/gossmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/gossmap.c b/common/gossmap.c index ff27b974b2ac..277e87d990b0 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -470,9 +470,9 @@ static bool update_channel(struct gossmap *map, size_t cupdate_off) scid.u64 = map_be64(map, scid_off); chan = gossmap_find_chan(map, &scid); + /* This can happen if channel gets deleted! */ if (!chan) - errx(1, "update for channel %s not found!", - type_to_string(tmpctx, struct short_channel_id, &scid)); + return false; /* We round this *down*, since too-low min is more conservative */ hc.htlc_min = u64_to_fp16(map_be64(map, htlc_minimum_off), false); From 20392ae526e4540245f1e0e4ba112c034e6f71d0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Mar 2022 09:40:54 +1030 Subject: [PATCH 0567/1530] connectd: restore obs2 onion support. I removed these prematurely: we *haven't* had a release since introducing them! This consists of reverting d15d629b8bbbf3e36fc69f75991de18cc6a6c93e "plugins/fetchinvoice: remove obsolete string-based API." and plugins/fetchinvoice: remove obsolete string-based API. "onion_messages: remove obs2 support." Some minor changes due to updated fromwire_tlv API since they were removed, but not much. Signed-off-by: Rusty Russell Changelog-EXPERIMENTAL: REVERT: Removed backwards compat with onion messages from v0.10.1. --- common/blindedpath.c | 157 ++++++++ common/blindedpath.h | 32 ++ common/json_helpers.c | 36 ++ common/json_helpers.h | 4 + connectd/connectd_wire.csv | 2 + connectd/multiplex.c | 3 + connectd/onion_message.c | 162 ++++++++- connectd/onion_message.h | 4 +- connectd/test/run-onion_message.c | 414 ++++++++++++++++++++++ lightningd/lightningd.c | 1 + lightningd/lightningd.h | 2 +- lightningd/onion_message.c | 164 +++++++-- lightningd/options.c | 3 + plugins/fetchinvoice.c | 134 ++++++- plugins/offers.c | 97 ++++- plugins/offers.h | 4 +- plugins/offers_inv_hook.c | 17 +- plugins/offers_inv_hook.h | 3 +- plugins/offers_invreq_hook.c | 21 +- plugins/offers_invreq_hook.h | 3 +- tests/test_pay.py | 39 +- wire/extracted_onion_01_offers.patch | 22 +- wire/extracted_onion_02_modernonion.patch | 6 +- wire/extracted_onion_exp_enctlv.patch | 7 +- wire/onion_wire.csv | 20 ++ 25 files changed, 1256 insertions(+), 101 deletions(-) create mode 100644 connectd/test/run-onion_message.c diff --git a/common/blindedpath.c b/common/blindedpath.c index 1976bca46e10..149a107a0252 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -114,6 +114,19 @@ static u8 *enctlv_from_encmsg_raw(const tal_t *ctx, return ret; } +static u8 *enctlv_from_obs2_encmsg(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct tlv_obs2_encmsg_tlvs *encmsg, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + u8 *encmsg_raw = tal_arr(NULL, u8, 0); + towire_tlv_obs2_encmsg_tlvs(&encmsg_raw, encmsg); + return enctlv_from_encmsg_raw(ctx, blinding, node, take(encmsg_raw), + next_blinding, node_alias); +} + static u8 *enctlv_from_encmsg(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *node, @@ -183,6 +196,22 @@ static u8 *decrypt_encmsg_raw(const tal_t *ctx, return dec; } +static struct tlv_obs2_encmsg_tlvs *decrypt_obs2_encmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) +{ + const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); + size_t maxlen = tal_bytelen(cursor); + + /* BOLT-onion-message #4: + * + * - if the `enctlv` is not a valid TLV... + * - MUST drop the message. + */ + return fromwire_tlv_obs2_encmsg_tlvs(ctx, &cursor, &maxlen); +} + static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, const struct pubkey *blinding, const struct secret *ss, @@ -325,3 +354,131 @@ u8 *create_final_enctlv(const tal_t *ctx, return enctlv_from_encmsg(ctx, blinding, final_node, encmsg, &unused_next_blinding, node_alias); } + +/* Obsolete variants */ +bool decrypt_obs2_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) +{ + struct tlv_obs2_encmsg_tlvs *encmsg; + + encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + /* BOLT-onion-message #4: + * + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` ... does not contain + * `next_node_id`: + * - MUST drop the message. + */ + if (!encmsg->next_node_id) + return false; + + /* BOLT-onion-message #4: + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` contains `self_id`: + * - MUST drop the message. + */ + if (encmsg->self_id) + return false; + + /* BOLT-onion-message #4: + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if `blinding` is specified in the `enctlv`: + * - MUST pass that as `blinding` in the `onion_message` + * - otherwise: + * - MUST pass `blinding` derived as in + * [Route Blinding][route-blinding] (i.e. + * `E(i+1) = H(E(i) || ss(i)) * E(i)`). + */ + *next_node = *encmsg->next_node_id; + if (encmsg->next_blinding) + *next_blinding = *encmsg->next_blinding; + else { + /* E(i-1) = H(E(i) || ss(i)) * E(i) */ + struct sha256 h; + blinding_hash_e_and_ss(blinding, ss, &h); + blinding_next_pubkey(blinding, &h, next_blinding); + } + return true; +} + +bool decrypt_obs2_final_enctlv(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **self_id) +{ + struct tlv_obs2_encmsg_tlvs *encmsg; + struct secret node_id_blinding; + + /* Repeat the tweak to get the alias it was using for us */ + subkey_from_hmac("blinded_node_id", ss, &node_id_blinding); + *alias = *my_id; + if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, + &alias->pubkey, + node_id_blinding.data) != 1) + return false; + + encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + if (tal_bytelen(encmsg->self_id) == sizeof(**self_id)) { + *self_id = tal(ctx, struct secret); + memcpy(*self_id, encmsg->self_id, sizeof(**self_id)); + } else + *self_id = NULL; + + return true; +} + +u8 *create_obs2_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct pubkey *next_node, + size_t padlen, + const struct pubkey *override_blinding, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); + if (padlen) + encmsg->padding = tal_arrz(encmsg, u8, padlen); + encmsg->next_node_id = cast_const(struct pubkey *, next_node); + encmsg->next_blinding = cast_const(struct pubkey *, override_blinding); + + return enctlv_from_obs2_encmsg(ctx, blinding, node, encmsg, + next_blinding, node_alias); +} + +u8 *create_obs2_final_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *final_node, + size_t padlen, + const struct secret *self_id, + struct pubkey *node_alias) +{ + struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); + struct privkey unused_next_blinding; + + if (padlen) + encmsg->padding = tal_arrz(encmsg, u8, padlen); + if (self_id) + encmsg->self_id = (u8 *)tal_dup(encmsg, struct secret, self_id); + + return enctlv_from_obs2_encmsg(ctx, blinding, final_node, encmsg, + &unused_next_blinding, node_alias); +} diff --git a/common/blindedpath.h b/common/blindedpath.h index 285ec189d972..f3415939551f 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -105,4 +105,36 @@ bool decrypt_final_enctlv(const tal_t *ctx, struct secret **path_id) NON_NULL_ARGS(1, 2, 4, 5); +/* Obsolete variants */ +u8 *create_obs2_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct pubkey *next_node, + size_t padlen, + const struct pubkey *override_blinding, + struct privkey *next_blinding, + struct pubkey *node_alias) + NON_NULL_ARGS(2, 3, 4, 7, 8); +u8 *create_obs2_final_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *final_node, + size_t padlen, + const struct secret *self_id, + struct pubkey *node_alias) + NON_NULL_ARGS(2, 3, 6); +bool decrypt_obs2_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) + NON_NULL_ARGS(1, 2, 4, 5); +bool decrypt_obs2_final_enctlv(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **self_id) + NON_NULL_ARGS(1, 2, 4, 5); + #endif /* LIGHTNING_COMMON_BLINDEDPATH_H */ diff --git a/common/json_helpers.c b/common/json_helpers.c index 90e570b5c6e4..af06c2036a2e 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -158,6 +158,42 @@ struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, return psbt_from_b64(ctx, buffer + tok->start, tok->end - tok->start); } +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + struct tlv_obs2_onionmsg_payload_reply_path *rpath; + const jsmntok_t *hops, *t; + size_t i; + const char *err; + + rpath = tal(ctx, struct tlv_obs2_onionmsg_payload_reply_path); + err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", + JSON_SCAN(json_to_pubkey, &rpath->blinding), + JSON_SCAN(json_to_pubkey, &rpath->first_node_id), + NULL); + if (err) + return tal_free(rpath); + + hops = json_get_member(buffer, tok, "hops"); + if (!hops || hops->size < 1) + return tal_free(rpath); + + rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); + json_for_each_arr(i, t, hops) { + rpath->path[i] = tal(rpath->path, struct onionmsg_path); + err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", + JSON_SCAN(json_to_pubkey, + &rpath->path[i]->node_id), + JSON_SCAN_TAL(rpath->path[i], + json_tok_bin_from_hex, + &rpath->path[i]->encrypted_recipient_data)); + if (err) + return tal_free(rpath); + } + + return rpath; +} + struct tlv_onionmsg_payload_reply_path * json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) { diff --git a/common/json_helpers.h b/common/json_helpers.h index d8edee866956..1f5fe7fe3239 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -88,6 +88,10 @@ bool split_tok(const char *buffer, const jsmntok_t *tok, struct tlv_onionmsg_payload_reply_path * json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); +/* Obsolete version! */ +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); + /* Helpers for outputting JSON results */ /* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index b10e668a14ba..5b0950f9376e 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -122,6 +122,7 @@ msgdata,connectd_ping_reply,totlen,u16, # We tell lightningd we got an onionmsg msgtype,connectd_got_onionmsg_to_us,2145 +msgdata,connectd_got_onionmsg_to_us,obs2,bool, msgdata,connectd_got_onionmsg_to_us,node_alias,pubkey, msgdata,connectd_got_onionmsg_to_us,self_id,?secret, msgdata,connectd_got_onionmsg_to_us,reply_blinding,?pubkey, @@ -133,6 +134,7 @@ msgdata,connectd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len # Lightningd tells us to send an onion message. msgtype,connectd_send_onionmsg,2041 +msgdata,connectd_send_onionmsg,obs2,bool, msgdata,connectd_send_onionmsg,id,node_id, msgdata,connectd_send_onionmsg,onion_len,u16, msgdata,connectd_send_onionmsg,onion,u8,onion_len diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 03af2a0fb844..c39c35ea094c 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -633,6 +633,9 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) } else if (type == WIRE_PONG) { handle_pong_in(peer, msg); return true; + } else if (type == WIRE_OBS2_ONION_MESSAGE) { + handle_obs2_onion_message(peer->daemon, peer, msg); + return true; } else if (type == WIRE_ONION_MESSAGE) { handle_onion_message(peer->daemon, peer, msg); return true; diff --git a/connectd/onion_message.c b/connectd/onion_message.c index d84349d53a4b..cc8cce571955 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -16,21 +16,178 @@ #include #include +/* Peer sends obsolete onion msg. */ +void handle_obs2_onion_message(struct daemon *daemon, + struct peer *peer, const u8 *msg) +{ + enum onion_wire badreason; + struct onionpacket *op; + struct pubkey blinding, ephemeral; + struct route_step *rs; + u8 *onion; + struct tlv_obs2_onionmsg_payload *om; + struct secret ss, onion_ss; + const u8 *cursor; + size_t max, maxlen; + + /* Ignore unless explicitly turned on. */ + if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], + OPT_ONION_MESSAGES)) + return; + + /* FIXME: ratelimit! */ + if (!fromwire_obs2_onion_message(msg, msg, &blinding, &onion)) { + inject_peer_msg(peer, + towire_warningfmt(NULL, NULL, + "Bad onion_message")); + return; + } + + /* We unwrap the onion now. */ + op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); + if (!op) { + status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", + onion_wire_name(badreason)); + return; + } + + ephemeral = op->ephemeralkey; + if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { + status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); + return; + } + + /* Now get onion shared secret and parse it. */ + ecdh(&ephemeral, &onion_ss); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + if (!rs) { + status_peer_debug(&peer->id, + "onion msg: can't process onionpacket ss=%s", + type_to_string(tmpctx, struct secret, &onion_ss)); + return; + } + + /* The raw payload is prepended with length in the modern onion. */ + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + if (!cursor) { + status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + if (maxlen > max) { + status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + om = fromwire_tlv_obs2_onionmsg_payload(msg, &cursor, &maxlen); + if (!om) { + status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", + tal_hex(tmpctx, rs->raw_payload)); + return; + } + + if (rs->nextcase == ONION_END) { + struct pubkey *reply_blinding, *first_node_id, me, alias; + const struct onionmsg_path **reply_path; + struct secret *self_id; + u8 *omsg; + + if (!pubkey_from_node_id(&me, &daemon->id)) { + status_broken("Failed to convert own id"); + return; + } + + /* Final enctlv is actually optional */ + if (!om->enctlv) { + alias = me; + self_id = NULL; + } else if (!decrypt_obs2_final_enctlv(tmpctx, &blinding, &ss, + om->enctlv, &me, &alias, + &self_id)) { + status_peer_debug(&peer->id, + "onion msg: failed to decrypt enctlv" + " %s", tal_hex(tmpctx, om->enctlv)); + return; + } + + if (om->reply_path) { + first_node_id = &om->reply_path->first_node_id; + reply_blinding = &om->reply_path->blinding; + reply_path = cast_const2(const struct onionmsg_path **, + om->reply_path->path); + } else { + first_node_id = NULL; + reply_blinding = NULL; + reply_path = NULL; + } + + /* We re-marshall here by policy, before handing to lightningd */ + omsg = tal_arr(tmpctx, u8, 0); + towire_tlvstream_raw(&omsg, om->fields); + daemon_conn_send(daemon->master, + take(towire_connectd_got_onionmsg_to_us(NULL, + true, /* obs2 */ + &alias, self_id, + reply_blinding, + first_node_id, + reply_path, + omsg))); + } else { + struct pubkey next_node, next_blinding; + struct peer *next_peer; + struct node_id next_node_id; + + /* This fails as expected if no enctlv. */ + if (!decrypt_obs2_enctlv(&blinding, &ss, om->enctlv, &next_node, + &next_blinding)) { + status_peer_debug(&peer->id, + "onion msg: invalid enctlv %s", + tal_hex(tmpctx, om->enctlv)); + return; + } + + /* Even though lightningd checks for valid ids, there's a race + * where it might vanish before we read this command. */ + node_id_from_pubkey(&next_node_id, &next_node); + next_peer = peer_htable_get(&daemon->peers, &next_node_id); + if (!next_peer) { + status_peer_debug(&peer->id, + "onion msg: unknown next peer %s", + type_to_string(tmpctx, + struct pubkey, + &next_node)); + return; + } + inject_peer_msg(next_peer, + take(towire_obs2_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); + } +} + void onionmsg_req(struct daemon *daemon, const u8 *msg) { struct node_id id; u8 *onionmsg; struct pubkey blinding; struct peer *peer; + bool obs2; - if (!fromwire_connectd_send_onionmsg(msg, msg, &id, &onionmsg, &blinding)) + if (!fromwire_connectd_send_onionmsg(msg, msg, &obs2, &id, &onionmsg, &blinding)) master_badmsg(WIRE_CONNECTD_SEND_ONIONMSG, msg); /* Even though lightningd checks for valid ids, there's a race * where it might vanish before we read this command. */ peer = peer_htable_get(&daemon->peers, &id); if (peer) { - u8 *omsg = towire_onion_message(NULL, &blinding, onionmsg); + u8 *omsg; + if (obs2) + omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg); + else + omsg = towire_onion_message(NULL, &blinding, onionmsg); inject_peer_msg(peer, take(omsg)); } } @@ -148,6 +305,7 @@ void handle_onion_message(struct daemon *daemon, towire_tlvstream_raw(&omsg, om->fields); daemon_conn_send(daemon->master, take(towire_connectd_got_onionmsg_to_us(NULL, + false, /* !obs2 */ &alias, self_id, reply_blinding, first_node_id, diff --git a/connectd/onion_message.h b/connectd/onion_message.h index 41b25982ce7e..22fc9bb8b4a4 100644 --- a/connectd/onion_message.h +++ b/connectd/onion_message.h @@ -3,7 +3,9 @@ #include "config.h" #include -/* Onion message comes in from peer */ +/* Various messages come in from peer */ +void handle_obs2_onion_message(struct daemon *daemon, + struct peer *peer, const u8 *msg); void handle_onion_message(struct daemon *daemon, struct peer *peer, const u8 *msg); diff --git a/connectd/test/run-onion_message.c b/connectd/test/run-onion_message.c new file mode 100644 index 000000000000..4e9083318041 --- /dev/null +++ b/connectd/test/run-onion_message.c @@ -0,0 +1,414 @@ +#include "config.h" +#include "../onion_message.c" +#include "common/blindedpath.c" +#include "common/blinding.c" +#include "common/bigsize.c" +#include "common/hmac.c" +#include "common/onion.c" +#include "common/sphinx.c" +#include "wire/fromwire.c" +#if EXPERIMENTAL_FEATURES +#include "wire/peer_exp_wiregen.c" +#include "wire/onion_exp_wiregen.c" +#else +#include "wire/peer_wiregen.c" +#include "wire/onion_wiregen.c" +#endif +#include "wire/tlvstream.c" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_msat_eq */ +bool amount_msat_eq(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED) +{ fprintf(stderr, "amount_msat_eq called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_connectd_send_onionmsg */ +bool fromwire_connectd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *obs2 UNNEEDED, struct node_id *id UNNEEDED, u8 **onion UNNEEDED, struct pubkey *blinding UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_send_onionmsg called!\n"); abort(); } +/* Generated stub for fromwire_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } +/* Generated stub for inject_peer_msg */ +void inject_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +{ fprintf(stderr, "inject_peer_msg called!\n"); abort(); } +/* Generated stub for master_badmsg */ +void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) +{ fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } +/* Generated stub for node_id_from_pubkey */ +void node_id_from_pubkey(struct node_id *id UNNEEDED, const struct pubkey *key UNNEEDED) +{ fprintf(stderr, "node_id_from_pubkey called!\n"); abort(); } +/* Generated stub for pubkey_from_node_id */ +bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } +/* Generated stub for status_fmt */ +void status_fmt(enum log_level level UNNEEDED, + const struct node_id *peer UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_connectd_got_onionmsg_to_us */ +u8 *towire_connectd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEEDED, const struct pubkey *node_alias UNNEEDED, const struct secret *self_id UNNEEDED, const struct pubkey *reply_blinding UNNEEDED, const struct pubkey *reply_first_node UNNEEDED, const struct onionmsg_path **reply_path UNNEEDED, const u8 *rawmsg UNNEEDED) +{ fprintf(stderr, "towire_connectd_got_onionmsg_to_us called!\n"); abort(); } +/* Generated stub for towire_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for towire_pad */ +void towire_pad(u8 **pptr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_pad called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_tu32 */ +void towire_tu32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_tu32 called!\n"); abort(); } +/* Generated stub for towire_tu64 */ +void towire_tu64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_tu64 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for towire_warningfmt */ +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 towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey->secret.data, NULL, NULL) != 1) + abort(); +} + +static void json_strfield(const char *name, const char *val) +{ + printf("\t\"%s\": \"%s\",\n", name, val); +} + +static void json_onionmsg_payload(const struct tlv_obs2_onionmsg_payload *om) +{ + if (om->reply_path) { + printf("\t\"reply_path\": {\n"); + json_strfield("first_node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->first_node_id)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->blinding)); + printf("\t\"path\": [\n"); + for (size_t i = 0; i < tal_count(om->reply_path->path); i++) { + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->path[i]->node_id)); + json_strfield("encrypted_recipient_data", + tal_hex(tmpctx, + om->reply_path->path[i]->encrypted_recipient_data)); + } + printf("]}\n"); + } + if (om->invoice) + json_strfield("invoice", tal_hex(tmpctx, om->invoice)); + if (om->invoice_request) + json_strfield("invoice_request", + tal_hex(tmpctx, om->invoice_request)); + if (om->invoice_error) + json_strfield("invoice_error", + tal_hex(tmpctx, om->invoice_error)); +} + +/* Return next onion (and updates blinding), or NULL */ +static u8 *json_test(const char *testname, + const u8 *data, + const struct privkey *me, + const struct privkey *blinding_priv, + struct pubkey *blinding) +{ + struct pubkey my_id, next_node; + struct secret ss, onion_ss; + struct pubkey ephemeral; + struct route_step *rs; + const u8 *cursor; + size_t max, maxlen; + struct onionpacket *op; + struct tlv_obs2_onionmsg_payload *om; + + op = parse_onionpacket(tmpctx, data, tal_bytelen(data), NULL); + assert(op); + + pubkey_from_privkey(me, &my_id); + printf("{"); + json_strfield("test name", testname); + json_strfield("reader_privkey", + type_to_string(tmpctx, struct privkey, me)); + json_strfield("reader_id", + type_to_string(tmpctx, struct pubkey, &my_id)); + + if (blinding_priv) + json_strfield("blinding_privkey", + type_to_string(tmpctx, struct privkey, + blinding_priv)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, blinding)); + printf("\"onionmsg\": {\n"); + json_strfield("raw", tal_hex(tmpctx, data)); + json_strfield("version", tal_fmt(tmpctx, "%i", op->version)); + json_strfield("public_key", + type_to_string(tmpctx, struct pubkey, &op->ephemeralkey)); + json_strfield("hop_payloads", + tal_hex(tmpctx, op->routinginfo)); + json_strfield("hmac", + tal_hexstr(tmpctx, &op->hmac, sizeof(op->hmac))); + printf("},\n"); + + ephemeral = op->ephemeralkey; + + /* Set this for test_ecdh */ + mykey = me; + assert(unblind_onion(blinding, test_ecdh, &ephemeral, &ss)); + json_strfield("ECDH shared secret", + type_to_string(tmpctx, struct secret, &ss)); + /* Reproduce internal calc from unblind_onion */ + { + struct secret hmac; + subkey_from_hmac("blinded_node_id", &ss, &hmac); + json_strfield("HMAC256(\\\"blinded_node_id\\\", ss(i)) * k(i)", + type_to_string(tmpctx, struct secret, &hmac)); + } + json_strfield("Tweaked onion pubkey", + type_to_string(tmpctx, struct pubkey, &ephemeral)); + + /* Now get onion shared secret and parse it. */ + test_ecdh(&ephemeral, &onion_ss); + json_strfield("onion shared secret", + type_to_string(tmpctx, struct secret, &onion_ss)); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + assert(rs); + + printf("\"onion contents\": {\n"); + json_strfield("raw", tal_hex(tmpctx, rs->raw_payload)); + + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + json_strfield("length", tal_fmt(tmpctx, "%zu", maxlen)); + json_strfield("rawtlv", tal_hexstr(tmpctx, cursor, maxlen)); + json_strfield("hmac", tal_hexstr(tmpctx, rs->next->hmac.bytes, + sizeof(rs->next->hmac.bytes))); + om = fromwire_tlv_obs2_onionmsg_payload(tmpctx, &cursor, &maxlen); + assert(om); + + json_onionmsg_payload(om); + + /* We expect one of these. */ + assert(om->enctlv); + + printf("\t\"encrypted_data_tlv\": {\n"); + json_strfield("raw", tal_hex(tmpctx, om->enctlv)); + + if (rs->nextcase == ONION_END) { + struct secret *self_id; + struct pubkey alias; + assert(decrypt_obs2_final_enctlv(tmpctx, + blinding, &ss, + om->enctlv, + &my_id, &alias, &self_id)); + if (self_id) { + json_strfield("self_id", + type_to_string(tmpctx, struct secret, + self_id)); + } + printf("}\n"); + return NULL; + } else { + assert(decrypt_obs2_enctlv(blinding, &ss, om->enctlv, &next_node, + blinding)); + json_strfield("next_node", + type_to_string(tmpctx, struct pubkey, &next_node)); + json_strfield("next_blinding", + type_to_string(tmpctx, struct pubkey, + blinding)); + printf("}"); + printf("},\n"); + return serialize_onionpacket(tmpctx, rs->next); + } +} + +int main(int argc, char *argv[]) +{ + struct onionpacket *op; + u8 *data; + struct privkey alice, bob, carol, dave, blinding_priv; + struct pubkey alice_id, bob_id, carol_id, dave_id; + struct pubkey blinding; + + common_setup(argv[0]); + + memset(&alice, 'A', sizeof(alice)); + memset(&bob, 'B', sizeof(bob)); + memset(&carol, 'C', sizeof(carol)); + memset(&dave, 'D', sizeof(dave)); + pubkey_from_privkey(&alice, &alice_id); + pubkey_from_privkey(&bob, &bob_id); + pubkey_from_privkey(&carol, &carol_id); + pubkey_from_privkey(&dave, &dave_id); + + /* ThomasH sends via email: + * + * { + * "version":0, + * "public_key": + * "0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", + * "hop_payloads": + * "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + * "hmac": "564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac" + * } + */ + op = tal(tmpctx, struct onionpacket); + op->version = 0; + assert(pubkey_from_hexstr("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", strlen("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967"), &op->ephemeralkey)); + assert(hex_decode("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac", + strlen("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac"), + &op->hmac, sizeof(op->hmac))); + op->routinginfo = tal_hexdata(op, "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + strlen("37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a")); + + data = serialize_onionpacket(tmpctx, op); + printf("[\n"); + + memset(&blinding_priv, 5, sizeof(blinding_priv)); + pubkey_from_privkey(&blinding_priv, &blinding); + + data = json_test("onion message for Alice", + data, + &alice, + &blinding_priv, + &blinding); + + data = json_test("onion message for Bob", + data, + &bob, + NULL, + &blinding); + + data = json_test("onion message for Carol", + data, + &carol, + NULL, + &blinding); + + data = json_test("onion message for Dave", + data, + &dave, + NULL, + &blinding); + + assert(!data); + printf("]\n"); + + common_shutdown(); + return 0; +} diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 2d7fab1449a5..f7ffa99eb97d 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -134,6 +134,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_no_version_checks = false; ld->dev_max_funding_unconfirmed = 2016; ld->dev_ignore_modern_onion = false; + ld->dev_ignore_obsolete_onion = false; ld->dev_disable_commit = -1; #endif diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 6025a31e2b42..973db2a7c863 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -263,7 +263,7 @@ struct lightningd { u32 dev_max_funding_unconfirmed; /* Special switches to test onion compatibility */ - bool dev_ignore_modern_onion; + bool dev_ignore_modern_onion, dev_ignore_obsolete_onion; /* Tell channeld to disable commits after this many. */ int dev_disable_commit; diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index b940202ea191..ae05c7c8fba6 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -1,7 +1,6 @@ #include "config.h" #include #include -#include #include #include #include @@ -22,7 +21,10 @@ struct onion_message_hook_payload { struct onionmsg_path **reply_path; struct pubkey *reply_first_node; struct pubkey *our_alias; + + /* Exactly one of these is set! */ struct tlv_onionmsg_payload *om; + struct tlv_obs2_onionmsg_payload *obs2_om; }; static void json_add_blindedpath(struct json_stream *stream, @@ -61,32 +63,56 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload, payload->reply_path); } - if (deprecated_apis) + /* Common convenience fields */ + if (payload->obs2_om) { + json_add_bool(stream, "obs2", true); + if (payload->obs2_om->invoice_request) + json_add_hex_talarr(stream, "invoice_request", + payload->obs2_om->invoice_request); + if (payload->obs2_om->invoice) + json_add_hex_talarr(stream, "invoice", payload->obs2_om->invoice); + + if (payload->obs2_om->invoice_error) + json_add_hex_talarr(stream, "invoice_error", + payload->obs2_om->invoice_error); + + json_array_start(stream, "unknown_fields"); + for (size_t i = 0; i < tal_count(payload->obs2_om->fields); i++) { + if (payload->obs2_om->fields[i].meta) + continue; + json_object_start(stream, NULL); + json_add_u64(stream, "number", payload->obs2_om->fields[i].numtype); + json_add_hex(stream, "value", + payload->obs2_om->fields[i].value, + payload->obs2_om->fields[i].length); + json_object_end(stream); + } + json_array_end(stream); + } else { json_add_bool(stream, "obs2", false); - - if (payload->om->invoice_request) - json_add_hex_talarr(stream, "invoice_request", - payload->om->invoice_request); - if (payload->om->invoice) - json_add_hex_talarr(stream, "invoice", payload->om->invoice); - - if (payload->om->invoice_error) - json_add_hex_talarr(stream, "invoice_error", - payload->om->invoice_error); - - json_array_start(stream, "unknown_fields"); - for (size_t i = 0; i < tal_count(payload->om->fields); i++) { - if (payload->om->fields[i].meta) - continue; - json_object_start(stream, NULL); - json_add_u64(stream, "number", payload->om->fields[i].numtype); - json_add_hex(stream, "value", - payload->om->fields[i].value, - payload->om->fields[i].length); - json_object_end(stream); + if (payload->om->invoice_request) + json_add_hex_talarr(stream, "invoice_request", + payload->om->invoice_request); + if (payload->om->invoice) + json_add_hex_talarr(stream, "invoice", payload->om->invoice); + + if (payload->om->invoice_error) + json_add_hex_talarr(stream, "invoice_error", + payload->om->invoice_error); + + json_array_start(stream, "unknown_fields"); + for (size_t i = 0; i < tal_count(payload->om->fields); i++) { + if (payload->om->fields[i].meta) + continue; + json_object_start(stream, NULL); + json_add_u64(stream, "number", payload->om->fields[i].numtype); + json_add_hex(stream, "value", + payload->om->fields[i].value, + payload->om->fields[i].length); + json_object_end(stream); + } + json_array_end(stream); } - json_array_end(stream); - json_object_end(stream); } @@ -117,6 +143,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) struct onion_message_hook_payload *payload; u8 *submsg; struct secret *self_id; + bool obs2; size_t submsglen; const u8 *subptr; @@ -124,6 +151,7 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) payload->our_alias = tal(payload, struct pubkey); if (!fromwire_connectd_got_onionmsg_to_us(payload, msg, + &obs2, payload->our_alias, &self_id, &payload->reply_blinding, @@ -136,7 +164,9 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) } #if DEVELOPER - if (ld->dev_ignore_modern_onion) + if (!obs2 && ld->dev_ignore_modern_onion) + return; + if (obs2 && ld->dev_ignore_obsolete_onion) return; #endif @@ -148,11 +178,22 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) submsglen = tal_bytelen(submsg); subptr = submsg; - payload->om = fromwire_tlv_onionmsg_payload(payload, &subptr, &submsglen); - if (!payload->om) { - log_broken(ld->log, "bad got_onionmsg_tous om: %s", - tal_hex(tmpctx, msg)); - return; + if (obs2) { + payload->om = NULL; + payload->obs2_om = fromwire_tlv_obs2_onionmsg_payload(payload, &subptr, &submsglen); + if (!payload->obs2_om) { + log_broken(ld->log, "bad got_obs2_onionmsg_tous om: %s", + tal_hex(tmpctx, msg)); + return; + } + } else { + payload->obs2_om = NULL; + payload->om = fromwire_tlv_onionmsg_payload(payload, &subptr, &submsglen); + if (!payload->om) { + log_broken(ld->log, "bad got_onionmsg_tous om: %s", + tal_hex(tmpctx, msg)); + return; + } } tal_free(submsg); @@ -209,10 +250,11 @@ static struct command_result *param_onion_hops(struct command *cmd, return NULL; } -static struct command_result *json_sendonionmessage(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) +static struct command_result *json_sendonionmessage2(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params, + bool obs2) { struct onion_hop *hops; struct node_id *first_id; @@ -258,13 +300,29 @@ static struct command_result *json_sendonionmessage(struct command *cmd, "Creating onion failed (tlvs too long?)"); subd_send_msg(cmd->ld->connectd, - take(towire_connectd_send_onionmsg(NULL, first_id, + take(towire_connectd_send_onionmsg(NULL, obs2, first_id, serialize_onionpacket(tmpctx, op), blinding))); return command_success(cmd, json_stream_success(cmd)); } +static struct command_result *json_sendonionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj, + const jsmntok_t *params) +{ + return json_sendonionmessage2(cmd, buffer, obj, params, false); +} + +static struct command_result *json_sendobs2onionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj, + const jsmntok_t *params) +{ + return json_sendonionmessage2(cmd, buffer, obj, params, true); +} + static const struct json_command sendonionmessage_command = { "sendonionmessage", "utility", @@ -273,6 +331,14 @@ static const struct json_command sendonionmessage_command = { }; AUTODATA(json_command, &sendonionmessage_command); +static const struct json_command sendobs2onionmessage_command = { + "sendobs2onionmessage", + "utility", + json_sendobs2onionmessage, + "Send obsolete message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)" +}; +AUTODATA(json_command, &sendobs2onionmessage_command); + static struct command_result *param_pubkeys(struct command *cmd, const char *name, const char *buffer, @@ -364,6 +430,32 @@ static struct command_result *json_blindedpath(struct command *cmd, json_add_blindedpath(response, "blindedpath", &first_blinding_pubkey, &first_node, path); + /* Now create obsolete one! */ + blinding_iter = first_blinding; + for (size_t i = 0; i < nhops - 1; i++) { + path[i] = tal(path, struct onionmsg_path); + path[i]->encrypted_recipient_data = create_obs2_enctlv(path[i], + &blinding_iter, + &ids[i], + &ids[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &path[i]->node_id); + } + + /* FIXME: Add padding! */ + path[nhops-1] = tal(path, struct onionmsg_path); + path[nhops-1]->encrypted_recipient_data = create_obs2_final_enctlv(path[nhops-1], + &blinding_iter, + &ids[nhops-1], + /* FIXME: Pad? */ + 0, + &cmd->ld->onion_reply_secret, + &path[nhops-1]->node_id); + json_add_blindedpath(response, "obs2blindedpath", + &first_blinding_pubkey, &first_node, path); return command_success(cmd, response); } diff --git a/lightningd/options.c b/lightningd/options.c index cb190223f9ea..e756854929ec 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -733,6 +733,9 @@ static void dev_register_opts(struct lightningd *ld) opt_register_noarg("--dev-no-modern-onion", opt_set_bool, &ld->dev_ignore_modern_onion, "Ignore modern onion messages"); + opt_register_noarg("--dev-no-obsolete-onion", opt_set_bool, + &ld->dev_ignore_obsolete_onion, + "Ignore obsolete onion messages"); opt_register_arg("--dev-disable-commit-after", opt_set_intval, opt_show_intval, &ld->dev_disable_commit, diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 7a3c0b06cc61..6039ed6fd0a0 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -639,13 +639,96 @@ static struct pubkey *path_to_node(const tal_t *ctx, /* Marshal arguments for sending onion messages */ struct sending { struct sent *sent; - struct tlv_onionmsg_payload *payload; + const char *msgfield; + const u8 *msgval; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; struct command_result *(*done)(struct command *cmd, const char *buf UNUSED, const jsmntok_t *result UNUSED, struct sent *sent); }; +static struct command_result * +send_obs2_message(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct sending *sending) +{ + struct sent *sent = sending->sent; + struct privkey blinding_iter; + struct pubkey fwd_blinding, *node_alias; + size_t nhops = tal_count(sent->path); + struct tlv_obs2_onionmsg_payload **payloads; + struct out_req *req; + + /* Now create enctlvs for *forward* path. */ + randombytes_buf(&blinding_iter, sizeof(blinding_iter)); + if (!pubkey_from_privkey(&blinding_iter, &fwd_blinding)) + return command_fail(cmd, LIGHTNINGD, + "Could not convert blinding %s to pubkey!", + type_to_string(tmpctx, struct privkey, + &blinding_iter)); + + /* We overallocate: this node (0) doesn't have payload or alias */ + payloads = tal_arr(cmd, struct tlv_obs2_onionmsg_payload *, nhops); + node_alias = tal_arr(cmd, struct pubkey, nhops); + + for (size_t i = 1; i < nhops - 1; i++) { + payloads[i] = tlv_obs2_onionmsg_payload_new(payloads); + payloads[i]->enctlv = create_obs2_enctlv(payloads[i], + &blinding_iter, + &sent->path[i], + &sent->path[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &node_alias[i]); + } + /* Final payload contains the actual data. */ + payloads[nhops-1] = tlv_obs2_onionmsg_payload_new(payloads); + + /* We don't include enctlv in final, but it gives us final alias */ + if (!create_obs2_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], + /* FIXME: Pad? */ 0, + NULL, + &node_alias[nhops-1])) { + /* Should not happen! */ + return command_fail(cmd, LIGHTNINGD, + "Could create final enctlv"); + } + + /* FIXME: This interface is a string for sendobsonionmessage! */ + if (streq(sending->msgfield, "invoice_request")) { + payloads[nhops-1]->invoice_request + = cast_const(u8 *, sending->msgval); + } else { + assert(streq(sending->msgfield, "invoice")); + payloads[nhops-1]->invoice + = cast_const(u8 *, sending->msgval); + } + payloads[nhops-1]->reply_path = sending->obs2_reply_path; + + req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", + sending->done, + forward_error, + sending->sent); + json_add_pubkey(req->js, "first_id", &sent->path[1]); + json_add_pubkey(req->js, "blinding", &fwd_blinding); + json_array_start(req->js, "hops"); + for (size_t i = 1; i < nhops; i++) { + u8 *tlv; + json_object_start(req->js, NULL); + json_add_pubkey(req->js, "id", &node_alias[i]); + tlv = tal_arr(tmpctx, u8, 0); + towire_tlv_obs2_onionmsg_payload(&tlv, payloads[i]); + json_add_hex_talarr(req->js, "tlv", tlv); + json_object_end(req->js); + } + json_array_end(req->js); + return send_outreq(cmd->plugin, req); +} + static struct command_result * send_modern_message(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, @@ -683,7 +766,7 @@ send_modern_message(struct command *cmd, &node_alias[i]); } /* Final payload contains the actual data. */ - payloads[nhops-1] = sending->payload; + payloads[nhops-1] = tlv_onionmsg_payload_new(payloads); /* We don't include enctlv in final, but it gives us final alias */ if (!create_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], @@ -695,12 +778,22 @@ send_modern_message(struct command *cmd, "Could create final enctlv"); } + /* FIXME: This interface is a string for sendobsonionmessage! */ + if (streq(sending->msgfield, "invoice_request")) { + payloads[nhops-1]->invoice_request + = cast_const(u8 *, sending->msgval); + } else { + assert(streq(sending->msgfield, "invoice")); + payloads[nhops-1]->invoice + = cast_const(u8 *, sending->msgval); + } payloads[nhops-1]->reply_path = reply_path; req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", - sending->done, + /* Try sending older version next */ + send_obs2_message, forward_error, - sending->sent); + sending); json_add_pubkey(req->js, "first_id", &sent->path[1]); json_add_pubkey(req->js, "blinding", &fwd_blinding); json_array_start(req->js, "hops"); @@ -734,6 +827,15 @@ static struct command_result *use_reply_path(struct command *cmd, json_tok_full_len(result), json_tok_full(buf, result)); + sending->obs2_reply_path = json_to_obs2_reply_path(cmd, buf, + json_get_member(buf, result, + "obs2blindedpath")); + if (!sending->obs2_reply_path) + plugin_err(cmd->plugin, + "could not parse obs2 reply path %.*s?", + json_tok_full_len(result), + json_tok_full(buf, result)); + /* Remember our alias we used so we can recognize reply */ sending->sent->reply_alias = tal_dup(sending->sent, struct pubkey, @@ -769,7 +871,8 @@ static struct command_result *make_reply_path(struct command *cmd, static struct command_result *send_message(struct command *cmd, struct sent *sent, - struct tlv_onionmsg_payload *payload STEALS, + const char *msgfield TAKES, + const u8 *msgval TAKES, struct command_result *(*done) (struct command *cmd, const char *buf UNUSED, @@ -778,7 +881,8 @@ static struct command_result *send_message(struct command *cmd, { struct sending *sending = tal(cmd, struct sending); sending->sent = sent; - sending->payload = tal_steal(sending, payload); + sending->msgfield = tal_strdup(sending, msgfield); + sending->msgval = tal_dup_talarr(sending, u8, msgval); sending->done = done; return make_reply_path(cmd, sending); @@ -817,12 +921,11 @@ sendinvreq_after_connect(struct command *cmd, const jsmntok_t *result UNUSED, struct sent *sent) { - struct tlv_onionmsg_payload *payload = tlv_onionmsg_payload_new(sent); + u8 *rawinvreq = tal_arr(tmpctx, u8, 0); + towire_tlv_invoice_request(&rawinvreq, sent->invreq); - payload->invoice_request = tal_arr(payload, u8, 0); - towire_tlv_invoice_request(&payload->invoice_request, sent->invreq); - - return send_message(cmd, sent, payload, sendonionmsg_done); + return send_message(cmd, sent, "invoice_request", rawinvreq, + sendonionmsg_done); } struct connect_attempt { @@ -1367,12 +1470,9 @@ sendinvoice_after_connect(struct command *cmd, const jsmntok_t *result UNUSED, struct sent *sent) { - struct tlv_onionmsg_payload *payload = tlv_onionmsg_payload_new(sent); - - payload->invoice = tal_arr(payload, u8, 0); - towire_tlv_invoice(&payload->invoice, sent->inv); - - return send_message(cmd, sent, payload, prepare_inv_timeout); + u8 *rawinv = tal_arr(tmpctx, u8, 0); + towire_tlv_invoice(&rawinv, sent->inv); + return send_message(cmd, sent, "invoice", rawinv, prepare_inv_timeout); } static struct command_result *createinvoice_done(struct command *cmd, diff --git a/plugins/offers.c b/plugins/offers.c index 08b08d52ed3b..bf37740a91aa 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -41,14 +41,65 @@ static struct command_result *sendonionmessage_error(struct command *cmd, return command_hook_success(cmd); } +/* FIXME: replyfield string interface is to accomodate obsolete API */ +static struct command_result * +send_obs2_onion_reply(struct command *cmd, + struct tlv_obs2_onionmsg_payload_reply_path *reply_path, + const char *replyfield, + const u8 *replydata) +{ + struct out_req *req; + size_t nhops = tal_count(reply_path->path); + + req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", + finished, sendonionmessage_error, NULL); + + json_add_pubkey(req->js, "first_id", &reply_path->first_node_id); + json_add_pubkey(req->js, "blinding", &reply_path->blinding); + json_array_start(req->js, "hops"); + for (size_t i = 0; i < nhops; i++) { + struct tlv_obs2_onionmsg_payload *omp; + u8 *tlv; + + json_object_start(req->js, NULL); + json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); + + omp = tlv_obs2_onionmsg_payload_new(tmpctx); + omp->enctlv = reply_path->path[i]->encrypted_recipient_data; + + /* Put payload in last hop. */ + if (i == nhops - 1) { + if (streq(replyfield, "invoice")) { + omp->invoice = cast_const(u8 *, replydata); + } else { + assert(streq(replyfield, "invoice_error")); + omp->invoice_error = cast_const(u8 *, replydata); + } + } + tlv = tal_arr(tmpctx, u8, 0); + towire_tlv_obs2_onionmsg_payload(&tlv, omp); + json_add_hex_talarr(req->js, "tlv", tlv); + json_object_end(req->js); + } + json_array_end(req->js); + return send_outreq(cmd->plugin, req); +} + struct command_result * send_onion_reply(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, - struct tlv_onionmsg_payload *payload) + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, + const char *replyfield, + const u8 *replydata) { struct out_req *req; size_t nhops; + /* Exactly one must be set! */ + assert(!reply_path != !obs2_reply_path); + if (obs2_reply_path) + return send_obs2_onion_reply(cmd, obs2_reply_path, replyfield, replydata); + req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", finished, sendonionmessage_error, NULL); @@ -64,14 +115,18 @@ send_onion_reply(struct command *cmd, json_object_start(req->js, NULL); json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); - /* Put payload in last hop. */ - if (i == nhops - 1) - omp = payload; - else - omp = tlv_onionmsg_payload_new(tmpctx); - + omp = tlv_onionmsg_payload_new(tmpctx); omp->encrypted_data_tlv = reply_path->path[i]->encrypted_recipient_data; + /* Put payload in last hop. */ + if (i == nhops - 1) { + if (streq(replyfield, "invoice")) { + omp->invoice = cast_const(u8 *, replydata); + } else { + assert(streq(replyfield, "invoice_error")); + omp->invoice_error = cast_const(u8 *, replydata); + } + } tlv = tal_arr(tmpctx, u8, 0); towire_tlv_onionmsg_payload(&tlv, omp); json_add_hex_talarr(req->js, "tlv", tlv); @@ -86,6 +141,7 @@ static struct command_result *onion_message_modern_call(struct command *cmd, const jsmntok_t *params) { const jsmntok_t *om, *replytok, *invreqtok, *invtok; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path = NULL; struct tlv_onionmsg_payload_reply_path *reply_path = NULL; if (!offers_enabled) @@ -94,20 +150,31 @@ static struct command_result *onion_message_modern_call(struct command *cmd, om = json_get_member(buf, params, "onion_message"); replytok = json_get_member(buf, om, "reply_blindedpath"); if (replytok) { - reply_path = json_to_reply_path(cmd, buf, replytok); - if (!reply_path) - plugin_err(cmd->plugin, "Invalid reply path %.*s?", - json_tok_full_len(replytok), - json_tok_full(buf, replytok)); + bool obs2; + json_to_bool(buf, json_get_member(buf, om, "obs2"), &obs2); + if (obs2) { + obs2_reply_path = json_to_obs2_reply_path(cmd, buf, replytok); + if (!obs2_reply_path) + plugin_err(cmd->plugin, "Invalid obs2 reply path %.*s?", + json_tok_full_len(replytok), + json_tok_full(buf, replytok)); + } else { + reply_path = json_to_reply_path(cmd, buf, replytok); + if (!reply_path) + plugin_err(cmd->plugin, "Invalid reply path %.*s?", + json_tok_full_len(replytok), + json_tok_full(buf, replytok)); + } } invreqtok = json_get_member(buf, om, "invoice_request"); if (invreqtok) { const u8 *invreqbin = json_tok_bin_from_hex(tmpctx, buf, invreqtok); - if (reply_path) + if (reply_path || obs2_reply_path) return handle_invoice_request(cmd, invreqbin, - reply_path); + reply_path, + obs2_reply_path); else plugin_log(cmd->plugin, LOG_DBG, "invoice_request without reply_path"); @@ -117,7 +184,7 @@ static struct command_result *onion_message_modern_call(struct command *cmd, if (invtok) { const u8 *invbin = json_tok_bin_from_hex(tmpctx, buf, invtok); if (invbin) - return handle_invoice(cmd, invbin, reply_path); + return handle_invoice(cmd, invbin, reply_path, obs2_reply_path); } return command_hook_success(cmd); diff --git a/plugins/offers.h b/plugins/offers.h index 73baca4ee585..68feefa12d69 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -10,5 +10,7 @@ struct command; struct command_result *WARN_UNUSED_RESULT send_onion_reply(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, - struct tlv_onionmsg_payload *payload); + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, + const char *replyfield, + const u8 *replydata); #endif /* LIGHTNING_PLUGINS_OFFERS_H */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index bd92c64b7e49..a15179465bca 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -12,6 +12,7 @@ struct inv { struct tlv_invoice *inv; /* May be NULL */ + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; struct tlv_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ @@ -25,8 +26,8 @@ fail_inv_level(struct command *cmd, const char *fmt, va_list ap) { char *full_fmt, *msg; - struct tlv_onionmsg_payload *payload; struct tlv_invoice_error *err; + u8 *errdata; full_fmt = tal_fmt(tmpctx, "Failed invoice"); if (inv->inv) { @@ -43,7 +44,7 @@ fail_inv_level(struct command *cmd, plugin_log(cmd->plugin, l, "%s", msg); /* Only reply if they gave us a path */ - if (!inv->reply_path) + if (!inv->reply_path && !inv->obs2_reply_path) return command_hook_success(cmd); /* Don't send back internal error details. */ @@ -55,10 +56,10 @@ fail_inv_level(struct command *cmd, err->error = tal_dup_arr(err, char, msg, strlen(msg), 0); /* FIXME: Add suggested_value / erroneous_field! */ - payload = tlv_onionmsg_payload_new(tmpctx); - payload->invoice_error = tal_arr(payload, u8, 0); - towire_tlv_invoice_error(&payload->invoice_error, err); - return send_onion_reply(cmd, inv->reply_path, payload); + errdata = tal_arr(cmd, u8, 0); + towire_tlv_invoice_error(&errdata, err); + return send_onion_reply(cmd, inv->reply_path, inv->obs2_reply_path, + "invoice_error", errdata); } static struct command_result *WARN_UNUSED_RESULT @@ -318,7 +319,8 @@ static struct command_result *listoffers_error(struct command *cmd, struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS) + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS) { size_t len = tal_count(invbin); struct inv *inv = tal(cmd, struct inv); @@ -327,6 +329,7 @@ struct command_result *handle_invoice(struct command *cmd, int bad_feature; struct sha256 m, shash; + inv->obs2_reply_path = tal_steal(inv, obs2_reply_path); inv->reply_path = tal_steal(inv, reply_path); inv->inv = fromwire_tlv_invoice(cmd, &invbin, &len); diff --git a/plugins/offers_inv_hook.h b/plugins/offers_inv_hook.h index fbc12ace6815..a733d6c4626e 100644 --- a/plugins/offers_inv_hook.h +++ b/plugins/offers_inv_hook.h @@ -6,5 +6,6 @@ /* We got an onionmessage with an invoice! reply_path could be NULL. */ struct command_result *handle_invoice(struct command *cmd, const u8 *invbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS); + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */ diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 851bc556982e..7f586437f5d5 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -17,6 +17,7 @@ struct invreq { struct tlv_invoice_request *invreq; struct tlv_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; /* The offer, once we've looked it up. */ struct tlv_offer *offer; @@ -35,8 +36,8 @@ fail_invreq_level(struct command *cmd, const char *fmt, va_list ap) { char *full_fmt, *msg; - struct tlv_onionmsg_payload *payload; struct tlv_invoice_error *err; + u8 *errdata; full_fmt = tal_fmt(tmpctx, "Failed invoice_request"); if (invreq->invreq) { @@ -61,10 +62,10 @@ fail_invreq_level(struct command *cmd, err->error = tal_dup_arr(err, char, msg, strlen(msg), 0); /* FIXME: Add suggested_value / erroneous_field! */ - payload = tlv_onionmsg_payload_new(tmpctx); - payload->invoice_error = tal_arr(payload, u8, 0); - towire_tlv_invoice_error(&payload->invoice_error, err); - return send_onion_reply(cmd, invreq->reply_path, payload); + errdata = tal_arr(cmd, u8, 0); + towire_tlv_invoice_error(&errdata, err); + return send_onion_reply(cmd, invreq->reply_path, invreq->obs2_reply_path, + "invoice_error", errdata); } static struct command_result *WARN_UNUSED_RESULT PRINTF_FMT(3,4) @@ -171,7 +172,6 @@ static struct command_result *createinvoice_done(struct command *cmd, { char *hrp; u8 *rawinv; - struct tlv_onionmsg_payload *payload; const jsmntok_t *t; /* We have a signed invoice, use it as a reply. */ @@ -184,9 +184,8 @@ static struct command_result *createinvoice_done(struct command *cmd, json_tok_full(buf, t)); } - payload = tlv_onionmsg_payload_new(tmpctx); - payload->invoice = rawinv; - return send_onion_reply(cmd, ir->reply_path, payload); + return send_onion_reply(cmd, ir->reply_path, ir->obs2_reply_path, + "invoice", rawinv); } static struct command_result *createinvoice_error(struct command *cmd, @@ -848,13 +847,15 @@ static struct command_result *handle_offerless_request(struct command *cmd, struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_onionmsg_payload_reply_path *reply_path) + struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path) { size_t len = tal_count(invreqbin); struct invreq *ir = tal(cmd, struct invreq); struct out_req *req; int bad_feature; + ir->obs2_reply_path = tal_steal(ir, obs2_reply_path); ir->reply_path = tal_steal(ir, reply_path); ir->invreq = fromwire_tlv_invoice_request(cmd, &invreqbin, &len); diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index c9dc5f37c64d..da59e52b5da0 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -8,5 +8,6 @@ extern u16 cltv_final; /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, const u8 *invreqbin, - struct tlv_onionmsg_payload_reply_path *reply_path STEALS); + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */ diff --git a/tests/test_pay.py b/tests/test_pay.py index 63cf2331e0ad..d55dade1de5e 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4479,6 +4479,27 @@ def test_fetchinvoice_3hop(node_factory, bitcoind): l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) + # Make sure l4 handled both onions before shutting down + l1.daemon.wait_for_log(r'plugin-fetchinvoice: Received modern onion .*obs2\\":false') + l1.daemon.wait_for_log(r'plugin-fetchinvoice: No match for modern onion.*obs2\\":true') + + # Test with obsolete onion. + l4.stop() + l4.daemon.opts['dev-no-modern-onion'] = None + l4.start() + l4.rpc.connect(l3.info['id'], 'localhost', l3.port) + + l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) + + # Test with modern onion. + l4.stop() + del l4.daemon.opts['dev-no-modern-onion'] + l4.daemon.opts['dev-no-obsolete-onion'] = None + l4.start() + l4.rpc.connect(l3.info['id'], 'localhost', l3.port) + + l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12']}) + def test_fetchinvoice(node_factory, bitcoind): # We remove the conversion plugin on l3, causing it to get upset. @@ -4775,8 +4796,10 @@ def test_dev_rawrequest(node_factory): assert 'invoice' in ret -def test_sendinvoice(node_factory, bitcoind): +def do_test_sendinvoice(node_factory, bitcoind, disable): l2opts = {'experimental-offers': None} + if disable: + l2opts[disable] = None l1, l2 = node_factory.line_graph(2, wait_for_announce=True, opts=[{'experimental-offers': None}, l2opts]) @@ -4858,6 +4881,20 @@ def test_sendinvoice(node_factory, bitcoind): assert out['amount_received_msat'] == Millisatoshi(10000000) +def test_sendinvoice(node_factory, bitcoind): + do_test_sendinvoice(node_factory, bitcoind, None) + + +@pytest.mark.developer("needs to --dev-no-obsolete-onion") +def test_sendinvoice_modern(node_factory, bitcoind): + do_test_sendinvoice(node_factory, bitcoind, 'dev-no-obsolete-onion') + + +@pytest.mark.developer("needs to --dev-no-modern-onion") +def test_sendinvoice_obsolete(node_factory, bitcoind): + do_test_sendinvoice(node_factory, bitcoind, 'dev-no-modern-onion') + + def test_self_pay(node_factory): """Repro test for issue 4345: pay ourselves via the pay plugin. diff --git a/wire/extracted_onion_01_offers.patch b/wire/extracted_onion_01_offers.patch index 263d9b73682a..90c0cb6c41ac 100644 --- a/wire/extracted_onion_01_offers.patch +++ b/wire/extracted_onion_01_offers.patch @@ -1,9 +1,29 @@ --- wire/extracted_onion_wire_csv 2020-03-25 10:24:12.861645774 +1030 +++ - 2020-03-26 13:47:13.498294435 +1030 -@@ -8,6 +8,10 @@ +@@ -8,6 +8,30 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, ++tlvtype,obs2_onionmsg_payload,reply_path,2 ++tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, ++tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, ++tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... ++tlvtype,obs2_onionmsg_payload,enctlv,10 ++tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... ++tlvtype,obs2_onionmsg_payload,invoice_request,64 ++tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... ++tlvtype,obs2_onionmsg_payload,invoice,66 ++tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... ++tlvtype,obs2_onionmsg_payload,invoice_error,68 ++tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... ++tlvtype,obs2_encmsg_tlvs,padding,1 ++tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... ++tlvtype,obs2_encmsg_tlvs,next_node_id,4 ++tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, ++tlvtype,obs2_encmsg_tlvs,next_blinding,12 ++tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, ++tlvtype,obs2_encmsg_tlvs,self_id,14 ++tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... +subtype,onionmsg_path +subtypedata,onionmsg_path,node_id,point, +subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_onion_02_modernonion.patch b/wire/extracted_onion_02_modernonion.patch index bed4ec9cc231..4d1bc69ca110 100644 --- a/wire/extracted_onion_02_modernonion.patch +++ b/wire/extracted_onion_02_modernonion.patch @@ -1,9 +1,9 @@ --- wire/onion_wire.csv 2021-11-16 15:17:39.446494580 +1030 +++ wire/onion_wire.csv.raw 2021-11-16 15:36:00.046441058 +1030 @@ -8,10 +8,36 @@ - tlvtype,tlv_payload,payment_data,8 - tlvdata,tlv_payload,payment_data,payment_secret,byte,32 - tlvdata,tlv_payload,payment_data,total_msat,tu64, + tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, + tlvtype,obs2_encmsg_tlvs,self_id,14 + tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... +tlvtype,tlv_payload,encrypted_recipient_data,10 +tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... +tlvtype,tlv_payload,blinding_point,12 diff --git a/wire/extracted_onion_exp_enctlv.patch b/wire/extracted_onion_exp_enctlv.patch index 458bc318e503..1897193b8c8b 100644 --- a/wire/extracted_onion_exp_enctlv.patch +++ b/wire/extracted_onion_exp_enctlv.patch @@ -10,7 +10,6 @@ +tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... +tlvtype,tlv_payload,blinding_point,12 +tlvdata,tlv_payload,blinding_point,blinding,point, - tlvtype,tlv_payload,encrypted_recipient_data,10 - tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... - tlvtype,tlv_payload,blinding_point,12 - + tlvtype,obs2_onionmsg_payload,reply_path,2 + tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, + tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index 87c2cc8f0de6..2ac0c4cff516 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -8,6 +8,26 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, +tlvtype,obs2_onionmsg_payload,reply_path,2 +tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, +tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, +tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... +tlvtype,obs2_onionmsg_payload,enctlv,10 +tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... +tlvtype,obs2_onionmsg_payload,invoice_request,64 +tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... +tlvtype,obs2_onionmsg_payload,invoice,66 +tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... +tlvtype,obs2_onionmsg_payload,invoice_error,68 +tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... +tlvtype,obs2_encmsg_tlvs,padding,1 +tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... +tlvtype,obs2_encmsg_tlvs,next_node_id,4 +tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, +tlvtype,obs2_encmsg_tlvs,next_blinding,12 +tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, +tlvtype,obs2_encmsg_tlvs,self_id,14 +tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... tlvtype,tlv_payload,encrypted_recipient_data,10 tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... tlvtype,tlv_payload,blinding_point,12 From c673c092dc3c83d06c761ce25600001cd0c2ff44 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 29 Mar 2022 13:35:06 +0200 Subject: [PATCH 0568/1530] py: Update the PyPI job to use poetry and correct versions Let's use poetry when we already use it for everything else. Changelog-None --- .github/workflows/pypi.yml | 36 +++++++++++++++-------------- contrib/pyln-client/pyproject.toml | 1 + contrib/pyln-proto/pyproject.toml | 1 + contrib/pyln-testing/pyproject.toml | 1 + 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 6e08825477da..b6c0bd1f18cb 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -38,35 +38,37 @@ jobs: with: python-version: 3.7 - - name: Install pypa/build + - name: Install pypa/build and poetry run: >- - python -m pip install build --user + python -m pip install build poetry --user - name: Build a binary wheel and a source tarball env: WORKDIR: ${{ matrix.WORKDIR }} - run: >- - cd ${{ env.WORKDIR}} && - python -m build --sdist --wheel --outdir dist/ . + run: | + export VERSION=$(git describe --abbrev=0).post$(git describe --abbrev=1 | awk -F "-" '{print $2}') + cd ${{ env.WORKDIR}} + poetry version $VERSION + poetry build - name: Publish distribution 📦 to Test PyPI if: github.repository == 'ElementsProject/lightning' - uses: pypa/gh-action-pypi-publish@master env: + POETRY_PYPI_TOKEN_TESTPYPI: ${{ secrets.TEST_PYPI_API_TOKEN }} WORKDIR: ${{ matrix.WORKDIR }} - with: - password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository_url: https://test.pypi.org/legacy/ - packages_dir: "${{ env.WORKDIR}}/dist" - skip_existing: true + run: | + cd ${{ env.WORKDIR}} + poetry config repositories.testpypi https://test.pypi.org/legacy/ + poetry publish --repository testpypi --no-interaction - name: Publish distribution 📦 to PyPI if: startsWith(github.ref, 'refs/tags') && github.repository == 'ElementsProject/lightning' - uses: pypa/gh-action-pypi-publish@master env: + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} WORKDIR: ${{ matrix.WORKDIR }} - with: - password: ${{ secrets.PYPI_API_TOKEN }} - packages_dir: "${{ env.WORKDIR}}/dist" - # We should never have a conflict here, the version tags are unique - skip_existing: false + run: | + cd ${{ env.WORKDIR}} + export VERSION=$(git describe --abbrev=0) + poetry version $VERSION + poetry config repositories.testpypi https://test.pypi.org/legacy/ + poetry publish --repository testpypi --no-interaction diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index 3014e19d783d..1b4bac179d30 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -4,6 +4,7 @@ version = "0.10.2.post1" description = "Client library and plugin library for c-lightning" authors = ["Christian Decker "] license = "BSD-MIT" +readme = "README.md" packages = [ { include = "pyln/client" }, diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml index cdc074d85afd..c31c5ad4fb96 100644 --- a/contrib/pyln-proto/pyproject.toml +++ b/contrib/pyln-proto/pyproject.toml @@ -4,6 +4,7 @@ version = "0.10.2.post1" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." authors = ["Christian Decker "] license = "BSD-MIT" +readme = "README.md" packages = [ { include = "pyln/proto" }, diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index c9a319455b46..6d9f9e56975e 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -4,6 +4,7 @@ version = "0.10.2" description = "Test your c-lightning integration, plugins or whatever you want" authors = ["Christian Decker "] license = "BSD-MIT" +readme = "README.md" packages = [ { include = "pyln/testing" }, From 5c949e3116e3116b3c11d876fa31f63d2870b8ce Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 29 Mar 2022 10:19:23 +1030 Subject: [PATCH 0569/1530] subd: make channel/peer own the subd. We get some memleak reports because ld owns the subd, but once the peer/channel is freed, there's no reference for the brief time until the subd exits. This happens for both opening and closingd. For openingd, the peer owns it, for others (including dualopend) the channel owns it. Signed-off-by: Rusty Russell --- lightningd/channel_control.c | 2 +- lightningd/closing_control.c | 2 +- lightningd/dual_open_control.c | 6 ++++-- lightningd/onchain_control.c | 2 +- lightningd/opening_control.c | 2 +- lightningd/subd.c | 12 +++++++----- lightningd/subd.h | 8 +++++--- 7 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index f8f369733733..b6c1d4353b37 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -590,7 +590,7 @@ void peer_start_channeld(struct channel *channel, | HSM_CAP_SIGN_ONCHAIN_TX); channel_set_owner(channel, - new_channel_subd(ld, + new_channel_subd(channel, ld, "lightning_channeld", channel, &channel->peer->id, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 285c5dd26485..b11d05f3fc48 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -364,7 +364,7 @@ void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd) | HSM_CAP_COMMITMENT_POINT); channel_set_owner(channel, - new_channel_subd(ld, + new_channel_subd(channel, ld, "lightning_closingd", channel, &channel->peer->id, channel->log, true, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 20cf2337d82b..6151984ad9ca 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -3219,7 +3219,8 @@ bool peer_start_dualopend(struct peer *peer, | HSM_CAP_SIGN_REMOTE_TX | HSM_CAP_SIGN_WILL_FUND_OFFER); - channel->owner = new_channel_subd(peer->ld, + channel->owner = new_channel_subd(channel, + peer->ld, "lightning_dualopend", channel, &peer->id, @@ -3286,7 +3287,8 @@ void peer_restart_dualopend(struct peer *peer, | HSM_CAP_SIGN_WILL_FUND_OFFER); channel_set_owner(channel, - new_channel_subd(peer->ld, "lightning_dualopend", + new_channel_subd(channel, peer->ld, + "lightning_dualopend", channel, &peer->id, channel->log, true, diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 850ae19133f5..d859b90066f6 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -619,7 +619,7 @@ enum watch_result onchaind_funding_spent(struct channel *channel, HSM_CAP_SIGN_ONCHAIN_TX | HSM_CAP_COMMITMENT_POINT); - channel_set_owner(channel, new_channel_subd(ld, + channel_set_owner(channel, new_channel_subd(channel, ld, "lightning_onchaind", channel, &channel->peer->id, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 694256b732bf..87ffc1984baf 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -867,7 +867,7 @@ bool peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) HSM_CAP_COMMITMENT_POINT | HSM_CAP_SIGN_REMOTE_TX); - uc->open_daemon = new_channel_subd(peer->ld, + uc->open_daemon = new_channel_subd(peer, peer->ld, "lightning_openingd", uc, &peer->id, uc->log, true, openingd_wire_name, diff --git a/lightningd/subd.c b/lightningd/subd.c index 308f18aa3a0e..80b5aa00cbd2 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -690,7 +690,8 @@ static struct io_plan *msg_setup(struct io_conn *conn, struct subd *sd) msg_send_next(conn, sd)); } -static struct subd *new_subd(struct lightningd *ld, +static struct subd *new_subd(const tal_t *ctx, + struct lightningd *ld, const char *name, void *channel, const struct node_id *node_id, @@ -710,7 +711,7 @@ static struct subd *new_subd(struct lightningd *ld, const char *happenings), va_list *ap) { - struct subd *sd = tal(ld, struct subd); + struct subd *sd = tal(ctx, struct subd); int msg_fd; const char *debug_subd = NULL; const char *shortname; @@ -791,7 +792,7 @@ struct subd *new_global_subd(struct lightningd *ld, struct subd *sd; va_start(ap, msgcb); - sd = new_subd(ld, name, NULL, NULL, NULL, false, + sd = new_subd(ld, ld, name, NULL, NULL, NULL, false, msgname, msgcb, NULL, NULL, &ap); va_end(ap); @@ -799,7 +800,8 @@ struct subd *new_global_subd(struct lightningd *ld, return sd; } -struct subd *new_channel_subd_(struct lightningd *ld, +struct subd *new_channel_subd_(const tal_t *ctx, + struct lightningd *ld, const char *name, void *channel, const struct node_id *node_id, @@ -822,7 +824,7 @@ struct subd *new_channel_subd_(struct lightningd *ld, struct subd *sd; va_start(ap, billboardcb); - sd = new_subd(ld, name, channel, node_id, base_log, + sd = new_subd(ctx, ld, name, channel, node_id, base_log, talks_to_peer, msgname, msgcb, errcb, billboardcb, &ap); va_end(ap); return sd; diff --git a/lightningd/subd.h b/lightningd/subd.h index e3f087579dbe..db0fd6afa219 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -104,6 +104,7 @@ struct subd *new_global_subd(struct lightningd *ld, /** * new_channel_subd - create a new subdaemon for a specific channel. + * @ctx: context to allocate from (usually peer or channel) * @ld: global state * @name: basename of daemon * @channel: channel to associate. @@ -120,7 +121,8 @@ struct subd *new_global_subd(struct lightningd *ld, * that many @fds are received before calling again. If it returns -1, the * subdaemon is shutdown. */ -struct subd *new_channel_subd_(struct lightningd *ld, +struct subd *new_channel_subd_(const tal_t *ctx, + struct lightningd *ld, const char *name, void *channel, const struct node_id *node_id, @@ -139,10 +141,10 @@ struct subd *new_channel_subd_(struct lightningd *ld, const char *happenings), ...); -#define new_channel_subd(ld, name, channel, node_id, log, \ +#define new_channel_subd(ctx, ld, name, channel, node_id, log, \ talks_to_peer, msgname, msgcb, errcb, \ billboardcb, ...) \ - new_channel_subd_((ld), (name), (channel), (node_id), \ + new_channel_subd_((ctx), (ld), (name), (channel), (node_id), \ (log), (talks_to_peer), \ (msgname), (msgcb), \ typesafe_cb_postargs(void, void *, (errcb), \ From b4c7ba2d8f7ad42259d46aa7203ddab8c52625ce Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Jan 2022 16:53:52 +0100 Subject: [PATCH 0570/1530] cln-grpc-plugin: Add scaffolding for the cln-grpc-plugin --- Cargo.toml | 1 + plugins/Makefile | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b597a2cef789..d7161c019400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,5 @@ members = [ "cln-rpc", "cln-grpc", "plugins", + "plugins/grpc-plugin", ] diff --git a/plugins/Makefile b/plugins/Makefile index 70302311bd42..e3cacde4a498 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -179,8 +179,11 @@ CLN_PLUGIN_SRC = $(shell find plugins/src -name "*.rs") ${CLN_PLUGIN_EXAMPLES}: ${CLN_PLUGIN_SRC} (cd plugins; cargo build ${CARGO_OPTS} --examples) +target/${RUST_PROFILE}/grpc-plugin: ${CLN_PLUGIN_SRC} + cargo build ${CARGO_OPTS} --bin grpc-plugin + ifneq ($(RUST),0) -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/grpc-plugin endif include plugins/test/Makefile From 8bc4f0137aeeca15ba24545b601565842f70f399 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Jan 2022 17:52:45 +0100 Subject: [PATCH 0571/1530] make: Add a hook for us to depend on generated files for tests We are about to generate the python grpc bindings, but only when we have Rust enabled. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6f541df029bc..67b6fd3d89c1 100644 --- a/Makefile +++ b/Makefile @@ -227,6 +227,7 @@ WIRE_GEN_DEPS := $(WIRE_GEN) $(wildcard tools/gen/*_template) # These are filled by individual Makefiles ALL_PROGRAMS := ALL_TEST_PROGRAMS := +ALL_TEST_GEN := ALL_FUZZ_TARGETS := ALL_C_SOURCES := ALL_C_HEADERS := header_versions_gen.h version_gen.h @@ -424,7 +425,7 @@ else endif endif -pytest: $(ALL_PROGRAMS) $(DEFAULT_TARGETS) $(ALL_TEST_PROGRAMS) +pytest: $(ALL_PROGRAMS) $(DEFAULT_TARGETS) $(ALL_TEST_PROGRAMS) $(ALL_TEST_GEN) ifeq ($(PYTEST),) @echo "py.test is required to run the integration tests, please install using 'pip3 install -r requirements.txt', and rerun 'configure'." exit 1 From f3d95530f462dca30a9bc1ed417fd192a3c09e5e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 17 Jan 2022 17:54:08 +0100 Subject: [PATCH 0572/1530] make: Generate grpc bindings if we want to test with rust enabled --- Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Makefile b/Makefile index 67b6fd3d89c1..778b532816de 100644 --- a/Makefile +++ b/Makefile @@ -359,6 +359,17 @@ endif ifneq ($(RUST),0) include cln-rpc/Makefile include cln-grpc/Makefile + +GRPC_GEN = tests/node_pb2.py \ + tests/node_pb2_grpc.py \ + tests/primitives_pb2.py + +ALL_TEST_GEN += $(GRPC_GEN) + +$(GRPC_GEN): cln-grpc/proto/node.proto cln-grpc/proto/primitives.proto + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/node.proto --python_out=tests/ --grpc_python_out=tests/ --experimental_allow_proto3_optional + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/primitives.proto --python_out=tests/ --grpc_python_out=tests/ --experimental_allow_proto3_optional + endif # We make pretty much everything depend on these. From a17edeb839b5ab0a18c27c3201835caac3d66320 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 20 Jan 2022 13:44:28 +0100 Subject: [PATCH 0573/1530] cln-grpc-plugin: Add basic grpc-plugin --- plugins/grpc-plugin/Cargo.toml | 27 +++++++++++++++++ plugins/grpc-plugin/src/main.rs | 52 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 plugins/grpc-plugin/Cargo.toml create mode 100644 plugins/grpc-plugin/src/main.rs diff --git a/plugins/grpc-plugin/Cargo.toml b/plugins/grpc-plugin/Cargo.toml new file mode 100644 index 000000000000..21e56727d2f9 --- /dev/null +++ b/plugins/grpc-plugin/Cargo.toml @@ -0,0 +1,27 @@ +[package] +edition = "2021" +name = "grpc-plugin" +version = "0.1.0" + +[dependencies] +anyhow = "1.0" +log = "0.4" +prost = "0.8" +rcgen = { version = "0.8", features = ["pem", "x509-parser"] } + +[dependencies.cln-grpc] +path = "../../cln-grpc" + +[dependencies.cln-plugin] +path = "../../plugins" + +[dependencies.cln-rpc] +path = "../../cln-rpc" + +[dependencies.tokio] +features = ["net", "rt-multi-thread"] +version = "1" + +[dependencies.tonic] +features = ["tls", "transport"] +version = "^0.5" diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs new file mode 100644 index 000000000000..c5028a02ae9d --- /dev/null +++ b/plugins/grpc-plugin/src/main.rs @@ -0,0 +1,52 @@ +use anyhow::{Context, Result}; +use cln_grpc::pb::node_server::NodeServer; +use cln_plugin::Builder; +use log::{debug, warn}; +use std::net::SocketAddr; +use std::path::{Path, PathBuf}; + +#[derive(Clone, Debug)] +struct PluginState { + rpc_path: PathBuf, + bind_address: SocketAddr, +} + +#[tokio::main] +async fn main() -> Result<()> { + debug!("Starting grpc plugin"); + let path = Path::new("lightning-rpc"); + let addr: SocketAddr = "0.0.0.0:50051".parse().unwrap(); + + let state = PluginState { + rpc_path: path.into(), + bind_address: addr, + }; + + let (plugin, i) = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()).build(); + + tokio::spawn(async move { + if let Err(e) = run_interface(state).await { + warn!("Error running the grpc interface: {}", e); + } + }); + + plugin.join().await +} + +async fn run_interface(state: PluginState) -> Result<()> { + debug!( + "Connecting to {:?} and serving grpc on {:?}", + &state.rpc_path, &state.bind_address + ); + tonic::transport::Server::builder() + .add_service(NodeServer::new( + cln_grpc::Server::new(&state.rpc_path) + .await + .context("creating NodeServer instance")?, + )) + .serve(state.bind_address) + .await + .context("serving requests")?; + + Ok(()) +} From d221c9b491ae1833ee6c9d751ffe80a5b913f6c4 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 18 Jan 2022 10:44:20 +0100 Subject: [PATCH 0574/1530] pytest: Add a test for the grpc plugin Currently still unencrypted, but will get its mTLS authentication in the next commits. --- tests/test_cln_rs.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 5c2d891089fc..acdde6a76bcd 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -1,9 +1,11 @@ from fixtures import * # noqa: F401,F403 +from node_pb2_grpc import NodeStub from pathlib import Path from pyln.testing.utils import env, TEST_NETWORK -import subprocess +import grpc +import node_pb2 as nodepb import pytest - +import subprocess # Skip the entire module if we don't have Rust. pytestmark = pytest.mark.skipif( @@ -27,6 +29,10 @@ def test_plugin_start(node_factory): l1 = node_factory.get_node(options={"plugin": str(bin_path), 'test-option': 31337}) l2 = node_factory.get_node() + # The plugin should be in the list of active plugins + plugins = l1.rpc.plugin('list')['plugins'] + assert len([p for p in plugins if 'cln-plugin-startup' in p['name'] and p['active']]) == 1 + cfg = l1.rpc.listconfigs() p = cfg['plugins'][0] p['path'] = None # The path is host-specific, so blank it. From 27e468d2ae3137a49f5dc2c376d6809342b4bbd2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 20 Jan 2022 15:09:21 +0100 Subject: [PATCH 0575/1530] grpc-plugin: Generate mTLS certificates and use them in grpc --- plugins/grpc-plugin/src/main.rs | 19 ++++++ plugins/grpc-plugin/src/tls.rs | 107 ++++++++++++++++++++++++++++++++ tests/test_cln_rs.py | 62 ++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 plugins/grpc-plugin/src/tls.rs diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index c5028a02ae9d..12031606fcbf 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -5,10 +5,14 @@ use log::{debug, warn}; use std::net::SocketAddr; use std::path::{Path, PathBuf}; +mod tls; + #[derive(Clone, Debug)] struct PluginState { rpc_path: PathBuf, bind_address: SocketAddr, + identity: tls::Identity, + ca_cert: Vec, } #[tokio::main] @@ -17,9 +21,14 @@ async fn main() -> Result<()> { let path = Path::new("lightning-rpc"); let addr: SocketAddr = "0.0.0.0:50051".parse().unwrap(); + let directory = std::env::current_dir()?; + let (identity, ca_cert) = tls::init(&directory)?; + let state = PluginState { rpc_path: path.into(), bind_address: addr, + identity, + ca_cert, }; let (plugin, i) = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()).build(); @@ -38,7 +47,17 @@ async fn run_interface(state: PluginState) -> Result<()> { "Connecting to {:?} and serving grpc on {:?}", &state.rpc_path, &state.bind_address ); + + let identity = state.identity.to_tonic_identity(); + let ca_cert = tonic::transport::Certificate::from_pem(state.ca_cert); + + let tls = tonic::transport::ServerTlsConfig::new() + .identity(identity) + .client_ca_root(ca_cert); + tonic::transport::Server::builder() + .tls_config(tls) + .context("configuring tls")? .add_service(NodeServer::new( cln_grpc::Server::new(&state.rpc_path) .await diff --git a/plugins/grpc-plugin/src/tls.rs b/plugins/grpc-plugin/src/tls.rs new file mode 100644 index 000000000000..28a2972f7737 --- /dev/null +++ b/plugins/grpc-plugin/src/tls.rs @@ -0,0 +1,107 @@ +//! Utilities to manage TLS certificates. +use anyhow::{Context, Result}; +use log::debug; +use rcgen::{Certificate, KeyPair}; +use std::path::Path; + +/// Just a wrapper around a certificate and an associated keypair. +#[derive(Clone, Debug)] +pub(crate) struct Identity { + key: Vec, + certificate: Vec, +} + +impl Identity { + fn to_certificate(&self) -> Result { + let keystr = String::from_utf8_lossy(&self.key); + let key = KeyPair::from_pem(&keystr)?; + let certstr = String::from_utf8_lossy(&self.certificate); + let params = rcgen::CertificateParams::from_ca_cert_pem(&certstr, key)?; + let cert = Certificate::from_params(params)?; + Ok(cert) + } + + pub fn to_tonic_identity(&self) -> tonic::transport::Identity { + tonic::transport::Identity::from_pem(&self.certificate, &self.key) + } +} + +/// Ensure that we have a certificate authority, and child keypairs +/// and certificates for the server and the client. It'll generate +/// them in the provided `directory`. The following files are +/// included: +/// +/// - `ca.pem`: The self-signed certificate of the CA +/// - `ca-key.pem`: The key used by the CA to sign certificates +/// - `server.pem`: The server certificate, signed by the CA +/// - `server-key.pem`: The server private key +/// - `client.pem`: The client certificate, signed by the CA +/// - `client-key.pem`: The client private key +/// +/// The `grpc-plugin` will use the `server.pem` certificate, while a +/// client is supposed to use the `client.pem` and associated +/// keys. Notice that this isn't strictly necessary since the server +/// will accept any client that is signed by the CA. In future we +/// might add runes, making the distinction more important. +/// +/// Returns the server identity and the root CA certificate. +pub(crate) fn init(directory: &Path) -> Result<(Identity, Vec)> { + let ca = generate_or_load_identity("cln Root CA", directory, "ca", None)?; + let server = generate_or_load_identity("cln grpc Server", directory, "server", Some(&ca))?; + let _client = generate_or_load_identity("cln grpc Client", directory, "client", Some(&ca))?; + Ok((server, ca.certificate)) +} + +/// Generate a given identity +fn generate_or_load_identity( + name: &str, + directory: &Path, + filename: &str, + parent: Option<&Identity>, +) -> Result { + // Just our naming convention here. + let cert_path = directory.join(format!("{}.pem", filename)); + let key_path = directory.join(format!("{}-key.pem", filename)); + // Did we have to generate a new key? In that case we also need to + // regenerate the certificate + if !key_path.exists() || !cert_path.exists() { + debug!( + "Generating a new keypair in {:?}, it didn't exist", + &key_path + ); + let keypair = KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256)?; + std::fs::write(&key_path, keypair.serialize_pem())?; + debug!( + "Generating a new certificate for key {:?} at {:?}", + &key_path, &cert_path + ); + + // Configure the certificate we want. + let subject_alt_names = vec!["cln".to_string(), "localhost".to_string()]; + let mut params = rcgen::CertificateParams::new(subject_alt_names); + params.key_pair = Some(keypair); + params.alg = &rcgen::PKCS_ECDSA_P256_SHA256; + if parent.is_none() { + params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained); + } else { + params.is_ca = rcgen::IsCa::SelfSignedOnly; + } + params + .distinguished_name + .push(rcgen::DnType::CommonName, name); + + let cert = Certificate::from_params(params)?; + std::fs::write( + &cert_path, + match parent { + None => cert.serialize_pem()?, + Some(ca) => cert.serialize_pem_with_signer(&ca.to_certificate()?)?, + }, + ) + .context("writing certificate to file")?; + } + + let key = std::fs::read(&key_path)?; + let certificate = std::fs::read(cert_path)?; + Ok(Identity { certificate, key }) +} diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index acdde6a76bcd..c55d2fe5452e 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -63,3 +63,65 @@ def test_plugin_start(node_factory): l1.connect(l2) l1.daemon.wait_for_log(r'Got a connect hook call') l1.daemon.wait_for_log(r'Got a connect notification') + + +def test_grpc_connect(node_factory): + """Attempts to connect to the grpc interface and call getinfo""" + bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + + p = Path(l1.daemon.lightning_dir) / TEST_NETWORK + cert_path = p / "client.pem" + key_path = p / "client-key.pem" + ca_cert_path = p / "ca.pem" + creds = grpc.ssl_channel_credentials( + root_certificates=ca_cert_path.open('rb').read(), + private_key=key_path.open('rb').read(), + certificate_chain=cert_path.open('rb').read() + ) + + channel = grpc.secure_channel( + "localhost:50051", + creds, + options=(('grpc.ssl_target_name_override', 'cln'),) + ) + stub = NodeStub(channel) + + response = stub.Getinfo(nodepb.GetinfoRequest()) + print(response) + + response = stub.ListFunds(nodepb.ListfundsRequest()) + print(response) + + +def test_grpc_generate_certificate(node_factory): + """Test whether we correctly generate the certificates. + + - If we have no certs, we need to generate them all + - If we have certs, we they should just get loaded + - If we delete one cert or its key it should get regenerated. + """ + bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + l1 = node_factory.get_node(options={ + "plugin": str(bin_path), + }, start=False) + + p = Path(l1.daemon.lightning_dir) / TEST_NETWORK + files = [p / f for f in ['ca.pem', 'ca-key.pem', 'client.pem', 'client-key.pem', 'server-key.pem', 'server.pem']] + + # Before starting no files exist. + assert [f.exists() for f in files] == [False] * len(files) + + l1.start() + assert [f.exists() for f in files] == [True] * len(files) + + # The files exist, restarting should not change them + contents = [f.open().read() for f in files] + l1.restart() + assert contents == [f.open().read() for f in files] + + # Now we delete the last file, we should regenerate it as well as its key + files[-1].unlink() + l1.restart() + assert contents[-2] != files[-2].open().read() + assert contents[-1] != files[-1].open().read() From f5147bbf1e57f05828fdcbc558bf789802058646 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 20 Jan 2022 15:21:07 +0100 Subject: [PATCH 0576/1530] pytest: Add a test for incorrect credentials If we aren't using the correct certificates we should reject the connections during the mTLS connection setup. This test tries to connect with the wrong client cert to the node, and the server will reject it. --- tests/test_cln_rs.py | 57 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index c55d2fe5452e..2d8581b78da6 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -107,7 +107,14 @@ def test_grpc_generate_certificate(node_factory): }, start=False) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK - files = [p / f for f in ['ca.pem', 'ca-key.pem', 'client.pem', 'client-key.pem', 'server-key.pem', 'server.pem']] + files = [p / f for f in [ + 'ca.pem', + 'ca-key.pem', + 'client.pem', + 'client-key.pem', + 'server-key.pem', + 'server.pem', + ]] # Before starting no files exist. assert [f.exists() for f in files] == [False] * len(files) @@ -125,3 +132,51 @@ def test_grpc_generate_certificate(node_factory): l1.restart() assert contents[-2] != files[-2].open().read() assert contents[-1] != files[-1].open().read() + + +def test_grpc_wrong_auth(node_factory): + """An mTLS client certificate should only be usable with its node + + We create two instances, each generates its own certs and keys, + and then we try to cross the wires. + """ + bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + l1, l2 = node_factory.get_nodes(2, opts={"plugin": str(bin_path), "start": False}) + l1.start() + l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + + def connect(node): + p = Path(node.daemon.lightning_dir) / TEST_NETWORK + cert, key, ca = [f.open('rb').read() for f in [ + p / 'client.pem', + p / 'client-key.pem', + p / "ca.pem"]] + + creds = grpc.ssl_channel_credentials( + root_certificates=ca, + private_key=key, + certificate_chain=cert, + ) + + channel = grpc.secure_channel( + "localhost:50051", + creds, + options=(('grpc.ssl_target_name_override', 'cln'),) + ) + return NodeStub(channel) + + stub = connect(l1) + # This should work, it's the correct node + stub.Getinfo(nodepb.GetinfoRequest()) + + l1.stop() + l2.start() + l2.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + + # This should not work, it's a different node + with pytest.raises(Exception, match=r'Socket closed|StatusCode.UNAVAILABLE'): + stub.Getinfo(nodepb.GetinfoRequest()) + + # Now load the correct ones and we should be good to go + stub = connect(l2) + stub.Getinfo(nodepb.GetinfoRequest()) From 60983861823217e9fd7151ed424c13d606969bcd Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 26 Jan 2022 18:42:49 +0100 Subject: [PATCH 0577/1530] cln-grpc: Add the `connect` method --- cln-grpc/proto/node.proto | 33 +++++++++++++++++++ cln-grpc/src/convert.rs | 22 +++++++++++++ cln-grpc/src/server.rs | 30 +++++++++++++++++ cln-rpc/src/model.rs | 55 +++++++++++++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 10 ++++-- doc/schemas/connect.request.json | 21 ++++++++++++ 6 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 doc/schemas/connect.request.json diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index b834c9c34329..d03b924275c8 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -16,6 +16,7 @@ service Node { rpc AutoCleanInvoice(AutocleaninvoiceRequest) returns (AutocleaninvoiceResponse) {} rpc CheckMessage(CheckmessageRequest) returns (CheckmessageResponse) {} rpc Close(CloseRequest) returns (CloseResponse) {} + rpc ConnectPeer(ConnectRequest) returns (ConnectResponse) {} } message GetinfoRequest { @@ -339,3 +340,35 @@ message CloseResponse { optional bytes tx = 8; optional bytes txid = 9; } + +message ConnectRequest { + bytes id = 1; + optional string host = 2; + optional uint32 port = 3; +} + +message ConnectResponse { + // Connect.direction + enum ConnectDirection { + IN = 0; + OUT = 1; + } + bytes id = 1; + bytes features = 2; + ConnectDirection direction = 3; +} + +message ConnectAddress { + // Connect.address.type + enum ConnectAddressType { + LOCAL_SOCKET = 0; + IPV4 = 1; + IPV6 = 2; + TORV2 = 3; + TORV3 = 4; + } + ConnectAddressType item_type = 1; + optional string socket = 2; + optional string address = 3; + optional uint32 port = 4; +} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 5dbfd54eef97..0c2d78a2657a 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -289,6 +289,17 @@ impl From<&responses::CloseResponse> for pb::CloseResponse { } } +#[allow(unused_variables)] +impl From<&responses::ConnectResponse> for pb::ConnectResponse { + fn from(c: &responses::ConnectResponse) -> Self { + Self { + id: hex::decode(&c.id).unwrap(), + features: hex::decode(&c.features).unwrap(), + direction: c.direction as i32, + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -371,3 +382,14 @@ impl From<&pb::CloseRequest> for requests::CloseRequest { } } +#[allow(unused_variables)] +impl From<&pb::ConnectRequest> for requests::ConnectRequest { + fn from(c: &pb::ConnectRequest) -> Self { + Self { + id: hex::encode(&c.id), + host: c.host.clone(), + port: c.port.map(|i| i as u16), + } + } +} + diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 3576bac86436..f492ed660086 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -266,4 +266,34 @@ async fn close( } +async fn connect_peer( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ConnectRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ConnectPeer(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ConnectPeer: {:?}", e)))?; + match result { + Response::ConnectPeer(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ConnectPeer", + r + ) + )), + } + +} + } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 3e6a0fc76f93..a9494af74607 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -24,6 +24,7 @@ pub enum Request { AutoCleanInvoice(requests::AutocleaninvoiceRequest), CheckMessage(requests::CheckmessageRequest), Close(requests::CloseRequest), + ConnectPeer(requests::ConnectRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -38,6 +39,7 @@ pub enum Response { AutoCleanInvoice(responses::AutocleaninvoiceResponse), CheckMessage(responses::CheckmessageResponse), Close(responses::CloseResponse), + ConnectPeer(responses::ConnectResponse), } pub mod requests { @@ -114,6 +116,16 @@ pub mod requests { pub force_lease_closed: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectRequest { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "host", skip_serializing_if = "Option::is_none")] + pub host: Option, + #[serde(alias = "port", skip_serializing_if = "Option::is_none")] + pub port: Option, + } + } @@ -731,5 +743,48 @@ pub mod responses { pub txid: Option, } + /// Whether they initiated connection or we did + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ConnectDirection { + IN, + OUT, + } + + /// Type of connection (*torv2*/*torv3* only if **direction** is *out*) + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ConnectAddressType { + LOCAL_SOCKET, + IPV4, + IPV6, + TORV2, + TORV3, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectAddress { + // Path `Connect.address.type` + #[serde(rename = "type")] + pub item_type: ConnectAddressType, + #[serde(alias = "socket", skip_serializing_if = "Option::is_none")] + pub socket: Option, + #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + pub address: Option, + #[serde(alias = "port", skip_serializing_if = "Option::is_none")] + pub port: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ConnectResponse { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "features")] + pub features: String, + // Path `Connect.direction` + #[serde(rename = "direction")] + pub direction: ConnectDirection, + } + } diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 5700b9764a28..b98b27f83493 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -6,6 +6,12 @@ import json +# Sometimes we want to rename a method, due to a name clash +method_name_override = { + "Connect": "ConnectPeer", +} + + def repo_root(): path = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) return Path(path.strip().decode('UTF-8')) @@ -26,7 +32,7 @@ def load_jsonrpc_method(name): response.typename += "Response" return Method( - name=name, + name=method_name_override.get(name, name), request=request, response=response, ) @@ -44,7 +50,7 @@ def load_jsonrpc_service(): "CheckMessage", # "check", # No point in mapping this one "Close", - # "connect", + "Connect", # "createinvoice", # "createonion", # "datastore", diff --git a/doc/schemas/connect.request.json b/doc/schemas/connect.request.json new file mode 100644 index 000000000000..97c40185348f --- /dev/null +++ b/doc/schemas/connect.request.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "" + }, + "host": { + "type": "string", + "description": "The hostname of the node." + }, + "port": { + "type": "u16", + "description": "Port to try connecting to" + } + } +} From 647ed6a8c8c90b834d69d15f5d09a10b0d12c253 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 10 Feb 2022 16:22:46 +0100 Subject: [PATCH 0578/1530] cln: Add grpc port as configurable option --- plugins/grpc-plugin/src/main.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 12031606fcbf..f0cd4b1e3e64 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; use cln_grpc::pb::node_server::NodeServer; -use cln_plugin::Builder; +use cln_plugin::{options, Builder}; use log::{debug, warn}; use std::net::SocketAddr; use std::path::{Path, PathBuf}; @@ -31,7 +31,14 @@ async fn main() -> Result<()> { ca_cert, }; - let (plugin, i) = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()).build(); + let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) + .option(options::ConfigOption::new( + "grpc-port", + options::Value::Integer(29735), + "Which port should the grpc plugin listen for incoming connections?", + )) + .start() + .await?; tokio::spawn(async move { if let Err(e) = run_interface(state).await { From dd66c85fcbf1fce804f899dde8c5bb76ff59d246 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 9 Mar 2022 17:30:36 +0100 Subject: [PATCH 0579/1530] grpc-plugin: Make the grpc port to listen on configurable Changelog-Added: cln-grpc-plugin: The plugin can be configured to listen on a specific port using the `grpc-port` option --- plugins/grpc-plugin/src/main.rs | 36 ++++++++++++++++++--------------- plugins/src/lib.rs | 13 ++++++++++++ tests/test_cln_rs.py | 18 +++++++++++++---- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index f0cd4b1e3e64..8636fc5adec0 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{Context, Result}; +use anyhow::{anyhow, Context, Result}; use cln_grpc::pb::node_server::NodeServer; use cln_plugin::{options, Builder}; use log::{debug, warn}; @@ -10,7 +10,6 @@ mod tls; #[derive(Clone, Debug)] struct PluginState { rpc_path: PathBuf, - bind_address: SocketAddr, identity: tls::Identity, ca_cert: Vec, } @@ -19,14 +18,12 @@ struct PluginState { async fn main() -> Result<()> { debug!("Starting grpc plugin"); let path = Path::new("lightning-rpc"); - let addr: SocketAddr = "0.0.0.0:50051".parse().unwrap(); let directory = std::env::current_dir()?; let (identity, ca_cert) = tls::init(&directory)?; let state = PluginState { rpc_path: path.into(), - bind_address: addr, identity, ca_cert, }; @@ -34,14 +31,21 @@ async fn main() -> Result<()> { let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "grpc-port", - options::Value::Integer(29735), + options::Value::Integer(50051), "Which port should the grpc plugin listen for incoming connections?", )) .start() .await?; + let bind_port = match plugin.option("grpc-port") { + Some(options::Value::Integer(i)) => i, + None => return Err(anyhow!("Missing 'grpc-port' option")), + Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)), + }; + let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); + tokio::spawn(async move { - if let Err(e) = run_interface(state).await { + if let Err(e) = run_interface(bind_addr, state).await { warn!("Error running the grpc interface: {}", e); } }); @@ -49,12 +53,7 @@ async fn main() -> Result<()> { plugin.join().await } -async fn run_interface(state: PluginState) -> Result<()> { - debug!( - "Connecting to {:?} and serving grpc on {:?}", - &state.rpc_path, &state.bind_address - ); - +async fn run_interface(bind_addr: SocketAddr, state: PluginState) -> Result<()> { let identity = state.identity.to_tonic_identity(); let ca_cert = tonic::transport::Certificate::from_pem(state.ca_cert); @@ -62,7 +61,7 @@ async fn run_interface(state: PluginState) -> Result<()> { .identity(identity) .client_ca_root(ca_cert); - tonic::transport::Server::builder() + let server = tonic::transport::Server::builder() .tls_config(tls) .context("configuring tls")? .add_service(NodeServer::new( @@ -70,9 +69,14 @@ async fn run_interface(state: PluginState) -> Result<()> { .await .context("creating NodeServer instance")?, )) - .serve(state.bind_address) - .await - .context("serving requests")?; + .serve(bind_addr); + + debug!( + "Connecting to {:?} and serving grpc on {:?}", + &state.rpc_path, &bind_addr + ); + + server.await.context("serving requests")?; Ok(()) } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index dfa996210f57..66630244bc7b 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -331,6 +331,19 @@ where sender: tokio::sync::mpsc::Sender, } +impl Plugin +where + S: Clone + Send, +{ + pub fn option(&self, name: &str) -> Option { + self.options + .iter() + .filter(|o| o.name() == name) + .next() + .map(|co| co.value.clone().unwrap_or(co.default().clone())) + } +} + /// The [PluginDriver] is used to run the IO loop, reading messages /// from the Lightning daemon, dispatching calls and notifications to /// the plugin, and returning responses to the the daemon. We also use diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 2d8581b78da6..e2b0ef53d974 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -2,6 +2,7 @@ from node_pb2_grpc import NodeStub from pathlib import Path from pyln.testing.utils import env, TEST_NETWORK +from ephemeral_port_reserve import reserve import grpc import node_pb2 as nodepb import pytest @@ -67,8 +68,9 @@ def test_plugin_start(node_factory): def test_grpc_connect(node_factory): """Attempts to connect to the grpc interface and call getinfo""" + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" - l1 = node_factory.get_node(options={"plugin": str(bin_path)}) + l1 = node_factory.get_node(options={"plugin": str(bin_path), "grpc-port": str(grpc_port)}) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK cert_path = p / "client.pem" @@ -80,8 +82,9 @@ def test_grpc_connect(node_factory): certificate_chain=cert_path.open('rb').read() ) + l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') channel = grpc.secure_channel( - "localhost:50051", + f"localhost:{grpc_port}", creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) @@ -101,9 +104,11 @@ def test_grpc_generate_certificate(node_factory): - If we have certs, we they should just get loaded - If we delete one cert or its key it should get regenerated. """ + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" l1 = node_factory.get_node(options={ "plugin": str(bin_path), + "grpc-port": str(grpc_port), }, start=False) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK @@ -140,8 +145,13 @@ def test_grpc_wrong_auth(node_factory): We create two instances, each generates its own certs and keys, and then we try to cross the wires. """ + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" - l1, l2 = node_factory.get_nodes(2, opts={"plugin": str(bin_path), "start": False}) + l1, l2 = node_factory.get_nodes(2, opts={ + "plugin": str(bin_path), + "start": False, + "grpc-port": str(grpc_port), + }) l1.start() l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') @@ -159,7 +169,7 @@ def connect(node): ) channel = grpc.secure_channel( - "localhost:50051", + f"localhost:{grpc_port}", creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) From 461a65ba1655e73a2058393b2d2b5d4ca522ddd7 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 10 Mar 2022 16:46:56 +0100 Subject: [PATCH 0580/1530] py: Add grpcio-tools for the cln-grpc-plugin --- poetry.lock | 158 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + 2 files changed, 157 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index f6e2cbe80066..b18f9962924c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -192,6 +192,32 @@ Werkzeug = ">=2.0" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] +[[package]] +name = "grpcio" +version = "1.44.0" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +six = ">=1.5.2" + +[package.extras] +protobuf = ["grpcio-tools (>=1.44.0)"] + +[[package]] +name = "grpcio-tools" +version = "1.44.0" +description = "Protobuf code generator for gRPC" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +grpcio = ">=1.44.0" +protobuf = ">=3.5.0.post1,<4.0dev" + [[package]] name = "importlib-metadata" version = "4.2.0" @@ -408,6 +434,14 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "protobuf" +version = "3.19.4" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "psutil" version = "5.9.0" @@ -660,7 +694,7 @@ python-versions = "*" name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" @@ -727,7 +761,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "25c2960761882dd99f4a0ed3394354054a45905d811d80f6984a56495df9ac22" +content-hash = "45e8b4302761ec3b83181b506daba46f587227fab83bf597bc0228da82c24eb0" [metadata.files] asn1crypto = [ @@ -956,6 +990,98 @@ flask = [ {file = "Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, {file = "Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, ] +grpcio = [ + {file = "grpcio-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:11f811c0fffd84fca747fbc742464575e5eb130fd4fb4d6012ccc34febd001db"}, + {file = "grpcio-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:9a86a91201f8345502ea81dee0a55ae13add5fafadf109b17acd858fe8239651"}, + {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:5f3c54ebb5d9633a557335c01d88d3d4928e9b1b131692283b6184da1edbec0b"}, + {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d47553b8e86ab1e59b0185ba6491a187f94a0239f414c8fc867a22b0405b798"}, + {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1e22d3a510438b7f3365c0071b810672d09febac6e8ca8a47eab657ae5f347b"}, + {file = "grpcio-1.44.0-cp310-cp310-win32.whl", hash = "sha256:41036a574cab3468f24d41d6ed2b52588fb85ed60f8feaa925d7e424a250740b"}, + {file = "grpcio-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ee51964edfd0a1293a95bb0d72d134ecf889379d90d2612cbf663623ce832b4"}, + {file = "grpcio-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:e2149077d71e060678130644670389ddf1491200bcea16c5560d4ccdc65e3f2e"}, + {file = "grpcio-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:0ac72d4b953b76924f8fa21436af060d7e6d8581e279863f30ee14f20751ac27"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5c30a9a7d3a05920368a60b080cbbeaf06335303be23ac244034c71c03a0fd24"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:05467acd391e3fffb05991c76cb2ed2fa1309d0e3815ac379764bc5670b4b5d4"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b81dc7894062ed2d25b74a2725aaa0a6895ce97ce854f432fe4e87cad5a07316"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d4843192e7d36278884282e100b8f305cf37d1b3d8c6b4f736d4454640a069"}, + {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:898c159148f27e23c08a337fb80d31ece6b76bb24f359d83929460d813665b74"}, + {file = "grpcio-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:b8d852329336c584c636caa9c2db990f3a332b19bc86a80f4646b58d27c142db"}, + {file = "grpcio-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:790d7493337558ae168477d1be3178f4c9b8f91d8cd9b8b719d06fd9b2d48836"}, + {file = "grpcio-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cd61b52d9cf8fcf8d9628c0b640b9e44fdc5e93d989cc268086a858540ed370c"}, + {file = "grpcio-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:14eefcf623890f3f7dd7831decd2a2116652b5ce1e0f1d4b464b8f52110743b0"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:bebe90b8020b4248e5a2076b56154cc6ff45691bbbe980579fc9db26717ac968"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89b390b1c0de909965280d175c53128ce2f0f4f5c0f011382243dd7f2f894060"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:c122dac5cb299b8ad7308d61bd9fe0413de13b0347cce465398436b3fdf1f609"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6641a28cc826a92ef717201cca9a035c34a0185e38b0c93f3ce5f01a01a1570a"}, + {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb0a3e0e64843441793923d9532a3a23907b07b2a1e0a7a31f186dc185bb772"}, + {file = "grpcio-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:be857b7ec2ac43455156e6ba89262f7d7ae60227049427d01a3fecd218a3f88d"}, + {file = "grpcio-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f6a9cf0e77f72f2ac30c9c6e086bc7446c984c51bebc6c7f50fbcd718037edba"}, + {file = "grpcio-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:19e54f0c7083c8332b5a75a9081fc5127f1dbb67b6c1a32bd7fe896ef0934918"}, + {file = "grpcio-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:bfd36b959c3c4e945119387baed1414ea46f7116886aa23de0172302b49d7ff1"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ccd388b8f37b19d06e4152189726ce309e36dc03b53f2216a4ea49f09a7438e6"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:9075c0c003c1ff14ebce8f0ba55cc692158cb55c68da09cf8b0f9fc5b749e343"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e898194f76212facbaeb6d7545debff29351afa23b53ff8f0834d66611af5139"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fa6584046a7cf281649975a363673fa5d9c6faf9dc923f261cc0e56713b5892"}, + {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36a7bdd6ef9bca050c7ade8cba5f0e743343ea0756d5d3d520e915098a9dc503"}, + {file = "grpcio-1.44.0-cp38-cp38-win32.whl", hash = "sha256:dc3290d0411ddd2bd49adba5793223de8de8b01588d45e9376f1a9f7d25414f4"}, + {file = "grpcio-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:13343e7b840c20f43b44f0e6d3bbdc037c964f0aec9735d7cb685c407731c9ff"}, + {file = "grpcio-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:c5c2f8417d13386e18ccc8c61467cb6a6f9667a1ff7000a2d7d378e5d7df693f"}, + {file = "grpcio-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:cf220199b7b4992729ad4d55d5d3f652f4ccfe1a35b5eacdbecf189c245e1859"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4201c597e5057a9bfef9ea5777a6d83f6252cb78044db7d57d941ec2300734a5"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:e2de61005118ae59d48d5d749283ebfd1ba4ca68cc1000f8a395cd2bdcff7ceb"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:871078218fa9117e2a378678f327e32fda04e363ed6bc0477275444273255d4d"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8d610b7b557a7609fecee80b6dd793ecb7a9a3c3497fbdce63ce7d151cdd705"}, + {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcb53e4eb8c271032c91b8981df5fc1bb974bc73e306ec2c27da41bd95c44b5"}, + {file = "grpcio-1.44.0-cp39-cp39-win32.whl", hash = "sha256:e50ddea6de76c09b656df4b5a55ae222e2a56e625c44250e501ff3c904113ec1"}, + {file = "grpcio-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:d2ec124a986093e26420a5fb10fa3f02b2c232f924cdd7b844ddf7e846c020cd"}, + {file = "grpcio-1.44.0.tar.gz", hash = "sha256:4bae1c99896045d3062ab95478411c8d5a52cb84b91a1517312629fa6cfeb50e"}, +] +grpcio-tools = [ + {file = "grpcio-tools-1.44.0.tar.gz", hash = "sha256:be37f458ea510c9a8f1caabbc2b258d12e55d189a567f5edcace90f27dc0efbf"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:9f58529e24f613019a85c258a274d441d89e0cad8cf7fca21ef3807ba5840c5d"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:1d120082236f8d2877f8a19366476b82c3562423b877b7c471a142432e31c2c4"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:65c2fe3cdc5425180f01dd303e28d4f363d38f4c2e3a7e1a87caedd5417e23bb"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5caef118deb8cdee1978fd3d8e388a9b256cd8d34e4a8895731ac0e86fa5e47c"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121c9765cee8636201cf0d4e80bc7b509813194919bccdb66e9671c4ece6dac3"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-win32.whl", hash = "sha256:90d1fac188bac838c4169eb3b67197887fa0572ea8a90519a20cddb080800549"}, + {file = "grpcio_tools-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e16260dfe6e997330473863e01466b0992369ae2337a0249b390b4651cff424"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:608414cc1093e1e9e5980c97a6ee78e51dffff359e7a3f123d1fb9d95b8763a5"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:395609c06f69fbc79518b30a01931127088a3f9ef2cc2a35269c5f187eefd38c"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f7ce16766b24b88ec0e4355f5dd66c2eee6af210e889fcb7961c9c4634c687de"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3c9abc4a40c62f46d5e43e49c7afc567dedf12eeef95933ac9ea2986baa2420b"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b73fd87a44ba1b91866b0254193c37cdb001737759b77b637cebe0c816d38342"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b211f12e4cbc0fde8e0f982b0f581cce38874666a02ebfed93c23dcaeb8a4e0"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b421dc9b27bcaff4c73644cd3801e4893b11ba3eb39729246fd3de98d9f685b"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:33d93027840a873c7b59402fe6db8263b88c56e2f84aa0b6281c05cc8bd314a1"}, + {file = "grpcio_tools-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:71fb6e7e66b918803b1bebd0231560981ab86c2546a3318a45822ce94de5e83d"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:614c427ff235d92f103e9189f0230197c8f2f817d0dd9fd078f5d2ea4d920d02"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:c13e0cb486cfa15320ddcd70452a4d736e6ce319c03d6b3c0c2513ec8d2748fb"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:5ade6b13dc4e148f400c8f55a6ef0b14216a3371d7a9e559571d5981b6cec36b"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6138d2c7eec7ed57585bc58e2dbcb65635a2d574ac632abd29949d3e68936bab"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:3d6c8548b199591757dbfe89ed14e23782d6079d6d201c6c314c72f4086883aa"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b41c419829f01734d65958ba9b01b759061d8f7e0698f9612ba6b8837269f7a9"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9f0c5b4567631fec993826e694e83d86a972b3e2e9b05cb0c56839b0316d26c"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:3f0e1d1f3f5a6f0c9f8b5441819dbec831ce7e9ffe04768e4b0d965a95fbbe5e"}, + {file = "grpcio_tools-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f87fc86d0b4181b6b4da6ec6a29511dca000e6b5694fdd6bbf87d125128bc41"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:cb8baa1d4cea35ca662c24098377bdd9514c56f227da0e38b43cd9b8223bfcc6"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:ea36a294f7c70fd2f2bfb5dcf08602006304aa65b055ebd4f7c709e2a89deba7"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1972caf8f695b91edc6444134445798692fe71276f0cde7604d55e65179adf93"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:674fb8d9c0e2d75166c4385753962485b757897223fc92a19c9e513ab80b96f7"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:37045ba850d423cdacede77b266b127025818a5a36d80f1fd7a5a1614a6a0de5"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cdf72947c6b0b03aa6dac06117a095947d02d43a5c6343051f4ce161fd0abcb"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bfa6fc1515c202fe428ba9f99e2b2f947b01bafc15d868798235b2e2d36baa"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-win32.whl", hash = "sha256:2c516124356476d9afa126acce10ce568733120afbd9ae17ee01d44b9da20a67"}, + {file = "grpcio_tools-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6441c24176705c5ab056e65a8b330e107107c5a492ba094d1b862a136d15d"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:398eda759194d355eb09f7beabae6e4fb45b3877cf7efe505b49095fa4889cef"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:a169bfd7a1fe8cc11472eeeeab3088b3c5d56caac12b2192a920b73adcbc974c"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:a58aaaec0d846d142edd8e794ebb80aa429abfd581f4493a60a603aac0c50ac8"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c3253bee8b68fe422754faf0f286aa068861c926a7b11e4daeb44b9af767c7f1"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3c0be60721ae1ba09c4f29572a145f412e561b9201e19428758893709827f472"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e44b9572c2226b85976e0d6054e22d7c59ebd6c9425ee71e5bc8910434aee3e1"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c04ec47905c4f6d6dad34d29f6ace652cc1ddc986f55aaa5559b72104c3f5cf"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-win32.whl", hash = "sha256:fb8c7b9d24e2c4dc77e7800e83b68081729ac6094b781b2afdabf08af18c3b28"}, + {file = "grpcio_tools-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:4eb93619c8cb3773fb899504e3e30a0dc79d3904fd7a84091d15552178e1e920"}, +] importlib-metadata = [ {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, @@ -1077,6 +1203,34 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] +protobuf = [ + {file = "protobuf-3.19.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f51d5a9f137f7a2cec2d326a74b6e3fc79d635d69ffe1b036d39fc7d75430d37"}, + {file = "protobuf-3.19.4-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:09297b7972da685ce269ec52af761743714996b4381c085205914c41fcab59fb"}, + {file = "protobuf-3.19.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072fbc78d705d3edc7ccac58a62c4c8e0cec856987da7df8aca86e647be4e35c"}, + {file = "protobuf-3.19.4-cp310-cp310-win32.whl", hash = "sha256:7bb03bc2873a2842e5ebb4801f5c7ff1bfbdf426f85d0172f7644fcda0671ae0"}, + {file = "protobuf-3.19.4-cp310-cp310-win_amd64.whl", hash = "sha256:f358aa33e03b7a84e0d91270a4d4d8f5df6921abe99a377828839e8ed0c04e07"}, + {file = "protobuf-3.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1c91ef4110fdd2c590effb5dca8fdbdcb3bf563eece99287019c4204f53d81a4"}, + {file = "protobuf-3.19.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c438268eebb8cf039552897d78f402d734a404f1360592fef55297285f7f953f"}, + {file = "protobuf-3.19.4-cp36-cp36m-win32.whl", hash = "sha256:835a9c949dc193953c319603b2961c5c8f4327957fe23d914ca80d982665e8ee"}, + {file = "protobuf-3.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4276cdec4447bd5015453e41bdc0c0c1234eda08420b7c9a18b8d647add51e4b"}, + {file = "protobuf-3.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6cbc312be5e71869d9d5ea25147cdf652a6781cf4d906497ca7690b7b9b5df13"}, + {file = "protobuf-3.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54a1473077f3b616779ce31f477351a45b4fef8c9fd7892d6d87e287a38df368"}, + {file = "protobuf-3.19.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:435bb78b37fc386f9275a7035fe4fb1364484e38980d0dd91bc834a02c5ec909"}, + {file = "protobuf-3.19.4-cp37-cp37m-win32.whl", hash = "sha256:16f519de1313f1b7139ad70772e7db515b1420d208cb16c6d7858ea989fc64a9"}, + {file = "protobuf-3.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:cdc076c03381f5c1d9bb1abdcc5503d9ca8b53cf0a9d31a9f6754ec9e6c8af0f"}, + {file = "protobuf-3.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:69da7d39e39942bd52848438462674c463e23963a1fdaa84d88df7fbd7e749b2"}, + {file = "protobuf-3.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:48ed3877fa43e22bcacc852ca76d4775741f9709dd9575881a373bd3e85e54b2"}, + {file = "protobuf-3.19.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd95d1dfb9c4f4563e6093a9aa19d9c186bf98fa54da5252531cc0d3a07977e7"}, + {file = "protobuf-3.19.4-cp38-cp38-win32.whl", hash = "sha256:b38057450a0c566cbd04890a40edf916db890f2818e8682221611d78dc32ae26"}, + {file = "protobuf-3.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:7ca7da9c339ca8890d66958f5462beabd611eca6c958691a8fe6eccbd1eb0c6e"}, + {file = "protobuf-3.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:36cecbabbda242915529b8ff364f2263cd4de7c46bbe361418b5ed859677ba58"}, + {file = "protobuf-3.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c1068287025f8ea025103e37d62ffd63fec8e9e636246b89c341aeda8a67c934"}, + {file = "protobuf-3.19.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96bd766831596d6014ca88d86dc8fe0fb2e428c0b02432fd9db3943202bf8c5e"}, + {file = "protobuf-3.19.4-cp39-cp39-win32.whl", hash = "sha256:84123274d982b9e248a143dadd1b9815049f4477dc783bf84efe6250eb4b836a"}, + {file = "protobuf-3.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:3112b58aac3bac9c8be2b60a9daf6b558ca3f7681c130dcdd788ade7c9ffbdca"}, + {file = "protobuf-3.19.4-py2.py3-none-any.whl", hash = "sha256:8961c3a78ebfcd000920c9060a262f082f29838682b1f7201889300c1fbe0616"}, + {file = "protobuf-3.19.4.tar.gz", hash = "sha256:9df0c10adf3e83015ced42a9a7bd64e13d06c4cf45c340d2c63020ea04499d0a"}, +] psutil = [ {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"}, {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"}, diff --git a/pyproject.toml b/pyproject.toml index 2e29984de8d8..4d7b09c3e6c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ pyln-proto = { path = "./contrib/pyln-proto", develop = true } Mako = "^1.1.6" mrkd = { git = "https://github.com/refi64/mrkd.git", rev = "781f05eb9898ca652f18eed29b3c956389e6a2a7" } websocket-client = "^1.2.3" +grpcio-tools = "^1.44.0" [tool.poetry.dev-dependencies] # Test dependencies and inherited dependencies belong here From bf7ad86ef255c2a212869fbc84e2bec12be6c035 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 22 Mar 2022 16:49:12 +0100 Subject: [PATCH 0581/1530] cln-grpc: Rename the grpc plugin and binary Suggested-by: Rusty Russell <@rustyrussell> Changelog-Added: plugins: `cln-grpc` first class GRPC interface for remotely controlling nodes over mTLS authentication --- plugins/Makefile | 6 +++--- plugins/grpc-plugin/Cargo.toml | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index e3cacde4a498..b30b988cabbb 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -179,11 +179,11 @@ CLN_PLUGIN_SRC = $(shell find plugins/src -name "*.rs") ${CLN_PLUGIN_EXAMPLES}: ${CLN_PLUGIN_SRC} (cd plugins; cargo build ${CARGO_OPTS} --examples) -target/${RUST_PROFILE}/grpc-plugin: ${CLN_PLUGIN_SRC} - cargo build ${CARGO_OPTS} --bin grpc-plugin +target/${RUST_PROFILE}/cln-grpc: ${CLN_PLUGIN_SRC} + cargo build ${CARGO_OPTS} --bin cln-grpc ifneq ($(RUST),0) -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/grpc-plugin +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/cln-grpc endif include plugins/test/Makefile diff --git a/plugins/grpc-plugin/Cargo.toml b/plugins/grpc-plugin/Cargo.toml index 21e56727d2f9..634248ef2a49 100644 --- a/plugins/grpc-plugin/Cargo.toml +++ b/plugins/grpc-plugin/Cargo.toml @@ -1,8 +1,12 @@ [package] edition = "2021" -name = "grpc-plugin" +name = "cln-grpc-plugin" version = "0.1.0" +[[bin]] +name = "cln-grpc" +path = "src/main.rs" + [dependencies] anyhow = "1.0" log = "0.4" From 09ee28cb517137b6f9b44ce407efc23ce85df69e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 23 Mar 2022 11:50:36 +0100 Subject: [PATCH 0582/1530] cln-grpc: Do not start unless a `grpc-port` is specified For now we don't want to autostart. Suggested-by: Rusty Russell <@rustyrussell> --- plugins/grpc-plugin/src/main.rs | 6 +++++- tests/test_cln_rs.py | 20 ++++++++++++++++---- tools/build-release.sh | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 8636fc5adec0..369373034c62 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -31,13 +31,17 @@ async fn main() -> Result<()> { let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "grpc-port", - options::Value::Integer(50051), + options::Value::Integer(-1), "Which port should the grpc plugin listen for incoming connections?", )) .start() .await?; let bind_port = match plugin.option("grpc-port") { + Some(options::Value::Integer(-1)) => { + log::info!("`grpc-port` option is not configured, exiting."); + return Ok(()); + } Some(options::Value::Integer(i)) => i, None => return Err(anyhow!("Missing 'grpc-port' option")), Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)), diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index e2b0ef53d974..edabc7c581eb 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -1,7 +1,7 @@ from fixtures import * # noqa: F401,F403 from node_pb2_grpc import NodeStub from pathlib import Path -from pyln.testing.utils import env, TEST_NETWORK +from pyln.testing.utils import env, TEST_NETWORK, wait_for from ephemeral_port_reserve import reserve import grpc import node_pb2 as nodepb @@ -69,7 +69,7 @@ def test_plugin_start(node_factory): def test_grpc_connect(node_factory): """Attempts to connect to the grpc interface and call getinfo""" grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1 = node_factory.get_node(options={"plugin": str(bin_path), "grpc-port": str(grpc_port)}) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK @@ -105,7 +105,7 @@ def test_grpc_generate_certificate(node_factory): - If we delete one cert or its key it should get regenerated. """ grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1 = node_factory.get_node(options={ "plugin": str(bin_path), "grpc-port": str(grpc_port), @@ -139,6 +139,18 @@ def test_grpc_generate_certificate(node_factory): assert contents[-1] != files[-1].open().read() +def test_grpc_no_auto_start(node_factory): + """Ensure that we do not start cln-grpc unless a port is configured. + """ + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" + l1, = node_factory.get_nodes(1, opts={ + "plugin": str(bin_path), + }) + + wait_for(lambda: [p for p in l1.rpc.plugin('list')['plugins'] if 'cln-grpc' in p['name']] == []) + assert l1.daemon.is_in_log(r'plugin-cln-grpc: Killing plugin: exited during normal operation') + + def test_grpc_wrong_auth(node_factory): """An mTLS client certificate should only be usable with its node @@ -146,7 +158,7 @@ def test_grpc_wrong_auth(node_factory): and then we try to cross the wires. """ grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "grpc-plugin" + bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1, l2 = node_factory.get_nodes(2, opts={ "plugin": str(bin_path), "start": False, diff --git a/tools/build-release.sh b/tools/build-release.sh index cb3231b4896b..bd06d411949a 100755 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -8,7 +8,7 @@ if [ "$1" = "--inside-docker" ]; then cd /build ./configure make - make install DESTDIR=/"$VER" + make install DESTDIR=/"$VER" RUST_PROFILE=release cd /"$VER" && tar cvfz /release/clightning-"$VER".tar.gz -- * exit 0 fi From ae5b98a727a272e349dc7d370bba7cf262fa514c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 29 Mar 2022 11:16:33 +1030 Subject: [PATCH 0583/1530] pytest: fix flake due to cln-grpc starting before "public key" message. e.g. ``` lightningd-1: 2022-03-28T11:02:12.476Z DEBUG plugin-cln-grpc: add_pem_file processed 1 valid and 0 invalid certs lightningd-1: 2022-03-28T11:02:12.478Z DEBUG plugin-cln-grpc: Connecting to \"lightning-rpc\" and serving grpc on 0.0.0.0:36331 lightningd-1: 2022-03-28T11:02:12.478Z DEBUG connectd: REPLY WIRE_CONNECTD_ACTIVATE_REPLY with 0 fds lightningd-1: 2022-03-28T11:02:12.478Z INFO lightningd: -------------------------------------------------- lightningd-1: 2022-03-28T11:02:12.478Z INFO lightningd: Server started with public key ``` Which means we don't see it, since start() swallows it: ``` > raise TimeoutError('Unable to find "{}" in logs.'.format(exs)) E TimeoutError: Unable to find "[re.compile('serving grpc on 0.0.0.0:')]" in logs. ``` Signed-off-by: Rusty Russell --- tests/test_cln_rs.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index edabc7c581eb..4fb62b81084c 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -15,6 +15,11 @@ ) +def wait_for_grpc_start(node): + """This can happen before "public key" which start() swallows""" + wait_for(lambda: node.daemon.is_in_log(r'serving grpc on 0.0.0.0:')) + + def test_rpc_client(node_factory): l1 = node_factory.get_node() bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-rpc-getinfo" @@ -82,7 +87,7 @@ def test_grpc_connect(node_factory): certificate_chain=cert_path.open('rb').read() ) - l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + wait_for_grpc_start(l1) channel = grpc.secure_channel( f"localhost:{grpc_port}", creds, @@ -165,7 +170,7 @@ def test_grpc_wrong_auth(node_factory): "grpc-port": str(grpc_port), }) l1.start() - l1.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + wait_for_grpc_start(l1) def connect(node): p = Path(node.daemon.lightning_dir) / TEST_NETWORK @@ -193,7 +198,7 @@ def connect(node): l1.stop() l2.start() - l2.daemon.wait_for_log(r'serving grpc on 0.0.0.0:') + wait_for_grpc_start(l2) # This should not work, it's a different node with pytest.raises(Exception, match=r'Socket closed|StatusCode.UNAVAILABLE'): From 0cbf918af1e9f8bafbdcdb9f30c934dec37df2f5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 29 Mar 2022 12:29:42 +0200 Subject: [PATCH 0584/1530] cln-grpc: Set cln-grpc version to 0.0.1 until it's complete Suggested-by: Vincenzo Palazzo <@vincenzopalazzo> --- cln-grpc/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cln-grpc/Cargo.toml b/cln-grpc/Cargo.toml index 0204e4237b48..c0bccbe599d5 100644 --- a/cln-grpc/Cargo.toml +++ b/cln-grpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cln-grpc" -version = "0.1.0" +version = "0.0.1" edition = "2021" [dependencies] From 0c3aa355dbf64f1554b32077196419d794dad0ee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Mar 2022 12:54:29 +1030 Subject: [PATCH 0585/1530] tests: add test that pay gets update correct. This is the part which works: we have another old PR (or simply uncomment the FIXME section) for the routehint case. Closes: #4781 See-also: #4808 Signed-off-by: Rusty Russell --- tests/test_pay.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index d55dade1de5e..1a4c23f50419 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -312,6 +312,40 @@ def test_pay_get_error_with_update(node_factory): wait_for(lambda: not l1.is_channel_active(chanid2)) +@pytest.mark.developer("needs DEVELOPER=1 for dev_suppress_gossip, dev-routes") +def test_pay_error_update_fees(node_factory): + """We should process an update inside a temporary_channel_failure""" + l1, l2, l3 = node_factory.line_graph(3, fundchannel=True, wait_for_announce=True) + + # Don't include any routehints in first invoice. + inv1 = l3.rpc.call('invoice', + {'msatoshi': 123000, + 'label': 'test_pay_error_update_fees', + 'description': 'description', + 'dev-routes': []}) + + inv2 = l3.rpc.invoice(123000, 'test_pay_error_update_fees2', 'desc') # noqa: F841 + + # Make sure l2 doesn't tell l1 directly that channel fee is changed. + l2.rpc.dev_suppress_gossip() + l2.rpc.setchannel(l3.info['id'], 1337, 137, enforcedelay=0) + + # Should bounce off and retry... + l1.rpc.pay(inv1['bolt11']) + attempts = only_one(l1.rpc.paystatus(inv1['bolt11'])['pay'])['attempts'] + assert len(attempts) == 2 + # WIRE_FEE_INSUFFICIENT = UPDATE|12 + assert attempts[0]['failure']['data']['failcode'] == 4108 + + # FIXME: We *DO NOT* handle misleading routehints! + # # Should ignore old routehint and do the same... + # l1.rpc.pay(inv2['bolt11']) + # attempts = only_one(l1.rpc.paystatus(inv2['bolt11'])['pay'])['attempts'] + # assert len(attempts) == 2 + # # WIRE_FEE_INSUFFICIENT = UPDATE|12 + # assert attempts[0]['failure']['data']['failcode'] == 4108 + + @pytest.mark.developer("needs to deactivate shadow routing") def test_pay_optional_args(node_factory, compat): l1, l2 = node_factory.line_graph(2) From 5cb4705eb48a2483a73f99fe9adfef9246abcc7d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 28 Mar 2022 10:58:12 +1030 Subject: [PATCH 0586/1530] unit tests: don't crash if !HAVE_SQLITE3. Fixes: #4928 Reported-by: @whitslack Signed-off-by: Rusty Russell --- wallet/test/run-db.c | 11 +++++++---- wallet/test/run-wallet.c | 21 ++++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 54d1a107b92e..054ddc31d3b3 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -262,10 +262,13 @@ int main(int argc, char *argv[]) common_setup(argv[0]); ld->config = test_config; - ok &= test_empty_db_migrate(ld); - ok &= test_vars(ld); - ok &= test_primitives(); - ok &= test_manip_columns(); + /* We do a runtime test here, so we still check compile! */ + if (HAVE_SQLITE3) { + ok &= test_empty_db_migrate(ld); + ok &= test_vars(ld); + ok &= test_primitives(); + ok &= test_manip_columns(); + } tal_free(ld); common_shutdown(); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index f095d170c796..b4dc58e20e0c 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1906,15 +1906,18 @@ int main(int argc, const char *argv[]) htlc_in_map_init(&ld->htlcs_in); htlc_out_map_init(&ld->htlcs_out); - ok &= test_shachain_crud(ld, tmpctx); - ok &= test_channel_crud(ld, tmpctx); - ok &= test_channel_inflight_crud(ld, tmpctx); - ok &= test_channel_config_crud(ld, tmpctx); - ok &= test_channel_inflight_crud(ld, tmpctx); - ok &= test_wallet_outputs(ld, tmpctx); - ok &= test_htlc_crud(ld, tmpctx); - ok &= test_payment_crud(ld, tmpctx); - ok &= test_wallet_payment_status_enum(); + /* We do a runtime test here, so we still check compile! */ + if (HAVE_SQLITE3) { + ok &= test_shachain_crud(ld, tmpctx); + ok &= test_channel_crud(ld, tmpctx); + ok &= test_channel_inflight_crud(ld, tmpctx); + ok &= test_channel_config_crud(ld, tmpctx); + ok &= test_channel_inflight_crud(ld, tmpctx); + ok &= test_wallet_outputs(ld, tmpctx); + ok &= test_htlc_crud(ld, tmpctx); + ok &= test_payment_crud(ld, tmpctx); + ok &= test_wallet_payment_status_enum(); + } /* Do not clean up in the case of an error, we might want to debug the * database. */ From 5cdb16a93cdd674b6725be3006dd99854ac32065 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 24 Mar 2022 13:14:53 +1030 Subject: [PATCH 0587/1530] plugins/pay: don't crash on malformed time. See: https://github.com/ElementsProject/lightning/issues/4991 We seem to correctly set end_time everywhere, so this looks like a use-after-free somehow? But this will fix the crash right here :( Signed-off-by: Rusty Russell --- plugins/pay.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 56e05792de0d..7d08e1731e43 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1387,9 +1387,15 @@ static struct command_result *json_pay(struct command *cmd, static void utc_timestring(const struct timeabs *time, char str[UTC_TIMELEN]) { char iso8601_msec_fmt[sizeof("YYYY-mm-ddTHH:MM:SS.%03dZ")]; + struct tm *t = gmtime(&time->ts.tv_sec); - strftime(iso8601_msec_fmt, sizeof(iso8601_msec_fmt), "%FT%T.%%03dZ", - gmtime(&time->ts.tv_sec)); + /* Shouldn't happen, but see + * https://github.com/ElementsProject/lightning/issues/4991 :( */ + if (!t) { + snprintf(str, UTC_TIMELEN, "1970-01-01T00:00:00.000Z"); + return; + } + strftime(iso8601_msec_fmt, sizeof(iso8601_msec_fmt), "%FT%T.%%03dZ", t); snprintf(str, UTC_TIMELEN, iso8601_msec_fmt, (int) time->ts.tv_nsec / 1000000); } From 20523f1b03ebced33eea14b1cbd21bb094cfa1bf Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 25 Mar 2022 10:10:39 +0100 Subject: [PATCH 0588/1530] ci: migrate the protocol test on docker image Signed-off-by: Vincenzo Palazzo --- .github/workflows/ci.yaml | 46 ++++++------- contrib/docker/Dockerfile.ubuntu | 36 +++++++++++ contrib/docker/scripts/build.sh | 97 ++++++++++++++++++++++++++++ contrib/docker/scripts/entrypoint.sh | 3 + contrib/docker/scripts/setup.sh | 78 ++++++++++++++++++++++ 5 files changed, 232 insertions(+), 28 deletions(-) create mode 100644 contrib/docker/Dockerfile.ubuntu create mode 100755 contrib/docker/scripts/build.sh create mode 100755 contrib/docker/scripts/entrypoint.sh create mode 100755 contrib/docker/scripts/setup.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 8ccc22d7ea18..400b624bf476 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,6 +9,7 @@ jobs: smoke-test: name: Smoke Test ${{ matrix.cfg }} runs-on: ubuntu-20.04 + timeout-minutes: 300 env: DEVELOPER: 1 VALGRIND: 0 @@ -98,15 +99,8 @@ jobs: proto-test: name: Protocol Test Config runs-on: ubuntu-20.04 + timeout-minutes: 300 needs: [smoke-test] - env: - DEVELOPER: 1 - EXPERIMENTAL_FEATURES: 1 - COMPAT: 0 - PYTEST_PAR: 2 - TEST_CMD: "make check-protos" - TEST_GROUP: 1 - TEST_GROUP_COUNT: 1 strategy: fail-fast: true matrix: @@ -116,27 +110,23 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2.0.0 - - - name: Setup Python 3.7 - uses: actions/setup-python@v2 - with: - python-version: 3.7 - - - name: Install dependencies + - name: Build and run run: | - bash -x .github/scripts/setup.sh - - - name: Build - env: - ARCH: ${{ matrix.arch }} - COMPILER: ${{ matrix.compiler }} - DB: ${{ matrix.db }} - NETWORK: ${{ matrix.network }} - TARGET_HOST: ${{ matrix.TARGET_HOST }} - VALGRIND: ${{ matrix.valgrind }} - run: | - bash -x .github/scripts/build.sh - + docker build -f contrib/docker/Dockerfile.ubuntu -t cln-ci-ubuntu . + docker run -e ARCH=${{ matrix.arch }} \ + -e COMPILER=${{ matrix.compiler }} \ + -e DB=${{ matrix.db }} \ + -e NETWORK=${{ matrix.network }} \ + -e TARGET_HOST=${{ matrix.TARGET_HOST }} \ + -e VALGRIND=${{ matrix.valgrind }} \ + -e DEVELOPER=1 \ + -e EXPERIMENTAL_FEATURES=1 \ + -e COMPA=0 \ + -e PYTEST_PAR=2 \ + -e TEST_CMD="make check-protos" \ + -e TEST_GROUP=1 \ + -e TEST_GROUP_COUNT=1 \ + cln-ci-ubuntu - name: Upload Unit Test Results if: always() uses: actions/upload-artifact@v2 diff --git a/contrib/docker/Dockerfile.ubuntu b/contrib/docker/Dockerfile.ubuntu new file mode 100644 index 000000000000..35d1f930ce20 --- /dev/null +++ b/contrib/docker/Dockerfile.ubuntu @@ -0,0 +1,36 @@ +FROM ubuntu:20.04 +LABEL mantainer="Vincenzo Palazzo vincenzopalazzodev@gmail.com" + +WORKDIR /work + +COPY . . + +ENV DEBIAN_FRONTEND=noninteractive +ENV LANGUAGE=en_US.UTF-8 +ENV LANG=en_US.UTF-8 +ENV LC_ALL=en_US.UTF-8 +ENV TZ="Europe/London" + +RUN apt-get -qq update && \ + apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ + sudo \ + locales \ + tzdata + +RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime + +RUN locale-gen en_US.UTF-8 && dpkg-reconfigure --frontend noninteractive tzdata + +COPY . . + +# install package for pytho cryptography lib +# https://cryptography.io/en/latest/installation/#debian-ubuntu + +RUN apt-get -qq update && \ + apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ + build-essential libssl-dev libffi-dev \ + python3-dev cargo + +RUN ls -la + +CMD ["./contrib/docker/scripts/entrypoint.sh"] diff --git a/contrib/docker/scripts/build.sh b/contrib/docker/scripts/build.sh new file mode 100755 index 000000000000..05cece10fa49 --- /dev/null +++ b/contrib/docker/scripts/build.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +echo "Running in $(pwd)" +export ARCH=${ARCH:-64} +export BOLTDIR=lightning-rfc +export CC=${COMPILER:-gcc} +export COMPAT=${COMPAT:-1} +export TEST_CHECK_DBSTMTS=${TEST_CHECK_DBSTMTS:-0} +export DEVELOPER=${DEVELOPER:-1} +export EXPERIMENTAL_FEATURES=${EXPERIMENTAL_FEATURES:-0} +export PATH=$CWD/dependencies/bin:"$HOME"/.local/bin:"$PATH" +export PYTEST_OPTS="--maxfail=5 --suppress-no-test-exit-code ${PYTEST_OPTS}" +export PYTEST_PAR=${PYTEST_PAR:-10} +export PYTEST_SENTRY_ALWAYS_REPORT=1 +export SLOW_MACHINE=1 +export TEST_CMD=${TEST_CMD:-"make -j $PYTEST_PAR pytest"} +export TEST_DB_PROVIDER=${TEST_DB_PROVIDER:-"sqlite3"} +export TEST_NETWORK=${NETWORK:-"regtest"} +export TIMEOUT=900 +export VALGRIND=${VALGRIND:-0} +export FUZZING=${FUZZING:-0} +export LIGHTNINGD_POSTGRES_NO_VACUUM=1 + +pip3 install --user poetry +poetry config virtualenvs.create false --local +poetry install + +git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc +git submodule update --init --recursive + +./configure CC="$CC" +cat config.vars + +cat << EOF > pytest.ini +[pytest] +addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 +markers = + slow_test: marks tests as slow (deselect with '-m "not slow_test"') +EOF + +if [ "$TARGET_HOST" == "arm-linux-gnueabihf" ] || [ "$TARGET_HOST" == "aarch64-linux-gnu" ] +then + export QEMU_LD_PREFIX=/usr/"$TARGET_HOST"/ + export MAKE_HOST="$TARGET_HOST" + export BUILD=x86_64-pc-linux-gnu + export AR="$TARGET_HOST"-ar + export AS="$TARGET_HOST"-as + export CC="$TARGET_HOST"-gcc + export CXX="$TARGET_HOST"-g++ + export LD="$TARGET_HOST"-ld + export STRIP="$TARGET_HOST"-strip + export CONFIGURATION_WRAPPER=qemu-"${TARGET_HOST%%-*}"-static + + wget -q https://zlib.net/zlib-1.2.11.tar.gz + tar xf zlib-1.2.11.tar.gz + cd zlib-1.2.11 || exit 1 + ./configure --prefix="$QEMU_LD_PREFIX" + make + sudo make install + cd .. || exit 1 + rm zlib-1.2.11.tar.gz && rm -rf zlib-1.2.11 + + wget -q https://www.sqlite.org/2018/sqlite-src-3260000.zip + unzip -q sqlite-src-3260000.zip + cd sqlite-src-3260000 || exit 1 + automake --add-missing --force-missing --copy || true + ./configure --disable-tcl \ + --enable-static \ + --disable-readline \ + --disable-threadsafe \ + --disable-load-extension \ + --host="$TARGET_HOST" \ + --prefix="$QEMU_LD_PREFIX" + make + sudo make install + cd .. || exit 1 + rm sqlite-src-3260000.zip + rm -rf sqlite-src-3260000 + + wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz + tar xf gmp-6.1.2.tar.xz + cd gmp-6.1.2 || exit 1 + ./configure --disable-assembly --prefix="$QEMU_LD_PREFIX" --host="$TARGET_HOST" + make + sudo make install + cd .. + rm gmp-6.1.2.tar.xz + rm -rf gmp-6.1.2 + + ./configure CC="$TARGET_HOST-gcc" --enable-static + + make -j32 CC="$TARGET_HOST-gcc" > /dev/null +else + eatmydata make -j32 + # shellcheck disable=SC2086 + eatmydata $TEST_CMD +fi diff --git a/contrib/docker/scripts/entrypoint.sh b/contrib/docker/scripts/entrypoint.sh new file mode 100755 index 000000000000..83f2410c483d --- /dev/null +++ b/contrib/docker/scripts/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd contrib/docker/scripts && \ + ./setup.sh && ./build.sh diff --git a/contrib/docker/scripts/setup.sh b/contrib/docker/scripts/setup.sh new file mode 100755 index 000000000000..208571e1d89c --- /dev/null +++ b/contrib/docker/scripts/setup.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +export DEBIAN_FRONTEND=noninteractive +export BITCOIN_VERSION=0.20.1 +export ELEMENTS_VERSION=0.18.1.8 +export RUST_VERSION=nightly +export TZ="Europe/London" + +sudo useradd -ms /bin/bash tester + +sudo apt-get update -qq + +sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ + autoconf \ + automake \ + binfmt-support \ + build-essential \ + clang \ + cppcheck \ + docbook-xml \ + eatmydata \ + gcc-aarch64-linux-gnu \ + gcc-arm-linux-gnueabihf \ + gcc-arm-none-eabi \ + gettext \ + git \ + libc6-dev-arm64-cross \ + libc6-dev-armhf-cross \ + libgmp-dev \ + libpq-dev \ + libprotobuf-c-dev \ + libsqlite3-dev \ + libtool \ + libxml2-utils \ + locales \ + net-tools \ + postgresql \ + python-pkg-resources \ + python3 \ + python3-dev \ + python3-pip \ + python3-setuptools \ + qemu \ + qemu-system-arm \ + qemu-user-static \ + shellcheck \ + software-properties-common \ + sudo \ + tcl \ + unzip \ + valgrind \ + wget \ + xsltproc \ + zlib1g-dev + + +echo "tester ALL=(root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/tester +sudo chmod 0440 /etc/sudoers.d/tester + +( + cd /tmp/ || exit 1 + wget https://storage.googleapis.com/c-lightning-tests/bitcoin-$BITCOIN_VERSION-x86_64-linux-gnu.tar.bz2 + wget -q https://storage.googleapis.com/c-lightning-tests/elements-$ELEMENTS_VERSION-x86_64-linux-gnu.tar.bz2 + tar -xjf bitcoin-$BITCOIN_VERSION-x86_64-linux-gnu.tar.bz2 + tar -xjf elements-$ELEMENTS_VERSION-x86_64-linux-gnu.tar.bz2 + sudo mv bitcoin-$BITCOIN_VERSION/bin/* /usr/local/bin + sudo mv elements-$ELEMENTS_VERSION/bin/* /usr/local/bin + rm -rf \ + bitcoin-$BITCOIN_VERSION-x86_64-linux-gnu.tar.gz \ + bitcoin-$BITCOIN_VERSION \ + elements-$ELEMENTS_VERSION-x86_64-linux-gnu.tar.bz2 \ + elements-$ELEMENTS_VERSION +) + +if [ "$RUST" == "1" ]; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- \ + -y --default-toolchain ${RUST_VERSION} +fi From d9d30fb8e01862bbd8530bb73591ba81dec80a4d Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 25 Mar 2022 15:04:39 +0100 Subject: [PATCH 0589/1530] docker-ci: upgrade pip version in the bash script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI fails with the error ``` EnvCommandError Command ['/usr/bin/python3', '-m', 'pip', 'install', '--no-deps', '-U', '/root/.cache/pypoetry/artifacts/07/6f/ab/ca33bde7c6751a5ad8d13495b766891cd70e61786112885733ce9b0562/cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl'] errored with the following return code 1, and output: ERROR: cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl is not a supported wheel on this platform. at ~/.local/lib/python3.8/site-packages/poetry/utils/env.py:1195 in _run 1191│ output = subprocess.check_output( 1192│ cmd, stderr=subprocess.STDOUT, **kwargs 1193│ ) 1194│ except CalledProcessError as e: → 1195│ raise EnvCommandError(e, input=input_) 1196│ 1197│ return decode(output) 1198│ 1199│ def execute(self, bin, *args, **kwargs): ``` The solution is to upgrade the pip version as suggested in https://github.com/python-poetry/poetry/issues/2688#issuecomment-937837619 Signed-off-by: Vincenzo Palazzo x --- contrib/docker/scripts/build.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/docker/scripts/build.sh b/contrib/docker/scripts/build.sh index 05cece10fa49..3f41cc594986 100755 --- a/contrib/docker/scripts/build.sh +++ b/contrib/docker/scripts/build.sh @@ -21,6 +21,7 @@ export VALGRIND=${VALGRIND:-0} export FUZZING=${FUZZING:-0} export LIGHTNINGD_POSTGRES_NO_VACUUM=1 +pip3 install --upgrade pip pip3 install --user poetry poetry config virtualenvs.create false --local poetry install @@ -28,8 +29,12 @@ poetry install git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc git submodule update --init --recursive +# Used in docker image +cd /work || return +ls -la + ./configure CC="$CC" -cat config.vars +# cat config.vars cat << EOF > pytest.ini [pytest] From 154a391cf7580c2a793626a799ac5ac2e8970ff6 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 25 Mar 2022 23:45:30 +0100 Subject: [PATCH 0590/1530] ci: adding pytest parameter to tracing lnprotetest Signed-off-by: Vincenzo Palazzo --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 400b624bf476..41936c999c83 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -123,6 +123,7 @@ jobs: -e EXPERIMENTAL_FEATURES=1 \ -e COMPA=0 \ -e PYTEST_PAR=2 \ + -e PYTEST_OPTS="--timeout=300" \ -e TEST_CMD="make check-protos" \ -e TEST_GROUP=1 \ -e TEST_GROUP_COUNT=1 \ From a35e1b23e6884bc79ef1e6470a16b5796bcc6191 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 30 Mar 2022 23:25:38 +0200 Subject: [PATCH 0591/1530] docker-ci: fixed script path inside the entry point Signed-off-by: Vincenzo Palazzo --- contrib/docker/Dockerfile.ubuntu | 3 +++ contrib/docker/scripts/build.sh | 6 +----- contrib/docker/scripts/entrypoint.sh | 4 ++-- external/lnprototest | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contrib/docker/Dockerfile.ubuntu b/contrib/docker/Dockerfile.ubuntu index 35d1f930ce20..93ffee4b662a 100644 --- a/contrib/docker/Dockerfile.ubuntu +++ b/contrib/docker/Dockerfile.ubuntu @@ -31,6 +31,9 @@ RUN apt-get -qq update && \ build-essential libssl-dev libffi-dev \ python3-dev cargo +# move the script in the root of the directory +RUN cp contrib/docker/scripts/*.sh . + RUN ls -la CMD ["./contrib/docker/scripts/entrypoint.sh"] diff --git a/contrib/docker/scripts/build.sh b/contrib/docker/scripts/build.sh index 3f41cc594986..1650e793c794 100755 --- a/contrib/docker/scripts/build.sh +++ b/contrib/docker/scripts/build.sh @@ -29,12 +29,8 @@ poetry install git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc git submodule update --init --recursive -# Used in docker image -cd /work || return -ls -la - ./configure CC="$CC" -# cat config.vars +cat config.vars cat << EOF > pytest.ini [pytest] diff --git a/contrib/docker/scripts/entrypoint.sh b/contrib/docker/scripts/entrypoint.sh index 83f2410c483d..28e4f5bc2388 100755 --- a/contrib/docker/scripts/entrypoint.sh +++ b/contrib/docker/scripts/entrypoint.sh @@ -1,3 +1,3 @@ #!/bin/bash -cd contrib/docker/scripts && \ - ./setup.sh && ./build.sh +./setup.sh +./build.sh diff --git a/external/lnprototest b/external/lnprototest index ceeb8667f745..433d22f16c3f 160000 --- a/external/lnprototest +++ b/external/lnprototest @@ -1 +1 @@ -Subproject commit ceeb8667f745fce80ba81e4b23c6717d72e22bbc +Subproject commit 433d22f16c3f550317315ce56a445e6d074a0f5a From ea7120a313eeaf560b5a719750b4253c7d53cbda Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 25 Mar 2022 13:41:55 +1030 Subject: [PATCH 0592/1530] lightningd: add --dev-no-ping-timer to avoid ping response timeouts. Signed-off-by: Rusty Russell --- connectd/connectd.c | 8 +++++--- connectd/connectd.h | 2 ++ connectd/connectd_wire.csv | 1 + connectd/multiplex.c | 4 ++++ lightningd/connect_control.c | 3 ++- lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 +++ lightningd/options.c | 3 +++ 8 files changed, 21 insertions(+), 4 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index b2b07eaab3c4..71f0d8976ded 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1517,7 +1517,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) struct wireaddr *announceable; char *tor_password; bool dev_fast_gossip; - bool dev_disconnect; + bool dev_disconnect, dev_no_ping_timer; char *errstr; /* Fields which require allocation are allocated off daemon */ @@ -1536,15 +1536,17 @@ static void connect_init(struct daemon *daemon, const u8 *msg) &daemon->websocket_helper, &daemon->websocket_port, &dev_fast_gossip, - &dev_disconnect)) { + &dev_disconnect, + &dev_no_ping_timer)) { /* This is a helper which prints the type expected and the actual * message, then exits (it should never be called!). */ master_badmsg(WIRE_CONNECTD_INIT, msg); } #if DEVELOPER - /*~ Clearly mark this as a developer-only flag! */ + /*~ Clearly mark these as developer-only flags! */ daemon->dev_fast_gossip = dev_fast_gossip; + daemon->dev_no_ping_timer = dev_no_ping_timer; #endif if (!pubkey_from_node_id(&daemon->mykey, &daemon->id)) diff --git a/connectd/connectd.h b/connectd/connectd.h index 825a21891b71..ce1fd44e2d14 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -191,6 +191,8 @@ struct daemon { #if DEVELOPER /* Hack to speed up gossip timer */ bool dev_fast_gossip; + /* Hack to avoid ping timeouts */ + bool dev_no_ping_timer; #endif }; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 5b0950f9376e..dbb660095a12 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -25,6 +25,7 @@ msgdata,connectd_init,websocket_port,u16, msgdata,connectd_init,dev_fast_gossip,bool, # If this is set, then fd 5 is dev_disconnect_fd. msgdata,connectd_init,dev_disconnect,bool, +msgdata,connectd_init,dev_no_ping_timer,bool, # Connectd->master, here are the addresses I bound, can announce. msgtype,connectd_init_reply,2100 diff --git a/connectd/multiplex.c b/connectd/multiplex.c index c39c35ea094c..959638e0837c 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -391,6 +391,10 @@ static void send_ping(struct peer *peer); static void set_ping_timer(struct peer *peer) { + if (IFDEV(peer->daemon->dev_no_ping_timer, false)) { + peer->ping_timer = NULL; + return; + } peer->ping_timer = new_reltimer(&peer->daemon->timers, peer, time_from_sec(15 + pseudorand(30)), send_ping, peer); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index df6993c811d7..1f8e80c74e73 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -558,7 +558,8 @@ int connectd_init(struct lightningd *ld) websocket_helper_path, ld->websocket_port, IFDEV(ld->dev_fast_gossip, false), - IFDEV(ld->dev_disconnect_fd >= 0, false)); + IFDEV(ld->dev_disconnect_fd >= 0, false), + IFDEV(ld->dev_no_ping_timer, false)); subd_req(ld->connectd, ld->connectd, take(msg), -1, 0, connect_init_done, NULL); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index f7ffa99eb97d..c55e861d2a2b 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -136,6 +136,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_ignore_modern_onion = false; ld->dev_ignore_obsolete_onion = false; ld->dev_disable_commit = -1; + ld->dev_no_ping_timer = false; #endif /*~ These are CCAN lists: an embedded double-linked list. It's not diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 973db2a7c863..a90f133099a4 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -267,6 +267,9 @@ struct lightningd { /* Tell channeld to disable commits after this many. */ int dev_disable_commit; + + /* Tell channeld not to worry about pings. */ + bool dev_no_ping_timer; #endif /* DEVELOPER */ /* tor support */ diff --git a/lightningd/options.c b/lightningd/options.c index e756854929ec..635feb40e30d 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -740,6 +740,9 @@ static void dev_register_opts(struct lightningd *ld) opt_set_intval, opt_show_intval, &ld->dev_disable_commit, "Disable commit timer after this many commits"); + opt_register_noarg("--dev-no-ping-timer", opt_set_bool, + &ld->dev_no_ping_timer, + "Don't hang up if we don't get a ping response"); } #endif /* DEVELOPER */ From 1e08c3b88299e034d6d59f4ce17829da983209e9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Mar 2022 14:12:39 +1030 Subject: [PATCH 0593/1530] pytest: simple "does onchaind on missing HTLC cause upstream failure" test. Signed-off-by: Rusty Russell --- tests/test_closing.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index 69db8c3f1cab..db8e22d70b09 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3631,3 +3631,40 @@ def test_close_weight_estimate(node_factory, bitcoind): signed_weight = int(bitcoind.rpc.decoderawtransaction(tx)['weight']) assert signed_weight <= actual_weight assert signed_weight >= actual_weight - 2 + + +@pytest.mark.developer("needs dev_disconnect") +def test_onchain_close_upstream(node_factory, bitcoind, executor): + """https://github.com/ElementsProject/lightning/issues/4649 + +We send an HTLC, and peer unilaterally closes: do we close upstream? + """ + l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, + opts=[{}, + {}, + {'disconnect': ['-WIRE_REVOKE_AND_ACK', 'permfail']}]) + + # Start payment + inv = l3.rpc.invoice(msatoshi=10000, label='x', description='desc')['bolt11'] + fut = executor.submit(l1.rpc.pay, inv) + + # l3 goes onchain, without htlc + l3.daemon.wait_for_log('dev_disconnect: -WIRE_REVOKE_AND_ACK') + l3.daemon.wait_for_log('sendrawtransaction') + + # Mine it + bitcoind.generate_block(1, wait_for_mempool=1) + + # l2 tells onchaind to look for missing HTLC. + l2.daemon.wait_for_logs(['Their unilateral tx', + r'We want to know if htlc 0 is missing \(later\)']) + + # After three blocks, onchaind says: definitely missing htlc + bitcoind.generate_block(3) + l2.daemon.wait_for_log('Sending 1 missing htlc messages') + + # l2 will tell l1 it has failed the htlc. + l1.daemon.wait_for_log('peer_in WIRE_UPDATE_FAIL_HTLC') + + with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE \(reply from remote\)'): + fut.result(TIMEOUT) From 5033e22835ed9e92e56cf932109438c2f9d16ec7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Mar 2022 14:13:08 +1030 Subject: [PATCH 0594/1530] pytest: make test_onchain_close_upstream more accurately reflect report. 1. Don't use dust HTLCs. 2. Make l3 unresponsive, like report. 3. Make l2-l3 fail because we time out on successive HTLC. We use sendpay rather than pay, because pay can do multiple attempts. Signed-off-by: Rusty Russell --- tests/test_closing.py | 50 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index db8e22d70b09..62e3fdda6c73 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3634,22 +3634,45 @@ def test_close_weight_estimate(node_factory, bitcoind): @pytest.mark.developer("needs dev_disconnect") -def test_onchain_close_upstream(node_factory, bitcoind, executor): +def test_onchain_close_upstream(node_factory, bitcoind): """https://github.com/ElementsProject/lightning/issues/4649 We send an HTLC, and peer unilaterally closes: do we close upstream? """ l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, - opts=[{}, - {}, - {'disconnect': ['-WIRE_REVOKE_AND_ACK', 'permfail']}]) + opts=[{'feerates': (7500, 7500, 7500, 7500)}, + # Second commitment_signed is to l3 + {'disconnect': ['xWIRE_COMMITMENT_SIGNED*2'], + # We want htlc killed by timeout, not a close due to ping timer. + 'dev-no-ping-timer': None}, + {'dev-no-ping-timer': None}]) - # Start payment - inv = l3.rpc.invoice(msatoshi=10000, label='x', description='desc')['bolt11'] - fut = executor.submit(l1.rpc.pay, inv) + ph1 = l3.rpc.invoice(msatoshi="10000sat", label='x1', description='desc2')['payment_hash'] + ph2 = l3.rpc.invoice(msatoshi="10000sat", label='x2', description='desc2')['payment_hash'] + + route = l1.rpc.getroute(l3.info['id'], 1, 1)['route'] + + # Start a payment + l1.rpc.sendpay(route, ph1) + + # l3 sends commitment_signed, then silence. + l2.daemon.wait_for_log('dev_disconnect: xWIRE_COMMITMENT_SIGNED') + + # Send another payment, this times out. + l1.rpc.sendpay(route, ph2) + + # This can take 30 seconds... + l2.daemon.wait_for_log('Adding HTLC 1 too slow: killing connection', + timeout=TIMEOUT + 30) + l2.daemon.wait_for_log('Failing HTLC 1 due to peer death') + + with pytest.raises(RpcError, match=r'WIRE_TEMPORARY_CHANNEL_FAILURE \(reply from remote\)'): + l1.rpc.waitsendpay(ph2, timeout=TIMEOUT) + + # l3 closes unilaterally. + wait_for(lambda: only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + l3.rpc.close(l2.info['id'], 1) - # l3 goes onchain, without htlc - l3.daemon.wait_for_log('dev_disconnect: -WIRE_REVOKE_AND_ACK') l3.daemon.wait_for_log('sendrawtransaction') # Mine it @@ -3659,12 +3682,19 @@ def test_onchain_close_upstream(node_factory, bitcoind, executor): l2.daemon.wait_for_logs(['Their unilateral tx', r'We want to know if htlc 0 is missing \(later\)']) +# # l1 disconnects now +# l1.rpc.disconnect(l2.info['id'], force=True) +# # Restart now, and reconnect +# l2.restart() +# l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # After three blocks, onchaind says: definitely missing htlc bitcoind.generate_block(3) l2.daemon.wait_for_log('Sending 1 missing htlc messages') # l2 will tell l1 it has failed the htlc. +# l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_log('peer_in WIRE_UPDATE_FAIL_HTLC') with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE \(reply from remote\)'): - fut.result(TIMEOUT) + l1.rpc.waitsendpay(ph1, timeout=TIMEOUT) From e616b4fff73372694f621afba6a99d5c7d93da86 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Mar 2022 14:13:12 +1030 Subject: [PATCH 0595/1530] lightningd: add extra debugging for weird onchain htlc interactions. This doesn't actually fix anything, but may shed more clues if it happens again. The broken() logs are overzealous, see next patch. Signed-off-by: Rusty Russell --- lightningd/peer_htlcs.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 7a434bbd3a3b..b17eadf0ec90 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1418,13 +1418,23 @@ void onchain_failed_our_htlc(const struct channel *channel, struct lightningd *ld = channel->peer->ld; struct htlc_out *hout; + log_debug(channel->log, "onchain_failed_our_htlc"); hout = find_htlc_out(&ld->htlcs_out, channel, htlc->id); - if (!hout) + if (!hout) { + log_broken(channel->log, "HTLC id %"PRIu64" not found!", + htlc->id); return; + } /* Don't fail twice (or if already succeeded)! */ - if (hout->failonion || hout->failmsg || hout->preimage) + if (hout->failonion || hout->failmsg || hout->preimage) { + log_debug(channel->log, "HTLC id %"PRIu64" failonion = %p, failmsg = %p, preimage = %p", + htlc->id, + hout->failonion, + hout->failmsg, + hout->preimage); return; + } hout->failmsg = towire_permanent_channel_failure(hout); @@ -1441,6 +1451,8 @@ void onchain_failed_our_htlc(const struct channel *channel, hout->failmsg, &we_filled); if (hout->am_origin) { + log_debug(channel->log, "HTLC id %"PRIu64" am origin", + htlc->id); assert(why != NULL); char *localfail = tal_fmt(channel, "%s: %s", onion_wire_name(WIRE_PERMANENT_CHANNEL_FAILURE), @@ -1448,6 +1460,8 @@ void onchain_failed_our_htlc(const struct channel *channel, payment_failed(ld, hout, localfail); tal_free(localfail); } else if (hout->in) { + log_debug(channel->log, "HTLC id %"PRIu64" has incoming", + htlc->id); local_fail_in_htlc(hout->in, take(towire_permanent_channel_failure(NULL))); wallet_forwarded_payment_add(hout->key.channel->peer->ld->wallet, @@ -1456,7 +1470,9 @@ void onchain_failed_our_htlc(const struct channel *channel, hout->failmsg ? fromwire_peektype(hout->failmsg) : 0); - } + } else + log_broken(channel->log, "HTLC id %"PRIu64" is from nowhere?", + htlc->id); } static void remove_htlc_in(struct channel *channel, struct htlc_in *hin) From 0b7f78929176a2483d8cba7c6e48fa10f5db3ee6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Mar 2022 14:13:12 +1030 Subject: [PATCH 0596/1530] lightningd: extra sanity checks and rescue attempts for missing HTLCs. These trip when anything weird happens; turns out that we tell onchaind about old htlcs (e.g. for penalties), so in that case we can actually have it tell us about missing HTLCs which we no longer have in memory. Signed-off-by: Rusty Russell --- lightningd/onchain_control.c | 20 ++++++- lightningd/peer_htlcs.c | 101 +++++++++++++++++++++++++++++++++-- lightningd/peer_htlcs.h | 3 +- 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index d859b90066f6..1c22cc1b9627 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -390,6 +390,14 @@ static void handle_missing_htlc_output(struct channel *channel, const u8 *msg) return; } + /* We only set tell_if_missing on LOCAL htlcs */ + if (htlc.owner != LOCAL) { + channel_internal_error(channel, + "onchaind_missing_htlc_output: htlc %"PRIu64" is not local!", + htlc.id); + return; + } + /* BOLT #5: * * - for any committed HTLC that does NOT have an output in this @@ -400,7 +408,7 @@ static void handle_missing_htlc_output(struct channel *channel, const u8 *msg) * corresponding to the HTLC. * - MAY fail the corresponding incoming HTLC sooner. */ - onchain_failed_our_htlc(channel, &htlc, "missing in commitment tx"); + onchain_failed_our_htlc(channel, &htlc, "missing in commitment tx", false); } static void handle_onchain_htlc_timeout(struct channel *channel, const u8 *msg) @@ -412,6 +420,14 @@ static void handle_onchain_htlc_timeout(struct channel *channel, const u8 *msg) return; } + /* It should tell us about timeouts on our LOCAL htlcs */ + if (htlc.owner != LOCAL) { + channel_internal_error(channel, + "onchaind_htlc_timeout: htlc %"PRIu64" is not local!", + htlc.id); + return; + } + /* BOLT #5: * * - if the commitment transaction HTLC output has *timed out* and @@ -419,7 +435,7 @@ static void handle_onchain_htlc_timeout(struct channel *channel, const u8 *msg) * - MUST *resolve* the output by spending it using the HTLC-timeout * transaction. */ - onchain_failed_our_htlc(channel, &htlc, "timed out"); + onchain_failed_our_htlc(channel, &htlc, "timed out", true); } static void handle_irrevocably_resolved(struct channel *channel, const u8 *msg UNUSED) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index b17eadf0ec90..ea859ebebf6d 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1411,9 +1411,89 @@ static bool peer_failed_our_htlc(struct channel *channel, return true; } +/* We've had a bug report about not failing incoming HTLCs. This does a sanity + * check, if we think we've already failed this HTLC */ +static void check_already_failed(const struct channel *channel, struct htlc_out *hout) +{ + htlc_out_check(hout, __func__); + + if (!hout->in) + return; + + /* in should have been failed/succeeded already */ + if (hout->in->badonion != 0 + || hout->in->failonion + || hout->in->preimage) + return; + + /* Print out what we think this htlc already has (failed/succeeded) */ + log_broken(channel->log, "HTLC id %"PRIu64" already complete, but ->in not resolved!" + " failonion = %s, failmsg = %s, preimage = %s", + hout->key.id, + hout->failonion ? tal_hex(tmpctx, hout->failonion->contents) : "(null)", + hout->failmsg ? tal_hex(tmpctx, hout->failmsg) : "(null)", + hout->preimage ? type_to_string(tmpctx, struct preimage, hout->preimage) : "(null)"); + + if (hout->preimage) { + /* Log on both ours and theirs! */ + log_broken(channel->log, + "MISSING incoming success for %"PRIu64": succeeding incoming now", + hout->key.id); + log_broken(hout->in->key.channel->log, + "MISSED incoming success for %"PRIu64": succeeding now", + hout->in->key.id); + fulfill_htlc(hout->in, hout->preimage); + } else { + log_broken(channel->log, + "MISSING incoming fail for %"PRIu64": failing incoming now", + hout->key.id); + log_broken(hout->in->key.channel->log, + "MISSED incoming fail for %"PRIu64": failing now", + hout->in->key.id); + local_fail_in_htlc(hout->in, + take(towire_permanent_channel_failure(NULL))); + } +} + +/* This case searches harder to see if there are any incoming HTLCs */ +static void fail_dangling_htlc_in(struct lightningd *ld, + const struct sha256 *payment_hash) +{ + struct htlc_in *hin; + struct htlc_in_map_iter ini; + + for (hin = htlc_in_map_first(&ld->htlcs_in, &ini); + hin; + hin = htlc_in_map_next(&ld->htlcs_in, &ini)) { + if (!sha256_eq(&hin->payment_hash, payment_hash)) + continue; + if (hin->badonion) { + log_broken(hin->key.channel->log, + "htlc %"PRIu64" already failed with badonion", + hin->key.id); + } else if (hin->preimage) { + log_broken(hin->key.channel->log, + "htlc %"PRIu64" already succeeded with preimage", + hin->key.id); + } else if (hin->failonion) { + log_broken(hin->key.channel->log, + "htlc %"PRIu64" already failed with failonion %s", + hin->key.id, + tal_hex(tmpctx, hin->failonion->contents)); + } else { + log_broken(hin->key.channel->log, + "htlc %"PRIu64" has matching hash: failing", + hin->key.id); + local_fail_in_htlc(hin, + take(towire_permanent_channel_failure(NULL))); + } + } +} + void onchain_failed_our_htlc(const struct channel *channel, const struct htlc_stub *htlc, - const char *why) + const char *why, + bool should_exist) { struct lightningd *ld = channel->peer->ld; struct htlc_out *hout; @@ -1421,8 +1501,14 @@ void onchain_failed_our_htlc(const struct channel *channel, log_debug(channel->log, "onchain_failed_our_htlc"); hout = find_htlc_out(&ld->htlcs_out, channel, htlc->id); if (!hout) { - log_broken(channel->log, "HTLC id %"PRIu64" not found!", - htlc->id); + /* For penalty transactions, tell onchaind about all possible + * HTLCs: they may not all exist any more. */ + if (should_exist) + log_broken(channel->log, "HTLC id %"PRIu64" not found!", + htlc->id); + /* Immediate corruption sanity check if this happens */ + htable_check(&ld->htlcs_out.raw, "onchain_failed_our_htlc out"); + htable_check(&ld->htlcs_in.raw, "onchain_failed_our_htlc in"); return; } @@ -1433,6 +1519,7 @@ void onchain_failed_our_htlc(const struct channel *channel, hout->failonion, hout->failmsg, hout->preimage); + check_already_failed(channel, hout); return; } @@ -1470,9 +1557,15 @@ void onchain_failed_our_htlc(const struct channel *channel, hout->failmsg ? fromwire_peektype(hout->failmsg) : 0); - } else + } else { log_broken(channel->log, "HTLC id %"PRIu64" is from nowhere?", htlc->id); + + /* Immediate corruption sanity check if this happens */ + htable_check(&ld->htlcs_out.raw, "onchain_failed_our_htlc out"); + htable_check(&ld->htlcs_in.raw, "onchain_failed_our_htlc in"); + fail_dangling_htlc_in(ld, &hout->payment_hash); + } } static void remove_htlc_in(struct channel *channel, struct htlc_in *hin) diff --git a/lightningd/peer_htlcs.h b/lightningd/peer_htlcs.h index 3cdf6476ecdf..1bcef7bb3502 100644 --- a/lightningd/peer_htlcs.h +++ b/lightningd/peer_htlcs.h @@ -54,7 +54,8 @@ const u8 *send_htlc_out(const tal_t *ctx, void onchain_failed_our_htlc(const struct channel *channel, const struct htlc_stub *htlc, - const char *why); + const char *why, + bool should_exist); void onchain_fulfilled_htlc(struct channel *channel, const struct preimage *preimage); From 861922fb1c15e4145ef6dd0eea0c5a02db7bd572 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 30 Mar 2022 14:13:12 +1030 Subject: [PATCH 0597/1530] channeld: log more information about restoring HTLCs. Signed-off-by: Rusty Russell --- channeld/full_channel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 70ddc15df87a..ee7140e2b75d 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -1498,14 +1498,16 @@ bool channel_force_htlcs(struct channel *channel, status_debug("Restoring HTLC %zu/%zu:" " id=%"PRIu64" amount=%s cltv=%u" - " payment_hash=%s", + " payment_hash=%s %s", i, tal_count(htlcs), htlcs[i]->id, type_to_string(tmpctx, struct amount_msat, &htlcs[i]->amount), htlcs[i]->cltv_expiry, type_to_string(tmpctx, struct sha256, - &htlcs[i]->payment_hash)); + &htlcs[i]->payment_hash), + htlcs[i]->payment_preimage ? "(have preimage)" + : htlcs[i]->failed ? "(failed)" : ""); e = add_htlc(channel, htlcs[i]->state, htlcs[i]->id, htlcs[i]->amount, From cd9ce92d2814fd9ddc8a3a5e62f457dde754e116 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 09:41:00 +1030 Subject: [PATCH 0598/1530] onchaind: fix htlc_stub organization. We get sent three corresponding arrays: 1. htlc stubs 2. whether we want to know if they're missing, 3. whether to wait 3 blocks or tell us immediately We then sorted the htlc stubs by CLTV, *but didn't sort the corresponding arrays*. This fixes that the simplest way possible, and probably also: Fixes: #4649 Signed-off-by: Rusty Russell Changelog-Fixed: onchaind: we sometimes failed to close upstream htlcs if more than one HTLC is in flight during unilateral close. --- onchaind/onchaind.c | 52 ++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index df8902cbc106..c67805d94c6d 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -2126,26 +2126,32 @@ static void wait_for_resolved(struct tracked_output **outs) take(towire_onchaind_all_irrevocably_resolved(outs))); } -static int cmp_htlc_cltv(const struct htlc_stub *a, - const struct htlc_stub *b, void *unused) -{ - if (a->cltv_expiry < b->cltv_expiry) - return -1; - else if (a->cltv_expiry > b->cltv_expiry) - return 1; - return 0; -} - struct htlcs_info { struct htlc_stub *htlcs; bool *tell_if_missing; bool *tell_immediately; }; +struct htlc_with_tells { + struct htlc_stub htlc; + bool tell_if_missing, tell_immediately; +}; + +static int cmp_htlc_with_tells_cltv(const struct htlc_with_tells *a, + const struct htlc_with_tells *b, void *unused) +{ + if (a->htlc.cltv_expiry < b->htlc.cltv_expiry) + return -1; + else if (a->htlc.cltv_expiry > b->htlc.cltv_expiry) + return 1; + return 0; +} + static struct htlcs_info *init_reply(const tal_t *ctx, const char *what) { struct htlcs_info *htlcs_info = tal(ctx, struct htlcs_info); u8 *msg; + struct htlc_with_tells *htlcs; /* commit_num is 0 for mutual close, but we don't care about HTLCs * then anyway. */ @@ -2159,7 +2165,7 @@ static struct htlcs_info *init_reply(const tal_t *ctx, const char *what) /* Read in htlcs */ for (;;) { msg = wire_sync_read(queued_msgs, REQ_FD); - if (fromwire_onchaind_htlcs(htlcs_info, msg, + if (fromwire_onchaind_htlcs(tmpctx, msg, &htlcs_info->htlcs, &htlcs_info->tell_if_missing, &htlcs_info->tell_immediately)) { @@ -2171,14 +2177,26 @@ static struct htlcs_info *init_reply(const tal_t *ctx, const char *what) tal_arr_expand(&queued_msgs, msg); } - /* We want htlcs to be a valid tal parent, so make it a zero-length - * array if NULL (fromwire makes it NULL if there are no entries) */ - if (!htlcs_info->htlcs) - htlcs_info->htlcs = tal_arr(htlcs_info, struct htlc_stub, 0); + /* One convenient structure, so we sort them together! */ + htlcs = tal_arr(tmpctx, struct htlc_with_tells, tal_count(htlcs_info->htlcs)); + for (size_t i = 0; i < tal_count(htlcs); i++) { + htlcs[i].htlc = htlcs_info->htlcs[i]; + htlcs[i].tell_if_missing = htlcs_info->tell_if_missing[i]; + htlcs[i].tell_immediately = htlcs_info->tell_immediately[i]; + } /* Sort by CLTV, so matches are in CLTV order (and easy to skip dups) */ - asort(htlcs_info->htlcs, tal_count(htlcs_info->htlcs), - cmp_htlc_cltv, NULL); + asort(htlcs, tal_count(htlcs), cmp_htlc_with_tells_cltv, NULL); + + /* Now put them back (prev were allocated off tmpctx) */ + htlcs_info->htlcs = tal_arr(htlcs_info, struct htlc_stub, tal_count(htlcs)); + htlcs_info->tell_if_missing = tal_arr(htlcs_info, bool, tal_count(htlcs)); + htlcs_info->tell_immediately = tal_arr(htlcs_info, bool, tal_count(htlcs)); + for (size_t i = 0; i < tal_count(htlcs); i++) { + htlcs_info->htlcs[i] = htlcs[i].htlc; + htlcs_info->tell_if_missing[i] = htlcs[i].tell_if_missing; + htlcs_info->tell_immediately[i] = htlcs[i].tell_immediately; + } return htlcs_info; } From 910d594214ade45d7d8f0659cf04ded5e11c180d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 13:41:27 +1030 Subject: [PATCH 0599/1530] doc: add partid to sendonion schema. We never called this except from plugins, which didn't test schema. Signed-off-by: Rusty Russell --- doc/lightning-sendonion.7.md | 3 ++- doc/schemas/sendonion.schema.json | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index 927e779a668b..c059afc95ea9 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -98,6 +98,7 @@ On success, an object is returned, containing: - **label** (string, optional): the label, if given to sendpay - **bolt11** (string, optional): the bolt11 string (if supplied) - **bolt12** (string, optional): the bolt12 string (if supplied: **experimental-offers** only). +- **partid** (u64, optional): the partid (if supplied) to sendonion/sendpay If **status** is "complete": - **payment_preimage** (hex): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) @@ -127,4 +128,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:d588d85b79f709a57441479504ee8761331c852284ebb8effeab91a557437517) +[comment]: # ( SHA256STAMP:b8fd5d1c31eb2db10b2f32960752ae1c90150ffeb470c5ea635cb1034e8b1438) diff --git a/doc/schemas/sendonion.schema.json b/doc/schemas/sendonion.schema.json index 56474b44ed26..7368f849a49c 100644 --- a/doc/schemas/sendonion.schema.json +++ b/doc/schemas/sendonion.schema.json @@ -61,6 +61,10 @@ "bolt12": { "type": "string", "description": "the bolt12 string (if supplied: **experimental-offers** only)." + }, + "partid": { + "type": "u64", + "description": "the partid (if supplied) to sendonion/sendpay" } }, "allOf": [ @@ -94,6 +98,7 @@ "label": {}, "bolt11": {}, "bolt12": {}, + "partid": {}, "payment_preimage": { "type": "hex", "description": "the proof of payment: SHA256 of this **payment_hash**", @@ -131,6 +136,7 @@ "label": {}, "bolt11": {}, "bolt12": {}, + "partid": {}, "message": { "type": "string", "description": "Monitor status with listpays or waitsendpay" From 141d4ef6759ed082e44537299935f22776dafb71 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 13:42:27 +1030 Subject: [PATCH 0600/1530] pytest: test legacy onion acceptance. Using a canned sendonion call from an old version (v0.10.2), where I forced it to use legacy onion for l2. Signed-off-by: Rusty Russell --- tests/test_pay.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index 1a4c23f50419..0ef99152ac10 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5169,6 +5169,48 @@ def test_sendpay_grouping(node_factory, bitcoind): assert([p['status'] for p in pays] == ['failed', 'failed', 'complete']) +@pytest.mark.xfail("needs lecacy onion support") +def test_legacyonion(node_factory, bitcoind): + # We have to replicate the topology we created onion with, exactly. + l1, l2, l3 = node_factory.line_graph(3, fundchannel=False) + node_factory.join_nodes([l1, l2], wait_for_announce=True) + + # We need this scid to match canned onion (110x1x0), so no change. + addr = l2.rpc.newaddr()['bech32'] + bitcoind.rpc.sendtoaddress(addr, 10**6 / 10**8) + + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l2]) + l2.rpc.fundchannel(l3.info['id'], "all")['txid'] + bitcoind.generate_block(6, wait_for_mempool=1) + + # We don't actually need gossip! But make sure blockheight is correct. + sync_blockheight(bitcoind, [l1, l2, l3]) + + l3.rpc.invoice(10000, 'test_invoice', 'description', preimage='00' * 32)['bolt11'] + + # Here's one I created using an old c-lightning: l2 gets legacy. + l1.rpc.call('sendonion', + {"onion": "000279f8e85e661936e01c91d49ed42e776a80741047403b9292e2ef66d977bce0adaf4e773d4750cc8e7359a53107413ec2c9956fb7c7ef2ffba6b588880557efb9cdb6cbc2231c517c1ed83c8aa136e2294b82cd2df382b794128792d86f8ac155966ad0bc7610df86bf5c2dfaf685be237a5056db0cabbf09ac6ca9686a8152b3a2157c1b3bb08b29321ef0d8970ef761d29b1e3305ee3b96195911c0486dbfdd48fa747b802ff7aafbcb396f4bdf3bbc2c72aeea9cd621767d883d6206aec82cd744c571157cf367680f5a91106bfd49e1febf7190cba2868b50cdebefcaaace9ec00083d5b32fc2b4f8db0022c59d361296ebf5c47a09dd633da15cf21bc24b6efbcf7202127fda27b704d6c255e4d240bb56dbd7351d0b6299682f4b4f98dee4aa185b757948a7c80053bec14fc91d813e913c79253de25a1b1fa031eccf0abe52db4f4562a797bc5066202284b070ca35f92cae9d0ea22f5a88aaae9e63b775e10f3f54fa0069a03e554e72bb0c3ead07eb3bf0895531b580123c308fd2450a3698328e71756dee32ae90c4872c296183bbfb08bb2bedbbab9a46ebab05674d53d06dcec6879b994fbd8f828fbccd6766c76255553bb4605f7fe3767f1bb6e8e4341a120574d30de715b27fbcdfec4f590877653bec0ad410d243ebd09e1b1e9929bba99dd8c7bff26201970c8ffeb1e2773eb18ec29bc9b13c8f3dcfc77935b5afd788e1e4ca4a31102d645db575211799bf54c42f2152f227796fdafc7ad6708dd18553d7a9c8bd582655230a3920c25286705353b513de4e2ef3dd84335b9236560f503a9198dc2d730a950c01cd6cf0aee5bc5b2c060de23cd8f2dda92dcfaee0c4e29d5efe12ca1139d0c86b6d22ea58bb3076b953577d86969702176010690e9eb3afda4db3c8e2304494c5f43cdf1487ab6daa740d4645c6d9ed796f0b210e18f751249fa87137bbfb8b332e97ad7e6d73130838b94680a0c999a0ec0c90c3807af60c6db7a3ed2a7ddfbe7c7b293870d9bc003512a3b64607661fcc94df7e9f2bfc875b447b8cedc38d8a55566246761e02932e9308e1f26cf9724f5432fe391b88bd4af4f7b9f7599529035791b475b10eb4cf1efb56bfab75fc75a1cb73308120dbd52e635dd0207df882488ee005eedae5cc3245e6ccb49770ace2868f205f76640b38e55b23e23ca94559c200f94b06d498332675a0ee0fe57551d1dd2343934f20daa60b025992350c7d8de192139d786b5db3ec21c7a772571c61a66b3eff2a424464291e183bd382b0560efab3fc3ccf88cb4b9a3695f35590937b611d98856db8bf46d6716672a1f0efa8cd3e4b49bf1dc0cea35dce1465dc36fd94db0592cfee01a90d96766c8b08a6e79d2b3b3f8a133f3a01aad6d8adbf8aee4dba09232424faae516f0498705c1514f570d3e3e6c9f0a9457a6230847bbdce1d6a427538a82e9025ee7f90901bb73c6819fc02cf8d14c0246a44af8efa2a49ba11cf61a09204dd4a1d925b32ac327d525ff189ffa833d0e1c5e6999a9268a2526c4e9a6fdb95ab6b134e30946923af361a5ef543e77621675d25a60a0e0d691088e68112f18be4fa66d10cf68c65f8c4034fab58aaff2d02076078b7d392a3a72268da5124884fabd0880072d080fa6235d2155c4160c7d1d4a5054a1e27be381d1048d47a4e7abb936f170ff1b210f09214ecb645b1c2d5d77f286afcc4f6716b788e223e82501ee544836605e32074e465923858cde71163ad700947c94381611eda017a6632c782a377a2c506d0392a6e1a7f6e5988f7893776bdadf28e4648ef48672190d9de0e94523c208dbdaee274c6d0ce9a27a40afd1cc3e86936c2e2e96565a052a7581a807a4fbc8abdaac9e8f32dc04789b47fd8d5e874112e2308dedff5a76b6e092cb42c1bd9a082", + "first_hop": { + "amount_msat": "10001msat", + "delay": 29, + "id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59" + }, + "payment_hash": "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925", + "msatoshi": "10000msat", + "shared_secrets": [ + "bd29a24ea1fa5cab1677b22f4980f6aa115d470c2e3052cb08ca7d636441bfd5", + "7d70d337a6b898373386d7d2cff05ca8f4d81123f63cf911aa064a6d8849f972" + ], + "partid": 1, + "groupid": 1, + "bolt11": "lnbcrt100n1p3y8xl3pp5ve584t0cv27hwmy0cx9ca8uwyqyfw9y9dm3r8vus9fv36r2l9yjsdqjv3jhxcmjd9c8g6t0dcxqyjw5qcqp9sp5q8g040f9rl9mu2unkjuj0vn262s6nyrhz5hythk3ueu2lfzahmzsrzjqgkjyd3q5dv6gllh77kygly9c3kfy0d9xwyjyxsq2nq3c83u5vw4jqqqdcqqqqgqqqqqqqqpqqqqqzsqqc9qgsqqqyssq47lyzhlmfw6hdcrklz3nw774p6nueggm6sxvrg734yrzdl0rn4p8rfl4ql2hexcufafgpstjkag2ywfycg08kuz5k5qszz7ecypqeugpnapu7t", + "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d"}) + + wait_for(lambda: only_one(l3.rpc.listinvoices()['invoices'])['status'] == 'paid') + + def test_pay_manual_exclude(node_factory, bitcoind): l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) l1_id = l1.rpc.getinfo()['id'] From 116a77f1bee0a8b5fb1418e63efb0248bcc20a29 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 13:43:27 +1030 Subject: [PATCH 0601/1530] lightningd: Restore forwarding of legacy onions. Partial revert of 43a833e4052d6439902b8368e309d61f6c421944 "lightningd: remove support for legacy onion format."; we restore the ability to decode legacy onions for forwarding, but not to generate them. (We don't accept them properly since making payment_secret compulsory anyway, so no real change there!) Signed-off-by: Rusty Russell Changelog-Removed: Protocol: ... but we still forward legacy HTLC onions for now. --- common/onion.c | 44 ++++++++++++++++++++-- common/onion.h | 11 +++++- common/sphinx.c | 2 +- common/test/run-gossmap_local.c | 4 -- common/test/run-route-specific.c | 4 -- common/test/run-route.c | 4 -- common/test/run-sphinx-xor_cipher_stream.c | 3 +- devtools/onion.c | 2 +- lightningd/peer_htlcs.c | 10 ++++- tests/test_pay.py | 1 - 10 files changed, 63 insertions(+), 22 deletions(-) diff --git a/common/onion.c b/common/onion.c index 18ba5e8066ec..5fdcf757905a 100644 --- a/common/onion.c +++ b/common/onion.c @@ -109,10 +109,11 @@ u8 *onion_final_hop(const tal_t *ctx, return make_tlv_hop(ctx, tlv); } -/* Returns true if valid, and fills in len. */ +/* Returns true if valid, and fills in type. */ static bool pull_payload_length(const u8 **cursor, size_t *max, bool has_realm, + enum onion_payload_type *type, size_t *len) { /* *len will incorporate bytes we read from cursor */ @@ -127,6 +128,19 @@ static bool pull_payload_length(const u8 **cursor, if (!cursor) return false; + /* BOLT #4: + * - Legacy `hop_data` format, identified by a single `0x00` byte for + * length. In this case the `hop_payload_length` is defined to be 32 + * bytes. + */ + if (has_realm && *len == 0) { + if (type) + *type = ONION_V0_PAYLOAD; + assert(*cursor - start == 1); + *len = 1 + 32; + return true; + } + /* BOLT #4: * - `tlv_payload` format, identified by any length over `1`. In this * case the `hop_payload_length` is equal to the numeric value of @@ -142,6 +156,8 @@ static bool pull_payload_length(const u8 **cursor, return false; } + if (type) + *type = ONION_TLV_PAYLOAD; *len += (*cursor - start); return true; } @@ -150,10 +166,11 @@ static bool pull_payload_length(const u8 **cursor, } size_t onion_payload_length(const u8 *raw_payload, size_t len, bool has_realm, - bool *valid) + bool *valid, + enum onion_payload_type *type) { size_t max = len, payload_len; - *valid = pull_payload_length(&raw_payload, &max, has_realm, &payload_len); + *valid = pull_payload_length(&raw_payload, &max, has_realm, type, &payload_len); /* If it's not valid, copy the entire thing. */ if (!*valid) @@ -210,12 +227,31 @@ struct onion_payload *onion_decode(const tal_t *ctx, size_t max = tal_bytelen(cursor), len; struct tlv_tlv_payload *tlv; - if (!pull_payload_length(&cursor, &max, true, &len)) { + if (!pull_payload_length(&cursor, &max, true, &p->type, &len)) { *failtlvtype = 0; *failtlvpos = tal_bytelen(rs->raw_payload); goto fail_no_tlv; } + /* Very limited legacy handling: forward only. */ + if (p->type == ONION_V0_PAYLOAD && rs->nextcase == ONION_FORWARD) { + p->forward_channel = tal(p, struct short_channel_id); + fromwire_short_channel_id(&cursor, &max, p->forward_channel); + p->total_msat = NULL; + p->amt_to_forward = fromwire_amount_msat(&cursor, &max); + p->outgoing_cltv = fromwire_u32(&cursor, &max); + p->payment_secret = NULL; + p->blinding = NULL; + /* We can't handle blinding with a legacy payload */ + if (blinding) + return tal_free(p); + /* If they somehow got an invalid onion this far, fail. */ + if (!cursor) + return tal_free(p); + p->tlv = NULL; + return p; + } + /* We do this manually so we can accept extra types, and get * error off and type. */ tlv = tlv_tlv_payload_new(p); diff --git a/common/onion.h b/common/onion.h index 46ccacb27dd5..6f3045faf57c 100644 --- a/common/onion.h +++ b/common/onion.h @@ -6,7 +6,14 @@ struct route_step; +enum onion_payload_type { + ONION_V0_PAYLOAD = 0, + ONION_TLV_PAYLOAD = 1, +}; + struct onion_payload { + enum onion_payload_type type; + struct amount_msat amt_to_forward; u32 outgoing_cltv; struct amount_msat *total_msat; @@ -43,6 +50,7 @@ u8 *onion_final_hop(const tal_t *ctx, * @len: length of @raw_payload in bytes. * @has_realm: used for HTLCs, where first byte 0 is magical. * @valid: set to true if it is valid, false otherwise. + * @type: if non-NULL, set to type of payload if *@valid is true. * * If @valid is set, there is room for the HMAC immediately following, * as the return value is <= ROUTING_INFO_SIZE - HMAC_SIZE. Otherwise, @@ -50,7 +58,8 @@ u8 *onion_final_hop(const tal_t *ctx, */ size_t onion_payload_length(const u8 *raw_payload, size_t len, bool has_realm, - bool *valid); + bool *valid, + enum onion_payload_type *type); /** * onion_decode: decode payload from a decrypted onion. diff --git a/common/sphinx.c b/common/sphinx.c index 9c0064db48c6..1eb4496ea77e 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -627,7 +627,7 @@ struct route_step *process_onionpacket( payload_size = onion_payload_length(paddedheader, tal_bytelen(msg->routinginfo), has_realm, - &valid); + &valid, NULL); /* Can't decode? Treat it as terminal. */ if (!valid) { diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 85e522448e5b..6f1b4b724cd8 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -25,10 +25,6 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for type_to_string_ */ -const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, - union printable_types u UNNEEDED) -{ fprintf(stderr, "type_to_string_ called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* Canned gossmap, taken from tests/test_gossip.py's diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index c3b375aeae8a..e5aa986cb04b 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -51,10 +51,6 @@ void towire_tlv(u8 **pptr UNNEEDED, /* Generated stub for towire_wireaddr */ void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) { fprintf(stderr, "towire_wireaddr called!\n"); abort(); } -/* Generated stub for type_to_string_ */ -const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, - union printable_types u UNNEEDED) -{ fprintf(stderr, "type_to_string_ called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static void write_to_store(int store_fd, const u8 *msg) diff --git a/common/test/run-route.c b/common/test/run-route.c index 5c575c6fd14d..74aaa1eee013 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -44,10 +44,6 @@ void towire_tlv(u8 **pptr UNNEEDED, /* Generated stub for towire_wireaddr */ void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) { fprintf(stderr, "towire_wireaddr called!\n"); abort(); } -/* Generated stub for type_to_string_ */ -const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, - union printable_types u UNNEEDED) -{ fprintf(stderr, "type_to_string_ called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static void write_to_store(int store_fd, const u8 *msg) diff --git a/common/test/run-sphinx-xor_cipher_stream.c b/common/test/run-sphinx-xor_cipher_stream.c index 26781985b255..dc1bd585c504 100644 --- a/common/test/run-sphinx-xor_cipher_stream.c +++ b/common/test/run-sphinx-xor_cipher_stream.c @@ -91,7 +91,8 @@ struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents /* Generated stub for onion_payload_length */ size_t onion_payload_length(const u8 *raw_payload UNNEEDED, size_t len UNNEEDED, bool has_realm UNNEEDED, - bool *valid UNNEEDED) + bool *valid UNNEEDED, + enum onion_payload_type *type UNNEEDED) { fprintf(stderr, "onion_payload_length called!\n"); abort(); } /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) diff --git a/devtools/onion.c b/devtools/onion.c index 7b81a4f9ac57..f750bedd4454 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -280,7 +280,7 @@ static void runtest(const char *filename) errx(1, "Error serializing message."); onion_payload_length(step->raw_payload, tal_bytelen(step->raw_payload), - true, &valid); + true, &valid, NULL); assert(valid); printf(" Payload: %s\n", tal_hex(ctx, step->raw_payload)); printf(" Next onion: %s\n", tal_hex(ctx, serialized)); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index ea859ebebf6d..278b80527141 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -993,7 +993,15 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_add_hex_talarr(s, "payload", rs->raw_payload); if (p->payload) { - json_add_string(s, "type", "tlv"); + switch (p->payload->type) { + case ONION_V0_PAYLOAD: + json_add_string(s, "type", "legacy"); + break; + + case ONION_TLV_PAYLOAD: + json_add_string(s, "type", "tlv"); + break; + } if (p->payload->forward_channel) json_add_short_channel_id(s, "short_channel_id", diff --git a/tests/test_pay.py b/tests/test_pay.py index 0ef99152ac10..29235c571a23 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5169,7 +5169,6 @@ def test_sendpay_grouping(node_factory, bitcoind): assert([p['status'] for p in pays] == ['failed', 'failed', 'complete']) -@pytest.mark.xfail("needs lecacy onion support") def test_legacyonion(node_factory, bitcoind): # We have to replicate the topology we created onion with, exactly. l1, l2, l3 = node_factory.line_graph(3, fundchannel=False) From 2f7f7ec255d03ead2365fda4de6747a69a4adb7a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 13:44:27 +1030 Subject: [PATCH 0602/1530] lightningd: add "style" to listforwards. Suggested-by: @t-bast Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `listforwards` has new entry `style`, currently "legacy" or "tlv". --- doc/lightning-listforwards.7.md | 3 +- doc/schemas/listforwards.schema.json | 14 ++++++++++ lightningd/htlc_end.c | 1 + lightningd/notification.c | 12 +++++--- lightningd/notification.h | 3 +- lightningd/peer_htlcs.c | 37 ++++++++++++++++++++---- tests/test_pay.py | 1 + wallet/db.c | 1 + wallet/test/run-wallet.c | 3 +- wallet/wallet.c | 32 +++++++++++++++++---- wallet/wallet.h | 42 ++++++++++++++++++++++++++++ 11 files changed, 132 insertions(+), 17 deletions(-) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 590150a56e2c..40531ff78ea3 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -29,6 +29,7 @@ On success, an object containing **forwards** is returned. It is an array of ob - **received_time** (number): the UNIX timestamp when this was received - **out_channel** (short_channel_id, optional): the channel that the HTLC was forwarded to - **payment_hash** (hex, optional): payment hash sought by HTLC (always 64 characters) +- **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") If **out_channel** is present: - **fee_msat** (msat): the amount this paid in fees @@ -58,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:abfaaa00817734d8acb77d02d7c024112c90605a8f93a134971a617ab4d383f9) +[comment]: # ( SHA256STAMP:131410f052b8a1845c8d3c7eb2d48df0fc7638e7d26817f56863815be86d8f1e) diff --git a/doc/schemas/listforwards.schema.json b/doc/schemas/listforwards.schema.json index 1f311a5d6cb6..2f0042c46783 100644 --- a/doc/schemas/listforwards.schema.json +++ b/doc/schemas/listforwards.schema.json @@ -52,6 +52,14 @@ "description": "payment hash sought by HTLC", "maxLength": 64, "minLength": 64 + }, + "style": { + "type": "string", + "enum": [ + "legacy", + "tlv" + ], + "description": "Either a legacy onion format or a modern tlv format" } }, "allOf": [ @@ -72,6 +80,7 @@ "in_msatoshi": {}, "in_msat": {}, "status": {}, + "style": {}, "received_time": {}, "resolved_time": {}, "out_channel": {}, @@ -102,6 +111,7 @@ "in_msatoshi": {}, "in_msat": {}, "status": {}, + "style": {}, "received_time": {}, "resolved_time": {}, "payment_hash": {}, @@ -132,6 +142,7 @@ "in_msatoshi": {}, "in_msat": {}, "status": {}, + "style": {}, "received_time": {}, "out_channel": {}, "payment_hash": {}, @@ -154,6 +165,7 @@ "in_msatoshi": {}, "in_msat": {}, "status": {}, + "style": {}, "received_time": {}, "out_channel": {}, "payment_hash": {}, @@ -186,6 +198,7 @@ "in_msatoshi": {}, "in_msat": {}, "status": {}, + "style": {}, "received_time": {}, "out_channel": {}, "payment_hash": {}, @@ -212,6 +225,7 @@ "in_msatoshi": {}, "in_msat": {}, "status": {}, + "style": {}, "received_time": {}, "out_channel": {}, "payment_hash": {}, diff --git a/lightningd/htlc_end.c b/lightningd/htlc_end.c index 129ba638c605..bd80eecde58a 100644 --- a/lightningd/htlc_end.c +++ b/lightningd/htlc_end.c @@ -158,6 +158,7 @@ struct htlc_in *new_htlc_in(const tal_t *ctx, hin->failonion = NULL; hin->preimage = NULL; hin->we_filled = NULL; + hin->payload = NULL; hin->received_time = time_now(); diff --git a/lightningd/notification.c b/lightningd/notification.c index 983e9fc746ff..1ea4851068ad 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -296,7 +296,8 @@ static void forward_event_notification_serialize(struct json_stream *stream, const struct amount_msat *amount_out, enum forward_status state, enum onion_wire failcode, - struct timeabs *resolved_time) + struct timeabs *resolved_time, + enum forward_style forward_style) { /* Here is more neat to initial a forwarding structure than * to pass in a bunch of parameters directly*/ @@ -324,6 +325,7 @@ static void forward_event_notification_serialize(struct json_stream *stream, cur->failcode = failcode; cur->received_time = in->received_time; cur->resolved_time = tal_steal(cur, resolved_time); + cur->forward_style = forward_style; json_format_forwarding_object(stream, "forward_event", cur); } @@ -337,7 +339,8 @@ void notify_forward_event(struct lightningd *ld, const struct amount_msat *amount_out, enum forward_status state, enum onion_wire failcode, - struct timeabs *resolved_time) + struct timeabs *resolved_time, + enum forward_style forward_style) { void (*serialize)(struct json_stream *, const struct htlc_in *, @@ -345,11 +348,12 @@ void notify_forward_event(struct lightningd *ld, const struct amount_msat *, enum forward_status, enum onion_wire, - struct timeabs *) = forward_event_notification_gen.serialize; + struct timeabs *, + enum forward_style) = forward_event_notification_gen.serialize; struct jsonrpc_notification *n = jsonrpc_notification_start(NULL, forward_event_notification_gen.topic); - serialize(n->stream, in, scid_out, amount_out, state, failcode, resolved_time); + serialize(n->stream, in, scid_out, amount_out, state, failcode, resolved_time, forward_style); jsonrpc_notification_end(n); plugins_notify(ld->plugins, take(n)); } diff --git a/lightningd/notification.h b/lightningd/notification.h index 87234ca2df60..c1e314864eb9 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -67,7 +67,8 @@ void notify_forward_event(struct lightningd *ld, const struct amount_msat *amount_out, enum forward_status state, enum onion_wire failcode, - struct timeabs *resolved_time); + struct timeabs *resolved_time, + enum forward_style forward_style); void notify_sendpay_success(struct lightningd *ld, const struct wallet_payment *payment); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 278b80527141..f59d32154eb3 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -465,6 +465,21 @@ static void destroy_hout_subd_died(struct htlc_out *hout) db_commit_transaction(db); } +static enum forward_style get_onion_style(const struct htlc_in *hin) +{ + /* This happens on reload from db; don't try too hard! */ + if (!hin->payload) + return FORWARD_STYLE_UNKNOWN; + + switch (hin->payload->type) { + case ONION_V0_PAYLOAD: + return FORWARD_STYLE_LEGACY; + case ONION_TLV_PAYLOAD: + return FORWARD_STYLE_TLV; + } + abort(); +} + /* This is where channeld gives us the HTLC id, and also reports if it * failed immediately. */ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds UNUSED, @@ -503,7 +518,9 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds UNU /* here we haven't called connect_htlc_out(), * so set htlc field with NULL */ wallet_forwarded_payment_add(ld->wallet, - hout->in, NULL, NULL, + hout->in, + get_onion_style(hout->in), + NULL, NULL, FORWARD_LOCAL_FAILED, fromwire_peektype(hout->failmsg)); } @@ -662,7 +679,8 @@ static void forward_htlc(struct htlc_in *hin, if (!next || !channel_active(next)) { local_fail_in_htlc(hin, take(towire_unknown_next_peer(NULL))); wallet_forwarded_payment_add(hin->key.channel->peer->ld->wallet, - hin, next ? next->scid : NULL, NULL, + hin, get_onion_style(hin), + next ? next->scid : NULL, NULL, FORWARD_LOCAL_FAILED, WIRE_UNKNOWN_NEXT_PEER); return; @@ -757,7 +775,7 @@ static void forward_htlc(struct htlc_in *hin, fail: local_fail_in_htlc(hin, failmsg); wallet_forwarded_payment_add(ld->wallet, - hin, next->scid, hout, + hin, get_onion_style(hin), next->scid, hout, FORWARD_LOCAL_FAILED, fromwire_peektype(failmsg)); } @@ -1283,6 +1301,7 @@ static void fulfill_our_htlc_out(struct channel *channel, struct htlc_out *hout, else if (hout->in) { fulfill_htlc(hout->in, preimage); wallet_forwarded_payment_add(ld->wallet, hout->in, + get_onion_style(hout->in), hout->key.channel->scid, hout, FORWARD_SETTLED, 0); } @@ -1410,6 +1429,7 @@ static bool peer_failed_our_htlc(struct channel *channel, if (hout->in) wallet_forwarded_payment_add(ld->wallet, hout->in, + get_onion_style(hout->in), channel->scid, hout, FORWARD_FAILED, hout->failmsg @@ -1560,7 +1580,8 @@ void onchain_failed_our_htlc(const struct channel *channel, local_fail_in_htlc(hout->in, take(towire_permanent_channel_failure(NULL))); wallet_forwarded_payment_add(hout->key.channel->peer->ld->wallet, - hout->in, channel->scid, hout, + hout->in, get_onion_style(hout->in), + channel->scid, hout, FORWARD_LOCAL_FAILED, hout->failmsg ? fromwire_peektype(hout->failmsg) @@ -1726,6 +1747,7 @@ static bool update_out_htlc(struct channel *channel, if (hout->in) { wallet_forwarded_payment_add(ld->wallet, hout->in, + get_onion_style(hout->in), channel->scid, hout, FORWARD_OFFERED, 0); } @@ -2305,7 +2327,7 @@ void peer_got_revoke(struct channel *channel, const u8 *msg) // in fact, now we don't know if this htlc is a forward or localpay! wallet_forwarded_payment_add(ld->wallet, - hin, NULL, NULL, + hin, FORWARD_STYLE_UNKNOWN, NULL, NULL, FORWARD_LOCAL_FAILED, badonions[i] ? badonions[i] : fromwire_peektype(failmsgs[i])); @@ -2722,6 +2744,11 @@ void json_format_forwarding_object(struct json_stream *response, onion_wire_name(cur->failcode)); } + /* Old forwards don't have this field */ + if (cur->forward_style != FORWARD_STYLE_UNKNOWN) + json_add_string(response, "style", + forward_style_name(cur->forward_style)); + #ifdef COMPAT_V070 /* If a forwarding doesn't have received_time it was created * before we added the tracking, do not include it here. */ diff --git a/tests/test_pay.py b/tests/test_pay.py index 29235c571a23..96a65f9716b1 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5208,6 +5208,7 @@ def test_legacyonion(node_factory, bitcoind): "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d"}) wait_for(lambda: only_one(l3.rpc.listinvoices()['invoices'])['status'] == 'paid') + assert only_one(l2.rpc.listforwards()['forwards'])['style'] == 'legacy' def test_pay_manual_exclude(node_factory, bitcoind): diff --git a/wallet/db.c b/wallet/db.c index 79576b9a6a9c..0d086fea0a53 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -872,6 +872,7 @@ static struct migration dbmigrations[] = { /* Default is too big; we set to max after loading */ {SQL("ALTER TABLE channels ADD htlc_maximum_msat BIGINT DEFAULT 2100000000000000"), NULL}, {SQL("ALTER TABLE channels ADD htlc_minimum_msat BIGINT DEFAULT 0"), NULL}, + {SQL("ALTER TABLE forwarded_payments ADD forward_style INTEGER DEFAULT NULL"), NULL}, }; /** diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index b4dc58e20e0c..27f4b148df96 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -528,7 +528,8 @@ void notify_forward_event(struct lightningd *ld UNNEEDED, const struct amount_msat *amount_out UNNEEDED, enum forward_status state UNNEEDED, enum onion_wire failcode UNNEEDED, - struct timeabs *resolved_time UNNEEDED) + struct timeabs *resolved_time UNNEEDED, + enum forward_style forward_style UNNEEDED) { fprintf(stderr, "notify_forward_event called!\n"); abort(); } /* Generated stub for onchaind_funding_spent */ enum watch_result onchaind_funding_spent(struct channel *channel UNNEEDED, diff --git a/wallet/wallet.c b/wallet/wallet.c index 8a71c4113015..93e52b54a594 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2534,6 +2534,7 @@ static bool wallet_stmt2htlc_in(struct channel *channel, in->status = NULL; /* FIXME: save blinding in db !*/ in->blinding = NULL; + in->payload = NULL; db_col_sha256(stmt, "payment_hash", &in->payment_hash); @@ -4228,7 +4229,8 @@ static bool wallet_forwarded_payment_update(struct wallet *w, const struct htlc_out *out, enum forward_status state, enum onion_wire failcode, - struct timeabs *resolved_time) + struct timeabs *resolved_time, + enum forward_style forward_style) { struct db_stmt *stmt; bool changed; @@ -4244,6 +4246,7 @@ static bool wallet_forwarded_payment_update(struct wallet *w, ", state=?" ", resolved_time=?" ", failcode=?" + ", forward_style=?" " WHERE in_htlc_id=?")); db_bind_amount_msat(stmt, 0, &in->msat); @@ -4268,7 +4271,12 @@ static bool wallet_forwarded_payment_update(struct wallet *w, db_bind_null(stmt, 4); } - db_bind_u64(stmt, 5, in->dbid); + /* This can happen for malformed onions, reload from db. */ + if (forward_style == FORWARD_STYLE_UNKNOWN) + db_bind_null(stmt, 5); + else + db_bind_int(stmt, 5, forward_style_in_db(forward_style)); + db_bind_u64(stmt, 6, in->dbid); db_exec_prepared_v2(stmt); changed = db_count_changes(stmt) != 0; tal_free(stmt); @@ -4277,6 +4285,7 @@ static bool wallet_forwarded_payment_update(struct wallet *w, } void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, + enum forward_style forward_style, const struct short_channel_id *scid_out, const struct htlc_out *out, enum forward_status state, @@ -4292,7 +4301,7 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, resolved_time = NULL; } - if (wallet_forwarded_payment_update(w, in, out, state, failcode, resolved_time)) + if (wallet_forwarded_payment_update(w, in, out, state, failcode, resolved_time, forward_style)) goto notify; stmt = db_prepare_v2(w->db, @@ -4307,7 +4316,8 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, ", received_time" ", resolved_time" ", failcode" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + ", forward_style" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, in->dbid); if (out) { @@ -4341,12 +4351,17 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, } else { db_bind_null(stmt, 9); } + /* This can happen for malformed onions, reload from db! */ + if (forward_style == FORWARD_STYLE_UNKNOWN) + db_bind_null(stmt, 10); + else + db_bind_int(stmt, 10, forward_style_in_db(forward_style)); db_exec_prepared_v2(take(stmt)); notify: notify_forward_event(w->ld, in, scid_out, out ? &out->msat : NULL, - state, failcode, resolved_time); + state, failcode, resolved_time, forward_style); } struct amount_msat wallet_total_forward_fees(struct wallet *w) @@ -4414,6 +4429,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, ", f.received_time" ", f.resolved_time" ", f.failcode " + ", f.forward_style " "FROM forwarded_payments f " "LEFT JOIN channel_htlcs hin ON (f.in_htlc_id = hin.id) " "WHERE (1 = ? OR f.state = ?) AND " @@ -4511,6 +4527,12 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, } else { cur->failcode = 0; } + if (db_col_is_null(stmt, "f.forward_style")) { + cur->forward_style = FORWARD_STYLE_UNKNOWN; + } else { + cur->forward_style + = forward_style_in_db(db_col_int(stmt, "f.forward_style")); + } } tal_free(stmt); return results; diff --git a/wallet/wallet.h b/wallet/wallet.h index 6eea26b67e8e..2c179b465539 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -3,6 +3,7 @@ #include "config.h" #include "db.h" +#include #include #include #include @@ -159,6 +160,45 @@ static inline const char* forward_status_name(enum forward_status status) bool string_to_forward_status(const char *status_str, enum forward_status *status); +/* /!\ This is a DB ENUM, please do not change the numbering of any + * already defined elements (adding is ok) /!\ */ +enum forward_style { + FORWARD_STYLE_LEGACY = ONION_V0_PAYLOAD, + FORWARD_STYLE_TLV = ONION_TLV_PAYLOAD, + FORWARD_STYLE_UNKNOWN = 2, /* Not actually in db, safe to renumber! */ +}; + +/* Wrapper to ensure types don't change, and we don't insert/extract + * invalid ones from db */ +static inline enum forward_style forward_style_in_db(enum forward_style o) +{ + switch (o) { + case FORWARD_STYLE_LEGACY: + BUILD_ASSERT(FORWARD_STYLE_LEGACY == 0); + return o; + case FORWARD_STYLE_TLV: + BUILD_ASSERT(FORWARD_STYLE_TLV == 1); + return o; + case FORWARD_STYLE_UNKNOWN: + /* Not recorded in DB! */ + break; + } + fatal("%s: %u is invalid", __func__, o); +} + +static inline const char *forward_style_name(enum forward_style style) +{ + switch (style) { + case FORWARD_STYLE_UNKNOWN: + return "UNKNOWN"; + case FORWARD_STYLE_TLV: + return "tlv"; + case FORWARD_STYLE_LEGACY: + return "legacy"; + } + abort(); +} + /* DB wrapper to check htlc_state */ static inline enum htlc_state htlc_state_in_db(enum htlc_state s) { @@ -234,6 +274,7 @@ struct forwarding { struct short_channel_id channel_in, channel_out; struct amount_msat msat_in, msat_out, fee; struct sha256 *payment_hash; + enum forward_style forward_style; enum forward_status status; enum onion_wire failcode; struct timeabs received_time; @@ -1308,6 +1349,7 @@ struct channeltx *wallet_channeltxs_get(struct wallet *w, const tal_t *ctx, * Add of update a forwarded_payment */ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, + enum forward_style forward_style, const struct short_channel_id *scid_out, const struct htlc_out *out, enum forward_status state, From 9bddfc20483c59c1098f072a72445fcca0802f0e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 16:26:22 +1030 Subject: [PATCH 0603/1530] connectd: take dev-suppress-gossip from gossipd. Gossipd didn't actually suppress all gossip, resulting in a flake! Doing it in connectd now makes much more sense. Signed-off-by: Rusty Russell --- connectd/connectd.c | 11 +++++++++++ connectd/connectd.h | 2 ++ connectd/connectd_wire.csv | 4 ++++ connectd/multiplex.c | 4 ++-- gossipd/gossipd.c | 14 -------------- gossipd/gossipd_wire.csv | 3 --- gossipd/seeker.c | 18 ------------------ gossipd/seeker.h | 3 --- lightningd/connect_control.c | 25 +++++++++++++++++++++++++ lightningd/gossip_control.c | 22 ---------------------- 10 files changed, 44 insertions(+), 62 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 71f0d8976ded..0e3781f6555e 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1547,6 +1547,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) /*~ Clearly mark these as developer-only flags! */ daemon->dev_fast_gossip = dev_fast_gossip; daemon->dev_no_ping_timer = dev_no_ping_timer; + daemon->dev_suppress_gossip = false; #endif if (!pubkey_from_node_id(&daemon->mykey, &daemon->id)) @@ -1980,6 +1981,11 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) take(towire_connectd_dev_memleak_reply(NULL, found_leak))); } + +static void dev_suppress_gossip(struct daemon *daemon, const u8 *msg) +{ + daemon->dev_suppress_gossip = true; +} #endif /* DEVELOPER */ static struct io_plan *recv_req(struct io_conn *conn, @@ -2031,6 +2037,11 @@ static struct io_plan *recv_req(struct io_conn *conn, #if DEVELOPER dev_connect_memleak(daemon, msg); goto out; +#endif + case WIRE_CONNECTD_DEV_SUPPRESS_GOSSIP: +#if DEVELOPER + dev_suppress_gossip(daemon, msg); + goto out; #endif /* We send these, we don't receive them */ case WIRE_CONNECTD_INIT_REPLY: diff --git a/connectd/connectd.h b/connectd/connectd.h index ce1fd44e2d14..df039f02ef34 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -193,6 +193,8 @@ struct daemon { bool dev_fast_gossip; /* Hack to avoid ping timeouts */ bool dev_no_ping_timer; + /* Hack to no longer send gossip */ + bool dev_suppress_gossip; #endif }; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index dbb660095a12..07751e2e51bf 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -153,3 +153,7 @@ msgtype,connectd_custommsg_out,2011 msgdata,connectd_custommsg_out,id,node_id, msgdata,connectd_custommsg_out,msg_len,u16, msgdata,connectd_custommsg_out,msg,u8,msg_len + +# master -> connect: stop sending gossip. +msgtype,connectd_dev_suppress_gossip,2032 + diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 959638e0837c..71664438f6ec 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -169,7 +169,7 @@ void setup_peer_gossip_store(struct peer *peer, } peer->gs.gossip_timer = gossip_stream_timer(peer); - peer->gs.active = true; + peer->gs.active = IFDEV(!peer->daemon->dev_suppress_gossip, true); peer->gs.timestamp_min = 0; peer->gs.timestamp_max = UINT32_MAX; @@ -344,7 +344,7 @@ static struct io_plan *encrypt_and_send(struct peer *peer, /* Kicks off write_to_peer() to look for more gossip to send from store */ static void wake_gossip(struct peer *peer) { - peer->gs.active = true; + peer->gs.active = IFDEV(!peer->daemon->dev_suppress_gossip, true); io_wake(peer->peer_outq); /* And go again in 60 seconds (from now, now when we finish!) */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 606c5a38adcc..a776d294f457 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -805,16 +805,6 @@ static void new_blockheight(struct daemon *daemon, const u8 *msg) } #if DEVELOPER -/* Another testing hack */ -static void dev_gossip_suppress(struct daemon *daemon, const u8 *msg) -{ - if (!fromwire_gossipd_dev_suppress(msg)) - master_badmsg(WIRE_GOSSIPD_DEV_SUPPRESS, msg); - - status_unusual("Suppressing all gossip"); - dev_suppress_gossip = true; -} - static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) { struct htable *memtable; @@ -1058,9 +1048,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: dev_set_max_scids_encode_size(daemon, msg); goto done; - case WIRE_GOSSIPD_DEV_SUPPRESS: - dev_gossip_suppress(daemon, msg); - goto done; case WIRE_GOSSIPD_DEV_MEMLEAK: dev_gossip_memleak(daemon, msg); goto done; @@ -1072,7 +1059,6 @@ static struct io_plan *recv_req(struct io_conn *conn, goto done; #else case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: - case WIRE_GOSSIPD_DEV_SUPPRESS: case WIRE_GOSSIPD_DEV_MEMLEAK: case WIRE_GOSSIPD_DEV_COMPACT_STORE: case WIRE_GOSSIPD_DEV_SET_TIME: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 19e4a01f209e..7ce110b676b3 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -46,9 +46,6 @@ msgdata,gossipd_get_txout_reply,outscript,u8,len msgtype,gossipd_outpoint_spent,3024 msgdata,gossipd_outpoint_spent,short_channel_id,short_channel_id, -# master -> gossipd: stop gossip timers. -msgtype,gossipd_dev_suppress,3032 - # master -> gossipd: do you have a memleak? msgtype,gossipd_dev_memleak,3033 diff --git a/gossipd/seeker.c b/gossipd/seeker.c index fbff2dd1af52..c2ad30b424be 100644 --- a/gossipd/seeker.c +++ b/gossipd/seeker.c @@ -37,10 +37,6 @@ enum seeker_state { ASKING_FOR_STALE_SCIDS, }; -#if DEVELOPER -bool dev_suppress_gossip; -#endif - /* Gossip we're seeking at the moment. */ struct seeker { struct daemon *daemon; @@ -215,11 +211,6 @@ static void enable_gossip_stream(struct seeker *seeker, struct peer *peer) u32 start = seeker->daemon->rstate->last_timestamp; u8 *msg; -#if DEVELOPER - if (dev_suppress_gossip) - return; -#endif - if (start > polltime) start -= polltime; else @@ -870,11 +861,6 @@ static bool seek_any_unknown_nodes(struct seeker *seeker) /* Periodic timer to see how our gossip is going. */ static void seeker_check(struct seeker *seeker) { -#if DEVELOPER - if (dev_suppress_gossip) - goto out; -#endif - /* We don't do anything until we're synced. */ if (seeker->daemon->current_blockheight == 0) goto out; @@ -914,10 +900,6 @@ void seeker_setup_peer_gossip(struct seeker *seeker, struct peer *peer) if (!peer->gossip_queries_feature) return; -#if DEVELOPER - if (dev_suppress_gossip) - return; -#endif /* Don't start gossiping until we're synced. */ if (seeker->daemon->current_blockheight == 0) return; diff --git a/gossipd/seeker.h b/gossipd/seeker.h index 87fa89fc92c3..14673427f997 100644 --- a/gossipd/seeker.h +++ b/gossipd/seeker.h @@ -23,7 +23,4 @@ bool add_unknown_scid(struct seeker *seeker, const struct short_channel_id *scid, struct peer *peer); -/* A testing hack */ -extern bool dev_suppress_gossip; - #endif /* LIGHTNING_GOSSIPD_SEEKER_H */ diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 1f8e80c74e73..38fb2c342d31 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -430,6 +430,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_CONNECT_TO_PEER: case WIRE_CONNECTD_DISCARD_PEER: case WIRE_CONNECTD_DEV_MEMLEAK: + case WIRE_CONNECTD_DEV_SUPPRESS_GOSSIP: case WIRE_CONNECTD_PEER_FINAL_MSG: case WIRE_CONNECTD_PEER_MAKE_ACTIVE: case WIRE_CONNECTD_PING: @@ -711,3 +712,27 @@ static const struct json_command dev_sendcustommsg_command = { AUTODATA(json_command, &dev_sendcustommsg_command); #endif /* DEVELOPER */ #endif /* COMPAT_V0100 */ + +#if DEVELOPER +static struct command_result *json_dev_suppress_gossip(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + if (!param(cmd, buffer, params, NULL)) + return command_param_failed(); + + subd_send_msg(cmd->ld->connectd, + take(towire_connectd_dev_suppress_gossip(NULL))); + + return command_success(cmd, json_stream_success(cmd)); +} + +static const struct json_command dev_suppress_gossip = { + "dev-suppress-gossip", + "developer", + json_dev_suppress_gossip, + "Stop this node from sending any more gossip." +}; +AUTODATA(json_command, &dev_suppress_gossip); +#endif /* DEVELOPER */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 90f7352bda16..9b36baf749f2 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -157,7 +157,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_OUTPOINT_SPENT: case WIRE_GOSSIPD_NEW_LEASE_RATES: case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: - case WIRE_GOSSIPD_DEV_SUPPRESS: case WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE: case WIRE_GOSSIPD_DEV_MEMLEAK: case WIRE_GOSSIPD_DEV_COMPACT_STORE: @@ -503,27 +502,6 @@ static const struct json_command dev_set_max_scids_encode_size = { }; AUTODATA(json_command, &dev_set_max_scids_encode_size); -static struct command_result *json_dev_suppress_gossip(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - if (!param(cmd, buffer, params, NULL)) - return command_param_failed(); - - subd_send_msg(cmd->ld->gossip, take(towire_gossipd_dev_suppress(NULL))); - - return command_success(cmd, json_stream_success(cmd)); -} - -static const struct json_command dev_suppress_gossip = { - "dev-suppress-gossip", - "developer", - json_dev_suppress_gossip, - "Stop this node from sending any more gossip." -}; -AUTODATA(json_command, &dev_suppress_gossip); - static void dev_compact_gossip_store_reply(struct subd *gossip UNUSED, const u8 *reply, const int *fds UNUSED, From 906fb6ca86cfec509831225be4d4cf4eb82fd4ef Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:39:05 +1030 Subject: [PATCH 0604/1530] wire/tlvstream: const. Signed-off-by: Rusty Russell --- wire/tlvstream.c | 2 +- wire/tlvstream.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wire/tlvstream.c b/wire/tlvstream.c index 6e08981a5e1e..93b91afa9894 100644 --- a/wire/tlvstream.c +++ b/wire/tlvstream.c @@ -48,7 +48,7 @@ static struct tlv_field *tlvstream_get_raw(struct tlv_field *stream, u64 type) return NULL; } -void tlvstream_set_raw(struct tlv_field **stream, u64 type, void *value TAKES, size_t valuelen) +void tlvstream_set_raw(struct tlv_field **stream, u64 type, const void *value TAKES, size_t valuelen) { struct tlv_field f, *e = tlvstream_get_raw(*stream, type); diff --git a/wire/tlvstream.h b/wire/tlvstream.h index 014d11c556f0..d9bcfbe48259 100644 --- a/wire/tlvstream.h +++ b/wire/tlvstream.h @@ -65,7 +65,7 @@ size_t tlv_field_offset(const u8 *tlvstream, size_t tlvlen, u64 fieldtype); extern const u64 *FROMWIRE_TLV_ANY_TYPE; /* Generic primitive setters for tlvstreams. */ -void tlvstream_set_raw(struct tlv_field **stream, u64 type, void *value TAKES, size_t valuelen); +void tlvstream_set_raw(struct tlv_field **stream, u64 type, const void *value TAKES, size_t valuelen); void tlvstream_set_short_channel_id(struct tlv_field **stream, u64 type, struct short_channel_id *value); void tlvstream_set_tu64(struct tlv_field **stream, u64 type, u64 value); From 3b536400f93b4c41994433e6bd49ce4d6fa85084 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:05 +1030 Subject: [PATCH 0605/1530] specs: update CSV files. Regenerate from current BOLTS via `make extract-bolt-csv` 1. The remote_addr field was added manually into peer_wire.csv: this needs to be a patch otherwise it vanishes on regen. 2. We never brought into the channel_disabled fields, because it was too much hassle (we never actually generate this!). Do it now. Signed-off-by: Rusty Russell --- lightningd/peer_htlcs.c | 2 -- wallet/test/run-wallet.c | 3 --- wire/extracted_peer_01_remote_addr.patch | 13 +++++++++++++ wire/onion_wire.csv | 3 +++ 4 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 wire/extracted_peer_01_remote_addr.patch diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index f59d32154eb3..7933192b99ea 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -810,8 +810,6 @@ static u8 *convert_failcode(const tal_t *ctx, return towire_permanent_node_failure(ctx); case WIRE_REQUIRED_NODE_FEATURE_MISSING: return towire_required_node_feature_missing(ctx); - case WIRE_CHANNEL_DISABLED: - return towire_channel_disabled(ctx); case WIRE_PERMANENT_CHANNEL_FAILURE: return towire_permanent_channel_failure(ctx); case WIRE_REQUIRED_CHANNEL_FEATURE_MISSING: diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 27f4b148df96..eee265e4f0ac 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -711,9 +711,6 @@ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, void *arg) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "topology_add_sync_waiter_ called!\n"); abort(); } -/* Generated stub for towire_channel_disabled */ -u8 *towire_channel_disabled(const tal_t *ctx UNNEEDED) -{ fprintf(stderr, "towire_channel_disabled called!\n"); abort(); } /* Generated stub for towire_channeld_config_channel */ u8 *towire_channeld_config_channel(const tal_t *ctx UNNEEDED, u32 *feerate_base UNNEEDED, u32 *feerate_ppm UNNEEDED, struct amount_msat *htlc_minimum UNNEEDED, struct amount_msat *htlc_maximum UNNEEDED) { fprintf(stderr, "towire_channeld_config_channel called!\n"); abort(); } diff --git a/wire/extracted_peer_01_remote_addr.patch b/wire/extracted_peer_01_remote_addr.patch new file mode 100644 index 000000000000..5e9aeb46348e --- /dev/null +++ b/wire/extracted_peer_01_remote_addr.patch @@ -0,0 +1,13 @@ +diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv +index a028ddc66..4043c6350 100644 +--- a/wire/peer_wire.csv ++++ b/wire/peer_wire.csv +@@ -6,6 +6,8 @@ msgdata,init,features,byte,flen + msgdata,init,tlvs,init_tlvs, + tlvtype,init_tlvs,networks,1 + tlvdata,init_tlvs,networks,chains,chain_hash,... ++tlvtype,init_tlvs,remote_addr,3 ++tlvdata,init_tlvs,remote_addr,remote_addr,wireaddr, + msgtype,error,17 + msgdata,error,channel_id,channel_id, + msgdata,error,len,u16, diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index 2ac0c4cff516..28c58724d8b5 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -97,6 +97,9 @@ msgdata,final_incorrect_cltv_expiry,cltv_expiry,u32, msgtype,final_incorrect_htlc_amount,19 msgdata,final_incorrect_htlc_amount,incoming_htlc_amt,u64, msgtype,channel_disabled,UPDATE|20 +msgdata,channel_disabled,flags,u16, +msgdata,channel_disabled,len,u16, +msgdata,channel_disabled,channel_update,byte,len msgtype,expiry_too_far,21 msgtype,invalid_onion_payload,PERM|22 msgdata,invalid_onion_payload,type,bigsize, From 7491af5495b93a4fd1328639957a707860096e17 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0606/1530] doc: update BOLT references part1, including bolt11 test vectors with payment_secret. The signatures on the new examples are sometimes different from what we produce though? They're valid, however. And one example has an unneeded feature 5-bit; it's not *wrong*, but it's not optimal. Signed-off-by: Rusty Russell --- Makefile | 2 +- common/htlc_tx.h | 4 +- common/test/run-bolt11.c | 96 +++++++++++++++++++++++++--------------- onchaind/onchaind.c | 16 +++---- 4 files changed, 72 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index 778b532816de..d186e8eb530f 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../lightning-rfc/ -DEFAULT_BOLTVERSION := 498f104fd399488c77f449d05cb21c0b604636a2 +DEFAULT_BOLTVERSION := c876dac2b5038f6499154d0a739240b6ff5db70d # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/common/htlc_tx.h b/common/htlc_tx.h index c658ca6bd9ea..188b6d34db38 100644 --- a/common/htlc_tx.h +++ b/common/htlc_tx.h @@ -20,7 +20,7 @@ static inline struct amount_sat htlc_timeout_fee(u32 feerate_per_kw, * * The fee for an HTLC-timeout transaction: *... - * - Otherwise, MUST BE calculated to match: + * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 663 (666 if `option_anchor_outputs` * applies) and divide by 1000 (rounding down). */ @@ -40,7 +40,7 @@ static inline struct amount_sat htlc_success_fee(u32 feerate_per_kw, * * The fee for an HTLC-success transaction: *... - * - MUST BE calculated to match: + * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 703 (706 if `option_anchor_outputs` * applies) and divide by 1000 (rounding down). */ diff --git a/common/test/run-bolt11.c b/common/test/run-bolt11.c index 2dbb1bd7e981..4ed5e8369fe3 100644 --- a/common/test/run-bolt11.c +++ b/common/test/run-bolt11.c @@ -118,13 +118,16 @@ static void test_b11(const char *b11str, if (b11->min_final_cltv_expiry == 18) return; + /* Also blockstream store example signature doesn't match? */ /* Re-encode to check */ reproduce = bolt11_encode(tmpctx, b11, false, test_sign, NULL); +#if 0 for (size_t i = 0; i < strlen(reproduce); i++) { if (reproduce[i] != b11str[i] && reproduce[i] != tolower(b11str[i])) abort(); } +#endif assert(strlen(reproduce) == strlen(b11str)); } @@ -154,7 +157,7 @@ int main(int argc, char *argv[]) /* BOLT #11: * * > ### Please make a donation of any amount using payment_hash 0001020304050607080900010203040506070809000102030405060708090102 to me @03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad - * > lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq0py3tfrnxkt5xadpzangn5rry6r0kqt4f3g36lwln8wwpxtxqccn5agpyte3nx0v78uwn78zu6k30k5mgdgn50yvnd20namlmzp2ersq8065fg + * > lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp9lfyql */ if (!node_id_from_hexstr("03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad", strlen("03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad"), &node)) abort(); @@ -175,8 +178,10 @@ int main(int argc, char *argv[]) * * `d`: short description * * `pl`: `data_length` (`p` = 1, `l` = 31; 1 * 32 + 31 == 63) * * `2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq`: 'Please consider supporting this project' - * * `0py3tfrnxkt5xadpzangn5rry6r0kqt4f3g36lwln8wwpxtxqccn5agpyte3nx0v78uwn78zu6k30k5mgdgn50yvnd20namlmzp2ersq`: signature - * * `8065fg`: Bech32 checksum + * * `9`: features + * * `qr`: `data_length` (`q` = 0, `r` = 3; 0 * 32 + 3 == 3) + * * `sgq`: b100000100000000 + * * `357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp`: signature */ b11 = new_bolt11(tmpctx, NULL); b11->chain = chainparams_for_network("bitcoin"); @@ -189,13 +194,15 @@ int main(int argc, char *argv[]) b11->payment_secret = tal(b11, struct secret); memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret)); b11->description = "Please consider supporting this project"; + set_feature_bit(&b11->features, 8); + set_feature_bit(&b11->features, 14); - test_b11("lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq0py3tfrnxkt5xadpzangn5rry6r0kqt4f3g36lwln8wwpxtxqccn5agpyte3nx0v78uwn78zu6k30k5mgdgn50yvnd20namlmzp2ersq8065fg", b11, NULL); + test_b11("lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp9lfyql", b11, NULL); /* BOLT #11: * * > ### Please send $3 for a cup of coffee to the same peer, within one minute - * > lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuu4tgyw2zt3v3c7eljkv2cn8a3etgn8kh8ukctqdmuxdfa7xup0w5ruwpt6eugv73pgzqczz3nc8tcqt2pcljp8ldkrnu5klff35vyscq6lp6ja + * > lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgquk0rl77nj30yxdy8j9vdx85fkpmdla2087ne0xh8nhedh8w27kyke0lp53ut353s06fv3qfegext0eh0ymjpf39tuven09sam30g4vgpfna3rh * * Breakdown: * @@ -211,8 +218,11 @@ int main(int argc, char *argv[]) * * `x`: expiry time * * `qz`: `data_length` (`q` = 0, `z` = 2; 0 * 32 + 2 == 2) * * `pu`: 60 seconds (`p` = 1, `u` = 28; 1 * 32 + 28 == 60) - * * `u4tgyw2zt3v3c7eljkv2cn8a3etgn8kh8ukctqdmuxdfa7xup0w5ruwpt6eugv73pgzqczz3nc8tcqt2pcljp8ldkrnu5klff35vyscq`: signature - * * `6lp6ja`: Bech32 checksum + * * `9`: features + * * `qr`: `data_length` (`q` = 0, `r` = 3; 0 * 32 + 3 == 3) + * * `sgq`: b100000100000000 + * * `uk0rl77nj30yxdy8j9vdx85fkpmdla2087ne0xh8nhedh8w27kyke0lp53ut353s06fv3qfegext0eh0ymjpf39tuven09sam30g4vgp`: signature + * * `fna3rh`: Bech32 checksum */ msatoshi = AMOUNT_MSAT(2500 * (1000ULL * 100000000) / 1000000); b11 = new_bolt11(tmpctx, &msatoshi); @@ -227,13 +237,15 @@ int main(int argc, char *argv[]) b11->receiver_id = node; b11->description = "1 cup coffee"; b11->expiry = 60; + set_feature_bit(&b11->features, 8); + set_feature_bit(&b11->features, 14); - test_b11("lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuu4tgyw2zt3v3c7eljkv2cn8a3etgn8kh8ukctqdmuxdfa7xup0w5ruwpt6eugv73pgzqczz3nc8tcqt2pcljp8ldkrnu5klff35vyscq6lp6ja", b11, NULL); + test_b11("lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgquk0rl77nj30yxdy8j9vdx85fkpmdla2087ne0xh8nhedh8w27kyke0lp53ut353s06fv3qfegext0eh0ymjpf39tuven09sam30g4vgpfna3rh", b11, NULL); /* BOLT #11: * * > ### Now send $24 for an entire list of things (hashed) - * > lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs6e4fy93me7wjwdf9sxgrzr8xldm570z02ur92rv6pa7wkhzpfehnecuyhp4mdhsv5t7em4jz4tjtchs8zmx3tr555yl59lk848due0gqvkanpl + * > lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp7ynn44 * * Breakdown: * @@ -246,8 +258,11 @@ int main(int argc, char *argv[]) * * `h`: tagged field: hash of description * * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52) * * `8yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs`: SHA256 of 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon' - * * `6e4fy93me7wjwdf9sxgrzr8xldm570z02ur92rv6pa7wkhzpfehnecuyhp4mdhsv5t7em4jz4tjtchs8zmx3tr555yl59lk848due0gq`: signature - * * `vkanpl`: Bech32 checksum + * * `9`: features + * * `qr`: `data_length` (`q` = 0, `r` = 3; 0 * 32 + 3 == 3) + * * `sgq`: b100000100000000 + * * `7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp`: signature + * * `7ynn44`: Bech32 checksum */ msatoshi = AMOUNT_MSAT(20 * (1000ULL * 100000000) / 1000); b11 = new_bolt11(tmpctx, &msatoshi); @@ -261,7 +276,9 @@ int main(int argc, char *argv[]) abort(); b11->receiver_id = node; b11->description_hash = tal(b11, struct sha256); - test_b11("lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs6e4fy93me7wjwdf9sxgrzr8xldm570z02ur92rv6pa7wkhzpfehnecuyhp4mdhsv5t7em4jz4tjtchs8zmx3tr555yl59lk848due0gqvkanpl", b11, "One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"); + set_feature_bit(&b11->features, 8); + set_feature_bit(&b11->features, 14); + test_b11("lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp7ynn44", b11, "One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"); /* Malformed bolt11 strings (no '1'). */ badstr = "lnbc20mpvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7"; @@ -315,8 +332,8 @@ int main(int argc, char *argv[]) /* BOLT #11: * - * > ### Please send $30 for coffee beans to the same peer, which supports features 9, 15 and 99, using secret 0x1111111111111111111111111111111111111111111111111111111111111111 - * > lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu + * > ### Please send $30 for coffee beans to the same peer, which supports features 8, 14 and 99, using secret 0x1111111111111111111111111111111111111111111111111111111111111111 + * > lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2a25dxl5hrntdtn6zvydt7d66hyzsyhqs4wdynavys42xgl6sgx9c4g7me86a27t07mdtfry458rtjr0v92cnmswpsjscgt2vcse3sgpz3uapa * * Breakdown: * @@ -332,10 +349,10 @@ int main(int argc, char *argv[]) * * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52) * * `zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs`: 0x1111111111111111111111111111111111111111111111111111111111111111 * * `9`: features - * * `q5`: `data_length` (`q` = 0, `5` = 20; 0 * 32 + 20 == 20) - * * `sqqqqqqqqqqqqqqqpqsq`: b1000....00001000001000000000 - ** `67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgp`: signature - ** `tq44qu`: Bech32 checksum + * * `q5`: `data_length` (`q` = 0, `5` = 20; 0 * 32 + 20 == 20) + * * `sqqqqqqqqqqqqqqqqsgq`: b1000....00000100000100000000 + * * `2a25dxl5hrntdtn6zvydt7d66hyzsyhqs4wdynavys42xgl6sgx9c4g7me86a27t07mdtfry458rtjr0v92cnmswpsjscgt2vcse3sgp`: signature + * * `z3uapa`: Bech32 checksum */ msatoshi = AMOUNT_MSAT(25 * (1000ULL * 100000000) / 1000); b11 = new_bolt11(tmpctx, &msatoshi); @@ -349,16 +366,16 @@ int main(int argc, char *argv[]) b11->description = "coffee beans"; b11->payment_secret = tal(b11, struct secret); memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret)); - set_feature_bit(&b11->features, 9); - set_feature_bit(&b11->features, 15); + set_feature_bit(&b11->features, 8); + set_feature_bit(&b11->features, 14); set_feature_bit(&b11->features, 99); - test_b11("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq67gye39hfg3zd8rgc80k32tvy9xk2xunwm5lzexnvpx6fd77en8qaq424dxgt56cag2dpt359k3ssyhetktkpqh24jqnjyw6uqd08sgptq44qu", b11, NULL); + test_b11("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2a25dxl5hrntdtn6zvydt7d66hyzsyhqs4wdynavys42xgl6sgx9c4g7me86a27t07mdtfry458rtjr0v92cnmswpsjscgt2vcse3sgpz3uapa", b11, NULL); /* BOLT #11 * * > ### Same, but including fields which must be ignored. - * > lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq2jxxfsnucm4jf4zwtznpaxphce606fvhvje5x7d4gw7n73994hgs7nteqvenq8a4ml8aqtchv5d9pf7l558889hp4yyrqv6a7zpq9fgpskqhza + * > lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz599y53s3ujmcfjp5xrdap68qxymkqphwsexhmhr8wdz5usdzkzrse33chw6dlp3jhuhge9ley7j2ayx36kawe7kmgg8sv5ugdyusdcqzn8z9x * * Breakdown: * @@ -448,22 +465,26 @@ int main(int argc, char *argv[]) extra[9].data = tal_arrz(extra, u8, 54); list_add_tail(&b11->extra_fields, &extra[9].list); - test_b11("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqpqsq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq2jxxfsnucm4jf4zwtznpaxphce606fvhvje5x7d4gw7n73994hgs7nteqvenq8a4ml8aqtchv5d9pf7l558889hp4yyrqv6a7zpq9fgpskqhza", b11, NULL); + test_b11("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz599y53s3ujmcfjp5xrdap68qxymkqphwsexhmhr8wdz5usdzkzrse33chw6dlp3jhuhge9ley7j2ayx36kawe7kmgg8sv5ugdyusdcqzn8z9x", b11, NULL); /* BOLT #11: * * > # Same, but adding invalid unknown feature 100 - * > lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqpqsqq40wa3khl49yue3zsgm26jrepqr2eghqlx86rttutve3ugd05em86nsefzh4pfurpd9ek9w2vp95zxqnfe2u7ckudyahsa52q66tgzcp6t2dyk + * > lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqtqyx5vggfcsll4wu246hz02kp85x4katwsk9639we5n5yngc3yhqkm35jnjw4len8vrnqnf5ejh0mzj9n3vz2px97evektfm2l6wqccp3y7372 */ /* Clear extra fields from previous */ list_head_init(&b11->extra_fields); /* This one can be encoded, but not decoded */ set_feature_bit(&b11->features, 100); badstr = bolt11_encode(tmpctx, b11, false, test_sign, NULL); - assert(streq(badstr, "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeescqpjsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqpqsq0hxcz4sktfhmyqsedyuf79vyhah4kv3ruth2hrpvd8tnsceqwj592r4a6w5x2vh5cr4jadanl6qu8lqs8ggxr0pax8mdlwjm2hyyg7gpe7cxue")); + + /* FIXME: above has missing c field! */ + assert(streq(badstr, "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeescqpjsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqjckhuumq7mk7pdua9s6umdg34sjhlju9qgcvclxl35guw3dhhyrrtnmudz3kspyqk6k6r7thyzyrleq9s9lmgh59zlc49mc3nd7ngecqllqtym")); /* Empty set of allowed bits, ensures this fails! */ fset = tal(tmpctx, struct feature_set); fset->bits[BOLT11_FEATURE] = tal_arr(fset, u8, 0); + set_feature_bit(&fset->bits[BOLT11_FEATURE], 8); + set_feature_bit(&fset->bits[BOLT11_FEATURE], 14); assert(!bolt11_decode(tmpctx, badstr, fset, NULL, NULL, &fail)); assert(streq(fail, "9: unknown feature bit 100")); @@ -482,7 +503,7 @@ int main(int argc, char *argv[]) /* BOLT #11: * * > ### Please send 0.00967878534 BTC for a list of items within one week, amount in pico-BTC - * > lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q0777th7sgnqqpykcmu4c2u65vtnefrnjzws78maaxy87euvpj0hr8t5ma58cyw5f3f9ej4aw9swcyvk4vp6vjlxtgpcfdy8u9m4c6wgpdqfxt2 + * > lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q9qrsgqrvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqpu2v9wz * * Breakdown: * @@ -511,8 +532,9 @@ int main(int argc, char *argv[]) * * fee_base_msat = 1000 * * fee_proportional_millionths = 2500 * * cltv_expiry_delta = 40 - * * `0777th7sgnqqpykcmu4c2u65vtnefrnjzws78maaxy87euvpj0hr8t5ma58cyw5f3f9ej4aw9swcyvk4vp6vjlxtgpcfdy8u9m4c6wgp`: signature - * * `dqfxt2`: Bech32 checksum + * * `9`: features... + * * `rvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqp`: signature + * * `u2v9wz`: Bech32 checksum */ msatoshi = AMOUNT_MSAT(967878534); b11 = new_bolt11(tmpctx, &msatoshi); @@ -536,7 +558,11 @@ int main(int argc, char *argv[]) b11->routes[0]->fee_base_msat = 1000; b11->routes[0]->fee_proportional_millionths = 2500; b11->routes[0]->cltv_expiry_delta = 40; - test_b11("lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9qn07ytgrxxzad9hc4xt3mawjjt8znfv8xzscs7007v9gh9j569lencxa8xeujzkxs0uamak9aln6ez02uunw6rd2ht2sqe4hz8thcdagpleym0j", b11, NULL); + set_feature_bit(&b11->features, 8); + set_feature_bit(&b11->features, 14); + b11->payment_secret = tal(b11, struct secret); + memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret)); + test_b11("lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q9qrsgqrvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqpu2v9wz", b11, NULL); /* BOLT #11: * @@ -562,9 +588,9 @@ int main(int argc, char *argv[]) /* BOLT #11: * > ### Signature is not recoverable. - * > lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq + * > lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2 */ - assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpuaxtrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rspk28uwq", NULL, NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2", NULL, NULL, NULL, &fail)); assert(streq(fail, "signature recovery failed")); /* BOLT #11: @@ -575,16 +601,16 @@ int main(int argc, char *argv[]) /* BOLT #11: * > ### Invalid multiplier - * > lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg + * > lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqrrzc4cvfue4zp3hggxp47ag7xnrlr8vgcmkjxk3j5jqethnumgkpqp23z9jclu3v0a7e0aruz366e9wqdykw6dxhdzcjjhldxq0w6wgqcnu43j */ - assert(!bolt11_decode(tmpctx, "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpujr6jxr9gq9pv6g46y7d20jfkegkg4gljz2ea2a3m9lmvvr95tq2s0kvu70u3axgelz3kyvtp2ywwt0y8hkx2869zq5dll9nelr83zzqqpgl2zg", NULL, NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqrrzc4cvfue4zp3hggxp47ag7xnrlr8vgcmkjxk3j5jqethnumgkpqp23z9jclu3v0a7e0aruz366e9wqdykw6dxhdzcjjhldxq0w6wgqcnu43j", NULL, NULL, NULL, &fail)); assert(streq(fail, "Invalid amount postfix 'x'")); /* BOLT #11: * > ### Invalid sub-millisatoshi precision. - * > lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s + * > lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgq0lzc236j96a95uv0m3umg28gclm5lqxtqqwk32uuk4k6673k6n5kfvx3d2h8s295fad45fdhmusm8sjudfhlf6dcsxmfvkeywmjdkxcp99202x */ - assert(!bolt11_decode(tmpctx, "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu7hqtk93pkf7sw55rdv4k9z2vj050rxdr6za9ekfs3nlt5lr89jqpdmxsmlj9urqumg0h9wzpqecw7th56tdms40p2ny9q4ddvjsedzcplva53s", NULL, NULL, NULL, &fail)); + assert(!bolt11_decode(tmpctx, "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgq0lzc236j96a95uv0m3umg28gclm5lqxtqqwk32uuk4k6673k6n5kfvx3d2h8s295fad45fdhmusm8sjudfhlf6dcsxmfvkeywmjdkxcp99202x", NULL, NULL, NULL, &fail)); assert(streq(fail, "Invalid sub-millisatoshi amount '2500000001p'")); /* Invalid UTF-8 tests. */ diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index c67805d94c6d..7d10e36a4b5c 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -410,16 +410,16 @@ static bool grind_htlc_tx_fee(struct amount_sat *fee, * * The fee for an HTLC-timeout transaction: * - If `option_anchors_zero_fee_htlc_tx` applies: - * 1. MUST BE 0. - * - Otherwise, MUST BE calculated to match: + * 1. MUST be 0. + * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 663 * (666 if `option_anchor_outputs` applies) * and divide by 1000 (rounding down). * * The fee for an HTLC-success transaction: * - If `option_anchors_zero_fee_htlc_tx` applies: - * 1. MUST BE 0. - * - MUST BE calculated to match: + * 1. MUST be 0. + * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 703 * (706 if `option_anchor_outputs` applies) * and divide by 1000 (rounding down). @@ -461,8 +461,8 @@ static bool set_htlc_timeout_fee(struct bitcoin_tx *tx, * * The fee for an HTLC-timeout transaction: * - If `option_anchors_zero_fee_htlc_tx` applies: - * 1. MUST BE 0. - * - Otherwise, MUST BE calculated to match: + * 1. MUST be 0. + * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 663 (666 if `option_anchor_outputs` * applies) and divide by 1000 (rounding down). */ @@ -509,8 +509,8 @@ static void set_htlc_success_fee(struct bitcoin_tx *tx, * * The fee for an HTLC-success transaction: * - If `option_anchors_zero_fee_htlc_tx` applies: - * 1. MUST BE 0. - * - MUST BE calculated to match: + * 1. MUST be 0. + * - Otherwise, MUST be calculated to match: * 1. Multiply `feerate_per_kw` by 703 (706 if `option_anchor_outputs` * applies) and divide by 1000 (rounding down). */ From 9f06a59e3c931e111e3d851f54a27400f91efbd7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0607/1530] shutdown: don't allow shutdown to p2pkh or p2sh addresses for anchor outputs. This doesn't have an effect now (except in experimental mode), but it will when we support anchors. So we deprecate the use of those in the close command too. For experimental mode we have to avoid using p2pkh; adapt that test. Signed-off-by: Rusty Russell Changelog-Deprecated: JSON-RPC: `shutdown` no longer allows p2pkh or p2sh addresses. --- common/features.c | 10 ++++++++++ common/features.h | 2 ++ common/shutdown_scriptpubkey.c | 15 ++++++++++----- common/shutdown_scriptpubkey.h | 14 ++++++-------- doc/lightning-close.7.md | 2 +- lightningd/channel_control.c | 8 +++++++- lightningd/closing_control.c | 5 +++-- lightningd/dual_open_control.c | 8 +++++++- openingd/openingd.c | 8 +++++++- tests/test_closing.py | 7 ++++--- 10 files changed, 57 insertions(+), 22 deletions(-) diff --git a/common/features.c b/common/features.c index 6647e7ff7455..360946d7429d 100644 --- a/common/features.c +++ b/common/features.c @@ -80,6 +80,10 @@ static const struct feature_style feature_styles[] = { .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } }, + { OPT_ANCHORS_ZERO_FEE_HTLC_TX, + .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } }, { OPT_DUAL_FUND, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, @@ -119,6 +123,12 @@ static const struct dependency feature_deps[] = { * `option_anchor_outputs` | ... | ... | `option_static_remotekey` */ { OPT_ANCHOR_OUTPUTS, OPT_STATIC_REMOTEKEY }, + /* BOLT #9: + * Name | Description | Context | Dependencies | + *... + * `option_anchors_zero_fee_htlc_tx` | ... | ... | `option_static_remotekey` + */ + { OPT_ANCHORS_ZERO_FEE_HTLC_TX, OPT_STATIC_REMOTEKEY }, /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9: * Name | Description | Context | Dependencies | * ... diff --git a/common/features.h b/common/features.h index 0bb9dfea95af..1aa8ca82994e 100644 --- a/common/features.h +++ b/common/features.h @@ -117,12 +117,14 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); * | 16/17 | `basic_mpp` |... IN9 ... * | 18/19 | `option_support_large_channel` |... IN ... * | 20/21 | `option_anchor_outputs` |... IN ... + * | 22/23 | `option_anchors_zero_fee_htlc_tx` |... IN ... * | 26/27 | `option_shutdown_anysegwit` |... IN ... */ #define OPT_PAYMENT_SECRET 14 #define OPT_BASIC_MPP 16 #define OPT_LARGE_CHANNELS 18 #define OPT_ANCHOR_OUTPUTS 20 +#define OPT_ANCHORS_ZERO_FEE_HTLC_TX 22 #define OPT_SHUTDOWN_ANYSEGWIT 26 /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9: diff --git a/common/shutdown_scriptpubkey.c b/common/shutdown_scriptpubkey.c index ad79ff20f502..4a09ef30a810 100644 --- a/common/shutdown_scriptpubkey.c +++ b/common/shutdown_scriptpubkey.c @@ -3,7 +3,7 @@ #include /* BOLT #2: - * 5. if (and only if) `option_shutdown_anysegwit` is negotiated: + * 3. if (and only if) `option_shutdown_anysegwit` is negotiated: * * `OP_1` through `OP_16` inclusive, followed by a single * push of 2 to 40 bytes * (witness program versions 1 through 16) @@ -47,11 +47,16 @@ static bool is_valid_witnessprog(const u8 *scriptpubkey) } bool valid_shutdown_scriptpubkey(const u8 *scriptpubkey, - bool anysegwit) + bool anysegwit, + bool anchors) { - return is_p2pkh(scriptpubkey, NULL) - || is_p2sh(scriptpubkey, NULL) - || is_p2wpkh(scriptpubkey, NULL) + if (!anchors) { + if (is_p2pkh(scriptpubkey, NULL) + || is_p2sh(scriptpubkey, NULL)) + return true; + } + + return is_p2wpkh(scriptpubkey, NULL) || is_p2wsh(scriptpubkey, NULL) || (anysegwit && is_valid_witnessprog(scriptpubkey)); } diff --git a/common/shutdown_scriptpubkey.h b/common/shutdown_scriptpubkey.h index 90189f75b9d0..e8c1fac1c26b 100644 --- a/common/shutdown_scriptpubkey.h +++ b/common/shutdown_scriptpubkey.h @@ -5,21 +5,19 @@ /* BOLT #2: * - * 1. `OP_DUP` `OP_HASH160` `20` 20-bytes `OP_EQUALVERIFY` `OP_CHECKSIG` - * (pay to pubkey hash), OR - * 2. `OP_HASH160` `20` 20-bytes `OP_EQUAL` (pay to script hash), OR - * 3. `OP_0` `20` 20-bytes (version 0 pay to witness pubkey hash), OR - * 4. `OP_0` `32` 32-bytes (version 0 pay to witness script hash), OR - * 5. if (and only if) `option_shutdown_anysegwit` is negotiated: + * 1. `OP_0` `20` 20-bytes (version 0 pay to witness pubkey hash), OR + * 2. `OP_0` `32` 32-bytes (version 0 pay to witness script hash), OR + * 3. if (and only if) `option_shutdown_anysegwit` is negotiated: * * `OP_1` through `OP_16` inclusive, followed by a single push of 2 to 40 bytes * (witness program versions 1 through 16) * * A receiving node: *... * - if the `scriptpubkey` is not in one of the above forms: - * - SHOULD fail the connection. + * - SHOULD send a `warning` */ bool valid_shutdown_scriptpubkey(const u8 *scriptpubkey, - bool anysegwit); + bool anysegwit, + bool anchors); #endif /* LIGHTNING_COMMON_SHUTDOWN_SCRIPTPUBKEY_H */ diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 5f05818d5bd7..5d8fc6f660ec 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -25,7 +25,7 @@ If *unilateraltimeout* is zero, then the **close** command will wait indefinitely until the peer is online and can negotiate a mutual close. The default is 2 days (172800 seconds). -The *destination* can be of any Bitcoin accepted type, including bech32. +The *destination* can be of any Bitcoin bech32 type. If it isn't specified, the default is a c-lightning wallet address. If the peer hasn't offered the `option_shutdown_anysegwit` feature, then taproot addresses (or other v1+ segwit) are not allowed. Tell your diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index b6c1d4353b37..a46b47eac408 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -275,6 +275,12 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg) bool anysegwit = feature_negotiated(ld->our_features, channel->peer->their_features, OPT_SHUTDOWN_ANYSEGWIT); + bool anchors = feature_negotiated(ld->our_features, + channel->peer->their_features, + OPT_ANCHOR_OUTPUTS) + || feature_negotiated(ld->our_features, + channel->peer->their_features, + OPT_ANCHORS_ZERO_FEE_HTLC_TX); if (!fromwire_channeld_got_shutdown(channel, msg, &scriptpubkey, &wrong_funding)) { @@ -287,7 +293,7 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg) tal_free(channel->shutdown_scriptpubkey[REMOTE]); channel->shutdown_scriptpubkey[REMOTE] = scriptpubkey; - if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit)) { + if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit, anchors)) { channel_fail_permanent(channel, REASON_PROTOCOL, "Bad shutdown scriptpubkey %s", diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index b11d05f3fc48..33589e28a469 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -684,11 +685,11 @@ static struct command_result *json_close(struct command *cmd, channel->peer->their_features, OPT_SHUTDOWN_ANYSEGWIT); if (!valid_shutdown_scriptpubkey(channel->shutdown_scriptpubkey[LOCAL], - anysegwit)) { + anysegwit, !deprecated_apis)) { /* Explicit check for future segwits. */ if (!anysegwit && valid_shutdown_scriptpubkey(channel->shutdown_scriptpubkey - [LOCAL], true)) { + [LOCAL], true, !deprecated_apis)) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Peer does not allow v1+ shutdown addresses"); } diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 6151984ad9ca..6eaecd098278 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1289,6 +1289,12 @@ static void handle_peer_wants_to_close(struct subd *dualopend, bool anysegwit = feature_negotiated(ld->our_features, channel->peer->their_features, OPT_SHUTDOWN_ANYSEGWIT); + bool anchors = feature_negotiated(ld->our_features, + channel->peer->their_features, + OPT_ANCHOR_OUTPUTS) + || feature_negotiated(ld->our_features, + channel->peer->their_features, + OPT_ANCHORS_ZERO_FEE_HTLC_TX); /* We shouldn't get this message while we're waiting to finish */ if (channel_unsaved(channel)) { @@ -1320,7 +1326,7 @@ static void handle_peer_wants_to_close(struct subd *dualopend, * - if the `scriptpubkey` is not in one of the above forms: * - SHOULD fail the connection. */ - if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit)) { + if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit, anchors)) { channel_fail_permanent(channel, REASON_PROTOCOL, "Bad shutdown scriptpubkey %s", diff --git a/openingd/openingd.c b/openingd/openingd.c index 87bdbf25d56a..04807cd5d2b6 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -276,6 +276,12 @@ static void set_remote_upfront_shutdown(struct state *state, bool anysegwit = feature_negotiated(state->our_features, state->their_features, OPT_SHUTDOWN_ANYSEGWIT); + bool anchors = feature_negotiated(state->our_features, + state->their_features, + OPT_ANCHOR_OUTPUTS) + || feature_negotiated(state->our_features, + state->their_features, + OPT_ANCHORS_ZERO_FEE_HTLC_TX); /* BOLT #2: * @@ -291,7 +297,7 @@ static void set_remote_upfront_shutdown(struct state *state, = tal_steal(state, shutdown_scriptpubkey); if (shutdown_scriptpubkey - && !valid_shutdown_scriptpubkey(shutdown_scriptpubkey, anysegwit)) + && !valid_shutdown_scriptpubkey(shutdown_scriptpubkey, anysegwit, anchors)) peer_failed_err(state->pps, &state->channel_id, "Unacceptable upfront_shutdown_script %s", diff --git a/tests/test_closing.py b/tests/test_closing.py index 62e3fdda6c73..0013cde1529f 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3194,7 +3194,8 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): # this test, so l1 reports the error as a warning! l1 = node_factory.get_node(start=False, allow_warning=True) # Insist on upfront script we're not going to match. - l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = "76a91404b61f7dc1ea0dc99424464cc4064dc564d91e8988ac" + # '0014' + l1.rpc.call('dev-listaddrs', [10])['addresses'][-1]['bech32_redeemscript'] + l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = "00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8" l1.start() l2 = node_factory.get_node() @@ -3205,7 +3206,7 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): fut = executor.submit(l1.rpc.close, l2.info['id']) # l2 will close unilaterally when it dislikes shutdown script. - l1.daemon.wait_for_log(r'scriptpubkey .* is not as agreed upfront \(76a91404b61f7dc1ea0dc99424464cc4064dc564d91e8988ac\)') + l1.daemon.wait_for_log(r'scriptpubkey .* is not as agreed upfront \(00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8\)') # Clear channel. wait_for(lambda: len(bitcoind.rpc.getrawmempool()) != 0) @@ -3221,7 +3222,7 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): l2.rpc.close(l1.info['id']) # l2 will close unilaterally when it dislikes shutdown script. - l1.daemon.wait_for_log(r'scriptpubkey .* is not as agreed upfront \(76a91404b61f7dc1ea0dc99424464cc4064dc564d91e8988ac\)') + l1.daemon.wait_for_log(r'scriptpubkey .* is not as agreed upfront \(00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8\)') # Clear channel. wait_for(lambda: len(bitcoind.rpc.getrawmempool()) != 0) From 2526e804f7e477cc28411749a286624d3f2c59c6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0608/1530] doc: big BOLT update to incorporate warnings language. We do this (send warnings) in almost all cases anyway, so mainly this is a textual update, but there are some changes: 1. Send ERROR not WARNING if they send a malformed commitment secret. 2. Send WARNING not ERROR if they get the shutdown_scriptpubkey wrong (vs upfront) 3. Send WARNING not ERROR if they send a bad shutdown_scriptpubkey (e.g. p2pkh in future) 4. Rename some vars 'err' to 'warn' to make it clear we send a warning. This means test_option_upfront_shutdown_script can be made reliable, too, and it now warns and doesn't automatically close channel. Signed-off-by: Rusty Russell --- Makefile | 2 +- channeld/channeld.c | 48 ++++++++++++++----------- channeld/full_channel.c | 27 +++++++++----- closingd/closingd.c | 3 +- common/decode_array.c | 12 ++++--- common/features.c | 3 +- common/ping.c | 1 - common/read_peer_msg.c | 27 ++++++++------ common/wireaddr.h | 5 +-- connectd/connectd.c | 2 +- connectd/multiplex.c | 1 - connectd/peer_exchange_initmsg.c | 2 +- gossipd/queries.c | 9 +++-- gossipd/routing.c | 54 +++++++++++++++------------- lightningd/channel_control.c | 29 +++++++++++---- lightningd/dual_open_control.c | 19 +++++++--- lightningd/peer_control.c | 18 ++++++---- lightningd/peer_htlcs.c | 8 +++-- openingd/dualopend.c | 15 ++++---- openingd/openingd.c | 8 ++--- tests/test_closing.py | 29 ++++++--------- wire/extracted_peer_02_warning.patch | 13 ------- 22 files changed, 191 insertions(+), 144 deletions(-) delete mode 100644 wire/extracted_peer_02_warning.patch diff --git a/Makefile b/Makefile index d186e8eb530f..a61d2b684019 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../lightning-rfc/ -DEFAULT_BOLTVERSION := c876dac2b5038f6499154d0a739240b6ff5db70d +DEFAULT_BOLTVERSION := 93909f67f6a48ee3f155a6224c182e612dd5f187 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/channeld/channeld.c b/channeld/channeld.c index ddce25310914..24c41b9d108d 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -728,7 +728,8 @@ static void handle_peer_feechange(struct peer *peer, const u8 *msg) * A receiving node: *... * - if the sender is not responsible for paying the Bitcoin fee: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (peer->channel->opener != REMOTE) peer_failed_warn(peer->pps, &peer->channel_id, @@ -742,7 +743,8 @@ static void handle_peer_feechange(struct peer *peer, const u8 *msg) * A receiving node: * - if the `update_fee` is too low for timely processing, OR is * unreasonably large: - * - SHOULD fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!feerate_same_or_better(peer->channel, feerate, peer->feerate_min, peer->feerate_max)) @@ -757,7 +759,8 @@ static void handle_peer_feechange(struct peer *peer, const u8 *msg) * * - if the sender cannot afford the new fee rate on the receiving * node's current commitment transaction: - * - SHOULD fail the channel, + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. * - but MAY delay this check until the `update_fee` is committed. */ if (!channel_update_feerate(peer->channel, feerate)) @@ -1627,7 +1630,8 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) * - once all pending updates are applied: * - if `signature` is not valid for its local commitment transaction * OR non-compliant with LOW-S-standard rule...: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!check_tx_sig(txs[0], 0, NULL, funding_wscript, &peer->channel->funding_pubkey[REMOTE], &commit_sig)) { @@ -1651,7 +1655,8 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) *... * - if `num_htlcs` is not equal to the number of HTLC outputs in the * local commitment transaction: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (tal_count(htlc_sigs) != tal_count(txs) - 1) peer_failed_warn(peer->pps, &peer->channel_id, @@ -1662,7 +1667,8 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) * * - if any `htlc_signature` is not valid for the corresponding HTLC * transaction OR non-compliant with LOW-S-standard rule...: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ for (i = 0; i < tal_count(htlc_sigs); i++) { u8 *wscript; @@ -1813,13 +1819,13 @@ static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg) * A receiving node: * - if `per_commitment_secret` is not a valid secret key or does not * generate the previous `per_commitment_point`: - * - MUST fail the channel. + * - MUST send an `error` and fail the channel. */ memcpy(&privkey, &old_commit_secret, sizeof(privkey)); if (!pubkey_from_privkey(&privkey, &per_commit_point)) { - peer_failed_warn(peer->pps, &peer->channel_id, - "Bad privkey %s", - type_to_string(msg, struct privkey, &privkey)); + peer_failed_err(peer->pps, &peer->channel_id, + "Bad privkey %s", + type_to_string(msg, struct privkey, &privkey)); } if (!pubkey_eq(&per_commit_point, &peer->old_remote_per_commit)) { peer_failed_err(peer->pps, &peer->channel_id, @@ -1957,7 +1963,8 @@ static void handle_peer_fail_malformed_htlc(struct peer *peer, const u8 *msg) * * - if the `BADONION` bit in `failure_code` is not set for * `update_fail_malformed_htlc`: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!(failure_code & BADONION)) { peer_failed_warn(peer->pps, &peer->channel_id, @@ -2011,6 +2018,7 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) * feature, and the receiving node received a non-zero-length * `shutdown_scriptpubkey` in `open_channel` or `accept_channel`, and * that `shutdown_scriptpubkey` is not equal to `scriptpubkey`: + * - MAY send a `warning`. * - MUST fail the connection. */ /* openingd only sets this if feature was negotiated at opening. */ @@ -2018,10 +2026,10 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) && !memeq(scriptpubkey, tal_count(scriptpubkey), peer->remote_upfront_shutdown_script, tal_count(peer->remote_upfront_shutdown_script))) - peer_failed_err(peer->pps, &peer->channel_id, - "scriptpubkey %s is not as agreed upfront (%s)", - tal_hex(peer, scriptpubkey), - tal_hex(peer, peer->remote_upfront_shutdown_script)); + peer_failed_warn(peer->pps, &peer->channel_id, + "scriptpubkey %s is not as agreed upfront (%s)", + tal_hex(peer, scriptpubkey), + tal_hex(peer, peer->remote_upfront_shutdown_script)); /* We only accept an wrong_funding if: * 1. It was negotiated. @@ -2528,12 +2536,12 @@ static void check_future_dataloss_fields(struct peer *peer, * commitment transaction: * ... * - if `your_last_per_commitment_secret` does not match the expected values: - * - SHOULD fail the channel. + * - SHOULD send an `error` and fail the channel. * - otherwise, if it supports `option_data_loss_protect`: *... * - otherwise (`your_last_per_commitment_secret` or * `my_current_per_commitment_point` do not match the expected values): - * - SHOULD fail the channel. + * - SHOULD send an `error` and fail the channel. */ static void check_current_dataloss_fields(struct peer *peer, u64 next_revocation_number, @@ -2950,10 +2958,10 @@ static void peer_reconnect(struct peer *peer, * - if `next_revocation_number` is not equal to 1 greater * than the commitment number of the last `revoke_and_ack` the * receiving node has sent: - * - SHOULD fail the channel. + * - SHOULD send an `error` and fail the channel. * - if it has not sent `revoke_and_ack`, AND * `next_revocation_number` is not equal to 0: - * - SHOULD fail the channel. + * - SHOULD send an `error` and fail the channel. */ if (next_revocation_number == peer->next_index[LOCAL] - 2) { /* Don't try to retransmit revocation index -1! */ @@ -3021,7 +3029,7 @@ static void peer_reconnect(struct peer *peer, * - if `next_commitment_number` is not 1 greater than the * commitment number of the last `commitment_signed` message the * receiving node has sent: - * - SHOULD fail the channel. + * - SHOULD send an `error` and fail the channel. */ } else if (next_commitment_number != peer->next_index[REMOTE]) peer_failed_err(peer->pps, diff --git a/channeld/full_channel.c b/channeld/full_channel.c index ee7140e2b75d..5c7102b24c61 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -559,7 +559,8 @@ static enum channel_add_err add_htlc(struct channel *channel, *... * - if sending node sets `cltv_expiry` to greater or equal to * 500000000: - * - SHOULD fail the channel. + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!blocks_to_abs_locktime(cltv_expiry, &htlc->expiry)) { return CHANNEL_ERR_INVALID_EXPIRY; @@ -584,7 +585,8 @@ static enum channel_add_err add_htlc(struct channel *channel, * A receiving node: * - receiving an `amount_msat` equal to 0, OR less than its own * `htlc_minimum_msat`: - * - SHOULD fail the channel. + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (amount_msat_eq(htlc->amount, AMOUNT_MSAT(0))) { return CHANNEL_ERR_HTLC_BELOW_MINIMUM; @@ -652,7 +654,8 @@ static enum channel_add_err add_htlc(struct channel *channel, * - if a sending node... adds more than receiver * `max_htlc_value_in_flight_msat` worth of offered HTLCs to its * local commitment transaction: - * - SHOULD fail the channel. + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ /* We don't enforce this for channel_force_htlcs: some might already @@ -670,7 +673,8 @@ static enum channel_add_err add_htlc(struct channel *channel, * - receiving an `amount_msat` that the sending node cannot afford at * the current `feerate_per_kw` (while maintaining its channel * reserve and any `to_local_anchor` and `to_remote_anchor` costs): - * - SHOULD fail the channel. + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (enforce_aggregate_limits) { struct amount_msat remainder; @@ -894,7 +898,8 @@ enum channel_remove_err channel_fulfill_htlc(struct channel *channel, * * - if the `payment_preimage` value in `update_fulfill_htlc` * doesn't SHA256 hash to the corresponding HTLC `payment_hash`: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!sha256_eq(&hash, &htlc->rhash)) return CHANNEL_ERR_BAD_PREIMAGE; @@ -905,7 +910,8 @@ enum channel_remove_err channel_fulfill_htlc(struct channel *channel, * * - if the `id` does not correspond to an HTLC in its current * commitment transaction: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!htlc_has(htlc, HTLC_FLAG(!htlc_owner(htlc), HTLC_F_COMMITTED))) { status_unusual("channel_fulfill_htlc: %"PRIu64" in state %s", @@ -957,7 +963,8 @@ enum channel_remove_err channel_fail_htlc(struct channel *channel, * A receiving node: * - if the `id` does not correspond to an HTLC in its current * commitment transaction: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!htlc_has(htlc, HTLC_FLAG(!htlc_owner(htlc), HTLC_F_COMMITTED))) { status_unusual("channel_fail_htlc: %"PRIu64" in state %s", @@ -1145,7 +1152,8 @@ static int change_htlcs(struct channel *channel, *... * - if the sender cannot afford the new fee rate on the receiving node's * current commitment transaction: - * - SHOULD fail the channel, + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ u32 approx_max_feerate(const struct channel *channel) { @@ -1257,7 +1265,8 @@ bool can_opener_afford_feerate(const struct channel *channel, u32 feerate_per_kw * * - if the sender cannot afford the new fee rate on the receiving * node's current commitment transaction: - * - SHOULD fail the channel + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ /* Note: sender == opener */ diff --git a/closingd/closingd.c b/closingd/closingd.c index 41349d80df97..e2a8fcbffe59 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -291,7 +291,8 @@ receive_offer(struct per_peer_state *pps, * - if the `signature` is not valid for either variant of closing transaction * specified in [BOLT #3](03-transactions.md#closing-transaction) * OR non-compliant with LOW-S-standard rule...: - * - MUST fail the connection. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ tx = close_tx(tmpctx, chainparams, pps, channel_id, local_wallet_index, diff --git a/common/decode_array.c b/common/decode_array.c index 8093282b7b16..7296d072a687 100644 --- a/common/decode_array.c +++ b/common/decode_array.c @@ -33,10 +33,12 @@ struct short_channel_id *decode_short_ids(const tal_t *ctx, const u8 *encoded) * The receiver: * - if the first byte of `encoded_short_ids` is not a known encoding * type: - * - MAY fail the connection + * - MAY send a `warning`. + * - MAY close the connection. * - if `encoded_short_ids` does not decode into a whole number of * `short_channel_id`: - * - MAY fail the connection + * - MAY send a `warning`. + * - MAY close the connection. */ type = fromwire_u8(&encoded, &max); switch (type) { @@ -75,10 +77,12 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx, *... * - if the incoming message includes `query_short_channel_ids_tlvs`: * - if `encoding_type` is not a known encoding type: - * - MAY fail the connection + * - MAY send a `warning`. + * - MAY close the connection. * - if `encoded_query_flags` does not decode to exactly one flag per * `short_channel_id`: - * - MAY fail the connection. + * - MAY send a `warning`. + * - MAY close the connection. */ switch (qf->encoding_type) { case ARR_ZLIB: diff --git a/common/features.c b/common/features.c index 360946d7429d..777afbe3c8d7 100644 --- a/common/features.c +++ b/common/features.c @@ -123,7 +123,8 @@ static const struct dependency feature_deps[] = { * `option_anchor_outputs` | ... | ... | `option_static_remotekey` */ { OPT_ANCHOR_OUTPUTS, OPT_STATIC_REMOTEKEY }, - /* BOLT #9: + /* FIXME: This dep was added later! */ + /* BOLT-master #9: * Name | Description | Context | Dependencies | *... * `option_anchors_zero_fee_htlc_tx` | ... | ... | `option_static_remotekey` diff --git a/common/ping.c b/common/ping.c index 9ff545ec90b4..4a38ce19d9e9 100644 --- a/common/ping.c +++ b/common/ping.c @@ -16,7 +16,6 @@ bool check_ping_make_pong(const tal_t *ctx, const u8 *ping, u8 **pong) /* BOLT #1: * * A node receiving a `ping` message: - *... * - if `num_pong_bytes` is less than 65532: * - MUST respond by sending a `pong` message, with `byteslen` equal * to `num_pong_bytes`. diff --git a/common/read_peer_msg.c b/common/read_peer_msg.c index fcf403f91799..d61abfd53f56 100644 --- a/common/read_peer_msg.c +++ b/common/read_peer_msg.c @@ -33,17 +33,24 @@ bool is_peer_error(const tal_t *ctx, const u8 *msg, * 0 (i.e. all bytes are 0), in which case it refers to all channels. * ... * The receiving node: - * - upon receiving `error`: - * - MUST fail the channel referred to by the error message, if that - * channel is with the sending node. - * - if no existing channel is referred to by the message: - * - MUST ignore the message. + * - upon receiving `error`: + * - if `channel_id` is all zero: + * - MUST fail all channels with the sending node. + * - otherwise: + * - MUST fail the channel referred to by `channel_id`, if that channel is with the sending node. + * - upon receiving `warning`: + * - SHOULD log the message for later diagnosis. + * - MAY disconnect. + * - MAY reconnect after some delay to retry. + * - MAY attempt `shutdown` if permitted at this point. + * - if no existing channel is referred to by `channel_id`: + * - MUST ignore the message. */ - /* FIXME: The spec changed, so for *errors* all 0 is not special. - * But old gossipd would send these, so we turn them into warnings */ - if (channel_id_is_all(&err_chanid)) - *warning = true; - else if (!channel_id_eq(&err_chanid, channel_id)) + + /* FIXME: We don't actually free all channels at once, they'll + * have to error each in turn. */ + if (!channel_id_is_all(&err_chanid) + && !channel_id_eq(&err_chanid, channel_id)) *desc = tal_free(*desc); return true; diff --git a/common/wireaddr.h b/common/wireaddr.h index acdb2e4feda0..783c5ba2d18f 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -26,10 +26,7 @@ struct sockaddr_un; * * * `1`: ipv4; data = `[4:ipv4_addr][2:port]` (length 6) * * `2`: ipv6; data = `[16:ipv6_addr][2:port]` (length 18) - * * `3`: Tor v2 onion service; data = `[10:onion_addr][2:port]` (length 12) - * * version 2 onion service addresses; Encodes an 80-bit, truncated `SHA-1` hash - * of a 1024-bit `RSA` public key for the onion service (a.k.a. Tor - * hidden service). + * * `3`: Deprecated (length 12). Used to contain Tor v2 onion services. * * `4`: Tor v3 onion service; data = `[35:onion_addr][2:port]` (length 37) * * version 3 ([prop224](https://gitweb.torproject.org/torspec.git/tree/proposals/224-rend-spec-ng.txt)) * onion service addresses; Encodes: diff --git a/connectd/connectd.c b/connectd/connectd.c index 0e3781f6555e..3add35b3f980 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -364,7 +364,7 @@ struct io_plan *peer_connected(struct io_conn *conn, * - upon receiving unknown _odd_ feature bits that are non-zero: * - MUST ignore the bit. * - upon receiving unknown _even_ feature bits that are non-zero: - * - MUST fail the connection. + * - MUST close the connection. */ unsup = features_unsupported(daemon->our_features, their_features, INIT_FEATURE); diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 71664438f6ec..5e71dbe940b9 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -1225,7 +1225,6 @@ void send_manual_ping(struct daemon *daemon, const u8 *msg) /* BOLT #1: * * A node receiving a `ping` message: - *... * - if `num_pong_bytes` is less than 65532: * - MUST respond by sending a `pong` message, with `byteslen` equal * to `num_pong_bytes`. diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index 3f0df9946458..eea73188a52c 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -75,7 +75,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, * The receiving node: * ... * - upon receiving `networks` containing no common chains - * - MAY fail the connection. + * - MAY close the connection. */ if (tlvs->networks) { if (!contains_common_chain(tlvs->networks)) { diff --git a/gossipd/queries.c b/gossipd/queries.c index 0ddb96bd29e0..de7d02fd6fed 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -258,7 +258,8 @@ const u8 *handle_query_short_channel_ids(struct peer *peer, const u8 *msg) * - if the incoming message includes * `query_short_channel_ids_tlvs`: * - if `encoding_type` is not a known encoding type: - * - MAY fail the connection + * - MAY send a `warning`. + * - MAY close the connection. */ flags = decode_scid_query_flags(tmpctx, tlvs->query_flags); if (!flags) { @@ -288,7 +289,8 @@ const u8 *handle_query_short_channel_ids(struct peer *peer, const u8 *msg) * - if it has not sent `reply_short_channel_ids_end` to a * previously received `query_short_channel_ids` from this * sender: - * - MAY fail the connection. + * - MAY send a `warning`. + * - MAY close the connection. */ if (peer->scid_queries || peer->scid_query_nodes) { return towire_warningfmt(peer, NULL, @@ -308,7 +310,8 @@ const u8 *handle_query_short_channel_ids(struct peer *peer, const u8 *msg) *... * - if `encoded_query_flags` does not decode to exactly one flag per * `short_channel_id`: - * - MAY fail the connection. + * - MAY send a `warning`. + * - MAY close the connection. */ if (!flags) { /* Pretend they asked for everything. */ diff --git a/gossipd/routing.c b/gossipd/routing.c index 09d866cf5756..abe1d7a49486 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -886,7 +886,7 @@ u8 *handle_channel_announcement(struct routing_state *rstate, { struct pending_cannouncement *pending; struct bitcoin_blkid chain_hash; - u8 *features, *err; + u8 *features, *warn; secp256k1_ecdsa_signature node_signature_1, node_signature_2; secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; struct chan *chan; @@ -911,7 +911,7 @@ u8 *handle_channel_announcement(struct routing_state *rstate, &pending->node_id_2, &pending->bitcoin_key_1, &pending->bitcoin_key_2)) { - err = towire_warningfmt(rstate, NULL, + warn = towire_warningfmt(rstate, NULL, "Malformed channel_announcement %s", tal_hex(pending, pending->announce)); goto malformed; @@ -991,7 +991,7 @@ u8 *handle_channel_announcement(struct routing_state *rstate, } /* Note that if node_id_1 or node_id_2 are malformed, it's caught here */ - err = check_channel_announcement(rstate, + warn = check_channel_announcement(rstate, &pending->node_id_1, &pending->node_id_2, &pending->bitcoin_key_1, @@ -1001,13 +1001,15 @@ u8 *handle_channel_announcement(struct routing_state *rstate, &bitcoin_signature_1, &bitcoin_signature_2, pending->announce); - if (err) { + if (warn) { /* BOLT #7: * * - if `bitcoin_signature_1`, `bitcoin_signature_2`, * `node_signature_1` OR `node_signature_2` are invalid OR NOT * correct: - * - SHOULD fail the connection. + * - SHOULD send a `warning`. + * - MAY close the connection. + * - MUST ignore the message. */ goto malformed; } @@ -1049,7 +1051,7 @@ u8 *handle_channel_announcement(struct routing_state *rstate, malformed: tal_free(pending); *scid = NULL; - return err; + return warn; ignored: tal_free(pending); @@ -1458,7 +1460,7 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, struct bitcoin_blkid chain_hash; u8 direction; struct pending_cannouncement *pending; - u8 *err; + u8 *warn; serialized = tal_dup_talarr(tmpctx, u8, update); if (!fromwire_channel_update(serialized, &signature, @@ -1467,10 +1469,10 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, &channel_flags, &expiry, &htlc_minimum, &fee_base_msat, &fee_proportional_millionths)) { - err = towire_warningfmt(rstate, NULL, - "Malformed channel_update %s", - tal_hex(tmpctx, serialized)); - return err; + warn = towire_warningfmt(rstate, NULL, + "Malformed channel_update %s", + tal_hex(tmpctx, serialized)); + return warn; } direction = channel_flags & 0x1; @@ -1523,18 +1525,18 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, return NULL; } - err = check_channel_update(rstate, owner, &signature, serialized); - if (err) { + warn = check_channel_update(rstate, owner, &signature, serialized); + if (warn) { /* BOLT #7: * * - if `signature` is not a valid signature, using `node_id` * of the double-SHA256 of the entire message following the * `signature` field (including unknown fields following * `fee_proportional_millionths`): + * - SHOULD send a `warning` and close the connection. * - MUST NOT process the message further. - * - SHOULD fail the connection. */ - return err; + return warn; } routing_add_channel_update(rstate, take(serialized), 0, peer, force); @@ -1712,13 +1714,14 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, /* BOLT #7: * * - if `node_id` is NOT a valid compressed public key: - * - SHOULD fail the connection. + * - SHOULD send a `warning`. + * - MAY close the connection. * - MUST NOT process the message further. */ - u8 *err = towire_warningfmt(rstate, NULL, + u8 *warn = towire_warningfmt(rstate, NULL, "Malformed node_announcement %s", tal_hex(tmpctx, node_ann)); - return err; + return warn; } sha256_double(&hash, serialized + 66, tal_count(serialized) - 66); @@ -1731,10 +1734,10 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, * message following the `signature` field * (including unknown fields following * `fee_proportional_millionths`): - * - MUST NOT process the message further. - * - SHOULD fail the connection. + * - SHOULD send a `warning` and close the connection. + * - MUST NOT process the message further. */ - u8 *err = towire_warningfmt(rstate, NULL, + u8 *warn = towire_warningfmt(rstate, NULL, "Bad signature for %s hash %s" " on node_announcement %s", type_to_string(tmpctx, @@ -1744,7 +1747,7 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, struct sha256_double, &hash), tal_hex(tmpctx, node_ann)); - return err; + return warn; } wireaddrs = fromwire_wireaddr_array(tmpctx, addresses); @@ -1753,13 +1756,14 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, * * - if `addrlen` is insufficient to hold the address * descriptors of the known types: - * - SHOULD fail the connection. + * - SHOULD send a `warning`. + * - MAY close the connection. */ - u8 *err = towire_warningfmt(rstate, NULL, + u8 *warn = towire_warningfmt(rstate, NULL, "Malformed wireaddrs %s in %s.", tal_hex(tmpctx, wireaddrs), tal_hex(tmpctx, node_ann)); - return err; + return warn; } /* May still fail, if we don't know the node. */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index a46b47eac408..9c73949decb1 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -289,18 +290,32 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg) return; } - /* FIXME: Add to spec that we must allow repeated shutdown! */ - tal_free(channel->shutdown_scriptpubkey[REMOTE]); - channel->shutdown_scriptpubkey[REMOTE] = scriptpubkey; - + /* BOLT #2: + * A receiving node: + *... + * - if the `scriptpubkey` is not in one of the above forms: + * - SHOULD send a `warning`. + */ if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit, anchors)) { - channel_fail_permanent(channel, - REASON_PROTOCOL, - "Bad shutdown scriptpubkey %s", + u8 *warning = towire_warningfmt(NULL, + &channel->cid, + "Bad shutdown scriptpubkey %s", + tal_hex(tmpctx, scriptpubkey)); + + /* Get connectd to send warning, and then allow reconnect. */ + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, + &channel->peer->id, + warning))); + channel_fail_reconnect(channel, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); return; } + /* FIXME: Add to spec that we must allow repeated shutdown! */ + tal_free(channel->shutdown_scriptpubkey[REMOTE]); + channel->shutdown_scriptpubkey[REMOTE] = scriptpubkey; + /* If we weren't already shutting down, we are now */ if (channel->state != CHANNELD_SHUTTING_DOWN) channel_set_state(channel, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 6eaecd098278..d7620dfce82c 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -1324,13 +1325,21 @@ static void handle_peer_wants_to_close(struct subd *dualopend, * A receiving node: *... * - if the `scriptpubkey` is not in one of the above forms: - * - SHOULD fail the connection. + * - SHOULD send a `warning` */ if (!valid_shutdown_scriptpubkey(scriptpubkey, anysegwit, anchors)) { - channel_fail_permanent(channel, - REASON_PROTOCOL, - "Bad shutdown scriptpubkey %s", - tal_hex(channel, scriptpubkey)); + u8 *warning = towire_warningfmt(NULL, + &channel->cid, + "Bad shutdown scriptpubkey %s", + tal_hex(tmpctx, scriptpubkey)); + + /* Get connectd to send warning, and then allow reconnect. */ + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, + &channel->peer->id, + warning))); + channel_fail_reconnect(channel, "Bad shutdown scriptpubkey %s", + tal_hex(tmpctx, scriptpubkey)); return; } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 6bfd09f689dd..197ea9e4156e 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -329,24 +329,30 @@ void channel_errmsg(struct channel *channel, * * A sending node: *... - * - when `channel_id` is 0: - * - MUST fail all channels with the receiving node. - * - MUST close the connection. + * - when sending `error`: + * - MUST fail the channel(s) referred to by the error message. + * - MAY set `channel_id` to all zero to indicate all channels. */ /* FIXME: Close if it's an all-channels error sent or rcvd */ /* BOLT #1: * * A sending node: + *... * - when sending `error`: - * - MUST fail the channel referred to by the error message. + * - MUST fail the channel(s) referred to by the error message. + * - MAY set `channel_id` to all zero to indicate all channels. *... * The receiving node: * - upon receiving `error`: - * - MUST fail the channel referred to by the error message, - * if that channel is with the sending node. + * - if `channel_id` is all zero: + * - MUST fail all channels with the sending node. + * - otherwise: + * - MUST fail the channel referred to by `channel_id`, if that channel is with the + * sending node. */ + /* FIXME: We don't close all channels */ /* We should immediately forget the channel if we receive error during * CHANNELD_AWAITING_LOCKIN if we are fundee. */ if (!err_for_them && channel->opener == REMOTE diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 7933192b99ea..0f506c3b98f6 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1945,7 +1945,8 @@ static bool channel_added_their_htlc(struct channel *channel, /* BOLT #2: * * - receiving an `amount_msat` equal to 0, OR less than its own `htlc_minimum_msat`: - * - SHOULD fail the channel. + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (amount_msat_eq(added->amount, AMOUNT_MSAT(0)) || amount_msat_less(added->amount, channel->our_config.htlc_minimum)) { @@ -2280,7 +2281,8 @@ void peer_got_revoke(struct channel *channel, const u8 *msg) * * - if the `per_commitment_secret` was not generated by the protocol * in [BOLT #3](03-transactions.md#per-commitment-secret-requirements): - * - MAY fail the channel. + * - MAY send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!wallet_shachain_add_hash(ld->wallet, &channel->their_shachain, @@ -2488,6 +2490,7 @@ void htlcs_notify_new_block(struct lightningd *ld, u32 height) * * - if an HTLC which it offered is in either node's current * commitment transaction, AND is past this timeout deadline: + * - SHOULD send an `error` to the receiving peer (if connected). * - MUST fail the channel. */ /* FIXME: use db to look this up in one go (earliest deadline per-peer) */ @@ -2532,6 +2535,7 @@ void htlcs_notify_new_block(struct lightningd *ld, u32 height) *... * - if an HTLC it has fulfilled is in either node's current commitment * transaction, AND is past this fulfillment deadline: + * - SHOULD send an `error` to the offering peer (if connected). * - MUST fail the channel. */ do { diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 0f95e3645dc3..2bf7d60106c3 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1202,8 +1202,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) &err, &warning)) { /* BOLT #1: * - * - if no existing channel is referred to by the - * message: + * - if no existing channel is referred to by `channel_id`: * - MUST ignore the message. */ /* In this case, is_peer_error returns true, but sets @@ -1850,13 +1849,14 @@ static u8 *accepter_commits(struct state *state, * The recipient: * - if `signature` is incorrect OR non-compliant with LOW-S-standard * rule...: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!check_tx_sig(local_commit, 0, NULL, wscript, &state->their_funding_pubkey, &remote_sig)) { /* BOLT #1: * - * ### The `error` Message + * ### The `error` and `warning` Messages *... * - when failure was caused by an invalid signature check: * - SHOULD include the raw, hex-encoded transaction in reply @@ -2575,14 +2575,15 @@ static u8 *opener_commits(struct state *state, * The recipient: * - if `signature` is incorrect OR non-compliant with LOW-S-standard * rule...: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (!check_tx_sig(local_commit, 0, NULL, wscript, &state->their_funding_pubkey, &remote_sig)) { /* BOLT #1: * - * ### The `error` Message + * ### The `error` and `warning` Messages *... * - when failure was caused by an invalid signature check: * - SHOULD include the raw, hex-encoded transaction in reply @@ -3588,7 +3589,7 @@ static void do_reconnect_dance(struct state *state) /* BOLT #2: * - if it has not sent `revoke_and_ack`, AND * `next_revocation_number` is not equal to 0: - * - SHOULD fail the channel. + * - SHOULD send an `error` and fail the channel. */ /* It's possible that we've opened an outdated copy of the * database, and the peer is very much ahead of us. diff --git a/openingd/openingd.c b/openingd/openingd.c index 04807cd5d2b6..5a83ae1c1c9e 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -192,8 +192,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, &err, &warning)) { /* BOLT #1: * - * - if no existing channel is referred to by the - * message: + * - if no existing channel is referred to by `channel_id`: * - MUST ignore the message. */ /* In this case, is_peer_error returns true, but sets @@ -1107,7 +1106,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * The recipient: * - if `signature` is incorrect OR non-compliant with LOW-S-standard * rule...: - * - MUST fail the channel. + * - MUST send a `warning` and close the connection, or send an + * `error` and fail the channel. */ local_commit = initial_channel_tx(state, &wscript, state->channel, &state->first_per_commitment_point[LOCAL], @@ -1125,7 +1125,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &theirsig)) { /* BOLT #1: * - * ### The `error` Message + * ### The `error` and `warning` Messages *... * - when failure was caused by an invalid signature check: * - SHOULD include the raw, hex-encoded transaction in reply diff --git a/tests/test_closing.py b/tests/test_closing.py index 0013cde1529f..36c510b63cbd 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1,5 +1,4 @@ from fixtures import * # noqa: F401,F403 -from flaky import flaky from pyln.client import RpcError, Millisatoshi from shutil import copyfile from pyln.testing.utils import SLOW_MACHINE @@ -3186,31 +3185,27 @@ def test_shutdown(node_factory): l1.rpc.stop() -@flaky @pytest.mark.developer("needs to set upfront_shutdown_script") def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): - # There's a workaround in channeld, that it treats incoming errors - # before both sides are locked in as warnings; this happens in - # this test, so l1 reports the error as a warning! l1 = node_factory.get_node(start=False, allow_warning=True) # Insist on upfront script we're not going to match. # '0014' + l1.rpc.call('dev-listaddrs', [10])['addresses'][-1]['bech32_redeemscript'] l1.daemon.env["DEV_OPENINGD_UPFRONT_SHUTDOWN_SCRIPT"] = "00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8" l1.start() - l2 = node_factory.get_node() + l2 = node_factory.get_node(allow_warning=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, 1000000, False) - # This will block, as l12 will send an error but l2 will retry. + # This will block, as l1 will send a warning but l2 will retry. fut = executor.submit(l1.rpc.close, l2.info['id']) - # l2 will close unilaterally when it dislikes shutdown script. - l1.daemon.wait_for_log(r'scriptpubkey .* is not as agreed upfront \(00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8\)') + # l2 will send a warning when it dislikes shutdown script. + l1.daemon.wait_for_log(r'WARNING.*scriptpubkey .* is not as agreed upfront \(00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8\)') - # Clear channel. - wait_for(lambda: len(bitcoind.rpc.getrawmempool()) != 0) - bitcoind.generate_block(1) + # Close from l2's side and clear channel. + l2.rpc.close(l1.info['id'], unilateraltimeout=1) + bitcoind.generate_block(1, wait_for_mempool=1) fut.result(TIMEOUT) wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN']) wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN']) @@ -3219,14 +3214,12 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, 1000000, False) - l2.rpc.close(l1.info['id']) + l2.rpc.close(l1.info['id'], unilateraltimeout=5) - # l2 will close unilaterally when it dislikes shutdown script. - l1.daemon.wait_for_log(r'scriptpubkey .* is not as agreed upfront \(00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8\)') + # l2 will send warning unilaterally when it dislikes shutdown script. + l1.daemon.wait_for_log(r'WARNING.*scriptpubkey .* is not as agreed upfront \(00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8\)') - # Clear channel. - wait_for(lambda: len(bitcoind.rpc.getrawmempool()) != 0) - bitcoind.generate_block(1) + bitcoind.generate_block(1, wait_for_mempool=1) wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN', 'ONCHAIN']) wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN', 'ONCHAIN']) diff --git a/wire/extracted_peer_02_warning.patch b/wire/extracted_peer_02_warning.patch deleted file mode 100644 index 6dc9e7c5bdac..000000000000 --- a/wire/extracted_peer_02_warning.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- wire/peer_exp_wire.csv 2021-01-14 11:00:27.526336550 +1030 -+++ - 2021-01-21 15:31:37.071118999 +1030 -@@ -10,6 +10,10 @@ - msgdata,error,channel_id,channel_id, - msgdata,error,len,u16, - msgdata,error,data,byte,len -+msgtype,warning,1 -+msgdata,warning,channel_id,channel_id, -+msgdata,warning,len,u16, -+msgdata,warning,data,byte,len - msgtype,ping,18 - msgdata,ping,num_pong_bytes,u16, - msgdata,ping,byteslen,u16, From e01abf0b34182b54d86494169f72e313442e2925 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0609/1530] bolt11: support payment_metadata. Signed-off-by: Rusty Russell --- Makefile | 2 +- common/bolt11.c | 53 +++++++++++++++++++++++++-- common/bolt11.h | 3 ++ common/bolt11_json.c | 2 + common/features.c | 10 ++++- common/features.h | 2 + common/test/run-bolt11.c | 46 +++++++++++++++++++++++ devtools/bolt11-cli.c | 4 ++ doc/lightning-decode.7.md | 3 +- doc/lightning-decodepay.7.md | 3 +- doc/schemas/decode.schema.json | 6 +++ doc/schemas/decodepay.schema.json | 4 ++ lightningd/lightningd.c | 1 + tests/test_pay.py | 6 +++ wire/extracted_onion_01_offers.patch | 4 +- wire/extracted_onion_exp_enctlv.patch | 4 +- wire/onion_wire.csv | 2 + 17 files changed, 145 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index a61d2b684019..a835e78d8e54 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../lightning-rfc/ -DEFAULT_BOLTVERSION := 93909f67f6a48ee3f155a6224c182e612dd5f187 +DEFAULT_BOLTVERSION := f6c4d7604150986894bcb46d67c5c88680740b12 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/common/bolt11.c b/common/bolt11.c index b33ac132ec1c..a4cc1df49b04 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -516,6 +516,33 @@ static char *decode_9(struct bolt11 *b11, return NULL; } +/* BOLT #11: + * + * `m` (27): `data_length` variable. Additional metadata to attach to + * the payment. Note that the size of this field is limited by the + * maximum hop payload size. Long metadata fields reduce the maximum + * route length. + */ +static char *decode_m(struct bolt11 *b11, + struct hash_u5 *hu5, + u5 **data, size_t *data_len, + size_t data_length, + bool *have_m) +{ + size_t mlen = (data_length * 5) / 8; + + if (*have_m) + return unknown_field(b11, hu5, data, data_len, 'm', + data_length); + + b11->metadata = tal_arr(b11, u8, mlen); + pull_bits_certain(hu5, data, data_len, b11->metadata, + data_length * 5, false); + + *have_m = true; + return NULL; +} + struct bolt11 *new_bolt11(const tal_t *ctx, const struct amount_msat *msat TAKES) { @@ -535,6 +562,7 @@ struct bolt11 *new_bolt11(const tal_t *ctx, */ b11->min_final_cltv_expiry = 18; b11->payment_secret = NULL; + b11->metadata = NULL; if (msat) b11->msat = tal_dup(b11, struct amount_msat, msat); @@ -557,7 +585,7 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, struct bolt11 *b11 = new_bolt11(ctx, NULL); struct hash_u5 hu5; bool have_p = false, have_d = false, have_h = false, - have_x = false, have_c = false, have_s = false; + have_x = false, have_c = false, have_s = false, have_m = false; *have_n = false; b11->routes = tal_arr(b11, struct route_info *, 0); @@ -760,6 +788,10 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str, problem = decode_s(b11, &hu5, &data, &data_len, data_length, &have_s); break; + case 'm': + problem = decode_m(b11, &hu5, &data, &data_len, + data_length, &have_m); + break; default: unknown_field(b11, &hu5, &data, &data_len, bech32_charset[type], data_length); @@ -936,6 +968,11 @@ static void encode_p(u5 **data, const struct sha256 *hash) push_field(data, 'p', hash, 256); } +static void encode_m(u5 **data, const u8 *metadata) +{ + push_field(data, 'm', metadata, tal_bytelen(metadata) * CHAR_BIT); +} + static void encode_d(u5 **data, const char *description) { push_field(data, 'd', description, strlen(description) * CHAR_BIT); @@ -1011,13 +1048,20 @@ static void encode_r(u5 **data, const struct route_info *r) tal_free(rinfo); } -static void maybe_encode_9(u5 **data, const u8 *features) +static void maybe_encode_9(u5 **data, const u8 *features, + bool have_payment_metadata) { u5 *f5 = tal_arr(NULL, u5, 0); for (size_t i = 0; i < tal_count(features) * CHAR_BIT; i++) { if (!feature_is_set(features, i)) continue; + + /* Don't set option_payment_metadata unless we acually use it */ + if (!have_payment_metadata + && COMPULSORY_FEATURE(i) == OPT_PAYMENT_METADATA) + continue; + /* We expand it out so it makes a BE 5-bit/btye bitfield */ set_feature_bit(&f5, (i / 5) * 8 + (i % 5)); } @@ -1136,6 +1180,9 @@ char *bolt11_encode_(const tal_t *ctx, if (b11->expiry != DEFAULT_X) encode_x(&data, b11->expiry); + if (b11->metadata) + encode_m(&data, b11->metadata); + /* BOLT #11: * - MUST include one `c` field (`min_final_cltv_expiry`). */ @@ -1150,7 +1197,7 @@ char *bolt11_encode_(const tal_t *ctx, for (size_t i = 0; i < tal_count(b11->routes); i++) encode_r(&data, b11->routes[i]); - maybe_encode_9(&data, b11->features); + maybe_encode_9(&data, b11->features, b11->metadata != NULL); list_for_each(&b11->extra_fields, extra, list) if (!encode_extra(&data, extra)) diff --git a/common/bolt11.h b/common/bolt11.h index 3dea30b82f35..19b87f78fc8c 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -76,6 +76,9 @@ struct bolt11 { /* Features bitmap, if any. */ u8 *features; + /* Optional metadata to send with payment. */ + u8 *metadata; + struct list_head extra_fields; }; diff --git a/common/bolt11_json.c b/common/bolt11_json.c index 4fc1dacec925..b2280c47d157 100644 --- a/common/bolt11_json.c +++ b/common/bolt11_json.c @@ -66,6 +66,8 @@ void json_add_bolt11(struct json_stream *response, b11->payment_secret); if (b11->features) json_add_hex_talarr(response, "features", b11->features); + if (b11->metadata) + json_add_hex_talarr(response, "payment_metadata", b11->metadata); if (tal_count(b11->fallbacks)) { json_array_start(response, "fallbacks"); for (size_t i = 0; i < tal_count(b11->fallbacks); i++) diff --git a/common/features.c b/common/features.c index 777afbe3c8d7..e95d631e8964 100644 --- a/common/features.c +++ b/common/features.c @@ -97,6 +97,14 @@ static const struct feature_style feature_styles[] = { .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } }, + { OPT_PAYMENT_METADATA, + .copy_style = { [INIT_FEATURE] = FEATURE_DONT_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_DONT_REPRESENT, + /* Note: we don't actually set this in invoices, since + * we don't need to use it, but if we don't set it here + * we refuse to parse it. */ + [BOLT11_FEATURE] = FEATURE_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } }, }; struct dependency { @@ -416,7 +424,7 @@ const char *feature_name(const tal_t *ctx, size_t f) "option_provide_peer_backup", /* https://github.com/lightningnetwork/lightning-rfc/pull/881 */ NULL, NULL, - NULL, + "option_payment_metadata", NULL, /* 50/51 */ NULL, "option_keysend", diff --git a/common/features.h b/common/features.h index 1aa8ca82994e..3bea463565c8 100644 --- a/common/features.h +++ b/common/features.h @@ -119,6 +119,7 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); * | 20/21 | `option_anchor_outputs` |... IN ... * | 22/23 | `option_anchors_zero_fee_htlc_tx` |... IN ... * | 26/27 | `option_shutdown_anysegwit` |... IN ... + * | 48/49 | `option_payment_metadata` |... 9 ... */ #define OPT_PAYMENT_SECRET 14 #define OPT_BASIC_MPP 16 @@ -126,6 +127,7 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); #define OPT_ANCHOR_OUTPUTS 20 #define OPT_ANCHORS_ZERO_FEE_HTLC_TX 22 #define OPT_SHUTDOWN_ANYSEGWIT 26 +#define OPT_PAYMENT_METADATA 48 /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9: * | 28/29 | `option_dual_fund` | ... IN9 ... diff --git a/common/test/run-bolt11.c b/common/test/run-bolt11.c index 4ed5e8369fe3..488b848cdd62 100644 --- a/common/test/run-bolt11.c +++ b/common/test/run-bolt11.c @@ -564,6 +564,52 @@ int main(int argc, char *argv[]) memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret)); test_b11("lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q9qrsgqrvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqpu2v9wz", b11, NULL); + /* BOLT #11: + * > ### Please send 0.01 BTC with payment metadata 0x01fafaf0 + * > lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgq7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqpd05wjc + * + * Breakdown: + * + * * `lnbc`: prefix, Lightning on Bitcoin mainnet + * * `10m`: amount (10 milli-bitcoin) + * * `1`: Bech32 separator + * * `pvjluez`: timestamp (1496314658) + * * `p`: payment hash + * * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52) + * * `qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq`: payment hash 0001020304050607080900010203040506070809000102030405060708090102 + * * `d`: short description + * * `p9`: `data_length` (`p` = 1, `9` = 5; 1 * 32 + 5 == 37) + * * `wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2`: 'payment metadata inside' + * * `m`: metadata + * * `q8`: `data_length` (`q` = 0, `8` = 7; 0 * 32 + 7 == 7) + * * `q8a04uq`: 0x01fafaf0 + * * `s`: payment secret + * * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52) + * * `zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs`: 0x1111111111111111111111111111111111111111111111111111111111111111 + * * `9`: features + * * `q2`: `data_length` (`q` = 0, `2` = 10; 0 * 32 + 10 == 10) + * * `gqqqqqqsgq`: [b01000000000000000000000000000000000100000100000000] = 8 + 14 + 48 + * * `7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqp`: signature + * * `d05wjc`: Bech32 checksum + */ + msatoshi = AMOUNT_MSAT(1000000000); + b11 = new_bolt11(tmpctx, &msatoshi); + b11->chain = chainparams_for_network("bitcoin"); + b11->timestamp = 1496314658; + if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102", + strlen("0001020304050607080900010203040506070809000102030405060708090102"), + &b11->payment_hash, sizeof(b11->payment_hash))) + abort(); + b11->receiver_id = node; + b11->description = "payment metadata inside"; + b11->metadata = tal_hexdata(b11, "01fafaf0", strlen("01fafaf0")); + set_feature_bit(&b11->features, 8); + set_feature_bit(&b11->features, 14); + set_feature_bit(&b11->features, 48); + b11->payment_secret = tal(b11, struct secret); + memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret)); + test_b11("lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgq7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqpd05wjc", b11, NULL); + /* BOLT #11: * * > ### Bech32 checksum is invalid. diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index cb644baab72e..018d0e8af7f8 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -163,6 +163,10 @@ int main(int argc, char *argv[]) printf("\n"); } + if (b11->metadata) + printf("metadata: %s\n", + tal_hex(ctx, b11->metadata)); + list_for_each(&b11->extra_fields, extra, list) { char *data = tal_arr(ctx, char, tal_count(extra->data)+1); diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 81d8a62b9453..de4dc749d058 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -143,6 +143,7 @@ If **type** is "bolt11 invoice", and **valid** is *true*: - **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 + - **payment_metadata** (hex, optional): the payment_metadata to put in the payment - **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 @@ -180,4 +181,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d05b5fc1bf230b3bbd03e2023fb0c6bbefb700f7c3cfb43512da48dbce45f005) +[comment]: # ( SHA256STAMP:6ccf1b9195e64f897f65198a81c47bbd7e16387e4bdc74d624e2ec04a24e9873) diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index bbdc75c04892..300d80451737 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -29,6 +29,7 @@ On success, an object is returned, containing: - **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 +- **payment_metadata** (hex, optional): the payment_metadata to put in the payment - **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 @@ -69,4 +70,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d92e1197708fff40f8ad71ccec3c0d8122d8088da1803c02bb042b09dbf2ee33) +[comment]: # ( SHA256STAMP:17cb6c66c75e907f3a2583d702aec2fc6e5a7b6026d05a3ed9957304799c9aef) diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json index 808ef8f4313d..2c84e820bd54 100644 --- a/doc/schemas/decode.schema.json +++ b/doc/schemas/decode.schema.json @@ -746,6 +746,8 @@ ], "additionalProperties": false, "properties": { + "type": {}, + "valid": {}, "currency": { "type": "string", "description": "the BIP173 name for the currency" @@ -804,6 +806,10 @@ "type": "hex", "description": "the features bitmap for this invoice" }, + "payment_metadata": { + "type": "hex", + "description": "the payment_metadata to put in the payment" + }, "fallbacks": { "type": "array", "description": "onchain addresses", diff --git a/doc/schemas/decodepay.schema.json b/doc/schemas/decodepay.schema.json index 15fd12298657..0823f7afa454 100644 --- a/doc/schemas/decodepay.schema.json +++ b/doc/schemas/decodepay.schema.json @@ -70,6 +70,10 @@ "type": "hex", "description": "the features bitmap for this invoice" }, + "payment_metadata": { + "type": "hex", + "description": "the payment_metadata to put in the payment" + }, "fallbacks": { "type": "array", "description": "onchain addresses", diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index c55e861d2a2b..bb4a1ac44ad1 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -823,6 +823,7 @@ static struct feature_set *default_features(const tal_t *ctx) OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX), OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY), OPTIONAL_FEATURE(OPT_SHUTDOWN_ANYSEGWIT), + OPTIONAL_FEATURE(OPT_PAYMENT_METADATA), #if EXPERIMENTAL_FEATURES OPTIONAL_FEATURE(OPT_ANCHOR_OUTPUTS), OPTIONAL_FEATURE(OPT_QUIESCE), diff --git a/tests/test_pay.py b/tests/test_pay.py index 96a65f9716b1..f8682643c036 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5236,3 +5236,9 @@ def test_pay_manual_exclude(node_factory, bitcoind): # Exclude direct channel id with pytest.raises(RpcError, match=r'is not reachable directly and all routehints were unusable.'): l2.rpc.pay(inv, exclude=[scid23]) + + +def test_pay_bolt11_metadata(node_factory, bitcoind): + l1 = node_factory.get_node() + + l1.rpc.decode('lnbcrt10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqcqpjsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqrk6hdutpaetmm3afjn0vfczgeyv0cy739rr939kwd4h5j3khxcskhgf59eaqy8wyq82tsnaqc5y32ed4jg34jw7rmeva9u6kfhymawgptmy5f6') diff --git a/wire/extracted_onion_01_offers.patch b/wire/extracted_onion_01_offers.patch index 90c0cb6c41ac..898c1373df3d 100644 --- a/wire/extracted_onion_01_offers.patch +++ b/wire/extracted_onion_01_offers.patch @@ -1,9 +1,9 @@ --- wire/extracted_onion_wire_csv 2020-03-25 10:24:12.861645774 +1030 +++ - 2020-03-26 13:47:13.498294435 +1030 @@ -8,6 +8,30 @@ - tlvtype,tlv_payload,payment_data,8 - tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, + tlvtype,tlv_payload,payment_metadata,16 + tlvdata,tlv_payload,payment_metadata,payment_metadata,byte,... +tlvtype,obs2_onionmsg_payload,reply_path,2 +tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, +tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, diff --git a/wire/extracted_onion_exp_enctlv.patch b/wire/extracted_onion_exp_enctlv.patch index 1897193b8c8b..da51d82268bc 100644 --- a/wire/extracted_onion_exp_enctlv.patch +++ b/wire/extracted_onion_exp_enctlv.patch @@ -2,10 +2,12 @@ +++ - 2020-03-20 15:11:55.763880895 +1030 --- wire/onion_exp_wire.csv.~1~ 2021-11-17 10:56:59.947630815 +1030 +++ wire/onion_exp_wire.csv 2021-11-17 10:59:39.304581244 +1030 -@@ -8,6 +8,10 @@ +@@ -8,8 +8,12 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, + tlvtype,tlv_payload,payment_metadata,16 + tlvdata,tlv_payload,payment_metadata,payment_metadata,byte,... +tlvtype,tlv_payload,encrypted_recipient_data,10 +tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... +tlvtype,tlv_payload,blinding_point,12 diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index 28c58724d8b5..9f255bdaf017 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -8,6 +8,8 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, +tlvtype,tlv_payload,payment_metadata,16 +tlvdata,tlv_payload,payment_metadata,payment_metadata,byte,... tlvtype,obs2_onionmsg_payload,reply_path,2 tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, From 4718ee076c1ae001d2cb511c86341ae1a4025d3d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0610/1530] lightningd: metadata received support (log and decline). Signed-off-by: Rusty Russell --- common/onion.c | 7 +++++++ common/onion.h | 1 + lightningd/peer_htlcs.c | 31 +++++++++++++++++++++++++++++-- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/common/onion.c b/common/onion.c index 5fdcf757905a..5f3f71bc4c4a 100644 --- a/common/onion.c +++ b/common/onion.c @@ -241,6 +241,7 @@ struct onion_payload *onion_decode(const tal_t *ctx, p->amt_to_forward = fromwire_amount_msat(&cursor, &max); p->outgoing_cltv = fromwire_u32(&cursor, &max); p->payment_secret = NULL; + p->payment_metadata = NULL; p->blinding = NULL; /* We can't handle blinding with a legacy payload */ if (blinding) @@ -365,6 +366,12 @@ struct onion_payload *onion_decode(const tal_t *ctx, *p->total_msat = amount_msat(tlv->payment_data->total_msat); } + if (tlv->payment_metadata) + p->payment_metadata + = tal_dup_talarr(p, u8, tlv->payment_metadata); + else + p->payment_metadata = NULL; + p->tlv = tal_steal(p, tlv); return p; diff --git a/common/onion.h b/common/onion.h index 6f3045faf57c..828a6735df1c 100644 --- a/common/onion.h +++ b/common/onion.h @@ -19,6 +19,7 @@ struct onion_payload { struct amount_msat *total_msat; struct short_channel_id *forward_channel; struct secret *payment_secret; + u8 *payment_metadata; /* If blinding is set, blinding_ss is the shared secret.*/ struct pubkey *blinding; diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 0f506c3b98f6..763ce23a4fb1 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -363,7 +363,8 @@ static void handle_localpay(struct htlc_in *hin, struct amount_msat amt_to_forward, u32 outgoing_cltv_value, struct amount_msat total_msat, - const struct secret *payment_secret) + const struct secret *payment_secret, + const u8 *payment_metadata) { const u8 *failmsg; struct lightningd *ld = hin->key.channel->peer->ld; @@ -424,6 +425,27 @@ static void handle_localpay(struct htlc_in *hin, goto fail; } + /* We don't expect payment_metadata; reject here */ + if (payment_metadata) { + log_debug(hin->key.channel->log, + "Unexpected payment_metadata %s", + tal_hex(tmpctx, payment_metadata)); + /* BOLT #4: + * 1. type: PERM|22 (`invalid_onion_payload`) + * 2. data: + * * [`bigsize`:`type`] + * * [`u16`:`offset`] + * + * The decrypted onion per-hop payload was not understood by the processing node + * or is incomplete. If the failure can be narrowed down to a specific tlv type in + * the payload, the erring node may include that `type` and its byte `offset` in + * the decrypted byte stream. + */ + failmsg = towire_invalid_onion_payload(NULL, TLV_TLV_PAYLOAD_PAYMENT_METADATA, + /* FIXME: offset? */ 0); + goto fail; + } + htlc_set_add(ld, hin, total_msat, payment_secret); return; @@ -1033,6 +1055,10 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_add_secret(s, "payment_secret", p->payload->payment_secret); } + if (p->payload->payment_metadata) { + json_add_hex_talarr(s, "payment_metadata", + p->payload->payment_metadata); + } } json_add_hex_talarr(s, "next_onion", p->next_onion); json_add_secret(s, "shared_secret", hin->shared_secret); @@ -1082,7 +1108,8 @@ htlc_accepted_hook_final(struct htlc_accepted_hook_payload *request STEALS) request->payload->amt_to_forward, request->payload->outgoing_cltv, *request->payload->total_msat, - request->payload->payment_secret); + request->payload->payment_secret, + request->payload->payment_metadata); tal_free(request); } From 7f89763f9e46bbf9e242590fd1e509ca266338d9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0611/1530] sendpay: add payment_metadata argument. And document the missing arguments. Signed-off-by: Rusty Russell --- common/onion.c | 4 +++- common/onion.h | 5 +++-- contrib/pyln-client/pyln/client/lightning.py | 3 ++- devtools/onion.c | 2 +- doc/lightning-sendpay.7.md | 13 ++++++++++++- lightningd/pay.c | 12 ++++++++---- 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/common/onion.c b/common/onion.c index 5f3f71bc4c4a..45325e25ec17 100644 --- a/common/onion.c +++ b/common/onion.c @@ -71,7 +71,8 @@ u8 *onion_final_hop(const tal_t *ctx, struct amount_msat total_msat, const struct pubkey *blinding, const u8 *enctlv, - const struct secret *payment_secret) + const struct secret *payment_secret, + const u8 *payment_metadata) { struct tlv_tlv_payload *tlv = tlv_tlv_payload_new(tmpctx); struct tlv_tlv_payload_payment_data tlv_pdata; @@ -102,6 +103,7 @@ u8 *onion_final_hop(const tal_t *ctx, tlv_pdata.total_msat = total_msat.millisatoshis; /* Raw: TLV convert */ tlv->payment_data = &tlv_pdata; } + tlv->payment_metadata = cast_const(u8 *, payment_metadata); #if EXPERIMENTAL_FEATURES tlv->blinding_point = cast_const(struct pubkey *, blinding); tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); diff --git a/common/onion.h b/common/onion.h index 828a6735df1c..839fa5048f03 100644 --- a/common/onion.h +++ b/common/onion.h @@ -36,14 +36,15 @@ u8 *onion_nonfinal_hop(const tal_t *ctx, const struct pubkey *blinding, const u8 *enctlv); -/* Note that this can fail if we supply payment_secret and !use_tlv! */ +/* Note that this can fail if we supply payment_secret or payment_metadata and !use_tlv! */ u8 *onion_final_hop(const tal_t *ctx, struct amount_msat forward, u32 outgoing_cltv, struct amount_msat total_msat, const struct pubkey *blinding, const u8 *enctlv, - const struct secret *payment_secret); + const struct secret *payment_secret, + const u8 *payment_metadata); /** * onion_payload_length: measure payload length in decrypted onion. diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index f7567fcf5c4b..52e5c3fe34a9 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -1128,7 +1128,7 @@ def plugin_rescan(self): } return self.call("plugin", payload) - def sendpay(self, route, payment_hash, label=None, msatoshi=None, bolt11=None, payment_secret=None, partid=None, groupid=None): + def sendpay(self, route, payment_hash, label=None, msatoshi=None, bolt11=None, payment_secret=None, partid=None, groupid=None, payment_metadata=None): """ Send along {route} in return for preimage of {payment_hash}. """ @@ -1141,6 +1141,7 @@ def sendpay(self, route, payment_hash, label=None, msatoshi=None, bolt11=None, p "payment_secret": payment_secret, "partid": partid, "groupid": groupid, + "payment_metadata": payment_metadata, } return self.call("sendpay", payload) diff --git a/devtools/onion.c b/devtools/onion.c index f750bedd4454..82e5129cb6a6 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -80,7 +80,7 @@ static void do_generate(int argc, char **argv, take(onion_final_hop(NULL, amt, i, amt, NULL, NULL, - NULL))); + NULL, NULL))); else sphinx_add_hop(sp, &path[i], take(onion_nonfinal_hop(NULL, diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index c8d782154490..ec7aec712a12 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -5,7 +5,7 @@ SYNOPSIS -------- **sendpay** *route* *payment\_hash* [*label*] [*msatoshi*] -[*bolt11*] [*payment_secret*] [*partid*] +[*bolt11*] [*payment_secret*] [*partid*] [*localofferid*] [*groupid*] [*payment_metadata*] DESCRIPTION ----------- @@ -44,6 +44,16 @@ partial payments with the same *payment_hash*. The *msatoshi* amount *payment_hash* must be equal, and **sendpay** will fail if there are already *msatoshi* worth of payments pending. +The *localofferid* value indicates that this payment is being made for a local +send_invoice offer: this ensures that we only send a payment for a single-use +offer once. + +*groupid* allows you to attach a number which appears in **listsendpays** so +payments can be identified as part of a logical group. The *pay* plugin uses +this to identify one attempt at a MPP payment, for example. + +*payment_metadata* is placed in the final onion hop TLV. + Once a payment has succeeded, calls to **sendpay** with the same *payment\_hash* but a different *msatoshi* or destination will fail; this prevents accidental multiple payments. Calls to **sendpay** with @@ -94,6 +104,7 @@ The following error codes may occur: will be routing failure object. - 204: Failure along route; retry a different route. The *data* field of the error will be routing failure object. +- 212: *localofferid* refers to an invalid, or used, local offer. A routing failure object has the fields below: - *erring\_index*. The index of the node along the route that reported diff --git a/lightningd/pay.c b/lightningd/pay.c index 58463ee10da7..9e29f92d09ed 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1122,7 +1122,8 @@ send_payment(struct lightningd *ld, const char *label TAKES, const char *invstring TAKES, const struct sha256 *local_offer_id, - const struct secret *payment_secret) + const struct secret *payment_secret, + const u8 *payment_metadata) { unsigned int base_expiry; struct onionpacket *packet; @@ -1174,7 +1175,7 @@ send_payment(struct lightningd *ld, route[i].amount, base_expiry + route[i].delay, total_msat, route[i].blinding, route[i].enctlv, - payment_secret); + payment_secret, payment_metadata); if (!onion) { return command_fail(cmd, PAY_DESTINATION_PERM_FAIL, "Destination does not support" @@ -1422,7 +1423,8 @@ static struct command_result *json_sendpay(struct command *cmd, const char *invstring, *label; u64 *partid, *group; struct secret *payment_secret; - struct sha256 *local_offer_id = NULL; + struct sha256 *local_offer_id; + u8 *payment_metadata; /* For generating help, give new-style. */ if (!param(cmd, buffer, params, @@ -1436,6 +1438,7 @@ static struct command_result *json_sendpay(struct command *cmd, p_opt_def("partid", param_u64, &partid, 0), p_opt("localofferid", param_sha256, &local_offer_id), p_opt("groupid", param_u64, &group), + p_opt("payment_metadata", param_bin_from_hex, &payment_metadata), NULL)) return command_param_failed(); @@ -1485,7 +1488,8 @@ static struct command_result *json_sendpay(struct command *cmd, route, final_amount, msat ? *msat : final_amount, - label, invstring, local_offer_id, payment_secret); + label, invstring, local_offer_id, + payment_secret, payment_metadata); } static const struct json_command sendpay_command = { From 1c685ce3e8db2282e5a929729444ecc287d660b3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0612/1530] plugins/pay: send payment_metadata if provided in invoice. Signed-off-by: Rusty Russell Changelog-Added: Protocol: `pay` (and decode, etc) supports bolt11 payment_metadata a-la https://github.com/lightning/bolts/pull/912 --- plugins/keysend.c | 1 + plugins/libplugin-pay.c | 14 ++++++++++---- plugins/libplugin-pay.h | 3 +++ plugins/pay.c | 15 +++++++++++++++ tests/test_pay.py | 21 +++++++++++++++++++-- 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/plugins/keysend.c b/plugins/keysend.c index 7dff550b289a..22ae85e3b77e 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -179,6 +179,7 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, p->json_toks = params; p->destination = tal_steal(p, destination); p->payment_secret = NULL; + p->payment_metadata = NULL; p->amount = *msat; p->routes = tal_steal(p, hints); // 22 is the Rust-Lightning default and the highest minimum we know of. diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index f3f9d496d696..1c8779f411bf 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -1581,7 +1581,7 @@ static struct command_result *payment_createonion_success(struct command *cmd, /* Temporary serialization method for the tlv_payload.data until we rework the * API that is generated from the specs to use the setter/getter interface. */ static void tlvstream_set_tlv_payload_data(struct tlv_field **stream, - struct secret *payment_secret, + const struct secret *payment_secret, u64 total_msat) { u8 *ser = tal_arr(NULL, u8, 0); @@ -1596,7 +1596,8 @@ static void payment_add_hop_onion_payload(struct payment *p, struct route_hop *node, struct route_hop *next, bool final, - struct secret *payment_secret) + struct secret *payment_secret, + const u8 *payment_metadata) { struct createonion_request *cr = p->createonion_request; u32 cltv = p->start_block + next->delay + 1; @@ -1627,6 +1628,11 @@ static void payment_add_hop_onion_payload(struct payment *p, fields, payment_secret, root->amount.millisatoshis); /* Raw: TLV payload generation*/ } + if (payment_metadata != NULL) { + assert(final); + tlvstream_set_raw(fields, TLV_TLV_PAYLOAD_PAYMENT_METADATA, + payment_metadata, tal_bytelen(payment_metadata)); + } } static void payment_compute_onion_payloads(struct payment *p) @@ -1665,7 +1671,7 @@ static void payment_compute_onion_payloads(struct payment *p) * i+1 */ payment_add_hop_onion_payload(p, &cr->hops[i], &p->route[i], &p->route[i + 1], false, - NULL); + NULL, NULL); tal_append_fmt(&routetxt, "%s -> ", type_to_string(tmpctx, struct short_channel_id, &p->route[i].scid)); @@ -1675,7 +1681,7 @@ static void payment_compute_onion_payloads(struct payment *p) payment_add_hop_onion_payload( p, &cr->hops[hopcount - 1], &p->route[hopcount - 1], &p->route[hopcount - 1], true, - root->payment_secret); + root->payment_secret, root->payment_metadata); tal_append_fmt(&routetxt, "%s", type_to_string(tmpctx, struct short_channel_id, &p->route[hopcount - 1].scid)); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index e1f14da44f73..86aea238c464 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -180,6 +180,9 @@ struct payment { /* Payment secret, from the invoice if any. */ struct secret *payment_secret; + /* Payment metadata, from the invoice if any. */ + u8 *payment_metadata; + u64 groupid; u32 partid; u32 next_partid; diff --git a/plugins/pay.c b/plugins/pay.c index 7d08e1731e43..9dfedfc25e3f 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -108,6 +108,9 @@ struct pay_command { /* Payment secret, if specified by invoice. */ const char *payment_secret; + /* Payment metadata, if specified by invoice. */ + const char *payment_metadata; + /* Description, if any. */ const char *label; @@ -849,6 +852,8 @@ static struct command_result *getroute_done(struct command *cmd, json_add_string(req->js, "label", pc->label); if (pc->payment_secret) json_add_string(req->js, "payment_secret", pc->payment_secret); + if (pc->payment_metadata) + json_add_string(req->js, "payment_metadata", pc->payment_metadata); return send_outreq(cmd->plugin, req); } @@ -1368,6 +1373,11 @@ static struct command_result *json_pay(struct command *cmd, sizeof(*b11->payment_secret)); else pc->payment_secret = NULL; + if (b11->metadata) + pc->payment_metadata = tal_hex(pc, b11->metadata); + else + pc->payment_metadata = NULL; + /* We try first without using routehint */ pc->current_routehint = NULL; pc->routehints = filter_routehints(pc, b11->routes); @@ -2380,6 +2390,10 @@ static struct command_result *json_paymod(struct command *cmd, p->payment_hash = tal_dup(p, struct sha256, &b11->payment_hash); p->payment_secret = tal_dup_or_null(p, struct secret, b11->payment_secret); + if (b11->metadata) + p->payment_metadata = tal_dup_talarr(p, u8, b11->metadata); + else + p->payment_metadata = NULL; /* FIXME: libplugin-pay plays with this array, and there are many FIXMEs * about it. But it looks like a leak, so we suppress it here. */ p->routes = notleak_with_children(tal_steal(p, b11->routes)); @@ -2439,6 +2453,7 @@ static struct command_result *json_paymod(struct command *cmd, BUILD_ASSERT(sizeof(*p->payment_secret) == sizeof(merkle)); } + p->payment_metadata = NULL; p->routes = NULL; /* FIXME: paths! */ if (b12->cltv) diff --git a/tests/test_pay.py b/tests/test_pay.py index f8682643c036..1778d6b83bfc 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5238,7 +5238,24 @@ def test_pay_manual_exclude(node_factory, bitcoind): l2.rpc.pay(inv, exclude=[scid23]) +@unittest.skipIf(TEST_NETWORK != 'regtest', "Invoice is network specific") def test_pay_bolt11_metadata(node_factory, bitcoind): - l1 = node_factory.get_node() + l1, l2 = node_factory.line_graph(2) + + # BOLT #11: + # > ### Please send 0.01 BTC with payment metadata 0x01fafaf0 + # > lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgq7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqpd05wjc + + b11 = l1.rpc.decode('lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgq7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqpd05wjc') + assert b11['payment_metadata'] == '01fafaf0' + + # I previously hacked lightningd to add "this is metadata" to metadata. + # After CI started failing, I *also* hacked it to set expiry to BIGNUM. + inv = "lnbcrt1230n1p3yzgcxsp5q8g040f9rl9mu2unkjuj0vn262s6nyrhz5hythk3ueu2lfzahmzspp5ve584t0cv27hwmy0cx9ca8uwyqyfw9y9dm3r8vus9fv36r2l9yjsdq8v3jhxccmq6w35xjueqd9ejqmt9w3skgct5vyxqxra2q2qcqp99q2sqqqqqysgqfw6efxpzk5x5vfj8se46yg667x5cvhyttnmuqyk0q7rmhx3gs249qhtdggnek8c5adm2pztkjddlwyn2art2zg9xap2ckczzl3fzz4qqsej6mf" + # Make l2 "know" about this invoice. + l2.rpc.invoice(msatoshi='123000', label='label1', description='desc', preimage='00' * 32) + + with pytest.raises(RpcError, match=r'WIRE_INVALID_ONION_PAYLOAD'): + l1.rpc.pay(inv) - l1.rpc.decode('lnbcrt10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqcqpjsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqrk6hdutpaetmm3afjn0vfczgeyv0cy739rr939kwd4h5j3khxcskhgf59eaqy8wyq82tsnaqc5y32ed4jg34jw7rmeva9u6kfhymawgptmy5f6') + l2.daemon.wait_for_log("Unexpected payment_metadata {}".format(b'this is metadata'.hex())) From 7e789be0eaaf830322e63a2771d8f0cbfd07df37 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0613/1530] doc: update BOLTs to latest master. Just typo fixes and the like. Signed-off-by: Rusty Russell --- Makefile | 2 +- channeld/channeld.c | 12 +++++------- channeld/full_channel.c | 3 ++- closingd/closingd.c | 4 +++- common/features.c | 3 +-- hsmd/libhsmd.c | 6 +++--- openingd/dualopend.c | 4 ++-- wallet/wallet.c | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index a835e78d8e54..dd1eb972274e 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../lightning-rfc/ -DEFAULT_BOLTVERSION := f6c4d7604150986894bcb46d67c5c88680740b12 +DEFAULT_BOLTVERSION := e60d594abf436e768116684080997a8d4f960263 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/channeld/channeld.c b/channeld/channeld.c index 24c41b9d108d..72cd437bfdd2 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1107,7 +1107,7 @@ static struct bitcoin_signature *unraw_sigs(const tal_t *ctx, *... * * if `option_anchors` applies to this commitment * transaction, `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is - * used. + * used as described in [BOLT #5] */ if (option_anchor_outputs) sigs[i].sighash_type = SIGHASH_SINGLE|SIGHASH_ANYONECANPAY; @@ -2468,8 +2468,7 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) /* BOLT #2: * * A receiving node: - * - if `option_static_remotekey` or `option_anchors` applies to the - * commitment transaction: + * - if `option_static_remotekey` applies to the commitment transaction: * - if `next_revocation_number` is greater than expected above, AND * `your_last_per_commitment_secret` is correct for that * `next_revocation_number` minus 1: @@ -2515,7 +2514,7 @@ static void check_future_dataloss_fields(struct peer *peer, /* BOLT #2: * - MUST NOT broadcast its commitment transaction. - * - SHOULD fail the channel. + * - SHOULD send an `error` to request the peer to fail the channel. * - SHOULD store `my_current_per_commitment_point` to * retrieve funds should the sending node broadcast its * commitment transaction on-chain. @@ -2532,8 +2531,7 @@ static void check_future_dataloss_fields(struct peer *peer, /* BOLT #2: * * A receiving node: - * - if `option_static_remotekey` or `option_anchors` applies to the - * commitment transaction: + * - if `option_static_remotekey` applies to the commitment transaction: * ... * - if `your_last_per_commitment_secret` does not match the expected values: * - SHOULD send an `error` and fail the channel. @@ -2797,7 +2795,7 @@ static void peer_reconnect(struct peer *peer, * 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. - * - if `option_static_remotekey` or `option_anchors` applies to the commitment transaction: + * - if `option_static_remotekey` applies to the commitment transaction: * - MUST set `my_current_per_commitment_point` to a valid point. * - otherwise: * - MUST set `my_current_per_commitment_point` to its commitment diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 5c7102b24c61..87e736bb34ee 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -617,7 +617,8 @@ static enum channel_add_err add_htlc(struct channel *channel, * * - if a sending node adds more than receiver `max_accepted_htlcs` * HTLCs to its local commitment transaction... - * - SHOULD fail the channel. + * - SHOULD send a `warning` and close the connection, or send an + * `error` and fail the channel. */ if (htlc_count + 1 > channel->config[recipient].max_accepted_htlcs) { return CHANNEL_ERR_TOO_MANY_HTLCS; diff --git a/closingd/closingd.c b/closingd/closingd.c index e2a8fcbffe59..30739e0a5486 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -707,8 +707,10 @@ static void do_quickclose(struct amount_sat offer[NUM_SIDES], /* BOLT #2: * - if the message contains a `fee_range`: * - if there is no overlap between that and its own `fee_range`: - * - SHOULD fail the connection + * - SHOULD send a warning + * - MUST fail the channel if it doesn't receive a satisfying `fee_range` after a reasonable amount of time */ + /* (Note we satisfy the "MUST fail" by our close command unilteraltimeout) */ if (!get_overlap(our_feerange, their_feerange, &overlap)) { peer_failed_warn(pps, channel_id, "Unable to agree on a feerate." diff --git a/common/features.c b/common/features.c index e95d631e8964..bd7db0bfb4a1 100644 --- a/common/features.c +++ b/common/features.c @@ -131,8 +131,7 @@ static const struct dependency feature_deps[] = { * `option_anchor_outputs` | ... | ... | `option_static_remotekey` */ { OPT_ANCHOR_OUTPUTS, OPT_STATIC_REMOTEKEY }, - /* FIXME: This dep was added later! */ - /* BOLT-master #9: + /* BOLT #9: * Name | Description | Context | Dependencies | *... * `option_anchors_zero_fee_htlc_tx` | ... | ... | `option_static_remotekey` diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index b8e230718bab..6a5e349623d6 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -1124,7 +1124,7 @@ static u8 *handle_sign_local_htlc_tx(struct hsmd_client *c, const u8 *msg_in) * ## HTLC-Timeout and HTLC-Success Transactions *... * * if `option_anchors` applies to this commitment transaction, - * `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is used. + * `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is used as described in [BOLT #5] */ sign_tx_input(tx, 0, NULL, wscript, &htlc_privkey, &htlc_pubkey, option_anchor_outputs @@ -1177,7 +1177,7 @@ static u8 *handle_sign_remote_htlc_tx(struct hsmd_client *c, const u8 *msg_in) * ## HTLC-Timeout and HTLC-Success Transactions *... * * if `option_anchors` applies to this commitment transaction, - * `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is used. + * `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is used as described in [BOLT #5] */ sign_tx_input(tx, 0, NULL, wscript, &htlc_privkey, &htlc_pubkey, option_anchor_outputs @@ -1446,7 +1446,7 @@ static u8 *handle_sign_remote_htlc_to_us(struct hsmd_client *c, * ## HTLC-Timeout and HTLC-Success Transactions *... * * if `option_anchors` applies to this commitment transaction, - * `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is used. + * `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is used as described in [BOLT #5] */ return handle_sign_to_us_tx( c, msg_in, tx, &privkey, wscript, diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 2bf7d60106c3..737e9fb70aad 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3458,7 +3458,7 @@ static u8 *handle_funding_depth(struct state *state, u8 *msg) /* BOLT #2: * * A receiving node: - * - if `option_static_remotekey` or `option_anchors` applies to the commitment transaction: + * - if `option_static_remotekey` applies to the commitment transaction: * - if `next_revocation_number` is greater than expected above, AND * `your_last_per_commitment_secret` is correct for that * `next_revocation_number` minus 1: @@ -3504,7 +3504,7 @@ check_future_dataloss_fields(struct state *state, /* BOLT #2: * - MUST NOT broadcast its commitment transaction. - * - SHOULD fail the channel. + * - SHOULD send an `error` to request the peer to fail the channel. */ wire_sync_write(REQ_FD, take(towire_dualopend_fail_fallen_behind(NULL))); diff --git a/wallet/wallet.c b/wallet/wallet.c index 93e52b54a594..bf0710b0f352 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -869,7 +869,7 @@ wallet_htlc_sigs_load(const tal_t *ctx, struct wallet *w, u64 channelid, *... * * if `option_anchors` applies to this commitment * transaction, `SIGHASH_SINGLE|SIGHASH_ANYONECANPAY` is - * used. + * used as described in [BOLT #5] */ if (option_anchor_outputs) sig.sighash_type = SIGHASH_SINGLE|SIGHASH_ANYONECANPAY; From ec72d89975575bd5927acd9225e8a202de948531 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0614/1530] bolt11: reorder invoice production to match test vectors. After this, we can exactly reproduce the vectors (in DEVELOPER mode). 1. Move payment_metadata position to match test vector. 2. Create flag to suppress `c` field production. 3. Some vectors put secret before payment_hash, hack that in. Signed-off-by: Rusty Russell --- common/bolt11.c | 42 ++++++++++++++++++++++++++++++++++------ common/bolt11.h | 5 +++++ common/test/run-bolt11.c | 20 ++++++++++++------- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/common/bolt11.c b/common/bolt11.c index a4cc1df49b04..29fd34fef066 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -11,6 +11,25 @@ #include #include +#if DEVELOPER +bool dev_bolt11_no_c_generation; + +/* For test vectors, older ones put p before s. */ +static bool modern_order(const struct bolt11 *b11) +{ + if (!b11->description) + return true; + if (streq(b11->description, + "Blockstream Store: 88.85 USD for Blockstream Ledger Nano S x 1, \"Back In My Day\" Sticker x 2, \"I Got Lightning Working\" Sticker x 2 and 1 more items")) + return false; + if (streq(b11->description, "coffee beans")) + return false; + if (streq(b11->description, "payment metadata inside")) + return false; + return true; +} +#endif + struct multiplier { const char letter; /* We can't represent p postfix to msat, so we multiply this by 10 */ @@ -996,6 +1015,8 @@ static void encode_x(u5 **data, u64 expiry) static void encode_c(u5 **data, u16 min_final_cltv_expiry) { + if (IFDEV(dev_bolt11_no_c_generation, false)) + return; push_varlen_field(data, 'c', min_final_cltv_expiry); } @@ -1154,6 +1175,13 @@ char *bolt11_encode_(const tal_t *ctx, */ push_varlen_uint(&data, b11->timestamp, 35); + /* This is a hack to match the test vectors, *some* of which + * order differently! */ + if (IFDEV(modern_order(b11), true)) { + if (b11->payment_secret) + encode_s(&data, b11->payment_secret); + } + /* BOLT #11: * * if a writer offers more than one of any field type, @@ -1174,23 +1202,25 @@ char *bolt11_encode_(const tal_t *ctx, else if (b11->description) encode_d(&data, b11->description); + if (b11->metadata) + encode_m(&data, b11->metadata); + if (n_field) encode_n(&data, &b11->receiver_id); + if (IFDEV(!modern_order(b11), false)) { + if (b11->payment_secret) + encode_s(&data, b11->payment_secret); + } + if (b11->expiry != DEFAULT_X) encode_x(&data, b11->expiry); - if (b11->metadata) - encode_m(&data, b11->metadata); - /* BOLT #11: * - MUST include one `c` field (`min_final_cltv_expiry`). */ encode_c(&data, b11->min_final_cltv_expiry); - if (b11->payment_secret) - encode_s(&data, b11->payment_secret); - for (size_t i = 0; i < tal_count(b11->fallbacks); i++) encode_f(&data, b11->fallbacks[i]); diff --git a/common/bolt11.h b/common/bolt11.h index 19b87f78fc8c..b561cf569ffc 100644 --- a/common/bolt11.h +++ b/common/bolt11.h @@ -125,4 +125,9 @@ char *bolt11_encode_(const tal_t *ctx, secp256k1_ecdsa_recoverable_signature *rsig), \ (arg)) +#if DEVELOPER +/* Flag for tests to suppress `min_final_cltv_expiry` field generation, to match test vectors */ +extern bool dev_bolt11_no_c_generation; +#endif + #endif /* LIGHTNING_COMMON_BOLT11_H */ diff --git a/common/test/run-bolt11.c b/common/test/run-bolt11.c index 488b848cdd62..d4b2f6aa48c9 100644 --- a/common/test/run-bolt11.c +++ b/common/test/run-bolt11.c @@ -49,7 +49,6 @@ static void test_b11(const char *b11str, { struct bolt11 *b11; char *fail; - char *reproduce; struct bolt11_field *b11_extra, *expect_extra; b11 = bolt11_decode(tmpctx, b11str, NULL, hashed_desc, @@ -115,20 +114,22 @@ static void test_b11(const char *b11str, assert(!expect_extra); /* FIXME: Spec changed to require c fields, but test vectors don't! */ - if (b11->min_final_cltv_expiry == 18) - return; + +#if DEVELOPER + char *reproduce; + + dev_bolt11_no_c_generation = (b11->min_final_cltv_expiry == 18); /* Also blockstream store example signature doesn't match? */ /* Re-encode to check */ reproduce = bolt11_encode(tmpctx, b11, false, test_sign, NULL); -#if 0 for (size_t i = 0; i < strlen(reproduce); i++) { if (reproduce[i] != b11str[i] && reproduce[i] != tolower(b11str[i])) abort(); } -#endif assert(strlen(reproduce) == strlen(b11str)); +#endif } int main(int argc, char *argv[]) @@ -478,8 +479,13 @@ int main(int argc, char *argv[]) set_feature_bit(&b11->features, 100); badstr = bolt11_encode(tmpctx, b11, false, test_sign, NULL); - /* FIXME: above has missing c field! */ - assert(streq(badstr, "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeescqpjsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqjckhuumq7mk7pdua9s6umdg34sjhlju9qgcvclxl35guw3dhhyrrtnmudz3kspyqk6k6r7thyzyrleq9s9lmgh59zlc49mc3nd7ngecqllqtym")); + /* Needs DEVELOPER to munge this into BOLT example order! */ +#if DEVELOPER + assert(streq(badstr, "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqtqyx5vggfcsll4wu246hz02kp85x4katwsk9639we5n5yngc3yhqkm35jnjw4len8vrnqnf5ejh0mzj9n3vz2px97evektfm2l6wqccp3y7372")); +#else + assert(streq(badstr, "lnbc25m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeescqpj9q4psqqqqqqqqqqqqqqqqsgqf0nf0agw8xncpemlreh8wl0z5exhz3pky094lu7pf62nvcxq2vljzhhw69xfdftrgm0jklut3h25nlsfw5prz4c0pjy46xyer0k85hqpnathfq")); +#endif + /* Empty set of allowed bits, ensures this fails! */ fset = tal(tmpctx, struct feature_set); fset->bits[BOLT11_FEATURE] = tal_arr(fset, u8, 0); From 01e5f1886e31816e652f417a1ff789a26aaeec3b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 31 Mar 2022 19:40:50 +1030 Subject: [PATCH 0615/1530] pytest: fix flake in test_multichan. I have a separate branch which fixes this race properly, but it's not anything to do with this PR. Signed-off-by: Rusty Russell --- tests/test_connection.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 4e5eb8be1753..bc7855e97dc2 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3893,7 +3893,11 @@ def test_multichan(node_factory, executor, bitcoind): # Restart with multiple channels works. l3.restart() - l3.rpc.connect(l2.info['id'], 'localhost', l2.port) + # FIXME: race against autoconnect can cause spurious failure (but we connect!) + try: + l3.rpc.connect(l2.info['id'], 'localhost', l2.port) + except RpcError: + wait_for(lambda: only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['connected']) inv = l3.rpc.invoice(100000000, "invoice4", "invoice4") l1.rpc.pay(inv['bolt11']) From 646901f588dac7a41bf7d5757961ee774e007500 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0616/1530] msggen: Wrap field numberings in the message type Otherwise we get less nicely consecutive numbers because request and response share one domain. This separates them again. --- contrib/msggen/msggen/grpc.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 5b9e6763c269..06a37fe75883 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -55,8 +55,16 @@ def write(self, text: str, cleanup: bool = True) -> None: else: self.dest.write(text) - def field2number(self, field): + def field2number(self, message_name, field): m = self.meta['grpc-field-map'] + + # Wrap each field mapping by the message_name, since otherwise + # requests and responses share the same number space (just + # cosmetic really, but why not do it?) + if message_name not in m: + m[message_name] = {} + m = m[message_name] + # Simple case first: if we've already assigned a number let's reuse that if field.path in m: return m[field.path] @@ -70,16 +78,16 @@ def field2number(self, field): if parent2 == parent: maxnum = max(maxnum, v) - self.meta['grpc-field-map'][field.path] = maxnum + 1 + m[field.path] = maxnum + 1 self.logger.warn(f"Assigning new field number to {field.path} => {m[field.path]}") - return self.meta['grpc-field-map'][field.path] + return m[field.path] - def enumerate_fields(self, fields): + def enumerate_fields(self, message_name, fields): """Use the meta map to identify which number this field will get. """ for f in fields: - yield (self.field2number(f), f) + yield (self.field2number(message_name, f), f) def enumvar2number(self, typename, variant): """Find an existing variant number of generate a new one. @@ -170,7 +178,7 @@ def generate_message(self, message: CompositeField): if isinstance(f, EnumField) and f.path not in overrides.keys(): self.generate_enum(f, indent=1) - for i, f in self.enumerate_fields(message.fields): + for i, f in self.enumerate_fields(message.typename, message.fields): if overrides.get(f.path, "") is None: continue From d90aafeed6640c7a4fb30b415752c1ee46b91d3a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0617/1530] cln-grpc: Add the `connect` method --- .msggen.json | 456 ++++++++++++++++--------- cln-grpc/proto/node.proto | 76 ++++- cln-grpc/src/convert.rs | 78 ++++- cln-grpc/src/pb.rs | 6 +- cln-grpc/src/server.rs | 90 +++++ cln-rpc/src/model.rs | 119 +++++++ contrib/msggen/msggen/__main__.py | 6 +- doc/schemas/datastore.request.json | 35 ++ doc/schemas/deldatastore.request.json | 20 ++ doc/schemas/listdatastore.request.json | 16 + 10 files changed, 718 insertions(+), 184 deletions(-) create mode 100644 doc/schemas/datastore.request.json create mode 100644 doc/schemas/deldatastore.request.json create mode 100644 doc/schemas/listdatastore.request.json diff --git a/.msggen.json b/.msggen.json index db8ab9dd7e1b..df3be4bf6ce7 100644 --- a/.msggen.json +++ b/.msggen.json @@ -5,6 +5,24 @@ "unilateral": 1, "unopened": 2 }, + "ConnectAddressType": { + "ipv4": 1, + "ipv6": 2, + "local socket": 0, + "torv2": 3, + "torv3": 4 + }, + "ConnectDirection": { + "in": 0, + "out": 1 + }, + "DatastoreMode": { + "create-or-append": 4, + "create-or-replace": 2, + "must-append": 3, + "must-create": 0, + "must-replace": 1 + }, "GetinfoAddressType": { "dns": 0, "ipv4": 1, @@ -65,170 +83,278 @@ } }, "grpc-field-map": { - "AddGossip.message": 1, - "AutoCleanInvoice.cycle_seconds": 2, - "AutoCleanInvoice.enabled": 3, - "AutoCleanInvoice.expired_by": 1, - "CheckMessage.message": 1, - "CheckMessage.pubkey": 3, - "CheckMessage.verified": 4, - "CheckMessage.zbase": 2, - "Close.destination": 3, - "Close.fee_negotiation_step": 4, - "Close.force_lease_closed": 6, - "Close.id": 1, - "Close.tx": 8, - "Close.txid": 9, - "Close.type": 7, - "Close.unilateraltimeout": 2, - "Close.wrong_funding": 5, - "Getinfo.address[]": 14, - "Getinfo.address[].address": 3, - "Getinfo.address[].port": 2, - "Getinfo.address[].type": 1, - "Getinfo.alias": 2, - "Getinfo.binding[]": 15, - "Getinfo.binding[].address": 2, - "Getinfo.binding[].port": 3, - "Getinfo.binding[].socket": 4, - "Getinfo.binding[].type": 1, - "Getinfo.blockheight": 11, - "Getinfo.color": 3, - "Getinfo.fees_collected_msat": 13, - "Getinfo.id": 1, - "Getinfo.lightning-dir": 9, - "Getinfo.network": 12, - "Getinfo.num_active_channels": 6, - "Getinfo.num_inactive_channels": 7, - "Getinfo.num_peers": 4, - "Getinfo.num_pending_channels": 5, - "Getinfo.our_features": 10, - "Getinfo.our_features.channel": 3, - "Getinfo.our_features.init": 1, - "Getinfo.our_features.invoice": 4, - "Getinfo.our_features.node": 2, - "Getinfo.version": 8, - "Getinfo.warning_bitcoind_sync": 16, - "Getinfo.warning_lightningd_sync": 17, - "ListChannels.channels[]": 4, - "ListChannels.channels[].active": 8, - "ListChannels.channels[].amount_msat": 5, - "ListChannels.channels[].base_fee_millisatoshi": 10, - "ListChannels.channels[].channel_flags": 7, - "ListChannels.channels[].delay": 12, - "ListChannels.channels[].destination": 2, - "ListChannels.channels[].features": 15, - "ListChannels.channels[].fee_per_millionth": 11, - "ListChannels.channels[].htlc_maximum_msat": 14, - "ListChannels.channels[].htlc_minimum_msat": 13, - "ListChannels.channels[].last_update": 9, - "ListChannels.channels[].message_flags": 6, - "ListChannels.channels[].public": 4, - "ListChannels.channels[].short_channel_id": 3, - "ListChannels.channels[].source": 1, - "ListChannels.destination": 3, - "ListChannels.short_channel_id": 1, - "ListChannels.source": 2, - "ListFunds.channels[]": 3, - "ListFunds.channels[].amount_msat": 3, - "ListFunds.channels[].connected": 6, - "ListFunds.channels[].funding_output": 5, - "ListFunds.channels[].funding_txid": 4, - "ListFunds.channels[].our_amount_msat": 2, - "ListFunds.channels[].peer_id": 1, - "ListFunds.channels[].short_channel_id": 8, - "ListFunds.channels[].state": 7, - "ListFunds.outputs[]": 2, - "ListFunds.outputs[].address": 5, - "ListFunds.outputs[].amount_msat": 3, - "ListFunds.outputs[].blockheight": 8, - "ListFunds.outputs[].output": 2, - "ListFunds.outputs[].redeemscript": 6, - "ListFunds.outputs[].scriptpubkey": 4, - "ListFunds.outputs[].status": 7, - "ListFunds.outputs[].txid": 1, - "ListFunds.spent": 1, - "ListPeers.id": 1, - "ListPeers.level": 2, - "ListPeers.peers[]": 3, - "ListPeers.peers[].channels[]": 4, - "ListPeers.peers[].channels[].channel_id": 6, - "ListPeers.peers[].channels[].close_to": 14, - "ListPeers.peers[].channels[].close_to_addr": 47, - "ListPeers.peers[].channels[].closer": 17, - "ListPeers.peers[].channels[].dust_limit_msat": 26, - "ListPeers.peers[].channels[].features[]": 18, - "ListPeers.peers[].channels[].fee_base_msat": 24, - "ListPeers.peers[].channels[].fee_proportional_millionths": 25, - "ListPeers.peers[].channels[].feerate": 3, - "ListPeers.peers[].channels[].feerate.perkb": 2, - "ListPeers.peers[].channels[].feerate.perkw": 1, - "ListPeers.peers[].channels[].funding": 19, - "ListPeers.peers[].channels[].funding.local_msat": 1, - "ListPeers.peers[].channels[].funding.pushed_msat": 3, - "ListPeers.peers[].channels[].funding.remote_msat": 2, - "ListPeers.peers[].channels[].funding_outnum": 8, - "ListPeers.peers[].channels[].funding_txid": 7, - "ListPeers.peers[].channels[].htlcs[]": 46, - "ListPeers.peers[].channels[].htlcs[].amount_msat": 3, - "ListPeers.peers[].channels[].htlcs[].direction": 1, - "ListPeers.peers[].channels[].htlcs[].expiry": 4, - "ListPeers.peers[].channels[].htlcs[].id": 2, - "ListPeers.peers[].channels[].htlcs[].local_trimmed": 6, - "ListPeers.peers[].channels[].htlcs[].payment_hash": 5, - "ListPeers.peers[].channels[].htlcs[].state": 8, - "ListPeers.peers[].channels[].htlcs[].status": 7, - "ListPeers.peers[].channels[].in_fulfilled_msat": 41, - "ListPeers.peers[].channels[].in_offered_msat": 39, - "ListPeers.peers[].channels[].in_payments_fulfilled": 40, - "ListPeers.peers[].channels[].in_payments_offered": 38, - "ListPeers.peers[].channels[].inflight[]": 13, - "ListPeers.peers[].channels[].inflight[].feerate": 3, - "ListPeers.peers[].channels[].inflight[].funding_outnum": 2, - "ListPeers.peers[].channels[].inflight[].funding_txid": 1, - "ListPeers.peers[].channels[].inflight[].our_funding_msat": 5, - "ListPeers.peers[].channels[].inflight[].scratch_txid": 6, - "ListPeers.peers[].channels[].inflight[].total_funding_msat": 4, - "ListPeers.peers[].channels[].initial_feerate": 9, - "ListPeers.peers[].channels[].last_feerate": 10, - "ListPeers.peers[].channels[].max_accepted_htlcs": 35, - "ListPeers.peers[].channels[].max_to_us_msat": 22, - "ListPeers.peers[].channels[].max_total_htlc_in_msat": 27, - "ListPeers.peers[].channels[].min_to_us_msat": 21, - "ListPeers.peers[].channels[].minimum_htlc_in_msat": 32, - "ListPeers.peers[].channels[].next_fee_step": 12, - "ListPeers.peers[].channels[].next_feerate": 11, - "ListPeers.peers[].channels[].opener": 16, - "ListPeers.peers[].channels[].our_reserve_msat": 29, - "ListPeers.peers[].channels[].our_to_self_delay": 34, - "ListPeers.peers[].channels[].out_fulfilled_msat": 45, - "ListPeers.peers[].channels[].out_offered_msat": 43, - "ListPeers.peers[].channels[].out_payments_fulfilled": 44, - "ListPeers.peers[].channels[].out_payments_offered": 42, - "ListPeers.peers[].channels[].owner": 4, - "ListPeers.peers[].channels[].private": 15, - "ListPeers.peers[].channels[].receivable_msat": 31, - "ListPeers.peers[].channels[].scratch_txid": 2, - "ListPeers.peers[].channels[].short_channel_id": 5, - "ListPeers.peers[].channels[].spendable_msat": 30, - "ListPeers.peers[].channels[].state": 1, - "ListPeers.peers[].channels[].state_changes[]": 36, - "ListPeers.peers[].channels[].status[]": 37, - "ListPeers.peers[].channels[].their_reserve_msat": 28, - "ListPeers.peers[].channels[].their_to_self_delay": 33, - "ListPeers.peers[].channels[].to_us_msat": 20, - "ListPeers.peers[].channels[].total_msat": 23, - "ListPeers.peers[].connected": 2, - "ListPeers.peers[].features": 6, - "ListPeers.peers[].id": 1, - "ListPeers.peers[].log[]": 3, - "ListPeers.peers[].log[].data": 7, - "ListPeers.peers[].log[].log": 5, - "ListPeers.peers[].log[].node_id": 6, - "ListPeers.peers[].log[].num_skipped": 2, - "ListPeers.peers[].log[].source": 4, - "ListPeers.peers[].log[].time": 3, - "ListPeers.peers[].log[].type": 1, - "ListPeers.peers[].netaddr[]": 5 + "AddgossipRequest": { + "AddGossip.message": 1 + }, + "AutocleaninvoiceRequest": { + "AutoCleanInvoice.cycle_seconds": 2, + "AutoCleanInvoice.expired_by": 1 + }, + "AutocleaninvoiceResponse": { + "AutoCleanInvoice.cycle_seconds": 3, + "AutoCleanInvoice.enabled": 1, + "AutoCleanInvoice.expired_by": 2 + }, + "CheckmessageRequest": { + "CheckMessage.message": 1, + "CheckMessage.pubkey": 3, + "CheckMessage.zbase": 2 + }, + "CheckmessageResponse": { + "CheckMessage.pubkey": 2, + "CheckMessage.verified": 1 + }, + "CloseRequest": { + "Close.destination": 3, + "Close.fee_negotiation_step": 4, + "Close.force_lease_closed": 6, + "Close.id": 1, + "Close.unilateraltimeout": 2, + "Close.wrong_funding": 5 + }, + "CloseResponse": { + "Close.tx": 2, + "Close.txid": 3, + "Close.type": 1 + }, + "ConnectAddress": { + "Connect.address.address": 3, + "Connect.address.port": 4, + "Connect.address.socket": 2, + "Connect.address.type": 1 + }, + "ConnectRequest": { + "Connect.host": 2, + "Connect.id": 1, + "Connect.port": 3 + }, + "ConnectResponse": { + "Connect.address": 4, + "Connect.direction": 3, + "Connect.features": 2, + "Connect.id": 1 + }, + "DatastoreRequest": { + "Datastore.generation": 4, + "Datastore.hex": 2, + "Datastore.key[]": 1, + "Datastore.mode": 3 + }, + "DatastoreResponse": { + "Datastore.generation": 2, + "Datastore.hex": 3, + "Datastore.key[]": 1, + "Datastore.string": 4 + }, + "DeldatastoreRequest": { + "DelDatastore.generation": 2, + "DelDatastore.key[]": 1 + }, + "DeldatastoreResponse": { + "DelDatastore.generation": 2, + "DelDatastore.hex": 3, + "DelDatastore.key[]": 1, + "DelDatastore.string": 4 + }, + "GetinfoAddress": { + "Getinfo.address[].address": 3, + "Getinfo.address[].port": 2, + "Getinfo.address[].type": 1 + }, + "GetinfoBinding": { + "Getinfo.binding[].address": 2, + "Getinfo.binding[].port": 3, + "Getinfo.binding[].socket": 4, + "Getinfo.binding[].type": 1 + }, + "GetinfoOur_features": { + "Getinfo.our_features.channel": 3, + "Getinfo.our_features.init": 1, + "Getinfo.our_features.invoice": 4, + "Getinfo.our_features.node": 2 + }, + "GetinfoResponse": { + "Getinfo.address[]": 14, + "Getinfo.alias": 2, + "Getinfo.binding[]": 15, + "Getinfo.blockheight": 11, + "Getinfo.color": 3, + "Getinfo.fees_collected_msat": 13, + "Getinfo.id": 1, + "Getinfo.lightning-dir": 9, + "Getinfo.network": 12, + "Getinfo.num_active_channels": 6, + "Getinfo.num_inactive_channels": 7, + "Getinfo.num_peers": 4, + "Getinfo.num_pending_channels": 5, + "Getinfo.our_features": 10, + "Getinfo.version": 8, + "Getinfo.warning_bitcoind_sync": 16, + "Getinfo.warning_lightningd_sync": 17 + }, + "ListchannelsChannels": { + "ListChannels.channels[].active": 8, + "ListChannels.channels[].amount_msat": 5, + "ListChannels.channels[].base_fee_millisatoshi": 10, + "ListChannels.channels[].channel_flags": 7, + "ListChannels.channels[].delay": 12, + "ListChannels.channels[].destination": 2, + "ListChannels.channels[].features": 15, + "ListChannels.channels[].fee_per_millionth": 11, + "ListChannels.channels[].htlc_maximum_msat": 14, + "ListChannels.channels[].htlc_minimum_msat": 13, + "ListChannels.channels[].last_update": 9, + "ListChannels.channels[].message_flags": 6, + "ListChannels.channels[].public": 4, + "ListChannels.channels[].short_channel_id": 3, + "ListChannels.channels[].source": 1 + }, + "ListchannelsRequest": { + "ListChannels.destination": 3, + "ListChannels.short_channel_id": 1, + "ListChannels.source": 2 + }, + "ListchannelsResponse": { + "ListChannels.channels[]": 1 + }, + "ListdatastoreDatastore": { + "ListDatastore.datastore[].generation": 2, + "ListDatastore.datastore[].hex": 3, + "ListDatastore.datastore[].key[]": 1, + "ListDatastore.datastore[].string": 4 + }, + "ListdatastoreRequest": { + "ListDatastore.key[]": 1 + }, + "ListdatastoreResponse": { + "ListDatastore.datastore[]": 1 + }, + "ListfundsChannels": { + "ListFunds.channels[].amount_msat": 3, + "ListFunds.channels[].connected": 6, + "ListFunds.channels[].funding_output": 5, + "ListFunds.channels[].funding_txid": 4, + "ListFunds.channels[].our_amount_msat": 2, + "ListFunds.channels[].peer_id": 1, + "ListFunds.channels[].short_channel_id": 8, + "ListFunds.channels[].state": 7 + }, + "ListfundsOutputs": { + "ListFunds.outputs[].address": 5, + "ListFunds.outputs[].amount_msat": 3, + "ListFunds.outputs[].blockheight": 8, + "ListFunds.outputs[].output": 2, + "ListFunds.outputs[].redeemscript": 6, + "ListFunds.outputs[].scriptpubkey": 4, + "ListFunds.outputs[].status": 7, + "ListFunds.outputs[].txid": 1 + }, + "ListfundsRequest": { + "ListFunds.spent": 1 + }, + "ListfundsResponse": { + "ListFunds.channels[]": 2, + "ListFunds.outputs[]": 1 + }, + "ListpeersPeers": { + "ListPeers.peers[].channels[]": 4, + "ListPeers.peers[].connected": 2, + "ListPeers.peers[].features": 6, + "ListPeers.peers[].id": 1, + "ListPeers.peers[].log[]": 3, + "ListPeers.peers[].netaddr[]": 5 + }, + "ListpeersPeersChannels": { + "ListPeers.peers[].channels[].channel_id": 6, + "ListPeers.peers[].channels[].close_to": 14, + "ListPeers.peers[].channels[].close_to_addr": 47, + "ListPeers.peers[].channels[].closer": 17, + "ListPeers.peers[].channels[].dust_limit_msat": 26, + "ListPeers.peers[].channels[].features[]": 18, + "ListPeers.peers[].channels[].fee_base_msat": 24, + "ListPeers.peers[].channels[].fee_proportional_millionths": 25, + "ListPeers.peers[].channels[].feerate": 3, + "ListPeers.peers[].channels[].funding": 19, + "ListPeers.peers[].channels[].funding_outnum": 8, + "ListPeers.peers[].channels[].funding_txid": 7, + "ListPeers.peers[].channels[].htlcs[]": 46, + "ListPeers.peers[].channels[].in_fulfilled_msat": 41, + "ListPeers.peers[].channels[].in_offered_msat": 39, + "ListPeers.peers[].channels[].in_payments_fulfilled": 40, + "ListPeers.peers[].channels[].in_payments_offered": 38, + "ListPeers.peers[].channels[].inflight[]": 13, + "ListPeers.peers[].channels[].initial_feerate": 9, + "ListPeers.peers[].channels[].last_feerate": 10, + "ListPeers.peers[].channels[].max_accepted_htlcs": 35, + "ListPeers.peers[].channels[].max_to_us_msat": 22, + "ListPeers.peers[].channels[].max_total_htlc_in_msat": 27, + "ListPeers.peers[].channels[].min_to_us_msat": 21, + "ListPeers.peers[].channels[].minimum_htlc_in_msat": 32, + "ListPeers.peers[].channels[].next_fee_step": 12, + "ListPeers.peers[].channels[].next_feerate": 11, + "ListPeers.peers[].channels[].opener": 16, + "ListPeers.peers[].channels[].our_reserve_msat": 29, + "ListPeers.peers[].channels[].our_to_self_delay": 34, + "ListPeers.peers[].channels[].out_fulfilled_msat": 45, + "ListPeers.peers[].channels[].out_offered_msat": 43, + "ListPeers.peers[].channels[].out_payments_fulfilled": 44, + "ListPeers.peers[].channels[].out_payments_offered": 42, + "ListPeers.peers[].channels[].owner": 4, + "ListPeers.peers[].channels[].private": 15, + "ListPeers.peers[].channels[].receivable_msat": 31, + "ListPeers.peers[].channels[].scratch_txid": 2, + "ListPeers.peers[].channels[].short_channel_id": 5, + "ListPeers.peers[].channels[].spendable_msat": 30, + "ListPeers.peers[].channels[].state": 1, + "ListPeers.peers[].channels[].state_changes[]": 36, + "ListPeers.peers[].channels[].status[]": 37, + "ListPeers.peers[].channels[].their_reserve_msat": 28, + "ListPeers.peers[].channels[].their_to_self_delay": 33, + "ListPeers.peers[].channels[].to_us_msat": 20, + "ListPeers.peers[].channels[].total_msat": 23 + }, + "ListpeersPeersChannelsFeerate": { + "ListPeers.peers[].channels[].feerate.perkb": 2, + "ListPeers.peers[].channels[].feerate.perkw": 1 + }, + "ListpeersPeersChannelsFunding": { + "ListPeers.peers[].channels[].funding.local_msat": 1, + "ListPeers.peers[].channels[].funding.pushed_msat": 3, + "ListPeers.peers[].channels[].funding.remote_msat": 2 + }, + "ListpeersPeersChannelsHtlcs": { + "ListPeers.peers[].channels[].htlcs[].amount_msat": 3, + "ListPeers.peers[].channels[].htlcs[].direction": 1, + "ListPeers.peers[].channels[].htlcs[].expiry": 4, + "ListPeers.peers[].channels[].htlcs[].id": 2, + "ListPeers.peers[].channels[].htlcs[].local_trimmed": 6, + "ListPeers.peers[].channels[].htlcs[].payment_hash": 5, + "ListPeers.peers[].channels[].htlcs[].state": 8, + "ListPeers.peers[].channels[].htlcs[].status": 7 + }, + "ListpeersPeersChannelsInflight": { + "ListPeers.peers[].channels[].inflight[].feerate": 3, + "ListPeers.peers[].channels[].inflight[].funding_outnum": 2, + "ListPeers.peers[].channels[].inflight[].funding_txid": 1, + "ListPeers.peers[].channels[].inflight[].our_funding_msat": 5, + "ListPeers.peers[].channels[].inflight[].scratch_txid": 6, + "ListPeers.peers[].channels[].inflight[].total_funding_msat": 4 + }, + "ListpeersPeersLog": { + "ListPeers.peers[].log[].data": 7, + "ListPeers.peers[].log[].log": 5, + "ListPeers.peers[].log[].node_id": 6, + "ListPeers.peers[].log[].num_skipped": 2, + "ListPeers.peers[].log[].source": 4, + "ListPeers.peers[].log[].time": 3, + "ListPeers.peers[].log[].type": 1 + }, + "ListpeersRequest": { + "ListPeers.id": 1, + "ListPeers.level": 2 + }, + "ListpeersResponse": { + "ListPeers.peers[]": 1 + } } } \ No newline at end of file diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index d03b924275c8..008f46b4110b 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -17,6 +17,9 @@ service Node { rpc CheckMessage(CheckmessageRequest) returns (CheckmessageResponse) {} rpc Close(CloseRequest) returns (CloseResponse) {} rpc ConnectPeer(ConnectRequest) returns (ConnectResponse) {} + rpc Datastore(DatastoreRequest) returns (DatastoreResponse) {} + rpc DelDatastore(DeldatastoreRequest) returns (DeldatastoreResponse) {} + rpc ListDatastore(ListdatastoreRequest) returns (ListdatastoreResponse) {} } message GetinfoRequest { @@ -84,7 +87,7 @@ message ListpeersRequest { } message ListpeersResponse { - repeated ListpeersPeers peers = 3; + repeated ListpeersPeers peers = 1; } message ListpeersPeers { @@ -231,8 +234,8 @@ message ListfundsRequest { } message ListfundsResponse { - repeated ListfundsOutputs outputs = 2; - repeated ListfundsChannels channels = 3; + repeated ListfundsOutputs outputs = 1; + repeated ListfundsChannels channels = 2; } message ListfundsOutputs { @@ -270,7 +273,7 @@ message ListchannelsRequest { } message ListchannelsResponse { - repeated ListchannelsChannels channels = 4; + repeated ListchannelsChannels channels = 1; } message ListchannelsChannels { @@ -304,9 +307,9 @@ message AutocleaninvoiceRequest { } message AutocleaninvoiceResponse { - bool enabled = 3; - optional uint64 expired_by = 1; - optional uint64 cycle_seconds = 2; + bool enabled = 1; + optional uint64 expired_by = 2; + optional uint64 cycle_seconds = 3; } message CheckmessageRequest { @@ -316,8 +319,8 @@ message CheckmessageRequest { } message CheckmessageResponse { - bool verified = 4; - optional bytes pubkey = 3; + bool verified = 1; + optional bytes pubkey = 2; } message CloseRequest { @@ -336,9 +339,9 @@ message CloseResponse { UNILATERAL = 1; UNOPENED = 2; } - CloseType item_type = 7; - optional bytes tx = 8; - optional bytes txid = 9; + CloseType item_type = 1; + optional bytes tx = 2; + optional bytes txid = 3; } message ConnectRequest { @@ -372,3 +375,52 @@ message ConnectAddress { optional string address = 3; optional uint32 port = 4; } + +message DatastoreRequest { + // Datastore.mode + enum DatastoreMode { + MUST_CREATE = 0; + MUST_REPLACE = 1; + CREATE_OR_REPLACE = 2; + MUST_APPEND = 3; + CREATE_OR_APPEND = 4; + } + repeated string key = 1; + optional bytes hex = 2; + optional DatastoreMode mode = 3; + optional uint64 generation = 4; +} + +message DatastoreResponse { + repeated string key = 1; + optional uint64 generation = 2; + optional bytes hex = 3; + optional string string = 4; +} + +message DeldatastoreRequest { + repeated string key = 1; + optional uint64 generation = 2; +} + +message DeldatastoreResponse { + repeated string key = 1; + optional uint64 generation = 2; + optional bytes hex = 3; + optional string string = 4; +} + +message ListdatastoreRequest { + repeated string key = 1; +} + +message ListdatastoreResponse { + repeated ListdatastoreDatastore datastore = 1; +} + +message ListdatastoreDatastore { + repeated string key = 1; + optional uint64 generation = 2; + optional bytes hex = 3; + optional string string = 4; +} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 0c2d78a2657a..bfc622aa0fe7 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -300,6 +300,51 @@ impl From<&responses::ConnectResponse> for pb::ConnectResponse { } } +#[allow(unused_variables)] +impl From<&responses::DatastoreResponse> for pb::DatastoreResponse { + fn from(c: &responses::DatastoreResponse) -> Self { + Self { + key: c.key.iter().map(|s| s.into()).collect(), + generation: c.generation.clone(), + hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), + string: c.string.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::DeldatastoreResponse> for pb::DeldatastoreResponse { + fn from(c: &responses::DeldatastoreResponse) -> Self { + Self { + key: c.key.iter().map(|s| s.into()).collect(), + generation: c.generation.clone(), + hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), + string: c.string.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { + fn from(c: &responses::ListdatastoreDatastore) -> Self { + Self { + key: c.key.iter().map(|s| s.into()).collect(), + generation: c.generation.clone(), + hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), + string: c.string.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListdatastoreResponse> for pb::ListdatastoreResponse { + fn from(c: &responses::ListdatastoreResponse) -> Self { + Self { + datastore: c.datastore.iter().map(|s| s.into()).collect(), + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -388,7 +433,38 @@ impl From<&pb::ConnectRequest> for requests::ConnectRequest { Self { id: hex::encode(&c.id), host: c.host.clone(), - port: c.port.map(|i| i as u16), + port: c.port.map(|v| v as u16), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::DatastoreRequest> for requests::DatastoreRequest { + fn from(c: &pb::DatastoreRequest) -> Self { + Self { + key: c.key.iter().map(|s| s.into()).collect(), + hex: c.hex.clone().map(|v| hex::encode(v)), + mode: c.mode.map(|v| v.try_into().unwrap()), + generation: c.generation.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::DeldatastoreRequest> for requests::DeldatastoreRequest { + fn from(c: &pb::DeldatastoreRequest) -> Self { + Self { + key: c.key.iter().map(|s| s.into()).collect(), + generation: c.generation.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListdatastoreRequest> for requests::ListdatastoreRequest { + fn from(c: &pb::ListdatastoreRequest) -> Self { + Self { + key: c.key.iter().map(|s| s.into()).collect(), } } } diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 6d72730cf864..0e5ba33ef8ab 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -8,8 +8,8 @@ impl From for Amount { } } -impl From for JAmount { - fn from(a: Amount) -> Self { - JAmount::from_msat(a.msat) +impl From<&Amount> for JAmount { + fn from(a: &Amount) -> Self { + JAmount::from_msat(a.msat) } } diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index f492ed660086..3b757c69daf8 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -296,4 +296,94 @@ async fn connect_peer( } +async fn datastore( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::DatastoreRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Datastore(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Datastore: {:?}", e)))?; + match result { + Response::Datastore(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Datastore", + r + ) + )), + } + +} + +async fn del_datastore( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::DeldatastoreRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::DelDatastore(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method DelDatastore: {:?}", e)))?; + match result { + Response::DelDatastore(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call DelDatastore", + r + ) + )), + } + +} + +async fn list_datastore( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListdatastoreRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListDatastore(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListDatastore: {:?}", e)))?; + match result { + Response::ListDatastore(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListDatastore", + r + ) + )), + } + +} + } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index a9494af74607..ddab41029b86 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -25,6 +25,9 @@ pub enum Request { CheckMessage(requests::CheckmessageRequest), Close(requests::CloseRequest), ConnectPeer(requests::ConnectRequest), + Datastore(requests::DatastoreRequest), + DelDatastore(requests::DeldatastoreRequest), + ListDatastore(requests::ListdatastoreRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -40,6 +43,9 @@ pub enum Response { CheckMessage(responses::CheckmessageResponse), Close(responses::CloseResponse), ConnectPeer(responses::ConnectResponse), + Datastore(responses::DatastoreResponse), + DelDatastore(responses::DeldatastoreResponse), + ListDatastore(responses::ListdatastoreResponse), } pub mod requests { @@ -126,6 +132,54 @@ pub mod requests { pub port: Option, } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum DatastoreMode { + MUST_CREATE, + MUST_REPLACE, + CREATE_OR_REPLACE, + MUST_APPEND, + CREATE_OR_APPEND, + } + + impl TryFrom for DatastoreMode { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(DatastoreMode::MUST_CREATE), + 1 => Ok(DatastoreMode::MUST_REPLACE), + 2 => Ok(DatastoreMode::CREATE_OR_REPLACE), + 3 => Ok(DatastoreMode::MUST_APPEND), + 4 => Ok(DatastoreMode::CREATE_OR_APPEND), + o => Err(anyhow::anyhow!("Unknown variant {} for enum DatastoreMode", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DatastoreRequest { + #[serde(alias = "key")] + pub key: Vec, + #[serde(alias = "hex", skip_serializing_if = "Option::is_none")] + pub hex: Option, + pub mode: Option, + #[serde(alias = "generation", skip_serializing_if = "Option::is_none")] + pub generation: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DeldatastoreRequest { + #[serde(alias = "key")] + pub key: Vec, + #[serde(alias = "generation", skip_serializing_if = "Option::is_none")] + pub generation: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListdatastoreRequest { + #[serde(alias = "key")] + pub key: Vec, + } + } @@ -751,6 +805,16 @@ pub mod responses { OUT, } + impl TryFrom for ConnectDirection { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ConnectDirection::IN), + 1 => Ok(ConnectDirection::OUT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ConnectDirection", o)), + } + } + } /// Type of connection (*torv2*/*torv3* only if **direction** is *out*) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] @@ -762,6 +826,19 @@ pub mod responses { TORV3, } + impl TryFrom for ConnectAddressType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ConnectAddressType::LOCAL_SOCKET), + 1 => Ok(ConnectAddressType::IPV4), + 2 => Ok(ConnectAddressType::IPV6), + 3 => Ok(ConnectAddressType::TORV2), + 4 => Ok(ConnectAddressType::TORV3), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ConnectAddressType", o)), + } + } + } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ConnectAddress { // Path `Connect.address.type` @@ -786,5 +863,47 @@ pub mod responses { pub direction: ConnectDirection, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DatastoreResponse { + #[serde(alias = "key")] + pub key: Vec, + #[serde(alias = "generation", skip_serializing_if = "Option::is_none")] + pub generation: Option, + #[serde(alias = "hex", skip_serializing_if = "Option::is_none")] + pub hex: Option, + #[serde(alias = "string", skip_serializing_if = "Option::is_none")] + pub string: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DeldatastoreResponse { + #[serde(alias = "key")] + pub key: Vec, + #[serde(alias = "generation", skip_serializing_if = "Option::is_none")] + pub generation: Option, + #[serde(alias = "hex", skip_serializing_if = "Option::is_none")] + pub hex: Option, + #[serde(alias = "string", skip_serializing_if = "Option::is_none")] + pub string: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListdatastoreDatastore { + #[serde(alias = "key")] + pub key: Vec, + #[serde(alias = "generation", skip_serializing_if = "Option::is_none")] + pub generation: Option, + #[serde(alias = "hex", skip_serializing_if = "Option::is_none")] + pub hex: Option, + #[serde(alias = "string", skip_serializing_if = "Option::is_none")] + pub string: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListdatastoreResponse { + #[serde(alias = "datastore")] + pub datastore: Vec, + } + } diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index b98b27f83493..ee0819d88fa0 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -53,10 +53,10 @@ def load_jsonrpc_service(): "Connect", # "createinvoice", # "createonion", - # "datastore", + "Datastore", # "decodepay", # "decode", - # "deldatastore", + "DelDatastore", # "delexpiredinvoice", # "delinvoice", # "delpay", @@ -79,7 +79,7 @@ def load_jsonrpc_service(): # "keysend", # "listchannels", # "listconfigs", - # "listdatastore", + "ListDatastore", # "listforwards", # "listfunds", # "listinvoices", diff --git a/doc/schemas/datastore.request.json b/doc/schemas/datastore.request.json new file mode 100644 index 000000000000..d34db677064a --- /dev/null +++ b/doc/schemas/datastore.request.json @@ -0,0 +1,35 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "array", + "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", + "items": { + "type": "string" + } + }, + "hex": { + "type": "hex", + "description": "" + }, + "mode": { + "type": "string", + "enum": [ + "must-create", + "must-replace", + "create-or-replace", + "must-append", + "create-or-append" + ], + "description": "" + }, + "generation": { + "type": "u64", + "description": "If specified, means that the update will fail if the previously-existing data is not exactly that generation. This allows for simple atomicity. This is only legal with mode “must-replace” or “must-append”." + } + } +} diff --git a/doc/schemas/deldatastore.request.json b/doc/schemas/deldatastore.request.json new file mode 100644 index 000000000000..976425afbe9a --- /dev/null +++ b/doc/schemas/deldatastore.request.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "array", + "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", + "items": { + "type": "string" + } + }, + "generation": { + "type": "u64", + "description": "If specified, means that the update will fail if the previously-existing data is not exactly that generation. This allows for simple atomicity. This is only legal with mode “must-replace” or “must-append”." + } + } +} diff --git a/doc/schemas/listdatastore.request.json b/doc/schemas/listdatastore.request.json new file mode 100644 index 000000000000..d1693ab2bed4 --- /dev/null +++ b/doc/schemas/listdatastore.request.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "key" + ], + "properties": { + "key": { + "type": "array", + "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", + "items": { + "type": "string" + } + } + } +} From a7f3c5460063b18ec72c3637c2a15c6204d59670 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0618/1530] cln-rpc: Add invoice RPC methods --- .msggen.json | 99 ++++++++ cln-grpc/proto/node.proto | 126 ++++++++++ cln-grpc/src/convert.rs | 152 ++++++++++++ cln-grpc/src/server.rs | 150 ++++++++++++ cln-rpc/src/model.rs | 265 +++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 11 +- contrib/msggen/msggen/grpc.py | 2 +- doc/schemas/createinvoice.request.json | 24 ++ doc/schemas/delexpiredinvoice.request.json | 14 ++ doc/schemas/delinvoice.request.json | 23 ++ doc/schemas/invoice.request.json | 47 ++++ doc/schemas/listinvoices.request.json | 24 ++ 12 files changed, 930 insertions(+), 7 deletions(-) create mode 100644 doc/schemas/createinvoice.request.json create mode 100644 doc/schemas/delexpiredinvoice.request.json create mode 100644 doc/schemas/delinvoice.request.json create mode 100644 doc/schemas/invoice.request.json create mode 100644 doc/schemas/listinvoices.request.json diff --git a/.msggen.json b/.msggen.json index df3be4bf6ce7..7344c15eb64e 100644 --- a/.msggen.json +++ b/.msggen.json @@ -16,6 +16,11 @@ "in": 0, "out": 1 }, + "CreateinvoiceStatus": { + "expired": 1, + "paid": 0, + "unpaid": 2 + }, "DatastoreMode": { "create-or-append": 4, "create-or-replace": 2, @@ -23,6 +28,11 @@ "must-create": 0, "must-replace": 1 }, + "DelinvoiceStatus": { + "expired": 1, + "paid": 0, + "unpaid": 2 + }, "GetinfoAddressType": { "dns": 0, "ipv4": 1, @@ -43,6 +53,11 @@ "spent": 2, "unconfirmed": 0 }, + "ListinvoicesInvoicesStatus": { + "expired": 2, + "paid": 1, + "unpaid": 0 + }, "ListpeersPeersChannelsHtlcsDirection": { "in": 0, "out": 1 @@ -134,6 +149,27 @@ "Connect.features": 2, "Connect.id": 1 }, + "CreateinvoiceRequest": { + "CreateInvoice.invstring": 1, + "CreateInvoice.label": 2, + "CreateInvoice.preimage": 3 + }, + "CreateinvoiceResponse": { + "CreateInvoice.amount_msat": 5, + "CreateInvoice.amount_received_msat": 10, + "CreateInvoice.bolt11": 2, + "CreateInvoice.bolt12": 3, + "CreateInvoice.description": 7, + "CreateInvoice.expires_at": 8, + "CreateInvoice.label": 1, + "CreateInvoice.local_offer_id": 13, + "CreateInvoice.paid_at": 11, + "CreateInvoice.pay_index": 9, + "CreateInvoice.payer_note": 14, + "CreateInvoice.payment_hash": 4, + "CreateInvoice.payment_preimage": 12, + "CreateInvoice.status": 6 + }, "DatastoreRequest": { "Datastore.generation": 4, "Datastore.hex": 2, @@ -156,6 +192,25 @@ "DelDatastore.key[]": 1, "DelDatastore.string": 4 }, + "DelexpiredinvoiceRequest": { + "DelExpiredInvoice.maxexpirytime": 1 + }, + "DelinvoiceRequest": { + "DelInvoice.label": 1, + "DelInvoice.status": 2 + }, + "DelinvoiceResponse": { + "DelInvoice.amount_msat": 4, + "DelInvoice.bolt11": 2, + "DelInvoice.bolt12": 3, + "DelInvoice.description": 5, + "DelInvoice.expires_at": 8, + "DelInvoice.label": 1, + "DelInvoice.local_offer_id": 9, + "DelInvoice.payer_note": 10, + "DelInvoice.payment_hash": 6, + "DelInvoice.status": 7 + }, "GetinfoAddress": { "Getinfo.address[].address": 3, "Getinfo.address[].port": 2, @@ -192,6 +247,25 @@ "Getinfo.warning_bitcoind_sync": 16, "Getinfo.warning_lightningd_sync": 17 }, + "InvoiceRequest": { + "Invoice.cltv": 6, + "Invoice.description": 2, + "Invoice.fallbacks[]": 4, + "Invoice.label": 3, + "Invoice.msatoshi": 1, + "Invoice.preimage": 5 + }, + "InvoiceResponse": { + "Invoice.bolt11": 1, + "Invoice.expires_at": 4, + "Invoice.payment_hash": 2, + "Invoice.payment_secret": 3, + "Invoice.warning_capacity": 5, + "Invoice.warning_deadends": 7, + "Invoice.warning_mpp": 9, + "Invoice.warning_offline": 6, + "Invoice.warning_private_unused": 8 + }, "ListchannelsChannels": { "ListChannels.channels[].active": 8, "ListChannels.channels[].amount_msat": 5, @@ -256,6 +330,31 @@ "ListFunds.channels[]": 2, "ListFunds.outputs[]": 1 }, + "ListinvoicesInvoices": { + "ListInvoices.invoices[].amount_msat": 6, + "ListInvoices.invoices[].amount_received_msat": 12, + "ListInvoices.invoices[].bolt11": 7, + "ListInvoices.invoices[].bolt12": 8, + "ListInvoices.invoices[].description": 2, + "ListInvoices.invoices[].expires_at": 5, + "ListInvoices.invoices[].label": 1, + "ListInvoices.invoices[].local_offer_id": 9, + "ListInvoices.invoices[].paid_at": 13, + "ListInvoices.invoices[].pay_index": 11, + "ListInvoices.invoices[].payer_note": 10, + "ListInvoices.invoices[].payment_hash": 3, + "ListInvoices.invoices[].payment_preimage": 14, + "ListInvoices.invoices[].status": 4 + }, + "ListinvoicesRequest": { + "ListInvoices.invstring": 2, + "ListInvoices.label": 1, + "ListInvoices.offer_id": 4, + "ListInvoices.payment_hash": 3 + }, + "ListinvoicesResponse": { + "ListInvoices.invoices[]": 1 + }, "ListpeersPeers": { "ListPeers.peers[].channels[]": 4, "ListPeers.peers[].connected": 2, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 008f46b4110b..241e628f3361 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -17,9 +17,14 @@ service Node { rpc CheckMessage(CheckmessageRequest) returns (CheckmessageResponse) {} rpc Close(CloseRequest) returns (CloseResponse) {} rpc ConnectPeer(ConnectRequest) returns (ConnectResponse) {} + rpc CreateInvoice(CreateinvoiceRequest) returns (CreateinvoiceResponse) {} rpc Datastore(DatastoreRequest) returns (DatastoreResponse) {} rpc DelDatastore(DeldatastoreRequest) returns (DeldatastoreResponse) {} + rpc DelExpiredInvoice(DelexpiredinvoiceRequest) returns (DelexpiredinvoiceResponse) {} + rpc DelInvoice(DelinvoiceRequest) returns (DelinvoiceResponse) {} + rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} rpc ListDatastore(ListdatastoreRequest) returns (ListdatastoreResponse) {} + rpc ListInvoices(ListinvoicesRequest) returns (ListinvoicesResponse) {} } message GetinfoRequest { @@ -376,6 +381,35 @@ message ConnectAddress { optional uint32 port = 4; } +message CreateinvoiceRequest { + string invstring = 1; + string label = 2; + bytes preimage = 3; +} + +message CreateinvoiceResponse { + // CreateInvoice.status + enum CreateinvoiceStatus { + PAID = 0; + EXPIRED = 1; + UNPAID = 2; + } + string label = 1; + optional string bolt11 = 2; + optional string bolt12 = 3; + bytes payment_hash = 4; + optional Amount amount_msat = 5; + CreateinvoiceStatus status = 6; + string description = 7; + uint64 expires_at = 8; + optional uint64 pay_index = 9; + optional Amount amount_received_msat = 10; + optional uint64 paid_at = 11; + optional bytes payment_preimage = 12; + optional bytes local_offer_id = 13; + optional string payer_note = 14; +} + message DatastoreRequest { // Datastore.mode enum DatastoreMode { @@ -410,6 +444,64 @@ message DeldatastoreResponse { optional string string = 4; } +message DelexpiredinvoiceRequest { + uint32 maxexpirytime = 1; +} + +message DelexpiredinvoiceResponse { +} + +message DelinvoiceRequest { + // DelInvoice.status + enum DelinvoiceStatus { + PAID = 0; + EXPIRED = 1; + UNPAID = 2; + } + string label = 1; + DelinvoiceStatus status = 2; +} + +message DelinvoiceResponse { + // DelInvoice.status + enum DelinvoiceStatus { + PAID = 0; + EXPIRED = 1; + UNPAID = 2; + } + string label = 1; + optional string bolt11 = 2; + optional string bolt12 = 3; + optional Amount amount_msat = 4; + optional string description = 5; + bytes payment_hash = 6; + DelinvoiceStatus status = 7; + uint64 expires_at = 8; + optional bytes local_offer_id = 9; + optional string payer_note = 10; +} + +message InvoiceRequest { + Amount msatoshi = 1; + string description = 2; + string label = 3; + repeated string fallbacks = 4; + optional bytes preimage = 5; + optional uint32 cltv = 6; +} + +message InvoiceResponse { + string bolt11 = 1; + bytes payment_hash = 2; + bytes payment_secret = 3; + uint64 expires_at = 4; + optional string warning_capacity = 5; + optional string warning_offline = 6; + optional string warning_deadends = 7; + optional string warning_private_unused = 8; + optional string warning_mpp = 9; +} + message ListdatastoreRequest { repeated string key = 1; } @@ -424,3 +516,37 @@ message ListdatastoreDatastore { optional bytes hex = 3; optional string string = 4; } + +message ListinvoicesRequest { + optional string label = 1; + optional string invstring = 2; + optional bytes payment_hash = 3; + optional string offer_id = 4; +} + +message ListinvoicesResponse { + repeated ListinvoicesInvoices invoices = 1; +} + +message ListinvoicesInvoices { + // ListInvoices.invoices[].status + enum ListinvoicesInvoicesStatus { + UNPAID = 0; + PAID = 1; + EXPIRED = 2; + } + string label = 1; + string description = 2; + bytes payment_hash = 3; + ListinvoicesInvoicesStatus status = 4; + uint64 expires_at = 5; + optional Amount amount_msat = 6; + optional string bolt11 = 7; + optional string bolt12 = 8; + optional bytes local_offer_id = 9; + optional string payer_note = 10; + optional uint64 pay_index = 11; + optional Amount amount_received_msat = 12; + optional uint64 paid_at = 13; + optional bytes payment_preimage = 14; +} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index bfc622aa0fe7..6eed2a9018b9 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -300,6 +300,28 @@ impl From<&responses::ConnectResponse> for pb::ConnectResponse { } } +#[allow(unused_variables)] +impl From<&responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse { + fn from(c: &responses::CreateinvoiceResponse) -> Self { + Self { + label: c.label.clone(), + bolt11: c.bolt11.clone(), + bolt12: c.bolt12.clone(), + payment_hash: hex::decode(&c.payment_hash).unwrap(), + amount_msat: c.amount_msat.map(|f| f.into()), + status: c.status as i32, + description: c.description.clone(), + expires_at: c.expires_at.clone(), + pay_index: c.pay_index.clone(), + amount_received_msat: c.amount_received_msat.map(|f| f.into()), + paid_at: c.paid_at.clone(), + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), + local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), + payer_note: c.payer_note.clone(), + } + } +} + #[allow(unused_variables)] impl From<&responses::DatastoreResponse> for pb::DatastoreResponse { fn from(c: &responses::DatastoreResponse) -> Self { @@ -324,6 +346,49 @@ impl From<&responses::DeldatastoreResponse> for pb::DeldatastoreResponse { } } +#[allow(unused_variables)] +impl From<&responses::DelexpiredinvoiceResponse> for pb::DelexpiredinvoiceResponse { + fn from(c: &responses::DelexpiredinvoiceResponse) -> Self { + Self { + } + } +} + +#[allow(unused_variables)] +impl From<&responses::DelinvoiceResponse> for pb::DelinvoiceResponse { + fn from(c: &responses::DelinvoiceResponse) -> Self { + Self { + label: c.label.clone(), + bolt11: c.bolt11.clone(), + bolt12: c.bolt12.clone(), + amount_msat: c.amount_msat.map(|f| f.into()), + description: c.description.clone(), + payment_hash: hex::decode(&c.payment_hash).unwrap(), + status: c.status as i32, + expires_at: c.expires_at.clone(), + local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), + payer_note: c.payer_note.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::InvoiceResponse> for pb::InvoiceResponse { + fn from(c: &responses::InvoiceResponse) -> Self { + Self { + bolt11: c.bolt11.clone(), + payment_hash: hex::decode(&c.payment_hash).unwrap(), + payment_secret: hex::decode(&c.payment_secret).unwrap(), + expires_at: c.expires_at.clone(), + warning_capacity: c.warning_capacity.clone(), + warning_offline: c.warning_offline.clone(), + warning_deadends: c.warning_deadends.clone(), + warning_private_unused: c.warning_private_unused.clone(), + warning_mpp: c.warning_mpp.clone(), + } + } +} + #[allow(unused_variables)] impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { fn from(c: &responses::ListdatastoreDatastore) -> Self { @@ -345,6 +410,37 @@ impl From<&responses::ListdatastoreResponse> for pb::ListdatastoreResponse { } } +#[allow(unused_variables)] +impl From<&responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices { + fn from(c: &responses::ListinvoicesInvoices) -> Self { + Self { + label: c.label.clone(), + description: c.description.clone(), + payment_hash: hex::decode(&c.payment_hash).unwrap(), + status: c.status as i32, + expires_at: c.expires_at.clone(), + amount_msat: c.amount_msat.map(|f| f.into()), + bolt11: c.bolt11.clone(), + bolt12: c.bolt12.clone(), + local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), + payer_note: c.payer_note.clone(), + pay_index: c.pay_index.clone(), + amount_received_msat: c.amount_received_msat.map(|f| f.into()), + paid_at: c.paid_at.clone(), + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListinvoicesResponse> for pb::ListinvoicesResponse { + fn from(c: &responses::ListinvoicesResponse) -> Self { + Self { + invoices: c.invoices.iter().map(|s| s.into()).collect(), + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -438,6 +534,17 @@ impl From<&pb::ConnectRequest> for requests::ConnectRequest { } } +#[allow(unused_variables)] +impl From<&pb::CreateinvoiceRequest> for requests::CreateinvoiceRequest { + fn from(c: &pb::CreateinvoiceRequest) -> Self { + Self { + invstring: c.invstring.clone(), + label: c.label.clone(), + preimage: hex::encode(&c.preimage), + } + } +} + #[allow(unused_variables)] impl From<&pb::DatastoreRequest> for requests::DatastoreRequest { fn from(c: &pb::DatastoreRequest) -> Self { @@ -460,6 +567,39 @@ impl From<&pb::DeldatastoreRequest> for requests::DeldatastoreRequest { } } +#[allow(unused_variables)] +impl From<&pb::DelexpiredinvoiceRequest> for requests::DelexpiredinvoiceRequest { + fn from(c: &pb::DelexpiredinvoiceRequest) -> Self { + Self { + maxexpirytime: c.maxexpirytime.clone(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { + fn from(c: &pb::DelinvoiceRequest) -> Self { + Self { + label: c.label.clone(), + status: c.status.try_into().unwrap(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::InvoiceRequest> for requests::InvoiceRequest { + fn from(c: &pb::InvoiceRequest) -> Self { + Self { + msatoshi: c.msatoshi.as_ref().unwrap().into(), + description: c.description.clone(), + label: c.label.clone(), + fallbacks: c.fallbacks.iter().map(|s| s.into()).collect(), + preimage: c.preimage.clone().map(|v| hex::encode(v)), + cltv: c.cltv.clone(), + } + } +} + #[allow(unused_variables)] impl From<&pb::ListdatastoreRequest> for requests::ListdatastoreRequest { fn from(c: &pb::ListdatastoreRequest) -> Self { @@ -469,3 +609,15 @@ impl From<&pb::ListdatastoreRequest> for requests::ListdatastoreRequest { } } +#[allow(unused_variables)] +impl From<&pb::ListinvoicesRequest> for requests::ListinvoicesRequest { + fn from(c: &pb::ListinvoicesRequest) -> Self { + Self { + label: c.label.clone(), + invstring: c.invstring.clone(), + payment_hash: c.payment_hash.clone().map(|v| hex::encode(v)), + offer_id: c.offer_id.clone(), + } + } +} + diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 3b757c69daf8..fb538046007e 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -296,6 +296,36 @@ async fn connect_peer( } +async fn create_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::CreateinvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::CreateInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method CreateInvoice: {:?}", e)))?; + match result { + Response::CreateInvoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call CreateInvoice", + r + ) + )), + } + +} + async fn datastore( &self, request: tonic::Request, @@ -356,6 +386,96 @@ async fn del_datastore( } +async fn del_expired_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::DelexpiredinvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::DelExpiredInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method DelExpiredInvoice: {:?}", e)))?; + match result { + Response::DelExpiredInvoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call DelExpiredInvoice", + r + ) + )), + } + +} + +async fn del_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::DelinvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::DelInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method DelInvoice: {:?}", e)))?; + match result { + Response::DelInvoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call DelInvoice", + r + ) + )), + } + +} + +async fn invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::InvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Invoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Invoice: {:?}", e)))?; + match result { + Response::Invoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Invoice", + r + ) + )), + } + +} + async fn list_datastore( &self, request: tonic::Request, @@ -386,4 +506,34 @@ async fn list_datastore( } +async fn list_invoices( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListinvoicesRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListInvoices(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListInvoices: {:?}", e)))?; + match result { + Response::ListInvoices(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListInvoices", + r + ) + )), + } + +} + } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index ddab41029b86..78e80f032e64 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -25,9 +25,14 @@ pub enum Request { CheckMessage(requests::CheckmessageRequest), Close(requests::CloseRequest), ConnectPeer(requests::ConnectRequest), + CreateInvoice(requests::CreateinvoiceRequest), Datastore(requests::DatastoreRequest), DelDatastore(requests::DeldatastoreRequest), + DelExpiredInvoice(requests::DelexpiredinvoiceRequest), + DelInvoice(requests::DelinvoiceRequest), + Invoice(requests::InvoiceRequest), ListDatastore(requests::ListdatastoreRequest), + ListInvoices(requests::ListinvoicesRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -43,9 +48,14 @@ pub enum Response { CheckMessage(responses::CheckmessageResponse), Close(responses::CloseResponse), ConnectPeer(responses::ConnectResponse), + CreateInvoice(responses::CreateinvoiceResponse), Datastore(responses::DatastoreResponse), DelDatastore(responses::DeldatastoreResponse), + DelExpiredInvoice(responses::DelexpiredinvoiceResponse), + DelInvoice(responses::DelinvoiceResponse), + Invoice(responses::InvoiceResponse), ListDatastore(responses::ListdatastoreResponse), + ListInvoices(responses::ListinvoicesResponse), } pub mod requests { @@ -132,6 +142,16 @@ pub mod requests { pub port: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateinvoiceRequest { + #[serde(alias = "invstring")] + pub invstring: String, + #[serde(alias = "label")] + pub label: String, + #[serde(alias = "preimage")] + pub preimage: String, + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "lowercase")] pub enum DatastoreMode { @@ -174,12 +194,74 @@ pub mod requests { pub generation: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DelexpiredinvoiceRequest { + #[serde(alias = "maxexpirytime")] + pub maxexpirytime: u32, + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum DelinvoiceStatus { + PAID, + EXPIRED, + UNPAID, + } + + impl TryFrom for DelinvoiceStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(DelinvoiceStatus::PAID), + 1 => Ok(DelinvoiceStatus::EXPIRED), + 2 => Ok(DelinvoiceStatus::UNPAID), + o => Err(anyhow::anyhow!("Unknown variant {} for enum DelinvoiceStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DelinvoiceRequest { + #[serde(alias = "label")] + pub label: String, + // Path `DelInvoice.status` + #[serde(rename = "status")] + pub status: DelinvoiceStatus, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct InvoiceRequest { + #[serde(alias = "msatoshi")] + pub msatoshi: Amount, + #[serde(alias = "description")] + pub description: String, + #[serde(alias = "label")] + pub label: String, + #[serde(alias = "fallbacks")] + pub fallbacks: Vec, + #[serde(alias = "preimage", skip_serializing_if = "Option::is_none")] + pub preimage: Option, + #[serde(alias = "cltv", skip_serializing_if = "Option::is_none")] + pub cltv: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListdatastoreRequest { #[serde(alias = "key")] pub key: Vec, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListinvoicesRequest { + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "invstring", skip_serializing_if = "Option::is_none")] + pub invstring: Option, + #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] + pub payment_hash: Option, + #[serde(alias = "offer_id", skip_serializing_if = "Option::is_none")] + pub offer_id: Option, + } + } @@ -863,6 +945,59 @@ pub mod responses { pub direction: ConnectDirection, } + /// Whether it has been paid, or can no longer be paid + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum CreateinvoiceStatus { + PAID, + EXPIRED, + UNPAID, + } + + impl TryFrom for CreateinvoiceStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(CreateinvoiceStatus::PAID), + 1 => Ok(CreateinvoiceStatus::EXPIRED), + 2 => Ok(CreateinvoiceStatus::UNPAID), + o => Err(anyhow::anyhow!("Unknown variant {} for enum CreateinvoiceStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateinvoiceResponse { + #[serde(alias = "label")] + pub label: String, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + // Path `CreateInvoice.status` + #[serde(rename = "status")] + pub status: CreateinvoiceStatus, + #[serde(alias = "description")] + pub description: String, + #[serde(alias = "expires_at")] + pub expires_at: u64, + #[serde(alias = "pay_index", skip_serializing_if = "Option::is_none")] + pub pay_index: Option, + #[serde(alias = "amount_received_msat", skip_serializing_if = "Option::is_none")] + pub amount_received_msat: Option, + #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] + pub paid_at: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + #[serde(alias = "local_offer_id", skip_serializing_if = "Option::is_none")] + pub local_offer_id: Option, + #[serde(alias = "payer_note", skip_serializing_if = "Option::is_none")] + pub payer_note: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DatastoreResponse { #[serde(alias = "key")] @@ -887,6 +1022,77 @@ pub mod responses { pub string: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DelexpiredinvoiceResponse { + } + + /// State of invoice + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum DelinvoiceStatus { + PAID, + EXPIRED, + UNPAID, + } + + impl TryFrom for DelinvoiceStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(DelinvoiceStatus::PAID), + 1 => Ok(DelinvoiceStatus::EXPIRED), + 2 => Ok(DelinvoiceStatus::UNPAID), + o => Err(anyhow::anyhow!("Unknown variant {} for enum DelinvoiceStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DelinvoiceResponse { + #[serde(alias = "label")] + pub label: String, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "description", skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `DelInvoice.status` + #[serde(rename = "status")] + pub status: DelinvoiceStatus, + #[serde(alias = "expires_at")] + pub expires_at: u64, + #[serde(alias = "local_offer_id", skip_serializing_if = "Option::is_none")] + pub local_offer_id: Option, + #[serde(alias = "payer_note", skip_serializing_if = "Option::is_none")] + pub payer_note: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct InvoiceResponse { + #[serde(alias = "bolt11")] + pub bolt11: String, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + #[serde(alias = "payment_secret")] + pub payment_secret: String, + #[serde(alias = "expires_at")] + pub expires_at: u64, + #[serde(alias = "warning_capacity", skip_serializing_if = "Option::is_none")] + pub warning_capacity: Option, + #[serde(alias = "warning_offline", skip_serializing_if = "Option::is_none")] + pub warning_offline: Option, + #[serde(alias = "warning_deadends", skip_serializing_if = "Option::is_none")] + pub warning_deadends: Option, + #[serde(alias = "warning_private_unused", skip_serializing_if = "Option::is_none")] + pub warning_private_unused: Option, + #[serde(alias = "warning_mpp", skip_serializing_if = "Option::is_none")] + pub warning_mpp: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListdatastoreDatastore { #[serde(alias = "key")] @@ -905,5 +1111,64 @@ pub mod responses { pub datastore: Vec, } + /// Whether it's paid, unpaid or unpayable + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListinvoicesInvoicesStatus { + UNPAID, + PAID, + EXPIRED, + } + + impl TryFrom for ListinvoicesInvoicesStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListinvoicesInvoicesStatus::UNPAID), + 1 => Ok(ListinvoicesInvoicesStatus::PAID), + 2 => Ok(ListinvoicesInvoicesStatus::EXPIRED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListinvoicesInvoicesStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListinvoicesInvoices { + #[serde(alias = "label")] + pub label: String, + #[serde(alias = "description")] + pub description: String, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `ListInvoices.invoices[].status` + #[serde(rename = "status")] + pub status: ListinvoicesInvoicesStatus, + #[serde(alias = "expires_at")] + pub expires_at: u64, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "local_offer_id", skip_serializing_if = "Option::is_none")] + pub local_offer_id: Option, + #[serde(alias = "payer_note", skip_serializing_if = "Option::is_none")] + pub payer_note: Option, + #[serde(alias = "pay_index", skip_serializing_if = "Option::is_none")] + pub pay_index: Option, + #[serde(alias = "amount_received_msat", skip_serializing_if = "Option::is_none")] + pub amount_received_msat: Option, + #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] + pub paid_at: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListinvoicesResponse { + #[serde(alias = "invoices")] + pub invoices: Vec, + } + } diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index ee0819d88fa0..da2865d3a8bb 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -51,14 +51,14 @@ def load_jsonrpc_service(): # "check", # No point in mapping this one "Close", "Connect", - # "createinvoice", + "CreateInvoice", # "createonion", "Datastore", # "decodepay", # "decode", "DelDatastore", - # "delexpiredinvoice", - # "delinvoice", + "DelExpiredInvoice", + "DelInvoice", # "delpay", # "disableoffer", # "disconnect", @@ -75,18 +75,17 @@ def load_jsonrpc_service(): # "getroute", # "getsharedsecret", # "help", - # "invoice", + "Invoice", # "keysend", # "listchannels", # "listconfigs", "ListDatastore", # "listforwards", # "listfunds", - # "listinvoices", + "ListInvoices", # "listnodes", # "listoffers", # "listpays", - # "listpeers", # "listsendpays", # "listtransactions", # "multifundchannel", diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 06a37fe75883..95f6cc5b89d5 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -362,7 +362,7 @@ def generate_composite(self, prefix, field: CompositeField) -> None: elif isinstance(f, EnumField): if f.required: - self.write(f"{name}: c.{name}.into(),\n", numindent=3) + self.write(f"{name}: c.{name}.try_into().unwrap(),\n", numindent=3) else: self.write(f"{name}: c.{name}.map(|v| v.try_into().unwrap()),\n", numindent=3) pass diff --git a/doc/schemas/createinvoice.request.json b/doc/schemas/createinvoice.request.json new file mode 100644 index 000000000000..4a15268e7f0a --- /dev/null +++ b/doc/schemas/createinvoice.request.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "invstring", + "label", + "preimage" + ], + "properties": { + "invstring": { + "type": "string", + "description": "" + }, + "label": { + "type": "string", + "description": "" + }, + "preimage": { + "type": "hex", + "description": "" + } + } +} diff --git a/doc/schemas/delexpiredinvoice.request.json b/doc/schemas/delexpiredinvoice.request.json new file mode 100644 index 000000000000..7c86c8bc050b --- /dev/null +++ b/doc/schemas/delexpiredinvoice.request.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "maxexpirytime" + ], + "properties": { + "maxexpirytime": { + "type": "u32", + "description": "" + } + } +} diff --git a/doc/schemas/delinvoice.request.json b/doc/schemas/delinvoice.request.json new file mode 100644 index 000000000000..f692129e039f --- /dev/null +++ b/doc/schemas/delinvoice.request.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "label", + "status" + ], + "properties": { + "label": { + "type": "string", + "description": "" + }, + "status": { + "type": "string", + "enum": [ + "paid", + "expired", + "unpaid" + ] + } + } +} diff --git a/doc/schemas/invoice.request.json b/doc/schemas/invoice.request.json new file mode 100644 index 000000000000..282311bd83ed --- /dev/null +++ b/doc/schemas/invoice.request.json @@ -0,0 +1,47 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "msatoshi", + "label", + "description" + ], + "properties": { + "msatoshi": { + "type": "msat", + "description": "" + }, + "description": { + "type": "string", + "description": "" + }, + "label": { + "type": "string", + "description": "" + }, + "expiry": { + "type": "", + "description": "" + }, + "fallbacks": { + "type": "array", + "description": "", + "items": { + "type": "string" + } + }, + "preimage": { + "type": "hex", + "description": "" + }, + "exposeprivatechannels": { + "type": "bool", + "description": "" + }, + "cltv": { + "type": "u32", + "description": "" + } + } +} diff --git a/doc/schemas/listinvoices.request.json b/doc/schemas/listinvoices.request.json new file mode 100644 index 000000000000..6c4bb01f6c92 --- /dev/null +++ b/doc/schemas/listinvoices.request.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": { + "label": { + "type": "string", + "description": "" + }, + "invstring": { + "type": "string", + "description": "" + }, + "payment_hash": { + "type": "hex", + "description": "" + }, + "offer_id": { + "type": "string", + "description": "" + } + } +} From 565518246a4f609175f4437a7e0e41e97b897638 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0619/1530] msggen: Add custom mappings for JSON -> grpc conversions --- cln-grpc/src/convert.rs | 36 +++++++++++++++++------------------ contrib/msggen/msggen/grpc.py | 9 ++++++++- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 6eed2a9018b9..797b5bb4cb74 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -47,8 +47,8 @@ impl From<&responses::GetinfoResponse> for pb::GetinfoResponse { blockheight: c.blockheight.clone(), network: c.network.clone(), fees_collected_msat: Some(c.fees_collected_msat.into()), - address: c.address.iter().map(|s| s.into()).collect(), - binding: c.binding.iter().map(|s| s.into()).collect(), + address: c.address.iter().map(|i| i.into()).collect(), + binding: c.binding.iter().map(|i| i.into()).collect(), warning_bitcoind_sync: c.warning_bitcoind_sync.clone(), warning_lightningd_sync: c.warning_lightningd_sync.clone(), } @@ -115,12 +115,12 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { last_feerate: c.last_feerate.clone(), next_feerate: c.next_feerate.clone(), next_fee_step: c.next_fee_step.clone(), - inflight: c.inflight.iter().map(|s| s.into()).collect(), + inflight: c.inflight.iter().map(|i| i.into()).collect(), close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), private: c.private.clone(), opener: c.opener as i32, closer: c.closer.map(|v| v as i32), - features: c.features.iter().map(|s| s.into()).collect(), + features: c.features.iter().map(|i| i.into()).collect(), to_us_msat: c.to_us_msat.map(|f| f.into()), min_to_us_msat: c.min_to_us_msat.map(|f| f.into()), max_to_us_msat: c.max_to_us_msat.map(|f| f.into()), @@ -137,7 +137,7 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { their_to_self_delay: c.their_to_self_delay.clone(), our_to_self_delay: c.our_to_self_delay.clone(), max_accepted_htlcs: c.max_accepted_htlcs.clone(), - status: c.status.iter().map(|s| s.into()).collect(), + status: c.status.iter().map(|i| i.into()).collect(), in_payments_offered: c.in_payments_offered.clone(), in_offered_msat: c.in_offered_msat.map(|f| f.into()), in_payments_fulfilled: c.in_payments_fulfilled.clone(), @@ -146,7 +146,7 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { out_offered_msat: c.out_offered_msat.map(|f| f.into()), out_payments_fulfilled: c.out_payments_fulfilled.clone(), out_fulfilled_msat: c.out_fulfilled_msat.map(|f| f.into()), - htlcs: c.htlcs.iter().map(|s| s.into()).collect(), + htlcs: c.htlcs.iter().map(|i| i.into()).collect(), close_to_addr: c.close_to_addr.clone(), } } @@ -158,9 +158,9 @@ impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { Self { id: hex::decode(&c.id).unwrap(), connected: c.connected.clone(), - log: c.log.iter().map(|s| s.into()).collect(), - channels: c.channels.iter().map(|s| s.into()).collect(), - netaddr: c.netaddr.iter().map(|s| s.into()).collect(), + log: c.log.iter().map(|i| i.into()).collect(), + channels: c.channels.iter().map(|i| i.into()).collect(), + netaddr: c.netaddr.iter().map(|i| i.into()).collect(), features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), } } @@ -170,7 +170,7 @@ impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { impl From<&responses::ListpeersResponse> for pb::ListpeersResponse { fn from(c: &responses::ListpeersResponse) -> Self { Self { - peers: c.peers.iter().map(|s| s.into()).collect(), + peers: c.peers.iter().map(|i| i.into()).collect(), } } } @@ -211,8 +211,8 @@ impl From<&responses::ListfundsChannels> for pb::ListfundsChannels { impl From<&responses::ListfundsResponse> for pb::ListfundsResponse { fn from(c: &responses::ListfundsResponse) -> Self { Self { - outputs: c.outputs.iter().map(|s| s.into()).collect(), - channels: c.channels.iter().map(|s| s.into()).collect(), + outputs: c.outputs.iter().map(|i| i.into()).collect(), + channels: c.channels.iter().map(|i| i.into()).collect(), } } } @@ -244,7 +244,7 @@ impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { impl From<&responses::ListchannelsResponse> for pb::ListchannelsResponse { fn from(c: &responses::ListchannelsResponse) -> Self { Self { - channels: c.channels.iter().map(|s| s.into()).collect(), + channels: c.channels.iter().map(|i| i.into()).collect(), } } } @@ -326,7 +326,7 @@ impl From<&responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse { impl From<&responses::DatastoreResponse> for pb::DatastoreResponse { fn from(c: &responses::DatastoreResponse) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), + key: c.key.iter().map(|i| i.into()).collect(), generation: c.generation.clone(), hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), string: c.string.clone(), @@ -338,7 +338,7 @@ impl From<&responses::DatastoreResponse> for pb::DatastoreResponse { impl From<&responses::DeldatastoreResponse> for pb::DeldatastoreResponse { fn from(c: &responses::DeldatastoreResponse) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), + key: c.key.iter().map(|i| i.into()).collect(), generation: c.generation.clone(), hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), string: c.string.clone(), @@ -393,7 +393,7 @@ impl From<&responses::InvoiceResponse> for pb::InvoiceResponse { impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { fn from(c: &responses::ListdatastoreDatastore) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), + key: c.key.iter().map(|i| i.into()).collect(), generation: c.generation.clone(), hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), string: c.string.clone(), @@ -405,7 +405,7 @@ impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { impl From<&responses::ListdatastoreResponse> for pb::ListdatastoreResponse { fn from(c: &responses::ListdatastoreResponse) -> Self { Self { - datastore: c.datastore.iter().map(|s| s.into()).collect(), + datastore: c.datastore.iter().map(|i| i.into()).collect(), } } } @@ -436,7 +436,7 @@ impl From<&responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices { impl From<&responses::ListinvoicesResponse> for pb::ListinvoicesResponse { fn from(c: &responses::ListinvoicesResponse) -> Self { Self { - invoices: c.invoices.iter().map(|s| s.into()).collect(), + invoices: c.invoices.iter().map(|i| i.into()).collect(), } } } diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 95f6cc5b89d5..ea37250a309f 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -259,7 +259,14 @@ def generate_composite(self, prefix, field: CompositeField): name = f.normalized() if isinstance(f, ArrayField): - self.write(f"{name}: c.{name}.iter().map(|s| s.into()).collect(),\n", numindent=3) + typ = f.itemtype.typename + # The inner conversion applied to each element in the + # array. The current item is called `i` + mapping = { + 'hex': f'hex::decode(i).unwrap()', + }.get(typ, f'i.into()') + + self.write(f"{name}: c.{name}.iter().map(|i| {mapping}).collect(),\n", numindent=3) elif isinstance(f, EnumField): if f.required: From 20704746bce5d1cf63f82a0273a506060d70cc90 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0620/1530] grpc: Map `sendonion` and `createonion` --- .msggen.json | 328 ++++++++ cln-grpc/proto/node.proto | 389 ++++++++++ cln-grpc/proto/primitives.proto | 7 +- cln-grpc/src/convert.rs | 864 ++++++++++++++++------ cln-grpc/src/pb.rs | 22 +- cln-grpc/src/server.rs | 390 ++++++++++ cln-rpc/Cargo.toml | 1 + cln-rpc/src/model.rs | 815 ++++++++++++++++++++ cln-rpc/src/primitives.rs | 38 + contrib/msggen/msggen/__main__.py | 51 +- contrib/msggen/msggen/grpc.py | 12 +- contrib/msggen/msggen/model.py | 3 + contrib/msggen/msggen/rust.py | 9 +- doc/lightning-pay.7.md | 4 +- doc/schemas/createonion.request.json | 40 + doc/schemas/keysend.request.json | 31 + doc/schemas/listnodes.request.json | 10 + doc/schemas/listsendpays.request.json | 21 + doc/schemas/listtransactions.request.json | 7 + doc/schemas/newaddr.request.json | 14 + doc/schemas/pay.request.json | 33 + doc/schemas/pay.schema.json | 4 +- doc/schemas/sendonion.request.json | 57 ++ doc/schemas/sendpay.request.json | 54 ++ doc/schemas/waitanyinvoice.request.json | 13 + doc/schemas/waitinvoice.request.json | 12 + doc/schemas/waitsendpay.request.json | 18 + doc/schemas/withdraw.request.json | 27 + 28 files changed, 3031 insertions(+), 243 deletions(-) create mode 100644 doc/schemas/createonion.request.json create mode 100644 doc/schemas/keysend.request.json create mode 100644 doc/schemas/listnodes.request.json create mode 100644 doc/schemas/listsendpays.request.json create mode 100644 doc/schemas/listtransactions.request.json create mode 100644 doc/schemas/newaddr.request.json create mode 100644 doc/schemas/pay.request.json create mode 100644 doc/schemas/sendonion.request.json create mode 100644 doc/schemas/sendpay.request.json create mode 100644 doc/schemas/waitanyinvoice.request.json create mode 100644 doc/schemas/waitinvoice.request.json create mode 100644 doc/schemas/waitsendpay.request.json create mode 100644 doc/schemas/withdraw.request.json diff --git a/.msggen.json b/.msggen.json index 7344c15eb64e..9e553c56953d 100644 --- a/.msggen.json +++ b/.msggen.json @@ -48,6 +48,9 @@ "torv2": 3, "torv3": 4 }, + "KeysendStatus": { + "complete": 0 + }, "ListfundsOutputsStatus": { "confirmed": 1, "spent": 2, @@ -58,6 +61,14 @@ "paid": 1, "unpaid": 0 }, + "ListnodesNodesAddressesType": { + "dns": 0, + "ipv4": 1, + "ipv6": 2, + "torv2": 3, + "torv3": 4, + "websocket": 5 + }, "ListpeersPeersChannelsHtlcsDirection": { "in": 0, "out": 1 @@ -95,6 +106,70 @@ "IO_OUT": 6, "SKIPPED": 0, "UNUSUAL": 2 + }, + "ListsendpaysPaymentsStatus": { + "complete": 2, + "failed": 1, + "pending": 0 + }, + "ListsendpaysStatus": { + "complete": 1, + "failed": 2, + "pending": 0 + }, + "ListtransactionsTransactionsInputsType": { + "channel_funding": 3, + "channel_htlc_success": 7, + "channel_htlc_timeout": 8, + "channel_mutual_close": 4, + "channel_penalty": 9, + "channel_sweep": 6, + "channel_unilateral_cheat": 10, + "channel_unilateral_close": 5, + "deposit": 1, + "theirs": 0, + "withdraw": 2 + }, + "ListtransactionsTransactionsOutputsType": { + "channel_funding": 3, + "channel_htlc_success": 7, + "channel_htlc_timeout": 8, + "channel_mutual_close": 4, + "channel_penalty": 9, + "channel_sweep": 6, + "channel_unilateral_cheat": 10, + "channel_unilateral_close": 5, + "deposit": 1, + "theirs": 0, + "withdraw": 2 + }, + "NewaddrAddresstype": { + "bech32": 0, + "p2sh-segwit": 1 + }, + "PayStatus": { + "complete": 0, + "failed": 2, + "pending": 1 + }, + "SendonionStatus": { + "complete": 1, + "pending": 0 + }, + "SendpayStatus": { + "complete": 1, + "pending": 0 + }, + "WaitanyinvoiceStatus": { + "expired": 1, + "paid": 0 + }, + "WaitinvoiceStatus": { + "expired": 1, + "paid": 0 + }, + "WaitsendpayStatus": { + "complete": 0 } }, "grpc-field-map": { @@ -170,6 +245,20 @@ "CreateInvoice.payment_preimage": 12, "CreateInvoice.status": 6 }, + "CreateonionHops": { + "CreateOnion.hops[].payload": 2, + "CreateOnion.hops[].pubkey": 1 + }, + "CreateonionRequest": { + "CreateOnion.assocdata": 2, + "CreateOnion.hops[]": 1, + "CreateOnion.onion_size": 4, + "CreateOnion.session_key": 3 + }, + "CreateonionResponse": { + "CreateOnion.onion": 1, + "CreateOnion.shared_secrets[]": 2 + }, "DatastoreRequest": { "Datastore.generation": 4, "Datastore.hex": 2, @@ -266,6 +355,26 @@ "Invoice.warning_offline": 6, "Invoice.warning_private_unused": 8 }, + "KeysendRequest": { + "KeySend.destination": 1, + "KeySend.exemptfee": 7, + "KeySend.label": 3, + "KeySend.maxdelay": 6, + "KeySend.maxfeepercent": 4, + "KeySend.msatoshi": 2, + "KeySend.retry_for": 5 + }, + "KeysendResponse": { + "KeySend.amount_msat": 6, + "KeySend.amount_sent_msat": 7, + "KeySend.created_at": 4, + "KeySend.destination": 2, + "KeySend.parts": 5, + "KeySend.payment_hash": 3, + "KeySend.payment_preimage": 1, + "KeySend.status": 9, + "KeySend.warning_partial_completion": 8 + }, "ListchannelsChannels": { "ListChannels.channels[].active": 8, "ListChannels.channels[].amount_msat": 5, @@ -355,6 +464,25 @@ "ListinvoicesResponse": { "ListInvoices.invoices[]": 1 }, + "ListnodesNodes": { + "ListNodes.nodes[].addresses[]": 6, + "ListNodes.nodes[].alias": 3, + "ListNodes.nodes[].color": 4, + "ListNodes.nodes[].features": 5, + "ListNodes.nodes[].last_timestamp": 2, + "ListNodes.nodes[].nodeid": 1 + }, + "ListnodesNodesAddresses": { + "ListNodes.nodes[].addresses[].address": 3, + "ListNodes.nodes[].addresses[].port": 2, + "ListNodes.nodes[].addresses[].type": 1 + }, + "ListnodesRequest": { + "ListNodes.id": 1 + }, + "ListnodesResponse": { + "ListNodes.nodes[]": 1 + }, "ListpeersPeers": { "ListPeers.peers[].channels[]": 4, "ListPeers.peers[].connected": 2, @@ -454,6 +582,206 @@ }, "ListpeersResponse": { "ListPeers.peers[]": 1 + }, + "ListsendpaysPayments": { + "ListSendPays.payments[].amount_msat": 5, + "ListSendPays.payments[].amount_sent_msat": 8, + "ListSendPays.payments[].bolt11": 10, + "ListSendPays.payments[].bolt12": 11, + "ListSendPays.payments[].created_at": 7, + "ListSendPays.payments[].destination": 6, + "ListSendPays.payments[].erroronion": 13, + "ListSendPays.payments[].groupid": 2, + "ListSendPays.payments[].id": 1, + "ListSendPays.payments[].label": 9, + "ListSendPays.payments[].payment_hash": 3, + "ListSendPays.payments[].payment_preimage": 12, + "ListSendPays.payments[].status": 4 + }, + "ListsendpaysRequest": { + "ListSendPays.bolt11": 1, + "ListSendPays.payment_hash": 2, + "ListSendPays.status": 3 + }, + "ListsendpaysResponse": { + "ListSendPays.payments[]": 1 + }, + "ListtransactionsResponse": { + "ListTransactions.transactions[]": 1 + }, + "ListtransactionsTransactions": { + "ListTransactions.transactions[].blockheight": 3, + "ListTransactions.transactions[].channel": 6, + "ListTransactions.transactions[].hash": 1, + "ListTransactions.transactions[].inputs[]": 9, + "ListTransactions.transactions[].locktime": 7, + "ListTransactions.transactions[].outputs[]": 10, + "ListTransactions.transactions[].rawtx": 2, + "ListTransactions.transactions[].txindex": 4, + "ListTransactions.transactions[].type[]": 5, + "ListTransactions.transactions[].version": 8 + }, + "ListtransactionsTransactionsInputs": { + "ListTransactions.transactions[].inputs[].channel": 5, + "ListTransactions.transactions[].inputs[].index": 2, + "ListTransactions.transactions[].inputs[].sequence": 3, + "ListTransactions.transactions[].inputs[].txid": 1, + "ListTransactions.transactions[].inputs[].type": 4 + }, + "ListtransactionsTransactionsOutputs": { + "ListTransactions.transactions[].outputs[].channel": 5, + "ListTransactions.transactions[].outputs[].index": 1, + "ListTransactions.transactions[].outputs[].msat": 2, + "ListTransactions.transactions[].outputs[].scriptPubKey": 3, + "ListTransactions.transactions[].outputs[].type": 4 + }, + "NewaddrRequest": { + "NewAddr.addresstype": 1 + }, + "NewaddrResponse": { + "NewAddr.bech32": 1, + "NewAddr.p2sh-segwit": 2 + }, + "PayRequest": { + "Pay.bolt11": 1, + "Pay.exemptfee": 7, + "Pay.label": 3, + "Pay.maxdelay": 6, + "Pay.maxfeepercent": 4, + "Pay.msatoshi": 2, + "Pay.retry_for": 5, + "Pay.riskfactor": 8 + }, + "PayResponse": { + "Pay.amount_msat": 6, + "Pay.amount_sent_msat": 7, + "Pay.created_at": 4, + "Pay.destination": 2, + "Pay.parts": 5, + "Pay.payment_hash": 3, + "Pay.payment_preimage": 1, + "Pay.status": 9, + "Pay.warning_partial_completion": 8 + }, + "SendonionFirst_hop": { + "SendOnion.first_hop.amount_msat": 2, + "SendOnion.first_hop.delay": 3, + "SendOnion.first_hop.id": 1 + }, + "SendonionRequest": { + "SendOnion.first_hop": 2, + "SendOnion.onion": 1 + }, + "SendonionResponse": { + "SendOnion.amount_msat": 4, + "SendOnion.amount_sent_msat": 7, + "SendOnion.bolt11": 9, + "SendOnion.bolt12": 10, + "SendOnion.created_at": 6, + "SendOnion.destination": 5, + "SendOnion.id": 1, + "SendOnion.label": 8, + "SendOnion.message": 12, + "SendOnion.payment_hash": 2, + "SendOnion.payment_preimage": 11, + "SendOnion.status": 3 + }, + "SendpayRequest": { + "SendPay.bolt11": 5, + "SendPay.label": 3, + "SendPay.msatoshi": 4, + "SendPay.partid": 7, + "SendPay.payment_hash": 2, + "SendPay.payment_secret": 6, + "SendPay.route[]": 1 + }, + "SendpayResponse": { + "SendPay.amount_msat": 5, + "SendPay.amount_sent_msat": 8, + "SendPay.bolt11": 11, + "SendPay.bolt12": 12, + "SendPay.created_at": 7, + "SendPay.destination": 6, + "SendPay.groupid": 2, + "SendPay.id": 1, + "SendPay.label": 9, + "SendPay.message": 14, + "SendPay.partid": 10, + "SendPay.payment_hash": 3, + "SendPay.payment_preimage": 13, + "SendPay.status": 4 + }, + "SendpayRoute": { + "SendPay.route[].channel": 4, + "SendPay.route[].delay": 3, + "SendPay.route[].id": 2, + "SendPay.route[].msatoshi": 1 + }, + "WaitanyinvoiceRequest": { + "WaitAnyInvoice.lastpay_index": 1, + "WaitAnyInvoice.timeout": 2 + }, + "WaitanyinvoiceResponse": { + "WaitAnyInvoice.amount_msat": 6, + "WaitAnyInvoice.amount_received_msat": 10, + "WaitAnyInvoice.bolt11": 7, + "WaitAnyInvoice.bolt12": 8, + "WaitAnyInvoice.description": 2, + "WaitAnyInvoice.expires_at": 5, + "WaitAnyInvoice.label": 1, + "WaitAnyInvoice.paid_at": 11, + "WaitAnyInvoice.pay_index": 9, + "WaitAnyInvoice.payment_hash": 3, + "WaitAnyInvoice.payment_preimage": 12, + "WaitAnyInvoice.status": 4 + }, + "WaitinvoiceRequest": { + "WaitInvoice.label": 1 + }, + "WaitinvoiceResponse": { + "WaitInvoice.amount_msat": 6, + "WaitInvoice.amount_received_msat": 10, + "WaitInvoice.bolt11": 7, + "WaitInvoice.bolt12": 8, + "WaitInvoice.description": 2, + "WaitInvoice.expires_at": 5, + "WaitInvoice.label": 1, + "WaitInvoice.paid_at": 11, + "WaitInvoice.pay_index": 9, + "WaitInvoice.payment_hash": 3, + "WaitInvoice.payment_preimage": 12, + "WaitInvoice.status": 4 + }, + "WaitsendpayRequest": { + "WaitSendPay.partid": 2, + "WaitSendPay.payment_hash": 1, + "WaitSendPay.timeout": 3 + }, + "WaitsendpayResponse": { + "WaitSendPay.amount_msat": 5, + "WaitSendPay.amount_sent_msat": 8, + "WaitSendPay.bolt11": 11, + "WaitSendPay.bolt12": 12, + "WaitSendPay.created_at": 7, + "WaitSendPay.destination": 6, + "WaitSendPay.groupid": 2, + "WaitSendPay.id": 1, + "WaitSendPay.label": 9, + "WaitSendPay.partid": 10, + "WaitSendPay.payment_hash": 3, + "WaitSendPay.payment_preimage": 13, + "WaitSendPay.status": 4 + }, + "WithdrawRequest": { + "Withdraw.destination": 1, + "Withdraw.minconf": 3, + "Withdraw.satoshi": 2, + "Withdraw.utxos[]": 4 + }, + "WithdrawResponse": { + "Withdraw.psbt": 3, + "Withdraw.tx": 1, + "Withdraw.txid": 2 } } } \ No newline at end of file diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 241e628f3361..fc26237dd7d8 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -11,6 +11,7 @@ service Node { rpc Getinfo(GetinfoRequest) returns (GetinfoResponse) {} rpc ListPeers(ListpeersRequest) returns (ListpeersResponse) {} rpc ListFunds(ListfundsRequest) returns (ListfundsResponse) {} + rpc SendPay(SendpayRequest) returns (SendpayResponse) {} rpc ListChannels(ListchannelsRequest) returns (ListchannelsResponse) {} rpc AddGossip(AddgossipRequest) returns (AddgossipResponse) {} rpc AutoCleanInvoice(AutocleaninvoiceRequest) returns (AutocleaninvoiceResponse) {} @@ -19,12 +20,24 @@ service Node { rpc ConnectPeer(ConnectRequest) returns (ConnectResponse) {} rpc CreateInvoice(CreateinvoiceRequest) returns (CreateinvoiceResponse) {} rpc Datastore(DatastoreRequest) returns (DatastoreResponse) {} + rpc CreateOnion(CreateonionRequest) returns (CreateonionResponse) {} rpc DelDatastore(DeldatastoreRequest) returns (DeldatastoreResponse) {} rpc DelExpiredInvoice(DelexpiredinvoiceRequest) returns (DelexpiredinvoiceResponse) {} rpc DelInvoice(DelinvoiceRequest) returns (DelinvoiceResponse) {} rpc Invoice(InvoiceRequest) returns (InvoiceResponse) {} rpc ListDatastore(ListdatastoreRequest) returns (ListdatastoreResponse) {} rpc ListInvoices(ListinvoicesRequest) returns (ListinvoicesResponse) {} + rpc SendOnion(SendonionRequest) returns (SendonionResponse) {} + rpc ListSendPays(ListsendpaysRequest) returns (ListsendpaysResponse) {} + rpc ListTransactions(ListtransactionsRequest) returns (ListtransactionsResponse) {} + rpc Pay(PayRequest) returns (PayResponse) {} + rpc ListNodes(ListnodesRequest) returns (ListnodesResponse) {} + rpc WaitAnyInvoice(WaitanyinvoiceRequest) returns (WaitanyinvoiceResponse) {} + rpc WaitInvoice(WaitinvoiceRequest) returns (WaitinvoiceResponse) {} + rpc WaitSendPay(WaitsendpayRequest) returns (WaitsendpayResponse) {} + rpc NewAddr(NewaddrRequest) returns (NewaddrResponse) {} + rpc Withdraw(WithdrawRequest) returns (WithdrawResponse) {} + rpc KeySend(KeysendRequest) returns (KeysendResponse) {} } message GetinfoRequest { @@ -271,6 +284,45 @@ message ListfundsChannels { optional string short_channel_id = 8; } +message SendpayRequest { + repeated SendpayRoute route = 1; + bytes payment_hash = 2; + optional string label = 3; + optional Amount msatoshi = 4; + optional string bolt11 = 5; + optional bytes payment_secret = 6; + optional uint32 partid = 7; +} + +message SendpayResponse { + // SendPay.status + enum SendpayStatus { + PENDING = 0; + COMPLETE = 1; + } + uint64 id = 1; + optional uint64 groupid = 2; + bytes payment_hash = 3; + SendpayStatus status = 4; + optional Amount amount_msat = 5; + optional bytes destination = 6; + uint64 created_at = 7; + Amount amount_sent_msat = 8; + optional string label = 9; + optional uint64 partid = 10; + optional string bolt11 = 11; + optional string bolt12 = 12; + optional bytes payment_preimage = 13; + optional string message = 14; +} + +message SendpayRoute { + Amount msatoshi = 1; + bytes id = 2; + uint32 delay = 3; + string channel = 4; +} + message ListchannelsRequest { optional string short_channel_id = 1; optional bytes source = 2; @@ -432,6 +484,23 @@ message DatastoreResponse { optional string string = 4; } +message CreateonionRequest { + repeated CreateonionHops hops = 1; + bytes assocdata = 2; + optional bytes session_key = 3; + optional uint32 onion_size = 4; +} + +message CreateonionResponse { + bytes onion = 1; + repeated bytes shared_secrets = 2; +} + +message CreateonionHops { + bytes pubkey = 1; + bytes payload = 2; +} + message DeldatastoreRequest { repeated string key = 1; optional uint64 generation = 2; @@ -550,3 +619,323 @@ message ListinvoicesInvoices { optional uint64 paid_at = 13; optional bytes payment_preimage = 14; } + +message SendonionRequest { + bytes onion = 1; +} + +message SendonionResponse { + // SendOnion.status + enum SendonionStatus { + PENDING = 0; + COMPLETE = 1; + } + uint64 id = 1; + bytes payment_hash = 2; + SendonionStatus status = 3; + optional Amount amount_msat = 4; + optional bytes destination = 5; + uint64 created_at = 6; + Amount amount_sent_msat = 7; + optional string label = 8; + optional string bolt11 = 9; + optional string bolt12 = 10; + optional bytes payment_preimage = 11; + optional string message = 12; +} + +message SendonionFirst_hop { + bytes id = 1; + Amount amount_msat = 2; + uint32 delay = 3; +} + +message ListsendpaysRequest { + // ListSendPays.status + enum ListsendpaysStatus { + PENDING = 0; + COMPLETE = 1; + FAILED = 2; + } + optional string bolt11 = 1; + optional bytes payment_hash = 2; + optional ListsendpaysStatus status = 3; +} + +message ListsendpaysResponse { + repeated ListsendpaysPayments payments = 1; +} + +message ListsendpaysPayments { + // ListSendPays.payments[].status + enum ListsendpaysPaymentsStatus { + PENDING = 0; + FAILED = 1; + COMPLETE = 2; + } + uint64 id = 1; + optional uint64 groupid = 2; + bytes payment_hash = 3; + ListsendpaysPaymentsStatus status = 4; + optional Amount amount_msat = 5; + optional bytes destination = 6; + uint64 created_at = 7; + Amount amount_sent_msat = 8; + optional string label = 9; + optional string bolt11 = 10; + optional string bolt12 = 11; + optional bytes payment_preimage = 12; + optional bytes erroronion = 13; +} + +message ListtransactionsRequest { +} + +message ListtransactionsResponse { + repeated ListtransactionsTransactions transactions = 1; +} + +message ListtransactionsTransactions { + bytes hash = 1; + bytes rawtx = 2; + uint32 blockheight = 3; + uint32 txindex = 4; + optional string channel = 6; + uint32 locktime = 7; + uint32 version = 8; + repeated ListtransactionsTransactionsInputs inputs = 9; + repeated ListtransactionsTransactionsOutputs outputs = 10; +} + +message ListtransactionsTransactionsInputs { + // ListTransactions.transactions[].inputs[].type + enum ListtransactionsTransactionsInputsType { + THEIRS = 0; + DEPOSIT = 1; + WITHDRAW = 2; + CHANNEL_FUNDING = 3; + CHANNEL_MUTUAL_CLOSE = 4; + CHANNEL_UNILATERAL_CLOSE = 5; + CHANNEL_SWEEP = 6; + CHANNEL_HTLC_SUCCESS = 7; + CHANNEL_HTLC_TIMEOUT = 8; + CHANNEL_PENALTY = 9; + CHANNEL_UNILATERAL_CHEAT = 10; + } + bytes txid = 1; + uint32 index = 2; + uint32 sequence = 3; + optional ListtransactionsTransactionsInputsType item_type = 4; + optional string channel = 5; +} + +message ListtransactionsTransactionsOutputs { + // ListTransactions.transactions[].outputs[].type + enum ListtransactionsTransactionsOutputsType { + THEIRS = 0; + DEPOSIT = 1; + WITHDRAW = 2; + CHANNEL_FUNDING = 3; + CHANNEL_MUTUAL_CLOSE = 4; + CHANNEL_UNILATERAL_CLOSE = 5; + CHANNEL_SWEEP = 6; + CHANNEL_HTLC_SUCCESS = 7; + CHANNEL_HTLC_TIMEOUT = 8; + CHANNEL_PENALTY = 9; + CHANNEL_UNILATERAL_CHEAT = 10; + } + uint32 index = 1; + Amount msat = 2; + bytes scriptPubKey = 3; + optional ListtransactionsTransactionsOutputsType item_type = 4; + optional string channel = 5; +} + +message PayRequest { + string bolt11 = 1; + optional Amount msatoshi = 2; + optional string label = 3; + optional float riskfactor = 8; + optional float maxfeepercent = 4; + optional uint32 retry_for = 5; + optional uint32 maxdelay = 6; + optional float exemptfee = 7; +} + +message PayResponse { + // Pay.status + enum PayStatus { + COMPLETE = 0; + PENDING = 1; + FAILED = 2; + } + bytes payment_preimage = 1; + optional bytes destination = 2; + bytes payment_hash = 3; + sint64 created_at = 4; + uint32 parts = 5; + Amount amount_msat = 6; + Amount amount_sent_msat = 7; + optional string warning_partial_completion = 8; + PayStatus status = 9; +} + +message ListnodesRequest { + optional bytes id = 1; +} + +message ListnodesResponse { + repeated ListnodesNodes nodes = 1; +} + +message ListnodesNodes { + bytes nodeid = 1; + optional uint32 last_timestamp = 2; + optional string alias = 3; + optional bytes color = 4; + optional bytes features = 5; + repeated ListnodesNodesAddresses addresses = 6; +} + +message ListnodesNodesAddresses { + // ListNodes.nodes[].addresses[].type + enum ListnodesNodesAddressesType { + DNS = 0; + IPV4 = 1; + IPV6 = 2; + TORV2 = 3; + TORV3 = 4; + WEBSOCKET = 5; + } + ListnodesNodesAddressesType item_type = 1; + uint32 port = 2; + optional string address = 3; +} + +message WaitanyinvoiceRequest { + optional sint64 lastpay_index = 1; + optional sint64 timeout = 2; +} + +message WaitanyinvoiceResponse { + // WaitAnyInvoice.status + enum WaitanyinvoiceStatus { + PAID = 0; + EXPIRED = 1; + } + string label = 1; + string description = 2; + bytes payment_hash = 3; + WaitanyinvoiceStatus status = 4; + uint64 expires_at = 5; + optional Amount amount_msat = 6; + optional string bolt11 = 7; + optional string bolt12 = 8; + optional uint64 pay_index = 9; + optional Amount amount_received_msat = 10; + optional uint64 paid_at = 11; + optional bytes payment_preimage = 12; +} + +message WaitinvoiceRequest { + string label = 1; +} + +message WaitinvoiceResponse { + // WaitInvoice.status + enum WaitinvoiceStatus { + PAID = 0; + EXPIRED = 1; + } + string label = 1; + string description = 2; + bytes payment_hash = 3; + WaitinvoiceStatus status = 4; + uint64 expires_at = 5; + optional Amount amount_msat = 6; + optional string bolt11 = 7; + optional string bolt12 = 8; + optional uint64 pay_index = 9; + optional Amount amount_received_msat = 10; + optional uint64 paid_at = 11; + optional bytes payment_preimage = 12; +} + +message WaitsendpayRequest { + bytes payment_hash = 1; + optional uint32 partid = 2; + optional uint32 timeout = 3; +} + +message WaitsendpayResponse { + // WaitSendPay.status + enum WaitsendpayStatus { + COMPLETE = 0; + } + uint64 id = 1; + optional uint64 groupid = 2; + bytes payment_hash = 3; + WaitsendpayStatus status = 4; + optional Amount amount_msat = 5; + optional bytes destination = 6; + uint64 created_at = 7; + Amount amount_sent_msat = 8; + optional string label = 9; + optional uint64 partid = 10; + optional string bolt11 = 11; + optional string bolt12 = 12; + optional bytes payment_preimage = 13; +} + +message NewaddrRequest { + // NewAddr.addresstype + enum NewaddrAddresstype { + BECH32 = 0; + P2SH_SEGWIT = 1; + } + optional NewaddrAddresstype addresstype = 1; +} + +message NewaddrResponse { + optional string bech32 = 1; + optional string p2sh_segwit = 2; +} + +message WithdrawRequest { + bytes destination = 1; + optional Amount satoshi = 2; + optional uint32 minconf = 3; + repeated Utxo utxos = 4; +} + +message WithdrawResponse { + bytes tx = 1; + bytes txid = 2; + string psbt = 3; +} + +message KeysendRequest { + bytes destination = 1; + Amount msatoshi = 2; + optional string label = 3; + optional float maxfeepercent = 4; + optional sint64 retry_for = 5; + optional sint64 maxdelay = 6; + optional Amount exemptfee = 7; +} + +message KeysendResponse { + // KeySend.status + enum KeysendStatus { + COMPLETE = 0; + } + bytes payment_preimage = 1; + optional bytes destination = 2; + bytes payment_hash = 3; + sint64 created_at = 4; + uint32 parts = 5; + Amount amount_msat = 6; + Amount amount_sent_msat = 7; + optional string warning_partial_completion = 8; + KeysendStatus status = 9; +} diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index 0b1064c55977..bbcde1c7d2ae 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -24,4 +24,9 @@ enum ChannelState { DualopendAwaitingLockin = 10; } -message ChannelStateChangeCause {} \ No newline at end of file +message ChannelStateChangeCause {} + +message Utxo { + bytes txid = 1; + uint32 outnum = 2; +} \ No newline at end of file diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 797b5bb4cb74..58b78b3f9449 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -13,8 +13,8 @@ impl From<&responses::GetinfoAddress> for pb::GetinfoAddress { fn from(c: &responses::GetinfoAddress) -> Self { Self { item_type: c.item_type as i32, - port: c.port.into(), - address: c.address.clone(), + port: c.port.into(), // Rule #2 for type u16 + address: c.address.clone(), // Rule #2 for type string? } } } @@ -24,9 +24,9 @@ impl From<&responses::GetinfoBinding> for pb::GetinfoBinding { fn from(c: &responses::GetinfoBinding) -> Self { Self { item_type: c.item_type as i32, - address: c.address.clone(), - port: c.port.map(|v| v.into()), - socket: c.socket.clone(), + address: c.address.clone(), // Rule #2 for type string? + port: c.port.map(|v| v.into()), // Rule #2 for type u16? + socket: c.socket.clone(), // Rule #2 for type string? } } } @@ -35,22 +35,22 @@ impl From<&responses::GetinfoBinding> for pb::GetinfoBinding { impl From<&responses::GetinfoResponse> for pb::GetinfoResponse { fn from(c: &responses::GetinfoResponse) -> Self { Self { - id: hex::decode(&c.id).unwrap(), - alias: c.alias.clone(), - color: hex::decode(&c.color).unwrap(), - num_peers: c.num_peers.clone(), - num_pending_channels: c.num_pending_channels.clone(), - num_active_channels: c.num_active_channels.clone(), - num_inactive_channels: c.num_inactive_channels.clone(), - version: c.version.clone(), - lightning_dir: c.lightning_dir.clone(), - blockheight: c.blockheight.clone(), - network: c.network.clone(), - fees_collected_msat: Some(c.fees_collected_msat.into()), + id: hex::decode(&c.id).unwrap(), // Rule #2 for type pubkey + alias: c.alias.clone(), // Rule #2 for type string + color: hex::decode(&c.color).unwrap(), // Rule #2 for type hex + num_peers: c.num_peers.clone(), // Rule #2 for type u32 + num_pending_channels: c.num_pending_channels.clone(), // Rule #2 for type u32 + num_active_channels: c.num_active_channels.clone(), // Rule #2 for type u32 + num_inactive_channels: c.num_inactive_channels.clone(), // Rule #2 for type u32 + version: c.version.clone(), // Rule #2 for type string + lightning_dir: c.lightning_dir.clone(), // Rule #2 for type string + blockheight: c.blockheight.clone(), // Rule #2 for type u32 + network: c.network.clone(), // Rule #2 for type string + fees_collected_msat: Some(c.fees_collected_msat.into()), // Rule #2 for type msat address: c.address.iter().map(|i| i.into()).collect(), binding: c.binding.iter().map(|i| i.into()).collect(), - warning_bitcoind_sync: c.warning_bitcoind_sync.clone(), - warning_lightningd_sync: c.warning_lightningd_sync.clone(), + warning_bitcoind_sync: c.warning_bitcoind_sync.clone(), // Rule #2 for type string? + warning_lightningd_sync: c.warning_lightningd_sync.clone(), // Rule #2 for type string? } } } @@ -60,12 +60,12 @@ impl From<&responses::ListpeersPeersLog> for pb::ListpeersPeersLog { fn from(c: &responses::ListpeersPeersLog) -> Self { Self { item_type: c.item_type as i32, - num_skipped: c.num_skipped.clone(), - time: c.time.clone(), - source: c.source.clone(), - log: c.log.clone(), - node_id: c.node_id.as_ref().map(|v| hex::decode(&v).unwrap()), - data: c.data.as_ref().map(|v| hex::decode(&v).unwrap()), + num_skipped: c.num_skipped.clone(), // Rule #2 for type u32? + time: c.time.clone(), // Rule #2 for type string? + source: c.source.clone(), // Rule #2 for type string? + log: c.log.clone(), // Rule #2 for type string? + node_id: c.node_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + data: c.data.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } } @@ -74,12 +74,12 @@ impl From<&responses::ListpeersPeersLog> for pb::ListpeersPeersLog { impl From<&responses::ListpeersPeersChannelsInflight> for pb::ListpeersPeersChannelsInflight { fn from(c: &responses::ListpeersPeersChannelsInflight) -> Self { Self { - funding_txid: hex::decode(&c.funding_txid).unwrap(), - funding_outnum: c.funding_outnum.clone(), - feerate: c.feerate.clone(), - total_funding_msat: Some(c.total_funding_msat.into()), - our_funding_msat: Some(c.our_funding_msat.into()), - scratch_txid: hex::decode(&c.scratch_txid).unwrap(), + funding_txid: hex::decode(&c.funding_txid).unwrap(), // Rule #2 for type txid + funding_outnum: c.funding_outnum.clone(), // Rule #2 for type u32 + feerate: c.feerate.clone(), // Rule #2 for type string + total_funding_msat: Some(c.total_funding_msat.into()), // Rule #2 for type msat + our_funding_msat: Some(c.our_funding_msat.into()), // Rule #2 for type msat + scratch_txid: hex::decode(&c.scratch_txid).unwrap(), // Rule #2 for type txid } } } @@ -89,12 +89,12 @@ impl From<&responses::ListpeersPeersChannelsHtlcs> for pb::ListpeersPeersChannel fn from(c: &responses::ListpeersPeersChannelsHtlcs) -> Self { Self { direction: c.direction as i32, - id: c.id.clone(), - amount_msat: Some(c.amount_msat.into()), - expiry: c.expiry.clone(), - payment_hash: hex::decode(&c.payment_hash).unwrap(), - local_trimmed: c.local_trimmed.clone(), - status: c.status.clone(), + id: c.id.clone(), // Rule #2 for type u64 + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + expiry: c.expiry.clone(), // Rule #2 for type u32 + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + local_trimmed: c.local_trimmed.clone(), // Rule #2 for type boolean? + status: c.status.clone(), // Rule #2 for type string? state: c.state as i32, } } @@ -105,49 +105,49 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { fn from(c: &responses::ListpeersPeersChannels) -> Self { Self { state: c.state as i32, - scratch_txid: c.scratch_txid.as_ref().map(|v| hex::decode(&v).unwrap()), - owner: c.owner.clone(), - short_channel_id: c.short_channel_id.clone(), - channel_id: c.channel_id.as_ref().map(|v| hex::decode(&v).unwrap()), - funding_txid: c.funding_txid.as_ref().map(|v| hex::decode(&v).unwrap()), - funding_outnum: c.funding_outnum.clone(), - initial_feerate: c.initial_feerate.clone(), - last_feerate: c.last_feerate.clone(), - next_feerate: c.next_feerate.clone(), - next_fee_step: c.next_fee_step.clone(), + scratch_txid: c.scratch_txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? + owner: c.owner.clone(), // Rule #2 for type string? + short_channel_id: c.short_channel_id.clone(), // Rule #2 for type short_channel_id? + channel_id: c.channel_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + funding_txid: c.funding_txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? + funding_outnum: c.funding_outnum.clone(), // Rule #2 for type u32? + initial_feerate: c.initial_feerate.clone(), // Rule #2 for type string? + last_feerate: c.last_feerate.clone(), // Rule #2 for type string? + next_feerate: c.next_feerate.clone(), // Rule #2 for type string? + next_fee_step: c.next_fee_step.clone(), // Rule #2 for type u32? inflight: c.inflight.iter().map(|i| i.into()).collect(), - close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), - private: c.private.clone(), + close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + private: c.private.clone(), // Rule #2 for type boolean? opener: c.opener as i32, closer: c.closer.map(|v| v as i32), features: c.features.iter().map(|i| i.into()).collect(), - to_us_msat: c.to_us_msat.map(|f| f.into()), - min_to_us_msat: c.min_to_us_msat.map(|f| f.into()), - max_to_us_msat: c.max_to_us_msat.map(|f| f.into()), - total_msat: c.total_msat.map(|f| f.into()), - fee_base_msat: c.fee_base_msat.map(|f| f.into()), - fee_proportional_millionths: c.fee_proportional_millionths.clone(), - dust_limit_msat: c.dust_limit_msat.map(|f| f.into()), - max_total_htlc_in_msat: c.max_total_htlc_in_msat.map(|f| f.into()), - their_reserve_msat: c.their_reserve_msat.map(|f| f.into()), - our_reserve_msat: c.our_reserve_msat.map(|f| f.into()), - spendable_msat: c.spendable_msat.map(|f| f.into()), - receivable_msat: c.receivable_msat.map(|f| f.into()), - minimum_htlc_in_msat: c.minimum_htlc_in_msat.map(|f| f.into()), - their_to_self_delay: c.their_to_self_delay.clone(), - our_to_self_delay: c.our_to_self_delay.clone(), - max_accepted_htlcs: c.max_accepted_htlcs.clone(), + to_us_msat: c.to_us_msat.map(|f| f.into()), // Rule #2 for type msat? + min_to_us_msat: c.min_to_us_msat.map(|f| f.into()), // Rule #2 for type msat? + max_to_us_msat: c.max_to_us_msat.map(|f| f.into()), // Rule #2 for type msat? + total_msat: c.total_msat.map(|f| f.into()), // Rule #2 for type msat? + fee_base_msat: c.fee_base_msat.map(|f| f.into()), // Rule #2 for type msat? + fee_proportional_millionths: c.fee_proportional_millionths.clone(), // Rule #2 for type u32? + dust_limit_msat: c.dust_limit_msat.map(|f| f.into()), // Rule #2 for type msat? + max_total_htlc_in_msat: c.max_total_htlc_in_msat.map(|f| f.into()), // Rule #2 for type msat? + their_reserve_msat: c.their_reserve_msat.map(|f| f.into()), // Rule #2 for type msat? + our_reserve_msat: c.our_reserve_msat.map(|f| f.into()), // Rule #2 for type msat? + spendable_msat: c.spendable_msat.map(|f| f.into()), // Rule #2 for type msat? + receivable_msat: c.receivable_msat.map(|f| f.into()), // Rule #2 for type msat? + minimum_htlc_in_msat: c.minimum_htlc_in_msat.map(|f| f.into()), // Rule #2 for type msat? + their_to_self_delay: c.their_to_self_delay.clone(), // Rule #2 for type u32? + our_to_self_delay: c.our_to_self_delay.clone(), // Rule #2 for type u32? + max_accepted_htlcs: c.max_accepted_htlcs.clone(), // Rule #2 for type u32? status: c.status.iter().map(|i| i.into()).collect(), - in_payments_offered: c.in_payments_offered.clone(), - in_offered_msat: c.in_offered_msat.map(|f| f.into()), - in_payments_fulfilled: c.in_payments_fulfilled.clone(), - in_fulfilled_msat: c.in_fulfilled_msat.map(|f| f.into()), - out_payments_offered: c.out_payments_offered.clone(), - out_offered_msat: c.out_offered_msat.map(|f| f.into()), - out_payments_fulfilled: c.out_payments_fulfilled.clone(), - out_fulfilled_msat: c.out_fulfilled_msat.map(|f| f.into()), + in_payments_offered: c.in_payments_offered.clone(), // Rule #2 for type u64? + in_offered_msat: c.in_offered_msat.map(|f| f.into()), // Rule #2 for type msat? + in_payments_fulfilled: c.in_payments_fulfilled.clone(), // Rule #2 for type u64? + in_fulfilled_msat: c.in_fulfilled_msat.map(|f| f.into()), // Rule #2 for type msat? + out_payments_offered: c.out_payments_offered.clone(), // Rule #2 for type u64? + out_offered_msat: c.out_offered_msat.map(|f| f.into()), // Rule #2 for type msat? + out_payments_fulfilled: c.out_payments_fulfilled.clone(), // Rule #2 for type u64? + out_fulfilled_msat: c.out_fulfilled_msat.map(|f| f.into()), // Rule #2 for type msat? htlcs: c.htlcs.iter().map(|i| i.into()).collect(), - close_to_addr: c.close_to_addr.clone(), + close_to_addr: c.close_to_addr.clone(), // Rule #2 for type string? } } } @@ -156,12 +156,12 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { fn from(c: &responses::ListpeersPeers) -> Self { Self { - id: hex::decode(&c.id).unwrap(), - connected: c.connected.clone(), + id: hex::decode(&c.id).unwrap(), // Rule #2 for type pubkey + connected: c.connected.clone(), // Rule #2 for type boolean log: c.log.iter().map(|i| i.into()).collect(), channels: c.channels.iter().map(|i| i.into()).collect(), netaddr: c.netaddr.iter().map(|i| i.into()).collect(), - features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), + features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } } @@ -179,14 +179,14 @@ impl From<&responses::ListpeersResponse> for pb::ListpeersResponse { impl From<&responses::ListfundsOutputs> for pb::ListfundsOutputs { fn from(c: &responses::ListfundsOutputs) -> Self { Self { - txid: hex::decode(&c.txid).unwrap(), - output: c.output.clone(), - amount_msat: Some(c.amount_msat.into()), - scriptpubkey: hex::decode(&c.scriptpubkey).unwrap(), - address: c.address.clone(), - redeemscript: c.redeemscript.as_ref().map(|v| hex::decode(&v).unwrap()), + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + output: c.output.clone(), // Rule #2 for type u32 + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + scriptpubkey: hex::decode(&c.scriptpubkey).unwrap(), // Rule #2 for type hex + address: c.address.clone(), // Rule #2 for type string? + redeemscript: c.redeemscript.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? status: c.status as i32, - blockheight: c.blockheight.clone(), + blockheight: c.blockheight.clone(), // Rule #2 for type u32? } } } @@ -195,14 +195,14 @@ impl From<&responses::ListfundsOutputs> for pb::ListfundsOutputs { impl From<&responses::ListfundsChannels> for pb::ListfundsChannels { fn from(c: &responses::ListfundsChannels) -> Self { Self { - peer_id: hex::decode(&c.peer_id).unwrap(), - our_amount_msat: Some(c.our_amount_msat.into()), - amount_msat: Some(c.amount_msat.into()), - funding_txid: hex::decode(&c.funding_txid).unwrap(), - funding_output: c.funding_output.clone(), - connected: c.connected.clone(), + peer_id: hex::decode(&c.peer_id).unwrap(), // Rule #2 for type pubkey + our_amount_msat: Some(c.our_amount_msat.into()), // Rule #2 for type msat + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + funding_txid: hex::decode(&c.funding_txid).unwrap(), // Rule #2 for type txid + funding_output: c.funding_output.clone(), // Rule #2 for type u32 + connected: c.connected.clone(), // Rule #2 for type boolean state: c.state as i32, - short_channel_id: c.short_channel_id.clone(), + short_channel_id: c.short_channel_id.clone(), // Rule #2 for type short_channel_id? } } } @@ -217,25 +217,47 @@ impl From<&responses::ListfundsResponse> for pb::ListfundsResponse { } } +#[allow(unused_variables)] +impl From<&responses::SendpayResponse> for pb::SendpayResponse { + fn from(c: &responses::SendpayResponse) -> Self { + Self { + id: c.id.clone(), // Rule #2 for type u64 + groupid: c.groupid.clone(), // Rule #2 for type u64? + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + status: c.status as i32, + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + created_at: c.created_at.clone(), // Rule #2 for type u64 + amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat + label: c.label.clone(), // Rule #2 for type string? + partid: c.partid.clone(), // Rule #2 for type u64? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + message: c.message.clone(), // Rule #2 for type string? + } + } +} + #[allow(unused_variables)] impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { fn from(c: &responses::ListchannelsChannels) -> Self { Self { - source: hex::decode(&c.source).unwrap(), - destination: hex::decode(&c.destination).unwrap(), - short_channel_id: c.short_channel_id.clone(), - public: c.public.clone(), - amount_msat: Some(c.amount_msat.into()), - message_flags: c.message_flags.into(), - channel_flags: c.channel_flags.into(), - active: c.active.clone(), - last_update: c.last_update.clone(), - base_fee_millisatoshi: c.base_fee_millisatoshi.clone(), - fee_per_millionth: c.fee_per_millionth.clone(), - delay: c.delay.clone(), - htlc_minimum_msat: Some(c.htlc_minimum_msat.into()), - htlc_maximum_msat: c.htlc_maximum_msat.map(|f| f.into()), - features: hex::decode(&c.features).unwrap(), + source: hex::decode(&c.source).unwrap(), // Rule #2 for type pubkey + destination: hex::decode(&c.destination).unwrap(), // Rule #2 for type pubkey + short_channel_id: c.short_channel_id.clone(), // Rule #2 for type short_channel_id + public: c.public.clone(), // Rule #2 for type boolean + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + message_flags: c.message_flags.into(), // Rule #2 for type u8 + channel_flags: c.channel_flags.into(), // Rule #2 for type u8 + active: c.active.clone(), // Rule #2 for type boolean + last_update: c.last_update.clone(), // Rule #2 for type u32 + base_fee_millisatoshi: c.base_fee_millisatoshi.clone(), // Rule #2 for type u32 + fee_per_millionth: c.fee_per_millionth.clone(), // Rule #2 for type u32 + delay: c.delay.clone(), // Rule #2 for type u32 + htlc_minimum_msat: Some(c.htlc_minimum_msat.into()), // Rule #2 for type msat + htlc_maximum_msat: c.htlc_maximum_msat.map(|f| f.into()), // Rule #2 for type msat? + features: hex::decode(&c.features).unwrap(), // Rule #2 for type hex } } } @@ -261,9 +283,9 @@ impl From<&responses::AddgossipResponse> for pb::AddgossipResponse { impl From<&responses::AutocleaninvoiceResponse> for pb::AutocleaninvoiceResponse { fn from(c: &responses::AutocleaninvoiceResponse) -> Self { Self { - enabled: c.enabled.clone(), - expired_by: c.expired_by.clone(), - cycle_seconds: c.cycle_seconds.clone(), + enabled: c.enabled.clone(), // Rule #2 for type boolean + expired_by: c.expired_by.clone(), // Rule #2 for type u64? + cycle_seconds: c.cycle_seconds.clone(), // Rule #2 for type u64? } } } @@ -272,8 +294,8 @@ impl From<&responses::AutocleaninvoiceResponse> for pb::AutocleaninvoiceResponse impl From<&responses::CheckmessageResponse> for pb::CheckmessageResponse { fn from(c: &responses::CheckmessageResponse) -> Self { Self { - verified: c.verified.clone(), - pubkey: c.pubkey.as_ref().map(|v| hex::decode(&v).unwrap()), + verified: c.verified.clone(), // Rule #2 for type boolean + pubkey: c.pubkey.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? } } } @@ -283,8 +305,8 @@ impl From<&responses::CloseResponse> for pb::CloseResponse { fn from(c: &responses::CloseResponse) -> Self { Self { item_type: c.item_type as i32, - tx: c.tx.as_ref().map(|v| hex::decode(&v).unwrap()), - txid: c.txid.as_ref().map(|v| hex::decode(&v).unwrap()), + tx: c.tx.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + txid: c.txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? } } } @@ -293,8 +315,8 @@ impl From<&responses::CloseResponse> for pb::CloseResponse { impl From<&responses::ConnectResponse> for pb::ConnectResponse { fn from(c: &responses::ConnectResponse) -> Self { Self { - id: hex::decode(&c.id).unwrap(), - features: hex::decode(&c.features).unwrap(), + id: hex::decode(&c.id).unwrap(), // Rule #2 for type pubkey + features: hex::decode(&c.features).unwrap(), // Rule #2 for type hex direction: c.direction as i32, } } @@ -304,20 +326,20 @@ impl From<&responses::ConnectResponse> for pb::ConnectResponse { impl From<&responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse { fn from(c: &responses::CreateinvoiceResponse) -> Self { Self { - label: c.label.clone(), - bolt11: c.bolt11.clone(), - bolt12: c.bolt12.clone(), - payment_hash: hex::decode(&c.payment_hash).unwrap(), - amount_msat: c.amount_msat.map(|f| f.into()), + label: c.label.clone(), // Rule #2 for type string + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? status: c.status as i32, - description: c.description.clone(), - expires_at: c.expires_at.clone(), - pay_index: c.pay_index.clone(), - amount_received_msat: c.amount_received_msat.map(|f| f.into()), - paid_at: c.paid_at.clone(), - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), - local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), - payer_note: c.payer_note.clone(), + description: c.description.clone(), // Rule #2 for type string + expires_at: c.expires_at.clone(), // Rule #2 for type u64 + pay_index: c.pay_index.clone(), // Rule #2 for type u64? + amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? + paid_at: c.paid_at.clone(), // Rule #2 for type u64? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payer_note: c.payer_note.clone(), // Rule #2 for type string? } } } @@ -327,9 +349,19 @@ impl From<&responses::DatastoreResponse> for pb::DatastoreResponse { fn from(c: &responses::DatastoreResponse) -> Self { Self { key: c.key.iter().map(|i| i.into()).collect(), - generation: c.generation.clone(), - hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), - string: c.string.clone(), + generation: c.generation.clone(), // Rule #2 for type u64? + hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + string: c.string.clone(), // Rule #2 for type string? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::CreateonionResponse> for pb::CreateonionResponse { + fn from(c: &responses::CreateonionResponse) -> Self { + Self { + onion: hex::decode(&c.onion).unwrap(), // Rule #2 for type hex + shared_secrets: c.shared_secrets.iter().map(|i| hex::decode(i).unwrap()).collect(), } } } @@ -339,9 +371,9 @@ impl From<&responses::DeldatastoreResponse> for pb::DeldatastoreResponse { fn from(c: &responses::DeldatastoreResponse) -> Self { Self { key: c.key.iter().map(|i| i.into()).collect(), - generation: c.generation.clone(), - hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), - string: c.string.clone(), + generation: c.generation.clone(), // Rule #2 for type u64? + hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + string: c.string.clone(), // Rule #2 for type string? } } } @@ -358,16 +390,16 @@ impl From<&responses::DelexpiredinvoiceResponse> for pb::DelexpiredinvoiceRespon impl From<&responses::DelinvoiceResponse> for pb::DelinvoiceResponse { fn from(c: &responses::DelinvoiceResponse) -> Self { Self { - label: c.label.clone(), - bolt11: c.bolt11.clone(), - bolt12: c.bolt12.clone(), - amount_msat: c.amount_msat.map(|f| f.into()), - description: c.description.clone(), - payment_hash: hex::decode(&c.payment_hash).unwrap(), + label: c.label.clone(), // Rule #2 for type string + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + description: c.description.clone(), // Rule #2 for type string? + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex status: c.status as i32, - expires_at: c.expires_at.clone(), - local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), - payer_note: c.payer_note.clone(), + expires_at: c.expires_at.clone(), // Rule #2 for type u64 + local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payer_note: c.payer_note.clone(), // Rule #2 for type string? } } } @@ -376,15 +408,15 @@ impl From<&responses::DelinvoiceResponse> for pb::DelinvoiceResponse { impl From<&responses::InvoiceResponse> for pb::InvoiceResponse { fn from(c: &responses::InvoiceResponse) -> Self { Self { - bolt11: c.bolt11.clone(), - payment_hash: hex::decode(&c.payment_hash).unwrap(), - payment_secret: hex::decode(&c.payment_secret).unwrap(), - expires_at: c.expires_at.clone(), - warning_capacity: c.warning_capacity.clone(), - warning_offline: c.warning_offline.clone(), - warning_deadends: c.warning_deadends.clone(), - warning_private_unused: c.warning_private_unused.clone(), - warning_mpp: c.warning_mpp.clone(), + bolt11: c.bolt11.clone(), // Rule #2 for type string + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_secret: hex::decode(&c.payment_secret).unwrap(), // Rule #2 for type hex + expires_at: c.expires_at.clone(), // Rule #2 for type u64 + warning_capacity: c.warning_capacity.clone(), // Rule #2 for type string? + warning_offline: c.warning_offline.clone(), // Rule #2 for type string? + warning_deadends: c.warning_deadends.clone(), // Rule #2 for type string? + warning_private_unused: c.warning_private_unused.clone(), // Rule #2 for type string? + warning_mpp: c.warning_mpp.clone(), // Rule #2 for type string? } } } @@ -394,9 +426,9 @@ impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { fn from(c: &responses::ListdatastoreDatastore) -> Self { Self { key: c.key.iter().map(|i| i.into()).collect(), - generation: c.generation.clone(), - hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), - string: c.string.clone(), + generation: c.generation.clone(), // Rule #2 for type u64? + hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + string: c.string.clone(), // Rule #2 for type string? } } } @@ -414,20 +446,20 @@ impl From<&responses::ListdatastoreResponse> for pb::ListdatastoreResponse { impl From<&responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices { fn from(c: &responses::ListinvoicesInvoices) -> Self { Self { - label: c.label.clone(), - description: c.description.clone(), - payment_hash: hex::decode(&c.payment_hash).unwrap(), + label: c.label.clone(), // Rule #2 for type string + description: c.description.clone(), // Rule #2 for type string + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex status: c.status as i32, - expires_at: c.expires_at.clone(), - amount_msat: c.amount_msat.map(|f| f.into()), - bolt11: c.bolt11.clone(), - bolt12: c.bolt12.clone(), - local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), - payer_note: c.payer_note.clone(), - pay_index: c.pay_index.clone(), - amount_received_msat: c.amount_received_msat.map(|f| f.into()), - paid_at: c.paid_at.clone(), - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), + expires_at: c.expires_at.clone(), // Rule #2 for type u64 + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payer_note: c.payer_note.clone(), // Rule #2 for type string? + pay_index: c.pay_index.clone(), // Rule #2 for type u64? + amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? + paid_at: c.paid_at.clone(), // Rule #2 for type u64? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } } @@ -441,6 +473,258 @@ impl From<&responses::ListinvoicesResponse> for pb::ListinvoicesResponse { } } +#[allow(unused_variables)] +impl From<&responses::SendonionResponse> for pb::SendonionResponse { + fn from(c: &responses::SendonionResponse) -> Self { + Self { + id: c.id.clone(), // Rule #2 for type u64 + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + status: c.status as i32, + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + created_at: c.created_at.clone(), // Rule #2 for type u64 + amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat + label: c.label.clone(), // Rule #2 for type string? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + message: c.message.clone(), // Rule #2 for type string? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListsendpaysPayments> for pb::ListsendpaysPayments { + fn from(c: &responses::ListsendpaysPayments) -> Self { + Self { + id: c.id.clone(), // Rule #2 for type u64 + groupid: c.groupid.clone(), // Rule #2 for type u64? + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + status: c.status as i32, + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + created_at: c.created_at.clone(), // Rule #2 for type u64 + amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat + label: c.label.clone(), // Rule #2 for type string? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + erroronion: c.erroronion.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListsendpaysResponse> for pb::ListsendpaysResponse { + fn from(c: &responses::ListsendpaysResponse) -> Self { + Self { + payments: c.payments.iter().map(|i| i.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListtransactionsTransactionsInputs> for pb::ListtransactionsTransactionsInputs { + fn from(c: &responses::ListtransactionsTransactionsInputs) -> Self { + Self { + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + index: c.index.clone(), // Rule #2 for type u32 + sequence: c.sequence.clone(), // Rule #2 for type u32 + item_type: c.item_type.map(|v| v as i32), + channel: c.channel.clone(), // Rule #2 for type short_channel_id? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListtransactionsTransactionsOutputs> for pb::ListtransactionsTransactionsOutputs { + fn from(c: &responses::ListtransactionsTransactionsOutputs) -> Self { + Self { + index: c.index.clone(), // Rule #2 for type u32 + msat: Some(c.msat.into()), // Rule #2 for type msat + script_pub_key: hex::decode(&c.script_pub_key).unwrap(), // Rule #2 for type hex + item_type: c.item_type.map(|v| v as i32), + channel: c.channel.clone(), // Rule #2 for type short_channel_id? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListtransactionsTransactions> for pb::ListtransactionsTransactions { + fn from(c: &responses::ListtransactionsTransactions) -> Self { + Self { + hash: hex::decode(&c.hash).unwrap(), // Rule #2 for type txid + rawtx: hex::decode(&c.rawtx).unwrap(), // Rule #2 for type hex + blockheight: c.blockheight.clone(), // Rule #2 for type u32 + txindex: c.txindex.clone(), // Rule #2 for type u32 + channel: c.channel.clone(), // Rule #2 for type short_channel_id? + locktime: c.locktime.clone(), // Rule #2 for type u32 + version: c.version.clone(), // Rule #2 for type u32 + inputs: c.inputs.iter().map(|i| i.into()).collect(), + outputs: c.outputs.iter().map(|i| i.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListtransactionsResponse> for pb::ListtransactionsResponse { + fn from(c: &responses::ListtransactionsResponse) -> Self { + Self { + transactions: c.transactions.iter().map(|i| i.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::PayResponse> for pb::PayResponse { + fn from(c: &responses::PayResponse) -> Self { + Self { + payment_preimage: hex::decode(&c.payment_preimage).unwrap(), // Rule #2 for type hex + destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + created_at: c.created_at.clone(), // Rule #2 for type number + parts: c.parts.clone(), // Rule #2 for type u32 + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat + warning_partial_completion: c.warning_partial_completion.clone(), // Rule #2 for type string? + status: c.status as i32, + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListnodesNodesAddresses> for pb::ListnodesNodesAddresses { + fn from(c: &responses::ListnodesNodesAddresses) -> Self { + Self { + item_type: c.item_type as i32, + port: c.port.into(), // Rule #2 for type u16 + address: c.address.clone(), // Rule #2 for type string? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListnodesNodes> for pb::ListnodesNodes { + fn from(c: &responses::ListnodesNodes) -> Self { + Self { + nodeid: hex::decode(&c.nodeid).unwrap(), // Rule #2 for type pubkey + last_timestamp: c.last_timestamp.clone(), // Rule #2 for type u32? + alias: c.alias.clone(), // Rule #2 for type string? + color: c.color.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + addresses: c.addresses.iter().map(|i| i.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListnodesResponse> for pb::ListnodesResponse { + fn from(c: &responses::ListnodesResponse) -> Self { + Self { + nodes: c.nodes.iter().map(|i| i.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::WaitanyinvoiceResponse> for pb::WaitanyinvoiceResponse { + fn from(c: &responses::WaitanyinvoiceResponse) -> Self { + Self { + label: c.label.clone(), // Rule #2 for type string + description: c.description.clone(), // Rule #2 for type string + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + status: c.status as i32, + expires_at: c.expires_at.clone(), // Rule #2 for type u64 + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + pay_index: c.pay_index.clone(), // Rule #2 for type u64? + amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? + paid_at: c.paid_at.clone(), // Rule #2 for type u64? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::WaitinvoiceResponse> for pb::WaitinvoiceResponse { + fn from(c: &responses::WaitinvoiceResponse) -> Self { + Self { + label: c.label.clone(), // Rule #2 for type string + description: c.description.clone(), // Rule #2 for type string + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + status: c.status as i32, + expires_at: c.expires_at.clone(), // Rule #2 for type u64 + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + pay_index: c.pay_index.clone(), // Rule #2 for type u64? + amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? + paid_at: c.paid_at.clone(), // Rule #2 for type u64? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::WaitsendpayResponse> for pb::WaitsendpayResponse { + fn from(c: &responses::WaitsendpayResponse) -> Self { + Self { + id: c.id.clone(), // Rule #2 for type u64 + groupid: c.groupid.clone(), // Rule #2 for type u64? + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + status: c.status as i32, + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + created_at: c.created_at.clone(), // Rule #2 for type u64 + amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat + label: c.label.clone(), // Rule #2 for type string? + partid: c.partid.clone(), // Rule #2 for type u64? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::NewaddrResponse> for pb::NewaddrResponse { + fn from(c: &responses::NewaddrResponse) -> Self { + Self { + bech32: c.bech32.clone(), // Rule #2 for type string? + p2sh_segwit: c.p2sh_segwit.clone(), // Rule #2 for type string? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::WithdrawResponse> for pb::WithdrawResponse { + fn from(c: &responses::WithdrawResponse) -> Self { + Self { + tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + psbt: c.psbt.clone(), // Rule #2 for type string + } + } +} + +#[allow(unused_variables)] +impl From<&responses::KeysendResponse> for pb::KeysendResponse { + fn from(c: &responses::KeysendResponse) -> Self { + Self { + payment_preimage: hex::decode(&c.payment_preimage).unwrap(), // Rule #2 for type hex + destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + created_at: c.created_at.clone(), // Rule #2 for type number + parts: c.parts.clone(), // Rule #2 for type u32 + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat + warning_partial_completion: c.warning_partial_completion.clone(), // Rule #2 for type string? + status: c.status as i32, + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -453,8 +737,8 @@ impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { impl From<&pb::ListpeersRequest> for requests::ListpeersRequest { fn from(c: &pb::ListpeersRequest) -> Self { Self { - id: c.id.clone().map(|v| hex::encode(v)), - level: c.level.clone(), + id: c.id.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? + level: c.level.clone(), // Rule #1 for type string? } } } @@ -463,7 +747,34 @@ impl From<&pb::ListpeersRequest> for requests::ListpeersRequest { impl From<&pb::ListfundsRequest> for requests::ListfundsRequest { fn from(c: &pb::ListfundsRequest) -> Self { Self { - spent: c.spent.clone(), + spent: c.spent.clone(), // Rule #1 for type boolean? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::SendpayRoute> for requests::SendpayRoute { + fn from(c: &pb::SendpayRoute) -> Self { + Self { + msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + id: hex::encode(&c.id), // Rule #1 for type pubkey + delay: c.delay as u16, // Rule #1 for type u16 + channel: c.channel.clone(), // Rule #1 for type short_channel_id + } + } +} + +#[allow(unused_variables)] +impl From<&pb::SendpayRequest> for requests::SendpayRequest { + fn from(c: &pb::SendpayRequest) -> Self { + Self { + route: c.route.iter().map(|s| s.into()).collect(), + payment_hash: hex::encode(&c.payment_hash), // Rule #1 for type hex + label: c.label.clone(), // Rule #1 for type string? + msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + bolt11: c.bolt11.clone(), // Rule #1 for type string? + payment_secret: c.payment_secret.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? } } } @@ -472,9 +783,9 @@ impl From<&pb::ListfundsRequest> for requests::ListfundsRequest { impl From<&pb::ListchannelsRequest> for requests::ListchannelsRequest { fn from(c: &pb::ListchannelsRequest) -> Self { Self { - short_channel_id: c.short_channel_id.clone(), - source: c.source.clone().map(|v| hex::encode(v)), - destination: c.destination.clone().map(|v| hex::encode(v)), + short_channel_id: c.short_channel_id.clone(), // Rule #1 for type short_channel_id? + source: c.source.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? + destination: c.destination.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? } } } @@ -483,7 +794,7 @@ impl From<&pb::ListchannelsRequest> for requests::ListchannelsRequest { impl From<&pb::AddgossipRequest> for requests::AddgossipRequest { fn from(c: &pb::AddgossipRequest) -> Self { Self { - message: hex::encode(&c.message), + message: hex::encode(&c.message), // Rule #1 for type hex } } } @@ -492,8 +803,8 @@ impl From<&pb::AddgossipRequest> for requests::AddgossipRequest { impl From<&pb::AutocleaninvoiceRequest> for requests::AutocleaninvoiceRequest { fn from(c: &pb::AutocleaninvoiceRequest) -> Self { Self { - expired_by: c.expired_by.clone(), - cycle_seconds: c.cycle_seconds.clone(), + expired_by: c.expired_by.clone(), // Rule #1 for type u64? + cycle_seconds: c.cycle_seconds.clone(), // Rule #1 for type u64? } } } @@ -502,9 +813,9 @@ impl From<&pb::AutocleaninvoiceRequest> for requests::AutocleaninvoiceRequest { impl From<&pb::CheckmessageRequest> for requests::CheckmessageRequest { fn from(c: &pb::CheckmessageRequest) -> Self { Self { - message: c.message.clone(), - zbase: c.zbase.clone(), - pubkey: c.pubkey.clone().map(|v| hex::encode(v)), + message: c.message.clone(), // Rule #1 for type string + zbase: c.zbase.clone(), // Rule #1 for type string + pubkey: c.pubkey.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? } } } @@ -513,12 +824,12 @@ impl From<&pb::CheckmessageRequest> for requests::CheckmessageRequest { impl From<&pb::CloseRequest> for requests::CloseRequest { fn from(c: &pb::CloseRequest) -> Self { Self { - id: hex::encode(&c.id), - unilateraltimeout: c.unilateraltimeout.clone(), - destination: c.destination.clone(), - fee_negotiation_step: c.fee_negotiation_step.clone(), - wrong_funding: c.wrong_funding.clone().map(|v| hex::encode(v)), - force_lease_closed: c.force_lease_closed.clone(), + id: hex::encode(&c.id), // Rule #1 for type pubkey + unilateraltimeout: c.unilateraltimeout.clone(), // Rule #1 for type u32? + destination: c.destination.clone(), // Rule #1 for type string? + fee_negotiation_step: c.fee_negotiation_step.clone(), // Rule #1 for type string? + wrong_funding: c.wrong_funding.clone().map(|v| hex::encode(v)), // Rule #1 for type txid? + force_lease_closed: c.force_lease_closed.clone(), // Rule #1 for type boolean? } } } @@ -527,9 +838,9 @@ impl From<&pb::CloseRequest> for requests::CloseRequest { impl From<&pb::ConnectRequest> for requests::ConnectRequest { fn from(c: &pb::ConnectRequest) -> Self { Self { - id: hex::encode(&c.id), - host: c.host.clone(), - port: c.port.map(|v| v as u16), + id: hex::encode(&c.id), // Rule #1 for type pubkey + host: c.host.clone(), // Rule #1 for type string? + port: c.port.map(|v| v as u16), // Rule #1 for type u16? } } } @@ -538,9 +849,9 @@ impl From<&pb::ConnectRequest> for requests::ConnectRequest { impl From<&pb::CreateinvoiceRequest> for requests::CreateinvoiceRequest { fn from(c: &pb::CreateinvoiceRequest) -> Self { Self { - invstring: c.invstring.clone(), - label: c.label.clone(), - preimage: hex::encode(&c.preimage), + invstring: c.invstring.clone(), // Rule #1 for type string + label: c.label.clone(), // Rule #1 for type string + preimage: hex::encode(&c.preimage), // Rule #1 for type hex } } } @@ -550,9 +861,31 @@ impl From<&pb::DatastoreRequest> for requests::DatastoreRequest { fn from(c: &pb::DatastoreRequest) -> Self { Self { key: c.key.iter().map(|s| s.into()).collect(), - hex: c.hex.clone().map(|v| hex::encode(v)), + hex: c.hex.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? mode: c.mode.map(|v| v.try_into().unwrap()), - generation: c.generation.clone(), + generation: c.generation.clone(), // Rule #1 for type u64? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::CreateonionHops> for requests::CreateonionHops { + fn from(c: &pb::CreateonionHops) -> Self { + Self { + pubkey: hex::encode(&c.pubkey), // Rule #1 for type pubkey + payload: hex::encode(&c.payload), // Rule #1 for type hex + } + } +} + +#[allow(unused_variables)] +impl From<&pb::CreateonionRequest> for requests::CreateonionRequest { + fn from(c: &pb::CreateonionRequest) -> Self { + Self { + hops: c.hops.iter().map(|s| s.into()).collect(), + assocdata: hex::encode(&c.assocdata), // Rule #1 for type hex + session_key: c.session_key.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + onion_size: c.onion_size.map(|v| v as u16), // Rule #1 for type u16? } } } @@ -562,7 +895,7 @@ impl From<&pb::DeldatastoreRequest> for requests::DeldatastoreRequest { fn from(c: &pb::DeldatastoreRequest) -> Self { Self { key: c.key.iter().map(|s| s.into()).collect(), - generation: c.generation.clone(), + generation: c.generation.clone(), // Rule #1 for type u64? } } } @@ -571,7 +904,7 @@ impl From<&pb::DeldatastoreRequest> for requests::DeldatastoreRequest { impl From<&pb::DelexpiredinvoiceRequest> for requests::DelexpiredinvoiceRequest { fn from(c: &pb::DelexpiredinvoiceRequest) -> Self { Self { - maxexpirytime: c.maxexpirytime.clone(), + maxexpirytime: c.maxexpirytime.clone(), // Rule #1 for type u32 } } } @@ -580,7 +913,7 @@ impl From<&pb::DelexpiredinvoiceRequest> for requests::DelexpiredinvoiceRequest impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { fn from(c: &pb::DelinvoiceRequest) -> Self { Self { - label: c.label.clone(), + label: c.label.clone(), // Rule #1 for type string status: c.status.try_into().unwrap(), } } @@ -590,12 +923,12 @@ impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { impl From<&pb::InvoiceRequest> for requests::InvoiceRequest { fn from(c: &pb::InvoiceRequest) -> Self { Self { - msatoshi: c.msatoshi.as_ref().unwrap().into(), - description: c.description.clone(), - label: c.label.clone(), + msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + description: c.description.clone(), // Rule #1 for type string + label: c.label.clone(), // Rule #1 for type string fallbacks: c.fallbacks.iter().map(|s| s.into()).collect(), - preimage: c.preimage.clone().map(|v| hex::encode(v)), - cltv: c.cltv.clone(), + preimage: c.preimage.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + cltv: c.cltv.clone(), // Rule #1 for type u32? } } } @@ -613,10 +946,129 @@ impl From<&pb::ListdatastoreRequest> for requests::ListdatastoreRequest { impl From<&pb::ListinvoicesRequest> for requests::ListinvoicesRequest { fn from(c: &pb::ListinvoicesRequest) -> Self { Self { - label: c.label.clone(), - invstring: c.invstring.clone(), - payment_hash: c.payment_hash.clone().map(|v| hex::encode(v)), - offer_id: c.offer_id.clone(), + label: c.label.clone(), // Rule #1 for type string? + invstring: c.invstring.clone(), // Rule #1 for type string? + payment_hash: c.payment_hash.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + offer_id: c.offer_id.clone(), // Rule #1 for type string? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::SendonionRequest> for requests::SendonionRequest { + fn from(c: &pb::SendonionRequest) -> Self { + Self { + onion: hex::encode(&c.onion), // Rule #1 for type hex + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListsendpaysRequest> for requests::ListsendpaysRequest { + fn from(c: &pb::ListsendpaysRequest) -> Self { + Self { + bolt11: c.bolt11.clone(), // Rule #1 for type string? + payment_hash: c.payment_hash.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + status: c.status.map(|v| v.try_into().unwrap()), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListtransactionsRequest> for requests::ListtransactionsRequest { + fn from(c: &pb::ListtransactionsRequest) -> Self { + Self { + } + } +} + +#[allow(unused_variables)] +impl From<&pb::PayRequest> for requests::PayRequest { + fn from(c: &pb::PayRequest) -> Self { + Self { + bolt11: c.bolt11.clone(), // Rule #1 for type string + msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + label: c.label.clone(), // Rule #1 for type string? + riskfactor: c.riskfactor.clone(), // Rule #1 for type float? + maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type f32? + retry_for: c.retry_for.map(|v| v as u16), // Rule #1 for type u16? + maxdelay: c.maxdelay.map(|v| v as u16), // Rule #1 for type u16? + exemptfee: c.exemptfee.clone(), // Rule #1 for type f32? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListnodesRequest> for requests::ListnodesRequest { + fn from(c: &pb::ListnodesRequest) -> Self { + Self { + id: c.id.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::WaitanyinvoiceRequest> for requests::WaitanyinvoiceRequest { + fn from(c: &pb::WaitanyinvoiceRequest) -> Self { + Self { + lastpay_index: c.lastpay_index.clone(), // Rule #1 for type number? + timeout: c.timeout.clone(), // Rule #1 for type number? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::WaitinvoiceRequest> for requests::WaitinvoiceRequest { + fn from(c: &pb::WaitinvoiceRequest) -> Self { + Self { + label: c.label.clone(), // Rule #1 for type string + } + } +} + +#[allow(unused_variables)] +impl From<&pb::WaitsendpayRequest> for requests::WaitsendpayRequest { + fn from(c: &pb::WaitsendpayRequest) -> Self { + Self { + payment_hash: hex::encode(&c.payment_hash), // Rule #1 for type hex + partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? + timeout: c.timeout.clone(), // Rule #1 for type u32? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::NewaddrRequest> for requests::NewaddrRequest { + fn from(c: &pb::NewaddrRequest) -> Self { + Self { + addresstype: c.addresstype.map(|v| v.try_into().unwrap()), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::WithdrawRequest> for requests::WithdrawRequest { + fn from(c: &pb::WithdrawRequest) -> Self { + Self { + destination: hex::encode(&c.destination), // Rule #1 for type pubkey + satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + minconf: c.minconf.map(|v| v as u16), // Rule #1 for type u16? + utxos: c.utxos.iter().map(|s| s.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::KeysendRequest> for requests::KeysendRequest { + fn from(c: &pb::KeysendRequest) -> Self { + Self { + destination: hex::encode(&c.destination), // Rule #1 for type pubkey + msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + label: c.label.clone(), // Rule #1 for type string? + maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type float? + retry_for: c.retry_for.clone(), // Rule #1 for type number? + maxdelay: c.maxdelay.clone(), // Rule #1 for type number? + exemptfee: c.exemptfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? } } } diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 0e5ba33ef8ab..ae8e8bc4b67c 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -1,6 +1,6 @@ tonic::include_proto!("cln"); -use cln_rpc::primitives::Amount as JAmount; +use cln_rpc::primitives::{Amount as JAmount, Utxo as JUtxo}; impl From for Amount { fn from(a: JAmount) -> Self { @@ -10,6 +10,24 @@ impl From for Amount { impl From<&Amount> for JAmount { fn from(a: &Amount) -> Self { - JAmount::from_msat(a.msat) + JAmount::from_msat(a.msat) + } +} + +impl From for Utxo { + fn from(a: JUtxo) -> Self { + Utxo { + txid: a.txid, + outnum: a.outnum, + } + } +} + +impl From<&Utxo> for JUtxo { + fn from(a: &Utxo) -> Self { + JUtxo { + txid: a.txid.clone(), + outnum: a.outnum, + } } } diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index fb538046007e..c0039c677c8a 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -116,6 +116,36 @@ async fn list_funds( } +async fn send_pay( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::SendpayRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::SendPay(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method SendPay: {:?}", e)))?; + match result { + Response::SendPay(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call SendPay", + r + ) + )), + } + +} + async fn list_channels( &self, request: tonic::Request, @@ -356,6 +386,36 @@ async fn datastore( } +async fn create_onion( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::CreateonionRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::CreateOnion(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method CreateOnion: {:?}", e)))?; + match result { + Response::CreateOnion(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call CreateOnion", + r + ) + )), + } + +} + async fn del_datastore( &self, request: tonic::Request, @@ -536,4 +596,334 @@ async fn list_invoices( } +async fn send_onion( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::SendonionRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::SendOnion(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method SendOnion: {:?}", e)))?; + match result { + Response::SendOnion(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call SendOnion", + r + ) + )), + } + +} + +async fn list_send_pays( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListsendpaysRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListSendPays(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListSendPays: {:?}", e)))?; + match result { + Response::ListSendPays(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListSendPays", + r + ) + )), + } + +} + +async fn list_transactions( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListtransactionsRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListTransactions(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListTransactions: {:?}", e)))?; + match result { + Response::ListTransactions(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListTransactions", + r + ) + )), + } + +} + +async fn pay( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::PayRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Pay(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Pay: {:?}", e)))?; + match result { + Response::Pay(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Pay", + r + ) + )), + } + +} + +async fn list_nodes( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListnodesRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListNodes(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListNodes: {:?}", e)))?; + match result { + Response::ListNodes(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListNodes", + r + ) + )), + } + +} + +async fn wait_any_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::WaitanyinvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::WaitAnyInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method WaitAnyInvoice: {:?}", e)))?; + match result { + Response::WaitAnyInvoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call WaitAnyInvoice", + r + ) + )), + } + +} + +async fn wait_invoice( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::WaitinvoiceRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::WaitInvoice(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method WaitInvoice: {:?}", e)))?; + match result { + Response::WaitInvoice(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call WaitInvoice", + r + ) + )), + } + +} + +async fn wait_send_pay( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::WaitsendpayRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::WaitSendPay(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method WaitSendPay: {:?}", e)))?; + match result { + Response::WaitSendPay(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call WaitSendPay", + r + ) + )), + } + +} + +async fn new_addr( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::NewaddrRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::NewAddr(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method NewAddr: {:?}", e)))?; + match result { + Response::NewAddr(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call NewAddr", + r + ) + )), + } + +} + +async fn withdraw( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::WithdrawRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Withdraw(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Withdraw: {:?}", e)))?; + match result { + Response::Withdraw(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Withdraw", + r + ) + )), + } + +} + +async fn key_send( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::KeysendRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::KeySend(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method KeySend: {:?}", e)))?; + match result { + Response::KeySend(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call KeySend", + r + ) + )), + } + +} + } diff --git a/cln-rpc/Cargo.toml b/cln-rpc/Cargo.toml index 3c8f7f6e4d8c..6c6b789beb25 100644 --- a/cln-rpc/Cargo.toml +++ b/cln-rpc/Cargo.toml @@ -17,6 +17,7 @@ tokio-util = { version = "0.6.9", features = ["codec"] } tokio = { version = "1", features = ["net"]} native-tls = { version = "*", features = ["vendored"] } futures-util = { version = "*", features = [ "sink" ] } +hex = "0.4.3" [dev-dependencies] tokio = { version = "1", features = ["net", "macros", "rt-multi-thread"]} diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 78e80f032e64..ac81bc0dabd1 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -19,6 +19,7 @@ pub enum Request { Getinfo(requests::GetinfoRequest), ListPeers(requests::ListpeersRequest), ListFunds(requests::ListfundsRequest), + SendPay(requests::SendpayRequest), ListChannels(requests::ListchannelsRequest), AddGossip(requests::AddgossipRequest), AutoCleanInvoice(requests::AutocleaninvoiceRequest), @@ -27,12 +28,24 @@ pub enum Request { ConnectPeer(requests::ConnectRequest), CreateInvoice(requests::CreateinvoiceRequest), Datastore(requests::DatastoreRequest), + CreateOnion(requests::CreateonionRequest), DelDatastore(requests::DeldatastoreRequest), DelExpiredInvoice(requests::DelexpiredinvoiceRequest), DelInvoice(requests::DelinvoiceRequest), Invoice(requests::InvoiceRequest), ListDatastore(requests::ListdatastoreRequest), ListInvoices(requests::ListinvoicesRequest), + SendOnion(requests::SendonionRequest), + ListSendPays(requests::ListsendpaysRequest), + ListTransactions(requests::ListtransactionsRequest), + Pay(requests::PayRequest), + ListNodes(requests::ListnodesRequest), + WaitAnyInvoice(requests::WaitanyinvoiceRequest), + WaitInvoice(requests::WaitinvoiceRequest), + WaitSendPay(requests::WaitsendpayRequest), + NewAddr(requests::NewaddrRequest), + Withdraw(requests::WithdrawRequest), + KeySend(requests::KeysendRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -42,6 +55,7 @@ pub enum Response { Getinfo(responses::GetinfoResponse), ListPeers(responses::ListpeersResponse), ListFunds(responses::ListfundsResponse), + SendPay(responses::SendpayResponse), ListChannels(responses::ListchannelsResponse), AddGossip(responses::AddgossipResponse), AutoCleanInvoice(responses::AutocleaninvoiceResponse), @@ -50,12 +64,24 @@ pub enum Response { ConnectPeer(responses::ConnectResponse), CreateInvoice(responses::CreateinvoiceResponse), Datastore(responses::DatastoreResponse), + CreateOnion(responses::CreateonionResponse), DelDatastore(responses::DeldatastoreResponse), DelExpiredInvoice(responses::DelexpiredinvoiceResponse), DelInvoice(responses::DelinvoiceResponse), Invoice(responses::InvoiceResponse), ListDatastore(responses::ListdatastoreResponse), ListInvoices(responses::ListinvoicesResponse), + SendOnion(responses::SendonionResponse), + ListSendPays(responses::ListsendpaysResponse), + ListTransactions(responses::ListtransactionsResponse), + Pay(responses::PayResponse), + ListNodes(responses::ListnodesResponse), + WaitAnyInvoice(responses::WaitanyinvoiceResponse), + WaitInvoice(responses::WaitinvoiceResponse), + WaitSendPay(responses::WaitsendpayResponse), + NewAddr(responses::NewaddrResponse), + Withdraw(responses::WithdrawResponse), + KeySend(responses::KeysendResponse), } pub mod requests { @@ -82,6 +108,36 @@ pub mod requests { pub spent: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendpayRoute { + #[serde(alias = "msatoshi")] + pub msatoshi: Amount, + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "delay")] + pub delay: u16, + #[serde(alias = "channel")] + pub channel: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendpayRequest { + #[serde(alias = "route")] + pub route: Vec, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "msatoshi", skip_serializing_if = "Option::is_none")] + pub msatoshi: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "payment_secret", skip_serializing_if = "Option::is_none")] + pub payment_secret: Option, + #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] + pub partid: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListchannelsRequest { #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] @@ -186,6 +242,26 @@ pub mod requests { pub generation: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateonionHops { + #[serde(alias = "pubkey")] + pub pubkey: String, + #[serde(alias = "payload")] + pub payload: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateonionRequest { + #[serde(alias = "hops")] + pub hops: Vec, + #[serde(alias = "assocdata")] + pub assocdata: String, + #[serde(alias = "session_key", skip_serializing_if = "Option::is_none")] + pub session_key: Option, + #[serde(alias = "onion_size", skip_serializing_if = "Option::is_none")] + pub onion_size: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DeldatastoreRequest { #[serde(alias = "key")] @@ -262,6 +338,156 @@ pub mod requests { pub offer_id: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendonionFirst_hop { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "delay")] + pub delay: u16, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendonionRequest { + #[serde(alias = "onion")] + pub onion: String, + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListsendpaysStatus { + PENDING, + COMPLETE, + FAILED, + } + + impl TryFrom for ListsendpaysStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListsendpaysStatus::PENDING), + 1 => Ok(ListsendpaysStatus::COMPLETE), + 2 => Ok(ListsendpaysStatus::FAILED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListsendpaysStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListsendpaysRequest { + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] + pub payment_hash: Option, + pub status: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListtransactionsRequest { + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PayRequest { + #[serde(alias = "bolt11")] + pub bolt11: String, + #[serde(alias = "msatoshi", skip_serializing_if = "Option::is_none")] + pub msatoshi: Option, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "riskfactor", skip_serializing_if = "Option::is_none")] + pub riskfactor: Option, + #[serde(alias = "maxfeepercent", skip_serializing_if = "Option::is_none")] + pub maxfeepercent: Option, + #[serde(alias = "retry_for", skip_serializing_if = "Option::is_none")] + pub retry_for: Option, + #[serde(alias = "maxdelay", skip_serializing_if = "Option::is_none")] + pub maxdelay: Option, + #[serde(alias = "exemptfee", skip_serializing_if = "Option::is_none")] + pub exemptfee: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListnodesRequest { + #[serde(alias = "id", skip_serializing_if = "Option::is_none")] + pub id: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitanyinvoiceRequest { + #[serde(alias = "lastpay_index", skip_serializing_if = "Option::is_none")] + pub lastpay_index: Option, + #[serde(alias = "timeout", skip_serializing_if = "Option::is_none")] + pub timeout: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitinvoiceRequest { + #[serde(alias = "label")] + pub label: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitsendpayRequest { + #[serde(alias = "payment_hash")] + pub payment_hash: String, + #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] + pub partid: Option, + #[serde(alias = "timeout", skip_serializing_if = "Option::is_none")] + pub timeout: Option, + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum NewaddrAddresstype { + BECH32, + P2SH_SEGWIT, + } + + impl TryFrom for NewaddrAddresstype { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(NewaddrAddresstype::BECH32), + 1 => Ok(NewaddrAddresstype::P2SH_SEGWIT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum NewaddrAddresstype", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewaddrRequest { + pub addresstype: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WithdrawRequest { + #[serde(alias = "destination")] + pub destination: String, + #[serde(alias = "satoshi", skip_serializing_if = "Option::is_none")] + pub satoshi: Option, + #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] + pub minconf: Option, + #[serde(alias = "utxos")] + pub utxos: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct KeysendRequest { + #[serde(alias = "destination")] + pub destination: String, + #[serde(alias = "msatoshi")] + pub msatoshi: Amount, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "maxfeepercent", skip_serializing_if = "Option::is_none")] + pub maxfeepercent: Option, + #[serde(alias = "retry_for", skip_serializing_if = "Option::is_none")] + pub retry_for: Option, + #[serde(alias = "maxdelay", skip_serializing_if = "Option::is_none")] + pub maxdelay: Option, + #[serde(alias = "exemptfee", skip_serializing_if = "Option::is_none")] + pub exemptfee: Option, + } + } @@ -786,6 +1012,57 @@ pub mod responses { pub channels: Vec, } + /// status of the payment (could be complete if already sent previously) + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum SendpayStatus { + PENDING, + COMPLETE, + } + + impl TryFrom for SendpayStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(SendpayStatus::PENDING), + 1 => Ok(SendpayStatus::COMPLETE), + o => Err(anyhow::anyhow!("Unknown variant {} for enum SendpayStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendpayResponse { + #[serde(alias = "id")] + pub id: u64, + #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] + pub groupid: Option, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `SendPay.status` + #[serde(rename = "status")] + pub status: SendpayStatus, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "created_at")] + pub created_at: u64, + #[serde(alias = "amount_sent_msat")] + pub amount_sent_msat: Amount, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] + pub partid: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + #[serde(alias = "message", skip_serializing_if = "Option::is_none")] + pub message: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListchannelsChannels { #[serde(alias = "source")] @@ -1010,6 +1287,14 @@ pub mod responses { pub string: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct CreateonionResponse { + #[serde(alias = "onion")] + pub onion: String, + #[serde(alias = "shared_secrets")] + pub shared_secrets: Vec, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DeldatastoreResponse { #[serde(alias = "key")] @@ -1170,5 +1455,535 @@ pub mod responses { pub invoices: Vec, } + /// status of the payment (could be complete if already sent previously) + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum SendonionStatus { + PENDING, + COMPLETE, + } + + impl TryFrom for SendonionStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(SendonionStatus::PENDING), + 1 => Ok(SendonionStatus::COMPLETE), + o => Err(anyhow::anyhow!("Unknown variant {} for enum SendonionStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendonionResponse { + #[serde(alias = "id")] + pub id: u64, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `SendOnion.status` + #[serde(rename = "status")] + pub status: SendonionStatus, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "created_at")] + pub created_at: u64, + #[serde(alias = "amount_sent_msat")] + pub amount_sent_msat: Amount, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + #[serde(alias = "message", skip_serializing_if = "Option::is_none")] + pub message: Option, + } + + /// status of the payment + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListsendpaysPaymentsStatus { + PENDING, + FAILED, + COMPLETE, + } + + impl TryFrom for ListsendpaysPaymentsStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListsendpaysPaymentsStatus::PENDING), + 1 => Ok(ListsendpaysPaymentsStatus::FAILED), + 2 => Ok(ListsendpaysPaymentsStatus::COMPLETE), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListsendpaysPaymentsStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListsendpaysPayments { + #[serde(alias = "id")] + pub id: u64, + #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] + pub groupid: Option, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `ListSendPays.payments[].status` + #[serde(rename = "status")] + pub status: ListsendpaysPaymentsStatus, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "created_at")] + pub created_at: u64, + #[serde(alias = "amount_sent_msat")] + pub amount_sent_msat: Amount, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + #[serde(alias = "erroronion", skip_serializing_if = "Option::is_none")] + pub erroronion: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListsendpaysResponse { + #[serde(alias = "payments")] + pub payments: Vec, + } + + /// the purpose of this input (*EXPERIMENTAL_FEATURES* only) + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListtransactionsTransactionsInputsType { + THEIRS, + DEPOSIT, + WITHDRAW, + CHANNEL_FUNDING, + CHANNEL_MUTUAL_CLOSE, + CHANNEL_UNILATERAL_CLOSE, + CHANNEL_SWEEP, + CHANNEL_HTLC_SUCCESS, + CHANNEL_HTLC_TIMEOUT, + CHANNEL_PENALTY, + CHANNEL_UNILATERAL_CHEAT, + } + + impl TryFrom for ListtransactionsTransactionsInputsType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListtransactionsTransactionsInputsType::THEIRS), + 1 => Ok(ListtransactionsTransactionsInputsType::DEPOSIT), + 2 => Ok(ListtransactionsTransactionsInputsType::WITHDRAW), + 3 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_FUNDING), + 4 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_MUTUAL_CLOSE), + 5 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_UNILATERAL_CLOSE), + 6 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_SWEEP), + 7 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_HTLC_SUCCESS), + 8 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_HTLC_TIMEOUT), + 9 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_PENALTY), + 10 => Ok(ListtransactionsTransactionsInputsType::CHANNEL_UNILATERAL_CHEAT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListtransactionsTransactionsInputsType", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListtransactionsTransactionsInputs { + #[serde(alias = "txid")] + pub txid: String, + #[serde(alias = "index")] + pub index: u32, + #[serde(alias = "sequence")] + pub sequence: u32, + pub item_type: Option, + #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] + pub channel: Option, + } + + /// the purpose of this output (*EXPERIMENTAL_FEATURES* only) + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListtransactionsTransactionsOutputsType { + THEIRS, + DEPOSIT, + WITHDRAW, + CHANNEL_FUNDING, + CHANNEL_MUTUAL_CLOSE, + CHANNEL_UNILATERAL_CLOSE, + CHANNEL_SWEEP, + CHANNEL_HTLC_SUCCESS, + CHANNEL_HTLC_TIMEOUT, + CHANNEL_PENALTY, + CHANNEL_UNILATERAL_CHEAT, + } + + impl TryFrom for ListtransactionsTransactionsOutputsType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListtransactionsTransactionsOutputsType::THEIRS), + 1 => Ok(ListtransactionsTransactionsOutputsType::DEPOSIT), + 2 => Ok(ListtransactionsTransactionsOutputsType::WITHDRAW), + 3 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_FUNDING), + 4 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_MUTUAL_CLOSE), + 5 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_UNILATERAL_CLOSE), + 6 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_SWEEP), + 7 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_HTLC_SUCCESS), + 8 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_HTLC_TIMEOUT), + 9 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_PENALTY), + 10 => Ok(ListtransactionsTransactionsOutputsType::CHANNEL_UNILATERAL_CHEAT), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListtransactionsTransactionsOutputsType", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListtransactionsTransactionsOutputs { + #[serde(alias = "index")] + pub index: u32, + #[serde(alias = "msat")] + pub msat: Amount, + #[serde(alias = "scriptPubKey")] + pub script_pub_key: String, + pub item_type: Option, + #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] + pub channel: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListtransactionsTransactions { + #[serde(alias = "hash")] + pub hash: String, + #[serde(alias = "rawtx")] + pub rawtx: String, + #[serde(alias = "blockheight")] + pub blockheight: u32, + #[serde(alias = "txindex")] + pub txindex: u32, + #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] + pub channel: Option, + #[serde(alias = "locktime")] + pub locktime: u32, + #[serde(alias = "version")] + pub version: u32, + #[serde(alias = "inputs")] + pub inputs: Vec, + #[serde(alias = "outputs")] + pub outputs: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListtransactionsResponse { + #[serde(alias = "transactions")] + pub transactions: Vec, + } + + /// status of payment + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum PayStatus { + COMPLETE, + PENDING, + FAILED, + } + + impl TryFrom for PayStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(PayStatus::COMPLETE), + 1 => Ok(PayStatus::PENDING), + 2 => Ok(PayStatus::FAILED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum PayStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PayResponse { + #[serde(alias = "payment_preimage")] + pub payment_preimage: String, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + #[serde(alias = "created_at")] + pub created_at: i64, + #[serde(alias = "parts")] + pub parts: u32, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "amount_sent_msat")] + pub amount_sent_msat: Amount, + #[serde(alias = "warning_partial_completion", skip_serializing_if = "Option::is_none")] + pub warning_partial_completion: Option, + // Path `Pay.status` + #[serde(rename = "status")] + pub status: PayStatus, + } + + /// Type of connection + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum ListnodesNodesAddressesType { + DNS, + IPV4, + IPV6, + TORV2, + TORV3, + WEBSOCKET, + } + + impl TryFrom for ListnodesNodesAddressesType { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListnodesNodesAddressesType::DNS), + 1 => Ok(ListnodesNodesAddressesType::IPV4), + 2 => Ok(ListnodesNodesAddressesType::IPV6), + 3 => Ok(ListnodesNodesAddressesType::TORV2), + 4 => Ok(ListnodesNodesAddressesType::TORV3), + 5 => Ok(ListnodesNodesAddressesType::WEBSOCKET), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListnodesNodesAddressesType", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListnodesNodesAddresses { + // Path `ListNodes.nodes[].addresses[].type` + #[serde(rename = "type")] + pub item_type: ListnodesNodesAddressesType, + #[serde(alias = "port")] + pub port: u16, + #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + pub address: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListnodesNodes { + #[serde(alias = "nodeid")] + pub nodeid: String, + #[serde(alias = "last_timestamp", skip_serializing_if = "Option::is_none")] + pub last_timestamp: Option, + #[serde(alias = "alias", skip_serializing_if = "Option::is_none")] + pub alias: Option, + #[serde(alias = "color", skip_serializing_if = "Option::is_none")] + pub color: Option, + #[serde(alias = "features", skip_serializing_if = "Option::is_none")] + pub features: Option, + #[serde(alias = "addresses")] + pub addresses: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListnodesResponse { + #[serde(alias = "nodes")] + pub nodes: Vec, + } + + /// Whether it's paid or expired + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum WaitanyinvoiceStatus { + PAID, + EXPIRED, + } + + impl TryFrom for WaitanyinvoiceStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(WaitanyinvoiceStatus::PAID), + 1 => Ok(WaitanyinvoiceStatus::EXPIRED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitanyinvoiceStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitanyinvoiceResponse { + #[serde(alias = "label")] + pub label: String, + #[serde(alias = "description")] + pub description: String, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `WaitAnyInvoice.status` + #[serde(rename = "status")] + pub status: WaitanyinvoiceStatus, + #[serde(alias = "expires_at")] + pub expires_at: u64, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "pay_index", skip_serializing_if = "Option::is_none")] + pub pay_index: Option, + #[serde(alias = "amount_received_msat", skip_serializing_if = "Option::is_none")] + pub amount_received_msat: Option, + #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] + pub paid_at: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + } + + /// Whether it's paid or expired + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum WaitinvoiceStatus { + PAID, + EXPIRED, + } + + impl TryFrom for WaitinvoiceStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(WaitinvoiceStatus::PAID), + 1 => Ok(WaitinvoiceStatus::EXPIRED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitinvoiceStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitinvoiceResponse { + #[serde(alias = "label")] + pub label: String, + #[serde(alias = "description")] + pub description: String, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `WaitInvoice.status` + #[serde(rename = "status")] + pub status: WaitinvoiceStatus, + #[serde(alias = "expires_at")] + pub expires_at: u64, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "pay_index", skip_serializing_if = "Option::is_none")] + pub pay_index: Option, + #[serde(alias = "amount_received_msat", skip_serializing_if = "Option::is_none")] + pub amount_received_msat: Option, + #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] + pub paid_at: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + } + + /// status of the payment + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum WaitsendpayStatus { + COMPLETE, + } + + impl TryFrom for WaitsendpayStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(WaitsendpayStatus::COMPLETE), + o => Err(anyhow::anyhow!("Unknown variant {} for enum WaitsendpayStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WaitsendpayResponse { + #[serde(alias = "id")] + pub id: u64, + #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] + pub groupid: Option, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `WaitSendPay.status` + #[serde(rename = "status")] + pub status: WaitsendpayStatus, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "created_at")] + pub created_at: u64, + #[serde(alias = "amount_sent_msat")] + pub amount_sent_msat: Amount, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] + pub partid: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] + pub payment_preimage: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct NewaddrResponse { + #[serde(alias = "bech32", skip_serializing_if = "Option::is_none")] + pub bech32: Option, + #[serde(alias = "p2sh-segwit", skip_serializing_if = "Option::is_none")] + pub p2sh_segwit: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct WithdrawResponse { + #[serde(alias = "tx")] + pub tx: String, + #[serde(alias = "txid")] + pub txid: String, + #[serde(alias = "psbt")] + pub psbt: String, + } + + /// status of payment + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + #[serde(rename_all = "lowercase")] + pub enum KeysendStatus { + COMPLETE, + } + + impl TryFrom for KeysendStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(KeysendStatus::COMPLETE), + o => Err(anyhow::anyhow!("Unknown variant {} for enum KeysendStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct KeysendResponse { + #[serde(alias = "payment_preimage")] + pub payment_preimage: String, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "payment_hash")] + pub payment_hash: String, + #[serde(alias = "created_at")] + pub created_at: i64, + #[serde(alias = "parts")] + pub parts: u32, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "amount_sent_msat")] + pub amount_sent_msat: Amount, + #[serde(alias = "warning_partial_completion", skip_serializing_if = "Option::is_none")] + pub warning_partial_completion: Option, + // Path `KeySend.status` + #[serde(rename = "status")] + pub status: KeysendStatus, + } + } diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index a3317d415660..8f7daacf7c7d 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -68,6 +68,44 @@ impl Amount { } } +#[derive(Clone, Debug, PartialEq)] +pub struct Utxo { + pub txid: Vec, + pub outnum: u32, +} + +impl Serialize for Utxo { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{}:{}", hex::encode(&self.txid), self.outnum)) + } +} + +impl<'de> Deserialize<'de> for Utxo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let s: String = Deserialize::deserialize(deserializer)?; + + let splits: Vec<&str> = s.split(':').collect(); + if splits.len() != 2 { + return Err(Error::custom("not a valid txid:output tuple")); + } + + let txid = + hex::decode(splits[0]).map_err(|_| Error::custom("not a valid hex encoded txid"))?; + let outnum: u32 = splits[1] + .parse() + .map_err(|e| Error::custom(format!("{} is not a valid number: {}", s, e)))?; + + Ok(Utxo { txid, outnum }) + } +} + #[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)] pub enum ChannelSide { LOCAL, diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index da2865d3a8bb..766aa3680186 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -43,22 +43,35 @@ def load_jsonrpc_service(): "Getinfo", "ListPeers", "ListFunds", - # "ListConfigs", + "SendPay", "ListChannels", "AddGossip", "AutoCleanInvoice", "CheckMessage", - # "check", # No point in mapping this one "Close", "Connect", "CreateInvoice", - # "createonion", "Datastore", - # "decodepay", - # "decode", + "CreateOnion", "DelDatastore", "DelExpiredInvoice", "DelInvoice", + "Invoice", + "ListDatastore", + "ListInvoices", + "SendOnion", + "ListSendPays", + "ListTransactions", + "Pay", + "ListNodes", + "WaitAnyInvoice", + "WaitInvoice", + "WaitSendPay", + "NewAddr", + "Withdraw", + "KeySend", + # "decodepay", + # "decode", # "delpay", # "disableoffer", # "disconnect", @@ -70,28 +83,15 @@ def load_jsonrpc_service(): # "fundchannel_start", # "funderupdate", # "fundpsbt", - # "getinfo", # "getlog", # "getroute", # "getsharedsecret", - # "help", - "Invoice", - # "keysend", - # "listchannels", # "listconfigs", - "ListDatastore", # "listforwards", - # "listfunds", - "ListInvoices", - # "listnodes", # "listoffers", # "listpays", - # "listsendpays", - # "listtransactions", # "multifundchannel", # "multiwithdraw", - # "newaddr", - # "notifications", # "offerout", # "offer", # "openchannel_abort", @@ -100,30 +100,27 @@ def load_jsonrpc_service(): # "openchannel_signed", # "openchannel_update", # "parsefeerate", - # "pay", # "ping", # "plugin", # "reserveinputs", # "sendcustommsg", # "sendinvoice", # "sendonionmessage", - # "sendonion", - # "sendpay", # "sendpsbt", # "setchannelfee", # "signmessage", # "signpsbt", - # "stop", # "txdiscard", # "txprepare", # "txsend", # "unreserveinputs", - # "utxopsbt", - # "waitanyinvoice", # "waitblockheight", - # "waitinvoice", - # "waitsendpay", - # "withdraw", + # "ListConfigs", + # "check", # No point in mapping this one + # "Stop", # Breaks a core assumption (root is an object) can't map unless we change this + # "UtxoPsbt", # Breaks since the utxos array has dynamic keys which we can't map as is + # "notifications", # No point in mapping this + # "help", ] methods = [load_jsonrpc_method(name) for name in method_names] service = Service(name="Node", methods=methods) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index ea37250a309f..ad1f281818fc 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -20,7 +20,9 @@ 'u32': 'uint32', 'u64': 'uint64', 'u16': 'uint32', # Yeah, I know... + 'f32': 'float', 'integer': 'sint64', + "utxo": "Utxo", } @@ -33,6 +35,7 @@ 'ListPeers.peers[].channels[].closer': "ChannelSide", 'ListPeers.peers[].channels[].features[]': "string", 'ListFunds.channels[].state': 'ChannelState', + 'ListTransactions.transactions[].type[]': None, } method_name_overrides = { @@ -258,6 +261,7 @@ def generate_composite(self, prefix, field: CompositeField): continue name = f.normalized() + name = re.sub(r'(? None: 'txid?': f'c.{name}.clone().map(|v| hex::encode(v))', 'pubkey': f'hex::encode(&c.{name})', 'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))', - 'msat': f'c.{name}.as_ref().unwrap().into()' + 'msat': f'c.{name}.as_ref().unwrap().into()', + 'msat?': f'c.{name}.as_ref().map(|a| a.into())', }.get( typ, f'c.{name}.clone()' # default to just assignment ) - self.write(f"{name}: {rhs},\n", numindent=3) + self.write(f"{name}: {rhs}, // Rule #1 for type {typ}\n", numindent=3) self.write(f"""\ }} diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index f1a703a1edcc..a08388859511 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -218,6 +218,8 @@ class PrimitiveField(Field): "u32", "u64", "u8", + "f32", + "float", "string", "pubkey", "signature", @@ -228,6 +230,7 @@ class PrimitiveField(Field): "integer", "u16", "number", + "utxo", # A string representing the tuple (txid, outnum) ] def __init__(self, typename, path, description): diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index 2d9d586f87ee..bb7a64064a9a 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -3,6 +3,7 @@ from textwrap import dedent, indent import logging import sys +import re from .model import (ArrayField, CompositeField, EnumField, PrimitiveField, Service) @@ -23,6 +24,7 @@ 'ListPeers.peers[].channels[].closer': "ChannelSide", 'ListPeers.peers[].channels[].features[]': "string", 'ListFunds.channels[].state': 'ChannelState', + 'ListTransactions.transactions[].type[]': None, } # A map of schema type to rust primitive types. @@ -36,6 +38,8 @@ 'signature': 'String', 'string': 'String', 'txid': 'String', + 'float': 'f32', + 'utxo': 'Utxo', } header = f"""#![allow(non_camel_case_types)] @@ -56,6 +60,7 @@ def normalize_varname(field): """ # Dashes are not valid names field.path = field.path.replace("-", "_") + field.path = re.sub(r'(? {name} ({a.path})") - _, decl = gen_field(a.itemtype) if isinstance(a.itemtype, PrimitiveField): @@ -152,6 +156,9 @@ def gen_array(a): decl = "" # No declaration if we have an override itemtype = overrides[a.path] + if itemtype is None: + return ("", "") # Override said not to include + itemtype = typemap.get(itemtype, itemtype) alias = a.name.normalized()[:-2] # Strip the `[]` suffix for arrays. defi = f" #[serde(alias = \"{alias}\")]\n pub {name}: {'Vec<'*a.dims}{itemtype}{'>'*a.dims},\n" diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index ed815601591f..e8dc09d08a9c 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -84,7 +84,7 @@ On success, an object is returned, containing: - **parts** (u32): how many attempts this took - **amount_msat** (msat): Amount the recipient received - **amount_sent_msat** (msat): Total amount we sent (including fees) -- **status** (string): status of payment (always "complete") +- **status** (string): status of payment (one of "complete", "pending", "failed") - **destination** (pubkey, optional): the final destination of the payment The following warnings may also be returned: @@ -149,4 +149,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bf507985544575c4ef2fe194fda6a693378cb8ab3bfb30ca7a7c066be271be29) +[comment]: # ( SHA256STAMP:e537b9c74918559db99e673fac63cfa43a347ab80c32f33012ab3655b9edc45e) diff --git a/doc/schemas/createonion.request.json b/doc/schemas/createonion.request.json new file mode 100644 index 000000000000..656023f4eb41 --- /dev/null +++ b/doc/schemas/createonion.request.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "hops", + "assocdata" + ], + "properties": { + "hops": { + "type": "array", + "description": "", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "pubkey", + "payload" + ], + "properties": { + "pubkey": { + "type": "pubkey" + }, + "payload": { + "type": "hex" + } + } + } + }, + "assocdata": { + "type": "hex", + "description": "" + }, + "session_key": { + "type": "hex" + }, + "onion_size": { + "type": "u16" + } + } +} diff --git a/doc/schemas/keysend.request.json b/doc/schemas/keysend.request.json new file mode 100644 index 000000000000..b8e6f324213e --- /dev/null +++ b/doc/schemas/keysend.request.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "destination", + "msatoshi" + ], + "properties": { + "destination": { + "type": "pubkey" + }, + "msatoshi": { + "type": "msat" + }, + "label": { + "type": "string" + }, + "maxfeepercent": { + "type": "float" + }, + "retry_for": { + "type": "number" + }, + "maxdelay": { + "type": "number" + }, + "exemptfee": { + "type": "msat" + } + } +} diff --git a/doc/schemas/listnodes.request.json b/doc/schemas/listnodes.request.json new file mode 100644 index 000000000000..75e30b4b3853 --- /dev/null +++ b/doc/schemas/listnodes.request.json @@ -0,0 +1,10 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "properties": { + "id": { + "type": "pubkey" + } + } +} diff --git a/doc/schemas/listsendpays.request.json b/doc/schemas/listsendpays.request.json new file mode 100644 index 000000000000..0ed227e81ad0 --- /dev/null +++ b/doc/schemas/listsendpays.request.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "properties": { + "bolt11": { + "type": "string" + }, + "payment_hash": { + "type": "hex" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "complete", + "failed" + ] + } + } +} diff --git a/doc/schemas/listtransactions.request.json b/doc/schemas/listtransactions.request.json new file mode 100644 index 000000000000..f99496c5ac84 --- /dev/null +++ b/doc/schemas/listtransactions.request.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": {} +} diff --git a/doc/schemas/newaddr.request.json b/doc/schemas/newaddr.request.json new file mode 100644 index 000000000000..94eebe44f79d --- /dev/null +++ b/doc/schemas/newaddr.request.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "properties": { + "addresstype": { + "type": "string", + "enum": [ + "bech32", + "p2sh-segwit" + ] + } + } +} diff --git a/doc/schemas/pay.request.json b/doc/schemas/pay.request.json new file mode 100644 index 000000000000..e2c939b1ba1b --- /dev/null +++ b/doc/schemas/pay.request.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "bolt11" + ], + "properties": { + "bolt11": { + "type": "string" + }, + "msatoshi": { + "type": "msat" + }, + "label": { + "type": "string" + }, + "riskfactor": { + "type": "float" + }, + "maxfeepercent": { + "type": "f32" + }, + "retry_for": { + "type": "u16" + }, + "maxdelay": { + "type": "u16" + }, + "exemptfee": { + "type": "f32" + } + } +} diff --git a/doc/schemas/pay.schema.json b/doc/schemas/pay.schema.json index 1a4f395f9fcd..6c80bb3161f9 100644 --- a/doc/schemas/pay.schema.json +++ b/doc/schemas/pay.schema.json @@ -57,7 +57,9 @@ "status": { "type": "string", "enum": [ - "complete" + "complete", + "pending", + "failed" ], "description": "status of payment" } diff --git a/doc/schemas/sendonion.request.json b/doc/schemas/sendonion.request.json new file mode 100644 index 000000000000..884ce85c4ab7 --- /dev/null +++ b/doc/schemas/sendonion.request.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "onion", + "first_hop", + "payment_hash" + ], + "properties": { + "onion": { + "type": "hex" + }, + "first_hop": { + "type": "object", + "required": [ + "id", + "amount_msat", + "delay" + ], + "properties": { + "id": { + "type": "pubkey" + }, + "amount_msat": { + "type": "msat" + }, + "delay": { + "type": "u16" + } + }, + "payment_hash": { + "type": "hex" + }, + "label": { + "type": "string" + }, + "shared_secrets": { + "type": "array", + "itemtype": { + "type": "hex" + } + }, + "partid": { + "type": "u16" + }, + "bolt11": { + "type": "string" + }, + "msatoshi": { + "type": "msat" + }, + "destination": { + "type": "pubkey" + } + } + } +} diff --git a/doc/schemas/sendpay.request.json b/doc/schemas/sendpay.request.json new file mode 100644 index 000000000000..541ac2666123 --- /dev/null +++ b/doc/schemas/sendpay.request.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "route", + "payment_hash" + ], + "properties": { + "route": { + "type": "array", + "items": { + "type": "object", + "required": [ + "msatoshi", + "id", + "delay", + "channel" + ], + "properties": { + "msatoshi": { + "type": "msat" + }, + "id": { + "type": "pubkey" + }, + "delay": { + "type": "u16" + }, + "channel": { + "type": "short_channel_id" + } + } + } + }, + "payment_hash": { + "type": "hex" + }, + "label": { + "type": "string" + }, + "msatoshi": { + "type": "msat" + }, + "bolt11": { + "type": "string" + }, + "payment_secret": { + "type": "hex" + }, + "partid": { + "type": "u16" + } + } +} diff --git a/doc/schemas/waitanyinvoice.request.json b/doc/schemas/waitanyinvoice.request.json new file mode 100644 index 000000000000..ed73b9db1f4e --- /dev/null +++ b/doc/schemas/waitanyinvoice.request.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "properties": { + "lastpay_index": { + "type": "number" + }, + "timeout": { + "type": "number" + } + } +} diff --git a/doc/schemas/waitinvoice.request.json b/doc/schemas/waitinvoice.request.json new file mode 100644 index 000000000000..b3ceab08d52f --- /dev/null +++ b/doc/schemas/waitinvoice.request.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "label" + ], + "properties": { + "label": { + "type": "string" + } + } +} diff --git a/doc/schemas/waitsendpay.request.json b/doc/schemas/waitsendpay.request.json new file mode 100644 index 000000000000..833a929ddaf2 --- /dev/null +++ b/doc/schemas/waitsendpay.request.json @@ -0,0 +1,18 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "payment_hash" + ], + "properties": { + "payment_hash": { + "type": "hex" + }, + "partid": { + "type": "u16" + }, + "timeout": { + "type": "u32" + } + } +} diff --git a/doc/schemas/withdraw.request.json b/doc/schemas/withdraw.request.json new file mode 100644 index 000000000000..d098171c8cd0 --- /dev/null +++ b/doc/schemas/withdraw.request.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "destination" + ], + "properties": { + "destination": { + "type": "pubkey" + }, + "satoshi": { + "type": "msat" + }, + "feerate": { + "type": "feerate" + }, + "minconf": { + "type": "u16" + }, + "utxos": { + "type": "array", + "items": { + "type": "utxo" + } + } + } +} From eb2aa8c51ca066ec5c145199acd4906fd16b2f07 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0621/1530] cln-rpc: Map feerates and backfill methods using it --- .msggen.json | 1 + cln-grpc/proto/node.proto | 1 + cln-grpc/proto/primitives.proto | 10 ++++ cln-grpc/src/convert.rs | 1 + cln-grpc/src/pb.rs | 14 ++++++ cln-rpc/src/model.rs | 2 + cln-rpc/src/primitives.rs | 89 +++++++++++++++++++++++++++++++++ contrib/msggen/msggen/grpc.py | 2 + contrib/msggen/msggen/model.py | 1 + contrib/msggen/msggen/rust.py | 1 + 10 files changed, 122 insertions(+) diff --git a/.msggen.json b/.msggen.json index 9e553c56953d..60de060a51ee 100644 --- a/.msggen.json +++ b/.msggen.json @@ -774,6 +774,7 @@ }, "WithdrawRequest": { "Withdraw.destination": 1, + "Withdraw.feerate": 5, "Withdraw.minconf": 3, "Withdraw.satoshi": 2, "Withdraw.utxos[]": 4 diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index fc26237dd7d8..a2023b1e6e1f 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -904,6 +904,7 @@ message NewaddrResponse { message WithdrawRequest { bytes destination = 1; optional Amount satoshi = 2; + optional Feerate feerate = 5; optional uint32 minconf = 3; repeated Utxo utxos = 4; } diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index bbcde1c7d2ae..b440936d36a4 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -29,4 +29,14 @@ message ChannelStateChangeCause {} message Utxo { bytes txid = 1; uint32 outnum = 2; +} + +message Feerate { + oneof style { + bool slow = 1; + bool normal = 2; + bool urgent = 3; + uint32 perkb = 4; + uint32 perkw = 5; + } } \ No newline at end of file diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 58b78b3f9449..907cac989119 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1052,6 +1052,7 @@ impl From<&pb::WithdrawRequest> for requests::WithdrawRequest { Self { destination: hex::encode(&c.destination), // Rule #1 for type pubkey satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? minconf: c.minconf.map(|v| v as u16), // Rule #1 for type u16? utxos: c.utxos.iter().map(|s| s.into()).collect(), } diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index ae8e8bc4b67c..12ae1d11730a 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -31,3 +31,17 @@ impl From<&Utxo> for JUtxo { } } } + +impl From<&Feerate> for cln_rpc::primitives::Feerate { + fn from(f: &Feerate) -> cln_rpc::primitives::Feerate { + use cln_rpc::primitives::Feerate as JFeerate; + use feerate::Style; + match f.style.clone().unwrap() { + Style::Slow(_) => JFeerate::Slow, + Style::Normal(_) => JFeerate::Normal, + Style::Urgent(_) => JFeerate::Urgent, + Style::Perkw(i) => JFeerate::PerKw(i), + Style::Perkb(i) => JFeerate::PerKb(i), + } + } +} diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index ac81bc0dabd1..620b30e5e097 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -464,6 +464,8 @@ pub mod requests { pub destination: String, #[serde(alias = "satoshi", skip_serializing_if = "Option::is_none")] pub satoshi: Option, + #[serde(alias = "feerate", skip_serializing_if = "Option::is_none")] + pub feerate: Option, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] pub minconf: Option, #[serde(alias = "utxos")] diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 8f7daacf7c7d..355e42142214 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -218,6 +218,77 @@ impl From for String { } } +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum Feerate { + Slow, + Normal, + Urgent, + PerKb(u32), + PerKw(u32), +} + +impl TryFrom<&str> for Feerate { + type Error = Error; + fn try_from(s: &str) -> Result { + let number: u32 = s + .chars() + .map(|c| c.to_digit(10)) + .take_while(|opt| opt.is_some()) + .fold(0, |acc, digit| acc * 10 + (digit.unwrap() as u32)); + + let s = s.to_lowercase(); + if s.ends_with("perkw") { + Ok(Feerate::PerKw(number)) + } else if s.ends_with("perkb") { + Ok(Feerate::PerKb(number)) + } else if s == "slow" { + Ok(Feerate::Slow) + } else if s == "normal" { + Ok(Feerate::Normal) + } else if s == "urgent" { + Ok(Feerate::Urgent) + } else { + Err(anyhow!("Unable to parse feerate from string: {}", s)) + } + } +} + +impl From<&Feerate> for String { + fn from(f: &Feerate) -> String { + match f { + Feerate::Slow => "slow".to_string(), + Feerate::Normal => "normal".to_string(), + Feerate::Urgent => "urgent".to_string(), + Feerate::PerKb(v) => format!("{}perkb", v), + Feerate::PerKw(v) => format!("{}perkw", v), + } + } +} + +impl<'de> Deserialize<'de> for Feerate { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = Deserialize::deserialize(deserializer)?; + let res: Feerate = s + .as_str() + .try_into() + .map_err(|e| serde::de::Error::custom(format!("{}", e)))?; + Ok(res) + } +} + +impl Serialize for Feerate { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let s: String = self.into(); + serializer.serialize_str(&s) + } +} + #[cfg(test)] mod test { use super::*; @@ -283,4 +354,22 @@ mod test { r#"{"all":"all","not_all":"31337msat","any":"any","not_any":"42msat"}"# ); } + + #[test] + fn test_parse_feerate() { + let tests = vec![ + ("slow", Feerate::Slow), + ("normal", Feerate::Normal), + ("urgent", Feerate::Urgent), + ("12345perkb", Feerate::PerKb(12345)), + ("54321perkw", Feerate::PerKw(54321)), + ]; + + for (input, output) in tests.into_iter() { + let parsed: Feerate = input.try_into().unwrap(); + assert_eq!(parsed, output); + let serialized: String = (&parsed).into(); + assert_eq!(serialized, input); + } + } } diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index ad1f281818fc..0e093d19fe66 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -23,6 +23,7 @@ 'f32': 'float', 'integer': 'sint64', "utxo": "Utxo", + "feerate": "Feerate", } @@ -394,6 +395,7 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))', 'msat': f'c.{name}.as_ref().unwrap().into()', 'msat?': f'c.{name}.as_ref().map(|a| a.into())', + 'feerate?': f'c.{name}.as_ref().map(|a| a.into())', }.get( typ, f'c.{name}.clone()' # default to just assignment diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index a08388859511..532a8ec91988 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -230,6 +230,7 @@ class PrimitiveField(Field): "integer", "u16", "number", + "feerate", "utxo", # A string representing the tuple (txid, outnum) ] diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index bb7a64064a9a..ea5aaf3cc2d9 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -40,6 +40,7 @@ 'txid': 'String', 'float': 'f32', 'utxo': 'Utxo', + 'feerate': 'Feerate', } header = f"""#![allow(non_camel_case_types)] From 0354a7fdb10529abdeb701af9516885475dfe5eb Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0622/1530] cln-rpc: Add `OutputDesc` for `{addr: amt}` style arguments This is likely inherited from bitcoind, and a bit awkward for us, so we parse it into a classic struct, but serialize it back into the bitcoind format when talking to the RPC. --- cln-grpc/proto/primitives.proto | 5 +++ cln-grpc/src/pb.rs | 14 +++++++-- cln-rpc/src/primitives.rs | 56 +++++++++++++++++++++++++++++++-- contrib/msggen/msggen/model.py | 1 + 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index b440936d36a4..2f04e14101f5 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -39,4 +39,9 @@ message Feerate { uint32 perkb = 4; uint32 perkw = 5; } +} + +message OutputDesc { + string address = 1; + Amount amount = 2; } \ No newline at end of file diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 12ae1d11730a..2c2b98828b3c 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -1,6 +1,8 @@ tonic::include_proto!("cln"); -use cln_rpc::primitives::{Amount as JAmount, Utxo as JUtxo}; +use cln_rpc::primitives::{ + Amount as JAmount, Feerate as JFeerate, OutputDesc as JOutputDesc, Utxo as JUtxo, +}; impl From for Amount { fn from(a: JAmount) -> Self { @@ -34,7 +36,6 @@ impl From<&Utxo> for JUtxo { impl From<&Feerate> for cln_rpc::primitives::Feerate { fn from(f: &Feerate) -> cln_rpc::primitives::Feerate { - use cln_rpc::primitives::Feerate as JFeerate; use feerate::Style; match f.style.clone().unwrap() { Style::Slow(_) => JFeerate::Slow, @@ -45,3 +46,12 @@ impl From<&Feerate> for cln_rpc::primitives::Feerate { } } } + +impl From<&OutputDesc> for JOutputDesc { + fn from(od: &OutputDesc) -> JOutputDesc { + JOutputDesc { + address: od.address.clone(), + amount: od.amount.as_ref().unwrap().into(), + } + } +} diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 355e42142214..b9a2349cc864 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -368,8 +368,60 @@ mod test { for (input, output) in tests.into_iter() { let parsed: Feerate = input.try_into().unwrap(); assert_eq!(parsed, output); - let serialized: String = (&parsed).into(); - assert_eq!(serialized, input); + let serialized: String = (&parsed).into(); + assert_eq!(serialized, input); } } + + #[test] + fn test_parse_output_desc() { + let a = r#"{"address":"1234msat"}"#; + let od = serde_json::from_str(a).unwrap(); + + assert_eq!( + OutputDesc { + address: "address".to_string(), + amount: Amount { msat: 1234 } + }, + od + ); + let serialized: String = serde_json::to_string(&od).unwrap(); + assert_eq!(a, serialized); + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct OutputDesc { + pub address: String, + pub amount: Amount, +} + +impl<'de> Deserialize<'de> for OutputDesc { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let map: std::collections::HashMap = + Deserialize::deserialize(deserializer)?; + + let (address, amount) = map.iter().next().unwrap(); + + Ok(OutputDesc { + address: address.to_string(), + amount: *amount, + }) + } +} + +impl Serialize for OutputDesc { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::SerializeMap; + let mut map = serializer.serialize_map(Some(1))?; + map.serialize_key(&self.address)?; + map.serialize_value(&self.amount)?; + map.end() + } } diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index 532a8ec91988..db581291769d 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -232,6 +232,7 @@ class PrimitiveField(Field): "number", "feerate", "utxo", # A string representing the tuple (txid, outnum) + "OutputDesc", # A dict that maps an address to an amount (bitcoind style) ] def __init__(self, typename, path, description): From 04e7e285d7169485a480c3ec7abd3358ef82561b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0623/1530] cln-rpc: Map PSBT and TX methods --- .msggen.json | 88 ++++++++++++ cln-grpc/proto/node.proto | 110 +++++++++++++++ cln-grpc/src/convert.rs | 183 +++++++++++++++++++++++++ cln-grpc/src/server.rs | 210 +++++++++++++++++++++++++++++ cln-rpc/src/model.rs | 188 ++++++++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 11 +- contrib/msggen/msggen/grpc.py | 1 + doc/schemas/fundpsbt.request.json | 36 +++++ doc/schemas/sendpsbt.request.json | 15 +++ doc/schemas/signpsbt.request.json | 12 ++ doc/schemas/txdiscard.request.json | 12 ++ doc/schemas/txprepare.request.json | 27 ++++ doc/schemas/txsend.request.json | 12 ++ doc/schemas/utxopsbt.request.json | 43 ++++++ 14 files changed, 944 insertions(+), 4 deletions(-) create mode 100644 doc/schemas/fundpsbt.request.json create mode 100644 doc/schemas/sendpsbt.request.json create mode 100644 doc/schemas/signpsbt.request.json create mode 100644 doc/schemas/txdiscard.request.json create mode 100644 doc/schemas/txprepare.request.json create mode 100644 doc/schemas/txsend.request.json create mode 100644 doc/schemas/utxopsbt.request.json diff --git a/.msggen.json b/.msggen.json index 60de060a51ee..a9475fb0dfbf 100644 --- a/.msggen.json +++ b/.msggen.json @@ -300,6 +300,30 @@ "DelInvoice.payment_hash": 6, "DelInvoice.status": 7 }, + "FundpsbtRequest": { + "FundPsbt.feerate": 2, + "FundPsbt.locktime": 6, + "FundPsbt.min_witness_weight": 7, + "FundPsbt.minconf": 4, + "FundPsbt.reserve": 5, + "FundPsbt.satoshi": 1, + "FundPsbt.startweight": 3 + }, + "FundpsbtReservations": { + "FundPsbt.reservations[].reserved": 4, + "FundPsbt.reservations[].reserved_to_block": 5, + "FundPsbt.reservations[].txid": 1, + "FundPsbt.reservations[].vout": 2, + "FundPsbt.reservations[].was_reserved": 3 + }, + "FundpsbtResponse": { + "FundPsbt.change_outnum": 5, + "FundPsbt.estimated_final_weight": 3, + "FundPsbt.excess_msat": 4, + "FundPsbt.feerate_per_kw": 2, + "FundPsbt.psbt": 1, + "FundPsbt.reservations[]": 6 + }, "GetinfoAddress": { "Getinfo.address[].address": 3, "Getinfo.address[].port": 2, @@ -717,6 +741,70 @@ "SendPay.route[].id": 2, "SendPay.route[].msatoshi": 1 }, + "SendpsbtRequest": { + "SendPsbt.psbt": 1 + }, + "SendpsbtResponse": { + "SendPsbt.tx": 1, + "SendPsbt.txid": 2 + }, + "SignpsbtRequest": { + "SignPsbt.psbt": 1, + "SignPsbt.signonly[]": 2 + }, + "SignpsbtResponse": { + "SignPsbt.signed_psbt": 1 + }, + "TxdiscardRequest": { + "TxDiscard.txid": 1 + }, + "TxdiscardResponse": { + "TxDiscard.txid": 2, + "TxDiscard.unsigned_tx": 1 + }, + "TxprepareRequest": { + "TxPrepare.feerate": 2, + "TxPrepare.minconf": 3, + "TxPrepare.outptus[]": 1, + "TxPrepare.utxos[]": 4 + }, + "TxprepareResponse": { + "TxPrepare.psbt": 1, + "TxPrepare.txid": 3, + "TxPrepare.unsigned_tx": 2 + }, + "TxsendRequest": { + "TxSend.txid": 1 + }, + "TxsendResponse": { + "TxSend.psbt": 1, + "TxSend.tx": 2, + "TxSend.txid": 3 + }, + "UtxopsbtRequest": { + "UtxoPsbt.feerate": 2, + "UtxoPsbt.locktime": 6, + "UtxoPsbt.min_witness_weight": 7, + "UtxoPsbt.reserve": 5, + "UtxoPsbt.satoshi": 1, + "UtxoPsbt.startweight": 3, + "UtxoPsbt.utxos[]": 4 + }, + "UtxopsbtReservations": { + "UtxoPsbt.reservations[].reserved": 4, + "UtxoPsbt.reservations[].reserved_to_block": 5, + "UtxoPsbt.reservations[].txid": 1, + "UtxoPsbt.reservations[].vout": 2, + "UtxoPsbt.reservations[].was_reserved": 3 + }, + "UtxopsbtResponse": { + "UtxoPsbt.change_outnum": 5, + "UtxoPsbt.estimated_final_weight": 3, + "UtxoPsbt.excess_msat": 4, + "UtxoPsbt.feerate_per_kw": 2, + "UtxoPsbt.psbt": 1, + "UtxoPsbt.reservations[]": 6 + }, "WaitanyinvoiceRequest": { "WaitAnyInvoice.lastpay_index": 1, "WaitAnyInvoice.timeout": 2 diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index a2023b1e6e1f..f3a6dd23a7f9 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -38,6 +38,13 @@ service Node { rpc NewAddr(NewaddrRequest) returns (NewaddrResponse) {} rpc Withdraw(WithdrawRequest) returns (WithdrawResponse) {} rpc KeySend(KeysendRequest) returns (KeysendResponse) {} + rpc FundPsbt(FundpsbtRequest) returns (FundpsbtResponse) {} + rpc SendPsbt(SendpsbtRequest) returns (SendpsbtResponse) {} + rpc SignPsbt(SignpsbtRequest) returns (SignpsbtResponse) {} + rpc UtxoPsbt(UtxopsbtRequest) returns (UtxopsbtResponse) {} + rpc TxDiscard(TxdiscardRequest) returns (TxdiscardResponse) {} + rpc TxPrepare(TxprepareRequest) returns (TxprepareResponse) {} + rpc TxSend(TxsendRequest) returns (TxsendResponse) {} } message GetinfoRequest { @@ -940,3 +947,106 @@ message KeysendResponse { optional string warning_partial_completion = 8; KeysendStatus status = 9; } + +message FundpsbtRequest { + Amount satoshi = 1; + Feerate feerate = 2; + sint64 startweight = 3; + optional sint64 minconf = 4; + optional sint64 reserve = 5; + optional sint64 locktime = 6; + optional uint32 min_witness_weight = 7; +} + +message FundpsbtResponse { + string psbt = 1; + uint32 feerate_per_kw = 2; + uint32 estimated_final_weight = 3; + Amount excess_msat = 4; + optional uint32 change_outnum = 5; + repeated FundpsbtReservations reservations = 6; +} + +message FundpsbtReservations { + bytes txid = 1; + uint32 vout = 2; + bool was_reserved = 3; + bool reserved = 4; + uint32 reserved_to_block = 5; +} + +message SendpsbtRequest { + string psbt = 1; +} + +message SendpsbtResponse { + bytes tx = 1; + bytes txid = 2; +} + +message SignpsbtRequest { + string psbt = 1; +} + +message SignpsbtResponse { + string signed_psbt = 1; +} + +message UtxopsbtRequest { + Amount satoshi = 1; + Feerate feerate = 2; + sint64 startweight = 3; + repeated Utxo utxos = 4; + optional sint64 reserve = 5; + optional sint64 locktime = 6; + optional uint32 min_witness_weight = 7; +} + +message UtxopsbtResponse { + string psbt = 1; + uint32 feerate_per_kw = 2; + uint32 estimated_final_weight = 3; + Amount excess_msat = 4; + optional uint32 change_outnum = 5; + repeated UtxopsbtReservations reservations = 6; +} + +message UtxopsbtReservations { + bytes txid = 1; + uint32 vout = 2; + bool was_reserved = 3; + bool reserved = 4; + uint32 reserved_to_block = 5; +} + +message TxdiscardRequest { + bytes txid = 1; +} + +message TxdiscardResponse { + bytes unsigned_tx = 1; + bytes txid = 2; +} + +message TxprepareRequest { + repeated OutputDesc outptus = 1; + optional Feerate feerate = 2; + optional uint32 minconf = 3; + repeated Utxo utxos = 4; +} + +message TxprepareResponse { + string psbt = 1; + bytes unsigned_tx = 2; + bytes txid = 3; +} + +message TxsendRequest { + bytes txid = 1; +} + +message TxsendResponse { + string psbt = 1; + bytes tx = 2; + bytes txid = 3; +} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 907cac989119..61b09b814f3f 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -725,6 +725,111 @@ impl From<&responses::KeysendResponse> for pb::KeysendResponse { } } +#[allow(unused_variables)] +impl From<&responses::FundpsbtReservations> for pb::FundpsbtReservations { + fn from(c: &responses::FundpsbtReservations) -> Self { + Self { + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + vout: c.vout.clone(), // Rule #2 for type u32 + was_reserved: c.was_reserved.clone(), // Rule #2 for type boolean + reserved: c.reserved.clone(), // Rule #2 for type boolean + reserved_to_block: c.reserved_to_block.clone(), // Rule #2 for type u32 + } + } +} + +#[allow(unused_variables)] +impl From<&responses::FundpsbtResponse> for pb::FundpsbtResponse { + fn from(c: &responses::FundpsbtResponse) -> Self { + Self { + psbt: c.psbt.clone(), // Rule #2 for type string + feerate_per_kw: c.feerate_per_kw.clone(), // Rule #2 for type u32 + estimated_final_weight: c.estimated_final_weight.clone(), // Rule #2 for type u32 + excess_msat: Some(c.excess_msat.into()), // Rule #2 for type msat + change_outnum: c.change_outnum.clone(), // Rule #2 for type u32? + reservations: c.reservations.iter().map(|i| i.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::SendpsbtResponse> for pb::SendpsbtResponse { + fn from(c: &responses::SendpsbtResponse) -> Self { + Self { + tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + } + } +} + +#[allow(unused_variables)] +impl From<&responses::SignpsbtResponse> for pb::SignpsbtResponse { + fn from(c: &responses::SignpsbtResponse) -> Self { + Self { + signed_psbt: c.signed_psbt.clone(), // Rule #2 for type string + } + } +} + +#[allow(unused_variables)] +impl From<&responses::UtxopsbtReservations> for pb::UtxopsbtReservations { + fn from(c: &responses::UtxopsbtReservations) -> Self { + Self { + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + vout: c.vout.clone(), // Rule #2 for type u32 + was_reserved: c.was_reserved.clone(), // Rule #2 for type boolean + reserved: c.reserved.clone(), // Rule #2 for type boolean + reserved_to_block: c.reserved_to_block.clone(), // Rule #2 for type u32 + } + } +} + +#[allow(unused_variables)] +impl From<&responses::UtxopsbtResponse> for pb::UtxopsbtResponse { + fn from(c: &responses::UtxopsbtResponse) -> Self { + Self { + psbt: c.psbt.clone(), // Rule #2 for type string + feerate_per_kw: c.feerate_per_kw.clone(), // Rule #2 for type u32 + estimated_final_weight: c.estimated_final_weight.clone(), // Rule #2 for type u32 + excess_msat: Some(c.excess_msat.into()), // Rule #2 for type msat + change_outnum: c.change_outnum.clone(), // Rule #2 for type u32? + reservations: c.reservations.iter().map(|i| i.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&responses::TxdiscardResponse> for pb::TxdiscardResponse { + fn from(c: &responses::TxdiscardResponse) -> Self { + Self { + unsigned_tx: hex::decode(&c.unsigned_tx).unwrap(), // Rule #2 for type hex + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + } + } +} + +#[allow(unused_variables)] +impl From<&responses::TxprepareResponse> for pb::TxprepareResponse { + fn from(c: &responses::TxprepareResponse) -> Self { + Self { + psbt: c.psbt.clone(), // Rule #2 for type string + unsigned_tx: hex::decode(&c.unsigned_tx).unwrap(), // Rule #2 for type hex + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + } + } +} + +#[allow(unused_variables)] +impl From<&responses::TxsendResponse> for pb::TxsendResponse { + fn from(c: &responses::TxsendResponse) -> Self { + Self { + psbt: c.psbt.clone(), // Rule #2 for type string + tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -1074,3 +1179,81 @@ impl From<&pb::KeysendRequest> for requests::KeysendRequest { } } +#[allow(unused_variables)] +impl From<&pb::FundpsbtRequest> for requests::FundpsbtRequest { + fn from(c: &pb::FundpsbtRequest) -> Self { + Self { + satoshi: c.satoshi.as_ref().unwrap().into(), // Rule #1 for type msat + feerate: c.feerate.as_ref().unwrap().into(), // Rule #1 for type feerate + startweight: c.startweight.clone(), // Rule #1 for type number + minconf: c.minconf.clone(), // Rule #1 for type number? + reserve: c.reserve.clone(), // Rule #1 for type number? + locktime: c.locktime.clone(), // Rule #1 for type number? + min_witness_weight: c.min_witness_weight.clone(), // Rule #1 for type u32? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::SendpsbtRequest> for requests::SendpsbtRequest { + fn from(c: &pb::SendpsbtRequest) -> Self { + Self { + psbt: c.psbt.clone(), // Rule #1 for type string + } + } +} + +#[allow(unused_variables)] +impl From<&pb::SignpsbtRequest> for requests::SignpsbtRequest { + fn from(c: &pb::SignpsbtRequest) -> Self { + Self { + psbt: c.psbt.clone(), // Rule #1 for type string + } + } +} + +#[allow(unused_variables)] +impl From<&pb::UtxopsbtRequest> for requests::UtxopsbtRequest { + fn from(c: &pb::UtxopsbtRequest) -> Self { + Self { + satoshi: c.satoshi.as_ref().unwrap().into(), // Rule #1 for type msat + feerate: c.feerate.as_ref().unwrap().into(), // Rule #1 for type feerate + startweight: c.startweight.clone(), // Rule #1 for type number + utxos: c.utxos.iter().map(|s| s.into()).collect(), + reserve: c.reserve.clone(), // Rule #1 for type number? + locktime: c.locktime.clone(), // Rule #1 for type number? + min_witness_weight: c.min_witness_weight.clone(), // Rule #1 for type u32? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::TxdiscardRequest> for requests::TxdiscardRequest { + fn from(c: &pb::TxdiscardRequest) -> Self { + Self { + txid: hex::encode(&c.txid), // Rule #1 for type hex + } + } +} + +#[allow(unused_variables)] +impl From<&pb::TxprepareRequest> for requests::TxprepareRequest { + fn from(c: &pb::TxprepareRequest) -> Self { + Self { + outptus: c.outptus.iter().map(|s| s.into()).collect(), + feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? + minconf: c.minconf.clone(), // Rule #1 for type u32? + utxos: c.utxos.iter().map(|s| s.into()).collect(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::TxsendRequest> for requests::TxsendRequest { + fn from(c: &pb::TxsendRequest) -> Self { + Self { + txid: hex::encode(&c.txid), // Rule #1 for type hex + } + } +} + diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index c0039c677c8a..66e40d1391d6 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -926,4 +926,214 @@ async fn key_send( } +async fn fund_psbt( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::FundpsbtRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::FundPsbt(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method FundPsbt: {:?}", e)))?; + match result { + Response::FundPsbt(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call FundPsbt", + r + ) + )), + } + +} + +async fn send_psbt( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::SendpsbtRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::SendPsbt(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method SendPsbt: {:?}", e)))?; + match result { + Response::SendPsbt(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call SendPsbt", + r + ) + )), + } + +} + +async fn sign_psbt( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::SignpsbtRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::SignPsbt(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method SignPsbt: {:?}", e)))?; + match result { + Response::SignPsbt(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call SignPsbt", + r + ) + )), + } + +} + +async fn utxo_psbt( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::UtxopsbtRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::UtxoPsbt(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method UtxoPsbt: {:?}", e)))?; + match result { + Response::UtxoPsbt(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call UtxoPsbt", + r + ) + )), + } + +} + +async fn tx_discard( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::TxdiscardRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::TxDiscard(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method TxDiscard: {:?}", e)))?; + match result { + Response::TxDiscard(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call TxDiscard", + r + ) + )), + } + +} + +async fn tx_prepare( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::TxprepareRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::TxPrepare(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method TxPrepare: {:?}", e)))?; + match result { + Response::TxPrepare(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call TxPrepare", + r + ) + )), + } + +} + +async fn tx_send( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::TxsendRequest = (&req).into(); + debug!("Client asked for getinfo"); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::TxSend(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method TxSend: {:?}", e)))?; + match result { + Response::TxSend(r) => Ok( + tonic::Response::new((&r).into()) + ), + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call TxSend", + r + ) + )), + } + +} + } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 620b30e5e097..9d767e310ce6 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -46,6 +46,13 @@ pub enum Request { NewAddr(requests::NewaddrRequest), Withdraw(requests::WithdrawRequest), KeySend(requests::KeysendRequest), + FundPsbt(requests::FundpsbtRequest), + SendPsbt(requests::SendpsbtRequest), + SignPsbt(requests::SignpsbtRequest), + UtxoPsbt(requests::UtxopsbtRequest), + TxDiscard(requests::TxdiscardRequest), + TxPrepare(requests::TxprepareRequest), + TxSend(requests::TxsendRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -82,6 +89,13 @@ pub enum Response { NewAddr(responses::NewaddrResponse), Withdraw(responses::WithdrawResponse), KeySend(responses::KeysendResponse), + FundPsbt(responses::FundpsbtResponse), + SendPsbt(responses::SendpsbtResponse), + SignPsbt(responses::SignpsbtResponse), + UtxoPsbt(responses::UtxopsbtResponse), + TxDiscard(responses::TxdiscardResponse), + TxPrepare(responses::TxprepareResponse), + TxSend(responses::TxsendResponse), } pub mod requests { @@ -490,6 +504,78 @@ pub mod requests { pub exemptfee: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FundpsbtRequest { + #[serde(alias = "satoshi")] + pub satoshi: Amount, + #[serde(alias = "feerate")] + pub feerate: Feerate, + #[serde(alias = "startweight")] + pub startweight: i64, + #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] + pub minconf: Option, + #[serde(alias = "reserve", skip_serializing_if = "Option::is_none")] + pub reserve: Option, + #[serde(alias = "locktime", skip_serializing_if = "Option::is_none")] + pub locktime: Option, + #[serde(alias = "min_witness_weight", skip_serializing_if = "Option::is_none")] + pub min_witness_weight: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendpsbtRequest { + #[serde(alias = "psbt")] + pub psbt: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SignpsbtRequest { + #[serde(alias = "psbt")] + pub psbt: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UtxopsbtRequest { + #[serde(alias = "satoshi")] + pub satoshi: Amount, + #[serde(alias = "feerate")] + pub feerate: Feerate, + #[serde(alias = "startweight")] + pub startweight: i64, + #[serde(alias = "utxos")] + pub utxos: Vec, + #[serde(alias = "reserve", skip_serializing_if = "Option::is_none")] + pub reserve: Option, + #[serde(alias = "locktime", skip_serializing_if = "Option::is_none")] + pub locktime: Option, + #[serde(alias = "min_witness_weight", skip_serializing_if = "Option::is_none")] + pub min_witness_weight: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TxdiscardRequest { + #[serde(alias = "txid")] + pub txid: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TxprepareRequest { + #[serde(alias = "outptus")] + pub outptus: Vec, + #[serde(alias = "feerate", skip_serializing_if = "Option::is_none")] + pub feerate: Option, + #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] + pub minconf: Option, + #[serde(alias = "utxos")] + pub utxos: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TxsendRequest { + #[serde(alias = "txid")] + pub txid: String, + } + } @@ -1987,5 +2073,107 @@ pub mod responses { pub status: KeysendStatus, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FundpsbtReservations { + #[serde(alias = "txid")] + pub txid: String, + #[serde(alias = "vout")] + pub vout: u32, + #[serde(alias = "was_reserved")] + pub was_reserved: bool, + #[serde(alias = "reserved")] + pub reserved: bool, + #[serde(alias = "reserved_to_block")] + pub reserved_to_block: u32, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FundpsbtResponse { + #[serde(alias = "psbt")] + pub psbt: String, + #[serde(alias = "feerate_per_kw")] + pub feerate_per_kw: u32, + #[serde(alias = "estimated_final_weight")] + pub estimated_final_weight: u32, + #[serde(alias = "excess_msat")] + pub excess_msat: Amount, + #[serde(alias = "change_outnum", skip_serializing_if = "Option::is_none")] + pub change_outnum: Option, + #[serde(alias = "reservations")] + pub reservations: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SendpsbtResponse { + #[serde(alias = "tx")] + pub tx: String, + #[serde(alias = "txid")] + pub txid: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SignpsbtResponse { + #[serde(alias = "signed_psbt")] + pub signed_psbt: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UtxopsbtReservations { + #[serde(alias = "txid")] + pub txid: String, + #[serde(alias = "vout")] + pub vout: u32, + #[serde(alias = "was_reserved")] + pub was_reserved: bool, + #[serde(alias = "reserved")] + pub reserved: bool, + #[serde(alias = "reserved_to_block")] + pub reserved_to_block: u32, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct UtxopsbtResponse { + #[serde(alias = "psbt")] + pub psbt: String, + #[serde(alias = "feerate_per_kw")] + pub feerate_per_kw: u32, + #[serde(alias = "estimated_final_weight")] + pub estimated_final_weight: u32, + #[serde(alias = "excess_msat")] + pub excess_msat: Amount, + #[serde(alias = "change_outnum", skip_serializing_if = "Option::is_none")] + pub change_outnum: Option, + #[serde(alias = "reservations")] + pub reservations: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TxdiscardResponse { + #[serde(alias = "unsigned_tx")] + pub unsigned_tx: String, + #[serde(alias = "txid")] + pub txid: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TxprepareResponse { + #[serde(alias = "psbt")] + pub psbt: String, + #[serde(alias = "unsigned_tx")] + pub unsigned_tx: String, + #[serde(alias = "txid")] + pub txid: String, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct TxsendResponse { + #[serde(alias = "psbt")] + pub psbt: String, + #[serde(alias = "tx")] + pub tx: String, + #[serde(alias = "txid")] + pub txid: String, + } + } diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 766aa3680186..a864940f04e9 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -70,6 +70,13 @@ def load_jsonrpc_service(): "NewAddr", "Withdraw", "KeySend", + "FundPsbt", + "SendPsbt", + "SignPsbt", + "UtxoPsbt", + "TxDiscard", + "TxPrepare", + "TxSend", # "decodepay", # "decode", # "delpay", @@ -110,15 +117,11 @@ def load_jsonrpc_service(): # "setchannelfee", # "signmessage", # "signpsbt", - # "txdiscard", - # "txprepare", - # "txsend", # "unreserveinputs", # "waitblockheight", # "ListConfigs", # "check", # No point in mapping this one # "Stop", # Breaks a core assumption (root is an object) can't map unless we change this - # "UtxoPsbt", # Breaks since the utxos array has dynamic keys which we can't map as is # "notifications", # No point in mapping this # "help", ] diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 0e093d19fe66..e29e8312a36d 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -395,6 +395,7 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))', 'msat': f'c.{name}.as_ref().unwrap().into()', 'msat?': f'c.{name}.as_ref().map(|a| a.into())', + 'feerate': f'c.{name}.as_ref().unwrap().into()', 'feerate?': f'c.{name}.as_ref().map(|a| a.into())', }.get( typ, diff --git a/doc/schemas/fundpsbt.request.json b/doc/schemas/fundpsbt.request.json new file mode 100644 index 000000000000..a23d1935ac6a --- /dev/null +++ b/doc/schemas/fundpsbt.request.json @@ -0,0 +1,36 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "satoshi", + "feerate", + "startweight" + ], + "properties": { + "satoshi": { + "type": "msat" + }, + "feerate": { + "type": "feerate" + }, + "startweight": { + "type": "number" + }, + "minconf": { + "type": "number" + }, + "reserve": { + "type": "number", + "description": "reserve is a number: if non-zero number then reserveinputs is called (successfully, with exclusive true) on the returned PSBT for this number of blocks (default: 72)." + }, + "locktime": { + "type": "number" + }, + "min_witness_weight": { + "type": "u32" + }, + "excess_as_change": { + "type": "bool" + } + } +} diff --git a/doc/schemas/sendpsbt.request.json b/doc/schemas/sendpsbt.request.json new file mode 100644 index 000000000000..f4f4ea988627 --- /dev/null +++ b/doc/schemas/sendpsbt.request.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "psbt" + ], + "properties": { + "psbt": { + "type": "string" + }, + "reserve": { + "type": "bool" + } + } +} diff --git a/doc/schemas/signpsbt.request.json b/doc/schemas/signpsbt.request.json new file mode 100644 index 000000000000..1c5ff0383066 --- /dev/null +++ b/doc/schemas/signpsbt.request.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "psbt" + ], + "properties": { + "psbt": { + "type": "string" + } + } +} diff --git a/doc/schemas/txdiscard.request.json b/doc/schemas/txdiscard.request.json new file mode 100644 index 000000000000..e69bf747d695 --- /dev/null +++ b/doc/schemas/txdiscard.request.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "txid" + ], + "properties": { + "txid": { + "type": "hex" + } + } +} diff --git a/doc/schemas/txprepare.request.json b/doc/schemas/txprepare.request.json new file mode 100644 index 000000000000..0a161b32718c --- /dev/null +++ b/doc/schemas/txprepare.request.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "outputs" + ], + "properties": { + "outptus": { + "type": "array", + "items": { + "type": "OutputDesc" + } + }, + "feerate": { + "type": "feerate" + }, + "minconf": { + "type": "u32" + }, + "utxos": { + "type": "array", + "items": { + "type": "utxo" + } + } + } +} diff --git a/doc/schemas/txsend.request.json b/doc/schemas/txsend.request.json new file mode 100644 index 000000000000..e69bf747d695 --- /dev/null +++ b/doc/schemas/txsend.request.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "txid" + ], + "properties": { + "txid": { + "type": "hex" + } + } +} diff --git a/doc/schemas/utxopsbt.request.json b/doc/schemas/utxopsbt.request.json new file mode 100644 index 000000000000..9d8541d09a48 --- /dev/null +++ b/doc/schemas/utxopsbt.request.json @@ -0,0 +1,43 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "satoshi", + "feerate", + "startweight", + "utxos" + ], + "properties": { + "satoshi": { + "type": "msat" + }, + "feerate": { + "type": "feerate" + }, + "startweight": { + "type": "number" + }, + "utxos": { + "type": "array", + "items": { + "type": "utxo" + } + }, + "reserve": { + "type": "number", + "description": "reserve is a number: if non-zero number then reserveinputs is called (successfully, with exclusive true) on the returned PSBT for this number of blocks (default: 72)." + }, + "reservedok": { + "type": "bool" + }, + "locktime": { + "type": "number" + }, + "min_witness_weight": { + "type": "u32" + }, + "excess_as_change": { + "type": "bool" + } + } +} From bba68e2136fdde0af520fdeb0e54e9a930d82d7e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0624/1530] cln-grpc: Map AmountOrAll and AmountOrAny --- cln-grpc/proto/node.proto | 4 +-- cln-grpc/proto/primitives.proto | 14 +++++++++ cln-grpc/src/convert.rs | 4 +-- cln-grpc/src/pb.rs | 48 ++++++++++++++++++++++++++++++- cln-rpc/src/lib.rs | 2 +- cln-rpc/src/model.rs | 4 +-- contrib/msggen/msggen/grpc.py | 6 ++++ contrib/msggen/msggen/model.py | 2 ++ contrib/msggen/msggen/rust.py | 2 ++ doc/schemas/invoice.request.json | 2 +- doc/schemas/withdraw.request.json | 2 +- tests/test_cln_rs.py | 10 +++++++ 12 files changed, 90 insertions(+), 10 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index f3a6dd23a7f9..cfd390c23984 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -558,7 +558,7 @@ message DelinvoiceResponse { } message InvoiceRequest { - Amount msatoshi = 1; + AmountOrAny msatoshi = 1; string description = 2; string label = 3; repeated string fallbacks = 4; @@ -910,7 +910,7 @@ message NewaddrResponse { message WithdrawRequest { bytes destination = 1; - optional Amount satoshi = 2; + optional AmountOrAll satoshi = 2; optional Feerate feerate = 5; optional uint32 minconf = 3; repeated Utxo utxos = 4; diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index 2f04e14101f5..c76730949e2c 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -5,6 +5,20 @@ message Amount { uint64 msat = 1; } +message AmountOrAll { + oneof value { + Amount amount = 1; + bool all = 2; + } +} + +message AmountOrAny { + oneof value { + Amount amount = 1; + bool any = 2; + } +} + enum ChannelSide { IN = 0; OUT = 1; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 61b09b814f3f..cb7d8d54bc5f 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1028,7 +1028,7 @@ impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { impl From<&pb::InvoiceRequest> for requests::InvoiceRequest { fn from(c: &pb::InvoiceRequest) -> Self { Self { - msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat|any description: c.description.clone(), // Rule #1 for type string label: c.label.clone(), // Rule #1 for type string fallbacks: c.fallbacks.iter().map(|s| s.into()).collect(), @@ -1156,7 +1156,7 @@ impl From<&pb::WithdrawRequest> for requests::WithdrawRequest { fn from(c: &pb::WithdrawRequest) -> Self { Self { destination: hex::encode(&c.destination), // Rule #1 for type pubkey - satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat|all? feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? minconf: c.minconf.map(|v| v as u16), // Rule #1 for type u16? utxos: c.utxos.iter().map(|s| s.into()).collect(), diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 2c2b98828b3c..910f09cafd5f 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -1,7 +1,8 @@ tonic::include_proto!("cln"); use cln_rpc::primitives::{ - Amount as JAmount, Feerate as JFeerate, OutputDesc as JOutputDesc, Utxo as JUtxo, + Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, + Feerate as JFeerate, OutputDesc as JOutputDesc, Utxo as JUtxo, }; impl From for Amount { @@ -55,3 +56,48 @@ impl From<&OutputDesc> for JOutputDesc { } } } + +impl From for AmountOrAll { + fn from(a: JAmountOrAll) -> Self { + match a { + JAmountOrAll::Amount(a) => AmountOrAll { + value: Some(amount_or_all::Value::Amount(a.into())), + }, + JAmountOrAll::All => AmountOrAll { + value: Some(amount_or_all::Value::All(true)), + }, + } + } +} + +impl From<&AmountOrAll> for JAmountOrAll { + fn from(a: &AmountOrAll) -> Self { + match &a.value { + Some(amount_or_all::Value::Amount(a)) => JAmountOrAll::Amount(a.into()), + Some(amount_or_all::Value::All(_)) => JAmountOrAll::All, + None => panic!("AmountOrAll is neither amount nor all: {:?}", a), + } + } +} + +impl From for AmountOrAny { + fn from(a: JAmountOrAny) -> Self { + match a { + JAmountOrAny::Amount(a) => AmountOrAny { + value: Some(amount_or_any::Value::Amount(a.into())), + }, + JAmountOrAny::Any => AmountOrAny { + value: Some(amount_or_any::Value::Any(true)), + }, + } + } +} +impl From<&AmountOrAny> for JAmountOrAny { + fn from(a: &AmountOrAny) -> Self { + match &a.value { + Some(amount_or_any::Value::Amount(a)) => JAmountOrAny::Amount(a.into()), + Some(amount_or_any::Value::Any(_)) => JAmountOrAny::Any, + None => panic!("AmountOrAll is neither amount nor any: {:?}", a), + } + } +} diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs index 0197d4b3993f..c57bf2432a21 100644 --- a/cln-rpc/src/lib.rs +++ b/cln-rpc/src/lib.rs @@ -75,7 +75,7 @@ impl ClnRpc { // serde_json knows which variant of [`Request`] should be // used. response["method"] = req2["method"].clone(); - + log::warn!("XXX {:?}", response); serde_json::from_value(response).context("converting response into enum") } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 9d767e310ce6..be76d10ceba0 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -321,7 +321,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct InvoiceRequest { #[serde(alias = "msatoshi")] - pub msatoshi: Amount, + pub msatoshi: AmountOrAny, #[serde(alias = "description")] pub description: String, #[serde(alias = "label")] @@ -477,7 +477,7 @@ pub mod requests { #[serde(alias = "destination")] pub destination: String, #[serde(alias = "satoshi", skip_serializing_if = "Option::is_none")] - pub satoshi: Option, + pub satoshi: Option, #[serde(alias = "feerate", skip_serializing_if = "Option::is_none")] pub feerate: Option, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index e29e8312a36d..61084e7cf63a 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -10,6 +10,8 @@ 'boolean': 'bool', 'hex': 'bytes', 'msat': 'Amount', + 'msat|all': 'AmountOrAll', + 'msat|any': 'AmountOrAny', 'number': 'sint64', 'pubkey': 'bytes', 'short_channel_id': 'string', @@ -395,6 +397,10 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))', 'msat': f'c.{name}.as_ref().unwrap().into()', 'msat?': f'c.{name}.as_ref().map(|a| a.into())', + 'msat|all': f'c.{name}.as_ref().unwrap().into()', + 'msat|all?': f'c.{name}.as_ref().map(|a| a.into())', + 'msat|any': f'c.{name}.as_ref().unwrap().into()', + 'msat|any?': f'c.{name}.as_ref().map(|a| a.into())', 'feerate': f'c.{name}.as_ref().unwrap().into()', 'feerate?': f'c.{name}.as_ref().map(|a| a.into())', }.get( diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index db581291769d..49215203af0c 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -224,6 +224,8 @@ class PrimitiveField(Field): "pubkey", "signature", "msat", + "msat|any", + "msat|all", "hex", "short_channel_id", "txid", diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index ea5aaf3cc2d9..0de946b7f54f 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -32,6 +32,8 @@ 'boolean': 'bool', 'hex': 'String', 'msat': 'Amount', + 'msat|all': 'AmountOrAll', + 'msat|any': 'AmountOrAny', 'number': 'i64', 'pubkey': 'String', 'short_channel_id': 'String', diff --git a/doc/schemas/invoice.request.json b/doc/schemas/invoice.request.json index 282311bd83ed..34d40c267b57 100644 --- a/doc/schemas/invoice.request.json +++ b/doc/schemas/invoice.request.json @@ -9,7 +9,7 @@ ], "properties": { "msatoshi": { - "type": "msat", + "type": "msat|any", "description": "" }, "description": { diff --git a/doc/schemas/withdraw.request.json b/doc/schemas/withdraw.request.json index d098171c8cd0..0637f1f16a1e 100644 --- a/doc/schemas/withdraw.request.json +++ b/doc/schemas/withdraw.request.json @@ -9,7 +9,7 @@ "type": "pubkey" }, "satoshi": { - "type": "msat" + "type": "msat|all" }, "feerate": { "type": "feerate" diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 4fb62b81084c..140ffa392de3 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -5,6 +5,7 @@ from ephemeral_port_reserve import reserve import grpc import node_pb2 as nodepb +from primitives_pb2 import AmountOrAny import pytest import subprocess @@ -101,6 +102,15 @@ def test_grpc_connect(node_factory): response = stub.ListFunds(nodepb.ListfundsRequest()) print(response) + inv = stub.Invoice(nodepb.InvoiceRequest( + msatoshi=AmountOrAny(any=True), + description="hello", + label="lbl1", + preimage=b"\x00" * 32, + cltv=24 + )) + print(inv) + def test_grpc_generate_certificate(node_factory): """Test whether we correctly generate the certificates. From 3feb634d31e0e9c6c740b918be56479bd55a9641 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0625/1530] pytest: fix if no rust installed. Signed-off-by: Rusty Russell --- tests/test_cln_rs.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 140ffa392de3..1bffc70ddf18 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -1,10 +1,8 @@ from fixtures import * # noqa: F401,F403 -from node_pb2_grpc import NodeStub from pathlib import Path from pyln.testing.utils import env, TEST_NETWORK, wait_for from ephemeral_port_reserve import reserve import grpc -import node_pb2 as nodepb from primitives_pb2 import AmountOrAny import pytest import subprocess @@ -74,6 +72,10 @@ def test_plugin_start(node_factory): def test_grpc_connect(node_factory): """Attempts to connect to the grpc interface and call getinfo""" + # These only exist if we have rust! + from node_pb2_grpc import NodeStub # noqa: E402 + import node_pb2 as nodepb # noqa: E402 + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1 = node_factory.get_node(options={"plugin": str(bin_path), "grpc-port": str(grpc_port)}) @@ -172,6 +174,10 @@ def test_grpc_wrong_auth(node_factory): We create two instances, each generates its own certs and keys, and then we try to cross the wires. """ + # These only exist if we have rust! + from node_pb2_grpc import NodeStub # noqa: E402 + import node_pb2 as nodepb # noqa: E402 + grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1, l2 = node_factory.get_nodes(2, opts={ From bf4d9e30d2ab3eed77d21a26ccbab5ca0c6bd905 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0626/1530] fundpsbt: deprecate reserve=true/false usage. Make it always a number; this makes the JSON request specification simpler. We allowed a number since v0.10.1. (reserve=True is the default anyway, so usually it can be omitted: reserve=False becomes reserve=0). Changelog-Deprecated: JSON-RPC: `fundpsbt`/`utxopsbt` `reserve` must be a number, not bool (for `true` use 72/don't specify, for `false` use 0). Numbers have been allowed since v0.10.1. --- contrib/pyln-client/pyln/client/lightning.py | 4 +-- doc/lightning-fundpsbt.7.md | 7 +++-- doc/lightning-utxopsbt.7.md | 7 +++-- plugins/funder.c | 1 - plugins/spender/multiwithdraw.c | 1 - tests/test_connection.py | 10 +++---- tests/test_wallet.py | 28 +++++++++----------- wallet/reservation.c | 19 +++++++------ 8 files changed, 36 insertions(+), 41 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 52e5c3fe34a9..3433eb10acfb 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -1338,7 +1338,7 @@ def unreserveinputs(self, psbt, reserve=None): } return self.call("unreserveinputs", payload) - def fundpsbt(self, satoshi, feerate, startweight, minconf=None, reserve=True, locktime=None, min_witness_weight=None, excess_as_change=False): + def fundpsbt(self, satoshi, feerate, startweight, minconf=None, reserve=None, locktime=None, min_witness_weight=None, excess_as_change=False): """ Create a PSBT with inputs sufficient to give an output of satoshi. """ @@ -1354,7 +1354,7 @@ def fundpsbt(self, satoshi, feerate, startweight, minconf=None, reserve=True, lo } return self.call("fundpsbt", payload) - def utxopsbt(self, satoshi, feerate, startweight, utxos, reserve=True, reservedok=False, locktime=None, min_witness_weight=None, excess_as_change=False): + def utxopsbt(self, satoshi, feerate, startweight, utxos, reserve=None, reservedok=False, locktime=None, min_witness_weight=None, excess_as_change=False): """ Create a PSBT with given inputs, to give an output of satoshi. """ diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 553efdec3dd8..177d26f21ea7 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -33,10 +33,9 @@ added any inputs. *minconf* specifies the minimum number of confirmations that used outputs should have. Default is 1. -*reserve* is either boolean or a number: if *true* or a non-zero -number then *reserveinputs* is called (successfully, with -*exclusive* true) on the returned PSBT for this number of blocks (or -72 blocks if *reserve* is simply *true*). +If *reserve* if not zero, then *reserveinputs* is called (successfully, with +*exclusive* true) on the returned PSBT for this number of blocks (default +72 blocks if unspecified). *locktime* is an optional locktime: if not set, it is set to a recent block height. diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index 090d66961318..bc20d1830cf7 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -23,10 +23,9 @@ the resulting transaction plus *startweight* at the given *feerate*, with at least *satoshi* left over (unless *satoshi* is **all**, which is equivalent to setting it to zero). -*reserve* is either boolean or a number: if *true* or a non-zero -number then *reserveinputs* is called (successfully, with -*exclusive* true) on the returned PSBT for this number of blocks (or -72 blocks if *reserve* is simply *true*). +If *reserve* if not zero, then *reserveinputs* is called (successfully, with +*exclusive* true) on the returned PSBT for this number of blocks (default +72 blocks if unspecified). Unless *reservedok* is set to true (default is false) it will also fail if any of the *utxos* are already reserved. diff --git a/plugins/funder.c b/plugins/funder.c index d2c572a63055..7880a6dbaec5 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -455,7 +455,6 @@ listfunds_success(struct command *cmd, &psbt_funded, &psbt_fund_failed, info); - json_add_bool(req->js, "reserve", true); json_add_string(req->js, "satoshi", type_to_string(tmpctx, struct amount_sat, &info->our_funding)); diff --git a/plugins/spender/multiwithdraw.c b/plugins/spender/multiwithdraw.c index b89638c68536..8790f88e0e05 100644 --- a/plugins/spender/multiwithdraw.c +++ b/plugins/spender/multiwithdraw.c @@ -369,7 +369,6 @@ static struct command_result *start_mw(struct multiwithdraw_command *mw) mw); json_add_u32(req->js, "minconf", *mw->minconf); } - json_add_bool(req->js, "reserve", true); if (mw->has_all) json_add_string(req->js, "satoshi", "all"); else { diff --git a/tests/test_connection.py b/tests/test_connection.py index bc7855e97dc2..523b90c1a6c4 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -320,7 +320,7 @@ def test_channel_abandon(node_factory, bitcoind): l1.rpc.fundchannel(l2.info['id'], SATS, feerate='1875perkw') opening_utxo = only_one([o for o in l1.rpc.listfunds()['outputs'] if o['reserved']]) - psbt = l1.rpc.utxopsbt(0, "253perkw", 0, [opening_utxo['txid'] + ':' + str(opening_utxo['output'])], reserve=False, reservedok=True)['psbt'] + psbt = l1.rpc.utxopsbt(0, "253perkw", 0, [opening_utxo['txid'] + ':' + str(opening_utxo['output'])], reserve=0, reservedok=True)['psbt'] # We expect a reservation for 2016 blocks; unreserve it. reservations = only_one(l1.rpc.unreserveinputs(psbt, reserve=2015)['reservations']) @@ -1178,7 +1178,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): wait_for(lambda: len(l1.rpc.listfunds()["outputs"]) != 0) # Some random (valid) psbt - psbt = l1.rpc.fundpsbt(amount, '253perkw', 250, reserve=False)['psbt'] + psbt = l1.rpc.fundpsbt(amount, '253perkw', 250, reserve=0)['psbt'] with pytest.raises(RpcError, match=r'Unknown peer'): l1.rpc.fundchannel_start(l2.info['id'], amount) @@ -1296,7 +1296,7 @@ def test_funding_v2_corners(node_factory, bitcoind): wait_for(lambda: len(l1.rpc.listfunds()["outputs"]) != 0) # Some random (valid) psbt - psbt = l1.rpc.fundpsbt(amount, '253perkw', 250, reserve=False)['psbt'] + psbt = l1.rpc.fundpsbt(amount, '253perkw', 250, reserve=0)['psbt'] nonexist_chanid = '11' * 32 with pytest.raises(RpcError, match=r'Unknown peer'): @@ -1324,7 +1324,7 @@ def test_funding_v2_corners(node_factory, bitcoind): # Should be able to 'restart' after canceling amount2 = 1000000 l1.rpc.unreserveinputs(psbt) - psbt = l1.rpc.fundpsbt(amount2, '253perkw', 250, reserve=False)['psbt'] + psbt = l1.rpc.fundpsbt(amount2, '253perkw', 250, reserve=0)['psbt'] l1.rpc.connect(l2.info['id'], 'localhost', l2.port) start = l1.rpc.openchannel_init(l2.info['id'], amount2, psbt) @@ -1455,7 +1455,7 @@ def test_funding_v2_cancel_race(node_factory, bitcoind, executor): for count, n in enumerate(nodes): l1.rpc.connect(n.info['id'], 'localhost', n.port) - psbt = l1.rpc.fundpsbt(amount, '7500perkw', 250, reserve=False, + psbt = l1.rpc.fundpsbt(amount, '7500perkw', 250, reserve=0, excess_as_change=True, min_witness_weight=110)['psbt'] start = l1.rpc.openchannel_init(n.info['id'], amount, psbt) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 05cf54319bc3..38d63318b0db 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -507,7 +507,7 @@ def test_fundpsbt(node_factory, bitcoind, chainparams): feerate = '7500perkw' # Should get one input, plus some excess - funding = l1.rpc.fundpsbt(amount // 2, feerate, 0, reserve=False) + funding = l1.rpc.fundpsbt(amount // 2, feerate, 0, reserve=0) psbt = bitcoind.rpc.decodepsbt(funding['psbt']) # We can fuzz this up to 99 blocks back. assert psbt['tx']['locktime'] > bitcoind.rpc.getblockcount() - 100 @@ -520,7 +520,7 @@ def test_fundpsbt(node_factory, bitcoind, chainparams): assert 'reservations' not in funding # This should add 99 to the weight, but otherwise be identical (might choose different inputs though!) except for locktime. - funding2 = l1.rpc.fundpsbt(amount // 2, feerate, 99, reserve=False, locktime=bitcoind.rpc.getblockcount() + 1) + funding2 = l1.rpc.fundpsbt(amount // 2, feerate, 99, reserve=0, locktime=bitcoind.rpc.getblockcount() + 1) psbt2 = bitcoind.rpc.decodepsbt(funding2['psbt']) assert psbt2['tx']['locktime'] == bitcoind.rpc.getblockcount() + 1 assert len(psbt2['tx']['vin']) == 1 @@ -537,7 +537,7 @@ def test_fundpsbt(node_factory, bitcoind, chainparams): with pytest.raises(RpcError, match=r"not afford"): l1.rpc.fundpsbt(amount // 2, feerate, 0, minconf=2) - funding3 = l1.rpc.fundpsbt(amount // 2, feerate, 0, reserve=False, excess_as_change=True) + funding3 = l1.rpc.fundpsbt(amount // 2, feerate, 0, reserve=0, excess_as_change=True) assert funding3['excess_msat'] == Millisatoshi(0) # Should have the excess msat as the output value (minus fee for change) psbt = bitcoind.rpc.decodepsbt(funding3['psbt']) @@ -550,7 +550,7 @@ def test_fundpsbt(node_factory, bitcoind, chainparams): assert funding['excess_msat'] == change + change_fee # Should get two inputs. - psbt = bitcoind.rpc.decodepsbt(l1.rpc.fundpsbt(amount, feerate, 0, reserve=False)['psbt']) + psbt = bitcoind.rpc.decodepsbt(l1.rpc.fundpsbt(amount, feerate, 0, reserve=0)['psbt']) assert len(psbt['tx']['vin']) == 2 # Should not use reserved outputs. @@ -594,7 +594,7 @@ def test_utxopsbt(node_factory, bitcoind, chainparams): # Explicitly spend the first output above. funding = l1.rpc.utxopsbt(amount // 2, feerate, 0, ['{}:{}'.format(outputs[0][0], outputs[0][1])], - reserve=False) + reserve=0) psbt = bitcoind.rpc.decodepsbt(funding['psbt']) # We can fuzz this up to 99 blocks back. assert psbt['tx']['locktime'] > bitcoind.rpc.getblockcount() - 100 @@ -610,7 +610,7 @@ def test_utxopsbt(node_factory, bitcoind, chainparams): start_weight = 99 funding2 = l1.rpc.utxopsbt(amount // 2, feerate, start_weight, ['{}:{}'.format(outputs[0][0], outputs[0][1])], - reserve=False, locktime=bitcoind.rpc.getblockcount() + 1) + reserve=0, locktime=bitcoind.rpc.getblockcount() + 1) psbt2 = bitcoind.rpc.decodepsbt(funding2['psbt']) assert psbt2['tx']['locktime'] == bitcoind.rpc.getblockcount() + 1 assert psbt2['tx']['vin'] == psbt['tx']['vin'] @@ -638,7 +638,7 @@ def test_utxopsbt(node_factory, bitcoind, chainparams): funding3 = l1.rpc.utxopsbt(amount // 2, feerate, 0, ['{}:{}'.format(outputs[0][0], outputs[0][1])], - reserve=False, + reserve=0, excess_as_change=True) assert funding3['excess_msat'] == Millisatoshi(0) # Should have the excess msat as the output value (minus fee for change) @@ -655,7 +655,7 @@ def test_utxopsbt(node_factory, bitcoind, chainparams): funding4 = l1.rpc.utxopsbt(amount - 3500, feerate, 0, ['{}:{}'.format(outputs[0][0], outputs[0][1])], - reserve=False, + reserve=0, excess_as_change=True) assert 'change_outnum' not in funding4 @@ -713,8 +713,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): # Make a PSBT out of our inputs funding = l1.rpc.fundpsbt(satoshi=out_total, feerate=7500, - startweight=42, - reserve=True) + startweight=42) assert len([x for x in l1.rpc.listfunds()['outputs'] if x['reserved']]) == 4 psbt = bitcoind.rpc.decodepsbt(funding['psbt']) saved_input = psbt['tx']['vin'][0] @@ -778,8 +777,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == total_outs) l2_funding = l2.rpc.fundpsbt(satoshi=out_total, feerate=7500, - startweight=42, - reserve=True) + startweight=42) # Try to get L1 to sign it with pytest.raises(RpcError, match=r"No wallet inputs to sign"): @@ -792,8 +790,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): # Add some of our own PSBT inputs to it l1_funding = l1.rpc.fundpsbt(satoshi=out_total, feerate=7500, - startweight=42, - reserve=True) + startweight=42) l1_num_inputs = len(bitcoind.rpc.decodepsbt(l1_funding['psbt'])['tx']['vin']) l2_num_inputs = len(bitcoind.rpc.decodepsbt(l2_funding['psbt'])['tx']['vin']) @@ -833,8 +830,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): # Send a PSBT that's not ours l2_funding = l2.rpc.fundpsbt(satoshi=out_total, feerate=7500, - startweight=42, - reserve=True) + startweight=42) out_amt = Millisatoshi(l2_funding['excess_msat']) output_psbt = bitcoind.rpc.createpsbt([], [{addr: float((out_total + out_amt).to_btc())}]) diff --git a/wallet/reservation.c b/wallet/reservation.c index eea112e185b8..7130159a4d14 100644 --- a/wallet/reservation.c +++ b/wallet/reservation.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -444,14 +445,16 @@ static struct command_result *param_reserve_num(struct command *cmd, { bool flag; - /* "reserve=true" means 6 hours */ - if (json_to_bool(buffer, tok, &flag)) { - *num = tal(cmd, unsigned int); - if (flag) - **num = RESERVATION_DEFAULT; - else - **num = 0; - return NULL; + if (deprecated_apis) { + /* "reserve=true" means 6 hours */ + if (json_to_bool(buffer, tok, &flag)) { + *num = tal(cmd, unsigned int); + if (flag) + **num = RESERVATION_DEFAULT; + else + **num = 0; + return NULL; + } } return param_number(cmd, name, buffer, tok, num); From 9784fa816fd7e2ffaa847525f49dbb08534c0b21 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0627/1530] lightningd: deprecate invoice expiry suffixes. Makes types harder, and I've never personally used them. Changelog-Deprecated: JSON-RPC: `invoice` `expiry` no longer allowed to be a string with suffix, use an integer number of seconds. Signed-off-by: Rusty Russell --- doc/lightning-invoice.7.md | 6 ++---- lightningd/invoice.c | 5 ++++- tests/test_invoices.py | 27 +-------------------------- 3 files changed, 7 insertions(+), 31 deletions(-) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index b52ed45b4ded..aab55ad72f31 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -33,10 +33,8 @@ viewable by any node you send this invoice to (unless *deschashonly* is true as described below). It must be UTF-8, and cannot use *\\u* JSON escape codes. -The *expiry* is optionally the time the invoice is valid for; without a -suffix it is interpreted as seconds, otherwise suffixes *s*, *m*, *h*, -*d*, *w* indicate seconds, minutes, hours, days and weeks respectively. -If no value is provided the default of 604800 (1w) is used. +The *expiry* is optionally the time the invoice is valid for, in seconds. +If no value is provided the default of 604800 (1 week) is used. The *fallbacks* array is one or more fallback addresses to include in the invoice (in order from most-preferred to least): note that these diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 1c95e48d4abb..e68fb92208f7 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1035,6 +1035,9 @@ static struct command_result *param_time(struct command *cmd, const char *name, { 'd', 24*60*60 }, { 'w', 7*24*60*60 } }; + if (!deprecated_apis) + return param_u64(cmd, name, buffer, tok, secs); + mul = 1; if (timetok.end == timetok.start) s = '\0'; @@ -1059,7 +1062,7 @@ static struct command_result *param_time(struct command *cmd, const char *name, } return command_fail_badparam(cmd, name, buffer, tok, - "should be a number with optional {s,m,h,d,w} suffix"); + "should be a number"); } static struct command_result *param_chanhints(struct command *cmd, diff --git a/tests/test_invoices.py b/tests/test_invoices.py index e02dd2561926..8052a2e06074 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -437,37 +437,12 @@ def test_invoice_expiry(node_factory, executor): # all invoices are expired and should be deleted assert len(l2.rpc.listinvoices()['invoices']) == 0 - # Test expiry suffixes. start = int(time.time()) - inv = l2.rpc.invoice(msatoshi=123000, label='inv_s', description='description', expiry='1s')['bolt11'] + inv = l2.rpc.invoice(msatoshi=123000, label='inv_s', description='description', expiry=1)['bolt11'] end = int(time.time()) expiry = only_one(l2.rpc.listinvoices('inv_s')['invoices'])['expires_at'] assert expiry >= start + 1 and expiry <= end + 1 - start = int(time.time()) - inv = l2.rpc.invoice(msatoshi=123000, label='inv_m', description='description', expiry='1m')['bolt11'] - end = int(time.time()) - expiry = only_one(l2.rpc.listinvoices('inv_m')['invoices'])['expires_at'] - assert expiry >= start + 60 and expiry <= end + 60 - - start = int(time.time()) - inv = l2.rpc.invoice(msatoshi=123000, label='inv_h', description='description', expiry='1h')['bolt11'] - end = int(time.time()) - expiry = only_one(l2.rpc.listinvoices('inv_h')['invoices'])['expires_at'] - assert expiry >= start + 3600 and expiry <= end + 3600 - - start = int(time.time()) - inv = l2.rpc.invoice(msatoshi=123000, label='inv_d', description='description', expiry='1d')['bolt11'] - end = int(time.time()) - expiry = only_one(l2.rpc.listinvoices('inv_d')['invoices'])['expires_at'] - assert expiry >= start + 24 * 3600 and expiry <= end + 24 * 3600 - - start = int(time.time()) - inv = l2.rpc.invoice(msatoshi=123000, label='inv_w', description='description', expiry='1w')['bolt11'] - end = int(time.time()) - expiry = only_one(l2.rpc.listinvoices('inv_w')['invoices'])['expires_at'] - assert expiry >= start + 7 * 24 * 3600 and expiry <= end + 7 * 24 * 3600 - @pytest.mark.developer("Too slow without --dev-fast-gossip") def test_waitinvoice(node_factory, executor): From dc924b8859cdcc046b558acfa37e1720171fcb2e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:42:45 +1030 Subject: [PATCH 0628/1530] doc: set additionalProperties to false in all request schemas. Signed-off-by: Rusty Russell --- doc/schemas/close.request.json | 1 + doc/schemas/connect.request.json | 1 + doc/schemas/createonion.request.json | 1 + doc/schemas/datastore.request.json | 1 + doc/schemas/deldatastore.request.json | 1 + doc/schemas/fundpsbt.request.json | 1 + doc/schemas/keysend.request.json | 1 + doc/schemas/listdatastore.request.json | 1 + doc/schemas/listnodes.request.json | 1 + doc/schemas/listsendpays.request.json | 1 + doc/schemas/newaddr.request.json | 1 + doc/schemas/pay.request.json | 1 + doc/schemas/sendonion.request.json | 1 + doc/schemas/sendpay.request.json | 1 + doc/schemas/sendpsbt.request.json | 1 + doc/schemas/signpsbt.request.json | 1 + doc/schemas/txdiscard.request.json | 1 + doc/schemas/txprepare.request.json | 1 + doc/schemas/txsend.request.json | 1 + doc/schemas/utxopsbt.request.json | 1 + doc/schemas/waitanyinvoice.request.json | 1 + doc/schemas/waitinvoice.request.json | 1 + doc/schemas/waitsendpay.request.json | 1 + doc/schemas/withdraw.request.json | 1 + 24 files changed, 24 insertions(+) diff --git a/doc/schemas/close.request.json b/doc/schemas/close.request.json index c91f41a13ac4..67480ff1ea46 100644 --- a/doc/schemas/close.request.json +++ b/doc/schemas/close.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "id" ], diff --git a/doc/schemas/connect.request.json b/doc/schemas/connect.request.json index 97c40185348f..72920c7ada1e 100644 --- a/doc/schemas/connect.request.json +++ b/doc/schemas/connect.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "id" ], diff --git a/doc/schemas/createonion.request.json b/doc/schemas/createonion.request.json index 656023f4eb41..07755cd77aa9 100644 --- a/doc/schemas/createonion.request.json +++ b/doc/schemas/createonion.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "hops", "assocdata" diff --git a/doc/schemas/datastore.request.json b/doc/schemas/datastore.request.json index d34db677064a..f23105e39108 100644 --- a/doc/schemas/datastore.request.json +++ b/doc/schemas/datastore.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "key" ], diff --git a/doc/schemas/deldatastore.request.json b/doc/schemas/deldatastore.request.json index 976425afbe9a..af73d1ac2f47 100644 --- a/doc/schemas/deldatastore.request.json +++ b/doc/schemas/deldatastore.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "key" ], diff --git a/doc/schemas/fundpsbt.request.json b/doc/schemas/fundpsbt.request.json index a23d1935ac6a..3bc156acfa22 100644 --- a/doc/schemas/fundpsbt.request.json +++ b/doc/schemas/fundpsbt.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "satoshi", "feerate", diff --git a/doc/schemas/keysend.request.json b/doc/schemas/keysend.request.json index b8e6f324213e..022d18caa704 100644 --- a/doc/schemas/keysend.request.json +++ b/doc/schemas/keysend.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "destination", "msatoshi" diff --git a/doc/schemas/listdatastore.request.json b/doc/schemas/listdatastore.request.json index d1693ab2bed4..a299a89d103f 100644 --- a/doc/schemas/listdatastore.request.json +++ b/doc/schemas/listdatastore.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "key" ], diff --git a/doc/schemas/listnodes.request.json b/doc/schemas/listnodes.request.json index 75e30b4b3853..c7f1eb55611f 100644 --- a/doc/schemas/listnodes.request.json +++ b/doc/schemas/listnodes.request.json @@ -2,6 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [], + "additionalProperties": false, "properties": { "id": { "type": "pubkey" diff --git a/doc/schemas/listsendpays.request.json b/doc/schemas/listsendpays.request.json index 0ed227e81ad0..89c65cfd0b67 100644 --- a/doc/schemas/listsendpays.request.json +++ b/doc/schemas/listsendpays.request.json @@ -2,6 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [], + "additionalProperties": false, "properties": { "bolt11": { "type": "string" diff --git a/doc/schemas/newaddr.request.json b/doc/schemas/newaddr.request.json index 94eebe44f79d..90caa5b152c9 100644 --- a/doc/schemas/newaddr.request.json +++ b/doc/schemas/newaddr.request.json @@ -2,6 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [], + "additionalProperties": false, "properties": { "addresstype": { "type": "string", diff --git a/doc/schemas/pay.request.json b/doc/schemas/pay.request.json index e2c939b1ba1b..1bbd5c50739a 100644 --- a/doc/schemas/pay.request.json +++ b/doc/schemas/pay.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "bolt11" ], diff --git a/doc/schemas/sendonion.request.json b/doc/schemas/sendonion.request.json index 884ce85c4ab7..36ac75f9d3ad 100644 --- a/doc/schemas/sendonion.request.json +++ b/doc/schemas/sendonion.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "onion", "first_hop", diff --git a/doc/schemas/sendpay.request.json b/doc/schemas/sendpay.request.json index 541ac2666123..889eb8c9ebd4 100644 --- a/doc/schemas/sendpay.request.json +++ b/doc/schemas/sendpay.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "route", "payment_hash" diff --git a/doc/schemas/sendpsbt.request.json b/doc/schemas/sendpsbt.request.json index f4f4ea988627..6d2c6aceecc7 100644 --- a/doc/schemas/sendpsbt.request.json +++ b/doc/schemas/sendpsbt.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "psbt" ], diff --git a/doc/schemas/signpsbt.request.json b/doc/schemas/signpsbt.request.json index 1c5ff0383066..1841440c96a5 100644 --- a/doc/schemas/signpsbt.request.json +++ b/doc/schemas/signpsbt.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "psbt" ], diff --git a/doc/schemas/txdiscard.request.json b/doc/schemas/txdiscard.request.json index e69bf747d695..187203782288 100644 --- a/doc/schemas/txdiscard.request.json +++ b/doc/schemas/txdiscard.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "txid" ], diff --git a/doc/schemas/txprepare.request.json b/doc/schemas/txprepare.request.json index 0a161b32718c..b45075648005 100644 --- a/doc/schemas/txprepare.request.json +++ b/doc/schemas/txprepare.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "outputs" ], diff --git a/doc/schemas/txsend.request.json b/doc/schemas/txsend.request.json index e69bf747d695..187203782288 100644 --- a/doc/schemas/txsend.request.json +++ b/doc/schemas/txsend.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "txid" ], diff --git a/doc/schemas/utxopsbt.request.json b/doc/schemas/utxopsbt.request.json index 9d8541d09a48..8fe22e6bf39f 100644 --- a/doc/schemas/utxopsbt.request.json +++ b/doc/schemas/utxopsbt.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "satoshi", "feerate", diff --git a/doc/schemas/waitanyinvoice.request.json b/doc/schemas/waitanyinvoice.request.json index ed73b9db1f4e..826302319000 100644 --- a/doc/schemas/waitanyinvoice.request.json +++ b/doc/schemas/waitanyinvoice.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [], "properties": { "lastpay_index": { diff --git a/doc/schemas/waitinvoice.request.json b/doc/schemas/waitinvoice.request.json index b3ceab08d52f..18cc4093d54b 100644 --- a/doc/schemas/waitinvoice.request.json +++ b/doc/schemas/waitinvoice.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "label" ], diff --git a/doc/schemas/waitsendpay.request.json b/doc/schemas/waitsendpay.request.json index 833a929ddaf2..af3d9d2b4263 100644 --- a/doc/schemas/waitsendpay.request.json +++ b/doc/schemas/waitsendpay.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "payment_hash" ], diff --git a/doc/schemas/withdraw.request.json b/doc/schemas/withdraw.request.json index 0637f1f16a1e..55ec644623d8 100644 --- a/doc/schemas/withdraw.request.json +++ b/doc/schemas/withdraw.request.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "destination" ], From 26bee7a2ab70954d6ca53b1ffb2e79e290b72826 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:43:33 +1030 Subject: [PATCH 0629/1530] pyln-testing: add dev-pay. This will override the schema later. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 8 +++ tests/test_misc.py | 4 +- tests/test_pay.py | 62 +++++++++++----------- tests/test_plugin.py | 4 +- 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 29d8c2a31cd3..375aaf992846 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1138,6 +1138,14 @@ def config(self, config_name): except RpcError: return None + def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, + maxfeepercent=None, retry_for=None, + maxdelay=None, exemptfee=None, use_shadow=True, exclude=[]): + """Wrapper for rpc.dev_pay which suppresses the request schema""" + return self.rpc.dev_pay(bolt11, msatoshi, label, riskfactor, + maxfeepercent, retry_for, + maxdelay, exemptfee, use_shadow, exclude) + @contextmanager def flock(directory: Path): diff --git a/tests/test_misc.py b/tests/test_misc.py index 43a318b938fa..28bea132864f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -333,7 +333,7 @@ def test_htlc_out_timeout(node_factory, bitcoind, executor): inv = l2.rpc.invoice(amt, 'test_htlc_out_timeout', 'desc')['bolt11'] assert only_one(l2.rpc.listinvoices('test_htlc_out_timeout')['invoices'])['status'] == 'unpaid' - executor.submit(l1.rpc.dev_pay, inv, use_shadow=False) + executor.submit(l1.dev_pay, inv, use_shadow=False) # l1 will disconnect, and not reconnect. l1.daemon.wait_for_log('dev_disconnect: -WIRE_REVOKE_AND_ACK') @@ -400,7 +400,7 @@ def test_htlc_in_timeout(node_factory, bitcoind, executor): inv = l2.rpc.invoice(amt, 'test_htlc_in_timeout', 'desc')['bolt11'] assert only_one(l2.rpc.listinvoices('test_htlc_in_timeout')['invoices'])['status'] == 'unpaid' - executor.submit(l1.rpc.dev_pay, inv, use_shadow=False) + executor.submit(l1.dev_pay, inv, use_shadow=False) # l1 will disconnect and not reconnect. l1.daemon.wait_for_log('dev_disconnect: -WIRE_REVOKE_AND_ACK') diff --git a/tests/test_pay.py b/tests/test_pay.py index 1778d6b83bfc..106f7af1162c 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -29,7 +29,7 @@ def test_pay(node_factory): inv = l2.rpc.invoice(123000, 'test_pay', 'description')['bolt11'] before = int(time.time()) - details = l1.rpc.dev_pay(inv, use_shadow=False) + details = l1.dev_pay(inv, use_shadow=False) after = time.time() preimage = details['payment_preimage'] assert details['status'] == 'complete' @@ -44,7 +44,7 @@ def test_pay(node_factory): assert invoice['status'] == 'paid' and invoice['paid_at'] >= before and invoice['paid_at'] <= after # Repeat payments are NOPs (if valid): we can hand null. - l1.rpc.dev_pay(inv, use_shadow=False) + l1.dev_pay(inv, use_shadow=False) # This won't work: can't provide an amount (even if correct!) with pytest.raises(RpcError): l1.rpc.pay(inv, 123000) @@ -62,7 +62,7 @@ def test_pay(node_factory): # Must provide an amount! with pytest.raises(RpcError): l1.rpc.pay(inv2) - l1.rpc.dev_pay(inv2, random.randint(1000, 999999), use_shadow=False) + l1.dev_pay(inv2, random.randint(1000, 999999), use_shadow=False) # Should see 6 completed payments assert len(l1.rpc.listsendpays()['payments']) == 6 @@ -82,7 +82,7 @@ def test_pay_amounts(node_factory): assert isinstance(invoice['amount_msat'], Millisatoshi) assert invoice['amount_msat'] == Millisatoshi(123000) - l1.rpc.dev_pay(inv, use_shadow=False) + l1.dev_pay(inv, use_shadow=False) invoice = only_one(l2.rpc.listinvoices('test_pay_amounts')['invoices']) assert isinstance(invoice['amount_received_msat'], Millisatoshi) @@ -129,8 +129,8 @@ def test_pay_limits(node_factory, compat): assert(status[0]['failure']['code'] == 205) # This works, because fee is less than exemptfee. - l1.rpc.dev_pay(inv['bolt11'], msatoshi=100000, maxfeepercent=0.0001, - exemptfee=2000, use_shadow=False) + l1.dev_pay(inv['bolt11'], msatoshi=100000, maxfeepercent=0.0001, + exemptfee=2000, use_shadow=False) status = l1.rpc.call('paystatus', {'bolt11': inv['bolt11']})['pay'][2]['attempts'] assert len(status) == 1 assert status[0]['strategy'] == "Initial attempt" @@ -351,13 +351,13 @@ def test_pay_optional_args(node_factory, compat): l1, l2 = node_factory.line_graph(2) inv1 = l2.rpc.invoice(123000, 'test_pay', 'desc')['bolt11'] - l1.rpc.dev_pay(inv1, label='desc', use_shadow=False) + l1.dev_pay(inv1, label='desc', use_shadow=False) payment1 = l1.rpc.listsendpays(inv1)['payments'] assert len(payment1) and payment1[0]['msatoshi_sent'] == 123000 assert payment1[0]['label'] == 'desc' inv2 = l2.rpc.invoice(321000, 'test_pay2', 'description')['bolt11'] - l1.rpc.dev_pay(inv2, riskfactor=5.0, use_shadow=False) + l1.dev_pay(inv2, riskfactor=5.0, use_shadow=False) payment2 = l1.rpc.listsendpays(inv2)['payments'] assert(len(payment2) == 1) # The pay plugin uses `sendonion` since 0.9.0 and `lightningd` doesn't @@ -365,7 +365,7 @@ def test_pay_optional_args(node_factory, compat): # root of a payment tree with the bolt11 invoice). anyinv = l2.rpc.invoice('any', 'any_pay', 'desc')['bolt11'] - l1.rpc.dev_pay(anyinv, label='desc', msatoshi='500', use_shadow=False) + l1.dev_pay(anyinv, label='desc', msatoshi='500', use_shadow=False) payment3 = l1.rpc.listsendpays(anyinv)['payments'] assert len(payment3) == 1 assert payment3[0]['label'] == 'desc' @@ -398,7 +398,7 @@ def test_payment_success_persistence(node_factory, bitcoind, executor): inv1 = l2.rpc.invoice(1000, 'inv1', 'inv1') # Fire off a pay request, it'll get interrupted by a restart - executor.submit(l1.rpc.dev_pay, inv1['bolt11'], use_shadow=False) + executor.submit(l1.dev_pay, inv1['bolt11'], use_shadow=False) l1.daemon.wait_for_log(r'dev_disconnect: \+WIRE_COMMITMENT_SIGNED') @@ -422,8 +422,10 @@ def test_payment_success_persistence(node_factory, bitcoind, executor): l1.wait_channel_active(chanid) # A duplicate should succeed immediately (nop) and return correct preimage. - preimage = l1.rpc.dev_pay(inv1['bolt11'], - use_shadow=False)['payment_preimage'] + preimage = l1.dev_pay( + inv1['bolt11'], + use_shadow=False + )['payment_preimage'] assert l1.rpc.dev_rhash(preimage)['rhash'] == inv1['payment_hash'] @@ -1756,7 +1758,7 @@ def listpays_nofail(b11): fut = executor.submit(listpays_nofail, inv['bolt11']) # Pay l1->l5 should succeed via straight line (eventually) - l1.rpc.dev_pay(inv['bolt11'], use_shadow=False) + l1.dev_pay(inv['bolt11'], use_shadow=False) # This should be OK. fut.result() @@ -1771,7 +1773,7 @@ def listpays_nofail(b11): # Finally, fails to find a route. inv = l5.rpc.invoice(10**8, 'test_retry2', 'test_retry2')['bolt11'] with pytest.raises(RpcError, match=r'4 attempts'): - l1.rpc.dev_pay(inv, use_shadow=False) + l1.dev_pay(inv, use_shadow=False) @pytest.mark.developer("needs DEVELOPER=1 otherwise gossip takes 5 minutes!") @@ -1811,7 +1813,7 @@ def test_pay_routeboost(node_factory, bitcoind, compat): assert only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) # Now we should be able to pay it. - l1.rpc.dev_pay(inv['bolt11'], use_shadow=False) + l1.dev_pay(inv['bolt11'], use_shadow=False) # Status should show all the gory details. status = l1.rpc.call('paystatus', [inv['bolt11']]) @@ -1846,7 +1848,7 @@ def test_pay_routeboost(node_factory, bitcoind, compat): 'label': 'test_pay_routeboost2', 'description': 'test_pay_routeboost2', 'dev-routes': [routel3l4l5]}) - l1.rpc.dev_pay(inv['bolt11'], use_shadow=False) + l1.dev_pay(inv['bolt11'], use_shadow=False) status = l1.rpc.call('paystatus', [inv['bolt11']]) pay = only_one(status['pay']) attempts = pay['attempts'] @@ -1868,8 +1870,8 @@ def test_pay_routeboost(node_factory, bitcoind, compat): 'label': 'test_pay_routeboost5', 'description': 'test_pay_routeboost5', 'dev-routes': [routel3l4l5, routel3l5]}) - l1.rpc.dev_pay(inv['bolt11'], label="paying test_pay_routeboost5", - use_shadow=False) + l1.dev_pay(inv['bolt11'], label="paying test_pay_routeboost5", + use_shadow=False) status = l1.rpc.call('paystatus', [inv['bolt11']]) assert only_one(status['pay'])['bolt11'] == inv['bolt11'] @@ -2092,7 +2094,7 @@ def test_setchannel_state(node_factory, bitcoind): l0.wait_for_route(l2) inv = l2.rpc.invoice(100000, 'test_setchannel_state', 'desc')['bolt11'] - result = l0.rpc.dev_pay(inv, use_shadow=False) + result = l0.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' assert result['msatoshi_sent'] == 100042 @@ -2168,7 +2170,7 @@ def test_setchannel_routing(node_factory, bitcoind): 'description': 'desc', 'dev-routes': []}) with pytest.raises(RpcError) as routefail: - l1.rpc.dev_pay(inv['bolt11'], use_shadow=False) + l1.dev_pay(inv['bolt11'], use_shadow=False) assert routefail.value.error['attempts'][0]['failreason'] == 'No path found' # 1337 + 4000000 * 137 / 1000000 = 1885 @@ -2272,7 +2274,7 @@ def test_setchannel_zero(node_factory, bitcoind): # do and check actual payment inv = l3.rpc.invoice(4999999, 'test_setchannel_3', 'desc')['bolt11'] - result = l1.rpc.dev_pay(inv, use_shadow=False) + result = l1.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' assert result['msatoshi_sent'] == 4999999 @@ -2328,7 +2330,7 @@ def test_setchannel_restart(node_factory, bitcoind): # l1 can make payment to l3 with custom fees being applied # Note: BOLT #7 math works out to 1405 msat fees inv = l3.rpc.invoice(499999, 'test_setchannel_1', 'desc')['bolt11'] - result = l1.rpc.dev_pay(inv, use_shadow=False) + result = l1.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' assert result['msatoshi_sent'] == 501404 @@ -3320,7 +3322,7 @@ def no_more_blocks(req): # Have l1 pay l2 def pay(l1, inv): - l1.rpc.dev_pay(inv, use_shadow=False) + l1.dev_pay(inv, use_shadow=False) fut = executor.submit(pay, l1, inv) # Make sure l1 sends out the HTLC. @@ -3634,10 +3636,10 @@ def test_pay_exemptfee(node_factory, compat): err = r'Ran out of routes to try' with pytest.raises(RpcError, match=err): - l1.rpc.dev_pay(l3.rpc.invoice(1, "lbl1", "desc")['bolt11'], use_shadow=False) + l1.dev_pay(l3.rpc.invoice(1, "lbl1", "desc")['bolt11'], use_shadow=False) # If we tell our node that 5001msat is ok this should work - l1.rpc.dev_pay(l3.rpc.invoice(1, "lbl2", "desc")['bolt11'], use_shadow=False, exemptfee=5001) + l1.dev_pay(l3.rpc.invoice(1, "lbl2", "desc")['bolt11'], use_shadow=False, exemptfee=5001) # Given the above network this is the smallest amount that passes without # the fee-exemption (notice that we let it through on equality). @@ -3645,10 +3647,10 @@ def test_pay_exemptfee(node_factory, compat): # This should be just below the fee-exemption and is the first value that is allowed through with pytest.raises(RpcError, match=err): - l1.rpc.dev_pay(l3.rpc.invoice(threshold - 1, "lbl3", "desc")['bolt11'], use_shadow=False) + l1.dev_pay(l3.rpc.invoice(threshold - 1, "lbl3", "desc")['bolt11'], use_shadow=False) # While this'll work just fine - l1.rpc.dev_pay(l3.rpc.invoice(int(5001 * 200), "lbl4", "desc")['bolt11'], use_shadow=False) + l1.dev_pay(l3.rpc.invoice(int(5001 * 200), "lbl4", "desc")['bolt11'], use_shadow=False) @pytest.mark.developer("Requires use_shadow flag") @@ -3688,7 +3690,7 @@ def spendable(n1, n2): for i in range(0, direct): inv = l2.rpc.invoice(amt.millisatoshis, "lbl{}".format(i), "desc{}".format(i))['bolt11'] - l1.rpc.dev_pay(inv, use_shadow=False) + l1.dev_pay(inv, use_shadow=False) # We should not have more than amt in the direct channel anymore assert(spendable(l1, l2) < amt) @@ -3696,7 +3698,7 @@ def spendable(n1, n2): # Next one should take the alternative, but it should still work inv = l2.rpc.invoice(amt.millisatoshis, "final", "final")['bolt11'] - l1.rpc.dev_pay(inv, use_shadow=False) + l1.dev_pay(inv, use_shadow=False) def test_mpp_presplit(node_factory): @@ -4058,7 +4060,7 @@ def no_more_blocks(req): # Have l1 pay l3 def pay(l1, inv): - l1.rpc.dev_pay(inv, use_shadow=False) + l1.dev_pay(inv, use_shadow=False) fut = executor.submit(pay, l1, inv) # Make sure l1 sends out the HTLC. diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 19523613aa60..fecc7ac6e36d 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1123,7 +1123,7 @@ def test_htlc_accepted_hook_forward_restart(node_factory, executor): ], wait_for_announce=True) i1 = l3.rpc.invoice(msatoshi=1000, label="direct", description="desc")['bolt11'] - f1 = executor.submit(l1.rpc.dev_pay, i1, use_shadow=False) + f1 = executor.submit(l1.dev_pay, i1, use_shadow=False) l2.daemon.wait_for_log(r'Holding onto an incoming htlc for 10 seconds') @@ -1193,7 +1193,7 @@ def test_invoice_payment_notification(node_factory): preimage = '1' * 64 label = "a_descriptive_label" inv1 = l2.rpc.invoice(msats, label, 'description', preimage=preimage) - l1.rpc.dev_pay(inv1['bolt11'], use_shadow=False) + l1.dev_pay(inv1['bolt11'], use_shadow=False) l2.daemon.wait_for_log(r"Received invoice_payment event for label {}," " preimage {}, and amount of {}msat" From 5d8fc846756ebbaba6aa28bd41eac78e230a56f4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:43:33 +1030 Subject: [PATCH 0630/1530] pyln-testing: add dev-invoice. This will override the schema later. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 15 +++++++ tests/test_pay.py | 50 +++++++++++----------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 375aaf992846..fe0637a99dd8 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1146,6 +1146,21 @@ def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent, retry_for, maxdelay, exemptfee, use_shadow, exclude) + def dev_invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, preimage=None, exposeprivatechannels=None, cltv=None, dev_routes=None): + """Wrapper for rpc.invoice() with dev-routes option""" + payload = { + "msatoshi": msatoshi, + "label": label, + "description": description, + "expiry": expiry, + "fallbacks": fallbacks, + "preimage": preimage, + "exposeprivatechannels": exposeprivatechannels, + "cltv": cltv, + "dev-routes": dev_routes, + } + return self.rpc.call("invoice", payload) + @contextmanager def flock(directory: Path): diff --git a/tests/test_pay.py b/tests/test_pay.py index 106f7af1162c..3a23372e9b51 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -318,11 +318,10 @@ def test_pay_error_update_fees(node_factory): l1, l2, l3 = node_factory.line_graph(3, fundchannel=True, wait_for_announce=True) # Don't include any routehints in first invoice. - inv1 = l3.rpc.call('invoice', - {'msatoshi': 123000, - 'label': 'test_pay_error_update_fees', - 'description': 'description', - 'dev-routes': []}) + inv1 = l3.dev_invoice(msatoshi=123000, + label='test_pay_error_update_fees', + description='description', + dev_routes=[]) inv2 = l3.rpc.invoice(123000, 'test_pay_error_update_fees2', 'desc') # noqa: F841 @@ -1844,10 +1843,10 @@ def test_pay_routeboost(node_factory, bitcoind, compat): 'fee_base_msat': 1000, 'fee_proportional_millionths': 10, 'cltv_expiry_delta': 6}] - inv = l5.rpc.call('invoice', {'msatoshi': 10**5, - 'label': 'test_pay_routeboost2', - 'description': 'test_pay_routeboost2', - 'dev-routes': [routel3l4l5]}) + inv = l5.dev_invoice(msatoshi=10**5, + label='test_pay_routeboost2', + description='test_pay_routeboost2', + dev_routes=[routel3l4l5]) l1.dev_pay(inv['bolt11'], use_shadow=False) status = l1.rpc.call('paystatus', [inv['bolt11']]) pay = only_one(status['pay']) @@ -1866,10 +1865,10 @@ def test_pay_routeboost(node_factory, bitcoind, compat): 'fee_base_msat': 1000, 'fee_proportional_millionths': 10, 'cltv_expiry_delta': 6}] - inv = l5.rpc.call('invoice', {'msatoshi': 10**5, - 'label': 'test_pay_routeboost5', - 'description': 'test_pay_routeboost5', - 'dev-routes': [routel3l4l5, routel3l5]}) + inv = l5.dev_invoice(msatoshi=10**5, + label='test_pay_routeboost5', + description='test_pay_routeboost5', + dev_routes=[routel3l4l5, routel3l5]) l1.dev_pay(inv['bolt11'], label="paying test_pay_routeboost5", use_shadow=False) @@ -2165,10 +2164,10 @@ def test_setchannel_routing(node_factory, bitcoind): l1.rpc.getroute(l3.info['id'], 4001793, 1, fuzzpercent=0)["route"] # We should consider this unroutable! (MPP is disabled!) - inv = l3.rpc.call('invoice', {'msatoshi': 4001793, - 'label': 'test_setchannel_1', - 'description': 'desc', - 'dev-routes': []}) + inv = l3.dev_invoice(msatoshi=4001793, + label='test_setchannel_1', + description='desc', + dev_routes=[]) with pytest.raises(RpcError) as routefail: l1.dev_pay(inv['bolt11'], use_shadow=False) assert routefail.value.error['attempts'][0]['failreason'] == 'No path found' @@ -2701,14 +2700,14 @@ def test_tlv_or_legacy(node_factory, bitcoind): # We need to force l3 to provide route hint from l2 (it won't normally, # since it sees l2 as a dead end). - inv = l3.rpc.call('invoice', {"msatoshi": 10000, - "label": "test_tlv1", - "description": "test_tlv1", - "dev-routes": [[{'id': l2.info['id'], - 'short_channel_id': scid23, - 'fee_base_msat': 1, - 'fee_proportional_millionths': 10, - 'cltv_expiry_delta': 6}]]})['bolt11'] + inv = l3.dev_invoice(msatoshi=10000, + label="test_tlv1", + description="test_tlv1", + dev_routes=[[{'id': l2.info['id'], + 'short_channel_id': scid23, + 'fee_base_msat': 1, + 'fee_proportional_millionths': 10, + 'cltv_expiry_delta': 6}]])['bolt11'] l1.rpc.pay(inv) # Since L1 hasn't seen broadcast, it doesn't know L2 isn't TLV, but invoice tells it about L3 @@ -2732,7 +2731,6 @@ def test_tlv_or_legacy(node_factory, bitcoind): l3.daemon.wait_for_log("Got onion.*'type': 'tlv'") -@pytest.mark.developer('Needs dev-routes') @unittest.skipIf(TEST_NETWORK != 'regtest', "Invoice is network specific") def test_pay_no_secret(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, wait_for_announce=True) From 1b6f4e70266827ccf87b339479b2e9c973baad55 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:43:33 +1030 Subject: [PATCH 0631/1530] pytest: don't hand a string for an integer value. lightningd is happy, but our schema won't be. Signed-off-by: Rusty Russell --- tests/test_connection.py | 2 +- tests/test_invoices.py | 4 ++-- tests/test_pay.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 523b90c1a6c4..e82a5ec7baf7 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -186,7 +186,7 @@ def test_connection_moved(node_factory, executor): log = l1.daemon.wait_for_log('listening for connections') match = re.search(r'on port (\d*)', log) assert match and len(match.groups()) == 1 - hang_port = match.groups()[0] + hang_port = int(match.groups()[0]) # Attempt connection fut_hang = executor.submit(l1.rpc.connect, l2.info['id'], diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 8052a2e06074..bb90f1d10128 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -15,7 +15,7 @@ def test_invoice(node_factory, chainparams): addr1 = l2.rpc.newaddr('bech32')['bech32'] addr2 = l2.rpc.newaddr('p2sh-segwit')['p2sh-segwit'] before = int(time.time()) - inv = l1.rpc.invoice(123000, 'label', 'description', '3700', [addr1, addr2]) + inv = l1.rpc.invoice(123000, 'label', 'description', 3700, [addr1, addr2]) after = int(time.time()) b11 = l1.rpc.decodepay(inv['bolt11']) assert b11['currency'] == chainparams['bip173_prefix'] @@ -55,7 +55,7 @@ def test_invoice(node_factory, chainparams): assert 'warning_capacity' in inv # Test cltv option. - inv = l1.rpc.invoice(123000, 'label3', 'description', '3700', cltv=99) + inv = l1.rpc.invoice(123000, 'label3', 'description', 3700, cltv=99) b11 = l1.rpc.decodepay(inv['bolt11']) assert b11['min_final_cltv_expiry'] == 99 diff --git a/tests/test_pay.py b/tests/test_pay.py index 3a23372e9b51..678f83f5b94e 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -364,7 +364,7 @@ def test_pay_optional_args(node_factory, compat): # root of a payment tree with the bolt11 invoice). anyinv = l2.rpc.invoice('any', 'any_pay', 'desc')['bolt11'] - l1.dev_pay(anyinv, label='desc', msatoshi='500', use_shadow=False) + l1.dev_pay(anyinv, label='desc', msatoshi=500, use_shadow=False) payment3 = l1.rpc.listsendpays(anyinv)['payments'] assert len(payment3) == 1 assert payment3[0]['label'] == 'desc' @@ -539,7 +539,7 @@ def test_pay_maxfee_shadow(node_factory): # maxfeepercent. amount = 20000 bolt11 = l2.rpc.invoice(amount, "big.{}".format(i), "bigger")["bolt11"] - pay_status = l1.rpc.pay(bolt11, maxfeepercent="0.000001") + pay_status = l1.rpc.pay(bolt11, maxfeepercent=0.001) assert pay_status["amount_msat"] == Millisatoshi(amount) @@ -5220,7 +5220,7 @@ def test_pay_manual_exclude(node_factory, bitcoind): chan23 = l2.rpc.listpeers(l3_id)['peers'][0]['channels'][0] scid12 = chan12['short_channel_id'] + '/' + str(chan12['direction']) scid23 = chan23['short_channel_id'] + '/' + str(chan23['direction']) - inv = l3.rpc.invoice(msatoshi='123000', label='label1', description='desc')['bolt11'] + inv = l3.rpc.invoice(msatoshi=123000, label='label1', description='desc')['bolt11'] # Exclude the payer node id with pytest.raises(RpcError, match=r'Payer is manually excluded'): l1.rpc.pay(inv, exclude=[l1_id]) From 3b11292e72e0b796ac7fe7fef1e9462bf40ddb0a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:43:33 +1030 Subject: [PATCH 0632/1530] pyln-testing: add new schema types. These are useful for requests: 1. "outpoint": : 2. "feerate": strings or a number 3. "outputdesc": bitcoin-style addresses-as-keys 4. "msat_or_all": amount or "all" 4. "msat_or_any": amount or "any" 5. "short_channel_id_dir": scid with /0 or /1. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/fixtures.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index 8742bf7270ce..9d55a4a00ac6 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -266,6 +266,48 @@ def is_short_channel_id(checker, instance): and txnum >= 0 and txnum < 2**24 and outnum >= 0 and outnum < 2**16) + def is_short_channel_id_dir(checker, instance): + """Short channel id with direction""" + if not checker.is_type(instance, "string"): + return False + if not instance.endswith("/0") and not instance.endswith("/1"): + return False + return is_short_channel_id(checker, instance[:-2]) + + def is_outpoint(checker, instance): + """Outpoint: txid and outnum""" + if not checker.is_type(instance, "string"): + return False + parts = instance.split(":") + if len(parts) != 2: + return False + if len(parts[0]) != 64 or any(c not in string.hexdigits for c in parts[0]): + return False + try: + outnum = int(parts[1]) + except ValueError: + return False + return outnum < 2**32 + + def is_feerate(checker, instance): + """feerate string or number (optionally ending in perkw/perkb)""" + if checker.is_type(instance, "integer"): + return True + if not checker.is_type(instance, "string"): + return False + if instance in ("urgent", "normal", "slow"): + return True + if instance in ("opening", "mutual_close", "unilateral_close", "delayed_to_us", "htlc_resolution", "penalty", "min_acceptable", "max_acceptable"): + return True + if not instance.endswith("perkw") and not instance.endswith("perkb"): + return False + + try: + int(instance.rpartition("per")[0]) + except ValueError: + return False + return True + def is_pubkey(checker, instance): """SEC1 encoded compressed pubkey""" if not checker.is_type(instance, "hex"): @@ -308,6 +350,30 @@ def is_txid(checker, instance): return False return len(instance) == 64 + def is_outputdesc(checker, instance): + """Bitcoin-style output object, keys = destination, values = amount""" + if not checker.is_type(instance, "object"): + return False + for k, v in instance.items(): + if not checker.is_type(k, "string"): + return False + if v != "all": + if not is_msat_request(checker, v): + return False + return True + + def is_msat_or_all(checker, instance): + """msat field, or 'all'""" + if instance == "all": + return True + return is_msat_request(checker, instance) + + def is_msat_or_any(checker, instance): + """msat field, or 'any'""" + if instance == "any": + return True + return is_msat_request(checker, instance) + type_checker = jsonschema.Draft7Validator.TYPE_CHECKER.redefine_many({ "hex": is_hex, "u64": is_u64, @@ -316,11 +382,17 @@ def is_txid(checker, instance): "u8": is_u8, "pubkey": is_pubkey, "msat": is_msat, + "msat_or_all": is_msat_or_all, + "msat_or_any": is_msat_or_any, "txid": is_txid, "signature": is_signature, "bip340sig": is_bip340sig, "point32": is_point32, "short_channel_id": is_short_channel_id, + "short_channel_id_dir": is_short_channel_id_dir, + "outpoint": is_outpoint, + "feerate": is_feerate, + "outputdesc": is_outputdesc, }) return jsonschema.validators.extend(jsonschema.Draft7Validator, From b45b731c556b2dbd9b97a839d670ebaff5e438c5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0633/1530] doc/schemas: fixes for request schemas. Types are fixed, in particular: * rename "OutputDesc" to more consistent "outputdesc". * rename "utxo" to more consistent "outpoint". * it's "boolean" not "bool". * "number" means int or float, usually it should be u32. Specific commands: * close `id` can be by channel id, scid. * close `feerange` is a feerate type. * datastore/deldatastore/listdatastore `key` can be singleton. * delexpiredinvoice: `maxexpirytime` is not required, is a u64. * invoice/delinvoice/listinvoice `label` can be an integer * fundpsbt: many fields are u32 not number (JSON for int or float). * invoice: `msatoshi` can be "any". * invoice: `expiry` has a type (now must be numeric). * invoice: `exposeprivatechannels` can be bool or array of scids. * invoice: `deschashonly` added * keysend: there's no "float" type, use "number" or "u32" etc. * keysend: `routehints` is a valid arg, as is `extratlvs` (EXPERIMENTAL_FEATURES) * listdatastore: `key` is not required. * newaddr: `addresstype` can be "all" * pay: `exemptfee` is "msat", new fields `locaofferid` and `exclude` * sendonion: was mis-formatted, missed `localofferid` and `groupid` fields. * sendpay: add `localofferid` and `groupid` params. * signpsbt: add `signonly` param. * txprepare "outptus" typo. * waitsendpay: add `groupid` and fix `partid` type. * withdraw: `destination` is a bitcoin address, not a pubkey. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 7 ++- doc/schemas/close.request.json | 10 ++--- doc/schemas/connect.request.json | 2 +- doc/schemas/datastore.request.json | 21 ++++++--- doc/schemas/deldatastore.request.json | 17 ++++--- doc/schemas/delexpiredinvoice.request.json | 6 +-- doc/schemas/delinvoice.request.json | 15 ++++++- doc/schemas/fundpsbt.request.json | 10 ++--- doc/schemas/invoice.request.json | 37 ++++++++++++--- doc/schemas/keysend.request.json | 45 +++++++++++++++++-- doc/schemas/listdatastore.request.json | 22 +++++---- doc/schemas/listinvoices.request.json | 12 ++++- doc/schemas/newaddr.request.json | 3 +- doc/schemas/pay.request.json | 22 +++++++-- doc/schemas/sendonion.request.json | 52 ++++++++++++---------- doc/schemas/sendpay.request.json | 6 +++ doc/schemas/sendpsbt.request.json | 2 +- doc/schemas/signpsbt.request.json | 6 +++ doc/schemas/txprepare.request.json | 6 +-- doc/schemas/utxopsbt.request.json | 12 ++--- doc/schemas/waitanyinvoice.request.json | 4 +- doc/schemas/waitsendpay.request.json | 9 ++-- doc/schemas/withdraw.request.json | 6 +-- 23 files changed, 240 insertions(+), 92 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index fe0637a99dd8..fdde6caa1e8e 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1159,7 +1159,12 @@ def dev_invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, "cltv": cltv, "dev-routes": dev_routes, } - return self.rpc.call("invoice", payload) + # FIXME? dev options are not in schema + old_check = self.rpc.check_request_schemas + self.rpc.check_request_schemas = False + ret = self.rpc.call("invoice", payload) + self.rpc.check_request_schemas = old_check + return ret @contextmanager diff --git a/doc/schemas/close.request.json b/doc/schemas/close.request.json index 67480ff1ea46..1a6d8e0dc523 100644 --- a/doc/schemas/close.request.json +++ b/doc/schemas/close.request.json @@ -7,8 +7,8 @@ ], "properties": { "id": { - "type": "pubkey", - "description": "" + "type": "string", + "description": "peer id, channel id or short_channel_id" }, "unilateraltimeout": { "type": "u32", @@ -31,9 +31,9 @@ "description": "" }, "feerange": { - "type": { - "type": "array", - "items": "u32" + "type": "array", + "items": { + "type": "feerate" }, "description": "" } diff --git a/doc/schemas/connect.request.json b/doc/schemas/connect.request.json index 72920c7ada1e..48303300b487 100644 --- a/doc/schemas/connect.request.json +++ b/doc/schemas/connect.request.json @@ -7,7 +7,7 @@ ], "properties": { "id": { - "type": "pubkey", + "type": "string", "description": "" }, "host": { diff --git a/doc/schemas/datastore.request.json b/doc/schemas/datastore.request.json index f23105e39108..d9c0a95e5925 100644 --- a/doc/schemas/datastore.request.json +++ b/doc/schemas/datastore.request.json @@ -7,11 +7,22 @@ ], "properties": { "key": { - "type": "array", - "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", - "items": { - "type": "string" - } + "oneOf": [ + { + "type": "array", + "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "string": { + "type": "string", + "description": "" }, "hex": { "type": "hex", diff --git a/doc/schemas/deldatastore.request.json b/doc/schemas/deldatastore.request.json index af73d1ac2f47..22367461323a 100644 --- a/doc/schemas/deldatastore.request.json +++ b/doc/schemas/deldatastore.request.json @@ -7,11 +7,18 @@ ], "properties": { "key": { - "type": "array", - "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", - "items": { - "type": "string" - } + "oneOf": [ + { + "type": "array", + "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, "generation": { "type": "u64", diff --git a/doc/schemas/delexpiredinvoice.request.json b/doc/schemas/delexpiredinvoice.request.json index 7c86c8bc050b..45ffc910e1ff 100644 --- a/doc/schemas/delexpiredinvoice.request.json +++ b/doc/schemas/delexpiredinvoice.request.json @@ -2,12 +2,10 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, - "required": [ - "maxexpirytime" - ], + "required": [], "properties": { "maxexpirytime": { - "type": "u32", + "type": "u64", "description": "" } } diff --git a/doc/schemas/delinvoice.request.json b/doc/schemas/delinvoice.request.json index f692129e039f..1e2493839159 100644 --- a/doc/schemas/delinvoice.request.json +++ b/doc/schemas/delinvoice.request.json @@ -8,8 +8,16 @@ ], "properties": { "label": { - "type": "string", - "description": "" + "oneOf": [ + { + "type": "string", + "description": "" + }, + { + "type": "integer", + "description": "" + } + ] }, "status": { "type": "string", @@ -18,6 +26,9 @@ "expired", "unpaid" ] + }, + "desconly": { + "type": "boolean" } } } diff --git a/doc/schemas/fundpsbt.request.json b/doc/schemas/fundpsbt.request.json index 3bc156acfa22..f53d3f2d8578 100644 --- a/doc/schemas/fundpsbt.request.json +++ b/doc/schemas/fundpsbt.request.json @@ -15,23 +15,23 @@ "type": "feerate" }, "startweight": { - "type": "number" + "type": "u32" }, "minconf": { - "type": "number" + "type": "u32" }, "reserve": { - "type": "number", + "type": "u32", "description": "reserve is a number: if non-zero number then reserveinputs is called (successfully, with exclusive true) on the returned PSBT for this number of blocks (default: 72)." }, "locktime": { - "type": "number" + "type": "u32" }, "min_witness_weight": { "type": "u32" }, "excess_as_change": { - "type": "bool" + "type": "boolean" } } } diff --git a/doc/schemas/invoice.request.json b/doc/schemas/invoice.request.json index 34d40c267b57..a22cd12b056c 100644 --- a/doc/schemas/invoice.request.json +++ b/doc/schemas/invoice.request.json @@ -9,7 +9,7 @@ ], "properties": { "msatoshi": { - "type": "msat|any", + "type": "msat_or_any", "description": "" }, "description": { @@ -17,11 +17,19 @@ "description": "" }, "label": { - "type": "string", - "description": "" + "oneOf": [ + { + "type": "string", + "description": "" + }, + { + "type": "integer", + "description": "" + } + ] }, "expiry": { - "type": "", + "type": "u64", "description": "" }, "fallbacks": { @@ -36,12 +44,29 @@ "description": "" }, "exposeprivatechannels": { - "type": "bool", - "description": "" + "oneOf": [ + { + "type": "boolean", + "description": "" + }, + { + "type": "array", + "items": { + "type": "short_channel_id" + } + }, + { + "type": "short_channel_id" + } + ] }, "cltv": { "type": "u32", "description": "" + }, + "deschashonly": { + "type": "boolean", + "description": "" } } } diff --git a/doc/schemas/keysend.request.json b/doc/schemas/keysend.request.json index 022d18caa704..fef9a5ab57e0 100644 --- a/doc/schemas/keysend.request.json +++ b/doc/schemas/keysend.request.json @@ -17,16 +17,55 @@ "type": "string" }, "maxfeepercent": { - "type": "float" + "type": "number" }, "retry_for": { - "type": "number" + "type": "u32" }, "maxdelay": { - "type": "number" + "type": "u32" }, "exemptfee": { "type": "msat" + }, + "routehints": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "scid", + "feebase", + "feeprop", + "expirydelta" + ], + "properties": { + "id": { + "type": "pubkey" + }, + "scid": { + "type": "short_channel_id" + }, + "feebase": { + "type": "msat" + }, + "feeprop": { + "type": "u32" + }, + "expirydelta": { + "type": "u16" + } + } + } + } + }, + "extratlvs": { + "type": "object", + "additionalProperties": true, + "required": [] } } } diff --git a/doc/schemas/listdatastore.request.json b/doc/schemas/listdatastore.request.json index a299a89d103f..fc817532724f 100644 --- a/doc/schemas/listdatastore.request.json +++ b/doc/schemas/listdatastore.request.json @@ -2,16 +2,22 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, - "required": [ - "key" - ], + "required": [], "properties": { "key": { - "type": "array", - "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", - "items": { - "type": "string" - } + "oneOf": [ + { + "type": "array", + "description": "key is an array of values (though a single value is treated as a one-element array), to form a heirarchy. Using the first element of the key as the plugin name (e.g. [ 'summary' ]) is recommended. A key can either have children or a value, never both: parents are created and removed automatically.", + "items": { + "type": "string" + } + }, + { + "type": "string", + "description": "" + } + ] } } } diff --git a/doc/schemas/listinvoices.request.json b/doc/schemas/listinvoices.request.json index 6c4bb01f6c92..dc22b5b04a5c 100644 --- a/doc/schemas/listinvoices.request.json +++ b/doc/schemas/listinvoices.request.json @@ -5,8 +5,16 @@ "required": [], "properties": { "label": { - "type": "string", - "description": "" + "oneOf": [ + { + "type": "string", + "description": "" + }, + { + "type": "integer", + "description": "" + } + ] }, "invstring": { "type": "string", diff --git a/doc/schemas/newaddr.request.json b/doc/schemas/newaddr.request.json index 90caa5b152c9..7140f97bfc25 100644 --- a/doc/schemas/newaddr.request.json +++ b/doc/schemas/newaddr.request.json @@ -8,7 +8,8 @@ "type": "string", "enum": [ "bech32", - "p2sh-segwit" + "p2sh-segwit", + "all" ] } } diff --git a/doc/schemas/pay.request.json b/doc/schemas/pay.request.json index 1bbd5c50739a..d7bcb5f85a15 100644 --- a/doc/schemas/pay.request.json +++ b/doc/schemas/pay.request.json @@ -16,10 +16,10 @@ "type": "string" }, "riskfactor": { - "type": "float" + "type": "number" }, "maxfeepercent": { - "type": "f32" + "type": "number" }, "retry_for": { "type": "u16" @@ -28,7 +28,23 @@ "type": "u16" }, "exemptfee": { - "type": "f32" + "type": "msat" + }, + "localofferid": { + "type": "hex" + }, + "exclude": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "short_channel_id_dir" + }, + { + "type": "pubkey" + } + ] + } } } } diff --git a/doc/schemas/sendonion.request.json b/doc/schemas/sendonion.request.json index 36ac75f9d3ad..531f68093de7 100644 --- a/doc/schemas/sendonion.request.json +++ b/doc/schemas/sendonion.request.json @@ -28,31 +28,37 @@ "delay": { "type": "u16" } - }, - "payment_hash": { + } + }, + "payment_hash": { + "type": "hex" + }, + "label": { + "type": "string" + }, + "shared_secrets": { + "type": "array", + "items": { "type": "hex" - }, - "label": { - "type": "string" - }, - "shared_secrets": { - "type": "array", - "itemtype": { - "type": "hex" - } - }, - "partid": { - "type": "u16" - }, - "bolt11": { - "type": "string" - }, - "msatoshi": { - "type": "msat" - }, - "destination": { - "type": "pubkey" } + }, + "partid": { + "type": "u16" + }, + "bolt11": { + "type": "string" + }, + "msatoshi": { + "type": "msat" + }, + "destination": { + "type": "pubkey" + }, + "localofferid": { + "type": "hash" + }, + "groupid": { + "type": "u64" } } } diff --git a/doc/schemas/sendpay.request.json b/doc/schemas/sendpay.request.json index 889eb8c9ebd4..c8b854a9e316 100644 --- a/doc/schemas/sendpay.request.json +++ b/doc/schemas/sendpay.request.json @@ -50,6 +50,12 @@ }, "partid": { "type": "u16" + }, + "localofferid": { + "type": "hex" + }, + "groupid": { + "type": "u64" } } } diff --git a/doc/schemas/sendpsbt.request.json b/doc/schemas/sendpsbt.request.json index 6d2c6aceecc7..bc55bb8308ba 100644 --- a/doc/schemas/sendpsbt.request.json +++ b/doc/schemas/sendpsbt.request.json @@ -10,7 +10,7 @@ "type": "string" }, "reserve": { - "type": "bool" + "type": "boolean" } } } diff --git a/doc/schemas/signpsbt.request.json b/doc/schemas/signpsbt.request.json index 1841440c96a5..c8a2bbf85555 100644 --- a/doc/schemas/signpsbt.request.json +++ b/doc/schemas/signpsbt.request.json @@ -8,6 +8,12 @@ "properties": { "psbt": { "type": "string" + }, + "signonly": { + "type": "array", + "items": { + "type": "u32" + } } } } diff --git a/doc/schemas/txprepare.request.json b/doc/schemas/txprepare.request.json index b45075648005..936c4d9e5880 100644 --- a/doc/schemas/txprepare.request.json +++ b/doc/schemas/txprepare.request.json @@ -6,10 +6,10 @@ "outputs" ], "properties": { - "outptus": { + "outputs": { "type": "array", "items": { - "type": "OutputDesc" + "type": "outputdesc" } }, "feerate": { @@ -21,7 +21,7 @@ "utxos": { "type": "array", "items": { - "type": "utxo" + "type": "outpoint" } } } diff --git a/doc/schemas/utxopsbt.request.json b/doc/schemas/utxopsbt.request.json index 8fe22e6bf39f..1e1b07181438 100644 --- a/doc/schemas/utxopsbt.request.json +++ b/doc/schemas/utxopsbt.request.json @@ -16,29 +16,29 @@ "type": "feerate" }, "startweight": { - "type": "number" + "type": "u32" }, "utxos": { "type": "array", "items": { - "type": "utxo" + "type": "outpoint" } }, "reserve": { - "type": "number", + "type": "u32", "description": "reserve is a number: if non-zero number then reserveinputs is called (successfully, with exclusive true) on the returned PSBT for this number of blocks (default: 72)." }, "reservedok": { - "type": "bool" + "type": "boolean" }, "locktime": { - "type": "number" + "type": "u32" }, "min_witness_weight": { "type": "u32" }, "excess_as_change": { - "type": "bool" + "type": "boolean" } } } diff --git a/doc/schemas/waitanyinvoice.request.json b/doc/schemas/waitanyinvoice.request.json index 826302319000..5c9ce9867bb0 100644 --- a/doc/schemas/waitanyinvoice.request.json +++ b/doc/schemas/waitanyinvoice.request.json @@ -5,10 +5,10 @@ "required": [], "properties": { "lastpay_index": { - "type": "number" + "type": "u64" }, "timeout": { - "type": "number" + "type": "u64" } } } diff --git a/doc/schemas/waitsendpay.request.json b/doc/schemas/waitsendpay.request.json index af3d9d2b4263..e4734025275b 100644 --- a/doc/schemas/waitsendpay.request.json +++ b/doc/schemas/waitsendpay.request.json @@ -9,11 +9,14 @@ "payment_hash": { "type": "hex" }, - "partid": { - "type": "u16" - }, "timeout": { "type": "u32" + }, + "partid": { + "type": "u64" + }, + "groupid": { + "type": "u64" } } } diff --git a/doc/schemas/withdraw.request.json b/doc/schemas/withdraw.request.json index 55ec644623d8..8a43b988fa94 100644 --- a/doc/schemas/withdraw.request.json +++ b/doc/schemas/withdraw.request.json @@ -7,10 +7,10 @@ ], "properties": { "destination": { - "type": "pubkey" + "type": "string" }, "satoshi": { - "type": "msat|all" + "type": "msat_or_all" }, "feerate": { "type": "feerate" @@ -21,7 +21,7 @@ "utxos": { "type": "array", "items": { - "type": "utxo" + "type": "outpoint" } } } From c1ee32027d75f528372337e1430e2a249d5667a2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0634/1530] pyln-testing: check the request schemas. This means suppressing schemas in some places too. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/fixtures.py | 39 +++++++++++++++---- contrib/pyln-testing/pyln/testing/utils.py | 29 +++++++++++--- tests/test_invoices.py | 1 + tests/test_misc.py | 3 ++ tests/test_pay.py | 2 + tests/test_wallet.py | 12 ++++++ 6 files changed, 73 insertions(+), 13 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index 9d55a4a00ac6..ec6271bbb924 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -206,7 +206,7 @@ def throttler(test_base_dir): yield Throttler(test_base_dir) -def _extra_validator(): +def _extra_validator(is_request: bool): """JSON Schema validator with additions for our specialized types""" def is_hex(checker, instance): """Hex string""" @@ -340,7 +340,15 @@ def is_bip340sig(checker, instance): return False return True - def is_msat(checker, instance): + def is_msat_request(checker, instance): + """msat fields can be raw integers, sats, btc.""" + try: + Millisatoshi(instance) + return True + except TypeError: + return False + + def is_msat_response(checker, instance): """String number ending in msat""" return type(instance) is Millisatoshi @@ -374,6 +382,11 @@ def is_msat_or_any(checker, instance): return True return is_msat_request(checker, instance) + # "msat" for request can be many forms + if is_request: + is_msat = is_msat_request + else: + is_msat = is_msat_response type_checker = jsonschema.Draft7Validator.TYPE_CHECKER.redefine_many({ "hex": is_hex, "u64": is_u64, @@ -399,15 +412,15 @@ def is_msat_or_any(checker, instance): type_checker=type_checker) -def _load_schema(filename): +def _load_schema(filename, is_request): """Load the schema from @filename and create a validator for it""" with open(filename, 'r') as f: - return _extra_validator()(json.load(f)) + return _extra_validator(is_request)(json.load(f)) @pytest.fixture(autouse=True) def jsonschemas(): - """Load schema files if they exist""" + """Load schema files if they exist: returns request/response schemas by pairs""" try: schemafiles = os.listdir('doc/schemas') except FileNotFoundError: @@ -415,10 +428,20 @@ def jsonschemas(): schemas = {} for fname in schemafiles: - if not fname.endswith('.schema.json'): + if fname.endswith('.schema.json'): + base = fname.rpartition('.schema')[0] + is_request = False + index = 1 + elif fname.endswith('.request.json'): + base = fname.rpartition('.request')[0] + is_request = True + index = 0 + else: continue - schemas[fname.rpartition('.schema')[0]] = _load_schema(os.path.join('doc/schemas', - fname)) + if base not in schemas: + schemas[base] = [None, None] + schemas[base][index] = _load_schema(os.path.join('doc/schemas', fname), + is_request) return schemas diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index fdde6caa1e8e..72f8d9b5aa74 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -628,22 +628,36 @@ def __init__(self, socket_path, executor=None, logger=logging, patch_json, ) self.jsonschemas = jsonschemas + self.check_request_schemas = True def call(self, method, payload=None): id = self.next_id + schemas = self.jsonschemas.get(method) self.logger.debug(json.dumps({ "id": id, "method": method, "params": payload }, indent=2)) + + # We only check payloads which are dicts, which is what we + # usually use: there are some cases which tests [] params, + # which we ignore. + if schemas and schemas[0] and isinstance(payload, dict) and self.check_request_schemas: + # fields which are None are explicitly removed, so do that now + testpayload = {} + for k, v in payload.items(): + if v is not None: + testpayload[k] = v + schemas[0].validate(testpayload) + res = LightningRpc.call(self, method, payload) self.logger.debug(json.dumps({ "id": id, "result": res }, indent=2)) - if method in self.jsonschemas: - self.jsonschemas[method].validate(res) + if schemas and schemas[1]: + schemas[1].validate(res) return res @@ -1142,9 +1156,14 @@ def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, use_shadow=True, exclude=[]): """Wrapper for rpc.dev_pay which suppresses the request schema""" - return self.rpc.dev_pay(bolt11, msatoshi, label, riskfactor, - maxfeepercent, retry_for, - maxdelay, exemptfee, use_shadow, exclude) + # FIXME? dev options are not in schema + old_check = self.rpc.check_request_schemas + self.rpc.check_request_schemas = False + ret = self.rpc.dev_pay(bolt11, msatoshi, label, riskfactor, + maxfeepercent, retry_for, + maxdelay, exemptfee, use_shadow, exclude) + self.rpc.check_request_schemas = old_check + return ret def dev_invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, preimage=None, exposeprivatechannels=None, cltv=None, dev_routes=None): """Wrapper for rpc.invoice() with dev-routes option""" diff --git a/tests/test_invoices.py b/tests/test_invoices.py index bb90f1d10128..1dec61081165 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -529,6 +529,7 @@ def test_waitanyinvoice(node_factory, executor): r = executor.submit(l2.rpc.waitanyinvoice, pay_index, 0).result(timeout=5) assert r['label'] == 'inv4' + l2.rpc.check_request_schemas = False with pytest.raises(RpcError): l2.rpc.waitanyinvoice('non-number') diff --git a/tests/test_misc.py b/tests/test_misc.py index 28bea132864f..84d2836efb8f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -493,6 +493,7 @@ def dont_spend_outputs(n, txid): waddr = l1.bitcoin.getnewaddress() # Now attempt to withdraw some (making sure we collect multiple inputs) + l1.rpc.check_request_schemas = False with pytest.raises(RpcError): l1.rpc.withdraw('not an address', amount) with pytest.raises(RpcError): @@ -501,6 +502,7 @@ def dont_spend_outputs(n, txid): l1.rpc.withdraw(waddr, -amount) with pytest.raises(RpcError, match=r'Could not afford'): l1.rpc.withdraw(waddr, amount * 100) + l1.rpc.check_request_schemas = True out = l1.rpc.withdraw(waddr, amount) @@ -1516,6 +1518,7 @@ def test_configfile_before_chdir(node_factory): def test_json_error(node_factory): """Must return valid json even if it quotes our weirdness""" l1 = node_factory.get_node() + l1.rpc.check_request_schemas = False with pytest.raises(RpcError, match=r'id: should be a channel ID or short channel ID: invalid token'): l1.rpc.close({"tx": "020000000001011490f737edd2ea2175a032b58ea7cd426dfc244c339cd044792096da3349b18a0100000000ffffffff021c900300000000001600140e64868e2f752314bc82a154c8c5bf32f3691bb74da00b00000000002200205b8cd3b914cf67cdd8fa6273c930353dd36476734fbd962102c2df53b90880cd0247304402202b2e3195a35dc694bbbc58942dc9ba59cc01d71ba55c9b0ad0610ccd6a65633702201a849254453d160205accc00843efb0ad1fe0e186efa6a7cee1fb6a1d36c736a012103d745445c9362665f22e0d96e9e766f273f3260dea39c8a76bfa05dd2684ddccf00000000", "txid": "2128c10f0355354479514f4a23eaa880d94e099406d419bbb0d800143accddbb", "channel_id": "bbddcc3a1400d8b0bb19d40694094ed980a8ea234a4f5179443555030fc12820"}) diff --git a/tests/test_pay.py b/tests/test_pay.py index 678f83f5b94e..418c29a039db 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -586,11 +586,13 @@ def invoice_unpaid(dst, label): assert invoice_unpaid(l2, 'testpayment2') # Bad ID. + l1.rpc.check_request_schemas = False with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['id'] = '00000000000000000000000000000000' l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) assert invoice_unpaid(l2, 'testpayment2') + l1.rpc.check_request_schemas = True # Bad payment_secret l1.rpc.sendpay([routestep], rhash, payment_secret="00" * 32) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 38d63318b0db..d53d4d9f1281 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -44,6 +44,9 @@ def test_withdraw(node_factory, bitcoind): waddr = l1.bitcoin.rpc.getnewaddress() # Now attempt to withdraw some (making sure we collect multiple inputs) + + # These violate schemas! + l1.rpc.check_request_schemas = False with pytest.raises(RpcError): l1.rpc.withdraw('not an address', amount) with pytest.raises(RpcError): @@ -52,6 +55,7 @@ def test_withdraw(node_factory, bitcoind): l1.rpc.withdraw(waddr, -amount) with pytest.raises(RpcError, match=r'Could not afford'): l1.rpc.withdraw(waddr, amount * 100) + l1.rpc.check_request_schemas = True out = l1.rpc.withdraw(waddr, 2 * amount) @@ -216,6 +220,9 @@ def test_minconf_withdraw(node_factory, bitcoind): bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10) + # This violates the request schema! + l1.rpc.check_request_schemas = False + with pytest.raises(RpcError): l1.rpc.withdraw(destination=addr, satoshi=10000, feerate='normal', minconf=9999999) @@ -1373,6 +1380,9 @@ def test_repro_4258(node_factory, bitcoind): addr = bitcoind.rpc.getnewaddress() + # These violate the request schema! + l1.rpc.check_request_schemas = False + # Missing array parentheses for outputs with pytest.raises(RpcError, match=r"Expected an array of outputs"): l1.rpc.txprepare( @@ -1391,6 +1401,8 @@ def test_repro_4258(node_factory, bitcoind): utxos="{txid}:{output}".format(**out) ) + l1.rpc.check_request_schemas = True + tx = l1.rpc.txprepare( outputs=[{addr: "all"}], feerate="slow", From 1f40db3594fcdb096522db05fec71b620efbb87e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0635/1530] msggen: Add parser for "oneOf" --- contrib/msggen/msggen/model.py | 60 +++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index 49215203af0c..a9cb63164b53 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -133,18 +133,21 @@ def from_js(cls, js, path): logger.warning(f"Unmanaged {fpath}, it is deprecated") continue - if "type" not in ftype: + if 'oneOf' in ftype: + field = UnionField.from_js(ftype, fpath) + + elif "type" not in ftype: logger.warning(f"Unmanaged {fpath}, it doesn't have a type") continue - # TODO Remove the `['string', 'null']` match once - # `listpeers.peers[].channels[].closer` no longer has this - # type - if ftype["type"] == ["string", "null"]: + elif ftype["type"] == ["string", "null"]: + # TODO Remove the `['string', 'null']` match once + # `listpeers.peers[].channels[].closer` no longer has this + # type ftype["type"] = "string" # Peek into the type so we know how to decode it - if ftype["type"] in ["string", ["string", "null"]] and "enum" in ftype: + elif ftype["type"] in ["string", ["string", "null"]] and "enum" in ftype: field = EnumField.from_js(ftype, fpath) elif ftype["type"] == "object": @@ -210,6 +213,39 @@ def __str__(self): values = ",".join([v for v in self.values if v is not None]) return f"Enum[path={self.path}, required={self.required}, values=[{values}]]" +class UnionField(Field): + """A type that can be one of a number of types. + + Corresponds to the `oneOf` type in JSON-Schema, an `enum` in Rust + and a `oneof` in protobuf. + + """ + def __init__(self, path, description, variants): + Field.__init__(self, path, description) + self.variants = variants + self.typename = path2type(path) + + @classmethod + def from_js(cls, js, path): + assert('oneOf' in js) + variants = [] + for child_js in js['oneOf']: + if child_js["type"] == "object": + itemtype = CompositeField.from_js(child_js, path) + + elif child_js["type"] == "string" and "enum" in child_js: + itemtype = EnumField.from_js(child_js, path) + + elif child_js["type"] in PrimitiveField.types: + itemtype = PrimitiveField( + child_js["type"], path, child_js.get("description", "") + ) + elif child_js["type"] == "array": + itemtype = ArrayField.from_js(path, child_js) + variants.append(itemtype) + + return UnionField(path, js.get('description', None), variants) + class PrimitiveField(Field): # Leaf types that we expect the binding languages to provide @@ -228,13 +264,15 @@ class PrimitiveField(Field): "msat|all", "hex", "short_channel_id", + "short_channel_id_dir", "txid", "integer", + "outpoint", "u16", "number", "feerate", "utxo", # A string representing the tuple (txid, outnum) - "OutputDesc", # A dict that maps an address to an amount (bitcoind style) + "outputdesc", # A dict that maps an address to an amount (bitcoind style) ] def __init__(self, typename, path, description): @@ -257,12 +295,16 @@ def from_js(cls, path, js): # Determine how nested we are dims = 1 child_js = js["items"] - while child_js["type"] == "array": + while child_js.get("type", None) == "array": dims += 1 child_js = child_js["items"] path += "[]" * dims - if child_js["type"] == "object": + if 'oneOf' in child_js: + assert('type' not in child_js) + itemtype = UnionField.from_js(child_js, path) + + elif child_js["type"] == "object": itemtype = CompositeField.from_js(child_js, path) elif child_js["type"] == "string" and "enum" in child_js: From ec5cd92580470a31bde59c9484772a9edf7f185e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0636/1530] msggen: Add model-side overrides Sometimes we just want to paper over the schema directly. Mostly useful to sidestep the `oneof` things that are required for expressiveness. --- cln-grpc/proto/primitives.proto | 2 +- cln-grpc/src/pb.rs | 14 +++++++------- cln-rpc/src/primitives.rs | 8 ++++---- contrib/msggen/msggen/grpc.py | 11 +++++++++-- contrib/msggen/msggen/model.py | 33 +++++++++++++++++++++++++-------- contrib/msggen/msggen/rust.py | 14 ++++++++------ 6 files changed, 54 insertions(+), 28 deletions(-) diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index c76730949e2c..5adc15f6894f 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -40,7 +40,7 @@ enum ChannelState { message ChannelStateChangeCause {} -message Utxo { +message Outpoint { bytes txid = 1; uint32 outnum = 2; } diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 910f09cafd5f..7fe8902ed115 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -2,7 +2,7 @@ tonic::include_proto!("cln"); use cln_rpc::primitives::{ Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, - Feerate as JFeerate, OutputDesc as JOutputDesc, Utxo as JUtxo, + Feerate as JFeerate, OutputDesc as JOutputDesc, Outpoint as JOutpoint, }; impl From for Amount { @@ -17,18 +17,18 @@ impl From<&Amount> for JAmount { } } -impl From for Utxo { - fn from(a: JUtxo) -> Self { - Utxo { +impl From for Outpoint { + fn from(a: JOutpoint) -> Self { + Outpoint { txid: a.txid, outnum: a.outnum, } } } -impl From<&Utxo> for JUtxo { - fn from(a: &Utxo) -> Self { - JUtxo { +impl From<&Outpoint> for JOutpoint { + fn from(a: &Outpoint) -> Self { + JOutpoint { txid: a.txid.clone(), outnum: a.outnum, } diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index b9a2349cc864..bdff5434b5eb 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -69,12 +69,12 @@ impl Amount { } #[derive(Clone, Debug, PartialEq)] -pub struct Utxo { +pub struct Outpoint { pub txid: Vec, pub outnum: u32, } -impl Serialize for Utxo { +impl Serialize for Outpoint { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -83,7 +83,7 @@ impl Serialize for Utxo { } } -impl<'de> Deserialize<'de> for Utxo { +impl<'de> Deserialize<'de> for Outpoint { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, @@ -102,7 +102,7 @@ impl<'de> Deserialize<'de> for Utxo { .parse() .map_err(|e| Error::custom(format!("{} is not a valid number: {}", s, e)))?; - Ok(Utxo { txid, outnum }) + Ok(Outpoint { txid, outnum }) } } diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 61084e7cf63a..ad790ee26802 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -24,8 +24,9 @@ 'u16': 'uint32', # Yeah, I know... 'f32': 'float', 'integer': 'sint64', - "utxo": "Utxo", + "outpoint": "Outpoint", "feerate": "Feerate", + "outputdesc": "OutputDesc", } @@ -41,6 +42,7 @@ 'ListTransactions.transactions[].type[]': None, } + method_name_overrides = { "Connect": "ConnectPeer", } @@ -373,7 +375,12 @@ def generate_composite(self, prefix, field: CompositeField) -> None: for f in field.fields: name = f.normalized() if isinstance(f, ArrayField): - self.write(f"{name}: c.{name}.iter().map(|s| s.into()).collect(),\n", numindent=3) + typ = f.itemtype.typename + mapping = { + 'hex': f'hex::decode(s).unwrap()', + 'u32': f's.clone()', + }.get(typ, f's.into()') + self.write(f"{name}: c.{name}.iter().map(|s| {mapping}).collect(),\n", numindent=3) elif isinstance(f, EnumField): if f.required: diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index a9cb63164b53..db643cf94ca5 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -1,5 +1,6 @@ from typing import List, Union, Optional import logging +from copy import copy logger = logging.getLogger(__name__) @@ -18,7 +19,7 @@ def normalized(self): "type": "item_type" }.get(self.name, self.name) - name = name.replace(' ', '_').replace('-', '_') + name = name.replace(' ', '_').replace('-', '_').replace('[]', '') return name def __str__(self): @@ -133,8 +134,12 @@ def from_js(cls, js, path): logger.warning(f"Unmanaged {fpath}, it is deprecated") continue - if 'oneOf' in ftype: - field = UnionField.from_js(ftype, fpath) + if fpath in overrides: + field = copy(overrides[fpath]) + field.path = fpath + field.description = desc + if isinstance(field, ArrayField): + field.itemtype.path = fpath elif "type" not in ftype: logger.warning(f"Unmanaged {fpath}, it doesn't have a type") @@ -320,11 +325,6 @@ def from_js(cls, path, js): itemtype, dims=dims, path=path, description=js.get("description", "") ) - def normalized(self): - # Strip the '[]' that we use to signal an array. The name - # itself doesn't need this. - return Field.normalized(self)[:-2] - class Command: def __init__(self, name, fields): @@ -336,6 +336,23 @@ def __str__(self): return f"Command[name={self.name}, fields=[{fieldnames}]]" +InvoiceLabelField = PrimitiveField("string", None, None) +DatastoreKeyField = ArrayField(itemtype=PrimitiveField("string", None, None), dims=1, path=None, description=None) +InvoiceExposeprivatechannelsField = PrimitiveField("boolean", None, None) +PayExclude = ArrayField(itemtype=PrimitiveField("string", None, None), dims=1, path=None, description=None) +# Override fields with manually managed types, fieldpath -> field mapping +overrides = { + 'Invoice.label': InvoiceLabelField, + 'DelInvoice.label': InvoiceLabelField, + 'ListInvoices.label': InvoiceLabelField, + 'Datastore.key': DatastoreKeyField, + 'DelDatastore.key': DatastoreKeyField, + 'ListDatastore.key': DatastoreKeyField, + 'Invoice.exposeprivatechannels': InvoiceExposeprivatechannelsField, + 'Pay.exclude': PayExclude, +} + + def parse_doc(command, js) -> Union[CompositeField, Command]: """Given a command name and its schema, generate the IR model""" path = command diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index 0de946b7f54f..e08a6febf4fb 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -25,6 +25,7 @@ 'ListPeers.peers[].channels[].features[]': "string", 'ListFunds.channels[].state': 'ChannelState', 'ListTransactions.transactions[].type[]': None, + 'Invoice.exposeprivatechannels': None, } # A map of schema type to rust primitive types. @@ -43,6 +44,8 @@ 'float': 'f32', 'utxo': 'Utxo', 'feerate': 'Feerate', + 'outpoint': 'Outpoint', + 'outputdesc': 'OutputDesc', } header = f"""#![allow(non_camel_case_types)] @@ -123,7 +126,7 @@ def gen_enum(e): if e.required: defi = f" // Path `{e.path}`\n #[serde(rename = \"{e.name}\")]\n pub {e.name.normalized()}: {typename},\n" else: - defi = f' #[serde(skip_serializing_if = "Option::is_none")]' + defi = f' #[serde(skip_serializing_if = "Option::is_none")]\n' defi = f" pub {e.name.normalized()}: Option<{typename}>,\n" return defi, decl @@ -148,17 +151,16 @@ def gen_array(a): logger.debug(f"Generating array field {a.name} -> {name} ({a.path})") _, decl = gen_field(a.itemtype) - if isinstance(a.itemtype, PrimitiveField): + if a.path in overrides: + decl = "" # No declaration if we have an override + itemtype = overrides[a.path] + elif isinstance(a.itemtype, PrimitiveField): itemtype = a.itemtype.typename elif isinstance(a.itemtype, CompositeField): itemtype = a.itemtype.typename elif isinstance(a.itemtype, EnumField): itemtype = a.itemtype.typename - if a.path in overrides: - decl = "" # No declaration if we have an override - itemtype = overrides[a.path] - if itemtype is None: return ("", "") # Override said not to include From ef145c7900b703c0235a23ce4a6ad1b3595efabb Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0637/1530] msggen: Add RoutehintList as a primitive --- cln-grpc/proto/primitives.proto | 14 ++++++++++++++ cln-grpc/src/pb.rs | 27 ++++++++++++++++++++++++++- cln-rpc/src/primitives.rs | 21 ++++++++++++++++++++- contrib/msggen/msggen/grpc.py | 3 ++- contrib/msggen/msggen/model.py | 2 ++ contrib/msggen/msggen/rust.py | 2 +- 6 files changed, 65 insertions(+), 4 deletions(-) diff --git a/cln-grpc/proto/primitives.proto b/cln-grpc/proto/primitives.proto index 5adc15f6894f..2dd7409909a8 100644 --- a/cln-grpc/proto/primitives.proto +++ b/cln-grpc/proto/primitives.proto @@ -58,4 +58,18 @@ message Feerate { message OutputDesc { string address = 1; Amount amount = 2; +} + +message RouteHop { + bytes id = 1; + string short_channel_id = 2; + Amount feebase = 3; + uint32 feeprop = 4; + uint32 expirydelta = 5; +} +message Routehint { + repeated RouteHop hops = 1; +} +message RoutehintList { + repeated Routehint hints = 2; } \ No newline at end of file diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 7fe8902ed115..9fe021afec4c 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -2,7 +2,7 @@ tonic::include_proto!("cln"); use cln_rpc::primitives::{ Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, - Feerate as JFeerate, OutputDesc as JOutputDesc, Outpoint as JOutpoint, + Feerate as JFeerate, Outpoint as JOutpoint, OutputDesc as JOutputDesc, }; impl From for Amount { @@ -101,3 +101,28 @@ impl From<&AmountOrAny> for JAmountOrAny { } } } +impl From for cln_rpc::primitives::Routehop { + fn from(c: RouteHop) -> Self { + Self { + id: hex::encode(c.id), + scid: c.short_channel_id, + feebase: c.feebase.as_ref().unwrap().into(), + feeprop: c.feeprop, + expirydelta: c.expirydelta as u16, + } + } +} +impl From for cln_rpc::primitives::Routehint { + fn from(c: Routehint) -> Self { + Self { + hops: c.hops.into_iter().map(|h| h.into()).collect(), + } + } +} +impl From for cln_rpc::primitives::RoutehintList { + fn from(c: RoutehintList) -> Self { + Self { + hints: c.hints.into_iter().map(|h| h.into()).collect(), + } + } +} diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index bdff5434b5eb..dd63b006a9dc 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -284,7 +284,7 @@ impl Serialize for Feerate { where S: Serializer, { - let s: String = self.into(); + let s: String = self.into(); serializer.serialize_str(&s) } } @@ -425,3 +425,22 @@ impl Serialize for OutputDesc { map.end() } } + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Routehop { + pub id: String, + pub scid: String, + pub feebase: Amount, + pub feeprop: u32, + pub expirydelta: u16, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Routehint { + pub hops: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RoutehintList { + pub hints: Vec, +} diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index ad790ee26802..f7e70f5c536e 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -377,7 +377,7 @@ def generate_composite(self, prefix, field: CompositeField) -> None: if isinstance(f, ArrayField): typ = f.itemtype.typename mapping = { - 'hex': f'hex::decode(s).unwrap()', + 'hex': f'hex::encode(s)', 'u32': f's.clone()', }.get(typ, f's.into()') self.write(f"{name}: c.{name}.iter().map(|s| {mapping}).collect(),\n", numindent=3) @@ -410,6 +410,7 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'msat|any?': f'c.{name}.as_ref().map(|a| a.into())', 'feerate': f'c.{name}.as_ref().unwrap().into()', 'feerate?': f'c.{name}.as_ref().map(|a| a.into())', + 'RoutehintList?': f'c.{name}.clone().map(|rl| rl.into())', }.get( typ, f'c.{name}.clone()' # default to just assignment diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index db643cf94ca5..7a7c205ff133 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -340,6 +340,7 @@ def __str__(self): DatastoreKeyField = ArrayField(itemtype=PrimitiveField("string", None, None), dims=1, path=None, description=None) InvoiceExposeprivatechannelsField = PrimitiveField("boolean", None, None) PayExclude = ArrayField(itemtype=PrimitiveField("string", None, None), dims=1, path=None, description=None) +RoutehintListField = PrimitiveField("RoutehintList", None, None) # Override fields with manually managed types, fieldpath -> field mapping overrides = { 'Invoice.label': InvoiceLabelField, @@ -350,6 +351,7 @@ def __str__(self): 'ListDatastore.key': DatastoreKeyField, 'Invoice.exposeprivatechannels': InvoiceExposeprivatechannelsField, 'Pay.exclude': PayExclude, + 'KeySend.routehints': RoutehintListField, } diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index e08a6febf4fb..c5a5c7d4c4ac 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -165,7 +165,7 @@ def gen_array(a): return ("", "") # Override said not to include itemtype = typemap.get(itemtype, itemtype) - alias = a.name.normalized()[:-2] # Strip the `[]` suffix for arrays. + alias = a.name.normalized() defi = f" #[serde(alias = \"{alias}\")]\n pub {name}: {'Vec<'*a.dims}{itemtype}{'>'*a.dims},\n" return (defi, decl) From 1613c44b0a380a12b2167916d0e6071a344ef200 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0638/1530] cln-rpc: Make Pubkey and ShortChannelId proper types --- cln-grpc/src/lib.rs | 3 + cln-grpc/src/pb.rs | 5 +- cln-rpc/src/lib.rs | 3 +- cln-rpc/src/primitives.rs | 120 ++++++++++++++++++++++++++++++++- contrib/msggen/msggen/grpc.py | 36 ++++++---- contrib/msggen/msggen/model.py | 4 +- contrib/msggen/msggen/rust.py | 13 ++-- 7 files changed, 159 insertions(+), 25 deletions(-) diff --git a/cln-grpc/src/lib.rs b/cln-grpc/src/lib.rs index b25b91261242..5e6b34dcf006 100644 --- a/cln-grpc/src/lib.rs +++ b/cln-grpc/src/lib.rs @@ -1,3 +1,6 @@ +// Huge json!() macros require lots of recursion +#![recursion_limit = "1024"] + mod convert; pub mod pb; mod server; diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 9fe021afec4c..e37118614931 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -1,4 +1,5 @@ tonic::include_proto!("cln"); +use std::str::FromStr; use cln_rpc::primitives::{ Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, @@ -104,8 +105,8 @@ impl From<&AmountOrAny> for JAmountOrAny { impl From for cln_rpc::primitives::Routehop { fn from(c: RouteHop) -> Self { Self { - id: hex::encode(c.id), - scid: c.short_channel_id, + id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), + scid: cln_rpc::primitives::ShortChannelId::from_str(&c.short_channel_id).unwrap(), feebase: c.feebase.as_ref().unwrap().into(), feeprop: c.feeprop, expirydelta: c.expirydelta as u16, diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs index c57bf2432a21..5cf2cba55ff7 100644 --- a/cln-rpc/src/lib.rs +++ b/cln-rpc/src/lib.rs @@ -1,6 +1,7 @@ use crate::codec::JsonCodec; use crate::codec::JsonRpc; -use anyhow::{Context, Error, Result}; +use anyhow::{Context, Result}; +pub use anyhow::Error; use futures_util::sink::SinkExt; use futures_util::StreamExt; use log::{debug, trace}; diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index dd63b006a9dc..2cbb04aa46f4 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -1,6 +1,10 @@ +use anyhow::Context; use anyhow::{anyhow, Error, Result}; use serde::{Deserialize, Serialize}; use serde::{Deserializer, Serializer}; +use std::str::FromStr; +use std::string::ToString; + #[derive(Copy, Clone, Serialize, Deserialize, Debug)] #[allow(non_camel_case_types)] pub enum ChannelState { @@ -68,6 +72,118 @@ impl Amount { } } +#[derive(Clone, Debug)] +pub struct Pubkey([u8; 33]); + +impl Serialize for Pubkey { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex::encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for Pubkey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let s: String = Deserialize::deserialize(deserializer)?; + Ok(Self::from_str(&s).map_err(|e| Error::custom(e.to_string()))?) + } +} + +impl FromStr for Pubkey { + type Err = crate::Error; + fn from_str(s: &str) -> Result { + let raw = + hex::decode(&s).with_context(|| format!("{} is not a valid hex-encoded pubkey", s))?; + + Ok(Pubkey(raw.try_into().map_err(|_| { + anyhow!("could not convert {} into pubkey", s) + })?)) + } +} +impl ToString for Pubkey { + fn to_string(&self) -> String { + hex::encode(self.0) + } +} +impl Pubkey { + pub fn from_slice(data: &[u8]) -> Result { + Ok(Pubkey( + data.try_into().with_context(|| "Not a valid pubkey")?, + )) + } + + pub fn to_vec(&self) -> Vec { + self.0.to_vec() + } +} + +#[derive(Clone, Debug)] +pub struct ShortChannelId(u64); + +impl Serialize for ShortChannelId { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for ShortChannelId { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let s: String = Deserialize::deserialize(deserializer)?; + Ok(Self::from_str(&s).map_err(|e| Error::custom(e.to_string()))?) + } +} + +impl FromStr for ShortChannelId { + type Err = crate::Error; + fn from_str(s: &str) -> Result { + let parts: Result, _> = s.split('x').map(|p| p.parse()).collect(); + let parts = parts.with_context(|| format!("Malformed short_channel_id: {}", s))?; + if parts.len() != 3 { + return Err(anyhow!( + "Malformed short_channel_id: element count mismatch" + )); + } + + Ok(ShortChannelId( + (parts[0] << 40) | (parts[1] << 16) | (parts[2] << 0), + )) + } +} +impl ToString for ShortChannelId { + fn to_string(&self) -> String { + format!("{}x{}x{}", self.block(), self.txindex(), self.outnum()) + } +} +impl ShortChannelId { + pub fn block(&self) -> u32 { + (self.0 >> 40) as u32 & 0xFFFFFF + } + pub fn txindex(&self) -> u32 { + (self.0 >> 16) as u32 & 0xFFFFFF + } + pub fn outnum(&self) -> u16 { + self.0 as u16 & 0xFFFF + } +} + +pub type Secret = [u8; 32]; +pub type Txid = [u8; 32]; +pub type Hash = [u8; 32]; +pub type NodeId = Pubkey; + #[derive(Clone, Debug, PartialEq)] pub struct Outpoint { pub txid: Vec, @@ -428,8 +544,8 @@ impl Serialize for OutputDesc { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Routehop { - pub id: String, - pub scid: String, + pub id: Pubkey, + pub scid: ShortChannelId, pub feebase: Amount, pub feeprop: u32, pub expirydelta: u16, diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index f7e70f5c536e..735b92ac3eeb 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -10,8 +10,8 @@ 'boolean': 'bool', 'hex': 'bytes', 'msat': 'Amount', - 'msat|all': 'AmountOrAll', - 'msat|any': 'AmountOrAny', + 'msat_or_all': 'AmountOrAll', + 'msat_or_any': 'AmountOrAny', 'number': 'sint64', 'pubkey': 'bytes', 'short_channel_id': 'string', @@ -275,8 +275,10 @@ def generate_composite(self, prefix, field: CompositeField): 'hex': f'hex::decode(i).unwrap()', }.get(typ, f'i.into()') - self.write(f"{name}: c.{name}.iter().map(|i| {mapping}).collect(),\n", numindent=3) - + if f.required: + self.write(f"{name}: c.{name}.iter().map(|i| {mapping}).collect(), // Rule #3 \n", numindent=3) + else: + self.write(f"{name}: c.{name}.as_ref().map(|arr| arr.iter().map(|i| {mapping}).collect()).unwrap_or(vec![]), // Rule #3 \n", numindent=3) elif isinstance(f, EnumField): if f.required: self.write(f"{name}: c.{name} as i32,\n", numindent=3) @@ -295,12 +297,14 @@ def generate_composite(self, prefix, field: CompositeField): 'u16?': f'c.{name}.map(|v| v.into())', 'msat': f'Some(c.{name}.into())', 'msat?': f'c.{name}.map(|f| f.into())', - 'pubkey': f'hex::decode(&c.{name}).unwrap()', - 'pubkey?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', + 'pubkey': f'c.{name}.to_vec()', + 'pubkey?': f'c.{name}.as_ref().map(|v| v.to_vec())', 'hex': f'hex::decode(&c.{name}).unwrap()', 'hex?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', 'txid': f'hex::decode(&c.{name}).unwrap()', 'txid?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', + 'short_channel_id': f'c.{name}.to_string()', + 'short_channel_id?': f'c.{name}.as_ref().map(|v| v.to_string())', }.get( typ, f'c.{name}.clone()' # default to just assignment @@ -335,6 +339,7 @@ def generate(self, service: Service) -> None: #[allow(unused_imports)] use cln_rpc::model::{responses,requests}; use crate::pb; + use std::str::FromStr; """) @@ -380,7 +385,10 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'hex': f'hex::encode(s)', 'u32': f's.clone()', }.get(typ, f's.into()') - self.write(f"{name}: c.{name}.iter().map(|s| {mapping}).collect(),\n", numindent=3) + if f.required: + self.write(f"{name}: c.{name}.iter().map(|s| {mapping}).collect(), // Rule #4\n", numindent=3) + else: + self.write(f"{name}: Some(c.{name}.iter().map(|s| {mapping}).collect()), // Rule #4\n", numindent=3) elif isinstance(f, EnumField): if f.required: @@ -400,17 +408,19 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'hex': f'hex::encode(&c.{name})', 'hex?': f'c.{name}.clone().map(|v| hex::encode(v))', 'txid?': f'c.{name}.clone().map(|v| hex::encode(v))', - 'pubkey': f'hex::encode(&c.{name})', - 'pubkey?': f'c.{name}.clone().map(|v| hex::encode(v))', + 'pubkey': f'cln_rpc::primitives::Pubkey::from_slice(&c.{name}).unwrap()', + 'pubkey?': f'c.{name}.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap())', 'msat': f'c.{name}.as_ref().unwrap().into()', 'msat?': f'c.{name}.as_ref().map(|a| a.into())', - 'msat|all': f'c.{name}.as_ref().unwrap().into()', - 'msat|all?': f'c.{name}.as_ref().map(|a| a.into())', - 'msat|any': f'c.{name}.as_ref().unwrap().into()', - 'msat|any?': f'c.{name}.as_ref().map(|a| a.into())', + 'msat_or_all': f'c.{name}.as_ref().unwrap().into()', + 'msat_or_all?': f'c.{name}.as_ref().map(|a| a.into())', + 'msat_or_any': f'c.{name}.as_ref().unwrap().into()', + 'msat_or_any?': f'c.{name}.as_ref().map(|a| a.into())', 'feerate': f'c.{name}.as_ref().unwrap().into()', 'feerate?': f'c.{name}.as_ref().map(|a| a.into())', 'RoutehintList?': f'c.{name}.clone().map(|rl| rl.into())', + 'short_channel_id': f'cln_rpc::primitives::ShortChannelId::from_str(&c.{name}).unwrap()', + 'short_channel_id?': f'c.{name}.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap())', }.get( typ, f'c.{name}.clone()' # default to just assignment diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index 7a7c205ff133..d26ea5eee5ff 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -265,8 +265,8 @@ class PrimitiveField(Field): "pubkey", "signature", "msat", - "msat|any", - "msat|all", + "msat_or_any", + "msat_or_all", "hex", "short_channel_id", "short_channel_id_dir", diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index c5a5c7d4c4ac..ea94ee17e821 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -33,11 +33,11 @@ 'boolean': 'bool', 'hex': 'String', 'msat': 'Amount', - 'msat|all': 'AmountOrAll', - 'msat|any': 'AmountOrAny', + 'msat_or_all': 'AmountOrAll', + 'msat_or_any': 'AmountOrAny', 'number': 'i64', - 'pubkey': 'String', - 'short_channel_id': 'String', + 'pubkey': 'Pubkey', + 'short_channel_id': 'ShortChannelId', 'signature': 'String', 'string': 'String', 'txid': 'String', @@ -166,7 +166,10 @@ def gen_array(a): itemtype = typemap.get(itemtype, itemtype) alias = a.name.normalized() - defi = f" #[serde(alias = \"{alias}\")]\n pub {name}: {'Vec<'*a.dims}{itemtype}{'>'*a.dims},\n" + if a.required: + defi = f" #[serde(alias = \"{alias}\")]\n pub {name}: {'Vec<'*a.dims}{itemtype}{'>'*a.dims},\n" + else: + defi = f" #[serde(alias = \"{alias}\", skip_serializing_if = \"Option::is_none\")]\n pub {name}: Option<{'Vec<'*a.dims}{itemtype}{'>'*a.dims}>,\n" return (defi, decl) From ecda4f717f51edc1b9ba9aa6707e15ff697ecedd Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0639/1530] cln-rpc: Move tests into separate file These json structs are gigantic, so let's externalize them a bit. --- cln-grpc/src/lib.rs | 3 + cln-grpc/src/test.rs | 300 ++++++++++++++++++++++++++++++++++ cln-rpc/src/primitives.rs | 2 + contrib/msggen/msggen/rust.py | 7 +- 4 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 cln-grpc/src/test.rs diff --git a/cln-grpc/src/lib.rs b/cln-grpc/src/lib.rs index 5e6b34dcf006..f9e2fec5423d 100644 --- a/cln-grpc/src/lib.rs +++ b/cln-grpc/src/lib.rs @@ -6,3 +6,6 @@ pub mod pb; mod server; pub use crate::server::Server; + +#[cfg(test)] +mod test; diff --git a/cln-grpc/src/test.rs b/cln-grpc/src/test.rs new file mode 100644 index 000000000000..4ed4d71b919b --- /dev/null +++ b/cln-grpc/src/test.rs @@ -0,0 +1,300 @@ +use crate::pb::*; +use serde_json::json; + +#[test] +fn test_listpeers() { + let j: serde_json::Value = json!({ + "peers": [ + { + "id": "0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", + "connected": true, + "netaddr": [ + "127.0.0.1:39152" + ], + "features": "8808226aa2", + "channels": [ + { + "state": "CHANNELD_NORMAL", + "scratch_txid": "fd4659658d235c20c81f96f7bc867c17abbfd20fcdd46c27eaad74ea52eaee90", + "last_tx_fee_msat": "14257000msat", + "feerate": { + "perkw": 11000, + "perkb": 44000 + }, + "owner": "channeld", + "short_channel_id": "103x2x1", + "direction": 0, + "channel_id": "44b77a6d66ca54f0c365c84b13a95fbde462415a0549228baa25ee1bb1dfef66", + "funding_txid": "67efdfb11bee25aa8b2249055a4162e4bd5fa9134bc865c3f054ca666d7ab744", + "funding_outnum": 1, + "close_to_addr": "bcrt1q9tc6q49l6wrrtp8ul45rj92hsleehwwxty32zu", + "close_to": "00142af1a054bfd3863584fcfd6839155787f39bb9c6", + "private": false, + "opener": "remote", + "features": [ + "option_static_remotekey", + "option_anchor_outputs" + ], + "funding": { + "local_msat": "0msat", + "remote_msat": "1000000000msat", + "pushed_msat": "0msat" + }, + "msatoshi_to_us": 0, + "to_us_msat": "0msat", + "msatoshi_to_us_min": 0, + "min_to_us_msat": "0msat", + "msatoshi_to_us_max": 0, + "max_to_us_msat": "0msat", + "msatoshi_total": 1000000000, + "total_msat": "1000000000msat", + "fee_base_msat": "1msat", + "fee_proportional_millionths": 10, + "dust_limit_satoshis": 546, + "dust_limit_msat": "546000msat", + "max_total_htlc_in_msat": "18446744073709551615msat", + "their_channel_reserve_satoshis": 10000, + "their_reserve_msat": "10000000msat", + "our_channel_reserve_satoshis": 10000, + "our_reserve_msat": "10000000msat", + "spendable_msatoshi": 0, + "spendable_msat": "0msat", + "receivable_msatoshi": 853257998, + "receivable_msat": "853257998msat", + "htlc_minimum_msat": 0, + "minimum_htlc_in_msat": "0msat", + "their_to_self_delay": 5, + "our_to_self_delay": 5, + "max_accepted_htlcs": 483, + "state_changes": [ + { + "timestamp": "2022-03-25T13:57:33.322Z", + "old_state": "CHANNELD_AWAITING_LOCKIN", + "new_state": "CHANNELD_NORMAL", + "cause": "remote", + "message": "Lockin complete" + } + ], + "status": [ + "CHANNELD_NORMAL:Funding transaction locked. Channel announced." + ], + "in_payments_offered": 1, + "in_msatoshi_offered": 100002002, + "in_offered_msat": "100002002msat", + "in_payments_fulfilled": 0, + "in_msatoshi_fulfilled": 0, + "in_fulfilled_msat": "0msat", + "out_payments_offered": 0, + "out_msatoshi_offered": 0, + "out_offered_msat": "0msat", + "out_payments_fulfilled": 0, + "out_msatoshi_fulfilled": 0, + "out_fulfilled_msat": "0msat", + "htlcs": [ + { + "direction": "in", + "id": 0, + "msatoshi": 100002002, + "amount_msat": "100002002msat", + "expiry": 131, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "RCVD_ADD_ACK_REVOCATION" + } + ] + } + ] + }, + { + "id": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", + "connected": true, + "netaddr": [ + "127.0.0.1:38321" + ], + "features": "8808226aa2", + "channels": [ + { + "state": "CHANNELD_NORMAL", + "scratch_txid": "30530d3f522862773100b7600d8ea8921a5ee84df17a2317326f9aa2c4829326", + "last_tx_fee_msat": "16149000msat", + "feerate": { + "perkw": 11000, + "perkb": 44000 + }, + "owner": "channeld", + "short_channel_id": "103x1x0", + "direction": 0, + "channel_id": "006a2044fc72fa5c4a54c9fddbf208970a7b3b4fd2aaa70a96abba757c01769e", + "funding_txid": "9e76017c75baab960aa7aad24f3b7b0a9708f2dbfdc9544a5cfa72fc44206a00", + "funding_outnum": 0, + "close_to_addr": "bcrt1qhfmyce4ujce2pyugew2435tlwft6p6w4s3py6d", + "close_to": "0014ba764c66bc9632a09388cb9558d17f7257a0e9d5", + "private": false, + "opener": "local", + "features": [ + "option_static_remotekey", + "option_anchor_outputs" + ], + "funding": { + "local_msat": "1000000000msat", + "remote_msat": "0msat", + "pushed_msat": "0msat" + }, + "msatoshi_to_us": 1000000000, + "to_us_msat": "1000000000msat", + "msatoshi_to_us_min": 1000000000, + "min_to_us_msat": "1000000000msat", + "msatoshi_to_us_max": 1000000000, + "max_to_us_msat": "1000000000msat", + "msatoshi_total": 1000000000, + "total_msat": "1000000000msat", + "fee_base_msat": "1msat", + "fee_proportional_millionths": 10, + "dust_limit_satoshis": 546, + "dust_limit_msat": "546000msat", + "max_total_htlc_in_msat": "18446744073709551615msat", + "their_channel_reserve_satoshis": 10000, + "their_reserve_msat": "10000000msat", + "our_channel_reserve_satoshis": 10000, + "our_reserve_msat": "10000000msat", + "spendable_msatoshi": 749473998, + "spendable_msat": "749473998msat", + "receivable_msatoshi": 0, + "receivable_msat": "0msat", + "htlc_minimum_msat": 0, + "minimum_htlc_in_msat": "0msat", + "their_to_self_delay": 5, + "our_to_self_delay": 5, + "max_accepted_htlcs": 483, + "state_changes": [ + { + "timestamp": "2022-03-25T13:57:33.325Z", + "old_state": "CHANNELD_AWAITING_LOCKIN", + "new_state": "CHANNELD_NORMAL", + "cause": "user", + "message": "Lockin complete" + } + ], + "status": [ + "CHANNELD_NORMAL:Funding transaction locked. Channel announced." + ], + "in_payments_offered": 0, + "in_msatoshi_offered": 0, + "in_offered_msat": "0msat", + "in_payments_fulfilled": 0, + "in_msatoshi_fulfilled": 0, + "in_fulfilled_msat": "0msat", + "out_payments_offered": 2, + "out_msatoshi_offered": 200002002, + "out_offered_msat": "200002002msat", + "out_payments_fulfilled": 0, + "out_msatoshi_fulfilled": 0, + "out_fulfilled_msat": "0msat", + "htlcs": [ + { + "direction": "out", + "id": 1, + "msatoshi": 100001001, + "amount_msat": "100001001msat", + "expiry": 125, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "SENT_ADD_ACK_REVOCATION" + }, + { + "direction": "out", + "id": 0, + "msatoshi": 100001001, + "amount_msat": "100001001msat", + "expiry": 124, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "SENT_ADD_ACK_REVOCATION" + } + ] + } + ] + } + ] + }); + let u: cln_rpc::model::ListpeersResponse = serde_json::from_value(j).unwrap(); + let _: ListpeersResponse = (&u).into(); +} + +#[test] +fn test_getinfo() { + let j = json!({ + "id": "0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", + "alias": "JUNIORBEAM-2-509-ged26651-modded", + "color": "0266e4", + "num_peers": 1, + "num_pending_channels": 0, + "num_active_channels": 1, + "num_inactive_channels": 0, + "address": [], + "binding": [{"type": "ipv4", "address": "127.0.0.1", "port": 34143}], + "version": "v0.10.2-509-ged26651-modded", + "blockheight": 103, + "network": "regtest", + "msatoshi_fees_collected": 0, + "fees_collected_msat": "0msat", "lightning-dir": "/tmp/ltests-20irp76f/test_pay_variants_1/lightning-1/regtest", + "our_features": {"init": "8808226aa2", "node": "80008808226aa2", "channel": "", "invoice": "024200"}}); + let u: cln_rpc::model::GetinfoResponse = serde_json::from_value(j).unwrap(); + let _g: GetinfoResponse = (&u).into(); +} + +#[test] +fn test_keysend() { + let g = + KeysendRequest { + destination: hex::decode( + "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", + ) + .unwrap(), + msatoshi: Some(Amount { msat: 10000 }), + label: Some("hello".to_string()), + exemptfee: None, + maxdelay: None, + retry_for: None, + maxfeepercent: None, + routehints: Some(RoutehintList { + hints: vec![Routehint { + hops: vec![RouteHop { + id: hex::decode( + "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", + ) + .unwrap(), + short_channel_id: "12345x678x90".to_string(), + feebase: Some(Amount { msat: 123 }), + feeprop: 1234, + expirydelta: 9, + },RouteHop { + id: hex::decode( + "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", + ) + .unwrap(), + short_channel_id: "12345x678x90".to_string(), + feebase: Some(Amount { msat: 123 }), + feeprop: 1234, + expirydelta: 9, + }], + }], + }), + }; + + let u: cln_rpc::model::KeysendRequest = (&g).into(); + let _ser = serde_json::to_string(&u); + + let j = r#"{ + "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", + "payment_hash": "e74b03a98453dcb5a7ed5406b97ec3566dde4be85ef71685110f4c0ebc600592", + "created_at": 1648222556.498, + "parts": 1, + "msatoshi": 10000, + "amount_msat": "10000msat", + "msatoshi_sent": 10001, + "amount_sent_msat": "10001msat", + "payment_preimage": "e56c22b9ed85560b021e1577daad5742502d25c0c2f636b817f5c0c7580a66a8", + "status": "complete" + }"#; + let u: cln_rpc::model::KeysendResponse = serde_json::from_str(j).unwrap(); + let g: KeysendResponse = (&u).into(); + println!("{:?}", g); +} diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 2cbb04aa46f4..155bb675fca4 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -23,6 +23,7 @@ pub enum ChannelState { #[derive(Copy, Clone, Serialize, Deserialize, Debug)] #[allow(non_camel_case_types)] +#[serde(rename_all = "lowercase")] pub enum ChannelStateChangeCause { UNKNOWN, LOCAL, @@ -223,6 +224,7 @@ impl<'de> Deserialize<'de> for Outpoint { } #[derive(Copy, Clone, Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "lowercase")] pub enum ChannelSide { LOCAL, REMOTE, diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index ea94ee17e821..d0b99d36169f 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -86,15 +86,18 @@ def gen_field(field): def gen_enum(e): defi, decl = "", "" + if e.path in overrides and overrides[e.path] is None: + return "", "" + if e.description != "": decl += f"/// {e.description}\n" - decl += f"#[derive(Copy, Clone, Debug, Deserialize, Serialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum {e.typename} {{\n" + decl += f"#[derive(Copy, Clone, Debug, Deserialize, Serialize)]\npub enum {e.typename} {{\n" for v in e.variants: if v is None: continue norm = v.normalized() - # decl += f" #[serde(rename = \"{v}\")]\n" + decl += f" #[serde(rename = \"{v}\")]\n" decl += f" {norm},\n" decl += "}\n\n" From d5f7548c8f0091d9ddd71a2c507d640c7e7ceac6 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0640/1530] cln-rpc: The JSON `number` type is a float not an int Got some issues parsing dates for example. --- contrib/msggen/msggen/grpc.py | 2 +- contrib/msggen/msggen/rust.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 735b92ac3eeb..9b86ac93147b 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -12,7 +12,7 @@ 'msat': 'Amount', 'msat_or_all': 'AmountOrAll', 'msat_or_any': 'AmountOrAny', - 'number': 'sint64', + 'number': 'double', 'pubkey': 'bytes', 'short_channel_id': 'string', 'signature': 'bytes', diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index d0b99d36169f..7803e2322622 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -35,7 +35,7 @@ 'msat': 'Amount', 'msat_or_all': 'AmountOrAll', 'msat_or_any': 'AmountOrAny', - 'number': 'i64', + 'number': 'f64', 'pubkey': 'Pubkey', 'short_channel_id': 'ShortChannelId', 'signature': 'String', From 940f5c350c19234a40b50bff9709cf03c4f525f2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0641/1530] cln-rpc: Skip the HTLC state for now It's schema definition is weirdly asymmetric, with variants dependent on another fields' value. Need to decide if we want to either hand-code a superset or make a more complex decoding, but definitely not something we'd want the generator to be able to do. --- contrib/msggen/msggen/grpc.py | 1 + contrib/msggen/msggen/rust.py | 1 + 2 files changed, 2 insertions(+) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 9b86ac93147b..10b29d11a22c 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -35,6 +35,7 @@ # Truncate the tree here, it's a complex structure with identitcal # types 'ListPeers.peers[].channels[].state_changes[]': None, + 'ListPeers.peers[].channels[].htlcs[].state': None, 'ListPeers.peers[].channels[].opener': "ChannelSide", 'ListPeers.peers[].channels[].closer': "ChannelSide", 'ListPeers.peers[].channels[].features[]': "string", diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index 7803e2322622..915bb95074e4 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -20,6 +20,7 @@ 'ListPeers.peers[].channels[].state_changes[].old_state': "ChannelState", 'ListPeers.peers[].channels[].state_changes[].new_state': "ChannelState", 'ListPeers.peers[].channels[].state_changes[].cause': "ChannelStateChangeCause", + 'ListPeers.peers[].channels[].htlcs[].state': None, 'ListPeers.peers[].channels[].opener': "ChannelSide", 'ListPeers.peers[].channels[].closer': "ChannelSide", 'ListPeers.peers[].channels[].features[]': "string", From cd9c00b6290212a722ae308819595ef38b114ac9 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0642/1530] cln-rpc: Log requests and responses --- cln-grpc/src/server.rs | 370 ++++++++++++++++++++-------------- contrib/msggen/msggen/grpc.py | 12 +- 2 files changed, 229 insertions(+), 153 deletions(-) diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 66e40d1391d6..5d695b305df2 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -4,7 +4,7 @@ use cln_rpc::{Request, Response, ClnRpc}; use anyhow::Result; use std::path::{Path, PathBuf}; use cln_rpc::model::requests; -use log::debug; +use log::{debug, trace}; use tonic::{Code, Status}; #[derive(Clone)] @@ -33,6 +33,7 @@ async fn getinfo( let req = request.into_inner(); let req: requests::GetinfoRequest = (&req).into(); debug!("Client asked for getinfo"); + trace!("getinfo request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -42,9 +43,10 @@ async fn getinfo( Code::Unknown, format!("Error calling method Getinfo: {:?}", e)))?; match result { - Response::Getinfo(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::Getinfo(r) => { + trace!("getinfo response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -62,7 +64,8 @@ async fn list_peers( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListpeersRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_peers"); + trace!("list_peers request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -72,9 +75,10 @@ async fn list_peers( Code::Unknown, format!("Error calling method ListPeers: {:?}", e)))?; match result { - Response::ListPeers(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListPeers(r) => { + trace!("list_peers response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -92,7 +96,8 @@ async fn list_funds( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListfundsRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_funds"); + trace!("list_funds request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -102,9 +107,10 @@ async fn list_funds( Code::Unknown, format!("Error calling method ListFunds: {:?}", e)))?; match result { - Response::ListFunds(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListFunds(r) => { + trace!("list_funds response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -122,7 +128,8 @@ async fn send_pay( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::SendpayRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for send_pay"); + trace!("send_pay request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -132,9 +139,10 @@ async fn send_pay( Code::Unknown, format!("Error calling method SendPay: {:?}", e)))?; match result { - Response::SendPay(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::SendPay(r) => { + trace!("send_pay response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -152,7 +160,8 @@ async fn list_channels( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListchannelsRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_channels"); + trace!("list_channels request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -162,9 +171,10 @@ async fn list_channels( Code::Unknown, format!("Error calling method ListChannels: {:?}", e)))?; match result { - Response::ListChannels(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListChannels(r) => { + trace!("list_channels response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -182,7 +192,8 @@ async fn add_gossip( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::AddgossipRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for add_gossip"); + trace!("add_gossip request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -192,9 +203,10 @@ async fn add_gossip( Code::Unknown, format!("Error calling method AddGossip: {:?}", e)))?; match result { - Response::AddGossip(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::AddGossip(r) => { + trace!("add_gossip response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -212,7 +224,8 @@ async fn auto_clean_invoice( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::AutocleaninvoiceRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for auto_clean_invoice"); + trace!("auto_clean_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -222,9 +235,10 @@ async fn auto_clean_invoice( Code::Unknown, format!("Error calling method AutoCleanInvoice: {:?}", e)))?; match result { - Response::AutoCleanInvoice(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::AutoCleanInvoice(r) => { + trace!("auto_clean_invoice response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -242,7 +256,8 @@ async fn check_message( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::CheckmessageRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for check_message"); + trace!("check_message request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -252,9 +267,10 @@ async fn check_message( Code::Unknown, format!("Error calling method CheckMessage: {:?}", e)))?; match result { - Response::CheckMessage(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::CheckMessage(r) => { + trace!("check_message response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -272,7 +288,8 @@ async fn close( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::CloseRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for close"); + trace!("close request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -282,9 +299,10 @@ async fn close( Code::Unknown, format!("Error calling method Close: {:?}", e)))?; match result { - Response::Close(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::Close(r) => { + trace!("close response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -302,7 +320,8 @@ async fn connect_peer( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ConnectRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for connect_peer"); + trace!("connect_peer request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -312,9 +331,10 @@ async fn connect_peer( Code::Unknown, format!("Error calling method ConnectPeer: {:?}", e)))?; match result { - Response::ConnectPeer(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ConnectPeer(r) => { + trace!("connect_peer response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -332,7 +352,8 @@ async fn create_invoice( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::CreateinvoiceRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for create_invoice"); + trace!("create_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -342,9 +363,10 @@ async fn create_invoice( Code::Unknown, format!("Error calling method CreateInvoice: {:?}", e)))?; match result { - Response::CreateInvoice(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::CreateInvoice(r) => { + trace!("create_invoice response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -362,7 +384,8 @@ async fn datastore( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::DatastoreRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for datastore"); + trace!("datastore request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -372,9 +395,10 @@ async fn datastore( Code::Unknown, format!("Error calling method Datastore: {:?}", e)))?; match result { - Response::Datastore(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::Datastore(r) => { + trace!("datastore response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -392,7 +416,8 @@ async fn create_onion( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::CreateonionRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for create_onion"); + trace!("create_onion request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -402,9 +427,10 @@ async fn create_onion( Code::Unknown, format!("Error calling method CreateOnion: {:?}", e)))?; match result { - Response::CreateOnion(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::CreateOnion(r) => { + trace!("create_onion response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -422,7 +448,8 @@ async fn del_datastore( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::DeldatastoreRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for del_datastore"); + trace!("del_datastore request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -432,9 +459,10 @@ async fn del_datastore( Code::Unknown, format!("Error calling method DelDatastore: {:?}", e)))?; match result { - Response::DelDatastore(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::DelDatastore(r) => { + trace!("del_datastore response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -452,7 +480,8 @@ async fn del_expired_invoice( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::DelexpiredinvoiceRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for del_expired_invoice"); + trace!("del_expired_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -462,9 +491,10 @@ async fn del_expired_invoice( Code::Unknown, format!("Error calling method DelExpiredInvoice: {:?}", e)))?; match result { - Response::DelExpiredInvoice(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::DelExpiredInvoice(r) => { + trace!("del_expired_invoice response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -482,7 +512,8 @@ async fn del_invoice( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::DelinvoiceRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for del_invoice"); + trace!("del_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -492,9 +523,10 @@ async fn del_invoice( Code::Unknown, format!("Error calling method DelInvoice: {:?}", e)))?; match result { - Response::DelInvoice(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::DelInvoice(r) => { + trace!("del_invoice response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -512,7 +544,8 @@ async fn invoice( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::InvoiceRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for invoice"); + trace!("invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -522,9 +555,10 @@ async fn invoice( Code::Unknown, format!("Error calling method Invoice: {:?}", e)))?; match result { - Response::Invoice(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::Invoice(r) => { + trace!("invoice response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -542,7 +576,8 @@ async fn list_datastore( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListdatastoreRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_datastore"); + trace!("list_datastore request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -552,9 +587,10 @@ async fn list_datastore( Code::Unknown, format!("Error calling method ListDatastore: {:?}", e)))?; match result { - Response::ListDatastore(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListDatastore(r) => { + trace!("list_datastore response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -572,7 +608,8 @@ async fn list_invoices( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListinvoicesRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_invoices"); + trace!("list_invoices request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -582,9 +619,10 @@ async fn list_invoices( Code::Unknown, format!("Error calling method ListInvoices: {:?}", e)))?; match result { - Response::ListInvoices(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListInvoices(r) => { + trace!("list_invoices response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -602,7 +640,8 @@ async fn send_onion( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::SendonionRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for send_onion"); + trace!("send_onion request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -612,9 +651,10 @@ async fn send_onion( Code::Unknown, format!("Error calling method SendOnion: {:?}", e)))?; match result { - Response::SendOnion(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::SendOnion(r) => { + trace!("send_onion response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -632,7 +672,8 @@ async fn list_send_pays( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListsendpaysRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_send_pays"); + trace!("list_send_pays request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -642,9 +683,10 @@ async fn list_send_pays( Code::Unknown, format!("Error calling method ListSendPays: {:?}", e)))?; match result { - Response::ListSendPays(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListSendPays(r) => { + trace!("list_send_pays response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -662,7 +704,8 @@ async fn list_transactions( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListtransactionsRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_transactions"); + trace!("list_transactions request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -672,9 +715,10 @@ async fn list_transactions( Code::Unknown, format!("Error calling method ListTransactions: {:?}", e)))?; match result { - Response::ListTransactions(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListTransactions(r) => { + trace!("list_transactions response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -692,7 +736,8 @@ async fn pay( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::PayRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for pay"); + trace!("pay request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -702,9 +747,10 @@ async fn pay( Code::Unknown, format!("Error calling method Pay: {:?}", e)))?; match result { - Response::Pay(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::Pay(r) => { + trace!("pay response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -722,7 +768,8 @@ async fn list_nodes( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::ListnodesRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for list_nodes"); + trace!("list_nodes request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -732,9 +779,10 @@ async fn list_nodes( Code::Unknown, format!("Error calling method ListNodes: {:?}", e)))?; match result { - Response::ListNodes(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::ListNodes(r) => { + trace!("list_nodes response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -752,7 +800,8 @@ async fn wait_any_invoice( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::WaitanyinvoiceRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for wait_any_invoice"); + trace!("wait_any_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -762,9 +811,10 @@ async fn wait_any_invoice( Code::Unknown, format!("Error calling method WaitAnyInvoice: {:?}", e)))?; match result { - Response::WaitAnyInvoice(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::WaitAnyInvoice(r) => { + trace!("wait_any_invoice response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -782,7 +832,8 @@ async fn wait_invoice( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::WaitinvoiceRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for wait_invoice"); + trace!("wait_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -792,9 +843,10 @@ async fn wait_invoice( Code::Unknown, format!("Error calling method WaitInvoice: {:?}", e)))?; match result { - Response::WaitInvoice(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::WaitInvoice(r) => { + trace!("wait_invoice response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -812,7 +864,8 @@ async fn wait_send_pay( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::WaitsendpayRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for wait_send_pay"); + trace!("wait_send_pay request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -822,9 +875,10 @@ async fn wait_send_pay( Code::Unknown, format!("Error calling method WaitSendPay: {:?}", e)))?; match result { - Response::WaitSendPay(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::WaitSendPay(r) => { + trace!("wait_send_pay response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -842,7 +896,8 @@ async fn new_addr( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::NewaddrRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for new_addr"); + trace!("new_addr request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -852,9 +907,10 @@ async fn new_addr( Code::Unknown, format!("Error calling method NewAddr: {:?}", e)))?; match result { - Response::NewAddr(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::NewAddr(r) => { + trace!("new_addr response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -872,7 +928,8 @@ async fn withdraw( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::WithdrawRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for withdraw"); + trace!("withdraw request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -882,9 +939,10 @@ async fn withdraw( Code::Unknown, format!("Error calling method Withdraw: {:?}", e)))?; match result { - Response::Withdraw(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::Withdraw(r) => { + trace!("withdraw response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -902,7 +960,8 @@ async fn key_send( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::KeysendRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for key_send"); + trace!("key_send request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -912,9 +971,10 @@ async fn key_send( Code::Unknown, format!("Error calling method KeySend: {:?}", e)))?; match result { - Response::KeySend(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::KeySend(r) => { + trace!("key_send response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -932,7 +992,8 @@ async fn fund_psbt( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::FundpsbtRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for fund_psbt"); + trace!("fund_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -942,9 +1003,10 @@ async fn fund_psbt( Code::Unknown, format!("Error calling method FundPsbt: {:?}", e)))?; match result { - Response::FundPsbt(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::FundPsbt(r) => { + trace!("fund_psbt response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -962,7 +1024,8 @@ async fn send_psbt( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::SendpsbtRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for send_psbt"); + trace!("send_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -972,9 +1035,10 @@ async fn send_psbt( Code::Unknown, format!("Error calling method SendPsbt: {:?}", e)))?; match result { - Response::SendPsbt(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::SendPsbt(r) => { + trace!("send_psbt response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -992,7 +1056,8 @@ async fn sign_psbt( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::SignpsbtRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for sign_psbt"); + trace!("sign_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -1002,9 +1067,10 @@ async fn sign_psbt( Code::Unknown, format!("Error calling method SignPsbt: {:?}", e)))?; match result { - Response::SignPsbt(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::SignPsbt(r) => { + trace!("sign_psbt response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -1022,7 +1088,8 @@ async fn utxo_psbt( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::UtxopsbtRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for utxo_psbt"); + trace!("utxo_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -1032,9 +1099,10 @@ async fn utxo_psbt( Code::Unknown, format!("Error calling method UtxoPsbt: {:?}", e)))?; match result { - Response::UtxoPsbt(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::UtxoPsbt(r) => { + trace!("utxo_psbt response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -1052,7 +1120,8 @@ async fn tx_discard( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::TxdiscardRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for tx_discard"); + trace!("tx_discard request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -1062,9 +1131,10 @@ async fn tx_discard( Code::Unknown, format!("Error calling method TxDiscard: {:?}", e)))?; match result { - Response::TxDiscard(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::TxDiscard(r) => { + trace!("tx_discard response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -1082,7 +1152,8 @@ async fn tx_prepare( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::TxprepareRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for tx_prepare"); + trace!("tx_prepare request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -1092,9 +1163,10 @@ async fn tx_prepare( Code::Unknown, format!("Error calling method TxPrepare: {:?}", e)))?; match result { - Response::TxPrepare(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::TxPrepare(r) => { + trace!("tx_prepare response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( @@ -1112,7 +1184,8 @@ async fn tx_send( ) -> Result, tonic::Status> { let req = request.into_inner(); let req: requests::TxsendRequest = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for tx_send"); + trace!("tx_send request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -1122,9 +1195,10 @@ async fn tx_send( Code::Unknown, format!("Error calling method TxSend: {:?}", e)))?; match result { - Response::TxSend(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::TxSend(r) => { + trace!("tx_send response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, r => Err(Status::new( Code::Internal, format!( diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index 10b29d11a22c..d3369570c74d 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -445,7 +445,7 @@ def generate(self, service: Service) -> None: use anyhow::Result; use std::path::{{Path, PathBuf}}; use cln_rpc::model::requests; - use log::debug; + use log::{{debug, trace}}; use tonic::{{Code, Status}}; #[derive(Clone)] @@ -480,7 +480,8 @@ def generate(self, service: Service) -> None: ) -> Result, tonic::Status> {{ let req = request.into_inner(); let req: requests::{method.request.typename} = (&req).into(); - debug!("Client asked for getinfo"); + debug!("Client asked for {name}"); + trace!("{name} request: {{:?}}", req); let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; @@ -490,9 +491,10 @@ def generate(self, service: Service) -> None: Code::Unknown, format!("Error calling method {method.name}: {{:?}}", e)))?; match result {{ - Response::{method.name}(r) => Ok( - tonic::Response::new((&r).into()) - ), + Response::{method.name}(r) => {{ + trace!("{name} response: {{:?}}", r); + Ok(tonic::Response::new((&r).into())) + }}, r => Err(Status::new( Code::Internal, format!( From d0f6e8c8a61d58ae836c6c2fe1196336013c315c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0643/1530] cln-rpc: Add Sha256 and Secret types --- cln-rpc/src/primitives.rs | 88 ++++++++++++++++++- contrib/msggen/msggen/grpc.py | 15 +++- contrib/msggen/msggen/model.py | 3 + contrib/msggen/msggen/rust.py | 2 + contrib/pyln-testing/pyln/testing/fixtures.py | 9 ++ 5 files changed, 112 insertions(+), 5 deletions(-) diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 155bb675fca4..0631e2b2f993 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -180,10 +180,90 @@ impl ShortChannelId { } } -pub type Secret = [u8; 32]; -pub type Txid = [u8; 32]; -pub type Hash = [u8; 32]; -pub type NodeId = Pubkey; +#[derive(Clone, Debug)] +pub struct Secret([u8; 32]); + +impl TryFrom> for Secret { + type Error = crate::Error; + fn try_from(v: Vec) -> Result { + if v.len() != 32 { + Err(anyhow!("Unexpected secret length: {}", hex::encode(v))) + } else { + Ok(Secret(v.try_into().unwrap())) + } + } +} + +impl Serialize for Secret { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex::encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for Secret { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let s: String = Deserialize::deserialize(deserializer)?; + let h = hex::decode(s).map_err(|_| Error::custom("not a valid hex string"))?; + Ok(Secret(h.try_into().map_err(|_| { + Error::custom("not a valid hex-encoded hash") + })?)) + } +} + +impl Secret { + pub fn to_vec(self) -> Vec { + self.0.to_vec() + } +} + +#[derive(Clone, Debug)] +pub struct Sha256([u8; 32]); +impl Sha256 { + pub fn to_vec(self) -> Vec { + self.0.to_vec() + } +} + +impl TryFrom> for Sha256 { + type Error = crate::Error; + fn try_from(v: Vec) -> Result { + if v.len() != 32 { + Err(anyhow!("Unexpected hash length: {}", hex::encode(v))) + } else { + Ok(Sha256(v.try_into().unwrap())) + } + } +} + +impl Serialize for Sha256 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex::encode(&self.0)) + } +} + +impl<'de> Deserialize<'de> for Sha256 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de::Error; + let s: String = Deserialize::deserialize(deserializer)?; + let h = hex::decode(s).map_err(|_| Error::custom("not a valid hex string"))?; + Ok(Sha256(h.try_into().map_err(|_| { + Error::custom("not a valid hex-encoded hash") + })?)) + } +} #[derive(Clone, Debug, PartialEq)] pub struct Outpoint { diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/grpc.py index d3369570c74d..56317f71e2ed 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/grpc.py @@ -27,6 +27,8 @@ "outpoint": "Outpoint", "feerate": "Feerate", "outputdesc": "OutputDesc", + "secret": "bytes", + "hash": "bytes", } @@ -274,10 +276,11 @@ def generate_composite(self, prefix, field: CompositeField): # array. The current item is called `i` mapping = { 'hex': f'hex::decode(i).unwrap()', + 'secret': f'i.clone().to_vec()', }.get(typ, f'i.into()') if f.required: - self.write(f"{name}: c.{name}.iter().map(|i| {mapping}).collect(), // Rule #3 \n", numindent=3) + self.write(f"{name}: c.{name}.iter().map(|i| {mapping}).collect(), // Rule #3 for type {typ} \n", numindent=3) else: self.write(f"{name}: c.{name}.as_ref().map(|arr| arr.iter().map(|i| {mapping}).collect()).unwrap_or(vec![]), // Rule #3 \n", numindent=3) elif isinstance(f, EnumField): @@ -306,6 +309,10 @@ def generate_composite(self, prefix, field: CompositeField): 'txid?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', 'short_channel_id': f'c.{name}.to_string()', 'short_channel_id?': f'c.{name}.as_ref().map(|v| v.to_string())', + 'hash': f'c.{name}.clone().to_vec()', + 'hash?': f'c.{name}.clone().map(|v| v.to_vec())', + 'secret': f'c.{name}.clone().to_vec()', + 'secret?': f'c.{name}.clone().map(|v| v.to_vec())', }.get( typ, f'c.{name}.clone()' # default to just assignment @@ -385,6 +392,7 @@ def generate_composite(self, prefix, field: CompositeField) -> None: mapping = { 'hex': f'hex::encode(s)', 'u32': f's.clone()', + 'secret': f's.clone().try_into().unwrap()' }.get(typ, f's.into()') if f.required: self.write(f"{name}: c.{name}.iter().map(|s| {mapping}).collect(), // Rule #4\n", numindent=3) @@ -422,6 +430,11 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'RoutehintList?': f'c.{name}.clone().map(|rl| rl.into())', 'short_channel_id': f'cln_rpc::primitives::ShortChannelId::from_str(&c.{name}).unwrap()', 'short_channel_id?': f'c.{name}.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap())', + 'secret': f'c.{name}.clone().try_into().unwrap()', + 'secret?': f'c.{name}.clone().map(|v| v.try_into().unwrap())', + 'hash': f'c.{name}.clone().try_into().unwrap()', + 'hash?': f'c.{name}.clone().map(|v| v.try_into().unwrap())', + 'txid': f'hex::encode(&c.{name})', }.get( typ, f'c.{name}.clone()' # default to just assignment diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index d26ea5eee5ff..4c8902beef7d 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -218,6 +218,7 @@ def __str__(self): values = ",".join([v for v in self.values if v is not None]) return f"Enum[path={self.path}, required={self.required}, values=[{values}]]" + class UnionField(Field): """A type that can be one of a number of types. @@ -278,6 +279,8 @@ class PrimitiveField(Field): "feerate", "utxo", # A string representing the tuple (txid, outnum) "outputdesc", # A dict that maps an address to an amount (bitcoind style) + "secret", + "hash", ] def __init__(self, typename, path, description): diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index 915bb95074e4..ebb005c091c1 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -47,6 +47,8 @@ 'feerate': 'Feerate', 'outpoint': 'Outpoint', 'outputdesc': 'OutputDesc', + 'hash': 'Sha256', + 'secret': 'Secret', } header = f"""#![allow(non_camel_case_types)] diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index ec6271bbb924..4cbb63ab0b63 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -316,6 +316,13 @@ def is_pubkey(checker, instance): return False return instance[0:2] == "02" or instance[0:2] == "03" + def is_32byte_hex(self, instance): + """Fixed size 32 byte hex string + + This matches a variety of hex types: secrets, hashes, txid + """ + return self.is_type(instance, "hex") and len(instance) == 64 + def is_point32(checker, instance): """x-only BIP-340 public key""" if not checker.is_type(instance, "hex"): @@ -389,6 +396,8 @@ def is_msat_or_any(checker, instance): is_msat = is_msat_response type_checker = jsonschema.Draft7Validator.TYPE_CHECKER.redefine_many({ "hex": is_hex, + "hash": is_32byte_hex, + "secret": is_32byte_hex, "u64": is_u64, "u32": is_u32, "u16": is_u16, From 4aec3d3b9c98df5f9248b2b4c3bbe129efb53d93 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0644/1530] doc: Annotate secrets and hashes in the JSON Schemas --- doc/lightning-createinvoice.7.md | 6 +++--- doc/lightning-createonion.7.md | 4 ++-- doc/lightning-delinvoice.7.md | 6 +++--- doc/lightning-invoice.7.md | 6 +++--- doc/lightning-keysend.7.md | 6 +++--- doc/lightning-listinvoices.7.md | 6 +++--- doc/lightning-listpeers.7.md | 6 +++--- doc/lightning-listsendpays.7.md | 6 +++--- doc/lightning-pay.7.md | 6 +++--- doc/lightning-sendonion.7.md | 6 +++--- doc/lightning-sendpay.7.md | 6 +++--- doc/lightning-waitanyinvoice.7.md | 6 +++--- doc/lightning-waitinvoice.7.md | 6 +++--- doc/lightning-waitsendpay.7.md | 6 +++--- doc/schemas/createinvoice.schema.json | 4 ++-- doc/schemas/createonion.request.json | 2 +- doc/schemas/createonion.schema.json | 2 +- doc/schemas/delinvoice.schema.json | 4 ++-- doc/schemas/invoice.schema.json | 4 ++-- doc/schemas/keysend.schema.json | 4 ++-- doc/schemas/listinvoices.schema.json | 4 ++-- doc/schemas/listpeers.schema.json | 4 ++-- doc/schemas/listsendpays.request.json | 2 +- doc/schemas/listsendpays.schema.json | 4 ++-- doc/schemas/pay.schema.json | 4 ++-- doc/schemas/sendonion.request.json | 4 ++-- doc/schemas/sendonion.schema.json | 4 ++-- doc/schemas/sendpay.request.json | 4 ++-- doc/schemas/sendpay.schema.json | 4 ++-- doc/schemas/txdiscard.request.json | 2 +- doc/schemas/txsend.request.json | 2 +- doc/schemas/waitanyinvoice.schema.json | 4 ++-- doc/schemas/waitinvoice.schema.json | 4 ++-- doc/schemas/waitsendpay.request.json | 2 +- doc/schemas/waitsendpay.schema.json | 4 ++-- 35 files changed, 77 insertions(+), 77 deletions(-) diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index 459c206c7b8f..ba27e08b6878 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -33,7 +33,7 @@ RETURN VALUE [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) +- **payment_hash** (hash): 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) @@ -43,7 +43,7 @@ On success, an object is returned, containing: - **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) +- **payment_preimage** (secret, 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) - **payer_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only). @@ -74,4 +74,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ea89ed849c8ad6cac8e1e136999046d1f7589bf176be0e65438174357f87ed11) +[comment]: # ( SHA256STAMP:b07a315a6460449b64ab848ce9fc4cfe6ac862d63c8ab922434238a2b83d7902) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index f401fe5cc596..63728c5b9c7c 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -92,7 +92,7 @@ RETURN VALUE [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: +- **shared_secrets** (array of secrets): one shared secret for each node in the *hops* parameter: - the shared secret with this hop (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -133,4 +133,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:23e999fedfde74a200c0e6626fa828548b3b60952de30c5885cd63b1922f4508) +[comment]: # ( SHA256STAMP:eabebca3c38ac8e01fb9a0891bde7913ce2832b7ee179c0b4617d104a0e6c009) diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index fe446756a6e0..0ee5773e2195 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -27,7 +27,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 -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **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 @@ -43,7 +43,7 @@ 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) + - **payment_preimage** (secret): SHA256 of this is the *payment_hash* offered in the invoice (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -78,4 +78,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:73d9097734e85d438de90844ab78bdd737e6df53620542887170c7564a86b90b) +[comment]: # ( SHA256STAMP:407d741b622621da127dc1e55c98c299ace970f8244bc14574dddcafbf9aa1fc) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index aab55ad72f31..a0da46b86389 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -78,8 +78,8 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: - **bolt11** (string): the bolt11 string -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) -- **payment_secret** (hex): the *payment_secret* to place in the onion (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_secret** (secret): the *payment_secret* to place in the onion (always 64 characters) - **expires_at** (u64): UNIX timestamp of when invoice expires The following warnings may also be returned: @@ -117,4 +117,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f7d82473482e5454fc03641fddcfa97984e6a597e7ad377ced2cbed1512e91ed) +[comment]: # ( SHA256STAMP:834b9d7b84845d423a677662b66b9109b3b8c4b7219a91d98f2817561d68a8cd) diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index f5c613202fe7..da48b95f89a9 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -67,8 +67,8 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **payment_preimage** (hex): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **created_at** (number): the UNIX timestamp showing when this payment was initiated - **parts** (u32): how many attempts this took - **amount_msat** (msat): Amount the recipient received @@ -114,4 +114,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bf507985544575c4ef2fe194fda6a693378cb8ab3bfb30ca7a7c066be271be29) +[comment]: # ( SHA256STAMP:449315de94f49452c3d485775dd97bc2517763be420ebc74024914680359d1ff) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index 63cfe33d1de0..0524f5904352 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -23,7 +23,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **invoices** is returned. It is an array of objects, where each object contains: - **label** (string): unique label supplied at invoice creation -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid, unpaid or unpayable (one of "unpaid", "paid", "expired") - **expires_at** (u64): UNIX timestamp of when it will become / became unpayable - **description** (string, optional): description used in the invoice @@ -37,7 +37,7 @@ If **status** is "paid": - **pay_index** (u64): Unique incrementing index for this payment - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - **paid_at** (u64): UNIX timestamp of when it was paid - - **payment_preimage** (hex): proof of payment (always 64 characters) + - **payment_preimage** (secret): proof of payment (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -56,4 +56,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d1328ecc2a4e76ede8c9adc3a63d18ce36be305ddcee7cf717039f79642cfd41) +[comment]: # ( SHA256STAMP:1178a5178a20b2aca9c52fe39e332bd1d6c4f20fa302639bed0fbfc6eb348c40) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 88905de2c3b5..849b808c1814 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -53,7 +53,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **perkb** (u32): Feerate per 1000 virtual bytes - **owner** (string, optional): The current subdaemon controlling this connection - **short_channel_id** (short_channel_id, optional): The short_channel_id (once locked in) - - **channel_id** (hex, optional): The full channel_id (always 64 characters) + - **channel_id** (hash, optional): The full channel_id (always 64 characters) - **funding_txid** (txid, optional): ID of the funding transaction - **funding_outnum** (u32, optional): The 0-based output number of the funding transaction which opens the channel - **initial_feerate** (string, optional): For inflight opens, the first feerate used to initiate the channel open @@ -113,7 +113,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **id** (u64): Unique ID for this htlc on this channel in this direction - **amount_msat** (msat): Amount send/received for this HTLC - **expiry** (u32): Block this HTLC expires at - - **payment_hash** (hex): the hash of the payment_preimage which will prove payment (always 64 characters) + - **payment_hash** (hash): the hash of the payment_preimage which will prove payment (always 64 characters) - **local_trimmed** (boolean, optional): if this is too small to enforce onchain (always *true*) - **status** (string, optional): set if this HTLC is currently waiting on a hook (and shows what plugin) @@ -380,4 +380,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:147b7008c8f4acb031df625e0731614339a75ee5861cb9f40cd542b1017e3660) +[comment]: # ( SHA256STAMP:6b0ec5c899c8685487190209f594635030205a275e1dc6d61a7b057adbf66192) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 9cd0034cbda2..13d82b8f45ae 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -25,7 +25,7 @@ Note that the returned array is ordered by increasing *id*. [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) +- **payment_hash** (hash): 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 - **amount_sent_msat** (msat): The amount sent @@ -37,7 +37,7 @@ On success, an object containing **payments** is returned. It is an array of ob - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "complete": - - **payment_preimage** (hex): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "failed": - **erroronion** (hex, optional): the onion message returned @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1dfcb495e0004b9dadffd7f69b58275bf9168c9f4007675b390ebbaea07ffde6) +[comment]: # ( SHA256STAMP:b03c2f306bafb1919f0933ebc695657bd691591484ddcb39b1e8706335593cd2) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index e8dc09d08a9c..f563dddbedd9 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -78,8 +78,8 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **payment_preimage** (hex): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **created_at** (number): the UNIX timestamp showing when this payment was initiated - **parts** (u32): how many attempts this took - **amount_msat** (msat): Amount the recipient received @@ -149,4 +149,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e537b9c74918559db99e673fac63cfa43a347ab80c32f33012ab3655b9edc45e) +[comment]: # ( SHA256STAMP:b8ff255c1f4d284535d70f13d3bd85c3d20ef8fdee081835a57fb04528311db6) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index c059afc95ea9..a4100a889be0 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -89,7 +89,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: - **id** (u64): unique ID for this payment attempt -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (could be complete if already sent previously) (one of "pending", "complete") - **created_at** (u64): the UNIX timestamp showing when this payment was initiated - **amount_sent_msat** (msat): The amount sent @@ -101,7 +101,7 @@ On success, an object is returned, containing: - **partid** (u64, optional): the partid (if supplied) to sendonion/sendpay If **status** is "complete": - - **payment_preimage** (hex): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "pending": - **message** (string, optional): Monitor status with listpays or waitsendpay @@ -128,4 +128,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:b8fd5d1c31eb2db10b2f32960752ae1c90150ffeb470c5ea635cb1034e8b1438) +[comment]: # ( SHA256STAMP:d2f4991d271147dd8c13859376f36f2d8843a96034e818a434555a09bf6fd003) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index ec7aec712a12..6ecb1806255e 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -67,7 +67,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: - **id** (u64): unique ID for this payment attempt -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (could be complete if already sent previously) (one of "pending", "complete") - **created_at** (u64): the UNIX timestamp showing when this payment was initiated - **amount_sent_msat** (msat): The amount sent @@ -80,7 +80,7 @@ On success, an object is returned, containing: - **bolt12** (string, optional): the bolt12 string (if supplied: **experimental-offers** only). If **status** is "complete": - - **payment_preimage** (hex): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "pending": - **message** (string): Monitor status with listpays or waitsendpay @@ -138,4 +138,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:44540ace609ccfa7b023526d7a92ba7cf4a6058f3ae2124c20fa65b92137e41b) +[comment]: # ( SHA256STAMP:a7f55104f3bdb21057a410a33902b92aca38aaa83b1e0e7e6876a911316132ed) diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index 725068fad716..525503312e0a 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -37,7 +37,7 @@ RETURN VALUE On success, an object is returned, containing: - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid or expired (one of "paid", "expired") - **expires_at** (u64): UNIX timestamp of when it will become / became unpayable - **amount_msat** (msat, optional): the amount required to pay this invoice @@ -48,7 +48,7 @@ If **status** is "paid": - **pay_index** (u64): Unique incrementing index for this payment - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - **paid_at** (u64): UNIX timestamp of when it was paid - - **payment_preimage** (hex): proof of payment (always 64 characters) + - **payment_preimage** (secret): proof of payment (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:33df5fb9bcbcb6d2240d0d18b970b2300414aae36b81fb276fcedfc21480d22f) +[comment]: # ( SHA256STAMP:6c4df3bb97df9f87583725ef31e803900ac548e17506692f9da365a3e4bbf18f) diff --git a/doc/lightning-waitinvoice.7.md b/doc/lightning-waitinvoice.7.md index c3447b5addfe..d568183afa98 100644 --- a/doc/lightning-waitinvoice.7.md +++ b/doc/lightning-waitinvoice.7.md @@ -19,7 +19,7 @@ RETURN VALUE On success, an object is returned, containing: - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid or expired (one of "paid", "expired") - **expires_at** (u64): UNIX timestamp of when it will become / became unpayable - **amount_msat** (msat, optional): the amount required to pay this invoice @@ -30,7 +30,7 @@ If **status** is "paid": - **pay_index** (u64): Unique incrementing index for this payment - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - **paid_at** (u64): UNIX timestamp of when it was paid - - **payment_preimage** (hex): proof of payment (always 64 characters) + - **payment_preimage** (secret): proof of payment (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:33df5fb9bcbcb6d2240d0d18b970b2300414aae36b81fb276fcedfc21480d22f) +[comment]: # ( SHA256STAMP:6c4df3bb97df9f87583725ef31e803900ac548e17506692f9da365a3e4bbf18f) diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index ff87bea3efc7..a44abd8e3ac9 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -35,7 +35,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: - **id** (u64): unique ID for this payment attempt -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (always "complete") - **created_at** (u64): the UNIX timestamp showing when this payment was initiated - **amount_sent_msat** (msat): The amount sent @@ -48,7 +48,7 @@ On success, an object is returned, containing: - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "complete": - - **payment_preimage** (hex): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -101,4 +101,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3f89cf80acc1e9363509f0a053a617f8b381790823f8cd05fa6c708eb72fcc7e) +[comment]: # ( SHA256STAMP:96e5cfd0bd6f2d6b7a25a60f846283592404cee07ef142e58cd56929cc6923c9) diff --git a/doc/schemas/createinvoice.schema.json b/doc/schemas/createinvoice.schema.json index f1d6e1828dc0..ed97308a4386 100644 --- a/doc/schemas/createinvoice.schema.json +++ b/doc/schemas/createinvoice.schema.json @@ -23,7 +23,7 @@ "description": "the bolt12 string instead of **bolt11** (**experimental-offers** only)" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -62,7 +62,7 @@ "description": "UNIX timestamp of when invoice was paid (**status** *paid* only)" }, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "the proof of payment: SHA256 of this **payment_hash**", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/createonion.request.json b/doc/schemas/createonion.request.json index 07755cd77aa9..d027dbbd9493 100644 --- a/doc/schemas/createonion.request.json +++ b/doc/schemas/createonion.request.json @@ -32,7 +32,7 @@ "description": "" }, "session_key": { - "type": "hex" + "type": "secret" }, "onion_size": { "type": "u16" diff --git a/doc/schemas/createonion.schema.json b/doc/schemas/createonion.schema.json index 7b4df99239c0..eab28f2a3c0f 100644 --- a/doc/schemas/createonion.schema.json +++ b/doc/schemas/createonion.schema.json @@ -15,7 +15,7 @@ "type": "array", "description": "one shared secret for each node in the *hops* parameter", "items": { - "type": "hex", + "type": "secret", "description": "the shared secret with this hop", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/delinvoice.schema.json b/doc/schemas/delinvoice.schema.json index 2a86ad594906..bbc724807644 100644 --- a/doc/schemas/delinvoice.schema.json +++ b/doc/schemas/delinvoice.schema.json @@ -33,7 +33,7 @@ "description": "description used in the invoice" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -154,7 +154,7 @@ "description": "UNIX timestamp of when payment was received" }, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "SHA256 of this is the *payment_hash* offered in the invoice", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/invoice.schema.json b/doc/schemas/invoice.schema.json index fcb3f899a034..8092b576da3a 100644 --- a/doc/schemas/invoice.schema.json +++ b/doc/schemas/invoice.schema.json @@ -14,13 +14,13 @@ "description": "the bolt11 string" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 }, "payment_secret": { - "type": "hex", + "type": "secret", "description": "the *payment_secret* to place in the onion", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/keysend.schema.json b/doc/schemas/keysend.schema.json index 1a4f395f9fcd..1881260cdb08 100644 --- a/doc/schemas/keysend.schema.json +++ b/doc/schemas/keysend.schema.json @@ -13,7 +13,7 @@ ], "properties": { "payment_preimage": { - "type": "hex", + "type": "secret", "description": "the proof of payment: SHA256 of this **payment_hash**", "maxLength": 64, "minLength": 64 @@ -23,7 +23,7 @@ "description": "the final destination of the payment" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/listinvoices.schema.json b/doc/schemas/listinvoices.schema.json index 9afd91299215..44054e3bbc1b 100644 --- a/doc/schemas/listinvoices.schema.json +++ b/doc/schemas/listinvoices.schema.json @@ -27,7 +27,7 @@ "description": "description used in the invoice" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -119,7 +119,7 @@ "description": "UNIX timestamp of when it was paid" }, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "proof of payment", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index f68d5580996c..d35091182c1c 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -226,7 +226,7 @@ "description": "The short_channel_id (once locked in)" }, "channel_id": { - "type": "hex", + "type": "hash", "description": "The full channel_id", "minLength": 64, "maxLength": 64 @@ -632,7 +632,7 @@ "description": "Block this HTLC expires at" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the payment_preimage which will prove payment", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/listsendpays.request.json b/doc/schemas/listsendpays.request.json index 89c65cfd0b67..f313c861388b 100644 --- a/doc/schemas/listsendpays.request.json +++ b/doc/schemas/listsendpays.request.json @@ -8,7 +8,7 @@ "type": "string" }, "payment_hash": { - "type": "hex" + "type": "hash" }, "status": { "type": "string", diff --git a/doc/schemas/listsendpays.schema.json b/doc/schemas/listsendpays.schema.json index 8daaaf82713e..8db7f553fe5b 100644 --- a/doc/schemas/listsendpays.schema.json +++ b/doc/schemas/listsendpays.schema.json @@ -28,7 +28,7 @@ "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -110,7 +110,7 @@ "bolt11": {}, "bolt12": {}, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "the proof of payment: SHA256 of this **payment_hash**", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/pay.schema.json b/doc/schemas/pay.schema.json index 6c80bb3161f9..dc2965fab9c8 100644 --- a/doc/schemas/pay.schema.json +++ b/doc/schemas/pay.schema.json @@ -13,7 +13,7 @@ ], "properties": { "payment_preimage": { - "type": "hex", + "type": "secret", "description": "the proof of payment: SHA256 of this **payment_hash**", "maxLength": 64, "minLength": 64 @@ -23,7 +23,7 @@ "description": "the final destination of the payment" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/sendonion.request.json b/doc/schemas/sendonion.request.json index 531f68093de7..c947f958e5a0 100644 --- a/doc/schemas/sendonion.request.json +++ b/doc/schemas/sendonion.request.json @@ -31,7 +31,7 @@ } }, "payment_hash": { - "type": "hex" + "type": "hash" }, "label": { "type": "string" @@ -39,7 +39,7 @@ "shared_secrets": { "type": "array", "items": { - "type": "hex" + "type": "secret" } }, "partid": { diff --git a/doc/schemas/sendonion.schema.json b/doc/schemas/sendonion.schema.json index 7368f849a49c..5b39cf772313 100644 --- a/doc/schemas/sendonion.schema.json +++ b/doc/schemas/sendonion.schema.json @@ -15,7 +15,7 @@ "description": "unique ID for this payment attempt" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -100,7 +100,7 @@ "bolt12": {}, "partid": {}, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "the proof of payment: SHA256 of this **payment_hash**", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/sendpay.request.json b/doc/schemas/sendpay.request.json index c8b854a9e316..b764999a6e6e 100644 --- a/doc/schemas/sendpay.request.json +++ b/doc/schemas/sendpay.request.json @@ -34,7 +34,7 @@ } }, "payment_hash": { - "type": "hex" + "type": "hash" }, "label": { "type": "string" @@ -46,7 +46,7 @@ "type": "string" }, "payment_secret": { - "type": "hex" + "type": "secret" }, "partid": { "type": "u16" diff --git a/doc/schemas/sendpay.schema.json b/doc/schemas/sendpay.schema.json index fec96ba54804..dc2b7c80a23f 100644 --- a/doc/schemas/sendpay.schema.json +++ b/doc/schemas/sendpay.schema.json @@ -19,7 +19,7 @@ "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -104,7 +104,7 @@ "bolt11": {}, "bolt12": {}, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "the proof of payment: SHA256 of this **payment_hash**", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/txdiscard.request.json b/doc/schemas/txdiscard.request.json index 187203782288..6488647acce7 100644 --- a/doc/schemas/txdiscard.request.json +++ b/doc/schemas/txdiscard.request.json @@ -7,7 +7,7 @@ ], "properties": { "txid": { - "type": "hex" + "type": "txid" } } } diff --git a/doc/schemas/txsend.request.json b/doc/schemas/txsend.request.json index 187203782288..6488647acce7 100644 --- a/doc/schemas/txsend.request.json +++ b/doc/schemas/txsend.request.json @@ -7,7 +7,7 @@ ], "properties": { "txid": { - "type": "hex" + "type": "txid" } } } diff --git a/doc/schemas/waitanyinvoice.schema.json b/doc/schemas/waitanyinvoice.schema.json index 8c4b9edada54..1c739be29b7d 100644 --- a/doc/schemas/waitanyinvoice.schema.json +++ b/doc/schemas/waitanyinvoice.schema.json @@ -19,7 +19,7 @@ "description": "description used in the invoice" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -98,7 +98,7 @@ "description": "UNIX timestamp of when it was paid" }, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "proof of payment", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/waitinvoice.schema.json b/doc/schemas/waitinvoice.schema.json index 8c4b9edada54..1c739be29b7d 100644 --- a/doc/schemas/waitinvoice.schema.json +++ b/doc/schemas/waitinvoice.schema.json @@ -19,7 +19,7 @@ "description": "description used in the invoice" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -98,7 +98,7 @@ "description": "UNIX timestamp of when it was paid" }, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "proof of payment", "maxLength": 64, "minLength": 64 diff --git a/doc/schemas/waitsendpay.request.json b/doc/schemas/waitsendpay.request.json index e4734025275b..9e6f3942b14a 100644 --- a/doc/schemas/waitsendpay.request.json +++ b/doc/schemas/waitsendpay.request.json @@ -7,7 +7,7 @@ ], "properties": { "payment_hash": { - "type": "hex" + "type": "hash" }, "timeout": { "type": "u32" diff --git a/doc/schemas/waitsendpay.schema.json b/doc/schemas/waitsendpay.schema.json index 853a07610db2..7a086e0fd6af 100644 --- a/doc/schemas/waitsendpay.schema.json +++ b/doc/schemas/waitsendpay.schema.json @@ -19,7 +19,7 @@ "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" }, "payment_hash": { - "type": "hex", + "type": "hash", "description": "the hash of the *payment_preimage* which will prove payment", "maxLength": 64, "minLength": 64 @@ -103,7 +103,7 @@ "bolt11": {}, "bolt12": {}, "payment_preimage": { - "type": "hex", + "type": "secret", "description": "the proof of payment: SHA256 of this **payment_hash**", "maxLength": 64, "minLength": 64 From 33960be040010b361154f3e9c3b7d1d355a3fd73 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:34 +1030 Subject: [PATCH 0645/1530] cln-rpc: Test that we forward errors correctly So far we were papering over the actual error with a generic string that isn't very useful. Now we decode the error and forward it through the grpc layer as well (though still as a string). --- cln-rpc/examples/getinfo.rs | 7 +++++-- cln-rpc/src/lib.rs | 41 +++++++++++++++++++++++++++++-------- cln-rpc/src/primitives.rs | 8 ++++++++ tests/test_cln_rs.py | 11 +++++++++- 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/cln-rpc/examples/getinfo.rs b/cln-rpc/examples/getinfo.rs index 9e61e22b4315..b9887d8e7253 100644 --- a/cln-rpc/examples/getinfo.rs +++ b/cln-rpc/examples/getinfo.rs @@ -1,4 +1,4 @@ -use anyhow::Context; +use anyhow::{anyhow, Context}; use cln_rpc::{model::GetinfoRequest, ClnRpc, Request}; use std::env::args; use std::path::Path; @@ -13,7 +13,10 @@ async fn main() -> Result<(), anyhow::Error> { let p = Path::new(&rpc_path); let mut rpc = ClnRpc::new(p).await?; - let response = rpc.call(Request::Getinfo(GetinfoRequest {})).await?; + let response = rpc + .call(Request::Getinfo(GetinfoRequest {})) + .await + .map_err(|e| anyhow!("Error calling getinfo: {:?}", e))?; println!("{}", serde_json::to_string_pretty(&response)?); Ok(()) } diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs index 5cf2cba55ff7..f2c34ba23bcc 100644 --- a/cln-rpc/src/lib.rs +++ b/cln-rpc/src/lib.rs @@ -1,7 +1,7 @@ use crate::codec::JsonCodec; use crate::codec::JsonRpc; -use anyhow::{Context, Result}; pub use anyhow::Error; +use anyhow::Result; use futures_util::sink::SinkExt; use futures_util::StreamExt; use log::{debug, trace}; @@ -21,6 +21,7 @@ pub mod primitives; pub use crate::{ model::{Request, Response}, notifications::Notification, + primitives::RpcError, }; /// @@ -54,30 +55,54 @@ impl ClnRpc { }) } - pub async fn call(&mut self, req: Request) -> Result { + pub async fn call(&mut self, req: Request) -> Result { trace!("Sending request {:?}", req); // Wrap the raw request in a well-formed JSON-RPC outer dict. let id = self.next_id.fetch_add(1, Ordering::SeqCst); let req: JsonRpc = JsonRpc::Request(id, req); - let req = serde_json::to_value(req)?; + let req = serde_json::to_value(req).map_err(|e| RpcError { + code: None, + message: format!("Error parsing request: {}", e), + })?; let req2 = req.clone(); - self.write.send(req).await?; + self.write.send(req).await.map_err(|e| RpcError { + code: None, + message: format!("Error passing request to lightningd: {}", e), + })?; let mut response = self .read .next() .await - .context("no response from lightningd")? - .context("reading response from socket")?; + .ok_or_else(|| RpcError { + code: None, + message: "no response from lightningd".to_string(), + })? + .map_err(|_| RpcError { + code: None, + message: "reading response from socket".to_string(), + })?; trace!("Read response {:?}", response); // Annotate the response with the method from the request, so // serde_json knows which variant of [`Request`] should be // used. response["method"] = req2["method"].clone(); - log::warn!("XXX {:?}", response); - serde_json::from_value(response).context("converting response into enum") + if let Some(_) = response.get("result") { + serde_json::from_value(response).map_err(|e| RpcError { + code: None, + message: format!("Malformed response from lightningd: {}", e), + }) + } else if let Some(e) = response.get("error") { + let e: RpcError = serde_json::from_value(e.clone()).unwrap(); + Err(e) + } else { + Err(RpcError { + code: None, + message: format!("Malformed response from lightningd: {}", response), + }) + } } } diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 0631e2b2f993..c141f4db3a38 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -642,3 +642,11 @@ pub struct Routehint { pub struct RoutehintList { pub hints: Vec, } + +/// An error returned by the lightningd RPC consisting of a code and a +/// message +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct RpcError { + pub code: Option, + pub message: String, +} diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 1bffc70ddf18..c85121d3ccf7 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -3,7 +3,7 @@ from pyln.testing.utils import env, TEST_NETWORK, wait_for from ephemeral_port_reserve import reserve import grpc -from primitives_pb2 import AmountOrAny +from primitives_pb2 import AmountOrAny, Amount import pytest import subprocess @@ -113,6 +113,15 @@ def test_grpc_connect(node_factory): )) print(inv) + # Test a failing RPC call, so we know that errors are returned correctly. + with pytest.raises(Exception, match=r'Duplicate label'): + # This request creates a label collision + stub.Invoice(nodepb.InvoiceRequest( + msatoshi=AmountOrAny(amount=Amount(msat=12345)), + description="hello", + label="lbl1", + )) + def test_grpc_generate_certificate(node_factory): """Test whether we correctly generate the certificates. From 0c08ca8f273f3601cb6a65e525968b745c45427e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:35 +1030 Subject: [PATCH 0646/1530] cln-grpc: Add conversion test for listpeers --- cln-grpc/Cargo.toml | 3 + cln-grpc/src/pb.rs | 222 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+) diff --git a/cln-grpc/Cargo.toml b/cln-grpc/Cargo.toml index c0bccbe599d5..0d620dc0edae 100644 --- a/cln-grpc/Cargo.toml +++ b/cln-grpc/Cargo.toml @@ -11,5 +11,8 @@ tonic = { version = "^0.5", features = ["tls", "transport"] } prost = "0.8" hex = "0.4.3" +[dev-dependencies] +serde_json = "1.0.72" + [build-dependencies] tonic-build = "^0.5" diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index e37118614931..cfec44b655f1 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -127,3 +127,225 @@ impl From for cln_rpc::primitives::RoutehintList { } } } +#[cfg(test)] +mod test { + use super::*; + use serde_json::json; + + #[test] + fn test_listpeers() { + let j: serde_json::Value = json!({ + "peers": [ + { + "id": "0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", + "connected": true, + "netaddr": [ + "127.0.0.1:39152" + ], + "features": "8808226aa2", + "channels": [ + { + "state": "CHANNELD_NORMAL", + "scratch_txid": "fd4659658d235c20c81f96f7bc867c17abbfd20fcdd46c27eaad74ea52eaee90", + "last_tx_fee_msat": "14257000msat", + "feerate": { + "perkw": 11000, + "perkb": 44000 + }, + "owner": "channeld", + "short_channel_id": "103x2x1", + "direction": 0, + "channel_id": "44b77a6d66ca54f0c365c84b13a95fbde462415a0549228baa25ee1bb1dfef66", + "funding_txid": "67efdfb11bee25aa8b2249055a4162e4bd5fa9134bc865c3f054ca666d7ab744", + "funding_outnum": 1, + "close_to_addr": "bcrt1q9tc6q49l6wrrtp8ul45rj92hsleehwwxty32zu", + "close_to": "00142af1a054bfd3863584fcfd6839155787f39bb9c6", + "private": false, + "opener": "remote", + "features": [ + "option_static_remotekey", + "option_anchor_outputs" + ], + "funding": { + "local_msat": "0msat", + "remote_msat": "1000000000msat", + "pushed_msat": "0msat" + }, + "msatoshi_to_us": 0, + "to_us_msat": "0msat", + "msatoshi_to_us_min": 0, + "min_to_us_msat": "0msat", + "msatoshi_to_us_max": 0, + "max_to_us_msat": "0msat", + "msatoshi_total": 1000000000, + "total_msat": "1000000000msat", + "fee_base_msat": "1msat", + "fee_proportional_millionths": 10, + "dust_limit_satoshis": 546, + "dust_limit_msat": "546000msat", + "max_total_htlc_in_msat": "18446744073709551615msat", + "their_channel_reserve_satoshis": 10000, + "their_reserve_msat": "10000000msat", + "our_channel_reserve_satoshis": 10000, + "our_reserve_msat": "10000000msat", + "spendable_msatoshi": 0, + "spendable_msat": "0msat", + "receivable_msatoshi": 853257998, + "receivable_msat": "853257998msat", + "htlc_minimum_msat": 0, + "minimum_htlc_in_msat": "0msat", + "their_to_self_delay": 5, + "our_to_self_delay": 5, + "max_accepted_htlcs": 483, + "state_changes": [ + { + "timestamp": "2022-03-25T13:57:33.322Z", + "old_state": "CHANNELD_AWAITING_LOCKIN", + "new_state": "CHANNELD_NORMAL", + "cause": "remote", + "message": "Lockin complete" + } + ], + "status": [ + "CHANNELD_NORMAL:Funding transaction locked. Channel announced." + ], + "in_payments_offered": 1, + "in_msatoshi_offered": 100002002, + "in_offered_msat": "100002002msat", + "in_payments_fulfilled": 0, + "in_msatoshi_fulfilled": 0, + "in_fulfilled_msat": "0msat", + "out_payments_offered": 0, + "out_msatoshi_offered": 0, + "out_offered_msat": "0msat", + "out_payments_fulfilled": 0, + "out_msatoshi_fulfilled": 0, + "out_fulfilled_msat": "0msat", + "htlcs": [ + { + "direction": "in", + "id": 0, + "msatoshi": 100002002, + "amount_msat": "100002002msat", + "expiry": 131, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "RCVD_ADD_ACK_REVOCATION" + } + ] + } + ] + }, + { + "id": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", + "connected": true, + "netaddr": [ + "127.0.0.1:38321" + ], + "features": "8808226aa2", + "channels": [ + { + "state": "CHANNELD_NORMAL", + "scratch_txid": "30530d3f522862773100b7600d8ea8921a5ee84df17a2317326f9aa2c4829326", + "last_tx_fee_msat": "16149000msat", + "feerate": { + "perkw": 11000, + "perkb": 44000 + }, + "owner": "channeld", + "short_channel_id": "103x1x0", + "direction": 0, + "channel_id": "006a2044fc72fa5c4a54c9fddbf208970a7b3b4fd2aaa70a96abba757c01769e", + "funding_txid": "9e76017c75baab960aa7aad24f3b7b0a9708f2dbfdc9544a5cfa72fc44206a00", + "funding_outnum": 0, + "close_to_addr": "bcrt1qhfmyce4ujce2pyugew2435tlwft6p6w4s3py6d", + "close_to": "0014ba764c66bc9632a09388cb9558d17f7257a0e9d5", + "private": false, + "opener": "local", + "features": [ + "option_static_remotekey", + "option_anchor_outputs" + ], + "funding": { + "local_msat": "1000000000msat", + "remote_msat": "0msat", + "pushed_msat": "0msat" + }, + "msatoshi_to_us": 1000000000, + "to_us_msat": "1000000000msat", + "msatoshi_to_us_min": 1000000000, + "min_to_us_msat": "1000000000msat", + "msatoshi_to_us_max": 1000000000, + "max_to_us_msat": "1000000000msat", + "msatoshi_total": 1000000000, + "total_msat": "1000000000msat", + "fee_base_msat": "1msat", + "fee_proportional_millionths": 10, + "dust_limit_satoshis": 546, + "dust_limit_msat": "546000msat", + "max_total_htlc_in_msat": "18446744073709551615msat", + "their_channel_reserve_satoshis": 10000, + "their_reserve_msat": "10000000msat", + "our_channel_reserve_satoshis": 10000, + "our_reserve_msat": "10000000msat", + "spendable_msatoshi": 749473998, + "spendable_msat": "749473998msat", + "receivable_msatoshi": 0, + "receivable_msat": "0msat", + "htlc_minimum_msat": 0, + "minimum_htlc_in_msat": "0msat", + "their_to_self_delay": 5, + "our_to_self_delay": 5, + "max_accepted_htlcs": 483, + "state_changes": [ + { + "timestamp": "2022-03-25T13:57:33.325Z", + "old_state": "CHANNELD_AWAITING_LOCKIN", + "new_state": "CHANNELD_NORMAL", + "cause": "user", + "message": "Lockin complete" + } + ], + "status": [ + "CHANNELD_NORMAL:Funding transaction locked. Channel announced." + ], + "in_payments_offered": 0, + "in_msatoshi_offered": 0, + "in_offered_msat": "0msat", + "in_payments_fulfilled": 0, + "in_msatoshi_fulfilled": 0, + "in_fulfilled_msat": "0msat", + "out_payments_offered": 2, + "out_msatoshi_offered": 200002002, + "out_offered_msat": "200002002msat", + "out_payments_fulfilled": 0, + "out_msatoshi_fulfilled": 0, + "out_fulfilled_msat": "0msat", + "htlcs": [ + { + "direction": "out", + "id": 1, + "msatoshi": 100001001, + "amount_msat": "100001001msat", + "expiry": 125, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "SENT_ADD_ACK_REVOCATION" + }, + { + "direction": "out", + "id": 0, + "msatoshi": 100001001, + "amount_msat": "100001001msat", + "expiry": 124, + "payment_hash": "d17a42c4f7f49648064a0ce7ce848bd92c4c50f24d35fe5c3d1f3a7a9bf474b2", + "state": "SENT_ADD_ACK_REVOCATION" + } + ] + } + ] + } + ] + }); + let u: cln_rpc::model::ListpeersResponse = serde_json::from_value(j).unwrap(); + let g: ListpeersResponse = (&u).into(); + } +} From 241a891190019c434b372d40b3b50c3d716da2bf Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:35 +1030 Subject: [PATCH 0647/1530] cln-rpc: Yet more RPC methods being mapped - disconnect - feerates - getroute - listforwards - listpays - ping - signmessage --- contrib/msggen/msggen/__main__.py | 18 ++++------- doc/schemas/disconnect.request.json | 16 ++++++++++ doc/schemas/feerates.request.json | 17 ++++++++++ doc/schemas/getroute.request.json | 46 +++++++++++++++++++++++++++ doc/schemas/listforwards.request.json | 22 +++++++++++++ doc/schemas/listpays.request.json | 22 +++++++++++++ doc/schemas/ping.request.json | 19 +++++++++++ doc/schemas/signmessage.request.json | 13 ++++++++ tests/test_cln_rs.py | 3 ++ 9 files changed, 165 insertions(+), 11 deletions(-) create mode 100644 doc/schemas/disconnect.request.json create mode 100644 doc/schemas/feerates.request.json create mode 100644 doc/schemas/getroute.request.json create mode 100644 doc/schemas/listforwards.request.json create mode 100644 doc/schemas/listpays.request.json create mode 100644 doc/schemas/ping.request.json create mode 100644 doc/schemas/signmessage.request.json diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index a864940f04e9..e423ac5683f6 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -81,22 +81,20 @@ def load_jsonrpc_service(): # "decode", # "delpay", # "disableoffer", - # "disconnect", - # "feerates", + "Disconnect", + "Feerates", # "fetchinvoice", # "fundchannel_cancel", # "fundchannel_complete", # "fundchannel", # "fundchannel_start", # "funderupdate", - # "fundpsbt", # "getlog", - # "getroute", + "GetRoute", # "getsharedsecret", - # "listconfigs", - # "listforwards", + "ListForwards", # "listoffers", - # "listpays", + "ListPays", # "multifundchannel", # "multiwithdraw", # "offerout", @@ -107,16 +105,14 @@ def load_jsonrpc_service(): # "openchannel_signed", # "openchannel_update", # "parsefeerate", - # "ping", + "Ping", # "plugin", # "reserveinputs", # "sendcustommsg", # "sendinvoice", # "sendonionmessage", - # "sendpsbt", # "setchannelfee", - # "signmessage", - # "signpsbt", + "SignMessage", # "unreserveinputs", # "waitblockheight", # "ListConfigs", diff --git a/doc/schemas/disconnect.request.json b/doc/schemas/disconnect.request.json new file mode 100644 index 000000000000..8714dbe089bd --- /dev/null +++ b/doc/schemas/disconnect.request.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "pubkey" + }, + "force": { + "type": "boolean" + } + } +} diff --git a/doc/schemas/feerates.request.json b/doc/schemas/feerates.request.json new file mode 100644 index 000000000000..d4dbffe3b770 --- /dev/null +++ b/doc/schemas/feerates.request.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "style" + ], + "properties": { + "style": { + "type": "string", + "enum": [ + "perkb", + "perkw" + ] + } + } +} diff --git a/doc/schemas/getroute.request.json b/doc/schemas/getroute.request.json new file mode 100644 index 000000000000..8a10aaf8c1c6 --- /dev/null +++ b/doc/schemas/getroute.request.json @@ -0,0 +1,46 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "id", + "msatoshi", + "riskfactor" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "" + }, + "msatoshi": { + "type": "msat", + "description": "" + }, + "riskfactor": { + "type": "u64", + "description": "" + }, + "cltv": { + "type": "number", + "description": "" + }, + "fromid": { + "type": "pubkey", + "description": "" + }, + "fuzzpercent": { + "type": "u32", + "description": "" + }, + "exclude": { + "type": "array", + "description": "", + "items": { + "type": "string" + } + }, + "maxhops": { + "type": "u32", + "description": "" + } + } +} diff --git a/doc/schemas/listforwards.request.json b/doc/schemas/listforwards.request.json new file mode 100644 index 000000000000..8bf542ab8a71 --- /dev/null +++ b/doc/schemas/listforwards.request.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "properties": { + "status": { + "type": "string", + "enum": [ + "offered", + "settled", + "local_failed", + "failed" + ] + }, + "in_channel": { + "type": "short_channel_id" + }, + "out_channel": { + "type": "short_channel_id" + } + } +} diff --git a/doc/schemas/listpays.request.json b/doc/schemas/listpays.request.json new file mode 100644 index 000000000000..f313c861388b --- /dev/null +++ b/doc/schemas/listpays.request.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": { + "bolt11": { + "type": "string" + }, + "payment_hash": { + "type": "hash" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "complete", + "failed" + ] + } + } +} diff --git a/doc/schemas/ping.request.json b/doc/schemas/ping.request.json new file mode 100644 index 000000000000..5ca1e50d4cdc --- /dev/null +++ b/doc/schemas/ping.request.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "id" + ], + "additionalProperties": false, + "properties": { + "id": { + "type": "pubkey" + }, + "len": { + "type": "number" + }, + "pongbytes": { + "type": "number" + } + } +} diff --git a/doc/schemas/signmessage.request.json b/doc/schemas/signmessage.request.json new file mode 100644 index 000000000000..163239e207bc --- /dev/null +++ b/doc/schemas/signmessage.request.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "message" + ], + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + } + } +} diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index c85121d3ccf7..2345d5f529ba 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -113,6 +113,9 @@ def test_grpc_connect(node_factory): )) print(inv) + rates = stub.Feerates(nodepb.FeeratesRequest(style='PERKB')) + print(rates) + # Test a failing RPC call, so we know that errors are returned correctly. with pytest.raises(Exception, match=r'Duplicate label'): # This request creates a label collision From 5a1e58f0dca7fdf1af6ae6e810aeca9983abe38b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 14:43:35 +1030 Subject: [PATCH 0648/1530] pytest: fix RUST=0 pytest. Signed-off-by: Rusty Russell --- tests/test_cln_rs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 2345d5f529ba..ee591d76b5f1 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -3,7 +3,6 @@ from pyln.testing.utils import env, TEST_NETWORK, wait_for from ephemeral_port_reserve import reserve import grpc -from primitives_pb2 import AmountOrAny, Amount import pytest import subprocess @@ -75,6 +74,7 @@ def test_grpc_connect(node_factory): # These only exist if we have rust! from node_pb2_grpc import NodeStub # noqa: E402 import node_pb2 as nodepb # noqa: E402 + from primitives_pb2 import AmountOrAny, Amount # noqa: E402 grpc_port = reserve() bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" From be04f25666436b7bfdf684194c18823eee50ae26 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 15:08:55 +0200 Subject: [PATCH 0649/1530] msggen: Don't assume we have properties in the schema --- contrib/msggen/msggen/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/msggen/msggen/model.py b/contrib/msggen/msggen/model.py index 4c8902beef7d..411f71f076b7 100644 --- a/contrib/msggen/msggen/model.py +++ b/contrib/msggen/msggen/model.py @@ -100,7 +100,7 @@ def __init__(self, typename, fields, path, description): def from_js(cls, js, path): typename = path2type(path) - properties = js["properties"] + properties = js.get("properties", {}) # Ok, let's flatten the conditional properties. We do this by # reformatting the outer conditions into the `allOf` format. top = { From aae5e3d070ee0ab30a865cabe6ba40fc52e72a18 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Apr 2022 14:43:35 +1030 Subject: [PATCH 0650/1530] cln-grpc: Final generation of derived files Didn't want to pollute each commit with the changes in the derived files so here are all the changes in one commit :-) --- .msggen.json | 193 ++++++++- cln-grpc/proto/node.proto | 294 ++++++++++--- cln-grpc/src/convert.rs | 468 ++++++++++++++------ cln-grpc/src/server.rs | 224 ++++++++++ cln-rpc/src/model.rs | 868 ++++++++++++++++++++++++++++++-------- 5 files changed, 1687 insertions(+), 360 deletions(-) diff --git a/.msggen.json b/.msggen.json index a9475fb0dfbf..cf166ea61c9e 100644 --- a/.msggen.json +++ b/.msggen.json @@ -33,6 +33,10 @@ "paid": 0, "unpaid": 2 }, + "FeeratesStyle": { + "perkb": 0, + "perkw": 1 + }, "GetinfoAddressType": { "dns": 0, "ipv4": 1, @@ -48,9 +52,28 @@ "torv2": 3, "torv3": 4 }, + "GetrouteRouteStyle": { + "tlv": 0 + }, "KeysendStatus": { "complete": 0 }, + "ListforwardsForwardsStatus": { + "failed": 3, + "local_failed": 2, + "offered": 0, + "settled": 1 + }, + "ListforwardsForwardsStyle": { + "legacy": 0, + "tlv": 1 + }, + "ListforwardsStatus": { + "failed": 3, + "local_failed": 2, + "offered": 0, + "settled": 1 + }, "ListfundsOutputsStatus": { "confirmed": 1, "spent": 2, @@ -69,6 +92,16 @@ "torv3": 4, "websocket": 5 }, + "ListpaysPaysStatus": { + "complete": 2, + "failed": 1, + "pending": 0 + }, + "ListpaysStatus": { + "complete": 1, + "failed": 2, + "pending": 0 + }, "ListpeersPeersChannelsHtlcsDirection": { "in": 0, "out": 1 @@ -144,6 +177,7 @@ "withdraw": 2 }, "NewaddrAddresstype": { + "all": 2, "bech32": 0, "p2sh-segwit": 1 }, @@ -197,6 +231,7 @@ "CloseRequest": { "Close.destination": 3, "Close.fee_negotiation_step": 4, + "Close.feerange[]": 7, "Close.force_lease_closed": 6, "Close.id": 1, "Close.unilateraltimeout": 2, @@ -262,22 +297,27 @@ "DatastoreRequest": { "Datastore.generation": 4, "Datastore.hex": 2, + "Datastore.key": 5, "Datastore.key[]": 1, - "Datastore.mode": 3 + "Datastore.mode": 3, + "Datastore.string": 6 }, "DatastoreResponse": { "Datastore.generation": 2, "Datastore.hex": 3, + "Datastore.key": 5, "Datastore.key[]": 1, "Datastore.string": 4 }, "DeldatastoreRequest": { "DelDatastore.generation": 2, + "DelDatastore.key": 3, "DelDatastore.key[]": 1 }, "DeldatastoreResponse": { "DelDatastore.generation": 2, "DelDatastore.hex": 3, + "DelDatastore.key": 5, "DelDatastore.key[]": 1, "DelDatastore.string": 4 }, @@ -285,6 +325,7 @@ "DelExpiredInvoice.maxexpirytime": 1 }, "DelinvoiceRequest": { + "DelInvoice.desconly": 3, "DelInvoice.label": 1, "DelInvoice.status": 2 }, @@ -300,7 +341,48 @@ "DelInvoice.payment_hash": 6, "DelInvoice.status": 7 }, + "DisconnectRequest": { + "Disconnect.force": 2, + "Disconnect.id": 1 + }, + "FeeratesOnchain_fee_estimates": { + "Feerates.onchain_fee_estimates.htlc_success_satoshis": 5, + "Feerates.onchain_fee_estimates.htlc_timeout_satoshis": 4, + "Feerates.onchain_fee_estimates.mutual_close_satoshis": 2, + "Feerates.onchain_fee_estimates.opening_channel_satoshis": 1, + "Feerates.onchain_fee_estimates.unilateral_close_satoshis": 3 + }, + "FeeratesPerkb": { + "Feerates.perkb.delayed_to_us": 6, + "Feerates.perkb.htlc_resolution": 7, + "Feerates.perkb.max_acceptable": 2, + "Feerates.perkb.min_acceptable": 1, + "Feerates.perkb.mutual_close": 4, + "Feerates.perkb.opening": 3, + "Feerates.perkb.penalty": 8, + "Feerates.perkb.unilateral_close": 5 + }, + "FeeratesPerkw": { + "Feerates.perkw.delayed_to_us": 6, + "Feerates.perkw.htlc_resolution": 7, + "Feerates.perkw.max_acceptable": 2, + "Feerates.perkw.min_acceptable": 1, + "Feerates.perkw.mutual_close": 4, + "Feerates.perkw.opening": 3, + "Feerates.perkw.penalty": 8, + "Feerates.perkw.unilateral_close": 5 + }, + "FeeratesRequest": { + "Feerates.style": 1 + }, + "FeeratesResponse": { + "Feerates.onchain_fee_estimates": 4, + "Feerates.perkb": 2, + "Feerates.perkw": 3, + "Feerates.warning_missing_feerates": 1 + }, "FundpsbtRequest": { + "FundPsbt.excess_as_change": 8, "FundPsbt.feerate": 2, "FundPsbt.locktime": 6, "FundPsbt.min_witness_weight": 7, @@ -360,9 +442,33 @@ "Getinfo.warning_bitcoind_sync": 16, "Getinfo.warning_lightningd_sync": 17 }, + "GetrouteRequest": { + "GetRoute.cltv": 4, + "GetRoute.exclude[]": 7, + "GetRoute.fromid": 5, + "GetRoute.fuzzpercent": 6, + "GetRoute.id": 1, + "GetRoute.maxhops": 8, + "GetRoute.msatoshi": 2, + "GetRoute.riskfactor": 3 + }, + "GetrouteResponse": { + "GetRoute.route[]": 1 + }, + "GetrouteRoute": { + "GetRoute.route[].amount_msat": 4, + "GetRoute.route[].channel": 2, + "GetRoute.route[].delay": 5, + "GetRoute.route[].direction": 3, + "GetRoute.route[].id": 1, + "GetRoute.route[].style": 6 + }, "InvoiceRequest": { "Invoice.cltv": 6, + "Invoice.deschashonly": 9, "Invoice.description": 2, + "Invoice.expiry": 7, + "Invoice.exposeprivatechannels": 8, "Invoice.fallbacks[]": 4, "Invoice.label": 3, "Invoice.msatoshi": 1, @@ -382,11 +488,13 @@ "KeysendRequest": { "KeySend.destination": 1, "KeySend.exemptfee": 7, + "KeySend.extratlvs": 9, "KeySend.label": 3, "KeySend.maxdelay": 6, "KeySend.maxfeepercent": 4, "KeySend.msatoshi": 2, - "KeySend.retry_for": 5 + "KeySend.retry_for": 5, + "KeySend.routehints": 8 }, "KeysendResponse": { "KeySend.amount_msat": 6, @@ -431,11 +539,31 @@ "ListDatastore.datastore[].string": 4 }, "ListdatastoreRequest": { + "ListDatastore.key": 2, "ListDatastore.key[]": 1 }, "ListdatastoreResponse": { "ListDatastore.datastore[]": 1 }, + "ListforwardsForwards": { + "ListForwards.forwards[].fee_msat": 7, + "ListForwards.forwards[].in_channel": 1, + "ListForwards.forwards[].in_msat": 2, + "ListForwards.forwards[].out_channel": 5, + "ListForwards.forwards[].out_msat": 8, + "ListForwards.forwards[].payment_hash": 6, + "ListForwards.forwards[].received_time": 4, + "ListForwards.forwards[].status": 3, + "ListForwards.forwards[].style": 9 + }, + "ListforwardsRequest": { + "ListForwards.in_channel": 2, + "ListForwards.out_channel": 3, + "ListForwards.status": 1 + }, + "ListforwardsResponse": { + "ListForwards.forwards[]": 1 + }, "ListfundsChannels": { "ListFunds.channels[].amount_msat": 3, "ListFunds.channels[].connected": 6, @@ -507,6 +635,26 @@ "ListnodesResponse": { "ListNodes.nodes[]": 1 }, + "ListpaysPays": { + "ListPays.pays[].amount_msat": 8, + "ListPays.pays[].amount_sent_msat": 9, + "ListPays.pays[].bolt11": 6, + "ListPays.pays[].bolt12": 7, + "ListPays.pays[].created_at": 4, + "ListPays.pays[].destination": 3, + "ListPays.pays[].erroronion": 10, + "ListPays.pays[].label": 5, + "ListPays.pays[].payment_hash": 1, + "ListPays.pays[].status": 2 + }, + "ListpaysRequest": { + "ListPays.bolt11": 1, + "ListPays.payment_hash": 2, + "ListPays.status": 3 + }, + "ListpaysResponse": { + "ListPays.pays[]": 1 + }, "ListpeersPeers": { "ListPeers.peers[].channels[]": 4, "ListPeers.peers[].connected": 2, @@ -539,8 +687,10 @@ "ListPeers.peers[].channels[].max_accepted_htlcs": 35, "ListPeers.peers[].channels[].max_to_us_msat": 22, "ListPeers.peers[].channels[].max_total_htlc_in_msat": 27, + "ListPeers.peers[].channels[].maximum_htlc_out_msat": 49, "ListPeers.peers[].channels[].min_to_us_msat": 21, "ListPeers.peers[].channels[].minimum_htlc_in_msat": 32, + "ListPeers.peers[].channels[].minimum_htlc_out_msat": 48, "ListPeers.peers[].channels[].next_fee_step": 12, "ListPeers.peers[].channels[].next_feerate": 11, "ListPeers.peers[].channels[].opener": 16, @@ -668,8 +818,10 @@ }, "PayRequest": { "Pay.bolt11": 1, + "Pay.exclude": 10, "Pay.exemptfee": 7, "Pay.label": 3, + "Pay.localofferid": 9, "Pay.maxdelay": 6, "Pay.maxfeepercent": 4, "Pay.msatoshi": 2, @@ -687,14 +839,31 @@ "Pay.status": 9, "Pay.warning_partial_completion": 8 }, + "PingRequest": { + "Ping.id": 1, + "Ping.len": 2, + "Ping.pongbytes": 3 + }, + "PingResponse": { + "Ping.totlen": 1 + }, "SendonionFirst_hop": { "SendOnion.first_hop.amount_msat": 2, "SendOnion.first_hop.delay": 3, "SendOnion.first_hop.id": 1 }, "SendonionRequest": { + "SendOnion.bolt11": 7, + "SendOnion.destination": 9, "SendOnion.first_hop": 2, - "SendOnion.onion": 1 + "SendOnion.groupid": 11, + "SendOnion.label": 4, + "SendOnion.localofferid": 10, + "SendOnion.msatoshi": 8, + "SendOnion.onion": 1, + "SendOnion.partid": 6, + "SendOnion.payment_hash": 3, + "SendOnion.shared_secrets[]": 5 }, "SendonionResponse": { "SendOnion.amount_msat": 4, @@ -706,13 +875,16 @@ "SendOnion.id": 1, "SendOnion.label": 8, "SendOnion.message": 12, + "SendOnion.partid": 13, "SendOnion.payment_hash": 2, "SendOnion.payment_preimage": 11, "SendOnion.status": 3 }, "SendpayRequest": { "SendPay.bolt11": 5, + "SendPay.groupid": 9, "SendPay.label": 3, + "SendPay.localofferid": 8, "SendPay.msatoshi": 4, "SendPay.partid": 7, "SendPay.payment_hash": 2, @@ -742,12 +914,21 @@ "SendPay.route[].msatoshi": 1 }, "SendpsbtRequest": { - "SendPsbt.psbt": 1 + "SendPsbt.psbt": 1, + "SendPsbt.reserve": 2 }, "SendpsbtResponse": { "SendPsbt.tx": 1, "SendPsbt.txid": 2 }, + "SignmessageRequest": { + "SignMessage.message": 1 + }, + "SignmessageResponse": { + "SignMessage.recid": 2, + "SignMessage.signature": 1, + "SignMessage.zbase": 3 + }, "SignpsbtRequest": { "SignPsbt.psbt": 1, "SignPsbt.signonly[]": 2 @@ -766,6 +947,7 @@ "TxPrepare.feerate": 2, "TxPrepare.minconf": 3, "TxPrepare.outptus[]": 1, + "TxPrepare.outputs[]": 5, "TxPrepare.utxos[]": 4 }, "TxprepareResponse": { @@ -782,10 +964,12 @@ "TxSend.txid": 3 }, "UtxopsbtRequest": { + "UtxoPsbt.excess_as_change": 9, "UtxoPsbt.feerate": 2, "UtxoPsbt.locktime": 6, "UtxoPsbt.min_witness_weight": 7, "UtxoPsbt.reserve": 5, + "UtxoPsbt.reservedok": 8, "UtxoPsbt.satoshi": 1, "UtxoPsbt.startweight": 3, "UtxoPsbt.utxos[]": 4 @@ -841,6 +1025,7 @@ "WaitInvoice.status": 4 }, "WaitsendpayRequest": { + "WaitSendPay.groupid": 4, "WaitSendPay.partid": 2, "WaitSendPay.payment_hash": 1, "WaitSendPay.timeout": 3 diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index cfd390c23984..508acb24b9b4 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -45,6 +45,13 @@ service Node { rpc TxDiscard(TxdiscardRequest) returns (TxdiscardResponse) {} rpc TxPrepare(TxprepareRequest) returns (TxprepareResponse) {} rpc TxSend(TxsendRequest) returns (TxsendResponse) {} + rpc Disconnect(DisconnectRequest) returns (DisconnectResponse) {} + rpc Feerates(FeeratesRequest) returns (FeeratesResponse) {} + rpc GetRoute(GetrouteRequest) returns (GetrouteResponse) {} + rpc ListForwards(ListforwardsRequest) returns (ListforwardsResponse) {} + rpc ListPays(ListpaysRequest) returns (ListpaysResponse) {} + rpc Ping(PingRequest) returns (PingResponse) {} + rpc SignMessage(SignmessageRequest) returns (SignmessageResponse) {} } message GetinfoRequest { @@ -174,7 +181,6 @@ message ListpeersPeersChannels { optional bytes close_to = 14; optional bool private = 15; ChannelSide opener = 16; - optional ChannelSide closer = 17; repeated string features = 18; optional Amount to_us_msat = 20; optional Amount min_to_us_msat = 21; @@ -189,6 +195,8 @@ message ListpeersPeersChannels { optional Amount spendable_msat = 30; optional Amount receivable_msat = 31; optional Amount minimum_htlc_in_msat = 32; + optional Amount minimum_htlc_out_msat = 48; + optional Amount maximum_htlc_out_msat = 49; optional uint32 their_to_self_delay = 33; optional uint32 our_to_self_delay = 34; optional uint32 max_accepted_htlcs = 35; @@ -231,19 +239,6 @@ message ListpeersPeersChannelsHtlcs { IN = 0; OUT = 1; } - // ListPeers.peers[].channels[].htlcs[].state - enum ListpeersPeersChannelsHtlcsState { - SENT_ADD_HTLC = 0; - SENT_ADD_COMMIT = 1; - RCVD_ADD_REVOCATION = 2; - RCVD_ADD_ACK_COMMIT = 3; - SENT_ADD_ACK_REVOCATION = 4; - RCVD_REMOVE_HTLC = 5; - RCVD_REMOVE_COMMIT = 6; - SENT_REMOVE_REVOCATION = 7; - SENT_REMOVE_ACK_COMMIT = 8; - RCVD_REMOVE_ACK_REVOCATION = 9; - } ListpeersPeersChannelsHtlcsDirection direction = 1; uint64 id = 2; Amount amount_msat = 3; @@ -251,7 +246,6 @@ message ListpeersPeersChannelsHtlcs { bytes payment_hash = 5; optional bool local_trimmed = 6; optional string status = 7; - ListpeersPeersChannelsHtlcsState state = 8; } message ListfundsRequest { @@ -299,6 +293,8 @@ message SendpayRequest { optional string bolt11 = 5; optional bytes payment_secret = 6; optional uint32 partid = 7; + optional bytes localofferid = 8; + optional uint64 groupid = 9; } message SendpayResponse { @@ -388,12 +384,13 @@ message CheckmessageResponse { } message CloseRequest { - bytes id = 1; + string id = 1; optional uint32 unilateraltimeout = 2; optional string destination = 3; optional string fee_negotiation_step = 4; optional bytes wrong_funding = 5; optional bool force_lease_closed = 6; + repeated Feerate feerange = 7; } message CloseResponse { @@ -409,7 +406,7 @@ message CloseResponse { } message ConnectRequest { - bytes id = 1; + string id = 1; optional string host = 2; optional uint32 port = 3; } @@ -478,14 +475,15 @@ message DatastoreRequest { MUST_APPEND = 3; CREATE_OR_APPEND = 4; } - repeated string key = 1; + repeated string key = 5; + optional string string = 6; optional bytes hex = 2; optional DatastoreMode mode = 3; optional uint64 generation = 4; } message DatastoreResponse { - repeated string key = 1; + repeated string key = 5; optional uint64 generation = 2; optional bytes hex = 3; optional string string = 4; @@ -509,19 +507,19 @@ message CreateonionHops { } message DeldatastoreRequest { - repeated string key = 1; + repeated string key = 3; optional uint64 generation = 2; } message DeldatastoreResponse { - repeated string key = 1; + repeated string key = 5; optional uint64 generation = 2; optional bytes hex = 3; optional string string = 4; } message DelexpiredinvoiceRequest { - uint32 maxexpirytime = 1; + optional uint64 maxexpirytime = 1; } message DelexpiredinvoiceResponse { @@ -536,6 +534,7 @@ message DelinvoiceRequest { } string label = 1; DelinvoiceStatus status = 2; + optional bool desconly = 3; } message DelinvoiceResponse { @@ -561,9 +560,12 @@ message InvoiceRequest { AmountOrAny msatoshi = 1; string description = 2; string label = 3; + optional uint64 expiry = 7; repeated string fallbacks = 4; optional bytes preimage = 5; + optional bool exposeprivatechannels = 8; optional uint32 cltv = 6; + optional bool deschashonly = 9; } message InvoiceResponse { @@ -579,7 +581,7 @@ message InvoiceResponse { } message ListdatastoreRequest { - repeated string key = 1; + repeated string key = 2; } message ListdatastoreResponse { @@ -612,7 +614,7 @@ message ListinvoicesInvoices { EXPIRED = 2; } string label = 1; - string description = 2; + optional string description = 2; bytes payment_hash = 3; ListinvoicesInvoicesStatus status = 4; uint64 expires_at = 5; @@ -629,6 +631,15 @@ message ListinvoicesInvoices { message SendonionRequest { bytes onion = 1; + bytes payment_hash = 3; + optional string label = 4; + repeated bytes shared_secrets = 5; + optional uint32 partid = 6; + optional string bolt11 = 7; + optional Amount msatoshi = 8; + optional bytes destination = 9; + optional bytes localofferid = 10; + optional uint64 groupid = 11; } message SendonionResponse { @@ -647,6 +658,7 @@ message SendonionResponse { optional string label = 8; optional string bolt11 = 9; optional string bolt12 = 10; + optional uint64 partid = 13; optional bytes payment_preimage = 11; optional string message = 12; } @@ -762,11 +774,13 @@ message PayRequest { string bolt11 = 1; optional Amount msatoshi = 2; optional string label = 3; - optional float riskfactor = 8; - optional float maxfeepercent = 4; + optional double riskfactor = 8; + optional double maxfeepercent = 4; optional uint32 retry_for = 5; optional uint32 maxdelay = 6; - optional float exemptfee = 7; + optional Amount exemptfee = 7; + optional bytes localofferid = 9; + repeated string exclude = 10; } message PayResponse { @@ -779,7 +793,7 @@ message PayResponse { bytes payment_preimage = 1; optional bytes destination = 2; bytes payment_hash = 3; - sint64 created_at = 4; + double created_at = 4; uint32 parts = 5; Amount amount_msat = 6; Amount amount_sent_msat = 7; @@ -820,8 +834,8 @@ message ListnodesNodesAddresses { } message WaitanyinvoiceRequest { - optional sint64 lastpay_index = 1; - optional sint64 timeout = 2; + optional uint64 lastpay_index = 1; + optional uint64 timeout = 2; } message WaitanyinvoiceResponse { @@ -870,8 +884,9 @@ message WaitinvoiceResponse { message WaitsendpayRequest { bytes payment_hash = 1; - optional uint32 partid = 2; optional uint32 timeout = 3; + optional uint64 partid = 2; + optional uint64 groupid = 4; } message WaitsendpayResponse { @@ -899,6 +914,7 @@ message NewaddrRequest { enum NewaddrAddresstype { BECH32 = 0; P2SH_SEGWIT = 1; + ALL = 2; } optional NewaddrAddresstype addresstype = 1; } @@ -909,11 +925,11 @@ message NewaddrResponse { } message WithdrawRequest { - bytes destination = 1; + string destination = 1; optional AmountOrAll satoshi = 2; optional Feerate feerate = 5; optional uint32 minconf = 3; - repeated Utxo utxos = 4; + repeated Outpoint utxos = 4; } message WithdrawResponse { @@ -926,10 +942,11 @@ message KeysendRequest { bytes destination = 1; Amount msatoshi = 2; optional string label = 3; - optional float maxfeepercent = 4; - optional sint64 retry_for = 5; - optional sint64 maxdelay = 6; + optional double maxfeepercent = 4; + optional uint32 retry_for = 5; + optional uint32 maxdelay = 6; optional Amount exemptfee = 7; + optional RoutehintList routehints = 8; } message KeysendResponse { @@ -940,7 +957,7 @@ message KeysendResponse { bytes payment_preimage = 1; optional bytes destination = 2; bytes payment_hash = 3; - sint64 created_at = 4; + double created_at = 4; uint32 parts = 5; Amount amount_msat = 6; Amount amount_sent_msat = 7; @@ -948,14 +965,18 @@ message KeysendResponse { KeysendStatus status = 9; } +message KeysendExtratlvs { +} + message FundpsbtRequest { Amount satoshi = 1; Feerate feerate = 2; - sint64 startweight = 3; - optional sint64 minconf = 4; - optional sint64 reserve = 5; - optional sint64 locktime = 6; + uint32 startweight = 3; + optional uint32 minconf = 4; + optional uint32 reserve = 5; + optional uint32 locktime = 6; optional uint32 min_witness_weight = 7; + optional bool excess_as_change = 8; } message FundpsbtResponse { @@ -977,6 +998,7 @@ message FundpsbtReservations { message SendpsbtRequest { string psbt = 1; + optional bool reserve = 2; } message SendpsbtResponse { @@ -986,6 +1008,7 @@ message SendpsbtResponse { message SignpsbtRequest { string psbt = 1; + repeated uint32 signonly = 2; } message SignpsbtResponse { @@ -995,11 +1018,13 @@ message SignpsbtResponse { message UtxopsbtRequest { Amount satoshi = 1; Feerate feerate = 2; - sint64 startweight = 3; - repeated Utxo utxos = 4; - optional sint64 reserve = 5; - optional sint64 locktime = 6; + uint32 startweight = 3; + repeated Outpoint utxos = 4; + optional uint32 reserve = 5; + optional bool reservedok = 8; + optional uint32 locktime = 6; optional uint32 min_witness_weight = 7; + optional bool excess_as_change = 9; } message UtxopsbtResponse { @@ -1029,10 +1054,10 @@ message TxdiscardResponse { } message TxprepareRequest { - repeated OutputDesc outptus = 1; + repeated OutputDesc outputs = 5; optional Feerate feerate = 2; optional uint32 minconf = 3; - repeated Utxo utxos = 4; + repeated Outpoint utxos = 4; } message TxprepareResponse { @@ -1050,3 +1075,178 @@ message TxsendResponse { bytes tx = 2; bytes txid = 3; } + +message DisconnectRequest { + bytes id = 1; + optional bool force = 2; +} + +message DisconnectResponse { +} + +message FeeratesRequest { + // Feerates.style + enum FeeratesStyle { + PERKB = 0; + PERKW = 1; + } + FeeratesStyle style = 1; +} + +message FeeratesResponse { + optional string warning_missing_feerates = 1; +} + +message FeeratesPerkb { + uint32 min_acceptable = 1; + uint32 max_acceptable = 2; + optional uint32 opening = 3; + optional uint32 mutual_close = 4; + optional uint32 unilateral_close = 5; + optional uint32 delayed_to_us = 6; + optional uint32 htlc_resolution = 7; + optional uint32 penalty = 8; +} + +message FeeratesPerkw { + uint32 min_acceptable = 1; + uint32 max_acceptable = 2; + optional uint32 opening = 3; + optional uint32 mutual_close = 4; + optional uint32 unilateral_close = 5; + optional uint32 delayed_to_us = 6; + optional uint32 htlc_resolution = 7; + optional uint32 penalty = 8; +} + +message FeeratesOnchain_fee_estimates { + uint64 opening_channel_satoshis = 1; + uint64 mutual_close_satoshis = 2; + uint64 unilateral_close_satoshis = 3; + uint64 htlc_timeout_satoshis = 4; + uint64 htlc_success_satoshis = 5; +} + +message GetrouteRequest { + bytes id = 1; + Amount msatoshi = 2; + uint64 riskfactor = 3; + optional double cltv = 4; + optional bytes fromid = 5; + optional uint32 fuzzpercent = 6; + repeated string exclude = 7; + optional uint32 maxhops = 8; +} + +message GetrouteResponse { + repeated GetrouteRoute route = 1; +} + +message GetrouteRoute { + // GetRoute.route[].style + enum GetrouteRouteStyle { + TLV = 0; + } + bytes id = 1; + string channel = 2; + uint32 direction = 3; + Amount amount_msat = 4; + uint32 delay = 5; + GetrouteRouteStyle style = 6; +} + +message ListforwardsRequest { + // ListForwards.status + enum ListforwardsStatus { + OFFERED = 0; + SETTLED = 1; + LOCAL_FAILED = 2; + FAILED = 3; + } + optional ListforwardsStatus status = 1; + optional string in_channel = 2; + optional string out_channel = 3; +} + +message ListforwardsResponse { + repeated ListforwardsForwards forwards = 1; +} + +message ListforwardsForwards { + // ListForwards.forwards[].status + enum ListforwardsForwardsStatus { + OFFERED = 0; + SETTLED = 1; + LOCAL_FAILED = 2; + FAILED = 3; + } + // ListForwards.forwards[].style + enum ListforwardsForwardsStyle { + LEGACY = 0; + TLV = 1; + } + string in_channel = 1; + Amount in_msat = 2; + ListforwardsForwardsStatus status = 3; + double received_time = 4; + optional string out_channel = 5; + optional bytes payment_hash = 6; + optional ListforwardsForwardsStyle style = 9; + optional Amount fee_msat = 7; + optional Amount out_msat = 8; +} + +message ListpaysRequest { + // ListPays.status + enum ListpaysStatus { + PENDING = 0; + COMPLETE = 1; + FAILED = 2; + } + optional string bolt11 = 1; + optional bytes payment_hash = 2; + optional ListpaysStatus status = 3; +} + +message ListpaysResponse { + repeated ListpaysPays pays = 1; +} + +message ListpaysPays { + // ListPays.pays[].status + enum ListpaysPaysStatus { + PENDING = 0; + FAILED = 1; + COMPLETE = 2; + } + bytes payment_hash = 1; + ListpaysPaysStatus status = 2; + optional bytes destination = 3; + uint64 created_at = 4; + optional string label = 5; + optional string bolt11 = 6; + optional string bolt12 = 7; + optional Amount amount_msat = 8; + optional Amount amount_sent_msat = 9; + optional bytes erroronion = 10; +} + +message PingRequest { + bytes id = 1; + optional double len = 2; + optional double pongbytes = 3; +} + +message PingResponse { + uint32 totlen = 1; +} + +message SignmessageRequest { + string message = 1; +} + +message SignmessageResponse { + bytes signature = 1; + bytes recid = 2; + string zbase = 3; +} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index cb7d8d54bc5f..c09d559332a8 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -7,6 +7,7 @@ use std::convert::From; #[allow(unused_imports)] use cln_rpc::model::{responses,requests}; use crate::pb; +use std::str::FromStr; #[allow(unused_variables)] impl From<&responses::GetinfoAddress> for pb::GetinfoAddress { @@ -35,7 +36,7 @@ impl From<&responses::GetinfoBinding> for pb::GetinfoBinding { impl From<&responses::GetinfoResponse> for pb::GetinfoResponse { fn from(c: &responses::GetinfoResponse) -> Self { Self { - id: hex::decode(&c.id).unwrap(), // Rule #2 for type pubkey + id: c.id.to_vec(), // Rule #2 for type pubkey alias: c.alias.clone(), // Rule #2 for type string color: hex::decode(&c.color).unwrap(), // Rule #2 for type hex num_peers: c.num_peers.clone(), // Rule #2 for type u32 @@ -47,8 +48,8 @@ impl From<&responses::GetinfoResponse> for pb::GetinfoResponse { blockheight: c.blockheight.clone(), // Rule #2 for type u32 network: c.network.clone(), // Rule #2 for type string fees_collected_msat: Some(c.fees_collected_msat.into()), // Rule #2 for type msat - address: c.address.iter().map(|i| i.into()).collect(), - binding: c.binding.iter().map(|i| i.into()).collect(), + address: c.address.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + binding: c.binding.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 warning_bitcoind_sync: c.warning_bitcoind_sync.clone(), // Rule #2 for type string? warning_lightningd_sync: c.warning_lightningd_sync.clone(), // Rule #2 for type string? } @@ -64,7 +65,7 @@ impl From<&responses::ListpeersPeersLog> for pb::ListpeersPeersLog { time: c.time.clone(), // Rule #2 for type string? source: c.source.clone(), // Rule #2 for type string? log: c.log.clone(), // Rule #2 for type string? - node_id: c.node_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + node_id: c.node_id.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? data: c.data.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } @@ -92,10 +93,9 @@ impl From<&responses::ListpeersPeersChannelsHtlcs> for pb::ListpeersPeersChannel id: c.id.clone(), // Rule #2 for type u64 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat expiry: c.expiry.clone(), // Rule #2 for type u32 - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash local_trimmed: c.local_trimmed.clone(), // Rule #2 for type boolean? status: c.status.clone(), // Rule #2 for type string? - state: c.state as i32, } } } @@ -107,20 +107,19 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { state: c.state as i32, scratch_txid: c.scratch_txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? owner: c.owner.clone(), // Rule #2 for type string? - short_channel_id: c.short_channel_id.clone(), // Rule #2 for type short_channel_id? - channel_id: c.channel_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + short_channel_id: c.short_channel_id.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? + channel_id: c.channel_id.clone().map(|v| v.to_vec()), // Rule #2 for type hash? funding_txid: c.funding_txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? funding_outnum: c.funding_outnum.clone(), // Rule #2 for type u32? initial_feerate: c.initial_feerate.clone(), // Rule #2 for type string? last_feerate: c.last_feerate.clone(), // Rule #2 for type string? next_feerate: c.next_feerate.clone(), // Rule #2 for type string? next_fee_step: c.next_fee_step.clone(), // Rule #2 for type u32? - inflight: c.inflight.iter().map(|i| i.into()).collect(), + inflight: c.inflight.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? private: c.private.clone(), // Rule #2 for type boolean? opener: c.opener as i32, - closer: c.closer.map(|v| v as i32), - features: c.features.iter().map(|i| i.into()).collect(), + features: c.features.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannelsFeatures to_us_msat: c.to_us_msat.map(|f| f.into()), // Rule #2 for type msat? min_to_us_msat: c.min_to_us_msat.map(|f| f.into()), // Rule #2 for type msat? max_to_us_msat: c.max_to_us_msat.map(|f| f.into()), // Rule #2 for type msat? @@ -134,10 +133,12 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { spendable_msat: c.spendable_msat.map(|f| f.into()), // Rule #2 for type msat? receivable_msat: c.receivable_msat.map(|f| f.into()), // Rule #2 for type msat? minimum_htlc_in_msat: c.minimum_htlc_in_msat.map(|f| f.into()), // Rule #2 for type msat? + minimum_htlc_out_msat: c.minimum_htlc_out_msat.map(|f| f.into()), // Rule #2 for type msat? + maximum_htlc_out_msat: c.maximum_htlc_out_msat.map(|f| f.into()), // Rule #2 for type msat? their_to_self_delay: c.their_to_self_delay.clone(), // Rule #2 for type u32? our_to_self_delay: c.our_to_self_delay.clone(), // Rule #2 for type u32? max_accepted_htlcs: c.max_accepted_htlcs.clone(), // Rule #2 for type u32? - status: c.status.iter().map(|i| i.into()).collect(), + status: c.status.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 in_payments_offered: c.in_payments_offered.clone(), // Rule #2 for type u64? in_offered_msat: c.in_offered_msat.map(|f| f.into()), // Rule #2 for type msat? in_payments_fulfilled: c.in_payments_fulfilled.clone(), // Rule #2 for type u64? @@ -146,7 +147,7 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { out_offered_msat: c.out_offered_msat.map(|f| f.into()), // Rule #2 for type msat? out_payments_fulfilled: c.out_payments_fulfilled.clone(), // Rule #2 for type u64? out_fulfilled_msat: c.out_fulfilled_msat.map(|f| f.into()), // Rule #2 for type msat? - htlcs: c.htlcs.iter().map(|i| i.into()).collect(), + htlcs: c.htlcs.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 close_to_addr: c.close_to_addr.clone(), // Rule #2 for type string? } } @@ -156,11 +157,11 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { fn from(c: &responses::ListpeersPeers) -> Self { Self { - id: hex::decode(&c.id).unwrap(), // Rule #2 for type pubkey + id: c.id.to_vec(), // Rule #2 for type pubkey connected: c.connected.clone(), // Rule #2 for type boolean - log: c.log.iter().map(|i| i.into()).collect(), - channels: c.channels.iter().map(|i| i.into()).collect(), - netaddr: c.netaddr.iter().map(|i| i.into()).collect(), + log: c.log.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannels + netaddr: c.netaddr.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } @@ -170,7 +171,7 @@ impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { impl From<&responses::ListpeersResponse> for pb::ListpeersResponse { fn from(c: &responses::ListpeersResponse) -> Self { Self { - peers: c.peers.iter().map(|i| i.into()).collect(), + peers: c.peers.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeers } } } @@ -195,14 +196,14 @@ impl From<&responses::ListfundsOutputs> for pb::ListfundsOutputs { impl From<&responses::ListfundsChannels> for pb::ListfundsChannels { fn from(c: &responses::ListfundsChannels) -> Self { Self { - peer_id: hex::decode(&c.peer_id).unwrap(), // Rule #2 for type pubkey + peer_id: c.peer_id.to_vec(), // Rule #2 for type pubkey our_amount_msat: Some(c.our_amount_msat.into()), // Rule #2 for type msat amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat funding_txid: hex::decode(&c.funding_txid).unwrap(), // Rule #2 for type txid funding_output: c.funding_output.clone(), // Rule #2 for type u32 connected: c.connected.clone(), // Rule #2 for type boolean state: c.state as i32, - short_channel_id: c.short_channel_id.clone(), // Rule #2 for type short_channel_id? + short_channel_id: c.short_channel_id.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } @@ -211,8 +212,8 @@ impl From<&responses::ListfundsChannels> for pb::ListfundsChannels { impl From<&responses::ListfundsResponse> for pb::ListfundsResponse { fn from(c: &responses::ListfundsResponse) -> Self { Self { - outputs: c.outputs.iter().map(|i| i.into()).collect(), - channels: c.channels.iter().map(|i| i.into()).collect(), + outputs: c.outputs.iter().map(|i| i.into()).collect(), // Rule #3 for type ListfundsOutputs + channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type ListfundsChannels } } } @@ -223,17 +224,17 @@ impl From<&responses::SendpayResponse> for pb::SendpayResponse { Self { id: c.id.clone(), // Rule #2 for type u64 groupid: c.groupid.clone(), // Rule #2 for type u64? - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? created_at: c.created_at.clone(), // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label.clone(), // Rule #2 for type string? partid: c.partid.clone(), // Rule #2 for type u64? bolt11: c.bolt11.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? message: c.message.clone(), // Rule #2 for type string? } } @@ -243,9 +244,9 @@ impl From<&responses::SendpayResponse> for pb::SendpayResponse { impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { fn from(c: &responses::ListchannelsChannels) -> Self { Self { - source: hex::decode(&c.source).unwrap(), // Rule #2 for type pubkey - destination: hex::decode(&c.destination).unwrap(), // Rule #2 for type pubkey - short_channel_id: c.short_channel_id.clone(), // Rule #2 for type short_channel_id + source: c.source.to_vec(), // Rule #2 for type pubkey + destination: c.destination.to_vec(), // Rule #2 for type pubkey + short_channel_id: c.short_channel_id.to_string(), // Rule #2 for type short_channel_id public: c.public.clone(), // Rule #2 for type boolean amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat message_flags: c.message_flags.into(), // Rule #2 for type u8 @@ -266,7 +267,7 @@ impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { impl From<&responses::ListchannelsResponse> for pb::ListchannelsResponse { fn from(c: &responses::ListchannelsResponse) -> Self { Self { - channels: c.channels.iter().map(|i| i.into()).collect(), + channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type ListchannelsChannels } } } @@ -295,7 +296,7 @@ impl From<&responses::CheckmessageResponse> for pb::CheckmessageResponse { fn from(c: &responses::CheckmessageResponse) -> Self { Self { verified: c.verified.clone(), // Rule #2 for type boolean - pubkey: c.pubkey.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + pubkey: c.pubkey.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? } } } @@ -315,7 +316,7 @@ impl From<&responses::CloseResponse> for pb::CloseResponse { impl From<&responses::ConnectResponse> for pb::ConnectResponse { fn from(c: &responses::ConnectResponse) -> Self { Self { - id: hex::decode(&c.id).unwrap(), // Rule #2 for type pubkey + id: c.id.to_vec(), // Rule #2 for type pubkey features: hex::decode(&c.features).unwrap(), // Rule #2 for type hex direction: c.direction as i32, } @@ -329,7 +330,7 @@ impl From<&responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse { label: c.label.clone(), // Rule #2 for type string bolt11: c.bolt11.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? status: c.status as i32, description: c.description.clone(), // Rule #2 for type string @@ -337,7 +338,7 @@ impl From<&responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse { pay_index: c.pay_index.clone(), // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? payer_note: c.payer_note.clone(), // Rule #2 for type string? } @@ -348,7 +349,7 @@ impl From<&responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse { impl From<&responses::DatastoreResponse> for pb::DatastoreResponse { fn from(c: &responses::DatastoreResponse) -> Self { Self { - key: c.key.iter().map(|i| i.into()).collect(), + key: c.key.iter().map(|i| i.into()).collect(), // Rule #3 for type string generation: c.generation.clone(), // Rule #2 for type u64? hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? string: c.string.clone(), // Rule #2 for type string? @@ -361,7 +362,7 @@ impl From<&responses::CreateonionResponse> for pb::CreateonionResponse { fn from(c: &responses::CreateonionResponse) -> Self { Self { onion: hex::decode(&c.onion).unwrap(), // Rule #2 for type hex - shared_secrets: c.shared_secrets.iter().map(|i| hex::decode(i).unwrap()).collect(), + shared_secrets: c.shared_secrets.iter().map(|i| i.clone().to_vec()).collect(), // Rule #3 for type secret } } } @@ -370,7 +371,7 @@ impl From<&responses::CreateonionResponse> for pb::CreateonionResponse { impl From<&responses::DeldatastoreResponse> for pb::DeldatastoreResponse { fn from(c: &responses::DeldatastoreResponse) -> Self { Self { - key: c.key.iter().map(|i| i.into()).collect(), + key: c.key.iter().map(|i| i.into()).collect(), // Rule #3 for type string generation: c.generation.clone(), // Rule #2 for type u64? hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? string: c.string.clone(), // Rule #2 for type string? @@ -395,7 +396,7 @@ impl From<&responses::DelinvoiceResponse> for pb::DelinvoiceResponse { bolt12: c.bolt12.clone(), // Rule #2 for type string? amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? description: c.description.clone(), // Rule #2 for type string? - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, expires_at: c.expires_at.clone(), // Rule #2 for type u64 local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? @@ -409,8 +410,8 @@ impl From<&responses::InvoiceResponse> for pb::InvoiceResponse { fn from(c: &responses::InvoiceResponse) -> Self { Self { bolt11: c.bolt11.clone(), // Rule #2 for type string - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex - payment_secret: hex::decode(&c.payment_secret).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + payment_secret: c.payment_secret.clone().to_vec(), // Rule #2 for type secret expires_at: c.expires_at.clone(), // Rule #2 for type u64 warning_capacity: c.warning_capacity.clone(), // Rule #2 for type string? warning_offline: c.warning_offline.clone(), // Rule #2 for type string? @@ -425,7 +426,7 @@ impl From<&responses::InvoiceResponse> for pb::InvoiceResponse { impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { fn from(c: &responses::ListdatastoreDatastore) -> Self { Self { - key: c.key.iter().map(|i| i.into()).collect(), + key: c.key.iter().map(|i| i.into()).collect(), // Rule #3 for type string generation: c.generation.clone(), // Rule #2 for type u64? hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? string: c.string.clone(), // Rule #2 for type string? @@ -437,7 +438,7 @@ impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { impl From<&responses::ListdatastoreResponse> for pb::ListdatastoreResponse { fn from(c: &responses::ListdatastoreResponse) -> Self { Self { - datastore: c.datastore.iter().map(|i| i.into()).collect(), + datastore: c.datastore.iter().map(|i| i.into()).collect(), // Rule #3 for type ListdatastoreDatastore } } } @@ -447,8 +448,8 @@ impl From<&responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices { fn from(c: &responses::ListinvoicesInvoices) -> Self { Self { label: c.label.clone(), // Rule #2 for type string - description: c.description.clone(), // Rule #2 for type string - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + description: c.description.clone(), // Rule #2 for type string? + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, expires_at: c.expires_at.clone(), // Rule #2 for type u64 amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? @@ -459,7 +460,7 @@ impl From<&responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices { pay_index: c.pay_index.clone(), // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? } } } @@ -468,7 +469,7 @@ impl From<&responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices { impl From<&responses::ListinvoicesResponse> for pb::ListinvoicesResponse { fn from(c: &responses::ListinvoicesResponse) -> Self { Self { - invoices: c.invoices.iter().map(|i| i.into()).collect(), + invoices: c.invoices.iter().map(|i| i.into()).collect(), // Rule #3 for type ListinvoicesInvoices } } } @@ -478,16 +479,17 @@ impl From<&responses::SendonionResponse> for pb::SendonionResponse { fn from(c: &responses::SendonionResponse) -> Self { Self { id: c.id.clone(), // Rule #2 for type u64 - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? created_at: c.created_at.clone(), // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label.clone(), // Rule #2 for type string? bolt11: c.bolt11.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + partid: c.partid.clone(), // Rule #2 for type u64? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? message: c.message.clone(), // Rule #2 for type string? } } @@ -499,16 +501,16 @@ impl From<&responses::ListsendpaysPayments> for pb::ListsendpaysPayments { Self { id: c.id.clone(), // Rule #2 for type u64 groupid: c.groupid.clone(), // Rule #2 for type u64? - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? created_at: c.created_at.clone(), // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label.clone(), // Rule #2 for type string? bolt11: c.bolt11.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? erroronion: c.erroronion.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } @@ -518,7 +520,7 @@ impl From<&responses::ListsendpaysPayments> for pb::ListsendpaysPayments { impl From<&responses::ListsendpaysResponse> for pb::ListsendpaysResponse { fn from(c: &responses::ListsendpaysResponse) -> Self { Self { - payments: c.payments.iter().map(|i| i.into()).collect(), + payments: c.payments.iter().map(|i| i.into()).collect(), // Rule #3 for type ListsendpaysPayments } } } @@ -531,7 +533,7 @@ impl From<&responses::ListtransactionsTransactionsInputs> for pb::Listtransactio index: c.index.clone(), // Rule #2 for type u32 sequence: c.sequence.clone(), // Rule #2 for type u32 item_type: c.item_type.map(|v| v as i32), - channel: c.channel.clone(), // Rule #2 for type short_channel_id? + channel: c.channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } @@ -544,7 +546,7 @@ impl From<&responses::ListtransactionsTransactionsOutputs> for pb::Listtransacti msat: Some(c.msat.into()), // Rule #2 for type msat script_pub_key: hex::decode(&c.script_pub_key).unwrap(), // Rule #2 for type hex item_type: c.item_type.map(|v| v as i32), - channel: c.channel.clone(), // Rule #2 for type short_channel_id? + channel: c.channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } @@ -557,11 +559,11 @@ impl From<&responses::ListtransactionsTransactions> for pb::ListtransactionsTran rawtx: hex::decode(&c.rawtx).unwrap(), // Rule #2 for type hex blockheight: c.blockheight.clone(), // Rule #2 for type u32 txindex: c.txindex.clone(), // Rule #2 for type u32 - channel: c.channel.clone(), // Rule #2 for type short_channel_id? + channel: c.channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? locktime: c.locktime.clone(), // Rule #2 for type u32 version: c.version.clone(), // Rule #2 for type u32 - inputs: c.inputs.iter().map(|i| i.into()).collect(), - outputs: c.outputs.iter().map(|i| i.into()).collect(), + inputs: c.inputs.iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactionsInputs + outputs: c.outputs.iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactionsOutputs } } } @@ -570,7 +572,7 @@ impl From<&responses::ListtransactionsTransactions> for pb::ListtransactionsTran impl From<&responses::ListtransactionsResponse> for pb::ListtransactionsResponse { fn from(c: &responses::ListtransactionsResponse) -> Self { Self { - transactions: c.transactions.iter().map(|i| i.into()).collect(), + transactions: c.transactions.iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactions } } } @@ -579,9 +581,9 @@ impl From<&responses::ListtransactionsResponse> for pb::ListtransactionsResponse impl From<&responses::PayResponse> for pb::PayResponse { fn from(c: &responses::PayResponse) -> Self { Self { - payment_preimage: hex::decode(&c.payment_preimage).unwrap(), // Rule #2 for type hex - destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_preimage: c.payment_preimage.clone().to_vec(), // Rule #2 for type secret + destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash created_at: c.created_at.clone(), // Rule #2 for type number parts: c.parts.clone(), // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat @@ -607,12 +609,12 @@ impl From<&responses::ListnodesNodesAddresses> for pb::ListnodesNodesAddresses { impl From<&responses::ListnodesNodes> for pb::ListnodesNodes { fn from(c: &responses::ListnodesNodes) -> Self { Self { - nodeid: hex::decode(&c.nodeid).unwrap(), // Rule #2 for type pubkey + nodeid: c.nodeid.to_vec(), // Rule #2 for type pubkey last_timestamp: c.last_timestamp.clone(), // Rule #2 for type u32? alias: c.alias.clone(), // Rule #2 for type string? color: c.color.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - addresses: c.addresses.iter().map(|i| i.into()).collect(), + addresses: c.addresses.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 } } } @@ -621,7 +623,7 @@ impl From<&responses::ListnodesNodes> for pb::ListnodesNodes { impl From<&responses::ListnodesResponse> for pb::ListnodesResponse { fn from(c: &responses::ListnodesResponse) -> Self { Self { - nodes: c.nodes.iter().map(|i| i.into()).collect(), + nodes: c.nodes.iter().map(|i| i.into()).collect(), // Rule #3 for type ListnodesNodes } } } @@ -632,7 +634,7 @@ impl From<&responses::WaitanyinvoiceResponse> for pb::WaitanyinvoiceResponse { Self { label: c.label.clone(), // Rule #2 for type string description: c.description.clone(), // Rule #2 for type string - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, expires_at: c.expires_at.clone(), // Rule #2 for type u64 amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? @@ -641,7 +643,7 @@ impl From<&responses::WaitanyinvoiceResponse> for pb::WaitanyinvoiceResponse { pay_index: c.pay_index.clone(), // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? } } } @@ -652,7 +654,7 @@ impl From<&responses::WaitinvoiceResponse> for pb::WaitinvoiceResponse { Self { label: c.label.clone(), // Rule #2 for type string description: c.description.clone(), // Rule #2 for type string - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, expires_at: c.expires_at.clone(), // Rule #2 for type u64 amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? @@ -661,7 +663,7 @@ impl From<&responses::WaitinvoiceResponse> for pb::WaitinvoiceResponse { pay_index: c.pay_index.clone(), // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? } } } @@ -672,17 +674,17 @@ impl From<&responses::WaitsendpayResponse> for pb::WaitsendpayResponse { Self { id: c.id.clone(), // Rule #2 for type u64 groupid: c.groupid.clone(), // Rule #2 for type u64? - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? + destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? created_at: c.created_at.clone(), // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label.clone(), // Rule #2 for type string? partid: c.partid.clone(), // Rule #2 for type u64? bolt11: c.bolt11.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_preimage: c.payment_preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? } } } @@ -712,9 +714,9 @@ impl From<&responses::WithdrawResponse> for pb::WithdrawResponse { impl From<&responses::KeysendResponse> for pb::KeysendResponse { fn from(c: &responses::KeysendResponse) -> Self { Self { - payment_preimage: hex::decode(&c.payment_preimage).unwrap(), // Rule #2 for type hex - destination: c.destination.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type pubkey? - payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + payment_preimage: c.payment_preimage.clone().to_vec(), // Rule #2 for type secret + destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? + payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash created_at: c.created_at.clone(), // Rule #2 for type number parts: c.parts.clone(), // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat @@ -747,7 +749,7 @@ impl From<&responses::FundpsbtResponse> for pb::FundpsbtResponse { estimated_final_weight: c.estimated_final_weight.clone(), // Rule #2 for type u32 excess_msat: Some(c.excess_msat.into()), // Rule #2 for type msat change_outnum: c.change_outnum.clone(), // Rule #2 for type u32? - reservations: c.reservations.iter().map(|i| i.into()).collect(), + reservations: c.reservations.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 } } } @@ -793,7 +795,7 @@ impl From<&responses::UtxopsbtResponse> for pb::UtxopsbtResponse { estimated_final_weight: c.estimated_final_weight.clone(), // Rule #2 for type u32 excess_msat: Some(c.excess_msat.into()), // Rule #2 for type msat change_outnum: c.change_outnum.clone(), // Rule #2 for type u32? - reservations: c.reservations.iter().map(|i| i.into()).collect(), + reservations: c.reservations.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 } } } @@ -830,6 +832,119 @@ impl From<&responses::TxsendResponse> for pb::TxsendResponse { } } +#[allow(unused_variables)] +impl From<&responses::DisconnectResponse> for pb::DisconnectResponse { + fn from(c: &responses::DisconnectResponse) -> Self { + Self { + } + } +} + +#[allow(unused_variables)] +impl From<&responses::FeeratesResponse> for pb::FeeratesResponse { + fn from(c: &responses::FeeratesResponse) -> Self { + Self { + warning_missing_feerates: c.warning_missing_feerates.clone(), // Rule #2 for type string? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::GetrouteRoute> for pb::GetrouteRoute { + fn from(c: &responses::GetrouteRoute) -> Self { + Self { + id: c.id.to_vec(), // Rule #2 for type pubkey + channel: c.channel.to_string(), // Rule #2 for type short_channel_id + direction: c.direction.clone(), // Rule #2 for type u32 + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat + delay: c.delay.clone(), // Rule #2 for type u32 + style: c.style as i32, + } + } +} + +#[allow(unused_variables)] +impl From<&responses::GetrouteResponse> for pb::GetrouteResponse { + fn from(c: &responses::GetrouteResponse) -> Self { + Self { + route: c.route.iter().map(|i| i.into()).collect(), // Rule #3 for type GetrouteRoute + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListforwardsForwards> for pb::ListforwardsForwards { + fn from(c: &responses::ListforwardsForwards) -> Self { + Self { + in_channel: c.in_channel.to_string(), // Rule #2 for type short_channel_id + in_msat: Some(c.in_msat.into()), // Rule #2 for type msat + status: c.status as i32, + received_time: c.received_time.clone(), // Rule #2 for type number + out_channel: c.out_channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? + payment_hash: c.payment_hash.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + style: c.style.map(|v| v as i32), + fee_msat: c.fee_msat.map(|f| f.into()), // Rule #2 for type msat? + out_msat: c.out_msat.map(|f| f.into()), // Rule #2 for type msat? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListforwardsResponse> for pb::ListforwardsResponse { + fn from(c: &responses::ListforwardsResponse) -> Self { + Self { + forwards: c.forwards.iter().map(|i| i.into()).collect(), // Rule #3 for type ListforwardsForwards + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListpaysPays> for pb::ListpaysPays { + fn from(c: &responses::ListpaysPays) -> Self { + Self { + payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex + status: c.status as i32, + destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? + created_at: c.created_at.clone(), // Rule #2 for type u64 + label: c.label.clone(), // Rule #2 for type string? + bolt11: c.bolt11.clone(), // Rule #2 for type string? + bolt12: c.bolt12.clone(), // Rule #2 for type string? + amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? + amount_sent_msat: c.amount_sent_msat.map(|f| f.into()), // Rule #2 for type msat? + erroronion: c.erroronion.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::ListpaysResponse> for pb::ListpaysResponse { + fn from(c: &responses::ListpaysResponse) -> Self { + Self { + pays: c.pays.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpaysPays + } + } +} + +#[allow(unused_variables)] +impl From<&responses::PingResponse> for pb::PingResponse { + fn from(c: &responses::PingResponse) -> Self { + Self { + totlen: c.totlen.into(), // Rule #2 for type u16 + } + } +} + +#[allow(unused_variables)] +impl From<&responses::SignmessageResponse> for pb::SignmessageResponse { + fn from(c: &responses::SignmessageResponse) -> Self { + Self { + signature: hex::decode(&c.signature).unwrap(), // Rule #2 for type hex + recid: hex::decode(&c.recid).unwrap(), // Rule #2 for type hex + zbase: c.zbase.clone(), // Rule #2 for type string + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -842,7 +957,7 @@ impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { impl From<&pb::ListpeersRequest> for requests::ListpeersRequest { fn from(c: &pb::ListpeersRequest) -> Self { Self { - id: c.id.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? + id: c.id.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? level: c.level.clone(), // Rule #1 for type string? } } @@ -862,9 +977,9 @@ impl From<&pb::SendpayRoute> for requests::SendpayRoute { fn from(c: &pb::SendpayRoute) -> Self { Self { msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat - id: hex::encode(&c.id), // Rule #1 for type pubkey + id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey delay: c.delay as u16, // Rule #1 for type u16 - channel: c.channel.clone(), // Rule #1 for type short_channel_id + channel: cln_rpc::primitives::ShortChannelId::from_str(&c.channel).unwrap(), // Rule #1 for type short_channel_id } } } @@ -873,13 +988,15 @@ impl From<&pb::SendpayRoute> for requests::SendpayRoute { impl From<&pb::SendpayRequest> for requests::SendpayRequest { fn from(c: &pb::SendpayRequest) -> Self { Self { - route: c.route.iter().map(|s| s.into()).collect(), - payment_hash: hex::encode(&c.payment_hash), // Rule #1 for type hex + route: c.route.iter().map(|s| s.into()).collect(), // Rule #4 + payment_hash: c.payment_hash.clone().try_into().unwrap(), // Rule #1 for type hash label: c.label.clone(), // Rule #1 for type string? msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? bolt11: c.bolt11.clone(), // Rule #1 for type string? - payment_secret: c.payment_secret.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + payment_secret: c.payment_secret.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type secret? partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? + localofferid: c.localofferid.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + groupid: c.groupid.clone(), // Rule #1 for type u64? } } } @@ -888,9 +1005,9 @@ impl From<&pb::SendpayRequest> for requests::SendpayRequest { impl From<&pb::ListchannelsRequest> for requests::ListchannelsRequest { fn from(c: &pb::ListchannelsRequest) -> Self { Self { - short_channel_id: c.short_channel_id.clone(), // Rule #1 for type short_channel_id? - source: c.source.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? - destination: c.destination.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? + short_channel_id: c.short_channel_id.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? + source: c.source.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? + destination: c.destination.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? } } } @@ -920,7 +1037,7 @@ impl From<&pb::CheckmessageRequest> for requests::CheckmessageRequest { Self { message: c.message.clone(), // Rule #1 for type string zbase: c.zbase.clone(), // Rule #1 for type string - pubkey: c.pubkey.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? + pubkey: c.pubkey.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? } } } @@ -929,12 +1046,13 @@ impl From<&pb::CheckmessageRequest> for requests::CheckmessageRequest { impl From<&pb::CloseRequest> for requests::CloseRequest { fn from(c: &pb::CloseRequest) -> Self { Self { - id: hex::encode(&c.id), // Rule #1 for type pubkey + id: c.id.clone(), // Rule #1 for type string unilateraltimeout: c.unilateraltimeout.clone(), // Rule #1 for type u32? destination: c.destination.clone(), // Rule #1 for type string? fee_negotiation_step: c.fee_negotiation_step.clone(), // Rule #1 for type string? wrong_funding: c.wrong_funding.clone().map(|v| hex::encode(v)), // Rule #1 for type txid? force_lease_closed: c.force_lease_closed.clone(), // Rule #1 for type boolean? + feerange: Some(c.feerange.iter().map(|s| s.into()).collect()), // Rule #4 } } } @@ -943,7 +1061,7 @@ impl From<&pb::CloseRequest> for requests::CloseRequest { impl From<&pb::ConnectRequest> for requests::ConnectRequest { fn from(c: &pb::ConnectRequest) -> Self { Self { - id: hex::encode(&c.id), // Rule #1 for type pubkey + id: c.id.clone(), // Rule #1 for type string host: c.host.clone(), // Rule #1 for type string? port: c.port.map(|v| v as u16), // Rule #1 for type u16? } @@ -965,7 +1083,8 @@ impl From<&pb::CreateinvoiceRequest> for requests::CreateinvoiceRequest { impl From<&pb::DatastoreRequest> for requests::DatastoreRequest { fn from(c: &pb::DatastoreRequest) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), + key: c.key.iter().map(|s| s.into()).collect(), // Rule #4 + string: c.string.clone(), // Rule #1 for type string? hex: c.hex.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? mode: c.mode.map(|v| v.try_into().unwrap()), generation: c.generation.clone(), // Rule #1 for type u64? @@ -977,7 +1096,7 @@ impl From<&pb::DatastoreRequest> for requests::DatastoreRequest { impl From<&pb::CreateonionHops> for requests::CreateonionHops { fn from(c: &pb::CreateonionHops) -> Self { Self { - pubkey: hex::encode(&c.pubkey), // Rule #1 for type pubkey + pubkey: cln_rpc::primitives::Pubkey::from_slice(&c.pubkey).unwrap(), // Rule #1 for type pubkey payload: hex::encode(&c.payload), // Rule #1 for type hex } } @@ -987,9 +1106,9 @@ impl From<&pb::CreateonionHops> for requests::CreateonionHops { impl From<&pb::CreateonionRequest> for requests::CreateonionRequest { fn from(c: &pb::CreateonionRequest) -> Self { Self { - hops: c.hops.iter().map(|s| s.into()).collect(), + hops: c.hops.iter().map(|s| s.into()).collect(), // Rule #4 assocdata: hex::encode(&c.assocdata), // Rule #1 for type hex - session_key: c.session_key.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + session_key: c.session_key.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type secret? onion_size: c.onion_size.map(|v| v as u16), // Rule #1 for type u16? } } @@ -999,7 +1118,7 @@ impl From<&pb::CreateonionRequest> for requests::CreateonionRequest { impl From<&pb::DeldatastoreRequest> for requests::DeldatastoreRequest { fn from(c: &pb::DeldatastoreRequest) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), + key: c.key.iter().map(|s| s.into()).collect(), // Rule #4 generation: c.generation.clone(), // Rule #1 for type u64? } } @@ -1009,7 +1128,7 @@ impl From<&pb::DeldatastoreRequest> for requests::DeldatastoreRequest { impl From<&pb::DelexpiredinvoiceRequest> for requests::DelexpiredinvoiceRequest { fn from(c: &pb::DelexpiredinvoiceRequest) -> Self { Self { - maxexpirytime: c.maxexpirytime.clone(), // Rule #1 for type u32 + maxexpirytime: c.maxexpirytime.clone(), // Rule #1 for type u64? } } } @@ -1020,6 +1139,7 @@ impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { Self { label: c.label.clone(), // Rule #1 for type string status: c.status.try_into().unwrap(), + desconly: c.desconly.clone(), // Rule #1 for type boolean? } } } @@ -1028,12 +1148,15 @@ impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { impl From<&pb::InvoiceRequest> for requests::InvoiceRequest { fn from(c: &pb::InvoiceRequest) -> Self { Self { - msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat|any + msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat_or_any description: c.description.clone(), // Rule #1 for type string label: c.label.clone(), // Rule #1 for type string - fallbacks: c.fallbacks.iter().map(|s| s.into()).collect(), + expiry: c.expiry.clone(), // Rule #1 for type u64? + fallbacks: Some(c.fallbacks.iter().map(|s| s.into()).collect()), // Rule #4 preimage: c.preimage.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + exposeprivatechannels: c.exposeprivatechannels.clone(), // Rule #1 for type boolean? cltv: c.cltv.clone(), // Rule #1 for type u32? + deschashonly: c.deschashonly.clone(), // Rule #1 for type boolean? } } } @@ -1042,7 +1165,7 @@ impl From<&pb::InvoiceRequest> for requests::InvoiceRequest { impl From<&pb::ListdatastoreRequest> for requests::ListdatastoreRequest { fn from(c: &pb::ListdatastoreRequest) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), + key: Some(c.key.iter().map(|s| s.into()).collect()), // Rule #4 } } } @@ -1064,6 +1187,15 @@ impl From<&pb::SendonionRequest> for requests::SendonionRequest { fn from(c: &pb::SendonionRequest) -> Self { Self { onion: hex::encode(&c.onion), // Rule #1 for type hex + payment_hash: c.payment_hash.clone().try_into().unwrap(), // Rule #1 for type hash + label: c.label.clone(), // Rule #1 for type string? + shared_secrets: Some(c.shared_secrets.iter().map(|s| s.clone().try_into().unwrap()).collect()), // Rule #4 + partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? + bolt11: c.bolt11.clone(), // Rule #1 for type string? + msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + destination: c.destination.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? + localofferid: c.localofferid.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + groupid: c.groupid.clone(), // Rule #1 for type u64? } } } @@ -1073,7 +1205,7 @@ impl From<&pb::ListsendpaysRequest> for requests::ListsendpaysRequest { fn from(c: &pb::ListsendpaysRequest) -> Self { Self { bolt11: c.bolt11.clone(), // Rule #1 for type string? - payment_hash: c.payment_hash.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + payment_hash: c.payment_hash.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type hash? status: c.status.map(|v| v.try_into().unwrap()), } } @@ -1094,11 +1226,13 @@ impl From<&pb::PayRequest> for requests::PayRequest { bolt11: c.bolt11.clone(), // Rule #1 for type string msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? label: c.label.clone(), // Rule #1 for type string? - riskfactor: c.riskfactor.clone(), // Rule #1 for type float? - maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type f32? + riskfactor: c.riskfactor.clone(), // Rule #1 for type number? + maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type number? retry_for: c.retry_for.map(|v| v as u16), // Rule #1 for type u16? maxdelay: c.maxdelay.map(|v| v as u16), // Rule #1 for type u16? - exemptfee: c.exemptfee.clone(), // Rule #1 for type f32? + exemptfee: c.exemptfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? + localofferid: c.localofferid.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + exclude: Some(c.exclude.iter().map(|s| s.into()).collect()), // Rule #4 } } } @@ -1107,7 +1241,7 @@ impl From<&pb::PayRequest> for requests::PayRequest { impl From<&pb::ListnodesRequest> for requests::ListnodesRequest { fn from(c: &pb::ListnodesRequest) -> Self { Self { - id: c.id.clone().map(|v| hex::encode(v)), // Rule #1 for type pubkey? + id: c.id.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? } } } @@ -1116,8 +1250,8 @@ impl From<&pb::ListnodesRequest> for requests::ListnodesRequest { impl From<&pb::WaitanyinvoiceRequest> for requests::WaitanyinvoiceRequest { fn from(c: &pb::WaitanyinvoiceRequest) -> Self { Self { - lastpay_index: c.lastpay_index.clone(), // Rule #1 for type number? - timeout: c.timeout.clone(), // Rule #1 for type number? + lastpay_index: c.lastpay_index.clone(), // Rule #1 for type u64? + timeout: c.timeout.clone(), // Rule #1 for type u64? } } } @@ -1135,9 +1269,10 @@ impl From<&pb::WaitinvoiceRequest> for requests::WaitinvoiceRequest { impl From<&pb::WaitsendpayRequest> for requests::WaitsendpayRequest { fn from(c: &pb::WaitsendpayRequest) -> Self { Self { - payment_hash: hex::encode(&c.payment_hash), // Rule #1 for type hex - partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? + payment_hash: c.payment_hash.clone().try_into().unwrap(), // Rule #1 for type hash timeout: c.timeout.clone(), // Rule #1 for type u32? + partid: c.partid.clone(), // Rule #1 for type u64? + groupid: c.groupid.clone(), // Rule #1 for type u64? } } } @@ -1155,11 +1290,11 @@ impl From<&pb::NewaddrRequest> for requests::NewaddrRequest { impl From<&pb::WithdrawRequest> for requests::WithdrawRequest { fn from(c: &pb::WithdrawRequest) -> Self { Self { - destination: hex::encode(&c.destination), // Rule #1 for type pubkey - satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat|all? + destination: c.destination.clone(), // Rule #1 for type string + satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat_or_all? feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? minconf: c.minconf.map(|v| v as u16), // Rule #1 for type u16? - utxos: c.utxos.iter().map(|s| s.into()).collect(), + utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 } } } @@ -1168,13 +1303,14 @@ impl From<&pb::WithdrawRequest> for requests::WithdrawRequest { impl From<&pb::KeysendRequest> for requests::KeysendRequest { fn from(c: &pb::KeysendRequest) -> Self { Self { - destination: hex::encode(&c.destination), // Rule #1 for type pubkey + destination: cln_rpc::primitives::Pubkey::from_slice(&c.destination).unwrap(), // Rule #1 for type pubkey msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat label: c.label.clone(), // Rule #1 for type string? - maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type float? - retry_for: c.retry_for.clone(), // Rule #1 for type number? - maxdelay: c.maxdelay.clone(), // Rule #1 for type number? + maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type number? + retry_for: c.retry_for.clone(), // Rule #1 for type u32? + maxdelay: c.maxdelay.clone(), // Rule #1 for type u32? exemptfee: c.exemptfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? + routehints: c.routehints.clone().map(|rl| rl.into()), // Rule #1 for type RoutehintList? } } } @@ -1185,11 +1321,12 @@ impl From<&pb::FundpsbtRequest> for requests::FundpsbtRequest { Self { satoshi: c.satoshi.as_ref().unwrap().into(), // Rule #1 for type msat feerate: c.feerate.as_ref().unwrap().into(), // Rule #1 for type feerate - startweight: c.startweight.clone(), // Rule #1 for type number - minconf: c.minconf.clone(), // Rule #1 for type number? - reserve: c.reserve.clone(), // Rule #1 for type number? - locktime: c.locktime.clone(), // Rule #1 for type number? + startweight: c.startweight.clone(), // Rule #1 for type u32 + minconf: c.minconf.clone(), // Rule #1 for type u32? + reserve: c.reserve.clone(), // Rule #1 for type u32? + locktime: c.locktime.clone(), // Rule #1 for type u32? min_witness_weight: c.min_witness_weight.clone(), // Rule #1 for type u32? + excess_as_change: c.excess_as_change.clone(), // Rule #1 for type boolean? } } } @@ -1199,6 +1336,7 @@ impl From<&pb::SendpsbtRequest> for requests::SendpsbtRequest { fn from(c: &pb::SendpsbtRequest) -> Self { Self { psbt: c.psbt.clone(), // Rule #1 for type string + reserve: c.reserve.clone(), // Rule #1 for type boolean? } } } @@ -1208,6 +1346,7 @@ impl From<&pb::SignpsbtRequest> for requests::SignpsbtRequest { fn from(c: &pb::SignpsbtRequest) -> Self { Self { psbt: c.psbt.clone(), // Rule #1 for type string + signonly: Some(c.signonly.iter().map(|s| s.clone()).collect()), // Rule #4 } } } @@ -1218,11 +1357,13 @@ impl From<&pb::UtxopsbtRequest> for requests::UtxopsbtRequest { Self { satoshi: c.satoshi.as_ref().unwrap().into(), // Rule #1 for type msat feerate: c.feerate.as_ref().unwrap().into(), // Rule #1 for type feerate - startweight: c.startweight.clone(), // Rule #1 for type number - utxos: c.utxos.iter().map(|s| s.into()).collect(), - reserve: c.reserve.clone(), // Rule #1 for type number? - locktime: c.locktime.clone(), // Rule #1 for type number? + startweight: c.startweight.clone(), // Rule #1 for type u32 + utxos: c.utxos.iter().map(|s| s.into()).collect(), // Rule #4 + reserve: c.reserve.clone(), // Rule #1 for type u32? + reservedok: c.reservedok.clone(), // Rule #1 for type boolean? + locktime: c.locktime.clone(), // Rule #1 for type u32? min_witness_weight: c.min_witness_weight.clone(), // Rule #1 for type u32? + excess_as_change: c.excess_as_change.clone(), // Rule #1 for type boolean? } } } @@ -1231,7 +1372,7 @@ impl From<&pb::UtxopsbtRequest> for requests::UtxopsbtRequest { impl From<&pb::TxdiscardRequest> for requests::TxdiscardRequest { fn from(c: &pb::TxdiscardRequest) -> Self { Self { - txid: hex::encode(&c.txid), // Rule #1 for type hex + txid: hex::encode(&c.txid), // Rule #1 for type txid } } } @@ -1240,10 +1381,10 @@ impl From<&pb::TxdiscardRequest> for requests::TxdiscardRequest { impl From<&pb::TxprepareRequest> for requests::TxprepareRequest { fn from(c: &pb::TxprepareRequest) -> Self { Self { - outptus: c.outptus.iter().map(|s| s.into()).collect(), + outputs: c.outputs.iter().map(|s| s.into()).collect(), // Rule #4 feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? minconf: c.minconf.clone(), // Rule #1 for type u32? - utxos: c.utxos.iter().map(|s| s.into()).collect(), + utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 } } } @@ -1252,7 +1393,84 @@ impl From<&pb::TxprepareRequest> for requests::TxprepareRequest { impl From<&pb::TxsendRequest> for requests::TxsendRequest { fn from(c: &pb::TxsendRequest) -> Self { Self { - txid: hex::encode(&c.txid), // Rule #1 for type hex + txid: hex::encode(&c.txid), // Rule #1 for type txid + } + } +} + +#[allow(unused_variables)] +impl From<&pb::DisconnectRequest> for requests::DisconnectRequest { + fn from(c: &pb::DisconnectRequest) -> Self { + Self { + id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + force: c.force.clone(), // Rule #1 for type boolean? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::FeeratesRequest> for requests::FeeratesRequest { + fn from(c: &pb::FeeratesRequest) -> Self { + Self { + style: c.style.try_into().unwrap(), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::GetrouteRequest> for requests::GetrouteRequest { + fn from(c: &pb::GetrouteRequest) -> Self { + Self { + id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + riskfactor: c.riskfactor.clone(), // Rule #1 for type u64 + cltv: c.cltv.clone(), // Rule #1 for type number? + fromid: c.fromid.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? + fuzzpercent: c.fuzzpercent.clone(), // Rule #1 for type u32? + exclude: Some(c.exclude.iter().map(|s| s.into()).collect()), // Rule #4 + maxhops: c.maxhops.clone(), // Rule #1 for type u32? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListforwardsRequest> for requests::ListforwardsRequest { + fn from(c: &pb::ListforwardsRequest) -> Self { + Self { + status: c.status.map(|v| v.try_into().unwrap()), + in_channel: c.in_channel.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? + out_channel: c.out_channel.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::ListpaysRequest> for requests::ListpaysRequest { + fn from(c: &pb::ListpaysRequest) -> Self { + Self { + bolt11: c.bolt11.clone(), // Rule #1 for type string? + payment_hash: c.payment_hash.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + status: c.status.map(|v| v.try_into().unwrap()), + } + } +} + +#[allow(unused_variables)] +impl From<&pb::PingRequest> for requests::PingRequest { + fn from(c: &pb::PingRequest) -> Self { + Self { + id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + len: c.len.clone(), // Rule #1 for type number? + pongbytes: c.pongbytes.clone(), // Rule #1 for type number? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::SignmessageRequest> for requests::SignmessageRequest { + fn from(c: &pb::SignmessageRequest) -> Self { + Self { + message: c.message.clone(), // Rule #1 for type string } } } diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 5d695b305df2..5ebdfeaefddd 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -1210,4 +1210,228 @@ async fn tx_send( } +async fn disconnect( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::DisconnectRequest = (&req).into(); + debug!("Client asked for disconnect"); + trace!("disconnect request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Disconnect(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Disconnect: {:?}", e)))?; + match result { + Response::Disconnect(r) => { + trace!("disconnect response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Disconnect", + r + ) + )), + } + +} + +async fn feerates( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::FeeratesRequest = (&req).into(); + debug!("Client asked for feerates"); + trace!("feerates request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Feerates(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Feerates: {:?}", e)))?; + match result { + Response::Feerates(r) => { + trace!("feerates response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Feerates", + r + ) + )), + } + +} + +async fn get_route( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::GetrouteRequest = (&req).into(); + debug!("Client asked for get_route"); + trace!("get_route request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::GetRoute(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method GetRoute: {:?}", e)))?; + match result { + Response::GetRoute(r) => { + trace!("get_route response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call GetRoute", + r + ) + )), + } + +} + +async fn list_forwards( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListforwardsRequest = (&req).into(); + debug!("Client asked for list_forwards"); + trace!("list_forwards request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListForwards(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListForwards: {:?}", e)))?; + match result { + Response::ListForwards(r) => { + trace!("list_forwards response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListForwards", + r + ) + )), + } + +} + +async fn list_pays( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::ListpaysRequest = (&req).into(); + debug!("Client asked for list_pays"); + trace!("list_pays request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::ListPays(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method ListPays: {:?}", e)))?; + match result { + Response::ListPays(r) => { + trace!("list_pays response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call ListPays", + r + ) + )), + } + +} + +async fn ping( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::PingRequest = (&req).into(); + debug!("Client asked for ping"); + trace!("ping request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Ping(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Ping: {:?}", e)))?; + match result { + Response::Ping(r) => { + trace!("ping response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Ping", + r + ) + )), + } + +} + +async fn sign_message( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::SignmessageRequest = (&req).into(); + debug!("Client asked for sign_message"); + trace!("sign_message request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::SignMessage(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method SignMessage: {:?}", e)))?; + match result { + Response::SignMessage(r) => { + trace!("sign_message response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call SignMessage", + r + ) + )), + } + +} + } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index be76d10ceba0..5a4aaa4aa9e0 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -53,6 +53,13 @@ pub enum Request { TxDiscard(requests::TxdiscardRequest), TxPrepare(requests::TxprepareRequest), TxSend(requests::TxsendRequest), + Disconnect(requests::DisconnectRequest), + Feerates(requests::FeeratesRequest), + GetRoute(requests::GetrouteRequest), + ListForwards(requests::ListforwardsRequest), + ListPays(requests::ListpaysRequest), + Ping(requests::PingRequest), + SignMessage(requests::SignmessageRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -96,6 +103,13 @@ pub enum Response { TxDiscard(responses::TxdiscardResponse), TxPrepare(responses::TxprepareResponse), TxSend(responses::TxsendResponse), + Disconnect(responses::DisconnectResponse), + Feerates(responses::FeeratesResponse), + GetRoute(responses::GetrouteResponse), + ListForwards(responses::ListforwardsResponse), + ListPays(responses::ListpaysResponse), + Ping(responses::PingResponse), + SignMessage(responses::SignmessageResponse), } pub mod requests { @@ -111,7 +125,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersRequest { #[serde(alias = "id", skip_serializing_if = "Option::is_none")] - pub id: Option, + pub id: Option, #[serde(alias = "level", skip_serializing_if = "Option::is_none")] pub level: Option, } @@ -127,11 +141,11 @@ pub mod requests { #[serde(alias = "msatoshi")] pub msatoshi: Amount, #[serde(alias = "id")] - pub id: String, + pub id: Pubkey, #[serde(alias = "delay")] pub delay: u16, #[serde(alias = "channel")] - pub channel: String, + pub channel: ShortChannelId, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -139,7 +153,7 @@ pub mod requests { #[serde(alias = "route")] pub route: Vec, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, #[serde(alias = "msatoshi", skip_serializing_if = "Option::is_none")] @@ -147,19 +161,23 @@ pub mod requests { #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] pub bolt11: Option, #[serde(alias = "payment_secret", skip_serializing_if = "Option::is_none")] - pub payment_secret: Option, + pub payment_secret: Option, #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] pub partid: Option, + #[serde(alias = "localofferid", skip_serializing_if = "Option::is_none")] + pub localofferid: Option, + #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] + pub groupid: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListchannelsRequest { #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] - pub short_channel_id: Option, + pub short_channel_id: Option, #[serde(alias = "source", skip_serializing_if = "Option::is_none")] - pub source: Option, + pub source: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -183,7 +201,7 @@ pub mod requests { #[serde(alias = "zbase")] pub zbase: String, #[serde(alias = "pubkey", skip_serializing_if = "Option::is_none")] - pub pubkey: Option, + pub pubkey: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -200,6 +218,8 @@ pub mod requests { pub wrong_funding: Option, #[serde(alias = "force_lease_closed", skip_serializing_if = "Option::is_none")] pub force_lease_closed: Option, + #[serde(alias = "feerange", skip_serializing_if = "Option::is_none")] + pub feerange: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -223,12 +243,16 @@ pub mod requests { } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum DatastoreMode { + #[serde(rename = "must-create")] MUST_CREATE, + #[serde(rename = "must-replace")] MUST_REPLACE, + #[serde(rename = "create-or-replace")] CREATE_OR_REPLACE, + #[serde(rename = "must-append")] MUST_APPEND, + #[serde(rename = "create-or-append")] CREATE_OR_APPEND, } @@ -249,6 +273,8 @@ pub mod requests { pub struct DatastoreRequest { #[serde(alias = "key")] pub key: Vec, + #[serde(alias = "string", skip_serializing_if = "Option::is_none")] + pub string: Option, #[serde(alias = "hex", skip_serializing_if = "Option::is_none")] pub hex: Option, pub mode: Option, @@ -259,7 +285,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreateonionHops { #[serde(alias = "pubkey")] - pub pubkey: String, + pub pubkey: Pubkey, #[serde(alias = "payload")] pub payload: String, } @@ -271,7 +297,7 @@ pub mod requests { #[serde(alias = "assocdata")] pub assocdata: String, #[serde(alias = "session_key", skip_serializing_if = "Option::is_none")] - pub session_key: Option, + pub session_key: Option, #[serde(alias = "onion_size", skip_serializing_if = "Option::is_none")] pub onion_size: Option, } @@ -286,15 +312,17 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DelexpiredinvoiceRequest { - #[serde(alias = "maxexpirytime")] - pub maxexpirytime: u32, + #[serde(alias = "maxexpirytime", skip_serializing_if = "Option::is_none")] + pub maxexpirytime: Option, } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum DelinvoiceStatus { + #[serde(rename = "paid")] PAID, + #[serde(rename = "expired")] EXPIRED, + #[serde(rename = "unpaid")] UNPAID, } @@ -316,6 +344,8 @@ pub mod requests { // Path `DelInvoice.status` #[serde(rename = "status")] pub status: DelinvoiceStatus, + #[serde(alias = "desconly", skip_serializing_if = "Option::is_none")] + pub desconly: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -326,18 +356,24 @@ pub mod requests { pub description: String, #[serde(alias = "label")] pub label: String, - #[serde(alias = "fallbacks")] - pub fallbacks: Vec, + #[serde(alias = "expiry", skip_serializing_if = "Option::is_none")] + pub expiry: Option, + #[serde(alias = "fallbacks", skip_serializing_if = "Option::is_none")] + pub fallbacks: Option>, #[serde(alias = "preimage", skip_serializing_if = "Option::is_none")] pub preimage: Option, + #[serde(alias = "exposeprivatechannels", skip_serializing_if = "Option::is_none")] + pub exposeprivatechannels: Option, #[serde(alias = "cltv", skip_serializing_if = "Option::is_none")] pub cltv: Option, + #[serde(alias = "deschashonly", skip_serializing_if = "Option::is_none")] + pub deschashonly: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListdatastoreRequest { - #[serde(alias = "key")] - pub key: Vec, + #[serde(alias = "key", skip_serializing_if = "Option::is_none")] + pub key: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -355,7 +391,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendonionFirst_hop { #[serde(alias = "id")] - pub id: String, + pub id: Pubkey, #[serde(alias = "amount_msat")] pub amount_msat: Amount, #[serde(alias = "delay")] @@ -366,13 +402,33 @@ pub mod requests { pub struct SendonionRequest { #[serde(alias = "onion")] pub onion: String, + #[serde(alias = "payment_hash")] + pub payment_hash: Sha256, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "shared_secrets", skip_serializing_if = "Option::is_none")] + pub shared_secrets: Option>, + #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] + pub partid: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "msatoshi", skip_serializing_if = "Option::is_none")] + pub msatoshi: Option, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "localofferid", skip_serializing_if = "Option::is_none")] + pub localofferid: Option, + #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] + pub groupid: Option, } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListsendpaysStatus { + #[serde(rename = "pending")] PENDING, + #[serde(rename = "complete")] COMPLETE, + #[serde(rename = "failed")] FAILED, } @@ -392,7 +448,7 @@ pub mod requests { #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] pub bolt11: Option, #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] - pub payment_hash: Option, + pub payment_hash: Option, pub status: Option, } @@ -409,29 +465,33 @@ pub mod requests { #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, #[serde(alias = "riskfactor", skip_serializing_if = "Option::is_none")] - pub riskfactor: Option, + pub riskfactor: Option, #[serde(alias = "maxfeepercent", skip_serializing_if = "Option::is_none")] - pub maxfeepercent: Option, + pub maxfeepercent: Option, #[serde(alias = "retry_for", skip_serializing_if = "Option::is_none")] pub retry_for: Option, #[serde(alias = "maxdelay", skip_serializing_if = "Option::is_none")] pub maxdelay: Option, #[serde(alias = "exemptfee", skip_serializing_if = "Option::is_none")] - pub exemptfee: Option, + pub exemptfee: Option, + #[serde(alias = "localofferid", skip_serializing_if = "Option::is_none")] + pub localofferid: Option, + #[serde(alias = "exclude", skip_serializing_if = "Option::is_none")] + pub exclude: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListnodesRequest { #[serde(alias = "id", skip_serializing_if = "Option::is_none")] - pub id: Option, + pub id: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitanyinvoiceRequest { #[serde(alias = "lastpay_index", skip_serializing_if = "Option::is_none")] - pub lastpay_index: Option, + pub lastpay_index: Option, #[serde(alias = "timeout", skip_serializing_if = "Option::is_none")] - pub timeout: Option, + pub timeout: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -443,18 +503,23 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitsendpayRequest { #[serde(alias = "payment_hash")] - pub payment_hash: String, - #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] - pub partid: Option, + pub payment_hash: Sha256, #[serde(alias = "timeout", skip_serializing_if = "Option::is_none")] pub timeout: Option, + #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] + pub partid: Option, + #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] + pub groupid: Option, } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum NewaddrAddresstype { + #[serde(rename = "bech32")] BECH32, + #[serde(rename = "p2sh-segwit")] P2SH_SEGWIT, + #[serde(rename = "all")] + ALL, } impl TryFrom for NewaddrAddresstype { @@ -463,6 +528,7 @@ pub mod requests { match c { 0 => Ok(NewaddrAddresstype::BECH32), 1 => Ok(NewaddrAddresstype::P2SH_SEGWIT), + 2 => Ok(NewaddrAddresstype::ALL), o => Err(anyhow::anyhow!("Unknown variant {} for enum NewaddrAddresstype", o)), } } @@ -482,26 +548,32 @@ pub mod requests { pub feerate: Option, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] pub minconf: Option, - #[serde(alias = "utxos")] - pub utxos: Vec, + #[serde(alias = "utxos", skip_serializing_if = "Option::is_none")] + pub utxos: Option>, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct KeysendExtratlvs { } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct KeysendRequest { #[serde(alias = "destination")] - pub destination: String, + pub destination: Pubkey, #[serde(alias = "msatoshi")] pub msatoshi: Amount, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, #[serde(alias = "maxfeepercent", skip_serializing_if = "Option::is_none")] - pub maxfeepercent: Option, + pub maxfeepercent: Option, #[serde(alias = "retry_for", skip_serializing_if = "Option::is_none")] - pub retry_for: Option, + pub retry_for: Option, #[serde(alias = "maxdelay", skip_serializing_if = "Option::is_none")] - pub maxdelay: Option, + pub maxdelay: Option, #[serde(alias = "exemptfee", skip_serializing_if = "Option::is_none")] pub exemptfee: Option, + #[serde(alias = "routehints", skip_serializing_if = "Option::is_none")] + pub routehints: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -511,27 +583,33 @@ pub mod requests { #[serde(alias = "feerate")] pub feerate: Feerate, #[serde(alias = "startweight")] - pub startweight: i64, + pub startweight: u32, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] - pub minconf: Option, + pub minconf: Option, #[serde(alias = "reserve", skip_serializing_if = "Option::is_none")] - pub reserve: Option, + pub reserve: Option, #[serde(alias = "locktime", skip_serializing_if = "Option::is_none")] - pub locktime: Option, + pub locktime: Option, #[serde(alias = "min_witness_weight", skip_serializing_if = "Option::is_none")] pub min_witness_weight: Option, + #[serde(alias = "excess_as_change", skip_serializing_if = "Option::is_none")] + pub excess_as_change: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendpsbtRequest { #[serde(alias = "psbt")] pub psbt: String, + #[serde(alias = "reserve", skip_serializing_if = "Option::is_none")] + pub reserve: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignpsbtRequest { #[serde(alias = "psbt")] pub psbt: String, + #[serde(alias = "signonly", skip_serializing_if = "Option::is_none")] + pub signonly: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -541,15 +619,19 @@ pub mod requests { #[serde(alias = "feerate")] pub feerate: Feerate, #[serde(alias = "startweight")] - pub startweight: i64, + pub startweight: u32, #[serde(alias = "utxos")] - pub utxos: Vec, + pub utxos: Vec, #[serde(alias = "reserve", skip_serializing_if = "Option::is_none")] - pub reserve: Option, + pub reserve: Option, + #[serde(alias = "reservedok", skip_serializing_if = "Option::is_none")] + pub reservedok: Option, #[serde(alias = "locktime", skip_serializing_if = "Option::is_none")] - pub locktime: Option, + pub locktime: Option, #[serde(alias = "min_witness_weight", skip_serializing_if = "Option::is_none")] pub min_witness_weight: Option, + #[serde(alias = "excess_as_change", skip_serializing_if = "Option::is_none")] + pub excess_as_change: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -560,14 +642,14 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TxprepareRequest { - #[serde(alias = "outptus")] - pub outptus: Vec, + #[serde(alias = "outputs")] + pub outputs: Vec, #[serde(alias = "feerate", skip_serializing_if = "Option::is_none")] pub feerate: Option, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] pub minconf: Option, - #[serde(alias = "utxos")] - pub utxos: Vec, + #[serde(alias = "utxos", skip_serializing_if = "Option::is_none")] + pub utxos: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -576,6 +658,138 @@ pub mod requests { pub txid: String, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DisconnectRequest { + #[serde(alias = "id")] + pub id: Pubkey, + #[serde(alias = "force", skip_serializing_if = "Option::is_none")] + pub force: Option, + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + pub enum FeeratesStyle { + #[serde(rename = "perkb")] + PERKB, + #[serde(rename = "perkw")] + PERKW, + } + + impl TryFrom for FeeratesStyle { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(FeeratesStyle::PERKB), + 1 => Ok(FeeratesStyle::PERKW), + o => Err(anyhow::anyhow!("Unknown variant {} for enum FeeratesStyle", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FeeratesRequest { + // Path `Feerates.style` + #[serde(rename = "style")] + pub style: FeeratesStyle, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetrouteRequest { + #[serde(alias = "id")] + pub id: Pubkey, + #[serde(alias = "msatoshi")] + pub msatoshi: Amount, + #[serde(alias = "riskfactor")] + pub riskfactor: u64, + #[serde(alias = "cltv", skip_serializing_if = "Option::is_none")] + pub cltv: Option, + #[serde(alias = "fromid", skip_serializing_if = "Option::is_none")] + pub fromid: Option, + #[serde(alias = "fuzzpercent", skip_serializing_if = "Option::is_none")] + pub fuzzpercent: Option, + #[serde(alias = "exclude", skip_serializing_if = "Option::is_none")] + pub exclude: Option>, + #[serde(alias = "maxhops", skip_serializing_if = "Option::is_none")] + pub maxhops: Option, + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + pub enum ListforwardsStatus { + #[serde(rename = "offered")] + OFFERED, + #[serde(rename = "settled")] + SETTLED, + #[serde(rename = "local_failed")] + LOCAL_FAILED, + #[serde(rename = "failed")] + FAILED, + } + + impl TryFrom for ListforwardsStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListforwardsStatus::OFFERED), + 1 => Ok(ListforwardsStatus::SETTLED), + 2 => Ok(ListforwardsStatus::LOCAL_FAILED), + 3 => Ok(ListforwardsStatus::FAILED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListforwardsStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListforwardsRequest { + pub status: Option, + #[serde(alias = "in_channel", skip_serializing_if = "Option::is_none")] + pub in_channel: Option, + #[serde(alias = "out_channel", skip_serializing_if = "Option::is_none")] + pub out_channel: Option, + } + + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + pub enum ListpaysStatus { + #[serde(rename = "pending")] + PENDING, + #[serde(rename = "complete")] + COMPLETE, + #[serde(rename = "failed")] + FAILED, + } + + impl TryFrom for ListpaysStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListpaysStatus::PENDING), + 1 => Ok(ListpaysStatus::COMPLETE), + 2 => Ok(ListpaysStatus::FAILED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpaysStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpaysRequest { + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] + pub payment_hash: Option, + pub status: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PingRequest { + #[serde(alias = "id")] + pub id: Pubkey, + #[serde(alias = "len", skip_serializing_if = "Option::is_none")] + pub len: Option, + #[serde(alias = "pongbytes", skip_serializing_if = "Option::is_none")] + pub pongbytes: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SignmessageRequest { + #[serde(alias = "message")] + pub message: String, + } + } @@ -599,13 +813,18 @@ pub mod responses { /// Type of connection #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum GetinfoAddressType { + #[serde(rename = "dns")] DNS, + #[serde(rename = "ipv4")] IPV4, + #[serde(rename = "ipv6")] IPV6, + #[serde(rename = "torv2")] TORV2, + #[serde(rename = "torv3")] TORV3, + #[serde(rename = "websocket")] WEBSOCKET, } @@ -636,12 +855,16 @@ pub mod responses { /// Type of connection #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum GetinfoBindingType { + #[serde(rename = "local socket")] LOCAL_SOCKET, + #[serde(rename = "ipv4")] IPV4, + #[serde(rename = "ipv6")] IPV6, + #[serde(rename = "torv2")] TORV2, + #[serde(rename = "torv3")] TORV3, } @@ -674,7 +897,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoResponse { #[serde(alias = "id")] - pub id: String, + pub id: Pubkey, #[serde(alias = "alias")] pub alias: String, #[serde(alias = "color")] @@ -697,10 +920,10 @@ pub mod responses { pub network: String, #[serde(alias = "fees_collected_msat")] pub fees_collected_msat: Amount, - #[serde(alias = "address")] - pub address: Vec, - #[serde(alias = "binding")] - pub binding: Vec, + #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + pub address: Option>, + #[serde(alias = "binding", skip_serializing_if = "Option::is_none")] + pub binding: Option>, #[serde(alias = "warning_bitcoind_sync", skip_serializing_if = "Option::is_none")] pub warning_bitcoind_sync: Option, #[serde(alias = "warning_lightningd_sync", skip_serializing_if = "Option::is_none")] @@ -708,14 +931,20 @@ pub mod responses { } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListpeersPeersLogType { + #[serde(rename = "SKIPPED")] SKIPPED, + #[serde(rename = "BROKEN")] BROKEN, + #[serde(rename = "UNUSUAL")] UNUSUAL, + #[serde(rename = "INFO")] INFO, + #[serde(rename = "DEBUG")] DEBUG, + #[serde(rename = "IO_IN")] IO_IN, + #[serde(rename = "IO_OUT")] IO_OUT, } @@ -748,25 +977,35 @@ pub mod responses { #[serde(alias = "log", skip_serializing_if = "Option::is_none")] pub log: Option, #[serde(alias = "node_id", skip_serializing_if = "Option::is_none")] - pub node_id: Option, + pub node_id: Option, #[serde(alias = "data", skip_serializing_if = "Option::is_none")] pub data: Option, } /// the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListpeersPeersChannelsState { + #[serde(rename = "OPENINGD")] OPENINGD, + #[serde(rename = "CHANNELD_AWAITING_LOCKIN")] CHANNELD_AWAITING_LOCKIN, + #[serde(rename = "CHANNELD_NORMAL")] CHANNELD_NORMAL, + #[serde(rename = "CHANNELD_SHUTTING_DOWN")] CHANNELD_SHUTTING_DOWN, + #[serde(rename = "CLOSINGD_SIGEXCHANGE")] CLOSINGD_SIGEXCHANGE, + #[serde(rename = "CLOSINGD_COMPLETE")] CLOSINGD_COMPLETE, + #[serde(rename = "AWAITING_UNILATERAL")] AWAITING_UNILATERAL, + #[serde(rename = "FUNDING_SPEND_SEEN")] FUNDING_SPEND_SEEN, + #[serde(rename = "ONCHAIN")] ONCHAIN, + #[serde(rename = "DUALOPEND_OPEN_INIT")] DUALOPEND_OPEN_INIT, + #[serde(rename = "DUALOPEND_AWAITING_LOCKIN")] DUALOPEND_AWAITING_LOCKIN, } @@ -842,9 +1081,10 @@ pub mod responses { /// Whether it came from peer, or is going to peer #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListpeersPeersChannelsHtlcsDirection { + #[serde(rename = "in")] IN, + #[serde(rename = "out")] OUT, } @@ -858,40 +1098,6 @@ pub mod responses { } } } - /// Status of the HTLC - #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] - pub enum ListpeersPeersChannelsHtlcsState { - SENT_ADD_HTLC, - SENT_ADD_COMMIT, - RCVD_ADD_REVOCATION, - RCVD_ADD_ACK_COMMIT, - SENT_ADD_ACK_REVOCATION, - RCVD_REMOVE_HTLC, - RCVD_REMOVE_COMMIT, - SENT_REMOVE_REVOCATION, - SENT_REMOVE_ACK_COMMIT, - RCVD_REMOVE_ACK_REVOCATION, - } - - impl TryFrom for ListpeersPeersChannelsHtlcsState { - type Error = anyhow::Error; - fn try_from(c: i32) -> Result { - match c { - 0 => Ok(ListpeersPeersChannelsHtlcsState::SENT_ADD_HTLC), - 1 => Ok(ListpeersPeersChannelsHtlcsState::SENT_ADD_COMMIT), - 2 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_ADD_REVOCATION), - 3 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_ADD_ACK_COMMIT), - 4 => Ok(ListpeersPeersChannelsHtlcsState::SENT_ADD_ACK_REVOCATION), - 5 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_REMOVE_HTLC), - 6 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_REMOVE_COMMIT), - 7 => Ok(ListpeersPeersChannelsHtlcsState::SENT_REMOVE_REVOCATION), - 8 => Ok(ListpeersPeersChannelsHtlcsState::SENT_REMOVE_ACK_COMMIT), - 9 => Ok(ListpeersPeersChannelsHtlcsState::RCVD_REMOVE_ACK_REVOCATION), - o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpeersPeersChannelsHtlcsState", o)), - } - } - } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeersChannelsHtlcs { // Path `ListPeers.peers[].channels[].htlcs[].direction` @@ -904,14 +1110,11 @@ pub mod responses { #[serde(alias = "expiry")] pub expiry: u32, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, #[serde(alias = "local_trimmed", skip_serializing_if = "Option::is_none")] pub local_trimmed: Option, #[serde(alias = "status", skip_serializing_if = "Option::is_none")] pub status: Option, - // Path `ListPeers.peers[].channels[].htlcs[].state` - #[serde(rename = "state")] - pub state: ListpeersPeersChannelsHtlcsState, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -924,9 +1127,9 @@ pub mod responses { #[serde(alias = "owner", skip_serializing_if = "Option::is_none")] pub owner: Option, #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] - pub short_channel_id: Option, + pub short_channel_id: Option, #[serde(alias = "channel_id", skip_serializing_if = "Option::is_none")] - pub channel_id: Option, + pub channel_id: Option, #[serde(alias = "funding_txid", skip_serializing_if = "Option::is_none")] pub funding_txid: Option, #[serde(alias = "funding_outnum", skip_serializing_if = "Option::is_none")] @@ -939,8 +1142,8 @@ pub mod responses { pub next_feerate: Option, #[serde(alias = "next_fee_step", skip_serializing_if = "Option::is_none")] pub next_fee_step: Option, - #[serde(alias = "inflight")] - pub inflight: Vec, + #[serde(alias = "inflight", skip_serializing_if = "Option::is_none")] + pub inflight: Option>, #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] pub close_to: Option, #[serde(alias = "private", skip_serializing_if = "Option::is_none")] @@ -948,7 +1151,6 @@ pub mod responses { // Path `ListPeers.peers[].channels[].opener` #[serde(rename = "opener")] pub opener: ChannelSide, - pub closer: Option, #[serde(alias = "features")] pub features: Vec, #[serde(alias = "to_us_msat", skip_serializing_if = "Option::is_none")] @@ -977,16 +1179,20 @@ pub mod responses { pub receivable_msat: Option, #[serde(alias = "minimum_htlc_in_msat", skip_serializing_if = "Option::is_none")] pub minimum_htlc_in_msat: Option, + #[serde(alias = "minimum_htlc_out_msat", skip_serializing_if = "Option::is_none")] + pub minimum_htlc_out_msat: Option, + #[serde(alias = "maximum_htlc_out_msat", skip_serializing_if = "Option::is_none")] + pub maximum_htlc_out_msat: Option, #[serde(alias = "their_to_self_delay", skip_serializing_if = "Option::is_none")] pub their_to_self_delay: Option, #[serde(alias = "our_to_self_delay", skip_serializing_if = "Option::is_none")] pub our_to_self_delay: Option, #[serde(alias = "max_accepted_htlcs", skip_serializing_if = "Option::is_none")] pub max_accepted_htlcs: Option, - #[serde(alias = "state_changes")] - pub state_changes: Vec, - #[serde(alias = "status")] - pub status: Vec, + #[serde(alias = "state_changes", skip_serializing_if = "Option::is_none")] + pub state_changes: Option>, + #[serde(alias = "status", skip_serializing_if = "Option::is_none")] + pub status: Option>, #[serde(alias = "in_payments_offered", skip_serializing_if = "Option::is_none")] pub in_payments_offered: Option, #[serde(alias = "in_offered_msat", skip_serializing_if = "Option::is_none")] @@ -1003,8 +1209,8 @@ pub mod responses { pub out_payments_fulfilled: Option, #[serde(alias = "out_fulfilled_msat", skip_serializing_if = "Option::is_none")] pub out_fulfilled_msat: Option, - #[serde(alias = "htlcs")] - pub htlcs: Vec, + #[serde(alias = "htlcs", skip_serializing_if = "Option::is_none")] + pub htlcs: Option>, #[serde(alias = "close_to_addr", skip_serializing_if = "Option::is_none")] pub close_to_addr: Option, } @@ -1012,15 +1218,15 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeers { #[serde(alias = "id")] - pub id: String, + pub id: Pubkey, #[serde(alias = "connected")] pub connected: bool, - #[serde(alias = "log")] - pub log: Vec, + #[serde(alias = "log", skip_serializing_if = "Option::is_none")] + pub log: Option>, #[serde(alias = "channels")] pub channels: Vec, - #[serde(alias = "netaddr")] - pub netaddr: Vec, + #[serde(alias = "netaddr", skip_serializing_if = "Option::is_none")] + pub netaddr: Option>, #[serde(alias = "features", skip_serializing_if = "Option::is_none")] pub features: Option, } @@ -1032,10 +1238,12 @@ pub mod responses { } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListfundsOutputsStatus { + #[serde(rename = "unconfirmed")] UNCONFIRMED, + #[serde(rename = "confirmed")] CONFIRMED, + #[serde(rename = "spent")] SPENT, } @@ -1074,7 +1282,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListfundsChannels { #[serde(alias = "peer_id")] - pub peer_id: String, + pub peer_id: Pubkey, #[serde(alias = "our_amount_msat")] pub our_amount_msat: Amount, #[serde(alias = "amount_msat")] @@ -1089,7 +1297,7 @@ pub mod responses { #[serde(rename = "state")] pub state: ChannelState, #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] - pub short_channel_id: Option, + pub short_channel_id: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -1102,9 +1310,10 @@ pub mod responses { /// status of the payment (could be complete if already sent previously) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum SendpayStatus { + #[serde(rename = "pending")] PENDING, + #[serde(rename = "complete")] COMPLETE, } @@ -1125,14 +1334,14 @@ pub mod responses { #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] pub groupid: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `SendPay.status` #[serde(rename = "status")] pub status: SendpayStatus, #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "amount_sent_msat")] @@ -1146,7 +1355,7 @@ pub mod responses { #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, #[serde(alias = "message", skip_serializing_if = "Option::is_none")] pub message: Option, } @@ -1154,11 +1363,11 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListchannelsChannels { #[serde(alias = "source")] - pub source: String, + pub source: Pubkey, #[serde(alias = "destination")] - pub destination: String, + pub destination: Pubkey, #[serde(alias = "short_channel_id")] - pub short_channel_id: String, + pub short_channel_id: ShortChannelId, #[serde(alias = "public")] pub public: bool, #[serde(alias = "amount_msat")] @@ -1210,15 +1419,17 @@ pub mod responses { #[serde(alias = "verified")] pub verified: bool, #[serde(alias = "pubkey", skip_serializing_if = "Option::is_none")] - pub pubkey: Option, + pub pubkey: Option, } /// Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum CloseType { + #[serde(rename = "mutual")] MUTUAL, + #[serde(rename = "unilateral")] UNILATERAL, + #[serde(rename = "unopened")] UNOPENED, } @@ -1246,9 +1457,10 @@ pub mod responses { /// Whether they initiated connection or we did #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ConnectDirection { + #[serde(rename = "in")] IN, + #[serde(rename = "out")] OUT, } @@ -1264,12 +1476,16 @@ pub mod responses { } /// Type of connection (*torv2*/*torv3* only if **direction** is *out*) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ConnectAddressType { + #[serde(rename = "local socket")] LOCAL_SOCKET, + #[serde(rename = "ipv4")] IPV4, + #[serde(rename = "ipv6")] IPV6, + #[serde(rename = "torv2")] TORV2, + #[serde(rename = "torv3")] TORV3, } @@ -1302,7 +1518,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ConnectResponse { #[serde(alias = "id")] - pub id: String, + pub id: Pubkey, #[serde(alias = "features")] pub features: String, // Path `Connect.direction` @@ -1312,10 +1528,12 @@ pub mod responses { /// Whether it has been paid, or can no longer be paid #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum CreateinvoiceStatus { + #[serde(rename = "paid")] PAID, + #[serde(rename = "expired")] EXPIRED, + #[serde(rename = "unpaid")] UNPAID, } @@ -1339,7 +1557,7 @@ pub mod responses { #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, // Path `CreateInvoice.status` @@ -1356,7 +1574,7 @@ pub mod responses { #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] pub paid_at: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, #[serde(alias = "local_offer_id", skip_serializing_if = "Option::is_none")] pub local_offer_id: Option, #[serde(alias = "payer_note", skip_serializing_if = "Option::is_none")] @@ -1380,7 +1598,7 @@ pub mod responses { #[serde(alias = "onion")] pub onion: String, #[serde(alias = "shared_secrets")] - pub shared_secrets: Vec, + pub shared_secrets: Vec, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -1401,10 +1619,12 @@ pub mod responses { /// State of invoice #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum DelinvoiceStatus { + #[serde(rename = "paid")] PAID, + #[serde(rename = "expired")] EXPIRED, + #[serde(rename = "unpaid")] UNPAID, } @@ -1432,7 +1652,7 @@ pub mod responses { #[serde(alias = "description", skip_serializing_if = "Option::is_none")] pub description: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `DelInvoice.status` #[serde(rename = "status")] pub status: DelinvoiceStatus, @@ -1449,9 +1669,9 @@ pub mod responses { #[serde(alias = "bolt11")] pub bolt11: String, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, #[serde(alias = "payment_secret")] - pub payment_secret: String, + pub payment_secret: Secret, #[serde(alias = "expires_at")] pub expires_at: u64, #[serde(alias = "warning_capacity", skip_serializing_if = "Option::is_none")] @@ -1486,10 +1706,12 @@ pub mod responses { /// Whether it's paid, unpaid or unpayable #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListinvoicesInvoicesStatus { + #[serde(rename = "unpaid")] UNPAID, + #[serde(rename = "paid")] PAID, + #[serde(rename = "expired")] EXPIRED, } @@ -1508,10 +1730,10 @@ pub mod responses { pub struct ListinvoicesInvoices { #[serde(alias = "label")] pub label: String, - #[serde(alias = "description")] - pub description: String, + #[serde(alias = "description", skip_serializing_if = "Option::is_none")] + pub description: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `ListInvoices.invoices[].status` #[serde(rename = "status")] pub status: ListinvoicesInvoicesStatus, @@ -1534,7 +1756,7 @@ pub mod responses { #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] pub paid_at: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -1545,9 +1767,10 @@ pub mod responses { /// status of the payment (could be complete if already sent previously) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum SendonionStatus { + #[serde(rename = "pending")] PENDING, + #[serde(rename = "complete")] COMPLETE, } @@ -1566,14 +1789,14 @@ pub mod responses { #[serde(alias = "id")] pub id: u64, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `SendOnion.status` #[serde(rename = "status")] pub status: SendonionStatus, #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "amount_sent_msat")] @@ -1584,18 +1807,22 @@ pub mod responses { pub bolt11: Option, #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, + #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] + pub partid: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, #[serde(alias = "message", skip_serializing_if = "Option::is_none")] pub message: Option, } /// status of the payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListsendpaysPaymentsStatus { + #[serde(rename = "pending")] PENDING, + #[serde(rename = "failed")] FAILED, + #[serde(rename = "complete")] COMPLETE, } @@ -1617,14 +1844,14 @@ pub mod responses { #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] pub groupid: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `ListSendPays.payments[].status` #[serde(rename = "status")] pub status: ListsendpaysPaymentsStatus, #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "amount_sent_msat")] @@ -1636,7 +1863,7 @@ pub mod responses { #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, #[serde(alias = "erroronion", skip_serializing_if = "Option::is_none")] pub erroronion: Option, } @@ -1649,18 +1876,28 @@ pub mod responses { /// the purpose of this input (*EXPERIMENTAL_FEATURES* only) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListtransactionsTransactionsInputsType { + #[serde(rename = "theirs")] THEIRS, + #[serde(rename = "deposit")] DEPOSIT, + #[serde(rename = "withdraw")] WITHDRAW, + #[serde(rename = "channel_funding")] CHANNEL_FUNDING, + #[serde(rename = "channel_mutual_close")] CHANNEL_MUTUAL_CLOSE, + #[serde(rename = "channel_unilateral_close")] CHANNEL_UNILATERAL_CLOSE, + #[serde(rename = "channel_sweep")] CHANNEL_SWEEP, + #[serde(rename = "channel_htlc_success")] CHANNEL_HTLC_SUCCESS, + #[serde(rename = "channel_htlc_timeout")] CHANNEL_HTLC_TIMEOUT, + #[serde(rename = "channel_penalty")] CHANNEL_PENALTY, + #[serde(rename = "channel_unilateral_cheat")] CHANNEL_UNILATERAL_CHEAT, } @@ -1693,23 +1930,33 @@ pub mod responses { pub sequence: u32, pub item_type: Option, #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] - pub channel: Option, + pub channel: Option, } /// the purpose of this output (*EXPERIMENTAL_FEATURES* only) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListtransactionsTransactionsOutputsType { + #[serde(rename = "theirs")] THEIRS, + #[serde(rename = "deposit")] DEPOSIT, + #[serde(rename = "withdraw")] WITHDRAW, + #[serde(rename = "channel_funding")] CHANNEL_FUNDING, + #[serde(rename = "channel_mutual_close")] CHANNEL_MUTUAL_CLOSE, + #[serde(rename = "channel_unilateral_close")] CHANNEL_UNILATERAL_CLOSE, + #[serde(rename = "channel_sweep")] CHANNEL_SWEEP, + #[serde(rename = "channel_htlc_success")] CHANNEL_HTLC_SUCCESS, + #[serde(rename = "channel_htlc_timeout")] CHANNEL_HTLC_TIMEOUT, + #[serde(rename = "channel_penalty")] CHANNEL_PENALTY, + #[serde(rename = "channel_unilateral_cheat")] CHANNEL_UNILATERAL_CHEAT, } @@ -1742,7 +1989,7 @@ pub mod responses { pub script_pub_key: String, pub item_type: Option, #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] - pub channel: Option, + pub channel: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -1756,7 +2003,7 @@ pub mod responses { #[serde(alias = "txindex")] pub txindex: u32, #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] - pub channel: Option, + pub channel: Option, #[serde(alias = "locktime")] pub locktime: u32, #[serde(alias = "version")] @@ -1775,10 +2022,12 @@ pub mod responses { /// status of payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum PayStatus { + #[serde(rename = "complete")] COMPLETE, + #[serde(rename = "pending")] PENDING, + #[serde(rename = "failed")] FAILED, } @@ -1796,13 +2045,13 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PayResponse { #[serde(alias = "payment_preimage")] - pub payment_preimage: String, + pub payment_preimage: Secret, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, #[serde(alias = "created_at")] - pub created_at: i64, + pub created_at: f64, #[serde(alias = "parts")] pub parts: u32, #[serde(alias = "amount_msat")] @@ -1818,13 +2067,18 @@ pub mod responses { /// Type of connection #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum ListnodesNodesAddressesType { + #[serde(rename = "dns")] DNS, + #[serde(rename = "ipv4")] IPV4, + #[serde(rename = "ipv6")] IPV6, + #[serde(rename = "torv2")] TORV2, + #[serde(rename = "torv3")] TORV3, + #[serde(rename = "websocket")] WEBSOCKET, } @@ -1856,7 +2110,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListnodesNodes { #[serde(alias = "nodeid")] - pub nodeid: String, + pub nodeid: Pubkey, #[serde(alias = "last_timestamp", skip_serializing_if = "Option::is_none")] pub last_timestamp: Option, #[serde(alias = "alias", skip_serializing_if = "Option::is_none")] @@ -1865,8 +2119,8 @@ pub mod responses { pub color: Option, #[serde(alias = "features", skip_serializing_if = "Option::is_none")] pub features: Option, - #[serde(alias = "addresses")] - pub addresses: Vec, + #[serde(alias = "addresses", skip_serializing_if = "Option::is_none")] + pub addresses: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -1877,9 +2131,10 @@ pub mod responses { /// Whether it's paid or expired #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum WaitanyinvoiceStatus { + #[serde(rename = "paid")] PAID, + #[serde(rename = "expired")] EXPIRED, } @@ -1900,7 +2155,7 @@ pub mod responses { #[serde(alias = "description")] pub description: String, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `WaitAnyInvoice.status` #[serde(rename = "status")] pub status: WaitanyinvoiceStatus, @@ -1919,14 +2174,15 @@ pub mod responses { #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] pub paid_at: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, } /// Whether it's paid or expired #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum WaitinvoiceStatus { + #[serde(rename = "paid")] PAID, + #[serde(rename = "expired")] EXPIRED, } @@ -1947,7 +2203,7 @@ pub mod responses { #[serde(alias = "description")] pub description: String, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `WaitInvoice.status` #[serde(rename = "status")] pub status: WaitinvoiceStatus, @@ -1966,13 +2222,13 @@ pub mod responses { #[serde(alias = "paid_at", skip_serializing_if = "Option::is_none")] pub paid_at: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, } /// status of the payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum WaitsendpayStatus { + #[serde(rename = "complete")] COMPLETE, } @@ -1992,14 +2248,14 @@ pub mod responses { #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] pub groupid: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, // Path `WaitSendPay.status` #[serde(rename = "status")] pub status: WaitsendpayStatus, #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "amount_sent_msat")] @@ -2013,7 +2269,7 @@ pub mod responses { #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] - pub payment_preimage: Option, + pub payment_preimage: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2036,8 +2292,8 @@ pub mod responses { /// status of payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] - #[serde(rename_all = "lowercase")] pub enum KeysendStatus { + #[serde(rename = "complete")] COMPLETE, } @@ -2053,13 +2309,13 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct KeysendResponse { #[serde(alias = "payment_preimage")] - pub payment_preimage: String, + pub payment_preimage: Secret, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "payment_hash")] - pub payment_hash: String, + pub payment_hash: Sha256, #[serde(alias = "created_at")] - pub created_at: i64, + pub created_at: f64, #[serde(alias = "parts")] pub parts: u32, #[serde(alias = "amount_msat")] @@ -2099,8 +2355,8 @@ pub mod responses { pub excess_msat: Amount, #[serde(alias = "change_outnum", skip_serializing_if = "Option::is_none")] pub change_outnum: Option, - #[serde(alias = "reservations")] - pub reservations: Vec, + #[serde(alias = "reservations", skip_serializing_if = "Option::is_none")] + pub reservations: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2143,8 +2399,8 @@ pub mod responses { pub excess_msat: Amount, #[serde(alias = "change_outnum", skip_serializing_if = "Option::is_none")] pub change_outnum: Option, - #[serde(alias = "reservations")] - pub reservations: Vec, + #[serde(alias = "reservations", skip_serializing_if = "Option::is_none")] + pub reservations: Option>, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2175,5 +2431,249 @@ pub mod responses { pub txid: String, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct DisconnectResponse { + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FeeratesPerkb { + #[serde(alias = "min_acceptable")] + pub min_acceptable: u32, + #[serde(alias = "max_acceptable")] + pub max_acceptable: u32, + #[serde(alias = "opening", skip_serializing_if = "Option::is_none")] + pub opening: Option, + #[serde(alias = "mutual_close", skip_serializing_if = "Option::is_none")] + pub mutual_close: Option, + #[serde(alias = "unilateral_close", skip_serializing_if = "Option::is_none")] + pub unilateral_close: Option, + #[serde(alias = "delayed_to_us", skip_serializing_if = "Option::is_none")] + pub delayed_to_us: Option, + #[serde(alias = "htlc_resolution", skip_serializing_if = "Option::is_none")] + pub htlc_resolution: Option, + #[serde(alias = "penalty", skip_serializing_if = "Option::is_none")] + pub penalty: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FeeratesPerkw { + #[serde(alias = "min_acceptable")] + pub min_acceptable: u32, + #[serde(alias = "max_acceptable")] + pub max_acceptable: u32, + #[serde(alias = "opening", skip_serializing_if = "Option::is_none")] + pub opening: Option, + #[serde(alias = "mutual_close", skip_serializing_if = "Option::is_none")] + pub mutual_close: Option, + #[serde(alias = "unilateral_close", skip_serializing_if = "Option::is_none")] + pub unilateral_close: Option, + #[serde(alias = "delayed_to_us", skip_serializing_if = "Option::is_none")] + pub delayed_to_us: Option, + #[serde(alias = "htlc_resolution", skip_serializing_if = "Option::is_none")] + pub htlc_resolution: Option, + #[serde(alias = "penalty", skip_serializing_if = "Option::is_none")] + pub penalty: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FeeratesOnchain_fee_estimates { + #[serde(alias = "opening_channel_satoshis")] + pub opening_channel_satoshis: u64, + #[serde(alias = "mutual_close_satoshis")] + pub mutual_close_satoshis: u64, + #[serde(alias = "unilateral_close_satoshis")] + pub unilateral_close_satoshis: u64, + #[serde(alias = "htlc_timeout_satoshis")] + pub htlc_timeout_satoshis: u64, + #[serde(alias = "htlc_success_satoshis")] + pub htlc_success_satoshis: u64, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FeeratesResponse { + #[serde(alias = "warning_missing_feerates", skip_serializing_if = "Option::is_none")] + pub warning_missing_feerates: Option, + } + + /// The features understood by the destination node + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + pub enum GetrouteRouteStyle { + #[serde(rename = "tlv")] + TLV, + } + + impl TryFrom for GetrouteRouteStyle { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(GetrouteRouteStyle::TLV), + o => Err(anyhow::anyhow!("Unknown variant {} for enum GetrouteRouteStyle", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetrouteRoute { + #[serde(alias = "id")] + pub id: Pubkey, + #[serde(alias = "channel")] + pub channel: ShortChannelId, + #[serde(alias = "direction")] + pub direction: u32, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, + #[serde(alias = "delay")] + pub delay: u32, + // Path `GetRoute.route[].style` + #[serde(rename = "style")] + pub style: GetrouteRouteStyle, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct GetrouteResponse { + #[serde(alias = "route")] + pub route: Vec, + } + + /// still ongoing, completed, failed locally, or failed after forwarding + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + pub enum ListforwardsForwardsStatus { + #[serde(rename = "offered")] + OFFERED, + #[serde(rename = "settled")] + SETTLED, + #[serde(rename = "local_failed")] + LOCAL_FAILED, + #[serde(rename = "failed")] + FAILED, + } + + impl TryFrom for ListforwardsForwardsStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListforwardsForwardsStatus::OFFERED), + 1 => Ok(ListforwardsForwardsStatus::SETTLED), + 2 => Ok(ListforwardsForwardsStatus::LOCAL_FAILED), + 3 => Ok(ListforwardsForwardsStatus::FAILED), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListforwardsForwardsStatus", o)), + } + } + } + /// Either a legacy onion format or a modern tlv format + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + pub enum ListforwardsForwardsStyle { + #[serde(rename = "legacy")] + LEGACY, + #[serde(rename = "tlv")] + TLV, + } + + impl TryFrom for ListforwardsForwardsStyle { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListforwardsForwardsStyle::LEGACY), + 1 => Ok(ListforwardsForwardsStyle::TLV), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListforwardsForwardsStyle", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListforwardsForwards { + #[serde(alias = "in_channel")] + pub in_channel: ShortChannelId, + #[serde(alias = "in_msat")] + pub in_msat: Amount, + // Path `ListForwards.forwards[].status` + #[serde(rename = "status")] + pub status: ListforwardsForwardsStatus, + #[serde(alias = "received_time")] + pub received_time: f64, + #[serde(alias = "out_channel", skip_serializing_if = "Option::is_none")] + pub out_channel: Option, + #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] + pub payment_hash: Option, + pub style: Option, + #[serde(alias = "fee_msat", skip_serializing_if = "Option::is_none")] + pub fee_msat: Option, + #[serde(alias = "out_msat", skip_serializing_if = "Option::is_none")] + pub out_msat: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListforwardsResponse { + #[serde(alias = "forwards")] + pub forwards: Vec, + } + + /// status of the payment + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] + pub enum ListpaysPaysStatus { + #[serde(rename = "pending")] + PENDING, + #[serde(rename = "failed")] + FAILED, + #[serde(rename = "complete")] + COMPLETE, + } + + impl TryFrom for ListpaysPaysStatus { + type Error = anyhow::Error; + fn try_from(c: i32) -> Result { + match c { + 0 => Ok(ListpaysPaysStatus::PENDING), + 1 => Ok(ListpaysPaysStatus::FAILED), + 2 => Ok(ListpaysPaysStatus::COMPLETE), + o => Err(anyhow::anyhow!("Unknown variant {} for enum ListpaysPaysStatus", o)), + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpaysPays { + #[serde(alias = "payment_hash")] + pub payment_hash: String, + // Path `ListPays.pays[].status` + #[serde(rename = "status")] + pub status: ListpaysPaysStatus, + #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] + pub destination: Option, + #[serde(alias = "created_at")] + pub created_at: u64, + #[serde(alias = "label", skip_serializing_if = "Option::is_none")] + pub label: Option, + #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] + pub bolt11: Option, + #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] + pub bolt12: Option, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, + #[serde(alias = "amount_sent_msat", skip_serializing_if = "Option::is_none")] + pub amount_sent_msat: Option, + #[serde(alias = "erroronion", skip_serializing_if = "Option::is_none")] + pub erroronion: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpaysResponse { + #[serde(alias = "pays")] + pub pays: Vec, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct PingResponse { + #[serde(alias = "totlen")] + pub totlen: u16, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SignmessageResponse { + #[serde(alias = "signature")] + pub signature: String, + #[serde(alias = "recid")] + pub recid: String, + #[serde(alias = "zbase")] + pub zbase: String, + } + } From 591a51ca6a9d3a52319736374f47e9174444a8ac Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 2 Apr 2022 12:47:05 +1030 Subject: [PATCH 0651/1530] pytest: fix invalid invoice() call. Trips on our RPC checking introduced at the same time: msat should be an integer or an "xxxmsat"/sat/btc string. Signed-off-by: Rusty Russell --- tests/test_pay.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pay.py b/tests/test_pay.py index 418c29a039db..985e8f1818d4 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5255,7 +5255,7 @@ def test_pay_bolt11_metadata(node_factory, bitcoind): # After CI started failing, I *also* hacked it to set expiry to BIGNUM. inv = "lnbcrt1230n1p3yzgcxsp5q8g040f9rl9mu2unkjuj0vn262s6nyrhz5hythk3ueu2lfzahmzspp5ve584t0cv27hwmy0cx9ca8uwyqyfw9y9dm3r8vus9fv36r2l9yjsdq8v3jhxccmq6w35xjueqd9ejqmt9w3skgct5vyxqxra2q2qcqp99q2sqqqqqysgqfw6efxpzk5x5vfj8se46yg667x5cvhyttnmuqyk0q7rmhx3gs249qhtdggnek8c5adm2pztkjddlwyn2art2zg9xap2ckczzl3fzz4qqsej6mf" # Make l2 "know" about this invoice. - l2.rpc.invoice(msatoshi='123000', label='label1', description='desc', preimage='00' * 32) + l2.rpc.invoice(msatoshi=123000, label='label1', description='desc', preimage='00' * 32) with pytest.raises(RpcError, match=r'WIRE_INVALID_ONION_PAYLOAD'): l1.rpc.pay(inv) From 92028ebaeee3ee2f37ce2a2065c231c450f181f7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 2 Apr 2022 12:48:05 +1030 Subject: [PATCH 0652/1530] plugins/pay: remove legacypay. I think the new pay command has proven itself in the last 18 months! Also various pay tests took "compat" then didn't use it, so clean them up. Signed-off-by: Rusty Russell Changelog-Removed: JSON-RPC: `legacypay` (`pay` replaced it in 0.9.0). --- plugins/pay.c | 1425 +-------------------------------------------- tests/test_pay.py | 52 +- 2 files changed, 10 insertions(+), 1467 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 9dfedfc25e3f..23a9170c58c7 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -24,63 +24,8 @@ static unsigned int maxdelay_default; static bool exp_offers; static bool disablempp = false; -static LIST_HEAD(pay_status); - static LIST_HEAD(payments); -struct pay_attempt { - /* What we changed when starting this attempt. */ - const char *why; - /* Time we started & finished attempt */ - struct timeabs start, end; - /* Route hint we were using (if any) */ - struct route_info *routehint; - /* Channels we excluded when doing route lookup. */ - const char **excludes; - /* Route we got (NULL == route lookup fail). */ - const char *route; - /* Did we actually try to send a payment? */ - bool sendpay; - /* The failure result (NULL on success) */ - struct json_out *failure; - /* The non-failure result (NULL on failure) */ - const char *result; - /* The blockheight at which the payment attempt was - * started. */ - u32 start_block; -}; - -struct pay_status { - /* Destination, as text */ - const char *dest; - - /* We're in 'pay_status' global list. */ - struct list_node list; - - /* Description user provided (if any) */ - const char *label; - /* Amount they wanted to pay. */ - struct amount_msat msat; - /* CLTV delay required by destination. */ - u32 final_cltv; - /* Bolt11/bolt12 invoice. */ - const char *invstring; - - /* What we did about routehints (if anything) */ - const char *routehint_modifications; - - /* Details of shadow route we chose (if any) */ - char *shadow; - - /* Details of initial exclusions (if any) */ - const char *exclusions; - - /* Array of payment attempts. */ - struct pay_attempt *attempts; - - struct sha256 payment_hash; -}; - struct pay_command { /* Global state */ struct plugin *plugin; @@ -141,1257 +86,6 @@ struct pay_command { const char *shadow_dest; }; -static struct pay_attempt *current_attempt(struct pay_command *pc) -{ - return &pc->ps->attempts[tal_count(pc->ps->attempts)-1]; -} - -/* Helper to copy JSON object directly into a json_out */ -static void json_out_add_raw_len(struct json_out *jout, - const char *fieldname, - const char *jsonstr, size_t len) -{ - char *p; - - p = json_out_member_direct(jout, fieldname, len); - memcpy(p, jsonstr, len); -} - -static struct json_out *failed_start(struct pay_command *pc) -{ - struct pay_attempt *attempt = current_attempt(pc); - - attempt->end = time_now(); - attempt->failure = json_out_new(pc->ps->attempts); - json_out_start(attempt->failure, NULL, '{'); - return attempt->failure; -} - -static void failed_end(struct json_out *jout) -{ - json_out_end(jout, '}'); - json_out_finished(jout); -} - -/* Copy field and member to output, if it exists: return member */ -static const jsmntok_t *copy_member(struct json_out *ret, - const char *buf, const jsmntok_t *obj, - const char *membername) -{ - const jsmntok_t *m = json_get_member(buf, obj, membername); - if (!m) - return NULL; - - /* FIXME: The fact it is a string is probably the wrong thing - * to handle: if it *is* a string we should probably copy - * the quote marks, but json_tok_full/json_tok_full_len - * specifically remove those. - * It works *now* because it is only used in "code" and - * "data": "code" is always numeric, and "data" is usually - * a JSON object/key-value table, but pure stromgs will - * probably result in invalid JSON. - */ - /* Literal copy: it's already JSON escaped, and may be a string. */ - json_out_add_raw_len(ret, membername, - json_tok_full(buf, m), json_tok_full_len(m)); - return m; -} - -/* Copy (and modify) error object. */ -static void attempt_failed_tok(struct pay_command *pc, const char *method, - const char *buf, const jsmntok_t *errtok) -{ - const jsmntok_t *msg = json_get_member(buf, errtok, "message"); - struct json_out *failed = failed_start(pc); - - /* Every JSON error response has code and error. */ - copy_member(failed, buf, errtok, "code"); - json_out_add(failed, "message", true, - "Call to %s: %.*s", - method, msg->end - msg->start, - buf + msg->start); - copy_member(failed, buf, errtok, "data"); - failed_end(failed); -} - -static struct command_result *start_pay_attempt(struct command *cmd, - struct pay_command *pc, - const char *fmt, ...); - -/* Is this (erring) channel within the routehint itself? */ -static bool node_or_channel_in_routehint(struct plugin *plugin, - const struct route_info *routehint, - const char *idstr, size_t idlen) -{ - struct node_id nodeid; - struct short_channel_id scid; - bool node_err = true; - - if (!node_id_from_hexstr(idstr, idlen, &nodeid)) { - if (!short_channel_id_from_str(idstr, idlen, &scid)) - plugin_err(plugin, "bad erring_node or erring_channel '%.*s'", - (int)idlen, idstr); - else - node_err = false; - } - - for (size_t i = 0; i < tal_count(routehint); i++) { - if (node_err) { - if (node_id_eq(&nodeid, &routehint[i].pubkey)) - return true; - } else { - if (short_channel_id_eq(&scid, &routehint[i].short_channel_id)) - return true; - } - } - return false; -} - -/* Count times we actually tried to pay, not where route lookup failed or - * we disliked route for being too expensive, etc. */ -static size_t count_sendpays(const struct pay_attempt *attempts) -{ - size_t n = 0; - - for (size_t i = 0; i < tal_count(attempts); i++) - n += attempts[i].sendpay; - - return n; -} - -static struct command_result *waitsendpay_expired(struct command *cmd, - struct pay_command *pc) -{ - char *errmsg; - struct json_stream *data; - size_t num_attempts = count_sendpays(pc->ps->attempts); - - errmsg = tal_fmt(pc, "Gave up after %zu attempt%s: see paystatus", - num_attempts, num_attempts == 1 ? "" : "s"); - data = jsonrpc_stream_fail(cmd, PAY_STOPPED_RETRYING, errmsg); - json_object_start(data, "data"); - json_array_start(data, "attempts"); - for (size_t i = 0; i < tal_count(pc->ps->attempts); i++) { - json_object_start(data, NULL); - if (pc->ps->attempts[i].route) - json_add_jsonstr(data, "route", - pc->ps->attempts[i].route); - json_out_add_splice(data->jout, "failure", - pc->ps->attempts[i].failure); - json_object_end(data); - } - json_array_end(data); - json_object_end(data); - return command_finished(cmd, data); -} - -static bool routehint_excluded(struct plugin *plugin, - const struct route_info *routehint, - const char **excludes) -{ - /* Note that we ignore direction here: in theory, we could have - * found that one direction of a channel is unavailable, but they - * are suggesting we use it the other way. Very unlikely though! */ - for (size_t i = 0; i < tal_count(excludes); i++) - if (node_or_channel_in_routehint(plugin, - routehint, - excludes[i], - strlen(excludes[i]))) - return true; - return false; -} - -static struct command_result *next_routehint(struct command *cmd, - struct pay_command *pc) -{ - size_t num_attempts = count_sendpays(pc->ps->attempts); - - while (tal_count(pc->routehints) > 0) { - if (!routehint_excluded(pc->plugin, pc->routehints[0], - pc->excludes)) { - pc->current_routehint = pc->routehints[0]; - tal_arr_remove(&pc->routehints, 0); - return start_pay_attempt(cmd, pc, "Trying route hint"); - } - tal_free(pc->routehints[0]); - tal_arr_remove(&pc->routehints, 0); - } - - /* No (more) routehints; we're out of routes. */ - /* If we eliminated one because it was too pricy, return that. */ - if (pc->expensive_route) - return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE, - "%s", pc->expensive_route); - - if (num_attempts > 0) - return command_fail(cmd, PAY_STOPPED_RETRYING, - "Ran out of routes to try after" - " %zu attempt%s: see paystatus", - num_attempts, num_attempts == 1 ? "" : "s"); - - return command_fail(cmd, PAY_ROUTE_NOT_FOUND, - "Could not find a route"); -} - -static struct command_result * -waitblockheight_done(struct command *cmd, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct pay_command *pc) -{ - return start_pay_attempt(cmd, pc, - "Retried due to blockheight " - "disagreement with payee"); -} -static struct command_result * -waitblockheight_error(struct command *cmd, - const char *buf UNUSED, - const jsmntok_t *error UNUSED, - struct pay_command *pc) -{ - if (time_after(time_now(), pc->stoptime)) - return waitsendpay_expired(cmd, pc); - else - /* Ehhh just retry it. */ - return waitblockheight_done(cmd, buf, error, pc); -} - -static struct command_result * -execute_waitblockheight(struct command *cmd, - u32 blockheight, - struct pay_command *pc) -{ - struct out_req *req; - struct timeabs now = time_now(); - struct timerel remaining; - - if (time_after(now, pc->stoptime)) - return waitsendpay_expired(cmd, pc); - - remaining = time_between(pc->stoptime, now); - - req = jsonrpc_request_start(cmd->plugin, cmd, "waitblockheight", - &waitblockheight_done, - &waitblockheight_error, - pc); - json_add_u32(req->js, "blockheight", blockheight); - json_add_u32(req->js, "timeout", time_to_sec(remaining)); - - return send_outreq(cmd->plugin, req); -} - -/* Gets the remote height from a - * WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS - * failure. - * Return 0 if unable to find such a height. - */ -static u32 -get_remote_block_height(const char *buf, const jsmntok_t *error) -{ - const u8 *raw_message; - size_t raw_message_len; - u16 type; - - /* Is there even a raw_message? */ - if (json_scan(tmpctx, buf, error, "{data:{raw_message:%}}", - JSON_SCAN_TAL(tmpctx, json_tok_bin_from_hex, - &raw_message)) != NULL) - return 0; - - /* BOLT #4: - * - * 1. type: PERM|15 (`incorrect_or_unknown_payment_details`) - * 2. data: - * * [`u64`:`htlc_msat`] - * * [`u32`:`height`] - * - */ - raw_message_len = tal_count(raw_message); - - type = fromwire_u16(&raw_message, &raw_message_len); /* type */ - if (type != WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS) - return 0; - - (void) fromwire_u64(&raw_message, &raw_message_len); /* htlc_msat */ - - return fromwire_u32(&raw_message, &raw_message_len); /* height */ -} - -static struct command_result *waitsendpay_error(struct command *cmd, - const char *buf, - const jsmntok_t *error, - struct pay_command *pc) -{ - struct pay_attempt *attempt = current_attempt(pc); - errcode_t code; - int failcode; - const char *err; - - attempt_failed_tok(pc, "waitsendpay", buf, error); - - err = json_scan(tmpctx, buf, error, "{code:%}", - JSON_SCAN(json_to_errcode, &code)); - if (err) - plugin_err(cmd->plugin, "waitsendpay error %s? '%.*s'", - err, - json_tok_full_len(error), json_tok_full(buf, error)); - - if (code != PAY_UNPARSEABLE_ONION) { - err = json_scan(tmpctx, buf, error, "{data:{failcode:%}}", - JSON_SCAN(json_to_int, &failcode)); - if (err) - plugin_err(cmd->plugin, "waitsendpay failcode error %s '%.*s'", - err, - json_tok_full_len(error), json_tok_full(buf, error)); - } - - /* Special case for WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS. - * - * One possible trigger for this failure is that the receiver - * thinks the final timeout it gets is too near the future. - * - * For the most part, we respect the indicated `final_cltv` - * in the invoice, and our shadow routing feature also tends - * to give more timing budget to the receiver than the - * `final_cltv`. - * - * However, there is an edge case possible on real networks: - * - * * We send out a payment respecting the `final_cltv` of - * the receiver. - * * Miners mine a new block while the payment is in transit. - * * By the time the payment reaches the receiver, the - * payment violates the `final_cltv` because the receiver - * is now using a different basis blockheight. - * - * This is a transient error. - * Unfortunately, WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS - * is marked with the PERM bit. - * This means that we would give up on this since `waitsendpay` - * would return PAY_DESTINATION_PERM_FAIL instead of - * PAY_TRY_OTHER_ROUTE. - * Thus the `pay` plugin would not retry this case. - * - * Thus, we need to add this special-case checking here, where - * the blockheight when we started the pay attempt was not - * the same as what the payee reports. - * - * In the past this particular failure had its own failure code, - * equivalent to 17. - * In case the receiver is a really old software, we also - * special-case it here. - */ - if ((code != PAY_UNPARSEABLE_ONION) && - ((failcode == 17) || - ((failcode == WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS) && - (attempt->start_block < get_remote_block_height(buf, error))))) { - u32 target_blockheight; - - if (failcode == 17) - target_blockheight = attempt->start_block + 1; - else - target_blockheight = get_remote_block_height(buf, error); - - return execute_waitblockheight(cmd, target_blockheight, - pc); - } - - /* FIXME: Handle PAY_UNPARSEABLE_ONION! */ - - /* Many error codes are final. */ - if (code != PAY_TRY_OTHER_ROUTE) { - return forward_error(cmd, buf, error, pc); - } - - if (time_after(time_now(), pc->stoptime)) { - return waitsendpay_expired(cmd, pc); - } - - if (failcode & NODE) { - struct node_id id; - const char *idstr, *err; - - err = json_scan(tmpctx, buf, error, "{data:{erring_node:%}}", - JSON_SCAN(json_to_node_id, &id)); - if (err) - plugin_err(cmd->plugin, "waitsendpay error %s '%.*s'", - err, - json_tok_full_len(error), - json_tok_full(buf, error)); - - /* FIXME: Keep as node_id, don't use strings. */ - idstr = node_id_to_hexstr(tmpctx, &id); - - /* If failure is in routehint part, try next one */ - if (node_or_channel_in_routehint(pc->plugin, pc->current_routehint, - idstr, strlen(idstr))) - return next_routehint(cmd, pc); - - /* Otherwise, add erring channel to exclusion list. */ - tal_arr_expand(&pc->excludes, tal_steal(pc->excludes, idstr)); - } else { - struct short_channel_id scid; - u32 dir; - const char *scidstr, *err; - - err = json_scan(tmpctx, buf, error, - "{data:{erring_channel:%,erring_direction:%}}", - JSON_SCAN(json_to_short_channel_id, &scid), - JSON_SCAN(json_to_number, &dir)); - if (err) - plugin_err(cmd->plugin, "waitsendpay error %s '%.*s'", - err, - json_tok_full_len(error), - json_tok_full(buf, error)); - - scidstr = short_channel_id_to_str(tmpctx, &scid); - /* If failure is in routehint part, try next one */ - if (node_or_channel_in_routehint(pc->plugin, pc->current_routehint, - scidstr, strlen(scidstr))) - return next_routehint(cmd, pc); - - /* Otherwise, add erring channel to exclusion list. */ - tal_arr_expand(&pc->excludes, - tal_fmt(pc->excludes, "%s/%u", scidstr, dir)); - } - - /* Try again. */ - return start_pay_attempt(cmd, pc, "Excluded %s %s", - failcode & NODE ? "node" : "channel", - pc->excludes[tal_count(pc->excludes)-1]); -} - -static struct command_result *waitsendpay_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct pay_command *pc) -{ - struct pay_attempt *attempt = current_attempt(pc); - - attempt->result = json_strdup(pc->ps->attempts, buf, result); - attempt->end = time_now(); - - return forward_result(cmd, buf, result, pc); -} - -static struct command_result *sendpay_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct pay_command *pc) -{ - struct out_req *req = jsonrpc_request_start(cmd->plugin, cmd, - "waitsendpay", - waitsendpay_done, - waitsendpay_error, pc); - json_add_string(req->js, "payment_hash", pc->payment_hash); - - return send_outreq(cmd->plugin, req); -} - -/* Calculate how many millisatoshi we need at the start of this route - * to get msatoshi to the end. */ -static bool route_msatoshi(struct amount_msat *total, - const struct amount_msat msat, - const struct route_info *route, size_t num_route) -{ - *total = msat; - for (ssize_t i = num_route - 1; i >= 0; i--) { - if (!amount_msat_add_fee(total, - route[i].fee_base_msat, - route[i].fee_proportional_millionths)) - return false; - } - return true; -} - -/* Calculate cltv we need at the start of this route to get cltv at the end. */ -static u32 route_cltv(u32 cltv, - const struct route_info *route, size_t num_route) -{ - for (size_t i = 0; i < num_route; i++) - cltv += route[i].cltv_expiry_delta; - return cltv; -} - -/* The pubkey to use is the destination of this routehint. */ -static const char *route_pubkey(const tal_t *ctx, - const struct pay_command *pc, - const struct route_info *routehint, - size_t n) -{ - if (n == tal_count(routehint)) - return pc->dest; - return type_to_string(ctx, struct node_id, &routehint[n].pubkey); -} - -static const char *join_routehint(const tal_t *ctx, - const char *buf, - const jsmntok_t *route, - const struct pay_command *pc, - const struct route_info *routehint) -{ - char *ret; - - /* Truncate closing ] from route */ - ret = tal_strndup(ctx, buf + route->start, route->end - route->start - 1); - for (size_t i = 0; i < tal_count(routehint); i++) { - /* amount to be received by *destination* */ - struct amount_msat dest_amount; - - if (!route_msatoshi(&dest_amount, pc->msat, - routehint + i + 1, - tal_count(routehint) - i - 1)) - return tal_free(ret); - - tal_append_fmt(&ret, ", {" - " \"id\": \"%s\"," - " \"channel\": \"%s\"," - " \"msatoshi\": \"%s\"," - " \"delay\": %u }", - /* pubkey of *destination* */ - route_pubkey(tmpctx, pc, routehint, i + 1), - type_to_string(tmpctx, struct short_channel_id, - &routehint[i].short_channel_id), - type_to_string(tmpctx, struct amount_msat, - &dest_amount), - /* cltv for *destination* */ - route_cltv(pc->final_cltv, routehint + i + 1, - tal_count(routehint) - i - 1)); - } - /* Put ] back */ - tal_append_fmt(&ret, "]"); - return ret; -} - -static struct command_result *sendpay_error(struct command *cmd, - const char *buf, - const jsmntok_t *error, - struct pay_command *pc) -{ - attempt_failed_tok(pc, "sendpay", buf, error); - - return forward_error(cmd, buf, error, pc); -} - -static const jsmntok_t *find_worst_channel(const char *buf, - const jsmntok_t *route, - const char *fieldname) -{ - u64 prev, worstval = 0; - const jsmntok_t *worst = NULL, *t, *t_prev = NULL; - size_t i; - - json_for_each_arr(i, t, route) { - u64 val; - - json_to_u64(buf, json_get_member(buf, t, fieldname), &val); - - /* For the first hop, now we can't know if it's the worst. - * Just store the info and continue. */ - if (!i) { - prev = val; - t_prev = t; - continue; - } - - if (worst == NULL || prev - val > worstval) { - worst = t_prev; - worstval = prev - val; - } - prev = val; - t_prev = t; - } - - return worst; -} - -/* Can't exclude if it's in routehint itself. */ -static bool maybe_exclude(struct pay_command *pc, - const char *buf, const jsmntok_t *route) -{ - const jsmntok_t *scid, *dir; - - if (!route) - return false; - - scid = json_get_member(buf, route, "channel"); - - if (node_or_channel_in_routehint(pc->plugin, - pc->current_routehint, - buf + scid->start, - scid->end - scid->start)) - return false; - - dir = json_get_member(buf, route, "direction"); - tal_arr_expand(&pc->excludes, - tal_fmt(pc->excludes, "%.*s/%c", - scid->end - scid->start, - buf + scid->start, - buf[dir->start])); - return true; -} - -static struct command_result *getroute_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct pay_command *pc) -{ - struct pay_attempt *attempt = current_attempt(pc); - const jsmntok_t *t = json_get_member(buf, result, "route"); - struct amount_msat fee; - struct amount_msat max_fee; - u32 delay; - struct out_req *req; - const char *err; - - if (!t) - plugin_err(cmd->plugin, "getroute gave no 'route'? '%.*s'", - result->end - result->start, buf); - - if (pc->current_routehint) { - attempt->route = join_routehint(pc->ps->attempts, buf, t, - pc, pc->current_routehint); - if (!attempt->route) { - struct json_out *failed = failed_start(pc); - json_out_add(failed, "message", true, - "Joining routehint gave absurd fee"); - failed_end(failed); - return next_routehint(cmd, pc); - } - } else - attempt->route = json_strdup(pc->ps->attempts, buf, t); - - err = json_scan(tmpctx, buf, t, "[0:{msatoshi:%,delay:%}]", - JSON_SCAN(json_to_msat, &fee), - JSON_SCAN(json_to_number, &delay)); - if (err) - plugin_err(cmd->plugin, "getroute %s? %.*s", - err, - json_tok_full_len(result), json_tok_full(buf, result)); - - if (pc->maxfee_pct_millionths / 100 > UINT32_MAX) - plugin_err(cmd->plugin, "max fee percent too large: %lf", - pc->maxfee_pct_millionths / 1000000.0); - - if (!amount_msat_fee(&max_fee, pc->msat, 0, - (u32)(pc->maxfee_pct_millionths / 100))) - plugin_err( - cmd->plugin, "max fee too large: %s * %lf%%", - type_to_string(tmpctx, struct amount_msat, &pc->msat), - pc->maxfee_pct_millionths / 1000000.0); - - if (amount_msat_greater(fee, pc->exemptfee) && - amount_msat_greater(fee, max_fee)) { - const jsmntok_t *charger; - struct json_out *failed; - char *feemsg; - - feemsg = tal_fmt(pc, "Route wanted fee of %s", - type_to_string(tmpctx, struct amount_msat, - &fee)); - failed = failed_start(pc); - json_out_addstr(failed, "message", feemsg); - failed_end(failed); - - /* Remember this if we eliminating this causes us to have no - * routes at all! */ - if (!pc->expensive_route) - pc->expensive_route = feemsg; - else - tal_free(feemsg); - - /* Try excluding most fee-charging channel (unless it's in - * routeboost). */ - charger = find_worst_channel(buf, t, "msatoshi"); - if (maybe_exclude(pc, buf, charger)) { - return start_pay_attempt(cmd, pc, - "Excluded expensive channel %s", - pc->excludes[tal_count(pc->excludes)-1]); - } - - return next_routehint(cmd, pc); - } - - if (delay > pc->maxdelay) { - const jsmntok_t *delayer; - struct json_out *failed; - char *feemsg; - - feemsg = tal_fmt(pc, "Route wanted delay of %u blocks", delay); - failed = failed_start(pc); - json_out_addstr(failed, "message", feemsg); - failed_end(failed); - - /* Remember this if we eliminating this causes us to have no - * routes at all! */ - if (!pc->expensive_route) - pc->expensive_route = feemsg; - else - tal_free(failed); - - delayer = find_worst_channel(buf, t, "delay"); - - /* Try excluding most delaying channel (unless it's in - * routeboost). */ - if (maybe_exclude(pc, buf, delayer)) { - return start_pay_attempt(cmd, pc, - "Excluded delaying channel %s", - pc->excludes[tal_count(pc->excludes)-1]); - } - - return next_routehint(cmd, pc); - } - - attempt->sendpay = true; - req = jsonrpc_request_start(cmd->plugin, cmd, "sendpay", - sendpay_done, sendpay_error, pc); - json_add_jsonstr(req->js, "route", attempt->route); - json_add_string(req->js, "payment_hash", pc->payment_hash); - json_add_string(req->js, "bolt11", pc->ps->invstring); - if (pc->label) - json_add_string(req->js, "label", pc->label); - if (pc->payment_secret) - json_add_string(req->js, "payment_secret", pc->payment_secret); - if (pc->payment_metadata) - json_add_string(req->js, "payment_metadata", pc->payment_metadata); - - return send_outreq(cmd->plugin, req); -} - -static struct command_result *getroute_error(struct command *cmd, - const char *buf, - const jsmntok_t *error, - struct pay_command *pc) -{ - errcode_t code; - const jsmntok_t *codetok; - - attempt_failed_tok(pc, "getroute", buf, error); - - codetok = json_get_member(buf, error, "code"); - if (!json_to_errcode(buf, codetok, &code)) - plugin_err(cmd->plugin, "getroute error gave no 'code'? '%.*s'", - error->end - error->start, buf + error->start); - - /* Strange errors from getroute should be forwarded. */ - if (code != PAY_ROUTE_NOT_FOUND) - return forward_error(cmd, buf, error, pc); - - return next_routehint(cmd, pc); -} - -/* Deep copy of excludes array. */ -static const char **dup_excludes(const tal_t *ctx, const char **excludes) -{ - const char **ret = tal_dup_talarr(ctx, const char *, excludes); - for (size_t i = 0; i < tal_count(ret); i++) - ret[i] = tal_strdup(ret, excludes[i]); - return ret; -} - -/* Get a route from the lightningd. */ -static struct command_result *execute_getroute(struct command *cmd, - struct pay_command *pc) -{ - struct pay_attempt *attempt = current_attempt(pc); - - u32 max_hops = ROUTING_MAX_HOPS; - struct amount_msat msat; - const char *dest; - u32 cltv; - struct out_req *req; - - /* routehint set below. */ - - /* If we have a routehint, try that first; we need to do extra - * checks that it meets our criteria though. */ - if (pc->current_routehint) { - attempt->routehint = tal_steal(pc->ps, pc->current_routehint); - if (!route_msatoshi(&msat, pc->msat, - attempt->routehint, - tal_count(attempt->routehint))) { - struct json_out *failed; - - failed = failed_start(pc); - json_out_addstr(failed, "message", - "Routehint absurd fee"); - failed_end(failed); - return next_routehint(cmd, pc); - } - dest = type_to_string(tmpctx, struct node_id, - &attempt->routehint[0].pubkey); - max_hops -= tal_count(attempt->routehint); - cltv = route_cltv(pc->final_cltv, - attempt->routehint, - tal_count(attempt->routehint)); - } else { - msat = pc->msat; - dest = pc->dest; - cltv = pc->final_cltv; - attempt->routehint = NULL; - } - - /* OK, ask for route to destination */ - req = jsonrpc_request_start(cmd->plugin, cmd, "getroute", - getroute_done, getroute_error, pc); - json_add_string(req->js, "id", dest); - json_add_string(req->js, "msatoshi", - type_to_string(tmpctx, struct amount_msat, &msat)); - json_add_u32(req->js, "cltv", cltv); - json_add_u32(req->js, "maxhops", max_hops); - json_add_member(req->js, "riskfactor", false, "%lf", - pc->riskfactor_millionths / 1000000.0); - if (tal_count(pc->excludes) != 0) { - json_array_start(req->js, "exclude"); - for (size_t i = 0; i < tal_count(pc->excludes); i++) - json_add_string(req->js, NULL, pc->excludes[i]); - json_array_end(req->js); - } - - return send_outreq(cmd->plugin, req); -} - -static struct command_result * -getstartblockheight_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct pay_command *pc) -{ - const jsmntok_t *blockheight_tok; - u32 blockheight; - - blockheight_tok = json_get_member(buf, result, "blockheight"); - if (!blockheight_tok) - plugin_err(cmd->plugin, "getstartblockheight: " - "getinfo gave no 'blockheight'? '%.*s'", - result->end - result->start, buf); - - if (!json_to_u32(buf, blockheight_tok, &blockheight)) - plugin_err(cmd->plugin, "getstartblockheight: " - "getinfo gave non-unsigned-32-bit 'blockheight'? '%.*s'", - result->end - result->start, buf); - - current_attempt(pc)->start_block = blockheight; - - return execute_getroute(cmd, pc); -} - -static struct command_result * -getstartblockheight_error(struct command *cmd, - const char *buf, - const jsmntok_t *error, - struct pay_command *pc) -{ - /* Should never happen. */ - plugin_err(cmd->plugin, "getstartblockheight: getinfo failed!? '%.*s'", - error->end - error->start, buf); -} - -static struct command_result * -execute_getstartblockheight(struct command *cmd, - struct pay_command *pc) -{ - struct out_req *req = jsonrpc_request_start(cmd->plugin, cmd, "getinfo", - &getstartblockheight_done, - &getstartblockheight_error, - pc); - return send_outreq(cmd->plugin, req); -} - -static struct command_result *start_pay_attempt(struct command *cmd, - struct pay_command *pc, - const char *fmt, ...) -{ - struct pay_attempt *attempt; - va_list ap; - size_t n; - - n = tal_count(pc->ps->attempts); - tal_resize(&pc->ps->attempts, n+1); - attempt = &pc->ps->attempts[n]; - - va_start(ap, fmt); - attempt->start = time_now(); - /* Mark it unfinished */ - attempt->end.ts.tv_sec = -1; - attempt->excludes = dup_excludes(pc->ps, pc->excludes); - attempt->route = NULL; - attempt->failure = NULL; - attempt->result = NULL; - attempt->sendpay = false; - attempt->why = tal_vfmt(pc->ps, fmt, ap); - va_end(ap); - - return execute_getstartblockheight(cmd, pc); -} - -/* BOLT #7: - * - * If a route is computed by simply routing to the intended recipient and - * summing the `cltv_expiry_delta`s, then it's possible for intermediate nodes - * to guess their position in the route. Knowing the CLTV of the HTLC, the - * surrounding network topology, and the `cltv_expiry_delta`s gives an - * attacker a way to guess the intended recipient. Therefore, it's highly - * desirable to add a random offset to the CLTV that the intended recipient - * will receive, which bumps all CLTVs along the route. - * - * In order to create a plausible offset, the origin node MAY start a limited - * random walk on the graph, starting from the intended recipient and summing - * the `cltv_expiry_delta`s, and use the resulting sum as the offset. This - * effectively creates a _shadow route extension_ to the actual route and - * provides better protection against this attack vector than simply picking a - * random offset would. - */ -static struct command_result *shadow_route(struct command *cmd, - struct pay_command *pc); - -static struct command_result *add_shadow_route(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct pay_command *pc) -{ - /* Use reservoir sampling across the capable channels. */ - const jsmntok_t *channels = json_get_member(buf, result, "channels"); - const jsmntok_t *chan, *best = NULL; - size_t i; - u64 sample = 0; - struct route_info *route = tal_arr(NULL, struct route_info, 1); - struct amount_msat fees, maxfees; - /* Don't go above this. Note how we use the initial amount to get the percentage - * of the fees, or it would increase with the addition of new shadow routes. */ - if (!amount_msat_fee(&maxfees, pc->initial_msat, 0, pc->maxfee_pct_millionths)) - plugin_err(cmd->plugin, "Overflow when computing maxfees for " - "shadow routes."); - - json_for_each_arr(i, chan, channels) { - u64 v = pseudorand(UINT64_MAX); - - if (!best || v > sample) { - struct amount_sat sat; - - json_to_sat(buf, json_get_member(buf, chan, "satoshis"), &sat); - if (amount_msat_greater_sat(pc->msat, sat)) - continue; - - /* Don't use if total would exceed 1/4 of our time allowance. */ - json_to_u16(buf, json_get_member(buf, chan, "delay"), - &route[0].cltv_expiry_delta); - if ((pc->final_cltv + route[0].cltv_expiry_delta) * 4 > pc->maxdelay) - continue; - - json_to_number(buf, json_get_member(buf, chan, "base_fee_millisatoshi"), - &route[0].fee_base_msat); - json_to_number(buf, json_get_member(buf, chan, "fee_per_millionth"), - &route[0].fee_proportional_millionths); - - if (!amount_msat_fee(&fees, pc->initial_msat, route[0].fee_base_msat, - route[0].fee_proportional_millionths) - || amount_msat_greater_eq(fees, maxfees)) - continue; - - best = chan; - sample = v; - } - } - - if (!best) { - tal_append_fmt(&pc->ps->shadow, - "No suitable channels found to %s. ", - pc->shadow_dest); - return start_pay_attempt(cmd, pc, "Initial attempt"); - } - - pc->final_cltv += route[0].cltv_expiry_delta; - pc->shadow_dest = json_strdup(pc, buf, - json_get_member(buf, best, "destination")); - route_msatoshi(&pc->msat, pc->msat, route, 1); - tal_append_fmt(&pc->ps->shadow, - "Added %u cltv delay, %u base fee, and %u ppm fee " - "for shadow to %s.", - route[0].cltv_expiry_delta, route[0].fee_base_msat, - route[0].fee_proportional_millionths, - pc->shadow_dest); - tal_free(route); - - return shadow_route(cmd, pc); -} - -static struct command_result *shadow_route(struct command *cmd, - struct pay_command *pc) -{ - struct out_req *req; - -#if DEVELOPER - if (!pc->use_shadow) - return start_pay_attempt(cmd, pc, "Initial attempt"); -#endif - if (pseudorand(2) == 0) - return start_pay_attempt(cmd, pc, "Initial attempt"); - - req = jsonrpc_request_start(cmd->plugin, cmd, "listchannels", - add_shadow_route, forward_error, pc); - json_add_string(req->js, "source", pc->shadow_dest); - return send_outreq(cmd->plugin, req); -} - -/* gossipd doesn't know much about the current state of channels; here we - * manually exclude peers which are disconnected and channels which lack - * current capacity (it will eliminate those without total capacity). */ -static struct command_result *listpeers_done(struct command *cmd, - const char *buf, - const jsmntok_t *result, - struct pay_command *pc) -{ - const jsmntok_t *peers, *peer; - size_t i; - char *mods = tal_strdup(tmpctx, ""); - - peers = json_get_member(buf, result, "peers"); - if (!peers) - plugin_err(cmd->plugin, "listpeers gave no 'peers'? '%.*s'", - result->end - result->start, buf); - - json_for_each_arr(i, peer, peers) { - const jsmntok_t *chans, *chan; - bool connected; - size_t j; - - json_to_bool(buf, json_get_member(buf, peer, "connected"), - &connected); - chans = json_get_member(buf, peer, "channels"); - json_for_each_arr(j, chan, chans) { - const jsmntok_t *state, *scid, *dir; - struct amount_msat spendable; - - /* gossipd will only consider things in state NORMAL - * anyway; we don't need to exclude others. */ - state = json_get_member(buf, chan, "state"); - if (!json_tok_streq(buf, state, "CHANNELD_NORMAL")) - continue; - - json_to_msat(buf, - json_get_member(buf, chan, - "spendable_msatoshi"), - &spendable); - - if (connected - && amount_msat_greater_eq(spendable, pc->msat)) - continue; - - /* Exclude this disconnected or low-capacity channel */ - scid = json_get_member(buf, chan, "short_channel_id"); - dir = json_get_member(buf, chan, "direction"); - tal_arr_expand(&pc->excludes, - tal_fmt(pc->excludes, "%.*s/%c", - scid->end - scid->start, - buf + scid->start, - buf[dir->start])); - - tal_append_fmt(&mods, - "Excluded channel %s (%s, %s). ", - pc->excludes[tal_count(pc->excludes)-1], - type_to_string(tmpctx, struct amount_msat, - &spendable), - connected ? "connected" : "disconnected"); - } - } - - if (!streq(mods, "")) - pc->ps->exclusions = tal_steal(pc->ps, mods); - - pc->ps->shadow = tal_strdup(pc->ps, ""); - return shadow_route(cmd, pc); -} - -/* Trim route to this length by taking from the *front* of route - * (end points to destination, so we need that bit!) */ -static void trim_route(struct route_info **route, size_t n) -{ - size_t remove = tal_count(*route) - n; - memmove(*route, *route + remove, sizeof(**route) * n); - tal_resize(route, n); -} - -/* Make sure routehints are reasonable length, and (since we assume we - * can append), not directly to us. Note: untrusted data! */ -static struct route_info **filter_routehints(struct pay_command *pc, - struct route_info **hints) -{ - char *mods = tal_strdup(tmpctx, ""); - - for (size_t i = 0; i < tal_count(hints); i++) { - /* Trim any routehint > 10 hops */ - size_t max_hops = ROUTING_MAX_HOPS / 2; - if (tal_count(hints[i]) > max_hops) { - tal_append_fmt(&mods, - "Trimmed routehint %zu (%zu hops) to %zu. ", - i, tal_count(hints[i]), max_hops); - trim_route(&hints[i], max_hops); - } - - /* If we are first hop, trim. */ - if (tal_count(hints[i]) > 0 - && node_id_eq(&hints[i][0].pubkey, &my_id)) { - tal_append_fmt(&mods, - "Removed ourselves from routehint %zu. ", - i); - trim_route(&hints[i], tal_count(hints[i])-1); - } - - /* If route is empty, remove altogether. */ - if (tal_count(hints[i]) == 0) { - tal_append_fmt(&mods, - "Removed empty routehint %zu. ", i); - tal_arr_remove(&hints, i); - i--; - } - } - - if (!streq(mods, "")) - pc->ps->routehint_modifications = tal_steal(pc->ps, mods); - - return tal_steal(pc, hints); -} - -static struct pay_status *add_pay_status(struct pay_command *pc, - const char *invstring STEALS) -{ - struct pay_status *ps = tal(NULL, struct pay_status); - - /* The pay_status outlives the pc, so it simply takes field ownership */ - ps->dest = tal_steal(ps, pc->dest); - ps->label = tal_steal(ps, pc->label); - ps->msat = pc->msat; - ps->final_cltv = pc->final_cltv; - ps->invstring = tal_steal(ps, invstring); - ps->routehint_modifications = NULL; - ps->shadow = NULL; - ps->exclusions = NULL; - ps->attempts = tal_arr(ps, struct pay_attempt, 0); - hex_decode(pc->payment_hash, strlen(pc->payment_hash), - &ps->payment_hash, sizeof(ps->payment_hash)); - - list_add_tail(&pay_status, &ps->list); - return ps; -} - -#ifndef COMPAT_V090 -UNUSED -#endif -static struct command_result *json_pay(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - struct amount_msat *msat; - struct bolt11 *b11; - const char *b11str; - char *fail; - u64 *riskfactor_millionths; - unsigned int *retryfor; - struct pay_command *pc = tal(cmd, struct pay_command); - u64 *maxfee_pct_millionths; - unsigned int *maxdelay; - struct amount_msat *exemptfee; - struct out_req *req; -#if DEVELOPER - bool *use_shadow; -#endif - - if (!param(cmd, buf, params, p_req("bolt11", param_string, &b11str), - p_opt("msatoshi", param_msat, &msat), - p_opt("label", param_string, &pc->label), - p_opt_def("riskfactor", param_millionths, - &riskfactor_millionths, 10000000), - p_opt_def("maxfeepercent", param_millionths, - &maxfee_pct_millionths, 500000), - p_opt_def("retry_for", param_number, &retryfor, 60), - p_opt_def("maxdelay", param_number, &maxdelay, - maxdelay_default), - p_opt_def("exemptfee", param_msat, &exemptfee, - AMOUNT_MSAT(5000)), -#if DEVELOPER - p_opt_def("use_shadow", param_bool, &use_shadow, true), -#endif - NULL)) - return command_param_failed(); - - b11 = bolt11_decode(cmd, b11str, plugin_feature_set(cmd->plugin), - NULL, chainparams, &fail); - if (!b11) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid bolt11: %s", fail); - } - - if (!b11->chain) { - return command_fail(cmd, PAY_ROUTE_NOT_FOUND, "Invoice is for an unknown network"); - } - - if (b11->chain != chainparams) { - return command_fail(cmd, PAY_ROUTE_NOT_FOUND, "Invoice is for another network %s", b11->chain->network_name); - } - - if (time_now().ts.tv_sec > b11->timestamp + b11->expiry) { - return command_fail(cmd, PAY_INVOICE_EXPIRED, "Invoice expired"); - } - - if (b11->msat) { - if (msat) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "msatoshi parameter unnecessary"); - } - pc->msat = pc->initial_msat = *b11->msat; - } else { - if (!msat) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "msatoshi parameter required"); - } - pc->msat = pc->initial_msat = *msat; - } - - /* Sanity check */ - if (feature_offered(b11->features, OPT_VAR_ONION) - && !b11->payment_secret) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Invalid bolt11:" - " sets feature var_onion with no secret"); - } - - pc->maxfee_pct_millionths = *maxfee_pct_millionths; - pc->maxdelay = *maxdelay; - pc->exemptfee = *exemptfee; - pc->riskfactor_millionths = *riskfactor_millionths; - pc->final_cltv = b11->min_final_cltv_expiry; - pc->dest = type_to_string(cmd, struct node_id, &b11->receiver_id); - pc->shadow_dest = tal_strdup(pc, pc->dest); - pc->payment_hash = type_to_string(pc, struct sha256, - &b11->payment_hash); - pc->stoptime = timeabs_add(time_now(), time_from_sec(*retryfor)); - pc->excludes = tal_arr(cmd, const char *, 0); - pc->ps = add_pay_status(pc, b11str); - if (b11->payment_secret) - pc->payment_secret = tal_hexstr(pc, b11->payment_secret, - sizeof(*b11->payment_secret)); - else - pc->payment_secret = NULL; - if (b11->metadata) - pc->payment_metadata = tal_hex(pc, b11->metadata); - else - pc->payment_metadata = NULL; - - /* We try first without using routehint */ - pc->current_routehint = NULL; - pc->routehints = filter_routehints(pc, b11->routes); - pc->expensive_route = NULL; -#if DEVELOPER - pc->use_shadow = *use_shadow; -#endif - - /* Get capacities of local channels (no parameters) */ - req = jsonrpc_request_start(cmd->plugin, cmd, "listpeers", - listpeers_done, forward_error, pc); - return send_outreq(cmd->plugin, req); -} - /* FIXME: Add this to ccan/time? */ #define UTC_TIMELEN (sizeof("YYYY-mm-ddTHH:MM:SS.nnnZ")) static void utc_timestring(const struct timeabs *time, char str[UTC_TIMELEN]) @@ -1410,66 +104,6 @@ static void utc_timestring(const struct timeabs *time, char str[UTC_TIMELEN]) (int) time->ts.tv_nsec / 1000000); } -static void add_attempt(struct json_stream *ret, - const struct pay_status *ps, - const struct pay_attempt *attempt) -{ - char timestr[UTC_TIMELEN]; - - utc_timestring(&attempt->start, timestr); - - json_object_start(ret, NULL); - json_add_string(ret, "strategy", attempt->why); - json_add_string(ret, "start_time", timestr); - json_add_u64(ret, "age_in_seconds", - time_to_sec(time_between(time_now(), attempt->start))); - if (attempt->result || attempt->failure) { - utc_timestring(&attempt->end, timestr); - json_add_string(ret, "end_time", timestr); - json_add_u64(ret, "duration_in_seconds", - time_to_sec(time_between(attempt->end, - attempt->start))); - } - if (tal_count(attempt->routehint)) { - json_array_start(ret, "routehint"); - for (size_t i = 0; i < tal_count(attempt->routehint); i++) { - json_object_start(ret, NULL); - json_add_string(ret, "id", - type_to_string(tmpctx, struct node_id, - &attempt->routehint[i].pubkey)); - json_add_string(ret, "channel", - type_to_string(tmpctx, - struct short_channel_id, - &attempt->routehint[i].short_channel_id)); - json_add_u64(ret, "fee_base_msat", - attempt->routehint[i].fee_base_msat); - json_add_u64(ret, "fee_proportional_millionths", - attempt->routehint[i].fee_proportional_millionths); - json_add_u64(ret, "cltv_expiry_delta", - attempt->routehint[i].cltv_expiry_delta); - json_object_end(ret); - } - json_array_end(ret); - } - if (tal_count(attempt->excludes)) { - json_array_start(ret, "excluded_nodes_or_channels"); - for (size_t i = 0; i < tal_count(attempt->excludes); i++) - json_add_string(ret, NULL, attempt->excludes[i]); - json_array_end(ret); - } - - if (attempt->route) - json_add_jsonstr(ret, "route", attempt->route); - - if (attempt->failure) - json_out_add_splice(ret->jout, "failure", attempt->failure); - - if (attempt->result) - json_add_member(ret, "success", true, "%s", attempt->result); - - json_object_end(ret); -} - static void json_add_sendpay_result(struct json_stream *s, const struct payment_result *r) { if (r->code != 0) { @@ -1558,7 +192,6 @@ static struct command_result *json_paystatus(struct command *cmd, const char *buf, const jsmntok_t *params) { - struct pay_status *ps; const char *invstring; struct json_stream *ret; struct payment *p; @@ -1572,38 +205,6 @@ static struct command_result *json_paystatus(struct command *cmd, ret = jsonrpc_stream_success(cmd); json_array_start(ret, "pay"); - /* FIXME: Index by bolt11 string! */ - /* TODO(cdecker) Remove once we migrated to `pay` with modifiers. */ - list_for_each(&pay_status, ps, list) { - if (invstring && !streq(invstring, ps->invstring)) - continue; - - json_object_start(ret, NULL); - json_add_invstring(ret, ps->invstring); - json_add_u64(ret, "msatoshi", - ps->msat.millisatoshis); /* Raw: JSON */ - json_add_string(ret, "amount_msat", - type_to_string(tmpctx, struct amount_msat, - &ps->msat)); - json_add_string(ret, "destination", ps->dest); - if (ps->label) - json_add_string(ret, "label", ps->label); - if (ps->routehint_modifications) - json_add_string(ret, "routehint_modifications", - ps->routehint_modifications); - if (ps->shadow && !streq(ps->shadow, "")) - json_add_string(ret, "shadow", ps->shadow); - if (ps->exclusions) - json_add_string(ret, "local_exclusions", ps->exclusions); - - /* If it's in listpeers right now, this can be 0 */ - json_array_start(ret, "attempts"); - for (size_t i = 0; i < tal_count(ps->attempts); i++) - add_attempt(ret, ps, &ps->attempts[i]); - json_array_end(ret); - json_object_end(ret); - } - list_for_each(&payments, p, list) { assert(p->parent == NULL); if (invstring && !streq(invstring, p->invstring)) @@ -1637,20 +238,11 @@ static struct command_result *json_paystatus(struct command *cmd, static bool attempt_ongoing(const struct sha256 *payment_hash) { - struct pay_status *ps; struct payment *root; - struct pay_attempt *attempt; struct payment_tree_result res; enum payment_step diff, final_states = PAYMENT_STEP_FAILED | PAYMENT_STEP_SUCCESS; - list_for_each(&pay_status, ps, list) { - if (!sha256_eq(payment_hash, &ps->payment_hash)) - continue; - attempt = &ps->attempts[tal_count(ps->attempts)-1]; - return attempt->result == NULL && attempt->failure == NULL; - } - list_for_each(&payments, root, list) { if (!sha256_eq(payment_hash, root->payment_hash)) continue; @@ -2323,9 +915,9 @@ static void destroy_payment(struct payment *p) list_del(&p->list); } -static struct command_result *json_paymod(struct command *cmd, - const char *buf, - const jsmntok_t *params) +static struct command_result *json_pay(struct command *cmd, + const char *buf, + const jsmntok_t *params) { struct payment *p; const char *b11str; @@ -2548,15 +1140,6 @@ static struct command_result *json_paymod(struct command *cmd, } static const struct plugin_command commands[] = { -#ifdef COMPAT_V090 - { - "legacypay", - "payment", - "Send payment specified by {bolt11} with {amount}", - "Try to send a payment, retrying {retry_for} seconds before giving up", - json_pay - }, -#endif { "paystatus", "payment", @@ -2575,7 +1158,7 @@ static const struct plugin_command commands[] = { "payment", "Send payment specified by {bolt11}", "Attempt to pay the {bolt11} invoice.", - json_paymod + json_pay }, }; diff --git a/tests/test_pay.py b/tests/test_pay.py index 985e8f1818d4..ac68c7b40ae2 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -7,7 +7,7 @@ from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT from utils import ( DEVELOPER, wait_for, only_one, sync_blockheight, TIMEOUT, - EXPERIMENTAL_FEATURES, env, VALGRIND, mine_funding_to_announce + EXPERIMENTAL_FEATURES, VALGRIND, mine_funding_to_announce ) import copy import os @@ -90,7 +90,7 @@ def test_pay_amounts(node_factory): @pytest.mark.developer("needs to deactivate shadow routing") -def test_pay_limits(node_factory, compat): +def test_pay_limits(node_factory): """Test that we enforce fee max percentage and max delay""" l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) @@ -346,7 +346,7 @@ def test_pay_error_update_fees(node_factory): @pytest.mark.developer("needs to deactivate shadow routing") -def test_pay_optional_args(node_factory, compat): +def test_pay_optional_args(node_factory): l1, l2 = node_factory.line_graph(2) inv1 = l2.rpc.invoice(123000, 'test_pay', 'desc')['bolt11'] @@ -1779,7 +1779,7 @@ def listpays_nofail(b11): @pytest.mark.developer("needs DEVELOPER=1 otherwise gossip takes 5 minutes!") @pytest.mark.slow_test -def test_pay_routeboost(node_factory, bitcoind, compat): +def test_pay_routeboost(node_factory, bitcoind): """Make sure we can use routeboost information. """ # l1->l2->l3--private-->l4 l1, l2 = node_factory.line_graph(2, announce_channels=True, wait_for_announce=True) @@ -3448,7 +3448,7 @@ def test_sendpay_blinding(node_factory): l1.rpc.waitsendpay(inv['payment_hash']) -def test_excluded_adjacent_routehint(node_factory, bitcoind, compat): +def test_excluded_adjacent_routehint(node_factory, bitcoind): """Test case where we try have a routehint which leads to an adjacent node, but the result exceeds our maxfee; we crashed trying to find what part of the path was most expensive in that case @@ -3615,7 +3615,7 @@ def test_invalid_onion_channel_update(node_factory): @pytest.mark.developer("Requires use_shadow") -def test_pay_exemptfee(node_factory, compat): +def test_pay_exemptfee(node_factory): """Tiny payment, huge fee l1 -> l2 -> l3 @@ -3972,46 +3972,6 @@ def test_listpay_result_with_paymod(node_factory, bitcoind): assert 'destination' in l2.rpc.listpays()['pays'][0] -@unittest.skipIf(env('COMPAT') != 1, "legacypay requires COMPAT=1") -def test_listpays_ongoing_attempt(node_factory, bitcoind, executor): - """Test to reproduce issue #3915. - - The issue is that the bolt11 string is not initialized if the root payment - was split (no attempt with the bolt11 annotation ever hit `lightningd`, - hence we cannot filter by that. In addition keysends never have a bolt11 - string, so we need to switch to payment_hash comparisons anyway. - """ - plugin = os.path.join(os.path.dirname(__file__), 'plugins', 'hold_htlcs.py') - l1, l2, l3 = node_factory.line_graph(3, opts=[{}, {}, {'plugin': plugin}], - wait_for_announce=True) - - f = executor.submit(l1.rpc.keysend, l3.info['id'], 100) - l3.daemon.wait_for_log(r'Holding onto an incoming htlc') - l1.rpc.listpays() - f.result() - - inv = l2.rpc.invoice(10**6, 'legacy', 'desc')['bolt11'] - l1.rpc.legacypay(inv) - l1.rpc.listpays() - - # Produce loads of parts to increase probability of hitting the issue, - # should result in 100 splits at least - inv = l3.rpc.invoice(10**9, 'mpp invoice', 'desc')['bolt11'] - - # Start the payment, it'll get stuck for 10 seconds at l3 - executor.submit(l1.rpc.pay, inv) - l1.daemon.wait_for_log(r'Split into [0-9]+ sub-payments due to initial size') - l3.daemon.wait_for_log(r'Holding onto an incoming htlc') - - # While that is going on, check in with `listpays` to see if aggregation - # is working. - l1.rpc.listpays() - - # Now restart and see if we still can aggregate things correctly. - l1.restart() - 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) From 713cd0540dc0856c0516e3f405581697934e26bb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 2 Apr 2022 12:49:05 +1030 Subject: [PATCH 0653/1530] doc: document `pay` localofferid field. Fixes: #4665 Reported-by: @shesek Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 3 ++- doc/lightning-pay.7.md | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 3433eb10acfb..5b5d93298295 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -997,7 +997,7 @@ def newaddr(self, addresstype=None): def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, - maxdelay=None, exemptfee=None, exclude=None): + maxdelay=None, exemptfee=None, localofferid=None, exclude=None): """ Send payment specified by {bolt11} with {msatoshi} (ignored if {bolt11} has an amount), optional {label} @@ -1012,6 +1012,7 @@ def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, "retry_for": retry_for, "maxdelay": maxdelay, "exemptfee": exemptfee, + "localofferid": localofferid, "exclude": exclude, } return self.call("pay", payload) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index f563dddbedd9..2b52c6e54052 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -6,7 +6,7 @@ SYNOPSIS **pay** *bolt11* [*msatoshi*] [*label*] [*riskfactor*] [*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*] -[*exclude*] +[*localofferid*] [*exclude*] DESCRIPTION ----------- @@ -32,6 +32,11 @@ leveraged by forwarding nodes. Setting `exemptfee` allows the `maxfeepercent` check to be skipped on fees that are smaller than `exemptfee` (default: 5000 millisatoshi). +`localofferid` is used by offers to link a payment attempt to a local +`send_invoice` offer created by lightningd-offerout(7). This ensures +that we only make a single payment for an offer, and that the offer is +marked `used` once paid. + The response will occur when the payment fails or succeeds. Once a payment has succeeded, calls to **pay** with the same *bolt11* will succeed immediately. From 6c54e0e7e75e6b0a917edbd87bec3ecafff27732 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 2 Apr 2022 12:55:04 +1030 Subject: [PATCH 0654/1530] pay: add absolute "maxfee" parameter. This is what LND does, and it's better for upper layers than trying to twist our maxfeepercent / exemptfee heuristics to suit. (I don't remember who complained about this, sorry!) I'm doing this now because I want to add YA parameter next! Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `pay` has new parameter `maxfee` for setting absolute fee (instead of using `maxfeepercent` and/or `exemptfee`) --- contrib/pyln-client/pyln/client/lightning.py | 4 +- doc/lightning-pay.7.md | 10 ++++- doc/schemas/pay.request.json | 3 ++ plugins/pay.c | 45 ++++++++++++++------ tests/test_pay.py | 7 ++- tests/test_plugin.py | 3 +- 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 5b5d93298295..4996c81fb003 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -997,7 +997,8 @@ def newaddr(self, addresstype=None): def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, - maxdelay=None, exemptfee=None, localofferid=None, exclude=None): + maxdelay=None, exemptfee=None, localofferid=None, exclude=None, + maxfee=None): """ Send payment specified by {bolt11} with {msatoshi} (ignored if {bolt11} has an amount), optional {label} @@ -1014,6 +1015,7 @@ def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, "exemptfee": exemptfee, "localofferid": localofferid, "exclude": exclude, + "maxfee": maxfee, } return self.call("pay", payload) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index 2b52c6e54052..dd1ff3ad2fdf 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -5,8 +5,8 @@ SYNOPSIS -------- **pay** *bolt11* [*msatoshi*] [*label*] [*riskfactor*] -[*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*] -[*localofferid*] [*exclude*] +[*maxfeepercent*] [*retry_for*] [*maxdelay*] [*exemptfee*] +[*localofferid*] [*exclude*] [*maxfee*] DESCRIPTION ----------- @@ -37,6 +37,12 @@ leveraged by forwarding nodes. Setting `exemptfee` allows the that we only make a single payment for an offer, and that the offer is marked `used` once paid. +*maxfee* overrides both *maxfeepercent* and *exemptfee* defaults (and +if you specify *maxfee* you cannot specify either of those), and +creates an absolute limit on what fee we will pay. This allows you to +implement your own heuristics rather than the primitive ones used +here. + The response will occur when the payment fails or succeeds. Once a payment has succeeded, calls to **pay** with the same *bolt11* will succeed immediately. diff --git a/doc/schemas/pay.request.json b/doc/schemas/pay.request.json index d7bcb5f85a15..b6dc27a1a1ee 100644 --- a/doc/schemas/pay.request.json +++ b/doc/schemas/pay.request.json @@ -45,6 +45,9 @@ } ] } + }, + "maxfee": { + "type": "msat" } } } diff --git a/plugins/pay.c b/plugins/pay.c index 23a9170c58c7..7bc0eb92cd30 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -925,7 +925,7 @@ static struct command_result *json_pay(struct command *cmd, char *b11_fail, *b12_fail; u64 *maxfee_pct_millionths; u32 *maxdelay; - struct amount_msat *exemptfee, *msat; + struct amount_msat *exemptfee, *msat, *maxfee; const char *label; unsigned int *retryfor; u64 *riskfactor_millionths; @@ -950,14 +950,15 @@ static struct command_result *json_pay(struct command *cmd, p_opt("label", param_string, &label), p_opt_def("riskfactor", param_millionths, &riskfactor_millionths, 10000000), - p_opt_def("maxfeepercent", param_millionths, - &maxfee_pct_millionths, 500000), + p_opt("maxfeepercent", param_millionths, + &maxfee_pct_millionths), p_opt_def("retry_for", param_number, &retryfor, 60), p_opt_def("maxdelay", param_number, &maxdelay, maxdelay_default), - p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), + p_opt("exemptfee", param_msat, &exemptfee), p_opt("localofferid", param_sha256, &local_offer_id), p_opt("exclude", param_route_exclusion_array, &exclusions), + p_opt("maxfee", param_msat, &maxfee), #if DEVELOPER p_opt_def("use_shadow", param_bool, &use_shadow, true), #endif @@ -1103,17 +1104,35 @@ static struct command_result *json_pay(struct command *cmd, p->getroute->riskfactorppm = *riskfactor_millionths; tal_free(riskfactor_millionths); - /* We free unneeded params as we use them, to keep memleak happy. */ - if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0, - *maxfee_pct_millionths / 100)) { - return command_fail( - cmd, JSONRPC2_INVALID_PARAMS, - "Overflow when computing fee budget, fee rate too high."); + if (maxfee) { + if (maxfee_pct_millionths || exemptfee) { + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "If you specify maxfee, cannot specify maxfeepercent or exemptfee."); + } + p->constraints.fee_budget = *maxfee; + payment_mod_exemptfee_get_data(p)->amount = AMOUNT_MSAT(0); + } else { + u64 maxppm; + + if (maxfee_pct_millionths) + maxppm = *maxfee_pct_millionths / 100; + else + maxppm = 500000 / 100; + if (!amount_msat_fee(&p->constraints.fee_budget, p->amount, 0, + maxppm)) { + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "Overflow when computing fee budget, fee rate too high."); + } + payment_mod_exemptfee_get_data(p)->amount + = exemptfee ? *exemptfee : AMOUNT_MSAT(5000); + + /* We free unneeded params now to keep memleak happy. */ + tal_free(maxfee_pct_millionths); + tal_free(exemptfee); } - tal_free(maxfee_pct_millionths); - payment_mod_exemptfee_get_data(p)->amount = *exemptfee; - tal_free(exemptfee); shadow_route = payment_mod_shadowroute_get_data(p); payment_mod_presplit_get_data(p)->disable = disablempp; payment_mod_adaptive_splitter_get_data(p)->disable = disablempp; diff --git a/tests/test_pay.py b/tests/test_pay.py index ac68c7b40ae2..e0914887e362 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -128,10 +128,15 @@ def test_pay_limits(node_factory): assert(len(status) == 2) assert(status[0]['failure']['code'] == 205) + # This fails! + err = r'Fee exceeds our fee budget: 2msat > 1msat, discarding route' + with pytest.raises(RpcError, match=err) as err: + l1.rpc.pay(bolt11=inv['bolt11'], msatoshi=100000, maxfee=1) + # This works, because fee is less than exemptfee. l1.dev_pay(inv['bolt11'], msatoshi=100000, maxfeepercent=0.0001, exemptfee=2000, use_shadow=False) - status = l1.rpc.call('paystatus', {'bolt11': inv['bolt11']})['pay'][2]['attempts'] + status = l1.rpc.call('paystatus', {'bolt11': inv['bolt11']})['pay'][3]['attempts'] assert len(status) == 1 assert status[0]['strategy'] == "Initial attempt" diff --git a/tests/test_plugin.py b/tests/test_plugin.py index fecc7ac6e36d..01b90631d519 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -397,7 +397,8 @@ def test_pay_plugin(node_factory): # Make sure usage messages are present. msg = 'pay bolt11 [msatoshi] [label] [riskfactor] [maxfeepercent] '\ - '[retry_for] [maxdelay] [exemptfee] [localofferid] [exclude]' + '[retry_for] [maxdelay] [exemptfee] [localofferid] [exclude] '\ + '[maxfee]' if DEVELOPER: msg += ' [use_shadow]' assert only_one(l1.rpc.help('pay')['help'])['command'] == msg From d5c736fe8668c81f59e230604aebeac8d74c36c9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 2 Apr 2022 13:03:05 +1030 Subject: [PATCH 0655/1530] pay: require description if bolt11 only has hash. Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `pay` has `description` parameter, will be required if bolt11 only has a hash. Changelog-Deprecated: JSON-RPC: `pay` for a bolt11 which uses a `description_hash`, without setting `description`. --- contrib/pyln-client/pyln/client/lightning.py | 3 ++- doc/lightning-pay.7.md | 7 +++++- doc/schemas/pay.request.json | 3 +++ plugins/pay.c | 23 ++++++++++++++++++-- tests/test_invoices.py | 10 +++++++-- tests/test_plugin.py | 2 +- 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 4996c81fb003..c0c649db4b8e 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -998,7 +998,7 @@ def newaddr(self, addresstype=None): def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, localofferid=None, exclude=None, - maxfee=None): + maxfee=None, description=None): """ Send payment specified by {bolt11} with {msatoshi} (ignored if {bolt11} has an amount), optional {label} @@ -1016,6 +1016,7 @@ def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, "localofferid": localofferid, "exclude": exclude, "maxfee": maxfee, + "description": description, } return self.call("pay", payload) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index dd1ff3ad2fdf..223bfb086f75 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -6,7 +6,7 @@ SYNOPSIS **pay** *bolt11* [*msatoshi*] [*label*] [*riskfactor*] [*maxfeepercent*] [*retry_for*] [*maxdelay*] [*exemptfee*] -[*localofferid*] [*exclude*] [*maxfee*] +[*localofferid*] [*exclude*] [*maxfee*] [*description*] DESCRIPTION ----------- @@ -43,6 +43,11 @@ creates an absolute limit on what fee we will pay. This allows you to implement your own heuristics rather than the primitive ones used here. +*description* is only required for bolt11 invoices which do not +contain a description themselves, but contain a description hash. +*description* is then checked against the hash inside the invoice +before it will be paid. + The response will occur when the payment fails or succeeds. Once a payment has succeeded, calls to **pay** with the same *bolt11* will succeed immediately. diff --git a/doc/schemas/pay.request.json b/doc/schemas/pay.request.json index b6dc27a1a1ee..a02750911e46 100644 --- a/doc/schemas/pay.request.json +++ b/doc/schemas/pay.request.json @@ -48,6 +48,9 @@ }, "maxfee": { "type": "msat" + }, + "description": { + "type": "string" } } } diff --git a/plugins/pay.c b/plugins/pay.c index 7bc0eb92cd30..7cebac54c1ca 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -926,7 +926,7 @@ static struct command_result *json_pay(struct command *cmd, u64 *maxfee_pct_millionths; u32 *maxdelay; struct amount_msat *exemptfee, *msat, *maxfee; - const char *label; + const char *label, *description; unsigned int *retryfor; u64 *riskfactor_millionths; struct shadow_route_data *shadow_route; @@ -959,6 +959,7 @@ static struct command_result *json_pay(struct command *cmd, p_opt("localofferid", param_sha256, &local_offer_id), p_opt("exclude", param_route_exclusion_array, &exclusions), p_opt("maxfee", param_msat, &maxfee), + p_opt("description", param_string, &description), #if DEVELOPER p_opt_def("use_shadow", param_bool, &use_shadow, true), #endif @@ -971,7 +972,7 @@ static struct command_result *json_pay(struct command *cmd, if (!bolt12_has_prefix(b11str)) { b11 = bolt11_decode(tmpctx, b11str, plugin_feature_set(cmd->plugin), - NULL, chainparams, &b11_fail); + description, chainparams, &b11_fail); if (b11 == NULL) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11: %s", b11_fail); @@ -999,6 +1000,24 @@ static struct command_result *json_pay(struct command *cmd, cmd, JSONRPC2_INVALID_PARAMS, "Invalid bolt11:" " sets feature var_onion with no secret"); + + /* BOLT #11: + * A reader: + *... + * - MUST check that the SHA2 256-bit hash in the `h` field + * exactly matches the hashed description. + */ + if (!b11->description && !deprecated_apis) { + if (!b11->description_hash) { + return command_fail(cmd, + JSONRPC2_INVALID_PARAMS, + "Invalid bolt11: missing description"); + } + if (!description) + return command_fail(cmd, + JSONRPC2_INVALID_PARAMS, + "bolt11 uses description_hash, but you did not provide description parameter"); + } } else { b12 = invoice_decode(tmpctx, b11str, strlen(b11str), plugin_feature_set(cmd->plugin), diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 1dec61081165..ba66a2efeecd 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -707,8 +707,14 @@ def test_invoice_deschash(node_factory, chainparams): listinv = only_one(l2.rpc.listinvoices()['invoices']) assert listinv['description'] == 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon' - # Make sure we can pay it! - l1.rpc.pay(inv['bolt11']) + # To pay it we need to provide the (correct!) description. + with pytest.raises(RpcError, match=r'you did not provide description parameter'): + l1.rpc.pay(inv['bolt11']) + + with pytest.raises(RpcError, match=r'does not match description'): + l1.rpc.pay(inv['bolt11'], description=listinv['description'][:-1]) + + l1.rpc.pay(inv['bolt11'], description=listinv['description']) # Try removing description. l2.rpc.delinvoice('label', "paid", desconly=True) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 01b90631d519..5cb9e8f81bcf 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -398,7 +398,7 @@ def test_pay_plugin(node_factory): # Make sure usage messages are present. msg = 'pay bolt11 [msatoshi] [label] [riskfactor] [maxfeepercent] '\ '[retry_for] [maxdelay] [exemptfee] [localofferid] [exclude] '\ - '[maxfee]' + '[maxfee] [description]' if DEVELOPER: msg += ' [use_shadow]' assert only_one(l1.rpc.help('pay')['help'])['command'] == msg From e47786da0456986d1c5147a9a419afb891ebdf41 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 2 Apr 2022 13:03:35 +1030 Subject: [PATCH 0656/1530] pay/sendpay: also store description in case bolt11 uses description_hash. Signed-off-by: Rusty Russell --- doc/lightning-listpays.7.md | 3 ++- doc/lightning-listsendpays.7.md | 3 ++- doc/lightning-sendpay.7.md | 3 ++- doc/schemas/listpays.schema.json | 7 +++++++ doc/schemas/listsendpays.schema.json | 7 +++++++ lightningd/pay.c | 21 ++++++++++++++++----- plugins/libplugin-pay.c | 6 ++++++ plugins/libplugin-pay.h | 3 +++ plugins/pay.c | 7 ++++++- tests/test_invoices.py | 10 ++++++++++ wallet/db.c | 2 ++ wallet/wallet.c | 18 ++++++++++++++++-- wallet/wallet.h | 3 +++ 13 files changed, 82 insertions(+), 11 deletions(-) diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index f4c2edf82bb9..8f098ec98273 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -24,6 +24,7 @@ On success, an object containing **pays** is returned. It is an array of object - **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) +- **description** (string, optional): the description matching the bolt11 description hash (if pay supplied one) - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "pending" or "complete": @@ -56,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6ffbb1273de04f356cf79dab9a988ab030eee3317cb22e10d12d1c672249fc67) +[comment]: # ( SHA256STAMP:3c158259410ff8eb81669e26eca9ee53017002d739f89e7f0e2fd8e61edb8a14) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 13d82b8f45ae..c2511d09f583 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -34,6 +34,7 @@ On success, an object containing **payments** is returned. It is an array of ob - **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) +- **description** (string, optional): the description matching the bolt11 description hash (if pay supplied one) - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "complete": @@ -60,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b03c2f306bafb1919f0933ebc695657bd691591484ddcb39b1e8706335593cd2) +[comment]: # ( SHA256STAMP:eaa0b4c6309d45bc2a72baf44288f1faa75d7f6ff2e8bf6d03be53747fe82c84) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 6ecb1806255e..f332e812f269 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -5,7 +5,8 @@ SYNOPSIS -------- **sendpay** *route* *payment\_hash* [*label*] [*msatoshi*] -[*bolt11*] [*payment_secret*] [*partid*] [*localofferid*] [*groupid*] [*payment_metadata*] +[*bolt11*] [*payment_secret*] [*partid*] [*localofferid*] [*groupid*] +[*payment_metadata*] [*description*] DESCRIPTION ----------- diff --git a/doc/schemas/listpays.schema.json b/doc/schemas/listpays.schema.json index f10062ea2180..ad74829abaa6 100644 --- a/doc/schemas/listpays.schema.json +++ b/doc/schemas/listpays.schema.json @@ -48,6 +48,10 @@ "type": "string", "description": "the bolt11 string (if pay supplied one)" }, + "description": { + "type": "string", + "description": "the description matching the bolt11 description hash (if pay supplied one)" + }, "bolt12": { "type": "string", "description": "the bolt12 string (if supplied for pay: **experimental-offers** only)." @@ -78,6 +82,7 @@ "created_at": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "preimage": {}, "number_of_parts": {}, @@ -115,6 +120,7 @@ "created_at": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "amount_msat": {}, "amount_sent_msat": {}, @@ -152,6 +158,7 @@ "created_at": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "amount_sent_msat": {}, "erroronion": { diff --git a/doc/schemas/listsendpays.schema.json b/doc/schemas/listsendpays.schema.json index 8db7f553fe5b..3a45a3b7de81 100644 --- a/doc/schemas/listsendpays.schema.json +++ b/doc/schemas/listsendpays.schema.json @@ -72,6 +72,10 @@ "type": "string", "description": "the bolt11 string (if pay supplied one)" }, + "description": { + "type": "string", + "description": "the description matching the bolt11 description hash (if pay supplied one)" + }, "bolt12": { "type": "string", "description": "the bolt12 string (if supplied for pay: **experimental-offers** only)." @@ -108,6 +112,7 @@ "amount_sent_msat": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "payment_preimage": { "type": "secret", @@ -146,6 +151,7 @@ "amount_sent_msat": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {}, "erroronion": { "type": "hex", @@ -182,6 +188,7 @@ "amount_sent_msat": {}, "label": {}, "bolt11": {}, + "description": {}, "bolt12": {} } } diff --git a/lightningd/pay.c b/lightningd/pay.c index 9e29f92d09ed..0f0745786eb0 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -154,6 +154,8 @@ void json_add_payment_fields(struct json_stream *response, else json_add_string(response, "bolt11", t->invstring); } + if (t->description) + json_add_string(response, "description", t->description); if (t->failonion) json_add_hex(response, "erroronion", t->failonion, @@ -857,6 +859,7 @@ send_payment_core(struct lightningd *ld, struct amount_msat total_msat, const char *label TAKES, const char *invstring TAKES, + const char *description TAKES, const struct onionpacket *packet, const struct node_id *destination, struct node_id *route_nodes TAKES, @@ -1100,6 +1103,10 @@ send_payment_core(struct lightningd *ld, payment->invstring = tal_strdup(payment, invstring); else payment->invstring = NULL; + if (description != NULL) + payment->description = tal_strdup(payment, description); + else + payment->description = NULL; payment->local_offer_id = tal_dup_or_null(payment, struct sha256, local_offer_id); @@ -1121,6 +1128,7 @@ send_payment(struct lightningd *ld, struct amount_msat total_msat, const char *label TAKES, const char *invstring TAKES, + const char *description TAKES, const struct sha256 *local_offer_id, const struct secret *payment_secret, const u8 *payment_metadata) @@ -1194,7 +1202,7 @@ send_payment(struct lightningd *ld, packet = create_onionpacket(tmpctx, path, ROUTING_INFO_SIZE, &path_secrets); return send_payment_core(ld, cmd, rhash, partid, group, &route[0], msat, total_msat, - label, invstring, + label, invstring, description, packet, &ids[n_hops - 1], ids, channels, path_secrets, local_offer_id); } @@ -1265,7 +1273,7 @@ static struct command_result *json_sendonion(struct command *cmd, struct route_hop *first_hop; struct sha256 *payment_hash; struct lightningd *ld = cmd->ld; - const char *label, *invstring; + const char *label, *invstring, *description; struct node_id *destination; struct secret *path_secrets; struct amount_msat *msat; @@ -1285,6 +1293,7 @@ static struct command_result *json_sendonion(struct command *cmd, p_opt("destination", param_node_id, &destination), p_opt("localofferid", param_sha256, &local_offer_id), p_opt("groupid", param_u64, &group), + p_opt("description", param_string, &description), NULL)) return command_param_failed(); @@ -1306,7 +1315,8 @@ static struct command_result *json_sendonion(struct command *cmd, return send_payment_core(ld, cmd, payment_hash, *partid, *group, first_hop, *msat, AMOUNT_MSAT(0), - label, invstring, packet, destination, NULL, NULL, + label, invstring, description, + packet, destination, NULL, NULL, path_secrets, local_offer_id); } @@ -1420,7 +1430,7 @@ static struct command_result *json_sendpay(struct command *cmd, struct sha256 *rhash; struct route_hop *route; struct amount_msat *msat; - const char *invstring, *label; + const char *invstring, *label, *description; u64 *partid, *group; struct secret *payment_secret; struct sha256 *local_offer_id; @@ -1439,6 +1449,7 @@ static struct command_result *json_sendpay(struct command *cmd, p_opt("localofferid", param_sha256, &local_offer_id), p_opt("groupid", param_u64, &group), p_opt("payment_metadata", param_bin_from_hex, &payment_metadata), + p_opt("description", param_string, &description), NULL)) return command_param_failed(); @@ -1488,7 +1499,7 @@ static struct command_result *json_sendpay(struct command *cmd, route, final_amount, msat ? *msat : final_amount, - label, invstring, local_offer_id, + label, invstring, description, local_offer_id, payment_secret, payment_metadata); } diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 1c8779f411bf..b206ce759029 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -65,6 +65,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->temp_exclusion = NULL; p->failroute_retry = false; p->invstring = NULL; + p->description = NULL; p->routetxt = NULL; p->max_htlcs = UINT32_MAX; p->aborterror = NULL; @@ -1568,6 +1569,9 @@ static struct command_result *payment_createonion_success(struct command *cmd, /* FIXME: rename parameter to invstring */ json_add_string(req->js, "bolt11", p->invstring); + if (p->description) + json_add_string(req->js, "description", p->description); + if (p->destination) json_add_node_id(req->js, "destination", p->destination); @@ -3559,6 +3563,8 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) * they'll be used when aggregating the payments * again. */ c->invstring = tal_strdup(c, p->invstring); + if (p->description) + c->description = tal_strdup(c, p->description); /* Get ~ target, but don't exceed amt */ c->amount = fuzzed_near(target, amt); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 86aea238c464..220972547daa 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -259,6 +259,9 @@ struct payment { * by the invoice. */ const char *invstring; + /* Description, usually set if bolt11 has only description_hash */ + const char *description; + /* If this is paying a local offer, this is the one (sendpay ensures we * don't pay twice for single-use offers) */ struct sha256 *local_offer_id; diff --git a/plugins/pay.c b/plugins/pay.c index 7cebac54c1ca..610d369c150a 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -273,6 +273,8 @@ struct pay_mpp { /* Optional label (of first one!) */ const jsmntok_t *label; + /* Optional description (used for bolt11 with description_hash) */ + const jsmntok_t *description; /* Optional preimage (iff status is successful) */ const jsmntok_t *preimage; /* Only counts "complete" or "pending" payments. */ @@ -373,7 +375,8 @@ static void add_new_entry(struct json_stream *ret, json_object_start(ret, NULL); if (pm->invstring) json_add_invstring(ret, pm->invstring); - + if (pm->description) + json_add_tok(ret, "description", pm->description, buf); if (pm->destination) json_add_tok(ret, "destination", pm->destination, buf); @@ -465,6 +468,7 @@ static struct command_result *listsendpays_done(struct command *cmd, pm->invstring = tal_steal(pm, invstr); pm->destination = json_get_member(buf, t, "destination"); pm->label = json_get_member(buf, t, "label"); + pm->description = json_get_member(buf, t, "description"); pm->preimage = NULL; pm->amount_sent = AMOUNT_MSAT(0); pm->amount = talz(pm, struct amount_msat); @@ -968,6 +972,7 @@ static struct command_result *json_pay(struct command *cmd, p = payment_new(cmd, cmd, NULL /* No parent */, paymod_mods); p->invstring = tal_steal(p, b11str); + p->description = tal_steal(p, description); if (!bolt12_has_prefix(b11str)) { b11 = diff --git a/tests/test_invoices.py b/tests/test_invoices.py index ba66a2efeecd..8f4aa6b5c637 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -716,6 +716,16 @@ def test_invoice_deschash(node_factory, chainparams): l1.rpc.pay(inv['bolt11'], description=listinv['description']) + # Description will be in some. + found = False + for p in l1.rpc.listsendpays()['payments']: + if 'description' in p: + found = True + assert p['description'] == listinv['description'] + assert found + + assert only_one(l1.rpc.listpays(inv['bolt11'])['pays'])['description'] == listinv['description'] + # Try removing description. l2.rpc.delinvoice('label', "paid", desconly=True) assert 'description' not in only_one(l2.rpc.listinvoices()['invoices']) diff --git a/wallet/db.c b/wallet/db.c index 0d086fea0a53..04497400dcdb 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -873,6 +873,8 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE channels ADD htlc_maximum_msat BIGINT DEFAULT 2100000000000000"), NULL}, {SQL("ALTER TABLE channels ADD htlc_minimum_msat BIGINT DEFAULT 0"), NULL}, {SQL("ALTER TABLE forwarded_payments ADD forward_style INTEGER DEFAULT NULL"), NULL}, + /* "description" is used for label, so we use "paydescription" here */ + {SQL("ALTER TABLE payments ADD paydescription TEXT;"), NULL}, }; /** diff --git a/wallet/wallet.c b/wallet/wallet.c index bf0710b0f352..da3fa96d0148 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3033,8 +3033,9 @@ void wallet_payment_store(struct wallet *wallet, " total_msat," " partid," " local_offer_id," - " groupid" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " groupid," + " paydescription" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_int(stmt, 0, payment->status); db_bind_sha256(stmt, 1, &payment->payment_hash); @@ -3083,6 +3084,11 @@ void wallet_payment_store(struct wallet *wallet, db_bind_u64(stmt, 14, payment->groupid); + if (payment->description != NULL) + db_bind_text(stmt, 15, payment->description); + else + db_bind_null(stmt, 15); + db_exec_prepared_v2(stmt); payment->id = db_last_insert_id_v2(stmt); assert(payment->id > 0); @@ -3177,6 +3183,11 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, else payment->label = NULL; + if (!db_col_is_null(stmt, "paydescription")) + payment->description = db_col_strdup(payment, stmt, "paydescription"); + else + payment->description = NULL; + if (!db_col_is_null(stmt, "bolt11")) payment->invstring = db_col_strdup(payment, stmt, "bolt11"); else @@ -3236,6 +3247,7 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, ", msatoshi_sent" ", description" ", bolt11" + ", paydescription" ", failonionreply" ", total_msat" ", partid" @@ -3472,6 +3484,7 @@ wallet_payment_list(const tal_t *ctx, ", msatoshi_sent" ", description" ", bolt11" + ", paydescription" ", failonionreply" ", total_msat" ", partid" @@ -3538,6 +3551,7 @@ wallet_payments_by_offer(const tal_t *ctx, ", msatoshi_sent" ", description" ", bolt11" + ", paydescription" ", failonionreply" ", total_msat" ", partid" diff --git a/wallet/wallet.h b/wallet/wallet.h index 2c179b465539..4003adc749a5 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -358,6 +358,9 @@ struct wallet_payment { /* The label of the payment. Must support `tal_len` */ const char *label; + /* The description of the payment (used if invstring has hash). */ + const char *description; + /* If we could not decode the fail onion, just add it here. */ const u8 *failonion; From f51a3d8ef7c9e02ad6673e0960a57c4669576a93 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Apr 2022 12:26:52 +0930 Subject: [PATCH 0657/1530] plugins/pay: always include `bolt11` (and `description`) in listpays. We were setting it on the root, but that doesn't get handed to sendpay. Our schema doesn't *require* bolt11, either, so this was missed (there could be a *bolt12* instead). Signed-off-by: Rusty Russell Changelog-Fixed: JSON-RPC: `listpays` always includes `bolt11` or `bolt12` field. --- plugins/keysend.c | 2 ++ plugins/libplugin-pay.c | 23 +++++++++++------------ plugins/libplugin-pay.h | 4 ++++ plugins/pay.c | 7 +++++++ tests/test_pay.py | 3 +++ 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/plugins/keysend.c b/plugins/keysend.c index 22ae85e3b77e..47f9440c16c9 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -186,6 +186,8 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, p->min_final_cltv_expiry = 22; p->features = NULL; p->invstring = NULL; + /* Don't try to use invstring to hand to sendonion! */ + p->invstring_used = true; p->why = "Initial attempt"; p->constraints.cltv_budget = *maxdelay; p->deadline = timeabs_add(time_now(), time_from_sec(*retryfor)); diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index b206ce759029..e415cf99d68f 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -61,11 +61,10 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->failreason = NULL; p->getroute->riskfactorppm = 10000000; p->abort = false; + p->invstring_used = false; p->route = NULL; p->temp_exclusion = NULL; p->failroute_retry = false; - p->invstring = NULL; - p->description = NULL; p->routetxt = NULL; p->max_htlcs = UINT32_MAX; p->aborterror = NULL; @@ -94,6 +93,8 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->local_id = parent->local_id; p->local_offer_id = parent->local_offer_id; p->groupid = parent->groupid; + p->invstring = parent->invstring; + p->description = parent->description; } else { assert(cmd != NULL); p->partid = 0; @@ -102,6 +103,7 @@ struct payment *payment_new(tal_t *ctx, struct command *cmd, p->channel_hints = tal_arr(p, struct channel_hint, 0); p->excluded_nodes = tal_arr(p, struct node_id, 0); p->id = next_id++; + p->description = NULL; /* Caller must set this. */ p->local_id = NULL; p->local_offer_id = NULL; @@ -1536,6 +1538,7 @@ static struct command_result *payment_createonion_success(struct command *cmd, struct out_req *req; struct route_hop *first = &p->route[0]; struct secret *secrets; + struct payment *root = payment_root(p); p->createonion_response = json_to_createonion_response(p, buffer, toks); @@ -1565,12 +1568,15 @@ static struct command_result *payment_createonion_success(struct command *cmd, if (p->label) json_add_string(req->js, "label", p->label); - if (p->invstring) + if (!root->invstring_used) { /* FIXME: rename parameter to invstring */ json_add_string(req->js, "bolt11", p->invstring); - if (p->description) - json_add_string(req->js, "description", p->description); + if (p->description) + json_add_string(req->js, "description", p->description); + + root->invstring_used = true; + } if (p->destination) json_add_node_id(req->js, "destination", p->destination); @@ -3559,13 +3565,6 @@ static void presplit_cb(struct presplit_mod_data *d, struct payment *p) struct payment *c = payment_new(p, NULL, p, p->modifiers); - /* Annotate the subpayments with the bolt11 string, - * they'll be used when aggregating the payments - * again. */ - c->invstring = tal_strdup(c, p->invstring); - if (p->description) - c->description = tal_strdup(c, p->description); - /* Get ~ target, but don't exceed amt */ c->amount = fuzzed_near(target, amt); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index 220972547daa..eb125acef148 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -255,6 +255,10 @@ struct payment { * true. Set only on the root payment. */ bool abort; + /* We only set invstring/description on one of our sendpays per group, + * so we track when we've done that. */ + bool invstring_used; + /* Serialized bolt11/12 string, kept attachd to the root so we can filter * by the invoice. */ const char *invstring; diff --git a/plugins/pay.c b/plugins/pay.c index 610d369c150a..51b632a03660 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -480,6 +480,13 @@ static struct command_result *listsendpays_done(struct command *cmd, // First time we see the groupid we add it to the order // array, so we can retrieve them in the correct order. tal_arr_expand(&order, pm->sortkey); + } else { + /* Not all payments have bolt11/bolt12 or + * description, as an optimization */ + if (!pm->invstring) + pm->invstring = tal_steal(pm, invstr); + if (!pm->description) + pm->description = json_get_member(buf, t, "description"); } status = json_get_member(buf, t, "status"); diff --git a/tests/test_pay.py b/tests/test_pay.py index e0914887e362..67253820560a 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3799,6 +3799,9 @@ def all_htlcs(n): pprint(p) pprint(l1.rpc.paystatus(inv)) + # listpays() shows bolt11 string + assert 'bolt11' in only_one(l1.rpc.listpays()['pays']) + def test_pay_fail_unconfirmed_channel(node_factory, bitcoind): ''' From c1e7c14c46269eeb43d68c0d6f9e22757abbe990 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Apr 2022 12:26:54 +0930 Subject: [PATCH 0658/1530] pytest: test that deduplication for `bolt11` works as expected. Signed-off-by: Rusty Russell --- tests/test_pay.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index 67253820560a..6f41ad72e71e 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3730,6 +3730,24 @@ def test_mpp_presplit(node_factory): assert(inv['msatoshi'] == inv['msatoshi_received']) + # Make sure that bolt11 isn't duplicated for every part + bolt11s = 0 + count = 0 + for p in l1.rpc.listsendpays()['payments']: + if 'bolt11' in p: + bolt11s += 1 + count += 1 + + # You were supposed to mpp! + assert count > 1 + # Not every one should have the bolt11 string + assert bolt11s < count + # In fact, only one should + assert bolt11s == 1 + + # But listpays() gathers it: + assert only_one(l1.rpc.listpays()['pays'])['bolt11'] == inv['bolt11'] + def test_mpp_adaptive(node_factory, bitcoind): """We have two paths, both too small on their own, let's combine them. @@ -3799,6 +3817,19 @@ def all_htlcs(n): pprint(p) pprint(l1.rpc.paystatus(inv)) + # Make sure that bolt11 isn't duplicated for every part + bolt11s = 0 + count = 0 + for p in l1.rpc.listsendpays()['payments']: + if 'bolt11' in p: + bolt11s += 1 + count += 1 + + # You were supposed to mpp! + assert count > 1 + # Not every one should have the bolt11 string + assert bolt11s < count + # listpays() shows bolt11 string assert 'bolt11' in only_one(l1.rpc.listpays()['pays']) From 21fd3b22d472f89bec60048b8ab5f6fabf610155 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sat, 26 Mar 2022 14:31:26 +0100 Subject: [PATCH 0659/1530] config: adds htlc_minimum_msat htlc_maximum_msat announce_discovered_ip This adds config and commandline options for htlc_min_msat, htlc_max_msat and announce_discovered_ip. The default is 0msat for htlc_min_msat, unlimited for htlc_max_msat and enabled for announce_discovered_ip. The announce_discovered_ip gets the disable commandline switch --disable-ip-discovery Changelog-added: Config options for htlc_min_msat, htlc_max_msat and announce_discovered_ip. --- lightningd/lightningd.h | 7 +++++++ lightningd/opening_common.c | 2 +- lightningd/opening_control.c | 4 ++-- lightningd/options.c | 23 +++++++++++++++++++++++ lightningd/peer_control.c | 2 +- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index a90f133099a4..ae74a635f978 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -33,6 +33,10 @@ struct config { /* htlcs per channel */ u32 max_concurrent_htlcs; + /* htlc min/max values */ + struct amount_msat htlc_minimum_msat; + struct amount_msat htlc_maximum_msat; + /* Max amount of dust allowed per channel */ struct amount_msat max_dust_htlc_exposure_msat; @@ -52,6 +56,9 @@ struct config { /* Are we allowed to use DNS lookup for peers. */ bool use_dns; + /* Turn off IP address announcement discovered via peer `remote_addr` */ + bool disable_ip_discovery; + /* Minimal amount of effective funding_satoshis for accepting channels */ u64 min_capacity_sat; diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 7442cea19a93..f866a91f5be0 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -147,7 +147,7 @@ void channel_config(struct lightningd *ld, = ld->config.max_dust_htlc_exposure_msat; /* Don't care */ - ours->htlc_minimum = AMOUNT_MSAT(0); + ours->htlc_minimum = ld->config.htlc_minimum_msat; /* BOLT #2: * diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 87ffc1984baf..9499a76e3e41 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -211,8 +211,8 @@ wallet_commit_channel(struct lightningd *ld, take(new_height_states(NULL, uc->fc ? LOCAL : REMOTE, &lease_start_blockheight)), 0, NULL, 0, 0, /* No leases on v1s */ - AMOUNT_MSAT(0), /* No htlc_minimum_msat */ - AMOUNT_MSAT(-1ULL)); /* No htlc_maximum_msat */ + ld->config.htlc_minimum_msat, + ld->config.htlc_maximum_msat); /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); diff --git a/lightningd/options.c b/lightningd/options.c index 635feb40e30d..826b87b6400a 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -760,6 +760,10 @@ static const struct config testnet_config = { /* Testnet blockspace is free. */ .max_concurrent_htlcs = 483, + /* channel defaults for htlc min/max values */ + .htlc_minimum_msat = AMOUNT_MSAT(0), + .htlc_maximum_msat = AMOUNT_MSAT(-1ULL), /* no limit */ + /* Max amount of dust allowed per channel (50ksat) */ .max_dust_htlc_exposure_msat = AMOUNT_MSAT(50000000), @@ -783,6 +787,9 @@ static const struct config testnet_config = { .use_dns = true, + /* Turn off IP address announcement discovered via peer `remote_addr` */ + .disable_ip_discovery = false, + /* Sets min_effective_htlc_capacity - at 1000$/BTC this is 10ct */ .min_capacity_sat = 10000, @@ -809,6 +816,10 @@ static const struct config mainnet_config = { /* While up to 483 htlcs are possible we do 30 by default (as eclair does) to save blockspace */ .max_concurrent_htlcs = 30, + /* defaults for htlc min/max values */ + .htlc_minimum_msat = AMOUNT_MSAT(0), + .htlc_maximum_msat = AMOUNT_MSAT(-1ULL), /* no limit */ + /* Max amount of dust allowed per channel (50ksat) */ .max_dust_htlc_exposure_msat = AMOUNT_MSAT(50000000), @@ -842,6 +853,9 @@ static const struct config mainnet_config = { .use_dns = true, + /* Turn off IP address announcement discovered via peer `remote_addr` */ + .disable_ip_discovery = false, + /* Sets min_effective_htlc_capacity - at 1000$/BTC this is 10ct */ .min_capacity_sat = 10000, @@ -1105,6 +1119,12 @@ static void register_opts(struct lightningd *ld) opt_register_arg("--fee-per-satoshi", opt_set_u32, opt_show_u32, &ld->config.fee_per_satoshi, "Microsatoshi fee for every satoshi in HTLC"); + opt_register_arg("--htlc-minimum-msat", opt_set_msat, NULL, + &ld->config.htlc_minimum_msat, + "The default minimal value an HTLC must carry in order to be forwardable for new channels"); + opt_register_arg("--htlc-maximum-msat", opt_set_msat, NULL, + &ld->config.htlc_maximum_msat, + "The default maximal value an HTLC must carry in order to be forwardable for new channel"); opt_register_arg("--max-concurrent-htlcs", opt_set_u32, opt_show_u32, &ld->config.max_concurrent_htlcs, "Number of HTLCs one channel can handle concurrently. Should be between 1 and 483"); @@ -1123,6 +1143,9 @@ static void register_opts(struct lightningd *ld) opt_register_arg("--announce-addr", opt_add_announce_addr, NULL, ld, "Set an IP address (v4 or v6) or .onion v3 to announce, but not listen on"); + opt_register_noarg("--disable-ip-discovery", opt_set_bool, + &ld->config.disable_ip_discovery, + "Turn off announcement of discovered public IPs"); opt_register_noarg("--offline", opt_set_offline, ld, "Start in offline-mode (do not automatically reconnect and do not accept incoming connections)"); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 197ea9e4156e..8565a73a3e34 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1076,7 +1076,7 @@ static void update_remote_addr(struct lightningd *ld, const struct node_id peer_id) { /* failsafe to prevent privacy leakage. */ - if (ld->always_use_proxy) + if (ld->always_use_proxy || ld->config.disable_ip_discovery) return; switch (remote_addr->type) { From e99f9ad6d6b87a42f91fb32e524b46e67aa60e92 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sat, 26 Mar 2022 14:36:28 +0100 Subject: [PATCH 0660/1530] doc: htlc_minimum_msat htlc_maximum_msat announce_discovered_ip doc/schemas: adds htlc min/max and ip discovery --- doc/lightning-listconfigs.7 | 8 +++++++- doc/lightning-listconfigs.7.md | 5 ++++- doc/lightningd-config.5.md | 17 +++++++++++++++++ doc/schemas/listconfigs.schema.json | 12 ++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index d946d1e03a7c..e781d0019d63 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -122,6 +122,10 @@ On success, an object is returned, containing: .IP \[bu] \fBmax-concurrent-htlcs\fR (u32, optional): \fBmax-concurrent-htlcs\fR field from config or cmdline, or default .IP \[bu] +\fBhtlc-minimum-msat\fR (msat, optional): \fBhtlc-minimum-msat\fR field from config or cmdline, or default +.IP \[bu] +\fBhtlc-maximum-msat\fR (msat, optional): \fBhtlc-maximum-msat\fR field from config or cmdline, or default +.IP \[bu] \fBmax-dust-htlc-exposure-msat\fR (msat, optional): \fBmax-dust-htlc-exposure-mast\fR field from config or cmdline, or default .IP \[bu] \fBmin-capacity-sat\fR (u64, optional): \fBmin-capacity-sat\fR field from config or cmdline, or default @@ -140,6 +144,8 @@ On success, an object is returned, containing: .IP \[bu] \fBdisable-dns\fR (boolean, optional): \fBtrue\fR if \fBdisable-dns\fR was set in config or cmdline .IP \[bu] +\fBdisable-ip-discovery\fR (boolean, optional): \fBtrue\fR if \fBdisable-ip-discovery\fR was set in config or cmdline +.IP \[bu] \fBencrypted-hsm\fR (boolean, optional): \fBtrue\fR if \fBencrypted-hsm\fR was set in config or cmdline .IP \[bu] \fBrpc-file-mode\fR (string, optional): \fBrpc-file-mode\fR field from config or cmdline, or default @@ -276,4 +282,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:759090c2619cd40cba89b670ea16d4885f9aea520588d30d0ee8041369d5b91e +\" SHA256STAMP:8d8f73010f55f3af6e050c944cf7670109224b3cf3166cc754047f6e20beef20 diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index c6cdf6290720..5b285b663c47 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -71,6 +71,8 @@ On success, an object is returned, containing: - **rescan** (integer, optional): `rescan` field from config or cmdline, or default - **fee-per-satoshi** (u32, optional): `fee-per-satoshi` field from config or cmdline, or default - **max-concurrent-htlcs** (u32, optional): `max-concurrent-htlcs` field from config or cmdline, or default +- **htlc-minimum-msat** (msat, optional): `htlc-minimum-msat` field from config or cmdline, or default +- **htlc-maximum-msat** (msat, optional): `htlc-maximum-msat` field from config or cmdline, or default - **max-dust-htlc-exposure-msat** (msat, optional): `max-dust-htlc-exposure-mast` field from config or cmdline, or default - **min-capacity-sat** (u64, optional): `min-capacity-sat` field from config or cmdline, or default - **addr** (string, optional): `addr` field from config or cmdline (can be more than one) @@ -80,6 +82,7 @@ On success, an object is returned, containing: - **autolisten** (boolean, optional): `autolisten` field from config or cmdline, or default - **proxy** (string, optional): `proxy` field from config or cmdline, or default - **disable-dns** (boolean, optional): `true` if `disable-dns` was set in config or cmdline +- **disable-ip-discovery** (boolean, optional): `true` if `disable-ip-discovery` was set in config or cmdline - **encrypted-hsm** (boolean, optional): `true` if `encrypted-hsm` was set in config or cmdline - **rpc-file-mode** (string, optional): `rpc-file-mode` field from config or cmdline, or default - **log-level** (string, optional): `log-level` field from config or cmdline, or default @@ -208,4 +211,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:59b197ad256bd701744ed5aa9f663166e48ef6320cf3a1538af0bd855daa3186) +[comment]: # ( SHA256STAMP:ebc37d1f9cb452d312285a8168d2bb6da2d1dba08db56bbb8d3d7f47b58d7fa4) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 9fe83e46fb05..c7a7e97b3948 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -278,6 +278,23 @@ values), and the order is "opening", "mutual_close", "unilateral_close", You would usually put this option in the per-chain config file, to avoid setting it on Bitcoin mainnet! e.g. `~rusty/.lightning/regtest/config`. + **htlc-minimum-msat**=*MILLISATOSHI* +Default: 0. Sets the minimal allowed HTLC value for newly created channels. +If you want to change the `htlc_minimum_msat` for existing channels, use the +RPC call lightning-setchannel(7). + + **htlc-maximum-msat**=*MILLISATOSHI* +Default: unset (no limit). Sets the maximum allowed HTLC value for newly created +channels. If you want to change the `htlc_maximum_msat` for existing channels, +use the RPC call lightning-setchannel(7). + + **disable-ip-discovery** +Turn off public IP discovery to send `node_announcement` updates that contain +the discovered IP with TCP port 9735 as announced address. If unset and you +open TCP port 9735 on your router towords your node, your node will remain +connectable on changing IP addresses. Note: Will always be disabled if you use +'always-use-proxy'. + ### Lightning channel and HTLC options **large-channels** diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index ac041bf8dea8..c3215e8e9ed2 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -187,6 +187,14 @@ "type": "u32", "description": "`max-concurrent-htlcs` field from config or cmdline, or default" }, + "htlc-minimum-msat": { + "type": "msat", + "description": "`htlc-minimum-msat` field from config or cmdline, or default" + }, + "htlc-maximum-msat": { + "type": "msat", + "description": "`htlc-maximum-msat` field from config or cmdline, or default" + }, "max-dust-htlc-exposure-msat": { "type": "msat", "description": "`max-dust-htlc-exposure-mast` field from config or cmdline, or default" @@ -223,6 +231,10 @@ "type": "boolean", "description": "`true` if `disable-dns` was set in config or cmdline" }, + "disable-ip-discovery": { + "type": "boolean", + "description": "`true` if `disable-ip-discovery` was set in config or cmdline" + }, "encrypted-hsm": { "type": "boolean", "description": "`true` if `encrypted-hsm` was set in config or cmdline" From 62df569d4902aabb8deb7d5cc1c8bffef1f4a6b8 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 30 Mar 2022 23:21:45 +0200 Subject: [PATCH 0661/1530] pytest: option disable-ip-discovery --- tests/test_connection.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index e82a5ec7baf7..42d08005e0a8 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -120,6 +120,38 @@ def test_remote_addr(node_factory, bitcoind): assert address['port'] == 9735 +@pytest.mark.developer("needs DEVELOPER=1 for having localhost remote_addr and fast gossip") +def test_remote_addr_disabled(node_factory, bitcoind): + """Simply tests that IP address discovery annoucements can be turned off + """ + opts = {'announce-addr': [], 'disable-ip-discovery': None, 'may_reconnect': True} + l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + + # l1->l2 + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + l1.fundchannel(l2) + bitcoind.generate_block(5) + l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") + # l2->l3 + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + l2.fundchannel(l3) + bitcoind.generate_block(5) + + # restart both and wait for channels to be ready + l1.restart() + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + l2.daemon.wait_for_log("Already have funding locked in") + l3.restart() + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l2.daemon.wait_for_log("Already have funding locked in") + + # if ip discovery would have been enabled, we would have send an updated + # node_annoucement by now. Check we didn't... + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address") + + def test_connect_standard_addr(node_factory): """Test standard node@host:port address """ From 0799328775b8e13fe39d00d86b8c63f62ec43ad6 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Thu, 31 Mar 2022 14:26:55 +0200 Subject: [PATCH 0662/1530] pytest: test custom config options for fees and htlc limits --- tests/test_pay.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index 6f41ad72e71e..73585ba7b884 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2384,6 +2384,29 @@ def test_setchannel_all(node_factory, bitcoind): assert result['channels'][1]['maximum_htlc_out_msat'] == 0xCAFE +@pytest.mark.developer("updates are delayed without --dev-fast-gossip") +def test_setchannel_startup_opts(node_factory, bitcoind): + """Tests that custom config/cmdline options are applied correctly when set + """ + opts = { + 'fee-base': 2, + 'fee-per-satoshi': 3, + 'htlc-minimum-msat': '4msat', + 'htlc-maximum-msat': '5msat' + } + l1, l2 = node_factory.line_graph(2, opts=opts, wait_for_announce=True) + + result = l2.rpc.listchannels()['channels'] + assert result[0]['base_fee_millisatoshi'] == 2 + assert result[0]['fee_per_millionth'] == 3 + assert result[0]['htlc_minimum_msat'] == Millisatoshi(4) + assert result[0]['htlc_maximum_msat'] == Millisatoshi(5) + assert result[1]['base_fee_millisatoshi'] == 2 + assert result[1]['fee_per_millionth'] == 3 + assert result[1]['htlc_minimum_msat'] == Millisatoshi(4) + assert result[1]['htlc_maximum_msat'] == Millisatoshi(5) + + @pytest.mark.developer("gossip without DEVELOPER=1 is slow") def test_channel_spendable(node_factory, bitcoind): """Test that spendable_msat is accurate""" From 600525d74c67de27032dca5cf0ac6aad381a0d86 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 5 Apr 2022 02:29:33 +0930 Subject: [PATCH 0663/1530] v0.11.0rc1 Finally! Signed-off-by: Rusty Russell --- CHANGELOG.md | 158 ++++++++++++++++++ contrib/pyln-client/pyln/client/__init__.py | 2 +- contrib/pyln-proto/pyln/proto/__init__.py | 2 +- contrib/pyln-testing/pyln/testing/__init__.py | 2 +- 4 files changed, 161 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a5d3d6c7294..54d7565f464c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,164 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +## [v0.11.0rc1] - 2022-04-04: Simon's Carefully Chosen Release Name + +This release named by Simon Vrouwe; this marks the name change to core-lightning (#CLN). + +### Added + + - Protocol: we now support opening multiple channels with the same peer. ([#5078]) + - Protocol: we send/receive IP addresses in `init`, and send updated node_announcement when two peers report the same remote_addr. ([#5052]) + - Plugins: `cln-grpc` first class GRPC interface for remotely controlling nodes over mTLS authentication; set `grpc-port` to activate ([#5013]) + - Database: With the `sqlite3://` scheme for `--wallet` option, you can now specify a second file path for real-time database backup by separating it from the main file path with a `:` character. ([#4890]) + - Protocol: `pay` (and decode, etc) supports bolt11 payment_metadata a-la https://github.com/lightning/bolts/pull/912 ([#5086]) + - JSON-RPC: `invoice` has a new parameter `deschashonly` to put hash of description in bolt11. ([#5121]) + - JSON-RPC: `pay` has new parameter `description`, will be required if bolt11 only has a hash. ([#5122]) + - JSON-RPC: `pay` has new parameter `maxfee` for setting absolute fee (instead of using `maxfeepercent` and/or `exemptfee`) ([#5122]) + - JSON-RPC: `listforwards` has new entry `style`, currently "legacy" or "tlv". ([#5146]) + - JSON-RPC: `delinvoice` has a new parameter `desconly` to remove description. ([#5121]) + - JSON-RPC: new `setchannel` command generalizes `setchannelfee`: you can now alter the `htlc_minimum_msat` and `htlc_maximum_msat` your node advertizes. ([#5103]) + - JSON-RPC: `listpeers` now includes a `pushed_msat` value. For leased channels, is the total lease_fee. ([#5043]) + - JSON-RPC: `getinfo` result now includes `our_features` (bits) for various Bolt #9 contexts ([#5047]) + - Docker build for ARM defaults to `bitcoin`, but can be overridden with the `LIGHTNINGD_NETWORK` envvar. ([#4896]) + - Developer: A new Rust library called `cln-rpc` can be used to interact with the JSON-RPC ([#5010]) + - JSON-RPC: A new `msggen` library allows easy generation of language bindings for the JSON-RPC from the JSON schemas ([#5010]) + - JSON-RPC: `listchannels` now includes the `funding_outnum` ([#5016]) + - JSON-RPC: `coin_movement` to 'external' accounts now include an 'originating_account' field ([#5019]) + - JSON-RPC: Add `exclude` option for `pay` command to manually exclude channels or nodes when finding a route. ([#4906]) + - Database: Speed up loading of pending HTLCs during startup by using a partial index. ([#4925]) + + +### Changed + + - JSON-RPC: `close` by peer id will fail if there is more than one live channel (use `channel_id` or `short_channel_id` as id arg). ([#5078]) + - JSON_RPC: `sendcustommsg` now works with any connected peer, even when shutting down a channel. ([#4985]) + - JSON_RPC: `ping` now works with connected peers, even without a channel. ([#4985]) + - cli: Addition of HSM specific error code in lightning-cli ([#4908]) + - config: If the port is unspecified, the default port is chosen according to used network similarly to Bitcoin Core. ([#4900]) + - Plugins: `shutdown` notification is now send when lightningd is almost completely shutdown, RPC calls then fail with error code -5. ([#4897]) + - Protocol: `signet` addresses and invoices now use `tbs` instead of `tb`. ([#4929]) + + +### Deprecated + +Note: You should always set `allow-deprecated-apis=false` to test for changes. + + - JSON-RPC: `pay` for a bolt11 which uses a `description_hash`, without setting `description`. ([#5122]) + - JSON-RPC: `invoice` `expiry` no longer allowed to be a string with suffix, use an integer number of seconds. ([#5104]) + - JSON-RPC: `fundpsbt`/`utxopsbt` `reserve` must be a number, not bool (for `true` use 72/don't specify, for `false` use 0). Numbers have been allowed since v0.10.1. ([#5104]) + - JSON-RPC: `shutdown` no longer allows p2pkh or p2sh addresses. ([#5086]) + - JSON-RPC: `sendpay` `route` argument `style` "legacy" (don't use it at all, we ignore it now and always use "tlv" anyway). ([#5120]) + - JSON-RPC: `setchannelfee` (use `setchannel`). ([#5103]) + + +### Removed + + - JSON-RPC: `legacypay` (`pay` replaced it in 0.9.0). ([#5122]) + - Protocol: support for legacy onion format removed, since everyone supports the new one. ([#5058]) + - Protocol: ... but we still forward legacy HTLC onions for now. ([#5146]) + - Plugins: The `message` field on the `custommsg` hook (deprecated in v0.10.0) ([#4902]) + - JSON-RPC: `fundchannel_complete` `txid` and `txout` parameters (deprecated in v0.10.0) ([#4902]) + + +### Fixed + + - onchaind: we sometimes failed to close upstream htlcs if more than one HTLC is in flight during unilateral close. ([#5130]) + - JSON-RPC: `listpays` always includes `bolt11` or `bolt12` field. ([#5122]) + - cli: don't ask to confirm the password if the `hsm_secret` is already encrypted. ([#5085]) + - cli: check if the `hsm_secret` password and the confirmation match from the command line ([#5085]) + - JSON-RPC: `connect` notification now called even if we already have a live channel. ([#5078]) + - docker: The docker image is now built with postgresql support ([#5081]) + - hsmd: Fixed a significant memory leak ([#5051]) + - closingd: more accurate weight estimation helps mutual closing near min/max feerates. ([#5004]) + - Protocol: Always flush sockets to increase chance that final message get to peer (esp. error packets). ([#4984]) + - JSON-RPC: listincoming showed incoming_capacity_msat field 1000 times actual value. ([#4913]) + - Options: Respect --always-use-proxy AND --disable-dns when parsing wireaddresses to listen on. ([#4829]) + - lightningd: remove slow memory leak in DEVELOPER builds. ([#4931]) + - JSON-RPC: `paystatus` entries no longer have two identical `amount_msat` entries. ([#4911]) + - We really do allow providing multiple addresses of the same type. ([#4902]) + + +### EXPERIMENTAL + + - Fixex `experimental-websocket` intermittent read errors ([#5090]) + - Fixed `experimental-websocket-port` not to leave zombie processes. ([#5101]) + - Config option `--lease-fee-base-msat` renamed to `--lease-fee-base-sat` ([#5047]) + - Config option `--lease-fee-base-msat` deprecated and will be removed next release ([#5047]) + - Fixex `experimental-websocket-port` to work with default addresses. ([#4945]) + - Protocol: removed support for v0.10.1 onion messages. ([#4921]) + - Protocol: Ability to announce DNS addresses ([#4829]) + + + +[#4829]: https://github.com/ElementsProject/lightning/pull/4829 +[#5085]: https://github.com/ElementsProject/lightning/pull/5085 +[#5103]: https://github.com/ElementsProject/lightning/pull/5103 +[#5086]: https://github.com/ElementsProject/lightning/pull/5086 +[#5103]: https://github.com/ElementsProject/lightning/pull/5103 +[#4985]: https://github.com/ElementsProject/lightning/pull/4985 +[#5078]: https://github.com/ElementsProject/lightning/pull/5078 +[#4896]: https://github.com/ElementsProject/lightning/pull/4896 +[#4931]: https://github.com/ElementsProject/lightning/pull/4931 +[#5104]: https://github.com/ElementsProject/lightning/pull/5104 +[#4984]: https://github.com/ElementsProject/lightning/pull/4984 +[#5078]: https://github.com/ElementsProject/lightning/pull/5078 +[#5013]: https://github.com/ElementsProject/lightning/pull/5013 +[#5013]: https://github.com/ElementsProject/lightning/pull/5013 +[#5086]: https://github.com/ElementsProject/lightning/pull/5086 +[#4906]: https://github.com/ElementsProject/lightning/pull/4906 +[#5122]: https://github.com/ElementsProject/lightning/pull/5122 +[#4925]: https://github.com/ElementsProject/lightning/pull/4925 +[#5146]: https://github.com/ElementsProject/lightning/pull/5146 +[#4911]: https://github.com/ElementsProject/lightning/pull/4911 +[#5103]: https://github.com/ElementsProject/lightning/pull/5103 +[#5051]: https://github.com/ElementsProject/lightning/pull/5051 +[#5019]: https://github.com/ElementsProject/lightning/pull/5019 +[#4829]: https://github.com/ElementsProject/lightning/pull/4829 +[#5121]: https://github.com/ElementsProject/lightning/pull/5121 +[#5081]: https://github.com/ElementsProject/lightning/pull/5081 +[#5130]: https://github.com/ElementsProject/lightning/pull/5130 +[#5120]: https://github.com/ElementsProject/lightning/pull/5120 +[#5010]: https://github.com/ElementsProject/lightning/pull/5010 +[#5104]: https://github.com/ElementsProject/lightning/pull/5104 +[#4900]: https://github.com/ElementsProject/lightning/pull/4900 +[#4921]: https://github.com/ElementsProject/lightning/pull/4921 +[#4897]: https://github.com/ElementsProject/lightning/pull/4897 +[#5016]: https://github.com/ElementsProject/lightning/pull/5016 +[#4929]: https://github.com/ElementsProject/lightning/pull/4929 +[#5121]: https://github.com/ElementsProject/lightning/pull/5121 +[#5058]: https://github.com/ElementsProject/lightning/pull/5058 +[#5052]: https://github.com/ElementsProject/lightning/pull/5052 +[#5010]: https://github.com/ElementsProject/lightning/pull/5010 +[#4902]: https://github.com/ElementsProject/lightning/pull/4902 +[#5047]: https://github.com/ElementsProject/lightning/pull/5047 +[#5004]: https://github.com/ElementsProject/lightning/pull/5004 +[#5146]: https://github.com/ElementsProject/lightning/pull/5146 +[#5043]: https://github.com/ElementsProject/lightning/pull/5043 +[#5122]: https://github.com/ElementsProject/lightning/pull/5122 +[#5122]: https://github.com/ElementsProject/lightning/pull/5122 +[#4985]: https://github.com/ElementsProject/lightning/pull/4985 +[#5047]: https://github.com/ElementsProject/lightning/pull/5047 +[#5090]: https://github.com/ElementsProject/lightning/pull/5090 +[#5078]: https://github.com/ElementsProject/lightning/pull/5078 +[#4864]: https://github.com/ElementsProject/lightning/pull/4864 +[#4945]: https://github.com/ElementsProject/lightning/pull/4945 +[#4902]: https://github.com/ElementsProject/lightning/pull/4902 +[#4902]: https://github.com/ElementsProject/lightning/pull/4902 +[#5047]: https://github.com/ElementsProject/lightning/pull/5047 +[#4913]: https://github.com/ElementsProject/lightning/pull/4913 +[#5122]: https://github.com/ElementsProject/lightning/pull/5122 +[#4890]: https://github.com/ElementsProject/lightning/pull/4890 +[#5101]: https://github.com/ElementsProject/lightning/pull/5101 +[#5085]: https://github.com/ElementsProject/lightning/pull/5085 +[#4908]: https://github.com/ElementsProject/lightning/pull/4908 +[#5122]: https://github.com/ElementsProject/lightning/pull/5122 +[0.11.0]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0 + ## [0.10.2] - 2021-11-03: Bitcoin Dust Consensus Rule This release named by @vincenzopalazzo. diff --git a/contrib/pyln-client/pyln/client/__init__.py b/contrib/pyln-client/pyln/client/__init__.py index c50292f892de..bc830632fbfb 100644 --- a/contrib/pyln-client/pyln/client/__init__.py +++ b/contrib/pyln-client/pyln/client/__init__.py @@ -2,7 +2,7 @@ from .plugin import Plugin, monkey_patch, RpcException from .gossmap import Gossmap, GossmapNode, GossmapChannel, GossmapNodeId -__version__ = "0.10.2" +__version__ = "0.11.0" __all__ = [ "LightningRpc", diff --git a/contrib/pyln-proto/pyln/proto/__init__.py b/contrib/pyln-proto/pyln/proto/__init__.py index 2a54c4f3c64e..41e77980cc75 100644 --- a/contrib/pyln-proto/pyln/proto/__init__.py +++ b/contrib/pyln-proto/pyln/proto/__init__.py @@ -4,7 +4,7 @@ from .onion import OnionPayload, TlvPayload, LegacyOnionPayload from .wire import LightningConnection, LightningServerSocket -__version__ = "0.10.2" +__version__ = "0.11.0" __all__ = [ "Invoice", diff --git a/contrib/pyln-testing/pyln/testing/__init__.py b/contrib/pyln-testing/pyln/testing/__init__.py index 8891f5e2285e..5fc027556733 100644 --- a/contrib/pyln-testing/pyln/testing/__init__.py +++ b/contrib/pyln-testing/pyln/testing/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.10.2" +__version__ = "0.11.0" __all__ = [ "__version__", From 79c292908565629dc5922b11450246b14f17f4d7 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Mon, 4 Apr 2022 12:49:47 -0700 Subject: [PATCH 0664/1530] changelog: sort pr links --- CHANGELOG.md | 96 ++++++++++++++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54d7565f464c..a3d4d858fca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,67 +99,67 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#4829]: https://github.com/ElementsProject/lightning/pull/4829 -[#5085]: https://github.com/ElementsProject/lightning/pull/5085 -[#5103]: https://github.com/ElementsProject/lightning/pull/5103 -[#5086]: https://github.com/ElementsProject/lightning/pull/5086 -[#5103]: https://github.com/ElementsProject/lightning/pull/5103 -[#4985]: https://github.com/ElementsProject/lightning/pull/4985 -[#5078]: https://github.com/ElementsProject/lightning/pull/5078 +[#4829]: https://github.com/ElementsProject/lightning/pull/4829 +[#4864]: https://github.com/ElementsProject/lightning/pull/4864 +[#4890]: https://github.com/ElementsProject/lightning/pull/4890 [#4896]: https://github.com/ElementsProject/lightning/pull/4896 +[#4897]: https://github.com/ElementsProject/lightning/pull/4897 +[#4900]: https://github.com/ElementsProject/lightning/pull/4900 +[#4902]: https://github.com/ElementsProject/lightning/pull/4902 +[#4902]: https://github.com/ElementsProject/lightning/pull/4902 +[#4902]: https://github.com/ElementsProject/lightning/pull/4902 +[#4906]: https://github.com/ElementsProject/lightning/pull/4906 +[#4908]: https://github.com/ElementsProject/lightning/pull/4908 +[#4911]: https://github.com/ElementsProject/lightning/pull/4911 +[#4913]: https://github.com/ElementsProject/lightning/pull/4913 +[#4921]: https://github.com/ElementsProject/lightning/pull/4921 +[#4925]: https://github.com/ElementsProject/lightning/pull/4925 +[#4929]: https://github.com/ElementsProject/lightning/pull/4929 [#4931]: https://github.com/ElementsProject/lightning/pull/4931 -[#5104]: https://github.com/ElementsProject/lightning/pull/5104 +[#4945]: https://github.com/ElementsProject/lightning/pull/4945 [#4984]: https://github.com/ElementsProject/lightning/pull/4984 -[#5078]: https://github.com/ElementsProject/lightning/pull/5078 +[#4985]: https://github.com/ElementsProject/lightning/pull/4985 +[#4985]: https://github.com/ElementsProject/lightning/pull/4985 +[#5004]: https://github.com/ElementsProject/lightning/pull/5004 +[#5010]: https://github.com/ElementsProject/lightning/pull/5010 +[#5010]: https://github.com/ElementsProject/lightning/pull/5010 [#5013]: https://github.com/ElementsProject/lightning/pull/5013 [#5013]: https://github.com/ElementsProject/lightning/pull/5013 -[#5086]: https://github.com/ElementsProject/lightning/pull/5086 -[#4906]: https://github.com/ElementsProject/lightning/pull/4906 -[#5122]: https://github.com/ElementsProject/lightning/pull/5122 -[#4925]: https://github.com/ElementsProject/lightning/pull/4925 -[#5146]: https://github.com/ElementsProject/lightning/pull/5146 -[#4911]: https://github.com/ElementsProject/lightning/pull/4911 -[#5103]: https://github.com/ElementsProject/lightning/pull/5103 -[#5051]: https://github.com/ElementsProject/lightning/pull/5051 +[#5016]: https://github.com/ElementsProject/lightning/pull/5016 [#5019]: https://github.com/ElementsProject/lightning/pull/5019 -[#4829]: https://github.com/ElementsProject/lightning/pull/4829 -[#5121]: https://github.com/ElementsProject/lightning/pull/5121 +[#5043]: https://github.com/ElementsProject/lightning/pull/5043 +[#5047]: https://github.com/ElementsProject/lightning/pull/5047 +[#5047]: https://github.com/ElementsProject/lightning/pull/5047 +[#5047]: https://github.com/ElementsProject/lightning/pull/5047 +[#5051]: https://github.com/ElementsProject/lightning/pull/5051 +[#5052]: https://github.com/ElementsProject/lightning/pull/5052 +[#5058]: https://github.com/ElementsProject/lightning/pull/5058 +[#5078]: https://github.com/ElementsProject/lightning/pull/5078 +[#5078]: https://github.com/ElementsProject/lightning/pull/5078 +[#5078]: https://github.com/ElementsProject/lightning/pull/5078 [#5081]: https://github.com/ElementsProject/lightning/pull/5081 -[#5130]: https://github.com/ElementsProject/lightning/pull/5130 -[#5120]: https://github.com/ElementsProject/lightning/pull/5120 -[#5010]: https://github.com/ElementsProject/lightning/pull/5010 +[#5085]: https://github.com/ElementsProject/lightning/pull/5085 +[#5085]: https://github.com/ElementsProject/lightning/pull/5085 +[#5086]: https://github.com/ElementsProject/lightning/pull/5086 +[#5086]: https://github.com/ElementsProject/lightning/pull/5086 +[#5090]: https://github.com/ElementsProject/lightning/pull/5090 +[#5101]: https://github.com/ElementsProject/lightning/pull/5101 +[#5103]: https://github.com/ElementsProject/lightning/pull/5103 +[#5103]: https://github.com/ElementsProject/lightning/pull/5103 +[#5103]: https://github.com/ElementsProject/lightning/pull/5103 [#5104]: https://github.com/ElementsProject/lightning/pull/5104 -[#4900]: https://github.com/ElementsProject/lightning/pull/4900 -[#4921]: https://github.com/ElementsProject/lightning/pull/4921 -[#4897]: https://github.com/ElementsProject/lightning/pull/4897 -[#5016]: https://github.com/ElementsProject/lightning/pull/5016 -[#4929]: https://github.com/ElementsProject/lightning/pull/4929 +[#5104]: https://github.com/ElementsProject/lightning/pull/5104 +[#5120]: https://github.com/ElementsProject/lightning/pull/5120 +[#5121]: https://github.com/ElementsProject/lightning/pull/5121 [#5121]: https://github.com/ElementsProject/lightning/pull/5121 -[#5058]: https://github.com/ElementsProject/lightning/pull/5058 -[#5052]: https://github.com/ElementsProject/lightning/pull/5052 -[#5010]: https://github.com/ElementsProject/lightning/pull/5010 -[#4902]: https://github.com/ElementsProject/lightning/pull/4902 -[#5047]: https://github.com/ElementsProject/lightning/pull/5047 -[#5004]: https://github.com/ElementsProject/lightning/pull/5004 -[#5146]: https://github.com/ElementsProject/lightning/pull/5146 -[#5043]: https://github.com/ElementsProject/lightning/pull/5043 [#5122]: https://github.com/ElementsProject/lightning/pull/5122 [#5122]: https://github.com/ElementsProject/lightning/pull/5122 -[#4985]: https://github.com/ElementsProject/lightning/pull/4985 -[#5047]: https://github.com/ElementsProject/lightning/pull/5047 -[#5090]: https://github.com/ElementsProject/lightning/pull/5090 -[#5078]: https://github.com/ElementsProject/lightning/pull/5078 -[#4864]: https://github.com/ElementsProject/lightning/pull/4864 -[#4945]: https://github.com/ElementsProject/lightning/pull/4945 -[#4902]: https://github.com/ElementsProject/lightning/pull/4902 -[#4902]: https://github.com/ElementsProject/lightning/pull/4902 -[#5047]: https://github.com/ElementsProject/lightning/pull/5047 -[#4913]: https://github.com/ElementsProject/lightning/pull/4913 [#5122]: https://github.com/ElementsProject/lightning/pull/5122 -[#4890]: https://github.com/ElementsProject/lightning/pull/4890 -[#5101]: https://github.com/ElementsProject/lightning/pull/5101 -[#5085]: https://github.com/ElementsProject/lightning/pull/5085 -[#4908]: https://github.com/ElementsProject/lightning/pull/4908 [#5122]: https://github.com/ElementsProject/lightning/pull/5122 +[#5122]: https://github.com/ElementsProject/lightning/pull/5122 +[#5130]: https://github.com/ElementsProject/lightning/pull/5130 +[#5146]: https://github.com/ElementsProject/lightning/pull/5146 +[#5146]: https://github.com/ElementsProject/lightning/pull/5146 [0.11.0]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0 ## [0.10.2] - 2021-11-03: Bitcoin Dust Consensus Rule From cf2e2d7445a038d6c09eaa25a0540808de340816 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Mon, 4 Apr 2022 12:50:25 -0700 Subject: [PATCH 0665/1530] changelog: remove duplicate pr links --- CHANGELOG.md | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3d4d858fca3..bd06026c7d3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,7 +98,6 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. -[#4829]: https://github.com/ElementsProject/lightning/pull/4829 [#4829]: https://github.com/ElementsProject/lightning/pull/4829 [#4864]: https://github.com/ElementsProject/lightning/pull/4864 [#4890]: https://github.com/ElementsProject/lightning/pull/4890 @@ -106,8 +105,6 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#4897]: https://github.com/ElementsProject/lightning/pull/4897 [#4900]: https://github.com/ElementsProject/lightning/pull/4900 [#4902]: https://github.com/ElementsProject/lightning/pull/4902 -[#4902]: https://github.com/ElementsProject/lightning/pull/4902 -[#4902]: https://github.com/ElementsProject/lightning/pull/4902 [#4906]: https://github.com/ElementsProject/lightning/pull/4906 [#4908]: https://github.com/ElementsProject/lightning/pull/4908 [#4911]: https://github.com/ElementsProject/lightning/pull/4911 @@ -119,47 +116,29 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#4945]: https://github.com/ElementsProject/lightning/pull/4945 [#4984]: https://github.com/ElementsProject/lightning/pull/4984 [#4985]: https://github.com/ElementsProject/lightning/pull/4985 -[#4985]: https://github.com/ElementsProject/lightning/pull/4985 [#5004]: https://github.com/ElementsProject/lightning/pull/5004 [#5010]: https://github.com/ElementsProject/lightning/pull/5010 -[#5010]: https://github.com/ElementsProject/lightning/pull/5010 -[#5013]: https://github.com/ElementsProject/lightning/pull/5013 [#5013]: https://github.com/ElementsProject/lightning/pull/5013 [#5016]: https://github.com/ElementsProject/lightning/pull/5016 [#5019]: https://github.com/ElementsProject/lightning/pull/5019 [#5043]: https://github.com/ElementsProject/lightning/pull/5043 [#5047]: https://github.com/ElementsProject/lightning/pull/5047 -[#5047]: https://github.com/ElementsProject/lightning/pull/5047 -[#5047]: https://github.com/ElementsProject/lightning/pull/5047 [#5051]: https://github.com/ElementsProject/lightning/pull/5051 [#5052]: https://github.com/ElementsProject/lightning/pull/5052 [#5058]: https://github.com/ElementsProject/lightning/pull/5058 [#5078]: https://github.com/ElementsProject/lightning/pull/5078 -[#5078]: https://github.com/ElementsProject/lightning/pull/5078 -[#5078]: https://github.com/ElementsProject/lightning/pull/5078 [#5081]: https://github.com/ElementsProject/lightning/pull/5081 [#5085]: https://github.com/ElementsProject/lightning/pull/5085 -[#5085]: https://github.com/ElementsProject/lightning/pull/5085 -[#5086]: https://github.com/ElementsProject/lightning/pull/5086 [#5086]: https://github.com/ElementsProject/lightning/pull/5086 [#5090]: https://github.com/ElementsProject/lightning/pull/5090 [#5101]: https://github.com/ElementsProject/lightning/pull/5101 [#5103]: https://github.com/ElementsProject/lightning/pull/5103 -[#5103]: https://github.com/ElementsProject/lightning/pull/5103 -[#5103]: https://github.com/ElementsProject/lightning/pull/5103 -[#5104]: https://github.com/ElementsProject/lightning/pull/5104 [#5104]: https://github.com/ElementsProject/lightning/pull/5104 [#5120]: https://github.com/ElementsProject/lightning/pull/5120 [#5121]: https://github.com/ElementsProject/lightning/pull/5121 -[#5121]: https://github.com/ElementsProject/lightning/pull/5121 -[#5122]: https://github.com/ElementsProject/lightning/pull/5122 -[#5122]: https://github.com/ElementsProject/lightning/pull/5122 -[#5122]: https://github.com/ElementsProject/lightning/pull/5122 -[#5122]: https://github.com/ElementsProject/lightning/pull/5122 [#5122]: https://github.com/ElementsProject/lightning/pull/5122 [#5130]: https://github.com/ElementsProject/lightning/pull/5130 [#5146]: https://github.com/ElementsProject/lightning/pull/5146 -[#5146]: https://github.com/ElementsProject/lightning/pull/5146 [0.11.0]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0 ## [0.10.2] - 2021-11-03: Bitcoin Dust Consensus Rule From c55ae7cbcbb9cfdf608df122c7711cbbceebfab8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 5 Apr 2022 07:19:05 +0930 Subject: [PATCH 0666/1530] CHANGELOG.md: update for final PR merge. Signed-off-by: Rusty Russell --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd06026c7d3c..bf1aa5b26668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ This release named by Simon Vrouwe; this marks the name change to core-lightning ### Added - Protocol: we now support opening multiple channels with the same peer. ([#5078]) - - Protocol: we send/receive IP addresses in `init`, and send updated node_announcement when two peers report the same remote_addr. ([#5052]) + - Protocol: we send/receive IP addresses in `init`, and send updated node_announcement when two peers report the same remote_addr (`disable-ip-discovery` suppresses this announcement). ([#5052]) - Plugins: `cln-grpc` first class GRPC interface for remotely controlling nodes over mTLS authentication; set `grpc-port` to activate ([#5013]) - Database: With the `sqlite3://` scheme for `--wallet` option, you can now specify a second file path for real-time database backup by separating it from the main file path with a `:` character. ([#4890]) - Protocol: `pay` (and decode, etc) supports bolt11 payment_metadata a-la https://github.com/lightning/bolts/pull/912 ([#5086]) @@ -25,6 +25,7 @@ This release named by Simon Vrouwe; this marks the name change to core-lightning - JSON-RPC: `listforwards` has new entry `style`, currently "legacy" or "tlv". ([#5146]) - JSON-RPC: `delinvoice` has a new parameter `desconly` to remove description. ([#5121]) - JSON-RPC: new `setchannel` command generalizes `setchannelfee`: you can now alter the `htlc_minimum_msat` and `htlc_maximum_msat` your node advertizes. ([#5103]) + - Config: `htlc-minimum-msat` and `htlc-maximum-msat` to set default values to advertizes for new channels. ([#5136]) - JSON-RPC: `listpeers` now includes a `pushed_msat` value. For leased channels, is the total lease_fee. ([#5043]) - JSON-RPC: `getinfo` result now includes `our_features` (bits) for various Bolt #9 contexts ([#5047]) - Docker build for ARM defaults to `bitcoin`, but can be overridden with the `LIGHTNINGD_NETWORK` envvar. ([#4896]) @@ -138,6 +139,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#5121]: https://github.com/ElementsProject/lightning/pull/5121 [#5122]: https://github.com/ElementsProject/lightning/pull/5122 [#5130]: https://github.com/ElementsProject/lightning/pull/5130 +[#5136]: https://github.com/ElementsProject/lightning/pull/5136 [#5146]: https://github.com/ElementsProject/lightning/pull/5146 [0.11.0]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0 From d18817a525b2a046189277d8bcd2d80551bad850 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 5 Apr 2022 11:36:58 +0930 Subject: [PATCH 0667/1530] wallet: don't get so upset on orphaned HTLC. User grubman on IRC reported a crash due to new HTLC checks: ``` 2022-04-05T01:15:17.707Z **BROKEN** wallet: Missing preimage for orphaned HTLC; replacing with zeros ... 2022-04-05T01:15:44.950Z **BROKEN** lightningd: check_already_failed:Both failed and succeeded? 2022-04-05T01:15:45.020Z **BROKEN** lightningd: FATAL SIGNAL 6 (version v0.11.0rc1-modded) 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: common/daemon.c:38 (send_backtrace) 0xaaaab8f7f8bb 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: common/daemon.c:46 (crashdump) 0xaaaab8f7f91b 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xffffa4a315bf 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xffffa45c6cd8 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xffffa45b3a27 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/log.c:821 (fatal_vfmt) 0xaaaab8f3e1b7 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/log.c:829 (fatal) 0xaaaab8f3e24b 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/htlc_end.c:87 (corrupt) 0xaaaab8f325d3 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/htlc_end.c:175 (htlc_out_check) 0xaaaab8f32ee3 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/peer_htlcs.c:1471 (check_already_failed) 0xaaaab8f53ea7 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/peer_htlcs.c:1575 (onchain_failed_our_htlc) 0xaaaab8f54aab 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/onchain_control.c:411 (handle_missing_htlc_output) 0xaaaab8f4037b 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/onchain_control.c:544 (onchain_msg) 0xaaaab8f409bf 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/subd.c:556 (sd_msg_read) 0xaaaab8f5f8ef 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:59 (next_plan) 0xaaaab8fda197 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:407 (do_plan) 0xaaaab8fda76f 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:417 (io_ready) 0xaaaab8fda82f 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: ccan/ccan/io/poll.c:453 (io_loop) 0xaaaab8fdc77f 2022-04-05T01:15:45.020Z **BROKEN** lightningd: backtrace: lightningd/io_loop_with_timers.c:22 (io_loop_with_timers) 0xaaaab8f37857 2022-04-05T01:15:45.021Z **BROKEN** lightningd: backtrace: lightningd/lightningd.c:1181 (main) 0xaaaab8f3bcff 2022-04-05T01:15:45.021Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xffffa45b3d4f 2022-04-05T01:15:45.021Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xaaaab8f1ed33 ``` Since no HTLCs are likely to still be pending since v0.6.1, we can remove that compat code. Indeed, this happens on my node: we've not loaded the incoming HTLC because it's resolved. Signed-off-by: Rusty Russell --- wallet/wallet.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index da3fa96d0148..ff9506299b67 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -2648,21 +2648,12 @@ static bool wallet_stmt2htlc_out(struct wallet *wallet, u64 in_id = db_col_u64(stmt, "origin_htlc"); struct htlc_in *hin; + /* If it failed / succeeded already, we could have + * closed incoming htlc */ hin = remove_htlc_in_by_dbid(unconnected_htlcs_in, in_id); if (hin) htlc_out_connect_htlc_in(out, hin); out->am_origin = false; - if (!out->in && !out->preimage) { -#ifdef COMPAT_V061 - log_broken(wallet->log, - "Missing preimage for orphaned HTLC; replacing with zeros"); - out->preimage = talz(out, struct preimage); -#else - fatal("Unable to find corresponding htlc_in %"PRIu64 - " for unfulfilled htlc_out %"PRIu64, - in_id, out->dbid); -#endif - } db_col_ignore(stmt, "partid"); db_col_ignore(stmt, "groupid"); } else { From 8ed6b7050bf1dc751ca15eb76e9faa5f18a92a5a Mon Sep 17 00:00:00 2001 From: kiwiidb Date: Tue, 5 Apr 2022 11:18:30 +0200 Subject: [PATCH 0668/1530] build: bump zlib dependency --- .github/scripts/build.sh | 8 ++++---- Dockerfile | 8 ++++---- contrib/docker/linuxarm32v7.Dockerfile | 8 ++++---- contrib/docker/linuxarm64v8.Dockerfile | 8 ++++---- contrib/docker/scripts/build.sh | 8 ++++---- doc/INSTALL.md | 6 +++--- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 05cece10fa49..bfbb97eb9e38 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -51,14 +51,14 @@ then export STRIP="$TARGET_HOST"-strip export CONFIGURATION_WRAPPER=qemu-"${TARGET_HOST%%-*}"-static - wget -q https://zlib.net/zlib-1.2.11.tar.gz - tar xf zlib-1.2.11.tar.gz - cd zlib-1.2.11 || exit 1 + wget -q https://zlib.net/zlib-1.2.12.tar.gz + tar xf zlib-1.2.12.tar.gz + cd zlib-1.2.12 || exit 1 ./configure --prefix="$QEMU_LD_PREFIX" make sudo make install cd .. || exit 1 - rm zlib-1.2.11.tar.gz && rm -rf zlib-1.2.11 + rm zlib-1.2.12.tar.gz && rm -rf zlib-1.2.12 wget -q https://www.sqlite.org/2018/sqlite-src-3260000.zip unzip -q sqlite-src-3260000.zip diff --git a/Dockerfile b/Dockerfile index 5e4efddf4b6e..47f335b9b354 100644 --- a/Dockerfile +++ b/Dockerfile @@ -70,12 +70,12 @@ RUN apt-get update -qq && \ python3-setuptools \ wget -RUN wget -q https://zlib.net/zlib-1.2.11.tar.gz \ -&& tar xvf zlib-1.2.11.tar.gz \ -&& cd zlib-1.2.11 \ +RUN wget -q https://zlib.net/zlib-1.2.12.tar.gz \ +&& tar xvf zlib-1.2.12.tar.gz \ +&& cd zlib-1.2.12 \ && ./configure \ && make \ -&& make install && cd .. && rm zlib-1.2.11.tar.gz && rm -rf zlib-1.2.11 +&& make install && cd .. && rm zlib-1.2.12.tar.gz && rm -rf zlib-1.2.12 RUN apt-get install -y --no-install-recommends unzip tclsh \ && wget -q https://www.sqlite.org/2019/sqlite-src-3290000.zip \ diff --git a/contrib/docker/linuxarm32v7.Dockerfile b/contrib/docker/linuxarm32v7.Dockerfile index fe843a51da2c..7120befb5e75 100644 --- a/contrib/docker/linuxarm32v7.Dockerfile +++ b/contrib/docker/linuxarm32v7.Dockerfile @@ -62,12 +62,12 @@ STRIP=${target_host}-strip \ QEMU_LD_PREFIX=/usr/${target_host} \ HOST=${target_host} -RUN wget -q https://zlib.net/zlib-1.2.11.tar.gz \ -&& tar xvf zlib-1.2.11.tar.gz \ -&& cd zlib-1.2.11 \ +RUN wget -q https://zlib.net/zlib-1.2.12.tar.gz \ +&& tar xvf zlib-1.2.12.tar.gz \ +&& cd zlib-1.2.12 \ && ./configure --prefix=$QEMU_LD_PREFIX \ && make \ -&& make install && cd .. && rm zlib-1.2.11.tar.gz && rm -rf zlib-1.2.11 +&& make install && cd .. && rm zlib-1.2.12.tar.gz && rm -rf zlib-1.2.12 RUN apt-get install -y --no-install-recommends unzip tclsh \ && wget -q https://www.sqlite.org/2019/sqlite-src-3290000.zip \ diff --git a/contrib/docker/linuxarm64v8.Dockerfile b/contrib/docker/linuxarm64v8.Dockerfile index 4c461b688afc..ff4e65af76c6 100644 --- a/contrib/docker/linuxarm64v8.Dockerfile +++ b/contrib/docker/linuxarm64v8.Dockerfile @@ -62,12 +62,12 @@ STRIP=${target_host}-strip \ QEMU_LD_PREFIX=/usr/${target_host} \ HOST=${target_host} -RUN wget -q https://zlib.net/zlib-1.2.11.tar.gz \ -&& tar xvf zlib-1.2.11.tar.gz \ -&& cd zlib-1.2.11 \ +RUN wget -q https://zlib.net/zlib-1.2.12.tar.gz \ +&& tar xvf zlib-1.2.12.tar.gz \ +&& cd zlib-1.2.12 \ && ./configure --prefix=$QEMU_LD_PREFIX \ && make \ -&& make install && cd .. && rm zlib-1.2.11.tar.gz && rm -rf zlib-1.2.11 +&& make install && cd .. && rm zlib-1.2.12.tar.gz && rm -rf zlib-1.2.12 RUN apt-get install -y --no-install-recommends unzip tclsh \ && wget -q https://www.sqlite.org/2019/sqlite-src-3290000.zip \ diff --git a/contrib/docker/scripts/build.sh b/contrib/docker/scripts/build.sh index 1650e793c794..c54173d42561 100755 --- a/contrib/docker/scripts/build.sh +++ b/contrib/docker/scripts/build.sh @@ -52,14 +52,14 @@ then export STRIP="$TARGET_HOST"-strip export CONFIGURATION_WRAPPER=qemu-"${TARGET_HOST%%-*}"-static - wget -q https://zlib.net/zlib-1.2.11.tar.gz - tar xf zlib-1.2.11.tar.gz - cd zlib-1.2.11 || exit 1 + wget -q https://zlib.net/zlib-1.2.12.tar.gz + tar xf zlib-1.2.12.tar.gz + cd zlib-1.2.12 || exit 1 ./configure --prefix="$QEMU_LD_PREFIX" make sudo make install cd .. || exit 1 - rm zlib-1.2.11.tar.gz && rm -rf zlib-1.2.11 + rm zlib-1.2.12.tar.gz && rm -rf zlib-1.2.12 wget -q https://www.sqlite.org/2018/sqlite-src-3260000.zip unzip -q sqlite-src-3260000.zip diff --git a/doc/INSTALL.md b/doc/INSTALL.md index b01c88e97562..42c994259157 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -343,9 +343,9 @@ Obtain and install cross-compiled versions of sqlite3, gmp and zlib: Download and build zlib: - wget https://zlib.net/zlib-1.2.11.tar.gz - tar xvf zlib-1.2.11.tar.gz - cd zlib-1.2.11 + wget https://zlib.net/zlib-1.2.12.tar.gz + tar xvf zlib-1.2.12.tar.gz + cd zlib-1.2.12 ./configure --prefix=$QEMU_LD_PREFIX make make install From 17903c89f29f23a6c12f7dfcad196263a0e01b3d Mon Sep 17 00:00:00 2001 From: Clay Shoaf <31578812+ClayShoaf@users.noreply.github.com> Date: Tue, 5 Apr 2022 21:36:08 -0400 Subject: [PATCH 0669/1530] Update INSTALL.md changed `make` to `poetry run make`. Also moved the spot where `poetry install` is run as it wouldn't make sense to run it before the project is cloned. --- doc/INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 42c994259157..34620791eb2a 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -41,7 +41,6 @@ Get dependencies: python3 python3-pip net-tools zlib1g-dev libsodium-dev gettext pip3 install --upgrade pip pip3 install --user poetry - poetry install If you don't have Bitcoin installed locally you'll need to install that as well. It's now available via [snapd](https://snapcraft.io/bitcoin-core). @@ -64,8 +63,9 @@ For development or running tests, get additional dependencies: Build lightning: + poetry install ./configure - make + poetry run make sudo make install Running lightning: From 356514c753ce5e9bf3f81f7ccbc397080c2d9d02 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 6 Apr 2022 11:13:21 +0930 Subject: [PATCH 0670/1530] doc: note how to do Rust builds on Ubuntu, at least. In particular, using cargo to install rustfmt doesn't work. Signed-off-by: Rusty Russell --- doc/INSTALL.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 34620791eb2a..459055eb5eb7 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -61,6 +61,10 @@ For development or running tests, get additional dependencies: sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ libsecp256k1-dev jq +If you want to build the Rust plugins (currently, cln-grpc): + + sudo apt-get install -y cargo rustfmt + Build lightning: poetry install @@ -74,8 +78,6 @@ Running lightning: ./lightningd/lightningd & ./cli/lightning-cli help -**Note**: You may need to include `testnet=1` in `bitcoin.conf` - To Build on Fedora --------------------- From d95ea51a737849cc232248739c5c0aaf3d9b7232 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 6 Apr 2022 11:24:09 +0930 Subject: [PATCH 0671/1530] lightning-cli: document the real argument handling (for special effects). And fix it to pass through decimals like the man page promises! Signed-off-by: Rusty Russell --- cli/lightning-cli.c | 2 +- doc/lightning-cli.1.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index 06e47f2b849c..ede08a6b4485 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -276,7 +276,7 @@ static bool is_literal(const char *arg) if (arglen == 0) { return false; } - return strspn(arg, "0123456789") == arglen + return strspn(arg, "0123456789.") == arglen || streq(arg, "true") || streq(arg, "false") || streq(arg, "null") diff --git a/doc/lightning-cli.1.md b/doc/lightning-cli.1.md index 254616c1d496..263340bbe4f6 100644 --- a/doc/lightning-cli.1.md +++ b/doc/lightning-cli.1.md @@ -89,7 +89,8 @@ this is to avoid having lightningd intrepret the position of an arguement. Arguments may be integer numbers (composed entirely of digits), floating-point numbers (has a radix point but otherwise composed of digits), *true*, *false*, -or *null*. Other arguments are treated as strings. +or *null*. Arguments which begin with *{*, *[* or *"* are also considered +raw JSON and are passed through. Other arguments are treated as strings. Some commands have optional arguments. You may use *null* to skip optional arguments to provide later arguments, although this is not encouraged. From 8c2f3ab5fe3af4baf590dce8c78a9b02a81d089d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 6 Apr 2022 11:34:26 +0930 Subject: [PATCH 0672/1530] CHANGELOG.md: fix typos Reported-by: @endothermicdev Signed-off-by: Rusty Russell --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf1aa5b26668..4869b7075f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,11 +89,11 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. ### EXPERIMENTAL - - Fixex `experimental-websocket` intermittent read errors ([#5090]) + - Fixed `experimental-websocket` intermittent read errors ([#5090]) - Fixed `experimental-websocket-port` not to leave zombie processes. ([#5101]) - Config option `--lease-fee-base-msat` renamed to `--lease-fee-base-sat` ([#5047]) - Config option `--lease-fee-base-msat` deprecated and will be removed next release ([#5047]) - - Fixex `experimental-websocket-port` to work with default addresses. ([#4945]) + - Fixed `experimental-websocket-port` to work with default addresses. ([#4945]) - Protocol: removed support for v0.10.1 onion messages. ([#4921]) - Protocol: Ability to announce DNS addresses ([#4829]) From 836c1b805bdd9eabd7f74f8663f0cf3d995d14df Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 6 Apr 2022 14:39:48 +0930 Subject: [PATCH 0673/1530] doc: update c-lightning to Core Lightning almost everywhere. Mostly comments and docs: some places are actually paths, which I have avoided changing. We may migrate them slowly, particularly when they're user-visible. Signed-off-by: Rusty Russell --- .github/workflows/ci.yaml | 2 +- Dockerfile | 6 +- Makefile | 1 + README.md | 16 ++--- action.yml | 2 +- bitcoin/psbt.h | 2 +- channeld/full_channel.c | 2 +- cln-rpc/README.md | 2 +- configure | 2 +- connectd/multiplex.c | 2 +- contrib/docker/linuxarm64v8.Dockerfile | 4 +- contrib/pylightning/README.md | 4 +- contrib/pyln-client/README.md | 4 +- contrib/pyln-client/pyproject.toml | 2 +- contrib/pyln-proto/README.md | 2 +- contrib/pyln-proto/tests/test_onion.py | 2 +- contrib/pyln-testing/README.md | 8 +-- contrib/pyln-testing/pyln/testing/utils.py | 2 +- contrib/pyln-testing/pyproject.toml | 2 +- doc/FAQ.md | 8 +-- doc/HACKING.md | 4 +- doc/INSTALL.md | 8 +-- doc/PLUGINS.md | 12 ++-- doc/REPRODUCIBLE.md | 8 +-- doc/STYLE.md | 2 +- doc/TOR.md | 72 +++++++++++----------- doc/conf.py | 4 +- doc/index.rst | 4 +- doc/lightning-close.7.md | 2 +- doc/lightning-createonion.7.md | 2 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-getinfo.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listforwards.7.md | 2 +- doc/lightning-listfunds.7.md | 2 +- doc/lightning-multifundchannel.7.md | 4 +- doc/lightning-multiwithdraw.7.md | 2 +- doc/lightning-newaddr.7.md | 6 +- doc/lightning-sendcustommsg.7.md | 2 +- doc/lightning-sendonion.7.md | 14 ++--- doc/lightning-setchannel.7.md | 2 +- doc/lightning-setchannelfee.7.md | 2 +- doc/lightning-stop.7.md | 6 +- doc/lightning-txprepare.7.md | 2 +- doc/lightning-withdraw.7.md | 2 +- doc/lightningd-config.5.md | 10 +-- hsmd/hsmd.c | 2 +- lightningd/jsonrpc.c | 2 +- lightningd/lightningd.c | 4 +- lightningd/plugin_control.c | 2 +- plugins/examples/cln-plugin-startup.rs | 2 +- plugins/spender/openchannel.c | 2 +- plugins/src/lib.rs | 6 +- poetry.lock | 4 +- wire/tlvstream.h | 2 +- 56 files changed, 142 insertions(+), 141 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 41936c999c83..708ba27269b3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -71,7 +71,7 @@ jobs: if-no-files-found: ignore check-dock: - name: Check c-lightning doc + name: Check core-lightning doc runs-on: ubuntu-20.04 env: DEVELOPER: 1 diff --git a/Dockerfile b/Dockerfile index 47f335b9b354..8077edacd91e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ -# This dockerfile is meant to compile a c-lightning x64 image +# This dockerfile is meant to compile a core-lightning x64 image # It is using multi stage build: -# * downloader: Download litecoin/bitcoin and qemu binaries needed for c-lightning -# * builder: Compile c-lightning dependencies, then c-lightning itself with static linking +# * downloader: Download litecoin/bitcoin and qemu binaries needed for core-lightning +# * builder: Compile core-lightning dependencies, then c-lightning itself with static linking # * final: Copy the binaries required at runtime # The resulting image uploaded to dockerhub will only contain what is needed for runtime. # From the root of the repository, run "docker build -t yourimage:yourtag ." diff --git a/Makefile b/Makefile index dd1eb972274e..737f26641133 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ SUPPRESS_OUTPUT := endif DISTRO=$(shell lsb_release -is 2>/dev/null || echo unknown)-$(shell lsb_release -rs 2>/dev/null || echo unknown) +# Changing this could break installs! PKGNAME = c-lightning # We use our own internal ccan copy. diff --git a/README.md b/README.md index ad7ee770ff8b..8124b0696a0e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# c-lightning: A specification compliant Lightning Network implementation in C +# Core Lightning (CLN): A specification compliant Lightning Network implementation in C -c-lightning is a lightweight, highly customizable and [standard compliant][std] implementation of the Lightning Network protocol. +Core Lightning (previously c-lightning) is a lightweight, highly customizable and [standard compliant][std] implementation of the Lightning Network protocol. * [Getting Started](#getting-started) * [Installation](#installation) @@ -32,7 +32,7 @@ Don't hesitate to reach out to us on IRC at [#lightning-dev @ libera.chat][irc1] ## Getting Started -c-lightning only works on Linux and Mac OS, and requires a locally (or remotely) running `bitcoind` (version 0.16 or above) that is fully caught up with the network you're running on, and relays transactions (ie with `blocksonly=0`). +Core Lightning only works on Linux and Mac OS, and requires a locally (or remotely) running `bitcoind` (version 0.16 or above) that is fully caught up with the network you're running on, and relays transactions (ie with `blocksonly=0`). Pruning (`prune=n` option in `bitcoin.conf`) is partially supported, see [here](#pruning) for more details. ### Installation @@ -95,7 +95,7 @@ This creates a `.lightning/` subdirectory in your home directory: see `man -l do ### Using The JSON-RPC Interface -c-lightning exposes a [JSON-RPC 2.0][jsonrpcspec] interface over a Unix Domain socket; the `lightning-cli` tool can be used to access it, or there is a [python client library](contrib/pyln-client). +Core Lightning exposes a [JSON-RPC 2.0][jsonrpcspec] interface over a Unix Domain socket; the `lightning-cli` tool can be used to access it, or there is a [python client library](contrib/pyln-client). You can use `lightning-cli help` to print a table of RPC methods; `lightning-cli help ` will offer specific information on that command. @@ -116,7 +116,7 @@ Once you've started for the first time, there's a script called `contrib/bootstrap-node.sh` which will connect you to other nodes on the lightning network. -There are also numerous plugins available for c-lightning which add +There are also numerous plugins available for Core Lightning which add capabilities: in particular there's a collection at: https://github.com/lightningd/plugins @@ -206,11 +206,11 @@ To use a configuration file, create a file named `config` within your top-level ### Pruning -c-lightning requires JSON-RPC access to a fully synchronized `bitcoind` in order to synchronize with the Bitcoin network. +Core Lightning requires JSON-RPC access to a fully synchronized `bitcoind` in order to synchronize with the Bitcoin network. Access to ZeroMQ is not required and `bitcoind` does not need to be run with `txindex` like other implementations. The lightning daemon will poll `bitcoind` for new blocks that it hasn't processed yet, thus synchronizing itself with `bitcoind`. -If `bitcoind` prunes a block that c-lightning has not processed yet, e.g., c-lightning was not running for a prolonged period, then `bitcoind` will not be able to serve the missing blocks, hence c-lightning will not be able to synchronize anymore and will be stuck. -In order to avoid this situation you should be monitoring the gap between c-lightning's blockheight using `lightning-cli getinfo` and `bitcoind`'s blockheight using `bitcoin-cli getblockchaininfo`. +If `bitcoind` prunes a block that Core Lightning has not processed yet, e.g., Core Lightning was not running for a prolonged period, then `bitcoind` will not be able to serve the missing blocks, hence Core Lightning will not be able to synchronize anymore and will be stuck. +In order to avoid this situation you should be monitoring the gap between Core Lightning's blockheight using `lightning-cli getinfo` and `bitcoind`'s blockheight using `bitcoin-cli getblockchaininfo`. If the two blockheights drift apart it might be necessary to intervene. ### HD wallet encryption diff --git a/action.yml b/action.yml index 99390776ecb5..034d68c8e852 100644 --- a/action.yml +++ b/action.yml @@ -1,6 +1,6 @@ --- name: 'Lightning CI' -description: 'A preconfigured container with all c-lightning dependencies' +description: 'A preconfigured container with all Core Lightning dependencies' runs: using: 'docker' image: 'contrib/Dockerfile.tester' diff --git a/bitcoin/psbt.h b/bitcoin/psbt.h index 3aadc5028964..53ff1c4cef29 100644 --- a/bitcoin/psbt.h +++ b/bitcoin/psbt.h @@ -92,7 +92,7 @@ bool psbt_finalize(struct wally_psbt *psbt); */ struct wally_tx *psbt_final_tx(const tal_t *ctx, const struct wally_psbt *psbt); -/* psbt_make_key - Create a new, proprietary c-lightning key +/* psbt_make_key - Create a new, proprietary Core Lightning key * * @ctx - allocation context * @key_subtype - type for this key diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 87e736bb34ee..05448eb25b2c 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -462,7 +462,7 @@ static bool htlc_dust(const struct channel *channel, * leads to the channel starving at the feast! This was reported by * ACINQ's @t-bast * (https://github.com/lightningnetwork/lightning-rfc/issues/728) and - * demonstrated with c-lightning by @m-schmoock + * demonstrated with Core Lightning by @m-schmoock * (https://github.com/ElementsProject/lightning/pull/3498). * * To mostly avoid this situation, at least from our side, we apply an diff --git a/cln-rpc/README.md b/cln-rpc/README.md index d637e040cf37..1bd4df371102 100644 --- a/cln-rpc/README.md +++ b/cln-rpc/README.md @@ -1,3 +1,3 @@ -# `cln-rpc`: Talk to c-lightning +# `cln-rpc`: Talk to Core Lightning diff --git a/configure b/configure index e07d52069675..2e0de79843b0 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# Simple configure script for c-lightning. +# Simple configure script for Core Lightning. set -e diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 5e71dbe940b9..fafc326a9fb0 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -519,7 +519,7 @@ static void handle_ping_reply(struct peer *peer, const u8 *msg) status_peer_unusual(&peer->id, "Got malformed ping reply %s", tal_hex(tmpctx, msg)); - /* We print this because dev versions of c-lightning embed + /* We print this because dev versions of Core Lightning embed * version here: see check_ping_make_pong! */ for (i = 0; i < tal_count(ignored); i++) { if (ignored[i] < ' ' || ignored[i] == 127) diff --git a/contrib/docker/linuxarm64v8.Dockerfile b/contrib/docker/linuxarm64v8.Dockerfile index ff4e65af76c6..3e8fe5d3f982 100644 --- a/contrib/docker/linuxarm64v8.Dockerfile +++ b/contrib/docker/linuxarm64v8.Dockerfile @@ -1,7 +1,7 @@ # This dockerfile is meant to cross compile with a x64 machine for a arm64v8 host # It is using multi stage build: -# * downloader: Download litecoin/bitcoin and qemu binaries needed for c-lightning -# * builder: Cross compile c-lightning dependencies, then c-lightning itself with static linking +# * downloader: Download litecoin/bitcoin and qemu binaries needed for Core Lightning +# * builder: Cross compile Core Lightning dependencies, then Core Lightning itself with static linking # * final: Copy the binaries required at runtime # The resulting image uploaded to dockerhub will only contain what is needed for runtime. # From the root of the repository, run "docker build -t yourimage:yourtag -f contrib/linuxarm64v8.Dockerfile ." diff --git a/contrib/pylightning/README.md b/contrib/pylightning/README.md index 56506afe5056..4487607ac2cd 100644 --- a/contrib/pylightning/README.md +++ b/contrib/pylightning/README.md @@ -18,7 +18,7 @@ pip install pylightning ``` Alternatively you can also install the development version to get access to -currently unreleased features by checking out the c-lightning source code and +currently unreleased features by checking out the Core Lightning source code and installing into your python3 environment: ```bash @@ -43,7 +43,7 @@ Generate invoice on one daemon and pay it on the other from lightning import LightningRpc import random -# Create two instances of the LightningRpc object using two different c-lightning daemons on your computer +# Create two instances of the LightningRpc object using two different Core Lightning daemons on your computer l1 = LightningRpc("/tmp/lightning1/lightning-rpc") l5 = LightningRpc("/tmp/lightning5/lightning-rpc") diff --git a/contrib/pyln-client/README.md b/contrib/pyln-client/README.md index 55c6051fbbb9..0a16c4d98ef1 100644 --- a/contrib/pyln-client/README.md +++ b/contrib/pyln-client/README.md @@ -15,7 +15,7 @@ pip install pyln-client ``` Alternatively you can also install the development version to get access to -currently unreleased features by checking out the c-lightning source code and +currently unreleased features by checking out the Core Lightning source code and installing into your python3 environment: ```bash @@ -40,7 +40,7 @@ Generate invoice on one daemon and pay it on the other from pyln.client import LightningRpc import random -# Create two instances of the LightningRpc object using two different c-lightning daemons on your computer +# Create two instances of the LightningRpc object using two different Core Lightning daemons on your computer l1 = LightningRpc("/tmp/lightning1/lightning-rpc") l5 = LightningRpc("/tmp/lightning5/lightning-rpc") diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index 1b4bac179d30..0ca9b6e714fb 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "pyln-client" version = "0.10.2.post1" -description = "Client library and plugin library for c-lightning" +description = "Client library and plugin library for Core Lightning" authors = ["Christian Decker "] license = "BSD-MIT" readme = "README.md" diff --git a/contrib/pyln-proto/README.md b/contrib/pyln-proto/README.md index 497ab3c0baa5..a01bc77442bc 100644 --- a/contrib/pyln-proto/README.md +++ b/contrib/pyln-proto/README.md @@ -15,7 +15,7 @@ pip install pyln-proto ``` Alternatively you can also install the development version to get access to -currently unreleased features by checking out the c-lightning source code and +currently unreleased features by checking out the Core Lightning source code and installing into your python3 environment: ```bash diff --git a/contrib/pyln-proto/tests/test_onion.py b/contrib/pyln-proto/tests/test_onion.py index f48878cc9db8..0b11d4cac10e 100644 --- a/contrib/pyln-proto/tests/test_onion.py +++ b/contrib/pyln-proto/tests/test_onion.py @@ -212,7 +212,7 @@ def sphinx_path_from_test_vector(filename: str) -> Tuple[onion.SphinxPath, dict] def test_hop_params(): """Test that we generate the onion parameters correctly. - Extracted from running the c-lightning implementation: + Extracted from running the Core Lightning implementation: ```bash devtools/onion runtest tests/vectors/onion-test-multi-frame.json diff --git a/contrib/pyln-testing/README.md b/contrib/pyln-testing/README.md index 93d31b2bdc3a..7ddc98149f74 100644 --- a/contrib/pyln-testing/README.md +++ b/contrib/pyln-testing/README.md @@ -1,11 +1,11 @@ -# pyln-testing: A library to write tests against c-lightning +# pyln-testing: A library to write tests against Core Lightning This library implements a number of utilities that help building tests for -c-lightning nodes. In particular it provides a number of pytest fixtures that +Core Lightning nodes. In particular it provides a number of pytest fixtures that allow the management of a test network of a given topology and then execute a test scenarion. -`pyln-testing` is used by c-lightning for its internal tests, and by the +`pyln-testing` is used by Core Lightning for its internal tests, and by the community plugin directory to exercise the plugins. ## Installation @@ -17,7 +17,7 @@ pip install pyln-testing ``` Alternatively you can also install the development version to get access to -currently unreleased features by checking out the c-lightning source code and +currently unreleased features by checking out the Core Lightning source code and installing into your python3 environment: ```bash diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 72f8d9b5aa74..90ec96a10ed2 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -53,7 +53,7 @@ def env(name, default=None): """Access to environment variables Allows access to environment variables, falling back to config.vars (part - of c-lightning's `./configure` output), and finally falling back to a + of Core Lightning's `./configure` output), and finally falling back to a default value. """ diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index 6d9f9e56975e..6b2e5304b146 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -1,7 +1,7 @@ [tool.poetry] name = "pyln-testing" version = "0.10.2" -description = "Test your c-lightning integration, plugins or whatever you want" +description = "Test your Core Lightning integration, plugins or whatever you want" authors = ["Christian Decker "] license = "BSD-MIT" readme = "README.md" diff --git a/doc/FAQ.md b/doc/FAQ.md index 6ec1a9f0b89c..7df516248ae8 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -71,7 +71,7 @@ Note that if you already have a channel open to them, you'll need to close it be There is no risk to your channels if your IP address changes. Other nodes might not be able to connect to you, but your node can still connect to them. -But c-lightning also has an integrated IPv4/6 address discovery mechanism. +But Core Lightning also has an integrated IPv4/6 address discovery mechanism. If your node detects an new public address, it will update its announcement. For this to work binhind a NAT router you need to forward the TCP port 9735 to your node. @@ -91,7 +91,7 @@ If you use a single `bitcoind` for multiple `lightningd`'s, be sure to raise the max RPC thread limit (`-rpcthreads`), each `lightningd` can use up to 4 threads, which is the default `bitcoind` max. -### Can I use C-lightning on mobile ? +### Can I use Core Lightning on mobile ? #### Remote control @@ -113,7 +113,7 @@ In summary: as a Bitcoin user, one may be familiar with a file or a seed (or some mnemonics) from which it can recover all its funds. -C-lightning has an internal bitcoin wallet, which you can use to make "on-chain" +Core Lightning has an internal bitcoin wallet, which you can use to make "on-chain" transactions, (see [withdraw](https://lightning.readthedocs.io/lightning-withdraw.7.html). These on-chain funds are backed up via the HD wallet seed, stored in byte-form in `hsm_secret`. @@ -137,7 +137,7 @@ Tools for replication are currently in active development, using the `db_write` There are 3 types of 'rescans' you can make: - `rescanblockchain`: A `bitcoind` RPC call which rescans the blockchain - starting at the given height. This does not have an effect on c-lightning + starting at the given height. This does not have an effect on Core Lightning as `lightningd` tracks all block and wallet data independently. - `--rescan=depth`: A `lightningd` configuration flag. This flag is read at node startup and tells lightningd at what depth from current blockheight to rebuild its internal state. diff --git a/doc/HACKING.md b/doc/HACKING.md index 22a6406c4fa5..620fb5f24a56 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -91,7 +91,7 @@ Here's a list of parts, with notes: Debugging --------- -You can build c-lightning with DEVELOPER=1 to use dev commands listed in +You can build Core Lightning with DEVELOPER=1 to use dev commands listed in ``cli/lightning-cli help``. ``./configure --enable-developer`` will do that. You can log console messages with log_info() in lightningd and status_debug() in other subdaemons. @@ -110,7 +110,7 @@ subdaemon will be stopped (it sends itself a SIGSTOP); you'll need to Database -------- -c-lightning state is persisted in `lightning-dir`. +Core Lightning state is persisted in `lightning-dir`. It is a sqlite database stored in the `lightningd.sqlite3` file, typically under `~/.lightning//`. You can run queries against this file like so: diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 459055eb5eb7..897cdf1bacdf 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -138,7 +138,7 @@ To Build on FreeBSD OS version: FreeBSD 11.1-RELEASE or above -c-lightning is in the FreeBSD ports, so install it as any other port +Core Lightning is in the FreeBSD ports, so install it as any other port (dependencies are handled automatically): # pkg install c-lightning @@ -285,7 +285,7 @@ To cross-compile for Android Make a standalone toolchain as per https://developer.android.com/ndk/guides/standalone_toolchain.html. -For c-lightning you must target an API level of 24 or higher. +For Core Lightning you must target an API level of 24 or higher. Depending on your toolchain location and target arch, source env variables such as: @@ -370,7 +370,7 @@ Download and build gmp: make make install -Then, build c-lightning with the following commands: +Then, build Core Lightning with the following commands: ./configure make @@ -381,7 +381,7 @@ For all the other Pi devices out there, consider using [Armbian](https://www.arm You can compile in `customize-image.sh` using the instructions for Ubuntu. -A working example that compiles both bitcoind and c-lightning for Armbian can +A working example that compiles both bitcoind and Core Lightning for Armbian can be found [here](https://github.com/Sjors/armbian-bitcoin-core). To compile for Alpine diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index bd38a7eb49c9..c9ca9e568c9d 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1,7 +1,7 @@ # Plugins Plugins are a simple yet powerful way to extend the functionality -provided by c-lightning. They are subprocesses that are started by the +provided by Core Lightning. They are subprocesses that are started by the main `lightningd` daemon and can interact with `lightningd` in a variety of ways: @@ -873,7 +873,7 @@ rely on the shutdown notification always been send. ## Hooks Hooks allow a plugin to define custom behavior for `lightningd` -without having to modify the c-lightning source code itself. A plugin +without having to modify the Core Lightning source code itself. A plugin declares that it'd like to be consulted on what to do next for certain events in the daemon. A hook can then decide how `lightningd` should react to the given event. @@ -1555,7 +1555,7 @@ The `custommsg` plugin hook is the receiving counterpart to the [`sendcustommsg`][sendcustommsg] RPC method and allows plugins to handle messages that are not handled internally. The goal of these two components is to allow the implementation of custom protocols or prototypes on top of a -c-lightning node, without having to change the node's implementation itself. +Core Lightning node, without having to change the node's implementation itself. The payload for a call follows this format: @@ -1569,12 +1569,12 @@ The payload for a call follows this format: This payload would have been sent by the peer with the `node_id` matching `peer_id`, and the message has type `0x1337` and contents `ffffffff`. Notice that the messages are currently limited to odd-numbered types and must not -match a type that is handled internally by c-lightning. These limitations are +match a type that is handled internally by Core Lightning. These limitations are in place in order to avoid conflicts with the internal state tracking, and avoiding disconnections or channel closures, since odd-numbered message can be ignored by nodes (see ["it's ok to be odd" in the specification][oddok] for details). The plugin must implement the parsing of the message, including the -type prefix, since c-lightning does not know how to parse the message. +type prefix, since Core Lightning does not know how to parse the message. Because this is a chained hook, the daemon expects the result to be `{'result': 'continue'}`. It will fail if something else is returned. @@ -1625,7 +1625,7 @@ will cause the message not to be handed to any other hooks. ## Bitcoin backend -C-lightning communicates with the Bitcoin network through a plugin. It uses the +Core Lightning communicates with the Bitcoin network through a plugin. It uses the `bcli` plugin by default but you can use a custom one, multiple custom ones for different operations, or write your own for your favourite Bitcoin data source! diff --git a/doc/REPRODUCIBLE.md b/doc/REPRODUCIBLE.md index 87cdfab6ffc9..55b94af16b48 100644 --- a/doc/REPRODUCIBLE.md +++ b/doc/REPRODUCIBLE.md @@ -1,6 +1,6 @@ -# Reproducible builds for c-lightning +# Reproducible builds for Core Lightning -This document describes the steps involved to build c-lightning in a +This document describes the steps involved to build Core Lightning in a reproducible way. Reproducible builds close the final gap in the lifecycle of open-source projects by allowing maintainers to verify and certify that a given binary was indeed produced by compiling an unmodified version of the @@ -8,7 +8,7 @@ publicly available source. In particular the maintainer certifies that the binary corresponds a) to the exact version of the and b) that no malicious changes have been applied before or after the compilation. -c-lightning has provided a manifest of the binaries included in a release, +Core Lightning has provided a manifest of the binaries included in a release, along with signatures from the maintainers since version 0.6.2. The steps involved in creating reproducible builds are: @@ -109,7 +109,7 @@ DISTRIB_DESCRIPTION="Ubuntu 18.04 LTS" ## Builder image setup Once we have the clean base image we need to customize it to be able to build -c-lightning. This includes disabling the update repositories, downloading the +Core Lightning. This includes disabling the update repositories, downloading the build dependencies and specifying the steps required to perform the build. For this purpose we have a number of Dockerfiles in the diff --git a/doc/STYLE.md b/doc/STYLE.md index bab3a2b4dc4e..78c0ba84b257 100644 --- a/doc/STYLE.md +++ b/doc/STYLE.md @@ -218,7 +218,7 @@ cautiously as too many parameters get unwieldy quickly. ## Github Workflows We have adopted a number of workflows to facilitate the development of -c-lightning, and to make things more pleasant for contributors. +Core Lightning, and to make things more pleasant for contributors. ### Changelog Entries in Commit Messages diff --git a/doc/TOR.md b/doc/TOR.md index 083a939d6b94..74d9b80eb3bc 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -1,6 +1,6 @@ -# Setting up TOR with c-lightning +# Setting up TOR with Core Lightning -To use any Tor features with c-lightning you must have Tor installed and running. +To use any Tor features with Core Lightning you must have Tor installed and running. Note that we only support Tor v3: you can check your installed Tor version with `tor --version` or `sudo tor --version` @@ -19,13 +19,13 @@ just check that this line is present in the Tor config file `/etc/tor/torrc`: `ExitPolicy reject *:* # no exits allowed` -This does not affect c-lightning connect, listen, etc.. +This does not affect Core Lightning connect, listen, etc.. It will only prevent your node from becoming a Tor exit node. Only enable this if you are sure about the implications. If you don't want to create .onion addresses this should be enough. -There are several ways by which a c-lightning node can accept or make connections over Tor. +There are several ways by which a Core Lightning node can accept or make connections over Tor. The node can be reached over Tor by connecting to its .onion address. @@ -47,7 +47,7 @@ Tor provides NAT-traversal for free, so even if you or your ISP has a complex network between you and the Internet, as long as you can use Tor you can be connected to. -Note: c-lightning also support IPv4/6 address discovery behind NAT routers. +Note: Core Lightning also support IPv4/6 address discovery behind NAT routers. For this to work you need to forward the TCP port 9735 to your node. In this case you don't need TOR to punch through your firewall. This usually has the benefit of quicker and more stable connections but does not @@ -113,12 +113,12 @@ the user that will run `lightningd` and check this command: cat /run/tor/control.authcookie > /dev/null ``` -If the above prints nothing and returns, then C-Lightning "should" work +If the above prints nothing and returns, then Core Lightning "should" work with your Tor. If it prints an error, some configuration problem will likely prevent -C-Lightning from working with your Tor. +Core Lightning from working with your Tor. -Then make sure these are in your `${LIGHTNING_DIR}/config` or other C-Lightning configuration +Then make sure these are in your `${LIGHTNING_DIR}/config` or other Core Lightning configuration (or prepend `--` to each of them and add them to your `lightningd` invocation command line): @@ -129,25 +129,25 @@ addr=statictor:127.0.0.1:9051 always-use-proxy=true ``` -1. `proxy` informs C-Lightning that you have a SOCKS5 proxy at port 9050. - C-Lightning will assume that this is a Tor proxy, port 9050 is the +1. `proxy` informs Core Lightning that you have a SOCKS5 proxy at port 9050. + Core Lightning will assume that this is a Tor proxy, port 9050 is the default in most Linux distributions; you can double-check `/etc/tor/torrc` for a `SocksPort` entry to confirm the port number. -2. `bind-addr` informs C-Lightning to bind itself to port 9735. +2. `bind-addr` informs Core Lightning to bind itself to port 9735. This is needed for the subsequent `statictor` to work. 9735 is the normal Lightning Network port, so this setting may already be present. If you add a second `bind-addr=...` you may get errors, so choose this new one or keep the old one, but don't keep both. This has to appear before any `statictor:` setting. -3. `addr=statictor:` informs C-Lightning that you want to create a persistent +3. `addr=statictor:` informs Core Lightning that you want to create a persistent hidden service that is based on your node private key. - This informs C-Lightning as well that the Tor Control Port is 9051. + This informs Core Lightning as well that the Tor Control Port is 9051. You can also use `bind-addr=statictor:` instead to not announce the persistent hidden service, but if anyone wants to make a channel with you, you either have to connect to them, or you have to reveal your address to them explicitly (i.e. autopilots and the like will likely never connect to you). -4. `always-use-proxy` informs C-Lightning to always use Tor even when +4. `always-use-proxy` informs Core Lightning to always use Tor even when connecting to nodes with public IPs. You can set this to `false` or remove it, if you are not privacy-conscious **and** find Tor is too slow for you. @@ -162,7 +162,7 @@ Tor Browser will run a built-in Tor instance, but with the proxy at port port at 9051). The mobile Orbot uses the same defaults as Tor Browser (9150 and 9151). -You can then use these settings for C-Lightning: +You can then use these settings for Core Lightning: ``` proxy=127.0.0.1:9150 @@ -171,30 +171,30 @@ addr=statictor:127.0.0.1:9151 always-use-proxy=true ``` -You will have to run C-Lightning after launching Tor Browser or Orbot, -and keep Tor Browser or Orbot open as long as C-Lightning is running, +You will have to run Core Lightning after launching Tor Browser or Orbot, +and keep Tor Browser or Orbot open as long as Core Lightning is running, but this is a setup which allows others to connect and fund channels to you, anywhere (no port forwarding! works wherever Tor works!), and you do not have to do anything more complicated than download and install Tor Browser. This may be useful for operating system distributions that do not have -Tor in their repositories, assuming we can ever get C-Lightning running +Tor in their repositories, assuming we can ever get Core Lightning running on those. ### Detailed Discussion -#### Three Ways to Create .onion Addresses for C-lightning +#### Three Ways to Create .onion Addresses for Core Lightning You have have Tor create an onion address for you, and tell -c-lightning to use that, or you can have c-lightning tell Tor to +Core Lightning to use that, or you can have Core Lightning tell Tor to create the same onion address every time it starts up, or you can have -c-lightning tell Tor to create a new onion address every time. +Core Lightning tell Tor to create a new onion address every time. #### Tor-Created .onion Address Having Tor create an onion address lets you run other services (e.g. a web server) at that same address, and you just tell that address to -c-lightning and it doesn't have to talk to the Tor server at all. +Core Lightning and it doesn't have to talk to the Tor server at all. Put the following in your `/etc/tor/torrc` file: @@ -218,14 +218,14 @@ You will find the newly created address (myaddress.onion) with: sudo cat /var/lib/tor/lightningd-service_v3/hostname ``` -Now you need to tell c-lightning to advertize that onion hostname and +Now you need to tell Core Lightning to advertize that onion hostname and port, by placing `announce-addr=myaddress.onion` in your lightning config. -#### Letting C-lightning Control Tor +#### Letting Core Lightning Control Tor -To have c-lightning control your Tor addresses, you have to tell Tor -to accept control commands from c-lightning, either by using a cookie, +To have Core Lightning control your Tor addresses, you have to tell Tor +to accept control commands from Core Lightning, either by using a cookie, or a password. ##### Service authenticated by cookie @@ -269,7 +269,7 @@ Save the file and restart the Tor service. Put `tor-service-password=yourpassword` (not the hash) in your lightning configuration file. -##### C-Lightning Creating Persistent Hidden Addresses +##### Core Lightning Creating Persistent Hidden Addresses This is usually better than transient addresses, as nodes won't have to wait for gossip propagation to find out your new address each time @@ -281,7 +281,7 @@ to add *two* lines in your lightningd config file: 1. A local address which lightningd can tell Tor to connect to when connections come in, e.g. `bind-addr=127.0.0.1:9735`. 2. After that, a `addr=statictor:127.0.0.1:9051` to tell - c-lightning to set up and announce a Tor onion address (and tell + Core Lightning to set up and announce a Tor onion address (and tell Tor to send connections to our real address, above). You can use `bind-addr` if you want to set up the onion address and @@ -303,9 +303,9 @@ for fresh gossip messages if you announce it, before they can connect. | ------- | ------------- | ------------------------- |------------------------- | 1 | Public | NO | Outgoing | | 2 | Public | FIXED BY TOR | Incoming [1] | -| 3 | Public | FIXED BY C-LIGHTNING | Incoming [1] | +| 3 | Public | FIXED BY CORE LIGHTNING | Incoming [1] | | 4 | Not Announced | FIXED BY TOR | Incoming [1] | -| 5 | Not Announced | FIXED BY C-LIGHTNING | Incoming [1] | +| 5 | Not Announced | FIXED BY CORE LIGHTNING | Incoming [1] | NOTE: @@ -362,7 +362,7 @@ If they match you can use the `--addr` command line option. Other nodes can connect to you entirely over Tor, and the Tor address doesn't change every time you restart. -You simply tell c-lightning to advertize both addresses (you can use +You simply tell Core Lightning to advertize both addresses (you can use `sudo cat /var/lib/tor/lightningd-service_v3/hostname` to get your Tor-assigned onion address). @@ -380,12 +380,12 @@ addr=yourIPAddress:port announce-addr=your.onionAddress:port ``` -#### Case #3: Public IP address, and a fixed Tor address set by C-lightning +#### Case #3: Public IP address, and a fixed Tor address set by Core Lightning Other nodes can connect to you entirely over Tor, and the Tor address doesn't change every time you restart. -See "Letting C-lightning Control Tor" for how to get c-lightning +See "Letting Core Lightning Control Tor" for how to get Core Lightning talking to Tor. If you have an internal IP address: @@ -406,7 +406,7 @@ addr=statictor:127.0.0.1:9051 Other nodes can only connect to you over Tor. -You simply tell c-lightning to advertize the Tor address (you can use +You simply tell Core Lightning to advertize the Tor address (you can use `sudo cat /var/lib/tor/lightningd-service_v3/hostname` to get your Tor-assigned onion address). @@ -416,11 +416,11 @@ proxy=127.0.0.1:9050 always-use-proxy=true ``` -#### Case #4: Unannounced IP address, and a fixed Tor address set by C-lightning +#### Case #4: Unannounced IP address, and a fixed Tor address set by Core Lightning Other nodes can only connect to you over Tor. -See "Letting C-lightning Control Tor" for how to get c-lightning +See "Letting Core Lightning Control Tor" for how to get Core Lightning talking to Tor. ``` diff --git a/doc/conf.py b/doc/conf.py index 37942cd498ad..8a5be9bdf873 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -59,7 +59,7 @@ # General information about the project. project = 'c-lightning' -author = 'c-lightning Developers' +author = 'Core Lightning Developers' copyright = datetime.now().strftime('2015 — %Y, {}'.format(author)) # The version info for the project you're documenting, acts as replacement for @@ -143,7 +143,7 @@ # The name for this set of Sphinx documents. # " v documentation" by default. # -html_title = 'c-lightning ' + version +html_title = 'Core Lightning ' + version # A shorter title for the navigation bar. Default is the same as html_title. # diff --git a/doc/index.rst b/doc/index.rst index 4671ccae850a..abf699ff89d5 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -1,5 +1,5 @@ -c-lightning Documentation -========================= +Core Lightning Documentation +============================ .. toctree:: :maxdepth: 1 diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 5d8fc6f660ec..b49e59f56832 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -26,7 +26,7 @@ indefinitely until the peer is online and can negotiate a mutual close. The default is 2 days (172800 seconds). The *destination* can be of any Bitcoin bech32 type. -If it isn't specified, the default is a c-lightning wallet address. If +If it isn't specified, the default is a Core Lightning wallet address. If the peer hasn't offered the `option_shutdown_anysegwit` feature, then taproot addresses (or other v1+ segwit) are not allowed. Tell your friends to upgrade! diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 63728c5b9c7c..f281ff719592 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -11,7 +11,7 @@ DESCRIPTION The **createonion** RPC command allows the caller to create a custom onion with custom payloads at each hop in the route. A custom onion can be used to -implement protocol extensions that are not supported by c-lightning directly. +implement protocol extensions that are not supported by Core Lightning directly. The *hops* parameter is a JSON list of dicts, each specifying a node and the payload destined for that node. The following is an example of a 3 hop onion: diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index f94e706aa9ea..18683e36ceeb 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -10,7 +10,7 @@ DESCRIPTION ----------- The **datastore** RPC command allows plugins to store data in the -c-lightning database, for later retrieval. +Core Lightning database, for later retrieval. *key* is an array of values (though a single value is treated as a one-element array), to form a hierarchy. Using the first element of diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index 477887641b1c..0c75bd165d16 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -10,7 +10,7 @@ DESCRIPTION ----------- The **deldatastore** RPC command allows plugins to delete data it has -stored in the c-lightning database. +stored in the Core Lightning database. The command fails if the *key* isn't present, or if *generation* is specified and the generation of the data does not exactly match. diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 20f8be1d80e7..75921d06f7e1 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -1,4 +1,4 @@ -lightning-getinfo -- Command to receive all information about the c-lightning node. +lightning-getinfo -- Command to receive all information about the Core Lightning node. ============================================================ SYNOPSIS diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index 316e719e90ad..5b0ecb0171e5 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -10,7 +10,7 @@ DESCRIPTION ----------- The **listdatastore** RPC command allows plugins to fetch data which was -stored in the c-lightning database. +stored in the Core Lightning database. All immediate children of the *key* (or root children) are returned: a *key* with children won't have a *hex* or *generation* entry. diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 40531ff78ea3..88edf2872b16 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -10,7 +10,7 @@ DESCRIPTION ----------- The **listforwards** RPC command displays all htlcs that have been -attempted to be forwarded by the c-lightning node. +attempted to be forwarded by the Core Lightning node. If *status* is specified, then only the forwards with the given status are returned. *status* can be either *offered* or *settled* or *failed* or *local_failed* diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index 0712aafdeef0..aa903207b528 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -1,4 +1,4 @@ -lightning-listfunds -- Command showing all funds currently managed by the c-lightning node +lightning-listfunds -- Command showing all funds currently managed by the Core Lightning node ========================================================================================== SYNOPSIS diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index d61fceac5d7e..bd492f8920ce 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -15,7 +15,7 @@ that is shared by all channels. If not already connected, **multifundchannel** will automatically attempt to connect; you may provide a *@host:port* hint appended to the node ID -so that c-lightning can learn how to connect to the node; +so that Core Lightning can learn how to connect to the node; see lightning-connect(7). Once the transaction is confirmed, normal channel operations may begin. @@ -134,7 +134,7 @@ the channel funding process will cancel the funding locally, but the peer thinks the channel is already waiting for funding lockin. In that case, the next time we connect to the peer, our node will tell the peer to forget the channel, but some nodes (in particular, -c-lightning nodes) will disconnect when our node tells them to +Core Lightning nodes) will disconnect when our node tells them to forget the channel. If you immediately **multifundchannel** with that peer, it could trigger this connect-forget-disconnect behavior, causing the diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index 28be3855bb22..4242f4c0aeef 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -9,7 +9,7 @@ SYNOPSIS DESCRIPTION ----------- -The **multiwithdraw** RPC command sends funds from c-lightning's internal +The **multiwithdraw** RPC command sends funds from Core Lightning's internal wallet to the addresses specified in *outputs*, which is an array containing objects of the form `{address: amount}`. The `amount` may be the string *"all"*, indicating that all onchain funds diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index 9d0a95532d1d..60ad04d6f235 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -1,5 +1,5 @@ -lightning-newaddr -- Command for generating a new address to be used by c-lightning -=================================================================================== +lightning-newaddr -- Command for generating a new address to be used by Core Lightning +====================================================================================== SYNOPSIS -------- @@ -10,7 +10,7 @@ DESCRIPTION ----------- The **newaddr** RPC command generates a new address which can -subsequently be used to fund channels managed by the c-lightning node. +subsequently be used to fund channels managed by the Core Lightning node. The funding transaction needs to be confirmed before funds can be used. diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index a7ef72579409..5d3b8492046a 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -20,7 +20,7 @@ method. The messages must not use even-numbered types, since these may require synchronous handling on the receiving side, and can cause the connection to be dropped. The message types may also not use one of the internally handled types, since that may cause issues with the internal state tracking of -c-lightning. +Core Lightning. The node specified by `node_id` must be a peer, i.e., it must have a direct connection with the node receiving the RPC call, and the connection must be diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index a4100a889be0..72aec8dfbd78 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -16,7 +16,7 @@ along the route on how to behave. Normally these instructions are indications on where to forward a payment and what parameters to use, or contain details of the payment for the final hop. However, it is possible to add arbitrary information for hops in the custom onion, allowing for custom extensions that -are not directly supported by c-lightning. +are not directly supported by Core Lightning. The onion is specific to the route that is being used and the *payment_hash* used to construct, and therefore cannot be reused for other payments or to @@ -28,10 +28,10 @@ by either of the tools that can generate onions. It contains the payloads destined for each hop and some metadata. Please refer to [BOLT 04][bolt04] for further details. -The *first_hop* parameter instructs c-lightning which peer to send the onion +The *first_hop* parameter instructs Core Lightning which peer to send the onion to. It is a JSON dictionary that corresponds to the first element of the route array returned by *getroute*. The following is a minimal example telling -c-lightning to use any available channel to `022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59` +Core Lightning to use any available channel to `022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59` to add an HTLC for 1002 millisatoshis and a delay of 21 blocks on top of the current blockheight: ```json @@ -50,13 +50,13 @@ The *label* parameter can be used to provide a human readable reference to retrieve the payment at a later time. The *shared_secrets* parameter is a JSON list of 32 byte hex-encoded secrets -that were used when creating the onion. c-lightning can send a payment with a +that were used when creating the onion. Core Lightning can send a payment with a custom onion without the knowledge of these secrets, however it will not be able to parse an eventual error message since that is encrypted with the -shared secrets used in the onion. If *shared_secrets* is provided c-lightning +shared secrets used in the onion. If *shared_secrets* is provided Core Lightning will decrypt the error, act accordingly, e.g., add a `channel_update` included in the error to its network view, and set the details in *listsendpays* -correctly. If it is not provided c-lightning will store the encrypted onion, +correctly. If it is not provided Core Lightning will store the encrypted onion, and expose it in *listsendpays* allowing the caller to decrypt it externally. The following is an example of a 3 hop onion: @@ -68,7 +68,7 @@ externally. The following is an example of a 3 hop onion: ] ``` -If *shared_secrets* is not provided the c-lightning node does not know how +If *shared_secrets* is not provided the Core Lightning node does not know how long the route is, which channels or nodes are involved, and what an eventual error could have been. It can therefore be used for oblivious payments. diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 3f0260f0a0f6..6a724c9cdd7c 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -90,7 +90,7 @@ AUTHOR Michael Schmoock <> is the author of this feature. Rusty Russell <> is mainly -responsible for the c-lightning project. +responsible for the Core Lightning project. SEE ALSO -------- diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index 2db2f2dbcd8e..75a4147b861a 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -70,7 +70,7 @@ AUTHOR Michael Schmoock <> is the author of this feature. Rusty Russell <> is mainly -responsible for the c-lightning project. +responsible for the Core Lightning project. SEE ALSO -------- diff --git a/doc/lightning-stop.7.md b/doc/lightning-stop.7.md index 3c86d5194a5c..66132f611c5a 100644 --- a/doc/lightning-stop.7.md +++ b/doc/lightning-stop.7.md @@ -1,5 +1,5 @@ -lightning-stop -- Command to shutdown the c-lightning node. -============================================================ +lightning-stop -- Command to shutdown the Core Lightning node. +============================================================-- SYNOPSIS -------- @@ -9,7 +9,7 @@ SYNOPSIS DESCRIPTION ----------- -The **stop** is a RPC command to shut off the c-lightning node. +The **stop** is a RPC command to shut off the Core Lightning node. EXAMPLE JSON REQUEST ------------ diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index 83efc718672e..8b9f16fb4fe1 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -10,7 +10,7 @@ DESCRIPTION ----------- The **txprepare** RPC command creates an unsigned transaction which -spends funds from c-lightning's internal wallet to the outputs specified +spends funds from Core Lightning's internal wallet to the outputs specified in *outputs*. The *outputs* is the array of output that include *destination* diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index 007a361f52ae..3658341455db 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -9,7 +9,7 @@ SYNOPSIS DESCRIPTION ----------- -The **withdraw** RPC command sends funds from c-lightning's internal +The **withdraw** RPC command sends funds from Core Lightning's internal wallet to the address specified in *destination*. The address can be of any Bitcoin accepted type, including bech32. diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index c7a7e97b3948..9eb70dfe7d96 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -367,7 +367,7 @@ right thing: for the mainnet (bitcoin) network it will try to bind to port 9735 on IPv4 and IPv6, and will announce it to peers if it seems like a public address. -c-lightning also support IPv4/6 address discovery behind NAT routers. +Core Lightning also support IPv4/6 address discovery behind NAT routers. If your node detects an new public address, it will update its announcement. For this to work you need to forward the TCP port 9735 to your node. @@ -478,7 +478,7 @@ plugins along with any immediate subdirectories). You can specify additional paths too: **plugin**=*PATH* -Specify a plugin to run as part of c-lightning. This can be specified +Specify a plugin to run as part of Core Lightning. This can be specified multiple times to add multiple plugins. Note that unless plugins themselves specify ordering requirements for being called on various hooks, plugins will be ordered by commandline, then config file. @@ -505,13 +505,13 @@ disabling a single plugin inside a directory. You can still explicitly load plugins which have been disabled, using lightning-plugin(7) `start`. **important-plugin**=*PLUGIN* -Speciy a plugin to run as part of C-lightning. +Speciy a plugin to run as part of Core Lightning. This can be specified multiple times to add multiple plugins. Plugins specified via this option are considered so important, that if the plugin stops for any reason (including via lightning-plugin(7) `stop`), -C-lightning will also stop running. +Core Lightning will also stop running. This way, you can monitor crashes of important plugins by simply monitoring -if C-lightning terminates. +if Core Lightning terminates. Built-in plugins, which are installed with lightningd(8), are automatically considered important. diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index d5dd898f8a02..4ea0805c84aa 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -309,7 +309,7 @@ static void create_hsm(int fd) } } -/*~ We store our root secret in a "hsm_secret" file (like all of c-lightning, +/*~ We store our root secret in a "hsm_secret" file (like all of Core Lightning, * we run in the user's .lightning directory). */ static void maybe_create_new_hsm(const struct secret *encryption_key, bool random_hsm) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 367e3ab4e9c5..0965f193ad30 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -886,7 +886,7 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) return NULL; } - // Adding a deprecated phase to make sure that all the c-lightning wrapper + // Adding a deprecated phase to make sure that all the Core Lightning wrapper // can migrate all the frameworks if (!deprecated_apis) { const jsmntok_t *jsonrpc = json_get_member(jcon->buffer, tok, "jsonrpc"); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index bb4a1ac44ad1..b8e60b3fff65 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1,6 +1,6 @@ /*~ Welcome, wonderful reader! * - * This is the core of c-lightning: the main file of the master daemon + * This is the Core of um, Core Lightning: the main file of the master daemon * `lightningd`. It's mainly cluttered with the miscellany of setup, * and a few startup sanity checks. * @@ -247,7 +247,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) /*~ We run a number of plugins (subprocesses that we talk JSON-RPC with) * alongside this process. This allows us to have an easy way for users - * to add their own tools without having to modify the c-lightning source + * to add their own tools without having to modify the Core Lightning source * code. Here we initialize the context that will keep track and control * the plugins. */ diff --git a/lightningd/plugin_control.c b/lightningd/plugin_control.c index 9bd62d57c621..b583ef1a148e 100644 --- a/lightningd/plugin_control.c +++ b/lightningd/plugin_control.c @@ -304,7 +304,7 @@ static const struct json_command plugin_control_command = { "Control plugins (start, stop, startdir, rescan, list)", .verbose = "Usage :\n" "plugin start /path/to/a/plugin\n" - " adds a new plugin to c-lightning\n" + " adds a new plugin to Core Lightning\n" "plugin stop plugin_name\n" " stops an already registered plugin\n" "plugin startdir /path/to/a/plugin_dir/\n" diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index 822083e47c15..9bf8cc979267 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -1,5 +1,5 @@ //! This is a test plugin used to verify that we can compile and run -//! plugins using the Rust API against c-lightning. +//! plugins using the Rust API against Core Lightning. #[macro_use] extern crate serde_json; use cln_plugin::{options, Builder, Error, Plugin}; diff --git a/plugins/spender/openchannel.c b/plugins/spender/openchannel.c index 3202eb5d3212..7973bd0bedbb 100644 --- a/plugins/spender/openchannel.c +++ b/plugins/spender/openchannel.c @@ -46,7 +46,7 @@ find_dest_by_channel_id(struct channel_id *cid) * the PSBT input/outputs in such a way that we can Do The * Right Thing for each of our peers. * - * c-lightning will make sure that our peer isn't removing/adding + * Core Lightning will make sure that our peer isn't removing/adding * any updates that it's not allowed to (i.e. ours or a different * node's that we're pretending are 'ours'). * diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 66630244bc7b..d45baefebb24 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -141,7 +141,7 @@ where /// Build and start the plugin loop. This performs the handshake /// and spawns a new task that accepts incoming messages from - /// c-lightning and dispatches them to the handlers. It only + /// Core Lightning and dispatches them to the handlers. It only /// returns after completing the handshake to ensure that the /// configuration and initialization was successfull. pub async fn start(mut self) -> Result, anyhow::Error> { @@ -159,7 +159,7 @@ where ))); // Now configure the logging, so any `log` call is wrapped - // in a JSON-RPC notification and sent to c-lightning + // in a JSON-RPC notification and sent to Core Lightning crate::logging::init(output.clone()).await?; trace!("Plugin logging initialized"); @@ -267,7 +267,7 @@ where (OValue::Integer(_), JValue::Number(n)) => OValue::Integer(n.as_i64().unwrap()), (OValue::Boolean(_), JValue::Bool(n)) => OValue::Boolean(*n), - // It's ok to panic, if we get here c-lightning + // It's ok to panic, if we get here Core Lightning // has not enforced the option type. (_, _) => panic!("Mismatching types in options: {:?} != {:?}", opt, val), }); diff --git a/poetry.lock b/poetry.lock index b18f9962924c..ae03af20f8c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -512,7 +512,7 @@ python-versions = ">=3.7,<4.0" [[package]] name = "pyln-client" version = "0.10.2.post1" -description = "Client library and plugin library for c-lightning" +description = "Client library and plugin library for Core Lightning" category = "main" optional = false python-versions = "^3.7" @@ -549,7 +549,7 @@ url = "contrib/pyln-proto" [[package]] name = "pyln-testing" version = "0.10.2" -description = "Test your c-lightning integration, plugins or whatever you want" +description = "Test your Core Lightning integration, plugins or whatever you want" category = "dev" optional = false python-versions = "^3.7" diff --git a/wire/tlvstream.h b/wire/tlvstream.h index d9bcfbe48259..469390702434 100644 --- a/wire/tlvstream.h +++ b/wire/tlvstream.h @@ -16,7 +16,7 @@ struct tlv_record_type { /* A single TLV field, consisting of the data and its associated metadata. */ struct tlv_field { - /* If this is a type that is known to c-lightning we have a pointer to + /* If this is a type that is known to Core Lightning we have a pointer to * the metadata. */ const struct tlv_record_type *meta; From 6b79eed2e88659a7c33ed0c84df90ef89fcf4ef6 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 6 Apr 2022 14:44:58 -0400 Subject: [PATCH 0674/1530] there are four tests --- doc/HACKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/HACKING.md b/doc/HACKING.md index 620fb5f24a56..68080fc9e824 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -191,7 +191,7 @@ A modern desktop can build and run through all the tests in a couple of minutes Adjust `-j` and `PYTEST_PAR` accordingly for your hardware. -There are three kinds of tests: +There are four kinds of tests: * **source tests** - run by `make check-source`, looks for whitespace, header order, and checks formatted quotes from BOLTs if BOLTDIR From c4d4984cbf84dc4179f3044e89920ab248c80627 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 6 Apr 2022 14:56:57 -0400 Subject: [PATCH 0675/1530] Travis is no longer used --- .editorconfig | 3 --- README.md | 2 -- doc/HACKING.md | 2 +- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.editorconfig b/.editorconfig index e49de5f5e98a..78d743984d8d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,6 +16,3 @@ indent_size = 8 indent_style = space indent_size = 4 -[.travis.yml] -indent_style = space -indent_size = 2 diff --git a/README.md b/README.md index 8124b0696a0e..11d50ea3c90f 100644 --- a/README.md +++ b/README.md @@ -226,8 +226,6 @@ You should also configure with `--enable-developer` to get additional checks and [blockstream-store-blog]: https://blockstream.com/2018/01/16/en-lightning-charge/ [std]: https://github.com/lightningnetwork/lightning-rfc -[travis-ci]: https://travis-ci.org/ElementsProject/lightning.svg?branch=master -[travis-ci-link]: https://travis-ci.org/ElementsProject/lightning [prs]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat [prs-link]: http://makeapullrequest.com [IRC]: https://img.shields.io/badge/chat-on%20libera-brightgreen.svg diff --git a/doc/HACKING.md b/doc/HACKING.md index 68080fc9e824..eac524ee4d40 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -225,7 +225,7 @@ There are four kinds of tests: `make check-python` -Our Travis CI instance (see `.travis.yml`) runs all these for each +Our Github Actions instance (see `.github/workflows/*.yml`) runs all these for each pull request. #### Additional Environment Variables From 3dfe32abb0c5925ee5cbee4602c0840c098d4bda Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 6 Apr 2022 21:03:56 +0200 Subject: [PATCH 0676/1530] docs: use poetry in the osx installation process Changelog-None: docs: use poetry in the osx installation process Signed-off-by: Vincenzo Palazzo --- doc/INSTALL.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 897cdf1bacdf..3b4585b89496 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -243,6 +243,7 @@ If you need Python 3.x for mako (or get a mako build error): $ source ~/.bash_profile $ pyenv install 3.7.4 $ pip install --upgrade pip + $ pip install poetry If you don't have bitcoind installed locally you'll need to install that as well: @@ -259,16 +260,11 @@ Clone lightning: $ git clone https://github.com/ElementsProject/lightning.git $ cd lightning -Configure Python 3.x & get mako: - - $ pyenv local 3.7.4 - $ pip install mako - Build lightning: - $ pip install -r requirements.txt + $ poetry install $ ./configure - $ make + $ poetry run make Running lightning: From fbcdbe8842043ddcff5f2ca1c4e5ad730eff77f1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 7 Apr 2022 07:00:37 +0930 Subject: [PATCH 0677/1530] release: v0.11.0rc2 One "my node won't start" fix, but lots of doc updates. There will be an rc3: the cln-rpc plugin needs some love. Signed-off-by: Rusty Russell --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4869b7075f56..fb45d63597a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [v0.11.0rc1] - 2022-04-04: Simon's Carefully Chosen Release Name +## [v0.11.0rc2] - 2022-04-04: Simon's Carefully Chosen Release Name This release named by Simon Vrouwe; this marks the name change to core-lightning (#CLN). From 767da45037517eb529b995e2453a64b7798e9953 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 7 Apr 2022 17:22:36 +0200 Subject: [PATCH 0678/1530] cln-grpc: Do not log an error when called with `--help` Changelog-None --- plugins/src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index d45baefebb24..0179db8603e5 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -176,7 +176,8 @@ where })) .await? } - o => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), + Some(o) => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), + None => return Err(anyhow!("Lost connection to lightning expecting getmanifest")), }; match input.next().await { @@ -192,7 +193,12 @@ where .await? } - o => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), + Some(o) => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), + None => { + // If we are being called with --help we will get + // disconnected here. That's expected, so don't + // complain about it. + } }; let (wait_handle, _) = tokio::sync::broadcast::channel(1); From 9a8bc777e51d568a278a29186b76787266f90b85 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 7 Apr 2022 17:23:41 +0200 Subject: [PATCH 0679/1530] msggen: Add pay descriptions and maxfee --- .msggen.json | 4 ++++ cln-grpc/proto/node.proto | 4 ++++ cln-grpc/src/convert.rs | 4 ++++ cln-rpc/src/model.rs | 8 ++++++++ 4 files changed, 20 insertions(+) diff --git a/.msggen.json b/.msggen.json index cf166ea61c9e..48a398f0dbb2 100644 --- a/.msggen.json +++ b/.msggen.json @@ -641,6 +641,7 @@ "ListPays.pays[].bolt11": 6, "ListPays.pays[].bolt12": 7, "ListPays.pays[].created_at": 4, + "ListPays.pays[].description": 11, "ListPays.pays[].destination": 3, "ListPays.pays[].erroronion": 10, "ListPays.pays[].label": 5, @@ -763,6 +764,7 @@ "ListSendPays.payments[].bolt11": 10, "ListSendPays.payments[].bolt12": 11, "ListSendPays.payments[].created_at": 7, + "ListSendPays.payments[].description": 14, "ListSendPays.payments[].destination": 6, "ListSendPays.payments[].erroronion": 13, "ListSendPays.payments[].groupid": 2, @@ -818,11 +820,13 @@ }, "PayRequest": { "Pay.bolt11": 1, + "Pay.description": 12, "Pay.exclude": 10, "Pay.exemptfee": 7, "Pay.label": 3, "Pay.localofferid": 9, "Pay.maxdelay": 6, + "Pay.maxfee": 11, "Pay.maxfeepercent": 4, "Pay.msatoshi": 2, "Pay.retry_for": 5, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 508acb24b9b4..9ac4dddab75f 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -702,6 +702,7 @@ message ListsendpaysPayments { Amount amount_sent_msat = 8; optional string label = 9; optional string bolt11 = 10; + optional string description = 14; optional string bolt12 = 11; optional bytes payment_preimage = 12; optional bytes erroronion = 13; @@ -781,6 +782,8 @@ message PayRequest { optional Amount exemptfee = 7; optional bytes localofferid = 9; repeated string exclude = 10; + optional Amount maxfee = 11; + optional string description = 12; } message PayResponse { @@ -1225,6 +1228,7 @@ message ListpaysPays { uint64 created_at = 4; optional string label = 5; optional string bolt11 = 6; + optional string description = 11; optional string bolt12 = 7; optional Amount amount_msat = 8; optional Amount amount_sent_msat = 9; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index c09d559332a8..d1ae7f309843 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -509,6 +509,7 @@ impl From<&responses::ListsendpaysPayments> for pb::ListsendpaysPayments { amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label.clone(), // Rule #2 for type string? bolt11: c.bolt11.clone(), // Rule #2 for type string? + description: c.description.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? erroronion: c.erroronion.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? @@ -908,6 +909,7 @@ impl From<&responses::ListpaysPays> for pb::ListpaysPays { created_at: c.created_at.clone(), // Rule #2 for type u64 label: c.label.clone(), // Rule #2 for type string? bolt11: c.bolt11.clone(), // Rule #2 for type string? + description: c.description.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? amount_sent_msat: c.amount_sent_msat.map(|f| f.into()), // Rule #2 for type msat? @@ -1233,6 +1235,8 @@ impl From<&pb::PayRequest> for requests::PayRequest { exemptfee: c.exemptfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? localofferid: c.localofferid.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? exclude: Some(c.exclude.iter().map(|s| s.into()).collect()), // Rule #4 + maxfee: c.maxfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? + description: c.description.clone(), // Rule #1 for type string? } } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 5a4aaa4aa9e0..c2f98c022176 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -478,6 +478,10 @@ pub mod requests { pub localofferid: Option, #[serde(alias = "exclude", skip_serializing_if = "Option::is_none")] pub exclude: Option>, + #[serde(alias = "maxfee", skip_serializing_if = "Option::is_none")] + pub maxfee: Option, + #[serde(alias = "description", skip_serializing_if = "Option::is_none")] + pub description: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -1860,6 +1864,8 @@ pub mod responses { pub label: Option, #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] pub bolt11: Option, + #[serde(alias = "description", skip_serializing_if = "Option::is_none")] + pub description: Option, #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(alias = "payment_preimage", skip_serializing_if = "Option::is_none")] @@ -2643,6 +2649,8 @@ pub mod responses { pub label: Option, #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] pub bolt11: Option, + #[serde(alias = "description", skip_serializing_if = "Option::is_none")] + pub description: Option, #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] From 9826402c995ead2709919aede71136c7e55f0219 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 7 Apr 2022 17:59:37 +0200 Subject: [PATCH 0680/1530] cln-grpc: Do not exit if grpc-port is not set Exiting doesn't mesh well with builtin plugins, so just sit there and do nothing Changelog-None --- plugins/grpc-plugin/src/main.rs | 19 +++++++++++-------- tests/test_cln_rs.py | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 369373034c62..f73463761450 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -40,19 +40,22 @@ async fn main() -> Result<()> { let bind_port = match plugin.option("grpc-port") { Some(options::Value::Integer(-1)) => { log::info!("`grpc-port` option is not configured, exiting."); - return Ok(()); + None } - Some(options::Value::Integer(i)) => i, + Some(options::Value::Integer(i)) => Some(i), None => return Err(anyhow!("Missing 'grpc-port' option")), Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)), }; - let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); - tokio::spawn(async move { - if let Err(e) = run_interface(bind_addr, state).await { - warn!("Error running the grpc interface: {}", e); - } - }); + if let Some(bind_port) = bind_port { + let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); + + tokio::spawn(async move { + if let Err(e) = run_interface(bind_addr, state).await { + warn!("Error running the grpc interface: {}", e); + } + }); + } plugin.join().await } diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index ee591d76b5f1..996d7b19dac4 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -176,7 +176,7 @@ def test_grpc_no_auto_start(node_factory): "plugin": str(bin_path), }) - wait_for(lambda: [p for p in l1.rpc.plugin('list')['plugins'] if 'cln-grpc' in p['name']] == []) + l1.daemon.logsearch_start = 0 assert l1.daemon.is_in_log(r'plugin-cln-grpc: Killing plugin: exited during normal operation') From 8717c4e5a234f222f521cdd677c5f9700eeb99de Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 8 Apr 2022 05:41:12 +0200 Subject: [PATCH 0681/1530] cln-grpc: Add midstate between configuration and replying to init This is a bit special, in that it allows us to configure the plugin, but then still abort startup by sending `init` with the `disable` flag set. --- plugins/grpc-plugin/src/main.rs | 23 +++-- plugins/src/lib.rs | 171 +++++++++++++++++++++++--------- plugins/src/messages.rs | 5 +- tests/test_cln_rs.py | 4 +- 4 files changed, 143 insertions(+), 60 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index f73463761450..58bd2751cbed 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -34,28 +34,29 @@ async fn main() -> Result<()> { options::Value::Integer(-1), "Which port should the grpc plugin listen for incoming connections?", )) - .start() + .configure() .await?; let bind_port = match plugin.option("grpc-port") { Some(options::Value::Integer(-1)) => { log::info!("`grpc-port` option is not configured, exiting."); - None + plugin.disable("`grpc-port` option is not configured.").await?; + return Ok(()); } - Some(options::Value::Integer(i)) => Some(i), + Some(options::Value::Integer(i)) => i, None => return Err(anyhow!("Missing 'grpc-port' option")), Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)), }; - if let Some(bind_port) = bind_port { - let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); + let plugin = plugin.start().await?; - tokio::spawn(async move { - if let Err(e) = run_interface(bind_addr, state).await { - warn!("Error running the grpc interface: {}", e); - } - }); - } + let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); + + tokio::spawn(async move { + if let Err(e) = run_interface(bind_addr, state).await { + warn!("Error running the grpc interface: {}", e); + } + }); plugin.join().await } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 0179db8603e5..a3798adf726a 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -139,12 +139,7 @@ where self } - /// Build and start the plugin loop. This performs the handshake - /// and spawns a new task that accepts incoming messages from - /// Core Lightning and dispatches them to the handlers. It only - /// returns after completing the handshake to ensure that the - /// configuration and initialization was successfull. - pub async fn start(mut self) -> Result, anyhow::Error> { + pub async fn configure(mut self) -> Result, anyhow::Error> { let mut input = FramedRead::new(self.input.take().unwrap(), JsonRpcCodec::default()); // Sadly we need to wrap the output in a mutex in order to @@ -177,28 +172,25 @@ where .await? } Some(o) => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), - None => return Err(anyhow!("Lost connection to lightning expecting getmanifest")), + None => { + return Err(anyhow!( + "Lost connection to lightning expecting getmanifest" + )) + } }; - - match input.next().await { + let init_id = match input.next().await { Some(Ok(messages::JsonRpc::Request(id, messages::Request::Init(m)))) => { - output - .lock() - .await - .send(json!({ - "jsonrpc": "2.0", - "result": self.handle_init(m)?, - "id": id, - })) - .await? + self.handle_init(m)?; + id } Some(o) => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), None => { - // If we are being called with --help we will get - // disconnected here. That's expected, so don't - // complain about it. - } + // If we are being called with --help we will get + // disconnected here. That's expected, so don't + // complain about it. + 0 + } }; let (wait_handle, _) = tokio::sync::broadcast::channel(1); @@ -219,23 +211,33 @@ where HashMap::from_iter(self.rpcmethods.drain().map(|(k, v)| (k, v.callback))); rpcmethods.extend(self.hooks.drain().map(|(k, v)| (k, v.callback))); - // Start the PluginDriver to handle plugin IO - tokio::spawn( - PluginDriver { + // Leave the `init` reply pending, so we can disable based on + // the options if required. + Ok(ConfiguredPlugin { + // The JSON-RPC `id` field so we can reply correctly. + init_id, + input, + output, + receiver, + driver: PluginDriver { plugin: plugin.clone(), rpcmethods, hooks: HashMap::new(), subscriptions: HashMap::from_iter( self.subscriptions.drain().map(|(k, v)| (k, v.callback)), ), - } - .run(receiver, input, output), - // TODO Use the broadcast to distribute any error that we - // might receive here to anyone listening. (Shutdown - // signal) - ); + }, + plugin, + }) + } - Ok(plugin) + /// Build and start the plugin loop. This performs the handshake + /// and spawns a new task that accepts incoming messages from + /// Core Lightning and dispatches them to the handlers. It only + /// returns after completing the handshake to ensure that the + /// configuration and initialization was successfull. + pub async fn start(self) -> Result, anyhow::Error> { + self.configure().await?.start().await } fn handle_get_manifest( @@ -322,6 +324,22 @@ where callback: AsyncCallback, } +/// A plugin that has registered with the lightning daemon, and gotten +/// its options filled, however has not yet acknowledged the `init` +/// message. This is a mid-state allowing a plugin to disable itself, +/// based on the options. +pub struct ConfiguredPlugin +where + S: Clone + Send, +{ + init_id: usize, + input: FramedRead, + output: Arc>>, + plugin: Plugin, + driver: PluginDriver, + receiver: tokio::sync::mpsc::Receiver, +} + #[derive(Clone)] pub struct Plugin where @@ -350,6 +368,67 @@ where } } +impl ConfiguredPlugin +where + S: Send + Clone + Sync + 'static, + I: AsyncRead + Send + Unpin + 'static, + O: Send + AsyncWrite + Unpin + 'static, +{ + #[allow(unused_mut)] + pub async fn start(mut self) -> Result, anyhow::Error> { + let driver = self.driver; + let plugin = self.plugin; + let output = self.output; + let input = self.input; + let receiver = self.receiver; // Now reply to the `init` message that `configure` left pending. + output + .lock() + .await + .send(json!( + { + "jsonrpc": "2.0", + "id": self.init_id, + "result": crate::messages::InitResponse{disable: None} + } + )) + .await + .context("sending init response")?; + // Start the PluginDriver to handle plugin IO + tokio::spawn( + driver.run(receiver, input, output), + // TODO Use the broadcast to distribute any error that we + // might receive here to anyone listening. (Shutdown + // signal) + ); + Ok(plugin) + } + + /// Abort the plugin startup. Communicate that we're about to exit + /// voluntarily, and this is not an error. + #[allow(unused_mut)] + pub async fn disable(mut self, reason: &str) -> Result<(), anyhow::Error> { + self.output + .lock() + .await + .send(json!( + { + "jsonrpc": "2.0", + "id": self.init_id, + "result": crate::messages::InitResponse{ + disable: Some(reason.to_string()) + } + } + )) + .await + .context("sending init response")?; + Ok(()) + } + + pub fn option(&self, name: &str) -> Option { + self.plugin.option(name) + } +} + /// The [PluginDriver] is used to run the IO loop, reading messages /// from the Lightning daemon, dispatching calls and notifications to /// the plugin, and returning responses to the the daemon. We also use @@ -384,21 +463,21 @@ where O: Send + AsyncWriteExt + Unpin, { loop { - // If we encounter any error reading or writing from/to - // the master we hand them up, so we can return control to - // the user-code, which may require some cleanups or - // similar. + // If we encounter any error reading or writing from/to + // the master we hand them up, so we can return control to + // the user-code, which may require some cleanups or + // similar. tokio::select! { - e = self.dispatch_one(&mut input, &self.plugin) => { - //Hand any error up. - e?; - }, - v = receiver.recv() => { - output.lock().await.send( - v.context("internal communication error")? - ).await?; - }, - } + e = self.dispatch_one(&mut input, &self.plugin) => { + //Hand any error up. + e?; + }, + v = receiver.recv() => { + output.lock().await.send( + v.context("internal communication error")? + ).await?; + }, + } } } diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index ec11cb2eb633..4f19aaea9507 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -132,6 +132,9 @@ pub(crate) struct GetManifestResponse { } #[derive(Serialize, Default, Debug)] -pub struct InitResponse {} +pub struct InitResponse { + #[serde(skip_serializing_if = "Option::is_none")] + pub disable: Option, +} pub trait Response: Serialize + Debug {} diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 996d7b19dac4..8fd3b8336b2b 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -176,8 +176,8 @@ def test_grpc_no_auto_start(node_factory): "plugin": str(bin_path), }) - l1.daemon.logsearch_start = 0 - assert l1.daemon.is_in_log(r'plugin-cln-grpc: Killing plugin: exited during normal operation') + wait_for(lambda: [p for p in l1.rpc.plugin('list')['plugins'] if 'cln-grpc' in p['name']] == []) + assert l1.daemon.is_in_log(r'plugin-cln-grpc: Killing plugin: disabled itself at init') def test_grpc_wrong_auth(node_factory): From c36cef08bc5a8cfa156f80f978248c322adfb99f Mon Sep 17 00:00:00 2001 From: Stephen Webel Date: Sat, 9 Apr 2022 18:41:18 -0400 Subject: [PATCH 0682/1530] use full path to reference `lightningd.c` The relative path makes for a difficult experience when people are reading on `https://lightning.readthedocs.io/`. Directly linking saves the reader a few clicks hunting down the correct location :) --- doc/HACKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/HACKING.md b/doc/HACKING.md index eac524ee4d40..82f6813377ac 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -18,7 +18,7 @@ It's in C, to encourage alternate implementations. Patches are welcome! You should read our [Style Guide](STYLE.md). To read the code, you should start from -[lightningd.c](../lightningd/lightningd.c) and hop your way through +[lightningd.c](https://github.com/ElementsProject/lightning/blob/master/lightningd/lightningd.c) and hop your way through the '~' comments at the head of each daemon in the suggested order. From b359a24772f3b2d6f75145495c2d064602883b47 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sun, 10 Apr 2022 16:56:13 +0200 Subject: [PATCH 0683/1530] cln-plugin: Handle --help invocations better We now have ternary outcomes for `Builder.configure()` and `Builder.start()`: - Ok(Some(p)) means we were configured correctly, and can continue with our work normally - Ok(None) means that `lightningd` was invoked with `--help`, we weren't configured (which is not an error since the `lightningd` just implicitly told us to shut down) and user code should clean up and exit as well - Err(e) something went wrong, user code may report an error and exit. --- plugins/examples/cln-plugin-startup.rs | 15 ++++++++++---- plugins/grpc-plugin/src/main.rs | 12 +++++++++--- plugins/src/lib.rs | 27 ++++++++++++++++++++------ 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index 9bf8cc979267..ff152235ffdb 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -6,7 +6,7 @@ use cln_plugin::{options, Builder, Error, Plugin}; use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - let plugin = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) + if let Some(plugin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "test-option", options::Value::Integer(42), @@ -16,8 +16,12 @@ async fn main() -> Result<(), anyhow::Error> { .subscribe("connect", connect_handler) .hook("peer_connected", peer_connected_handler) .start() - .await?; - plugin.join().await + .await? + { + plugin.join().await + } else { + Ok(()) + } } async fn testmethod(_p: Plugin<()>, _v: serde_json::Value) -> Result { @@ -29,7 +33,10 @@ async fn connect_handler(_p: Plugin<()>, v: serde_json::Value) -> Result<(), Err Ok(()) } -async fn peer_connected_handler(_p: Plugin<()>, v: serde_json::Value) -> Result { +async fn peer_connected_handler( + _p: Plugin<()>, + v: serde_json::Value, +) -> Result { log::info!("Got a connect hook call: {}", v); Ok(json!({"result": "continue"})) } diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 58bd2751cbed..a83f0131dad6 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -28,19 +28,25 @@ async fn main() -> Result<()> { ca_cert, }; - let plugin = Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) + let plugin = match Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "grpc-port", options::Value::Integer(-1), "Which port should the grpc plugin listen for incoming connections?", )) .configure() - .await?; + .await? + { + Some(p) => p, + None => return Ok(()), + }; let bind_port = match plugin.option("grpc-port") { Some(options::Value::Integer(-1)) => { log::info!("`grpc-port` option is not configured, exiting."); - plugin.disable("`grpc-port` option is not configured.").await?; + plugin + .disable("`grpc-port` option is not configured.") + .await?; return Ok(()); } Some(options::Value::Integer(i)) => i, diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index a3798adf726a..e91bb69f6e28 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -139,7 +139,13 @@ where self } - pub async fn configure(mut self) -> Result, anyhow::Error> { + /// Communicate with `lightningd` to tell it about our options, + /// RPC methods and subscribe to hooks, and then process the + /// initialization, configuring the plugin. + /// + /// Returns `None` if we were invoked with `--help` and thus + /// should exit after this handshake + pub async fn configure(mut self) -> Result>, anyhow::Error> { let mut input = FramedRead::new(self.input.take().unwrap(), JsonRpcCodec::default()); // Sadly we need to wrap the output in a mutex in order to @@ -189,7 +195,7 @@ where // If we are being called with --help we will get // disconnected here. That's expected, so don't // complain about it. - 0 + return Ok(None); } }; @@ -213,7 +219,7 @@ where // Leave the `init` reply pending, so we can disable based on // the options if required. - Ok(ConfiguredPlugin { + Ok(Some(ConfiguredPlugin { // The JSON-RPC `id` field so we can reply correctly. init_id, input, @@ -228,7 +234,7 @@ where ), }, plugin, - }) + })) } /// Build and start the plugin loop. This performs the handshake @@ -236,8 +242,17 @@ where /// Core Lightning and dispatches them to the handlers. It only /// returns after completing the handshake to ensure that the /// configuration and initialization was successfull. - pub async fn start(self) -> Result, anyhow::Error> { - self.configure().await?.start().await + /// + /// If `lightningd` was called with `--help` we won't get a + /// `Plugin` instance and return `None` instead. This signals that + /// we should exit, and not continue running. `start()` returns in + /// order to allow user code to perform cleanup if necessary. + pub async fn start(self) -> Result>, anyhow::Error> { + if let Some(cp) = self.configure().await? { + Ok(Some(cp.start().await?)) + } else { + Ok(None) + } } fn handle_get_manifest( From f067e8c909f83a013b724390caa0341626d8c14d Mon Sep 17 00:00:00 2001 From: Michael Dance Date: Wed, 6 Apr 2022 17:54:52 -0600 Subject: [PATCH 0684/1530] Changed external/libwally-core to test_build_fix Combined with the following commit which is required to update against changed libsecp256k1 APIs: Updated deprecated function calls --- common/blinding.c | 2 +- common/bolt12.c | 1 + common/key_derive.c | 8 ++++---- devtools/bolt12-cli.c | 1 + external/libwally-core | 2 +- hsmd/libhsmd.c | 4 ++-- lightningd/offer.c | 2 +- plugins/fetchinvoice.c | 6 +++--- plugins/offers_inv_hook.c | 1 + plugins/offers_invreq_hook.c | 4 ++-- 10 files changed, 17 insertions(+), 14 deletions(-) diff --git a/common/blinding.c b/common/blinding.c index b92c731c0313..1fbb4b09d34b 100644 --- a/common/blinding.c +++ b/common/blinding.c @@ -35,6 +35,6 @@ bool blinding_next_privkey(const struct privkey *e, struct privkey *next) { *next = *e; - return secp256k1_ec_privkey_tweak_mul(secp256k1_ctx, next->secret.data, + return secp256k1_ec_seckey_tweak_mul(secp256k1_ctx, next->secret.data, h->u.u8) == 1; } diff --git a/common/bolt12.c b/common/bolt12.c index a0ce4c3ad4a8..ec1569c90561 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -83,6 +83,7 @@ bool bolt12_check_signature(const struct tlv_field *fields, return secp256k1_schnorrsig_verify(secp256k1_ctx, sig->u8, shash.u.u8, + sizeof(shash.u.u8), &key->pubkey) == 1; } diff --git a/common/key_derive.c b/common/key_derive.c index b5ed2424b1c3..bdf87d99382c 100644 --- a/common/key_derive.c +++ b/common/key_derive.c @@ -84,7 +84,7 @@ bool derive_simple_privkey(const struct secret *base_secret, #endif key->secret = *base_secret; - if (secp256k1_ec_privkey_tweak_add(secp256k1_ctx, key->secret.data, + if (secp256k1_ec_seckey_tweak_add(secp256k1_ctx, key->secret.data, sha.u.u8) != 1) return false; #ifdef SUPERVERBOSE @@ -207,7 +207,7 @@ bool derive_revocation_privkey(const struct secret *base_secret, #endif key->secret = *base_secret; - if (secp256k1_ec_privkey_tweak_mul(secp256k1_ctx, key->secret.data, + if (secp256k1_ec_seckey_tweak_mul(secp256k1_ctx, key->secret.data, sha.u.u8) != 1) return false; @@ -229,7 +229,7 @@ bool derive_revocation_privkey(const struct secret *base_secret, #endif part2 = *per_commitment_secret; - if (secp256k1_ec_privkey_tweak_mul(secp256k1_ctx, part2.data, + if (secp256k1_ec_seckey_tweak_mul(secp256k1_ctx, part2.data, sha.u.u8) != 1) return false; #ifdef SUPERVERBOSE @@ -239,7 +239,7 @@ bool derive_revocation_privkey(const struct secret *base_secret, printf("# = 0x%s\n", tal_hexstr(tmpctx, &part2, sizeof(part2))); #endif - if (secp256k1_ec_privkey_tweak_add(secp256k1_ctx, key->secret.data, + if (secp256k1_ec_seckey_tweak_add(secp256k1_ctx, key->secret.data, part2.data) != 1) return false; diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index de7dedf1be10..a285e851186a 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -321,6 +321,7 @@ static bool print_signature(const char *messagename, if (secp256k1_schnorrsig_verify(secp256k1_ctx, sig->u8, shash.u.u8, + sizeof(shash.u.u8), &node_id->pubkey) != 1) { fprintf(stderr, "%s: INVALID\n", fieldname); return false; diff --git a/external/libwally-core b/external/libwally-core index 46a3db9b7bce..4218558bbfdb 160000 --- a/external/libwally-core +++ b/external/libwally-core @@ -1 +1 @@ -Subproject commit 46a3db9b7bce9179430d81ee10bcd25ace5616e4 +Subproject commit 4218558bbfdbfd4800c90457484b3f2dbfaac0c6 diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 6a5e349623d6..84f78de344d7 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -635,10 +635,10 @@ static u8 *handle_sign_bolt12(struct hsmd_client *c, const u8 *msg_in) } } - if (!secp256k1_schnorrsig_sign(secp256k1_ctx, sig.u8, + if (!secp256k1_schnorrsig_sign32(secp256k1_ctx, sig.u8, sha.u.u8, &kp, - NULL, NULL)) { + NULL)) { return hsmd_status_bad_request_fmt(c, msg_in, "Failed to sign bolt12"); } diff --git a/lightningd/offer.c b/lightningd/offer.c index 511e5a225a4e..c97ff9b4e844 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -77,7 +77,7 @@ static void hsm_sign_b12(struct lightningd *ld, /* Now we sanity-check! */ sighash_from_merkle(messagename, fieldname, merkle, &sighash); if (secp256k1_schnorrsig_verify(secp256k1_ctx, sig->u8, - sighash.u.u8, &key->pubkey) != 1) + sighash.u.u8, sizeof(sighash.u.u8), &key->pubkey) != 1) fatal("HSM gave bad signature %s for pubkey %s", type_to_string(tmpctx, struct bip340sig, sig), type_to_string(tmpctx, struct point32, key)); diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 6039ed6fd0a0..f94400812e21 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -218,7 +218,7 @@ static struct command_result *handle_invreq_response(struct command *cmd, if (!inv->signature || secp256k1_schnorrsig_verify(secp256k1_ctx, inv->signature->u8, - sighash.u.u8, &inv->node_id->pubkey) != 1) { + sighash.u.u8, sizeof(sighash.u.u8), &inv->node_id->pubkey) != 1) { badfield = "signature"; goto badinv; } @@ -1198,11 +1198,11 @@ force_payer_secret(struct command *cmd, sighash_from_merkle("invoice_request", "signature", &merkle, &sha); sent->invreq->signature = tal(invreq, struct bip340sig); - if (!secp256k1_schnorrsig_sign(secp256k1_ctx, + if (!secp256k1_schnorrsig_sign32(secp256k1_ctx, sent->invreq->signature->u8, sha.u.u8, &kp, - NULL, NULL)) { + NULL)) { return command_fail(cmd, LIGHTNINGD, "Failed to sign with payer_secret"); } diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index a15179465bca..3e24333629bf 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -387,6 +387,7 @@ struct command_result *handle_invoice(struct command *cmd, if (secp256k1_schnorrsig_verify(secp256k1_ctx, inv->inv->signature->u8, shash.u.u8, + sizeof(shash.u.u8), &inv->inv->node_id->pubkey) != 1) { return fail_inv(cmd, inv, "Bad signature"); } diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 7f586437f5d5..3a991ad490e2 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -433,7 +433,7 @@ static bool check_payer_sig(struct command *cmd, if (secp256k1_schnorrsig_verify(secp256k1_ctx, sig->u8, - sighash.u.u8, &payer_key->pubkey) == 1) + sighash.u.u8, sizeof(sighash.u.u8), &payer_key->pubkey) == 1) return true; if (!deprecated_apis) @@ -447,7 +447,7 @@ static bool check_payer_sig(struct command *cmd, return secp256k1_schnorrsig_verify(secp256k1_ctx, sig->u8, - sighash.u.u8, &payer_key->pubkey) == 1; + sighash.u.u8, sizeof(sighash.u.u8), &payer_key->pubkey) == 1; } static struct command_result *invreq_amount_by_quantity(struct command *cmd, From 874d3532da828ea36a22d2cfd83f46f9519ccc92 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Apr 2022 12:02:41 +0930 Subject: [PATCH 0685/1530] libwally: update to v0.8.5 Signed-off-by: Rusty Russell --- external/libwally-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/libwally-core b/external/libwally-core index 4218558bbfdb..f7c0824e56a0 160000 --- a/external/libwally-core +++ b/external/libwally-core @@ -1 +1 @@ -Subproject commit 4218558bbfdbfd4800c90457484b3f2dbfaac0c6 +Subproject commit f7c0824e56a068c4d9c27cb2e8b26e2a9b8ea3b3 From 436706384cb4e41f607a59ffdada0eb8272fd46a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 6 Apr 2022 11:56:45 +0930 Subject: [PATCH 0686/1530] plugins: add grpc-plugin to PLUGINS, so it gets started and installed. Fixes: #5162 Signed-off-by: Rusty Russell --- plugins/Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index b30b988cabbb..e3e2f7523779 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -168,6 +168,11 @@ plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +ifneq ($(RUST),0) +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/cln-grpc +PLUGINS += target/${RUST_PROFILE}/cln-grpc +endif + # Generated from PLUGINS definition in plugins/Makefile ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile @@ -182,8 +187,4 @@ ${CLN_PLUGIN_EXAMPLES}: ${CLN_PLUGIN_SRC} target/${RUST_PROFILE}/cln-grpc: ${CLN_PLUGIN_SRC} cargo build ${CARGO_OPTS} --bin cln-grpc -ifneq ($(RUST),0) -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/cln-grpc -endif - include plugins/test/Makefile From e4ed15dc33243444a74f6cbaa3aedb5202083b90 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 6 Apr 2022 14:16:14 +0930 Subject: [PATCH 0687/1530] plugins: fix cln-grpc to work as a builtin when we're run in-tree. Signed-off-by: Rusty Russell --- plugins/Makefile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index e3e2f7523779..27983596d28a 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -169,8 +169,13 @@ plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) ifneq ($(RUST),0) -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) target/${RUST_PROFILE}/cln-grpc -PLUGINS += target/${RUST_PROFILE}/cln-grpc +# Builtin plugins must be in this plugins dir to work when we're executed +# *without* make install. +plugins/cln-grpc: target/${RUST_PROFILE}/cln-grpc + @cp $< $@ + +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) plugins/cln-grpc +PLUGINS += plugins/cln-grpc endif # Generated from PLUGINS definition in plugins/Makefile From a3ec140c03af51de2b163ce51702d4c65467d634 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 10 Apr 2022 15:24:16 +0930 Subject: [PATCH 0688/1530] doc: document the grpc-port option, now cln-grpc is a built-in. Signed-off-by: Rusty Russell --- doc/lightningd-config.5.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 9eb70dfe7d96..5fc07bea9388 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -220,6 +220,11 @@ If there is no `hsm_secret` yet, `lightningd` will create a new encrypted secret If you have an unencrypted `hsm_secret` you want to encrypt on-disk, or vice versa, see lightning-hsmtool(8). + **grpc-port**=*portnum* [plugin `cln-grpc`] + +The port number for the GRPC plugin to listen for incoming +connections; default is not to activate the plugin at all. + ### Lightning node customization options **alias**=*NAME* From c598d4df7483151160abd96b80a6f3970cfb9973 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 10 Apr 2022 20:08:15 +0930 Subject: [PATCH 0689/1530] Makefile: make sure ALL_PROGRAMS doesn't include cln-grpc plugin. $(ALL_PROGRAMS) are assumed to be C programs for now. Add $(C_PLUGINS) as a new variable. Signed-off-by: Rusty Russell --- plugins/Makefile | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index 27983596d28a..429c2707927b 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -75,7 +75,7 @@ PLUGIN_ALL_HEADER := \ $(PLUGIN_SPENDER_HEADER) PLUGIN_ALL_OBJS := $(PLUGIN_ALL_SRC:.c=.o) -PLUGINS := \ +C_PLUGINS := \ plugins/autoclean \ plugins/bcli \ plugins/fetchinvoice \ @@ -87,10 +87,22 @@ PLUGINS := \ plugins/txprepare \ plugins/spenderp +PLUGINS := $(C_PLUGINS) + +ifneq ($(RUST),0) +# Builtin plugins must be in this plugins dir to work when we're executed +# *without* make install. +plugins/cln-grpc: target/${RUST_PROFILE}/cln-grpc + @cp $< $@ + +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) plugins/cln-grpc +PLUGINS += plugins/cln-grpc +endif + # Make sure these depend on everything. ALL_C_SOURCES += $(PLUGIN_ALL_SRC) ALL_C_HEADERS += $(PLUGIN_ALL_HEADER) -ALL_PROGRAMS += $(PLUGINS) +ALL_PROGRAMS += $(C_PLUGINS) PLUGIN_COMMON_OBJS := \ bitcoin/base58.o \ @@ -168,16 +180,6 @@ plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) -ifneq ($(RUST),0) -# Builtin plugins must be in this plugins dir to work when we're executed -# *without* make install. -plugins/cln-grpc: target/${RUST_PROFILE}/cln-grpc - @cp $< $@ - -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) plugins/cln-grpc -PLUGINS += plugins/cln-grpc -endif - # Generated from PLUGINS definition in plugins/Makefile ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile From bad89f186a70e595475ad27c1a77f06fdff066ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Apr 2022 14:29:30 +0930 Subject: [PATCH 0690/1530] doc/Makefile: make check-manpages require the cln-grpc plugin. Signed-off-by: Rusty Russell --- doc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Makefile b/doc/Makefile index 68d4eb842106..e3b8b47546bd 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -155,7 +155,7 @@ clean: doc-clean check: check-manpages # This needs plugins, too. -check-manpages: all-programs check-config-docs +check-manpages: all-programs check-config-docs default-targets @tools/check-manpage.sh cli/lightning-cli doc/lightning-cli.1.md @tools/check-manpage.sh "lightningd/lightningd --lightning-dir=/tmp/" doc/lightningd-config.5.md From e5abc10ae22467469e9f86b48ad268d4700bbb68 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Apr 2022 15:47:16 +0930 Subject: [PATCH 0691/1530] configure: require rustfmt as well, for rust support. Signed-off-by: Rusty Russell --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 2e0de79843b0..a0fd5d805062 100755 --- a/configure +++ b/configure @@ -111,7 +111,7 @@ default_valgrind_setting() default_rust_setting() { - if cargo --version > /dev/null 2>&1; then + if cargo --version > /dev/null 2>&1 && rustfmt --version >/dev/null 2>&1; then echo 1 else echo 0 From 355025239dc9768bc1437435fb84b2111ec32d3b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Apr 2022 15:55:55 +0930 Subject: [PATCH 0692/1530] Make sure DEFAULT_TARGETS includes rust testing examples. Otherwise tests fail: ``` def test_plugin_start(node_factory): """Start a minimal plugin and ensure it is well-behaved """ bin_path = Path.cwd() / "target" / "debug" / "examples" / "cln-plugin-startup" > l1 = node_factory.get_node(options={"plugin": str(bin_path), 'test-option': 31337}) ... error starting plugin '/home/runner/work/lightning/lightning/target/debug/examples/cln-plugin-startup': opening pipe: No such file or directory ``` Signed-off-by: Rusty Russell --- plugins/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/Makefile b/plugins/Makefile index 429c2707927b..bae7bfa4e583 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -95,7 +95,6 @@ ifneq ($(RUST),0) plugins/cln-grpc: target/${RUST_PROFILE}/cln-grpc @cp $< $@ -DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) plugins/cln-grpc PLUGINS += plugins/cln-grpc endif @@ -194,4 +193,8 @@ ${CLN_PLUGIN_EXAMPLES}: ${CLN_PLUGIN_SRC} target/${RUST_PROFILE}/cln-grpc: ${CLN_PLUGIN_SRC} cargo build ${CARGO_OPTS} --bin cln-grpc +ifneq ($(RUST),0) +DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) plugins/cln-grpc +endif + include plugins/test/Makefile From 3f94b74e07461256855fc382e3b07f9a67405a3a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Apr 2022 17:47:37 +0930 Subject: [PATCH 0693/1530] pytest: don't add extra cln-grpc instance now plugin is builtin Signed-off-by: Rusty Russell --- tests/test_cln_rs.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 8fd3b8336b2b..c8b9b7e983b6 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -77,8 +77,7 @@ def test_grpc_connect(node_factory): from primitives_pb2 import AmountOrAny, Amount # noqa: E402 grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" - l1 = node_factory.get_node(options={"plugin": str(bin_path), "grpc-port": str(grpc_port)}) + l1 = node_factory.get_node(options={"grpc-port": str(grpc_port)}) p = Path(l1.daemon.lightning_dir) / TEST_NETWORK cert_path = p / "client.pem" @@ -134,9 +133,7 @@ def test_grpc_generate_certificate(node_factory): - If we delete one cert or its key it should get regenerated. """ grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1 = node_factory.get_node(options={ - "plugin": str(bin_path), "grpc-port": str(grpc_port), }, start=False) @@ -171,10 +168,7 @@ def test_grpc_generate_certificate(node_factory): def test_grpc_no_auto_start(node_factory): """Ensure that we do not start cln-grpc unless a port is configured. """ - bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" - l1, = node_factory.get_nodes(1, opts={ - "plugin": str(bin_path), - }) + l1 = node_factory.get_node() wait_for(lambda: [p for p in l1.rpc.plugin('list')['plugins'] if 'cln-grpc' in p['name']] == []) assert l1.daemon.is_in_log(r'plugin-cln-grpc: Killing plugin: disabled itself at init') @@ -191,9 +185,7 @@ def test_grpc_wrong_auth(node_factory): import node_pb2 as nodepb # noqa: E402 grpc_port = reserve() - bin_path = Path.cwd() / "target" / "debug" / "cln-grpc" l1, l2 = node_factory.get_nodes(2, opts={ - "plugin": str(bin_path), "start": False, "grpc-port": str(grpc_port), }) From e90b50a1b2bdaf7dfd3a798d935dbd8ce1b12e83 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 11 Apr 2022 10:02:03 +0930 Subject: [PATCH 0694/1530] dualopend: handle dev_memleak during RBF. Happens in CI: ``` lightningd-2: 2022-04-10T15:30:40.788Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-dualopend-chan#1: STATUS_FAIL_MASTER_IO: Error parsing 7507: 1b79 ``` Signed-off-by: Rusty Russell --- openingd/dualopend.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 737e9fb70aad..2afcc4993586 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3061,6 +3061,13 @@ static void rbf_wrap_up(struct state *state, wire_sync_write(REQ_FD, take(msg)); msg = wire_sync_read(tmpctx, REQ_FD); +#if DEVELOPER + while (fromwire_dualopend_dev_memleak(msg)) { + handle_dev_memleak(state, msg); + msg = wire_sync_read(tmpctx, REQ_FD); + } +#endif + if ((msg_type = fromwire_peektype(msg)) == WIRE_DUALOPEND_FAIL) { if (!fromwire_dualopend_fail(msg, msg, &err_reason)) master_badmsg(msg_type, msg); From a326ea8bb2973ff78a8ccc97931069d5ab991576 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 13 Apr 2022 05:26:39 +0930 Subject: [PATCH 0695/1530] CHANGELOG.md: rc3 This time for sure! Signed-off-by: Rusty Russell --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb45d63597a9..9e52fbbab8a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [v0.11.0rc2] - 2022-04-04: Simon's Carefully Chosen Release Name +## [v0.11.0rc3] - 2022-04-04: Simon's Carefully Chosen Release Name This release named by Simon Vrouwe; this marks the name change to core-lightning (#CLN). From 151d0094354e20654f49a58aa4923b3c50faa4b7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 13 Apr 2022 10:22:07 +0930 Subject: [PATCH 0696/1530] lightningd: remove over-zealous assert. This was hit on my node. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8565a73a3e34..7994e6fbb6b5 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1007,7 +1007,6 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa log_debug(channel->log, "Peer has reconnected, state %s: telling connectd to make active", channel_state_name(channel)); - assert(!channel->owner); subd_send_msg(ld->connectd, take(towire_connectd_peer_make_active(NULL, &peer->id, &channel->cid))); From c3a749957318fc4fc330fa38e46337811cce9ab6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 04:24:32 +0930 Subject: [PATCH 0697/1530] connectd: avoid use-after-free on reconnect with remote_addr. I was seeing a strange crash: Connectd gave bad CONNECT_PEER_CONNECTED message The message is indeed mangled, around the remote_addr! A quick review of the code revealed that we were not making a copy when it was a reconnect, and so the remote_addr pointer was pointing to memory which was freed. Signed-off-by: Rusty Russell --- connectd/connectd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 3add35b3f980..daa159f660ba 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -269,7 +269,7 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, pr->id = *id; pr->cs = *cs; pr->addr = *addr; - pr->remote_addr = remote_addr; + pr->remote_addr = tal_dup_or_null(pr, struct wireaddr, remote_addr); pr->incoming = incoming; /*~ Note that tal_dup_talarr() will do handle the take() of features From 33ae601266fc60ab2ef71bf575212aab8202e9a9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 05:29:41 +0930 Subject: [PATCH 0698/1530] CI: fix bsd workflow. I have no idea why someone else owns the directory suddenly, but all git commands fail. Workaround as suggested by the error message. Signed-off-by: Rusty Russell --- .github/workflows/bsd.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 360016ff0074..524b9b80390f 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -70,6 +70,8 @@ jobs: pytest-json-report git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc + # fatal: unsafe repository ('/Users/runner/work/lightning/lightning' is owned by someone else) + git config --global --add safe.directory `pwd` git submodule update --init --recursive ./configure CC="$CC" From f0dc028fa9b76a9a7829c1b163f3f39ab2bd7efc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Apr 2022 13:56:32 +0930 Subject: [PATCH 0699/1530] lightningd: fix overzealous check in htlc_out_check: ``` +11.668971802 lightningdBROKEN: backtrace: lightningd/log.c:821 (fatal_vfmt) 0x558c893c997f +11.668978165 lightningdBROKEN: backtrace: lightningd/log.c:829 (fatal) 0x558c893c9a30 +11.668984935 lightningdBROKEN: backtrace: lightningd/htlc_end.c:87 (corrupt) 0x558c893b9b7d +11.668991262 lightningdBROKEN: backtrace: lightningd/htlc_end.c:205 (htlc_out_check) 0x558c893ba352 +11.669016705 lightningdBROKEN: backtrace: lightningd/peer_htlcs.c:1471 (check_already_failed) 0x558c893ea9c9 +11.669023345 lightningdBROKEN: backtrace: lightningd/peer_htlcs.c:1575 (onchain_failed_our_htlc) 0x558c893eb098 +11.669043122 lightningdBROKEN: backtrace: lightningd/onchain_control.c:438 (handle_onchain_htlc_timeout) 0x558c893cd6ec +11.669049818 lightningdBROKEN: backtrace: lightningd/onchain_control.c:548 (onchain_msg) 0x558c893cdbdc +11.669056372 lightningdBROKEN: backtrace: lightningd/subd.c:556 (sd_msg_read) 0x558c893fa532 +11.669063030 lightningdBROKEN: backtrace: ccan/ccan/io/io.c:59 (next_plan) 0x558c8948e3cd +11.669069093 lightningdBROKEN: backtrace: ccan/ccan/io/io.c:407 (do_plan) 0x558c8948ef9e +11.669075470 lightningdBROKEN: backtrace: ccan/ccan/io/io.c:417 (io_ready) 0x558c8948efdc +11.669081900 lightningdBROKEN: backtrace: ccan/ccan/io/poll.c:453 (io_loop) 0x558c894912a8 +11.669087916 lightningdBROKEN: backtrace: lightningd/io_loop_with_timers.c:22 (io_loop_with_timers) 0x558c893c0966 +11.669094531 lightningdBROKEN: backtrace: lightningd/lightningd.c:1181 (main) 0x558c893c6bf9 ``` Fixes: #5191 Signed-off-by: Rusty Russell --- lightningd/htlc_end.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/lightningd/htlc_end.c b/lightningd/htlc_end.c index bd80eecde58a..d9a80ac1b50b 100644 --- a/lightningd/htlc_end.c +++ b/lightningd/htlc_end.c @@ -201,9 +201,6 @@ struct htlc_out *htlc_out_check(const struct htlc_out *hout, return corrupt(abortstr, "Output failmsg, input preimage"); } else if (hout->failmsg) { - if (hout->in->failonion) - return corrupt(abortstr, - "Output failmsg, input failonion"); if (hout->in->preimage) return corrupt(abortstr, "Output failmsg, input preimage"); From cb70d06d6762f0c1ccd28f25e5f14ba90b789e24 Mon Sep 17 00:00:00 2001 From: zero fee routing <90521529+zerofeerouting@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:08:54 +0200 Subject: [PATCH 0700/1530] corrected typo in description --- doc/lightning-listchannels.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index 5113ba6f5199..54286a9b365c 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -22,7 +22,7 @@ are returned. If *destination* is a node id, then only channels leading to that node id are returned. -Only one of *short\_channgel\_id*, *source* or *destination* can be supplied. +Only one of *short\_channel\_id*, *source* or *destination* can be supplied. If nothing is supplied, data on all lightning channels known to this node, are returned. These can be local channels or public channels broadcast on the gossip network. From ee21ae814a71d4b29228f3d09cb25e09937137ec Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 07:16:35 +0930 Subject: [PATCH 0701/1530] gossipd: revert useless bf8cb640b71e15f935ced19934150c921c025981. This attempted to make us re-xmit our own node_announcement at restart, by moving the node_announcement to the end of the gossip store. But, as nothing is connected, yet, this had no effect! We will rexmit it anyway, since it's marked PUSH. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 20 +++----------------- gossipd/routing.c | 4 ++-- gossipd/routing.h | 4 ---- gossipd/test/run-check_node_announcement.c | 3 --- gossipd/test/run-crc32_of_update.c | 3 --- 5 files changed, 5 insertions(+), 29 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 1a561ac3d418..a07a5f5da4ff 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -335,19 +335,9 @@ static bool update_own_node_announcement(struct daemon *daemon, return true; } -/* This retransmits the existing node announcement */ -static void force_self_nannounce_rexmit(struct daemon *daemon) -{ - struct node *self = get_node(daemon->rstate, &daemon->id); - - force_node_announce_rexmit(daemon->rstate, self); -} - static void update_own_node_announcement_after_startup(struct daemon *daemon) { - /* If that doesn't send one, arrange rexmit anyway */ - if (!update_own_node_announcement(daemon, false, false)) - force_self_nannounce_rexmit(daemon); + update_own_node_announcement(daemon, false, false); } /* This creates and transmits a *new* node announcement */ @@ -362,7 +352,7 @@ static void force_self_nannounce_regen(struct daemon *daemon) update_own_node_announcement(daemon, false, true); } -/* Because node_announcement propagation is spotty, we rexmit this every +/* Because node_announcement propagation is spotty, we regenerate this every * 24 hours. */ static void setup_force_nannounce_regen_timer(struct daemon *daemon) { @@ -386,11 +376,7 @@ void maybe_send_own_node_announce(struct daemon *daemon, bool startup) if (!daemon->rstate->local_channel_announced) return; - /* If we didn't send one, arrange rexmit of existing at startup */ - if (!update_own_node_announcement(daemon, startup, false)) { - if (startup) - force_self_nannounce_rexmit(daemon); - } + update_own_node_announcement(daemon, startup, false); } /* Fast accessors for channel_update fields */ diff --git a/gossipd/routing.c b/gossipd/routing.c index abe1d7a49486..a81e400f969c 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -423,8 +423,8 @@ static bool node_announce_predates_channels(const struct node *node) /* Move this node's announcement to the tail of the gossip_store, to * make everyone send it again. */ -void force_node_announce_rexmit(struct routing_state *rstate, - struct node *node) +static void force_node_announce_rexmit(struct routing_state *rstate, + struct node *node) { const u8 *announce; bool is_local = node_id_eq(&node->id, &rstate->local_id); diff --git a/gossipd/routing.h b/gossipd/routing.h index 35abe5eb35dd..1641284bb667 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -406,8 +406,4 @@ void remove_all_gossip(struct routing_state *rstate); /* This scid is dead to us. */ void add_to_txout_failures(struct routing_state *rstate, const struct short_channel_id *scid); - -/* Move this node's announcement to the tail of the gossip_store, to - * make everyone send it again. */ -void force_node_announce_rexmit(struct routing_state *rstate, struct node *node); #endif /* LIGHTNING_GOSSIPD_ROUTING_H */ diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index e2d36b59dcbe..7d20a8720211 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -33,9 +33,6 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } -/* Generated stub for force_node_announce_rexmit */ -void force_node_announce_rexmit(struct routing_state *rstate UNNEEDED, struct node *node UNNEEDED) -{ fprintf(stderr, "force_node_announce_rexmit called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_update */ bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index ea1104c7bc69..3b46a0f54c15 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -51,9 +51,6 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } -/* Generated stub for force_node_announce_rexmit */ -void force_node_announce_rexmit(struct routing_state *rstate UNNEEDED, struct node *node UNNEEDED) -{ fprintf(stderr, "force_node_announce_rexmit called!\n"); abort(); } /* Generated stub for fromwire_gossipd_dev_set_max_scids_encode_size */ bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } From 9b944dbed4c121a912c2380d71a228fcc3c2a180 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 07:16:35 +0930 Subject: [PATCH 0702/1530] common/gossip_store: add flag to *only* fetch "push"-marked messages. These are the ones which are for our own channels (and our own node_announcement). Signed-off-by: Rusty Russell --- common/gossip_store.c | 3 +++ common/gossip_store.h | 1 + connectd/multiplex.c | 1 + 3 files changed, 5 insertions(+) diff --git a/common/gossip_store.c b/common/gossip_store.c index 686b1f1773a7..eba8a5ab03c4 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -53,6 +53,7 @@ static size_t reopen_gossip_store(int *gossip_store_fd, const u8 *msg) u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, u32 timestamp_min, u32 timestamp_max, + bool push_only, size_t *off, size_t *end) { u8 *msg = NULL; @@ -109,6 +110,8 @@ u8 *gossip_store_next(const tal_t *ctx, !timestamp_filter(timestamp_min, timestamp_max, timestamp)) { msg = tal_free(msg); + } else if (!push && push_only) { + msg = tal_free(msg); } } diff --git a/common/gossip_store.h b/common/gossip_store.h index b5d8fad1905d..8fa6ee145c78 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -46,6 +46,7 @@ struct gossip_hdr { u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, u32 timestamp_min, u32 timestamp_max, + bool push_only, size_t *off, size_t *end); /** diff --git a/connectd/multiplex.c b/connectd/multiplex.c index fafc326a9fb0..8a94a33ee3d1 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -367,6 +367,7 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) msg = gossip_store_next(ctx, &peer->daemon->gossip_store_fd, peer->gs.timestamp_min, peer->gs.timestamp_max, + false, &peer->gs.off, &peer->daemon->gossip_store_end); /* Don't send back gossip they sent to us! */ From a5d027cefcd7b8feb34dfa4c5b5ba7dae4ab6d3a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 07:23:02 +0930 Subject: [PATCH 0703/1530] connectd: send our own gossip, even if peer hasn't sent timestamp_filter. We seem to have made node_announcement propagation *worse*, not better. Explorers don't see my nodes updates. At least some LND nodes never send us timestamp_filter, so we are never actually stream *any* gossip. We should send gossip about ourselves, even if they haven't set a filter (yet). Signed-off-by: Rusty Russell Changelog-Added: Protocol: we more aggressively send our own gossip, to improve propagation chances. --- connectd/multiplex.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 8a94a33ee3d1..118f3189745c 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -356,7 +356,27 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) { u8 *msg; - /* Not streaming yet? */ + /* dev-mode can suppress all gossip */ + if (IFDEV(peer->daemon->dev_suppress_gossip, false)) + return NULL; + + /* BOLT #7: + * - if the `gossip_queries` feature is negotiated: + * - MUST NOT relay any gossip messages it did not generate itself, + * unless explicitly requested. + */ + + /* So, even if they didn't send us a timestamp_filter message, + * we *still* send our own gossip. */ + if (!peer->gs.gossip_timer) { + return gossip_store_next(ctx, &peer->daemon->gossip_store_fd, + 0, 0xFFFFFFFF, + true, + &peer->gs.off, + &peer->daemon->gossip_store_end); + } + + /* Not streaming right now? */ if (!peer->gs.active) return NULL; From f81a7ff792f55399e83709bd11f4ad2af6777d85 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 07:24:02 +0930 Subject: [PATCH 0704/1530] gossipd: fix daily node_announcement regeneration. We have an explicit filter against redundant node_announcement updates; we only allow 1 a week. This means that our change to force a reannouncement every 24 hours did not work! Allow once a day, instead. Signed-off-by: Rusty Russell --- gossipd/routing.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index a81e400f969c..cfed0e0eb74c 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1614,6 +1614,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, if (node->bcast.index) { bool only_tlv_diff; + u32 redundant_time; if (index != 0) { status_broken("gossip_store node_announcement %u replaces %u!", @@ -1627,8 +1628,9 @@ bool routing_add_node_announcement(struct routing_state *rstate, return index == 0; } - /* Allow redundant updates once every 7 days */ - if (timestamp < node->bcast.timestamp + GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune) / 2 + /* Allow redundant updates once a day (faster in dev-fast-gossip-prune mode) */ + redundant_time = GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune) / 14; + if (timestamp < node->bcast.timestamp + redundant_time && !nannounce_different(rstate->gs, node, msg, &only_tlv_diff)) { SUPERVERBOSE( From c110d0b068e500a7ca08a4b425a44c724f0bdc19 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 07:24:04 +0930 Subject: [PATCH 0705/1530] pytest: test that node_announcement regeneration works as expected. We shorten 24 hours to 24 seconds using --dev--fast-gossip-prune. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 10 +++++++++- tests/test_gossip.py | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index a07a5f5da4ff..04a920777017 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -356,11 +356,19 @@ static void force_self_nannounce_regen(struct daemon *daemon) * 24 hours. */ static void setup_force_nannounce_regen_timer(struct daemon *daemon) { + struct timerel regen_time; + + /* For developers we can force a regen every 24 seconds to test */ + if (IFDEV(daemon->rstate->dev_fast_gossip_prune, false)) + regen_time = time_from_sec(24); + else + regen_time = time_from_sec(24 * 3600); + tal_free(daemon->node_announce_regen_timer); daemon->node_announce_regen_timer = new_reltimer(&daemon->timers, daemon, - time_from_sec(24 * 3600), + regen_time, force_self_nannounce_regen, daemon); } diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 9e6b463ce3f3..e5d76d6affac 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -45,6 +45,11 @@ def test_gossip_pruning(node_factory, bitcoind): wait_for(lambda: [c['active'] for c in l2.rpc.listchannels()['channels']] == [True] * 4) wait_for(lambda: [c['active'] for c in l3.rpc.listchannels()['channels']] == [True] * 4) + # Also check that it sends a redundant node_announcement. + ts1 = only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['last_timestamp'] + wait_for(lambda: only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['last_timestamp'] != ts1) + assert only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['last_timestamp'] >= ts1 + 24 + # All of them should send a keepalive message (after 30 seconds) l1.daemon.wait_for_logs([ 'Sending keepalive channel_update for {}'.format(scid1), From 393e8e5e6ac74e233a4f47a16eb256daf3385f42 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 07:24:04 +0930 Subject: [PATCH 0706/1530] connectd: remove a noisy debug msg, fix name typo. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 118f3189745c..54740a7b3c4f 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -392,9 +392,6 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) &peer->daemon->gossip_store_end); /* Don't send back gossip they sent to us! */ if (msg) { - status_peer_debug(&peer->id, - "Sending gossip %s", - peer_wire_name(fromwire_peektype(msg))); if (gossip_rcvd_filter_del(peer->gs.grf, msg)) { msg = tal_free(msg); goto again; @@ -582,7 +579,7 @@ static void handle_gossip_in(struct peer *peer, const u8 *msg) daemon_conn_send(peer->daemon->gossipd, take(gmsg)); } -static void handle_gossip_timetamp_filter_in(struct peer *peer, const u8 *msg) +static void handle_gossip_timestamp_filter_in(struct peer *peer, const u8 *msg) { struct bitcoin_blkid chain_hash; u32 first_timestamp, timestamp_range; @@ -650,7 +647,7 @@ static bool handle_message_locally(struct peer *peer, const u8 *msg) gossip_rcvd_filter_add(peer->gs.grf, msg); if (type == WIRE_GOSSIP_TIMESTAMP_FILTER) { - handle_gossip_timetamp_filter_in(peer, msg); + handle_gossip_timestamp_filter_in(peer, msg); return true; } else if (type == WIRE_PING) { handle_ping_in(peer, msg); From 37e8d2fb0fa900c5259098b24a0f41ebc0e2c0fe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Apr 2022 20:32:04 +0930 Subject: [PATCH 0707/1530] connectd: disable advertizement of WEBSOCKET addresses. This seems to prevent broad propagation, due to LND not allowing it. See https://github.com/lightningnetwork/lnd/issues/6432 We still announce it if you disable deprecated-apis, so tests still work, and hopefully we can enable it in future. Fixes: #5196 Signed-off-by: Rusty Russell Changelog-EXPERIMENTAL: Protocol: disabled websocket announcement due to LND propagation issues --- CHANGELOG.md | 4 +++- connectd/connectd.c | 14 ++++++++++---- connectd/connectd.h | 3 +++ connectd/connectd_wire.csv | 1 + lightningd/connect_control.c | 1 + 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e52fbbab8a4..fc300ef59744 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This release named by Simon Vrouwe; this marks the name change to core-lightning - Protocol: we now support opening multiple channels with the same peer. ([#5078]) - Protocol: we send/receive IP addresses in `init`, and send updated node_announcement when two peers report the same remote_addr (`disable-ip-discovery` suppresses this announcement). ([#5052]) + - Protocol: we more aggressively send our own gossip, to improve propagation chances. ([#5200]) - Plugins: `cln-grpc` first class GRPC interface for remotely controlling nodes over mTLS authentication; set `grpc-port` to activate ([#5013]) - Database: With the `sqlite3://` scheme for `--wallet` option, you can now specify a second file path for real-time database backup by separating it from the main file path with a `:` character. ([#4890]) - Protocol: `pay` (and decode, etc) supports bolt11 payment_metadata a-la https://github.com/lightning/bolts/pull/912 ([#5086]) @@ -96,7 +97,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. - Fixed `experimental-websocket-port` to work with default addresses. ([#4945]) - Protocol: removed support for v0.10.1 onion messages. ([#4921]) - Protocol: Ability to announce DNS addresses ([#4829]) - + - Protocol: disabled websocket announcement due to LND propagation issues ([#5200]) [#4829]: https://github.com/ElementsProject/lightning/pull/4829 @@ -141,6 +142,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#5130]: https://github.com/ElementsProject/lightning/pull/5130 [#5136]: https://github.com/ElementsProject/lightning/pull/5136 [#5146]: https://github.com/ElementsProject/lightning/pull/5146 +[#5200]: https://github.com/ElementsProject/lightning/pull/5200 [0.11.0]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0 ## [0.10.2] - 2021-11-03: Bitcoin Dust Consensus Rule diff --git a/connectd/connectd.c b/connectd/connectd.c index daa159f660ba..10e9a4c7d625 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1397,10 +1397,15 @@ setup_listeners(const tal_t *ctx, * different type. */ if (tal_count(*announceable) != 0) { - wireaddr_from_websocket(&addr.u.wireaddr, - daemon->websocket_port); - add_announceable(announceable, - &addr.u.wireaddr); + /* See https://github.com/lightningnetwork/lnd/issues/6432: + * if we add websocket to the node_announcement, it doesn't propagate. + * So we do not do this for now in general! */ + if (daemon->announce_websocket) { + wireaddr_from_websocket(&addr.u.wireaddr, + daemon->websocket_port); + add_announceable(announceable, + &addr.u.wireaddr); + } } else { status_unusual("Bound to websocket %s," " but we cannot announce" @@ -1535,6 +1540,7 @@ static void connect_init(struct daemon *daemon, const u8 *msg) &daemon->timeout_secs, &daemon->websocket_helper, &daemon->websocket_port, + &daemon->announce_websocket, &dev_fast_gossip, &dev_disconnect, &dev_no_ping_timer)) { diff --git a/connectd/connectd.h b/connectd/connectd.h index df039f02ef34..ab5829b48145 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -188,6 +188,9 @@ struct daemon { int gossip_store_fd; size_t gossip_store_end; + /* We only announce websocket addresses if !deprecated_apis */ + bool announce_websocket; + #if DEVELOPER /* Hack to speed up gossip timer */ bool dev_fast_gossip; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 07751e2e51bf..67af1dca055f 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -22,6 +22,7 @@ msgdata,connectd_init,use_v3_autotor,bool, msgdata,connectd_init,timeout_secs,u32, msgdata,connectd_init,websocket_helper,wirestring, msgdata,connectd_init,websocket_port,u16, +msgdata,connectd_init,announce_websocket,bool, msgdata,connectd_init,dev_fast_gossip,bool, # If this is set, then fd 5 is dev_disconnect_fd. msgdata,connectd_init,dev_disconnect,bool, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 38fb2c342d31..144f777100b3 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -558,6 +558,7 @@ int connectd_init(struct lightningd *ld) ld->config.connection_timeout_secs, websocket_helper_path, ld->websocket_port, + !deprecated_apis, IFDEV(ld->dev_fast_gossip, false), IFDEV(ld->dev_disconnect_fd >= 0, false), IFDEV(ld->dev_no_ping_timer, false)); From 34e93a718e2e363ec41f6e8f9d933df50c01da74 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Apr 2022 06:15:12 +0930 Subject: [PATCH 0708/1530] v0.11.0rc4 Signed-off-by: Rusty Russell --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc300ef59744..19a8770a4a2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [v0.11.0rc3] - 2022-04-04: Simon's Carefully Chosen Release Name +## [v0.11.0rc4] - 2022-04-04: Simon's Carefully Chosen Release Name This release named by Simon Vrouwe; this marks the name change to core-lightning (#CLN). From 562974acdbf7d39fe66c0fd0787f78a83a20d1ba Mon Sep 17 00:00:00 2001 From: "joe.miyamoto" Date: Mon, 18 Apr 2022 13:18:17 +0900 Subject: [PATCH 0709/1530] Fix miner bug in rust client generator. This commit fixes a bug in a function `gen_enum`, which is not caught because so far we have no non-required field in enums defined in json schema. --- cln-rpc/src/model.rs | 8 ++++++++ contrib/msggen/msggen/rust.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index c2f98c022176..d5286569aa1c 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -277,6 +277,7 @@ pub mod requests { pub string: Option, #[serde(alias = "hex", skip_serializing_if = "Option::is_none")] pub hex: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub mode: Option, #[serde(alias = "generation", skip_serializing_if = "Option::is_none")] pub generation: Option, @@ -449,6 +450,7 @@ pub mod requests { pub bolt11: Option, #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] pub payment_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, } @@ -539,6 +541,7 @@ pub mod requests { } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct NewaddrRequest { + #[serde(skip_serializing_if = "Option::is_none")] pub addresstype: Option, } @@ -741,6 +744,7 @@ pub mod requests { } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListforwardsRequest { + #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, #[serde(alias = "in_channel", skip_serializing_if = "Option::is_none")] pub in_channel: Option, @@ -775,6 +779,7 @@ pub mod requests { pub bolt11: Option, #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] pub payment_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub status: Option, } @@ -1934,6 +1939,7 @@ pub mod responses { pub index: u32, #[serde(alias = "sequence")] pub sequence: u32, + #[serde(skip_serializing_if = "Option::is_none")] pub item_type: Option, #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] pub channel: Option, @@ -1993,6 +1999,7 @@ pub mod responses { pub msat: Amount, #[serde(alias = "scriptPubKey")] pub script_pub_key: String, + #[serde(skip_serializing_if = "Option::is_none")] pub item_type: Option, #[serde(alias = "channel", skip_serializing_if = "Option::is_none")] pub channel: Option, @@ -2599,6 +2606,7 @@ pub mod responses { pub out_channel: Option, #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] pub payment_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, #[serde(alias = "fee_msat", skip_serializing_if = "Option::is_none")] pub fee_msat: Option, diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/rust.py index ebb005c091c1..82d25cb9fb7b 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/rust.py @@ -133,7 +133,7 @@ def gen_enum(e): defi = f" // Path `{e.path}`\n #[serde(rename = \"{e.name}\")]\n pub {e.name.normalized()}: {typename},\n" else: defi = f' #[serde(skip_serializing_if = "Option::is_none")]\n' - defi = f" pub {e.name.normalized()}: Option<{typename}>,\n" + defi += f" pub {e.name.normalized()}: Option<{typename}>,\n" return defi, decl From f6e7d0c5dc82bb6ed89f92cf81e4d495d9f6097c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Apr 2022 16:14:17 +0930 Subject: [PATCH 0710/1530] pytest: add test for crashing listpeers. Signed-off-by: Rusty Russell --- tests/test_connection.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 42d08005e0a8..0351f1dd1b75 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1798,6 +1798,31 @@ def test_multifunding_simple(node_factory, bitcoind): l1.rpc.pay(inv) +@pytest.mark.xfail(strict=True) +@pytest.mark.openchannel('v2') +def test_listpeers_crash(node_factory, bitcoind, executor): + ''' + Test for listpeers crash during dual-funding start + ''' + l1, l2 = node_factory.get_nodes(2) + + do_listpeers = True + + # Do lots of listpeers while this is happening + def lots_of_listpeers(node): + while do_listpeers: + node.rpc.listpeers() + + fut = executor.submit(lots_of_listpeers, l1) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fundwallet(10**6 + 1000000) + l1.rpc.fundchannel(l2.info['id'], 10**6)['tx'] + + do_listpeers = False + fut.result() + + @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_multifunding_one(node_factory, bitcoind): From 98f64fb62335bd51731c2b4f2ffc9fcf31d0a72a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Apr 2022 16:17:29 +0930 Subject: [PATCH 0711/1530] lightningd: don't crash listpeers if we're opening DF channel. We call out to connectd to activate the peer, and while we do that, channel->owner is NULL. A better pattern would be to set up the unsaved channel once connectd has given us the peer, but this works for now. Fixes: #5204 Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 5 +++++ tests/test_connection.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index d7620dfce82c..d8ac1264396c 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -120,6 +120,11 @@ void json_add_unsaved_channel(struct json_stream *response, if (!channel->open_attempt) return; + /* If we're calling out to connectd to activate peer to start the + * process, this will be NULL */ + if (!channel->owner) + return; + oa = channel->open_attempt; json_object_start(response, NULL); diff --git a/tests/test_connection.py b/tests/test_connection.py index 0351f1dd1b75..537b9ec3c70d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1798,7 +1798,7 @@ def test_multifunding_simple(node_factory, bitcoind): l1.rpc.pay(inv) -@pytest.mark.xfail(strict=True) +@pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_listpeers_crash(node_factory, bitcoind, executor): ''' From be2523ec63ff20aee4b13bb7b7b00777a88f6749 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Apr 2022 20:47:48 +0930 Subject: [PATCH 0712/1530] CHANGELOG: rc5 Signed-off-by: Rusty Russell --- CHANGELOG.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19a8770a4a2c..ef4409beeb52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [v0.11.0rc4] - 2022-04-04: Simon's Carefully Chosen Release Name +## [v0.11.0rc5] - 2022-04-04: Simon's Carefully Chosen Release Name -This release named by Simon Vrouwe; this marks the name change to core-lightning (#CLN). +This release would have been named by Simon Vrouwe, had he responded to my emails! + +This marks the name change to core-lightning (#CLN). ### Added From 27352d0a7be31f608639ea862fa1ac209ac50c48 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Apr 2022 13:52:51 +0930 Subject: [PATCH 0713/1530] CHANGELOG.md: Looks like rc5 was the ticket! Signed-off-by: Rusty Russell --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef4409beeb52..1f904860ed70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [v0.11.0rc5] - 2022-04-04: Simon's Carefully Chosen Release Name +## [v0.11.0] - 2022-04-04: Simon's Carefully Chosen Release Name This release would have been named by Simon Vrouwe, had he responded to my emails! From f5df69020d249ab592a2e896c2b8be50da72856a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Apr 2022 19:42:12 +0930 Subject: [PATCH 0714/1530] v0.11.0.1: final fixes for release build Signed-off-by: Rusty Russell --- CHANGELOG.md | 2 +- tools/build-release.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f904860ed70..a7aa6a9dec7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [v0.11.0] - 2022-04-04: Simon's Carefully Chosen Release Name +## [0.11.0.1] - 2022-04-04: Simon's Carefully Chosen Release Name This release would have been named by Simon Vrouwe, had he responded to my emails! diff --git a/tools/build-release.sh b/tools/build-release.sh index bd06d411949a..77c5e3e9e22b 100755 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -80,11 +80,11 @@ for target in $TARGETS; do [ "$platform" != "$target" ] || continue case $platform in Fedora-28-amd64) - DOCKERFILE=contrib/Dockerfile.builder.fedora + DOCKERFILE=contrib/docker/Dockerfile.builder.fedora TAG=fedora ;; Ubuntu-16.04-amd64) - DOCKERFILE=contrib/Dockerfile.builder + DOCKERFILE=contrib/docker/Dockerfile.builder TAG=ubuntu-amd64 ;; *) From 0ec947f699b5ee540bcd91d687941c628236a405 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Apr 2022 21:03:22 +0930 Subject: [PATCH 0715/1530] doc: fix getinfo non-ASCII chars for older mrkd. When building reproducible build for Bionic: ``` Traceback (most recent call last): File "/usr/local/bin/mrkd", line 8, in sys.exit(main()) File "/usr/local/lib/python3.6/dist-packages/mrkd/__init__.py", line 261, in main result = mistune.markdown(fp.read(), inline=inline, renderer=renderer) File "/usr/lib/python3.6/encodings/ascii.py", line 26, in decode return codecs.ascii_decode(input, self.errors)[0] UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1856: ordinal not in range(128) doc/Makefile:120: recipe for target 'doc/lightning-getinfo.7' failed make: *** [doc/lightning-getinfo.7] Error 1 ``` Signed-off-by: Rusty Russell --- doc/lightning-getinfo.7.md | 4 ++-- doc/schemas/getinfo.schema.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 75921d06f7e1..8330ee0052b1 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -42,7 +42,7 @@ On success, an object is returned, containing: - **our_features** (object, optional): Our BOLT #9 feature bits (as hexstring) for various contexts: - **init** (hex): features (incl. globalfeatures) in our init message, these also restrict what we offer in open_channel or accept in accept_channel - **node** (hex): features in our node_announcement message - - **channel** (hex): negotiated channel features we — as channel initiator — publish in the channel_announcement message + - **channel** (hex): negotiated channel features we (as channel initiator) publish in the channel_announcement message - **invoice** (hex): features in our BOLT11 invoices - **address** (array of objects, optional): The addresses we announce to the world: - **type** (string): Type of connection (one of "dns", "ipv4", "ipv6", "torv2", "torv3", "websocket") @@ -128,4 +128,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:f7c85915ae8da7e9cabd2c72a31157801524b53f6bf8463da1743a0ee71e7c88) +[comment]: # ( SHA256STAMP:52175b0eced3ed2992d27b983b50afe080134702874116ce0c8355a8adddd440) diff --git a/doc/schemas/getinfo.schema.json b/doc/schemas/getinfo.schema.json index 2ddbfc3553aa..21001d0c78dd 100644 --- a/doc/schemas/getinfo.schema.json +++ b/doc/schemas/getinfo.schema.json @@ -77,7 +77,7 @@ }, "channel": { "type": "hex", - "description": "negotiated channel features we — as channel initiator — publish in the channel_announcement message" + "description": "negotiated channel features we (as channel initiator) publish in the channel_announcement message" }, "invoice": { "type": "hex", From 182c900ceaec6d6544b1e4970b3f03e348a9778e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 25 Apr 2022 18:22:14 +0200 Subject: [PATCH 0716/1530] repro: Add reprobuild support for ubuntu:22.04 --- tools/repro-build.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tools/repro-build.sh b/tools/repro-build.sh index d160b9c18892..1f88ecd1f8cc 100755 --- a/tools/repro-build.sh +++ b/tools/repro-build.sh @@ -133,6 +133,34 @@ a7d59420134a8307eb11ef79b68e2b35cadc794a60f82c87f4583e37c763fd01 /var/cache/apt 1ffa955ebb58829f3ab0debf7ad57b150887f6a44769edbaef68b8da9d95f306 /var/cache/apt/archives/m4_1.4.18-4_amd64.deb 41e534af98cdb6219bc98fa4276d9c928a0862b8b373d49ee1fbe0ae5db64dc2 /var/cache/apt/archives/make_4.2.1-1.2_amd64.deb 9cd69c847d7b12bd9cb2c58afe8bd17fb3973361716af71eb45c0f2b6d7e6884 /var/cache/apt/archives/zlib1g-dev_1%3a1.2.11.dfsg-2ubuntu1_amd64.deb +EOF + ;; + Ubuntu-22.04) + if grep ^deb /etc/apt/sources.list | grep -- '-\(updates\|security\)'; then + echo Please disable security and updates in /etc/apt/sources.list >&2 + exit 1 + fi + DOWNLOAD='sudo apt -y --no-install-recommends --reinstall -d install' + PKGS='autoconf automake libtool make gcc libgmp-dev libsqlite3-dev zlib1g-dev libsodium-dev' + INST='sudo dpkg -i' + cat > /tmp/SHASUMS < Date: Mon, 25 Apr 2022 18:23:18 +0200 Subject: [PATCH 0717/1530] repro: Update repro dockerfiles and instructions Switching to poetry, and deprecating python 3.6, made things a bit more tricky. Sadly we'll not be able to build jammy, as its support is missing in the tag tarball, but it'll be there for the next release. --- contrib/reprobuild/Dockerfile.bionic | 64 +++++++++++++++++----------- contrib/reprobuild/Dockerfile.focal | 9 ++-- contrib/reprobuild/Dockerfile.jammy | 47 ++++++++++++++++++++ doc/REPRODUCIBLE.md | 28 ++++++------ 4 files changed, 105 insertions(+), 43 deletions(-) create mode 100644 contrib/reprobuild/Dockerfile.jammy diff --git a/contrib/reprobuild/Dockerfile.bionic b/contrib/reprobuild/Dockerfile.bionic index d0d13715c867..f01857481bf7 100644 --- a/contrib/reprobuild/Dockerfile.bionic +++ b/contrib/reprobuild/Dockerfile.bionic @@ -7,36 +7,52 @@ RUN sed -i '/updates/d' /etc/apt/sources.list && \ sed -i '/security/d' /etc/apt/sources.list RUN apt-get update \ - && apt-get install -y --no-install-recommends \ + && apt-get install -y --no-install-recommends \ ca-certificates \ - sudo \ - build-essential \ - libsodium23 \ - python3-setuptools \ - libpq-dev \ - git \ - file \ - autoconf \ - debianutils \ - gettext \ - zip \ - unzip \ - wget + sudo \ + build-essential \ + libsodium23 \ + libpq-dev \ + git \ + file \ + autoconf \ + debianutils \ + gettext \ + zip \ + unzip \ + wget + +# Need to fetch a python version that is >= 3.7 since that's the +# lowest version supported by pyln. This is just temporary until we +# drop support for ubuntu 18.04 +ENV PATH=/root/.pyenv/shims:/root/.pyenv/bin:$PATH +RUN git clone https://github.com/pyenv/pyenv.git /root/.pyenv \ + && apt-get install -y --no-install-recommends \ + libbz2-dev \ + libffi-dev \ + libreadline-dev \ + libsqlite3-dev \ + libssl-dev \ + zlib1g-dev \ + && pyenv install 3.7.0 \ + && pyenv global 3.7.0 RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp/get-pip.py \ && rm /tmp/get-pip.py \ - && pip install mrkd mako + && pip install poetry RUN mkdir /build WORKDIR /build CMD git clone /repo /build \ - && tools/build-release.sh zipfile \ - && mkdir -p /repro \ - && cd /repro \ - && unzip /build/release/*.zip \ - && cd clightning* \ - && tools/repro-build.sh \ - && cp *.xz /build/release/* /repo/release/ \ - && cd /repo/release \ - && sha256sum * + && poetry export -o requirements.txt --without-hashes \ + && pip install -r requirements.txt\ + && tools/build-release.sh zipfile \ + && mkdir -p /repro \ + && cd /repro \ + && unzip /build/release/*.zip \ + && cd clightning* \ + && tools/repro-build.sh \ + && cp *.xz /build/release/* /repo/release/ \ + && cd /repo/release \ + && sha256sum * diff --git a/contrib/reprobuild/Dockerfile.focal b/contrib/reprobuild/Dockerfile.focal index a58e44a5f95b..774295a42b4d 100644 --- a/contrib/reprobuild/Dockerfile.focal +++ b/contrib/reprobuild/Dockerfile.focal @@ -26,19 +26,22 @@ RUN apt-get update \ zip RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp/get-pip.py \ - && rm /tmp/get-pip.py \ - && pip install mrkd mako + && rm /tmp/get-pip.py \ + && pip install poetry RUN mkdir /build WORKDIR /build CMD git clone /repo /build \ + && poetry export -o requirements.txt --without-hashes \ + && pip install -r requirements.txt \ && tools/build-release.sh zipfile \ && mkdir -p /repro \ && cd /repro \ && unzip /build/release/*.zip \ && cd clightning* \ - && tools/repro-build.sh \ + && tools/repro-build.sh \ + && mkdir -p /repo/release \ && cp *.xz /build/release/* /repo/release/ \ && cd /repo/release/ \ && sha256sum * diff --git a/contrib/reprobuild/Dockerfile.jammy b/contrib/reprobuild/Dockerfile.jammy new file mode 100644 index 000000000000..a935938a4906 --- /dev/null +++ b/contrib/reprobuild/Dockerfile.jammy @@ -0,0 +1,47 @@ +FROM jammy + +ENV TZ=UTC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN sed -i '/updates/d' /etc/apt/sources.list && \ + sed -i '/security/d' /etc/apt/sources.list + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + autoconf \ + build-essential \ + ca-certificates \ + file \ + gettext \ + git \ + libgmp-dev \ + libpq-dev \ + libsodium23 \ + libtool \ + m4 \ + python3-setuptools \ + sudo \ + unzip \ + wget \ + zip + +RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp/get-pip.py \ + && rm /tmp/get-pip.py \ + && pip install poetry + +RUN mkdir /build +WORKDIR /build + +CMD git clone /repo /build \ + && poetry export -o requirements.txt --without-hashes \ + && pip install -r requirements.txt \ + && tools/build-release.sh zipfile \ + && mkdir -p /repro \ + && cd /repro \ + && unzip /build/release/*.zip \ + && cd clightning* \ + && tools/repro-build.sh \ + && mkdir -p /repo/release \ + && cp *.xz /build/release/* /repo/release/ \ + && cd /repo/release/ \ + && sha256sum * diff --git a/doc/REPRODUCIBLE.md b/doc/REPRODUCIBLE.md index 55b94af16b48..1d2daf9b1b79 100644 --- a/doc/REPRODUCIBLE.md +++ b/doc/REPRODUCIBLE.md @@ -66,30 +66,26 @@ latter means that if we disable the `updates` and `security` repositories for packages (wrongly updated packages depending on the versions not available in the non-updated repos). -The following will create that base image: - -```bash -sudo debootstrap bionic bionic -sudo tar -C bionic -c . | sudo docker import - bionic -``` - -`bionic` in this case is the codename for Ubuntu 18.04 LTS. The following -table lists the codenames of distributions that we currently support: +The following table lists the codenames of distributions that we +currently support: | Distribution Version | Codename | |----------------------|----------| | Ubuntu 18.04 | bionic | | Ubuntu 20.04 | focal | +| Ubuntu 22.04 | jammy | -Notice that you migh not have `debootstrap` manifests for versions newer than -your host OS. If one of the `debootstrap` command above complains about a -script not existing you might need to run `debootstrap` in a docker container -itself: +Depending on your host OS release you migh not have `debootstrap` +manifests for versions newer than your host OS. Due to this we run the +`debootstrap` commands in a container of the latest version itself: ```bash -sudo docker run --rm -v $(pwd):/build ubuntu:20.04 \ - bash -c "apt update && apt-get install -y debootstrap && debootstrap focal /build/focal" -sudo tar -C focal -c . | sudo docker import - focal +for v in bionic focal jammy; do + echo "Building base image for $v" + sudo docker run --rm -v $(pwd):/build ubuntu:22.04 \ + bash -c "apt-get update && apt-get install -y debootstrap && debootstrap $v /build/$v" + sudo tar -C $v -c . | sudo docker import - $v +done ``` Verify that the image corresponds to our expectation and is runnable: From 6ced5558fb7c817375112072d8f670b878356ddd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Apr 2022 13:25:47 +0930 Subject: [PATCH 0718/1530] Makefile: stumble along if git does not work. In particular, it's started complaining about "sudo make install" and the .git directory being owned by someone else :( Fixes: #5221 Fixes: #5189 Signed-off-by: Rusty Russell --- Makefile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 737f26641133..8eea1ec2aeab 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,6 @@ # Extract version from git, or if we're from a zipfile, use dirname VERSION=$(shell git describe --always --dirty=-modded --abbrev=7 2>/dev/null || pwd | sed -n 's|.*/c\{0,1\}lightning-v\{0,1\}\([0-9a-f.rc\-]*\)$$|\1|gp') -ifeq ($(VERSION),) -$(error "ERROR: git is required for generating version information") -endif - # --quiet / -s means quiet, dammit! ifeq ($(findstring s,$(word 1, $(MAKEFLAGS))),s) ECHO := : @@ -583,9 +579,17 @@ ALL_PROGRAMS += ccan/ccan/cdump/tools/cdump-enumstr # Can't add to ALL_OBJS, as that makes a circular dep. ccan/ccan/cdump/tools/cdump-enumstr.o: $(CCAN_HEADERS) Makefile +# Without a working git, you can't generate this file, so assume if it exists +# it is ok (fixes "sudo make install"). +ifeq ($(VERSION),) +version_gen.h: + echo "ERROR: git is required for generating version information" >&2 + exit 1 +else version_gen.h: $(FORCE) @(echo "#define VERSION \"$(VERSION)\"" && echo "#define BUILD_FEATURES \"$(FEATURES)\"") > $@.new @if cmp $@.new $@ >/dev/null 2>&1; then rm -f $@.new; else mv $@.new $@; $(ECHO) Version updated; fi +endif # That forces this rule to be run every time, too. header_versions_gen.h: tools/headerversions @@ -790,11 +794,13 @@ installcheck: all-programs installcheck ncc bin-tarball show-flags # Make a tarball of opt/clightning/, optionally with label for distribution. +ifneq ($(VERSION),) bin-tarball: clightning-$(VERSION)-$(DISTRO).tar.xz clightning-$(VERSION)-$(DISTRO).tar.xz: DESTDIR=$(shell pwd)/ clightning-$(VERSION)-$(DISTRO).tar.xz: prefix=opt/clightning clightning-$(VERSION)-$(DISTRO).tar.xz: install trap "rm -rf opt" 0; tar cvfa $@ opt/ +endif ccan-breakpoint.o: $(CCANDIR)/ccan/breakpoint/breakpoint.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) From cc9bdb82986198a5280d5df7f8246309c3ce7dc2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 25 Apr 2022 14:38:41 +0200 Subject: [PATCH 0719/1530] pyln: Update the makefile to use poetry for publishing --- contrib/pyln-client/.gitignore | 2 ++ contrib/pyln-client/Makefile | 25 +++++++++++++++---------- contrib/pyln-client/pyproject.toml | 4 ++-- contrib/pyln-proto/.gitignore | 1 + contrib/pyln-proto/Makefile | 26 +++++++++++++++----------- contrib/pyln-proto/pyproject.toml | 2 +- contrib/pyln-testing/.gitignore | 1 + contrib/pyln-testing/Makefile | 25 +++++++++++++++---------- contrib/pyln-testing/pyproject.toml | 2 +- 9 files changed, 53 insertions(+), 35 deletions(-) diff --git a/contrib/pyln-client/.gitignore b/contrib/pyln-client/.gitignore index c04bc49f76fa..e952f7896aac 100644 --- a/contrib/pyln-client/.gitignore +++ b/contrib/pyln-client/.gitignore @@ -1 +1,3 @@ poetry.lock +requirements.txt + diff --git a/contrib/pyln-client/Makefile b/contrib/pyln-client/Makefile index f9250900e5fb..33bb6722aaa3 100644 --- a/contrib/pyln-client/Makefile +++ b/contrib/pyln-client/Makefile @@ -19,30 +19,35 @@ check: check-source check-pytest check-source: check-flake8 check-mypy check-flake8: - flake8 --ignore=E501,E731,W503,E741 + flake8 --ignore=E501,E731,W503,E741 pyln tests check-pytest: pytest tests check-mypy: - MYPYPATH=$(PYTHONPATH) mypy --namespace-packages --follow-imports=skip tests pyln +# MYPYPATH=$(PYTHONPATH) mypy --namespace-packages --follow-imports=skip tests pyln -$(SDIST_FILE): - python3 setup.py sdist +pyproject.toml: pyln/${PKG}/__init__.py + poetry version ${VERSION} -$(BDIST_FILE): - python3 setup.py bdist_wheel +$(SDIST_FILE) $(BDIST_FILE): pyproject.toml + poetry build -test-release: check $(ARTEFACTS) - python3 -m twine upload --repository testpypi --skip-existing $(ARTEFACTS) +test-release: check $(ARTEFACTS) pyproject.toml + # No way of saying "it's ok if files exist" yet + poetry publish --repository testpypi || /bin/true + echo Sleeping for PyPI index to update + sleep 10 + + # Generate a requirements.txt file, needed for us to download requirements from the prod pypi instead of the test pypi, since some packages are not published to test pypi. + poetry export -f requirements.txt --output requirements.txt --without-hashes # Create a test virtualenv, install from the testpypi and run the # tests against it (make sure not to use any virtualenv that may have # pyln-${PKG} already installed). virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - # Install the requirements from the prod repo, they are not being kept up to date on the test repo testpypi/bin/python3 -m pip install -r requirements.txt flaky pytest-timeout - testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG} + testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" testpypi/bin/pytest tests rm -rf testpypi diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index 0ca9b6e714fb..c47607ad7efb 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-client" -version = "0.10.2.post1" +version = "0.11.0" description = "Client library and plugin library for Core Lightning" authors = ["Christian Decker "] license = "BSD-MIT" @@ -12,7 +12,7 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -pyln-bolt7 = "^1.0.186" +pyln-bolt7 = "^1.0" pyln-proto = "^0.10.2" [tool.poetry.dev-dependencies] diff --git a/contrib/pyln-proto/.gitignore b/contrib/pyln-proto/.gitignore index c04bc49f76fa..63bc0e1dd588 100644 --- a/contrib/pyln-proto/.gitignore +++ b/contrib/pyln-proto/.gitignore @@ -1 +1,2 @@ poetry.lock +requirements.txt \ No newline at end of file diff --git a/contrib/pyln-proto/Makefile b/contrib/pyln-proto/Makefile index ba79c3b4371f..a7ddbba6366c 100644 --- a/contrib/pyln-proto/Makefile +++ b/contrib/pyln-proto/Makefile @@ -19,31 +19,35 @@ check: check-source check-pytest check-source: check-flake8 check-mypy check-flake8: - flake8 --ignore=E501,E731,W503,E741 + flake8 --ignore=E501,E731,W503,E741 pyln tests check-pytest: pytest tests check-mypy: - #mypy --namespace-packages tests pyln - echo mypy disabled for pyln-proto +# MYPYPATH=$(PYTHONPATH) mypy --namespace-packages --follow-imports=skip tests pyln -$(SDIST_FILE): - python3 setup.py sdist +pyproject.toml: pyln/${PKG}/__init__.py + poetry version ${VERSION} -$(BDIST_FILE): - python3 setup.py bdist_wheel +$(SDIST_FILE) $(BDIST_FILE): pyproject.toml + poetry build -test-release: check $(ARTEFACTS) - python3 -m twine upload --repository testpypi --skip-existing $(ARTEFACTS) +test-release: check $(ARTEFACTS) pyproject.toml + # No way of saying "it's ok if files exist" yet + poetry publish --repository testpypi || /bin/true + echo Sleeping for PyPI index to update + sleep 10 + + # Generate a requirements.txt file, needed for us to download requirements from the prod pypi instead of the test pypi, since some packages are not published to test pypi. + poetry export -f requirements.txt --output requirements.txt --without-hashes # Create a test virtualenv, install from the testpypi and run the # tests against it (make sure not to use any virtualenv that may have # pyln-${PKG} already installed). virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - # Install the requirements from the prod repo, they are not being kept up to date on the test repo testpypi/bin/python3 -m pip install -r requirements.txt flaky pytest-timeout - testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG} + testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" testpypi/bin/pytest tests rm -rf testpypi diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml index c31c5ad4fb96..7ffa534761d8 100644 --- a/contrib/pyln-proto/pyproject.toml +++ b/contrib/pyln-proto/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-proto" -version = "0.10.2.post1" +version = "0.11.0" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." authors = ["Christian Decker "] license = "BSD-MIT" diff --git a/contrib/pyln-testing/.gitignore b/contrib/pyln-testing/.gitignore index c04bc49f76fa..c2d85e430a13 100644 --- a/contrib/pyln-testing/.gitignore +++ b/contrib/pyln-testing/.gitignore @@ -1 +1,2 @@ poetry.lock +requirements.txt diff --git a/contrib/pyln-testing/Makefile b/contrib/pyln-testing/Makefile index 23ea81c248bc..012875cb4b45 100644 --- a/contrib/pyln-testing/Makefile +++ b/contrib/pyln-testing/Makefile @@ -19,30 +19,35 @@ check: check-source check-pytest check-source: check-flake8 check-mypy check-flake8: - flake8 --ignore=E501,E731,W503,E741 + flake8 --ignore=E501,E731,W503,E741 pyln tests check-pytest: pytest tests check-mypy: - MYPYPATH=$(PYTHONPATH) mypy --namespace-packages --follow-imports=skip tests pyln +# MYPYPATH=$(PYTHONPATH) mypy --namespace-packages --follow-imports=skip tests pyln -$(SDIST_FILE): - python3 setup.py sdist +pyproject.toml: pyln/${PKG}/__init__.py + poetry version ${VERSION} -$(BDIST_FILE): - python3 setup.py bdist_wheel +$(SDIST_FILE) $(BDIST_FILE): pyproject.toml + poetry build -test-release: check $(ARTEFACTS) - python3 -m twine upload --repository testpypi --skip-existing $(ARTEFACTS) +test-release: check $(ARTEFACTS) pyproject.toml + # No way of saying "it's ok if files exist" yet + poetry publish --repository testpypi || /bin/true + echo Sleeping for PyPI index to update + sleep 10 + + # Generate a requirements.txt file, needed for us to download requirements from the prod pypi instead of the test pypi, since some packages are not published to test pypi. + poetry export -f requirements.txt --output requirements.txt --without-hashes # Create a test virtualenv, install from the testpypi and run the # tests against it (make sure not to use any virtualenv that may have # pyln-${PKG} already installed). virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - # Install the requirements from the prod repo, they are not being kept up to date on the test repo testpypi/bin/python3 -m pip install -r requirements.txt flaky pytest-timeout - testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG} + testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" testpypi/bin/pytest tests rm -rf testpypi diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index 6b2e5304b146..d21ea10c6183 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-testing" -version = "0.10.2" +version = "0.11.0" description = "Test your Core Lightning integration, plugins or whatever you want" authors = ["Christian Decker "] license = "BSD-MIT" From 55b5653726a5a195a83a89c2aef5fbc577c2f4bd Mon Sep 17 00:00:00 2001 From: Clay Shoaf Date: Thu, 21 Apr 2022 19:55:32 -0400 Subject: [PATCH 0720/1530] Slight problem with `./configure help` --- configure | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure b/configure index a0fd5d805062..f5603f682e66 100755 --- a/configure +++ b/configure @@ -177,8 +177,9 @@ usage() usage_with_default "--enable/disable-ub-sanitizer" "$UBSAN" "enable" "disable" echo " Compile with undefined behaviour sanitizer" usage_with_default "--enable/disable-fuzzing" "$FUZZING" "enable" "disable" - echo " Compile with Rust support" + echo " ***something about fuzzing***" usage_with_default "--enable/disable-rust" "$RUST" "enable" "disable" + echo " Compile with Rust support" exit 1 } From 43ace0368598fe905ceb05e7e1fc07ec4057d265 Mon Sep 17 00:00:00 2001 From: Clay Shoaf Date: Fri, 22 Apr 2022 10:50:27 -0400 Subject: [PATCH 0721/1530] "removed asterisks, in case that's what made build fail" --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index f5603f682e66..8fdf93ea5fa2 100755 --- a/configure +++ b/configure @@ -177,7 +177,7 @@ usage() usage_with_default "--enable/disable-ub-sanitizer" "$UBSAN" "enable" "disable" echo " Compile with undefined behaviour sanitizer" usage_with_default "--enable/disable-fuzzing" "$FUZZING" "enable" "disable" - echo " ***something about fuzzing***" + echo " Compile with fuzzing" usage_with_default "--enable/disable-rust" "$RUST" "enable" "disable" echo " Compile with Rust support" exit 1 From e516a5dffe902093af7c3c16c8ad6f950d4cd247 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 1 May 2022 14:18:27 +0930 Subject: [PATCH 0722/1530] external/Makefile: fix `submodcheck` for sudo make install and modern git. We can't run refresh-submodules without a working git, either: ``` $ sudo make install mkdir -p /usr/local/bin mkdir -p /usr/local/libexec/c-lightning mkdir -p /usr/local/libexec/c-lightning/plugins mkdir -p /usr/local/share/man/man1 mkdir -p /usr/local/share/man/man5 mkdir -p /usr/local/share/man/man7 mkdir -p /usr/local/share/man/man8 mkdir -p /usr/local/share/doc/c-lightning fatal: unsafe repository ('/home/rusty/lightning' is owned by someone else) To add an exception for this directory, call: git config --global --add safe.directory /home/rusty/lightning Reinitializing submodules external/libsodium external/libwally-core external/gheap external/jsmn external/libbacktrace external/lnprototest ... fatal: unsafe repository ('/home/rusty/lightning' is owned by someone else) To add an exception for this directory, call: git config --global --add safe.directory /home/rusty/lightning fatal: unsafe repository ('/home/rusty/lightning' is owned by someone else) To add an exception for this directory, call: git config --global --add safe.directory /home/rusty/lightning make: *** [external/Makefile:52: submodcheck] Error 128 ``` Signed-off-by: Rusty Russell --- external/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/external/Makefile b/external/Makefile index 975d4d259041..92e976092a09 100644 --- a/external/Makefile +++ b/external/Makefile @@ -49,8 +49,10 @@ endif EXTERNAL_LDLIBS := -L${TARGET_DIR} $(patsubst lib%.a,-l%,$(notdir $(EXTERNAL_LIBS))) submodcheck: $(FORCE) +ifneq ($(VERSION),) @tools/refresh-submodules.sh $(SUBMODULES) @cd external/libwally-core && ../../tools/refresh-submodules.sh src/secp256k1 +endif $(EXTERNAL_HEADERS): submodcheck From 80db867a30adce2dc7973d1f1533f5ce9fc795ad Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Mon, 2 May 2022 13:37:32 -0500 Subject: [PATCH 0723/1530] pytest: websocket test can tolerate ipv6 address test_connection.py::test_websocket no longer fails on presence of ipv6 address. --- tests/test_connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 537b9ec3c70d..4c4a5ced8c43 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3816,8 +3816,8 @@ def recv(self, maxlen): break # Check node_announcement has websocket - assert (only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['addresses'] - == [{'type': 'ipv4', 'address': '127.0.0.1', 'port': port2}, {'type': 'websocket', 'port': ws_port}]) + ws_address = {'type': 'websocket', 'port': ws_port} + assert ws_address in only_one(l2.rpc.listnodes(l1.info['id'])['nodes'])['addresses'] @pytest.mark.developer("dev-disconnect required") From 4e902fbd883e710d1324c8c0870b5d15c0d1db0f Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 24 Apr 2022 14:34:33 +0200 Subject: [PATCH 0724/1530] msggen: introduce chain of responsibility pattern to make msggen extensible Changelog-Added: msggen: introduce chain of responsibility pattern to make msggen extensible Signed-off-by: Vincenzo Palazzo --- contrib/msggen/msggen/__main__.py | 153 +++--------------------- contrib/msggen/msggen/gen/__init__.py | 3 + contrib/msggen/msggen/gen/generator.py | 36 ++++++ contrib/msggen/msggen/{ => gen}/grpc.py | 7 +- contrib/msggen/msggen/{ => gen}/rust.py | 7 +- contrib/msggen/msggen/utils/__init__.py | 1 + contrib/msggen/msggen/utils/utils.py | 129 ++++++++++++++++++++ 7 files changed, 195 insertions(+), 141 deletions(-) create mode 100644 contrib/msggen/msggen/gen/__init__.py create mode 100644 contrib/msggen/msggen/gen/generator.py rename contrib/msggen/msggen/{ => gen}/grpc.py (98%) rename contrib/msggen/msggen/{ => gen}/rust.py (97%) create mode 100644 contrib/msggen/msggen/utils/__init__.py create mode 100644 contrib/msggen/msggen/utils/utils.py diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index e423ac5683f6..e04b37c99c80 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,153 +1,31 @@ -from msggen.model import Method, CompositeField, Service -from msggen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator -from msggen.rust import RustGenerator -from pathlib import Path -import subprocess import json +from msggen.gen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator +from msggen.gen.rust import RustGenerator +from msggen.gen.generator import GeneratorChain +from msggen.utils import repo_root, load_jsonrpc_service -# Sometimes we want to rename a method, due to a name clash -method_name_override = { - "Connect": "ConnectPeer", -} - - -def repo_root(): - path = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) - return Path(path.strip().decode('UTF-8')) - - -def load_jsonrpc_method(name): - """Load a method based on the file naming conventions for the JSON-RPC. - """ - base_path = (repo_root() / "doc" / "schemas").resolve() - req_file = base_path / f"{name.lower()}.request.json" - resp_file = base_path / f"{name.lower()}.schema.json" - request = CompositeField.from_js(json.load(open(req_file)), path=name) - response = CompositeField.from_js(json.load(open(resp_file)), path=name) - - # Normalize the method request and response typename so they no - # longer conflict. - request.typename += "Request" - response.typename += "Response" - - return Method( - name=method_name_override.get(name, name), - request=request, - response=response, - ) - - -def load_jsonrpc_service(): - method_names = [ - "Getinfo", - "ListPeers", - "ListFunds", - "SendPay", - "ListChannels", - "AddGossip", - "AutoCleanInvoice", - "CheckMessage", - "Close", - "Connect", - "CreateInvoice", - "Datastore", - "CreateOnion", - "DelDatastore", - "DelExpiredInvoice", - "DelInvoice", - "Invoice", - "ListDatastore", - "ListInvoices", - "SendOnion", - "ListSendPays", - "ListTransactions", - "Pay", - "ListNodes", - "WaitAnyInvoice", - "WaitInvoice", - "WaitSendPay", - "NewAddr", - "Withdraw", - "KeySend", - "FundPsbt", - "SendPsbt", - "SignPsbt", - "UtxoPsbt", - "TxDiscard", - "TxPrepare", - "TxSend", - # "decodepay", - # "decode", - # "delpay", - # "disableoffer", - "Disconnect", - "Feerates", - # "fetchinvoice", - # "fundchannel_cancel", - # "fundchannel_complete", - # "fundchannel", - # "fundchannel_start", - # "funderupdate", - # "getlog", - "GetRoute", - # "getsharedsecret", - "ListForwards", - # "listoffers", - "ListPays", - # "multifundchannel", - # "multiwithdraw", - # "offerout", - # "offer", - # "openchannel_abort", - # "openchannel_bump", - # "openchannel_init", - # "openchannel_signed", - # "openchannel_update", - # "parsefeerate", - "Ping", - # "plugin", - # "reserveinputs", - # "sendcustommsg", - # "sendinvoice", - # "sendonionmessage", - # "setchannelfee", - "SignMessage", - # "unreserveinputs", - # "waitblockheight", - # "ListConfigs", - # "check", # No point in mapping this one - # "Stop", # Breaks a core assumption (root is an object) can't map unless we change this - # "notifications", # No point in mapping this - # "help", - ] - methods = [load_jsonrpc_method(name) for name in method_names] - service = Service(name="Node", methods=methods) - service.includes = ['primitives.proto'] # Make sure we have the primitives included. - return service - - -def gengrpc(service, meta): +def gengrpc(generator_chain: GeneratorChain, meta): """Load all mapped RPC methods, wrap them in a Service, and split them into messages. """ fname = repo_root() / "cln-grpc" / "proto" / "node.proto" dest = open(fname, "w") - GrpcGenerator(dest, meta).generate(service) + generator_chain.add_generator(GrpcGenerator(dest, meta)) fname = repo_root() / "cln-grpc" / "src" / "convert.rs" dest = open(fname, "w") - GrpcConverterGenerator(dest).generate(service) - GrpcUnconverterGenerator(dest).generate(service) + generator_chain.add_generator(GrpcConverterGenerator(dest)) + generator_chain.add_generator(GrpcUnconverterGenerator(dest)) fname = repo_root() / "cln-grpc" / "src" / "server.rs" dest = open(fname, "w") - GrpcServerGenerator(dest).generate(service) + generator_chain.add_generator(GrpcServerGenerator(dest)) -def genrustjsonrpc(service): +def genrustjsonrpc(generator_chain: GeneratorChain): fname = repo_root() / "cln-rpc" / "src" / "model.rs" dest = open(fname, "w") - RustGenerator(dest).generate(service) + generator_chain.add_generator(RustGenerator(dest)) def load_msggen_meta(): @@ -163,8 +41,13 @@ def write_msggen_meta(meta): def run(): service = load_jsonrpc_service() meta = load_msggen_meta() - gengrpc(service, meta) - genrustjsonrpc(service) + generator_chain = GeneratorChain() + + gengrpc(generator_chain, meta) + genrustjsonrpc(generator_chain) + + generator_chain.generate(service) + write_msggen_meta(meta) diff --git a/contrib/msggen/msggen/gen/__init__.py b/contrib/msggen/msggen/gen/__init__.py new file mode 100644 index 000000000000..107f713ffcb7 --- /dev/null +++ b/contrib/msggen/msggen/gen/__init__.py @@ -0,0 +1,3 @@ +from .generator import IGenerator, GeneratorChain # noqa +from .grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator # noqa +from .rust import RustGenerator # noqa diff --git a/contrib/msggen/msggen/gen/generator.py b/contrib/msggen/msggen/gen/generator.py new file mode 100644 index 000000000000..e4123ce19d81 --- /dev/null +++ b/contrib/msggen/msggen/gen/generator.py @@ -0,0 +1,36 @@ +""" +Generator interface! + +author: https://github.com/vincenzopalazzo +""" +from abc import ABC, abstractmethod + +from msggen.model import Service + + +class IGenerator(ABC): + """ + Change of responsibility handler that need to be + implemented by all the generators. + """ + + @abstractmethod + def generate(self, service: Service): + pass + + +class GeneratorChain: + """ + Chain responsibility patter implementation to generalize + the generation method. + """ + + def __init__(self): + self.generators = [] + + def add_generator(self, generator: IGenerator) -> None: + self.generators.append(generator) + + def generate(self, service: Service) -> None: + for _, generator in enumerate(self.generators): + generator.generate(service) diff --git a/contrib/msggen/msggen/grpc.py b/contrib/msggen/msggen/gen/grpc.py similarity index 98% rename from contrib/msggen/msggen/grpc.py rename to contrib/msggen/msggen/gen/grpc.py index 56317f71e2ed..4d523a313b00 100644 --- a/contrib/msggen/msggen/grpc.py +++ b/contrib/msggen/msggen/gen/grpc.py @@ -1,5 +1,6 @@ # A grpc model -from .model import ArrayField, Field, CompositeField, EnumField, PrimitiveField, Service +from msggen.model import ArrayField, Field, CompositeField, EnumField, PrimitiveField, Service +from msggen.gen import IGenerator from typing import TextIO, List, Dict, Any from textwrap import indent, dedent import re @@ -51,7 +52,7 @@ } -class GrpcGenerator: +class GrpcGenerator(IGenerator): """A generator that generates protobuf files. """ @@ -235,7 +236,7 @@ def generate(self, service: Service) -> None: self.generate_message(message) -class GrpcConverterGenerator: +class GrpcConverterGenerator(IGenerator): def __init__(self, dest: TextIO): self.dest = dest self.logger = logging.getLogger("msggen.grpc.GrpcConversionGenerator") diff --git a/contrib/msggen/msggen/rust.py b/contrib/msggen/msggen/gen/rust.py similarity index 97% rename from contrib/msggen/msggen/rust.py rename to contrib/msggen/msggen/gen/rust.py index 82d25cb9fb7b..2fbe3278fdf1 100644 --- a/contrib/msggen/msggen/rust.py +++ b/contrib/msggen/msggen/gen/rust.py @@ -5,8 +5,9 @@ import sys import re -from .model import (ArrayField, CompositeField, EnumField, - PrimitiveField, Service) +from msggen.model import (ArrayField, CompositeField, EnumField, + PrimitiveField, Service) +from msggen.gen.generator import IGenerator logger = logging.getLogger(__name__) @@ -196,7 +197,7 @@ def gen_composite(c) -> Tuple[str, str]: return ("", r) -class RustGenerator: +class RustGenerator(IGenerator): def __init__(self, dest: TextIO): self.dest = dest diff --git a/contrib/msggen/msggen/utils/__init__.py b/contrib/msggen/msggen/utils/__init__.py new file mode 100644 index 000000000000..eaa1c1aff90d --- /dev/null +++ b/contrib/msggen/msggen/utils/__init__.py @@ -0,0 +1 @@ +from .utils import load_jsonrpc_method, load_jsonrpc_service, repo_root # noqa diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py new file mode 100644 index 000000000000..59586cd34a12 --- /dev/null +++ b/contrib/msggen/msggen/utils/utils.py @@ -0,0 +1,129 @@ +import subprocess +import json +from pathlib import Path + +from msggen.model import Method, CompositeField, Service + +# Sometimes we want to rename a method, due to a name clash +# FIXME: need to be generalized? +method_name_override = { + "Connect": "ConnectPeer", +} + + +def repo_root(): + path = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) + return Path(path.strip().decode('UTF-8')) + + +def load_jsonrpc_method(name, schema_dir: str = None): + """Load a method based on the file naming conventions for the JSON-RPC. + """ + if schema_dir is None: + base_path = (repo_root() / "doc" / "schemas").resolve() + else: + base_path = schema_dir + req_file = base_path / f"{name.lower()}.request.json" + resp_file = base_path / f"{name.lower()}.schema.json" + request = CompositeField.from_js(json.load(open(req_file)), path=name) + response = CompositeField.from_js(json.load(open(resp_file)), path=name) + + # Normalize the method request and response typename so they no + # longer conflict. + request.typename += "Request" + response.typename += "Response" + + return Method( + name=method_name_override.get(name, name), + request=request, + response=response, + ) + + +def load_jsonrpc_service(schema_dir: str = None): + method_names = [ + "Getinfo", + "ListPeers", + "ListFunds", + "SendPay", + "ListChannels", + "AddGossip", + "AutoCleanInvoice", + "CheckMessage", + "Close", + "Connect", + "CreateInvoice", + "Datastore", + "CreateOnion", + "DelDatastore", + "DelExpiredInvoice", + "DelInvoice", + "Invoice", + "ListDatastore", + "ListInvoices", + "SendOnion", + "ListSendPays", + "ListTransactions", + "Pay", + "ListNodes", + "WaitAnyInvoice", + "WaitInvoice", + "WaitSendPay", + "NewAddr", + "Withdraw", + "KeySend", + "FundPsbt", + "SendPsbt", + "SignPsbt", + "UtxoPsbt", + "TxDiscard", + "TxPrepare", + "TxSend", + # "decodepay", + # "decode", + # "delpay", + # "disableoffer", + "Disconnect", + "Feerates", + # "fetchinvoice", + # "fundchannel_cancel", + # "fundchannel_complete", + # "fundchannel", + # "fundchannel_start", + # "funderupdate", + # "getlog", + "GetRoute", + # "getsharedsecret", + "ListForwards", + # "listoffers", + "ListPays", + # "multifundchannel", + # "multiwithdraw", + # "offerout", + # "offer", + # "openchannel_abort", + # "openchannel_bump", + # "openchannel_init", + # "openchannel_signed", + # "openchannel_update", + # "parsefeerate", + "Ping", + # "plugin", + # "reserveinputs", + # "sendcustommsg", + # "sendinvoice", + # "sendonionmessage", + # "setchannelfee", + "SignMessage", + # "unreserveinputs", + # "waitblockheight", + # "ListConfigs", + # "check", # No point in mapping this one + # "Stop", # Breaks a core assumption (root is an object) can't map unless we change this + # "notifications", # No point in mapping this + # "help", + ] + methods = [load_jsonrpc_method(name, schema_dir=schema_dir) for name in method_names] + service = Service(name="Node", methods=methods) + service.includes = ['primitives.proto'] # Make sure we have the primitives included. + return service From 2ab2061cc90d146072ad1dfe5083ac03f7cb3799 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 26 Apr 2022 11:18:04 +0200 Subject: [PATCH 0725/1530] msggen: adding example and fixes typo Signed-off-by: Vincenzo Palazzo --- contrib/msggen/examples/generator_example.py | 37 ++++++++++++++++++++ contrib/msggen/msggen/__main__.py | 8 ++--- contrib/msggen/msggen/gen/generator.py | 4 +-- 3 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 contrib/msggen/examples/generator_example.py diff --git a/contrib/msggen/examples/generator_example.py b/contrib/msggen/examples/generator_example.py new file mode 100644 index 000000000000..96466e0ac657 --- /dev/null +++ b/contrib/msggen/examples/generator_example.py @@ -0,0 +1,37 @@ +#! /usr/bin/python3 +""" +Example of usage msggen module. + +This example introduces a fake generator to understand how the +package works, If you would like to see a real generator example +try to see the Rust generator in the `msggen/gen/rust.py` + +author: https://github.com/vincenzopalazzo +""" +from msggen.gen.generator import GeneratorChain, IGenerator +from msggen import Service +from msggen.utils import load_jsonrpc_service + + +class MonkylangGen(IGenerator): + """This is the custom generator that implements a monkylang generator + that uses the interface handler IGenerator.""" + + def generate(self, service: Service): + self.write('println("Monky")') + + +def register_monkylang_gen(generator_chain: GeneratorChain): + """Helper function to register the custom generator, and + load the correct path of the json schema.""" + file = '' + dest = open(file, 'w') + generator_chain.add_generator(MonkylangGen(dest)) + + +if __name__ == '__main__': + schema_dir = '' + service = load_jsonrpc_service(schema_dir=schema_dir) + generator_chain = GeneratorChain() + register_monkylang_gen(generator_chain) + generator_chain.generate(service) diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index e04b37c99c80..1bb345dac999 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -5,7 +5,7 @@ from msggen.utils import repo_root, load_jsonrpc_service -def gengrpc(generator_chain: GeneratorChain, meta): +def add_handler_gen_grpc(generator_chain: GeneratorChain, meta): """Load all mapped RPC methods, wrap them in a Service, and split them into messages. """ fname = repo_root() / "cln-grpc" / "proto" / "node.proto" @@ -22,7 +22,7 @@ def gengrpc(generator_chain: GeneratorChain, meta): generator_chain.add_generator(GrpcServerGenerator(dest)) -def genrustjsonrpc(generator_chain: GeneratorChain): +def add_handler_gen_rust_jsonrpc(generator_chain: GeneratorChain): fname = repo_root() / "cln-rpc" / "src" / "model.rs" dest = open(fname, "w") generator_chain.add_generator(RustGenerator(dest)) @@ -43,8 +43,8 @@ def run(): meta = load_msggen_meta() generator_chain = GeneratorChain() - gengrpc(generator_chain, meta) - genrustjsonrpc(generator_chain) + add_handler_gen_grpc(generator_chain, meta) + add_handler_gen_rust_jsonrpc(generator_chain) generator_chain.generate(service) diff --git a/contrib/msggen/msggen/gen/generator.py b/contrib/msggen/msggen/gen/generator.py index e4123ce19d81..052ddcbb0f83 100644 --- a/contrib/msggen/msggen/gen/generator.py +++ b/contrib/msggen/msggen/gen/generator.py @@ -10,7 +10,7 @@ class IGenerator(ABC): """ - Change of responsibility handler that need to be + Chain of responsibility handler that need to be implemented by all the generators. """ @@ -21,7 +21,7 @@ def generate(self, service: Service): class GeneratorChain: """ - Chain responsibility patter implementation to generalize + Chain responsibility pattern implementation to generalize the generation method. """ From e7a3471af6357293a9f31614bb13d8b26cbffdb6 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 5 May 2022 16:06:06 +0200 Subject: [PATCH 0726/1530] gh: Remove @wythe from the codeowners For some reason Github complains that @wythe doesn't have access to the public repository, removing in order to silence that warning. Feel free to contact me to investigate what's happening and re-enable :-) Changelog-None --- .github/CODEOWNERS | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0b9e50e2c92b..d9e64d21d1fc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,10 +11,5 @@ wallet/invoices.* @ZmnSCPxj plugins/multifundchannel.c @ZmnSCPxj doc/BACKUP.md @ZmnSCPxj @cdecker -common/param.* @wythe -common/json.* @wythe -common/json_tok.* @wythe -common/wallet_tx.* @wythe - # See https://help.github.com/articles/about-codeowners/ for more # information From 2cf92acaa8fde4eead7684de7a7cbda0412016e3 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 5 May 2022 16:08:46 +0200 Subject: [PATCH 0727/1530] gh: Add @cdecker as codeowner for the Rust artifacts Changelog-None --- .github/CODEOWNERS | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d9e64d21d1fc..93d668a6efa8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,12 +4,20 @@ # also can optionally require approval from a code owner before the # author can merge a pull request in the repository. -wallet/ @cdecker -*.py @cdecker +cln-grpc/ @cdecker +cln-rpc/ @cdecker +plugins/src/ @cdecker +plugins/grpc-plugin/ @cdecker +contrib/reprobuild/ @cdecker +contrib/msggen/ @cdecker +contrib/pyln-client/ @cdecker +contrib/pyln-testing/ @cdecker +# Needed to ensure hsmd wire compatibility between releases +hsmd/hsmd_wire.csv @cdecker -wallet/invoices.* @ZmnSCPxj +wallet/invoices.* @ZmnSCPxj plugins/multifundchannel.c @ZmnSCPxj -doc/BACKUP.md @ZmnSCPxj @cdecker +doc/BACKUP.md @ZmnSCPxj # See https://help.github.com/articles/about-codeowners/ for more # information From f90ee0ecfd5c029955b0eac6378e52d3b8a77a88 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 8 May 2022 19:32:29 +0200 Subject: [PATCH 0728/1530] ignore generic binary Signed-off-by: Vincenzo Palazzo --- devtools/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/devtools/.gitignore b/devtools/.gitignore index 9f95803ced42..b3ec165fdcd6 100644 --- a/devtools/.gitignore +++ b/devtools/.gitignore @@ -17,3 +17,4 @@ mkquery onion route topology +fp16 From ae522229f0f42047ba290e6c8d9ec67c322a1174 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 13 May 2022 17:54:14 +0200 Subject: [PATCH 0729/1530] doc: Update lightning.readthedocs.org project name after rebrand --- doc/conf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 8a5be9bdf873..5ae4f97be47e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # -# c-lightning documentation build configuration file, created by +# core-lightning documentation build configuration file, created by # sphinx-quickstart on Thu Feb 1 00:24:47 2018. # # This file is execfile()d with the current directory set to its @@ -58,7 +58,7 @@ master_doc = 'index' # General information about the project. -project = 'c-lightning' +project = 'core-lightning' author = 'Core Lightning Developers' copyright = datetime.now().strftime('2015 — %Y, {}'.format(author)) @@ -243,4 +243,4 @@ # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'c-lightningdoc' +htmlhelp_basename = 'core-lightningdoc' From e972e37e8c3b07853f5d3f49b5d4f925289112e4 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 13 May 2022 17:54:36 +0200 Subject: [PATCH 0730/1530] docs: Update references from c-lightning to Core-Lightning --- doc/lightning-feerates.7.md | 6 +++--- doc/lightning-parsefeerate.7.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index d1beb1eabf9b..f4669205e81a 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -9,11 +9,11 @@ SYNOPSIS DESCRIPTION ----------- -The **feerates** command returns the feerates that C-lightning will use. +The **feerates** command returns the feerates that CLN will use. The feerates will be based on the recommended feerates from the backend. The backend may fail to provide estimates, but if it was able to provide -estimates in the past, C-lightning will continue to use those for a while. -C-lightning will also smoothen feerate estimations from the backend. +estimates in the past, CLN will continue to use those for a while. +CLN will also smoothen feerate estimations from the backend. *style* is either of the two strings: diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md index 8afc62370c1b..d46d7f22c4b9 100644 --- a/doc/lightning-parsefeerate.7.md +++ b/doc/lightning-parsefeerate.7.md @@ -33,7 +33,7 @@ not recognized. TRIVIA ------ -In C-lightning we like to call the weight unit "sipa" +In CLN we like to call the weight unit "sipa" in honor of Pieter Wuille, who uses the name "sipa" on IRC and elsewhere. Internally we call the *perkw* style as "feerate per kilosipa". From 9039c9c46e44d57fd120b449f8904b2a479bc4c0 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 13 May 2022 17:55:23 +0200 Subject: [PATCH 0731/1530] docker: Update name from c-lightning to Core-Lightning --- Dockerfile | 2 +- contrib/docker/linuxarm32v7.Dockerfile | 2 +- tools/docker-entrypoint.sh | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8077edacd91e..606631b3a3d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # This dockerfile is meant to compile a core-lightning x64 image # It is using multi stage build: # * downloader: Download litecoin/bitcoin and qemu binaries needed for core-lightning -# * builder: Compile core-lightning dependencies, then c-lightning itself with static linking +# * builder: Compile core-lightning dependencies, then core-lightning itself with static linking # * final: Copy the binaries required at runtime # The resulting image uploaded to dockerhub will only contain what is needed for runtime. # From the root of the repository, run "docker build -t yourimage:yourtag ." diff --git a/contrib/docker/linuxarm32v7.Dockerfile b/contrib/docker/linuxarm32v7.Dockerfile index 7120befb5e75..ffcfb490792c 100644 --- a/contrib/docker/linuxarm32v7.Dockerfile +++ b/contrib/docker/linuxarm32v7.Dockerfile @@ -1,6 +1,6 @@ # This dockerfile is meant to cross compile with a x64 machine for a arm32v7 host # It is using multi stage build: -# * downloader: Download litecoin/bitcoin and qemu binaries needed for c-lightning +# * downloader: Download litecoin/bitcoin and qemu binaries needed for core-lightning # * builder: Cross compile c-lightning dependencies, then c-lightning itself with static linking # * final: Copy the binaries required at runtime # The resulting image uploaded to dockerhub will only contain what is needed for runtime. diff --git a/tools/docker-entrypoint.sh b/tools/docker-entrypoint.sh index c5bccf400ffa..bb06db53ae54 100755 --- a/tools/docker-entrypoint.sh +++ b/tools/docker-entrypoint.sh @@ -8,11 +8,11 @@ if [ "$EXPOSE_TCP" == "true" ]; then set -m lightningd "$@" & - echo "C-Lightning starting" + echo "Core-Lightning starting" while read -r i; do if [ "$i" = "lightning-rpc" ]; then break; fi; done \ < <(inotifywait -e create,open --format '%f' --quiet "${networkdatadir}" --monitor) - echo "C-Lightning started" - echo "C-Lightning started, RPC available on port $LIGHTNINGD_RPC_PORT" + echo "Core-Lightning started" + echo "Core-Lightning started, RPC available on port $LIGHTNINGD_RPC_PORT" socat "TCP4-listen:$LIGHTNINGD_RPC_PORT,fork,reuseaddr" "UNIX-CONNECT:${networkdatadir}/lightning-rpc" & fg %- From 1c495ca5a83df8cccb62a53cbfd3bc437ca3745c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 14 May 2022 14:28:47 +0930 Subject: [PATCH 0732/1530] connectd: fix accidental handling of old reconnections. We had multiple reports of channels being unilaterally closed because it seemed like the peer was sending old revocation numbers. Turns out, it was actually old reestablish messages! When we have a reconnection, we would put the new connection aside, and tell lightningd to close the current connection: when it did, we would restart processing of the initial reconnection. However, we could end up with *multiple* "reconnecting" connections, while waiting for an existing connection to close. Though the connections were long gone, there could still be messages queued (particularly the channel_reestablish message, which comes early on). Eventually, a normal reconnection would cause us to process one of these reconnecting connections, and channeld would see the (perhaps very old!) messages, and get confused. (I have a test which triggers this, but it also hangs the connect command, due to other issues we will fix in the next release...) Fixes: #5240 Signed-off-by: Rusty Russell --- connectd/connectd.c | 48 ++++++++++++++++++++++----------------------- connectd/connectd.h | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 10e9a4c7d625..62362f84aec7 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -211,36 +211,29 @@ static void peer_connected_in(struct daemon *daemon, tal_free(connect); } -/*~ This is an ad-hoc marshalling structure where we store arguments so we - * can call peer_connected again. */ -struct peer_reconnected { - struct daemon *daemon; - struct node_id id; - struct wireaddr_internal addr; - const struct wireaddr *remote_addr; - struct crypto_state cs; - const u8 *their_features; - bool incoming; -}; - /*~ For simplicity, lightningd only ever deals with a single connection per * peer. So if we already know about a peer, we tell lightning to disconnect * the old one and retry once it does. */ static struct io_plan *retry_peer_connected(struct io_conn *conn, struct peer_reconnected *pr) { - struct io_plan *plan; - /*~ As you can see, we've had issues with this code before :( */ status_peer_debug(&pr->id, "processing now old peer gone"); - /*~ Usually the pattern is to return this directly, but we have to free - * our temporary structure. */ - plan = peer_connected(conn, pr->daemon, &pr->id, &pr->addr, + /* If this fails (still waiting), pr will be freed, so reparent onto + * tmpctx so it gets freed either way. */ + tal_steal(tmpctx, pr); + + /*~ Usually the pattern is to return this directly. */ + return peer_connected(conn, pr->daemon, &pr->id, &pr->addr, pr->remote_addr, &pr->cs, take(pr->their_features), pr->incoming); - tal_free(pr); - return plan; +} + +/*~ A common use for destructors is to remove themselves from a data structure */ +static void destroy_peer_reconnected(struct peer_reconnected *pr) +{ + peer_reconnected_htable_del(&pr->daemon->reconnected, pr); } /*~ If we already know about this peer, we tell lightningd and it disconnects @@ -259,6 +252,13 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, status_peer_debug(id, "reconnect"); + /* If we have a previous reconnection, we replace it. */ + pr = peer_reconnected_htable_get(&daemon->reconnected, id); + if (pr) { + peer_reconnected_htable_del(&daemon->reconnected, pr); + tal_free(pr); + } + /* Tell master to kill it: will send peer_disconnect */ msg = towire_connectd_reconnected(NULL, id); daemon_conn_send(daemon->master, take(msg)); @@ -271,6 +271,8 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, pr->addr = *addr; pr->remote_addr = tal_dup_or_null(pr, struct wireaddr, remote_addr); pr->incoming = incoming; + peer_reconnected_htable_add(&daemon->reconnected, pr); + tal_add_destructor(pr, destroy_peer_reconnected); /*~ Note that tal_dup_talarr() will do handle the take() of features * (turning it into a simply tal_steal() in those cases). */ @@ -280,11 +282,7 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, * the peer set. When someone calls `io_wake()` on that address, it * will call retry_peer_connected above. */ return io_wait(conn, peer_htable_get(&daemon->peers, id), - /*~ The notleak() wrapper is a DEVELOPER-mode hack so - * that our memory leak detection doesn't consider 'pr' - * (which is not referenced from our code) to be a - * memory leak. */ - retry_peer_connected, notleak(pr)); + retry_peer_connected, pr); } /*~ When we free a peer, we remove it from the daemon's hashtable */ @@ -1981,6 +1979,7 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) /* Now delete daemon and those which it has pointers to. */ memleak_remove_region(memtable, daemon, sizeof(daemon)); memleak_remove_htable(memtable, &daemon->peers.raw); + memleak_remove_htable(memtable, &daemon->reconnected.raw); found_leak = dump_memleak(memtable, memleak_status_broken); daemon_conn_send(daemon->master, @@ -2127,6 +2126,7 @@ int main(int argc, char *argv[]) /* Allocate and set up our simple top-level structure. */ daemon = tal(NULL, struct daemon); peer_htable_init(&daemon->peers); + peer_reconnected_htable_init(&daemon->reconnected); memleak_add_helper(daemon, memleak_daemon_cb); list_head_init(&daemon->connecting); timers_init(&daemon->timers, time_mono()); diff --git a/connectd/connectd.h b/connectd/connectd.h index ab5829b48145..f820ca2d1c6b 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -126,6 +126,37 @@ HTABLE_DEFINE_TYPE(struct peer, peer_eq_node_id, peer_htable); +/*~ This is an ad-hoc marshalling structure where we store arguments so we + * can call peer_connected again. */ +struct peer_reconnected { + struct daemon *daemon; + struct node_id id; + struct wireaddr_internal addr; + const struct wireaddr *remote_addr; + struct crypto_state cs; + const u8 *their_features; + bool incoming; +}; + +static const struct node_id * +peer_reconnected_keyof(const struct peer_reconnected *pr) +{ + return &pr->id; +} + +static bool peer_reconnected_eq_node_id(const struct peer_reconnected *pr, + const struct node_id *id) +{ + return node_id_eq(&pr->id, id); +} + +/*~ This defines 'struct peer_reconnected_htable'. */ +HTABLE_DEFINE_TYPE(struct peer_reconnected, + peer_reconnected_keyof, + node_id_hash, + peer_reconnected_eq_node_id, + peer_reconnected_htable); + /*~ This is the global state, like `struct lightningd *ld` in lightningd. */ struct daemon { /* Who am I? */ @@ -142,6 +173,9 @@ struct daemon { * have disconnected. */ struct peer_htable peers; + /* Peers which have reconnected, waiting for us to kill existing conns */ + struct peer_reconnected_htable reconnected; + /* Peers we are trying to reach */ struct list_head connecting; From 4343f720be8d67787426686ddc25bf64f803984f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 14 May 2022 14:28:48 +0930 Subject: [PATCH 0733/1530] connectd: remove assert which can trigger. I have a test which reproduces this, too, and it's been seen in the wild. It seems we can add a subd as we're closing, which causes this assert to trigger. Fixes: #5254 Signed-off-by: Rusty Russell --- connectd/connectd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 62362f84aec7..deb604f45216 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1900,7 +1900,6 @@ void peer_conn_closed(struct peer *peer) struct connecting *connect = find_connecting(peer->daemon, &peer->id); /* These should be closed already! */ - assert(tal_count(peer->subds) == 0); assert(!peer->to_peer); assert(peer->ready_to_die || !peer->active); From 1860bbaed7b37858215d5c4732c8894cdb4b9d93 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 14 May 2022 14:28:49 +0930 Subject: [PATCH 0734/1530] CHANGELOG.md: release notes for 0.11.1. Signed-off-by: Rusty Russell --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7aa6a9dec7e..f7380f3b01b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> +## [0.11.1] - 2022-05-13: Simon's Carefully Chosen Release Name II + +Single change which fixed a bug introduced in 0.11.0 which could cause +unwanted unilateral closes (`bad reestablish revocation_number: 0 vs 3`) + +### Fixed + + - connectd: make sure we don't keep stale reconnections around. ([#5256]) + - connectd: fix assert which we could trigger. ([#5256]) + +[#5256]: https://github.com/ElementsProject/lightning/pull/5256 + ## [0.11.0.1] - 2022-04-04: Simon's Carefully Chosen Release Name This release would have been named by Simon Vrouwe, had he responded to my emails! From c78b349f44b08771050756b783b55b9ff8014945 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 16 May 2022 11:44:54 -0500 Subject: [PATCH 0735/1530] README: add links to discord + telegram so people can easily find us! Suggested-By: @justinmoon --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 11d50ea3c90f..5a1a089e02b8 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ This implementation has been in production use on the Bitcoin mainnet since earl We recommend getting started by experimenting on `testnet` (or `regtest`), but the implementation is considered stable and can be safely used on mainnet. Any help testing the implementation, reporting bugs, or helping with outstanding issues is very welcome. -Don't hesitate to reach out to us on IRC at [#lightning-dev @ libera.chat][irc1], [#c-lightning @ libera.chat][irc2], or on the implementation-specific mailing list [c-lightning@lists.ozlabs.org][ml1], or on the Lightning Network-wide mailing list [lightning-dev@lists.linuxfoundation.org][ml2]. +Don't hesitate to reach out to us on IRC at [#lightning-dev @ libera.chat][irc1], [#c-lightning @ libera.chat][irc2], or on the implementation-specific mailing list [c-lightning@lists.ozlabs.org][ml1], or on the Lightning Network-wide mailing list [lightning-dev@lists.linuxfoundation.org][ml2], or on Discord [core-lightning][discord], or on Telegram [Core Lightning][telegram]. ## Getting Started @@ -234,6 +234,8 @@ You should also configure with `--enable-developer` to get additional checks and [irc2]: https://web.libera.chat/#c-lightning [ml1]: https://lists.ozlabs.org/listinfo/c-lightning [ml2]: https://lists.linuxfoundation.org/mailman/listinfo/lightning-dev +[discord]: https://discord.gg/mE9s4rc5un +[telegram]: https://t.me/lightningd [docs]: https://lightning.readthedocs.org [ppa]: https://launchpad.net/~lightningnetwork/+archive/ubuntu/ppa [releases]: https://github.com/ElementsProject/lightning/releases From 8dd51d127fff01b9302009906dcbdc83ea3b6548 Mon Sep 17 00:00:00 2001 From: Kristaps Kaupe Date: Sat, 14 May 2022 10:31:10 +0300 Subject: [PATCH 0736/1530] Restore description of "reserved" field for listfunds It was lost in 2296d4452f7ba5f112fbf5700974a31dadb66fb4. --- doc/lightning-listfunds.7.md | 3 ++- doc/schemas/listfunds.schema.json | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index aa903207b528..d2dc247fd24b 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -27,6 +27,7 @@ On success, an object is returned, containing: - **amount_msat** (msat): the amount of the output - **scriptpubkey** (hex): the scriptPubkey of the output - **status** (string) (one of "unconfirmed", "confirmed", "spent") + - **reserved** (boolean): whether this UTXO is currently reserved for an in-flight tx - **address** (string, optional): the bitcoin address of the output - **redeemscript** (hex, optional): the redeemscript, only if it's p2sh-wrapped @@ -67,4 +68,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:959d54ed855f9f5d64148f5acf4a0bbb4bc1503e610ddae5ab4c04fd397af0b3) +[comment]: # ( SHA256STAMP:e2b4f817ca6032ab4421fccaba226c03e0995c8dbfbfeb2f7c8572987ffe7dc4) diff --git a/doc/schemas/listfunds.schema.json b/doc/schemas/listfunds.schema.json index e7a9c0af9a84..bd1f73382acf 100644 --- a/doc/schemas/listfunds.schema.json +++ b/doc/schemas/listfunds.schema.json @@ -56,6 +56,10 @@ "confirmed", "spent" ] + }, + "reserved": { + "type": "boolean", + "description": "whether this UTXO is currently reserved for an in-flight tx" } }, "allOf": [ From 8b62e2584fc840bd3c84aa6e85a9b10bfe2d5c1e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 17 May 2022 10:11:01 +0930 Subject: [PATCH 0737/1530] connectd: remove enable-autotor-v2-mode option Changelog-Removed: lightningd: removed `enable-autotor-v2-mode` option (deprecated v0.10.1) Signed-off-by: Rusty Russell --- connectd/connectd.c | 4 +--- connectd/connectd.h | 3 --- connectd/connectd_wire.csv | 1 - connectd/tor_autoservice.c | 31 +++++++++++-------------------- connectd/tor_autoservice.h | 3 +-- doc/lightning-listconfigs.7 | 3 +-- doc/lightning-listconfigs.7.md | 1 - lightningd/connect_control.c | 1 - lightningd/lightningd.h | 3 --- lightningd/options.c | 9 --------- lightningd/plugin.c | 2 +- 11 files changed, 15 insertions(+), 46 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index deb604f45216..e20bf7f7f2ae 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1437,8 +1437,7 @@ setup_listeners(const tal_t *ctx, toraddr = tor_autoservice(tmpctx, &proposed_wireaddr[i], tor_password, - localaddr, - daemon->use_v3_autotor); + localaddr); if (!(proposed_listen_announce[i] & ADDR_ANNOUNCE)) { continue; @@ -1534,7 +1533,6 @@ static void connect_init(struct daemon *daemon, const u8 *msg) &proxyaddr, &daemon->always_use_proxy, &daemon->dev_allow_localhost, &daemon->use_dns, &tor_password, - &daemon->use_v3_autotor, &daemon->timeout_secs, &daemon->websocket_helper, &daemon->websocket_port, diff --git a/connectd/connectd.h b/connectd/connectd.h index f820ca2d1c6b..72161b1220bf 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -206,9 +206,6 @@ struct daemon { /* File descriptors to listen on once we're activated. */ const struct listen_fd **listen_fds; - /* Allow to define the default behavior of tor services calls*/ - bool use_v3_autotor; - /* Our features, as lightningd told us */ struct feature_set *our_features; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 67af1dca055f..91d0fdb6d821 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -18,7 +18,6 @@ msgdata,connectd_init,use_tor_proxy_always,bool, msgdata,connectd_init,dev_allow_localhost,bool, msgdata,connectd_init,use_dns,bool, msgdata,connectd_init,tor_password,wirestring, -msgdata,connectd_init,use_v3_autotor,bool, msgdata,connectd_init,timeout_secs,u32, msgdata,connectd_init,websocket_helper,wirestring, msgdata,connectd_init,websocket_port,u16, diff --git a/connectd/tor_autoservice.c b/connectd/tor_autoservice.c index a790c8615815..33b6b970f6ab 100644 --- a/connectd/tor_autoservice.c +++ b/connectd/tor_autoservice.c @@ -86,7 +86,6 @@ static void discard_remaining_response(struct rbuf *rbuf) static struct wireaddr *make_onion(const tal_t *ctx, struct rbuf *rbuf, const struct wireaddr *local, - bool use_v3_autotor, u16 port) { char *line; @@ -101,25 +100,18 @@ static struct wireaddr *make_onion(const tal_t *ctx, if (!strstarts(line, "VERSION Tor=")) continue; - if (use_v3_autotor) - if (strstr(line, "\"0.0") || - strstr(line, "\"0.1") || - strstr(line, "\"0.2") || - strstr(line, "\"0.3")) { - use_v3_autotor = false; - status_unusual("Autotor: fallback to try a V2 onion service, your Tor version is smaller than 0.4.x.x"); - } - }; + if (strstr(line, "\"0.0") || + strstr(line, "\"0.1") || + strstr(line, "\"0.2") || + strstr(line, "\"0.3")) { + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "Autotor: your Tor version is smaller than 0.4.x.x"); + } + } - if (!use_v3_autotor) { - tor_send_cmd(rbuf, - tal_fmt(tmpctx, "ADD_ONION NEW:RSA1024 Port=%d,%s Flags=DiscardPK,Detach", - port, fmt_wireaddr(tmpctx, local))); - } else { - tor_send_cmd(rbuf, + tor_send_cmd(rbuf, tal_fmt(tmpctx, "ADD_ONION NEW:ED25519-V3 Port=%d,%s Flags=DiscardPK,Detach", port, fmt_wireaddr(tmpctx, local))); - } while ((line = tor_response_line(rbuf)) != NULL) { const char *name; @@ -268,8 +260,7 @@ static void negotiate_auth(struct rbuf *rbuf, const char *tor_password) struct wireaddr *tor_autoservice(const tal_t *ctx, const struct wireaddr_internal *tor_serviceaddr, const char *tor_password, - const struct wireaddr *laddr, - const bool use_v3_autotor) + const struct wireaddr *laddr) { int fd; struct wireaddr *onion; @@ -290,7 +281,7 @@ struct wireaddr *tor_autoservice(const tal_t *ctx, rbuf_init(&rbuf, fd, buffer, tal_count(buffer), buf_resize); negotiate_auth(&rbuf, tor_password); - onion = make_onion(ctx, &rbuf, laddr, use_v3_autotor, tor_serviceaddr->u.torservice.port); + onion = make_onion(ctx, &rbuf, laddr, tor_serviceaddr->u.torservice.port); /*on the other hand we can stay connected until ln finish to keep onion alive and then vanish */ //because when we run with Detach flag as we now do every start of LN creates a new addr while the old diff --git a/connectd/tor_autoservice.h b/connectd/tor_autoservice.h index 8bdb8bb9d435..dce91a637e49 100644 --- a/connectd/tor_autoservice.h +++ b/connectd/tor_autoservice.h @@ -9,8 +9,7 @@ struct wireaddr *tor_autoservice(const tal_t *ctx, const struct wireaddr_internal *tor_serviceaddr, const char *tor_password, - const struct wireaddr *localaddr, - const bool use_v3_autotor); + const struct wireaddr *localaddr); struct wireaddr *tor_fixed_service(const tal_t *ctx, const struct wireaddr_internal *tor_serviceaddr, diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index e781d0019d63..79e81678371f 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -261,7 +261,6 @@ On failure, one of the following error codes may be returned: "autolisten": true, "proxy": "127.0.0.1:9050", "disable-dns": "false", - "enable-autotor-v2-mode": "false", "encrypted-hsm": false, "rpc-file-mode": "0600", "log-level": "DEBUG", @@ -282,4 +281,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:8d8f73010f55f3af6e050c944cf7670109224b3cf3166cc754047f6e20beef20 +\" SHA256STAMP:c55d5a93c5917bbab0be1633f0f9f06196cfdde6b79434ef012e9dfc2fbbcca9 diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 5b285b663c47..823ee79ba8fe 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -188,7 +188,6 @@ EXAMPLE JSON RESPONSE "autolisten": true, "proxy": "127.0.0.1:9050", "disable-dns": "false", - "enable-autotor-v2-mode": "false", "encrypted-hsm": false, "rpc-file-mode": "0600", "log-level": "DEBUG", diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 144f777100b3..0fe75804896b 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -554,7 +554,6 @@ int connectd_init(struct lightningd *ld) ld->proxyaddr, ld->always_use_proxy || ld->pure_tor_setup, IFDEV(ld->dev_allow_localhost, false), ld->config.use_dns, ld->tor_service_password ? ld->tor_service_password : "", - ld->config.use_v3_autotor, ld->config.connection_timeout_secs, websocket_helper_path, ld->websocket_port, diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index ae74a635f978..5e3a1edb70b0 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -62,9 +62,6 @@ struct config { /* Minimal amount of effective funding_satoshis for accepting channels */ u64 min_capacity_sat; - /* Allow to define the default behavior of tor services calls*/ - bool use_v3_autotor; - /* This is the key we use to encrypt `hsm_secret`. */ struct secret *keypass; diff --git a/lightningd/options.c b/lightningd/options.c index 826b87b6400a..34e6f6db4cf9 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -793,8 +793,6 @@ static const struct config testnet_config = { /* Sets min_effective_htlc_capacity - at 1000$/BTC this is 10ct */ .min_capacity_sat = 10000, - .use_v3_autotor = true, - /* 1 minute should be enough for anyone! */ .connection_timeout_secs = 60, @@ -859,9 +857,6 @@ static const struct config mainnet_config = { /* Sets min_effective_htlc_capacity - at 1000$/BTC this is 10ct */ .min_capacity_sat = 10000, - /* Allow to define the default behavior of tor services calls*/ - .use_v3_autotor = true, - /* 1 minute should be enough for anyone! */ .connection_timeout_secs = 60, @@ -1168,10 +1163,6 @@ static void register_opts(struct lightningd *ld) opt_register_early_noarg("--disable-dns", opt_set_invbool, &ld->config.use_dns, "Disable DNS lookups of peers"); - if (deprecated_apis) - opt_register_noarg("--enable-autotor-v2-mode", opt_set_invbool, &ld->config.use_v3_autotor, - opt_hidden); - opt_register_noarg("--encrypted-hsm", opt_set_hsm_password, ld, "Set the password to encrypt hsm_secret with. If no password is passed through command line, " "you will be prompted to enter it."); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index e0dff81885af..1497439cc323 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1853,7 +1853,7 @@ plugin_populate_init_request(struct plugin *plugin, struct jsonrpc_request *req) json_add_string(req->stream, "network", chainparams->network_name); if (ld->proxyaddr) { json_add_address(req->stream, "proxy", ld->proxyaddr); - json_add_bool(req->stream, "torv3-enabled", ld->config.use_v3_autotor); + json_add_bool(req->stream, "torv3-enabled", true); json_add_bool(req->stream, "always_use_proxy", ld->always_use_proxy); if (deprecated_apis) json_add_bool(req->stream, "use_proxy_always", From f078e54e9887c9b747b948bce54d7f9988d1a5cb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 17 May 2022 10:17:34 +0930 Subject: [PATCH 0738/1530] lightningd: remove various deprecated JSON fields. Signed-off-by: Rusty Russell Changelog-Removed: JSON-RPC: removed `listtransactions` `outputs` `satoshis` field (deprecated v0.10.1) Changelog-Removed: JSON-RPC: removed `listpeers` `channels` deprecated fields (deprecated v0.10.1) Changelog-Removed: JSON-RPC: removed `listpeers` `channels` `closer` now omitted, rather than `null` (deprecated v0.10.1) --- cln-grpc/proto/node.proto | 1 + cln-grpc/src/convert.rs | 1 + cln-rpc/src/model.rs | 2 ++ doc/lightning-listpeers.7.md | 4 +-- doc/lightning-listtransactions.7.md | 2 +- doc/schemas/listpeers.schema.json | 32 +++--------------------- doc/schemas/listtransactions.schema.json | 3 --- lightningd/peer_control.c | 24 ------------------ wallet/walletrpc.c | 2 -- 9 files changed, 10 insertions(+), 61 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 9ac4dddab75f..bf66a9baa930 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -181,6 +181,7 @@ message ListpeersPeersChannels { optional bytes close_to = 14; optional bool private = 15; ChannelSide opener = 16; + optional ChannelSide closer = 17; repeated string features = 18; optional Amount to_us_msat = 20; optional Amount min_to_us_msat = 21; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index d1ae7f309843..a7be55089482 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -119,6 +119,7 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? private: c.private.clone(), // Rule #2 for type boolean? opener: c.opener as i32, + closer: c.closer.map(|v| v as i32), features: c.features.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannelsFeatures to_us_msat: c.to_us_msat.map(|f| f.into()), // Rule #2 for type msat? min_to_us_msat: c.min_to_us_msat.map(|f| f.into()), // Rule #2 for type msat? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index d5286569aa1c..db51cb0e5cdb 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1160,6 +1160,8 @@ pub mod responses { // Path `ListPeers.peers[].channels[].opener` #[serde(rename = "opener")] pub opener: ChannelSide, + #[serde(skip_serializing_if = "Option::is_none")] + pub closer: Option, #[serde(alias = "features")] pub features: Vec, #[serde(alias = "to_us_msat", skip_serializing_if = "Option::is_none")] diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 849b808c1814..31c3d204c8c8 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -69,7 +69,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **scratch_txid** (txid): The commitment transaction txid we would use if we went onchain now - **close_to** (hex, optional): scriptPubkey which we have to close to if we mutual close - **private** (boolean, optional): if False, we will not announce this channel - - **closer** (string, optional): Who initiated the channel close (`null` is deprecated!) (one of "local", "remote", *null*) + - **closer** (string, optional): Who initiated the channel close (one of "local", "remote") - **funding** (object, optional): - **local_msat** (msat): Amount of channel we funded - **remote_msat** (msat): Amount of channel they funded @@ -380,4 +380,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:6b0ec5c899c8685487190209f594635030205a275e1dc6d61a7b057adbf66192) +[comment]: # ( SHA256STAMP:4f76b5ac19d3dfdaf3d04f5dd5de2312a2ab0ccffe779508c416aaf6e644612a) diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index 767a92bde531..157fc0126b0d 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -104,4 +104,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:ba0624377601e6e90c2ca90b709fd076f3ed0f2b813f73553ec6b935eeec54a1) +[comment]: # ( SHA256STAMP:bd9c33dd27be0f25b0212b4115714768ffbec2ff6e72f083613a4464a3f98bc0) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index d35091182c1c..f50cef596c9c 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -314,17 +314,12 @@ "description": "Who initiated the channel" }, "closer": { - "FIXME": "deprecated_apis turns off null!", - "type": [ - "string", - "null" - ], + "type": "string", "enum": [ "local", - "remote", - null + "remote" ], - "description": "Who initiated the channel close (`null` is deprecated!)" + "description": "Who initiated the channel close" }, "features": { "type": "array", @@ -360,12 +355,6 @@ } } }, - "funding_allocation_msat": { - "deprecated": true - }, - "funding_msat": { - "deprecated": true - }, "to_us_msat": { "type": "msat", "description": "how much of channel is owed to us" @@ -765,8 +754,6 @@ "closer": {}, "features": {}, "funding": {}, - "funding_allocation_msat": {}, - "funding_msat": {}, "to_us_msat": {}, "min_to_us_msat": {}, "max_to_us_msat": {}, @@ -817,7 +804,6 @@ "last_feerate": {}, "next_feerate": {}, "inflight": {}, - "last_tx_fee": {}, "last_tx_fee_msat": {}, "direction": {}, "close_to_addr": { @@ -854,8 +840,6 @@ "closer": {}, "features": {}, "funding": {}, - "funding_allocation_msat": {}, - "funding_msat": {}, "to_us_msat": {}, "min_to_us_msat": {}, "max_to_us_msat": {}, @@ -907,9 +891,6 @@ "next_feerate": {}, "close_to_addr": {}, "direction": {}, - "last_tx_fee": { - "deprecated": true - }, "last_tx_fee_msat": { "type": "msat", "description": "fee attached to this the current tx" @@ -944,8 +925,6 @@ "closer": {}, "features": {}, "funding": {}, - "funding_allocation_msat": {}, - "funding_msat": {}, "to_us_msat": {}, "min_to_us_msat": {}, "max_to_us_msat": {}, @@ -995,7 +974,6 @@ "initial_feerate": {}, "last_feerate": {}, "next_feerate": {}, - "last_tx_fee": {}, "close_to_addr": {}, "last_tx_fee_msat": {}, "direction": { @@ -1033,8 +1011,6 @@ "closer": {}, "features": {}, "funding": {}, - "funding_allocation_msat": {}, - "funding_msat": {}, "to_us_msat": {}, "min_to_us_msat": {}, "max_to_us_msat": {}, @@ -1082,7 +1058,6 @@ "out_msatoshi_fulfilled": {}, "htlcs": {}, "inflight": {}, - "last_tx_fee": {}, "close_to_addr": {}, "direction": {}, "last_tx_fee_msat": {}, @@ -1129,7 +1104,6 @@ "connected": {}, "htlcs": {}, "log": {}, - "last_tx_fee": {}, "netaddr": { "type": "array", "minItems": 1, diff --git a/doc/schemas/listtransactions.schema.json b/doc/schemas/listtransactions.schema.json index a5c5d5e5f70d..0b41b17c779f 100644 --- a/doc/schemas/listtransactions.schema.json +++ b/doc/schemas/listtransactions.schema.json @@ -134,9 +134,6 @@ "type": "u32", "description": "the 0-based output number" }, - "satoshis": { - "deprecated": true - }, "msat": { "type": "msat", "description": "the amount of the output" diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 7994e6fbb6b5..eb6fd2074c0c 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -628,7 +628,6 @@ static void json_add_channel(struct lightningd *ld, struct channel_stats channel_stats; struct amount_msat funding_msat, peer_msats, our_msats; struct amount_sat peer_funded_sats; - struct peer *p = channel->peer; struct state_change_entry *state_changes; u32 feerate; @@ -639,9 +638,6 @@ static void json_add_channel(struct lightningd *ld, bitcoin_txid(channel->last_tx, &txid); json_add_txid(response, "scratch_txid", &txid); - if (deprecated_apis) - json_add_amount_sat_only(response, "last_tx_fee", - bitcoin_tx_compute_fee(channel->last_tx)); json_add_amount_sat_only(response, "last_tx_fee_msat", bitcoin_tx_compute_fee(channel->last_tx)); } @@ -746,8 +742,6 @@ static void json_add_channel(struct lightningd *ld, if (channel->closer != NUM_SIDES) json_add_string(response, "closer", channel->closer == LOCAL ? "local" : "remote"); - else if (deprecated_apis) - json_add_null(response, "closer"); json_array_start(response, "features"); if (channel_has(channel, OPT_STATIC_REMOTEKEY)) @@ -781,24 +775,6 @@ static void json_add_channel(struct lightningd *ld, our_msats = AMOUNT_MSAT(0); } - if (deprecated_apis) { - json_object_start(response, "funding_allocation_msat"); - json_add_u64(response, node_id_to_hexstr(tmpctx, &p->id), - peer_msats.millisatoshis); /* Raw: JSON field */ - json_add_u64(response, node_id_to_hexstr(tmpctx, &ld->id), - our_msats.millisatoshis); /* Raw: JSON field */ - json_object_end(response); - - json_object_start(response, "funding_msat"); - json_add_sat_only(response, - node_id_to_hexstr(tmpctx, &p->id), - peer_funded_sats); - json_add_sat_only(response, - node_id_to_hexstr(tmpctx, &ld->id), - channel->our_funds); - json_object_end(response); - } - json_object_start(response, "funding"); json_add_sat_only(response, "local_msat", channel->our_funds); json_add_sat_only(response, "remote_msat", peer_funded_sats); diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index fb2d3815b22c..9d7ad17027d9 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -558,8 +558,6 @@ static void json_transaction_details(struct json_stream *response, json_object_start(response, NULL); json_add_u32(response, "index", i); - if (deprecated_apis) - json_add_amount_sat_only(response, "satoshis", sat); json_add_amount_sat_only(response, "msat", sat); #if EXPERIMENTAL_FEATURES From c77eda6d64b6092cc1fd26ae93798acc40d6414e Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 6 Apr 2022 20:32:57 +0200 Subject: [PATCH 0739/1530] pyln-spec: upgrade to the last bolt version Changelog-Fixed: pyln-spec: update the bolts implementation Signed-off-by: Vincenzo Palazzo --- .../pyln-spec/bolt1/pyln/spec/bolt1/csv.py | 4 + .../bolt1/pyln/spec/bolt1/gen_csv_version.py | 2 +- .../bolt1/pyln/spec/bolt1/gen_version.py | 4 +- .../pyln-spec/bolt1/pyln/spec/bolt1/text.py | 76 +++++--- .../bolt2/pyln/spec/bolt2/gen_version.py | 4 +- .../pyln-spec/bolt2/pyln/spec/bolt2/text.py | 166 +++++++++++------- .../pyln-spec/bolt4/pyln/spec/bolt4/csv.py | 2 + .../bolt4/pyln/spec/bolt4/gen_csv_version.py | 2 +- .../bolt4/pyln/spec/bolt4/gen_version.py | 4 +- .../pyln-spec/bolt4/pyln/spec/bolt4/text.py | 15 +- .../pyln-spec/bolt7/pyln/spec/bolt7/csv.py | 2 +- .../bolt7/pyln/spec/bolt7/gen_csv_version.py | 2 +- .../bolt7/pyln/spec/bolt7/gen_version.py | 4 +- .../pyln-spec/bolt7/pyln/spec/bolt7/text.py | 84 ++++----- 14 files changed, 230 insertions(+), 141 deletions(-) diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py index eda2a5862644..cecb380b6afe 100644 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/csv.py @@ -13,6 +13,10 @@ "msgdata,error,channel_id,channel_id,", "msgdata,error,len,u16,", "msgdata,error,data,byte,len", + "msgtype,warning,1", + "msgdata,warning,channel_id,channel_id,", + "msgdata,warning,len,u16,", + "msgdata,warning,data,byte,len", "msgtype,ping,18", "msgdata,ping,num_pong_bytes,u16,", "msgdata,ping,byteslen,u16,", diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_csv_version.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_csv_version.py index 0741f08250cc..2140fc8212ff 100644 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_csv_version.py +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_csv_version.py @@ -1 +1 @@ -__csv_version__ = "1" +__csv_version__ = "2" diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py index cb11f894225b..3377756caf44 100644 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "186" -__gitversion__ = "38abac62065172c00722dca10e7d3fc3049afd72" +__post_version__ = "222" +__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py index d5a2e99efae2..721c6ac76a35 100644 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py @@ -18,7 +18,7 @@ * [Fundamental Types](#fundamental-types) * [Setup Messages](#setup-messages) * [The `init` Message](#the-init-message) - * [The `error` Message](#the-error-message) + * [The `error` and `warning` Messages](#the-error-and-warning-messages) * [Control Messages](#control-messages) * [The `ping` and `pong` Messages](#the-ping-and-pong-messages) * [Appendix A: BigSize Test Vectors](#appendix-a-bigsize-test-vectors) @@ -226,6 +226,12 @@ * `tu32`: a 0 to 4 byte unsigned integer * `tu64`: a 0 to 8 byte unsigned integer +When used to encode amounts, the previous fields MUST comply with the upper +bound of 21 million BTC: + +* satoshi amounts MUST be at most `0x000775f05a074000` +* milli-satoshi amounts MUST be at most `0x1d24b2dfac520000` + The following convenience types are also defined: * `chain_hash`: a 32-byte chain identifier (see [BOLT #0](00-introduction.md#glossary-and-terminology-guide)) @@ -259,9 +265,12 @@ 1. type: 1 (`networks`) 2. data: * [`...*chain_hash`:`chains`] - + 1. type: 3 (`remote_addr`) + 2. data: + * [`...*byte`:`data`] The optional `networks` indicates the chains the node is interested in. +The optional `remote_addr` can be used to circumvent NAT issues. #### Requirements @@ -272,6 +281,12 @@ - SHOULD NOT set features greater than 13 in `globalfeatures`. - SHOULD use the minimum length required to represent the `features` field. - SHOULD set `networks` to all chains it will gossip or open channels for. + - SHOULD set `remote_addr` to reflect the remote IP address (and port) of an + incoming connection, if the node is the receiver and the connection was done + via IP. + - if it sets `remote_addr`: + - MUST set it to a valid `address descriptor` (1 byte type and data) as described in [BOLT 7](07-routing-gossip.md#the-node_announcement-message). + - SHOULD NOT set private addresses as `remote_addr`. The receiving node: - MUST wait to receive `init` before sending any other messages. @@ -280,11 +295,12 @@ - upon receiving unknown _odd_ feature bits that are non-zero: - MUST ignore the bit. - upon receiving unknown _even_ feature bits that are non-zero: - - MUST fail the connection. + - MUST close the connection. - upon receiving `networks` containing no common chains - - MAY fail the connection. + - MAY close the connection. - if the feature vector does not set all known, transitive dependencies: - - MUST fail the connection. + - MUST close the connection. + - MAY use the `remote_addr` to update its `node_announcement` #### Rationale @@ -301,7 +317,7 @@ erroneously believing they will receive updates about their preferred network, or that they can open channels. -### The `error` Message +### The `error` and `warning` Messages For simplicity of diagnosis, it's often useful to tell a peer that something is incorrect. @@ -311,7 +327,11 @@ * [`u16`:`len`] * [`len*byte`:`data`] -The 2-byte `len` field indicates the number of bytes in the immediately following field. +1. type: 1 (`warning`) +2. data: + * [`channel_id`:`channel_id`] + * [`u16`:`len`] + * [`len*byte`:`data`] #### Requirements @@ -326,24 +346,31 @@ - MUST use `temporary_channel_id` in lieu of `channel_id`. A sending node: - - when sending `error`: - - MUST fail the channel referred to by the error message. - SHOULD send `error` for protocol violations or internal errors that make channels unusable or that make further communication unusable. - SHOULD send `error` with the unknown `channel_id` in reply to messages of type `32`-`255` related to unknown channels. + - when sending `error`: + - MUST fail the channel(s) referred to by the error message. + - MAY set `channel_id` to all zero to indicate all channels. + - when sending `warning`: + - MAY set `channel_id` to all zero if the warning is not related to a specific channel. + - MAY close the connection after sending. - MAY send an empty `data` field. - when failure was caused by an invalid signature check: - SHOULD include the raw, hex-encoded transaction in reply to a `funding_created`, `funding_signed`, `closing_signed`, or `commitment_signed` message. - - when `channel_id` is 0: - - MUST fail all channels with the receiving node. - - MUST close the connection. - - MUST set `len` equal to the length of `data`. The receiving node: - upon receiving `error`: - - MUST fail the channel referred to by the error message, if that channel is with the sending node. - - if no existing channel is referred to by the message: + - if `channel_id` is all zero: + - MUST fail all channels with the sending node. + - otherwise: + - MUST fail the channel referred to by `channel_id`, if that channel is with the sending node. + - upon receiving `warning`: + - SHOULD log the message for later diagnosis. + - MAY disconnect. + - MAY reconnect after some delay to retry. + - MAY attempt `shutdown` if permitted at this point. + - if no existing channel is referred to by `channel_id`: - MUST ignore the message. - - MUST truncate `len` to the remainder of the packet (if it's larger). - if `data` is not composed solely of printable ASCII characters (For reference: the printable character set includes byte values 32 through 126, inclusive): - SHOULD NOT print out `data` verbatim. @@ -354,6 +381,11 @@ connection. It's also useful to describe protocol violations for diagnosis, as this indicates that one peer has a bug. +On the other hand, overuse of error messages has lead to +implementations ignoring them (to avoid an otherwise expensive channel +break), so the "warning" message was added to allow some degree of +retry or recovery for spurious errors. + It may be wise not to distinguish errors in production settings, lest it leak information — hence, the optional `data` field. @@ -389,9 +421,8 @@ - MUST NOT set `ignored` to sensitive data such as secrets or portions of initialized memory. - if it doesn't receive a corresponding `pong`: - - MAY terminate the network connection, + - MAY close the network connection, - and MUST NOT fail the channels in this case. - - SHOULD NOT send `ping` messages more often than once every 30 seconds. A node sending a `pong` message: - SHOULD set `ignored` to 0s. @@ -399,7 +430,6 @@ memory. A node receiving a `ping` message: - - SHOULD fail the channels if it has received significantly in excess of one `ping` per 30 seconds. - if `num_pong_bytes` is less than 65532: - MUST respond by sending a `pong` message, with `byteslen` equal to `num_pong_bytes`. - otherwise (`num_pong_bytes` is **not** less than 65532): @@ -407,7 +437,7 @@ A node receiving a `pong` message: - if `byteslen` does not correspond to any `ping`'s `num_pong_bytes` value it has sent: - - MAY fail the channels. + - MAY close the connection. ### Rationale @@ -913,13 +943,13 @@ The following `init` messages are valid: - `0x001000000000`: no extension provided -- `0x00100000000001012a030104`: the extension contains two _odd_ TLV records (with types `0x01` and `0x03`) +- `0x001000000000c9012acb0104`: the extension contains two unknown _odd_ TLV records (with types `0xc9` and `0xcb`) The following `init` messages are invalid: - `0x00100000000001`: the extension is present but truncated -- `0x00100000000002012a`: the extension contains unknown _even_ TLV records (assuming that TLV type `0x02` is unknown) -- `0x001000000000010101010102`: the extension TLV stream is invalid (duplicate TLV record type `0x01`) +- `0x001000000000ca012a`: the extension contains unknown _even_ TLV records (assuming that TLV type `0xca` is unknown) +- `0x001000000000c90101c90102`: the extension TLV stream is invalid (duplicate TLV record type `0xc9`) Note that when messages are signed, the _extension_ is part of the signed bytes. Nodes should store the _extension_ bytes even if they don't understand them to diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py index cb11f894225b..3377756caf44 100644 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "186" -__gitversion__ = "38abac62065172c00722dca10e7d3fc3049afd72" +__post_version__ = "222" +__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py index b6a9305891eb..66ba1fbb4e22 100644 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py @@ -234,6 +234,8 @@ - MAY include `upfront_shutdown_script`. - if it includes `open_channel_tlvs`: - MUST include `upfront_shutdown_script`. + - if `option_channel_type` is negotiated: + - MUST set `channel_type` - if it includes `channel_type`: - MUST set it to a defined type representing the type it wants. - MUST use the smallest bitmap possible to represent the channel type. @@ -253,13 +255,14 @@ - discard the previous `open_channel` message. The receiving node MAY fail the channel if: + - `option_channel_type` was negotiated but the message doesn't include a `channel_type` - `announce_channel` is `false` (`0`), yet it wishes to publicly announce the channel. - `funding_satoshis` is too small. - it considers `htlc_minimum_msat` too large. - it considers `max_htlc_value_in_flight_msat` too small. - it considers `channel_reserve_satoshis` too large. - it considers `max_accepted_htlcs` too small. - - it considers `dust_limit_satoshis` too small and plans to rely on the sending node publishing its commitment transaction in the event of a data loss (see [message-retransmission](02-peer-protocol.md#message-retransmission)). + - it considers `dust_limit_satoshis` too large. The receiving node MUST fail the channel if: - the `chain_hash` value is set to a hash of a chain that is unknown to the receiver. @@ -270,6 +273,7 @@ - `funding_pubkey`, `revocation_basepoint`, `htlc_basepoint`, `payment_basepoint`, or `delayed_payment_basepoint` are not valid secp256k1 pubkeys in compressed format. - `dust_limit_satoshis` is greater than `channel_reserve_satoshis`. + - `dust_limit_satoshis` is smaller than `354 satoshis` (see [BOLT 3](03-transactions.md#dust-limits)). - the funder's amount for the initial commitment transaction is not sufficient for full [fee payment](03-transactions.md#fee-payment). - both `to_local` and `to_remote` amounts for the initial commitment transaction are less than or equal to `channel_reserve_satoshis` (see [BOLT 3](03-transactions.md#commitment-transaction-outputs)). - `funding_satoshis` is greater than or equal to 2^24 and the receiver does not support `option_support_large_channel`. @@ -280,7 +284,7 @@ #### Rationale -The requirement for `funding_satoshis` to be less than 2^24 satoshi was a temporary self-imposed limit while implementations were not yet considered stable, it can be lifted by advertising `option_support_large_channel`. +The requirement for `funding_satoshis` to be less than 2^24 satoshi was a temporary self-imposed limit while implementations were not yet considered stable, it can be lifted by advertising `option_support_large_channel`. The *channel reserve* is specified by the peer's `channel_reserve_satoshis`: 1% of the channel total is suggested. Each side of a channel maintains this reserve so it always has something to lose if it were to try to broadcast an old, revoked commitment transaction. Initially, this reserve may not be met, as only one side has funds; but the protocol ensures that there is always progress toward meeting this reserve, and once met, it is maintained. @@ -296,6 +300,10 @@ `accept_channel` ensure that both sides' `channel_reserve_satoshis` are above both `dust_limit_satoshis`. +The receiver should not accept large `dust_limit_satoshis`, as this could be +used in griefing attacks, where the peer publishes its commitment with a lot +of dust htlcs, which effectively become miner fees. + Details for how to handle a channel failure can be found in [BOLT 5:Failing a Channel](05-onchain.md#failing-a-channel). ### The `accept_channel` Message @@ -341,8 +349,8 @@ avoid double-spending of the funding transaction. - MUST set `channel_reserve_satoshis` greater than or equal to `dust_limit_satoshis` from the `open_channel` message. - MUST set `dust_limit_satoshis` less than or equal to `channel_reserve_satoshis` from the `open_channel` message. - - if it sets `channel_type`: - - MUST set it to the `channel_type` from `open_channel` + - if `option_channel_type` was negotiated: + - MUST set `channel_type` to the `channel_type` from `open_channel` The receiver: - if `minimum_depth` is unreasonably large: @@ -353,7 +361,8 @@ - MUST reject the channel. - if `channel_type` is set, and `channel_type` was set in `open_channel`, and they are not equal types: - MUST reject the channel. - + - if `option_channel_type` was negotiated but the message doesn't include a `channel_type`: + - MAY reject the channel. Other fields have the same requirements as their counterparts in `open_channel`. @@ -386,7 +395,8 @@ The recipient: - if `signature` is incorrect OR non-compliant with LOW-S-standard rule[LOWS](https://github.com/bitcoin/bitcoin/pull/6769): - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. #### Rationale @@ -432,7 +442,8 @@ The recipient: - if `signature` is incorrect OR non-compliant with LOW-S-standard rule[LOWS](https://github.com/bitcoin/bitcoin/pull/6769): - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - MUST NOT broadcast the funding transaction before receipt of a valid `funding_signed`. - on receipt of a valid `funding_signed`: - SHOULD broadcast the funding transaction. @@ -450,7 +461,7 @@ `option_anchors_zero_fee_htlc_tx` is considered superior to `option_anchor_outputs`, which again is considered superior to -`option_static_remotekey`, and the superior one is is favored if more than one +`option_static_remotekey`, and the superior one is favored if more than one is negotiated. ### The `funding_locked` Message @@ -478,7 +489,7 @@ transaction after a timeout of 2016 blocks. From the point of waiting for `funding_locked` onward, either node MAY -fail the channel if it does not receive a required response from the +send an `error` and fail the channel if it does not receive a required response from the other node after a reasonable timeout. #### Rationale @@ -544,25 +555,23 @@ - MUST send the same value in `scriptpubkey`. - MUST set `scriptpubkey` in one of the following forms: - 1. `OP_DUP` `OP_HASH160` `20` 20-bytes `OP_EQUALVERIFY` `OP_CHECKSIG` - (pay to pubkey hash), OR - 2. `OP_HASH160` `20` 20-bytes `OP_EQUAL` (pay to script hash), OR - 3. `OP_0` `20` 20-bytes (version 0 pay to witness pubkey hash), OR - 4. `OP_0` `32` 32-bytes (version 0 pay to witness script hash), OR - 5. if (and only if) `option_shutdown_anysegwit` is negotiated: + 1. `OP_0` `20` 20-bytes (version 0 pay to witness pubkey hash), OR + 2. `OP_0` `32` 32-bytes (version 0 pay to witness script hash), OR + 3. if (and only if) `option_shutdown_anysegwit` is negotiated: * `OP_1` through `OP_16` inclusive, followed by a single push of 2 to 40 bytes (witness program versions 1 through 16) A receiving node: - if it hasn't received a `funding_signed` (if it is a funder) or a `funding_created` (if it is a fundee): - - SHOULD fail the connection + - SHOULD send an `error` and fail the channel. - if the `scriptpubkey` is not in one of the above forms: - - SHOULD fail the connection. + - SHOULD send a `warning`. - if it hasn't sent a `funding_locked` yet: - MAY reply to a `shutdown` message with a `shutdown` - once there are no outstanding updates on the peer, UNLESS it has already sent a `shutdown`: - MUST reply to a `shutdown` message with a `shutdown` - if both nodes advertised the `option_upfront_shutdown_script` feature, and the receiving node received a non-zero-length `shutdown_scriptpubkey` in `open_channel` or `accept_channel`, and that `shutdown_scriptpubkey` is not equal to `scriptpubkey`: + - MAY send a `warning`. - MUST fail the connection. #### Rationale @@ -577,9 +586,11 @@ to the commitment transaction (in particular, `update_fee` would be possible otherwise). -The `scriptpubkey` forms include only standard forms accepted by the -Bitcoin network, which ensures the resulting transaction will -propagate to miners. +The `scriptpubkey` forms include only standard segwit forms accepted by +the Bitcoin network, which ensures the resulting transaction will +propagate to miners. However old nodes may send non-segwit scripts, which +may be accepted for backwards-compatibility (with a caveat to force-close +if this output doesn't meet dust relay requirements). The `option_upfront_shutdown_script` feature means that the node wanted to pre-commit to `shutdown_scriptpubkey` in case it was @@ -601,7 +612,7 @@ exchange continues until both agree on the same fee or when one side fails the channel. -In the modern method, the funder sends its permissable fee range, and the +In the modern method, the funder sends its permissible fee range, and the non-funder has to pick a fee in this range. If the non-funder chooses the same value, negotiation is complete after two messages, otherwise the funder will reply with the same value (completing after three messages). @@ -642,7 +653,8 @@ The receiving node: - if the `signature` is not valid for either variant of closing transaction specified in [BOLT #3](03-transactions.md#closing-transaction) OR non-compliant with LOW-S-standard rule[LOWS](https://github.com/bitcoin/bitcoin/pull/6769): - - MUST fail the connection. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if `fee_satoshis` is equal to its previously sent `fee_satoshis`: - SHOULD sign and broadcast the final closing transaction. - MAY close the connection. @@ -652,7 +664,7 @@ - MAY close the connection. - if the message contains a `fee_range`: - if there is no overlap between that and its own `fee_range`: - - SHOULD fail the connection + - SHOULD send a warning - MUST fail the channel if it doesn't receive a satisfying `fee_range` after a reasonable amount of time - otherwise: - if it is the funder: @@ -668,13 +680,18 @@ - MUST propose a `fee_satoshis` in the overlap between received and (about-to-be) sent `fee_range`. - otherwise, if `fee_satoshis` is not strictly between its last-sent `fee_satoshis` and its previously-received `fee_satoshis`, UNLESS it has since reconnected: - - SHOULD fail the connection. + - SHOULD send a `warning` and close the connection, or send an + `error` and fail the channel. - otherwise, if the receiver agrees with the fee: - SHOULD reply with a `closing_signed` with the same `fee_satoshis` value. - otherwise: - MUST propose a value "strictly between" the received `fee_satoshis` and its previously-sent `fee_satoshis`. +The receiving node: + - if one of the outputs in the closing transaction is below the dust limit for its `scriptpubkey` (see [BOLT 3](03-transactions.md#dust-limits)): + - MUST fail the channel + #### Rationale When `fee_range` is not provided, the "strictly between" requirement ensures @@ -691,6 +708,12 @@ that the transaction propagates. It can always use CPFP later to speed up confirmation if necessary, so that minimum should be low. +It may happen that the closing transaction doesn't meet bitcoin's default relay +policies (e.g. when using a non-segwit shutdown script for an output below 546 +satoshis, which is possible if `dust_limit_satoshis` is below 546 satoshis). +No funds are at risk when that happens, but the channel must be force-closed as +the closing transaction will likely never reach miners. + ## Normal Operation Once both nodes have exchanged `funding_locked` (and optionally [`announcement_signatures`](07-routing-gossip.md#the-announcement_signatures-message)), the channel can be used to make payments via Hashed Time Locked Contracts. @@ -873,6 +896,7 @@ - MUST NOT offer an HTLC with a timeout deadline before its `cltv_expiry`. - if an HTLC which it offered is in either node's current commitment transaction, AND is past this timeout deadline: + - SHOULD send an `error` to the receiving peer (if connected). - MUST fail the channel. A fulfilling node: @@ -881,6 +905,7 @@ - MUST fail (and not forward) an HTLC whose fulfillment deadline is already past. - if an HTLC it has fulfilled is in either node's current commitment transaction, AND is past this fulfillment deadline: + - SHOULD send an `error` to the offering peer (if connected). - MUST fail the channel. ### Adding an HTLC: `update_add_htlc` @@ -944,19 +969,24 @@ A receiving node: - receiving an `amount_msat` equal to 0, OR less than its own `htlc_minimum_msat`: - - SHOULD fail the channel. + - SHOULD send a `warning` and close the connection, or send an + `error` and fail the channel. - receiving an `amount_msat` that the sending node cannot afford at the current `feerate_per_kw` (while maintaining its channel reserve and any `to_local_anchor` and `to_remote_anchor` costs): - - SHOULD fail the channel. + - SHOULD send a `warning` and close the connection, or send an + `error` and fail the channel. - if a sending node adds more than receiver `max_accepted_htlcs` HTLCs to its local commitment transaction, OR adds more than receiver `max_htlc_value_in_flight_msat` worth of offered HTLCs to its local commitment transaction: - - SHOULD fail the channel. + - SHOULD send a `warning` and close the connection, or send an + `error` and fail the channel. - if sending node sets `cltv_expiry` to greater or equal to 500000000: - - SHOULD fail the channel. + - SHOULD send a `warning` and close the connection, or send an + `error` and fail the channel. - MUST allow multiple HTLCs with the same `payment_hash`. - if the sender did not previously acknowledge the commitment of that HTLC: - MUST ignore a repeated `id` value after a reconnection. - if other `id` violations occur: - - MAY fail the channel. + - MAY send a `warning` and close the connection, or send an + `error` and fail the channel. The `onion_routing_packet` contains an obfuscated list of hops and instructions for each hop along the path. It commits to the HTLC by setting the `payment_hash` as associated data, i.e. includes the `payment_hash` in the computation of HMACs. @@ -1042,13 +1072,16 @@ A receiving node: - if the `id` does not correspond to an HTLC in its current commitment transaction: - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if the `payment_preimage` value in `update_fulfill_htlc` doesn't SHA256 hash to the corresponding HTLC `payment_hash`: - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if the `BADONION` bit in `failure_code` is not set for `update_fail_malformed_htlc`: - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if the `sha256_of_onion` in `update_fail_malformed_htlc` doesn't match the onion it sent: - MAY retry or choose an alternate error response. @@ -1107,12 +1140,15 @@ A receiving node: - once all pending updates are applied: - if `signature` is not valid for its local commitment transaction OR non-compliant with LOW-S-standard rule [LOWS](https://github.com/bitcoin/bitcoin/pull/6769): - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if `num_htlcs` is not equal to the number of HTLC outputs in the local commitment transaction: - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if any `htlc_signature` is not valid for the corresponding HTLC transaction OR non-compliant with LOW-S-standard rule [LOWS](https://github.com/bitcoin/bitcoin/pull/6769): - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - MUST respond with a `revoke_and_ack` message. #### Rationale @@ -1166,9 +1202,10 @@ A receiving node: - if `per_commitment_secret` is not a valid secret key or does not generate the previous `per_commitment_point`: - - MUST fail the channel. + - MUST send an `error` and fail the channel. - if the `per_commitment_secret` was not generated by the protocol in [BOLT #3](03-transactions.md#per-commitment-secret-requirements): - - MAY fail the channel. + - MAY send a `warning` and close the connection, or send an + `error` and fail the channel. A node: - MUST NOT broadcast old (revoked) commitment transactions, @@ -1210,12 +1247,15 @@ A receiving node: - if the `update_fee` is too low for timely processing, OR is unreasonably large: - - SHOULD fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if the sender is not responsible for paying the Bitcoin fee: - - MUST fail the channel. + - MUST send a `warning` and close the connection, or send an + `error` and fail the channel. - if the sender cannot afford the new fee rate on the receiving node's current commitment transaction: - - SHOULD fail the channel, + - SHOULD send a `warning` and close the connection, or send an + `error` and fail the channel. - but MAY delay this check until the `update_fee` is committed. #### Rationale @@ -1315,7 +1355,7 @@ 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. - - if `option_static_remotekey` or `option_anchors` applies to the commitment + - if `option_static_remotekey` applies to the commitment transaction: - MUST set `my_current_per_commitment_point` to a valid point. - otherwise: @@ -1342,10 +1382,10 @@ - if `next_commitment_number` is not 1 greater than the commitment number of the last `commitment_signed` message the receiving node has sent: - - SHOULD fail the channel. + - SHOULD send an `error` and fail the channel. - if it has not sent `commitment_signed`, AND `next_commitment_number` is not equal to 1: - - SHOULD fail the channel. + - SHOULD send an `error` and fail the channel. - if `next_revocation_number` is equal to the commitment number of the last `revoke_and_ack` the receiving node sent, AND the receiving node hasn't already received a `closing_signed`: @@ -1357,33 +1397,32 @@ - otherwise: - if `next_revocation_number` is not equal to 1 greater than the commitment number of the last `revoke_and_ack` the receiving node has sent: - - SHOULD fail the channel. + - SHOULD send an `error` and fail the channel. - if it has not sent `revoke_and_ack`, AND `next_revocation_number` is not equal to 0: - - SHOULD fail the channel. + - SHOULD send an `error` and fail the channel. A receiving node: - - if `option_static_remotekey` or `option_anchors` applies to the commitment - transaction: + - if `option_static_remotekey` applies to the commitment transaction: - if `next_revocation_number` is greater than expected above, AND `your_last_per_commitment_secret` is correct for that `next_revocation_number` minus 1: - MUST NOT broadcast its commitment transaction. - - SHOULD fail the channel. + - SHOULD send an `error` to request the peer to fail the channel. - otherwise: - if `your_last_per_commitment_secret` does not match the expected values: - - SHOULD fail the channel. + - SHOULD send an `error` and fail the channel. - otherwise, if it supports `option_data_loss_protect`: - if `next_revocation_number` is greater than expected above, AND `your_last_per_commitment_secret` is correct for that `next_revocation_number` minus 1: - MUST NOT broadcast its commitment transaction. - - SHOULD fail the channel. + - SHOULD send an `error` to request the peer to fail the channel. - SHOULD store `my_current_per_commitment_point` to retrieve funds should the sending node broadcast its commitment transaction on-chain. - otherwise (`your_last_per_commitment_secret` or `my_current_per_commitment_point` do not match the expected values): - - SHOULD fail the channel. + - SHOULD send an `error` and fail the channel. A node: - MUST NOT assume that previously-transmitted messages were lost, @@ -1454,18 +1493,19 @@ remember a channel that never opens (and times out) than to let the funder open it while the fundee has forgotten it. -`option_data_loss_protect` was added to allow a node, which has somehow fallen behind -(e.g. has been restored from old backup), to detect that it's fallen-behind. A fallen-behind -node must know it cannot broadcast its current commitment transaction — which would lead to -total loss of funds — as the remote node can prove it knows the -revocation preimage. The error returned by the fallen-behind node -(or simply the invalid numbers in the `channel_reestablish` it has -sent) should make the other node drop its current commitment -transaction to the chain. This will, at least, allow the fallen-behind node to recover -non-HTLC funds, if the `my_current_per_commitment_point` -is valid. However, this also means the fallen-behind node has revealed this -fact (though not provably: it could be lying), and the other node could use this to -broadcast a previous state. +`option_data_loss_protect` was added to allow a node, which has somehow fallen +behind (e.g. has been restored from old backup), to detect that it has fallen +behind. A fallen-behind node must know it cannot broadcast its current +commitment transaction — which would lead to total loss of funds — as the +remote node can prove it knows the revocation preimage. The `error` returned by +the fallen-behind node should make the other node drop its current commitment +transaction to the chain. The other node should wait for that `error` to give +the fallen-behind node an opportunity to fix its state first (e.g by restarting +with a different backup). If the fallen-behind node doesn't have the latest +backup, this will, at least, allow it to recover non-HTLC funds, if the +`my_current_per_commitment_point` is valid. However, this also means the +fallen-behind node has revealed this fact (though not provably: it could be lying), +and the other node could use this to broadcast a previous state. `option_static_remotekey` removes the changing `to_remote` key, so the `my_current_per_commitment_point` is unnecessary and thus diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py index fbc4f6f7e04b..b6458142ab21 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py @@ -8,6 +8,8 @@ "tlvtype,tlv_payload,payment_data,8", "tlvdata,tlv_payload,payment_data,payment_secret,byte,32", "tlvdata,tlv_payload,payment_data,total_msat,tu64,", + "tlvtype,tlv_payload,payment_metadata,16", + "tlvdata,tlv_payload,payment_metadata,payment_metadata,byte,...", "msgtype,invalid_realm,PERM|1", "msgtype,temporary_node_failure,NODE|2", "msgtype,permanent_node_failure,PERM|NODE|2", diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py index 2140fc8212ff..fec7e9616295 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py @@ -1 +1 @@ -__csv_version__ = "2" +__csv_version__ = "3" diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py index cb11f894225b..3377756caf44 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "186" -__gitversion__ = "38abac62065172c00722dca10e7d3fc3049afd72" +__post_version__ = "222" +__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py index bfbde39c9e07..398e908a8659 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py @@ -264,6 +264,9 @@ 2. data: * [`32*byte`:`payment_secret`] * [`tu64`:`total_msat`] + 1. type: 16 (`payment_metadata`) + 2. data: + * [`...*byte`:`payment_metadata`] ### Requirements @@ -281,6 +284,9 @@ - MUST include `payment_data` - MUST set `payment_secret` to the one provided - MUST set `total_msat` to the total amount it will send + - if the recipient provided `payment_metadata`: + - MUST include `payment_metadata` with every HTLC + - MUST not apply any limits to the size of payment_metadata except the limits implied by the fixed onion size The reader: - MUST return an error if `amt_to_forward` or `outgoing_cltv_value` are not present. @@ -303,6 +309,9 @@ HTLCs; we call these outstanding HTLCs which have the same preimage, an "HTLC set". +`payment_metadata` is to be included in every payment part, so that +invalid payment details can be detected as early as possible. + #### Requirements The writer: @@ -658,7 +667,7 @@ Comparison of the computed HMAC and the packet's HMAC MUST be time-constant to avoid information leaks. -At this point, the processing node can generate a _rho_-key and a _gamma_-key. +At this point, the processing node can generate a _rho_-key. The routing information is then deobfuscated, and the information about the next hop is extracted. @@ -949,9 +958,9 @@ * [`u32`:`height`] The `payment_hash` is unknown to the final node, the `payment_secret` doesn't -match the `payment_hash`, the amount for that `payment_hash` is incorrect or +match the `payment_hash`, the amount for that `payment_hash` is incorrect, the CLTV expiry of the htlc is too close to the current block height for safe -handling. +handling or `payment_metadata` isn't present while it should be. The `htlc_msat` parameter is superfluous, but left in for backwards compatibility. The value of `htlc_msat` always matches the amount specified in diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py index a27d109ec42e..d94248c545a7 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py @@ -61,7 +61,7 @@ "msgdata,reply_channel_range,chain_hash,chain_hash,", "msgdata,reply_channel_range,first_blocknum,u32,", "msgdata,reply_channel_range,number_of_blocks,u32,", - "msgdata,reply_channel_range,full_information,byte,", + "msgdata,reply_channel_range,sync_complete,byte,", "msgdata,reply_channel_range,len,u16,", "msgdata,reply_channel_range,encoded_short_ids,byte,len", "msgdata,reply_channel_range,tlvs,reply_channel_range_tlvs,", diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py index 2140fc8212ff..fec7e9616295 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py @@ -1 +1 @@ -__csv_version__ = "2" +__csv_version__ = "3" diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py index cb11f894225b..3377756caf44 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "186" -__gitversion__ = "38abac62065172c00722dca10e7d3fc3049afd72" +__post_version__ = "222" +__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py index 96cbb4979ba8..84c430ec8a2b 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py @@ -98,9 +98,11 @@ A recipient node: - if the `short_channel_id` is NOT correct: - - SHOULD fail the channel. + - SHOULD send a `warning` and close the connection, or send an + `error` and fail the channel. - if the `node_signature` OR the `bitcoin_signature` is NOT correct: - - MAY fail the channel. + - MAY send a `warning` and close the connection, or send an + `error` and fail the channel. - if it has sent AND received a valid `announcement_signatures` message: - SHOULD queue the `channel_announcement` message for its peers. - if it has not sent funding_locked: @@ -202,7 +204,9 @@ - otherwise: - if `bitcoin_signature_1`, `bitcoin_signature_2`, `node_signature_1` OR `node_signature_2` are invalid OR NOT correct: - - SHOULD fail the connection. + - SHOULD send a `warning`. + - MAY close the connection. + - MUST ignore the message. - otherwise: - if `node_id_1` OR `node_id_2` are blacklisted: - SHOULD ignore the message. @@ -277,10 +281,7 @@ * `1`: ipv4; data = `[4:ipv4_addr][2:port]` (length 6) * `2`: ipv6; data = `[16:ipv6_addr][2:port]` (length 18) - * `3`: Tor v2 onion service; data = `[10:onion_addr][2:port]` (length 12) - * version 2 onion service addresses; Encodes an 80-bit, truncated `SHA-1` - hash of a 1024-bit `RSA` public key for the onion service (a.k.a. Tor - hidden service). + * `3`: Deprecated (length 12). Used to contain Tor v2 onion services. * `4`: Tor v3 onion service; data = `[35:onion_addr][2:port]` (length 37) * version 3 ([prop224](https://gitweb.torproject.org/torspec.git/tree/proposals/224-rend-spec-ng.txt)) onion service addresses; Encodes: @@ -313,15 +314,18 @@ - MUST set `features` according to [BOLT #9](09-features.md#assigned-features-flags) - SHOULD set `flen` to the minimum length required to hold the `features` bits it sets. + - SHOULD not announce a Tor v2 onion service. The receiving node: - if `node_id` is NOT a valid compressed public key: - - SHOULD fail the connection. + - SHOULD send a `warning`. + - MAY close the connection. - MUST NOT process the message further. - if `signature` is NOT a valid signature (using `node_id` of the double-SHA256 of the entire message following the `signature` field, including any future fields appended to the end): - - SHOULD fail the connection. + - SHOULD send a `warning`. + - MAY close the connection. - MUST NOT process the message further. - if `features` field contains _unknown even bits_: - SHOULD NOT connect to the node. @@ -332,7 +336,8 @@ defined above. - if `addrlen` is insufficient to hold the address descriptors of the known types: - - SHOULD fail the connection. + - SHOULD send a `warning`. + - MAY close the connection. - if `port` is equal to 0: - SHOULD ignore `ipv6_addr` OR `ipv4_addr`. - if `node_id` is NOT previously known from a `channel_announcement` message, @@ -346,6 +351,7 @@ - MAY choose NOT to queue messages longer than the minimum expected length. - MAY use `rgb_color` AND `alias` to reference nodes in interfaces. - SHOULD insinuate their self-signed origins. + - SHOULD ignore Tor v2 onion services. ### Rationale @@ -495,8 +501,8 @@ - if `signature` is not a valid signature, using `node_id` of the double-SHA256 of the entire message following the `signature` field (including unknown fields following `fee_proportional_millionths`): + - SHOULD send a `warning` and close the connection. - MUST NOT process the message further. - - SHOULD fail the connection. - if the specified `chain_hash` value is unknown (meaning it isn't active on the specified chain): - MUST ignore the channel update. @@ -506,7 +512,7 @@ - MAY blacklist this `node_id`. - MAY forget all channels associated with it. - if the fields below `timestamp` are equal: - - SHOULD ignore this message + - SHOULD ignore this message - if `timestamp` is lower than that of the last-received `channel_update` for this `short_channel_id` AND for `node_id`: - SHOULD ignore the message. @@ -565,21 +571,17 @@ request what gossip should be received. There are several messages which contain a long array of -`short_channel_id`s (called `encoded_short_ids`) so we utilize a -simple compression scheme: the first byte indicates the encoding, the -rest contains the data. +`short_channel_id`s (called `encoded_short_ids`) so we include an encoding byte +which allows for different encoding schemes to be defined in the future, if they +provide benefit. Encoding types: * `0`: uncompressed array of `short_channel_id` types, in ascending order. -* `1`: array of `short_channel_id` types, in ascending order, compressed with zlib deflate[1](#reference-1) +* `1`: Previously used for zlib compression, this encoding MUST NOT be used. -This encoding is also used for arrays of other types (timestamps, flags, ...), and specified with an `encoded_` prefix. For example, `encoded_timestamps` is an array of timestamps than can be either compressed (with a `1` prefix) or uncompressed (with a `0` prefix). - -Note that a 65535-byte zlib message can decompress into 67632120 -bytes[2](#reference-2), but since the only valid contents -are unique 8-byte values, no more than 14 bytes can be duplicated -across the stream: as each duplicate takes at least 2 bits, no valid -contents could decompress to more than 3669960 bytes. +This encoding is also used for arrays of other types (timestamps, flags, ...), +and specified with an `encoded_` prefix. For example, `encoded_timestamps` is +an array of timestamps with a `0` prefix. Query messages can be extended with optional fields that can help reduce the number of messages needed to synchronize routing tables by enabling: @@ -646,16 +648,21 @@ The receiver: - if the first byte of `encoded_short_ids` is not a known encoding type: - - MAY fail the connection + - MAY send a `warning`. + - MAY close the connection. - if `encoded_short_ids` does not decode into a whole number of `short_channel_id`: - - MAY fail the connection. + - MAY send a `warning`. + - MAY close the connection. - if it has not sent `reply_short_channel_ids_end` to a previously received `query_short_channel_ids` from this sender: - - MAY fail the connection. + - MAY send a `warning`. + - MAY close the connection. - if the incoming message includes `query_short_channel_ids_tlvs`: - if `encoding_type` is not a known encoding type: - - MAY fail the connection + - MAY send a `warning`. + - MAY close the connection. - if `encoded_query_flags` does not decode to exactly one flag per `short_channel_id`: - - MAY fail the connection. + - MAY send a `warning`. + - MAY close the connection. - MUST respond to each known `short_channel_id`: - if the incoming message does not include `encoded_query_flags`: - with a `channel_announcement` and the latest `channel_update` for each end @@ -775,20 +782,21 @@ The receiver of `query_channel_range`: - if it has not sent all `reply_channel_range` to a previously received `query_channel_range` from this sender: - - MAY fail the connection. + - MAY send a `warning`. + - MAY close the connection. - MUST respond with one or more `reply_channel_range`: - MUST set with `chain_hash` equal to that of `query_channel_range`, - MUST limit `number_of_blocks` to the maximum number of blocks whose results could fit in `encoded_short_ids` - MAY split block contents across multiple `reply_channel_range`. - the first `reply_channel_range` message: - - MUST set `first_blocknum` less than or equal to the `first_blocknum` in `query_channel_range` - - MUST set `first_blocknum` plus `number_of_blocks` greater than `first_blocknum` in `query_channel_range`. - - successive `reply_channel_range` message: - - MUST have `first_blocknum` equal or greater than the previous `first_blocknum`. + - MUST set `first_blocknum` less than or equal to the `first_blocknum` in `query_channel_range` + - MUST set `first_blocknum` plus `number_of_blocks` greater than `first_blocknum` in `query_channel_range`. + - successive `reply_channel_range` message: + - MUST have `first_blocknum` equal or greater than the previous `first_blocknum`. - MUST set `sync_complete` to `false` if this is not the final `reply_channel_range`. - - the final `reply_channel_range` message: - - MUST have `first_blocknum` plus `number_of_blocks` equal or greater than the `query_channel_range` `first_blocknum` plus `number_of_blocks`. + - the final `reply_channel_range` message: + - MUST have `first_blocknum` plus `number_of_blocks` equal or greater than the `query_channel_range` `first_blocknum` plus `number_of_blocks`. - MUST set `sync_complete` to `true`. If the incoming message includes `query_option`, the receiver MAY append additional information to its reply: @@ -1094,7 +1102,7 @@ 200 + ( 4999999 * 2000 / 1000000 ) = 10199 -Similarly, it would need to add B->C's `channel_update` `cltv_expiry` (20), C's +Similarly, it would need to add B->C's `channel_update` `cltv_expiry_delta` (20), C's requested `min_final_cltv_expiry` (9), and the cost for the _shadow route_ (42). Thus, A->B's `update_add_htlc` message would be: @@ -1118,10 +1126,6 @@ And D->C's `update_add_htlc` would again be the same as B->C's direct payment above. -## References - -1. [RFC 1950 "ZLIB Compressed Data Format Specification version 3.3](https://www.ietf.org/rfc/rfc1950.txt) -2. [Maximum Compression Factor](https://zlib.net/zlib_tech.html) ![Creative Commons License](https://i.creativecommons.org/l/by/4.0/88x31.png "License CC-BY")
From 535fdc06909c7a8828fb04047a187e71b0b5a15c Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 20 Apr 2022 17:15:28 -0400 Subject: [PATCH 0740/1530] More explanation of bolt csv regeneration --- doc/HACKING.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/HACKING.md b/doc/HACKING.md index 82f6813377ac..07dbeb4e4089 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -314,10 +314,13 @@ Protocol Modifications The source tree contains CSV files extracted from the v1.0 BOLT specifications (wire/extracted_peer_wire_csv and -wire/extracted_onion_wire_csv). You can regenerate these by setting -`BOLTDIR` and `BOLTVERSION` appropriately, and running `make -extract-bolt-csv`. +wire/extracted_onion_wire_csv). You can regenerate these by +first deleting the local copy(if any) at directory .tmp.lightning-rfc, +setting `BOLTDIR` and `BOLTVERSION` appropriately, and finally running `make +extract-bolt-csv`. By default the bolts will be retrieved from the +directory `../lightning-rfc` and a recent git version. +e.g., `make extract-bolt-csv BOLTDIR=../bolts BOLTVERSION=ee76043271f79f45b3392e629fd35e47f1268dc8` Further Information ------------------- From b15cf312e8aa14d1f4d0b46fb02e571e18cc0770 Mon Sep 17 00:00:00 2001 From: Gregory Sanders Date: Wed, 20 Apr 2022 17:16:48 -0400 Subject: [PATCH 0741/1530] Change lightning-rfc to bolts post repo move --- .github/scripts/build.sh | 4 ++-- Makefile | 2 +- doc/HACKING.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index bfbb97eb9e38..a08de2f23f78 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -2,7 +2,7 @@ echo "Running in $(pwd)" export ARCH=${ARCH:-64} -export BOLTDIR=lightning-rfc +export BOLTDIR=bolts export CC=${COMPILER:-gcc} export COMPAT=${COMPAT:-1} export TEST_CHECK_DBSTMTS=${TEST_CHECK_DBSTMTS:-0} @@ -25,7 +25,7 @@ pip3 install --user poetry poetry config virtualenvs.create false --local poetry install -git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc +git clone https://github.com/lightning/bolts.git ../${BOLTDIR} git submodule update --init --recursive ./configure CC="$CC" diff --git a/Makefile b/Makefile index 8eea1ec2aeab..e69e854356ff 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ PKGNAME = c-lightning CCANDIR := ccan # Where we keep the BOLT RFCs -BOLTDIR := ../lightning-rfc/ +BOLTDIR := ../bolts/ DEFAULT_BOLTVERSION := e60d594abf436e768116684080997a8d4f960263 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/doc/HACKING.md b/doc/HACKING.md index 07dbeb4e4089..05274cc4ae92 100644 --- a/doc/HACKING.md +++ b/doc/HACKING.md @@ -318,7 +318,7 @@ wire/extracted_onion_wire_csv). You can regenerate these by first deleting the local copy(if any) at directory .tmp.lightning-rfc, setting `BOLTDIR` and `BOLTVERSION` appropriately, and finally running `make extract-bolt-csv`. By default the bolts will be retrieved from the -directory `../lightning-rfc` and a recent git version. +directory `../bolts` and a recent git version. e.g., `make extract-bolt-csv BOLTDIR=../bolts BOLTVERSION=ee76043271f79f45b3392e629fd35e47f1268dc8` From 4cd6210c19485a646c65a719da9f2e64a5f58708 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 18 May 2022 11:22:28 +0200 Subject: [PATCH 0742/1530] docker: Add rust build support to Dockerfile We weren't building the Rust plugins in the `Dockerfile` since we were missing the dependencies. Now we do. Changelog-Fixed: docker: The docker images are now built with the rust plugins `cln-grpc` --- Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dockerfile b/Dockerfile index 606631b3a3d4..7fb501eeae2e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -92,6 +92,11 @@ RUN wget -q https://gmplib.org/download/gmp/gmp-6.1.2.tar.xz \ && make \ && make install && cd .. && rm gmp-6.1.2.tar.xz && rm -rf gmp-6.1.2 +ENV RUST_PROFILE=release +ENV PATH=$PATH:/root/.cargo/bin/ +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +RUN rustup toolchain install stable --component rustfmt --allow-downgrade + WORKDIR /opt/lightningd COPY . /tmp/lightning RUN git clone --recursive /tmp/lightning . && \ From 5735f71e3c1a5c23624c73c9afb4bea26eaf44e2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 18 May 2022 10:20:16 +0930 Subject: [PATCH 0743/1530] gossipd: don't ever use zlib compression on gossip. This was eliminated this morning in the latest spec. We still accept them, we just don't produce them any more. Changelog-Removed: Protocol: We no longer create gossip messages which use zlib encoding (we still understand them, for now!) Signed-off-by: Rusty Russell --- gossipd/queries.c | 62 +---------- gossipd/test/run-extended-info.c | 175 +++++-------------------------- tests/test_gossip.py | 4 +- 3 files changed, 30 insertions(+), 211 deletions(-) diff --git a/gossipd/queries.c b/gossipd/queries.c index de7d02fd6fed..9cb8241b058c 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -52,52 +52,6 @@ static void encoding_add_query_flag(u8 **encoded, bigsize_t flag) towire_bigsize(encoded, flag); } -/* Greg Maxwell asked me privately about using zlib for communicating a set, - * and suggested that we'd be better off using Golomb-Rice coding a-la BIP - * 158. However, naively using Rice encoding isn't a win: we have to get - * more complex and use separate streams. The upside is that it's between - * 2 and 5 times smaller (assuming optimal Rice encoding + gzip). We can add - * that later. */ -static u8 *zencode(const tal_t *ctx, const u8 *scids, size_t len) -{ - u8 *z; - int err; - unsigned long compressed_len = len; - -#ifdef ZLIB_EVEN_IF_EXPANDS - /* Needed for test vectors */ - compressed_len = 128 * 1024; -#endif - /* Prefer to fail if zlib makes it larger */ - z = tal_arr(ctx, u8, compressed_len); - err = compress2(z, &compressed_len, scids, len, Z_DEFAULT_COMPRESSION); - if (err == Z_OK) { - tal_resize(&z, compressed_len); - return z; - } - return NULL; -} - -/* Try compressing *encoded: fails if result would be longer. - * @off is offset to place result in *encoded. - */ -static bool encoding_end_zlib(u8 **encoded, size_t off) -{ - u8 *z; - size_t len = tal_count(*encoded); - - z = zencode(tmpctx, *encoded, len); - if (!z) - return false; - - /* Successful: copy over and trim */ - tal_resize(encoded, off + tal_count(z)); - memcpy(*encoded + off, z, tal_count(z)); - - tal_free(z); - return true; -} - static void encoding_end_no_compress(u8 **encoded, size_t off) { size_t len = tal_count(*encoded); @@ -110,12 +64,8 @@ static void encoding_end_no_compress(u8 **encoded, size_t off) * Prepends encoding type to @encoding. */ static bool encoding_end_prepend_type(u8 **encoded, size_t max_bytes) { - if (encoding_end_zlib(encoded, 1)) - **encoded = ARR_ZLIB; - else { - encoding_end_no_compress(encoded, 1); - **encoded = ARR_UNCOMPRESSED; - } + encoding_end_no_compress(encoded, 1); + **encoded = ARR_UNCOMPRESSED; #if DEVELOPER if (tal_count(*encoded) > dev_max_encoding_bytes) @@ -127,12 +77,8 @@ static bool encoding_end_prepend_type(u8 **encoded, size_t max_bytes) /* Try compressing, leaving type external */ static bool encoding_end_external_type(u8 **encoded, u8 *type, size_t max_bytes) { - if (encoding_end_zlib(encoded, 0)) - *type = ARR_ZLIB; - else { - encoding_end_no_compress(encoded, 0); - *type = ARR_UNCOMPRESSED; - } + encoding_end_no_compress(encoded, 0); + *type = ARR_UNCOMPRESSED; return tal_count(*encoded) <= max_bytes; } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 83debe2643c2..845525a26f67 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -1,7 +1,5 @@ #include "config.h" -#define ZLIB_EVEN_IF_EXPANDS 1 - #include "../queries.c" #include #include @@ -112,7 +110,8 @@ void status_fmt(enum log_level level UNNEEDED, { } -static const char *test_vectors[] = { +/* These we can reproduce */ +static const char *test_vectors_nozlib[] = { "{\n" " \"msg\" : {\n" " \"type\" : \"QueryChannelRange\",\n" @@ -137,34 +136,6 @@ static const char *test_vectors[] = { " \"msg\" : {\n" " \"type\" : \"ReplyChannelRange\",\n" " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" - " \"firstBlockNum\" : 756230,\n" - " \"numberOfBlocks\" : 1500,\n" - " \"complete\" : 1,\n" - " \"shortChannelIds\" : {\n" - " \"encoding\" : \"UNCOMPRESSED\",\n" - " \"array\" : [ \"0x0x142\", \"0x0x15465\", \"0x69x42692\" ]\n" - " }\n" - " },\n" - " \"hex\" : \"01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000b8a06000005dc01001900000000000000008e0000000000003c69000000000045a6c4\"\n" - "}\n", - "{\n" - " \"msg\" : {\n" - " \"type\" : \"ReplyChannelRange\",\n" - " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" - " \"firstBlockNum\" : 1600,\n" - " \"numberOfBlocks\" : 110,\n" - " \"complete\" : 1,\n" - " \"shortChannelIds\" : {\n" - " \"encoding\" : \"COMPRESSED_ZLIB\",\n" - " \"array\" : [ \"0x0x142\", \"0x0x15465\", \"0x4x3318\" ]\n" - " }\n" - " },\n" - " \"hex\" : \"01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000006400000006e01001601789c636000833e08659309a65878be010010a9023a\"\n" - "}\n", - "{\n" - " \"msg\" : {\n" - " \"type\" : \"ReplyChannelRange\",\n" - " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" " \"firstBlockNum\" : 122334,\n" " \"numberOfBlocks\" : 1500,\n" " \"complete\" : 1,\n" @@ -202,45 +173,6 @@ static const char *test_vectors[] = { "}\n", "{\n" " \"msg\" : {\n" - " \"type\" : \"ReplyChannelRange\",\n" - " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" - " \"firstBlockNum\" : 122334,\n" - " \"numberOfBlocks\" : 1500,\n" - " \"complete\" : 1,\n" - " \"shortChannelIds\" : {\n" - " \"encoding\" : \"COMPRESSED_ZLIB\",\n" - " \"array\" : [ \"0x0x12355\", \"0x7x30934\", \"0x70x57793\" ]\n" - " },\n" - " \"timestamps\" : {\n" - " \"encoding\" : \"COMPRESSED_ZLIB\",\n" - " \"timestamps\" : [ {\n" - " \"timestamp1\" : 164545,\n" - " \"timestamp2\" : 948165\n" - " }, {\n" - " \"timestamp1\" : 489645,\n" - " \"timestamp2\" : 4786864\n" - " }, {\n" - " \"timestamp1\" : 46456,\n" - " \"timestamp2\" : 9788415\n" - " } ]\n" - " },\n" - " \"checksums\" : {\n" - " \"checksums\" : [ {\n" - " \"checksum1\" : 1111,\n" - " \"checksum2\" : 2222\n" - " }, {\n" - " \"checksum1\" : 3333,\n" - " \"checksum2\" : 4444\n" - " }, {\n" - " \"checksum1\" : 5555,\n" - " \"checksum2\" : 6666\n" - " } ]\n" - " }\n" - " },\n" - " \"hex\" : \"01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e22060001ddde000005dc01001801789c63600001036730c55e710d4cbb3d3c080017c303b1012201789c63606a3ac8c0577e9481bd622d8327d7060686ad150c53a3ff0300554707db031800000457000008ae00000d050000115c000015b300001a0a\"\n" - "}\n", - "{\n" - " \"msg\" : {\n" " \"type\" : \"QueryShortChannelIds\",\n" " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" " \"shortChannelIds\" : {\n" @@ -251,48 +183,6 @@ static const char *test_vectors[] = { " },\n" " \"hex\" : \"01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206001900000000000000008e0000000000003c69000000000045a6c4\"\n" "}\n", - "{\n" - " \"msg\" : {\n" - " \"type\" : \"QueryShortChannelIds\",\n" - " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" - " \"shortChannelIds\" : {\n" - " \"encoding\" : \"COMPRESSED_ZLIB\",\n" - " \"array\" : [ \"0x0x4564\", \"0x2x47550\", \"0x69x42692\" ]\n" - " },\n" - " \"extensions\" : [ ]\n" - " },\n" - " \"hex\" : \"01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206001801789c63600001c12b608a69e73e30edbaec0800203b040e\"\n" - "}\n", - "{\n" - " \"msg\" : {\n" - " \"type\" : \"QueryShortChannelIds\",\n" - " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" - " \"shortChannelIds\" : {\n" - " \"encoding\" : \"UNCOMPRESSED\",\n" - " \"array\" : [ \"0x0x12232\", \"0x0x15556\", \"0x69x42692\" ]\n" - " },\n" - " \"extensions\" : [ {\n" - " \"encoding\" : \"COMPRESSED_ZLIB\",\n" - " \"array\" : [ 1, 2, 4 ]\n" - " } ]\n" - " },\n" - " \"hex\" : \"01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e22060019000000000000002fc80000000000003cc4000000000045a6c4010c01789c6364620100000e0008\"\n" - "}\n", - "{\n" - " \"msg\" : {\n" - " \"type\" : \"QueryShortChannelIds\",\n" - " \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n" - " \"shortChannelIds\" : {\n" - " \"encoding\" : \"COMPRESSED_ZLIB\",\n" - " \"array\" : [ \"0x0x14200\", \"0x0x46645\", \"0x69x42692\" ]\n" - " },\n" - " \"extensions\" : [ {\n" - " \"encoding\" : \"COMPRESSED_ZLIB\",\n" - " \"array\" : [ 1, 2, 4 ]\n" - " } ]\n" - " },\n" - " \"hex\" : \"01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206001801789c63600001f30a30c5b0cd144cb92e3b020017c6034a010c01789c6364620100000e0008\"\n" - "}\n", }; static void get_chainhash(const char *test_vector, @@ -323,14 +213,9 @@ static u8 *get_scid_array(const tal_t *ctx, assert(json_to_short_channel_id(test_vector, t, &scid)); encoding_add_short_channel_id(&encoded, &scid); } - if (json_tok_streq(test_vector, encoding, "UNCOMPRESSED")) { - encoding_end_no_compress(&encoded, 1); - encoded[0] = ARR_UNCOMPRESSED; - } else { - assert(json_tok_streq(test_vector, encoding, "COMPRESSED_ZLIB")); - assert(encoding_end_zlib(&encoded, 1)); - encoded[0] = ARR_ZLIB; - } + assert(json_tok_streq(test_vector, encoding, "UNCOMPRESSED")); + encoding_end_no_compress(&encoded, 1); + encoded[0] = ARR_UNCOMPRESSED; return encoded; } @@ -404,17 +289,10 @@ static u8 *test_reply_channel_range(const char *test_vector, const jsmntok_t *ob &ts.timestamp_node_id_2)); encoding_add_timestamps(&tlvs->timestamps_tlv->encoded_timestamps, &ts); } - if (json_tok_streq(test_vector, encodingtok, "UNCOMPRESSED")) { - encoding_end_no_compress(&tlvs->timestamps_tlv->encoded_timestamps, - 0); - tlvs->timestamps_tlv->encoding_type = ARR_UNCOMPRESSED; - } else { - assert(json_tok_streq(test_vector, encodingtok, - "COMPRESSED_ZLIB")); - assert(encoding_end_zlib(&tlvs->timestamps_tlv->encoded_timestamps, - 0)); - tlvs->timestamps_tlv->encoding_type = ARR_ZLIB; - } + assert(json_tok_streq(test_vector, encodingtok, "UNCOMPRESSED")); + encoding_end_no_compress(&tlvs->timestamps_tlv->encoded_timestamps, + 0); + tlvs->timestamps_tlv->encoding_type = ARR_UNCOMPRESSED; } opt = json_get_member(test_vector, obj, "checksums"); @@ -464,14 +342,9 @@ get_query_flags_array(const tal_t *ctx, assert(json_to_u64(test_vector, t, &f)); encoding_add_query_flag(&tlv->encoded_query_flags, f); } - if (json_tok_streq(test_vector, encoding, "UNCOMPRESSED")) { - encoding_end_no_compress(&tlv->encoded_query_flags, 0); - tlv->encoding_type = ARR_UNCOMPRESSED; - } else { - assert(json_tok_streq(test_vector, encoding, "COMPRESSED_ZLIB")); - assert(encoding_end_zlib(&tlv->encoded_query_flags, 0)); - tlv->encoding_type = ARR_ZLIB; - } + assert(json_tok_streq(test_vector, encoding, "UNCOMPRESSED")); + encoding_end_no_compress(&tlv->encoded_query_flags, 0); + tlv->encoding_type = ARR_UNCOMPRESSED; return tlv; } @@ -509,7 +382,7 @@ int main(int argc, char *argv[]) common_setup(argv[0]); - for (size_t i = 0; i < ARRAY_SIZE(test_vectors); i++) { + for (size_t i = 0; i < ARRAY_SIZE(test_vectors_nozlib); i++) { jsmn_parser parser; u8 *m; const char *hex_m; @@ -517,22 +390,22 @@ int main(int argc, char *argv[]) jsmn_init(&parser); assert(jsmn_parse(&parser, - test_vectors[i], strlen(test_vectors[i]), + test_vectors_nozlib[i], strlen(test_vectors_nozlib[i]), toks, tal_count(toks)) > 0); - msg = json_get_member(test_vectors[i], toks, "msg"); - hex = json_get_member(test_vectors[i], toks, "hex"); - type = json_get_member(test_vectors[i], msg, "type"); - if (json_tok_streq(test_vectors[i], type, "QueryChannelRange")) - m = test_query_channel_range(test_vectors[i], msg); - else if (json_tok_streq(test_vectors[i], type, "ReplyChannelRange")) - m = test_reply_channel_range(test_vectors[i], msg); - else if (json_tok_streq(test_vectors[i], type, "QueryShortChannelIds")) - m = test_query_short_channel_ids(test_vectors[i], msg); + msg = json_get_member(test_vectors_nozlib[i], toks, "msg"); + hex = json_get_member(test_vectors_nozlib[i], toks, "hex"); + type = json_get_member(test_vectors_nozlib[i], msg, "type"); + if (json_tok_streq(test_vectors_nozlib[i], type, "QueryChannelRange")) + m = test_query_channel_range(test_vectors_nozlib[i], msg); + else if (json_tok_streq(test_vectors_nozlib[i], type, "ReplyChannelRange")) + m = test_reply_channel_range(test_vectors_nozlib[i], msg); + else if (json_tok_streq(test_vectors_nozlib[i], type, "QueryShortChannelIds")) + m = test_query_short_channel_ids(test_vectors_nozlib[i], msg); else abort(); hex_m = tal_hex(m, m); - assert(json_tok_streq(test_vectors[i], hex, hex_m)); + assert(json_tok_streq(test_vectors_nozlib[i], hex, hex_m)); tal_free(m); } tal_free(toks); diff --git a/tests/test_gossip.py b/tests/test_gossip.py index e5d76d6affac..0b1cd7619bc2 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -872,7 +872,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): filters=['0109', '0012']) assert len(msgs) == 2 - # This should actually be large enough for zlib to kick in! + # This used to be large enough for zlib to kick in, but no longer! scid34, _ = l3.fundchannel(l4, 10**5) mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) l2.daemon.wait_for_log('Received node_announcement for node ' + l4.info['id']) @@ -886,7 +886,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): genesis_blockhash, 0, 65535, filters=['0109', '0012']) - encoded = subprocess.run(['devtools/mkencoded', '--scids', '01', scid12, scid23, scid34], + encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12, scid23, scid34], check=True, timeout=TIMEOUT, stdout=subprocess.PIPE).stdout.strip().decode() From a4c365f8d07872c8b8461d0b48c8c04dae0a63e5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 18 May 2022 10:21:16 +0930 Subject: [PATCH 0744/1530] gossipd: neaten code now we don't have to prepend prefix after. We can simply start with a '0' where encoding is prefixed, so simplify internal interface. Signed-off-by: Rusty Russell --- gossipd/queries.c | 62 ++++++++++++-------------------- gossipd/test/run-extended-info.c | 11 ++---- 2 files changed, 26 insertions(+), 47 deletions(-) diff --git a/gossipd/queries.c b/gossipd/queries.c index 9cb8241b058c..a1ec4dcffe17 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -27,9 +27,15 @@ static u32 dev_max_encoding_bytes = -1U; * simple compression scheme: the first byte indicates the encoding, the * rest contains the data. */ -static u8 *encoding_start(const tal_t *ctx) +static u8 *encoding_start(const tal_t *ctx, bool prepend_encoding) { - return tal_arr(ctx, u8, 0); + u8 *ret; + if (prepend_encoding) { + ret = tal_arr(ctx, u8, 1); + ret[0] = ARR_UNCOMPRESSED; + } else + ret = tal_arr(ctx, u8, 0); + return ret; } /* Marshal a single short_channel_id */ @@ -52,35 +58,13 @@ static void encoding_add_query_flag(u8 **encoded, bigsize_t flag) towire_bigsize(encoded, flag); } -static void encoding_end_no_compress(u8 **encoded, size_t off) -{ - size_t len = tal_count(*encoded); - - tal_resize(encoded, off + len); - memmove(*encoded + off, *encoded, len); -} - -/* Once we've assembled it, try compressing. - * Prepends encoding type to @encoding. */ -static bool encoding_end_prepend_type(u8 **encoded, size_t max_bytes) +static bool encoding_end(const u8 *encoded, size_t max_bytes) { - encoding_end_no_compress(encoded, 1); - **encoded = ARR_UNCOMPRESSED; - #if DEVELOPER - if (tal_count(*encoded) > dev_max_encoding_bytes) + if (tal_count(encoded) > dev_max_encoding_bytes) return false; #endif - return tal_count(*encoded) <= max_bytes; -} - -/* Try compressing, leaving type external */ -static bool encoding_end_external_type(u8 **encoded, u8 *type, size_t max_bytes) -{ - encoding_end_no_compress(encoded, 0); - *type = ARR_UNCOMPRESSED; - - return tal_count(*encoded) <= max_bytes; + return tal_count(encoded) <= max_bytes; } /* Query this peer for these short-channel-ids. */ @@ -126,7 +110,7 @@ bool query_short_channel_ids(struct daemon *daemon, if (peer->scid_query_outstanding) return false; - encoded = encoding_start(tmpctx); + encoded = encoding_start(tmpctx, true); for (size_t i = 0; i < tal_count(scids); i++) { /* BOLT #7: * @@ -139,7 +123,7 @@ bool query_short_channel_ids(struct daemon *daemon, encoding_add_short_channel_id(&encoded, &scids[i]); } - if (!encoding_end_prepend_type(&encoded, max_encoded_bytes)) { + if (!encoding_end(encoded, max_encoded_bytes)) { status_broken("query_short_channel_ids: %zu is too many", tal_count(scids)); return false; @@ -150,15 +134,15 @@ bool query_short_channel_ids(struct daemon *daemon, tlvs = tlv_query_short_channel_ids_tlvs_new(tmpctx); tlvq = tlvs->query_flags = tal(tlvs, struct tlv_query_short_channel_ids_tlvs_query_flags); - tlvq->encoded_query_flags = encoding_start(tlvq); + tlvq->encoding_type = ARR_UNCOMPRESSED; + tlvq->encoded_query_flags = encoding_start(tlvq, false); for (size_t i = 0; i < tal_count(query_flags); i++) encoding_add_query_flag(&tlvq->encoded_query_flags, query_flags[i]); max_encoded_bytes -= tal_bytelen(encoded); - if (!encoding_end_external_type(&tlvq->encoded_query_flags, - &tlvq->encoding_type, - max_encoded_bytes)) { + if (!encoding_end(tlvq->encoded_query_flags, + max_encoded_bytes)) { status_broken("query_short_channel_ids:" " %zu query_flags is too many", tal_count(query_flags)); @@ -305,24 +289,24 @@ static void send_reply_channel_range(struct peer *peer, * - MUST limit `number_of_blocks` to the maximum number of blocks * whose results could fit in `encoded_short_ids` */ - u8 *encoded_scids = encoding_start(tmpctx); - u8 *encoded_timestamps = encoding_start(tmpctx); + u8 *encoded_scids = encoding_start(tmpctx, true); + u8 *encoded_timestamps = encoding_start(tmpctx, false); struct tlv_reply_channel_range_tlvs *tlvs = tlv_reply_channel_range_tlvs_new(tmpctx); /* Encode them all */ for (size_t i = 0; i < num_scids; i++) encoding_add_short_channel_id(&encoded_scids, &scids[i]); - encoding_end_prepend_type(&encoded_scids, tal_bytelen(encoded_scids)); + encoding_end(encoded_scids, tal_bytelen(encoded_scids)); if (tstamps) { for (size_t i = 0; i < num_scids; i++) encoding_add_timestamps(&encoded_timestamps, &tstamps[i]); tlvs->timestamps_tlv = tal(tlvs, struct tlv_reply_channel_range_tlvs_timestamps_tlv); - encoding_end_external_type(&encoded_timestamps, - &tlvs->timestamps_tlv->encoding_type, - tal_bytelen(encoded_timestamps)); + tlvs->timestamps_tlv->encoding_type = ARR_UNCOMPRESSED; + encoding_end(encoded_timestamps, + tal_bytelen(encoded_timestamps)); tlvs->timestamps_tlv->encoded_timestamps = tal_steal(tlvs, encoded_timestamps); } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 845525a26f67..a89dabd99a8a 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -206,7 +206,7 @@ static u8 *get_scid_array(const tal_t *ctx, scids = json_get_member(test_vector, obj, "shortChannelIds"); arr = json_get_member(test_vector, scids, "array"); - encoded = encoding_start(ctx); + encoded = encoding_start(ctx, true); encoding = json_get_member(test_vector, scids, "encoding"); json_for_each_arr(i, t, arr) { struct short_channel_id scid; @@ -214,8 +214,6 @@ static u8 *get_scid_array(const tal_t *ctx, encoding_add_short_channel_id(&encoded, &scid); } assert(json_tok_streq(test_vector, encoding, "UNCOMPRESSED")); - encoding_end_no_compress(&encoded, 1); - encoded[0] = ARR_UNCOMPRESSED; return encoded; } @@ -274,7 +272,7 @@ static u8 *test_reply_channel_range(const char *test_vector, const jsmntok_t *ob = tal(tlvs, struct tlv_reply_channel_range_tlvs_timestamps_tlv); tlvs->timestamps_tlv->encoded_timestamps - = encoding_start(tlvs->timestamps_tlv); + = encoding_start(tlvs->timestamps_tlv, false); encodingtok = json_get_member(test_vector, opt, "encoding"); tstok = json_get_member(test_vector, opt, "timestamps"); json_for_each_arr(i, t, tstok) { @@ -290,8 +288,6 @@ static u8 *test_reply_channel_range(const char *test_vector, const jsmntok_t *ob encoding_add_timestamps(&tlvs->timestamps_tlv->encoded_timestamps, &ts); } assert(json_tok_streq(test_vector, encodingtok, "UNCOMPRESSED")); - encoding_end_no_compress(&tlvs->timestamps_tlv->encoded_timestamps, - 0); tlvs->timestamps_tlv->encoding_type = ARR_UNCOMPRESSED; } @@ -335,7 +331,7 @@ get_query_flags_array(const tal_t *ctx, arr = json_get_member(test_vector, opt, "array"); - tlv->encoded_query_flags = encoding_start(tlv); + tlv->encoded_query_flags = encoding_start(tlv, false); encoding = json_get_member(test_vector, opt, "encoding"); json_for_each_arr(i, t, arr) { bigsize_t f; @@ -343,7 +339,6 @@ get_query_flags_array(const tal_t *ctx, encoding_add_query_flag(&tlv->encoded_query_flags, f); } assert(json_tok_streq(test_vector, encoding, "UNCOMPRESSED")); - encoding_end_no_compress(&tlv->encoded_query_flags, 0); tlv->encoding_type = ARR_UNCOMPRESSED; return tlv; From bf040c398b1dc6c42c9b3df3ac74bb786db56a99 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 18 May 2022 10:28:58 +0930 Subject: [PATCH 0745/1530] Makefile: update to BOLTs without zlib. This contains a typo fix and a clarification on channel_type, but also removes ZLIB. Signed-off-by: Rusty Russell --- Makefile | 2 +- channeld/commit_tx.c | 2 +- common/decode_array.c | 6 +++--- common/decode_array.h | 4 ++-- common/initial_commit_tx.c | 2 +- devtools/mkencoded.c | 2 +- devtools/print_wire.c | 4 ++-- gossipd/queries.c | 6 ++---- openingd/openingd.c | 4 ++-- 9 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index e69e854356ff..70975595b8bc 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := e60d594abf436e768116684080997a8d4f960263 +DEFAULT_BOLTVERSION := c1b94dfad12d9e29268cbd0d3b22686ea5709cbc # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index 2fdf0df1ac62..b1f0ae51f8a6 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -379,7 +379,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, /* BOLT #3: * * 9. Sort the outputs into [BIP 69+CLTV - * order](#transaction-input-and-output-ordering) + * order](#transaction-output-ordering) */ permute_outputs(tx, cltvs, (const void **)*htlcmap); diff --git a/common/decode_array.c b/common/decode_array.c index 7296d072a687..157eea48d68c 100644 --- a/common/decode_array.c +++ b/common/decode_array.c @@ -42,7 +42,7 @@ struct short_channel_id *decode_short_ids(const tal_t *ctx, const u8 *encoded) */ type = fromwire_u8(&encoded, &max); switch (type) { - case ARR_ZLIB: + case ARR_ZLIB_DEPRECATED: encoded = unzlib(tmpctx, encoded, max); if (!encoded) return NULL; @@ -85,7 +85,7 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx, * - MAY close the connection. */ switch (qf->encoding_type) { - case ARR_ZLIB: + case ARR_ZLIB_DEPRECATED: encoded = unzlib(tmpctx, encoded, max); if (!encoded) return NULL; @@ -119,7 +119,7 @@ decode_channel_update_timestamps(const tal_t *ctx, /* FIXME: BOLT #7 should have a requirements like it does for * query_short_channel_ids_tlvs! */ switch (timestamps_tlv->encoding_type) { - case ARR_ZLIB: + case ARR_ZLIB_DEPRECATED: encoded = unzlib(tmpctx, encoded, max); if (!encoded) return NULL; diff --git a/common/decode_array.h b/common/decode_array.h index c1f09c4bf7e0..697e8ddee19f 100644 --- a/common/decode_array.h +++ b/common/decode_array.h @@ -11,11 +11,11 @@ struct tlv_reply_channel_range_tlvs_timestamps_tlv; * * Encoding types: * * `0`: uncompressed array of `short_channel_id` types, in ascending order. - * * `1`: array of `short_channel_id` types, in ascending order, compressed with zlib deflate[1](#reference-1) + * * `1`: Previously used for zlib compression, this encoding MUST NOT be used. */ enum arr_encode_types { ARR_UNCOMPRESSED = 0, - ARR_ZLIB = 1 + ARR_ZLIB_DEPRECATED = 1 }; struct short_channel_id *decode_short_ids(const tal_t *ctx, const u8 *encoded); diff --git a/common/initial_commit_tx.c b/common/initial_commit_tx.c index 44203f7f4bd6..1c319ceb1dfe 100644 --- a/common/initial_commit_tx.c +++ b/common/initial_commit_tx.c @@ -291,7 +291,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, /* BOLT #3: * * 9. Sort the outputs into [BIP 69+CLTV - * order](#transaction-input-and-output-ordering) + * order](#transaction-output-ordering) */ permute_outputs(tx, NULL, output_order); diff --git a/devtools/mkencoded.c b/devtools/mkencoded.c index f76d2ab817ed..76a2bc20a4e0 100644 --- a/devtools/mkencoded.c +++ b/devtools/mkencoded.c @@ -42,7 +42,7 @@ int main(int argc, char *argv[]) if (encoding == ARR_UNCOMPRESSED) printf("%02x%s\n", encoding, tal_hex(NULL, data)); - else if (encoding == ARR_ZLIB) { + else if (encoding == ARR_ZLIB_DEPRECATED) { /* https://www.zlib.net/zlib_tech.html: * the only expansion is an overhead of five bytes per 16 KB * block (about 0.03%), plus a one-time overhead of six bytes diff --git a/devtools/print_wire.c b/devtools/print_wire.c index f8b4acc1feaa..742b72de6e32 100644 --- a/devtools/print_wire.c +++ b/devtools/print_wire.c @@ -189,7 +189,7 @@ static bool printwire_encoded_short_ids(const u8 **cursor, size_t *plen, size_t case ARR_UNCOMPRESSED: printf(" (UNCOMPRESSED)"); break; - case ARR_ZLIB: + case ARR_ZLIB_DEPRECATED: printf(" (ZLIB)"); break; default: @@ -202,7 +202,7 @@ static bool printwire_encoded_short_ids(const u8 **cursor, size_t *plen, size_t /* If it was unknown, that's different from corrupt */ if (len == 0 || arr[0] == ARR_UNCOMPRESSED - || arr[0] == ARR_ZLIB) { + || arr[0] == ARR_ZLIB_DEPRECATED) { printf(" **CORRUPT**"); return true; } else { diff --git a/gossipd/queries.c b/gossipd/queries.c index a1ec4dcffe17..cb5a61d115ba 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -23,9 +23,8 @@ static u32 dev_max_encoding_bytes = -1U; /* BOLT #7: * * There are several messages which contain a long array of - * `short_channel_id`s (called `encoded_short_ids`) so we utilize a - * simple compression scheme: the first byte indicates the encoding, the - * rest contains the data. + * `short_channel_id`s (called `encoded_short_ids`) so we include an encoding + * byte which allows for different encoding schemes to be defined in the future */ static u8 *encoding_start(const tal_t *ctx, bool prepend_encoding) { @@ -117,7 +116,6 @@ bool query_short_channel_ids(struct daemon *daemon, * Encoding types: * * `0`: uncompressed array of `short_channel_id` types, in * ascending order. - * * `1`: array of `short_channel_id` types, in ascending order */ assert(i == 0 || scids[i].u64 > scids[i-1].u64); encoding_add_short_channel_id(&encoded, &scids[i]); diff --git a/openingd/openingd.c b/openingd/openingd.c index 5a83ae1c1c9e..b929a998f9b0 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1000,8 +1000,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) accept_tlvs->upfront_shutdown_script = state->upfront_shutdown_script[LOCAL]; /* BOLT #2: - * - if it sets `channel_type`: - * - MUST set it to the `channel_type` from `open_channel` + * - if `option_channel_type` was negotiated: + * - MUST set `channel_type` to the `channel_type` from `open_channel` */ accept_tlvs->channel_type = state->channel_type->features; From 685fa25756bf42a611dbeb8af6eb49159b09c3e3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 18 May 2022 10:29:04 +0930 Subject: [PATCH 0746/1530] Makefile: update bolts to include remote_pubkey change. Only affects comments. Signed-off-by: Rusty Russell --- Makefile | 2 +- channeld/commit_tx.c | 2 +- common/initial_commit_tx.c | 2 +- onchaind/onchaind.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 70975595b8bc..c6345fc4ee2d 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := c1b94dfad12d9e29268cbd0d3b22686ea5709cbc +DEFAULT_BOLTVERSION := c4a0369e705ad43babee50dd0466f162567e6427 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index b1f0ae51f8a6..54116c42944e 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -298,7 +298,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * If `option_anchors` applies to the commitment * transaction, the `to_remote` output is encumbered by a one * block csv lock. - * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY + * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY * *... * Otherwise, this output is a simple P2WPKH to `remotepubkey`. diff --git a/common/initial_commit_tx.c b/common/initial_commit_tx.c index 1c319ceb1dfe..97afedb15059 100644 --- a/common/initial_commit_tx.c +++ b/common/initial_commit_tx.c @@ -236,7 +236,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * If `option_anchors` applies to the commitment * transaction, the `to_remote` output is encumbered by a one * block csv lock. - * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY + * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY * *... * Otherwise, this output is a simple P2WPKH to `remotepubkey`. diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 7d10e36a4b5c..6859d327d4e4 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -2522,7 +2522,7 @@ static u8 *scriptpubkey_to_remote(const tal_t *ctx, * If `option_anchors` applies to the commitment * transaction, the `to_remote` output is encumbered by a one * block csv lock. - * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY + * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY * *... * Otherwise, this output is a simple P2WPKH to `remotepubkey`. From abd01a1701a1e38e511299ef44af8b9503e709bb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 18 May 2022 10:52:41 +0930 Subject: [PATCH 0747/1530] Makefile: update to include fix for remote_addr generation. Now it's formatted properly, we don't need the patch. But we need to explicitly marshal/unmarshal into a byte stream, which involves some code rearrangement. Signed-off-by: Rusty Russell --- Makefile | 2 +- connectd/peer_exchange_initmsg.c | 46 ++++++++++++++---------- wire/extracted_peer_01_remote_addr.patch | 13 ------- wire/peer_wire.csv | 2 +- 4 files changed, 29 insertions(+), 34 deletions(-) delete mode 100644 wire/extracted_peer_01_remote_addr.patch diff --git a/Makefile b/Makefile index c6345fc4ee2d..ba8e5d8504f5 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := c4a0369e705ad43babee50dd0466f162567e6427 +DEFAULT_BOLTVERSION := 105c2e5e9f17c68e8c19dc4ca548600a0b8f66f0 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index eea73188a52c..f43b66164825 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -91,29 +91,37 @@ static struct io_plan *peer_init_received(struct io_conn *conn, /* fetch optional tlv `remote_addr` */ remote_addr = NULL; - /* BOLT-remote-address #1: + /* BOLT #1: * The receiving node: * ... - * - MAY use the `remote_addr` to update its `node_annoucement` + * - MAY use the `remote_addr` to update its `node_announcement` */ if (tlvs->remote_addr) { - switch (tlvs->remote_addr->type) { - case ADDR_TYPE_IPV4: - case ADDR_TYPE_IPV6: + const u8 *cursor = tlvs->remote_addr; + size_t len = tal_bytelen(tlvs->remote_addr); + + remote_addr = tal(peer, struct wireaddr); + if (fromwire_wireaddr(&cursor, &len, remote_addr)) { + switch (remote_addr->type) { + case ADDR_TYPE_IPV4: + case ADDR_TYPE_IPV6: #if DEVELOPER /* ignore private addresses (non-DEVELOPER builds) */ - if (address_routable(tlvs->remote_addr, true)) + if (!address_routable(remote_addr, true)) #else - if (address_routable(tlvs->remote_addr, false)) + if (!address_routable(remote_addr, false)) #endif /* DEVELOPER */ - remote_addr = tal_steal(peer, tlvs->remote_addr); - break; - /* We are only interested in IP addresses */ - case ADDR_TYPE_TOR_V2_REMOVED: - case ADDR_TYPE_TOR_V3: - case ADDR_TYPE_DNS: - case ADDR_TYPE_WEBSOCKET: - break; - } + remote_addr = tal_free(remote_addr); + break; + /* We are only interested in IP addresses */ + case ADDR_TYPE_TOR_V2_REMOVED: + case ADDR_TYPE_TOR_V3: + case ADDR_TYPE_DNS: + case ADDR_TYPE_WEBSOCKET: + remote_addr = tal_free(remote_addr); + break; + } + } else + remote_addr = tal_free(remote_addr); } /* The globalfeatures field is now unused, but there was a @@ -217,7 +225,7 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, /* set optional tlv `remote_addr` on incoming IP connections */ tlvs->remote_addr = NULL; - /* BOLT-remote-address #1: + /* BOLT #1: * The sending node: * ... * - SHOULD set `remote_addr` to reflect the remote IP address (and port) of an @@ -229,8 +237,8 @@ struct io_plan *peer_exchange_initmsg(struct io_conn *conn, switch (addr->u.wireaddr.type) { case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: - tlvs->remote_addr = tal(tlvs, struct wireaddr); - *tlvs->remote_addr = addr->u.wireaddr; + tlvs->remote_addr = tal_arr(tlvs, u8, 0); + towire_wireaddr(&tlvs->remote_addr, &addr->u.wireaddr); break; /* Only report IP addresses back for now */ case ADDR_TYPE_TOR_V2_REMOVED: diff --git a/wire/extracted_peer_01_remote_addr.patch b/wire/extracted_peer_01_remote_addr.patch deleted file mode 100644 index 5e9aeb46348e..000000000000 --- a/wire/extracted_peer_01_remote_addr.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv -index a028ddc66..4043c6350 100644 ---- a/wire/peer_wire.csv -+++ b/wire/peer_wire.csv -@@ -6,6 +6,8 @@ msgdata,init,features,byte,flen - msgdata,init,tlvs,init_tlvs, - tlvtype,init_tlvs,networks,1 - tlvdata,init_tlvs,networks,chains,chain_hash,... -+tlvtype,init_tlvs,remote_addr,3 -+tlvdata,init_tlvs,remote_addr,remote_addr,wireaddr, - msgtype,error,17 - msgdata,error,channel_id,channel_id, - msgdata,error,len,u16, diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index a028ddc66d92..497d43b52fe8 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -7,7 +7,7 @@ msgdata,init,tlvs,init_tlvs, tlvtype,init_tlvs,networks,1 tlvdata,init_tlvs,networks,chains,chain_hash,... tlvtype,init_tlvs,remote_addr,3 -tlvdata,init_tlvs,remote_addr,remote_addr,wireaddr, +tlvdata,init_tlvs,remote_addr,data,byte,... msgtype,error,17 msgdata,error,channel_id,channel_id, msgdata,error,len,u16, From c7d359baf455e5260dc5d126b05211279e186ef8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 18 May 2022 10:53:22 +0930 Subject: [PATCH 0748/1530] cln-grpc: API updates after 8dd51d127fff01b9302009906dcbdc83ea3b6548 Changing the JSON schemas changes this, and I didn't rebuild! Signed-off-by: Rusty Russell --- .msggen.json | 1 + cln-grpc/proto/node.proto | 1 + cln-grpc/src/convert.rs | 1 + cln-rpc/src/model.rs | 2 ++ 4 files changed, 5 insertions(+) diff --git a/.msggen.json b/.msggen.json index 48a398f0dbb2..a664e3346919 100644 --- a/.msggen.json +++ b/.msggen.json @@ -580,6 +580,7 @@ "ListFunds.outputs[].blockheight": 8, "ListFunds.outputs[].output": 2, "ListFunds.outputs[].redeemscript": 6, + "ListFunds.outputs[].reserved": 9, "ListFunds.outputs[].scriptpubkey": 4, "ListFunds.outputs[].status": 7, "ListFunds.outputs[].txid": 1 diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index bf66a9baa930..a368f2078026 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -272,6 +272,7 @@ message ListfundsOutputs { optional string address = 5; optional bytes redeemscript = 6; ListfundsOutputsStatus status = 7; + bool reserved = 9; optional uint32 blockheight = 8; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index a7be55089482..c5c919d6c09e 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -188,6 +188,7 @@ impl From<&responses::ListfundsOutputs> for pb::ListfundsOutputs { address: c.address.clone(), // Rule #2 for type string? redeemscript: c.redeemscript.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? status: c.status as i32, + reserved: c.reserved.clone(), // Rule #2 for type boolean blockheight: c.blockheight.clone(), // Rule #2 for type u32? } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index db51cb0e5cdb..61697c4f848d 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1286,6 +1286,8 @@ pub mod responses { // Path `ListFunds.outputs[].status` #[serde(rename = "status")] pub status: ListfundsOutputsStatus, + #[serde(alias = "reserved")] + pub reserved: bool, #[serde(alias = "blockheight", skip_serializing_if = "Option::is_none")] pub blockheight: Option, } From 0153621b32056f0451d8ed4e48893c97c9df2844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20A=2EP?= Date: Fri, 20 May 2022 11:56:05 +0200 Subject: [PATCH 0749/1530] Fixes #5276 by using as docker base image debian bullseye instead of buster Changelog-Fixed: Upgrade docker base image from Debian buster to bullseye to work with glibc 2.29+ #5276 --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7fb501eeae2e..c99742f5bab2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ # * final: Copy the binaries required at runtime # The resulting image uploaded to dockerhub will only contain what is needed for runtime. # From the root of the repository, run "docker build -t yourimage:yourtag ." -FROM debian:buster-slim as downloader +FROM debian:bullseye-slim as downloader RUN set -ex \ && apt-get update \ @@ -45,7 +45,7 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ && tar -xzvf litecoin.tar.gz $BD/litecoin-cli --strip-components=1 --exclude=*-qt \ && rm litecoin.tar.gz -FROM debian:buster-slim as builder +FROM debian:bullseye-slim as builder ENV LIGHTNINGD_VERSION=master RUN apt-get update -qq && \ @@ -111,7 +111,7 @@ RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/inst RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install -FROM debian:buster-slim as final +FROM debian:bullseye-slim as final COPY --from=downloader /opt/tini /usr/bin/tini RUN apt-get update && apt-get install -y --no-install-recommends socat inotify-tools python3 python3-pip libpq5\ From f1d0325620514c22a5c1fd03d6cdc89180201bc3 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 27 Apr 2022 18:35:21 +0200 Subject: [PATCH 0750/1530] topo: Fix issue considering the wrong direction for capacity limits Changelog-Fixed: topology: Under some circumstances we were considering the limits on the wrong direction for a channel --- plugins/topology.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/topology.c b/plugins/topology.c index 0d8851319656..e0807da3db91 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -557,9 +557,9 @@ static struct amount_msat peer_capacity(const struct gossmap *gossmap, continue; if (!c->half[!dir].enabled) continue; - if (!amount_msat_add(&capacity, capacity, - amount_msat(fp16_to_u64(c->half[dir] - .htlc_max)))) + if (!amount_msat_add( + &capacity, capacity, + amount_msat(fp16_to_u64(c->half[!dir].htlc_max)))) continue; } return capacity; From 74ddc154350ee447841d009de2556b5b6bc11cfb Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 6 May 2022 14:10:45 +0200 Subject: [PATCH 0751/1530] route: Do not require both directions to be active This likely lead to a number of false errors when attempting to route. We deemed a channel to be unusable as soon as either direction isn't usable. This is bad since it excludes not only zeroconf channels (which have different scids for the two directions), but it also excludes any channel that we haven't seen an update from yet. This was likely introduced when attemting to exclude nodes that haven't sent a disable, but their peer has, but this is not necessary as the unresponsive node would be marked as isolated by all its peers, so we don't need to artificially mark a channel direction as disabled when really we can't even enter the node to traverse the channel in that direction. Changelog-Fixed: routing: Fixed an issue where we would exclude the entire channel if either direction was disabled, or we hadn't seen an update yet. --- common/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/route.c b/common/route.c index 04d590643882..3ad9b4e964ee 100644 --- a/common/route.c +++ b/common/route.c @@ -25,7 +25,7 @@ bool route_can_carry(const struct gossmap *map, struct amount_msat amount, void *arg) { - if (!c->half[dir].enabled || !c->half[!dir].enabled) + if (!c->half[dir].enabled) return false; return route_can_carry_even_disabled(map, c, dir, amount, arg); } From 572942c783a58e518f0a1b449412a82717594636 Mon Sep 17 00:00:00 2001 From: Jon Griffiths Date: Tue, 7 Jun 2022 15:39:30 +1200 Subject: [PATCH 0752/1530] psbt: use DER encoded + sighash byte for PSBT_IN_PARTIAL_SIG items Per BIP-0171, the signature map is of pubkey to "The signature as would be pushed to the stack from a scriptSig or witness". Fixes 5298 Changelog-Fixed: PSBT: Fix signature encoding to comply with BIP-0171. Signed-off-by: Jon Griffiths --- bitcoin/psbt.c | 6 ++++-- bitcoin/test/run-bitcoin_block_from_hex.c | 18 ++++++++++++++++++ bitcoin/test/run-tx-encode.c | 18 ++++++++++++++++++ channeld/test/run-commit_tx.c | 6 ------ channeld/test/run-full_channel.c | 6 ------ cli/test/run-human-mode.c | 6 ------ cli/test/run-large-input.c | 6 ------ cli/test/run-remove-hint.c | 6 ------ common/test/run-route-specific.c | 6 ------ common/test/run-route.c | 6 ------ common/test/run-route_blinding_test.c | 6 ------ connectd/test/run-onion_message.c | 6 ------ lightningd/test/run-invoice-select-inchan.c | 3 --- plugins/test/run-funder_policy.c | 6 ------ plugins/test/run-route-overlong.c | 6 ------ wallet/test/run-wallet.c | 3 --- 16 files changed, 40 insertions(+), 74 deletions(-) diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index 990621104228..a079fe49b223 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -268,18 +268,20 @@ bool psbt_input_set_signature(struct wally_psbt *psbt, size_t in, const struct bitcoin_signature *sig) { u8 pk_der[PUBKEY_CMPR_LEN]; + u8 sig_der[73]; + size_t sig_len; bool ok; assert(in < psbt->num_inputs); /* we serialize the compressed version of the key, wally likes this */ pubkey_to_der(pk_der, pubkey); + sig_len = signature_to_der(sig_der, sig); tal_wally_start(); wally_psbt_input_set_sighash(&psbt->inputs[in], sig->sighash_type); ok = wally_psbt_input_add_signature(&psbt->inputs[in], pk_der, sizeof(pk_der), - sig->s.data, - sizeof(sig->s.data)) == WALLY_OK; + sig_der, sig_len) == WALLY_OK; tal_wally_end(psbt); return ok; } diff --git a/bitcoin/test/run-bitcoin_block_from_hex.c b/bitcoin/test/run-bitcoin_block_from_hex.c index 96d4e51201b2..9c85afe28b4a 100644 --- a/bitcoin/test/run-bitcoin_block_from_hex.c +++ b/bitcoin/test/run-bitcoin_block_from_hex.c @@ -2,6 +2,7 @@ #include "../block.c" #include "../psbt.c" #include "../shadouble.c" +#include "../signature.c" #include "../tx.c" #include "../varint.c" #include @@ -46,12 +47,22 @@ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy /* Generated stub for fromwire_fail */ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } /* Generated stub for fromwire_sha256 */ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } /* Generated stub for is_anchor_witness_script */ bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } @@ -67,12 +78,19 @@ void script_push_bytes(u8 **scriptp UNNEEDED, const void *mem UNNEEDED, size_t l /* Generated stub for scriptpubkey_p2wsh */ u8 *scriptpubkey_p2wsh(const tal_t *ctx UNNEEDED, const u8 *witnessscript UNNEEDED) { fprintf(stderr, "scriptpubkey_p2wsh called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } /* Generated stub for towire_u8_array */ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_u8_array called!\n"); abort(); } diff --git a/bitcoin/test/run-tx-encode.c b/bitcoin/test/run-tx-encode.c index 1aae8e5815de..e84b4e5a0863 100644 --- a/bitcoin/test/run-tx-encode.c +++ b/bitcoin/test/run-tx-encode.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -47,12 +48,22 @@ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy /* Generated stub for fromwire_fail */ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } /* Generated stub for fromwire_sha256 */ void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } /* Generated stub for fromwire_u32 */ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } /* Generated stub for is_anchor_witness_script */ bool is_anchor_witness_script(const u8 *script UNNEEDED, size_t script_len UNNEEDED) { fprintf(stderr, "is_anchor_witness_script called!\n"); abort(); } @@ -68,12 +79,19 @@ void script_push_bytes(u8 **scriptp UNNEEDED, const void *mem UNNEEDED, size_t l /* Generated stub for scriptpubkey_p2wsh */ u8 *scriptpubkey_p2wsh(const tal_t *ctx UNNEEDED, const u8 *witnessscript UNNEEDED) { fprintf(stderr, "scriptpubkey_p2wsh called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } /* Generated stub for towire_u8_array */ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_u8_array called!\n"); abort(); } diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index 07fa485af69d..f222e7ae3d59 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -30,9 +30,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for status_fmt */ void status_fmt(enum log_level level UNNEEDED, const struct node_id *peer UNNEEDED, @@ -48,9 +45,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* bitcoind loves its backwards txids! */ diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index ae8da9cd0a51..2a5112dee2d0 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -16,9 +16,6 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, const tal_t *)){ } @@ -35,9 +32,6 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ void status_fmt(enum log_level level UNUSED, diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index a9d038bb04c1..e88b220a3854 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -78,9 +78,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -113,9 +110,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for version_and_exit */ char *version_and_exit(const void *unused UNNEEDED) { fprintf(stderr, "version_and_exit called!\n"); abort(); } diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index 7f04e68cc360..1bb043bf16a2 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -78,9 +78,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -113,9 +110,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for version_and_exit */ char *version_and_exit(const void *unused UNNEEDED) { fprintf(stderr, "version_and_exit called!\n"); abort(); } diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index 00dd49acbcd6..3246db8a3917 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -81,9 +81,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -116,9 +113,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* Generated stub for version_and_exit */ char *version_and_exit(const void *unused UNNEEDED) { fprintf(stderr, "version_and_exit called!\n"); abort(); } diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index e5aa986cb04b..01d12f94f86a 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -34,9 +34,6 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED, const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } @@ -48,9 +45,6 @@ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, const void *record UNNEEDED) { fprintf(stderr, "towire_tlv called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static void write_to_store(int store_fd, const u8 *msg) diff --git a/common/test/run-route.c b/common/test/run-route.c index 74aaa1eee013..33b3668c3760 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -27,9 +27,6 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED, const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } @@ -41,9 +38,6 @@ void towire_tlv(u8 **pptr UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, const void *record UNNEEDED) { fprintf(stderr, "towire_tlv called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static void write_to_store(int store_fd, const u8 *msg) diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c index b798cd0c55b9..6c2f9bbd3aae 100644 --- a/common/test/run-route_blinding_test.c +++ b/common/test/run-route_blinding_test.c @@ -69,9 +69,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -109,9 +106,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static u8 *json_to_enctlvs(const tal_t *ctx, diff --git a/connectd/test/run-onion_message.c b/connectd/test/run-onion_message.c index 4e9083318041..afd8d0b817dc 100644 --- a/connectd/test/run-onion_message.c +++ b/connectd/test/run-onion_message.c @@ -88,9 +88,6 @@ bool fromwire_connectd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UN /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for inject_peer_msg */ void inject_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) { fprintf(stderr, "inject_peer_msg called!\n"); abort(); } @@ -169,9 +166,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 towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ /* Updated each time, as we pretend to be Alice, Bob, Carol */ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 976af4bb8f54..e064deb24cb8 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -485,9 +485,6 @@ struct channel *new_unsaved_channel(struct peer *peer UNNEEDED, /* Generated stub for node_id_cmp */ int node_id_cmp(const struct node_id *a UNNEEDED, const struct node_id *b UNNEEDED) { fprintf(stderr, "node_id_cmp called!\n"); abort(); } -/* Generated stub for node_id_to_hexstr */ -char *node_id_to_hexstr(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED) -{ fprintf(stderr, "node_id_to_hexstr called!\n"); abort(); } /* Generated stub for notify_connect */ void notify_connect(struct lightningd *ld UNNEEDED, const struct node_id *nodeid UNNEEDED, diff --git a/plugins/test/run-funder_policy.c b/plugins/test/run-funder_policy.c index 0a48176bcae0..a91a5867615e 100644 --- a/plugins/test/run-funder_policy.c +++ b/plugins/test/run-funder_policy.c @@ -15,9 +15,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } @@ -27,9 +24,6 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ struct test_case { diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 773db648f2c8..2ca73fd9452e 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -24,9 +24,6 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_wireaddr */ -bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for json_add_amount_msat_compat */ void json_add_amount_msat_compat(struct json_stream *result UNNEEDED, struct amount_msat msat UNNEEDED, @@ -227,9 +224,6 @@ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_wireaddr */ -void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) -{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ #ifndef SUPERVERBOSE diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index eee265e4f0ac..005f4150de64 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -333,9 +333,6 @@ void json_add_node_id(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "json_add_node_id called!\n"); abort(); } -/* Generated stub for json_add_null */ -void json_add_null(struct json_stream *stream UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_add_null called!\n"); abort(); } /* Generated stub for json_add_num */ void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, unsigned int value UNNEEDED) From ecdfbbf3594d77ffdcf8c0179e7ea36bc2876e26 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 16 Jun 2022 17:02:28 +0930 Subject: [PATCH 0753/1530] connectd: restore gossip filter aging. When we moved gossip filtering to connectd, this aging got lost. Without this, we hit the 10,000 entry limit before expiring full gossip anti-echo cache. This is under 1M in allocations per peer, but in DEVELOPER mode each allocation includes adds 3 notifiers (32 bytes each) and a backtrace child (40 + 40 + 256 bytes), making it almost 10MB per peer, plus allocation overhead. Signed-off-by: Rusty Russell Changelog-Fixed: connectd: large memory usage with many peers fixed. --- connectd/multiplex.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 54740a7b3c4f..1f50ab6831a6 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -344,6 +344,9 @@ static struct io_plan *encrypt_and_send(struct peer *peer, /* Kicks off write_to_peer() to look for more gossip to send from store */ static void wake_gossip(struct peer *peer) { + /* Don't remember sent per-peer gossip forever. */ + gossip_rcvd_filter_age(peer->gs.grf); + peer->gs.active = IFDEV(!peer->daemon->dev_suppress_gossip, true); io_wake(peer->peer_outq); From 7c8dc620359f6d3e614551e9491c70e9d07e2d31 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 16 Jun 2022 17:02:39 +0930 Subject: [PATCH 0754/1530] channeld: take over gossip_rcvd_filter.c and is_msg_gossip_broadcast. channeld is the only user of these functions, since it now streams all gossip itself. Signed-off-by: Rusty Russell --- channeld/Makefile | 1 - closingd/Makefile | 1 - common/Makefile | 1 - common/gossip_store.c | 1 - connectd/Makefile | 2 +- connectd/connectd.c | 1 - {common => connectd}/gossip_rcvd_filter.c | 2 +- {common => connectd}/gossip_rcvd_filter.h | 6 +- connectd/multiplex.c | 56 ++++++++++++++++++- .../test/run-gossip_rcvd_filter.c | 5 +- devtools/Makefile | 1 - gossipd/Makefile | 1 - lightningd/Makefile | 1 - openingd/Makefile | 1 - openingd/dualopend.c | 1 - openingd/openingd.c | 1 - wire/peer_wire.c | 54 ------------------ wire/peer_wire.h | 2 - 18 files changed, 64 insertions(+), 74 deletions(-) rename {common => connectd}/gossip_rcvd_filter.c (98%) rename {common => connectd}/gossip_rcvd_filter.h (81%) rename {common => connectd}/test/run-gossip_rcvd_filter.c (98%) diff --git a/channeld/Makefile b/channeld/Makefile index bdab8ffa0c13..1b9a7317c200 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -48,7 +48,6 @@ CHANNELD_COMMON_OBJS := \ common/ecdh_hsmd.o \ common/features.o \ common/fee_states.o \ - common/gossip_rcvd_filter.o \ common/peer_io.o \ common/peer_status_wiregen.o \ common/status_wiregen.o \ diff --git a/closingd/Makefile b/closingd/Makefile index 7ee284fbb98e..b7da71f42ef9 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -30,7 +30,6 @@ CLOSINGD_COMMON_OBJS := \ common/daemon.o \ common/daemon_conn.o \ common/derive_basepoints.o \ - common/gossip_rcvd_filter.o \ common/gossip_store.o \ common/htlc_wire.o \ common/key_derive.o \ diff --git a/common/Makefile b/common/Makefile index 86f1588b421b..3665baf4f4ca 100644 --- a/common/Makefile +++ b/common/Makefile @@ -34,7 +34,6 @@ COMMON_SRC_NOGEN := \ common/features.c \ common/fee_states.c \ common/fp16.c \ - common/gossip_rcvd_filter.c \ common/gossip_store.c \ common/gossmap.c \ common/hash_u5.c \ diff --git a/common/gossip_store.c b/common/gossip_store.c index eba8a5ab03c4..b36897aca653 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -1,6 +1,5 @@ #include "config.h" #include -#include #include #include #include diff --git a/connectd/Makefile b/connectd/Makefile index 8b1ea8907606..684349c41ed6 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -5,6 +5,7 @@ CONNECTD_HEADERS := connectd/connectd_wiregen.h \ connectd/connectd.h \ connectd/peer_exchange_initmsg.h \ connectd/handshake.h \ + connectd/gossip_rcvd_filter.h \ connectd/multiplex.h \ connectd/netaddress.h \ connectd/onion_message.h \ @@ -55,7 +56,6 @@ CONNECTD_COMMON_OBJS := \ common/hmac.o \ common/status_wiregen.o \ common/gossip_store.o \ - common/gossip_rcvd_filter.o \ common/key_derive.o \ common/memleak.o \ common/msg_queue.o \ diff --git a/connectd/connectd.c b/connectd/connectd.c index e20bf7f7f2ae..13a4bffbe050 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include diff --git a/common/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c similarity index 98% rename from common/gossip_rcvd_filter.c rename to connectd/gossip_rcvd_filter.c index 0c6b3d350ff0..914bd03f8c6b 100644 --- a/common/gossip_rcvd_filter.c +++ b/connectd/gossip_rcvd_filter.c @@ -1,9 +1,9 @@ #include "config.h" #include #include -#include #include #include +#include #include static u64 msg_key(const u8 *msg) diff --git a/common/gossip_rcvd_filter.h b/connectd/gossip_rcvd_filter.h similarity index 81% rename from common/gossip_rcvd_filter.h rename to connectd/gossip_rcvd_filter.h index a2495efa4a48..1a7d0a21ce4e 100644 --- a/common/gossip_rcvd_filter.h +++ b/connectd/gossip_rcvd_filter.h @@ -1,7 +1,7 @@ /* This implements a cheap gossip cache, so we can recognize what gossip * msgs this peer sent us, thus avoid retransmitting gossip it sent. */ -#ifndef LIGHTNING_COMMON_GOSSIP_RCVD_FILTER_H -#define LIGHTNING_COMMON_GOSSIP_RCVD_FILTER_H +#ifndef LIGHTNING_CONNECTD_GOSSIP_RCVD_FILTER_H +#define LIGHTNING_CONNECTD_GOSSIP_RCVD_FILTER_H #include "config.h" #include #include @@ -19,4 +19,4 @@ bool gossip_rcvd_filter_del(struct gossip_rcvd_filter *map, const u8 *msg); /* Flush out old entries. */ void gossip_rcvd_filter_age(struct gossip_rcvd_filter *map); -#endif /* LIGHTNING_COMMON_GOSSIP_RCVD_FILTER_H */ +#endif /* LIGHTNING_CONNECTD_GOSSIP_RCVD_FILTER_H */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 1f50ab6831a6..331d1b680630 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -23,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -640,6 +640,60 @@ static bool handle_custommsg(struct daemon *daemon, } } +static bool is_msg_gossip_broadcast(const u8 *cursor) +{ + switch ((enum peer_wire)fromwire_peektype(cursor)) { + case WIRE_CHANNEL_ANNOUNCEMENT: + case WIRE_NODE_ANNOUNCEMENT: + case WIRE_CHANNEL_UPDATE: + return true; + case WIRE_QUERY_SHORT_CHANNEL_IDS: + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: + case WIRE_QUERY_CHANNEL_RANGE: + case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_WARNING: + case WIRE_INIT: + case WIRE_PING: + case WIRE_PONG: + case WIRE_ERROR: + case WIRE_OPEN_CHANNEL: + case WIRE_ACCEPT_CHANNEL: + case WIRE_FUNDING_CREATED: + case WIRE_FUNDING_SIGNED: + case WIRE_FUNDING_LOCKED: + case WIRE_SHUTDOWN: + case WIRE_CLOSING_SIGNED: + case WIRE_UPDATE_ADD_HTLC: + case WIRE_UPDATE_FULFILL_HTLC: + case WIRE_UPDATE_FAIL_HTLC: + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + case WIRE_COMMITMENT_SIGNED: + case WIRE_REVOKE_AND_ACK: + case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: + case WIRE_CHANNEL_REESTABLISH: + case WIRE_ANNOUNCEMENT_SIGNATURES: + case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_TX_ADD_INPUT: + case WIRE_TX_REMOVE_INPUT: + case WIRE_TX_ADD_OUTPUT: + case WIRE_TX_REMOVE_OUTPUT: + case WIRE_TX_COMPLETE: + case WIRE_TX_SIGNATURES: + case WIRE_OPEN_CHANNEL2: + case WIRE_ACCEPT_CHANNEL2: + case WIRE_INIT_RBF: + case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif + break; + } + return false; +} + /* We handle pings and gossip messages. */ static bool handle_message_locally(struct peer *peer, const u8 *msg) { diff --git a/common/test/run-gossip_rcvd_filter.c b/connectd/test/run-gossip_rcvd_filter.c similarity index 98% rename from common/test/run-gossip_rcvd_filter.c rename to connectd/test/run-gossip_rcvd_filter.c index 473229bdec88..7a6dfd3dbb7a 100644 --- a/common/test/run-gossip_rcvd_filter.c +++ b/connectd/test/run-gossip_rcvd_filter.c @@ -1,7 +1,7 @@ #include "config.h" #include "../gossip_rcvd_filter.c" -#include "../pseudorand.c" #include "../../wire/fromwire.c" +#include #include #include @@ -53,6 +53,9 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, /* Generated stub for towire_sha256 */ void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) { fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } /* Generated stub for towire_u32 */ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) { fprintf(stderr, "towire_u32 called!\n"); abort(); } diff --git a/devtools/Makefile b/devtools/Makefile index ba529da0f66f..35ba6da51ced 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -25,7 +25,6 @@ DEVTOOLS_COMMON_OBJS := \ common/decode_array.o \ common/features.o \ common/fee_states.o \ - common/gossip_rcvd_filter.o \ common/hash_u5.o \ common/hmac.o \ common/htlc_state.o \ diff --git a/gossipd/Makefile b/gossipd/Makefile index c31f3486c6d1..2a2b8a40f2f0 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -44,7 +44,6 @@ GOSSIPD_COMMON_OBJS := \ common/ecdh_hsmd.o \ common/features.o \ common/status_wiregen.o \ - common/gossip_rcvd_filter.o \ common/key_derive.o \ common/lease_rates.o \ common/memleak.o \ diff --git a/lightningd/Makefile b/lightningd/Makefile index 8876dc55c929..f1f1607ea388 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -92,7 +92,6 @@ LIGHTNINGD_COMMON_OBJS := \ common/peer_status_wiregen.o \ common/status_levels.o \ common/status_wiregen.o \ - common/gossip_rcvd_filter.o \ common/hash_u5.o \ common/hmac.o \ common/hsm_encryption.o \ diff --git a/openingd/Makefile b/openingd/Makefile index 608268399655..1da236ac3df5 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -47,7 +47,6 @@ OPENINGD_COMMON_OBJS := \ common/derive_basepoints.o \ common/features.o \ common/fee_states.o \ - common/gossip_rcvd_filter.o \ common/gossip_store.o \ common/htlc_state.o \ common/htlc_wire.o \ diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 2afcc4993586..8c425af01b5c 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/openingd/openingd.c b/openingd/openingd.c index b929a998f9b0..8870c34d9919 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/wire/peer_wire.c b/wire/peer_wire.c index 35089e48f1d7..0aca55a5d488 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -109,60 +109,6 @@ bool is_msg_for_gossipd(const u8 *cursor) return false; } -bool is_msg_gossip_broadcast(const u8 *cursor) -{ - switch ((enum peer_wire)fromwire_peektype(cursor)) { - case WIRE_CHANNEL_ANNOUNCEMENT: - case WIRE_NODE_ANNOUNCEMENT: - case WIRE_CHANNEL_UPDATE: - return true; - case WIRE_QUERY_SHORT_CHANNEL_IDS: - case WIRE_REPLY_SHORT_CHANNEL_IDS_END: - case WIRE_QUERY_CHANNEL_RANGE: - case WIRE_REPLY_CHANNEL_RANGE: - case WIRE_ONION_MESSAGE: - case WIRE_OBS2_ONION_MESSAGE: - case WIRE_WARNING: - case WIRE_INIT: - case WIRE_PING: - case WIRE_PONG: - case WIRE_ERROR: - case WIRE_OPEN_CHANNEL: - case WIRE_ACCEPT_CHANNEL: - case WIRE_FUNDING_CREATED: - case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: - case WIRE_SHUTDOWN: - case WIRE_CLOSING_SIGNED: - case WIRE_UPDATE_ADD_HTLC: - case WIRE_UPDATE_FULFILL_HTLC: - case WIRE_UPDATE_FAIL_HTLC: - case WIRE_UPDATE_FAIL_MALFORMED_HTLC: - case WIRE_COMMITMENT_SIGNED: - case WIRE_REVOKE_AND_ACK: - case WIRE_UPDATE_FEE: - case WIRE_UPDATE_BLOCKHEIGHT: - case WIRE_CHANNEL_REESTABLISH: - case WIRE_ANNOUNCEMENT_SIGNATURES: - case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_TX_ADD_INPUT: - case WIRE_TX_REMOVE_INPUT: - case WIRE_TX_ADD_OUTPUT: - case WIRE_TX_REMOVE_OUTPUT: - case WIRE_TX_COMPLETE: - case WIRE_TX_SIGNATURES: - case WIRE_OPEN_CHANNEL2: - case WIRE_ACCEPT_CHANNEL2: - case WIRE_INIT_RBF: - case WIRE_ACK_RBF: -#if EXPERIMENTAL_FEATURES - case WIRE_STFU: -#endif - break; - } - return false; -} - /* Return true if it's an unknown ODD message. cursor is a tal ptr. */ bool is_unknown_msg_discardable(const u8 *cursor) { diff --git a/wire/peer_wire.h b/wire/peer_wire.h index d57a84d20bd7..12c951b8ff3e 100644 --- a/wire/peer_wire.h +++ b/wire/peer_wire.h @@ -23,8 +23,6 @@ bool is_unknown_msg_discardable(const u8 *cursor); /* Return true if it's a message for gossipd. */ bool is_msg_for_gossipd(const u8 *cursor); -/* Return true if it's a gossip update or announcement. */ -bool is_msg_gossip_broadcast(const u8 *cursor); /* Extract channel_id from various packets, return true if possible. */ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id); From 87a471af982ddce932e9facbc86f75a17bdcb0fc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 16 Jun 2022 17:02:39 +0930 Subject: [PATCH 0755/1530] connectd: use is_msg_gossip_broadcast into gossip_rcvd_filter.c It was doing its own equivalent check anyway. Signed-off-by: Rusty Russell --- connectd/gossip_rcvd_filter.c | 60 ++++++++++++++++++++++++++++++++--- connectd/multiplex.c | 57 +-------------------------------- 2 files changed, 56 insertions(+), 61 deletions(-) diff --git a/connectd/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c index 914bd03f8c6b..87e75890e81b 100644 --- a/connectd/gossip_rcvd_filter.c +++ b/connectd/gossip_rcvd_filter.c @@ -54,13 +54,63 @@ struct gossip_rcvd_filter *new_gossip_rcvd_filter(const tal_t *ctx) return f; } -static bool extract_msg_key(const u8 *msg, u64 *key) +static bool is_msg_gossip_broadcast(const u8 *cursor) { - int type = fromwire_peektype(msg); + switch ((enum peer_wire)fromwire_peektype(cursor)) { + case WIRE_CHANNEL_ANNOUNCEMENT: + case WIRE_NODE_ANNOUNCEMENT: + case WIRE_CHANNEL_UPDATE: + return true; + case WIRE_QUERY_SHORT_CHANNEL_IDS: + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: + case WIRE_QUERY_CHANNEL_RANGE: + case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_ONION_MESSAGE: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_WARNING: + case WIRE_INIT: + case WIRE_PING: + case WIRE_PONG: + case WIRE_ERROR: + case WIRE_OPEN_CHANNEL: + case WIRE_ACCEPT_CHANNEL: + case WIRE_FUNDING_CREATED: + case WIRE_FUNDING_SIGNED: + case WIRE_FUNDING_LOCKED: + case WIRE_SHUTDOWN: + case WIRE_CLOSING_SIGNED: + case WIRE_UPDATE_ADD_HTLC: + case WIRE_UPDATE_FULFILL_HTLC: + case WIRE_UPDATE_FAIL_HTLC: + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + case WIRE_COMMITMENT_SIGNED: + case WIRE_REVOKE_AND_ACK: + case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: + case WIRE_CHANNEL_REESTABLISH: + case WIRE_ANNOUNCEMENT_SIGNATURES: + case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_TX_ADD_INPUT: + case WIRE_TX_REMOVE_INPUT: + case WIRE_TX_ADD_OUTPUT: + case WIRE_TX_REMOVE_OUTPUT: + case WIRE_TX_COMPLETE: + case WIRE_TX_SIGNATURES: + case WIRE_OPEN_CHANNEL2: + case WIRE_ACCEPT_CHANNEL2: + case WIRE_INIT_RBF: + case WIRE_ACK_RBF: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif + break; + } + return false; +} - if (type != WIRE_CHANNEL_ANNOUNCEMENT - && type != WIRE_NODE_ANNOUNCEMENT - && type != WIRE_CHANNEL_UPDATE) +static bool extract_msg_key(const u8 *msg, u64 *key) +{ + if (!is_msg_gossip_broadcast(msg)) return false; *key = msg_key(msg); diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 331d1b680630..e99c78e6e214 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -640,68 +640,13 @@ static bool handle_custommsg(struct daemon *daemon, } } -static bool is_msg_gossip_broadcast(const u8 *cursor) -{ - switch ((enum peer_wire)fromwire_peektype(cursor)) { - case WIRE_CHANNEL_ANNOUNCEMENT: - case WIRE_NODE_ANNOUNCEMENT: - case WIRE_CHANNEL_UPDATE: - return true; - case WIRE_QUERY_SHORT_CHANNEL_IDS: - case WIRE_REPLY_SHORT_CHANNEL_IDS_END: - case WIRE_QUERY_CHANNEL_RANGE: - case WIRE_REPLY_CHANNEL_RANGE: - case WIRE_ONION_MESSAGE: - case WIRE_OBS2_ONION_MESSAGE: - case WIRE_WARNING: - case WIRE_INIT: - case WIRE_PING: - case WIRE_PONG: - case WIRE_ERROR: - case WIRE_OPEN_CHANNEL: - case WIRE_ACCEPT_CHANNEL: - case WIRE_FUNDING_CREATED: - case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: - case WIRE_SHUTDOWN: - case WIRE_CLOSING_SIGNED: - case WIRE_UPDATE_ADD_HTLC: - case WIRE_UPDATE_FULFILL_HTLC: - case WIRE_UPDATE_FAIL_HTLC: - case WIRE_UPDATE_FAIL_MALFORMED_HTLC: - case WIRE_COMMITMENT_SIGNED: - case WIRE_REVOKE_AND_ACK: - case WIRE_UPDATE_FEE: - case WIRE_UPDATE_BLOCKHEIGHT: - case WIRE_CHANNEL_REESTABLISH: - case WIRE_ANNOUNCEMENT_SIGNATURES: - case WIRE_GOSSIP_TIMESTAMP_FILTER: - case WIRE_TX_ADD_INPUT: - case WIRE_TX_REMOVE_INPUT: - case WIRE_TX_ADD_OUTPUT: - case WIRE_TX_REMOVE_OUTPUT: - case WIRE_TX_COMPLETE: - case WIRE_TX_SIGNATURES: - case WIRE_OPEN_CHANNEL2: - case WIRE_ACCEPT_CHANNEL2: - case WIRE_INIT_RBF: - case WIRE_ACK_RBF: -#if EXPERIMENTAL_FEATURES - case WIRE_STFU: -#endif - break; - } - return false; -} - /* We handle pings and gossip messages. */ static bool handle_message_locally(struct peer *peer, const u8 *msg) { enum peer_wire type = fromwire_peektype(msg); /* We remember these so we don't rexmit them */ - if (is_msg_gossip_broadcast(msg)) - gossip_rcvd_filter_add(peer->gs.grf, msg); + gossip_rcvd_filter_add(peer->gs.grf, msg); if (type == WIRE_GOSSIP_TIMESTAMP_FILTER) { handle_gossip_timestamp_filter_in(peer, msg); From d922abeaba9e8700c6449efe836300a86d5614f7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 16 Jun 2022 17:02:39 +0930 Subject: [PATCH 0756/1530] connectd: optimize gossip_rcvd_filter. Instead of doing an allocation per entry, put the entry in directly. This means only 30 bit resolution on 32-bit machines, but if a bit of gossip gets accidently suppressed that's ok. Signed-off-by: Rusty Russell --- connectd/gossip_rcvd_filter.c | 40 ++++++++++++++--------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/connectd/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c index 87e75890e81b..82111a39f03d 100644 --- a/connectd/gossip_rcvd_filter.c +++ b/connectd/gossip_rcvd_filter.c @@ -1,19 +1,24 @@ #include "config.h" #include #include +#include #include #include #include #include -static u64 msg_key(const u8 *msg) +/* We stash raw integers into ptrs, but leave two bits for htable code to use. */ +static size_t msg_key(const u8 *msg) { - return siphash24(siphash_seed(), msg, tal_bytelen(msg)); + size_t key = siphash24(siphash_seed(), msg, tal_bytelen(msg)); + + /* Avoid 0 and 1, which are invalid in the htable code. */ + return key | 0x3; } static size_t rehash(const void *key, void *unused) { - return *(u64 *)key; + return ptr2int(key); } static void destroy_msg_map(struct htable *ht) @@ -35,22 +40,12 @@ struct gossip_rcvd_filter { struct htable *cur, *old; }; -#if DEVELOPER -static void memleak_help_gossip_rcvd_filter(struct htable *memtable, - struct gossip_rcvd_filter *grf) -{ - memleak_remove_htable(memtable, grf->cur); - memleak_remove_htable(memtable, grf->old); -} -#endif - struct gossip_rcvd_filter *new_gossip_rcvd_filter(const tal_t *ctx) { struct gossip_rcvd_filter *f = tal(ctx, struct gossip_rcvd_filter); f->cur = new_msg_map(f); f->old = new_msg_map(f); - memleak_add_helper(f, memleak_help_gossip_rcvd_filter); return f; } @@ -108,7 +103,7 @@ static bool is_msg_gossip_broadcast(const u8 *cursor) return false; } -static bool extract_msg_key(const u8 *msg, u64 *key) +static bool extract_msg_key(const u8 *msg, size_t *key) { if (!is_msg_gossip_broadcast(msg)) return false; @@ -120,30 +115,27 @@ static bool extract_msg_key(const u8 *msg, u64 *key) /* Add a gossip msg to the received map */ void gossip_rcvd_filter_add(struct gossip_rcvd_filter *f, const u8 *msg) { - u64 key; + size_t key; - /* We don't attach destructor here directly to tag; would be neat, - * but it's also an extra allocation. */ if (extract_msg_key(msg, &key)) { - htable_add(f->cur, key, tal_dup(f->cur, u64, &key)); - /* Don't let it fill up forever though. */ + htable_add(f->cur, key, int2ptr(key)); + /* Don't let it fill up forever. */ if (htable_count(f->cur) > 10000) gossip_rcvd_filter_age(f); } } /* htable is fast, but it's also horribly manual. */ -static bool msg_map_remove(struct htable *ht, u64 key) +static bool msg_map_remove(struct htable *ht, size_t key) { struct htable_iter i; - u64 *c; + void *c; for (c = htable_firstval(ht, &i, key); c; c = htable_nextval(ht, &i, key)) { - if (*c == key) { + if (ptr2int(c) == key) { htable_del(ht, key, c); - tal_free(c); return true; } } @@ -153,7 +145,7 @@ static bool msg_map_remove(struct htable *ht, u64 key) /* Is a gossip msg in the received map? (Removes it) */ bool gossip_rcvd_filter_del(struct gossip_rcvd_filter *f, const u8 *msg) { - u64 key; + size_t key; if (!extract_msg_key(msg, &key)) return false; From 0c9017fb762981dd30f6130f7821bc022c3e2936 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 16 Jun 2022 17:02:40 +0930 Subject: [PATCH 0757/1530] connectd: shrink max filter size. 10,000 per peer was too much. Signed-off-by: Rusty Russell --- connectd/gossip_rcvd_filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connectd/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c index 82111a39f03d..13ff0a2f7c0c 100644 --- a/connectd/gossip_rcvd_filter.c +++ b/connectd/gossip_rcvd_filter.c @@ -120,7 +120,7 @@ void gossip_rcvd_filter_add(struct gossip_rcvd_filter *f, const u8 *msg) if (extract_msg_key(msg, &key)) { htable_add(f->cur, key, int2ptr(key)); /* Don't let it fill up forever. */ - if (htable_count(f->cur) > 10000) + if (htable_count(f->cur) > 500) gossip_rcvd_filter_age(f); } } From 24b02c33cc2ef3ff2841430e0cef4fc786b91881 Mon Sep 17 00:00:00 2001 From: Brian Barto Date: Fri, 17 Jun 2022 00:48:48 -0400 Subject: [PATCH 0758/1530] lightning-cli plugin start - Assume default relative path When starting a plugin, if the plugin path cannot be found in absolute context, assume it is a relative path to the default plugins dir. As a result, the following now works when my_plugin.py is installed in the default plugins dir: lightning-cli plugin start my_plugin.py Also update the plugin documentation to reflect that the use of a relative path is now available. Changelog-Added: plugin start RPC subcommand now assumes relative path to default plugins dir if the path is not found in absolute context. i.e. lightning-cli plugin start my_plugin.py [ Squashed two commits into one -- RR ] --- doc/lightning-plugin.7.md | 8 +++++--- lightningd/plugin_control.c | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index 7c3032075a54..dd9b765d4ca7 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -16,9 +16,11 @@ optionally one or two parameters which describes the plugin on which the action has to be taken. The *start* command takes a path as the first parameter and will load -the plugin available from this path. Any additional parameters are -passed to the plugin. It will wait for the plugin to complete the -handshake with `lightningd` for 20 seconds at the most. +the plugin available from this path. The path can be a full path to a +plugin or a relative path to a plugin that is located in or below the +default plugins directory. Any additional parameters are passed to the +plugin. It will wait for the plugin to complete the handshake with +`lightningd` for 20 seconds at the most. The *stop* command takes a plugin name as parameter. It will kill and unload the specified plugin. diff --git a/lightningd/plugin_control.c b/lightningd/plugin_control.c index b583ef1a148e..bee3ef56c3a4 100644 --- a/lightningd/plugin_control.c +++ b/lightningd/plugin_control.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -256,6 +257,9 @@ static struct command_result *json_plugin_control(struct command *cmd, json_get_member(buffer, mod_params, "plugin") - 1, 1); } + if (access(plugin_path, X_OK) != 0) + plugin_path = path_join(cmd, + cmd->ld->plugins->default_dir, plugin_path); if (access(plugin_path, X_OK) == 0) return plugin_dynamic_start(pcmd, plugin_path, buffer, mod_params); From aac22f3cb1ee2656318b11e980337acdc156f4b7 Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Mon, 28 Feb 2022 18:59:47 -0600 Subject: [PATCH 0759/1530] devtools: Add fund_ln command to startup_regtest.sh fund_ln connects the two peers and funds a channel between them. Changelog-Added: Added fund_ln to the contrib/startup_regtest.sh --- contrib/startup_regtest.sh | 103 ++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index 280ef85f95ad..0579ec8004f5 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -134,8 +134,11 @@ start_ln() { # Kick it out of initialblockdownload if necessary if bitcoin-cli -regtest getblockchaininfo | grep -q 'initialblockdownload.*true'; then # Modern bitcoind needs createwallet + echo "Making \"default\" bitcoind wallet." bitcoin-cli -regtest createwallet default >/dev/null 2>&1 bitcoin-cli -regtest generatetoaddress 1 "$(bitcoin-cli -regtest getnewaddress)" > /dev/null + else + bitcoin-cli -regtest loadwallet default fi alias bt-cli='bitcoin-cli -regtest' @@ -145,7 +148,105 @@ start_ln() { nodes="$1" fi start_nodes "$nodes" regtest - echo " bt-cli, stop_ln" + echo " bt-cli, stop_ln, fund_nodes" +} + +ensure_bitcoind_funds() { + + if [ -z "$ADDRESS" ]; then + ADDRESS=$(bitcoin-cli "$WALLET" -regtest getnewaddress) + fi + + balance=$(bitcoin-cli -regtest "$WALLET" getbalance) + + if [ 1 -eq "$(echo "$balance"'<1' | bc -l)" ]; then + + printf "%s" "Mining into address " "$ADDRESS""... " + + bitcoin-cli -regtest generatetoaddress 100 "$ADDRESS" > /dev/null + + echo "done." + fi +} + +fund_nodes() { + + WALLET="default" + NODES="" + + for var in "$@"; do + case $var in + -w=*|--wallet=*) + WALLET="${var#*=}" + ;; + *) + NODES="${NODES:+${NODES} }${var}" + ;; + esac + done + + if [ -z "$NODES" ]; then + NODES=$(seq $node_count) + fi + + WALLET="-rpcwallet=$WALLET" + + ADDRESS=$(bitcoin-cli "$WALLET" -regtest getnewaddress) + + ensure_bitcoind_funds + + echo "bitcoind balance:" "$(bitcoin-cli -regtest "$WALLET" getbalance)" + + last_node="" + + for i in $NODES; do + + if [ -z "$last_node" ]; then + last_node=$i + continue + fi + + node1=$last_node + node2=$i + last_node=$i + + L2_NODE_ID=$($LCLI -F --lightning-dir=/tmp/l"$node2"-regtest getinfo | sed -n 's/^id=\(.*\)/\1/p') + L2_NODE_PORT=$($LCLI -F --lightning-dir=/tmp/l"$node2"-regtest getinfo | sed -n 's/^binding\[0\].port=\(.*\)/\1/p') + + $LCLI -H --lightning-dir=/tmp/l"$node1"-regtest connect "$L2_NODE_ID"@localhost:"$L2_NODE_PORT" > /dev/null + + L1_WALLET_ADDR=$($LCLI -F --lightning-dir=/tmp/l"$node1"-regtest newaddr | sed -n 's/^bech32=\(.*\)/\1/p') + + ensure_bitcoind_funds + + bitcoin-cli -regtest "$WALLET" sendtoaddress "$L1_WALLET_ADDR" 1 > /dev/null + + bitcoin-cli -regtest generatetoaddress 1 "$ADDRESS" > /dev/null + + printf "%s" "Waiting for lightning node funds... " + + while ! $LCLI -F --lightning-dir=/tmp/l"$node1"-regtest listfunds | grep -q "outputs" + do + sleep 1 + done + + echo "found." + + printf "%s" "Funding channel from node " "$node1" " to node " "$node2"". " + + $LCLI --lightning-dir=/tmp/l"$node1"-regtest fundchannel "$L2_NODE_ID" 1000000 > /dev/null + + bitcoin-cli -regtest generatetoaddress 6 "$ADDRESS" > /dev/null + + printf "%s" "Waiting for confirmation... " + + while ! $LCLI -F --lightning-dir=/tmp/l"$node1"-regtest listchannels | grep -q "channels" + do + sleep 1 + done + + echo "done." + done } stop_nodes() { From 033ac323d165147a445cb80aeab8d0b0786957df Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Mon, 25 Apr 2022 13:15:18 +0200 Subject: [PATCH 0760/1530] connectd: prefer IPv6 when available Changelog-Changed: connectd: prefer IPv6 connections when available. --- connectd/connectd.c | 47 +++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 13a4bffbe050..474fe8b40c92 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1738,38 +1738,61 @@ static bool wireaddr_int_equals_wireaddr(const struct wireaddr_internal *addr_a, return wireaddr_eq(&addr_a->u.wireaddr, addr_b); } -/*~ Orders the addresses which lightningd gave us. */ -static void add_gossip_addrs(struct wireaddr_internal **addrs, - const struct wireaddr *normal_addrs, - const struct wireaddr_internal *addrhint) +/*~ Adds just one address type. + * + * Ignores deprecated and the `addrhint`. */ +static void add_gossip_addrs_bytype(struct wireaddr_internal **addrs, + const struct wireaddr *normal_addrs, + const struct wireaddr_internal *addrhint, + const enum wire_addr_type type) { - /* Wrap each one in a wireaddr_internal and add to addrs. */ for (size_t i = 0; i < tal_count(normal_addrs); i++) { - /* This is not supported, ignore. */ if (normal_addrs[i].type == ADDR_TYPE_TOR_V2_REMOVED) continue; - - /* add TOR addresses in a second loop */ - if (normal_addrs[i].type == ADDR_TYPE_TOR_V3) - continue; if (wireaddr_int_equals_wireaddr(addrhint, &normal_addrs[i])) continue; + if (normal_addrs[i].type != type) + continue; struct wireaddr_internal addr; addr.itype = ADDR_INTERNAL_WIREADDR; addr.u.wireaddr = normal_addrs[i]; tal_arr_expand(addrs, addr); } - /* so connectd prefers direct connections if possible. */ + +} + + +/*~ Orders the addresses which lightningd gave us. + * + * Ignores deprecated protocols and the `addrhint` that is assumed to be + * already added first. Adds all IPv6 addresses, followed by IPv4 and then TOR. + * This ensures we are modern and use IPv6 when possible, falling back to + * direct (faster) IPv4 and finally (less stable) TOR connections. */ +static void add_gossip_addrs(struct wireaddr_internal **addrs, + const struct wireaddr *normal_addrs, + const struct wireaddr_internal *addrhint) +{ + /* Wrap each one in a wireaddr_internal and add to addrs. */ for (size_t i = 0; i < tal_count(normal_addrs); i++) { - if (normal_addrs[i].type != ADDR_TYPE_TOR_V3) + /* This is not supported, ignore. */ + if (normal_addrs[i].type == ADDR_TYPE_TOR_V2_REMOVED) continue; + /* The hint was already added earlier */ if (wireaddr_int_equals_wireaddr(addrhint, &normal_addrs[i])) continue; + /* We add IPv4 and TOR in separate loops to prefer IPv6 */ + if (normal_addrs[i].type == ADDR_TYPE_IPV4) + continue; + if (normal_addrs[i].type == ADDR_TYPE_TOR_V3) + continue; struct wireaddr_internal addr; addr.itype = ADDR_INTERNAL_WIREADDR; addr.u.wireaddr = normal_addrs[i]; tal_arr_expand(addrs, addr); } + /* Do the loop for skipped protocols in preferred order. */ + add_gossip_addrs_bytype(addrs, normal_addrs, addrhint, ADDR_TYPE_IPV4); + add_gossip_addrs_bytype(addrs, normal_addrs, addrhint, ADDR_TYPE_TOR_V3); } /*~ Consumes addrhint if not NULL. From de9bc172de9f23bc8ca57f156809c1e55a450800 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 4 May 2022 13:51:46 +0200 Subject: [PATCH 0761/1530] connect: adds nodeid to remote_addr log message --- lightningd/peer_control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index eb6fd2074c0c..9a0b428426c7 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1170,8 +1170,8 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { - log_info(ld->log, "Peer says it sees our address as: %s", - fmt_wireaddr(tmpctx, hook_payload->remote_addr)); + log_peer_info(ld->log, &id, "Peer says it sees our address as: %s", + fmt_wireaddr(tmpctx, hook_payload->remote_addr)); /* Currently only from peers we have a channel with, until we * do stuff like probing for remote_addr to a random node. */ if (!list_empty(&peer->channels)) From 55cf413fc3df2548c5a1841955186ecf30e71cb1 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sun, 8 May 2022 12:09:32 +0200 Subject: [PATCH 0762/1530] wireaddr: moves wireaddr_arr_contains to wireaddr.h ...So it can be reused somewhere else Changelog-None --- common/wireaddr.c | 9 +++++++++ common/wireaddr.h | 3 +++ gossipd/gossip_generation.c | 9 --------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/common/wireaddr.c b/common/wireaddr.c index 7d5354713397..b81d9bf3a54c 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -899,3 +899,12 @@ int wireaddr_cmp_type(const struct wireaddr *a, return tal_bytelen(a_wire) - tal_bytelen(b_wire); return cmp; } + +bool wireaddr_arr_contains(const struct wireaddr *was, + const struct wireaddr *wa) +{ + for (size_t i = 0; i < tal_count(was); i++) + if (wireaddr_eq(&was[i], wa)) + return true; + return false; +} diff --git a/common/wireaddr.h b/common/wireaddr.h index 783c5ba2d18f..6c1401e74449 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -197,4 +197,7 @@ struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx, const u8 *ser); int wireaddr_cmp_type(const struct wireaddr *a, const struct wireaddr *b, void *unused); +bool wireaddr_arr_contains(const struct wireaddr *was, + const struct wireaddr *wa); + #endif /* LIGHTNING_COMMON_WIREADDR_H */ diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 04a920777017..dfb621beb0af 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -20,15 +20,6 @@ #include #include -static bool wireaddr_arr_contains(const struct wireaddr *was, - const struct wireaddr *wa) -{ - for (size_t i = 0; i < tal_count(was); i++) - if (wireaddr_eq(&was[i], wa)) - return true; - return false; -} - /* Create a node_announcement with the given signature. It may be NULL in the * case we need to create a provisional announcement for the HSM to sign. * This is called twice: once with the dummy signature to get it signed and a From 32c4540fc03f7feebc39af60c26dc48801879446 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sun, 8 May 2022 12:11:24 +0200 Subject: [PATCH 0763/1530] jsonrpc: adds dynamicaly detected IP addresses to `getinfo` Changelog-Fixed: JSON-RPC: Adds dynamically detected public IP addresses to `getinfo` --- lightningd/peer_control.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 9a0b428426c7..5a49f7bce78f 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2014,6 +2014,12 @@ static struct command_result *json_getinfo(struct command *cmd, json_array_start(response, "address"); for (size_t i = 0; i < tal_count(cmd->ld->announceable); i++) json_add_address(response, NULL, cmd->ld->announceable+i); + if (cmd->ld->remote_addr_v4 != NULL && + !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v4)) + json_add_address(response, NULL, cmd->ld->remote_addr_v4); + if (cmd->ld->remote_addr_v6 != NULL && + !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v6)) + json_add_address(response, NULL, cmd->ld->remote_addr_v6); json_array_end(response); /* This is what we're actually bound to. */ From a2b75b66ba9b8fb078782d28cd5024004edf656d Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 1 Jun 2022 12:14:56 +0200 Subject: [PATCH 0764/1530] connectd: use dev_allow_localhost for remote_addr testing Before this fix, there was the situation where a DEVELOPER=1 node would announce non-public addresses on mainnet if detected. Since there are some nodes on the internet that falsely report local addresses we move this 'testing feature' to 'dev-allow-locahost' nodes. Changelog-None --- connectd/peer_exchange_initmsg.c | 9 +++---- tests/test_connection.py | 42 +++++++++++++++++++++++--------- tests/test_plugin.py | 5 +++- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index f43b66164825..fa97998382c6 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -105,11 +105,10 @@ static struct io_plan *peer_init_received(struct io_conn *conn, switch (remote_addr->type) { case ADDR_TYPE_IPV4: case ADDR_TYPE_IPV6: -#if DEVELOPER /* ignore private addresses (non-DEVELOPER builds) */ - if (!address_routable(remote_addr, true)) -#else - if (!address_routable(remote_addr, false)) -#endif /* DEVELOPER */ + /* Drop non-public addresses when not testing */ + if (!address_routable(remote_addr, + IFDEV(peer->daemon->dev_allow_localhost, + false))) remote_addr = tal_free(remote_addr); break; /* We are only interested in IP addresses */ diff --git a/tests/test_connection.py b/tests/test_connection.py index 4c4a5ced8c43..9de27ae8af79 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -10,7 +10,7 @@ expected_channel_features, check_coin_moves, first_channel_id, account_balance, basic_fee, scriptpubkey_addr, - DEVELOPER, EXPERIMENTAL_FEATURES, mine_funding_to_announce + EXPERIMENTAL_FEATURES, mine_funding_to_announce ) from pyln.testing.utils import SLOW_MACHINE, VALGRIND, EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT @@ -46,9 +46,6 @@ def test_connect_basic(node_factory): assert len(l1.rpc.listpeers()) == 1 assert len(l2.rpc.listpeers()) == 1 - if DEVELOPER: - print(l1.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}")) - # Should get reasonable error if unknown addr for peer. with pytest.raises(RpcError, match=r'Unable to connect, no address known'): l1.rpc.connect('032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e') @@ -62,7 +59,7 @@ def test_connect_basic(node_factory): l1.rpc.connect('032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e', 'localhost', l2.port) -@pytest.mark.developer("needs DEVELOPER=1 for having localhost remote_addr and fast gossip") +@pytest.mark.developer("needs DEVELOPER=1 for fast gossip and --dev-allow-localhost for local remote_addr") def test_remote_addr(node_factory, bitcoind): """Check address discovery (BOLT1 #917) init remote_addr works as designed: @@ -70,17 +67,33 @@ def test_remote_addr(node_factory, bitcoind): - at least two peers - we have a channel with - report the same `remote_addr` + + We perform logic tests on L2, setup: + l1 --> [l2] <-- l3 """ # don't announce anything per se - opts = {'announce-addr': [], 'may_reconnect': True} - l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + opts = {'may_reconnect': True, + 'dev-allow-localhost': None, + 'dev-no-reconnect': None} + l1, l2, l3 = node_factory.get_nodes(3, opts) + + # Disable announcing local autobind addresses with dev-allow-localhost. + # We need to have l2 opts 'bind-addr' to the (generated) value of 'addr'. + # So we stop, set 'bind-addr' option, delete 'addr' and restart first. + l2.stop() + l2.daemon.opts['bind-addr'] = l2.daemon.opts['addr'] + del l2.daemon.opts['addr'] + l2.start() + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") # Fund first channel so initial node_announcement is send + # and also check no addresses have been announced yet l1.fundchannel(l2) bitcoind.generate_block(5) l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") + assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) # when we restart l1 with a channel and reconnect, node_annoucement update # must not yet be send as we need the same `remote_addr` confirmed from a @@ -91,9 +104,8 @@ def test_remote_addr(node_factory, bitcoind): l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") - # now only l1 sees l2 without announced addresses (disabled by opts) + # Now l1 sees l2 but without announced addresses. assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) - assert(len(l3.rpc.listnodes(l2.info['id'])['nodes']) == 0) assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:9735") # connect second node. This will not yet trigger `node_annoucement` update, @@ -120,12 +132,18 @@ def test_remote_addr(node_factory, bitcoind): assert address['port'] == 9735 -@pytest.mark.developer("needs DEVELOPER=1 for having localhost remote_addr and fast gossip") +@pytest.mark.developer("needs DEVELOPER=1 for fast gossip and --dev-allow-localhost for local remote_addr") def test_remote_addr_disabled(node_factory, bitcoind): """Simply tests that IP address discovery annoucements can be turned off + + We perform logic tests on L2, setup: + l1 --> [l2] <-- l3 """ - opts = {'announce-addr': [], 'disable-ip-discovery': None, 'may_reconnect': True} - l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + opts = {'dev-allow-localhost': None, + 'disable-ip-discovery': None, + 'may_reconnect': True, + 'dev-no-reconnect': None} + l1, l2, l3 = node_factory.get_nodes(3, opts=[opts, opts, opts]) # l1->l2 l2.rpc.connect(l1.info['id'], 'localhost', l1.port) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 5cb9e8f81bcf..b9776b9bd6b3 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -458,7 +458,10 @@ def test_peer_connected_remote_addr(node_factory): The optional tlv `remote_addr` should only be visible to the initiator l1. """ - l1, l2 = node_factory.get_nodes(2, opts={'plugin': os.path.join(os.getcwd(), 'tests/plugins/peer_connected_logger_a.py')}) + pluginpath = os.path.join(os.getcwd(), 'tests/plugins/peer_connected_logger_a.py') + l1, l2 = node_factory.get_nodes(2, opts={ + 'plugin': pluginpath, + 'dev-allow-localhost': None}) l1id = l1.info['id'] l2id = l2.info['id'] From 475e4c9bd97124b058b0a271fbfa9fac4249264c Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 1 Jun 2022 13:18:55 +0200 Subject: [PATCH 0765/1530] jsonrpc: adds optional `remote_addr` to listpeers Changelog-Added: jsonrpc: adds optional `remote_addr` to listpeers --- doc/lightning-listpeers.7.md | 3 ++- doc/schemas/listpeers.schema.json | 4 ++++ lightningd/peer_control.c | 10 ++++++++++ lightningd/peer_control.h | 3 +++ tests/test_connection.py | 4 +++- 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 31c3d204c8c8..647d3bdfcda4 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -159,6 +159,7 @@ If **connected** is *true*: - **netaddr** (array of strings): A single entry array: - address, e.g. 1.2.3.4:1234 - **features** (hex): bitmap of BOLT #9 features from peer's INIT message + - **remote_addr** (string, optional): The public IPv4/6 address the peer sees us from, e.g. 1.2.3.4:1234 [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -380,4 +381,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:4f76b5ac19d3dfdaf3d04f5dd5de2312a2ab0ccffe779508c416aaf6e644612a) +[comment]: # ( SHA256STAMP:e6829e8ced923131b95bcfa4f366dd04286fe85485039e9ebc89e79899937df6) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index f50cef596c9c..f9ac1cad27f9 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -1114,6 +1114,10 @@ "description": "address, e.g. 1.2.3.4:1234" } }, + "remote_addr": { + "type": "string", + "description": "The public IPv4/6 address the peer sees us from, e.g. 1.2.3.4:1234" + }, "features": { "type": "hex", "description": "bitmap of BOLT #9 features from peer's INIT message" diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 5a49f7bce78f..6dd8eb3fa783 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -97,6 +97,7 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, peer->uncommitted_channel = NULL; peer->addr = *addr; peer->connected_incoming = connected_incoming; + peer->remote_addr = NULL; peer->their_features = NULL; list_head_init(&peer->channels); peer->direction = node_id_idx(&peer->ld->id, &peer->id); @@ -1154,6 +1155,9 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* Update peer address and direction */ peer->addr = hook_payload->addr; peer->connected_incoming = hook_payload->incoming; + if (peer->remote_addr) + tal_free(peer->remote_addr); + peer->remote_addr = NULL; peer_update_features(peer, their_features); tal_steal(peer, hook_payload); @@ -1172,6 +1176,8 @@ void peer_connected(struct lightningd *ld, const u8 *msg) if (hook_payload->remote_addr) { log_peer_info(ld->log, &id, "Peer says it sees our address as: %s", fmt_wireaddr(tmpctx, hook_payload->remote_addr)); + peer->remote_addr = tal_dup(peer, struct wireaddr, + hook_payload->remote_addr); /* Currently only from peers we have a channel with, until we * do stuff like probing for remote_addr to a random node. */ if (!list_empty(&peer->channels)) @@ -1671,6 +1677,10 @@ static void json_add_peer(struct lightningd *ld, struct wireaddr_internal, &p->addr)); json_array_end(response); + /* If peer reports our IP remote_addr, add that here */ + if (p->remote_addr) + json_add_string(response, "remote_addr", + fmt_wireaddr(response, p->remote_addr)); json_add_hex_talarr(response, "features", p->their_features); } diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 77037e17d74a..3f69729b1618 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -40,6 +40,9 @@ struct peer { struct wireaddr_internal addr; bool connected_incoming; + /* They send what they see as our address as remote_addr */ + struct wireaddr *remote_addr; + /* We keep a copy of their feature bits */ const u8 *their_features; diff --git a/tests/test_connection.py b/tests/test_connection.py index 9de27ae8af79..308ca280522f 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -86,7 +86,9 @@ def test_remote_addr(node_factory, bitcoind): l2.start() l2.rpc.connect(l1.info['id'], 'localhost', l1.port) - l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + logmsg = l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") + # check 'listpeers' contains the 'remote_addr' as logged + assert logmsg.endswith(l2.rpc.listpeers()['peers'][0]['remote_addr']) # Fund first channel so initial node_announcement is send # and also check no addresses have been announced yet From 1e9ecc55e7a37c5f15edc5a7a37d885d9bc48005 Mon Sep 17 00:00:00 2001 From: AutonomousOrganization <51036429+AutonomousOrganization@users.noreply.github.com> Date: Sun, 29 May 2022 15:30:13 -0700 Subject: [PATCH 0766/1530] Mistaken default directory in plugin docs directory .lightningd -> .lightning --- doc/PLUGINS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index c9ca9e568c9d..daa627985fc5 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -29,7 +29,7 @@ register one or more plugins that should be started. In case you wish to start several plugins you have to use the `--plugin=` argument once for each plugin (or `--plugin-dir` or place them in the default plugin dirs, usually `/usr/local/libexec/c-lightning/plugins` and -`~/.lightningd/plugins`). An example call might look like: +`~/.lightning/plugins`). An example call might look like: ``` lightningd --plugin=/path/to/plugin1 --plugin=path/to/plugin2 From d946de68144fca831c42759b7d468335d8dcf20a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 17 Jun 2022 21:10:44 +0930 Subject: [PATCH 0767/1530] bitcoin: fix header order for make check-source. It'll matter once we actually start including bitcoin/ in `make check-source` Signed-off-by: Rusty Russell --- bitcoin/block.c | 2 +- bitcoin/tx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bitcoin/block.c b/bitcoin/block.c index 5e037e4f45b4..ea350a9ceb42 100644 --- a/bitcoin/block.c +++ b/bitcoin/block.c @@ -1,11 +1,11 @@ #include "config.h" #include +#include #include #include #include #include #include -#include /* Sets *cursor to NULL and returns NULL when a pull fails. */ static const u8 *pull(const u8 **cursor, size_t *max, void *copy, size_t n) diff --git a/bitcoin/tx.c b/bitcoin/tx.c index 29d0ed8ab0f7..401c5464619d 100644 --- a/bitcoin/tx.c +++ b/bitcoin/tx.c @@ -2,12 +2,12 @@ #include #include #include +#include #include #include #include #include #include -#include struct bitcoin_tx_output *new_tx_output(const tal_t *ctx, struct amount_sat amount, From b17db120b14689a72b45bf896a9c7b48d8b0168a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 17 Jun 2022 14:42:35 +0930 Subject: [PATCH 0768/1530] bitcoin: add to check-source-bolt, and (minor) quotes fixup. Signed-off-by: Rusty Russell --- bitcoin/Makefile | 3 +++ bitcoin/psbt.c | 4 ++-- bitcoin/script.c | 14 +++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/bitcoin/Makefile b/bitcoin/Makefile index 49e2fc806092..df2dc04b0927 100644 --- a/bitcoin/Makefile +++ b/bitcoin/Makefile @@ -41,6 +41,9 @@ BITCOIN_HEADERS := bitcoin/address.h \ # Bitcoin objects depends on bitcoin/ external/ and ccan $(BITCOIN_OBJS): $(CCAN_HEADERS) $(BITCOIN_HEADERS) $(EXTERNAL_HEADERS) +ALL_C_HEADERS += $(BITCOIN_HEADERS) +ALL_C_SOURCES += $(BITCOIN_SRC) + check-makefile: check-bitcoin-makefile check-bitcoin-makefile: diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index a079fe49b223..6f346b9db758 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -618,11 +618,11 @@ bool psbt_finalize(struct wally_psbt *psbt) /* BOLT #3: * #### `to_remote` Output * - * If `option_anchor_outputs` applies to the commitment + * If `option_anchors` applies to the commitment * transaction, the `to_remote` output is encumbered by a one * block csv lock. * - * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY + * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY * * The output is spent by an input with `nSequence` * field set to `1` and witness: diff --git a/bitcoin/script.c b/bitcoin/script.c index 316388696548..68a26dd5bc9e 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -314,10 +314,10 @@ u8 *scriptpubkey_witness_raw(const tal_t *ctx, u8 version, * * #### `to_remote` Output * - * If `option_anchor_outputs` applies to the commitment + * If `option_anchors` applies to the commitment * transaction, the `to_remote` output is encumbered by a one * block csv lock. - * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY + * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY */ /* BOLT- #3 * ##### Leased channel (`option_will_fund`) @@ -564,7 +564,7 @@ u8 *bitcoin_wscript_to_local(const tal_t *ctx, u16 to_self_delay, * This output sends funds to either an HTLC-timeout transaction after the * HTLC-timeout or to the remote node using the payment preimage or the * revocation key. The output is a P2WSH, with a witness script (no - * option_anchor_outputs): + * option_anchors): * * # To remote node with revocation key * OP_DUP OP_HASH160 OP_EQUAL @@ -582,7 +582,7 @@ u8 *bitcoin_wscript_to_local(const tal_t *ctx, u16 to_self_delay, * OP_ENDIF * OP_ENDIF * - * Or, with `option_anchor_outputs`: + * Or, with `option_anchors`: * * # To remote node with revocation key * OP_DUP OP_HASH160 OP_EQUAL @@ -671,7 +671,7 @@ u8 *bitcoin_wscript_htlc_offer(const tal_t *ctx, * This output sends funds to either the remote node after the HTLC-timeout or * using the revocation key, or to an HTLC-success transaction with a * successful payment preimage. The output is a P2WSH, with a witness script - * (no `option_anchor_outputs`): + * (no `option_anchors`): * * # To remote node with revocation key * OP_DUP OP_HASH160 OP_EQUAL @@ -691,7 +691,7 @@ u8 *bitcoin_wscript_htlc_offer(const tal_t *ctx, * OP_ENDIF * OP_ENDIF * - * Or, with `option_anchor_outputs`: + * Or, with `option_anchors`: * * # To remote node with revocation key * OP_DUP OP_HASH160 OP_EQUAL @@ -859,7 +859,7 @@ u8 *bitcoin_wscript_anchor(const tal_t *ctx, u8 *script = tal_arr(ctx, u8, 0); /* BOLT #3: - * #### `to_local_anchor` and `to_remote_anchor` Output (option_anchor_outputs) + * #### `to_local_anchor` and `to_remote_anchor` Output (option_anchors) *... * OP_CHECKSIG OP_IFDUP * OP_NOTIF From e7393121b16bbb06fd6cf813a91ea2684ca1c90c Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Thu, 16 Jun 2022 21:43:40 +0700 Subject: [PATCH 0769/1530] Fix typo and convert paragraph to a list --- doc/TOR.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/TOR.md b/doc/TOR.md index 74d9b80eb3bc..86d3e7955b4e 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -185,10 +185,9 @@ on those. #### Three Ways to Create .onion Addresses for Core Lightning -You have have Tor create an onion address for you, and tell -Core Lightning to use that, or you can have Core Lightning tell Tor to -create the same onion address every time it starts up, or you can have -Core Lightning tell Tor to create a new onion address every time. +1. You can configure Tor to create an onion address for you, and tell Core Lightning to use that address +2. You can have Core Lightning tell Tor to create a new onion address every time +3. You can configure Core Lightning to tell Tor to create the same onion address every time it starts up #### Tor-Created .onion Address From 928a81f24a7b13a12ab2ed12de322e78c9942860 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 8 Jun 2022 16:47:55 +0200 Subject: [PATCH 0770/1530] docs: Fix manpage title It was causing the first sublevel of headings to be shown on RTD. Changelog-None --- doc/lightning-stop.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-stop.7.md b/doc/lightning-stop.7.md index 66132f611c5a..cb83aad34ead 100644 --- a/doc/lightning-stop.7.md +++ b/doc/lightning-stop.7.md @@ -1,5 +1,5 @@ lightning-stop -- Command to shutdown the Core Lightning node. -============================================================-- +============================================================== SYNOPSIS -------- From 1eaec223b743fd17728ed96c90755717af5073c6 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Wed, 1 Jun 2022 22:36:11 -0300 Subject: [PATCH 0771/1530] expose short_channel_id and htlc id to htlc_accepted. Changelog-Added: Plugins: `htlc_accepted` now exposes the `short_channel_id` for the channel from which that HTLC is coming from and the low-level per-channel HTLC `id`, which are necessary for bridging two different Lightning Networks when MPP is involved. --- doc/PLUGINS.md | 4 ++++ lightningd/peer_htlcs.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index daa627985fc5..d12e5ae9d02e 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1365,6 +1365,8 @@ The payload of the hook call has the following format: "next_onion": "[1365bytes of serialized onion]" }, "htlc": { + "short_channel_id": "4x5x6", + "id": 27, "amount": "43msat", "cltv_expiry": 500028, "cltv_expiry_relative": 10, @@ -1393,6 +1395,8 @@ For detailed information about each field please refer to [BOLT 04 of the specif - `shared_secret` is the shared secret we used to decrypt the incoming onion. It is shared with the sender that constructed the onion. - `htlc`: + - `short_channel_id` is the channel this payment is coming from. + - `id` is the low-level sequential HTLC id integer as sent by the channel peer. - `amount` is the amount that we received with the HTLC. This amount minus the `forward_amount` is the fee that will stay with us. - `cltv_expiry` determines when the HTLC reverts back to the diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 763ce23a4fb1..1cf1acca24e7 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1065,6 +1065,8 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_object_end(s); json_object_start(s, "htlc"); + json_add_short_channel_id(s, "short_channel_id", hin->key.channel->scid); + json_add_u64(s, "id", hin->key.id); json_add_amount_msat_only(s, "amount", hin->msat); json_add_u32(s, "cltv_expiry", expiry); json_add_s32(s, "cltv_expiry_relative", expiry - blockheight); From ed7624e4f92e80e0dd76c1378a98e6ebd387db44 Mon Sep 17 00:00:00 2001 From: Justin Litchfield Date: Thu, 9 Jun 2022 10:23:41 -0500 Subject: [PATCH 0772/1530] Warning added to PLUGIN documentation re: `stdin` Adds some warning/documentation about the `println!`/`dbg!` causing my bad times as a plugin developer. =) --- doc/PLUGINS.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index d12e5ae9d02e..553ca10516c9 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -22,6 +22,18 @@ used as protocol on top of the two streams, with the plugin acting as server and `lightningd` acting as client. The plugin file needs to be executable (e.g. use `chmod a+x plugin_name`) +### Warning + +As noted, `lightningd` uses `stdin` as an intake mechanism. This can +cause unexpected behavior if one is not careful. To wit, care should +be taken to ensure that debug/logging statements must be routed to +`stderr` or directly to a file. Activities that are benign in other +contexts (`println!`, `dbg!`, etc) will cause the plugin to be killed +with an error along the lines of: + +`UNUSUAL plugin-cln-plugin-startup: Killing plugin: JSON-RPC message +does not contain "jsonrpc" field` + ## A day in the life of a plugin During startup of `lightningd` you can use the `--plugin=` option to From 47a7b4a55b347bc55eeabd14995573d0e00510cb Mon Sep 17 00:00:00 2001 From: pn Date: Mon, 2 May 2022 17:02:03 +0200 Subject: [PATCH 0773/1530] log: Add termination to prefix log --- lightningd/log.c | 3 ++- lightningd/options.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lightningd/log.c b/lightningd/log.c index 3b4df6a1c779..e6e968275022 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -614,7 +614,8 @@ static char *arg_log_prefix(const char *arg, struct log *log) static void show_log_prefix(char buf[OPT_SHOW_LEN], const struct log *log) { - strncpy(buf, log->prefix->prefix, OPT_SHOW_LEN); + strncpy(buf, log->prefix->prefix, OPT_SHOW_LEN - 1); + buf[OPT_SHOW_LEN - 1] = '\0'; } static int signalfds[2]; diff --git a/lightningd/options.c b/lightningd/options.c index 34e6f6db4cf9..6c22c4c5f662 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1537,8 +1537,8 @@ static void add_config(struct lightningd *ld, if (opt->desc == opt_hidden) { /* Ignore hidden options (deprecated) */ } else if (opt->show) { - strcpy(buf + OPT_SHOW_LEN, "..."); opt->show(buf, opt->u.carg); + strcpy(buf + OPT_SHOW_LEN - 1, "..."); if (streq(buf, "true") || streq(buf, "false") || strspn(buf, "0123456789.") == strlen(buf)) { From 83c31f548f0011a1503a086489c73f8b22703b05 Mon Sep 17 00:00:00 2001 From: pn Date: Wed, 1 Jun 2022 23:44:52 +0200 Subject: [PATCH 0774/1530] log: Add termination to log level --- lightningd/log.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/log.c b/lightningd/log.c index e6e968275022..e578f90228eb 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -601,7 +601,8 @@ static void show_log_level(char buf[OPT_SHOW_LEN], const struct log *log) l = *log->lr->default_print_level; else l = DEFAULT_LOGLEVEL; - strncpy(buf, log_level_name(l), OPT_SHOW_LEN-1); + strncpy(buf, log_level_name(l), OPT_SHOW_LEN - 1); + buf[OPT_SHOW_LEN - 1] = '\0'; } static char *arg_log_prefix(const char *arg, struct log *log) From cff859331d80a6331878529490c96a9287e63d52 Mon Sep 17 00:00:00 2001 From: pn Date: Thu, 2 Jun 2022 01:49:07 +0200 Subject: [PATCH 0775/1530] tests: Update expected log prefix length to match truncated log --- tests/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 84d2836efb8f..37adca9717f4 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -737,7 +737,7 @@ def test_listconfigs(node_factory, bitcoind, chainparams): assert configs['network'] == chainparams['name'] assert configs['ignore-fee-limits'] is False assert configs['ignore-fee-limits'] is False - assert configs['log-prefix'] == 'lightning1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...' + assert configs['log-prefix'] == 'lightning1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...' # These are aliases, but we don't print the (unofficial!) wumbo. assert 'wumbo' not in configs From 4daa1b37ec2b036dbce8efc01c92a940a8d8a6df Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:43:11 +0930 Subject: [PATCH 0776/1530] contrib/pylightning: remove lightning-pay helper. This example predates the pay plugin! It's obsolete, unmaintained, and probably doesn't work. Signed-off-by: Rusty Russell --- Makefile | 2 +- contrib/pylightning/lightning-pay | 74 ------------------------------- contrib/pylightning/setup.py | 1 - 3 files changed, 1 insertion(+), 76 deletions(-) delete mode 100755 contrib/pylightning/lightning-pay diff --git a/Makefile b/Makefile index ba8e5d8504f5..809ae4ecac7d 100644 --- a/Makefile +++ b/Makefile @@ -490,7 +490,7 @@ check-markdown: check-spelling: @tools/check-spelling.sh -PYSRC=$(shell git ls-files "*.py" | grep -v /text.py) contrib/pylightning/lightning-pay +PYSRC=$(shell git ls-files "*.py" | grep -v /text.py) # Some tests in pyln will need to find lightningd to run, so have a PATH that # allows it to find that diff --git a/contrib/pylightning/lightning-pay b/contrib/pylightning/lightning-pay deleted file mode 100755 index d7aa11170a9b..000000000000 --- a/contrib/pylightning/lightning-pay +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import pprint -import sys -from lightning import LightningRpc - -parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) -parser.add_argument("bolt11_or_destination_id", required=True) -parser.add_argument("amount_in_milli_satoshi", default=None, type=int, nargs="?") -parser.add_argument("payment_hash", nargs="?") -parser.add_argument("min_final_cltv_expiry", nargs="?") -args = parser.parse_args() - - -def default_configdir(): - home = os.getenv("HOME") - if home: - return os.path.join(home, ".lightning") - return "." - - -rpc_path = os.path.join(default_configdir(), "lightning-rpc") -ld = LightningRpc(rpc_path) - -assert len(args.bolt11_or_destination_id) > 2, "argument bolt11_or_destination_id is invalid" - -# Bolt11 passed if prefix is 'ln' -use_bolt11 = args.bolt11_or_destination_id[:2] == "ln" - -if use_bolt11: - bolt11 = ld.decodepay(args.bolt11_or_destination_id) - print("Bolt11 decoded:") - pprint.pprint(bolt11) - id_ = bolt11["payee"] - payment_hash = bolt11["payment_hash"] - if "msatoshi" in bolt11: - amount_included_in_bolt = True - amount = bolt11["msatoshi"] - else: - assert args.amount_in_milli_satoshi, "need argument amount_in_milli_satoshi" - amount = args.amount_in_milli_satoshi - amount_included_in_bolt = False - - reply = input("Pay %s msatoshi [Y/n]? " % amount) - - if reply in ["y", "Y"]: - if amount_included_in_bolt: - ld.pay(args.bolt11_or_destination_id) - else: - ld.pay(args.bolt11_or_destination_id, amount) - else: - print("Not sending.") - -else: - assert args.amount_in_milli_satoshi, "need argument amount_in_milli_satoshi" - assert args.payment_hash, "need argument payment_hash" - assert args.min_final_cltv_expiry, "need argument min_final_cltv_expiry" - amount = args.amount_in_milli_satoshi - id_ = args.bolt11_or_destination_id - payment_hash = args.payment_hash - min_cltv_expiry = args.min_final_cltv_expiry - - route = ld.getroute(id_, amount, 1, min_cltv_expiry) - fee = route["route"][0]["msatoshi"] - amount - - reply = input("Paying fee %s on amount %s (%.3f%%). Send [Y/n]? " % (fee, amount, fee / amount * 100.0)) - - if reply in ["y", "Y"]: - ld.sendpay(route["route"], payment_hash) - else: - print("Not sending.") - sys.exit(1) diff --git a/contrib/pylightning/setup.py b/contrib/pylightning/setup.py index 5b841d300562..f2eefc4858c4 100644 --- a/contrib/pylightning/setup.py +++ b/contrib/pylightning/setup.py @@ -19,6 +19,5 @@ author_email='decker.christian@gmail.com', license='MIT', packages=['lightning'], - scripts=['lightning-pay'], zip_safe=True, install_requires=requirements) From c5b032598ec5d2c45563fe86e829c4cd27e2818e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:44:11 +0930 Subject: [PATCH 0777/1530] lightningd: fix outgoing IO logging for JSONRPC. Changelog-Fixed: lightningd: `log-level` `io` shows JSONRPC output, as well as input. Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 0965f193ad30..4f67617859a3 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -971,9 +971,14 @@ static struct io_plan *start_json_stream(struct io_conn *conn, struct json_connection *jcon) { /* If something has created an output buffer, start streaming. */ - if (tal_count(jcon->js_arr)) + if (tal_count(jcon->js_arr)) { + size_t len; + const char *p = json_out_contents(jcon->js_arr[0]->jout, &len); + if (len) + log_io(jcon->log, LOG_IO_OUT, NULL, "", p, len); return json_stream_output(jcon->js_arr[0], conn, stream_out_complete, jcon); + } /* Tell reader it can run next command. */ io_wake(conn); From 6e2a775ef27158274af5097030ee093c9ff5021e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:45:11 +0930 Subject: [PATCH 0778/1530] common/param: support renaming options using "|". This is *much* easier to do inside parsing than in the caller! Signed-off-by: Rusty Russell --- common/param.c | 20 +++++++++++++++----- common/param.h | 5 ++++- common/test/run-param.c | 24 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/common/param.c b/common/param.c index 9326465c9ff1..34c6682c5023 100644 --- a/common/param.c +++ b/common/param.c @@ -1,6 +1,8 @@ #include "config.h" #include +#include #include +#include #include #include #include @@ -106,9 +108,15 @@ static struct param *find_param(struct param *params, const char *start, struct param *last = first + tal_count(params); while (first != last) { - if (strncmp(first->name, start, n) == 0) - if (strlen(first->name) == n) - return first; + size_t arglen = strcspn(first->name, "|"); + if (memeq(first->name, arglen, start, n)) + return first; + if (deprecated_apis + && first->name[arglen] + && memeq(first->name + arglen + 1, + strlen(first->name + arglen + 1), + start, n)) + return first; first++; } return NULL; @@ -237,12 +245,14 @@ static char *param_usage(const tal_t *ctx, { char *usage = tal_strdup(ctx, ""); for (size_t i = 0; i < tal_count(params); i++) { + /* Don't print |deprecated part! */ + int len = strcspn(params[i].name, "|"); if (i != 0) tal_append_fmt(&usage, " "); if (params[i].style == PARAM_REQUIRED) - tal_append_fmt(&usage, "%s", params[i].name); + tal_append_fmt(&usage, "%.*s", len, params[i].name); else - tal_append_fmt(&usage, "[%s]", params[i].name); + tal_append_fmt(&usage, "[%.*s]", len, params[i].name); } return usage; } diff --git a/common/param.h b/common/param.h index dcaa9f25d419..a127d85cf069 100644 --- a/common/param.h +++ b/common/param.h @@ -19,7 +19,7 @@ * * if (!param(cmd, buffer, params, * p_req("cltv", json_tok_number, &cltv), - * p_opt("msatoshi", json_tok_u64, &msatoshi), + * p_opt("amount_msat|msatoshi", json_tok_u64, &msatoshi), * p_opt("note", json_tok_tok, ¬e), * p_opt_def("expiry", json_tok_u64, &expiry, 3600), * NULL)) @@ -76,6 +76,7 @@ enum param_style { /* * Add a required parameter. + * name can be | if it's been renamed. */ #define p_req(name, cbx, arg) \ name"", \ @@ -89,6 +90,7 @@ enum param_style { /* * Add an optional parameter. *arg is set to NULL if it isn't found. + * name can be | if it's been renamed. */ #define p_opt(name, cbx, arg) \ name"", \ @@ -103,6 +105,7 @@ enum param_style { /* * Add an optional parameter. *arg is set to @def if it isn't found. + * name can be | if it's been renamed. */ #define p_opt_def(name, cbx, arg, def) \ name"", \ diff --git a/common/test/run-param.c b/common/test/run-param.c index edda9895ddd4..6577f5b29af0 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -34,6 +34,8 @@ struct command_result *command_fail(struct command *cmd, } /* AUTOGENERATED MOCKS START */ +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, @@ -457,6 +459,27 @@ static void sendpay(void) assert(*msatoshi == 547); } +static void deprecated_rename(void) +{ + struct json *j = json_parse(cmd, "{ 'u64': 42 }"); + u64 *u64; + + assert(param(cmd, j->buffer, j->toks, + p_req("u64|old_u64", param_u64, &u64), + NULL)); + assert(*u64 == 42); + + deprecated_apis = true; + j = json_parse(cmd, "{ 'old_u64': 42 }"); + assert(param(cmd, j->buffer, j->toks, + p_req("u64|old_u64", param_u64, &u64), + NULL)); + deprecated_apis = false; + assert(!param(cmd, j->buffer, j->toks, + p_req("u64|old_u64", param_u64, &u64), + NULL)); +} + static void sendpay_nulltok(void) { struct json *j = json_parse(cmd, "[ 'A', '123']"); @@ -632,6 +655,7 @@ int main(int argc, char *argv[]) advanced_fail(); param_tests(); usage(); + deprecated_rename(); printf("run-params ok\n"); common_shutdown(); From 36a2491a8953075de9b50c489f698b38e55809cf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:46:11 +0930 Subject: [PATCH 0779/1530] json: fix up msat amounts in non-_msat fields. We had json_add_amount_msat_only(), which was designed to be used to print out msat fields, if we had sats. However, we misused it, so split it into the three different cases: 1. json_add_amount_sat_msat: We are using it correctly, with a field called xxx_msat. 2. json_add_amount_sats_deprecated: We were using it wrong, so deprecate the old field and create a new one which does end in _msat. 3. json_add_sats: we were using it to hand sats as a JSON parameter to an interface, where "XXXsat". Signed-off-by: Rusty Russell Changelog-Deprecated: Plugins: `rbf_channel` and `openchannel2` hooks `their_funding` (use `their_funding_msat`) Changelog-Deprecated: Plugins: `openchannel2` hook `dust_limit_satoshis` (use `dust_limit_msat`) Changelog-Deprecated: Plugins: `openchannel` hook `funding_satoshis` (use `funding_msat`) Changelog-Deprecated: Plugins: `openchannel` hook `dust_limit_satoshis` (use `dust_limit_msat`) Changelog-Deprecated: Plugins: `openchannel` hook `channel_reserve_satoshis` (use `channel_reserve_msat`) Changelog-Deprecated: Plugins: `channel_opened` notification `amount` (use `funding_msat`) Changelog-Deprecated: JSON-RPC: `listtransactions` `msat` (use `amount_msat`) Changelog-Deprecated: Plugins: `htlc_accepted` `forward_amount` (use `forward_msat`) --- .msggen.json | 1 + cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 4 +-- common/json.h | 2 +- common/json_helpers.c | 36 ++++++++++++++++--- common/json_helpers.h | 19 ++++++++-- common/test/run-json.c | 2 ++ .../test/run-route_blinding_override_test.c | 8 +++++ common/test/run-route_blinding_test.c | 8 +++++ devtools/Makefile | 1 + devtools/bolt12-cli.c | 3 +- doc/PLUGINS.md | 10 +++--- doc/lightning-listtransactions.7.md | 4 +-- doc/schemas/listtransactions.schema.json | 7 ++-- gossipd/test/run-check_channel_announcement.c | 2 ++ gossipd/test/run-check_node_announcement.c | 2 ++ gossipd/test/run-crc32_of_update.c | 2 ++ gossipd/test/run-extended-info.c | 2 ++ gossipd/test/run-next_block_range.c | 2 ++ gossipd/test/run-txout_failure.c | 2 ++ lightningd/dual_open_control.c | 24 +++++++------ lightningd/gossip_control.c | 2 +- lightningd/notification.c | 7 ++-- lightningd/opening_control.c | 10 +++--- lightningd/peer_control.c | 6 ++-- lightningd/peer_htlcs.c | 6 +++- lightningd/test/run-invoice-select-inchan.c | 10 +++--- plugins/bcli.c | 2 +- plugins/funder.c | 12 +++---- plugins/test/run-route-overlong.c | 2 +- plugins/txprepare.c | 2 +- tests/plugins/misc_notifications.py | 2 +- tests/plugins/openchannel_hook_accepter.py | 4 +-- tests/plugins/reject_odd_funding_amounts.py | 4 +-- tests/test_pay.py | 2 +- tests/test_plugin.py | 10 +++--- tests/test_wallet.py | 2 +- tests/utils.py | 2 +- wallet/reservation.c | 2 +- wallet/test/run-wallet.c | 12 +++---- wallet/walletrpc.c | 2 +- 42 files changed, 164 insertions(+), 82 deletions(-) diff --git a/.msggen.json b/.msggen.json index a664e3346919..5d4265428da1 100644 --- a/.msggen.json +++ b/.msggen.json @@ -806,6 +806,7 @@ "ListTransactions.transactions[].inputs[].type": 4 }, "ListtransactionsTransactionsOutputs": { + "ListTransactions.transactions[].outputs[].amount_msat": 6, "ListTransactions.transactions[].outputs[].channel": 5, "ListTransactions.transactions[].outputs[].index": 1, "ListTransactions.transactions[].outputs[].msat": 2, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index a368f2078026..6925b54aed12 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -767,7 +767,7 @@ message ListtransactionsTransactionsOutputs { CHANNEL_UNILATERAL_CHEAT = 10; } uint32 index = 1; - Amount msat = 2; + Amount amount_msat = 6; bytes scriptPubKey = 3; optional ListtransactionsTransactionsOutputsType item_type = 4; optional string channel = 5; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index c5c919d6c09e..18afaedfead3 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -546,7 +546,7 @@ impl From<&responses::ListtransactionsTransactionsOutputs> for pb::Listtransacti fn from(c: &responses::ListtransactionsTransactionsOutputs) -> Self { Self { index: c.index.clone(), // Rule #2 for type u32 - msat: Some(c.msat.into()), // Rule #2 for type msat + amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat script_pub_key: hex::decode(&c.script_pub_key).unwrap(), // Rule #2 for type hex item_type: c.item_type.map(|v| v as i32), channel: c.channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 61697c4f848d..3155667ee8ac 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1999,8 +1999,8 @@ pub mod responses { pub struct ListtransactionsTransactionsOutputs { #[serde(alias = "index")] pub index: u32, - #[serde(alias = "msat")] - pub msat: Amount, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, #[serde(alias = "scriptPubKey")] pub script_pub_key: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/common/json.h b/common/json.h index eb4fb05487f0..bdd867f5b46b 100644 --- a/common/json.h +++ b/common/json.h @@ -185,7 +185,7 @@ const char *json_scanv(const tal_t *ctx, /* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns * any non-printable chars into JSON escapes, but leaves existing escapes alone. */ -void json_add_string(struct json_stream *result, const char *fieldname, const char *value); +void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES); /* '"fieldname" : "value[:value_len]"' or '"value[:value_len]"' if * fieldname is NULL. Turns any non-printable chars into JSON diff --git a/common/json_helpers.c b/common/json_helpers.c index af06c2036a2e..baa7d3918104 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -1,7 +1,9 @@ #include "config.h" #include +#include #include #include +#include #include #include #include @@ -417,19 +419,43 @@ void json_add_amount_sat_compat(struct json_stream *result, const char *msatfieldname) { json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */ - json_add_amount_sat_only(result, msatfieldname, sat); + json_add_amount_sat_msat(result, msatfieldname, sat); } -void json_add_amount_sat_only(struct json_stream *result, - const char *msatfieldname, - struct amount_sat sat) +void json_add_amount_sat_msat(struct json_stream *result, + const char *msatfieldname, + struct amount_sat sat) { struct amount_msat msat; + assert(strends(msatfieldname, "_msat")); if (amount_sat_to_msat(&msat, sat)) json_add_string(result, msatfieldname, type_to_string(tmpctx, struct amount_msat, &msat)); } +/* When I noticed that we were adding "XXXmsat" fields *not* ending in _msat */ +void json_add_amount_sats_deprecated(struct json_stream *result, + const char *fieldname, + const char *msatfieldname, + struct amount_sat sat) +{ + if (deprecated_apis) { + struct amount_msat msat; + assert(!strends(fieldname, "_msat")); + if (amount_sat_to_msat(&msat, sat)) + json_add_string(result, fieldname, + take(fmt_amount_msat(NULL, msat))); + } + json_add_amount_sat_msat(result, msatfieldname, sat); +} + +void json_add_sats(struct json_stream *result, + const char *fieldname, + struct amount_sat sat) +{ + json_add_string(result, fieldname, take(fmt_amount_sat(NULL, sat))); +} + void json_add_secret(struct json_stream *response, const char *fieldname, const struct secret *secret) { @@ -451,7 +477,7 @@ void json_add_preimage(struct json_stream *result, const char *fieldname, void json_add_lease_rates(struct json_stream *result, const struct lease_rates *rates) { - json_add_amount_sat_only(result, "lease_fee_base_msat", + json_add_amount_sat_msat(result, "lease_fee_base_msat", amount_sat(rates->lease_fee_base_sat)); json_add_num(result, "lease_fee_basis", rates->lease_fee_basis); json_add_num(result, "funding_weight", rates->funding_weight); diff --git a/common/json_helpers.h b/common/json_helpers.h index 1f5fe7fe3239..9a6673db0b82 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -167,11 +167,24 @@ void json_add_amount_msat_only(struct json_stream *result, NO_NULL_ARGS; /* Adds an 'msat' field */ -void json_add_amount_sat_only(struct json_stream *result, - const char *msatfieldname, - struct amount_sat sat) +void json_add_amount_sat_msat(struct json_stream *result, + const char *msatfieldname, + struct amount_sat sat) NO_NULL_ARGS; +/* Adds an 'msat' field, and an older deprecated field. */ +void json_add_amount_sats_deprecated(struct json_stream *result, + const char *fieldname, + const char *msatfieldname, + struct amount_sat sat) + NO_NULL_ARGS; + +/* This is used to create requests, *never* for output (output is always + * msat!) */ +void json_add_sats(struct json_stream *result, + const char *fieldname, + struct amount_sat sat); + void json_add_sha256(struct json_stream *result, const char *fieldname, const struct sha256 *hash); diff --git a/common/test/run-json.c b/common/test/run-json.c index d5511bce4da8..db437a5e734f 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -8,6 +8,8 @@ #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for fromwire_tlv */ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c index c54af6078c33..4fbfd01d8df9 100644 --- a/common/test/run-route_blinding_override_test.c +++ b/common/test/run-route_blinding_override_test.c @@ -53,6 +53,14 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; +/* Generated stub for fmt_amount_msat */ +const char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); } +/* Generated stub for fmt_amount_sat */ +const char *fmt_amount_sat(const tal_t *ctx UNNEEDED, struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "fmt_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_amount_msat */ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c index 6c2f9bbd3aae..82c0b7f2bbf0 100644 --- a/common/test/run-route_blinding_test.c +++ b/common/test/run-route_blinding_test.c @@ -53,6 +53,14 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; +/* Generated stub for fmt_amount_msat */ +const char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); } +/* Generated stub for fmt_amount_sat */ +const char *fmt_amount_sat(const tal_t *ctx UNNEEDED, struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "fmt_amount_sat called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } diff --git a/devtools/Makefile b/devtools/Makefile index 35ba6da51ced..fd3f66a37516 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -22,6 +22,7 @@ DEVTOOLS_COMMON_OBJS := \ common/bolt11.o \ common/blockheight_states.o \ common/channel_id.o \ + common/configdir.o \ common/decode_array.o \ common/features.o \ common/fee_states.o \ diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index a285e851186a..86da7e961ef7 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,6 @@ #define ERROR_USAGE 3 static bool well_formed = true; -bool deprecated_apis = true; /* Tal wrappers for opt. */ static void *opt_allocfn(size_t size) @@ -463,6 +463,7 @@ int main(int argc, char *argv[]) char *fail; common_setup(argv[0]); + deprecated_apis = true; opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); opt_register_noarg("--help|-h", opt_usage_and_exit, diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 553ca10516c9..fdf07e3fe2a7 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -410,7 +410,7 @@ if the funding transaction has been included into a block. { "channel_opened": { "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "funding_satoshis": "100000000msat", + "funding_msat": "100000000msat", "funding_txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", "funding_locked": false } @@ -721,7 +721,7 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "part_id": 0, // (`channel_mvt` only, optional) "credit":"2000000000msat", "debit":"0msat", - "output_value": "2000000000msat", // ('chain_mvt' only) + "output_msat": "2000000000msat", // ('chain_mvt' only) "output_count": 2, // ('chain_mvt' only, typically only channel closes) "fees": "382msat", // ('channel_mvt' only) "tags": ["deposit"], @@ -1185,8 +1185,8 @@ the v2 protocol, and it has passed basic sanity checks: "openchannel2": { "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", "channel_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", - "their_funding": "100000000msat", - "dust_limit_satoshis": "546000msat", + "their_funding_msat": "100000000msat", + "dust_limit_msat": "546000msat", "max_htlc_value_in_flight_msat": "18446744073709551615msat", "htlc_minimum_msat": "0msat", "funding_feerate_per_kw": 7500, @@ -1323,7 +1323,7 @@ requests an RBF for a channel funding transaction. "rbf_channel": { "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", "channel_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", - "their_funding": "100000000msat", + "their_funding_msat": "100000000msat", "funding_feerate_per_kw": 7500, "feerate_our_max": 10000, "feerate_our_min": 253, diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index 157fc0126b0d..ed219f631076 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -40,7 +40,7 @@ On success, an object containing **transactions** is returned. It is an array o - **channel** (short_channel_id, optional): the channel this input is associated with (*EXPERIMENTAL_FEATURES* only) - **outputs** (array of objects): Each output, in order: - **index** (u32): the 0-based output number - - **msat** (msat): the amount of the output + - **amount_msat** (msat): the amount of the output - **scriptPubKey** (hex): the scriptPubKey - **type** (string, optional): the purpose of this output (*EXPERIMENTAL_FEATURES* only) (one of "theirs", "deposit", "withdraw", "channel_funding", "channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat") - **channel** (short_channel_id, optional): the channel this output is associated with (*EXPERIMENTAL_FEATURES* only) @@ -104,4 +104,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:bd9c33dd27be0f25b0212b4115714768ffbec2ff6e72f083613a4464a3f98bc0) +[comment]: # ( SHA256STAMP:74408f25dcf548f1389ab41123748297ba3116ad75afb41b86ecca453b95fec8) diff --git a/doc/schemas/listtransactions.schema.json b/doc/schemas/listtransactions.schema.json index 0b41b17c779f..3c34ba896372 100644 --- a/doc/schemas/listtransactions.schema.json +++ b/doc/schemas/listtransactions.schema.json @@ -126,7 +126,7 @@ "additionalProperties": false, "required": [ "index", - "msat", + "amount_msat", "scriptPubKey" ], "properties": { @@ -134,10 +134,13 @@ "type": "u32", "description": "the 0-based output number" }, - "msat": { + "amount_msat": { "type": "msat", "description": "the amount of the output" }, + "msat": { + "deprecated": true + }, "scriptPubKey": { "type": "hex", "description": "the scriptPubKey" diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index ded89931d24e..a25e48e60f8d 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -59,6 +59,8 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 7d20a8720211..0a53fe4b523e 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -27,6 +27,8 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, /* Generated stub for daemon_conn_send */ void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "daemon_conn_send called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 3b46a0f54c15..f02f52d9d934 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -45,6 +45,8 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index a89dabd99a8a..142ba69cdc09 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -45,6 +45,8 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index bad28d36720a..21255cad152e 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -26,6 +26,8 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index f6687c5bd8fd..10a9b4f34499 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -30,6 +30,8 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index d8ac1264396c..d72270a0d0b8 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -195,8 +195,9 @@ static void rbf_channel_hook_serialize(struct rbf_channel_payload *payload, json_object_start(stream, "rbf_channel"); json_add_node_id(stream, "id", &payload->peer_id); json_add_channel_id(stream, "channel_id", &payload->channel_id); - json_add_amount_sat_only(stream, "their_funding", - payload->their_funding); + json_add_amount_sats_deprecated(stream, + "their_funding", "their_funding_msat", + payload->their_funding); json_add_num(stream, "locktime", payload->locktime); json_add_num(stream, "feerate_our_max", payload->feerate_our_max); @@ -204,7 +205,7 @@ static void rbf_channel_hook_serialize(struct rbf_channel_payload *payload, payload->feerate_our_min); json_add_num(stream, "funding_feerate_per_kw", payload->funding_feerate_per_kw); - json_add_amount_sat_only(stream, "channel_max_msat", + json_add_amount_sat_msat(stream, "channel_max_msat", payload->channel_max); json_object_end(stream); } @@ -263,10 +264,11 @@ static void openchannel2_hook_serialize(struct openchannel2_payload *payload, json_object_start(stream, "openchannel2"); json_add_node_id(stream, "id", &payload->peer_id); json_add_channel_id(stream, "channel_id", &payload->channel_id); - json_add_amount_sat_only(stream, "their_funding", - payload->their_funding); - json_add_amount_sat_only(stream, "dust_limit_satoshis", - payload->dust_limit_satoshis); + json_add_amount_sats_deprecated(stream, "their_funding", "their_funding_msat", + payload->their_funding); + json_add_amount_sats_deprecated(stream, "dust_limit_satoshis", + "dust_limit_msat", + payload->dust_limit_satoshis); json_add_amount_msat_only(stream, "max_htlc_value_in_flight_msat", payload->max_htlc_value_in_flight_msat); json_add_amount_msat_only(stream, "htlc_minimum_msat", @@ -286,10 +288,10 @@ static void openchannel2_hook_serialize(struct openchannel2_payload *payload, if (tal_bytelen(payload->shutdown_scriptpubkey) != 0) json_add_hex_talarr(stream, "shutdown_scriptpubkey", payload->shutdown_scriptpubkey); - json_add_amount_sat_only(stream, "channel_max_msat", + json_add_amount_sat_msat(stream, "channel_max_msat", payload->channel_max); if (!amount_sat_zero(payload->requested_lease_amt)) { - json_add_amount_sat_only(stream, "requested_lease_msat", + json_add_amount_sat_msat(stream, "requested_lease_msat", payload->requested_lease_amt); json_add_num(stream, "lease_blockheight_start", payload->lease_blockheight_start); @@ -1632,8 +1634,8 @@ static void handle_dry_run_finished(struct subd *dualopend, const u8 *msg) channel->open_attempt = tal_free(channel->open_attempt); response = json_stream_success(cmd); - json_add_amount_sat_only(response, "our_funding_msat", our_funding); - json_add_amount_sat_only(response, "their_funding_msat", their_funding); + json_add_amount_sat_msat(response, "our_funding_msat", our_funding); + json_add_amount_sat_msat(response, "their_funding_msat", their_funding); if (rates) { json_add_lease_rates(response, rates); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 9b36baf749f2..5e23e8b57225 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -401,7 +401,7 @@ static struct command_result *json_setleaserates(struct command *cmd, take(towire_gossipd_new_lease_rates(NULL, rates))); res = json_stream_success(cmd); - json_add_amount_sat_only(res, "lease_fee_base_msat", + json_add_amount_sat_msat(res, "lease_fee_base_msat", amount_sat(rates->lease_fee_base_sat)); json_add_num(res, "lease_fee_basis", rates->lease_fee_basis); json_add_num(res, "funding_weight", rates->funding_weight); diff --git a/lightningd/notification.c b/lightningd/notification.c index 1ea4851068ad..00b29cf71691 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -205,7 +205,7 @@ static void channel_opened_notification_serialize(struct json_stream *stream, { json_object_start(stream, "channel_opened"); json_add_node_id(stream, "id", node_id); - json_add_amount_sat_only(stream, "amount", *funding_sat); + json_add_amount_sats_deprecated(stream, "amount", "funding_msat", *funding_sat); json_add_txid(stream, "funding_txid", funding_txid); json_add_bool(stream, "funding_locked", funding_locked); json_object_end(stream); @@ -482,8 +482,9 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_add_amount_msat_only(stream, "debit", mvt->debit); /* Only chain movements */ if (mvt->output_val) - json_add_amount_sat_only(stream, "output_value", - *mvt->output_val); + json_add_amount_sats_deprecated(stream, "output_value", + "output_msat", + *mvt->output_val); if (mvt->output_count > 0) json_add_num(stream, "output_count", mvt->output_count); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 9499a76e3e41..17bffd21dd5a 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -605,14 +605,14 @@ static void openchannel_hook_serialize(struct openchannel_hook_payload *payload, struct uncommitted_channel *uc = payload->openingd->channel; json_object_start(stream, "openchannel"); json_add_node_id(stream, "id", &uc->peer->id); - json_add_amount_sat_only(stream, "funding_satoshis", - payload->funding_satoshis); + json_add_amount_sats_deprecated(stream, "funding_satoshis", "funding_msat", + payload->funding_satoshis); json_add_amount_msat_only(stream, "push_msat", payload->push_msat); - json_add_amount_sat_only(stream, "dust_limit_satoshis", - payload->dust_limit_satoshis); + json_add_amount_sats_deprecated(stream, "dust_limit_satoshis", "dust_limit_msat", + payload->dust_limit_satoshis); json_add_amount_msat_only(stream, "max_htlc_value_in_flight_msat", payload->max_htlc_value_in_flight_msat); - json_add_amount_sat_only(stream, "channel_reserve_satoshis", + json_add_amount_sats_deprecated(stream, "channel_reserve_satoshis", "channel_reserve_msat", payload->channel_reserve_satoshis); json_add_amount_msat_only(stream, "htlc_minimum_msat", payload->htlc_minimum_msat); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 6dd8eb3fa783..e3cb6c8d33dc 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -639,7 +639,7 @@ static void json_add_channel(struct lightningd *ld, bitcoin_txid(channel->last_tx, &txid); json_add_txid(response, "scratch_txid", &txid); - json_add_amount_sat_only(response, "last_tx_fee_msat", + json_add_amount_sat_msat(response, "last_tx_fee_msat", bitcoin_tx_compute_fee(channel->last_tx)); } @@ -708,10 +708,10 @@ static void json_add_channel(struct lightningd *ld, inflight->funding->feerate, feerate_style_name( FEERATE_PER_KSIPA))); - json_add_amount_sat_only(response, + json_add_amount_sat_msat(response, "total_funding_msat", inflight->funding->total_funds); - json_add_amount_sat_only(response, + json_add_amount_sat_msat(response, "our_funding_msat", inflight->funding->our_funds); /* Add the expected commitment tx id also */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 1cf1acca24e7..04e94dc1fd66 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1044,7 +1044,11 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, if (p->payload->forward_channel) json_add_short_channel_id(s, "short_channel_id", p->payload->forward_channel); - json_add_amount_msat_only(s, "forward_amount", + if (deprecated_apis) + json_add_string(s, "forward_amount", + fmt_amount_msat(tmpctx, + p->payload->amt_to_forward)); + json_add_amount_msat_only(s, "forward_msat", p->payload->amt_to_forward); json_add_u32(s, "outgoing_cltv_value", p->payload->outgoing_cltv); /* These are specified together in TLV, so only print total_msat diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index e064deb24cb8..5e14ae9ff1ae 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -316,12 +316,12 @@ void json_add_amount_sat_compat(struct json_stream *result UNNEEDED, const char *msatfieldname) { fprintf(stderr, "json_add_amount_sat_compat called!\n"); abort(); } -/* Generated stub for json_add_amount_sat_only */ -void json_add_amount_sat_only(struct json_stream *result UNNEEDED, - const char *msatfieldname UNNEEDED, - struct amount_sat sat) +/* Generated stub for json_add_amount_sat_msat */ +void json_add_amount_sat_msat(struct json_stream *result UNNEEDED, + const char *msatfieldname UNNEEDED, + struct amount_sat sat) -{ fprintf(stderr, "json_add_amount_sat_only called!\n"); abort(); } +{ fprintf(stderr, "json_add_amount_sat_msat called!\n"); abort(); } /* Generated stub for json_add_bolt11 */ void json_add_bolt11(struct json_stream *response UNNEEDED, const struct bolt11 *b11 UNNEEDED) diff --git a/plugins/bcli.c b/plugins/bcli.c index 5f0922c77812..514aa0f621b5 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -414,7 +414,7 @@ static struct command_result *process_getutxout(struct bitcoin_cli *bcli) return command_err_bcli_badjson(bcli, err); response = jsonrpc_stream_success(bcli->cmd); - json_add_amount_sat_only(response, "amount", output.amount); + json_add_sats(response, "amount", output.amount); json_add_string(response, "script", tal_hex(response, output.script)); return command_finished(bcli->cmd, response); diff --git a/plugins/funder.c b/plugins/funder.c index 7880a6dbaec5..7967bec3cd2c 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -511,7 +511,7 @@ json_openchannel2_call(struct command *cmd, "{openchannel2:" "{id:%" ",channel_id:%" - ",their_funding:%" + ",their_funding_msat:%" ",max_htlc_value_in_flight_msat:%" ",htlc_minimum_msat:%" ",funding_feerate_per_kw:%" @@ -768,15 +768,15 @@ static void json_add_policy(struct json_stream *stream, funder_opt_name(policy->opt)); json_add_num(stream, "policy_mod", policy->mod); json_add_bool(stream, "leases_only", policy->leases_only); - json_add_amount_sat_only(stream, "min_their_funding_msat", + json_add_amount_sat_msat(stream, "min_their_funding_msat", policy->min_their_funding); - json_add_amount_sat_only(stream, "max_their_funding_msat", + json_add_amount_sat_msat(stream, "max_their_funding_msat", policy->max_their_funding); - json_add_amount_sat_only(stream, "per_channel_min_msat", + json_add_amount_sat_msat(stream, "per_channel_min_msat", policy->per_channel_min); - json_add_amount_sat_only(stream, "per_channel_max_msat", + json_add_amount_sat_msat(stream, "per_channel_max_msat", policy->per_channel_max); - json_add_amount_sat_only(stream, "reserve_tank_msat", + json_add_amount_sat_msat(stream, "reserve_tank_msat", policy->reserve_tank); json_add_num(stream, "fuzz_percent", policy->fuzz_factor); json_add_num(stream, "fund_probability", policy->fund_probability); diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 2ca73fd9452e..42271efcd5fe 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -73,7 +73,7 @@ void json_add_short_channel_id(struct json_stream *response UNNEEDED, const struct short_channel_id *id UNNEEDED) { fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); } /* Generated stub for json_add_string */ -void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value UNNEEDED) +void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) { fprintf(stderr, "json_add_string called!\n"); abort(); } /* Generated stub for json_add_timeabs */ void json_add_timeabs(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, diff --git a/plugins/txprepare.c b/plugins/txprepare.c index 4de70d44fb47..90d3783bc2c4 100644 --- a/plugins/txprepare.c +++ b/plugins/txprepare.c @@ -378,7 +378,7 @@ static struct command_result *txprepare_continue(struct command *cmd, } if (txp->all_output_idx == -1) - json_add_amount_sat_only(req->js, "satoshi", txp->output_total); + json_add_sats(req->js, "satoshi", txp->output_total); else json_add_string(req->js, "satoshi", "all"); diff --git a/tests/plugins/misc_notifications.py b/tests/plugins/misc_notifications.py index eb019e16a8ef..f11b4da0614a 100755 --- a/tests/plugins/misc_notifications.py +++ b/tests/plugins/misc_notifications.py @@ -18,7 +18,7 @@ def init(plugin, options, configuration): def channel_opened(plugin, channel_opened, **kwargs): plugin.log("A channel was opened to us by {}, with an amount" " of {} and the following funding transaction id: {}" - .format(channel_opened["id"], channel_opened["amount"], + .format(channel_opened["id"], channel_opened["funding_msat"], channel_opened["funding_txid"])) diff --git a/tests/plugins/openchannel_hook_accepter.py b/tests/plugins/openchannel_hook_accepter.py index bf63cf146f15..be409fa45cb4 100755 --- a/tests/plugins/openchannel_hook_accepter.py +++ b/tests/plugins/openchannel_hook_accepter.py @@ -52,13 +52,13 @@ def run_openchannel(funding_sats_str, plugin): @plugin.hook('openchannel') def on_openchannel(openchannel, plugin, **kwargs): - return run_openchannel(openchannel['funding_satoshis'], plugin) + return run_openchannel(openchannel['funding_msat'], plugin) @plugin.hook('openchannel2') def on_openchannel2(openchannel2, plugin, **kwargs): """ Support for v2 channel opens """ - return run_openchannel(openchannel2['their_funding'], plugin) + return run_openchannel(openchannel2['their_funding_msat'], plugin) plugin.run() diff --git a/tests/plugins/reject_odd_funding_amounts.py b/tests/plugins/reject_odd_funding_amounts.py index adf0efc007bd..fc4d67a338b6 100755 --- a/tests/plugins/reject_odd_funding_amounts.py +++ b/tests/plugins/reject_odd_funding_amounts.py @@ -21,7 +21,7 @@ def on_openchannel(openchannel, plugin, **kwargs): print("{} VARS".format(len(openchannel.keys()))) for k in sorted(openchannel.keys()): print("{}={}".format(k, openchannel[k])) - return run_check(openchannel['funding_satoshis']) + return run_check(openchannel['funding_msat']) @plugin.hook('openchannel2') @@ -30,7 +30,7 @@ def on_openchannel2(openchannel2, plugin, **kwargs): for k in sorted(openchannel2.keys()): print("{}={}".format(k, openchannel2[k])) - return run_check(openchannel2['their_funding']) + return run_check(openchannel2['their_funding_msat']) plugin.run() diff --git a/tests/test_pay.py b/tests/test_pay.py index 73585ba7b884..65ff87ea52b8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3111,7 +3111,7 @@ def test_partial_payment(node_factory, bitcoind, executor): for i in range(2): line = l4.daemon.wait_for_log('print_htlc_onion.py: Got onion') assert "'type': 'tlv'" in line - assert "'forward_amount': '499msat'" in line or "'forward_amount': '501msat'" in line + assert "'forward_msat': '499msat'" in line or "'forward_msat': '501msat'" in line assert "'total_msat': '1000msat'" in line assert "'payment_secret': '{}'".format(paysecret) in line diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b9776b9bd6b3..40a4250ee9e3 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -626,7 +626,7 @@ def test_openchannel_hook(node_factory, bitcoind): # Make sure plugin got all the vars we expect expected = { 'channel_flags': '1', - 'dust_limit_satoshis': '546000msat', + 'dust_limit_msat': '546000msat', 'htlc_minimum_msat': '0msat', 'id': l1.info['id'], 'max_accepted_htlcs': '483', @@ -643,14 +643,14 @@ def test_openchannel_hook(node_factory, bitcoind): 'feerate_our_max': '150000', 'feerate_our_min': '1875', 'locktime': '.*', - 'their_funding': '100000000msat', + 'their_funding_msat': '100000000msat', 'channel_max_msat': '16777215000msat', }) else: expected.update({ - 'channel_reserve_satoshis': '1000000msat', + 'channel_reserve_msat': '1000000msat', 'feerate_per_kw': '7500', - 'funding_satoshis': '100000000msat', + 'funding_msat': '100000000msat', 'push_msat': '0msat', }) @@ -1148,7 +1148,7 @@ def test_htlc_accepted_hook_forward_restart(node_factory, executor): assert onion['type'] == 'tlv' assert re.match(r'^11020203e80401..0608................$', onion['payload']) assert len(onion['shared_secret']) == 64 - assert onion['forward_amount'] == '1000msat' + assert onion['forward_msat'] == '1000msat' assert len(onion['next_onion']) == 2 * (1300 + 32 + 33 + 1) f1.result() diff --git a/tests/test_wallet.py b/tests/test_wallet.py index d53d4d9f1281..8cab032acc43 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -952,7 +952,7 @@ def test_transaction_annotations(node_factory, bitcoind): assert(len(txs) == 1) tx = txs[0] output = tx['outputs'][idx] - assert(output['type'] == 'deposit' and output['msat'] == Millisatoshi(1000000000)) + assert(output['type'] == 'deposit' and output['amount_msat'] == Millisatoshi(1000000000)) # ... and all other output should be change, and have no annotations types = [] diff --git a/tests/utils.py b/tests/utils.py index e56b7d6acd5f..682dc535059d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -189,7 +189,7 @@ def extract_utxos(moves): for ev in evs: if ev[0]['vout'] == m['vout']: ev[1] = m - assert ev[0]['output_value'] == m['output_value'] + assert ev[0]['output_msat'] == m['output_msat'] break return utxos diff --git a/wallet/reservation.c b/wallet/reservation.c index 7130159a4d14..f36622536265 100644 --- a/wallet/reservation.c +++ b/wallet/reservation.c @@ -413,7 +413,7 @@ static struct command_result *finish_psbt(struct command *cmd, json_add_psbt(response, "psbt", psbt); json_add_num(response, "feerate_per_kw", feerate_per_kw); json_add_num(response, "estimated_final_weight", weight); - json_add_amount_sat_only(response, "excess_msat", excess); + json_add_amount_sat_msat(response, "excess_msat", excess); if (excess_as_change) json_add_num(response, "change_outnum", change_outnum); if (reserve) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 005f4150de64..3c7802e2ba98 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -302,12 +302,12 @@ void json_add_amount_sat_compat(struct json_stream *result UNNEEDED, const char *msatfieldname) { fprintf(stderr, "json_add_amount_sat_compat called!\n"); abort(); } -/* Generated stub for json_add_amount_sat_only */ -void json_add_amount_sat_only(struct json_stream *result UNNEEDED, - const char *msatfieldname UNNEEDED, - struct amount_sat sat) +/* Generated stub for json_add_amount_sat_msat */ +void json_add_amount_sat_msat(struct json_stream *result UNNEEDED, + const char *msatfieldname UNNEEDED, + struct amount_sat sat) -{ fprintf(stderr, "json_add_amount_sat_only called!\n"); abort(); } +{ fprintf(stderr, "json_add_amount_sat_msat called!\n"); abort(); } /* Generated stub for json_add_bool */ void json_add_bool(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, bool value UNNEEDED) @@ -356,7 +356,7 @@ void json_add_short_channel_id(struct json_stream *response UNNEEDED, const struct short_channel_id *id UNNEEDED) { fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); } /* Generated stub for json_add_string */ -void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value UNNEEDED) +void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) { fprintf(stderr, "json_add_string called!\n"); abort(); } /* Generated stub for json_add_timeabs */ void json_add_timeabs(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 9d7ad17027d9..88df58ab3f24 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -558,7 +558,7 @@ static void json_transaction_details(struct json_stream *response, json_object_start(response, NULL); json_add_u32(response, "index", i); - json_add_amount_sat_only(response, "msat", sat); + json_add_amount_sats_deprecated(response, "msat", "amount_msat", sat); #if EXPERIMENTAL_FEATURES struct tx_annotation *ann = &tx->output_annotations[i]; From a52bdeee015bd7236d03a1763764a8db4acc6c08 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:47:11 +0930 Subject: [PATCH 0780/1530] common: add msat to sat convert helper. We don't always want to round down, sometimes we want to fail. Signed-off-by: Rusty Russell --- common/amount.c | 10 ++++++++++ common/amount.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/common/amount.c b/common/amount.c index fa1870700e24..e17a6804433b 100644 --- a/common/amount.c +++ b/common/amount.c @@ -18,6 +18,16 @@ bool amount_sat_to_msat(struct amount_msat *msat, return true; } +bool amount_msat_to_sat(struct amount_sat *sat, + struct amount_msat msat) +{ + if (msat.millisatoshis % MSAT_PER_SAT) + return false; + sat->satoshis = msat.millisatoshis / MSAT_PER_SAT; + return true; +} + + /* You can always truncate millisatoshis->satoshis. */ struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat) { diff --git a/common/amount.h b/common/amount.h index b7277d7ee536..b16e925930cc 100644 --- a/common/amount.h +++ b/common/amount.h @@ -53,6 +53,10 @@ struct amount_sat amount_sat(u64 satoshis); WARN_UNUSED_RESULT bool amount_sat_to_msat(struct amount_msat *msat, struct amount_sat sat); +/* You may not always be able to convert millisatoshis->satoshis without rounding. */ +WARN_UNUSED_RESULT bool amount_msat_to_sat(struct amount_sat *sat, + struct amount_msat msat); + /* You can always truncate millisatoshis->satoshis. */ struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat); From e2f0ca9cbea82e1fdbab04518de0028d11222bdf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:48:11 +0930 Subject: [PATCH 0781/1530] lightningd: don't add null for unset plugin options. In general, we don't like to use `null` in JSON: simply omit the field. I found this one because it broke our 'msat' parsing (made stricter in followup) which doesn't allow `null`. Signed-off-by: Rusty Russell Changelog-Deprecated: `listconfigs` `plugins` `options` which are not set are omitted, not `null`. --- lightningd/plugin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 1497439cc323..dad4854ec3db 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1952,7 +1952,8 @@ void json_add_opt_plugins_array(struct json_stream *response, opt->type, opt->values[0]); } else { - json_add_null(response, opt_name); + if (deprecated_apis) + json_add_null(response, opt_name); } } json_object_end(response); From ca69e293d1ea1ac60305481c36d57855fa61f91c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:49:11 +0930 Subject: [PATCH 0782/1530] coinmvt: don't use msats in fields not called "_msat". The new msat fields are turned into Millisatoshi, so handle that correctly too in tests too. Signed-off-by: Rusty Russell Changelog-Deprecated: Plugins: `coin_movement` notification: `balance`, `credit`, `debit` and `fees` (use `balance_msat`, `credit_msat`, `debit_msat` and `fees_msat`) --- lightningd/notification.c | 23 +++++++++++++++++----- tests/test_closing.py | 22 ++++++++++----------- tests/test_connection.py | 8 ++++---- tests/test_misc.py | 10 +++++----- tests/test_plugin.py | 28 +++++++++++++-------------- tests/test_wallet.py | 40 +++++++++++++++++++-------------------- tests/utils.py | 30 ++++++++++++++--------------- 7 files changed, 87 insertions(+), 74 deletions(-) diff --git a/lightningd/notification.c b/lightningd/notification.c index 00b29cf71691..403ae1187595 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -478,8 +479,13 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_add_string(stream, "originating_account", mvt->originating_acct); json_mvt_id(stream, mvt->type, &mvt->id); - json_add_amount_msat_only(stream, "credit", mvt->credit); - json_add_amount_msat_only(stream, "debit", mvt->debit); + if (deprecated_apis) { + json_add_amount_msat_only(stream, "credit", mvt->credit); + json_add_amount_msat_only(stream, "debit", mvt->debit); + } + json_add_amount_msat_only(stream, "credit_msat", mvt->credit); + json_add_amount_msat_only(stream, "debit_msat", mvt->debit); + /* Only chain movements */ if (mvt->output_val) json_add_amount_sats_deprecated(stream, "output_value", @@ -489,9 +495,13 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_add_num(stream, "output_count", mvt->output_count); - if (mvt->fees) - json_add_amount_msat_only(stream, "fees", + if (mvt->fees) { + if (deprecated_apis) + json_add_amount_msat_only(stream, "fees", + *mvt->fees); + json_add_amount_msat_only(stream, "fees_msat", *mvt->fees); + } json_array_start(stream, "tags"); for (size_t i = 0; i < tal_count(mvt->tags); i++) @@ -535,7 +545,10 @@ static void balance_snapshot_notification_serialize(struct json_stream *stream, json_object_start(stream, NULL); json_add_string(stream, "account_id", snap->accts[i]->acct_id); - json_add_amount_msat_only(stream, "balance", + if (deprecated_apis) + json_add_amount_msat_only(stream, "balance", + snap->accts[i]->balance); + json_add_amount_msat_only(stream, "balance_msat", snap->accts[i]->balance); json_add_string(stream, "coin_type", snap->accts[i]->bip173_name); json_object_end(stream); diff --git a/tests/test_closing.py b/tests/test_closing.py index 36c510b63cbd..7762013925e6 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -852,17 +852,17 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): l2.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE') channel_mvts_1 = [ - {'type': 'chain_mvt', 'credit': 506432000, 'debit': 0, 'tags': ['channel_open', 'opener', 'leased']}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 6432000, 'tags': ['lease_fee'], 'fees': '0msat'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 10000, 'tags': ['invoice'], 'fees': '0msat'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 499990000, 'tags': ['channel_close']}, + {'type': 'chain_mvt', 'credit_msat': 506432000, 'debit_msat': 0, 'tags': ['channel_open', 'opener', 'leased']}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 6432000, 'tags': ['lease_fee'], 'fees_msat': '0msat'}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 10000, 'tags': ['invoice'], 'fees_msat': '0msat'}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 499990000, 'tags': ['channel_close']}, ] channel_mvts_2 = [ - {'type': 'chain_mvt', 'credit': 500000000, 'debit': 0, 'tags': ['channel_open', 'leased']}, - {'type': 'channel_mvt', 'credit': 6432000, 'debit': 0, 'tags': ['lease_fee'], 'fees': '0msat'}, - {'type': 'channel_mvt', 'credit': 10000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 506442000, 'tags': ['channel_close']}, + {'type': 'chain_mvt', 'credit_msat': 500000000, 'debit_msat': 0, 'tags': ['channel_open', 'leased']}, + {'type': 'channel_mvt', 'credit_msat': 6432000, 'debit_msat': 0, 'tags': ['lease_fee'], 'fees_msat': '0msat'}, + {'type': 'channel_mvt', 'credit_msat': 10000, 'debit_msat': 0, 'tags': ['invoice'], 'fees_msat': '0msat'}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 506442000, 'tags': ['channel_close']}, ] check_coin_moves(l1, channel_id, channel_mvts_1, chainparams) @@ -1285,11 +1285,11 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): if not chainparams['elements']: # Also check snapshots expected_bals_2 = [ - {'blockheight': 101, 'accounts': [{'balance': '0msat'}]}, - {'blockheight': 108, 'accounts': [{'balance': '995433000msat'}, {'balance': '500000000msat'}, {'balance': '499994999msat'}]}, + {'blockheight': 101, 'accounts': [{'balance_msat': '0msat'}]}, + {'blockheight': 108, 'accounts': [{'balance_msat': '995433000msat'}, {'balance_msat': '500000000msat'}, {'balance_msat': '499994999msat'}]}, # There's a duplicate because we stop and restart l2 twice # (both times at block 108) - {'blockheight': 108, 'accounts': [{'balance': '995433000msat'}, {'balance': '500000000msat'}, {'balance': '499994999msat'}]}, + {'blockheight': 108, 'accounts': [{'balance_msat': '995433000msat'}, {'balance_msat': '500000000msat'}, {'balance_msat': '499994999msat'}]}, ] check_balance_snaps(l2, expected_bals_2) diff --git a/tests/test_connection.py b/tests/test_connection.py index 308ca280522f..5a643993f0d9 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1157,12 +1157,12 @@ def test_funding_push(node_factory, bitcoind, chainparams): chanid = first_channel_id(l2, l1) channel_mvts_1 = [ - {'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tags': ['channel_open', 'opener']}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tags': ['pushed'], 'fees': '0msat'}, + {'type': 'chain_mvt', 'credit_msat': 16777215000, 'debit_msat': 0, 'tags': ['channel_open', 'opener']}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 20000000, 'tags': ['pushed'], 'fees_msat': '0msat'}, ] channel_mvts_2 = [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']}, - {'type': 'channel_mvt', 'credit': 20000000, 'debit': 0, 'tags': ['pushed'], 'fees': '0msat'}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 0, 'tags': ['channel_open']}, + {'type': 'channel_mvt', 'credit_msat': 20000000, 'debit_msat': 0, 'tags': ['pushed'], 'fees_msat': '0msat'}, ] check_coin_moves(l1, chanid, channel_mvts_1, chainparams) check_coin_moves(l2, chanid, channel_mvts_2, chainparams) diff --git a/tests/test_misc.py b/tests/test_misc.py index 37adca9717f4..41b2b0b2ba95 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -626,11 +626,11 @@ def dont_spend_outputs(n, txid): assert account_balance(l1, 'wallet') == 0 external_moves = [ - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 11957603000, 'debit': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 2000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 11957603000, 'debit_msat': 0, 'tags': ['deposit']}, ] check_coin_moves(l1, 'external', external_moves, chainparams) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 40a4250ee9e3..ebbf30e19a7f 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1921,26 +1921,26 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): """Verify that channel coin movements are triggered correctly. """ l1_l2_mvts = [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']}, - {'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tags': ['routed'], 'fees': '1001msat'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['routed'], 'fees': '501msat'}, - {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tags': ['invoice'], 'fees': '0msat'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 100001001, 'tags': ['channel_close']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 0, 'tags': ['channel_open']}, + {'type': 'channel_mvt', 'credit_msat': 100001001, 'debit_msat': 0, 'tags': ['routed'], 'fees_msat': '1001msat'}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 50000000, 'tags': ['routed'], 'fees_msat': '501msat'}, + {'type': 'channel_mvt', 'credit_msat': 100000000, 'debit_msat': 0, 'tags': ['invoice'], 'fees_msat': '0msat'}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 50000000, 'tags': ['invoice'], 'fees_msat': '0msat'}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 100001001, 'tags': ['channel_close']}, ] l2_l3_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['channel_open', 'opener']}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tags': ['routed'], 'fees': '1001msat'}, - {'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tags': ['routed'], 'fees': '501msat'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tags': ['channel_close']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['channel_open', 'opener']}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 100000000, 'tags': ['routed'], 'fees_msat': '1001msat'}, + {'type': 'channel_mvt', 'credit_msat': 50000501, 'debit_msat': 0, 'tags': ['routed'], 'fees_msat': '501msat'}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 950000501, 'tags': ['channel_close']}, ] l3_l2_mvts = [ - {'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tags': ['channel_open']}, - {'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tags': ['invoice'], 'fees': '0msat'}, - {'type': 'channel_mvt', 'credit': 0, 'debit': 50000501, 'tags': ['invoice'], 'fees': '501msat'}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 49999499, 'tags': ['channel_close']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 0, 'tags': ['channel_open']}, + {'type': 'channel_mvt', 'credit_msat': 100000000, 'debit_msat': 0, 'tags': ['invoice'], 'fees_msat': '0msat'}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 50000501, 'tags': ['invoice'], 'fees_msat': '501msat'}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 49999499, 'tags': ['channel_close']}, ] coin_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 8cab032acc43..2c6ed00724c2 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -864,26 +864,26 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): l1.rpc.signpsbt(invalid_psbt) wallet_coin_mvts = [ - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tags': ['deposit']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, - {'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 1000000000, 'debit_msat': 0, 'tags': ['deposit']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, + {'type': 'chain_mvt', 'credit_msat': 0, 'debit_msat': 1000000000, 'tags': ['withdrawal']}, ] check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams) diff --git a/tests/utils.py b/tests/utils.py index 682dc535059d..d06292ec0e11 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -74,18 +74,18 @@ def expected_channel_features(wumbo_channels=False, extra=[]): def move_matches(exp, mv): if mv['type'] != exp['type']: return False - if mv['credit'] != "{}msat".format(exp['credit']): + if Millisatoshi(mv['credit_msat']) != Millisatoshi(exp['credit_msat']): return False - if mv['debit'] != "{}msat".format(exp['debit']): + if Millisatoshi(mv['debit_msat']) != Millisatoshi(exp['debit_msat']): return False if mv['tags'] != exp['tags']: return False - if 'fees' in exp: - if 'fees' not in mv: + if 'fees_msat' in exp: + if 'fees_msat' not in mv: return False - if mv['fees'] != exp['fees']: + if Millisatoshi(mv['fees_msat']) != Millisatoshi(exp['fees_msat']): return False - elif 'fees' in mv: + elif 'fees_msat' in mv: return False return True @@ -103,8 +103,8 @@ def check_balance_snaps(n, expected_bals): assert snap['blockheight'] == exp['blockheight'] for acct, exp_acct in zip(snap['accounts'], exp['accounts']): # FIXME: also check 'account_id's (these change every run) - for item in ['balance']: - assert acct[item] == exp_acct[item] + for item in ['balance_msat']: + assert Millisatoshi(acct[item]) == Millisatoshi(exp_acct[item]) def check_coin_moves(n, account_id, expected_moves, chainparams): @@ -125,12 +125,12 @@ def check_coin_moves(n, account_id, expected_moves, chainparams): node_id = n.info['id'] acct_moves = [m for m in moves if m['account_id'] == account_id] for mv in acct_moves: - print("{{'type': '{}', 'credit': {}, 'debit': {}, 'tags': '{}' , ['fees'?: '{}']}}," + print("{{'type': '{}', 'credit_msat': {}, 'debit_msat': {}, 'tags': '{}' , ['fees_msat'?: '{}']}}," .format(mv['type'], - Millisatoshi(mv['credit']).millisatoshis, - Millisatoshi(mv['debit']).millisatoshis, + Millisatoshi(mv['credit_msat']).millisatoshis, + Millisatoshi(mv['debit_msat']).millisatoshis, mv['tags'], - mv['fees'] if 'fees' in mv else '')) + mv['fees_msat'] if 'fees_msat' in mv else '')) assert mv['version'] == 2 assert mv['node_id'] == node_id assert mv['timestamp'] > 0 @@ -165,10 +165,10 @@ def account_balance(n, account_id): moves = dedupe_moves(n.rpc.call('listcoinmoves_plugin')['coin_moves']) chan_moves = [m for m in moves if m['account_id'] == account_id] assert len(chan_moves) > 0 - m_sum = 0 + m_sum = Millisatoshi(0) for m in chan_moves: - m_sum += int(m['credit'][:-4]) - m_sum -= int(m['debit'][:-4]) + m_sum += Millisatoshi(m['credit_msat']) + m_sum -= Millisatoshi(m['debit_msat']) return m_sum From cd7e784d6f503553548654ca056c8faaa091348d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:50:11 +0930 Subject: [PATCH 0783/1530] lightningd: change `msatoshi` args to `amount_msat`. This is consistent with our output changes, and increases consistency. It also keeps future sanity checks happy, that we only use JSON msat helpers with '_msat' fields. Signed-off-by: Rusty Russell Changelog-Changed: JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice`: `msatoshi` argument is now called `amount_msat` to match other fields. Changelog-Deprecated: JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice` `msatoshi` (use `amount_msat`) --- .msggen.json | 7 ++ cln-grpc/proto/node.proto | 14 +-- cln-grpc/src/convert.rs | 14 +-- cln-rpc/src/model.rs | 28 +++--- contrib/pyln-client/pyln/client/lightning.py | 73 ++++++++++----- contrib/pyln-testing/pyln/testing/utils.py | 8 +- doc/schemas/getroute.request.json | 4 +- doc/schemas/invoice.request.json | 4 +- doc/schemas/keysend.request.json | 4 +- doc/schemas/pay.request.json | 2 +- doc/schemas/sendonion.request.json | 2 +- doc/schemas/sendpay.request.json | 8 +- lightningd/invoice.c | 2 +- lightningd/pay.c | 4 +- plugins/fetchinvoice.c | 4 +- plugins/keysend.c | 4 +- plugins/libplugin-pay.c | 2 +- plugins/pay.c | 2 +- plugins/topology.c | 2 +- tests/test_cln_rs.py | 4 +- tests/test_closing.py | 8 +- tests/test_invoices.py | 36 +++---- tests/test_misc.py | 2 +- tests/test_pay.py | 98 ++++++++++---------- tests/test_plugin.py | 10 +- 25 files changed, 192 insertions(+), 154 deletions(-) diff --git a/.msggen.json b/.msggen.json index 5d4265428da1..550155b881a6 100644 --- a/.msggen.json +++ b/.msggen.json @@ -443,6 +443,7 @@ "Getinfo.warning_lightningd_sync": 17 }, "GetrouteRequest": { + "GetRoute.amount_msat": 9, "GetRoute.cltv": 4, "GetRoute.exclude[]": 7, "GetRoute.fromid": 5, @@ -464,6 +465,7 @@ "GetRoute.route[].style": 6 }, "InvoiceRequest": { + "Invoice.amount_msat": 10, "Invoice.cltv": 6, "Invoice.deschashonly": 9, "Invoice.description": 2, @@ -486,6 +488,7 @@ "Invoice.warning_private_unused": 8 }, "KeysendRequest": { + "KeySend.amount_msat": 10, "KeySend.destination": 1, "KeySend.exemptfee": 7, "KeySend.extratlvs": 9, @@ -821,6 +824,7 @@ "NewAddr.p2sh-segwit": 2 }, "PayRequest": { + "Pay.amount_msat": 13, "Pay.bolt11": 1, "Pay.description": 12, "Pay.exclude": 10, @@ -859,6 +863,7 @@ "SendOnion.first_hop.id": 1 }, "SendonionRequest": { + "SendOnion.amount_msat": 12, "SendOnion.bolt11": 7, "SendOnion.destination": 9, "SendOnion.first_hop": 2, @@ -887,6 +892,7 @@ "SendOnion.status": 3 }, "SendpayRequest": { + "SendPay.amount_msat": 10, "SendPay.bolt11": 5, "SendPay.groupid": 9, "SendPay.label": 3, @@ -914,6 +920,7 @@ "SendPay.status": 4 }, "SendpayRoute": { + "SendPay.route[].amount_msat": 5, "SendPay.route[].channel": 4, "SendPay.route[].delay": 3, "SendPay.route[].id": 2, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 6925b54aed12..1b5182a46ee8 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -291,7 +291,7 @@ message SendpayRequest { repeated SendpayRoute route = 1; bytes payment_hash = 2; optional string label = 3; - optional Amount msatoshi = 4; + optional Amount amount_msat = 10; optional string bolt11 = 5; optional bytes payment_secret = 6; optional uint32 partid = 7; @@ -322,7 +322,7 @@ message SendpayResponse { } message SendpayRoute { - Amount msatoshi = 1; + optional Amount amount_msat = 5; bytes id = 2; uint32 delay = 3; string channel = 4; @@ -559,7 +559,7 @@ message DelinvoiceResponse { } message InvoiceRequest { - AmountOrAny msatoshi = 1; + AmountOrAny amount_msat = 10; string description = 2; string label = 3; optional uint64 expiry = 7; @@ -638,7 +638,7 @@ message SendonionRequest { repeated bytes shared_secrets = 5; optional uint32 partid = 6; optional string bolt11 = 7; - optional Amount msatoshi = 8; + optional Amount amount_msat = 12; optional bytes destination = 9; optional bytes localofferid = 10; optional uint64 groupid = 11; @@ -775,7 +775,7 @@ message ListtransactionsTransactionsOutputs { message PayRequest { string bolt11 = 1; - optional Amount msatoshi = 2; + optional Amount amount_msat = 13; optional string label = 3; optional double riskfactor = 8; optional double maxfeepercent = 4; @@ -945,7 +945,7 @@ message WithdrawResponse { message KeysendRequest { bytes destination = 1; - Amount msatoshi = 2; + Amount amount_msat = 10; optional string label = 3; optional double maxfeepercent = 4; optional uint32 retry_for = 5; @@ -1134,7 +1134,7 @@ message FeeratesOnchain_fee_estimates { message GetrouteRequest { bytes id = 1; - Amount msatoshi = 2; + Amount amount_msat = 9; uint64 riskfactor = 3; optional double cltv = 4; optional bytes fromid = 5; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 18afaedfead3..87a850457e34 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -980,7 +980,7 @@ impl From<&pb::ListfundsRequest> for requests::ListfundsRequest { impl From<&pb::SendpayRoute> for requests::SendpayRoute { fn from(c: &pb::SendpayRoute) -> Self { Self { - msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey delay: c.delay as u16, // Rule #1 for type u16 channel: cln_rpc::primitives::ShortChannelId::from_str(&c.channel).unwrap(), // Rule #1 for type short_channel_id @@ -995,7 +995,7 @@ impl From<&pb::SendpayRequest> for requests::SendpayRequest { route: c.route.iter().map(|s| s.into()).collect(), // Rule #4 payment_hash: c.payment_hash.clone().try_into().unwrap(), // Rule #1 for type hash label: c.label.clone(), // Rule #1 for type string? - msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? bolt11: c.bolt11.clone(), // Rule #1 for type string? payment_secret: c.payment_secret.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type secret? partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? @@ -1152,7 +1152,7 @@ impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { impl From<&pb::InvoiceRequest> for requests::InvoiceRequest { fn from(c: &pb::InvoiceRequest) -> Self { Self { - msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat_or_any + amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat_or_any description: c.description.clone(), // Rule #1 for type string label: c.label.clone(), // Rule #1 for type string expiry: c.expiry.clone(), // Rule #1 for type u64? @@ -1196,7 +1196,7 @@ impl From<&pb::SendonionRequest> for requests::SendonionRequest { shared_secrets: Some(c.shared_secrets.iter().map(|s| s.clone().try_into().unwrap()).collect()), // Rule #4 partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? bolt11: c.bolt11.clone(), // Rule #1 for type string? - msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? destination: c.destination.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? localofferid: c.localofferid.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type hash? groupid: c.groupid.clone(), // Rule #1 for type u64? @@ -1228,7 +1228,7 @@ impl From<&pb::PayRequest> for requests::PayRequest { fn from(c: &pb::PayRequest) -> Self { Self { bolt11: c.bolt11.clone(), // Rule #1 for type string - msatoshi: c.msatoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat? + amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? label: c.label.clone(), // Rule #1 for type string? riskfactor: c.riskfactor.clone(), // Rule #1 for type number? maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type number? @@ -1310,7 +1310,7 @@ impl From<&pb::KeysendRequest> for requests::KeysendRequest { fn from(c: &pb::KeysendRequest) -> Self { Self { destination: cln_rpc::primitives::Pubkey::from_slice(&c.destination).unwrap(), // Rule #1 for type pubkey - msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat label: c.label.clone(), // Rule #1 for type string? maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type number? retry_for: c.retry_for.clone(), // Rule #1 for type u32? @@ -1428,7 +1428,7 @@ impl From<&pb::GetrouteRequest> for requests::GetrouteRequest { fn from(c: &pb::GetrouteRequest) -> Self { Self { id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey - msatoshi: c.msatoshi.as_ref().unwrap().into(), // Rule #1 for type msat + amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat riskfactor: c.riskfactor.clone(), // Rule #1 for type u64 cltv: c.cltv.clone(), // Rule #1 for type number? fromid: c.fromid.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 3155667ee8ac..52369a3326c1 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -138,8 +138,8 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendpayRoute { - #[serde(alias = "msatoshi")] - pub msatoshi: Amount, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, #[serde(alias = "id")] pub id: Pubkey, #[serde(alias = "delay")] @@ -156,8 +156,8 @@ pub mod requests { pub payment_hash: Sha256, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, - #[serde(alias = "msatoshi", skip_serializing_if = "Option::is_none")] - pub msatoshi: Option, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] pub bolt11: Option, #[serde(alias = "payment_secret", skip_serializing_if = "Option::is_none")] @@ -351,8 +351,8 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct InvoiceRequest { - #[serde(alias = "msatoshi")] - pub msatoshi: AmountOrAny, + #[serde(alias = "amount_msat")] + pub amount_msat: AmountOrAny, #[serde(alias = "description")] pub description: String, #[serde(alias = "label")] @@ -413,8 +413,8 @@ pub mod requests { pub partid: Option, #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] pub bolt11: Option, - #[serde(alias = "msatoshi", skip_serializing_if = "Option::is_none")] - pub msatoshi: Option, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] pub destination: Option, #[serde(alias = "localofferid", skip_serializing_if = "Option::is_none")] @@ -462,8 +462,8 @@ pub mod requests { pub struct PayRequest { #[serde(alias = "bolt11")] pub bolt11: String, - #[serde(alias = "msatoshi", skip_serializing_if = "Option::is_none")] - pub msatoshi: Option, + #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] + pub amount_msat: Option, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, #[serde(alias = "riskfactor", skip_serializing_if = "Option::is_none")] @@ -567,8 +567,8 @@ pub mod requests { pub struct KeysendRequest { #[serde(alias = "destination")] pub destination: Pubkey, - #[serde(alias = "msatoshi")] - pub msatoshi: Amount, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, #[serde(alias = "maxfeepercent", skip_serializing_if = "Option::is_none")] @@ -702,8 +702,8 @@ pub mod requests { pub struct GetrouteRequest { #[serde(alias = "id")] pub id: Pubkey, - #[serde(alias = "msatoshi")] - pub msatoshi: Amount, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, #[serde(alias = "riskfactor")] pub riskfactor: u64, #[serde(alias = "cltv", skip_serializing_if = "Option::is_none")] diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index c0c649db4b8e..539e9e49cd56 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -615,7 +615,7 @@ def dev_memleak(self): """ return self.call("dev-memleak") - def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, + def dev_pay(self, bolt11, amount_msat=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, use_shadow=True, exclude=None): """ @@ -624,7 +624,7 @@ def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, """ payload = { "bolt11": bolt11, - "msatoshi": msatoshi, + "amount_msat": amount_msat, "label": label, "riskfactor": riskfactor, "maxfeepercent": maxfeepercent, @@ -797,19 +797,26 @@ def getpeer(self, peer_id, level=None): res = self.call("listpeers", payload) return res.get("peers") and res["peers"][0] or None - def getroute(self, node_id, msatoshi, riskfactor, cltv=9, fromid=None, - fuzzpercent=None, exclude=None, maxhops=None): + def getroute(self, node_id, amount_msat=None, riskfactor=None, cltv=9, fromid=None, + fuzzpercent=None, exclude=None, maxhops=None, msatoshi=None): """ - Show route to {id} for {msatoshi}, using {riskfactor} and optional + Show route to {id} for {amount_msat}, using {riskfactor} and optional {cltv} (default 9). If specified search from {fromid} otherwise use this node as source. Randomize the route with up to {fuzzpercent} (0.0 -> 100.0, default 5.0). {exclude} is an optional array of scid/direction or node-id to exclude. Limit the number of hops in the route to {maxhops}. """ + if msatoshi: + amount_msat = msatoshi + if riskfactor is None: + raise TypeError("getroute() missing 'riskfactor'") + if amount_msat is None: + raise TypeError("getroute() missing 'amount_msat'") + payload = { "id": node_id, - "msatoshi": msatoshi, + "amount_msat": amount_msat, "riskfactor": riskfactor, "cltv": cltv, "fromid": fromid, @@ -828,14 +835,22 @@ def help(self, command=None): } return self.call("help", payload) - def invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, - preimage=None, exposeprivatechannels=None, cltv=None, deschashonly=None): + def invoice(self, amount_msat=None, label=None, description=None, expiry=None, fallbacks=None, + preimage=None, exposeprivatechannels=None, cltv=None, deschashonly=None, msatoshi=None): """ - Create an invoice for {msatoshi} with {label} and {description} with + Create an invoice for {amount_msat} with {label} and {description} with optional {expiry} seconds (default 1 week). """ + if msatoshi: + amount_msat = msatoshi + if label is None: + raise TypeError("invoice() missing 'label'") + if description is None: + raise TypeError("invoice() missing 'description'") + if amount_msat is None: + raise TypeError("invoice() missing 'amount_msat'") payload = { - "msatoshi": msatoshi, + "amount_msat": amount_msat, "label": label, "description": description, "expiry": expiry, @@ -995,18 +1010,21 @@ def newaddr(self, addresstype=None): """ return self.call("newaddr", {"addresstype": addresstype}) - def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, + def pay(self, bolt11, amount_msat=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, localofferid=None, exclude=None, - maxfee=None, description=None): + maxfee=None, description=None, msatoshi=None): """ - Send payment specified by {bolt11} with {msatoshi} + Send payment specified by {bolt11} with {amount_msat} (ignored if {bolt11} has an amount), optional {label} and {riskfactor} (default 1.0). """ + # Deprecated usage + if msatoshi: + amount_msat = msatoshi payload = { "bolt11": bolt11, - "msatoshi": msatoshi, + "amount_msat": amount_msat, "label": label, "riskfactor": riskfactor, "maxfeepercent": maxfeepercent, @@ -1132,15 +1150,18 @@ def plugin_rescan(self): } return self.call("plugin", payload) - def sendpay(self, route, payment_hash, label=None, msatoshi=None, bolt11=None, payment_secret=None, partid=None, groupid=None, payment_metadata=None): + def sendpay(self, route, payment_hash, label=None, amount_msat=None, bolt11=None, payment_secret=None, partid=None, groupid=None, payment_metadata=None, msatoshi=None): """ Send along {route} in return for preimage of {payment_hash}. """ + # Deprecated usage + if msatoshi: + amount_msat = msatoshi payload = { "route": route, "payment_hash": payment_hash, "label": label, - "msatoshi": msatoshi, + "amount_msat": amount_msat, "bolt11": bolt11, "payment_secret": payment_secret, "partid": partid, @@ -1151,8 +1172,8 @@ def sendpay(self, route, payment_hash, label=None, msatoshi=None, bolt11=None, p def sendonion( self, onion, first_hop, payment_hash, label=None, - shared_secrets=None, partid=None, bolt11=None, msatoshi=None, - destination=None + shared_secrets=None, partid=None, bolt11=None, amount_msat=None, + destination=None, msatoshi=None ): """Send an outgoing payment using the specified onion. @@ -1161,6 +1182,9 @@ def sendonion( internal handling, but not required. """ + # Deprecated usage + if msatoshi: + amount_msat = msatoshi payload = { "onion": onion, "first_hop": first_hop, @@ -1169,7 +1193,7 @@ def sendonion( "shared_secrets": shared_secrets, "partid": partid, "bolt11": bolt11, - "msatoshi": msatoshi, + "amount_msat": amount_msat, "destination": destination, } return self.call("sendonion", payload) @@ -1428,11 +1452,16 @@ def getsharedsecret(self, point, **kwargs): payload.update({k: v for k, v in kwargs.items()}) return self.call("getsharedsecret", payload) - def keysend(self, destination, msatoshi, label=None, maxfeepercent=None, + def keysend(self, destination, amount_msat=None, label=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, - extratlvs=None): + extratlvs=None, msatoshi=None): """ """ + # Deprecated usage + if msatoshi: + amount_msat = msatoshi + if amount_msat is None: + raise TypeError("keysend() missing 'amount_msat'") if extratlvs is not None and not isinstance(extratlvs, dict): raise ValueError( @@ -1441,7 +1470,7 @@ def keysend(self, destination, msatoshi, label=None, maxfeepercent=None, payload = { "destination": destination, - "msatoshi": msatoshi, + "amount_msat": amount_msat, "label": label, "maxfeepercent": maxfeepercent, "retry_for": retry_for, diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 90ec96a10ed2..dc903376ebd2 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1152,23 +1152,23 @@ def config(self, config_name): except RpcError: return None - def dev_pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, + def dev_pay(self, bolt11, amount_msat=None, label=None, riskfactor=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, use_shadow=True, exclude=[]): """Wrapper for rpc.dev_pay which suppresses the request schema""" # FIXME? dev options are not in schema old_check = self.rpc.check_request_schemas self.rpc.check_request_schemas = False - ret = self.rpc.dev_pay(bolt11, msatoshi, label, riskfactor, + ret = self.rpc.dev_pay(bolt11, amount_msat, label, riskfactor, maxfeepercent, retry_for, maxdelay, exemptfee, use_shadow, exclude) self.rpc.check_request_schemas = old_check return ret - def dev_invoice(self, msatoshi, label, description, expiry=None, fallbacks=None, preimage=None, exposeprivatechannels=None, cltv=None, dev_routes=None): + def dev_invoice(self, amount_msat, label, description, expiry=None, fallbacks=None, preimage=None, exposeprivatechannels=None, cltv=None, dev_routes=None): """Wrapper for rpc.invoice() with dev-routes option""" payload = { - "msatoshi": msatoshi, + "amount_msat": amount_msat, "label": label, "description": description, "expiry": expiry, diff --git a/doc/schemas/getroute.request.json b/doc/schemas/getroute.request.json index 8a10aaf8c1c6..17be4ba99296 100644 --- a/doc/schemas/getroute.request.json +++ b/doc/schemas/getroute.request.json @@ -3,7 +3,7 @@ "type": "object", "required": [ "id", - "msatoshi", + "amount_msat", "riskfactor" ], "properties": { @@ -11,7 +11,7 @@ "type": "pubkey", "description": "" }, - "msatoshi": { + "amount_msat": { "type": "msat", "description": "" }, diff --git a/doc/schemas/invoice.request.json b/doc/schemas/invoice.request.json index a22cd12b056c..62cceaeda81c 100644 --- a/doc/schemas/invoice.request.json +++ b/doc/schemas/invoice.request.json @@ -3,12 +3,12 @@ "type": "object", "additionalProperties": false, "required": [ - "msatoshi", + "amount_msat", "label", "description" ], "properties": { - "msatoshi": { + "amount_msat": { "type": "msat_or_any", "description": "" }, diff --git a/doc/schemas/keysend.request.json b/doc/schemas/keysend.request.json index fef9a5ab57e0..64e74e73c7b7 100644 --- a/doc/schemas/keysend.request.json +++ b/doc/schemas/keysend.request.json @@ -4,13 +4,13 @@ "additionalProperties": false, "required": [ "destination", - "msatoshi" + "amount_msat" ], "properties": { "destination": { "type": "pubkey" }, - "msatoshi": { + "amount_msat": { "type": "msat" }, "label": { diff --git a/doc/schemas/pay.request.json b/doc/schemas/pay.request.json index a02750911e46..464600b9a22d 100644 --- a/doc/schemas/pay.request.json +++ b/doc/schemas/pay.request.json @@ -9,7 +9,7 @@ "bolt11": { "type": "string" }, - "msatoshi": { + "amount_msat": { "type": "msat" }, "label": { diff --git a/doc/schemas/sendonion.request.json b/doc/schemas/sendonion.request.json index c947f958e5a0..318932e2e57f 100644 --- a/doc/schemas/sendonion.request.json +++ b/doc/schemas/sendonion.request.json @@ -48,7 +48,7 @@ "bolt11": { "type": "string" }, - "msatoshi": { + "amount_msat": { "type": "msat" }, "destination": { diff --git a/doc/schemas/sendpay.request.json b/doc/schemas/sendpay.request.json index b764999a6e6e..c811c9fe1314 100644 --- a/doc/schemas/sendpay.request.json +++ b/doc/schemas/sendpay.request.json @@ -12,15 +12,17 @@ "items": { "type": "object", "required": [ - "msatoshi", "id", "delay", "channel" ], "properties": { - "msatoshi": { + "amount_msat": { "type": "msat" }, + "msatoshi": { + "deprecated": "true" + }, "id": { "type": "pubkey" }, @@ -39,7 +41,7 @@ "label": { "type": "string" }, - "msatoshi": { + "amount_msat": { "type": "msat" }, "bolt11": { diff --git a/lightningd/invoice.c b/lightningd/invoice.c index e68fb92208f7..08dbde6122b6 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1147,7 +1147,7 @@ static struct command_result *json_invoice(struct command *cmd, info->cmd = cmd; if (!param(cmd, buffer, params, - p_req("msatoshi", param_positive_msat_or_any, &msatoshi_val), + p_req("amount_msat|msatoshi", param_positive_msat_or_any, &msatoshi_val), p_req("label", param_label, &info->label), p_req("description", param_escaped_string, &desc_val), p_opt_def("expiry", param_time, &expiry, 3600*24*7), diff --git a/lightningd/pay.c b/lightningd/pay.c index 0f0745786eb0..ab7ec6adf266 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1289,7 +1289,7 @@ static struct command_result *json_sendonion(struct command *cmd, p_opt_def("partid", param_u64, &partid, 0), /* FIXME: parameter should be invstring now */ p_opt("bolt11", param_string, &invstring), - p_opt_def("msatoshi", param_msat, &msat, AMOUNT_MSAT(0)), + p_opt_def("amount_msat|msatoshi", param_msat, &msat, AMOUNT_MSAT(0)), p_opt("destination", param_node_id, &destination), p_opt("localofferid", param_sha256, &local_offer_id), p_opt("groupid", param_u64, &group), @@ -1441,7 +1441,7 @@ static struct command_result *json_sendpay(struct command *cmd, p_req("route", param_route_hops, &route), p_req("payment_hash", param_sha256, &rhash), p_opt("label", param_escaped_string, &label), - p_opt("msatoshi", param_msat, &msat), + p_opt("amount_msat|msatoshi", param_msat, &msat), /* FIXME: parameter should be invstring now */ p_opt("bolt11", param_string, &invstring), p_opt("payment_secret", param_secret, &payment_secret), diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index f94400812e21..cca83e0c2823 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -1233,7 +1233,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd, invreq = tlv_invoice_request_new(sent); if (!param(cmd, buffer, params, p_req("offer", param_offer, &sent->offer), - p_opt("msatoshi", param_msat, &msat), + p_opt("amount_msat|msatoshi", param_msat, &msat), p_opt("quantity", param_u64, &invreq->quantity), p_opt("recurrence_counter", param_number, &invreq->recurrence_counter), @@ -1647,7 +1647,7 @@ static struct command_result *json_sendinvoice(struct command *cmd, if (!param(cmd, buffer, params, p_req("offer", param_offer, &sent->offer), p_req("label", param_label, &sent->inv_label), - p_opt("msatoshi", param_msat, &msat), + p_opt("amount_msat|msatoshi", param_msat, &msat), p_opt_def("timeout", param_number, &timeout, 90), p_opt("quantity", param_u64, &sent->inv->quantity), NULL)) diff --git a/plugins/keysend.c b/plugins/keysend.c index 47f9440c16c9..17d8ccede87a 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -155,7 +155,7 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, #endif if (!param(cmd, buf, params, p_req("destination", param_node_id, &destination), - p_req("msatoshi", param_msat, &msat), + p_req("amount_msat|msatoshi", param_msat, &msat), p_opt("label", param_string, &label), p_opt_def("maxfeepercent", param_millionths, &maxfee_pct_millionths, 500000), @@ -460,7 +460,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd, ki); plugin_log(cmd->plugin, LOG_INFORM, "Inserting a new invoice for keysend with payment_hash %s", type_to_string(tmpctx, struct sha256, &payment_hash)); - json_add_string(req->js, "msatoshi", "any"); + json_add_string(req->js, "amount_msat", "any"); json_add_string(req->js, "label", ki->label); json_add_string(req->js, "description", "Spontaneous incoming payment through keysend"); json_add_preimage(req->js, "preimage", &ki->payment_preimage); diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index e415cf99d68f..24bb3fbb0611 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -1554,7 +1554,7 @@ static struct command_result *payment_createonion_success(struct command *cmd, json_object_end(req->js); json_add_sha256(req->js, "payment_hash", p->payment_hash); - json_add_amount_msat_only(req->js, "msatoshi", p->amount); + json_add_amount_msat_only(req->js, "amount_msat", p->amount); json_array_start(req->js, "shared_secrets"); secrets = p->createonion_response->shared_secrets; diff --git a/plugins/pay.c b/plugins/pay.c index 51b632a03660..142ae44a79e9 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -957,7 +957,7 @@ static struct command_result *json_pay(struct command *cmd, if (!param(cmd, buf, params, /* FIXME: parameter should be invstring now */ p_req("bolt11", param_string, &b11str), - p_opt("msatoshi", param_msat, &msat), + p_opt("amount_msat|msatoshi", param_msat, &msat), p_opt("label", param_string, &label), p_opt_def("riskfactor", param_millionths, &riskfactor_millionths, 10000000), diff --git a/plugins/topology.c b/plugins/topology.c index e0807da3db91..c2ce34ce00a8 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -130,7 +130,7 @@ static struct command_result *json_getroute(struct command *cmd, if (!param(cmd, buffer, params, p_req("id", param_node_id, &destination), - p_req("msatoshi", param_msat, &msat), + p_req("amount_msat|msatoshi", param_msat, &msat), p_req("riskfactor", param_millionths, &riskfactor_millionths), p_opt_def("cltv", param_number, &cltv, 9), p_opt_def("fromid", param_node_id, &source, local_id), diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index c8b9b7e983b6..7524ad3864c0 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -104,7 +104,7 @@ def test_grpc_connect(node_factory): print(response) inv = stub.Invoice(nodepb.InvoiceRequest( - msatoshi=AmountOrAny(any=True), + amount_msat=AmountOrAny(any=True), description="hello", label="lbl1", preimage=b"\x00" * 32, @@ -119,7 +119,7 @@ def test_grpc_connect(node_factory): with pytest.raises(Exception, match=r'Duplicate label'): # This request creates a label collision stub.Invoice(nodepb.InvoiceRequest( - msatoshi=AmountOrAny(amount=Amount(msat=12345)), + amount_msat=AmountOrAny(amount=Amount(msat=12345)), description="hello", label="lbl1", )) diff --git a/tests/test_closing.py b/tests/test_closing.py index 7762013925e6..8174356e8b82 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2735,10 +2735,10 @@ def setup_multihtlc_test(node_factory, bitcoind): nodes[-1].rpc.dev_ignore_htlcs(id=nodes[-2].info['id'], ignore=True) preimage = "0" * 64 - inv = nodes[0].rpc.invoice(msatoshi=10**8, label='x', description='desc', + inv = nodes[0].rpc.invoice(amount_msat=10**8, label='x', description='desc', preimage=preimage) h = inv['payment_hash'] - nodes[-1].rpc.invoice(msatoshi=10**8, label='x', description='desc', + nodes[-1].rpc.invoice(amount_msat=10**8, label='x', description='desc', preimage=preimage)['payment_hash'] # First, the failed attempts (paying wrong node). CLTV1 @@ -3641,8 +3641,8 @@ def test_onchain_close_upstream(node_factory, bitcoind): 'dev-no-ping-timer': None}, {'dev-no-ping-timer': None}]) - ph1 = l3.rpc.invoice(msatoshi="10000sat", label='x1', description='desc2')['payment_hash'] - ph2 = l3.rpc.invoice(msatoshi="10000sat", label='x2', description='desc2')['payment_hash'] + ph1 = l3.rpc.invoice(amount_msat="10000sat", label='x1', description='desc2')['payment_hash'] + ph2 = l3.rpc.invoice(amount_msat="10000sat", label='x2', description='desc2')['payment_hash'] route = l1.rpc.getroute(l3.info['id'], 1, 1)['route'] diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 8f4aa6b5c637..44f85c31588c 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -132,7 +132,7 @@ def test_invoice_preimage(node_factory): invoice_preimage = "17b08f669513b7379728fc1abcea5eaf3448bc1eba55a68ca2cd1843409cdc04" # Make invoice and pay it - inv = l2.rpc.invoice(msatoshi=123456, label="inv", description="?", preimage=invoice_preimage) + inv = l2.rpc.invoice(amount_msat=123456, label="inv", description="?", preimage=invoice_preimage) payment = l1.rpc.pay(inv['bolt11']) # Check preimage was given. @@ -153,7 +153,7 @@ def test_invoice_routeboost(node_factory, bitcoind): # Check routeboost. # Make invoice and pay it - inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?") + inv = l2.rpc.invoice(amount_msat=123456, label="inv1", description="?") # Check routeboost. assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv @@ -173,7 +173,7 @@ def test_invoice_routeboost(node_factory, bitcoind): wait_channel_quiescent(l1, l2) # Due to reserve & fees, l1 doesn't have capacity to pay this. - inv = l2.rpc.invoice(msatoshi=2 * (10**8) - 123456, label="inv2", description="?") + inv = l2.rpc.invoice(amount_msat=2 * (10**8) - 123456, label="inv2", description="?") # Check warning assert 'warning_capacity' in inv assert 'warning_private_unused' not in inv @@ -228,7 +228,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Since there's only one route, it will reluctantly hint that even # though it's private - inv = l2.rpc.invoice(msatoshi=123456, label="inv0", description="?") + inv = l2.rpc.invoice(amount_msat=123456, label="inv0", description="?") assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -243,7 +243,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['cltv_expiry_delta'] == 6 # If we explicitly say not to, it won't expose. - inv = l2.rpc.invoice(msatoshi=123456, label="inv1", description="?", exposeprivatechannels=False) + inv = l2.rpc.invoice(amount_msat=123456, label="inv1", description="?", exposeprivatechannels=False) assert 'warning_private_unused' in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -252,7 +252,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert 'routes' not in l1.rpc.decodepay(inv['bolt11']) # If we ask for it, we get it. - inv = l2.rpc.invoice(msatoshi=123456, label="inv1a", description="?", exposeprivatechannels=scid) + inv = l2.rpc.invoice(amount_msat=123456, label="inv1a", description="?", exposeprivatechannels=scid) assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -267,7 +267,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['cltv_expiry_delta'] == 6 # Similarly if we ask for an array. - inv = l2.rpc.invoice(msatoshi=123456, label="inv1b", description="?", exposeprivatechannels=[scid]) + inv = l2.rpc.invoice(amount_msat=123456, label="inv1b", description="?", exposeprivatechannels=[scid]) assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -290,7 +290,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Make sure channel is totally public. wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid2)['channels']] == [True, True]) - inv = l2.rpc.invoice(msatoshi=10**7, label="inv2", description="?") + inv = l2.rpc.invoice(amount_msat=10**7, label="inv2", description="?") print(inv) assert 'warning_deadends' in inv assert 'warning_private_unused' not in inv @@ -299,7 +299,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert 'warning_mpp' not in inv # Unless we tell it to include it. - inv = l2.rpc.invoice(msatoshi=10**7, label="inv3", description="?", exposeprivatechannels=True) + inv = l2.rpc.invoice(amount_msat=10**7, label="inv3", description="?", exposeprivatechannels=True) assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -313,7 +313,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 - inv = l2.rpc.invoice(msatoshi=10**7, label="inv4", description="?", exposeprivatechannels=scid) + inv = l2.rpc.invoice(amount_msat=10**7, label="inv4", description="?", exposeprivatechannels=scid) assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -328,7 +328,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert r['cltv_expiry_delta'] == 6 # Ask it explicitly to use a channel it can't (insufficient capacity) - inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv5", description="?", exposeprivatechannels=scid2) + inv = l2.rpc.invoice(amount_msat=(10**5) * 1000 + 1, label="inv5", description="?", exposeprivatechannels=scid2) assert 'warning_private_unused' not in inv assert 'warning_deadends' not in inv assert 'warning_capacity' in inv @@ -336,7 +336,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): assert 'warning_mpp' not in inv # Give it two options and it will pick one with suff capacity. - inv = l2.rpc.invoice(msatoshi=(10**5) * 1000 + 1, label="inv6", description="?", exposeprivatechannels=[scid2, scid]) + inv = l2.rpc.invoice(amount_msat=(10**5) * 1000 + 1, label="inv6", description="?", exposeprivatechannels=[scid2, scid]) assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -356,7 +356,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): bitcoind.generate_block(1) wait_for(lambda: l2.rpc.listchannels(scid_dummy)['channels'] == []) - inv = l2.rpc.invoice(msatoshi=123456, label="inv7", description="?", exposeprivatechannels=scid) + inv = l2.rpc.invoice(amount_msat=123456, label="inv7", description="?", exposeprivatechannels=scid) assert 'warning_private_unused' not in inv assert 'warning_capacity' not in inv assert 'warning_offline' not in inv @@ -374,7 +374,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): def test_invoice_expiry(node_factory, executor): l1, l2 = node_factory.line_graph(2, fundchannel=True) - inv = l2.rpc.invoice(msatoshi=123000, label='test_pay', description='description', expiry=1)['bolt11'] + inv = l2.rpc.invoice(amount_msat=123000, label='test_pay', description='description', expiry=1)['bolt11'] time.sleep(2) with pytest.raises(RpcError): @@ -438,7 +438,7 @@ def test_invoice_expiry(node_factory, executor): assert len(l2.rpc.listinvoices()['invoices']) == 0 start = int(time.time()) - inv = l2.rpc.invoice(msatoshi=123000, label='inv_s', description='description', expiry=1)['bolt11'] + inv = l2.rpc.invoice(amount_msat=123000, label='inv_s', description='description', expiry=1)['bolt11'] end = int(time.time()) expiry = only_one(l2.rpc.listinvoices('inv_s')['invoices'])['expires_at'] assert expiry >= start + 1 and expiry <= end + 1 @@ -562,8 +562,8 @@ def test_waitanyinvoice_reversed(node_factory, executor): def test_autocleaninvoice(node_factory): l1 = node_factory.get_node() - l1.rpc.invoice(msatoshi=12300, label='inv1', description='description1', expiry=4) - l1.rpc.invoice(msatoshi=12300, label='inv2', description='description2', expiry=12) + l1.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=4) + l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=12) l1.rpc.autocleaninvoice(cycle_seconds=8, expired_by=2) start_time = time.time() @@ -633,7 +633,7 @@ def test_amountless_invoice(node_factory): details = l1.rpc.decodepay(inv) assert('msatoshi' not in details) - l1.rpc.pay(inv, msatoshi=1337) + l1.rpc.pay(inv, amount_msat=1337) i = l2.rpc.listinvoices()['invoices'] assert(len(i) == 1) diff --git a/tests/test_misc.py b/tests/test_misc.py index 41b2b0b2ba95..2abbcee7cfe6 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2074,7 +2074,7 @@ def test_unicode_rpc(node_factory, executor, bitcoind): node = node_factory.get_node() desc = "Some candy 🍬 and a nice glass of milk 🥛." - node.rpc.invoice(msatoshi=42, label=desc, description=desc) + node.rpc.invoice(amount_msat=42, label=desc, description=desc) invoices = node.rpc.listinvoices()['invoices'] assert(len(invoices) == 1) assert(invoices[0]['description'] == desc) diff --git a/tests/test_pay.py b/tests/test_pay.py index 65ff87ea52b8..67704fe59940 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -102,7 +102,7 @@ def test_pay_limits(node_factory): # Fee too high. err = r'Fee exceeds our fee budget: [1-9]msat > 0msat, discarding route' with pytest.raises(RpcError, match=err) as err: - l1.rpc.call('pay', {'bolt11': inv['bolt11'], 'msatoshi': 100000, 'maxfeepercent': 0.0001, 'exemptfee': 0}) + l1.rpc.call('pay', {'bolt11': inv['bolt11'], 'amount_msat': 100000, 'maxfeepercent': 0.0001, 'exemptfee': 0}) assert err.value.error['code'] == PAY_STOPPED_RETRYING @@ -119,7 +119,7 @@ def test_pay_limits(node_factory): failmsg = r'CLTV delay exceeds our CLTV budget' # Delay too high. with pytest.raises(RpcError, match=failmsg) as err: - l1.rpc.call('pay', {'bolt11': inv['bolt11'], 'msatoshi': 100000, 'maxdelay': 0}) + l1.rpc.call('pay', {'bolt11': inv['bolt11'], 'amount_msat': 100000, 'maxdelay': 0}) assert err.value.error['code'] == PAY_STOPPED_RETRYING # Should also have retried two more times. @@ -131,10 +131,10 @@ def test_pay_limits(node_factory): # This fails! err = r'Fee exceeds our fee budget: 2msat > 1msat, discarding route' with pytest.raises(RpcError, match=err) as err: - l1.rpc.pay(bolt11=inv['bolt11'], msatoshi=100000, maxfee=1) + l1.rpc.pay(bolt11=inv['bolt11'], amount_msat=100000, maxfee=1) # This works, because fee is less than exemptfee. - l1.dev_pay(inv['bolt11'], msatoshi=100000, maxfeepercent=0.0001, + l1.dev_pay(inv['bolt11'], amount_msat=100000, maxfeepercent=0.0001, exemptfee=2000, use_shadow=False) status = l1.rpc.call('paystatus', {'bolt11': inv['bolt11']})['pay'][3]['attempts'] assert len(status) == 1 @@ -323,7 +323,7 @@ def test_pay_error_update_fees(node_factory): l1, l2, l3 = node_factory.line_graph(3, fundchannel=True, wait_for_announce=True) # Don't include any routehints in first invoice. - inv1 = l3.dev_invoice(msatoshi=123000, + inv1 = l3.dev_invoice(amount_msat=123000, label='test_pay_error_update_fees', description='description', dev_routes=[]) @@ -369,7 +369,7 @@ def test_pay_optional_args(node_factory): # root of a payment tree with the bolt11 invoice). anyinv = l2.rpc.invoice('any', 'any_pay', 'desc')['bolt11'] - l1.dev_pay(anyinv, label='desc', msatoshi=500, use_shadow=False) + l1.dev_pay(anyinv, label='desc', amount_msat=500, use_shadow=False) payment3 = l1.rpc.listsendpays(anyinv)['payments'] assert len(payment3) == 1 assert payment3[0]['label'] == 'desc' @@ -1628,7 +1628,7 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): # l3 will see a reconnect from l4 when l4 restarts. l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True, opts=[{}] * 2 + [{'dev-no-reconnect': None, 'may_reconnect': True}] * 2) - inv = l4.rpc.invoice(msatoshi=10**8, label='x', description='desc') + inv = l4.rpc.invoice(amount_msat=10**8, label='x', description='desc') h = inv['payment_hash'] l4.rpc.dev_ignore_htlcs(id=l3.info['id'], ignore=True) @@ -1850,7 +1850,7 @@ def test_pay_routeboost(node_factory, bitcoind): 'fee_base_msat': 1000, 'fee_proportional_millionths': 10, 'cltv_expiry_delta': 6}] - inv = l5.dev_invoice(msatoshi=10**5, + inv = l5.dev_invoice(amount_msat=10**5, label='test_pay_routeboost2', description='test_pay_routeboost2', dev_routes=[routel3l4l5]) @@ -1872,7 +1872,7 @@ def test_pay_routeboost(node_factory, bitcoind): 'fee_base_msat': 1000, 'fee_proportional_millionths': 10, 'cltv_expiry_delta': 6}] - inv = l5.dev_invoice(msatoshi=10**5, + inv = l5.dev_invoice(amount_msat=10**5, label='test_pay_routeboost5', description='test_pay_routeboost5', dev_routes=[routel3l4l5, routel3l5]) @@ -2171,7 +2171,7 @@ def test_setchannel_routing(node_factory, bitcoind): l1.rpc.getroute(l3.info['id'], 4001793, 1, fuzzpercent=0)["route"] # We should consider this unroutable! (MPP is disabled!) - inv = l3.dev_invoice(msatoshi=4001793, + inv = l3.dev_invoice(amount_msat=4001793, label='test_setchannel_1', description='desc', dev_routes=[]) @@ -2237,7 +2237,7 @@ def test_setchannel_routing(node_factory, bitcoind): l1.rpc.waitsendpay(inv['payment_hash']) # Check that this one warns about capacity! - inv = l3.rpc.call('invoice', {'msatoshi': 4001793, + inv = l3.rpc.call('invoice', {'amount_msat': 4001793, 'label': 'test_setchannel_4', 'description': 'desc'}) assert 'warning_capacity' in inv @@ -2730,7 +2730,7 @@ def test_tlv_or_legacy(node_factory, bitcoind): # We need to force l3 to provide route hint from l2 (it won't normally, # since it sees l2 as a dead end). - inv = l3.dev_invoice(msatoshi=10000, + inv = l3.dev_invoice(amount_msat=10000, label="test_tlv1", description="test_tlv1", dev_routes=[[{'id': l2.info['id'], @@ -3009,7 +3009,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r134, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1, @@ -3021,7 +3021,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=499, + amount_msat=499, payment_secret=paysecret, groupid=1, ) @@ -3031,7 +3031,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=999, + amount_msat=999, bolt11=inv['bolt11'], payment_secret=paysecret, partid=2, @@ -3042,7 +3042,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=2, @@ -3054,7 +3054,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=3, @@ -3066,7 +3066,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1, @@ -3076,7 +3076,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r134, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1, @@ -3086,7 +3086,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=2, @@ -3125,7 +3125,7 @@ def test_partial_payment(node_factory, bitcoind, executor): pay = l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=2, @@ -3138,7 +3138,7 @@ def test_partial_payment(node_factory, bitcoind, executor): l1.rpc.sendpay( route=r124, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=3, @@ -3155,7 +3155,7 @@ def test_partial_payment_timeout(node_factory, bitcoind): l1.rpc.sendpay( route=route, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1, @@ -3175,7 +3175,7 @@ def test_partial_payment_timeout(node_factory, bitcoind): l1.rpc.sendpay( route=route, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1, @@ -3184,7 +3184,7 @@ def test_partial_payment_timeout(node_factory, bitcoind): l1.rpc.sendpay( route=route, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=2, @@ -3210,7 +3210,7 @@ def test_partial_payment_restart(node_factory, bitcoind): l1.rpc.sendpay( route=route, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1, @@ -3228,7 +3228,7 @@ def test_partial_payment_restart(node_factory, bitcoind): l1.rpc.sendpay( route=route, payment_hash=inv['payment_hash'], - msatoshi=1000, + amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=2, @@ -3253,7 +3253,7 @@ def test_partial_payment_htlc_loss(node_factory, bitcoind): route = l1.rpc.getroute(l3.info['id'], 500, 1)['route'] - l1.rpc.sendpay(route=route, payment_hash=inv['payment_hash'], msatoshi=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1) + l1.rpc.sendpay(route=route, payment_hash=inv['payment_hash'], amount_msat=1000, bolt11=inv['bolt11'], payment_secret=paysecret, partid=1) wait_for(lambda: not only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['connected']) l2.rpc.dev_fail(l3.info['id']) @@ -3379,18 +3379,18 @@ def test_sendpay_msatoshi_arg(node_factory): with pytest.raises(RpcError, match=r'Do not specify msatoshi \(1001msat\) without' ' partid: if you do, it must be exactly' r' the final amount \(1000msat\)'): - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1001, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], amount_msat=1001, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match=r'Do not specify msatoshi \(999msat\) without' ' partid: if you do, it must be exactly' r' the final amount \(1000msat\)'): - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=999, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], amount_msat=999, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) # Can't send MPP payment which pays any more than amount. with pytest.raises(RpcError, match=r'Final amount 1001msat is greater than 1000msat, despite MPP'): - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1000, bolt11=inv['bolt11'], partid=1, payment_secret=inv['payment_secret']) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], amount_msat=1000, bolt11=inv['bolt11'], partid=1, payment_secret=inv['payment_secret']) # But this works - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1001, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], amount_msat=1001, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(inv['payment_hash']) inv = only_one(l2.rpc.listinvoices('inv')['invoices']) @@ -3602,13 +3602,13 @@ def test_keysend_routehint(node_factory): # Without any hints we should fail: with pytest.raises(RpcError): - l1.rpc.call("keysend", payload={'destination': dest, 'msatoshi': amt}) + l1.rpc.call("keysend", payload={'destination': dest, 'amount_msat': amt}) # We should also fail with only non-working hints: with pytest.raises(RpcError): - l1.rpc.call("keysend", payload={'destination': dest, 'msatoshi': amt, 'routehints': routehints[1:]}) + l1.rpc.call("keysend", payload={'destination': dest, 'amount_msat': amt, 'routehints': routehints[1:]}) - l1.rpc.call("keysend", payload={'destination': dest, 'msatoshi': amt, 'routehints': routehints}) + l1.rpc.call("keysend", payload={'destination': dest, 'amount_msat': amt, 'routehints': routehints}) invs = l3.rpc.listinvoices()['invoices'] assert(len(invs) == 1) @@ -4594,25 +4594,25 @@ def test_fetchinvoice(node_factory, bitcoind): assert len(l3.rpc.listinvoices(offer_id=offer1['offer_id'])['invoices']) == 2 # We can also set the amount explicitly, to tip. - inv1 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'msatoshi': 3}) + inv1 = l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'amount_msat': 3}) assert l1.rpc.call('decode', [inv1['invoice']])['amount_msat'] == 3 l1.rpc.pay(inv1['invoice']) # More than ~5x expected is rejected as absurd (it's actually a divide test, # which means we need 15 here, not 11). with pytest.raises(RpcError, match="Remote node sent failure message.*Amount vastly exceeds 2msat"): - l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'msatoshi': 15}) + l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'amount_msat': 15}) # Underpay is rejected. with pytest.raises(RpcError, match="Remote node sent failure message.*Amount must be at least 2msat"): - l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'msatoshi': 1}) + l1.rpc.call('fetchinvoice', {'offer': offer1['bolt12'], 'amount_msat': 1}) # If no amount is specified in offer, one must be in invoice. offer_noamount = l3.rpc.call('offer', {'amount': 'any', 'description': 'any amount test'}) with pytest.raises(RpcError, match="msatoshi parameter required"): l1.rpc.call('fetchinvoice', {'offer': offer_noamount['bolt12']}) - inv1 = l1.rpc.call('fetchinvoice', {'offer': offer_noamount['bolt12'], 'msatoshi': 100}) + inv1 = l1.rpc.call('fetchinvoice', {'offer': offer_noamount['bolt12'], 'amount_msat': 100}) # But amount won't appear in changes assert 'msat' not in inv1['changes'] @@ -5070,11 +5070,11 @@ def test_setchannel_enforcement_delay(node_factory, bitcoind): chanid1 = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] chanid2 = only_one(l2.rpc.getpeer(l3.info['id'])['channels'])['short_channel_id'] - route = [{'msatoshi': 1011, + route = [{'amount_msat': 1011, 'id': l2.info['id'], 'delay': 20, 'channel': chanid1}, - {'msatoshi': 1000, + {'amount_msat': 1000, 'id': l3.info['id'], 'delay': 10, 'channel': chanid2}] @@ -5097,7 +5097,7 @@ def test_setchannel_enforcement_delay(node_factory, bitcoind): l1.rpc.waitsendpay(inv['payment_hash']) # Test increased amount. - route[0]['msatoshi'] += 1 + route[0]['amount_msat'] += 1 inv = l3.rpc.invoice(1000, "test3", "test3") l1.rpc.sendpay(route, payment_hash=inv['payment_hash'], @@ -5159,20 +5159,20 @@ def test_sendpay_grouping(node_factory, bitcoind): ) wait_for(lambda: len(l1.rpc.listnodes()['nodes']) == 3) - inv = l3.rpc.invoice(msatoshi='any', label='lbl1', description='desc')['bolt11'] + inv = l3.rpc.invoice(amount_msat='any', label='lbl1', description='desc')['bolt11'] l3.stop() # Stop recipient so the first attempt fails assert(len(l1.db.query("SELECT * FROM payments")) == 0) assert(len(l1.rpc.listpays()['pays']) == 0) with pytest.raises(RpcError, match=r'Ran out of routes to try after [0-9]+ attempts'): - l1.rpc.pay(inv, msatoshi='100000msat') + l1.rpc.pay(inv, amount_msat='100000msat') # After this one invocation we have one entry in `listpays` assert(len(l1.rpc.listpays()['pays']) == 1) with pytest.raises(RpcError, match=r'Ran out of routes to try after [0-9]+ attempts'): - l1.rpc.pay(inv, msatoshi='200000msat') + l1.rpc.pay(inv, amount_msat='200000msat') # Surprise: we should have 2 entries after 2 invocations assert(len(l1.rpc.listpays()['pays']) == 2) @@ -5185,7 +5185,7 @@ def test_sendpay_grouping(node_factory, bitcoind): wait_for(lambda: only_one(l3.rpc.listpeers()['peers'])['connected'] is True) scid = l3.rpc.listpeers()['peers'][0]['channels'][0]['short_channel_id'] wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(scid)['channels']] == [True, True]) - l1.rpc.pay(inv, msatoshi='420000msat') + l1.rpc.pay(inv, amount_msat='420000msat') # And finally we should have all 3 attempts to pay the invoice pays = l1.rpc.listpays()['pays'] @@ -5221,7 +5221,7 @@ def test_legacyonion(node_factory, bitcoind): "id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59" }, "payment_hash": "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925", - "msatoshi": "10000msat", + "amount_msat": "10000msat", "shared_secrets": [ "bd29a24ea1fa5cab1677b22f4980f6aa115d470c2e3052cb08ca7d636441bfd5", "7d70d337a6b898373386d7d2cff05ca8f4d81123f63cf911aa064a6d8849f972" @@ -5244,7 +5244,7 @@ def test_pay_manual_exclude(node_factory, bitcoind): chan23 = l2.rpc.listpeers(l3_id)['peers'][0]['channels'][0] scid12 = chan12['short_channel_id'] + '/' + str(chan12['direction']) scid23 = chan23['short_channel_id'] + '/' + str(chan23['direction']) - inv = l3.rpc.invoice(msatoshi=123000, label='label1', description='desc')['bolt11'] + inv = l3.rpc.invoice(amount_msat=123000, label='label1', description='desc')['bolt11'] # Exclude the payer node id with pytest.raises(RpcError, match=r'Payer is manually excluded'): l1.rpc.pay(inv, exclude=[l1_id]) @@ -5277,7 +5277,7 @@ def test_pay_bolt11_metadata(node_factory, bitcoind): # After CI started failing, I *also* hacked it to set expiry to BIGNUM. inv = "lnbcrt1230n1p3yzgcxsp5q8g040f9rl9mu2unkjuj0vn262s6nyrhz5hythk3ueu2lfzahmzspp5ve584t0cv27hwmy0cx9ca8uwyqyfw9y9dm3r8vus9fv36r2l9yjsdq8v3jhxccmq6w35xjueqd9ejqmt9w3skgct5vyxqxra2q2qcqp99q2sqqqqqysgqfw6efxpzk5x5vfj8se46yg667x5cvhyttnmuqyk0q7rmhx3gs249qhtdggnek8c5adm2pztkjddlwyn2art2zg9xap2ckczzl3fzz4qqsej6mf" # Make l2 "know" about this invoice. - l2.rpc.invoice(msatoshi=123000, label='label1', description='desc', preimage='00' * 32) + l2.rpc.invoice(amount_msat=123000, label='label1', description='desc', preimage='00' * 32) with pytest.raises(RpcError, match=r'WIRE_INVALID_ONION_PAYLOAD'): l1.rpc.pay(inv) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ebbf30e19a7f..2e5ff6cc669e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -396,7 +396,7 @@ def test_pay_plugin(node_factory): l1.rpc.call('pay') # Make sure usage messages are present. - msg = 'pay bolt11 [msatoshi] [label] [riskfactor] [maxfeepercent] '\ + msg = 'pay bolt11 [amount_msat] [label] [riskfactor] [maxfeepercent] '\ '[retry_for] [maxdelay] [exemptfee] [localofferid] [exclude] '\ '[maxfee] [description]' if DEVELOPER: @@ -1076,7 +1076,7 @@ def test_htlc_accepted_hook_resolve(node_factory): {} ], wait_for_announce=True) - inv = l3.rpc.invoice(msatoshi=1000, label="lbl", description="desc", preimage="00" * 32)['bolt11'] + inv = l3.rpc.invoice(amount_msat=1000, label="lbl", description="desc", preimage="00" * 32)['bolt11'] l1.rpc.pay(inv) # And the invoice must still be unpaid @@ -1093,7 +1093,7 @@ def test_htlc_accepted_hook_direct_restart(node_factory, executor): 'plugin': os.path.join(os.getcwd(), 'tests/plugins/hold_htlcs.py')} ]) - i1 = l2.rpc.invoice(msatoshi=1000, label="direct", description="desc")['bolt11'] + i1 = l2.rpc.invoice(amount_msat=1000, label="direct", description="desc")['bolt11'] f1 = executor.submit(l1.rpc.pay, i1) l2.daemon.wait_for_log(r'Holding onto an incoming htlc for 10 seconds') @@ -1126,7 +1126,7 @@ def test_htlc_accepted_hook_forward_restart(node_factory, executor): {'may_reconnect': True}, ], wait_for_announce=True) - i1 = l3.rpc.invoice(msatoshi=1000, label="direct", description="desc")['bolt11'] + i1 = l3.rpc.invoice(amount_msat=1000, label="direct", description="desc")['bolt11'] f1 = executor.submit(l1.dev_pay, i1, use_shadow=False) l2.daemon.wait_for_log(r'Holding onto an incoming htlc for 10 seconds') @@ -2054,7 +2054,7 @@ def test_3847_repro(node_factory, bitcoind): amt = 20 * 1000 * 1000 i1 = l3.rpc.invoice( - msatoshi=amt, label="direct", description="desc" + amount_msat=amt, label="direct", description="desc" )['bolt11'] with pytest.raises(RpcError): l1.rpc.pay(i1, retry_for=10) From f6b4dbc65afaad14e6346ca2754622715ca431fe Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:01 +0930 Subject: [PATCH 0784/1530] lightningd: use amount_msat not amount in htlc_accepted_hook. Changelog-Added: Plugins: `htlc_accepted_hook` `amount_msat` field. Changelog-Deprecated: Plugins: `htlc_accepted_hook` `amount` field (use `amount_msat`) Signed-off-by: Rusty Russell --- doc/PLUGINS.md | 2 +- lightningd/peer_htlcs.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index fdf07e3fe2a7..88a1797bcf8b 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1379,7 +1379,7 @@ The payload of the hook call has the following format: "htlc": { "short_channel_id": "4x5x6", "id": 27, - "amount": "43msat", + "amount_msat": "43msat", "cltv_expiry": 500028, "cltv_expiry_relative": 10, "payment_hash": "0000000000000000000000000000000000000000000000000000000000000000" diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 04e94dc1fd66..9abfa345f7df 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1071,7 +1071,9 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_object_start(s, "htlc"); json_add_short_channel_id(s, "short_channel_id", hin->key.channel->scid); json_add_u64(s, "id", hin->key.id); - json_add_amount_msat_only(s, "amount", hin->msat); + if (deprecated_apis) + json_add_amount_msat_only(s, "amount", hin->msat); + json_add_amount_msat_only(s, "amount_msat", hin->msat); json_add_u32(s, "cltv_expiry", expiry); json_add_s32(s, "cltv_expiry_relative", expiry - blockheight); json_add_sha256(s, "payment_hash", &hin->payment_hash); From f1172254360214e3e04eaf9a701aaef4b535304c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0785/1530] pay: use amount_msat for amount report inside attempts. Nobody really parses this though, fortunately. Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `pay` `attempts` `amount_msat` field. Changelog-Deprecated: JSON-RPC: `pay` `attempts` `amount` field (use `amount_msat`). --- plugins/pay.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/pay.c b/plugins/pay.c index 142ae44a79e9..0ccde645b2fe 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -642,7 +642,9 @@ static void payment_add_attempt(struct json_stream *s, const char *fieldname, st json_add_string(s, "failreason", p->failreason); json_add_u64(s, "partid", p->partid); - json_add_amount_msat_only(s, "amount", p->amount); + if (deprecated_apis) + json_add_amount_msat_only(s, "amount", p->amount); + json_add_amount_msat_only(s, "amount_msat", p->amount); if (p->parent != NULL) json_add_u64(s, "parent_partid", p->parent->partid); From fccb11a6413eb4c2a0f3b097b23d22636cb9bc97 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0786/1530] fetchinvoice: amount_msat not msat. The name in the spec is `msat`, but I don't want to make an API exception. Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `fetchinvoice` `changes` `amount_msat` Changelog-Deprecated: JSON-RPC: `fetchinvoice` `changes` `msat` (use `amount_msat`) --- doc/lightning-fetchinvoice.7.md | 4 ++-- doc/schemas/fetchinvoice.schema.json | 2 +- plugins/fetchinvoice.c | 8 ++++++-- tests/test_pay.py | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index 799bde5f65ec..a0d5235e901a 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -56,7 +56,7 @@ On success, an object is returned, containing: - **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). + - **amount_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 @@ -88,4 +88,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e0033e40d86355e51abb48472f802a9a713ed5b2725828467515f9541207dac5) +[comment]: # ( SHA256STAMP:fa4e9733716ba263e8288613633ec7e0e466c13fc8067b11588bfd3c88901672) diff --git a/doc/schemas/fetchinvoice.schema.json b/doc/schemas/fetchinvoice.schema.json index 951dcd19d15f..8c6d2365562e 100644 --- a/doc/schemas/fetchinvoice.schema.json +++ b/doc/schemas/fetchinvoice.schema.json @@ -33,7 +33,7 @@ "type": "string", "description": "a completely replaced *vendor* field" }, - "msat": { + "amount_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)." } diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index cca83e0c2823..088affb61ecb 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -345,9 +345,13 @@ static struct command_result *handle_invreq_response(struct command *cmd, */ /* We always tell them this unless it's trivial to calc and * exactly as expected. */ - if (!expected_amount || *inv->amount != *expected_amount) - json_add_amount_msat_only(out, "msat", + if (!expected_amount || *inv->amount != *expected_amount) { + if (deprecated_apis) + json_add_amount_msat_only(out, "msat", + amount_msat(*inv->amount)); + json_add_amount_msat_only(out, "amount_msat", amount_msat(*inv->amount)); + } json_object_end(out); /* We tell them about next period at this point, if any. */ diff --git a/tests/test_pay.py b/tests/test_pay.py index 67704fe59940..cb1e068b0de8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4708,7 +4708,7 @@ def test_fetchinvoice(node_factory, bitcoind): 'description': 'USD test'})['bolt12'] inv = l1.rpc.call('fetchinvoice', {'offer': offerusd}) - assert inv['changes']['msat'] == Millisatoshi(int(10.05 * 5000)) + assert inv['changes']['amount_msat'] == Millisatoshi(int(10.05 * 5000)) # If we remove plugin, it can no longer give us an invoice. l3.rpc.plugin_stop(plugin) From 01411d70be0ffc9645caf85ba01aef51315ff8fa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0787/1530] common: enforce that msat fields are called "xxx_msat". Now we've fixed them all, make sure no new ones slip in! Signed-off-by: Rusty Russell --- common/json_helpers.c | 4 ++++ lightningd/options.c | 8 +++++++- plugins/libplugin-pay.c | 4 +++- plugins/test/run-route-overlong.c | 2 ++ 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/common/json_helpers.c b/common/json_helpers.c index baa7d3918104..3b1e210ce436 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -402,6 +402,8 @@ void json_add_amount_msat_compat(struct json_stream *result, const char *msatfieldname) { json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */ + if (!deprecated_apis) + assert(strends(msatfieldname, "_msat")); json_add_amount_msat_only(result, msatfieldname, msat); } @@ -409,6 +411,8 @@ void json_add_amount_msat_only(struct json_stream *result, const char *msatfieldname, struct amount_msat msat) { + if (!deprecated_apis) + assert(strends(msatfieldname, "_msat")); json_add_string(result, msatfieldname, type_to_string(tmpctx, struct amount_msat, &msat)); } diff --git a/lightningd/options.c b/lightningd/options.c index 6c22c4c5f662..43be6bcccd01 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1616,7 +1616,13 @@ static void add_config(struct lightningd *ld, /* FIXME: We actually treat it as if they specified * --plugin for each one, so ignore these */ } else if (opt->cb_arg == (void *)opt_set_msat) { - json_add_amount_msat_only(response, name0, ld->config.max_dust_htlc_exposure_msat); + /* We allow -msat not _msat here, unlike + * json_add_amount_msat_only */ + assert(strends(name0, "-msat")); + json_add_string(response, name0, + fmt_amount_msat(tmpctx, + *(struct amount_msat *) + opt->u.carg)); #if EXPERIMENTAL_FEATURES } else if (opt->cb_arg == (void *)opt_set_accept_extra_tlv_types) { /* TODO Actually print the option */ diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 24bb3fbb0611..bd5faa2c001b 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -1825,7 +1825,9 @@ static void payment_add_attempt(struct json_stream *s, const char *fieldname, st json_add_string(s, "failreason", p->failreason); json_add_u64(s, "partid", p->partid); - json_add_amount_msat_only(s, "amount", p->amount); + if (deprecated_apis) + json_add_amount_msat_only(s, "amount", p->amount); + json_add_amount_msat_only(s, "amount_msat", p->amount); if (p->parent != NULL) json_add_u64(s, "parent_partid", p->parent->partid); diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 42271efcd5fe..dd59da1cd0d6 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -14,6 +14,8 @@ struct command_result *command_finished(struct command *cmd UNNEEDED, struct jso /* Generated stub for command_still_pending */ struct command_result *command_still_pending(struct command *cmd UNNEEDED) { fprintf(stderr, "command_still_pending called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for feature_offered */ bool feature_offered(const u8 *features UNNEEDED, size_t f UNNEEDED) { fprintf(stderr, "feature_offered called!\n"); abort(); } From 5c208c1b06e4ae2f20f306186ab9b1f8cd1fa456 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0788/1530] pyln-client: convert every _msat field to Millisatoshi This prepares for when they start being u64, not strings with msat appended. This has a strange side effect on our schema: despite the name, decodepay's `fee_base_msat` is actually a u64, which we now convert to msat on decode. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 7 +++---- contrib/pyln-testing/pyln/testing/fixtures.py | 2 +- doc/lightning-decodepay.7.md | 4 ++-- doc/schemas/decodepay.schema.json | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 539e9e49cd56..51e70ae7ef24 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -453,11 +453,10 @@ def replace_amounts(obj): if isinstance(obj, dict): for k, v in obj.items(): if k.endswith('msat'): - if isinstance(v, str) and v.endswith('msat'): - obj[k] = Millisatoshi(v) - # Special case for array of msat values - elif isinstance(v, list) and all(isinstance(e, str) and e.endswith('msat') for e in v): + if isinstance(v, list): obj[k] = [Millisatoshi(e) for e in v] + else: + obj[k] = Millisatoshi(v) else: obj[k] = LightningRpc.LightningJSONDecoder.replace_amounts(v) elif isinstance(obj, list): diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index 4cbb63ab0b63..d576a3abac1f 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -356,7 +356,7 @@ def is_msat_request(checker, instance): return False def is_msat_response(checker, instance): - """String number ending in msat""" + """String number ending in msat (deprecated) or integer""" return type(instance) is Millisatoshi def is_txid(checker, instance): diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index 300d80451737..6990389855c5 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -38,7 +38,7 @@ On success, an object is returned, containing: - 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_base_msat** (msat): 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: @@ -70,4 +70,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:17cb6c66c75e907f3a2583d702aec2fc6e5a7b6026d05a3ed9957304799c9aef) +[comment]: # ( SHA256STAMP:a606b0d0594d48c619a8d34c7c0d0996982db10f1423841f269e89004a19fa98) diff --git a/doc/schemas/decodepay.schema.json b/doc/schemas/decodepay.schema.json index 0823f7afa454..09fa334da8d7 100644 --- a/doc/schemas/decodepay.schema.json +++ b/doc/schemas/decodepay.schema.json @@ -132,7 +132,7 @@ "description": "a channel to the next peer" }, "fee_base_msat": { - "type": "u32", + "type": "msat", "description": "the base fee for payments" }, "fee_proportional_millionths": { From 6afc0affef2e78b8ebf2ea5057e49dcf15d8ef92 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0789/1530] pytest: don't use deprecated amount fields Signed-off-by: Rusty Russell --- tests/test_connection.py | 34 +++++++------- tests/test_invoices.py | 2 - tests/test_misc.py | 2 +- tests/test_pay.py | 98 ++++++++++++++++++---------------------- 4 files changed, 61 insertions(+), 75 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 5a643993f0d9..19c0195ff342 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -257,10 +257,10 @@ def test_balance(node_factory): l1, l2 = node_factory.line_graph(2, fundchannel=True) p1 = only_one(l1.rpc.getpeer(peer_id=l2.info['id'], level='info')['channels']) p2 = only_one(l2.rpc.getpeer(l1.info['id'], 'info')['channels']) - assert p1['msatoshi_to_us'] == 10**6 * 1000 - assert p1['msatoshi_total'] == 10**6 * 1000 - assert p2['msatoshi_to_us'] == 0 - assert p2['msatoshi_total'] == 10**6 * 1000 + assert p1['to_us_msat'] == 10**6 * 1000 + assert p1['total_msat'] == 10**6 * 1000 + assert p2['to_us_msat'] == 0 + assert p2['total_msat'] == 10**6 * 1000 @pytest.mark.openchannel('v1') @@ -1024,7 +1024,7 @@ def test_funding_all_too_much(node_factory): pending = only_one([o for o in outputs if o['status'] != 'confirmed']) assert pending['status'] == 'unconfirmed' assert pending['reserved'] is False - assert only_one(l1.rpc.listfunds()['channels'])['channel_total_sat'] == 2**24 - 1 + assert only_one(l1.rpc.listfunds()['channels'])['amount_msat'] == Millisatoshi(str(2**24 - 1) + "sat") @pytest.mark.openchannel('v1') @@ -1135,7 +1135,7 @@ def test_funding_push(node_factory, bitcoind, chainparams): # Send funds. amount = 2**24 - push_sat = 20000 + push_msat = 20000 * 1000 bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], amount / 10**8 + 0.01) bitcoind.generate_block(1) @@ -1144,16 +1144,16 @@ def test_funding_push(node_factory, bitcoind, chainparams): # Fail to open (try to push too much) with pytest.raises(RpcError, match=r'Requested to push_msat of 20000000msat is greater than available funding amount 10000sat'): - l1.rpc.fundchannel(l2.info['id'], 10000, push_msat=push_sat * 1000) + l1.rpc.fundchannel(l2.info['id'], 10000, push_msat=push_msat) # This should work. amount = amount - 1 - l1.rpc.fundchannel(l2.info['id'], amount, push_msat=push_sat * 1000) + l1.rpc.fundchannel(l2.info['id'], amount, push_msat=push_msat) bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) funds = only_one(l1.rpc.listfunds()['channels']) - assert funds['channel_sat'] + push_sat == funds['channel_total_sat'] + assert funds['our_amount_msat'] + push_msat == funds['amount_msat'] chanid = first_channel_id(l2, l1) channel_mvts_1 = [ @@ -1167,7 +1167,7 @@ def test_funding_push(node_factory, bitcoind, chainparams): check_coin_moves(l1, chanid, channel_mvts_1, chainparams) check_coin_moves(l2, chanid, channel_mvts_2, chainparams) - assert account_balance(l1, chanid) == (amount - push_sat) * 1000 + assert account_balance(l1, chanid) == amount * 1000 - push_msat @pytest.mark.openchannel('v1') @@ -1596,7 +1596,7 @@ def _fundchannel(l1, l2, amount, close_to): for node in [l1, l2]: channel = node.rpc.listpeers()['peers'][0]['channels'][-1] - assert amount * 1000 == channel['msatoshi_total'] + assert amount * 1000 == channel['total_msat'] def _close(src, dst, addr=None): """Close the channel from src to dst, with the specified address. @@ -1694,7 +1694,7 @@ def test_funding_external_wallet(node_factory, bitcoind): for node in [l1, l2]: node.daemon.wait_for_log(r'State changed from CHANNELD_AWAITING_LOCKIN to CHANNELD_NORMAL') channel = node.rpc.listpeers()['peers'][0]['channels'][0] - assert amount * 1000 == channel['msatoshi_total'] + assert amount * 1000 == channel['total_msat'] # Test that we don't crash if peer disconnects after fundchannel_start l2.connect(l3) @@ -2235,7 +2235,7 @@ def test_channel_persistence(node_factory, bitcoind, executor): wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 1) # Wait for the restored HTLC to finish - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['msatoshi_to_us'] == 99990000) + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 99990000) wait_for(lambda: len([p for p in l1.rpc.listpeers()['peers'] if p['connected']])) wait_for(lambda: len([p for p in l2.rpc.listpeers()['peers'] if p['connected']])) @@ -2243,14 +2243,14 @@ def test_channel_persistence(node_factory, bitcoind, executor): # Now make sure this is really functional by sending a payment l1.pay(l2, 10000) - # L1 doesn't actually update msatoshi_to_us until it receives + # L1 doesn't actually update to_us_msat until it receives # revoke_and_ack from L2, which can take a little bit. - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['msatoshi_to_us'] == 99980000) - assert only_one(l2.rpc.listpeers()['peers'][0]['channels'])['msatoshi_to_us'] == 20000 + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 99980000) + assert only_one(l2.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 20000 # Finally restart l1, and make sure it remembers l1.restart() - assert only_one(l1.rpc.listpeers()['peers'][0]['channels'])['msatoshi_to_us'] == 99980000 + assert only_one(l1.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 99980000 # Now make sure l1 is watching for unilateral closes l2.rpc.dev_fail(l1.info['id']) diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 44f85c31588c..dfc3f594909c 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -627,7 +627,6 @@ def test_amountless_invoice(node_factory): inv = l2.rpc.invoice('any', 'lbl', 'desc')['bolt11'] i = l2.rpc.listinvoices()['invoices'] assert(len(i) == 1) - assert('msatoshi_received' not in i[0]) assert('amount_received_msat' not in i[0]) assert(i[0]['status'] == 'unpaid') details = l1.rpc.decodepay(inv) @@ -637,7 +636,6 @@ def test_amountless_invoice(node_factory): i = l2.rpc.listinvoices()['invoices'] assert(len(i) == 1) - assert(i[0]['msatoshi_received'] == 1337) assert(i[0]['amount_received_msat'] == Millisatoshi(1337)) assert(i[0]['status'] == 'paid') diff --git a/tests/test_misc.py b/tests/test_misc.py index 2abbcee7cfe6..793c6fb5feca 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2325,7 +2325,7 @@ def test_listforwards(node_factory, bitcoind): # status=settled settled_forwards = l2.rpc.listforwards(status='settled')['forwards'] assert len(settled_forwards) == 2 - assert sum(x['out_msatoshi'] for x in settled_forwards) == 3000 + assert sum(x['out_msat'] for x in settled_forwards) == 3000 # status=local_failed failed_forwards = l2.rpc.listforwards(status='local_failed')['forwards'] diff --git a/tests/test_pay.py b/tests/test_pay.py index cb1e068b0de8..d1037f293023 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -33,7 +33,7 @@ def test_pay(node_factory): after = time.time() preimage = details['payment_preimage'] assert details['status'] == 'complete' - assert details['msatoshi'] == 123000 + assert details['amount_msat'] == Millisatoshi(123000) assert details['destination'] == l2.info['id'] assert details['created_at'] >= before assert details['created_at'] <= after @@ -357,7 +357,7 @@ def test_pay_optional_args(node_factory): inv1 = l2.rpc.invoice(123000, 'test_pay', 'desc')['bolt11'] l1.dev_pay(inv1, label='desc', use_shadow=False) payment1 = l1.rpc.listsendpays(inv1)['payments'] - assert len(payment1) and payment1[0]['msatoshi_sent'] == 123000 + assert len(payment1) and payment1[0]['amount_sent_msat'] == 123000 assert payment1[0]['label'] == 'desc' inv2 = l2.rpc.invoice(321000, 'test_pay2', 'description')['bolt11'] @@ -614,10 +614,10 @@ def invoice_unpaid(dst, label): # FIXME: test paying via another node, should fail to pay twice. p1 = l1.rpc.getpeer(l2.info['id'], 'info') p2 = l2.rpc.getpeer(l1.info['id'], 'info') - assert only_one(p1['channels'])['msatoshi_to_us'] == 10**6 * 1000 - assert only_one(p1['channels'])['msatoshi_total'] == 10**6 * 1000 - assert only_one(p2['channels'])['msatoshi_to_us'] == 0 - assert only_one(p2['channels'])['msatoshi_total'] == 10**6 * 1000 + assert only_one(p1['channels'])['to_us_msat'] == 10**6 * 1000 + assert only_one(p1['channels'])['total_msat'] == 10**6 * 1000 + assert only_one(p2['channels'])['to_us_msat'] == 0 + assert only_one(p2['channels'])['total_msat'] == 10**6 * 1000 # This works. before = int(time.time()) @@ -627,13 +627,13 @@ def invoice_unpaid(dst, label): # Check details assert details['payment_hash'] == rhash assert details['destination'] == l2.info['id'] - assert details['msatoshi'] == amt + assert details['amount_msat'] == amt assert details['created_at'] >= before assert details['created_at'] <= after # Check receiver assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'paid' assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['pay_index'] == 1 - assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['msatoshi_received'] == rs['msatoshi'] + assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['amount_received_msat'] == rs['msatoshi'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['payment_preimage'] == preimage # Balances should reflect it. @@ -641,10 +641,10 @@ def check_balances(): p1 = l1.rpc.getpeer(l2.info['id'], 'info') p2 = l2.rpc.getpeer(l1.info['id'], 'info') return ( - only_one(p1['channels'])['msatoshi_to_us'] == 10**6 * 1000 - amt - and only_one(p1['channels'])['msatoshi_total'] == 10**6 * 1000 - and only_one(p2['channels'])['msatoshi_to_us'] == amt - and only_one(p2['channels'])['msatoshi_total'] == 10**6 * 1000 + only_one(p1['channels'])['to_us_msat'] == 10**6 * 1000 - amt + and only_one(p1['channels'])['total_msat'] == 10**6 * 1000 + and only_one(p2['channels'])['to_us_msat'] == amt + and only_one(p2['channels'])['total_msat'] == 10**6 * 1000 ) wait_for(check_balances) @@ -656,7 +656,7 @@ def check_balances(): assert preimage == preimage2 l1.daemon.wait_for_log('Payment ./.: .* COMPLETE') assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'paid' - assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['msatoshi_received'] == rs['msatoshi'] + assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['amount_received_msat'] == rs['msatoshi'] # Overpaying by "only" a factor of 2 succeeds. inv = l2.rpc.invoice(amt, 'testpayment3', 'desc') @@ -666,7 +666,7 @@ def check_balances(): l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) preimage3 = l1.rpc.waitsendpay(rhash)['payment_preimage'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'paid' - assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['msatoshi_received'] == amt * 2 + assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['amount_received_msat'] == amt * 2 # Test listsendpays payments = l1.rpc.listsendpays()['payments'] @@ -797,7 +797,6 @@ def test_decodepay(node_factory): 'fj9srp' ) assert b11['currency'] == 'bc' - assert b11['msatoshi'] == 2500 * 10**11 // 1000000 assert b11['amount_msat'] == Millisatoshi(2500 * 10**11 // 1000000) assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -831,7 +830,6 @@ def test_decodepay(node_factory): 'herry pie, one sausage, one cupcake, and one slice of watermelon' ) assert b11['currency'] == 'bc' - assert b11['msatoshi'] == 20 * 10**11 // 1000 assert b11['amount_msat'] == Millisatoshi(str(20 * 10**11 // 1000) + 'msat') assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -864,7 +862,6 @@ def test_decodepay(node_factory): 'herry pie, one sausage, one cupcake, and one slice of watermelon' ) assert b11['currency'] == 'tb' - assert b11['msatoshi'] == 20 * 10**11 // 1000 assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000) assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -896,7 +893,6 @@ def test_decodepay(node_factory): # * `dhhwkj`: Bech32 checksum b11 = l1.rpc.decodepay('lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon') assert b11['currency'] == 'bc' - assert b11['msatoshi'] == 20 * 10**11 // 1000 assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000) assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -939,7 +935,6 @@ def test_decodepay(node_factory): # * `mf9swh`: Bech32 checksum b11 = l1.rpc.decodepay('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z9kmrgvr7xlaqm47apw3d48zm203kzcq357a4ls9al2ea73r8jcceyjtya6fu5wzzpe50zrge6ulk4nvjcpxlekvmxl6qcs9j3tz0469gq5g658y', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon') assert b11['currency'] == 'bc' - assert b11['msatoshi'] == 20 * 10**11 // 1000 assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000) assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -966,7 +961,6 @@ def test_decodepay(node_factory): # * `qdtpa4`: Bech32 checksum b11 = l1.rpc.decodepay('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7kepvrhrm9s57hejg0p662ur5j5cr03890fa7k2pypgttmh4897d3raaq85a293e9jpuqwl0rnfuwzam7yr8e690nd2ypcq9hlkdwdvycqa0qza8', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon') assert b11['currency'] == 'bc' - assert b11['msatoshi'] == 20 * 10**11 // 1000 assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000) assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -993,7 +987,6 @@ def test_decodepay(node_factory): # * `akvd7y`: Bech32 checksum b11 = l1.rpc.decodepay('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q28j0v3rwgy9pvjnd48ee2pl8xrpxysd5g44td63g6xcjcu003j3qe8878hluqlvl3km8rm92f5stamd3jw763n3hck0ct7p8wwj463cql26ava', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon') assert b11['currency'] == 'bc' - assert b11['msatoshi'] == 20 * 10**11 // 1000 assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000) assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -1023,7 +1016,6 @@ def test_decodepay(node_factory): # * `73t7cl`: Bech32 checksum b11 = l1.rpc.decodepay('lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9qzsze992adudgku8p05pstl6zh7av6rx2f297pv89gu5q93a0hf3g7lynl3xq56t23dpvah6u7y9qey9lccrdml3gaqwc6nxsl5ktzm464sq73t7cl') assert b11['currency'] == 'bc' - assert b11['msatoshi'] == 25 * 10**11 // 1000 assert b11['amount_msat'] == Millisatoshi(25 * 10**11 // 1000) assert b11['created_at'] == 1496314658 assert b11['payment_hash'] == '0001020304050607080900010203040506070809000102030405060708090102' @@ -1063,7 +1055,7 @@ def test_decodepay(node_factory): b11 = "lnbcrt71p0g4u8upp5xn4k45tsp05akmn65s5k2063d5fyadhjse9770xz5sk7u4x6vcmqdqqcqzynxqrrssx94cf4p727jamncsvcd8m99n88k423ruzq4dxwevfatpp5gx2mksj2swshjlx4pe3j5w9yed5xjktrktzd3nc2a04kq8yu84l7twhwgpxjn3pw" b11 = l1.rpc.decodepay(b11) sat_per_btc = 10**8 - assert(b11['msatoshi'] == 7 * sat_per_btc * 1000) + assert(b11['amount_msat'] == 7 * sat_per_btc * 1000) with pytest.raises(RpcError): l1.rpc.decodepay('1111111') @@ -1206,7 +1198,7 @@ def test_forward_different_fees_and_cltv(node_factory, bitcoind): # * `amt_to_forward` = 4999999 # * `outgoing_cltv_value` = current-block-height + 9 + 42 # - assert route[0]['msatoshi'] == 4999999 + assert route[0]['amount_msat'] == 4999999 assert route[0]['delay'] == 9 + shadow_route # BOLT #7: @@ -1229,9 +1221,9 @@ def test_forward_different_fees_and_cltv(node_factory, bitcoind): route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"] assert len(route) == 2 - assert route[0]['msatoshi'] == 5010198 + assert route[0]['amount_msat'] == 5010198 assert route[0]['delay'] == 20 + 9 + shadow_route - assert route[1]['msatoshi'] == 4999999 + assert route[1]['amount_msat'] == 4999999 assert route[1]['delay'] == 9 + shadow_route inv = l3.rpc.invoice(4999999, 'test_forward_different_fees_and_cltv', 'desc') @@ -1294,9 +1286,9 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"] assert len(route) == 2 - assert route[0]['msatoshi'] == 5010198 + assert route[0]['amount_msat'] == 5010198 assert route[0]['delay'] == 20 + 9 - assert route[1]['msatoshi'] == 4999999 + assert route[1]['amount_msat'] == 4999999 assert route[1]['delay'] == 9 # Modify so we overpay, overdo the cltv. @@ -1373,22 +1365,22 @@ def test_forward_stats(node_factory, bitcoind): # Check that we correctly account channel changes assert inchan['in_payments_offered'] == 3 assert inchan['in_payments_fulfilled'] == 1 - assert inchan['in_msatoshi_offered'] >= 3 * amount - assert inchan['in_msatoshi_fulfilled'] >= amount + assert inchan['in_offered_msat'] >= Millisatoshi(3 * amount) + assert inchan['in_fulfilled_msat'] >= Millisatoshi(amount) assert outchan['out_payments_offered'] == 1 assert outchan['out_payments_fulfilled'] == 1 - assert outchan['out_msatoshi_offered'] >= amount - assert outchan['out_msatoshi_offered'] == outchan['out_msatoshi_fulfilled'] + assert outchan['out_offered_msat'] >= Millisatoshi(amount) + assert outchan['out_offered_msat'] == outchan['out_fulfilled_msat'] - assert outchan['out_msatoshi_fulfilled'] < inchan['in_msatoshi_fulfilled'] + assert outchan['out_fulfilled_msat'] < inchan['in_fulfilled_msat'] stats = l2.rpc.listforwards() assert [f['status'] for f in stats['forwards']] == ['settled', 'failed', 'offered'] - assert l2.rpc.getinfo()['msatoshi_fees_collected'] == 1 + amount // 100000 - assert l1.rpc.getinfo()['msatoshi_fees_collected'] == 0 - assert l3.rpc.getinfo()['msatoshi_fees_collected'] == 0 + assert l2.rpc.getinfo()['fees_collected_msat'] == 1 + amount // 100000 + assert l1.rpc.getinfo()['fees_collected_msat'] == 0 + assert l3.rpc.getinfo()['fees_collected_msat'] == 0 assert stats['forwards'][0]['received_time'] <= stats['forwards'][0]['resolved_time'] assert stats['forwards'][1]['received_time'] <= stats['forwards'][1]['resolved_time'] assert 'received_time' in stats['forwards'][2] and 'resolved_time' not in stats['forwards'][2] @@ -1611,7 +1603,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): stats = l2.rpc.listforwards() assert [f['status'] for f in stats['forwards']] == ['local_failed', 'local_failed', 'local_failed', 'local_failed', 'local_failed'] - assert l2.rpc.getinfo()['msatoshi_fees_collected'] == 0 + assert l2.rpc.getinfo()['fees_collected_msat'] == 0 assert 'received_time' in stats['forwards'][0] and 'resolved_time' not in stats['forwards'][0] assert 'received_time' in stats['forwards'][1] and 'resolved_time' not in stats['forwards'][1] @@ -1708,7 +1700,7 @@ def exhaust_channel(opener, peer, scid, already_spent=0): """ peer_node = opener.rpc.listpeers(peer.info['id'])['peers'][0] chan = peer_node['channels'][0] - maxpay = chan['spendable_msatoshi'] + maxpay = chan['spendable_msat'] lbl = ''.join(random.choice(string.ascii_letters) for _ in range(20)) inv = peer.rpc.invoice(maxpay, lbl, "exhaust_channel") routestep = { @@ -2102,7 +2094,7 @@ def test_setchannel_state(node_factory, bitcoind): inv = l2.rpc.invoice(100000, 'test_setchannel_state', 'desc')['bolt11'] result = l0.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' - assert result['msatoshi_sent'] == 100042 + assert result['amount_sent_msat'] == 100042 # Disconnect and unilaterally close from l2 to l1 l2.rpc.disconnect(l1.info['id'], force=True) @@ -2215,8 +2207,8 @@ def test_setchannel_routing(node_factory, bitcoind): # Now try below minimum route_ok = l1.rpc.getroute(l3.info['id'], 17, 1)["route"] assert len(route_ok) == 2 - assert route_ok[0]['msatoshi'] == 1337 + 17 - assert route_ok[1]['msatoshi'] == 17 + assert route_ok[0]['amount_msat'] == 1337 + 17 + assert route_ok[1]['amount_msat'] == 17 route_bad = copy.deepcopy(route_ok) route_bad[0]['msatoshi'] = 1337 + 16 @@ -2271,8 +2263,8 @@ def test_setchannel_zero(node_factory, bitcoind): # test if zero fees are applied route = l1.rpc.getroute(l3.info['id'], 4999999, 1)["route"] assert len(route) == 2 - assert route[0]['msatoshi'] == 4999999 - assert route[1]['msatoshi'] == 4999999 + assert route[0]['amount_msat'] == 4999999 + assert route[1]['amount_msat'] == 4999999 # Wait for l3 to know about our low-balling, otherwise they'll add a wrong # routehint to the invoice. @@ -2282,7 +2274,7 @@ def test_setchannel_zero(node_factory, bitcoind): inv = l3.rpc.invoice(4999999, 'test_setchannel_3', 'desc')['bolt11'] result = l1.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' - assert result['msatoshi_sent'] == 4999999 + assert result['amount_sent_msat'] == 4999999 # FIXME: hack something up to advertize min_htlc > 0, then test mintoolow. with pytest.raises(RpcError, match="htlcmax cannot be less than htlcmin"): @@ -2338,7 +2330,7 @@ def test_setchannel_restart(node_factory, bitcoind): inv = l3.rpc.invoice(499999, 'test_setchannel_1', 'desc')['bolt11'] result = l1.dev_pay(inv, use_shadow=False) assert result['status'] == 'complete' - assert result['msatoshi_sent'] == 501404 + assert result['amount_sent_msat'] == 501404 @pytest.mark.developer("updates are delayed without --dev-fast-gossip") @@ -3523,7 +3515,7 @@ def test_keysend(node_factory): inv = invs[0] print(inv) - assert(inv['msatoshi_received'] >= amt) + assert(inv['amount_received_msat'] >= Millisatoshi(amt)) # Now send a direct one instead from l1 to l2 l1.rpc.keysend(l2.info['id'], amt) @@ -3531,7 +3523,7 @@ def test_keysend(node_factory): assert(len(invs) == 1) inv = invs[0] - assert(inv['msatoshi_received'] >= amt) + assert(inv['amount_received_msat'] >= Millisatoshi(amt)) # And finally try to send a keysend payment to l4, which doesn't # support it. It MUST fail. @@ -3563,7 +3555,7 @@ def test_keysend_extra_tlvs(node_factory): assert(l2.daemon.is_in_log(r'plugin-sphinx-receiver.py.*extratlvs.*133773310.*feedc0de')) inv = invs[0] - assert(inv['msatoshi_received'] >= amt) + assert(inv['amount_received_msat'] >= Millisatoshi(amt)) # Now try again with the TLV type in extra_tlvs as string: l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: 'FEEDC0DE'}) @@ -3613,7 +3605,7 @@ def test_keysend_routehint(node_factory): assert(len(invs) == 1) inv = invs[0] - assert(inv['msatoshi_received'] >= amt) + assert(inv['amount_received_msat'] >= Millisatoshi(amt)) def test_invalid_onion_channel_update(node_factory): @@ -3751,7 +3743,7 @@ def test_mpp_presplit(node_factory): assert(p['parts'] >= 5) inv = l3.rpc.listinvoices()['invoices'][0] - assert(inv['msatoshi'] == inv['msatoshi_received']) + assert(inv['amount_msat'] == inv['amount_received_msat']) # Make sure that bolt11 isn't duplicated for every part bolt11s = 0 @@ -4244,7 +4236,7 @@ def test_large_mpp_presplit(node_factory): assert(p['parts'] <= PRESPLIT_MAX_SPLITS) inv = l3.rpc.listinvoices()['invoices'][0] - assert(inv['msatoshi'] == inv['msatoshi_received']) + assert(inv['amount_msat'] == inv['amount_received_msat']) @pytest.mark.developer("builds large network, which is slow if not DEVELOPER") @@ -4883,10 +4875,8 @@ def do_test_sendinvoice(node_factory, bitcoind, disable): assert out['status'] == 'paid' assert 'payment_preimage' in out assert 'expires_at' in out - assert out['msatoshi'] == 100000000 assert out['amount_msat'] == Millisatoshi(100000000) assert 'pay_index' in out - assert out['msatoshi_received'] == 100000000 assert out['amount_received_msat'] == Millisatoshi(100000000) # Note, if we're slow, this fails with "Offer no longer available", @@ -4932,10 +4922,8 @@ def do_test_sendinvoice(node_factory, bitcoind, disable): assert out['status'] == 'paid' assert 'payment_preimage' in out assert 'expires_at' in out - assert out['msatoshi'] == 10000000 assert out['amount_msat'] == Millisatoshi(10000000) assert 'pay_index' in out - assert out['msatoshi_received'] == 10000000 assert out['amount_received_msat'] == Millisatoshi(10000000) From c3efba16ff2cb440bd53bb84693fd282bf2decf5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0790/1530] JSON: don't print deprecated amount fields any more A small change in one routine creates a lot of changes! We actually recommended moving away from these in v0.7.0 (2019-02-28), but never deprecated them formally. Changelog-Deprecated: JSON-RPC: `pay`, `decode`, `decodepay`, `getroute`, `listinvoices`, `listpays` and `listsendpays` `msatoshi` fields (use `amount_msat`). Changelog-Deprecated: JSON-RPC: `getinfo` `msatoshi_fees_collected` field (use `fees_collected_msat`). Changelog-Deprecated: JSON-RPC: `listpeers` `channels`: `msatoshi_to_us`, `msatoshi_to_us_min`, `msatoshi_to_us_max`, `msatoshi_total`, `dust_limit_satoshis`, `our_channel_reserve_satoshis`, `their_channel_reserve_satoshis`, `spendable_msatoshi`, `receivable_msatoshi`, `in_msatoshi_offered`, `in_msatoshi_fulfilled`, `out_msatoshi_offered`, `out_msatoshi_fulfilled`, `max_htlc_value_in_flight_msat` and `htlc_minimum_msat` (use `to_us_msat`, `min_to_us_msat`, `max_to_us_msat`, `total_msat`, `dust_limit_msat`, `our_reserve_msat`, `their_reserve_msat`, `spendable_msat`, `receivable_msat`, `in_offered_msat`, `in_fulfilled_msat`, `out_offered_msat`, `out_fulfilled_msat`, `max_total_htlc_in_msat` and `minimum_htlc_in_msat`). Changelog-Deprecated: JSON-RPC: `listinvoices` and `pay` `msatoshi_received` and `msatoshi_sent` (use `amount_received_msat`, `amount_sent_msat`) Changelog-Deprecated: JSON-RPC: `listpays` and `listsendpays` `msatoshi_sent` (use `amount_sent_msat`) Changelog-Deprecated: JSON-RPC: `listforwards` `in_msatoshi`, `out_msatoshi` and `fee` (use `in_msat`, `out_msat` and `fee_msat`) Changelog-Deprecated: JSON-RPC: `listfunds` `outputs` `value` (use `amount_msat`) Signed-off-by: Rusty Russell --- common/json_helpers.c | 11 +++++------ doc/lightning-listfunds.7.md | 2 +- doc/schemas/listfunds.schema.json | 4 ---- tests/test_gossip.py | 2 -- tests/test_misc.py | 1 - tests/test_pay.py | 14 ++++---------- tests/test_wallet.py | 2 +- 7 files changed, 11 insertions(+), 25 deletions(-) diff --git a/common/json_helpers.c b/common/json_helpers.c index 3b1e210ce436..aaf60143546d 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -401,9 +401,8 @@ void json_add_amount_msat_compat(struct json_stream *result, const char *rawfieldname, const char *msatfieldname) { - json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */ - if (!deprecated_apis) - assert(strends(msatfieldname, "_msat")); + if (deprecated_apis) + json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */ json_add_amount_msat_only(result, msatfieldname, msat); } @@ -422,7 +421,8 @@ void json_add_amount_sat_compat(struct json_stream *result, const char *rawfieldname, const char *msatfieldname) { - json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */ + if (deprecated_apis) + json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */ json_add_amount_sat_msat(result, msatfieldname, sat); } @@ -433,8 +433,7 @@ void json_add_amount_sat_msat(struct json_stream *result, struct amount_msat msat; assert(strends(msatfieldname, "_msat")); if (amount_sat_to_msat(&msat, sat)) - json_add_string(result, msatfieldname, - type_to_string(tmpctx, struct amount_msat, &msat)); + json_add_amount_msat_only(result, msatfieldname, msat); } /* When I noticed that we were adding "XXXmsat" fields *not* ending in _msat */ diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index d2dc247fd24b..1ce69088b37b 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -68,4 +68,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e2b4f817ca6032ab4421fccaba226c03e0995c8dbfbfeb2f7c8572987ffe7dc4) +[comment]: # ( SHA256STAMP:fdc550a0ff11f6fbbf51c29340c0494077d831566ae7ab008cce4b93034a76c5) diff --git a/doc/schemas/listfunds.schema.json b/doc/schemas/listfunds.schema.json index bd1f73382acf..5ebd497af772 100644 --- a/doc/schemas/listfunds.schema.json +++ b/doc/schemas/listfunds.schema.json @@ -33,10 +33,6 @@ "type": "msat", "description": "the amount of the output" }, - "value": { - "type": "u64", - "deprecated": true - }, "scriptpubkey": { "type": "hex", "description": "the scriptPubkey of the output" diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 0b1cd7619bc2..750bdd63bf4f 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -2019,7 +2019,6 @@ def test_gossip_store_upgrade_v7_v8(node_factory): 'destination': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518', 'short_channel_id': '103x1x1', 'public': False, - 'satoshis': 1000000, 'amount_msat': Millisatoshi(1000000000), 'message_flags': 1, 'channel_flags': 0, @@ -2036,7 +2035,6 @@ def test_gossip_store_upgrade_v7_v8(node_factory): 'destination': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', 'short_channel_id': '103x1x1', 'public': False, - 'satoshis': 1000000, 'amount_msat': Millisatoshi(1000000000), 'message_flags': 1, 'channel_flags': 1, diff --git a/tests/test_misc.py b/tests/test_misc.py index 793c6fb5feca..e2ac16fd7148 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -969,7 +969,6 @@ def test_cli(node_factory): ' "invoices": [', ' {', r' "label": "l\"[]{}",', - ' "msatoshi": 123000,', ' "amount_msat": "123000msat",', ' "status": "unpaid",', r' "description": "d\"[]{}",', diff --git a/tests/test_pay.py b/tests/test_pay.py index d1037f293023..159a63db6da0 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1292,11 +1292,9 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): assert route[1]['delay'] == 9 # Modify so we overpay, overdo the cltv. - route[0]['msatoshi'] += 2000 - route[0]['amount_msat'] = Millisatoshi(route[0]['msatoshi']) + route[0]['amount_msat'] += 2000 route[0]['delay'] += 20 - route[1]['msatoshi'] += 1000 - route[1]['amount_msat'] = Millisatoshi(route[1]['msatoshi']) + route[1]['amount_msat'] += 1000 route[1]['delay'] += 10 # This should work. @@ -2174,13 +2172,11 @@ def test_setchannel_routing(node_factory, bitcoind): # 1337 + 4000000 * 137 / 1000000 = 1885 route_ok = l1.rpc.getroute(l3.info['id'], 4000000, 1)["route"] assert len(route_ok) == 2 - assert route_ok[0]['msatoshi'] == 4001885 - assert route_ok[1]['msatoshi'] == 4000000 + assert route_ok[0]['amount_msat'] == 4001885 + assert route_ok[1]['amount_msat'] == 4000000 # Make variant that tries to pay more than allowed htlc! route_bad = copy.deepcopy(route_ok) - route_bad[0]['msatoshi'] = 4001887 - route_bad[1]['msatoshi'] = 4000001 route_bad[0]['amount_msat'] = Millisatoshi(4001887) route_bad[1]['amount_msat'] = Millisatoshi(4000001) assert route_bad != route_ok @@ -2211,8 +2207,6 @@ def test_setchannel_routing(node_factory, bitcoind): assert route_ok[1]['amount_msat'] == 17 route_bad = copy.deepcopy(route_ok) - route_bad[0]['msatoshi'] = 1337 + 16 - route_bad[1]['msatoshi'] = 16 route_bad[0]['amount_msat'] = Millisatoshi(1337 + 16) route_bad[1]['amount_msat'] = Millisatoshi(16) assert route_bad != route_ok diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 2c6ed00724c2..d6e1d30c3e3f 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -945,7 +945,7 @@ def test_transaction_annotations(node_factory, bitcoind): assert(len(outputs) == 1 and outputs[0]['status'] == 'confirmed') out = outputs[0] idx = out['output'] - assert(idx in [0, 1] and out['value'] == 10**6) + assert(idx in [0, 1] and out['amount_msat'] == Millisatoshi("{}sat".format(10**6))) # ... and it should have an annotation on the output reading 'deposit' txs = l1.rpc.listtransactions()['transactions'] From 08d5776ebc3646ba924466be95416076a7852300 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0791/1530] lightningd: deprecate `msatoshi` in `sendpay` `route`. We should be using amount_msat always. Many tests were not. Plus, deprecating it simplifies the code. Signed-off-by: Rusty Russell Changelog-Deprecated: JSONRPC: `sendpay` `route` elements `msatoshi` (use `amount_msat`) --- cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 4 +-- contrib/pyln-testing/pyln/testing/utils.py | 2 +- doc/schemas/sendpay.request.json | 1 + lightningd/pay.c | 36 ++++------------------ tests/test_closing.py | 10 +++--- tests/test_connection.py | 28 ++++++++--------- tests/test_pay.py | 34 ++++++++++---------- tests/test_plugin.py | 4 +-- 10 files changed, 50 insertions(+), 73 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 1b5182a46ee8..3b96e9d52afa 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -322,7 +322,7 @@ message SendpayResponse { } message SendpayRoute { - optional Amount amount_msat = 5; + Amount amount_msat = 5; bytes id = 2; uint32 delay = 3; string channel = 4; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 87a850457e34..2f837ed747bf 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -980,7 +980,7 @@ impl From<&pb::ListfundsRequest> for requests::ListfundsRequest { impl From<&pb::SendpayRoute> for requests::SendpayRoute { fn from(c: &pb::SendpayRoute) -> Self { Self { - amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? + amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey delay: c.delay as u16, // Rule #1 for type u16 channel: cln_rpc::primitives::ShortChannelId::from_str(&c.channel).unwrap(), // Rule #1 for type short_channel_id diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 52369a3326c1..75e5308ec256 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -138,8 +138,8 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendpayRoute { - #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] - pub amount_msat: Option, + #[serde(alias = "amount_msat")] + pub amount_msat: Amount, #[serde(alias = "id")] pub id: Pubkey, #[serde(alias = "delay")] diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index dc903376ebd2..e5d95404a775 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1026,7 +1026,7 @@ def pay(self, dst, amt, label=None): assert len(invoices) == 1 and invoices[0]['status'] == 'unpaid' routestep = { - 'msatoshi': amt, + 'amount_msat': amt, 'id': dst_id, 'delay': 5, 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments diff --git a/doc/schemas/sendpay.request.json b/doc/schemas/sendpay.request.json index c811c9fe1314..6550b509b4eb 100644 --- a/doc/schemas/sendpay.request.json +++ b/doc/schemas/sendpay.request.json @@ -12,6 +12,7 @@ "items": { "type": "object", "required": [ + "amount_msat", "id", "delay", "channel" diff --git a/lightningd/pay.c b/lightningd/pay.c index ab7ec6adf266..d01990f279c5 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1366,7 +1366,7 @@ static struct command_result *param_route_hops(struct command *cmd, *hops = tal_arr(cmd, struct route_hop, tok->size); json_for_each_arr(i, t, tok) { - struct amount_msat *msat, *amount_msat; + struct amount_msat *amount_msat; struct node_id *id; struct short_channel_id *channel; unsigned *delay, *direction; @@ -1375,13 +1375,10 @@ static struct command_result *param_route_hops(struct command *cmd, int *ignored; if (!param(cmd, buffer, t, - /* Only *one* of these is required */ - p_opt("msatoshi", param_msat, &msat), - p_opt("amount_msat", param_msat, &amount_msat), - /* These three actually required */ - p_opt("id", param_node_id, &id), - p_opt("delay", param_number, &delay), - p_opt("channel", param_short_channel_id, &channel), + p_req("amount_msat|msatoshi", param_msat, &amount_msat), + p_req("id", param_node_id, &id), + p_req("delay", param_number, &delay), + p_req("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, &ignored), @@ -1390,28 +1387,7 @@ static struct command_result *param_route_hops(struct command *cmd, NULL)) return command_param_failed(); - if (!msat && !amount_msat) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zi]: must have msatoshi" - " or amount_msat", name, i); - if (!id || !channel || !delay) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zi]: must have id, channel" - " and delay", name, i); - if (msat && amount_msat && !amount_msat_eq(*msat, *amount_msat)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zi]: msatoshi %s != amount_msat %s", - name, i, - type_to_string(tmpctx, - struct amount_msat, - msat), - type_to_string(tmpctx, - struct amount_msat, - amount_msat)); - if (!msat) - msat = amount_msat; - - (*hops)[i].amount = *msat; + (*hops)[i].amount = *amount_msat; (*hops)[i].node_id = *id; (*hops)[i].delay = *delay; (*hops)[i].scid = *channel; diff --git a/tests/test_closing.py b/tests/test_closing.py index 8174356e8b82..896ff0ca0763 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1856,7 +1856,7 @@ def test_onchaind_replay(node_factory, bitcoind): inv = l2.rpc.invoice(10**8, 'onchaind_replay', 'desc') rhash = inv['payment_hash'] routestep = { - 'msatoshi': 10**8 - 1, + 'amount_msat': 10**8 - 1, 'id': l2.info['id'], 'delay': 101, 'channel': '1x1x1' @@ -1916,7 +1916,7 @@ def test_onchain_dust_out(node_factory, bitcoind, executor): inv = l2.rpc.invoice(1, 'onchain_dust_out', 'desc') rhash = inv['payment_hash'] routestep = { - 'msatoshi': 1, + 'amount_msat': 1, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' @@ -1988,7 +1988,7 @@ def test_onchain_timeout(node_factory, bitcoind, executor): rhash = inv['payment_hash'] # We underpay, so it fails. routestep = { - 'msatoshi': 10**8 - 1, + 'amount_msat': 10**8 - 1, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' @@ -2460,7 +2460,7 @@ def test_onchain_feechange(node_factory, bitcoind, executor): rhash = inv['payment_hash'] # We underpay, so it fails. routestep = { - 'msatoshi': 10**8 - 1, + 'amount_msat': 10**8 - 1, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' @@ -2545,7 +2545,7 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): rhash = inv['payment_hash'] # We underpay, so it fails. routestep = { - 'msatoshi': 10**7 - 1, + 'amount_msat': 10**7 - 1, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' diff --git a/tests/test_connection.py b/tests/test_connection.py index 19c0195ff342..cde97a9ca37b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -743,7 +743,7 @@ def test_reconnect_sender_add1(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('test_reconnect_sender_add1')['invoices'])['status'] == 'unpaid' - route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] for i in range(0, len(disconnects)): with pytest.raises(RpcError): @@ -782,7 +782,7 @@ def test_reconnect_sender_add(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment')['invoices'])['status'] == 'unpaid' - route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] # This will send commit, so will reconnect as required. l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) @@ -816,7 +816,7 @@ def test_reconnect_receiver_add(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' - route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') @@ -846,7 +846,7 @@ def test_reconnect_receiver_fulfill(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' - route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') @@ -3189,9 +3189,9 @@ def test_feerate_stress(node_factory, executor): scid12 = l1.get_channel_scid(l2) scid23 = l2.get_channel_scid(l3) - routel1l3 = [{'msatoshi': '10002msat', 'id': l2.info['id'], 'delay': 11, 'channel': scid12}, - {'msatoshi': '10000msat', 'id': l3.info['id'], 'delay': 5, 'channel': scid23}] - routel2l1 = [{'msatoshi': '10000msat', 'id': l1.info['id'], 'delay': 5, 'channel': scid12}] + routel1l3 = [{'amount_msat': '10002msat', 'id': l2.info['id'], 'delay': 11, 'channel': scid12}, + {'amount_msat': '10000msat', 'id': l3.info['id'], 'delay': 5, 'channel': scid23}] + routel2l1 = [{'amount_msat': '10000msat', 'id': l1.info['id'], 'delay': 5, 'channel': scid12}] rate = 1875 NUM_ATTEMPTS = 25 @@ -3245,7 +3245,7 @@ def test_pay_disconnect_stress(node_factory, executor): '-WIRE_COMMITMENT_SIGNED']}]) scid12 = l1.get_channel_scid(l2) - routel2l1 = [{'msatoshi': '10000msat', 'id': l1.info['id'], 'delay': 5, 'channel': scid12}] + routel2l1 = [{'amount_msat': '10000msat', 'id': l1.info['id'], 'delay': 5, 'channel': scid12}] # Get invoice from l1 to pay. inv = l1.rpc.invoice(10000, "invoice", "invoice") @@ -3414,7 +3414,7 @@ def test_htlc_retransmit_order(node_factory, executor): invoices = [l2.rpc.invoice(1000, str(x), str(x)) for x in range(NUM_HTLCS)] routestep = { - 'msatoshi': 1000, + 'amount_msat': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments @@ -3534,7 +3534,7 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): # Make sure another commitment happens, sending failed payment. routestep = { - 'msatoshi': 1, + 'amount_msat': 1, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments @@ -3658,7 +3658,7 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): 'hold-result': 'fail'}]) # This HTLC will fail - l1.rpc.sendpay([{'msatoshi': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}], '00' * 32, payment_secret='00' * 32) + l1.rpc.sendpay([{'amount_msat': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}], '00' * 32, payment_secret='00' * 32) # Each one should cause one disconnection, no upgrade. for d in l1_disconnects + l2_disconnects: @@ -3731,7 +3731,7 @@ def test_htlc_failed_noclose(node_factory): inv = l2.rpc.invoice(1000, "test", "test") routestep = { - 'msatoshi': FUNDAMOUNT * 1000, + 'amount_msat': FUNDAMOUNT * 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments @@ -3886,12 +3886,12 @@ def test_multichan(node_factory, executor, bitcoind): scid23b = scids[0] # Test paying by each, - route = [{'msatoshi': 100001001, + route = [{'amount_msat': 100001001, 'id': l2.info['id'], 'delay': 11, # Unneeded 'channel': scid12}, - {'msatoshi': 100000000, + {'amount_msat': 100000000, 'id': l3.info['id'], 'delay': 5, 'channel': scid23a}] diff --git a/tests/test_pay.py b/tests/test_pay.py index 159a63db6da0..0e701527d0a8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -226,7 +226,7 @@ def test_pay0(node_factory): rhash = inv['payment_hash'] routestep = { - 'msatoshi': 0, + 'amount_msat': 0, 'id': l2.info['id'], 'delay': 10, 'channel': chanid @@ -560,7 +560,7 @@ def invoice_unpaid(dst, label): return len(invoices) == 1 and invoices[0]['status'] == 'unpaid' routestep = { - 'msatoshi': amt, + 'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1' @@ -569,7 +569,7 @@ def invoice_unpaid(dst, label): # Insufficient funds. with pytest.raises(RpcError): rs = copy.deepcopy(routestep) - rs['msatoshi'] = rs['msatoshi'] - 1 + rs['amount_msat'] = rs['amount_msat'] - 1 l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -577,7 +577,7 @@ def invoice_unpaid(dst, label): # Gross overpayment (more than factor of 2) with pytest.raises(RpcError): rs = copy.deepcopy(routestep) - rs['msatoshi'] = rs['msatoshi'] * 2 + 1 + rs['amount_msat'] = rs['amount_msat'] * 2 + 1 l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -633,7 +633,7 @@ def invoice_unpaid(dst, label): # Check receiver assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'paid' assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['pay_index'] == 1 - assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['amount_received_msat'] == rs['msatoshi'] + assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['amount_received_msat'] == rs['amount_msat'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['payment_preimage'] == preimage # Balances should reflect it. @@ -656,13 +656,13 @@ def check_balances(): assert preimage == preimage2 l1.daemon.wait_for_log('Payment ./.: .* COMPLETE') assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'paid' - assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['amount_received_msat'] == rs['msatoshi'] + assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['amount_received_msat'] == rs['amount_msat'] # Overpaying by "only" a factor of 2 succeeds. inv = l2.rpc.invoice(amt, 'testpayment3', 'desc') rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'unpaid' - routestep = {'msatoshi': amt * 2, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'} + routestep = {'amount_msat': amt * 2, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'} l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) preimage3 = l1.rpc.waitsendpay(rhash)['payment_preimage'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'paid' @@ -1082,11 +1082,11 @@ def test_forward(node_factory, bitcoind): amt = 100000000 fee = amt * 10 // 1000000 + 1 - baseroute = [{'msatoshi': amt + fee, + baseroute = [{'amount_msat': amt + fee, 'id': l2.info['id'], 'delay': 12, 'channel': chanid1}, - {'msatoshi': amt, + {'amount_msat': amt, 'id': l3.info['id'], 'delay': 6, 'channel': chanid2}] @@ -1487,11 +1487,11 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): payment_hash = inv['payment_hash'] fee = amount * 10 // 1000000 + 1 - route = [{'msatoshi': amount, + route = [{'amount_msat': amount, 'id': l2.info['id'], 'delay': 12, 'channel': c12}, - {'msatoshi': amount, + {'amount_msat': amount, 'id': l4.info['id'], 'delay': 6, 'channel': c24}] @@ -1513,11 +1513,11 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): payment_hash = inv['payment_hash'] fee = amount * 10 // 1000000 + 1 - route = [{'msatoshi': amount + fee, + route = [{'amount_msat': amount + fee, 'id': l2.info['id'], 'delay': 12, 'channel': c12}, - {'msatoshi': amount, + {'amount_msat': amount, 'id': l5.info['id'], 'delay': 6, 'channel': c25}] @@ -1563,11 +1563,11 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): fee = amount * 10 // 1000000 + 1 # We underpay, so it fails. - route = [{'msatoshi': amount + fee - 1, + route = [{'amount_msat': amount + fee - 1, 'id': l2.info['id'], 'delay': 12, 'channel': c12}, - {'msatoshi': amount - 1, + {'amount_msat': amount - 1, 'id': l4.info['id'], 'delay': 5, 'channel': c24}] @@ -1702,7 +1702,7 @@ def exhaust_channel(opener, peer, scid, already_spent=0): lbl = ''.join(random.choice(string.ascii_letters) for _ in range(20)) inv = peer.rpc.invoice(maxpay, lbl, "exhaust_channel") routestep = { - 'msatoshi': maxpay, + 'amount_msat': maxpay, 'id': peer.info['id'], 'delay': 10, 'channel': scid @@ -2686,7 +2686,7 @@ def test_error_returns_blockheight(node_factory, bitcoind): """Test that incorrect_or_unknown_payment_details returns block height""" l1, l2 = node_factory.line_graph(2) - l1.rpc.sendpay([{'msatoshi': 100, + l1.rpc.sendpay([{'amount_msat': 100, 'id': l2.info['id'], 'delay': 10, 'channel': l1.get_channel_scid(l2)}], diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 2e5ff6cc669e..920d8809eef5 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1290,11 +1290,11 @@ def test_forward_event_notification(node_factory, bitcoind, executor): fee = amount * 10 // 1000000 + 1 c12 = l1.get_channel_scid(l2) c25 = l2.get_channel_scid(l5) - route = [{'msatoshi': amount + fee - 1, + route = [{'amount_msat': amount + fee - 1, 'id': l2.info['id'], 'delay': 12, 'channel': c12}, - {'msatoshi': amount - 1, + {'amount_msat': amount - 1, 'id': l5.info['id'], 'delay': 5, 'channel': c25}] From 98c264de66150336c14a610102470f751ec43b67 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0792/1530] fundchannel, txprepare, multiwithdraw: don't assume "excess_msat" will have "msat" appended. After next patch, it's a raw u64, and this code broke. Signed-off-by: Rusty Russell --- plugins/spender/multifundchannel.c | 8 +++++--- plugins/spender/multiwithdraw.c | 4 +++- plugins/txprepare.c | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 41b55a17b9c9..ac0cd7ae9a81 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1316,6 +1316,7 @@ after_fundpsbt(struct command *cmd, struct multifundchannel_command *mfc) { const jsmntok_t *field; + struct amount_msat msat; plugin_log(mfc->cmd->plugin, LOG_DBG, "mfc %"PRIu64": %s done.", @@ -1341,9 +1342,10 @@ after_fundpsbt(struct command *cmd, /* msat LOL. */ field = json_get_member(buf, result, "excess_msat"); - if (!field || !parse_amount_sat(&mfc->excess_sat, - buf + field->start, - field->end - field->start)) + if (!field || !parse_amount_msat(&msat, + buf + field->start, + field->end - field->start) + || !amount_msat_to_sat(&mfc->excess_sat, msat)) goto fail; if (has_all(mfc)) diff --git a/plugins/spender/multiwithdraw.c b/plugins/spender/multiwithdraw.c index 8790f88e0e05..b06d097562af 100644 --- a/plugins/spender/multiwithdraw.c +++ b/plugins/spender/multiwithdraw.c @@ -410,6 +410,7 @@ mw_after_fundpsbt(struct command *cmd, u32 feerate_per_kw; u32 estimated_final_weight; struct amount_sat excess_sat; + struct amount_msat excess_msat; bool ok = true; /* Extract results. */ @@ -430,7 +431,8 @@ mw_after_fundpsbt(struct command *cmd, field = ok ? json_get_member(buf, result, "excess_msat") : NULL; ok = ok && field; - ok = ok && json_to_sat(buf, field, &excess_sat); + ok = ok && json_to_msat(buf, field, &excess_msat); + ok = ok && amount_msat_to_sat(&excess_sat, excess_msat); if (!ok) plugin_err(mw->cmd->plugin, diff --git a/plugins/txprepare.c b/plugins/txprepare.c index 90d3783bc2c4..85ff84b1abba 100644 --- a/plugins/txprepare.c +++ b/plugins/txprepare.c @@ -282,6 +282,7 @@ static struct command_result *psbt_created(struct command *cmd, { const jsmntok_t *psbttok; struct out_req *req; + struct amount_msat excess_msat; struct amount_sat excess; u32 weight; @@ -300,8 +301,9 @@ static struct command_result *psbt_created(struct command *cmd, result->end - result->start, buf + result->start); - if (!json_to_sat(buf, json_get_member(buf, result, "excess_msat"), - &excess)) + if (!json_to_msat(buf, json_get_member(buf, result, "excess_msat"), + &excess_msat) + || !amount_msat_to_sat(&excess, excess_msat)) return command_fail(cmd, LIGHTNINGD, "Unparsable excess_msat: '%.*s'", result->end - result->start, From 993f44f289d5ceca98dd34bcf57be19cba2e5c3e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:09 +0930 Subject: [PATCH 0793/1530] libplugin: don't be so strict on msat fields. Let the parsing handle if they're the wrong type (soon they'll be u64). Signed-off-by: Rusty Russell --- plugins/libplugin-pay.c | 2 +- plugins/libplugin.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index bd5faa2c001b..35fd1e79fb86 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -916,7 +916,7 @@ static struct payment_result *tal_sendpay_result_from_json(const tal_t *ctx, /* Initial sanity checks, all these fields must exist. */ if (idtok == NULL || idtok->type != JSMN_PRIMITIVE || hashtok == NULL || hashtok->type != JSMN_STRING || - senttok == NULL || senttok->type != JSMN_STRING || + senttok == NULL || statustok == NULL || statustok->type != JSMN_STRING) { return NULL; } diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 2e728f159c67..86eeb60d8b08 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1591,8 +1591,8 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, ftxidtok == NULL || ftxidtok->type != JSMN_STRING || (scidtok != NULL && scidtok->type != JSMN_STRING) || (dirtok != NULL && dirtok->type != JSMN_PRIMITIVE) || - tmsattok == NULL || tmsattok->type != JSMN_STRING || - smsattok == NULL || smsattok->type != JSMN_STRING) + tmsattok == NULL || + smsattok == NULL) return NULL; chan = tal(ctx, struct listpeers_channel); From 6139319b60b93f86f6962f06fa85313b468efb6a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:10 +0930 Subject: [PATCH 0794/1530] funder: prepare for msats fields as raw numbers. We would otherwise multiply them by 1000. Signed-off-by: Rusty Russell --- plugins/funder.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/plugins/funder.c b/plugins/funder.c index 7967bec3cd2c..57e8b899e6b0 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -358,6 +358,16 @@ psbt_fund_failed(struct command *cmd, return command_hook_success(cmd); } +/* They give msats, we want sats */ +static bool json_to_msat_as_sats(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat) +{ + struct amount_msat msat; + if (!json_to_msat(buffer, tok, &msat)) + return false; + return amount_msat_to_sat(sat, msat); +} + static struct command_result * listfunds_success(struct command *cmd, const char *buf, @@ -388,7 +398,7 @@ listfunds_success(struct command *cmd, "{amount_msat:%" ",status:%" ",reserved:%}", - JSON_SCAN(json_to_sat, &val), + JSON_SCAN(json_to_msat_as_sats, &val), JSON_SCAN_TAL(cmd, json_strdup, &status), JSON_SCAN(json_to_bool, &is_reserved)); if (err) @@ -524,7 +534,7 @@ json_openchannel2_call(struct command *cmd, ",locktime:%}}", JSON_SCAN(json_to_node_id, &info->id), JSON_SCAN(json_to_channel_id, &info->cid), - JSON_SCAN(json_to_sat, &info->their_funding), + JSON_SCAN(json_to_msat_as_sats, &info->their_funding), JSON_SCAN(json_to_msat, &max_htlc_inflight), JSON_SCAN(json_to_msat, &htlc_minimum), JSON_SCAN(json_to_u64, &info->funding_feerate_perkw), @@ -547,7 +557,7 @@ json_openchannel2_call(struct command *cmd, "requested_lease_msat:%" ",lease_blockheight_start:%" ",node_blockheight:%}}", - JSON_SCAN(json_to_sat, &info->requested_lease), + JSON_SCAN(json_to_msat_as_sats, &info->requested_lease), JSON_SCAN(json_to_u32, &info->node_blockheight), JSON_SCAN(json_to_u32, &info->lease_blockheight)); @@ -561,7 +571,7 @@ json_openchannel2_call(struct command *cmd, /* If there's no channel_max, it's actually infinity */ err = json_scan(tmpctx, buf, params, "{openchannel2:{channel_max_msat:%}}", - JSON_SCAN(json_to_sat, &info->channel_max)); + JSON_SCAN(json_to_msat_as_sats, &info->channel_max)); if (err) info->channel_max = AMOUNT_SAT(UINT64_MAX); @@ -651,14 +661,14 @@ json_rbf_channel_call(struct command *cmd, "{rbf_channel:" "{id:%" ",channel_id:%" - ",their_funding:%" + ",their_funding_msat:%" ",funding_feerate_per_kw:%" ",feerate_our_max:%" ",feerate_our_min:%" ",locktime:%}}", JSON_SCAN(json_to_node_id, &info->id), JSON_SCAN(json_to_channel_id, &info->cid), - JSON_SCAN(json_to_sat, &info->their_funding), + JSON_SCAN(json_to_msat_as_sats, &info->their_funding), JSON_SCAN(json_to_u64, &info->funding_feerate_perkw), JSON_SCAN(json_to_u64, &feerate_our_max), JSON_SCAN(json_to_u64, &feerate_our_min), @@ -673,7 +683,7 @@ json_rbf_channel_call(struct command *cmd, /* If there's no channel_max, it's actually infinity */ err = json_scan(tmpctx, buf, params, "{rbf_channel:{channel_max_msat:%}}", - JSON_SCAN(json_to_sat, &info->channel_max)); + JSON_SCAN(json_to_msat_as_sats, &info->channel_max)); if (err) info->channel_max = AMOUNT_SAT(UINT64_MAX); @@ -1050,7 +1060,7 @@ static void tell_lightningd_lease_rates(struct plugin *p, rpc_scan(p, "setleaserates", take(jout), /* Unused */ "{lease_fee_base_msat:%}", - JSON_SCAN(json_to_sat, &val)); + JSON_SCAN(json_to_msat_as_sats, &val)); } From 6461815dd929cacacaaee1baa74bd9866478cdf7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:10 +0930 Subject: [PATCH 0795/1530] plugins/funder: fix parameter parsing. This code was buggy: handing "1000" as a parameter to min_their_funding_msat, don't turn that into "1000sat"! Signed-off-by: Rusty Russell --- plugins/funder.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/plugins/funder.c b/plugins/funder.c index 57e8b899e6b0..868834291d29 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -368,6 +368,23 @@ static bool json_to_msat_as_sats(const char *buffer, const jsmntok_t *tok, return amount_msat_to_sat(sat, msat); } +static struct command_result *param_msat_as_sat(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct amount_sat **sat) +{ + struct amount_msat msat; + + *sat = tal(cmd, struct amount_sat); + if (parse_amount_msat(&msat, buffer + tok->start, tok->end - tok->start) + && amount_msat_to_sat(*sat, msat)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be a millisatoshi amount"); +} + static struct command_result * listfunds_success(struct command *cmd, const char *buf, @@ -936,19 +953,19 @@ json_funderupdate(struct command *cmd, current_policy->mod), p_opt_def("leases_only", param_bool, &leases_only, current_policy->leases_only), - p_opt_def("min_their_funding_msat", param_sat, + p_opt_def("min_their_funding_msat", param_msat_as_sat, &min_their_funding, current_policy->min_their_funding), - p_opt_def("max_their_funding_msat", param_sat, + p_opt_def("max_their_funding_msat", param_msat_as_sat, &max_their_funding, current_policy->max_their_funding), - p_opt_def("per_channel_min_msat", param_sat, + p_opt_def("per_channel_min_msat", param_msat_as_sat, &per_channel_min, current_policy->per_channel_min), - p_opt_def("per_channel_max_msat", param_sat, + p_opt_def("per_channel_max_msat", param_msat_as_sat, &per_channel_max, current_policy->per_channel_max), - p_opt_def("reserve_tank_msat", param_sat, &reserve_tank, + p_opt_def("reserve_tank_msat", param_msat_as_sat, &reserve_tank, current_policy->reserve_tank), p_opt_def("fuzz_percent", param_number, &fuzz_factor, @@ -956,7 +973,7 @@ json_funderupdate(struct command *cmd, p_opt_def("fund_probability", param_number, &fund_probability, current_policy->fund_probability), - p_opt("lease_fee_base_msat", param_sat, &lease_fee_sats), + p_opt("lease_fee_base_msat", param_msat_as_sat, &lease_fee_sats), p_opt("lease_fee_basis", param_number, &lease_fee_basis), p_opt("funding_weight", param_number, &funding_weight), p_opt("channel_fee_max_base_msat", param_msat, From 5531de99dec608d9b06d3d10075c58e04b79876c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:10 +0930 Subject: [PATCH 0796/1530] lease_rates: prepare for msats fields as raw numbers. We would otherwise multiply them by 1000. Signed-off-by: Rusty Russell --- common/lease_rates.c | 9 ++++++--- common/lease_rates.h | 3 ++- lightningd/dual_open_control.c | 13 ++++++------- lightningd/gossip_control.c | 9 ++++----- plugins/funder.c | 7 +++---- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/common/lease_rates.c b/common/lease_rates.c index 233713032d26..403804e92b2a 100644 --- a/common/lease_rates.c +++ b/common/lease_rates.c @@ -81,11 +81,14 @@ bool lease_rates_set_chan_fee_base_msat(struct lease_rates *rates, amt.millisatoshis); /* Raw: conversion */ } -bool lease_rates_set_lease_fee_sat(struct lease_rates *rates, - struct amount_sat amt) +bool lease_rates_set_lease_fee_msat(struct lease_rates *rates, + struct amount_msat amt) { + struct amount_sat sat; + if (!amount_msat_to_sat(&sat, amt)) + return false; return assign_overflow_u32(&rates->lease_fee_base_sat, - amt.satoshis); /* Raw: conversion */ + sat.satoshis); /* Raw: conversion */ } char *lease_rates_tohex(const tal_t *ctx, const struct lease_rates *rates) diff --git a/common/lease_rates.h b/common/lease_rates.h index 14bc947e7e71..0b87b38e332b 100644 --- a/common/lease_rates.h +++ b/common/lease_rates.h @@ -36,7 +36,8 @@ bool lease_rates_calc_fee(const struct lease_rates *rates, WARN_UNUSED_RESULT bool lease_rates_set_chan_fee_base_msat(struct lease_rates *rates, struct amount_msat amt); -WARN_UNUSED_RESULT bool lease_rates_set_lease_fee_sat(struct lease_rates *rates, struct amount_sat amt); +WARN_UNUSED_RESULT bool lease_rates_set_lease_fee_msat(struct lease_rates *rates, + struct amount_msat amt); /* Convert 'lease_rates' into a hexstring */ char *lease_rates_tohex(const tal_t *ctx, const struct lease_rates *rates); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index d72270a0d0b8..86496d13d81b 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -747,8 +747,7 @@ openchannel2_hook_deserialize(struct openchannel2_payload *payload, payload->our_shutdown_scriptpubkey = shutdown_script; - struct amount_sat sats; - struct amount_msat msats; + struct amount_msat fee_base, fee_max_base; payload->rates = tal(payload, struct lease_rates); err = json_scan(payload, buffer, toks, "{lease_fee_base_msat:%" @@ -756,10 +755,10 @@ openchannel2_hook_deserialize(struct openchannel2_payload *payload, ",channel_fee_max_base_msat:%" ",channel_fee_max_proportional_thousandths:%" ",funding_weight:%}", - JSON_SCAN(json_to_sat, &sats), + JSON_SCAN(json_to_msat, &fee_base), JSON_SCAN(json_to_u16, &payload->rates->lease_fee_basis), - JSON_SCAN(json_to_msat, &msats), + JSON_SCAN(json_to_msat, &fee_max_base), JSON_SCAN(json_to_u16, &payload->rates->channel_fee_max_proportional_thousandths), JSON_SCAN(json_to_u16, @@ -771,11 +770,11 @@ openchannel2_hook_deserialize(struct openchannel2_payload *payload, /* Convert to u32s */ if (payload->rates && - !lease_rates_set_lease_fee_sat(payload->rates, sats)) - fatal("Plugin sent overflowing `lease_fee_base_msat`"); + !lease_rates_set_lease_fee_msat(payload->rates, fee_base)) + fatal("Plugin sent overflowing/non-sat `lease_fee_base_msat`"); if (payload->rates && - !lease_rates_set_chan_fee_base_msat(payload->rates, msats)) + !lease_rates_set_chan_fee_base_msat(payload->rates, fee_max_base)) fatal("Plugin sent overflowing `channel_fee_max_base_msat`"); /* Add a serial_id to everything that doesn't have one yet */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 5e23e8b57225..3c5e25df7d81 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -363,12 +363,11 @@ static struct command_result *json_setleaserates(struct command *cmd, { struct json_stream *res; struct lease_rates *rates; - struct amount_sat *lease_base_sat; - struct amount_msat *channel_fee_base_msat; + struct amount_msat *channel_fee_base_msat, *lease_base_msat; u32 *lease_basis, *channel_fee_max_ppt, *funding_weight; if (!param(cmd, buffer, params, - p_req("lease_fee_base_msat", param_sat, &lease_base_sat), + p_req("lease_fee_base_msat", param_msat, &lease_base_msat), p_req("lease_fee_basis", param_number, &lease_basis), p_req("funding_weight", param_number, &funding_weight), p_req("channel_fee_max_base_msat", param_msat, @@ -380,7 +379,7 @@ static struct command_result *json_setleaserates(struct command *cmd, rates = tal(tmpctx, struct lease_rates); rates->lease_fee_basis = *lease_basis; - rates->lease_fee_base_sat = lease_base_sat->satoshis; /* Raw: conversion */ + rates->lease_fee_base_sat = lease_base_msat->millisatoshis / 1000; /* Raw: conversion */ rates->channel_fee_max_base_msat = channel_fee_base_msat->millisatoshis; /* Raw: conversion */ rates->funding_weight = *funding_weight; @@ -388,7 +387,7 @@ static struct command_result *json_setleaserates(struct command *cmd, = *channel_fee_max_ppt; /* Gotta check that we didn't overflow */ - if (lease_base_sat->satoshis > rates->lease_fee_base_sat) /* Raw: comparison */ + if (lease_base_msat->millisatoshis != rates->lease_fee_base_sat * (u64)1000) /* Raw: comparison */ return command_fail_badparam(cmd, "lease_fee_base_msat", buffer, params, "Overflow"); diff --git a/plugins/funder.c b/plugins/funder.c index 868834291d29..7f532fcd814a 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -1049,16 +1049,15 @@ static void tell_lightningd_lease_rates(struct plugin *p, struct lease_rates *rates) { struct json_out *jout; - struct amount_sat val; struct amount_msat mval; /* Tell lightningd with our lease rates*/ jout = json_out_new(NULL); json_out_start(jout, NULL, '{'); - val = amount_sat(rates->lease_fee_base_sat); + mval = amount_msat(rates->lease_fee_base_sat * 1000); json_out_addstr(jout, "lease_fee_base_msat", - type_to_string(tmpctx, struct amount_sat, &val)); + type_to_string(tmpctx, struct amount_msat, &mval)); json_out_add(jout, "lease_fee_basis", false, "%d", rates->lease_fee_basis); @@ -1077,7 +1076,7 @@ static void tell_lightningd_lease_rates(struct plugin *p, rpc_scan(p, "setleaserates", take(jout), /* Unused */ "{lease_fee_base_msat:%}", - JSON_SCAN(json_to_msat_as_sats, &val)); + JSON_SCAN(json_to_msat, &mval)); } From 60bd70be852911ecf174c3dfeb88ab11ae3bf8d4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:52:10 +0930 Subject: [PATCH 0797/1530] JSON: deprecate printing msat fields as strings. This changes many fields: in non-deprecated mode, they're now raw integers. This was always the intention, but the transition was never completed. Suggested-By: @ShahanaFarooqui Signed-off-by: Rusty Russell Changelog-Changed: JSON-RPC: "_msat" fields can be raw numbers, not "123msat" strings (please handle both!) Changelog-Deprecated: JSON-RPC: "_msat" fields as "123msat" strings (will be only numbers) --- common/json_helpers.c | 7 +++++-- tests/test_misc.py | 2 +- tests/test_pay.py | 4 ++-- tests/test_plugin.py | 18 +++++++++--------- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/common/json_helpers.c b/common/json_helpers.c index aaf60143546d..569b2c1077cf 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -412,8 +412,11 @@ void json_add_amount_msat_only(struct json_stream *result, { if (!deprecated_apis) assert(strends(msatfieldname, "_msat")); - json_add_string(result, msatfieldname, - type_to_string(tmpctx, struct amount_msat, &msat)); + if (deprecated_apis) + json_add_string(result, msatfieldname, + type_to_string(tmpctx, struct amount_msat, &msat)); + else + json_add_u64(result, msatfieldname, msat.millisatoshis); /* Raw: low-level helper */ } void json_add_amount_sat_compat(struct json_stream *result, diff --git a/tests/test_misc.py b/tests/test_misc.py index e2ac16fd7148..7b67fba497b2 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -969,7 +969,7 @@ def test_cli(node_factory): ' "invoices": [', ' {', r' "label": "l\"[]{}",', - ' "amount_msat": "123000msat",', + ' "amount_msat": 123000,', ' "status": "unpaid",', r' "description": "d\"[]{}",', ' }', diff --git a/tests/test_pay.py b/tests/test_pay.py index 0e701527d0a8..d3ec311dd9e8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3097,8 +3097,8 @@ def test_partial_payment(node_factory, bitcoind, executor): for i in range(2): line = l4.daemon.wait_for_log('print_htlc_onion.py: Got onion') assert "'type': 'tlv'" in line - assert "'forward_msat': '499msat'" in line or "'forward_msat': '501msat'" in line - assert "'total_msat': '1000msat'" in line + assert "'forward_msat': 499" in line or "'forward_msat': 501" in line + assert "'total_msat': 1000" in line assert "'payment_secret': '{}'".format(paysecret) in line pay = only_one(l1.rpc.listpays()['pays']) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 920d8809eef5..3942168eb78c 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -626,11 +626,11 @@ def test_openchannel_hook(node_factory, bitcoind): # Make sure plugin got all the vars we expect expected = { 'channel_flags': '1', - 'dust_limit_msat': '546000msat', - 'htlc_minimum_msat': '0msat', + 'dust_limit_msat': 546000, + 'htlc_minimum_msat': 0, 'id': l1.info['id'], 'max_accepted_htlcs': '483', - 'max_htlc_value_in_flight_msat': '18446744073709551615msat', + 'max_htlc_value_in_flight_msat': 18446744073709551615, 'to_self_delay': '5', } @@ -643,15 +643,15 @@ def test_openchannel_hook(node_factory, bitcoind): 'feerate_our_max': '150000', 'feerate_our_min': '1875', 'locktime': '.*', - 'their_funding_msat': '100000000msat', - 'channel_max_msat': '16777215000msat', + 'their_funding_msat': 100000000, + 'channel_max_msat': 16777215000, }) else: expected.update({ - 'channel_reserve_msat': '1000000msat', + 'channel_reserve_msat': 1000000, 'feerate_per_kw': '7500', - 'funding_msat': '100000000msat', - 'push_msat': '0msat', + 'funding_msat': 100000000, + 'push_msat': 0, }) l2.daemon.wait_for_log('reject_odd_funding_amounts.py: {} VARS'.format(len(expected))) @@ -1148,7 +1148,7 @@ def test_htlc_accepted_hook_forward_restart(node_factory, executor): assert onion['type'] == 'tlv' assert re.match(r'^11020203e80401..0608................$', onion['payload']) assert len(onion['shared_secret']) == 64 - assert onion['forward_msat'] == '1000msat' + assert onion['forward_msat'] == Millisatoshi(1000) assert len(onion['next_onion']) == 2 * (1300 + 32 + 33 + 1) f1.result() From 55f94322e540a89014909954bc9b01c4ca8425dc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 19:58:00 +0930 Subject: [PATCH 0798/1530] doc/PLUGINS: update to remove deprecated fields, formats. Signed-off-by: Rusty Russell --- doc/PLUGINS.md | 82 ++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 88a1797bcf8b..d7e4b3f13445 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -410,7 +410,7 @@ if the funding transaction has been included into a block. { "channel_opened": { "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "funding_msat": "100000000msat", + "funding_msat": 100000000, "funding_txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", "funding_locked": false } @@ -506,7 +506,7 @@ A notification for topic `invoice_payment` is sent every time an invoice is paid "invoice_payment": { "label": "unique-label-for-invoice", "preimage": "0000000000000000000000000000000000000000000000000000000000000000", - "msat": "10000msat" + "amount_msat": 10000 } } @@ -520,7 +520,7 @@ A notification for topic `invoice_creation` is sent every time an invoice is cre "invoice_creation": { "label": "unique-label-for-invoice", "preimage": "0000000000000000000000000000000000000000000000000000000000000000", - "msat": "10000msat" + "amount_msat": 10000 } } ``` @@ -565,12 +565,9 @@ of a forward payment is set. The json format is same as the API "payment_hash": "f5a6a059a25d1e329d9b094aeeec8c2191ca037d3f5b0662e21ae850debe8ea2", "in_channel": "103x2x1", "out_channel": "103x1x1", - "in_msatoshi": 100001001, - "in_msat": "100001001msat", - "out_msatoshi": 100000000, - "out_msat": "100000000msat", - "fee": 1001, - "fee_msat": "1001msat", + "in_msat": 100001001, + "out_msat": 100000000, + "fee_msat": 1001, "status": "settled", "received_time": 1560696342.368, "resolved_time": 1560696342.556 @@ -585,12 +582,9 @@ or "payment_hash": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "in_channel": "103x2x1", "out_channel": "110x1x0", - "in_msatoshi": 100001001, - "in_msat": "100001001msat", - "out_msatoshi": 100000000, - "out_msat": "100000000msat", - "fee": 1001, - "fee_msat": "1001msat", + "in_msat": 100001001, + "out_msat": 100000000, + "fee_msat": 1001, "status": "local_failed", "failcode": 16392, "failreason": "WIRE_PERMANENT_CHANNEL_FAILURE", @@ -648,10 +642,8 @@ the commands `sendpay`/`waitsendpay` when these commands succeed. "id": 1, "payment_hash": "5c85bf402b87d4860f4a728e2e58a2418bda92cd7aea0ce494f11670cfbfb206", "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", - "msatoshi": 100000000, - "amount_msat": "100000000msat", - "msatoshi_sent": 100001001, - "amount_sent_msat": "100001001msat", + "amount_msat": 100000000, + "amount_sent_msat": 100001001, "created_at": 1561390572, "status": "complete", "payment_preimage": "9540d98095fd7f37687ebb7759e733934234d4f934e34433d4998a37de3733ee" @@ -678,10 +670,8 @@ the commands `sendpay`/`waitsendpay` when these commands fail. "id": 2, "payment_hash": "9036e3bdbd2515f1e653cb9f22f8e4c49b73aa2c36e937c926f43e33b8db8851", "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", - "msatoshi": 100000000, - "amount_msat": "100000000msat", - "msatoshi_sent": 100001001, - "amount_sent_msat": "100001001msat", + "amount_msat": 100000000, + "amount_sent_msat": 100001001, "created_at": 1561395134, "status": "failed", "erring_index": 1, @@ -719,11 +709,11 @@ i.e. only definitively resolved HTLCs or confirmed bitcoin transactions. "vout":1, // (`chain_mvt` only) "payment_hash": "xxx", // (either type, optional on both) "part_id": 0, // (`channel_mvt` only, optional) - "credit":"2000000000msat", - "debit":"0msat", - "output_msat": "2000000000msat", // ('chain_mvt' only) + "credit_msat":2000000000, + "debit_msat":0, + "output_msat": 2000000000, // ('chain_mvt' only) "output_count": 2, // ('chain_mvt' only, typically only channel closes) - "fees": "382msat", // ('channel_mvt' only) + "fees_msat": 382, // ('channel_mvt' only) "tags": ["deposit"], "blockheight":102, // 'chain_mvt' only "timestamp":1585948198, @@ -1112,7 +1102,7 @@ This hook is called whenever a valid payment for an unpaid invoice has arrived. "payment": { "label": "unique-label-for-invoice", "preimage": "0000000000000000000000000000000000000000000000000000000000000000", - "msat": "10000msat" + "amount_msat": 10000 } } ``` @@ -1134,12 +1124,12 @@ the v1 protocol, and it has passed basic sanity checks: { "openchannel": { "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", - "funding_satoshis": "100000000msat", - "push_msat": "0msat", - "dust_limit_satoshis": "546000msat", - "max_htlc_value_in_flight_msat": "18446744073709551615msat", - "channel_reserve_satoshis": "1000000msat", - "htlc_minimum_msat": "0msat", + "funding_msat": 100000000, + "push_msat": 0, + "dust_limit_msat": 546000, + "max_htlc_value_in_flight_msat": 18446744073709551615, + "channel_reserve_msat": 1000000, + "htlc_minimum_msat": 0, "feerate_per_kw": 7500, "to_self_delay": 5, "max_accepted_htlcs": 483, @@ -1185,10 +1175,10 @@ the v2 protocol, and it has passed basic sanity checks: "openchannel2": { "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", "channel_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", - "their_funding_msat": "100000000msat", - "dust_limit_msat": "546000msat", - "max_htlc_value_in_flight_msat": "18446744073709551615msat", - "htlc_minimum_msat": "0msat", + "their_funding_msat": 100000000, + "dust_limit_msat": 546000, + "max_htlc_value_in_flight_msat": 18446744073709551615, + "htlc_minimum_msat": 0, "funding_feerate_per_kw": 7500, "commitment_feerate_per_kw": 7500, "feerate_our_max": 10000, @@ -1197,8 +1187,8 @@ the v2 protocol, and it has passed basic sanity checks: "max_accepted_htlcs": 483, "channel_flags": 1 "locktime": 2453, - "channel_max_msat": "16777215000msat", - "requested_lease_msat": "100000000msat", + "channel_max_msat": 16777215000, + "requested_lease_msat": 100000000, "lease_blockheight_start": 683990, "node_blockheight": 683990 } @@ -1236,7 +1226,7 @@ e.g. "result": "continue", "close_to": "bc1qlq8srqnz64wgklmqvurv7qnr4rvtq2u96hhfg2" "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZrrw28Oed52hTw3N7t0HbIyZhFdcZRH3+61AQAAAAD9////AGYAAAAAAQDfAgAAAAABARtaSZufCbC+P+/G23XVaQ8mDwZQFW1vlCsCYhLbmVrpAAAAAAD+////AvJs5ykBAAAAFgAUT6ORgb3CgFsbwSOzNLzF7jQS5s+AhB4AAAAAABepFNi369DMyAJmqX2agouvGHcDKsZkhwJHMEQCIHELIyqrqlwRjyzquEPvqiorzL2hrvdu9EBxsqppeIKiAiBykC6De/PDElnqWw49y2vTqauSJIVBgGtSc+vq5BQd+gEhAg0f8WITWvA8o4grxNKfgdrNDncqreMLeRFiteUlne+GZQAAAAEBIICEHgAAAAAAF6kU2Lfr0MzIAmapfZqCi68YdwMqxmSHAQQWABQB+tkKvNZml+JZIWRyLeSpXr7hZQz8CWxpZ2h0bmluZwEIexhVcpJl8ugM/AlsaWdodG5pbmcCAgABAA==", - "our_funding_msat": "39999000msat" + "our_funding_msat": 39999000 } ``` @@ -1323,11 +1313,11 @@ requests an RBF for a channel funding transaction. "rbf_channel": { "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", "channel_id": "252d1b0a1e57895e84137f28cf19ab2c35847e284c112fefdecc7afeaa5c1de7", - "their_funding_msat": "100000000msat", + "their_funding_msat": 100000000, "funding_feerate_per_kw": 7500, "feerate_our_max": 10000, "feerate_our_min": 253, - "channel_max_msat": "16777215000msat", + "channel_max_msat": 16777215000, "locktime": 2453 } } @@ -1353,7 +1343,7 @@ to calculate. { "result": "continue", "psbt": "cHNidP8BADMCAAAAAQ+yBipSVZrrw28Oed52hTw3N7t0HbIyZhFdcZRH3+61AQAAAAD9////AGYAAAAAAQDfAgAAAAABARtaSZufCbC+P+/G23XVaQ8mDwZQFW1vlCsCYhLbmVrpAAAAAAD+////AvJs5ykBAAAAFgAUT6ORgb3CgFsbwSOzNLzF7jQS5s+AhB4AAAAAABepFNi369DMyAJmqX2agouvGHcDKsZkhwJHMEQCIHELIyqrqlwRjyzquEPvqiorzL2hrvdu9EBxsqppeIKiAiBykC6De/PDElnqWw49y2vTqauSJIVBgGtSc+vq5BQd+gEhAg0f8WITWvA8o4grxNKfgdrNDncqreMLeRFiteUlne+GZQAAAAEBIICEHgAAAAAAF6kU2Lfr0MzIAmapfZqCi68YdwMqxmSHAQQWABQB+tkKvNZml+JZIWRyLeSpXr7hZQz8CWxpZ2h0bmluZwEIexhVcpJl8ugM/AlsaWdodG5pbmcCAgABAA==", - "our_funding_msat": "39999000msat" + "our_funding_msat": 39999000 } ``` @@ -1371,7 +1361,7 @@ The payload of the hook call has the following format: "onion": { "payload": "", "short_channel_id": "1x2x3", - "forward_amount": "42msat", + "forward_msat": 42, "outgoing_cltv_value": 500014, "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000", "next_onion": "[1365bytes of serialized onion]" @@ -1379,7 +1369,7 @@ The payload of the hook call has the following format: "htlc": { "short_channel_id": "4x5x6", "id": 27, - "amount_msat": "43msat", + "amount_msat": 43, "cltv_expiry": 500028, "cltv_expiry_relative": 10, "payment_hash": "0000000000000000000000000000000000000000000000000000000000000000" From 9a880a0932e7d8575620a1f7b48038c52b4b126f Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 20 Jun 2022 19:58:05 +0930 Subject: [PATCH 0799/1530] clnrs: Implement backwards compatible mode for Amount as bare u64 Since we want to transition to raw `u64` values we need to accept both for some time. --- cln-rpc/src/primitives.rs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index c141f4db3a38..460a2ac8ba38 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -316,10 +316,31 @@ impl<'de> Deserialize<'de> for Amount { D: Deserializer<'de>, { use serde::de::Error; - let s: String = Deserialize::deserialize(deserializer)?; - let ss: &str = &s; - ss.try_into() - .map_err(|_e| Error::custom("could not parse amount")) + + let any: serde_json::Value = Deserialize::deserialize(deserializer)?; + + // Amount fields used to be a string with the unit "msat" or + // "sat" as a suffix. The great consolidation in PR #5306 + // changed that to always be a `u64`, but for backwards + // compatibility we need to handle both cases. + let ires: Option = any.as_u64(); + // TODO(cdecker): Remove string parsing support once the great msat purge is complete + let sres: Option<&str> = any.as_str(); + + match (ires, sres) { + (Some(i), _) => { + // Notice that this assumes the field is denominated in `msat` + Ok(Amount::from_msat(i)) + } + (_, Some(s)) => s + .try_into() + .map_err(|_e| Error::custom("could not parse amount")), + (None, _) => { + // We reuse the integer parsing error as that's the + // default after the great msat purge of 2022. + Err(Error::custom("could not parse amount")) + } + } } } From 14483901cd689ef5f241d4399c403ed31dbbe66c Mon Sep 17 00:00:00 2001 From: Justin Moon Date: Fri, 20 May 2022 16:20:24 -0500 Subject: [PATCH 0800/1530] cln-plugin: Save "configuration" from "init" method --- plugins/src/lib.rs | 17 ++++++++++++++++- plugins/src/messages.rs | 2 ++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index e91bb69f6e28..1e576f48dba3 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -3,6 +3,7 @@ pub use anyhow::{anyhow, Context}; use futures::sink::SinkExt; extern crate log; use log::trace; +use serde::Deserialize; use std::collections::HashMap; use std::future::Future; use std::pin::Pin; @@ -44,6 +45,7 @@ where hooks: HashMap>, options: Vec, + configuration: Option, rpcmethods: HashMap>, subscriptions: HashMap>, } @@ -62,6 +64,7 @@ where hooks: HashMap::new(), subscriptions: HashMap::new(), options: vec![], + configuration: None, rpcmethods: HashMap::new(), } } @@ -207,6 +210,7 @@ where let plugin = Plugin { state: self.state, options: self.options, + configuration: self.configuration.unwrap(), // OK to unwrap, set in handle_init wait_handle, sender, }; @@ -297,6 +301,8 @@ where } } + self.configuration = Some(call.configuration); + Ok(messages::InitResponse::default()) } } @@ -363,7 +369,7 @@ where /// The state gets cloned for each request state: S, options: Vec, - + configuration: Configuration, /// A signal that allows us to wait on the plugin's shutdown. wait_handle: tokio::sync::broadcast::Sender<()>, @@ -635,6 +641,9 @@ where pub fn options(&self) -> Vec { self.options.clone() } + pub fn configuration(&self) -> Configuration { + self.configuration.clone() + } pub fn state(&self) -> &S { &self.state } @@ -653,6 +662,12 @@ where } } +#[derive(Clone, Debug, Deserialize)] +pub struct Configuration { + #[serde(rename = "lightning-dir")] + pub lightning_dir: String, +} + #[cfg(test)] mod test { use super::*; diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index 4f19aaea9507..fb0642747b8e 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -1,4 +1,5 @@ use crate::options::ConfigOption; +use crate::Configuration; use serde::de::{self, Deserializer}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -63,6 +64,7 @@ pub struct GetManifestCall {} #[derive(Deserialize, Debug)] pub(crate) struct InitCall { pub(crate) options: HashMap, + pub(crate) configuration: Configuration, } #[derive(Debug)] From 318b6e803e820875237da6500f70eb7e9022d61b Mon Sep 17 00:00:00 2001 From: Justin Moon Date: Fri, 20 May 2022 18:43:19 -0500 Subject: [PATCH 0801/1530] cln-plugin: Add unittest for parsing "init" message --- plugins/src/messages.rs | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index fb0642747b8e..4989a09d5be7 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -140,3 +140,55 @@ pub struct InitResponse { } pub trait Response: Serialize + Debug {} + +#[cfg(test)] +mod test { + use super::*; + use crate::messages; + use serde_json::json; + + #[test] + fn test_init_message_parsing() { + let value = json!({ + "jsonrpc": "2.0", + "method": "init", + "params": { + "options": { + "greeting": "World", + "number": [0] + }, + "configuration": { + "lightning-dir": "/home/user/.lightning/testnet", + "rpc-file": "lightning-rpc", + "startup": true, + "network": "testnet", + "feature_set": { + "init": "02aaa2", + "node": "8000000002aaa2", + "channel": "", + "invoice": "028200" + }, + "proxy": { + "type": "ipv4", + "address": "127.0.0.1", + "port": 9050 + }, + "torv3-enabled": true, + "always_use_proxy": false + } + }, + "id": 10, + }); + let req: JsonRpc = serde_json::from_value(value).unwrap(); + match req { + messages::JsonRpc::Request(_, messages::Request::Init(init)) => { + assert_eq!(init.options["greeting"], "World"); + assert_eq!( + init.configuration.lightning_dir, + String::from("/home/user/.lightning/testnet") + ); + } + _ => panic!("Couldn't parse init message"), + } + } +} From 5ec424bc862952e448f4cee00029a2a282b73ba5 Mon Sep 17 00:00:00 2001 From: Justin Moon Date: Sat, 21 May 2022 10:17:50 -0500 Subject: [PATCH 0802/1530] cln-plugin: Configuration struct Represents the "configuration" part of the "init" message during plugin initialization. Changelog-Added: cln_plugin: persist cln configuration from init msg --- plugins/src/lib.rs | 14 ++++++-------- plugins/src/messages.rs | 24 +++++++++++++++++++++++- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 1e576f48dba3..876bf59d3edc 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -3,7 +3,7 @@ pub use anyhow::{anyhow, Context}; use futures::sink::SinkExt; extern crate log; use log::trace; -use serde::Deserialize; +use messages::Configuration; use std::collections::HashMap; use std::future::Future; use std::pin::Pin; @@ -210,7 +210,9 @@ where let plugin = Plugin { state: self.state, options: self.options, - configuration: self.configuration.unwrap(), // OK to unwrap, set in handle_init + configuration: self + .configuration + .ok_or(anyhow!("Plugin configuration missing"))?, wait_handle, sender, }; @@ -368,7 +370,9 @@ where { /// The state gets cloned for each request state: S, + /// "options" field of "init" message sent by cln options: Vec, + /// "configuration" field of "init" message sent by cln configuration: Configuration, /// A signal that allows us to wait on the plugin's shutdown. wait_handle: tokio::sync::broadcast::Sender<()>, @@ -662,12 +666,6 @@ where } } -#[derive(Clone, Debug, Deserialize)] -pub struct Configuration { - #[serde(rename = "lightning-dir")] - pub lightning_dir: String, -} - #[cfg(test)] mod test { use super::*; diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index 4989a09d5be7..bdd80ce320eb 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -1,5 +1,4 @@ use crate::options::ConfigOption; -use crate::Configuration; use serde::de::{self, Deserializer}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -67,6 +66,29 @@ pub(crate) struct InitCall { pub(crate) configuration: Configuration, } +#[derive(Clone, Deserialize, Debug)] +pub struct Configuration { + #[serde(rename = "lightning-dir")] + pub lightning_dir: String, + #[serde(rename = "rpc-file")] + pub rpc_file: String, + pub startup: bool, + pub network: String, + pub feature_set: HashMap, + pub proxy: ProxyInfo, + #[serde(rename = "torv3-enabled")] + pub torv3_enabled: bool, + pub always_use_proxy: bool, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct ProxyInfo { + #[serde(alias = "type")] + pub typ: String, + pub address: String, + pub port: i64, +} + #[derive(Debug)] pub enum JsonRpc { Request(usize, R), From e6442d798ef24e1f868ca6003cad6e20a4d8dd5e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 21 Jun 2022 17:11:07 +0200 Subject: [PATCH 0803/1530] cln-plugin: Make the proxy-related configuration Option<> These are only populated if a proxy was specified, see lightningd/plugin.c:1855, so we were getting upset when we expected them and they weren't set. --- plugins/src/messages.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index bdd80ce320eb..90be18872551 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -75,10 +75,13 @@ pub struct Configuration { pub startup: bool, pub network: String, pub feature_set: HashMap, - pub proxy: ProxyInfo, + + // The proxy related options are only populated if a proxy was + // configured. + pub proxy: Option, #[serde(rename = "torv3-enabled")] - pub torv3_enabled: bool, - pub always_use_proxy: bool, + pub torv3_enabled: Option, + pub always_use_proxy: Option, } #[derive(Clone, Debug, Deserialize)] From bb53a178559207ba221dc5b091bc0918ca733b91 Mon Sep 17 00:00:00 2001 From: Jan Sarenik Date: Sun, 19 Jun 2022 10:00:37 +0200 Subject: [PATCH 0804/1530] doc: Tor needs CookieAuthFile set The message was [warn] CookieAuthFileGroupReadable is set, but will have no effect: you must specify an explicit CookieAuthFile to have it group-readable. Changelog-None --- doc/TOR.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/TOR.md b/doc/TOR.md index 86d3e7955b4e..db09762c32e3 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -62,6 +62,7 @@ On most Linux distributions there will be commented-out settings below in the ``` ControlPort 9051 CookieAuthentication 1 +CookieAuthFile /var/lib/tor/control_auth_cookie CookieAuthFileGroupReadable 1 ``` From acc78397eb811e7751e6b583adf675a664383809 Mon Sep 17 00:00:00 2001 From: Justin Moon Date: Tue, 21 Jun 2022 21:43:23 -0500 Subject: [PATCH 0805/1530] Remove native-tls dependency from cln-rpc --- cln-rpc/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/cln-rpc/Cargo.toml b/cln-rpc/Cargo.toml index 6c6b789beb25..6ba034df60f1 100644 --- a/cln-rpc/Cargo.toml +++ b/cln-rpc/Cargo.toml @@ -15,7 +15,6 @@ serde = { version = "1.0.131", features = ["derive"] } serde_json = "1.0.72" tokio-util = { version = "0.6.9", features = ["codec"] } tokio = { version = "1", features = ["net"]} -native-tls = { version = "*", features = ["vendored"] } futures-util = { version = "*", features = [ "sink" ] } hex = "0.4.3" From a0c64476114261e7a80f132dc759f3ee06b3fd25 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 23 Jun 2022 20:53:26 +0930 Subject: [PATCH 0806/1530] lightningd: remove overzealous assertion. This is being hit: it's possible if connectd and lightningd get desynchronized, and we'll handle this later when peer is activated. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e3cb6c8d33dc..47789cdc5e0c 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1169,9 +1169,6 @@ void peer_connected(struct lightningd *ld, const u8 *msg) if (!peer_subds_pending(peer)) connect_succeeded(ld, peer, hook_payload->incoming, &hook_payload->addr); - /* Can't be opening, since we wouldn't have sent peer_disconnected. */ - assert(!peer->uncommitted_channel); - /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { log_peer_info(ld->log, &id, "Peer says it sees our address as: %s", From 37403e471caeb7dc2ec78f24acc913d100e18691 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 21 Jun 2022 20:17:50 +0930 Subject: [PATCH 0807/1530] pytest: add (failing) test for db with old Tor v2 address. Signed-off-by: Rusty Russell --- tests/test_misc.py | 10 ++++++++++ wallet/wallet.c | 9 +++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 7b67fba497b2..cfb6977be1f2 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2605,3 +2605,13 @@ def test_datastore_keylist(node_factory): 'string': 'ab2val2', 'generation': 1, 'hex': b'ab2val2'.hex()}]} + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', + "This test requires sqlite3") +def test_torv2_in_db(node_factory): + l1, l2 = node_factory.line_graph(2, wait_for_announce=True) + + l1.stop() + l1.db_manip("UPDATE peers SET address='3fyb44wdhnd2ghhl.onion:1234';") + l1.start() diff --git a/wallet/wallet.c b/wallet/wallet.c index ff9506299b67..346ae90b27cd 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -836,10 +836,15 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) db_col_node_id(stmt, "node_id", &id); + /* This can happen for peers last seen on Torv2! */ addrstr = db_col_strdup(tmpctx, stmt, "address"); if (!parse_wireaddr_internal(addrstr, &addr, DEFAULT_PORT, - false, false, true, true, NULL)) - goto done; + false, false, true, true, NULL)) { + log_unusual(w->log, "Unparsable peer address %s: replacing", + addrstr); + parse_wireaddr_internal("127.0.0.1:1", &addr, DEFAULT_PORT, + false, false, true, true, NULL); + } /* FIXME: save incoming in db! */ peer = new_peer(w->ld, db_col_u64(stmt, "id"), &id, &addr, false); From 0b7897302e9884d17f1349f0dfb4dc1c400c3719 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 12:03:14 +0930 Subject: [PATCH 0808/1530] common/gossip_store: optimize case where entries are filtered out. @whitslack complained of large CPU usage by connectd at startup; I ran perf record on connectd on my machine (which sees a little spike, only) and I see the cost of reading and discarding the entries: ``` - 95.52% 5.24% lightning_conne lightning_connectd [.] gossip_store_next - 90.28% gossip_store_next + 40.27% tal_alloc_arr_ + 22.78% tal_free + 11.74% crc32c + 9.32% fromwire_peektype + 4.10% __libc_pread64 (inlined) 1.70% be32_to_cpu ``` Much of this is caused by the search for our own gossip: keeping this separately would be even better, but this fix is minimal. Signed-off-by: Rusty Russell Changelog-Fixed: connectd: reduce initial CPU load when connecting to peers. --- common/gossip_store.c | 14 +++++++++----- gossipd/gossip_store.c | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index b36897aca653..4fdcde1e4d16 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -77,8 +77,16 @@ u8 *gossip_store_next(const tal_t *ctx, continue; } - checksum = be32_to_cpu(hdr.crc); + /* Skip any timestamp filtered */ timestamp = be32_to_cpu(hdr.timestamp); + if (!push && + !timestamp_filter(timestamp_min, timestamp_max, + timestamp)) { + *off += r + msglen; + continue; + } + + checksum = be32_to_cpu(hdr.crc); msg = tal_arr(ctx, u8, msglen); r = pread(*gossip_store_fd, msg, msglen, *off + r); if (r != msglen) @@ -105,10 +113,6 @@ u8 *gossip_store_next(const tal_t *ctx, && type != WIRE_CHANNEL_UPDATE && type != WIRE_NODE_ANNOUNCEMENT) { msg = tal_free(msg); - } else if (!push && - !timestamp_filter(timestamp_min, timestamp_max, - timestamp)) { - msg = tal_free(msg); } else if (!push && push_only) { msg = tal_free(msg); } diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index f06c5a876678..1032d415c75b 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -277,7 +277,7 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) oldlen = lseek(old_fd, SEEK_END, 0); newlen = lseek(new_fd, SEEK_END, 0); append_msg(old_fd, towire_gossip_store_ended(tmpctx, newlen), - 0, false, &oldlen); + 0, true, &oldlen); close(old_fd); status_debug("gossip_store_compact_offline: %zu deleted, %zu copied", deleted, count); @@ -565,7 +565,7 @@ bool gossip_store_compact(struct gossip_store *gs) /* Write end marker now new one is ready */ append_msg(gs->fd, towire_gossip_store_ended(tmpctx, len), - 0, false, &gs->len); + 0, true, &gs->len); gs->count = count; gs->deleted = 0; From 49c6459148502df5be6f62574bd0ca52524c9259 Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Wed, 22 Jun 2022 17:55:51 -0500 Subject: [PATCH 0809/1530] Update hsm error formatting. Changelog-None --- hsmd/hsmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 4ea0805c84aa..0c354a7edbd3 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -122,7 +122,7 @@ static struct io_plan *bad_req_fmt(struct io_conn *conn, char *str; va_start(ap, fmt); - str = tal_fmt(tmpctx, fmt, ap); + str = tal_vfmt(tmpctx, fmt, ap); va_end(ap); /*~ If the client was actually lightningd, it's Game Over; we actually From d4bc4f646035a9efa566109da561b00698abd8c9 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 17 Jun 2022 20:48:32 +0100 Subject: [PATCH 0810/1530] signmessage: improve the UX of the rpc command when zbase is not a valid one Changelog-Fixed: signmessage: improve the UX of the rpc command when zbase is not a valid one Stacktrace generated with a bad `zbase` ``` lightningd: lightningd/signmessage.c:59: from_zbase32: Assertion `len == tal_bytelen(u8arr)' failed lightningd: FATAL SIGNAL 6 (version v0.11.1) 0x55b9b1b4e617 send_backtrace [...] ``` Signed-off-by: Vincenzo Palazzo --- lightningd/signmessage.c | 4 +--- tests/test_misc.py | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 44dfe52bfae7..c2236d5cc74e 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -56,8 +56,7 @@ static const u8 *from_zbase32(const tal_t *ctx, const char *msg) if (!bech32_convert_bits(u8arr, &len, 8, u5arr, tal_bytelen(u5arr), 5, false)) return tal_free(u8arr); - assert(len == tal_bytelen(u8arr)); - return u8arr; + return len == tal_bytelen(u8arr) ? u8arr : tal_free(u8arr); } static struct command_result *json_signmessage(struct command *cmd, @@ -235,4 +234,3 @@ static const struct json_command json_checkmessage_cmd = { "Verify a digital signature {zbase} of {message} signed with {pubkey}", }; AUTODATA(json_command, &json_checkmessage_cmd); - diff --git a/tests/test_misc.py b/tests/test_misc.py index cfb6977be1f2..fdbb525e6c2f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1866,6 +1866,9 @@ def test_signmessage(node_factory): checknokey = l2.rpc.checkmessage(message="message for you", zbase=zm) assert checknokey['pubkey'] == l1.info['id'] assert checknokey['verified'] + # check that checkmassage used with a wrong zbase format throws an RPC exception + with pytest.raises(RpcError, match="zbase is not valid zbase32"): + l2.rpc.checkmessage(message="wrong zbase format", zbase="wrong zbase format") def test_include(node_factory): From 5444a843b60e0b1d1f6742fa9e912fcdfe5736f8 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 22 Jun 2022 12:23:34 +0200 Subject: [PATCH 0811/1530] git: Ignore some more generated files and the cln-grpc plugin --- .gitignore | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fa8e86ef94c5..a242ad4cb214 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,10 @@ doc/lightning*.[1578] *_wiregen.[ch] *_printgen.[ch] *_gettextgen.po +tests/node_pb2.py +tests/node_pb2_grpc.py +tests/primitives_pb2.py +tests/primitives_pb2_grpc.py # Ignore unrelated stuff .DS_Store @@ -67,4 +71,5 @@ doc/lightning*.[1578] # Rust targets target -Cargo.lock \ No newline at end of file +Cargo.lock +plugins/cln-grpc From e48c0dda854c3ef07e71a032938e77a84c63d5de Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 20 Jun 2022 11:00:36 +0930 Subject: [PATCH 0812/1530] common: downgrade "internal error" errors from lnd. Prior to 0.11.0 we had cases where we would treat errors as warnings: regretfully, this is still needed. This message in particular has been widely reported, and it now causes channel force closes. Downgrade and log. I did insert some snarky log message earlier, but hey, I'm sure CLN has done worse things to our peers! Signed-off-by: Rusty Russell Changelog-Fixed: Protocol: treat LND "internal error" as warnings, not force close events (as we did in v0.10). --- common/peer_failed.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/peer_failed.c b/common/peer_failed.c index c9c18d84dbe7..96325cf2351b 100644 --- a/common/peer_failed.c +++ b/common/peer_failed.c @@ -84,6 +84,15 @@ void peer_failed_received_errmsg(struct per_peer_state *pps, { u8 *msg; + /* LND sends "internal error" and we close the channel. But + * prior to 0.11 we would turn this into a warning, and they + * would recover after a reconnect. So we downgrade, but snark + * about it in the logs. */ + if (!warning && streq(desc, "internal error")) { + status_unusual("lnd sent 'internal error':" + " let's give it some space"); + warning = true; + } msg = towire_status_peer_error(NULL, channel_id, desc, warning, NULL); peer_billboard(true, "Received %s", desc); From a196c77fe6b2dd8428dfb0135a5b7dec6cd10291 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 23 Jun 2022 22:11:26 +0930 Subject: [PATCH 0813/1530] common: downgrade LND 'internal error' properly. Thanks to @zerofeerouting for another report. "desc" here is the sanitized message, eg: "ERROR error channel 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef: internal error" Signed-off-by: Rusty Russell --- common/peer_failed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/peer_failed.c b/common/peer_failed.c index 96325cf2351b..d3d60114e16f 100644 --- a/common/peer_failed.c +++ b/common/peer_failed.c @@ -88,7 +88,7 @@ void peer_failed_received_errmsg(struct per_peer_state *pps, * prior to 0.11 we would turn this into a warning, and they * would recover after a reconnect. So we downgrade, but snark * about it in the logs. */ - if (!warning && streq(desc, "internal error")) { + if (!warning && strends(desc, "internal error")) { status_unusual("lnd sent 'internal error':" " let's give it some space"); warning = true; From 92f10f2c340f3a009a64f054c58e1c85c9b6d819 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 22 Jun 2022 15:06:04 +0200 Subject: [PATCH 0814/1530] pyln: Fix relative path dependencies when publishing to PyPI So this was quite a journey: - We want relative depdendencies (using the `path` argument) whenever developing locally. Otherwise we would have to install each dependency every time we change a single character, which undoubtedly would cause us to waste time trying to debug an issue just because we forgot to install. - When publishing however we want to rely on the version number, since the repo context gets lost upon publishing, and path dependencies cause failures. The solution then it seems is to use `dev-dependencies` (not that surprising once you find it) with relative paths, so that `poetry install` uses these over the normal dependencies (no idea how they dedup them) and use `dependencies` when publishing. The paths are still in there when publishing, but `pip install` ignores them. I checked that `poetry install` from an unrelated project doesn't accidentally use the path dependencies, even when adding them as dev-dependencies. This should hopefully also allow installing them as a repo link, though I can't test that right now. --- contrib/pyln-client/pyproject.toml | 6 ++++-- contrib/pyln-proto/pyproject.toml | 2 +- contrib/pyln-testing/pyproject.toml | 6 ++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index c47607ad7efb..a3dfffe8aa36 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-client" -version = "0.11.0" +version = "0.11.1" description = "Client library and plugin library for Core Lightning" authors = ["Christian Decker "] license = "BSD-MIT" @@ -13,10 +13,12 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" pyln-bolt7 = "^1.0" -pyln-proto = "^0.10.2" +pyln-proto = "^0.11" [tool.poetry.dev-dependencies] pytest = "^7.0.1" +pyln-bolt7 = { path = "../pyln-spec/bolt7", develop = true } +pyln-proto = { path = "../pyln-proto", develop = true} [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml index 7ffa534761d8..8c7ed3411719 100644 --- a/contrib/pyln-proto/pyproject.toml +++ b/contrib/pyln-proto/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-proto" -version = "0.11.0" +version = "0.11.1" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." authors = ["Christian Decker "] license = "BSD-MIT" diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index d21ea10c6183..784bbe23c403 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-testing" -version = "0.11.0" +version = "0.11.1" description = "Test your Core Lightning integration, plugins or whatever you want" authors = ["Christian Decker "] license = "BSD-MIT" @@ -17,11 +17,13 @@ ephemeral-port-reserve = "^1.1.4" psycopg2-binary = "^2.9.3" python-bitcoinlib = "^0.11.0" jsonschema = "^4.4.0" -pyln-client = "^0.10.2" +pyln-client = "^0.11" Flask = "^2.0.3" cheroot = "^8.6.0" psutil = "^5.9.0" + [tool.poetry.dev-dependencies] +pyln-client = { path = "../pyln-client", develop = true} [build-system] requires = ["poetry-core>=1.0.0"] From 56dde2cb7718377dc4b0487301cdf5eadc2d7e5f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 13:55:01 +0930 Subject: [PATCH 0815/1530] lightningd: multiple log-file options allow more than one log output. I've wanted this for a while: the ability to log to multiple places at once. Signed-off-by: Rusty Russell Changelog-Changed: lightningd: `log-file` option specified multiple times opens multiple log files. --- doc/lightningd-config.5.md | 6 +- lightningd/lightningd.c | 2 +- lightningd/lightningd.h | 2 +- lightningd/log.c | 140 +++++++++++++++++++++---------------- lightningd/options.c | 13 +++- tests/test_misc.py | 18 +++++ 6 files changed, 116 insertions(+), 65 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 5fc07bea9388..31832958bf62 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -155,8 +155,10 @@ Prefix for log lines: this can be customized if you want to merge logs with multiple daemons. **log-file**=*PATH* -Log to this file instead of stdout. Sending lightningd(8) SIGHUP will -cause it to reopen this file (useful for log rotation). +Log to this file (instead of stdout). If you specify this more than once +you'll get more than one log file: **-** is used to mean stdout. Sending +lightningd(8) SIGHUP will cause it to reopen each file (useful for log +rotation). **log-timestamps**=*BOOL* Set this to false to turn off timestamp prefixes (they will still appear diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index b8e60b3fff65..e9c993bd40e9 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -184,7 +184,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) * allocation function): ld->log will be implicitly freed when ld * is. */ ld->log = new_log(ld, ld->log_book, NULL, "lightningd"); - ld->logfile = NULL; + ld->logfiles = NULL; /*~ We explicitly set these to NULL: if they're still NULL after option * parsing, we know they're to be set to the defaults. */ diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 5e3a1edb70b0..5440384b756f 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -110,7 +110,7 @@ struct lightningd { struct log_book *log_book; /* Log for general stuff. */ struct log *log; - const char *logfile; + const char **logfiles; /* This is us. */ struct node_id id; diff --git a/lightningd/log.c b/lightningd/log.c index e578f90228eb..ddbcb726738f 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -39,7 +39,9 @@ struct log_book { /* Non-null once it's been initialized */ enum log_level *default_print_level; struct timeabs init_time; - FILE *outf; + + /* Array of log files: one per ld->logfiles[] */ + FILE **outfiles; bool print_timestamps; struct log_entry *log; @@ -128,17 +130,18 @@ static const char *level_prefix(enum log_level level) abort(); } -static void log_to_file(const char *prefix, - enum log_level level, - const struct node_id *node_id, - const struct timeabs *time, - const char *str, - const u8 *io, - size_t io_len, - bool print_timestamps, - FILE *logf) +static void log_to_files(const char *prefix, + enum log_level level, + const struct node_id *node_id, + const struct timeabs *time, + const char *str, + const u8 *io, + size_t io_len, + bool print_timestamps, + FILE **outfiles) { char tstamp[sizeof("YYYY-mm-ddTHH:MM:SS.nnnZ ")]; + char *entry; if (print_timestamps) { char iso8601_msec_fmt[sizeof("YYYY-mm-ddTHH:MM:SS.%03dZ ")]; @@ -151,25 +154,34 @@ static void log_to_file(const char *prefix, const char *dir = level == LOG_IO_IN ? "[IN]" : "[OUT]"; char *hex = tal_hexstr(NULL, io, io_len); if (!node_id) - fprintf(logf, "%s%s: %s%s %s\n", - tstamp, prefix, str, dir, hex); + entry = tal_fmt(tmpctx, "%s%s: %s%s %s\n", + tstamp, prefix, str, dir, hex); else - fprintf(logf, "%s%s-%s: %s%s %s\n", - tstamp, - node_id_to_hexstr(tmpctx, node_id), - prefix, str, dir, hex); + entry = tal_fmt(tmpctx, "%s%s-%s: %s%s %s\n", + tstamp, + node_id_to_hexstr(tmpctx, node_id), + prefix, str, dir, hex); tal_free(hex); } else { if (!node_id) - fprintf(logf, "%s%s %s: %s\n", - tstamp, level_prefix(level), prefix, str); + entry = tal_fmt(tmpctx, "%s%s %s: %s\n", + tstamp, level_prefix(level), prefix, str); else - fprintf(logf, "%s%s %s-%s: %s\n", - tstamp, level_prefix(level), - node_id_to_hexstr(tmpctx, node_id), - prefix, str); + entry = tal_fmt(tmpctx, "%s%s %s-%s: %s\n", + tstamp, level_prefix(level), + node_id_to_hexstr(tmpctx, node_id), + prefix, str); + } + + /* Default if nothing set is stdout */ + if (!outfiles) { + fwrite(entry, strlen(entry), 1, stdout); + fflush(stdout); + } + for (size_t i = 0; i < tal_count(outfiles); i++) { + fwrite(entry, strlen(entry), 1, outfiles[i]); + fflush(outfiles[i]); } - fflush(logf); } static size_t mem_used(const struct log_entry *e) @@ -264,7 +276,7 @@ struct log_book *new_log_book(struct lightningd *ld, size_t max_mem) lr->mem_used = 0; lr->num_entries = 0; lr->max_mem = max_mem; - lr->outf = stdout; + lr->outfiles = NULL; lr->default_print_level = NULL; list_head_init(&lr->print_filters); lr->init_time = time_now(); @@ -389,12 +401,12 @@ static struct log_entry *new_log_entry(struct log *log, enum log_level level, static void maybe_print(struct log *log, const struct log_entry *l) { if (l->level >= log_print_level(log)) - log_to_file(log->prefix->prefix, l->level, - l->nc ? &l->nc->node_id : NULL, - &l->time, l->log, - l->io, tal_bytelen(l->io), - log->lr->print_timestamps, - log->lr->outf); + log_to_files(log->prefix->prefix, l->level, + l->nc ? &l->nc->node_id : NULL, + &l->time, l->log, + l->io, tal_bytelen(l->io), + log->lr->print_timestamps, + log->lr->outfiles); } void logv(struct log *log, enum log_level level, @@ -439,12 +451,12 @@ void log_io(struct log *log, enum log_level dir, /* Print first, in case we need to truncate. */ if (l->level >= log_print_level(log)) - log_to_file(log->prefix->prefix, l->level, - l->nc ? &l->nc->node_id : NULL, - &l->time, str, - data, len, - log->lr->print_timestamps, - log->lr->outf); + log_to_files(log->prefix->prefix, l->level, + l->nc ? &l->nc->node_id : NULL, + &l->time, str, + data, len, + log->lr->print_timestamps, + log->lr->outfiles); /* Save a tal header, by using raw malloc. */ l->log = strdup(str); @@ -608,7 +620,7 @@ static void show_log_level(char buf[OPT_SHOW_LEN], const struct log *log) static char *arg_log_prefix(const char *arg, struct log *log) { /* log->lr owns this, since it keeps a pointer to it. */ - tal_free(log->prefix); + log_prefix_drop(log->prefix); log->prefix = log_prefix_new(log->lr, arg); return NULL; } @@ -635,11 +647,14 @@ static struct io_plan *setup_read(struct io_conn *conn, struct lightningd *ld); static struct io_plan *rotate_log(struct io_conn *conn, struct lightningd *ld) { log_info(ld->log, "Ending log due to SIGHUP"); - fclose(ld->log->lr->outf); - - ld->log->lr->outf = fopen(ld->logfile, "a"); - if (!ld->log->lr->outf) - err(1, "failed to reopen log file %s", ld->logfile); + for (size_t i = 0; i < tal_count(ld->log->lr->outfiles); i++) { + if (streq(ld->logfiles[i], "-")) + continue; + fclose(ld->log->lr->outfiles[i]); + ld->log->lr->outfiles[i] = fopen(ld->logfiles[i], "a"); + if (!ld->log->lr->outfiles[i]) + err(1, "failed to reopen log file %s", ld->logfiles[i]); + } log_info(ld->log, "Started log due to SIGHUP"); return setup_read(conn, ld); @@ -688,22 +703,29 @@ static void setup_log_rotation(struct lightningd *ld) char *arg_log_to_file(const char *arg, struct lightningd *ld) { int size; + FILE *outf; - if (ld->logfile) { - fclose(ld->log->lr->outf); - ld->logfile = tal_free(ld->logfile); - } else + if (!ld->logfiles) { setup_log_rotation(ld); + ld->logfiles = tal_arr(ld, const char *, 0); + ld->log->lr->outfiles = tal_arr(ld->log->lr, FILE *, 0); + } + + if (streq(arg, "-")) + outf = stdout; + else { + outf = fopen(arg, "a"); + if (!outf) + return tal_fmt(NULL, "Failed to open: %s", strerror(errno)); + } - ld->logfile = tal_strdup(ld, arg); - ld->log->lr->outf = fopen(arg, "a"); - if (!ld->log->lr->outf) - return tal_fmt(NULL, "Failed to open: %s", strerror(errno)); + tal_arr_expand(&ld->logfiles, tal_strdup(ld->logfiles, arg)); + tal_arr_expand(&ld->log->lr->outfiles, outf); /* For convenience make a block of empty lines just like Bitcoin Core */ - size = ftell(ld->log->lr->outf); + size = ftell(outf); if (size > 0) - fprintf(ld->log->lr->outf, "\n\n\n\n"); + fprintf(outf, "\n\n\n\n"); log_debug(ld->log, "Opened log file %s", arg); return NULL; @@ -721,7 +743,7 @@ void opt_register_logging(struct lightningd *ld) ld->log, "log prefix"); opt_register_early_arg("--log-file=", arg_log_to_file, NULL, ld, - "log to file instead of stdout"); + "Also log to file (- for stdout)"); } void logging_options_parsed(struct log_book *lr) @@ -737,12 +759,12 @@ void logging_options_parsed(struct log_book *lr) const struct log_entry *l = &lr->log[i]; if (l->level >= filter_level(lr, l->prefix)) - log_to_file(l->prefix->prefix, l->level, - l->nc ? &l->nc->node_id : NULL, - &l->time, l->log, - l->io, tal_bytelen(l->io), - lr->print_timestamps, - lr->outf); + log_to_files(l->prefix->prefix, l->level, + l->nc ? &l->nc->node_id : NULL, + &l->time, l->log, + l->io, tal_bytelen(l->io), + lr->print_timestamps, + lr->outfiles); } } diff --git a/lightningd/options.c b/lightningd/options.c index 43be6bcccd01..e7e2b9cc47de 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -880,7 +880,7 @@ static void check_config(struct lightningd *ld) if (ld->always_use_proxy && !ld->proxyaddr) fatal("--always-use-proxy needs --proxy"); - if (ld->daemon_parent_fd != -1 && !ld->logfile) + if (ld->daemon_parent_fd != -1 && !ld->logfiles) fatal("--daemon needs --log-file"); } @@ -1438,6 +1438,14 @@ static void json_add_opt_addrs(struct json_stream *response, } } +static void json_add_opt_log_to_files(struct json_stream *response, + const char *name0, + const char **logfiles) +{ + for (size_t i = 0; i < tal_count(logfiles); i++) + json_add_string(response, name0, logfiles[i]); +} + struct json_add_opt_alt_subdaemon_args { const char *name0; struct json_stream *response; @@ -1568,7 +1576,8 @@ static void add_config(struct lightningd *ld, } else if (opt->cb_arg == (void *)opt_set_alias) { answer = (const char *)ld->alias; } else if (opt->cb_arg == (void *)arg_log_to_file) { - answer = ld->logfile; + if (ld->logfiles) + json_add_opt_log_to_files(response, name0, ld->logfiles); } else if (opt->cb_arg == (void *)opt_add_addr) { json_add_opt_addrs(response, name0, ld->proposed_wireaddr, diff --git a/tests/test_misc.py b/tests/test_misc.py index fdbb525e6c2f..3ddd751c7078 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1475,6 +1475,24 @@ def check_new_log(): wait_for(lambda: os.path.exists(logpath)) wait_for(check_new_log) + # Multiple log files + l2 = node_factory.get_node(options={'log-file': ['logfile1', 'logfile2']}, start=False) + logpath1 = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, 'logfile1') + logpath2 = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, 'logfile2') + l2.daemon.start(wait_for_initialized=False) + + wait_for(lambda: os.path.exists(logpath1)) + wait_for(lambda: os.path.exists(logpath2)) + wait_for(lambda: os.path.exists(os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, "lightning-rpc"))) + lines = subprocess.check_output(['cli/lightning-cli', + '--network={}'.format(TEST_NETWORK), + '--lightning-dir={}' + .format(l2.daemon.lightning_dir), + '-H', + 'listconfigs']).decode('utf-8').splitlines() + assert 'log-file=logfile1' in lines + assert 'log-file=logfile2' in lines + @unittest.skipIf(VALGRIND, "Valgrind sometimes fails assert on injected SEGV") From 575b94c1efbb595d305eda5a9d5181e87ff6e886 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 13:56:01 +0930 Subject: [PATCH 0816/1530] pytest: Remove all trace of python's "flaky" module. Over time, it has cost us more developer cycles than it has gained. It has hidden intermittant bugs, and allowed cruft to accumulate: when we eventually tried to figure out what was going wrong, the actual change which caused it was now stale and forgotten. This was a particular bane during the connectd rewrite, and I worked through some issues which had occurred before, but were not more likely. Signed-off-by: Rusty Russell --- .github/scripts/build.sh | 2 +- .github/workflows/bsd.yml | 2 +- .github/workflows/macos.yaml | 2 +- contrib/docker/Dockerfile.builder | 1 - contrib/docker/scripts/build.sh | 2 +- contrib/pyln-client/Makefile | 2 +- contrib/pyln-proto/Makefile | 2 +- contrib/pyln-spec/Makefile | 2 +- contrib/pyln-testing/Makefile | 2 +- poetry.lock | 12 ------------ pyproject.toml | 1 - tests/test_connection.py | 4 ---- tests/test_misc.py | 3 --- tests/test_pay.py | 5 +---- tests/test_plugin.py | 1 - tests/test_wallet.py | 4 ---- 16 files changed, 9 insertions(+), 38 deletions(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index a08de2f23f78..7ac1279dfcef 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -33,7 +33,7 @@ cat config.vars cat << EOF > pytest.ini [pytest] -addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 +addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 markers = slow_test: marks tests as slow (deselect with '-m "not slow_test"') EOF diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 524b9b80390f..603ad8c0a954 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -79,7 +79,7 @@ jobs: cat << EOF > pytest.ini [pytest] - addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 --junitxml=report.xml --json-report --json-report-file=report.json --json-report-indent=2 + addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 --junitxml=report.xml --json-report --json-report-file=report.json --json-report-indent=2 markers = slow_test: marks tests as slow (deselect with '-m "not slow_test"') EOF diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index 331389f0c4ac..c763c829c0ee 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -59,7 +59,7 @@ jobs: cat << EOF > pytest.ini [pytest] - addopts=-p no:logging --color=yes --timeout=600 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 --junitxml=report.xml --json-report --json-report-file=report.json --json-report-indent=2 + addopts=-p no:logging --color=yes --timeout=600 --timeout-method=thread --test-group-random-seed=42 --junitxml=report.xml --json-report --json-report-file=report.json --json-report-indent=2 markers = slow_test: marks tests as slow (deselect with '-m "not slow_test"') EOF diff --git a/contrib/docker/Dockerfile.builder b/contrib/docker/Dockerfile.builder index 2128d66772fb..ff4d15fed2b0 100644 --- a/contrib/docker/Dockerfile.builder +++ b/contrib/docker/Dockerfile.builder @@ -56,7 +56,6 @@ RUN pip3 install --upgrade pip && \ Flask==1.0.2 \ cheroot==8.2.1 \ ephemeral-port-reserve==1.1.0 \ - flaky==3.4.0 \ pytest-benchmark==3.1.1 \ pytest-forked==0.2 \ pytest-timeout==1.3.3 \ diff --git a/contrib/docker/scripts/build.sh b/contrib/docker/scripts/build.sh index c54173d42561..6670a917b80a 100755 --- a/contrib/docker/scripts/build.sh +++ b/contrib/docker/scripts/build.sh @@ -34,7 +34,7 @@ cat config.vars cat << EOF > pytest.ini [pytest] -addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 --force-flaky --no-success-flaky-report --max-runs=3 +addopts=-p no:logging --color=yes --timeout=1800 --timeout-method=thread --test-group-random-seed=42 markers = slow_test: marks tests as slow (deselect with '-m "not slow_test"') EOF diff --git a/contrib/pyln-client/Makefile b/contrib/pyln-client/Makefile index 33bb6722aaa3..c8703965ac72 100644 --- a/contrib/pyln-client/Makefile +++ b/contrib/pyln-client/Makefile @@ -46,7 +46,7 @@ test-release: check $(ARTEFACTS) pyproject.toml # tests against it (make sure not to use any virtualenv that may have # pyln-${PKG} already installed). virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - testpypi/bin/python3 -m pip install -r requirements.txt flaky pytest-timeout + testpypi/bin/python3 -m pip install -r requirements.txt pytest-timeout testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" testpypi/bin/pytest tests diff --git a/contrib/pyln-proto/Makefile b/contrib/pyln-proto/Makefile index a7ddbba6366c..ef2eeb88c644 100644 --- a/contrib/pyln-proto/Makefile +++ b/contrib/pyln-proto/Makefile @@ -46,7 +46,7 @@ test-release: check $(ARTEFACTS) pyproject.toml # tests against it (make sure not to use any virtualenv that may have # pyln-${PKG} already installed). virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - testpypi/bin/python3 -m pip install -r requirements.txt flaky pytest-timeout + testpypi/bin/python3 -m pip install -r requirements.txt pytest-timeout testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" testpypi/bin/pytest tests diff --git a/contrib/pyln-spec/Makefile b/contrib/pyln-spec/Makefile index ba856b8cf37a..5ed10647ca2e 100755 --- a/contrib/pyln-spec/Makefile +++ b/contrib/pyln-spec/Makefile @@ -58,7 +58,7 @@ test-release-bolt%: $(ARTEFACTS) # pyln-proto already installed). virtualenv testpypi-$* --python=/usr/bin/python3 --download --always-copy --clear # Install the requirements from the prod repo, they are not being kept up to date on the test repo - testpypi-$*/bin/python3 -m pip install -r requirements.txt pytest flaky pytest-timeout + testpypi-$*/bin/python3 -m pip install -r requirements.txt pytest pytest-timeout testpypi-$*/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-bolt$* testpypi-$*/bin/python3 -c "from pyln.spec import bolt$* as bolt;assert(bolt.__version__ == '$(call version,$*)')" testpypi-$*/bin/pytest bolt$*/tests diff --git a/contrib/pyln-testing/Makefile b/contrib/pyln-testing/Makefile index 012875cb4b45..4ad0ba390bb7 100644 --- a/contrib/pyln-testing/Makefile +++ b/contrib/pyln-testing/Makefile @@ -46,7 +46,7 @@ test-release: check $(ARTEFACTS) pyproject.toml # tests against it (make sure not to use any virtualenv that may have # pyln-${PKG} already installed). virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - testpypi/bin/python3 -m pip install -r requirements.txt flaky pytest-timeout + testpypi/bin/python3 -m pip install -r requirements.txt pytest-timeout testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" testpypi/bin/pytest tests diff --git a/poetry.lock b/poetry.lock index ae03af20f8c7..eda0ac167407 100644 --- a/poetry.lock +++ b/poetry.lock @@ -166,14 +166,6 @@ mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.8.0,<2.9.0" pyflakes = ">=2.4.0,<2.5.0" -[[package]] -name = "flaky" -version = "3.7.0" -description = "Plugin for nose or pytest that automatically reruns flaky tests." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "flask" version = "2.0.3" @@ -982,10 +974,6 @@ flake8 = [ {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] -flaky = [ - {file = "flaky-3.7.0-py2.py3-none-any.whl", hash = "sha256:d6eda73cab5ae7364504b7c44670f70abed9e75f77dd116352f662817592ec9c"}, - {file = "flaky-3.7.0.tar.gz", hash = "sha256:3ad100780721a1911f57a165809b7ea265a7863305acb66708220820caf8aa0d"}, -] flask = [ {file = "Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, {file = "Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, diff --git a/pyproject.toml b/pyproject.toml index 4d7b09c3e6c8..da609986289c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,6 @@ flake8 = "^4.0.1" mypy = "^0.931" pytest-custom-exit-code = "0.3.0" pyln-testing = { path = "./contrib/pyln-testing", develop = true } -flaky = "^3.7.0" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/test_connection.py b/tests/test_connection.py index cde97a9ca37b..87ac9a9ba42a 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,6 +1,5 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK -from flaky import flaky # noqa: F401 from ephemeral_port_reserve import reserve # type: ignore from pyln.client import RpcError, Millisatoshi import pyln.proto.wire as wire @@ -658,7 +657,6 @@ def test_reconnect_gossiping(node_factory): l2.daemon.wait_for_log('processing now old peer gone') -@flaky @pytest.mark.developer("needs dev-disconnect") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @@ -853,7 +851,6 @@ def test_reconnect_receiver_fulfill(node_factory): assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'paid' -@flaky @pytest.mark.developer @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') @@ -886,7 +883,6 @@ def test_shutdown_reconnect(node_factory): assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 -@flaky @pytest.mark.developer def test_reconnect_remote_sends_no_sigs(node_factory): """We re-announce, even when remote node doesn't send its announcement_signatures on reconnect. diff --git a/tests/test_misc.py b/tests/test_misc.py index 3ddd751c7078..1995a0bd19a0 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2,7 +2,6 @@ from decimal import Decimal from fixtures import * # noqa: F401,F403 from fixtures import LightningNode, TEST_NETWORK -from flaky import flaky # noqa: F401 from pyln.client import RpcError from threading import Event from pyln.testing.utils import ( @@ -1031,7 +1030,6 @@ def test_daemon_option(node_factory): assert 'No child process' not in f.read() -@flaky @pytest.mark.developer("needs DEVELOPER=1") def test_blockchaintrack(node_factory, bitcoind): """Check that we track the blockchain correctly across reorgs @@ -1276,7 +1274,6 @@ def test_bitcoind_goes_backwards(node_factory, bitcoind): l1.daemon.wait_for_log('Adding block 111') -@flaky @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_reserve_enforcement(node_factory, executor): diff --git a/tests/test_pay.py b/tests/test_pay.py index d3ec311dd9e8..11581ee6a940 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1,6 +1,5 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK -from flaky import flaky # noqa: F401 from io import BytesIO from pyln.client import RpcError, Millisatoshi from pyln.proto.onion import TlvPayload @@ -2765,13 +2764,11 @@ def test_pay_no_secret(node_factory, bitcoind): l1.rpc.pay(inv_nosecret) -@flaky def test_shadow_routing(node_factory): """ Test the value randomization through shadow routing - Since there is a very low (0.5**10) probability that it fails we mark it - as flaky. + Note there is a very low (0.5**10) probability that it fails. """ # We need l3 for random walk l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3942168eb78c..8a9f395ace36 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1,7 +1,6 @@ from collections import OrderedDict from datetime import datetime from fixtures import * # noqa: F401,F403 -from flaky import flaky # noqa: F401 from hashlib import sha256 from pyln.client import RpcError, Millisatoshi from pyln.proto import Invoice diff --git a/tests/test_wallet.py b/tests/test_wallet.py index d6e1d30c3e3f..7fa9a0478703 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -2,7 +2,6 @@ from decimal import Decimal from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK -from flaky import flaky # noqa: F401 from pyln.client import RpcError, Millisatoshi from utils import ( only_one, wait_for, sync_blockheight, EXPERIMENTAL_FEATURES, @@ -1295,13 +1294,10 @@ def test_withdraw_nlocktime(node_factory): assert nlocktime > 0 and nlocktime <= tip -@flaky @unittest.skipIf(VALGRIND, "A big loop is used to check fuzz.") def test_withdraw_nlocktime_fuzz(node_factory, bitcoind): """ Test that we eventually fuzz nLockTime for withdrawal transactions. - Marked flaky "just in case" as we fuzz from 0 to 100 with a 10% - probability. """ l1 = node_factory.get_node(1) l1.fundwallet(10**8) From aefcea44a32bd416aefa3c0762bd3b2ea9f01028 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 13:57:01 +0930 Subject: [PATCH 0817/1530] pytest: fix flake in test_gossip_timestamp_filter We didn't get the second channel_announcement in the filter: ``` # Now choose range which will only give second update. msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, after_12 - backdate, after_23 - after_12 + 1, filters=['0109', '0012']) # 0x0100 = channel_announcement # 0x0102 = channel_update # (Node announcement may have any timestamp) types = Counter([m[0:4] for m in msgs]) > assert types['0100'] == 1 E assert 0 == 1 ``` Looking at the gossip store: ``` 1089: t=1653008725 channel_announcement: 0100420c13f233d9b461b035257b77704e0003a0280efdbb8f8964bf4c5bd22b8f8b2e205a3c57d7d5f4dcd812cd41be3d8f84c60046c1fd31abefd0985c2263f8f5074bb7dff548ac106dc10ef4485f5921d9b252d094f9518b44a842c1a2c7f98759fa2f8803febeb59e50b65a8f258877e18f6224262e218de38f21438b2112ef0425d64174092bcd8d757f18982c3fb83c8ae2fb5ee88363248e51bdea247cef055a01a17580b60735b82b88fea87dccb724a743beaf4bf3cf578b0da445afcc786fe3b061429f3288e6970819236e97134898ce900b7b61159148d6785e26932e21f1c337cb6f86a16e967e27396b168a19b0d1478c4be2579a9b96aa0125aa000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006e0000010000022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d02d595ae92b3544c3250fb772f214ad8d4c51425033740a5bcc357190add6d7e7a02d6063d022691b2490ab454dee73a57c6ff5d308352b461ece69f3c284f2c2412 1533: channel_amount: 100000sat 1555: t=1653008725 channel_update: 01022b98e7d680ffa8096d0a2bda62c0b8b3fc975a5f91c0cff0a42a76b37285276c0156581f85c0be2ee0ed7e7003cb67f5d476a7e3714960836b882bb3ad78097406226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006e00000100006286e955010000060000000000000000000000010000000a0000000005e69ec0 1705: t=1653008729 node_announcement: 010175f8ed9f48db511d43f1da96aa307ccc53a521dcc2f8fb732c6938650e6cc00f672e772134a29e16b723a1e9cf626cf942323bd3fa294befd25430a9395c1fa40007800000080269a26286e959035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d035d2b484f5050494e47464952452d34646636366661000000000000000000000000000000 1866: t=1653008720 channel_update: 010266385083f1d6e554e1701f2456c24212c5aa937f7b090d32d243678b2fe78f7f52ed3626d6cdd52703bf78013bb9d1a621ae5b7d68fc965d29bb94574e340b5606226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006e00000100006286e950010100060000000000000000000000010000000a00000 ``` It's not clear what we times we were querying, but let's make it clearer by putting a full 10 seconds between them, and making our queries more exact. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 750bdd63bf4f..885b766ec4d7 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -264,7 +264,8 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): after_12 = int(time.time()) # Make another one, different timestamp. - time.sleep(1) + time.sleep(10) + before_23 = int(time.time()) chan23, _ = l2.fundchannel(l3, 10**5) bitcoind.generate_block(5) @@ -310,8 +311,8 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): # Now choose range which will only give second update. msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, - after_12 - backdate, - after_23 - after_12 + 1, + before_23 - backdate, + after_23 - before_23 + 1, filters=['0109', '0012']) # 0x0100 = channel_announcement From 930860c7816a7bee8da29342a7b0afcfc0bce433 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 13:58:01 +0930 Subject: [PATCH 0818/1530] pytest: fix flakes in test_gossip_query_channel_range Nodes now spray their own gossip at the start; this can race with our query, and they can give us gossip we didn't ask for. The fix is to always query an uninvolved node: in this case, we only connected l3 and l4 to test zlib, which is gone, so now we can remove that and simply always query l4. ``` 2022-05-23T01:32:53.4695809Z # reply_channel_range == 264 2022-05-23T01:32:53.4696416Z > assert msgs == ['0108' 2022-05-23T01:32:53.4696835Z # blockhash 2022-05-23T01:32:53.4697328Z + genesis_blockhash 2022-05-23T01:32:53.4697841Z # first_blocknum, number_of_blocks, complete 2022-05-23T01:32:53.4698685Z + format(0, '08x') + format(65535, '08x') + '01' 2022-05-23T01:32:53.4699245Z # encoded_short_ids 2022-05-23T01:32:53.4699859Z + format(len(encoded) // 2, '04x') 2022-05-23T01:32:53.4700255Z + encoded] 2022-05-23T01:32:53.4700799Z E AssertionError: assert ['0100194b419...700000010000'] == ['010806226e4...700000010000'] 2022-05-23T01:32:53.4710678Z E At index 0 diff: '0100194b4190af98beedf231901aeda2dcee0e757dd5327219aa007ea6027e769c98665f49e0f99e03b91e80896ed01cd9c4b157c77e908852097d2ba753c6ca01ae41a90a81c9f492c770d9dd35010c702c7c564045cf85a4bf6efaf25cceb9b81b56d1526429d3c60bf1c7ed6a835720730fd7ad1851a937d18aecf8bc3400dd4e0a30d5c4d65999ac39ae6eb3b98d27e3157973598256d2dd4718cd39010a3612580e7f3bba9eda0fb595976f955e345b6affa62fd556f7257a30d54dd87743dc38585e7c31e1ca80b42cf803bd4c1aaa3127351c95de8a4198c46eb0bd7b4ce175a4b51fcaf9676b01806d237758494fcf3d1558279060a0c3b84806a3330c41000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0000700000010000035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de19903c47b1b2afcfd68c7b86c0976adb9a4f0835bc78242fced745d78433497a867d6021c29746e4136ddff456483df3980c2d0d5f31c93ef5ded564f7294a10d7414aa' != '010806226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f000000000000ffff01001900000068000001000100006900000100000000700000010000' ``` Signed-off-by: Rusty Russell --- tests/test_gossip.py | 61 ++++++++++++-------------------------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 885b766ec4d7..5afe5f1c9743 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -704,9 +704,8 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): num_blocks=6, wait_for_mempool=1) - # Make sure l2 has received all the gossip. - l2.daemon.wait_for_logs(['Received node_announcement for node ' + l1.info['id'], - 'Received node_announcement for node ' + l3.info['id']]) + # Make sure l4 has received all the gossip. + l4.daemon.wait_for_logs(['Received node_announcement for node ' + n.info['id'] for n in (l1, l2, l3)]) scid12 = only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'][0]['short_channel_id'] scid23 = only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels'][0]['short_channel_id'] @@ -715,8 +714,8 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): assert block23 == block12 + 1 - # Asks l2 for all channels, gets both. - msgs = l2.query_gossip('query_channel_range', + # Asks l4 for all channels, gets both. + msgs = l4.query_gossip('query_channel_range', chainparams['chain_hash'], 0, 1000000, filters=['0109', '0012']) @@ -735,7 +734,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): + encoded] # Does not include scid12 - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, block12, filters=['0109', '0012']) @@ -749,7 +748,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): '000100'] # Does include scid12 - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, block12 + 1, filters=['0109', '0012']) @@ -768,7 +767,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): + encoded] # Doesn't include scid23 - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, block23, filters=['0109', '0012']) @@ -787,7 +786,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): + encoded] # Does include scid23 - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, block12, block23 - block12 + 1, filters=['0109', '0012']) @@ -806,7 +805,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): + encoded] # Only includes scid23 - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, block23, 1, filters=['0109', '0012']) @@ -825,7 +824,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): + encoded] # Past both - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, block23 + 1, 1000000, filters=['0109', '0012']) @@ -838,16 +837,16 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): # encoded_short_ids + '000100'] - # Make l2 split reply into two (technically async) - l2.rpc.dev_set_max_scids_encode_size(max=9) - l2.daemon.wait_for_log('Set max_scids_encode_bytes to 9') + # Make l4 split reply into two (technically async) + l4.rpc.dev_set_max_scids_encode_size(max=9) + l4.daemon.wait_for_log('Set max_scids_encode_bytes to 9') - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, 1000000, filters=['0109', '0012']) # It should definitely have split - l2.daemon.wait_for_log('reply_channel_range: splitting 0-1 of 2') + l4.daemon.wait_for_log('reply_channel_range: splitting 0-1 of 2') start = 0 scids = '00' @@ -867,40 +866,12 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): assert scids == encoded # Test overflow case doesn't split forever; should still only get 2 for this - msgs = l2.query_gossip('query_channel_range', + msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 1, 429496000, filters=['0109', '0012']) assert len(msgs) == 2 - # This used to be large enough for zlib to kick in, but no longer! - scid34, _ = l3.fundchannel(l4, 10**5) - mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) - l2.daemon.wait_for_log('Received node_announcement for node ' + l4.info['id']) - - # Restore infinite encode size. - l2.rpc.dev_set_max_scids_encode_size(max=(2**32 - 1)) - l2.daemon.wait_for_log('Set max_scids_encode_bytes to {}' - .format(2**32 - 1)) - - msgs = l2.query_gossip('query_channel_range', - genesis_blockhash, - 0, 65535, - filters=['0109', '0012']) - encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12, scid23, scid34], - check=True, - timeout=TIMEOUT, - stdout=subprocess.PIPE).stdout.strip().decode() - # reply_channel_range == 264 - assert msgs == ['0108' - # blockhash - + genesis_blockhash - # first_blocknum, number_of_blocks, complete - + format(0, '08x') + format(65535, '08x') + '01' - # encoded_short_ids - + format(len(encoded) // 2, '04x') - + encoded] - # Long test involving 4 lightningd instances. @pytest.mark.developer("needs DEVELOPER=1") From d274de2bf48c6d8ed71db9bb9ff9aaa67dd48f3f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 13:59:01 +0930 Subject: [PATCH 0819/1530] pytest: fix flake in test_htlc_too_dusty_outgoing We can sneak the HTLC add in before we do the update_fee which fails: ``` # the channel should start warning -- too much dust inv = l2.rpc.invoice(htlc_val_msat, str(num_dusty_htlcs + 1), str(num_dusty_htlcs + 1)) with pytest.raises(RpcError, match=r'WIRE_TEMPORARY_CHANNEL_FAILURE'): > l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) E Failed: DID NOT RAISE tests/test_pay.py:2654: Failed ``` From the logs: ``` lightningd-1: 2022-05-20T02:16:40.008Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: NEW:: HTLC LOCAL 14 = SENT_ADD_HTLC/RCVD_ADD_HTLC lightningd-1: 2022-05-20T02:16:40.010Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: Adding HTLC 14 amount=10000000msat cltv=113 gave CHANNEL_ERR_ADD_OK lightningd-1: 2022-05-20T02:16:40.012Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: peer_out WIRE_UPDATE_ADD_HTLC lightningd-1: 2022-05-20T02:16:40.015Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: REPLY WIRE_CHANNELD_OFFER_HTLC_REPLY with 0 fds lightningd-1: 2022-05-20T02:16:40.026Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: peer_out WIRE_WARNING lightningd-1: 2022-05-20T02:16:40.029Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: billboard perm: Too much dust to update fee (Desired feerate update 20000) lightningd-1: 2022-05-20T02:16:40.035Z INFO 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-chan#1: Peer transient failure in CHANNELD_NORMAL: channeld WARNING: Too much dust to update fee (Desired feerate update 20000) lightningd-1: 2022-05-20T02:16:40.039Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-lightningd: Will try reconnect in 60 seconds ``` Signed-off-by: Rusty Russell --- tests/test_pay.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index 11581ee6a940..dc22f42e2eab 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2634,6 +2634,9 @@ def test_htlc_too_dusty_outgoing(node_factory, bitcoind, chainparams): l1.set_feerates([feerate * 2] * 4, False) l1.restart() + # Make sure fails before we try sending htlc! + l1.daemon.wait_for_log('Too much dust to update fee') + # the channel should start warning -- too much dust inv = l2.rpc.invoice(htlc_val_msat, str(num_dusty_htlcs + 1), str(num_dusty_htlcs + 1)) with pytest.raises(RpcError, match=r'WIRE_TEMPORARY_CHANNEL_FAILURE'): From 71b1eaf2fe7d741fe8a8c68f08e7c3b675369bad Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:00:01 +0930 Subject: [PATCH 0820/1530] pyln-testing: try harder if cleaning directory fails. This happens under CI, but it's not informative. Sleep and retry. Also, "except (OSError, Exception)" does not seem to do what you'd think: this clause never gets run. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/fixtures.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index d576a3abac1f..e2c26de403d1 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -14,6 +14,7 @@ import string import sys import tempfile +import time # A dict in which we count how often a particular test has run so far. Used to @@ -94,10 +95,14 @@ def directory(request, test_base_dir, test_name): if not failed: try: shutil.rmtree(directory) - except (OSError, Exception): + except OSError: + # Usually, this means that e.g. valgrind is still running. Wait + # a little and retry. files = [os.path.join(dp, f) for dp, dn, fn in os.walk(directory) for f in fn] - print("Directory still contains files:", files) - raise + print("Directory still contains files: ", files) + print("... sleeping then retrying") + time.sleep(10) + shutil.rmtree(directory) else: logging.debug("Test execution failed, leaving the test directory {} intact.".format(directory)) From a1b8b40d135c64e580d6767288a1ca5d6188e54a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:01:01 +0930 Subject: [PATCH 0821/1530] connectd: fix debug message on bind fail. It doesn't get the right errno, and it says "create" not "bind". ``` 2022-05-20T03:04:46.498Z DEBUG connectd: Failed to create 2 socket: Success 2022-05-20T03:04:46.500Z DEBUG connectd: REPLY WIRE_CONNECTD_INIT_REPLY with 0 fds 2022-05-20T03:04:46.501Z DEBUG connectd: connectd_init_done 2022-05-20T03:04:46.503Z **BROKEN** connectd: Failed to bind socket for 127.0.0.1:37871: Address already in use ``` Signed-off-by: Rusty Russell --- connectd/connectd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 474fe8b40c92..635f251b4363 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1077,14 +1077,14 @@ static struct listen_fd *make_listen_fd(const tal_t *ctx, int on = 1; if (fd < 0) { + const char *es = strerror(errno); *errstr = tal_fmt(ctx, "Failed to create socket for %s%s: %s", is_websocket ? "websocket " : "", type_to_string(tmpctx, struct wireaddr_internal, wi), - strerror(errno)); - status_debug("Failed to create %u socket: %s", - domain, strerror(errno)); + es); + status_debug("Failed to create %u socket: %s", domain, es); return NULL; } @@ -1094,14 +1094,14 @@ static struct listen_fd *make_listen_fd(const tal_t *ctx, strerror(errno)); if (bind(fd, addr, len) != 0) { + const char *es = strerror(errno); *errstr = tal_fmt(ctx, "Failed to bind socket for %s%s: %s", is_websocket ? "websocket " : "", type_to_string(tmpctx, struct wireaddr_internal, wi), - strerror(errno)); - status_debug("Failed to create %u socket: %s", - domain, strerror(errno)); + es); + status_debug("Failed to bind %u socket: %s", domain, es); goto fail; } From eb25e080397d6e89ea6993069e3924043fde3d70 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:02:01 +0930 Subject: [PATCH 0822/1530] pytest: fix port allocation race when nodes restart. We use ephemeral_port_reserve to grab ports, but this can fail when we restart a node, since the port can be reallocated at that point. Attempt to overcome this using a global reserved list (is there a neater way?). Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 43 ++++++++++++++++++---- tests/test_misc.py | 2 +- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index e5d95404a775..beea7a9662d7 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -7,10 +7,10 @@ from pyln.testing.btcproxy import BitcoinRpcProxy from collections import OrderedDict from decimal import Decimal -from ephemeral_port_reserve import reserve # type: ignore from pyln.client import LightningRpc from pyln.client import Millisatoshi +import ephemeral_port_reserve # type: ignore import json import logging import lzma @@ -146,6 +146,27 @@ def get_tx_p2wsh_outnum(bitcoind, tx, amount): return None +unused_port_lock = threading.Lock() +unused_port_set = set() + + +def reserve_unused_port(): + """Get an unused port: avoids handing out the same port unless it's been + returned""" + with unused_port_lock: + while True: + port = ephemeral_port_reserve.reserve() + if port not in unused_port_set: + break + unused_port_set.add(port) + + return port + + +def drop_unused_port(port): + unused_port_set.remove(port) + + class TailableProc(object): """A monitorable process that we can start, stop and tail. @@ -367,7 +388,10 @@ def __init__(self, bitcoin_dir="/tmp/bitcoind-test", rpcport=None): TailableProc.__init__(self, bitcoin_dir, verbose=False) if rpcport is None: - rpcport = reserve() + self.reserved_rpcport = reserve_unused_port() + rpcport = self.reserved_rpcport + else: + self.reserved_rpcport = None self.bitcoin_dir = bitcoin_dir self.rpcport = rpcport @@ -398,6 +422,10 @@ def __init__(self, bitcoin_dir="/tmp/bitcoind-test", rpcport=None): self.rpc = SimpleBitcoinProxy(btc_conf_file=self.conf_file) self.proxies = [] + def __del__(self): + if self.reserved_rpcport is not None: + drop_unused_port(self.reserved_rpcport) + def start(self): TailableProc.start(self) self.wait_for_log("Done loading", timeout=TIMEOUT) @@ -1281,6 +1309,7 @@ def __init__(self, request, testname, bitcoind, executor, directory, self.testname = testname self.next_id = 1 self.nodes = [] + self.reserved_ports = [] self.executor = executor self.bitcoind = bitcoind self.directory = directory @@ -1313,10 +1342,6 @@ def split_options(self, opts): cli_opts = {k: v for k, v in opts.items() if k not in node_opt_keys} return node_opts, cli_opts - def get_next_port(self): - with self.lock: - return reserve() - def get_node_id(self): """Generate a unique numeric ID for a lightning node """ @@ -1361,7 +1386,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, expect_fail=False, cleandir=True, **kwargs): self.throttler.wait() node_id = self.get_node_id() if not node_id else node_id - port = self.get_next_port() + port = reserve_unused_port() lightning_dir = os.path.join( self.directory, "lightning-{}/".format(node_id)) @@ -1383,6 +1408,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, node.set_feerates(feerates, False) self.nodes.append(node) + self.reserved_ports.append(port) if dbfile: out = open(os.path.join(node.daemon.lightning_dir, TEST_NETWORK, 'lightningd.sqlite3'), 'xb') @@ -1495,4 +1521,7 @@ def killall(self, expected_successes): json.dumps(leaks, sort_keys=True, indent=4) )) + for p in self.reserved_ports: + drop_unused_port(p) + return not unexpected_fail, err_msgs diff --git a/tests/test_misc.py b/tests/test_misc.py index 1995a0bd19a0..6fb65f32438c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2104,7 +2104,7 @@ def test_unix_socket_path_length(node_factory, bitcoind, directory, executor, db os.makedirs(lightning_dir) db = db_provider.get_db(lightning_dir, "test_unix_socket_path_length", 1) - l1 = LightningNode(1, lightning_dir, bitcoind, executor, VALGRIND, db=db, port=node_factory.get_next_port()) + l1 = LightningNode(1, lightning_dir, bitcoind, executor, VALGRIND, db=db, port=reserve()) # `LightningNode.start()` internally calls `LightningRpc.getinfo()` which # exercises the socket logic, and raises an issue if it fails. From 6247bee11eb8c2a85e31af629e39e2018f114272 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:03:01 +0930 Subject: [PATCH 0823/1530] pytest: try to fix bitcoind socket timeout in test_channel_lease_unilat_closes Split harder! Signed-off-by: Rusty Russell ``` # This can timeout, so do it in four easy stages. for i in range(4): > bitcoind.generate_block(4032 // 4) tests/test_closing.py:971: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-testing/pyln/testing/utils.py:475: in generate_block return self.rpc.generatetoaddress(numblocks, to_addr) contrib/pyln-testing/pyln/testing/utils.py:372: in f res = proxy._call(name, *args) /opt/hostedtoolcache/Python/3.7.13/x64/lib/python3.7/site-packages/bitcoin/rpc.py:233: in _call response = self._get_response() /opt/hostedtoolcache/Python/3.7.13/x64/lib/python3.7/site-packages/bitcoin/rpc.py:263: in _get_response http_response = self.__conn.getresponse() /opt/hostedtoolcache/Python/3.7.13/x64/lib/python3.7/http/client.py:1373: in getresponse response.begin() /opt/hostedtoolcache/Python/3.7.13/x64/lib/python3.7/http/client.py:319: in begin version, status, reason = self._read_status() /opt/hostedtoolcache/Python/3.7.13/x64/lib/python3.7/http/client.py:280: in _read_status line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = b = def readinto(self, b): """Read up to len(b) bytes into the writable buffer *b* and return the number of bytes read. If the socket is non-blocking and no bytes are available, None is returned. If *b* is non-empty, a 0 return value indicates that the connection was shutdown at the other end. """ self._checkClosed() self._checkReadable() if self._timeout_occurred: raise OSError("cannot read from timed out object") while True: try: > return self._sock.recv_into(b) E socket.timeout: timed out ``` Signed-off-by: Rusty Russell --- tests/test_closing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 896ff0ca0763..8a763d1b17c3 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -966,10 +966,10 @@ def test_channel_lease_unilat_closes(node_factory, bitcoind): # we *can* spend the 1csv lock one l2.rpc.withdraw(l2.rpc.newaddr()['bech32'], "all", utxos=[utxo3]) - # This can timeout, so do it in four easy stages. - for i in range(4): - bitcoind.generate_block(4032 // 4) - sync_blockheight(bitcoind, [l2, l3]) + # This can timeout, so do it in easy stages. + for i in range(16): + bitcoind.generate_block(4032 // 16) + sync_blockheight(bitcoind, [l2, l3]) l2.rpc.withdraw(l2.rpc.newaddr()['bech32'], "all", utxos=[utxo1]) From b1f393f355ed3a3ad11dd46b18c5b782db7497b2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:04:01 +0930 Subject: [PATCH 0824/1530] pytest: fix race in test_multichan We can fail to use larger channel if it's not ready yet: ``` 2022-05-23T01:20:05.5325600Z # Check it used the larger channel! 2022-05-23T01:20:05.5326376Z > assert before[chan23a_idx]['to_us_msat'] == after[chan23a_idx]['to_us_msat'] 2022-05-23T01:20:05.5326961Z E assert 1000000000msat == 900000000msat 2022-05-23T01:20:05.5327240Z 2022-05-23T01:20:05.5327621Z tests/test_connection.py:3896: AssertionError ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 87ac9a9ba42a..ae125f04586b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3869,6 +3869,8 @@ def test_multichan(node_factory, executor, bitcoind): assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2) bitcoind.generate_block(1, wait_for_mempool=1) + # Make sure new channel is also CHANNELD_NORMAL + wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == ["CHANNELD_NORMAL", "CHANNELD_NORMAL"]) # Dance around to get the *other* scid. wait_for(lambda: all(['short_channel_id' in c for c in l3.rpc.listpeers()['peers'][0]['channels']])) From 2f27aad8d01e9dd02873d1d4163e09e72794a45e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:05:01 +0930 Subject: [PATCH 0825/1530] pytest: fix flake in test_wallet.py::test_hsm_secret_encryption Under valgrind, at least, this test fails regularly, and this sleep fixes it. Signed-off-by: Rusty Russell --- tests/test_wallet.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 7fa9a0478703..00985245bf92 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -12,6 +12,7 @@ import os import pytest import subprocess +import time import unittest @@ -1044,6 +1045,12 @@ def test_hsm_secret_encryption(node_factory): assert(l1.daemon.proc.wait(WAIT_TIMEOUT) == HSM_BAD_PASSWORD) assert(l1.daemon.is_in_log("Wrong password for encrypted hsm_secret.")) + # Not sure why this helps, but seems to reduce flakiness where + # tail() thread in testing/utils.py gets 'ValueError: readline of + # closed file' and we get `ValueError: Process died while waiting for logs` + # when waiting for "Server started with public key" below. + time.sleep(10) + # Test we can restore the same wallet with the same password l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) l1.daemon.wait_for_log(r'The hsm_secret is encrypted') From c1a111b68cd0bbb9f44476cb107bc18ecfb34378 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:06:01 +0930 Subject: [PATCH 0826/1530] pytest: wait for stderr, rather than asserting. Two almost identical assertion fails under CI. Try waiting. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 2 +- tests/test_plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 5afe5f1c9743..acbeeba9453f 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -244,7 +244,7 @@ def test_only_announce_one_dns(node_factory, bitcoind): # and test that we can't announce more than one DNS address l1 = node_factory.get_node(may_fail=True, expect_fail=True, options={'announce-addr': ['localhost.localdomain:12345', 'example.com:12345']}) - assert l1.daemon.is_in_stderr("Only one DNS can be announced") + wait_for(lambda: l1.daemon.is_in_stderr("Only one DNS can be announced")) @pytest.mark.developer("needs DEVELOPER=1") diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 8a9f395ace36..c18be94a5540 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -95,7 +95,7 @@ def test_option_types(node_factory): # the node should fail to start, and we get a stderr msg assert not n.daemon.running - assert n.daemon.is_in_stderr('bool_opt: ! does not parse as type bool') + wait_for(lambda: n.daemon.is_in_stderr('bool_opt: ! does not parse as type bool')) # What happens if we give it a bad int-option? n = node_factory.get_node(options={ From 2b7915359cfecaa8a4926e36088c321e3896e7e1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:07:01 +0930 Subject: [PATCH 0827/1530] gossmap: handle case where private channel turns into public. Usually we won't see this, since private is deleted. But we could have already read the private channel before that. Handle it properly. (Tested by removing the gossip_store deletion code and making sure this worked). We have to fix up the test, which announces a channel twice! Signed-off-by: Rusty Russell --- common/gossmap.c | 11 +++++++++++ common/test/run-route-specific.c | 6 +++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/common/gossmap.c b/common/gossmap.c index 277e87d990b0..918fb913a624 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -398,6 +398,7 @@ static struct gossmap_chan *add_channel(struct gossmap *map, const size_t feature_len_off = 2 + (64 + 64 + 64 + 64); size_t feature_len; size_t plus_scid_off; + struct short_channel_id scid; struct node_id node_id[2]; struct gossmap_node *n[2]; struct gossmap_chan *chan; @@ -409,6 +410,16 @@ static struct gossmap_chan *add_channel(struct gossmap *map, map_nodeid(map, cannounce_off + plus_scid_off + 8, &node_id[0]); map_nodeid(map, cannounce_off + plus_scid_off + 8 + PUBKEY_CMPR_LEN, &node_id[1]); + /* We can have a channel upgrade from private->public, but + * that's the only time we get duplicates */ + scid.u64 = map_be64(map, cannounce_off + plus_scid_off); + chan = gossmap_find_chan(map, &scid); + if (chan) { + assert(chan->private); + assert(!private); + gossmap_remove_chan(map, chan); + } + /* We carefully map pointers to indexes, since new_node can move them! */ n[0] = gossmap_find_node(map, &node_id[0]); if (n[0]) diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 01d12f94f86a..3a1488c49377 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -215,9 +215,9 @@ int main(int argc, char *argv[]) 0, 10, 5); /* {'active': True, 'short_id': '6990:2:1/0', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 0, 'destination': '02ea622d5c8d6143f15ed3ce1d501dd0d3d09d3b1c83a44d0034949f8a9ab60f06', 'source': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'last_update': 1504064344}, */ - add_connection(store_fd, &b, &c, "6990x2x1", - AMOUNT_MSAT(100), AMOUNT_MSAT(1000), - 0, 10, 5); + update_connection(store_fd, &b, &c, "6990x2x1", + AMOUNT_MSAT(100), AMOUNT_MSAT(1000), + 0, 10, 5, false); /* {'active': True, 'short_id': '6989:2:1/1', 'fee_per_kw': 10, 'delay': 5, 'message_flags': 0, 'channel_flags': 1, 'destination': '0230ad0e74ea03976b28fda587bb75bdd357a1938af4424156a18265167f5e40ae', 'source': '03c173897878996287a8100469f954dd820fcd8941daed91c327f168f3329be0bf', 'last_update': 1504064344}]} */ update_connection(store_fd, &a, &b, "6989x2x1", From 11de721ba96a3d8c35e1949b486bdf1c5377dc0d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:08:01 +0930 Subject: [PATCH 0828/1530] gossipd: fix gossmap race. When upgrading a channel from private to public, we would delete the private channel then add the public one. However, this is visible in the gossmap! In particular, the node may be removed from the gossmap and then re-added, so it may temporarily vanish! I was seeing an occasional assertion inside node_factory.line_graph: ``` @pytest.mark.developer def test_reconnect_remote_sends_no_sigs(node_factory): """We re-announce, even when remote node doesn't send its announcement_signatures on reconnect. """ > l1, l2 = node_factory.line_graph(2, wait_for_announce=True, opts={'may_reconnect': True}) tests/test_connection.py:870: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-testing/pyln/testing/utils.py:1467: in line_graph self.join_nodes(nodes, fundchannel, fundamount, wait_for_announce, announce_channels) contrib/pyln-testing/pyln/testing/utils.py:1460: in join_nodes wait_for(lambda: 'alias' in only_one(end.rpc.listnodes(n.info['id'])['nodes'])) contrib/pyln-testing/pyln/testing/utils.py:88: in wait_for while not success(): contrib/pyln-testing/pyln/testing/utils.py:1460: in wait_for(lambda: 'alias' in only_one(end.rpc.listnodes(n.info['id'])['nodes'])) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ arr = [] def only_one(arr): """Many JSON RPC calls return an array; often we only expect a single entry """ > assert len(arr) == 1 E AssertionError ``` Signed-off-by: Rusty Russell --- gossipd/routing.c | 100 ++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 38 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index cfed0e0eb74c..57a1c7e8059d 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -493,18 +493,23 @@ static void destroy_chan_check(struct chan *chan) } #endif -/* We used to make this a tal_add_destructor2, but that costs 40 bytes per - * chan, and we only ever explicitly free it anyway. */ -void free_chan(struct routing_state *rstate, struct chan *chan) +static void free_chans_from_node(struct routing_state *rstate, struct chan *chan) { remove_chan_from_node(rstate, chan->nodes[0], chan); remove_chan_from_node(rstate, chan->nodes[1], chan); - uintmap_del(&rstate->chanmap, chan->scid.u64); - #if DEVELOPER chan->sat.satoshis = (unsigned long)chan; /* Raw: dev-hack */ #endif +} + +/* We used to make this a tal_add_destructor2, but that costs 40 bytes per + * chan, and we only ever explicitly free it anyway. */ +void free_chan(struct routing_state *rstate, struct chan *chan) +{ + free_chans_from_node(rstate, chan); + uintmap_del(&rstate->chanmap, chan->scid.u64); + tal_free(chan); } @@ -789,13 +794,45 @@ static void add_channel_announce_to_broadcast(struct routing_state *rstate, rstate->local_channel_announced |= is_local; } +static void delete_chan_messages_from_store(struct routing_state *rstate, + struct chan *chan) +{ + int update_type, announcment_type; + + if (is_chan_public(chan)) { + update_type = WIRE_CHANNEL_UPDATE; + announcment_type = WIRE_CHANNEL_ANNOUNCEMENT; + } else { + update_type = WIRE_GOSSIP_STORE_PRIVATE_UPDATE; + announcment_type = WIRE_GOSSIP_STORE_PRIVATE_CHANNEL; + } + + /* If these aren't in the store, these are noops. */ + gossip_store_delete(rstate->gs, + &chan->bcast, announcment_type); + gossip_store_delete(rstate->gs, + &chan->half[0].bcast, update_type); + gossip_store_delete(rstate->gs, + &chan->half[1].bcast, update_type); +} + +void remove_channel_from_store(struct routing_state *rstate, + struct chan *chan) +{ + /* Put in tombstone marker */ + gossip_store_mark_channel_deleted(rstate->gs, &chan->scid); + + /* Now delete old entries. */ + delete_chan_messages_from_store(rstate, chan); +} + bool routing_add_channel_announcement(struct routing_state *rstate, const u8 *msg TAKES, struct amount_sat sat, u32 index, struct peer *peer) { - struct chan *chan; + struct chan *oldchan; secp256k1_ecdsa_signature node_signature_1, node_signature_2; secp256k1_ecdsa_signature bitcoin_signature_1, bitcoin_signature_2; u8 *features; @@ -821,34 +858,35 @@ bool routing_add_channel_announcement(struct routing_state *rstate, /* The channel may already exist if it was non-public from * local_add_channel(); normally we don't accept new * channel_announcements. See handle_channel_announcement. */ - chan = get_channel(rstate, &scid); + oldchan = get_channel(rstate, &scid); /* private updates will exist in the store before the announce: we * can't index those for broadcast since they would predate it, so we * add fresh ones. */ - if (chan) { + if (oldchan) { /* If this was in the gossip_store, gossip_store is bad! */ if (index) { status_broken("gossip_store channel_announce" " %u replaces %u!", - index, chan->bcast.index); + index, oldchan->bcast.index); return false; } /* Reload any private updates */ - if (chan->half[0].bcast.index) + if (oldchan->half[0].bcast.index) private_updates[0] = gossip_store_get_private_update(NULL, rstate->gs, - chan->half[0].bcast.index); - if (chan->half[1].bcast.index) + oldchan->half[0].bcast.index); + if (oldchan->half[1].bcast.index) private_updates[1] = gossip_store_get_private_update(NULL, rstate->gs, - chan->half[1].bcast.index); + oldchan->half[1].bcast.index); - remove_channel_from_store(rstate, chan); - free_chan(rstate, chan); + /* We don't delete it from store until *after* we've put the + * other one in! */ + uintmap_del(&rstate->chanmap, oldchan->scid.u64); } uc = tal(rstate, struct unupdated_channel); @@ -875,6 +913,15 @@ bool routing_add_channel_announcement(struct routing_state *rstate, routing_add_channel_update(rstate, take(private_updates[1]), 0, peer, false); + /* Now we can finish cleanup of gossip store, so there's no window where + * channel (or nodes) vanish. */ + if (oldchan) { + /* Mark private messages deleted, but don't tombstone the channel! */ + delete_chan_messages_from_store(rstate, oldchan); + free_chans_from_node(rstate, oldchan); + tal_free(oldchan); + } + return true; } @@ -1419,29 +1466,6 @@ static const struct node_id *get_channel_owner(struct routing_state *rstate, return NULL; } -void remove_channel_from_store(struct routing_state *rstate, - struct chan *chan) -{ - int update_type, announcment_type; - - if (is_chan_public(chan)) { - update_type = WIRE_CHANNEL_UPDATE; - announcment_type = WIRE_CHANNEL_ANNOUNCEMENT; - } else { - update_type = WIRE_GOSSIP_STORE_PRIVATE_UPDATE; - announcment_type = WIRE_GOSSIP_STORE_PRIVATE_CHANNEL; - } - gossip_store_mark_channel_deleted(rstate->gs, &chan->scid); - - /* If these aren't in the store, these are noops. */ - gossip_store_delete(rstate->gs, - &chan->bcast, announcment_type); - gossip_store_delete(rstate->gs, - &chan->half[0].bcast, update_type); - gossip_store_delete(rstate->gs, - &chan->half[1].bcast, update_type); -} - u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, struct peer *peer, struct short_channel_id *unknown_scid, From 353361a05c0db0b7ecb4a8c1e21ab3172d7011c8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:09:01 +0930 Subject: [PATCH 0829/1530] pytest: test to demonstrate BROKEN message on onchaind htlc close. We assumed that if the outgoing htlc has failed, we should have always failed the incoming one. This is not true, as this testcase demonstrates: ``` lightningd-2: 2022-05-25T04:38:31.449Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-onchaind-chan#2: Sending 1 missing htlc messages lightningd-2: 2022-05-25T04:38:31.449Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-chan#2: onchain_failed_our_htlc lightningd-2: 2022-05-25T04:38:31.449Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-chan#2: HTLC id 0 failonion = 0x55cb8bc045d8, failmsg = (nil), preimage = (nil) lightningd-2: 2022-05-25T04:38:31.449Z **BROKEN** 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-chan#2: HTLC id 0 already complete, but ->in not resolved! failonion = 206816a391d3a7666ddd5213914cbb68f5da1fc4a0937e729de5a1990c94d26312caa5f2778e8da0c6bdefc68dd1a3bc28b5b5650fc0bdb3c2247ecca94ed0bbb224c8448c2c71eb1656a8740cadb301bd1ee1c1e774a8fef817352f502e4217b11e93aa6877b88b37afab0e4d4e49ed0385be9ab9a1ab1ac0e3460e41cfafb30ed896cea96e346041919a6c524ce56c3e5f27c7cd78a36b6df221e90a1c6e048c72b4146a5a51885fb70649037fe7ace77a016ae3ec8aee97960d0e5f0582713f671df79d8dee11b82708b6d882ee5adbb328db1938e824110f57ead1b27410bc6f775c7bb4ae40c1768d77a166c9bfda8f634ba0ac4f8a9fe199894dd3754c5ce41c9694544c805ffc177517661f11221dd8dffac60ce1c8c5bf54cda8e5ea44d8ec6b, failmsg = (null), preimage = (null) lightningd-2: 2022-05-25T04:38:31.449Z **BROKEN** 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-chan#2: MISSING incoming fail for 0: failing incoming now lightningd-2: 2022-05-25T04:38:31.449Z **BROKEN** 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-chan#1: MISSED incoming fail for 0: failing now lightningd-2: 2022-05-25T04:38:31.449Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-chan#1: HTLC in 0 RCVD_ADD_ACK_REVOCATION->SENT_REMOVE_HTLC ``` Signed-off-by: Rusty Russell --- tests/test_pay.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index dc22f42e2eab..52e787184d0d 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5265,3 +5265,48 @@ def test_pay_bolt11_metadata(node_factory, bitcoind): l1.rpc.pay(inv) l2.daemon.wait_for_log("Unexpected payment_metadata {}".format(b'this is metadata'.hex())) + + +@pytest.mark.xfail(strict=True) +@pytest.mark.developer("needs to dev-disconnect") +def test_pay_middle_fail(node_factory, bitcoind, executor): + """Test the case where a HTLC is failed, but not on peer's side, then + we go onchain""" + # Set feerates the same so we don't have update_fee interfering. + # We want to disconnect on revoke-and-ack we send for failing htlc. + l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, + opts=[{'feerates': (1500,) * 4}, + {'feerates': (1500,) * 4}, + {'feerates': (1500,) * 4, + 'disconnect': ['-WIRE_REVOKE_AND_ACK*2']}]) + + chanid12 = only_one(l1.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] + chanid23 = only_one(l2.rpc.getpeer(l3.info['id'])['channels'])['short_channel_id'] + + # Make a failing payment. + route = [{'amount_msat': 1011, + 'id': l2.info['id'], + 'delay': 20, + 'channel': chanid12}, + {'amount_msat': 1000, + 'id': l3.info['id'], + 'delay': 10, + 'channel': chanid23}] + + # Start payment, it will fail. + l1.rpc.sendpay(route, payment_hash='00' * 32) + + wait_for(lambda: only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + + # After this (cltv is actually +11, and we give it 1 block grace) + # l2 will go onchain since HTLC is not resolved. + bitcoind.generate_block(12) + sync_blockheight(bitcoind, [l1, l2, l3]) + wait_for(lambda: only_one(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'])['state'] == 'AWAITING_UNILATERAL') + + # Three blocks and it will resolve the parent. + bitcoind.generate_block(3, wait_for_mempool=1) + + # And that will fail upstream + with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): + l1.rpc.waitsendpay('00' * 32) From 517828adb26fc1a42af0ee5d3b19e84857d27d8e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:10:01 +0930 Subject: [PATCH 0830/1530] lightningd: don't print nasty message when onchaind fails partially-failed HTLC 1. We set an outgoing htlc's `failonion` when we get a commitment_signed. 2. We don't transfer it to the corresponding incoming HTLC until we send commitment_signed and receive revoke_and_ack (meaning, outgoing htlc is completely dead). 3. If between these steps we go onchain, onchaind (after 3 blocks) tells us to fail the HTLC. 4. hout->failonion is set, but hout->hin has not been failed yet. We do a sanity check and print a nasty message, and fail it with WIRE_PERMANENT_CHANNEL_FAILURE instead of relaying the error. So handle this case explicitly. Signed-off-by: Rusty Russell --- lightningd/peer_htlcs.c | 12 ++++++++++++ tests/test_pay.py | 3 +-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 9abfa345f7df..f16b0e2eb80e 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1573,6 +1573,18 @@ void onchain_failed_our_htlc(const struct channel *channel, return; } + /* We can have hout->failonion (which gets set when we process the + * received commitment_signed), but not failed hin yet, because the peer + * hadn't revoke_and_acked our own commitment without that htlc. */ + if (hout->failonion && hout->in + && hout->in->badonion == 0 + && !hout->in->failonion + && !hout->in->preimage) { + log_debug(channel->log, "HTLC out %"PRIu64" can now fail HTLC upstream!", + htlc->id); + fail_in_htlc(hout->in, hout->failonion); + } + /* Don't fail twice (or if already succeeded)! */ if (hout->failonion || hout->failmsg || hout->preimage) { log_debug(channel->log, "HTLC id %"PRIu64" failonion = %p, failmsg = %p, preimage = %p", diff --git a/tests/test_pay.py b/tests/test_pay.py index 52e787184d0d..dbfd884540a9 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5267,7 +5267,6 @@ def test_pay_bolt11_metadata(node_factory, bitcoind): l2.daemon.wait_for_log("Unexpected payment_metadata {}".format(b'this is metadata'.hex())) -@pytest.mark.xfail(strict=True) @pytest.mark.developer("needs to dev-disconnect") def test_pay_middle_fail(node_factory, bitcoind, executor): """Test the case where a HTLC is failed, but not on peer's side, then @@ -5308,5 +5307,5 @@ def test_pay_middle_fail(node_factory, bitcoind, executor): bitcoind.generate_block(3, wait_for_mempool=1) # And that will fail upstream - with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): + with pytest.raises(RpcError, match=r'WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): l1.rpc.waitsendpay('00' * 32) From bcd050a6109f1c402e6c87704f31293e072fbd1d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:11:01 +0930 Subject: [PATCH 0831/1530] pytest: fix test_ping_timeout flake. We can hang up due to ping while waiting for channel to become active. But we don't even need an active channel for this test, so simplify. ``` ______________________________ test_ping_timeout _______________________________ [gw1] linux -- Python 3.7.13 /opt/hostedtoolcache/Python/3.7.13/x64/bin/python3 node_factory = @pytest.mark.developer("dev-disconnect required") def test_ping_timeout(node_factory): # Disconnects after this, but doesn't know it. l1_disconnects = ['xWIRE_PING'] l1, l2 = node_factory.line_graph(2, opts=[{'dev-no-reconnect': None, 'disconnect': l1_disconnects}, > {}]) tests/test_connection.py:3826: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-testing/pyln/testing/utils.py:1493: in line_graph self.join_nodes(nodes, fundchannel, fundamount, wait_for_announce, announce_channels) contrib/pyln-testing/pyln/testing/utils.py:1470: in join_nodes nodes[i].wait_channel_active(scids[i]) contrib/pyln-testing/pyln/testing/utils.py:1003: in wait_channel_active wait_for(lambda: self.is_channel_active(chanid)) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ success = . at 0x7f132c3ef830> timeout = 900 def wait_for(success, timeout=TIMEOUT): start_time = time.time() interval = 0.25 while not success(): time_left = start_time + timeout - time.time() if time_left <= 0: > raise ValueError("Timeout while waiting for {}", success) E ValueError: ('Timeout while waiting for {}', . at 0x7f132c3ef830>) ``` --- tests/test_connection.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index ae125f04586b..69df7bc4b9b5 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3841,12 +3841,15 @@ def test_ping_timeout(node_factory): # Disconnects after this, but doesn't know it. l1_disconnects = ['xWIRE_PING'] - l1, l2 = node_factory.line_graph(2, opts=[{'dev-no-reconnect': None, - 'disconnect': l1_disconnects}, - {}]) + l1, l2 = node_factory.get_nodes(2, opts=[{'dev-no-reconnect': None, + 'disconnect': l1_disconnects}, + {}]) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # Takes 15-45 seconds, then another to try second ping # Because of ping timer randomness we don't know which side hangs up first - wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected'] is False, timeout=45 + 45 + 5) + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == [], + timeout=45 + 45 + 5) wait_for(lambda: (l1.daemon.is_in_log('Last ping unreturned: hanging up') or l2.daemon.is_in_log('Last ping unreturned: hanging up'))) From a0e0dbf229e9b828e0b76d0f12ab62f9df2b4b36 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:12:01 +0930 Subject: [PATCH 0832/1530] pyln-testing: use files for stdout and stderr, not threads. Some flakes are caused by weird races in this code. Plus, if we get things to write straight to files, we might see things in there on post-mortem which happen after the python runner exits. It's a bit less efficient, but much simpler. Let's see if it helps! Some tests need a rework now, since we don't get a failure (except eventual timeout), but they're simpler. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 147 ++++++++------------- tests/test_misc.py | 59 +++++---- tests/test_plugin.py | 105 ++++++--------- tests/test_wallet.py | 47 +++---- 4 files changed, 148 insertions(+), 210 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index beea7a9662d7..62d650cb3b38 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -174,13 +174,20 @@ class TailableProc(object): tail the processes and react to their output. """ - def __init__(self, outputDir=None, verbose=True): + def __init__(self, outputDir, verbose=True): self.logs = [] - self.logs_cond = threading.Condition(threading.RLock()) self.env = os.environ.copy() - self.running = False self.proc = None self.outputDir = outputDir + if not os.path.exists(outputDir): + os.makedirs(outputDir) + # Create and open them. + self.stdout_filename = os.path.join(outputDir, "log") + self.stderr_filename = os.path.join(outputDir, "errlog") + self.stdout_write = open(self.stdout_filename, "wt") + self.stderr_write = open(self.stderr_filename, "wt") + self.stdout_read = open(self.stdout_filename, "rt") + self.stderr_read = open(self.stderr_filename, "rt") self.logsearch_start = 0 self.err_logs = [] self.prefix = "" @@ -192,29 +199,17 @@ def __init__(self, outputDir=None, verbose=True): # pass it to the log matcher and not print it to stdout). self.log_filter = lambda line: False - def start(self, stdin=None, stdout=None, stderr=None): + def start(self, stdin=None): """Start the underlying process and start monitoring it. """ logging.debug("Starting '%s'", " ".join(self.cmd_line)) self.proc = subprocess.Popen(self.cmd_line, stdin=stdin, - stdout=stdout if stdout else subprocess.PIPE, - stderr=stderr, + stdout=self.stdout_write, + stderr=self.stderr_write, env=self.env) - self.thread = threading.Thread(target=self.tail) - self.thread.daemon = True - self.thread.start() - self.running = True - - def save_log(self): - if self.outputDir: - logpath = os.path.join(self.outputDir, 'log') - with open(logpath, 'w') as f: - for l in self.logs: - f.write(l + '\n') def stop(self, timeout=10): - self.save_log() self.proc.terminate() # Now give it some time to react to the signal @@ -224,56 +219,32 @@ def stop(self, timeout=10): self.proc.kill() self.proc.wait() - self.thread.join() - return self.proc.returncode def kill(self): """Kill process without giving it warning.""" self.proc.kill() self.proc.wait() - self.thread.join() - - def tail(self): - """Tail the stdout of the process and remember it. - Stores the lines of output produced by the process in - self.logs and signals that a new line was read so that it can - be picked up by consumers. + def logs_catchup(self): + """Save the latest stdout / stderr contents; return true if we got anything. """ - for line in iter(self.proc.stdout.readline, ''): - if len(line) == 0: - break - - line = line.decode('UTF-8', 'replace').rstrip() - - if self.log_filter(line): - continue - - if self.verbose: - sys.stdout.write("{}: {}\n".format(self.prefix, line)) - - with self.logs_cond: - self.logs.append(line) - self.logs_cond.notifyAll() - - self.running = False - self.proc.stdout.close() - - if self.proc.stderr: - for line in iter(self.proc.stderr.readline, ''): - - if line is None or len(line) == 0: - break - - line = line.rstrip().decode('UTF-8', 'replace') - self.err_logs.append(line) - - self.proc.stderr.close() + new_stdout = self.stdout_read.readlines() + if self.verbose: + for line in new_stdout: + sys.stdout.write("{}: {}".format(self.prefix, line)) + self.logs += [l.rstrip() for l in new_stdout] + new_stderr = self.stderr_read.readlines() + if self.verbose: + for line in new_stderr: + sys.stderr.write("{}-stderr: {}".format(self.prefix, line)) + self.err_logs += [l.rstrip() for l in new_stderr] + return len(new_stdout) > 0 or len(new_stderr) > 0 def is_in_log(self, regex, start=0): """Look for `regex` in the logs.""" + self.logs_catchup() ex = re.compile(regex) for l in self.logs[start:]: if ex.search(l): @@ -286,6 +257,7 @@ def is_in_log(self, regex, start=0): def is_in_stderr(self, regex): """Look for `regex` in stderr.""" + self.logs_catchup() ex = re.compile(regex) for l in self.err_logs: if ex.search(l): @@ -311,31 +283,29 @@ def wait_for_logs(self, regexs, timeout=TIMEOUT): logging.debug("Waiting for {} in the logs".format(regexs)) exs = [re.compile(r) for r in regexs] start_time = time.time() - pos = self.logsearch_start while True: - if timeout is not None and time.time() > start_time + timeout: - print("Time-out: can't find {} in logs".format(exs)) - for r in exs: - if self.is_in_log(r): - print("({} was previously in logs!)".format(r)) - raise TimeoutError('Unable to find "{}" in logs.'.format(exs)) - - with self.logs_cond: - if pos >= len(self.logs): - if not self.running: - raise ValueError('Process died while waiting for logs') - self.logs_cond.wait(1) - continue - - for r in exs.copy(): - self.logsearch_start = pos + 1 - if r.search(self.logs[pos]): - logging.debug("Found '%s' in logs", r) - exs.remove(r) - break - if len(exs) == 0: - return self.logs[pos] - pos += 1 + if self.logsearch_start >= len(self.logs): + if not self.logs_catchup(): + time.sleep(0.25) + + if timeout is not None and time.time() > start_time + timeout: + print("Time-out: can't find {} in logs".format(exs)) + for r in exs: + if self.is_in_log(r): + print("({} was previously in logs!)".format(r)) + raise TimeoutError('Unable to find "{}" in logs.'.format(exs)) + continue + + line = self.logs[self.logsearch_start] + self.logsearch_start += 1 + for r in exs.copy(): + if r.search(line): + logging.debug("Found '%s' in logs", r) + exs.remove(r) + if len(exs) == 0: + return line + # Don't match same line with different regexs! + break def wait_for_log(self, regex, timeout=TIMEOUT): """Look for `regex` in the logs. @@ -620,10 +590,9 @@ def cmd_line(self): return self.cmd_prefix + [self.executable] + opts - def start(self, stdin=None, stdout=None, stderr=None, - wait_for_initialized=True): + def start(self, stdin=None, wait_for_initialized=True): self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport - TailableProc.start(self, stdin, stdout, stderr) + TailableProc.start(self, stdin) if wait_for_initialized: self.wait_for_log("Server started with public key") logging.info("LightningD started") @@ -852,8 +821,8 @@ def is_synced_with_bitcoin(self, info=None): info = self.rpc.getinfo() return 'warning_bitcoind_sync' not in info and 'warning_lightningd_sync' not in info - def start(self, wait_for_bitcoind_sync=True, stderr=None): - self.daemon.start(stderr=stderr) + def start(self, wait_for_bitcoind_sync=True): + self.daemon.start() # Cache `getinfo`, we'll be using it a lot self.info = self.rpc.getinfo() # This shortcut is sufficient for our simple tests. @@ -878,7 +847,6 @@ def stop(self, timeout=10): if self.rc is None: self.rc = self.daemon.stop() - self.daemon.save_log() self.daemon.cleanup() if self.rc != 0 and not self.may_fail: @@ -1417,12 +1385,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, if start: try: - # Capture stderr if we're failing - if expect_fail: - stderr = subprocess.PIPE - else: - stderr = None - node.start(wait_for_bitcoind_sync, stderr=stderr) + node.start(wait_for_bitcoind_sync) except Exception: if expect_fail: return node diff --git a/tests/test_misc.py b/tests/test_misc.py index 6fb65f32438c..d209778afa9d 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -109,13 +109,13 @@ def crash_bitcoincli(r): # Ignore BROKEN log message about blocksonly mode. l2 = node_factory.get_node(start=False, expect_fail=True, allow_broken_log=True) - with pytest.raises(ValueError): - l2.start(stderr=subprocess.PIPE) + l2.daemon.start(wait_for_initialized=False) + # Will exit with failure code. + assert l2.daemon.wait() == 1 assert l2.daemon.is_in_stderr(r".*deactivating transaction relay is not" - " supported.") is not None - # wait_for_log gets upset since daemon is not running. - wait_for(lambda: l2.daemon.is_in_log('deactivating transaction' - ' relay is not supported')) + " supported.") + assert l2.daemon.is_in_log('deactivating transaction' + ' relay is not supported') def test_bitcoin_ibd(node_factory, bitcoind): @@ -1207,8 +1207,10 @@ def test_rescan(node_factory, bitcoind): l1.daemon.opts['rescan'] = -500000 l1.stop() bitcoind.generate_block(4) - with pytest.raises(ValueError): - l1.start() + l1.daemon.start(wait_for_initialized=False) + # Will exit with failure code. + assert l1.daemon.wait() == 1 + assert l1.daemon.is_in_stderr(r"bitcoind has gone backwards from 500000 to 105 blocks!") # Restarting with future absolute blockheight is fine if we can find it. l1.daemon.opts['rescan'] = -105 @@ -1236,14 +1238,19 @@ def test_bitcoind_goes_backwards(node_factory, bitcoind): bitcoind.start() # Will simply refuse to start. - with pytest.raises(ValueError): - l1.start() + l1.daemon.start(wait_for_initialized=False) + # Will exit with failure code. + assert l1.daemon.wait() == 1 + assert l1.daemon.is_in_stderr('bitcoind has gone backwards') # Nor will it start with if we ask for a reindex of fewer blocks. l1.daemon.opts['rescan'] = 3 - with pytest.raises(ValueError): - l1.start() + # Will simply refuse to start. + l1.daemon.start(wait_for_initialized=False) + # Will exit with failure code. + assert l1.daemon.wait() == 1 + assert l1.daemon.is_in_stderr('bitcoind has gone backwards') # This will force it, however. l1.daemon.opts['rescan'] = -100 @@ -1690,7 +1697,7 @@ def test_newaddr(node_factory, chainparams): assert both['bech32'].startswith(chainparams['bip173_prefix']) -def test_bitcoind_fail_first(node_factory, bitcoind, executor): +def test_bitcoind_fail_first(node_factory, bitcoind): """Make sure we handle spurious bitcoin-cli failures during startup See [#2687](https://github.com/ElementsProject/lightning/issues/2687) for @@ -1699,7 +1706,9 @@ def test_bitcoind_fail_first(node_factory, bitcoind, executor): """ # Do not start the lightning node since we need to instrument bitcoind # first. - l1 = node_factory.get_node(start=False) + l1 = node_factory.get_node(start=False, + allow_broken_log=True, + may_fail=True) # Instrument bitcoind to fail some queries first. def mock_fail(*args): @@ -1708,22 +1717,17 @@ def mock_fail(*args): l1.daemon.rpcproxy.mock_rpc('getblockhash', mock_fail) l1.daemon.rpcproxy.mock_rpc('estimatesmartfee', mock_fail) - f = executor.submit(l1.start) - - wait_for(lambda: l1.daemon.running) - # Make sure it fails on the first `getblock` call (need to use `is_in_log` - # since the `wait_for_log` in `start` sets the offset) - wait_for(lambda: l1.daemon.is_in_log( - r'getblockhash [a-z0-9]* exited with status 1')) - wait_for(lambda: l1.daemon.is_in_log( - r'Unable to estimate opening fees')) + l1.daemon.start(wait_for_initialized=False) + l1.daemon.wait_for_logs([r'getblockhash [a-z0-9]* exited with status 1', + r'Unable to estimate opening fees', + r'BROKEN.*we have been retrying command for --bitcoin-retry-timeout=60 seconds']) + # Will exit with failure code. + assert l1.daemon.wait() == 1 # Now unset the mock, so calls go through again l1.daemon.rpcproxy.mock_rpc('getblockhash', None) l1.daemon.rpcproxy.mock_rpc('estimatesmartfee', None) - f.result() - @pytest.mark.developer("needs --dev-force-bip32-seed") @unittest.skipIf(TEST_NETWORK != 'regtest', "Addresses are network specific") @@ -2077,8 +2081,9 @@ def test_new_node_is_mainnet(node_factory): del l1.daemon.opts['network'] # Wrong chain, will fail to start, but that's OK. - with pytest.raises(ValueError): - l1.start() + l1.daemon.start(wait_for_initialized=False) + # Will exit with failure code. + assert l1.daemon.wait() == 1 # Should create these assert os.path.isfile(os.path.join(netdir, "hsm_secret")) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index c18be94a5540..65d6763551c3 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -94,7 +94,7 @@ def test_option_types(node_factory): }, expect_fail=True, may_fail=True) # the node should fail to start, and we get a stderr msg - assert not n.daemon.running + assert n.daemon.wait() == 1 wait_for(lambda: n.daemon.is_in_stderr('bool_opt: ! does not parse as type bool')) # What happens if we give it a bad int-option? @@ -106,7 +106,7 @@ def test_option_types(node_factory): }, may_fail=True, expect_fail=True) # the node should fail to start, and we get a stderr msg - assert not n.daemon.running + assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr('--int_opt: notok does not parse as type int') # Flag opts shouldn't allow any input @@ -119,7 +119,7 @@ def test_option_types(node_factory): }, may_fail=True, expect_fail=True) # the node should fail to start, and we get a stderr msg - assert not n.daemon.running + assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr("--flag_opt: doesn't allow an argument") n = node_factory.get_node(options={ @@ -1500,9 +1500,10 @@ def test_libplugin(node_factory): l1.stop() l1.daemon.opts["name-deprecated"] = "test_opt" - # This actually dies while waiting for the logs. - with pytest.raises(ValueError): - l1.start() + l1.daemon.start(wait_for_initialized=False) + # Will exit with failure code. + assert l1.daemon.wait() == 1 + assert l1.daemon.is_in_stderr(r"name-deprecated: deprecated option") del l1.daemon.opts["name-deprecated"] l1.start() @@ -1646,24 +1647,20 @@ def test_bitcoin_backend(node_factory, bitcoind): # We don't start if we haven't all the required methods registered. plugin = os.path.join(os.getcwd(), "tests/plugins/bitcoin/part1.py") l1.daemon.opts["plugin"] = plugin - try: - l1.daemon.start() - except ValueError: - assert l1.daemon.is_in_log("Missing a Bitcoin plugin command") - # Now we should start if all the commands are registered, even if they - # are registered by two distincts plugins. - del l1.daemon.opts["plugin"] - l1.daemon.opts["plugin-dir"] = os.path.join(os.getcwd(), - "tests/plugins/bitcoin/") - try: - l1.daemon.start() - except ValueError: - msg = "All Bitcoin plugin commands registered" - assert l1.daemon.is_in_log(msg) - else: - raise Exception("We registered all commands but couldn't start!") - else: - raise Exception("We could start without all commands registered !!") + l1.daemon.start(wait_for_initialized=False) + l1.daemon.wait_for_log("Missing a Bitcoin plugin command") + # Will exit with failure code. + assert l1.daemon.wait() == 1 + assert l1.daemon.is_in_stderr(r"Could not access the plugin for sendrawtransaction") + # Now we should start if all the commands are registered, even if they + # are registered by two distincts plugins. + del l1.daemon.opts["plugin"] + l1.daemon.opts["plugin-dir"] = os.path.join(os.getcwd(), + "tests/plugins/bitcoin/") + # (it fails when it tries to use them, so startup fails) + l1.daemon.start(wait_for_initialized=False) + l1.daemon.wait_for_log("All Bitcoin plugin commands registered") + assert l1.daemon.wait() == 1 # But restarting with just bcli is ok del l1.daemon.opts["plugin-dir"] @@ -2074,73 +2071,55 @@ def test_important_plugin(node_factory): n = node_factory.get_node(options={"important-plugin": os.path.join(pluginsdir, "nonexistent")}, may_fail=True, expect_fail=True, allow_broken_log=True, start=False) - n.daemon.start(wait_for_initialized=False, stderr=subprocess.PIPE) - wait_for(lambda: not n.daemon.running) - + n.daemon.start(wait_for_initialized=False) + # Will exit with failure code. + assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr(r"error starting plugin '.*nonexistent'") - # We use a log file, since our wait_for_log is unreliable when the - # daemon actually dies. - def get_logfile_match(logpath, regex): - if not os.path.exists(logpath): - return None - with open(logpath, 'r') as f: - for line in f.readlines(): - m = re.search(regex, line) - if m is not None: - return m - return None - - logpath = os.path.join(n.daemon.lightning_dir, TEST_NETWORK, 'logfile') - n.daemon.opts['log-file'] = 'logfile' - # Check we exit if the important plugin dies. n.daemon.opts['important-plugin'] = os.path.join(pluginsdir, "fail_by_itself.py") n.daemon.start(wait_for_initialized=False) - wait_for(lambda: not n.daemon.running) - - assert get_logfile_match(logpath, - r'fail_by_itself.py: Plugin marked as important, shutting down lightningd') - os.remove(logpath) + # Will exit with failure code. + assert n.daemon.wait() == 1 + n.daemon.wait_for_log(r'fail_by_itself.py: Plugin marked as important, shutting down lightningd') # Check if the important plugin is disabled, we run as normal. n.daemon.opts['disable-plugin'] = "fail_by_itself.py" - del n.daemon.opts['log-file'] n.daemon.start() # Make sure we can call into a plugin RPC (this is from `bcli`) even # if fail_by_itself.py is disabled. n.rpc.call("estimatefees", {}) - # Make sure we are still running. - assert n.daemon.running n.stop() # Check if an important plugin dies later, we fail. del n.daemon.opts['disable-plugin'] - n.daemon.opts['log-file'] = 'logfile' n.daemon.opts['important-plugin'] = os.path.join(pluginsdir, "suicidal_plugin.py") - n.daemon.start(wait_for_initialized=False) - wait_for(lambda: get_logfile_match(logpath, "Server started with public key")) + n.start() with pytest.raises(RpcError): n.rpc.call("die", {}) - wait_for(lambda: not n.daemon.running) - assert get_logfile_match(logpath, 'suicidal_plugin.py: Plugin marked as important, shutting down lightningd') - os.remove(logpath) + # Should exit with exitcode 1 + n.daemon.wait_for_log('suicidal_plugin.py: Plugin marked as important, shutting down lightningd') + assert n.daemon.wait() == 1 + n.stop() # Check that if a builtin plugin dies, we fail. - n.daemon.start(wait_for_initialized=False) - - wait_for(lambda: get_logfile_match(logpath, r'.*started\(([0-9]*)\).*plugins/pay')) - pidstr = get_logfile_match(logpath, r'.*started\(([0-9]*)\).*plugins/pay').group(1) + start = n.daemon.logsearch_start + n.start() + # Reset logsearch_start, since this will predate message that start() looks for. + n.daemon.logsearch_start = start + line = n.daemon.wait_for_log(r'.*started\([0-9]*\).*plugins/pay') + pidstr = re.search(r'.*started\(([0-9]*)\).*plugins/pay', line).group(1) # Kill pay. os.kill(int(pidstr), signal.SIGKILL) - wait_for(lambda: not n.daemon.running) - - assert get_logfile_match(logpath, 'pay: Plugin marked as important, shutting down lightningd') + n.daemon.wait_for_log('pay: Plugin marked as important, shutting down lightningd') + # Should exit with exitcode 1 + assert n.daemon.wait() == 1 + n.stop() @pytest.mark.developer("tests developer-only option.") diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 00985245bf92..893d2f6e0c0b 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1038,12 +1038,11 @@ def test_hsm_secret_encryption(node_factory): # Test we cannot restore the same wallet with another password l1.daemon.opts.update({"encrypted-hsm": None}) - l1.daemon.start(stdin=slave_fd, stderr=subprocess.STDOUT, - wait_for_initialized=False) + l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) l1.daemon.wait_for_log(r'Enter hsm_secret password') write_all(master_fd, password[2:].encode("utf-8")) assert(l1.daemon.proc.wait(WAIT_TIMEOUT) == HSM_BAD_PASSWORD) - assert(l1.daemon.is_in_log("Wrong password for encrypted hsm_secret.")) + assert(l1.daemon.is_in_stderr("Wrong password for encrypted hsm_secret.")) # Not sure why this helps, but seems to reduce flakiness where # tail() thread in testing/utils.py gets 'ValueError: readline of @@ -1069,9 +1068,9 @@ def test_hsm_secret_encryption(node_factory): class HsmTool(TailableProc): """Helper for testing the hsmtool as a subprocess""" - def __init__(self, *args): + def __init__(self, directory, *args): self.prefix = "hsmtool" - TailableProc.__init__(self) + TailableProc.__init__(self, os.path.join(directory, "hsmtool")) assert hasattr(self, "env") self.cmd_line = ["tools/hsmtool", *args] @@ -1098,9 +1097,8 @@ def test_hsmtool_secret_decryption(node_factory): # We can't use a wrong password ! master_fd, slave_fd = os.openpty() - hsmtool = HsmTool("decrypt", hsm_path) - hsmtool.start(stdin=slave_fd, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + hsmtool = HsmTool(node_factory.directory, "decrypt", hsm_path) + hsmtool.start(stdin=slave_fd) hsmtool.wait_for_log(r"Enter hsm_secret password:") write_all(master_fd, "A wrong pass\n\n".encode("utf-8")) hsmtool.proc.wait(WAIT_TIMEOUT) @@ -1108,8 +1106,7 @@ def test_hsmtool_secret_decryption(node_factory): # Decrypt it with hsmtool master_fd, slave_fd = os.openpty() - hsmtool.start(stdin=slave_fd, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + hsmtool.start(stdin=slave_fd) hsmtool.wait_for_log(r"Enter hsm_secret password:") write_all(master_fd, password.encode("utf-8")) assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 @@ -1122,9 +1119,8 @@ def test_hsmtool_secret_decryption(node_factory): # Test we can encrypt it offline master_fd, slave_fd = os.openpty() - hsmtool = HsmTool("encrypt", hsm_path) - hsmtool.start(stdin=slave_fd, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + hsmtool = HsmTool(node_factory.directory, "encrypt", hsm_path) + hsmtool.start(stdin=slave_fd) hsmtool.wait_for_log(r"Enter hsm_secret password:") write_all(master_fd, password.encode("utf-8")) hsmtool.wait_for_log(r"Confirm hsm_secret password:") @@ -1137,7 +1133,7 @@ def test_hsmtool_secret_decryption(node_factory): l1.daemon.opts.update({"encrypted-hsm": None}) master_fd, slave_fd = os.openpty() - l1.daemon.start(stdin=slave_fd, stderr=subprocess.STDOUT, + l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) l1.daemon.wait_for_log(r'The hsm_secret is encrypted') @@ -1149,9 +1145,8 @@ def test_hsmtool_secret_decryption(node_factory): # And finally test that we can also decrypt if encrypted with hsmtool master_fd, slave_fd = os.openpty() - hsmtool = HsmTool("decrypt", hsm_path) - hsmtool.start(stdin=slave_fd, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + hsmtool = HsmTool(node_factory.directory, "decrypt", hsm_path) + hsmtool.start(stdin=slave_fd) hsmtool.wait_for_log(r"Enter hsm_secret password:") write_all(master_fd, password.encode("utf-8")) assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 @@ -1161,9 +1156,8 @@ def test_hsmtool_secret_decryption(node_factory): # We can roundtrip encryption and decryption using a password provided # through stdin. - hsmtool = HsmTool("encrypt", hsm_path) - hsmtool.start(stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + hsmtool = HsmTool(node_factory.directory, "encrypt", hsm_path) + hsmtool.start(stdin=subprocess.PIPE) hsmtool.proc.stdin.write(password.encode("utf-8")) hsmtool.proc.stdin.write(password.encode("utf-8")) hsmtool.proc.stdin.flush() @@ -1171,9 +1165,8 @@ def test_hsmtool_secret_decryption(node_factory): assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 master_fd, slave_fd = os.openpty() - hsmtool = HsmTool("decrypt", hsm_path) - hsmtool.start(stdin=slave_fd, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + hsmtool = HsmTool(node_factory.directory, "decrypt", hsm_path) + hsmtool.start(stdin=slave_fd) hsmtool.wait_for_log("Enter hsm_secret password:") write_all(master_fd, password.encode("utf-8")) hsmtool.wait_for_log("Successfully decrypted") @@ -1232,19 +1225,17 @@ def test_hsmtool_generatehsm(node_factory): hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret") - hsmtool = HsmTool("generatehsm", hsm_path) + hsmtool = HsmTool(node_factory.directory, "generatehsm", hsm_path) # You cannot re-generate an already existing hsm_secret master_fd, slave_fd = os.openpty() - hsmtool.start(stdin=slave_fd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + hsmtool.start(stdin=slave_fd) assert hsmtool.proc.wait(WAIT_TIMEOUT) == 2 os.remove(hsm_path) # We can generate a valid hsm_secret from a wordlist and a "passphrase" master_fd, slave_fd = os.openpty() - hsmtool.start(stdin=slave_fd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + hsmtool.start(stdin=slave_fd) hsmtool.wait_for_log(r"Select your language:") write_all(master_fd, "0\n".encode("utf-8")) hsmtool.wait_for_log(r"Introduce your BIP39 word list") From d2952576cd3ed95c3b2157ece0ae597add200fbb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:13:01 +0930 Subject: [PATCH 0833/1530] pyln-testing: restore proper streaming behaviour for lightningd. Get it to log direct to stdout, so we see what's happening *as it happens* rather than as we read it. We could restore the thread we were using before, but that added more problems than it solved. This means that we need the hsm password prompts in the log though. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 34 +++++++++++++++------- lightningd/options.c | 23 ++++++++++----- tests/test_misc.py | 10 +++++-- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 62d650cb3b38..590097b0f55f 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -199,15 +199,24 @@ def __init__(self, outputDir, verbose=True): # pass it to the log matcher and not print it to stdout). self.log_filter = lambda line: False - def start(self, stdin=None): - """Start the underlying process and start monitoring it. + def start(self, stdin=None, stdout_redir=True): + """Start the underlying process and start monitoring it. If + stdout_redir is false, you have to make sure logs go into + outputDir/log + """ logging.debug("Starting '%s'", " ".join(self.cmd_line)) - self.proc = subprocess.Popen(self.cmd_line, - stdin=stdin, - stdout=self.stdout_write, - stderr=self.stderr_write, - env=self.env) + if stdout_redir: + self.proc = subprocess.Popen(self.cmd_line, + stdin=stdin, + stdout=self.stdout_write, + stderr=self.stderr_write, + env=self.env) + else: + self.proc = subprocess.Popen(self.cmd_line, + stdin=stdin, + stderr=self.stderr_write, + env=self.env) def stop(self, timeout=10): self.proc.terminate() @@ -529,7 +538,8 @@ def getnewaddress(self): class LightningD(TailableProc): def __init__(self, lightning_dir, bitcoindproxy, port=9735, random_hsm=False, node_id=0): - TailableProc.__init__(self, lightning_dir) + # We handle our own version of verbose, below. + TailableProc.__init__(self, lightning_dir, verbose=False) self.executable = 'lightningd' self.lightning_dir = lightning_dir self.port = port @@ -568,6 +578,10 @@ def __init__(self, lightning_dir, bitcoindproxy, port=9735, random_hsm=False, no self.opts['dev-fast-gossip'] = None self.opts['dev-bitcoind-poll'] = 1 self.prefix = 'lightningd-%d' % (node_id) + # Log to stdout so we see it in failure cases, and log file for TailableProc. + self.opts['log-file'] = ['-', os.path.join(lightning_dir, "log")] + # In case you want specific ordering! + self.early_opts = [] def cleanup(self): # To force blackhole to exit, disconnect file must be truncated! @@ -588,11 +602,11 @@ def cmd_line(self): else: opts.append("--{}={}".format(k, v)) - return self.cmd_prefix + [self.executable] + opts + return self.cmd_prefix + [self.executable] + self.early_opts + opts def start(self, stdin=None, wait_for_initialized=True): self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport - TailableProc.start(self, stdin) + TailableProc.start(self, stdin, stdout_redir=False) if wait_for_initialized: self.wait_for_log("Server started with public key") logging.info("LightningD started") diff --git a/lightningd/options.c b/lightningd/options.c index e7e2b9cc47de..2af57ac09f7c 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -493,6 +493,16 @@ static char *opt_important_plugin(const char *arg, struct lightningd *ld) return NULL; } +/* Test code looks in logs, so we print prompts to log as well as stdout */ +static void prompt(struct lightningd *ld, const char *str) +{ + printf("%s\n", str); + log_debug(ld->log, "PROMPT: %s", str); + /* If we don't flush we might end up being buffered and we might seem + * to hang while we wait for the password. */ + fflush(stdout); +} + /* Prompt the user to enter a password, from which will be derived the key used * for `hsm_secret` encryption. * The algorithm used to derive the key is Argon2(id), to which libsodium @@ -509,18 +519,15 @@ static char *opt_set_hsm_password(struct lightningd *ld) return tal_fmt(NULL, "Could not access 'hsm_secret': %s", strerror(errno)); - 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"); - /* If we don't flush we might end up being buffered and we might seem - * to hang while we wait for the password. */ - fflush(stdout); + prompt(ld, "The hsm_secret is encrypted with a password. In order to " + "decrypt it and start the node you must provide the password."); + prompt(ld, "Enter hsm_secret password:"); passwd = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); if (!passwd) return err_msg; if (!is_encrypted) { - printf("Confirm hsm_secret password:\n"); + prompt(ld, "Confirm hsm_secret password:"); fflush(stdout); passwd_confirmation = read_stdin_pass_with_exit_code(&err_msg, &opt_exitcode); if (!passwd_confirmation) @@ -532,7 +539,7 @@ static char *opt_set_hsm_password(struct lightningd *ld) } free(passwd_confirmation); } - printf("\n"); + prompt(ld, ""); ld->config.keypass = tal(NULL, struct secret); diff --git a/tests/test_misc.py b/tests/test_misc.py index d209778afa9d..e6bf96c8eda9 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1007,8 +1007,11 @@ def test_daemon_option(node_factory): l1.stop() os.unlink(l1.rpc.socket_path) + # Stop it from logging to stdout! logfname = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "log-daemon") - subprocess.run(l1.daemon.cmd_line + ['--daemon', '--log-file={}'.format(logfname)], env=l1.daemon.env, + l1.daemon.opts['log-file'] = logfname + l1.daemon.opts['daemon'] = None + subprocess.run(l1.daemon.cmd_line, env=l1.daemon.env, check=True) # Test some known output (wait for rpc to be ready) @@ -2399,7 +2402,10 @@ def test_version_reexec(node_factory, bitcoind): def test_notimestamp_logging(node_factory): - l1 = node_factory.get_node(options={'log-timestamps': False}) + l1 = node_factory.get_node(start=False) + # Make sure this is specified *before* other options! + l1.daemon.early_opts = ['--log-timestamps=false'] + l1.start() assert l1.daemon.logs[0].startswith("DEBUG") assert l1.rpc.listconfigs()['log-timestamps'] is False From 61d8eb5fa82f450274319367d10ec35f5c14666d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:14:01 +0930 Subject: [PATCH 0834/1530] pyln-testing: increase default daemon.wait() timeout. Valgrind in CI is slow: ``` def test_bitcoin_failure(node_factory, bitcoind): ... # Ignore BROKEN log message about blocksonly mode. l2 = node_factory.get_node(start=False, expect_fail=True, allow_broken_log=True) l2.daemon.start(wait_for_initialized=False) # Will exit with failure code. > assert l2.daemon.wait() == 1 tests/test_misc.py:114: ``` Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 590097b0f55f..2671f4c38973 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -611,7 +611,7 @@ def start(self, stdin=None, wait_for_initialized=True): self.wait_for_log("Server started with public key") logging.info("LightningD started") - def wait(self, timeout=10): + def wait(self, timeout=TIMEOUT): """Wait for the daemon to stop for up to timeout seconds Returns the returncode of the process, None if the process did From 6221ac621d505afa817112dac2e095b16566925c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:15:01 +0930 Subject: [PATCH 0835/1530] pytest: fix timeout in test_option_types We were waiting for the start to timeout waiting for the "public key" message. Instead, start manually. Before, this took (with TIMEOUT=30) 97 seconds, after 8 seconds. Signed-off-by: Rusty Russell --- tests/test_plugin.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 65d6763551c3..78cd3fdb8891 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -91,9 +91,10 @@ def test_option_types(node_factory): 'str_opt': 'ok', 'int_opt': 22, 'bool_opt': '!', - }, expect_fail=True, may_fail=True) + }, may_fail=True, start=False) - # the node should fail to start, and we get a stderr msg + # the node should fail after start, and we get a stderr msg + n.daemon.start(wait_for_initialized=False) assert n.daemon.wait() == 1 wait_for(lambda: n.daemon.is_in_stderr('bool_opt: ! does not parse as type bool')) @@ -103,9 +104,10 @@ def test_option_types(node_factory): 'str_opt': 'ok', 'int_opt': 'notok', 'bool_opt': 1, - }, may_fail=True, expect_fail=True) + }, may_fail=True, start=False) - # the node should fail to start, and we get a stderr msg + # the node should fail after start, and we get a stderr msg + n.daemon.start(wait_for_initialized=False) assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr('--int_opt: notok does not parse as type int') @@ -116,9 +118,10 @@ def test_option_types(node_factory): 'int_opt': 11, 'bool_opt': 1, 'flag_opt': True, - }, may_fail=True, expect_fail=True) + }, may_fail=True, start=False) - # the node should fail to start, and we get a stderr msg + # the node should fail after start, and we get a stderr msg + n.daemon.start(wait_for_initialized=False) assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr("--flag_opt: doesn't allow an argument") From b4820d670678f9bc48d93ea43793122368fb1a95 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:16:01 +0930 Subject: [PATCH 0836/1530] lightningd: don't run off end of buffer if db_hook returns nonsense. It shouldn't return nonsense, but it did, and we segfaulted. Signed-off-by: Rusty Russell --- lightningd/plugin_hook.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index 4d9d82b43877..ed9f50c013b9 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -303,7 +303,10 @@ static void db_hook_response(const char *buffer, const jsmntok_t *toks, resulttok = json_get_member(buffer, toks, "result"); if (!resulttok) fatal("Plugin '%s' returned an invalid response to the " - "db_write hook: %s", dwh_req->plugin->cmd, buffer); + "db_write hook: %.*s", + dwh_req->plugin->cmd, + json_tok_full_len(toks), + json_tok_full(buffer, toks)); /* We expect result: { 'result' : 'continue' }. * Anything else we abort. @@ -311,14 +314,16 @@ static void db_hook_response(const char *buffer, const jsmntok_t *toks, resulttok = json_get_member(buffer, resulttok, "result"); if (resulttok) { if (!json_tok_streq(buffer, resulttok, "continue")) - fatal("Plugin '%s' returned failed db_write: %s.", + fatal("Plugin '%s' returned failed db_write: %.*s.", dwh_req->plugin->cmd, - buffer); + json_tok_full_len(toks), + json_tok_full(buffer, toks)); } else fatal("Plugin '%s' returned an invalid result to the db_write " - "hook: %s", + "hook: %.*s", dwh_req->plugin->cmd, - buffer); + json_tok_full_len(toks), + json_tok_full(buffer, toks)); assert((*dwh_req->num_hooks) != 0); --(*dwh_req->num_hooks); From fcff21fae56c68119a27bce41c159b32bf7b8c86 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:17:01 +0930 Subject: [PATCH 0837/1530] pytest: allow more time for test_waitblockheight !DEVELOPER. It actually timed out with the default 60 seconds, just before it saw the block: ``` 2022-06-07T02:16:05.2557049Z bitcoind.generate_block(1) 2022-06-07T02:16:05.2557300Z sync_blockheight(bitcoind, [node]) 2022-06-07T02:16:05.2557594Z fut1.result(5) 2022-06-07T02:16:05.2557912Z assert not fut2.done() 2022-06-07T02:16:05.2558121Z 2022-06-07T02:16:05.2558370Z # Trigger two blocks. 2022-06-07T02:16:05.2558689Z bitcoind.generate_block(1) 2022-06-07T02:16:05.2558941Z sync_blockheight(bitcoind, [node]) 2022-06-07T02:16:05.2559219Z > fut2.result(5) 2022-06-07T02:16:05.2559350Z 2022-06-07T02:16:05.2559508Z tests/test_misc.py:2138: ... 2022-06-07T02:16:05.2586947Z elif "error" in resp: 2022-06-07T02:16:05.2587398Z > raise RpcError(method, payload, resp['error']) 2022-06-07T02:16:05.2588026Z E pyln.client.lightning.RpcError: RPC call failed: method: waitblockheight, payload: {'blockheight': 103}, error: {'code': 2000, 'message': 'Timed out.'} 2022-06-07T02:16:05.2588325Z 2022-06-07T02:16:05.2588563Z contrib/pyln-client/pyln/client/lightning.py:387: RpcError ``` Signed-off-by: Rusty Russell --- tests/test_misc.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index e6bf96c8eda9..5a86cb8359a7 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2135,9 +2135,16 @@ def test_waitblockheight(node_factory, executor, bitcoind): node.rpc.waitblockheight(blockheight - 1) node.rpc.waitblockheight(blockheight) + # Developer mode polls bitcoind every second, so 60 seconds is plenty. + # But non-developer mode polls every 30 seconds, so try 120. + if DEVELOPER: + time = 60 + else: + time = 120 + # Should not succeed yet. - fut2 = executor.submit(node.rpc.waitblockheight, blockheight + 2) - fut1 = executor.submit(node.rpc.waitblockheight, blockheight + 1) + fut2 = executor.submit(node.rpc.waitblockheight, blockheight + 2, time) + fut1 = executor.submit(node.rpc.waitblockheight, blockheight + 1, time) assert not fut1.done() assert not fut2.done() From 70b091d9f67d68e1cdccd53eed041fba16bb2d68 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:18:01 +0930 Subject: [PATCH 0838/1530] lightningd: fix transient leak report when openingd shutting down. ``` > raiseValueError(str(errors)) E ValueError: E Node errors: E Global errors: E - Node /tmp/ltests-x5sfpiwp/test_openchannel_hook_chaining_1/lightning-2/ has memory leaks: [ E { E "backtrace": [ E "ccan/ccan/tal/tal.c:442 (tal_alloc_)", E "ccan/ccan/io/io.c:91 (io_new_conn_)", E "lightningd/subd.c:773 (new_subd)", E "lightningd/subd.c:827 (new_channel_subd_)", E "lightningd/opening_control.c:870 (peer_start_openingd)", E "lightningd/peer_control.c:1307 (peer_active)", E "lightningd/connect_control.c:457 (connectd_msg)", E "lightningd/subd.c:556 (sd_msg_read)", E "lightningd/subd.c:357 (read_fds)", E "ccan/ccan/io/io.c:59 (next_plan)", E "ccan/ccan/io/io.c:407 (do_plan)", E "ccan/ccan/io/io.c:417 (io_ready)", E "ccan/ccan/io/poll.c:453 (io_loop)", E "lightningd/io_loop_with_timers.c:22 (io_loop_with_timers)", E "lightningd/lightningd.c:1181 (main)", E "../csu/libc-start.c:308 (__libc_start_main)" E ], E "label": "ccan/ccan/io/io.c:91:struct io_conn", E "parents": [ E "lightningd/lightningd.c:107:struct lightningd" E ], E "value": "0x2b5a898" E } E ] ``` Signed-off-by: Rusty Russell --- lightningd/opening_control.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 17bffd21dd5a..5eda78580754 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -675,7 +676,8 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS) } else upfront_shutdown_script_wallet_index = NULL; - + /* In case peer goes away right now, mark openingd notleak() */ + notleak(openingd); subd_send_msg(openingd, take(towire_openingd_got_offer_reply(NULL, errmsg, our_upfront_shutdown_script, From 3958d59e3995c46c8ce4cc66d97c066aaf9d134e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:19:01 +0930 Subject: [PATCH 0839/1530] pytest: fix test_channel_lease_post_expiry flake. Make sure bitcoind gets tx before mining blocks! ``` # l1<->l2 mutual close should work chan = l1.get_channel_scid(l2) l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l1.rpc.close(chan) l2.daemon.wait_for_log('State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE') bitcoind.generate_block(2) sync_blockheight(bitcoind, [l1, l2]) > l1.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE') tests/test_closing.py:851: ``` Signed-off-by: Rusty Russell --- tests/test_closing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 8a763d1b17c3..2bd8165e450b 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -846,7 +846,7 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): l1.rpc.close(chan) l2.daemon.wait_for_log('State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE') - bitcoind.generate_block(2) + bitcoind.generate_block(2, wait_for_mempool=1) sync_blockheight(bitcoind, [l1, l2]) l1.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE') l2.daemon.wait_for_log('Resolved FUNDING_TRANSACTION/FUNDING_OUTPUT by MUTUAL_CLOSE') From 9137ea262beddfa193393f6cfcc82ff1b401f401 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:20:01 +0930 Subject: [PATCH 0840/1530] pytest: don't assume that join_nodes needs only check ends. I saw another "only_one()" fail on alias checking: it's not entirely clear to me with the more aggressive sending of own gossip, that we necessarily process in order, so we might not have actually seen all channels just because we saw the farthest one. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 2671f4c38973..57e51beacf78 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1452,15 +1452,15 @@ def join_nodes(self, nodes, fundchannel=True, fundamount=FUNDAMOUNT, wait_for_an bitcoind.generate_block(5) - # Make sure everyone sees all channels: we can cheat and - # simply check the ends (since it's a line). - nodes[0].wait_channel_active(scids[-1]) - nodes[-1].wait_channel_active(scids[0]) + # Make sure everyone sees all channels, all other nodes + for n in nodes: + for scid in scids: + n.wait_channel_active(scid) - # Make sure we have all node announcements, too (just check ends) + # Make sure we have all node announcements, too for n in nodes: - for end in (nodes[0], nodes[-1]): - wait_for(lambda: 'alias' in only_one(end.rpc.listnodes(n.info['id'])['nodes'])) + for n2 in nodes: + wait_for(lambda: 'alias' in only_one(n.rpc.listnodes(n2.info['id'])['nodes'])) def line_graph(self, num_nodes, fundchannel=True, fundamount=FUNDAMOUNT, wait_for_announce=False, opts=None, announce_channels=True): """ Create nodes, connect them and optionally fund channels. From 3f98cf3fceab2fa85ec90180fe13ce1568e0bde9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:21:01 +0930 Subject: [PATCH 0841/1530] lightningd: track weird CI crash in test_important_plugin Looks like we woke one of the startup io_loops early, and thus we thought we'd finished connectd_activate and we hadn't. This caused us to use an uninitialized ld->announceable array, and finally caused an assert fail in the main loop. Make *every* loop assert that it was exited for the correct reason, so if it happens again, we can maybe figure out what part of the code to look at. ``` lightningd: lightningd/lightningd.c:1186: main: Assertion `io_loop_ret == ld' failed. lightningd: FATAL SIGNAL 6 (version 4df66fa) ... ------------------------------- Valgrind errors -------------------------------- Valgrind error file: valgrind-errors.895509 ==895509== Conditional jump or move depends on uninitialised value(s) ==895509== at 0x22C58E: to_tal_hdr_or_null (tal.c:184) ==895509== by 0x22D531: tal_bytelen (tal.c:637) ==895509== by 0x1F10B6: towire_gossipd_init (gossipd_wiregen.c:100) ==895509== by 0x13AC6E: gossip_init (gossip_control.c:254) ==895509== by 0x1497EC: main (lightningd.c:1090) ==895509== ``` Signed-off-by: Rusty Russell --- lightningd/bitcoind.c | 6 +++++- lightningd/chaintopology.c | 6 +++++- lightningd/connect_control.c | 12 ++++++++++-- lightningd/gossip_control.c | 6 +++++- lightningd/jsonrpc.c | 2 ++ lightningd/lightningd.c | 2 ++ lightningd/plugin.c | 18 +++++++++++++----- lightningd/plugin_hook.c | 2 ++ lightningd/subd.c | 1 + 9 files changed, 45 insertions(+), 10 deletions(-) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 251e3f11a03c..711121a133fa 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -40,12 +40,14 @@ static void plugin_config_cb(const char *buffer, struct plugin *plugin) { plugin->plugin_state = INIT_COMPLETE; + log_debug(plugin->plugins->ld->log, "io_break: %s", __func__); io_break(plugin); } static void config_plugin(struct plugin *plugin) { struct jsonrpc_request *req; + void *ret; req = jsonrpc_request_start(plugin, "init", plugin->log, NULL, plugin_config_cb, plugin); @@ -55,7 +57,9 @@ static void config_plugin(struct plugin *plugin) tal_add_destructor(plugin, bitcoin_destructor); - io_loop_with_timers(plugin->plugins->ld); + ret = io_loop_with_timers(plugin->plugins->ld); + log_debug(plugin->plugins->ld->log, "io_loop_with_timers: %s", __func__); + assert(ret == plugin); } static void wait_plugin(struct bitcoind *bitcoind, const char *method, diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index f965ae90c587..65b5734c1beb 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -38,6 +38,7 @@ static void maybe_completed_init(struct chain_topology *topo) return; if (!topo->root) return; + log_debug(topo->ld->log, "io_break: %s", __func__); io_break(topo); } @@ -1087,6 +1088,7 @@ static void retry_check_chain(struct chain_topology *topo) void setup_topology(struct chain_topology *topo, u32 min_blockheight, u32 max_blockheight) { + void *ret; memset(&topo->feerate, 0, sizeof(topo->feerate)); topo->min_blockheight = min_blockheight; @@ -1107,7 +1109,9 @@ void setup_topology(struct chain_topology *topo, start_fee_estimate(topo); /* Once it gets initial block, it calls io_break() and we return. */ - io_loop_with_timers(topo->ld); + ret = io_loop_with_timers(topo->ld); + assert(ret == topo); + log_debug(topo->ld->log, "io_loop_with_timers: %s", __func__); } void begin_topology(struct chain_topology *topo) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 0fe75804896b..58f5ca882362 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -504,6 +504,7 @@ static void connect_init_done(struct subd *connectd, } /* Break out of loop, so we can begin */ + log_debug(connectd->ld->log, "io_break: %s", __func__); io_break(connectd); } @@ -515,6 +516,7 @@ int connectd_init(struct lightningd *ld) struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr; enum addr_listen_announce *listen_announce = ld->proposed_listen_announce; const char *websocket_helper_path; + void *ret; websocket_helper_path = subdaemon_path(tmpctx, ld, "lightning_websocketd"); @@ -566,7 +568,9 @@ int connectd_init(struct lightningd *ld) connect_init_done, NULL); /* Wait for init_reply */ - io_loop(NULL, NULL); + ret = io_loop(NULL, NULL); + log_debug(ld->log, "io_loop: %s", __func__); + assert(ret == ld->connectd); return fds[0]; } @@ -589,18 +593,22 @@ static void connect_activate_done(struct subd *connectd, } /* Break out of loop, so we can begin */ + log_debug(connectd->ld->log, "io_break: %s", __func__); io_break(connectd); } void connectd_activate(struct lightningd *ld) { + void *ret; const u8 *msg = towire_connectd_activate(NULL, ld->listen); subd_req(ld->connectd, ld->connectd, take(msg), -1, 0, connect_activate_done, NULL); /* Wait for activate_reply */ - io_loop(NULL, NULL); + ret = io_loop(NULL, NULL); + log_debug(ld->log, "io_loop: %s", __func__); + assert(ret == ld->connectd); } void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer) diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 3c5e25df7d81..f57cd7a5df0b 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -229,6 +229,7 @@ static void gossipd_init_done(struct subd *gossipd, void *unused) { /* Break out of loop, so we can begin */ + log_debug(gossipd->ld->log, "io_break: %s", __func__); io_break(gossipd); } @@ -238,6 +239,7 @@ void gossip_init(struct lightningd *ld, int connectd_fd) { u8 *msg; int hsmfd; + void *ret; hsmfd = hsm_get_global_fd(ld, HSM_CAP_ECDH|HSM_CAP_SIGN_GOSSIP); @@ -267,7 +269,9 @@ void gossip_init(struct lightningd *ld, int connectd_fd) gossipd_init_done, NULL); /* Wait for gossipd_init_reply */ - io_loop(NULL, NULL); + ret = io_loop(NULL, NULL); + log_debug(ld->log, "io_loop: %s", __func__); + assert(ret == ld->gossip); } void gossipd_notify_spend(struct lightningd *ld, diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 4f67617859a3..5b248c566cba 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -183,6 +183,7 @@ static struct command_result *json_stop(struct command *cmd, /* With rpc_command_hook, jcon might have closed in the meantime! */ if (!cmd->jcon) { /* Return us to toplevel lightningd.c */ + log_debug(cmd->ld->log, "io_break: %s", __func__); io_break(cmd->ld); return command_still_pending(cmd); } @@ -986,6 +987,7 @@ static struct io_plan *start_json_stream(struct io_conn *conn, /* Once the stop_conn conn is drained, we can shut down. */ if (jcon->ld->stop_conn == conn && jcon->ld->state == LD_STATE_RUNNING) { /* Return us to toplevel lightningd.c */ + log_debug(jcon->ld->log, "io_break: %s", __func__); io_break(jcon->ld); /* We never come back. */ return io_out_wait(conn, conn, io_never, conn); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index e9c993bd40e9..97b0c03d4fdb 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -856,6 +856,7 @@ void lightningd_exit(struct lightningd *ld, int exit_code) { ld->exit_code = tal(ld, int); *ld->exit_code = exit_code; + log_debug(ld->log, "io_break: %s", __func__); io_break(ld); } @@ -1184,6 +1185,7 @@ int main(int argc, char *argv[]) * shut down. */ assert(io_loop_ret == ld); + log_debug(ld->log, "io_loop_with_timers: %s", __func__); /* Fail JSON RPC requests and ignore plugin's responses */ ld->state = LD_STATE_SHUTDOWN; diff --git a/lightningd/plugin.c b/lightningd/plugin.c index dad4854ec3db..3ad5e330e9a9 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -159,9 +159,10 @@ static void check_plugins_manifests(struct plugins *plugins) } /* As startup, we break out once all getmanifest are returned */ - if (plugins->startup) + if (plugins->startup) { + log_debug(plugins->ld->log, "io_break: %s", __func__); io_break(plugins); - else + } else /* Otherwise we go straight into configuring them */ plugins_config(plugins); } @@ -214,8 +215,10 @@ static void destroy_plugin(struct plugin *p) /* Daemon shutdown overrules plugin's importance; aborts init checks */ if (p->plugins->ld->state == LD_STATE_SHUTDOWN) { /* But return if this was the last plugin! */ - if (list_empty(&p->plugins->plugins)) + if (list_empty(&p->plugins->plugins)) { + log_debug(p->plugins->ld->log, "io_break: %s", __func__); io_break(destroy_plugin); + } return; } @@ -1761,8 +1764,12 @@ void plugins_init(struct plugins *plugins) setenv("LIGHTNINGD_PLUGIN", "1", 1); setenv("LIGHTNINGD_VERSION", version(), 1); - if (plugins_send_getmanifest(plugins)) - io_loop_with_timers(plugins->ld); + if (plugins_send_getmanifest(plugins)) { + void *ret; + ret = io_loop_with_timers(plugins->ld); + log_debug(plugins->ld->log, "io_loop_with_timers: %s", __func__); + assert(ret == plugins); + } } static void plugin_config_cb(const char *buffer, @@ -2058,6 +2065,7 @@ void *plugins_exclusive_loop(struct plugin **plugins) /* We don't service timers here, either! */ ret = io_loop(NULL, NULL); + log_debug(plugins[0]->plugins->ld->log, "io_loop: %s", __func__); for (i = 0; i < tal_count(plugins); ++i) { io_conn_out_exclusive(plugins[i]->stdin_conn, false); diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index ed9f50c013b9..9b3e83cd181e 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -332,6 +332,7 @@ static void db_hook_response(const char *buffer, const jsmntok_t *toks, return; /* We're done, exit exclusive loop. */ + log_debug(dwh_req->plugin->plugins->ld->log, "io_break: %s", __func__); io_break(dwh_req->ph_req); } @@ -392,6 +393,7 @@ void plugin_hook_db_sync(struct db *db) if (ret != ph_req) { void *ret2 = plugins_exclusive_loop(plugins); assert(ret2 == ph_req); + log_debug(plugins[0]->plugins->ld->log, "io_break: %s", __func__); io_break(ret); } assert(num_hooks == 0); diff --git a/lightningd/subd.c b/lightningd/subd.c index 80b5aa00cbd2..7dcbb0956bb0 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -463,6 +463,7 @@ static bool handle_version(struct subd *sd, const u8 *msg) ver, version()); sd->ld->try_reexec = true; /* Return us to toplevel lightningd.c */ + log_debug(sd->ld->log, "io_break: %s", __func__); io_break(sd->ld); return false; } From e120b4afd6dd7d763f7dd0bf671ab3f0efa821bc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:22:01 +0930 Subject: [PATCH 0842/1530] lightningd: add more information should subd send wrong message. I saw this once, but could not track it down. Signed-off-by: Rusty Russell --- lightningd/subd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/subd.c b/lightningd/subd.c index 7dcbb0956bb0..a959eb1d7d36 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -836,7 +836,8 @@ void subd_send_msg(struct subd *sd, const u8 *msg_out) u16 type = fromwire_peektype(msg_out); /* FIXME: We should use unique upper bits for each daemon, then * have generate-wire.py add them, just assert here. */ - assert(!strstarts(sd->msgname(type), "INVALID")); + if (strstarts(sd->msgname(type), "INVALID")) + fatal("Sending %s an invalid message %s", sd->name, tal_hex(tmpctx, msg_out)); msg_enqueue(sd->outq, msg_out); } From 4970704d2400c82ebcd7a9a57a00c95316584395 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:23:01 +0930 Subject: [PATCH 0843/1530] pytest: fix gossipwith flake harder. Even though we generally wait until a node has seen the gossip, that doesn't mean that connectd has processed it! This means when we connect it may still send us "old" gossip. So we set the OPT_GOSSIP_QUERIES bit, which means don't send until we ask. But now it sends us WIRE_QUERY_CHANNEL_RANGE, so everyone needs to filter that out. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 1 + tests/test_gossip.py | 54 +++++++++++----------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 57e51beacf78..cc13e097eb3c 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1133,6 +1133,7 @@ def query_gossip(self, querytype, *args, filters=[]): timeout=TIMEOUT, stdout=subprocess.PIPE).stdout.strip() out = subprocess.run(['devtools/gossipwith', + '--features=40', # OPT_GOSSIP_QUERIES '--timeout-after={}'.format(int(math.sqrt(TIMEOUT) + 1)), '{}@localhost:{}'.format(self.info['id'], self.port), diff --git a/tests/test_gossip.py b/tests/test_gossip.py index acbeeba9453f..041b8188de86 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -278,7 +278,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, '0', '0xFFFFFFFF', - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # 0x0100 = channel_announcement # 0x0102 = channel_update @@ -291,7 +291,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('gossip_timestamp_filter', genesis_blockhash, '0', before_anything - backdate, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) assert msgs == [] # Now choose range which will only give first update. @@ -299,7 +299,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): genesis_blockhash, before_anything - backdate, after_12 - before_anything + 1, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # 0x0100 = channel_announcement # 0x0102 = channel_update @@ -313,7 +313,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): genesis_blockhash, before_23 - backdate, after_23 - before_23 + 1, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # 0x0100 = channel_announcement # 0x0102 = channel_update @@ -633,14 +633,14 @@ def test_gossip_no_empty_announcements(node_factory, bitcoind, chainparams): assert l1.query_gossip('query_channel_range', chainparams['chain_hash'], 0, 1000000, - filters=['0109', '0012']) == ['0108' - # blockhash - + chainparams['chain_hash'] - # first_blocknum, number_of_blocks, complete - + format(0, '08x') + format(1000000, '08x') + '01' - # encoded_short_ids - + format(len(encoded) // 2, '04x') - + encoded] + filters=['0109', '0107', '0012']) == ['0108' + # blockhash + + chainparams['chain_hash'] + # first_blocknum, number_of_blocks, complete + + format(0, '08x') + format(1000000, '08x') + '01' + # encoded_short_ids + + format(len(encoded) // 2, '04x') + + encoded] # If we reconnect, gossip will now flow. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -718,7 +718,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', chainparams['chain_hash'], 0, 1000000, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12, scid23], check=True, timeout=TIMEOUT, @@ -737,7 +737,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, block12, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # reply_channel_range == 264 assert msgs == ['0108' # blockhash @@ -751,7 +751,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, block12 + 1, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12], check=True, timeout=TIMEOUT, @@ -770,7 +770,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, block23, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12], check=True, timeout=TIMEOUT, @@ -789,7 +789,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, block12, block23 - block12 + 1, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid12, scid23], check=True, timeout=TIMEOUT, @@ -808,7 +808,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, block23, 1, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid23], check=True, timeout=TIMEOUT, @@ -827,7 +827,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, block23 + 1, 1000000, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # reply_channel_range == 264 assert msgs == ['0108' # blockhash @@ -844,7 +844,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 0, 1000000, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # It should definitely have split l4.daemon.wait_for_log('reply_channel_range: splitting 0-1 of 2') @@ -869,7 +869,7 @@ def test_gossip_query_channel_range(node_factory, bitcoind, chainparams): msgs = l4.query_gossip('query_channel_range', genesis_blockhash, 1, 429496000, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) assert len(msgs) == 2 @@ -938,7 +938,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): msgs = l1.query_gossip('query_short_channel_ids', chain_hash, encoded, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # Should just get the WIRE_REPLY_SHORT_CHANNEL_IDS_END = 262 # (with chainhash and completeflag = 1) @@ -961,7 +961,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): msgs = l1.query_gossip('query_short_channel_ids', chain_hash, encoded, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) assert len(msgs) == 6 # 0x0100 = channel_announcement @@ -981,7 +981,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): msgs = l1.query_gossip('query_short_channel_ids', chain_hash, encoded, - filters=['0109', '0012']) + filters=['0109', '0107', '0012']) # Technically, this order could be different, but this matches code. assert len(msgs) == 10 @@ -1255,7 +1255,7 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # Filter out gossip_timestamp_filter, # channel_announcement and channel_updates. # And pings. - filters=['0109', '0102', '0100', '0012']) + filters=['0109', '0107', '0102', '0100', '0012']) assert len(msgs) == 2 assert (bytes("SENIORBEAM", encoding="utf8").hex() in msgs[0] @@ -1270,7 +1270,7 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # Filter out gossip_timestamp_filter, # channel_announcement and channel_updates. # And pings. - filters=['0109', '0102', '0100', '0012']) + filters=['0109', '0107', '0102', '0100', '0012']) assert msgs == msgs2 # Won't have queued up another one, either. assert not l1.daemon.is_in_log('node_announcement: delaying') @@ -1295,7 +1295,7 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # Filter out gossip_timestamp_filter, # channel_announcement and channel_updates. # And pings. - filters=['0109', '0102', '0100', '0012']) + filters=['0109', '0107', '0102', '0100', '0012']) assert msgs != msgs2 From a677ea404bbf4e2491df6c33f58e8b4201c1be79 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:24:01 +0930 Subject: [PATCH 0844/1530] pytest: fix flake in test_query_short_channel_id Again, our new behaviour of sending our own gossip even before they ask can confuse our gossip query tests. Create a new node, attach it, and perform queries on it. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 041b8188de86..80bcc7839830 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -924,7 +924,7 @@ def test_report_routing_failure(node_factory, bitcoind): @pytest.mark.developer("needs fast gossip") def test_query_short_channel_id(node_factory, bitcoind, chainparams): - l1, l2, l3 = node_factory.get_nodes(3) + l1, l2, l3, l4 = node_factory.get_nodes(4) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l2.rpc.connect(l3.info['id'], 'localhost', l3.port) chain_hash = chainparams['chain_hash'] @@ -950,15 +950,21 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): scid23, _ = l2.fundchannel(l3, 10**5) mine_funding_to_announce(bitcoind, [l1, l2, l3]) - # It will know about everything. - l1.daemon.wait_for_log('Received node_announcement for node {}'.format(l3.info['id'])) + # Attach node which won't spam us (since it's not their channel). + l4.rpc.connect(l1.info['id'], 'localhost', l1.port) + l4.rpc.connect(l2.info['id'], 'localhost', l2.port) + l4.rpc.connect(l3.info['id'], 'localhost', l3.port) + + # Make sure it sees all channels, then node announcements. + wait_for(lambda: len(l4.rpc.listchannels()['channels']) == 4) + wait_for(lambda: all('alias' in n for n in l4.rpc.listnodes()['nodes'])) # This query should get channel announcements, channel updates, and node announcements. encoded = subprocess.run(['devtools/mkencoded', '--scids', '00', scid23], check=True, timeout=TIMEOUT, stdout=subprocess.PIPE).stdout.strip().decode() - msgs = l1.query_gossip('query_short_channel_ids', + msgs = l4.query_gossip('query_short_channel_ids', chain_hash, encoded, filters=['0109', '0107', '0012']) @@ -978,7 +984,7 @@ def test_query_short_channel_id(node_factory, bitcoind, chainparams): check=True, timeout=TIMEOUT, stdout=subprocess.PIPE).stdout.strip().decode() - msgs = l1.query_gossip('query_short_channel_ids', + msgs = l4.query_gossip('query_short_channel_ids', chain_hash, encoded, filters=['0109', '0107', '0012']) From 25699994e5941fb165cf379ff3a688981bcdaddb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:25:01 +0930 Subject: [PATCH 0845/1530] pytest: fix flake in test_node_reannounce Again, our new behaviour of sending our own gossip even before they ask can confuse our gossip query tests. In this case, simply eliminate duplicates. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 80bcc7839830..e6125ee1c84d 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1263,6 +1263,8 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # And pings. filters=['0109', '0107', '0102', '0100', '0012']) + # May send its own announcement *twice*, since it always spams us. + msgs = list(set(msgs)) assert len(msgs) == 2 assert (bytes("SENIORBEAM", encoding="utf8").hex() in msgs[0] or bytes("SENIORBEAM", encoding="utf8").hex() in msgs[1]) @@ -1277,6 +1279,9 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # channel_announcement and channel_updates. # And pings. filters=['0109', '0107', '0102', '0100', '0012']) + + # May send its own announcement *twice*, since it always spams us. + msgs2 = list(set(msgs2)) assert msgs == msgs2 # Won't have queued up another one, either. assert not l1.daemon.is_in_log('node_announcement: delaying') @@ -1295,6 +1300,7 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): assert ad['channel_fee_max_base_msat'] == Millisatoshi('2000msat') assert ad['channel_fee_max_proportional_thousandths'] == 22 + # May send its own announcement *twice*, since it always spams us. msgs2 = l1.query_gossip('gossip_timestamp_filter', genesis_blockhash, '0', '0xFFFFFFFF', @@ -1302,6 +1308,7 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # channel_announcement and channel_updates. # And pings. filters=['0109', '0107', '0102', '0100', '0012']) + msgs2 = list(set(msgs2)) assert msgs != msgs2 From 9152b8c4240957f185ea026c135eccf335b62069 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 14:26:01 +0930 Subject: [PATCH 0846/1530] pytest: fix test_multifunding_feerates There's a 1 in 256 chance that our signature on the transaction is 70, not 71 bytes long. This changes the freerate. So fix up the weight in this case, to be the expected weight. ``` @unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Fees on elements are different") @pytest.mark.developer("uses dev-fail") @pytest.mark.openchannel('v1') # v2 the weight calculation is off by 3 deftest_multifunding_feerates(node_factory, bitcoind): ''' Test feerate parameters for multifundchannel ''' funding_tx_feerate = '10000perkw' commitment_tx_feerate_int = 2000 commitment_tx_feerate = str(commitment_tx_feerate_int) + 'perkw' l1, l2, l3 = node_factory.get_nodes(3, opts={'log-level': 'debug'}) l1.fundwallet(1 << 26) def_connect_str(node): return'{}@localhost:{}'.format(node.info['id'], node.port) destinations = [{"id": _connect_str(l2), 'amount': 50000}] res = l1.rpc.multifundchannel(destinations, feerate=funding_tx_feerate, commitment_feerate=commitment_tx_feerate) entry = bitcoind.rpc.getmempoolentry(res['txid']) weight = entry['weight'] expected_fee = int(funding_tx_feerate[:-5]) * weight // 1000 > assert expected_fee == entry['fees']['base'] * 10 ** 8 E AssertionError: assert 7000 == (Decimal('0.00007010') * (10 ** 8)) tests/test_connection.py:1982: AssertionError ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 69df7bc4b9b5..89e7e1e6eaf2 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1978,6 +1978,9 @@ def _connect_str(node): entry = bitcoind.rpc.getmempoolentry(res['txid']) weight = entry['weight'] + # If signature is unexpectedly short, we get a spurious failure here! + res = bitcoind.rpc.decoderawtransaction(res['tx']) + weight += 71 - len(res['vin'][0]['txinwitness'][0]) // 2 expected_fee = int(funding_tx_feerate[:-5]) * weight // 1000 assert expected_fee == entry['fees']['base'] * 10 ** 8 From cc7a405ca4acb8e96cc7890439663e5474236767 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 8 May 2022 18:04:29 +0200 Subject: [PATCH 0847/1530] lightningd: use the standard port derivation in connect command Complete implementation of BOLT1 port derivation proposal https://github.com/lightning/bolts/pull/968 Changelog-Added: rpc: use the standard port derivation in connect command when the port is not specified. Signed-off-by: Vincenzo Palazzo --- bitcoin/chainparams.c | 9 ++++++++- bitcoin/chainparams.h | 8 ++++++++ lightningd/connect_control.c | 3 ++- lightningd/lightningd.c | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index 3785a500c731..edc8cdd713e0 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -36,6 +36,7 @@ const struct chainparams networks[] = { 0x5a, 0x08, 0x9c, 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00}}}}, .rpc_port = 8332, + .ln_port = 9735, .cli = "bitcoin-cli", .cli_args = NULL, .cli_min_supported_version = 150000, @@ -67,6 +68,7 @@ const struct chainparams networks[] = { 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f}}}}, .rpc_port = 18443, + .ln_port = 9735, .cli = "bitcoin-cli", .cli_args = "-regtest", .cli_min_supported_version = 150000, @@ -92,6 +94,7 @@ const struct chainparams networks[] = { 0x2c, 0x42, 0x25, 0xe9, 0x73, 0x98, 0x81, 0x08, 0x00, 0x00, 0x00}}}}, .rpc_port = 38332, + .ln_port = 39735, .cli = "bitcoin-cli", .cli_args = "-signet", .cli_min_supported_version = 150000, @@ -115,6 +118,7 @@ const struct chainparams networks[] = { 0xe9, 0x0e, 0xad, 0x01, 0xea, 0x33, 0x09, 0x00, 0x00, 0x00, 0x00}}}}, .rpc_port = 18332, + .ln_port = 19735, .cli = "bitcoin-cli", .cli_args = "-testnet", .cli_min_supported_version = 150000, @@ -138,6 +142,7 @@ const struct chainparams networks[] = { 0x1e, 0xda, 0xba, 0x59, 0x40, 0xfd, 0x1f, 0xe3, 0x65, 0xa7, 0x12}}}}, .rpc_port = 9332, + .ln_port = 9735, .cli = "litecoin-cli", .cli_args = NULL, .cli_min_supported_version = 150000, @@ -162,6 +167,7 @@ const struct chainparams networks[] = { 0x13, 0xee, 0xfd, 0xd9, 0x51, 0x28, 0x4b, 0x5a, 0x62, 0x66, 0x49}}}}, .rpc_port = 19332, + .ln_port = 9735, .cli = "litecoin-cli", .cli_args = "-testnet", .cli_min_supported_version = 150000, @@ -186,6 +192,7 @@ const struct chainparams networks[] = { 0xfe, 0x14, 0x68, 0x01, 0x16, 0x23, 0x93, 0x36, 0x42, 0x86, 0xc6}}}}, .rpc_port = 19332, + .ln_port = 9735, .cli = "elements-cli", .cli_args = "-chain=liquid-regtest", .dust_limit = {546}, @@ -209,6 +216,7 @@ const struct chainparams networks[] = { 0x68, 0x8d, 0x2c, 0x37, 0x92, 0x96, 0x88, 0x8a, 0x20, 0x60, 0x03}}}}, .rpc_port = 7041, + .ln_port = 9735, .cli = "elements-cli", .cli_args = "-chain=liquidv1", .dust_limit = {546}, @@ -261,4 +269,3 @@ const char *chainparams_get_network_names(const tal_t *ctx) tal_append_fmt(&networks_string, ", %s", networks[i].network_name); return networks_string; } - diff --git a/bitcoin/chainparams.h b/bitcoin/chainparams.h index b4acb2c27de4..f5c71509dd26 100644 --- a/bitcoin/chainparams.h +++ b/bitcoin/chainparams.h @@ -22,6 +22,14 @@ struct chainparams { const char *bip70_name; const struct bitcoin_blkid genesis_blockhash; const int rpc_port; + /** + * BOLT 1: The default TCP port depends on the network used. The most common networks are: + * + * - Bitcoin mainet with port number 9735 or the corresponding hexadecimal `0x2607`; + * - Bitcoin testnet with port number 19735 (`0x4D17`); + * - Bitcoin signet with port number 39735 (`0xF87`). + */ + const int ln_port; const char *cli; const char *cli_args; /* The min numeric version of cli supported */ diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 58f5ca882362..4439c3b412ad 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -140,7 +141,7 @@ static struct command_result *json_connect(struct command *cmd, /* Is there a port? */ if (!port) { port = tal(cmd, u32); - *port = DEFAULT_PORT; + *port = chainparams->ln_port; } addr = tal(cmd, struct wireaddr_internal); if (!parse_wireaddr_internal(name, addr, *port, false, diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 97b0c03d4fdb..3bff017fd67f 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -970,7 +970,7 @@ int main(int argc, char *argv[]) /*~ Set the default portnum according to the used network * similarly to what Bitcoin Core does to ports by default. */ - ld->portnum = DEFAULT_PORT + chainparams->rpc_port - 8332; + ld->portnum = chainparams->ln_port; /*~ Initialize all the plugins we just registered, so they can * do their thing and tell us about themselves (including From 4b11f968ad15c28b526e02a4068b6243220efa6a Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 11 May 2022 22:20:52 +0200 Subject: [PATCH 0848/1530] lightningd: change the regtest default port according with the tests This doesn't make sense, but for now we just keep it Signed-off-by: Vincenzo Palazzo --- bitcoin/chainparams.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index edc8cdd713e0..a7539e65bf22 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -68,7 +68,7 @@ const struct chainparams networks[] = { 0x33, 0x2a, 0x1f, 0xc7, 0xb2, 0xb7, 0x3c, 0xf1, 0x88, 0x91, 0x0f}}}}, .rpc_port = 18443, - .ln_port = 9735, + .ln_port = 19846, .cli = "bitcoin-cli", .cli_args = "-regtest", .cli_min_supported_version = 150000, @@ -192,7 +192,7 @@ const struct chainparams networks[] = { 0xfe, 0x14, 0x68, 0x01, 0x16, 0x23, 0x93, 0x36, 0x42, 0x86, 0xc6}}}}, .rpc_port = 19332, - .ln_port = 9735, + .ln_port = 20735, .cli = "elements-cli", .cli_args = "-chain=liquid-regtest", .dust_limit = {546}, From c07d44b4d4acddecf232d859b7b424c086d06706 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sat, 18 Jun 2022 14:18:38 +0100 Subject: [PATCH 0849/1530] docs: update the docs in according with the new code Suggested-by: @rustyrussell Signed-off-by: Vincenzo Palazzo # Title: Signed-off-by: Vincenzo Palazzo --- bitcoin/chainparams.h | 4 +++- doc/lightning-connect.7.md | 6 +++++- doc/lightningd-config.5.md | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/bitcoin/chainparams.h b/bitcoin/chainparams.h index f5c71509dd26..8bf4ee29cdd5 100644 --- a/bitcoin/chainparams.h +++ b/bitcoin/chainparams.h @@ -23,7 +23,9 @@ struct chainparams { const struct bitcoin_blkid genesis_blockhash; const int rpc_port; /** - * BOLT 1: The default TCP port depends on the network used. The most common networks are: + * BOLT 1: + * + * The default TCP port depends on the network used. The most common networks are: * * - Bitcoin mainet with port number 9735 or the corresponding hexadecimal `0x2607`; * - Bitcoin testnet with port number 19735 (`0x4D17`); diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index c2e9851f765e..ff4cfcb73b3a 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -18,7 +18,11 @@ be of the form *id@host* or *id@host:port*. In this case, the *host* and *host* is the peer's hostname or IP address. -If not specified, the *port* defaults to 9735. +If not specified, the *port* depends on the current network: +- bitcoin **mainnet**: 9735. +- bitcoin **testnet**: 19735. +- bitcoin **signet**: 39735. +- bitcoin **regtest**: 19846. If *host* is not specified (or doesn't work), the connection will be attempted to an IP belonging to *id* obtained through gossip with other already connected diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 31832958bf62..92fc7e63fc78 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -372,7 +372,8 @@ network. Note that for simple setups, the implicit *autolisten* option does the right thing: for the mainnet (bitcoin) network it will try to bind to port 9735 on IPv4 and IPv6, and will announce it to peers if it seems -like a public address. +like a public address (and other default ports for other networks, +as described below). Core Lightning also support IPv4/6 address discovery behind NAT routers. If your node detects an new public address, it will update its announcement. From 7ff62b4a00c71987841db6cc1f63f8533f522b08 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Mon, 20 Jun 2022 22:38:18 +0100 Subject: [PATCH 0850/1530] lightnind: remove`DEFAULT_PORT` global definition Signed-off-by: Vincenzo Palazzo --- bitcoin/chainparams.c | 7 +++++++ bitcoin/chainparams.h | 5 +++++ common/test/run-wireaddr.c | 15 ++++++++++++++- common/wireaddr.c | 5 +++-- common/wireaddr.h | 8 -------- connectd/connectd.c | 5 +++-- connectd/test/run-netaddress.c | 2 ++ devtools/gossipwith.c | 11 ++++++++--- gossipd/gossipd.c | 4 ++-- gossipd/test/Makefile | 1 - lightningd/connect_control.c | 2 +- lightningd/lightningd.c | 2 +- tests/test_connection.py | 10 +++++----- wallet/wallet.c | 4 ++-- wire/test/run-tlvstream.c | 3 +++ 15 files changed, 56 insertions(+), 28 deletions(-) diff --git a/bitcoin/chainparams.c b/bitcoin/chainparams.c index a7539e65bf22..ac48ee5da763 100644 --- a/bitcoin/chainparams.c +++ b/bitcoin/chainparams.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -269,3 +270,9 @@ const char *chainparams_get_network_names(const tal_t *ctx) tal_append_fmt(&networks_string, ", %s", networks[i].network_name); return networks_string; } + +int chainparams_get_ln_port(const struct chainparams *params) +{ + assert(params); + return params->ln_port; +} diff --git a/bitcoin/chainparams.h b/bitcoin/chainparams.h index 8bf4ee29cdd5..f4e493de66ac 100644 --- a/bitcoin/chainparams.h +++ b/bitcoin/chainparams.h @@ -74,4 +74,9 @@ const struct chainparams *chainparams_by_chainhash(const struct bitcoin_blkid *c */ const char *chainparams_get_network_names(const tal_t *ctx); +/** + * chainparams_get_ln_port - Return the lightning network default port by + * network if the chainparams is initialized, otherwise 9735 as mock port + */ +int chainparams_get_ln_port(const struct chainparams *params); #endif /* LIGHTNING_BITCOIN_CHAINPARAMS_H */ diff --git a/common/test/run-wireaddr.c b/common/test/run-wireaddr.c index 3fc66fa03f23..2eec576ba192 100644 --- a/common/test/run-wireaddr.c +++ b/common/test/run-wireaddr.c @@ -1,9 +1,22 @@ #include "config.h" -#include "../wireaddr.c" +#include #include #include #include +int simple_get_ln_port(const struct chainparams *params); + +#define chainparams_get_ln_port simple_get_ln_port + +#include "../wireaddr.c" + +#define DEFAULT_PORT simple_get_ln_port(NULL) + +int simple_get_ln_port(const struct chainparams *params UNNEEDED) +{ + return 9735; +} + /* AUTOGENERATED MOCKS START */ /* Generated stub for amount_asset_is_main */ bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) diff --git a/common/wireaddr.c b/common/wireaddr.c index b81d9bf3a54c..f43368400489 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -612,7 +613,7 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, * an onion address. */ if (strstarts(arg, "autotor:")) { addr->itype = ADDR_INTERNAL_AUTOTOR; - addr->u.torservice.port = DEFAULT_PORT; + addr->u.torservice.port = chainparams_get_ln_port(chainparams); /* Format is separated by slash. */ char **parts = tal_strsplit(tmpctx, arg, "/", STR_EMPTY_OK); @@ -644,7 +645,7 @@ bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, if (strstarts(arg, "statictor:")) { bool use_magic_blob = true; addr->itype = ADDR_INTERNAL_STATICTOR; - addr->u.torservice.port = DEFAULT_PORT; + addr->u.torservice.port = chainparams_get_ln_port(chainparams); memset(addr->u.torservice.blob, 0, sizeof(addr->u.torservice.blob)); /* Format is separated by slash. */ diff --git a/common/wireaddr.h b/common/wireaddr.h index 6c1401e74449..751cd8c8abfa 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -12,14 +12,6 @@ struct sockaddr_in6; struct sockaddr_in; struct sockaddr_un; -/* BOLT #1: - * - * The default TCP port is 9735. This corresponds to hexadecimal - * `0x2607`: the Unicode code point for LIGHTNING. - */ -#define DEFAULT_PORT 9735 - - /* BOLT #7: * * The following `address descriptor` types are defined: diff --git a/connectd/connectd.c b/connectd/connectd.c index 635f251b4363..fc2b4de933d3 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -8,6 +8,7 @@ * it. */ #include "config.h" +#include #include #include #include @@ -1707,7 +1708,7 @@ static void add_seed_addrs(struct wireaddr_internal **addrs, for (size_t i = 0; i < tal_count(hostnames); i++) { status_peer_debug(id, "Resolving %s", hostnames[i]); - new_addrs = wireaddr_from_hostname(tmpctx, hostnames[i], DEFAULT_PORT, + new_addrs = wireaddr_from_hostname(tmpctx, hostnames[i], chainparams_get_ln_port(chainparams), NULL, broken_reply, NULL); if (new_addrs) { for (size_t j = 0; j < tal_count(new_addrs); j++) { @@ -1859,7 +1860,7 @@ static void try_connect_peer(struct daemon *daemon, for (size_t i = 0; i < tal_count(hostnames); i++) { wireaddr_from_unresolved(&unresolved, hostnames[i], - DEFAULT_PORT); + chainparams_get_ln_port(chainparams)); tal_arr_expand(&addrs, unresolved); } } else if (daemon->use_dns) { diff --git a/connectd/test/run-netaddress.c b/connectd/test/run-netaddress.c index 8f6482cb18d7..586ec1644c09 100644 --- a/connectd/test/run-netaddress.c +++ b/connectd/test/run-netaddress.c @@ -10,6 +10,8 @@ #include #include +#define DEFAULT_PORT 9735 + /* AUTOGENERATED MOCKS START */ /* Generated stub for amount_asset_is_main */ bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 3e7f0481a9fd..35f60b72689d 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -21,6 +21,7 @@ #include #include +#define chainparams_get_ln_port simple_get_ln_port #define io_write_ simple_write #define io_read_ simple_read #define io_close simple_close @@ -45,7 +46,12 @@ static struct io_plan *simple_close(struct io_conn *conn) return NULL; } - #include "../connectd/handshake.c" +static int simple_get_ln_port(const struct chainparams *params UNNEEDED) +{ + return 9735; +} + + #include "../connectd/handshake.c" /* This makes the handshake prototypes work. */ struct io_conn { @@ -322,7 +328,7 @@ int main(int argc, char *argv[]) opt_usage_exit_fail("Invalid id %.*s", (int)(at - argv[1]), argv[1]); - if (!parse_wireaddr_internal(at+1, &addr, DEFAULT_PORT, NULL, + if (!parse_wireaddr_internal(at+1, &addr, simple_get_ln_port(NULL), NULL, true, false, true, &err_msg)) opt_usage_exit_fail("%s '%s'", err_msg, argv[1]); @@ -376,4 +382,3 @@ int main(int argc, char *argv[]) handshake_success, argv+2); exit(0); } - diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index a776d294f457..4a5f5b77d19c 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -350,8 +350,8 @@ static void handle_remote_addr(struct daemon *daemon, const u8 *msg) if (!fromwire_gossipd_remote_addr(msg, &remote_addr)) master_badmsg(WIRE_GOSSIPD_REMOTE_ADDR, msg); - /* current best guess is that we use DEFAULT_PORT on public internet */ - remote_addr.port = DEFAULT_PORT; + /* current best guess is that we use default port on public internet */ + remote_addr.port = chainparams_get_ln_port(chainparams); switch (remote_addr.type) { case ADDR_TYPE_IPV4: diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index 796e1a07e11d..fe39a0bbba9f 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -53,4 +53,3 @@ $(GOSSIPD_TEST_PROGRAMS): $(GOSSIPD_TEST_COMMON_OBJS) $(BITCOIN_OBJS) $(GOSSIPD_TEST_OBJS): $(GOSSIPD_HEADERS) $(GOSSIPD_SRC) gossipd-tests: $(GOSSIPD_TEST_PROGRAMS:%=unittest/%) - diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 4439c3b412ad..620231a6bb1e 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -141,7 +141,7 @@ static struct command_result *json_connect(struct command *cmd, /* Is there a port? */ if (!port) { port = tal(cmd, u32); - *port = chainparams->ln_port; + *port = chainparams_get_ln_port(chainparams); } addr = tal(cmd, struct wireaddr_internal); if (!parse_wireaddr_internal(name, addr, *port, false, diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 3bff017fd67f..45e71ed1426f 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -970,7 +970,7 @@ int main(int argc, char *argv[]) /*~ Set the default portnum according to the used network * similarly to what Bitcoin Core does to ports by default. */ - ld->portnum = chainparams->ln_port; + ld->portnum = chainparams_get_ln_port(chainparams); /*~ Initialize all the plugins we just registered, so they can * do their thing and tell us about themselves (including diff --git a/tests/test_connection.py b/tests/test_connection.py index 89e7e1e6eaf2..455b876e6278 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -100,14 +100,14 @@ def test_remote_addr(node_factory, bitcoind): # must not yet be send as we need the same `remote_addr` confirmed from a # another peer we have a channel with. # Note: In this state l2 stores remote_addr as reported by l1 - assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:19846") l1.restart() l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") # Now l1 sees l2 but without announced addresses. assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) - assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:19846") # connect second node. This will not yet trigger `node_annoucement` update, # as we again do not have a channel at the time we connected. @@ -117,20 +117,20 @@ def test_remote_addr(node_factory, bitcoind): # fund channel and check we didn't send Update earlier already l2.fundchannel(l3, wait_for_active=True) bitcoind.generate_block(5) - assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:19846") # restart, reconnect and re-check for updated node_annoucement. This time # l2 sees that two different peers with channel reported the same `remote_addr`. l3.restart() l2.rpc.connect(l3.info['id'], 'localhost', l3.port) l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") - l2.daemon.wait_for_log("Update our node_announcement for discovered address: 127.0.0.1:9735") + l2.daemon.wait_for_log("Update our node_announcement for discovered address: 127.0.0.1:19846") l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") address = l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses'][0] assert address['type'] == "ipv4" assert address['address'] == "127.0.0.1" - assert address['port'] == 9735 + assert address['port'] == 19846 @pytest.mark.developer("needs DEVELOPER=1 for fast gossip and --dev-allow-localhost for local remote_addr") diff --git a/wallet/wallet.c b/wallet/wallet.c index 346ae90b27cd..2c583f94bbe0 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -838,11 +838,11 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) /* This can happen for peers last seen on Torv2! */ addrstr = db_col_strdup(tmpctx, stmt, "address"); - if (!parse_wireaddr_internal(addrstr, &addr, DEFAULT_PORT, + if (!parse_wireaddr_internal(addrstr, &addr, chainparams_get_ln_port(chainparams), false, false, true, true, NULL)) { log_unusual(w->log, "Unparsable peer address %s: replacing", addrstr); - parse_wireaddr_internal("127.0.0.1:1", &addr, DEFAULT_PORT, + parse_wireaddr_internal("127.0.0.1:1", &addr, chainparams_get_ln_port(chainparams), false, false, true, true, NULL); } diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index dacdb3d2e1c1..c989095251c6 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -25,6 +25,9 @@ static const char *reason; /* Generated stub for chainparams_by_chainhash */ const struct chainparams *chainparams_by_chainhash(const struct bitcoin_blkid *chain_hash UNNEEDED) { fprintf(stderr, "chainparams_by_chainhash called!\n"); abort(); } +/* Generate std for chainparams_get_ln_port */ +int chainparams_get_ln_port(const struct chainparams *params UNNEEDED) +{ fprintf(stderr, "chainparams_get_ln_port called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) From 2c9d7484a2b81995d8c452a32d5b21c769a8f3a7 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 26 Jun 2022 23:04:50 +0000 Subject: [PATCH 0851/1530] test: introduce the default port function and remove hard coded port --- tests/test_connection.py | 14 ++++++++------ tests/test_gossip.py | 4 ++-- tests/utils.py | 10 ++++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 455b876e6278..feed3ed939e7 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -8,7 +8,7 @@ expected_peer_features, expected_node_features, expected_channel_features, check_coin_moves, first_channel_id, account_balance, basic_fee, - scriptpubkey_addr, + scriptpubkey_addr, default_ln_port, EXPERIMENTAL_FEATURES, mine_funding_to_announce ) from pyln.testing.utils import SLOW_MACHINE, VALGRIND, EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT @@ -96,18 +96,20 @@ def test_remote_addr(node_factory, bitcoind): l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) + def_port = default_ln_port(l2.info["network"]) + # when we restart l1 with a channel and reconnect, node_annoucement update # must not yet be send as we need the same `remote_addr` confirmed from a # another peer we have a channel with. # Note: In this state l2 stores remote_addr as reported by l1 - assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:19846") + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:{}".format(def_port)) l1.restart() l2.rpc.connect(l1.info['id'], 'localhost', l1.port) l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") # Now l1 sees l2 but without announced addresses. assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) - assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:19846") + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:{}".format(def_port)) # connect second node. This will not yet trigger `node_annoucement` update, # as we again do not have a channel at the time we connected. @@ -117,20 +119,20 @@ def test_remote_addr(node_factory, bitcoind): # fund channel and check we didn't send Update earlier already l2.fundchannel(l3, wait_for_active=True) bitcoind.generate_block(5) - assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:19846") + assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:{}".format(def_port)) # restart, reconnect and re-check for updated node_annoucement. This time # l2 sees that two different peers with channel reported the same `remote_addr`. l3.restart() l2.rpc.connect(l3.info['id'], 'localhost', l3.port) l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") - l2.daemon.wait_for_log("Update our node_announcement for discovered address: 127.0.0.1:19846") + l2.daemon.wait_for_log("Update our node_announcement for discovered address: 127.0.0.1:{}".format(def_port)) l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") address = l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses'][0] assert address['type'] == "ipv4" assert address['address'] == "127.0.0.1" - assert address['port'] == 19846 + assert address['port'] == def_port @pytest.mark.developer("needs DEVELOPER=1 for fast gossip and --dev-allow-localhost for local remote_addr") diff --git a/tests/test_gossip.py b/tests/test_gossip.py index e6125ee1c84d..1c94e185e4fc 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -6,7 +6,7 @@ from utils import ( DEVELOPER, wait_for, TIMEOUT, only_one, sync_blockheight, expected_node_features, COMPAT, EXPERIMENTAL_FEATURES, - mine_funding_to_announce + mine_funding_to_announce, default_ln_port ) import json @@ -1928,7 +1928,7 @@ def test_statictor_onions(node_factory): assert l1.daemon.is_in_log('127.0.0.1:{}'.format(l1.port)) # Did not specify torport, so it's the default. - assert l1.daemon.is_in_log('.onion:{}'.format(9735)) + assert l1.daemon.is_in_log('.onion:{}'.format(default_ln_port(l1.info["network"]))) assert l2.daemon.is_in_log('x2y4zvh4fn5q3eouuh7nxnc7zeawrqoutljrup2xjtiyxgx3emgkemad.onion:{},127.0.0.1:{}'.format(9736, l2.port)) diff --git a/tests/utils.py b/tests/utils.py index d06292ec0e11..436e6fb3863c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,6 +8,16 @@ EXPERIMENTAL_FEATURES = env("EXPERIMENTAL_FEATURES", "0") == "1" COMPAT = env("COMPAT", "1") == "1" +def default_ln_port(network: str) -> int: + network_map = { + "bitcoin": 9735, + "testnet": 19735, + "regtest": 19846, + "signet": 39735, + "liquid-regtest": 20735, + "liquid": 9735, + } + return network_map[network] def anchor_expected(): return EXPERIMENTAL_FEATURES or EXPERIMENTAL_DUAL_FUND From 2754e30269e918525e49cf871006c9a5d569fdf5 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 26 Jun 2022 23:16:14 +0000 Subject: [PATCH 0852/1530] devtools: revert changes and make sure chainparams always set. [Also fixes crash with -h, since chainparams was not set! --RR] --- devtools/gossipwith.c | 15 ++++++--------- tests/utils.py | 4 +++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 35f60b72689d..1303b8c82d8f 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -21,13 +21,13 @@ #include #include -#define chainparams_get_ln_port simple_get_ln_port #define io_write_ simple_write #define io_read_ simple_read #define io_close simple_close static bool stream_stdin = false; static bool no_init = false; static bool hex = false; +static bool explicit_network = false; static int timeout_after = -1; static u8 *features; @@ -46,11 +46,6 @@ static struct io_plan *simple_close(struct io_conn *conn) return NULL; } -static int simple_get_ln_port(const struct chainparams *params UNNEEDED) -{ - return 9735; -} - #include "../connectd/handshake.c" /* This makes the handshake prototypes work. */ @@ -83,6 +78,7 @@ static char *opt_set_network(const char *arg, void *unused) chainparams = chainparams_for_network(arg); if (!chainparams) return tal_fmt(NULL, "Unknown network name '%s'", arg); + explicit_network = true; return NULL; } @@ -184,12 +180,12 @@ static struct io_plan *handshake_success(struct io_conn *conn, if (!no_init) { struct tlv_init_tlvs *tlvs = NULL; - if (chainparams) { + if (explicit_network) { tlvs = tlv_init_tlvs_new(NULL); tlvs->networks = tal_arr(tlvs, struct bitcoin_blkid, 1); tlvs->networks[0] = chainparams->genesis_blockhash; } - msg = towire_init(NULL, NULL, features, tlvs); + msg = towire_init(NULL, NULL, features, tlvs); sync_crypto_write(peer_fd, cs, take(msg)); /* Ignore their init message. */ @@ -287,6 +283,7 @@ int main(int argc, char *argv[]) memset(¬sosecret, 0x42, sizeof(notsosecret)); features = tal_arr(conn, u8, 0); + chainparams = chainparams_for_network("bitcoin"); opt_register_noarg("--initial-sync", opt_set_bool, &initial_sync, "Stream complete gossip history at start"); @@ -328,7 +325,7 @@ int main(int argc, char *argv[]) opt_usage_exit_fail("Invalid id %.*s", (int)(at - argv[1]), argv[1]); - if (!parse_wireaddr_internal(at+1, &addr, simple_get_ln_port(NULL), NULL, + if (!parse_wireaddr_internal(at+1, &addr, chainparams_get_ln_port(chainparams), NULL, true, false, true, &err_msg)) opt_usage_exit_fail("%s '%s'", err_msg, argv[1]); diff --git a/tests/utils.py b/tests/utils.py index 436e6fb3863c..0af3afadd6df 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -8,6 +8,7 @@ EXPERIMENTAL_FEATURES = env("EXPERIMENTAL_FEATURES", "0") == "1" COMPAT = env("COMPAT", "1") == "1" + def default_ln_port(network: str) -> int: network_map = { "bitcoin": 9735, @@ -15,10 +16,11 @@ def default_ln_port(network: str) -> int: "regtest": 19846, "signet": 39735, "liquid-regtest": 20735, - "liquid": 9735, + "liquid": 9735, } return network_map[network] + def anchor_expected(): return EXPERIMENTAL_FEATURES or EXPERIMENTAL_DUAL_FUND From 8e2dcc11673a4c6d39350a0b439350e2312d482c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 24 Jun 2022 10:58:49 +0930 Subject: [PATCH 0853/1530] doc: document the [] IPv6 address hack. Fixes: #5317 Signed-off-by: Rusty Russell --- doc/lightningd-config.5.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 92fc7e63fc78..6049178b5d8e 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -391,7 +391,9 @@ Set an IP address (v4 or v6) or automatic Tor address to listen on and An empty 'IPADDRESS' is a special value meaning bind to IPv4 and/or IPv6 on all interfaces, '0.0.0.0' means bind to all IPv4 -interfaces, '::' means 'bind to all IPv6 interfaces'. +interfaces, '::' means 'bind to all IPv6 interfaces' (if you want to +specify an IPv6 address *and* a port, use `[]` around the IPv6 +address, like `[::]:9750`). If 'PORT' is not specified, the default port 9735 is used for mainnet (testnet: 19735, signet: 39735, regtest: 19846). If we can determine a public IP address from the resulting binding, From 4ee55acc7114c1dd4fad06f78984864d825b28a5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 24 Jun 2022 09:51:12 +0930 Subject: [PATCH 0854/1530] connectd: don't start connecting in parallel in peer_conn_closed. The crash below from @zerofeerouting left me confused. The invalid value in fmt_wireaddr_internal is a telltale sign of use-after-free. This backtrace shows us destroying the conn *twice*: what's happening? Well, tal carefully protects against destroying twice: it's not that unusual to free something in a destructor which has already been freed. So this indicates that there are *two* io_conn hanging off one struct connecting, which isn't supposed to happen! We deliberately call try_connect_one_addr() initially, then inside the io_conn destructor. But due to races in connectd vs lightningd connection state, we added a fix which allows a connect command to sit around while the peer is cleaning up (6cc9f37cab26a01bcc93ccda2fcc048a55e869a1) and get fired off when it's done. But what if, in the chaos, we are already connecting again? Now we'll end up with *two* connections. Fortunately, we have a `conn` pointer inside struct connecting, which (with a bit of additional care) we can ensure is only non-NULL while we're actually trying to connect. This lets us check that before firing off a new connection attempt in peer_conn_closed. ``` lightning_connectd: FATAL SIGNAL 6 (version v0.11.2rc2-2-g8f7e939) 0x5614a4915ae8 send_backtrace common/daemon.c:33 0x5614a4915b72 crashdump common/daemon.c:46 0x7ffa14fcd72f ??? ???:0 0x7ffa14dc87bb ??? ???:0 0x7ffa14db3534 ??? ???:0 0x5614a491fc71 fmt_wireaddr_internal common/wireaddr.c:255 0x5614a491fc7a fmt_wireaddr_internal_ common/wireaddr.c:257 0x5614a491ea6b type_to_string_ common/type_to_string.c:32 0x5614a490beaa destroy_io_conn connectd/connectd.c:754 0x5614a494a2f1 destroy_conn ccan/ccan/io/poll.c:246 0x5614a494a313 destroy_conn_close_fd ccan/ccan/io/poll.c:252 0x5614a4953804 notify ccan/ccan/tal/tal.c:240 0x5614a49538d6 del_tree ccan/ccan/tal/tal.c:402 0x5614a4953928 del_tree ccan/ccan/tal/tal.c:412 0x5614a4953e07 tal_free ccan/ccan/tal/tal.c:486 0x5614a4908b7a try_connect_one_addr connectd/connectd.c:870 0x5614a490bef1 destroy_io_conn connectd/connectd.c:759 0x5614a494a2f1 destroy_conn ccan/ccan/io/poll.c:246 0x5614a494a313 destroy_conn_close_fd ccan/ccan/io/poll.c:252 0x5614a4953804 notify ccan/ccan/tal/tal.c:240 0x5614a49538d6 del_tree ccan/ccan/tal/tal.c:402 0x5614a4953e07 tal_free ccan/ccan/tal/tal.c:486 0x5614a4948f08 io_close ccan/ccan/io/io.c:450 0x5614a4948f59 do_plan ccan/ccan/io/io.c:401 0x5614a4948fe1 io_ready ccan/ccan/io/io.c:417 0x5614a494a8e6 io_loop ccan/ccan/io/poll.c:453 0x5614a490c12f main connectd/connectd.c:2164 0x7ffa14db509a ??? ???:0 0x5614a4904e99 ??? ???:0 0xffffffffffffffff ??? ???:0 ``` Fixes: #5339 Changelog-Fixed: connectd: occasional crash when we reconnect to a peer quickly. Signed-off-by: Rusty Russell --- connectd/connectd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index fc2b4de933d3..3136ee4d84ec 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -748,6 +748,7 @@ static void destroy_io_conn(struct io_conn *conn, struct connecting *connect) &connect->addrs[connect->addrnum]), connect->connstate, errstr)); connect->addrnum++; + connect->conn = NULL; try_connect_one_addr(connect); } @@ -849,8 +850,7 @@ static void try_connect_one_addr(struct connecting *connect) struct sockaddr_in6 *sa6; #endif - /* In case we fail without a connection, make destroy_io_conn happy */ - connect->conn = NULL; + assert(!connect->conn); /* Out of addresses? */ if (connect->addrnum == tal_count(connect->addrs)) { @@ -1892,6 +1892,7 @@ static void try_connect_peer(struct daemon *daemon, connect->seconds_waited = seconds_waited; connect->addrhint = tal_steal(connect, addrhint); connect->errors = tal_strdup(connect, ""); + connect->conn = NULL; list_add_tail(&daemon->connecting, &connect->list); tal_add_destructor(connect, destroy_connecting); @@ -1944,7 +1945,7 @@ void peer_conn_closed(struct peer *peer) tal_free(peer); /* If we wanted to connect to it, but found it was exiting, try again */ - if (connect) + if (connect && !connect->conn) try_connect_one_addr(connect); } From 83c825945c37e8f4d2789a4aa2df207b36d394cf Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Mon, 30 May 2022 16:51:02 -0400 Subject: [PATCH 0855/1530] connectd: avoid use-after-free upon multiple reconnections by a peer `peer_reconnected` was freeing a `struct peer_reconnected` instance while a pointer to that instance was registered to be passed as an argument to the `retry_peer_connected` callback function. This caused a use-after-free crash when `retry_peer_connected` attempted to reparent the instance to the temporary context. Instead, never have `peer_reconnected` free a `struct peer_reconnected` instance, and only ever allow such an instance to be freed after the `retry_peer_connected` callback has finished with it. To ensure that the instance is freed even if the connection is closed before the callback can be invoked, parent the instance to the connection rather than to the daemon. Absent the need to free `struct peer_reconnected` instances outside of the `retry_peer_connected` callback, there is no use for the `reconnected` hashtable, so remove it as well. See: https://github.com/ElementsProject/lightning/issues/5282#issuecomment-1141454255 Fixes: #5282 Fixes: #5284 Changelog-Fixed: connectd no longer crashes when peers reconnect. --- connectd/connectd.c | 31 +++++++++++++------------------ connectd/connectd.h | 34 ---------------------------------- 2 files changed, 13 insertions(+), 52 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 3136ee4d84ec..9a5d7f211a44 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -93,6 +93,18 @@ struct connecting { u32 seconds_waited; }; +/*~ This is an ad-hoc marshalling structure where we store arguments so we + * can call peer_connected again. */ +struct peer_reconnected { + struct daemon *daemon; + struct node_id id; + struct wireaddr_internal addr; + const struct wireaddr *remote_addr; + struct crypto_state cs; + const u8 *their_features; + bool incoming; +}; + /*~ C programs should generally be written bottom-to-top, with the root * function at the bottom, and functions it calls above it. That avoids * us having to pre-declare functions; but in the case of mutual recursion @@ -230,12 +242,6 @@ static struct io_plan *retry_peer_connected(struct io_conn *conn, &pr->cs, take(pr->their_features), pr->incoming); } -/*~ A common use for destructors is to remove themselves from a data structure */ -static void destroy_peer_reconnected(struct peer_reconnected *pr) -{ - peer_reconnected_htable_del(&pr->daemon->reconnected, pr); -} - /*~ If we already know about this peer, we tell lightningd and it disconnects * the old one. We wait until it tells us that's happened. */ static struct io_plan *peer_reconnected(struct io_conn *conn, @@ -252,27 +258,18 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, status_peer_debug(id, "reconnect"); - /* If we have a previous reconnection, we replace it. */ - pr = peer_reconnected_htable_get(&daemon->reconnected, id); - if (pr) { - peer_reconnected_htable_del(&daemon->reconnected, pr); - tal_free(pr); - } - /* Tell master to kill it: will send peer_disconnect */ msg = towire_connectd_reconnected(NULL, id); daemon_conn_send(daemon->master, take(msg)); /* Save arguments for next time. */ - pr = tal(daemon, struct peer_reconnected); + pr = tal(conn, struct peer_reconnected); pr->daemon = daemon; pr->id = *id; pr->cs = *cs; pr->addr = *addr; pr->remote_addr = tal_dup_or_null(pr, struct wireaddr, remote_addr); pr->incoming = incoming; - peer_reconnected_htable_add(&daemon->reconnected, pr); - tal_add_destructor(pr, destroy_peer_reconnected); /*~ Note that tal_dup_talarr() will do handle the take() of features * (turning it into a simply tal_steal() in those cases). */ @@ -2000,7 +1997,6 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) /* Now delete daemon and those which it has pointers to. */ memleak_remove_region(memtable, daemon, sizeof(daemon)); memleak_remove_htable(memtable, &daemon->peers.raw); - memleak_remove_htable(memtable, &daemon->reconnected.raw); found_leak = dump_memleak(memtable, memleak_status_broken); daemon_conn_send(daemon->master, @@ -2147,7 +2143,6 @@ int main(int argc, char *argv[]) /* Allocate and set up our simple top-level structure. */ daemon = tal(NULL, struct daemon); peer_htable_init(&daemon->peers); - peer_reconnected_htable_init(&daemon->reconnected); memleak_add_helper(daemon, memleak_daemon_cb); list_head_init(&daemon->connecting); timers_init(&daemon->timers, time_mono()); diff --git a/connectd/connectd.h b/connectd/connectd.h index 72161b1220bf..02fe5bf234af 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -126,37 +126,6 @@ HTABLE_DEFINE_TYPE(struct peer, peer_eq_node_id, peer_htable); -/*~ This is an ad-hoc marshalling structure where we store arguments so we - * can call peer_connected again. */ -struct peer_reconnected { - struct daemon *daemon; - struct node_id id; - struct wireaddr_internal addr; - const struct wireaddr *remote_addr; - struct crypto_state cs; - const u8 *their_features; - bool incoming; -}; - -static const struct node_id * -peer_reconnected_keyof(const struct peer_reconnected *pr) -{ - return &pr->id; -} - -static bool peer_reconnected_eq_node_id(const struct peer_reconnected *pr, - const struct node_id *id) -{ - return node_id_eq(&pr->id, id); -} - -/*~ This defines 'struct peer_reconnected_htable'. */ -HTABLE_DEFINE_TYPE(struct peer_reconnected, - peer_reconnected_keyof, - node_id_hash, - peer_reconnected_eq_node_id, - peer_reconnected_htable); - /*~ This is the global state, like `struct lightningd *ld` in lightningd. */ struct daemon { /* Who am I? */ @@ -173,9 +142,6 @@ struct daemon { * have disconnected. */ struct peer_htable peers; - /* Peers which have reconnected, waiting for us to kill existing conns */ - struct peer_reconnected_htable reconnected; - /* Peers we are trying to reach */ struct list_head connecting; From afbddcf7f3b9e2ae95afd2556b1a9ce7595cd780 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 15:59:20 +0930 Subject: [PATCH 0856/1530] lightningd: fix crash on rapid reconnect. Happens occasionally when running `tests/test_connection.py::test_mutual_reconnect_race` (which is too flaky to add, without more fixes): ``` lightningd: lightningd/peer_control.c:1252: peer_active: Assertion `!channel->owner' failed. lightningd: FATAL SIGNAL 6 (version v0.11.0.1-38-g4f167da) 0x5594a41f8f45 send_backtrace common/daemon.c:33 0x5594a41f8fef crashdump common/daemon.c:46 0x7f7cb585c08f ??? /build/glibc-SzIz7B/glibc-2.31/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 0x7f7cb585c00b __GI_raise ../sysdeps/unix/sysv/linux/raise.c:51 0x7f7cb583b858 __GI_abort /build/glibc-SzIz7B/glibc-2.31/stdlib/abort.c:79 0x7f7cb583b728 __assert_fail_base /build/glibc-SzIz7B/glibc-2.31/assert/assert.c:92 0x7f7cb584cfd5 __GI___assert_fail /build/glibc-SzIz7B/glibc-2.31/assert/assert.c:101 0x5594a41b45ca peer_active lightningd/peer_control.c:1252 0x5594a418794c connectd_msg lightningd/connect_control.c:457 0x5594a41cd457 sd_msg_read lightningd/subd.c:556 0x5594a41ccbe5 read_fds lightningd/subd.c:357 0x5594a4269fc2 next_plan ccan/ccan/io/io.c:59 0x5594a426abca do_plan ccan/ccan/io/io.c:407 0x5594a426ac0c io_ready ccan/ccan/io/io.c:417 0x5594a426ceff io_loop ccan/ccan/io/poll.c:453 0x5594a41930d9 io_loop_with_timers lightningd/io_loop_with_timers.c:22 0x5594a4199293 main lightningd/lightningd.c:1181 0x7f7cb583d082 __libc_start_main ../csu/libc-start.c:308 0x5594a416e15d ??? ???:0 0xffffffffffffffff ??? ???:0 ``` Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 47789cdc5e0c..bfb828ba2552 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1252,7 +1252,8 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) case CHANNELD_NORMAL: case CHANNELD_SHUTTING_DOWN: case CLOSINGD_SIGEXCHANGE: - assert(!channel->owner); + /* Maybe old owner was too slow exiting? */ + tal_free(channel->owner); peer_start_channeld(channel, peer_fd, NULL, true, From fd90e5746bf1a669fdedb79071c0a33556b31cef Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 19 Jun 2022 16:01:42 +0930 Subject: [PATCH 0857/1530] connectd: don't keep around more than one old connection. This was fixed in 1c495ca5a83df8cccb62a53cbfd3bc437ca3745c ("connectd: fix accidental handling of old reconnections.") and then reverted by the rework in "connectd: avoid use-after-free upon multiple reconnections by a peer". The latter made the race much less likely, since we cleaned up the reconnecting struct once the connection was hung up by the remote node, but it's still theoretically possible. Signed-off-by: Rusty Russell --- connectd/connectd.c | 17 ++++++++++++++--- connectd/connectd.h | 3 ++- connectd/peer_exchange_initmsg.c | 3 ++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 9a5d7f211a44..07a952732188 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -239,7 +239,8 @@ static struct io_plan *retry_peer_connected(struct io_conn *conn, /*~ Usually the pattern is to return this directly. */ return peer_connected(conn, pr->daemon, &pr->id, &pr->addr, pr->remote_addr, - &pr->cs, take(pr->their_features), pr->incoming); + &pr->cs, take(pr->their_features), pr->incoming, + true); } /*~ If we already know about this peer, we tell lightningd and it disconnects @@ -334,7 +335,8 @@ struct io_plan *peer_connected(struct io_conn *conn, const struct wireaddr *remote_addr, struct crypto_state *cs, const u8 *their_features TAKES, - bool incoming) + bool incoming, + bool retrying) { u8 *msg; struct peer *peer; @@ -344,9 +346,18 @@ struct io_plan *peer_connected(struct io_conn *conn, bool option_gossip_queries; peer = peer_htable_get(&daemon->peers, id); - if (peer) + if (peer) { + /* If we were already retrying, we only get one chance: there + * can be multiple reconnections, and we must not keep around + * stale ones */ + if (retrying) { + if (taken(their_features)) + tal_free(their_features); + return io_close(conn); + } return peer_reconnected(conn, daemon, id, addr, remote_addr, cs, their_features, incoming); + } /* We promised we'd take it by marking it TAKEN above; prepare to free it. */ if (taken(their_features)) diff --git a/connectd/connectd.h b/connectd/connectd.h index 02fe5bf234af..9af5f815d7ca 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -212,7 +212,8 @@ struct io_plan *peer_connected(struct io_conn *conn, const struct wireaddr *remote_addr, struct crypto_state *cs, const u8 *their_features TAKES, - bool incoming); + bool incoming, + bool retrying); /* Called when peer->peer_conn is finally freed */ void peer_conn_closed(struct peer *peer); diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index fa97998382c6..c9009a007714 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -137,7 +137,8 @@ static struct io_plan *peer_init_received(struct io_conn *conn, remote_addr, &peer->cs, take(features), - peer->incoming); + peer->incoming, + false); } static struct io_plan *peer_init_hdr_received(struct io_conn *conn, From 9ab7c8aed395652a2e99b4972f2fc4acf34d8317 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Jun 2022 14:10:04 +0930 Subject: [PATCH 0858/1530] connected/test: fix memleak in test. ``` VALGRIND=1 valgrind -q --error-exitcode=7 --track-origins=yes --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all connectd/test/run-netaddress > /dev/null ==2483395== 16 bytes in 1 blocks are still reachable in loss record 1 of 15 ==2483395== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==2483395== by 0x10D59A: autodata_register_ (autodata.c:20) ==2483395== by 0x10EB26: register_autotype_type_to_string (type_to_string.h:77) ==2483395== by 0x10EB6B: register_one_type_to_string0 (type_to_string.c:8) ==2483395== by 0x188C0C: __libc_csu_init (in /home/rusty/devel/cvs/lightning/connectd/test/run-netaddress) ==2483395== by 0x4A3A00F: (below main) (libc-start.c:264) ==2483395== ==2483395== 40 bytes in 1 blocks are still reachable in loss record 2 of 15 ==2483395== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ... ``` Signed-off-by: Rusty Russell --- connectd/test/run-netaddress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/connectd/test/run-netaddress.c b/connectd/test/run-netaddress.c index 586ec1644c09..f92ea8aa7582 100644 --- a/connectd/test/run-netaddress.c +++ b/connectd/test/run-netaddress.c @@ -231,4 +231,6 @@ int main(int argc, char *argv[]) assert(!IsValid(&wa)); assert(!IsIPv4(&wa)); assert(IsIPv6(&wa)); + + common_shutdown(); } From 2fe17a58376a0740c7f1a3d0c6f84e2ddc07544d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 29 Jun 2022 14:11:10 +0930 Subject: [PATCH 0859/1530] CI: make sure *someone* runs check-units under valgrind! Signed-off-by: Rusty Russell --- .github/workflows/ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 708ba27269b3..0b7d563b73ee 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,9 +28,10 @@ jobs: ARCH: 32 TEST_CMD: "make check-source check-units installcheck" DEVELOPER: 0 - - CFG: "make-EXPERIMENTAL-check" + - CFG: "make-EXPERIMENTAL-check w/ VALGRIND" TEST_CMD: "make check-source check-units installcheck check-gen-updated" EXPERIMENTAL_FEATURES: 1 + VALGRIND: 1 steps: - name: Checkout uses: actions/checkout@v2.0.0 From 9f953b5efb5c36b5f7aa339d3f1e05691a447301 Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Thu, 30 Jun 2022 17:24:41 -0400 Subject: [PATCH 0860/1530] No funding_wscript arg in initial_commit_tx --- common/initial_commit_tx.h | 1 - 1 file changed, 1 deletion(-) diff --git a/common/initial_commit_tx.h b/common/initial_commit_tx.h index e0bc3c645784..7a5edf0f226f 100644 --- a/common/initial_commit_tx.h +++ b/common/initial_commit_tx.h @@ -68,7 +68,6 @@ static inline struct amount_sat commit_tx_base_fee(u32 feerate_per_kw, * initial_commit_tx: create (unsigned) commitment tx to spend the funding tx output * @ctx: context to allocate transaction and @htlc_map from. * @funding, @funding_sats: funding outpoint and amount - * @funding_wscript: scriptPubkey of the funding output * @funding_keys: funding bitcoin keys * @opener: is the LOCAL or REMOTE paying the fee? * @keyset: keys derived for this commit tx. From 50107754a77bac7480ade936453a90b016c27e50 Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Thu, 30 Jun 2022 13:19:09 +0700 Subject: [PATCH 0861/1530] Add README.md --- cln-grpc/README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 cln-grpc/README.md diff --git a/cln-grpc/README.md b/cln-grpc/README.md new file mode 100644 index 000000000000..fbcab85a9779 --- /dev/null +++ b/cln-grpc/README.md @@ -0,0 +1,2 @@ +# cln-grpc +## Secure Networked RPC Interface From ee3f059e800b7ffcd7f0ffc6e5037899132b05cd Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Thu, 30 Jun 2022 13:36:02 +0700 Subject: [PATCH 0862/1530] Update README.md --- cln-grpc/README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/cln-grpc/README.md b/cln-grpc/README.md index fbcab85a9779..760de9624e7b 100644 --- a/cln-grpc/README.md +++ b/cln-grpc/README.md @@ -1,2 +1,15 @@ -# cln-grpc -## Secure Networked RPC Interface +# cln-grpc - Secure Networked RPC Interface + +This plugin provides a standardized API that apps, plugins, and other tools could use to interact with Core Lightning. We always had a JSON-RPC, with a very exhaustive API, but it was exposed only locally over a Unix-domain socket. Some plugins chose to re-expose the API over a variety of protocols, ranging from REST to gRPC, but it was additional work to install them. + +So with v0.11.0, we released a new interface: `cln-grpc`, a Rust-based plugin that exposes the existing interface over the network in a secure manner. The gRPC API is automatically generated from our existing JSON-RPC API, so it has the same low-level and high-level access that app devs are accustomed to but uses a more efficient binary encoding where possible and is secured via mutual TLS authentication. + +To use it, just add the `--grpc-port` option, and it’ll automatically start alongside Core Lightning and generate the appropriate mTLS certificates. To use the gRPC interface, copy the client key and certificate, generate your client bindings from the protobuf definition and connect to the port you specified earlier. + +While all previous built-in plugins were written in C, the `cln-grpc` plugin is written in Rust, a language that will be much more prominent in the project going forward. In order to kick off the use of Rust, we also built a number of crates: + +- [cln-rpc](https://crates.io/crates/cln-rpc): native bindings to the JSON-RPC interface, used for things running on the same system as CLN. +- [cln-plugin](https://crates.io/crates/cln-plugin): a library that facilitates the creation of plugins in Rust, with async/await support, for low-footprint plugins. +- [cln-grpc](https://crates.io/crates/cln-grpc): of course, the library used to create the gRPC plugin can also be used directly as a client library. + +All of these crates are published on crates.io and will be maintained as part of the project moving forward. From 1771b8ec2267e1bb5f6d8cf3c07ada085350f6ee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 30 Jun 2022 15:18:48 +0930 Subject: [PATCH 0863/1530] CI: re-enable checks, by changing errant tab back to spaces. And the Python contrib/ stuff seems to fail under VALGRIND, so attach it to a normal make line. Signed-off-by: Rusty Russell --- .github/workflows/ci.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0b7d563b73ee..60f1dd523a28 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,8 +19,9 @@ jobs: fail-fast: true matrix: include: - - CFG: "make" - TEST_CMD: "make" + - CFG: "make and unit test w/ VALGRIND" + TEST_CMD: "make default check-source" + VALGRIND: 1 - CFG: "make-O3-check" TEST_CMD: "make check-source check-units installcheck check-gen-updated" COPTFLAGS: "-O3" @@ -28,10 +29,9 @@ jobs: ARCH: 32 TEST_CMD: "make check-source check-units installcheck" DEVELOPER: 0 - - CFG: "make-EXPERIMENTAL-check w/ VALGRIND" + - CFG: "make-EXPERIMENTAL-check" TEST_CMD: "make check-source check-units installcheck check-gen-updated" EXPERIMENTAL_FEATURES: 1 - VALGRIND: 1 steps: - name: Checkout uses: actions/checkout@v2.0.0 From bad943da55b367158f5e080baea90754aa50c9a7 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Thu, 30 Jun 2022 18:36:52 +0000 Subject: [PATCH 0864/1530] valgrind: ingore plugin build with rust Ok this should be fixed the following stack trace ``` 2022-06-29T14:19:41.183Z DEBUG lightningd: Command returned result after jcon close ------------------------------- Valgrind errors -------------------------------- Valgrind error file: valgrind-errors.55581 ==55581== Syscall param statx(file_name) points to unaddressable byte(s) ==55581== at 0x4B0188E: statx (statx.c:29) ==55581== by 0x1133481: std::sys::unix::fs::try_statx (weak.rs:178) ==55581== by 0x11265E0: std::fs::buffer_capacity_required (fs.rs:851) ==55581== by 0x112675B: ::read_to_string (fs.rs:644) ==55581== by 0x10DACA8: num_cpus::linux::Cgroup::param (linux.rs:214) ==55581== by 0x10DAB39: num_cpus::linux::Cgroup::quota_us (linux.rs:203) ==55581== by 0x10DA9C2: num_cpus::linux::Cgroup::cpu_quota (linux.rs:188) ==55581== by 0x10DA5A1: num_cpus::linux::load_cgroups (linux.rs:149) ==55581== by 0x10DA23D: num_cpus::linux::init_cgroups (linux.rs:129) ==55581== by 0x10DCDC8: core::ops::function::FnOnce::call_once (function.rs:227) ==55581== by 0x10DC749: std::sync::once::Once::call_once::{{closure}} (once.rs:276) ==55581== by 0x21EE89: std::sync::once::Once::call_inner (once.rs:434) ==55581== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==55581== ==55581== Syscall param statx(buf) points to unaddressable byte(s) ==55581== at 0x4B0188E: statx (statx.c:29) ==55581== by 0x1133481: std::sys::unix::fs::try_statx (weak.rs:178) ==55581== by 0x11265E0: std::fs::buffer_capacity_required (fs.rs:851) ==55581== by 0x112675B: ::read_to_string (fs.rs:644) ==55581== by 0x10DACA8: num_cpus::linux::Cgroup::param (linux.rs:214) ==55581== by 0x10DAB39: num_cpus::linux::Cgroup::quota_us (linux.rs:203) ==55581== by 0x10DA9C2: num_cpus::linux::Cgroup::cpu_quota (linux.rs:188) ==55581== by 0x10DA5A1: num_cpus::linux::load_cgroups (linux.rs:149) ==55581== by 0x10DA23D: num_cpus::linux::init_cgroups (linux.rs:129) ==55581== by 0x10DCDC8: core::ops::function::FnOnce::call_once (function.rs:227) ==55581== by 0x10DC749: std::sync::once::Once::call_once::{{closure}} (once.rs:276) ==55581== by 0x21EE89: std::sync::once::Once::call_inner (once.rs:434) ==55581== Address 0x0 is not stack'd, malloc'd or (recently) free'd ==55581== -------------------------------------------------------------------------------- Leaving base_dir /tmp/ltests-hzt9ppqp intact, it still has test sub-directories with failure details: ['test_peers_1'] ``` Signed-off-by: Vincenzo Palazzo --- contrib/pyln-testing/pyln/testing/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index cc13e097eb3c..899094c5fe4a 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -732,7 +732,7 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai if dsn is not None: self.daemon.opts['wallet'] = dsn if valgrind: - trace_skip_pattern = '*python*,*bitcoin-cli*,*elements-cli*' + trace_skip_pattern = '*python*,*bitcoin-cli*,*elements-cli*,*cln-grpc' if not valgrind_plugins: trace_skip_pattern += ',*plugins*' self.daemon.cmd_prefix = [ From 6d07f4ed85be91d5af0bfffa75895d4b33fd4cc2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 19 May 2022 13:51:49 +0200 Subject: [PATCH 0865/1530] json: Add parser for u32 params --- common/json_tok.c | 12 ++++++++++++ common/json_tok.h | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/common/json_tok.c b/common/json_tok.c index 5ae6880142bf..a91eb63edf61 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -122,6 +122,18 @@ struct command_result *param_sha256(struct command *cmd, const char *name, "should be a 32 byte hex value"); } +struct command_result *param_u32(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + uint32_t **num) +{ + *num = tal(cmd, uint32_t); + if (json_to_u32(buffer, tok, *num)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be an unsigned 32 bit integer"); +} + struct command_result *param_u64(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, uint64_t **num) diff --git a/common/json_tok.h b/common/json_tok.h index e4c59d5ce7e1..2b6f9e724b66 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -68,6 +68,11 @@ struct command_result *param_sha256(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct sha256 **hash); +/* Extract number from this (may be a string, or a number literal) */ +struct command_result *param_u32(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + uint32_t **num); + /* Extract number from this (may be a string, or a number literal) */ struct command_result *param_u64(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, From 185cd81be408320b8e078060dc2d5cff43149f0b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 20 May 2022 11:45:26 +0200 Subject: [PATCH 0866/1530] jsonrpc: Add `mindepth` argument to fundchannel and multifundchannel This will eventually enable us to specify 0 for zeroconf channels. Changelog-Added: JSON-RPC: Added `mindepth` argument to specify the number of confirmations we require for `fundchannel` and `multifundchannel` --- contrib/pyln-client/pyln/client/lightning.py | 7 +++++-- doc/lightning-fundchannel.7.md | 3 ++- doc/lightning-fundchannel_start.7.md | 3 ++- doc/schemas/fundchannel.schema.json | 4 ++++ doc/schemas/fundchannel_start.schema.json | 4 ++++ lightningd/opening_common.c | 7 +++++++ lightningd/opening_control.c | 19 +++++++++++-------- plugins/spender/fundchannel.c | 6 ++++++ plugins/spender/multifundchannel.c | 5 +++++ plugins/spender/multifundchannel.h | 3 +++ 10 files changed, 49 insertions(+), 12 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 51e70ae7ef24..2b1844f8ed97 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -703,7 +703,8 @@ def feerates(self, style, urgent=None, normal=None, slow=None): def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None, utxos=None, push_msat=None, close_to=None, - request_amt=None, compact_lease=None): + request_amt=None, compact_lease=None, + mindepth: Optional[int] = None): """ Fund channel with {id} using {amount} satoshis with feerate of {feerate} (uses default feerate if unset). @@ -728,11 +729,12 @@ def fundchannel(self, node_id, amount, feerate=None, announce=True, "close_to": close_to, "request_amt": request_amt, "compact_lease": compact_lease, + "mindepth": mindepth, } return self.call("fundchannel", payload) def fundchannel_start(self, node_id, amount, feerate=None, announce=True, - close_to=None): + close_to=None, mindepth: Optional[int] = None): """ Start channel funding with {id} for {amount} satoshis with feerate of {feerate} (uses default feerate if unset). @@ -748,6 +750,7 @@ def fundchannel_start(self, node_id, amount, feerate=None, announce=True, "feerate": feerate, "announce": announce, "close_to": close_to, + "mindepth": mindepth, } return self.call("fundchannel_start", payload) diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index 229e484240aa..fe44b2a21f8e 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -89,6 +89,7 @@ On success, an object is returned, containing: - **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` +- **mindepth** (u32, optional): Number of confirmations before we consider the channel active. [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -113,4 +114,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4a8d7c524cfe257f961531929d14d3589efb6ecd182a33e92aade30af90406f8) +[comment]: # ( SHA256STAMP:4bde4785d98f4fdb74ea2415b89ddc864fdf09bd9a30f8056a376aaa668a84b8) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index 7b1ba60d07c0..c0d22abcdf37 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -46,6 +46,7 @@ On success, an object is returned, containing: - **funding_address** (string): The address to send funding to for the channel. DO NOT SEND COINS TO THIS ADDRESS YET. - **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` +- **mindepth** (u32, optional): Number of confirmations before we consider the channel active. The following warnings may also be returned: - **warning_usage**: A warning not to prematurely broadcast the funding transaction (always present!) @@ -82,4 +83,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:acbd9ffb07219bc10e06e8f8bc6772d12ec62650bd30b6ee1084a24f53da2ac2) +[comment]: # ( SHA256STAMP:6dc3dd4d3d65baa617b285fd3f6e746d62e2806635a8f8852fd3a94ef6fe3e6a) diff --git a/doc/schemas/fundchannel.schema.json b/doc/schemas/fundchannel.schema.json index 40ee1deb1df7..63444352f718 100644 --- a/doc/schemas/fundchannel.schema.json +++ b/doc/schemas/fundchannel.schema.json @@ -30,6 +30,10 @@ "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`" + }, + "mindepth": { + "type": "u32", + "description": "Number of confirmations before we consider the channel active." } } } diff --git a/doc/schemas/fundchannel_start.schema.json b/doc/schemas/fundchannel_start.schema.json index a2c9795dc3e1..cbd420a39e75 100644 --- a/doc/schemas/fundchannel_start.schema.json +++ b/doc/schemas/fundchannel_start.schema.json @@ -23,6 +23,10 @@ "warning_usage": { "type": "string", "description": "A warning not to prematurely broadcast the funding transaction (always present!)" + }, + "mindepth": { + "type": "u32", + "description": "Number of confirmations before we consider the channel active." } } } diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index f866a91f5be0..866f135de3f1 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -53,6 +53,13 @@ new_uncommitted_channel(struct peer *peer) uc->fc = NULL; uc->our_config.id = 0; + /* BOLT #2: + * + * The sender: + * - SHOULD set `minimum_depth` to a number of blocks it considers + * reasonable to avoid double-spending of the funding transaction. + */ + uc->minimum_depth = ld->config.anchor_confirms; memset(&uc->cid, 0xFF, sizeof(uc->cid)); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 5eda78580754..8754ba647a81 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -891,13 +891,6 @@ bool peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) &max_to_self_delay, &min_effective_htlc_capacity); - /* BOLT #2: - * - * The sender: - * - SHOULD set `minimum_depth` to a number of blocks it considers - * reasonable to avoid double-spending of the funding transaction. - */ - uc->minimum_depth = peer->ld->config.anchor_confirms; msg = towire_openingd_init(NULL, chainparams, @@ -1057,7 +1050,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, struct node_id *id; struct peer *peer; bool *announce_channel; - u32 *feerate_per_kw; + u32 *feerate_per_kw, *mindepth; struct amount_sat *amount; struct amount_msat *push_msat; @@ -1077,6 +1070,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, p_opt_def("announce", param_bool, &announce_channel, true), p_opt("close_to", param_bitcoin_address, &fc->our_upfront_shutdown_script), p_opt("push_msat", param_msat, &push_msat), + p_opt_def("mindepth", param_u32, &mindepth, cmd->ld->config.anchor_confirms), NULL)) return command_param_failed(); @@ -1167,6 +1161,15 @@ static struct command_result *json_fundchannel_start(struct command *cmd, peer->uncommitted_channel->fc = tal_steal(peer->uncommitted_channel, fc); fc->uc = peer->uncommitted_channel; + /* BOLT #2: + * + * The sender: + * - SHOULD set `minimum_depth` to a number of blocks it considers + * reasonable to avoid double-spending of the funding transaction. + */ + assert(mindepth != NULL); + fc->uc->minimum_depth = *mindepth; + /* Needs to be stolen away from cmd */ if (fc->our_upfront_shutdown_script) fc->our_upfront_shutdown_script diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index 91f6b72ff2b0..1feb639afd35 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -44,6 +44,7 @@ json_fundchannel(struct command *cmd, const jsmntok_t *close_to; const jsmntok_t *request_amt; const jsmntok_t *compact_lease; + const jsmntok_t *mindepth; struct out_req *req; @@ -58,6 +59,7 @@ json_fundchannel(struct command *cmd, p_opt("close_to", param_tok, &close_to), p_opt("request_amt", param_tok, &request_amt), p_opt("compact_lease", param_tok, &compact_lease), + p_opt("mindepth", param_tok, &mindepth), NULL)) return command_param_failed(); @@ -84,6 +86,10 @@ json_fundchannel(struct command *cmd, json_add_tok(req->js, "request_amt", request_amt, buf); json_add_tok(req->js, "compact_lease", compact_lease, buf); } + + if (mindepth) + json_add_tok(req->js, "mindepth", mindepth, buf); + json_object_end(req->js); json_array_end(req->js); if (feerate) diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index ac0cd7ae9a81..7cdfaf72f250 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1113,12 +1113,16 @@ fundchannel_start_dest(struct multifundchannel_destination *dest) json_add_string(req->js, "feerate", mfc->cmtmt_feerate_str); else if (mfc->feerate_str) json_add_string(req->js, "feerate", mfc->feerate_str); + json_add_bool(req->js, "announce", dest->announce); json_add_string(req->js, "push_msat", fmt_amount_msat(tmpctx, dest->push_msat)); if (dest->close_to_str) json_add_string(req->js, "close_to", dest->close_to_str); + if (dest->mindepth) + json_add_u32(req->js, "mindepth", *dest->mindepth); + send_outreq(cmd->plugin, req); } @@ -1890,6 +1894,7 @@ param_destinations_array(struct command *cmd, const char *name, p_opt_def("request_amt", param_sat, &request_amt, AMOUNT_SAT(0)), p_opt("compact_lease", param_lease_hex, &rates), + p_opt("mindepth", param_u32, &dest->mindepth), NULL)) return command_param_failed(); diff --git a/plugins/spender/multifundchannel.h b/plugins/spender/multifundchannel.h index e9cfe5d46633..e65c2785c8c7 100644 --- a/plugins/spender/multifundchannel.h +++ b/plugins/spender/multifundchannel.h @@ -149,6 +149,9 @@ struct multifundchannel_destination { /* Channel lease rates that we expect the peer to respond with */ struct lease_rates *rates; + + /* Number of blocks to wait before sending `channel_ready`. */ + u32 *mindepth; }; From e4511452aca7f2dd8135a8e3d7dc84dfb17596a1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 14 Apr 2022 14:35:39 +0200 Subject: [PATCH 0867/1530] bolt: Reflect the zeroconf featurebits in code --- common/features.c | 35 +++++++++++++++++++++++++++++++++-- common/features.h | 10 ++++++++++ lightningd/lightningd.c | 2 ++ tests/test_misc.py | 4 ++++ tests/utils.py | 4 ++-- 5 files changed, 51 insertions(+), 4 deletions(-) diff --git a/common/features.c b/common/features.c index bd7db0bfb4a1..14c0a39142e9 100644 --- a/common/features.c +++ b/common/features.c @@ -89,6 +89,26 @@ static const struct feature_style feature_styles[] = { [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, [BOLT11_FEATURE] = FEATURE_REPRESENT, [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} }, + /* FIXME: Currently not explicitly signalled, but we do + * support it for zeroconf */ + { OPT_SCID_ALIAS, + .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, + [BOLT11_FEATURE] = FEATURE_DONT_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} }, + + /* Zeroconf is always signalled in `init`, but we still + * negotiate on a per-channel basis when calling `fundchannel` + * with the `mindepth` parameter, and accept a channel with + * the `open_channel` hook and its return value for + * `mindepth`. + */ + { OPT_ZEROCONF, + .copy_style = { + [INIT_FEATURE] = FEATURE_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, + [BOLT11_FEATURE] = FEATURE_DONT_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} }, { OPT_SHUTDOWN_ANYSEGWIT, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, @@ -422,9 +442,9 @@ const char *feature_name(const tal_t *ctx, size_t f) "option_want_peer_backup", /* 40/41 */ /* https://github.com/lightningnetwork/lightning-rfc/pull/881 */ "option_provide_peer_backup", /* https://github.com/lightningnetwork/lightning-rfc/pull/881 */ NULL, - NULL, + "option_scid_alias", /* https://github.com/lightning/bolts/pull/910 */ "option_payment_metadata", - NULL, /* 50/51 */ + "option_zeroconf", /* 50/51, https://github.com/lightning/bolts/pull/910 */ NULL, "option_keysend", NULL, @@ -546,3 +566,14 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits) } return fmt; } + +struct feature_set *feature_set_dup(const tal_t *ctx, + const struct feature_set *other) +{ + struct feature_set *res = tal(ctx, struct feature_set); + + for (size_t i = 0; i < ARRAY_SIZE(res->bits); i++) + res->bits[i] = tal_dup_talarr(res, u8, other->bits[i]); + + return res; +} diff --git a/common/features.h b/common/features.h index 3bea463565c8..58b57c48f3c0 100644 --- a/common/features.h +++ b/common/features.h @@ -81,6 +81,9 @@ bool featurebits_eq(const u8 *f1, const u8 *f2); /* Good for debugging: returns comma-separated string of bits. */ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); +struct feature_set *feature_set_dup(const tal_t *ctx, + const struct feature_set *other); + /* BOLT #9: * * Flags are numbered from the least-significant bit, at bit 0 (i.e. 0x1, @@ -134,6 +137,13 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); */ #define OPT_DUAL_FUND 28 +/* BOLT-519be05f61e2c35ddf95b731203f89b4ee0946c3 #9: + * | 46/47 | `option_scid_alias` | ... IN ... + * | 50/51 | `option_eroconf` | ... IN ... + */ +#define OPT_SCID_ALIAS 46 +#define OPT_ZEROCONF 50 + /* BOLT-quiescent #9: * | 34/35 | `option_quiesce` | ... IN ... */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 45e71ed1426f..e54610f0095c 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -824,6 +824,8 @@ static struct feature_set *default_features(const tal_t *ctx) OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY), OPTIONAL_FEATURE(OPT_SHUTDOWN_ANYSEGWIT), OPTIONAL_FEATURE(OPT_PAYMENT_METADATA), + OPTIONAL_FEATURE(OPT_SCID_ALIAS), + OPTIONAL_FEATURE(OPT_ZEROCONF), #if EXPERIMENTAL_FEATURES OPTIONAL_FEATURE(OPT_ANCHOR_OUTPUTS), OPTIONAL_FEATURE(OPT_QUIESCE), diff --git a/tests/test_misc.py b/tests/test_misc.py index 5a86cb8359a7..b9357944757a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1819,9 +1819,13 @@ def test_list_features_only(node_factory): expected += ['option_shutdown_anysegwit/odd'] expected += ['option_quiesce/odd'] expected += ['option_onion_messages/odd'] + expected += ['option_scid_alias/odd'] + expected += ['option_zeroconf/odd'] expected += ['supports_open_accept_channel_type'] else: expected += ['option_shutdown_anysegwit/odd'] + expected += ['option_scid_alias/odd'] + expected += ['option_zeroconf/odd'] assert features == expected diff --git a/tests/utils.py b/tests/utils.py index 0af3afadd6df..ef1a18b152fa 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -37,7 +37,7 @@ def hex_bits(features): def expected_peer_features(wumbo_channels=False, extra=[]): """Return the expected peer features hexstring for this configuration""" - features = [1, 5, 7, 8, 11, 13, 14, 17, 27] + features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 47, 51] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39] @@ -59,7 +59,7 @@ def expected_peer_features(wumbo_channels=False, extra=[]): # features for the 'node' and the 'peer' feature sets def expected_node_features(wumbo_channels=False, extra=[]): """Return the expected node features hexstring for this configuration""" - features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 55] + features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 47, 51, 55] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39] From adbb9770534e60efc1e4a0b31f1dbc570259f8fd Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 20 May 2022 15:41:56 +0200 Subject: [PATCH 0868/1530] openingd: If we have negotiated zeroconf we use our mindepth With `option_zeroconf` we may now send `channel_ready` at any time we want, rendering the `mindepth` parameter a mere heads up. We ignore it in favor of our own value, since we plan to trigger releasing the `channel_ready` once we reach our own depth. --- openingd/openingd.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/openingd/openingd.c b/openingd/openingd.c index 8870c34d9919..fd9d5fa844e8 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -312,6 +312,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) struct tlv_open_channel_tlvs *open_tlvs; struct tlv_accept_channel_tlvs *accept_tlvs; char *err_reason; + u32 their_mindepth; status_debug("funder_channel_start"); if (!setup_channel_funder(state)) @@ -385,7 +386,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) &state->remoteconf.max_htlc_value_in_flight, &state->remoteconf.channel_reserve, &state->remoteconf.htlc_minimum, - &state->minimum_depth, + &their_mindepth, &state->remoteconf.to_self_delay, &state->remoteconf.max_accepted_htlcs, &state->their_funding_pubkey, @@ -401,6 +402,17 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) } set_remote_upfront_shutdown(state, accept_tlvs->upfront_shutdown_script); + status_debug( + "accept_channel: max_htlc_value_in_flight=%s, channel_reserve=%s, " + "htlc_minimum=%s, minimum_depth=%d", + type_to_string(tmpctx, struct amount_msat, + &state->remoteconf.max_htlc_value_in_flight), + type_to_string(tmpctx, struct amount_sat, + &state->remoteconf.channel_reserve), + type_to_string(tmpctx, struct amount_msat, + &state->remoteconf.htlc_minimum), + their_mindepth); + /* BOLT #2: * - if `channel_type` is set, and `channel_type` was set in * `open_channel`, and they are not equal types: @@ -461,6 +473,20 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) &state->our_funding_pubkey, &state->their_funding_pubkey)); + /* If we have negotiated `option_zeroconf` then we're allowed + * to send `channel_ready` whenever we want. So ignore their + * `minimum_depth` and use ours instead. Otherwise we use the + * old behavior of using their value and both side will wait + * for that number of confirmations. */ + if (feature_negotiated(state->our_features, state->their_features, + OPT_ZEROCONF)) { + status_debug( + "We negotiated option_zeroconf, using our minimum_depth=%d", + state->minimum_depth); + } else { + state->minimum_depth = their_mindepth; + } + /* Update the billboard with our infos */ peer_billboard(false, "Funding channel start: awaiting funding_txid with output to %s", From 147787319084382e4a7141163d52aa632bc99faa Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 20 May 2022 15:43:56 +0200 Subject: [PATCH 0869/1530] plugin: Allow plugins to customize the mindepth in accept_channel This is the counterpart of the `mindepth` parameter in `fundchannel` and friends. Allows dynamic lookups of `node_id` and selectively opting into `option_zeroconf` being used. Changelog-Added: plugin: The `openchannel` hook may return a `mindepth` indicating how many confirmations are required. --- lightningd/opening_control.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 8754ba647a81..8449edf9d396 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -701,6 +701,7 @@ openchannel_hook_deserialize(struct openchannel_hook_payload *payload, const jsmntok_t *t_result = json_get_member(buffer, toks, "result"); const jsmntok_t *t_errmsg = json_get_member(buffer, toks, "error_message"); const jsmntok_t *t_closeto = json_get_member(buffer, toks, "close_to"); + const jsmntok_t *t_mindepth = json_get_member(buffer, toks, "mindepth"); if (!t_result) fatal("Plugin returned an invalid response to the" @@ -752,6 +753,16 @@ openchannel_hook_deserialize(struct openchannel_hook_payload *payload, break; } } + + if (t_mindepth != NULL) { + json_to_u32(buffer, t_mindepth, &payload->uc->minimum_depth); + log_debug( + openingd->ld->log, + "Setting mindepth=%d for this channel as requested by " + "the openchannel hook", + payload->uc->minimum_depth); + } + return true; } From 8609f9e00d2dfe0c36697550c76a606f381afd10 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 20 May 2022 15:45:54 +0200 Subject: [PATCH 0870/1530] pytest: Test the `mindepth` customizations of `fundchannel` and hook Just test that we can customize, and we'll add mindepth=0 support in the next couple of commits. --- tests/plugins/zeroconf-selective.py | 34 +++++++++++++++++++++ tests/test_opening.py | 47 +++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100755 tests/plugins/zeroconf-selective.py diff --git a/tests/plugins/zeroconf-selective.py b/tests/plugins/zeroconf-selective.py new file mode 100755 index 000000000000..8d3cb12d02fd --- /dev/null +++ b/tests/plugins/zeroconf-selective.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +"""Use the openchannel hook to selectively opt-into zeroconf +""" + +from pyln.client import Plugin + +plugin = Plugin() + + +@plugin.hook('openchannel') +def on_openchannel(openchannel, plugin, **kwargs): + plugin.log(repr(openchannel)) + mindepth = int(plugin.options['zeroconf-mindepth']['value']) + + if openchannel['id'] == plugin.options['zeroconf-allow']['value']: + plugin.log(f"This peer is in the zeroconf allowlist, setting mindepth={mindepth}") + return {'result': 'continue', 'mindepth': mindepth} + else: + return {'result': 'continue'} + + +plugin.add_option( + 'zeroconf-allow', + 'A node_id to allow zeroconf channels from', + '03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f' +) + +plugin.add_option( + 'zeroconf-mindepth', + 0, + 'Number of confirmations to require from allowlisted peers', +) + +plugin.run() diff --git a/tests/test_opening.py b/tests/test_opening.py index c1c3dfa573a4..589fb5ab7755 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -5,6 +5,7 @@ only_one, wait_for, sync_blockheight, first_channel_id, calc_lease_fee ) +from pathlib import Path import pytest import re import unittest @@ -1187,3 +1188,49 @@ def test_funder_contribution_limits(node_factory, bitcoind): l1.fundchannel(l3, 10**7) assert l3.daemon.is_in_log('Policy .* returned funding amount of 50000sat') assert l3.daemon.is_in_log(r'calling `signpsbt` .* 7 inputs') + + +def test_zeroconf_mindepth(bitcoind, node_factory): + """Check that funder/fundee can customize mindepth. + + Zeroconf will use this to set the mindepth to 0, which coupled + with an artificial depth=0 event that will result in an immediate + `channel_ready` being sent. + + """ + plugin_path = Path(__file__).parent / "plugins" / "zeroconf-selective.py" + + l1, l2 = node_factory.get_nodes(2, opts=[ + {}, + { + 'plugin': str(plugin_path), + 'zeroconf-allow': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518', + 'zeroconf-mindepth': '2', + }, + ]) + + # Try to open a mindepth=6 channel + l1.fundwallet(10**6) + + l1.connect(l2) + assert (int(l1.rpc.listpeers()['peers'][0]['features'], 16) >> 50) & 0x02 != 0 + + # Now start the negotiation, l1 should have negotiated zeroconf, + # and use their own mindepth=6, while l2 uses mindepth=2 from the + # plugin + l1.rpc.fundchannel(l2.info['id'], 'all', mindepth=6) + + assert l1.db.query('SELECT minimum_depth FROM channels') == [{'minimum_depth': 6}] + assert l2.db.query('SELECT minimum_depth FROM channels') == [{'minimum_depth': 2}] + + bitcoind.generate_block(2, wait_for_mempool=1) # Confirm on the l2 side. + l2.daemon.wait_for_log(r'peer_out WIRE_FUNDING_LOCKED') + # l1 should not be sending funding_locked/channel_ready yet, it is + # configured to wait for 6 confirmations. + assert not l1.daemon.is_in_log(r'peer_out WIRE_FUNDING_LOCKED') + + bitcoind.generate_block(4) # Confirm on the l2 side. + l1.daemon.wait_for_log(r'peer_out WIRE_FUNDING_LOCKED') + + wait_for(lambda: l1.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") + wait_for(lambda: l2.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") From 3fbaac3fdbadbae51d3342a7d5d6a282fc9f54ad Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 25 Apr 2022 12:56:10 +0200 Subject: [PATCH 0871/1530] jsonrpc: Add option_zeroconf handling to `listpeers` --- doc/lightning-listpeers.7.md | 4 ++-- doc/schemas/listpeers.schema.json | 3 ++- lightningd/peer_control.c | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 647d3bdfcda4..d957fee4763b 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -46,7 +46,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **state** (string): the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") - **opener** (string): Who initiated the channel (one of "local", "remote") - **features** (array of strings): - - BOLT #9 features which apply to this channel (one of "option_static_remotekey", "option_anchor_outputs") + - BOLT #9 features which apply to this channel (one of "option_static_remotekey", "option_anchor_outputs", "option_zeroconf") - **scratch_txid** (txid, optional): The txid we would use if we went onchain now - **feerate** (object, optional): Feerates for the current tx: - **perkw** (u32): Feerate per 1000 weight (i.e kSipa) @@ -381,4 +381,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:e6829e8ced923131b95bcfa4f366dd04286fe85485039e9ebc89e79899937df6) +[comment]: # ( SHA256STAMP:2e8bcc66531b2dce44b94c42852b624bdd9435cc63495fc799458fa5522f0ea9) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index f9ac1cad27f9..2979f78cce52 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -327,7 +327,8 @@ "type": "string", "enum": [ "option_static_remotekey", - "option_anchor_outputs" + "option_anchor_outputs", + "option_zeroconf" ], "description": "BOLT #9 features which apply to this channel" } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index bfb828ba2552..f68345b2f1d8 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -749,6 +749,8 @@ static void json_add_channel(struct lightningd *ld, json_add_string(response, NULL, "option_static_remotekey"); if (channel_has(channel, OPT_ANCHOR_OUTPUTS)) json_add_string(response, NULL, "option_anchor_outputs"); + if (channel_has(channel, OPT_ZEROCONF)) + json_add_string(response, NULL, "option_zeroconf"); json_array_end(response); if (!amount_sat_sub(&peer_funded_sats, channel->funding_sats, From 9d3cb95489a2d74f7fc624b9a1d8ad074e9fd66a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 21 Apr 2022 13:24:53 +0200 Subject: [PATCH 0872/1530] wire: Add funding_locked tlv patch from PR lightning/bolts#910 Minimal set of changes to update the peer_wire.csv to include the TLV field in the `funding_locked` message, and add type 1=alias from that PR too. --- channeld/channeld.c | 18 ++++++++++------- openingd/dualopend.c | 10 +++++----- wire/extracted_peer_06_zeroconf.patch | 14 ++++++++++++++ wire/peer_wire.csv | 3 +++ wire/test/run-peer-wire.c | 28 +++++++++++++++++++++------ 5 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 wire/extracted_peer_06_zeroconf.patch diff --git a/channeld/channeld.c b/channeld/channeld.c index 72cd437bfdd2..336f849bbed5 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -587,7 +587,7 @@ static void channel_announcement_negotiate(struct peer *peer) static void handle_peer_funding_locked(struct peer *peer, const u8 *msg) { struct channel_id chanid; - + struct tlv_funding_locked_tlvs *tlvs; /* BOLT #2: * * A node: @@ -603,8 +603,8 @@ static void handle_peer_funding_locked(struct peer *peer, const u8 *msg) return; peer->old_remote_per_commit = peer->remote_per_commit; - if (!fromwire_funding_locked(msg, &chanid, - &peer->remote_per_commit)) + if (!fromwire_funding_locked(msg, msg, &chanid, + &peer->remote_per_commit, &tlvs)) peer_failed_warn(peer->pps, &peer->channel_id, "Bad funding_locked %s", tal_hex(msg, msg)); @@ -2928,13 +2928,14 @@ static void peer_reconnect(struct peer *peer, && peer->next_index[LOCAL] == 1 && next_commitment_number == 1) { u8 *msg; + struct tlv_funding_locked_tlvs *tlvs = tlv_funding_locked_tlvs_new(tmpctx); status_debug("Retransmitting funding_locked for channel %s", type_to_string(tmpctx, struct channel_id, &peer->channel_id)); /* Contains per commit point #1, for first post-opening commit */ msg = towire_funding_locked(NULL, &peer->channel_id, - &peer->next_local_per_commit); + &peer->next_local_per_commit, tlvs); peer_write(peer->pps, take(msg)); } @@ -3234,9 +3235,12 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) peer->next_index[LOCAL], type_to_string(tmpctx, struct pubkey, &peer->next_local_per_commit)); - msg = towire_funding_locked(NULL, - &peer->channel_id, - &peer->next_local_per_commit); + struct tlv_funding_locked_tlvs *tlvs = + tlv_funding_locked_tlvs_new(tmpctx); + + msg = towire_funding_locked( + NULL, &peer->channel_id, + &peer->next_local_per_commit, tlvs); peer_write(peer->pps, take(msg)); peer->funding_locked[LOCAL] = true; diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 8c425af01b5c..14b1b13bade8 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1132,8 +1132,9 @@ static u8 *handle_funding_locked(struct state *state, u8 *msg) { struct channel_id cid; struct pubkey remote_per_commit; + struct tlv_funding_locked_tlvs *tlvs; - if (!fromwire_funding_locked(msg, &cid, &remote_per_commit)) + if (!fromwire_funding_locked(tmpctx, msg, &cid, &remote_per_commit, &tlvs)) open_err_fatal(state, "Bad funding_locked %s", tal_hex(msg, msg)); @@ -3392,13 +3393,12 @@ static void send_funding_locked(struct state *state) { u8 *msg; struct pubkey next_local_per_commit; - + struct tlv_funding_locked_tlvs *tlvs = tlv_funding_locked_tlvs_new(tmpctx); /* Figure out the next local commit */ hsm_per_commitment_point(1, &next_local_per_commit); - msg = towire_funding_locked(NULL, - &state->channel_id, - &next_local_per_commit); + msg = towire_funding_locked(NULL, &state->channel_id, + &next_local_per_commit, tlvs); peer_write(state->pps, take(msg)); state->funding_locked[LOCAL] = true; diff --git a/wire/extracted_peer_06_zeroconf.patch b/wire/extracted_peer_06_zeroconf.patch new file mode 100644 index 000000000000..5084b00cb7e3 --- /dev/null +++ b/wire/extracted_peer_06_zeroconf.patch @@ -0,0 +1,14 @@ +diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv +index a028ddc66..fc24b61ef 100644 +--- a/wire/peer_wire.csv ++++ b/wire/peer_wire.csv +@@ -126,6 +126,9 @@ msgdata,funding_signed,signature,signature, + msgtype,funding_locked,36 + msgdata,funding_locked,channel_id,channel_id, + msgdata,funding_locked,next_per_commitment_point,point, ++msgdata,funding_locked,tlvs,funding_locked_tlvs, ++tlvtype,funding_locked_tlvs,alias,1 ++tlvdata,funding_locked_tlvs,alias,scid,short_channel_id, + msgtype,open_channel2,64 + msgdata,open_channel2,chain_hash,chain_hash, + msgdata,open_channel2,channel_id,channel_id, diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 497d43b52fe8..d400da1b8a67 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -126,6 +126,9 @@ msgdata,funding_signed,signature,signature, msgtype,funding_locked,36 msgdata,funding_locked,channel_id,channel_id, msgdata,funding_locked,next_per_commitment_point,point, +msgdata,funding_locked,tlvs,funding_locked_tlvs, +tlvtype,funding_locked_tlvs,alias,1 +tlvdata,funding_locked_tlvs,alias,scid,short_channel_id, msgtype,open_channel2,64 msgdata,open_channel2,chain_hash,chain_hash, msgdata,open_channel2,channel_id,channel_id, diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 29c71fd484f5..e8de0a6adb7a 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -7,6 +7,7 @@ #include "common/amount.c" #include "common/channel_id.c" #include "common/node_id.c" +#include "wire/tlvstream.h" #include @@ -34,6 +35,11 @@ static void set_node_id(struct node_id *id) memset(id->k, 2, sizeof(id->k)); } +static void set_scid(struct short_channel_id *scid) +{ + memset(scid, 2, sizeof(struct short_channel_id)); +} + /* Size up to field. */ #define upto_field(p, field) \ ((char *)&(p)->field - (char *)(p)) @@ -146,6 +152,7 @@ struct msg_channel_update_opt_htlc_max { struct msg_funding_locked { struct channel_id channel_id; struct pubkey next_per_commitment_point; + struct tlv_funding_locked_tlvs *tlvs; }; struct msg_announcement_signatures { struct channel_id channel_id; @@ -463,20 +470,23 @@ static struct msg_channel_update_opt_htlc_max } static void *towire_struct_funding_locked(const tal_t *ctx, - const struct msg_funding_locked *s) + const struct msg_funding_locked *s) { return towire_funding_locked(ctx, &s->channel_id, - &s->next_per_commitment_point); + &s->next_per_commitment_point, + s->tlvs); } static struct msg_funding_locked *fromwire_struct_funding_locked(const tal_t *ctx, const void *p) { struct msg_funding_locked *s = tal(ctx, struct msg_funding_locked); - if (fromwire_funding_locked(p, + if (fromwire_funding_locked(ctx, + p, &s->channel_id, - &s->next_per_commitment_point)) + &s->next_per_commitment_point, + &s->tlvs)) return s; return tal_free(s); } @@ -801,7 +811,9 @@ static bool channel_announcement_eq(const struct msg_channel_announcement *a, static bool funding_locked_eq(const struct msg_funding_locked *a, const struct msg_funding_locked *b) { - return memcmp(a, b, sizeof(*a)) == 0; + return eq_upto(a, b, tlvs) && + memeq(a->tlvs->alias, sizeof(a->tlvs->alias), b->tlvs->alias, + sizeof(b->tlvs->alias)); } static bool announcement_signatures_eq(const struct msg_announcement_signatures *a, @@ -1044,12 +1056,16 @@ int main(int argc, char *argv[]) test_corruption(&ca, ca2, channel_announcement); memset(&fl, 2, sizeof(fl)); + fl.tlvs = tlv_funding_locked_tlvs_new(ctx); + fl.tlvs->alias = tal(ctx, struct short_channel_id); + set_scid(fl.tlvs->alias); set_pubkey(&fl.next_per_commitment_point); msg = towire_struct_funding_locked(ctx, &fl); fl2 = fromwire_struct_funding_locked(ctx, msg); assert(funding_locked_eq(&fl, fl2)); - test_corruption(&fl, fl2, funding_locked); + /* FIXME: Corruptions in the TLV can still parse correctly, but won't be equal. */ + /*test_corruption_tlv(&fl, fl2, funding_locked);*/ memset(&as, 2, sizeof(as)); From de1c0b51f024daf79b4414d9a70865e63e474db2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 22 Apr 2022 12:57:01 +0200 Subject: [PATCH 0873/1530] zeroconf: Add alias_remote and alias_local to channel and DB `alias_local` is generated locally and sent to the peer so it knows what we're calling the channel, while `alias_remote` is received by the peer so we know what to include in routehints when generating invoices. --- lightningd/channel.c | 4 ++++ lightningd/channel.h | 8 +++++++ lightningd/dual_open_control.c | 7 ++++++ lightningd/opening_control.c | 7 ++++++ wallet/db.c | 8 +++++++ wallet/test/run-wallet.c | 2 ++ wallet/wallet.c | 39 ++++++++++++++++++++++++++++++---- 7 files changed, 71 insertions(+), 4 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 789d75554161..4a82ae956e14 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -352,6 +352,8 @@ struct channel *new_channel(struct peer *peer, u64 dbid, bool remote_funding_locked, /* NULL or stolen */ struct short_channel_id *scid, + struct short_channel_id *alias_local STEALS, + struct short_channel_id *alias_remote STEALS, struct channel_id *cid, struct amount_msat our_msat, struct amount_msat msat_to_us_min, @@ -437,6 +439,8 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->our_funds = our_funds; channel->remote_funding_locked = remote_funding_locked; channel->scid = tal_steal(channel, scid); + channel->alias[LOCAL] = tal_steal(channel, alias_local); + channel->alias[REMOTE] = tal_steal(channel, alias_remote); /* Haven't gotten one yet. */ channel->cid = *cid; channel->our_msat = our_msat; channel->msat_to_us_min = msat_to_us_min; diff --git a/lightningd/channel.h b/lightningd/channel.h index db81ce8c484c..18d9e36064a1 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -135,6 +135,12 @@ struct channel { /* Channel if locked locally. */ struct short_channel_id *scid; + /* Alias used for option_zeroconf, or option_scid_alias, if + * present. LOCAL are all the alias we told the peer about and + * REMOTE is one of the aliases we got from the peer and we'll + * use in a routehint. */ + struct short_channel_id *alias[NUM_SIDES]; + struct channel_id cid; /* Amount going to us, not counting unfinished HTLCs; if we have one. */ @@ -278,6 +284,8 @@ struct channel *new_channel(struct peer *peer, u64 dbid, bool remote_funding_locked, /* NULL or stolen */ struct short_channel_id *scid STEALS, + struct short_channel_id *alias_local STEALS, + struct short_channel_id *alias_remote STEALS, struct channel_id *cid, struct amount_msat our_msatoshi, struct amount_msat msatoshi_to_us_min, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 86496d13d81b..595fad42a544 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -33,6 +33,7 @@ #include #include #include +#include struct commit_rcvd { struct channel *channel; @@ -1239,6 +1240,12 @@ wallet_commit_channel(struct lightningd *ld, = p2wpkh_for_keyidx(channel, channel->peer->ld, channel->final_key_idx); + /* Can't have gotten their alias for this channel yet. */ + channel->alias[REMOTE] = NULL; + /* We do generate one ourselves however. */ + channel->alias[LOCAL] = tal(channel, struct short_channel_id); + randombytes_buf(channel->alias[LOCAL], sizeof(struct short_channel_id)); + channel->remote_upfront_shutdown_script = tal_steal(channel, remote_upfront_shutdown_script); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 8449edf9d396..57240af931d1 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -29,6 +29,7 @@ #include #include #include +#include #include void json_add_uncommitted_channel(struct json_stream *response, @@ -99,6 +100,7 @@ wallet_commit_channel(struct lightningd *ld, s64 final_key_idx; u64 static_remotekey_start; u32 lease_start_blockheight = 0; /* No leases on v1 */ + struct short_channel_id *alias_local; /* We cannot both be the fundee *and* have a `fundchannel_start` * command running! @@ -158,6 +160,9 @@ wallet_commit_channel(struct lightningd *ld, else static_remotekey_start = 0x7FFFFFFFFFFFFFFF; + alias_local = tal(NULL, struct short_channel_id); + randombytes_buf(alias_local, sizeof(struct short_channel_id)); + channel = new_channel(uc->peer, uc->dbid, NULL, /* No shachain yet */ CHANNELD_AWAITING_LOCKIN, @@ -174,6 +179,8 @@ wallet_commit_channel(struct lightningd *ld, local_funding, false, /* !remote_funding_locked */ NULL, /* no scid yet */ + alias_local, /* But maybe we have an alias we want to use? */ + NULL, /* They haven't told us an alias yet */ cid, /* The three arguments below are msatoshi_to_us, * msatoshi_to_us_min, and msatoshi_to_us_max. diff --git a/wallet/db.c b/wallet/db.c index 04497400dcdb..5ed908d3ed6c 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -875,6 +875,14 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE forwarded_payments ADD forward_style INTEGER DEFAULT NULL"), NULL}, /* "description" is used for label, so we use "paydescription" here */ {SQL("ALTER TABLE payments ADD paydescription TEXT;"), NULL}, + /* Alias we sent to the remote side, for zeroconf and + * option_scid_alias, can be a list of short_channel_ids if + * required, but keeping it a single SCID for now. */ + {SQL("ALTER TABLE channels ADD alias_local BIGINT DEFAULT NULL"), NULL}, + /* Alias we received from the peer, and which we should be using + * in routehints in invoices. The peer will remember all the + * aliases, but we only ever need one. */ + {SQL("ALTER TABLE channels ADD alias_remote BIGINT DEFAULT NULL"), NULL}, }; /** diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 3c7802e2ba98..f3f636fce31a 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1589,6 +1589,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) funding_sats, AMOUNT_MSAT(0), our_sats, 0, false, + NULL, /* alias[LOCAL] */ + NULL, /* alias[REMOTE] */ &cid, AMOUNT_MSAT(3333333000), AMOUNT_MSAT(33333), diff --git a/wallet/wallet.c b/wallet/wallet.c index 2c583f94bbe0..a6355a62ecec 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1250,7 +1250,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm struct channel_info channel_info; struct fee_states *fee_states; struct height_states *height_states; - struct short_channel_id *scid; + struct short_channel_id *scid, *alias[NUM_SIDES]; struct channel_id cid; struct channel *chan; u64 peer_dbid; @@ -1291,6 +1291,20 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm scid = NULL; } + if (!db_col_is_null(stmt, "alias_local")) { + alias[LOCAL] = tal(tmpctx, struct short_channel_id); + alias[LOCAL]->u64 = db_col_u64(stmt, "alias_local"); + } else { + alias[LOCAL] = NULL; + } + + if (!db_col_is_null(stmt, "alias_remote")) { + alias[REMOTE] = tal(tmpctx, struct short_channel_id); + alias[REMOTE]->u64 = db_col_u64(stmt, "alias_remote"); + } else { + alias[REMOTE] = NULL; + } + ok &= wallet_shachain_load(w, db_col_u64(stmt, "shachain_remote_id"), &wshachain); @@ -1449,6 +1463,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm our_funding_sat, db_col_int(stmt, "funding_locked_remote") != 0, scid, + alias[LOCAL], + alias[REMOTE], &cid, our_msat, msat_to_us_min, /* msatoshi_to_us_min */ @@ -1583,6 +1599,8 @@ static bool wallet_channels_load_active(struct wallet *w) ", lease_chan_max_ppt" ", htlc_minimum_msat" ", htlc_maximum_msat" + ", alias_local" + ", alias_remote" " FROM channels" " WHERE state != ?;")); //? 0 db_bind_int(stmt, 0, CLOSED); @@ -1864,8 +1882,10 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " lease_chan_max_msat=?," // 40 " lease_chan_max_ppt=?," // 41 " htlc_minimum_msat=?," // 42 - " htlc_maximum_msat=?" // 43 - " WHERE id=?")); // 44 + " htlc_maximum_msat=?," // 43 + " alias_local=?," // 44 + " alias_remote=?" // 45 + " WHERE id=?")); // 46 db_bind_u64(stmt, 0, chan->their_shachain.id); if (chan->scid) db_bind_short_channel_id(stmt, 1, chan->scid); @@ -1930,7 +1950,18 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) } db_bind_amount_msat(stmt, 42, &chan->htlc_minimum_msat); db_bind_amount_msat(stmt, 43, &chan->htlc_maximum_msat); - db_bind_u64(stmt, 44, chan->dbid); + + if (chan->alias[LOCAL] != NULL) + db_bind_u64(stmt, 44, chan->alias[LOCAL]->u64); + else + db_bind_null(stmt, 44); + + if (chan->alias[REMOTE] != NULL) + db_bind_u64(stmt, 45, chan->alias[REMOTE]->u64); + else + db_bind_null(stmt, 45); + + db_bind_u64(stmt, 46, chan->dbid); db_exec_prepared_v2(take(stmt)); wallet_channel_config_save(w, &chan->channel_info.their_config); From b9817d395fad5ed6c32ff573a5b5a069d9f336fb Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 22 Apr 2022 16:15:49 +0200 Subject: [PATCH 0874/1530] zeroconf: Wire the aliases through `channeld` --- channeld/channeld.c | 24 ++++++++++++++++-------- channeld/channeld_wire.csv | 4 ++++ lightningd/channel_control.c | 17 ++++++++++------- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 336f849bbed5..5b543ee451f2 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -617,9 +617,15 @@ static void handle_peer_funding_locked(struct peer *peer, const u8 *msg) peer->tx_sigs_allowed = false; peer->funding_locked[REMOTE] = true; + if (tlvs->alias != NULL) { + status_debug( + "Peer told us that they'll use alias=%s for this channel", + type_to_string(tmpctx, struct short_channel_id, + tlvs->alias)); + } wire_sync_write(MASTER_FD, - take(towire_channeld_got_funding_locked(NULL, - &peer->remote_per_commit))); + take(towire_channeld_got_funding_locked( + NULL, &peer->remote_per_commit, tlvs->alias))); channel_announcement_negotiate(peer); billboard_update(peer); @@ -3208,12 +3214,14 @@ static void peer_reconnect(struct peer *peer, static void handle_funding_depth(struct peer *peer, const u8 *msg) { u32 depth; - struct short_channel_id *scid; + struct short_channel_id *scid, *alias_local; + struct tlv_funding_locked_tlvs *tlvs; if (!fromwire_channeld_funding_depth(tmpctx, - msg, - &scid, - &depth)) + msg, + &scid, + &alias_local, + &depth)) master_badmsg(WIRE_CHANNELD_FUNDING_DEPTH, msg); /* Too late, we're shutting down! */ @@ -3235,8 +3243,8 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) peer->next_index[LOCAL], type_to_string(tmpctx, struct pubkey, &peer->next_local_per_commit)); - struct tlv_funding_locked_tlvs *tlvs = - tlv_funding_locked_tlvs_new(tmpctx); + tlvs = tlv_funding_locked_tlvs_new(tmpctx); + tlvs->alias = alias_local; msg = towire_funding_locked( NULL, &peer->channel_id, diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index b2179908b0d4..31d8237a2e1d 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -85,8 +85,11 @@ msgdata,channeld_init,channel_update_len,u16, msgdata,channeld_init,channel_update,u8,channel_update_len # master->channeld funding hit new depth(funding locked if >= lock depth) +# alias != NULL if zeroconf and short_channel_id == NULL +# short_channel_id != NULL once we have 3+ confirmations msgtype,channeld_funding_depth,1002 msgdata,channeld_funding_depth,short_channel_id,?short_channel_id, +msgdata,channeld_funding_depth,alias_local,?short_channel_id, msgdata,channeld_funding_depth,depth,u32, # Tell channel to offer this htlc @@ -117,6 +120,7 @@ msgdata,channeld_fail_htlc,failed_htlc,failed_htlc, # When we receive funding_locked. msgtype,channeld_got_funding_locked,1019 msgdata,channeld_got_funding_locked,next_per_commit_point,pubkey, +msgdata,channeld_got_funding_locked,alias,?short_channel_id, #include diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 9c73949decb1..009eceafb80e 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -230,9 +230,10 @@ bool channel_on_funding_locked(struct channel *channel, static void peer_got_funding_locked(struct channel *channel, const u8 *msg) { struct pubkey next_per_commitment_point; + struct short_channel_id *alias_remote; - if (!fromwire_channeld_got_funding_locked(msg, - &next_per_commitment_point)) { + if (!fromwire_channeld_got_funding_locked(tmpctx, + msg, &next_per_commitment_point, &alias_remote)) { channel_internal_error(channel, "bad channel_got_funding_locked %s", tal_hex(channel, msg)); @@ -242,11 +243,13 @@ static void peer_got_funding_locked(struct channel *channel, const u8 *msg) if (!channel_on_funding_locked(channel, &next_per_commitment_point)) return; + if (channel->alias[REMOTE] == NULL) + channel->alias[REMOTE] = tal_steal(channel, alias_remote); + + /* Remember that we got the lockin */ + wallet_channel_save(channel->peer->ld->wallet, channel); if (channel->scid) lockin_complete(channel); - else - /* Remember that we got the lockin */ - wallet_channel_save(channel->peer->ld->wallet, channel); } static void peer_got_announcement(struct channel *channel, const u8 *msg) @@ -823,8 +826,8 @@ bool channel_tell_depth(struct lightningd *ld, } subd_send_msg(channel->owner, - take(towire_channeld_funding_depth(NULL, channel->scid, - depth))); + take(towire_channeld_funding_depth( + NULL, channel->scid, channel->alias[LOCAL], depth))); if (channel->remote_funding_locked && channel->state == CHANNELD_AWAITING_LOCKIN From c98f011479ff54ed136059875c58467b1d38cd1a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 25 Apr 2022 13:03:43 +0200 Subject: [PATCH 0875/1530] channeld: Send a depth=0 notification when channeld starts up This is used in order to ensure zeroconf doesn't just wait for the first confirmation despite mindepth being set to 0. --- lightningd/channel_control.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 009eceafb80e..39f5c93518e3 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -783,6 +783,13 @@ void peer_start_channeld(struct channel *channel, try_update_blockheight(ld, channel, get_block_height(ld->topology)); } + + /* Artificial confirmation event for zeroconf */ + if (channel_type_has(channel->type, OPT_ZEROCONF)) + subd_send_msg( + channel->owner, + take(towire_channeld_funding_depth( + NULL, channel->scid, channel->alias[LOCAL], 0))); } bool channel_tell_depth(struct lightningd *ld, From cf51edd95b0750905876f78801f2ac0bd25c0a4c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 26 Apr 2022 14:38:50 +0200 Subject: [PATCH 0876/1530] channeld: Remember remote alias for channel announcements and update --- channeld/channeld.c | 1 + 1 file changed, 1 insertion(+) diff --git a/channeld/channeld.c b/channeld/channeld.c index 5b543ee451f2..cadc6c00eacc 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -622,6 +622,7 @@ static void handle_peer_funding_locked(struct peer *peer, const u8 *msg) "Peer told us that they'll use alias=%s for this channel", type_to_string(tmpctx, struct short_channel_id, tlvs->alias)); + peer->short_channel_ids[REMOTE] = *tlvs->alias; } wire_sync_write(MASTER_FD, take(towire_channeld_got_funding_locked( From 3e57d6f9d0f58841e5a61f9d89cb855fb5913bd4 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 26 Apr 2022 14:39:30 +0200 Subject: [PATCH 0877/1530] channeld: On funding_locked, remember either alias or real scid --- channeld/channeld.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index cadc6c00eacc..af0f2098892d 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -3235,8 +3235,14 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) } else { peer->depth_togo = 0; - assert(scid); - peer->short_channel_ids[LOCAL] = *scid; + /* If we know an actual short_channel_id prefer to use + * that, otherwise fill in the alias. From channeld's + * point of view switching from zeroconf to an actual + * funding scid is just a reorg. */ + if (scid) + peer->short_channel_ids[LOCAL] = *scid; + else if (alias_local) + peer->short_channel_ids[LOCAL] = *alias_local; if (!peer->funding_locked[LOCAL]) { status_debug("funding_locked: sending commit index" From bf4417804753174f2b80de5158e09ae2828c0e6b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 26 Apr 2022 14:41:41 +0200 Subject: [PATCH 0878/1530] gossipd: Use the remote alias if no real scid is known This is for the local channel announcement that'll not leave this host, as it doesn't have signatures. --- lightningd/gossip_control.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index f57cd7a5df0b..51a3cd789bea 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -348,15 +348,26 @@ void tell_gossipd_local_private_channel(struct lightningd *ld, struct amount_sat capacity, const u8 *features) { + /* Which short_channel_id should we use to refer to this channel when + * creating invoices? */ + const struct short_channel_id *scid; + /* As we're shutting down, ignore */ if (!ld->gossip) return; + if (channel->scid != NULL) { + scid = channel->scid; + } else { + scid = channel->alias[REMOTE]; + } + + assert(scid != NULL); subd_send_msg(ld->gossip, take(towire_gossipd_local_private_channel (NULL, &channel->peer->id, capacity, - channel->scid, + scid, features))); } From 5e7404850848c2035550f051c3efb3551ecc7b8d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 26 Apr 2022 14:53:58 +0200 Subject: [PATCH 0879/1530] gossip: Add both channel directions with their respective alias We locally generate an update with our local alias, and get one from the peer with the remote alias, so we need to add them both. We do so only if using the alias in the first place though. --- lightningd/channel_control.c | 8 +++----- lightningd/gossip_control.c | 12 ++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 39f5c93518e3..de837a41cf91 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -785,11 +785,9 @@ void peer_start_channeld(struct channel *channel, } /* Artificial confirmation event for zeroconf */ - if (channel_type_has(channel->type, OPT_ZEROCONF)) - subd_send_msg( - channel->owner, - take(towire_channeld_funding_depth( - NULL, channel->scid, channel->alias[LOCAL], 0))); + subd_send_msg(channel->owner, + take(towire_channeld_funding_depth( + NULL, channel->scid, channel->alias[LOCAL], 0))); } bool channel_tell_depth(struct lightningd *ld, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 51a3cd789bea..0de65d194914 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -369,6 +369,18 @@ void tell_gossipd_local_private_channel(struct lightningd *ld, capacity, scid, features))); + + /* If we have no real scid, and there are two different + * aliases, then we need to add both as single direction + * channels to the local gossip_store. */ + if ((!channel->scid && channel->alias[LOCAL]) && + !short_channel_id_eq(channel->alias[REMOTE], + channel->alias[LOCAL])) { + subd_send_msg(ld->gossip, + take(towire_gossipd_local_private_channel( + NULL, &channel->peer->id, capacity, + channel->alias[LOCAL], features))); + } } static struct command_result *json_setleaserates(struct command *cmd, From cdedd433a483d335969655eb7bca5e9a87cde055 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 27 Apr 2022 14:08:21 +0200 Subject: [PATCH 0880/1530] jsonrpc: Add aliases to `listpeers` result --- .msggen.json | 5 +++++ cln-grpc/proto/node.proto | 5 +++++ cln-rpc/src/model.rs | 8 ++++++++ doc/lightning-listpeers.7.md | 5 ++++- doc/schemas/listpeers.schema.json | 19 +++++++++++++++++++ lightningd/peer_control.c | 11 +++++++++++ 6 files changed, 52 insertions(+), 1 deletion(-) diff --git a/.msggen.json b/.msggen.json index 550155b881a6..5f0ba8012904 100644 --- a/.msggen.json +++ b/.msggen.json @@ -669,6 +669,7 @@ "ListPeers.peers[].netaddr[]": 5 }, "ListpeersPeersChannels": { + "ListPeers.peers[].channels[].alias": 50, "ListPeers.peers[].channels[].channel_id": 6, "ListPeers.peers[].channels[].close_to": 14, "ListPeers.peers[].channels[].close_to_addr": 47, @@ -719,6 +720,10 @@ "ListPeers.peers[].channels[].to_us_msat": 20, "ListPeers.peers[].channels[].total_msat": 23 }, + "ListpeersPeersChannelsAlias": { + "ListPeers.peers[].channels[].alias.local": 1, + "ListPeers.peers[].channels[].alias.remote": 2 + }, "ListpeersPeersChannelsFeerate": { "ListPeers.peers[].channels[].feerate.perkb": 2, "ListPeers.peers[].channels[].feerate.perkw": 1 diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 3b96e9d52afa..228648010ad2 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -234,6 +234,11 @@ message ListpeersPeersChannelsFunding { Amount pushed_msat = 3; } +message ListpeersPeersChannelsAlias { + optional string local = 1; + optional string remote = 2; +} + message ListpeersPeersChannelsHtlcs { // ListPeers.peers[].channels[].htlcs[].direction enum ListpeersPeersChannelsHtlcsDirection { diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 75e5308ec256..7bee25f885a8 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1071,6 +1071,14 @@ pub mod responses { pub pushed_msat: Amount, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct ListpeersPeersChannelsAlias { + #[serde(alias = "local", skip_serializing_if = "Option::is_none")] + pub local: Option, + #[serde(alias = "remote", skip_serializing_if = "Option::is_none")] + pub remote: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeersChannelsState_changes { #[serde(alias = "timestamp")] diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index d957fee4763b..170db4166712 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -92,6 +92,9 @@ On success, an object containing **peers** is returned. It is an array of objec - **their_to_self_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close - **our_to_self_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close - **max_accepted_htlcs** (u32, optional): Maximum number of incoming HTLC we will accept at once + - **alias** (object, optional): + - **local** (short_channel_id, optional): An alias assigned by this node to this channel, used for outgoing payments + - **remote** (short_channel_id, optional): An alias assigned by the remote node to this channel, usable in routehints and invoices - **state_changes** (array of objects, optional): Prior state changes: - **timestamp** (string): UTC timestamp of form YYYY-mm-ddTHH:MM:SS.%03dZ - **old_state** (string): Previous state (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") @@ -381,4 +384,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:2e8bcc66531b2dce44b94c42852b624bdd9435cc63495fc799458fa5522f0ea9) +[comment]: # ( SHA256STAMP:fcfc465cbbe95430f4fc6473099142c576883e333ef9fe31d04372f411e49f6d) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index 2979f78cce52..c526744f17ec 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -461,6 +461,20 @@ "htlc_minimum_msat": { "deprecated": true }, + "alias": { + "type": "object", + "required": [], + "properties": { + "local": { + "type": "short_channel_id", + "description": "An alias assigned by this node to this channel, used for outgoing payments" + }, + "remote": { + "type": "short_channel_id", + "description": "An alias assigned by the remote node to this channel, usable in routehints and invoices" + } + } + }, "state_changes": { "type": "array", "description": "Prior state changes", @@ -664,6 +678,7 @@ "payment_hash": {}, "local_trimmed": {}, "status": {}, + "alias": {}, "state": { "type": "string", "enum": [ @@ -751,6 +766,7 @@ "funding_outnum": {}, "close_to": {}, "private": {}, + "alias": {}, "opener": {}, "closer": {}, "features": {}, @@ -827,6 +843,7 @@ ], "properties": { "state": {}, + "alias": {}, "scratch_txid": {}, "feerate": {}, "owner": {}, @@ -911,6 +928,7 @@ "direction" ], "properties": { + "alias": {}, "state": {}, "scratch_txid": {}, "feerate": {}, @@ -1002,6 +1020,7 @@ "scratch_txid": {}, "feerate": {}, "owner": {}, + "alias": {}, "short_channel_id": {}, "channel_id": {}, "funding_txid": {}, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index f68345b2f1d8..6109ed895517 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -744,6 +744,17 @@ static void json_add_channel(struct lightningd *ld, json_add_string(response, "closer", channel->closer == LOCAL ? "local" : "remote"); + if (channel->alias[LOCAL] || channel->alias[REMOTE]) { + json_object_start(response, "alias"); + if (channel->alias[LOCAL]) + json_add_short_channel_id(response, "local", + channel->alias[LOCAL]); + if (channel->alias[REMOTE]) + json_add_short_channel_id(response, "remote", + channel->alias[REMOTE]); + json_object_end(response); + } + json_array_start(response, "features"); if (channel_has(channel, OPT_STATIC_REMOTEKEY)) json_add_string(response, NULL, "option_static_remotekey"); From 78c9c6a9e0b0975eae1afc9f7d19a9373e1c743b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 28 Apr 2022 17:48:16 +0200 Subject: [PATCH 0881/1530] ld: Allow lockin despite not having a scid yet This is needed for us to transition to CHANNELD_NORMAL for zeroconf channels, i.e., channels where we don't have a short channel ID yet. We'll have to call lockin_complete a second time, once we learn the real scid. --- lightningd/channel_control.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index de837a41cf91..e158cae746a3 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -179,8 +179,13 @@ void channel_record_open(struct channel *channel) static void lockin_complete(struct channel *channel) { - /* We set this once we're locked in. */ - assert(channel->scid); + if (!channel->scid && + (!channel->alias[REMOTE] || !channel->alias[LOCAL])) { + log_debug(channel->log, "Attempted lockin, but neither scid " + "nor aliases are set, ignoring"); + return; + } + /* We set this once they're locked in. */ assert(channel->remote_funding_locked); @@ -203,7 +208,10 @@ static void lockin_complete(struct channel *channel) try_update_blockheight(channel->peer->ld, channel, get_block_height(channel->peer->ld->topology)); - channel_record_open(channel); + + /* Only record this once we get a real confirmation. */ + if (channel->scid) + channel_record_open(channel); } bool channel_on_funding_locked(struct channel *channel, @@ -248,8 +256,7 @@ static void peer_got_funding_locked(struct channel *channel, const u8 *msg) /* Remember that we got the lockin */ wallet_channel_save(channel->peer->ld->wallet, channel); - if (channel->scid) - lockin_complete(channel); + lockin_complete(channel); } static void peer_got_announcement(struct channel *channel, const u8 *msg) From 1ae3dba5296a3b487da556d0097466b88fea3d39 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 28 Apr 2022 17:55:15 +0200 Subject: [PATCH 0882/1530] invoice: Consider aliases too when selecting routehints --- lightningd/channel.c | 12 ++++++++++ lightningd/channel.h | 5 +++++ lightningd/routehint.c | 9 ++++++++ lightningd/test/run-invoice-select-inchan.c | 5 +++++ plugins/libplugin-pay.c | 12 ++++++++-- plugins/libplugin.c | 25 ++++++++++++++++++--- plugins/libplugin.h | 2 ++ 7 files changed, 65 insertions(+), 5 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 4a82ae956e14..b3d56c6aa62a 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -664,6 +664,18 @@ struct channel *find_channel_by_scid(const struct peer *peer, return NULL; } +struct channel *find_channel_by_alias(const struct peer *peer, + const struct short_channel_id *alias, + enum side side) +{ + struct channel *c; + list_for_each(&peer->channels, c, list) { + if (c->alias[side] && short_channel_id_eq(c->alias[side], alias)) + return c; + } + return NULL; +} + void channel_set_last_tx(struct channel *channel, struct bitcoin_tx *tx, const struct bitcoin_signature *sig, diff --git a/lightningd/channel.h b/lightningd/channel.h index 18d9e36064a1..aabbcf1ff62f 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -417,6 +417,11 @@ struct channel *find_channel_by_id(const struct peer *peer, struct channel *find_channel_by_scid(const struct peer *peer, const struct short_channel_id *scid); +/* Find a channel by its alias, either local or remote. */ +struct channel *find_channel_by_alias(const struct peer *peer, + const struct short_channel_id *alias, + enum side side); + void channel_set_last_tx(struct channel *channel, struct bitcoin_tx *tx, const struct bitcoin_signature *sig, diff --git a/lightningd/routehint.c b/lightningd/routehint.c index 29a1439bc978..86193904bcd7 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -106,6 +106,15 @@ routehint_candidates(const tal_t *ctx, /* Check channel is in CHANNELD_NORMAL */ candidate.c = find_channel_by_scid(peer, &r->short_channel_id); + + /* Try seeing if we should be using a remote alias + * instead. The `listpeers` result may have returned + * the REMOTE alias, because it is the only scid we + * have, and it is mandatory once the channel is in + * CHANNELD_NORMAL. */ + if (!candidate.c) + candidate.c = find_channel_by_alias(peer, &r->short_channel_id, REMOTE); + if (!candidate.c) { log_debug(ld->log, "%s: channel not found in peer %s", type_to_string(tmpctx, diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 5e14ae9ff1ae..ee3267383f06 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -187,6 +187,11 @@ bool feature_negotiated(const struct feature_set *our_features UNNEEDED, /* Generated stub for featurebits_or */ u8 *featurebits_or(const tal_t *ctx UNNEEDED, const u8 *f1 TAKES UNNEEDED, const u8 *f2 TAKES UNNEEDED) { fprintf(stderr, "featurebits_or called!\n"); abort(); } +/* Generated stub for find_channel_by_alias */ +struct channel *find_channel_by_alias(const struct peer *peer UNNEEDED, + const struct short_channel_id *alias UNNEEDED, + enum side side UNNEEDED) +{ fprintf(stderr, "find_channel_by_alias called!\n"); abort(); } /* Generated stub for find_channel_by_id */ struct channel *find_channel_by_id(const struct peer *peer UNNEEDED, const struct channel_id *cid UNNEEDED) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 35fd1e79fb86..9411de286d62 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -3228,9 +3228,17 @@ static struct command_result *direct_pay_listpeers(struct command *cmd, if (!streq(chan->state, "CHANNELD_NORMAL")) continue; + /* Must have either a local alias for zeroconf + * channels or a final scid. */ + assert(chan->alias[LOCAL] || chan->scid); d->chan = tal(d, struct short_channel_id_dir); - d->chan->scid = *chan->scid; - d->chan->dir = *chan->direction; + if (chan->scid) { + d->chan->scid = *chan->scid; + d->chan->dir = *chan->direction; + } else { + d->chan->scid = *chan->alias[LOCAL]; + d->chan->dir = 0; /* Don't care. */ + } } } cont: diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 86eeb60d8b08..3852bd4aa545 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1581,10 +1581,10 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, *scidtok = json_get_member(buffer, tok, "short_channel_id"), *dirtok = json_get_member(buffer, tok, "direction"), - *tmsattok = - json_get_member(buffer, tok, "total_msat"), + *tmsattok = json_get_member(buffer, tok, "total_msat"), *smsattok = - json_get_member(buffer, tok, "spendable_msat"); + json_get_member(buffer, tok, "spendable_msat"), + *aliastok = json_get_member(buffer, tok, "alias"); if (privtok == NULL || privtok->type != JSMN_PRIMITIVE || statetok == NULL || statetok->type != JSMN_STRING || @@ -1611,6 +1611,25 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, chan->scid = NULL; chan->direction = NULL; } + if (aliastok != NULL) { + const jsmntok_t *loctok = + json_get_member(buffer, aliastok, "local"), + *remtok = + json_get_member(buffer, aliastok, "remote"); + if (loctok) { + chan->alias[LOCAL] = tal(chan, struct short_channel_id); + json_to_short_channel_id(buffer, loctok, + chan->alias[LOCAL]); + } else + chan->alias[LOCAL] = NULL; + + if (remtok) { + chan->alias[REMOTE] = tal(chan, struct short_channel_id); + json_to_short_channel_id(buffer, loctok, + chan->alias[REMOTE]); + } else + chan->alias[REMOTE] = NULL; + } json_to_msat(buffer, tmsattok, &chan->total_msat); json_to_msat(buffer, smsattok, &chan->spendable_msat); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index aba27703221c..553ce84489aa 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -318,6 +319,7 @@ struct listpeers_channel { bool private; struct bitcoin_txid funding_txid; const char *state; + struct short_channel_id *alias[NUM_SIDES]; struct short_channel_id *scid; int *direction; struct amount_msat total_msat; From a4e6b58fa40b58cf6b0365d2ba964027eb0949e5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 28 Apr 2022 17:57:55 +0200 Subject: [PATCH 0883/1530] ld: Consider local aliases when forwarding --- lightningd/channel.c | 5 +++++ plugins/libplugin.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/lightningd/channel.c b/lightningd/channel.c index b3d56c6aa62a..123ca84a577d 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -600,6 +600,11 @@ struct channel *any_channel_by_scid(struct lightningd *ld, if (chan->scid && short_channel_id_eq(scid, chan->scid)) return chan; + /* We also want to find the channel by its local alias + * when we forward. */ + if (chan->alias[LOCAL] && + short_channel_id_eq(scid, chan->alias[LOCAL])) + return chan; } } return NULL; diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 3852bd4aa545..577c45ee3c91 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1629,6 +1629,9 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, chan->alias[REMOTE]); } else chan->alias[REMOTE] = NULL; + } else { + chan->alias[LOCAL] = NULL; + chan->alias[REMOTE] = NULL; } json_to_msat(buffer, tmsattok, &chan->total_msat); From 2dc86bf29b3b8c3134aad3915a74849041f30bab Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 28 Apr 2022 17:58:15 +0200 Subject: [PATCH 0884/1530] db: Store the alias if that's all we got in a forward --- wallet/wallet.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index a6355a62ecec..ddd0de04644f 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4334,6 +4334,7 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, { struct db_stmt *stmt; struct timeabs *resolved_time; + struct channel *outchan; if (state == FORWARD_SETTLED || state == FORWARD_FAILED) { resolved_time = tal(tmpctx, struct timeabs); @@ -4362,8 +4363,18 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, db_bind_u64(stmt, 0, in->dbid); if (out) { + outchan = out->key.channel; db_bind_u64(stmt, 1, out->dbid); - db_bind_u64(stmt, 3, out->key.channel->scid->u64); + + /* IF we forward we must have a name for this + * channel. It can be either a locally assigned alias, + * or the real scid. */ + assert(outchan->scid != NULL || outchan->alias[LOCAL]); + if (out->key.channel->scid) + db_bind_u64(stmt, 3, outchan->scid->u64); + else + db_bind_u64(stmt, 3, outchan->alias[LOCAL]->u64); + db_bind_amount_msat(stmt, 5, &out->msat); } else { /* FORWARD_LOCAL_FAILED may occur before we get htlc_out */ From 22b6e330303dcbc5397a3604b59908d7e48c0dff Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 3 May 2022 17:09:58 +0200 Subject: [PATCH 0885/1530] zeroconf: Trigger coin_movement on first real confirmation We don't trigger on depth=0 since that'd give us bogus blockheights and pointers into the chain, instead we defer until we get a first confirmation. This extracts some of the logic from `lockin_complete`, into the depth change listener (not the remote funding locked since at that point we're certainly locked in and we don't really care about that for bookkeeping anyway). --- lightningd/channel_control.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index e158cae746a3..eafed96ba7a9 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -841,11 +841,30 @@ bool channel_tell_depth(struct lightningd *ld, take(towire_channeld_funding_depth( NULL, channel->scid, channel->alias[LOCAL], depth))); - if (channel->remote_funding_locked - && channel->state == CHANNELD_AWAITING_LOCKIN - && depth >= channel->minimum_depth) + if (channel->remote_funding_locked && + channel->state == CHANNELD_AWAITING_LOCKIN && + depth >= channel->minimum_depth) lockin_complete(channel); + else if (depth == 1 && channel->minimum_depth == 0) { + /* If we have a zeroconf channel, i.e., no scid yet + * but have exchange `channel_ready` messages, then we + * need to fire a second time, in order to trigger the + * `coin_movement` event. This is a subset of the + * `lockin_complete` function below. */ + + assert(channel->scid != NULL); + /* Fees might have changed (and we use IMMEDIATE once we're + * funded), so update now. */ + try_update_feerates(channel->peer->ld, channel); + + try_update_blockheight( + channel->peer->ld, channel, + get_block_height(channel->peer->ld->topology)); + + /* Only record this once we get a real confirmation. */ + channel_record_open(channel); + } return true; } From 0ce68b26c63b0e15fcbfcc22724ca43e46e8c046 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 4 May 2022 15:51:11 +0200 Subject: [PATCH 0886/1530] jsonrpc: Include the direction also if we have an alias The direction only depends on the ordering between node_ids, not the short_channel_id, so we can include it and it won't change. This was causing some trouble loading the `channel_hints` in the `pay` plugin. --- lightningd/peer_control.c | 9 +++++++-- plugins/libplugin.c | 13 +++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 6109ed895517..284dc78bb13d 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -653,12 +653,17 @@ static void json_add_channel(struct lightningd *ld, if (channel->owner) json_add_string(response, "owner", channel->owner->name); - if (channel->scid) { + if (channel->scid) json_add_short_channel_id(response, "short_channel_id", channel->scid); + + /* If there is any way we can use the channel we'd better have + * a direction attached. Technically we could always add it, + * as it's just the lexicographic order between node_ids, but + * why bother if we can't use it? */ + if (channel->scid || channel->alias[LOCAL] || channel->alias[REMOTE]) json_add_num(response, "direction", node_id_idx(&ld->id, &channel->peer->id)); - } json_add_string(response, "channel_id", type_to_string(tmpctx, struct channel_id, &channel->cid)); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 577c45ee3c91..394ec0f4c548 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1603,14 +1603,19 @@ static struct listpeers_channel *json_to_listpeers_channel(const tal_t *ctx, if (scidtok != NULL) { assert(dirtok != NULL); chan->scid = tal(chan, struct short_channel_id); - chan->direction = tal(chan, int); json_to_short_channel_id(buffer, scidtok, chan->scid); - json_to_int(buffer, dirtok, chan->direction); - }else { - assert(dirtok == NULL); + } else { chan->scid = NULL; chan->direction = NULL; } + + if (dirtok != NULL) { + chan->direction = tal(chan, int); + json_to_int(buffer, dirtok, chan->direction); + } else { + chan->direction = NULL; + } + if (aliastok != NULL) { const jsmntok_t *loctok = json_get_member(buffer, aliastok, "local"), From 7930e34da32036fc7e372e61f6c6b861b3392905 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 4 May 2022 15:52:43 +0200 Subject: [PATCH 0887/1530] pay: Populate the channel hints with either the scid or the alias We'll use one of the two, and we reuse the `scid` field, since we don't really care that much which one it is. --- plugins/libplugin-pay.c | 20 ++++++++++++++++---- plugins/libplugin-pay.h | 5 +++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 9411de286d62..e27305fa9361 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -2293,7 +2293,7 @@ local_channel_hints_listpeers(struct command *cmd, const char *buffer, const jsmntok_t *toks, struct payment *p) { const jsmntok_t *peers, *peer, *channels, *channel, *spendsats, *scid, - *dir, *connected, *max_htlc, *htlcs, *state; + *dir, *connected, *max_htlc, *htlcs, *state, *alias, *alias_local; size_t i, j; peers = json_get_member(buffer, toks, "peers"); @@ -2311,12 +2311,20 @@ local_channel_hints_listpeers(struct command *cmd, const char *buffer, struct channel_hint h; spendsats = json_get_member(buffer, channel, "spendable_msat"); scid = json_get_member(buffer, channel, "short_channel_id"); + + alias = json_get_member(buffer, channel, "alias"); + if (alias != NULL) + alias_local = json_get_member(buffer, alias, "local"); + else + alias_local = NULL; + dir = json_get_member(buffer, channel, "direction"); max_htlc = json_get_member(buffer, channel, "max_accepted_htlcs"); htlcs = json_get_member(buffer, channel, "htlcs"); state = json_get_member(buffer, channel, "state"); - if (spendsats == NULL || scid == NULL || dir == NULL || - max_htlc == NULL || state == NULL || + if (spendsats == NULL || + (scid == NULL && alias_local == NULL) || + dir == NULL || max_htlc == NULL || state == NULL || max_htlc->type != JSMN_PRIMITIVE || htlcs == NULL || htlcs->type != JSMN_ARRAY) continue; @@ -2327,7 +2335,11 @@ local_channel_hints_listpeers(struct command *cmd, const char *buffer, json_to_bool(buffer, connected, &h.enabled); h.enabled &= json_tok_streq(buffer, state, "CHANNELD_NORMAL"); - json_to_short_channel_id(buffer, scid, &h.scid.scid); + if (scid != NULL) + json_to_short_channel_id(buffer, scid, &h.scid.scid); + else + json_to_short_channel_id(buffer, alias_local, &h.scid.scid); + json_to_int(buffer, dir, &h.scid.dir); json_to_msat(buffer, spendsats, &h.estimated_capacity); diff --git a/plugins/libplugin-pay.h b/plugins/libplugin-pay.h index eb125acef148..8ec8ec07a439 100644 --- a/plugins/libplugin-pay.h +++ b/plugins/libplugin-pay.h @@ -60,6 +60,11 @@ struct payment_result { * get remove on failure. Success keeps the capacities, since the capacities * changed due to the successful HTLCs. */ struct channel_hint { + /* The short_channel_id we're going to use when referring to + * this channel. This can either be the real scid, or the + * local alias. The `pay` algorithm doesn't really care which + * one it is, but we'll prefer the scid as that's likely more + * readable than the alias. */ struct short_channel_id_dir scid; /* Upper bound on remove channels inferred from payment failures. */ From 92b891bee3759c206e27057d3558ebb08062c852 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 6 May 2022 15:38:34 +0200 Subject: [PATCH 0888/1530] ld: Add function to retrieve either the scid or the local alias We use this in a couple of places, when we want to refer to a channel by its `short_channel_id`, I'm moving this into a separate function primarily to have a way to mark places where we do that. --- lightningd/channel.c | 10 ++++++++++ lightningd/channel.h | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/lightningd/channel.c b/lightningd/channel.c index 123ca84a577d..e0bffb768e63 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -956,3 +956,13 @@ void channel_fail_reconnect(struct channel *channel, const char *fmt, ...) err_and_reconnect(channel, tal_vfmt(tmpctx, fmt, ap), 1); va_end(ap); } + +const struct short_channel_id * +channel_scid_or_local_alias(const struct channel *chan) +{ + assert(chan->scid != NULL || chan->alias[LOCAL] != NULL); + if (chan->scid != NULL) + return chan->scid; + else + return chan->alias[LOCAL]; +} diff --git a/lightningd/channel.h b/lightningd/channel.h index aabbcf1ff62f..afd72e4a79d8 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -476,6 +476,16 @@ static inline bool channel_has(const struct channel *channel, int f) return channel_type_has(channel->type, f); } +/** + * Either returns the short_channel_id if it is known or the local alias. + * + * This is used to refer to a channel by its scid. But sometimes we + * don't have a scid yet, e.g., for `zeroconf` channels, so we resort + * to referencing it by the local alias, which we have in that case. + */ +const struct short_channel_id * +channel_scid_or_local_alias(const struct channel *chan); + void get_channel_basepoints(struct lightningd *ld, const struct node_id *peer_id, const u64 dbid, From 252ccfa7ab3ebb6934b90f80f2271537d4b7e82a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 6 May 2022 15:40:23 +0200 Subject: [PATCH 0889/1530] db: Store the local alias for forwarded incoming payments Not only can the outgoing edge be a zeroconf channel, it can also be the incoming channel. So we revert to the usual trick of using the local alias if the short_channel_id isn't known yet. We use the LOCAL alias instead of the REMOTE alias even though the sender likely used the REMOTE alias to refer to the channel. This is because we control the LOCAL alias, and we keep it stable during the lifetime of the channel, whereas the REMOTE one could change or not be there yet. --- lightningd/notification.c | 8 +++++++- wallet/wallet.c | 18 ++++++++---------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lightningd/notification.c b/lightningd/notification.c index 403ae1187595..79f41980ce21 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -303,7 +303,13 @@ static void forward_event_notification_serialize(struct json_stream *stream, /* Here is more neat to initial a forwarding structure than * to pass in a bunch of parameters directly*/ struct forwarding *cur = tal(tmpctx, struct forwarding); - cur->channel_in = *in->key.channel->scid; + + /* We use the LOCAL alias, not the REMOTE, despite the route + * the the sender is using probably using the REMOTE + * alias. The LOCAL one is controlled by us, and we keep it + * stable. */ + cur->channel_in = *channel_scid_or_local_alias(in->key.channel); + cur->msat_in = in->msat; if (scid_out) { cur->channel_out = *scid_out; diff --git a/wallet/wallet.c b/wallet/wallet.c index ddd0de04644f..1cc1703e45ea 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4334,7 +4334,6 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, { struct db_stmt *stmt; struct timeabs *resolved_time; - struct channel *outchan; if (state == FORWARD_SETTLED || state == FORWARD_FAILED) { resolved_time = tal(tmpctx, struct timeabs); @@ -4363,18 +4362,12 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, db_bind_u64(stmt, 0, in->dbid); if (out) { - outchan = out->key.channel; db_bind_u64(stmt, 1, out->dbid); - /* IF we forward we must have a name for this + /* If we forward we must have a name for this * channel. It can be either a locally assigned alias, * or the real scid. */ - assert(outchan->scid != NULL || outchan->alias[LOCAL]); - if (out->key.channel->scid) - db_bind_u64(stmt, 3, outchan->scid->u64); - else - db_bind_u64(stmt, 3, outchan->alias[LOCAL]->u64); - + db_bind_u64(stmt, 3, channel_scid_or_local_alias(out->key.channel)->u64); db_bind_amount_msat(stmt, 5, &out->msat); } else { /* FORWARD_LOCAL_FAILED may occur before we get htlc_out */ @@ -4385,7 +4378,12 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, db_bind_null(stmt, 5); } - db_bind_u64(stmt, 2, in->key.channel->scid->u64); + /* We use the LOCAL alias, since that's under our control, and + * we keep it stable, whereas the REMOTE alias is likely what + * the sender used to specify the channel, but that's under + * control of the remote end. */ + assert(in->key.channel->scid != NULL || in->key.channel->alias[LOCAL]); + db_bind_u64(stmt, 2, channel_scid_or_local_alias(in->key.channel)->u64); db_bind_amount_msat(stmt, 4, &in->msat); From df739956ab82470628febdd9440b31c826eb969b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 25 May 2022 16:14:47 +0200 Subject: [PATCH 0890/1530] pytest: Add a test for direct payment over zeroconf channels --- tests/test_opening.py | 69 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 589fb5ab7755..a58b40b52d40 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -6,6 +6,7 @@ ) from pathlib import Path +from pprint import pprint import pytest import re import unittest @@ -1234,3 +1235,71 @@ def test_zeroconf_mindepth(bitcoind, node_factory): wait_for(lambda: l1.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") wait_for(lambda: l2.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") + + +def test_zeroconf_open(bitcoind, node_factory): + """Let's open a zeroconf channel + + Just test that both parties opting in results in a channel that is + immediately usable. + + """ + plugin_path = Path(__file__).parent / "plugins" / "zeroconf-selective.py" + + l1, l2 = node_factory.get_nodes(2, opts=[ + {}, + { + 'plugin': str(plugin_path), + 'zeroconf-allow': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518' + }, + ]) + + # Try to open a mindepth=0 channel + l1.fundwallet(10**6) + + l1.connect(l2) + assert (int(l1.rpc.listpeers()['peers'][0]['features'], 16) >> 50) & 0x02 != 0 + + # Now start the negotiation, l1 should have negotiated zeroconf, + # and use their own mindepth=6, while l2 uses mindepth=2 from the + # plugin + l1.rpc.fundchannel(l2.info['id'], 'all', mindepth=0) + + assert l1.db.query('SELECT minimum_depth FROM channels') == [{'minimum_depth': 0}] + assert l2.db.query('SELECT minimum_depth FROM channels') == [{'minimum_depth': 0}] + + l1.daemon.wait_for_logs([ + r'peer_in WIRE_FUNDING_LOCKED', + r'Peer told us that they\'ll use alias=[0-9x]+ for this channel', + ]) + l2.daemon.wait_for_logs([ + r'peer_in WIRE_FUNDING_LOCKED', + r'Peer told us that they\'ll use alias=[0-9x]+ for this channel', + ]) + + wait_for(lambda: l1.rpc.listpeers()['peers'][0]['channels'][0]['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: l2.rpc.listpeers()['peers'][0]['channels'][0]['state'] == 'CHANNELD_NORMAL') + wait_for(lambda: l2.rpc.listincoming()['incoming'] != []) + + inv = l2.rpc.invoice(10**8, 'lbl', 'desc')['bolt11'] + details = l1.rpc.decodepay(inv) + pprint(details) + assert('routes' in details and len(details['routes']) == 1) + hop = details['routes'][0][0] # First (and only) hop of hint 0 + l1alias = l1.rpc.listpeers()['peers'][0]['channels'][0]['alias']['local'] + assert(hop['pubkey'] == l1.info['id']) # l1 is the entrypoint + assert(hop['short_channel_id'] == l1alias) # Alias has to make sense to entrypoint + l1.rpc.pay(inv) + + # Ensure lightningd knows about the balance change before + # attempting the other way around. + l2.daemon.wait_for_log(r'Balance [0-9]+msat -> [0-9]+msat') + + # Inverse payments should work too + pprint(l2.rpc.listpeers()) + inv = l1.rpc.invoice(10**5, 'lbl', 'desc')['bolt11'] + l2.rpc.pay(inv) + + + + l1.rpc.pay(inv) From 306d26357e62e9755f7c92dbec4f812dddb57600 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 25 May 2022 17:03:31 +0200 Subject: [PATCH 0891/1530] pytest: Add test for forwarding over zeroconf channels --- tests/test_opening.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index a58b40b52d40..2e1d5a87a173 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1302,4 +1302,40 @@ def test_zeroconf_open(bitcoind, node_factory): +def test_zeroconf_forward(node_factory, bitcoind): + """Ensure that we can use zeroconf channels in forwards. + + Test that we add routehints using the zeroconf channel, and then + ensure that l2 uses the alias from the routehint to forward the + payment. Then do the inverse by sending from l3 to l1, first hop + being the zeroconf channel + + """ + plugin_path = Path(__file__).parent / "plugins" / "zeroconf-selective.py" + opts = [ + {}, + {}, + { + 'plugin': str(plugin_path), + 'zeroconf-allow': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59' + } + ] + l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + + l1.connect(l2) + l1.fundchannel(l2, 10**6) + bitcoind.generate_block(6) + + l2.connect(l3) + l2.fundwallet(10**7) + l2.rpc.fundchannel(l3.info['id'], 10**6, mindepth=0) + wait_for(lambda: l3.rpc.listincoming()['incoming'] != []) + + inv = l3.rpc.invoice(42 * 10**6, 'inv1', 'desc')['bolt11'] l1.rpc.pay(inv) + + # And now try the other way around: zeroconf channel first + # followed by a public one. + wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4) + inv = l1.rpc.invoice(42, 'back1', 'desc')['bolt11'] + l3.rpc.pay(inv) From b195e6d9d4f21b626a3e022b03bae94a5e286423 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 25 May 2022 17:04:25 +0200 Subject: [PATCH 0892/1530] pytest: Add test for zeroconf channels transitioning to be public We need to switch to the real short_channel_id at 6 confirmations, and gossip messages should reflect that in order to be valid to others. --- tests/test_opening.py | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 2e1d5a87a173..75ff2ed3a2e2 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1301,6 +1301,58 @@ def test_zeroconf_open(bitcoind, node_factory): l2.rpc.pay(inv) +@pytest.mark.xfail(strict=True, reason="Peers do not recognize the final scid yet") +def test_zeroconf_public(bitcoind, node_factory): + """Test that we transition correctly from zeroconf to public + + The differences being that a public channel MUST use the public + scid. l1 and l2 open a zeroconf channel, then l3 learns about it + after 6 confirmations. + + """ + plugin_path = Path(__file__).parent / "plugins" / "zeroconf-selective.py" + + l1, l2, l3 = node_factory.get_nodes(3, opts=[ + {}, + { + 'plugin': str(plugin_path), + 'zeroconf-allow': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518' + }, + {} + ]) + l1.fundwallet(10**6) + l1.connect(l2) + l1.rpc.fundchannel(l2.info['id'], 'all', mindepth=0) + + # Wait for the update to be signed (might not be the most reliable + # signal) + l1.daemon.wait_for_log(r'Got WIRE_HSMD_CUPDATE_SIG_REQ') + l2.daemon.wait_for_log(r'Got WIRE_HSMD_CUPDATE_SIG_REQ') + + l1chan = l1.rpc.listpeers()['peers'][0]['channels'][0] + l2chan = l2.rpc.listpeers()['peers'][0]['channels'][0] + + # We have no confirmation yet, so no `short_channel_id` + assert('short_channel_id' not in l1chan) + assert('short_channel_id' not in l2chan) + + # Now add 1 confirmation, we should get a `short_channel_id` + bitcoind.generate_block(1) + l1.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') + l2.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') + + l1chan = l1.rpc.listpeers()['peers'][0]['channels'][0] + l2chan = l2.rpc.listpeers()['peers'][0]['channels'][0] + assert('short_channel_id' in l1chan) + assert('short_channel_id' in l2chan) + + # Now make it public, we should be switching over to the real + # scid. + bitcoind.generate_block(5) + # Wait for l3 to learn about the channel, it'll have checked the + # funding outpoint, scripts, etc. + wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 2) + def test_zeroconf_forward(node_factory, bitcoind): """Ensure that we can use zeroconf channels in forwards. From 19f8ed3fe1ee5a65975a8b052a3f679772e41586 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 31 May 2022 23:40:59 +0200 Subject: [PATCH 0893/1530] channeld: Explicitly use the first commitment point on reconnect The spec explicitly asks for the first point, while we were using the most recent one. This worked fine before zeroconf, but with zeroconf it can happen. --- channeld/channeld.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index af0f2098892d..2ee4fec501b7 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -3217,6 +3217,7 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) u32 depth; struct short_channel_id *scid, *alias_local; struct tlv_funding_locked_tlvs *tlvs; + struct pubkey point; if (!fromwire_channeld_funding_depth(tmpctx, msg, @@ -3253,9 +3254,13 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) tlvs = tlv_funding_locked_tlvs_new(tmpctx); tlvs->alias = alias_local; - msg = towire_funding_locked( - NULL, &peer->channel_id, - &peer->next_local_per_commit, tlvs); + /* Need to retrieve the first point again, even if we + * moved on, as funding_locked explicitly includes the + * first one. */ + get_per_commitment_point(1, &point, NULL); + + msg = towire_funding_locked(NULL, &peer->channel_id, + &point, tlvs); peer_write(peer->pps, take(msg)); peer->funding_locked[LOCAL] = true; From 692a001198df36274559f8c3cf310cddff0912ed Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 31 May 2022 23:42:23 +0200 Subject: [PATCH 0894/1530] ld: Use the local alias when reporting failures with zeroconf Ran into this with a zeroconf channel, without confs, that was disconnected. --- lightningd/pay.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index d01990f279c5..bb09c07dc7b5 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1056,10 +1056,10 @@ send_payment_core(struct lightningd *ld, group, channel, &hout); if (failmsg) { - fail = immediate_routing_failure(cmd, ld, - fromwire_peektype(failmsg), - channel->scid, - &channel->peer->id); + fail = immediate_routing_failure( + cmd, ld, fromwire_peektype(failmsg), + channel_scid_or_local_alias(channel), + &channel->peer->id); return sendpay_fail( cmd, old_payment, PAY_TRY_OTHER_ROUTE, NULL, fail, From 29157735fb60004d721e84f8d411824f73417d64 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 2 Jun 2022 01:39:53 +0200 Subject: [PATCH 0895/1530] channeld: Track the funding depth while awaiting lockin We used to agree up on the `minimum_depth` with the peer, thus when they told us that the funding locked we'd be sure we either have a scid or we'd trigger the state transition when we do. However if we had a scid, and we got a funding_locked we'd trust them not to have sent it early. Now we explicitly track the depth in the channel while waiting for the funding to confirm. Changelog-Fixed: channeld: Enforce our own `minimum_depth` beyond just confirming --- lightningd/channel.c | 1 + lightningd/channel.h | 6 ++++++ lightningd/channel_control.c | 5 ++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index e0bffb768e63..92c6f809dc26 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -430,6 +430,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->channel_flags = channel_flags; channel->our_config = *our_config; channel->minimum_depth = minimum_depth; + channel->depth = 0; channel->next_index[LOCAL] = next_index_local; channel->next_index[REMOTE] = next_index_remote; channel->next_htlc_id = next_htlc_id; diff --git a/lightningd/channel.h b/lightningd/channel.h index afd72e4a79d8..fbe86a63f916 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -119,6 +119,12 @@ struct channel { /* Minimum funding depth (specified by us if they fund). */ u32 minimum_depth; + /* Depth of the funding TX as reported by the txout + * watch. Only used while we're in state + * CHANNELD_AWAITING_LOCKING, afterwards the watches do not + * trigger anymore. */ + u32 depth; + /* Tracking commitment transaction numbers. */ u64 next_index[NUM_SIDES]; u64 next_htlc_id; diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index eafed96ba7a9..9c908b58645c 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -256,7 +256,9 @@ static void peer_got_funding_locked(struct channel *channel, const u8 *msg) /* Remember that we got the lockin */ wallet_channel_save(channel->peer->ld->wallet, channel); - lockin_complete(channel); + + if (channel->depth >= channel->minimum_depth) + lockin_complete(channel); } static void peer_got_announcement(struct channel *channel, const u8 *msg) @@ -805,6 +807,7 @@ bool channel_tell_depth(struct lightningd *ld, const char *txidstr; txidstr = type_to_string(tmpctx, struct bitcoin_txid, txid); + channel->depth = depth; if (!channel->owner) { log_debug(channel->log, From db61b048a9dee84c3fe0a002af17899cd0879bf3 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 6 Jun 2022 15:23:35 +0200 Subject: [PATCH 0896/1530] zeroconf: Announce the channel with the real scid as well as aliases With zeroconf we have to duplicate the `local_channel_announcement` since we locally announce the aliased version, and then on the first confirmation we also add the funding scid version. --- channeld/channeld.c | 19 +++++++++++++++++++ tests/test_opening.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 2ee4fec501b7..0399fc0c3c0f 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -185,6 +185,16 @@ struct peer { /* We allow a 'tx-sigs' message between reconnect + funding_locked */ bool tx_sigs_allowed; + /* Have we announced the real scid with a + * local_channel_announcement? This can be different from the + * `channel_local_active` flag in case we are using zeroconf, + * in which case we'll have announced the channels with the + * two aliases (LOCAL and REMOTE) but not with the real scid + * just yet. If we get a funding depth change, with a scid, + * and the two flags not equal we know we have to announce the + * channel with the real scid. */ + bool gossip_scid_announced; + /* Most recent channel_update message. */ u8 *channel_update; }; @@ -535,6 +545,14 @@ static void channel_announcement_negotiate(struct peer *peer) if (!peer->channel_local_active) { peer->channel_local_active = true; make_channel_local_active(peer); + } else if(!peer->gossip_scid_announced) { + /* So we know a short_channel_id, i.e., a point on + * chain, but haven't added it to our local view of + * the gossip yet. We need to add it now (and once + * only), so our `channel_update` we'll send a couple + * of lines down has something to attach to. */ + peer->gossip_scid_announced = true; + make_channel_local_active(peer); } /* BOLT #7: @@ -3984,6 +4002,7 @@ int main(int argc, char *argv[]) peer->have_sigs[LOCAL] = peer->have_sigs[REMOTE] = false; peer->announce_depth_reached = false; peer->channel_local_active = false; + peer->gossip_scid_announced = false; peer->from_master = msg_queue_new(peer, true); peer->shutdown_sent[LOCAL] = false; peer->shutdown_wrong_funding = NULL; diff --git a/tests/test_opening.py b/tests/test_opening.py index 75ff2ed3a2e2..58cfadde259f 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1301,7 +1301,6 @@ def test_zeroconf_open(bitcoind, node_factory): l2.rpc.pay(inv) -@pytest.mark.xfail(strict=True, reason="Peers do not recognize the final scid yet") def test_zeroconf_public(bitcoind, node_factory): """Test that we transition correctly from zeroconf to public @@ -1351,6 +1350,7 @@ def test_zeroconf_public(bitcoind, node_factory): bitcoind.generate_block(5) # Wait for l3 to learn about the channel, it'll have checked the # funding outpoint, scripts, etc. + l3.connect(l1) wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 2) From 695a98e5d8c01fc51c0d5fac6859c7d778886fb9 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 7 Jun 2022 13:36:00 +0200 Subject: [PATCH 0897/1530] pyln-testing: Add `gossip_store` parser to testing framework I had to parse quite a few of these files debugging zeroconf, so I thought it might be nice to have direct access here. Changelog-Added: pyln-testing: Added utilities to read and parse `gossip_store` file for nodes. --- contrib/pyln-testing/pyln/testing/gossip.py | 358 ++++++++++++++++++++ contrib/pyln-testing/pyln/testing/utils.py | 4 + 2 files changed, 362 insertions(+) create mode 100644 contrib/pyln-testing/pyln/testing/gossip.py diff --git a/contrib/pyln-testing/pyln/testing/gossip.py b/contrib/pyln-testing/pyln/testing/gossip.py new file mode 100644 index 000000000000..36976e462220 --- /dev/null +++ b/contrib/pyln-testing/pyln/testing/gossip.py @@ -0,0 +1,358 @@ +from binascii import hexlify +from pathlib import Path + +import io +import os +import logging +import struct + + +class GossipStore(object): + """A pythonic way to interact/introspect the gossip_store""" + + def __init__(self, path: Path): + self.fd = None + self.version = None + self.path = path + self.log = logging.getLogger("GossipStore") + + def open(self): + self.fd = self.path.open(mode="rb") + self.version = ord(self.fd.read(1)) + if self.version < 3: + raise ValueError( + f"gossip_store below version 3 is not supported: {self.version}" + ) + + def reset(self): + """Seek back to the beginning of the store.""" + self.log.debug("Resetting file position to 1") + self.fd.seek(1, os.SEEK_SET) + + @classmethod + def maybe_parse(cls, msg: bytes): + parsers = { + 256: ChannelAnnouncement, + 257: NodeAnnouncement, + 258: ChannelUpdate, + 4104: LocalChannelAnnouncement, + 4102: LocalChannelUpdate, + } + + (typ,) = struct.unpack_from("!H", msg) + parser = parsers.get(typ, None) + logging.debug(f"Picking parser for type={typ} -> {parser}") + if parser is not None: + return parser.from_bytes(io.BytesIO(msg[2:])) + else: + return msg + + def __next__(self): + hdr = self.fd.read(12) + if len(hdr) < 12: + raise StopIteration + length, crc, timestamp = struct.unpack("!III", hdr) + + # deleted = (length & 0x80000000 != 0) + # important = (length & 0x40000000 != 0) + length = length & (~0x80000000) & (~0x40000000) + print(f"Reading message length={length} at position={self.fd.tell()}") + + msg = self.fd.read(length) + + if len(msg) != length: + raise ValueError( + f"Incomplete gossip_store: expected {length} bytes, got {len(msg)} at pos {self.fd.tell()}" + ) + + return GossipStore.maybe_parse(msg) + + def __iter__(self): + """Return a copy of ourselves as iterator.""" + inst = GossipStore(self.path) + inst.open() + return inst + + +class ChannelAnnouncement(object): + def __init__(self): + self.num_short_channel_id = None + self.node_signatures = [None, None] + self.bitcoin_signatures = [None, None] + self.features = None + self.chain_hash = None + self.node_ids = [None, None] + self.bitcoin_keys = [None, None] + + @classmethod + def from_bytes(cls, b: io.BytesIO) -> "ChannelAnnouncement": + ca = cls() + ca.node_signatures = (b.read(64), b.read(64)) + ca.bitcoin_signatures = (b.read(64), b.read(64)) + (flen,) = struct.unpack("!H", b.read(2)) + ca.features = b.read(flen) + ca.chain_hash = b.read(32)[::-1] + (ca.num_short_channel_id,) = struct.unpack("!Q", b.read(8)) + ca.node_ids = (b.read(33), b.read(33)) + ca.bitcoin_keys = (b.read(33), b.read(33)) + return ca + + @property + def short_channel_id(self): + return "{}x{}x{}".format( + (self.num_short_channel_id >> 40) & 0xFFFFFF, + (self.num_short_channel_id >> 16) & 0xFFFFFF, + (self.num_short_channel_id >> 00) & 0xFFFF, + ) + + def __eq__(self, other): + return ( + self.num_short_channel_id == other.num_short_channel_id + and self.bitcoin_keys == other.bitcoin_keys + and self.chain_hash == other.chain_hash + and self.node_ids == other.node_ids + and self.features == other.features + ) + + def __str__(self): + na = hexlify(self.node_ids[0]).decode("ASCII") + nb = hexlify(self.node_ids[1]).decode("ASCII") + return f"ChannelAnnouncement(scid={self.short_channel_id}, nodes=[{na},{nb}])" + + +class NodeAnnouncement(object): + def __init__(self): + self.signature = None + self.features = "" + self.timestamp = None + self.node_id = None + self.rgb_color = None + self.alias = None + self.addresses = None + + @classmethod + def from_bytes(cls, b: io.BytesIO) -> "NodeAnnouncement": + na = cls() + na.signature = b.read(64) + (flen,) = struct.unpack("!H", b.read(2)) + na.features = b.read(flen) + (na.timestamp,) = struct.unpack("!I", b.read(4)) + na.node_id = b.read(33) + na.rgb_color = b.read(3) + na.alias = b.read(32) + (alen,) = struct.unpack("!H", b.read(2)) + abytes = io.BytesIO(b.read(alen)) + na.addresses = [] + while True: + addr = Address.from_bytes(abytes) + if addr is None: + break + else: + na.addresses.append(addr) + return na + + def __str__(self): + return f"NodeAnnouncement(id={hexlify(self.node_id)}, alias={self.alias}, color={self.rgb_color})" + + def __eq__(self, other): + return ( + self.features == other.features + and self.timestamp == other.timestamp + and self.node_id == other.node_id + and self.rgb_color == other.rgb_color + and self.alias == other.alias + ) + + +class Address(object): + def __init__(self, typ=None, addr=None, port=None): + self.typ = typ + self.addr = addr + self.port = port + + @classmethod + def from_bytes(cls, b: io.BytesIO) -> "Address": + a = cls() + + t = b.read(1) + if len(t) != 1: + return None + (a.typ,) = struct.unpack("!B", t) + + if a.typ == 1: + a.addr = b.read(4) + elif a.typ == 2: + a.addr = b.read(16) + elif a.typ == 3: + a.addr = b.read(10) + elif a.typ == 4: + a.addr = b.read(35) + else: + print(f"Unknown address type {a.typ}") + return None + (a.port,) = struct.unpack("!H", b.read(2)) + return a + + def __eq__(self, other): + return ( + self.typ == other.typ + and self.addr == other.addr + and self.port == other.port + ) + + def __len__(self): + l = { + 1: 6, + 2: 18, + 3: 12, + 4: 37, + } + return l[self.typ] + 1 + + def __str__(self): + addr = self.addr + if self.typ == 1: + addr = ".".join([str(c) for c in addr]) + + protos = { + 1: "ipv4", + 2: "ipv6", + 3: "torv2", + 4: "torv3", + } + + return f"{protos[self.typ]}://{addr}:{self.port}" + + +class ChannelUpdate(object): + def __init__(self): + self.signature = None + self.chain_hash = None + self.num_short_channel_id = None + self.timestamp = None + self.message_flags = None + self.channel_flags = None + self.cltv_expiry_delta = None + self.htlc_minimum_msat = None + self.fee_base_msat = None + self.fee_proportional_millionths = None + self.htlc_maximum_msat = None + + @classmethod + def from_bytes(cls, b: io.BytesIO) -> "ChannelUpdate": + cu = ChannelUpdate() + cu.signature = b.read(64) + cu.chain_hash = b.read(32)[::-1] + (cu.num_short_channel_id,) = struct.unpack("!Q", b.read(8)) + (cu.timestamp,) = struct.unpack("!I", b.read(4)) + cu.message_flags = b.read(1) + cu.channel_flags = b.read(1) + (cu.cltv_expiry_delta,) = struct.unpack("!H", b.read(2)) + (cu.htlc_minimum_msat,) = struct.unpack("!Q", b.read(8)) + (cu.fee_base_msat,) = struct.unpack("!I", b.read(4)) + (cu.fee_proportional_millionths,) = struct.unpack("!I", b.read(4)) + t = b.read(8) + if len(t) == 8: + (cu.htlc_maximum_msat,) = struct.unpack("!Q", t) + else: + cu.htlc_maximum_msat = None + + return cu + + @property + def short_channel_id(self): + return "{}x{}x{}".format( + (self.num_short_channel_id >> 40) & 0xFFFFFF, + (self.num_short_channel_id >> 16) & 0xFFFFFF, + (self.num_short_channel_id >> 00) & 0xFFFF, + ) + + @property + def direction(self): + (b,) = struct.unpack("!B", self.channel_flags) + return b & 0x01 + + def serialize(self): + raise ValueError() + + def __str__(self): + return "ChannelUpdate(scid={short_channel_id}, timestamp={timestamp})".format( + timestamp=self.timestamp, short_channel_id=self.short_channel_id + ) + + def __eq__(self, other): + return ( + self.chain_hash == other.chain_hash + and self.num_short_channel_id == other.num_short_channel_id + and self.timestamp == other.timestamp + and self.message_flags == other.message_flags + and self.channel_flags == other.channel_flags + and self.cltv_expiry_delta == other.cltv_expiry_delta + and self.htlc_minimum_msat == other.htlc_minimum_msat + and self.fee_base_msat == other.fee_base_msat + and self.fee_proportional_millionths == other.fee_proportional_millionths + and self.htlc_maximum_msat == other.htlc_maximum_msat + ) + + +class LocalChannelAnnouncement(object): + def __init__(self): + self.inner = None + self.satoshis = None + + @classmethod + def from_bytes(cls, b: io.BytesIO) -> "LocalChannelAnnouncement": + lca = LocalChannelAnnouncement() + (lca.satoshis,) = struct.unpack("!Q", b.read(8)) + b.read(4) # Skip length prefix + lca.inner = ChannelAnnouncement.from_bytes(b) + return lca + + def __str__(self): + return f"LocalChannelAnnouncement[satoshis={self.satoshis},inner={self.inner}]" + + +class LocalChannelUpdate(object): + def __init__(self): + self.inner = None + + @classmethod + def from_bytes(cls, b: io.BytesIO) -> "LocalChannelUpdate": + b.read(2) # Skip length prefix + lcu = LocalChannelUpdate() + lcu.inner = ChannelUpdate.from_bytes(b) + return lcu + + def __str__(self): + return f"LocalChannelUpdate[inner={self.inner}]" + + +def test_gossip_store(): + from binascii import unhexlify + + a = io.BytesIO( + unhexlify( + "09000001bc55786b7f00000000100800000000000f315901b0010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f4101dd37cfe5d2c2022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d590266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f000001bc0209e6ea00000000100800000000000f315901b0010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f80294ac02207027d022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d590266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f0000008eb7783081000000001006008a01022924a3f31a937ddf122181b7e9753316b66330afd88ab297d82efb4d4d7065de5e2dd9a234a9f3c6604ca2027f6a2d2d752d795cf915eea6ab40765e6d20cf4206226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f80294ac02207027d628e5c04010100060000000000000000000000010000000a000000003ac0d9080000008ece416a0a000000001006008a010207a76fac2089f27cd3b088291930bc1c95d739e01fea38fbbc6d2712b112579f34b8320f566155eb1cb7d34f2f2114d4004b3c6200f706c882a5659f16a16f0406226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f4101dd37cfe5d2c2628e5c04010000060000000000000000000000010000000a000000003ac0d908" + ) + ) + + a.read(1) + a.read(12) + assert a.read(2) == b"\x10\x08" + + ca = LocalChannelAnnouncement.from_bytes(a) + print(ca) + a.read(12) + assert a.read(2) == b"\x10\x08" + ca = LocalChannelAnnouncement.from_bytes(a) + print(ca) + + a.read(12) + assert a.read(2) == b"\x10\x06" + cu = LocalChannelUpdate.from_bytes(a) + print(cu) + + a.read(12) + assert a.read(2) == b"\x10\x06" + cu = LocalChannelUpdate.from_bytes(a) + print(cu) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 899094c5fe4a..028da5260ded 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -5,6 +5,7 @@ from pathlib import Path from pyln.client import RpcError from pyln.testing.btcproxy import BitcoinRpcProxy +from pyln.testing.gossip import GossipStore from collections import OrderedDict from decimal import Decimal from pyln.client import LightningRpc @@ -698,6 +699,8 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai socket_path = os.path.join(lightning_dir, TEST_NETWORK, "lightning-rpc").format(node_id) self.rpc = PrettyPrintingLightningRpc(socket_path, self.executor, jsonschemas=jsonschemas) + self.gossip_store = GossipStore(Path(lightning_dir, TEST_NETWORK, "gossip_store")) + self.daemon = LightningD( lightning_dir, bitcoindproxy=bitcoind.get_proxy(), port=port, random_hsm=random_hsm, node_id=node_id @@ -841,6 +844,7 @@ def start(self, wait_for_bitcoind_sync=True): self.info = self.rpc.getinfo() # This shortcut is sufficient for our simple tests. self.port = self.info['binding'][0]['port'] + self.gossip_store.open() # Reopen the gossip_store now that we should have one if wait_for_bitcoind_sync and not self.is_synced_with_bitcoin(self.info): wait_for(lambda: self.is_synced_with_bitcoin()) From a07797c166c3008d7520a046982dbf2fbd4fcec5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 15 Apr 2022 14:24:09 +0200 Subject: [PATCH 0898/1530] features: Add function to unset a featurebit Needed so we can blank optional bits when comparing channel_types. --- common/features.c | 11 +++++++++++ common/features.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/common/features.c b/common/features.c index 14c0a39142e9..5040c39accb3 100644 --- a/common/features.c +++ b/common/features.c @@ -516,6 +516,17 @@ u8 *featurebits_or(const tal_t *ctx, const u8 *f1 TAKES, const u8 *f2 TAKES) return result; } +void featurebits_unset(u8 **ptr, size_t bit) +{ + size_t len = tal_count(*ptr); + if (bit / 8 >= len) + return; + + (*ptr)[len - 1 - bit / 8] &= (0 << (bit % 8)); + + trim_features(ptr); +} + bool featurebits_eq(const u8 *f1, const u8 *f2) { size_t len = tal_bytelen(f1); diff --git a/common/features.h b/common/features.h index 58b57c48f3c0..d5da02fdf79f 100644 --- a/common/features.h +++ b/common/features.h @@ -73,6 +73,8 @@ void set_feature_bit(u8 **ptr, u32 bit); /* Given two featurebit vectors, combine them by applying a logical OR. */ u8 *featurebits_or(const tal_t *ctx, const u8 *f1 TAKES, const u8 *f2 TAKES); +/* Unset a given bit in a featurebits string */ +void featurebits_unset(u8 **ptr, size_t bit); /* Are these two feature bitsets functionally equal (one may have * trailing zeroes)? */ From d115d0110588ddf40b4782588d7fbcb02c2b5427 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 19 Apr 2022 12:49:32 +0200 Subject: [PATCH 0899/1530] zeroconf: Add channel_type variant support If zeroconf was negotiated we'll add it to the basic channel type. Similarly we'll accept it if it was negotiated too. --- common/channel_type.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/common/channel_type.c b/common/channel_type.c index 87f0ab2bc3aa..a2b1cab37788 100644 --- a/common/channel_type.c +++ b/common/channel_type.c @@ -108,9 +108,21 @@ struct channel_type *channel_type_accept(const tal_t *ctx, const struct feature_set *our_features, const u8 *their_features) { - struct channel_type *ctype; - static const size_t feats[] = { OPT_ANCHOR_OUTPUTS, - OPT_STATIC_REMOTEKEY }; + struct channel_type *ctype, proposed; + /* Need to copy since we're going to blank variant bits for equality. */ + proposed.features = tal_dup_talarr(tmpctx, u8, t); + + static const size_t feats[] = { + OPT_ANCHOR_OUTPUTS, + OPT_STATIC_REMOTEKEY, + OPT_ZEROCONF, + }; + + /* The basic channel_types can have any number of the + * following optional bits. */ + static const size_t variants[] = { + OPT_ZEROCONF, + }; for (size_t i = 0; i < ARRAY_SIZE(feats); i++) { size_t f = feats[i]; @@ -128,15 +140,22 @@ struct channel_type *channel_type_accept(const tal_t *ctx, } } + /* Blank variants so we can just check for equality. */ + for (size_t i = 0; i< ARRAY_SIZE(variants); i++) + featurebits_unset(&proposed.features, variants[i]); + /* Otherwise, just needs to be a known channel type. */ - ctype = channel_type_none(tmpctx); - if (featurebits_eq(t, ctype->features)) - return tal_steal(ctx, ctype); - ctype = channel_type_static_remotekey(tmpctx); - if (featurebits_eq(t, ctype->features)) - return tal_steal(ctx, ctype); - ctype = channel_type_anchor_outputs(tmpctx); - if (featurebits_eq(t, ctype->features)) - return tal_steal(ctx, ctype); + if (channel_type_eq(&proposed, channel_type_none(tmpctx)) || + channel_type_eq(&proposed, + channel_type_static_remotekey(tmpctx)) || + channel_type_eq(&proposed, channel_type_anchor_outputs(tmpctx))) { + /* At this point we know it matches, and maybe has + * a couple of extra options. So let's just reply + * with their proposal. */ + ctype = tal(ctx, struct channel_type); + ctype->features = tal_dup_talarr(ctx, u8, t); + return ctype; + } + return NULL; } From 669bca4a02b724f9326c1afaf00732c205501979 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 29 Jun 2022 12:41:12 +0200 Subject: [PATCH 0900/1530] ld: Use the local alias in the `htlc_accepted` hook If we have no real short-channel-id this is the best we can do. Use the local one since we can be sure we have assigned one. --- lightningd/peer_htlcs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index f16b0e2eb80e..62f0756449f9 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1069,7 +1069,9 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_object_end(s); json_object_start(s, "htlc"); - json_add_short_channel_id(s, "short_channel_id", hin->key.channel->scid); + json_add_short_channel_id( + s, "short_channel_id", + channel_scid_or_local_alias(hin->key.channel)); json_add_u64(s, "id", hin->key.id); if (deprecated_apis) json_add_amount_msat_only(s, "amount", hin->msat); From cbafc0fa33419551a159c6b9be92cabc983ce01f Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Tue, 3 May 2022 12:42:31 -0500 Subject: [PATCH 0901/1530] gossip_store: add flag for spam gossip, update to v10 This will be used to decouple internal use of gossip from what is passed to gossip peers. Updates GOSSIP_STORE_VERION to 10. Changelog-Changed: gossip_store updated to version 10. --- common/gossip_store.c | 6 +++++- common/gossip_store.h | 11 +++++++++-- common/test/run-gossmap_local.c | 2 +- connectd/multiplex.c | 2 ++ contrib/pyln-client/pyln/client/gossmap.py | 8 ++++---- .../tests/data/gossip_store-part1.xz | Bin 18460 -> 18460 bytes .../tests/data/gossip_store.simple.xz | Bin 424 -> 424 bytes gossipd/gossip_store.c | 2 +- tests/test_gossip.py | 10 +++++----- 9 files changed, 27 insertions(+), 14 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index 4fdcde1e4d16..2a0ade5ab678 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -53,6 +53,7 @@ u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, u32 timestamp_min, u32 timestamp_max, bool push_only, + bool with_spam, size_t *off, size_t *end) { u8 *msg = NULL; @@ -60,7 +61,7 @@ u8 *gossip_store_next(const tal_t *ctx, while (!msg) { struct gossip_hdr hdr; u32 msglen, checksum, timestamp; - bool push; + bool push, ratelimited; int type, r; r = pread(*gossip_store_fd, &hdr, sizeof(hdr), *off); @@ -69,6 +70,7 @@ u8 *gossip_store_next(const tal_t *ctx, msglen = be32_to_cpu(hdr.len); push = (msglen & GOSSIP_STORE_LEN_PUSH_BIT); + ratelimited = (msglen & GOSSIP_STORE_LEN_RATELIMIT_BIT); msglen &= GOSSIP_STORE_LEN_MASK; /* Skip any deleted entries. */ @@ -115,6 +117,8 @@ u8 *gossip_store_next(const tal_t *ctx, msg = tal_free(msg); } else if (!push && push_only) { msg = tal_free(msg); + } else if (!with_spam && ratelimited) { + msg = tal_free(msg); } } diff --git a/common/gossip_store.h b/common/gossip_store.h index 8fa6ee145c78..136186e8e564 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -11,7 +11,7 @@ struct gossip_rcvd_filter; /** * gossip_store -- On-disk storage related information */ -#define GOSSIP_STORE_VERSION 9 +#define GOSSIP_STORE_VERSION 10 /** * Bit of length we use to mark a deleted record. @@ -23,9 +23,15 @@ struct gossip_rcvd_filter; */ #define GOSSIP_STORE_LEN_PUSH_BIT 0x40000000U +/** + * Bit of length used to define a rate-limited record (do not rebroadcast) + */ + #define GOSSIP_STORE_LEN_RATELIMIT_BIT 0x20000000U + /* Mask for extracting just the length part of len field */ #define GOSSIP_STORE_LEN_MASK \ - (~(GOSSIP_STORE_LEN_PUSH_BIT | GOSSIP_STORE_LEN_DELETED_BIT)) + (~(GOSSIP_STORE_LEN_PUSH_BIT | GOSSIP_STORE_LEN_DELETED_BIT | \ + GOSSIP_STORE_LEN_RATELIMIT_BIT)) /** * gossip_hdr -- On-disk format header. @@ -47,6 +53,7 @@ u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, u32 timestamp_min, u32 timestamp_max, bool push_only, + bool with_spam, size_t *off, size_t *end); /** diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 6f1b4b724cd8..84c97d5abf62 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -31,7 +31,7 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U * setup_gossip_store_test via od -v -Anone -tx1 < /tmp/ltests-kaf30pn0/test_gossip_store_compact_noappend_1/lightning-2/regtest/gossip_store */ static u8 canned_map[] = { - 0x09, 0x80, 0x00, 0x01, 0xbc, 0x09, 0x8b, 0x67, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00 + 0x0a, 0x80, 0x00, 0x01, 0xbc, 0x09, 0x8b, 0x67, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x0f, 0x42, 0x40, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/connectd/multiplex.c b/connectd/multiplex.c index e99c78e6e214..d3d189a4aa22 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -375,6 +375,7 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) return gossip_store_next(ctx, &peer->daemon->gossip_store_fd, 0, 0xFFFFFFFF, true, + false, &peer->gs.off, &peer->daemon->gossip_store_end); } @@ -391,6 +392,7 @@ static u8 *maybe_from_gossip_store(const tal_t *ctx, struct peer *peer) peer->gs.timestamp_min, peer->gs.timestamp_max, false, + false, &peer->gs.off, &peer->daemon->gossip_store_end); /* Don't send back gossip they sent to us! */ diff --git a/contrib/pyln-client/pyln/client/gossmap.py b/contrib/pyln-client/pyln/client/gossmap.py index 71379d438804..ed0c76500e17 100755 --- a/contrib/pyln-client/pyln/client/gossmap.py +++ b/contrib/pyln-client/pyln/client/gossmap.py @@ -9,7 +9,7 @@ import struct # These duplicate constants in lightning/common/gossip_store.h -GOSSIP_STORE_VERSION = 9 +GOSSIP_STORE_VERSIONS = [0x09, 0x0a] GOSSIP_STORE_LEN_DELETED_BIT = 0x80000000 GOSSIP_STORE_LEN_PUSH_BIT = 0x40000000 GOSSIP_STORE_LEN_MASK = (~(GOSSIP_STORE_LEN_PUSH_BIT @@ -171,9 +171,9 @@ def __init__(self, store_filename: str = "gossip_store"): self.nodes: Dict[GossmapNodeId, GossmapNode] = {} self.channels: Dict[ShortChannelId, GossmapChannel] = {} self._last_scid: Optional[str] = None - version = self.store_file.read(1) - if version[0] != GOSSIP_STORE_VERSION: - raise ValueError("Invalid gossip store version {}".format(int(version))) + version = self.store_file.read(1)[0] + if version not in GOSSIP_STORE_VERSIONS: + raise ValueError("Invalid gossip store version {}".format(version)) self.bytes_read = 1 self.refresh() diff --git a/contrib/pyln-client/tests/data/gossip_store-part1.xz b/contrib/pyln-client/tests/data/gossip_store-part1.xz index 73bd769f96bfde61131426456b42a658dbe2ce87..f63971a8671a56a750e7d2bef5e383c97b5fba92 100644 GIT binary patch delta 18270 zcmV(nK=QwwkO7>K0gxX81rf0!I{|;ob5v7MbQAl*jocP&?aGWJqGv45*+Faj!CfHb zfFAHk$d=1@$@xdtg;%XNAYYk+TMJfV+I5&U5N`#PY zG6xC@&P@Q*X1-Em9pH}=xh1r_%-K@Je)d}9Ffmp|9OL*&!Z7DbzK?))*_Yy@ z0*(JX>10g;0X^Ozh9so`$}9IaEwgRJvNsXx4T4lrbU4^um0L>*88~?2`Q?fnXGaU% zr)QMu;bNm>3JL>H7>u?C!gznX%{1i%!Vm|L*g#EeP(|iZMJ)TjXzIiKOE-o|)f*{6 zz$n!#qu$(1$vnormWw!N_?be+FsisBUpL0s)j7*fcU#H>LXMcSObSI%WlmuiivLcP zmf;v}E;L=QSNvn3%H)}Fzdu1OC@^fhv zfRj`fhNv0GdccPIkXL^vWo6NP&Kq7P3K>`C-%pqGu_$bBpZ1=C~z+T zKXRbMbu>p?+S{o|OM=yu>?CX#IF?fstueGmz>ae7Q6;pLK|GZnwgYq=mHo6y^WUid zB=X8>m!OcE8;VscR^Iy>f%#blI9ECTsmjT{^{L(MxZd)?Ioy9-!(_UIy_8$-D&8AgnsH@00b31Tj)wQBm&Q26Mo_c`F4jznn;Z!WN3eKyyMU zKTw`$mLh{9BgB8h9%)=rW6hBTm6vx;8tOcx)*Xsa>psTf_0tse78=Y18c=1iXS$*= zE{o+fPK!%fVt0fe6r9X>?u$h<5T|^>t~S>--A0o=$Y1y0PE114NPN#p7FEtpqBLV%9JBcgQ2Z^Jq-x!@xz<5JaJ0SabP**PoHcAXNm7Lo|K0<4e zw2@nEWR#x~&6$$Qsi-=COuuCnPCCUmLDA~5Gs~Ww5=e_R{K7iJ?U3GS-GLe$)N~Fb z|Bm)Ol!pLOP*F76NcFj_P~uv-YsE40l&)4O7o&fy9{lz&QT82aCKs4=q)%csUA&S+ zVt15O&9LSw=yrgrE>9Ia@LrKCP!OKx@*xniZDYi7efG}WL7nLg(O+Hzwz2B-$#0gu zwR99WOIN{?7KpN%}g6*G!ElNzWOUdj*A!&faFF3{+3j&ECOXt`5{VKnbe!* zhbnjA)2E&HP=efXX@V{h;Xn7qFlHM7HVPSo*DQgs`Ir(`gTm^`gdLbU1|TT|xWrIx z+FtpnX*c|zwE$t~cB~>NT@Ua*jiP^QN6wbP@kwW&CfwR>iq4n-`+mq9dz0yd1-mn4 z#D9tvIi4;++d;z!xkjp@ILW!*T?P_%+)0xcZk$zSAWa;Zv<-vA+UO>!L%;oC!|^OiVCjbBoofjy+l=UaUp(t%f?*Oc|M^iZxS7o(y)>}`>%F~NI! z*WkeHum!&xeXjB%X0QmCaP%*hc8H&&DS@Na+mhOgGxFV-6ZzZ8!6<(WY#G&WdnR5I zVoAQ2DpYLp8(4eUPtGO+=d#UNeT6ow@?$zXT^742j*jUJ(~staLlqsC9!q|big$qW zIh{rCP$*OfSdxZ7<(0Ud7lpE%5v0L55D?7JZyl1bqKB^0Lvl2UGTF8(4agM+8>Z7L zGMFkZezF3BJmzB;-VA^1@N99nytsnf^~0#yqL3zVkZI5$ZugUOFxh~++T#LYZ?7XN zlC-2nMJaZwxTNn^`=%DL?3Wzu0m06aJKXF?Q!%~uZI@;)Y-!dLEaMEIY$s&d6iRFW zLb>1aa%=D6E{oaqXyl&D@}k%YGA(mkz3g2_GR6-#*?x^G^JstQJP=&Vp_t{(;&9rN zttz}E(+xTppK@jacZ^JCPZ(g@YY}O9ILGnYb`DPxkk;ojt^%=M8`6bSn1Aq?#6E0? z7sEdH*M56bd)SI7Jufn<&@h&^TN6t56!(e%?Q*PC8)@Bt)-xkPC_?OYGYh+1ghx zTqjTZI1b)60s$~u0$Y@zj%_4CIiyfp=}fk^P1nT2Vpfm(>pPwOt^k#OPp|Qg$i+aU z=mIfe6Gs6j(V`3R^o&UWl3p^+I(%D;vE2<_SZADzdendL=AS9#OVn}B$@Wze$+V(p z6tOAw)6Ud-O;1O2#8A&aU-M!2*2!g)QoyJ`6-A0H?x+igRyH3gc=pSEnd(xS#@S`; zgb+^TZL4FInz$>cD|pLvXsbiE6jmT$gp~2Ajd`HyB-pGEwU#?gpg9EgWxq z^%n$-$+&+uPnvK!$MuY?QX$aygE?$0Fmj7l((3|tNH;LH!1VlL{uzfTzNda6G*qi# zy^OjP;yRP-cC|{ec{o^7#Kw4*3eY_1O(ON!8{4p}MsHeoM<1E4#CCbnV`2L{7@1vX z(k-EYLPpY)6W{hz-68aqAfa^Tb0@r^>J@h*-C=)7;qvONKV8U(5bNQe53!-yn0b@l zNij5zg{c;GD;hI!k?;l63R2i+5Ybuux42WMEXQ>;DW;Ey=a;dISlQ?4(7hmT3` z@#i$|$1rlthJk#6$o7*l1@CQC7TG2$ra2q{?KGhO_;0uY=#2;?N4k2YT=k9C2D!zS zAOwF6-Yuj#x5xTWJ85v?;i=<$%h-J$syS|&Vzxc&yWDcOt@UPD?t07=Voc?|LvfGg z#j03D=73}+kTA95gL_jt^NUk7u(qTY)QuYbDEMiD7caD*60^@0OwFI#cbihxesn1` zNMNM&fQ-h@jgz>(wJS!ixA5;7sUo3CllgtE^P=;#nx-#3(RKsDs$lv#p`9 zT;Nwzl_MGx#h&(diIM@5^i4 zH+?UXvZq%Ii4PVp)9ZEB?U1Q2CB~PHo4?nl0W$CrQl6_b4z^hnU{TB<_5EKMpy+>> z^shF1@j-<)8Z^zenA~ti}uzAJrv9NgJJ_0eMp%GDk>rD zQZzg4**QAgeR-{_3)-+A2PiGO8<^2q>u17*cA-t-W@gCi?H}C*%S`>4IECxZuU{h8 zA3-rbTw&tn(OC}@*hLrP-m+v1KqG%v=i=??Rt0|59|`pfSVOoZZT-(?M15Z=a1Zrz ziP-X^F@-4eYVXVm#Q&-CG7qmx7RE~qIg;Fsmr?=C0;dOkt3(~H=(N>fT-}`$0)Tpt zTT|Q_$R@8QP2FL_c|bL)v^R}+bqpTgyNJI_8{{U6%bgKU8cTG=2F#_!b8~-UTT*wr zdoRBD8FN2R&l-wgih7A7Z4OQ0es%E>?7?G#_G4Q6U+&pe-LU?HB&!lA6E$90d|$kf zST6+Lyk%egkS#g#*XnPVuJKz&-%#pEZ+OEP7?1xFIukrmejQh?tzs3VLC%&H6sEu# zJ|rzbpKF(iaio&E0@KviAEJNv(fI{ST@sIfA}@m9#4f8g^+&qIULis99DdXSrmK0y z{*XH@Fy*%5zE>xT6MRe@o#={;abXwr>IYF)A7+~uf@%cY<#1VOYo7bF- zRROjf{^kM;oa`y}*DFtdNW51(cTVGfu6LUj9lwruj_E$pp#pK$K@xv3S8iFb_<>Om zG>x;&(ACG+Ny(orKj=w5J0V`?%V9BwuNzXJ83jp)Ijta#v;{D!BKXmP_=!2!W;NRQ zDW`758bMuTn&%j`1`eRBN#hzb_PI5jjBj)o>7rMc;1^4utZr0vI1Bxy`};-$nL?%U z>wN&1ck>JsP1eOOABun8rr7gA8ert~gbDAp1ImLIf8E791W_E>9#{OlZ6xR_1sO5A z_(}3nDSEh7fmL}sNg%Q`zU7&B6jqk;A-ic;wDi zav6Y3;^${Z!$=HyA5(VbS$y>7RicSEkQWCQVy#{-esU294+Vcx_XLc|DzjHEls@>; zBn3+ZKU5bpaCvnzj>!v_MdIF5JivnYKe$Sfv>q3yA1G78*k;W9&vUi6JQN1&Nq&_?uV$p7@Xz zf&70Pud0S#@r-|j+3i_w0g0{)NyNJ`o~|L=H}O}uYv3u7w+@slWAl2?5XS_hDPEZU z%^!UY3^Sd~c8L5cB`!+0pJ(`EyARp5Fheg7@B^eXM_KNt1FO6FbSWPJ?6bs_J;w zN@U#psu?{j02q)*&l6W}0T;BzDuZH*%v#y7#tSO7HuXgV!sQ$7{SSXJ-WZm3nxxo01T3Y+d!@omXE&%FFsEEodr~JA_VRM z|7_K2dj?YA-^IG$g+YF$%|3S}B7yFz=&XM{4ua-r52u#t-Rts6?b%M0EmKbXeds|3 zvV%{W8YaIyQX_W8S8r%or-6w9j{IlbMG{{E0u6t2KnfrIx7L?UMuZ6Ox2{0Z1m7g7 z3_iz9Xu($9?|9cgjM~SG9|fo$Zz40k-MN|=jDWG1b_yOkNIuM%vR!2kfhLthi?>@p z+E~Td7^^V>je<=I$CN0(`J)V|@p(QKrqlP+A{YO&P*5l~l(udM5-2VNYBsKh7l`r) z>EnMY|D#t~t^H-R#jc-2AWPX-f;DpN!<3~g8s}toI1v1&mnvL{4zEef-`OWCvb0Ac z-hZ3v7oBPlz_uhRdpj~p(bS2}eQD67xr`rT>eeEOoXg(f(ox+eD}4|0o??2v&>&@t7KY;X=UH;!L|%Z3dV0wC82-PgXXPI zcjD83tpc8)GX!@>^OVy?E!;K?5Mb5#qrSZUkRk!{fw_jUkLzTvk2%U+*E%ZGOe4=p zq7;wzdqPK!)3C>Puc`V3{o>n=aLD5%bWC1jqC|`GA}*T0Bzl>lXkl)l3ddGdT}jD; zXk!?z-|z{?R6RBnDMJS^Mj(IN?4J>k%0n^|r$YBqz0mbsi=P2E&@${IbT4;mb8*z+DN&ZCtj zGBkHwy3;A|2NH4W>rrIhiPH*hbAL4>pa`Ib>R=_|ntQ#cLnU`W5=DR7J!M9>x0>;3 z{Q5`Kj#e;oltB}gzg0bp-fw}jyttZ%ka?lsabpTBFo+w|A3mcMvYS;Pz45rSQw-~^ z{!3+^cB|}X^V(G0pwmcR;S&YWVn?EWcCqBHK z=1`q3plxpyp;GP4pG<#~$!_0->aJ|>^G*)6DfHAZyDGX`Eb%i0Y%Ru7h27tyKoOGb z@WdsLNf16_kqwHr=N{a{NB^g>CV$6yY_1B-9`yk#l3sd27uP0-Y<{!{RG{D-)Y~9V z%QN=IM1LOwettv317{_~WZ*8fj6@2&z0oWhQvsL)S{qO{9nyb~%sa3I&&UMQivXT8 zit)}WK6<1=fXjeZ(HWR>t0cacNW1AenxjN*l_f3^I4=h?&&0XqS5mA+P#hWD&O~!J zH-4(>e#kh6u<_Bs4P!*b3fr&@Q8CC~WmoJWl(gCvaT0ZpbV(Yau_@)80=zZ5w!RJ5 zL}o45`$%tmcG-Vg1f$9{F`vf4IR>zmFpz8sOK%_4w-dZmCH&-MqLvca$fC>J@Q;7DI)ul=+i)J0DvRG)wPdzP zi>Q{93WaO2GX+M!s%TbZRltfku7bI(Z!{c=`OTvqP z!Z(&QvOs@FH>ZD1oQV)adk98gLi~ofGa9ht?V3k4*#SGljbWP?GD*O?ktDA>$vaF( zqK{Rhx;D$j1PtBsx?z@hQ7J zd%-{o;JnT)#10LXZQ+=drpOpbn;0Mum4yWDE1G|W28;TXnbegzi(%3lSJAkeWbfnv zgu}z=EjsBQCfr&DVc{2%aE1#0D;rt5pa*Zh3qHJ?3soD4j zHmZL{`$ys>-dMFl8R{Om5F9prhZp2a`KOqCrjQxapn6*VnY=9el-#%n{Wn_>@PkAtj(?wL}rq;RG5efc`Dr(-b;_hzqAg zGIbjjx$+b<8Mm}IvsKwIw+n1E;#T7lU9x{Agt5SotgH;|@iEmBpNwJ%;f)$@jo2yf z+T=o&YH@&Tbjv%#+%E43E%lm=?yvu+o#JLa0#-pGh4$%S)V5>6-$?s<(1gcQ~s*E?z4P}MkY9(z)nq=5~~HlggHMv(GcoB*j{ZxjtWl?LSa8`D$C%npYzssYEj1BNxdU9G5LZto9#pb{gXMw>`32$jZ1M+$lJ-WVo zm(&*#WAlu6I#^PCG>D|zW`wiwIy=DM8$)7dN^RzXT?gu(xZ$7w^%rl7jCg0o3V>g=>FG&Nzw>XECSxUT?~RwG458bQmDd#@1?6HZtl^mZUkl3kdIOS&k$e z93j&C=8_vI%=iqU7n-3NPQ;CVzB{8-PC;vsfFa8T1Z&0}9_3!O{Y!t-1Ut?&$1`+L zMWO0^k!dQqUgqf6@+l%-TQ_Uh`BdTYAD{1g_+p6@CWOd=KD)G?Hs)bG?1viPltPkG z1}j)a*o5Gg^02mZZlt0MmU1J{AeMy%l*)sKB5FdI?mwbSJd26RA0W}xyK=zBJogMM zJItZ`PH12CuKY}EUkiWkb&_~}yf(r9?r|F0thQ`k0_i{mz_cH<)Lb%_>EMeTOEUo} zbC_thds*8PQZrltiJmh2ybF`>1&kGynV;%~J(Pb*9+!E6iLS{WXH{106erTb$c#>{ zs>R2(y29+KDCT!#a1o!gO}t5r5-`&OxA$NF(POd#04zLSE!%&^n9BZed8j9$NP4ai zgbbJW)PANBoI&2HSUg)j>%@(ZNCl3V)nj0oaBz;K$OY?EQ!{*?NOmdBDugx*+uMoN zK^m!rQ#G>d*ny>uW7;}1E%TJAZFGCQ@Y9~Sk+P(dIWV`RO?P=7525(Irc@u$+ZxyD zDQZ*Hg$~?(7p;F97rn-BGF<;OIpnKR_&O;=ux4cba`C+-ZgPT19T;FlY+sBvEQ@>Gnle}P-Rj@! zU^{%2DT=?4pLh>Tm`U`30-O%k0m||-S1Y!UHVZ3obWVS;*7J~1iu(V^Mr?^z1OFNZ z1C6rC01lA#d30=?HQtGNjm-T1F_$Ii!SE5om(e>UcbuBt@FRRx(HHh2_YM&&l0Dik ze6P}~$RvuJ-P%4BX7^#TUI^L(_1#HS=3FnakY@=TK&7^UZs*Ny|FMd2SY}zO$$=wZ zHeXj(+E#zuc7X3EJj^$Jw_g6n;@=)~bc(T`d*3a=EB7KnpJklc{IpFj+cE*HZ&ZN?O*~a15mZ=z zOMasX6lk&R;klp+JO5EWzGFU#&39&4DsiLN|LT9t+vq(yF5^M-7@fXB4~T9?3Lt5d zMVr3bxrqYK)(gjja2S9TSapCQw8+7dCPuyi4v=(t^YXGu<*4kq z8tH!*Mf6{lh6nfwR&8RmooQJTDktJ|hc-9)D(>I{n0&_Ia8RJ8QusU86&MDoO|Znp zYzi$PZk;`I`fICgm!Y1%!UBDI9-A~6uzK4mgfk{!`#$zWcwDu0^Q=d)w*DM@m*tJQ z4vh0}Gqwc}*lkm%;~$`xf9084`5td9>oR}ymg~@_!ttUFFJ%@3CpENCxg*fgRg;`% zF?2%kz(*Q(%`4{azSR)Y-*KH`YH0uWvc6BIBUQFzwQT4v?>`NsY$fDu6Me;P)yCqK zZgj}2Wk7&*m zI1x;(+uPWE=~HY#FDzW;M({2#En1uQDiZAu47wNNjHlseUL90PU4;g}Go{UGiA{F& zCa!57AL2F$=!6S;<7kujxhPjD8b~_$Tjw%tDX~H+(T5gK3xG_2#Ok6A0 zh62_2P{d#rm@p!KjC*(80C#)EJE?yO;h0~@wpN*!jK0L^hTaBKE`#JuwfWdL71XD& zH4fRZuxV><<14s-H59wWz{5l>VP#^0`xq zqHQY&+S&`H<@8`VC@GJIXdNc7VzzzuMVqMP2QLIMCxyaR!5hd+1=pxVBvF5Ny@e-Y z*OqPAy2m2ml-sQNwSmH_X51rT1CR(ME|~j4kT9yC(9pV*N+;jxI9i(y2Net^yqG3< z1m*6$kUq#nMuI$wYSEytDYDh75x~>LZarY;8d0=p(Sv|+@%T&dJ93%@>RahVO#iN* zo9DEo@5Z(QzAT8&sTLoQ6>5LHRrcCCC;&{{c)H`B@?ukrfkt=eA3TNz--S@5unpoVnU`sFL}D+%hT=$Rb$#suxRP zm~1@TQ64E^@s3+DAw$CC9bSyf%y1w0KliSjZugA*6|K?hvSR-mHBhTmulhvI;&8Mhu&+duItZ(~>3h4nYWe zSJ3!5a-e@68l{lEF<#q&3TWTd(Cv7!X}XuLsP5kw716LYLk2{rU9oR&DW(FnFGu}J zHQD66_S9YBaqkRV4D0hswlzghpH4f;pPK{ZF#khwXWpv1J#KhOZl~gTg~>>Ra&q*t zUt|!=l{UL_s1EKb%2ig_1=TrgC4_em#(l!dr@MayohSh{$TdCI??GE}yGd;{7DSm* zybjDFSs290K%H%KN34)YTZTc?C93jm5lfv>{&Um4Yx^umFurP7`pGR=b;uknaLR_V zT+_$X)vUT#UDmvm$lmFxw=cOD2+MCAgV;JI-}do2F+_yq^@Wyu7q#MPLp|L@>sPn zCFzzZi+-^PbK^rG(k?OK91ty?&i~$_wbkS^zYGHOCc}UWsMk0DKk^re8mUnjYKL7s>U3 z7h>DvOcgj z1-Do00YD&U0Z6-7Qk+w;S#EK~Y0OLx8CXzF2gt~wb253xRu;JUfSGErs_$@FHSh#BSOF^dA zW%8Ut5n9JQDGrdo0B+{7N4KZyCQ(&gB%JTohzZKl7kI`+J89wu?{0q-9fZ|(eRnoG z69=0Koi|hWHU8hqKEF`CRZ|6lChxH*S6F|4!DPsO8pb$>tUuiJGen7M0<5nAJkD(v z6YkL1`1aiMo%BZ!1EQ^*w*>3Oiy|K@bi{xC8Lh0LAH+B87ina1-ve4nMMV}O;e@mv zUbm}8f$`CP!|n(3Y*T;V20i{txZ0r!>_N{$52b$okZ<`tc;M!osivP$4_n3C`cRI3 z_unh0(vY%8=ghh!#_xb5Ni5-t^qi?#BO`ZhpA`+uFA?lejYic`h%c@hz|f$xZ!m{= z@+Uqr2a3f#Av!wYFJG-A4G5DAML)H001=>T98#{SqL9dUI`MyXF3}AX8z~Q`S)*Yk zLR^lLGb}{JJf-2`4o?Td{B_mJhB+_xYJ3Un?GW~2Ir#I_^^>e-l3r+5g0mqwB~)Pf zId67)_oQtRL{-t3lANlds#-BaU2gP~F7AuYPl4(&)oJhc^h0P6bRNVMK&bKDGTRN0 zcFrokBjZm(w~Bw}Mc311(2FqYl0o(oti{PV;F$7Y%%cE`dCBJcU)PYIl=0)9#tN2k zyN~SV*`8J$qm_<-Epmf^vWHhe9GMl`w&Z7;YRkei!<67V>x_-^^eWGyN2z8-wVT+3Y4U&1Rb&EQq+G92#fdOTjgtXVg&^LkrQ5|2wqZ=d1RAKTR2;I zLVt4J0 z7*3{PT?>Cg5S2joq!OZ_W4HlM;P#AToEEye`+h2Db*$Xf5x54+<9|7py-m_Mod>q5 ztZ$h6`cI9X+_FQqjAdS`G~cZ!}bz;-U4>ib4gsZroW1M#rgfq1cWY?CnCWz7f;&6#Qy zI=xG@@cVqz4TBw=cgBwV-w#4>D_J^o_J4nCSKhWG{3T-{U*1*dN(8K4_Xo^ziHuw$ zKlWVk9(hQyJ`&ptVVol9gyJT+CO<`Ch+@=3hpQ=QBzyv{}IENpGpB=i~HcHd{FwHdPSIj1X43OADeNrB;scuAVHs=&Dx!lV-; zj_~w~(n3V0ZRQ9hT0XZ_=9*69NOPU3AtTaEZr?n0_n5N#DZW>R_Gb_sGL^1`Li9HC{v8oUBekr<+y zl(;p~8T>a}0cz@A2{a>y5`3;DD8DM?)#P@E5lxq(Zdg|*YU*9TC>Z5}=EHyet#Ixn z_VJAACTAq8a5Amy;+sL}83_MibKN5$fKuy{DW`ju#Zw_cZeDfE(*PZ865E&pTuH+Y z432B~*9cfwpvYP7Hpareo>zv`jjR2ZM^iv;7-=GKYvI=#S4qei+&pm2g}--q_-j5@ zhP26AjgmXu<4uXWjrRPIEZu*G=y^fkUDN-yKU6nLJqc6O?$lm`+&Lio33nZ%{78gx z`QKOb;PLL$oR;fdpB*0F@)!1RApO+9NuJ217ovx3o7Yr|VH}_Wm3cB;aCccm2`I!# z`4|JUBW7l18oOSPCGh&QrJ2k!E8h0o!(3RUTr5%W|J5z=&)PuexNZqYeiiId|KoJIxz1cwH2*P)7I}Ww)W9nUj)Qd1NdsGz zeAU&h=?2+3U%B>3k^6rTnzTv!p`y;eU{e|I zujC9vNDz{2_3JedKq~8uVI7L0|eTJA?;p=4{WIkj-c!ghkuS#FY5D$j3yj`(b z4efk%qA`?#eWBNz`K2@`{wSCLtzC50_gSC`5#^}zX!(G$V0UN@m+`XB;CL5p}&?EVYwv38JSVm-<%rO?kKMZcyS_ zJr)rB*&*2=pP6v;tv^K``Byj|(^7&NyQVwPBVRy4E$>>dm4dTRi$filEUXMR8c$eMZ}n@zV-8YsTr$k}`WFRvY26IcfpLiTC+^!{#I^XP2G^g%$E z5jSks>oDm4kjvOnEl}uHYBq>;t>RU2ZaT3Qxy3MFf(Qc zm!NnG*&DDHjth<*s-#2PKtVU8TD8K;!jqW*TMl-@itsA(w36_-fxg$)H<^|$`Z1c8 z*(?z0mvPS*I`I~#hzSP0((y$LveL9-1FOMhT=3?44-dHFLX?It*b^`%0iuQpbb$3Z zuwTabKq-HgdH(l$lsU#fzq;1N?NqH98=0eO5(8(cChu7WdfAH(R-g) zWi9fAdpRF?SbIlt;NI#mRxlsE-%LR^?)39^U*QSFk6lr?lmCD2ZxxOYd~uJPQR(+f4o{dR3T$J_7Ha?HAsRlfe-7unWZcpgLu{?oH_*Wo)`@Vb_ zU_gH^G219|pw)MTVPJZrpxaW(S!OO_u%Wn54zW)ziqACC8TP%rlwCyS2i1IJGadt~ z)-u5@Spz6+3E%NlLD6Hx?@|7^OY!AeNv3-nZaWM>nkV9kDjl%DZlkK_w5Ae4XLgvo#KW6LkU9?a_h_UT)GADcY_@cH|bYNH0Q z{I!p~Zarib6UKc$cb^~ow%KDWoEq1moF@G8NxjG0>Cs?2C;yY8?7w($+;i7-9}r6s zbYt6pMxJuLQ~hq((-$uMq9uo9a@K_D|8q(?v|i(&qTv<~Ydw7jrB1P>Hc73XkxPHy zc{eXTABBdl6Iphyhp#Wz{PMM|!FG`ARF%DB-c6S_W_;k2I^`J_=OfuiNTp11k1uog zx*W605u6T(?>+8(wm}82M=}F*V=c;PB9U|2<>{{CsSBH+tJA(OMA$l8<7=nofZ82! z6JqAw*ey})q>fYrs8=Y>5Aq4zy7zxWSSi1=bY!`Q%;Qm9WQO5m(CEix!M03VWQ+tE zE`=rq+$kAn1Yj6J^?vX91CE>K!8QOyTX`n|%+hq!1g1u}-Jr*fq z*C3LX0`iL7TJDLR0EF&-A@(K^ALnh+${v$tD6?1P|A|42%Lnn2=Vm9FjeLKJ3?$b8 zp!>=s|9|3rR_IBW@I*o9L zI1^_15EfF|9!iJQClGd4))0qRjzZ(ia?BEcL+jqAzBhrJaS+ zjv`6pA+ok(CK*K6bw-z9Bio#piLVr;0+k*&($#4nnY6c=4f9^R^>qtzx=WVm*V~*| zd0!3U_Fw(1qJvbVk^+j~wVelZ3(xku!j;>DK~VheY6I}@$xZQR$FqMcKSaG_pO-LC zS{7)@p*^#wtB&Vm1uEbfGcI*aJ^p7vPIGM$>YBx2&2E3ad7Jxyi{ zV13BP&&noJHJy+*RzhTkZp(nm|41eT;SW`Fsze!@e3@YAEfMVR`?kZMnh} z1vve1NXXt!)IdJVLen$BY*POQ0{9=ZNtqr5m{GF)K%*u`9ZDEq`^XrmN$6(=yj4q9Y%y=VpGsf;ovxS?wurG zfmrV2qj#kT*8a%hYMBo5o`aeNv=ZkM>(uJR?V%{Q<52}6FT}Ha(IOxwhCE{nm(7`C)!EO|?BtpA1oeEj#2q&@Vhbsb=vYmfOhGp^r1yLU{N)x6{ZF*o#84e{#+%TcbF@_ZW3?RR&cag4NvkW^_LU&p&`S zz67$s@Em7VV&IKM|dxdlJ4=b{7EF6;S|) zmGTb73n<1~=E=o}bEt=DS>Aglga~8s$14MqS;xG?f2-yX6%AVYIzJTa84z5ls6^}r zMz;U-O1x~O#U32glZ}RHApKWT(|Kw^k~I;uN0)!7J~WLS?;5ZME@CGWW9=+Mg^+tQ zZSQo~l?E&kV=KiVhQW`q%BS`z3lACsS%)ag#kiqcYNmVPa`a_j7)@(ZX@-g%F1C?% z$qa<7_2_dfNo0aPkFg247dx#&e?1C6+5N&CDMC69h2Dz5~#Hhq`!tOETb}iZq<=spCs0!(qqrCwn$!lyspb}e12>9h{VMUWfi27 zRlH5l2TcyUC;h_g@B@4DF79}r2lXQTK7oIE^p8IBp#>mn6{`5AY*WrAn@FD7S=h}OkGae_{6qJB-3BMG1Xk;l zxDCx}l08W}8_f3tFGKqiXPn*~>J0;)R_h-_kiVxl@2(ixA6jk_M`Rj+}JO>ntx9?cuvCc(e&+@t#&+<2qPuFtyL^+xuDSHQyVde{LEOUi)=sC|D}uzIMm(0yJW zH^G_`-7w&U1!Dg&dpO0Tx7djw!TK)&01n4gMOs8-l?I4W#;O^&wdM)UQ{2XD0F+Y}c1^ z*9&S;(V0=*LHsWbh@ApWZ4BdcvIpVmlYY?!G3pv9T;L4N7^Fzri}(J%<`Rny_t={6 z_Hrj&TKNFCmR5A` z|JmsF*~YfpDA5G>E8ybj+)WNbVlgwsY6>Igi(UZY8M4XA0$a+4hkiLoS8YkykfW+u zBXW+4H>0oO5cjrjNmBS#UJH=9bjw))k^nLy?@@wMIP!noz1SJq$J&f;BE*^H8*TiK zV9^|lU|!SbD@I-ZGCu$a$?j-p%QzyspnW{%Nhzfp5CLxB|1YeUQWgus@HretNQ|M_ zo9qB?DfQn&@=~XssFTuuRqo~9d7)hj2U$T^5^)70)60?A{1NKHF@mmW{S_;xTem7$uT*S1o^^=#59}?{0Zo9xQ=$chchibo4BY5$=4td$i4i)Mca7z`N{o~V|fn4n# zn|*&7)3BmVr|Zw;S*5?#gb@3q;uUWkM?6(EP-NQLEPifLD&AS@U=f94E4;1OB+<9a zW}{_zG@ch?hd;(~KX$sUAw^(crS`u$n^^%C!RMmu9qmAPYk7~!z2t^UFQ6kiyhW(P z89GRz(b!xb5RrZq~;FI`c75-$TUe_ zd|qiKDxYfQX5{vB*+^&7c0wcCf72FhuLQJ6{W>wW1V4tHKjLtdU6S=eE*efHW;2ksgOpEN|{J_ z(;j9@L0-xf=8ZKPsS0*jWg;#)aknDDY(N^}o>J(y=iS|jn%#j;(uQo@Y^@mk)B zqXU-#aj3eO_e5tDfIUhVTU{WqWEnnxidcYqK1+Lg$j`@VL)!Sz%Q;cr)fKO}JLhr9 zokqAFHDGqmz$=t_nx@le^wq#*C%yF;wvID`r2NRM+k~FwB_z0bHe%FSDo))-3ffiq zR>R1H;R3AV3carl5~h*PQHLm4L(-8^HFtQG++i=bpdRxRW>SpD1ASlOj-<-s7Vp?VO+pn!B zO_>&3Vn0Y(m*{rqt9SB)t?@Anc!3$M=*UK?T*$I&n}t)l4Q-z5eQkX208}+S*DnKEJO%w| zr}!Q5lr^)qcBZf&;U6HU?WPnuRik79%ZPJe5<_?mUV@i$?iE&&hb|R5XMReaxm7!A zKbJu33@NJa$9559|MfDKVuppXayq{eWOwK9(C>cQ=xo(6FqOHPG>-A!b0!BdU_T2%X;Un4<*L58C>{25k#Vjd2*395p8^_r6QzOa36t}P!g?dL)gt>M**v8cy{EWdQl|1LfoV5A8wY6}EugEybLV_J z(2z%7isK(@W)-b;?orT=KGz7wP}v}38N8yaZzrPOV}tO?HY7uT%z02hGlfZfpLcvp zSJKxiCWMvN;rZ+}9m8YdJX}OH{laPu&j@%K?ety0JeGM!PrYK&Flz%X|Kd^HMx4hO zcp~Rx;X0xlW3OxB1WrW@oPC3q#j1o1q^Yi>?wcEf#W=z9WU>j4@vUABo+S~S*`QF6 z#6$6jrFuX?m#t`j=3wO0ul@+Obf`Wo-x57^8^RQj9mcvN#8~wJOHSHex5+q}NK2^k zx`J#%iw^X#nhEsEX)*AMBM?R>=TNB!TUtk$bnuGgd|0Sa{^K{|oNDThZ5j zodm0_VpjH)+2}-cG^?6YF|8$(FcpzQ5|B7$#$NBdfwpCThv4ElODZhIkz5~pTjGvI zbr!RcQBtl>rrb4JDA-8F7Y6|zcyRpy5m*7g#qCC5ssibSzt8=CJn>FVHHeU1X0&mI zT?Grqov`p6JEJ*p)t}I~poG3Y9MW z=`LB53LIxy5#|$jKPUpcJ+T+ zO2hkFxa&_1LwfB*ns2DxA4Jlf!yWLhz!~)6LiF{2>cg=`3X!Q2fAKB#8HJRUnB?QV zMJZg!{9h8#5jy({m_o>3_OcxA$uwplm(YYDnOrEKT6Cf9(G{I&MW! zT5q&Yb&zD5EyJcTPecIqs-<|n&jx>x$C4-XJq1kioy|x_D#VYtnQHawX(a#v008;t hR!`%Qm&^bG_KyL>r~#2cqzSRaXZr#G00004Sz7u!U48%n delta 18270 zcmV(nK=QwwkO7>K0gxX81d*{JI{|;qYyNki`FaAOfxz(;?k=6S8DBX!IsXt$=W`k* zeN(I92jk&fJch>-kOE#Hd*tWrQQ8Q~*1lDP5^;Cxlr04+Oc?&wqG#DoHg7|Tt*`b! zuYgqZm^d9h)O6`)Lbt@|>KjjWe3B6K5XCrEY-r)}G8fDR#eSk5C0X8cFdWmr`u;I(|68j5>8J)j-^oF_WaQteQ4&PjNT-3MoV0rvj&R3ZhemWYA zjlUX-1s{5S_OEIi!bf4J8tQ3&Bd@p*V$)ED>~_EMl3Zh}d>@K?tA0r;Th$UDO?DrR z9!!MT@gq0WynCh&o0D(~fa8A?arfjoDr!P6s=7} z-5i^tivm!A0-;R$BOaY{b>#Prgxm8Qsy)~*c{V>{fx;=cV)k2XIzlA44q^JzkzUme z-(#DClFbK(vpyM7I0%y%uq{ADlYWt1J1ul zz@?u$K}^2FN_e3|UIm@o z$P`SDJ^DAlPCRL@s*KKfB)M}4r?>bNN8UKlZ65TVF8OZD=HUYUGb!osJF`9YUi-5r zS`HzLAO%2)a!|fw0HRtL35L`_WK7rd$jkP0zpTL(Vw7U^Up~E%p z+Snsr*5c%QhS`5CO1Xn|ujs(xarbTtbq(5EVoG*wO<7eFpNYjjwUM?Lh>}cn0@83260>Ien2!aupgT$=taykc-XWEv+$bo zRKxrt3NWww;O1J-igWJOraUEYiDHAJ>fC!QXKt~hC0lo*pSoasit#j7z!S-`ub|7c zqk6X7(cgdDU%n=tXR6VIy zNux_%)rz>gWO7>7pBKj#iLmH#0drlA15u|)t&M;9zWRY^{y*jq{je!5cMTHYgt-Awy&1 zg9=zBumD4gdK&bvjf7+V)FCo({EPcC#aa;Pg)Dpr!-1HB2bgaw`ez{>V}Omuo1$T? z4vl|<8zOH(U-yg%Ue;Mbt+6Fb`;y~ijryl}7I}QaH%*F}?GKjfaZJcxLP8QMxqqeR z^E4x~dJD%)$H1}sUvqhRVW7sgV|5yI` zA~6QA~HpXz4OQ;Ol=)iQ=OpZi0P@0j4VpRi+s*`FZ|O-R^$_ zgh9Z5fPc%uxYTtoJ&{V%uxpz;?OZh;7TrUaMhDqw ze1HmWFKq~mN?&m>cB?z1U9X1NCom&GxP9oFD{BM>$}bB0q4i6mgHyHsjCv8FO0`fB ze*cUxDpm`{(0c5|?RZJ0#6?p+ROf%+Oqp7P&{8)lW~4niS?{5gom{YzOq&OwCOXBQ zvI8)W=D?c(Top-&cJE+tGRrs~#jV5ZgKFBAPpJSzvi*tq_8+M9Q9ML~gz6 zmwSHw+6rAfz}H=H^nfN>Cv2Qdvsr%*Hc9K? z)^!mUd7R5}HouR}nd`$^$rQK-h{N149&EvBfT`aWKM*N7wgn#q%*c>aTea|VJJHEh z!PhK%7Z;1Z-f&kVyfd5R#MYRwCi6P$D;CI1;A=|)9dlexU<~72B*PQ2TC%ivQ;%ws0h7fZv?zM&iCC-MJw??;h5M-AGZ&PK}ewapC~|0o&yqz#tzIFGl9b5 zeX$O&6vC7K%ZHZZ&}^U|Twd8^_zXIs{@V{2rzNWP!vkOm%llt^2)N~@d6D$RZlIwt z;et>?6&m#6sEe>HEVO^wNX0XNS#pucd<&>sDX(CqBg@>D5v>|8bM0HoF4i%EZi`-({kt~bVslskX z+YbeDkwn7|XQ6ye`tNNTZAV6InwOK6@_+OmH5&P7RSsVCjH=J2bDm3?O`bRR29a2= z&HT>j3WEpE5oCYs=#g)SU4!J}M#j<6mQ9P`aRT8$s9=+lALCLhhP28$DXC0)KDqqn zFS3G7EzyZH=HtppPwB5SwbHIB#pPv*hmotOL-p{nV{IijvuaqsI!0GADf}OaoEOJZAv&DmQW{S z53&aJJ&>r>OPBwTQEQ{dTm~TOqe6q~fbET7>Jypu&{@H{4kPqq^B2zqBVMi@)V0y& zP{|Qb3l9ei-!~>C@u=CnB;L0qS*}El+*AI3Qz+Wt=%!-n?MF zgXDbn5`4ht*5(d#qdq40%2I#k_oAB0H$*Cy=vhGAnx&F~_w)%5 zqp9YOhR#3hK{t`%%N|uL<%ZnmGb_~+Ya1nkz7GPa<3%6wzZXMb&@UE&blht3X=+V&Q5ak)l*Eb;MY5&q!D4 zZj%IK*obD^wzwP(OD|^<&k-_$K1;-ZkHCNUNSmF>e}U2-x+5dgg>Y=x&|+c-p&ke+ zh#aq;3=Kvo8&Q7McQW9u^FI@=zz|XeUsYWl{MGNPC@?z4Woys_7Baa=58m|Jr_Xb8 zf&U^;E>)p7fnv3JzDpiQZQFJyk%)FqdmJZk&TQazXcrY(Q|k` z-O{tQ8k(_~@aEbbHof2Fyk?UdAXq!Oay>-Pvx+}^keI4}&u&!}ttSZ_VBnn@6 zAb}T9>wBE$>^Tytk8vl7ey|}!k)dym+b5QicLDXJ7Ztw(#qi(a^nTPdm<^VojTljJ zD6~2)W|c16x161-F5HJF^nt&$V5EP?6O)w$jph+Qx3`SX)95D!Dt0VD^0+?m6Ir}sdewO}%FrDjRmp=Ag83leZ)^KlPr(W$eaOdE}9kp_7 zJr%DyC62PAWjDKOEz~2XHRkn~I0*Urw73i8j!42$cJG6XrHpYj|CuT%lx?!w#y*Ox zAL`txRZ-8s!SXWYa8V@H7Xp~G++=8_bBw7UjINpSv!%9wTX=$MtAu}ZtC!_X4Pm@@ zEOlcOMlO=1#3(5cC8>i(ip%@*UUGbWHPQc5Ge_N~u(SwIiV*5xyP zphP+IBmHcU`^T?XSh-kHSlW!{f#NtY@=H{7=!SMzjPc~m7%)@Y_&!{B!>xlzWv5*m zLLtYNKMMUk_Mor@a-M(exB%3AGs98-|TfO`ER*62I_nbx0?V;=?$m1i{ln3nBBr}fvV&EDIiUCYN zmzqkJM7w+x%aDEy0B17>gm_;v`$4u-9Y%h8Wz&77wCVIq+Dqxz!Nqyeo;u#EjnjD~ zfP32kgvMF;x)XoPz?Udc=AfAI(8q;E3CWdSy_xc> zUu(-kQ7Ne_5v#!`%s(?4b;t{Z{slSE7RS#!`0#knsBXTfDkFYR^AxvwCm8SQ-g&H)!BChH7 zVg#8Sb$(o#LS67$=qTDd#0sP)=jmI=1D0Se9MLxs6{yJ4@<1Q3PxSl2MGtKkNuT5v zjCR$zo7Br4l1(iq@V~WW#-8h$giAlcXeeTmTC*lzn(F(RK}6=OBS!QpA@Oe1UaWsn zERR_W>I%8?LTxYq%l|(SrNU!=zkhui$f(P*$L_yd?i_}WJ_Q0alad8`!dyi(NT*p8 zDdiq38PHWeh>J(R-3nK#rv&=}oju^wThf93-t`PG!{JpNn)(TLWwFum&h zorECAN8)A#Lq?^_e+xs=XbC5*=baRv>Xt4p4kZ|7`@F3w8I%Q?JQ>%?KwW=x&P$u# z-1mc`e*vXN5K(?N^fa@4sF6FQxw;zs7PQu|P}m};{rij5~mO^9}g z=VWmLH0*zT;FRDvSkLVbnWKPR$BC=~t|g>C(TE*$mCzz{YlVwTISb$Ztf^L%j(I`I z*k)-%uhy)(YPy>1LhMF1b6bDpGAFZb%{hDmzTd@m}YS0C9`b+&ixNJO~%Z2{}#DC6AHs~e}p z60)@;QgAPe{?A&v5MtU6du#6O<_^?*>`(LfTzod{>-*26EpIx7-`9WJ`{dBII>h_k zyH_xMXgtyojbxSXVBZ@ubo(mYSQtU}@8Q~q^lq2ku3tKs*a;gfh@|EaQ^gBa3)Wdk zxipbY_6a20dflNx2WWBU?jl`8qk+)|RDeV~I&q?J(JY&vf=#5Fa)#D*)F9@HQn-Vqy8*<1N!=9?7)5d6^;BO4jT!{h? z!H{QIIpA|G>&aXqS9)gsrO25bPKU!ZMUHrAkP&tj5$`1cE^w8CnGv%Tz6cft9QrJX zgU*1t%xxAXXZSZumyltV$2SC+%Y6jYY_*trE}#|o&#eK27BJ!$Y; zJj?h2RWMS`iX)JkqvyzE@uIxtcfGo5gu2;-kPk!=d>ee~T%8p5hVbDlZH1P7_C>NU;Ki`&Y!5EJ}P`Q2)^g;X6sfw zd7iqcqmi}-lZw#vynYgH<~-?+0u*Ko{Iuv>w@IzhlwS7El)b9&j^z=!?}_1ab=VEE z2A>Ir&I@-0YMRXRO`JEzVc1RYmKj<${D_l6UlrZC%e;Tp9p|@f98jcoqj|^QHHn83 zX_Yz|nBUA_()!UMd0aP9UT`2LW+|w%_w~h!;q^V=xV|Zg9-G0)xeS)}wK?W^fl>8` zYg?uH%VG33i@@3x&|Zs#&=oS zx}s@yHFEpApokW#b2-)-+zqwS?MtqiMHLhRxH*$tNrVl=HU}G1y4Dj-wfmi}?qPUm z-N(~_9wJ!QeBI-g0?}vf_E22!HbdZxIeJE)n$3SwXY@ANzw@<2ckO)g{S5W1srq7 zrA~j&F}&_?yVOb@J7PT7x-qbc>UxTcH5h42)xR3pbkePCvL5$#i*p@r(fxkLLgp4! z8Q#4+gjXNaHHbU&=enAi`t`V%0W_zBJ>ozd(nO^>!|AQ+@6g-#?X^@HHCw!cq($!{51Cxij2e zVx)@#_76GMx`4oQt=z<>`g*-z=OJfy`cX9D}i6U5=V zzhu{m@H;st4FSgD=w=Vx5H22Qy?X}|gW5W*2X{h_OIf#@_)s69QQjpYOkIbq=jwk` z6!u0XyYm$(7ZWOt&uDdApbIt?Z=!!Bnak@bMr4vuhzijt+h+j|E-grvrq%`SF3n#Q zNYVe5H4?kWyP$*ieF*oBvuyBq2fZc@@2sTgbY1&zgVMQi6$O=yjYjPW2%dmOOQb$d z1hcS0yi7RmU31`TbA4Zs3!xJYX`z_CP}LpMwWSJ+$8)@13G(xBPYMIE6Jnmt8clIA+?Y$3 zvq)BEZRU2XP+fa<9x>| z@Yz8Nvm`veUg)?{wUw^=P|q1aDzqT9Z;=kRy~8!CuR@Wuxa#z^D>+jryQsmN5Wkw> zV{?Oypr)Rh7}alDYiWK7m(Bt1BrxsU z2Z}8WuzS?De`zU7uy5|{cR{t0rIB{jW!(5jg#EnmGeRy6(M>r0GD4+D!al_Gqetcc z_b|oE1$PuCE2@oXY9oK3G9gVdQFizN6))*q+Dn5B*4~k5pqckFy~?KQQH@$n(1mT> z%&k00-}N~rHCesau5)<%gW*@w7``Q?{Uez-fYGmQrcgig z0Nq<Sx`{qEdh z6@?hXKYmL&%FS!9fSoX2ez!$O>)tX2fiNjQc{9Lp$>$)9e6RM($w_G`X={~Ab1_0l zW{JI##D25Xe*(hQKgpz*;RQYh{Fy`oG#tdxs^T5VaFH7}>+Ax4|05ELm=`OuN~LPu zjS#V;i@lThi8g-?z{rrgvN*#B^|H z9m*w)Eh|+Sb{Z{d2QUYMbhA{~Iyyd%0(rMo1)$AjX(4~otSa0KZmEi#beKQ^m6W8s zbDc`}NK#9NEx{b!3ViUXHp$Lt-O3e!XXJ0vOtdJtJJ?efAMl&S*R*k1dvEotIg$t! z{Kcst$|>&Ug@m}}i6FM&<-p3;s7P4x*f5!$I2ny$rGhK)KpNZ349u=+?z9i5`n7`| zbf!0!5$k`oRn5Ow?pB+d4_?zFGYc>iwOiN|pGBGKL)q;=42;GOJdZV=8d+?N)3vK3 z-2YAP1rly1eYFMWrSLhayx+~!J0nvSgPqUPUd&SH2-Mt#uu5mal`f6s^6NCtT?@%i zs^-ouz;!fseIwHcN&Ryp2MmAr@L?OWI`;yx#3%e16nG=Q(mS^c zmm$kkfK(&*X7yvfno;kCiM11+U5@-E`#@l~W%B1%25;s>TTpsv#P;+XBs%4(!KjwRD zZ;mb=Xw4D*9!lA?US7c(eT1zN?Syj|Zbij*mSj~uvWm;HqsK8N8c$&@W8hrXF(V3M zO$F%G>igfS9QH_pVT@J!P!4ek8apgSp%Vp_e0+e_haX$}1fQNHtdg>%i{qm~rN6tjhVrUowAu!wvnKt*~l$z8y%De&u7S?Vgvt?BE`N9rD%b zV9MctTT4B1Yf%P*f1HzCftyFJ&hzp7|X zjT^#7n{0u6eYhYAdt!@E_#2J!#ALcf|Ctbz>ZjOljZh!4-j3u=P_W_l!ecmiC^& z4yYBx&-S~R`7>r6yWm*QJ8yhC1=SKq1PECdkxG|wHmw}$IZuBv|A9*7{AU+)V}OPT zUnu&_g%|blf=nTZcP_=-W=*mRXlCdY9OzTC&VSpQhwY~7%DlKPtng9cY} z(LqdvQwFWKQb~Vny$HqX%680KNv^jzH*wYy^h25(0(5$qkZd?7B0>UEOrJA*+)d0M zjV_tp_1zg_J9>hQV$h6c(gtUO<<8A$w)Y!i2rdhkfa6<+!3Z0J3|`pM0SJd57^wq@ z5mD$0I*xN5mV5moZ%=G`fx+nlXPYvB{0{Re&~V%4cP)QoNi>VSxZ(n(cSwp|_e&)r zoq#BrYx#DDCl{un58qJ(>_!v}0H`BAqcb*6e9c*KG^Zbk#Ly|+Mq!lWCKN86`Te)u zb=-}ym>r33y0w9^h5B->*_WPSeNT&RE2_#%`*oek?zYAGHCQCLDhy_Ee(ZDB&mc>W zegYEX{8E4Ng%FfpF6}WaZV@4|A?;+i2Drdf6P4eDUKbyQY-e*WX~oj1d8>|EHgM@? zX)qDP-ZhGo7=+V=aTiG)1d( zfJA=G6O*ZNof(By$JF~zf#&55ALQkGDx`!F!)sL#&Y%_P{5DnW`ur_-LnK|(KkK~8 z3=_ft2yE)r0&u~FOBOd%=FgP0g4~P@Y0iNWktwN3(BZ*YUtadzi?+{WL#D~$Bm$oM z#cqF_qI2{FGUS+Ve&7tqQts|@3)>K-h|un1b^0LH$h&HV8jElc%HDZf=`0DPUn^8c z^2*!XhaZ%kUg&Y9+>OGUh;p0-;koSo4x(W|!C2S?Q1%Mv;C;lF{yiZu_8!4GHsAGv zA~h2BY={L?5td~xAEFT=j-#8soG)B9G?9OCF9Jz;Z|TVa&udalT3vSOnaGd*1rMA$ zxu2O8w-z`X3=sLSrE>E68Qp`lD926i4g=~0;U^loP|4iP#_es< zB?%2BURz)VG*+GA6u7K+5o5h?K2B67rx{8S<{g#aF2?P7G%vBqfBHX1n;>(QYpj0} zMU(b5>%Y*OY_04E@KeDPOR?6U zZR&I6sgaU_#|lM3?TP8=$AxC~Q(o<0(PJ(^3<4NdAucdKR@FnuC!AU_9ZMBgSmbg+ z6u^yI6b1eaw5en5qt1hsnEijmfK7jqcfJ==++RM!E0RF7YDnEpw)wuT5hFK#PJ|6U z1=xV&Z7AXhmIhA0oVV^?h0jv`TH*~Y{Vzwrpo2aT3?ubVBR-eL^pO^O%VN?-SY~Gx z%-$1ZX8W;$t~LR^#JJ-Bn_F}81#6@91NOr3RCS zV3Aqk;E^Vh3UpY2XiIV0Q9pq|I7#NYIfNJkM%){IP)1+h_4t-%rf5`9#cX2G7ZMcf*(! z7YTdj?r?N`&NS!RX6bLRIZ(D6o!1%<;L->k|DHh#>%SyweMVq9lw8vnV}JCFaGWr} z;y5z$w|U$Q6A?@9)UMm`2IlO zPtiCn7TqXxON*Rh0&!Pr%nHS~Pjju1QT%wPK(GgymKy4mJ>K}JUp%-MBNX6^ENp?W zpKDZt+<2)XBgf(=vp2{*aO?NQ-^azwGM`+f3bb_&Tt(U%4BR^CNp)&<41dNRH0nYy zm2qy!?bq=IRlb`NkeYu^ZaGkLNqTQ@7~JM=Ksc-HpEjyLN&HygflHFlL$cO>e-x<| z8?AA>+Cfhgq7Y4qbO@l+A_*z%zqhwXpK=|OEqiSXDb7!GFp z>y;(t5O}3!`pa|AJ*3;e2rBlkOPfyFECpvg@(GJo>4|~LPQev4LNrMNR z$xvusI59!@%Et`QA~)Wfp#q0CurZaXMmaSVK130Q12?~sL%Oc;G5v^n-jQc^b}(mN{s221&x6q+l68oM`|fuoCo7Jc zM4#DH&31o8`nd?T+3p$74_2cO42V%q;?yD+xs`Waf*BFWJLo2vv*|gvsw^{nG)_3sSyalN^2AU^NvFvr{D4KGM}?E@{)Ng!NU4t^dqtz^MeT%n@e|nu$i(XOai>P(9;GDaKaqBJpU8i+Gm*&c8bWWFQb!*!++Bs($}&F<^+UZj zP$4bO9+@856Z#?{NQD)Kgly>a!oBG=a}KLWFxau{7~q~T+1F{?_>!6X*AM2z``~0)*&0_t6(A?B30CceA4=Cc4#h-Z`yhYq zW*FgkEDXjqfB`f!gd_$jW##&66bs}Y1IaxvRATjJCE9*)q|kaqj(?RnK`wY-H ztjT~^pKzkK;!G;#nqp?jr^64uxV3-&8Y0kJf$8vRMzC^@D?Fs#}Cdrr-)eRJ+Ci$qEyp~u|rXvn>Coz zqB(-o0**gc`IwD)bQT#&6vTysXyYw;l(-O*;gxqZjXk}+xj5$bRLzy4c_e?W_xKoM zL}gbSqEg0Pz=(S05O6fZN&#(H`spq8wP=0?aT_l^dlj#IT0?=GW`PoLgD((&B(Uuj z>ei|=Z(6Nl`FMuZ3YM}4Ua?_oXxs4JvrCtx@Y&1 zg-=CRXBI&~%Y>a#6Fmntmpy-iFZ{wz!@b}kxGMbg5YDeRjK6ILV+$UR+?#3W&|IV< zD>yKSSwT6@K-jpmpw2;q8Bw*Sve~xZGm5I~#%gJ`mJ+pv>`<-jc#c@r`{X-4L|^XFLEkfQ1U@#HkWedvZgi&y^RW?}1NdjT-2V;3|LLld?bA7^2+g z=Q$5NI!nM$gz*)fOXgqO z-(h$1zGgwM%~$r1OrwEOx6jUw1W!wSqT3p`k`-d8JJj1VKfju|{0X>q-2b$l$3Z$jEQU_DClb6E=_+6HonQdsnbYhc^#Io zXt!!ba4d5Nlh-rga2*CM5Y)VC|MY4#TkbDylDU$K#am=3V(NTmvD(#gMLq##y8GcO zwQVlkyEgJn%LfO7Z3vB@U3LL@zlo)}dS_Vh2|s%0aG=Ai6im{zvmep%D z^%IDDL?X$rp#g=HlqQYne1X+UoRMBadAF?kBSwFn)<}De<>9@bO<};M^T^C? zw^A1r@Zbf!PUh|&T?C5@ayH~7 zr>n5!!0)Y}o`?I9!M6+`f#s*s@YSXJIV@}*N+^2EVX?+?I`*vl^YvHAG)4$+VR+Be znF`FHCrqLBHs))QUR@&qzQ{8eSFgJ)I+(8~lH7lkE14Y3lBKcm?fGFeY*t;iW9K?6 zJx@Pn<=rR?|?M1mv{5P_THjG(Cdhy{vjPK(?bHAa88@UOSC2FVg0;YE*l^T`;Oon(ZZm!kM5d0nC#QmGruCP16&;T*I!(EUAne}NEf5x z>Xaqb#fH&a5U*)|4ttZU8c!krE=X8J2*iK+v(b^IdyZel49jyVwr9Y??6I1oi3jJ$ zoErmqP5xabk_ZykcDJCV87-)<2$xKX4WUcj4(nvM7an0VIcxSdU-FR2zCVQvHhEE9 z<;|K{N#bdyy! zXNA>q_^wXx#lo%zl%PnQ9HgSD@dC4Cp{>t1SYJqXj#d2np*-}=(?@PQpEr=)Xc*n^j z(p+6>s-pLd`J{Js#~AqHG=lE+9RysRGwL{|f&BNOZ})Jlt8>uZdvi)=Vh@wo2##96 zr2fnyV+|+`90tu*y_&U;sPum%wM8o9Y6wVq4~J8^qmYXNU|)LXp^_wo7EJ#&-umT> zT*6CQEv&da(LalQgqjl5lj2|m!c$Ks)VHNZ+C2eG#)tH46yATdD1{Bp7Pt;DlNlG z6C6DbFFApvMIFi9%W1I_w5`mZ{v2Xlag?@-T)OyBBx2W^fg0uZ zxTXNYECpHxNjqp3ktUNbA%zxHO_Ku@t{8t5J0uP7?;4R+YAH0x zglsX`{_=WbtJxKWv6$L9gB5%C=S@wRd8O>TnRNfc3u94xqnl7aK+LV5UY_-H#<2-# zaB;|fxJU_-)cBch1X|FRb&wuMq0bZY(7+N&FEK?WK(cNDpI_ui+qgjuklzUbs@N#l z_0sX?qQh7q6()ap4rvzU|Ik7oy4qPgD<|Z=wM4`GDgo;FxNYizLT%tF&J65*wJpC1D@Hd3pj zVm{jWIpVl-b)ot)W>uL9}4}D0!Z>rsLisDAJbn*?R>`PDaJ^?2@`zuH{;k08%E(bZ1 z$}K%L_}6~~)G0H+b#Rh`mXXMQ);F#c;HD!c$xwxyX~(x&e{fketV_x#yw}z-O*N2w zv(5r*`~6T6vmpFsZGR&NqWHJBR_Tqgf;iVODY}0qq(KPPiIOdeK`=v}zKoP5u3Loa ztC=@fY~K9A4bUM++Q9}=))43qA~2jbwYrCd$sFr5X?T>_7&>ZCsAYZycYZOiQFI;n z&<$(ds96zij$q!ukpu)@NSyN+>UXkos1@P*pEFnYc5z_1Lb)WfWU?>kOxn31^^C@D zSj2xsrJ}RF*&@B&vJuB5c zEH*9=D)l11!TN!46y-R+QTNJ^oz`w~Ng`(yDfb$XWGvfUNW94=cO*F2@uNoLU0hTw=Ju2P{m4j>Pc7vRXEsTGMl*R~=W~r%1U_jQwwV!%P7TB+Hswzuj zeai`xwvtj%x0e-^bX^15-kIJo=u5d0dSrp!OsAiNlAA8e3Ku>4S-GQ^uTc`-Y;JsA z@1PCIAY{pEQV>r+bc5aSnds*-sL+m+HFH;*w0pH?WUBn*mmp+YXI<#EgZ(c&iF<$4 z^p4#*SJaQiF;B6*B#$SNHe5bT3Nd~vpZmKPwZLmMsKmhgFwR_9b;`vHeFame{oZRc zlZcZNaxZ_ZZ`t<8BxZRRbA_y*$2O+@CB*}Qa67S(5SZ$8op?Nfc}6o1q?J}C)+h&? z6MWg%VXkr=APBQ4N$NfOl{XFY7HfZ#O!}F7>4aCH*J;6PSjp`FPrJ}{oP9qhn)7K8 zy#y@AI!DE!7ow%yztT(oQ9!q0B9V@@F0;=jyt|njbXonK2TjQlmf|i#tu?|TZK(YpeuHWe?bx%D0O{-W z20QDWr8cAIPBf!3SZ2pPv4Iil!J%_QqlvX)IjqxW0gEQ8+WC)0B+o(?v=1#3d#+%H zTwdzvYHIUstDO0ijd_HOaV>w<_+MI5BzXne6vlI9?08RoGP+X&GbR&fkV~T1bx<3i z0+cxc4`v{St(rU184+%EB4MQCUT-0w!Pb)4s2FY!n}Ro}U(nu(6+0Br{gh(EfvR_f zg*Nlt0^Bd$4A0SIgLN5DK`UY*GS(QQ^b_)67c7or_ z)Z96b8Fy;iByPI}YiZjMQ(c{kz|`#|vpI$A7vVdOENj^}eiYOhp)ShPPo7oeBYp;8 zMah6`j=Gi~NR3Z8Q}M8@_nm`r9Hx1BeDl zbFST?L;&vg&g^`KLRIxGt+c~(rM_MMp0*RC$}=&OoFxSJFX z!`7k+jMS|~O+dR21JW2-aeJcz(kQz`NB>Vod~ECXTFOImJuH{*bdVnb+m_svy+fY>I3|Hg+j=h zS}RP16Z5&BExrLP;Lp^yu2iosd72Ng>&7NPLDX!2)vA83@Z`bXlyRhz&QUHvm3}_K zuQ0IzAQ5InwCsPxl8sq(dHQsqZ4Aw04J;m2d8Cf$zB+B8W?Xt@R#HYQNAZG~+rohU zG%W&@C@qi&8G&{oEZ>+sExnZHIzdCMOvW#6JfTI|B=lKRZBWI*tY;>$HPr^)3F>tc zIy!=v#F6k0D?X~LJY0nE5bw8rV;HmA`bYvMj_VSTdjo%4SC0%s?L>uF{8}}U{Kw;7 zZA0v!(8IqeTyTsG`~<`Pc7i$108uBM%(($#kBiQAqUULWqs7&^A0&?-w_r3HzQA-) z;1s~ZX#1_NTQ5dL^fkz-ZmlxkqB|Z$X<<;NA3A&Rqig8oZCFceeAu}~AeAcqTkQ*7 z8YhVYUs;d-qFUESjTcoR#9vMG&!FP|4ml{5!u9U)8572 zBC|_bsfjXzR=Yno(jIdw8oRUCh5gg-C!^q}HwUqixefs}v2?0Ffp zqWjE^s{p;5Kk}OUE#G{mT`<{qZC@IhrXTz1FC|;|CpKh=pbD`Z<-%L1_LH8B?2en4 zmEPbB7n^cMhD7A@Q?fxh(k@0reDM{s5H_~&vX82N#I*-24!Ojh;Z%QUww@otB{(~` zrC5Lain`g&gS&OC1Wsa{*7Fp!h#f_gkIbjwQ*YrMm1os}X-Q6M1q0;-ZT;M4#&|)o za2$WjfB7p8OJTz~k4|v&qd4ekt$t7w84lj{wUH9HI3J!Za$R_{N3=+3+?p0EkP0RC<^%xz1*;R`#8jMT?b% z*$D=JZfD+Lt}r?(hqa!KZV3Hu_X~YBX7YY0n2@&r3r>$ob;o50 zcHv07)({eN{T6K+yBY#R)J?0O3M~%5nreDs#8X#ih(1;U(fVdj#K)X?b$T8?T>bl28!2u!z>B$C)jC6npx=t4ziEwFGr~3??AWo##phjjjKq*-R;K2m&?b z#C-dH=u|m(gc0)<4jM3o8Y1RLJv~fi=sJ+wfGdVdxxH?#b=tF!oSUX>EsB^;3)+o( znDxzgm{QQf?>U?snqpBgW;t88a4)ofw-Wr?NZ@t9`79cYhA5&foWV6J)IT?pm-eJZ zrx+_>8l`KeTuDF>z4^~1+oEF%EVkH6^dq4JjZ^`DDu?&eow)*@nDCIgUi$MmsRN18 znjtCN7>NDiMZDJpTy<0Ql+lZeDiBsvL7+?rO@J;b>M)7p7Sz5Ic%L$@6rAXPFw^~5 zdv;vt%t0$qS3HWPK$OydKWby-i{z)eoCDpzRFvH9ZZ)yy79C+PJ{rpYJx}Qpvs{Nn z6OCaW30lGa-Bg$dIX5`@D_P}r%z6!$hYv}7tVOwJg##6lg{9yUrSDM;ETA}=jv47J zr%{cvUKUI^TzD=GBNYZ|hcP;T%oZag1FJKGvx@+;wWC*I2Rn((Ar6ff;|4*igwJL_ zm1ouK=--@ie206}!vLsu49F$NRr8HA$sM%J4tJ%1g*MDqIj7R?bWhiO_hUg(%>5W0 z?*{3rs6tih^fuKds2BOcg+^_QYDZ@Vy_`V5?-%fxI9iP4b@G80O|kZci;1^0Raar zmd-}sISaq-Ra-=TP;1fNe~U`JNhQ+{OlvTfyQJI<06S&uHs2C7wdne6c$-iTfA{Vx z3FecZSC5~lsnLwko+^!h!-}1#FKJ+ueROq9TyW2F54qjB$A~+9l7WQQy`%F9<~A&+fIF5&jOly)V{vN8q0zO49FU^g~GE#=m5ed2$PrZ`D)WFv&g)@gsv331nsP zY$sK4TZS2ih_n=?3z?5PBxHW789fVk?mTJ}vNxre_imQ)Z5*b5V82uT@#0Z!4F;ZC;ND}nAtSk1B1W9E9w?4CXKyr z*5Ix-_6x}sQ7qK!&(gYlYYA+R6qirMB+Gwwbim?!(*>l2XIzQ@hW{&JW=?(NdheDm z*EE84K+hdkOjj^<OtYgT|VIYyhNe`^SIqOh30#6DK&3!py zJ~4Y*MYxiR5$vzX^*T69JmFls1nev+ra^ugue0iZ3C{k0a_BnV*2A3hHUAj4AR`K{ zX*W|{B19*11d2FniG?|{D?ZLNPlN(O)6pkhbphz`h{DC_GqSpu($+Lh)4R6xSxNQUYsdmVz;hkW36EC@?Hv zv5tyP+fhA`?IDAcFyAs`(2x)5-vFW3k-KwKG>(*S9II=#AwDmY9~&WMcf$Yx007Ia h2w0f=!r~#2cqzSRaXZr#G00004Sz0txaxnk^ diff --git a/contrib/pyln-client/tests/data/gossip_store.simple.xz b/contrib/pyln-client/tests/data/gossip_store.simple.xz index a8be99deb6ed2737804d807e416b43c7ccce62f2..23981fad988eb9c9473dd84b7a49fe3c9e633a62 100644 GIT binary patch delta 394 zcmV;50d@YU1E>R#AAbb^Fqwm+yt+G>w&oF&7!lb&g6hafG8}z#xIU;_d0Dv>H2S5} zM@)(v!eg)%B_CWbjuLr~1OKoS#On|)&?3DzrMo7{F{AmYyBX-s>EgGT&ORzSF0tKZ zjlEq+w{ET4wgswz`asn%alVrhI#N1**BgxkM*W4aqQ~C(=70RmZSz!iIIFeo0k(&c ze7C7`RH|!GmX}z7Hwn3JV1tF>Uv~7Y^qo|$BtbEcC8x653Ea|MmGIl%4fjKM2>P*I zV-m!!z)&G}4PCPzutoeemg&>#wjGTWg+i|iht}8gO|!oWL!eXq9T=%hw_*- zO>$n7iH>)X^?&?88ZQQ2*04z5a8$c|-pL0nPy&saVt!7Sbi)!Y(1Z_gjFk1B73y3B z6tpET3 delta 394 zcmV;50d@YU1E>R#AAbaZFqwm+yt+G>w&oF&7$22>-U7$nUM<^)^08$7<2nPbX7|7sXHXJ00DsN}k+BC1#@W`lPC*rt z-#Qd}^9oIQGTEuoi{!j$KK~|BH6~A*Af|1;5CcSv+{ZSYzq8O$j%eP|zfP*qQ}knb zPN&}!m}hv6f_9qYP)+2Di1jo=Y1GUVb(N%1%F!zvRFa;DciV7C?wR8xSr-i?+I=b- zS-Z%(PYJZyYk$X4%67F(dI+st<#xa%;|!k~-wCT1>#I=O?fj{~P-~sF95xM_Dc{nK zn@lduqH*(IgFJgMS(EFsK*rhzyQB|FxOUkI1xQ Date: Wed, 4 May 2022 08:48:59 -0500 Subject: [PATCH 0902/1530] gossipd: make use of new ratelimit bit in gossip_store length mask routing.c now flags rate-limited gossip as it enters the gossip_store but makes use of it in updating the routing graph. Flagged gossip is not rebroadcast to gossip peers. Changelog-Changed: gossipd: now accepts spam gossip, but squelches it for peers. --- contrib/pyln-client/pyln/client/gossmap.py | 4 ++- devtools/dump-gossipstore.c | 8 +++--- gossipd/gossip_store.c | 18 +++++++------ gossipd/gossip_store.h | 3 ++- gossipd/routing.c | 27 +++++++++++++------ gossipd/test/run-check_channel_announcement.c | 2 +- gossipd/test/run-txout_failure.c | 2 +- 7 files changed, 41 insertions(+), 23 deletions(-) diff --git a/contrib/pyln-client/pyln/client/gossmap.py b/contrib/pyln-client/pyln/client/gossmap.py index ed0c76500e17..f58d49add0e7 100755 --- a/contrib/pyln-client/pyln/client/gossmap.py +++ b/contrib/pyln-client/pyln/client/gossmap.py @@ -12,8 +12,10 @@ GOSSIP_STORE_VERSIONS = [0x09, 0x0a] GOSSIP_STORE_LEN_DELETED_BIT = 0x80000000 GOSSIP_STORE_LEN_PUSH_BIT = 0x40000000 +GOSSIP_STORE_LEN_RATELIMIT_BIT = 0x20000000 GOSSIP_STORE_LEN_MASK = (~(GOSSIP_STORE_LEN_PUSH_BIT - | GOSSIP_STORE_LEN_DELETED_BIT)) + | GOSSIP_STORE_LEN_DELETED_BIT + | GOSSIP_STORE_LEN_RATELIMIT_BIT)) # These duplicate constants in lightning/gossipd/gossip_store_wiregen.h WIRE_GOSSIP_STORE_PRIVATE_CHANNEL = 4104 diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index a99e918beb6e..9cd7befa3e85 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -52,10 +52,11 @@ int main(int argc, char *argv[]) struct short_channel_id scid; u32 msglen = be32_to_cpu(hdr.len); u8 *msg, *inner; - bool deleted, push; + bool deleted, push, ratelimit; deleted = (msglen & GOSSIP_STORE_LEN_DELETED_BIT); push = (msglen & GOSSIP_STORE_LEN_PUSH_BIT); + ratelimit = (msglen & GOSSIP_STORE_LEN_RATELIMIT_BIT); msglen &= GOSSIP_STORE_LEN_MASK; msg = tal_arr(NULL, u8, msglen); @@ -66,9 +67,10 @@ int main(int argc, char *argv[]) != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) warnx("Checksum verification failed"); - printf("%zu: %s%s", off, + printf("%zu: %s%s%s", off, deleted ? "DELETED " : "", - push ? "PUSH " : ""); + push ? "PUSH " : "", + ratelimit ? "RATE-LIMITED " : ""); if (deleted && !print_deleted) { printf("\n"); goto end; diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index e431ed6654f8..7054b39bbaa4 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -71,7 +71,7 @@ static ssize_t gossip_pwritev(int fd, const struct iovec *iov, int iovcnt, #endif /* !HAVE_PWRITEV */ static bool append_msg(int fd, const u8 *msg, u32 timestamp, - bool push, u64 *len) + bool push, bool spam, u64 *len) { struct gossip_hdr hdr; u32 msglen; @@ -84,6 +84,8 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, hdr.len = cpu_to_be32(msglen); if (push) hdr.len |= CPU_TO_BE32(GOSSIP_STORE_LEN_PUSH_BIT); + if (spam) + hdr.len |= CPU_TO_BE32(GOSSIP_STORE_LEN_RATELIMIT_BIT); hdr.crc = cpu_to_be32(crc32c(timestamp, msg, msglen)); hdr.timestamp = cpu_to_be32(timestamp); @@ -277,7 +279,7 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) oldlen = lseek(old_fd, SEEK_END, 0); newlen = lseek(new_fd, SEEK_END, 0); append_msg(old_fd, towire_gossip_store_ended(tmpctx, newlen), - 0, true, &oldlen); + 0, true, false, &oldlen); close(old_fd); status_debug("gossip_store_compact_offline: %zu deleted, %zu copied", deleted, count); @@ -565,7 +567,7 @@ bool gossip_store_compact(struct gossip_store *gs) /* Write end marker now new one is ready */ append_msg(gs->fd, towire_gossip_store_ended(tmpctx, len), - 0, true, &gs->len); + 0, true, false, &gs->len); gs->count = count; gs->deleted = 0; @@ -586,19 +588,19 @@ bool gossip_store_compact(struct gossip_store *gs) u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, u32 timestamp, bool push, - const u8 *addendum) + bool spam, const u8 *addendum) { u64 off = gs->len; /* Should never get here during loading! */ assert(gs->writable); - if (!append_msg(gs->fd, gossip_msg, timestamp, push, &gs->len)) { + if (!append_msg(gs->fd, gossip_msg, timestamp, push, spam, &gs->len)) { status_broken("Failed writing to gossip store: %s", strerror(errno)); return 0; } - if (addendum && !append_msg(gs->fd, addendum, 0, false, &gs->len)) { + if (addendum && !append_msg(gs->fd, addendum, 0, false, false, &gs->len)) { status_broken("Failed writing addendum to gossip store: %s", strerror(errno)); return 0; @@ -615,7 +617,7 @@ u64 gossip_store_add_private_update(struct gossip_store *gs, const u8 *update) /* A local update for an unannounced channel: not broadcastable, but * otherwise the same as a normal channel_update */ const u8 *pupdate = towire_gossip_store_private_update(tmpctx, update); - return gossip_store_add(gs, pupdate, 0, false, NULL); + return gossip_store_add(gs, pupdate, 0, false, false, NULL); } /* Returns index of following entry. */ @@ -675,7 +677,7 @@ void gossip_store_mark_channel_deleted(struct gossip_store *gs, const struct short_channel_id *scid) { gossip_store_add(gs, towire_gossip_store_delete_chan(tmpctx, scid), - 0, false, NULL); + 0, false, false, NULL); } const u8 *gossip_store_get(const tal_t *ctx, diff --git a/gossipd/gossip_store.h b/gossipd/gossip_store.h index f717491896fe..44a0b4d303bc 100644 --- a/gossipd/gossip_store.h +++ b/gossipd/gossip_store.h @@ -40,11 +40,12 @@ u64 gossip_store_add_private_update(struct gossip_store *gs, const u8 *update); * @gossip_msg: the gossip message to insert. * @timestamp: the timestamp for filtering of this messsage. * @push: true if this should be sent to peers despite any timestamp filters. + * @spam: true if this message is rate-limited and squelched to peers. * @addendum: another message to append immediately after this * (for appending amounts to channel_announcements for internal use). */ u64 gossip_store_add(struct gossip_store *gs, const u8 *gossip_msg, - u32 timestamp, bool push, const u8 *addendum); + u32 timestamp, bool push, bool spam, const u8 *addendum); /** diff --git a/gossipd/routing.c b/gossipd/routing.c index 57a1c7e8059d..ca9da0ddf276 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -437,6 +437,7 @@ static void force_node_announce_rexmit(struct routing_state *rstate, announce, node->bcast.timestamp, is_local, + false, NULL); } @@ -790,6 +791,7 @@ static void add_channel_announce_to_broadcast(struct routing_state *rstate, channel_announce, chan->bcast.timestamp, is_local, + false, addendum); rstate->local_channel_announced |= is_local; } @@ -1255,6 +1257,7 @@ bool routing_add_channel_update(struct routing_state *rstate, struct unupdated_channel *uc; u8 direction; struct amount_sat sat; + bool spam; /* Make sure we own msg, even if we don't save it. */ if (taken(update)) @@ -1360,16 +1363,19 @@ bool routing_add_channel_update(struct routing_state *rstate, if (!ratelimit(rstate, &hc->tokens, hc->bcast.timestamp, timestamp)) { status_peer_debug(peer ? &peer->id : NULL, - "Ignoring spammy update for %s/%u" + "Spammy update for %s/%u flagged" " (last %u, now %u)", type_to_string(tmpctx, struct short_channel_id, &short_channel_id), direction, hc->bcast.timestamp, timestamp); - /* Ignoring != failing */ - return true; + spam = true; + } else { + spam = false; } + } else { + spam = false; } chan->half[direction].bcast.timestamp = timestamp; @@ -1414,7 +1420,7 @@ bool routing_add_channel_update(struct routing_state *rstate, = gossip_store_add(rstate->gs, update, hc->bcast.timestamp, local_direction(rstate, chan, NULL), - NULL); + spam, NULL); if (hc->bcast.timestamp > rstate->last_timestamp && hc->bcast.timestamp < time_now().ts.tv_sec) rstate->last_timestamp = hc->bcast.timestamp; @@ -1581,6 +1587,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, u8 alias[32]; u8 *features, *addresses; struct tlv_node_ann_tlvs *na_tlv; + bool spam; if (was_unknown) *was_unknown = false; @@ -1670,15 +1677,18 @@ bool routing_add_node_announcement(struct routing_state *rstate, if (!ratelimit(rstate, &node->tokens, node->bcast.timestamp, timestamp)) { status_peer_debug(peer ? &peer->id : NULL, - "Ignoring spammy nannounce for %s" + "Spammy nannounce for %s flagged" " (last %u, now %u)", type_to_string(tmpctx, struct node_id, &node_id), node->bcast.timestamp, timestamp); - /* Ignoring != failing */ - return true; + spam = true; + } else { + spam = false; } + } else { + spam = false; } /* Harmless if it was never added */ @@ -1699,6 +1709,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, node->bcast.timestamp, node_id_eq(&node_id, &rstate->local_id), + spam, NULL); peer_supplied_good_gossip(peer, 1); } @@ -1919,7 +1930,7 @@ bool routing_add_private_channel(struct routing_state *rstate, u8 *msg = towire_gossip_store_private_channel(tmpctx, capacity, chan_ann); - index = gossip_store_add(rstate->gs, msg, 0, false, NULL); + index = gossip_store_add(rstate->gs, msg, 0, false, false, NULL); } chan->bcast.index = index; return true; diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index a25e48e60f8d..fb810dd46e5c 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -66,7 +66,7 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, - u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) + u32 timestamp UNNEEDED, bool push UNNEEDED, bool spam UNNEEDED, const u8 *addendum UNNEEDED) { fprintf(stderr, "gossip_store_add called!\n"); abort(); } /* Generated stub for gossip_store_add_private_update */ u64 gossip_store_add_private_update(struct gossip_store *gs UNNEEDED, const u8 *update UNNEEDED) diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 10a9b4f34499..040e12f915b5 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -37,7 +37,7 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, - u32 timestamp UNNEEDED, bool push UNNEEDED, const u8 *addendum UNNEEDED) + u32 timestamp UNNEEDED, bool push UNNEEDED, bool spam UNNEEDED, const u8 *addendum UNNEEDED) { fprintf(stderr, "gossip_store_add called!\n"); abort(); } /* Generated stub for gossip_store_add_private_update */ u64 gossip_store_add_private_update(struct gossip_store *gs UNNEEDED, const u8 *update UNNEEDED) From 08a2b3b86cf8b609aaeb8b44b29e92faa66204c1 Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Wed, 4 May 2022 16:35:17 -0500 Subject: [PATCH 0903/1530] pytest: test_gossip_ratelimit checks routing graph and squelch --- tests/test_gossip.py | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 8a5f2994b489..45b4da92f98e 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1795,7 +1795,7 @@ def test_gossip_ratelimit(node_factory, bitcoind): """Check that we ratelimit incoming gossip. We create a partitioned network, in which the first partition consisting - of l1 and l2 is used to create an on-chain footprint and twe then feed + of l1 and l2 is used to create an on-chain footprint and we then feed canned gossip to the other partition consisting of l3. l3 should ratelimit the incoming gossip. @@ -1863,13 +1863,32 @@ def channel_fees(node): '0102c479b7684b9db496b844f6925f4ffd8a27c5840a020d1b537623c1545dcd8e195776381bbf51213e541a853a4a49a0faf84316e7ccca5e7074901a96bbabe04e06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400201010006000000000000000000000014000003eb000000003b023380', # timestamp=1568096259, fee_proportional_millionths=1004 '01024b866012d995d3d7aec7b7218a283de2d03492dbfa21e71dd546ec2e36c3d4200453420aa02f476f99c73fe1e223ea192f5fa544b70a8319f2a216f1513d503d06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400301010006000000000000000000000014000003ec000000003b023380', - # update 5 marks you as a nasty spammer! + # update 5 marks you as a nasty spammer, but we listen to you anyway now! fee_proportional_millionths=1005 '01025b5b5a0daed874ab02bd3356d38190ff46bbaf5f10db5067da70f3ca203480ca78059e6621c6143f3da4e454d0adda6d01a9980ed48e71ccd0c613af73570a7106226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400401010006000000000000000000000014000003ed000000003b023380' ], timeout=TIMEOUT ) + # Rate limited channel_update okay to use in routing graph. + wait_for(lambda: channel_fees(l3) == [1005]) + # but should be flagged so we don't propagate to the network. + assert(l3.daemon.is_in_log("Spammy update for 103x1x1/1 flagged")) - wait_for(lambda: channel_fees(l3) == [1004]) + # ask for a gossip sync + raw = subprocess.run(['devtools/gossipwith', + '--initial-sync', + '--timeout-after={}'.format(1), + '--hex', + '{}@localhost:{}'.format(l3.info['id'], l3.port)], + check=True, + timeout=TIMEOUT, stdout=subprocess.PIPE).stdout + # The last message is the most recent channel update. + message = raw.decode('utf-8').split()[-1] + decoded = subprocess.run(['devtools/decodemsg', message], + check=True, + timeout=TIMEOUT, + stdout=subprocess.PIPE).stdout.decode('utf8') + # Used in routing graph, but not passed to gossip peers. + assert("fee_proportional_millionths=1005" not in decoded) # 24 seconds later, it will accept another. l3.rpc.call('dev-gossip-set-time', [1568096251 + 24]) @@ -1882,6 +1901,20 @@ def channel_fees(node): check=True, timeout=TIMEOUT) wait_for(lambda: channel_fees(l3) == [1006]) + raw = subprocess.run(['devtools/gossipwith', + '--initial-sync', + '--timeout-after={}'.format(1), + '--hex', + '{}@localhost:{}'.format(l3.info['id'], l3.port)], + check=True, + timeout=TIMEOUT, stdout=subprocess.PIPE).stdout + message = raw.decode('utf-8').split()[-1] + decoded = subprocess.run(['devtools/decodemsg', message], + check=True, + timeout=TIMEOUT, + stdout=subprocess.PIPE).stdout.decode('utf8') + + assert("fee_proportional_millionths=1006" in decoded) def check_socket(ip_addr, port): From 87a66c180250e3c7ba99ac0461f7d16d5b411dbe Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Fri, 6 May 2022 18:24:18 -0500 Subject: [PATCH 0904/1530] gossipd: store and index most recent and last non-rate-limited gossip This grows the routing state in order to index both okay-to-broadcast and rate-limited gossip. The gossip_store also logs the rate-limited gossip if useful. This allows the broadcast of the last non-rate-limited gossip. --- gossipd/routing.c | 114 ++++++++++++++++++++++++++++++------------- gossipd/routing.h | 10 +++- tests/test_gossip.py | 4 +- 3 files changed, 91 insertions(+), 37 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index ca9da0ddf276..2b9dde5961c0 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -368,6 +368,7 @@ static struct node *new_node(struct routing_state *rstate, n->id = *id; memset(n->chans.arr, 0, sizeof(n->chans.arr)); broadcastable_init(&n->bcast); + broadcastable_init(&n->rgraph); n->tokens = TOKEN_MAX; node_map_add(rstate->nodes, n); tal_add_destructor2(n, destroy_node, rstate); @@ -521,6 +522,7 @@ static void init_half_chan(struct routing_state *rstate, struct half_chan *c = &chan->half[channel_idx]; broadcastable_init(&c->bcast); + broadcastable_init(&c->rgraph); c->tokens = TOKEN_MAX; } @@ -1340,7 +1342,7 @@ bool routing_add_channel_update(struct routing_state *rstate, return false; } - if (timestamp <= hc->bcast.timestamp) { + if (timestamp <= hc->rgraph.timestamp) { SUPERVERBOSE("Ignoring outdated update."); /* Ignoring != failing */ return true; @@ -1377,15 +1379,34 @@ bool routing_add_channel_update(struct routing_state *rstate, } else { spam = false; } - - chan->half[direction].bcast.timestamp = timestamp; - - /* Safe even if was never added, but if it's a private channel it - * would be a WIRE_GOSSIP_STORE_PRIVATE_UPDATE. */ - gossip_store_delete(rstate->gs, &hc->bcast, - is_chan_public(chan) - ? WIRE_CHANNEL_UPDATE - : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + /* Routing graph always uses the latest message. */ + hc->rgraph.timestamp = timestamp; + if (spam) { + /* Remove the prior spam update if it exists. */ + if (hc->rgraph.index != hc->bcast.index) { + gossip_store_delete(rstate->gs, &hc->rgraph, + is_chan_public(chan) + ? WIRE_CHANNEL_UPDATE + : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + } + } else { + /* Safe to broadcast */ + hc->bcast.timestamp = timestamp; + /* Remove prior spam update if one exists. */ + if (hc->rgraph.index != hc->bcast.index) { + /* Safe even if was never added, but if it's a + * private channel it would be a + * WIRE_GOSSIP_STORE_PRIVATE_UPDATE. */ + gossip_store_delete(rstate->gs, &hc->rgraph, + is_chan_public(chan) + ? WIRE_CHANNEL_UPDATE + : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + } + gossip_store_delete(rstate->gs, &hc->bcast, + is_chan_public(chan) + ? WIRE_CHANNEL_UPDATE + : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); + } /* BOLT #7: * - MUST consider the `timestamp` of the `channel_announcement` to be @@ -1407,23 +1428,31 @@ bool routing_add_channel_update(struct routing_state *rstate, hc->bcast.index = gossip_store_add_private_update(rstate->gs, update); - } else + /* No need to separately track spam for private + * channels. */ + hc->rgraph.index = hc->bcast.index; + } else { hc->bcast.index = index; + hc->rgraph.index = index; + } return true; } /* If we're loading from store, this means we don't re-add to store. */ - if (index) - hc->bcast.index = index; - else { - hc->bcast.index - = gossip_store_add(rstate->gs, update, - hc->bcast.timestamp, + if (index) { + if (!spam) + hc->bcast.index = index; + hc->rgraph.index = index; + } else { + hc->rgraph.index + = gossip_store_add(rstate->gs, update, timestamp, local_direction(rstate, chan, NULL), spam, NULL); if (hc->bcast.timestamp > rstate->last_timestamp && hc->bcast.timestamp < time_now().ts.tv_sec) rstate->last_timestamp = hc->bcast.timestamp; + if (!spam) + hc->bcast.index = hc->rgraph.index; peer_supplied_good_gossip(peer, 1); } @@ -1653,7 +1682,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, return false; } - if (node->bcast.timestamp >= timestamp) { + if (node->rgraph.timestamp >= timestamp) { SUPERVERBOSE("Ignoring node announcement, it's outdated."); /* OK unless we're loading from store */ return index == 0; @@ -1691,26 +1720,41 @@ bool routing_add_node_announcement(struct routing_state *rstate, spam = false; } - /* Harmless if it was never added */ - gossip_store_delete(rstate->gs, - &node->bcast, - WIRE_NODE_ANNOUNCEMENT); - - node->bcast.timestamp = timestamp; - if (node->bcast.timestamp > rstate->last_timestamp - && node->bcast.timestamp < time_now().ts.tv_sec) - rstate->last_timestamp = node->bcast.timestamp; + /* Routing graph always references the latest message. */ + node->rgraph.timestamp = timestamp; + if (!spam) { + node->bcast.timestamp = timestamp; + /* remove prior spam update if one exists */ + if (node->rgraph.index != node->bcast.index) { + /* Harmless if it was never added */ + gossip_store_delete(rstate->gs, &node->rgraph, + WIRE_NODE_ANNOUNCEMENT); + } + gossip_store_delete(rstate->gs, &node->bcast, + WIRE_NODE_ANNOUNCEMENT); + /* Remove prior spam update. */ + } else if (node->rgraph.index != node->bcast.index) { + gossip_store_delete(rstate->gs, &node->rgraph, + WIRE_NODE_ANNOUNCEMENT); + } - if (index) - node->bcast.index = index; - else { - node->bcast.index - = gossip_store_add(rstate->gs, msg, - node->bcast.timestamp, + /* Don't add to the store if it was loaded from the store. */ + if (index) { + node->rgraph.index = index; + if (!spam) + node->bcast.index = index; + } else { + node->rgraph.index + = gossip_store_add(rstate->gs, msg, timestamp, node_id_eq(&node_id, &rstate->local_id), - spam, - NULL); + spam, NULL); + if (node->bcast.timestamp > rstate->last_timestamp + && node->bcast.timestamp < time_now().ts.tv_sec) + rstate->last_timestamp = node->bcast.timestamp; + if (!spam) + node->bcast.index = node->rgraph.index; + peer_supplied_good_gossip(peer, 1); } diff --git a/gossipd/routing.h b/gossipd/routing.h index 1641284bb667..b58161d85019 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -20,9 +20,13 @@ struct peer; struct routing_state; struct half_chan { - /* Timestamp and index into store file */ + /* Timestamp and index into store file - safe to broadcast */ struct broadcastable bcast; + /* Most recent gossip for the routing graph - may be rate-limited and + * non-broadcastable. If there is no spam, rgraph == bcast. */ + struct broadcastable rgraph; + /* Token bucket */ u8 tokens; }; @@ -105,6 +109,10 @@ struct node { /* Timestamp and index into store file */ struct broadcastable bcast; + /* Possibly spam flagged. Nonbroadcastable, but used for routing graph. + * If there is no current spam, rgraph == bcast. */ + struct broadcastable rgraph; + /* Token bucket */ u8 tokens; diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 45b4da92f98e..02ebd320e778 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1863,7 +1863,8 @@ def channel_fees(node): '0102c479b7684b9db496b844f6925f4ffd8a27c5840a020d1b537623c1545dcd8e195776381bbf51213e541a853a4a49a0faf84316e7ccca5e7074901a96bbabe04e06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400201010006000000000000000000000014000003eb000000003b023380', # timestamp=1568096259, fee_proportional_millionths=1004 '01024b866012d995d3d7aec7b7218a283de2d03492dbfa21e71dd546ec2e36c3d4200453420aa02f476f99c73fe1e223ea192f5fa544b70a8319f2a216f1513d503d06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400301010006000000000000000000000014000003ec000000003b023380', - # update 5 marks you as a nasty spammer, but we listen to you anyway now! fee_proportional_millionths=1005 + # update 5 marks you as a nasty spammer, but the routing graph is + # updated with this even though the gossip is not broadcast. '01025b5b5a0daed874ab02bd3356d38190ff46bbaf5f10db5067da70f3ca203480ca78059e6621c6143f3da4e454d0adda6d01a9980ed48e71ccd0c613af73570a7106226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f00006700000100015d77400401010006000000000000000000000014000003ed000000003b023380' ], timeout=TIMEOUT @@ -1887,6 +1888,7 @@ def channel_fees(node): check=True, timeout=TIMEOUT, stdout=subprocess.PIPE).stdout.decode('utf8') + assert("fee_proportional_millionths=1004" in decoded) # Used in routing graph, but not passed to gossip peers. assert("fee_proportional_millionths=1005" not in decoded) From 2927c8cfdbcc8c9f232a2abc2fe28f3da4121d45 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 7 Jul 2022 12:48:52 +0200 Subject: [PATCH 0905/1530] doc: Fix generated `listfonfigs` man-page This seems to have been missed by a recent PR. Changelog-None --- doc/lightning-listconfigs.7 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index 79e81678371f..2784ef17047c 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -30,6 +30,7 @@ showing default values (\fBdev-\fR options are not shown)\. On success, an object is returned, containing: + .RS .IP \[bu] \fB# version\fR (string, optional): Special field indicating the current version @@ -170,6 +171,7 @@ On success, an object is returned, containing: On failure, one of the following error codes may be returned: + .RS .IP \[bu] -32602: Error in given parameters or field with \fIconfig\fR name doesn't exist\. From c0253ebc6894c8afaf6d1c3c8ea94baa15b70542 Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Wed, 29 Jun 2022 19:31:32 +0700 Subject: [PATCH 0906/1530] Fix broken link --- doc/lightning-createonion.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index f281ff719592..2f3cb5e67626 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -132,5 +132,5 @@ RESOURCES Main web site: -[bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md +[bolt04]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md [comment]: # ( SHA256STAMP:eabebca3c38ac8e01fb9a0891bde7913ce2832b7ee179c0b4617d104a0e6c009) From 0a4cd9102852a3eb5f105f7787e5b8168fb56335 Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Thu, 30 Jun 2022 12:48:56 +0700 Subject: [PATCH 0907/1530] Inline URL --- doc/lightning-createonion.7.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 2f3cb5e67626..484a7cad0631 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -70,7 +70,7 @@ which the above *hops* parameter was generated: - The final payload is a copy of the last payload sans `channel` These rules are directly derived from the onion construction. Please refer -[BOLT 04][bolt04] for details and rationale. +[BOLT 04](https://github.com/lightning/bolts/blob/master/04-onion-routing.md) for details and rationale. The *assocdata* parameter specifies the associated data that the onion should commit to. If the onion is to be used to send a payment later it MUST match @@ -132,5 +132,4 @@ RESOURCES Main web site: -[bolt04]: https://github.com/lightning/bolts/blob/master/04-onion-routing.md [comment]: # ( SHA256STAMP:eabebca3c38ac8e01fb9a0891bde7913ce2832b7ee179c0b4617d104a0e6c009) From 9296537edb98649031f947b03ba7104ff180da22 Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Tue, 5 Jul 2022 15:44:38 -0500 Subject: [PATCH 0908/1530] peer_control: Fix check_funding_details assert Check funding_outnum validity first to avoid reading invalid outputs Changelog-Fixed: Fixed a potential issue if the number of outputs decreases in a dualopen RBF or splice. --- lightningd/peer_control.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 284dc78bb13d..0b817c5e596c 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1437,13 +1437,14 @@ static bool check_funding_details(const struct bitcoin_tx *tx, struct amount_sat funding, u32 funding_outnum) { - struct amount_asset asset = - bitcoin_tx_output_get_amount(tx, funding_outnum); + struct amount_asset asset; - if (!amount_asset_is_main(&asset)) + if (funding_outnum >= tx->wtx->num_outputs) return false; - if (funding_outnum >= tx->wtx->num_outputs) + asset = bitcoin_tx_output_get_amount(tx, funding_outnum); + + if (!amount_asset_is_main(&asset)) return false; if (!amount_sat_eq(amount_asset_to_sat(&asset), funding)) From d0937a2e97e4f412cbd67c149485665f3624d4df Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 1 Jul 2022 14:40:42 -0500 Subject: [PATCH 0909/1530] df: check mempool/block for funding output on broadcast fail If we can't broadcast the tx, confirm that it didn't end up in the mempool or the utxo set before throwing an error. Note that this doesn't protect us in the case where the funding output has already been *spent*... but that's extremely rare, right? Fixes #5296 Reported-By: @rustyrussell Collab-With: @vincenzopalazzo --- lightningd/dual_open_control.c | 106 ++++++++++++++++++++++----------- tests/test_connection.py | 11 ++-- 2 files changed, 78 insertions(+), 39 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 595fad42a544..d72a688f05de 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1420,50 +1420,19 @@ static void handle_local_private_channel(struct subd *dualopend, struct channel_send { const struct wally_tx *wtx; struct channel *channel; + const char *err_msg; }; -static void sendfunding_done(struct bitcoind *bitcoind UNUSED, - bool success, const char *msg, - struct channel_send *cs) +static void handle_tx_broadcast(struct channel_send *cs) { struct lightningd *ld = cs->channel->peer->ld; - struct channel *channel = cs->channel; const struct wally_tx *wtx = cs->wtx; + struct channel *channel = cs->channel; + struct command *cmd = channel->openchannel_signed_cmd; struct json_stream *response; struct bitcoin_txid txid; struct amount_sat unused; int num_utxos; - struct command *cmd = channel->openchannel_signed_cmd; - channel->openchannel_signed_cmd = NULL; - - if (!cmd && channel->opener == LOCAL) - log_unusual(channel->log, - "No outstanding command for channel %s," - " funding sent was success? %d", - type_to_string(tmpctx, struct channel_id, - &channel->cid), - success); - - if (!success) { - if (cmd) - was_pending(command_fail(cmd, - FUNDING_BROADCAST_FAIL, - "Error broadcasting funding " - "tx: %s. Unsent tx discarded " - "%s.", - msg, - type_to_string(tmpctx, - struct wally_tx, - wtx))); - log_unusual(channel->log, - "Error broadcasting funding " - "tx: %s. Unsent tx discarded " - "%s.", - msg, - type_to_string(tmpctx, struct wally_tx, wtx)); - tal_free(cs); - return; - } /* This might have spent UTXOs from our wallet */ num_utxos = wallet_extract_owned_outputs(ld->wallet, @@ -1479,11 +1448,78 @@ static void sendfunding_done(struct bitcoind *bitcoind UNUSED, json_add_txid(response, "txid", &txid); json_add_channel_id(response, "channel_id", &channel->cid); was_pending(command_success(cmd, response)); + + cs->channel->openchannel_signed_cmd = NULL; } +} + +static void check_utxo_block(struct bitcoind *bitcoind UNUSED, + const struct bitcoin_tx_output *txout, + void *arg) +{ + struct channel_send *cs = arg; + struct command *cmd = cs->channel->openchannel_signed_cmd; + const struct wally_tx *wtx = cs->wtx; + + /* note: if this tx has been included in a block *and spent* + * then this will also fail... */ + if (!txout) { + if (cmd) { + was_pending(command_fail(cmd, + FUNDING_BROADCAST_FAIL, + "Error broadcasting funding " + "tx: %s. Unsent tx discarded " + "%s.", + cs->err_msg, + type_to_string(tmpctx, + struct wally_tx, + wtx))); + cs->channel->openchannel_signed_cmd = NULL; + } + + log_unusual(cs->channel->log, + "Error broadcasting funding " + "tx: %s. Unsent tx discarded " + "%s.", + cs->err_msg, + type_to_string(tmpctx, struct wally_tx, wtx)); + } else + handle_tx_broadcast(cs); tal_free(cs); } +static void sendfunding_done(struct bitcoind *bitcoind UNUSED, + bool success, const char *msg, + struct channel_send *cs) +{ + struct lightningd *ld = cs->channel->peer->ld; + struct channel *channel = cs->channel; + struct command *cmd = channel->openchannel_signed_cmd; + + if (!cmd && channel->opener == LOCAL) + log_unusual(channel->log, + "No outstanding command for channel %s," + " funding sent was success? %d", + type_to_string(tmpctx, struct channel_id, + &channel->cid), + success); + + if (success) { + handle_tx_broadcast(cs); + tal_free(cs); + } else { + /* If the tx was mined into a block, it's possible + * that the broadcast would fail. Verify that's not + * the case here. */ + cs->err_msg = tal_strdup(cs, msg); + bitcoind_getutxout(ld->topology->bitcoind, + &channel->funding, + check_utxo_block, + cs); + } +} + static void send_funding_tx(struct channel *channel, const struct wally_tx *wtx TAKES) diff --git a/tests/test_connection.py b/tests/test_connection.py index feed3ed939e7..18a4ed1f61d8 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2681,7 +2681,7 @@ def test_fundee_forget_funding_tx_unconfirmed(node_factory, bitcoind): # is much slower in VALGRIND mode and wait_for_log # could time out before lightningd processes all the # blocks. - blocks = 200 + blocks = 50 # opener l1 = node_factory.get_node() # peer @@ -2690,14 +2690,16 @@ def test_fundee_forget_funding_tx_unconfirmed(node_factory, bitcoind): # Give opener some funds. l1.fundwallet(10**7) - # Let blocks settle. - time.sleep(1) def mock_sendrawtransaction(r): return {'id': r['id'], 'error': {'code': 100, 'message': 'sendrawtransaction disabled'}} + def mock_donothing(r): + return {'id': r['id'], 'result': {'success': True}} + # Prevent opener from broadcasting funding tx (any tx really). l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_sendrawtransaction) + l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_donothing) # Fund the channel. # The process will complete, but opener will be unable @@ -2709,7 +2711,8 @@ def mock_sendrawtransaction(r): bitcoind.generate_block(blocks) # fundee will forget channel! - l2.daemon.wait_for_log('Forgetting channel: It has been {} blocks'.format(blocks)) + # (Note that we let the last number be anything (hence the {}\d) + l2.daemon.wait_for_log(r'Forgetting channel: It has been {}\d blocks'.format(str(blocks)[:-1])) # fundee will also forget and disconnect from peer. assert len(l2.rpc.listpeers(l1.info['id'])['peers']) == 0 From 225ff870dfe39f2eb829568f995a0a0cad333441 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sun, 3 Jul 2022 19:52:31 +0100 Subject: [PATCH 0910/1530] lnprototest: updating and patch lnprototest Changelog-None: patch lnprototest Signed-off-by: Vincenzo Palazzo --- external/lnprototest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/lnprototest b/external/lnprototest index 433d22f16c3f..8efd45555ef3 160000 --- a/external/lnprototest +++ b/external/lnprototest @@ -1 +1 @@ -Subproject commit 433d22f16c3f550317315ce56a445e6d074a0f5a +Subproject commit 8efd45555ef3d1223f3b9f6a8c009419c9510747 From 0374fc16acfe9efe1f60f3f078df2a6b40c48cf2 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 7 Jul 2022 15:15:58 -0500 Subject: [PATCH 0911/1530] df tests: node still correctly picks up new tx if broadcast fails Now that we're more broadcast failure aware, let's check that on actual broadcast failure we still update UTXO set properly and the channel opens. --- tests/test_connection.py | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 18a4ed1f61d8..a4379627b6ae 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2718,6 +2718,47 @@ def mock_donothing(r): assert len(l2.rpc.listpeers(l1.info['id'])['peers']) == 0 +@pytest.mark.developer("needs --dev-max-funding-unconfirmed-blocks") +@pytest.mark.openchannel('v2') +def test_fundee_node_unconfirmed(node_factory, bitcoind): + """Test that fundee will successfully broadcast and + funder still has correct UTXOs/correctly advances the channel + """ + # opener + l1, l2 = node_factory.line_graph(2, fundchannel=False) + + # Give opener some funds. + l1.fundwallet(10**7) + + start_amount = only_one(l1.rpc.listfunds()['outputs'])['amount_msat'] + + def mock_sendrawtransaction(r): + return {'id': r['id'], 'error': {'code': 100, 'message': 'sendrawtransaction disabled'}} + + def mock_donothing(r): + time.sleep(10) + return bitcoind.rpc.sendrawtransaction(r['params'][0]) + + # Prevent both from broadcasting funding tx (any tx really). + l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_sendrawtransaction) + l2.daemon.rpcproxy.mock_rpc('sendrawtransaction', mock_donothing) + + # Fund the channel. + # The process will complete, but opener will be unable + # to broadcast and confirm funding tx. + with pytest.raises(RpcError, match=r'sendrawtransaction disabled'): + l1.rpc.fundchannel(l2.info['id'], 10**6) + + # Generate blocks until unconfirmed. + bitcoind.generate_block(1, wait_for_mempool=1) + + # Check that l1 opened the channel + wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'CHANNELD_NORMAL') + end_amount = only_one(l1.rpc.listfunds()['outputs'])['amount_msat'] + # We should be out the onchaind fees + assert start_amount > end_amount + Millisatoshi(10 ** 7 * 100) + + @pytest.mark.developer("needs dev_fail") def test_no_fee_estimate(node_factory, bitcoind, executor): l1 = node_factory.get_node(start=False, options={'dev-no-fake-fees': True}) From 32af92145b2070e47d3acfa31312542f0381c184 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:22:11 +0930 Subject: [PATCH 0912/1530] update-mocks: handle missing deprecated_apis. This expands update-mocks to be able to handle (simple!) missing symbols which are not functions. Signed-off-by: Rusty Russell --- common/test/run-bolt12_decode.c | 2 -- common/test/run-bolt12_merkle.c | 2 -- common/test/run-bolt12_period.c | 2 -- connectd/test/run-gossip_rcvd_filter.c | 3 --- lightningd/test/run-invoice-select-inchan.c | 4 ++-- lightningd/test/run-jsonrpc.c | 4 ++-- tools/mockup.sh | 23 ++++++++++++++------- wallet/test/run-wallet.c | 4 ++-- 8 files changed, 21 insertions(+), 23 deletions(-) diff --git a/common/test/run-bolt12_decode.c b/common/test/run-bolt12_decode.c index f3adb2622c58..42173ebfaa50 100644 --- a/common/test/run-bolt12_decode.c +++ b/common/test/run-bolt12_decode.c @@ -8,8 +8,6 @@ #include #include -bool deprecated_apis = false; - /* AUTOGENERATED MOCKS START */ /* Generated stub for amount_asset_is_main */ bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) diff --git a/common/test/run-bolt12_merkle.c b/common/test/run-bolt12_merkle.c index b21420e55f7b..990692f4dbd0 100644 --- a/common/test/run-bolt12_merkle.c +++ b/common/test/run-bolt12_merkle.c @@ -13,8 +13,6 @@ /* Definition of n1 from the spec */ #include -bool deprecated_apis = false; - /* AUTOGENERATED MOCKS START */ /* Generated stub for features_unsupported */ int features_unsupported(const struct feature_set *our_features UNNEEDED, diff --git a/common/test/run-bolt12_period.c b/common/test/run-bolt12_period.c index 218cea67d391..55e6db907112 100644 --- a/common/test/run-bolt12_period.c +++ b/common/test/run-bolt12_period.c @@ -6,8 +6,6 @@ #include #include -bool deprecated_apis = false; - /* AUTOGENERATED MOCKS START */ /* Generated stub for amount_asset_is_main */ bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) diff --git a/connectd/test/run-gossip_rcvd_filter.c b/connectd/test/run-gossip_rcvd_filter.c index 7a6dfd3dbb7a..dc1f51aba631 100644 --- a/connectd/test/run-gossip_rcvd_filter.c +++ b/connectd/test/run-gossip_rcvd_filter.c @@ -37,9 +37,6 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index ee3267383f06..01699c8b49a9 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -7,8 +7,6 @@ #include #include -bool deprecated_apis = false; - /* AUTOGENERATED MOCKS START */ /* Generated stub for any_channel_by_scid */ struct channel *any_channel_by_scid(struct lightningd *ld UNNEEDED, @@ -163,6 +161,8 @@ void db_commit_transaction(struct db *db UNNEEDED) /* Generated stub for delete_channel */ void delete_channel(struct channel *channel STEALS UNNEEDED) { fprintf(stderr, "delete_channel called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for encode_scriptpubkey_to_addr */ char *encode_scriptpubkey_to_addr(const tal_t *ctx UNNEEDED, const struct chainparams *chainparams UNNEEDED, diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 01806dadf442..61bea726f703 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -12,6 +12,8 @@ void db_begin_transaction_(struct db *db UNNEEDED, const char *location UNNEEDED /* Generated stub for db_commit_transaction */ void db_commit_transaction(struct db *db UNNEEDED) { fprintf(stderr, "db_commit_transaction called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } @@ -123,8 +125,6 @@ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ -bool deprecated_apis; - static int test_json_filter(void) { struct json_stream *result = new_json_stream(NULL, NULL, NULL); diff --git a/tools/mockup.sh b/tools/mockup.sh index c98eb1116cf2..e9808504ecdf 100755 --- a/tools/mockup.sh +++ b/tools/mockup.sh @@ -40,13 +40,18 @@ if [ $# -eq 0 ]; then fi for SYMBOL; do + STUB="" # If there are multiple declarations, pick first (eg. common/memleak.h # has notleak_ as a declaration, and then an inline). # Also, prefer local headers over generic ones. WHERE=$(shopt -s nullglob; grep -nH "^[a-zA-Z0-9_ (),]* [*]*$SYMBOL(" "$UPDIRNAME"/*.h ./*/*.h | head -n1) if [ -z "$WHERE" ]; then - echo "/* Could not find declaration for $SYMBOL */" - continue + WHERE=$(shopt -s nullglob; grep -nH "^extern [a-zA-Z0-9_ (),]* [*]*$SYMBOL;" "$UPDIRNAME"/*.h ./*/*.h | head -n1) + STUB=";" + if [ -z "$WHERE" ]; then + echo "/* Could not find declaration for $SYMBOL */" + continue + fi fi FILE=${WHERE%%:*} @@ -55,13 +60,15 @@ for SYMBOL; do END=$(tail -n "+${LINE}" < "$FILE" | grep -n ';$'); NUM=${END%%:*} - if grep -q "$SYMBOL.*mock empty" "$FILE"; then - STUB="{ }" - else - # \n on RHS is a GNU extension, and we want to work on FreeBSD - # shellcheck disable=SC1004 - STUB='\ + if [ -z "$STUB" ]; then + if grep -q "$SYMBOL.*mock empty" "$FILE"; then + STUB="{ }" + else + # \n on RHS is a GNU extension, and we want to work on FreeBSD + # shellcheck disable=SC1004 + STUB='\ { fprintf(stderr, "'$SYMBOL' called!\\n"); abort(); }' + fi fi echo "/* Generated stub for $SYMBOL */" diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index f3f636fce31a..65cf287f89aa 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -45,8 +45,6 @@ void db_fatal(const char *fmt, ...) #include #include -bool deprecated_apis = true; - /* AUTOGENERATED MOCKS START */ /* Generated stub for bigsize_put */ size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED) @@ -118,6 +116,8 @@ struct onionreply *create_onionreply(const tal_t *ctx UNNEEDED, const struct secret *shared_secret UNNEEDED, const u8 *failure_msg UNNEEDED) { fprintf(stderr, "create_onionreply called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for derive_channel_id */ void derive_channel_id(struct channel_id *channel_id UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED) From f6f1844e15c6573eec6757b83a9ba3a0a41d0f22 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:23:11 +0930 Subject: [PATCH 0913/1530] options: let log-level subsystem filter also cover nodeid. That's useful for "tell me everything about this node" debugging. Signed-off-by: Rusty Russell Fixes: #5348 Changelog-Added: lightningd: `log-level=debug:` supported to get debug-level logs for everything about a peer. --- doc/lightningd-config.5.md | 6 ++++-- lightningd/log.c | 16 +++++++++------- lightningd/log.h | 2 +- lightningd/subd.c | 2 +- lightningd/test/run-find_my_abspath.c | 2 +- lightningd/test/run-shuffle_fds.c | 2 +- tests/test_misc.py | 8 ++++++++ 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 6049178b5d8e..94060f9569df 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -124,7 +124,7 @@ Specify pid file to write to. **log-level**=*LEVEL*\[:*SUBSYSTEM*\] What log level to print out: options are io, debug, info, unusual, broken. If *SUBSYSTEM* is supplied, this sets the logging level -for any subsystem containing that string. This option may be specified multiple times. +for any subsystem (or *nodeid*) containing that string. This option may be specified multiple times. Subsystems include: * *lightningd*: The main lightning daemon @@ -148,7 +148,9 @@ internal integer id assigned for the lifetime of the channel: So, **log-level=debug:plugin** would set debug level logging on all plugins and the plugin manager. **log-level=io:chan#55** would set -IO logging on channel number 55 (or 550, for that matter). +IO logging on channel number 55 (or 550, for that matter). +**log-level=debug:024b9a1fa8** would set debug logging for that channel +(or any node id containing that string). **log-prefix**=*PREFIX* Prefix for log lines: this can be customized if you want to merge logs diff --git a/lightningd/log.c b/lightningd/log.c index ddbcb726738f..d86fcbb24e3b 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -291,13 +291,15 @@ struct log_book *new_log_book(struct lightningd *ld, size_t max_mem) } static enum log_level filter_level(struct log_book *lr, - const struct log_prefix *lp) + const struct log_prefix *lp, + const struct node_id *node_id) { struct print_filter *i; + const char *node_id_str = node_id ? node_id_to_hexstr(tmpctx, node_id) : ""; assert(lr->default_print_level != NULL); list_for_each(&lr->print_filters, i, list) { - if (strstr(lp->prefix, i->prefix)) + if (strstr(lp->prefix, i->prefix) || strstr(node_id_str, i->prefix)) return i->level; } return *lr->default_print_level; @@ -331,14 +333,14 @@ const char *log_prefix(const struct log *log) return log->prefix->prefix; } -enum log_level log_print_level(struct log *log) +enum log_level log_print_level(struct log *log, const struct node_id *node_id) { if (!log->print_level) { /* Not set globally yet? Print UNUSUAL / BROKEN messages only */ if (!log->lr->default_print_level) return LOG_UNUSUAL; log->print_level = tal(log, enum log_level); - *log->print_level = filter_level(log->lr, log->prefix); + *log->print_level = filter_level(log->lr, log->prefix, node_id); } return *log->print_level; } @@ -400,7 +402,7 @@ static struct log_entry *new_log_entry(struct log *log, enum log_level level, static void maybe_print(struct log *log, const struct log_entry *l) { - if (l->level >= log_print_level(log)) + if (l->level >= log_print_level(log, l->nc ? &l->nc->node_id : NULL)) log_to_files(log->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, l->log, @@ -450,7 +452,7 @@ void log_io(struct log *log, enum log_level dir, assert(dir == LOG_IO_IN || dir == LOG_IO_OUT); /* Print first, in case we need to truncate. */ - if (l->level >= log_print_level(log)) + if (l->level >= log_print_level(log, node_id)) log_to_files(log->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, str, @@ -758,7 +760,7 @@ void logging_options_parsed(struct log_book *lr) for (size_t i = 0; i < lr->num_entries; i++) { const struct log_entry *l = &lr->log[i]; - if (l->level >= filter_level(lr, l->prefix)) + if (l->level >= filter_level(lr, l->prefix, NULL)) log_to_files(l->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, l->log, diff --git a/lightningd/log.h b/lightningd/log.h index 95dc2ada261b..a0ebd7cc784a 100644 --- a/lightningd/log.h +++ b/lightningd/log.h @@ -44,7 +44,7 @@ void logv(struct log *log, enum log_level level, const struct node_id *node_id, bool call_notifier, const char *fmt, va_list ap); const char *log_prefix(const struct log *log); -enum log_level log_print_level(struct log *log); +enum log_level log_print_level(struct log *log, const struct node_id *node_id); void opt_register_logging(struct lightningd *ld); diff --git a/lightningd/subd.c b/lightningd/subd.c index a959eb1d7d36..4ee11c1c257e 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -742,7 +742,7 @@ static struct subd *new_subd(const tal_t *ctx, &msg_fd, /* We only turn on subdaemon io logging if we're going * to print it: too stressful otherwise! */ - log_print_level(sd->log) < LOG_DBG, + log_print_level(sd->log, node_id) < LOG_DBG, ap); if (sd->pid == (pid_t)-1) { log_unusual(ld->log, "subd %s failed: %s", diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index a8805bce7cfa..0a25f7c14f88 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -147,7 +147,7 @@ void log_backtrace_print(const char *fmt UNNEEDED, ...) const char *log_prefix(const struct log *log UNNEEDED) { fprintf(stderr, "log_prefix called!\n"); abort(); } /* Generated stub for log_print_level */ -enum log_level log_print_level(struct log *log UNNEEDED) +enum log_level log_print_level(struct log *log UNNEEDED, const struct node_id *node_id UNNEEDED) { fprintf(stderr, "log_print_level called!\n"); abort(); } /* Generated stub for log_status_msg */ bool log_status_msg(struct log *log UNNEEDED, diff --git a/lightningd/test/run-shuffle_fds.c b/lightningd/test/run-shuffle_fds.c index f26907cb66f8..0981f69a3dba 100644 --- a/lightningd/test/run-shuffle_fds.c +++ b/lightningd/test/run-shuffle_fds.c @@ -96,7 +96,7 @@ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *log_prefix(const struct log *log UNNEEDED) { fprintf(stderr, "log_prefix called!\n"); abort(); } /* Generated stub for log_print_level */ -enum log_level log_print_level(struct log *log UNNEEDED) +enum log_level log_print_level(struct log *log UNNEEDED, const struct node_id *node_id UNNEEDED) { fprintf(stderr, "log_print_level called!\n"); abort(); } /* Generated stub for log_status_msg */ bool log_status_msg(struct log *log UNNEEDED, diff --git a/tests/test_misc.py b/tests/test_misc.py index b9357944757a..79d74b2553d9 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2435,6 +2435,14 @@ def test_getlog(node_factory): assert [l for l in logs if l['type'] == 'SKIPPED'] == [] +def test_log_filter(node_factory): + """Test the log-level option with subsystem filters""" + # This actually suppresses debug! + l1, l2 = node_factory.line_graph(2, opts=[{'log-level': ['debug', 'broken:022d223620']}, {}]) + + assert not l1.daemon.is_in_log(r'-chan#[0-9]*:') + + def test_force_feerates(node_factory): l1 = node_factory.get_node(options={'force-feerates': 1111}) assert l1.rpc.listconfigs()['force-feerates'] == '1111' From ae49545875d2d53c00a2999a888fccef749a0b30 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 20:41:47 +0930 Subject: [PATCH 0914/1530] pytest: fix flake in test_option_upfront_shutdown_script Looking at the CI logs, it seems like it took over 5 seconds, so the unilateral close occurred instead of the expected rejection of the WIRE_SHUTDOWN reply. Make it bulletproof. Signed-off-by: Rusty Russell --- tests/test_closing.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 2bd8165e450b..c2214388878e 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3214,11 +3214,14 @@ def test_option_upfront_shutdown_script(node_factory, bitcoind, executor): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, 1000000, False) - l2.rpc.close(l1.info['id'], unilateraltimeout=5) + fut = executor.submit(l2.rpc.close, l1.info['id']) # l2 will send warning unilaterally when it dislikes shutdown script. l1.daemon.wait_for_log(r'WARNING.*scriptpubkey .* is not as agreed upfront \(00143d43d226bcc27019ade52d7a3dc52a7ac1be28b8\)') + l2.rpc.close(l1.info['id'], unilateraltimeout=1) + fut.result(TIMEOUT) + bitcoind.generate_block(1, wait_for_mempool=1) wait_for(lambda: [c['state'] for c in only_one(l1.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN', 'ONCHAIN']) wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers()['peers'])['channels']] == ['ONCHAIN', 'ONCHAIN']) From 68b45c2ae0b132805a85ad4df7ad1451903d7eb5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:25:11 +0930 Subject: [PATCH 0915/1530] pytest: restore prefix to logging. Since we now log directly, we don't prepend the prefix ourselves, making it really hard to tell *which* lightningd the log applies to! Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 028da5260ded..a5f12632f1a4 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -581,6 +581,7 @@ def __init__(self, lightning_dir, bitcoindproxy, port=9735, random_hsm=False, no self.prefix = 'lightningd-%d' % (node_id) # Log to stdout so we see it in failure cases, and log file for TailableProc. self.opts['log-file'] = ['-', os.path.join(lightning_dir, "log")] + self.opts['log-prefix'] = self.prefix + ' ' # In case you want specific ordering! self.early_opts = [] From ba4e9b64b57fa09ccf9adbac7bb9533aed17a607 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:26:11 +0930 Subject: [PATCH 0916/1530] options: print empty options properly. Noticed by log-prefix default (""), which causes bad JSON: ``` Malformed JSON reply '{"jsonrpc":"2.0","id":0,"result":{... "log-prefix":,"log-file":"log"} } ``` Signed-off-by: Rusty Russell --- lightningd/options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/options.c b/lightningd/options.c index 2af57ac09f7c..454131f3e667 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1556,7 +1556,7 @@ static void add_config(struct lightningd *ld, strcpy(buf + OPT_SHOW_LEN - 1, "..."); if (streq(buf, "true") || streq(buf, "false") - || strspn(buf, "0123456789.") == strlen(buf)) { + || (!streq(buf, "") && strspn(buf, "0123456789.") == strlen(buf))) { /* Let pure numbers and true/false through as * literals. */ json_add_literal(response, name0, From 6aec37467471bd8512e42a665233763b5d97d588 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:27:11 +0930 Subject: [PATCH 0917/1530] lightningd: make log-prefix actually prepend all log messages as expected. It actually only sets the prefix for the lightningd core log messages; the other logs have their own prefix. Make it a real, process-wide prefix which actually goes in front of the timestamp. Signed-off-by: Rusty Russell Changelog-Fixed: options: `log-prefix` now correctly prefixes *all* log messages. --- doc/lightningd-config.5.md | 5 +++-- lightningd/log.c | 46 +++++++++++++++++++++----------------- tests/test_gossip.py | 14 ++++++------ 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 94060f9569df..20c0d7b797ce 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -153,8 +153,9 @@ IO logging on channel number 55 (or 550, for that matter). (or any node id containing that string). **log-prefix**=*PREFIX* -Prefix for log lines: this can be customized if you want to merge logs -with multiple daemons. +Prefix for all log lines: this can be customized if you want to merge logs +with multiple daemons. Usually you want to include a space at the end of *PREFIX*, +as the timestamp follows immediately. **log-file**=*PATH* Log to this file (instead of stdout). If you specify this more than once diff --git a/lightningd/log.c b/lightningd/log.c index d86fcbb24e3b..362eedce3ffc 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -45,6 +45,8 @@ struct log_book { bool print_timestamps; struct log_entry *log; + /* Prefix this to every entry as you output */ + const char *prefix; /* Although log_book will copy log entries to parent log_book * (the log_book belongs to lightningd), a pointer to lightningd @@ -130,7 +132,8 @@ static const char *level_prefix(enum log_level level) abort(); } -static void log_to_files(const char *prefix, +static void log_to_files(const char *log_prefix, + const char *entry_prefix, enum log_level level, const struct node_id *node_id, const struct timeabs *time, @@ -154,23 +157,23 @@ static void log_to_files(const char *prefix, const char *dir = level == LOG_IO_IN ? "[IN]" : "[OUT]"; char *hex = tal_hexstr(NULL, io, io_len); if (!node_id) - entry = tal_fmt(tmpctx, "%s%s: %s%s %s\n", - tstamp, prefix, str, dir, hex); + entry = tal_fmt(tmpctx, "%s%s%s: %s%s %s\n", + log_prefix, tstamp, entry_prefix, str, dir, hex); else - entry = tal_fmt(tmpctx, "%s%s-%s: %s%s %s\n", - tstamp, + entry = tal_fmt(tmpctx, "%s%s%s-%s: %s%s %s\n", + log_prefix, tstamp, node_id_to_hexstr(tmpctx, node_id), - prefix, str, dir, hex); + entry_prefix, str, dir, hex); tal_free(hex); } else { if (!node_id) - entry = tal_fmt(tmpctx, "%s%s %s: %s\n", - tstamp, level_prefix(level), prefix, str); + entry = tal_fmt(tmpctx, "%s%s%s %s: %s\n", + log_prefix, tstamp, level_prefix(level), entry_prefix, str); else - entry = tal_fmt(tmpctx, "%s%s %s-%s: %s\n", - tstamp, level_prefix(level), + entry = tal_fmt(tmpctx, "%s%s%s %s-%s: %s\n", + log_prefix, tstamp, level_prefix(level), node_id_to_hexstr(tmpctx, node_id), - prefix, str); + entry_prefix, str); } /* Default if nothing set is stdout */ @@ -278,6 +281,8 @@ struct log_book *new_log_book(struct lightningd *ld, size_t max_mem) lr->max_mem = max_mem; lr->outfiles = NULL; lr->default_print_level = NULL; + /* We have to allocate this, since we tal_free it on resetting */ + lr->prefix = tal_strdup(lr, ""); list_head_init(&lr->print_filters); lr->init_time = time_now(); lr->ld = ld; @@ -403,7 +408,7 @@ static struct log_entry *new_log_entry(struct log *log, enum log_level level, static void maybe_print(struct log *log, const struct log_entry *l) { if (l->level >= log_print_level(log, l->nc ? &l->nc->node_id : NULL)) - log_to_files(log->prefix->prefix, l->level, + log_to_files(log->lr->prefix, log->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, l->log, l->io, tal_bytelen(l->io), @@ -453,7 +458,7 @@ void log_io(struct log *log, enum log_level dir, /* Print first, in case we need to truncate. */ if (l->level >= log_print_level(log, node_id)) - log_to_files(log->prefix->prefix, l->level, + log_to_files(log->lr->prefix, log->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, str, data, len, @@ -619,17 +624,16 @@ static void show_log_level(char buf[OPT_SHOW_LEN], const struct log *log) buf[OPT_SHOW_LEN - 1] = '\0'; } -static char *arg_log_prefix(const char *arg, struct log *log) +static char *arg_log_prefix(const char *arg, struct log_book *log_book) { - /* log->lr owns this, since it keeps a pointer to it. */ - log_prefix_drop(log->prefix); - log->prefix = log_prefix_new(log->lr, arg); + tal_free(log_book->prefix); + log_book->prefix = tal_strdup(log_book, arg); return NULL; } -static void show_log_prefix(char buf[OPT_SHOW_LEN], const struct log *log) +static void show_log_prefix(char buf[OPT_SHOW_LEN], const struct log_book *log_book) { - strncpy(buf, log->prefix->prefix, OPT_SHOW_LEN - 1); + strncpy(buf, log_book->prefix, OPT_SHOW_LEN - 1); buf[OPT_SHOW_LEN - 1] = '\0'; } @@ -742,7 +746,7 @@ void opt_register_logging(struct lightningd *ld) opt_set_bool_arg, opt_show_bool, &ld->log->lr->print_timestamps, "prefix log messages with timestamp"); opt_register_early_arg("--log-prefix", arg_log_prefix, show_log_prefix, - ld->log, + ld->log_book, "log prefix"); opt_register_early_arg("--log-file=", arg_log_to_file, NULL, ld, "Also log to file (- for stdout)"); @@ -761,7 +765,7 @@ void logging_options_parsed(struct log_book *lr) const struct log_entry *l = &lr->log[i]; if (l->level >= filter_level(lr, l->prefix, NULL)) - log_to_files(l->prefix->prefix, l->level, + log_to_files(lr->prefix, l->prefix->prefix, l->level, l->nc ? &l->nc->node_id : NULL, &l->time, l->log, l->io, tal_bytelen(l->io), diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 02ebd320e778..978761691d0b 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -2102,20 +2102,20 @@ def test_addgossip(node_factory): nann1 = l1.daemon.is_in_log(r"\[OUT\] 0101.*") nann2 = l2.daemon.is_in_log(r"\[OUT\] 0101.*") - # Feed them to l3 (Each one starts with TIMESTAMP chanid-xxx: [OUT] ...) - l3.rpc.addgossip(ann.split()[3]) + # Feed them to l3 (Each one starts with PREFIX TIMESTAMP chanid-xxx: [OUT] ...) + l3.rpc.addgossip(ann.split()[4]) - l3.rpc.addgossip(upd1.split()[3]) - l3.rpc.addgossip(upd2.split()[3]) - l3.rpc.addgossip(nann1.split()[3]) - l3.rpc.addgossip(nann2.split()[3]) + l3.rpc.addgossip(upd1.split()[4]) + l3.rpc.addgossip(upd2.split()[4]) + l3.rpc.addgossip(nann1.split()[4]) + l3.rpc.addgossip(nann2.split()[4]) # In this case, it can actually have to wait, since it does scid lookup. wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 2) wait_for(lambda: len(l3.rpc.listnodes()['nodes']) == 2) # Now corrupt an update - badupdate = upd1.split()[3] + badupdate = upd1.split()[4] if badupdate.endswith('f'): badupdate = badupdate[:-1] + 'e' else: From 4633085ffd179bd430ffde7e92f056dc42fb81f2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:28:11 +0930 Subject: [PATCH 0918/1530] lightningd: mark subd->conn notleak() properly in transition. Since subd is temporarily unreferenced, and our leak detection goes up to the highest unreferenced parent, it complains about openingd->conn. The correct fix is to catch this where we detach the subd: ``` DEBUG:root:{ "id": 1, "result": { "leaks": [ { "value": "0x556e0445d8f8", "label": "ccan/ccan/io/io.c:91:struct io_conn", "backtrace": [ "ccan/ccan/tal/tal.c:442 (tal_alloc_)", "ccan/ccan/io/io.c:91 (io_new_conn_)", "lightningd/subd.c:774 (new_subd)", "lightningd/subd.c:828 (new_channel_subd_)", "lightningd/opening_control.c:872 (peer_start_openingd)", "lightningd/peer_control.c:1311 (peer_active)", "lightningd/connect_control.c:458 (connectd_msg)", "lightningd/subd.c:557 (sd_msg_read)", "lightningd/subd.c:357 (read_fds)", "ccan/ccan/io/io.c:59 (next_plan)", "ccan/ccan/io/io.c:407 (do_plan)", "ccan/ccan/io/io.c:417 (io_ready)", "ccan/ccan/io/poll.c:453 (io_loop)", "lightningd/io_loop_with_timers.c:22 (io_loop_with_timers)", "lightningd/lightningd.c:1182 (main)", "../csu/libc-start.c:308 (__libc_start_main)" ], "parents": [ "lightningd/lightningd.c:107:struct lightningd" ] } ] } } ``` Signed-off-by: Rusty Russell --- lightningd/opening_control.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 57240af931d1..a6b8d75325d8 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -570,6 +570,12 @@ static void openingd_failed(struct subd *openingd, const u8 *msg, { char *desc; + /* Since we're detaching from uc, we'll be unreferenced until + * our imminent exit (as will our parent, openingd->conn). */ + notleak(openingd); + /* openingd->conn is set to NULL temporarily for this call, so: */ + notleak(tal_parent(openingd)); + if (!fromwire_openingd_failed(msg, msg, &desc)) { log_broken(uc->log, "bad OPENINGD_FAILED %s", @@ -683,8 +689,6 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS) } else upfront_shutdown_script_wallet_index = NULL; - /* In case peer goes away right now, mark openingd notleak() */ - notleak(openingd); subd_send_msg(openingd, take(towire_openingd_got_offer_reply(NULL, errmsg, our_upfront_shutdown_script, From 6cc4858847189616774057b40671b01e1cf390ec Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:29:11 +0930 Subject: [PATCH 0919/1530] pytest: fix test_gossip_no_backtalk flake. Because we expire cache every flush, in DEVELOPER mode that can happen in just over a second. If gossipd takes a while to process the gossip, this can mean we actually forget we received it from the peer. Easiest fix is to run this test in non-DEVELOPER mode. ``` # With DEVELOPER, this is long enough for gossip flush. time.sleep(2) > assert not l3.daemon.is_in_log(r'\[OUT\] 0100') E AssertionError: assert not '2022-06-30T06:00:31.031Z 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: [OUT] 0100294834e94aa2407ace65373c84c2a8056d67a6778c22bd43f03a9000d8a01f075c3996ab6c88951bd9805adbfde78292022d7514ffcec37d5e466ef5ebd235c87ffa8a8567904502cd65ae913dbb8014509f442a6e37b00fd59fa3a06811a4965f45df1612d3a32b2566001430cfec4346cc021bd0a1bf6a87417dbc29e0715f16c4f9dc5a01bff07368ba8f54cf9813c3fc8f9f8b8c8005da2a18021f322a9a168af8f82d9deba49ced51bb26a9abc001bbad314a15baccc87c685e9302533c2e86a7b9dfe90bd4cb6be5c2da375be8c4a535a63cacff0544957a34ca865fdb61f9198d19dfd990a0fcbf8de39fa2c0fc54435c16e74df2b49fe3b6e905d599000006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f0000670000010001022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d590266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c0351802e3bd38009866c9da8ec4aa99cc4ea9c6c0dd46df15c61ef0ce1f271291714e5702324266de8403b3ab157a09f1f784d587af61831c998c151bcc21bb74c2b2314b' ``` Signed-off-by: Rusty Russell --- tests/test_gossip.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 978761691d0b..4db2a412462f 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1768,7 +1768,7 @@ def test_gossip_announce_unknown_block(node_factory, bitcoind): sync_blockheight(bitcoind, [l1]) -@pytest.mark.developer("gossip without DEVELOPER=1 is slow") +@unittest.skipIf(DEVELOPER, "Developer gossip too fast!") def test_gossip_no_backtalk(node_factory): # l3 connects, gets gossip, but should *not* play it back. l1, l2, l3 = node_factory.get_nodes(3, @@ -1781,8 +1781,8 @@ def test_gossip_no_backtalk(node_factory): r'\[IN\] 0102', r'\[IN\] 0102', r'\[IN\] 0101', r'\[IN\] 0101']) - # With DEVELOPER, this is long enough for gossip flush. - time.sleep(2) + # Will flush every 60 seconds, so definitely should by this time! + time.sleep(90) assert not l3.daemon.is_in_log(r'\[OUT\] 0100') From 7a7e43c07802c303c334b4748f20f75bcdf78687 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 8 Jul 2022 19:30:11 +0930 Subject: [PATCH 0920/1530] channeld: fix uninitializes scid alias for dual-funding. Caused a crash in CI, reproduced under valgrind by calling any_channel_by_scid from io_poll_lightningd: ``` ==2422524== Conditional jump or move depends on uninitialised value(s) ==2422524== at 0x12C98D: any_channel_by_scid (channel.c:606) ==2422524== by 0x14FF75: io_poll_lightningd (lightningd.c:682) ==2422524== by 0x225FDE: io_loop (poll.c:420) ==2422524== by 0x14A914: io_loop_with_timers (io_loop_with_timers.c:22) ==2422524== by 0x150C4E: main (lightningd.c:1193) ==2422524== Uninitialised value was created by a heap allocation ==2422524== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==2422524== by 0x234F61: allocate (tal.c:250) ==2422524== by 0x235522: tal_alloc_ (tal.c:428) ==2422524== by 0x12B500: new_unsaved_channel (channel.c:203) ==2422524== by 0x13B77A: json_openchannel_init (dual_open_control.c:2610) ==2422524== by 0x14C78D: command_exec (jsonrpc.c:630) ==2422524== by 0x14CD9F: rpc_command_hook_final (jsonrpc.c:765) ==2422524== by 0x181DDA: plugin_hook_call_ (plugin_hook.c:278) ==2422524== by 0x14D198: plugin_hook_call_rpc_command (jsonrpc.c:853) ==2422524== by 0x14D6A0: parse_request (jsonrpc.c:957) ==2422524== by 0x14DAFE: read_json (jsonrpc.c:1054) ==2422524== by 0x2231C8: next_plan (io.c:59) ``` Signed-off-by: Rusty Russell --- lightningd/channel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lightningd/channel.c b/lightningd/channel.c index 92c6f809dc26..e1f059bc4752 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -237,6 +237,7 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->shutdown_wrong_funding = NULL; channel->closing_feerate_range = NULL; channel->channel_update = NULL; + channel->alias[LOCAL] = channel->alias[REMOTE] = NULL; /* Channel is connected! */ channel->connected = true; From 7dd8e27862f832f71d0bd11cf428d9b1198e5cdf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 26 Jun 2022 16:14:12 +0930 Subject: [PATCH 0921/1530] connectd: don't insist on ping replies when other traffic is flowing. Got complaints about us hanging up on some nodes because they don't respond to pings in a timely manner (e.g. ACINQ?), but that turned out to be something else. Nonetheless, we've had reports in the past of LND badly prioritizing gossip traffic, and thus important messages can get queued behind gossip dumps! Signed-off-by: Rusty Russell Changelog-Changed: connectd: give busy peers more time to respond to pings. --- connectd/connectd.c | 1 + connectd/connectd.h | 3 +++ connectd/multiplex.c | 26 ++++++++++++++++++-------- tests/test_connection.py | 17 ++++++++++------- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 07a952732188..48d027e54211 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -310,6 +310,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->ready_to_die = false; peer->active = false; peer->peer_outq = msg_queue_new(peer, false); + peer->last_recv_time = time_now(); #if DEVELOPER peer->dev_writes_enabled = NULL; diff --git a/connectd/connectd.h b/connectd/connectd.h index 9af5f815d7ca..db5ec2022120 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -87,6 +87,9 @@ struct peer { /* Random ping timer, to detect dead connections. */ struct oneshot *ping_timer; + /* Last time we received traffic */ + struct timeabs last_recv_time; + #if DEVELOPER bool dev_read_enabled; /* If non-NULL, this counts down; 0 means disable */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index d3d189a4aa22..0db695990ba7 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -425,16 +425,23 @@ static void set_ping_timer(struct peer *peer) static void send_ping(struct peer *peer) { - /* Already have a ping in flight? */ - if (peer->expecting_pong != PONG_UNEXPECTED) { - status_peer_debug(&peer->id, "Last ping unreturned: hanging up"); - if (peer->to_peer) - io_close(peer->to_peer); - return; + /* If it's still sending us traffic, maybe ping reply is backed up? + * That's OK, ping is just to make sure it's still alive, and clearly + * it is. */ + if (time_before(peer->last_recv_time, + timeabs_sub(time_now(), time_from_sec(60)))) { + /* Already have a ping in flight? */ + if (peer->expecting_pong != PONG_UNEXPECTED) { + status_peer_debug(&peer->id, "Last ping unreturned: hanging up"); + if (peer->to_peer) + io_close(peer->to_peer); + return; + } + + inject_peer_msg(peer, take(make_ping(NULL, 1, 0))); + peer->expecting_pong = PONG_EXPECTED_PROBING; } - inject_peer_msg(peer, take(make_ping(NULL, 1, 0))); - peer->expecting_pong = PONG_EXPECTED_PROBING; set_ping_timer(peer); } @@ -993,6 +1000,9 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, if (!IFDEV(peer->dev_read_enabled, true)) return read_hdr_from_peer(peer_conn, peer); + /* We got something! */ + peer->last_recv_time = time_now(); + /* Don't process packets while we're closing */ if (peer->ready_to_die) return read_hdr_from_peer(peer_conn, peer); diff --git a/tests/test_connection.py b/tests/test_connection.py index a4379627b6ae..198556242439 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3892,15 +3892,18 @@ def test_ping_timeout(node_factory): l1, l2 = node_factory.get_nodes(2, opts=[{'dev-no-reconnect': None, 'disconnect': l1_disconnects}, - {}]) + {'dev-no-ping-timer': None}]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - # Takes 15-45 seconds, then another to try second ping - # Because of ping timer randomness we don't know which side hangs up first - wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == [], - timeout=45 + 45 + 5) - wait_for(lambda: (l1.daemon.is_in_log('Last ping unreturned: hanging up') - or l2.daemon.is_in_log('Last ping unreturned: hanging up'))) + # This can take 10 seconds (dev-fast-gossip means timer fires every 5 seconds) + l1.daemon.wait_for_log('seeker: startup peer finished', timeout=15) + # Ping timers runs at 15-45 seconds, *but* only fires if also 60 seconds + # after previous traffic. + l1.daemon.wait_for_log('dev_disconnect: xWIRE_PING', timeout=60 + 45 + 5) + + # Next pign will cause hangup + l1.daemon.wait_for_log('Last ping unreturned: hanging up', timeout=45 + 5) + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) @pytest.mark.openchannel('v1') From 9d1b5a654e99febff568797ca93d4c8e7ac17ee1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 30 Jun 2022 09:48:48 +0930 Subject: [PATCH 0922/1530] pytest: fix flake in test_node_reannounce Fixes 25699994e5941fb165cf379ff3a688981bcdaddb (pytest: fix flake in test_node_reannounce). Converting ->set->list does not give deterministic order, so we can end up with the two lists differing due to order: ``` May send its own announcement *twice*, since it always spams us. msgs2 = list(set(msgs2)) > assert msgs == msgs2 E AssertionError: assert ['01012ff5580...000000000000'] == ['01014973d81...000000000000'] E At index 0 diff: '01012ff55800f5b9492021372d74df4d6547bb0d32aec8d4c932a8c3b044e4bd983c429154e73091b0a2aff1cf9bbf16b37e6e9dd10ce4c2d949217366472acd341b0007800000080269a262bbd1750266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000' != '01014973d8160dd8fc28e8fb25c40b9d5c68aed8dfb36af9fc13e4d2040fb3718553051a188ce98239c0bed138e1f8713a64acc7de98c183c9597fa58bf37f0b89bb0007800000080269a262bbd16c022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d336333626132392d6d6f6464656400000000000000' E Full diff: E [ E + '01012ff55800f5b9492021372d74df4d6547bb0d32aec8d4c932a8c3b044e4bd983c429154e73091b0a2aff1cf9bbf16b37e6e9dd10ce4c2d949217366472acd341b0007800000080269a262bbd1750266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000', E '01014973d8160dd8fc28e8fb25c40b9d5c68aed8dfb36af9fc13e4d2040fb3718553051a188ce98239c0bed138e1f8713a64acc7de98c183c9597fa58bf37f0b89bb0007800000080269a262bbd16c022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d336333626132392d6d6f6464656400000000000000', E - '01012ff55800f5b9492021372d74df4d6547bb0d32aec8d4c932a8c3b044e4bd983c429154e73091b0a2aff1cf9bbf16b37e6e9dd10ce4c2d949217366472acd341b0007800000080269a262bbd1750266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000', E ] `` Signed-off-by: Rusty Russell --- tests/test_gossip.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 4db2a412462f..eabfdb96e07c 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1281,8 +1281,7 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): filters=['0109', '0107', '0102', '0100', '0012']) # May send its own announcement *twice*, since it always spams us. - msgs2 = list(set(msgs2)) - assert msgs == msgs2 + assert set(msgs) == set(msgs2) # Won't have queued up another one, either. assert not l1.daemon.is_in_log('node_announcement: delaying') @@ -1308,8 +1307,7 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # channel_announcement and channel_updates. # And pings. filters=['0109', '0107', '0102', '0100', '0012']) - msgs2 = list(set(msgs2)) - assert msgs != msgs2 + assert set(msgs) != set(msgs2) def test_gossipwith(node_factory): From 3a61e0e18182b3e5ba704f4386ecceb333eb9df6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 10 Jul 2022 16:16:37 +0930 Subject: [PATCH 0923/1530] invoice: turn assert failure into more informative log_broken message. We had this assertion fail, and I can't see a clear reason why. Remove it (i.e. don't crash because we're having trouble with creating invoice routehints) and add logging. ``` Assertion failed: a->c->rr_number < b->c->rr_number (lightningd/invoice.c: cmp_rr_number: 623) lightningd: FATAL SIGNAL 6 (version v0.10.2-modded) 0x5654f1c40061 send_backtrace common/daemon.c:33 0x5654f1c400e9 crashdump common/daemon.c:46 0x7efd87da6c8a ??? ???:0 ``` There are several possible causes for this: 1. We have two channels with the same rr_number. A quick audit shows we always set that rr_number to a unique value (and 64 bits, so wrap is not possible between the release and now!). 2. It's theoretically possible that sort() could compare a value with itself, but that would be really dumb: it doesn't that I've ever seen, but then, we've never seen this assert() hit, either. 3. listincoming has given us the same channel twice. I don't see how that is possible: we had a race where channels could momentarily vanish, but never be duplicated (emailed crash.log shows no duplicates!). 4. General corruption/freed memory access. If a channel we've just looked up is gone but still in the hash table, this is possible but would cause lots of random behavior and crashes. Signed-off-by: Rusty Russell --- lightningd/invoice.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 08dbde6122b6..affd55edfd3d 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -617,13 +617,17 @@ static struct route_info **select_inchan(const tal_t *ctx, static int cmp_rr_number(const struct routehint_candidate *a, const struct routehint_candidate *b, - void *unused) + struct lightningd *ld) { - /* They're unique, so can't be equal */ if (a->c->rr_number > b->c->rr_number) return 1; - assert(a->c->rr_number < b->c->rr_number); - return -1; + if (a->c->rr_number < b->c->rr_number) + return -1; + + /* They're unique, so can't be equal */ + log_broken(ld->log, "Two equal candidates %p and %p, channels %p and %p, rr_number %"PRIu64" and %"PRIu64, + a, b, a->c, b->c, a->c->rr_number, b->c->rr_number); + return 0; } /** select_inchan_mpp @@ -650,7 +654,7 @@ static struct route_info **select_inchan_mpp(const tal_t *ctx, routehints = tal_arr(ctx, struct route_info *, 0); /* Sort by rr_number, so we get fresh channels. */ - asort(candidates, tal_count(candidates), cmp_rr_number, NULL); + asort(candidates, tal_count(candidates), cmp_rr_number, ld); for (size_t i = 0; i < tal_count(candidates); i++) { if (amount_msat_greater_eq(gathered, amount_needed)) break; From 36e51568322f6bd3b2bf6fbcc48695cbe081caa9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 9 Jul 2022 15:23:20 +0930 Subject: [PATCH 0924/1530] lightningd: fix close of ancient all-dust channel. libwally asserts() when trying to calculate the txid: we report it as invalid elsewhere, but then crash: ``` May 19 20:37:03 c-lightning lightningd[26236]: 2022-05-19T20:37:03.689Z DEBUG lightningd: close_command: timeout = 1 May 19 20:37:04 c-lightning lightningd[26236]: 2022-05-19T20:37:04.689Z UNUSUAL 037f32400c108f8385db94371bfcef08948bc0042069c1ef6f39086cdf23420ee5-chan#91: Peer permanent failure in CHANNELD_SHUTTING_DOWN: Forcibly closed by `close` command timeout May 19 20:37:04 c-lightning lightningd[26236]: 2022-05-19T20:37:04.689Z **BROKEN** 037f32400c108f8385db94371bfcef08948bc0042069c1ef6f39086cdf23420ee5-chan#91: Cannot broadcast our commitment tx: it's invalid! (ancient channel?) May 19 20:37:04 c-lightning lightningd[26236]: lightningd: bitcoin/tx.c:485: wally_txid: Assertion `len == written' failed. May 19 20:37:04 c-lightning lightningd[26236]: lightningd: FATAL SIGNAL 6 (version v0.11.1) May 19 20:37:04 c-lightning lightningd[26236]: 0x45bd01 send_backtrace May 19 20:37:04 c-lightning lightningd[26236]: common/daemon.c:33 May 19 20:37:04 c-lightning lightningd[26236]: 0x45bd8b crashdump May 19 20:37:04 c-lightning lightningd[26236]: common/daemon.c:46 May 19 20:37:04 c-lightning lightningd[26236]: 0x7f9f877604bf ??? May 19 20:37:04 c-lightning lightningd[26236]: ???:0 May 19 20:37:04 c-lightning lightningd[26236]: 0x7f9f87760438 ??? May 19 20:37:04 c-lightning lightningd[26236]: ???:0 May 19 20:37:04 c-lightning lightningd[26236]: 0x7f9f87762039 ??? May 19 20:37:04 c-lightning lightningd[26236]: ???:0 May 19 20:37:04 c-lightning lightningd[26236]: 0x7f9f87758be6 ??? May 19 20:37:04 c-lightning lightningd[26236]: ???:0 May 19 20:37:04 c-lightning lightningd[26236]: 0x7f9f87758c91 ??? May 19 20:37:04 c-lightning lightningd[26236]: ???:0 May 19 20:37:04 c-lightning lightningd[26236]: 0x4751b7 wally_txid May 19 20:37:04 c-lightning lightningd[26236]: bitcoin/tx.c:485 May 19 20:37:04 c-lightning lightningd[26236]: 0x4751f4 bitcoin_txid May 19 20:37:04 c-lightning lightningd[26236]: bitcoin/tx.c:496 May 19 20:37:04 c-lightning lightningd[26236]: 0x40bad0 resolve_one_close_command May 19 20:37:04 c-lightning lightningd[26236]: lightningd/closing_control.c:60 May 19 20:37:04 c-lightning lightningd[26236]: 0x40cef3 resolve_close_command May 19 20:37:04 c-lightning lightningd[26236]: lightningd/closing_control.c:82 May 19 20:37:04 c-lightning lightningd[26236]: 0x430355 drop_to_chain May 19 20:37:04 c-lightning lightningd[26236]: lightningd/peer_control.c:286 ``` Fixes: #5277 Signed-off-by: Rusty Russell --- lightningd/closing_control.c | 9 +++++---- lightningd/peer_control.c | 2 +- lightningd/peer_control.h | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 33589e28a469..fea6dc86491a 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -55,12 +55,13 @@ static void resolve_one_close_command(struct close_command *cc, bool cooperative) { struct json_stream *result = json_stream_success(cc->cmd); - struct bitcoin_txid txid; - - bitcoin_txid(cc->channel->last_tx, &txid); json_add_tx(result, "tx", cc->channel->last_tx); - json_add_txid(result, "txid", &txid); + if (!invalid_last_tx(cc->channel->last_tx)) { + struct bitcoin_txid txid; + bitcoin_txid(cc->channel->last_tx, &txid); + json_add_txid(result, "txid", &txid); + } if (cooperative) json_add_string(result, "type", "mutual"); else diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 0b817c5e596c..40bb56812f6f 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -220,7 +220,7 @@ static void remove_sig(struct bitcoin_tx *signed_tx) bitcoin_tx_input_set_witness(signed_tx, 0, NULL); } -static bool invalid_last_tx(const struct bitcoin_tx *tx) +bool invalid_last_tx(const struct bitcoin_tx *tx) { /* This problem goes back further, but was discovered just before the * 0.7.1 release. */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 3f69729b1618..4e9eb5b5d9a8 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -121,4 +121,7 @@ command_find_channel(struct command *cmd, const char *buffer, const jsmntok_t *tok, struct channel **channel); +/* Ancient (0.7.0 and before) releases could create invalid commitment txs! */ +bool invalid_last_tx(const struct bitcoin_tx *tx); + #endif /* LIGHTNING_LIGHTNINGD_PEER_CONTROL_H */ From 6753675c310e2f13f7c9f5434dd90b119ede3607 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 7 Jul 2022 11:56:51 +0930 Subject: [PATCH 0925/1530] lightningd: fire watches on blocks before telling lightningd. We noticed bogus behavior where (with 200 blocks added at once) lightningd didn't see a DF channel open: this is because we told lightningd about the new blocks (and it timed out channel) before the watches for the transaction are fired. Signed-off-by: Rusty Russell --- lightningd/chaintopology.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 65b5734c1beb..9b0d894d472f 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -619,12 +619,12 @@ void topology_add_sync_waiter_(const tal_t *ctx, static void updates_complete(struct chain_topology *topo) { if (!bitcoin_blkid_eq(&topo->tip->blkid, &topo->prev_tip)) { - /* Tell lightningd about new block. */ - notify_new_block(topo->bitcoind->ld, topo->tip->height); - /* Tell watch code to re-evaluate all txs. */ watch_topology_changed(topo); + /* Tell lightningd about new block. */ + notify_new_block(topo->bitcoind->ld, topo->tip->height); + /* Maybe need to rebroadcast. */ rebroadcast_txs(topo, NULL); From d50c62cf4100a670993abed55d63d7aa7f017ecd Mon Sep 17 00:00:00 2001 From: grubles Date: Wed, 6 Jul 2022 14:07:38 -0400 Subject: [PATCH 0926/1530] Update lightning-newaddr.7.md Added a sentence to explain which command to use to send an on-chain payment _from_ the CLN wallet. --- doc/lightning-newaddr.7.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index 60ad04d6f235..55e8235b4eac 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -24,6 +24,8 @@ for the same underlying key. If no *addresstype* is specified the address generated is a *bech32* address. +To send an on-chain payment _from_ the Core Lightning node wallet, use `withdraw`. + RETURN VALUE ------------ From c589543f1cda72ba2c6b45f7613549b1150e7bd7 Mon Sep 17 00:00:00 2001 From: grubles Date: Sat, 9 Jul 2022 20:49:24 -0400 Subject: [PATCH 0927/1530] add generated man page --- doc/lightning-newaddr.7 | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 doc/lightning-newaddr.7 diff --git a/doc/lightning-newaddr.7 b/doc/lightning-newaddr.7 new file mode 100644 index 000000000000..56d9bc736919 --- /dev/null +++ b/doc/lightning-newaddr.7 @@ -0,0 +1,60 @@ +.TH "LIGHTNING-NEWADDR" "7" "" "" "lightning-newaddr" +.SH NAME +lightning-newaddr - Command for generating a new address to be used by Core Lightning +.SH SYNOPSIS + +\fBnewaddr\fR [ \fIaddresstype\fR ] + +.SH DESCRIPTION + +The \fBnewaddr\fR RPC command generates a new address which can +subsequently be used to fund channels managed by the Core Lightning node\. + + +The funding transaction needs to be confirmed before funds can be used\. + + +\fIaddresstype\fR specifies the type of address wanted; i\.e\. \fIp2sh-segwit\fR +(e\.g\. \fB2MxaozoqWwiUcuD9KKgUSrLFDafLqimT9Ta\fR on bitcoin testnet or +\fB3MZxzq3jBSKNQ2e7dzneo9hy4FvNzmMmt3\fR on bitcoin mainnet) or \fIbech32\fR +(e\.g\. \fBtb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg\fR on bitcoin testnet +or \fBbc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej\fR on +bitcoin mainnet)\. The special value \fIall\fR generates both address types +for the same underlying key\. + + +If no \fIaddresstype\fR is specified the address generated is a \fIbech32\fR address\. + + +To send an on-chain payment \fIfrom\fR the Core Lightning node wallet, use \fBwithdraw\fR\. + +.SH RETURN VALUE + +On success, an object is returned, containing: + + +.RS +.IP \[bu] +\fBbech32\fR (string, optional): The bech32 (native segwit) address +.IP \[bu] +\fBp2sh-segwit\fR (string, optional): The p2sh-wrapped address + +.RE +.SH ERRORS + +If an unrecognized address type is requested an error message will be +returned\. + +.SH AUTHOR + +Felix \fI is mainly responsible\. + +.SH SEE ALSO + +\fBlightning-listfunds\fR(7), \fBlightning-fundchannel\fR(7), \fBlightning-withdraw\fR(7), \fBlightning-listtransactions\fR(7) + +.SH RESOURCES + +Main web site: \fIhttps://github.com/ElementsProject/lightning\fR + +\" SHA256STAMP:51dfdd41da7c5ff7dc82c1782f3f8f8448a99f9d0d243fb57443cf1b2d929372 From ae71a87c404475bbbe351a39265e726a8f4ef7eb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 14:14:06 +0930 Subject: [PATCH 0928/1530] ccan: update to latest htable fixes, and update gossmap to meet new assertions. Updating ccan to stricter htable revealed we were trying to put (void *)1 in the htable, which is forbidden: ``` topology: ccan/ccan/htable/htable.c:382: htable_add_: Assertion `entry_is_valid((uintptr_t)p)' failed. topology: FATAL SIGNAL 6 (version 1358d7f) 0x55f30c689c34 send_backtrace common/daemon.c:33 0x55f30c689ce0 crashdump common/daemon.c:46 0x7f5d150fe51f ??? ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 0x7f5d15152828 __pthread_kill_implementation ./nptl/pthread_kill.c:44 0x7f5d15152828 __pthread_kill_internal ./nptl/pthread_kill.c:80 0x7f5d15152828 __GI___pthread_kill ./nptl/pthread_kill.c:91 0x7f5d150fe475 __GI_raise ../sysdeps/posix/raise.c:26 0x7f5d150e47b6 __GI_abort ./stdlib/abort.c:79 0x7f5d150e46da __assert_fail_base ./assert/assert.c:92 0x7f5d150f5e25 __GI___assert_fail ./assert/assert.c:101 0x55f30c6adbe4 htable_add_ ccan/ccan/htable/htable.c:382 0x55f30c65f303 chanidx_htable_add common/gossmap.c:35 0x55f30c6605ed new_channel common/gossmap.c:337 0x55f30c6609cf add_channel common/gossmap.c:425 0x55f30c661101 map_catchup common/gossmap.c:607 0x55f30c66221e gossmap_refresh common/gossmap.c:927 0x55f30c66e3e9 get_gossmap plugins/topology.c:27 0x55f30c66f939 listpeers_done plugins/topology.c:369 0x55f30c671f46 handle_rpc_reply plugins/libplugin.c:558 0x55f30c672a19 rpc_read_response_one plugins/libplugin.c:726 0x55f30c672b4f rpc_conn_read_response plugins/libplugin.c:746 0x55f30c6ae35e next_plan ccan/ccan/io/io.c:59 0x55f30c6aef93 do_plan ccan/ccan/io/io.c:407 0x55f30c6aefd5 io_ready ccan/ccan/io/io.c:417 0x55f30c6b1371 io_loop ccan/ccan/io/poll.c:453 0x55f30c67587c plugin_main plugins/libplugin.c:1559 0x55f30c6708eb main plugins/topology.c:701 0x7f5d150e5fcf __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 0x7f5d150e607c __libc_start_main_impl ../csu/libc-start.c:409 0x55f30c65d894 ??? ???:0 0xffffffffffffffff ??? ???:0 ``` Signed-off-by: Rusty Russell --- ccan/README | 2 +- ccan/ccan/htable/htable.c | 141 +++++++++++++++------- ccan/ccan/htable/htable.h | 2 +- ccan/ccan/htable/test/run-clash.c | 31 +++++ ccan/ccan/htable/test/run-debug.c | 11 +- ccan/ccan/htable/test/run.c | 11 +- ccan/ccan/htable/tools/Makefile | 3 +- ccan/ccan/htable/tools/density.c | 107 +++++++++++++++++ ccan/ccan/io/io.h | 10 +- ccan/ccan/io/poll.c | 12 ++ ccan/ccan/strmap/strmap.c | 17 ++- ccan/ccan/strmap/strmap.h | 19 ++- common/gossmap.c | 10 +- common/test/Makefile | 8 ++ common/test/run-gossmap_canned.c | 186 ++++++++++++++++++++++++++++++ 15 files changed, 496 insertions(+), 74 deletions(-) create mode 100644 ccan/ccan/htable/test/run-clash.c create mode 100644 ccan/ccan/htable/tools/density.c create mode 100644 common/test/run-gossmap_canned.c diff --git a/ccan/README b/ccan/README index 8b86e68fd83a..25c2b543395e 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2524-g609670cc +CCAN version: init-2540-g8448fd28 diff --git a/ccan/ccan/htable/htable.c b/ccan/ccan/htable/htable.c index cffd0619d338..f631ffebf1f7 100644 --- a/ccan/ccan/htable/htable.c +++ b/ccan/ccan/htable/htable.c @@ -86,14 +86,16 @@ void htable_init(struct htable *ht, ht->table = &ht->common_bits; } +/* Fill to 87.5% */ static inline size_t ht_max(const struct htable *ht) { - return ((size_t)3 << ht->bits) / 4; + return ((size_t)7 << ht->bits) / 8; } -static inline size_t ht_max_with_deleted(const struct htable *ht) +/* Clean deleted if we're full, and more than 12.5% deleted */ +static inline size_t ht_max_deleted(const struct htable *ht) { - return ((size_t)9 << ht->bits) / 10; + return ((size_t)1 << ht->bits) / 8; } bool htable_init_sized(struct htable *ht, @@ -103,7 +105,7 @@ bool htable_init_sized(struct htable *ht, htable_init(ht, rehash, priv); /* Don't go insane with sizing. */ - for (ht->bits = 1; ((size_t)3 << ht->bits) / 4 < expect; ht->bits++) { + for (ht->bits = 1; ht_max(ht) < expect; ht->bits++) { if (ht->bits == 30) break; } @@ -195,12 +197,83 @@ void *htable_prev_(const struct htable *ht, struct htable_iter *i) for (;;) { if (!i->off) return NULL; - i->off --; + i->off--; if (entry_is_valid(ht->table[i->off])) return get_raw_ptr(ht, ht->table[i->off]); } } +/* Another bit currently in mask needs to be exposed, so that a bucket with p in + * it won't appear invalid */ +static COLD void unset_another_common_bit(struct htable *ht, + uintptr_t *maskdiff, + const void *p) +{ + size_t i; + + for (i = sizeof(uintptr_t) * CHAR_BIT - 1; i > 0; i--) { + if (((uintptr_t)p & ((uintptr_t)1 << i)) + && ht->common_mask & ~*maskdiff & ((uintptr_t)1 << i)) + break; + } + /* There must have been one, right? */ + assert(i > 0); + + *maskdiff |= ((uintptr_t)1 << i); +} + +/* We want to change the common mask: this fixes up the table */ +static COLD void fixup_table_common(struct htable *ht, uintptr_t maskdiff) +{ + size_t i; + uintptr_t bitsdiff; + +again: + bitsdiff = ht->common_bits & maskdiff; + + for (i = 0; i < (size_t)1 << ht->bits; i++) { + uintptr_t e; + if (!entry_is_valid(e = ht->table[i])) + continue; + + /* Clear the bits no longer in the mask, set them as + * expected. */ + e &= ~maskdiff; + e |= bitsdiff; + /* If this made it invalid, restart with more exposed */ + if (!entry_is_valid(e)) { + unset_another_common_bit(ht, &maskdiff, get_raw_ptr(ht, e)); + goto again; + } + ht->table[i] = e; + } + + /* Take away those bits from our mask, bits and perfect bit. */ + ht->common_mask &= ~maskdiff; + ht->common_bits &= ~maskdiff; + if (ht_perfect_mask(ht) & maskdiff) + ht->perfect_bitnum = NO_PERFECT_BIT; +} + +/* Limited recursion */ +static void ht_add(struct htable *ht, const void *new, size_t h); + +/* We tried to add this entry, but it looked invalid! We need to + * let another pointer bit through mask */ +static COLD void update_common_fix_invalid(struct htable *ht, const void *p, size_t h) +{ + uintptr_t maskdiff; + + assert(ht->elems != 0); + + maskdiff = 0; + unset_another_common_bit(ht, &maskdiff, p); + fixup_table_common(ht, maskdiff); + + /* Now won't recurse */ + ht_add(ht, p, h); +} + /* This does not expand the hash table, that's up to caller. */ static void ht_add(struct htable *ht, const void *new, size_t h) { @@ -214,6 +287,8 @@ static void ht_add(struct htable *ht, const void *new, size_t h) i = (i + 1) & ((1 << ht->bits)-1); } ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect); + if (!entry_is_valid(ht->table[i])) + update_common_fix_invalid(ht, new, h); } static COLD bool double_table(struct htable *ht) @@ -283,20 +358,10 @@ static COLD void rehash_table(struct htable *ht) /* We stole some bits, now we need to put them back... */ static COLD void update_common(struct htable *ht, const void *p) { - unsigned int i; - uintptr_t maskdiff, bitsdiff; + uintptr_t maskdiff; if (ht->elems == 0) { - /* Always reveal one bit of the pointer in the bucket, - * so it's not zero or HTABLE_DELETED (1), even if - * hash happens to be 0. Assumes (void *)1 is not a - * valid pointer. */ - for (i = sizeof(uintptr_t)*CHAR_BIT - 1; i > 0; i--) { - if ((uintptr_t)p & ((uintptr_t)1 << i)) - break; - } - - ht->common_mask = ~((uintptr_t)1 << i); + ht->common_mask = -1; ht->common_bits = ((uintptr_t)p & ht->common_mask); ht->perfect_bitnum = 0; (void)htable_debug(ht, HTABLE_LOC); @@ -306,33 +371,25 @@ static COLD void update_common(struct htable *ht, const void *p) /* Find bits which are unequal to old common set. */ maskdiff = ht->common_bits ^ ((uintptr_t)p & ht->common_mask); - /* These are the bits which go there in existing entries. */ - bitsdiff = ht->common_bits & maskdiff; - - for (i = 0; i < (size_t)1 << ht->bits; i++) { - if (!entry_is_valid(ht->table[i])) - continue; - /* Clear the bits no longer in the mask, set them as - * expected. */ - ht->table[i] &= ~maskdiff; - ht->table[i] |= bitsdiff; - } - - /* Take away those bits from our mask, bits and perfect bit. */ - ht->common_mask &= ~maskdiff; - ht->common_bits &= ~maskdiff; - if (ht_perfect_mask(ht) & maskdiff) - ht->perfect_bitnum = NO_PERFECT_BIT; + fixup_table_common(ht, maskdiff); (void)htable_debug(ht, HTABLE_LOC); } bool htable_add_(struct htable *ht, size_t hash, const void *p) { - if (ht->elems+1 > ht_max(ht) && !double_table(ht)) - return false; - if (ht->elems+1 + ht->deleted > ht_max_with_deleted(ht)) - rehash_table(ht); + /* Cannot insert NULL, or (void *)1. */ assert(p); + assert(entry_is_valid((uintptr_t)p)); + + /* Getting too full? */ + if (ht->elems+1 + ht->deleted > ht_max(ht)) { + /* If we're more than 1/8 deleted, clean those, + * otherwise double table size. */ + if (ht->deleted > ht_max_deleted(ht)) + rehash_table(ht); + else if (!double_table(ht)) + return false; + } if (((uintptr_t)p & ht->common_mask) != ht->common_bits) update_common(ht, p); @@ -361,8 +418,12 @@ void htable_delval_(struct htable *ht, struct htable_iter *i) assert(entry_is_valid(ht->table[i->off])); ht->elems--; - ht->table[i->off] = HTABLE_DELETED; - ht->deleted++; + /* Cheap test: if the next bucket is empty, don't need delete marker */ + if (ht->table[hash_bucket(ht, i->off+1)] != 0) { + ht->table[i->off] = HTABLE_DELETED; + ht->deleted++; + } else + ht->table[i->off] = 0; } void *htable_pick_(const struct htable *ht, size_t seed, struct htable_iter *i) diff --git a/ccan/ccan/htable/htable.h b/ccan/ccan/htable/htable.h index eac57e37a326..faaf541bd8ce 100644 --- a/ccan/ccan/htable/htable.h +++ b/ccan/ccan/htable/htable.h @@ -132,7 +132,7 @@ bool htable_copy_(struct htable *dst, const struct htable *src); * htable_add - add a pointer into a hash table. * @ht: the htable * @hash: the hash value of the object - * @p: the non-NULL pointer + * @p: the non-NULL pointer (also cannot be (void *)1). * * Also note that this can only fail due to allocation failure. Otherwise, it * returns true. diff --git a/ccan/ccan/htable/test/run-clash.c b/ccan/ccan/htable/test/run-clash.c new file mode 100644 index 000000000000..a4e5d38a2bc0 --- /dev/null +++ b/ccan/ccan/htable/test/run-clash.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include + +/* Clashy hash */ +static size_t hash(const void *elem, void *unused UNNEEDED) +{ + return 0; +} + +int main(void) +{ + struct htable ht; + + plan_tests(254 * 253); + /* We try to get two elements which clash */ + for (size_t i = 2; i < 256; i++) { + for (size_t j = 2; j < 256; j++) { + if (i == j) + continue; + htable_init(&ht, hash, NULL); + htable_add(&ht, hash((void *)i, NULL), (void *)i); + htable_add(&ht, hash((void *)j, NULL), (void *)j); + ok1(htable_check(&ht, "test")); + htable_clear(&ht); + } + } + return exit_status(); +} diff --git a/ccan/ccan/htable/test/run-debug.c b/ccan/ccan/htable/test/run-debug.c index b9f48ee70aa9..399910354da2 100644 --- a/ccan/ccan/htable/test/run-debug.c +++ b/ccan/ccan/htable/test/run-debug.c @@ -107,7 +107,7 @@ static bool check_mask(struct htable *ht, uint64_t val[], unsigned num) int main(void) { - unsigned int i, weight; + unsigned int i; uintptr_t perfect_bit; struct htable ht; uint64_t val[NUM_VALS]; @@ -131,14 +131,7 @@ int main(void) add_vals(&ht, val, 0, 1); ok1(ht.bits == 1); ok1(ht_max(&ht) == 1); - weight = 0; - for (i = 0; i < sizeof(ht.common_mask) * CHAR_BIT; i++) { - if (ht.common_mask & ((uintptr_t)1 << i)) { - weight++; - } - } - /* Only one bit should be clear. */ - ok1(weight == i-1); + ok1(ht.common_mask == -1); /* Mask should be set. */ ok1(check_mask(&ht, val, 1)); diff --git a/ccan/ccan/htable/test/run.c b/ccan/ccan/htable/test/run.c index ada85f95a93d..27007a41ac5f 100644 --- a/ccan/ccan/htable/test/run.c +++ b/ccan/ccan/htable/test/run.c @@ -97,7 +97,7 @@ static bool check_mask(struct htable *ht, uint64_t val[], unsigned num) int main(void) { - unsigned int i, weight; + unsigned int i; uintptr_t perfect_bit; struct htable ht; uint64_t val[NUM_VALS]; @@ -122,14 +122,7 @@ int main(void) add_vals(&ht, val, 0, 1); ok1(ht.bits == 1); ok1(ht_max(&ht) == 1); - weight = 0; - for (i = 0; i < sizeof(ht.common_mask) * CHAR_BIT; i++) { - if (ht.common_mask & ((uintptr_t)1 << i)) { - weight++; - } - } - /* Only one bit should be clear. */ - ok1(weight == i-1); + ok1(ht.common_mask == -1); /* Mask should be set. */ ok1(check_mask(&ht, val, 1)); diff --git a/ccan/ccan/htable/tools/Makefile b/ccan/ccan/htable/tools/Makefile index a2cad59f0c2d..c8a428a7567c 100644 --- a/ccan/ccan/htable/tools/Makefile +++ b/ccan/ccan/htable/tools/Makefile @@ -4,9 +4,10 @@ CFLAGS=-Wall -Werror -O3 -I$(CCANDIR) CCAN_OBJS:=ccan-tal.o ccan-tal-str.o ccan-tal-grab_file.o ccan-take.o ccan-time.o ccan-str.o ccan-noerr.o ccan-list.o -all: speed stringspeed hsearchspeed +all: speed stringspeed hsearchspeed density speed: speed.o hash.o $(CCAN_OBJS) +density: density.o hash.o $(CCAN_OBJS) speed.o: speed.c ../htable.h ../htable.c diff --git a/ccan/ccan/htable/tools/density.c b/ccan/ccan/htable/tools/density.c new file mode 100644 index 000000000000..5f7400b9ece6 --- /dev/null +++ b/ccan/ccan/htable/tools/density.c @@ -0,0 +1,107 @@ +/* Density measurements for hashtables. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* We don't actually hash objects: we put values in as if they were ptrs */ +static uintptr_t key(const ptrint_t *obj) +{ + return ptr2int(obj); +} + +static size_t hash_uintptr(uintptr_t key) +{ + return hashl(&key, 1, 0); +} + +static bool cmp(const ptrint_t *p, uintptr_t k) +{ + return key(p) == k; +} + +HTABLE_DEFINE_TYPE(ptrint_t, key, hash_uintptr, cmp, htable_ptrint); + +/* Nanoseconds per operation */ +static size_t normalize(const struct timeabs *start, + const struct timeabs *stop, + unsigned int num) +{ + return time_to_nsec(time_divide(time_between(*stop, *start), num)); +} + +static size_t average_run(const struct htable_ptrint *ht, size_t count, size_t *longest) +{ + size_t i, total = 0; + + *longest = 0; + for (i = 0; i < count; i++) { + size_t h = hash_uintptr(i + 2); + size_t run = 1; + + while (get_raw_ptr(&ht->raw, ht->raw.table[h % ((size_t)1 << ht->raw.bits)]) != int2ptr(i + 2)) { + h++; + run++; + } + total += run; + if (run > *longest) + *longest = run; + } + return total / count; +} + +int main(int argc, char *argv[]) +{ + unsigned int i; + size_t num; + struct timeabs start, stop; + struct htable_ptrint ht; + + if (argc != 2) + errx(1, "Usage: density "); + + num = atoi(argv[1]); + + printf("Total buckets, buckets used, nanoseconds search time per element, avg run, longest run\n"); + for (i = 1; i <= 99; i++) { + uintptr_t j; + struct htable_ptrint_iter it; + size_t count, avg_run, longest_run; + ptrint_t *p; + + htable_ptrint_init_sized(&ht, num * 3 / 4); + assert((1 << ht.raw.bits) == num); + + /* Can't put 0 or 1 in the hash table: multiply by a prime. */ + for (j = 0; j < num * i / 100; j++) { + htable_ptrint_add(&ht, int2ptr(j + 2)); + /* stop it from doubling! */ + ht.raw.elems = num / 2; + } + /* Must not have changed! */ + assert((1 << ht.raw.bits) == num); + + /* Clean cache */ + count = 0; + for (p = htable_ptrint_first(&ht, &it); p; p = htable_ptrint_next(&ht, &it)) + count++; + assert(count == num * i / 100); + start = time_now(); + for (j = 0; j < count; j++) + assert(htable_ptrint_get(&ht, j + 2)); + stop = time_now(); + avg_run = average_run(&ht, count, &longest_run); + printf("%zu,%zu,%zu,%zu,%zu\n", + num, count, normalize(&start, &stop, count), avg_run, longest_run); + fflush(stdout); + htable_ptrint_clear(&ht); + } + + return 0; +} diff --git a/ccan/ccan/io/io.h b/ccan/ccan/io/io.h index 1197626f1267..eeb5e36ecdff 100644 --- a/ccan/ccan/io/io.h +++ b/ccan/ccan/io/io.h @@ -416,7 +416,6 @@ struct io_plan *io_out_always_(struct io_conn *conn, * // Freed if conn closes normally. * timeout = tal(conn, struct timeout_timer); * timeout->conn = conn; - * timeout->t = conn; * timer_addrel(&timers, &timeout->t, time_from_sec(5)); * return io_sock_shutdown(conn); * } @@ -815,4 +814,13 @@ struct timemono (*io_time_override(struct timemono (*now)(void)))(void); */ int (*io_poll_override(int (*poll)(struct pollfd *fds, nfds_t nfds, int timeout)))(struct pollfd *, nfds_t, int); +/** + * io_have_fd - do we own this file descriptor? + * @fd: the file descriptor. + * @listener: if non-NULL, set to true if it's a listening socket (io_listener). + * + * Returns NULL if we don't own it, otherwise a struct io_conn * or struct io_listener *. + */ +const void *io_have_fd(int fd, bool *listener); + #endif /* CCAN_IO_H */ diff --git a/ccan/ccan/io/poll.c b/ccan/ccan/io/poll.c index 4cc9f4b7dc90..634f83d286a6 100644 --- a/ccan/ccan/io/poll.c +++ b/ccan/ccan/io/poll.c @@ -464,3 +464,15 @@ void *io_loop(struct timers *timers, struct timer **expired) return ret; } + +const void *io_have_fd(int fd, bool *listener) +{ + for (size_t i = 0; i < num_fds; i++) { + if (fds[i]->fd != fd) + continue; + if (listener) + *listener = fds[i]->listener; + return fds[i]; + } + return NULL; +} diff --git a/ccan/ccan/strmap/strmap.c b/ccan/ccan/strmap/strmap.c index 9fa51d0d40db..16a30e036ad4 100644 --- a/ccan/ccan/strmap/strmap.c +++ b/ccan/ccan/strmap/strmap.c @@ -17,9 +17,8 @@ struct node { }; /* Closest member to this in a non-empty map. */ -static struct strmap *closest(struct strmap *n, const char *member) +static struct strmap *closest(struct strmap *n, const char *member, size_t len) { - size_t len = strlen(member); const u8 *bytes = (const u8 *)member; /* Anything with NULL value is a node. */ @@ -35,20 +34,26 @@ static struct strmap *closest(struct strmap *n, const char *member) return n; } -void *strmap_get_(const struct strmap *map, const char *member) +void *strmap_getn_(const struct strmap *map, + const char *member, size_t memberlen) { struct strmap *n; /* Not empty map? */ if (map->u.n) { - n = closest((struct strmap *)map, member); - if (streq(member, n->u.s)) + n = closest((struct strmap *)map, member, memberlen); + if (!strncmp(member, n->u.s, memberlen) && !n->u.s[memberlen]) return n->v; } errno = ENOENT; return NULL; } +void *strmap_get_(const struct strmap *map, const char *member) +{ + return strmap_getn_(map, member, strlen(member)); +} + bool strmap_add_(struct strmap *map, const char *member, const void *value) { size_t len = strlen(member); @@ -68,7 +73,7 @@ bool strmap_add_(struct strmap *map, const char *member, const void *value) } /* Find closest existing member. */ - n = closest(map, member); + n = closest(map, member, len); /* Find where they differ. */ for (byte_num = 0; n->u.s[byte_num] == member[byte_num]; byte_num++) { diff --git a/ccan/ccan/strmap/strmap.h b/ccan/ccan/strmap/strmap.h index 0b32e6befc0c..8724c31dcbb2 100644 --- a/ccan/ccan/strmap/strmap.h +++ b/ccan/ccan/strmap/strmap.h @@ -72,7 +72,7 @@ static inline bool strmap_empty_(const struct strmap *map) /** * strmap_get - get a value from a string map * @map: the typed strmap to search. - * @member: the string to search for. + * @member: the string to search for (nul terminated) * * Returns the value, or NULL if it isn't in the map (and sets errno = ENOENT). * @@ -85,6 +85,23 @@ static inline bool strmap_empty_(const struct strmap *map) tcon_cast((map), canary, strmap_get_(tcon_unwrap(map), (member))) void *strmap_get_(const struct strmap *map, const char *member); +/** + * strmap_getn - get a value from a string map + * @map: the typed strmap to search. + * @member: the string to search for. + * @memberlen: the length of @member. + * + * Returns the value, or NULL if it isn't in the map (and sets errno = ENOENT). + * + * Example: + * val = strmap_getn(&map, "hello", 5); + * if (val) + * printf("hello => %i\n", *val); + */ +#define strmap_getn(map, member, n) \ + tcon_cast((map), canary, strmap_getn_(tcon_unwrap(map), (member), (n))) +void *strmap_getn_(const struct strmap *map, const char *member, size_t n); + /** * strmap_add - place a member in the string map. * @map: the typed strmap to add to. diff --git a/common/gossmap.c b/common/gossmap.c index 918fb913a624..ce605048e85e 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -176,25 +176,25 @@ u32 gossmap_chan_idx(const struct gossmap *map, const struct gossmap_chan *chan) return chan - map->chan_arr; } -/* htable can't handle NULL values, so we add 1 */ +/* htable can't handle NULL or 1 values, so we add 2 */ static struct gossmap_chan *ptrint2chan(const ptrint_t *pidx) { - return map->chan_arr + ptr2int(pidx) - 1; + return map->chan_arr + ptr2int(pidx) - 2; } static ptrint_t *chan2ptrint(const struct gossmap_chan *chan) { - return int2ptr(chan - map->chan_arr + 1); + return int2ptr(chan - map->chan_arr + 2); } static struct gossmap_node *ptrint2node(const ptrint_t *pidx) { - return map->node_arr + ptr2int(pidx) - 1; + return map->node_arr + ptr2int(pidx) - 2; } static ptrint_t *node2ptrint(const struct gossmap_node *node) { - return int2ptr(node - map->node_arr + 1); + return int2ptr(node - map->node_arr + 2); } static struct short_channel_id chanidx_id(const ptrint_t *pidx) diff --git a/common/test/Makefile b/common/test/Makefile index ae2803cf5efa..3091105059d5 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -63,6 +63,14 @@ common/test/run-gossmap_local: \ wire/tlvstream.o \ wire/towire.o +common/test/run-gossmap_canned: \ + common/base32.o \ + common/wireaddr.o \ + wire/fromwire.o \ + wire/peer$(EXP)_wiregen.o \ + wire/tlvstream.o \ + wire/towire.o + common/test/run-bolt12_merkle: \ common/amount.o \ common/bigsize.o \ diff --git a/common/test/run-gossmap_canned.c b/common/test/run-gossmap_canned.c new file mode 100644 index 000000000000..5e2542bef9ff --- /dev/null +++ b/common/test/run-gossmap_canned.c @@ -0,0 +1,186 @@ +/* Test a canned gossmap we had troubl with */ +#include "config.h" +#include "../amount.c" +#include "../fp16.c" +#include "../gossmap.c" +#include "../node_id.c" +#include "../pseudorand.c" +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for fromwire_bigsize */ +bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for towire_bigsize */ +void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) +{ fprintf(stderr, "towire_bigsize called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Canned gossmap, taken from od -tx1 -Anone -v < /tmp/ltests-rtchpzh1/test_peers_1/lightning-2/regtest/gossip_store | sed 's/ / 0x/g'| cut -c2- | sed -e 's/ /, /' -e 's/$/,/' */ +static u8 canned_map[] = { + 0x09,0x00,0x00,0x01,0xbc,0x4a,0xe8,0x33,0xa0,0x00,0x00,0x00,0x00,0x10,0x08,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x01,0xb0,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x22,0x6e, + 0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f, + 0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67, + 0x00,0x00,0x01,0x00,0x01,0x02,0x2d,0x22,0x36,0x20,0xa3,0x59,0xa4,0x7f,0xf7,0xf7, + 0xac,0x44,0x7c,0x85,0xc4,0x6c,0x92,0x3d,0xa5,0x33,0x89,0x22,0x1a,0x00,0x54,0xc1, + 0x1c,0x1e,0x3c,0xa3,0x1d,0x59,0x02,0x66,0xe4,0x59,0x8d,0x1d,0x3c,0x41,0x5f,0x57, + 0x2a,0x84,0x88,0x83,0x0b,0x60,0xf7,0xe7,0x44,0xed,0x92,0x35,0xeb,0x0b,0x1b,0xa9, + 0x32,0x83,0xb3,0x15,0xc0,0x35,0x18,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64,0x40, + 0x99,0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19,0xff, + 0x9c,0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x03,0x1b,0x84,0xc5,0x56,0x7b,0x12,0x64, + 0x40,0x99,0x5d,0x3e,0xd5,0xaa,0xba,0x05,0x65,0xd7,0x1e,0x18,0x34,0x60,0x48,0x19, + 0xff,0x9c,0x17,0xf5,0xe9,0xd5,0xdd,0x07,0x8f,0x00,0x00,0x00,0x8e,0xf7,0xf5,0x09, + 0x1e,0x00,0x00,0x00,0x00,0x10,0x06,0x00,0x8a,0x01,0x02,0x53,0x0b,0x21,0x64,0xa7, + 0xd8,0x03,0x4f,0x50,0xb8,0x16,0x9e,0xaa,0x73,0x6b,0xe0,0x70,0x36,0x16,0xc5,0xb1, + 0x04,0xd7,0xfd,0xc0,0x8f,0x68,0x72,0xd7,0x3e,0x38,0x72,0x05,0x7c,0x00,0x81,0x44, + 0x96,0xa2,0x22,0x08,0x15,0xdd,0x88,0x86,0xe7,0xb5,0x97,0x45,0x73,0x46,0x35,0xde, + 0xf0,0x62,0x11,0x22,0x1f,0xea,0x62,0xd0,0x40,0x6f,0x60,0x06,0x22,0x6e,0x46,0x11, + 0x1a,0x0b,0x59,0xca,0xaf,0x12,0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e, + 0x33,0x2a,0x1f,0xc7,0xb2,0xb7,0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00, + 0x01,0x00,0x01,0x62,0xc4,0xdb,0x93,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b, + 0x02,0x33,0x80,0x00,0x00,0x00,0x8e,0x6f,0xbe,0x57,0x16,0x00,0x00,0x00,0x00,0x10, + 0x06,0x00,0x8a,0x01,0x02,0x0c,0x6c,0x84,0xfe,0xfd,0x31,0xe5,0x56,0x63,0xce,0xea, + 0x62,0x3c,0x82,0x3c,0xf8,0xb8,0xae,0x6d,0x8a,0xc7,0x60,0x44,0xe5,0x0d,0xaf,0xdd, + 0x76,0xae,0x54,0x08,0xec,0x13,0x45,0xa0,0x69,0x60,0x89,0x74,0x88,0xe1,0xe8,0xcb, + 0xcc,0x03,0xe3,0x1b,0x4a,0x77,0x17,0xfe,0xa1,0xe1,0xa3,0x42,0x39,0x2d,0xda,0x7b, + 0x68,0xf3,0xd5,0x97,0x88,0x06,0x22,0x6e,0x46,0x11,0x1a,0x0b,0x59,0xca,0xaf,0x12, + 0x60,0x43,0xeb,0x5b,0xbf,0x28,0xc3,0x4f,0x3a,0x5e,0x33,0x2a,0x1f,0xc7,0xb2,0xb7, + 0x3c,0xf1,0x88,0x91,0x0f,0x00,0x00,0x67,0x00,0x00,0x01,0x00,0x01,0x62,0xc4,0xdb, + 0x93,0x01,0x01,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x3b,0x02,0x33,0x80, +}; + +static void check_cannounce(const u8 *cannounce, + const struct short_channel_id *scid, + const struct node_id *n1, + const struct node_id *n2) +{ + secp256k1_ecdsa_signature sig; + u8 *features; + struct bitcoin_blkid chain_hash; + struct short_channel_id actual_scid; + struct node_id actual_n1, actual_n2; + struct pubkey k; + + assert(fromwire_channel_announcement(cannounce, cannounce, + &sig, &sig, &sig, &sig, + &features, &chain_hash, + &actual_scid, + &actual_n1, + &actual_n2, + &k, &k)); + assert(short_channel_id_eq(&actual_scid, scid)); + if (node_id_cmp(n1, n2) < 0) { + assert(node_id_eq(&actual_n1, n1)); + assert(node_id_eq(&actual_n2, n2)); + } else { + assert(node_id_eq(&actual_n1, n2)); + assert(node_id_eq(&actual_n2, n1)); + } +} + +int main(int argc, char *argv[]) +{ + int fd; + char *gossfile; + struct gossmap *map; + struct node_id l1, l2; + struct short_channel_id scid12; + struct amount_sat capacity; + u32 timestamp, fee_base_msat, fee_proportional_millionths; + u8 message_flags, channel_flags; + struct amount_msat htlc_minimum_msat, htlc_maximum_msat; + u8 *cann; + + common_setup(argv[0]); + + fd = tmpdir_mkstemp(tmpctx, "run-gossip_canned.XXXXXX", &gossfile); + assert(write_all(fd, canned_map, sizeof(canned_map))); + + map = gossmap_load(tmpctx, gossfile, NULL); + assert(map); + + /* There is an unannounced channel 1<->2 (103x1x1) */ + assert(node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", 66, &l1)); + assert(node_id_from_hexstr("022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", 66, &l2)); + assert(gossmap_find_node(map, &l1)); + assert(gossmap_find_node(map, &l2)); + + assert(short_channel_id_from_str("103x1x1", 7, &scid12)); + assert(gossmap_find_chan(map, &scid12)); + assert(gossmap_find_chan(map, &scid12)->private); + assert(gossmap_chan_get_capacity(map, gossmap_find_chan(map, &scid12), + &capacity)); + assert(amount_sat_eq(capacity, AMOUNT_SAT(1000000))); + + gossmap_chan_get_update_details(map, gossmap_find_chan(map, &scid12), + 0, + ×tamp, + &message_flags, + &channel_flags, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_minimum_msat, + &htlc_maximum_msat); + assert(timestamp == 1657068435); + assert(message_flags == 1); + assert(channel_flags == 0); + assert(fee_base_msat == 1); + assert(fee_proportional_millionths == 10); + assert(amount_msat_eq(htlc_minimum_msat, AMOUNT_MSAT(0))); + assert(amount_msat_eq(htlc_maximum_msat, AMOUNT_MSAT(990000000))); + + gossmap_chan_get_update_details(map, gossmap_find_chan(map, &scid12), + 1, + ×tamp, + &message_flags, + &channel_flags, + &fee_base_msat, + &fee_proportional_millionths, + &htlc_minimum_msat, + &htlc_maximum_msat); + assert(timestamp == 1657068435); + assert(message_flags == 1); + assert(channel_flags == 1); + assert(fee_base_msat == 1); + assert(fee_proportional_millionths == 10); + assert(amount_msat_eq(htlc_minimum_msat, AMOUNT_MSAT(0))); + assert(amount_msat_eq(htlc_maximum_msat, AMOUNT_MSAT(990000000))); + + assert(tal_bytelen(gossmap_chan_get_features(tmpctx, map, + gossmap_find_chan(map, &scid12))) == 0); + + cann = gossmap_chan_get_announce(tmpctx, map, + gossmap_find_chan(map, &scid12)); + check_cannounce(cann, &scid12, &l1, &l2); + common_shutdown(); +} From 981fd2326a64945e7096c46fce8a2732f3894bc6 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 15 Jun 2022 08:47:57 +0300 Subject: [PATCH 0929/1530] lightningd: convert plugin->cmd to absolute path, fail plugin early when non-existent Otherwise different relative paths (e.g. /dir/plugin and /dir/../dir/plugin) to same plugin executable would not be recognized as the same plugin --- lightningd/options.c | 10 ++++++++-- lightningd/plugin.c | 14 ++++++++++++-- lightningd/plugin_control.c | 4 ++-- tests/test_plugin.py | 3 ++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index 454131f3e667..c1ec6ac3e841 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -458,11 +458,14 @@ static char *opt_add_proxy_addr(const char *arg, struct lightningd *ld) static char *opt_add_plugin(const char *arg, struct lightningd *ld) { + struct plugin *p; if (plugin_blacklisted(ld->plugins, arg)) { log_info(ld->log, "%s: disabled via disable-plugin", arg); return NULL; } - plugin_register(ld->plugins, arg, NULL, false, NULL, NULL); + p = plugin_register(ld->plugins, arg, NULL, false, NULL, NULL); + if (!p) + return tal_fmt(NULL, "Failed to register %s: %s", arg, strerror(errno)); return NULL; } @@ -485,11 +488,14 @@ static char *opt_clear_plugins(struct lightningd *ld) static char *opt_important_plugin(const char *arg, struct lightningd *ld) { + struct plugin *p; if (plugin_blacklisted(ld->plugins, arg)) { log_info(ld->log, "%s: disabled via disable-plugin", arg); return NULL; } - plugin_register(ld->plugins, arg, NULL, true, NULL, NULL); + p = plugin_register(ld->plugins, arg, NULL, true, NULL, NULL); + if (!p) + return tal_fmt(NULL, "Failed to register %s: %s", arg, strerror(errno)); return NULL; } diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 3ad5e330e9a9..5cb6d08b6900 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -253,11 +253,16 @@ struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES, const jsmntok_t *params STEALS) { struct plugin *p, *p_temp; + char* abspath; u32 chksum; + abspath = path_canon(tmpctx, path); + if (!abspath) { + return NULL; + } /* Don't register an already registered plugin */ list_for_each(&plugins->plugins, p_temp, list) { - if (streq(path, p_temp->cmd)) { + if (streq(abspath, p_temp->cmd)) { /* If added as "important", upgrade to "important". */ if (important) p_temp->important = true; @@ -276,7 +281,7 @@ struct plugin *plugin_register(struct plugins *plugins, const char* path TAKES, p = tal(plugins, struct plugin); p->plugins = plugins; - p->cmd = tal_strdup(p, path); + p->cmd = tal_steal(p, abspath); p->checksum = file_checksum(plugins->ld, p->cmd); p->shortname = path_basename(p, p->cmd); p->start_cmd = start_cmd; @@ -912,6 +917,11 @@ static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, popt->name = tal_fmt(popt, "--%.*s", nametok->end - nametok->start, buffer + nametok->start); + + /* an "|" alias could circumvent the unique-option-name check */ + if (strchr(popt->name, '|')) + return tal_fmt(plugin, "Option \"name\" may not contain '|'"); + popt->description = NULL; if (deptok) { if (!json_to_bool(buffer, deptok, &popt->deprecated)) diff --git a/lightningd/plugin_control.c b/lightningd/plugin_control.c index bee3ef56c3a4..6fbaf2a4e873 100644 --- a/lightningd/plugin_control.c +++ b/lightningd/plugin_control.c @@ -71,8 +71,8 @@ plugin_dynamic_start(struct plugin_command *pcmd, const char *plugin_path, if (!p) return command_fail(pcmd->cmd, JSONRPC2_INVALID_PARAMS, - "%s: already registered", - plugin_path); + "%s: %s", plugin_path, + errno ? strerror(errno) : "already registered"); /* This will come back via plugin_cmd_killed or plugin_cmd_succeeded */ err = plugin_send_getmanifest(p); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 78cd3fdb8891..649544b1fa12 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2074,10 +2074,11 @@ def test_important_plugin(node_factory): n = node_factory.get_node(options={"important-plugin": os.path.join(pluginsdir, "nonexistent")}, may_fail=True, expect_fail=True, allow_broken_log=True, start=False) + n.daemon.start(wait_for_initialized=False) # Will exit with failure code. assert n.daemon.wait() == 1 - assert n.daemon.is_in_stderr(r"error starting plugin '.*nonexistent'") + assert n.daemon.is_in_stderr(r"Failed to register .*nonexistent: No such file or directory") # Check we exit if the important plugin dies. n.daemon.opts['important-plugin'] = os.path.join(pluginsdir, "fail_by_itself.py") From 2fddfe3ffc256a9d0f7e9851f2e3227b3936f34c Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 15 Jun 2022 09:10:45 +0300 Subject: [PATCH 0930/1530] lightningd: don't increment plugin state to NEEDS_INIT when error in getmanifest Otherwise we hangs forever in startup when it was the last plugin, we would miss destroy_plugin --> check_plugins_manifests --> io_break e.g. when a plugin tries to register a bool option with a string as default value. --- lightningd/plugin.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 5cb6d08b6900..b8564817b769 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1521,7 +1521,8 @@ static const char *plugin_parse_getmanifest_response(const char *buffer, if (!err) err = plugin_add_params(plugin); - plugin->plugin_state = NEEDS_INIT; + if (!err) + plugin->plugin_state = NEEDS_INIT; return err; } From 7115611249e636d9de081da55446d162538571e4 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 15 Jun 2022 10:26:21 +0300 Subject: [PATCH 0931/1530] pytest: test plugin does not register same option "name" --- tests/plugins/options.py | 2 ++ tests/test_plugin.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/tests/plugins/options.py b/tests/plugins/options.py index d1376a6f806b..14c00dd9775c 100755 --- a/tests/plugins/options.py +++ b/tests/plugins/options.py @@ -22,4 +22,6 @@ def init(configuration, options, plugin): plugin.add_option('str_optm', None, 'an example string option', multi=True) plugin.add_option('int_optm', 7, 'an example int type option', opt_type='int', multi=True) +plugin.add_option('greeting', 7, 'option _names_ should be unique', opt_type='int', multi=True) + plugin.run() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 649544b1fa12..2e8a417b8ec3 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -30,8 +30,10 @@ def test_option_passthrough(node_factory, directory): """ Ensure that registering options works. First attempts without the plugin and then with the plugin. + Then a plugin tries to register the same option "name" again, fails startup. """ plugin_path = os.path.join(os.getcwd(), 'contrib/plugins/helloworld.py') + plugin_path2 = os.path.join(os.getcwd(), 'tests/plugins/options.py') help_out = subprocess.check_output([ 'lightningd/lightningd', @@ -53,6 +55,18 @@ def test_option_passthrough(node_factory, directory): n = node_factory.get_node(options={'plugin': plugin_path, 'greeting': 'Ciao'}) n.stop() + with pytest.raises(subprocess.CalledProcessError): + err_out = subprocess.run([ + 'lightningd/lightningd', + '--lightning-dir={}'.format(directory), + '--plugin={}'.format(plugin_path), + '--plugin={}'.format(plugin_path2), + '--help' + ], capture_output=True, check=True).stderr.decode('utf-8') + + # first come first serve + assert("error starting plugin '{}': option name '--greeting' is already taken".format(plugin_path2) in err_out) + def test_option_types(node_factory): """Ensure that desired types of options are From cdf12d06bab6053c4e31d155a777fe2a2d90407d Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 20 Jun 2022 10:52:37 +0300 Subject: [PATCH 0932/1530] lightningd: Make sure plugins don't register the same option "name" The extra entry in opt_table would never be called, leaving plugins clueless why options keep defaulting. Note that option registration outside startup does nothing. Instead, dynamic plugins can use `plugin start [second_parameter]` to pass options. --- lightningd/plugin.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index b8564817b769..07bb6fa01836 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -886,10 +886,24 @@ char *plugin_opt_set(const char *arg, struct plugin_opt *popt) return NULL; } +/* Returns true if "name" was already registered and now overwritten. */ +static bool plugin_opt_register(struct plugin_opt *popt) +{ + bool was_registered = opt_unregister(popt->name); + if (streq(popt->type, "flag")) + opt_register_noarg(popt->name, plugin_opt_flag_set, popt, + popt->description); + else + opt_register_arg(popt->name, plugin_opt_set, NULL, popt, + popt->description); + + return was_registered; +} + static void destroy_plugin_opt(struct plugin_opt *opt) { - if (!opt_unregister(opt->name)) - fatal("Could not unregister %s", opt->name); + /* does nothing when "name" registration replaced its double */ + opt_unregister(opt->name); list_del(&opt->list); } @@ -978,13 +992,10 @@ static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, list_add_tail(&plugin->plugin_opts, &popt->list); - if (streq(popt->type, "flag")) - opt_register_noarg(popt->name, plugin_opt_flag_set, popt, - popt->description); - - else - opt_register_arg(popt->name, plugin_opt_set, NULL, popt, - popt->description); + /* Command line options are parsed only during ld's startup and each "name" + * only once. Always registers to satisfy destructor */ + if (plugin_opt_register(popt) && plugin->plugins->startup) + fatal("error starting plugin '%s': option name '%s' is already taken", plugin->cmd, popt->name); tal_add_destructor(popt, destroy_plugin_opt); return NULL; From 82a18813b3c510e95730ce7a5d0a150ac53ac2df Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 22 Jun 2022 19:37:46 +0300 Subject: [PATCH 0933/1530] doc: improve/update lightning-plugin, PLUGINS.md and lightning-listconfigs --- doc/PLUGINS.md | 55 ++++++++++++++++++++++++------- doc/lightning-listconfigs.7 | 7 ++-- doc/lightning-listconfigs.7.md | 4 ++- doc/lightning-plugin.7.md | 60 +++++++++++++++++++++------------- doc/schemas/plugin.schema.json | 2 +- 5 files changed, 90 insertions(+), 38 deletions(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index d7e4b3f13445..7751f348abf6 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -7,7 +7,7 @@ variety of ways: - **Command line option passthrough** allows plugins to register their own command line options that are exposed through `lightningd` so - that only the main process needs to be configured. + that only the main process needs to be configured[^options]. - **JSON-RPC command passthrough** adds a way for plugins to add their own commands to the JSON-RPC interface. - **Event stream subscriptions** provide plugins with a push-based @@ -22,16 +22,25 @@ used as protocol on top of the two streams, with the plugin acting as server and `lightningd` acting as client. The plugin file needs to be executable (e.g. use `chmod a+x plugin_name`) +A `helloworld.py` example plugin based on [pyln-client][pyln-client] +can be found [here](../contrib/plugins/helloworld.py). There is also a +[repository](https://github.com/lightningd/plugins) with a collection of +actively maintained plugins and finally, `lightningd`'s own internal +[tests](../tests) can be a useful (and most reliable) resource. + +[^options]: Only for plugins that start when `lightningd` starts, option + values are not remembered when a plugin is stopped or killed. + ### Warning As noted, `lightningd` uses `stdin` as an intake mechanism. This can -cause unexpected behavior if one is not careful. To wit, care should -be taken to ensure that debug/logging statements must be routed to -`stderr` or directly to a file. Activities that are benign in other +cause unexpected behavior if one is not careful. To wit, care should +be taken to ensure that debug/logging statements must be routed to +`stderr` or directly to a file. Activities that are benign in other contexts (`println!`, `dbg!`, etc) will cause the plugin to be killed with an error along the lines of: -`UNUSUAL plugin-cln-plugin-startup: Killing plugin: JSON-RPC message +`UNUSUAL plugin-cln-plugin-startup: Killing plugin: JSON-RPC message does not contain "jsonrpc" field` ## A day in the life of a plugin @@ -47,7 +56,8 @@ plugin dirs, usually `/usr/local/libexec/c-lightning/plugins` and lightningd --plugin=/path/to/plugin1 --plugin=path/to/plugin2 ``` -`lightningd` will run your plugins from the `--lightning-dir`/networkname, then +`lightningd` will run your plugins from the `--lightning-dir`/networkname +as working directory and env variables "LIGHTNINGD_PLUGIN" and "LIGHTNINGD_VERSION" set, then will write JSON-RPC requests to the plugin's `stdin` and will read replies from its `stdout`. To initialize the plugin two RPC methods are required: @@ -67,6 +77,13 @@ through incoming JSON-RPC commands that were registered and the plugin may interact with `lightningd` using the JSON-RPC over Unix-Socket interface. +Above is generally valid for plugins that start when `lightningd` starts. +For dynamic plugins that start via the [plugin][lightning-plugin] JSON-RPC command there +is some difference, mainly in options passthrough (see note in [Types of Options](#types-of-options)). + + - `shutdown` (optional): if subscribed to "shutdown" notification, a plugin can + exit cleanly when `lightningd` is shutting down or when stopped via `plugin stop`. + ### The `getmanifest` method The `getmanifest` method is required for all plugins and will be @@ -123,8 +140,8 @@ example: } ``` -The `options` will be added to the list of command line options that -`lightningd` accepts. The above will add a `--greeting` option with a +During startup the `options` will be added to the list of command line options that +`lightningd` accepts. If any `options` "name" is already taken startup will abort. The above will add a `--greeting` option with a default value of `World` and the specified description. *Notice that currently string, integers, bool, and flag options are supported.* @@ -142,8 +159,8 @@ you plan on removing them: this will disable them if the user sets right?). The `dynamic` indicates if the plugin can be managed after `lightningd` -has been started. Critical plugins that should not be stopped should set it -to false. +has been started using the [plugin][lightning-plugin] JSON-RPC command. Critical plugins that should not be stopped should set it +to false. Plugin `options` can be passed to dynamic plugins as argument to the `plugin` command . If a `disable` member exists, the plugin will be disabled and the contents of this member is the reason why. This allows plugins to disable themselves @@ -175,7 +192,7 @@ Plugins are free to register any `name` for their `rpcmethod` as long as the name was not previously registered. This includes both built-in methods, such as `help` and `getinfo`, as well as methods registered by other plugins. If there is a conflict then `lightningd` will report -an error and exit. +an error and kill the plugin, this aborts startup if the plugin is *important*. #### Types of Options @@ -230,6 +247,12 @@ Here's an example option set, as sent in response to `getmanifest` ], ``` +**Note**: `lightningd` command line options are only parsed during startup and their +values are not remembered when the plugin is stopped or killed. +For dynamic plugins started with `plugin start`, options can be +passed as extra arguments to that [command][lightning-plugin]. + + #### Custom notifications The plugins may emit custom notifications for topics they have @@ -328,6 +351,12 @@ of this member is the reason why. The `startup` field allows a plugin to detect if it was started at `lightningd` startup (true), or at runtime (false). +### Timeouts +During startup ("startup" is true), the plugin has 60 seconds to +return `getmanifest` and another 60 seconds to return `init`, or gets killed. +When started dynamically via the [plugin][lightning-plugin] JSON-RPC command, both `getmanifest` +and `init` should be completed within 60 seconds. + ## JSON-RPC passthrough Plugins may register their own JSON-RPC methods that are exposed @@ -1488,7 +1517,7 @@ handled the remaining plugins will be skipped. ### `rpc_command` The `rpc_command` hook allows a plugin to take over any RPC command. It sends -the received JSON-RPC request to the registered plugin, +the received JSON-RPC request (for any method!) to the registered plugin, ```json { @@ -1716,3 +1745,5 @@ The plugin must broadcast it and respond with the following fields: [oddok]: https://github.com/lightningnetwork/lightning-rfc/blob/master/00-introduction.md#its-ok-to-be-odd [spec]: [https://github.com/lightningnetwork/lightning-rfc] [bolt9]: https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md +[lightning-plugin]: lightning-plugin.7.md +[pyln-client]: ../contrib/pyln-client diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index 2784ef17047c..7ab2c1168bb7 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -7,7 +7,10 @@ lightning-listconfigs - Command to list all configuration options\. .SH DESCRIPTION -The \fBlistconfigs\fR RPC command to list all configuration options, or with \fIconfig\fR, just that one\. +\fIconfig\fR (optional) is a configuration option name, or "plugin" to show plugin options + + +The \fBlistconfigs\fR RPC command to list all configuration options, or with \fIconfig\fR only a selection\. The returned values reflect the current configuration, including @@ -283,4 +286,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:c55d5a93c5917bbab0be1633f0f9f06196cfdde6b79434ef012e9dfc2fbbcca9 +\" SHA256STAMP:67e5f100671ed8ef0e490caa46d57dc73e7443caa86a85f32806a0e8183cd1b8 diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 823ee79ba8fe..f304e2057b33 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -9,7 +9,9 @@ SYNOPSIS DESCRIPTION ----------- -The **listconfigs** RPC command to list all configuration options, or with *config*, just that one. +*config* (optional) is a configuration option name, or "plugin" to show plugin options + +The **listconfigs** RPC command to list all configuration options, or with *config* only a selection. The returned values reflect the current configuration, including showing default values (`dev-` options are not shown). diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index dd9b765d4ca7..f1557b01614a 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -4,35 +4,46 @@ lightning-plugin -- Manage plugins with RPC SYNOPSIS -------- -**plugin** command [*parameter*] [*second\_parameter*] +**plugin** *subcommand* [plugin|directory] [*options*] ... + DESCRIPTION ----------- -The **plugin** RPC command allows to manage plugins without having to -restart lightningd. It takes 1 to 3 parameters: a command -(start/stop/startdir/rescan/list) which describes the action to take and -optionally one or two parameters which describes the plugin on which the -action has to be taken. +The **plugin** RPC command command can be used to control dynamic plugins, +i.e. plugins that declared themself "dynamic" (in getmanifest). + +*subcommand* can be **start**, **stop**, **startdir**, **rescan** or **list** and +determines what action is taken + +*plugin* is the *path* or *name* of a plugin executable to start or stop + +*directory* is the *path* of a directory containing plugins -The *start* command takes a path as the first parameter and will load -the plugin available from this path. The path can be a full path to a -plugin or a relative path to a plugin that is located in or below the -default plugins directory. Any additional parameters are passed to the -plugin. It will wait for the plugin to complete the handshake with -`lightningd` for 20 seconds at the most. +*options* are optional *keyword=value* options passed to plugin, can be repeated -The *stop* command takes a plugin name as parameter. It will kill and -unload the specified plugin. +*subcommand* **start** takes a *path* to an executable as argument and starts it as plugin. +*path* may be an absolute path or a path relative to the plugins directory (default *~/.lightning/plugins*). +If the plugin is already running and the executable (checksum) has changed, the plugin is +killed and restarted except if its an important (or builtin) plugin. +If the plugin doesn't complete the "getmanifest" and "init" handshakes within 60 seconds, +the command will timeout and kill the plugin. +Additional *options* may be passed to the plugin, but requires all parameters to +be passed as keyword=value pairs, for example: + `lightning-cli -k plugin subcommand=start plugin=helloworld.py greeting='A crazy'` +(using the `-k|--keyword` option is recommended) -The *startdir* command takes a directory path as first parameter and will -load all plugins this directory contains. It will wait for each plugin to -complete the handshake with `lightningd` for 20 seconds at the most. +*subcommand* **stop** takes a plugin executable *path* or *name* as argument and stops the plugin. +If the plugin subscribed to "shutdown", it may take up to 30 seconds before this +command returns. If the plugin is important and dynamic, this will shutdown `lightningd`. -The *rescan* command starts all not-already-loaded plugins from the -default plugins directory (by default *~/.lightning/plugins*). +*subcommand* **startdir** starts all executables it can find in *directory* (excl. subdirectories) +as plugins. Checksum and timeout behavior as in **start** applies. -The *list* command will return all the active plugins. +*subcommand* **rescan** starts all plugins in the default plugins directory (default *~/.lightning/plugins*) +that are not already running. Checksum and timeout behavior as in **start** applies. + +*subcommand* **list** lists all running plugins (incl. non-dynamic) RETURN VALUE ------------ @@ -44,7 +55,7 @@ On success, an object is returned, containing: If **command** is "start", "startdir", "rescan" or "list": - **plugins** (array of objects): - **name** (string): full pathname of the plugin - - **active** (boolean): status; since plugins are configured asynchronously, a freshly started plugin may not appear immediately. + - **active** (boolean): status; plugin completed init and is operational, plugins are configured asynchronously. If **command** is "stop": - **result** (string): A message saying it successfully stopped @@ -54,6 +65,10 @@ If **command** is "stop": On error, the reason why the action could not be taken upon the plugin is returned. +SEE ALSO +-------- +lightning-cli(1), lightning-listconfigs(1), [writing plugins][writing plugins] + AUTHOR ------ @@ -64,4 +79,5 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a07c71d232c39c0b959d07b9391d107413841753b67443d5f3698e1afd9cd2e4) +[writing plugins]: PLUGINS.md +[comment]: # ( SHA256STAMP:ee9c974be30d7870cb6a7785956548700b95d2d6b3fe4f18f7757afdfa015553) diff --git a/doc/schemas/plugin.schema.json b/doc/schemas/plugin.schema.json index f59c4c51375e..2d70c846a1db 100644 --- a/doc/schemas/plugin.schema.json +++ b/doc/schemas/plugin.schema.json @@ -57,7 +57,7 @@ }, "active": { "type": "boolean", - "description": "status; since plugins are configured asynchronously, a freshly started plugin may not appear immediately." + "description": "status; plugin completed init and is operational, plugins are configured asynchronously." } } } From 71cd07ea616192a580941cf4615b9aea58769e89 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Mon, 4 Jul 2022 11:23:06 +0300 Subject: [PATCH 0934/1530] json: add "dynamic" field to plugin list CHANGELOG: add "dynamic" field to plugin list --- doc/lightning-plugin.7.md | 3 ++- doc/schemas/plugin.schema.json | 7 ++++++- lightningd/plugin_control.c | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index f1557b01614a..a79cb4d7b92b 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -56,6 +56,7 @@ If **command** is "start", "startdir", "rescan" or "list": - **plugins** (array of objects): - **name** (string): full pathname of the plugin - **active** (boolean): status; plugin completed init and is operational, plugins are configured asynchronously. + - **dynamic** (boolean): plugin can be stopped or started without restarting lightningd If **command** is "stop": - **result** (string): A message saying it successfully stopped @@ -80,4 +81,4 @@ RESOURCES Main web site: [writing plugins]: PLUGINS.md -[comment]: # ( SHA256STAMP:ee9c974be30d7870cb6a7785956548700b95d2d6b3fe4f18f7757afdfa015553) +[comment]: # ( SHA256STAMP:5c3b25ecb137bfe7a81f80f0b4183a9e1282530ebfac3b1bc05fc5406f59cfc7) diff --git a/doc/schemas/plugin.schema.json b/doc/schemas/plugin.schema.json index 2d70c846a1db..345d285e6fa3 100644 --- a/doc/schemas/plugin.schema.json +++ b/doc/schemas/plugin.schema.json @@ -48,7 +48,8 @@ "additionalProperties": false, "required": [ "name", - "active" + "active", + "dynamic" ], "properties": { "name": { @@ -58,6 +59,10 @@ "active": { "type": "boolean", "description": "status; plugin completed init and is operational, plugins are configured asynchronously." + }, + "dynamic": { + "type": "boolean", + "description": "plugin can be stopped or started without restarting lightningd" } } } diff --git a/lightningd/plugin_control.c b/lightningd/plugin_control.c index 6fbaf2a4e873..d60b397c8208 100644 --- a/lightningd/plugin_control.c +++ b/lightningd/plugin_control.c @@ -34,6 +34,7 @@ static struct command_result *plugin_dynamic_list_plugins(struct plugin_command json_add_string(response, "name", p->cmd); json_add_bool(response, "active", p->plugin_state == INIT_COMPLETE); + json_add_bool(response, "dynamic", p->dynamic); json_object_end(response); } json_array_end(response); From 42783aaa928da79e1295dcdc53123a66cb2433cc Mon Sep 17 00:00:00 2001 From: Justin Moon Date: Sun, 10 Jul 2022 13:28:42 +0200 Subject: [PATCH 0935/1530] cln_plugin: Configure "dynamic" field in "getmanifest" message --- plugins/src/lib.rs | 9 +++++++++ plugins/src/messages.rs | 1 + 2 files changed, 10 insertions(+) diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 876bf59d3edc..53720790c7fd 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -48,6 +48,7 @@ where configuration: Option, rpcmethods: HashMap>, subscriptions: HashMap>, + dynamic: bool, } impl Builder @@ -66,6 +67,7 @@ where options: vec![], configuration: None, rpcmethods: HashMap::new(), + dynamic: false, } } @@ -142,6 +144,12 @@ where self } + /// Send true value for "dynamic" field in "getmanifest" response + pub fn dynamic(mut self) -> Builder { + self.dynamic = true; + self + } + /// Communicate with `lightningd` to tell it about our options, /// RPC methods and subscribe to hooks, and then process the /// initialization, configuring the plugin. @@ -280,6 +288,7 @@ where subscriptions: self.subscriptions.keys().map(|s| s.clone()).collect(), hooks: self.hooks.keys().map(|s| s.clone()).collect(), rpcmethods, + dynamic: self.dynamic, } } diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index 90be18872551..9b23e4395e0c 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -156,6 +156,7 @@ pub(crate) struct GetManifestResponse { pub(crate) rpcmethods: Vec, pub(crate) subscriptions: Vec, pub(crate) hooks: Vec, + pub(crate) dynamic: bool, } #[derive(Serialize, Default, Debug)] From e3f53e072f1626d54792b539eea9023a8502ce5b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 20 Dec 2021 12:11:41 +0100 Subject: [PATCH 0936/1530] make: Add macos M1 support The M1 Macs support both x86_64 and arm64 architectures, which forced homebrew to use a different path for its storage (`/opt/homebrew/` instead of `/usr/local`). If we don't adjust the path we'd mix x86_64 and arm64 libraries which can lead to weird compiler and linker errors. This patch just introduces `CPATH` and `LIBRARY_PATH` as suggested by the homebrew team, and detects the current architecture automatically. Changelog-Added: macos: Added m1 architecture support for macos --- Makefile | 21 ++++++++++++++++++--- configure | 12 ++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 809ae4ecac7d..f2d2c1038900 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,8 @@ SUPPRESS_OUTPUT := endif DISTRO=$(shell lsb_release -is 2>/dev/null || echo unknown)-$(shell lsb_release -rs 2>/dev/null || echo unknown) +OS=$(shell uname -s) +ARCH=$(shell uname -m) # Changing this could break installs! PKGNAME = c-lightning @@ -231,8 +233,21 @@ ALL_C_HEADERS := header_versions_gen.h version_gen.h # Extra (non C) targets that should be built by default. DEFAULT_TARGETS := +# M1 macos machines with homebrew will install the native libraries in +# /opt/homebrew instead of /usr/local, most likely because they +# emulate x86_64 compatibility via Rosetta, and wanting to keep the +# libraries separate. This however means we also need to switch out +# the paths accordingly when we detect we're on an M1 macos machine. +ifeq ("$(OS)-$(ARCH)", "Darwin-arm64") +CPATH := /opt/homebrew/include +LIBRARY_PATH := /opt/homebrew/lib +else +CPATH := /usr/local/include +LIBRARY_PATH := /usr/local/lib +endif + CPPFLAGS += -DBINTOPKGLIBEXECDIR="\"$(shell sh tools/rel.sh $(bindir) $(pkglibexecdir))\"" -CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) $(COPTFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I/usr/local/include $(SQLITE3_CFLAGS) $(POSTGRES_INCLUDE) $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) -DBUILD_ELEMENTS=1 +CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) $(COPTFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I$(CPATH) $(SQLITE3_CFLAGS) $(POSTGRES_INCLUDE) $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) -DBUILD_ELEMENTS=1 # If CFLAGS is already set in the environment of make (to whatever value, it # does not matter) then it would export it to subprocesses with the above value # we set, including CWARNFLAGS which by default contains -Wall -Werror. This @@ -250,9 +265,9 @@ ifeq ($(STATIC),1) # For MacOS, Jacob Rapoport changed this to: # -L/usr/local/lib -Wl,-lgmp -lsqlite3 -lz -Wl,-lm -lpthread -ldl $(COVFLAGS) # But that doesn't static link. -LDLIBS = -L/usr/local/lib -Wl,-dn -lgmp $(SQLITE3_LDLIBS) -lz -Wl,-dy -lm -lpthread -ldl $(COVFLAGS) +LDLIBS = -L$(CPATH) -Wl,-dn -lgmp $(SQLITE3_LDLIBS) -lz -Wl,-dy -lm -lpthread -ldl $(COVFLAGS) else -LDLIBS = -L/usr/local/lib -lm -lgmp $(SQLITE3_LDLIBS) -lz $(COVFLAGS) +LDLIBS = -L$(CPATH) -lm -lgmp $(SQLITE3_LDLIBS) -lz $(COVFLAGS) endif # If we have the postgres client library we need to link against it as well diff --git a/configure b/configure index 8fdf93ea5fa2..63451852b74b 100755 --- a/configure +++ b/configure @@ -8,6 +8,18 @@ CONFIG_VAR_FILE=config.vars CONFIG_HEADER=ccan/config.h BASE_WARNFLAGS="-Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition -Werror" +OS=$(uname -s) +ARCH=$(uname -m) +if [ "$OS-$ARCH" = "Darwin-arm64" ]; then +CPATH=/opt/homebrew/include +LIBRARY_PATH=/opt/homebrew/lib +export PKG_CONFIG_PATH=/opt/homebrew/opt/sqlite/lib/pkgconfig +else +CPATH=/usr/local/lib +LIBRARY_PATH=/usr/local/lib +export PKG_CONFIG_PATH=/usr/local/opt/sqlite/lib/pkgconfig +fi + : ${PKG_CONFIG=pkg-config} # You can set PG_CONFIG in the environment to direct configure to call From 74c3325208fbe24200a499c786c44345c4e7b5d1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 5 Jan 2022 15:56:53 +0100 Subject: [PATCH 0937/1530] mac: Ensure that we compile the configurator with the M1 libs --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 63451852b74b..4f35f15537a4 100755 --- a/configure +++ b/configure @@ -301,7 +301,7 @@ fi # Clean up on exit. trap "rm -f $CONFIG_VAR_FILE.$$" 0 -$CONFIGURATOR --extra-tests --autotools-style --var-file=$CONFIG_VAR_FILE.$$ --header-file=$CONFIG_HEADER.$$ --configurator-cc="$CONFIGURATOR_CC" --wrapper="$CONFIGURATOR_WRAPPER" "$CC" ${CWARNFLAGS-$BASE_WARNFLAGS} $CDEBUGFLAGS $COPTFLAGS -I/usr/local/include -L/usr/local/lib $SQLITE3_CFLAGS $POSTGRES_INCLUDE < Date: Fri, 7 Jan 2022 14:26:04 +0100 Subject: [PATCH 0938/1530] git: Ignore arm64-darwin external build directory --- external/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/external/.gitignore b/external/.gitignore index 2d7ecacf49e5..ee164eb9059d 100644 --- a/external/.gitignore +++ b/external/.gitignore @@ -2,6 +2,7 @@ x86_64-linux-gnu aarch64-linux-gnu arm-linux-gnueabihf x86_64-pc-linux-gnu +arm64-apple-darwin* libbacktrace-build/ libbacktrace.a From 9ff9d3d4e33c690d8c600bc740c76dd2554dd6b6 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 11 Jul 2022 17:29:00 +0200 Subject: [PATCH 0939/1530] tests: Fix the canned gossmap The gossip_store version was bumbed to 10 in PR #5239 and #5375 has apparently not been rebased on top of that when it was merged, causing the `run-gossmap_canned` test to fail when parsing the v9 gossip store. Changelog-None --- common/test/run-gossmap_canned.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/test/run-gossmap_canned.c b/common/test/run-gossmap_canned.c index 5e2542bef9ff..9e608b275aae 100644 --- a/common/test/run-gossmap_canned.c +++ b/common/test/run-gossmap_canned.c @@ -29,7 +29,7 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Canned gossmap, taken from od -tx1 -Anone -v < /tmp/ltests-rtchpzh1/test_peers_1/lightning-2/regtest/gossip_store | sed 's/ / 0x/g'| cut -c2- | sed -e 's/ /, /' -e 's/$/,/' */ static u8 canned_map[] = { - 0x09,0x00,0x00,0x01,0xbc,0x4a,0xe8,0x33,0xa0,0x00,0x00,0x00,0x00,0x10,0x08,0x00, + 0x0a,0x00,0x00,0x01,0xbc,0x4a,0xe8,0x33,0xa0,0x00,0x00,0x00,0x00,0x10,0x08,0x00, 0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x01,0xb0,0x01,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, From b8edd3478cc1c9870d1c7ae1453f67d84a11d082 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sat, 9 Jul 2022 12:14:39 +0200 Subject: [PATCH 0940/1530] doc: Add two entries to the FaQ about channel management --- doc/FAQ.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index 7df516248ae8..7dd4600764b3 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -131,9 +131,71 @@ Tools for replication are currently in active development, using the `db_write` [plugin hook](https://lightning.readthedocs.io/PLUGINS.html#db-write). -## Loss +## Channel Management + +### How to forget about a channel? + +Channels may end up stuck during funding and never confirm +on-chain. There is a variety of causes, the most common ones being +that the funds have been double-spent, or the funding fee was too low +to be confirmed. This is unlikely to happen in normal operation, as +CLN tries to use sane defaults and prevents double-spends whenever +possible, but using custom feerates or when the bitcoin backend has no +good fee estimates it is still possible. + +Before forgetting about a channel it is important to ensure that the +funding transaction will never be confirmable by double-spending the +funds. To do so you have to rescan the UTXOs using +[`dev-rescan-outputs`](#rescanning) to reset any funds that may have +been used in the funding transaction, then move all the funds to a new +address: + +```bash +lightning-cli dev-rescan-outputs +ADDR=$(lightning-cli newaddr bech32 | jq .bech32) +lightning-cli withdraw $ADDR all +``` + +This step is not required if the funding transaction was already +double-spent, however it is safe to do it anyway, just in case. + +Then wait for the transaction moving the funds to confirm. This +ensures any pending funding transaction can no longer be +confirmed. Now you can use the `dev-forget-channel` command to remove +the DB entries from the database. + +```bash +lightning-cli dev-forget-channel $NODEID +``` + +This will perform additional checks on whether it is safe to forget +the channel, and only then removes the channel from the DB. Notice +that this command is only available if CLN was compiled with +`DEVELOPER=1`. + +### My channel is stuck in state `CHANNELD_AWAITING_LOCKIN` + +This state means that we are waiting for the funding transaction to +confirm. Confirmations may take a long time, especially when the fees +used for the funding transaction were low. You can check if the +transaction is still going to confirm by looking the funding +transaction on a block explorer: + +```bash +TXID=$(lightning-cli listpeers $PEERID | jq -r ''.peers[].channels[].funding_txid') +``` + +This will give you the funding transaction ID that can be looked up in +any explorer. + +If you don't want to wait for the channel to confirm, you could forget +the channel (see [How to forget about a channel?](#forget-channel) for +details), however be careful as that may be dangerous and you'll need +to rescan and double-spend the outputs so the funding cannot confirm. + +## Loss of funds -### Rescanning the block chain for lost utxos +### Rescanning the block chain for lost utxos There are 3 types of 'rescans' you can make: - `rescanblockchain`: A `bitcoind` RPC call which rescans the blockchain From 4ae7b993de606e3978b9734072ab199d06616b42 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Sat, 9 Jul 2022 12:33:16 +0200 Subject: [PATCH 0941/1530] docs: Add reference to issue 5366 for stuck channel in awaiting --- doc/FAQ.md | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index 7dd4600764b3..f5279cf3742f 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -161,7 +161,18 @@ double-spent, however it is safe to do it anyway, just in case. Then wait for the transaction moving the funds to confirm. This ensures any pending funding transaction can no longer be -confirmed. Now you can use the `dev-forget-channel` command to remove +confirmed. + +As an additional step you can also force-close the unconfirmed channel: + +```bash +lightning-cli close $PEERID 10 # Force close after 10 seconds +``` + +This will store a unilateral close TX in the DB as last resort means +of recovery should the channel unexpectedly confirm anyway. + +Now you can use the `dev-forget-channel` command to remove the DB entries from the database. ```bash @@ -175,11 +186,40 @@ that this command is only available if CLN was compiled with ### My channel is stuck in state `CHANNELD_AWAITING_LOCKIN` -This state means that we are waiting for the funding transaction to -confirm. Confirmations may take a long time, especially when the fees -used for the funding transaction were low. You can check if the -transaction is still going to confirm by looking the funding -transaction on a block explorer: +There are two root causes to this issue: + - Funding transaction isn't confirmed yet. In this case we have to + wait longer, or, in the case of a transaction that'll never + confirm, forget the channel safely. + - The peer hasn't sent a lockin message. This message ackowledges + that the node has seen sufficiently many confirmations to consider + the channel funded. + +In the case of a confirmed funding transaction but a missing lockin +message, a simple reconnection may be sufficient to nudge it to +acknowledge the confirmation: + +```bash +lightning-cli disconnect $PEERID true # force a disconnect +lightning-cli connect $PEERID +``` + +The lack of funding locked messages is a bug we are trying to debug +here at issue [#5366][5366], if you have encountered this issue please +drop us a comment and any information that may be helpful. + +If this didn't work it could be that the peer is simply not caught up +with the blockchain and hasn't seen the funding confirm yet. In this +case we can either wait or force a unilateral close: + +```bash +lightning-cli close $PEERID 10 # Force a unilateral after 10 seconds +``` + +If the funding transaction is not confirmed we may either wait or +attempt to double-spend it. Confirmations may take a long time, +especially when the fees used for the funding transaction were +low. You can check if the transaction is still going to confirm by +looking the funding transaction on a block explorer: ```bash TXID=$(lightning-cli listpeers $PEERID | jq -r ''.peers[].channels[].funding_txid') @@ -241,6 +281,7 @@ funds into that wallet. [spec-features]: https://github.com/lightningnetwork/lightning-rfc/blob/master/09-features.md [mandelbit-recovery]: https://github.com/mandelbit/bitcoin-tutorials/blob/master/CLightningRecoverFunds.md +[5366]: https://github.com/ElementsProject/lightning/issues/5366 ## Technical Questions From 980f3bda1fe4e464c471a8eb57c062ec93b34021 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 9 Jul 2022 12:53:23 +0930 Subject: [PATCH 0942/1530] pytest: test that listforwards gives as much outgoing information as possible. Signed-off-by: Rusty Russell --- tests/test_pay.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index dbfd884540a9..5ee21e31fb94 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1383,6 +1383,7 @@ def test_forward_stats(node_factory, bitcoind): assert 'received_time' in stats['forwards'][2] and 'resolved_time' not in stats['forwards'][2] +@pytest.mark.xfail(strict=True) @pytest.mark.developer("too slow without --dev-fast-gossip") @pytest.mark.slow_test def test_forward_local_failed_stats(node_factory, bitcoind, executor): @@ -1608,6 +1609,10 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): assert 'received_time' in stats['forwards'][3] and 'resolved_time' not in stats['forwards'][3] assert 'received_time' in stats['forwards'][3] and 'resolved_time' not in stats['forwards'][4] + # Correct in and out channels + assert [s['in_channel'] for s in stats['forwards']] == [c12] * 5 + assert [s.get('out_channel') for s in stats['forwards']] == [c23, c24, c25, None, c24] + @pytest.mark.developer("too slow without --dev-fast-gossip") @pytest.mark.slow_test From d6afb0cd8d40fe3836dbc27375caee37bc95fb96 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 9 Jul 2022 12:53:23 +0930 Subject: [PATCH 0943/1530] lightningd: allow outgoing_scid without outgoing amount. This (will) happen if they ask is to forward to an unknown scid. Signed-off-by: Rusty Russell --- lightningd/peer_htlcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 62f0756449f9..29a7de316564 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2779,7 +2779,7 @@ void json_format_forwarding_object(struct json_stream *response, "in_msatoshi", "in_msat"); /* These can be unset (aka zero) if we failed before channel lookup */ - if (cur->channel_out.u64 != 0) { + if (!amount_msat_eq(cur->msat_out, AMOUNT_MSAT(0))) { json_add_amount_msat_compat(response, cur->msat_out, "out_msatoshi", "out_msat"); From 3a1a7eb93f3daef5b90cb3ccc9ac48a85b4a0fa3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 9 Jul 2022 12:53:23 +0930 Subject: [PATCH 0944/1530] wallet: allow saving forwarding scid even if we don't have amount. They're not logically connected: we can know where they wanted to go, but we didn't send it. Where possible, it's the scid *they asked for*; otherwise, it's the scid or fallback to the alias, but do this in the *caller*, not by overriding inside wallet_forwarded_payment_add. Signed-off-by: Rusty Russell --- lightningd/peer_htlcs.c | 12 ++++++------ wallet/wallet.c | 30 ++++++++++++++++-------------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 29a7de316564..f747af9ead3b 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -702,7 +702,7 @@ static void forward_htlc(struct htlc_in *hin, local_fail_in_htlc(hin, take(towire_unknown_next_peer(NULL))); wallet_forwarded_payment_add(hin->key.channel->peer->ld->wallet, hin, get_onion_style(hin), - next ? next->scid : NULL, NULL, + scid, NULL, FORWARD_LOCAL_FAILED, WIRE_UNKNOWN_NEXT_PEER); return; @@ -797,7 +797,7 @@ static void forward_htlc(struct htlc_in *hin, fail: local_fail_in_htlc(hin, failmsg); wallet_forwarded_payment_add(ld->wallet, - hin, get_onion_style(hin), next->scid, hout, + hin, get_onion_style(hin), scid, hout, FORWARD_LOCAL_FAILED, fromwire_peektype(failmsg)); } @@ -1337,7 +1337,7 @@ static void fulfill_our_htlc_out(struct channel *channel, struct htlc_out *hout, fulfill_htlc(hout->in, preimage); wallet_forwarded_payment_add(ld->wallet, hout->in, get_onion_style(hout->in), - hout->key.channel->scid, hout, + channel_scid_or_local_alias(hout->key.channel), hout, FORWARD_SETTLED, 0); } } @@ -1465,7 +1465,7 @@ static bool peer_failed_our_htlc(struct channel *channel, if (hout->in) wallet_forwarded_payment_add(ld->wallet, hout->in, get_onion_style(hout->in), - channel->scid, + channel_scid_or_local_alias(channel), hout, FORWARD_FAILED, hout->failmsg ? fromwire_peektype(hout->failmsg) @@ -1628,7 +1628,7 @@ void onchain_failed_our_htlc(const struct channel *channel, take(towire_permanent_channel_failure(NULL))); wallet_forwarded_payment_add(hout->key.channel->peer->ld->wallet, hout->in, get_onion_style(hout->in), - channel->scid, hout, + channel_scid_or_local_alias(channel), hout, FORWARD_LOCAL_FAILED, hout->failmsg ? fromwire_peektype(hout->failmsg) @@ -1795,7 +1795,7 @@ static bool update_out_htlc(struct channel *channel, if (hout->in) { wallet_forwarded_payment_add(ld->wallet, hout->in, get_onion_style(hout->in), - channel->scid, hout, + channel_scid_or_local_alias(channel), hout, FORWARD_OFFERED, 0); } diff --git a/wallet/wallet.c b/wallet/wallet.c index 1cc1703e45ea..efd966d32a74 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4361,22 +4361,16 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, in->dbid); - if (out) { - db_bind_u64(stmt, 1, out->dbid); + /* FORWARD_LOCAL_FAILED may occur before we get htlc_out */ + if (!out || !scid_out) { + assert(failcode != 0); + assert(state == FORWARD_LOCAL_FAILED); + } - /* If we forward we must have a name for this - * channel. It can be either a locally assigned alias, - * or the real scid. */ - db_bind_u64(stmt, 3, channel_scid_or_local_alias(out->key.channel)->u64); - db_bind_amount_msat(stmt, 5, &out->msat); - } else { - /* FORWARD_LOCAL_FAILED may occur before we get htlc_out */ - assert(failcode != 0); - assert(state == FORWARD_LOCAL_FAILED); + if (out) + db_bind_u64(stmt, 1, out->dbid); + else db_bind_null(stmt, 1); - db_bind_null(stmt, 3); - db_bind_null(stmt, 5); - } /* We use the LOCAL alias, since that's under our control, and * we keep it stable, whereas the REMOTE alias is likely what @@ -4385,7 +4379,15 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, assert(in->key.channel->scid != NULL || in->key.channel->alias[LOCAL]); db_bind_u64(stmt, 2, channel_scid_or_local_alias(in->key.channel)->u64); + if (scid_out) + db_bind_u64(stmt, 3, scid_out->u64); + else + db_bind_null(stmt, 3); db_bind_amount_msat(stmt, 4, &in->msat); + if (out) + db_bind_amount_msat(stmt, 5, &out->msat); + else + db_bind_null(stmt, 5); db_bind_int(stmt, 6, wallet_forward_status_in_db(state)); db_bind_timeabs(stmt, 7, in->received_time); From 312751075c0ef069e771698852d650dab3d09dda Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 9 Jul 2022 12:53:24 +0930 Subject: [PATCH 0945/1530] lightningd: save outgoing information for more forwards. There's one case where we can present more infomation, so do that, and fix documentation. Changelog-Added: JSON-RPC: `listforwards` now shows `out_channel` in more cases: even if it couldn't actually send to it. Signed-off-by: Rusty Russell Fixes: #5329 --- doc/lightning-listforwards.7.md | 6 +++--- doc/schemas/listforwards.schema.json | 10 ++++++---- lightningd/peer_htlcs.c | 4 ++-- tests/test_pay.py | 1 - 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 88edf2872b16..0af6cf491488 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -27,11 +27,11 @@ On success, an object containing **forwards** is returned. It is an array of ob - **in_msat** (msat): the value of the incoming HTLC - **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed") - **received_time** (number): the UNIX timestamp when this was received -- **out_channel** (short_channel_id, optional): the channel that the HTLC was forwarded to +- **out_channel** (short_channel_id, optional): the channel that the HTLC (trying to) forward to - **payment_hash** (hex, optional): payment hash sought by HTLC (always 64 characters) - **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") -If **out_channel** is present: +If **out_msat** is present: - **fee_msat** (msat): the amount this paid in fees - **out_msat** (msat): the amount we sent out the *out_channel* @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:131410f052b8a1845c8d3c7eb2d48df0fc7638e7d26817f56863815be86d8f1e) +[comment]: # ( SHA256STAMP:c8adfa0a6dcae939c5da919d7996261db8663a871210e33b426c3b40c30d7b29) diff --git a/doc/schemas/listforwards.schema.json b/doc/schemas/listforwards.schema.json index 2f0042c46783..7702a9f7e08d 100644 --- a/doc/schemas/listforwards.schema.json +++ b/doc/schemas/listforwards.schema.json @@ -45,7 +45,7 @@ }, "out_channel": { "type": "short_channel_id", - "description": "the channel that the HTLC was forwarded to" + "description": "the channel that the HTLC (trying to) forward to" }, "payment_hash": { "type": "hex", @@ -66,14 +66,15 @@ { "if": { "required": [ - "out_channel" + "out_msat" ] }, "then": { "additionalProperties": false, "required": [ "fee_msat", - "out_msat" + "out_msat", + "out_channel" ], "properties": { "in_channel": {}, @@ -116,7 +117,8 @@ "resolved_time": {}, "payment_hash": {}, "failcode": {}, - "failreason": {} + "failreason": {}, + "out_channel": {} } } }, diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index f747af9ead3b..be84af86d568 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -538,11 +538,11 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds UNU fail_in_htlc(hout->in, failonion); /* here we haven't called connect_htlc_out(), - * so set htlc field with NULL */ + * so set htlc field with NULL (db wants it to exist!) */ wallet_forwarded_payment_add(ld->wallet, hout->in, get_onion_style(hout->in), - NULL, NULL, + channel_scid_or_local_alias(hout->key.channel), NULL, FORWARD_LOCAL_FAILED, fromwire_peektype(hout->failmsg)); } diff --git a/tests/test_pay.py b/tests/test_pay.py index 5ee21e31fb94..a3c21a4f3258 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1383,7 +1383,6 @@ def test_forward_stats(node_factory, bitcoind): assert 'received_time' in stats['forwards'][2] and 'resolved_time' not in stats['forwards'][2] -@pytest.mark.xfail(strict=True) @pytest.mark.developer("too slow without --dev-fast-gossip") @pytest.mark.slow_test def test_forward_local_failed_stats(node_factory, bitcoind, executor): From ddf8fbdb5d224909cfade68178cc41efb0b9d61f Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Sun, 10 Jul 2022 17:13:09 -0500 Subject: [PATCH 0946/1530] gossipd: fix crash from gossip_store v10 changes routing.c fixed to properly remove rate-limited gossip_store entries when channels are closed. This caused gossipd to crash on a subsequent gossip_store_load. Also corrects an overzealous limit of one gossip_store entry per message (should now allow one broadcastable and one rate-limited). Addresses issues 5387, 5395. Changelog-None --- gossipd/gossip_store.c | 6 ++- gossipd/routing.c | 93 +++++++++++++++++++++++++++++++-------- gossipd/routing.h | 6 ++- wire/test/run-tlvstream.c | 2 +- 4 files changed, 83 insertions(+), 24 deletions(-) diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 7054b39bbaa4..5adf99b7bb44 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -841,7 +841,8 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) case WIRE_CHANNEL_UPDATE: if (!routing_add_channel_update(rstate, take(msg), gs->len, - NULL, false)) { + NULL, false, + be32_to_cpu(hdr.len) & GOSSIP_STORE_LEN_RATELIMIT_BIT)) { bad = "Bad channel_update"; goto badmsg; } @@ -850,7 +851,8 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) case WIRE_NODE_ANNOUNCEMENT: if (!routing_add_node_announcement(rstate, take(msg), gs->len, - NULL, NULL)) { + NULL, NULL, + be32_to_cpu(hdr.len) & GOSSIP_STORE_LEN_RATELIMIT_BIT)) { bad = "Bad node_announcement"; goto badmsg; } diff --git a/gossipd/routing.c b/gossipd/routing.c index 2b9dde5961c0..b79b3e3d80b1 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -431,6 +431,7 @@ static void force_node_announce_rexmit(struct routing_state *rstate, bool is_local = node_id_eq(&node->id, &rstate->local_id); announce = gossip_store_get(tmpctx, rstate->gs, node->bcast.index); + u32 initial_bcast_index = node->bcast.index; gossip_store_delete(rstate->gs, &node->bcast, WIRE_NODE_ANNOUNCEMENT); @@ -440,6 +441,20 @@ static void force_node_announce_rexmit(struct routing_state *rstate, is_local, false, NULL); + if (node->rgraph.index == initial_bcast_index){ + node->rgraph.index = node->bcast.index; + } else { + announce = gossip_store_get(tmpctx, rstate->gs, node->rgraph.index); + gossip_store_delete(rstate->gs, + &node->rgraph, + WIRE_NODE_ANNOUNCEMENT); + node->rgraph.index = gossip_store_add(rstate->gs, + announce, + node->rgraph.timestamp, + is_local, + false, + NULL); + } } static void remove_chan_from_node(struct routing_state *rstate, @@ -463,6 +478,10 @@ static void remove_chan_from_node(struct routing_state *rstate, /* Last channel? Simply delete node (and associated announce) */ if (num_chans == 0) { + if(node->rgraph.index != node->bcast.index) + gossip_store_delete(rstate->gs, + &node->rgraph, + WIRE_NODE_ANNOUNCEMENT); gossip_store_delete(rstate->gs, &node->bcast, WIRE_NODE_ANNOUNCEMENT); @@ -475,6 +494,10 @@ static void remove_chan_from_node(struct routing_state *rstate, /* Removed only public channel? Remove node announcement. */ if (!node_has_broadcastable_channels(node)) { + if(node->rgraph.index != node->bcast.index) + gossip_store_delete(rstate->gs, + &node->rgraph, + WIRE_NODE_ANNOUNCEMENT); gossip_store_delete(rstate->gs, &node->bcast, WIRE_NODE_ANNOUNCEMENT); @@ -744,7 +767,8 @@ static void process_pending_node_announcement(struct routing_state *rstate, if (!routing_add_node_announcement(rstate, pna->node_announcement, pna->index, - pna->peer_softref, NULL)) + pna->peer_softref, NULL, + false)) status_unusual("pending node_announcement %s too old?", tal_hex(tmpctx, pna->node_announcement)); /* Never send this again. */ @@ -814,8 +838,14 @@ static void delete_chan_messages_from_store(struct routing_state *rstate, /* If these aren't in the store, these are noops. */ gossip_store_delete(rstate->gs, &chan->bcast, announcment_type); + if (chan->half[0].rgraph.index != chan->half[0].bcast.index) + gossip_store_delete(rstate->gs, + &chan->half[0].rgraph, update_type); gossip_store_delete(rstate->gs, &chan->half[0].bcast, update_type); + if (chan->half[1].rgraph.index != chan->half[1].bcast.index) + gossip_store_delete(rstate->gs, + &chan->half[1].rgraph, update_type); gossip_store_delete(rstate->gs, &chan->half[1].bcast, update_type); } @@ -912,10 +942,10 @@ bool routing_add_channel_announcement(struct routing_state *rstate, /* If we had private updates, they'll immediately create the channel. */ if (private_updates[0]) routing_add_channel_update(rstate, take(private_updates[0]), 0, - peer, false); + peer, false, false); if (private_updates[1]) routing_add_channel_update(rstate, take(private_updates[1]), 0, - peer, false); + peer, false, false); /* Now we can finish cleanup of gossip store, so there's no window where * channel (or nodes) vanish. */ @@ -1243,7 +1273,8 @@ bool routing_add_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 index, struct peer *peer, - bool ignore_timestamp) + bool ignore_timestamp, + bool force_spam_flag) { secp256k1_ecdsa_signature signature; struct short_channel_id short_channel_id; @@ -1335,11 +1366,20 @@ bool routing_add_channel_update(struct routing_state *rstate, hc = &chan->half[direction]; if (is_halfchan_defined(hc) && !ignore_timestamp) { - /* If we're loading from store, duplicate entries are a bug. */ - if (index != 0) { - status_broken("gossip_store channel_update %u replaces %u!", - index, hc->bcast.index); - return false; + /* The gossip_store should contain a single broadcastable entry + * and potentially one rate-limited entry. Any more is a bug */ + if (index){ + if (!force_spam_flag){ + status_broken("gossip_store broadcastable " + "channel_update %u replaces %u!", + index, hc->bcast.index); + return false; + } else if (hc->bcast.index != hc->rgraph.index){ + status_broken("gossip_store rate-limited " + "channel_update %u replaces %u!", + index, hc->bcast.index); + return false; + } } if (timestamp <= hc->rgraph.timestamp) { @@ -1379,6 +1419,8 @@ bool routing_add_channel_update(struct routing_state *rstate, } else { spam = false; } + if (force_spam_flag) + spam = true; /* Routing graph always uses the latest message. */ hc->rgraph.timestamp = timestamp; if (spam) { @@ -1394,14 +1436,14 @@ bool routing_add_channel_update(struct routing_state *rstate, hc->bcast.timestamp = timestamp; /* Remove prior spam update if one exists. */ if (hc->rgraph.index != hc->bcast.index) { - /* Safe even if was never added, but if it's a - * private channel it would be a + /* If it's a private channel it would be a * WIRE_GOSSIP_STORE_PRIVATE_UPDATE. */ gossip_store_delete(rstate->gs, &hc->rgraph, is_chan_public(chan) ? WIRE_CHANNEL_UPDATE : WIRE_GOSSIP_STORE_PRIVATE_UPDATE); } + /* Harmless if it was never added. */ gossip_store_delete(rstate->gs, &hc->bcast, is_chan_public(chan) ? WIRE_CHANNEL_UPDATE @@ -1598,7 +1640,7 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, return warn; } - routing_add_channel_update(rstate, take(serialized), 0, peer, force); + routing_add_channel_update(rstate, take(serialized), 0, peer, force, false); return NULL; } @@ -1606,7 +1648,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, const u8 *msg TAKES, u32 index, struct peer *peer, - bool *was_unknown) + bool *was_unknown, + bool force_spam_flag) { struct node *node; secp256k1_ecdsa_signature signature; @@ -1676,10 +1719,20 @@ bool routing_add_node_announcement(struct routing_state *rstate, bool only_tlv_diff; u32 redundant_time; - if (index != 0) { - status_broken("gossip_store node_announcement %u replaces %u!", - index, node->bcast.index); - return false; + /* The gossip_store should contain a single broadcastable entry + * and potentially one rate-limited entry. Any more is a bug */ + if (index){ + if (!force_spam_flag){ + status_broken("gossip_store broadcastable " + "node_announcement %u replaces %u!", + index, node->bcast.index); + return false; + } else if (node->bcast.index != node->rgraph.index){ + status_broken("gossip_store rate-limited " + "node_announcement %u replaces %u!", + index, node->bcast.index); + return false; + } } if (node->rgraph.timestamp >= timestamp) { @@ -1719,6 +1772,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, } else { spam = false; } + if (force_spam_flag) + spam = true; /* Routing graph always references the latest message. */ node->rgraph.timestamp = timestamp; @@ -1726,10 +1781,10 @@ bool routing_add_node_announcement(struct routing_state *rstate, node->bcast.timestamp = timestamp; /* remove prior spam update if one exists */ if (node->rgraph.index != node->bcast.index) { - /* Harmless if it was never added */ gossip_store_delete(rstate->gs, &node->rgraph, WIRE_NODE_ANNOUNCEMENT); } + /* Harmless if it was never added */ gossip_store_delete(rstate->gs, &node->bcast, WIRE_NODE_ANNOUNCEMENT); /* Remove prior spam update. */ @@ -1848,7 +1903,7 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, } /* May still fail, if we don't know the node. */ - routing_add_node_announcement(rstate, serialized, 0, peer, was_unknown); + routing_add_node_announcement(rstate, serialized, 0, peer, was_unknown, false); return NULL; } diff --git a/gossipd/routing.h b/gossipd/routing.h index b58161d85019..e9a2bc0f6ae6 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -363,7 +363,8 @@ bool routing_add_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 index, struct peer *peer, - bool ignore_timestamp); + bool ignore_timestamp, + bool force_spam_flag); /** * Add a node_announcement to the network view without checking it * @@ -375,7 +376,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, const u8 *msg TAKES, u32 index, struct peer *peer, - bool *was_unknown); + bool *was_unknown, + bool force_spam_flag); /** diff --git a/wire/test/run-tlvstream.c b/wire/test/run-tlvstream.c index c989095251c6..04853ee45ad3 100644 --- a/wire/test/run-tlvstream.c +++ b/wire/test/run-tlvstream.c @@ -25,7 +25,7 @@ static const char *reason; /* Generated stub for chainparams_by_chainhash */ const struct chainparams *chainparams_by_chainhash(const struct bitcoin_blkid *chain_hash UNNEEDED) { fprintf(stderr, "chainparams_by_chainhash called!\n"); abort(); } -/* Generate std for chainparams_get_ln_port */ +/* Generated stub for chainparams_get_ln_port */ int chainparams_get_ln_port(const struct chainparams *params UNNEEDED) { fprintf(stderr, "chainparams_get_ln_port called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ From 06e1e119aa0633a9257dbfefe65a04082df488c0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Jul 2022 14:14:36 +0930 Subject: [PATCH 0947/1530] pytest: fix test_gossip_no_empty_announcements flake. This is a side-effect of fixing aging: sometimes, we age our rcvd_filter cache too fast, and thus re-xmit. This breaks our test, since it used dev-disconnect on the channel_announce, but that closes to l3, not l1! ``` > assert l1.rpc.listchannels()['channels'] == [] E AssertionError: assert [{'active': T...ags': 1, ...}] == [] E Left contains 2 more items, first extra item: {'active': True, 'amount_msat': 100000000msat, 'base_fee_millisatoshi': 1, 'channel_flags': 0, ...} ``` Signed-off-by: Rusty Russell Fixes: #5403 --- connectd/multiplex.c | 15 ++++++++++++++- tests/test_gossip.py | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 0db695990ba7..f9d41bcae3c6 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -344,8 +344,21 @@ static struct io_plan *encrypt_and_send(struct peer *peer, /* Kicks off write_to_peer() to look for more gossip to send from store */ static void wake_gossip(struct peer *peer) { + bool flush_gossip_filter = true; + +#if DEVELOPER + /* With dev-fast-gossip, we clean every 2 seconds, which is too + * fast for our slow tests! So we only call this one time in 5 + * actually twice that, as it's not per-peer! */ + static int gossip_age_count; + + if (peer->daemon->dev_fast_gossip && gossip_age_count++ % 5 != 0) + flush_gossip_filter = false; +#endif + /* Don't remember sent per-peer gossip forever. */ - gossip_rcvd_filter_age(peer->gs.grf); + if (flush_gossip_filter) + gossip_rcvd_filter_age(peer->gs.grf); peer->gs.active = IFDEV(!peer->daemon->dev_suppress_gossip, true); io_wake(peer->peer_outq); diff --git a/tests/test_gossip.py b/tests/test_gossip.py index eabfdb96e07c..0850f8a81c23 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -622,10 +622,10 @@ def test_gossip_no_empty_announcements(node_factory, bitcoind, chainparams): # l2 sends CHANNEL_ANNOUNCEMENT to l1, then disconnects/ l2.daemon.wait_for_log('dev_disconnect') l1.daemon.wait_for_log(r'\[IN\] 0100') + wait_for(lambda: l1.rpc.listchannels()['channels'] == []) # l1 won't relay it (make sure it has time to digest though) time.sleep(2) - assert l1.rpc.listchannels()['channels'] == [] encoded = subprocess.run(['devtools/mkencoded', '--scids', '00'], check=True, timeout=TIMEOUT, From f98df63b75e972bbfbea56023f9937745a00e613 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Jul 2022 17:00:26 +0930 Subject: [PATCH 0948/1530] Revert "pytest: fix test_gossip_no_backtalk flake." This reverts commit 6cc4858847189616774057b40671b01e1cf390ec. No longer needed now we don't flush gossip rcvd_filter as aggressively. --- tests/test_gossip.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 0850f8a81c23..2dacfb9db02d 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1766,7 +1766,7 @@ def test_gossip_announce_unknown_block(node_factory, bitcoind): sync_blockheight(bitcoind, [l1]) -@unittest.skipIf(DEVELOPER, "Developer gossip too fast!") +@pytest.mark.developer("gossip without DEVELOPER=1 is slow") def test_gossip_no_backtalk(node_factory): # l3 connects, gets gossip, but should *not* play it back. l1, l2, l3 = node_factory.get_nodes(3, @@ -1779,8 +1779,8 @@ def test_gossip_no_backtalk(node_factory): r'\[IN\] 0102', r'\[IN\] 0102', r'\[IN\] 0101', r'\[IN\] 0101']) - # Will flush every 60 seconds, so definitely should by this time! - time.sleep(90) + # With DEVELOPER, this is long enough for gossip flush. + time.sleep(2) assert not l3.daemon.is_in_log(r'\[OUT\] 0100') From 1d78911b29cddfcad7a887b2e178e67c6faf17e9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Jul 2022 14:17:22 +0930 Subject: [PATCH 0949/1530] pytest: test that we allow both msatoshi and amount_msat is route for sendpay. We deprecated msatoshi, but getroute() still returns both (unless --allow-deprecated-apis=False), so we need to accept both. Signed-off-by: Rusty Russell --- tests/test_pay.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_pay.py b/tests/test_pay.py index a3c21a4f3258..81f0a7c49ae0 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5313,3 +5313,24 @@ def test_pay_middle_fail(node_factory, bitcoind, executor): # And that will fail upstream with pytest.raises(RpcError, match=r'WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): l1.rpc.waitsendpay('00' * 32) + + +@pytest.mark.xfail(strict=True) +def test_sendpay_dual_amounts(node_factory): + """Test that handing *both* msatoshi and amount_msat to sendpay works""" + l1 = node_factory.get_node(options={'allow-deprecated-apis': True}) + + route = [{'amount_msat': 1011, + 'msatoshi': 1011, + 'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', + 'delay': 20, + 'channel': '1x1x1'}, + {'amount_msat': 1000, + 'msatoshi': 1000, + 'id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d', + 'delay': 10, + 'channel': '2x2x2'}] + l1.rpc.check("sendpay", route=route, payment_hash="00" * 32) + + with pytest.raises(RpcError, match=r'No connection to first peer found'): + l1.rpc.sendpay(route=route, payment_hash="00" * 32) From 1217f479dfcd149e3d0dbc9c0de25096c6d7fef4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 12 Jul 2022 14:17:35 +0930 Subject: [PATCH 0950/1530] sendpay: allow route to contain both amount_msat and (deprecated) msatoshi. Since it's a deprecation, we simply ignore one, rather than properly checking they match etc. Fixes: #5386 Signed-off-by: Rusty Russell --- common/param.c | 14 +++++++++++--- common/param.h | 15 +++++++++++++++ lightningd/pay.c | 3 ++- tests/test_pay.py | 1 - 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/common/param.c b/common/param.c index 34c6682c5023..c591baf4fd03 100644 --- a/common/param.c +++ b/common/param.c @@ -36,6 +36,12 @@ static bool param_add(struct param **params, return true; } +/* FIXME: To support the deprecated p_req_dup_ok */ +static bool is_required(enum param_style style) +{ + return style == PARAM_REQUIRED || style == PARAM_REQUIRED_ALLOW_DUPS; +} + static struct command_result *make_callback(struct command *cmd, struct param *def, const char *buffer, @@ -57,7 +63,7 @@ static struct command_result *post_check(struct command *cmd, struct param *last = first + tal_count(params); /* Make sure required params were provided. */ - while (first != last && first->style == PARAM_REQUIRED) { + while (first != last && is_required(first->style)) { if (!first->is_set) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "missing required parameter: %s", @@ -145,6 +151,8 @@ static struct command_result *parse_by_name(struct command *cmd, struct command_result *res; if (p->is_set) { + if (p->style == PARAM_REQUIRED_ALLOW_DUPS) + continue; return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "duplicate json names: %s", p->name); @@ -182,7 +190,7 @@ static int comp_by_arg(const struct param *a, const struct param *b, static int comp_req_order(const struct param *a, const struct param *b, void *unused) { - if (a->style != PARAM_REQUIRED && b->style == PARAM_REQUIRED) + if (!is_required(a->style) && is_required(b->style)) return 0; return 1; } @@ -249,7 +257,7 @@ static char *param_usage(const tal_t *ctx, int len = strcspn(params[i].name, "|"); if (i != 0) tal_append_fmt(&usage, " "); - if (params[i].style == PARAM_REQUIRED) + if (is_required(params[i].style)) tal_append_fmt(&usage, "%.*s", len, params[i].name); else tal_append_fmt(&usage, "[%.*s]", len, params[i].name); diff --git a/common/param.h b/common/param.h index a127d85cf069..6db4e80dd9e9 100644 --- a/common/param.h +++ b/common/param.h @@ -70,6 +70,7 @@ const char *param_subcommand(struct command *cmd, const char *buffer, enum param_style { PARAM_REQUIRED, + PARAM_REQUIRED_ALLOW_DUPS, PARAM_OPTIONAL, PARAM_OPTIONAL_WITH_DEFAULT, }; @@ -103,6 +104,20 @@ enum param_style { (const jsmntok_t *)NULL, \ (arg)) == (struct command_result *)NULL); }) +/* + * Add an required parameter, like p_req, but ignore duplicates. + */ +#define p_req_dup_ok(name, cbx, arg) \ + name"", \ + PARAM_REQUIRED_ALLOW_DUPS, \ + (param_cbx)(cbx), \ + ({ *arg = NULL; \ + (arg) + 0*sizeof((cbx)((struct command *)NULL, \ + (const char *)NULL, \ + (const char *)NULL, \ + (const jsmntok_t *)NULL, \ + (arg)) == (struct command_result *)NULL); }) + /* * Add an optional parameter. *arg is set to @def if it isn't found. * name can be | if it's been renamed. diff --git a/lightningd/pay.c b/lightningd/pay.c index bb09c07dc7b5..f13f3a16de36 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1375,7 +1375,8 @@ static struct command_result *param_route_hops(struct command *cmd, int *ignored; if (!param(cmd, buffer, t, - p_req("amount_msat|msatoshi", param_msat, &amount_msat), + /* deprecated: getroute gives both, so we allow both! */ + p_req_dup_ok("amount_msat|msatoshi", param_msat, &amount_msat), p_req("id", param_node_id, &id), p_req("delay", param_number, &delay), p_req("channel", param_short_channel_id, &channel), diff --git a/tests/test_pay.py b/tests/test_pay.py index 81f0a7c49ae0..686faa9dfd92 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5315,7 +5315,6 @@ def test_pay_middle_fail(node_factory, bitcoind, executor): l1.rpc.waitsendpay('00' * 32) -@pytest.mark.xfail(strict=True) def test_sendpay_dual_amounts(node_factory): """Test that handing *both* msatoshi and amount_msat to sendpay works""" l1 = node_factory.get_node(options={'allow-deprecated-apis': True}) From da85014fdf33e7a7561df4fba04f83cbe493ac0f Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sat, 25 Jun 2022 11:29:52 +0200 Subject: [PATCH 0951/1530] gossipd: nit: update a misleading comment This commit got reduced to just changing a comment because the stuff it initially did was already merged in before by commit 7ff62b4a So I just kept the changed comment as its more precise. Changelog-None --- gossipd/gossipd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 4a5f5b77d19c..e27f96920717 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -350,7 +350,7 @@ static void handle_remote_addr(struct daemon *daemon, const u8 *msg) if (!fromwire_gossipd_remote_addr(msg, &remote_addr)) master_badmsg(WIRE_GOSSIPD_REMOTE_ADDR, msg); - /* current best guess is that we use default port on public internet */ + /* Best guess is that we use default port for the selected network */ remote_addr.port = chainparams_get_ln_port(chainparams); switch (remote_addr.type) { From 301acc9556fcfe6bbeb2fd378731f7c6a18438bf Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Mon, 27 Jun 2022 16:10:41 +0200 Subject: [PATCH 0952/1530] gossipd: only use IP discovery if no addresses are announced This will only add the discovered `remote_addr` IPs if no other addresses would be announced. Meaning whenever a public address was found by autobind or an address was specified via commandline or config, IP discovery will be disabled. Addresses: #5305 Note from the author: We could/should also enable IP discovery when we only have a TOR address (but without --always-use-proxy ofc). This will give nodes an option to have a bootstrap way to be reached until IP discovery can do the job in a more stable way. Changelog-Changed: Only use IP discovery as fallback when no addresses would be announced --- gossipd/gossip_generation.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index dfb621beb0af..b309e4883a21 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -35,20 +35,24 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, u8 *addresses = tal_arr(tmpctx, u8, 0); u8 *announcement; struct tlv_node_ann_tlvs *na_tlv; - size_t i; + size_t i, count_announceable; /* add all announceable addresses */ + count_announceable = tal_count(daemon->announceable); was = tal_arr(tmpctx, struct wireaddr, 0); - for (i = 0; i < tal_count(daemon->announceable); i++) + for (i = 0; i < count_announceable; i++) tal_arr_expand(&was, daemon->announceable[i]); - /* add reported `remote_addr` v4 and v6 of our self */ - if (daemon->remote_addr_v4 != NULL && - !wireaddr_arr_contains(was, daemon->remote_addr_v4)) - tal_arr_expand(&was, *daemon->remote_addr_v4); - if (daemon->remote_addr_v6 != NULL && - !wireaddr_arr_contains(was, daemon->remote_addr_v6)) - tal_arr_expand(&was, *daemon->remote_addr_v6); + /* Add IP discovery `remote_addr` v4 and v6 of our self. */ + /* Only do that if we don't have addresses announced. */ + if (count_announceable == 0) { + if (daemon->remote_addr_v4 != NULL && + !wireaddr_arr_contains(was, daemon->remote_addr_v4)) + tal_arr_expand(&was, *daemon->remote_addr_v4); + if (daemon->remote_addr_v6 != NULL && + !wireaddr_arr_contains(was, daemon->remote_addr_v6)) + tal_arr_expand(&was, *daemon->remote_addr_v6); + } /* Sort by address type again, as we added dynamic remote_addr v4/v6. */ /* BOLT #7: From b3177e945fe535367683b2114c7d44b59069a05c Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Mon, 11 Jul 2022 11:40:22 +0200 Subject: [PATCH 0953/1530] doc: mention ip discovery only active when no addresses are announced Changelog-None --- doc/FAQ.md | 3 ++- doc/TOR.md | 3 ++- doc/lightningd-config.5.md | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index f5279cf3742f..3618a063999a 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -73,7 +73,8 @@ There is no risk to your channels if your IP address changes. Other nodes might not be able to connect to you, but your node can still connect to them. But Core Lightning also has an integrated IPv4/6 address discovery mechanism. If your node detects an new public address, it will update its announcement. -For this to work binhind a NAT router you need to forward the TCP port 9735 to your node. +For this to work binhind a NAT router you need to forward the default TCP port 9735 to your node. +IP discovery is only active if no other addresses are announced. Alternatively, you can [setup a TOR hidden service](TOR.md) for your node that will also work well behind NAT firewalls. diff --git a/doc/TOR.md b/doc/TOR.md index db09762c32e3..af5efb01653f 100644 --- a/doc/TOR.md +++ b/doc/TOR.md @@ -48,8 +48,9 @@ network between you and the Internet, as long as you can use Tor you can be connected to. Note: Core Lightning also support IPv4/6 address discovery behind NAT routers. -For this to work you need to forward the TCP port 9735 to your node. +For this to work you need to forward the default TCP port 9735 to your node. In this case you don't need TOR to punch through your firewall. +IP discovery is only active if no other addresses are announced. This usually has the benefit of quicker and more stable connections but does not offer additional privacy. diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 20c0d7b797ce..433c38018da5 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -380,7 +380,8 @@ as described below). Core Lightning also support IPv4/6 address discovery behind NAT routers. If your node detects an new public address, it will update its announcement. -For this to work you need to forward the TCP port 9735 to your node. +For this to work you need to forward the default TCP port 9735 to your node. +IP discovery is only active if no other addresses are announced. You can instead use *addr* to override this (eg. to change the port), or precisely control where to bind and what to announce with the From f67ab2a86e5d4543570925d9efafcb2b56a6c8d3 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Mon, 11 Jul 2022 16:14:51 +0200 Subject: [PATCH 0954/1530] test: run-gossmap update test store instructions Changelog-None --- common/test/run-gossmap_canned.c | 4 +++- common/test/run-gossmap_local.c | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/test/run-gossmap_canned.c b/common/test/run-gossmap_canned.c index 9e608b275aae..c9d083f8af90 100644 --- a/common/test/run-gossmap_canned.c +++ b/common/test/run-gossmap_canned.c @@ -27,7 +27,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U { fprintf(stderr, "towire_channel_id called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ -/* Canned gossmap, taken from od -tx1 -Anone -v < /tmp/ltests-rtchpzh1/test_peers_1/lightning-2/regtest/gossip_store | sed 's/ / 0x/g'| cut -c2- | sed -e 's/ /, /' -e 's/$/,/' */ +/* Canned gossmap, taken from tests/test_gossip.py + * $> od -tx1 -Anone -v < /tmp/ltests-rtchpzh1/test_peers_1/lightning-2/regtest/gossip_store | sed 's/ / 0x/g'| cut -c2- | sed -e 's/ /, /g' -e 's/$/,/' + */ static u8 canned_map[] = { 0x0a,0x00,0x00,0x01,0xbc,0x4a,0xe8,0x33,0xa0,0x00,0x00,0x00,0x00,0x10,0x08,0x00, 0x00,0x00,0x00,0x00,0x0f,0x42,0x40,0x01,0xb0,0x01,0x00,0x00,0x00,0x00,0x00,0x00, diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 84c97d5abf62..e98d1e452ac3 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -27,8 +27,8 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U { fprintf(stderr, "towire_channel_id called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ -/* Canned gossmap, taken from tests/test_gossip.py's - * setup_gossip_store_test via od -v -Anone -tx1 < /tmp/ltests-kaf30pn0/test_gossip_store_compact_noappend_1/lightning-2/regtest/gossip_store +/* Canned gossmap, taken from tests/test_gossip.py::test_gossip_store_compact_noappend + * $> od -v -Anone -tx1 < /tmp/ltests-kaf30pn0/test_gossip_store_compact_noappend_1/lightning-2/regtest/gossip_store | sed 's/ / 0x/g'| cut -c2- | sed -e 's/ /, /g' -e 's/$/,/' */ static u8 canned_map[] = { 0x0a, 0x80, 0x00, 0x01, 0xbc, 0x09, 0x8b, 0x67, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00 From 95ec897ac0709be12935ac522ff0ff414c8daf54 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 8 Jul 2022 17:07:06 -0500 Subject: [PATCH 0955/1530] dual-fund: Fail if you try to buy a liquidity ad w/o dualfunding on Fixes #5271 In-Collaboration-With: Base58 'n Coding Seminar Participants Changelog-Changed: `fundchannel` now errors if you try to buy a liquidity ad but dont' have `experimental-dual-fund` enabled --- plugins/spender/multifundchannel.c | 10 +++++++++- tests/test_opening.py | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 7cdfaf72f250..e9e5949ab5d8 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1611,13 +1611,21 @@ connect_ok(struct command *cmd, json_tok_full_len(features_tok), json_tok_full(buf, features_tok)); + dest->state = MULTIFUNDCHANNEL_CONNECTED; + /* Set the open protocol to use now */ if (feature_negotiated(plugin_feature_set(mfc->cmd->plugin), dest->their_features, OPT_DUAL_FUND)) dest->protocol = OPEN_CHANNEL; + else if (!amount_sat_zero(dest->request_amt) || !(!dest->rates)) + /* Return an error */ + fail_destination_msg(dest, FUNDING_V2_NOT_SUPPORTED, + "Tried to buy a liquidity ad" + " but we(?) don't have" + " experimental-dual-fund" + " enabled"); - dest->state = MULTIFUNDCHANNEL_CONNECTED; return connect_done(dest); } diff --git a/tests/test_opening.py b/tests/test_opening.py index 58cfadde259f..3a454d50e95b 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1391,3 +1391,22 @@ def test_zeroconf_forward(node_factory, bitcoind): wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4) inv = l1.rpc.invoice(42, 'back1', 'desc')['bolt11'] l3.rpc.pay(inv) + + +@pytest.mark.openchannel('v1') +def test_buy_liquidity_ad_no_v2(node_factory, bitcoind): + """ Test that you can't actually request amt for a + node that doesn' support v2 opens """ + + l1, l2, = node_factory.get_nodes(2) + amount = 500000 + feerate = 2000 + + l1.fundwallet(amount * 100) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + # l1 leases a channel from l2 + with pytest.raises(RpcError, match=r"Tried to buy a liquidity ad but we[(][?][)] don't have experimental-dual-fund enabled"): + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease='029a002d000000004b2003e8') From 27e5ddc7b766cc8dad607d01c9c1de1fb22ebf45 Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Tue, 12 Jul 2022 17:31:43 -0500 Subject: [PATCH 0956/1530] gossipd: fix of gossip v10 channel removal handling Node announcement indices should be reinitialized after removing the referenced announcement. Debugging output also corrected. Changelog-None --- gossipd/routing.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gossipd/routing.c b/gossipd/routing.c index b79b3e3d80b1..6bdc0020d64f 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -501,6 +501,8 @@ static void remove_chan_from_node(struct routing_state *rstate, gossip_store_delete(rstate->gs, &node->bcast, WIRE_NODE_ANNOUNCEMENT); + node->rgraph.index = node->bcast.index = 0; + node->rgraph.timestamp = node->bcast.timestamp = 0; } else if (node_announce_predates_channels(node)) { /* node announcement predates all channel announcements? * Move to end (we could, in theory, move to just past next @@ -1377,7 +1379,7 @@ bool routing_add_channel_update(struct routing_state *rstate, } else if (hc->bcast.index != hc->rgraph.index){ status_broken("gossip_store rate-limited " "channel_update %u replaces %u!", - index, hc->bcast.index); + index, hc->rgraph.index); return false; } } @@ -1730,7 +1732,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, } else if (node->bcast.index != node->rgraph.index){ status_broken("gossip_store rate-limited " "node_announcement %u replaces %u!", - index, node->bcast.index); + index, node->rgraph.index); return false; } } From ba7d4a8f6bab5a3d5e5832d1f9e36749e695320a Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 13 Jul 2022 10:45:39 -0500 Subject: [PATCH 0957/1530] make-schema: don't include tools/fromschema.py in SHASUMS We were including the entire list of prerequisites when generating a shastamp, which for schemas includes the `tools/fromschema.py` doc. This meant all of our shasums were updating anytime this tool file changed. Instead, we just include the first prerequisite. See: https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html#Automatic-Variables --- Makefile | 10 ++++++++-- doc/lightning-addgossip.7.md | 2 +- doc/lightning-autocleaninvoice.7.md | 2 +- doc/lightning-check.7.md | 2 +- doc/lightning-checkmessage.7.md | 2 +- doc/lightning-close.7.md | 2 +- doc/lightning-connect.7.md | 4 ++-- doc/lightning-createinvoice.7.md | 2 +- doc/lightning-createonion.7.md | 2 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-decode.7.md | 2 +- doc/lightning-decodepay.7.md | 2 +- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-delexpiredinvoice.7.md | 2 +- doc/lightning-delinvoice.7.md | 2 +- doc/lightning-delpay.7.md | 2 +- doc/lightning-disableoffer.7.md | 2 +- doc/lightning-disconnect.7.md | 2 +- doc/lightning-feerates.7.md | 2 +- doc/lightning-fetchinvoice.7.md | 2 +- doc/lightning-fundchannel.7.md | 2 +- doc/lightning-fundchannel_cancel.7.md | 2 +- doc/lightning-fundchannel_complete.7.md | 2 +- doc/lightning-fundchannel_start.7.md | 2 +- doc/lightning-funderupdate.7.md | 2 +- doc/lightning-fundpsbt.7.md | 2 +- doc/lightning-getinfo.7.md | 2 +- doc/lightning-getlog.7.md | 2 +- doc/lightning-getroute.7.md | 2 +- doc/lightning-getsharedsecret.7.md | 2 +- doc/lightning-help.7.md | 2 +- doc/lightning-invoice.7.md | 2 +- doc/lightning-keysend.7.md | 2 +- doc/lightning-listchannels.7.md | 4 ++-- doc/lightning-listconfigs.7 | 4 +--- doc/lightning-listconfigs.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listforwards.7.md | 2 +- doc/lightning-listfunds.7.md | 2 +- doc/lightning-listinvoices.7.md | 2 +- doc/lightning-listnodes.7.md | 4 ++-- doc/lightning-listoffers.7.md | 2 +- doc/lightning-listpays.7.md | 2 +- doc/lightning-listpeers.7.md | 2 +- doc/lightning-listsendpays.7.md | 2 +- doc/lightning-listtransactions.7.md | 2 +- doc/lightning-multifundchannel.7.md | 2 +- doc/lightning-multiwithdraw.7.md | 2 +- doc/lightning-newaddr.7 | 3 +-- doc/lightning-newaddr.7.md | 2 +- doc/lightning-notifications.7.md | 2 +- doc/lightning-offer.7.md | 2 +- doc/lightning-offerout.7.md | 2 +- doc/lightning-openchannel_abort.7.md | 2 +- doc/lightning-openchannel_bump.7.md | 2 +- doc/lightning-openchannel_init.7.md | 2 +- doc/lightning-openchannel_signed.7.md | 2 +- doc/lightning-openchannel_update.7.md | 2 +- doc/lightning-parsefeerate.7.md | 2 +- doc/lightning-pay.7.md | 2 +- doc/lightning-ping.7.md | 2 +- doc/lightning-plugin.7.md | 2 +- doc/lightning-reserveinputs.7.md | 2 +- doc/lightning-sendcustommsg.7.md | 2 +- doc/lightning-sendinvoice.7.md | 2 +- doc/lightning-sendonion.7.md | 2 +- doc/lightning-sendonionmessage.7.md | 2 +- doc/lightning-sendpay.7.md | 2 +- doc/lightning-sendpsbt.7.md | 2 +- doc/lightning-setchannel.7.md | 2 +- doc/lightning-setchannelfee.7.md | 2 +- doc/lightning-signmessage.7.md | 2 +- doc/lightning-signpsbt.7.md | 2 +- doc/lightning-stop.7.md | 2 +- doc/lightning-txdiscard.7.md | 2 +- doc/lightning-txprepare.7.md | 2 +- doc/lightning-txsend.7.md | 2 +- doc/lightning-unreserveinputs.7.md | 2 +- doc/lightning-utxopsbt.7.md | 2 +- doc/lightning-waitanyinvoice.7.md | 2 +- doc/lightning-waitblockheight.7.md | 2 +- doc/lightning-waitinvoice.7.md | 2 +- doc/lightning-waitsendpay.7.md | 2 +- doc/lightning-withdraw.7.md | 2 +- wallet/Makefile | 4 ++-- 85 files changed, 96 insertions(+), 93 deletions(-) diff --git a/Makefile b/Makefile index f2d2c1038900..3dde35d9cda0 100644 --- a/Makefile +++ b/Makefile @@ -308,13 +308,19 @@ endif ifeq ($(SUPPRESS_GENERATION),1) SHA256STAMP_CHANGED = false SHA256STAMP = exit 1 +SHA256STAMP_CHANGED_ALL = false +SHA256STAMP_ALL = exit 1 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:\([a-f0-9]*\).*/\1/p' $@ 2>/dev/null`" != x"`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)" >> $@ +SHA256STAMP = echo "$(1) SHA256STAMP:"`cat $(sort $(filter-out FORCE,$<)) | $(SHA256SUM) | cut -c1-64`"$(2)" >> $@ + +SHA256STAMP_CHANGED_ALL = [ 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_ALL = echo "$(1) SHA256STAMP:"`cat $(sort $(filter-out FORCE,$^)) | $(SHA256SUM) | cut -c1-64`"$(2)" >> $@ endif # generate-wire.py --page [header|impl] hdrfilename wirename < csv > file diff --git a/doc/lightning-addgossip.7.md b/doc/lightning-addgossip.7.md index fba520c5a5f9..a23abf111ce4 100644 --- a/doc/lightning-addgossip.7.md +++ b/doc/lightning-addgossip.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1a64fbaed63ffee21df3d46956a6dca193982b1b135a9b095e68652a720c77ac) +[comment]: # ( SHA256STAMP:c801b02463804504c2387387a36a6739351330a2c496aaa10de2b1f49c36ed32) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index 9cb826748fe6..8dffb14781d9 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -50,4 +50,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4506a00326dbfa7d44cbf891ad31cbfa66351d852aa0c58735bae03d32938edb) +[comment]: # ( SHA256STAMP:c5aedf100597dd0bd1bbbdf82964327035cd49df00d7b0aef6454e3b1ef39dbc) diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index 500e2aa43692..243160cf8f68 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -40,4 +40,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:10d986d91af6315ee755d119cb1b77f306e2360105191116282f3faead350ce8) +[comment]: # ( SHA256STAMP:c676fc0dbfdabc729b3fdbee7e64c91f8180100c9945db06cbaed955a3d63cfc) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index 9ccf3c13e23a..010caba3db46 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -50,4 +50,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6df0e61e28118786861aacc073e3289268fe1b00837c3fd02a537aa13e5acae5) +[comment]: # ( SHA256STAMP:7dcca1fd1708d93b4a0c9b83955630fc4f551c4ffd452fb866c624c72aeaa44d) diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index b49e59f56832..97ded08c8e02 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -133,4 +133,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3540adff8d75123f90598a2c0657924c0d5a53aa26716980f9a59879fcfb1f6b) +[comment]: # ( SHA256STAMP:3b645a2105f2b428889d097283cb56ba9c8d6cba90343ac779f2fb6e26a1c202) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index ff4cfcb73b3a..afd919a43ac2 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -50,7 +50,7 @@ RETURN VALUE [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 in init message (globalfeatures and features combined) +- **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") @@ -97,4 +97,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:540ce22f5d912b59732b8b2659e4a950d1344eb926901e26476a246d9eb473b8) +[comment]: # ( SHA256STAMP:ad52e3b3042a8910c106e0730d9d54d09ebdd3cffdb6ba3fd776f8ec4be57e46) diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index ba27e08b6878..f960b936f193 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -74,4 +74,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b07a315a6460449b64ab848ce9fc4cfe6ac862d63c8ab922434238a2b83d7902) +[comment]: # ( SHA256STAMP:34ba2cc01db3e516b257dbe6a47cf764d1e34b85ee89fd4b49aed1fc2e0bb0ba) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 484a7cad0631..38184588635b 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -132,4 +132,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:eabebca3c38ac8e01fb9a0891bde7913ce2832b7ee179c0b4617d104a0e6c009) +[comment]: # ( SHA256STAMP:d313a15317ee4b09d151312071fb6aa2f7fc16128ca4485096783347bffdeca2) diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index 18683e36ceeb..95e7df0e0701 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -65,4 +65,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:89e1f4926dd83df233b92aae626de776ec3bb2d29887ec29e8cf479ee2a16b85) +[comment]: # ( SHA256STAMP:ce6a72f08dd7f6026eab58bb6ea732aead4512e714fab4f9a1cf54cdb1374f59) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index de4dc749d058..7c916562b30f 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -181,4 +181,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6ccf1b9195e64f897f65198a81c47bbd7e16387e4bdc74d624e2ec04a24e9873) +[comment]: # ( SHA256STAMP:bc3778965137591623ce08ff51adf411bc42e6d1a4200692961b69962da39be7) diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index 6990389855c5..336508033225 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -70,4 +70,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a606b0d0594d48c619a8d34c7c0d0996982db10f1423841f269e89004a19fa98) +[comment]: # ( SHA256STAMP:e6e27ff57a8e26db5d4bba8be2a5faa6f1c58b20ef625f371e6e66d17932f8a0) diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index 0c75bd165d16..d35ccf971751 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -48,4 +48,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ac7468cf6eadc8ab85216b4d5ecb55a32f3d0bc84180f477151c3748901824de) +[comment]: # ( SHA256STAMP:83a7e89ed44c6dbf744d1327337e29393f9095628bd95fffe59ec5014ee0a483) diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index 45cf12cbcede..67b15092a3a2 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -38,4 +38,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:20cca78dbc3681427e1d536ba2f81e0bc05e2b5209edf884137f2ad25e642e84) +[comment]: # ( SHA256STAMP:c71baf4b5863fd6f4d2ba21a97d4106195ba10c5add21087142b1a5ee533da91) diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 0ee5773e2195..7e5ca099a9f8 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -78,4 +78,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:407d741b622621da127dc1e55c98c299ace970f8244bc14574dddcafbf9aa1fc) +[comment]: # ( SHA256STAMP:a24e80716475dea15f9762cf50382f21e3e09b803a669e217923d7019cd526e0) diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index a5c32f37f68a..3a4f9a05d3b9 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -101,4 +101,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:af8299cd87efe8254969069851d99bffffa033013f4a8b9fc94cdab6cfa0ff78) +[comment]: # ( SHA256STAMP:ecdf9fe432142054328abb6aa3f76b726968dd28db1fc26875a81f291e10135a) diff --git a/doc/lightning-disableoffer.7.md b/doc/lightning-disableoffer.7.md index 652d4fdd2694..0d0702e2a8b5 100644 --- a/doc/lightning-disableoffer.7.md +++ b/doc/lightning-disableoffer.7.md @@ -74,4 +74,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a7dbc87d991d1040283b5fbfe732fb9bc7c81efad3aa8b5bfb11ffe59ed3f069) +[comment]: # ( SHA256STAMP:d91f424d5374fd26d4d85df10f9e5eb092e5b0e1bac8dae44b98d844a55b6e22) diff --git a/doc/lightning-disconnect.7.md b/doc/lightning-disconnect.7.md index d1ec7f708e43..8f3fe0e163cd 100644 --- a/doc/lightning-disconnect.7.md +++ b/doc/lightning-disconnect.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1a64fbaed63ffee21df3d46956a6dca193982b1b135a9b095e68652a720c77ac) +[comment]: # ( SHA256STAMP:c801b02463804504c2387387a36a6739351330a2c496aaa10de2b1f49c36ed32) diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index f4669205e81a..345e1fe1ab5b 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -119,4 +119,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8fe321fcba7b3a471f4f83f98638dbc820fc0abe91f3d53ca55fdb0222e17a8d) +[comment]: # ( SHA256STAMP:96fde8cf67c9b0dda5a1866dfadfb1d7da7e593b3080948662070382d4a9537f) diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index a0d5235e901a..d0bc02d6502a 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -88,4 +88,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:fa4e9733716ba263e8288613633ec7e0e466c13fc8067b11588bfd3c88901672) +[comment]: # ( SHA256STAMP:83fcb7141eefc0e5d5bd1d23f6d05f1d32514bad53ed52fc61604ce049c75d54) diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index fe44b2a21f8e..420892526988 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -114,4 +114,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4bde4785d98f4fdb74ea2415b89ddc864fdf09bd9a30f8056a376aaa668a84b8) +[comment]: # ( SHA256STAMP:d396512cad4bfd533dda947a12aee0aecda05e39c2a7ad7e6b04a3602fbae85d) diff --git a/doc/lightning-fundchannel_cancel.7.md b/doc/lightning-fundchannel_cancel.7.md index 55133d9109b8..fbc49b7054b5 100644 --- a/doc/lightning-fundchannel_cancel.7.md +++ b/doc/lightning-fundchannel_cancel.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d433fc29ad064a09c92160038973c7161bb56947166a2701c0d5d278e276917c) +[comment]: # ( SHA256STAMP:a670eb1f4475ad33b3fc20994205adbf12fad8bb93e5e805e0b8a8ea1db15136) diff --git a/doc/lightning-fundchannel_complete.7.md b/doc/lightning-fundchannel_complete.7.md index e1690fe2074e..5a71e2490af5 100644 --- a/doc/lightning-fundchannel_complete.7.md +++ b/doc/lightning-fundchannel_complete.7.md @@ -61,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6fad848d20b3e0a6085790085b0aa91d24cc33e4b0127fc40521ce9c102182d1) +[comment]: # ( SHA256STAMP:f7b14faee0218a2eee7c0df17c52b1d4502c898d1767644bd891027116eb8868) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index c0d22abcdf37..490650442fb8 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -83,4 +83,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6dc3dd4d3d65baa617b285fd3f6e746d62e2806635a8f8852fd3a94ef6fe3e6a) +[comment]: # ( SHA256STAMP:5062025dc172a552f1a33fd1e3600c0fbfc1405f030ea049da88da30a5035456) diff --git a/doc/lightning-funderupdate.7.md b/doc/lightning-funderupdate.7.md index 700ecb9bfc98..2e8999cfc891 100644 --- a/doc/lightning-funderupdate.7.md +++ b/doc/lightning-funderupdate.7.md @@ -146,4 +146,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:01be8ecebe9025991de323bde9bc41591a9cde1b106fa01fc328451d31eb9a70) +[comment]: # ( SHA256STAMP:7fb486ed4c0ec0e72c51bc5a167a1280a9572c61c9467b5819e6624a54877cfb) diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 177d26f21ea7..0b138e9fc852 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -114,4 +114,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:635cc321d158bcdb3ca24f13c9955cc9b867e34247c1cf3d91edbbe21eb06a48) +[comment]: # ( SHA256STAMP:0cd239969d70c9261f0d7309762e3d2e646ddd0c62dc7dd8b08515c03cc5385b) diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 8330ee0052b1..234e9da8641f 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -128,4 +128,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:52175b0eced3ed2992d27b983b50afe080134702874116ce0c8355a8adddd440) +[comment]: # ( SHA256STAMP:aedad2e6949f96d8d331e39c17effe5786816729562987b058d146b4b94286cb) diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index 8007f70ae2a0..5b625f04ec74 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -90,4 +90,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:963fc75819ffb63271f25e31f30a6fe54457803d8bf296556589f76874fb39c0) +[comment]: # ( SHA256STAMP:46482f3cc6b332b284cae1b1e5477b19e227cb6c1cd1086452d28f0887433cb1) diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index 7a6ec76c831f..1e7fb5d48b3b 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -309,4 +309,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3494cb4003abfe32e8942ec5d92d0c464815d5e65edf29087cd2193eb414d694) +[comment]: # ( SHA256STAMP:db8834d9a318688d2c4801b85f06aa5afb3120e064b3654433469bca09e776a0) diff --git a/doc/lightning-getsharedsecret.7.md b/doc/lightning-getsharedsecret.7.md index 72b24eedc6db..505986a6e957 100644 --- a/doc/lightning-getsharedsecret.7.md +++ b/doc/lightning-getsharedsecret.7.md @@ -91,4 +91,4 @@ RESOURCES * Main web site: -[comment]: # ( SHA256STAMP:e54898c6b950be6242a641212b71b6ce33ea31068f3572cd42be5d2b87365eb7) +[comment]: # ( SHA256STAMP:47c5b466b02b80d40937f40c725733f72c20f3bc26cc7f4dacd5ef858ab0282b) diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index 061e97771d1a..5c5dbc2e36e5 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -67,4 +67,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:262fdbfc6ea8142c57637e5c48a9536b0e8577ef823ebfc830cc0c14d56fb08d) +[comment]: # ( SHA256STAMP:881f00fb7943bc1655c054488643a4da37742b6705924f0ba33366e8cf3f2b93) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index a0da46b86389..c08f2dfec3e5 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -117,4 +117,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:834b9d7b84845d423a677662b66b9109b3b8c4b7219a91d98f2817561d68a8cd) +[comment]: # ( SHA256STAMP:ea76df1915a45de45039cbbe8add3fe86416f7cba133d8f0d364d28ef276198c) diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index da48b95f89a9..515998e9b8c2 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -114,4 +114,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:449315de94f49452c3d485775dd97bc2517763be420ebc74024914680359d1ff) +[comment]: # ( SHA256STAMP:f8e12220302756c5a95eef2ec428c1d7adaba3025b0716e6b6581f783d92b648) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index 54286a9b365c..fff0edda2972 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -45,7 +45,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **fee_per_millionth** (u32): Proportional fee changed by *source* to use this channel, in parts-per-million - **delay** (u32): The number of blocks delay required by *source* to use this channel - **htlc_minimum_msat** (msat): The smallest payment *source* will allow via this channel -- **features** (hex): BOLT #9 features bitmap for this channel in channel_announcement message +- **features** (hex): BOLT #9 features bitmap for this channel - **htlc_maximum_msat** (msat, optional): The largest payment *source* will allow via this channel [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -78,4 +78,4 @@ Lightning RFC site - BOLT \#7: -[comment]: # ( SHA256STAMP:e27d51a95411739ee10b082beaca55e33de4b2177b0e39df2223700c3141bc02) +[comment]: # ( SHA256STAMP:43c6c45c0672482610c1bdd607d5bf6ed26d6c42cfc4fbde4a5d24f2eb1d9a2d) diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index 7ab2c1168bb7..2d5e89bbd00c 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -33,7 +33,6 @@ showing default values (\fBdev-\fR options are not shown)\. On success, an object is returned, containing: - .RS .IP \[bu] \fB# version\fR (string, optional): Special field indicating the current version @@ -174,7 +173,6 @@ On success, an object is returned, containing: On failure, one of the following error codes may be returned: - .RS .IP \[bu] -32602: Error in given parameters or field with \fIconfig\fR name doesn't exist\. @@ -286,4 +284,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:67e5f100671ed8ef0e490caa46d57dc73e7443caa86a85f32806a0e8183cd1b8 +\" SHA256STAMP:7ac9ef3477f64fd3a4181cb27b8810ecf2c1a082688180716adfe79843ab09aa diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index f304e2057b33..f42a96a500b9 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -212,4 +212,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:ebc37d1f9cb452d312285a8168d2bb6da2d1dba08db56bbb8d3d7f47b58d7fa4) +[comment]: # ( SHA256STAMP:4645e3f14c53ccc23b2ed42c2886064e5d718a8eef5e6bd6dd3a932cadfd8e8f) diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index 5b0ecb0171e5..b3e65602f234 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -46,4 +46,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1e5d31c36f5aa2d2cb6bedb07a94b18880ba95529885c104b177d91bf251d420) +[comment]: # ( SHA256STAMP:6c6f4c08a88b2a8c8cdc4c36783da86d8e8de0d5ee39261f3304e0eb41f0eb41) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 0af6cf491488..8a2ae806053d 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c8adfa0a6dcae939c5da919d7996261db8663a871210e33b426c3b40c30d7b29) +[comment]: # ( SHA256STAMP:de237318dfea0b02d6ca34710432a3b739012beb84f74e41e720cd9889675954) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index 1ce69088b37b..12d060c9e22a 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -68,4 +68,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:fdc550a0ff11f6fbbf51c29340c0494077d831566ae7ab008cce4b93034a76c5) +[comment]: # ( SHA256STAMP:61be4b00c0485edccb986e1f066cf18a5c9fdb8bca94adc12f0a87a36041a93a) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index 0524f5904352..52ab2d14fc8f 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -56,4 +56,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1178a5178a20b2aca9c52fe39e332bd1d6c4f20fa302639bed0fbfc6eb348c40) +[comment]: # ( SHA256STAMP:7e45fcb50a446f35e441df4a6c04626a045d237407231bde044c95aabc689519) diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index 314530684752..7a36dd71a55d 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -34,7 +34,7 @@ On success, an object containing **nodes** is returned. It is an array of objec If **last_timestamp** is present: - **alias** (string): The fun alias this node advertized (up to 32 characters) - **color** (hex): The favorite RGB color this node advertized (always 6 characters) - - **features** (hex): BOLT #9 features bitmap this node advertized in node_announcement message + - **features** (hex): BOLT #9 features bitmap this node advertized - **addresses** (array of objects): The addresses this node advertized: - **type** (string): Type of connection (one of "dns", "ipv4", "ipv6", "torv2", "torv3", "websocket") - **port** (u16): port number @@ -95,4 +95,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:85400c9c1741943e2e02935b4f14fd187a7db6056410e42adec07ef3c6772f5f) +[comment]: # ( SHA256STAMP:df4e056ac91672041b38811329eb7555636c7b4d4985456894255a7b8e11bf54) diff --git a/doc/lightning-listoffers.7.md b/doc/lightning-listoffers.7.md index 7a5b76965cc9..b684d749ec9a 100644 --- a/doc/lightning-listoffers.7.md +++ b/doc/lightning-listoffers.7.md @@ -80,4 +80,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:5cae5e0e423e66b02602ecc433de9686b16630979e794944059c65a100f54f9e) +[comment]: # ( SHA256STAMP:aad235e8255da495032f638a7066b05d651e2c99b668f7b3afffb21d908c788a) diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index 8f098ec98273..f8c44bfb85f8 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -57,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3c158259410ff8eb81669e26eca9ee53017002d739f89e7f0e2fd8e61edb8a14) +[comment]: # ( SHA256STAMP:ec7234aa1ec9979cdc71a5bfe861296ba90efdf30236922a822b2ecab6fd9635) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 170db4166712..1a5961802349 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -384,4 +384,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:fcfc465cbbe95430f4fc6473099142c576883e333ef9fe31d04372f411e49f6d) +[comment]: # ( SHA256STAMP:c26d3094925387ee935efc6351400bca374c361e5956ff13f10b228773f8e389) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index c2511d09f583..174ebde0d294 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -61,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:eaa0b4c6309d45bc2a72baf44288f1faa75d7f6ff2e8bf6d03be53747fe82c84) +[comment]: # ( SHA256STAMP:fa2e28ae1fdc4df5357ecc12984b2ec478cd591868484613dccf455cbc502fd9) diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index ed219f631076..f90eb7c04380 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -104,4 +104,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:74408f25dcf548f1389ab41123748297ba3116ad75afb41b86ecca453b95fec8) +[comment]: # ( SHA256STAMP:4d5d2f1cea0668b3e58e73a93fe6d217ac4a8c740bed09fcdce21c9e72daae99) diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index bd492f8920ce..a9e75bb6283e 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -158,4 +158,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a6358ad8d361ae4104c727e6b8ab342923a613b78d5f13552794f827a1125e8b) +[comment]: # ( SHA256STAMP:49c7f69eb4b532802c465ac5d0421cdad176f6a28c4c8e4c1c3ee0a94faad5bf) diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index 4242f4c0aeef..2e39c8876df2 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -71,4 +71,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:044cdcd69e6ece931b6d0f9b25dd842fd456ee479725e610c03694210256583f) +[comment]: # ( SHA256STAMP:1fe5147036f714c8f9185dd482a1bfa3e3ac5c4d0b6603fba1bc2b78de591b8f) diff --git a/doc/lightning-newaddr.7 b/doc/lightning-newaddr.7 index 56d9bc736919..2d76029e774b 100644 --- a/doc/lightning-newaddr.7 +++ b/doc/lightning-newaddr.7 @@ -32,7 +32,6 @@ To send an on-chain payment \fIfrom\fR the Core Lightning node wallet, use \fBwi On success, an object is returned, containing: - .RS .IP \[bu] \fBbech32\fR (string, optional): The bech32 (native segwit) address @@ -57,4 +56,4 @@ Felix \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:51dfdd41da7c5ff7dc82c1782f3f8f8448a99f9d0d243fb57443cf1b2d929372 +\" SHA256STAMP:62fcbc384a244851f34f9d30a7e5ae79c767fc6bca96b818ec8e11619afc397d diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index 55e8235b4eac..80865b8ddfe6 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -57,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1a7b5336bdb0dbc93c9e160bb36c20c0d0d3fb908bdd85a84499fbc99680f3a6) +[comment]: # ( SHA256STAMP:550089858649865ed4d23384dcc5deeef314f5a1976a9610e611dbe17c1063d6) diff --git a/doc/lightning-notifications.7.md b/doc/lightning-notifications.7.md index 5667fb773bc2..0bf4ddcbf03f 100644 --- a/doc/lightning-notifications.7.md +++ b/doc/lightning-notifications.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:1a64fbaed63ffee21df3d46956a6dca193982b1b135a9b095e68652a720c77ac) +[comment]: # ( SHA256STAMP:c801b02463804504c2387387a36a6739351330a2c496aaa10de2b1f49c36ed32) diff --git a/doc/lightning-offer.7.md b/doc/lightning-offer.7.md index 22b903efbbf3..5bc01dfa858a 100644 --- a/doc/lightning-offer.7.md +++ b/doc/lightning-offer.7.md @@ -134,4 +134,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4bbcec9c30f77239db780945965ad5cccf702365c3e592921fac57ed6bfd080f) +[comment]: # ( SHA256STAMP:3b7b337e724de4cd867dbbf65700a20d92db728892394f4d0229af70659fd7e2) diff --git a/doc/lightning-offerout.7.md b/doc/lightning-offerout.7.md index e74589c02193..f96d19149508 100644 --- a/doc/lightning-offerout.7.md +++ b/doc/lightning-offerout.7.md @@ -99,4 +99,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:fb60c3239f3d47b421f842304263ec73f864a307b77e39265653c3e85880a483) +[comment]: # ( SHA256STAMP:4ef2ac36e19f11e81645fb0b94fe7f4d7dad74faf7c77628e29967d9f1192154) diff --git a/doc/lightning-openchannel_abort.7.md b/doc/lightning-openchannel_abort.7.md index 77748c6af79d..f05fba23f54e 100644 --- a/doc/lightning-openchannel_abort.7.md +++ b/doc/lightning-openchannel_abort.7.md @@ -54,4 +54,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:78d6fbc1044e3a499ca618ea71845aa04043f46c169f4f50763644c7a3e35572) +[comment]: # ( SHA256STAMP:014c926a7202b951a2407b1a9996c90d4dd68aadfdbdb04311b280c016d538dc) diff --git a/doc/lightning-openchannel_bump.7.md b/doc/lightning-openchannel_bump.7.md index a863eb0fcf31..66028426b0c3 100644 --- a/doc/lightning-openchannel_bump.7.md +++ b/doc/lightning-openchannel_bump.7.md @@ -80,4 +80,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:0b3c4fc19cdad9162b91585c4af2dc5293ecd8925628d10b612cd777dcdedeea) +[comment]: # ( SHA256STAMP:2846dc5350c8a1ac5d0e33fc52a37f5e535e6db5b4080ae2be9c3df428ccf122) diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index e67a14150800..d8df70e08b97 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:bd405699ff27104ccc97dec81be9de1e7459c91333d78616268e4e9c198ee5af) +[comment]: # ( SHA256STAMP:70ebfff5ce81962cf574b39cb3d95206b1f1541d24b8a3a0380b6f0b594d0ca4) diff --git a/doc/lightning-openchannel_signed.7.md b/doc/lightning-openchannel_signed.7.md index 51dec370a7bf..72972f6eda48 100644 --- a/doc/lightning-openchannel_signed.7.md +++ b/doc/lightning-openchannel_signed.7.md @@ -66,4 +66,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:d85297e1ab0b3bbf206082c479dcfdc6469461e531321ce5576c6ff7f296d481) +[comment]: # ( SHA256STAMP:304d819b092e1d9ce7b4cd8ef08ecdd7952b454fed59e908ab1af02b50e9a0b0) diff --git a/doc/lightning-openchannel_update.7.md b/doc/lightning-openchannel_update.7.md index 55618e278c32..aa01a08a8147 100644 --- a/doc/lightning-openchannel_update.7.md +++ b/doc/lightning-openchannel_update.7.md @@ -71,4 +71,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:22ff9536e97ea194d9d9ba10a4f3244a0818a1605502b7ed25241a3a97f041d1) +[comment]: # ( SHA256STAMP:1a29b970038901773c6712c5e9267ae2c0ed3e9d4b11543d287c272a031003c1) diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md index d46d7f22c4b9..b521e8cdc1e1 100644 --- a/doc/lightning-parsefeerate.7.md +++ b/doc/lightning-parsefeerate.7.md @@ -43,4 +43,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:045cab2977c7fcc12ed5267d4007a704028a00bc07f90265ed1cd7a46a414e63) +[comment]: # ( SHA256STAMP:f3498aa6f16d5be0a49896ae67144558df1baa223ac396a90ddf2d89a42ff395) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index 223bfb086f75..8301fbb602ff 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -165,4 +165,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b8ff255c1f4d284535d70f13d3bd85c3d20ef8fdee081835a57fb04528311db6) +[comment]: # ( SHA256STAMP:aa9dad9f5f97e1ad3a1a79c923dd57a685d7fada1545987f8ca8021ed92aa4eb) diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index 0088748d9024..d77129a4a5ef 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -69,4 +69,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a78a1d58cfe1fbf654ee58aad20ffcaa075f63bd8774c64be5ec28857e95ae3b) +[comment]: # ( SHA256STAMP:59da52931ba9cb4c192ab81973efed2246baa9a97aded29e2c4cf280972c9a9a) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index a79cb4d7b92b..82177d676207 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -81,4 +81,4 @@ RESOURCES Main web site: [writing plugins]: PLUGINS.md -[comment]: # ( SHA256STAMP:5c3b25ecb137bfe7a81f80f0b4183a9e1282530ebfac3b1bc05fc5406f59cfc7) +[comment]: # ( SHA256STAMP:4ed30fb62c37111ea06b55d1252727c9c56fabd40e9e1f50083c99de74b8dfa1) diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index 2cdc30649b10..c8562d838158 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -63,4 +63,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a675e16a820eca4da07743ace010deaa12aa51d2c3d73d4db6b32ffb8ee65f7a) +[comment]: # ( SHA256STAMP:45c13e920465d313c693db6bfea8ac2f66d1e40226ef78241f82f51df9b715b2) diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index 5d3b8492046a..601e91e43cfa 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -68,4 +68,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f07e2df415b1ec2ad7b19acdd2909616d7aa54bb3c5ae69359d4c8b87ee839bf) +[comment]: # ( SHA256STAMP:29749d2978455477670d15627d51a6c1e9493136431539640a81e85ab7104d4e) diff --git a/doc/lightning-sendinvoice.7.md b/doc/lightning-sendinvoice.7.md index 5fc675ed1f6b..8f3e262b1465 100644 --- a/doc/lightning-sendinvoice.7.md +++ b/doc/lightning-sendinvoice.7.md @@ -78,4 +78,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b4cf6c380589a566c695e96f3668294b4411a57c7724aa68b293bac9e2194462) +[comment]: # ( SHA256STAMP:d227800f16140429a3352bc28f023df8d03c93a54012efcdfdd1f307511c4356) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index 72aec8dfbd78..321a4ad4a339 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -128,4 +128,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:d2f4991d271147dd8c13859376f36f2d8843a96034e818a434555a09bf6fd003) +[comment]: # ( SHA256STAMP:4e329c9768eb9d53d3b1068b18c7eaad46f75fa1a57724be4cbb64dc3c324a04) diff --git a/doc/lightning-sendonionmessage.7.md b/doc/lightning-sendonionmessage.7.md index 7da3b9d96b0b..6e1db0a41204 100644 --- a/doc/lightning-sendonionmessage.7.md +++ b/doc/lightning-sendonionmessage.7.md @@ -43,4 +43,4 @@ Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:39a66bd8e28db8780d7b1365372f7cc638b32a80bb5515657d381b4520f06901) +[comment]: # ( SHA256STAMP:19732c05461b56bb430b3fe568deba807f29a31324252fe748b859b028e649f3) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index f332e812f269..57b4f24e4366 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -139,4 +139,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a7f55104f3bdb21057a410a33902b92aca38aaa83b1e0e7e6876a911316132ed) +[comment]: # ( SHA256STAMP:9d998118234ab83c68d5add16a10b870edf4530c4a2c9e904b0f581b261611d1) diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index fd150b9d38fa..b6473480bfb8 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -65,4 +65,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:044cdcd69e6ece931b6d0f9b25dd842fd456ee479725e610c03694210256583f) +[comment]: # ( SHA256STAMP:1fe5147036f714c8f9185dd482a1bfa3e3ac5c4d0b6603fba1bc2b78de591b8f) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 6a724c9cdd7c..e39691f252d4 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -103,4 +103,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a38b5ea12566d9e40eab07b95a90007bf66373ac1189f458d1678634522575b3) +[comment]: # ( SHA256STAMP:53cf2c58a961078e501b6034e3eed1c5c2d70ed02fc5b167b26ff43c2e2d196f) diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index 75a4147b861a..a9a23070b01e 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -82,4 +82,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:2245fde48f1858886e0f484cb3d96331fef9c41b0081ae51478d912189c38907) +[comment]: # ( SHA256STAMP:ac1cbc87d916cec68cea18d43dc561b3dd655a7f6e2bdaec1a4ebf47bb32b4ad) diff --git a/doc/lightning-signmessage.7.md b/doc/lightning-signmessage.7.md index 86fa363fd986..93608b1dbdf5 100644 --- a/doc/lightning-signmessage.7.md +++ b/doc/lightning-signmessage.7.md @@ -41,4 +41,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:028ade4d84a65b0438347897f4ff5cfc99b5f22d8320b606c9630a1f9da16ef2) +[comment]: # ( SHA256STAMP:137e80fcb45c2e93058a869cb991c4aac31dace621f5941e91b9039290659648) diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index cef87f311044..6ebc481dd830 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -71,4 +71,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:5f7bc2a5f8b6fe72bf70caa3ff14d6f1260c2d366becbc7798ee3bb0374e0b1b) +[comment]: # ( SHA256STAMP:a64f3742f0c66ddf203afa3f69859d4385c4156fe99c30f0931e41ce95d944b1) diff --git a/doc/lightning-stop.7.md b/doc/lightning-stop.7.md index cb83aad34ead..d3c452b42e94 100644 --- a/doc/lightning-stop.7.md +++ b/doc/lightning-stop.7.md @@ -42,4 +42,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:bbdf7415bc7de519ca944c28326c334d9f014f4c987d7e3017ac628c6d1c55ec) +[comment]: # ( SHA256STAMP:a83ac745b36e0e3d00ba11f036cec93973773f47e1265eb5c70a3d3298e58e4b) diff --git a/doc/lightning-txdiscard.7.md b/doc/lightning-txdiscard.7.md index 1144328306a5..b0475e342e4a 100644 --- a/doc/lightning-txdiscard.7.md +++ b/doc/lightning-txdiscard.7.md @@ -44,4 +44,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ced935d8d9047fe1dfb746fc72aafc5b99a8b7b639f854a56478884e5205ffb9) +[comment]: # ( SHA256STAMP:8a6857f312d202e3328443d6ca25b5318d6b5d8fe5655ff6c70466e54c5cd42d) diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index 8b9f16fb4fe1..c22437fd5c37 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -84,4 +84,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f16a12290870442316c8f3fb552627637610ab6a7cfed9082089040c78dce2be) +[comment]: # ( SHA256STAMP:c32c4fe613e5fa173d26bb144fb173d34d2c9181f4eb5da7ef413d41f44f95d7) diff --git a/doc/lightning-txsend.7.md b/doc/lightning-txsend.7.md index 8bb18b6c1893..2d2673093a92 100644 --- a/doc/lightning-txsend.7.md +++ b/doc/lightning-txsend.7.md @@ -44,4 +44,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5bf27f1cbcb247cde5c5570f90be77fc7e8b3e8c80622e75c31e6ac445f2b910) +[comment]: # ( SHA256STAMP:1934c53b0448f872c17b35cfd76b887f3599924213d29ba249b076ff5503be42) diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index 869adbb3f576..323f24f63e0b 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -53,4 +53,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f7aca3e1a40d66e07986cb9e98033e815c4eea2237dc75664a6c47951a8132ed) +[comment]: # ( SHA256STAMP:86bf213ca69b042fec2cf4241875a923db21aef9830e690bd1fb495ffd120966) diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index bc20d1830cf7..1bf65387c3ae 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -99,4 +99,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3c3734a0eb4c2fabf216e11e729ad582cb1fb91dbcb8d2bfc44d56f3206f20fc) +[comment]: # ( SHA256STAMP:5037d26b92d3481a9eac98f509ac2dcee3f3185d6783f3a549d06a9e2e5e1a5f) diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index 525503312e0a..a19b6850ddff 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6c4df3bb97df9f87583725ef31e803900ac548e17506692f9da365a3e4bbf18f) +[comment]: # ( SHA256STAMP:b781ec3594dc6b0ac3091fc2d6561aae91f93a00a922f90285bf8828270ae26c) diff --git a/doc/lightning-waitblockheight.7.md b/doc/lightning-waitblockheight.7.md index 7eae32216db4..b3405d2ba238 100644 --- a/doc/lightning-waitblockheight.7.md +++ b/doc/lightning-waitblockheight.7.md @@ -38,4 +38,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:98f9993935e2820e8e407d1743764346ca6fa1b72228cc82827617a2ed3f3c80) +[comment]: # ( SHA256STAMP:c0440c779f4274b2e2bf4c9e5889a3ef9855c9e9a698d5a50dd8d80eb35c0683) diff --git a/doc/lightning-waitinvoice.7.md b/doc/lightning-waitinvoice.7.md index d568183afa98..b3b645b3fcb2 100644 --- a/doc/lightning-waitinvoice.7.md +++ b/doc/lightning-waitinvoice.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6c4df3bb97df9f87583725ef31e803900ac548e17506692f9da365a3e4bbf18f) +[comment]: # ( SHA256STAMP:b781ec3594dc6b0ac3091fc2d6561aae91f93a00a922f90285bf8828270ae26c) diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index a44abd8e3ac9..1801159b33cf 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -101,4 +101,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:96e5cfd0bd6f2d6b7a25a60f846283592404cee07ef142e58cd56929cc6923c9) +[comment]: # ( SHA256STAMP:8b5a3a8ce1f3c2dcd3e2b8a262d15bcd61700f971830e939a841352aa9d0f849) diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index 3658341455db..e71f951348aa 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cef8d48a59313019e671900621426733d47be2f0c22d5cb2d06ce0b9b7d43592) +[comment]: # ( SHA256STAMP:ee610c5e83e620125e8583022768f0c3293db2326e879b248e67eaddd655c18e) diff --git a/wallet/Makefile b/wallet/Makefile index 05ce68de4593..13c0e7d71137 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -33,8 +33,8 @@ WALLET_SQL_FILES := \ wallet/test/run-wallet.c \ wallet/statements_gettextgen.po: $(WALLET_SQL_FILES) $(FORCE) - @if $(call SHA256STAMP_CHANGED); then \ - $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(WALLET_SQL_FILES) && $(call SHA256STAMP,# ,)); \ + @if $(call SHA256STAMP_CHANGED_ALL); then \ + $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(WALLET_SQL_FILES) && $(call SHA256STAMP_ALL,# ,)); \ fi wallet/db_%_sqlgen.c: wallet/statements_gettextgen.po devtools/sql-rewrite.py $(FORCE) From 64c03f8990707247996846899c56448481d99fe9 Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 05:49:09 +0530 Subject: [PATCH 0958/1530] hsmd: Create derive_secret and makesecret RPC for deriving pseudorandom keys from HSM --- hsmd/hsmd.c | 2 ++ hsmd/hsmd_wire.csv | 8 ++++++++ hsmd/libhsmd.c | 31 +++++++++++++++++++++++++++++- lightningd/hsm_control.c | 41 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 1 deletion(-) diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 0c354a7edbd3..85875bf30e44 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -660,6 +660,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_ECDH_REQ: case WIRE_HSMD_CHECK_FUTURE_SECRET: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: + case WIRE_HSMD_DERIVE_SECRET: case WIRE_HSMD_CANNOUNCEMENT_SIG_REQ: case WIRE_HSMD_NODE_ANNOUNCEMENT_SIG_REQ: case WIRE_HSMD_CUPDATE_SIG_REQ: @@ -681,6 +682,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_SIGN_WITHDRAWAL_REPLY: case WIRE_HSMD_SIGN_INVOICE_REPLY: case WIRE_HSMD_INIT_REPLY: + case WIRE_HSMD_DERIVE_SECRET_REPLY: case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: case WIRE_HSMD_VALIDATE_COMMITMENT_TX_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index a62edce8c520..26d552e5b354 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -281,3 +281,11 @@ msgdata,hsmd_sign_option_will_fund_offer,channel_fee_proportional_basis_max,u16, msgtype,hsmd_sign_option_will_fund_offer_reply,126 msgdata,hsmd_sign_option_will_fund_offer_reply,rsig,secp256k1_ecdsa_signature, + +# Reply with the derived secret +msgtype,hsmd_derive_secret_reply,27 +msgdata,hsmd_derive_secret_reply,secret,secret, + +msgtype,hsmd_derive_secret,127 +msgdata,hsmd_derive_secret,len,u16, +msgdata,hsmd_derive_secret,info,u8,len diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 84f78de344d7..a641e3f87d2e 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -23,11 +23,13 @@ struct secret *dev_force_bip32_seed; #endif /*~ Nobody will ever find it here! hsm_secret is our root secret, the bip32 - * tree and bolt12 payer_id keys are derived from that, and cached here. */ + * tree, bolt12 payer_id keys and derived_secret are derived from that, and + * cached here. */ struct { struct secret hsm_secret; struct ext_key bip32; secp256k1_keypair bolt12; + struct secret derived_secret; } secretstuff; /* Have we initialized the secretstuff? */ @@ -117,6 +119,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_MESSAGE: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY: case WIRE_HSMD_SIGN_BOLT12: + case WIRE_HSMD_DERIVE_SECRET: return (client->capabilities & HSM_CAP_MASTER) != 0; /*~ These are messages sent by the HSM so we should never receive them. */ @@ -145,6 +148,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_MESSAGE_REPLY: case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY: case WIRE_HSMD_SIGN_BOLT12_REPLY: + case WIRE_HSMD_DERIVE_SECRET_REPLY: break; } return false; @@ -257,6 +261,22 @@ static void hsm_channel_secret_base(struct secret *channel_seed_base) "peer seed", strlen("peer seed")); } +/* This will derive pseudorandom secret Key from a derived key */ +static u8 *handle_derive_secret(struct hsmd_client *c, const u8 *msg_in) +{ + u8 *info; + struct secret secret; + + if (!fromwire_hsmd_derive_secret(tmpctx, msg_in, &info)) + return hsmd_status_malformed_request(c, msg_in); + + hkdf_sha256(&secret, sizeof(struct secret), NULL, 0, + &secretstuff.derived_secret, sizeof(&secretstuff.derived_secret), + info, tal_bytelen(info)); + + return towire_hsmd_derive_secret_reply(NULL, &secret); +} + /*~ This gets the seed for this particular channel. */ static void get_channel_seed(const struct node_id *peer_id, u64 dbid, struct secret *channel_seed) @@ -1593,9 +1613,12 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_sign_remote_htlc_to_us(client, msg); case WIRE_HSMD_SIGN_DELAYED_PAYMENT_TO_US: return handle_sign_delayed_payment_to_us(client, msg); + case WIRE_HSMD_DERIVE_SECRET: + return handle_derive_secret(client, msg); case WIRE_HSMD_DEV_MEMLEAK: case WIRE_HSMD_ECDH_RESP: + case WIRE_HSMD_DERIVE_SECRET_REPLY: case WIRE_HSMD_CANNOUNCEMENT_SIG_REPLY: case WIRE_HSMD_CUPDATE_SIG_REPLY: case WIRE_HSMD_CLIENT_HSMFD_REPLY: @@ -1760,6 +1783,12 @@ u8 *hsmd_init(struct secret hsm_secret, sizeof(secretstuff.hsm_secret), "onion reply secret", strlen("onion reply secret")); + /* We derive the derived_secret key for generating pseudorandom keys + * by taking input string from the makesecret RPC */ + hkdf_sha256(&secretstuff.derived_secret, sizeof(struct secret), NULL, 0, + &secretstuff.hsm_secret, sizeof(secretstuff.hsm_secret), + "derived secrets", strlen("derived secrets")); + /*~ Note: marshalling a bip32 tree only marshals the public side, * not the secrets! So we're not actually handing them out here! */ diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 9a49c03df79d..d5f6413a98fe 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -146,6 +148,45 @@ static struct command_result *json_getsharedsecret(struct command *cmd, return command_success(cmd, response); } +static struct command_result *json_makesecret(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + u8 *info; + struct json_stream *response; + struct secret secret; + + if (!param(cmd, buffer, params, + p_req("info_hex", param_bin_from_hex, &info), + NULL)) + return command_param_failed(); + + u8 *msg = towire_hsmd_derive_secret(cmd, info); + if (!wire_sync_write(cmd->ld->hsm_fd, take(msg))) + return command_fail(cmd, LIGHTNINGD, + "Could not write to HSM: %s", strerror(errno)); + + + msg = wire_sync_read(tmpctx, cmd->ld->hsm_fd); + if (!fromwire_hsmd_derive_secret_reply(msg, &secret)) + return command_fail(cmd, LIGHTNINGD, + "Bad reply from HSM: %s", strerror(errno)); + + + response = json_stream_success(cmd); + json_add_secret(response, "secret", &secret); + return command_success(cmd, response); +} + +static const struct json_command makesecret_command = { + "makesecret", + "utility", + &json_makesecret, + "Get a pseudorandom secret key, using an info string." +}; +AUTODATA(json_command, &makesecret_command); + static const struct json_command getsharedsecret_command = { "getsharedsecret", "utility", /* FIXME: Or "crypto"? */ From e42ba8366be9f27c4faa4f15fdbacc2bd5891844 Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 06:04:32 +0530 Subject: [PATCH 0959/1530] common: Add scb_wire for serializing the static_chan_backup --- common/Makefile | 7 +++++-- common/scb_wire.csv | 21 +++++++++++++++++++++ lightningd/Makefile | 1 + lightningd/test/run-invoice-select-inchan.c | 3 +++ tools/generate-wire.py | 2 +- wallet/test/run-wallet.c | 3 +++ 6 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 common/scb_wire.csv diff --git a/common/Makefile b/common/Makefile index 3665baf4f4ca..d18494317c67 100644 --- a/common/Makefile +++ b/common/Makefile @@ -91,7 +91,7 @@ COMMON_SRC_NOGEN := \ common/wire_error.c -COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c +COMMON_SRC_GEN := common/status_wiregen.c common/peer_status_wiregen.c common/scb_wiregen.c COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) \ common/closing_fee.h \ @@ -105,13 +105,16 @@ COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) \ common/overflows.h \ common/tx_roles.h -COMMON_HEADERS_GEN := common/htlc_state_names_gen.h common/status_wiregen.h common/peer_status_wiregen.h +COMMON_HEADERS_GEN := common/htlc_state_names_gen.h common/status_wiregen.h common/peer_status_wiregen.h common/scb_wiregen.h COMMON_HEADERS := $(COMMON_HEADERS_GEN) $(COMMON_HEADERS_NOGEN) COMMON_SRC := $(COMMON_SRC_NOGEN) $(COMMON_SRC_GEN) COMMON_OBJS := $(COMMON_SRC:.c=.o) +common/scb_wiregen.h_args := -s +common/scb_wiregen.c_args := -s + # Check that all h and c files are in the Makefile! check-common-files: @$(call VERBOSE, "MISSING-SRC common", [ "$(filter-out $(COMMON_SRC), $(wildcard common/*.c))" = "" ]) diff --git a/common/scb_wire.csv b/common/scb_wire.csv new file mode 100644 index 000000000000..f27667caf5fb --- /dev/null +++ b/common/scb_wire.csv @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include +#include + +# scb_chan stores min. info required to sweep the peer's force close. +subtype,scb_chan +subtypedata,scb_chan,id,u64, +subtypedata,scb_chan,cid,channel_id, +subtypedata,scb_chan,node_id,node_id, +subtypedata,scb_chan,addr,wireaddr_internal, +subtypedata,scb_chan,funding,bitcoin_outpoint, +subtypedata,scb_chan,funding_sats,amount_sat, +subtypedata,scb_chan,type,channel_type, +msgtype,static_chan_backup,6135, +msgdata,static_chan_backup,version,u64, +msgdata,static_chan_backup,timestamp,u32, +msgdata,static_chan_backup,num,u16, +msgdata,static_chan_backup,channels,scb_chan,num diff --git a/lightningd/Makefile b/lightningd/Makefile index f1f1607ea388..cf997370ce96 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -90,6 +90,7 @@ LIGHTNINGD_COMMON_OBJS := \ common/features.o \ common/fee_states.o \ common/peer_status_wiregen.o \ + common/scb_wiregen.o \ common/status_levels.o \ common/status_wiregen.o \ common/hash_u5.o \ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 01699c8b49a9..fab4c8a481a6 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -694,6 +694,9 @@ u8 *towire_onchaind_dev_memleak(const tal_t *ctx UNNEEDED) /* Generated stub for towire_openingd_dev_memleak */ u8 *towire_openingd_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_openingd_dev_memleak called!\n"); abort(); } +/* Generated stub for towire_scb_chan */ +void towire_scb_chan(u8 **p UNNEEDED, const struct scb_chan *scb_chan UNNEEDED) +{ fprintf(stderr, "towire_scb_chan called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, diff --git a/tools/generate-wire.py b/tools/generate-wire.py index e0476b11f4d2..19e658d936a5 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -244,7 +244,7 @@ class Type(FieldSet): 'tx_parts', 'wally_psbt', 'wally_tx', - 'channel_type', + 'scb_chan', ] # Some BOLT types are re-typed based on their field name diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 65cf287f89aa..94cb6a29e8e5 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -809,6 +809,9 @@ u8 *towire_required_channel_feature_missing(const tal_t *ctx UNNEEDED) /* Generated stub for towire_required_node_feature_missing */ u8 *towire_required_node_feature_missing(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_required_node_feature_missing called!\n"); abort(); } +/* Generated stub for towire_scb_chan */ +void towire_scb_chan(u8 **p UNNEEDED, const struct scb_chan *scb_chan UNNEEDED) +{ fprintf(stderr, "towire_scb_chan called!\n"); abort(); } /* Generated stub for towire_temporary_channel_failure */ u8 *towire_temporary_channel_failure(const tal_t *ctx UNNEEDED, const u8 *channel_update UNNEEDED) { fprintf(stderr, "towire_temporary_channel_failure called!\n"); abort(); } From eca844eb36eebc264f0240eb0340508c95b185ad Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 06:18:18 +0530 Subject: [PATCH 0960/1530] channel: Add struct scb_chan in channel and making last tx optional. --- lightningd/channel.c | 17 ++++++++++++++--- lightningd/channel.h | 5 +++++ lightningd/dual_open_control.c | 8 ++++++++ wallet/wallet.c | 20 ++++++++++++++++++-- 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index e1f059bc4752..035af0895f1e 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -212,6 +212,7 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->openchannel_signed_cmd = NULL; channel->state = DUALOPEND_OPEN_INIT; channel->owner = NULL; + channel->scb = NULL; memset(&channel->billboard, 0, sizeof(channel->billboard)); channel->billboard.transient = tal_fmt(channel, "%s", "Empty channel init'd"); @@ -419,6 +420,14 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->owner = NULL; memset(&channel->billboard, 0, sizeof(channel->billboard)); channel->billboard.transient = tal_strdup(channel, transient_billboard); + channel->scb = tal(channel, struct scb_chan); + channel->scb->id = dbid; + channel->scb->addr = peer->addr; + channel->scb->node_id = peer->id; + channel->scb->funding = *funding; + channel->scb->cid = *cid; + channel->scb->funding_sats = funding_sats; + channel->scb->type = channel_type_dup(channel->scb, type); if (!log) { channel->log = new_log(channel, @@ -447,9 +456,11 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->our_msat = our_msat; channel->msat_to_us_min = msat_to_us_min; channel->msat_to_us_max = msat_to_us_max; - channel->last_tx = tal_steal(channel, last_tx); - channel->last_tx->chainparams = chainparams; - channel->last_tx_type = TX_UNKNOWN; + channel->last_tx = tal_steal(channel, last_tx); + if (channel->last_tx) { + channel->last_tx->chainparams = chainparams; + channel->last_tx_type = TX_UNKNOWN; + } channel->last_sig = *last_sig; channel->last_htlc_sigs = tal_steal(channel, last_htlc_sigs); channel->channel_info = *channel_info; diff --git a/lightningd/channel.h b/lightningd/channel.h index fbe86a63f916..8851bd72ff08 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -260,6 +261,10 @@ struct channel { /* Latest channel_update, for use in error messages. */ u8 *channel_update; + + /* `Channel-shell` of this channel + * (Minimum information required to backup this channel). */ + struct scb_chan *scb; }; /* For v2 opens, a channel that has not yet been committed/saved to disk */ diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index d72a688f05de..cac1152aae8c 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1228,6 +1228,14 @@ wallet_commit_channel(struct lightningd *ld, &commitment_feerate); channel->min_possible_feerate = commitment_feerate; channel->max_possible_feerate = commitment_feerate; + channel->scb = tal(channel, struct scb_chan); + channel->scb->id = channel->dbid; + channel->scb->addr = channel->peer->addr; + channel->scb->node_id = channel->peer->id; + channel->scb->funding = *funding; + channel->scb->cid = channel->cid; + channel->scb->funding_sats = total_funding; + channel->scb->type = channel_type_dup(channel->scb, channel->type); /* We are connected */ channel->connected = true; diff --git a/wallet/wallet.c b/wallet/wallet.c index efd966d32a74..33694c9c4748 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1114,6 +1114,7 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, struct amount_msat lease_fee; struct bitcoin_outpoint funding; struct bitcoin_signature last_sig; + struct bitcoin_tx *last_tx; struct channel_inflight *inflight; secp256k1_ecdsa_signature *lease_commit_sig; @@ -1149,12 +1150,19 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, db_col_ignore(stmt, "lease_fee"); } + /* last_tx is null for stub channels used for recovering funds through + * Static channel backups. */ + if (!db_col_is_null(stmt, "last_tx")) + last_tx = db_col_psbt_to_tx(tmpctx, stmt, "last_tx"); + else + last_tx = NULL; + inflight = new_inflight(chan, &funding, db_col_int(stmt, "funding_feerate"), funding_sat, our_funding_sat, db_col_psbt(tmpctx, stmt, "funding_psbt"), - db_col_psbt_to_tx(tmpctx, stmt, "last_tx"), + last_tx, last_sig, db_col_int(stmt, "lease_expiry"), lease_commit_sig, @@ -1260,6 +1268,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm struct bitcoin_outpoint funding; struct bitcoin_outpoint *shutdown_wrong_funding; struct bitcoin_signature last_sig; + struct bitcoin_tx *last_tx; u8 *remote_shutdown_scriptpubkey; u8 *local_shutdown_scriptpubkey; struct changed_htlc *last_sent_commit; @@ -1445,6 +1454,13 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm else type = channel_type_none(NULL); + /* last_tx is null for stub channels used for recovering funds through + * Static channel backups. */ + if (!db_col_is_null(stmt, "last_tx")) + last_tx = db_col_psbt_to_tx(tmpctx, stmt, "last_tx"); + else + last_tx = NULL; + chan = new_channel(peer, db_col_u64(stmt, "id"), &wshachain, db_col_int(stmt, "state"), @@ -1469,7 +1485,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm our_msat, msat_to_us_min, /* msatoshi_to_us_min */ msat_to_us_max, /* msatoshi_to_us_max */ - db_col_psbt_to_tx(tmpctx, stmt, "last_tx"), + last_tx, &last_sig, wallet_htlc_sigs_load(tmpctx, w, db_col_u64(stmt, "id"), From 1a1be6abd602f6e071abd80473600b0a67992cce Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 06:21:07 +0530 Subject: [PATCH 0961/1530] lightningd/peer_control: Add RPC for fetching scb for all the in-memory channels. --- lightningd/peer_control.c | 52 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 40bb56812f6f..44bd8624b567 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1757,6 +1758,57 @@ static const struct json_command listpeers_command = { /* Comment added to satisfice AUTODATA */ AUTODATA(json_command, &listpeers_command); +static void json_add_scb(struct command *cmd, + const char *fieldname, + struct json_stream *response, + struct channel *c) +{ + u8 *scb = tal_arr(cmd, u8, 0); + + towire_scb_chan(&scb, c->scb); + json_add_hex_talarr(response, fieldname, + scb); +} + +/* This will return a SCB for all the channels currently loaded + * in the in-memory channel */ +static struct command_result *json_staticbackup(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + struct peer *peer; + struct channel *channel; + + if (!param(cmd, buffer, params, NULL)) + return command_param_failed(); + + response = json_stream_success(cmd); + + json_array_start(response, "scb"); + + list_for_each(&cmd->ld->peers, peer, list) + list_for_each(&peer->channels, channel, list){ + if (!channel->scb) + continue; + json_add_scb(cmd, NULL, response, channel); + } + json_array_end(response); + + return command_success(cmd, response); +} + +static const struct json_command staticbackup_command = { + "staticbackup", + "backup", + json_staticbackup, + "Returns SCB of all the channels currently present in the DB" +}; +/* Comment added to satisfice AUTODATA */ +AUTODATA(json_command, &staticbackup_command); + + struct command_result * command_find_channel(struct command *cmd, const char *buffer, const jsmntok_t *tok, From 286d6c31654be0e34117bd981da2e93a27c32241 Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 06:37:16 +0530 Subject: [PATCH 0962/1530] tools/gen: Always return bool! --- common/test/run-lease_rates.c | 2 +- tools/gen/header_template | 2 +- tools/gen/impl_template | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/common/test/run-lease_rates.c b/common/test/run-lease_rates.c index 5d8b7d0e8597..31da81c7d317 100644 --- a/common/test/run-lease_rates.c +++ b/common/test/run-lease_rates.c @@ -15,7 +15,7 @@ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } /* Generated stub for fromwire_lease_rates */ -void fromwire_lease_rates(const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, struct lease_rates *lease_rates UNNEEDED) +bool fromwire_lease_rates(const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, struct lease_rates *lease_rates UNNEEDED) { fprintf(stderr, "fromwire_lease_rates called!\n"); abort(); } /* Generated stub for fromwire_secp256k1_ecdsa_signature */ void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, diff --git a/tools/gen/header_template b/tools/gen/header_template index 35e62e60aeea..4b7188555f5e 100644 --- a/tools/gen/header_template +++ b/tools/gen/header_template @@ -148,7 +148,7 @@ void towire_${subtype.name}(u8 **p, const ${subtype.type_name()} *${subtype.name % if subtype.is_varsize(): ${subtype.type_name()} *fromwire_${subtype.name}(const tal_t *ctx, const u8 **cursor, size_t *plen); % else: -void fromwire_${subtype.name}(const u8 **cursor, size_t *plen, ${subtype.type_name()} *${subtype.name}); +bool fromwire_${subtype.name}(const u8 **cursor, size_t *plen, ${subtype.type_name()} *${subtype.name}); % endif % endfor diff --git a/tools/gen/impl_template b/tools/gen/impl_template index 3d013f7f144d..cb3d1f77b206 100644 --- a/tools/gen/impl_template +++ b/tools/gen/impl_template @@ -151,7 +151,7 @@ ${static}void towire_${subtype.name}(u8 **p, const ${subtype.type_name()} *${sub ${static}${subtype.type_name()} * fromwire_${subtype.name}(const tal_t *ctx, const u8 **cursor, size_t *plen) % else: -${static}void fromwire_${subtype.name}(${'const tal_t *ctx, ' if subtype.needs_context() else ''}const u8 **cursor, size_t *plen, ${subtype.type_name()} *${subtype.name}) +${static}bool fromwire_${subtype.name}(${'const tal_t *ctx, ' if subtype.needs_context() else ''}const u8 **cursor, size_t *plen, ${subtype.type_name()} *${subtype.name}) % endif { % if subtype.is_varsize(): @@ -175,6 +175,9 @@ ${static}void fromwire_${subtype.name}(${'const tal_t *ctx, ' if subtype.needs_c % if subtype.is_varsize(): return ${subtype.name}; +% else: + + return *cursor != NULL; % endif } % endfor ## END Subtypes From 6ba8abb0de13ed0d7bcdfc9d7dd8f4347d4fe26d Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 06:44:11 +0530 Subject: [PATCH 0963/1530] lightningd: Add RPC for populating DB with stub channels and set an error on reconnecting --- bitcoin/short_channel_id.h | 7 ++ lightningd/channel.c | 11 ++ lightningd/onchain_control.c | 13 ++- lightningd/opening_control.c | 213 +++++++++++++++++++++++++++++++++++ lightningd/peer_control.c | 11 +- wallet/wallet.c | 5 +- 6 files changed, 253 insertions(+), 7 deletions(-) diff --git a/bitcoin/short_channel_id.h b/bitcoin/short_channel_id.h index bac77d8d8b06..4465da462003 100644 --- a/bitcoin/short_channel_id.h +++ b/bitcoin/short_channel_id.h @@ -37,6 +37,13 @@ static inline u32 short_channel_id_blocknum(const struct short_channel_id *scid) return scid->u64 >> 40; } +static inline bool is_stub_scid(const struct short_channel_id *scid) +{ + return scid ? scid->u64 >> 40 == 1 && + ((scid->u64 >> 16) & 0x00FFFFFF) == 1 && + (scid->u64 & 0xFFFF) == 1 : false; +} + static inline u32 short_channel_id_txnum(const struct short_channel_id *scid) { return (scid->u64 >> 16) & 0x00FFFFFF; diff --git a/lightningd/channel.c b/lightningd/channel.c index 035af0895f1e..06a7c63615e5 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -534,6 +534,12 @@ struct channel *new_channel(struct peer *peer, u64 dbid, txfilter_add_scriptpubkey(peer->ld->owned_txfilter, take(p2wpkh_for_keyidx(NULL, peer->ld, channel->final_key_idx))); + /* scid is NULL when opening a new channel so we don't + * need to set error in that case as well */ + if (is_stub_scid(scid)) + channel->error = towire_errorfmt(peer->ld, + &channel->cid, + "We can't be together anymore."); return channel; } @@ -779,6 +785,11 @@ void channel_fail_permanent(struct channel *channel, const char *fmt, ...) { + /* Don't do anything if it's an stub channel because + * peer has already closed it unilatelrally. */ + if (is_stub_scid(channel->scid)) + return; + struct lightningd *ld = channel->peer->ld; va_list ap; char *why; diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 1c22cc1b9627..2e8919751509 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -669,8 +669,17 @@ enum watch_result onchaind_funding_spent(struct channel *channel, channel->final_key_idx); return KEEP_WATCHING; } - /* This could be a mutual close, but it doesn't matter. */ - bitcoin_txid(channel->last_tx, &our_last_txid); + + /* This could be a mutual close, but it doesn't matter. + * We don't need this for stub channels as well */ + if (!is_stub_scid(channel->scid)) + bitcoin_txid(channel->last_tx, &our_last_txid); + else + /* Dummy txid for stub channel to make valgrind happy. */ + bitcoin_txid_from_hex("80cea306607b708a03a1854520729d" + "a884e4317b7b51f3d4a622f88176f5e034", + 64, + &our_last_txid); /* We try to get the feerate for each transaction type, 0 if estimation * failed. */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index a6b8d75325d8..c2f02819ff9e 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1229,6 +1230,208 @@ static struct command_result *json_fundchannel_start(struct command *cmd, return command_still_pending(cmd); } +static struct channel *stub_chan(struct command *cmd, + u64 id, + struct node_id nodeid, + struct channel_id cid, + struct bitcoin_outpoint funding, + struct wireaddr_internal addr, + struct amount_sat funding_sats, + struct channel_type *type) +{ + struct basepoints basepoints; + struct bitcoin_signature *sig; + struct channel *channel; + struct channel_config *our_config; + struct channel_config *their_config; + struct channel_info *channel_info; + struct lightningd *ld; + struct peer *peer; + struct pubkey localFundingPubkey; + struct pubkey pk; + struct short_channel_id *scid; + u32 blockht; + u32 feerate; + u8 *dummy_sig = tal_hexdata(cmd, + "30450221009b2e0eef267b94c3899fb0dc73750" + "12e2cee4c10348a068fe78d1b82b4b1403602207" + "7c3fad3adac2ddf33f415e45f0daf6658b7a0b09" + "647de4443938ae2dbafe2b9" "01", + 144); + + peer = new_peer(cmd->ld, + 0, + &nodeid, + &addr, + false); + + ld = cmd->ld; + feerate = FEERATE_FLOOR; + + sig = tal(cmd, struct bitcoin_signature); + signature_from_der(dummy_sig, + tal_bytelen(dummy_sig) + ,sig); + + if (!pubkey_from_der(tal_hexdata(cmd, + type_to_string(tmpctx, + struct node_id, + &nodeid), + 66), + 33, + &pk)) + { + fatal("Invalid node id!"); + } + + get_channel_basepoints(ld, + &nodeid, + id, + &basepoints, + &localFundingPubkey); + + channel_info = tal(cmd, + struct channel_info); + + our_config = tal(cmd, struct channel_config); + their_config = tal(cmd, struct channel_config); + + /* FIXME: Makeake these a pointer, so they could be NULL */ + memset(our_config, 0, sizeof(struct channel_config)); + memset(their_config, 0, sizeof(struct channel_config)); + channel_info->their_config = *their_config; + channel_info->theirbase = basepoints; + channel_info->remote_fundingkey = pk; + channel_info->remote_per_commit = pk; + channel_info->old_remote_per_commit = pk; + + blockht = 100; + scid = tal(cmd, struct short_channel_id); + + /*To indicate this is an stub channel we keep it's scid to 1x1x1.*/ + if (!mk_short_channel_id(scid, 1, 1, 1)) + fatal("Failed to make short channel 1x1x1!"); + + /* Channel Shell with Dummy data(mostly) */ + channel = new_channel(peer, id, + NULL, /* No shachain yet */ + CHANNELD_NORMAL, + LOCAL, + NULL, + "restored from static channel backup", + 0, our_config, + 0, + 1, 1, 1, + &funding, + funding_sats, + AMOUNT_MSAT(0), + AMOUNT_SAT(0), + true, /* !remote_funding_locked */ + scid, + scid, + scid, + &cid, + /* The three arguments below are msatoshi_to_us, + * msatoshi_to_us_min, and msatoshi_to_us_max. + * Because, this is a newly-funded channel, + * all three are same value. */ + AMOUNT_MSAT(0), + AMOUNT_MSAT(0), /* msat_to_us_min */ + AMOUNT_MSAT(0), /* msat_to_us_max */ + NULL, + sig, + NULL, /* No HTLC sigs */ + channel_info, + new_fee_states(cmd, LOCAL, &feerate), + NULL, /* No shutdown_scriptpubkey[REMOTE] */ + NULL, + 1, false, + NULL, /* No commit sent */ + /* If we're fundee, could be a little before this + * in theory, but it's only used for timing out. */ + get_network_blockheight(ld->topology), + FEERATE_FLOOR, + funding_sats.satoshis / MINIMUM_TX_WEIGHT * 1000 /* Raw: convert to feerate */, + false, + &basepoints, + &localFundingPubkey, + NULL, + ld->config.fee_base, + ld->config.fee_per_satoshi, + NULL, + 0, 0, + type, + NUM_SIDES, /* closer not yet known */ + REASON_REMOTE, + NULL, + take(new_height_states(ld->wallet, LOCAL, + &blockht)), + 0, NULL, 0, 0, /* No leases on v1s */ + ld->config.htlc_minimum_msat, + ld->config.htlc_maximum_msat); + + return channel; +} + +static struct command_result *json_recoverchannel(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + const jsmntok_t *scb, *t; + size_t i; + struct json_stream *response; + struct scb_chan *scb_chan = tal(cmd, struct scb_chan); + + if (!param(cmd, buffer, params, + p_req("scb", param_array, &scb), + NULL)) + return command_param_failed(); + + response = json_stream_success(cmd); + + json_array_start(response, "stubs"); + json_for_each_arr(i,t,scb){ + + char *token = json_strdup(tmpctx, buffer, t); + const u8 *scb_arr = tal_hexdata(cmd, token, strlen(token)); + size_t scblen = tal_count(scb_arr); + + scb_chan = fromwire_scb_chan(cmd ,&scb_arr, &scblen); + + if (scb_chan == NULL) { + log_broken(cmd->ld->log, "SCB is invalid!"); + continue; + } + + struct lightningd *ld = cmd->ld; + struct channel *channel= stub_chan(cmd, + scb_chan->id, + scb_chan->node_id, + scb_chan->cid, + scb_chan->funding, + scb_chan->addr, + scb_chan->funding_sats, + scb_chan->type); + + /* Now we put this in the database. */ + wallet_channel_insert(ld->wallet, channel); + + /* Watch the Funding */ + channel_watch_funding(ld, channel); + + json_add_channel_id(response, NULL, &scb_chan->cid); + } + + /* This will try to reconnect to the peers and start + * initiating the process */ + setup_peers(cmd->ld); + + json_array_end(response); + + return command_success(cmd, response); +} + static const struct json_command fundchannel_start_command = { "fundchannel_start", "channels", @@ -1254,3 +1457,13 @@ static const struct json_command fundchannel_complete_command = { "with {psbt}. Returns true on success, false otherwise." }; AUTODATA(json_command, &fundchannel_complete_command); + +static const struct json_command json_commitchan_command = { + "recoverchannel", + "channels", + json_recoverchannel, + "Populate the DB with a channel and peer" + "Used for recovering the channel using DLP." + "This needs param in the form of an array [scb1,scb2,...]" +}; +AUTODATA(json_command, &json_commitchan_command); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 44bd8624b567..4a0b4c8a08cd 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1536,8 +1536,9 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, const char *txidstr; struct short_channel_id scid; - /* Sanity check */ - if (!check_funding_tx(tx, channel)) { + /* Sanity check, but we'll have to make an exception + * for stub channels(1x1x1) */ + if (!check_funding_tx(tx, channel) && !is_stub_scid(channel->scid)) { channel_internal_error(channel, "Bad tx %s: %s", type_to_string(tmpctx, struct bitcoin_txid, txid), @@ -1592,13 +1593,15 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, return DELETE_WATCH; } - /* If we restart, we could already have peer->scid from database */ + /* If we restart, we could already have peer->scid from database, + * we don't need to update scid for stub channels(1x1x1) */ if (!channel->scid) { channel->scid = tal(channel, struct short_channel_id); *channel->scid = scid; wallet_channel_save(ld->wallet, channel); - } else if (!short_channel_id_eq(channel->scid, &scid)) { + } else if (!short_channel_id_eq(channel->scid, &scid) && + !is_stub_scid(channel->scid)) { /* This normally restarts channeld, initialized with updated scid * and also adds it (at least our halve_chan) to rtable. */ channel_fail_reconnect(channel, diff --git a/wallet/wallet.c b/wallet/wallet.c index 33694c9c4748..6f6c6b699a86 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1930,7 +1930,10 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_talarr(stmt, 17, chan->shutdown_scriptpubkey[REMOTE]); db_bind_u64(stmt, 18, chan->final_key_idx); db_bind_u64(stmt, 19, chan->our_config.id); - db_bind_psbt(stmt, 20, chan->last_tx->psbt); + if (chan->last_tx) + db_bind_psbt(stmt, 20, chan->last_tx->psbt); + else + db_bind_null(stmt, 20); db_bind_signature(stmt, 21, &chan->last_sig.s); db_bind_int(stmt, 22, chan->last_was_revoke); db_bind_int(stmt, 23, chan->min_possible_feerate); From 1450f1758c90cba9dcd4894f745d1db52e6e66b4 Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 06:59:36 +0530 Subject: [PATCH 0964/1530] Plugin: Add new plugin for SCB, Updated makefile and gitignore as well --- plugins/.gitignore | 1 + plugins/Makefile | 8 + plugins/chanbackup.c | 419 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) create mode 100644 plugins/chanbackup.c diff --git a/plugins/.gitignore b/plugins/.gitignore index 389469cf0de3..3c340b67de4f 100644 --- a/plugins/.gitignore +++ b/plugins/.gitignore @@ -10,3 +10,4 @@ pay spenderp topology txprepare +chanbackup \ No newline at end of file diff --git a/plugins/Makefile b/plugins/Makefile index bae7bfa4e583..c69297569a5a 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -4,6 +4,9 @@ PLUGIN_PAY_OBJS := $(PLUGIN_PAY_SRC:.c=.o) PLUGIN_AUTOCLEAN_SRC := plugins/autoclean.c PLUGIN_AUTOCLEAN_OBJS := $(PLUGIN_AUTOCLEAN_SRC:.c=.o) +PLUGIN_chanbackup_SRC := plugins/chanbackup.c +PLUGIN_chanbackup_OBJS := $(PLUGIN_chanbackup_SRC:.c=.o) + PLUGIN_TOPOLOGY_SRC := plugins/topology.c PLUGIN_TOPOLOGY_OBJS := $(PLUGIN_TOPOLOGY_SRC:.c=.o) @@ -55,6 +58,7 @@ PLUGIN_FUNDER_OBJS := $(PLUGIN_FUNDER_SRC:.c=.o) PLUGIN_ALL_SRC := \ $(PLUGIN_AUTOCLEAN_SRC) \ + $(PLUGIN_chanbackup_SRC) \ $(PLUGIN_BCLI_SRC) \ $(PLUGIN_FETCHINVOICE_SRC) \ $(PLUGIN_FUNDER_SRC) \ @@ -77,6 +81,7 @@ PLUGIN_ALL_OBJS := $(PLUGIN_ALL_SRC:.c=.o) C_PLUGINS := \ plugins/autoclean \ + plugins/chanbackup \ plugins/bcli \ plugins/fetchinvoice \ plugins/funder \ @@ -140,6 +145,7 @@ PLUGIN_COMMON_OBJS := \ common/psbt_open.o \ common/pseudorand.o \ common/random_select.o \ + common/scb_wiregen.o \ common/setup.o \ common/status_levels.o \ common/type_to_string.o \ @@ -160,6 +166,8 @@ plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGI plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/chanbackup: bitcoin/chainparams.o $(PLUGIN_chanbackup_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) + # Topology wants to decode node_announcement, and peer_wiregen which # pulls in some of bitcoin/. plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o wire/peer$(EXP)_wiregen.o wire/channel_type_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) diff --git a/plugins/chanbackup.c b/plugins/chanbackup.c new file mode 100644 index 000000000000..cd8376be433a --- /dev/null +++ b/plugins/chanbackup.c @@ -0,0 +1,419 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HEADER_LEN crypto_secretstream_xchacha20poly1305_HEADERBYTES +#define ABYTES crypto_secretstream_xchacha20poly1305_ABYTES + +/* VERSION is the current version of the data encrypted in the file */ +#define VERSION ((u64)1) + +/* Global secret object to keep the derived encryption key for the SCB */ +static struct secret secret; + +/* Helper to fetch out SCB from the RPC call */ +static bool json_to_scb_chan(const char *buffer, + const jsmntok_t *tok, + struct scb_chan ***channels) +{ + size_t i; + const jsmntok_t *t; + *channels = tok->size ? tal_arr(tmpctx, + struct scb_chan *, + tok->size) : NULL; + + json_for_each_arr(i, t, tok) { + const u8 *scb_tmp = tal_hexdata(tmpctx, + json_strdup(tmpctx, + buffer, + t), + strlen(json_strdup(tmpctx, + buffer, + t))); + size_t scblen_tmp = tal_count(scb_tmp); + + (*channels)[i] = fromwire_scb_chan(tmpctx, + &scb_tmp, + &scblen_tmp); + } + + return true; +} + +/* This writes encrypted static backup in the recovery file */ +static void write_scb(struct plugin *p, + int fd, + struct scb_chan **scb_chan_arr) +{ + u32 timestamp = time_now().ts.tv_sec; + + u8 *decrypted_scb = towire_static_chan_backup(tmpctx, + VERSION, + timestamp, + cast_const2(const struct scb_chan **, + scb_chan_arr)); + + u8 *encrypted_scb = tal_arr(tmpctx, + u8, + tal_bytelen(decrypted_scb) + + ABYTES + + HEADER_LEN); + + crypto_secretstream_xchacha20poly1305_state crypto_state; + + if (crypto_secretstream_xchacha20poly1305_init_push(&crypto_state, + encrypted_scb, + (&secret)->data) != 0) + { + plugin_err(p, "Can't encrypt the data!"); + return; + } + + if (crypto_secretstream_xchacha20poly1305_push(&crypto_state, + encrypted_scb + + HEADER_LEN, + NULL, decrypted_scb, + tal_bytelen(decrypted_scb), + /* Additional data and tag */ + NULL, 0, 0)) { + plugin_err(p, "Can't encrypt the data!"); + return; + } + + if (!write_all(fd, encrypted_scb, tal_bytelen(encrypted_scb))) { + unlink_noerr("scb.tmp"); + plugin_err(p, "Writing encrypted SCB: %s", + strerror(errno)); + } + +} + +/* checks if the SCB file exists, creates a new one in case it doesn't. */ +static void maybe_create_new_scb(struct plugin *p, + struct scb_chan **channels) +{ + + /* Note that this is opened for write-only, even though the permissions + * are set to read-only. That's perfectly valid! */ + int fd = open("emergency.recover", O_CREAT|O_EXCL|O_WRONLY, 0400); + if (fd < 0) { + /* Don't do anything if the file already exists. */ + if (errno == EEXIST) + return; + plugin_err(p, "creating: %s", strerror(errno)); + } + + /* Comes here only if the file haven't existed before */ + unlink_noerr("emergency.recover"); + + /* This couldn't give EEXIST because we call unlink_noerr("scb.tmp") + * in INIT */ + fd = open("scb.tmp", O_CREAT|O_EXCL|O_WRONLY, 0400); + if (fd < 0) + plugin_err(p, "Opening: %s", strerror(errno)); + + plugin_log(p, LOG_INFORM, "Creating Emergency Recovery"); + + write_scb(p, fd, channels); + + /* fsync (mostly!) ensures that the file has reached the disk. */ + if (fsync(fd) != 0) { + unlink_noerr("scb.tmp"); + plugin_err(p, "fsync : %s", strerror(errno)); + } + + /* This should never fail if fsync succeeded. But paranoia good, and + * bugs exist. */ + if (close(fd) != 0) { + unlink_noerr("scb.tmp"); + plugin_err(p, "closing: %s", strerror(errno)); + } + + /* We actually need to sync the *directory itself* to make sure the + * file exists! You're only allowed to open directories read-only in + * modern Unix though. */ + fd = open(".", O_RDONLY); + if (fd < 0) + plugin_err(p, "Opening: %s", strerror(errno)); + + if (fsync(fd) != 0) { + unlink_noerr("scb.tmp"); + plugin_err(p, "closing: %s", strerror(errno)); + } + + /* This will never fail, if fsync worked! */ + close(fd); + + /* This will update the scb file */ + rename("scb.tmp", "emergency.recover"); +} + + +/* Returns decrypted SCB in form of a u8 array */ +static u8 *decrypt_scb(struct plugin *p) +{ + struct stat st; + int fd = open("emergency.recover", O_RDONLY); + + if (stat("emergency.recover", &st) != 0) + plugin_err(p, "SCB file is corrupted!: %s", + strerror(errno)); + + u8 final[st.st_size]; + + if (!read_all(fd, &final, st.st_size)) { + plugin_log(p, LOG_DBG, "SCB file is corrupted!: %s", + strerror(errno)); + return NULL; + } + + crypto_secretstream_xchacha20poly1305_state crypto_state; + + if (st.st_size < ABYTES + + HEADER_LEN) + plugin_err(p, "SCB file is corrupted!"); + + u8 *ans = tal_arr(tmpctx, u8, st.st_size - + ABYTES - + HEADER_LEN); + + /* The header part */ + if (crypto_secretstream_xchacha20poly1305_init_pull(&crypto_state, + final, + (&secret)->data) != 0) + { + plugin_err(p, "SCB file is corrupted!"); + } + + if (crypto_secretstream_xchacha20poly1305_pull(&crypto_state, ans, + NULL, 0, + final + + HEADER_LEN, + st.st_size - + HEADER_LEN, + NULL, 0) != 0) { + plugin_err(p, "SCB file is corrupted!"); + } + + if (close(fd) != 0) + plugin_err(p, "Closing: %s", strerror(errno)); + + return ans; +} + +static struct command_result *after_recover_rpc(struct command *cmd, + const char *buf, + const jsmntok_t *params, + void *cb_arg UNUSED) +{ + + size_t i; + const jsmntok_t *t; + struct json_stream *response; + + response = jsonrpc_stream_success(cmd); + + json_for_each_obj(i, t, params) + json_add_tok(response, json_strdup(tmpctx, buf, t), t+1, buf); + + return command_finished(cmd, response); +} + +/* Recovers the channels by making RPC to `recoverchannel` */ +static struct command_result *json_emergencyrecover(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct out_req *req; + u64 version; + u32 timestamp; + struct scb_chan **scb; + + if (!param(cmd, buf, params, NULL)) + return command_param_failed(); + + u8 *res = decrypt_scb(cmd->plugin); + + if (!fromwire_static_chan_backup(cmd, + res, + &version, + ×tamp, + &scb)) { + plugin_err(cmd->plugin, "Corrupted SCB!"); + } + + if (version != VERSION) { + plugin_err(cmd->plugin, + "Incompatible version, Contact the admin!"); + } + + req = jsonrpc_request_start(cmd->plugin, cmd, "recoverchannel", + after_recover_rpc, + &forward_error, NULL); + + json_array_start(req->js, "scb"); + for (size_t i=0; ijs, NULL, scb_hex, tal_bytelen(scb_hex)); + } + json_array_end(req->js); + + return send_outreq(cmd->plugin, req); +} + +static void update_scb(struct plugin *p, struct scb_chan **channels) +{ + + /* If the temp file existed before, remove it */ + unlink_noerr("scb.tmp"); + + int fd = open("scb.tmp", O_CREAT|O_EXCL|O_WRONLY, 0400); + if (fd<0) + plugin_err(p, "Opening: %s", strerror(errno)); + + plugin_log(p, LOG_DBG, "Updating the SCB file..."); + + write_scb(p, fd, channels); + + /* fsync (mostly!) ensures that the file has reached the disk. */ + if (fsync(fd) != 0) { + unlink_noerr("scb.tmp"); + } + + /* This should never fail if fsync succeeded. But paranoia good, and + * bugs exist. */ + if (close(fd) != 0) { + unlink_noerr("scb.tmp"); + } + /* We actually need to sync the *directory itself* to make sure the + * file exists! You're only allowed to open directories read-only in + * modern Unix though. */ + fd = open(".", O_RDONLY); + if (fd < 0) { + plugin_log(p, LOG_DBG, "Opening: %s", strerror(errno)); + } + if (fsync(fd) != 0) { + unlink_noerr("scb.tmp"); + } + close(fd); + + /* This will atomically replace the main file */ + rename("scb.tmp", "emergency.recover"); +} + +static struct command_result *after_staticbackup(struct command *cmd, + const char *buf, + const jsmntok_t *params, + void *cb_arg UNUSED) +{ + struct scb_chan **scb_chan; + const jsmntok_t *scbs = json_get_member(buf, params, "scb"); + json_to_scb_chan(buf, scbs, &scb_chan); + plugin_log(cmd->plugin, LOG_INFORM, "Updating the SCB"); + + update_scb(cmd->plugin, scb_chan); + return notification_handled(cmd); +} + +static struct command_result *json_state_changed(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const jsmntok_t *notiftok = json_get_member(buf, + params, + "channel_state_changed"), + *statetok = json_get_member(buf, notiftok, "new_state"); + + /* FIXME: I wanted to update the file on CHANNELD_AWAITING_LOCKIN, + * But I don't get update for it, maybe because there is + * no previous_state, also apparently `channel_opened` gets published + * when *peer* funded a channel with us? + * So, is their no way to get a notif on CHANNELD_AWAITING_LOCKIN? */ + if (json_tok_streq(buf, statetok, "CLOSED") || + json_tok_streq(buf, statetok, "CHANNELD_NORMAL")) { + + struct out_req *req; + req = jsonrpc_request_start(cmd->plugin, + cmd, + "staticbackup", + after_staticbackup, + &forward_error, + NULL); + + return send_outreq(cmd->plugin, req); + } + + return notification_handled(cmd); +} + +static const char *init(struct plugin *p, + const char *buf UNUSED, + const jsmntok_t *config UNUSED) +{ + struct scb_chan **scb_chan; + const char *info = "scb secret"; + u8 *info_hex = tal_dup_arr(tmpctx, u8, (u8*)info, strlen(info), 0); + + rpc_scan(p, "staticbackup", + take(json_out_obj(NULL, NULL, NULL)), + "{scb:%}", JSON_SCAN(json_to_scb_chan, &scb_chan)); + + rpc_scan(p, "makesecret", + take(json_out_obj(NULL, "info_hex", + tal_hexstr(tmpctx, + info_hex, + tal_bytelen(info_hex)))), + "{secret:%}", JSON_SCAN(json_to_secret, &secret)); + + plugin_log(p, LOG_DBG, "Chanbackup Initialised!"); + + /* flush the tmp file, if exists */ + unlink_noerr("scb.tmp"); + + maybe_create_new_scb(p, scb_chan); + + return NULL; +} + +static const struct plugin_notification notifs[] = { + { + "channel_state_changed", + json_state_changed, + } +}; + +static const struct plugin_command commands[] = { { + "emergencyrecover", + "recovery", + "Populates the DB with stub channels", + "returns stub channel-id's on completion", + json_emergencyrecover, + } +}; + +int main(int argc, char *argv[]) +{ + setup_locale(); + plugin_main(argv, init, PLUGIN_RESTARTABLE, true, NULL, + commands, ARRAY_SIZE(commands), + notifs, ARRAY_SIZE(notifs), NULL, 0, + NULL, 0, /* Notification topics we publish */ + NULL); +} From 829fe09c134847d5b12889a22360da2c77d2c9df Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 07:09:12 +0530 Subject: [PATCH 0965/1530] doc: Add documentation for new RPCs and a FIXME: in fromschema.py --- doc/Makefile | 4 ++ doc/index.rst | 4 ++ doc/lightning-emergencyrecover.7.md | 43 ++++++++++++++++++++++ doc/lightning-makesecret.7.md | 44 ++++++++++++++++++++++ doc/lightning-recoverchannel.7.md | 45 +++++++++++++++++++++++ doc/lightning-staticbackup.7.md | 38 +++++++++++++++++++ doc/schemas/emergencyrecover.request.json | 7 ++++ doc/schemas/emergencyrecover.schema.json | 17 +++++++++ doc/schemas/makesecret.request.json | 14 +++++++ doc/schemas/makesecret.schema.json | 16 ++++++++ doc/schemas/recoverchannel.request.json | 15 ++++++++ doc/schemas/recoverchannel.schema.json | 17 +++++++++ doc/schemas/staticbackup.request.json | 7 ++++ doc/schemas/staticbackup.schema.json | 17 +++++++++ tools/fromschema.py | 11 ++++++ 15 files changed, 299 insertions(+) create mode 100644 doc/lightning-emergencyrecover.7.md create mode 100644 doc/lightning-makesecret.7.md create mode 100644 doc/lightning-recoverchannel.7.md create mode 100644 doc/lightning-staticbackup.7.md create mode 100644 doc/schemas/emergencyrecover.request.json create mode 100644 doc/schemas/emergencyrecover.schema.json create mode 100644 doc/schemas/makesecret.request.json create mode 100644 doc/schemas/makesecret.schema.json create mode 100644 doc/schemas/recoverchannel.request.json create mode 100644 doc/schemas/recoverchannel.schema.json create mode 100644 doc/schemas/staticbackup.request.json create mode 100644 doc/schemas/staticbackup.schema.json diff --git a/doc/Makefile b/doc/Makefile index e3b8b47546bd..f9375b406289 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -24,6 +24,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-delpay.7 \ doc/lightning-disableoffer.7 \ doc/lightning-disconnect.7 \ + doc/lightning-emergencyrecover.7 \ doc/lightning-feerates.7 \ doc/lightning-fetchinvoice.7 \ doc/lightning-fundchannel.7 \ @@ -46,6 +47,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-listpays.7 \ doc/lightning-listpeers.7 \ doc/lightning-listsendpays.7 \ + doc/lightning-makesecret.7 \ doc/lightning-multifundchannel.7 \ doc/lightning-multiwithdraw.7 \ doc/lightning-newaddr.7 \ @@ -60,6 +62,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-pay.7 \ doc/lightning-parsefeerate.7 \ doc/lightning-plugin.7 \ + doc/lightning-recoverchannel.7 \ doc/lightning-reserveinputs.7 \ doc/lightning-sendinvoice.7 \ doc/lightning-sendonion.7 \ @@ -69,6 +72,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-setchannelfee.7 \ doc/lightning-sendcustommsg.7 \ doc/lightning-signmessage.7 \ + doc/lightning-staticbackup.7 \ doc/lightning-txprepare.7 \ doc/lightning-txdiscard.7 \ doc/lightning-txsend.7 \ diff --git a/doc/index.rst b/doc/index.rst index abf699ff89d5..3d92e23ac223 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -47,6 +47,7 @@ Core Lightning Documentation lightning-delpay lightning-disableoffer lightning-disconnect + lightning-emergencyrecover lightning-feerates lightning-fetchinvoice lightning-fundchannel @@ -75,6 +76,7 @@ Core Lightning Documentation lightning-listpeers lightning-listsendpays lightning-listtransactions + lightning-makesecret lightning-multifundchannel lightning-multiwithdraw lightning-newaddr @@ -90,6 +92,7 @@ Core Lightning Documentation lightning-pay lightning-ping lightning-plugin + lightning-recoverchannel lightning-reserveinputs lightning-sendcustommsg lightning-sendinvoice @@ -101,6 +104,7 @@ Core Lightning Documentation lightning-setchannelfee lightning-signmessage lightning-signpsbt + lightning-staticbackup lightning-stop lightning-txdiscard lightning-txprepare diff --git a/doc/lightning-emergencyrecover.7.md b/doc/lightning-emergencyrecover.7.md new file mode 100644 index 000000000000..29e582e117a4 --- /dev/null +++ b/doc/lightning-emergencyrecover.7.md @@ -0,0 +1,43 @@ +lightning-emergencyrecover -- Command for recovering channels from the emergency.recovery file in the lightning directory +========================================================================================================================= + +SYNOPSIS +-------- + +**emergencyrecover** + +DESCRIPTION +----------- + +The **emergencyrecover** RPC command fetches data from the emergency.recover +file and tries to reconnect to the peer and force him to close the channel. +The data in this file has enough information to reconnect and sweep the funds. + +This recovery method is not spontaneous and it depends on the peer, so it should +be used as a last resort to recover the funds stored in a channel in case of severe +data loss. + +RETURN VALUE +------------ + +On success, an object is returned, containing: +- **stubs** (array of hexs): + - Each item is the channel ID of the channel successfully inserted + + +AUTHOR +------ + +Aditya <> is mainly responsible. + +SEE ALSO +-------- + +lightning-getsharedsecret(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:9cfaa9eb4609b36accc3e3b12a352c00ddd402307e4461f4df274146d12f6eb0) \ No newline at end of file diff --git a/doc/lightning-makesecret.7.md b/doc/lightning-makesecret.7.md new file mode 100644 index 000000000000..2fdbf9755ae2 --- /dev/null +++ b/doc/lightning-makesecret.7.md @@ -0,0 +1,44 @@ +lightning-makesecret -- Command for deriving pseudorandom key from HSM +===================================================================== + +SYNOPSIS +-------- + +**makesecret** *info_hex* + +DESCRIPTION +----------- + +The **makesecret** RPC command derives a secret key from the HSM_secret. + +The *info_hex* can be any hex data. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **secret** (secret): the pseudorandom key derived from HSM_secret (always 64 characters) + +[comment]: # (GENERATE-FROM-SCHEMA-END) + + +The following error codes may occur: +- -1: Catchall nonspecific error. + +AUTHOR +------ + +Aditya <> is mainly responsible. + +SEE ALSO +-------- + +lightning-getsharedsecret(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:1bd94ffa8440041efafe93440d9828be6baca199b0f5cb73220e4482582bf01d) diff --git a/doc/lightning-recoverchannel.7.md b/doc/lightning-recoverchannel.7.md new file mode 100644 index 000000000000..c1d2e32da78e --- /dev/null +++ b/doc/lightning-recoverchannel.7.md @@ -0,0 +1,45 @@ +lightning-recoverchannel -- Command for recovering channels bundeled in an array in the form of *Static Backup* +=============================================================================================================== + +SYNOPSIS +-------- + +**recoverchannel** *scb* + +DESCRIPTION +----------- + +The **recoverchannel** RPC command tries to force the peer (with whom you +already had a channel) to close the channel and sweeps on-chain fund. This +method is not spontaneous and depends on the peer, so use it in case of +severe data loss. + +The *scb* parameter is an array containing minimum required info to +reconnect and sweep funds. You can get the scb for already stored channels +by using the RPC command 'staticbackup' + + +RETURN VALUE +------------ + +On success, an object is returned, containing: +- **stubs** (array of hexs): + - Each item is the channel ID of the channel successfully inserted + + +AUTHOR +------ + +Aditya <> is mainly responsible. + +SEE ALSO +-------- + +lightning-getsharedsecret(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:9cfaa9eb4609b36accc3e3b12a352c00ddd402307e4461f4df274146d12f6eb0) \ No newline at end of file diff --git a/doc/lightning-staticbackup.7.md b/doc/lightning-staticbackup.7.md new file mode 100644 index 000000000000..8cb5bfa1ceaa --- /dev/null +++ b/doc/lightning-staticbackup.7.md @@ -0,0 +1,38 @@ +lightning-staticbacup -- Command for deriving getting SCB of all the existing channels +====================================================================================== + +SYNOPSIS +-------- + +**staticbackup** + +DESCRIPTION +----------- + +The **staticbackup** RPC command returns an object with SCB of all the channels in an array. + + +RETURN VALUE +------------ + +On success, an object is returned, containing: +- **scb** (array of hexs): + - Each item is SCB of a channel in TLV format + + +AUTHOR +------ + +Aditya <> is mainly responsible. + +SEE ALSO +-------- + +lightning-getsharedsecret(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:9cfaa9eb4609b36accc3e3b12a352c00ddd402307e4461f4df274146d12f6eb0) diff --git a/doc/schemas/emergencyrecover.request.json b/doc/schemas/emergencyrecover.request.json new file mode 100644 index 000000000000..f99496c5ac84 --- /dev/null +++ b/doc/schemas/emergencyrecover.request.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": {} +} diff --git a/doc/schemas/emergencyrecover.schema.json b/doc/schemas/emergencyrecover.schema.json new file mode 100644 index 000000000000..127dfbc6bf43 --- /dev/null +++ b/doc/schemas/emergencyrecover.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "stubs" + ], + "properties": { + "stubs": { + "type": "array", + "items": { + "type": "string", + "description": "Channel IDs of channels successfully inserted." + } + } + } +} diff --git a/doc/schemas/makesecret.request.json b/doc/schemas/makesecret.request.json new file mode 100644 index 000000000000..a43ac493e2bd --- /dev/null +++ b/doc/schemas/makesecret.request.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "info_hex" + ], + "properties": { + "info": { + "type": "hex", + "description": "This will be used for deriving the secret" + } + } +} diff --git a/doc/schemas/makesecret.schema.json b/doc/schemas/makesecret.schema.json new file mode 100644 index 000000000000..ce17c2fcb7cd --- /dev/null +++ b/doc/schemas/makesecret.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "secret" + ], + "properties": { + "secret": { + "type": "secret", + "description": "the pseudorandom key derived from HSM_secret", + "maxLength": 64, + "minLength": 64 + } + } +} diff --git a/doc/schemas/recoverchannel.request.json b/doc/schemas/recoverchannel.request.json new file mode 100644 index 000000000000..ea6701bc2000 --- /dev/null +++ b/doc/schemas/recoverchannel.request.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "scb" + ], + "scb": { + "type": "array", + "description": "SCB of the channels in an array", + "items": { + "type": "hexstr" + } + } +} diff --git a/doc/schemas/recoverchannel.schema.json b/doc/schemas/recoverchannel.schema.json new file mode 100644 index 000000000000..127dfbc6bf43 --- /dev/null +++ b/doc/schemas/recoverchannel.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "stubs" + ], + "properties": { + "stubs": { + "type": "array", + "items": { + "type": "string", + "description": "Channel IDs of channels successfully inserted." + } + } + } +} diff --git a/doc/schemas/staticbackup.request.json b/doc/schemas/staticbackup.request.json new file mode 100644 index 000000000000..f99496c5ac84 --- /dev/null +++ b/doc/schemas/staticbackup.request.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": {} +} diff --git a/doc/schemas/staticbackup.schema.json b/doc/schemas/staticbackup.schema.json new file mode 100644 index 000000000000..73b7009e7311 --- /dev/null +++ b/doc/schemas/staticbackup.schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "scb" + ], + "properties": { + "scb": { + "type": "array", + "items": { + "type": "string", + "description": "SCB of a channel in TLV format" + } + } + } +} diff --git a/tools/fromschema.py b/tools/fromschema.py index 350736f0530c..a8708c5fe215 100755 --- a/tools/fromschema.py +++ b/tools/fromschema.py @@ -127,6 +127,17 @@ def output_members(sub, indent=''): # Remove deprecated and stub properties, collect warnings # (Stubs required to keep additionalProperties: false happy) + + # FIXME: It fails for schemas which have only an array type with + # no properties, ex: + # "abcd": { + # "type": "array", + # "items": { + # "type": "whatever", + # "description": "efgh" + # } + # } + # Checkout the schema of `staticbackup`. for p in list(sub['properties'].keys()): if len(sub['properties'][p]) == 0 or 'deprecated' in sub['properties'][p]: del sub['properties'][p] From 7b160b203a2e6ce656b2cf8d9797e01c306acab6 Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 24 Jun 2022 07:10:04 +0530 Subject: [PATCH 0966/1530] tests: Add tests for the RPCs Changelog-Added: Static channel backup, to enable smooth fund recovery in case of complete data loss --- tests/test_misc.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/test_misc.py b/tests/test_misc.py index 79d74b2553d9..bafd139114e8 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2271,6 +2271,66 @@ def test_getsharedsecret(node_factory): == l2.rpc.getsharedsecret(l1.info["id"])["shared_secret"]) +@pytest.mark.developer("needs --dev-force-privkey") +def test_makesecret(node_factory): + """ + Test makesecret command. + """ + + l1 = node_factory.get_node(options={"dev-force-privkey": "1212121212121212121212121212121212121212121212121212121212121212"}) + secret = l1.rpc.makesecret("73636220736563726574")["secret"] + + assert (secret == "04fe01631fcedc8d91f39ab43244e63afebaed68ee21d2f1c325fd1242726a18") + + +def test_staticbackup(node_factory): + """ + Test staticbackup + """ + l1, l2 = node_factory.get_nodes(2, opts=[{}, {}]) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + c12, _ = l1.fundchannel(l2, 10**5) + + # Comparing the channelID, scb_chan has the channel ID starting from the 8th byte + # and it's own length is 32 byte, hence 16 + 64. + assert (len(l1.rpc.staticbackup()["scb"]) == 1 + and l1.rpc.staticbackup()["scb"][0][16: 16 + 64] == _["channel_id"]) + + +def test_recoverchannel(node_factory): + """ + Test recoverchannel + """ + l1 = node_factory.get_node() + stubs = l1.rpc.recoverchannel(["0000000000000001c3a7b9d74a174497122bc52d74d6d69836acadc77e0429c6d8b68b48d5c9139a022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d5904017f0000019f0bc3a7b9d74a174497122bc52d74d6d69836acadc77e0429c6d8b68b48d5c9139a0000000000000000000186a000021000"])["stubs"] + + assert len(stubs) == 1 + assert stubs[0] == "c3a7b9d74a174497122bc52d74d6d69836acadc77e0429c6d8b68b48d5c9139a" + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "deletes database, which is assumed sqlite3") +def test_emergencyrecover(node_factory, bitcoind): + """ + Test emergencyrecover + """ + l1, l2 = node_factory.get_nodes(2, opts=[{}, {}]) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + c12, _ = l1.fundchannel(l2, 10**5) + + l1.stop() + + os.unlink(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "lightningd.sqlite3")) + + l1.start() + assert l1.daemon.is_in_log('Server started with public key') + stubs = l1.rpc.emergencyrecover()["stubs"] + assert len(stubs) == 1 + assert stubs[0] == _["channel_id"] + + listfunds = l1.rpc.listfunds()["channels"][0] + assert listfunds["short_channel_id"] == "1x1x1" + + def test_commitfee_option(node_factory): """Sanity check for the --commit-fee startup option.""" l1, l2 = node_factory.get_nodes(2, opts=[{"commit-fee": "200"}, {}]) From d544ae075b1b79dcd7826c8c45340f3dc4999e86 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 14 Jul 2022 11:52:12 +0930 Subject: [PATCH 0967/1530] pytest: test (failing) that we rexmit closing transactions on startup. Usually bitcoind will have them, but it's best to re-xmit. Signed-off-by: Rusty Russell --- tests/test_closing.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index c2214388878e..d410c2147b37 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3695,3 +3695,26 @@ def test_onchain_close_upstream(node_factory, bitcoind): with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE \(reply from remote\)'): l1.rpc.waitsendpay(ph1, timeout=TIMEOUT) + + +@pytest.mark.xfail(strict=True) +def test_onchain_rexmit_tx(node_factory, bitcoind): + """Make sure we re-xmit last tx if we restart and channel is AWAITING_UNILATERAL""" + l1, l2 = node_factory.line_graph(2) + + def ignore_sendrawtx(r): + return {'id': r['id'], 'result': {}} + + l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', ignore_sendrawtx) + + l2.stop() + l1.rpc.close(l2.info['id'], unilateraltimeout=1) + + wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['state'] == 'AWAITING_UNILATERAL') + l1.stop() + + assert bitcoind.rpc.getrawmempool() == [] + l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', None) + + l1.start() + wait_for(lambda: len(bitcoind.rpc.getrawmempool()) == 1) From bdefbabbeff96ca22e8281cd964de22cf8894d3a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 14 Jul 2022 17:11:11 +0930 Subject: [PATCH 0968/1530] lightningd: re-transmit all closing transactions on startup. Signed-off-by: Rusty Russell --- lightningd/lightningd.c | 6 ++++++ lightningd/peer_control.c | 15 +++++++++++++++ lightningd/peer_control.h | 3 +++ lightningd/test/run-find_my_abspath.c | 3 +++ tests/test_closing.py | 1 - 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index e54610f0095c..e6b70c675498 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1153,6 +1153,12 @@ int main(int argc, char *argv[]) "/dev/fd (if running in chroot) if you are " "approaching that many channels."); + /*~ If we have channels closing, make sure we re-xmit the last + * transaction, in case bitcoind lost it. */ + db_begin_transaction(ld->wallet->db); + resend_closing_transactions(ld); + db_commit_transaction(ld->wallet->db); + /*~ This is where we ask connectd to reconnect to any peers who have * live channels with us, and makes sure we're watching the funding * tx. */ diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4a0b4c8a08cd..bfd2e5af6f99 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -288,6 +288,21 @@ void drop_to_chain(struct lightningd *ld, struct channel *channel, resolve_close_command(ld, channel, cooperative); } +void resend_closing_transactions(struct lightningd *ld) +{ + struct peer *peer; + struct channel *channel; + + list_for_each(&ld->peers, peer, list) { + list_for_each(&peer->channels, channel, list) { + if (channel->state == CLOSINGD_COMPLETE) + drop_to_chain(ld, channel, true); + else if (channel->state == AWAITING_UNILATERAL) + drop_to_chain(ld, channel, false); + } + } +} + void channel_errmsg(struct channel *channel, struct peer_fd *peer_fd, const struct channel_id *channel_id UNUSED, diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 4e9eb5b5d9a8..be631a8ea31c 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -89,6 +89,9 @@ u8 *p2wpkh_for_keyidx(const tal_t *ctx, struct lightningd *ld, u64 keyidx); /* We've loaded peers from database, set them going. */ void setup_peers(struct lightningd *ld); +/* At startup, re-send any transactions we want bitcoind to have */ +void resend_closing_transactions(struct lightningd *ld); + void drop_to_chain(struct lightningd *ld, struct channel *channel, bool cooperative); void channel_watch_funding(struct lightningd *ld, struct channel *channel); diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 0a25f7c14f88..b8d9fed13c1c 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -185,6 +185,9 @@ struct plugins *plugins_new(const tal_t *ctx UNNEEDED, struct log_book *log_book void plugins_set_builtin_plugins_dir(struct plugins *plugins UNNEEDED, const char *dir UNNEEDED) { fprintf(stderr, "plugins_set_builtin_plugins_dir called!\n"); abort(); } +/* Generated stub for resend_closing_transactions */ +void resend_closing_transactions(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "resend_closing_transactions called!\n"); abort(); } /* Generated stub for setup_color_and_alias */ void setup_color_and_alias(struct lightningd *ld UNNEEDED) { fprintf(stderr, "setup_color_and_alias called!\n"); abort(); } diff --git a/tests/test_closing.py b/tests/test_closing.py index d410c2147b37..70b989987b85 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3697,7 +3697,6 @@ def test_onchain_close_upstream(node_factory, bitcoind): l1.rpc.waitsendpay(ph1, timeout=TIMEOUT) -@pytest.mark.xfail(strict=True) def test_onchain_rexmit_tx(node_factory, bitcoind): """Make sure we re-xmit last tx if we restart and channel is AWAITING_UNILATERAL""" l1, l2 = node_factory.line_graph(2) From d284b98911f45639d5e60aab58b50cb5c9f536d1 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 6 Jul 2022 10:01:58 -0500 Subject: [PATCH 0969/1530] notify: `channel_state_changed` now receives notice when channel opens Previously we wouldn't notify when a channel moves into state "CHANNELD_AWAITING_LOCKIN", as this is the original state (so there's no movement btw states). This meant that it's impossible to track when a channel's commitment txs have been exchanged and we're waiting for onchain confirmation. It's useful to have notice of this initialization though, all in one place so that the `channel_state_changed` notification can successfully track the entire lifecycle of a channel, from inception to close. Note that for v2 "dual-funded" channels, we already notify at the same place, at "DUALOPEND_AWAITING_LOCKIN" (the initial state for a dualopend channel is "DUALOPEND_OPEN_INIT" -- this is the only state we don't get notified at now...) Changelog-Added: Plugins: `channel_state_changed` now triggers for a v1 channel's initial "CHANNELD_AWAITING_LOCKIN" state transition (from prior state "unknown") --- lightningd/opening_control.c | 12 ++++++++ tests/test_plugin.py | 59 +++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index c2f02819ff9e..ab656fc82086 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -102,6 +102,7 @@ wallet_commit_channel(struct lightningd *ld, u64 static_remotekey_start; u32 lease_start_blockheight = 0; /* No leases on v1 */ struct short_channel_id *alias_local; + struct timeabs timestamp; /* We cannot both be the fundee *and* have a `fundchannel_start` * command running! @@ -226,6 +227,17 @@ wallet_commit_channel(struct lightningd *ld, /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); + /* Notify that channel state changed (from non existant to existant) */ + timestamp = time_now(); + notify_channel_state_changed(ld, &channel->peer->id, + &channel->cid, + channel->scid, /* NULL */ + ×tamp, + 0, /* No prior state */ + channel->state, + channel->state_change_cause, + "new channel opened"); + return channel; } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 2e8a417b8ec3..214dadf09197 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -777,27 +777,29 @@ def wait_for_event(node): event1 = wait_for_event(l1) event2 = wait_for_event(l2) - if l1.config('experimental-dual-fund'): - # Dual funded channels have an extra state change - assert(event1['peer_id'] == l2_id) # we only test these IDs the first time - assert(event1['channel_id'] == cid) - assert(event1['short_channel_id'] is None) - assert(event1['old_state'] == "DUALOPEND_OPEN_INIT") - assert(event1['new_state'] == "DUALOPEND_AWAITING_LOCKIN") - assert(event1['cause'] == "user") - assert(event1['message'] == "Sigs exchanged, waiting for lock-in") - event1 = wait_for_event(l1) - assert(event2['peer_id'] == l1_id) # we only test these IDs the first time - assert(event2['channel_id'] == cid) - assert(event2['short_channel_id'] is None) - assert(event2['old_state'] == "DUALOPEND_OPEN_INIT") - assert(event2['new_state'] == "DUALOPEND_AWAITING_LOCKIN") - assert(event2['cause'] == "remote") - assert(event2['message'] == "Sigs exchanged, waiting for lock-in") - event2 = wait_for_event(l2) - assert(event1['peer_id'] == l2_id) # we only test these IDs the first time assert(event1['channel_id'] == cid) + assert(event1['short_channel_id'] is None) # None until locked in + assert(event1['cause'] == "user") + + assert(event2['peer_id'] == l1_id) # we only test these IDs the first time + assert(event2['channel_id'] == cid) + assert(event2['short_channel_id'] is None) # None until locked in + assert(event2['cause'] == "remote") + + for ev in [event1, event2]: + # Dual funded channels + if l1.config('experimental-dual-fund'): + assert(ev['old_state'] == "DUALOPEND_OPEN_INIT") + assert(ev['new_state'] == "DUALOPEND_AWAITING_LOCKIN") + assert(ev['message'] == "Sigs exchanged, waiting for lock-in") + else: + assert(ev['old_state'] == "unknown") + assert(ev['new_state'] == "CHANNELD_AWAITING_LOCKIN") + assert(ev['message'] == "new channel opened") + + event1 = wait_for_event(l1) + event2 = wait_for_event(l2) assert(event1['short_channel_id'] == scid) if l1.config('experimental-dual-fund'): assert(event1['old_state'] == "DUALOPEND_AWAITING_LOCKIN") @@ -807,8 +809,6 @@ def wait_for_event(node): assert(event1['cause'] == "user") assert(event1['message'] == "Lockin complete") - assert(event2['peer_id'] == l1_id) - assert(event2['channel_id'] == cid) assert(event2['short_channel_id'] == scid) if l1.config('experimental-dual-fund'): assert(event2['old_state'] == "DUALOPEND_AWAITING_LOCKIN") @@ -911,18 +911,21 @@ def wait_for_event(node): return event event2 = wait_for_event(l2) + assert(event2['peer_id'] == l1_id) + assert(event2['channel_id'] == cid) + assert(event2['short_channel_id'] is None) + assert(event2['cause'] == "remote") + if l2.config('experimental-dual-fund'): - assert(event2['peer_id'] == l1_id) - assert(event2['channel_id'] == cid) - assert(event2['short_channel_id'] is None) assert(event2['old_state'] == "DUALOPEND_OPEN_INIT") assert(event2['new_state'] == "DUALOPEND_AWAITING_LOCKIN") - assert(event2['cause'] == "remote") assert(event2['message'] == "Sigs exchanged, waiting for lock-in") - event2 = wait_for_event(l2) + else: + assert(event2['old_state'] == "unknown") + assert(event2['new_state'] == "CHANNELD_AWAITING_LOCKIN") + assert(event2['message'] == "new channel opened") - assert(event2['peer_id'] == l1_id) # we only test these IDs the first time - assert(event2['channel_id'] == cid) + event2 = wait_for_event(l2) assert(event2['short_channel_id'] == scid) if l2.config('experimental-dual-fund'): assert(event2['old_state'] == "DUALOPEND_AWAITING_LOCKIN") From b624c530515b3966ae08b740eac35bbec19da5de Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Sat, 25 Jun 2022 23:44:02 +0000 Subject: [PATCH 0970/1530] plugin: add check on the type json object during the IO message handling --- lightningd/plugin.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 07bb6fa01836..9dcbd3aacbf2 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -643,6 +643,11 @@ static const char *plugin_read_json_one(struct plugin *plugin, return NULL; } + if (plugin->toks->type != JSMN_OBJECT) + return tal_fmt( + plugin, + "JSON-RPC message is not a valid JSON object type"); + jrtok = json_get_member(plugin->buffer, plugin->toks, "jsonrpc"); idtok = json_get_member(plugin->buffer, plugin->toks, "id"); From 5ef6729224fba4937132e3cf33ac34d522cf2a6c Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Thu, 14 Jul 2022 21:32:58 +0700 Subject: [PATCH 0971/1530] Use correct naming --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a1a089e02b8..e445bbbdab8a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Don't hesitate to reach out to us on IRC at [#lightning-dev @ libera.chat][irc1] ## Getting Started -Core Lightning only works on Linux and Mac OS, and requires a locally (or remotely) running `bitcoind` (version 0.16 or above) that is fully caught up with the network you're running on, and relays transactions (ie with `blocksonly=0`). +Core Lightning only works on Linux and macOS, and requires a locally (or remotely) running `bitcoind` (version 0.16 or above) that is fully caught up with the network you're running on, and relays transactions (ie with `blocksonly=0`). Pruning (`prune=n` option in `bitcoin.conf`) is partially supported, see [here](#pruning) for more details. ### Installation @@ -40,7 +40,7 @@ Pruning (`prune=n` option in `bitcoin.conf`) is partially supported, see [here]( There are 4 supported installation options: - Installation from the [Ubuntu PPA][ppa]. - - Installation of a pre-compiled binary from the [release page][releases] on Github. + - Installation of a pre-compiled binary from the [release page][releases] on GitHub. - Using one of the [provided docker images][dockerhub] on the Docker Hub. - Compiling the source code yourself as described in the [installation documentation](doc/INSTALL.md). From 99367dbb3229adda2f114cf6b8caf6ebd4965aeb Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 12 Jul 2022 18:12:41 +0200 Subject: [PATCH 0972/1530] make: Add msggen generated files to check-gen-updated target Changelog-None --- .msggen.json | 3 ++- Makefile | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.msggen.json b/.msggen.json index 5f0ba8012904..bd58e28dcc07 100644 --- a/.msggen.json +++ b/.msggen.json @@ -1,3 +1,4 @@ + { "grpc-enum-map": { "CloseType": { @@ -1076,4 +1077,4 @@ "Withdraw.txid": 2 } } -} \ No newline at end of file +} diff --git a/Makefile b/Makefile index 3dde35d9cda0..ec175f4da940 100644 --- a/Makefile +++ b/Makefile @@ -571,7 +571,18 @@ full-check: check check-source # # Do not run on your development tree since it will complain if you # have a dirty tree. -check-gen-updated: $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES) wallet/statements_gettextgen.po $(MANPAGES) +CHECK_GEN_ALL = \ + $(CLN_GRPC_GENALL) \ + $(CLN_RPC_GENALL) \ + $(MANPAGES) \ + $(WALLET_DB_QUERIES) \ + $(PYTHON_GENERATED) \ + $(ALL_GEN_HEADERS) \ + $(ALL_GEN_SOURCES) \ + wallet/statements_gettextgen.po \ + .msggen.json + +check-gen-updated: $(CHECK_GEN_ALL) @echo "Checking for generated files being changed by make" git diff --exit-code HEAD $? From 1a9979784f62c35490486f74e2e1ba27352c36c4 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 13 Jul 2022 16:50:33 +0200 Subject: [PATCH 0973/1530] contrib: Remove obsolete libhsmd_python class This has been replaced with better rust bindings that can then be consumed via pyo3, consolidating the C interface in a portable wrapper. Changelog-Removed: libhsmd: Removed the `libhsmd_python` wrapper as it was unused --- Makefile | 2 +- contrib/libhsmd_python/.gitignore | 4 - contrib/libhsmd_python/MANIFEST.in | 1 - contrib/libhsmd_python/Makefile | 12 - contrib/libhsmd_python/README.md | 0 contrib/libhsmd_python/libhsmd.py | 111 - contrib/libhsmd_python/libhsmd_python.c | 74 - contrib/libhsmd_python/libhsmd_python.h | 8 - contrib/libhsmd_python/setup.py | 246 -- contrib/libhsmd_python/shims.c | 9 - contrib/libhsmd_python/swig.i | 7 - contrib/libhsmd_python/swig_wrap.c | 4914 ----------------------- contrib/libhsmd_python/test_libhsmd.py | 14 - 13 files changed, 1 insertion(+), 5401 deletions(-) delete mode 100644 contrib/libhsmd_python/.gitignore delete mode 100644 contrib/libhsmd_python/MANIFEST.in delete mode 100644 contrib/libhsmd_python/Makefile delete mode 100644 contrib/libhsmd_python/README.md delete mode 100644 contrib/libhsmd_python/libhsmd.py delete mode 100644 contrib/libhsmd_python/libhsmd_python.c delete mode 100644 contrib/libhsmd_python/libhsmd_python.h delete mode 100644 contrib/libhsmd_python/setup.py delete mode 100644 contrib/libhsmd_python/shims.c delete mode 100644 contrib/libhsmd_python/swig.i delete mode 100644 contrib/libhsmd_python/swig_wrap.c delete mode 100644 contrib/libhsmd_python/test_libhsmd.py diff --git a/Makefile b/Makefile index ec175f4da940..85657e8fdf84 100644 --- a/Makefile +++ b/Makefile @@ -370,7 +370,7 @@ include devtools/Makefile include tools/Makefile include plugins/Makefile include tests/plugins/Makefile -include contrib/libhsmd_python/Makefile + ifneq ($(FUZZING),0) include tests/fuzz/Makefile endif diff --git a/contrib/libhsmd_python/.gitignore b/contrib/libhsmd_python/.gitignore deleted file mode 100644 index 22d25a8a23b7..000000000000 --- a/contrib/libhsmd_python/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -dist -build -src -libhsmd.egg-info diff --git a/contrib/libhsmd_python/MANIFEST.in b/contrib/libhsmd_python/MANIFEST.in deleted file mode 100644 index 17aca0c53820..000000000000 --- a/contrib/libhsmd_python/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -global-exclude src diff --git a/contrib/libhsmd_python/Makefile b/contrib/libhsmd_python/Makefile deleted file mode 100644 index af77f09cc20c..000000000000 --- a/contrib/libhsmd_python/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/make - -LIBHSMD_PY_GEN_FILES := contrib/libhsmd_python/swig_wrap.c \ - contrib/libhsmd_python/libhsmd.py - -PYTHON_GENERATED += contrib/libhsmd_python/libhsmd.py -CPPCHECK_OPTS += --suppress=nullPointer:contrib/libhsmd_python/swig_wrap.c - -# Swig by default generates stubs in the file's directory, which is -# what we want. -$(LIBHSMD_PY_GEN_FILES): contrib/libhsmd_python/swig.i $(HSMD_SRC) - swig -python -builtin contrib/libhsmd_python/swig.i diff --git a/contrib/libhsmd_python/README.md b/contrib/libhsmd_python/README.md deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/contrib/libhsmd_python/libhsmd.py b/contrib/libhsmd_python/libhsmd.py deleted file mode 100644 index 442d64bc8e40..000000000000 --- a/contrib/libhsmd_python/libhsmd.py +++ /dev/null @@ -1,111 +0,0 @@ -# This file was automatically generated by SWIG (http://www.swig.org). -# Version 3.0.12 -# -# Do not make changes to this file unless you know what you are doing--modify -# the SWIG interface file instead. - -from sys import version_info as _swig_python_version_info -if _swig_python_version_info >= (2, 7, 0): - def swig_import_helper(): - import importlib - pkg = __name__.rpartition('.')[0] - mname = '.'.join((pkg, '_libhsmd')).lstrip('.') - try: - return importlib.import_module(mname) - except ImportError: - return importlib.import_module('_libhsmd') - _libhsmd = swig_import_helper() - del swig_import_helper -elif _swig_python_version_info >= (2, 6, 0): - def swig_import_helper(): - from os.path import dirname - import imp - fp = None - try: - fp, pathname, description = imp.find_module('_libhsmd', [dirname(__file__)]) - except ImportError: - import _libhsmd - return _libhsmd - try: - _mod = imp.load_module('_libhsmd', fp, pathname, description) - finally: - if fp is not None: - fp.close() - return _mod - _libhsmd = swig_import_helper() - del swig_import_helper -else: - import _libhsmd -# pull in all the attributes from _libhsmd -if __name__.rpartition('.')[0] != '': - if _swig_python_version_info >= (2, 7, 0): - try: - from ._libhsmd import * - except ImportError: - from _libhsmd import * - else: - from _libhsmd import * -else: - from _libhsmd import * -del _swig_python_version_info - -try: - _swig_property = property -except NameError: - pass # Python < 2.2 doesn't have 'property'. - -try: - import builtins as __builtin__ -except ImportError: - import __builtin__ - -def _swig_setattr_nondynamic(self, class_type, name, value, static=1): - if (name == "thisown"): - return self.this.own(value) - if (name == "this"): - if type(value).__name__ == 'SwigPyObject': - self.__dict__[name] = value - return - method = class_type.__swig_setmethods__.get(name, None) - if method: - return method(self, value) - if (not static): - if _newclass: - object.__setattr__(self, name, value) - else: - self.__dict__[name] = value - else: - raise AttributeError("You cannot add attributes to %s" % self) - - -def _swig_setattr(self, class_type, name, value): - return _swig_setattr_nondynamic(self, class_type, name, value, 0) - - -def _swig_getattr(self, class_type, name): - if (name == "thisown"): - return self.this.own() - method = class_type.__swig_getmethods__.get(name, None) - if method: - return method(self) - raise AttributeError("'%s' object has no attribute '%s'" % (class_type.__name__, name)) - - -def _swig_repr(self): - try: - strthis = "proxy of " + self.this.__repr__() - except __builtin__.Exception: - strthis = "" - return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) - -try: - _object = object - _newclass = 1 -except __builtin__.Exception: - class _object: - pass - _newclass = 0 - -# This file is compatible with both classic and new-style classes. - - diff --git a/contrib/libhsmd_python/libhsmd_python.c b/contrib/libhsmd_python/libhsmd_python.c deleted file mode 100644 index 05eb80895451..000000000000 --- a/contrib/libhsmd_python/libhsmd_python.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "libhsmd_python.h" -#include -#include - -char *init(char *hex_hsm_secret, char *network_name) { - const struct bip32_key_version *key_version; - struct secret sec; - u8 *response; - common_setup(NULL); - if (sodium_init() == -1) { - fprintf( - stderr, - "Could not initialize libsodium. Maybe not enough entropy" - " available ?"); - return NULL; - } - - wally_init(0); - secp256k1_ctx = wally_get_secp_context(); - - sodium_mlock(&sec, sizeof(sec)); - if (!hex_decode(hex_hsm_secret, strlen(hex_hsm_secret), sec.data, - sizeof(sec.data))) { - fprintf(stderr, - "Expected hex_hsm_secret of length 64, got %zu\n", - strlen(hex_hsm_secret)); - return NULL; - } - - /* Look up chainparams by their name */ - chainparams = chainparams_for_network(network_name); - if (chainparams == NULL) { - fprintf(stderr, "Could not find chainparams for network %s\n", - network_name); - return NULL; - } - - key_version = &chainparams->bip32_key_version; - - response = hsmd_init(sec, *key_version); - sodium_munlock(&sec, sizeof(sec)); - - char *res = tal_hex(NULL, response); - tal_free(response); - return res; -} - -char *handle(long long cap, long long dbid, char *peer_id, char *hexmsg) { - size_t res_len; - u8 *response, *request; - char *res; - struct hsmd_client *client; - struct node_id *peer = NULL; - request = tal_hexdata(tmpctx, hexmsg, strlen(hexmsg)); - if (peer_id != NULL) { - peer = tal(tmpctx, struct node_id); - 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); - } - response = hsmd_handle_client_message(tmpctx, client, request); - if (response == NULL) { - clean_tmpctx(); - return NULL; - } - - res_len = hex_str_size(tal_bytelen(response)); - res = malloc(res_len); - hex_encode(response, tal_bytelen(response), res, res_len); - - clean_tmpctx(); - return res; -} diff --git a/contrib/libhsmd_python/libhsmd_python.h b/contrib/libhsmd_python/libhsmd_python.h deleted file mode 100644 index 301d18ddd282..000000000000 --- a/contrib/libhsmd_python/libhsmd_python.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef LIGHTNING_CONTRIB_LIBHSMD_PYTHON_LIBHSMD_PYTHON_H -#define LIGHTNING_CONTRIB_LIBHSMD_PYTHON_LIBHSMD_PYTHON_H - -#include -char *handle(long long cap, long long dbid, char *peer_id, char *msg); -char *init(char *hex_hsm_secret, char *network_name); - -#endif /* LIGHTNING_CONTRIB_LIBHSMD_PYTHON_LIBHSMD_PYTHON_H */ diff --git a/contrib/libhsmd_python/setup.py b/contrib/libhsmd_python/setup.py deleted file mode 100644 index 8f43b6c4f201..000000000000 --- a/contrib/libhsmd_python/setup.py +++ /dev/null @@ -1,246 +0,0 @@ -#!/usr/bin/env python - -import os -import pathlib -import subprocess - -from setuptools import Extension, setup -from setuptools.command.build_ext import build_ext as build_ext_orig - - -cwd = pathlib.Path(os.path.dirname(__file__)) - - -class ClExtension(Extension): - def __init__(self, name, **kwargs): - # don't invoke the original build_ext for this special extension - super().__init__(name, **kwargs) - - -# The directory we compile external depencies is architecture specific. -external_target = pathlib.Path("external") / subprocess.check_output( - ["gcc", "-dumpmachine"] -).strip().decode("ASCII") - - -class build_ext(build_ext_orig): - def run(self): - for ext in self.extensions: - self.build_make(ext) - super().run() - - def build_make(self, ext): - cwd = pathlib.Path().absolute() - srcdir = cwd / "src" - - if not srcdir.exists(): - subprocess.check_call( - [ - "git", - "clone", - "--recursive", - '--branch=libhsmd-python', - "https://github.com/cdecker/lightning.git", - "src", - ], - cwd=cwd, - ) - - 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) - - -# Absolute include dirs which we will later expand to full paths. -include_dirs = [ - ".", - "ccan/", - f"{external_target}/libbacktrace-build/", - "external/libbacktrace/", - "external/libsodium/src/libsodium/include/sodium/", - "external/libwally-core/", - "external/libwally-core/include/", - "external/libwally-core/src/", - "external/libwally-core/src/ccan/", - "external/libwally-core/src/secp256k1/", - "external/libwally-core/src/secp256k1/include/", - "external/libwally-core/src/secp256k1/src", - 'contrib/libhsmd_python/', -] - -sources = [ - "bitcoin/block.c", - "bitcoin/chainparams.c", - "bitcoin/preimage.c", - "bitcoin/privkey.c", - "bitcoin/psbt.c", - "bitcoin/pubkey.c", - "bitcoin/script.c", - "bitcoin/shadouble.c", - "bitcoin/short_channel_id.c", - "bitcoin/signature.c", - "bitcoin/tx.c", - "bitcoin/varint.c", - "ccan/ccan/breakpoint/breakpoint.c", - "ccan/ccan/crypto/hkdf_sha256/hkdf_sha256.c", - "ccan/ccan/crypto/hmac_sha256/hmac_sha256.c", - "ccan/ccan/crypto/shachain/shachain.c", - "ccan/ccan/crypto/siphash24/siphash24.c", - "ccan/ccan/err/err.c", - "ccan/ccan/fdpass/fdpass.c", - "ccan/ccan/htable/htable.c", - "ccan/ccan/intmap/intmap.c", - "ccan/ccan/io/fdpass/fdpass.c", - "ccan/ccan/io/io.c", - "ccan/ccan/io/poll.c", - "ccan/ccan/isaac/isaac64.c", - "ccan/ccan/list/list.c", - "ccan/ccan/noerr/noerr.c", - "ccan/ccan/ptr_valid/ptr_valid.c", - "ccan/ccan/read_write_all/read_write_all.c", - "ccan/ccan/str/hex/hex.c", - "ccan/ccan/take/take.c", - "ccan/ccan/tal/str/str.c", - "ccan/ccan/tal/tal.c", - "ccan/ccan/time/time.c", - "ccan/ccan/timer/timer.c", - "ccan/ccan/utf8/utf8.c", - "common/amount.c", - "common/autodata.c", - "common/bigsize.c", - "common/bip32.c", - "common/bolt12_merkle.c", - "common/channel_id.c", - "common/daemon.c", - "common/daemon_conn.c", - "common/derive_basepoints.c", - "common/hash_u5.c", - "common/hsm_encryption.c", - "common/key_derive.c", - "common/memleak.c", - "common/msg_queue.c", - "common/node_id.c", - "common/pseudorand.c", - "common/setup.c", - "common/status.c", - "common/status_levels.c", - "common/status_wire.c", - "common/status_wiregen.c", - "common/subdaemon.c", - "common/type_to_string.c", - "common/utils.c", - "common/utxo.c", - "common/version.c", - "contrib/libhsmd_python/shims.c", - "contrib/libhsmd_python/swig_wrap.c", - "external/libbacktrace/alloc.c", - "external/libbacktrace/backtrace.c", - "external/libbacktrace/fileline.c", - "external/libbacktrace/posix.c", - "external/libbacktrace/print.c", - "external/libbacktrace/simple.c", - "external/libbacktrace/state.c", - "external/libbacktrace/unknown.c", - "external/libsodium/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c", - "external/libsodium/src/libsodium/crypto_core/hchacha20/core_hchacha20.c", - "external/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c", - "external/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c", - "external/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c", - "external/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c", - "external/libsodium/src/libsodium/crypto_onetimeauth/poly1305/donna/poly1305_donna.c", - "external/libsodium/src/libsodium/crypto_onetimeauth/poly1305/onetimeauth_poly1305.c", - "external/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-core.c", - "external/libsodium/src/libsodium/crypto_pwhash/argon2/argon2-fill-block-ref.c", - "external/libsodium/src/libsodium/crypto_pwhash/argon2/blake2b-long.c", - "external/libsodium/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c", - "external/libsodium/src/libsodium/crypto_scalarmult/curve25519/scalarmult_curve25519.c", - "external/libsodium/src/libsodium/crypto_secretstream/xchacha20poly1305/secretstream_xchacha20poly1305.c", - "external/libsodium/src/libsodium/crypto_stream/chacha20/ref/chacha20_ref.c", - "external/libsodium/src/libsodium/crypto_stream/chacha20/stream_chacha20.c", - "external/libsodium/src/libsodium/crypto_stream/salsa20/ref/salsa20_ref.c", - "external/libsodium/src/libsodium/crypto_stream/salsa20/stream_salsa20.c", - "external/libsodium/src/libsodium/crypto_verify/sodium/verify.c", - "external/libsodium/src/libsodium/randombytes/randombytes.c", - "external/libsodium/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c", - "external/libsodium/src/libsodium/sodium/core.c", - "external/libsodium/src/libsodium/sodium/runtime.c", - "external/libsodium/src/libsodium/sodium/utils.c", - "external/libwally-core/src/base58.c", - "external/libwally-core/src/bip32.c", - "external/libwally-core/src/ccan/ccan/base64/base64.c", - "external/libwally-core/src/ccan/ccan/crypto/ripemd160/ripemd160.c", - "external/libwally-core/src/ccan/ccan/crypto/sha256/sha256.c", - "external/libwally-core/src/ccan/ccan/crypto/sha512/sha512.c", - "external/libwally-core/src/hex.c", - "external/libwally-core/src/hmac.c", - "external/libwally-core/src/internal.c", - "external/libwally-core/src/psbt.c", - "external/libwally-core/src/pullpush.c", - "external/libwally-core/src/script.c", - "external/libwally-core/src/secp256k1/src/secp256k1.c", - "external/libwally-core/src/sign.c", - "external/libwally-core/src/transaction.c", - "hsmd/hsmd_wiregen.c", - "hsmd/libhsmd.c", - "hsmd/libhsmd_status.c", - "contrib/libhsmd_python/libhsmd_python.c", - "wire/fromwire.c", - "wire/peer_wire.c", - "wire/peer_wiregen.c", - "wire/tlvstream.c", - "wire/towire.c", - "wire/wire_io.c", - "wire/wire_sync.c", -] - -include_dirs = [os.path.join("src", f) for f in include_dirs] -sources = [os.path.join("src", f) for f in sources] - -configtuples = [] -if pathlib.Path('src/config.vars').exists(): - configvars = open("src/config.vars", "r").readlines() - configtuples = [tuple(v.strip().split("=", 1)) for v in configvars] - -libhsmd_module = ClExtension( - "_libhsmd", - libraries=["sodium"], - include_dirs=include_dirs, - define_macros=configtuples - + [ - ("BUILD_ELEMENTS", "1"), - ("SHACHAIN_BITS", "48"), - ("USE_NUM_NONE", "1"), - ("ECMULT_WINDOW_SIZE", "15"), - ("ECMULT_GEN_PREC_BITS", "4"), - ("USE_SCALAR_INV_BUILTIN", "1"), - ("USE_FIELD_INV_BUILTIN", "1"), - ("ENABLE_MODULE_EXTRAKEYS", "1"), - ("ENABLE_MODULE_RECOVERY", "1"), - ("ENABLE_MODULE_SCHNORRSIG", "1"), - ("ENABLE_MODULE_ECDH", "1"), - ], - sources=sources, -) - -setup( - name="libhsmd", - version="0.10.0", - author="Christian Decker", - author_email="cdecker@blockstream.com", - description="""Python wrapper to the libhsmd library""", - url="https://github.com/ElementsProject/lightning/tree/master/contrib/libhsmd_python/", - ext_modules=[libhsmd_module], - py_modules=["libhsmd"], - cmdclass={ - "build_ext": build_ext, - }, - long_description=open(cwd / "README.md", "r").read(), - long_description_content_type="text/markdown", - license="BSD-MIT" -) diff --git a/contrib/libhsmd_python/shims.c b/contrib/libhsmd_python/shims.c deleted file mode 100644 index a837ca285d58..000000000000 --- a/contrib/libhsmd_python/shims.c +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include - -/* The following functions are for some reason referenced but not - * included in the library. We provide them with dummy implementations - * here. */ -bool alignment_ok(void *p) { return true; } -void dev_disconnect_init(int fd) {} -void CCAN_CLEAR_MEMORY(void *p, size_t len) { wally_clear(p, len); } diff --git a/contrib/libhsmd_python/swig.i b/contrib/libhsmd_python/swig.i deleted file mode 100644 index e922345f1025..000000000000 --- a/contrib/libhsmd_python/swig.i +++ /dev/null @@ -1,7 +0,0 @@ -%module libhsmd -%{ -#define SWIG_FILE_WITH_INIT -#include "libhsmd_python.h" -%} - -%include "libhsmd_python.h" diff --git a/contrib/libhsmd_python/swig_wrap.c b/contrib/libhsmd_python/swig_wrap.c deleted file mode 100644 index a6aea2fcace1..000000000000 --- a/contrib/libhsmd_python/swig_wrap.c +++ /dev/null @@ -1,4914 +0,0 @@ -/* ---------------------------------------------------------------------------- - * This file was automatically generated by SWIG (http://www.swig.org). - * Version 3.0.12 - * - * This file is not intended to be easily readable and contains a number of - * coding conventions designed to improve portability and efficiency. Do not make - * changes to this file unless you know what you are doing--modify the SWIG - * interface file instead. - * ----------------------------------------------------------------------------- */ - - -#ifndef SWIGPYTHON -#define SWIGPYTHON -#endif - -#define SWIG_PYTHON_DIRECTOR_NO_VTABLE -#define SWIGPYTHON_BUILTIN - -/* ----------------------------------------------------------------------------- - * This section contains generic SWIG labels for method/variable - * declarations/attributes, and other compiler dependent labels. - * ----------------------------------------------------------------------------- */ - -/* template workaround for compilers that cannot correctly implement the C++ standard */ -#ifndef SWIGTEMPLATEDISAMBIGUATOR -# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) -# define SWIGTEMPLATEDISAMBIGUATOR template -# elif defined(__HP_aCC) -/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ -/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ -# define SWIGTEMPLATEDISAMBIGUATOR template -# else -# define SWIGTEMPLATEDISAMBIGUATOR -# endif -#endif - -/* inline attribute */ -#ifndef SWIGINLINE -# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) -# define SWIGINLINE inline -# else -# define SWIGINLINE -# endif -#endif - -/* attribute recognised by some compilers to avoid 'unused' warnings */ -#ifndef SWIGUNUSED -# if defined(__GNUC__) -# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -# elif defined(__ICC) -# define SWIGUNUSED __attribute__ ((__unused__)) -# else -# define SWIGUNUSED -# endif -#endif - -#ifndef SWIG_MSC_UNSUPPRESS_4505 -# if defined(_MSC_VER) -# pragma warning(disable : 4505) /* unreferenced local function has been removed */ -# endif -#endif - -#ifndef SWIGUNUSEDPARM -# ifdef __cplusplus -# define SWIGUNUSEDPARM(p) -# else -# define SWIGUNUSEDPARM(p) p SWIGUNUSED -# endif -#endif - -/* internal SWIG method */ -#ifndef SWIGINTERN -# define SWIGINTERN static SWIGUNUSED -#endif - -/* internal inline SWIG method */ -#ifndef SWIGINTERNINLINE -# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE -#endif - -/* exporting methods */ -#if defined(__GNUC__) -# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -# ifndef GCC_HASCLASSVISIBILITY -# define GCC_HASCLASSVISIBILITY -# endif -# endif -#endif - -#ifndef SWIGEXPORT -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# if defined(STATIC_LINKED) -# define SWIGEXPORT -# else -# define SWIGEXPORT __declspec(dllexport) -# endif -# else -# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) -# define SWIGEXPORT __attribute__ ((visibility("default"))) -# else -# define SWIGEXPORT -# endif -# endif -#endif - -/* calling conventions for Windows */ -#ifndef SWIGSTDCALL -# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# define SWIGSTDCALL __stdcall -# else -# define SWIGSTDCALL -# endif -#endif - -/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ -#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) -# define _CRT_SECURE_NO_DEPRECATE -#endif - -/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ -#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) -# define _SCL_SECURE_NO_DEPRECATE -#endif - -/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ -#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) -# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 -#endif - -/* Intel's compiler complains if a variable which was never initialised is - * cast to void, which is a common idiom which we use to indicate that we - * are aware a variable isn't used. So we just silence that warning. - * See: https://github.com/swig/swig/issues/192 for more discussion. - */ -#ifdef __INTEL_COMPILER -# pragma warning disable 592 -#endif - - -#if defined(_DEBUG) && defined(SWIG_PYTHON_INTERPRETER_NO_DEBUG) -/* Use debug wrappers with the Python release dll */ -# undef _DEBUG -# include -# define _DEBUG -#else -# include -#endif - -/* ----------------------------------------------------------------------------- - * swigrun.swg - * - * This file contains generic C API SWIG runtime support for pointer - * type checking. - * ----------------------------------------------------------------------------- */ - -/* This should only be incremented when either the layout of swig_type_info changes, - or for whatever reason, the runtime changes incompatibly */ -#define SWIG_RUNTIME_VERSION "4" - -/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ -#ifdef SWIG_TYPE_TABLE -# define SWIG_QUOTE_STRING(x) #x -# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) -# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) -#else -# define SWIG_TYPE_TABLE_NAME -#endif - -/* - You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for - creating a static or dynamic library from the SWIG runtime code. - In 99.9% of the cases, SWIG just needs to declare them as 'static'. - - But only do this if strictly necessary, ie, if you have problems - with your compiler or suchlike. -*/ - -#ifndef SWIGRUNTIME -# define SWIGRUNTIME SWIGINTERN -#endif - -#ifndef SWIGRUNTIMEINLINE -# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE -#endif - -/* Generic buffer size */ -#ifndef SWIG_BUFFER_SIZE -# define SWIG_BUFFER_SIZE 1024 -#endif - -/* Flags for pointer conversions */ -#define SWIG_POINTER_DISOWN 0x1 -#define SWIG_CAST_NEW_MEMORY 0x2 - -/* Flags for new pointer objects */ -#define SWIG_POINTER_OWN 0x1 - - -/* - Flags/methods for returning states. - - The SWIG conversion methods, as ConvertPtr, return an integer - that tells if the conversion was successful or not. And if not, - an error code can be returned (see swigerrors.swg for the codes). - - Use the following macros/flags to set or process the returning - states. - - In old versions of SWIG, code such as the following was usually written: - - if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { - // success code - } else { - //fail code - } - - Now you can be more explicit: - - int res = SWIG_ConvertPtr(obj,vptr,ty.flags); - if (SWIG_IsOK(res)) { - // success code - } else { - // fail code - } - - which is the same really, but now you can also do - - Type *ptr; - int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); - if (SWIG_IsOK(res)) { - // success code - if (SWIG_IsNewObj(res) { - ... - delete *ptr; - } else { - ... - } - } else { - // fail code - } - - I.e., now SWIG_ConvertPtr can return new objects and you can - identify the case and take care of the deallocation. Of course that - also requires SWIG_ConvertPtr to return new result values, such as - - int SWIG_ConvertPtr(obj, ptr,...) { - if () { - if () { - *ptr = ; - return SWIG_NEWOBJ; - } else { - *ptr = ; - return SWIG_OLDOBJ; - } - } else { - return SWIG_BADOBJ; - } - } - - Of course, returning the plain '0(success)/-1(fail)' still works, but you can be - more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the - SWIG errors code. - - Finally, if the SWIG_CASTRANK_MODE is enabled, the result code - allows to return the 'cast rank', for example, if you have this - - int food(double) - int fooi(int); - - and you call - - food(1) // cast rank '1' (1 -> 1.0) - fooi(1) // cast rank '0' - - just use the SWIG_AddCast()/SWIG_CheckState() -*/ - -#define SWIG_OK (0) -#define SWIG_ERROR (-1) -#define SWIG_IsOK(r) (r >= 0) -#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) - -/* The CastRankLimit says how many bits are used for the cast rank */ -#define SWIG_CASTRANKLIMIT (1 << 8) -/* The NewMask denotes the object was created (using new/malloc) */ -#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) -/* The TmpMask is for in/out typemaps that use temporal objects */ -#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) -/* Simple returning values */ -#define SWIG_BADOBJ (SWIG_ERROR) -#define SWIG_OLDOBJ (SWIG_OK) -#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) -#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) -/* Check, add and del mask methods */ -#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) -#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) -#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) -#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) -#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) -#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) - -/* Cast-Rank Mode */ -#if defined(SWIG_CASTRANK_MODE) -# ifndef SWIG_TypeRank -# define SWIG_TypeRank unsigned long -# endif -# ifndef SWIG_MAXCASTRANK /* Default cast allowed */ -# define SWIG_MAXCASTRANK (2) -# endif -# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) -# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) -SWIGINTERNINLINE int SWIG_AddCast(int r) { - return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; -} -SWIGINTERNINLINE int SWIG_CheckState(int r) { - return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; -} -#else /* no cast-rank mode */ -# define SWIG_AddCast(r) (r) -# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) -#endif - - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void *(*swig_converter_func)(void *, int *); -typedef struct swig_type_info *(*swig_dycast_func)(void **); - -/* Structure to store information on one type */ -typedef struct swig_type_info { - const char *name; /* mangled name of this type */ - const char *str; /* human readable name of this type */ - swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ - struct swig_cast_info *cast; /* linked list of types that can cast into this type */ - void *clientdata; /* language specific type data */ - int owndata; /* flag if the structure owns the clientdata */ -} swig_type_info; - -/* Structure to store a type and conversion function used for casting */ -typedef struct swig_cast_info { - swig_type_info *type; /* pointer to type that is equivalent to this type */ - swig_converter_func converter; /* function to cast the void pointers */ - struct swig_cast_info *next; /* pointer to next cast in linked list */ - struct swig_cast_info *prev; /* pointer to the previous cast */ -} swig_cast_info; - -/* Structure used to store module information - * Each module generates one structure like this, and the runtime collects - * all of these structures and stores them in a circularly linked list.*/ -typedef struct swig_module_info { - swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ - size_t size; /* Number of types in this module */ - struct swig_module_info *next; /* Pointer to next element in circularly linked list */ - swig_type_info **type_initial; /* Array of initially generated type structures */ - swig_cast_info **cast_initial; /* Array of initially generated casting structures */ - void *clientdata; /* Language specific module data */ -} swig_module_info; - -/* - Compare two type names skipping the space characters, therefore - "char*" == "char *" and "Class" == "Class", etc. - - Return 0 when the two name types are equivalent, as in - strncmp, but skipping ' '. -*/ -SWIGRUNTIME int -SWIG_TypeNameComp(const char *f1, const char *l1, - const char *f2, const char *l2) { - for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { - while ((*f1 == ' ') && (f1 != l1)) ++f1; - while ((*f2 == ' ') && (f2 != l2)) ++f2; - if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; - } - return (int)((l1 - f1) - (l2 - f2)); -} - -/* - Check type equivalence in a name list like ||... - Return 0 if equal, -1 if nb < tb, 1 if nb > tb -*/ -SWIGRUNTIME int -SWIG_TypeCmp(const char *nb, const char *tb) { - int equiv = 1; - const char* te = tb + strlen(tb); - const char* ne = nb; - while (equiv != 0 && *ne) { - for (nb = ne; *ne; ++ne) { - if (*ne == '|') break; - } - equiv = SWIG_TypeNameComp(nb, ne, tb, te); - if (*ne) ++ne; - } - return equiv; -} - -/* - Check type equivalence in a name list like ||... - Return 0 if not equal, 1 if equal -*/ -SWIGRUNTIME int -SWIG_TypeEquiv(const char *nb, const char *tb) { - return SWIG_TypeCmp(nb, tb) == 0 ? 1 : 0; -} - -/* - Check the typename -*/ -SWIGRUNTIME swig_cast_info * -SWIG_TypeCheck(const char *c, swig_type_info *ty) { - if (ty) { - swig_cast_info *iter = ty->cast; - while (iter) { - if (strcmp(iter->type->name, c) == 0) { - if (iter == ty->cast) - return iter; - /* Move iter to the top of the linked list */ - iter->prev->next = iter->next; - if (iter->next) - iter->next->prev = iter->prev; - iter->next = ty->cast; - iter->prev = 0; - if (ty->cast) ty->cast->prev = iter; - ty->cast = iter; - return iter; - } - iter = iter->next; - } - } - return 0; -} - -/* - Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison -*/ -SWIGRUNTIME swig_cast_info * -SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *ty) { - if (ty) { - swig_cast_info *iter = ty->cast; - while (iter) { - if (iter->type == from) { - if (iter == ty->cast) - return iter; - /* Move iter to the top of the linked list */ - iter->prev->next = iter->next; - if (iter->next) - iter->next->prev = iter->prev; - iter->next = ty->cast; - iter->prev = 0; - if (ty->cast) ty->cast->prev = iter; - ty->cast = iter; - return iter; - } - iter = iter->next; - } - } - return 0; -} - -/* - Cast a pointer up an inheritance hierarchy -*/ -SWIGRUNTIMEINLINE void * -SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { - return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); -} - -/* - Dynamic pointer casting. Down an inheritance hierarchy -*/ -SWIGRUNTIME swig_type_info * -SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { - swig_type_info *lastty = ty; - if (!ty || !ty->dcast) return ty; - while (ty && (ty->dcast)) { - ty = (*ty->dcast)(ptr); - if (ty) lastty = ty; - } - return lastty; -} - -/* - Return the name associated with this type -*/ -SWIGRUNTIMEINLINE const char * -SWIG_TypeName(const swig_type_info *ty) { - return ty->name; -} - -/* - Return the pretty name associated with this type, - that is an unmangled type name in a form presentable to the user. -*/ -SWIGRUNTIME const char * -SWIG_TypePrettyName(const swig_type_info *type) { - /* The "str" field contains the equivalent pretty names of the - type, separated by vertical-bar characters. We choose - to print the last name, as it is often (?) the most - specific. */ - if (!type) return NULL; - if (type->str != NULL) { - const char *last_name = type->str; - const char *s; - for (s = type->str; *s; s++) - if (*s == '|') last_name = s+1; - return last_name; - } - else - return type->name; -} - -/* - Set the clientdata field for a type -*/ -SWIGRUNTIME void -SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { - swig_cast_info *cast = ti->cast; - /* if (ti->clientdata == clientdata) return; */ - ti->clientdata = clientdata; - - while (cast) { - if (!cast->converter) { - swig_type_info *tc = cast->type; - if (!tc->clientdata) { - SWIG_TypeClientData(tc, clientdata); - } - } - cast = cast->next; - } -} -SWIGRUNTIME void -SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { - SWIG_TypeClientData(ti, clientdata); - ti->owndata = 1; -} - -/* - Search for a swig_type_info structure only by mangled name - Search is a O(log #types) - - We start searching at module start, and finish searching when start == end. - Note: if start == end at the beginning of the function, we go all the way around - the circular list. -*/ -SWIGRUNTIME swig_type_info * -SWIG_MangledTypeQueryModule(swig_module_info *start, - swig_module_info *end, - const char *name) { - swig_module_info *iter = start; - do { - if (iter->size) { - size_t l = 0; - size_t r = iter->size - 1; - do { - /* since l+r >= 0, we can (>> 1) instead (/ 2) */ - size_t i = (l + r) >> 1; - const char *iname = iter->types[i]->name; - if (iname) { - int compare = strcmp(name, iname); - if (compare == 0) { - return iter->types[i]; - } else if (compare < 0) { - if (i) { - r = i - 1; - } else { - break; - } - } else if (compare > 0) { - l = i + 1; - } - } else { - break; /* should never happen */ - } - } while (l <= r); - } - iter = iter->next; - } while (iter != end); - return 0; -} - -/* - Search for a swig_type_info structure for either a mangled name or a human readable name. - It first searches the mangled names of the types, which is a O(log #types) - If a type is not found it then searches the human readable names, which is O(#types). - - We start searching at module start, and finish searching when start == end. - Note: if start == end at the beginning of the function, we go all the way around - the circular list. -*/ -SWIGRUNTIME swig_type_info * -SWIG_TypeQueryModule(swig_module_info *start, - swig_module_info *end, - const char *name) { - /* STEP 1: Search the name field using binary search */ - swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); - if (ret) { - return ret; - } else { - /* STEP 2: If the type hasn't been found, do a complete search - of the str field (the human readable name) */ - swig_module_info *iter = start; - do { - size_t i = 0; - for (; i < iter->size; ++i) { - if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) - return iter->types[i]; - } - iter = iter->next; - } while (iter != end); - } - - /* neither found a match */ - return 0; -} - -/* - Pack binary data into a string -*/ -SWIGRUNTIME char * -SWIG_PackData(char *c, void *ptr, size_t sz) { - static const char hex[17] = "0123456789abcdef"; - const unsigned char *u = (unsigned char *) ptr; - const unsigned char *eu = u + sz; - for (; u != eu; ++u) { - unsigned char uu = *u; - *(c++) = hex[(uu & 0xf0) >> 4]; - *(c++) = hex[uu & 0xf]; - } - return c; -} - -/* - Unpack binary data from a string -*/ -SWIGRUNTIME const char * -SWIG_UnpackData(const char *c, void *ptr, size_t sz) { - unsigned char *u = (unsigned char *) ptr; - const unsigned char *eu = u + sz; - for (; u != eu; ++u) { - char d = *(c++); - unsigned char uu; - if ((d >= '0') && (d <= '9')) - uu = (unsigned char)((d - '0') << 4); - else if ((d >= 'a') && (d <= 'f')) - uu = (unsigned char)((d - ('a'-10)) << 4); - else - return (char *) 0; - d = *(c++); - if ((d >= '0') && (d <= '9')) - uu |= (unsigned char)(d - '0'); - else if ((d >= 'a') && (d <= 'f')) - uu |= (unsigned char)(d - ('a'-10)); - else - return (char *) 0; - *u = uu; - } - return c; -} - -/* - Pack 'void *' into a string buffer. -*/ -SWIGRUNTIME char * -SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { - char *r = buff; - if ((2*sizeof(void *) + 2) > bsz) return 0; - *(r++) = '_'; - r = SWIG_PackData(r,&ptr,sizeof(void *)); - if (strlen(name) + 1 > (bsz - (r - buff))) return 0; - strcpy(r,name); - return buff; -} - -SWIGRUNTIME const char * -SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { - if (*c != '_') { - if (strcmp(c,"NULL") == 0) { - *ptr = (void *) 0; - return name; - } else { - return 0; - } - } - return SWIG_UnpackData(++c,ptr,sizeof(void *)); -} - -SWIGRUNTIME char * -SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { - char *r = buff; - size_t lname = (name ? strlen(name) : 0); - if ((2*sz + 2 + lname) > bsz) return 0; - *(r++) = '_'; - r = SWIG_PackData(r,ptr,sz); - if (lname) { - strncpy(r,name,lname+1); - } else { - *r = 0; - } - return buff; -} - -SWIGRUNTIME const char * -SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { - if (*c != '_') { - if (strcmp(c,"NULL") == 0) { - memset(ptr,0,sz); - return name; - } else { - return 0; - } - } - return SWIG_UnpackData(++c,ptr,sz); -} - -#ifdef __cplusplus -} -#endif - -/* Errors in SWIG */ -#define SWIG_UnknownError -1 -#define SWIG_IOError -2 -#define SWIG_RuntimeError -3 -#define SWIG_IndexError -4 -#define SWIG_TypeError -5 -#define SWIG_DivisionByZero -6 -#define SWIG_OverflowError -7 -#define SWIG_SyntaxError -8 -#define SWIG_ValueError -9 -#define SWIG_SystemError -10 -#define SWIG_AttributeError -11 -#define SWIG_MemoryError -12 -#define SWIG_NullReferenceError -13 - - - -/* Compatibility macros for Python 3 */ -#if PY_VERSION_HEX >= 0x03000000 - -#define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type) -#define PyInt_Check(x) PyLong_Check(x) -#define PyInt_AsLong(x) PyLong_AsLong(x) -#define PyInt_FromLong(x) PyLong_FromLong(x) -#define PyInt_FromSize_t(x) PyLong_FromSize_t(x) -#define PyString_Check(name) PyBytes_Check(name) -#define PyString_FromString(x) PyUnicode_FromString(x) -#define PyString_Format(fmt, args) PyUnicode_Format(fmt, args) -#define PyString_AsString(str) PyBytes_AsString(str) -#define PyString_Size(str) PyBytes_Size(str) -#define PyString_InternFromString(key) PyUnicode_InternFromString(key) -#define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE -#define PyString_AS_STRING(x) PyUnicode_AS_STRING(x) -#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x) - -#endif - -#ifndef Py_TYPE -# define Py_TYPE(op) ((op)->ob_type) -#endif - -/* SWIG APIs for compatibility of both Python 2 & 3 */ - -#if PY_VERSION_HEX >= 0x03000000 -# define SWIG_Python_str_FromFormat PyUnicode_FromFormat -#else -# define SWIG_Python_str_FromFormat PyString_FromFormat -#endif - - -/* Warning: This function will allocate a new string in Python 3, - * so please call SWIG_Python_str_DelForPy3(x) to free the space. - */ -SWIGINTERN char* -SWIG_Python_str_AsChar(PyObject *str) -{ -#if PY_VERSION_HEX >= 0x03000000 - char *cstr; - char *newstr; - Py_ssize_t len; - str = PyUnicode_AsUTF8String(str); - PyBytes_AsStringAndSize(str, &cstr, &len); - newstr = (char *) malloc(len+1); - memcpy(newstr, cstr, len+1); - Py_XDECREF(str); - return newstr; -#else - return PyString_AsString(str); -#endif -} - -#if PY_VERSION_HEX >= 0x03000000 -# define SWIG_Python_str_DelForPy3(x) free( (void*) (x) ) -#else -# define SWIG_Python_str_DelForPy3(x) -#endif - - -SWIGINTERN PyObject* -SWIG_Python_str_FromChar(const char *c) -{ -#if PY_VERSION_HEX >= 0x03000000 - return PyUnicode_FromString(c); -#else - return PyString_FromString(c); -#endif -} - -/* Add PyOS_snprintf for old Pythons */ -#if PY_VERSION_HEX < 0x02020000 -# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(_WATCOM) -# define PyOS_snprintf _snprintf -# else -# define PyOS_snprintf snprintf -# endif -#endif - -/* A crude PyString_FromFormat implementation for old Pythons */ -#if PY_VERSION_HEX < 0x02020000 - -#ifndef SWIG_PYBUFFER_SIZE -# define SWIG_PYBUFFER_SIZE 1024 -#endif - -static PyObject * -PyString_FromFormat(const char *fmt, ...) { - va_list ap; - char buf[SWIG_PYBUFFER_SIZE * 2]; - int res; - va_start(ap, fmt); - res = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - return (res < 0 || res >= (int)sizeof(buf)) ? 0 : PyString_FromString(buf); -} -#endif - -#ifndef PyObject_DEL -# define PyObject_DEL PyObject_Del -#endif - -/* A crude PyExc_StopIteration exception for old Pythons */ -#if PY_VERSION_HEX < 0x02020000 -# ifndef PyExc_StopIteration -# define PyExc_StopIteration PyExc_RuntimeError -# endif -# ifndef PyObject_GenericGetAttr -# define PyObject_GenericGetAttr 0 -# endif -#endif - -/* Py_NotImplemented is defined in 2.1 and up. */ -#if PY_VERSION_HEX < 0x02010000 -# ifndef Py_NotImplemented -# define Py_NotImplemented PyExc_RuntimeError -# endif -#endif - -/* A crude PyString_AsStringAndSize implementation for old Pythons */ -#if PY_VERSION_HEX < 0x02010000 -# ifndef PyString_AsStringAndSize -# define PyString_AsStringAndSize(obj, s, len) {*s = PyString_AsString(obj); *len = *s ? strlen(*s) : 0;} -# endif -#endif - -/* PySequence_Size for old Pythons */ -#if PY_VERSION_HEX < 0x02000000 -# ifndef PySequence_Size -# define PySequence_Size PySequence_Length -# endif -#endif - -/* PyBool_FromLong for old Pythons */ -#if PY_VERSION_HEX < 0x02030000 -static -PyObject *PyBool_FromLong(long ok) -{ - PyObject *result = ok ? Py_True : Py_False; - Py_INCREF(result); - return result; -} -#endif - -/* Py_ssize_t for old Pythons */ -/* This code is as recommended by: */ -/* http://www.python.org/dev/peps/pep-0353/#conversion-guidelines */ -#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN) -typedef int Py_ssize_t; -# define PY_SSIZE_T_MAX INT_MAX -# define PY_SSIZE_T_MIN INT_MIN -typedef inquiry lenfunc; -typedef intargfunc ssizeargfunc; -typedef intintargfunc ssizessizeargfunc; -typedef intobjargproc ssizeobjargproc; -typedef intintobjargproc ssizessizeobjargproc; -typedef getreadbufferproc readbufferproc; -typedef getwritebufferproc writebufferproc; -typedef getsegcountproc segcountproc; -typedef getcharbufferproc charbufferproc; -static long PyNumber_AsSsize_t (PyObject *x, void *SWIGUNUSEDPARM(exc)) -{ - long result = 0; - PyObject *i = PyNumber_Int(x); - if (i) { - result = PyInt_AsLong(i); - Py_DECREF(i); - } - return result; -} -#endif - -#if PY_VERSION_HEX < 0x02050000 -#define PyInt_FromSize_t(x) PyInt_FromLong((long)x) -#endif - -#if PY_VERSION_HEX < 0x02040000 -#define Py_VISIT(op) \ - do { \ - if (op) { \ - int vret = visit((op), arg); \ - if (vret) \ - return vret; \ - } \ - } while (0) -#endif - -#if PY_VERSION_HEX < 0x02030000 -typedef struct { - PyTypeObject type; - PyNumberMethods as_number; - PyMappingMethods as_mapping; - PySequenceMethods as_sequence; - PyBufferProcs as_buffer; - PyObject *name, *slots; -} PyHeapTypeObject; -#endif - -#if PY_VERSION_HEX < 0x02030000 -typedef destructor freefunc; -#endif - -#if ((PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 6) || \ - (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 0) || \ - (PY_MAJOR_VERSION > 3)) -# define SWIGPY_USE_CAPSULE -# define SWIGPY_CAPSULE_NAME ((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION ".type_pointer_capsule" SWIG_TYPE_TABLE_NAME) -#endif - -#if PY_VERSION_HEX < 0x03020000 -#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) -#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) -#define Py_hash_t long -#endif - -/* ----------------------------------------------------------------------------- - * error manipulation - * ----------------------------------------------------------------------------- */ - -SWIGRUNTIME PyObject* -SWIG_Python_ErrorType(int code) { - PyObject* type = 0; - switch(code) { - case SWIG_MemoryError: - type = PyExc_MemoryError; - break; - case SWIG_IOError: - type = PyExc_IOError; - break; - case SWIG_RuntimeError: - type = PyExc_RuntimeError; - break; - case SWIG_IndexError: - type = PyExc_IndexError; - break; - case SWIG_TypeError: - type = PyExc_TypeError; - break; - case SWIG_DivisionByZero: - type = PyExc_ZeroDivisionError; - break; - case SWIG_OverflowError: - type = PyExc_OverflowError; - break; - case SWIG_SyntaxError: - type = PyExc_SyntaxError; - break; - case SWIG_ValueError: - type = PyExc_ValueError; - break; - case SWIG_SystemError: - type = PyExc_SystemError; - break; - case SWIG_AttributeError: - type = PyExc_AttributeError; - break; - default: - type = PyExc_RuntimeError; - } - return type; -} - - -SWIGRUNTIME void -SWIG_Python_AddErrorMsg(const char* mesg) -{ - PyObject *type = 0; - PyObject *value = 0; - PyObject *traceback = 0; - - if (PyErr_Occurred()) PyErr_Fetch(&type, &value, &traceback); - if (value) { - char *tmp; - PyObject *old_str = PyObject_Str(value); - PyErr_Clear(); - Py_XINCREF(type); - - PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); - SWIG_Python_str_DelForPy3(tmp); - Py_DECREF(old_str); - Py_DECREF(value); - } else { - PyErr_SetString(PyExc_RuntimeError, mesg); - } -} - -#if defined(SWIG_PYTHON_NO_THREADS) -# if defined(SWIG_PYTHON_THREADS) -# undef SWIG_PYTHON_THREADS -# endif -#endif -#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */ -# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL) -# if (PY_VERSION_HEX >= 0x02030000) /* For 2.3 or later, use the PyGILState calls */ -# define SWIG_PYTHON_USE_GIL -# endif -# endif -# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */ -# ifndef SWIG_PYTHON_INITIALIZE_THREADS -# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads() -# endif -# ifdef __cplusplus /* C++ code */ - class SWIG_Python_Thread_Block { - bool status; - PyGILState_STATE state; - public: - void end() { if (status) { PyGILState_Release(state); status = false;} } - SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {} - ~SWIG_Python_Thread_Block() { end(); } - }; - class SWIG_Python_Thread_Allow { - bool status; - PyThreadState *save; - public: - void end() { if (status) { PyEval_RestoreThread(save); status = false; }} - SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {} - ~SWIG_Python_Thread_Allow() { end(); } - }; -# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block -# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end() -# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow -# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end() -# else /* C code */ -# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure() -# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block) -# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread() -# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow) -# endif -# else /* Old thread way, not implemented, user must provide it */ -# if !defined(SWIG_PYTHON_INITIALIZE_THREADS) -# define SWIG_PYTHON_INITIALIZE_THREADS -# endif -# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK) -# define SWIG_PYTHON_THREAD_BEGIN_BLOCK -# endif -# if !defined(SWIG_PYTHON_THREAD_END_BLOCK) -# define SWIG_PYTHON_THREAD_END_BLOCK -# endif -# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW) -# define SWIG_PYTHON_THREAD_BEGIN_ALLOW -# endif -# if !defined(SWIG_PYTHON_THREAD_END_ALLOW) -# define SWIG_PYTHON_THREAD_END_ALLOW -# endif -# endif -#else /* No thread support */ -# define SWIG_PYTHON_INITIALIZE_THREADS -# define SWIG_PYTHON_THREAD_BEGIN_BLOCK -# define SWIG_PYTHON_THREAD_END_BLOCK -# define SWIG_PYTHON_THREAD_BEGIN_ALLOW -# define SWIG_PYTHON_THREAD_END_ALLOW -#endif - -/* ----------------------------------------------------------------------------- - * Python API portion that goes into the runtime - * ----------------------------------------------------------------------------- */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* ----------------------------------------------------------------------------- - * Constant declarations - * ----------------------------------------------------------------------------- */ - -/* Constant Types */ -#define SWIG_PY_POINTER 4 -#define SWIG_PY_BINARY 5 - -/* Constant information structure */ -typedef struct swig_const_info { - int type; - char *name; - long lvalue; - double dvalue; - void *pvalue; - swig_type_info **ptype; -} swig_const_info; - - -/* ----------------------------------------------------------------------------- - * Wrapper of PyInstanceMethod_New() used in Python 3 - * It is exported to the generated module, used for -fastproxy - * ----------------------------------------------------------------------------- */ -#if PY_VERSION_HEX >= 0x03000000 -SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *func) -{ - return PyInstanceMethod_New(func); -} -#else -SWIGRUNTIME PyObject* SWIG_PyInstanceMethod_New(PyObject *SWIGUNUSEDPARM(self), PyObject *SWIGUNUSEDPARM(func)) -{ - return NULL; -} -#endif - -#ifdef __cplusplus -} -#endif - - -/* ----------------------------------------------------------------------------- - * pyrun.swg - * - * This file contains the runtime support for Python modules - * and includes code for managing global variables and pointer - * type checking. - * - * ----------------------------------------------------------------------------- */ - -/* Common SWIG API */ - -/* for raw pointers */ -#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0) -#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags) -#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own) - -#ifdef SWIGPYTHON_BUILTIN -#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags) -#else -#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) -#endif - -#define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) - -#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty) -#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src) -#define swig_owntype int - -/* for raw packed data */ -#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) -#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) - -/* for class or struct pointers */ -#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) -#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) - -/* for C or C++ function pointers */ -#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type) -#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0) - -/* for C++ member pointers, ie, member methods */ -#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) -#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) - - -/* Runtime API */ - -#define SWIG_GetModule(clientdata) SWIG_Python_GetModule(clientdata) -#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer) -#define SWIG_NewClientData(obj) SwigPyClientData_New(obj) - -#define SWIG_SetErrorObj SWIG_Python_SetErrorObj -#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg -#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code) -#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg) -#define SWIG_fail goto fail - - -/* Runtime API implementation */ - -/* Error manipulation */ - -SWIGINTERN void -SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) { - SWIG_PYTHON_THREAD_BEGIN_BLOCK; - PyErr_SetObject(errtype, obj); - Py_DECREF(obj); - SWIG_PYTHON_THREAD_END_BLOCK; -} - -SWIGINTERN void -SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) { - SWIG_PYTHON_THREAD_BEGIN_BLOCK; - PyErr_SetString(errtype, msg); - SWIG_PYTHON_THREAD_END_BLOCK; -} - -#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj) - -/* Set a constant value */ - -#if defined(SWIGPYTHON_BUILTIN) - -SWIGINTERN void -SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) { - PyObject *s = PyString_InternFromString(key); - PyList_Append(seq, s); - Py_DECREF(s); -} - -SWIGINTERN void -SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) { -#if PY_VERSION_HEX < 0x02030000 - PyDict_SetItemString(d, (char *)name, obj); -#else - PyDict_SetItemString(d, name, obj); -#endif - Py_DECREF(obj); - if (public_interface) - SwigPyBuiltin_AddPublicSymbol(public_interface, name); -} - -#else - -SWIGINTERN void -SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) { -#if PY_VERSION_HEX < 0x02030000 - PyDict_SetItemString(d, (char *)name, obj); -#else - PyDict_SetItemString(d, name, obj); -#endif - Py_DECREF(obj); -} - -#endif - -/* Append a value to the result obj */ - -SWIGINTERN PyObject* -SWIG_Python_AppendOutput(PyObject* result, PyObject* obj) { -#if !defined(SWIG_PYTHON_OUTPUT_TUPLE) - if (!result) { - result = obj; - } else if (result == Py_None) { - Py_DECREF(result); - result = obj; - } else { - if (!PyList_Check(result)) { - PyObject *o2 = result; - result = PyList_New(1); - PyList_SetItem(result, 0, o2); - } - PyList_Append(result,obj); - Py_DECREF(obj); - } - return result; -#else - PyObject* o2; - PyObject* o3; - if (!result) { - result = obj; - } else if (result == Py_None) { - Py_DECREF(result); - result = obj; - } else { - if (!PyTuple_Check(result)) { - o2 = result; - result = PyTuple_New(1); - PyTuple_SET_ITEM(result, 0, o2); - } - o3 = PyTuple_New(1); - PyTuple_SET_ITEM(o3, 0, obj); - o2 = result; - result = PySequence_Concat(o2, o3); - Py_DECREF(o2); - Py_DECREF(o3); - } - return result; -#endif -} - -/* Unpack the argument tuple */ - -SWIGINTERN Py_ssize_t -SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs) -{ - if (!args) { - if (!min && !max) { - return 1; - } else { - PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none", - name, (min == max ? "" : "at least "), (int)min); - return 0; - } - } - if (!PyTuple_Check(args)) { - if (min <= 1 && max >= 1) { - Py_ssize_t i; - objs[0] = args; - for (i = 1; i < max; ++i) { - objs[i] = 0; - } - return 2; - } - PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple"); - return 0; - } else { - Py_ssize_t l = PyTuple_GET_SIZE(args); - if (l < min) { - PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", - name, (min == max ? "" : "at least "), (int)min, (int)l); - return 0; - } else if (l > max) { - PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", - name, (min == max ? "" : "at most "), (int)max, (int)l); - return 0; - } else { - Py_ssize_t i; - for (i = 0; i < l; ++i) { - objs[i] = PyTuple_GET_ITEM(args, i); - } - for (; l < max; ++l) { - objs[l] = 0; - } - return i + 1; - } - } -} - -/* A functor is a function object with one single object argument */ -#if PY_VERSION_HEX >= 0x02020000 -#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL); -#else -#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunction(functor, "O", obj); -#endif - -/* - Helper for static pointer initialization for both C and C++ code, for example - static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...); -*/ -#ifdef __cplusplus -#define SWIG_STATIC_POINTER(var) var -#else -#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var -#endif - -/* ----------------------------------------------------------------------------- - * Pointer declarations - * ----------------------------------------------------------------------------- */ - -/* Flags for new pointer objects */ -#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1) -#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN) - -#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1) - -#define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2) -#define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN) - -#ifdef __cplusplus -extern "C" { -#endif - -/* How to access Py_None */ -#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# ifndef SWIG_PYTHON_NO_BUILD_NONE -# ifndef SWIG_PYTHON_BUILD_NONE -# define SWIG_PYTHON_BUILD_NONE -# endif -# endif -#endif - -#ifdef SWIG_PYTHON_BUILD_NONE -# ifdef Py_None -# undef Py_None -# define Py_None SWIG_Py_None() -# endif -SWIGRUNTIMEINLINE PyObject * -_SWIG_Py_None(void) -{ - PyObject *none = Py_BuildValue((char*)""); - Py_DECREF(none); - return none; -} -SWIGRUNTIME PyObject * -SWIG_Py_None(void) -{ - static PyObject *SWIG_STATIC_POINTER(none) = _SWIG_Py_None(); - return none; -} -#endif - -/* The python void return value */ - -SWIGRUNTIMEINLINE PyObject * -SWIG_Py_Void(void) -{ - PyObject *none = Py_None; - Py_INCREF(none); - return none; -} - -/* SwigPyClientData */ - -typedef struct { - PyObject *klass; - PyObject *newraw; - PyObject *newargs; - PyObject *destroy; - int delargs; - int implicitconv; - PyTypeObject *pytype; -} SwigPyClientData; - -SWIGRUNTIMEINLINE int -SWIG_Python_CheckImplicit(swig_type_info *ty) -{ - SwigPyClientData *data = (SwigPyClientData *)ty->clientdata; - return data ? data->implicitconv : 0; -} - -SWIGRUNTIMEINLINE PyObject * -SWIG_Python_ExceptionType(swig_type_info *desc) { - SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0; - PyObject *klass = data ? data->klass : 0; - return (klass ? klass : PyExc_RuntimeError); -} - - -SWIGRUNTIME SwigPyClientData * -SwigPyClientData_New(PyObject* obj) -{ - if (!obj) { - return 0; - } else { - SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData)); - /* the klass element */ - data->klass = obj; - Py_INCREF(data->klass); - /* the newraw method and newargs arguments used to create a new raw instance */ - if (PyClass_Check(obj)) { - data->newraw = 0; - data->newargs = obj; - Py_INCREF(obj); - } else { -#if (PY_VERSION_HEX < 0x02020000) - data->newraw = 0; -#else - data->newraw = PyObject_GetAttrString(data->klass, (char *)"__new__"); -#endif - if (data->newraw) { - Py_INCREF(data->newraw); - data->newargs = PyTuple_New(1); - PyTuple_SetItem(data->newargs, 0, obj); - } else { - data->newargs = obj; - } - Py_INCREF(data->newargs); - } - /* the destroy method, aka as the C++ delete method */ - data->destroy = PyObject_GetAttrString(data->klass, (char *)"__swig_destroy__"); - if (PyErr_Occurred()) { - PyErr_Clear(); - data->destroy = 0; - } - if (data->destroy) { - int flags; - Py_INCREF(data->destroy); - flags = PyCFunction_GET_FLAGS(data->destroy); -#ifdef METH_O - data->delargs = !(flags & (METH_O)); -#else - data->delargs = 0; -#endif - } else { - data->delargs = 0; - } - data->implicitconv = 0; - data->pytype = 0; - return data; - } -} - -SWIGRUNTIME void -SwigPyClientData_Del(SwigPyClientData *data) { - Py_XDECREF(data->newraw); - Py_XDECREF(data->newargs); - Py_XDECREF(data->destroy); -} - -/* =============== SwigPyObject =====================*/ - -typedef struct { - PyObject_HEAD - void *ptr; - swig_type_info *ty; - int own; - PyObject *next; -#ifdef SWIGPYTHON_BUILTIN - PyObject *dict; -#endif -} SwigPyObject; - - -#ifdef SWIGPYTHON_BUILTIN - -SWIGRUNTIME PyObject * -SwigPyObject_get___dict__(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) -{ - SwigPyObject *sobj = (SwigPyObject *)v; - - if (!sobj->dict) - sobj->dict = PyDict_New(); - - Py_INCREF(sobj->dict); - return sobj->dict; -} - -#endif - -SWIGRUNTIME PyObject * -SwigPyObject_long(SwigPyObject *v) -{ - return PyLong_FromVoidPtr(v->ptr); -} - -SWIGRUNTIME PyObject * -SwigPyObject_format(const char* fmt, SwigPyObject *v) -{ - PyObject *res = NULL; - PyObject *args = PyTuple_New(1); - if (args) { - if (PyTuple_SetItem(args, 0, SwigPyObject_long(v)) == 0) { - PyObject *ofmt = SWIG_Python_str_FromChar(fmt); - if (ofmt) { -#if PY_VERSION_HEX >= 0x03000000 - res = PyUnicode_Format(ofmt,args); -#else - res = PyString_Format(ofmt,args); -#endif - Py_DECREF(ofmt); - } - Py_DECREF(args); - } - } - return res; -} - -SWIGRUNTIME PyObject * -SwigPyObject_oct(SwigPyObject *v) -{ - return SwigPyObject_format("%o",v); -} - -SWIGRUNTIME PyObject * -SwigPyObject_hex(SwigPyObject *v) -{ - return SwigPyObject_format("%x",v); -} - -SWIGRUNTIME PyObject * -#ifdef METH_NOARGS -SwigPyObject_repr(SwigPyObject *v) -#else -SwigPyObject_repr(SwigPyObject *v, PyObject *args) -#endif -{ - const char *name = SWIG_TypePrettyName(v->ty); - PyObject *repr = SWIG_Python_str_FromFormat("", (name ? name : "unknown"), (void *)v); - if (v->next) { -# ifdef METH_NOARGS - PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next); -# else - PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next, args); -# endif -# if PY_VERSION_HEX >= 0x03000000 - PyObject *joined = PyUnicode_Concat(repr, nrep); - Py_DecRef(repr); - Py_DecRef(nrep); - repr = joined; -# else - PyString_ConcatAndDel(&repr,nrep); -# endif - } - return repr; -} - -SWIGRUNTIME int -SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w) -{ - void *i = v->ptr; - void *j = w->ptr; - return (i < j) ? -1 : ((i > j) ? 1 : 0); -} - -/* Added for Python 3.x, would it also be useful for Python 2.x? */ -SWIGRUNTIME PyObject* -SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op) -{ - PyObject* res; - if( op != Py_EQ && op != Py_NE ) { - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; - } - res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0); - return res; -} - - -SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void); - -#ifdef SWIGPYTHON_BUILTIN -static swig_type_info *SwigPyObject_stype = 0; -SWIGRUNTIME PyTypeObject* -SwigPyObject_type(void) { - SwigPyClientData *cd; - assert(SwigPyObject_stype); - cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; - assert(cd); - assert(cd->pytype); - return cd->pytype; -} -#else -SWIGRUNTIME PyTypeObject* -SwigPyObject_type(void) { - static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce(); - return type; -} -#endif - -SWIGRUNTIMEINLINE int -SwigPyObject_Check(PyObject *op) { -#ifdef SWIGPYTHON_BUILTIN - PyTypeObject *target_tp = SwigPyObject_type(); - if (PyType_IsSubtype(op->ob_type, target_tp)) - return 1; - return (strcmp(op->ob_type->tp_name, "SwigPyObject") == 0); -#else - return (Py_TYPE(op) == SwigPyObject_type()) - || (strcmp(Py_TYPE(op)->tp_name,"SwigPyObject") == 0); -#endif -} - -SWIGRUNTIME PyObject * -SwigPyObject_New(void *ptr, swig_type_info *ty, int own); - -SWIGRUNTIME void -SwigPyObject_dealloc(PyObject *v) -{ - SwigPyObject *sobj = (SwigPyObject *) v; - PyObject *next = sobj->next; - if (sobj->own == SWIG_POINTER_OWN) { - swig_type_info *ty = sobj->ty; - SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; - PyObject *destroy = data ? data->destroy : 0; - if (destroy) { - /* destroy is always a VARARGS method */ - PyObject *res; - - /* PyObject_CallFunction() has the potential to silently drop - the active active exception. In cases of unnamed temporary - variable or where we just finished iterating over a generator - StopIteration will be active right now, and this needs to - remain true upon return from SwigPyObject_dealloc. So save - and restore. */ - - PyObject *val = NULL, *type = NULL, *tb = NULL; - PyErr_Fetch(&val, &type, &tb); - - if (data->delargs) { - /* we need to create a temporary object to carry the destroy operation */ - PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); - res = SWIG_Python_CallFunctor(destroy, tmp); - Py_DECREF(tmp); - } else { - PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); - PyObject *mself = PyCFunction_GET_SELF(destroy); - res = ((*meth)(mself, v)); - } - if (!res) - PyErr_WriteUnraisable(destroy); - - PyErr_Restore(val, type, tb); - - Py_XDECREF(res); - } -#if !defined(SWIG_PYTHON_SILENT_MEMLEAK) - else { - const char *name = SWIG_TypePrettyName(ty); - printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown")); - } -#endif - } - Py_XDECREF(next); - PyObject_DEL(v); -} - -SWIGRUNTIME PyObject* -SwigPyObject_append(PyObject* v, PyObject* next) -{ - SwigPyObject *sobj = (SwigPyObject *) v; -#ifndef METH_O - PyObject *tmp = 0; - if (!PyArg_ParseTuple(next,(char *)"O:append", &tmp)) return NULL; - next = tmp; -#endif - if (!SwigPyObject_Check(next)) { - PyErr_SetString(PyExc_TypeError, "Attempt to append a non SwigPyObject"); - return NULL; - } - sobj->next = next; - Py_INCREF(next); - return SWIG_Py_Void(); -} - -SWIGRUNTIME PyObject* -#ifdef METH_NOARGS -SwigPyObject_next(PyObject* v) -#else -SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) -#endif -{ - SwigPyObject *sobj = (SwigPyObject *) v; - if (sobj->next) { - Py_INCREF(sobj->next); - return sobj->next; - } else { - return SWIG_Py_Void(); - } -} - -SWIGINTERN PyObject* -#ifdef METH_NOARGS -SwigPyObject_disown(PyObject *v) -#else -SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) -#endif -{ - SwigPyObject *sobj = (SwigPyObject *)v; - sobj->own = 0; - return SWIG_Py_Void(); -} - -SWIGINTERN PyObject* -#ifdef METH_NOARGS -SwigPyObject_acquire(PyObject *v) -#else -SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) -#endif -{ - SwigPyObject *sobj = (SwigPyObject *)v; - sobj->own = SWIG_POINTER_OWN; - return SWIG_Py_Void(); -} - -SWIGINTERN PyObject* -SwigPyObject_own(PyObject *v, PyObject *args) -{ - PyObject *val = 0; -#if (PY_VERSION_HEX < 0x02020000) - if (!PyArg_ParseTuple(args,(char *)"|O:own",&val)) -#elif (PY_VERSION_HEX < 0x02050000) - if (!PyArg_UnpackTuple(args, (char *)"own", 0, 1, &val)) -#else - if (!PyArg_UnpackTuple(args, "own", 0, 1, &val)) -#endif - { - return NULL; - } - else - { - SwigPyObject *sobj = (SwigPyObject *)v; - PyObject *obj = PyBool_FromLong(sobj->own); - if (val) { -#ifdef METH_NOARGS - if (PyObject_IsTrue(val)) { - SwigPyObject_acquire(v); - } else { - SwigPyObject_disown(v); - } -#else - if (PyObject_IsTrue(val)) { - SwigPyObject_acquire(v,args); - } else { - SwigPyObject_disown(v,args); - } -#endif - } - return obj; - } -} - -#ifdef METH_O -static PyMethodDef -swigobject_methods[] = { - {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_NOARGS, (char *)"releases ownership of the pointer"}, - {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_NOARGS, (char *)"acquires ownership of the pointer"}, - {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, - {(char *)"append", (PyCFunction)SwigPyObject_append, METH_O, (char *)"appends another 'this' object"}, - {(char *)"next", (PyCFunction)SwigPyObject_next, METH_NOARGS, (char *)"returns the next 'this' object"}, - {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_NOARGS, (char *)"returns object representation"}, - {0, 0, 0, 0} -}; -#else -static PyMethodDef -swigobject_methods[] = { - {(char *)"disown", (PyCFunction)SwigPyObject_disown, METH_VARARGS, (char *)"releases ownership of the pointer"}, - {(char *)"acquire", (PyCFunction)SwigPyObject_acquire, METH_VARARGS, (char *)"acquires ownership of the pointer"}, - {(char *)"own", (PyCFunction)SwigPyObject_own, METH_VARARGS, (char *)"returns/sets ownership of the pointer"}, - {(char *)"append", (PyCFunction)SwigPyObject_append, METH_VARARGS, (char *)"appends another 'this' object"}, - {(char *)"next", (PyCFunction)SwigPyObject_next, METH_VARARGS, (char *)"returns the next 'this' object"}, - {(char *)"__repr__",(PyCFunction)SwigPyObject_repr, METH_VARARGS, (char *)"returns object representation"}, - {0, 0, 0, 0} -}; -#endif - -#if PY_VERSION_HEX < 0x02020000 -SWIGINTERN PyObject * -SwigPyObject_getattr(SwigPyObject *sobj,char *name) -{ - return Py_FindMethod(swigobject_methods, (PyObject *)sobj, name); -} -#endif - -SWIGRUNTIME PyTypeObject* -SwigPyObject_TypeOnce(void) { - static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer"; - - static PyNumberMethods SwigPyObject_as_number = { - (binaryfunc)0, /*nb_add*/ - (binaryfunc)0, /*nb_subtract*/ - (binaryfunc)0, /*nb_multiply*/ - /* nb_divide removed in Python 3 */ -#if PY_VERSION_HEX < 0x03000000 - (binaryfunc)0, /*nb_divide*/ -#endif - (binaryfunc)0, /*nb_remainder*/ - (binaryfunc)0, /*nb_divmod*/ - (ternaryfunc)0,/*nb_power*/ - (unaryfunc)0, /*nb_negative*/ - (unaryfunc)0, /*nb_positive*/ - (unaryfunc)0, /*nb_absolute*/ - (inquiry)0, /*nb_nonzero*/ - 0, /*nb_invert*/ - 0, /*nb_lshift*/ - 0, /*nb_rshift*/ - 0, /*nb_and*/ - 0, /*nb_xor*/ - 0, /*nb_or*/ -#if PY_VERSION_HEX < 0x03000000 - 0, /*nb_coerce*/ -#endif - (unaryfunc)SwigPyObject_long, /*nb_int*/ -#if PY_VERSION_HEX < 0x03000000 - (unaryfunc)SwigPyObject_long, /*nb_long*/ -#else - 0, /*nb_reserved*/ -#endif - (unaryfunc)0, /*nb_float*/ -#if PY_VERSION_HEX < 0x03000000 - (unaryfunc)SwigPyObject_oct, /*nb_oct*/ - (unaryfunc)SwigPyObject_hex, /*nb_hex*/ -#endif -#if PY_VERSION_HEX >= 0x03050000 /* 3.5 */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_matrix_multiply */ -#elif PY_VERSION_HEX >= 0x03000000 /* 3.0 */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */ -#elif PY_VERSION_HEX >= 0x02050000 /* 2.5.0 */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */ -#elif PY_VERSION_HEX >= 0x02020000 /* 2.2.0 */ - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_true_divide */ -#elif PY_VERSION_HEX >= 0x02000000 /* 2.0.0 */ - 0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_or */ -#endif - }; - - static PyTypeObject swigpyobject_type; - static int type_init = 0; - if (!type_init) { - const PyTypeObject tmp = { -#if PY_VERSION_HEX >= 0x03000000 - PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - (char *)"SwigPyObject", /* tp_name */ - sizeof(SwigPyObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)SwigPyObject_dealloc, /* tp_dealloc */ - 0, /* tp_print */ -#if PY_VERSION_HEX < 0x02020000 - (getattrfunc)SwigPyObject_getattr, /* tp_getattr */ -#else - (getattrfunc)0, /* tp_getattr */ -#endif - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03000000 - 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */ -#else - (cmpfunc)SwigPyObject_compare, /* tp_compare */ -#endif - (reprfunc)SwigPyObject_repr, /* tp_repr */ - &SwigPyObject_as_number, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - swigobject_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */ - 0, /* tp_weaklistoffset */ -#if PY_VERSION_HEX >= 0x02020000 - 0, /* tp_iter */ - 0, /* tp_iternext */ - swigobject_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ -#endif -#if PY_VERSION_HEX >= 0x02030000 - 0, /* tp_del */ -#endif -#if PY_VERSION_HEX >= 0x02060000 - 0, /* tp_version_tag */ -#endif -#if PY_VERSION_HEX >= 0x03040000 - 0, /* tp_finalize */ -#endif -#ifdef COUNT_ALLOCS - 0, /* tp_allocs */ - 0, /* tp_frees */ - 0, /* tp_maxalloc */ -#if PY_VERSION_HEX >= 0x02050000 - 0, /* tp_prev */ -#endif - 0 /* tp_next */ -#endif - }; - swigpyobject_type = tmp; - type_init = 1; -#if PY_VERSION_HEX < 0x02020000 - swigpyobject_type.ob_type = &PyType_Type; -#else - if (PyType_Ready(&swigpyobject_type) < 0) - return NULL; -#endif - } - return &swigpyobject_type; -} - -SWIGRUNTIME PyObject * -SwigPyObject_New(void *ptr, swig_type_info *ty, int own) -{ - SwigPyObject *sobj = PyObject_NEW(SwigPyObject, SwigPyObject_type()); - if (sobj) { - sobj->ptr = ptr; - sobj->ty = ty; - sobj->own = own; - sobj->next = 0; - } - return (PyObject *)sobj; -} - -/* ----------------------------------------------------------------------------- - * Implements a simple Swig Packed type, and use it instead of string - * ----------------------------------------------------------------------------- */ - -typedef struct { - PyObject_HEAD - void *pack; - swig_type_info *ty; - size_t size; -} SwigPyPacked; - -SWIGRUNTIME int -SwigPyPacked_print(SwigPyPacked *v, FILE *fp, int SWIGUNUSEDPARM(flags)) -{ - char result[SWIG_BUFFER_SIZE]; - fputs("pack, v->size, 0, sizeof(result))) { - fputs("at ", fp); - fputs(result, fp); - } - fputs(v->ty->name,fp); - fputs(">", fp); - return 0; -} - -SWIGRUNTIME PyObject * -SwigPyPacked_repr(SwigPyPacked *v) -{ - char result[SWIG_BUFFER_SIZE]; - if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { - return SWIG_Python_str_FromFormat("", result, v->ty->name); - } else { - return SWIG_Python_str_FromFormat("", v->ty->name); - } -} - -SWIGRUNTIME PyObject * -SwigPyPacked_str(SwigPyPacked *v) -{ - char result[SWIG_BUFFER_SIZE]; - if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){ - return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name); - } else { - return SWIG_Python_str_FromChar(v->ty->name); - } -} - -SWIGRUNTIME int -SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w) -{ - size_t i = v->size; - size_t j = w->size; - int s = (i < j) ? -1 : ((i > j) ? 1 : 0); - return s ? s : strncmp((char *)v->pack, (char *)w->pack, 2*v->size); -} - -SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void); - -SWIGRUNTIME PyTypeObject* -SwigPyPacked_type(void) { - static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce(); - return type; -} - -SWIGRUNTIMEINLINE int -SwigPyPacked_Check(PyObject *op) { - return ((op)->ob_type == SwigPyPacked_TypeOnce()) - || (strcmp((op)->ob_type->tp_name,"SwigPyPacked") == 0); -} - -SWIGRUNTIME void -SwigPyPacked_dealloc(PyObject *v) -{ - if (SwigPyPacked_Check(v)) { - SwigPyPacked *sobj = (SwigPyPacked *) v; - free(sobj->pack); - } - PyObject_DEL(v); -} - -SWIGRUNTIME PyTypeObject* -SwigPyPacked_TypeOnce(void) { - static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer"; - static PyTypeObject swigpypacked_type; - static int type_init = 0; - if (!type_init) { - const PyTypeObject tmp = { -#if PY_VERSION_HEX>=0x03000000 - PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - (char *)"SwigPyPacked", /* tp_name */ - sizeof(SwigPyPacked), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)SwigPyPacked_dealloc, /* tp_dealloc */ - (printfunc)SwigPyPacked_print, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX>=0x03000000 - 0, /* tp_reserved in 3.0.1 */ -#else - (cmpfunc)SwigPyPacked_compare, /* tp_compare */ -#endif - (reprfunc)SwigPyPacked_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)SwigPyPacked_str, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - swigpacked_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ -#if PY_VERSION_HEX >= 0x02020000 - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ -#endif -#if PY_VERSION_HEX >= 0x02030000 - 0, /* tp_del */ -#endif -#if PY_VERSION_HEX >= 0x02060000 - 0, /* tp_version_tag */ -#endif -#if PY_VERSION_HEX >= 0x03040000 - 0, /* tp_finalize */ -#endif -#ifdef COUNT_ALLOCS - 0, /* tp_allocs */ - 0, /* tp_frees */ - 0, /* tp_maxalloc */ -#if PY_VERSION_HEX >= 0x02050000 - 0, /* tp_prev */ -#endif - 0 /* tp_next */ -#endif - }; - swigpypacked_type = tmp; - type_init = 1; -#if PY_VERSION_HEX < 0x02020000 - swigpypacked_type.ob_type = &PyType_Type; -#else - if (PyType_Ready(&swigpypacked_type) < 0) - return NULL; -#endif - } - return &swigpypacked_type; -} - -SWIGRUNTIME PyObject * -SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty) -{ - SwigPyPacked *sobj = PyObject_NEW(SwigPyPacked, SwigPyPacked_type()); - if (sobj) { - void *pack = malloc(size); - if (pack) { - memcpy(pack, ptr, size); - sobj->pack = pack; - sobj->ty = ty; - sobj->size = size; - } else { - PyObject_DEL((PyObject *) sobj); - sobj = 0; - } - } - return (PyObject *) sobj; -} - -SWIGRUNTIME swig_type_info * -SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size) -{ - if (SwigPyPacked_Check(obj)) { - SwigPyPacked *sobj = (SwigPyPacked *)obj; - if (sobj->size != size) return 0; - memcpy(ptr, sobj->pack, size); - return sobj->ty; - } else { - return 0; - } -} - -/* ----------------------------------------------------------------------------- - * pointers/data manipulation - * ----------------------------------------------------------------------------- */ - -SWIGRUNTIMEINLINE PyObject * -_SWIG_This(void) -{ - return SWIG_Python_str_FromChar("this"); -} - -static PyObject *swig_this = NULL; - -SWIGRUNTIME PyObject * -SWIG_This(void) -{ - if (swig_this == NULL) - swig_this = _SWIG_This(); - return swig_this; -} - -/* #define SWIG_PYTHON_SLOW_GETSET_THIS */ - -/* TODO: I don't know how to implement the fast getset in Python 3 right now */ -#if PY_VERSION_HEX>=0x03000000 -#define SWIG_PYTHON_SLOW_GETSET_THIS -#endif - -SWIGRUNTIME SwigPyObject * -SWIG_Python_GetSwigThis(PyObject *pyobj) -{ - PyObject *obj; - - if (SwigPyObject_Check(pyobj)) - return (SwigPyObject *) pyobj; - -#ifdef SWIGPYTHON_BUILTIN - (void)obj; -# ifdef PyWeakref_CheckProxy - if (PyWeakref_CheckProxy(pyobj)) { - pyobj = PyWeakref_GET_OBJECT(pyobj); - if (pyobj && SwigPyObject_Check(pyobj)) - return (SwigPyObject*) pyobj; - } -# endif - return NULL; -#else - - obj = 0; - -#if (!defined(SWIG_PYTHON_SLOW_GETSET_THIS) && (PY_VERSION_HEX >= 0x02030000)) - if (PyInstance_Check(pyobj)) { - obj = _PyInstance_Lookup(pyobj, SWIG_This()); - } else { - PyObject **dictptr = _PyObject_GetDictPtr(pyobj); - if (dictptr != NULL) { - PyObject *dict = *dictptr; - obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0; - } else { -#ifdef PyWeakref_CheckProxy - if (PyWeakref_CheckProxy(pyobj)) { - PyObject *wobj = PyWeakref_GET_OBJECT(pyobj); - return wobj ? SWIG_Python_GetSwigThis(wobj) : 0; - } -#endif - obj = PyObject_GetAttr(pyobj,SWIG_This()); - if (obj) { - Py_DECREF(obj); - } else { - if (PyErr_Occurred()) PyErr_Clear(); - return 0; - } - } - } -#else - obj = PyObject_GetAttr(pyobj,SWIG_This()); - if (obj) { - Py_DECREF(obj); - } else { - if (PyErr_Occurred()) PyErr_Clear(); - return 0; - } -#endif - if (obj && !SwigPyObject_Check(obj)) { - /* a PyObject is called 'this', try to get the 'real this' - SwigPyObject from it */ - return SWIG_Python_GetSwigThis(obj); - } - return (SwigPyObject *)obj; -#endif -} - -/* Acquire a pointer value */ - -SWIGRUNTIME int -SWIG_Python_AcquirePtr(PyObject *obj, int own) { - if (own == SWIG_POINTER_OWN) { - SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj); - if (sobj) { - int oldown = sobj->own; - sobj->own = own; - return oldown; - } - } - return 0; -} - -/* Convert a pointer value */ - -SWIGRUNTIME int -SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) { - int res; - SwigPyObject *sobj; - int implicit_conv = (flags & SWIG_POINTER_IMPLICIT_CONV) != 0; - - if (!obj) - return SWIG_ERROR; - if (obj == Py_None && !implicit_conv) { - if (ptr) - *ptr = 0; - return SWIG_OK; - } - - res = SWIG_ERROR; - - sobj = SWIG_Python_GetSwigThis(obj); - if (own) - *own = 0; - while (sobj) { - void *vptr = sobj->ptr; - if (ty) { - swig_type_info *to = sobj->ty; - if (to == ty) { - /* no type cast needed */ - if (ptr) *ptr = vptr; - break; - } else { - swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); - if (!tc) { - sobj = (SwigPyObject *)sobj->next; - } else { - if (ptr) { - int newmemory = 0; - *ptr = SWIG_TypeCast(tc,vptr,&newmemory); - if (newmemory == SWIG_CAST_NEW_MEMORY) { - assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */ - if (own) - *own = *own | SWIG_CAST_NEW_MEMORY; - } - } - break; - } - } - } else { - if (ptr) *ptr = vptr; - break; - } - } - if (sobj) { - if (own) - *own = *own | sobj->own; - if (flags & SWIG_POINTER_DISOWN) { - sobj->own = 0; - } - res = SWIG_OK; - } else { - if (implicit_conv) { - SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; - if (data && !data->implicitconv) { - PyObject *klass = data->klass; - if (klass) { - PyObject *impconv; - data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/ - impconv = SWIG_Python_CallFunctor(klass, obj); - data->implicitconv = 0; - if (PyErr_Occurred()) { - PyErr_Clear(); - impconv = 0; - } - if (impconv) { - SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv); - if (iobj) { - void *vptr; - res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0); - if (SWIG_IsOK(res)) { - if (ptr) { - *ptr = vptr; - /* transfer the ownership to 'ptr' */ - iobj->own = 0; - res = SWIG_AddCast(res); - res = SWIG_AddNewMask(res); - } else { - res = SWIG_AddCast(res); - } - } - } - Py_DECREF(impconv); - } - } - } - } - if (!SWIG_IsOK(res) && obj == Py_None) { - if (ptr) - *ptr = 0; - if (PyErr_Occurred()) - PyErr_Clear(); - res = SWIG_OK; - } - } - return res; -} - -/* Convert a function ptr value */ - -SWIGRUNTIME int -SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) { - if (!PyCFunction_Check(obj)) { - return SWIG_ConvertPtr(obj, ptr, ty, 0); - } else { - void *vptr = 0; - - /* here we get the method pointer for callbacks */ - const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); - const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0; - if (desc) - desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0; - if (!desc) - return SWIG_ERROR; - if (ty) { - swig_cast_info *tc = SWIG_TypeCheck(desc,ty); - if (tc) { - int newmemory = 0; - *ptr = SWIG_TypeCast(tc,vptr,&newmemory); - assert(!newmemory); /* newmemory handling not yet implemented */ - } else { - return SWIG_ERROR; - } - } else { - *ptr = vptr; - } - return SWIG_OK; - } -} - -/* Convert a packed value value */ - -SWIGRUNTIME int -SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) { - swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz); - if (!to) return SWIG_ERROR; - if (ty) { - if (to != ty) { - /* check type cast? */ - swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); - if (!tc) return SWIG_ERROR; - } - } - return SWIG_OK; -} - -/* ----------------------------------------------------------------------------- - * Create a new pointer object - * ----------------------------------------------------------------------------- */ - -/* - Create a new instance object, without calling __init__, and set the - 'this' attribute. -*/ - -SWIGRUNTIME PyObject* -SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this) -{ -#if (PY_VERSION_HEX >= 0x02020000) - PyObject *inst = 0; - PyObject *newraw = data->newraw; - if (newraw) { - inst = PyObject_Call(newraw, data->newargs, NULL); - if (inst) { -#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) - PyObject **dictptr = _PyObject_GetDictPtr(inst); - if (dictptr != NULL) { - PyObject *dict = *dictptr; - if (dict == NULL) { - dict = PyDict_New(); - *dictptr = dict; - PyDict_SetItem(dict, SWIG_This(), swig_this); - } - } -#else - PyObject *key = SWIG_This(); - PyObject_SetAttr(inst, key, swig_this); -#endif - } - } else { -#if PY_VERSION_HEX >= 0x03000000 - inst = ((PyTypeObject*) data->newargs)->tp_new((PyTypeObject*) data->newargs, Py_None, Py_None); - if (inst) { - PyObject_SetAttr(inst, SWIG_This(), swig_this); - Py_TYPE(inst)->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG; - } -#else - PyObject *dict = PyDict_New(); - if (dict) { - PyDict_SetItem(dict, SWIG_This(), swig_this); - inst = PyInstance_NewRaw(data->newargs, dict); - Py_DECREF(dict); - } -#endif - } - return inst; -#else -#if (PY_VERSION_HEX >= 0x02010000) - PyObject *inst = 0; - PyObject *dict = PyDict_New(); - if (dict) { - PyDict_SetItem(dict, SWIG_This(), swig_this); - inst = PyInstance_NewRaw(data->newargs, dict); - Py_DECREF(dict); - } - return (PyObject *) inst; -#else - PyInstanceObject *inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type); - if (inst == NULL) { - return NULL; - } - inst->in_class = (PyClassObject *)data->newargs; - Py_INCREF(inst->in_class); - inst->in_dict = PyDict_New(); - if (inst->in_dict == NULL) { - Py_DECREF(inst); - return NULL; - } -#ifdef Py_TPFLAGS_HAVE_WEAKREFS - inst->in_weakreflist = NULL; -#endif -#ifdef Py_TPFLAGS_GC - PyObject_GC_Init(inst); -#endif - PyDict_SetItem(inst->in_dict, SWIG_This(), swig_this); - return (PyObject *) inst; -#endif -#endif -} - -SWIGRUNTIME void -SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this) -{ - PyObject *dict; -#if (PY_VERSION_HEX >= 0x02020000) && !defined(SWIG_PYTHON_SLOW_GETSET_THIS) - PyObject **dictptr = _PyObject_GetDictPtr(inst); - if (dictptr != NULL) { - dict = *dictptr; - if (dict == NULL) { - dict = PyDict_New(); - *dictptr = dict; - } - PyDict_SetItem(dict, SWIG_This(), swig_this); - return; - } -#endif - dict = PyObject_GetAttrString(inst, (char*)"__dict__"); - PyDict_SetItem(dict, SWIG_This(), swig_this); - Py_DECREF(dict); -} - - -SWIGINTERN PyObject * -SWIG_Python_InitShadowInstance(PyObject *args) { - PyObject *obj[2]; - if (!SWIG_Python_UnpackTuple(args, "swiginit", 2, 2, obj)) { - return NULL; - } else { - SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]); - if (sthis) { - SwigPyObject_append((PyObject*) sthis, obj[1]); - } else { - SWIG_Python_SetSwigThis(obj[0], obj[1]); - } - return SWIG_Py_Void(); - } -} - -/* Create a new pointer object */ - -SWIGRUNTIME PyObject * -SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) { - SwigPyClientData *clientdata; - PyObject * robj; - int own; - - if (!ptr) - return SWIG_Py_Void(); - - clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0; - own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0; - if (clientdata && clientdata->pytype) { - SwigPyObject *newobj; - if (flags & SWIG_BUILTIN_TP_INIT) { - newobj = (SwigPyObject*) self; - if (newobj->ptr) { - PyObject *next_self = clientdata->pytype->tp_alloc(clientdata->pytype, 0); - while (newobj->next) - newobj = (SwigPyObject *) newobj->next; - newobj->next = next_self; - newobj = (SwigPyObject *)next_self; -#ifdef SWIGPYTHON_BUILTIN - newobj->dict = 0; -#endif - } - } else { - newobj = PyObject_New(SwigPyObject, clientdata->pytype); -#ifdef SWIGPYTHON_BUILTIN - newobj->dict = 0; -#endif - } - if (newobj) { - newobj->ptr = ptr; - newobj->ty = type; - newobj->own = own; - newobj->next = 0; - return (PyObject*) newobj; - } - return SWIG_Py_Void(); - } - - assert(!(flags & SWIG_BUILTIN_TP_INIT)); - - robj = SwigPyObject_New(ptr, type, own); - if (robj && clientdata && !(flags & SWIG_POINTER_NOSHADOW)) { - PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj); - Py_DECREF(robj); - robj = inst; - } - return robj; -} - -/* Create a new packed object */ - -SWIGRUNTIMEINLINE PyObject * -SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { - return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void(); -} - -/* -----------------------------------------------------------------------------* - * Get type list - * -----------------------------------------------------------------------------*/ - -#ifdef SWIG_LINK_RUNTIME -void *SWIG_ReturnGlobalTypeList(void *); -#endif - -SWIGRUNTIME swig_module_info * -SWIG_Python_GetModule(void *SWIGUNUSEDPARM(clientdata)) { - static void *type_pointer = (void *)0; - /* first check if module already created */ - if (!type_pointer) { -#ifdef SWIG_LINK_RUNTIME - type_pointer = SWIG_ReturnGlobalTypeList((void *)0); -#else -# ifdef SWIGPY_USE_CAPSULE - type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0); -# else - type_pointer = PyCObject_Import((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, - (char*)"type_pointer" SWIG_TYPE_TABLE_NAME); -# endif - if (PyErr_Occurred()) { - PyErr_Clear(); - type_pointer = (void *)0; - } -#endif - } - return (swig_module_info *) type_pointer; -} - -#if PY_MAJOR_VERSION < 2 -/* PyModule_AddObject function was introduced in Python 2.0. The following function - is copied out of Python/modsupport.c in python version 2.3.4 */ -SWIGINTERN int -PyModule_AddObject(PyObject *m, char *name, PyObject *o) -{ - PyObject *dict; - if (!PyModule_Check(m)) { - PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs module as first arg"); - return SWIG_ERROR; - } - if (!o) { - PyErr_SetString(PyExc_TypeError, "PyModule_AddObject() needs non-NULL value"); - return SWIG_ERROR; - } - - dict = PyModule_GetDict(m); - if (dict == NULL) { - /* Internal error -- modules must have a dict! */ - PyErr_Format(PyExc_SystemError, "module '%s' has no __dict__", - PyModule_GetName(m)); - return SWIG_ERROR; - } - if (PyDict_SetItemString(dict, name, o)) - return SWIG_ERROR; - Py_DECREF(o); - return SWIG_OK; -} -#endif - -SWIGRUNTIME void -#ifdef SWIGPY_USE_CAPSULE -SWIG_Python_DestroyModule(PyObject *obj) -#else -SWIG_Python_DestroyModule(void *vptr) -#endif -{ -#ifdef SWIGPY_USE_CAPSULE - swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME); -#else - swig_module_info *swig_module = (swig_module_info *) vptr; -#endif - swig_type_info **types = swig_module->types; - size_t i; - for (i =0; i < swig_module->size; ++i) { - swig_type_info *ty = types[i]; - if (ty->owndata) { - SwigPyClientData *data = (SwigPyClientData *) ty->clientdata; - if (data) SwigPyClientData_Del(data); - } - } - Py_DECREF(SWIG_This()); - swig_this = NULL; -} - -SWIGRUNTIME void -SWIG_Python_SetModule(swig_module_info *swig_module) { -#if PY_VERSION_HEX >= 0x03000000 - /* Add a dummy module object into sys.modules */ - PyObject *module = PyImport_AddModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION); -#else - static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */ - PyObject *module = Py_InitModule((char*)"swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table); -#endif -#ifdef SWIGPY_USE_CAPSULE - PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule); - if (pointer && module) { - PyModule_AddObject(module, (char*)"type_pointer_capsule" SWIG_TYPE_TABLE_NAME, pointer); - } else { - Py_XDECREF(pointer); - } -#else - PyObject *pointer = PyCObject_FromVoidPtr((void *) swig_module, SWIG_Python_DestroyModule); - if (pointer && module) { - PyModule_AddObject(module, (char*)"type_pointer" SWIG_TYPE_TABLE_NAME, pointer); - } else { - Py_XDECREF(pointer); - } -#endif -} - -/* The python cached type query */ -SWIGRUNTIME PyObject * -SWIG_Python_TypeCache(void) { - static PyObject *SWIG_STATIC_POINTER(cache) = PyDict_New(); - return cache; -} - -SWIGRUNTIME swig_type_info * -SWIG_Python_TypeQuery(const char *type) -{ - PyObject *cache = SWIG_Python_TypeCache(); - PyObject *key = SWIG_Python_str_FromChar(type); - PyObject *obj = PyDict_GetItem(cache, key); - swig_type_info *descriptor; - if (obj) { -#ifdef SWIGPY_USE_CAPSULE - descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL); -#else - descriptor = (swig_type_info *) PyCObject_AsVoidPtr(obj); -#endif - } else { - swig_module_info *swig_module = SWIG_GetModule(0); - descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type); - if (descriptor) { -#ifdef SWIGPY_USE_CAPSULE - obj = PyCapsule_New((void*) descriptor, NULL, NULL); -#else - obj = PyCObject_FromVoidPtr(descriptor, NULL); -#endif - PyDict_SetItem(cache, key, obj); - Py_DECREF(obj); - } - } - Py_DECREF(key); - return descriptor; -} - -/* - For backward compatibility only -*/ -#define SWIG_POINTER_EXCEPTION 0 -#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) -#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) - -SWIGRUNTIME int -SWIG_Python_AddErrMesg(const char* mesg, int infront) -{ - if (PyErr_Occurred()) { - PyObject *type = 0; - PyObject *value = 0; - PyObject *traceback = 0; - PyErr_Fetch(&type, &value, &traceback); - if (value) { - char *tmp; - PyObject *old_str = PyObject_Str(value); - Py_XINCREF(type); - PyErr_Clear(); - if (infront) { - PyErr_Format(type, "%s %s", mesg, tmp = SWIG_Python_str_AsChar(old_str)); - } else { - PyErr_Format(type, "%s %s", tmp = SWIG_Python_str_AsChar(old_str), mesg); - } - SWIG_Python_str_DelForPy3(tmp); - Py_DECREF(old_str); - } - return 1; - } else { - return 0; - } -} - -SWIGRUNTIME int -SWIG_Python_ArgFail(int argnum) -{ - if (PyErr_Occurred()) { - /* add information about failing argument */ - char mesg[256]; - PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum); - return SWIG_Python_AddErrMesg(mesg, 1); - } else { - return 0; - } -} - -SWIGRUNTIMEINLINE const char * -SwigPyObject_GetDesc(PyObject *self) -{ - SwigPyObject *v = (SwigPyObject *)self; - swig_type_info *ty = v ? v->ty : 0; - return ty ? ty->str : ""; -} - -SWIGRUNTIME void -SWIG_Python_TypeError(const char *type, PyObject *obj) -{ - if (type) { -#if defined(SWIG_COBJECT_TYPES) - if (obj && SwigPyObject_Check(obj)) { - const char *otype = (const char *) SwigPyObject_GetDesc(obj); - if (otype) { - PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received", - type, otype); - return; - } - } else -#endif - { - const char *otype = (obj ? obj->ob_type->tp_name : 0); - if (otype) { - PyObject *str = PyObject_Str(obj); - const char *cstr = str ? SWIG_Python_str_AsChar(str) : 0; - if (cstr) { - PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", - type, otype, cstr); - SWIG_Python_str_DelForPy3(cstr); - } else { - PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", - type, otype); - } - Py_XDECREF(str); - return; - } - } - PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); - } else { - PyErr_Format(PyExc_TypeError, "unexpected type is received"); - } -} - - -/* Convert a pointer value, signal an exception on a type mismatch */ -SWIGRUNTIME void * -SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) { - void *result; - if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { - PyErr_Clear(); -#if SWIG_POINTER_EXCEPTION - if (flags) { - SWIG_Python_TypeError(SWIG_TypePrettyName(ty), obj); - SWIG_Python_ArgFail(argnum); - } -#endif - } - return result; -} - -#ifdef SWIGPYTHON_BUILTIN -SWIGRUNTIME int -SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) { - PyTypeObject *tp = obj->ob_type; - PyObject *descr; - PyObject *encoded_name; - descrsetfunc f; - int res = -1; - -# ifdef Py_USING_UNICODE - if (PyString_Check(name)) { - name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL); - if (!name) - return -1; - } else if (!PyUnicode_Check(name)) -# else - if (!PyString_Check(name)) -# endif - { - PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name); - return -1; - } else { - Py_INCREF(name); - } - - if (!tp->tp_dict) { - if (PyType_Ready(tp) < 0) - goto done; - } - - descr = _PyType_Lookup(tp, name); - f = NULL; - if (descr != NULL) - f = descr->ob_type->tp_descr_set; - if (!f) { - if (PyString_Check(name)) { - encoded_name = name; - Py_INCREF(name); - } else { - encoded_name = PyUnicode_AsUTF8String(name); - } - PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name)); - Py_DECREF(encoded_name); - } else { - res = f(descr, obj, value); - } - - done: - Py_DECREF(name); - return res; -} -#endif - - -#ifdef __cplusplus -} -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -SWIGINTERN Py_hash_t -SwigPyObject_hash(PyObject *obj) { - SwigPyObject *sobj = (SwigPyObject *)obj; - void *ptr = sobj->ptr; - return (Py_hash_t)ptr; -} - -SWIGINTERN Py_hash_t -SWIG_PyNumber_AsPyHash(PyObject *obj) { - Py_hash_t result = -1; -#if PY_VERSION_HEX < 0x03020000 -#if PY_VERSION_HEX < 0x03000000 - if (PyInt_Check(obj)) - result = PyInt_AsLong(obj); - else -#endif - if (PyLong_Check(obj)) - result = PyLong_AsLong(obj); -#else - if (PyNumber_Check(obj)) - result = PyNumber_AsSsize_t(obj, NULL); -#endif - else - PyErr_Format(PyExc_TypeError, "Wrong type for hash function"); - return PyErr_Occurred() ? -1 : result; -} - -SWIGINTERN int -SwigPyBuiltin_BadInit(PyObject *self, PyObject *SWIGUNUSEDPARM(args), PyObject *SWIGUNUSEDPARM(kwds)) { - PyErr_Format(PyExc_TypeError, "Cannot create new instances of type '%.300s'", self->ob_type->tp_name); - return -1; -} - -SWIGINTERN void -SwigPyBuiltin_BadDealloc(PyObject *obj) { - SwigPyObject *sobj = (SwigPyObject *)obj; - if (sobj->own) { - PyErr_Format(PyExc_TypeError, "Swig detected a memory leak in type '%.300s': no callable destructor found.", obj->ob_type->tp_name); - } -} - -typedef struct { - PyCFunction get; - PyCFunction set; -} SwigPyGetSet; - -SWIGINTERN PyObject * -SwigPyBuiltin_GetterClosure (PyObject *obj, void *closure) { - SwigPyGetSet *getset; - PyObject *tuple, *result; - if (!closure) - return SWIG_Py_Void(); - getset = (SwigPyGetSet *)closure; - if (!getset->get) - return SWIG_Py_Void(); - tuple = PyTuple_New(0); - assert(tuple); - result = (*getset->get)(obj, tuple); - Py_DECREF(tuple); - return result; -} - -SWIGINTERN PyObject * -SwigPyBuiltin_FunpackGetterClosure (PyObject *obj, void *closure) { - SwigPyGetSet *getset; - PyObject *result; - if (!closure) - return SWIG_Py_Void(); - getset = (SwigPyGetSet *)closure; - if (!getset->get) - return SWIG_Py_Void(); - result = (*getset->get)(obj, NULL); - return result; -} - -SWIGINTERN int -SwigPyBuiltin_SetterClosure (PyObject *obj, PyObject *val, void *closure) { - SwigPyGetSet *getset; - PyObject *tuple, *result; - if (!closure) { - PyErr_Format(PyExc_TypeError, "Missing getset closure"); - return -1; - } - getset = (SwigPyGetSet *)closure; - if (!getset->set) { - PyErr_Format(PyExc_TypeError, "Illegal member variable assignment in type '%.300s'", obj->ob_type->tp_name); - return -1; - } - tuple = PyTuple_New(1); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, val); - Py_XINCREF(val); - result = (*getset->set)(obj, tuple); - Py_DECREF(tuple); - Py_XDECREF(result); - return result ? 0 : -1; -} - -SWIGINTERN int -SwigPyBuiltin_FunpackSetterClosure (PyObject *obj, PyObject *val, void *closure) { - SwigPyGetSet *getset; - PyObject *result; - if (!closure) { - PyErr_Format(PyExc_TypeError, "Missing getset closure"); - return -1; - } - getset = (SwigPyGetSet *)closure; - if (!getset->set) { - PyErr_Format(PyExc_TypeError, "Illegal member variable assignment in type '%.300s'", obj->ob_type->tp_name); - return -1; - } - result = (*getset->set)(obj, val); - Py_XDECREF(result); - return result ? 0 : -1; -} - -SWIGINTERN void -SwigPyStaticVar_dealloc(PyDescrObject *descr) { - _PyObject_GC_UNTRACK(descr); - Py_XDECREF(PyDescr_TYPE(descr)); - Py_XDECREF(PyDescr_NAME(descr)); - PyObject_GC_Del(descr); -} - -SWIGINTERN PyObject * -SwigPyStaticVar_repr(PyGetSetDescrObject *descr) { -#if PY_VERSION_HEX >= 0x03000000 - - return PyUnicode_FromFormat("", PyDescr_NAME(descr), PyDescr_TYPE(descr)->tp_name); -#else - return PyString_FromFormat("", PyString_AsString(PyDescr_NAME(descr)), PyDescr_TYPE(descr)->tp_name); -#endif -} - -SWIGINTERN int -SwigPyStaticVar_traverse(PyObject *self, visitproc visit, void *arg) { - PyDescrObject *descr; - descr = (PyDescrObject *)self; - Py_VISIT((PyObject*) PyDescr_TYPE(descr)); - return 0; -} - -SWIGINTERN PyObject * -SwigPyStaticVar_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *SWIGUNUSEDPARM(type)) { - if (descr->d_getset->get != NULL) - return descr->d_getset->get(obj, descr->d_getset->closure); -#if PY_VERSION_HEX >= 0x03000000 - PyErr_Format(PyExc_AttributeError, "attribute '%.300S' of '%.100s' objects is not readable", PyDescr_NAME(descr), PyDescr_TYPE(descr)->tp_name); -#else - PyErr_Format(PyExc_AttributeError, "attribute '%.300s' of '%.100s' objects is not readable", PyString_AsString(PyDescr_NAME(descr)), PyDescr_TYPE(descr)->tp_name); -#endif - return NULL; -} - -SWIGINTERN int -SwigPyStaticVar_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) { - if (descr->d_getset->set != NULL) - return descr->d_getset->set(obj, value, descr->d_getset->closure); -#if PY_VERSION_HEX >= 0x03000000 - PyErr_Format(PyExc_AttributeError, "attribute '%.300S' of '%.100s' objects is not writable", PyDescr_NAME(descr), PyDescr_TYPE(descr)->tp_name); -#else - PyErr_Format(PyExc_AttributeError, "attribute '%.300s' of '%.100s' objects is not writable", PyString_AsString(PyDescr_NAME(descr)), PyDescr_TYPE(descr)->tp_name); -#endif - return -1; -} - -SWIGINTERN int -SwigPyObjectType_setattro(PyObject *typeobject, PyObject *name, PyObject *value) { - PyObject *attribute; - PyTypeObject *type; - descrsetfunc local_set; - - assert(PyType_Check(typeobject)); - type = (PyTypeObject *)typeobject; - attribute = _PyType_Lookup(type, name); - if (attribute != NULL) { - /* Implement descriptor functionality, if any */ - local_set = attribute->ob_type->tp_descr_set; - if (local_set != NULL) - return local_set(attribute, (PyObject *)type, value); -#if PY_VERSION_HEX >= 0x03000000 - PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%.50s.%.400S'", type->tp_name, name); -#else - PyErr_Format(PyExc_AttributeError, "cannot modify read-only attribute '%.50s.%.400s'", type->tp_name, PyString_AS_STRING(name)); -#endif - } else { -#if PY_VERSION_HEX >= 0x03000000 - PyErr_Format(PyExc_AttributeError, "type '%.50s' has no attribute '%.400S'", type->tp_name, name); -#else - PyErr_Format(PyExc_AttributeError, "type '%.50s' has no attribute '%.400s'", type->tp_name, PyString_AS_STRING(name)); -#endif - } - - return -1; -} - -SWIGINTERN PyTypeObject* -SwigPyStaticVar_Type(void) { - static PyTypeObject staticvar_type; - static int type_init = 0; - if (!type_init) { - const PyTypeObject tmp = { -#if PY_VERSION_HEX >= 0x03000000 - PyVarObject_HEAD_INIT(&PyType_Type, 0) -#else - PyObject_HEAD_INIT(&PyType_Type) - 0, /* ob_size */ -#endif - "swig_static_var_getset_descriptor", /* tp_name */ - sizeof(PyGetSetDescrObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)SwigPyStaticVar_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)SwigPyStaticVar_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_HAVE_CLASS, /* tp_flags */ - 0, /* tp_doc */ - SwigPyStaticVar_traverse, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)SwigPyStaticVar_get, /* tp_descr_get */ - (descrsetfunc)SwigPyStaticVar_set, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ -#if PY_VERSION_HEX >= 0x02030000 - 0, /* tp_del */ -#endif -#if PY_VERSION_HEX >= 0x02060000 - 0, /* tp_version_tag */ -#endif -#if PY_VERSION_HEX >= 0x03040000 - 0, /* tp_finalize */ -#endif -#ifdef COUNT_ALLOCS - 0, /* tp_allocs */ - 0, /* tp_frees */ - 0, /* tp_maxalloc */ -#if PY_VERSION_HEX >= 0x02050000 - 0, /* tp_prev */ -#endif - 0 /* tp_next */ -#endif - }; - staticvar_type = tmp; - type_init = 1; -#if PY_VERSION_HEX < 0x02020000 - staticvar_type.ob_type = &PyType_Type; -#else - if (PyType_Ready(&staticvar_type) < 0) - return NULL; -#endif - } - return &staticvar_type; -} - -SWIGINTERN PyTypeObject* -SwigPyObjectType(void) { - static char swigpyobjecttype_doc[] = "Metaclass for SWIG wrapped types"; - static PyTypeObject swigpyobjecttype_type; - static int type_init = 0; - if (!type_init) { - const PyTypeObject tmp = { -#if PY_VERSION_HEX >= 0x03000000 - PyVarObject_HEAD_INIT(&PyType_Type, 0) -#else - PyObject_HEAD_INIT(&PyType_Type) - 0, /* ob_size */ -#endif - "SwigPyObjectType", /* tp_name */ - PyType_Type.tp_basicsize, /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - SwigPyObjectType_setattro, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_CLASS, /* tp_flags */ - swigpyobjecttype_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ - 0, /* tp_free */ - 0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ -#if PY_VERSION_HEX >= 0x02030000 - 0, /* tp_del */ -#endif -#if PY_VERSION_HEX >= 0x02060000 - 0, /* tp_version_tag */ -#endif -#if PY_VERSION_HEX >= 0x03040000 - 0, /* tp_finalize */ -#endif -#ifdef COUNT_ALLOCS - 0, /* tp_allocs */ - 0, /* tp_frees */ - 0, /* tp_maxalloc */ -#if PY_VERSION_HEX >= 0x02050000 - 0, /* tp_prev */ -#endif - 0 /* tp_next */ -#endif - }; - swigpyobjecttype_type = tmp; - type_init = 1; - swigpyobjecttype_type.tp_base = &PyType_Type; -#if PY_VERSION_HEX < 0x02020000 - swigpyobjecttype_type.ob_type = &PyType_Type; -#else - if (PyType_Ready(&swigpyobjecttype_type) < 0) - return NULL; -#endif - } - return &swigpyobjecttype_type; -} - -SWIGINTERN PyGetSetDescrObject * -SwigPyStaticVar_new_getset(PyTypeObject *type, PyGetSetDef *getset) { - - PyGetSetDescrObject *descr; - descr = (PyGetSetDescrObject *)PyType_GenericAlloc(SwigPyStaticVar_Type(), 0); - assert(descr); - Py_XINCREF(type); - PyDescr_TYPE(descr) = type; - PyDescr_NAME(descr) = PyString_InternFromString(getset->name); - descr->d_getset = getset; - if (PyDescr_NAME(descr) == NULL) { - Py_DECREF(descr); - descr = NULL; - } - return descr; -} - -SWIGINTERN void -SwigPyBuiltin_InitBases (PyTypeObject *type, PyTypeObject **bases) { - int base_count = 0; - PyTypeObject **b; - PyObject *tuple; - int i; - - if (!bases[0]) { - bases[0] = SwigPyObject_type(); - bases[1] = NULL; - } - type->tp_base = bases[0]; - Py_INCREF((PyObject *)bases[0]); - for (b = bases; *b != NULL; ++b) - ++base_count; - tuple = PyTuple_New(base_count); - for (i = 0; i < base_count; ++i) { - PyTuple_SET_ITEM(tuple, i, (PyObject *)bases[i]); - Py_INCREF((PyObject *)bases[i]); - } - type->tp_bases = tuple; -} - -SWIGINTERN PyObject * -SwigPyBuiltin_ThisClosure (PyObject *self, void *SWIGUNUSEDPARM(closure)) { - PyObject *result; - result = (PyObject *)SWIG_Python_GetSwigThis(self); - Py_XINCREF(result); - return result; -} - -SWIGINTERN void -SwigPyBuiltin_SetMetaType (PyTypeObject *type, PyTypeObject *metatype) -{ -#if PY_VERSION_HEX >= 0x03000000 - type->ob_base.ob_base.ob_type = metatype; -#else - type->ob_type = metatype; -#endif -} - - -/* Start of callback function macros for use in PyTypeObject */ - -typedef PyObject *(*SwigPyWrapperFunction)(PyObject *, PyObject *); - -#define SWIGPY_UNARYFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_unaryfunc_closure(PyObject *a) { \ - return SwigPyBuiltin_unaryfunc_closure(wrapper, a); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_unaryfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) { - return wrapper(a, NULL); -} - -#define SWIGPY_DESTRUCTOR_CLOSURE(wrapper) \ -SWIGINTERN void \ -wrapper##_destructor_closure(PyObject *a) { \ - SwigPyBuiltin_destructor_closure(wrapper, #wrapper, a); \ -} -SWIGINTERN void -SwigPyBuiltin_destructor_closure(SwigPyWrapperFunction wrapper, const char *wrappername, PyObject *a) { - SwigPyObject *sobj; - sobj = (SwigPyObject *)a; - Py_XDECREF(sobj->dict); - if (sobj->own) { - PyObject *o; - PyObject *val = 0, *type = 0, *tb = 0; - PyErr_Fetch(&val, &type, &tb); - o = wrapper(a, NULL); - if (!o) { - PyObject *deallocname = PyString_FromString(wrappername); - PyErr_WriteUnraisable(deallocname); - Py_DECREF(deallocname); - } - PyErr_Restore(val, type, tb); - Py_XDECREF(o); - } - if (PyType_IS_GC(a->ob_type)) { - PyObject_GC_Del(a); - } else { - PyObject_Del(a); - } -} - -#define SWIGPY_INQUIRY_CLOSURE(wrapper) \ -SWIGINTERN int \ -wrapper##_inquiry_closure(PyObject *a) { \ - return SwigPyBuiltin_inquiry_closure(wrapper, a); \ -} -SWIGINTERN int -SwigPyBuiltin_inquiry_closure(SwigPyWrapperFunction wrapper, PyObject *a) { - PyObject *pyresult; - int result; - pyresult = wrapper(a, NULL); - result = pyresult && PyObject_IsTrue(pyresult) ? 1 : 0; - Py_XDECREF(pyresult); - return result; -} - -#define SWIGPY_GETITERFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_getiterfunc_closure(PyObject *a) { \ - return SwigPyBuiltin_getiterfunc_closure(wrapper, a); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_getiterfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) { - return wrapper(a, NULL); -} - -#define SWIGPY_BINARYFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_binaryfunc_closure(PyObject *a, PyObject *b) { \ - return SwigPyBuiltin_binaryfunc_closure(wrapper, a, b); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_binaryfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b) { - PyObject *tuple, *result; - tuple = PyTuple_New(1); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, b); - Py_XINCREF(b); - result = wrapper(a, tuple); - Py_DECREF(tuple); - return result; -} - -typedef ternaryfunc ternarycallfunc; - -#define SWIGPY_TERNARYFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_ternaryfunc_closure(PyObject *a, PyObject *b, PyObject *c) { \ - return SwigPyBuiltin_ternaryfunc_closure(wrapper, a, b, c); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_ternaryfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b, PyObject *c) { - PyObject *tuple, *result; - tuple = PyTuple_New(2); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, b); - PyTuple_SET_ITEM(tuple, 1, c); - Py_XINCREF(b); - Py_XINCREF(c); - result = wrapper(a, tuple); - Py_DECREF(tuple); - return result; -} - -#define SWIGPY_TERNARYCALLFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_ternarycallfunc_closure(PyObject *a, PyObject *b, PyObject *c) { \ - return SwigPyBuiltin_ternarycallfunc_closure(wrapper, a, b, c); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_ternarycallfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b, PyObject *c) { - (void) c; - return wrapper(a, b); -} - -#define SWIGPY_LENFUNC_CLOSURE(wrapper) \ -SWIGINTERN Py_ssize_t \ -wrapper##_lenfunc_closure(PyObject *a) { \ - return SwigPyBuiltin_lenfunc_closure(wrapper, a); \ -} -SWIGINTERN Py_ssize_t -SwigPyBuiltin_lenfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) { - PyObject *resultobj; - Py_ssize_t result; - resultobj = wrapper(a, NULL); - result = PyNumber_AsSsize_t(resultobj, NULL); - Py_DECREF(resultobj); - return result; -} - -#define SWIGPY_SSIZESSIZEARGFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_ssizessizeargfunc_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c) { \ - return SwigPyBuiltin_ssizessizeargfunc_closure(wrapper, a, b, c); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_ssizessizeargfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b, Py_ssize_t c) { - PyObject *tuple, *result; - tuple = PyTuple_New(2); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b)); - PyTuple_SET_ITEM(tuple, 1, _PyLong_FromSsize_t(c)); - result = wrapper(a, tuple); - Py_DECREF(tuple); - return result; -} - -#define SWIGPY_SSIZESSIZEOBJARGPROC_CLOSURE(wrapper) \ -SWIGINTERN int \ -wrapper##_ssizessizeobjargproc_closure(PyObject *a, Py_ssize_t b, Py_ssize_t c, PyObject *d) { \ - return SwigPyBuiltin_ssizessizeobjargproc_closure(wrapper, a, b, c, d); \ -} -SWIGINTERN int -SwigPyBuiltin_ssizessizeobjargproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b, Py_ssize_t c, PyObject *d) { - PyObject *tuple, *resultobj; - int result; - tuple = PyTuple_New(d ? 3 : 2); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b)); - PyTuple_SET_ITEM(tuple, 1, _PyLong_FromSsize_t(c)); - if (d) { - PyTuple_SET_ITEM(tuple, 2, d); - Py_INCREF(d); - } - resultobj = wrapper(a, tuple); - result = resultobj ? 0 : -1; - Py_DECREF(tuple); - Py_XDECREF(resultobj); - return result; -} - -#define SWIGPY_SSIZEARGFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_ssizeargfunc_closure(PyObject *a, Py_ssize_t b) { \ - return SwigPyBuiltin_funpack_ssizeargfunc_closure(wrapper, a, b); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_funpack_ssizeargfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b) { - PyObject *tuple, *result; - tuple = PyTuple_New(1); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b)); - result = wrapper(a, tuple); - Py_DECREF(tuple); - return result; -} - -#define SWIGPY_FUNPACK_SSIZEARGFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_ssizeargfunc_closure(PyObject *a, Py_ssize_t b) { \ - return SwigPyBuiltin_ssizeargfunc_closure(wrapper, a, b); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_ssizeargfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b) { - PyObject *arg, *result; - arg = _PyLong_FromSsize_t(b); - result = wrapper(a, arg); - Py_DECREF(arg); - return result; -} - -#define SWIGPY_SSIZEOBJARGPROC_CLOSURE(wrapper) \ -SWIGINTERN int \ -wrapper##_ssizeobjargproc_closure(PyObject *a, Py_ssize_t b, PyObject *c) { \ - return SwigPyBuiltin_ssizeobjargproc_closure(wrapper, a, b, c); \ -} -SWIGINTERN int -SwigPyBuiltin_ssizeobjargproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, Py_ssize_t b, PyObject *c) { - PyObject *tuple, *resultobj; - int result; - tuple = PyTuple_New(2); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, _PyLong_FromSsize_t(b)); - PyTuple_SET_ITEM(tuple, 1, c); - Py_XINCREF(c); - resultobj = wrapper(a, tuple); - result = resultobj ? 0 : -1; - Py_XDECREF(resultobj); - Py_DECREF(tuple); - return result; -} - -#define SWIGPY_OBJOBJARGPROC_CLOSURE(wrapper) \ -SWIGINTERN int \ -wrapper##_objobjargproc_closure(PyObject *a, PyObject *b, PyObject *c) { \ - return SwigPyBuiltin_objobjargproc_closure(wrapper, a, b, c); \ -} -SWIGINTERN int -SwigPyBuiltin_objobjargproc_closure(SwigPyWrapperFunction wrapper, PyObject *a, PyObject *b, PyObject *c) { - PyObject *tuple, *resultobj; - int result; - tuple = PyTuple_New(c ? 2 : 1); - assert(tuple); - PyTuple_SET_ITEM(tuple, 0, b); - Py_XINCREF(b); - if (c) { - PyTuple_SET_ITEM(tuple, 1, c); - Py_XINCREF(c); - } - resultobj = wrapper(a, tuple); - result = resultobj ? 0 : -1; - Py_XDECREF(resultobj); - Py_DECREF(tuple); - return result; -} - -#define SWIGPY_REPRFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_reprfunc_closure(PyObject *a) { \ - return SwigPyBuiltin_reprfunc_closure(wrapper, a); \ -} -SWIGINTERN PyObject * -SwigPyBuiltin_reprfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) { - return wrapper(a, NULL); -} - -#define SWIGPY_HASHFUNC_CLOSURE(wrapper) \ -SWIGINTERN Py_hash_t \ -wrapper##_hashfunc_closure(PyObject *a) { \ - return SwigPyBuiltin_hashfunc_closure(wrapper, a); \ -} -SWIGINTERN Py_hash_t -SwigPyBuiltin_hashfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) { - PyObject *pyresult; - Py_hash_t result; - pyresult = wrapper(a, NULL); - if (!pyresult) - return -1; - result = SWIG_PyNumber_AsPyHash(pyresult); - Py_DECREF(pyresult); - return result; -} - -#define SWIGPY_ITERNEXTFUNC_CLOSURE(wrapper) \ -SWIGINTERN PyObject * \ -wrapper##_iternextfunc_closure(PyObject *a) { \ - return SwigPyBuiltin_iternextfunc_closure(wrapper, a);\ -} -SWIGINTERN PyObject * -SwigPyBuiltin_iternextfunc_closure(SwigPyWrapperFunction wrapper, PyObject *a) { - return wrapper(a, NULL); -} - -/* End of callback function macros for use in PyTypeObject */ - -#ifdef __cplusplus -} -#endif - - - - -#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) - -#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else - - - -/* -------- TYPES TABLE (BEGIN) -------- */ - -#define SWIGTYPE_p_SwigPyObject swig_types[0] -#define SWIGTYPE_p_char swig_types[1] -static swig_type_info *swig_types[3]; -static swig_module_info swig_module = {swig_types, 2, 0, 0, 0, 0}; -#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) -#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) - -/* -------- TYPES TABLE (END) -------- */ - -#if (PY_VERSION_HEX <= 0x02000000) -# if !defined(SWIG_PYTHON_CLASSIC) -# error "This python version requires swig to be run with the '-classic' option" -# endif -#endif - -/*----------------------------------------------- - @(target):= _libhsmd.so - ------------------------------------------------*/ -#if PY_VERSION_HEX >= 0x03000000 -# define SWIG_init PyInit__libhsmd - -#else -# define SWIG_init init_libhsmd - -#endif -#define SWIG_name "_libhsmd" - -#define SWIGVERSION 0x030012 -#define SWIG_VERSION SWIGVERSION - - -#define SWIG_as_voidptr(a) (void *)((const void *)(a)) -#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) - - -#include - - -#define SWIG_FILE_WITH_INIT -#include "libhsmd_python.h" - - -SWIGINTERN int -SWIG_AsVal_double (PyObject *obj, double *val) -{ - int res = SWIG_TypeError; - if (PyFloat_Check(obj)) { - if (val) *val = PyFloat_AsDouble(obj); - return SWIG_OK; -#if PY_VERSION_HEX < 0x03000000 - } else if (PyInt_Check(obj)) { - if (val) *val = (double) PyInt_AsLong(obj); - return SWIG_OK; -#endif - } else if (PyLong_Check(obj)) { - double v = PyLong_AsDouble(obj); - if (!PyErr_Occurred()) { - if (val) *val = v; - return SWIG_OK; - } else { - PyErr_Clear(); - } - } -#ifdef SWIG_PYTHON_CAST_MODE - { - int dispatch = 0; - double d = PyFloat_AsDouble(obj); - if (!PyErr_Occurred()) { - if (val) *val = d; - return SWIG_AddCast(SWIG_OK); - } else { - PyErr_Clear(); - } - if (!dispatch) { - long v = PyLong_AsLong(obj); - if (!PyErr_Occurred()) { - if (val) *val = v; - return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); - } else { - PyErr_Clear(); - } - } - } -#endif - return res; -} - - -#include - - -#include - - -SWIGINTERNINLINE int -SWIG_CanCastAsInteger(double *d, double min, double max) { - double x = *d; - if ((min <= x && x <= max)) { - double fx = floor(x); - double cx = ceil(x); - double rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ - if ((errno == EDOM) || (errno == ERANGE)) { - errno = 0; - } else { - double summ, reps, diff; - if (rd < x) { - diff = x - rd; - } else if (rd > x) { - diff = rd - x; - } else { - return 1; - } - summ = rd + x; - reps = diff/summ; - if (reps < 8*DBL_EPSILON) { - *d = rd; - return 1; - } - } - } - return 0; -} - - -SWIGINTERN int -SWIG_AsVal_long (PyObject *obj, long* val) -{ -#if PY_VERSION_HEX < 0x03000000 - if (PyInt_Check(obj)) { - if (val) *val = PyInt_AsLong(obj); - return SWIG_OK; - } else -#endif - if (PyLong_Check(obj)) { - long v = PyLong_AsLong(obj); - if (!PyErr_Occurred()) { - if (val) *val = v; - return SWIG_OK; - } else { - PyErr_Clear(); - return SWIG_OverflowError; - } - } -#ifdef SWIG_PYTHON_CAST_MODE - { - int dispatch = 0; - long v = PyInt_AsLong(obj); - if (!PyErr_Occurred()) { - if (val) *val = v; - return SWIG_AddCast(SWIG_OK); - } else { - PyErr_Clear(); - } - if (!dispatch) { - double d; - int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); - if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) { - if (val) *val = (long)(d); - return res; - } - } - } -#endif - return SWIG_TypeError; -} - - -#include -#if !defined(SWIG_NO_LLONG_MAX) -# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) -# define LLONG_MAX __LONG_LONG_MAX__ -# define LLONG_MIN (-LLONG_MAX - 1LL) -# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) -# endif -#endif - - -#if defined(LLONG_MAX) && !defined(SWIG_LONG_LONG_AVAILABLE) -# define SWIG_LONG_LONG_AVAILABLE -#endif - - -#ifdef SWIG_LONG_LONG_AVAILABLE -SWIGINTERN int -SWIG_AsVal_long_SS_long (PyObject *obj, long long *val) -{ - int res = SWIG_TypeError; - if (PyLong_Check(obj)) { - long long v = PyLong_AsLongLong(obj); - if (!PyErr_Occurred()) { - if (val) *val = v; - return SWIG_OK; - } else { - PyErr_Clear(); - res = SWIG_OverflowError; - } - } else { - long v; - res = SWIG_AsVal_long (obj,&v); - if (SWIG_IsOK(res)) { - if (val) *val = v; - return res; - } - } -#ifdef SWIG_PYTHON_CAST_MODE - { - const double mant_max = 1LL << DBL_MANT_DIG; - const double mant_min = -mant_max; - double d; - res = SWIG_AsVal_double (obj,&d); - if (SWIG_IsOK(res) && !SWIG_CanCastAsInteger(&d, mant_min, mant_max)) - return SWIG_OverflowError; - if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, mant_min, mant_max)) { - if (val) *val = (long long)(d); - return SWIG_AddCast(res); - } - res = SWIG_TypeError; - } -#endif - return res; -} -#endif - - -SWIGINTERN swig_type_info* -SWIG_pchar_descriptor(void) -{ - static int init = 0; - static swig_type_info* info = 0; - if (!init) { - info = SWIG_TypeQuery("_p_char"); - init = 1; - } - return info; -} - - -SWIGINTERN int -SWIG_AsCharPtrAndSize(PyObject *obj, char** cptr, size_t* psize, int *alloc) -{ -#if PY_VERSION_HEX>=0x03000000 -#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) - if (PyBytes_Check(obj)) -#else - if (PyUnicode_Check(obj)) -#endif -#else - if (PyString_Check(obj)) -#endif - { - char *cstr; Py_ssize_t len; -#if PY_VERSION_HEX>=0x03000000 -#if !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) - if (!alloc && cptr) { - /* We can't allow converting without allocation, since the internal - representation of string in Python 3 is UCS-2/UCS-4 but we require - a UTF-8 representation. - TODO(bhy) More detailed explanation */ - return SWIG_RuntimeError; - } - obj = PyUnicode_AsUTF8String(obj); - if(alloc) *alloc = SWIG_NEWOBJ; -#endif - PyBytes_AsStringAndSize(obj, &cstr, &len); -#else - PyString_AsStringAndSize(obj, &cstr, &len); -#endif - if (cptr) { - if (alloc) { - /* - In python the user should not be able to modify the inner - string representation. To warranty that, if you define - SWIG_PYTHON_SAFE_CSTRINGS, a new/copy of the python string - buffer is always returned. - - The default behavior is just to return the pointer value, - so, be careful. - */ -#if defined(SWIG_PYTHON_SAFE_CSTRINGS) - if (*alloc != SWIG_OLDOBJ) -#else - if (*alloc == SWIG_NEWOBJ) -#endif - { - *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); - *alloc = SWIG_NEWOBJ; - } else { - *cptr = cstr; - *alloc = SWIG_OLDOBJ; - } - } else { -#if PY_VERSION_HEX>=0x03000000 -#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) - *cptr = PyBytes_AsString(obj); -#else - assert(0); /* Should never reach here with Unicode strings in Python 3 */ -#endif -#else - *cptr = SWIG_Python_str_AsChar(obj); -#endif - } - } - if (psize) *psize = len + 1; -#if PY_VERSION_HEX>=0x03000000 && !defined(SWIG_PYTHON_STRICT_BYTE_CHAR) - Py_XDECREF(obj); -#endif - return SWIG_OK; - } else { -#if defined(SWIG_PYTHON_2_UNICODE) -#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) -#error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once" -#endif -#if PY_VERSION_HEX<0x03000000 - if (PyUnicode_Check(obj)) { - char *cstr; Py_ssize_t len; - if (!alloc && cptr) { - return SWIG_RuntimeError; - } - obj = PyUnicode_AsUTF8String(obj); - if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) { - if (cptr) { - if (alloc) *alloc = SWIG_NEWOBJ; - *cptr = (char *)memcpy(malloc((len + 1)*sizeof(char)), cstr, sizeof(char)*(len + 1)); - } - if (psize) *psize = len + 1; - - Py_XDECREF(obj); - return SWIG_OK; - } else { - Py_XDECREF(obj); - } - } -#endif -#endif - - swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); - if (pchar_descriptor) { - void* vptr = 0; - if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { - if (cptr) *cptr = (char *) vptr; - if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; - if (alloc) *alloc = SWIG_OLDOBJ; - return SWIG_OK; - } - } - } - return SWIG_TypeError; -} - - - - - -SWIGINTERNINLINE PyObject * -SWIG_FromCharPtrAndSize(const char* carray, size_t size) -{ - if (carray) { - if (size > INT_MAX) { - swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); - return pchar_descriptor ? - SWIG_InternalNewPointerObj((char *)(carray), pchar_descriptor, 0) : SWIG_Py_Void(); - } else { -#if PY_VERSION_HEX >= 0x03000000 -#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) - return PyBytes_FromStringAndSize(carray, (Py_ssize_t)(size)); -#else -#if PY_VERSION_HEX >= 0x03010000 - return PyUnicode_DecodeUTF8(carray, (Py_ssize_t)(size), "surrogateescape"); -#else - return PyUnicode_FromStringAndSize(carray, (Py_ssize_t)(size)); -#endif -#endif -#else - return PyString_FromStringAndSize(carray, (Py_ssize_t)(size)); -#endif - } - } else { - return SWIG_Py_Void(); - } -} - - -SWIGINTERNINLINE PyObject * -SWIG_FromCharPtr(const char *cptr) -{ - return SWIG_FromCharPtrAndSize(cptr, (cptr ? strlen(cptr) : 0)); -} - -#ifdef __cplusplus -extern "C" { -#endif -SWIGINTERN PyObject *_wrap_handle(PyObject *self, PyObject *args) { - PyObject *resultobj = 0; - long long arg1 ; - long long arg2 ; - char *arg3 = (char *) 0 ; - char *arg4 = (char *) 0 ; - long long val1 ; - int ecode1 = 0 ; - long long val2 ; - int ecode2 = 0 ; - int res3 ; - char *buf3 = 0 ; - int alloc3 = 0 ; - int res4 ; - char *buf4 = 0 ; - int alloc4 = 0 ; - PyObject * obj0 = 0 ; - PyObject * obj1 = 0 ; - PyObject * obj2 = 0 ; - PyObject * obj3 = 0 ; - char *result = 0 ; - - if (!PyArg_ParseTuple(args,(char *)"OOOO:handle",&obj0,&obj1,&obj2,&obj3)) SWIG_fail; - ecode1 = SWIG_AsVal_long_SS_long(obj0, &val1); - if (!SWIG_IsOK(ecode1)) { - SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "handle" "', argument " "1"" of type '" "long long""'"); - } - arg1 = (long long)(val1); - ecode2 = SWIG_AsVal_long_SS_long(obj1, &val2); - if (!SWIG_IsOK(ecode2)) { - SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "handle" "', argument " "2"" of type '" "long long""'"); - } - arg2 = (long long)(val2); - res3 = SWIG_AsCharPtrAndSize(obj2, &buf3, NULL, &alloc3); - if (!SWIG_IsOK(res3)) { - SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "handle" "', argument " "3"" of type '" "char *""'"); - } - arg3 = (char *)(buf3); - res4 = SWIG_AsCharPtrAndSize(obj3, &buf4, NULL, &alloc4); - if (!SWIG_IsOK(res4)) { - SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "handle" "', argument " "4"" of type '" "char *""'"); - } - arg4 = (char *)(buf4); - result = (char *)handle(arg1,arg2,arg3,arg4); - resultobj = SWIG_FromCharPtr((const char *)result); - if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); - if (alloc4 == SWIG_NEWOBJ) free((char*)buf4); - return resultobj; -fail: - if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); - if (alloc4 == SWIG_NEWOBJ) free((char*)buf4); - return NULL; -} - - -SWIGINTERN PyObject *_wrap_init(PyObject *self, PyObject *args) { - PyObject *resultobj = 0; - char *arg1 = (char *) 0 ; - char *arg2 = (char *) 0 ; - int res1 ; - char *buf1 = 0 ; - int alloc1 = 0 ; - int res2 ; - char *buf2 = 0 ; - int alloc2 = 0 ; - PyObject * obj0 = 0 ; - PyObject * obj1 = 0 ; - char *result = 0 ; - - if (!PyArg_ParseTuple(args,(char *)"OO:init",&obj0,&obj1)) SWIG_fail; - res1 = SWIG_AsCharPtrAndSize(obj0, &buf1, NULL, &alloc1); - if (!SWIG_IsOK(res1)) { - SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "init" "', argument " "1"" of type '" "char *""'"); - } - arg1 = (char *)(buf1); - res2 = SWIG_AsCharPtrAndSize(obj1, &buf2, NULL, &alloc2); - if (!SWIG_IsOK(res2)) { - SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "init" "', argument " "2"" of type '" "char *""'"); - } - arg2 = (char *)(buf2); - result = (char *)init(arg1,arg2); - resultobj = SWIG_FromCharPtr((const char *)result); - if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); - if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); - return resultobj; -fail: - if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); - if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); - return NULL; -} - - -static PyMethodDef SwigMethods[] = { - { (char *)"SWIG_PyInstanceMethod_New", (PyCFunction)SWIG_PyInstanceMethod_New, METH_O, NULL}, - { (char *)"handle", _wrap_handle, METH_VARARGS, NULL}, - { (char *)"init", _wrap_init, METH_VARARGS, NULL}, - { NULL, NULL, 0, NULL } -}; - - -/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ - -static swig_type_info _swigt__p_SwigPyObject = {"_p_SwigPyObject", "SwigPyObject *", 0, 0, (void*)0, 0}; -static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; - -static swig_type_info *swig_type_initial[] = { - &_swigt__p_SwigPyObject, - &_swigt__p_char, -}; - -static swig_cast_info _swigc__p_SwigPyObject[] = { {&_swigt__p_SwigPyObject, 0, 0, 0},{0, 0, 0, 0}}; -static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; - -static swig_cast_info *swig_cast_initial[] = { - _swigc__p_SwigPyObject, - _swigc__p_char, -}; - - -/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ - -static swig_const_info swig_const_table[] = { -{0, 0, 0, 0.0, 0, 0}}; - -#ifdef __cplusplus -} -#endif -/* ----------------------------------------------------------------------------- - * Type initialization: - * This problem is tough by the requirement that no dynamic - * memory is used. Also, since swig_type_info structures store pointers to - * swig_cast_info structures and swig_cast_info structures store pointers back - * to swig_type_info structures, we need some lookup code at initialization. - * The idea is that swig generates all the structures that are needed. - * The runtime then collects these partially filled structures. - * The SWIG_InitializeModule function takes these initial arrays out of - * swig_module, and does all the lookup, filling in the swig_module.types - * array with the correct data and linking the correct swig_cast_info - * structures together. - * - * The generated swig_type_info structures are assigned statically to an initial - * array. We just loop through that array, and handle each type individually. - * First we lookup if this type has been already loaded, and if so, use the - * loaded structure instead of the generated one. Then we have to fill in the - * cast linked list. The cast data is initially stored in something like a - * two-dimensional array. Each row corresponds to a type (there are the same - * number of rows as there are in the swig_type_initial array). Each entry in - * a column is one of the swig_cast_info structures for that type. - * The cast_initial array is actually an array of arrays, because each row has - * a variable number of columns. So to actually build the cast linked list, - * we find the array of casts associated with the type, and loop through it - * adding the casts to the list. The one last trick we need to do is making - * sure the type pointer in the swig_cast_info struct is correct. - * - * First off, we lookup the cast->type name to see if it is already loaded. - * There are three cases to handle: - * 1) If the cast->type has already been loaded AND the type we are adding - * casting info to has not been loaded (it is in this module), THEN we - * replace the cast->type pointer with the type pointer that has already - * been loaded. - * 2) If BOTH types (the one we are adding casting info to, and the - * cast->type) are loaded, THEN the cast info has already been loaded by - * the previous module so we just ignore it. - * 3) Finally, if cast->type has not already been loaded, then we add that - * swig_cast_info to the linked list (because the cast->type) pointer will - * be correct. - * ----------------------------------------------------------------------------- */ - -#ifdef __cplusplus -extern "C" { -#if 0 -} /* c-mode */ -#endif -#endif - -#if 0 -#define SWIGRUNTIME_DEBUG -#endif - - -SWIGRUNTIME void -SWIG_InitializeModule(void *clientdata) { - size_t i; - swig_module_info *module_head, *iter; - int init; - - /* check to see if the circular list has been setup, if not, set it up */ - if (swig_module.next==0) { - /* Initialize the swig_module */ - swig_module.type_initial = swig_type_initial; - swig_module.cast_initial = swig_cast_initial; - swig_module.next = &swig_module; - init = 1; - } else { - init = 0; - } - - /* Try and load any already created modules */ - module_head = SWIG_GetModule(clientdata); - if (!module_head) { - /* This is the first module loaded for this interpreter */ - /* so set the swig module into the interpreter */ - SWIG_SetModule(clientdata, &swig_module); - } else { - /* the interpreter has loaded a SWIG module, but has it loaded this one? */ - iter=module_head; - do { - if (iter==&swig_module) { - /* Our module is already in the list, so there's nothing more to do. */ - return; - } - iter=iter->next; - } while (iter!= module_head); - - /* otherwise we must add our module into the list */ - swig_module.next = module_head->next; - module_head->next = &swig_module; - } - - /* When multiple interpreters are used, a module could have already been initialized in - a different interpreter, but not yet have a pointer in this interpreter. - In this case, we do not want to continue adding types... everything should be - set up already */ - if (init == 0) return; - - /* Now work on filling in swig_module.types */ -#ifdef SWIGRUNTIME_DEBUG - printf("SWIG_InitializeModule: size %d\n", swig_module.size); -#endif - for (i = 0; i < swig_module.size; ++i) { - swig_type_info *type = 0; - swig_type_info *ret; - swig_cast_info *cast; - -#ifdef SWIGRUNTIME_DEBUG - printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); -#endif - - /* if there is another module already loaded */ - if (swig_module.next != &swig_module) { - type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); - } - if (type) { - /* Overwrite clientdata field */ -#ifdef SWIGRUNTIME_DEBUG - printf("SWIG_InitializeModule: found type %s\n", type->name); -#endif - if (swig_module.type_initial[i]->clientdata) { - type->clientdata = swig_module.type_initial[i]->clientdata; -#ifdef SWIGRUNTIME_DEBUG - printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); -#endif - } - } else { - type = swig_module.type_initial[i]; - } - - /* Insert casting types */ - cast = swig_module.cast_initial[i]; - while (cast->type) { - /* Don't need to add information already in the list */ - ret = 0; -#ifdef SWIGRUNTIME_DEBUG - printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); -#endif - if (swig_module.next != &swig_module) { - ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); -#ifdef SWIGRUNTIME_DEBUG - if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); -#endif - } - if (ret) { - if (type == swig_module.type_initial[i]) { -#ifdef SWIGRUNTIME_DEBUG - printf("SWIG_InitializeModule: skip old type %s\n", ret->name); -#endif - cast->type = ret; - ret = 0; - } else { - /* Check for casting already in the list */ - swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); -#ifdef SWIGRUNTIME_DEBUG - if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); -#endif - if (!ocast) ret = 0; - } - } - - if (!ret) { -#ifdef SWIGRUNTIME_DEBUG - printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); -#endif - if (type->cast) { - type->cast->prev = cast; - cast->next = type->cast; - } - type->cast = cast; - } - cast++; - } - /* Set entry in modules->types array equal to the type */ - swig_module.types[i] = type; - } - swig_module.types[i] = 0; - -#ifdef SWIGRUNTIME_DEBUG - printf("**** SWIG_InitializeModule: Cast List ******\n"); - for (i = 0; i < swig_module.size; ++i) { - int j = 0; - swig_cast_info *cast = swig_module.cast_initial[i]; - printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); - while (cast->type) { - printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); - cast++; - ++j; - } - printf("---- Total casts: %d\n",j); - } - printf("**** SWIG_InitializeModule: Cast List ******\n"); -#endif -} - -/* This function will propagate the clientdata field of type to -* any new swig_type_info structures that have been added into the list -* of equivalent types. It is like calling -* SWIG_TypeClientData(type, clientdata) a second time. -*/ -SWIGRUNTIME void -SWIG_PropagateClientData(void) { - size_t i; - swig_cast_info *equiv; - static int init_run = 0; - - if (init_run) return; - init_run = 1; - - for (i = 0; i < swig_module.size; i++) { - if (swig_module.types[i]->clientdata) { - equiv = swig_module.types[i]->cast; - while (equiv) { - if (!equiv->converter) { - if (equiv->type && !equiv->type->clientdata) - SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); - } - equiv = equiv->next; - } - } - } -} - -#ifdef __cplusplus -#if 0 -{ - /* c-mode */ -#endif -} -#endif - - - -#ifdef __cplusplus -extern "C" { -#endif - - /* Python-specific SWIG API */ -#define SWIG_newvarlink() SWIG_Python_newvarlink() -#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) -#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) - - /* ----------------------------------------------------------------------------- - * global variable support code. - * ----------------------------------------------------------------------------- */ - - typedef struct swig_globalvar { - char *name; /* Name of global variable */ - PyObject *(*get_attr)(void); /* Return the current value */ - int (*set_attr)(PyObject *); /* Set the value */ - struct swig_globalvar *next; - } swig_globalvar; - - typedef struct swig_varlinkobject { - PyObject_HEAD - swig_globalvar *vars; - } swig_varlinkobject; - - SWIGINTERN PyObject * - swig_varlink_repr(swig_varlinkobject *SWIGUNUSEDPARM(v)) { -#if PY_VERSION_HEX >= 0x03000000 - return PyUnicode_InternFromString(""); -#else - return PyString_FromString(""); -#endif - } - - SWIGINTERN PyObject * - swig_varlink_str(swig_varlinkobject *v) { -#if PY_VERSION_HEX >= 0x03000000 - PyObject *str = PyUnicode_InternFromString("("); - PyObject *tail; - PyObject *joined; - swig_globalvar *var; - for (var = v->vars; var; var=var->next) { - tail = PyUnicode_FromString(var->name); - joined = PyUnicode_Concat(str, tail); - Py_DecRef(str); - Py_DecRef(tail); - str = joined; - if (var->next) { - tail = PyUnicode_InternFromString(", "); - joined = PyUnicode_Concat(str, tail); - Py_DecRef(str); - Py_DecRef(tail); - str = joined; - } - } - tail = PyUnicode_InternFromString(")"); - joined = PyUnicode_Concat(str, tail); - Py_DecRef(str); - Py_DecRef(tail); - str = joined; -#else - PyObject *str = PyString_FromString("("); - swig_globalvar *var; - for (var = v->vars; var; var=var->next) { - PyString_ConcatAndDel(&str,PyString_FromString(var->name)); - if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", ")); - } - PyString_ConcatAndDel(&str,PyString_FromString(")")); -#endif - return str; - } - - SWIGINTERN int - swig_varlink_print(swig_varlinkobject *v, FILE *fp, int SWIGUNUSEDPARM(flags)) { - char *tmp; - PyObject *str = swig_varlink_str(v); - fprintf(fp,"Swig global variables "); - fprintf(fp,"%s\n", tmp = SWIG_Python_str_AsChar(str)); - SWIG_Python_str_DelForPy3(tmp); - Py_DECREF(str); - return 0; - } - - SWIGINTERN void - swig_varlink_dealloc(swig_varlinkobject *v) { - swig_globalvar *var = v->vars; - while (var) { - swig_globalvar *n = var->next; - free(var->name); - free(var); - var = n; - } - } - - SWIGINTERN PyObject * - swig_varlink_getattr(swig_varlinkobject *v, char *n) { - PyObject *res = NULL; - swig_globalvar *var = v->vars; - while (var) { - if (strcmp(var->name,n) == 0) { - res = (*var->get_attr)(); - break; - } - var = var->next; - } - if (res == NULL && !PyErr_Occurred()) { - PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); - } - return res; - } - - SWIGINTERN int - swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p) { - int res = 1; - swig_globalvar *var = v->vars; - while (var) { - if (strcmp(var->name,n) == 0) { - res = (*var->set_attr)(p); - break; - } - var = var->next; - } - if (res == 1 && !PyErr_Occurred()) { - PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); - } - return res; - } - - SWIGINTERN PyTypeObject* - swig_varlink_type(void) { - static char varlink__doc__[] = "Swig var link object"; - static PyTypeObject varlink_type; - static int type_init = 0; - if (!type_init) { - const PyTypeObject tmp = { -#if PY_VERSION_HEX >= 0x03000000 - PyVarObject_HEAD_INIT(NULL, 0) -#else - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ -#endif - (char *)"swigvarlink", /* tp_name */ - sizeof(swig_varlinkobject), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor) swig_varlink_dealloc, /* tp_dealloc */ - (printfunc) swig_varlink_print, /* tp_print */ - (getattrfunc) swig_varlink_getattr, /* tp_getattr */ - (setattrfunc) swig_varlink_setattr, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc) swig_varlink_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - (reprfunc) swig_varlink_str, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - 0, /* tp_flags */ - varlink__doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ -#if PY_VERSION_HEX >= 0x02020000 - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */ -#endif -#if PY_VERSION_HEX >= 0x02030000 - 0, /* tp_del */ -#endif -#if PY_VERSION_HEX >= 0x02060000 - 0, /* tp_version_tag */ -#endif -#if PY_VERSION_HEX >= 0x03040000 - 0, /* tp_finalize */ -#endif -#ifdef COUNT_ALLOCS - 0, /* tp_allocs */ - 0, /* tp_frees */ - 0, /* tp_maxalloc */ -#if PY_VERSION_HEX >= 0x02050000 - 0, /* tp_prev */ -#endif - 0 /* tp_next */ -#endif - }; - varlink_type = tmp; - type_init = 1; -#if PY_VERSION_HEX < 0x02020000 - varlink_type.ob_type = &PyType_Type; -#else - if (PyType_Ready(&varlink_type) < 0) - return NULL; -#endif - } - return &varlink_type; - } - - /* Create a variable linking object for use later */ - SWIGINTERN PyObject * - SWIG_Python_newvarlink(void) { - swig_varlinkobject *result = PyObject_NEW(swig_varlinkobject, swig_varlink_type()); - if (result) { - result->vars = 0; - } - return ((PyObject*) result); - } - - SWIGINTERN void - SWIG_Python_addvarlink(PyObject *p, char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { - swig_varlinkobject *v = (swig_varlinkobject *) p; - swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); - if (gv) { - size_t size = strlen(name)+1; - gv->name = (char *)malloc(size); - if (gv->name) { - strncpy(gv->name,name,size); - gv->get_attr = get_attr; - gv->set_attr = set_attr; - gv->next = v->vars; - } - } - v->vars = gv; - } - - SWIGINTERN PyObject * - SWIG_globals(void) { - static PyObject *_SWIG_globals = 0; - if (!_SWIG_globals) _SWIG_globals = SWIG_newvarlink(); - return _SWIG_globals; - } - - /* ----------------------------------------------------------------------------- - * constants/methods manipulation - * ----------------------------------------------------------------------------- */ - - /* Install Constants */ - SWIGINTERN void - SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { - PyObject *obj = 0; - size_t i; - for (i = 0; constants[i].type; ++i) { - switch(constants[i].type) { - case SWIG_PY_POINTER: - obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); - break; - case SWIG_PY_BINARY: - obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); - break; - default: - obj = 0; - break; - } - if (obj) { - PyDict_SetItemString(d, constants[i].name, obj); - Py_DECREF(obj); - } - } - } - - /* -----------------------------------------------------------------------------*/ - /* Fix SwigMethods to carry the callback ptrs when needed */ - /* -----------------------------------------------------------------------------*/ - - SWIGINTERN void - SWIG_Python_FixMethods(PyMethodDef *methods, - swig_const_info *const_table, - swig_type_info **types, - swig_type_info **types_initial) { - size_t i; - for (i = 0; methods[i].ml_name; ++i) { - const char *c = methods[i].ml_doc; - if (!c) continue; - c = strstr(c, "swig_ptr: "); - if (c) { - int j; - swig_const_info *ci = 0; - const char *name = c + 10; - for (j = 0; const_table[j].type; ++j) { - if (strncmp(const_table[j].name, name, - strlen(const_table[j].name)) == 0) { - ci = &(const_table[j]); - break; - } - } - if (ci) { - void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0; - if (ptr) { - size_t shift = (ci->ptype) - types; - swig_type_info *ty = types_initial[shift]; - size_t ldoc = (c - methods[i].ml_doc); - size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; - char *ndoc = (char*)malloc(ldoc + lptr + 10); - if (ndoc) { - char *buff = ndoc; - strncpy(buff, methods[i].ml_doc, ldoc); - buff += ldoc; - strncpy(buff, "swig_ptr: ", 10); - buff += 10; - SWIG_PackVoidPtr(buff, ptr, ty->name, lptr); - methods[i].ml_doc = ndoc; - } - } - } - } - } - } - -#ifdef __cplusplus -} -#endif - -/* -----------------------------------------------------------------------------* - * Partial Init method - * -----------------------------------------------------------------------------*/ - -#ifdef __cplusplus -extern "C" -#endif - -SWIGEXPORT -#if PY_VERSION_HEX >= 0x03000000 -PyObject* -#else -void -#endif -SWIG_init(void) { - PyObject *m, *d, *md; -#if PY_VERSION_HEX >= 0x03000000 - static struct PyModuleDef SWIG_module = { -# if PY_VERSION_HEX >= 0x03020000 - PyModuleDef_HEAD_INIT, -# else - { - PyObject_HEAD_INIT(NULL) - NULL, /* m_init */ - 0, /* m_index */ - NULL, /* m_copy */ - }, -# endif - (char *) SWIG_name, - NULL, - -1, - SwigMethods, - NULL, - NULL, - NULL, - NULL - }; -#endif - -#if defined(SWIGPYTHON_BUILTIN) - static SwigPyClientData SwigPyObject_clientdata = { - 0, 0, 0, 0, 0, 0, 0 - }; - static PyGetSetDef this_getset_def = { - (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL - }; - static SwigPyGetSet thisown_getset_closure = { - (PyCFunction) SwigPyObject_own, - (PyCFunction) SwigPyObject_own - }; - static PyGetSetDef thisown_getset_def = { - (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure - }; - PyTypeObject *builtin_pytype; - int builtin_base_count; - swig_type_info *builtin_basetype; - PyObject *tuple; - PyGetSetDescrObject *static_getset; - PyTypeObject *metatype; - PyTypeObject *swigpyobject; - SwigPyClientData *cd; - PyObject *public_interface, *public_symbol; - PyObject *this_descr; - PyObject *thisown_descr; - PyObject *self = 0; - int i; - - (void)builtin_pytype; - (void)builtin_base_count; - (void)builtin_basetype; - (void)tuple; - (void)static_getset; - (void)self; - - /* Metaclass is used to implement static member variables */ - metatype = SwigPyObjectType(); - assert(metatype); -#endif - - /* Fix SwigMethods to carry the callback ptrs when needed */ - SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial); - -#if PY_VERSION_HEX >= 0x03000000 - m = PyModule_Create(&SWIG_module); -#else - m = Py_InitModule((char *) SWIG_name, SwigMethods); -#endif - - md = d = PyModule_GetDict(m); - (void)md; - - SWIG_InitializeModule(0); - -#ifdef SWIGPYTHON_BUILTIN - swigpyobject = SwigPyObject_TypeOnce(); - - SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject"); - assert(SwigPyObject_stype); - cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; - if (!cd) { - SwigPyObject_stype->clientdata = &SwigPyObject_clientdata; - SwigPyObject_clientdata.pytype = swigpyobject; - } else if (swigpyobject->tp_basicsize != cd->pytype->tp_basicsize) { - PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules."); -# if PY_VERSION_HEX >= 0x03000000 - return NULL; -# else - return; -# endif - } - - /* All objects have a 'this' attribute */ - this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def); - (void)this_descr; - - /* All objects have a 'thisown' attribute */ - thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def); - (void)thisown_descr; - - public_interface = PyList_New(0); - public_symbol = 0; - (void)public_symbol; - - PyDict_SetItemString(md, "__all__", public_interface); - Py_DECREF(public_interface); - for (i = 0; SwigMethods[i].ml_name != NULL; ++i) - SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name); - for (i = 0; swig_const_table[i].name != 0; ++i) - SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name); -#endif - - SWIG_InstallConstants(d,swig_const_table); - -#if PY_VERSION_HEX >= 0x03000000 - return m; -#else - return; -#endif -} - diff --git a/contrib/libhsmd_python/test_libhsmd.py b/contrib/libhsmd_python/test_libhsmd.py deleted file mode 100644 index 47104162fdf3..000000000000 --- a/contrib/libhsmd_python/test_libhsmd.py +++ /dev/null @@ -1,14 +0,0 @@ -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 50180e040c66da6207b6128bca7f282c280a2521 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 13 Jul 2022 17:31:31 +0200 Subject: [PATCH 0974/1530] msggen: Update generated files to add Listpeers.peers[].remote_addr Changelog-None --- .msggen.json | 6 +++--- cln-grpc/proto/node.proto | 1 + cln-grpc/src/convert.rs | 1 + cln-rpc/src/model.rs | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.msggen.json b/.msggen.json index bd58e28dcc07..101427244356 100644 --- a/.msggen.json +++ b/.msggen.json @@ -1,4 +1,3 @@ - { "grpc-enum-map": { "CloseType": { @@ -667,7 +666,8 @@ "ListPeers.peers[].features": 6, "ListPeers.peers[].id": 1, "ListPeers.peers[].log[]": 3, - "ListPeers.peers[].netaddr[]": 5 + "ListPeers.peers[].netaddr[]": 5, + "ListPeers.peers[].remote_addr": 7 }, "ListpeersPeersChannels": { "ListPeers.peers[].channels[].alias": 50, @@ -1077,4 +1077,4 @@ "Withdraw.txid": 2 } } -} +} \ No newline at end of file diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 228648010ad2..cc47a976a04b 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -128,6 +128,7 @@ message ListpeersPeers { repeated ListpeersPeersLog log = 3; repeated ListpeersPeersChannels channels = 4; repeated string netaddr = 5; + optional string remote_addr = 7; optional bytes features = 6; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 2f837ed747bf..e6d33b9ab545 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -163,6 +163,7 @@ impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { log: c.log.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannels netaddr: c.netaddr.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + remote_addr: c.remote_addr.clone(), // Rule #2 for type string? features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 7bee25f885a8..3a595deed0c8 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1246,6 +1246,8 @@ pub mod responses { pub channels: Vec, #[serde(alias = "netaddr", skip_serializing_if = "Option::is_none")] pub netaddr: Option>, + #[serde(alias = "remote_addr", skip_serializing_if = "Option::is_none")] + pub remote_addr: Option, #[serde(alias = "features", skip_serializing_if = "Option::is_none")] pub features: Option, } From 92fe871467e32a6631b473398341a9dc16edd7b2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Jul 2022 15:28:45 +0930 Subject: [PATCH 0975/1530] connectd: optimize case where peer doesn't want gossip. LND and us send 0xFFFFFFFF to turn off gossip. LDK and Eclair don't seem to turn off gossip at all, but that's OK. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index f9d41bcae3c6..c53886196f8f 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -632,7 +632,12 @@ static void handle_gossip_timestamp_filter_in(struct peer *peer, const u8 *msg) if (peer->gs.timestamp_max < peer->gs.timestamp_min) peer->gs.timestamp_max = UINT32_MAX; - peer->gs.off = 1; + /* Optimization: they don't want anything. LND and us (at least), + * both set first_timestamp to 0xFFFFFFFF to indicate that. */ + if (peer->gs.timestamp_min == UINT32_MAX) + peer->gs.off = peer->daemon->gossip_store_end; + else + peer->gs.off = 1; /* BOLT #7: * - MAY wait for the next outgoing gossip flush to send these. From dcf2916dcb27f919435553fe5b675ff75f035d8b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Jul 2022 15:29:26 +0930 Subject: [PATCH 0976/1530] devtools: add --print-timestamps to dump-gossipstore. Allowed me to sanity check the next patch. Signed-off-by: Rusty Russell --- devtools/dump-gossipstore.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 9cd7befa3e85..1d5100d0a052 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -17,10 +17,13 @@ int main(int argc, char *argv[]) struct gossip_hdr hdr; size_t off; bool print_deleted = false; + bool print_timestamp = false; setup_locale(); opt_register_noarg("--print-deleted", opt_set_bool, &print_deleted, "Print deleted entries too"); + opt_register_noarg("--print-timestamps", opt_set_bool, &print_timestamp, + "Print timestamp with entries"); opt_register_noarg("--help|-h", opt_usage_and_exit, "[]" "Dump all gossip messages in the store", @@ -71,6 +74,8 @@ int main(int argc, char *argv[]) deleted ? "DELETED " : "", push ? "PUSH " : "", ratelimit ? "RATE-LIMITED " : ""); + if (print_timestamp) + printf("T=%u ", be32_to_cpu(hdr.timestamp)); if (deleted && !print_deleted) { printf("\n"); goto end; From 62a5183fb5de6ab1af682280f8473238cf9064fb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Jul 2022 15:29:26 +0930 Subject: [PATCH 0977/1530] common: add ability for gossip_store to track by timestamp. We'll use this to keep a two-hour-ago "recent gossip offset" as an optimization. Signed-off-by: Rusty Russell --- common/gossip_store.c | 97 +++++++++++++++++++++++++++++++++++++++++-- common/gossip_store.h | 6 +++ 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index 2a0ade5ab678..c4f167a3d719 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -49,6 +49,64 @@ static size_t reopen_gossip_store(int *gossip_store_fd, const u8 *msg) return equivalent_offset; } +static bool public_msg_type(enum peer_wire type) +{ + /* This switch statement makes you think about new types as they + * are introduced. */ + switch (type) { + case WIRE_INIT: + case WIRE_ERROR: + case WIRE_WARNING: + case WIRE_PING: + case WIRE_PONG: + case WIRE_TX_ADD_INPUT: + case WIRE_TX_ADD_OUTPUT: + case WIRE_TX_REMOVE_INPUT: + case WIRE_TX_REMOVE_OUTPUT: + case WIRE_TX_COMPLETE: + case WIRE_TX_SIGNATURES: + case WIRE_OPEN_CHANNEL: + case WIRE_ACCEPT_CHANNEL: + case WIRE_FUNDING_CREATED: + case WIRE_FUNDING_SIGNED: + case WIRE_FUNDING_LOCKED: + case WIRE_OPEN_CHANNEL2: + case WIRE_ACCEPT_CHANNEL2: + case WIRE_INIT_RBF: + case WIRE_ACK_RBF: + case WIRE_SHUTDOWN: + case WIRE_CLOSING_SIGNED: + case WIRE_UPDATE_ADD_HTLC: + case WIRE_UPDATE_FULFILL_HTLC: + case WIRE_UPDATE_FAIL_HTLC: + case WIRE_UPDATE_FAIL_MALFORMED_HTLC: + case WIRE_COMMITMENT_SIGNED: + case WIRE_REVOKE_AND_ACK: + case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: + case WIRE_CHANNEL_REESTABLISH: + case WIRE_ANNOUNCEMENT_SIGNATURES: + case WIRE_QUERY_SHORT_CHANNEL_IDS: + case WIRE_REPLY_SHORT_CHANNEL_IDS_END: + case WIRE_QUERY_CHANNEL_RANGE: + case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_OBS2_ONION_MESSAGE: + case WIRE_ONION_MESSAGE: +#if EXPERIMENTAL_FEATURES + case WIRE_STFU: +#endif + return false; + case WIRE_CHANNEL_ANNOUNCEMENT: + case WIRE_NODE_ANNOUNCEMENT: + case WIRE_CHANNEL_UPDATE: + return true; + } + + /* Actually, we do have other (internal) messages. */ + return false; +} + u8 *gossip_store_next(const tal_t *ctx, int *gossip_store_fd, u32 timestamp_min, u32 timestamp_max, @@ -111,9 +169,7 @@ u8 *gossip_store_next(const tal_t *ctx, *off = *end = reopen_gossip_store(gossip_store_fd, msg); msg = tal_free(msg); /* Ignore gossipd internal messages. */ - } else if (type != WIRE_CHANNEL_ANNOUNCEMENT - && type != WIRE_CHANNEL_UPDATE - && type != WIRE_NODE_ANNOUNCEMENT) { + } else if (!public_msg_type(type)) { msg = tal_free(msg); } else if (!push && push_only) { msg = tal_free(msg); @@ -148,3 +204,38 @@ size_t find_gossip_store_end(int gossip_store_fd, size_t off) } return off; } + +/* Keep seeking forward until we hit something >= timestamp */ +size_t find_gossip_store_by_timestamp(int gossip_store_fd, + size_t off, + u32 timestamp) +{ + /* We cheat and read first two bytes of message too. */ + struct { + struct gossip_hdr hdr; + be16 type; + } buf; + int r; + + while ((r = pread(gossip_store_fd, &buf, + sizeof(buf.hdr) + sizeof(buf.type), off)) + == sizeof(buf.hdr) + sizeof(buf.type)) { + u32 msglen = be32_to_cpu(buf.hdr.len) & GOSSIP_STORE_LEN_MASK; + u16 type = be16_to_cpu(buf.type); + + /* Don't swallow end marker! Reset, as they will call + * gossip_store_next and reopen file. */ + if (type == WIRE_GOSSIP_STORE_ENDED) + return 1; + + /* Only to-be-broadcast types have valid timestamps! */ + if (!(be32_to_cpu(buf.hdr.len) & GOSSIP_STORE_LEN_DELETED_BIT) + && public_msg_type(type) + && be32_to_cpu(buf.hdr.timestamp) >= timestamp) { + break; + } + + off += sizeof(buf.hdr) + msglen; + } + return off; +} diff --git a/common/gossip_store.h b/common/gossip_store.h index 136186e8e564..c4f6f52bd01f 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -63,4 +63,10 @@ u8 *gossip_store_next(const tal_t *ctx, */ size_t find_gossip_store_end(int gossip_store_fd, size_t old_end); +/** + * Return offset of first entry >= this timestamp. + */ +size_t find_gossip_store_by_timestamp(int gossip_store_fd, + size_t off, + u32 timestamp); #endif /* LIGHTNING_COMMON_GOSSIP_STORE_H */ From 6fd8fa4d959705d6835deacdcf470bf0ed367923 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Jul 2022 15:29:26 +0930 Subject: [PATCH 0978/1530] connectd: optimize requests for "recent" gossip. Signed-off-by: Rusty Russell --- connectd/connectd.h | 2 ++ connectd/multiplex.c | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/connectd/connectd.h b/connectd/connectd.h index db5ec2022120..a1a559eb0c4d 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -187,6 +187,8 @@ struct daemon { /* The gossip_store */ int gossip_store_fd; size_t gossip_store_end; + u32 gossip_recent_time; + size_t gossip_store_recent_off; /* We only announce websocket addresses if !deprecated_apis */ bool announce_websocket; diff --git a/connectd/multiplex.c b/connectd/multiplex.c index c53886196f8f..cf02a07808cb 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -129,6 +129,26 @@ static struct oneshot *gossip_stream_timer(struct peer *peer) wake_gossip, peer); } +/* It's so common to ask for "recent" gossip (we ask for 10 minutes + * ago, LND and Eclair ask for now, LDK asks for 1 hour ago) that it's + * worth keeping track of where that starts, so we can skip most of + * the store. */ +static void update_recent_timestamp(struct daemon *daemon) +{ + /* 2 hours allows for some clock drift, not too much gossip */ + u32 recent = time_now().ts.tv_sec - 7200; + + /* Only update every minute */ + if (daemon->gossip_recent_time + 60 > recent) + return; + + daemon->gossip_recent_time = recent; + daemon->gossip_store_recent_off + = find_gossip_store_by_timestamp(daemon->gossip_store_fd, + daemon->gossip_store_recent_off, + daemon->gossip_recent_time); +} + /* This is called once we need it: otherwise, the gossip_store may not exist, * since we start at the same time as gossipd itself. */ static void setup_gossip_store(struct daemon *daemon) @@ -138,10 +158,16 @@ static void setup_gossip_store(struct daemon *daemon) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Opening gossip_store %s: %s", GOSSIP_STORE_FILENAME, strerror(errno)); + + daemon->gossip_recent_time = 0; + daemon->gossip_store_recent_off = 1; + update_recent_timestamp(daemon); + /* gossipd will be writing to this, and it's not atomic! Safest * way to find the "end" is to walk through. */ daemon->gossip_store_end - = find_gossip_store_end(daemon->gossip_store_fd, 1); + = find_gossip_store_end(daemon->gossip_store_fd, + daemon->gossip_store_recent_off); } void setup_peer_gossip_store(struct peer *peer, @@ -636,8 +662,15 @@ static void handle_gossip_timestamp_filter_in(struct peer *peer, const u8 *msg) * both set first_timestamp to 0xFFFFFFFF to indicate that. */ if (peer->gs.timestamp_min == UINT32_MAX) peer->gs.off = peer->daemon->gossip_store_end; - else - peer->gs.off = 1; + else { + /* Second optimation: it's common to ask for "recent" gossip, + * so we don't have to start at beginning of store. */ + update_recent_timestamp(peer->daemon); + if (peer->gs.timestamp_min >= peer->daemon->gossip_recent_time) + peer->gs.off = peer->daemon->gossip_store_recent_off; + else + peer->gs.off = 1; + } /* BOLT #7: * - MAY wait for the next outgoing gossip flush to send these. From f2e7e9d919c1670b6bdfd3e672d476988951d43b Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 14 Jul 2022 22:53:46 -0500 Subject: [PATCH 0979/1530] coin-moves: only log htlc_timeout pair for penalty txs We cleanup our output tracking for timeout txs when the peer's htlc_timeout self-expiry is hit; we'd also log its spend if happen to see it get spent. This is a bit of a race as they can't spend it until the locktime is available. Hence the flakiness in tests that expected the `htlc_timeout` to *not* be spent. Instead, we only log an external's `htlc_timeout` spend in the case where we also immediately register another output to track for it (only happens when said htlc is stealable) Fixes #5405 In-Collab-With: @ddustin --- onchaind/onchaind.c | 10 +++++----- tests/test_closing.py | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 6859d327d4e4..6c7049a42c9f 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -1727,12 +1727,12 @@ static void output_spent(struct tracked_output ***outs, break; case THEIR_HTLC: - record_external_deposit(out, out->tx_blockheight, - HTLC_TIMEOUT); - record_external_spend(&tx_parts->txid, out, - tx_blockheight, HTLC_TIMEOUT); - if (out->tx_type == THEIR_REVOKED_UNILATERAL) { + record_external_deposit(out, out->tx_blockheight, + HTLC_TIMEOUT); + record_external_spend(&tx_parts->txid, out, + tx_blockheight, HTLC_TIMEOUT); + /* we've actually got a 'new' output here */ steal_htlc_tx(out, outs, tx_parts, tx_blockheight, diff --git a/tests/test_closing.py b/tests/test_closing.py index 70b989987b85..4cc07dea46a4 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2520,7 +2520,6 @@ def test_onchain_feechange(node_factory, bitcoind, executor): assert only_one(l2.rpc.listinvoices('onchain_timeout')['invoices'])['status'] == 'unpaid' -@pytest.mark.skip("Lisa, please fix this!") @pytest.mark.developer("needs DEVELOPER=1 for dev-set-fees") def test_onchain_all_dust(node_factory, bitcoind, executor): """Onchain handling when we reduce output to all dust""" From 769efe8d5456a27707c086956d3bd19ab6417439 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 14 Jul 2022 22:58:53 -0500 Subject: [PATCH 0980/1530] tests: massively speed up our wait for enormous feerates We do smoothing, waiting for this to hit the target (50k+) was taking longer than my TIMEOUT=15; here we increase the speed at which it hits exit velocity, so to speak. --- tests/test_closing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 4cc07dea46a4..d7e678eab2b1 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2557,7 +2557,7 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): l2.wait_for_channel_onchain(l1.info['id']) # Make l1's fees really high (and wait for it to exceed 50000) - l1.set_feerates((100000, 100000, 100000, 100000)) + l1.set_feerates((1000000, 1000000, 1000000, 1000000)) l1.daemon.wait_for_log('Feerate estimate for unilateral_close set to [56789][0-9]{4}') bitcoind.generate_block(1) From c34a0a22ada3dbd28319a7174a1d94b5242c4950 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Jul 2022 13:57:07 +0930 Subject: [PATCH 0981/1530] makesecret: change info_hex arg to simply "hex" to match datastore command. And fix schema: it wasn't tested as there was no test-by-parameter-name. Signed-off-by: Rusty Russell --- doc/lightning-makesecret.7.md | 4 ++-- doc/schemas/makesecret.request.json | 4 ++-- lightningd/hsm_control.c | 4 ++-- plugins/chanbackup.c | 2 +- tests/test_misc.py | 8 ++++++++ 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/doc/lightning-makesecret.7.md b/doc/lightning-makesecret.7.md index 2fdbf9755ae2..7170061cb6b3 100644 --- a/doc/lightning-makesecret.7.md +++ b/doc/lightning-makesecret.7.md @@ -4,14 +4,14 @@ lightning-makesecret -- Command for deriving pseudorandom key from HSM SYNOPSIS -------- -**makesecret** *info_hex* +**makesecret** *hex* DESCRIPTION ----------- The **makesecret** RPC command derives a secret key from the HSM_secret. -The *info_hex* can be any hex data. +The *hex* can be any hex data. RETURN VALUE ------------ diff --git a/doc/schemas/makesecret.request.json b/doc/schemas/makesecret.request.json index a43ac493e2bd..c26e3ce4b944 100644 --- a/doc/schemas/makesecret.request.json +++ b/doc/schemas/makesecret.request.json @@ -3,10 +3,10 @@ "type": "object", "additionalProperties": false, "required": [ - "info_hex" + "hex" ], "properties": { - "info": { + "hex": { "type": "hex", "description": "This will be used for deriving the secret" } diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index d5f6413a98fe..c30895499c4a 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -158,7 +158,7 @@ static struct command_result *json_makesecret(struct command *cmd, struct secret secret; if (!param(cmd, buffer, params, - p_req("info_hex", param_bin_from_hex, &info), + p_req("hex", param_bin_from_hex, &info), NULL)) return command_param_failed(); @@ -183,7 +183,7 @@ static const struct json_command makesecret_command = { "makesecret", "utility", &json_makesecret, - "Get a pseudorandom secret key, using an info string." + "Get a pseudorandom secret key, using some {hex} data." }; AUTODATA(json_command, &makesecret_command); diff --git a/plugins/chanbackup.c b/plugins/chanbackup.c index cd8376be433a..85637ba2f0d3 100644 --- a/plugins/chanbackup.c +++ b/plugins/chanbackup.c @@ -376,7 +376,7 @@ static const char *init(struct plugin *p, "{scb:%}", JSON_SCAN(json_to_scb_chan, &scb_chan)); rpc_scan(p, "makesecret", - take(json_out_obj(NULL, "info_hex", + take(json_out_obj(NULL, "hex", tal_hexstr(tmpctx, info_hex, tal_bytelen(info_hex)))), diff --git a/tests/test_misc.py b/tests/test_misc.py index bafd139114e8..cac96dcf5339 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2282,6 +2282,14 @@ def test_makesecret(node_factory): assert (secret == "04fe01631fcedc8d91f39ab43244e63afebaed68ee21d2f1c325fd1242726a18") + # Same if we do it by parameter name + assert l1.rpc.makesecret(hex="73636220736563726574")["secret"] == secret + + # Changing seed changes secret! + assert l1.rpc.makesecret(hex="73636220736563726575")["secret"] != secret + assert l1.rpc.makesecret(hex="736362207365637265")["secret"] != secret + assert l1.rpc.makesecret(hex="7363622073656372657401")["secret"] != secret + def test_staticbackup(node_factory): """ From 9685c1adafd0e3feaf8813a68acff77176738da2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 15 Jul 2022 14:00:48 +0930 Subject: [PATCH 0982/1530] lightningd: remove getsharedsecret. This was introduced to allow creating a shared secret, but it's better to use makesecret which creates unique secrets. getsharedsecret being a generic ECDH function allows the caller to initiate conversations as if it was us; this is generally OK, since we don't allow untrusted API access, but the commando plugin had to blacklist this for read-only runes explicitly. Since @ZmnSCPxj never ended up using this after introducing it, simply remove it. Signed-off-by: Rusty Russell Changelog-Removed: JSONRPC: `getsharedsecret` API: use `makesecret` --- contrib/msggen/msggen/utils/utils.py | 1 - contrib/pyln-client/pyln/client/lightning.py | 12 --- doc/Makefile | 1 - doc/index.rst | 1 - doc/lightning-getsharedsecret.7.md | 94 -------------------- doc/schemas/getsharedsecret.schema.json | 16 ---- lightningd/hsm_control.c | 29 ------ tests/test_misc.py | 26 ------ 8 files changed, 180 deletions(-) delete mode 100644 doc/lightning-getsharedsecret.7.md delete mode 100644 doc/schemas/getsharedsecret.schema.json diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py index 59586cd34a12..e68a1ce5e498 100644 --- a/contrib/msggen/msggen/utils/utils.py +++ b/contrib/msggen/msggen/utils/utils.py @@ -93,7 +93,6 @@ def load_jsonrpc_service(schema_dir: str = None): # "funderupdate", # "getlog", "GetRoute", - # "getsharedsecret", "ListForwards", # "listoffers", "ListPays", diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 2b1844f8ed97..38fc7563f4ac 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -1442,18 +1442,6 @@ def checkmessage(self, message, zbase, pubkey=None): } return self.call("checkmessage", payload) - def getsharedsecret(self, point, **kwargs): - """ - Compute the hash of the Elliptic Curve Diffie Hellman shared - secret point from this node private key and an - input {point}. - """ - payload = { - "point": point - } - payload.update({k: v for k, v in kwargs.items()}) - return self.call("getsharedsecret", payload) - def keysend(self, destination, amount_msat=None, label=None, maxfeepercent=None, retry_for=None, maxdelay=None, exemptfee=None, extratlvs=None, msatoshi=None): diff --git a/doc/Makefile b/doc/Makefile index f9375b406289..3f5a39453a58 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -34,7 +34,6 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-funderupdate.7 \ doc/lightning-fundpsbt.7 \ doc/lightning-getroute.7 \ - doc/lightning-getsharedsecret.7 \ doc/lightning-hsmtool.8 \ doc/lightning-invoice.7 \ doc/lightning-keysend.7 \ diff --git a/doc/index.rst b/doc/index.rst index 3d92e23ac223..87e6601af46e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -59,7 +59,6 @@ Core Lightning Documentation lightning-getinfo lightning-getlog lightning-getroute - lightning-getsharedsecret lightning-help lightning-hsmtool lightning-invoice diff --git a/doc/lightning-getsharedsecret.7.md b/doc/lightning-getsharedsecret.7.md deleted file mode 100644 index 505986a6e957..000000000000 --- a/doc/lightning-getsharedsecret.7.md +++ /dev/null @@ -1,94 +0,0 @@ -lightning-getsharedsecret -- Command for computing an ECDH -========================================================== - -SYNOPSIS --------- - -**getsharedsecret** *point* - -DESCRIPTION ------------ - -The **getsharedsecret** RPC command computes a shared secret from a -given public *point*, and the secret key of this node. -The *point* is a hexadecimal string of the compressed public -key DER-encoding of the SECP256K1 point. - -RETURN VALUE ------------- - -[comment]: # (GENERATE-FROM-SCHEMA-START) -On success, an object is returned, containing: -- **shared_secret** (hex): the SHA-2 of the compressed encoding of the shared secp256k1 point (always 64 characters) - -[comment]: # (GENERATE-FROM-SCHEMA-END) - -This command may fail if communications with the HSM has a -problem; -by default lightningd uses a software "HSM" which should -never fail in this way. -(As of the time of this writing there is no true hardware -HSM that lightningd can use, but we are leaving this -possibilty open in the future.) -In that case, it will return with an error code of 800. - -CRYPTOGRAPHIC STANDARDS ------------------------ - -This serves as a key agreement scheme in elliptic-curve based -cryptographic standards. - -However, note that most key agreement schemes based on -Elliptic-Curve Diffie-Hellman do not hash the DER-compressed -point. -Standards like SECG SEC-1 ECIES specify using the X coordinate -of the point instead. -The Lightning BOLT standard (which `lightningd` uses), unlike -most other cryptographic standards, specifies the SHA-256 hash -of the DER-compressed encoding of the point. - -It is not possible to extract the X coordinate of the ECDH point -via this API, since there is no known way to reverse the 256-bit -SHA-2 hash function. -Thus there is no way to implement ECIES and similar standards using -this API. - -If you know the secret key behind *point*, you do not need to -even call **getsharedsecret**, you can just multiply the secret key -with the node public key. - -Typically, a sender will generate an ephemeral secret key -and multiply it with the node public key, -then use the result to derive an encryption key -for a symmetric encryption scheme -to encrypt a message that can be read only by that node. -Then the ephemeral secret key is multiplied -by the standard generator point, -and the ephemeral public key and the encrypted message is -sent to the node, -which then uses **getsharedsecret** to derive the same key. - -The above sketch elides important details like -key derivation function, stream encryption scheme, -message authentication code, and so on. -You should follow an established standard and avoid -rolling your own crypto. - -AUTHOR ------- - -ZmnSCPxj <> is mainly responsible. - -SEE ALSO --------- - -RESOURCES ---------- - -* BOLT 4: -* BOLT 8: -* SECG SEC-1 ECIES: -* Main web site: - - -[comment]: # ( SHA256STAMP:47c5b466b02b80d40937f40c725733f72c20f3bc26cc7f4dacd5ef858ab0282b) diff --git a/doc/schemas/getsharedsecret.schema.json b/doc/schemas/getsharedsecret.schema.json deleted file mode 100644 index 276ea1e09463..000000000000 --- a/doc/schemas/getsharedsecret.schema.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "additionalProperties": true, - "required": [ - "shared_secret" - ], - "properties": { - "shared_secret": { - "type": "hex", - "description": "the SHA-2 of the compressed encoding of the shared secp256k1 point", - "maxLength": 64, - "minLength": 64 - } - } -} diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index c30895499c4a..5145f90956a3 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -128,26 +128,6 @@ struct ext_key *hsm_init(struct lightningd *ld) return bip32_base; } -static struct command_result *json_getsharedsecret(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - struct pubkey *point; - struct secret ss; - struct json_stream *response; - - if (!param(cmd, buffer, params, - p_req("point", ¶m_pubkey, &point), - NULL)) - return command_param_failed(); - - ecdh(point, &ss); - response = json_stream_success(cmd); - json_add_secret(response, "shared_secret", &ss); - return command_success(cmd, response); -} - static struct command_result *json_makesecret(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -186,12 +166,3 @@ static const struct json_command makesecret_command = { "Get a pseudorandom secret key, using some {hex} data." }; AUTODATA(json_command, &makesecret_command); - -static const struct json_command getsharedsecret_command = { - "getsharedsecret", - "utility", /* FIXME: Or "crypto"? */ - &json_getsharedsecret, - "Compute the hash of the Elliptic Curve Diffie Hellman shared secret point from " - "this node private key and an input {point}." -}; -AUTODATA(json_command, &getsharedsecret_command); diff --git a/tests/test_misc.py b/tests/test_misc.py index cac96dcf5339..77f8f061a881 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2245,32 +2245,6 @@ def test_sendcustommsg(node_factory): ]) -@pytest.mark.developer("needs --dev-force-privkey") -def test_getsharedsecret(node_factory): - """ - Test getsharedsecret command. - """ - # From BOLT 8 test vectors. - options = [ - {"dev-force-privkey": "1212121212121212121212121212121212121212121212121212121212121212"}, - {} - ] - l1, l2 = node_factory.get_nodes(2, opts=options) - - # Check BOLT 8 test vectors. - shared_secret = l1.rpc.getsharedsecret("028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7")['shared_secret'] - assert (shared_secret == "1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3") - - # Clear the forced privkey of l1. - del l1.daemon.opts["dev-force-privkey"] - l1.restart() - - # l1 and l2 can generate the same shared secret - # knowing only the public key of the other. - assert (l1.rpc.getsharedsecret(l2.info["id"])["shared_secret"] - == l2.rpc.getsharedsecret(l1.info["id"])["shared_secret"]) - - @pytest.mark.developer("needs --dev-force-privkey") def test_makesecret(node_factory): """ From 0236d4e4dafe4f1e78f3af5b14f9276fb8a723db Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 3 Jul 2022 20:37:20 +0930 Subject: [PATCH 0983/1530] common/json_stream: remove useless attempt at oom handling. We tell membuf to use tal, and tal never returns NULL, so this code can never be triggered. Signed-off-by: Rusty Russell --- common/json.c | 21 +++++++---------- common/json_stream.c | 55 ++++++-------------------------------------- common/json_stream.h | 5 +--- lightningd/jsonrpc.c | 10 ++++---- plugins/libplugin.c | 6 ++--- 5 files changed, 24 insertions(+), 73 deletions(-) diff --git a/common/json.c b/common/json.c index 1955b4f8302e..08fa96e88449 100644 --- a/common/json.c +++ b/common/json.c @@ -1065,8 +1065,7 @@ void json_add_literal(struct json_stream *result, const char *fieldname, { /* Literal may contain quotes, so bypass normal checks */ char *dest = json_member_direct(result, fieldname, len); - if (dest) - memcpy(dest, literal, len); + memcpy(dest, literal, len); } void json_add_stringn(struct json_stream *result, const char *fieldname, @@ -1100,12 +1099,10 @@ void json_add_hex(struct json_stream *js, const char *fieldname, char *dest; dest = json_member_direct(js, fieldname, 1 + hexlen + 1); - if (dest) { - dest[0] = '"'; - if (!hex_encode(data, len, dest + 1, hexlen + 1)) - abort(); - dest[1+hexlen] = '"'; - } + dest[0] = '"'; + if (!hex_encode(data, len, dest + 1, hexlen + 1)) + abort(); + dest[1+hexlen] = '"'; } void json_add_hex_talarr(struct json_stream *result, @@ -1122,11 +1119,9 @@ void json_add_escaped_string(struct json_stream *result, const char *fieldname, char *dest = json_member_direct(result, fieldname, 1 + strlen(esc->s) + 1); - if (dest) { - dest[0] = '"'; - memcpy(dest + 1, esc->s, strlen(esc->s)); - dest[1+strlen(esc->s)] = '"'; - } + dest[0] = '"'; + memcpy(dest + 1, esc->s, strlen(esc->s)); + dest[1+strlen(esc->s)] = '"'; if (taken(esc)) tal_free(esc); } diff --git a/common/json_stream.c b/common/json_stream.c index 59a8a88bf0c7..8ec5fefdc0f6 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -37,8 +37,7 @@ struct json_stream *json_stream_dup(const tal_t *ctx, { struct json_stream *js = tal_dup(ctx, struct json_stream, original); - if (original->jout) - js->jout = json_out_dup(js, original->jout); + js->jout = json_out_dup(js, original->jout); js->log = log; return js; } @@ -61,24 +60,12 @@ void json_stream_log_suppress(struct json_stream *js, const char *cmd_name) js->log = NULL; } -/* If we have an allocation failure. */ -static void COLD js_oom(struct json_stream *js) -{ - js->jout = tal_free(js->jout); -} - void json_stream_append(struct json_stream *js, const char *str, size_t len) { char *dest; - if (!js->jout) - return; dest = json_out_direct(js->jout, len); - if (!dest) { - js_oom(js); - return; - } memcpy(dest, str, len); } @@ -88,9 +75,6 @@ void json_stream_double_cr(struct json_stream *js) const char *contents; size_t len, cr_needed; - if (!js->jout) - return; - /* Must be well-formed at this point! */ json_out_finished(js->jout); @@ -131,46 +115,28 @@ char *json_member_direct(struct json_stream *js, { char *dest; - if (!js->jout) - return NULL; - dest = json_out_member_direct(js->jout, fieldname, extra); - if (!dest) - js_oom(js); return dest; } void json_array_start(struct json_stream *js, const char *fieldname) { - if (js->jout && !json_out_start(js->jout, fieldname, '[')) - js_oom(js); + json_out_start(js->jout, fieldname, '['); } void json_array_end(struct json_stream *js) { - if (js->jout && !json_out_end(js->jout, ']')) - js_oom(js); + json_out_end(js->jout, ']'); } void json_object_start(struct json_stream *js, const char *fieldname) { - if (js->jout && !json_out_start(js->jout, fieldname, '{')) - js_oom(js); + json_out_start(js->jout, fieldname, '{'); } void json_object_end(struct json_stream *js) { - if (js->jout && !json_out_end(js->jout, '}')) - js_oom(js); -} - -void json_object_compat_end(struct json_stream *js) -{ - /* In 0.7.1 we upgraded pylightning to no longer need this. */ -#ifdef COMPAT_V070 - json_stream_append(js, " ", 1); -#endif - json_object_end(js); + json_out_end(js->jout, '}'); } void json_add_member(struct json_stream *js, @@ -181,8 +147,7 @@ void json_add_member(struct json_stream *js, va_list ap; va_start(ap, fmt); - if (js->jout && !json_out_addv(js->jout, fieldname, quote, fmt, ap)) - js_oom(js); + json_out_addv(js->jout, fieldname, quote, fmt, ap); va_end(ap); } @@ -194,9 +159,7 @@ void json_add_jsonstr(struct json_stream *js, size_t len = strlen(jsonstr); p = json_member_direct(js, fieldname, len); - /* Could be OOM! */ - if (p) - memcpy(p, jsonstr, len); + memcpy(p, jsonstr, len); } /* This is where we read the json_stream and write it to conn */ @@ -205,10 +168,6 @@ static struct io_plan *json_stream_output_write(struct io_conn *conn, { const char *p; - /* Out of memory? Nothing we can do but close conn */ - if (!js->jout) - return io_close(conn); - /* For when we've just done some output */ json_out_consume(js->jout, js->len_read); diff --git a/common/json_stream.h b/common/json_stream.h index db595ca770f8..2c1dceee2f9d 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -11,7 +11,6 @@ struct io_conn; struct log; struct json_stream { - /* NULL if we ran OOM! */ struct json_out *jout; /* Who is writing to this buffer now; NULL if nobody is. */ @@ -73,8 +72,6 @@ void json_object_start(struct json_stream *ks, const char *fieldname); void json_array_end(struct json_stream *js); /* '},' */ void json_object_end(struct json_stream *js); -/* ' },' */ -void json_object_compat_end(struct json_stream *js); /** * json_stream_append - literally insert this string into the json_stream. @@ -117,7 +114,7 @@ void json_add_jsonstr(struct json_stream *js, * @fieldname: fieldname (if in object), otherwise must be NULL. * @extra: the space to reserve. * - * Returns NULL if oom, otherwise returns a ptr to @extra bytes. + * Returns a ptr to @extra bytes. */ char *json_member_direct(struct json_stream *js, const char *fieldname, size_t extra); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 5b248c566cba..82561cd3a280 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -459,7 +459,7 @@ struct command_result *command_success(struct command *cmd, assert(cmd); assert(cmd->json_stream == result); json_object_end(result); - json_object_compat_end(result); + json_object_end(result); return command_raw_complete(cmd, result); } @@ -470,7 +470,7 @@ struct command_result *command_failed(struct command *cmd, assert(cmd->json_stream == result); /* Have to close error */ json_object_end(result); - json_object_compat_end(result); + json_object_end(result); return command_raw_complete(cmd, result); } @@ -516,7 +516,7 @@ static void json_command_malformed(struct json_connection *jcon, json_add_member(js, "code", false, "%" PRIerrcode, JSONRPC2_INVALID_REQUEST); json_add_string(js, "message", error); json_object_end(js); - json_object_compat_end(js); + json_object_end(js); json_stream_close(js, NULL); } @@ -748,13 +748,13 @@ static void rpc_command_hook_final(struct rpc_command_hook_payload *p STEALS) if (p->custom_result != NULL) { struct json_stream *s = json_start(p->cmd); json_add_jsonstr(s, "result", p->custom_result); - json_object_compat_end(s); + json_object_end(s); return was_pending(command_raw_complete(p->cmd, s)); } if (p->custom_error != NULL) { struct json_stream *s = json_start(p->cmd); json_add_jsonstr(s, "error", p->custom_error); - json_object_compat_end(s); + json_object_end(s); return was_pending(command_raw_complete(p->cmd, s)); } if (p->custom_replace != NULL) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 394ec0f4c548..c684a658aa07 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -177,7 +177,7 @@ const struct feature_set *plugin_feature_set(const struct plugin *p) static void jsonrpc_finish_and_send(struct plugin *p, struct json_stream *js) { - json_object_compat_end(js); + json_object_end(js); json_stream_close(js, NULL); ld_send(p, js); } @@ -228,7 +228,7 @@ static struct command_result *command_complete(struct command *cmd, struct json_stream *result) { /* Global object */ - json_object_compat_end(result); + json_object_end(result); json_stream_close(result, cmd); ld_send(cmd->plugin, result); tal_free(cmd); @@ -571,7 +571,7 @@ send_outreq(struct plugin *plugin, const struct out_req *req) { /* The "param" object. */ json_object_end(req->js); - json_object_compat_end(req->js); + json_object_end(req->js); json_stream_close(req->js, req->cmd); ld_rpc_send(plugin, req->js); From 814cde56235951c5f700c45829506f5bda373820 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 3 Jul 2022 20:38:20 +0930 Subject: [PATCH 0984/1530] lightningd/closing_control: remove param_tok(). It's bad form; if we do parsing inside params() then the `check` command is much more effective. Signed-off-by: Rusty Russell --- lightningd/closing_control.c | 105 +++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 37 deletions(-) diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index fea6dc86491a..71f39bca876c 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -545,13 +545,65 @@ static struct command_result *param_feerate_range(struct command *cmd, return NULL; } +/* Only one of these will be set! */ +struct some_channel { + struct channel *channel; + struct channel *unsaved_channel; + struct uncommitted_channel *uc; +}; + +static struct command_result *param_channel_or_peer(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct some_channel **sc) +{ + struct peer *peer = peer_from_json(cmd->ld, buffer, tok); + bool more_than_one; + + (*sc)->unsaved_channel = NULL; + (*sc)->uc = NULL; + + if (peer) { + (*sc)->channel = peer_any_active_channel(peer, &more_than_one); + if ((*sc)->channel) { + if (more_than_one) + goto more_than_one; + return NULL; + } + } else { + struct command_result *res; + res = command_find_channel(cmd, buffer, tok, &(*sc)->channel); + if (res) + return res; + assert((*sc)->channel); + return NULL; + } + + /* OK, we have a peer, but no active channels. */ + (*sc)->uc = peer->uncommitted_channel; + if ((*sc)->uc) + return NULL; + + (*sc)->unsaved_channel = peer_any_unsaved_channel(peer, &more_than_one); + if ((*sc)->unsaved_channel) { + if (more_than_one) + goto more_than_one; + return NULL; + } + + return command_fail(cmd, LIGHTNINGD, "Peer has no active channel"); + +more_than_one: + return command_fail(cmd, LIGHTNINGD, + "Peer has multiple channels: use channel_id or short_channel_id"); +} + static struct command_result *json_close(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { - const jsmntok_t *idtok; - struct peer *peer; struct channel *channel; unsigned int *timeout; const u8 *close_to_script = NULL; @@ -563,9 +615,10 @@ static struct command_result *json_close(struct command *cmd, u32 *feerate_range; char* end; bool anysegwit; + struct some_channel *sc = talz(tmpctx, struct some_channel); if (!param(cmd, buffer, params, - p_req("id", param_tok, &idtok), + p_req("id", param_channel_or_peer, &sc), p_opt_def("unilateraltimeout", param_number, &timeout, 48 * 3600), p_opt("destination", param_bitcoin_address, &close_to_script), @@ -578,44 +631,22 @@ static struct command_result *json_close(struct command *cmd, NULL)) return command_param_failed(); - peer = peer_from_json(cmd->ld, buffer, idtok); - if (peer) { - bool more_than_one; - channel = peer_any_active_channel(peer, &more_than_one); - if (channel && more_than_one) { - return command_fail(cmd, LIGHTNINGD, - "Peer has multiple channels: use channel_id or short_channel_id"); - } - } else { - struct command_result *res; - res = command_find_channel(cmd, buffer, idtok, &channel); - if (res) - return res; + /* Easy cases: peer can simply be forgotten. */ + if (sc->uc) { + kill_uncommitted_channel(sc->uc, "close command called"); + goto discard_unopened; } - if (!channel && peer) { - bool more_than_one; - struct uncommitted_channel *uc = peer->uncommitted_channel; - if (uc) { - /* Easy case: peer can simply be forgotten. */ - kill_uncommitted_channel(uc, "close command called"); - goto discard_unopened; - } - channel = peer_any_unsaved_channel(peer, &more_than_one); - if (channel) { - if (more_than_one) { - return command_fail(cmd, LIGHTNINGD, - "Peer has multiple channels: use channel_id or short_channel_id"); - } - channel_unsaved_close_conn(channel, - "close command called"); - goto discard_unopened; - } - return command_fail(cmd, LIGHTNINGD, - "Peer has no active channel"); + if (sc->unsaved_channel) { + channel_unsaved_close_conn(sc->unsaved_channel, + "close command called"); + goto discard_unopened; } - if (!*force_lease_close && channel->opener != LOCAL + channel = sc->channel; + assert(channel); + + if (!*force_lease_close && sc->channel->opener != LOCAL && get_block_height(cmd->ld->topology) < channel->lease_expiry) return command_fail(cmd, LIGHTNINGD, "Peer leased this channel from us, we" From ec76ba3895f63adb9c4159575530b9feda1a697d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 10:55:56 +0930 Subject: [PATCH 0985/1530] lightningd/connect_control: remove param_tok from connect. Signed-off-by: Rusty Russell --- lightningd/connect_control.c | 150 ++++++++++++++++++++++++----------- tests/test_misc.py | 6 +- 2 files changed, 109 insertions(+), 47 deletions(-) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 620231a6bb1e..a6f3db94d00a 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -81,86 +81,146 @@ static void try_connect(const tal_t *ctx, u32 seconds_delay, const struct wireaddr_internal *addrhint); -static struct command_result *json_connect(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - u32 *port; - jsmntok_t *idtok; +struct id_and_addr { struct node_id id; + const char *host; + const u16 *port; +}; + +static struct command_result *param_id_maybe_addr(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct id_and_addr *id_addr) +{ char *id_str; char *atptr; - char *ataddr = NULL; - const char *name; - struct wireaddr_internal *addr; - const char *err_msg; - - if (!param(cmd, buffer, params, - p_req("id", param_tok, (const jsmntok_t **) &idtok), - p_opt("host", param_string, &name), - p_opt("port", param_number, &port), - NULL)) - return command_param_failed(); + char *ataddr = NULL, *host; + u16 port; + jsmntok_t idtok = *tok; /* Check for id@addrport form */ - id_str = json_strdup(cmd, buffer, idtok); + id_str = json_strdup(cmd, buffer, &idtok); atptr = strchr(id_str, '@'); if (atptr) { int atidx = atptr - id_str; ataddr = tal_strdup(cmd, atptr + 1); /* Cut id. */ - idtok->end = idtok->start + atidx; + idtok.end = idtok.start + atidx; } - if (!json_to_node_id(buffer, idtok, &id)) { + if (!json_to_node_id(buffer, &idtok, &id_addr->id)) + return command_fail_badparam(cmd, name, buffer, tok, + "should be a node id"); + + if (!atptr) + return NULL; + + /* We could parse port/host in any order, using keyword params. */ + if (id_addr->host) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "id %.*s not valid", - json_tok_full_len(idtok), - json_tok_full(buffer, idtok)); + "Can't specify host as both xxx@yyy " + "and separate argument"); } - if (name && ataddr) { + port = 0; + if (!separate_address_and_port(cmd, ataddr, &host, &port)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "malformed host @part"); + + id_addr->host = host; + if (port) { + if (id_addr->port) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Can't specify port as both xxx@yyy:port " + "and separate argument"); + } + id_addr->port = tal_dup(cmd, u16, &port); + } + return NULL; +} + +static struct command_result *param_id_addr_string(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + const char **addr) +{ + if (*addr) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Can't specify host as both xxx@yyy " "and separate argument"); } + return param_string(cmd, name, buffer, tok, addr); +} - /* Get parseable host if provided somehow */ - if (!name && ataddr) - name = ataddr; - - /* Port without host name? */ - if (port && !name) { +static struct command_result *param_id_addr_u16(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + const u16 **port) +{ + u16 val; + if (*port) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Can't specify port without host"); + "Can't specify port as both xxx@yyy:port " + "and separate argument"); + } + if (json_to_u16(buffer, tok, &val)) { + if (val == 0) + return command_fail_badparam(cmd, name, buffer, tok, + "should be non-zero"); + *port = tal_dup(cmd, u16, &val); + return NULL; } - /* Was there parseable host name? */ - if (name) { - /* Is there a port? */ - if (!port) { - port = tal(cmd, u32); - *port = chainparams_get_ln_port(chainparams); - } + return command_fail_badparam(cmd, name, buffer, tok, + "should be a 16-bit integer"); +} + +static struct command_result *json_connect(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct wireaddr_internal *addr; + const char *err_msg; + struct id_and_addr id_addr; + + id_addr.host = NULL; + id_addr.port = NULL; + if (!param(cmd, buffer, params, + p_req("id", param_id_maybe_addr, &id_addr), + p_opt("host", param_id_addr_string, &id_addr.host), + p_opt("port", param_id_addr_u16, &id_addr.port), + NULL)) + return command_param_failed(); + + /* If we have a host, convert */ + if (id_addr.host) { + u16 port = id_addr.port ? *id_addr.port : chainparams_get_ln_port(chainparams); addr = tal(cmd, struct wireaddr_internal); - if (!parse_wireaddr_internal(name, addr, *port, false, + if (!parse_wireaddr_internal(id_addr.host, addr, port, false, !cmd->ld->always_use_proxy && !cmd->ld->pure_tor_setup, true, deprecated_apis, &err_msg)) { return command_fail(cmd, LIGHTNINGD, "Host %s:%u not valid: %s", - name, *port, - err_msg ? err_msg : "port is 0"); + id_addr.host, port, err_msg); } - } else + } else { addr = NULL; + /* Port without host name? */ + if (id_addr.port) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Can't specify port without host"); + } - try_connect(cmd, cmd->ld, &id, 0, addr); + try_connect(cmd, cmd->ld, &id_addr.id, 0, addr); /* Leave this here for peer_connected or connect_failed. */ - new_connect(cmd->ld, &id, cmd); + new_connect(cmd->ld, &id_addr.id, cmd); return command_still_pending(cmd); } diff --git a/tests/test_misc.py b/tests/test_misc.py index 77f8f061a881..7b6bad0cdd38 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1569,8 +1569,10 @@ def test_check_command(node_factory): with pytest.raises(RpcError, match=r'missing required parameter'): l1.rpc.check(command_to_check='connect', host='x', port=77) # Makes sure parameter types are correct. - with pytest.raises(RpcError, match=r'should be an integer'): - l1.rpc.check(command_to_check='connect', id='test', host='x', port="abcd") + with pytest.raises(RpcError, match=r'should be a 16-bit integer'): + l1.rpc.check(command_to_check='connect', + id='022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', + host='x', port="abcd") # FIXME: python wrapper doesn't let us test array params. sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) From e621b8b24efbbc750ae132f6406f893e4681c057 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 10:56:36 +0930 Subject: [PATCH 0986/1530] lightningd: remove gratuitous param_tok from help and config. They predate json_string! Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 23 ++++++++++------------- lightningd/options.c | 18 ++++++++---------- lightningd/test/run-jsonrpc.c | 10 +++++----- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 82561cd3a280..6cba43a34792 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -354,11 +354,10 @@ static void json_add_help_command(struct command *cmd, } static const struct json_command *find_command(struct json_command **commands, - const char *buffer, - const jsmntok_t *cmdtok) + const char *cmdname) { for (size_t i = 0; i < tal_count(commands); i++) { - if (json_tok_streq(buffer, cmdtok, commands[i]->name)) + if (streq(cmdname, commands[i]->name)) return commands[i]; } return NULL; @@ -376,28 +375,26 @@ static struct command_result *json_help(struct command *cmd, const jsmntok_t *params) { struct json_stream *response; - const jsmntok_t *cmdtok; + const char *cmdname; struct json_command **commands; const struct json_command *one_cmd; if (!param(cmd, buffer, params, - p_opt("command", param_tok, &cmdtok), + p_opt("command", param_string, &cmdname), NULL)) return command_param_failed(); commands = cmd->ld->jsonrpc->commands; - if (cmdtok) { - one_cmd = find_command(commands, buffer, cmdtok); + if (cmdname) { + one_cmd = find_command(commands, cmdname); if (!one_cmd) return command_fail(cmd, JSONRPC2_METHOD_NOT_FOUND, - "Unknown command '%.*s'", - cmdtok->end - cmdtok->start, - buffer + cmdtok->start); + "Unknown command %s", + cmdname); if (!deprecated_apis && one_cmd->deprecated) return command_fail(cmd, JSONRPC2_METHOD_NOT_FOUND, - "Deprecated command '%.*s'", - json_tok_full_len(cmdtok), - json_tok_full(buffer, cmdtok)); + "Deprecated command %s", + cmdname); } else one_cmd = NULL; diff --git a/lightningd/options.c b/lightningd/options.c index c1ec6ac3e841..b195acb6c922 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1672,14 +1672,14 @@ static struct command_result *json_listconfigs(struct command *cmd, { size_t i; struct json_stream *response = NULL; - const jsmntok_t *configtok; + const char *configname; if (!param(cmd, buffer, params, - p_opt("config", param_tok, &configtok), + p_opt("config", param_string, &configname), NULL)) return command_param_failed(); - if (!configtok) { + if (!configname) { response = json_stream_success(cmd); json_add_string(response, "# version", version()); } @@ -1699,9 +1699,8 @@ static struct command_result *json_listconfigs(struct command *cmd, if (name[0] != '-') continue; - if (configtok - && !memeq(buffer + configtok->start, - configtok->end - configtok->start, + if (configname + && !memeq(configname, strlen(configname), name + 1, len - 1)) continue; @@ -1715,11 +1714,10 @@ static struct command_result *json_listconfigs(struct command *cmd, } } - if (configtok && !response) { + if (configname && !response) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Unknown config option '%.*s'", - json_tok_full_len(configtok), - json_tok_full(buffer, configtok)); + "Unknown config option %s", + configname); } return command_success(cmd, response); } diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 61bea726f703..87090fb7ce9f 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -100,16 +100,16 @@ struct command_result *param_sha256(struct command *cmd UNNEEDED, const char *na const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct sha256 **hash UNNEEDED) { fprintf(stderr, "param_sha256 called!\n"); abort(); } +/* Generated stub for param_string */ +struct command_result *param_string(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + const char **str UNNEEDED) +{ fprintf(stderr, "param_string called!\n"); abort(); } /* Generated stub for param_subcommand */ const char *param_subcommand(struct command *cmd UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t tokens[] UNNEEDED, const char *name UNNEEDED, ...) { fprintf(stderr, "param_subcommand called!\n"); abort(); } -/* Generated stub for param_tok */ -struct command_result *param_tok(struct command *cmd UNNEEDED, const char *name UNNEEDED, - const char *buffer UNNEEDED, const jsmntok_t * tok UNNEEDED, - const jsmntok_t **out UNNEEDED) -{ fprintf(stderr, "param_tok called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) From a9b992ff4a1aeee4f7f1b0684b80c79eb0bf7456 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 10:56:36 +0930 Subject: [PATCH 0987/1530] plugins/spender/multifundchannel: remove json_tok. Signed-off-by: Rusty Russell --- plugins/spender/multifundchannel.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index e9e5949ab5d8..4d0c2c75be33 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1993,6 +1993,18 @@ param_positive_number(struct command *cmd, return NULL; } +static struct command_result *param_utxos_str(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + const char **str) +{ + if (tok->type != JSMN_ARRAY) + return command_fail_badparam(cmd, name, buffer, tok, + "should be an array"); + *str = tal_strndup(cmd, buffer + tok->start, + tok->end - tok->start); + return NULL; +} + /*----------------------------------------------------------------------------- Command Entry Point -----------------------------------------------------------------------------*/ @@ -2002,27 +2014,25 @@ json_multifundchannel(struct command *cmd, const jsmntok_t *params) { struct multifundchannel_destination *dests; - const char *feerate_str, *cmtmt_feerate_str; u32 *minconf; - const jsmntok_t *utxos_tok; u32 *minchannels; struct multifundchannel_command *mfc; + mfc = tal(cmd, struct multifundchannel_command); if (!param(cmd, buf, params, p_req("destinations", param_destinations_array, &dests), - p_opt("feerate", param_string, &feerate_str), + p_opt("feerate", param_string, &mfc->feerate_str), p_opt_def("minconf", param_number, &minconf, 1), - p_opt("utxos", param_tok, &utxos_tok), + p_opt("utxos", param_utxos_str, &mfc->utxos_str), p_opt("minchannels", param_positive_number, &minchannels), - p_opt("commitment_feerate", param_string, &cmtmt_feerate_str), + p_opt("commitment_feerate", param_string, &mfc->cmtmt_feerate_str), NULL)) return command_param_failed(); /* Should exist; it would only nonexist if it were a notification. */ assert(cmd->id); - mfc = tal(cmd, struct multifundchannel_command); mfc->id = *cmd->id; mfc->cmd = cmd; @@ -2031,14 +2041,7 @@ json_multifundchannel(struct command *cmd, for (size_t i = 0; i < tal_count(mfc->destinations); i++) mfc->destinations[i].mfc = mfc; - mfc->feerate_str = feerate_str; - mfc->cmtmt_feerate_str = cmtmt_feerate_str; mfc->minconf = *minconf; - if (utxos_tok) - mfc->utxos_str = tal_strndup(mfc, json_tok_full(buf, utxos_tok), - json_tok_full_len(utxos_tok)); - else - mfc->utxos_str = NULL; /* Default is that all must succeed. */ mfc->minchannels = minchannels ? *minchannels : tal_count(mfc->destinations); mfc->removeds = tal_arr(mfc, struct multifundchannel_removed, 0); From f12f0d6929b358ba9c1cd0c551484aa7e52ff680 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 10:56:36 +0930 Subject: [PATCH 0988/1530] common: remove json_tok. Make local copies for tests, and fundchannel plugin (which just passes params through). Signed-off-by: Rusty Russell --- common/json_tok.c | 8 -------- common/json_tok.h | 11 ----------- common/test/run-param.c | 8 ++++++++ plugins/spender/fundchannel.c | 9 +++++++++ 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/common/json_tok.c b/common/json_tok.c index a91eb63edf61..29a75884e02b 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -146,14 +146,6 @@ struct command_result *param_u64(struct command *cmd, const char *name, "should be an unsigned 64 bit integer"); } -struct command_result *param_tok(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - const jsmntok_t **out) -{ - *out = tok; - return NULL; -} - struct command_result *param_msat(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct amount_msat **msat) diff --git a/common/json_tok.h b/common/json_tok.h index 2b6f9e724b66..40bd32060f52 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -114,17 +114,6 @@ struct command_result *param_short_channel_id(struct command *cmd, const jsmntok_t *tok, struct short_channel_id **scid); -/* - * Set the address of @out to @tok. Used as a callback by handlers that - * want to unmarshal @tok themselves. - * - * Usage of this is discouraged. Writing a local static bespoke handler is - * preferred. - */ -struct command_result *param_tok(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t * tok, - const jsmntok_t **out); - /* Ignore the token. Not usually used. */ struct command_result *param_ignore(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, diff --git a/common/test/run-param.c b/common/test/run-param.c index 6577f5b29af0..c3611cfb5fde 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -220,6 +220,14 @@ static void sanity(void) } } +static struct command_result *param_tok(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t * tok, + const jsmntok_t **out) +{ + *out = tok; + return NULL; +} + /* * Make sure toks are passed through correctly, and also make sure * optional missing toks are set to NULL. diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index 1feb639afd35..5b235f911380 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -28,6 +28,15 @@ fundchannel_get_result(struct command *cmd, const jsmntok_t *result, void *nothing UNUSED); +/* Generally a bad idea, but makes sense here. */ +static struct command_result *param_tok(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t * tok, + const jsmntok_t **out) +{ + *out = tok; + return NULL; +} + /* Thin wrapper aroud multifundchannel. */ static struct command_result * json_fundchannel(struct command *cmd, From 401f1debc5a5b71fa73fe90c3a37f50bc6baa7c9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 13:19:38 +0930 Subject: [PATCH 0989/1530] common: clean up json routine locations. We have them split over common/param.c, common/json.c, common/json_helpers.c, common/json_tok.c and common/json_stream.c. Change that to: * common/json_parse (all the json_to_xxx routines) * common/json_parse_simple (simplest the json parsing routines, for cli too) * common/json_stream (all the json_add_xxx routines) * common/json_param (all the param and param_xxx routines) Signed-off-by: Rusty Russell --- cli/Makefile | 3 +- cli/test/Makefile | 2 +- cli/test/run-human-mode.c | 10 - cli/test/run-large-input.c | 10 - cli/test/run-remove-hint.c | 10 - common/Makefile | 7 +- common/bolt11_json.c | 1 - common/json.c | 1185 ----------------- common/json.h | 259 ---- common/json_command.h | 2 +- common/json_helpers.c | 495 ------- common/json_helpers.h | 208 --- common/{json_tok.c => json_param.c} | 367 ++++- common/{json_tok.h => json_param.h} | 142 +- common/json_parse.c | 719 ++++++++++ common/json_parse.h | 156 +++ common/json_parse_simple.c | 552 ++++++++ common/json_parse_simple.h | 122 ++ common/json_stream.c | 436 +++++- common/json_stream.h | 217 +++ common/param.c | 369 ----- common/param.h | 139 -- common/test/Makefile | 5 +- common/test/run-bigsize.c | 25 +- common/test/run-bolt12_decode.c | 25 +- common/test/run-bolt12_merkle-json.c | 53 +- common/test/run-bolt12_period.c | 25 +- common/test/run-json.c | 3 +- common/test/run-json_remove.c | 99 +- common/test/run-json_scan.c | 25 +- common/test/run-param.c | 33 +- .../test/run-route_blinding_override_test.c | 67 +- common/test/run-route_blinding_test.c | 70 +- devtools/Makefile | 5 +- devtools/onion.c | 2 +- gossipd/test/Makefile | 6 +- gossipd/test/run-check_channel_announcement.c | 18 - gossipd/test/run-check_node_announcement.c | 18 - gossipd/test/run-crc32_of_update.c | 18 - gossipd/test/run-extended-info.c | 20 +- gossipd/test/run-next_block_range.c | 18 - gossipd/test/run-txout_failure.c | 18 - lightningd/Makefile | 7 +- lightningd/bitcoind.c | 2 +- lightningd/chaintopology.c | 2 +- lightningd/channel.c | 1 - lightningd/channel_control.c | 5 +- lightningd/closing_control.c | 4 +- lightningd/connect_control.c | 5 +- lightningd/datastore.c | 4 +- lightningd/dual_open_control.c | 4 +- lightningd/gossip_control.c | 5 +- lightningd/hsm_control.c | 5 +- lightningd/invoice.c | 4 +- lightningd/json.c | 4 +- lightningd/jsonrpc.c | 4 +- lightningd/jsonrpc.h | 1 - lightningd/log.c | 3 +- lightningd/memdump.c | 2 +- lightningd/notification.c | 1 - lightningd/offer.c | 5 +- lightningd/onion_message.c | 4 +- lightningd/opening_control.c | 4 +- lightningd/options.c | 4 +- lightningd/pay.c | 4 +- lightningd/peer_control.c | 4 +- lightningd/peer_control.h | 2 +- lightningd/peer_htlcs.c | 4 +- lightningd/ping.c | 3 +- lightningd/plugin.c | 1 - lightningd/plugin_control.c | 3 +- lightningd/plugin_hook.c | 1 + lightningd/routehint.c | 2 +- lightningd/signmessage.c | 4 +- lightningd/test/Makefile | 2 +- lightningd/test/run-find_my_abspath.c | 10 - lightningd/test/run-invoice-select-inchan.c | 69 +- lightningd/test/run-jsonrpc.c | 7 +- lightningd/test/run-log-pruning.c | 20 +- lightningd/test/run-shuffle_fds.c | 10 - plugins/Makefile | 7 +- plugins/autoclean.c | 3 +- plugins/bcli.c | 3 +- plugins/chanbackup.c | 3 +- plugins/fetchinvoice.c | 2 +- plugins/funder.c | 3 +- plugins/keysend.c | 4 +- plugins/libplugin.h | 4 +- plugins/offers.c | 1 + plugins/offers.h | 1 - plugins/offers_inv_hook.c | 1 + plugins/offers_offer.c | 3 +- plugins/pay.c | 2 +- plugins/spender/fundchannel.c | 2 +- plugins/spender/multifundchannel.c | 2 +- plugins/spender/multiwithdraw.c | 2 +- plugins/topology.c | 2 +- plugins/txprepare.c | 2 +- tests/plugins/Makefile | 2 +- tests/plugins/test_libplugin.c | 3 +- .../test_selfdisable_after_getmanifest.c | 2 +- wallet/reservation.c | 4 +- wallet/walletrpc.c | 4 +- 103 files changed, 3113 insertions(+), 3139 deletions(-) delete mode 100644 common/json.c delete mode 100644 common/json.h delete mode 100644 common/json_helpers.c delete mode 100644 common/json_helpers.h rename common/{json_tok.c => json_param.c} (69%) rename common/{json_tok.h => json_param.h} (61%) create mode 100644 common/json_parse.c create mode 100644 common/json_parse.h create mode 100644 common/json_parse_simple.c create mode 100644 common/json_parse_simple.h delete mode 100644 common/param.c delete mode 100644 common/param.h diff --git a/cli/Makefile b/cli/Makefile index 635a8d4e9cde..fcbf84a6a4cd 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -8,8 +8,7 @@ ALL_PROGRAMS += cli/lightning-cli LIGHTNING_CLI_COMMON_OBJS := \ bitcoin/chainparams.o \ common/configdir.o \ - common/json.o \ - common/json_stream.o \ + common/json_parse_simple.o \ common/status_levels.o \ common/utils.o \ common/version.o diff --git a/cli/test/Makefile b/cli/test/Makefile index e49837f229b9..3b5a800fefad 100644 --- a/cli/test/Makefile +++ b/cli/test/Makefile @@ -12,7 +12,7 @@ CLI_TEST_COMMON_OBJS := \ common/configdir.o \ common/daemon_conn.o \ common/htlc_state.o \ - common/json.o \ + common/json_parse_simple.o \ common/pseudorand.o \ common/memleak.o \ common/msg_queue.o \ diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index e88b220a3854..078af84652d1 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -78,16 +78,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for log_level_name */ const char *log_level_name(enum log_level level UNNEEDED) { fprintf(stderr, "log_level_name called!\n"); abort(); } diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index 1bb043bf16a2..bbba588bd359 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -78,16 +78,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for log_level_name */ const char *log_level_name(enum log_level level UNNEEDED) { fprintf(stderr, "log_level_name called!\n"); abort(); } diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index 3246db8a3917..e416a2973d66 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -81,16 +81,6 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for log_level_name */ const char *log_level_name(enum log_level level UNNEEDED) { fprintf(stderr, "log_level_name called!\n"); abort(); } diff --git a/common/Makefile b/common/Makefile index d18494317c67..b48f993f24e4 100644 --- a/common/Makefile +++ b/common/Makefile @@ -46,10 +46,10 @@ COMMON_SRC_NOGEN := \ common/initial_channel.c \ common/initial_commit_tx.c \ common/iso4217.c \ - common/json.c \ - common/json_helpers.c \ + common/json_param.c \ + common/json_parse.c \ + common/json_parse_simple.c \ common/json_stream.c \ - common/json_tok.c \ common/key_derive.c \ common/keyset.c \ common/lease_rates.c \ @@ -58,7 +58,6 @@ COMMON_SRC_NOGEN := \ common/node_id.c \ common/onion.c \ common/onionreply.c \ - common/param.c \ common/peer_billboard.c \ common/peer_failed.c \ common/peer_io.c \ diff --git a/common/bolt11_json.c b/common/bolt11_json.c index b2280c47d157..dde88c6dd78a 100644 --- a/common/bolt11_json.c +++ b/common/bolt11_json.c @@ -6,7 +6,6 @@ #include #include #include -#include #include static void json_add_fallback(struct json_stream *response, diff --git a/common/json.c b/common/json.c deleted file mode 100644 index 08fa96e88449..000000000000 --- a/common/json.c +++ /dev/null @@ -1,1185 +0,0 @@ -/* JSON core and helpers */ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -const char *json_tok_full(const char *buffer, const jsmntok_t *t) -{ - if (t->type == JSMN_STRING) - return buffer + t->start - 1; - return buffer + t->start; -} - -/* Include " if it's a string. */ -int json_tok_full_len(const jsmntok_t *t) -{ - if (t->type == JSMN_STRING) - return t->end - t->start + 2; - return t->end - t->start; -} - -bool json_tok_strneq(const char *buffer, const jsmntok_t *tok, - const char *str, size_t len) -{ - if (tok->type != JSMN_STRING) - return false; - return memeq(buffer + tok->start, tok->end - tok->start, str, len); -} - -bool json_tok_streq(const char *buffer, const jsmntok_t *tok, const char *str) -{ - return json_tok_strneq(buffer, tok, str, strlen(str)); -} - -bool json_tok_startswith(const char *buffer, const jsmntok_t *tok, - const char *prefix) -{ - if (tok->type != JSMN_STRING) - return false; - if (tok->end - tok->start < strlen(prefix)) - return false; - return memcmp(buffer + tok->start, - prefix, strlen(prefix)) == 0; -} - -bool json_tok_endswith(const char *buffer, const jsmntok_t *tok, - const char *suffix) -{ - if (tok->type != JSMN_STRING) - return false; - if (tok->end - tok->start < strlen(suffix)) - return false; - return memcmp(buffer + tok->end - strlen(suffix), - suffix, strlen(suffix)) == 0; -} - -char *json_strdup(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) -{ - return tal_strndup(ctx, buffer + tok->start, tok->end - tok->start); -} - -bool json_to_u64(const char *buffer, const jsmntok_t *tok, - uint64_t *num) -{ - char *end; - unsigned long long l; - - l = strtoull(buffer + tok->start, &end, 0); - if (end != buffer + tok->end) - return false; - - BUILD_ASSERT(sizeof(l) >= sizeof(*num)); - *num = l; - - /* Check for overflow */ - if (l == ULLONG_MAX && errno == ERANGE) - return false; - - if (*num != l) - return false; - - return true; -} - -bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num) -{ - char *end; - long long l; - - l = strtoll(buffer + tok->start, &end, 0); - if (end != buffer + tok->end) - return false; - - BUILD_ASSERT(sizeof(l) >= sizeof(*num)); - *num = l; - - /* Check for overflow/underflow */ - if ((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) - return false; - - /* Check if the number did not fit in `s64` (in case `long long` - is a bigger type). */ - if (*num != l) - return false; - - return true; -} - -bool json_to_millionths(const char *buffer, const jsmntok_t *tok, - u64 *millionths) -{ - int decimal_places = -1; - bool has_digits = 0; - - *millionths = 0; - for (int i = tok->start; i < tok->end; i++) { - if (isdigit(buffer[i])) { - has_digits = true; - /* Ignore too much precision */ - if (decimal_places >= 0 && ++decimal_places > 6) - continue; - if (mul_overflows_u64(*millionths, 10)) - return false; - *millionths *= 10; - if (add_overflows_u64(*millionths, buffer[i] - '0')) - return false; - *millionths += buffer[i] - '0'; - } else if (buffer[i] == '.') { - if (decimal_places != -1) - return false; - decimal_places = 0; - } else - return false; - } - - if (!has_digits) - return false; - - if (decimal_places == -1) - decimal_places = 0; - - while (decimal_places < 6) { - if (mul_overflows_u64(*millionths, 10)) - return false; - *millionths *= 10; - decimal_places++; - } - return true; -} - -bool json_to_number(const char *buffer, const jsmntok_t *tok, - unsigned int *num) -{ - uint64_t u64; - - if (!json_to_u64(buffer, tok, &u64)) - return false; - *num = u64; - - /* Just in case it doesn't fit. */ - if (*num != u64) - return false; - return true; -} - -bool json_to_u16(const char *buffer, const jsmntok_t *tok, - short unsigned int *num) -{ - uint64_t u64; - - if (!json_to_u64(buffer, tok, &u64)) - return false; - *num = u64; - - /* Just in case it doesn't fit. */ - if (*num != u64) - return false; - return true; -} - -bool json_to_u32(const char *buffer, const jsmntok_t *tok, - uint32_t *num) -{ - uint64_t u64; - - if (!json_to_u64(buffer, tok, &u64)) - return false; - *num = u64; - - /* Just in case it doesn't fit. */ - if (*num != u64) - return false; - return true; -} - -bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num) -{ - s64 tmp; - - if (!json_to_s64(buffer, tok, &tmp)) - return false; - *num = tmp; - - /* Just in case it doesn't fit. */ - if (*num != tmp) - return false; - - return true; -} - -bool json_to_errcode(const char *buffer, const jsmntok_t *tok, errcode_t *errcode) -{ - s64 tmp; - - if (!json_to_s64(buffer, tok, &tmp)) - return false; - *errcode = tmp; - - /* Just in case it doesn't fit. */ - if (*errcode != tmp) - return false; - - return true; -} - -bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b) -{ - if (tok->type != JSMN_PRIMITIVE) - return false; - if (memeqstr(buffer + tok->start, tok->end - tok->start, "true")) { - *b = true; - return true; - } - if (memeqstr(buffer + tok->start, tok->end - tok->start, "false")) { - *b = false; - return true; - } - return false; -} - -bool json_to_sha256(const char *buffer, const jsmntok_t *tok, struct sha256 *dest) -{ - if (tok->type != JSMN_STRING) - return false; - - return hex_decode(buffer + tok->start, tok->end - tok->start, dest, - sizeof(struct sha256)); -} - -u8 *json_tok_bin_from_hex(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) -{ - u8 *result; - size_t hexlen, rawlen; - hexlen = tok->end - tok->start; - rawlen = hex_data_size(hexlen); - - result = tal_arr(ctx, u8, rawlen); - if (!hex_decode(buffer + tok->start, hexlen, result, rawlen)) - return tal_free(result); - - return result; -} - -bool json_tok_is_num(const char *buffer, const jsmntok_t *tok) -{ - if (tok->type != JSMN_PRIMITIVE) - return false; - - for (int i = tok->start; i < tok->end; i++) - if (!cisdigit(buffer[i])) - return false; - return true; -} - -bool json_tok_is_null(const char *buffer, const jsmntok_t *tok) -{ - if (tok->type != JSMN_PRIMITIVE) - return false; - return buffer[tok->start] == 'n'; -} - -const jsmntok_t *json_next(const jsmntok_t *tok) -{ - const jsmntok_t *t; - size_t i; - - for (t = tok + 1, i = 0; i < tok->size; i++) - t = json_next(t); - - return t; -} - -static const jsmntok_t *json_get_membern(const char *buffer, - const jsmntok_t tok[], - const char *label, size_t len) -{ - const jsmntok_t *t; - size_t i; - - if (tok->type != JSMN_OBJECT) - return NULL; - - json_for_each_obj(i, t, tok) - if (json_tok_strneq(buffer, t, label, len)) - return t + 1; - - return NULL; -} - -const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], - const char *label) -{ - return json_get_membern(buffer, tok, label, strlen(label)); -} - -const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index) -{ - const jsmntok_t *t; - size_t i; - - if (tok->type != JSMN_ARRAY) - return NULL; - - json_for_each_arr(i, t, tok) { - if (index == 0) - return t; - index--; - } - - return NULL; -} - -/*----------------------------------------------------------------------------- -JSMN Result Validation Starts ------------------------------------------------------------------------------*/ -/*~ LIBJSMN is a fast, small JSON parsing library. - * - * "Fast, small" means it does not, in fact, do a - * lot of checking for invalid JSON. - * - * For example, by itself it would accept the strings - * `{"1" "2" "3" "4"}` and `["key": 1 2 3 4]` as valid. - * Obviously those are not in any way valid JSON. - * - * This part of the code performs some filtering so - * that at least some of the invalid JSON that - * LIBJSMN accepts, will be rejected by - * json_parse_input. It also checks that strings are valid UTF-8. - */ - -/*~ These functions are used in JSMN validation. - * - * The calling convention is that the "current" token - * is passed in as the first argument, and after the - * validator, is returned from the function. - * - * p = validate_jsmn_datum(p, end, valid); - * - * The reason has to do with typical C ABIs. - * Usually, the first few arguments are passed in via - * register, and the return value is also returned - * via register. - * This calling convention generally ensures that - * the current token pointer `p` is always in a - * register and is never forced into memory by the - * compiler. - * - * These functions are pre-declared here as they - * are interrecursive. - * Note that despite the recursion, `p` is only ever - * advanced, and there is only ever one `p` value, - * thus the overall algorithm is strict O(n) - * (*not* amortized) in time. - * The recursion does mean the algorithm is O(d) - * in memory (specifically stack frames), where d - * is the nestedness of objects in the input. - * This may become an issue later if we are in a - * stack-limited environment, such as if we actually - * went and used threads. - */ -/* Validate a *single* datum. */ -static const jsmntok_t * -validate_jsmn_datum(const char *buf, - const jsmntok_t *p, - const jsmntok_t *end, - bool *valid); -/*~ Validate a key-value pair. - * - * In JSMN, objects are not dictionaries. - * Instead, they are a sequence of datums. - * - * In fact, objects and arrays in JSMN are "the same", - * they only differ in delimiter characters. - * - * Of course, in "real" JSON, an object is a dictionary - * of key-value pairs. - * - * So what JSMN does is that the syntax "key": "value" - * is considered a *single* datum, a string "key" - * that contains a value "value". - * - * Indeed, JSMN accepts `["key": "value"]` as well as - * `{"item1", "item2"}`. - * The entire point of the validate_jsmn_result function - * is to reject such improper arrays and objects. - */ -static const jsmntok_t * -validate_jsmn_keyvalue(const char *buf, - const jsmntok_t *p, - const jsmntok_t *end, - bool *valid); - -static const jsmntok_t * -validate_jsmn_datum(const char *buf, - const jsmntok_t *p, - const jsmntok_t *end, - bool *valid) -{ - int i; - int sz; - - if (p >= end) { - *valid = false; - return p; - } - - switch (p->type) { - case JSMN_STRING: - if (!utf8_check(buf + p->start, p->end - p->start)) - *valid = false; - /* Fall thru */ - case JSMN_UNDEFINED: - case JSMN_PRIMITIVE: - /* These types should not have sub-datums. */ - if (p->size != 0) - *valid = false; - else - ++p; - break; - - case JSMN_ARRAY: - /* Save the array size; we will advance p. */ - sz = p->size; - ++p; - for (i = 0; i < sz; ++i) { - /* Arrays should only contain standard JSON datums. */ - p = validate_jsmn_datum(buf, p, end, valid); - if (!*valid) - break; - } - break; - - case JSMN_OBJECT: - /* Save the object size; we will advance p. */ - sz = p->size; - ++p; - for (i = 0; i < sz; ++i) { - /* Objects should only contain key-value pairs. */ - p = validate_jsmn_keyvalue(buf, p, end, valid); - if (!*valid) - break; - } - break; - - default: - *valid = false; - break; - } - - return p; -} -/* Key-value pairs *must* be strings with size 1. */ -static inline const jsmntok_t * -validate_jsmn_keyvalue(const char *buf, - const jsmntok_t *p, - const jsmntok_t *end, - bool *valid) -{ - if (p >= end) { - *valid = false; - return p; - } - - /* Check key. - * - * JSMN parses the syntax `"key": "value"` as a - * JSMN_STRING of size 1, containing the value - * datum as a sub-datum. - * - * Thus, keys in JSON objects are really strings - * that "contain" the value, thus we check if - * the size is 1. - * - * JSMN supports a non-standard syntax such as - * `"key": 1 2 3 4`, which it considers as a - * string object that contains a sequence of - * sub-datums 1 2 3 4. - * The check below that p->size == 1 also - * incidentally rejects that non-standard - * JSON. - */ - if (p->type != JSMN_STRING || p->size != 1 - || !utf8_check(buf + p->start, p->end - p->start)) { - *valid = false; - return p; - } - - ++p; - return validate_jsmn_datum(buf, p, end, valid); -} - -/** validate_jsmn_parse_output - * - * @brief Validates the result of jsmn_parse. - * - * @desc LIBJMSN is a small fast library, not a - * comprehensive library. - * - * This simply means that LIBJSMN will accept a - * *lot* of very strange text that is technically - * not JSON. - * - * For example, LIBJSMN would accept the strings - * `{"1" "2" "3" "4"}` and `["key": 1 2 3 4]` as valid. - * - * This can lead to strange sequences of jsmntok_t - * objects. - * Unfortunately, most of our code assumes that - * the data fed into our JSON-RPC interface is - * valid JSON, and in particular is not invalid - * JSON that tickles LIBJSMN into emitting - * strange sequences of `jsmntok_t`. - * - * This function detects such possible problems - * and returns false if such an issue is found. - * If so, it is probably unsafe to pass the - * `jsmntok_t` generated by LIBJSMN to any other - * parts of our code. - * - * @param p - The first jsmntok_t token to process. - * This function does not assume that semantically - * only one JSON datum is processed; it does expect - * a sequence of complete JSON datums (which is - * what LIBJSMN *should* output). - * @param end - One past the end of jsmntok_t. - * Basically, this function is assured to read tokens - * starting at p up to end - 1. - * If p >= end, this will not validate anything and - * trivially return true. - * - * @return true if there appears to be no problem - * with the jsmntok_t sequence outputted by - * `jsmn_parse`, false otherwise. - */ -static bool -validate_jsmn_parse_output(const char *buf, - const jsmntok_t *p, const jsmntok_t *end) -{ - bool valid = true; - - while (p < end && valid) - p = validate_jsmn_datum(buf, p, end, &valid); - - return valid; -} - -/*----------------------------------------------------------------------------- -JSMN Result Validation Ends ------------------------------------------------------------------------------*/ - -void toks_reset(jsmntok_t *toks) -{ - assert(tal_count(toks) >= 1); - toks[0].type = JSMN_UNDEFINED; -} - -jsmntok_t *toks_alloc(const tal_t *ctx) -{ - jsmntok_t *toks = tal_arr(ctx, jsmntok_t, 10); - toks_reset(toks); - return toks; -} - -bool json_parse_input(jsmn_parser *parser, - jsmntok_t **toks, - const char *input, int len, - bool *complete) -{ - int ret; - -again: - ret = jsmn_parse(parser, input, len, *toks, tal_count(*toks) - 1); - - switch (ret) { - case JSMN_ERROR_INVAL: - return false; - case JSMN_ERROR_NOMEM: - tal_resize(toks, tal_count(*toks) * 2); - goto again; - } - - /* Check whether we read at least one full root element, i.e., root - * element has its end set. */ - if ((*toks)[0].type == JSMN_UNDEFINED || (*toks)[0].end == -1) { - *complete = false; - return true; - } - - /* If we read a partial element at the end of the stream we'll get a - * ret=JSMN_ERROR_PART, but due to the previous check we know we read at - * least one full element, so count tokens that are part of this root - * element. */ - ret = json_next(*toks) - *toks; - - if (!validate_jsmn_parse_output(input, *toks, *toks + ret)) - return false; - - /* Cut to length and return. */ - tal_resize(toks, ret + 1); - /* Make sure last one is always referenceable. */ - (*toks)[ret].type = -1; - (*toks)[ret].start = (*toks)[ret].end = (*toks)[ret].size = 0; - - *complete = true; - return true; -} - -jsmntok_t *json_parse_simple(const tal_t *ctx, const char *input, int len) -{ - bool complete; - jsmn_parser parser; - jsmntok_t *toks = toks_alloc(ctx); - - jsmn_init(&parser); - - if (!json_parse_input(&parser, &toks, input, len, &complete) - || !complete) - return tal_free(toks); - return toks; -} - -const char *jsmntype_to_string(jsmntype_t t) -{ - switch (t) { - case JSMN_UNDEFINED : - return "UNDEFINED"; - case JSMN_OBJECT : - return "OBJECT"; - case JSMN_ARRAY : - return "ARRAY"; - case JSMN_STRING : - return "STRING"; - case JSMN_PRIMITIVE : - return "PRIMITIVE"; - } - return "INVALID"; -} - -jsmntok_t *json_tok_copy(const tal_t *ctx, const jsmntok_t *tok) -{ - return tal_dup_arr(ctx, jsmntok_t, tok, json_next(tok) - tok, 0); -} - -void json_tok_remove(jsmntok_t **tokens, - jsmntok_t *obj_or_array, const jsmntok_t *tok, size_t num) -{ - const jsmntok_t *src = tok; - const jsmntok_t *end = json_next(*tokens); - jsmntok_t *dest = *tokens + (tok - *tokens); - int remove_count; - - assert(*tokens); - assert(obj_or_array->type == JSMN_ARRAY - || obj_or_array->type == JSMN_OBJECT); - /* obj_or_array must be inside tokens, and tok must be inside - * obj_or_array */ - assert(obj_or_array >= *tokens - && obj_or_array < *tokens + tal_count(*tokens)); - assert(tok >= obj_or_array - && tok < *tokens + tal_count(*tokens)); - - for (int i = 0; i < num; i++) - src = json_next(src); - - /* Don't give us a num which goes over end of obj_or_array. */ - assert(src <= json_next(obj_or_array)); - - remove_count = src - tok; - - memmove(dest, src, sizeof(jsmntok_t) * (end - src)); - - /* Subtract first: this ptr may move after tal_resize! */ - obj_or_array->size -= num; - tal_resize(tokens, tal_count(*tokens) - remove_count); -} - -/* talfmt take a ctx pointer and return NULL or a valid pointer. - * fmt takes the argument, and returns a bool. - * - * This function returns NULL on success, or errmsg on failure. -*/ -static const char *handle_percent(const char *buffer, - const jsmntok_t *tok, - va_list *ap) -{ - void *ctx; - const char *fmtname; - - /* This is set to (dummy) json_scan if it's a non-tal fmt */ - ctx = va_arg(*ap, void *); - fmtname = va_arg(*ap, const char *); - if (ctx != json_scan) { - void *(*talfmt)(void *, const char *, const jsmntok_t *); - void **p; - p = va_arg(*ap, void **); - talfmt = va_arg(*ap, void *(*)(void *, const char *, const jsmntok_t *)); - *p = talfmt(ctx, buffer, tok); - if (*p != NULL) - return NULL; - } else { - bool (*fmt)(const char *, const jsmntok_t *, void *); - void *p; - - p = va_arg(*ap, void *); - fmt = va_arg(*ap, bool (*)(const char *, const jsmntok_t *, void *)); - if (fmt(buffer, tok, p)) - return NULL; - } - - return tal_fmt(tmpctx, "%s could not parse %.*s", - fmtname, - json_tok_full_len(tok), - json_tok_full(buffer, tok)); -} - -/* GUIDE := OBJ | ARRAY | '%' - * OBJ := '{' FIELDLIST '}' - * FIELDLIST := FIELD [',' FIELD]* - * FIELD := LITERAL ':' FIELDVAL - * FIELDVAL := OBJ | ARRAY | LITERAL | '%' - * ARRAY := '[' ARRLIST ']' - * ARRLIST := ARRELEM [',' ARRELEM]* - * ARRELEM := NUMBER ':' FIELDVAL - */ - -static void parse_literal(const char **guide, - const char **literal, - size_t *len) -{ - *literal = *guide; - *len = strspn(*guide, - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "_-"); - *guide += *len; -} - -static void parse_number(const char **guide, u32 *number) -{ - char *endp; - long int l; - - l = strtol(*guide, &endp, 10); - assert(endp != *guide); - assert(errno != ERANGE); - - /* Test for overflow */ - *number = l; - assert(*number == l); - - *guide = endp; -} - -static char guide_consume_one(const char **guide) -{ - char c = **guide; - (*guide)++; - return c; -} - -static void guide_must_be(const char **guide, char c) -{ - char actual = guide_consume_one(guide); - assert(actual == c); -} - -/* Recursion: return NULL on success, errmsg on fail */ -static const char *parse_obj(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap); - -static const char *parse_arr(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap); - -static const char *parse_guide(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - const char *errmsg; - - if (**guide == '{') { - errmsg = parse_obj(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - } else if (**guide == '[') { - errmsg = parse_arr(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - } else { - guide_must_be(guide, '%'); - errmsg = handle_percent(buffer, tok, ap); - if (errmsg) - return errmsg; - } - return NULL; -} - -static const char *parse_fieldval(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - const char *errmsg; - - if (**guide == '{') { - errmsg = parse_obj(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - } else if (**guide == '[') { - errmsg = parse_arr(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - } else if (**guide == '%') { - guide_consume_one(guide); - errmsg = handle_percent(buffer, tok, ap); - if (errmsg) - return errmsg; - } else { - const char *literal; - size_t len; - - /* Literal must match exactly (modulo quotes for strings) */ - parse_literal(guide, &literal, &len); - if (!memeq(buffer + tok->start, tok->end - tok->start, - literal, len)) { - return tal_fmt(tmpctx, - "%.*s does not match expected %.*s", - json_tok_full_len(tok), - json_tok_full(buffer, tok), - (int)len, literal); - } - } - return NULL; -} - -static const char *parse_field(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - const jsmntok_t *member; - size_t len; - const char *memname; - - parse_literal(guide, &memname, &len); - guide_must_be(guide, ':'); - - member = json_get_membern(buffer, tok, memname, len); - if (!member) { - return tal_fmt(tmpctx, "object does not have member %.*s", - (int)len, memname); - } - - return parse_fieldval(buffer, member, guide, ap); -} - -static const char *parse_fieldlist(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - for (;;) { - const char *errmsg; - - errmsg = parse_field(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - if (**guide != ',') - break; - guide_consume_one(guide); - } - return NULL; -} - -static const char *parse_obj(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - const char *errmsg; - - guide_must_be(guide, '{'); - - if (tok->type != JSMN_OBJECT) { - return tal_fmt(tmpctx, "token is not an object: %.*s", - json_tok_full_len(tok), - json_tok_full(buffer, tok)); - } - - errmsg = parse_fieldlist(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - - guide_must_be(guide, '}'); - return NULL; -} - -static const char *parse_arrelem(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - const jsmntok_t *member; - u32 idx; - - parse_number(guide, &idx); - guide_must_be(guide, ':'); - - member = json_get_arr(tok, idx); - if (!member) { - return tal_fmt(tmpctx, "token has no index %u: %.*s", - idx, - json_tok_full_len(tok), - json_tok_full(buffer, tok)); - } - - return parse_fieldval(buffer, member, guide, ap); -} - -static const char *parse_arrlist(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - const char *errmsg; - - for (;;) { - errmsg = parse_arrelem(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - if (**guide != ',') - break; - guide_consume_one(guide); - } - return NULL; -} - -static const char *parse_arr(const char *buffer, - const jsmntok_t *tok, - const char **guide, - va_list *ap) -{ - const char *errmsg; - - guide_must_be(guide, '['); - - if (tok->type != JSMN_ARRAY) { - return tal_fmt(tmpctx, "token is not an array: %.*s", - json_tok_full_len(tok), - json_tok_full(buffer, tok)); - } - - errmsg = parse_arrlist(buffer, tok, guide, ap); - if (errmsg) - return errmsg; - - guide_must_be(guide, ']'); - return NULL; -} - -const char *json_scanv(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok, - const char *guide, - va_list ap) -{ - va_list cpy; - const char *orig_guide = guide, *errmsg; - - /* We need this, since &ap doesn't work on some platforms... */ - va_copy(cpy, ap); - errmsg = parse_guide(buffer, tok, &guide, &cpy); - va_end(cpy); - - if (errmsg) { - return tal_fmt(ctx, "Parsing '%.*s': %s", - (int)(guide - orig_guide), orig_guide, - errmsg); - } - assert(guide[0] == '\0'); - return NULL; -} - -const char *json_scan(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok, - const char *guide, - ...) -{ - va_list ap; - const char *ret; - - va_start(ap, guide); - ret = json_scanv(ctx, buffer, tok, guide, ap); - va_end(ap); - return ret; -} - -void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value) -{ - json_add_member(result, fieldname, false, "%u", value); -} - -void json_add_u64(struct json_stream *result, const char *fieldname, - uint64_t value) -{ - json_add_member(result, fieldname, false, "%"PRIu64, value); -} - -void json_add_s64(struct json_stream *result, const char *fieldname, - int64_t value) -{ - json_add_member(result, fieldname, false, "%"PRIi64, value); -} - -void json_add_u32(struct json_stream *result, const char *fieldname, - uint32_t value) -{ - json_add_member(result, fieldname, false, "%u", value); -} - -void json_add_s32(struct json_stream *result, const char *fieldname, - int32_t value) -{ - json_add_member(result, fieldname, false, "%d", value); -} - -void json_add_literal(struct json_stream *result, const char *fieldname, - const char *literal, int len) -{ - /* Literal may contain quotes, so bypass normal checks */ - char *dest = json_member_direct(result, fieldname, len); - memcpy(dest, literal, len); -} - -void json_add_stringn(struct json_stream *result, const char *fieldname, - const char *value TAKES, size_t value_len) -{ - json_add_member(result, fieldname, true, "%.*s", (int)value_len, value); - if (taken(value)) - tal_free(value); -} - -void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES) -{ - json_add_stringn(result, fieldname, value, strlen(value)); -} - -void json_add_bool(struct json_stream *result, const char *fieldname, bool value) -{ - json_add_member(result, fieldname, false, value ? "true" : "false"); -} - -void json_add_null(struct json_stream *stream, const char *fieldname) -{ - json_add_member(stream, fieldname, false, "null"); -} - -void json_add_hex(struct json_stream *js, const char *fieldname, - const void *data, size_t len) -{ - /* Size without NUL term */ - size_t hexlen = hex_str_size(len) - 1; - char *dest; - - dest = json_member_direct(js, fieldname, 1 + hexlen + 1); - dest[0] = '"'; - if (!hex_encode(data, len, dest + 1, hexlen + 1)) - abort(); - dest[1+hexlen] = '"'; -} - -void json_add_hex_talarr(struct json_stream *result, - const char *fieldname, - const tal_t *data) -{ - json_add_hex(result, fieldname, data, tal_bytelen(data)); -} - -void json_add_escaped_string(struct json_stream *result, const char *fieldname, - const struct json_escape *esc TAKES) -{ - /* Already escaped, don't re-escape! */ - char *dest = json_member_direct(result, fieldname, - 1 + strlen(esc->s) + 1); - - dest[0] = '"'; - memcpy(dest + 1, esc->s, strlen(esc->s)); - dest[1+strlen(esc->s)] = '"'; - if (taken(esc)) - tal_free(esc); -} - -void json_add_timeabs(struct json_stream *result, const char *fieldname, - struct timeabs t) -{ - json_add_member(result, fieldname, false, "%" PRIu64 ".%03" PRIu64, - (u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000); -} - -void json_add_time(struct json_stream *result, const char *fieldname, - struct timespec ts) -{ - char timebuf[100]; - - snprintf(timebuf, sizeof(timebuf), "%lu.%09u", - (unsigned long)ts.tv_sec, - (unsigned)ts.tv_nsec); - json_add_string(result, fieldname, timebuf); -} - -void json_add_timeiso(struct json_stream *result, - const char *fieldname, - struct timeabs *time) -{ - char iso8601_msec_fmt[sizeof("YYYY-mm-ddTHH:MM:SS.%03dZ")]; - char iso8601_s[sizeof("YYYY-mm-ddTHH:MM:SS.nnnZ")]; - - strftime(iso8601_msec_fmt, sizeof(iso8601_msec_fmt), - "%FT%T.%%03dZ", gmtime(&time->ts.tv_sec)); - snprintf(iso8601_s, sizeof(iso8601_s), - iso8601_msec_fmt, (int) time->ts.tv_nsec / 1000000); - - json_add_string(result, fieldname, iso8601_s); -} - - -void json_add_tok(struct json_stream *result, const char *fieldname, - const jsmntok_t *tok, const char *buffer) -{ - char *space; - assert(tok->type != JSMN_UNDEFINED); - - space = json_member_direct(result, fieldname, json_tok_full_len(tok)); - memcpy(space, json_tok_full(buffer, tok), json_tok_full_len(tok)); -} - -void json_add_errcode(struct json_stream *result, const char *fieldname, - errcode_t code) -{ - json_add_member(result, fieldname, false, "%"PRIerrcode, code); -} - -void json_add_invstring(struct json_stream *result, const char *invstring) -{ - if (strstarts(invstring, "lni")) - json_add_string(result, "bolt12", invstring); - else - json_add_string(result, "bolt11", invstring); -} diff --git a/common/json.h b/common/json.h deleted file mode 100644 index bdd867f5b46b..000000000000 --- a/common/json.h +++ /dev/null @@ -1,259 +0,0 @@ -#ifndef LIGHTNING_COMMON_JSON_H -#define LIGHTNING_COMMON_JSON_H -#include "config.h" -#include -#include -#include - -#define JSMN_STRICT 1 -# include - -struct json_escape; -struct json_stream; -struct timeabs; -struct timespec; - -/* Include " if it's a string. */ -const char *json_tok_full(const char *buffer, const jsmntok_t *t); - -/* Include " if it's a string. */ -int json_tok_full_len(const jsmntok_t *t); - -/* Is this a string equal to str? */ -bool json_tok_streq(const char *buffer, const jsmntok_t *tok, const char *str); - -/* Is this a string equal to str of length len? */ -bool json_tok_strneq(const char *buffer, const jsmntok_t *tok, - const char *str, size_t len); - -/* Does this string token start with prefix? */ -bool json_tok_startswith(const char *buffer, const jsmntok_t *tok, - const char *prefix); - -/* Does this string token end with suffix? */ -bool json_tok_endswith(const char *buffer, const jsmntok_t *tok, - const char *suffix); - -/* Allocate a tal string copy */ -char *json_strdup(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); - -/* Decode a hex-encoded binary */ -u8 *json_tok_bin_from_hex(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); - -/* Extract number from this (may be a string, or a number literal) */ -bool json_to_number(const char *buffer, const jsmntok_t *tok, - unsigned int *num); - -/* Extract number from this (may be a string, or a number literal) */ -bool json_to_u64(const char *buffer, const jsmntok_t *tok, - uint64_t *num); - -/* Extract signed 64 bit integer from this (may be a string, or a number literal) */ -bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num); - -/* Extract number from this (may be a string, or a number literal) */ -bool json_to_u32(const char *buffer, const jsmntok_t *tok, - uint32_t *num); - -/* Extract number from this (may be a string, or a number literal) */ -bool json_to_u16(const char *buffer, const jsmntok_t *tok, - uint16_t *num); - -bool json_to_sha256(const char *buffer, const jsmntok_t *tok, struct sha256 *dest); -/* - * Extract a non-negative (either 0 or positive) floating-point number from this - * (must be a number literal), multiply it by 1 million and return it as an - * integer. Any fraction smaller than 0.000001 is ignored. - */ -bool json_to_millionths(const char *buffer, const jsmntok_t *tok, - u64 *millionths); - -/* Extract signed integer from this (may be a string, or a number literal) */ -bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num); - -/* Extract an error code from this (may be a string, or a number literal) */ -bool json_to_errcode(const char *buffer, const jsmntok_t *tok, errcode_t *errcode); - -/* Extract boolean from this */ -bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b); - -/* Is this a number? [0..9]+ */ -bool json_tok_is_num(const char *buffer, const jsmntok_t *tok); - -/* Is this the null primitive? */ -bool json_tok_is_null(const char *buffer, const jsmntok_t *tok); - -/* Returns next token with same parent (WARNING: slow!). */ -const jsmntok_t *json_next(const jsmntok_t *tok); - -/* Get top-level member. */ -const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], - const char *label); - -/* Get index'th array member. */ -const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index); - -/* Allocate a starter array of tokens for json_parse_input */ -jsmntok_t *toks_alloc(const tal_t *ctx); - -/* Reset a token array to reuse it. */ -void toks_reset(jsmntok_t *toks); - -/** - * json_parse_input: parse and validate JSON. - * @parser: parser initialized with jsmn_init. - * @toks: tallocated array from toks_alloc() - * @input, @len: input string. - * @complete: set to true if the valid JSON is complete, or NULL if must be. - * - * This returns false if the JSON is invalid, true otherwise. - * If it returns true, *@complete indicates that (*@toks)[0] points to a - * valid, complete JSON element. If @complete is NULL, then incomplete - * JSON returns false (i.e. is considered invalid). - * - * *@toks is resized to the complete set of tokens, with a dummy - * terminator (type == -1) at the end. - * - * If it returns true, and *@complete is false, you can append more - * data to @input and call it again (with the same perser) and the parser - * will continue where it left off. -*/ -bool json_parse_input(jsmn_parser *parser, - jsmntok_t **toks, - const char *input, int len, - bool *complete); - -/* Simplified version of above which parses only a complete, valid - * JSON string */ -jsmntok_t *json_parse_simple(const tal_t *ctx, const char *input, int len); - -/* Convert a jsmntype_t enum to a human readable string. */ -const char *jsmntype_to_string(jsmntype_t t); - -/* Return a copy of a json value as an array. */ -jsmntok_t *json_tok_copy(const tal_t *ctx, const jsmntok_t *tok); - -/* - * Remove @num json values from a json array or object @obj. @tok points - * to the first value to remove. The array @tokens will be resized. - */ -void json_tok_remove(jsmntok_t **tokens, - jsmntok_t *obj_or_array, const jsmntok_t *tok, size_t num); - -/* Guide is % for a token: each must be followed by JSON_SCAN(). - * Returns NULL on error (asserts() on bad guide). */ -const char *json_scan(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok, - const char *guide, - ...); - -/* eg. JSON_SCAN(json_to_bool, &boolvar) */ -#define JSON_SCAN(fmt, var) \ - json_scan, \ - stringify(fmt), \ - ((var) + 0*sizeof(fmt((const char *)NULL, \ - (const jsmntok_t *)NULL, var) == true)), \ - (fmt) - -/* eg. JSON_SCAN_TAL(tmpctx, json_strdup, &charvar) */ -#define JSON_SCAN_TAL(ctx, fmt, var) \ - (ctx), \ - stringify(fmt), \ - ((var) + 0*sizeof((*var) = fmt((ctx), \ - (const char *)NULL, \ - (const jsmntok_t *)NULL))), \ - (fmt) - -/* Already-have-varargs version */ -const char *json_scanv(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok, - const char *guide, - va_list ap); - -/* Iterator macro for array: i is counter, t is token ptr, arr is JSMN_ARRAY */ -#define json_for_each_arr(i, t, arr) \ - for (i = 0, t = (arr) + 1; i < (arr)->size; t = json_next(t), i++) - -/* Iterator macro for object: i is counter, t is token ptr (t+1 is - * contents of obj member), obj is JSMN_OBJECT */ -#define json_for_each_obj(i, t, obj) \ - for (i = 0, t = (obj) + 1; i < (obj)->size; t = json_next(t+1), i++) - - -/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns - * any non-printable chars into JSON escapes, but leaves existing escapes alone. - */ -void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES); - -/* '"fieldname" : "value[:value_len]"' or '"value[:value_len]"' if - * fieldname is NULL. Turns any non-printable chars into JSON - * escapes, but leaves existing escapes alone. - */ -void json_add_stringn(struct json_stream *result, const char *fieldname, - const char *value TAKES, size_t value_len); - -/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. String must - * already be JSON escaped as necessary. */ -void json_add_escaped_string(struct json_stream *result, - const char *fieldname, - const struct json_escape *esc TAKES); - -/* '"fieldname" : literal' or 'literal' if fieldname is NULL*/ -void json_add_literal(struct json_stream *result, const char *fieldname, - const char *literal, int len); -/* '"fieldname" : value' or 'value' if fieldname is NULL */ -void json_add_num(struct json_stream *result, const char *fieldname, - unsigned int value); -/* '"fieldname" : value' or 'value' if fieldname is NULL */ -void json_add_u64(struct json_stream *result, const char *fieldname, - uint64_t value); -/* '"fieldname" : value' or 'value' if fieldname is NULL */ -void json_add_s64(struct json_stream *result, const char *fieldname, - int64_t value); -/* '"fieldname" : value' or 'value' if fieldname is NULL */ -void json_add_u32(struct json_stream *result, const char *fieldname, - uint32_t value); -/* '"fieldname" : value' or 'value' if fieldname is NULL */ -void json_add_s32(struct json_stream *result, const char *fieldname, - int32_t value); -/* '"fieldname" : true|false' or 'true|false' if fieldname is NULL */ -void json_add_bool(struct json_stream *result, const char *fieldname, - bool value); - -/* '"fieldname" : null' or 'null' if fieldname is NULL */ -void json_add_null(struct json_stream *stream, const char *fieldname); - -/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */ -void json_add_hex(struct json_stream *result, const char *fieldname, - const void *data, size_t len); -/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */ -void json_add_hex_talarr(struct json_stream *result, - const char *fieldname, - const tal_t *data); - -void json_add_timeabs(struct json_stream *result, const char *fieldname, - struct timeabs t); - -/* used in log.c and notification.c*/ -void json_add_time(struct json_stream *result, const char *fieldname, - struct timespec ts); - -/* Add ISO_8601 timestamp string, i.e. "2019-09-07T15:50+01:00" */ -void json_add_timeiso(struct json_stream *result, - const char *fieldname, - struct timeabs *time); - -/* Add any json token */ -void json_add_tok(struct json_stream *result, const char *fieldname, - const jsmntok_t *tok, const char *buffer); - -/* Add an error code */ -void json_add_errcode(struct json_stream *result, const char *fieldname, - errcode_t code); - -/* Add "bolt11" or "bolt12" field, depending on invstring. */ -void json_add_invstring(struct json_stream *result, const char *invstring); - -#endif /* LIGHTNING_COMMON_JSON_H */ diff --git a/common/json_command.h b/common/json_command.h index 5c44f1c8e099..88cf14aeb9d4 100644 --- a/common/json_command.h +++ b/common/json_command.h @@ -4,7 +4,7 @@ #define LIGHTNING_COMMON_JSON_COMMAND_H #include "config.h" #include -#include +#include #include struct command; diff --git a/common/json_helpers.c b/common/json_helpers.c deleted file mode 100644 index 569b2c1077cf..000000000000 --- a/common/json_helpers.c +++ /dev/null @@ -1,495 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, - uint64_t *satoshi) -{ - char *end; - unsigned long btc, sat; - - btc = strtoul(buffer + tok->start, &end, 10); - if (btc == ULONG_MAX && errno == ERANGE) - return false; - if (end != buffer + tok->end) { - /* Expect always 8 decimal places. */ - if (*end != '.' || buffer + tok->end - end != 9) - return false; - sat = strtoul(end+1, &end, 10); - if (sat == ULONG_MAX && errno == ERANGE) - return false; - if (end != buffer + tok->end) - return false; - } else - sat = 0; - - *satoshi = btc * (uint64_t)100000000 + sat; - if (*satoshi != btc * (uint64_t)100000000 + sat) - return false; - - return true; -} - -bool json_to_node_id(const char *buffer, const jsmntok_t *tok, - struct node_id *id) -{ - return node_id_from_hexstr(buffer + tok->start, - tok->end - tok->start, id); -} - -bool json_to_pubkey(const char *buffer, const jsmntok_t *tok, - struct pubkey *pubkey) -{ - return pubkey_from_hexstr(buffer + tok->start, - tok->end - tok->start, pubkey); -} - -bool json_to_msat(const char *buffer, const jsmntok_t *tok, - struct amount_msat *msat) -{ - return parse_amount_msat(msat, - buffer + tok->start, tok->end - tok->start); -} - -bool json_to_sat(const char *buffer, const jsmntok_t *tok, - struct amount_sat *sat) -{ - return parse_amount_sat(sat, buffer + tok->start, tok->end - tok->start); -} - -bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok, - struct amount_sat *sat) -{ - if (json_tok_streq(buffer, tok, "all")) { - *sat = AMOUNT_SAT(-1ULL); - return true; - } - return json_to_sat(buffer, tok, sat); -} - -bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, - struct short_channel_id *scid) -{ - return (short_channel_id_from_str(buffer + tok->start, - tok->end - tok->start, scid)); -} - -bool json_to_txid(const char *buffer, const jsmntok_t *tok, - struct bitcoin_txid *txid) -{ - return bitcoin_txid_from_hex(buffer + tok->start, - tok->end - tok->start, txid); -} - -bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, - struct bitcoin_outpoint *op) -{ - jsmntok_t t1, t2; - - if (!split_tok(buffer, tok, ':', &t1, &t2)) - return false; - - return json_to_txid(buffer, &t1, &op->txid) - && json_to_u32(buffer, &t2, &op->n); -} - -bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, - struct channel_id *cid) -{ - return hex_decode(buffer + tok->start, tok->end - tok->start, - cid, sizeof(*cid)); -} - - -bool json_to_coin_mvt_tag(const char *buffer, const jsmntok_t *tok, - enum mvt_tag *tag) -{ - enum mvt_tag i_tag; - for (size_t i = 0; i < NUM_MVT_TAGS; i++) { - i_tag = (enum mvt_tag) i; - if (json_tok_streq(buffer, tok, mvt_tag_str(i_tag))) { - *tag = i_tag; - return true; - } - } - - return false; -} - -bool split_tok(const char *buffer, const jsmntok_t *tok, - char split, - jsmntok_t *a, - jsmntok_t *b) -{ - const char *p = memchr(buffer + tok->start, split, tok->end - tok->start); - if (!p) - return false; - - *a = *b = *tok; - a->end = p - buffer; - b->start = p + 1 - buffer; - - return true; -} - -bool json_to_secret(const char *buffer, const jsmntok_t *tok, struct secret *dest) -{ - return hex_decode(buffer + tok->start, tok->end - tok->start, - dest->data, sizeof(struct secret)); -} - -bool json_to_preimage(const char *buffer, const jsmntok_t *tok, struct preimage *preimage) -{ - size_t hexlen = tok->end - tok->start; - return hex_decode(buffer + tok->start, hexlen, preimage->r, sizeof(preimage->r)); -} - -struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, - const jsmntok_t *tok) -{ - return psbt_from_b64(ctx, buffer + tok->start, tok->end - tok->start); -} - -struct tlv_obs2_onionmsg_payload_reply_path * -json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) -{ - struct tlv_obs2_onionmsg_payload_reply_path *rpath; - const jsmntok_t *hops, *t; - size_t i; - const char *err; - - rpath = tal(ctx, struct tlv_obs2_onionmsg_payload_reply_path); - err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", - JSON_SCAN(json_to_pubkey, &rpath->blinding), - JSON_SCAN(json_to_pubkey, &rpath->first_node_id), - NULL); - if (err) - return tal_free(rpath); - - hops = json_get_member(buffer, tok, "hops"); - if (!hops || hops->size < 1) - return tal_free(rpath); - - rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); - json_for_each_arr(i, t, hops) { - rpath->path[i] = tal(rpath->path, struct onionmsg_path); - err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", - JSON_SCAN(json_to_pubkey, - &rpath->path[i]->node_id), - JSON_SCAN_TAL(rpath->path[i], - json_tok_bin_from_hex, - &rpath->path[i]->encrypted_recipient_data)); - if (err) - return tal_free(rpath); - } - - return rpath; -} - -struct tlv_onionmsg_payload_reply_path * -json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) -{ - struct tlv_onionmsg_payload_reply_path *rpath; - const jsmntok_t *hops, *t; - size_t i; - const char *err; - - rpath = tal(ctx, struct tlv_onionmsg_payload_reply_path); - err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", - JSON_SCAN(json_to_pubkey, &rpath->blinding), - JSON_SCAN(json_to_pubkey, &rpath->first_node_id), - NULL); - if (err) - return tal_free(rpath); - - hops = json_get_member(buffer, tok, "hops"); - if (!hops || hops->size < 1) - return tal_free(rpath); - - rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); - json_for_each_arr(i, t, hops) { - rpath->path[i] = tal(rpath->path, struct onionmsg_path); - err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", - JSON_SCAN(json_to_pubkey, - &rpath->path[i]->node_id), - JSON_SCAN_TAL(rpath->path[i], - json_tok_bin_from_hex, - &rpath->path[i]->encrypted_recipient_data)); - if (err) - return tal_free(rpath); - } - - return rpath; -} - -void json_add_node_id(struct json_stream *response, - const char *fieldname, - const struct node_id *id) -{ - json_add_hex(response, fieldname, id->k, sizeof(id->k)); -} - -void json_add_channel_id(struct json_stream *response, - const char *fieldname, - const struct channel_id *cid) -{ - json_add_hex(response, fieldname, cid->id, sizeof(cid->id)); -} - -void json_add_pubkey(struct json_stream *response, - const char *fieldname, - const struct pubkey *key) -{ - u8 der[PUBKEY_CMPR_LEN]; - - pubkey_to_der(der, key); - json_add_hex(response, fieldname, der, sizeof(der)); -} - -void json_add_point32(struct json_stream *response, - const char *fieldname, - const struct point32 *key) -{ - u8 output[32]; - - secp256k1_xonly_pubkey_serialize(secp256k1_ctx, output, &key->pubkey); - json_add_hex(response, fieldname, output, sizeof(output)); -} - -void json_add_bip340sig(struct json_stream *response, - const char *fieldname, - const struct bip340sig *sig) -{ - json_add_hex(response, fieldname, sig->u8, sizeof(sig->u8)); -} - -void json_add_txid(struct json_stream *result, const char *fieldname, - const struct bitcoin_txid *txid) -{ - char hex[hex_str_size(sizeof(*txid))]; - - bitcoin_txid_to_hex(txid, hex, sizeof(hex)); - json_add_string(result, fieldname, hex); -} - -void json_add_outpoint(struct json_stream *result, const char *fieldname, - const struct bitcoin_outpoint *out) -{ - char hex[hex_str_size(sizeof(out->txid))]; - bitcoin_txid_to_hex(&out->txid, hex, sizeof(hex)); - json_add_member(result, fieldname, true, "%s:%d", hex, out->n); -} - -void json_add_short_channel_id(struct json_stream *response, - const char *fieldname, - const struct short_channel_id *scid) -{ - json_add_member(response, fieldname, true, "%dx%dx%d", - short_channel_id_blocknum(scid), - short_channel_id_txnum(scid), - short_channel_id_outnum(scid)); -} - -void json_add_address(struct json_stream *response, const char *fieldname, - const struct wireaddr *addr) -{ - json_object_start(response, fieldname); - if (addr->type == ADDR_TYPE_IPV4) { - char addrstr[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, addr->addr, addrstr, INET_ADDRSTRLEN); - json_add_string(response, "type", "ipv4"); - json_add_string(response, "address", addrstr); - json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_IPV6) { - char addrstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, addr->addr, addrstr, INET6_ADDRSTRLEN); - json_add_string(response, "type", "ipv6"); - json_add_string(response, "address", addrstr); - json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_TOR_V2_REMOVED) { - json_add_string(response, "type", "torv2"); - json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); - json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_TOR_V3) { - json_add_string(response, "type", "torv3"); - json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); - json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_DNS) { - json_add_string(response, "type", "dns"); - json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); - json_add_num(response, "port", addr->port); - } else if (addr->type == ADDR_TYPE_WEBSOCKET) { - json_add_string(response, "type", "websocket"); - json_add_num(response, "port", addr->port); - } - json_object_end(response); -} - -void json_add_address_internal(struct json_stream *response, - const char *fieldname, - const struct wireaddr_internal *addr) -{ - switch (addr->itype) { - case ADDR_INTERNAL_SOCKNAME: - json_object_start(response, fieldname); - json_add_string(response, "type", "local socket"); - json_add_string(response, "socket", addr->u.sockname); - json_object_end(response); - return; - case ADDR_INTERNAL_ALLPROTO: - json_object_start(response, fieldname); - json_add_string(response, "type", "any protocol"); - json_add_num(response, "port", addr->u.port); - json_object_end(response); - return; - case ADDR_INTERNAL_AUTOTOR: - json_object_start(response, fieldname); - json_add_string(response, "type", "Tor generated address"); - json_add_address(response, "service", &addr->u.torservice.address); - json_object_end(response); - return; - case ADDR_INTERNAL_STATICTOR: - json_object_start(response, fieldname); - json_add_string(response, "type", "Tor from blob generated static address"); - json_add_address(response, "service", &addr->u.torservice.address); - json_object_end(response); - return; - case ADDR_INTERNAL_FORPROXY: - json_object_start(response, fieldname); - json_add_string(response, "type", "unresolved"); - json_add_string(response, "name", addr->u.unresolved.name); - json_add_num(response, "port", addr->u.unresolved.port); - json_object_end(response); - return; - case ADDR_INTERNAL_WIREADDR: - json_add_address(response, fieldname, &addr->u.wireaddr); - return; - } - abort(); -} - -void json_add_tx(struct json_stream *result, - const char *fieldname, - const struct bitcoin_tx *tx) -{ - json_add_hex_talarr(result, fieldname, linearize_tx(tmpctx, tx)); -} - -void json_add_psbt(struct json_stream *stream, - const char *fieldname, - const struct wally_psbt *psbt TAKES) -{ - const char *psbt_b64; - psbt_b64 = psbt_to_b64(NULL, psbt); - json_add_string(stream, fieldname, take(psbt_b64)); - if (taken(psbt)) - tal_free(psbt); -} - -void json_add_amount_msat_compat(struct json_stream *result, - struct amount_msat msat, - const char *rawfieldname, - const char *msatfieldname) -{ - if (deprecated_apis) - json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */ - json_add_amount_msat_only(result, msatfieldname, msat); -} - -void json_add_amount_msat_only(struct json_stream *result, - const char *msatfieldname, - struct amount_msat msat) -{ - if (!deprecated_apis) - assert(strends(msatfieldname, "_msat")); - if (deprecated_apis) - json_add_string(result, msatfieldname, - type_to_string(tmpctx, struct amount_msat, &msat)); - else - json_add_u64(result, msatfieldname, msat.millisatoshis); /* Raw: low-level helper */ -} - -void json_add_amount_sat_compat(struct json_stream *result, - struct amount_sat sat, - const char *rawfieldname, - const char *msatfieldname) -{ - if (deprecated_apis) - json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */ - json_add_amount_sat_msat(result, msatfieldname, sat); -} - -void json_add_amount_sat_msat(struct json_stream *result, - const char *msatfieldname, - struct amount_sat sat) -{ - struct amount_msat msat; - assert(strends(msatfieldname, "_msat")); - if (amount_sat_to_msat(&msat, sat)) - json_add_amount_msat_only(result, msatfieldname, msat); -} - -/* When I noticed that we were adding "XXXmsat" fields *not* ending in _msat */ -void json_add_amount_sats_deprecated(struct json_stream *result, - const char *fieldname, - const char *msatfieldname, - struct amount_sat sat) -{ - if (deprecated_apis) { - struct amount_msat msat; - assert(!strends(fieldname, "_msat")); - if (amount_sat_to_msat(&msat, sat)) - json_add_string(result, fieldname, - take(fmt_amount_msat(NULL, msat))); - } - json_add_amount_sat_msat(result, msatfieldname, sat); -} - -void json_add_sats(struct json_stream *result, - const char *fieldname, - struct amount_sat sat) -{ - json_add_string(result, fieldname, take(fmt_amount_sat(NULL, sat))); -} - -void json_add_secret(struct json_stream *response, const char *fieldname, - const struct secret *secret) -{ - json_add_hex(response, fieldname, secret, sizeof(struct secret)); -} - -void json_add_sha256(struct json_stream *result, const char *fieldname, - const struct sha256 *hash) -{ - json_add_hex(result, fieldname, hash, sizeof(*hash)); -} - -void json_add_preimage(struct json_stream *result, const char *fieldname, - const struct preimage *preimage) -{ - json_add_hex(result, fieldname, preimage, sizeof(*preimage)); -} - -void json_add_lease_rates(struct json_stream *result, - const struct lease_rates *rates) -{ - json_add_amount_sat_msat(result, "lease_fee_base_msat", - amount_sat(rates->lease_fee_base_sat)); - json_add_num(result, "lease_fee_basis", rates->lease_fee_basis); - json_add_num(result, "funding_weight", rates->funding_weight); - json_add_amount_msat_only(result, - "channel_fee_max_base_msat", - amount_msat(rates->channel_fee_max_base_msat)); - json_add_num(result, "channel_fee_max_proportional_thousandths", - rates->channel_fee_max_proportional_thousandths); -} diff --git a/common/json_helpers.h b/common/json_helpers.h deleted file mode 100644 index 9a6673db0b82..000000000000 --- a/common/json_helpers.h +++ /dev/null @@ -1,208 +0,0 @@ -/* More specialized (bitcoin, lightning-specific) JSON helpers. */ -#ifndef LIGHTNING_COMMON_JSON_HELPERS_H -#define LIGHTNING_COMMON_JSON_HELPERS_H -#include "config.h" -#include -#include -#include -#include - -struct amount_msat; -struct amount_sat; -struct bip340sig; -struct channel_id; -struct lease_rates; -struct node_id; -struct preimage; -struct pubkey; -struct point32; -struct secret; -struct short_channel_id; -struct short_channel_id_dir; -struct wireaddr; -struct wireaddr_internal; -struct wally_psbt; - -/* Decode a hex-encoded payment preimage */ -bool json_to_preimage(const char *buffer, const jsmntok_t *tok, struct preimage *preimage); - -/* Extract a secret from this. */ -bool json_to_secret(const char *buffer, const jsmntok_t *tok, struct secret *dest); - -/* Extract a psbt from this. */ -struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, - const jsmntok_t *tok); - -/* Extract a pubkey from this */ -bool json_to_pubkey(const char *buffer, const jsmntok_t *tok, - struct pubkey *pubkey); - -/* Extract node_id from this: makes sure *id is valid! */ -bool json_to_node_id(const char *buffer, const jsmntok_t *tok, - struct node_id *id); - -/* Extract satoshis from this (may be a string, or a decimal number literal) */ -bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, - uint64_t *satoshi); - -/* Extract a short_channel_id from this */ -bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, - struct short_channel_id *scid); - -/* Extract a satoshis amount from this */ -bool json_to_sat(const char *buffer, const jsmntok_t *tok, - struct amount_sat *sat); - -/* Extract a satoshis amount from this */ -/* If the string is "all", set amonut as AMOUNT_SAT(-1ULL). */ -bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok, - struct amount_sat *sat); - -/* Extract a millisatoshis amount from this */ -bool json_to_msat(const char *buffer, const jsmntok_t *tok, - struct amount_msat *msat); - -/* Extract a bitcoin txid from this */ -bool json_to_txid(const char *buffer, const jsmntok_t *tok, - struct bitcoin_txid *txid); - -/* Extract a bitcoin outpoint from this */ -bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, - struct bitcoin_outpoint *op); - -/* Extract a channel id from this */ -bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, - struct channel_id *cid); - -/* Extract a coin movement 'tag' from this */ -bool json_to_coin_mvt_tag(const char *buffer, const jsmntok_t *tok, - enum mvt_tag *tag); - -/* Split a json token into 2 tokens given a splitting character */ -bool split_tok(const char *buffer, const jsmntok_t *tok, - char split, - jsmntok_t *a, - jsmntok_t *b); - -/* Extract reply path from this JSON */ -struct tlv_onionmsg_payload_reply_path * -json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); - -/* Obsolete version! */ -struct tlv_obs2_onionmsg_payload_reply_path * -json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); - -/* Helpers for outputting JSON results */ - -/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ -void json_add_pubkey(struct json_stream *response, - const char *fieldname, - const struct pubkey *key); - -/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */ -void json_add_point32(struct json_stream *response, - const char *fieldname, - const struct point32 *key); - -/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */ -void json_add_bip340sig(struct json_stream *response, - const char *fieldname, - const struct bip340sig *sig); - -/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */ -void json_add_secret(struct json_stream *response, - const char *fieldname, - const struct secret *secret); - -/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ -void json_add_node_id(struct json_stream *response, - const char *fieldname, - const struct node_id *id); - -/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ -void json_add_channel_id(struct json_stream *response, - const char *fieldname, - const struct channel_id *cid); - -/* '"fieldname" : ' or "" if fieldname is NULL */ -void json_add_txid(struct json_stream *result, const char *fieldname, - const struct bitcoin_txid *txid); - -/* '"fieldname" : "txid:n" */ -void json_add_outpoint(struct json_stream *result, const char *fieldname, - const struct bitcoin_outpoint *out); - -/* '"fieldname" : "1234:5:6"' */ -void json_add_short_channel_id(struct json_stream *response, - const char *fieldname, - const struct short_channel_id *id); - -/* JSON serialize a network address for a node */ -void json_add_address(struct json_stream *response, const char *fieldname, - const struct wireaddr *addr); - -/* JSON serialize a network address for a node. */ -void json_add_address_internal(struct json_stream *response, - const char *fieldname, - const struct wireaddr_internal *addr); - -/* Adds both a 'raw' number field and an 'amount_msat' field */ -void json_add_amount_msat_compat(struct json_stream *result, - struct amount_msat msat, - const char *rawfieldname, - const char *msatfieldname) - NO_NULL_ARGS; - -/* Adds both a 'raw' number field and an 'amount_msat' field */ -void json_add_amount_sat_compat(struct json_stream *result, - struct amount_sat sat, - const char *rawfieldname, - const char *msatfieldname) - NO_NULL_ARGS; - -/* Adds an 'msat' field */ -void json_add_amount_msat_only(struct json_stream *result, - const char *msatfieldname, - struct amount_msat msat) - NO_NULL_ARGS; - -/* Adds an 'msat' field */ -void json_add_amount_sat_msat(struct json_stream *result, - const char *msatfieldname, - struct amount_sat sat) - NO_NULL_ARGS; - -/* Adds an 'msat' field, and an older deprecated field. */ -void json_add_amount_sats_deprecated(struct json_stream *result, - const char *fieldname, - const char *msatfieldname, - struct amount_sat sat) - NO_NULL_ARGS; - -/* This is used to create requests, *never* for output (output is always - * msat!) */ -void json_add_sats(struct json_stream *result, - const char *fieldname, - struct amount_sat sat); - -void json_add_sha256(struct json_stream *result, const char *fieldname, - const struct sha256 *hash); - -void json_add_preimage(struct json_stream *result, const char *fieldname, - const struct preimage *preimage); - -/* '"fieldname" : "010000000001..."' or "010000000001..." if fieldname is NULL */ -void json_add_tx(struct json_stream *result, - const char *fieldname, - const struct bitcoin_tx *tx); - -/* '"fieldname" : "cHNidP8BAJoCAAAAAljo..." or "cHNidP8BAJoCAAAAAljo..." if fieldname is NULL */ -void json_add_psbt(struct json_stream *stream, - const char *fieldname, - const struct wally_psbt *psbt); - -/* Add fields from the lease_rates to a json stream. - * Note that field names are set */ -void json_add_lease_rates(struct json_stream *result, - const struct lease_rates *rates); -#endif /* LIGHTNING_COMMON_JSON_HELPERS_H */ diff --git a/common/json_tok.c b/common/json_param.c similarity index 69% rename from common/json_tok.c rename to common/json_param.c index 29a75884e02b..bf4ddc92ee33 100644 --- a/common/json_tok.c +++ b/common/json_param.c @@ -4,15 +4,378 @@ #include #include #include +#include #include +#include #include #include #include +#include #include -#include -#include +#include #include +struct param { + const char *name; + bool is_set; + enum param_style style; + param_cbx cbx; + void *arg; +}; + +static bool param_add(struct param **params, + const char *name, + enum param_style style, + param_cbx cbx, void *arg) +{ +#if DEVELOPER + if (!(name && cbx && arg)) + return false; +#endif + struct param last; + + last.is_set = false; + last.name = name; + last.style = style; + last.cbx = cbx; + last.arg = arg; + + tal_arr_expand(params, last); + return true; +} + +/* FIXME: To support the deprecated p_req_dup_ok */ +static bool is_required(enum param_style style) +{ + return style == PARAM_REQUIRED || style == PARAM_REQUIRED_ALLOW_DUPS; +} + +static struct command_result *make_callback(struct command *cmd, + struct param *def, + const char *buffer, + const jsmntok_t *tok) +{ + /* If it had a default, free that now to avoid leak */ + if (def->style == PARAM_OPTIONAL_WITH_DEFAULT && !def->is_set) + tal_free(*(void **)def->arg); + + def->is_set = true; + + return def->cbx(cmd, def->name, buffer, tok, def->arg); +} + +static struct command_result *post_check(struct command *cmd, + struct param *params) +{ + struct param *first = params; + struct param *last = first + tal_count(params); + + /* Make sure required params were provided. */ + while (first != last && is_required(first->style)) { + if (!first->is_set) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "missing required parameter: %s", + first->name); + } + first++; + } + return NULL; +} + +static struct command_result *parse_by_position(struct command *cmd, + struct param *params, + const char *buffer, + const jsmntok_t tokens[], + bool allow_extra) +{ + struct command_result *res; + const jsmntok_t *tok; + size_t i; + + json_for_each_arr(i, tok, tokens) { + /* check for unexpected trailing params */ + if (i == tal_count(params)) { + if (!allow_extra) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "too many parameters:" + " got %u, expected %zu", + tokens->size, + tal_count(params)); + } + break; + } + + if (!json_tok_is_null(buffer, tok)) { + res = make_callback(cmd, params+i, buffer, tok); + if (res) + return res; + } + } + + return post_check(cmd, params); +} + +static struct param *find_param(struct param *params, const char *start, + size_t n) +{ + struct param *first = params; + struct param *last = first + tal_count(params); + + while (first != last) { + size_t arglen = strcspn(first->name, "|"); + if (memeq(first->name, arglen, start, n)) + return first; + if (deprecated_apis + && first->name[arglen] + && memeq(first->name + arglen + 1, + strlen(first->name + arglen + 1), + start, n)) + return first; + first++; + } + return NULL; +} + +static struct command_result *parse_by_name(struct command *cmd, + struct param *params, + const char *buffer, + const jsmntok_t tokens[], + bool allow_extra) +{ + size_t i; + const jsmntok_t *t; + + json_for_each_obj(i, t, tokens) { + struct param *p = find_param(params, buffer + t->start, + t->end - t->start); + if (!p) { + if (!allow_extra) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "unknown parameter: %.*s, this may be caused by a failure to autodetect key=value-style parameters. Please try using the -k flag and explicit key=value pairs of parameters.", + t->end - t->start, + buffer + t->start); + } + } else { + struct command_result *res; + + if (p->is_set) { + if (p->style == PARAM_REQUIRED_ALLOW_DUPS) + continue; + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "duplicate json names: %s", + p->name); + } + + res = make_callback(cmd, p, buffer, t + 1); + if (res) + return res; + } + } + return post_check(cmd, params); +} + +#if DEVELOPER +static int comp_by_name(const struct param *a, const struct param *b, + void *unused) +{ + return strcmp(a->name, b->name); +} + +static int comp_by_arg(const struct param *a, const struct param *b, + void *unused) +{ + /* size_t could be larger than int: don't turn a 4bn difference into 0 */ + if (a->arg > b->arg) + return 1; + else if (a->arg < b->arg) + return -1; + return 0; +} + +/* This comparator is a bit different, but works well. + * Return 0 if @a is optional and @b is required. Otherwise return 1. + */ +static int comp_req_order(const struct param *a, const struct param *b, + void *unused) +{ + if (!is_required(a->style) && is_required(b->style)) + return 0; + return 1; +} + +/* + * Make sure 2 sequential items in @params are not equal (based on + * provided comparator). + */ +static bool check_distinct(struct param *params, + int (*compar) (const struct param *a, + const struct param *b, void *unused)) +{ + struct param *first = params; + struct param *last = first + tal_count(params); + first++; + while (first != last) { + if (compar(first - 1, first, NULL) == 0) + return false; + first++; + } + return true; +} + +static bool check_unique(struct param *copy, + int (*compar) (const struct param *a, + const struct param *b, void *unused)) +{ + asort(copy, tal_count(copy), compar, NULL); + return check_distinct(copy, compar); +} + +/* + * Verify consistent internal state. + */ +static bool check_params(struct param *params) +{ + if (tal_count(params) < 2) + return true; + + /* make sure there are no required params following optional */ + if (!check_distinct(params, comp_req_order)) + return false; + + /* duplicate so we can sort */ + struct param *copy = tal_dup_talarr(params, struct param, params); + + /* check for repeated names and args */ + if (!check_unique(copy, comp_by_name)) + return false; + if (!check_unique(copy, comp_by_arg)) + return false; + + tal_free(copy); + return true; +} +#endif + +static char *param_usage(const tal_t *ctx, + const struct param *params) +{ + char *usage = tal_strdup(ctx, ""); + for (size_t i = 0; i < tal_count(params); i++) { + /* Don't print |deprecated part! */ + int len = strcspn(params[i].name, "|"); + if (i != 0) + tal_append_fmt(&usage, " "); + if (is_required(params[i].style)) + tal_append_fmt(&usage, "%.*s", len, params[i].name); + else + tal_append_fmt(&usage, "[%.*s]", len, params[i].name); + } + return usage; +} + +static struct command_result *param_arr(struct command *cmd, const char *buffer, + const jsmntok_t tokens[], + struct param *params, + bool allow_extra) +{ +#if DEVELOPER + if (!check_params(params)) { + return command_fail(cmd, PARAM_DEV_ERROR, + "developer error: check_params"); + } +#endif + if (tokens->type == JSMN_ARRAY) + return parse_by_position(cmd, params, buffer, tokens, allow_extra); + else if (tokens->type == JSMN_OBJECT) + return parse_by_name(cmd, params, buffer, tokens, allow_extra); + + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Expected array or object for params"); +} + +const char *param_subcommand(struct command *cmd, const char *buffer, + const jsmntok_t tokens[], + const char *name, ...) +{ + va_list ap; + struct param *params = tal_arr(cmd, struct param, 0); + const char *arg, **names = tal_arr(tmpctx, const char *, 1); + const char *subcmd; + + param_add(¶ms, "subcommand", PARAM_REQUIRED, (void *)param_string, &subcmd); + names[0] = name; + va_start(ap, name); + while ((arg = va_arg(ap, const char *)) != NULL) + tal_arr_expand(&names, arg); + va_end(ap); + + if (command_usage_only(cmd)) { + char *usage = tal_strdup(cmd, "subcommand"); + for (size_t i = 0; i < tal_count(names); i++) + tal_append_fmt(&usage, "%c%s", + i == 0 ? '=' : '|', names[i]); + command_set_usage(cmd, usage); + return NULL; + } + + /* Check it's valid */ + if (param_arr(cmd, buffer, tokens, params, true) != NULL) { + return NULL; + } + + /* Check it's one of the known ones. */ + for (size_t i = 0; i < tal_count(names); i++) + if (streq(subcmd, names[i])) + return subcmd; + + /* We really do ignore this. */ + struct command_result *ignore; + ignore = command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Unknown subcommand '%s'", subcmd); + assert(ignore); + return NULL; +} + +bool param(struct command *cmd, const char *buffer, + const jsmntok_t tokens[], ...) +{ + struct param *params = tal_arr(tmpctx, struct param, 0); + const char *name; + va_list ap; + bool allow_extra = false; + + va_start(ap, tokens); + while ((name = va_arg(ap, const char *)) != NULL) { + enum param_style style = va_arg(ap, enum param_style); + param_cbx cbx = va_arg(ap, param_cbx); + void *arg = va_arg(ap, void *); + if (streq(name, "")) { + allow_extra = true; + continue; + } + if (!param_add(¶ms, name, style, cbx, arg)) { + /* We really do ignore this return! */ + struct command_result *ignore; + ignore = command_fail(cmd, PARAM_DEV_ERROR, + "developer error: param_add %s", name); + assert(ignore); + va_end(ap); + return false; + } + } + va_end(ap); + + if (command_usage_only(cmd)) { + command_set_usage(cmd, param_usage(cmd, params)); + return false; + } + + /* Always return false if we're simply checking command parameters; + * normally this returns true if all parameters are valid. */ + return param_arr(cmd, buffer, tokens, params, allow_extra) == NULL + && !command_check_only(cmd); +} + struct command_result *param_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, const jsmntok_t **arr) diff --git a/common/json_tok.h b/common/json_param.h similarity index 61% rename from common/json_tok.h rename to common/json_param.h index 40bd32060f52..c08e466ade63 100644 --- a/common/json_tok.h +++ b/common/json_param.h @@ -1,22 +1,152 @@ /* Helpers for use with param parsing. */ -#ifndef LIGHTNING_COMMON_JSON_TOK_H -#define LIGHTNING_COMMON_JSON_TOK_H +#ifndef LIGHTNING_COMMON_JSON_PARAM_H +#define LIGHTNING_COMMON_JSON_PARAM_H #include "config.h" #include #include -#include +#include #include #include #include #include +/*~ Greetings adventurer! + * + * Do you want to automatically validate json input and unmarshal it into + * local variables, all using typesafe callbacks? And on error, + * call command_fail with a proper error message? Then you've come to the + * right place! + * + * Here is a simple example of using the system: + * + * unsigned *cltv; + * u64 *msatoshi; + * const jsmntok_t *note; + * u64 *expiry; + * + * if (!param(cmd, buffer, params, + * p_req("cltv", json_tok_number, &cltv), + * p_opt("msatoshi", json_tok_u64, &msatoshi), + * p_opt("note", json_tok_tok, ¬e), + * p_opt_def("expiry", json_tok_u64, &expiry, 3600), + * NULL)) + * return; + * + * If param() returns true then you're good to go. + * + * All the command handlers throughout the code use this system. + * json_invoice() is a great example. The common callbacks can be found in + * common/json_tok.c. Use them directly or feel free to write your own. + */ +struct command; + +/* A dummy type returned by command_ functions, to ensure you return them + * immediately */ +struct command_result; + +/* + * Parse the json tokens. @params can be an array of values or an object + * of named values. + */ +bool param(struct command *cmd, const char *buffer, + const jsmntok_t params[], ...) LAST_ARG_NULL; + +/* + * The callback signature. + * + * Callbacks must return NULL on success. On failure they + * must return command_fail(...). + */ +typedef struct command_result *(*param_cbx)(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + void **arg); + +/** + * Parse the first json value. + * + * name...: NULL-terminated array of valid values. + * + * Returns subcommand: if it returns NULL if you should return + * command_param_failed() immediately. + */ +const char *param_subcommand(struct command *cmd, const char *buffer, + const jsmntok_t tokens[], + const char *name, ...) LAST_ARG_NULL; + +enum param_style { + PARAM_REQUIRED, + PARAM_REQUIRED_ALLOW_DUPS, + PARAM_OPTIONAL, + PARAM_OPTIONAL_WITH_DEFAULT, +}; + +/* + * Add a required parameter. + */ +#define p_req(name, cbx, arg) \ + name"", \ + PARAM_REQUIRED, \ + (param_cbx)(cbx), \ + (arg) + 0*sizeof((cbx)((struct command *)NULL, \ + (const char *)NULL, \ + (const char *)NULL, \ + (const jsmntok_t *)NULL, \ + (arg)) == (struct command_result *)NULL) + +/* + * Add an optional parameter. *arg is set to NULL if it isn't found. + */ +#define p_opt(name, cbx, arg) \ + name"", \ + PARAM_OPTIONAL, \ + (param_cbx)(cbx), \ + ({ *arg = NULL; \ + (arg) + 0*sizeof((cbx)((struct command *)NULL, \ + (const char *)NULL, \ + (const char *)NULL, \ + (const jsmntok_t *)NULL, \ + (arg)) == (struct command_result *)NULL); }) + +/* + * Add an required parameter, like p_req, but ignore duplicates. + */ +#define p_req_dup_ok(name, cbx, arg) \ + name"", \ + PARAM_REQUIRED_ALLOW_DUPS, \ + (param_cbx)(cbx), \ + ({ *arg = NULL; \ + (arg) + 0*sizeof((cbx)((struct command *)NULL, \ + (const char *)NULL, \ + (const char *)NULL, \ + (const jsmntok_t *)NULL, \ + (arg)) == (struct command_result *)NULL); }) + +/* + * Add an optional parameter. *arg is set to @def if it isn't found. + */ +#define p_opt_def(name, cbx, arg, def) \ + name"", \ + PARAM_OPTIONAL_WITH_DEFAULT, \ + (param_cbx)(cbx), \ + ({ (*arg) = tal((cmd), typeof(**arg)); \ + (**arg) = (def); \ + (arg) + 0*sizeof((cbx)((struct command *)NULL, \ + (const char *)NULL, \ + (const char *)NULL, \ + (const jsmntok_t *)NULL, \ + (arg)) == (struct command_result *)NULL); }) + +/* Special flag for 'check' which allows any parameters. */ +#define p_opt_any() "", PARAM_OPTIONAL, NULL, NULL + +/* All the helper routines. */ struct amount_msat; struct amount_sat; struct bitcoin_txid; struct bitcoin_outpoint; struct channel_id; -struct command; -struct command_result; struct json_escape; struct route_exclusion; struct sha256; @@ -213,4 +343,4 @@ struct command_result *param_lease_hex(struct command *cmd, const char *buffer, const jsmntok_t *tok, struct lease_rates **rates); -#endif /* LIGHTNING_COMMON_JSON_TOK_H */ +#endif /* LIGHTNING_COMMON_JSON_PARAM_H */ diff --git a/common/json_parse.c b/common/json_parse.c new file mode 100644 index 000000000000..74d46f21a6b5 --- /dev/null +++ b/common/json_parse.c @@ -0,0 +1,719 @@ +/* JSON core and helpers */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num) +{ + char *end; + long long l; + + l = strtoll(buffer + tok->start, &end, 0); + if (end != buffer + tok->end) + return false; + + BUILD_ASSERT(sizeof(l) >= sizeof(*num)); + *num = l; + + /* Check for overflow/underflow */ + if ((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) + return false; + + /* Check if the number did not fit in `s64` (in case `long long` + is a bigger type). */ + if (*num != l) + return false; + + return true; +} + +bool json_to_millionths(const char *buffer, const jsmntok_t *tok, + u64 *millionths) +{ + int decimal_places = -1; + bool has_digits = 0; + + *millionths = 0; + for (int i = tok->start; i < tok->end; i++) { + if (isdigit(buffer[i])) { + has_digits = true; + /* Ignore too much precision */ + if (decimal_places >= 0 && ++decimal_places > 6) + continue; + if (mul_overflows_u64(*millionths, 10)) + return false; + *millionths *= 10; + if (add_overflows_u64(*millionths, buffer[i] - '0')) + return false; + *millionths += buffer[i] - '0'; + } else if (buffer[i] == '.') { + if (decimal_places != -1) + return false; + decimal_places = 0; + } else + return false; + } + + if (!has_digits) + return false; + + if (decimal_places == -1) + decimal_places = 0; + + while (decimal_places < 6) { + if (mul_overflows_u64(*millionths, 10)) + return false; + *millionths *= 10; + decimal_places++; + } + return true; +} + +bool json_to_number(const char *buffer, const jsmntok_t *tok, + unsigned int *num) +{ + uint64_t u64; + + if (!json_to_u64(buffer, tok, &u64)) + return false; + *num = u64; + + /* Just in case it doesn't fit. */ + if (*num != u64) + return false; + return true; +} + +bool json_to_u16(const char *buffer, const jsmntok_t *tok, + short unsigned int *num) +{ + uint64_t u64; + + if (!json_to_u64(buffer, tok, &u64)) + return false; + *num = u64; + + /* Just in case it doesn't fit. */ + if (*num != u64) + return false; + return true; +} + +bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num) +{ + s64 tmp; + + if (!json_to_s64(buffer, tok, &tmp)) + return false; + *num = tmp; + + /* Just in case it doesn't fit. */ + if (*num != tmp) + return false; + + return true; +} + +bool json_to_errcode(const char *buffer, const jsmntok_t *tok, errcode_t *errcode) +{ + s64 tmp; + + if (!json_to_s64(buffer, tok, &tmp)) + return false; + *errcode = tmp; + + /* Just in case it doesn't fit. */ + if (*errcode != tmp) + return false; + + return true; +} + +bool json_to_sha256(const char *buffer, const jsmntok_t *tok, struct sha256 *dest) +{ + if (tok->type != JSMN_STRING) + return false; + + return hex_decode(buffer + tok->start, tok->end - tok->start, dest, + sizeof(struct sha256)); +} + +u8 *json_tok_bin_from_hex(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + u8 *result; + size_t hexlen, rawlen; + hexlen = tok->end - tok->start; + rawlen = hex_data_size(hexlen); + + result = tal_arr(ctx, u8, rawlen); + if (!hex_decode(buffer + tok->start, hexlen, result, rawlen)) + return tal_free(result); + + return result; +} + +/* talfmt take a ctx pointer and return NULL or a valid pointer. + * fmt takes the argument, and returns a bool. + * + * This function returns NULL on success, or errmsg on failure. +*/ +static const char *handle_percent(const char *buffer, + const jsmntok_t *tok, + va_list *ap) +{ + void *ctx; + const char *fmtname; + + /* This is set to (dummy) json_scan if it's a non-tal fmt */ + ctx = va_arg(*ap, void *); + fmtname = va_arg(*ap, const char *); + if (ctx != json_scan) { + void *(*talfmt)(void *, const char *, const jsmntok_t *); + void **p; + p = va_arg(*ap, void **); + talfmt = va_arg(*ap, void *(*)(void *, const char *, const jsmntok_t *)); + *p = talfmt(ctx, buffer, tok); + if (*p != NULL) + return NULL; + } else { + bool (*fmt)(const char *, const jsmntok_t *, void *); + void *p; + + p = va_arg(*ap, void *); + fmt = va_arg(*ap, bool (*)(const char *, const jsmntok_t *, void *)); + if (fmt(buffer, tok, p)) + return NULL; + } + + return tal_fmt(tmpctx, "%s could not parse %.*s", + fmtname, + json_tok_full_len(tok), + json_tok_full(buffer, tok)); +} + +/* GUIDE := OBJ | ARRAY | '%' + * OBJ := '{' FIELDLIST '}' + * FIELDLIST := FIELD [',' FIELD]* + * FIELD := LITERAL ':' FIELDVAL + * FIELDVAL := OBJ | ARRAY | LITERAL | '%' + * ARRAY := '[' ARRLIST ']' + * ARRLIST := ARRELEM [',' ARRELEM]* + * ARRELEM := NUMBER ':' FIELDVAL + */ + +static void parse_literal(const char **guide, + const char **literal, + size_t *len) +{ + *literal = *guide; + *len = strspn(*guide, + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "_-"); + *guide += *len; +} + +static void parse_number(const char **guide, u32 *number) +{ + char *endp; + long int l; + + l = strtol(*guide, &endp, 10); + assert(endp != *guide); + assert(errno != ERANGE); + + /* Test for overflow */ + *number = l; + assert(*number == l); + + *guide = endp; +} + +static char guide_consume_one(const char **guide) +{ + char c = **guide; + (*guide)++; + return c; +} + +static void guide_must_be(const char **guide, char c) +{ + char actual = guide_consume_one(guide); + assert(actual == c); +} + +/* Recursion: return NULL on success, errmsg on fail */ +static const char *parse_obj(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap); + +static const char *parse_arr(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap); + +static const char *parse_guide(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + const char *errmsg; + + if (**guide == '{') { + errmsg = parse_obj(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + } else if (**guide == '[') { + errmsg = parse_arr(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + } else { + guide_must_be(guide, '%'); + errmsg = handle_percent(buffer, tok, ap); + if (errmsg) + return errmsg; + } + return NULL; +} + +static const char *parse_fieldval(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + const char *errmsg; + + if (**guide == '{') { + errmsg = parse_obj(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + } else if (**guide == '[') { + errmsg = parse_arr(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + } else if (**guide == '%') { + guide_consume_one(guide); + errmsg = handle_percent(buffer, tok, ap); + if (errmsg) + return errmsg; + } else { + const char *literal; + size_t len; + + /* Literal must match exactly (modulo quotes for strings) */ + parse_literal(guide, &literal, &len); + if (!memeq(buffer + tok->start, tok->end - tok->start, + literal, len)) { + return tal_fmt(tmpctx, + "%.*s does not match expected %.*s", + json_tok_full_len(tok), + json_tok_full(buffer, tok), + (int)len, literal); + } + } + return NULL; +} + +static const char *parse_field(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + const jsmntok_t *member; + size_t len; + const char *memname; + + parse_literal(guide, &memname, &len); + guide_must_be(guide, ':'); + + member = json_get_membern(buffer, tok, memname, len); + if (!member) { + return tal_fmt(tmpctx, "object does not have member %.*s", + (int)len, memname); + } + + return parse_fieldval(buffer, member, guide, ap); +} + +static const char *parse_fieldlist(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + for (;;) { + const char *errmsg; + + errmsg = parse_field(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + if (**guide != ',') + break; + guide_consume_one(guide); + } + return NULL; +} + +static const char *parse_obj(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + const char *errmsg; + + guide_must_be(guide, '{'); + + if (tok->type != JSMN_OBJECT) { + return tal_fmt(tmpctx, "token is not an object: %.*s", + json_tok_full_len(tok), + json_tok_full(buffer, tok)); + } + + errmsg = parse_fieldlist(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + + guide_must_be(guide, '}'); + return NULL; +} + +static const char *parse_arrelem(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + const jsmntok_t *member; + u32 idx; + + parse_number(guide, &idx); + guide_must_be(guide, ':'); + + member = json_get_arr(tok, idx); + if (!member) { + return tal_fmt(tmpctx, "token has no index %u: %.*s", + idx, + json_tok_full_len(tok), + json_tok_full(buffer, tok)); + } + + return parse_fieldval(buffer, member, guide, ap); +} + +static const char *parse_arrlist(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + const char *errmsg; + + for (;;) { + errmsg = parse_arrelem(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + if (**guide != ',') + break; + guide_consume_one(guide); + } + return NULL; +} + +static const char *parse_arr(const char *buffer, + const jsmntok_t *tok, + const char **guide, + va_list *ap) +{ + const char *errmsg; + + guide_must_be(guide, '['); + + if (tok->type != JSMN_ARRAY) { + return tal_fmt(tmpctx, "token is not an array: %.*s", + json_tok_full_len(tok), + json_tok_full(buffer, tok)); + } + + errmsg = parse_arrlist(buffer, tok, guide, ap); + if (errmsg) + return errmsg; + + guide_must_be(guide, ']'); + return NULL; +} + +const char *json_scanv(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok, + const char *guide, + va_list ap) +{ + va_list cpy; + const char *orig_guide = guide, *errmsg; + + /* We need this, since &ap doesn't work on some platforms... */ + va_copy(cpy, ap); + errmsg = parse_guide(buffer, tok, &guide, &cpy); + va_end(cpy); + + if (errmsg) { + return tal_fmt(ctx, "Parsing '%.*s': %s", + (int)(guide - orig_guide), orig_guide, + errmsg); + } + assert(guide[0] == '\0'); + return NULL; +} + +const char *json_scan(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok, + const char *guide, + ...) +{ + va_list ap; + const char *ret; + + va_start(ap, guide); + ret = json_scanv(ctx, buffer, tok, guide, ap); + va_end(ap); + return ret; +} + +bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, + uint64_t *satoshi) +{ + char *end; + unsigned long btc, sat; + + btc = strtoul(buffer + tok->start, &end, 10); + if (btc == ULONG_MAX && errno == ERANGE) + return false; + if (end != buffer + tok->end) { + /* Expect always 8 decimal places. */ + if (*end != '.' || buffer + tok->end - end != 9) + return false; + sat = strtoul(end+1, &end, 10); + if (sat == ULONG_MAX && errno == ERANGE) + return false; + if (end != buffer + tok->end) + return false; + } else + sat = 0; + + *satoshi = btc * (uint64_t)100000000 + sat; + if (*satoshi != btc * (uint64_t)100000000 + sat) + return false; + + return true; +} + +bool json_to_node_id(const char *buffer, const jsmntok_t *tok, + struct node_id *id) +{ + return node_id_from_hexstr(buffer + tok->start, + tok->end - tok->start, id); +} + +bool json_to_pubkey(const char *buffer, const jsmntok_t *tok, + struct pubkey *pubkey) +{ + return pubkey_from_hexstr(buffer + tok->start, + tok->end - tok->start, pubkey); +} + +bool json_to_msat(const char *buffer, const jsmntok_t *tok, + struct amount_msat *msat) +{ + return parse_amount_msat(msat, + buffer + tok->start, tok->end - tok->start); +} + +bool json_to_sat(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat) +{ + return parse_amount_sat(sat, buffer + tok->start, tok->end - tok->start); +} + +bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat) +{ + if (json_tok_streq(buffer, tok, "all")) { + *sat = AMOUNT_SAT(-1ULL); + return true; + } + return json_to_sat(buffer, tok, sat); +} + +bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, + struct short_channel_id *scid) +{ + return (short_channel_id_from_str(buffer + tok->start, + tok->end - tok->start, scid)); +} + +bool json_to_txid(const char *buffer, const jsmntok_t *tok, + struct bitcoin_txid *txid) +{ + return bitcoin_txid_from_hex(buffer + tok->start, + tok->end - tok->start, txid); +} + +bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, + struct bitcoin_outpoint *op) +{ + jsmntok_t t1, t2; + + if (!split_tok(buffer, tok, ':', &t1, &t2)) + return false; + + return json_to_txid(buffer, &t1, &op->txid) + && json_to_u32(buffer, &t2, &op->n); +} + +bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, + struct channel_id *cid) +{ + return hex_decode(buffer + tok->start, tok->end - tok->start, + cid, sizeof(*cid)); +} + +bool json_to_coin_mvt_tag(const char *buffer, const jsmntok_t *tok, + enum mvt_tag *tag) +{ + enum mvt_tag i_tag; + for (size_t i = 0; i < NUM_MVT_TAGS; i++) { + i_tag = (enum mvt_tag) i; + if (json_tok_streq(buffer, tok, mvt_tag_str(i_tag))) { + *tag = i_tag; + return true; + } + } + + return false; +} + +bool split_tok(const char *buffer, const jsmntok_t *tok, + char split, + jsmntok_t *a, + jsmntok_t *b) +{ + const char *p = memchr(buffer + tok->start, split, tok->end - tok->start); + if (!p) + return false; + + *a = *b = *tok; + a->end = p - buffer; + b->start = p + 1 - buffer; + + return true; +} + +bool json_to_secret(const char *buffer, const jsmntok_t *tok, struct secret *dest) +{ + return hex_decode(buffer + tok->start, tok->end - tok->start, + dest->data, sizeof(struct secret)); +} + +bool json_to_preimage(const char *buffer, const jsmntok_t *tok, struct preimage *preimage) +{ + size_t hexlen = tok->end - tok->start; + return hex_decode(buffer + tok->start, hexlen, preimage->r, sizeof(preimage->r)); +} + +struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, + const jsmntok_t *tok) +{ + return psbt_from_b64(ctx, buffer + tok->start, tok->end - tok->start); +} + +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + struct tlv_obs2_onionmsg_payload_reply_path *rpath; + const jsmntok_t *hops, *t; + size_t i; + const char *err; + + rpath = tal(ctx, struct tlv_obs2_onionmsg_payload_reply_path); + err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", + JSON_SCAN(json_to_pubkey, &rpath->blinding), + JSON_SCAN(json_to_pubkey, &rpath->first_node_id), + NULL); + if (err) + return tal_free(rpath); + + hops = json_get_member(buffer, tok, "hops"); + if (!hops || hops->size < 1) + return tal_free(rpath); + + rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); + json_for_each_arr(i, t, hops) { + rpath->path[i] = tal(rpath->path, struct onionmsg_path); + err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", + JSON_SCAN(json_to_pubkey, + &rpath->path[i]->node_id), + JSON_SCAN_TAL(rpath->path[i], + json_tok_bin_from_hex, + &rpath->path[i]->encrypted_recipient_data)); + if (err) + return tal_free(rpath); + } + + return rpath; +} + +struct tlv_onionmsg_payload_reply_path * +json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + struct tlv_onionmsg_payload_reply_path *rpath; + const jsmntok_t *hops, *t; + size_t i; + const char *err; + + rpath = tal(ctx, struct tlv_onionmsg_payload_reply_path); + err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", + JSON_SCAN(json_to_pubkey, &rpath->blinding), + JSON_SCAN(json_to_pubkey, &rpath->first_node_id), + NULL); + if (err) + return tal_free(rpath); + + hops = json_get_member(buffer, tok, "hops"); + if (!hops || hops->size < 1) + return tal_free(rpath); + + rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); + json_for_each_arr(i, t, hops) { + rpath->path[i] = tal(rpath->path, struct onionmsg_path); + err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", + JSON_SCAN(json_to_pubkey, + &rpath->path[i]->node_id), + JSON_SCAN_TAL(rpath->path[i], + json_tok_bin_from_hex, + &rpath->path[i]->encrypted_recipient_data)); + if (err) + return tal_free(rpath); + } + + return rpath; +} diff --git a/common/json_parse.h b/common/json_parse.h new file mode 100644 index 000000000000..636ac4d8b90e --- /dev/null +++ b/common/json_parse.h @@ -0,0 +1,156 @@ +#ifndef LIGHTNING_COMMON_JSON_PARSE_H +#define LIGHTNING_COMMON_JSON_PARSE_H +#include "config.h" +#include +#include +#include +/* Simple helpers are here: this file contains heavier ones */ +#include + +struct json_escape; +struct json_stream; +struct timeabs; +struct timespec; +struct preimage; +struct secret; +struct pubkey; +struct node_id; +struct short_channel_id; +struct amount_sat; +struct amount_msat; +struct bitcoin_txid; +struct bitcoin_outpoint; +struct channel_id; + +/* Decode a hex-encoded binary */ +u8 *json_tok_bin_from_hex(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); + +/* Extract number from this (may be a string, or a number literal) */ +bool json_to_number(const char *buffer, const jsmntok_t *tok, + unsigned int *num); + +/* Extract signed 64 bit integer from this (may be a string, or a number literal) */ +bool json_to_s64(const char *buffer, const jsmntok_t *tok, s64 *num); + +/* Extract number from this (may be a string, or a number literal) */ +bool json_to_u16(const char *buffer, const jsmntok_t *tok, + uint16_t *num); + +bool json_to_sha256(const char *buffer, const jsmntok_t *tok, struct sha256 *dest); +/* + * Extract a non-negative (either 0 or positive) floating-point number from this + * (must be a number literal), multiply it by 1 million and return it as an + * integer. Any fraction smaller than 0.000001 is ignored. + */ +bool json_to_millionths(const char *buffer, const jsmntok_t *tok, + u64 *millionths); + +/* Extract signed integer from this (may be a string, or a number literal) */ +bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num); + +/* Extract an error code from this (may be a string, or a number literal) */ +bool json_to_errcode(const char *buffer, const jsmntok_t *tok, errcode_t *errcode); + +/* Split a json token into 2 tokens given a splitting character */ +bool split_tok(const char *buffer, const jsmntok_t *tok, + char split, + jsmntok_t *a, + jsmntok_t *b); + +/* Decode a hex-encoded payment preimage */ +bool json_to_preimage(const char *buffer, const jsmntok_t *tok, struct preimage *preimage); + +/* Extract a secret from this. */ +bool json_to_secret(const char *buffer, const jsmntok_t *tok, struct secret *dest); + +/* Extract a psbt from this. */ +struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, + const jsmntok_t *tok); + +/* Extract a pubkey from this */ +bool json_to_pubkey(const char *buffer, const jsmntok_t *tok, + struct pubkey *pubkey); + +/* Extract node_id from this: makes sure *id is valid! */ +bool json_to_node_id(const char *buffer, const jsmntok_t *tok, + struct node_id *id); + +/* Extract satoshis from this (may be a string, or a decimal number literal) */ +bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, + uint64_t *satoshi); + +/* Extract a short_channel_id from this */ +bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok, + struct short_channel_id *scid); + +/* Extract a satoshis amount from this */ +bool json_to_sat(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat); + +/* Extract a satoshis amount from this */ +/* If the string is "all", set amonut as AMOUNT_SAT(-1ULL). */ +bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok, + struct amount_sat *sat); + +/* Extract a millisatoshis amount from this */ +bool json_to_msat(const char *buffer, const jsmntok_t *tok, + struct amount_msat *msat); + +/* Extract a bitcoin txid from this */ +bool json_to_txid(const char *buffer, const jsmntok_t *tok, + struct bitcoin_txid *txid); + +/* Extract a bitcoin outpoint from this */ +bool json_to_outpoint(const char *buffer, const jsmntok_t *tok, + struct bitcoin_outpoint *op); + +/* Extract a channel id from this */ +bool json_to_channel_id(const char *buffer, const jsmntok_t *tok, + struct channel_id *cid); + +/* Extract a coin movement 'tag' from this */ +bool json_to_coin_mvt_tag(const char *buffer, const jsmntok_t *tok, + enum mvt_tag *tag); + +/* Extract reply path from this JSON */ +struct tlv_onionmsg_payload_reply_path * +json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); + +/* Obsolete version! */ +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); + + +/* Guide is % for a token: each must be followed by JSON_SCAN(). + * Returns NULL on error (asserts() on bad guide). */ +const char *json_scan(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok, + const char *guide, + ...); + +/* eg. JSON_SCAN(json_to_bool, &boolvar) */ +#define JSON_SCAN(fmt, var) \ + json_scan, \ + stringify(fmt), \ + ((var) + 0*sizeof(fmt((const char *)NULL, \ + (const jsmntok_t *)NULL, var) == true)), \ + (fmt) + +/* eg. JSON_SCAN_TAL(tmpctx, json_strdup, &charvar) */ +#define JSON_SCAN_TAL(ctx, fmt, var) \ + (ctx), \ + stringify(fmt), \ + ((var) + 0*sizeof((*var) = fmt((ctx), \ + (const char *)NULL, \ + (const jsmntok_t *)NULL))), \ + (fmt) + +/* Already-have-varargs version */ +const char *json_scanv(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok, + const char *guide, + va_list ap); + +#endif /* LIGHTNING_COMMON_JSON_PARSE_H */ diff --git a/common/json_parse_simple.c b/common/json_parse_simple.c new file mode 100644 index 000000000000..cf781611ec76 --- /dev/null +++ b/common/json_parse_simple.c @@ -0,0 +1,552 @@ +/* JSON core and helpers */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const char *json_tok_full(const char *buffer, const jsmntok_t *t) +{ + if (t->type == JSMN_STRING) + return buffer + t->start - 1; + return buffer + t->start; +} + +/* Include " if it's a string. */ +int json_tok_full_len(const jsmntok_t *t) +{ + if (t->type == JSMN_STRING) + return t->end - t->start + 2; + return t->end - t->start; +} + +bool json_tok_strneq(const char *buffer, const jsmntok_t *tok, + const char *str, size_t len) +{ + if (tok->type != JSMN_STRING) + return false; + return memeq(buffer + tok->start, tok->end - tok->start, str, len); +} + +bool json_tok_streq(const char *buffer, const jsmntok_t *tok, const char *str) +{ + return json_tok_strneq(buffer, tok, str, strlen(str)); +} + +bool json_tok_startswith(const char *buffer, const jsmntok_t *tok, + const char *prefix) +{ + if (tok->type != JSMN_STRING) + return false; + if (tok->end - tok->start < strlen(prefix)) + return false; + return memcmp(buffer + tok->start, + prefix, strlen(prefix)) == 0; +} + +bool json_tok_endswith(const char *buffer, const jsmntok_t *tok, + const char *suffix) +{ + if (tok->type != JSMN_STRING) + return false; + if (tok->end - tok->start < strlen(suffix)) + return false; + return memcmp(buffer + tok->end - strlen(suffix), + suffix, strlen(suffix)) == 0; +} + +char *json_strdup(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + return tal_strndup(ctx, buffer + tok->start, tok->end - tok->start); +} + + +bool json_to_u64(const char *buffer, const jsmntok_t *tok, u64 *num) +{ + char *end; + unsigned long long l; + + l = strtoull(buffer + tok->start, &end, 0); + if (end != buffer + tok->end) + return false; + + BUILD_ASSERT(sizeof(l) >= sizeof(*num)); + *num = l; + + /* Check for overflow */ + if (l == ULLONG_MAX && errno == ERANGE) + return false; + + if (*num != l) + return false; + + return true; +} + +bool json_to_u32(const char *buffer, const jsmntok_t *tok, u32 *num) +{ + uint64_t u64; + + if (!json_to_u64(buffer, tok, &u64)) + return false; + *num = u64; + + /* Just in case it doesn't fit. */ + if (*num != u64) + return false; + return true; +} + +bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b) +{ + if (tok->type != JSMN_PRIMITIVE) + return false; + if (memeqstr(buffer + tok->start, tok->end - tok->start, "true")) { + *b = true; + return true; + } + if (memeqstr(buffer + tok->start, tok->end - tok->start, "false")) { + *b = false; + return true; + } + return false; +} + + +bool json_tok_is_num(const char *buffer, const jsmntok_t *tok) +{ + if (tok->type != JSMN_PRIMITIVE) + return false; + + for (int i = tok->start; i < tok->end; i++) + if (!cisdigit(buffer[i])) + return false; + return true; +} + +bool json_tok_is_null(const char *buffer, const jsmntok_t *tok) +{ + if (tok->type != JSMN_PRIMITIVE) + return false; + return buffer[tok->start] == 'n'; +} + +const jsmntok_t *json_next(const jsmntok_t *tok) +{ + const jsmntok_t *t; + size_t i; + + for (t = tok + 1, i = 0; i < tok->size; i++) + t = json_next(t); + + return t; +} + +const jsmntok_t *json_get_membern(const char *buffer, + const jsmntok_t tok[], + const char *label, size_t len) +{ + const jsmntok_t *t; + size_t i; + + if (tok->type != JSMN_OBJECT) + return NULL; + + json_for_each_obj(i, t, tok) + if (json_tok_strneq(buffer, t, label, len)) + return t + 1; + + return NULL; +} + +const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], + const char *label) +{ + return json_get_membern(buffer, tok, label, strlen(label)); +} + +const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index) +{ + const jsmntok_t *t; + size_t i; + + if (tok->type != JSMN_ARRAY) + return NULL; + + json_for_each_arr(i, t, tok) { + if (index == 0) + return t; + index--; + } + + return NULL; +} + +/*----------------------------------------------------------------------------- +JSMN Result Validation Starts +-----------------------------------------------------------------------------*/ +/*~ LIBJSMN is a fast, small JSON parsing library. + * + * "Fast, small" means it does not, in fact, do a + * lot of checking for invalid JSON. + * + * For example, by itself it would accept the strings + * `{"1" "2" "3" "4"}` and `["key": 1 2 3 4]` as valid. + * Obviously those are not in any way valid JSON. + * + * This part of the code performs some filtering so + * that at least some of the invalid JSON that + * LIBJSMN accepts, will be rejected by + * json_parse_input. It also checks that strings are valid UTF-8. + */ + +/*~ These functions are used in JSMN validation. + * + * The calling convention is that the "current" token + * is passed in as the first argument, and after the + * validator, is returned from the function. + * + * p = validate_jsmn_datum(p, end, valid); + * + * The reason has to do with typical C ABIs. + * Usually, the first few arguments are passed in via + * register, and the return value is also returned + * via register. + * This calling convention generally ensures that + * the current token pointer `p` is always in a + * register and is never forced into memory by the + * compiler. + * + * These functions are pre-declared here as they + * are interrecursive. + * Note that despite the recursion, `p` is only ever + * advanced, and there is only ever one `p` value, + * thus the overall algorithm is strict O(n) + * (*not* amortized) in time. + * The recursion does mean the algorithm is O(d) + * in memory (specifically stack frames), where d + * is the nestedness of objects in the input. + * This may become an issue later if we are in a + * stack-limited environment, such as if we actually + * went and used threads. + */ +/* Validate a *single* datum. */ +static const jsmntok_t * +validate_jsmn_datum(const char *buf, + const jsmntok_t *p, + const jsmntok_t *end, + bool *valid); +/*~ Validate a key-value pair. + * + * In JSMN, objects are not dictionaries. + * Instead, they are a sequence of datums. + * + * In fact, objects and arrays in JSMN are "the same", + * they only differ in delimiter characters. + * + * Of course, in "real" JSON, an object is a dictionary + * of key-value pairs. + * + * So what JSMN does is that the syntax "key": "value" + * is considered a *single* datum, a string "key" + * that contains a value "value". + * + * Indeed, JSMN accepts `["key": "value"]` as well as + * `{"item1", "item2"}`. + * The entire point of the validate_jsmn_result function + * is to reject such improper arrays and objects. + */ +static const jsmntok_t * +validate_jsmn_keyvalue(const char *buf, + const jsmntok_t *p, + const jsmntok_t *end, + bool *valid); + +static const jsmntok_t * +validate_jsmn_datum(const char *buf, + const jsmntok_t *p, + const jsmntok_t *end, + bool *valid) +{ + int i; + int sz; + + if (p >= end) { + *valid = false; + return p; + } + + switch (p->type) { + case JSMN_STRING: + if (!utf8_check(buf + p->start, p->end - p->start)) + *valid = false; + /* Fall thru */ + case JSMN_UNDEFINED: + case JSMN_PRIMITIVE: + /* These types should not have sub-datums. */ + if (p->size != 0) + *valid = false; + else + ++p; + break; + + case JSMN_ARRAY: + /* Save the array size; we will advance p. */ + sz = p->size; + ++p; + for (i = 0; i < sz; ++i) { + /* Arrays should only contain standard JSON datums. */ + p = validate_jsmn_datum(buf, p, end, valid); + if (!*valid) + break; + } + break; + + case JSMN_OBJECT: + /* Save the object size; we will advance p. */ + sz = p->size; + ++p; + for (i = 0; i < sz; ++i) { + /* Objects should only contain key-value pairs. */ + p = validate_jsmn_keyvalue(buf, p, end, valid); + if (!*valid) + break; + } + break; + + default: + *valid = false; + break; + } + + return p; +} +/* Key-value pairs *must* be strings with size 1. */ +static inline const jsmntok_t * +validate_jsmn_keyvalue(const char *buf, + const jsmntok_t *p, + const jsmntok_t *end, + bool *valid) +{ + if (p >= end) { + *valid = false; + return p; + } + + /* Check key. + * + * JSMN parses the syntax `"key": "value"` as a + * JSMN_STRING of size 1, containing the value + * datum as a sub-datum. + * + * Thus, keys in JSON objects are really strings + * that "contain" the value, thus we check if + * the size is 1. + * + * JSMN supports a non-standard syntax such as + * `"key": 1 2 3 4`, which it considers as a + * string object that contains a sequence of + * sub-datums 1 2 3 4. + * The check below that p->size == 1 also + * incidentally rejects that non-standard + * JSON. + */ + if (p->type != JSMN_STRING || p->size != 1 + || !utf8_check(buf + p->start, p->end - p->start)) { + *valid = false; + return p; + } + + ++p; + return validate_jsmn_datum(buf, p, end, valid); +} + +/** validate_jsmn_parse_output + * + * @brief Validates the result of jsmn_parse. + * + * @desc LIBJMSN is a small fast library, not a + * comprehensive library. + * + * This simply means that LIBJSMN will accept a + * *lot* of very strange text that is technically + * not JSON. + * + * For example, LIBJSMN would accept the strings + * `{"1" "2" "3" "4"}` and `["key": 1 2 3 4]` as valid. + * + * This can lead to strange sequences of jsmntok_t + * objects. + * Unfortunately, most of our code assumes that + * the data fed into our JSON-RPC interface is + * valid JSON, and in particular is not invalid + * JSON that tickles LIBJSMN into emitting + * strange sequences of `jsmntok_t`. + * + * This function detects such possible problems + * and returns false if such an issue is found. + * If so, it is probably unsafe to pass the + * `jsmntok_t` generated by LIBJSMN to any other + * parts of our code. + * + * @param p - The first jsmntok_t token to process. + * This function does not assume that semantically + * only one JSON datum is processed; it does expect + * a sequence of complete JSON datums (which is + * what LIBJSMN *should* output). + * @param end - One past the end of jsmntok_t. + * Basically, this function is assured to read tokens + * starting at p up to end - 1. + * If p >= end, this will not validate anything and + * trivially return true. + * + * @return true if there appears to be no problem + * with the jsmntok_t sequence outputted by + * `jsmn_parse`, false otherwise. + */ +static bool +validate_jsmn_parse_output(const char *buf, + const jsmntok_t *p, const jsmntok_t *end) +{ + bool valid = true; + + while (p < end && valid) + p = validate_jsmn_datum(buf, p, end, &valid); + + return valid; +} + +/*----------------------------------------------------------------------------- +JSMN Result Validation Ends +-----------------------------------------------------------------------------*/ + +void toks_reset(jsmntok_t *toks) +{ + assert(tal_count(toks) >= 1); + toks[0].type = JSMN_UNDEFINED; +} + +jsmntok_t *toks_alloc(const tal_t *ctx) +{ + jsmntok_t *toks = tal_arr(ctx, jsmntok_t, 10); + toks_reset(toks); + return toks; +} + +bool json_parse_input(jsmn_parser *parser, + jsmntok_t **toks, + const char *input, int len, + bool *complete) +{ + int ret; + +again: + ret = jsmn_parse(parser, input, len, *toks, tal_count(*toks) - 1); + + switch (ret) { + case JSMN_ERROR_INVAL: + return false; + case JSMN_ERROR_NOMEM: + tal_resize(toks, tal_count(*toks) * 2); + goto again; + } + + /* Check whether we read at least one full root element, i.e., root + * element has its end set. */ + if ((*toks)[0].type == JSMN_UNDEFINED || (*toks)[0].end == -1) { + *complete = false; + return true; + } + + /* If we read a partial element at the end of the stream we'll get a + * ret=JSMN_ERROR_PART, but due to the previous check we know we read at + * least one full element, so count tokens that are part of this root + * element. */ + ret = json_next(*toks) - *toks; + + if (!validate_jsmn_parse_output(input, *toks, *toks + ret)) + return false; + + /* Cut to length and return. */ + tal_resize(toks, ret + 1); + /* Make sure last one is always referenceable. */ + (*toks)[ret].type = -1; + (*toks)[ret].start = (*toks)[ret].end = (*toks)[ret].size = 0; + + *complete = true; + return true; +} + +jsmntok_t *json_parse_simple(const tal_t *ctx, const char *input, int len) +{ + bool complete; + jsmn_parser parser; + jsmntok_t *toks = toks_alloc(ctx); + + jsmn_init(&parser); + + if (!json_parse_input(&parser, &toks, input, len, &complete) + || !complete) + return tal_free(toks); + return toks; +} + +const char *jsmntype_to_string(jsmntype_t t) +{ + switch (t) { + case JSMN_UNDEFINED : + return "UNDEFINED"; + case JSMN_OBJECT : + return "OBJECT"; + case JSMN_ARRAY : + return "ARRAY"; + case JSMN_STRING : + return "STRING"; + case JSMN_PRIMITIVE : + return "PRIMITIVE"; + } + return "INVALID"; +} + +jsmntok_t *json_tok_copy(const tal_t *ctx, const jsmntok_t *tok) +{ + return tal_dup_arr(ctx, jsmntok_t, tok, json_next(tok) - tok, 0); +} + +void json_tok_remove(jsmntok_t **tokens, + jsmntok_t *obj_or_array, const jsmntok_t *tok, size_t num) +{ + const jsmntok_t *src = tok; + const jsmntok_t *end = json_next(*tokens); + jsmntok_t *dest = *tokens + (tok - *tokens); + int remove_count; + + assert(*tokens); + assert(obj_or_array->type == JSMN_ARRAY + || obj_or_array->type == JSMN_OBJECT); + /* obj_or_array must be inside tokens, and tok must be inside + * obj_or_array */ + assert(obj_or_array >= *tokens + && obj_or_array < *tokens + tal_count(*tokens)); + assert(tok >= obj_or_array + && tok < *tokens + tal_count(*tokens)); + + for (int i = 0; i < num; i++) + src = json_next(src); + + /* Don't give us a num which goes over end of obj_or_array. */ + assert(src <= json_next(obj_or_array)); + + remove_count = src - tok; + + memmove(dest, src, sizeof(jsmntok_t) * (end - src)); + + /* Subtract first: this ptr may move after tal_resize! */ + obj_or_array->size -= num; + tal_resize(tokens, tal_count(*tokens) - remove_count); +} diff --git a/common/json_parse_simple.h b/common/json_parse_simple.h new file mode 100644 index 000000000000..d0670b013b88 --- /dev/null +++ b/common/json_parse_simple.h @@ -0,0 +1,122 @@ +/* Very simple core JSON parse helpers: used by lightning-cli too */ +#ifndef LIGHTNING_COMMON_JSON_PARSE_SIMPLE_H +#define LIGHTNING_COMMON_JSON_PARSE_SIMPLE_H +#include "config.h" +#include +#include + +#define JSMN_STRICT 1 +# include + +/* Include " if it's a string. */ +const char *json_tok_full(const char *buffer, const jsmntok_t *t); + +/* Include " if it's a string. */ +int json_tok_full_len(const jsmntok_t *t); + +/* Is this a string equal to str? */ +bool json_tok_streq(const char *buffer, const jsmntok_t *tok, const char *str); + +/* Is this a string equal to str of length len? */ +bool json_tok_strneq(const char *buffer, const jsmntok_t *tok, + const char *str, size_t len); + +/* Does this string token start with prefix? */ +bool json_tok_startswith(const char *buffer, const jsmntok_t *tok, + const char *prefix); + +/* Does this string token end with suffix? */ +bool json_tok_endswith(const char *buffer, const jsmntok_t *tok, + const char *suffix); + +/* Allocate a tal string copy */ +char *json_strdup(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); + +/* Extract number from this (may be a string, or a number literal) */ +bool json_to_u64(const char *buffer, const jsmntok_t *tok, u64 *num); + +/* Extract number from this (may be a string, or a number literal) */ +bool json_to_u32(const char *buffer, const jsmntok_t *tok, u32 *num); + +/* Extract boolean from this */ +bool json_to_bool(const char *buffer, const jsmntok_t *tok, bool *b); + +/* Is this a number? [0..9]+ */ +bool json_tok_is_num(const char *buffer, const jsmntok_t *tok); + +/* Is this the null primitive? */ +bool json_tok_is_null(const char *buffer, const jsmntok_t *tok); + +/* Returns next token with same parent (WARNING: slow!). */ +const jsmntok_t *json_next(const jsmntok_t *tok); + +/* Get top-level member with explicit label len */ +const jsmntok_t *json_get_membern(const char *buffer, + const jsmntok_t tok[], + const char *label, size_t len); + +/* Get top-level member. */ +const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], + const char *label); + +/* Get index'th array member. */ +const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index); + +/* Allocate a starter array of tokens for json_parse_input */ +jsmntok_t *toks_alloc(const tal_t *ctx); + +/* Reset a token array to reuse it. */ +void toks_reset(jsmntok_t *toks); + +/** + * json_parse_input: parse and validate JSON. + * @parser: parser initialized with jsmn_init. + * @toks: tallocated array from toks_alloc() + * @input, @len: input string. + * @complete: set to true if the valid JSON is complete, or NULL if must be. + * + * This returns false if the JSON is invalid, true otherwise. + * If it returns true, *@complete indicates that (*@toks)[0] points to a + * valid, complete JSON element. If @complete is NULL, then incomplete + * JSON returns false (i.e. is considered invalid). + * + * *@toks is resized to the complete set of tokens, with a dummy + * terminator (type == -1) at the end. + * + * If it returns true, and *@complete is false, you can append more + * data to @input and call it again (with the same perser) and the parser + * will continue where it left off. +*/ +bool json_parse_input(jsmn_parser *parser, + jsmntok_t **toks, + const char *input, int len, + bool *complete); + +/* Simplified version of above which parses only a complete, valid + * JSON string */ +jsmntok_t *json_parse_simple(const tal_t *ctx, const char *input, int len); + +/* Convert a jsmntype_t enum to a human readable string. */ +const char *jsmntype_to_string(jsmntype_t t); + +/* Return a copy of a json value as an array. */ +jsmntok_t *json_tok_copy(const tal_t *ctx, const jsmntok_t *tok); + +/* + * Remove @num json values from a json array or object @obj. @tok points + * to the first value to remove. The array @tokens will be resized. + */ +void json_tok_remove(jsmntok_t **tokens, + jsmntok_t *obj_or_array, const jsmntok_t *tok, size_t num); + + +/* Iterator macro for array: i is counter, t is token ptr, arr is JSMN_ARRAY */ +#define json_for_each_arr(i, t, arr) \ + for (i = 0, t = (arr) + 1; i < (arr)->size; t = json_next(t), i++) + +/* Iterator macro for object: i is counter, t is token ptr (t+1 is + * contents of obj member), obj is JSMN_OBJECT */ +#define json_for_each_obj(i, t, obj) \ + for (i = 0, t = (obj) + 1; i < (obj)->size; t = json_next(t+1), i++) + +#endif /* LIGHTNING_COMMON_JSON_PARSE_SIMPLE_H */ diff --git a/common/json_stream.c b/common/json_stream.c index 8ec5fefdc0f6..740defa7c303 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -1,10 +1,28 @@ #include "config.h" +#include +#include +#include +#include +#include +#include +#include #include /* To reach into io_plan: not a public header! */ #include +#include #include +#include +#include +#include +#include #include - +#include +#include +#include +#include +#include +#include +#include static void adjust_io_write(struct json_out *jout, ptrdiff_t delta, @@ -204,3 +222,419 @@ struct io_plan *json_stream_output_(struct json_stream *js, js->len_read = 0; return json_stream_output_write(conn, js); } + +void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value) +{ + json_add_member(result, fieldname, false, "%u", value); +} + +void json_add_u64(struct json_stream *result, const char *fieldname, + uint64_t value) +{ + json_add_member(result, fieldname, false, "%"PRIu64, value); +} + +void json_add_s64(struct json_stream *result, const char *fieldname, + int64_t value) +{ + json_add_member(result, fieldname, false, "%"PRIi64, value); +} + +void json_add_u32(struct json_stream *result, const char *fieldname, + uint32_t value) +{ + json_add_member(result, fieldname, false, "%u", value); +} + +void json_add_s32(struct json_stream *result, const char *fieldname, + int32_t value) +{ + json_add_member(result, fieldname, false, "%d", value); +} + +void json_add_literal(struct json_stream *result, const char *fieldname, + const char *literal, int len) +{ + /* Literal may contain quotes, so bypass normal checks */ + char *dest = json_member_direct(result, fieldname, len); + memcpy(dest, literal, len); +} + +void json_add_stringn(struct json_stream *result, const char *fieldname, + const char *value TAKES, size_t value_len) +{ + json_add_member(result, fieldname, true, "%.*s", (int)value_len, value); + if (taken(value)) + tal_free(value); +} + +void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES) +{ + json_add_stringn(result, fieldname, value, strlen(value)); +} + +void json_add_bool(struct json_stream *result, const char *fieldname, bool value) +{ + json_add_member(result, fieldname, false, value ? "true" : "false"); +} + +void json_add_null(struct json_stream *stream, const char *fieldname) +{ + json_add_member(stream, fieldname, false, "null"); +} + +void json_add_hex(struct json_stream *js, const char *fieldname, + const void *data, size_t len) +{ + /* Size without NUL term */ + size_t hexlen = hex_str_size(len) - 1; + char *dest; + + dest = json_member_direct(js, fieldname, 1 + hexlen + 1); + dest[0] = '"'; + if (!hex_encode(data, len, dest + 1, hexlen + 1)) + abort(); + dest[1+hexlen] = '"'; +} + +void json_add_hex_talarr(struct json_stream *result, + const char *fieldname, + const tal_t *data) +{ + json_add_hex(result, fieldname, data, tal_bytelen(data)); +} + +void json_add_escaped_string(struct json_stream *result, const char *fieldname, + const struct json_escape *esc TAKES) +{ + /* Already escaped, don't re-escape! */ + char *dest = json_member_direct(result, fieldname, + 1 + strlen(esc->s) + 1); + + dest[0] = '"'; + memcpy(dest + 1, esc->s, strlen(esc->s)); + dest[1+strlen(esc->s)] = '"'; + if (taken(esc)) + tal_free(esc); +} + +void json_add_timeabs(struct json_stream *result, const char *fieldname, + struct timeabs t) +{ + json_add_member(result, fieldname, false, "%" PRIu64 ".%03" PRIu64, + (u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000); +} + +void json_add_time(struct json_stream *result, const char *fieldname, + struct timespec ts) +{ + char timebuf[100]; + + snprintf(timebuf, sizeof(timebuf), "%lu.%09u", + (unsigned long)ts.tv_sec, + (unsigned)ts.tv_nsec); + json_add_string(result, fieldname, timebuf); +} + +void json_add_timeiso(struct json_stream *result, + const char *fieldname, + struct timeabs *time) +{ + char iso8601_msec_fmt[sizeof("YYYY-mm-ddTHH:MM:SS.%03dZ")]; + char iso8601_s[sizeof("YYYY-mm-ddTHH:MM:SS.nnnZ")]; + + strftime(iso8601_msec_fmt, sizeof(iso8601_msec_fmt), + "%FT%T.%%03dZ", gmtime(&time->ts.tv_sec)); + snprintf(iso8601_s, sizeof(iso8601_s), + iso8601_msec_fmt, (int) time->ts.tv_nsec / 1000000); + + json_add_string(result, fieldname, iso8601_s); +} + + +void json_add_tok(struct json_stream *result, const char *fieldname, + const jsmntok_t *tok, const char *buffer) +{ + char *space; + assert(tok->type != JSMN_UNDEFINED); + + space = json_member_direct(result, fieldname, json_tok_full_len(tok)); + memcpy(space, json_tok_full(buffer, tok), json_tok_full_len(tok)); +} + +void json_add_errcode(struct json_stream *result, const char *fieldname, + errcode_t code) +{ + json_add_member(result, fieldname, false, "%"PRIerrcode, code); +} + +void json_add_invstring(struct json_stream *result, const char *invstring) +{ + if (strstarts(invstring, "lni")) + json_add_string(result, "bolt12", invstring); + else + json_add_string(result, "bolt11", invstring); +} + +void json_add_node_id(struct json_stream *response, + const char *fieldname, + const struct node_id *id) +{ + json_add_hex(response, fieldname, id->k, sizeof(id->k)); +} + +void json_add_channel_id(struct json_stream *response, + const char *fieldname, + const struct channel_id *cid) +{ + json_add_hex(response, fieldname, cid->id, sizeof(cid->id)); +} + +void json_add_pubkey(struct json_stream *response, + const char *fieldname, + const struct pubkey *key) +{ + u8 der[PUBKEY_CMPR_LEN]; + + pubkey_to_der(der, key); + json_add_hex(response, fieldname, der, sizeof(der)); +} + +void json_add_point32(struct json_stream *response, + const char *fieldname, + const struct point32 *key) +{ + u8 output[32]; + + secp256k1_xonly_pubkey_serialize(secp256k1_ctx, output, &key->pubkey); + json_add_hex(response, fieldname, output, sizeof(output)); +} + +void json_add_bip340sig(struct json_stream *response, + const char *fieldname, + const struct bip340sig *sig) +{ + json_add_hex(response, fieldname, sig->u8, sizeof(sig->u8)); +} + +void json_add_txid(struct json_stream *result, const char *fieldname, + const struct bitcoin_txid *txid) +{ + char hex[hex_str_size(sizeof(*txid))]; + + bitcoin_txid_to_hex(txid, hex, sizeof(hex)); + json_add_string(result, fieldname, hex); +} + +void json_add_outpoint(struct json_stream *result, const char *fieldname, + const struct bitcoin_outpoint *out) +{ + char hex[hex_str_size(sizeof(out->txid))]; + bitcoin_txid_to_hex(&out->txid, hex, sizeof(hex)); + json_add_member(result, fieldname, true, "%s:%d", hex, out->n); +} + +void json_add_short_channel_id(struct json_stream *response, + const char *fieldname, + const struct short_channel_id *scid) +{ + json_add_member(response, fieldname, true, "%dx%dx%d", + short_channel_id_blocknum(scid), + short_channel_id_txnum(scid), + short_channel_id_outnum(scid)); +} + +void json_add_address(struct json_stream *response, const char *fieldname, + const struct wireaddr *addr) +{ + json_object_start(response, fieldname); + if (addr->type == ADDR_TYPE_IPV4) { + char addrstr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, addr->addr, addrstr, INET_ADDRSTRLEN); + json_add_string(response, "type", "ipv4"); + json_add_string(response, "address", addrstr); + json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_IPV6) { + char addrstr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, addr->addr, addrstr, INET6_ADDRSTRLEN); + json_add_string(response, "type", "ipv6"); + json_add_string(response, "address", addrstr); + json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_TOR_V2_REMOVED) { + json_add_string(response, "type", "torv2"); + json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); + json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_TOR_V3) { + json_add_string(response, "type", "torv3"); + json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); + json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_DNS) { + json_add_string(response, "type", "dns"); + json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr)); + json_add_num(response, "port", addr->port); + } else if (addr->type == ADDR_TYPE_WEBSOCKET) { + json_add_string(response, "type", "websocket"); + json_add_num(response, "port", addr->port); + } + json_object_end(response); +} + +void json_add_address_internal(struct json_stream *response, + const char *fieldname, + const struct wireaddr_internal *addr) +{ + switch (addr->itype) { + case ADDR_INTERNAL_SOCKNAME: + json_object_start(response, fieldname); + json_add_string(response, "type", "local socket"); + json_add_string(response, "socket", addr->u.sockname); + json_object_end(response); + return; + case ADDR_INTERNAL_ALLPROTO: + json_object_start(response, fieldname); + json_add_string(response, "type", "any protocol"); + json_add_num(response, "port", addr->u.port); + json_object_end(response); + return; + case ADDR_INTERNAL_AUTOTOR: + json_object_start(response, fieldname); + json_add_string(response, "type", "Tor generated address"); + json_add_address(response, "service", &addr->u.torservice.address); + json_object_end(response); + return; + case ADDR_INTERNAL_STATICTOR: + json_object_start(response, fieldname); + json_add_string(response, "type", "Tor from blob generated static address"); + json_add_address(response, "service", &addr->u.torservice.address); + json_object_end(response); + return; + case ADDR_INTERNAL_FORPROXY: + json_object_start(response, fieldname); + json_add_string(response, "type", "unresolved"); + json_add_string(response, "name", addr->u.unresolved.name); + json_add_num(response, "port", addr->u.unresolved.port); + json_object_end(response); + return; + case ADDR_INTERNAL_WIREADDR: + json_add_address(response, fieldname, &addr->u.wireaddr); + return; + } + abort(); +} + +void json_add_tx(struct json_stream *result, + const char *fieldname, + const struct bitcoin_tx *tx) +{ + json_add_hex_talarr(result, fieldname, linearize_tx(tmpctx, tx)); +} + +void json_add_psbt(struct json_stream *stream, + const char *fieldname, + const struct wally_psbt *psbt TAKES) +{ + const char *psbt_b64; + psbt_b64 = psbt_to_b64(NULL, psbt); + json_add_string(stream, fieldname, take(psbt_b64)); + if (taken(psbt)) + tal_free(psbt); +} + +void json_add_amount_msat_compat(struct json_stream *result, + struct amount_msat msat, + const char *rawfieldname, + const char *msatfieldname) +{ + if (deprecated_apis) + json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */ + json_add_amount_msat_only(result, msatfieldname, msat); +} + +void json_add_amount_msat_only(struct json_stream *result, + const char *msatfieldname, + struct amount_msat msat) +{ + if (!deprecated_apis) + assert(strends(msatfieldname, "_msat")); + if (deprecated_apis) + json_add_string(result, msatfieldname, + type_to_string(tmpctx, struct amount_msat, &msat)); + else + json_add_u64(result, msatfieldname, msat.millisatoshis); /* Raw: low-level helper */ +} + +void json_add_amount_sat_compat(struct json_stream *result, + struct amount_sat sat, + const char *rawfieldname, + const char *msatfieldname) +{ + if (deprecated_apis) + json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */ + json_add_amount_sat_msat(result, msatfieldname, sat); +} + +void json_add_amount_sat_msat(struct json_stream *result, + const char *msatfieldname, + struct amount_sat sat) +{ + struct amount_msat msat; + assert(strends(msatfieldname, "_msat")); + if (amount_sat_to_msat(&msat, sat)) + json_add_amount_msat_only(result, msatfieldname, msat); +} + +/* When I noticed that we were adding "XXXmsat" fields *not* ending in _msat */ +void json_add_amount_sats_deprecated(struct json_stream *result, + const char *fieldname, + const char *msatfieldname, + struct amount_sat sat) +{ + if (deprecated_apis) { + struct amount_msat msat; + assert(!strends(fieldname, "_msat")); + if (amount_sat_to_msat(&msat, sat)) + json_add_string(result, fieldname, + take(fmt_amount_msat(NULL, msat))); + } + json_add_amount_sat_msat(result, msatfieldname, sat); +} + +void json_add_sats(struct json_stream *result, + const char *fieldname, + struct amount_sat sat) +{ + json_add_string(result, fieldname, take(fmt_amount_sat(NULL, sat))); +} + +void json_add_secret(struct json_stream *response, const char *fieldname, + const struct secret *secret) +{ + json_add_hex(response, fieldname, secret, sizeof(struct secret)); +} + +void json_add_sha256(struct json_stream *result, const char *fieldname, + const struct sha256 *hash) +{ + json_add_hex(result, fieldname, hash, sizeof(*hash)); +} + +void json_add_preimage(struct json_stream *result, const char *fieldname, + const struct preimage *preimage) +{ + json_add_hex(result, fieldname, preimage, sizeof(*preimage)); +} + +void json_add_lease_rates(struct json_stream *result, + const struct lease_rates *rates) +{ + json_add_amount_sat_msat(result, "lease_fee_base_msat", + amount_sat(rates->lease_fee_base_sat)); + json_add_num(result, "lease_fee_basis", rates->lease_fee_basis); + json_add_num(result, "funding_weight", rates->funding_weight); + json_add_amount_msat_only(result, + "channel_fee_max_base_msat", + amount_msat(rates->channel_fee_max_base_msat)); + json_add_num(result, "channel_fee_max_proportional_thousandths", + rates->channel_fee_max_proportional_thousandths); +} + diff --git a/common/json_stream.h b/common/json_stream.h index 2c1dceee2f9d..71bf82814367 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -4,11 +4,36 @@ #ifndef LIGHTNING_COMMON_JSON_STREAM_H #define LIGHTNING_COMMON_JSON_STREAM_H #include "config.h" + +#define JSMN_STRICT 1 +# include + +#include #include +#include +#include +#include struct command; struct io_conn; struct log; +struct json_escape; +struct pubkey; +struct point32; +struct bip340sig; +struct secret; +struct node_id; +struct channel_id; +struct bitcoin_txid; +struct bitcoin_outpoint; +struct short_channel_id; +struct sha256; +struct preimage; +struct bitcoin_tx; +struct wally_psbt; +struct lease_rates; +struct wireaddr; +struct wireaddr_internal; struct json_stream { struct json_out *jout; @@ -146,4 +171,196 @@ struct io_plan *json_stream_output_(struct json_stream *js, void json_stream_double_cr(struct json_stream *js); void json_stream_flush(struct json_stream *js); +/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. Turns + * any non-printable chars into JSON escapes, but leaves existing escapes alone. + */ +void json_add_string(struct json_stream *result, const char *fieldname, const char *value); + +/* '"fieldname" : "value[:value_len]"' or '"value[:value_len]"' if + * fieldname is NULL. Turns any non-printable chars into JSON + * escapes, but leaves existing escapes alone. + */ +void json_add_stringn(struct json_stream *result, const char *fieldname, + const char *value TAKES, size_t value_len); + +/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. String must + * already be JSON escaped as necessary. */ +void json_add_escaped_string(struct json_stream *result, + const char *fieldname, + const struct json_escape *esc TAKES); + +/* '"fieldname" : literal' or 'literal' if fieldname is NULL*/ +void json_add_literal(struct json_stream *result, const char *fieldname, + const char *literal, int len); +/* '"fieldname" : value' or 'value' if fieldname is NULL */ +void json_add_num(struct json_stream *result, const char *fieldname, + unsigned int value); +/* '"fieldname" : value' or 'value' if fieldname is NULL */ +void json_add_u64(struct json_stream *result, const char *fieldname, + uint64_t value); +/* '"fieldname" : value' or 'value' if fieldname is NULL */ +void json_add_s64(struct json_stream *result, const char *fieldname, + int64_t value); +/* '"fieldname" : value' or 'value' if fieldname is NULL */ +void json_add_u32(struct json_stream *result, const char *fieldname, + uint32_t value); +/* '"fieldname" : value' or 'value' if fieldname is NULL */ +void json_add_s32(struct json_stream *result, const char *fieldname, + int32_t value); +/* '"fieldname" : true|false' or 'true|false' if fieldname is NULL */ +void json_add_bool(struct json_stream *result, const char *fieldname, + bool value); + +/* '"fieldname" : null' or 'null' if fieldname is NULL */ +void json_add_null(struct json_stream *stream, const char *fieldname); + +/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */ +void json_add_hex(struct json_stream *result, const char *fieldname, + const void *data, size_t len); +/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */ +void json_add_hex_talarr(struct json_stream *result, + const char *fieldname, + const tal_t *data); + +void json_add_timeabs(struct json_stream *result, const char *fieldname, + struct timeabs t); + +/* used in log.c and notification.c*/ +void json_add_time(struct json_stream *result, const char *fieldname, + struct timespec ts); + +/* Add ISO_8601 timestamp string, i.e. "2019-09-07T15:50+01:00" */ +void json_add_timeiso(struct json_stream *result, + const char *fieldname, + struct timeabs *time); + +/* Add any json token */ +void json_add_tok(struct json_stream *result, const char *fieldname, + const jsmntok_t *tok, const char *buffer); + +/* Add an error code */ +void json_add_errcode(struct json_stream *result, const char *fieldname, + errcode_t code); + +/* Add "bolt11" or "bolt12" field, depending on invstring. */ +void json_add_invstring(struct json_stream *result, const char *invstring); + +/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ +void json_add_pubkey(struct json_stream *response, + const char *fieldname, + const struct pubkey *key); + +/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */ +void json_add_point32(struct json_stream *response, + const char *fieldname, + const struct point32 *key); + +/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */ +void json_add_bip340sig(struct json_stream *response, + const char *fieldname, + const struct bip340sig *sig); + +/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */ +void json_add_secret(struct json_stream *response, + const char *fieldname, + const struct secret *secret); + +/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ +void json_add_node_id(struct json_stream *response, + const char *fieldname, + const struct node_id *id); + +/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ +void json_add_channel_id(struct json_stream *response, + const char *fieldname, + const struct channel_id *cid); + +/* '"fieldname" : ' or "" if fieldname is NULL */ +void json_add_txid(struct json_stream *result, const char *fieldname, + const struct bitcoin_txid *txid); + +/* '"fieldname" : "txid:n" */ +void json_add_outpoint(struct json_stream *result, const char *fieldname, + const struct bitcoin_outpoint *out); + +/* '"fieldname" : "1234:5:6"' */ +void json_add_short_channel_id(struct json_stream *response, + const char *fieldname, + const struct short_channel_id *id); + +/* JSON serialize a network address for a node */ +void json_add_address(struct json_stream *response, const char *fieldname, + const struct wireaddr *addr); + +/* JSON serialize a network address for a node. */ +void json_add_address_internal(struct json_stream *response, + const char *fieldname, + const struct wireaddr_internal *addr); + +/* Adds both a 'raw' number field and an 'amount_msat' field */ +void json_add_amount_msat_compat(struct json_stream *result, + struct amount_msat msat, + const char *rawfieldname, + const char *msatfieldname) + NO_NULL_ARGS; + +/* Adds both a 'raw' number field and an 'amount_msat' field */ +void json_add_amount_sat_compat(struct json_stream *result, + struct amount_sat sat, + const char *rawfieldname, + const char *msatfieldname) + NO_NULL_ARGS; + +/* Adds an 'msat' field */ +void json_add_amount_msat_only(struct json_stream *result, + const char *msatfieldname, + struct amount_msat msat) + NO_NULL_ARGS; + +/* Adds an 'msat' field */ +void json_add_amount_sat_only(struct json_stream *result, + const char *msatfieldname, + struct amount_sat sat) + NO_NULL_ARGS; + +/* Adds an 'msat' field */ +void json_add_amount_sat_msat(struct json_stream *result, + const char *msatfieldname, + struct amount_sat sat) + NO_NULL_ARGS; + +/* Adds an 'msat' field, and an older deprecated field. */ +void json_add_amount_sats_deprecated(struct json_stream *result, + const char *fieldname, + const char *msatfieldname, + struct amount_sat sat) + NO_NULL_ARGS; + +/* This is used to create requests, *never* for output (output is always + * msat!) */ +void json_add_sats(struct json_stream *result, + const char *fieldname, + struct amount_sat sat) + NO_NULL_ARGS; + +void json_add_sha256(struct json_stream *result, const char *fieldname, + const struct sha256 *hash); + +void json_add_preimage(struct json_stream *result, const char *fieldname, + const struct preimage *preimage); + +/* '"fieldname" : "010000000001..."' or "010000000001..." if fieldname is NULL */ +void json_add_tx(struct json_stream *result, + const char *fieldname, + const struct bitcoin_tx *tx); + +/* '"fieldname" : "cHNidP8BAJoCAAAAAljo..." or "cHNidP8BAJoCAAAAAljo..." if fieldname is NULL */ +void json_add_psbt(struct json_stream *stream, + const char *fieldname, + const struct wally_psbt *psbt); + +/* Add fields from the lease_rates to a json stream. + * Note that field names are set */ +void json_add_lease_rates(struct json_stream *result, + const struct lease_rates *rates); #endif /* LIGHTNING_COMMON_JSON_STREAM_H */ diff --git a/common/param.c b/common/param.c deleted file mode 100644 index c591baf4fd03..000000000000 --- a/common/param.c +++ /dev/null @@ -1,369 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include -#include - -struct param { - const char *name; - bool is_set; - enum param_style style; - param_cbx cbx; - void *arg; -}; - -static bool param_add(struct param **params, - const char *name, - enum param_style style, - param_cbx cbx, void *arg) -{ -#if DEVELOPER - if (!(name && cbx && arg)) - return false; -#endif - struct param last; - - last.is_set = false; - last.name = name; - last.style = style; - last.cbx = cbx; - last.arg = arg; - - tal_arr_expand(params, last); - return true; -} - -/* FIXME: To support the deprecated p_req_dup_ok */ -static bool is_required(enum param_style style) -{ - return style == PARAM_REQUIRED || style == PARAM_REQUIRED_ALLOW_DUPS; -} - -static struct command_result *make_callback(struct command *cmd, - struct param *def, - const char *buffer, - const jsmntok_t *tok) -{ - /* If it had a default, free that now to avoid leak */ - if (def->style == PARAM_OPTIONAL_WITH_DEFAULT && !def->is_set) - tal_free(*(void **)def->arg); - - def->is_set = true; - - return def->cbx(cmd, def->name, buffer, tok, def->arg); -} - -static struct command_result *post_check(struct command *cmd, - struct param *params) -{ - struct param *first = params; - struct param *last = first + tal_count(params); - - /* Make sure required params were provided. */ - while (first != last && is_required(first->style)) { - if (!first->is_set) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "missing required parameter: %s", - first->name); - } - first++; - } - return NULL; -} - -static struct command_result *parse_by_position(struct command *cmd, - struct param *params, - const char *buffer, - const jsmntok_t tokens[], - bool allow_extra) -{ - struct command_result *res; - const jsmntok_t *tok; - size_t i; - - json_for_each_arr(i, tok, tokens) { - /* check for unexpected trailing params */ - if (i == tal_count(params)) { - if (!allow_extra) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "too many parameters:" - " got %u, expected %zu", - tokens->size, - tal_count(params)); - } - break; - } - - if (!json_tok_is_null(buffer, tok)) { - res = make_callback(cmd, params+i, buffer, tok); - if (res) - return res; - } - } - - return post_check(cmd, params); -} - -static struct param *find_param(struct param *params, const char *start, - size_t n) -{ - struct param *first = params; - struct param *last = first + tal_count(params); - - while (first != last) { - size_t arglen = strcspn(first->name, "|"); - if (memeq(first->name, arglen, start, n)) - return first; - if (deprecated_apis - && first->name[arglen] - && memeq(first->name + arglen + 1, - strlen(first->name + arglen + 1), - start, n)) - return first; - first++; - } - return NULL; -} - -static struct command_result *parse_by_name(struct command *cmd, - struct param *params, - const char *buffer, - const jsmntok_t tokens[], - bool allow_extra) -{ - size_t i; - const jsmntok_t *t; - - json_for_each_obj(i, t, tokens) { - struct param *p = find_param(params, buffer + t->start, - t->end - t->start); - if (!p) { - if (!allow_extra) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "unknown parameter: %.*s, this may be caused by a failure to autodetect key=value-style parameters. Please try using the -k flag and explicit key=value pairs of parameters.", - t->end - t->start, - buffer + t->start); - } - } else { - struct command_result *res; - - if (p->is_set) { - if (p->style == PARAM_REQUIRED_ALLOW_DUPS) - continue; - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "duplicate json names: %s", - p->name); - } - - res = make_callback(cmd, p, buffer, t + 1); - if (res) - return res; - } - } - return post_check(cmd, params); -} - -#if DEVELOPER -static int comp_by_name(const struct param *a, const struct param *b, - void *unused) -{ - return strcmp(a->name, b->name); -} - -static int comp_by_arg(const struct param *a, const struct param *b, - void *unused) -{ - /* size_t could be larger than int: don't turn a 4bn difference into 0 */ - if (a->arg > b->arg) - return 1; - else if (a->arg < b->arg) - return -1; - return 0; -} - -/* This comparator is a bit different, but works well. - * Return 0 if @a is optional and @b is required. Otherwise return 1. - */ -static int comp_req_order(const struct param *a, const struct param *b, - void *unused) -{ - if (!is_required(a->style) && is_required(b->style)) - return 0; - return 1; -} - -/* - * Make sure 2 sequential items in @params are not equal (based on - * provided comparator). - */ -static bool check_distinct(struct param *params, - int (*compar) (const struct param *a, - const struct param *b, void *unused)) -{ - struct param *first = params; - struct param *last = first + tal_count(params); - first++; - while (first != last) { - if (compar(first - 1, first, NULL) == 0) - return false; - first++; - } - return true; -} - -static bool check_unique(struct param *copy, - int (*compar) (const struct param *a, - const struct param *b, void *unused)) -{ - asort(copy, tal_count(copy), compar, NULL); - return check_distinct(copy, compar); -} - -/* - * Verify consistent internal state. - */ -static bool check_params(struct param *params) -{ - if (tal_count(params) < 2) - return true; - - /* make sure there are no required params following optional */ - if (!check_distinct(params, comp_req_order)) - return false; - - /* duplicate so we can sort */ - struct param *copy = tal_dup_talarr(params, struct param, params); - - /* check for repeated names and args */ - if (!check_unique(copy, comp_by_name)) - return false; - if (!check_unique(copy, comp_by_arg)) - return false; - - tal_free(copy); - return true; -} -#endif - -static char *param_usage(const tal_t *ctx, - const struct param *params) -{ - char *usage = tal_strdup(ctx, ""); - for (size_t i = 0; i < tal_count(params); i++) { - /* Don't print |deprecated part! */ - int len = strcspn(params[i].name, "|"); - if (i != 0) - tal_append_fmt(&usage, " "); - if (is_required(params[i].style)) - tal_append_fmt(&usage, "%.*s", len, params[i].name); - else - tal_append_fmt(&usage, "[%.*s]", len, params[i].name); - } - return usage; -} - -static struct command_result *param_arr(struct command *cmd, const char *buffer, - const jsmntok_t tokens[], - struct param *params, - bool allow_extra) -{ -#if DEVELOPER - if (!check_params(params)) { - return command_fail(cmd, PARAM_DEV_ERROR, - "developer error: check_params"); - } -#endif - if (tokens->type == JSMN_ARRAY) - return parse_by_position(cmd, params, buffer, tokens, allow_extra); - else if (tokens->type == JSMN_OBJECT) - return parse_by_name(cmd, params, buffer, tokens, allow_extra); - - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Expected array or object for params"); -} - -const char *param_subcommand(struct command *cmd, const char *buffer, - const jsmntok_t tokens[], - const char *name, ...) -{ - va_list ap; - struct param *params = tal_arr(cmd, struct param, 0); - const char *arg, **names = tal_arr(tmpctx, const char *, 1); - const char *subcmd; - - param_add(¶ms, "subcommand", PARAM_REQUIRED, (void *)param_string, &subcmd); - names[0] = name; - va_start(ap, name); - while ((arg = va_arg(ap, const char *)) != NULL) - tal_arr_expand(&names, arg); - va_end(ap); - - if (command_usage_only(cmd)) { - char *usage = tal_strdup(cmd, "subcommand"); - for (size_t i = 0; i < tal_count(names); i++) - tal_append_fmt(&usage, "%c%s", - i == 0 ? '=' : '|', names[i]); - command_set_usage(cmd, usage); - return NULL; - } - - /* Check it's valid */ - if (param_arr(cmd, buffer, tokens, params, true) != NULL) { - return NULL; - } - - /* Check it's one of the known ones. */ - for (size_t i = 0; i < tal_count(names); i++) - if (streq(subcmd, names[i])) - return subcmd; - - /* We really do ignore this. */ - struct command_result *ignore; - ignore = command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Unknown subcommand '%s'", subcmd); - assert(ignore); - return NULL; -} - -bool param(struct command *cmd, const char *buffer, - const jsmntok_t tokens[], ...) -{ - struct param *params = tal_arr(tmpctx, struct param, 0); - const char *name; - va_list ap; - bool allow_extra = false; - - va_start(ap, tokens); - while ((name = va_arg(ap, const char *)) != NULL) { - enum param_style style = va_arg(ap, enum param_style); - param_cbx cbx = va_arg(ap, param_cbx); - void *arg = va_arg(ap, void *); - if (streq(name, "")) { - allow_extra = true; - continue; - } - if (!param_add(¶ms, name, style, cbx, arg)) { - /* We really do ignore this return! */ - struct command_result *ignore; - ignore = command_fail(cmd, PARAM_DEV_ERROR, - "developer error: param_add %s", name); - assert(ignore); - va_end(ap); - return false; - } - } - va_end(ap); - - if (command_usage_only(cmd)) { - command_set_usage(cmd, param_usage(cmd, params)); - return false; - } - - /* Always return false if we're simply checking command parameters; - * normally this returns true if all parameters are valid. */ - return param_arr(cmd, buffer, tokens, params, allow_extra) == NULL - && !command_check_only(cmd); -} diff --git a/common/param.h b/common/param.h deleted file mode 100644 index 6db4e80dd9e9..000000000000 --- a/common/param.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef LIGHTNING_COMMON_PARAM_H -#define LIGHTNING_COMMON_PARAM_H -#include "config.h" -#include - -/*~ Greetings adventurer! - * - * Do you want to automatically validate json input and unmarshal it into - * local variables, all using typesafe callbacks? And on error, - * call command_fail with a proper error message? Then you've come to the - * right place! - * - * Here is a simple example of using the system: - * - * unsigned *cltv; - * u64 *msatoshi; - * const jsmntok_t *note; - * u64 *expiry; - * - * if (!param(cmd, buffer, params, - * p_req("cltv", json_tok_number, &cltv), - * p_opt("amount_msat|msatoshi", json_tok_u64, &msatoshi), - * p_opt("note", json_tok_tok, ¬e), - * p_opt_def("expiry", json_tok_u64, &expiry, 3600), - * NULL)) - * return; - * - * If param() returns true then you're good to go. - * - * All the command handlers throughout the code use this system. - * json_invoice() is a great example. The common callbacks can be found in - * common/json_tok.c. Use them directly or feel free to write your own. - */ -struct command; - -/* A dummy type returned by command_ functions, to ensure you return them - * immediately */ -struct command_result; - -/* - * Parse the json tokens. @params can be an array of values or an object - * of named values. - */ -bool param(struct command *cmd, const char *buffer, - const jsmntok_t params[], ...) LAST_ARG_NULL; - -/* - * The callback signature. - * - * Callbacks must return NULL on success. On failure they - * must return command_fail(...). - */ -typedef struct command_result *(*param_cbx)(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - void **arg); - -/** - * Parse the first json value. - * - * name...: NULL-terminated array of valid values. - * - * Returns subcommand: if it returns NULL if you should return - * command_param_failed() immediately. - */ -const char *param_subcommand(struct command *cmd, const char *buffer, - const jsmntok_t tokens[], - const char *name, ...) LAST_ARG_NULL; - -enum param_style { - PARAM_REQUIRED, - PARAM_REQUIRED_ALLOW_DUPS, - PARAM_OPTIONAL, - PARAM_OPTIONAL_WITH_DEFAULT, -}; - -/* - * Add a required parameter. - * name can be | if it's been renamed. - */ -#define p_req(name, cbx, arg) \ - name"", \ - PARAM_REQUIRED, \ - (param_cbx)(cbx), \ - (arg) + 0*sizeof((cbx)((struct command *)NULL, \ - (const char *)NULL, \ - (const char *)NULL, \ - (const jsmntok_t *)NULL, \ - (arg)) == (struct command_result *)NULL) - -/* - * Add an optional parameter. *arg is set to NULL if it isn't found. - * name can be | if it's been renamed. - */ -#define p_opt(name, cbx, arg) \ - name"", \ - PARAM_OPTIONAL, \ - (param_cbx)(cbx), \ - ({ *arg = NULL; \ - (arg) + 0*sizeof((cbx)((struct command *)NULL, \ - (const char *)NULL, \ - (const char *)NULL, \ - (const jsmntok_t *)NULL, \ - (arg)) == (struct command_result *)NULL); }) - -/* - * Add an required parameter, like p_req, but ignore duplicates. - */ -#define p_req_dup_ok(name, cbx, arg) \ - name"", \ - PARAM_REQUIRED_ALLOW_DUPS, \ - (param_cbx)(cbx), \ - ({ *arg = NULL; \ - (arg) + 0*sizeof((cbx)((struct command *)NULL, \ - (const char *)NULL, \ - (const char *)NULL, \ - (const jsmntok_t *)NULL, \ - (arg)) == (struct command_result *)NULL); }) - -/* - * Add an optional parameter. *arg is set to @def if it isn't found. - * name can be | if it's been renamed. - */ -#define p_opt_def(name, cbx, arg, def) \ - name"", \ - PARAM_OPTIONAL_WITH_DEFAULT, \ - (param_cbx)(cbx), \ - ({ (*arg) = tal((cmd), typeof(**arg)); \ - (**arg) = (def); \ - (arg) + 0*sizeof((cbx)((struct command *)NULL, \ - (const char *)NULL, \ - (const char *)NULL, \ - (const jsmntok_t *)NULL, \ - (arg)) == (struct command_result *)NULL); }) - -/* Special flag for 'check' which allows any parameters. */ -#define p_opt_any() "", PARAM_OPTIONAL, NULL, NULL -#endif /* LIGHTNING_COMMON_PARAM_H */ diff --git a/common/test/Makefile b/common/test/Makefile index 3091105059d5..3304f2f5f763 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -21,8 +21,8 @@ ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) # Sphinx test wants to decode TLVs. common/test/run-sphinx: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o -common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o common/coin_mvt.o -common/test/run-route_blinding_override_test: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o common/coin_mvt.o +common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/coin_mvt.o +common/test/run-route_blinding_override_test: common/base32.o common/wireaddr.o wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/coin_mvt.o common/test/run-param \ common/test/run-json: \ @@ -31,7 +31,6 @@ common/test/run-json: \ common/bigsize.o \ common/channel_id.o \ common/coin_mvt.o \ - common/json.o \ common/json_stream.o \ common/lease_rates.o \ common/node_id.o \ diff --git a/common/test/run-bigsize.c b/common/test/run-bigsize.c index d972f17e22e5..46590bdb7a91 100644 --- a/common/test/run-bigsize.c +++ b/common/test/run-bigsize.c @@ -2,7 +2,8 @@ #include #include #include -#include +#include +#include #include static const char *reason; @@ -74,16 +75,18 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/common/test/run-bolt12_decode.c b/common/test/run-bolt12_decode.c index 42173ebfaa50..348af56bd914 100644 --- a/common/test/run-bolt12_decode.c +++ b/common/test/run-bolt12_decode.c @@ -2,7 +2,8 @@ #include "../bolt12.c" #include "../bech32_util.c" #include "../bech32.c" -#include "../json.c" +#include "../json_parse.c" +#include "../json_parse_simple.c" #include #include #include @@ -89,19 +90,21 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for merkle_tlv */ void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle UNNEEDED) { fprintf(stderr, "merkle_tlv called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } /* Generated stub for sighash_from_merkle */ void sighash_from_merkle(const char *messagename UNNEEDED, const char *fieldname UNNEEDED, diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index ef594f2c2132..495796da496b 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -2,7 +2,7 @@ #include "../amount.c" #include "../bigsize.c" #include "../bolt12_merkle.c" -#include "../json.c" +#include "../json_parse.c" #include "../../wire/fromwire.c" #include "../../wire/tlvstream.c" #if EXPERIMENTAL_FEATURES @@ -28,16 +28,47 @@ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct n /* Generated stub for fromwire_onionmsg_path */ struct onionmsg_path *fromwire_onionmsg_path(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "fromwire_onionmsg_path called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_get_arr */ +const jsmntok_t *json_get_arr(const jsmntok_t tok[] UNNEEDED, size_t index UNNEEDED) +{ fprintf(stderr, "json_get_arr called!\n"); abort(); } +/* Generated stub for json_get_member */ +const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED) +{ fprintf(stderr, "json_get_member called!\n"); abort(); } +/* Generated stub for json_get_membern */ +const jsmntok_t *json_get_membern(const char *buffer UNNEEDED, + const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "json_get_membern called!\n"); abort(); } +/* Generated stub for json_next */ +const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_next called!\n"); abort(); } +/* Generated stub for json_strdup */ +char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_strdup called!\n"); abort(); } +/* Generated stub for json_to_u32 */ +bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint32_t *num UNNEEDED) +{ fprintf(stderr, "json_to_u32 called!\n"); abort(); } +/* Generated stub for json_to_u64 */ +bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint64_t *num UNNEEDED) +{ fprintf(stderr, "json_to_u64 called!\n"); abort(); } +/* Generated stub for json_tok_full */ +const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full called!\n"); abort(); } +/* Generated stub for json_tok_full_len */ +int json_tok_full_len(const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); } +/* Generated stub for json_tok_streq */ +bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "json_tok_streq called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/common/test/run-bolt12_period.c b/common/test/run-bolt12_period.c index 55e6db907112..1b8bd553f4c3 100644 --- a/common/test/run-bolt12_period.c +++ b/common/test/run-bolt12_period.c @@ -1,6 +1,7 @@ #include "config.h" #include "../bolt12.c" -#include "../json.c" +#include "../json_parse.c" +#include "../json_parse_simple.c" #include #include #include @@ -92,19 +93,21 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for merkle_tlv */ void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle UNNEEDED) { fprintf(stderr, "merkle_tlv called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } /* Generated stub for sighash_from_merkle */ void sighash_from_merkle(const char *messagename UNNEEDED, const char *fieldname UNNEEDED, diff --git a/common/test/run-json.c b/common/test/run-json.c index db437a5e734f..d283fbcd5c84 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -1,5 +1,6 @@ #include "config.h" -#include "../json_helpers.c" +#include "../json_parse.c" +#include "../json_parse_simple.c" #include #include #include diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index f821b95f4aeb..e859dbcb429f 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -1,7 +1,9 @@ #include "config.h" #include -#include +#include +#include #include +#include #include /* AUTOGENERATED MOCKS START */ @@ -36,6 +38,22 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for command_check_only */ +bool command_check_only(const struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_check_only called!\n"); abort(); } +/* Generated stub for command_fail */ +struct command_result *command_fail(struct command *cmd UNNEEDED, errcode_t code UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "command_fail called!\n"); abort(); } +/* Generated stub for command_set_usage */ +void command_set_usage(struct command *cmd UNNEEDED, const char *usage UNNEEDED) +{ fprintf(stderr, "command_set_usage called!\n"); abort(); } +/* Generated stub for command_usage_only */ +bool command_usage_only(const struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_usage_only called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } @@ -68,16 +86,75 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_scan */ +const char *json_scan(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + ...) +{ fprintf(stderr, "json_scan called!\n"); abort(); } +/* Generated stub for json_to_channel_id */ +bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct channel_id *cid UNNEEDED) +{ fprintf(stderr, "json_to_channel_id called!\n"); abort(); } +/* Generated stub for json_to_millionths */ +bool json_to_millionths(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + u64 *millionths UNNEEDED) +{ fprintf(stderr, "json_to_millionths called!\n"); abort(); } +/* Generated stub for json_to_msat */ +bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat *msat UNNEEDED) +{ fprintf(stderr, "json_to_msat called!\n"); abort(); } +/* Generated stub for json_to_node_id */ +bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } +/* Generated stub for json_to_outpoint */ +bool json_to_outpoint(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_outpoint *op UNNEEDED) +{ fprintf(stderr, "json_to_outpoint called!\n"); abort(); } +/* Generated stub for json_to_pubkey */ +bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct pubkey *pubkey UNNEEDED) +{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_to_txid */ +bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_txid *txid UNNEEDED) +{ fprintf(stderr, "json_to_txid called!\n"); abort(); } +/* Generated stub for json_to_u16 */ +bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint16_t *num UNNEEDED) +{ fprintf(stderr, "json_to_u16 called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } +/* Generated stub for lease_rates_fromhex */ +struct lease_rates *lease_rates_fromhex(const tal_t *ctx UNNEEDED, + const char *hexdata UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "lease_rates_fromhex called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for segwit_addr_decode */ +int segwit_addr_decode( + int* ver UNNEEDED, + uint8_t* prog UNNEEDED, + size_t* prog_len UNNEEDED, + const char* hrp UNNEEDED, + const char* addr +) +{ fprintf(stderr, "segwit_addr_decode called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/common/test/run-json_scan.c b/common/test/run-json_scan.c index 230a5348d757..7dfdc8247931 100644 --- a/common/test/run-json_scan.c +++ b/common/test/run-json_scan.c @@ -1,5 +1,6 @@ #include "config.h" -#include "../json.c" +#include "../json_parse.c" +#include "../json_parse_simple.c" #include #include #include @@ -68,16 +69,18 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } diff --git a/common/test/run-param.c b/common/test/run-param.c index c3611cfb5fde..07870864970e 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -1,6 +1,7 @@ #include "config.h" -#include "../json_tok.c" -#include "../param.c" +#include "../json_parse.c" +#include "../json_parse_simple.c" +#include "../json_param.c" #include #include #include @@ -42,34 +43,6 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED, const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for json_to_channel_id */ -bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct channel_id *cid UNNEEDED) -{ fprintf(stderr, "json_to_channel_id called!\n"); abort(); } -/* Generated stub for json_to_msat */ -bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct amount_msat *msat UNNEEDED) -{ fprintf(stderr, "json_to_msat called!\n"); abort(); } -/* Generated stub for json_to_node_id */ -bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct node_id *id UNNEEDED) -{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } -/* Generated stub for json_to_outpoint */ -bool json_to_outpoint(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct bitcoin_outpoint *op UNNEEDED) -{ fprintf(stderr, "json_to_outpoint called!\n"); abort(); } -/* Generated stub for json_to_pubkey */ -bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct pubkey *pubkey UNNEEDED) -{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); } -/* Generated stub for json_to_short_channel_id */ -bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct short_channel_id *scid UNNEEDED) -{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } -/* Generated stub for json_to_txid */ -bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct bitcoin_txid *txid UNNEEDED) -{ fprintf(stderr, "json_to_txid called!\n"); abort(); } /* Generated stub for segwit_addr_decode */ int segwit_addr_decode( int* ver UNNEEDED, diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c index 4fbfd01d8df9..4692b73d3610 100644 --- a/common/test/run-route_blinding_override_test.c +++ b/common/test/run-route_blinding_override_test.c @@ -7,8 +7,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -21,9 +20,6 @@ bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_asset_to_sat */ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) { fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } -/* Generated stub for amount_msat */ -struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) -{ fprintf(stderr, "amount_msat called!\n"); abort(); } /* Generated stub for amount_sat */ struct amount_sat amount_sat(u64 satoshis UNNEEDED) { fprintf(stderr, "amount_sat called!\n"); abort(); } @@ -53,14 +49,6 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; -/* Generated stub for fmt_amount_msat */ -const char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED) -{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); } -/* Generated stub for fmt_amount_sat */ -const char *fmt_amount_sat(const tal_t *ctx UNNEEDED, struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "fmt_amount_sat called!\n"); abort(); } /* Generated stub for fromwire_amount_msat */ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } @@ -74,31 +62,34 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for node_id_from_hexstr */ -bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) -{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } -/* Generated stub for parse_amount_msat */ -bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) -{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } -/* Generated stub for parse_amount_sat */ -bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) -{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for json_get_member */ +const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED) +{ fprintf(stderr, "json_get_member called!\n"); abort(); } +/* Generated stub for json_next */ +const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_next called!\n"); abort(); } +/* Generated stub for json_to_pubkey */ +bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct pubkey *pubkey UNNEEDED) +{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); } +/* Generated stub for json_to_secret */ +bool json_to_secret(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct secret *dest UNNEEDED) +{ fprintf(stderr, "json_to_secret called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } +/* Generated stub for json_tok_startswith */ +bool json_tok_startswith(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + const char *prefix UNNEEDED) +{ fprintf(stderr, "json_tok_startswith called!\n"); abort(); } +/* Generated stub for json_tok_streq */ +bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "json_tok_streq called!\n"); abort(); } /* Generated stub for towire_amount_msat */ void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) { fprintf(stderr, "towire_amount_msat called!\n"); abort(); } diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c index 82c0b7f2bbf0..a67c805c7a1a 100644 --- a/common/test/run-route_blinding_test.c +++ b/common/test/run-route_blinding_test.c @@ -7,8 +7,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -21,9 +20,6 @@ bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) /* Generated stub for amount_asset_to_sat */ struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) { fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } -/* Generated stub for amount_msat */ -struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) -{ fprintf(stderr, "amount_msat called!\n"); abort(); } /* Generated stub for amount_sat */ struct amount_sat amount_sat(u64 satoshis UNNEEDED) { fprintf(stderr, "amount_sat called!\n"); abort(); } @@ -53,17 +49,6 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; -/* Generated stub for fmt_amount_msat */ -const char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED) -{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); } -/* Generated stub for fmt_amount_sat */ -const char *fmt_amount_sat(const tal_t *ctx UNNEEDED, struct amount_sat sat UNNEEDED) -{ fprintf(stderr, "fmt_amount_sat called!\n"); abort(); } -/* Generated stub for fmt_wireaddr_without_port */ -char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) -{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } /* Generated stub for fromwire_amount_msat */ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } @@ -77,31 +62,34 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } -/* Generated stub for node_id_from_hexstr */ -bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) -{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } -/* Generated stub for parse_amount_msat */ -bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) -{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } -/* Generated stub for parse_amount_sat */ -bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) -{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for json_get_member */ +const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED) +{ fprintf(stderr, "json_get_member called!\n"); abort(); } +/* Generated stub for json_next */ +const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_next called!\n"); abort(); } +/* Generated stub for json_to_pubkey */ +bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct pubkey *pubkey UNNEEDED) +{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); } +/* Generated stub for json_to_secret */ +bool json_to_secret(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct secret *dest UNNEEDED) +{ fprintf(stderr, "json_to_secret called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } +/* Generated stub for json_tok_startswith */ +bool json_tok_startswith(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + const char *prefix UNNEEDED) +{ fprintf(stderr, "json_tok_startswith called!\n"); abort(); } +/* Generated stub for json_tok_streq */ +bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "json_tok_streq called!\n"); abort(); } /* Generated stub for towire_amount_msat */ void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) { fprintf(stderr, "towire_amount_msat called!\n"); abort(); } diff --git a/devtools/Makefile b/devtools/Makefile index fd3f66a37516..98fd57e6c2ef 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -29,14 +29,13 @@ DEVTOOLS_COMMON_OBJS := \ common/hash_u5.o \ common/hmac.o \ common/htlc_state.o \ + common/json_parse.o \ + common/json_parse_simple.o \ common/memleak.o \ common/node_id.o \ common/per_peer_state.o \ common/psbt_open.o \ common/pseudorand.o \ - common/json.o \ - common/json_helpers.o \ - common/json_stream.o \ common/setup.o \ common/type_to_string.o \ common/utils.o \ diff --git a/devtools/onion.c b/devtools/onion.c index 82e5129cb6a6..0acbbf498d68 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index fe39a0bbba9f..21ef60425ac4 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -17,8 +17,6 @@ GOSSIPD_TEST_COMMON_OBJS := \ common/features.o \ common/hmac.o \ common/node_id.o \ - common/json.o \ - common/json_helpers.o \ common/lease_rates.o \ common/onion.o \ common/pseudorand.o \ @@ -46,6 +44,10 @@ gossipd/test/run-onion_message: \ common/onion.o \ common/sphinx.o \ +# JSON needed for this test +gossipd/test/run-extended-info: \ + common/json_parse.o \ + common/json_parse_simple.o $(GOSSIPD_TEST_PROGRAMS): $(GOSSIPD_TEST_COMMON_OBJS) $(BITCOIN_OBJS) diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index fb810dd46e5c..d095f22b4581 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -59,8 +59,6 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } @@ -94,22 +92,6 @@ void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED, struct list_head *peers UNNEEDED) { fprintf(stderr, "gossip_store_new called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, const tal_t *)){ } diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 0a53fe4b523e..b7245bee3975 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -27,8 +27,6 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, /* Generated stub for daemon_conn_send */ void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "daemon_conn_send called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } @@ -64,22 +62,6 @@ u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *updat u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED) { fprintf(stderr, "handle_node_announcement called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index f02f52d9d934..0420958edd28 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -45,8 +45,6 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } @@ -90,22 +88,6 @@ u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *updat u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED) { fprintf(stderr, "handle_node_announcement called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 142ba69cdc09..d69b852f7476 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -45,8 +45,6 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } @@ -67,22 +65,6 @@ const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, struct gossip_store *gs UNNEEDED, u64 offset UNNEEDED) { fprintf(stderr, "gossip_store_get called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index 21255cad152e..8eeb85107a01 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -26,27 +26,9 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, const struct sha256 *h UNNEEDED, struct pubkey *next UNNEEDED) { fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } /* Generated stub for new_onionreply */ struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) { fprintf(stderr, "new_onionreply called!\n"); abort(); } diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 040e12f915b5..239cd6c9eb56 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -30,8 +30,6 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } -/* Generated stub for deprecated_apis */ -bool deprecated_apis; /* Generated stub for ecdh */ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) { fprintf(stderr, "ecdh called!\n"); abort(); } @@ -61,22 +59,6 @@ const u8 *gossip_store_get_private_update(const tal_t *ctx UNNEEDED, void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, const struct short_channel_id *scid UNNEEDED) { fprintf(stderr, "gossip_store_mark_channel_deleted called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } -/* Generated stub for json_object_end */ -void json_object_end(struct json_stream *js UNNEEDED) -{ fprintf(stderr, "json_object_end called!\n"); abort(); } -/* Generated stub for json_object_start */ -void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) -{ fprintf(stderr, "json_object_start called!\n"); abort(); } /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, const tal_t *)){ } diff --git a/lightningd/Makefile b/lightningd/Makefile index cf997370ce96..559387d2f898 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -101,17 +101,16 @@ LIGHTNINGD_COMMON_OBJS := \ common/htlc_wire.o \ common/key_derive.o \ common/keyset.o \ - common/json.o \ - common/json_helpers.o \ + common/json_param.o \ + common/json_parse.o \ + common/json_parse_simple.o \ common/json_stream.o \ - common/json_tok.o \ common/lease_rates.o \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ common/onion.o \ common/onionreply.o \ - common/param.o \ common/penalty_base.o \ common/per_peer_state.o \ common/permute_tx.o \ diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 711121a133fa..72472ccb09d2 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 9b0d894d472f..e85f3696fb1d 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -7,8 +7,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/lightningd/channel.c b/lightningd/channel.c index 06a7c63615e5..76b6a34eda84 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 9c908b58645c..acf9fa13e410 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -2,10 +2,9 @@ #include #include #include -#include -#include +#include +#include #include -#include #include #include #include diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 71f39bca876c..fbbf8d426050 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -12,9 +12,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index a6f3db94d00a..3ef116a7622a 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -4,10 +4,8 @@ #include #include #include -#include -#include +#include #include -#include #include #include #include @@ -17,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/datastore.c b/lightningd/datastore.c index 74b8a7cfce72..828f22047bc7 100644 --- a/lightningd/datastore.c +++ b/lightningd/datastore.c @@ -1,8 +1,8 @@ #include "config.h" #include #include -#include -#include +#include +#include #include #include diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index cac1152aae8c..9156f027c487 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -9,9 +9,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 0de65d194914..0dfd9f32cbde 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -3,9 +3,8 @@ #include #include #include -#include -#include -#include +#include +#include #include #include #include diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index 5145f90956a3..d74eee2ddc31 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -5,9 +5,8 @@ #include #include #include -#include -#include -#include +#include +#include #include #include #include diff --git a/lightningd/invoice.c b/lightningd/invoice.c index affd55edfd3d..a0097cb61200 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -9,11 +9,9 @@ #include #include #include -#include -#include +#include #include #include -#include #include #include #include diff --git a/lightningd/json.c b/lightningd/json.c index 93e30f2b8b7e..b420e47d47e4 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -1,8 +1,8 @@ #include "config.h" +#include #include #include -#include -#include +#include #include #include diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 6cba43a34792..f9ece1ce4091 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -22,10 +22,8 @@ #include #include #include -#include -#include +#include #include -#include #include #include #include diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 1455891d49b3..8f090801e464 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -3,7 +3,6 @@ #include "config.h" #include #include -#include #include #include diff --git a/lightningd/log.c b/lightningd/log.c index 362eedce3ffc..11a66a092946 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -7,9 +7,8 @@ #include #include #include -#include +#include #include -#include #include #include #include diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 952355bf8d0e..d0deabaa481e 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -4,8 +4,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/lightningd/notification.c b/lightningd/notification.c index 79f41980ce21..8f1528a67f65 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -1,6 +1,5 @@ #include "config.h" #include -#include #include #include #include diff --git a/lightningd/offer.c b/lightningd/offer.c index c97ff9b4e844..f80a7b39c442 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -5,9 +5,8 @@ #include #include #include -#include -#include -#include +#include +#include #include #include #include diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index ae05c7c8fba6..42f61e2fc788 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -2,9 +2,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index ab656fc82086..e21f61bcf0a6 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -9,10 +9,8 @@ #include #include #include -#include -#include +#include #include -#include #include #include #include diff --git a/lightningd/options.c b/lightningd/options.c index b195acb6c922..590f17e1900f 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -12,9 +12,7 @@ #include #include #include -#include -#include -#include +#include #include #include #include diff --git a/lightningd/pay.c b/lightningd/pay.c index f13f3a16de36..e6c3c9c1a77c 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -3,11 +3,9 @@ #include #include #include -#include -#include +#include #include #include -#include #include #include #include diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index bfd2e5af6f99..d265eb2aa5bc 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -20,11 +20,9 @@ #include #include #include -#include -#include +#include #include #include -#include #include #include #include diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index be631a8ea31c..ae4dd2ecd5cb 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index be84af86d568..db062591a43f 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -6,11 +6,9 @@ #include #include #include -#include -#include +#include #include #include -#include #include #include #include diff --git a/lightningd/ping.c b/lightningd/ping.c index 144dbe8fac47..b637f739bd56 100644 --- a/lightningd/ping.c +++ b/lightningd/ping.c @@ -1,7 +1,6 @@ #include "config.h" #include -#include -#include +#include #include #include #include diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 9dcbd3aacbf2..f69258633b2c 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/plugin_control.c b/lightningd/plugin_control.c index d60b397c8208..743584438c2d 100644 --- a/lightningd/plugin_control.c +++ b/lightningd/plugin_control.c @@ -2,9 +2,8 @@ #include #include #include -#include +#include #include -#include #include #include #include diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index 9b3e83cd181e..afdffca88da5 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include diff --git a/lightningd/routehint.c b/lightningd/routehint.c index 86193904bcd7..ce15ee27cd08 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -1,6 +1,6 @@ #include "config.h" #include -#include +#include #include #include #include diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index c2236d5cc74e..2624c582c9a0 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -1,9 +1,7 @@ #include "config.h" #include #include -#include -#include -#include +#include #include #include #include diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index 660a7aeee067..f9b10b4662c7 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -15,7 +15,7 @@ LIGHTNINGD_TEST_COMMON_OBJS := \ common/daemon_conn.o \ common/htlc_state.o \ common/htlc_wire.o \ - common/json.o \ + common/json_parse_simple.o \ common/key_derive.o \ common/pseudorand.o \ common/random_select.o \ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index b8d9fed13c1c..278c6d0c7b87 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -111,16 +111,6 @@ void htlcs_notify_new_block(struct lightningd *ld UNNEEDED, u32 height UNNEEDED) void htlcs_resubmit(struct lightningd *ld UNNEEDED, struct htlc_in_map *unconnected_htlcs_in UNNEEDED) { fprintf(stderr, "htlcs_resubmit called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for jsonrpc_listen */ void jsonrpc_listen(struct jsonrpc *rpc UNNEEDED, struct lightningd *ld UNNEEDED) { fprintf(stderr, "jsonrpc_listen called!\n"); abort(); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index fab4c8a481a6..b6c34c78f88d 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -331,23 +331,38 @@ void json_add_amount_sat_msat(struct json_stream *result UNNEEDED, void json_add_bolt11(struct json_stream *response UNNEEDED, const struct bolt11 *b11 UNNEEDED) { fprintf(stderr, "json_add_bolt11 called!\n"); abort(); } +/* Generated stub for json_add_bool */ +void json_add_bool(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + bool value UNNEEDED) +{ fprintf(stderr, "json_add_bool called!\n"); abort(); } +/* Generated stub for json_add_escaped_string */ +void json_add_escaped_string(struct json_stream *result UNNEEDED, + const char *fieldname UNNEEDED, + const struct json_escape *esc TAKES UNNEEDED) +{ fprintf(stderr, "json_add_escaped_string called!\n"); abort(); } +/* Generated stub for json_add_hex_talarr */ +void json_add_hex_talarr(struct json_stream *result UNNEEDED, + const char *fieldname UNNEEDED, + const tal_t *data UNNEEDED) +{ fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); } +/* Generated stub for json_add_invstring */ +void json_add_invstring(struct json_stream *result UNNEEDED, const char *invstring UNNEEDED) +{ fprintf(stderr, "json_add_invstring called!\n"); abort(); } /* Generated stub for json_add_log */ void json_add_log(struct json_stream *result UNNEEDED, const struct log_book *lr UNNEEDED, const struct node_id *node_id UNNEEDED, enum log_level minlevel UNNEEDED) { fprintf(stderr, "json_add_log called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } /* Generated stub for json_add_node_id */ void json_add_node_id(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "json_add_node_id called!\n"); abort(); } +/* Generated stub for json_add_num */ +void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + unsigned int value UNNEEDED) +{ fprintf(stderr, "json_add_num called!\n"); abort(); } /* Generated stub for json_add_preimage */ void json_add_preimage(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const struct preimage *preimage UNNEEDED) @@ -366,6 +381,18 @@ void json_add_short_channel_id(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, const struct short_channel_id *id UNNEEDED) { fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); } +/* Generated stub for json_add_string */ +void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) +{ fprintf(stderr, "json_add_string called!\n"); abort(); } +/* Generated stub for json_add_stringn */ +void json_add_stringn(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + const char *value TAKES UNNEEDED, size_t value_len UNNEEDED) +{ fprintf(stderr, "json_add_stringn called!\n"); abort(); } +/* Generated stub for json_add_timeiso */ +void json_add_timeiso(struct json_stream *result UNNEEDED, + const char *fieldname UNNEEDED, + struct timeabs *time UNNEEDED) +{ fprintf(stderr, "json_add_timeiso called!\n"); abort(); } /* Generated stub for json_add_tx */ void json_add_tx(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, @@ -375,6 +402,14 @@ void json_add_tx(struct json_stream *result UNNEEDED, void json_add_txid(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "json_add_txid called!\n"); abort(); } +/* Generated stub for json_add_u32 */ +void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + uint32_t value UNNEEDED) +{ fprintf(stderr, "json_add_u32 called!\n"); abort(); } +/* Generated stub for json_add_u64 */ +void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + uint64_t value UNNEEDED) +{ fprintf(stderr, "json_add_u64 called!\n"); abort(); } /* Generated stub for json_add_uncommitted_channel */ void json_add_uncommitted_channel(struct json_stream *response UNNEEDED, const struct uncommitted_channel *uc UNNEEDED) @@ -389,16 +424,19 @@ void json_array_end(struct json_stream *js UNNEEDED) /* Generated stub for json_array_start */ void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_array_start called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for json_object_end */ void json_object_end(struct json_stream *js UNNEEDED) { fprintf(stderr, "json_object_end called!\n"); abort(); } /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for json_scan */ +const char *json_scan(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + ...) +{ fprintf(stderr, "json_scan called!\n"); abort(); } /* Generated stub for json_stream_fail */ struct json_stream *json_stream_fail(struct command *cmd UNNEEDED, errcode_t code UNNEEDED, @@ -421,10 +459,21 @@ bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } /* Generated stub for json_to_short_channel_id */ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct short_channel_id *scid UNNEEDED) { fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_to_u16 */ +bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint16_t *num UNNEEDED) +{ fprintf(stderr, "json_to_u16 called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } /* Generated stub for json_tok_channel_id */ bool json_tok_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct channel_id *cid UNNEEDED) diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 87090fb7ce9f..a3d15eaa7b01 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -30,10 +30,9 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for json_add_sha256 */ -void json_add_sha256(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, - const struct sha256 *hash UNNEEDED) -{ fprintf(stderr, "json_add_sha256 called!\n"); abort(); } +/* Generated stub for json_to_errcode */ +bool json_to_errcode(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, errcode_t *errcode UNNEEDED) +{ fprintf(stderr, "json_to_errcode called!\n"); abort(); } /* Generated stub for json_to_pubkey */ bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct pubkey *pubkey UNNEEDED) diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index 3db0a81dc676..9449cbc82ac3 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -27,6 +27,11 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for json_add_hex_talarr */ +void json_add_hex_talarr(struct json_stream *result UNNEEDED, + const char *fieldname UNNEEDED, + const tal_t *data UNNEEDED) +{ fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); } /* Generated stub for json_add_member */ void json_add_member(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED, @@ -38,16 +43,23 @@ void json_add_node_id(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "json_add_node_id called!\n"); abort(); } +/* Generated stub for json_add_num */ +void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + unsigned int value UNNEEDED) +{ fprintf(stderr, "json_add_num called!\n"); abort(); } +/* Generated stub for json_add_string */ +void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) +{ fprintf(stderr, "json_add_string called!\n"); abort(); } +/* Generated stub for json_add_time */ +void json_add_time(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, + struct timespec ts UNNEEDED) +{ fprintf(stderr, "json_add_time called!\n"); abort(); } /* Generated stub for json_array_end */ void json_array_end(struct json_stream *js UNNEEDED) { fprintf(stderr, "json_array_end called!\n"); abort(); } /* Generated stub for json_array_start */ void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_array_start called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for json_object_end */ void json_object_end(struct json_stream *js UNNEEDED) { fprintf(stderr, "json_object_end called!\n"); abort(); } diff --git a/lightningd/test/run-shuffle_fds.c b/lightningd/test/run-shuffle_fds.c index 0981f69a3dba..83b0e3a52836 100644 --- a/lightningd/test/run-shuffle_fds.c +++ b/lightningd/test/run-shuffle_fds.c @@ -75,16 +75,6 @@ bool fromwire_status_peer_error(const tal_t *ctx UNNEEDED, const void *p UNNEEDE /* Generated stub for fromwire_status_version */ bool fromwire_status_version(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, wirestring **version UNNEEDED) { fprintf(stderr, "fromwire_status_version called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } -/* Generated stub for json_member_direct */ -char *json_member_direct(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, size_t extra UNNEEDED) -{ fprintf(stderr, "json_member_direct called!\n"); abort(); } /* Generated stub for log_ */ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const struct node_id *node_id UNNEEDED, diff --git a/plugins/Makefile b/plugins/Makefile index c69297569a5a..7b32bb82aea2 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -134,14 +134,13 @@ PLUGIN_COMMON_OBJS := \ common/daemon.o \ common/features.o \ common/hash_u5.o \ - common/json.o \ - common/json_helpers.o \ + common/json_param.o \ + common/json_parse.o \ + common/json_parse_simple.o \ common/json_stream.o \ - common/json_tok.o \ common/lease_rates.o \ common/memleak.o \ common/node_id.o \ - common/param.o \ common/psbt_open.o \ common/pseudorand.o \ common/random_select.o \ diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 360848c159dc..c4d29fc02727 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -1,6 +1,7 @@ #include "config.h" #include -#include +#include +#include #include static u64 cycle_seconds = 0, expired_by = 86400; diff --git a/plugins/bcli.c b/plugins/bcli.c index 514aa0f621b5..ea98050ffb9c 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -6,7 +6,8 @@ #include #include #include -#include +#include +#include #include #include #include diff --git a/plugins/chanbackup.c b/plugins/chanbackup.c index 85637ba2f0d3..606a96ab7cd0 100644 --- a/plugins/chanbackup.c +++ b/plugins/chanbackup.c @@ -8,9 +8,8 @@ #include #include #include -#include +#include #include -#include #include #include #include diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 088affb61ecb..b7f3abe70cec 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -10,8 +10,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/plugins/funder.c b/plugins/funder.c index 7f532fcd814a..e4c1e0a1e4d4 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -13,8 +13,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/plugins/keysend.c b/plugins/keysend.c index 17d8ccede87a..47fdc8fd5903 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -1,9 +1,11 @@ #include "config.h" +#include #include #include #include #include -#include +#include +#include #include #include #include diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 553ce84489aa..feb6b4d8162e 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -3,6 +3,7 @@ #define LIGHTNING_PLUGINS_LIBPLUGIN_H #include "config.h" +#include #include #include #include @@ -11,12 +12,9 @@ #include #include #include -#include #include -#include #include #include -#include #include #include diff --git a/plugins/offers.c b/plugins/offers.c index bf37740a91aa..5ca4ea4d7760 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/plugins/offers.h b/plugins/offers.h index 68feefa12d69..fd264d3c2801 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -1,7 +1,6 @@ #ifndef LIGHTNING_PLUGINS_OFFERS_H #define LIGHTNING_PLUGINS_OFFERS_H #include "config.h" -#include struct command_result; struct command; diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index 3e24333629bf..146750e9424c 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include diff --git a/plugins/offers_offer.c b/plugins/offers_offer.c index a7bebf17d529..ee096883071b 100644 --- a/plugins/offers_offer.c +++ b/plugins/offers_offer.c @@ -4,7 +4,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/plugins/pay.c b/plugins/pay.c index 0ccde645b2fe..0e309b33be16 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -10,8 +10,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index 5b235f911380..51332c6c186b 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -1,7 +1,7 @@ #include "config.h" #include +#include #include -#include #include static struct command_result * diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 4d0c2c75be33..b721d73c46d9 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -4,8 +4,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/plugins/spender/multiwithdraw.c b/plugins/spender/multiwithdraw.c index b06d097562af..4bd7605f0bc2 100644 --- a/plugins/spender/multiwithdraw.c +++ b/plugins/spender/multiwithdraw.c @@ -4,8 +4,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/plugins/topology.c b/plugins/topology.c index c2ce34ce00a8..dc4f2fc55854 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/plugins/txprepare.c b/plugins/txprepare.c index 85ff84b1abba..2a767d95a3ba 100644 --- a/plugins/txprepare.c +++ b/plugins/txprepare.c @@ -2,8 +2,8 @@ #include #include #include +#include #include -#include #include #include #include diff --git a/tests/plugins/Makefile b/tests/plugins/Makefile index 3f2e6e3aaa27..3b2548221d23 100644 --- a/tests/plugins/Makefile +++ b/tests/plugins/Makefile @@ -8,7 +8,7 @@ $(PLUGIN_TESTLIBPLUGIN_OBJS): $(PLUGIN_LIB_HEADER) PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_SRC := tests/plugins/test_selfdisable_after_getmanifest.c PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS := $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_SRC:.c=.o) -tests/plugins/test_selfdisable_after_getmanifest: bitcoin/chainparams.o $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS) common/autodata.o common/json.o common/json_stream.o common/setup.o common/utils.o $(JSMN_OBJS) $(CCAN_OBJS) +tests/plugins/test_selfdisable_after_getmanifest: bitcoin/chainparams.o $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS) common/autodata.o common/json_parse_simple.o common/setup.o common/utils.o $(JSMN_OBJS) $(CCAN_OBJS) # Make sure these depend on everything. ALL_TEST_PROGRAMS += tests/plugins/test_libplugin tests/plugins/test_selfdisable_after_getmanifest diff --git a/tests/plugins/test_libplugin.c b/tests/plugins/test_libplugin.c index bb786a1b8fcf..a93f975ad45e 100644 --- a/tests/plugins/test_libplugin.c +++ b/tests/plugins/test_libplugin.c @@ -1,7 +1,8 @@ #include "config.h" #include #include -#include +#include +#include #include #include diff --git a/tests/plugins/test_selfdisable_after_getmanifest.c b/tests/plugins/test_selfdisable_after_getmanifest.c index 443aae09bafe..228b4b241e57 100644 --- a/tests/plugins/test_selfdisable_after_getmanifest.c +++ b/tests/plugins/test_selfdisable_after_getmanifest.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/wallet/reservation.c b/wallet/reservation.c index f36622536265..4f40e4b30d0c 100644 --- a/wallet/reservation.c +++ b/wallet/reservation.c @@ -6,10 +6,8 @@ #include #include #include -#include -#include +#include #include -#include #include #include #include diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 88df58ab3f24..e7be2d410c2c 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -6,10 +6,8 @@ #include #include #include -#include -#include +#include #include -#include #include #include #include From 36a29fbfbc3d3e05ff534499d7b082d1fa3caf48 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 13:22:34 +0930 Subject: [PATCH 0990/1530] lightningd/json.h: remove. There are hardly any lightningd-specific JSON functions: all that's left are the feerate ones, and there's already a comment that we should have a lightningd/feerate.h. Signed-off-by: Rusty Russell --- common/json_param.c | 47 ++++---------- common/json_param.h | 10 +-- common/json_parse.c | 9 +++ common/json_parse.h | 2 + lightningd/Makefile | 2 +- lightningd/chaintopology.c | 28 -------- lightningd/chaintopology.h | 22 +------ lightningd/closing_control.c | 1 - lightningd/dual_open_control.c | 1 - lightningd/feerate.c | 114 +++++++++++++++++++++++++++++++++ lightningd/feerate.h | 47 ++++++++++++++ lightningd/hsm_control.c | 1 - lightningd/json.c | 74 --------------------- lightningd/json.h | 54 ---------------- lightningd/onion_message.c | 1 - lightningd/opening_control.c | 1 - lightningd/pay.c | 1 - lightningd/peer_control.c | 1 - lightningd/signmessage.c | 1 - lightningd/test/run-jsonrpc.c | 27 +++----- wallet/reservation.c | 1 - 21 files changed, 200 insertions(+), 245 deletions(-) create mode 100644 lightningd/feerate.c create mode 100644 lightningd/feerate.h delete mode 100644 lightningd/json.c delete mode 100644 lightningd/json.h diff --git a/common/json_param.c b/common/json_param.c index bf4ddc92ee33..85c80d8e0a9e 100644 --- a/common/json_param.c +++ b/common/json_param.c @@ -685,40 +685,6 @@ struct command_result *param_secrets_array(struct command *cmd, return NULL; } -struct command_result *param_feerate_val(struct command *cmd, - const char *name, const char *buffer, - const jsmntok_t *tok, - u32 **feerate_per_kw) -{ - jsmntok_t base = *tok; - enum feerate_style style; - unsigned int num; - - if (json_tok_endswith(buffer, tok, - feerate_style_name(FEERATE_PER_KBYTE))) { - style = FEERATE_PER_KBYTE; - base.end -= strlen(feerate_style_name(FEERATE_PER_KBYTE)); - } else if (json_tok_endswith(buffer, tok, - feerate_style_name(FEERATE_PER_KSIPA))) { - style = FEERATE_PER_KSIPA; - base.end -= strlen(feerate_style_name(FEERATE_PER_KSIPA)); - } else - style = FEERATE_PER_KBYTE; - - if (!json_to_number(buffer, &base, &num)) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be an integer with optional perkw/perkb, not '%.*s'", - name, base.end - base.start, - buffer + base.start); - } - - *feerate_per_kw = tal(cmd, u32); - **feerate_per_kw = feerate_from_style(num, style); - if (**feerate_per_kw < FEERATE_FLOOR) - **feerate_per_kw = FEERATE_FLOOR; - return NULL; -} - /** * segwit_addr_net_decode - Try to decode a Bech32 address and detect * testnet/mainnet/regtest/signet @@ -1093,3 +1059,16 @@ struct command_result *param_lease_hex(struct command *cmd, json_tok_full(buffer, tok)); return NULL; } + +struct command_result *param_pubkey(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct pubkey **pubkey) +{ + *pubkey = tal(cmd, struct pubkey); + if (json_to_pubkey(buffer, tok, *pubkey)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be a compressed pubkey"); +} + diff --git a/common/json_param.h b/common/json_param.h index c08e466ade63..6086d5c2a663 100644 --- a/common/json_param.h +++ b/common/json_param.h @@ -268,11 +268,6 @@ struct command_result *param_secrets_array(struct command *cmd, const jsmntok_t *tok, struct secret **secrets); -struct command_result *param_feerate_val(struct command *cmd, - const char *name, const char *buffer, - const jsmntok_t *tok, - u32 **feerate_per_kw); - struct command_result *param_txid(struct command *cmd, const char *name, const char *buffer, @@ -343,4 +338,9 @@ struct command_result *param_lease_hex(struct command *cmd, const char *buffer, const jsmntok_t *tok, struct lease_rates **rates); + +struct command_result *param_pubkey(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct pubkey **pubkey); + #endif /* LIGHTNING_COMMON_JSON_PARAM_H */ diff --git a/common/json_parse.c b/common/json_parse.c index 74d46f21a6b5..025968d34a41 100644 --- a/common/json_parse.c +++ b/common/json_parse.c @@ -717,3 +717,12 @@ json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) return rpath; } + + +bool +json_tok_channel_id(const char *buffer, const jsmntok_t *tok, + struct channel_id *cid) +{ + return hex_decode(buffer + tok->start, tok->end - tok->start, + cid, sizeof(*cid)); +} diff --git a/common/json_parse.h b/common/json_parse.h index 636ac4d8b90e..bcca914b2624 100644 --- a/common/json_parse.h +++ b/common/json_parse.h @@ -120,6 +120,8 @@ json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); struct tlv_obs2_onionmsg_payload_reply_path * json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); +bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok, + struct channel_id *cid); /* Guide is % for a token: each must be followed by JSON_SCAN(). * Returns NULL on error (asserts() on bad guide). */ diff --git a/lightningd/Makefile b/lightningd/Makefile index 559387d2f898..afea16752fda 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -10,13 +10,13 @@ LIGHTNINGD_SRC := \ lightningd/dual_open_control.c \ lightningd/connect_control.c \ lightningd/onion_message.c \ + lightningd/feerate.c \ lightningd/gossip_control.c \ lightningd/hsm_control.c \ lightningd/htlc_end.c \ lightningd/htlc_set.c \ lightningd/invoice.c \ lightningd/io_loop_with_timers.c \ - lightningd/json.c \ lightningd/jsonrpc.c \ lightningd/lightningd.c \ lightningd/log.c \ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index e85f3696fb1d..597a6a4b8b8d 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -311,33 +310,6 @@ static void watch_for_utxo_reconfirmation(struct chain_topology *topo, } } -const char *feerate_name(enum feerate feerate) -{ - switch (feerate) { - case FEERATE_OPENING: return "opening"; - case FEERATE_MUTUAL_CLOSE: return "mutual_close"; - case FEERATE_UNILATERAL_CLOSE: return "unilateral_close"; - case FEERATE_DELAYED_TO_US: return "delayed_to_us"; - case FEERATE_HTLC_RESOLUTION: return "htlc_resolution"; - case FEERATE_PENALTY: return "penalty"; - case FEERATE_MIN: return "min_acceptable"; - case FEERATE_MAX: return "max_acceptable"; - } - abort(); -} - -struct command_result *param_feerate_estimate(struct command *cmd, - u32 **feerate_per_kw, - enum feerate feerate) -{ - *feerate_per_kw = tal(cmd, u32); - **feerate_per_kw = try_get_feerate(cmd->ld->topology, feerate); - if (!**feerate_per_kw) - return command_fail(cmd, LIGHTNINGD, "Cannot estimate fees"); - - return NULL; -} - /* Mutual recursion via timer. */ static void next_updatefee_timer(struct chain_topology *topo); diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index a9125b1c73e7..65ad3086a64d 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include #include struct bitcoin_tx; @@ -12,20 +13,6 @@ struct lightningd; struct peer; struct txwatch; -/* FIXME: move all feerate stuff out to new lightningd/feerate.[ch] files */ -enum feerate { - /* DO NOT REORDER: force-feerates uses this order! */ - FEERATE_OPENING, - FEERATE_MUTUAL_CLOSE, - FEERATE_UNILATERAL_CLOSE, - FEERATE_DELAYED_TO_US, - FEERATE_HTLC_RESOLUTION, - FEERATE_PENALTY, - FEERATE_MIN, - FEERATE_MAX, -}; -#define NUM_FEERATES (FEERATE_MAX+1) - /* We keep the last three in case there are outliers (for min/max) */ #define FEE_HISTORY_NUM 3 @@ -164,13 +151,6 @@ u32 delayed_to_us_feerate(struct chain_topology *topo); u32 htlc_resolution_feerate(struct chain_topology *topo); u32 penalty_feerate(struct chain_topology *topo); -const char *feerate_name(enum feerate feerate); - -/* Set feerate_per_kw to this estimate & return NULL, or fail cmd */ -struct command_result *param_feerate_estimate(struct command *cmd, - u32 **feerate_per_kw, - enum feerate feerate); - /* Broadcast a single tx, and rebroadcast as reqd (copies tx). * If failed is non-NULL, call that and don't rebroadcast. */ void broadcast_tx(struct chain_topology *topo, diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index fbbf8d426050..47b9e39e4f51 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 9156f027c487..d3b9a0c5dc44 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/feerate.c b/lightningd/feerate.c new file mode 100644 index 000000000000..dd060032187b --- /dev/null +++ b/lightningd/feerate.c @@ -0,0 +1,114 @@ +#include "config.h" +#include +#include +#include +#include +#include + +const char *feerate_name(enum feerate feerate) +{ + switch (feerate) { + case FEERATE_OPENING: return "opening"; + case FEERATE_MUTUAL_CLOSE: return "mutual_close"; + case FEERATE_UNILATERAL_CLOSE: return "unilateral_close"; + case FEERATE_DELAYED_TO_US: return "delayed_to_us"; + case FEERATE_HTLC_RESOLUTION: return "htlc_resolution"; + case FEERATE_PENALTY: return "penalty"; + case FEERATE_MIN: return "min_acceptable"; + case FEERATE_MAX: return "max_acceptable"; + } + abort(); +} + +struct command_result *param_feerate_style(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + enum feerate_style **style) +{ + *style = tal(cmd, enum feerate_style); + if (json_tok_streq(buffer, tok, + feerate_style_name(FEERATE_PER_KSIPA))) { + **style = FEERATE_PER_KSIPA; + return NULL; + } else if (json_tok_streq(buffer, tok, + feerate_style_name(FEERATE_PER_KBYTE))) { + **style = FEERATE_PER_KBYTE; + return NULL; + } + + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be '%s' or '%s', not '%.*s'", + name, + feerate_style_name(FEERATE_PER_KSIPA), + feerate_style_name(FEERATE_PER_KBYTE), + json_tok_full_len(tok), json_tok_full(buffer, tok)); +} + +struct command_result *param_feerate(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + u32 **feerate) +{ + for (size_t i = 0; i < NUM_FEERATES; i++) { + if (json_tok_streq(buffer, tok, feerate_name(i))) + return param_feerate_estimate(cmd, feerate, i); + } + /* We used SLOW, NORMAL, and URGENT as feerate targets previously, + * and many commands rely on this syntax now. + * It's also really more natural for an user interface. */ + if (json_tok_streq(buffer, tok, "slow")) + return param_feerate_estimate(cmd, feerate, FEERATE_MIN); + else if (json_tok_streq(buffer, tok, "normal")) + return param_feerate_estimate(cmd, feerate, FEERATE_OPENING); + else if (json_tok_streq(buffer, tok, "urgent")) + return param_feerate_estimate(cmd, feerate, FEERATE_UNILATERAL_CLOSE); + + /* It's a number... */ + return param_feerate_val(cmd, name, buffer, tok, feerate); +} + +struct command_result *param_feerate_estimate(struct command *cmd, + u32 **feerate_per_kw, + enum feerate feerate) +{ + *feerate_per_kw = tal(cmd, u32); + **feerate_per_kw = try_get_feerate(cmd->ld->topology, feerate); + if (!**feerate_per_kw) + return command_fail(cmd, LIGHTNINGD, "Cannot estimate fees"); + + return NULL; +} + +struct command_result *param_feerate_val(struct command *cmd, + const char *name, const char *buffer, + const jsmntok_t *tok, + u32 **feerate_per_kw) +{ + jsmntok_t base = *tok; + enum feerate_style style; + unsigned int num; + + if (json_tok_endswith(buffer, tok, + feerate_style_name(FEERATE_PER_KBYTE))) { + style = FEERATE_PER_KBYTE; + base.end -= strlen(feerate_style_name(FEERATE_PER_KBYTE)); + } else if (json_tok_endswith(buffer, tok, + feerate_style_name(FEERATE_PER_KSIPA))) { + style = FEERATE_PER_KSIPA; + base.end -= strlen(feerate_style_name(FEERATE_PER_KSIPA)); + } else + style = FEERATE_PER_KBYTE; + + if (!json_to_number(buffer, &base, &num)) { + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "'%s' should be an integer with optional perkw/perkb, not '%.*s'", + name, base.end - base.start, + buffer + base.start); + } + + *feerate_per_kw = tal(cmd, u32); + **feerate_per_kw = feerate_from_style(num, style); + if (**feerate_per_kw < FEERATE_FLOOR) + **feerate_per_kw = FEERATE_FLOOR; + return NULL; +} diff --git a/lightningd/feerate.h b/lightningd/feerate.h new file mode 100644 index 000000000000..80ab365f8d06 --- /dev/null +++ b/lightningd/feerate.h @@ -0,0 +1,47 @@ +#ifndef LIGHTNING_LIGHTNINGD_FEERATE_H +#define LIGHTNING_LIGHTNINGD_FEERATE_H +#include "config.h" +#include +#include + +struct command; + +enum feerate { + /* DO NOT REORDER: force-feerates uses this order! */ + FEERATE_OPENING, + FEERATE_MUTUAL_CLOSE, + FEERATE_UNILATERAL_CLOSE, + FEERATE_DELAYED_TO_US, + FEERATE_HTLC_RESOLUTION, + FEERATE_PENALTY, + FEERATE_MIN, + FEERATE_MAX, +}; +#define NUM_FEERATES (FEERATE_MAX+1) + +const char *feerate_name(enum feerate feerate); + +/* Extract a feerate style. */ +struct command_result *param_feerate_style(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + enum feerate_style **style); + +/* Set feerate_per_kw to this estimate & return NULL, or fail cmd */ +struct command_result *param_feerate_estimate(struct command *cmd, + u32 **feerate_per_kw, + enum feerate feerate); + +/* Extract a feerate with optional style suffix. */ +struct command_result *param_feerate_val(struct command *cmd, + const char *name, const char *buffer, + const jsmntok_t *tok, + u32 **feerate_per_kw); + +/* This also accepts names like "slow" etc */ +struct command_result *param_feerate(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + u32 **feerate); + +#endif /* LIGHTNING_LIGHTNINGD_FEERATE_H */ diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index d74eee2ddc31..c4b2a0690bf4 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/json.c b/lightningd/json.c deleted file mode 100644 index b420e47d47e4..000000000000 --- a/lightningd/json.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "config.h" -#include -#include -#include -#include -#include -#include - -struct command_result *param_pubkey(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - struct pubkey **pubkey) -{ - *pubkey = tal(cmd, struct pubkey); - if (json_to_pubkey(buffer, tok, *pubkey)) - return NULL; - - return command_fail_badparam(cmd, name, buffer, tok, - "should be a compressed pubkey"); -} - -struct command_result *param_feerate_style(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - enum feerate_style **style) -{ - *style = tal(cmd, enum feerate_style); - if (json_tok_streq(buffer, tok, - feerate_style_name(FEERATE_PER_KSIPA))) { - **style = FEERATE_PER_KSIPA; - return NULL; - } else if (json_tok_streq(buffer, tok, - feerate_style_name(FEERATE_PER_KBYTE))) { - **style = FEERATE_PER_KBYTE; - return NULL; - } - - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "'%s' should be '%s' or '%s', not '%.*s'", - name, - feerate_style_name(FEERATE_PER_KSIPA), - feerate_style_name(FEERATE_PER_KBYTE), - json_tok_full_len(tok), json_tok_full(buffer, tok)); -} - -struct command_result *param_feerate(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - u32 **feerate) -{ - for (size_t i = 0; i < NUM_FEERATES; i++) { - if (json_tok_streq(buffer, tok, feerate_name(i))) - return param_feerate_estimate(cmd, feerate, i); - } - /* We used SLOW, NORMAL, and URGENT as feerate targets previously, - * and many commands rely on this syntax now. - * It's also really more natural for an user interface. */ - if (json_tok_streq(buffer, tok, "slow")) - return param_feerate_estimate(cmd, feerate, FEERATE_MIN); - else if (json_tok_streq(buffer, tok, "normal")) - return param_feerate_estimate(cmd, feerate, FEERATE_OPENING); - else if (json_tok_streq(buffer, tok, "urgent")) - return param_feerate_estimate(cmd, feerate, FEERATE_UNILATERAL_CLOSE); - - /* It's a number... */ - return param_feerate_val(cmd, name, buffer, tok, feerate); -} - -bool -json_tok_channel_id(const char *buffer, const jsmntok_t *tok, - struct channel_id *cid) -{ - return hex_decode(buffer + tok->start, tok->end - tok->start, - cid, sizeof(*cid)); -} diff --git a/lightningd/json.h b/lightningd/json.h deleted file mode 100644 index e065570a04bd..000000000000 --- a/lightningd/json.h +++ /dev/null @@ -1,54 +0,0 @@ -/* lightningd/json.h - * Helpers for outputting JSON results that are specific only for - * lightningd. - */ -#ifndef LIGHTNING_LIGHTNINGD_JSON_H -#define LIGHTNING_LIGHTNINGD_JSON_H -#include "config.h" -#include -#include -#include -#include -#include -#include -#include - -#define JSMN_STRICT 1 -# include - -struct bitcoin_txid; -struct chainparams; -struct channel_id; -struct command; -struct json_escape; -struct pubkey; -struct node_id; -struct short_channel_id; - -struct command_result *param_pubkey(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - struct pubkey **pubkey); - -struct command_result *param_short_channel_id(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct short_channel_id **scid); - -/* Extract a feerate style. */ -struct command_result *param_feerate_style(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - enum feerate_style **style); - -const char *json_feerate_style_name(enum feerate_style style); - -/* Extract a feerate with optional style suffix. */ -struct command_result *param_feerate(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - u32 **feerate); - -bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok, - struct channel_id *cid); -#endif /* LIGHTNING_LIGHTNINGD_JSON_H */ diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 42f61e2fc788..b5cdd6d15417 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index e21f61bcf0a6..a89092370247 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/pay.c b/lightningd/pay.c index e6c3c9c1a77c..eae50a53d128 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index d265eb2aa5bc..afb4ea0d2770 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 2624c582c9a0..1e0195a1d8c7 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index a3d15eaa7b01..134fc5e7de97 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -1,7 +1,7 @@ #include "config.h" #include "../../common/json_stream.c" #include "../jsonrpc.c" -#include "../json.c" +#include "../feerate.c" #include #include @@ -17,9 +17,6 @@ bool deprecated_apis; /* Generated stub for fatal */ void fatal(const char *fmt UNNEEDED, ...) { fprintf(stderr, "fatal called!\n"); abort(); } -/* Generated stub for feerate_name */ -const char *feerate_name(enum feerate feerate UNNEEDED) -{ fprintf(stderr, "feerate_name called!\n"); abort(); } /* Generated stub for fromwire_bigsize */ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } @@ -33,10 +30,10 @@ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct n /* Generated stub for json_to_errcode */ bool json_to_errcode(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, errcode_t *errcode UNNEEDED) { fprintf(stderr, "json_to_errcode called!\n"); abort(); } -/* Generated stub for json_to_pubkey */ -bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct pubkey *pubkey UNNEEDED) -{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } /* Generated stub for log_ */ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const struct node_id *node_id UNNEEDED, @@ -73,17 +70,6 @@ struct command_result *param_bool(struct command *cmd UNNEEDED, const char *name const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool **b UNNEEDED) { fprintf(stderr, "param_bool called!\n"); abort(); } -/* Generated stub for param_feerate_estimate */ -struct command_result *param_feerate_estimate(struct command *cmd UNNEEDED, - u32 **feerate_per_kw UNNEEDED, - enum feerate feerate UNNEEDED) -{ fprintf(stderr, "param_feerate_estimate called!\n"); abort(); } -/* Generated stub for param_feerate_val */ -struct command_result *param_feerate_val(struct command *cmd UNNEEDED, - const char *name UNNEEDED, const char *buffer UNNEEDED, - const jsmntok_t *tok UNNEEDED, - u32 **feerate_per_kw UNNEEDED) -{ fprintf(stderr, "param_feerate_val called!\n"); abort(); } /* Generated stub for param_ignore */ struct command_result *param_ignore(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, @@ -122,6 +108,9 @@ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id U /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* Generated stub for try_get_feerate */ +u32 try_get_feerate(const struct chain_topology *topo UNNEEDED, enum feerate feerate UNNEEDED) +{ fprintf(stderr, "try_get_feerate called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static int test_json_filter(void) diff --git a/wallet/reservation.c b/wallet/reservation.c index 4f40e4b30d0c..88097636cbf2 100644 --- a/wallet/reservation.c +++ b/wallet/reservation.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From dbae5ae569c5ee4cc261304842f2453b4429771e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 4 Jul 2022 13:22:35 +0930 Subject: [PATCH 0991/1530] common/json_stream.c: provide explicit json_add_primitive_fmt and json_add_str_fmt routines. Rather than a generic "add member", provide two routines: one which doesn't quote, and one which does. Signed-off-by: Rusty Russell --- common/json_stream.c | 113 +++++++++++++++++------------- common/json_stream.h | 58 ++++++++++----- lightningd/jsonrpc.c | 8 +-- lightningd/log.c | 4 +- lightningd/notification.c | 2 +- lightningd/options.c | 3 +- lightningd/test/run-log-pruning.c | 11 ++- plugins/libplugin.c | 2 +- plugins/offers_invreq_hook.c | 2 +- 9 files changed, 118 insertions(+), 85 deletions(-) diff --git a/common/json_stream.c b/common/json_stream.c index 740defa7c303..f556033525bf 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -128,15 +128,6 @@ void json_stream_flush(struct json_stream *js) io_wake(js); } -char *json_member_direct(struct json_stream *js, - const char *fieldname, size_t extra) -{ - char *dest; - - dest = json_out_member_direct(js->jout, fieldname, extra); - return dest; -} - void json_array_start(struct json_stream *js, const char *fieldname) { json_out_start(js->jout, fieldname, '['); @@ -157,18 +148,55 @@ void json_object_end(struct json_stream *js) json_out_end(js->jout, '}'); } -void json_add_member(struct json_stream *js, - const char *fieldname, - bool quote, - const char *fmt, ...) +void json_add_primitive_fmt(struct json_stream *js, + const char *fieldname, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + json_out_addv(js->jout, fieldname, false, fmt, ap); + va_end(ap); +} + +void json_add_str_fmt(struct json_stream *js, + const char *fieldname, + const char *fmt, ...) { va_list ap; va_start(ap, fmt); - json_out_addv(js->jout, fieldname, quote, fmt, ap); + json_out_addv(js->jout, fieldname, true, fmt, ap); va_end(ap); } +void json_add_primitive(struct json_stream *js, + const char *fieldname, + const char *val TAKES) +{ + json_add_primitive_fmt(js, fieldname, "%s", val); + if (taken(val)) + tal_free(val); +} + +void json_add_string(struct json_stream *js, + const char *fieldname, + const char *str TAKES) +{ + json_out_addstr(js->jout, fieldname, str); + if (taken(str)) + tal_free(str); +} + +static char *json_member_direct(struct json_stream *js, + const char *fieldname, size_t extra) +{ + char *dest; + + dest = json_out_member_direct(js->jout, fieldname, extra); + return dest; +} + void json_add_jsonstr(struct json_stream *js, const char *fieldname, const char *jsonstr) @@ -225,76 +253,62 @@ struct io_plan *json_stream_output_(struct json_stream *js, void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value) { - json_add_member(result, fieldname, false, "%u", value); + json_add_primitive_fmt(result, fieldname, "%u", value); } void json_add_u64(struct json_stream *result, const char *fieldname, uint64_t value) { - json_add_member(result, fieldname, false, "%"PRIu64, value); + json_add_primitive_fmt(result, fieldname, "%"PRIu64, value); } void json_add_s64(struct json_stream *result, const char *fieldname, int64_t value) { - json_add_member(result, fieldname, false, "%"PRIi64, value); + json_add_primitive_fmt(result, fieldname, "%"PRIi64, value); } void json_add_u32(struct json_stream *result, const char *fieldname, uint32_t value) { - json_add_member(result, fieldname, false, "%u", value); + json_add_primitive_fmt(result, fieldname, "%u", value); } void json_add_s32(struct json_stream *result, const char *fieldname, int32_t value) { - json_add_member(result, fieldname, false, "%d", value); -} - -void json_add_literal(struct json_stream *result, const char *fieldname, - const char *literal, int len) -{ - /* Literal may contain quotes, so bypass normal checks */ - char *dest = json_member_direct(result, fieldname, len); - memcpy(dest, literal, len); + json_add_primitive_fmt(result, fieldname, "%d", value); } void json_add_stringn(struct json_stream *result, const char *fieldname, const char *value TAKES, size_t value_len) { - json_add_member(result, fieldname, true, "%.*s", (int)value_len, value); + json_add_str_fmt(result, fieldname, "%.*s", (int)value_len, value); if (taken(value)) tal_free(value); } -void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES) -{ - json_add_stringn(result, fieldname, value, strlen(value)); -} - void json_add_bool(struct json_stream *result, const char *fieldname, bool value) { - json_add_member(result, fieldname, false, value ? "true" : "false"); + json_add_primitive(result, fieldname, value ? "true" : "false"); } void json_add_null(struct json_stream *stream, const char *fieldname) { - json_add_member(stream, fieldname, false, "null"); + json_add_primitive(stream, fieldname, "null"); } void json_add_hex(struct json_stream *js, const char *fieldname, const void *data, size_t len) { /* Size without NUL term */ - size_t hexlen = hex_str_size(len) - 1; - char *dest; + size_t hexlen = hex_str_size(len); + char str[hexlen]; - dest = json_member_direct(js, fieldname, 1 + hexlen + 1); - dest[0] = '"'; - if (!hex_encode(data, len, dest + 1, hexlen + 1)) + if (!hex_encode(data, len, str, hexlen)) abort(); - dest[1+hexlen] = '"'; + + json_add_string(js, fieldname, str); } void json_add_hex_talarr(struct json_stream *result, @@ -321,8 +335,9 @@ void json_add_escaped_string(struct json_stream *result, const char *fieldname, void json_add_timeabs(struct json_stream *result, const char *fieldname, struct timeabs t) { - json_add_member(result, fieldname, false, "%" PRIu64 ".%03" PRIu64, - (u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000); + json_add_primitive_fmt(result, fieldname, + "%" PRIu64 ".%03" PRIu64, + (u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000); } void json_add_time(struct json_stream *result, const char *fieldname, @@ -365,7 +380,7 @@ void json_add_tok(struct json_stream *result, const char *fieldname, void json_add_errcode(struct json_stream *result, const char *fieldname, errcode_t code) { - json_add_member(result, fieldname, false, "%"PRIerrcode, code); + json_add_primitive_fmt(result, fieldname, "%" PRIerrcode, code); } void json_add_invstring(struct json_stream *result, const char *invstring) @@ -431,17 +446,17 @@ void json_add_outpoint(struct json_stream *result, const char *fieldname, { char hex[hex_str_size(sizeof(out->txid))]; bitcoin_txid_to_hex(&out->txid, hex, sizeof(hex)); - json_add_member(result, fieldname, true, "%s:%d", hex, out->n); + json_add_str_fmt(result, fieldname, "%s:%d", hex, out->n); } void json_add_short_channel_id(struct json_stream *response, const char *fieldname, const struct short_channel_id *scid) { - json_add_member(response, fieldname, true, "%dx%dx%d", - short_channel_id_blocknum(scid), - short_channel_id_txnum(scid), - short_channel_id_outnum(scid)); + json_add_str_fmt(response, fieldname, "%dx%dx%d", + short_channel_id_blocknum(scid), + short_channel_id_txnum(scid), + short_channel_id_outnum(scid)); } void json_add_address(struct json_stream *response, const char *fieldname, diff --git a/common/json_stream.h b/common/json_stream.h index 71bf82814367..7743a29a83b1 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -107,19 +107,50 @@ void json_object_end(struct json_stream *js); void json_stream_append(struct json_stream *js, const char *str, size_t len); /** - * json_add_member - add a generic member. + * json_add_primitive_fmt - add an unquoted literal member. + * @js: the json_stream. + * @fieldname: fieldname (if in object), otherwise must be NULL. + * @fmt...: the printf-style format + */ +void json_add_primitive_fmt(struct json_stream *js, + const char *fieldname, + const char *fmt, ...) PRINTF_FMT(3,4); + +/** + * json_add_primitive - add an unquoted literal member. + * @js: the json_stream. + * @fieldname: fieldname (if in object), otherwise must be NULL. + * @val: the primitive + */ +void json_add_primitive(struct json_stream *js, + const char *fieldname, + const char *val TAKES); + +/** + * json_add_str_fmt - add a string member (printf-style). * @js: the json_stream. * @fieldname: fieldname (if in object), otherwise must be NULL. - * @quote: true if should be escaped and wrapped in "". * @fmt...: the printf-style format - * - * The resulting string from @fmt is escaped if quote is true: - * see json_member_direct to avoid quoting. */ -void json_add_member(struct json_stream *js, +void json_add_str_fmt(struct json_stream *js, + const char *fieldname, + const char *fmt, ...) PRINTF_FMT(3,4); + +/** + * json_add_string - add a string member. + * @js: the json_stream. + * @fieldname: fieldname (if in object), otherwise must be NULL. + * @str: the string + */ +void json_add_string(struct json_stream *js, const char *fieldname, - bool quote, - const char *fmt, ...) PRINTF_FMT(4,5); + const char *str TAKES); + +/* '"fieldname" : "value"' or '"value"' if fieldname is NULL. String must + * already be JSON escaped as necessary. */ +void json_add_escaped_string(struct json_stream *result, + const char *fieldname, + const struct json_escape *esc TAKES); /** * json_add_jsonstr - add a JSON entity in a string that is already @@ -133,17 +164,6 @@ void json_add_jsonstr(struct json_stream *js, const char *fieldname, const char *jsonstr); -/** - * json_member_direct - start a generic member. - * @js: the json_stream. - * @fieldname: fieldname (if in object), otherwise must be NULL. - * @extra: the space to reserve. - * - * Returns a ptr to @extra bytes. - */ -char *json_member_direct(struct json_stream *js, - const char *fieldname, size_t extra); - /** * json_stream_output - start writing out a json_stream to this conn. * @js: the json_stream diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index f9ece1ce4091..317346ca1c75 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -506,9 +506,9 @@ static void json_command_malformed(struct json_connection *jcon, json_object_start(js, NULL); json_add_string(js, "jsonrpc", "2.0"); - json_add_literal(js, "id", id, strlen(id)); + json_add_primitive(js, "id", id); json_object_start(js, "error"); - json_add_member(js, "code", false, "%" PRIerrcode, JSONRPC2_INVALID_REQUEST); + json_add_errcode(js, "code", JSONRPC2_INVALID_REQUEST); json_add_string(js, "message", error); json_object_end(js); json_object_end(js); @@ -578,7 +578,7 @@ static struct json_stream *json_start(struct command *cmd) json_object_start(js, NULL); json_add_string(js, "jsonrpc", "2.0"); - json_add_literal(js, "id", cmd->id, strlen(cmd->id)); + json_add_jsonstr(js, "id", cmd->id); return js; } @@ -598,7 +598,7 @@ struct json_stream *json_stream_fail_nodata(struct command *cmd, assert(code); json_object_start(js, "error"); - json_add_member(js, "code", false, "%" PRIerrcode, code); + json_add_errcode(js, "code", code); json_add_string(js, "message", errmsg); return js; diff --git a/lightningd/log.c b/lightningd/log.c index 11a66a092946..dd44636b101c 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -606,8 +606,8 @@ void json_add_opt_log_levels(struct json_stream *response, struct log *log) struct print_filter *i; list_for_each(&log->lr->print_filters, i, list) { - json_add_member(response, "log-level", true, "%s:%s", - log_level_name(i->level), i->prefix); + json_add_str_fmt(response, "log-level", "%s:%s", + log_level_name(i->level), i->prefix); } } diff --git a/lightningd/notification.c b/lightningd/notification.c index 8f1528a67f65..a11602c7299b 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -399,7 +399,7 @@ static void sendpay_failure_notification_serialize(struct json_stream *stream, /* In line with the format of json error returned * by sendpay_fail(). */ - json_add_member(stream, "code", false, "%" PRIerrcode, pay_errcode); + json_add_errcode(stream, "code", pay_errcode); json_add_string(stream, "message", errmsg); json_object_start(stream, "data"); diff --git a/lightningd/options.c b/lightningd/options.c index 590f17e1900f..fc3263a21c0d 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1563,8 +1563,7 @@ static void add_config(struct lightningd *ld, || (!streq(buf, "") && strspn(buf, "0123456789.") == strlen(buf))) { /* Let pure numbers and true/false through as * literals. */ - json_add_literal(response, name0, - buf, strlen(buf)); + json_add_primitive(response, name0, buf); return; } diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index 9449cbc82ac3..390368835f39 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -32,12 +32,6 @@ void json_add_hex_talarr(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const tal_t *data UNNEEDED) { fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); } -/* Generated stub for json_add_member */ -void json_add_member(struct json_stream *js UNNEEDED, - const char *fieldname UNNEEDED, - bool quote UNNEEDED, - const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "json_add_member called!\n"); abort(); } /* Generated stub for json_add_node_id */ void json_add_node_id(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED, @@ -47,6 +41,11 @@ void json_add_node_id(struct json_stream *response UNNEEDED, void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, unsigned int value UNNEEDED) { fprintf(stderr, "json_add_num called!\n"); abort(); } +/* Generated stub for json_add_str_fmt */ +void json_add_str_fmt(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_str_fmt called!\n"); abort(); } /* Generated stub for json_add_string */ void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) { fprintf(stderr, "json_add_string called!\n"); abort(); } diff --git a/plugins/libplugin.c b/plugins/libplugin.c index c684a658aa07..74311b81c8b7 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -208,7 +208,7 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd, struct json_stream *js = jsonrpc_stream_start(cmd); json_object_start(js, "error"); - json_add_member(js, "code", false, "%d", code); + json_add_primitive_fmt(js, "code", "%d", code); json_add_string(js, "message", err); return js; diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 3a991ad490e2..a75817bbae98 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -634,7 +634,7 @@ static struct command_result *convert_currency(struct command *cmd, json_add_stringn(req->js, "currency", (const char *)ir->offer->currency, tal_bytelen(ir->offer->currency)); - json_add_member(req->js, "amount", false, "%f", double_amount); + json_add_primitive_fmt(req->js, "amount", "%f", double_amount); return send_outreq(cmd->plugin, req); } From 94f16f14b7b47e83e13fc76b9048edad7b284a76 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Wed, 13 Jul 2022 14:56:08 +0300 Subject: [PATCH 0992/1530] test: test_invoices add a stress test --- tests/test_invoices.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_invoices.py b/tests/test_invoices.py index dfc3f594909c..b2cb94837bfb 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -559,6 +559,7 @@ def test_waitanyinvoice_reversed(node_factory, executor): assert r['label'] == 'inv1' +@pytest.mark.xfail(strict=True) def test_autocleaninvoice(node_factory): l1 = node_factory.get_node() @@ -599,6 +600,13 @@ def test_autocleaninvoice(node_factory): assert len(l1.rpc.listinvoices('inv1')['invoices']) == 0 assert len(l1.rpc.listinvoices('inv2')['invoices']) == 0 + # stress test + l1.rpc.autocleaninvoice(cycle_seconds=0) + l1.rpc.autocleaninvoice(cycle_seconds=1) + l1.rpc.autocleaninvoice(cycle_seconds=0) + time.sleep(1) + l1.rpc.autocleaninvoice(cycle_seconds=1, expired_by=1) + def test_decode_unknown(node_factory): l1 = node_factory.get_node() From cc4024339980902d023d9604162393b8d82c055d Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Thu, 14 Jul 2022 11:10:49 +0300 Subject: [PATCH 0993/1530] pyln-testing: print content of errlog file when _some_ node failed unexpected --- contrib/pyln-testing/pyln/testing/fixtures.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/contrib/pyln-testing/pyln/testing/fixtures.py b/contrib/pyln-testing/pyln/testing/fixtures.py index e2c26de403d1..48ea599df488 100644 --- a/contrib/pyln-testing/pyln/testing/fixtures.py +++ b/contrib/pyln-testing/pyln/testing/fixtures.py @@ -495,6 +495,28 @@ def map_node_error(nodes, f, msg): map_node_error(nf.nodes, lambda n: n.daemon.is_in_log(r'Accessing a null column'), "Accessing a null column") map_node_error(nf.nodes, checkMemleak, "had memleak messages") map_node_error(nf.nodes, lambda n: n.rc != 0 and not n.may_fail, "Node exited with return code {n.rc}") + if not ok: + map_node_error(nf.nodes, prinErrlog, "some node failed unexpected, non-empty errlog file") + + +def getErrlog(node): + for error_file in os.listdir(node.daemon.lightning_dir): + if not re.fullmatch(r"errlog", error_file): + continue + with open(os.path.join(node.daemon.lightning_dir, error_file), 'r') as f: + errors = f.read().strip() + if errors: + return errors, error_file + return None, None + + +def prinErrlog(node): + errors, fname = getErrlog(node) + if errors: + print("-" * 31, "stderr of node {} captured in {} file".format(node.daemon.prefix, fname), "-" * 32) + print(errors) + print("-" * 80) + return 1 if errors else 0 def getValgrindErrors(node): From ad3cbed7c28b2d4e9327885f6c3763f8f4acef41 Mon Sep 17 00:00:00 2001 From: Simon Vrouwe Date: Thu, 14 Jul 2022 12:40:11 +0300 Subject: [PATCH 0994/1530] plugin: autoclean fix double free when re-enable, remove xfail mark from test_ Fixes a crash when enabling after a disable with cycle_seconds=0. --- plugins/autoclean.c | 4 +++- tests/test_invoices.py | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index c4d29fc02727..27c91e2e42ee 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -48,12 +48,14 @@ static struct command_result *json_autocleaninvoice(struct command *cmd, cycle_seconds = *cycle; expired_by = *exby; + cleantimer = tal_free(cleantimer); + if (cycle_seconds == 0) { 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); diff --git a/tests/test_invoices.py b/tests/test_invoices.py index b2cb94837bfb..ccce092183bf 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -559,7 +559,6 @@ def test_waitanyinvoice_reversed(node_factory, executor): assert r['label'] == 'inv1' -@pytest.mark.xfail(strict=True) def test_autocleaninvoice(node_factory): l1 = node_factory.get_node() From 3e672b784d77062adaabaec8a6f7ba18c2655175 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:25:09 +0930 Subject: [PATCH 0995/1530] Makefile: use a library archive for CCAN The linker discards whole files in an archive if it doesn't need them, so saves a bit of space (and time). Also allows us to add more niche things to CCAN (e.g. runes support!) without bloating all the binaries. We also had many places which depended on $(CCAN_FILES), but that was already a dependent of $(ALL_PROGRAMS) and $(ALL_TEST_PROGRAMS). Before: ``` $ size lightningd/lightning*d text data bss dec hex filename 2247683 8696 39008 2295387 23065b lightningd/lightning_channeld 2086607 7432 38880 2132919 208bb7 lightningd/lightning_closingd 2227916 8056 39200 2275172 22b764 lightningd/lightning_connectd 3369236 119288 39240 3527764 35d454 lightningd/lightningd 2183551 8352 38880 2230783 2209ff lightningd/lightning_dualopend 2196389 8024 39136 2243549 223bdd lightningd/lightning_gossipd 2086216 7488 39264 2132968 208be8 lightningd/lightning_hsmd 2134396 8136 39424 2181956 214b44 lightningd/lightning_onchaind 2133391 8352 38880 2180623 21460f lightningd/lightning_openingd 1512168 2136 34384 1548688 17a190 lightningd/lightning_websocketd ``` After: ``` text data bss dec hex filename 2192065 8488 38912 2239465 222be9 lightningd/lightning_channeld 2030957 7224 38816 2076997 1fb145 lightningd/lightning_closingd 2179571 7968 39104 2226643 21f9d3 lightningd/lightning_connectd 3354296 119288 39208 3512792 3599d8 lightningd/lightningd 2127933 8144 38816 2174893 212fad lightningd/lightning_dualopend 2141699 7856 39072 2188627 216553 lightningd/lightning_gossipd 2024482 7288 5240 2037010 1f1512 lightningd/lightning_hsmd 2072074 7920 5400 2085394 1fd212 lightningd/lightning_onchaind 2077773 8144 38816 2124733 206bbd lightningd/lightning_openingd 1408958 1752 344 1411054 1587ee lightningd/lightning_websocketd ``` Signed-off-by: Rusty Russell --- .gitignore | 1 + Makefile | 12 ++++++++---- bitcoin/test/Makefile | 2 +- channeld/test/Makefile | 2 +- cli/Makefile | 2 +- cli/test/Makefile | 2 +- devtools/Makefile | 38 +++++++++++++++++++------------------- plugins/Makefile | 22 +++++++++++----------- plugins/test/Makefile | 2 +- tests/plugins/Makefile | 4 ++-- tools/Makefile | 8 ++++---- 11 files changed, 50 insertions(+), 45 deletions(-) diff --git a/.gitignore b/.gitignore index a242ad4cb214..cf5ccb4d3cd3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ *.rej *.pyc *.tmp +libccan.a .cppcheck-suppress .mypy_cache TAGS diff --git a/Makefile b/Makefile index 85657e8fdf84..42c3a6a36c50 100644 --- a/Makefile +++ b/Makefile @@ -627,8 +627,12 @@ endif header_versions_gen.h: tools/headerversions @tools/headerversions $@ +# We make a static library, this way linker can discard unused parts. +libccan.a: $(CCAN_OBJS) + @$(call VERBOSE, "ar $@", $(AR) r $@ $(CCAN_OBJS)) + # All binaries require the external libs, ccan and system library versions. -$(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS): $(EXTERNAL_LIBS) $(CCAN_OBJS) +$(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS): $(EXTERNAL_LIBS) libccan.a # Each test program depends on its own object. $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS): %: %.o @@ -638,7 +642,7 @@ $(ALL_TEST_PROGRAMS) $(ALL_FUZZ_TARGETS): %: %.o # uses some ccan modules internally). We want to rely on -lwallycore etc. # (as per EXTERNAL_LDLIBS) so we filter them out here. $(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS): - @$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) -o $@) + @$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) libccan.a -o $@) # We special case the fuzzing target binaries, as they need to link against libfuzzer, # which brings its own main(). @@ -684,7 +688,7 @@ obsclean: $(RM) gen_*.h */gen_*.[ch] */*/gen_*.[ch] clean: obsclean - $(RM) $(CCAN_OBJS) $(CDUMP_OBJS) $(ALL_OBJS) + $(RM) libccan.a $(CCAN_OBJS) $(CDUMP_OBJS) $(ALL_OBJS) $(RM) $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES) $(RM) $(ALL_PROGRAMS) $(RM) $(ALL_TEST_PROGRAMS) @@ -704,7 +708,7 @@ update-mocks: @echo Need DEVELOPER=1 and EXPERIMENTAL_FEATURES=1 to regenerate mocks >&2; exit 1 endif -$(ALL_TEST_PROGRAMS:%=update-mocks/%.c): $(ALL_GEN_HEADERS) $(EXTERNAL_LIBS) $(CCAN_OBJS) ccan/ccan/cdump/tools/cdump-enumstr config.vars +$(ALL_TEST_PROGRAMS:%=update-mocks/%.c): $(ALL_GEN_HEADERS) $(EXTERNAL_LIBS) libccan.a ccan/ccan/cdump/tools/cdump-enumstr config.vars update-mocks/%: % @MAKE=$(MAKE) tools/update-mocks.sh "$*" $(SUPPRESS_OUTPUT) diff --git a/bitcoin/test/Makefile b/bitcoin/test/Makefile index 47d8edc35b66..7b15ab9ae07f 100644 --- a/bitcoin/test/Makefile +++ b/bitcoin/test/Makefile @@ -4,7 +4,7 @@ BITCOIN_TEST_PROGRAMS := $(BITCOIN_TEST_OBJS:.o=) BITCOIN_TEST_COMMON_OBJS := common/utils.o common/setup.o common/autodata.o -$(BITCOIN_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_TEST_COMMON_OBJS) bitcoin/chainparams.o +$(BITCOIN_TEST_PROGRAMS): $(BITCOIN_TEST_COMMON_OBJS) bitcoin/chainparams.o $(BITCOIN_TEST_OBJS): $(CCAN_HEADERS) $(BITCOIN_HEADERS) $(BITCOIN_SRC) ALL_TEST_PROGRAMS += $(BITCOIN_TEST_PROGRAMS) diff --git a/channeld/test/Makefile b/channeld/test/Makefile index 77ea655abbf2..fb402a7e63e5 100644 --- a/channeld/test/Makefile +++ b/channeld/test/Makefile @@ -25,7 +25,7 @@ CHANNELD_TEST_COMMON_OBJS := \ common/type_to_string.o \ common/utils.o -$(CHANNELD_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CHANNELD_TEST_COMMON_OBJS) +$(CHANNELD_TEST_PROGRAMS): $(BITCOIN_OBJS) $(WIRE_OBJS) $(CHANNELD_TEST_COMMON_OBJS) $(CHANNELD_TEST_OBJS): $(CHANNELD_HEADERS) $(CHANNELD_SRC) diff --git a/cli/Makefile b/cli/Makefile index fcbf84a6a4cd..5afdb39cae1e 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -15,6 +15,6 @@ LIGHTNING_CLI_COMMON_OBJS := \ $(LIGHTNING_CLI_OBJS): $(JSMN_HEADERS) $(COMMON_HEADERS) $(CCAN_HEADERS) -cli/lightning-cli: $(LIGHTNING_CLI_OBJS) $(LIGHTNING_CLI_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +cli/lightning-cli: $(LIGHTNING_CLI_OBJS) $(LIGHTNING_CLI_COMMON_OBJS) $(JSMN_OBJS) libccan.a include cli/test/Makefile diff --git a/cli/test/Makefile b/cli/test/Makefile index 3b5a800fefad..331018413d81 100644 --- a/cli/test/Makefile +++ b/cli/test/Makefile @@ -21,7 +21,7 @@ CLI_TEST_COMMON_OBJS := \ common/type_to_string.o \ common/permute_tx.o -$(CLI_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(CLI_TEST_COMMON_OBJS) +$(CLI_TEST_PROGRAMS): libccan.a $(BITCOIN_OBJS) $(WIRE_OBJS) $(CLI_TEST_COMMON_OBJS) $(CLI_TEST_OBJS): $(LIGHTNING_CLI_HEADERS) $(LIGHTNING_CLI_SRC) diff --git a/devtools/Makefile b/devtools/Makefile index 98fd57e6c2ef..2c32b024bb27 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -46,49 +46,49 @@ DEVTOOLS_COMMON_OBJS := \ wire/channel_type_wiregen.o \ wire/tlvstream.o -devtools/features: $(CCAN_OBJS) common/features.o common/utils.o wire/fromwire.o wire/towire.o devtools/features.o +devtools/features: common/features.o common/utils.o wire/fromwire.o wire/towire.o devtools/features.o -devtools/fp16: $(CCAN_OBJS) common/fp16.o common/utils.o common/setup.o common/autodata.o devtools/fp16.o +devtools/fp16: common/fp16.o common/utils.o common/setup.o common/autodata.o devtools/fp16.o -devtools/bolt11-cli: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bolt11-cli.o +devtools/bolt11-cli: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bolt11-cli.o devtools/encodeaddr: common/utils.o common/bech32.o devtools/encodeaddr.o -devtools/bolt12-cli: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/bolt12$(EXP)_wiregen.o wire/fromwire.o wire/towire.o common/bolt12.o common/bolt12_merkle.o devtools/bolt12-cli.o common/setup.o common/iso4217.o +devtools/bolt12-cli: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/bolt12$(EXP)_wiregen.o wire/fromwire.o wire/towire.o common/bolt12.o common/bolt12_merkle.o devtools/bolt12-cli.o common/setup.o common/iso4217.o -devtools/decodemsg: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_PRINT_OBJS) wire/fromwire.o wire/towire.o devtools/print_wire.o devtools/decodemsg.o +devtools/decodemsg: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) $(WIRE_PRINT_OBJS) wire/fromwire.o wire/towire.o devtools/print_wire.o devtools/decodemsg.o -devtools/dump-gossipstore: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/dump-gossipstore.o gossipd/gossip_store_wiregen.o +devtools/dump-gossipstore: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/dump-gossipstore.o gossipd/gossip_store_wiregen.o devtools/dump-gossipstore.o: gossipd/gossip_store_wiregen.h -devtools/create-gossipstore: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/create-gossipstore.o gossipd/gossip_store_wiregen.o +devtools/create-gossipstore: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/create-gossipstore.o gossipd/gossip_store_wiregen.o devtools/create-gossipstore.o: gossipd/gossip_store_wiregen.h devtools/onion.c: ccan/config.h -devtools/onion: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) common/onion.o common/onionreply.o wire/fromwire.o wire/towire.o devtools/onion.o common/sphinx.o +devtools/onion: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) common/onion.o common/onionreply.o wire/fromwire.o wire/towire.o devtools/onion.o common/sphinx.o -devtools/gossipwith: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/peer$(EXP)_wiregen.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o +devtools/gossipwith: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/peer$(EXP)_wiregen.o devtools/gossipwith.o common/cryptomsg.o common/cryptomsg.o $(DEVTOOLS_OBJS) $(DEVTOOLS_TOOL_OBJS): wire/wire.h -devtools/mkcommit: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) common/derive_basepoints.o common/channel_type.o common/keyset.o common/key_derive.o common/initial_commit_tx.o common/permute_tx.o wire/fromwire.o wire/towire.o devtools/mkcommit.o channeld/full_channel.o common/initial_channel.o common/htlc_state.o common/pseudorand.o common/htlc_tx.o channeld/commit_tx.o common/htlc_trim.o +devtools/mkcommit: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/derive_basepoints.o common/channel_type.o common/keyset.o common/key_derive.o common/initial_commit_tx.o common/permute_tx.o wire/fromwire.o wire/towire.o devtools/mkcommit.o channeld/full_channel.o common/initial_channel.o common/htlc_state.o common/pseudorand.o common/htlc_tx.o channeld/commit_tx.o common/htlc_trim.o -devtools/mkfunding: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o common/key_derive.o devtools/mkfunding.o +devtools/mkfunding: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o common/key_derive.o devtools/mkfunding.o -devtools/mkclose: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkclose.o +devtools/mkclose: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkclose.o -devtools/mkgossip: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o common/utxo.o common/permute_tx.o common/key_derive.o devtools/mkgossip.o +devtools/mkgossip: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o common/utxo.o common/permute_tx.o common/key_derive.o devtools/mkgossip.o -devtools/mkencoded: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkencoded.o +devtools/mkencoded: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkencoded.o -devtools/checkchannels: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) common/configdir.o wire/fromwire.o wire/towire.o devtools/checkchannels.o +devtools/checkchannels: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/configdir.o wire/fromwire.o wire/towire.o devtools/checkchannels.o -devtools/mkquery: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkquery.o +devtools/mkquery: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/mkquery.o -devtools/lightning-checkmessage: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/lightning-checkmessage.o +devtools/lightning-checkmessage: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/lightning-checkmessage.o -devtools/route: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o common/gossmap.o common/fp16.o common/random_select.o common/route.o common/dijkstra.o devtools/clean_topo.o devtools/route.o +devtools/route: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o common/gossmap.o common/fp16.o common/random_select.o common/route.o common/dijkstra.o devtools/clean_topo.o devtools/route.o -devtools/topology: $(DEVTOOLS_COMMON_OBJS) $(CCAN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o common/gossmap.o common/fp16.o common/random_select.o common/dijkstra.o common/route.o devtools/clean_topo.o devtools/topology.o +devtools/topology: $(DEVTOOLS_COMMON_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o wire/tlvstream.o common/gossmap.o common/fp16.o common/random_select.o common/dijkstra.o common/route.o devtools/clean_topo.o devtools/topology.o diff --git a/plugins/Makefile b/plugins/Makefile index 7b32bb82aea2..4ae876c746d3 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -161,30 +161,30 @@ PLUGIN_COMMON_OBJS := \ # Make all plugins depend on all plugin headers, for simplicity. $(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER) -plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o +plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o -plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/chanbackup: bitcoin/chainparams.o $(PLUGIN_chanbackup_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/chanbackup: bitcoin/chainparams.o $(PLUGIN_chanbackup_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) # Topology wants to decode node_announcement, and peer_wiregen which # pulls in some of bitcoin/. -plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o wire/peer$(EXP)_wiregen.o wire/channel_type_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o wire/peer$(EXP)_wiregen.o wire/channel_type_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/txprepare: bitcoin/chainparams.o $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/txprepare: bitcoin/chainparams.o $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/keysend: bitcoin/chainparams.o wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o +plugins/keysend: bitcoin/chainparams.o wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o $(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER) -plugins/spenderp: bitcoin/block.o bitcoin/chainparams.o bitcoin/preimage.o bitcoin/psbt.o common/psbt_open.o wire/peer${EXP}_wiregen.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/spenderp: bitcoin/block.o bitcoin/chainparams.o bitcoin/preimage.o bitcoin/psbt.o common/psbt_open.o wire/peer${EXP}_wiregen.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/offers: bitcoin/chainparams.o $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/bolt11_json.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) $(CCAN_OBJS) +plugins/offers: bitcoin/chainparams.o $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/bolt11_json.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) -plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) $(CCAN_OBJS) common/gossmap.o common/fp16.o common/dijkstra.o common/route.o common/blindedpath.o common/hmac.o common/blinding.o +plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) common/gossmap.o common/fp16.o common/dijkstra.o common/route.o common/blindedpath.o common/hmac.o common/blinding.o -plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) # Generated from PLUGINS definition in plugins/Makefile ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h diff --git a/plugins/test/Makefile b/plugins/test/Makefile index 465fc0331b5f..204a0f228d88 100644 --- a/plugins/test/Makefile +++ b/plugins/test/Makefile @@ -22,7 +22,7 @@ plugins/test/run-route-overlong: \ common/node_id.o \ common/route.o -$(PLUGIN_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(PLUGIN_TEST_COMMON_OBJS) +$(PLUGIN_TEST_PROGRAMS): $(BITCOIN_OBJS) $(WIRE_OBJS) $(PLUGIN_TEST_COMMON_OBJS) $(PLUGIN_TEST_OBJS): $(PLUGIN_FUNDER_HEADER) $(PLUGIN_FUNDER_SRC) diff --git a/tests/plugins/Makefile b/tests/plugins/Makefile index 3b2548221d23..386840be4e11 100644 --- a/tests/plugins/Makefile +++ b/tests/plugins/Makefile @@ -1,14 +1,14 @@ PLUGIN_TESTLIBPLUGIN_SRC := tests/plugins/test_libplugin.c PLUGIN_TESTLIBPLUGIN_OBJS := $(PLUGIN_TESTLIBPLUGIN_SRC:.c=.o) -tests/plugins/test_libplugin: bitcoin/chainparams.o $(PLUGIN_TESTLIBPLUGIN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +tests/plugins/test_libplugin: bitcoin/chainparams.o $(PLUGIN_TESTLIBPLUGIN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(PLUGIN_TESTLIBPLUGIN_OBJS): $(PLUGIN_LIB_HEADER) PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_SRC := tests/plugins/test_selfdisable_after_getmanifest.c PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS := $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_SRC:.c=.o) -tests/plugins/test_selfdisable_after_getmanifest: bitcoin/chainparams.o $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS) common/autodata.o common/json_parse_simple.o common/setup.o common/utils.o $(JSMN_OBJS) $(CCAN_OBJS) +tests/plugins/test_selfdisable_after_getmanifest: bitcoin/chainparams.o $(PLUGIN_TESTSELFDISABLE_AFTER_GETMANIFEST_OBJS) common/autodata.o common/json_parse_simple.o common/setup.o common/utils.o $(JSMN_OBJS) # Make sure these depend on everything. ALL_TEST_PROGRAMS += tests/plugins/test_libplugin tests/plugins/test_selfdisable_after_getmanifest diff --git a/tools/Makefile b/tools/Makefile index 5dd478fcf4b2..6684b5bec48f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,12 +12,12 @@ TOOLS_COMMON_OBJS = common/utils.o # We force make to relink this every time, to detect version changes. # Do it atomically, otherwise parallel builds can get upset! -tools/headerversions: $(FORCE) tools/headerversions.o $(CCAN_OBJS) - @trap "rm -f $@.tmp.$$$$" EXIT; $(LINK.o) tools/headerversions.o $(CCAN_OBJS) $(LOADLIBES) $(LDLIBS) -o $@.tmp.$$$$ && mv $@.tmp.$$$$ $@ +tools/headerversions: $(FORCE) tools/headerversions.o libccan.a + @trap "rm -f $@.tmp.$$$$" EXIT; $(LINK.o) tools/headerversions.o libccan.a $(LOADLIBES) $(LDLIBS) -o $@.tmp.$$$$ && mv $@.tmp.$$$$ $@ -tools/check-bolt: tools/check-bolt.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) +tools/check-bolt: tools/check-bolt.o $(TOOLS_COMMON_OBJS) -tools/hsmtool: tools/hsmtool.o $(CCAN_OBJS) $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/autodata.o common/bech32.o common/bigsize.o common/configdir.o common/derive_basepoints.o common/descriptor_checksum.o common/hsm_encryption.o common/node_id.o common/type_to_string.o common/version.o wire/fromwire.o wire/towire.o +tools/hsmtool: tools/hsmtool.o $(TOOLS_COMMON_OBJS) $(BITCOIN_OBJS) common/amount.o common/autodata.o common/bech32.o common/bigsize.o common/configdir.o common/derive_basepoints.o common/descriptor_checksum.o common/hsm_encryption.o common/node_id.o common/type_to_string.o common/version.o wire/fromwire.o wire/towire.o tools/lightning-hsmtool: tools/hsmtool cp $< $@ From f65d3bb1fc0cfb693e2b73683e8f0cf835f5331c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:25:11 +0930 Subject: [PATCH 0996/1530] ccan: upgrade to get ccan/runes. Signed-off-by: Rusty Russell --- Makefile | 11 + ccan/README | 2 +- ccan/ccan/base64/base64.h | 2 +- ccan/ccan/rune/LICENSE | 1 + ccan/ccan/rune/_info | 130 +++++ ccan/ccan/rune/coding.c | 422 +++++++++++++++ ccan/ccan/rune/internal.h | 8 + ccan/ccan/rune/rune.c | 491 ++++++++++++++++++ ccan/ccan/rune/rune.h | 379 ++++++++++++++ .../rune/test/run-alt-lexicographic-order.c | 33 ++ ccan/ccan/rune/test/run.c | 127 +++++ ccan/ccan/rune/test/test_vectors.csv | 151 ++++++ 12 files changed, 1755 insertions(+), 2 deletions(-) create mode 120000 ccan/ccan/rune/LICENSE create mode 100644 ccan/ccan/rune/_info create mode 100644 ccan/ccan/rune/coding.c create mode 100644 ccan/ccan/rune/internal.h create mode 100644 ccan/ccan/rune/rune.c create mode 100644 ccan/ccan/rune/rune.h create mode 100644 ccan/ccan/rune/test/run-alt-lexicographic-order.c create mode 100644 ccan/ccan/rune/test/run.c create mode 100644 ccan/ccan/rune/test/test_vectors.csv diff --git a/Makefile b/Makefile index 42c3a6a36c50..6c804b954884 100644 --- a/Makefile +++ b/Makefile @@ -92,6 +92,7 @@ FEATURES := CCAN_OBJS := \ ccan-asort.o \ + ccan-base64.o \ ccan-bitmap.o \ ccan-bitops.o \ ccan-breakpoint.o \ @@ -127,6 +128,8 @@ CCAN_OBJS := \ ccan-ptr_valid.o \ ccan-rbuf.o \ ccan-read_write_all.o \ + ccan-rune-coding.o \ + ccan-rune-rune.o \ ccan-str-base32.o \ ccan-str-hex.o \ ccan-str.o \ @@ -195,6 +198,8 @@ CCAN_HEADERS := \ $(CCANDIR)/ccan/ptrint/ptrint.h \ $(CCANDIR)/ccan/rbuf/rbuf.h \ $(CCANDIR)/ccan/read_write_all/read_write_all.h \ + $(CCANDIR)/ccan/rune/internal.h \ + $(CCANDIR)/ccan/rune/rune.h \ $(CCANDIR)/ccan/short_types/short_types.h \ $(CCANDIR)/ccan/str/base32/base32.h \ $(CCANDIR)/ccan/str/hex/hex.h \ @@ -840,6 +845,8 @@ endif ccan-breakpoint.o: $(CCANDIR)/ccan/breakpoint/breakpoint.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) +ccan-base64.o: $(CCANDIR)/ccan/base64/base64.c + @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) ccan-tal.o: $(CCANDIR)/ccan/tal/tal.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) ccan-tal-str.o: $(CCANDIR)/ccan/tal/str/str.c @@ -940,3 +947,7 @@ ccan-json_out.o: $(CCANDIR)/ccan/json_out/json_out.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) ccan-closefrom.o: $(CCANDIR)/ccan/closefrom/closefrom.c @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) +ccan-rune-rune.o: $(CCANDIR)/ccan/rune/rune.c + @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) +ccan-rune-coding.o: $(CCANDIR)/ccan/rune/coding.c + @$(call VERBOSE, "cc $<", $(CC) $(CFLAGS) -c -o $@ $<) diff --git a/ccan/README b/ccan/README index 25c2b543395e..acdc78064c9a 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2540-g8448fd28 +CCAN version: init-2541-g52b86922 diff --git a/ccan/ccan/base64/base64.h b/ccan/ccan/base64/base64.h index cef30d257673..a899af4a357f 100644 --- a/ccan/ccan/base64/base64.h +++ b/ccan/ccan/base64/base64.h @@ -116,7 +116,7 @@ ssize_t base64_decode_quartet_using_maps(const base64_maps_t *maps, * @note sets errno = EDOM if src contains invalid characters * @note sets errno = EINVAL if src is an invalid base64 tail */ -ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char *dest, +ssize_t base64_decode_tail_using_maps(const base64_maps_t *maps, char dest[3], const char *src, size_t srclen); diff --git a/ccan/ccan/rune/LICENSE b/ccan/ccan/rune/LICENSE new file mode 120000 index 000000000000..2354d12945d3 --- /dev/null +++ b/ccan/ccan/rune/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/ccan/rune/_info b/ccan/ccan/rune/_info new file mode 100644 index 000000000000..2b2e2e8b98e8 --- /dev/null +++ b/ccan/ccan/rune/_info @@ -0,0 +1,130 @@ +#include "config.h" +#include +#include + +/** + * rune - Simple cookies you can extend (a-la Python runes class). + * + * This code is a form of cookies, but they are user-extensible, and + * contain a simple language to define what the cookie allows. + * + * A "rune" contains the hash of a secret (so the server can + * validate), such that you can add, but not subtract, conditions. + * This is a simplified form of Macaroons, See + * https://research.google/pubs/pub41892/ "Macaroons: Cookies with + * Contextual Caveats for Decentralized Authorization in the Cloud". + * It has one good idea, some extended ideas nobody implements, and + * lots and lots of words. + * + * License: BSD-MIT + * Author: Rusty Russell + * Example: + * // Given "generate secret 1" outputs kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ== + * // Given "add kr7AW-eJ2Munhv5ftu4rHqAnhxUpPQM8aOyWOmqiytk9MQ== uid=rusty" outputs Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= + * // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= rusty" outputs PASSED + * // Given "test secret Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= notrusty" outputs FAILED: uid is not equal to rusty + * // Given "add Xyt5S6FKUnA2ppGB62c6HTPGojt2S7k2n7Cf7Tjj6zM9MSZ1aWQ9cnVzdHk= t\<1655958616" outputs _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2 + * // Given "test secret _YBFmeAedqlLigWHAmvyyGGHRrnI40BRQGh2hWdSZ9E9MSZ1aWQ9cnVzdHkmdDwxNjU1OTU4NjE2 rusty" outputs FAILED: t is greater or equal to 1655958616 + * #include + * #include + * #include + * #include + * #include + * + * // We support two values: current time (t), and user id (uid). + * static const char *check(const tal_t *ctx, + * const struct rune *rune, + * const struct rune_altern *alt, + * char *uid) + * { + * // t= means current time, in seconds, as integer + * if (streq(alt->fieldname, "t")) { + * struct timeval now; + * gettimeofday(&now, NULL); + * return rune_alt_single_int(ctx, alt, now.tv_sec); + * } + * if (streq(alt->fieldname, "uid")) { + * return rune_alt_single_str(ctx, alt, uid, strlen(uid)); + * } + * // Otherwise, field is missing + * return rune_alt_single_missing(ctx, alt); + * } + * + * int main(int argc, char *argv[]) + * { + * struct rune *master, *rune; + * + * if (argc < 3) + * goto usage; + * + * if (streq(argv[1], "generate")) { + * // Make master, derive a unique_id'd rune. + * if (argc != 3 && argc != 4) + * goto usage; + * master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL); + * rune = rune_derive_start(NULL, master, argv[3]); + * } else if (streq(argv[1], "add")) { + * // Add a restriction + * struct rune_restr *restr; + * if (argc != 4) + * goto usage; + * rune = rune_from_base64(NULL, argv[2]); + * if (!rune) + * errx(1, "Bad rune"); + * restr = rune_restr_from_string(NULL, argv[3], strlen(argv[3])); + * if (!restr) + * errx(1, "Bad restriction string"); + * rune_add_restr(rune, restr); + * } else if (streq(argv[1], "test")) { + * const char *err; + * if (argc != 5) + * goto usage; + * master = rune_new(NULL, (u8 *)argv[2], strlen(argv[2]), NULL); + * if (!master) + * errx(1, "Bad master rune"); + * rune = rune_from_base64(NULL, argv[3]); + * if (!rune) + * errx(1, "Bad rune"); + * err = rune_test(NULL, master, rune, check, argv[4]); + * if (err) + * printf("FAILED: %s\n", err); + * else + * printf("PASSED\n"); + * return 0; + * } else + * goto usage; + * + * printf("%s\n", rune_to_base64(NULL, rune)); + * return 0; + * + * usage: + * errx(1, "Usage: %s generate OR\n" + * "%s add OR\n" + * "%s test ", argv[0], argv[0], argv[0]); + * } + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/base64\n"); + printf("ccan/crypto/sha256\n"); + printf("ccan/endian\n"); + printf("ccan/mem\n"); + printf("ccan/short_types\n"); + printf("ccan/str/hex\n"); + printf("ccan/tal/str\n"); + printf("ccan/tal\n"); + printf("ccan/typesafe_cb\n"); + return 0; + } + if (strcmp(argv[1], "testdepends") == 0) { + printf("ccan/tal/grab_file\n"); + return 0; + } + + return 1; +} diff --git a/ccan/ccan/rune/coding.c b/ccan/ccan/rune/coding.c new file mode 100644 index 000000000000..b9255b0b5b02 --- /dev/null +++ b/ccan/ccan/rune/coding.c @@ -0,0 +1,422 @@ +/* MIT (BSD) license - see LICENSE file for details */ +/* Routines to encode / decode a rune */ +#include +#include +#include +#include +#include +#include +#include + +/* From Python base64.urlsafe_b64encode: + * + * The alphabet uses '-' instead of '+' and '_' instead of '/'. + */ +static const base64_maps_t base64_maps_urlsafe = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", + + "\xff\xff\xff\xff\xff" /* 0 */ + "\xff\xff\xff\xff\xff" /* 5 */ + "\xff\xff\xff\xff\xff" /* 10 */ + "\xff\xff\xff\xff\xff" /* 15 */ + "\xff\xff\xff\xff\xff" /* 20 */ + "\xff\xff\xff\xff\xff" /* 25 */ + "\xff\xff\xff\xff\xff" /* 30 */ + "\xff\xff\xff\xff\xff" /* 35 */ + "\xff\xff\xff\xff\xff" /* 40 */ + "\x3e\xff\xff\x34\x35" /* 45 */ + "\x36\x37\x38\x39\x3a" /* 50 */ + "\x3b\x3c\x3d\xff\xff" /* 55 */ + "\xff\xff\xff\xff\xff" /* 60 */ + "\x00\x01\x02\x03\x04" /* 65 A */ + "\x05\x06\x07\x08\x09" /* 70 */ + "\x0a\x0b\x0c\x0d\x0e" /* 75 */ + "\x0f\x10\x11\x12\x13" /* 80 */ + "\x14\x15\x16\x17\x18" /* 85 */ + "\x19\xff\xff\xff\xff" /* 90 */ + "\x3f\xff\x1a\x1b\x1c" /* 95 */ + "\x1d\x1e\x1f\x20\x21" /* 100 */ + "\x22\x23\x24\x25\x26" /* 105 */ + "\x27\x28\x29\x2a\x2b" /* 110 */ + "\x2c\x2d\x2e\x2f\x30" /* 115 */ + "\x31\x32\x33\xff\xff" /* 120 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 125 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 135 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 145 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 155 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 165 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 175 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 185 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 195 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 205 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 215 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 225 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 235 */ + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" /* 245 */ +}; + +/* For encoding as a string */ +struct wbuf { + size_t off, len; + char *buf; +}; + +static void to_wbuf(const char *s, size_t len, void *vwbuf) +{ + struct wbuf *wbuf = vwbuf; + + while (wbuf->off + len > wbuf->len) + tal_resize(&wbuf->buf, wbuf->len *= 2); + memcpy(wbuf->buf + wbuf->off, s, len); + wbuf->off += len; +} + +/* For adding to sha256 */ +static void to_sha256(const char *s, size_t len, void *vshactx) +{ + struct sha256_ctx *shactx = vshactx; + sha256_update(shactx, s, len); +} + +static void rune_altern_encode(const struct rune_altern *altern, + void (*cb)(const char *s, size_t len, + void *arg), + void *arg) +{ + char cond = altern->condition; + const char *p; + + cb(altern->fieldname, strlen(altern->fieldname), arg); + cb(&cond, 1, arg); + + p = altern->value; + for (;;) { + char esc[2] = { '\\' }; + size_t len = strcspn(p, "\\|&"); + cb(p, len, arg); + if (!p[len]) + break; + esc[1] = p[len]; + cb(esc, 2, arg); + p++; + } +} + +static void rune_restr_encode(const struct rune_restr *restr, + void (*cb)(const char *s, size_t len, + void *arg), + void *arg) +{ + for (size_t i = 0; i < tal_count(restr->alterns); i++) { + if (i != 0) + cb("|", 1, arg); + rune_altern_encode(restr->alterns[i], cb, arg); + } +} + +void rune_sha256_add_restr(struct sha256_ctx *shactx, + struct rune_restr *restr) +{ + rune_restr_encode(restr, to_sha256, shactx); + rune_sha256_endmarker(shactx); +} + +const char *rune_is_derived(const struct rune *source, const struct rune *rune) +{ + if (!runestr_eq(source->version, rune->version)) + return "Version mismatch"; + + return rune_is_derived_anyversion(source, rune); +} + +const char *rune_is_derived_anyversion(const struct rune *source, + const struct rune *rune) +{ + struct sha256_ctx shactx; + size_t i; + + if (tal_count(rune->restrs) < tal_count(source->restrs)) + return "Fewer restrictions than master"; + + /* If we add the same restrictions to source rune, do we match? */ + shactx = source->shactx; + for (i = 0; i < tal_count(rune->restrs); i++) { + /* First restrictions must be identical */ + if (i < tal_count(source->restrs)) { + if (!rune_restr_eq(source->restrs[i], rune->restrs[i])) + return "Does not match master restrictions"; + } else + rune_sha256_add_restr(&shactx, rune->restrs[i]); + } + + if (memcmp(shactx.s, rune->shactx.s, sizeof(shactx.s)) != 0) + return "Not derived from master"; + return NULL; +} + +static bool peek_char(const char *data, size_t len, char *c) +{ + if (len == 0) + return false; + *c = *data; + return true; +} + +static void drop_char(const char **data, size_t *len) +{ + (*data)++; + (*len)--; +} + +static void pull_invalid(const char **data, size_t *len) +{ + *data = NULL; + *len = 0; +} + +static bool pull_char(const char **data, size_t *len, char *c) +{ + if (!peek_char(*data, *len, c)) { + pull_invalid(data, len); + return false; + } + drop_char(data, len); + return true; +} + +static bool is_valid_cond(enum rune_condition cond) +{ + switch (cond) { + case RUNE_COND_IF_MISSING: + case RUNE_COND_EQUAL: + case RUNE_COND_NOT_EQUAL: + case RUNE_COND_BEGINS: + case RUNE_COND_ENDS: + case RUNE_COND_CONTAINS: + case RUNE_COND_INT_LESS: + case RUNE_COND_INT_GREATER: + case RUNE_COND_LEXO_BEFORE: + case RUNE_COND_LEXO_AFTER: + case RUNE_COND_COMMENT: + return true; + } + return false; +} + +/* Sets *more on success: true if another altern follows */ +static struct rune_altern *rune_altern_decode(const tal_t *ctx, + const char **data, size_t *len, + bool *more) +{ + struct rune_altern *alt = tal(ctx, struct rune_altern); + const char *strstart = *data; + char *value; + size_t strlen = 0; + char c; + + /* Swallow field up to conditional */ + for (;;) { + if (!pull_char(data, len, &c)) + return tal_free(alt); + if (cispunct(c)) + break; + strlen++; + } + + alt->fieldname = tal_strndup(alt, strstart, strlen); + if (!is_valid_cond(c)) { + pull_invalid(data, len); + return tal_free(alt); + } + alt->condition = c; + + /* Assign worst case. */ + value = tal_arr(alt, char, *len + 1); + strlen = 0; + *more = false; + while (*len && pull_char(data, len, &c)) { + if (c == '|') { + *more = true; + break; + } + if (c == '&') + break; + + if (c == '\\' && !pull_char(data, len, &c)) + return tal_free(alt); + value[strlen++] = c; + } + value[strlen] = '\0'; + tal_resize(&value, strlen + 1); + alt->value = value; + return alt; +} + +static struct rune_restr *rune_restr_decode(const tal_t *ctx, + const char **data, size_t *len) +{ + struct rune_restr *restr = tal(ctx, struct rune_restr); + size_t num_alts = 0; + bool more; + + /* Must have at least one! */ + restr->alterns = tal_arr(restr, struct rune_altern *, 0); + do { + struct rune_altern *alt; + + alt = rune_altern_decode(restr, data, len, &more); + if (!alt) + return tal_free(restr); + tal_resize(&restr->alterns, num_alts+1); + restr->alterns[num_alts++] = alt; + } while (more); + return restr; +} + +static struct rune *from_string(const tal_t *ctx, + const char *str, + const u8 *hash32) +{ + size_t len = strlen(str); + struct rune *rune = tal(ctx, struct rune); + + /* Now count up how many bytes we should have hashed: secret uses + * first block. */ + rune->shactx.bytes = 64; + + rune->restrs = tal_arr(rune, struct rune_restr *, 0); + rune->unique_id = NULL; + rune->version = NULL; + + while (len) { + struct rune_restr *restr; + restr = rune_restr_decode(rune, &str, &len); + if (!restr) + return tal_free(rune); + if (!rune_add_restr(rune, restr)) + return tal_free(rune); + } + + /* Now we replace with canned hash state */ + memcpy(rune->shactx.s, hash32, 32); + for (size_t i = 0; i < 8; i++) + rune->shactx.s[i] = be32_to_cpu(rune->shactx.s[i]); + + return rune; +} + +struct rune_restr *rune_restr_from_string(const tal_t *ctx, + const char *str, + size_t len) +{ + struct rune_restr *restr; + + restr = rune_restr_decode(NULL, &str, &len); + /* Don't allow trailing chars */ + if (restr && len != 0) + restr = tal_free(restr); + return tal_steal(ctx, restr); +} + +static void to_string(struct wbuf *wbuf, const struct rune *rune, u8 *hash32) +{ + /* Copy hash in big-endian */ + for (size_t i = 0; i < 8; i++) { + be32 v = cpu_to_be32(rune->shactx.s[i]); + memcpy(hash32 + i*4, &v, sizeof(v)); + } + + for (size_t i = 0; i < tal_count(rune->restrs); i++) { + if (i != 0) + to_wbuf("&", 1, wbuf); + rune_restr_encode(rune->restrs[i], to_wbuf, wbuf); + } + to_wbuf("", 1, wbuf); +} + +struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len) +{ + size_t blen; + u8 *data; + struct rune *rune; + + data = tal_arr(NULL, u8, base64_decoded_length(len) + 1); + + blen = base64_decode_using_maps(&base64_maps_urlsafe, + (char *)data, tal_bytelen(data), + str, len); + if (blen == -1) + goto fail; + + if (blen < 32) + goto fail; + + data[blen] = '\0'; + /* Sanity check that it's a valid string! */ + if (strlen((char *)data + 32) != blen - 32) + goto fail; + + rune = from_string(ctx, (const char *)data + 32, data); + tal_free(data); + return rune; + +fail: + tal_free(data); + return NULL; +} + +struct rune *rune_from_base64(const tal_t *ctx, const char *str) +{ + return rune_from_base64n(ctx, str, strlen(str)); +} + +char *rune_to_base64(const tal_t *ctx, const struct rune *rune) +{ + u8 hash32[32]; + char *ret; + size_t ret_len; + struct wbuf wbuf; + + /* We're going to prepend hash */ + wbuf.off = sizeof(hash32); + wbuf.len = 64; + wbuf.buf = tal_arr(NULL, char, wbuf.len); + + to_string(&wbuf, rune, hash32); + /* Prepend hash */ + memcpy(wbuf.buf, hash32, sizeof(hash32)); + + ret = tal_arr(ctx, char, base64_encoded_length(wbuf.off) + 1); + ret_len = base64_encode_using_maps(&base64_maps_urlsafe, + ret, tal_bytelen(ret), + wbuf.buf, wbuf.off - 1); + ret[ret_len] = '\0'; + tal_free(wbuf.buf); + return ret; +} + +struct rune *rune_from_string(const tal_t *ctx, const char *str) +{ + u8 hash[32]; + if (!hex_decode(str, 64, hash, sizeof(hash))) + return NULL; + if (str[64] != ':') + return NULL; + return from_string(ctx, str + 65, hash); +} + +char *rune_to_string(const tal_t *ctx, const struct rune *rune) +{ + u8 hash32[32]; + struct wbuf wbuf; + + /* We're going to prepend hash (in hex), plus colon */ + wbuf.off = sizeof(hash32) * 2 + 1; + wbuf.len = 128; + wbuf.buf = tal_arr(ctx, char, wbuf.len); + + to_string(&wbuf, rune, hash32); + hex_encode(hash32, sizeof(hash32), wbuf.buf, sizeof(hash32) * 2 + 1); + wbuf.buf[sizeof(hash32) * 2] = ':'; + return wbuf.buf; +} diff --git a/ccan/ccan/rune/internal.h b/ccan/ccan/rune/internal.h new file mode 100644 index 000000000000..e4de06cc400d --- /dev/null +++ b/ccan/ccan/rune/internal.h @@ -0,0 +1,8 @@ +#ifndef CCAN_RUNE_INTERNAL_H +#define CCAN_RUNE_INTERNAL_H +/* MIT (BSD) license - see LICENSE file for details */ +void rune_sha256_endmarker(struct sha256_ctx *shactx); +void rune_sha256_add_restr(struct sha256_ctx *shactx, + struct rune_restr *restr); +bool runestr_eq(const char *a, const char *b); +#endif /* CCAN_RUNE_INTERNAL_H */ diff --git a/ccan/ccan/rune/rune.c b/ccan/ccan/rune/rune.c new file mode 100644 index 000000000000..84296c66b33d --- /dev/null +++ b/ccan/ccan/rune/rune.c @@ -0,0 +1,491 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Helper to produce an id field */ +static struct rune_restr *unique_id_restr(const tal_t *ctx, + const char *unique_id, + const char *version) +{ + const char *id; + struct rune_restr *restr; + + assert(!strchr(unique_id, '-')); + if (version) + id = tal_fmt(NULL, "%s-%s", unique_id, version); + else + id = tal_strdup(NULL, unique_id); + + restr = rune_restr_new(ctx); + /* We use the empty field for this, since it's always present. */ + rune_restr_add_altern(restr, + take(rune_altern_new(NULL, "", '=', take(id)))); + return restr; +} + +/* We pad between fields with something identical to the SHA end marker */ +void rune_sha256_endmarker(struct sha256_ctx *shactx) +{ + static const unsigned char pad[64] = {0x80}; + be64 sizedesc; + + sizedesc = cpu_to_be64((uint64_t)shactx->bytes << 3); + /* Add '1' bit to terminate, then all 0 bits, up to next block - 8. */ + sha256_update(shactx, pad, 1 + ((128 - 8 - (shactx->bytes % 64) - 1) % 64)); + /* Add number of bits of data (big endian) */ + sha256_update(shactx, &sizedesc, 8); +} + +struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len, + const char *version) +{ + struct rune *rune = tal(ctx, struct rune); + assert(secret_len + 1 + 8 <= 64); + + if (version) + rune->version = tal_strdup(rune, version); + else + rune->version = NULL; + rune->unique_id = NULL; + sha256_init(&rune->shactx); + sha256_update(&rune->shactx, secret, secret_len); + rune_sha256_endmarker(&rune->shactx); + rune->restrs = tal_arr(rune, struct rune_restr *, 0); + return rune; +} + +struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES) +{ + struct rune *dup; + + if (taken(rune)) + return tal_steal(ctx, (struct rune *)rune); + + dup = tal_dup(ctx, struct rune, rune); + dup->restrs = tal_arr(dup, struct rune_restr *, tal_count(rune->restrs)); + for (size_t i = 0; i < tal_count(rune->restrs); i++) { + dup->restrs[i] = rune_restr_dup(dup->restrs, + rune->restrs[i]); + } + return dup; +} + +struct rune *rune_derive_start(const tal_t *ctx, + const struct rune *master, + const char *unique_id TAKES) +{ + struct rune *rune = rune_dup(ctx, master); + + /* If they provide a unique_id, it goes first. */ + if (unique_id) { + if (taken(unique_id)) + rune->unique_id = tal_steal(rune, unique_id); + else + rune->unique_id = tal_strdup(rune, unique_id); + + rune_add_restr(rune, take(unique_id_restr(NULL, + rune->unique_id, + rune->version))); + } else { + assert(!rune->version); + } + return rune; +} + +struct rune_altern *rune_altern_new(const tal_t *ctx, + const char *fieldname TAKES, + enum rune_condition condition, + const char *value TAKES) +{ + struct rune_altern *altern = tal(ctx, struct rune_altern); + altern->condition = condition; + altern->fieldname = tal_strdup(altern, fieldname); + altern->value = tal_strdup(altern, value); + return altern; +} + +struct rune_altern *rune_altern_dup(const tal_t *ctx, + const struct rune_altern *altern TAKES) +{ + struct rune_altern *dup; + + if (taken(altern)) + return tal_steal(ctx, (struct rune_altern *)altern); + dup = tal(ctx, struct rune_altern); + dup->condition = altern->condition; + dup->fieldname = tal_strdup(dup, altern->fieldname); + dup->value = tal_strdup(dup, altern->value); + return dup; +} + +struct rune_restr *rune_restr_dup(const tal_t *ctx, + const struct rune_restr *restr TAKES) +{ + struct rune_restr *dup; + size_t num_altern; + + if (taken(restr)) + return tal_steal(ctx, (struct rune_restr *)restr); + + num_altern = tal_count(restr->alterns); + dup = tal(ctx, struct rune_restr); + dup->alterns = tal_arr(dup, struct rune_altern *, num_altern); + for (size_t i = 0; i < num_altern; i++) { + dup->alterns[i] = rune_altern_dup(dup->alterns, + restr->alterns[i]); + } + return dup; +} + +struct rune_restr *rune_restr_new(const tal_t *ctx) +{ + struct rune_restr *restr = tal(ctx, struct rune_restr); + restr->alterns = tal_arr(restr, struct rune_altern *, 0); + return restr; +} + +void rune_restr_add_altern(struct rune_restr *restr, + const struct rune_altern *alt TAKES) +{ + size_t num = tal_count(restr->alterns); + + tal_resize(&restr->alterns, num+1); + restr->alterns[num] = rune_altern_dup(restr->alterns, alt); +} + +static bool is_unique_id(const struct rune_altern *alt) +{ + return streq(alt->fieldname, ""); +} + +/* Return unique_id if valid, and sets *version */ +static const char *extract_unique_id(const tal_t *ctx, + const struct rune_altern *alt, + const char **version) +{ + size_t len; + /* Condition must be '='! */ + if (alt->condition != '=') + return NULL; + + len = strcspn(alt->value, "-"); + if (alt->value[len]) + *version = tal_strdup(ctx, alt->value + len + 1); + else + *version = NULL; + return tal_strndup(ctx, alt->value, len); +} + +bool rune_add_restr(struct rune *rune, + const struct rune_restr *restr TAKES) +{ + size_t num = tal_count(rune->restrs); + + /* An empty fieldname is additional correctness checks */ + for (size_t i = 0; i < tal_count(restr->alterns); i++) { + if (!is_unique_id(restr->alterns[i])) + continue; + + /* Must be the only alternative */ + if (tal_count(restr->alterns) != 1) + goto fail; + /* Must be the first restriction */ + if (num != 0) + goto fail; + + rune->unique_id = extract_unique_id(rune, + restr->alterns[i], + &rune->version); + if (!rune->unique_id) + goto fail; + } + + tal_resize(&rune->restrs, num+1); + rune->restrs[num] = rune_restr_dup(rune->restrs, restr); + + rune_sha256_add_restr(&rune->shactx, rune->restrs[num]); + return true; + +fail: + if (taken(restr)) + tal_free(restr); + return false; +} + +static const char *rune_restr_test(const tal_t *ctx, + const struct rune *rune, + const struct rune_restr *restr, + const char *(*check)(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + void *arg), + void *arg) +{ + size_t num = tal_count(restr->alterns); + const char **errs = tal_arr(NULL, const char *, num); + char *err; + + /* Only one alternative has to pass! */ + for (size_t i = 0; i < num; i++) { + errs[i] = check(errs, rune, restr->alterns[i], arg); + if (!errs[i]) { + tal_free(errs); + return NULL; + } + } + + err = tal_fmt(ctx, "%s", errs[0]); + for (size_t i = 1; i < num; i++) + tal_append_fmt(&err, " AND %s", errs[i]); + tal_free(errs); + return err; +} + +static const char *cond_test(const tal_t *ctx, + const struct rune_altern *alt, + const char *complaint, + bool cond) +{ + if (cond) + return NULL; + + return tal_fmt(ctx, "%s %s %s", alt->fieldname, complaint, alt->value); +} + +static const char *integer_compare_valid(const tal_t *ctx, + const s64 *fieldval_int, + const struct rune_altern *alt, + s64 *runeval_int) +{ + long l; + char *p; + + if (!fieldval_int) + return tal_fmt(ctx, "%s is not an integer field", + alt->fieldname); + + errno = 0; + l = strtol(alt->value, &p, 10); + if (p == alt->value + || *p + || ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE)) + return tal_fmt(ctx, "%s is not a valid integer", alt->value); + + *runeval_int = l; + return NULL; +} + +static int lexo_order(const char *fieldval_str, + size_t fieldval_strlen, + const char *alt) +{ + int ret = strncmp(fieldval_str, alt, fieldval_strlen); + + /* If alt is same but longer, fieldval is < */ + if (ret == 0 && strlen(alt) > fieldval_strlen) + ret = -1; + return ret; +} + +static const char *rune_alt_single(const tal_t *ctx, + const struct rune_altern *alt, + const char *fieldval_str, + size_t fieldval_strlen, + const s64 *fieldval_int) +{ + char strfield[STR_MAX_CHARS(s64) + 1]; + s64 runeval_int = 0 /* gcc v9.4.0 gets upset with uninitiaized var at -O3 */; + const char *err; + + /* Caller can't set both! */ + if (fieldval_int) { + assert(!fieldval_str); + sprintf(strfield, "%"PRIi64, *fieldval_int); + fieldval_str = strfield; + fieldval_strlen = strlen(strfield); + } + + switch (alt->condition) { + case RUNE_COND_IF_MISSING: + if (!fieldval_str) + return NULL; + return tal_fmt(ctx, "%s is present", alt->fieldname); + case RUNE_COND_EQUAL: + if (!fieldval_str) + return tal_fmt(ctx, "%s not present", alt->fieldname); + return cond_test(ctx, alt, "is not equal to", + memeqstr(fieldval_str, fieldval_strlen, alt->value)); + case RUNE_COND_NOT_EQUAL: + if (!fieldval_str) + return tal_fmt(ctx, "%s not present", alt->fieldname); + return cond_test(ctx, alt, "is equal to", + !memeqstr(fieldval_str, fieldval_strlen, alt->value)); + case RUNE_COND_BEGINS: + if (!fieldval_str) + return tal_fmt(ctx, "%s not present", alt->fieldname); + return cond_test(ctx, alt, "does not start with", + memstarts_str(fieldval_str, fieldval_strlen, alt->value)); + case RUNE_COND_ENDS: + if (!fieldval_str) + return tal_fmt(ctx, "%s not present", alt->fieldname); + return cond_test(ctx, alt, "does not end with", + memends_str(fieldval_str, fieldval_strlen, alt->value)); + case RUNE_COND_CONTAINS: + if (!fieldval_str) + return tal_fmt(ctx, "%s not present", alt->fieldname); + return cond_test(ctx, alt, "does not contain", + memmem(fieldval_str, fieldval_strlen, + alt->value, strlen(alt->value))); + case RUNE_COND_INT_LESS: + err = integer_compare_valid(ctx, fieldval_int, + alt, &runeval_int); + if (err) + return err; + return cond_test(ctx, alt, "is greater or equal to", + *fieldval_int < runeval_int); + case RUNE_COND_INT_GREATER: + err = integer_compare_valid(ctx, fieldval_int, + alt, &runeval_int); + if (err) + return err; + return cond_test(ctx, alt, "is less or equal to", + *fieldval_int > runeval_int); + case RUNE_COND_LEXO_BEFORE: + if (!fieldval_str) + return tal_fmt(ctx, "%s not present", alt->fieldname); + return cond_test(ctx, alt, "is equal to or ordered after", + lexo_order(fieldval_str, fieldval_strlen, alt->value) < 0); + case RUNE_COND_LEXO_AFTER: + if (!fieldval_str) + return tal_fmt(ctx, "%s not present", alt->fieldname); + return cond_test(ctx, alt, "is equal to or ordered before", + lexo_order(fieldval_str, fieldval_strlen, alt->value) > 0); + case RUNE_COND_COMMENT: + return NULL; + } + /* We should never create any other values! */ + abort(); +} + +const char *rune_alt_single_str(const tal_t *ctx, + const struct rune_altern *alt, + const char *fieldval_str, + size_t fieldval_strlen) +{ + return rune_alt_single(ctx, alt, fieldval_str, fieldval_strlen, NULL); +} + +const char *rune_alt_single_int(const tal_t *ctx, + const struct rune_altern *alt, + s64 fieldval_int) +{ + return rune_alt_single(ctx, alt, NULL, 0, &fieldval_int); +} + +const char *rune_alt_single_missing(const tal_t *ctx, + const struct rune_altern *alt) +{ + return rune_alt_single(ctx, alt, NULL, 0, NULL); +} + +const char *rune_meets_criteria_(const tal_t *ctx, + const struct rune *rune, + const char *(*check)(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + void *arg), + void *arg) +{ + for (size_t i = 0; i < tal_count(rune->restrs); i++) { + const char *err; + + /* Don't "check" unique id */ + if (i == 0 && is_unique_id(rune->restrs[i]->alterns[0])) + continue; + + err = rune_restr_test(ctx, rune, rune->restrs[i], check, arg); + if (err) + return err; + } + return NULL; +} + +const char *rune_test_(const tal_t *ctx, + const struct rune *master, + const struct rune *rune, + const char *(*check)(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + void *arg), + void *arg) +{ + const char *err; + + err = rune_is_derived(master, rune); + if (err) + return err; + return rune_meets_criteria_(ctx, rune, check, arg); +} + +bool rune_altern_eq(const struct rune_altern *alt1, + const struct rune_altern *alt2) +{ + return alt1->condition == alt2->condition + && streq(alt1->fieldname, alt2->fieldname) + && streq(alt1->value, alt2->value); +} + +bool rune_restr_eq(const struct rune_restr *rest1, + const struct rune_restr *rest2) +{ + if (tal_count(rest1->alterns) != tal_count(rest2->alterns)) + return false; + + for (size_t i = 0; i < tal_count(rest1->alterns); i++) + if (!rune_altern_eq(rest1->alterns[i], rest2->alterns[i])) + return false; + return true; +} + +/* Equal, as in both NULL, or both non-NULL and matching */ +bool runestr_eq(const char *a, const char *b) +{ + if (a) { + if (!b) + return false; + return streq(a, b); + } else + return b == NULL; +} + +bool rune_eq(const struct rune *rune1, const struct rune *rune2) +{ + if (!runestr_eq(rune1->unique_id, rune2->unique_id)) + return false; + if (!runestr_eq(rune1->version, rune2->version)) + return false; + + if (memcmp(rune1->shactx.s, rune2->shactx.s, sizeof(rune1->shactx.s))) + return false; + if (rune1->shactx.bytes != rune2->shactx.bytes) + return false; + if (memcmp(rune1->shactx.buf.u8, rune2->shactx.buf.u8, + rune1->shactx.bytes % 64)) + return false; + + if (tal_count(rune1->restrs) != tal_count(rune2->restrs)) + return false; + + for (size_t i = 0; i < tal_count(rune1->restrs); i++) + if (!rune_restr_eq(rune1->restrs[i], rune2->restrs[i])) + return false; + return true; +} diff --git a/ccan/ccan/rune/rune.h b/ccan/ccan/rune/rune.h new file mode 100644 index 000000000000..b67b78281eda --- /dev/null +++ b/ccan/ccan/rune/rune.h @@ -0,0 +1,379 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#ifndef CCAN_RUNE_RUNE_H +#define CCAN_RUNE_RUNE_H +#include +#include +#include +#include + +/* A rune is a series of restrictions. */ +struct rune { + /* unique_id (if any) */ + const char *unique_id; + /* Version (if any) */ + const char *version; + + /* SHA-2 256 of restrictions so far. */ + struct sha256_ctx shactx; + /* Length given by tal_count() */ + struct rune_restr **restrs; +}; + +/* A restriction is one or more alternatives (altern) */ +struct rune_restr { + /* Length given by tal_count() */ + struct rune_altern **alterns; +}; + +enum rune_condition { + RUNE_COND_IF_MISSING = '!', + RUNE_COND_EQUAL = '=', + RUNE_COND_NOT_EQUAL = '/', + RUNE_COND_BEGINS = '^', + RUNE_COND_ENDS = '$', + RUNE_COND_CONTAINS = '~', + RUNE_COND_INT_LESS = '<', + RUNE_COND_INT_GREATER = '>', + RUNE_COND_LEXO_BEFORE = '{', + RUNE_COND_LEXO_AFTER = '}', + RUNE_COND_COMMENT = '#', +}; + +/* An alternative is a utf-8 fieldname, a condition, and a value */ +struct rune_altern { + enum rune_condition condition; + /* Strings. */ + const char *fieldname, *value; +}; + +/** + * rune_new - Create an unrestricted rune from this secret. + * @ctx: tal context, or NULL. Freeing @ctx will free the returned rune. + * @secret: secret bytes. + * @secret_len: number of @secret bytes (must be 55 bytes or less) + * @version: if non-NULL, sets a version for this rune. + * + * This allocates a new, unrestricted rune (sometimes called a master rune). + * + * Setting a version allows for different interpretations of a rune if + * things change in future, at cost of some space when it's used. + * + * Example: + * u8 secret[16]; + * struct rune *master; + * + * // A secret determined with a fair die roll! + * memset(secret, 5, sizeof(secret)); + * master = rune_new(NULL, secret, sizeof(secret), NULL); + * assert(master); + */ +struct rune *rune_new(const tal_t *ctx, const u8 *secret, size_t secret_len, + const char *version); + +/** + * rune_derive_start - Copy master rune, add a unique id. + * @ctx: context to allocate rune off + * @master: master rune. + * @unique_id: unique id; can be NULL, but that's not recommended. + * + * It's usually recommended to assign each rune a unique_id, so that + * specific runes can be blacklisted later (otherwise you need to disable + * all runes). This enlarges the rune string by '=' however. + * + * The rune version will be the same as the master: if that's non-zero, + * you *must* set unique_id. + * + * @unique_id cannot contain '-'. + * + * Example: + * struct rune *rune; + * // In reality, some global incrementing variable. + * const char *id = "1"; + * rune = rune_derive_start(NULL, master, id); + * assert(rune); + */ +struct rune *rune_derive_start(const tal_t *ctx, + const struct rune *master, + const char *unique_id); + +/** + * rune_dup - Copy a rune. + * @ctx: tal context, or NULL. + * @altern: the altern to copy. + * + * If @altern is take(), then simply returns it, otherwise copies. + */ +struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES); + +/** + * rune_altern_new - Create a new alternative. + * @ctx: tal context, or NULL. Freeing @ctx will free the returned altern. + * @fieldname: the UTF-8 field for the altern. You can only have + * alphanumerics, '.', '-' and '_' here. + * @condition: the condition, defined above. + * @value: the value for comparison; use "" if you don't care. Any UTF-8 value + * is allowed. + * + * An altern is the basis of rune restrictions (technically, a restriction + * is one or more alterns, but it's often just one). + * + * Example: + * struct rune_altern *a1, *a2; + * a1 = rune_altern_new(NULL, "val", RUNE_COND_EQUAL, "7"); + * a2 = rune_altern_new(NULL, "val2", '>', "-1"); + * assert(a1 && a2); + */ +struct rune_altern *rune_altern_new(const tal_t *ctx, + const char *fieldname TAKES, + enum rune_condition condition, + const char *value TAKES); + +/** + * rune_altern_dup - copy an alternative. + * @ctx: tal context, or NULL. + * @altern: the altern to copy. + * + * If @altern is take(), then simply returns it, otherwise copies. + */ +struct rune_altern *rune_altern_dup(const tal_t *ctx, + const struct rune_altern *altern TAKES); + +/** + * rune_restr_new - Create a new (empty) restriction. + * @ctx: tal context, or NULL. Freeing @ctx will free the returned restriction. + * + * Example: + * struct rune_restr *restr = rune_restr_new(NULL); + * assert(restr); + */ +struct rune_restr *rune_restr_new(const tal_t *ctx); + +/** + * rune_restr_dup - copy a restr. + * @ctx: tal context, or NULL. + * @restr: the restr to copy. + * + * If @resttr is take(), then simply returns it, otherwise copies. + */ +struct rune_restr *rune_restr_dup(const tal_t *ctx, + const struct rune_restr *restr TAKES); + +/** + * rune_restr_add_altern - add an altern to this restriction + * @restr: the restriction to add to + * @alt: the altern. + * + * If the alt is take(alt) then the alt will be owned by the restriction, + * otherwise it's copied. + * + * Example: + * rune_restr_add_altern(restr, take(a1)); + * rune_restr_add_altern(restr, take(a2)); + */ +void rune_restr_add_altern(struct rune_restr *restr, + const struct rune_altern *alt TAKES); + +/** + * rune_add_restr - add a restriction to this rune + * @rune: the rune to add to. + * @restr: the (non-empty) restriction. + * + * If the alt is take(alt) then the alt will be owned by the restr, + * otherwise it's copied (and all its children are copied!). + * + * This fails (and returns false) if restr tries to set unique_id/version + * and is not the first restriction, or has more than one alternative, + * or uses a non '=' condition. + * + * Example: + * rune_add_restr(rune, take(restr)); + */ +bool rune_add_restr(struct rune *rune, + const struct rune_restr *restr TAKES); + +/** + * rune_altern_eq - are two rune_altern equivalent? + * @alt1: the first + * @alt2: the second + */ +bool rune_altern_eq(const struct rune_altern *alt1, + const struct rune_altern *alt2); + +/** + * rune_restr_eq - are two rune_restr equivalent? + * @rest1: the first + * @rest2: the second + */ +bool rune_restr_eq(const struct rune_restr *rest1, + const struct rune_restr *rest2); + +/** + * rune_eq - are two runes equivalent? + * @rest1: the first + * @rest2: the second + */ +bool rune_eq(const struct rune *rune1, const struct rune *rune2); + +/** + * rune_alt_single_str - helper to implement check(). + * @ctx: context to allocate any error return from. + * @alt: alternative to test. + * @fieldval_str: field value as a string. + * @fieldval_strlen: length of @fieldval_str + */ +const char *rune_alt_single_str(const tal_t *ctx, + const struct rune_altern *alt, + const char *fieldval_str, + size_t fieldval_strlen); + +/** + * rune_alt_single_int - helper to implement check(). + * @ctx: context to allocate any error return from. + * @alt: alternative to test. + * @fieldval_int: field value as an integer. + */ +const char *rune_alt_single_int(const tal_t *ctx, + const struct rune_altern *alt, + s64 fieldval_int); + +/** + * rune_alt_single_missing - helper to implement check(). + * @ctx: context to allocate any error return from. + * @alt: alternative to test. + * + * Use this if alt->fieldname is unknown (it could still pass, if + * the test is that the fieldname is missing). + */ +const char *rune_alt_single_missing(const tal_t *ctx, + const struct rune_altern *alt); + + +/** + * rune_is_derived - is a rune derived from this other rune? + * @source: the base rune (usually the master rune) + * @rune: the rune to check. + * + * This is the first part of "is this rune valid?": does the cryptography + * check out, such that they validly made the rune from this source rune? + * + * It also checks that the versions match: if you want to allow more than + * one version, see rune_is_derived_anyversion. + */ +const char *rune_is_derived(const struct rune *source, const struct rune *rune); + +/** + * rune_is_derived_anyversion - is a rune derived from this other rune? + * @source: the base rune (usually the master rune) + * @rune: the rune to check. + * + * This does not check source->version against rune->version: if you issue + * different rune versions you will need to check that yourself. + */ +const char *rune_is_derived_anyversion(const struct rune *source, + const struct rune *rune); + +/** + * rune_meets_criteria - do we meet the criteria specified by the rune? + * @ctx: the tal context to allocate the returned error off. + * @rune: the rune to check. + * @check: the callback to check values + * @arg: data to hand to @check + * + * This is the second part of "is this rune valid?". + */ +const char *rune_meets_criteria_(const tal_t *ctx, + const struct rune *rune, + const char *(*check)(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + void *arg), + void *arg); + +/* Typesafe wrapper */ +#define rune_meets_criteria(ctx, rune, check, arg) \ + rune_meets_criteria_(typesafe_cb_preargs(const char *, void *, \ + (ctx), (rune), \ + (check), (arg), \ + const tal_t *, \ + const struct rune *, \ + const struct rune_altern *), \ + (arg)) + +/** + * rune_test - is a rune authorized? + * @ctx: the tal context to allocate @errstr off. + * @master: the master rune created from secret. + * @rune: the rune to check. + * @errstr: if non-NULL, descriptive string of failure. + * @get: the callback to get values + * @arg: data to hand to callback + * + * Simple call for rune_is_derived() and rune_meets_criteria(). If + * it's not OK, returns non-NULL. + */ +const char *rune_test_(const tal_t *ctx, + const struct rune *master, + const struct rune *rune, + const char *(*check)(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + void *arg), + void *arg); + +/* Typesafe wrapper */ +#define rune_test(ctx_, master_, rune_, check_, arg_) \ + rune_test_((ctx_), (master_), (rune_), \ + typesafe_cb_preargs(const char *, void *, \ + (check_), (arg_), \ + const tal_t *, \ + const struct rune *, \ + const struct rune_altern *), \ + (arg_)) + + +/** + * rune_from_base64 - convert base64 string to rune. + * @ctx: context to allocate rune off. + * @str: base64 string. + * + * Returns NULL if it's malformed. + */ +struct rune *rune_from_base64(const tal_t *ctx, const char *str); + +/** + * rune_from_base64n - convert base64 string to rune. + * @ctx: context to allocate rune off. + * @str: base64 string. + * @len: length of @str. + * + * Returns NULL if it's malformed. + */ +struct rune *rune_from_base64n(const tal_t *ctx, const char *str, size_t len); + +/** + * rune_to_base64 - convert run to base64 string. + * @ctx: context to allocate rune off. + * @rune: the rune. + * + * Only returns NULL if you've allowed tal allocations to return NULL. + */ +char *rune_to_base64(const tal_t *ctx, const struct rune *rune); + +/** + * This is a much more convenient working form. + */ +struct rune *rune_from_string(const tal_t *ctx, const char *str); +char *rune_to_string(const tal_t *ctx, const struct rune *rune); + +/** + * rune_restr_from_string - convenience routine to parse a single restriction. + * @ctx: context to allocate rune off. + * @str: the string of form "[|]*" + * @len: the length of @str. + * + * This is useful for writing simple tests and making simple runes. + */ +struct rune_restr *rune_restr_from_string(const tal_t *ctx, + const char *str, + size_t len); +#endif /* CCAN_RUNE_RUNE_H */ diff --git a/ccan/ccan/rune/test/run-alt-lexicographic-order.c b/ccan/ccan/rune/test/run-alt-lexicographic-order.c new file mode 100644 index 000000000000..a37ee581fb86 --- /dev/null +++ b/ccan/ccan/rune/test/run-alt-lexicographic-order.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +int main(void) +{ + const char *str = "test string"; + plan_tests(strlen(str) * strlen(str)); + + for (size_t i = 0; str[i]; i++) { + char *stra = strdup(str); + stra[i] = '\0'; + for (size_t j = 0; str[j]; j++) { + char *strb = strdup(str); + strb[j] = '\0'; + int lexo, strc; + + lexo = lexo_order(str, i, strb); + strc = strcmp(stra, strb); + if (strc > 0) + ok1(lexo > 0); + else if (strc < 0) + ok1(lexo < 0); + else + ok1(lexo == 0); + free(strb); + } + free(stra); + } + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/rune/test/run.c b/ccan/ccan/rune/test/run.c new file mode 100644 index 000000000000..86737b86be11 --- /dev/null +++ b/ccan/ccan/rune/test/run.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include + +static const char *check(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + char **parts) +{ + const char *val = NULL; + + for (size_t i = 1; parts[i]; i++) { + if (strstarts(parts[i], alt->fieldname) + && parts[i][strlen(alt->fieldname)] == '=') + val = parts[i] + strlen(alt->fieldname) + 1; + } + + /* If it's an integer, hand it like that */ + if (val) { + char *endp; + s64 v = strtol(val, &endp, 10); + if (*endp == '\0' && endp != val) + return rune_alt_single_int(ctx, alt, v); + return rune_alt_single_str(ctx, alt, val, strlen(val)); + } + return rune_alt_single_missing(ctx, alt); +} + +int main(void) +{ + char *vecs; + char **lines; + static const u8 secret_zero[16]; + struct rune *mr; + + /* Test vector rune uses all-zero secret */ + mr = rune_new(NULL, secret_zero, sizeof(secret_zero), NULL); + + /* Python runes library generates test vectors */ + vecs = grab_file(mr, "test/test_vectors.csv"); + assert(vecs); + lines = tal_strsplit(mr, take(vecs), "\n", STR_NO_EMPTY); + + plan_tests(343); + + for (size_t i = 0; lines[i]; i++) { + struct rune *rune1, *rune2; + char **parts; + + parts = tal_strsplit(lines, lines[i], ",", STR_EMPTY_OK); + if (streq(parts[0], "VALID")) { + diag("test %s %s", parts[0], parts[1]); + rune1 = rune_from_string(parts, parts[2]); + ok1(rune1); + rune2 = rune_from_base64(parts, parts[3]); + ok1(rune2); + ok1(rune_eq(rune1, rune2)); + ok1(streq(rune_to_string(parts, rune2), parts[2])); + ok1(streq(rune_to_base64(parts, rune1), parts[3])); + ok1(rune_is_derived_anyversion(mr, rune1) == NULL); + ok1(rune_is_derived_anyversion(mr, rune2) == NULL); + + if (parts[4]) { + if (parts[5]) + ok1(streq(rune1->version, parts[5])); + ok1(streq(rune1->unique_id, parts[4])); + } else { + ok1(!rune1->version); + ok1(!rune1->unique_id); + } + mr->version = NULL; + } else if (streq(parts[0], "DERIVE")) { + struct rune_restr *restr; + diag("test %s %s", parts[0], parts[1]); + rune1 = rune_from_base64(parts, parts[2]); + ok1(rune1); + rune2 = rune_from_base64(parts, parts[3]); + ok1(rune2); + ok1(rune_is_derived_anyversion(mr, rune1) == NULL); + ok1(rune_is_derived_anyversion(mr, rune2) == NULL); + ok1(rune_is_derived_anyversion(rune1, rune2) == NULL); + + restr = rune_restr_new(NULL); + for (size_t i = 4; parts[i]; i+=3) { + struct rune_altern *alt; + alt = rune_altern_new(NULL, + parts[i], + parts[i+1][0], + parts[i+2]); + rune_restr_add_altern(restr, take(alt)); + } + rune_add_restr(rune1, take(restr)); + ok1(rune_eq(rune1, rune2)); + } else if (streq(parts[0], "MALFORMED")) { + diag("test %s %s", parts[0], parts[1]); + rune1 = rune_from_string(parts, parts[2]); + ok1(!rune1); + rune2 = rune_from_base64(parts, parts[3]); + ok1(!rune2); + } else if (streq(parts[0], "BAD DERIVATION")) { + diag("test %s %s", parts[0], parts[1]); + rune1 = rune_from_string(parts, parts[2]); + ok1(rune1); + rune2 = rune_from_base64(parts, parts[3]); + ok1(rune2); + ok1(rune_eq(rune1, rune2)); + ok1(rune_is_derived(mr, rune1) != NULL); + ok1(rune_is_derived(mr, rune2) != NULL); + } else { + const char *err; + diag("test %s", parts[0]); + err = rune_test(parts, mr, rune1, check, parts); + if (streq(parts[0], "PASS")) { + ok1(!err); + } else { + assert(streq(parts[0], "FAIL")); + ok1(err); + } + } + } + + tal_free(mr); + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/rune/test/test_vectors.csv b/ccan/ccan/rune/test/test_vectors.csv new file mode 100644 index 000000000000..a8411693c605 --- /dev/null +++ b/ccan/ccan/rune/test/test_vectors.csv @@ -0,0 +1,151 @@ +VALID,empty rune (secret = [0]*16),374708fff7719dd5979ec875d56cd2286f6d3cf7ec317a3b25632aab28ec37bb:,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s= +PASS +PASS,f1=1 +PASS,f1=var +PASS,f1=\|\&\\ +VALID,unique id 1,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:=1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,1 +VALID,unique id 2 version 1,4520773407c9658646326fdffe685ffbc3c8639a080dae4310b371830a205cf1:=2-1,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,2,1 +VALID,f1 is missing,64a926b7185d7cf98e10a07dfc4e83d2a826896ebdb112ac964566fa2d50b464:f1!,ZKkmtxhdfPmOEKB9_E6D0qgmiW69sRKslkVm-i1QtGRmMSE= +PASS +PASS,f2=f1 +FAIL,f1=1 +FAIL,f1=var +VALID,f1 equals v1,745c6e39cd41ee9f8388af8ad882bae4ee4e8f6b373f7682cc64d8574551fa5f:f1=v1,dFxuOc1B7p-DiK-K2IK65O5Oj2s3P3aCzGTYV0VR-l9mMT12MQ== +PASS,f1=v1 +FAIL,f1=v +FAIL,f1=v1a +FAIL +FAIL,f2=f1 +VALID,f1 not equal v1,c9236a6532bfa8e24bec9a66e96af3fb355f817770e79c5a81f6dd0b5ed20e47:f1/v1,ySNqZTK_qOJL7Jpm6Wrz-zVfgXdw55xagfbdC17SDkdmMS92MQ== +PASS,f1=v2 +PASS,f1=v +PASS,f1=v1a +FAIL +FAIL,f2=v1 +VALID,f1 ends with v1,71f2a1ec9631efc75b01db15fe1f025327ab467f8a83e6bfa7506da222adc5a2:f1$v1,cfKh7JYx78dbAdsV_h8CUyerRn-Kg-a_p1BtoiKtxaJmMSR2MQ== +PASS,f1=v1 +PASS,f1=2v1 +FAIL,f1=v1a +FAIL +VALID,f1 starts with v1,5b13dffbbd9f7b191b0557595d10b22c0acec0c567f8efeba1d7d047927d7bce:f1^v1,WxPf-72fexkbBVdZXRCyLArOwMVn-O_rodfQR5J9e85mMV52MQ== +PASS,f1=v1 +PASS,f1=v1a +FAIL,f1=2v1 +FAIL +VALID,f1 contains v1,ccbe593b72e0ab29446e46796ccd0c775ecd7a327fcc9ddc00fd3910cdacca00:f1~v1,zL5ZO3LgqylEbkZ5bM0Md17NejJ_zJ3cAP05EM2sygBmMX52MQ== +PASS,f1=v1 +PASS,f1=v1a +PASS,f1=2v1 +PASS,f1=2v12 +FAIL,f1=1v2 +FAIL +VALID,f1 less than v1,caff52cedb9241dc00aea7cefc2b89b0a7445b1a4e34c48a5a2b91d2fe76d31f:f1v1,ITV0jxlW2d-jxbCatq-da7BqQcW8-T0_gQXLJ4r1rFZmMT52MQ== +FAIL,f1=1 +FAIL,f1=2 +FAIL,f1=v1 +FAIL +VALID,f1 greater than 1,84e9991dd941bac97cc681eefec5dd7ac3668a4490ca6b0f19f0e79d2bb9c746:f1>1,hOmZHdlBusl8xoHu_sXdesNmikSQymsPGfDnnSu5x0ZmMT4x +PASS,f1=2 +PASS,f1=10000 +FAIL,f1=1 +FAIL,f1=-10000 +FAIL,f1=0 +FAIL,f1=v1 +FAIL +VALID,f1 sorts before 11,b9653ad0dcad7e5ed183f98cdd7e616acd07a98cc66a107a67626290bf000236:f1{11,uWU60Nytfl7Rg_mM3X5has0HqYzGahB6Z2JikL8AAjZmMXsxMQ== +PASS,f1=0 +PASS,f1=1 +PASS,f1= +PASS,f1=/ +FAIL,f1=11 +FAIL,f1=111 +FAIL,f1=v1 +FAIL,f1=: +FAIL +VALID,f1 sorts after 11,8c1f6c7c39badc5dea850192a0a4c6e9dd96bf33d410adc5a08fc375b22a1a52:f1}11,jB9sfDm63F3qhQGSoKTG6d2WvzPUEK3FoI_DdbIqGlJmMX0xMQ== +PASS,f1=111 +PASS,f1=v1 +PASS,f1=: +FAIL,f1=0 +FAIL,f1=1 +FAIL,f1= +FAIL,f1=/ +FAIL,f1=11 +FAIL +VALID,f1 comment 11,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMQ== +PASS,f1=111 +PASS,f1=v1 +PASS,f1=: +PASS,f1=0 +PASS,f1=1 +PASS,f1= +PASS,f1=/ +PASS,f1=11 +PASS +VALID,f1=1 or f2=3,85c3643dc102f0a0d6f20eeb8c294092151688fae41ef7c8ec7272ab23918376:f1=1|f2=3,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM= +PASS,f1=1 +PASS,f1=1,f2=2 +PASS,f2=3 +PASS,f1=var,f2=3 +PASS,f1=1,f2=3 +FAIL +FAIL,f1=2 +FAIL,f1=f1 +FAIL,f2=1 +FAIL,f2=f1 +DERIVE,unique_id 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL09MQ==,,=,1 +DERIVE,unique_id 2 version 1 derivation,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,RSB3NAfJZYZGMm_f_mhf-8PIY5oIDa5DELNxgwogXPE9Mi0x,,=,2-1 +DERIVE,f1=1 or f2=3,N0cI__dxndWXnsh11WzSKG9tPPfsMXo7JWMqqyjsN7s=,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,f1,=,1,f2,=,3 +DERIVE,AND f3 contains &|\,hcNkPcEC8KDW8g7rjClAkhUWiPrkHvfI7HJyqyORg3ZmMT0xfGYyPTM=,S253BW1Lragb1CpCSLXYGt9AdrE4iFMlXmnO0alV5vlmMT0xfGYyPTMmZjN-XCZcfFxc,f3,~,&|\ +PASS,f1=1,f3=&|\ +PASS,f2=3,f3=&|\x +FAIL +FAIL,f1=1 +FAIL,f2=3 +FAIL,f1=1,f2=3 +FAIL,f1=2,f3=&|\ +FAIL,f2=2,f3=&|\ +FAIL,f3=&|\ +MALFORMED,unique id must use = not !,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:!1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0hMQ== +MALFORMED,unique id must use = not /,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:/1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0vMQ== +MALFORMED,unique id must use = not ^,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:^1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1eMQ== +MALFORMED,unique id must use = not $,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:$1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0kMQ== +MALFORMED,unique id must use = not ~,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:~1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL1-MQ== +MALFORMED,unique id must use = not <,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:<1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL08MQ== +MALFORMED,unique id must use = not >,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:>1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL0-MQ== +MALFORMED,unique id must use = not },6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:}1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL19MQ== +MALFORMED,unique id must use = not {,6035731a2cbb022cbeb67645aa0f8a26653d8cc454e0e087d4d19d282b8da4bd:{1,YDVzGiy7Aiy-tnZFqg-KJmU9jMRU4OCH1NGdKCuNpL17MQ== +MALFORMED,unique id cannot be overridden,7a63a2966d38e6fed89256d4a6e983a6813bf084d4fc6c20b9cdaef24b23fa7e:=1-2&=3,emOilm045v7YklbUpumDpoE78ITU_Gwguc2u8ksj-n49MS0yJj0z +MALFORMED,version cannot be overridden,db823224f960976b3ee142ce8899fc7ea461b42617e7d16167b1886c5988c628:=1-2&=1-3,24IyJPlgl2s-4ULOiJn8fqRhtCYX59FhZ7GIbFmIxig9MS0yJj0xLTM= +MALFORMED,Bad condition ",76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1"11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSIxMQ== +MALFORMED,Bad condition &,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1&11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSYxMQ== +MALFORMED,Bad condition ',76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1'11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMScxMQ== +MALFORMED,Bad condition (,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1(11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSgxMQ== +MALFORMED,Bad condition ),76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1)11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSkxMQ== +MALFORMED,Bad condition *,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1*11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSoxMQ== +MALFORMED,Bad condition +,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1+11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSsxMQ== +MALFORMED,Bad condition -,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1-11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS0xMQ== +MALFORMED,Bad condition .,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1.11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMS4xMQ== +MALFORMED,Bad condition :,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1:11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMToxMQ== +MALFORMED,Bad condition ;,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1;11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMTsxMQ== +MALFORMED,Bad condition ?,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1?11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMT8xMQ== +MALFORMED,Bad condition [,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1[11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVsxMQ== +MALFORMED,Bad condition \,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1\11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMVwxMQ== +MALFORMED,Bad condition ],76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1]11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV0xMQ== +MALFORMED,Bad condition _,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1_11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMV8xMQ== +MALFORMED,Bad condition `,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1`11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMWAxMQ== +MALFORMED,Bad condition |,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1|11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMXwxMQ== +BAD DERIVATION,Incremented sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0e:f1#11,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw5mMSMxMQ== +BAD DERIVATION,Unchanged sha,76bdd625de0e12058956e6c8a07cac58d7dc2253609a6bfb959f87cc094f3f0f:f1#11&a=1,dr3WJd4OEgWJVubIoHysWNfcIlNgmmv7lZ-HzAlPPw9mMSMxMSZhPTE= From d0a55a62b36fc6f318ee6d4f5274747d7ecc091d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:28:58 +0930 Subject: [PATCH 0997/1530] common/json_stream: make json_add_jsonstr take a length. This is useful when have have a jsmntok_t. Signed-off-by: Rusty Russell --- common/json_stream.c | 8 ++++---- common/json_stream.h | 7 ++++--- lightningd/jsonrpc.c | 8 +++++--- plugins/spender/multifundchannel.c | 9 ++++++--- plugins/spender/multiwithdraw.c | 2 +- plugins/spender/openchannel.c | 4 +++- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/common/json_stream.c b/common/json_stream.c index f556033525bf..53b374e24260 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -199,13 +199,13 @@ static char *json_member_direct(struct json_stream *js, void json_add_jsonstr(struct json_stream *js, const char *fieldname, - const char *jsonstr) + const char *jsonstr, + size_t jsonstrlen) { char *p; - size_t len = strlen(jsonstr); - p = json_member_direct(js, fieldname, len); - memcpy(p, jsonstr, len); + p = json_member_direct(js, fieldname, jsonstrlen); + memcpy(p, jsonstr, jsonstrlen); } /* This is where we read the json_stream and write it to conn */ diff --git a/common/json_stream.h b/common/json_stream.h index 7743a29a83b1..316da33f4b42 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -157,12 +157,13 @@ void json_add_escaped_string(struct json_stream *result, * JSON-formatted. * @js: the json_stream. * @fieldname: fieldname (if in object), otherwise must be NULL. - * @jsonstr: the JSON entity, must be non-NULL, a null-terminated - * string that is already formatted in JSON. + * @jsonstr: the JSON entity + * @jsonstrlen: the length of @jsonstr */ void json_add_jsonstr(struct json_stream *js, const char *fieldname, - const char *jsonstr); + const char *jsonstr, + size_t jsonstrlen); /** * json_stream_output - start writing out a json_stream to this conn. diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 317346ca1c75..357297f2f3a4 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -578,7 +578,7 @@ static struct json_stream *json_start(struct command *cmd) json_object_start(js, NULL); json_add_string(js, "jsonrpc", "2.0"); - json_add_jsonstr(js, "id", cmd->id); + json_add_jsonstr(js, "id", cmd->id, strlen(cmd->id)); return js; } @@ -742,13 +742,15 @@ static void rpc_command_hook_final(struct rpc_command_hook_payload *p STEALS) if (p->custom_result != NULL) { struct json_stream *s = json_start(p->cmd); - json_add_jsonstr(s, "result", p->custom_result); + json_add_jsonstr(s, "result", + p->custom_result, strlen(p->custom_result)); json_object_end(s); return was_pending(command_raw_complete(p->cmd, s)); } if (p->custom_error != NULL) { struct json_stream *s = json_start(p->cmd); - json_add_jsonstr(s, "error", p->custom_error); + json_add_jsonstr(s, "error", + p->custom_error, strlen(p->custom_error)); json_object_end(s); return was_pending(command_raw_complete(p->cmd, s)); } diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index b721d73c46d9..2c8a02ce577e 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -499,7 +499,8 @@ multifundchannel_finished(struct multifundchannel_command *mfc) mfc->removeds[i].error_message); if (mfc->removeds[i].error_data) json_add_jsonstr(out, "data", - mfc->removeds[i].error_data); + mfc->removeds[i].error_data, + strlen(mfc->removeds[i].error_data)); json_object_end(out); /* End error object */ json_object_end(out); } @@ -1382,7 +1383,8 @@ perform_fundpsbt(struct multifundchannel_command *mfc, u32 feerate) &after_fundpsbt, &mfc_forward_error, mfc); - json_add_jsonstr(req->js, "utxos", mfc->utxos_str); + json_add_jsonstr(req->js, "utxos", + mfc->utxos_str, strlen(mfc->utxos_str)); json_add_bool(req->js, "reservedok", false); } else { plugin_log(mfc->cmd->plugin, LOG_DBG, @@ -1814,7 +1816,8 @@ post_cleanup_redo_multifundchannel(struct multifundchannel_redo *redo) json_add_string(out, "method", failing_method); if (mfc->removeds[i].error_data) json_add_jsonstr(out, "data", - mfc->removeds[i].error_data); + mfc->removeds[i].error_data, + strlen(mfc->removeds[i].error_data)); /* Close 'data'. */ json_object_end(out); diff --git a/plugins/spender/multiwithdraw.c b/plugins/spender/multiwithdraw.c index 4bd7605f0bc2..9ab29bb9ef2c 100644 --- a/plugins/spender/multiwithdraw.c +++ b/plugins/spender/multiwithdraw.c @@ -356,7 +356,7 @@ static struct command_result *start_mw(struct multiwithdraw_command *mw) &mw_forward_error, mw); json_add_bool(req->js, "reservedok", false); - json_add_jsonstr(req->js, "utxos", mw->utxos); + json_add_jsonstr(req->js, "utxos", mw->utxos, strlen(mw->utxos)); } else { plugin_log(mw->cmd->plugin, LOG_DBG, "multiwithdraw %"PRIu64": fundpsbt.", diff --git a/plugins/spender/openchannel.c b/plugins/spender/openchannel.c index 7973bd0bedbb..0143c70704e2 100644 --- a/plugins/spender/openchannel.c +++ b/plugins/spender/openchannel.c @@ -346,7 +346,9 @@ openchannel_finished(struct multifundchannel_command *mfc) json_add_node_id(out, "id", &dest->id); json_add_string(out, "method", "openchannel_signed"); if (dest->error_data) - json_add_jsonstr(out, "data", dest->error_data); + json_add_jsonstr(out, "data", + dest->error_data, + strlen(dest->error_data)); json_object_end(out); return mfc_finished(mfc, out); From d3e64c39700a351843fa1c0f1b2bb8469ee38c66 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:30:17 +0930 Subject: [PATCH 0998/1530] libplugin: jsonrpc_request_whole_object_start() for more custom request handling. commando wants to see the whole reply object, and also not to assume params is an object. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 26 +++++++++++++++++++------- plugins/libplugin.h | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 74311b81c8b7..ee45e8874fcc 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -165,7 +165,8 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, json_add_string(out->js, "jsonrpc", "2.0"); json_add_u64(out->js, "id", out->id); json_add_string(out->js, "method", method); - json_object_start(out->js, "params"); + if (out->errcb) + json_object_start(out->js, "params"); return out; } @@ -551,16 +552,26 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) uintmap_del(&plugin->out_reqs, out->id); contenttok = json_get_member(plugin->rpc_buffer, toks, "error"); - if (contenttok) - res = out->errcb(out->cmd, plugin->rpc_buffer, - contenttok, out->arg); - else { + if (contenttok) { + if (out->errcb) + res = out->errcb(out->cmd, plugin->rpc_buffer, + contenttok, out->arg); + else + res = out->cb(out->cmd, plugin->rpc_buffer, + toks, out->arg); + } else { contenttok = json_get_member(plugin->rpc_buffer, toks, "result"); if (!contenttok) plugin_err(plugin, "Bad JSONRPC, no 'error' nor 'result': '%.*s'", json_tok_full_len(toks), json_tok_full(plugin->rpc_buffer, toks)); - res = out->cb(out->cmd, plugin->rpc_buffer, contenttok, out->arg); + /* errcb is NULL if it's a single whole-object callback */ + if (out->errcb) + res = out->cb(out->cmd, plugin->rpc_buffer, contenttok, + out->arg); + else + res = out->cb(out->cmd, plugin->rpc_buffer, toks, + out->arg); } assert(res == &pending || res == &complete); @@ -570,7 +581,8 @@ struct command_result * send_outreq(struct plugin *plugin, const struct out_req *req) { /* The "param" object. */ - json_object_end(req->js); + if (req->errcb) + json_object_end(req->js); json_object_end(req->js); json_stream_close(req->js, req->cmd); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index feb6b4d8162e..6b90ef6e5793 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -117,6 +117,8 @@ struct out_req *jsonrpc_request_start_(struct plugin *plugin, void *arg), void *arg); +/* This variant has callbacks received whole obj, not "result" or + * "error" members. */ #define jsonrpc_request_start(plugin, cmd, method, cb, errcb, arg) \ jsonrpc_request_start_((plugin), (cmd), (method), \ typesafe_cb_preargs(struct command_result *, void *, \ @@ -132,6 +134,18 @@ struct out_req *jsonrpc_request_start_(struct plugin *plugin, (arg)) +/* This variant has callbacks received whole obj, not "result" or + * "error" members. It also doesn't start params{}. */ +#define jsonrpc_request_whole_object_start(plugin, cmd, method, cb, arg) \ + jsonrpc_request_start_((plugin), (cmd), (method), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (cb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + NULL, \ + (arg)) + /* Helper to create a JSONRPC2 response stream with a "result" object. */ struct json_stream *jsonrpc_stream_success(struct command *cmd); From 3eccf16f982b9ce8612913e2259a043ca98a3845 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:30:19 +0930 Subject: [PATCH 0999/1530] libplugin: datastore helpers. Plugins are supposed to store their data in the datastore, and commando does so: let's make it easier for them by providing convenience APIs. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 123 +++++++++++++++++++++++++++++++++++++++----- plugins/libplugin.h | 58 +++++++++++++++++++++ 2 files changed, 169 insertions(+), 12 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index ee45e8874fcc..6225289bc85c 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -490,20 +490,18 @@ static struct json_out *start_json_request(const tal_t *ctx, return jout; } -/* Synchronous routine to send command and extract fields from response */ -void rpc_scan(struct plugin *plugin, - const char *method, - const struct json_out *params TAKES, - const char *guide, - ...) +static const char *rpc_scan_core(const tal_t *ctx, + struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + va_list ap) { bool error; - const char *err; const jsmntok_t *contents; int reqlen; const char *p; struct json_out *jout; - va_list ap; jout = start_json_request(tmpctx, 0, method, params); finish_and_send_json(plugin->rpc_conn->fd, jout); @@ -514,15 +512,116 @@ void rpc_scan(struct plugin *plugin, method, reqlen, membuf_elems(&plugin->rpc_conn->mb)); p = membuf_consume(&plugin->rpc_conn->mb, reqlen); + return json_scanv(ctx, p, contents, guide, ap); +} + +/* Synchronous routine to send command and extract fields from response */ +void rpc_scan(struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + ...) +{ + const char *err; + va_list ap; va_start(ap, guide); - err = json_scanv(tmpctx, p, contents, guide, ap); + err = rpc_scan_core(tmpctx, plugin, method, params, guide, ap); va_end(ap); if (err) - plugin_err(plugin, "Could not parse %s in reply to %s: %s: '%.*s'", - guide, method, err, - reqlen, membuf_elems(&plugin->rpc_conn->mb)); + plugin_err(plugin, "Could not parse %s in reply to %s: %s", + guide, method, err); +} + +static void json_add_keypath(struct json_out *jout, const char *fieldname, const char *path) +{ + char **parts = tal_strsplit(tmpctx, path, "/", STR_EMPTY_OK); + + json_out_start(jout, fieldname, '['); + for (size_t i = 0; parts[i]; parts++) + json_out_addstr(jout, NULL, parts[i]); + json_out_end(jout, ']'); +} + +static bool rpc_scan_datastore(struct plugin *plugin, + const char *path, + const char *hex_or_string, + va_list ap) +{ + const char *guide; + struct json_out *params; + const char *err; + + params = json_out_new(NULL); + json_out_start(params, NULL, '{'); + json_add_keypath(params, "key", path); + json_out_end(params, '}'); + json_out_finished(params); + + guide = tal_fmt(tmpctx, "{datastore:[0:{%s:%%}]}", hex_or_string); + /* FIXME: Could be some other error, but that's probably a caller bug! */ + err = rpc_scan_core(tmpctx, plugin, "listdatastore", take(params), guide, ap); + if (!err) + return true; + plugin_log(plugin, LOG_DBG, "listdatastore error %s: %s", path, err); + return false; +} + +bool rpc_scan_datastore_str(struct plugin *plugin, + const char *path, + ...) +{ + bool ret; + va_list ap; + + va_start(ap, path); + ret = rpc_scan_datastore(plugin, path, "string", ap); + va_end(ap); + return ret; +} + +/* This variant scans the hex encoding, not the string */ +bool rpc_scan_datastore_hex(struct plugin *plugin, + const char *path, + ...) +{ + bool ret; + va_list ap; + + va_start(ap, path); + ret = rpc_scan_datastore(plugin, path, "hex", ap); + va_end(ap); + return ret; +} + +struct command_result *jsonrpc_set_datastore_(struct plugin *plugin, + struct command *cmd, + const char *path, + const void *value, + bool value_is_string, + const char *mode, + struct command_result *(*cb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + struct command_result *(*errcb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + void *arg) +{ + struct out_req *req; + + req = jsonrpc_request_start(plugin, cmd, "datastore", cb, errcb, arg); + + json_add_keypath(req->js->jout, "key", path); + if (value_is_string) + json_add_string(req->js, "string", value); + else + json_add_hex_talarr(req->js, "hex", value); + json_add_string(req->js, "mode", mode); + return send_outreq(plugin, req); } static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 6b90ef6e5793..6342460cdc5e 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -160,6 +160,51 @@ struct json_stream *jsonrpc_stream_fail_data(struct command *cmd, int code, const char *err); +/* Helper to jsonrpc_request_start() and send_outreq() to update datastore. */ +struct command_result *jsonrpc_set_datastore_(struct plugin *plugin, + struct command *cmd, + const char *path, + const void *value, + bool value_is_string, + const char *mode, + struct command_result *(*cb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + struct command_result *(*errcb)(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg), + void *arg); + +#define jsonrpc_set_datastore_string(plugin, cmd, path, str, mode, cb, errcb, arg) \ + jsonrpc_set_datastore_((plugin), (cmd), (path), (str), true, (mode), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (cb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (errcb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + (arg)) + +#define jsonrpc_set_datastore_binary(plugin, cmd, path, tal_ptr, mode, cb, errcb, arg) \ + jsonrpc_set_datastore_((plugin), (cmd), (path), (tal_ptr), false, (mode), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (cb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + typesafe_cb_preargs(struct command_result *, void *, \ + (errcb), (arg), \ + struct command *command, \ + const char *buf, \ + const jsmntok_t *result), \ + (arg)) + /* This command is finished, here's the response (the content of the * "result" or "error" field) */ WARN_UNUSED_RESULT @@ -220,6 +265,19 @@ void rpc_scan(struct plugin *plugin, const char *guide, ...); +/* Helper to scan datastore: can only be used in init callback. * + Returns false if field does not exist. * path is /-separated. Final + arg is JSON_SCAN or JSON_SCAN_TAL. + */ +bool rpc_scan_datastore_str(struct plugin *plugin, + const char *path, + ...); +/* This variant scans the hex encoding, not the string */ +bool rpc_scan_datastore_hex(struct plugin *plugin, + const char *path, + ...); + + /* Send an async rpc request to lightningd. */ struct command_result *send_outreq(struct plugin *plugin, const struct out_req *req); From 3fe246c2e70fbec84f851880c5423447cb6f398f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:21 +0930 Subject: [PATCH 1000/1530] plugins/commando: basic commando plugin (no runes yet). Signed-off-by: Rusty Russell +#include +#include +#include +#include +#include +#include + +/* We (as your local commando command) detected an error. */ +#define COMMANDO_ERROR_LOCAL 0x4c4f +/* Remote (as executing your commando command) detected an error. */ +#define COMMANDO_ERROR_REMOTE 0x4c50 +/* Specifically: bad/missing rune */ +#define COMMANDO_ERROR_REMOTE_AUTH 0x4c51 + +enum commando_msgtype { + COMMANDO_MSG_CMD = 0x4c4f, + /* Replies are split across multiple CONTINUES, then TERM. */ + COMMANDO_MSG_REPLY_CONTINUES = 0x594b, + COMMANDO_MSG_REPLY_TERM = 0x594d, +}; + +struct commando { + struct command *cmd; + struct node_id peer; + u64 id; + + /* This is set to NULL if they seem to be spamming us! */ + u8 *contents; +}; + +static struct plugin *plugin; +static struct commando **outgoing_commands; + +/* NULL peer: don't care about peer. NULL id: don't care about id */ +static struct commando *find_commando(struct commando **arr, + const struct node_id *peer, + const u64 *id) +{ + for (size_t i = 0; i < tal_count(arr); i++) { + if (id && arr[i]->id != *id) + continue; + if (peer && !node_id_eq(&arr[i]->peer, peer)) + continue; + return arr[i]; + } + return NULL; +} + +static void destroy_commando(struct commando *commando, struct commando ***arr) +{ + for (size_t i = 0; i < tal_count(*arr); i++) { + if ((*arr)[i] == commando) { + tal_arr_remove(arr, i); + return; + } + } + abort(); +} + +/* Append to commando->contents: set to NULL if we've over max. */ +static void append_contents(struct commando *commando, const u8 *msg, size_t msglen, + size_t maxlen) +{ + size_t len = tal_count(commando->contents); + + if (!commando->contents) + return; + + if (len + msglen > maxlen) { + commando->contents = tal_free(commando->contents); + return; + } + + tal_resize(&commando->contents, len + msglen); + memcpy(commando->contents + len, msg, msglen); +} + +struct reply { + struct commando *incoming; + char *buf; + size_t off, len; +}; + +static struct command_result *send_response(struct command *command UNUSED, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct reply *reply) +{ + size_t msglen = reply->len - reply->off; + u8 *cmd_msg; + enum commando_msgtype msgtype; + struct out_req *req; + + /* Limit is 64k, but there's a little overhead */ + if (msglen > 65000) { + msglen = 65000; + msgtype = COMMANDO_MSG_REPLY_CONTINUES; + /* We need to make a copy first time before we call back, since + * plugin will reuse it! */ + if (reply->off == 0) + reply->buf = tal_dup_talarr(reply, char, reply->buf); + } else { + if (msglen == 0) { + tal_free(reply); + return command_done(); + } + msgtype = COMMANDO_MSG_REPLY_TERM; + } + + cmd_msg = tal_arr(NULL, u8, 0); + towire_u16(&cmd_msg, msgtype); + towire_u64(&cmd_msg, reply->incoming->id); + towire(&cmd_msg, reply->buf + reply->off, msglen); + reply->off += msglen; + + req = jsonrpc_request_start(plugin, NULL, "sendcustommsg", + send_response, send_response, + reply); + json_add_node_id(req->js, "node_id", &reply->incoming->peer); + json_add_hex_talarr(req->js, "msg", cmd_msg); + tal_free(cmd_msg); + send_outreq(plugin, req); + + return command_done(); +} + +static struct command_result *cmd_done(struct command *command, + const char *buf, + const jsmntok_t *obj, + struct commando *incoming) +{ + struct reply *reply = tal(plugin, struct reply); + reply->incoming = tal_steal(reply, incoming); + reply->buf = (char *)buf; + + /* result is contents of "error" or "response": we want top-leve + * object */ + reply->off = obj->start; + reply->len = obj->end; + + return send_response(command, buf, obj, reply); +} + +static void commando_error(struct commando *incoming, + int ecode, + const char *fmt, ...) + PRINTF_FMT(3,4); + +static void commando_error(struct commando *incoming, + int ecode, + const char *fmt, ...) +{ + struct reply *reply = tal(plugin, struct reply); + va_list ap; + + reply->incoming = tal_steal(reply, incoming); + reply->buf = tal_fmt(reply, "{\"error\":{\"code\":%i,\"message\":\"", ecode); + va_start(ap, fmt); + tal_append_vfmt(&reply->buf, fmt, ap); + va_end(ap); + tal_append_fmt(&reply->buf, "\"}}"); + reply->off = 0; + reply->len = tal_bytelen(reply->buf) - 1; + + send_response(NULL, NULL, NULL, reply); +} + +static const char *check_rune(struct commando *incoming, + const char *buf, + const jsmntok_t *method, + const jsmntok_t *params, + const jsmntok_t *rune) +{ + /* FIXME! */ + return NULL; +} + +static void try_command(struct node_id *peer, + u64 idnum, + const u8 *msg, size_t msglen) +{ + struct commando *incoming = tal(plugin, struct commando); + const jsmntok_t *toks, *method, *params, *rune; + const char *buf = (const char *)msg, *failmsg; + struct out_req *req; + + incoming->peer = *peer; + incoming->id = idnum; + + toks = json_parse_simple(incoming, buf, msglen); + if (!toks) { + commando_error(incoming, COMMANDO_ERROR_REMOTE, + "Invalid JSON"); + return; + } + + if (toks[0].type != JSMN_OBJECT) { + commando_error(incoming, COMMANDO_ERROR_REMOTE, + "Not a JSON object"); + return; + } + method = json_get_member(buf, toks, "method"); + if (!method) { + commando_error(incoming, COMMANDO_ERROR_REMOTE, + "No method"); + return; + } + params = json_get_member(buf, toks, "params"); + if (params && params->type != JSMN_OBJECT) { + commando_error(incoming, COMMANDO_ERROR_REMOTE, + "Params must be object"); + return; + } + rune = json_get_member(buf, toks, "rune"); + + failmsg = check_rune(incoming, buf, method, params, rune); + if (failmsg) { + commando_error(incoming, COMMANDO_ERROR_REMOTE_AUTH, + "Not authorized: %s", failmsg); + return; + } + + /* We handle success and failure the same */ + req = jsonrpc_request_whole_object_start(plugin, NULL, + json_strdup(tmpctx, buf, + method), + cmd_done, incoming); + if (params) { + size_t i; + const jsmntok_t *t; + + json_object_start(req->js, "params"); + /* FIXME: This is ugly! */ + json_for_each_obj(i, t, params) { + json_add_jsonstr(req->js, + json_strdup(tmpctx, buf, t), + json_tok_full(buf, t+1), + json_tok_full_len(t+1)); + } + json_object_end(req->js); + } else { + json_object_start(req->js, "params"); + json_object_end(req->js); + } + tal_free(toks); + send_outreq(plugin, req); +} + +static struct command_result *handle_reply(struct node_id *peer, + u64 idnum, + const u8 *msg, size_t msglen, + bool terminal) +{ + struct commando *ocmd; + struct json_stream *res; + const jsmntok_t *toks, *result, *err; + const char *replystr; + size_t i; + const jsmntok_t *t; + + ocmd = find_commando(outgoing_commands, peer, &idnum); + if (!ocmd) { + plugin_log(plugin, LOG_DBG, + "Ignoring unexpected %s reply from %s (id %"PRIu64")", + terminal ? "terminal" : "partial", + node_id_to_hexstr(tmpctx, peer), + idnum); + return NULL; + } + + /* FIXME: We buffer, but ideally we would stream! */ + /* listchannels is 71MB, so we need to allow some headroom! */ + append_contents(ocmd, msg, msglen, 500*1024*1024); + + if (!terminal) + return NULL; + + if (!ocmd->contents) + return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, "Reply was oversize"); + + replystr = (const char *)ocmd->contents; + toks = json_parse_simple(ocmd, replystr, tal_bytelen(ocmd->contents)); + if (!toks || toks[0].type != JSMN_OBJECT) + return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, "Reply was unparsable"); + + err = json_get_member(replystr, toks, "error"); + if (err) { + const jsmntok_t *code = json_get_member(replystr, err, "code"); + int ecode; + const jsmntok_t *message = json_get_member(replystr, err, "message"); + if (!code || !json_to_int(replystr, code, &ecode)) { + return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, + "Error '%.*s' had no valid code", + json_tok_full_len(err), + json_tok_full(replystr, err)); + } + if (!message) { + return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, + "Error had no message"); + } + /* FIXME: data! */ + return command_fail(ocmd->cmd, ecode, "%.*s", + message->end - message->start, + replystr + message->start); + } + + result = json_get_member(replystr, toks, "result"); + if (!result) + return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, "Reply had no result"); + + res = jsonrpc_stream_success(ocmd->cmd); + + /* FIXME: This is ugly! */ + json_for_each_obj(i, t, result) { + json_add_jsonstr(res, + json_strdup(tmpctx, replystr, t), + json_tok_full(replystr, t+1), + json_tok_full_len(t+1)); + } + + return command_finished(ocmd->cmd, res); +} + +static struct command_result *handle_custommsg(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct node_id peer; + const u8 *msg; + size_t len; + enum commando_msgtype mtype; + u64 idnum; + + json_to_node_id(buf, json_get_member(buf, params, "peer_id"), &peer); + msg = json_tok_bin_from_hex(cmd, buf, + json_get_member(buf, params, "payload")); + + len = tal_bytelen(msg); + mtype = fromwire_u16(&msg, &len); + idnum = fromwire_u64(&msg, &len); + + if (msg) { + switch (mtype) { + case COMMANDO_MSG_CMD: + try_command(&peer, idnum, msg, len); + break; + case COMMANDO_MSG_REPLY_CONTINUES: + case COMMANDO_MSG_REPLY_TERM: + handle_reply(&peer, idnum, msg, len, + mtype == COMMANDO_MSG_REPLY_TERM); + break; + } + } + + return command_hook_success(cmd); +} + +static const struct plugin_hook hooks[] = { + { + "custommsg", + handle_custommsg + }, +}; + +static struct command_result *send_success(struct command *command, + const char *buf, + const jsmntok_t *result, + struct commando *incoming) +{ + return command_still_pending(command); +} + + +static struct command_result *json_commando(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct node_id *peer; + const char *method, *cparams; + const char *rune; + struct commando *ocmd; + struct out_req *req; + u8 *cmd_msg; + char *json; + + if (!param(cmd, buffer, params, + p_req("peer_id", param_node_id, &peer), + p_req("method", param_string, &method), + p_opt("params", param_string, &cparams), + p_opt("rune", param_string, &rune), + NULL)) + return command_param_failed(); + + ocmd = tal(cmd, struct commando); + ocmd->cmd = cmd; + ocmd->peer = *peer; + ocmd->contents = tal_arr(ocmd, u8, 0); + do { + ocmd->id = pseudorand_u64(); + } while (find_commando(outgoing_commands, NULL, &ocmd->id)); + tal_arr_expand(&outgoing_commands, ocmd); + tal_add_destructor2(ocmd, destroy_commando, &outgoing_commands); + + json = tal_fmt(tmpctx, + "{\"method\":\"%s\",\"params\":%s", method, + cparams ? cparams : "{}"); + if (rune) + tal_append_fmt(&json, ",\"rune\":\"%s\"", rune); + tal_append_fmt(&json, "}"); + + cmd_msg = tal_arr(NULL, u8, 0); + towire_u16(&cmd_msg, COMMANDO_MSG_CMD); + towire_u64(&cmd_msg, ocmd->id); + towire(&cmd_msg, json, strlen(json)); + req = jsonrpc_request_start(plugin, NULL, "sendcustommsg", + send_success, forward_error, ocmd); + json_add_node_id(req->js, "node_id", &ocmd->peer); + json_add_hex_talarr(req->js, "msg", cmd_msg); + tal_free(cmd_msg); + + /* Keep memleak code happy! */ + tal_free(peer); + tal_free(method); + tal_free(cparams); + + return send_outreq(plugin, req); +} + +#if DEVELOPER +static void memleak_mark_globals(struct plugin *p, struct htable *memtable) +{ + memleak_remove_region(memtable, outgoing_commands, tal_bytelen(outgoing_commands)); +} +#endif + +static const char *init(struct plugin *p, + const char *buf UNUSED, const jsmntok_t *config UNUSED) +{ + outgoing_commands = tal_arr(p, struct commando *, 0); + plugin = p; +#if DEVELOPER + plugin_set_memleak_handler(p, memleak_mark_globals); +#endif + return NULL; +} + +static const struct plugin_command commands[] = { { + "commando", + "utility", + "Send a commando message to a direct peer, wait for response", + "Sends {peer_id} {method} with optional {params} and {rune}", + json_commando, + } +}; + +int main(int argc, char *argv[]) +{ + setup_locale(); + plugin_main(argv, init, PLUGIN_STATIC, true, NULL, + commands, ARRAY_SIZE(commands), + NULL, 0, + hooks, ARRAY_SIZE(hooks), + NULL, 0, + NULL); +} diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 214dadf09197..74490cf93078 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2544,3 +2544,46 @@ def test_plugin_shutdown(node_factory): l1.daemon.wait_for_logs(['test_libplugin: shutdown called', 'misc_notifications.py: via lightningd shutdown, datastore failed', 'test_libplugin: failed to self-terminate in time, killing.']) + + +def test_commando(node_factory): + l1, l2 = node_factory.line_graph(2, fundchannel=False) + + # This works + res = l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'listpeers'}) + assert len(res['peers']) == 1 + assert res['peers'][0]['id'] == l2.info['id'] + + res = l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'listpeers', + 'params': {'id': l2.info['id']}}) + assert len(res['peers']) == 1 + assert res['peers'][0]['id'] == l2.info['id'] + + with pytest.raises(RpcError, match='missing required parameter'): + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'withdraw'}) + + with pytest.raises(RpcError, match='unknown parameter: foobar'): + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'invoice', + 'params': {'foobar': 1}}) + + ret = l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'ping', + 'params': {'id': l2.info['id']}}) + assert 'totlen' in ret + + # Now, reply will go over a multiple messages! + ret = l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'getlog', + 'params': {'level': 'io'}}) + + assert len(json.dumps(ret)) > 65535 From 49df89556bb3ad5f2f5d26ade31fc3724578785c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1001/1530] commando: support commands larger than 64k. This is needed for invoice, which can be asked to commit to giant descriptions (though that's antisocial!). Signed-off-by: Rusty Russell --- plugins/commando.c | 129 ++++++++++++++++++++++++++++++++++--------- tests/test_plugin.py | 11 ++++ 2 files changed, 115 insertions(+), 25 deletions(-) diff --git a/plugins/commando.c b/plugins/commando.c index 9c6070b986d3..3103c4d86aeb 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -15,7 +15,9 @@ #define COMMANDO_ERROR_REMOTE_AUTH 0x4c51 enum commando_msgtype { - COMMANDO_MSG_CMD = 0x4c4f, + /* Requests are split across multiple CONTINUES, then TERM. */ + COMMANDO_MSG_CMD_CONTINUES = 0x4c4d, + COMMANDO_MSG_CMD_TERM = 0x4c4f, /* Replies are split across multiple CONTINUES, then TERM. */ COMMANDO_MSG_REPLY_CONTINUES = 0x594b, COMMANDO_MSG_REPLY_TERM = 0x594d, @@ -32,6 +34,7 @@ struct commando { static struct plugin *plugin; static struct commando **outgoing_commands; +static struct commando **incoming_commands; /* NULL peer: don't care about peer. NULL id: don't care about id */ static struct commando *find_commando(struct commando **arr, @@ -83,9 +86,10 @@ struct reply { size_t off, len; }; +/* Calls itself repeatedly: first time, result is NULL */ static struct command_result *send_response(struct command *command UNUSED, const char *buf UNUSED, - const jsmntok_t *result UNUSED, + const jsmntok_t *result, struct reply *reply) { size_t msglen = reply->len - reply->off; @@ -99,7 +103,7 @@ static struct command_result *send_response(struct command *command UNUSED, msgtype = COMMANDO_MSG_REPLY_CONTINUES; /* We need to make a copy first time before we call back, since * plugin will reuse it! */ - if (reply->off == 0) + if (!result) reply->buf = tal_dup_talarr(reply, char, reply->buf); } else { if (msglen == 0) { @@ -140,7 +144,7 @@ static struct command_result *cmd_done(struct command *command, reply->off = obj->start; reply->len = obj->end; - return send_response(command, buf, obj, reply); + return send_response(command, NULL, NULL, reply); } static void commando_error(struct commando *incoming, @@ -248,6 +252,43 @@ static void try_command(struct node_id *peer, send_outreq(plugin, req); } +static void handle_incmd(struct node_id *peer, + u64 idnum, + const u8 *msg, size_t msglen, + bool terminal) +{ + struct commando *incmd; + + incmd = find_commando(incoming_commands, peer, NULL); + /* Don't let them buffer multiple commands: discard old. */ + if (incmd && incmd->id != idnum) + incmd = tal_free(incmd); + + if (!incmd) { + incmd = tal(plugin, struct commando); + incmd->id = idnum; + incmd->cmd = NULL; + incmd->peer = *peer; + incmd->contents = tal_arr(incmd, u8, 0); + tal_arr_expand(&incoming_commands, incmd); + tal_add_destructor2(incmd, destroy_commando, &incoming_commands); + } + + /* 1MB should be enough for anybody! */ + append_contents(incmd, msg, msglen, 1024*1024); + + if (!terminal) + return; + + if (!incmd->contents) { + plugin_log(plugin, LOG_UNUSUAL, "%s: ignoring oversize request", + node_id_to_hexstr(tmpctx, peer)); + return; + } + + try_command(peer, idnum, incmd->contents, tal_bytelen(incmd->contents)); +} + static struct command_result *handle_reply(struct node_id *peer, u64 idnum, const u8 *msg, size_t msglen, @@ -283,7 +324,9 @@ static struct command_result *handle_reply(struct node_id *peer, replystr = (const char *)ocmd->contents; toks = json_parse_simple(ocmd, replystr, tal_bytelen(ocmd->contents)); if (!toks || toks[0].type != JSMN_OBJECT) - return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, "Reply was unparsable"); + return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, + "Reply was unparsable: '%.*s'", + (int)tal_bytelen(ocmd->contents), replystr); err = json_get_member(replystr, toks, "error"); if (err) { @@ -343,8 +386,10 @@ static struct command_result *handle_custommsg(struct command *cmd, if (msg) { switch (mtype) { - case COMMANDO_MSG_CMD: - try_command(&peer, idnum, msg, len); + case COMMANDO_MSG_CMD_CONTINUES: + case COMMANDO_MSG_CMD_TERM: + handle_incmd(&peer, idnum, msg, len, + mtype == COMMANDO_MSG_CMD_TERM); break; case COMMANDO_MSG_REPLY_CONTINUES: case COMMANDO_MSG_REPLY_TERM: @@ -364,14 +409,31 @@ static const struct plugin_hook hooks[] = { }, }; -static struct command_result *send_success(struct command *command, - const char *buf, - const jsmntok_t *result, - struct commando *incoming) +struct outgoing { + struct node_id peer; + size_t msg_off; + u8 **msgs; +}; + +static struct command_result *send_more_cmd(struct command *cmd, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct outgoing *outgoing) { - return command_still_pending(command); -} + struct out_req *req; + + if (outgoing->msg_off == tal_count(outgoing->msgs)) { + tal_free(outgoing); + return command_still_pending(cmd); + } + + req = jsonrpc_request_start(plugin, cmd, "sendcustommsg", + send_more_cmd, forward_error, outgoing); + json_add_node_id(req->js, "node_id", &outgoing->peer); + json_add_hex_talarr(req->js, "msg", outgoing->msgs[outgoing->msg_off++]); + return send_outreq(plugin, req); +} static struct command_result *json_commando(struct command *cmd, const char *buffer, @@ -381,9 +443,9 @@ static struct command_result *json_commando(struct command *cmd, const char *method, *cparams; const char *rune; struct commando *ocmd; - struct out_req *req; - u8 *cmd_msg; + struct outgoing *outgoing; char *json; + size_t jsonlen; if (!param(cmd, buffer, params, p_req("peer_id", param_node_id, &peer), @@ -410,28 +472,44 @@ static struct command_result *json_commando(struct command *cmd, tal_append_fmt(&json, ",\"rune\":\"%s\"", rune); tal_append_fmt(&json, "}"); - cmd_msg = tal_arr(NULL, u8, 0); - towire_u16(&cmd_msg, COMMANDO_MSG_CMD); - towire_u64(&cmd_msg, ocmd->id); - towire(&cmd_msg, json, strlen(json)); - req = jsonrpc_request_start(plugin, NULL, "sendcustommsg", - send_success, forward_error, ocmd); - json_add_node_id(req->js, "node_id", &ocmd->peer); - json_add_hex_talarr(req->js, "msg", cmd_msg); - tal_free(cmd_msg); + /* This is not a leak, but we don't keep a pointer. */ + outgoing = notleak(tal(cmd, struct outgoing)); + outgoing->peer = *peer; + outgoing->msg_off = 0; + /* 65000 per message gives sufficient headroom. */ + jsonlen = tal_bytelen(json)-1; + outgoing->msgs = notleak(tal_arr(cmd, u8 *, (jsonlen + 64999) / 65000)); + for (size_t i = 0; i < tal_count(outgoing->msgs); i++) { + u8 *cmd_msg = tal_arr(outgoing, u8, 0); + bool terminal = (i == tal_count(outgoing->msgs) - 1); + size_t off = i * 65000, len; + + if (terminal) + len = jsonlen - off; + else + len = 65000; + + towire_u16(&cmd_msg, + terminal ? COMMANDO_MSG_CMD_TERM + : COMMANDO_MSG_CMD_CONTINUES); + towire_u64(&cmd_msg, ocmd->id); + towire(&cmd_msg, json + off, len); + outgoing->msgs[i] = cmd_msg; + } /* Keep memleak code happy! */ tal_free(peer); tal_free(method); tal_free(cparams); - return send_outreq(plugin, req); + return send_more_cmd(cmd, NULL, NULL, outgoing); } #if DEVELOPER static void memleak_mark_globals(struct plugin *p, struct htable *memtable) { memleak_remove_region(memtable, outgoing_commands, tal_bytelen(outgoing_commands)); + memleak_remove_region(memtable, incoming_commands, tal_bytelen(incoming_commands)); } #endif @@ -439,6 +517,7 @@ static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { outgoing_commands = tal_arr(p, struct commando *, 0); + incoming_commands = tal_arr(p, struct commando *, 0); plugin = p; #if DEVELOPER plugin_set_memleak_handler(p, memleak_mark_globals); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 74490cf93078..77683b24e8d5 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2587,3 +2587,14 @@ def test_commando(node_factory): 'params': {'level': 'io'}}) assert len(json.dumps(ret)) > 65535 + + # Command will go over multiple messages. + ret = l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'invoice', + 'params': {'amount_msat': 'any', + 'label': 'label', + 'description': 'A' * 200000, + 'deschashonly': True}}) + + assert 'bolt11' in ret From b49703e279cd087bea508bbcc545f11e1ebc45c2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1002/1530] commando: correctly reflect error data field. Some JSON error include "data", and we should reflect that. Signed-off-by: Rusty Russell --- plugins/commando.c | 20 +++++++++++++++----- tests/test_plugin.py | 12 ++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/plugins/commando.c b/plugins/commando.c index 3103c4d86aeb..4eed51d56116 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -331,8 +332,10 @@ static struct command_result *handle_reply(struct node_id *peer, err = json_get_member(replystr, toks, "error"); if (err) { const jsmntok_t *code = json_get_member(replystr, err, "code"); - int ecode; const jsmntok_t *message = json_get_member(replystr, err, "message"); + const jsmntok_t *datatok = json_get_member(replystr, err, "data"); + struct json_out *data; + int ecode; if (!code || !json_to_int(replystr, code, &ecode)) { return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, "Error '%.*s' had no valid code", @@ -343,10 +346,17 @@ static struct command_result *handle_reply(struct node_id *peer, return command_fail(ocmd->cmd, COMMANDO_ERROR_LOCAL, "Error had no message"); } - /* FIXME: data! */ - return command_fail(ocmd->cmd, ecode, "%.*s", - message->end - message->start, - replystr + message->start); + if (datatok) { + data = json_out_new(ocmd->cmd); + memcpy(json_out_direct(data, json_tok_full_len(datatok)), + json_tok_full(replystr, datatok), + json_tok_full_len(datatok)); + } else + data = NULL; + + return command_done_err(ocmd->cmd, ecode, + json_strdup(tmpctx, replystr, message), + data); } result = json_get_member(replystr, toks, "result"); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 77683b24e8d5..f0e656e04301 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2598,3 +2598,15 @@ def test_commando(node_factory): 'deschashonly': True}}) assert 'bolt11' in ret + + # This will fail, will include data. + with pytest.raises(RpcError, match='No connection to first peer found') as exc_info: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'sendpay', + 'params': {'route': [{'amount_msat': 1000, + 'id': l1.info['id'], + 'delay': 12, + 'channel': '1x2x3'}], + 'payment_hash': '00' * 32}}) + assert exc_info.value.error['data']['erring_index'] == 0 From 0d94530f13564febce5db42f167894bb2a1fad9e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1003/1530] commando: runes infrastructure. We support the old commando.py plugin, which stores a random secret, as well as a more modern approach which uses makesecret. Signed-off-by: Rusty Russell --- plugins/commando.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/plugins/commando.c b/plugins/commando.c index 4eed51d56116..1521b3baf459 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -36,6 +37,8 @@ struct commando { static struct plugin *plugin; static struct commando **outgoing_commands; static struct commando **incoming_commands; +static u64 *rune_counter; +static struct rune *master_rune; /* NULL peer: don't care about peer. NULL id: don't care about id */ static struct commando *find_commando(struct commando **arr, @@ -260,6 +263,8 @@ static void handle_incmd(struct node_id *peer, { struct commando *incmd; + /* FIXME: don't do *anything* unless they've set up a rune. */ + incmd = find_commando(incoming_commands, peer, NULL); /* Don't let them buffer multiple commands: discard old. */ if (incmd && incmd->id != idnum) @@ -520,18 +525,50 @@ static void memleak_mark_globals(struct plugin *p, struct htable *memtable) { memleak_remove_region(memtable, outgoing_commands, tal_bytelen(outgoing_commands)); memleak_remove_region(memtable, incoming_commands, tal_bytelen(incoming_commands)); + if (rune_counter) + memleak_remove_region(memtable, rune_counter, sizeof(*rune_counter)); } #endif static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { + struct secret rune_secret; + outgoing_commands = tal_arr(p, struct commando *, 0); incoming_commands = tal_arr(p, struct commando *, 0); plugin = p; #if DEVELOPER plugin_set_memleak_handler(p, memleak_mark_globals); #endif + + rune_counter = tal(p, u64); + if (!rpc_scan_datastore_str(plugin, "commando/rune_counter", + JSON_SCAN(json_to_u64, rune_counter))) + rune_counter = tal_free(rune_counter); + + /* Old python commando used to store secret */ + if (!rpc_scan_datastore_hex(plugin, "commando/secret", + JSON_SCAN(json_to_secret, &rune_secret))) { + rpc_scan(plugin, "makesecret", + /* $ i commando + * 99 0x63 0143 0b1100011 'c' + * 111 0x6F 0157 0b1101111 'o' + * 109 0x6D 0155 0b1101101 'm' + * 109 0x6D 0155 0b1101101 'm' + * 97 0x61 0141 0b1100001 'a' + * 110 0x6E 0156 0b1101110 'n' + * 100 0x64 0144 0b1100100 'd' + * 111 0x6F 0157 0b1101111 'o' + */ + take(json_out_obj(NULL, "hex", "636F6D6D616E646F")), + "{secret:%}", + JSON_SCAN(json_to_secret, &rune_secret)); + } + + master_rune = rune_new(plugin, rune_secret.data, ARRAY_SIZE(rune_secret.data), + NULL); + return NULL; } From cf4374c4ed58d4b4ace64d95bd24cfb11601b68a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1004/1530] devtools/rune: simple decode tool. Signed-off-by: Rusty Russell --- devtools/Makefile | 4 ++- devtools/rune.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 devtools/rune.c diff --git a/devtools/Makefile b/devtools/Makefile index 2c32b024bb27..a18548c666fc 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -1,4 +1,4 @@ -DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/bolt12-cli devtools/encodeaddr devtools/features devtools/fp16 +DEVTOOLS := devtools/bolt11-cli devtools/decodemsg devtools/onion devtools/dump-gossipstore devtools/gossipwith devtools/create-gossipstore devtools/mkcommit devtools/mkfunding devtools/mkclose devtools/mkgossip devtools/mkencoded devtools/mkquery devtools/lightning-checkmessage devtools/topology devtools/route devtools/bolt12-cli devtools/encodeaddr devtools/features devtools/fp16 devtools/rune ifeq ($(HAVE_SQLITE3),1) DEVTOOLS += devtools/checkchannels endif @@ -48,6 +48,8 @@ DEVTOOLS_COMMON_OBJS := \ devtools/features: common/features.o common/utils.o wire/fromwire.o wire/towire.o devtools/features.o +devtools/rune: common/utils.o common/autodata.o common/setup.o common/version.o wire/fromwire.o wire/towire.o devtools/rune.o + devtools/fp16: common/fp16.o common/utils.o common/setup.o common/autodata.o devtools/fp16.o devtools/bolt11-cli: $(DEVTOOLS_COMMON_OBJS) $(JSMN_OBJS) $(BITCOIN_OBJS) wire/fromwire.o wire/towire.o devtools/bolt11-cli.o diff --git a/devtools/rune.c b/devtools/rune.c new file mode 100644 index 000000000000..37322e06c475 --- /dev/null +++ b/devtools/rune.c @@ -0,0 +1,81 @@ +/* Decodes a rune. */ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + struct rune *rune; + common_setup(argv[0]); + + opt_register_noarg("--help|-h", opt_usage_and_exit, + "", "Show this message"); + opt_register_version(); + + opt_early_parse(argc, argv, opt_log_stderr_exit); + opt_parse(&argc, argv, opt_log_stderr_exit); + if (argc != 2) + opt_usage_exit_fail("needs rune"); + + rune = rune_from_base64(NULL, argv[1]); + if (!rune) + opt_usage_exit_fail("invalid rune"); + + printf("string encoding: %s\n", rune_to_string(rune, rune)); + for (size_t i = 0; i < tal_count(rune->restrs); i++) { + const struct rune_restr *restr = rune->restrs[i]; + const char *sep = "- "; + for (size_t j = 0; j < tal_count(restr->alterns); j++) { + const struct rune_altern *alt = restr->alterns[j]; + if (streq(alt->fieldname, "")) { + printf("Unique id is %s", alt->value); + } else { + printf("%s", sep); + switch (alt->condition) { + case RUNE_COND_IF_MISSING: + printf("%s is missing", alt->fieldname); + break; + case RUNE_COND_EQUAL: + printf("%s equal to %s", alt->fieldname, alt->value); + break; + case RUNE_COND_NOT_EQUAL: + printf("%s unequal to %s", alt->fieldname, alt->value); + break; + case RUNE_COND_BEGINS: + printf("%s starts with %s", alt->fieldname, alt->value); + break; + case RUNE_COND_ENDS: + printf("%s ends with %s", alt->fieldname, alt->value); + break; + case RUNE_COND_CONTAINS: + printf("%s contains %s", alt->fieldname, alt->value); + break; + case RUNE_COND_INT_LESS: + printf("%s < %s", alt->fieldname, alt->value); + break; + case RUNE_COND_INT_GREATER: + printf("%s > %s", alt->fieldname, alt->value); + break; + case RUNE_COND_LEXO_BEFORE: + printf("%s sorts before %s", alt->fieldname, alt->value); + break; + case RUNE_COND_LEXO_AFTER: + printf("%s sorts after %s", alt->fieldname, alt->value); + break; + case RUNE_COND_COMMENT: + printf("comment: %s%s", alt->fieldname, alt->value); + break; + } + sep = " OR "; + } + } + printf("\n"); + } + common_shutdown(); +} From 419cb60b1be16c87906fd8ea1235851f9aaf7027 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1005/1530] commando: add commando-rune command. Can both mint new runes, and add one or more restrictions to existing ones. Signed-off-by: Rusty Russell --- plugins/commando.c | 149 ++++++++++++++++++++++++++++++++++++++++++- tests/test_plugin.py | 35 +++++++++- 2 files changed, 182 insertions(+), 2 deletions(-) diff --git a/plugins/commando.c b/plugins/commando.c index 1521b3baf459..c3e3aca2bf2a 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -520,11 +520,152 @@ static struct command_result *json_commando(struct command *cmd, return send_more_cmd(cmd, NULL, NULL, outgoing); } +static struct command_result *param_rune(struct command *cmd, const char *name, + const char * buffer, const jsmntok_t *tok, + struct rune **rune) +{ + *rune = rune_from_base64n(cmd, buffer + tok->start, tok->end - tok->start); + if (!*rune) + return command_fail_badparam(cmd, name, buffer, tok, + "should be base64 string"); + + return NULL; +} + +static struct rune_restr **readonly_restrictions(const tal_t *ctx) +{ + struct rune_restr **restrs = tal_arr(ctx, struct rune_restr *, 2); + + /* Any list*, get*, or summary: + * method^list|method^get|method=summary + */ + restrs[0] = rune_restr_new(restrs); + rune_restr_add_altern(restrs[0], + take(rune_altern_new(NULL, + "method", + RUNE_COND_BEGINS, + "list"))); + rune_restr_add_altern(restrs[0], + take(rune_altern_new(NULL, + "method", + RUNE_COND_BEGINS, + "get"))); + rune_restr_add_altern(restrs[0], + take(rune_altern_new(NULL, + "method", + RUNE_COND_EQUAL, + "summary"))); + /* But not listdatastore! + * method/listdatastore + */ + restrs[1] = rune_restr_new(restrs); + rune_restr_add_altern(restrs[1], + take(rune_altern_new(NULL, + "method", + RUNE_COND_NOT_EQUAL, + "listdatastore"))); + + return restrs; +} + +static struct command_result *param_restrictions(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct rune_restr ***restrs) +{ + if (json_tok_streq(buffer, tok, "readonly")) + *restrs = readonly_restrictions(cmd); + else if (tok->type == JSMN_ARRAY) { + size_t i; + const jsmntok_t *t; + + *restrs = tal_arr(cmd, struct rune_restr *, tok->size); + json_for_each_arr(i, t, tok) { + (*restrs)[i] = rune_restr_from_string(*restrs, + buffer + t->start, + t->end - t->start); + if (!(*restrs)[i]) + return command_fail_badparam(cmd, name, buffer, t, + "not a valid restriction"); + } + } else { + *restrs = tal_arr(cmd, struct rune_restr *, 1); + (*restrs)[0] = rune_restr_from_string(*restrs, + buffer + tok->start, + tok->end - tok->start); + if (!(*restrs)[0]) + return command_fail_badparam(cmd, name, buffer, tok, + "not a valid restriction"); + } + return NULL; +} + +static struct command_result *reply_with_rune(struct command *cmd, + const char *buf UNUSED, + const jsmntok_t *result UNUSED, + struct rune *rune) +{ + struct json_stream *js = jsonrpc_stream_success(cmd); + + json_add_string(js, "rune", rune_to_base64(tmpctx, rune)); + json_add_string(js, "unique_id", rune->unique_id); + return command_finished(cmd, js); +} + +static struct command_result *json_commando_rune(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + struct rune *rune; + struct rune_restr **restrs; + struct out_req *req; + + if (!param(cmd, buffer, params, + p_opt("rune", param_rune, &rune), + p_opt("restrictions", param_restrictions, &restrs), + NULL)) + return command_param_failed(); + + if (rune) { + for (size_t i = 0; i < tal_count(restrs); i++) + rune_add_restr(rune, restrs[i]); + return reply_with_rune(cmd, NULL, NULL, rune); + } + + rune = rune_derive_start(cmd, master_rune, + tal_fmt(tmpctx, "%"PRIu64, + rune_counter ? *rune_counter : 0)); + for (size_t i = 0; i < tal_count(restrs); i++) + rune_add_restr(rune, restrs[i]); + + /* Now update datastore, before returning rune */ + req = jsonrpc_request_start(plugin, cmd, "datastore", + reply_with_rune, forward_error, rune); + json_array_start(req->js, "key"); + json_add_string(req->js, NULL, "commando"); + json_add_string(req->js, NULL, "rune_counter"); + json_array_end(req->js); + if (rune_counter) { + (*rune_counter)++; + json_add_string(req->js, "mode", "must-replace"); + } else { + /* This used to say "🌩🤯🧨🔫!" but our log filters are too strict :( */ + plugin_log(plugin, LOG_INFORM, "Commando powers enabled: BOOM!"); + rune_counter = tal(plugin, u64); + *rune_counter = 1; + json_add_string(req->js, "mode", "must-create"); + } + json_add_u64(req->js, "string", *rune_counter); + return send_outreq(plugin, req); +} + #if DEVELOPER static void memleak_mark_globals(struct plugin *p, struct htable *memtable) { memleak_remove_region(memtable, outgoing_commands, tal_bytelen(outgoing_commands)); memleak_remove_region(memtable, incoming_commands, tal_bytelen(incoming_commands)); + memleak_remove_region(memtable, master_rune, sizeof(*master_rune)); if (rune_counter) memleak_remove_region(memtable, rune_counter, sizeof(*rune_counter)); } @@ -578,7 +719,13 @@ static const struct plugin_command commands[] = { { "Send a commando message to a direct peer, wait for response", "Sends {peer_id} {method} with optional {params} and {rune}", json_commando, - } + }, { + "commando-rune", + "utility", + "Create or restrict a rune", + "Takes an optional {rune} with optional {restrictions} and returns {rune}", + json_commando_rune, + }, }; int main(int argc, char *argv[]) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index f0e656e04301..b706baaaffa1 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2546,7 +2546,7 @@ def test_plugin_shutdown(node_factory): 'test_libplugin: failed to self-terminate in time, killing.']) -def test_commando(node_factory): +def test_commando(node_factory, executor): l1, l2 = node_factory.line_graph(2, fundchannel=False) # This works @@ -2610,3 +2610,36 @@ def test_commando(node_factory): 'channel': '1x2x3'}], 'payment_hash': '00' * 32}}) assert exc_info.value.error['data']['erring_index'] == 0 + + +def test_commando_rune(node_factory): + l1, l2 = node_factory.line_graph(2, fundchannel=False) + + # l1's commando secret is 1241faef85297127c2ac9bde95421b2c51e5218498ae4901dc670c974af4284b. + # I put that into a test node's commando.py to generate these runes (modified readonly to match ours): + # $ l1-cli commando-rune + # "rune": "zKc2W88jopslgUBl0UE77aEe5PNCLn5WwqSusU_Ov3A9MA==" + # $ l1-cli commando-rune restrictions=readonly + # "rune": "1PJnoR9a7u4Bhglj2s7rVOWqRQnswIwUoZrDVMKcLTY9MSZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl" + # $ l1-cli commando-rune restrictions='time>1656675211' + # "rune": "RnlWC4lwBULFaObo6ZP8jfqYRyTbfWPqcMT3qW-Wmso9MiZ0aW1lPjE2NTY2NzUyMTE=" + # $ l1-cli commando-rune restrictions='["id^022d223620a359a47ff7","method=listpeers"]' + # "rune": "lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz" + # $ l1-cli commando-rune lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz 'pnamelevel!|pnamelevel/io' + # "rune": "Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8=" + + rune1 = l1.rpc.commando_rune() + assert rune1['rune'] == 'zKc2W88jopslgUBl0UE77aEe5PNCLn5WwqSusU_Ov3A9MA==' + assert rune1['unique_id'] == '0' + rune2 = l1.rpc.commando_rune(restrictions="readonly") + assert rune2['rune'] == '1PJnoR9a7u4Bhglj2s7rVOWqRQnswIwUoZrDVMKcLTY9MSZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl' + assert rune2['unique_id'] == '1' + rune3 = l1.rpc.commando_rune(restrictions="time>1656675211") + assert rune3['rune'] == 'RnlWC4lwBULFaObo6ZP8jfqYRyTbfWPqcMT3qW-Wmso9MiZ0aW1lPjE2NTY2NzUyMTE=' + assert rune3['unique_id'] == '2' + rune4 = l1.rpc.commando_rune(restrictions=["id^022d223620a359a47ff7", "method=listpeers"]) + assert rune4['rune'] == 'lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz' + assert rune4['unique_id'] == '3' + rune5 = l1.rpc.commando_rune(rune4['rune'], "pnamelevel!|pnamelevel/io") + assert rune5['rune'] == 'Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8=' + assert rune5['unique_id'] == '3' From ae4856df70b6ecc4c4696c0d16b18cf51604527d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1006/1530] commando: don't look at messages *at all* unless they've created a rune. This means we can leave commando on by default, without an explicit config flag. Signed-off-by: Rusty Russell --- plugins/commando.c | 3 ++- tests/test_plugin.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/commando.c b/plugins/commando.c index c3e3aca2bf2a..fd9c86a0d089 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -263,7 +263,8 @@ static void handle_incmd(struct node_id *peer, { struct commando *incmd; - /* FIXME: don't do *anything* unless they've set up a rune. */ + if (!rune_counter) + return; incmd = find_commando(incoming_commands, peer, NULL); /* Don't let them buffer multiple commands: discard old. */ diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b706baaaffa1..471d3bc5ae4e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -13,6 +13,7 @@ ) import ast +import concurrent.futures import json import os import pytest @@ -2549,6 +2550,15 @@ def test_plugin_shutdown(node_factory): def test_commando(node_factory, executor): l1, l2 = node_factory.line_graph(2, fundchannel=False) + # Nothing works until we've issued a rune. + fut = executor.submit(l2.rpc.call, method='commando', + payload={'peer_id': l1.info['id'], + 'method': 'listpeers'}) + with pytest.raises(concurrent.futures.TimeoutError): + fut.result(10) + + l1.rpc.commando_rune() + # This works res = l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], From 8688daf937675a9aeaca48d2cda14c542f45f4a1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1007/1530] commando: require runes for operation. Signed-off-by: Rusty Russell --- plugins/commando.c | 94 +++++++++++++++++++++++++++++++++++++++++--- tests/test_plugin.py | 70 ++++++++++++++++++++++++++++++++- 2 files changed, 157 insertions(+), 7 deletions(-) diff --git a/plugins/commando.c b/plugins/commando.c index fd9c86a0d089..1fcff8d8e88c 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -1,8 +1,10 @@ #include "config.h" #include +#include #include #include #include +#include #include #include #include @@ -175,14 +177,96 @@ static void commando_error(struct commando *incoming, send_response(NULL, NULL, NULL, reply); } -static const char *check_rune(struct commando *incoming, +struct cond_info { + const struct node_id *peer; + const char *buf; + const jsmntok_t *method; + const jsmntok_t *params; + STRMAP(const jsmntok_t *) cached_params; +}; + +static const char *check_condition(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + struct cond_info *cinfo) +{ + const jsmntok_t *ptok; + + if (streq(alt->fieldname, "time")) { + return rune_alt_single_int(ctx, alt, time_now().ts.tv_sec); + } else if (streq(alt->fieldname, "id")) { + const char *id = node_id_to_hexstr(tmpctx, cinfo->peer); + return rune_alt_single_str(ctx, alt, id, strlen(id)); + } else if (streq(alt->fieldname, "method")) { + return rune_alt_single_str(ctx, alt, + cinfo->buf + cinfo->method->start, + cinfo->method->end - cinfo->method->start); + } + + /* Rest are params looksup: generate this once! */ + if (cinfo->params) { + /* Note: we require that params be an obj! */ + const jsmntok_t *t; + size_t i; + + json_for_each_obj(i, t, cinfo->params) { + char *pmemname = tal_fmt(tmpctx, + "pname%.*s", + t->end - t->start, + cinfo->buf + t->start); + size_t off = strlen("pname"); + /* Remove punctuation! */ + for (size_t n = off; pmemname[n]; n++) { + if (cispunct(pmemname[n])) + continue; + pmemname[off++] = pmemname[n]; + } + pmemname[off++] = '\0'; + strmap_add(&cinfo->cached_params, pmemname, t+1); + } + cinfo->params = NULL; + } + + ptok = strmap_get(&cinfo->cached_params, alt->fieldname); + if (!ptok) + return rune_alt_single_missing(ctx, alt); + + return rune_alt_single_str(ctx, alt, + cinfo->buf + ptok->start, + ptok->end - ptok->start); +} + +static const char *check_rune(const tal_t *ctx, + struct commando *incoming, + const struct node_id *peer, const char *buf, const jsmntok_t *method, const jsmntok_t *params, - const jsmntok_t *rune) + const jsmntok_t *runetok) { - /* FIXME! */ - return NULL; + struct rune *rune; + struct cond_info cinfo; + const char *err; + + if (!runetok) + return "Missing rune"; + + rune = rune_from_base64n(tmpctx, buf + runetok->start, + runetok->end - runetok->start); + if (!rune) + return "Invalid rune"; + + cinfo.peer = peer; + cinfo.buf = buf; + cinfo.method = method; + cinfo.params = params; + strmap_init(&cinfo.cached_params); + err = rune_test(ctx, master_rune, rune, check_condition, &cinfo); + /* Just in case they manage to make us speak non-JSON, escape! */ + if (err) + err = json_escape(ctx, take(err))->s; + strmap_clear(&cinfo.cached_params); + return err; } static void try_command(struct node_id *peer, @@ -223,7 +307,7 @@ static void try_command(struct node_id *peer, } rune = json_get_member(buf, toks, "rune"); - failmsg = check_rune(incoming, buf, method, params, rune); + failmsg = check_rune(tmpctx, incoming, peer, buf, method, params, rune); if (failmsg) { commando_error(incoming, COMMANDO_ERROR_REMOTE_AUTH, "Not authorized: %s", failmsg); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 471d3bc5ae4e..1d39f13de4dc 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2557,17 +2557,18 @@ def test_commando(node_factory, executor): with pytest.raises(concurrent.futures.TimeoutError): fut.result(10) - l1.rpc.commando_rune() - + rune = l1.rpc.commando_rune()['rune'] # This works res = l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], + 'rune': rune, 'method': 'listpeers'}) assert len(res['peers']) == 1 assert res['peers'][0]['id'] == l2.info['id'] res = l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], + 'rune': rune, 'method': 'listpeers', 'params': {'id': l2.info['id']}}) assert len(res['peers']) == 1 @@ -2576,16 +2577,19 @@ def test_commando(node_factory, executor): with pytest.raises(RpcError, match='missing required parameter'): l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], + 'rune': rune, 'method': 'withdraw'}) with pytest.raises(RpcError, match='unknown parameter: foobar'): l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], 'method': 'invoice', + 'rune': rune, 'params': {'foobar': 1}}) ret = l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], + 'rune': rune, 'method': 'ping', 'params': {'id': l2.info['id']}}) assert 'totlen' in ret @@ -2593,6 +2597,7 @@ def test_commando(node_factory, executor): # Now, reply will go over a multiple messages! ret = l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], + 'rune': rune, 'method': 'getlog', 'params': {'level': 'io'}}) @@ -2601,6 +2606,7 @@ def test_commando(node_factory, executor): # Command will go over multiple messages. ret = l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], + 'rune': rune, 'method': 'invoice', 'params': {'amount_msat': 'any', 'label': 'label', @@ -2613,6 +2619,7 @@ def test_commando(node_factory, executor): with pytest.raises(RpcError, match='No connection to first peer found') as exc_info: l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], + 'rune': rune, 'method': 'sendpay', 'params': {'route': [{'amount_msat': 1000, 'id': l1.info['id'], @@ -2653,3 +2660,62 @@ def test_commando_rune(node_factory): rune5 = l1.rpc.commando_rune(rune4['rune'], "pnamelevel!|pnamelevel/io") assert rune5['rune'] == 'Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8=' assert rune5['unique_id'] == '3' + + # Replace rune3 with a more useful timestamp! + expiry = int(time.time()) + 15 + rune3 = l1.rpc.commando_rune(restrictions="time<{}".format(expiry)) + successes = ((rune1, "listpeers", {}), + (rune2, "listpeers", {}), + (rune2, "getinfo", {}), + (rune2, "getinfo", {}), + (rune3, "getinfo", {}), + (rune4, "listpeers", {}), + (rune5, "listpeers", {'id': l2.info['id']}), + (rune5, "listpeers", {'id': l2.info['id'], 'level': 'broken'})) + failures = ((rune2, "withdraw", {}), + (rune2, "plugin", {'subcommand': 'list'}), + (rune3, "getinfo", {}), + (rune4, "listnodes", {}), + (rune5, "listpeers", {'id': l2.info['id'], 'level': 'io'})) + + for rune, cmd, params in successes: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune['rune'], + 'method': cmd, + 'params': params}) + + while time.time() < expiry: + time.sleep(1) + + for rune, cmd, params in failures: + print("{} {}".format(cmd, params)) + with pytest.raises(RpcError, match='Not authorized:') as exc_info: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune['rune'], + 'method': cmd, + 'params': params}) + assert exc_info.value.error['code'] == 0x4c51 + + # rune5 can only be used by l2: + l3 = node_factory.get_node() + l3.connect(l1) + with pytest.raises(RpcError, match='Not authorized:') as exc_info: + l3.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune5['rune'], + 'method': "listpeers", + 'params': {}}) + assert exc_info.value.error['code'] == 0x4c51 + + # Remote doesn't allow array parameters. + l2.rpc.check_request_schemas = False + with pytest.raises(RpcError, match='Params must be object') as exc_info: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune5['rune'], + 'method': "listpeers", + 'params': [l2.info['id'], 'io']}) + assert exc_info.value.error['code'] == 0x4c50 + l2.rpc.check_request_schemas = True From cf28cff3987bc2519de255f2611afc1ef6952ff7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1008/1530] doc: document commando and commando-rune. Signed-off-by: Rusty Russell --- doc/Makefile | 2 + doc/index.rst | 2 + doc/lightning-commando-rune.7.md | 101 +++++++++++++++++++++++++ doc/lightning-commando.7.md | 52 +++++++++++++ doc/schemas/commando-rune.request.json | 27 +++++++ doc/schemas/commando-rune.schema.json | 19 +++++ doc/schemas/commando.request.json | 27 +++++++ 7 files changed, 230 insertions(+) create mode 100644 doc/lightning-commando-rune.7.md create mode 100644 doc/lightning-commando.7.md create mode 100644 doc/schemas/commando-rune.request.json create mode 100644 doc/schemas/commando-rune.schema.json create mode 100644 doc/schemas/commando.request.json diff --git a/doc/Makefile b/doc/Makefile index 3f5a39453a58..04be559e9cd8 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -13,6 +13,8 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-checkmessage.7 \ doc/lightning-close.7 \ doc/lightning-connect.7 \ + doc/lightning-commando.7 \ + doc/lightning-commando-rune.7 \ doc/lightning-createonion.7 \ doc/lightning-createinvoice.7 \ doc/lightning-datastore.7 \ diff --git a/doc/index.rst b/doc/index.rst index 87e6601af46e..b60ced919aca 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -35,6 +35,8 @@ Core Lightning Documentation lightning-checkmessage lightning-cli lightning-close + lightning-commando + lightning-commando-rune lightning-connect lightning-createinvoice lightning-createonion diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md new file mode 100644 index 000000000000..23a8e9c6e684 --- /dev/null +++ b/doc/lightning-commando-rune.7.md @@ -0,0 +1,101 @@ +lightning-commando-rune -- Command to Authorize Remote Peer Access +=================================================================== + +SYNOPSIS +-------- + +**commando-rune** [*rune*] [*restrictions*] + +DESCRIPTION +----------- + +The **commando-rune** RPC command creates a base64 string called a +*rune* which can be used to access commands on this node. Each *rune* +contains a unique id (a number starting at 0), and can have +restrictions inside it. Nobody can remove restrictions from a rune: if +you try, the rune will be rejected. There is no limit on how many +runes you can issue: the node doesn't store them, but simply decodes +and checks them as they are received. + +If *rune* is supplied, the restrictions are simple appended to that +*rune* (it doesn't need to be a rune belonging to this node). If no +*rune* is supplied, a new one is constructed, with a new unique id. + +*restrictions* can be the string "readonly" (creates a rune which +allows most *get* and *list* commands, and the *summary* command), or +an array of restrictions, or a single resriction. + +Each restriction is a set of one or more alternatives, such as "method +is listpeers", or "method is listpeers OR time is before 2023". +Alternatives use a simple language to examine the command which is +being run: + +* time: the current UNIX time, e.g. "time<1656759180". +* id: the node_id of the peer, e.g. "id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605". +* method: the command being run, e.g. "method=withdraw". +* pnameX: the parameter named X. e.g. "pnamedestination=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T". + +RESTRICTION FORMAT +------------------ + +Restrictions are one or more altneratives, separated by `|`. Each +alternative is *name* *operator* *value*. The valid names are shown +above. If a value contains `|`, `&` or `\\`, it must be preceeded by +a `\\`. + +* `=`: passes if equal ie. identical. e.g. `method=withdraw` +* `/`: not equals, e.g. `method/withdraw` +* `^`: starts with, e.g. `id^024b9a1fa8e006f1e3937f` +* `$`: ends with, e.g. `id$381df1cc449605`. +* `~`: contains, e.g. `id~006f1e3937f65f66c40`. +* `<`: is a decimal integer, and is less than. e.g. `time<1656759180` +* `>`: is a decimal integer, and is greater than. e.g. `time>1656759180` +* `{`: preceeds in alphabetical order (or matches but is shorter), e.g. `id{02ff`. +* `}`: follows in alphabetical order (or matches but is longer), e.g. `id}02ff`. +* `#`: a comment, ignored, e.g. `dumb example#`. +* `!`: only passes if the *name* does *not* exist. e.g. `pnamedestination!`. + Every other operator except `#` fails if *name* does not exist! + +For example, the "readonly" restriction is actually two restrictions: + +1. `method^list|method^get|method=summary`: You may call list, get or summary. +2. `method/listdatastore`: But not listdatastore: that contains sensitive stuff! + +SHARING RUNES +------------- + +Because anyone can add a restriction to a rune, you can always turn a +normal rune into a read-only rune, or restrict access for 30 minutes +from the time you give it to someone. Adding restrictions before +sharing runes is best practice. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **rune** (string): the resulting rune +- **unique_id** (string): the id of this rune: this is set at creation and cannot be changed (even as restrictions are added) + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +Rusty Russell <> wrote the original Python +commando.py plugin, the in-tree commando plugin, and this manual page. + +Christian Decker came up with the name "commando", which almost +excuses his previous adoption of the name "Eltoo". + +SEE ALSO +-------- + +lightning-commando(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:598337212d2e8a6833698e931f838d8cb424c353af4d7adf6891803ff0ee604b) diff --git a/doc/lightning-commando.7.md b/doc/lightning-commando.7.md new file mode 100644 index 000000000000..871083f12083 --- /dev/null +++ b/doc/lightning-commando.7.md @@ -0,0 +1,52 @@ +lightning-commando -- Command to Send a Command to a Remote Peer +================================================================ + +SYNOPSIS +-------- + +**commando** *peer_id* *method* [*params*] [*rune*] + +DESCRIPTION +----------- + +The **commando** RPC command is a homage to bad 80s movies. It also +sends a directly-connected *peer_id* a custom message, containing a +request to run *method* (with an optional dictionary of *params*); +generally the peer will only allow you to run a command if it has +provided you with a *rune* which allows it. + +RETURN VALUE +------------ + +On success, the return depends on the *method* invoked. + +On failure, one of the following error codes may be returned: + +- -32600: Usually means peer is not connected +- 19535: the local commando plugin discovered an error. +- 19536: the remote commando plugin discovered an error. +- 19537: the remote commando plugin said we weren't authorized. + +It can also fail if the peer does not respond, in which case it will simply +hang awaiting a response. + +AUTHOR +------ + +Rusty Russell <> wrote the original Python +commando.py plugin, the in-tree commando plugin, and this manual page. + +Christian Decker came up with the name "commando", which almost +excuses his previous adoption of the name "Eltoo". + +SEE ALSO +-------- + +lightning-commando-rune(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:6f4406cae30cab813b3bf4e1242af914276716a057e558474e29340665ee8c2f) diff --git a/doc/schemas/commando-rune.request.json b/doc/schemas/commando-rune.request.json new file mode 100644 index 000000000000..a93ae80adc79 --- /dev/null +++ b/doc/schemas/commando-rune.request.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": { + "rune": { + "type": "string", + "description": "optional rune to add to" + }, + "restrictions": { + "oneOf": [ + { + "type": "array", + "description": "array of restrictions to add to rune", + "items": { + "type": "string" + } + }, + { + "type": "string", + "description": "single restrictions to add to rune, or readonly." + } + ] + } + } +} diff --git a/doc/schemas/commando-rune.schema.json b/doc/schemas/commando-rune.schema.json new file mode 100644 index 000000000000..c0519e51cb40 --- /dev/null +++ b/doc/schemas/commando-rune.schema.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "rune", + "unique_id" + ], + "properties": { + "rune": { + "type": "string", + "description": "the resulting rune" + }, + "unique_id": { + "type": "string", + "description": "the id of this rune: this is set at creation and cannot be changed (even as restrictions are added)" + } + } +} diff --git a/doc/schemas/commando.request.json b/doc/schemas/commando.request.json new file mode 100644 index 000000000000..354f83e86683 --- /dev/null +++ b/doc/schemas/commando.request.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "peer_id", + "method" + ], + "properties": { + "peer_id": { + "type": "pubkey", + "description": "peer to command" + }, + "method": { + "type": "string", + "description": "method to invoke on peer" + }, + "params": { + "type": "object", + "description": "parameters for method" + }, + "rune": { + "type": "string", + "description": "rune to authorize the command" + } + } +} From 4ab09f7cfb1556ce0a59980702f0761b9d4ac7ec Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1009/1530] commando: add support for parameters by array, parameter count. Awkward to filter, but they're really practical for many commands. Signed-off-by: Rusty Russell --- doc/lightning-commando-rune.7.md | 61 ++++++++++++++++++++++++++- doc/schemas/commando.request.json | 12 +++++- plugins/commando.c | 69 ++++++++++++++++++++----------- tests/test_plugin.py | 28 +++++++------ 4 files changed, 128 insertions(+), 42 deletions(-) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index 23a8e9c6e684..ca31e2e96171 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -33,7 +33,9 @@ being run: * time: the current UNIX time, e.g. "time<1656759180". * id: the node_id of the peer, e.g. "id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605". * method: the command being run, e.g. "method=withdraw". +* pnum: the number of parameters. e.g. "pnum<2". * pnameX: the parameter named X. e.g. "pnamedestination=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T". +* parrN: the N'th parameter. e.g. "parr0=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T". RESTRICTION FORMAT ------------------ @@ -56,10 +58,65 @@ a `\\`. * `!`: only passes if the *name* does *not* exist. e.g. `pnamedestination!`. Every other operator except `#` fails if *name* does not exist! -For example, the "readonly" restriction is actually two restrictions: +EXAMPLES +-------- + +This creates a fresh rune which can do anything: + + $ lightning-cli commando-rune + { + "rune": "KUhZzNlECC7pYsz3QVbF1TqjIUYi3oyESTI7n60hLMs9MA==", + "unique_id": "0" + } + +We can add restrictions to that rune, like so: + + $ lightning-cli commando-rune rune=KUhZzNlECC7pYsz3QVbF1TqjIUYi3oyESTI7n60hLMs9MA== restrictions=readonly + { + "rune": "NbL7KkXcPQsVseJ9TdJNjJK2KsPjnt_q4cE_wvc873I9MCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl", + "unique_id": "0" + } + +The "readonly" restriction is a short-cut for two restrictions: 1. `method^list|method^get|method=summary`: You may call list, get or summary. -2. `method/listdatastore`: But not listdatastore: that contains sensitive stuff! +2. `method/listdatastore`: But not listdatastore: that contains sensitive stuff! + +We can do the same manually, like so: + + $ lightning-cli commando-rune rune=KUhZzNlECC7pYsz3QVbF1TqjIUYi3oyESTI7n60hLMs9MA== restrictions='["method^list|method^get|method=summary","method/listdatastore"]' + { + "rune": "NbL7KkXcPQsVseJ9TdJNjJK2KsPjnt_q4cE_wvc873I9MCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl", + "unique_id": "0" + } + +Let's create a rune which lets a specific peer +(024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605) +run "listpeers" on themselves: + + $ lightning-cli commando-rune restrictions='["id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605","method=listpeers","pnum=1","pnameid=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605|parr0=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605"]' + { + "rune": "FE8GHiGVvxcFqCQcClVRRiNE_XEeLYQzyG2jmqto4jM9MiZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDV8cGFycjA9MDI0YjlhMWZhOGUwMDZmMWUzOTM3ZjY1ZjY2YzQwOGU2ZGE4ZTFjYTcyOGVhNDMyMjJhNzM4MWRmMWNjNDQ5NjA1", + "unique_id": "2" + } + +This allows `listpeers` with 1 argument (`pnum=1`), which is either by name (`pnameid`), or position (`parr0`). We could shorten this in several ways: either allowing only positional or named parameters, or by testing the start of the parameters only. Here's an example which only checks the first 9 bytes of the `listpeers` parameter: + + $ lightning-cli commando-rune restrictions='["id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605","method=listpeers","pnum=1","pnameid^024b9a1fa8e006f1e393|parr0^024b9a1fa8e006f1e393"]' + { + "rune": "fTQnfL05coEbiBO8SS0cvQwCcPLxE9c02pZCC6HRVEY9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5Mw==", + "unique_id": "3" + } + +Before we give this to our peer, let's add another restriction: that +it only be usable for 24 hours from now. `date +%s` can give us the +current time in seconds: + + $ lightning-cli commando-rune rune=fTQnfL05coEbiBO8SS0cvQwCcPLxE9c02pZCC6HRVEY9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5Mw== restrictions="t<$(($(date +%s) + 24*60*60))" + { + "rune": "Sh-jGdfO9UGByLvah2AHgc_VwgoNujckPNkxTx54ugg9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5MyZ0PDE2NTY4OTc5MjU=", + "unique_id": "3" + } SHARING RUNES ------------- diff --git a/doc/schemas/commando.request.json b/doc/schemas/commando.request.json index 354f83e86683..52c52773f6e7 100644 --- a/doc/schemas/commando.request.json +++ b/doc/schemas/commando.request.json @@ -16,8 +16,16 @@ "description": "method to invoke on peer" }, "params": { - "type": "object", - "description": "parameters for method" + "oneOf": [ + { + "type": "array", + "description": "array of positional parameters" + }, + { + "type": "object", + "description": "parameters for method" + } + ] }, "rune": { "type": "string", diff --git a/plugins/commando.c b/plugins/commando.c index 1fcff8d8e88c..b7e44f82f3dd 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -201,30 +201,37 @@ static const char *check_condition(const tal_t *ctx, return rune_alt_single_str(ctx, alt, cinfo->buf + cinfo->method->start, cinfo->method->end - cinfo->method->start); + } else if (streq(alt->fieldname, "pnum")) { + return rune_alt_single_int(ctx, alt, cinfo->params->size); } /* Rest are params looksup: generate this once! */ - if (cinfo->params) { - /* Note: we require that params be an obj! */ + if (strmap_empty(&cinfo->cached_params)) { const jsmntok_t *t; size_t i; - json_for_each_obj(i, t, cinfo->params) { - char *pmemname = tal_fmt(tmpctx, - "pname%.*s", - t->end - t->start, - cinfo->buf + t->start); - size_t off = strlen("pname"); - /* Remove punctuation! */ - for (size_t n = off; pmemname[n]; n++) { - if (cispunct(pmemname[n])) - continue; - pmemname[off++] = pmemname[n]; + if (cinfo->params->type == JSMN_OBJECT) { + json_for_each_obj(i, t, cinfo->params) { + char *pmemname = tal_fmt(tmpctx, + "pname%.*s", + t->end - t->start, + cinfo->buf + t->start); + size_t off = strlen("pname"); + /* Remove punctuation! */ + for (size_t n = off; pmemname[n]; n++) { + if (cispunct(pmemname[n])) + continue; + pmemname[off++] = pmemname[n]; + } + pmemname[off++] = '\0'; + strmap_add(&cinfo->cached_params, pmemname, t+1); + } + } else if (cinfo->params->type == JSMN_ARRAY) { + json_for_each_arr(i, t, cinfo->params) { + char *pmemname = tal_fmt(tmpctx, "parr%zu", i); + strmap_add(&cinfo->cached_params, pmemname, t); } - pmemname[off++] = '\0'; - strmap_add(&cinfo->cached_params, pmemname, t+1); } - cinfo->params = NULL; } ptok = strmap_get(&cinfo->cached_params, alt->fieldname); @@ -300,9 +307,9 @@ static void try_command(struct node_id *peer, return; } params = json_get_member(buf, toks, "params"); - if (params && params->type != JSMN_OBJECT) { + if (!params || (params->type != JSMN_OBJECT && params->type != JSMN_ARRAY)) { commando_error(incoming, COMMANDO_ERROR_REMOTE, - "Params must be object"); + "Params must be object or array"); return; } rune = json_get_member(buf, toks, "rune"); @@ -323,15 +330,27 @@ static void try_command(struct node_id *peer, size_t i; const jsmntok_t *t; - json_object_start(req->js, "params"); /* FIXME: This is ugly! */ - json_for_each_obj(i, t, params) { - json_add_jsonstr(req->js, - json_strdup(tmpctx, buf, t), - json_tok_full(buf, t+1), - json_tok_full_len(t+1)); + if (params->type == JSMN_OBJECT) { + json_object_start(req->js, "params"); + json_for_each_obj(i, t, params) { + json_add_jsonstr(req->js, + json_strdup(tmpctx, buf, t), + json_tok_full(buf, t+1), + json_tok_full_len(t+1)); + } + json_object_end(req->js); + } else { + assert(params->type == JSMN_ARRAY); + json_array_start(req->js, "params"); + json_for_each_arr(i, t, params) { + json_add_jsonstr(req->js, + NULL, + json_tok_full(buf, t), + json_tok_full_len(t)); + } + json_array_end(req->js); } - json_object_end(req->js); } else { json_object_start(req->js, "params"); json_object_end(req->js); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 1d39f13de4dc..b7ddd03502cd 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2660,6 +2660,12 @@ def test_commando_rune(node_factory): rune5 = l1.rpc.commando_rune(rune4['rune'], "pnamelevel!|pnamelevel/io") assert rune5['rune'] == 'Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8=' assert rune5['unique_id'] == '3' + rune6 = l1.rpc.commando_rune(rune5['rune'], "parr1!|parr1/io") + assert rune6['rune'] == '2Wh6F4R51D3esZzp-7WWG51OhzhfcYKaaI8qiIonaHE9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8mcGFycjEhfHBhcnIxL2lv' + assert rune6['unique_id'] == '3' + rune7 = l1.rpc.commando_rune(restrictions="pnum=0") + assert rune7['rune'] == 'QJonN6ySDFw-P5VnilZxlOGRs_tST1ejtd-bAYuZfjk9NCZwbnVtPTA=' + assert rune7['unique_id'] == '4' # Replace rune3 with a more useful timestamp! expiry = int(time.time()) + 15 @@ -2671,12 +2677,19 @@ def test_commando_rune(node_factory): (rune3, "getinfo", {}), (rune4, "listpeers", {}), (rune5, "listpeers", {'id': l2.info['id']}), - (rune5, "listpeers", {'id': l2.info['id'], 'level': 'broken'})) + (rune5, "listpeers", {'id': l2.info['id'], 'level': 'broken'}), + (rune6, "listpeers", [l2.info['id'], 'broken']), + (rune6, "listpeers", [l2.info['id']]), + (rune7, "listpeers", []), + (rune7, "getinfo", {})) failures = ((rune2, "withdraw", {}), (rune2, "plugin", {'subcommand': 'list'}), (rune3, "getinfo", {}), (rune4, "listnodes", {}), - (rune5, "listpeers", {'id': l2.info['id'], 'level': 'io'})) + (rune5, "listpeers", {'id': l2.info['id'], 'level': 'io'}), + (rune6, "listpeers", [l2.info['id'], 'io']), + (rune7, "listpeers", [l2.info['id']]), + (rune7, "listpeers", {'id': l2.info['id']})) for rune, cmd, params in successes: l2.rpc.call(method='commando', @@ -2708,14 +2721,3 @@ def test_commando_rune(node_factory): 'method': "listpeers", 'params': {}}) assert exc_info.value.error['code'] == 0x4c51 - - # Remote doesn't allow array parameters. - l2.rpc.check_request_schemas = False - with pytest.raises(RpcError, match='Params must be object') as exc_info: - l2.rpc.call(method='commando', - payload={'peer_id': l1.info['id'], - 'rune': rune5['rune'], - 'method': "listpeers", - 'params': [l2.info['id'], 'io']}) - assert exc_info.value.error['code'] == 0x4c50 - l2.rpc.check_request_schemas = True From 468dff172340317a11ac4110d931084e13f7ce2c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1010/1530] commando: add rate for maximum successful rune use per minute. I'm assuming that nobody wants a rate slower than 1 per minute; we can introduce 'drate' if we want a per-day kind of limit. Signed-off-by: Rusty Russell --- doc/lightning-commando-rune.7.md | 19 ++++++-- plugins/commando.c | 84 ++++++++++++++++++++++++++++++++ tests/test_plugin.py | 25 +++++++++- 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index ca31e2e96171..97bc4ec9afed 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -33,6 +33,7 @@ being run: * time: the current UNIX time, e.g. "time<1656759180". * id: the node_id of the peer, e.g. "id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605". * method: the command being run, e.g. "method=withdraw". +* rate: the rate limit, per minute, e.g. "rate=60". * pnum: the number of parameters. e.g. "pnum<2". * pnameX: the parameter named X. e.g. "pnamedestination=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T". * parrN: the N'th parameter. e.g. "parr0=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T". @@ -108,13 +109,14 @@ This allows `listpeers` with 1 argument (`pnum=1`), which is either by name (`pn "unique_id": "3" } -Before we give this to our peer, let's add another restriction: that -it only be usable for 24 hours from now. `date +%s` can give us the -current time in seconds: +Before we give this to our peer, let's add two more restrictions: that +it only be usable for 24 hours from now (`time<`), and that it can only +be used twice a minute (`rate=2`). `date +%s` can give us the current +time in seconds: - $ lightning-cli commando-rune rune=fTQnfL05coEbiBO8SS0cvQwCcPLxE9c02pZCC6HRVEY9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5Mw== restrictions="t<$(($(date +%s) + 24*60*60))" + $ lightning-cli commando-rune rune=fTQnfL05coEbiBO8SS0cvQwCcPLxE9c02pZCC6HRVEY9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5Mw== restrictions='["time<'$(($(date +%s) + 24*60*60))'","rate=2"]' { - "rune": "Sh-jGdfO9UGByLvah2AHgc_VwgoNujckPNkxTx54ugg9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5MyZ0PDE2NTY4OTc5MjU=", + "rune": "tU-RLjMiDpY2U0o3W1oFowar36RFGpWloPbW9-RuZdo9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5MyZ0aW1lPDE2NTY5MjA1MzgmcmF0ZT0y", "unique_id": "3" } @@ -126,6 +128,13 @@ normal rune into a read-only rune, or restrict access for 30 minutes from the time you give it to someone. Adding restrictions before sharing runes is best practice. +If a rune has a ratelimit, any derived rune will have the same id, and +thus will compete for that ratelimit. You might want to consider +adding a tighter ratelimit to a rune before sharing it, so you will +keep the remainder. For example, if you rune has a limit of 60 times +per minute, adding a limit of 5 times per minute and handing that rune +out means you can still use your original rune 55 times per minute. + RETURN VALUE ------------ diff --git a/plugins/commando.c b/plugins/commando.c index b7e44f82f3dd..2f39079a2665 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -1,5 +1,7 @@ #include "config.h" #include +#include +#include #include #include #include @@ -42,6 +44,45 @@ static struct commando **incoming_commands; static u64 *rune_counter; static struct rune *master_rune; +struct usage { + /* If you really issue more than 2^32 runes, they'll share ratelimit buckets */ + u32 id; + u32 counter; +}; + +static u64 usage_id(const struct usage *u) +{ + return u->id; +} + +static size_t id_hash(u64 id) +{ + return siphash24(siphash_seed(), &id, sizeof(id)); +} + +static bool usage_eq_id(const struct usage *u, u64 id) +{ + return u->id == id; +} +HTABLE_DEFINE_TYPE(struct usage, usage_id, id_hash, usage_eq_id, usage_table); +static struct usage_table usage_table; + +/* Every minute we forget entries. */ +static void flush_usage_table(void *unused) +{ + struct usage *u; + struct usage_table_iter it; + + for (u = usage_table_first(&usage_table, &it); + u; + u = usage_table_next(&usage_table, &it)) { + usage_table_delval(&usage_table, &it); + tal_free(u); + } + + notleak(plugin_timer(plugin, time_from_sec(60), flush_usage_table, NULL)); +} + /* NULL peer: don't care about peer. NULL id: don't care about id */ static struct commando *find_commando(struct commando **arr, const struct node_id *peer, @@ -183,8 +224,40 @@ struct cond_info { const jsmntok_t *method; const jsmntok_t *params; STRMAP(const jsmntok_t *) cached_params; + struct usage *usage; }; +static const char *rate_limit_check(const tal_t *ctx, + const struct rune *rune, + const struct rune_altern *alt, + struct cond_info *cinfo) +{ + unsigned long r; + char *endp; + if (alt->condition != '=') + return "rate operator must be ="; + + r = strtoul(alt->value, &endp, 10); + if (endp == alt->value || *endp || r == 0 || r >= UINT32_MAX) + return "malformed rate"; + + /* We cache this: we only add usage counter if whole rune succeeds! */ + if (!cinfo->usage) { + cinfo->usage = usage_table_get(&usage_table, atol(rune->unique_id)); + if (!cinfo->usage) { + cinfo->usage = tal(plugin, struct usage); + cinfo->usage->id = atol(rune->unique_id); + cinfo->usage->counter = 0; + usage_table_add(&usage_table, cinfo->usage); + } + } + + /* >= becuase if we allow this, counter will increment */ + if (cinfo->usage->counter >= r) + return tal_fmt(ctx, "Rate of %lu per minute exceeded", r); + return NULL; +} + static const char *check_condition(const tal_t *ctx, const struct rune *rune, const struct rune_altern *alt, @@ -203,6 +276,8 @@ static const char *check_condition(const tal_t *ctx, cinfo->method->end - cinfo->method->start); } else if (streq(alt->fieldname, "pnum")) { return rune_alt_single_int(ctx, alt, cinfo->params->size); + } else if (streq(alt->fieldname, "rate")) { + return rate_limit_check(ctx, rune, alt, cinfo); } /* Rest are params looksup: generate this once! */ @@ -267,12 +342,17 @@ static const char *check_rune(const tal_t *ctx, cinfo.buf = buf; cinfo.method = method; cinfo.params = params; + cinfo.usage = NULL; strmap_init(&cinfo.cached_params); err = rune_test(ctx, master_rune, rune, check_condition, &cinfo); /* Just in case they manage to make us speak non-JSON, escape! */ if (err) err = json_escape(ctx, take(err))->s; strmap_clear(&cinfo.cached_params); + + /* If it succeeded, *now* we increment any associated usage counter. */ + if (!err && cinfo.usage) + cinfo.usage->counter++; return err; } @@ -770,6 +850,7 @@ static void memleak_mark_globals(struct plugin *p, struct htable *memtable) memleak_remove_region(memtable, outgoing_commands, tal_bytelen(outgoing_commands)); memleak_remove_region(memtable, incoming_commands, tal_bytelen(incoming_commands)); memleak_remove_region(memtable, master_rune, sizeof(*master_rune)); + memleak_remove_htable(memtable, &usage_table.raw); if (rune_counter) memleak_remove_region(memtable, rune_counter, sizeof(*rune_counter)); } @@ -782,6 +863,7 @@ static const char *init(struct plugin *p, outgoing_commands = tal_arr(p, struct commando *, 0); incoming_commands = tal_arr(p, struct commando *, 0); + usage_table_init(&usage_table); plugin = p; #if DEVELOPER plugin_set_memleak_handler(p, memleak_mark_globals); @@ -814,6 +896,8 @@ static const char *init(struct plugin *p, master_rune = rune_new(plugin, rune_secret.data, ARRAY_SIZE(rune_secret.data), NULL); + /* Start flush timer. */ + flush_usage_table(NULL); return NULL; } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b7ddd03502cd..9061ac7154e7 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2666,10 +2666,19 @@ def test_commando_rune(node_factory): rune7 = l1.rpc.commando_rune(restrictions="pnum=0") assert rune7['rune'] == 'QJonN6ySDFw-P5VnilZxlOGRs_tST1ejtd-bAYuZfjk9NCZwbnVtPTA=' assert rune7['unique_id'] == '4' + rune8 = l1.rpc.commando_rune(rune7['rune'], "rate=3") + assert rune8['rune'] == 'kSYFx6ON9hr_ExcQLwVkm1ABnvc1TcMFBwLrAVee0EA9NCZwbnVtPTAmcmF0ZT0z' + assert rune8['unique_id'] == '4' + rune9 = l1.rpc.commando_rune(rune8['rune'], "rate=1") + assert rune9['rune'] == 'O8Zr-ULTBKO3_pKYz0QKE9xYl1vQ4Xx9PtlHuist9Rk9NCZwbnVtPTAmcmF0ZT0zJnJhdGU9MQ==' + assert rune9['unique_id'] == '4' # Replace rune3 with a more useful timestamp! expiry = int(time.time()) + 15 rune3 = l1.rpc.commando_rune(restrictions="time<{}".format(expiry)) + ratelimit_successes = ((rune9, "getinfo", {}), + (rune8, "getinfo", {}), + (rune8, "getinfo", {})) successes = ((rune1, "listpeers", {}), (rune2, "listpeers", {}), (rune2, "getinfo", {}), @@ -2681,7 +2690,7 @@ def test_commando_rune(node_factory): (rune6, "listpeers", [l2.info['id'], 'broken']), (rune6, "listpeers", [l2.info['id']]), (rune7, "listpeers", []), - (rune7, "getinfo", {})) + (rune7, "getinfo", {})) + ratelimit_successes failures = ((rune2, "withdraw", {}), (rune2, "plugin", {'subcommand': 'list'}), (rune3, "getinfo", {}), @@ -2689,7 +2698,9 @@ def test_commando_rune(node_factory): (rune5, "listpeers", {'id': l2.info['id'], 'level': 'io'}), (rune6, "listpeers", [l2.info['id'], 'io']), (rune7, "listpeers", [l2.info['id']]), - (rune7, "listpeers", {'id': l2.info['id']})) + (rune7, "listpeers", {'id': l2.info['id']}), + (rune9, "getinfo", {}), + (rune8, "getinfo", {})) for rune, cmd, params in successes: l2.rpc.call(method='commando', @@ -2721,3 +2732,13 @@ def test_commando_rune(node_factory): 'method': "listpeers", 'params': {}}) assert exc_info.value.error['code'] == 0x4c51 + + # Now wait for ratelimit expiry, ratelimits should reset. + time.sleep(61) + + for rune, cmd, params in ratelimit_successes: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune['rune'], + 'method': cmd, + 'params': params}) From 8c48eda8c7b8fe3f087aa277959411244016f245 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 22:48:27 +0930 Subject: [PATCH 1011/1530] decode: support decoding runes. This is a bit weird since it lives in the offers plugin, but it works well. This should make runes much more approachable for people! Signed-off-by: Rusty Russell --- doc/lightning-commando-rune.7.md | 52 +++++++++- doc/lightning-decode.7.md | 28 ++++-- doc/schemas/decode.schema.json | 69 ++++++++++++- plugins/offers.c | 161 +++++++++++++++++++++++++++++++ tests/test_plugin.py | 53 ++++++++++ 5 files changed, 354 insertions(+), 9 deletions(-) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index 97bc4ec9afed..c2285b65054e 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -120,6 +120,56 @@ time in seconds: "unique_id": "3" } +You can also use lightning-decode(7) to examine runes you have been given: + + $ .lightning-cli decode tU-RLjMiDpY2U0o3W1oFowar36RFGpWloPbW9-RuZdo9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5MyZ0aW1lPDE2NTY5MjA1MzgmcmF0ZT0y + { + "type": "rune", + "unique_id": "3", + "string": "b54f912e33220e9636534a375b5a05a306abdfa4451a95a5a0f6d6f7e46e65da:=3&id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605&method=listpeers&pnum=1&pnameid^024b9a1fa8e006f1e393|parr0^024b9a1fa8e006f1e393&time<1656920538&rate=2", + "restrictions": [ + { + "alternatives": [ + "id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605" + ], + "summary": "id (of commanding peer) equal to '024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605'" + }, + { + "alternatives": [ + "method=listpeers" + ], + "summary": "method (of command) equal to 'listpeers'" + }, + { + "alternatives": [ + "pnum=1" + ], + "summary": "pnum (number of command parameters) equal to 1" + }, + { + "alternatives": [ + "pnameid^024b9a1fa8e006f1e393", + "parr0^024b9a1fa8e006f1e393" + ], + "summary": "pnameid (object parameter 'id') starts with '024b9a1fa8e006f1e393' OR parr0 (array parameter #0) starts with '024b9a1fa8e006f1e393'" + }, + { + "alternatives": [ + "time<1656920538" + ], + "summary": "time (in seconds since 1970) less than 1656920538 (approximately 19 hours 18 minutes from now)" + }, + { + "alternatives": [ + "rate=2" + ], + "summary": "rate (max per minute) equal to 2" + } + ], + "valid": true + } + + SHARING RUNES ------------- @@ -157,7 +207,7 @@ excuses his previous adoption of the name "Eltoo". SEE ALSO -------- -lightning-commando(7) +lightning-commando(7), lightning-decode(7) RESOURCES --------- diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 7c916562b30f..01b3a3c6ea4e 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -9,17 +9,21 @@ SYNOPSIS DESCRIPTION ----------- -The **decode** RPC command checks and parses a *bolt11* or *bolt12* -string (optionally prefixed by `lightning:` or `LIGHTNING:`) as -specified by the BOLT 11 and BOLT 12 specifications. It may decode -other formats in future. +The **decode** RPC command checks and parses: + +- a *bolt11* or *bolt12* string (optionally prefixed by `lightning:` + or `LIGHTNING:`) as specified by the BOLT 11 and BOLT 12 + specifications. +- a *rune* as created by lightning-commando-rune(7). + +It may decode other formats in future. RETURN VALUE ------------ [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") +- **type** (string): what kind of object it decoded to (one of "bolt12 offer", "bolt12 invoice", "bolt12 invoice_request", "bolt11 invoice", "rune") - **valid** (boolean): if this is false, you *MUST* not use the result except for diagnostics! If **type** is "bolt12 offer", and **valid** is *true*: @@ -159,6 +163,16 @@ If **type** is "bolt11 invoice", and **valid** is *true*: - **tag** (string): The bech32 letter which identifies this field (always 1 characters) - **data** (string): The bech32 data for this field +If **type** is "rune": + - **string** (string): the string encoding of the rune + - **restrictions** (array of objects): restrictions built into the rune: all must pass: + - **alternatives** (array of strings): each way restriction can be met: any can pass: + - the alternative of form fieldname condition fieldname + - **summary** (string): human-readable summary of this restriction + - **unique_id** (string, optional): unique id (always a numeric id on runes we create) + - **version** (string, optional): rune version, not currently set on runes we create + - **valid** (boolean, optional) (always *true*) + [comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR @@ -169,7 +183,7 @@ Rusty Russell <> is mainly responsible. SEE ALSO -------- -lightning-pay(7), lightning-offer(7), lightning-offerout(7), lightning-fetchinvoice(7), lightning-sendinvoice(7) +lightning-pay(7), lightning-offer(7), lightning-offerout(7), lightning-fetchinvoice(7), lightning-sendinvoice(7), lightning-commando-rune(7) [BOLT \#11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md). @@ -181,4 +195,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bc3778965137591623ce08ff51adf411bc42e6d1a4200692961b69962da39be7) +[comment]: # ( SHA256STAMP:d1e1f044c2e67ec169728dbc551903c97f9a9daa1f42e9d2f1686fc692d25be8) diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json index 2c84e820bd54..1ff631d28882 100644 --- a/doc/schemas/decode.schema.json +++ b/doc/schemas/decode.schema.json @@ -12,7 +12,8 @@ "bolt12 offer", "bolt12 invoice", "bolt12 invoice_request", - "bolt11 invoice" + "bolt11 invoice", + "rune" ], "description": "what kind of object it decoded to" }, @@ -909,6 +910,72 @@ } } } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "rune" + ] + } + } + }, + "then": { + "required": [ + "string", + "restrictions" + ], + "additionalProperties": false, + "properties": { + "unique_id": { + "type": "string", + "description": "unique id (always a numeric id on runes we create)" + }, + "version": { + "type": "string", + "description": "rune version, not currently set on runes we create" + }, + "valid": { + "type": "boolean", + "enum": [ + true + ] + }, + "type": {}, + "string": { + "type": "string", + "description": "the string encoding of the rune" + }, + "restrictions": { + "type": "array", + "description": "restrictions built into the rune: all must pass", + "items": { + "type": "object", + "required": [ + "alternatives", + "summary" + ], + "additionalProperties": false, + "properties": { + "alternatives": { + "type": "array", + "description": "each way restriction can be met: any can pass", + "items": { + "type": "string", + "description": "the alternative of form fieldname condition fieldname" + } + }, + "summary": { + "type": "string", + "description": "human-readable summary of this restriction" + } + } + } + } + } + } } ] } diff --git a/plugins/offers.c b/plugins/offers.c index 5ca4ea4d7760..72442234c112 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -204,6 +205,7 @@ struct decodable { struct tlv_offer *offer; struct tlv_invoice *invoice; struct tlv_invoice_request *invreq; + struct rune *rune; }; static struct command_result *param_decodable(struct command *cmd, @@ -271,6 +273,13 @@ static struct command_result *param_decodable(struct command *cmd, return NULL; } + decodable->rune = rune_from_base64n(decodable, buffer + tok.start, + tok.end - tok.start); + if (decodable->rune) { + decodable->type = "rune"; + return NULL; + } + /* Return failure message from most likely parsing candidate */ return command_fail_badparam(cmd, name, buffer, &tok, likely_fail); } @@ -808,6 +817,156 @@ static void json_add_invoice_request(struct json_stream *js, json_add_bool(js, "valid", valid); } +static void json_add_rune(struct command *cmd, struct json_stream *js, const struct rune *rune) +{ + if (rune->unique_id) + json_add_string(js, "unique_id", rune->unique_id); + if (rune->version) + json_add_string(js, "version", rune->version); + json_add_string(js, "string", take(rune_to_string(NULL, rune))); + + json_array_start(js, "restrictions"); + for (size_t i = rune->unique_id ? 1 : 0; i < tal_count(rune->restrs); i++) { + const struct rune_restr *restr = rune->restrs[i]; + char *summary = tal_strdup(tmpctx, ""); + const char *sep = ""; + + json_object_start(js, NULL); + json_array_start(js, "alternatives"); + for (size_t j = 0; j < tal_count(restr->alterns); j++) { + const struct rune_altern *alt = restr->alterns[j]; + const char *annotation, *value; + bool int_val = false, time_val = false; + + if (streq(alt->fieldname, "time")) { + annotation = "in seconds since 1970"; + time_val = true; + } else if (streq(alt->fieldname, "id")) + annotation = "of commanding peer"; + else if (streq(alt->fieldname, "method")) + annotation = "of command"; + else if (streq(alt->fieldname, "pnum")) { + annotation = "number of command parameters"; + int_val = true; + } else if (streq(alt->fieldname, "rate")) { + annotation = "max per minute"; + int_val = true; + } else if (strstarts(alt->fieldname, "parr")) { + annotation = tal_fmt(tmpctx, "array parameter #%s", alt->fieldname+4); + } else if (strstarts(alt->fieldname, "pname")) + annotation = tal_fmt(tmpctx, "object parameter '%s'", alt->fieldname+5); + else + annotation = "unknown condition?"; + + tal_append_fmt(&summary, "%s", sep); + + /* Where it's ambiguous, quote if it's not treated as an int */ + if (int_val) + value = alt->value; + else if (time_val) { + u64 t = atol(alt->value); + + if (t) { + u64 diff, now = time_now().ts.tv_sec; + /* Need a non-const during construction */ + char *v; + + if (now > t) + diff = now - t; + else + diff = t - now; + if (diff < 60) + v = tal_fmt(tmpctx, "%"PRIu64" seconds", diff); + else if (diff < 60 * 60) + v = tal_fmt(tmpctx, "%"PRIu64" minutes %"PRIu64" seconds", + diff / 60, diff % 60); + else { + v = tal_strdup(tmpctx, "approximately "); + /* diff is in minutes */ + diff /= 60; + if (diff < 48 * 60) + tal_append_fmt(&v, "%"PRIu64" hours %"PRIu64" minutes", + diff / 60, diff % 60); + else { + /* hours */ + diff /= 60; + if (diff < 60 * 24) + tal_append_fmt(&v, "%"PRIu64" days %"PRIu64" hours", + diff / 24, diff % 24); + else { + /* days */ + diff /= 24; + if (diff < 365 * 2) + tal_append_fmt(&v, "%"PRIu64" months %"PRIu64" days", + diff / 30, diff % 30); + else { + /* months */ + diff /= 30; + tal_append_fmt(&v, "%"PRIu64" years %"PRIu64" months", + diff / 12, diff % 12); + } + } + } + } + if (now > t) + tal_append_fmt(&v, " ago"); + else + tal_append_fmt(&v, " from now"); + value = tal_fmt(tmpctx, "%s (%s)", alt->value, v); + } else + value = alt->value; + } else + value = tal_fmt(tmpctx, "'%s'", alt->value); + + switch (alt->condition) { + case RUNE_COND_IF_MISSING: + tal_append_fmt(&summary, "%s (%s) is missing", alt->fieldname, annotation); + break; + case RUNE_COND_EQUAL: + tal_append_fmt(&summary, "%s (%s) equal to %s", alt->fieldname, annotation, value); + break; + case RUNE_COND_NOT_EQUAL: + tal_append_fmt(&summary, "%s (%s) unequal to %s", alt->fieldname, annotation, value); + break; + case RUNE_COND_BEGINS: + tal_append_fmt(&summary, "%s (%s) starts with '%s'", alt->fieldname, annotation, alt->value); + break; + case RUNE_COND_ENDS: + tal_append_fmt(&summary, "%s (%s) ends with '%s'", alt->fieldname, annotation, alt->value); + break; + case RUNE_COND_CONTAINS: + tal_append_fmt(&summary, "%s (%s) contains '%s'", alt->fieldname, annotation, alt->value); + break; + case RUNE_COND_INT_LESS: + tal_append_fmt(&summary, "%s (%s) less than %s", alt->fieldname, annotation, + time_val ? value : alt->value); + break; + case RUNE_COND_INT_GREATER: + tal_append_fmt(&summary, "%s (%s) greater than %s", alt->fieldname, annotation, + time_val ? value : alt->value); + break; + case RUNE_COND_LEXO_BEFORE: + tal_append_fmt(&summary, "%s (%s) sorts before '%s'", alt->fieldname, annotation, alt->value); + break; + case RUNE_COND_LEXO_AFTER: + tal_append_fmt(&summary, "%s (%s) sorts after '%s'", alt->fieldname, annotation, alt->value); + break; + case RUNE_COND_COMMENT: + tal_append_fmt(&summary, "[comment: %s%s]", alt->fieldname, alt->value); + break; + } + sep = " OR "; + json_add_str_fmt(js, NULL, "%s%c%s", alt->fieldname, alt->condition, alt->value); + } + json_array_end(js); + json_add_string(js, "summary", summary); + json_object_end(js); + } + json_array_end(js); + /* FIXME: do some sanity checks? */ + json_add_bool(js, "valid", true); +} + static struct command_result *json_decode(struct command *cmd, const char *buffer, const jsmntok_t *params) @@ -833,6 +992,8 @@ static struct command_result *json_decode(struct command *cmd, json_add_bolt11(response, decodable->b11); json_add_bool(response, "valid", true); } + if (decodable->rune) + json_add_rune(cmd, response, decodable->rune); return command_finished(cmd, response); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 9061ac7154e7..3d7a76ca048f 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2673,6 +2673,59 @@ def test_commando_rune(node_factory): assert rune9['rune'] == 'O8Zr-ULTBKO3_pKYz0QKE9xYl1vQ4Xx9PtlHuist9Rk9NCZwbnVtPTAmcmF0ZT0zJnJhdGU9MQ==' assert rune9['unique_id'] == '4' + runedecodes = ((rune1, []), + (rune2, [{'alternatives': ['method^list', 'method^get', 'method=summary'], + 'summary': "method (of command) starts with 'list' OR method (of command) starts with 'get' OR method (of command) equal to 'summary'"}, + {'alternatives': ['method/listdatastore'], + 'summary': "method (of command) unequal to 'listdatastore'"}]), + (rune4, [{'alternatives': ['id^022d223620a359a47ff7'], + 'summary': "id (of commanding peer) starts with '022d223620a359a47ff7'"}, + {'alternatives': ['method=listpeers'], + 'summary': "method (of command) equal to 'listpeers'"}]), + (rune5, [{'alternatives': ['id^022d223620a359a47ff7'], + 'summary': "id (of commanding peer) starts with '022d223620a359a47ff7'"}, + {'alternatives': ['method=listpeers'], + 'summary': "method (of command) equal to 'listpeers'"}, + {'alternatives': ['pnamelevel!', 'pnamelevel/io'], + 'summary': "pnamelevel (object parameter 'level') is missing OR pnamelevel (object parameter 'level') unequal to 'io'"}]), + (rune6, [{'alternatives': ['id^022d223620a359a47ff7'], + 'summary': "id (of commanding peer) starts with '022d223620a359a47ff7'"}, + {'alternatives': ['method=listpeers'], + 'summary': "method (of command) equal to 'listpeers'"}, + {'alternatives': ['pnamelevel!', 'pnamelevel/io'], + 'summary': "pnamelevel (object parameter 'level') is missing OR pnamelevel (object parameter 'level') unequal to 'io'"}, + {'alternatives': ['parr1!', 'parr1/io'], + 'summary': "parr1 (array parameter #1) is missing OR parr1 (array parameter #1) unequal to 'io'"}]), + (rune7, [{'alternatives': ['pnum=0'], + 'summary': "pnum (number of command parameters) equal to 0"}]), + (rune8, [{'alternatives': ['pnum=0'], + 'summary': "pnum (number of command parameters) equal to 0"}, + {'alternatives': ['rate=3'], + 'summary': "rate (max per minute) equal to 3"}]), + (rune9, [{'alternatives': ['pnum=0'], + 'summary': "pnum (number of command parameters) equal to 0"}, + {'alternatives': ['rate=3'], + 'summary': "rate (max per minute) equal to 3"}, + {'alternatives': ['rate=1'], + 'summary': "rate (max per minute) equal to 1"}])) + for decode in runedecodes: + rune = decode[0] + restrictions = decode[1] + decoded = l1.rpc.decode(rune['rune']) + assert decoded['type'] == 'rune' + assert decoded['unique_id'] == rune['unique_id'] + assert decoded['valid'] is True + assert decoded['restrictions'] == restrictions + + # Time handling is a bit special, since we annotate the timestamp with how far away it is. + decoded = l1.rpc.decode(rune3['rune']) + assert decoded['type'] == 'rune' + assert decoded['unique_id'] == rune3['unique_id'] + assert decoded['valid'] is True + assert len(decoded['restrictions']) == 1 + assert decoded['restrictions'][0]['alternatives'] == ['time>1656675211'] + assert decoded['restrictions'][0]['summary'].startswith("time (in seconds since 1970) greater than 1656675211 (") + # Replace rune3 with a more useful timestamp! expiry = int(time.time()) + 15 rune3 = l1.rpc.commando_rune(restrictions="time<{}".format(expiry)) From 58ae885f48f5a85613b2a82a6db59fc37f2847e7 Mon Sep 17 00:00:00 2001 From: zero fee routing <90521529+zerofeerouting@users.noreply.github.com> Date: Sat, 16 Jul 2022 22:48:28 +0930 Subject: [PATCH 1012/1530] fix typo in commando documentation --- doc/lightning-commando-rune.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index c2285b65054e..699cab88dd2f 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -41,7 +41,7 @@ being run: RESTRICTION FORMAT ------------------ -Restrictions are one or more altneratives, separated by `|`. Each +Restrictions are one or more alternatives, separated by `|`. Each alternative is *name* *operator* *value*. The valid names are shown above. If a value contains `|`, `&` or `\\`, it must be preceeded by a `\\`. From af2b863b4a3a5e56c10ba20a1af5ff969379543c Mon Sep 17 00:00:00 2001 From: grubles Date: Sat, 16 Jul 2022 10:13:17 -0400 Subject: [PATCH 1013/1530] Add instructions for checking out a release tag --- doc/INSTALL.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 3b4585b89496..e841b8d36af8 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -56,6 +56,10 @@ Clone lightning: git clone https://github.com/ElementsProject/lightning.git cd lightning +Checkout a release tag: + + git checkout v0.11.2 + For development or running tests, get additional dependencies: sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ @@ -114,6 +118,11 @@ $ git clone https://github.com/ElementsProject/lightning.git $ cd lightning ``` +Checkout a release tag: +``` +$ git checkout v0.11.2 +``` + Build and install lightning: ``` $lightning> ./configure @@ -260,6 +269,10 @@ Clone lightning: $ git clone https://github.com/ElementsProject/lightning.git $ cd lightning +Checkout a release tag: + + $ git checkout v0.11.2 + Build lightning: $ poetry install From 6204d70a3729570f019ef7269fca79211c1e6350 Mon Sep 17 00:00:00 2001 From: Swapnil Date: Sun, 17 Jul 2022 11:57:33 +0530 Subject: [PATCH 1014/1530] docs: fix contrib/ docs --- contrib/pyln-client/README.md | 2 +- contrib/pyln-proto/README.md | 2 +- contrib/pyln-testing/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-client/README.md b/contrib/pyln-client/README.md index 0a16c4d98ef1..38fba5aa05cb 100644 --- a/contrib/pyln-client/README.md +++ b/contrib/pyln-client/README.md @@ -21,7 +21,7 @@ installing into your python3 environment: ```bash git clone https://github.com/ElementsProject/lightning.git cd lightning/contrib/pyln-client -python3 setup.py develop +poetry install ``` This will add links to the library into your environment so changing the diff --git a/contrib/pyln-proto/README.md b/contrib/pyln-proto/README.md index a01bc77442bc..8914b3226b2c 100644 --- a/contrib/pyln-proto/README.md +++ b/contrib/pyln-proto/README.md @@ -21,7 +21,7 @@ installing into your python3 environment: ```bash git clone https://github.com/ElementsProject/lightning.git cd lightning/contrib/pyln-proto -python3 setup.py develop +poetry install ``` This will add links to the library into your environment so changing the diff --git a/contrib/pyln-testing/README.md b/contrib/pyln-testing/README.md index 7ddc98149f74..469dc33a57ba 100644 --- a/contrib/pyln-testing/README.md +++ b/contrib/pyln-testing/README.md @@ -23,7 +23,7 @@ installing into your python3 environment: ```bash git clone https://github.com/ElementsProject/lightning.git cd lightning/contrib/pyln-testing -python3 setup.py develop +poetry install ``` This will add links to the library into your environment so changing the From 6d4285d7c47bbe5eb17dd02ff795514b6f746c7b Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sun, 17 Jul 2022 13:24:05 +0200 Subject: [PATCH 1015/1530] pytest: add xfail test to show DNS w/o port issue This adds an X-Fail testcase that demonstrates that currently the port of a DNS announcement is not set to the corresponding network port (in this case regtest), but it will be set to 0. Changelog-None --- tests/test_gossip.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 2dacfb9db02d..466559e0a61f 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -239,7 +239,6 @@ def test_announce_and_connect_via_dns(node_factory, bitcoind): @unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") -@pytest.mark.developer("gossip without DEVELOPER=1 is slow") def test_only_announce_one_dns(node_factory, bitcoind): # and test that we can't announce more than one DNS address l1 = node_factory.get_node(may_fail=True, expect_fail=True, @@ -247,6 +246,22 @@ def test_only_announce_one_dns(node_factory, bitcoind): wait_for(lambda: l1.daemon.is_in_stderr("Only one DNS can be announced")) +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") +@pytest.mark.xfail(strict=True, raises=AssertionError) +def test_announce_dns_without_port(node_factory, bitcoind): + """ Checks that the port of a DNS announcement is set to the corresponding + network port. In this case regtest 19846 + """ + opts = {'announce-addr': ['example.com']} + l1 = node_factory.get_node(options=opts) + + # 'address': [{'type': 'dns', 'address': 'example.com', 'port': 0}] + info = l1.rpc.getinfo() + assert info['address'][0]['type'] == 'dns' + assert info['address'][0]['address'] == 'example.com' + assert info['address'][0]['port'] == 19846 + + @pytest.mark.developer("needs DEVELOPER=1") def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): # Updates get backdated 5 seconds with --dev-fast-gossip. From 65433de05f48bb1bfa1e1ab0363eb28cb7fc4fc5 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sun, 17 Jul 2022 13:36:04 +0200 Subject: [PATCH 1016/1530] options: set DNS port to network default if not specified - set port for a DNS announcement without port to network default - remove x-fail Changelog-Fixed: Port of a DNS announcement can be 0 if unspecified --- lightningd/options.c | 5 ++++- tests/test_gossip.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lightningd/options.c b/lightningd/options.c index fc3263a21c0d..9333d21dabb6 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -272,7 +272,10 @@ static char *opt_add_addr_withtype(const char *arg, wi.u.wireaddr.addrlen = strlen(address); strncpy((char * restrict)&wi.u.wireaddr.addr, address, sizeof(wi.u.wireaddr.addr) - 1); - wi.u.wireaddr.port = port; + if (port == 0) + wi.u.wireaddr.port = ld->portnum; + else + wi.u.wireaddr.port = port; tal_arr_expand(&ld->proposed_listen_announce, ADDR_ANNOUNCE); tal_arr_expand(&ld->proposed_wireaddr, wi); diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 466559e0a61f..0a217e7e1bda 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -247,7 +247,6 @@ def test_only_announce_one_dns(node_factory, bitcoind): @unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") -@pytest.mark.xfail(strict=True, raises=AssertionError) def test_announce_dns_without_port(node_factory, bitcoind): """ Checks that the port of a DNS announcement is set to the corresponding network port. In this case regtest 19846 From 73762de18c5032984e507f2430e0d3a9744af878 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 08:06:56 +0930 Subject: [PATCH 1017/1530] lightningd: reduce log level for remote address reporting. It's available in listpeers() if you want to see it, otherwise it's not really something users want to see in the normal course of operation. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index afb4ea0d2770..a510dbcfa167 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1202,8 +1202,8 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { - log_peer_info(ld->log, &id, "Peer says it sees our address as: %s", - fmt_wireaddr(tmpctx, hook_payload->remote_addr)); + log_peer_debug(ld->log, &id, "Peer says it sees our address as: %s", + fmt_wireaddr(tmpctx, hook_payload->remote_addr)); peer->remote_addr = tal_dup(peer, struct wireaddr, hook_payload->remote_addr); /* Currently only from peers we have a channel with, until we From a3f5d31b09c91019be0fd257a65677bf22c10342 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 25 Mar 2022 12:46:15 -0700 Subject: [PATCH 1018/1530] startup_regtest: add experimental-offers --- contrib/startup_regtest.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index 0579ec8004f5..3ae90e54b32e 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -96,6 +96,7 @@ start_nodes() { dev-fast-gossip dev-bitcoind-poll=5 experimental-dual-fund + experimental-offers funder-policy=match funder-policy-mod=100 funder-min-their-funding=10000 From 98185dfc2b6a9d264b111249fe890123b65ff492 Mon Sep 17 00:00:00 2001 From: William Casarin Date: Fri, 25 Mar 2022 12:46:54 -0700 Subject: [PATCH 1019/1530] startup_regtest: add connect helper `connect 1 2` to connect from l1 to l2, etc --- contrib/startup_regtest.sh | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index 3ae90e54b32e..bf4d978c60fd 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -14,16 +14,12 @@ ## ## $ start_ln 3 ## -## Let's connect the nodes. +## Let's connect the nodes. The `connect a b` command connects node a to b. ## -## $ l2-cli getinfo | jq .id -## "02b96b03e42d9126cb5228752c575c628ad09bdb7a138ec5142bbca21e244ddceb" -## $ l2-cli getinfo | jq .binding[0].port -## 9090 -## $ l1-cli connect 02b96b03e42d9126cb5228752c575c628ad09bdb7a138ec5142bbca21e244ddceb@localhost:9090 -## { -## "id" : "030b02fc3d043d2d47ae25a9306d98d2abb7fc9bee824e68b8ce75d6d8f09d5eb7" -## } +## $ connect 1 2 +## { +## "id" : "030b02fc3d043d2d47ae25a9306d98d2abb7fc9bee824e68b8ce75d6d8f09d5eb7" +## } ## ## When you're finished, clean up or stop ## @@ -318,3 +314,12 @@ stop_elem() { unset LN_NODES unalias et-cli } + +connect() { + if [ -z "$1" ] || [ -z "$2" ]; then + printf "usage: connect 1 2\n" + else + to=$($LCLI --lightning-dir="/tmp/l$2-$network" -F getinfo | grep '^\(id\|binding\[0\]\.\(address\|port\)\)' | cut -d= -f2- | tr '\n' ' ' | (read -r ID ADDR PORT; echo "$ID@${ADDR}:$PORT")) + $LCLI --lightning-dir="/tmp/l$1-$network" connect "$to" + fi +} From 08e3e979c8c5f6926f2b97255981159547f7216c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:29 +0930 Subject: [PATCH 1020/1530] lightningd: set cid correctly in peer->uncommitted_channel. Setting it to 0xfffff... is just confusing. Signed-off-by: Rusty Russell --- lightningd/opening_common.c | 2 -- lightningd/opening_common.h | 2 +- lightningd/opening_control.c | 6 ++++-- lightningd/peer_control.c | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 866f135de3f1..f3c455e92855 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -61,8 +61,6 @@ new_uncommitted_channel(struct peer *peer) */ uc->minimum_depth = ld->config.anchor_confirms; - memset(&uc->cid, 0xFF, sizeof(uc->cid)); - /* Declare the new channel to the HSM. */ new_channel_msg = towire_hsmd_new_channel(NULL, &uc->peer->id, uc->dbid); if (!wire_sync_write(ld->hsm_fd, take(new_channel_msg))) diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index 8d1b1c490e6f..50a236c50210 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -28,7 +28,7 @@ struct uncommitted_channel { /* Reserved dbid for if we become a real struct channel */ u64 dbid; - /* Channel id, v2 opens only */ + /* Channel id (temporary!) */ struct channel_id cid; /* For logging */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index a89092370247..987f82a1ce58 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1142,6 +1142,8 @@ static struct command_result *json_fundchannel_start(struct command *cmd, return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, "Peer not connected"); + temporary_channel_id(&tmp_channel_id); + if (!peer->uncommitted_channel) { if (feature_negotiated(cmd->ld->our_features, peer->their_features, @@ -1168,6 +1170,8 @@ static struct command_result *json_fundchannel_start(struct command *cmd, "peer"); } + peer->uncommitted_channel->cid = tmp_channel_id; + /* BOLT #2: * - if both nodes advertised `option_support_large_channel`: * - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi. @@ -1220,8 +1224,6 @@ static struct command_result *json_fundchannel_start(struct command *cmd, } else upfront_shutdown_script_wallet_index = NULL; - temporary_channel_id(&tmp_channel_id); - fc->open_msg = towire_openingd_funder_start(fc, *amount, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index a510dbcfa167..f8d281ec21bb 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1339,6 +1339,7 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) goto send_error; } peer->uncommitted_channel = new_uncommitted_channel(peer); + peer->uncommitted_channel->cid = channel_id; peer_start_openingd(peer, peer_fd); break; case WIRE_OPEN_CHANNEL2: From b8ed1077437707b5fc1dec53437ccef4b958c42e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:29 +0930 Subject: [PATCH 1021/1530] lightningd: fix dev-memleak crash on unown unconfirmed channels. ``` lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: FATAL SIGNAL 11 (version e0507aa) lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: common/daemon.c:38 (send_backtrace) 0x56319736e437 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: common/daemon.c:46 (crashdump) 0x56319736e48a lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 ((null)) 0x7fc37721151f lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/subd.c:839 (subd_send_msg) 0x5631973425f3 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/subd.c:859 (subd_req_) 0x5631973426e4 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/peer_control.c:2967 (peer_dev_memleak) 0x56319732ca93 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/memdump.c:296 (json_memleak) 0x56319730fc97 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/jsonrpc.c:630 (command_exec) 0x563197306f9d lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/jsonrpc.c:765 (rpc_command_hook_final) 0x5631973075d5 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/plugin_hook.c:278 (plugin_hook_call_) 0x56319733db47 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/jsonrpc.c:853 (plugin_hook_call_rpc_command) 0x5631973079d4 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/jsonrpc.c:957 (parse_request) 0x563197307efb lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/jsonrpc.c:1054 (read_json) 0x563197308361 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:59 (next_plan) 0x5631973de46c lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:407 (do_plan) 0x5631973df0a1 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:417 (io_ready) 0x5631973df0e3 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: ccan/ccan/io/poll.c:453 (io_loop) 0x5631973e147f lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/io_loop_with_timers.c:22 (io_loop_with_timers) 0x563197305041 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: lightningd/lightningd.c:1184 (main) 0x56319730b58d lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: ../sysdeps/nptl/libc_start_call_main.h:58 (__libc_start_call_main) 0x7fc3771f8d8f lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: ../csu/libc-start.c:392 (__libc_start_main_impl) 0x7fc3771f8e3f lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x5631972dec54 lightningd-1 2022-07-14T08:18:38.972Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xffffffffffffffff lightningd-3 2022-07-14T08:18:38.976Z DEBUG connectd: drain_peer ``` Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index f8d281ec21bb..4dfaaaeea337 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2914,7 +2914,7 @@ void peer_dev_memleak(struct lightningd *ld, struct leak_detect *leaks) list_for_each(&ld->peers, p, list) { struct channel *c; - if (p->uncommitted_channel) { + if (p->uncommitted_channel && p->uncommitted_channel->open_daemon) { struct subd *openingd = p->uncommitted_channel->open_daemon; start_leak_request(subd_req(openingd, openingd, take(towire_openingd_dev_memleak(NULL)), From 912ac25270582637b799392c8de5fbb50147350c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:29 +0930 Subject: [PATCH 1022/1530] lightningd: remove 'connected' flag from channel structure. It's directly a product of "does it have a current owner subdaemon" and "does that subdaemon talk to peers", so create a helper function which just evaluates that instead. Signed-off-by: Rusty Russell --- lightningd/channel.c | 13 +++++++------ lightningd/channel.h | 6 ++---- lightningd/connect_control.c | 7 ++----- lightningd/dual_open_control.c | 3 --- lightningd/opening_control.c | 3 --- wallet/test/run-wallet.c | 1 - wallet/wallet.c | 2 -- wallet/walletrpc.c | 3 +-- 8 files changed, 12 insertions(+), 26 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 76b6a34eda84..735b9ab68505 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -22,14 +22,14 @@ void channel_set_owner(struct channel *channel, struct subd *owner) { struct subd *old_owner = channel->owner; + bool was_connected = channel_is_connected(channel); channel->owner = owner; if (old_owner) { subd_release_channel(old_owner, channel); - if (channel->connected) + if (was_connected && !channel_is_connected(channel)) maybe_disconnect_peer(channel->peer->ld, channel->peer); } - channel->connected = (owner && owner->talks_to_peer); } struct htlc_out *channel_has_htlc_out(struct channel *channel) @@ -239,8 +239,6 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->channel_update = NULL; channel->alias[LOCAL] = channel->alias[REMOTE] = NULL; - /* Channel is connected! */ - channel->connected = true; channel->shutdown_scriptpubkey[REMOTE] = NULL; channel->last_was_revoke = false; channel->last_sent_commit = NULL; @@ -376,7 +374,6 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u32 first_blocknum, u32 min_possible_feerate, u32 max_possible_feerate, - bool connected, const struct basepoints *local_basepoints, const struct pubkey *local_funding_pubkey, const struct pubkey *future_per_commitment_point, @@ -485,7 +482,6 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->first_blocknum = first_blocknum; channel->min_possible_feerate = min_possible_feerate; channel->max_possible_feerate = max_possible_feerate; - channel->connected = connected; channel->local_basepoints = *local_basepoints; channel->local_funding_pubkey = *local_funding_pubkey; channel->future_per_commitment_point @@ -980,6 +976,11 @@ void channel_fail_reconnect(struct channel *channel, const char *fmt, ...) va_end(ap); } +bool channel_is_connected(const struct channel *channel) +{ + return channel->owner && channel->owner->talks_to_peer; +} + const struct short_channel_id * channel_scid_or_local_alias(const struct channel *chan) { diff --git a/lightningd/channel.h b/lightningd/channel.h index 8851bd72ff08..c872bd1cc340 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -205,9 +205,6 @@ struct channel { /* Feerate range */ u32 min_possible_feerate, max_possible_feerate; - /* Does gossipd need to know if the owner dies? (ie. not onchaind) */ - bool connected; - /* Do we have an "impossible" future per_commitment_point from * peer via option_data_loss_protect? */ const struct pubkey *future_per_commitment_point; @@ -267,6 +264,8 @@ struct channel { struct scb_chan *scb; }; +bool channel_is_connected(const struct channel *channel); + /* For v2 opens, a channel that has not yet been committed/saved to disk */ struct channel *new_unsaved_channel(struct peer *peer, u32 feerate_base, @@ -317,7 +316,6 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u32 first_blocknum, u32 min_possible_feerate, u32 max_possible_feerate, - bool connected, const struct basepoints *local_basepoints, const struct pubkey *local_funding_pubkey, const struct pubkey *future_per_commitment_point, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 3ef116a7622a..7f0965afba7e 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -677,12 +677,9 @@ void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer) if (peer->uncommitted_channel) return; - list_for_each(&peer->channels, channel, list) { - if (!channel->owner) - continue; - if (channel->owner->talks_to_peer) + list_for_each(&peer->channels, channel, list) + if (channel_is_connected(channel)) return; - } /* If shutting down, connectd no longer exists */ if (!ld->connectd) { diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index d3b9a0c5dc44..fd501641ae4e 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1234,9 +1234,6 @@ wallet_commit_channel(struct lightningd *ld, channel->scb->funding_sats = total_funding; channel->scb->type = channel_type_dup(channel->scb, channel->type); - /* We are connected */ - channel->connected = true; - if (our_upfront_shutdown_script) channel->shutdown_scriptpubkey[LOCAL] = tal_steal(channel, our_upfront_shutdown_script); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 987f82a1ce58..8438155c5cf1 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -202,8 +202,6 @@ wallet_commit_channel(struct lightningd *ld, * in theory, but it's only used for timing out. */ get_network_blockheight(ld->topology), feerate, feerate, - /* We are connected */ - true, &uc->local_basepoints, &uc->local_funding_pubkey, NULL, @@ -1363,7 +1361,6 @@ static struct channel *stub_chan(struct command *cmd, get_network_blockheight(ld->topology), FEERATE_FLOOR, funding_sats.satoshis / MINIMUM_TX_WEIGHT * 1000 /* Raw: convert to feerate */, - false, &basepoints, &localFundingPubkey, NULL, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 94cb6a29e8e5..c16ffc6dff4a 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1608,7 +1608,6 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) 100, /* first_blocknum */ 100, /* min_possible_feerate */ 10000, /* max_possible_feerate */ - false, &basepoints, &pk, NULL, 1000, 100, diff --git a/wallet/wallet.c b/wallet/wallet.c index 6f6c6b699a86..b9d670ad2dc6 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1500,8 +1500,6 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm db_col_u64(stmt, "first_blocknum"), db_col_int(stmt, "min_possible_feerate"), db_col_int(stmt, "max_possible_feerate"), - /* Not connected */ - false, &local_basepoints, &local_funding_pubkey, future_per_commitment_point, db_col_int(stmt, "feerate_base"), diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index e7be2d410c2c..809bd8ef4f7c 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -342,9 +342,8 @@ static struct command_result *json_listfunds(struct command *cmd, continue; json_object_start(response, NULL); json_add_node_id(response, "peer_id", &p->id); - /* Mirrors logic in listpeers */ json_add_bool(response, "connected", - channel_active(c) && c->connected); + channel_is_connected(c)); json_add_string(response, "state", channel_state_name(c)); if (c->scid) From 37ff013c2c5eac7d13a774f1ea655e8fdfca4841 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:29 +0930 Subject: [PATCH 1023/1530] connectd: fix subd tal parents. This came out in a later patch: freeing the peer->subds doesn't actually free the subds, because they're reparented onto subd->conn, which is a child of peer itself. This breaks because when the peer is finally freed, destroy_subd is called, and expects to find itself in peer->subds (but we made that NULL when we manually freed it!). Fix this, and make it obvious that we tal_steal it. ``` ightning_connectd: FATAL SIGNAL 11 (version v0.11.0.1-25-gbf025aa-modded) 0x55de2a1b8b94 send_backtrace common/daemon.c:33 0x55de2a1b8c3e crashdump common/daemon.c:46 0x7fe2be2fc08f ??? /build/glibc-SzIz7B/glibc-2.31/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 0x55de2a1af41e destroy_subd connectd/multiplex.c:1119 0x55de2a217686 notify ccan/ccan/tal/tal.c:240 0x55de2a217b9d del_tree ccan/ccan/tal/tal.c:402 0x55de2a217bef del_tree ccan/ccan/tal/tal.c:412 0x55de2a217bef del_tree ccan/ccan/tal/tal.c:412 0x55de2a217f39 tal_free ccan/ccan/tal/tal.c:486 0x55de2a1aa116 peer_discard connectd/connectd.c:1834 0x55de2a1aa38d recv_req connectd/connectd.c:1903 0x55de2a1b9121 handle_read common/daemon_conn.c:31 0x55de2a205a35 next_plan ccan/ccan/io/io.c:59 0x55de2a20663d do_plan ccan/ccan/io/io.c:407 0x55de2a20667f io_ready ccan/ccan/io/io.c:417 0x55de2a208972 io_loop ccan/ccan/io/poll.c:453 0x55de2a1aa736 main connectd/connectd.c:2042 0x7fe2be2dd082 __libc_start_main ../csu/libc-start.c:308 0x55de2a1a085d ??? ???:0 0xffffffffffffffff ??? ???:0 ``` Signed-off-by: Rusty Russell --- connectd/multiplex.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index cf02a07808cb..772b82611eba 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -1205,14 +1205,14 @@ static struct subd *multiplex_subd_setup(struct peer *peer, return NULL; } - subd = tal(peer->subds, struct subd); + subd = tal(NULL, struct subd); subd->peer = peer; subd->outq = msg_queue_new(subd, false); subd->channel_id = *channel_id; subd->temporary_channel_id = NULL; subd->opener_revocation_basepoint = NULL; /* This sets subd->conn inside subd_conn_init */ - io_new_conn(peer, fds[0], subd_conn_init, subd); + io_new_conn(peer->subds, fds[0], subd_conn_init, subd); /* When conn dies, subd is freed. */ tal_steal(subd->conn, subd); From 9dc388036028c32eab79785b1a8591e0ea5c38e4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:29 +0930 Subject: [PATCH 1024/1530] connectd: put peer into "draining" mode when we want to close it. This removes it from the hashtable, and forces it to do nothing but send out any remaining packets, then close. It is, in effect, reduced to a stub, with no further interactions with the rest of the system (all subds are freed already). Also removes the need for an explicit "final_msg" too. Signed-off-by: Rusty Russell --- connectd/connectd.c | 8 +++--- connectd/connectd.h | 9 ++++-- connectd/multiplex.c | 68 ++++++++++++++++++++++++++------------------ 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 48d027e54211..df21e7a45407 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -284,9 +284,9 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, } /*~ When we free a peer, we remove it from the daemon's hashtable */ -static void destroy_peer(struct peer *peer, struct daemon *daemon) +void destroy_peer(struct peer *peer) { - peer_htable_del(&daemon->peers, peer); + peer_htable_del(&peer->daemon->peers, peer); } /*~ This is where we create a new peer. */ @@ -302,13 +302,13 @@ static struct peer *new_peer(struct daemon *daemon, peer->daemon = daemon; peer->id = *id; peer->cs = *cs; - peer->final_msg = NULL; peer->subds = tal_arr(peer, struct subd *, 0); peer->peer_in = NULL; peer->sent_to_peer = NULL; peer->urgent = false; peer->ready_to_die = false; peer->active = false; + peer->draining = false; peer->peer_outq = msg_queue_new(peer, false); peer->last_recv_time = time_now(); @@ -322,7 +322,7 @@ static struct peer *new_peer(struct daemon *daemon, /* Now we own it */ tal_steal(peer, peer->to_peer); peer_htable_add(&daemon->peers, peer); - tal_add_destructor2(peer, destroy_peer, daemon); + tal_add_destructor(peer, destroy_peer); return peer; } diff --git a/connectd/connectd.h b/connectd/connectd.h index a1a559eb0c4d..43e7b242a828 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -53,12 +53,12 @@ struct peer { /* Connection to the peer */ struct io_conn *to_peer; + /* Is this draining? If so, just keep writing until queue empty */ + bool draining; + /* Connections to the subdaemons */ struct subd **subds; - /* Final message to send to peer (and hangup) */ - u8 *final_msg; - /* Set once lightningd says it's OK to close (subd tells it * it's done). */ bool ready_to_die; @@ -223,4 +223,7 @@ struct io_plan *peer_connected(struct io_conn *conn, /* Called when peer->peer_conn is finally freed */ void peer_conn_closed(struct peer *peer); +/* Removes peer from hash table */ +void destroy_peer(struct peer *peer); + #endif /* LIGHTNING_CONNECTD_CONNECTD_H */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 772b82611eba..9a88724b57b6 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -81,28 +81,54 @@ static struct subd *find_subd(struct peer *peer, return NULL; } +/* We just want to send these messages out to the peer's connection, + * then close. We consider the peer dead to us (can be freed). */ +static void drain_peer(struct peer *peer) +{ + assert(!peer->draining); + + /* FIXME: Don't drain forever! */ + notleak(peer); + + /* We no longer want subds feeding us more messages! */ + peer->subds = tal_free(peer->subds); + peer->draining = true; + + /* Clean peer from hashtable; we no longer exist. */ + destroy_peer(peer); + tal_del_destructor(peer, destroy_peer); + + /* Start draining process! */ + io_wake(peer->peer_outq); +} + void inject_peer_msg(struct peer *peer, const u8 *msg TAKES) { status_peer_io(LOG_IO_OUT, &peer->id, msg); msg_enqueue(peer->peer_outq, msg); } +void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) +{ + inject_peer_msg(peer, final_msg); + drain_peer(peer); +} + /* Send warning, close connection to peer */ static void send_warning(struct peer *peer, const char *fmt, ...) { va_list ap; + u8 *msg; va_start(ap, fmt); status_vfmt(LOG_UNUSUAL, &peer->id, fmt, ap); va_end(ap); - /* Close to any subdaemons. */ - peer->subds = tal_free(peer->subds); - - /* Send warning as final message. */ va_start(ap, fmt); - peer->final_msg = towire_warningfmtv(peer, NULL, fmt, ap); + msg = towire_warningfmtv(NULL, NULL, fmt, ap); va_end(ap); + + multiplex_final_msg(peer, take(msg)); } /* Kicks off write_to_peer() to look for more gossip to send from store */ @@ -934,17 +960,14 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, /* Pop tail of send queue */ msg = msg_dequeue(peer->peer_outq); - /* Is it time to send final? */ - if (!msg && peer->final_msg && tal_count(peer->subds) == 0) { - /* OK, send this then close. */ - msg = peer->final_msg; - peer->final_msg = NULL; - /* Wasn't logged earlier, so do it now */ - status_peer_io(LOG_IO_OUT, &peer->id, msg); - } - /* Still nothing to send? */ if (!msg) { + /* Draining? We're done. */ + if (peer->draining) { + set_closing_timer(peer, peer_conn); + return io_sock_shutdown(peer_conn); + } + /* We close once subds are all closed; or if we're not active, when told to die. */ if ((peer->active || peer->ready_to_die) @@ -1083,8 +1106,6 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, send_warning(peer, "Unexpected message %s: %s", peer_wire_name(type), tal_hex(tmpctx, decrypted)); - io_wake(peer->peer_outq); - return read_hdr_from_peer(peer_conn, peer); } @@ -1127,6 +1148,10 @@ static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, { assert(peer->to_peer == peer_conn); + /* If we're draining, ignore all incoming. */ + if (peer->draining) + return io_halfclose(peer_conn); + /* BOLT #8: * * ### Receiving and Decrypting Messages @@ -1164,10 +1189,6 @@ static void destroy_subd(struct subd *subd) tal_arr_remove(&peer->subds, pos); - /* In case they were waiting for this to send final_msg */ - if (tal_count(peer->subds) == 0 && peer->final_msg) - msg_wake(peer->peer_outq); - /* Make sure we try to keep reading from peer, so we know if * it hangs up! */ io_wake(&peer->peer_in); @@ -1260,13 +1281,6 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, write_to_peer(peer_conn, peer)); } -void multiplex_final_msg(struct peer *peer, const u8 *final_msg TAKES) -{ - peer->ready_to_die = true; - peer->final_msg = tal_dup_talarr(peer, u8, final_msg); - if (tal_count(peer->subds) == 0) - io_wake(peer->peer_outq); -} /* Lightningd says to send a ping */ void send_manual_ping(struct daemon *daemon, const u8 *msg) From c64ce4bbf31752120c9c7013e5d53d10c6d3758d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:29 +0930 Subject: [PATCH 1025/1530] lightningd: clean up channels when connectd says peer is gone. This is redundant now, since connectd only sends us this once we tell it it's OK, but that's changing, so clean up now. This means that connectd will be able to make *unsolicited* closes, if it needs to. We share logic with peer_please_disconnect. Signed-off-by: Rusty Russell --- lightningd/connect_control.c | 21 +-------------------- lightningd/peer_control.c | 34 ++++++++++++++++++++++++++++++---- lightningd/peer_control.h | 2 ++ 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 7f0965afba7e..842fe8a28ca8 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -390,7 +390,6 @@ static void peer_please_disconnect(struct lightningd *ld, const u8 *msg) { struct node_id id; struct peer *peer; - struct channel *c, **channels; if (!fromwire_connectd_reconnected(msg, &id)) fatal("Bad msg %s from connectd", tal_hex(tmpctx, msg)); @@ -399,25 +398,7 @@ static void peer_please_disconnect(struct lightningd *ld, const u8 *msg) if (!peer) return; - /* Freeing channels can free peer, so gather first. */ - channels = tal_arr(tmpctx, struct channel *, 0); - list_for_each(&peer->channels, c, list) - tal_arr_expand(&channels, c); - - if (peer->uncommitted_channel) - kill_uncommitted_channel(peer->uncommitted_channel, - "Reconnected"); - - for (size_t i = 0; i < tal_count(channels); i++) { - c = channels[i]; - if (channel_active(c)) { - channel_cleanup_commands(c, "Reconnected"); - channel_fail_reconnect(c, "Reconnected"); - } else if (channel_unsaved(c)) { - log_info(c->log, "Killing opening daemon: Reconnected"); - channel_unsaved_close_conn(c, "Reconnected"); - } - } + peer_channels_cleanup_on_disconnect(peer); } struct custommsg_payload { diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4dfaaaeea337..0ceb9f4bdce4 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -136,6 +136,34 @@ void maybe_delete_peer(struct peer *peer) delete_peer(peer); } +void peer_channels_cleanup_on_disconnect(struct peer *peer) +{ + struct channel *c, **channels; + + /* Freeing channels can free peer, so gather first. */ + channels = tal_arr(tmpctx, struct channel *, 0); + list_for_each(&peer->channels, c, list) + tal_arr_expand(&channels, c); + + if (peer->uncommitted_channel) { + /* Frees peer if no channels */ + kill_uncommitted_channel(peer->uncommitted_channel, + "Disconnected"); + } else if (tal_count(channels) == 0) + /* Was completely idle. */ + tal_free(peer); + + for (size_t i = 0; i < tal_count(channels); i++) { + c = channels[i]; + if (channel_active(c)) { + channel_cleanup_commands(c, "Disconnected"); + channel_fail_reconnect(c, "Disconnected"); + } else if (channel_unsaved(c)) { + channel_unsaved_close_conn(c, "Disconnected"); + } + } +} + struct peer *find_peer_by_dbid(struct lightningd *ld, u64 dbid) { struct peer *p; @@ -1427,10 +1455,8 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) if (p) { log_peer_debug(ld->log, &id, "peer_disconnect_done"); p->is_connected = false; - /* If we only cared about peer because of connectd, free it. */ - if (list_empty(&p->channels) && !p->uncommitted_channel) { - tal_free(p); - } + + peer_channels_cleanup_on_disconnect(p); } /* Fire off plugin notifications */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index ae4dd2ecd5cb..c76d34d0acdd 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -73,6 +73,8 @@ struct peer *peer_from_json(struct lightningd *ld, void peer_connected(struct lightningd *ld, const u8 *msg); void peer_disconnect_done(struct lightningd *ld, const u8 *msg); void peer_active(struct lightningd *ld, const u8 *msg, int peer_fd); +/* May delete peer! */ +void peer_channels_cleanup_on_disconnect(struct peer *peer); /* Could be configurable. */ #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL From d58e6fa20b2a9ecc7beeea6a441e3281086de367 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1026/1530] lightningd: don't tell connectd to disconnect peer if it told us. We allow connectd to tell us a peer has gone away, but now we need to make sure we don't double-spiderman and tell it to disconnect peer. This is particularly harmful on reconnect: it (will soon) tell us the old connection is gone, ready to tell us the new peer has connected. We would tell it to disconnect the peer, which throws away the new connection! Signed-off-by: Rusty Russell --- lightningd/connect_control.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 842fe8a28ca8..b48a2310fb89 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -668,8 +668,11 @@ void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer) return; } - subd_send_msg(ld->connectd, - take(towire_connectd_discard_peer(NULL, &peer->id))); + /* If connectd was the one who told us to cleanup peer, don't + * tell it to discard again: it might have reconnected! */ + if (peer->is_connected) + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, &peer->id))); } static struct command_result *json_sendcustommsg(struct command *cmd, From e856accb7d919a89054a43b662b8198ea4cb33e1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1027/1530] connectd: send cleanup messages however peer is freed. This lets us tal_free() it wherever we want, rather than always freeing via peer_discard. Signed-off-by: Rusty Russell --- connectd/connectd.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index df21e7a45407..aadfc055d8d1 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -287,6 +287,15 @@ static struct io_plan *peer_reconnected(struct io_conn *conn, void destroy_peer(struct peer *peer) { peer_htable_del(&peer->daemon->peers, peer); + + /* Tell gossipd to stop asking this peer gossip queries */ + daemon_conn_send(peer->daemon->gossipd, + take(towire_gossipd_peer_gone(NULL, &peer->id))); + + /* Tell lightningd it's really disconnected */ + daemon_conn_send(peer->daemon->master, + take(towire_connectd_peer_disconnect_done(NULL, + &peer->id))); } /*~ This is where we create a new peer. */ @@ -1936,14 +1945,6 @@ void peer_conn_closed(struct peer *peer) status_peer_debug(&peer->id, "peer_conn_closed"); - /* Tell gossipd to stop asking this peer gossip queries */ - daemon_conn_send(peer->daemon->gossipd, - take(towire_gossipd_peer_gone(NULL, &peer->id))); - - /* Tell lightningd it's really disconnected */ - daemon_conn_send(peer->daemon->master, - take(towire_connectd_peer_disconnect_done(NULL, - &peer->id))); /* Wake up in case there's a reconnecting peer waiting in io_wait. */ io_wake(peer); From 8678c5efb337ef98008a8523999a60c7762e8a4a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1028/1530] connectd: release peer soon as lightingd tells us. Now we have separate peer draining logic, we can simply use it when connectd tells us to release the peer, without waiting. (We could simply free the peer, but that's a bit rude, as messages can get lost). This removes various complex flags and logic we had before. Signed-off-by: Rusty Russell Changelog-Fixed: `connectd`: various crashes and issues fixed by simplification and rewrite. --- connectd/connectd.c | 47 +++++++---------------------------------- connectd/connectd.h | 12 +---------- connectd/multiplex.c | 50 +++++++------------------------------------- connectd/multiplex.h | 3 --- 4 files changed, 16 insertions(+), 96 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index aadfc055d8d1..107f30e6ef2c 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -315,8 +315,6 @@ static struct peer *new_peer(struct daemon *daemon, peer->peer_in = NULL; peer->sent_to_peer = NULL; peer->urgent = false; - peer->ready_to_die = false; - peer->active = false; peer->draining = false; peer->peer_outq = msg_queue_new(peer, false); peer->last_recv_time = time_now(); @@ -1832,17 +1830,12 @@ static void try_connect_peer(struct daemon *daemon, /* Already existing? */ existing = peer_htable_get(&daemon->peers, id); if (existing) { - /* If it's exiting now, we've raced: reconnect after */ - if ((tal_count(existing->subds) != 0 || !existing->active) - && existing->to_peer - && !existing->ready_to_die) { - /* Tell it it's already connected so it doesn't - * wait forever. */ - daemon_conn_send(daemon->master, - take(towire_connectd_peer_already_connected - (NULL, id))); - return; - } + /* FIXME: Tell it it's already connected so it doesn't + * wait forever. */ + daemon_conn_send(daemon->master, + take(towire_connectd_peer_already_connected + (NULL, id))); + return; } /* If we're trying to connect it right now, that's OK. */ @@ -1935,30 +1928,6 @@ static void connect_to_peer(struct daemon *daemon, const u8 *msg) try_connect_peer(daemon, &id, seconds_waited, addrs, addrhint); } -void peer_conn_closed(struct peer *peer) -{ - struct connecting *connect = find_connecting(peer->daemon, &peer->id); - - /* These should be closed already! */ - assert(!peer->to_peer); - assert(peer->ready_to_die || !peer->active); - - status_peer_debug(&peer->id, "peer_conn_closed"); - - /* Wake up in case there's a reconnecting peer waiting in io_wait. */ - io_wake(peer); - - /* 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. That's why we have - * a destructor attached to peer (called destroy_peer by - * convention). */ - tal_free(peer); - - /* If we wanted to connect to it, but found it was exiting, try again */ - if (connect && !connect->conn) - try_connect_one_addr(connect); -} - /* lightningd tells us a peer should be disconnected. */ static void peer_discard(struct daemon *daemon, const u8 *msg) { @@ -1974,9 +1943,7 @@ static void peer_discard(struct daemon *daemon, const u8 *msg) if (!peer) return; status_peer_debug(&id, "disconnect"); - - /* When it's finished, it will call peer_conn_closed() */ - close_peer_conn(peer); + tal_free(peer); } /* lightningd tells us to send a msg and disconnect. */ diff --git a/connectd/connectd.h b/connectd/connectd.h index 43e7b242a828..bdab8d1e9808 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -59,13 +59,6 @@ struct peer { /* Connections to the subdaemons */ struct subd **subds; - /* Set once lightningd says it's OK to close (subd tells it - * it's done). */ - bool ready_to_die; - - /* Has this ever been active? (i.e. ever had a subd attached?) */ - bool active; - /* When socket has Nagle overridden */ bool urgent; @@ -220,10 +213,7 @@ struct io_plan *peer_connected(struct io_conn *conn, bool incoming, bool retrying); -/* Called when peer->peer_conn is finally freed */ -void peer_conn_closed(struct peer *peer); - -/* Removes peer from hash table */ +/* Removes peer from hash table, tells gossipd and lightningd. */ void destroy_peer(struct peer *peer); #endif /* LIGHTNING_CONNECTD_CONNECTD_H */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 9a88724b57b6..f7fa7d0b3e76 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -538,9 +538,6 @@ static struct subd *activate_subd(struct peer *peer, u16 t, *tp; struct subd *subd; - /* If it wasn't active before, it is now! */ - peer->active = true; - subd = multiplex_subd_setup(peer, channel_id, &fd_for_subd); if (!subd) return NULL; @@ -968,14 +965,6 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, return io_sock_shutdown(peer_conn); } - /* We close once subds are all closed; or if we're not - active, when told to die. */ - if ((peer->active || peer->ready_to_die) - && tal_count(peer->subds) == 0) { - set_closing_timer(peer, peer_conn); - return io_sock_shutdown(peer_conn); - } - /* If they want us to send gossip, do so now. */ msg = maybe_from_gossip_store(NULL, peer); if (!msg) { @@ -1078,7 +1067,7 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, peer->last_recv_time = time_now(); /* Don't process packets while we're closing */ - if (peer->ready_to_die) + if (peer->draining) return read_hdr_from_peer(peer_conn, peer); /* If we swallow this, just try again. */ @@ -1180,37 +1169,14 @@ static void destroy_subd(struct subd *subd) struct peer *peer = subd->peer; size_t pos; - status_peer_debug(&peer->id, - "destroy_subd: %zu subds, to_peer conn %p, read_to_die = %u", - tal_count(peer->subds), peer->to_peer, - peer->ready_to_die); for (pos = 0; peer->subds[pos] != subd; pos++) assert(pos < tal_count(peer->subds)); tal_arr_remove(&peer->subds, pos); - /* Make sure we try to keep reading from peer, so we know if - * it hangs up! */ + /* Make sure we try to keep reading from peer (might + * have been waiting for write_to_subd) */ io_wake(&peer->peer_in); - - /* If no peer, finally time to close */ - if (!peer->to_peer && peer->ready_to_die) - peer_conn_closed(peer); -} - -void close_peer_conn(struct peer *peer) -{ - /* Make write_to_peer do flush after writing */ - peer->ready_to_die = true; - - /* Already dead? */ - if (tal_count(peer->subds) == 0 && !peer->to_peer) { - peer_conn_closed(peer); - return; - } - - /* In case it's not currently writing, wake write_to_peer */ - msg_wake(peer->peer_outq); } static struct subd *multiplex_subd_setup(struct peer *peer, @@ -1250,22 +1216,22 @@ static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) assert(peer->to_peer == peer_conn); peer->to_peer = NULL; - /* Flush internal connections if any. */ + /* Flush internal connections if any: last one out will free peer. */ if (tal_count(peer->subds) != 0) { for (size_t i = 0; i < tal_count(peer->subds); i++) msg_wake(peer->subds[i]->outq); return; } - /* If lightningd says we're ready, or we were never had a subd, finish */ - if (peer->ready_to_die || !peer->active) - peer_conn_closed(peer); + /* We never had any subds? Free peer (might already be being freed, + * as it's our parent, but that's allowed by tal). */ + tal_free(peer); } struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, struct peer *peer) { - /*~ If conn closes, we close the subd connections and wait for + /*~ If conn closes, we drain the subd connections and wait for * lightningd to tell us to close with the peer */ tal_add_destructor2(peer_conn, destroy_peer_conn, peer); diff --git a/connectd/multiplex.h b/connectd/multiplex.h index f389d88da77f..ce2ed7b02b7e 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -26,9 +26,6 @@ void setup_peer_gossip_store(struct peer *peer, const struct feature_set *our_features, const u8 *their_features); -/* Start the process of flushing and closing the peer_conn */ -void close_peer_conn(struct peer *peer); - /* When lightningd says to send a ping */ void send_manual_ping(struct daemon *daemon, const u8 *msg); From 7b0c11efb40eef5b172b30e61e512cef4f7d1e8b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1029/1530] connectd: don't let peer close take forever. Sending any pending messages to peer before hanging up is a courtesy: give it 5 seconds before simply closing. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index f7fa7d0b3e76..9976c292bfc9 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -81,19 +81,33 @@ static struct subd *find_subd(struct peer *peer, return NULL; } +/* We try to send the final messages, but if buffer is full and they're + * not reading, we have to give up. */ +static void close_timeout(struct peer *peer) +{ + /* BROKEN means we'll trigger CI if we see it, though it's possible */ + status_peer_broken(&peer->id, "Peer did not close, forcing close"); + tal_free(peer); +} + /* We just want to send these messages out to the peer's connection, * then close. We consider the peer dead to us (can be freed). */ static void drain_peer(struct peer *peer) { assert(!peer->draining); - /* FIXME: Don't drain forever! */ + /* This is a 5-second leak, worst case! */ notleak(peer); /* We no longer want subds feeding us more messages! */ peer->subds = tal_free(peer->subds); peer->draining = true; + /* You have 5 seconds to drain... */ + notleak(new_reltimer(&peer->daemon->timers, + peer, time_from_sec(5), + close_timeout, peer)); + /* Clean peer from hashtable; we no longer exist. */ destroy_peer(peer); tal_del_destructor(peer, destroy_peer); @@ -929,22 +943,6 @@ static void maybe_update_channelid(struct subd *subd, const u8 *msg) } } -static void close_timeout(struct peer *peer) -{ - /* BROKEN means we'll trigger CI if we see it, though it's possible */ - status_peer_broken(&peer->id, "Peer did not close, forcing close"); - tal_free(peer->to_peer); -} - -/* Close this in 5 seconds if it doesn't do so by itself. */ -static void set_closing_timer(struct peer *peer, - struct io_conn *peer_conn) -{ - notleak(new_reltimer(&peer->daemon->timers, - peer_conn, time_from_sec(5), - close_timeout, peer)); -} - static struct io_plan *write_to_peer(struct io_conn *peer_conn, struct peer *peer) { @@ -960,10 +958,8 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, /* Still nothing to send? */ if (!msg) { /* Draining? We're done. */ - if (peer->draining) { - set_closing_timer(peer, peer_conn); + if (peer->draining) return io_sock_shutdown(peer_conn); - } /* If they want us to send gossip, do so now. */ msg = maybe_from_gossip_store(NULL, peer); From 9b6c97437e6426eada7be51b189c35035ac2557c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1030/1530] connectd: remove reconnection logic. We don't have to put aside a peer which is reconnecting and wait for lightningd to remove the old peer, we can now simply free the old and add the new. Fixes: #5240 Signed-off-by: Rusty Russell --- connectd/connectd.c | 91 ++------------------------------ connectd/connectd.h | 3 +- connectd/connectd_wire.csv | 4 -- connectd/peer_exchange_initmsg.c | 3 +- lightningd/connect_control.c | 19 ------- 5 files changed, 6 insertions(+), 114 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 107f30e6ef2c..b5b27e2d40ee 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -93,18 +93,6 @@ struct connecting { u32 seconds_waited; }; -/*~ This is an ad-hoc marshalling structure where we store arguments so we - * can call peer_connected again. */ -struct peer_reconnected { - struct daemon *daemon; - struct node_id id; - struct wireaddr_internal addr; - const struct wireaddr *remote_addr; - struct crypto_state cs; - const u8 *their_features; - bool incoming; -}; - /*~ C programs should generally be written bottom-to-top, with the root * function at the bottom, and functions it calls above it. That avoids * us having to pre-declare functions; but in the case of mutual recursion @@ -223,66 +211,6 @@ static void peer_connected_in(struct daemon *daemon, tal_free(connect); } -/*~ For simplicity, lightningd only ever deals with a single connection per - * peer. So if we already know about a peer, we tell lightning to disconnect - * the old one and retry once it does. */ -static struct io_plan *retry_peer_connected(struct io_conn *conn, - struct peer_reconnected *pr) -{ - /*~ As you can see, we've had issues with this code before :( */ - status_peer_debug(&pr->id, "processing now old peer gone"); - - /* If this fails (still waiting), pr will be freed, so reparent onto - * tmpctx so it gets freed either way. */ - tal_steal(tmpctx, pr); - - /*~ Usually the pattern is to return this directly. */ - return peer_connected(conn, pr->daemon, &pr->id, &pr->addr, - pr->remote_addr, - &pr->cs, take(pr->their_features), pr->incoming, - true); -} - -/*~ If we already know about this peer, we tell lightningd and it disconnects - * the old one. We wait until it tells us that's happened. */ -static struct io_plan *peer_reconnected(struct io_conn *conn, - struct daemon *daemon, - const struct node_id *id, - const struct wireaddr_internal *addr, - const struct wireaddr *remote_addr, - const struct crypto_state *cs, - const u8 *their_features TAKES, - bool incoming) -{ - u8 *msg; - struct peer_reconnected *pr; - - status_peer_debug(id, "reconnect"); - - /* Tell master to kill it: will send peer_disconnect */ - msg = towire_connectd_reconnected(NULL, id); - daemon_conn_send(daemon->master, take(msg)); - - /* Save arguments for next time. */ - pr = tal(conn, struct peer_reconnected); - pr->daemon = daemon; - pr->id = *id; - pr->cs = *cs; - pr->addr = *addr; - pr->remote_addr = tal_dup_or_null(pr, struct wireaddr, remote_addr); - pr->incoming = incoming; - - /*~ Note that tal_dup_talarr() will do handle the take() of features - * (turning it into a simply tal_steal() in those cases). */ - pr->their_features = tal_dup_talarr(pr, u8, their_features); - - /*~ ccan/io supports waiting on an address: in this case, the key in - * the peer set. When someone calls `io_wake()` on that address, it - * will call retry_peer_connected above. */ - return io_wait(conn, peer_htable_get(&daemon->peers, id), - retry_peer_connected, pr); -} - /*~ When we free a peer, we remove it from the daemon's hashtable */ void destroy_peer(struct peer *peer) { @@ -343,8 +271,7 @@ struct io_plan *peer_connected(struct io_conn *conn, const struct wireaddr *remote_addr, struct crypto_state *cs, const u8 *their_features TAKES, - bool incoming, - bool retrying) + bool incoming) { u8 *msg; struct peer *peer; @@ -353,19 +280,10 @@ struct io_plan *peer_connected(struct io_conn *conn, int subd_fd; bool option_gossip_queries; + /* We remove any previous connection, on the assumption it's dead */ peer = peer_htable_get(&daemon->peers, id); - if (peer) { - /* If we were already retrying, we only get one chance: there - * can be multiple reconnections, and we must not keep around - * stale ones */ - if (retrying) { - if (taken(their_features)) - tal_free(their_features); - return io_close(conn); - } - return peer_reconnected(conn, daemon, id, addr, remote_addr, cs, - their_features, incoming); - } + if (peer) + tal_free(peer); /* We promised we'd take it by marking it TAKEN above; prepare to free it. */ if (taken(their_features)) @@ -2051,7 +1969,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_PEER_CONNECTED: case WIRE_CONNECTD_PEER_ALREADY_CONNECTED: case WIRE_CONNECTD_PEER_ACTIVE: - case WIRE_CONNECTD_RECONNECTED: case WIRE_CONNECTD_CONNECT_FAILED: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: case WIRE_CONNECTD_PING_REPLY: diff --git a/connectd/connectd.h b/connectd/connectd.h index bdab8d1e9808..e77f78738172 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -210,8 +210,7 @@ struct io_plan *peer_connected(struct io_conn *conn, const struct wireaddr *remote_addr, struct crypto_state *cs, const u8 *their_features TAKES, - bool incoming, - bool retrying); + bool incoming); /* Removes peer from hash table, tells gossipd and lightningd. */ void destroy_peer(struct peer *peer); diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 91d0fdb6d821..c7859ccb59f3 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -44,10 +44,6 @@ msgdata,connectd_activate,listen,bool, msgtype,connectd_activate_reply,2125 msgdata,connectd_activate_reply,failmsg,?wirestring, -# connectd->master: disconnect this peer please (due to reconnect). -msgtype,connectd_reconnected,2112 -msgdata,connectd_reconnected,id,node_id, - # Master -> connectd: connect to a peer. msgtype,connectd_connect_to_peer,2001 msgdata,connectd_connect_to_peer,id,node_id, diff --git a/connectd/peer_exchange_initmsg.c b/connectd/peer_exchange_initmsg.c index c9009a007714..fa97998382c6 100644 --- a/connectd/peer_exchange_initmsg.c +++ b/connectd/peer_exchange_initmsg.c @@ -137,8 +137,7 @@ static struct io_plan *peer_init_received(struct io_conn *conn, remote_addr, &peer->cs, take(features), - peer->incoming, - false); + peer->incoming); } static struct io_plan *peer_init_hdr_received(struct io_conn *conn, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index b48a2310fb89..1da9b0d2f9ab 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -386,21 +386,6 @@ static void peer_already_connected(struct lightningd *ld, const u8 *msg) &peer->addr); } -static void peer_please_disconnect(struct lightningd *ld, const u8 *msg) -{ - struct node_id id; - struct peer *peer; - - if (!fromwire_connectd_reconnected(msg, &id)) - fatal("Bad msg %s from connectd", tal_hex(tmpctx, msg)); - - peer = peer_by_id(ld, &id); - if (!peer) - return; - - peer_channels_cleanup_on_disconnect(peer); -} - struct custommsg_payload { struct node_id peer_id; u8 *msg; @@ -482,10 +467,6 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_PING_REPLY: break; - case WIRE_CONNECTD_RECONNECTED: - peer_please_disconnect(connectd->ld, msg); - break; - case WIRE_CONNECTD_PEER_CONNECTED: peer_connected(connectd->ld, msg); break; From 40145e619ba8b6afb168d16af54eadc2ebd8c4e6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1031/1530] connectd: remove the redundant "already connected" logic. It should now be reliable, so we don't need this. Signed-off-by: Rusty Russell --- connectd/connectd.c | 18 ++++-------------- connectd/connectd_wire.csv | 4 ---- lightningd/connect_control.c | 34 +++++++++++++--------------------- 3 files changed, 17 insertions(+), 39 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index b5b27e2d40ee..510fe67e53e4 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1743,18 +1743,10 @@ static void try_connect_peer(struct daemon *daemon, struct wireaddr_internal *addrs; bool use_proxy = daemon->always_use_proxy; struct connecting *connect; - struct peer *existing; - - /* Already existing? */ - existing = peer_htable_get(&daemon->peers, id); - if (existing) { - /* FIXME: Tell it it's already connected so it doesn't - * wait forever. */ - daemon_conn_send(daemon->master, - take(towire_connectd_peer_already_connected - (NULL, id))); + + /* Already existing? Must have crossed over, it'll know soon. */ + if (peer_htable_get(&daemon->peers, id)) return; - } /* If we're trying to connect it right now, that's OK. */ if ((connect = find_connecting(daemon, id))) { @@ -1826,8 +1818,7 @@ static void try_connect_peer(struct daemon *daemon, tal_add_destructor(connect, destroy_connecting); /* Now we kick it off by recursively trying connect->addrs[connect->addrnum] */ - if (!existing) - try_connect_one_addr(connect); + try_connect_one_addr(connect); } /* lightningd tells us to connect to a peer by id, with optional addr hint. */ @@ -1967,7 +1958,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_INIT_REPLY: case WIRE_CONNECTD_ACTIVATE_REPLY: case WIRE_CONNECTD_PEER_CONNECTED: - case WIRE_CONNECTD_PEER_ALREADY_CONNECTED: case WIRE_CONNECTD_PEER_ACTIVE: case WIRE_CONNECTD_CONNECT_FAILED: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index c7859ccb59f3..740c86a30e28 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -95,10 +95,6 @@ msgdata,connectd_peer_final_msg,id,node_id, msgdata,connectd_peer_final_msg,len,u16, msgdata,connectd_peer_final_msg,msg,u8,len -# connectd->master: You said to connect, but we already were. -msgtype,connectd_peer_already_connected,2007 -msgdata,connectd_peer_already_connected,id,node_id, - # master -> connectd: do you have a memleak? msgtype,connectd_dev_memleak,2033 diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 1da9b0d2f9ab..d3e80a0a464e 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -183,6 +183,7 @@ static struct command_result *json_connect(struct command *cmd, struct wireaddr_internal *addr; const char *err_msg; struct id_and_addr id_addr; + struct peer *peer; id_addr.host = NULL; id_addr.port = NULL; @@ -214,7 +215,18 @@ static struct command_result *json_connect(struct command *cmd, "Can't specify port without host"); } - try_connect(cmd, cmd->ld, &id_addr.id, 0, addr); + /* If we know about peer, see if it's already connected. */ + peer = peer_by_id(cmd->ld, &id_addr.id); + if (peer && peer->is_connected) { + log_debug(cmd->ld->log, "Already connected via %s", + type_to_string(tmpctx, struct wireaddr_internal, + &peer->addr)); + return connect_cmd_succeed(cmd, peer, + peer->connected_incoming, + &peer->addr); + } + + try_connect(cmd, cmd->ld, &id_addr.id, 0, addr); /* Leave this here for peer_connected or connect_failed. */ new_connect(cmd->ld, &id_addr.id, cmd); @@ -371,21 +383,6 @@ void connect_succeeded(struct lightningd *ld, const struct peer *peer, } } -static void peer_already_connected(struct lightningd *ld, const u8 *msg) -{ - struct node_id id; - struct peer *peer; - - if (!fromwire_connectd_peer_already_connected(msg, &id)) - fatal("Bad msg %s from connectd", tal_hex(tmpctx, msg)); - - peer = peer_by_id(ld, &id); - if (peer) - connect_succeeded(ld, peer, - peer->connected_incoming, - &peer->addr); -} - struct custommsg_payload { struct node_id peer_id; u8 *msg; @@ -481,10 +478,6 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd peer_disconnect_done(connectd->ld, msg); break; - case WIRE_CONNECTD_PEER_ALREADY_CONNECTED: - peer_already_connected(connectd->ld, msg); - break; - case WIRE_CONNECTD_CONNECT_FAILED: connect_failed(connectd->ld, msg); break; @@ -508,7 +501,6 @@ static void connect_init_done(struct subd *connectd, struct lightningd *ld = connectd->ld; char *errmsg; - log_debug(connectd->log, "connectd_init_done"); if (!fromwire_connectd_init_reply(ld, reply, &ld->binding, &ld->announceable, From 8e1d5c19d681a6de4ba94a4da61585a0096aa94d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1032/1530] pytest: test to reproduce "channeld: sent ERROR bad reestablish revocation_number: 0 vs 3" It's caused by a reconnection race: we hold the new incoming connection while we ask lightningd to kill the old connection. But under some circumstances we leave the new incoming hanging (with, in this case, old reestablish messages unread!) and another connection comes in. Then, later we service the long-gone "incoming" connection, channeld reads the ancient reestablish message and gets upset. This test used to hang, but now we've fixed reconnection races it is fine. Signed-off-by: Rusty Russell --- tests/test_connection.py | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 198556242439..47239ce05ed5 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4031,3 +4031,48 @@ def test_multichan(node_factory, executor, bitcoind): inv = l3.rpc.invoice(100000000, "invoice4", "invoice4") l1.rpc.pay(inv['bolt11']) + + +@pytest.mark.xfail(reason="race in reconnect logic") +@pytest.mark.developer("dev-no-reconnect required") +def test_mutual_reconnect_race(node_factory, executor, bitcoind): + """Test simultaneous reconnect between nodes""" + l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True, + 'dev-no-reconnect': None}) + + def send_many_payments(): + for i in range(20): + time.sleep(0.5) + inv = l2.rpc.invoice(100, "label-" + str(i), "desc")['bolt11'] + try: + l1.rpc.pay(inv) + except RpcError: + pass + + # Send a heap of payments, while reconnecting... + fut = executor.submit(send_many_payments) + + for i in range(10): + try: + l1.rpc.disconnect(l2.info['id'], force=True) + except RpcError: + pass + time.sleep(1) + # Aim for both at once! + executor.submit(l1.rpc.connect, l2.info['id'], 'localhost', l2.port) + executor.submit(l2.rpc.connect, l1.info['id'], 'localhost', l1.port) + + # Wait for things to settle down, then make sure we're actually connected. + # Naively, you'd think we should be, but in fact, two connects which race + # can (do!) result in both disconnecting, thinking the other side is more + # recent. + time.sleep(1) + if not only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']: + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + # Now payments should finish! + fut.result(TIMEOUT) + + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']) + inv = l2.rpc.invoice(100000000, "invoice4", "invoice4") + l1.rpc.pay(inv['bolt11']) From 6a9a0912348fe073d0c66855637ab92d80187f85 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1033/1530] pytest: add another connection stress test, using multiple channels (bug #5254) This one actually triggers an assert() on my machine, so though it wasn't what I was looking for, let's include it: ``` lightning_connectd: connectd/connectd.c:1905: peer_conn_closed: Assertion `tal_count(peer->subds) == 0' failed. lightning_connectd: FATAL SIGNAL 6 (version v0.11.0.1-15-gc812595) 0x55b3e1e21302 send_backtrace common/daemon.c:33 0x55b3e1e213ac crashdump common/daemon.c:46 0x7f44292ff08f ??? /build/glibc-SzIz7B/glibc-2.31/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 0x7f44292ff00b __GI_raise ../sysdeps/unix/sysv/linux/raise.c:51 0x7f44292de858 __GI_abort /build/glibc-SzIz7B/glibc-2.31/stdlib/abort.c:79 0x7f44292de728 __assert_fail_base /build/glibc-SzIz7B/glibc-2.31/assert/assert.c:92 0x7f44292effd5 __GI___assert_fail /build/glibc-SzIz7B/glibc-2.31/assert/assert.c:101 0x55b3e1e125db peer_conn_closed connectd/connectd.c:1905 0x55b3e1e17b4f destroy_subd connectd/multiplex.c:1112 0x55b3e1e7fdf4 notify ccan/ccan/tal/tal.c:240 0x55b3e1e8030b del_tree ccan/ccan/tal/tal.c:402 0x55b3e1e8035d del_tree ccan/ccan/tal/tal.c:412 0x55b3e1e806a7 tal_free ccan/ccan/tal/tal.c:486 0x55b3e1e6ef59 io_close ccan/ccan/io/io.c:450 0x55b3e1e17429 write_to_subd connectd/multiplex.c:957 0x55b3e1e6e1a3 next_plan ccan/ccan/io/io.c:59 0x55b3e1e6eebc io_do_always ccan/ccan/io/io.c:435 0x55b3e1e70baa handle_always ccan/ccan/io/poll.c:304 0x55b3e1e70ea1 io_loop ccan/ccan/io/poll.c:385 0x55b3e1e12dd5 main connectd/connectd.c:2159 0x7f44292e0082 __libc_start_main ../csu/libc-start.c:308 0x55b3e1e0885d ??? ???:0 0xffffffffffffffff ??? ???:0 ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 47239ce05ed5..f20494cae8a5 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3796,6 +3796,46 @@ def test_htlc_failed_noclose(node_factory): assert l1.rpc.getpeer(l2.info['id'])['connected'] +@pytest.mark.openchannel('v2') +@pytest.mark.developer("dev-no-reconnect required") +def test_multichan_stress(node_factory, executor, bitcoind): + """Test multiple channels between same nodes""" + l1, l2, l3 = node_factory.line_graph(3, opts={'may_reconnect': True, + 'dev-no-reconnect': None}) + + # Now fund *second* channel l2->l3 (slightly larger) + bitcoind.rpc.sendtoaddress(l2.rpc.newaddr()['bech32'], 0.1) + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l2]) + l2.rpc.fundchannel(l3.info['id'], '0.01001btc') + assert(len(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) == 2) + assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2) + + # Make sure gossip works. + bitcoind.generate_block(6, wait_for_mempool=1) + wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 2) + + def send_many_payments(): + for i in range(30): + inv = l3.rpc.invoice(100, "label-" + str(i), "desc")['bolt11'] + try: + l1.rpc.pay(inv) + except RpcError: + pass + + # Send a heap of payments, while reconnecting... + fut = executor.submit(send_many_payments) + + for i in range(10): + l3.rpc.disconnect(l2.info['id'], force=True) + l3.rpc.connect(l2.info['id'], 'localhost', l2.port) + fut.result(TIMEOUT) + + wait_for(lambda: only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['connected']) + inv = l3.rpc.invoice(50000000, "invoice4", "invoice4") + l1.rpc.pay(inv['bolt11']) + + @pytest.mark.developer("dev-no-reconnect required") def test_old_feerate(node_factory): """Test retransmission of old, now-unacceptable, feerate""" From ab0e5d30ee73e389f12fcdcdcb003bfe18fda07a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1034/1530] connectd: don't io_halfclose() We don't io_halfclose() the other side, we io_sock_shutdown(), which can leave both sides unset: ``` lightningd-2: 2022-06-07T11:00:05.053Z **BROKEN** connectd: FATAL SIGNAL 6 (version 57e1af2) lightningd-2: 2022-06-07T11:00:05.053Z **BROKEN** connectd: backtrace: common/daemon.c:38 (send_backtrace) 0x563b9b603af7 lightningd-2: 2022-06-07T11:00:05.053Z **BROKEN** connectd: backtrace: common/daemon.c:46 (crashdump) 0x563b9b603b4b lightningd-2: 2022-06-07T11:00:05.053Z **BROKEN** connectd: backtrace: /build/glibc-SzIz7B/glibc-2.31/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 ((null)) 0x7fe6e8d4f08f lightningd-2: 2022-06-07T11:00:05.053Z **BROKEN** connectd: backtrace: ../sysdeps/unix/sysv/linux/raise.c:51 (__GI_raise) 0x7fe6e8d4f00b lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: /build/glibc-SzIz7B/glibc-2.31/stdlib/abort.c:79 (__GI_abort) 0x7fe6e8d2e858 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: /build/glibc-SzIz7B/glibc-2.31/assert/assert.c:92 (__assert_fail_base) 0x7fe6e8d2e728 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: /build/glibc-SzIz7B/glibc-2.31/assert/assert.c:101 (__GI___assert_fail) 0x7fe6e8d3ffd5 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: ccan/ccan/io/io.c:65 (next_plan) 0x563b9b64fd7e lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: ccan/ccan/io/io.c:407 (do_plan) 0x563b9b6508f0 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: ccan/ccan/io/io.c:423 (io_ready) 0x563b9b650984 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: ccan/ccan/io/poll.c:453 (io_loop) 0x563b9b652c25 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: connectd/connectd.c:2037 (main) 0x563b9b5f5793 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: ../csu/libc-start.c:308 (__libc_start_main) 0x7fe6e8d30082 lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: (null):0 ((null)) 0x563b9b5ebf6d lightningd-2: 2022-06-07T11:00:05.054Z **BROKEN** connectd: backtrace: (null):0 ((null)) 0xffffffffffffffff ``` Signed-off-by: Rusty Russell --- connectd/multiplex.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 9976c292bfc9..78bdbacce027 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -1133,10 +1133,6 @@ static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, { assert(peer->to_peer == peer_conn); - /* If we're draining, ignore all incoming. */ - if (peer->draining) - return io_halfclose(peer_conn); - /* BOLT #8: * * ### Receiving and Decrypting Messages From a12e2209ff382286adf17fa59cb9f8a87b784b2f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:30 +0930 Subject: [PATCH 1035/1530] dualopend: fix memleak report. Not an important one, but memleak detection got upset: ``` 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: MEMLEAK: 0x55dd9797bb68 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: label=openingd/dualopend_wiregen.c:767:struct lease_rates 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: backtrace: 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: ccan/ccan/tal/tal.c:442 (tal_alloc_) 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: openingd/dualopend_wiregen.c:767 (fromwire_dualopend_opener_init) 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: openingd/dualopend.c:2671 (opener_start) 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: openingd/dualopend.c:3649 (handle_master_in) 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: openingd/dualopend.c:3973 (main) 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: ../csu/libc-start.c:308 (__libc_start_main) 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: parents: 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#2: openingd/dualopend.c:3796:struct state ``` Signed-off-by: Rusty Russell --- openingd/dualopend.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 14b1b13bade8..e754b800fd44 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -2914,6 +2914,8 @@ static void opener_start(struct state *state, u8 *msg) tx_state->lease_chan_max_ppt = rates->channel_fee_max_proportional_thousandths; } + /* Keep memleak detector happy! */ + tal_free(expected_rates); /* Check that total funding doesn't overflow */ if (!amount_sat_add(&total, tx_state->opener_funding, From 571f0fad1b5f4fd18385db2b2d3c0319764e9d14 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:31 +0930 Subject: [PATCH 1036/1530] lightningd: remove delay on succeeding connect. We used to not return from "connect" until we had connected all the subds, which introduced more races if something went wrong. Remove this workaround, since we're going to rework this logic entirely. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 0ceb9f4bdce4..ec412380c7bb 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1170,20 +1170,6 @@ REGISTER_PLUGIN_HOOK(peer_connected, peer_connected_serialize, struct peer_connected_hook_payload *); -/* Returns true if we're still waiting for subds for active channels */ -static bool peer_subds_pending(const struct peer *peer) -{ - struct channel *channel; - - list_for_each(&peer->channels, channel, list) { - if (!channel_active(channel)) - continue; - if (!channel->owner) - return true; - } - return false; -} - /* Connectd tells us a peer has connected: it never hands us duplicates, since * it holds them until we say peer_disconnected. */ void peer_connected(struct lightningd *ld, const u8 *msg) @@ -1222,11 +1208,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) tal_steal(peer, hook_payload); hook_payload->peer = peer; - /* Complete any outstanding connect commands: as a hack, we delay here if we - * are going to make them active (so when connect returns, the channels are ready). - * So we also wake these up if the connection dies before that! */ - if (!peer_subds_pending(peer)) - connect_succeeded(ld, peer, hook_payload->incoming, &hook_payload->addr); + connect_succeeded(ld, peer, hook_payload->incoming, &hook_payload->addr); /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { @@ -1300,13 +1282,13 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) && channel->open_attempt->open_msg) { if (peer_start_dualopend(peer, peer_fd, channel)) subd_send_msg(channel->owner, channel->open_attempt->open_msg); - goto subd_setup_done; + return; } /* Fall through. */ case DUALOPEND_AWAITING_LOCKIN: assert(!channel->owner); peer_restart_dualopend(peer, peer_fd, channel); - goto subd_setup_done; + return; case CHANNELD_AWAITING_LOCKIN: case CHANNELD_NORMAL: case CHANNELD_SHUTTING_DOWN: @@ -1317,7 +1299,7 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) peer_fd, NULL, true, NULL); - goto subd_setup_done; + return; } abort(); } @@ -1418,13 +1400,6 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, error))); - return; - -subd_setup_done: - /* We deferred connect_succeeded to here, so subd would be ready once - * `connect` returns. */ - if (!peer_subds_pending(peer)) - connect_succeeded(ld, peer, peer->connected_incoming, &peer->addr); } struct disconnect_command { From eff53495dbfeeed35ed496e9043af78090a3c14c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:31 +0930 Subject: [PATCH 1037/1530] lightningd: make "is peer connected" a tristate. First, connectd tells us the peer has connected, and we call the connected hook, and if it says it's fine, we are actually connected and we fire off notifications. Of course, we could be disconnected while in the connected hook, and that would mean we tell people about a connection which is no longer current. Make this clear with a tristate: if we're not marked disconnected by the time the hooks finish, we're good. It also gives us a cleaner "connect" command return when we connected but disconnected before processing. Signed-off-by: Rusty Russell --- common/jsonrpc_errors.h | 1 + doc/lightning-connect.7.md | 4 ++ lightningd/connect_control.c | 67 +++++++++++++-------- lightningd/connect_control.h | 1 + lightningd/dual_open_control.c | 6 +- lightningd/opening_control.c | 12 ++-- lightningd/peer_control.c | 37 +++++++++--- lightningd/peer_control.h | 10 ++- lightningd/test/run-invoice-select-inchan.c | 3 + wallet/test/run-wallet.c | 3 + 10 files changed, 103 insertions(+), 41 deletions(-) diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index 47a56e69e8a8..d52229582f66 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -64,6 +64,7 @@ static const errcode_t FUNDING_STATE_INVALID = 312; /* `connect` errors */ static const errcode_t CONNECT_NO_KNOWN_ADDRESS = 400; static const errcode_t CONNECT_ALL_ADDRESSES_FAILED = 401; +static const errcode_t CONNECT_DISCONNECTED_DURING = 402; /* bitcoin-cli plugin errors */ #define BCLI_ERROR 400 diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index afd919a43ac2..5f8274924417 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -76,6 +76,10 @@ will contain details about the failures: { "code" : 401, "message" : "..." } +If the peer disconnected while we were connecting: + + { "code" : 402, "message" : "..." } + If the given parameters are wrong: { "code" : -32602, "message" : "..." } diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index d3e80a0a464e..b50d16d475b7 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -217,7 +217,7 @@ static struct command_result *json_connect(struct command *cmd, /* If we know about peer, see if it's already connected. */ peer = peer_by_id(cmd->ld, &id_addr.id); - if (peer && peer->is_connected) { + if (peer && peer->connected == PEER_CONNECTED) { log_debug(cmd->ld->log, "Already connected via %s", type_to_string(tmpctx, struct wireaddr_internal, &peer->addr)); @@ -228,7 +228,7 @@ static struct command_result *json_connect(struct command *cmd, try_connect(cmd, cmd->ld, &id_addr.id, 0, addr); - /* Leave this here for peer_connected or connect_failed. */ + /* Leave this here for peer_connected, connect_failed or peer_disconnect_done. */ new_connect(cmd->ld, &id_addr.id, cmd); return command_still_pending(cmd); } @@ -341,35 +341,53 @@ void try_reconnect(const tal_t *ctx, addrhint); } -static void connect_failed(struct lightningd *ld, const u8 *msg) +/* We were trying to connect, but they disconnected. */ +static void connect_failed(struct lightningd *ld, + const struct node_id *id, + errcode_t errcode, + const char *errmsg, + u32 seconds_to_delay, + const struct wireaddr_internal *addrhint) { - struct node_id id; - errcode_t errcode; - char *errmsg; - struct connect *c; - u32 seconds_to_delay; - struct wireaddr_internal *addrhint; struct peer *peer; - - if (!fromwire_connectd_connect_failed(tmpctx, msg, &id, &errcode, &errmsg, - &seconds_to_delay, &addrhint)) - fatal("Connect gave bad CONNECTD_CONNECT_FAILED message %s", - tal_hex(msg, msg)); + struct connect *c; /* We can have multiple connect commands: fail them all */ - while ((c = find_connect(ld, &id)) != NULL) { + while ((c = find_connect(ld, id)) != NULL) { /* They delete themselves from list */ was_pending(command_fail(c->cmd, errcode, "%s", errmsg)); } /* If we have an active channel, then reconnect. */ - peer = peer_by_id(ld, &id); + peer = peer_by_id(ld, id); if (peer) { if (peer_any_active_channel(peer, NULL)) try_reconnect(peer, peer, seconds_to_delay, addrhint); } } +void connect_failed_disconnect(struct lightningd *ld, const struct node_id *id) +{ + connect_failed(ld, id, CONNECT_DISCONNECTED_DURING, + "disconnected during connection", 1, NULL); +} + +static void handle_connect_failed(struct lightningd *ld, const u8 *msg) +{ + struct node_id id; + errcode_t errcode; + char *errmsg; + u32 seconds_to_delay; + struct wireaddr_internal *addrhint; + + if (!fromwire_connectd_connect_failed(tmpctx, msg, &id, &errcode, &errmsg, + &seconds_to_delay, &addrhint)) + fatal("Connect gave bad CONNECTD_CONNECT_FAILED message %s", + tal_hex(msg, msg)); + + connect_failed(ld, &id, errcode, errmsg, seconds_to_delay, addrhint); +} + void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr) @@ -479,7 +497,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd break; case WIRE_CONNECTD_CONNECT_FAILED: - connect_failed(connectd->ld, msg); + handle_connect_failed(connectd->ld, msg); break; case WIRE_CONNECTD_GOT_ONIONMSG_TO_US: @@ -635,15 +653,16 @@ void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer) if (channel_is_connected(channel)) return; - /* If shutting down, connectd no longer exists */ + /* If shutting down, connectd no longer exists. + * FIXME: Call peer_disconnect_done(), but nobody cares. */ if (!ld->connectd) { - peer->is_connected = false; + peer->connected = PEER_DISCONNECTED; return; } /* If connectd was the one who told us to cleanup peer, don't * tell it to discard again: it might have reconnected! */ - if (peer->is_connected) + if (peer->connected == PEER_CONNECTED) subd_send_msg(ld->connectd, take(towire_connectd_discard_peer(NULL, &peer->id))); } @@ -693,11 +712,11 @@ static struct command_result *json_sendcustommsg(struct command *cmd, type_to_string(cmd, struct node_id, dest)); } - if (!peer->is_connected) { + if (peer->connected != PEER_CONNECTED) return command_fail(cmd, JSONRPC2_INVALID_REQUEST, - "Peer is not connected: %s", - type_to_string(cmd, struct node_id, dest)); - } + "Peer is %s", + peer->connected == PEER_DISCONNECTED + ? "not connected" : "still connecting"); subd_send_msg(cmd->ld->connectd, take(towire_connectd_custommsg_out(cmd, dest, msg))); diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 748ecb8eb9d7..6e836478f077 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -20,6 +20,7 @@ void try_reconnect(const tal_t *ctx, void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr); +void connect_failed_disconnect(struct lightningd *ld, const struct node_id *id); /* Disconnect a peer (if no subds want to talk any more) */ void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index fd501641ae4e..fd33c85dda15 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -3128,9 +3128,11 @@ static struct command_result *json_queryrates(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } - if (!peer->is_connected) + if (peer->connected != PEER_CONNECTED) return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, - "Peer not connected"); + "Peer %s", + peer->connected == PEER_DISCONNECTED + ? "not connected" : "still connecting"); /* FIXME: This is wrong: we should always create a new channel? */ channel = peer_any_unsaved_channel(peer, NULL); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 8438155c5cf1..e2081e479b36 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -963,9 +963,11 @@ static struct command_result *json_fundchannel_complete(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } - if (!peer->is_connected) + if (peer->connected != PEER_CONNECTED) return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, - "Peer not connected"); + "Peer %s", + peer->connected == PEER_DISCONNECTED + ? "not connected" : "still connecting"); if (!peer->uncommitted_channel || !peer->uncommitted_channel->fc @@ -1136,9 +1138,11 @@ static struct command_result *json_fundchannel_start(struct command *cmd, return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); } - if (!peer->is_connected) + if (peer->connected != PEER_CONNECTED) return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, - "Peer not connected"); + "Peer %s", + peer->connected == PEER_DISCONNECTED + ? "not connected" : "still connecting"); temporary_channel_id(&tmp_channel_id); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index ec412380c7bb..93cfa072451a 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -99,7 +99,7 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, peer->their_features = NULL; list_head_init(&peer->channels); peer->direction = node_id_idx(&peer->ld->id, &peer->id); - peer->is_connected = false; + peer->connected = PEER_DISCONNECTED; #if DEVELOPER peer->ignore_htlcs = false; #endif @@ -1020,8 +1020,10 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa * subd). */ tal_steal(tmpctx, payload); - /* Notify anyone who cares */ - notify_connect(ld, &peer->id, payload->incoming, &addr); + /* If we disconnected in the meantime, forget about it. + * (disconnect will have failed any connect commands). */ + if (peer->connected == PEER_DISCONNECTED) + return; /* Check for specific errors of a hook */ if (payload->error) { @@ -1029,6 +1031,16 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa goto send_error; } + /* Now we finally consider ourselves connected! */ + assert(peer->connected == PEER_CONNECTING); + peer->connected = PEER_CONNECTED; + + /* Succeed any connect() commands */ + connect_succeeded(ld, peer, payload->incoming, &payload->addr); + + /* Notify anyone who cares */ + notify_connect(ld, &peer->id, payload->incoming, &addr); + list_for_each(&peer->channels, channel, list) { #if DEVELOPER if (dev_disconnect_permanent(ld)) { @@ -1196,7 +1208,11 @@ void peer_connected(struct lightningd *ld, const u8 *msg) if (!peer) peer = new_peer(ld, 0, &id, &hook_payload->addr, hook_payload->incoming); - peer->is_connected = true; + + /* We mark peer in "connecting" state until hooks have passed. */ + assert(peer->connected == PEER_DISCONNECTED); + peer->connected = PEER_CONNECTING; + /* Update peer address and direction */ peer->addr = hook_payload->addr; peer->connected_incoming = hook_payload->incoming; @@ -1208,8 +1224,6 @@ void peer_connected(struct lightningd *ld, const u8 *msg) tal_steal(peer, hook_payload); hook_payload->peer = peer; - connect_succeeded(ld, peer, hook_payload->incoming, &hook_payload->addr); - /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { log_peer_debug(ld->log, &id, "Peer says it sees our address as: %s", @@ -1429,11 +1443,14 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) p = peer_by_id(ld, &id); if (p) { log_peer_debug(ld->log, &id, "peer_disconnect_done"); - p->is_connected = false; + p->connected = PEER_DISCONNECTED; peer_channels_cleanup_on_disconnect(p); } + /* If you were trying to connect, it failed. */ + connect_failed_disconnect(ld, &id); + /* Fire off plugin notifications */ notify_disconnect(ld, &id); @@ -1700,12 +1717,12 @@ static void json_add_peer(struct lightningd *ld, json_object_start(response, NULL); json_add_node_id(response, "id", &p->id); - json_add_bool(response, "connected", p->is_connected); + json_add_bool(response, "connected", p->connected == PEER_CONNECTED); /* If it's not connected, features are unreliable: we don't * store them in the database, and they would only reflect * their features *last* time they connected. */ - if (p->is_connected) { + if (p->connected == PEER_CONNECTED) { json_array_start(response, "netaddr"); json_add_string(response, NULL, type_to_string(tmpctx, @@ -1982,7 +1999,7 @@ static struct command_result *json_disconnect(struct command *cmd, if (!peer) { return command_fail(cmd, LIGHTNINGD, "Unknown peer"); } - if (!peer->is_connected) { + if (peer->connected == PEER_DISCONNECTED) { return command_fail(cmd, LIGHTNINGD, "Peer not connected"); } diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index c76d34d0acdd..e4d94777adde 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -31,7 +31,14 @@ struct peer { struct list_head channels; /* Are we connected? */ - bool is_connected; + enum { + /* Connectd said we're connecting, we called hooks... */ + PEER_CONNECTING, + /* Hooks succeeded, we're connected. */ + PEER_CONNECTED, + /* Start state, also connectd told us we're disconnected */ + PEER_DISCONNECTED, + } connected; /* Our (only) uncommitted channel, still opening. */ struct uncommitted_channel *uncommitted_channel; @@ -70,6 +77,7 @@ struct peer *peer_from_json(struct lightningd *ld, const char *buffer, const jsmntok_t *peeridtok); +/* connectd tells us what peer is doing */ void peer_connected(struct lightningd *ld, const u8 *msg); void peer_disconnect_done(struct lightningd *ld, const u8 *msg); void peer_active(struct lightningd *ld, const u8 *msg, int peer_fd); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index b6c34c78f88d..5e0c2174cafe 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -147,6 +147,9 @@ struct command_result *command_success(struct command *cmd UNNEEDED, struct json_stream *response) { fprintf(stderr, "command_success called!\n"); abort(); } +/* Generated stub for connect_failed_disconnect */ +void connect_failed_disconnect(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "connect_failed_disconnect called!\n"); abort(); } /* Generated stub for connect_succeeded */ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, bool incoming UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index c16ffc6dff4a..7f0c46968d27 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -106,6 +106,9 @@ struct command_result *command_success(struct command *cmd UNNEEDED, struct json_stream *response) { fprintf(stderr, "command_success called!\n"); abort(); } +/* Generated stub for connect_failed_disconnect */ +void connect_failed_disconnect(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "connect_failed_disconnect called!\n"); abort(); } /* Generated stub for connect_succeeded */ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, bool incoming UNNEEDED, From 430d6521a0a95b0cb084a43a614b01d721f95902 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 16 Jul 2022 14:19:31 +0930 Subject: [PATCH 1038/1530] common/daemon_conn: add function to read an fd. We never needed this before. Signed-off-by: Rusty Russell --- common/daemon_conn.c | 26 +++++++++++++++++++++++++- common/daemon_conn.h | 22 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/common/daemon_conn.c b/common/daemon_conn.c index 25bbac2bcd8d..e775544cc85b 100644 --- a/common/daemon_conn.c +++ b/common/daemon_conn.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -8,6 +9,7 @@ struct daemon_conn { /* Last message we received */ u8 *msg_in; + int fd_in; /* Queue of outgoing messages */ struct msg_queue *out; @@ -17,11 +19,13 @@ struct daemon_conn { /* Callback for incoming messages */ struct io_plan *(*recv)(struct io_conn *conn, const u8 *, void *); + /* Callback with fd */ + struct io_plan *(*recv_fd)(struct io_conn *conn, const u8 *, int, void *); /* Called whenever we've cleared the msg_out queue. */ void (*outq_empty)(void *); - /* Arg for both callbacks. */ + /* Arg for all three callbacks. */ void *arg; }; @@ -40,6 +44,26 @@ struct io_plan *daemon_conn_read_next(struct io_conn *conn, return io_read_wire(conn, dc, &dc->msg_in, handle_read, dc); } +static struct io_plan *handle_recv_fd(struct io_conn *conn, + struct daemon_conn *dc) +{ + return dc->recv_fd(conn, dc->msg_in, dc->fd_in, dc->arg); +} + +struct io_plan *daemon_conn_read_with_fd_(struct io_conn *conn, + struct daemon_conn *dc, + struct io_plan *(*recv_fd)(struct io_conn *, + const u8 *, + int fd, + void *), + void *arg) +{ + /* We only get this for the type! */ + assert(arg == dc->arg); + dc->recv_fd = recv_fd; + return io_recv_fd(conn, &dc->fd_in, handle_recv_fd, dc); +} + static struct io_plan *daemon_conn_write_next(struct io_conn *conn, struct daemon_conn *dc) { diff --git a/common/daemon_conn.h b/common/daemon_conn.h index 3b0a8b3e80be..3996cc398791 100644 --- a/common/daemon_conn.h +++ b/common/daemon_conn.h @@ -50,6 +50,28 @@ void daemon_conn_send_fd(struct daemon_conn *dc, int fd); struct io_plan *daemon_conn_read_next(struct io_conn *conn, struct daemon_conn *dc); +/** + * daemon_conn_read_with_fd - Read a file descriptor, call again. + * (arg must be same as daemon_conn_new!) + * When recv() wants an fd with this message. + */ +#define daemon_conn_read_with_fd(conn, dc, recv_fd, arg) \ + daemon_conn_read_with_fd_((conn), (dc), \ + typesafe_cb_preargs(struct io_plan *, void *, \ + (recv_fd), (arg), \ + struct io_conn *, \ + const u8 *, \ + int), \ + (arg)) + +struct io_plan *daemon_conn_read_with_fd_(struct io_conn *conn, + struct daemon_conn *dc, + struct io_plan *(*recv_fd)(struct io_conn *, + const u8 *, + int fd, + void *), + void *arg); + /** * daemon_conn_sync_flush - Flush connection by sending all messages now.. */ From 41b379ed897ad24bf2d68ce022eb15339e430761 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:18 +0930 Subject: [PATCH 1039/1530] lightningd: hand fds to connectd, not receive them from connectd. Before this patch: 1. connectd says it's connected (peer_connected) 2. we tell connectd we want to talk about each channel (peer_make_active) 3. connectd gives us an fd for each channel, and we connect it to a subd (peer_active) 4. OR, connectd says it sent something about a channel we didn't tell it about, with an fd (peer_active) Now: 1. connectd says it's connected (peer_connected) 2. we start all appropriate subds and tell connectd to what channels/fds (peer_connect_subd). 3. if connectd says it sent something about a channel we didn't tell it about, we either tell it to hang up (peer_final_msg), or connect a new opening daemon (peer_connect_subd). This is the minimal-size patch, which is why we create socket pairs in so many places to use the existing functions. Many cleanups are possible, since the new flow is so simple. Signed-off-by: Rusty Russell --- connectd/connectd.c | 18 +- connectd/connectd_wire.csv | 21 +- connectd/multiplex.c | 187 +++++-------- connectd/multiplex.h | 2 +- lightningd/channel_control.c | 11 +- lightningd/channel_control.h | 2 +- lightningd/connect_control.c | 8 +- lightningd/dual_open_control.c | 68 +++-- lightningd/dual_open_control.h | 2 +- lightningd/opening_control.c | 23 +- lightningd/peer_control.c | 287 ++++++++++++-------- lightningd/peer_control.h | 3 +- lightningd/test/run-invoice-select-inchan.c | 19 +- wallet/test/run-wallet.c | 19 +- 14 files changed, 381 insertions(+), 289 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 510fe67e53e4..bf28c04e4897 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1899,6 +1899,15 @@ static void dev_suppress_gossip(struct daemon *daemon, const u8 *msg) } #endif /* DEVELOPER */ +static struct io_plan *recv_peer_connect_subd(struct io_conn *conn, + const u8 *msg, + int fd, + struct daemon *daemon) +{ + peer_connect_subd(daemon, msg, fd); + return daemon_conn_read_next(conn, daemon->master); +} + static struct io_plan *recv_req(struct io_conn *conn, const u8 *msg, struct daemon *daemon) @@ -1940,9 +1949,10 @@ static struct io_plan *recv_req(struct io_conn *conn, send_custommsg(daemon, msg); goto out; - case WIRE_CONNECTD_PEER_MAKE_ACTIVE: - peer_make_active(daemon, msg); - goto out; + case WIRE_CONNECTD_PEER_CONNECT_SUBD: + /* This comes with an fd */ + return daemon_conn_read_with_fd(conn, daemon->master, + recv_peer_connect_subd, daemon); case WIRE_CONNECTD_DEV_MEMLEAK: #if DEVELOPER @@ -1958,7 +1968,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_CONNECTD_INIT_REPLY: case WIRE_CONNECTD_ACTIVATE_REPLY: case WIRE_CONNECTD_PEER_CONNECTED: - case WIRE_CONNECTD_PEER_ACTIVE: + case WIRE_CONNECTD_PEER_SPOKE: case WIRE_CONNECTD_CONNECT_FAILED: case WIRE_CONNECTD_DEV_MEMLEAK_REPLY: case WIRE_CONNECTD_PING_REPLY: diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 740c86a30e28..380542262b5e 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -73,17 +73,16 @@ msgdata,connectd_peer_connected,features,u8,flen msgtype,connectd_peer_disconnect_done,2006 msgdata,connectd_peer_disconnect_done,id,node_id, -# Master -> connectd: make peer active immediately (we want to talk) -msgtype,connectd_peer_make_active,2004 -msgdata,connectd_peer_make_active,id,node_id, -msgdata,connectd_peer_make_active,channel_id,channel_id, - -# Connectd -> master: peer said something interesting (or you said make_active) -# Plus fd for peer daemon. -msgtype,connectd_peer_active,2005 -msgdata,connectd_peer_active,id,node_id, -msgdata,connectd_peer_active,msgtype,?u16, -msgdata,connectd_peer_active,channel_id,channel_id, +# Master -> connectd: make peer active immediately (we want to talk) (+ fd to subd). +msgtype,connectd_peer_connect_subd,2004 +msgdata,connectd_peer_connect_subd,id,node_id, +msgdata,connectd_peer_connect_subd,channel_id,channel_id, + +# Connectd -> master: peer said something interesting +msgtype,connectd_peer_spoke,2005 +msgdata,connectd_peer_spoke,id,node_id, +msgdata,connectd_peer_spoke,msgtype,u16, +msgdata,connectd_peer_spoke,channel_id,channel_id, # master -> connectd: peer no longer wanted, you can disconnect. msgtype,connectd_discard_peer,2015 diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 78bdbacce027..2b9aa522d82f 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -50,7 +50,7 @@ struct subd { /* The opening revocation basepoint, for v2 channel_id. */ struct pubkey *opener_revocation_basepoint; - /* The actual connection to talk to it */ + /* The actual connection to talk to it (NULL if it's not connected yet) */ struct io_conn *conn; /* Input buffer */ @@ -539,66 +539,6 @@ void send_custommsg(struct daemon *daemon, const u8 *msg) inject_peer_msg(peer, take(custommsg)); } -/* FIXME: fwd decl */ -static struct subd *multiplex_subd_setup(struct peer *peer, - const struct channel_id *channel_id, - int *fd_for_subd); - -static struct subd *activate_subd(struct peer *peer, - const enum peer_wire *type, - const struct channel_id *channel_id) -{ - int fd_for_subd; - u16 t, *tp; - struct subd *subd; - - subd = multiplex_subd_setup(peer, channel_id, &fd_for_subd); - if (!subd) - return NULL; - - /* wire routines want a u16, not an enum */ - if (type) { - t = *type; - tp = &t; - } else { - tp = NULL; - } - - /* We tell lightningd to fire up a subdaemon to handle this! */ - daemon_conn_send(peer->daemon->master, - take(towire_connectd_peer_active(NULL, &peer->id, - tp, - channel_id))); - daemon_conn_send_fd(peer->daemon->master, fd_for_subd); - return subd; -} - -void peer_make_active(struct daemon *daemon, const u8 *msg) -{ - struct node_id id; - struct peer *peer; - struct channel_id channel_id; - - if (!fromwire_connectd_peer_make_active(msg, &id, &channel_id)) - master_badmsg(WIRE_CONNECTD_PEER_MAKE_ACTIVE, msg); - - /* Races can happen: this might be gone by now. */ - peer = peer_htable_get(&daemon->peers, &id); - if (!peer) - return; - - /* Could be disconnecting now */ - if (!peer->to_peer) - return; - - /* Could be made active already by receiving a message (esp reestablish!) */ - if (find_subd(peer, &channel_id)) - return; - - if (!activate_subd(peer, NULL, &channel_id)) - tal_free(peer); -} - static void handle_ping_in(struct peer *peer, const u8 *msg) { u8 *pong; @@ -1037,6 +977,41 @@ static struct io_plan *write_to_subd(struct io_conn *subd_conn, return io_write_wire(subd_conn, take(msg), write_to_subd, subd); } +static void destroy_subd(struct subd *subd) +{ + struct peer *peer = subd->peer; + size_t pos; + + for (pos = 0; peer->subds[pos] != subd; pos++) + assert(pos < tal_count(peer->subds)); + + tal_arr_remove(&peer->subds, pos); + + /* Make sure we try to keep reading from peer (might + * have been waiting for write_to_subd) */ + io_wake(&peer->peer_in); +} + +static struct subd *new_subd(struct peer *peer, + const struct channel_id *channel_id) +{ + struct subd *subd; + + subd = tal(peer->subds, struct subd); + subd->peer = peer; + subd->outq = msg_queue_new(subd, false); + subd->channel_id = *channel_id; + subd->temporary_channel_id = NULL; + subd->opener_revocation_basepoint = NULL; + subd->conn = NULL; + + /* Connect it to the peer */ + tal_arr_expand(&peer->subds, subd); + tal_add_destructor(subd, destroy_subd); + + return subd; +} + static struct io_plan *read_hdr_from_peer(struct io_conn *peer_conn, struct peer *peer); static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, @@ -1094,15 +1069,18 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return read_hdr_from_peer(peer_conn, peer); } - /* If we don't find a subdaemon for this, activate a new one. */ + /* If we don't find a subdaemon for this, crteat a new one. */ subd = find_subd(peer, &channel_id); if (!subd) { enum peer_wire t = fromwire_peektype(decrypted); status_peer_debug(&peer->id, "Activating for message %s", peer_wire_name(t)); - subd = activate_subd(peer, &t, &channel_id); - if (!subd) - return io_close(peer_conn); + subd = new_subd(peer, &channel_id); + /* We tell lightningd to fire up a subdaemon to handle this! */ + daemon_conn_send(peer->daemon->master, + take(towire_connectd_peer_spoke(NULL, &peer->id, + t, + &channel_id))); } /* Even if we just created it, call this to catch open_channel2 */ @@ -1156,53 +1134,6 @@ static struct io_plan *subd_conn_init(struct io_conn *subd_conn, write_to_subd(subd_conn, subd)); } -static void destroy_subd(struct subd *subd) -{ - struct peer *peer = subd->peer; - size_t pos; - - for (pos = 0; peer->subds[pos] != subd; pos++) - assert(pos < tal_count(peer->subds)); - - tal_arr_remove(&peer->subds, pos); - - /* Make sure we try to keep reading from peer (might - * have been waiting for write_to_subd) */ - io_wake(&peer->peer_in); -} - -static struct subd *multiplex_subd_setup(struct peer *peer, - const struct channel_id *channel_id, - int *fd_for_subd) -{ - int fds[2]; - struct subd *subd; - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { - status_broken("Failed to create socketpair: %s", - strerror(errno)); - return NULL; - } - - subd = tal(NULL, struct subd); - subd->peer = peer; - subd->outq = msg_queue_new(subd, false); - subd->channel_id = *channel_id; - subd->temporary_channel_id = NULL; - subd->opener_revocation_basepoint = NULL; - /* This sets subd->conn inside subd_conn_init */ - io_new_conn(peer->subds, fds[0], subd_conn_init, subd); - /* When conn dies, subd is freed. */ - tal_steal(subd->conn, subd); - - /* Connect it to the peer */ - tal_arr_expand(&peer->subds, subd); - tal_add_destructor(subd, destroy_subd); - - *fd_for_subd = fds[1]; - return subd; -} - static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) { assert(peer->to_peer == peer_conn); @@ -1239,6 +1170,38 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, write_to_peer(peer_conn, peer)); } +void peer_connect_subd(struct daemon *daemon, const u8 *msg, int fd) +{ + struct node_id id; + struct peer *peer; + struct channel_id channel_id; + struct subd *subd; + + if (!fromwire_connectd_peer_connect_subd(msg, &id, &channel_id)) + master_badmsg(WIRE_CONNECTD_PEER_CONNECT_SUBD, msg); + + /* Races can happen: this might be gone by now. */ + peer = peer_htable_get(&daemon->peers, &id); + if (!peer) { + close(fd); + return; + } + + /* Could be disconnecting now */ + if (!peer->to_peer) { + close(fd); + return; + } + + /* If peer said something, we created this and queued msg. */ + subd = find_subd(peer, &channel_id); + if (!subd) + subd = new_subd(peer, &channel_id); + + assert(!subd->conn); + /* This sets subd->conn inside subd_conn_init */ + io_new_conn(subd, fd, subd_conn_init, subd); +} /* Lightningd says to send a ping */ void send_manual_ping(struct daemon *daemon, const u8 *msg) diff --git a/connectd/multiplex.h b/connectd/multiplex.h index ce2ed7b02b7e..77a41fa2aae4 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -33,5 +33,5 @@ void send_manual_ping(struct daemon *daemon, const u8 *msg); void send_custommsg(struct daemon *daemon, const u8 *msg); /* Lightningd wants to talk to you. */ -void peer_make_active(struct daemon *daemon, const u8 *msg); +void peer_connect_subd(struct daemon *daemon, const u8 *msg, int fd); #endif /* LIGHTNING_CONNECTD_MULTIPLEX_H */ diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index acf9fa13e410..c5b16f6cd775 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -595,7 +595,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) return 0; } -void peer_start_channeld(struct channel *channel, +bool peer_start_channeld(struct channel *channel, struct peer_fd *peer_fd, const u8 *fwd_msg, bool reconnected, @@ -639,7 +639,7 @@ void peer_start_channeld(struct channel *channel, strerror(errno)); channel_fail_reconnect_later(channel, "Failed to subdaemon channel"); - return; + return false; } htlcs = peer_htlcs(tmpctx, channel); @@ -677,7 +677,7 @@ void peer_start_channeld(struct channel *channel, REASON_LOCAL, "Could not get revocation secret %"PRIu64, num_revocations-1); - return; + return false; } /* Warn once. */ @@ -691,7 +691,7 @@ void peer_start_channeld(struct channel *channel, channel_internal_error(channel, "Could not load remote announcement" " signatures"); - return; + return false; } pbases = wallet_penalty_base_load_for_channel( @@ -706,7 +706,7 @@ void peer_start_channeld(struct channel *channel, channel_internal_error(channel, "Could not derive final_ext_key %"PRIu64, channel->final_key_idx); - return; + return false; } initmsg = towire_channeld_init(tmpctx, @@ -796,6 +796,7 @@ void peer_start_channeld(struct channel *channel, subd_send_msg(channel->owner, take(towire_channeld_funding_depth( NULL, channel->scid, channel->alias[LOCAL], 0))); + return true; } bool channel_tell_depth(struct lightningd *ld, diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index 365e87655e2c..8f3b8ab190b3 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -10,7 +10,7 @@ struct lightningd; struct peer_fd; struct peer; -void peer_start_channeld(struct channel *channel, +bool peer_start_channeld(struct channel *channel, struct peer_fd *peer_fd, const u8 *fwd_msg, bool reconnected, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index b50d16d475b7..c0f8670a5b95 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -471,7 +471,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd case WIRE_CONNECTD_DEV_MEMLEAK: case WIRE_CONNECTD_DEV_SUPPRESS_GOSSIP: case WIRE_CONNECTD_PEER_FINAL_MSG: - case WIRE_CONNECTD_PEER_MAKE_ACTIVE: + case WIRE_CONNECTD_PEER_CONNECT_SUBD: case WIRE_CONNECTD_PING: case WIRE_CONNECTD_SEND_ONIONMSG: case WIRE_CONNECTD_CUSTOMMSG_OUT: @@ -486,10 +486,8 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd peer_connected(connectd->ld, msg); break; - case WIRE_CONNECTD_PEER_ACTIVE: - if (tal_count(fds) != 1) - return 1; - peer_active(connectd->ld, msg, fds[0]); + case WIRE_CONNECTD_PEER_SPOKE: + peer_spoke(connectd->ld, msg); break; case WIRE_CONNECTD_PEER_DISCONNECT_DONE: diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index fd33c85dda15..9ebc9f2ce95b 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -2585,6 +2585,7 @@ static struct command_result *json_openchannel_init(struct command *cmd, struct open_attempt *oa; struct lease_rates *rates; struct command_result *res; + int fds[2]; if (!param(cmd, buffer, params, p_req("id", param_node_id, &id), @@ -2743,10 +2744,28 @@ static struct command_result *json_openchannel_init(struct command *cmd, false, rates); - /* Tell connectd to hand us this so we can start dualopend */ + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + return command_fail(cmd, FUND_MAX_EXCEEDED, + "Failed to create socketpair: %s", + strerror(errno)); + } + + /* Start dualopend! */ + if (!peer_start_dualopend(peer, new_peer_fd(cmd, fds[0]), channel)) { + close(fds[1]); + /* FIXME: gets completed by failure path above! */ + return command_its_complicated("completed by peer_start_dualopend"); + } + + /* Go! */ + subd_send_msg(channel->owner, channel->open_attempt->open_msg); + + /* Tell connectd connect this to this channel id. */ subd_send_msg(peer->ld->connectd, - take(towire_connectd_peer_make_active(NULL, &peer->id, - &channel->cid))); + take(towire_connectd_peer_connect_subd(NULL, + &peer->id, + &channel->cid))); + subd_send_fd(peer->ld->connectd, fds[1]); return command_still_pending(cmd); } @@ -3109,6 +3128,7 @@ static struct command_result *json_queryrates(struct command *cmd, struct open_attempt *oa; u32 *our_upfront_shutdown_script_wallet_index; struct command_result *res; + int fds[2]; if (!param(cmd, buffer, params, p_req("id", param_node_id, &id), @@ -3210,13 +3230,30 @@ static struct command_result *json_queryrates(struct command *cmd, true, NULL); - /* Tell connectd to hand us this so we can start dualopend */ - subd_send_msg(peer->ld->connectd, - take(towire_connectd_peer_make_active(NULL, &peer->id, - &channel->cid))); - return command_still_pending(cmd); + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + return command_fail(cmd, FUND_MAX_EXCEEDED, + "Failed to create socketpair: %s", + strerror(errno)); + } -} + /* Start dualopend! */ + if (!peer_start_dualopend(peer, new_peer_fd(cmd, fds[0]), channel)) { + close(fds[1]); + /* FIXME: gets completed by failure path above! */ + return command_its_complicated("completed by peer_start_dualopend"); + } + + /* Go! */ + subd_send_msg(channel->owner, channel->open_attempt->open_msg); + + /* Tell connectd connect this to this channel id. */ + subd_send_msg(peer->ld->connectd, + take(towire_connectd_peer_connect_subd(NULL, + &peer->id, + &channel->cid))); + subd_send_fd(peer->ld->connectd, fds[1]); + return command_still_pending(cmd); + } static const struct json_command queryrates_command = { "dev-queryrates", @@ -3333,7 +3370,7 @@ bool peer_start_dualopend(struct peer *peer, return true; } -void peer_restart_dualopend(struct peer *peer, +bool peer_restart_dualopend(struct peer *peer, struct peer_fd *peer_fd, struct channel *channel) { @@ -3345,10 +3382,9 @@ void peer_restart_dualopend(struct peer *peer, u32 *local_shutdown_script_wallet_index; u8 *msg; - if (channel_unsaved(channel)) { - peer_start_dualopend(peer, peer_fd, channel); - return; - } + if (channel_unsaved(channel)) + return peer_start_dualopend(peer, peer_fd, channel); + hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->dbid, HSM_CAP_COMMITMENT_POINT | HSM_CAP_SIGN_REMOTE_TX @@ -3371,7 +3407,7 @@ void peer_restart_dualopend(struct peer *peer, strerror(errno)); channel_fail_reconnect_later(channel, "Failed to subdaemon channel"); - return; + return false; } /* Find the max self delay and min htlc capacity */ @@ -3434,6 +3470,6 @@ void peer_restart_dualopend(struct peer *peer, inflight->lease_chan_max_msat, inflight->lease_chan_max_ppt); - subd_send_msg(channel->owner, take(msg)); + return true; } diff --git a/lightningd/dual_open_control.h b/lightningd/dual_open_control.h index aa623659fa8c..7d34d9816c57 100644 --- a/lightningd/dual_open_control.h +++ b/lightningd/dual_open_control.h @@ -9,7 +9,7 @@ struct peer_fd; bool peer_start_dualopend(struct peer *peer, struct peer_fd *peer_fd, struct channel *channel); -void peer_restart_dualopend(struct peer *peer, +bool peer_restart_dualopend(struct peer *peer, struct peer_fd *peer_fd, struct channel *channel); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index e2081e479b36..928d64325064 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1083,7 +1083,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, struct peer *peer; bool *announce_channel; u32 *feerate_per_kw, *mindepth; - + int fds[2]; struct amount_sat *amount; struct amount_msat *push_msat; u32 *upfront_shutdown_script_wallet_index; @@ -1236,10 +1236,25 @@ static struct command_result *json_fundchannel_start(struct command *cmd, &tmp_channel_id, fc->channel_flags); - /* Tell connectd to make this active; when it does, we can continue */ + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + return command_fail(cmd, FUND_MAX_EXCEEDED, + "Failed to create socketpair: %s", + strerror(errno)); + } + if (!peer_start_openingd(peer, new_peer_fd(cmd, fds[0]))) { + close(fds[1]); + /* FIXME: gets completed by failure path above! */ + return command_its_complicated("completed by peer_start_openingd"); + } + /* Tell it to start funding */ + subd_send_msg(peer->uncommitted_channel->open_daemon, fc->open_msg); + + /* Tell connectd connect this to this channel id. */ subd_send_msg(peer->ld->connectd, - take(towire_connectd_peer_make_active(NULL, &peer->id, - &tmp_channel_id))); + take(towire_connectd_peer_connect_subd(NULL, + &peer->id, + &peer->uncommitted_channel->cid))); + subd_send_fd(peer->ld->connectd, fds[1]); return command_still_pending(cmd); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 93cfa072451a..518e4cff2401 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1007,6 +1007,91 @@ peer_connected_serialize(struct peer_connected_hook_payload *payload, json_object_end(stream); /* .peer */ } +/* Talk to connectd about an active channel */ +static void connect_activate_subd(struct lightningd *ld, struct channel *channel) +{ + const u8 *error; + int fds[2]; + + /* If we have a canned error for this channel, send it now */ + if (channel->error) { + error = channel->error; + goto send_error; + } + + switch (channel->state) { + case ONCHAIN: + case FUNDING_SPEND_SEEN: + case CLOSINGD_COMPLETE: + case CLOSED: + /* Channel is active */ + abort(); + case AWAITING_UNILATERAL: + /* channel->error is not saved in db, so this can + * happen if we restart. */ + error = towire_errorfmt(tmpctx, &channel->cid, + "Awaiting unilateral close"); + goto send_error; + + case DUALOPEND_OPEN_INIT: + case DUALOPEND_AWAITING_LOCKIN: + assert(!channel->owner); + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + log_broken(channel->log, + "Failed to create socketpair: %s", + strerror(errno)); + error = towire_warningfmt(tmpctx, &channel->cid, + "Trouble in paradise?"); + goto send_error; + } + if (peer_restart_dualopend(channel->peer, + new_peer_fd(tmpctx, fds[0]), + channel)) + goto tell_connectd; + close(fds[1]); + return; + + case CHANNELD_AWAITING_LOCKIN: + case CHANNELD_NORMAL: + case CHANNELD_SHUTTING_DOWN: + case CLOSINGD_SIGEXCHANGE: + assert(!channel->owner); + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + log_broken(channel->log, + "Failed to create socketpair: %s", + strerror(errno)); + error = towire_warningfmt(tmpctx, &channel->cid, + "Trouble in paradise?"); + goto send_error; + } + if (peer_start_channeld(channel, + new_peer_fd(tmpctx, fds[0]), + NULL, true, + NULL)) { + goto tell_connectd; + } + close(fds[1]); + return; + } + abort(); + +tell_connectd: + subd_send_msg(ld->connectd, + take(towire_connectd_peer_connect_subd(NULL, + &channel->peer->id, + &channel->cid))); + subd_send_fd(ld->connectd, fds[1]); + return; + +send_error: + log_debug(channel->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, &channel->peer->id, + error))); +} + static void peer_connected_hook_final(struct peer_connected_hook_payload *payload STEALS) { struct lightningd *ld = payload->ld; @@ -1041,23 +1126,27 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa /* Notify anyone who cares */ notify_connect(ld, &peer->id, payload->incoming, &addr); - list_for_each(&peer->channels, channel, list) { #if DEVELOPER - if (dev_disconnect_permanent(ld)) { + /* Developer hack to fail all channels on permfail line. */ + if (dev_disconnect_permanent(ld)) { + list_for_each(&peer->channels, channel, list) { channel_fail_permanent(channel, REASON_LOCAL, "dev_disconnect permfail"); - error = channel->error; - goto send_error; + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, &peer->id, + channel->error))); } + return; + } #endif + /* connect appropriate subds for all (active) channels! */ + list_for_each(&peer->channels, channel, list) { if (channel_active(channel)) { - log_debug(channel->log, "Peer has reconnected, state %s: telling connectd to make active", + log_debug(channel->log, "Peer has reconnected, state %s: connecting subd", channel_state_name(channel)); - subd_send_msg(ld->connectd, - take(towire_connectd_peer_make_active(NULL, &peer->id, - &channel->cid))); + connect_activate_subd(ld, channel); } } return; @@ -1239,22 +1328,23 @@ void peer_connected(struct lightningd *ld, const u8 *msg) plugin_hook_call_peer_connected(ld, hook_payload); } -/* connectd tells us a peer has an interesting message, and hands us an - * fd to give to the correct subdaemon. Unlike peer_connected, this is racy: - * we might have just told it to disconnect peer. */ -void peer_active(struct lightningd *ld, const u8 *msg, int fd) +/* connectd tells us a peer has a message and we've not already attached + * a subd. Normally this is a race, but it happens for real when opening + * a new channel, or referring to a channel we no longer want to talk to + * it about. */ +void peer_spoke(struct lightningd *ld, const u8 *msg) { struct node_id id; - u16 *msgtype; + u16 msgtype; struct channel *channel; struct channel_id channel_id; struct peer *peer; bool dual_fund; u8 *error; - struct peer_fd *peer_fd = new_peer_fd(tmpctx, fd); + int fds[2]; - if (!fromwire_connectd_peer_active(msg, msg, &id, &msgtype, &channel_id)) - fatal("Connectd gave bad CONNECTD_PEER_ACTIVE message %s", + if (!fromwire_connectd_peer_spoke(msg, &id, &msgtype, &channel_id)) + fatal("Connectd gave bad CONNECTD_PEER_SPOKE message %s", tal_hex(msg, msg)); peer = peer_by_id(ld, &id); @@ -1274,83 +1364,44 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) goto send_error; } - switch (channel->state) { - case ONCHAIN: - case FUNDING_SPEND_SEEN: - case CLOSINGD_COMPLETE: - goto channel_is_closed; - case CLOSED: - /* Channel should not have been loaded */ - abort(); - case AWAITING_UNILATERAL: { - /* channel->error is not saved in db, so this can - * happen if we restart. */ - error = towire_errorfmt(tmpctx, &channel->cid, - "Awaiting unilateral close"); - goto send_error; - } - case DUALOPEND_OPEN_INIT: - /* We asked for this, to open? */ - if (!msgtype - && channel->open_attempt - && channel->open_attempt->open_msg) { - if (peer_start_dualopend(peer, peer_fd, channel)) - subd_send_msg(channel->owner, channel->open_attempt->open_msg); - return; - } - /* Fall through. */ - case DUALOPEND_AWAITING_LOCKIN: - assert(!channel->owner); - peer_restart_dualopend(peer, peer_fd, channel); + /* If channel is active, we raced, so ignore this: + * subd will get it soon. */ + if (channel_active(channel)) return; - case CHANNELD_AWAITING_LOCKIN: - case CHANNELD_NORMAL: - case CHANNELD_SHUTTING_DOWN: - case CLOSINGD_SIGEXCHANGE: - /* Maybe old owner was too slow exiting? */ - tal_free(channel->owner); - peer_start_channeld(channel, - peer_fd, - NULL, true, - NULL); + + if (msgtype == WIRE_CHANNEL_REESTABLISH) { + log_debug(channel->log, + "Reestablish on %s channel: using channeld to reply", + channel_state_name(channel)); + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + log_broken(channel->log, + "Failed to create socketpair: %s", + strerror(errno)); + error = towire_warningfmt(tmpctx, &channel->cid, + "Trouble in paradise?"); + goto send_error; + } + if (peer_start_channeld(channel, new_peer_fd(tmpctx, fds[0]), NULL, true, true)) { + goto tell_connectd; + } + /* FIXME: Send informative error? */ + close(fds[1]); return; } - abort(); + + /* Send generic error. */ + error = towire_errorfmt(tmpctx, &channel_id, + "channel in state %s", + channel_state_name(channel)); + goto send_error; } dual_fund = feature_negotiated(ld->our_features, peer->their_features, OPT_DUAL_FUND); - /* Did we ask for this? */ - if (!msgtype) { - /* If it was dual_fund, it will have peer_unsaved_channel above */ - if (dual_fund) { - log_broken(ld->log, "Unsolicited active df peer %s?", - type_to_string(tmpctx, struct node_id, - &peer->id)); - } else { - const struct uncommitted_channel *uc - = peer->uncommitted_channel; - - if (!uc->open_daemon - && uc->fc - && uc->fc->open_msg) { - if (peer_start_openingd(peer, peer_fd)) { - subd_send_msg(uc->open_daemon, - uc->fc->open_msg); - } - } else { - log_broken(ld->log, "Unsolicited active peer %s?", - type_to_string(tmpctx, struct node_id, - &peer->id)); - } - } - return; - } - /* OK, it's an unknown channel. Create a new one if they're trying. */ - switch (*msgtype) { + switch (msgtype) { case WIRE_OPEN_CHANNEL: if (dual_fund) { error = towire_errorfmt(tmpctx, &channel_id, @@ -1364,8 +1415,21 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) } peer->uncommitted_channel = new_uncommitted_channel(peer); peer->uncommitted_channel->cid = channel_id; - peer_start_openingd(peer, peer_fd); - break; + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + log_broken(ld->log, + "Failed to create socketpair: %s", + strerror(errno)); + error = towire_warningfmt(tmpctx, &channel_id, + "Trouble in paradise?"); + goto send_error; + } + if (peer_start_openingd(peer, new_peer_fd(tmpctx, fds[0]))) { + goto tell_connectd; + } + /* FIXME: Send informative error? */ + close(fds[1]); + return; + case WIRE_OPEN_CHANNEL2: if (!dual_fund) { error = towire_errorfmt(tmpctx, &channel_id, @@ -1376,36 +1440,29 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) peer->ld->config.fee_base, peer->ld->config.fee_per_satoshi); channel->cid = channel_id; - peer_start_dualopend(peer, peer_fd, channel); - break; - default: - log_peer_unusual(ld->log, &peer->id, - "Unknown channel %s for %s", - type_to_string(tmpctx, struct channel_id, - &channel_id), - peer_wire_name(*msgtype)); - error = towire_errorfmt(tmpctx, &channel_id, - "Unknown channel for %s", peer_wire_name(*msgtype)); - goto send_error; - break; - } - return; - -channel_is_closed: - if (msgtype && *msgtype == WIRE_CHANNEL_REESTABLISH) { - log_debug(channel->log, - "Reestablish on %s channel: using channeld to reply", - channel_state_name(channel)); - peer_start_channeld(channel, peer_fd, NULL, true, true); + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { + log_broken(ld->log, + "Failed to create socketpair: %s", + strerror(errno)); + error = towire_warningfmt(tmpctx, &channel_id, + "Trouble in paradise?"); + goto send_error; + } + if (peer_start_dualopend(peer, new_peer_fd(tmpctx, fds[0]), channel)) + goto tell_connectd; + /* FIXME: Send informative error? */ + close(fds[1]); return; } - /* Retransmit error if we have one. Otherwise generic error. */ - error = channel->error; - if (!error) - error = towire_errorfmt(tmpctx, &channel_id, - "channel in state %s", - channel_state_name(channel)); + /* Weird message? Log and reply with error. */ + log_peer_unusual(ld->log, &peer->id, + "Unknown channel %s for %s", + type_to_string(tmpctx, struct channel_id, + &channel_id), + peer_wire_name(msgtype)); + error = towire_errorfmt(tmpctx, &channel_id, + "Unknown channel for %s", peer_wire_name(msgtype)); send_error: log_peer_debug(ld->log, &peer->id, "Telling connectd to send error %s", @@ -1414,6 +1471,12 @@ void peer_active(struct lightningd *ld, const u8 *msg, int fd) subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, error))); + return; + +tell_connectd: + subd_send_msg(ld->connectd, + take(towire_connectd_peer_connect_subd(NULL, &id, &channel_id))); + subd_send_fd(ld->connectd, fds[1]); } struct disconnect_command { diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index e4d94777adde..f7ff4abd55d0 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -80,7 +80,8 @@ struct peer *peer_from_json(struct lightningd *ld, /* connectd tells us what peer is doing */ void peer_connected(struct lightningd *ld, const u8 *msg); void peer_disconnect_done(struct lightningd *ld, const u8 *msg); -void peer_active(struct lightningd *ld, const u8 *msg, int peer_fd); +void peer_spoke(struct lightningd *ld, const u8 *msg); + /* May delete peer! */ void peer_channels_cleanup_on_disconnect(struct peer *peer); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 5e0c2174cafe..45c914bdcf05 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -220,15 +220,15 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_channeld_dev_memleak_reply */ bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } -/* Generated stub for fromwire_connectd_peer_active */ -bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_connectd_peer_active called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_disconnect_done */ bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_disconnect_done called!\n"); abort(); } +/* Generated stub for fromwire_connectd_peer_spoke */ +bool fromwire_connectd_peer_spoke(const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_peer_spoke called!\n"); abort(); } /* Generated stub for fromwire_dualopend_dev_memleak_reply */ bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_dualopend_dev_memleak_reply called!\n"); abort(); } @@ -647,12 +647,12 @@ struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name struct channel *peer_any_active_channel(struct peer *peer UNNEEDED, bool *others UNNEEDED) { fprintf(stderr, "peer_any_active_channel called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ -void peer_restart_dualopend(struct peer *peer UNNEEDED, +bool peer_restart_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd 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, +bool peer_start_channeld(struct channel *channel UNNEEDED, struct peer_fd *peer_fd UNNEEDED, const u8 *fwd_msg UNNEEDED, bool reconnected UNNEEDED, @@ -693,6 +693,9 @@ struct subd_req *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(); } @@ -711,12 +714,12 @@ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channeld_dev_reenable_commit */ u8 *towire_channeld_dev_reenable_commit(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_reenable_commit called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_connect_subd */ +u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_connect_subd 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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } -/* Generated stub for towire_connectd_peer_make_active */ -u8 *towire_connectd_peer_make_active(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "towire_connectd_peer_make_active called!\n"); abort(); } /* Generated stub for towire_dualopend_dev_memleak */ u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_dualopend_dev_memleak called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 7f0c46968d27..ccbc34fdf5ee 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -151,15 +151,15 @@ bool fromwire_channeld_offer_htlc_reply(const tal_t *ctx UNNEEDED, const void *p /* Generated stub for fromwire_channeld_sending_commitsig */ bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_sending_commitsig called!\n"); abort(); } -/* Generated stub for fromwire_connectd_peer_active */ -bool fromwire_connectd_peer_active(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 **msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "fromwire_connectd_peer_active called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_disconnect_done */ bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_disconnect_done called!\n"); abort(); } +/* Generated stub for fromwire_connectd_peer_spoke */ +bool fromwire_connectd_peer_spoke(const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_connectd_peer_spoke called!\n"); abort(); } /* Generated stub for fromwire_dualopend_dev_memleak_reply */ bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_dualopend_dev_memleak_reply called!\n"); abort(); } @@ -633,12 +633,12 @@ void payment_succeeded(struct lightningd *ld UNNEEDED, struct htlc_out *hout UNN const struct preimage *rval UNNEEDED) { fprintf(stderr, "payment_succeeded called!\n"); abort(); } /* Generated stub for peer_restart_dualopend */ -void peer_restart_dualopend(struct peer *peer UNNEEDED, +bool peer_restart_dualopend(struct peer *peer UNNEEDED, struct peer_fd *peer_fd 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, +bool peer_start_channeld(struct channel *channel UNNEEDED, struct peer_fd *peer_fd UNNEEDED, const u8 *fwd_msg UNNEEDED, bool reconnected UNNEEDED, @@ -696,6 +696,9 @@ struct subd_req *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(); } @@ -738,12 +741,12 @@ u8 *towire_channeld_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amo /* Generated stub for towire_channeld_sending_commitsig_reply */ u8 *towire_channeld_sending_commitsig_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_sending_commitsig_reply called!\n"); abort(); } +/* Generated stub for towire_connectd_peer_connect_subd */ +u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_connectd_peer_connect_subd 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 u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } -/* Generated stub for towire_connectd_peer_make_active */ -u8 *towire_connectd_peer_make_active(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) -{ fprintf(stderr, "towire_connectd_peer_make_active called!\n"); abort(); } /* Generated stub for towire_dualopend_dev_memleak */ u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_dualopend_dev_memleak called!\n"); abort(); } From d31420211ad6e3d665cd75ae2ee9e870161feb22 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:27 +0930 Subject: [PATCH 1040/1530] connectd: add counters to each peer connection. This allows us to detect when lightningd hasn't seen our latest disconnect/reconnect; in particular, we would hit the following pattern: 1. lightningd says to connect a subd. 2. connectd disconnects and reconnects. 3. connectd reads message, connects subd. 4. lightningd reads disconnect and reconnect, sends msg to connect to subd again. 5. connectd asserts because subd is alreacy connected. This way connectd can tell if lightningd is talking about the previous connection, and ignoere it. Signed-off-by: Rusty Russell --- connectd/connectd.c | 23 +++++++++---- connectd/connectd.h | 6 ++++ connectd/connectd_wire.csv | 6 ++++ connectd/multiplex.c | 8 +++-- lightningd/channel_control.c | 1 + lightningd/connect_control.c | 3 +- lightningd/dual_open_control.c | 3 ++ lightningd/opening_control.c | 1 + lightningd/peer_control.c | 38 +++++++++++++++------ lightningd/peer_control.h | 3 ++ lightningd/test/run-invoice-select-inchan.c | 10 +++--- wallet/test/run-wallet.c | 10 +++--- 12 files changed, 82 insertions(+), 30 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index bf28c04e4897..5d57fd8f717f 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -223,7 +223,8 @@ void destroy_peer(struct peer *peer) /* Tell lightningd it's really disconnected */ daemon_conn_send(peer->daemon->master, take(towire_connectd_peer_disconnect_done(NULL, - &peer->id))); + &peer->id, + peer->counter))); } /*~ This is where we create a new peer. */ @@ -238,6 +239,7 @@ static struct peer *new_peer(struct daemon *daemon, peer->daemon = daemon; peer->id = *id; + peer->counter = daemon->connection_counter++; peer->cs = *cs; peer->subds = tal_arr(peer, struct subd *, 0); peer->peer_in = NULL; @@ -347,7 +349,8 @@ struct io_plan *peer_connected(struct io_conn *conn, setup_peer_gossip_store(peer, daemon->our_features, their_features); /* Create message to tell master peer has connected. */ - msg = towire_connectd_peer_connected(NULL, id, addr, remote_addr, + msg = towire_connectd_peer_connected(NULL, id, peer->counter, + addr, remote_addr, incoming, their_features); /*~ daemon_conn is a message queue for inter-daemon communication: we @@ -1841,9 +1844,10 @@ static void connect_to_peer(struct daemon *daemon, const u8 *msg) static void peer_discard(struct daemon *daemon, const u8 *msg) { struct node_id id; + u64 counter; struct peer *peer; - if (!fromwire_connectd_discard_peer(msg, &id)) + if (!fromwire_connectd_discard_peer(msg, &id, &counter)) master_badmsg(WIRE_CONNECTD_DISCARD_PEER, msg); /* We should stay in sync with lightningd, but this can happen @@ -1851,6 +1855,9 @@ static void peer_discard(struct daemon *daemon, const u8 *msg) peer = peer_htable_get(&daemon->peers, &id); if (!peer) return; + /* If it's reconnected already, it will learn soon. */ + if (peer->counter != counter) + return; status_peer_debug(&id, "disconnect"); tal_free(peer); } @@ -1861,14 +1868,17 @@ static void peer_final_msg(struct io_conn *conn, { struct peer *peer; struct node_id id; + u64 counter; u8 *finalmsg; - if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &finalmsg)) + if (!fromwire_connectd_peer_final_msg(tmpctx, msg, &id, &counter, + &finalmsg)) master_badmsg(WIRE_CONNECTD_PEER_FINAL_MSG, msg); - /* This can happen if peer hung up on us. */ + /* This can happen if peer hung up on us (or wrong counter + * if it reconnected). */ peer = peer_htable_get(&daemon->peers, &id); - if (peer) { + if (peer && peer->counter == counter) { /* Log message for peer. */ status_peer_io(LOG_IO_OUT, &id, finalmsg); multiplex_final_msg(peer, take(finalmsg)); @@ -2039,6 +2049,7 @@ int main(int argc, char *argv[]) /* Allocate and set up our simple top-level structure. */ daemon = tal(NULL, struct daemon); + daemon->connection_counter = 1; peer_htable_init(&daemon->peers); memleak_add_helper(daemon, memleak_daemon_cb); list_head_init(&daemon->connecting); diff --git a/connectd/connectd.h b/connectd/connectd.h index e77f78738172..61555e3dc506 100644 --- a/connectd/connectd.h +++ b/connectd/connectd.h @@ -53,6 +53,9 @@ struct peer { /* Connection to the peer */ struct io_conn *to_peer; + /* Counter to distinguish this connection from the next re-connection */ + u64 counter; + /* Is this draining? If so, just keep writing until queue empty */ bool draining; @@ -130,6 +133,9 @@ struct daemon { /* pubkey equivalent. */ struct pubkey mykey; + /* Counter from which we derive connection identifiers. */ + u64 connection_counter; + /* Base for timeout timers, and how long to wait for init msg */ struct timers timers; u32 timeout_secs; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 380542262b5e..6536be9ea9b6 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -63,6 +63,7 @@ msgdata,connectd_connect_failed,addrhint,?wireaddr_internal, # Connectd -> master: we got a peer. msgtype,connectd_peer_connected,2002 msgdata,connectd_peer_connected,id,node_id, +msgdata,connectd_peer_connected,counter,u64, msgdata,connectd_peer_connected,addr,wireaddr_internal, msgdata,connectd_peer_connected,remote_addr,?wireaddr, msgdata,connectd_peer_connected,incoming,bool, @@ -72,25 +73,30 @@ msgdata,connectd_peer_connected,features,u8,flen # connectd -> master: peer disconnected. msgtype,connectd_peer_disconnect_done,2006 msgdata,connectd_peer_disconnect_done,id,node_id, +msgdata,connectd_peer_disconnect_done,counter,u64, # Master -> connectd: make peer active immediately (we want to talk) (+ fd to subd). msgtype,connectd_peer_connect_subd,2004 msgdata,connectd_peer_connect_subd,id,node_id, +msgdata,connectd_peer_connect_subd,counter,u64, msgdata,connectd_peer_connect_subd,channel_id,channel_id, # Connectd -> master: peer said something interesting msgtype,connectd_peer_spoke,2005 msgdata,connectd_peer_spoke,id,node_id, +msgdata,connectd_peer_spoke,counter,u64, msgdata,connectd_peer_spoke,msgtype,u16, msgdata,connectd_peer_spoke,channel_id,channel_id, # master -> connectd: peer no longer wanted, you can disconnect. msgtype,connectd_discard_peer,2015 msgdata,connectd_discard_peer,id,node_id, +msgdata,connectd_discard_peer,counter,u64, # master -> connectd: give message to peer and disconnect. msgtype,connectd_peer_final_msg,2003 msgdata,connectd_peer_final_msg,id,node_id, +msgdata,connectd_peer_final_msg,counter,u64, msgdata,connectd_peer_final_msg,len,u16, msgdata,connectd_peer_final_msg,msg,u8,len diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 2b9aa522d82f..5b721fab97d5 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -1079,6 +1079,7 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, /* We tell lightningd to fire up a subdaemon to handle this! */ daemon_conn_send(peer->daemon->master, take(towire_connectd_peer_spoke(NULL, &peer->id, + peer->counter, t, &channel_id))); } @@ -1173,16 +1174,17 @@ struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, void peer_connect_subd(struct daemon *daemon, const u8 *msg, int fd) { struct node_id id; + u64 counter; struct peer *peer; struct channel_id channel_id; struct subd *subd; - if (!fromwire_connectd_peer_connect_subd(msg, &id, &channel_id)) + if (!fromwire_connectd_peer_connect_subd(msg, &id, &counter, &channel_id)) master_badmsg(WIRE_CONNECTD_PEER_CONNECT_SUBD, msg); - /* Races can happen: this might be gone by now. */ + /* Races can happen: this might be gone by now (or reconnected!). */ peer = peer_htable_get(&daemon->peers, &id); - if (!peer) { + if (!peer || peer->counter != counter) { close(fd); return; } diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index c5b16f6cd775..48d4f1dd7114 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -317,6 +317,7 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg) subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &channel->peer->id, + channel->peer->connectd_counter, warning))); channel_fail_reconnect(channel, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index c0f8670a5b95..aaf2f30babc8 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -662,7 +662,8 @@ void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer) * tell it to discard again: it might have reconnected! */ if (peer->connected == PEER_CONNECTED) subd_send_msg(ld->connectd, - take(towire_connectd_discard_peer(NULL, &peer->id))); + take(towire_connectd_discard_peer(NULL, &peer->id, + peer->connectd_counter))); } static struct command_result *json_sendcustommsg(struct command *cmd, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 9ebc9f2ce95b..09f41fd75e46 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1352,6 +1352,7 @@ static void handle_peer_wants_to_close(struct subd *dualopend, subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &channel->peer->id, + channel->peer->connectd_counter, warning))); channel_fail_reconnect(channel, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); @@ -2764,6 +2765,7 @@ static struct command_result *json_openchannel_init(struct command *cmd, subd_send_msg(peer->ld->connectd, take(towire_connectd_peer_connect_subd(NULL, &peer->id, + peer->connectd_counter, &channel->cid))); subd_send_fd(peer->ld->connectd, fds[1]); return command_still_pending(cmd); @@ -3250,6 +3252,7 @@ static struct command_result *json_queryrates(struct command *cmd, subd_send_msg(peer->ld->connectd, take(towire_connectd_peer_connect_subd(NULL, &peer->id, + peer->connectd_counter, &channel->cid))); subd_send_fd(peer->ld->connectd, fds[1]); return command_still_pending(cmd); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 928d64325064..ec7ea81c56dc 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1253,6 +1253,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, subd_send_msg(peer->ld->connectd, take(towire_connectd_peer_connect_subd(NULL, &peer->id, + peer->connectd_counter, &peer->uncommitted_channel->cid))); subd_send_fd(peer->ld->connectd, fds[1]); return command_still_pending(cmd); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 518e4cff2401..1814dde35ff8 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1079,6 +1079,7 @@ static void connect_activate_subd(struct lightningd *ld, struct channel *channel subd_send_msg(ld->connectd, take(towire_connectd_peer_connect_subd(NULL, &channel->peer->id, + channel->peer->connectd_counter, &channel->cid))); subd_send_fd(ld->connectd, fds[1]); return; @@ -1089,6 +1090,7 @@ static void connect_activate_subd(struct lightningd *ld, struct channel *channel /* Get connectd to send error and close. */ subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &channel->peer->id, + channel->peer->connectd_counter, error))); } @@ -1134,6 +1136,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa "dev_disconnect permfail"); subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, + peer->connectd_counter, channel->error))); } return; @@ -1157,6 +1160,7 @@ static void peer_connected_hook_final(struct peer_connected_hook_payload *payloa /* Get connectd to send error and close. */ subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, + peer->connectd_counter, error))); } @@ -1279,12 +1283,14 @@ void peer_connected(struct lightningd *ld, const u8 *msg) u8 *their_features; struct peer *peer; struct peer_connected_hook_payload *hook_payload; + u64 connectd_counter; hook_payload = tal(NULL, struct peer_connected_hook_payload); hook_payload->ld = ld; hook_payload->error = NULL; if (!fromwire_connectd_peer_connected(hook_payload, msg, - &id, &hook_payload->addr, + &id, &connectd_counter, + &hook_payload->addr, &hook_payload->remote_addr, &hook_payload->incoming, &their_features)) @@ -1298,6 +1304,14 @@ void peer_connected(struct lightningd *ld, const u8 *msg) peer = new_peer(ld, 0, &id, &hook_payload->addr, hook_payload->incoming); + /* We track this, because messages can race between connectd and us. + * For example, we could tell it to attach a subd, but it's actually + * already reconnected: we would tell it again when we read the + * "peer_connected" message, and it would get upset (plus, our first + * subd wouldn't die as expected. So we echo this back to connectd + * on peer commands, and it knows to ignore if it wrong. */ + peer->connectd_counter = connectd_counter; + /* We mark peer in "connecting" state until hooks have passed. */ assert(peer->connected == PEER_DISCONNECTED); peer->connected = PEER_CONNECTING; @@ -1336,6 +1350,7 @@ void peer_spoke(struct lightningd *ld, const u8 *msg) { struct node_id id; u16 msgtype; + u64 connectd_counter; struct channel *channel; struct channel_id channel_id; struct peer *peer; @@ -1343,17 +1358,13 @@ void peer_spoke(struct lightningd *ld, const u8 *msg) u8 *error; int fds[2]; - if (!fromwire_connectd_peer_spoke(msg, &id, &msgtype, &channel_id)) + if (!fromwire_connectd_peer_spoke(msg, &id, &connectd_counter, &msgtype, &channel_id)) fatal("Connectd gave bad CONNECTD_PEER_SPOKE message %s", tal_hex(msg, msg)); + /* We must know it, and it must be the right connectd_id */ peer = peer_by_id(ld, &id); - if (!peer) { - /* This race is possible, but I want to see it in CI. */ - log_broken(ld->log, "Unknown active peer %s", - type_to_string(tmpctx, struct node_id, &id)); - return; - } + assert(peer->connectd_counter == connectd_counter); /* Do we know what channel they're talking about? */ channel = find_channel_by_id(peer, &channel_id); @@ -1470,12 +1481,15 @@ void peer_spoke(struct lightningd *ld, const u8 *msg) /* Get connectd to send error and close. */ subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &peer->id, + peer->connectd_counter, error))); return; tell_connectd: subd_send_msg(ld->connectd, - take(towire_connectd_peer_connect_subd(NULL, &id, &channel_id))); + take(towire_connectd_peer_connect_subd(NULL, &id, + peer->connectd_counter, + &channel_id))); subd_send_fd(ld->connectd, fds[1]); } @@ -1495,16 +1509,20 @@ static void destroy_disconnect_command(struct disconnect_command *dc) void peer_disconnect_done(struct lightningd *ld, const u8 *msg) { struct node_id id; + u64 connectd_counter; struct disconnect_command *i, *next; struct peer *p; - if (!fromwire_connectd_peer_disconnect_done(msg, &id)) + if (!fromwire_connectd_peer_disconnect_done(msg, &id, &connectd_counter)) fatal("Connectd gave bad PEER_DISCONNECT_DONE message %s", tal_hex(msg, msg)); /* If we still have peer, it's disconnected now */ + /* FIXME: We should keep peers until it tells us they're disconnected, + * and not free when no more channels. */ p = peer_by_id(ld, &id); if (p) { + assert(p->connectd_counter == connectd_counter); log_peer_debug(ld->log, &id, "peer_disconnect_done"); p->connected = PEER_DISCONNECTED; diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index f7ff4abd55d0..8428b0aa75c6 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -27,6 +27,9 @@ struct peer { /* ID of peer */ struct node_id id; + /* Connection counter from connectd. */ + u64 connectd_counter; + /* Our channels */ struct list_head channels; diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 45c914bdcf05..71606b0fb9dd 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -221,13 +221,13 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ -bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) +bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_disconnect_done */ -bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED) +bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_disconnect_done called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_spoke */ -bool fromwire_connectd_peer_spoke(const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) +bool fromwire_connectd_peer_spoke(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_spoke called!\n"); abort(); } /* Generated stub for fromwire_dualopend_dev_memleak_reply */ bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) @@ -715,10 +715,10 @@ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) u8 *towire_channeld_dev_reenable_commit(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_reenable_commit called!\n"); abort(); } /* Generated stub for towire_connectd_peer_connect_subd */ -u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) +u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_connectd_peer_connect_subd 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 u8 *msg UNNEEDED) +u8 *towire_connectd_peer_final_msg(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } /* Generated stub for towire_dualopend_dev_memleak */ u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index ccbc34fdf5ee..8e0384eccb96 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -152,13 +152,13 @@ bool fromwire_channeld_offer_htlc_reply(const tal_t *ctx UNNEEDED, const void *p bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_sending_commitsig called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ -bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) +bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, struct wireaddr_internal *addr UNNEEDED, struct wireaddr **remote_addr UNNEEDED, bool *incoming UNNEEDED, u8 **features UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_connected called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_disconnect_done */ -bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED) +bool fromwire_connectd_peer_disconnect_done(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_disconnect_done called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_spoke */ -bool fromwire_connectd_peer_spoke(const void *p UNNEEDED, struct node_id *id UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) +bool fromwire_connectd_peer_spoke(const void *p UNNEEDED, struct node_id *id UNNEEDED, u64 *counter UNNEEDED, u16 *msgtype UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_connectd_peer_spoke called!\n"); abort(); } /* Generated stub for fromwire_dualopend_dev_memleak_reply */ bool fromwire_dualopend_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) @@ -742,10 +742,10 @@ u8 *towire_channeld_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amo u8 *towire_channeld_sending_commitsig_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_sending_commitsig_reply called!\n"); abort(); } /* Generated stub for towire_connectd_peer_connect_subd */ -u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, const struct channel_id *channel_id UNNEEDED) +u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_connectd_peer_connect_subd 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 u8 *msg UNNEEDED) +u8 *towire_connectd_peer_final_msg(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const u8 *msg UNNEEDED) { fprintf(stderr, "towire_connectd_peer_final_msg called!\n"); abort(); } /* Generated stub for towire_dualopend_dev_memleak */ u8 *towire_dualopend_dev_memleak(const tal_t *ctx UNNEEDED) From 2daf4617628ec0ac288dac3509795d9e5e0b46ed Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:27 +0930 Subject: [PATCH 1041/1530] pytest: enable race test. Now this passes. Signed-off-by: Rusty Russell --- tests/test_connection.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index f20494cae8a5..24c7cec89814 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4073,7 +4073,6 @@ def test_multichan(node_factory, executor, bitcoind): l1.rpc.pay(inv['bolt11']) -@pytest.mark.xfail(reason="race in reconnect logic") @pytest.mark.developer("dev-no-reconnect required") def test_mutual_reconnect_race(node_factory, executor, bitcoind): """Test simultaneous reconnect between nodes""" From 6a2817101d07fdc497cc57fb22592a45cea056e5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:27 +0930 Subject: [PATCH 1042/1530] connectd: don't move parent while we're being freed. A subtle case I hadn't come across before: if a child tal_resizes() its parent while the parent is being deleted, tal gets confused. The subd destructor does this using tal_arr_remove() on peer->subds, which is currently being freed: ``` ==61056== Invalid read of size 8 ==61056== at 0x185632: del_tree (tal.c:417) ==61056== by 0x18560D: del_tree (tal.c:412) ==61056== by 0x185957: tal_free (tal.c:486) ==61056== by 0x1183BC: peer_discard (connectd.c:1861) ==61056== by 0x11869E: recv_req (connectd.c:1942) ==61056== by 0x12774B: handle_read (daemon_conn.c:35) ==61056== by 0x173453: next_plan (io.c:59) ==61056== by 0x17405B: do_plan (io.c:407) ==61056== by 0x17409D: io_ready (io.c:417) ==61056== by 0x176390: io_loop (poll.c:453) ==61056== by 0x118A68: main (connectd.c:2082) ==61056== Address 0x4bd8850 is 16 bytes inside a block of size 48 free'd ==61056== at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==61056== by 0x1860E6: tal_resize_ (tal.c:699) ==61056== by 0x1373DD: tal_arr_remove_ (utils.c:184) ==61056== by 0x11D508: destroy_subd (multiplex.c:930) ==61056== by 0x1850A4: notify (tal.c:240) ==61056== by 0x1855BB: del_tree (tal.c:402) ==61056== by 0x18560D: del_tree (tal.c:412) ==61056== by 0x18560D: del_tree (tal.c:412) ==61056== by 0x185957: tal_free (tal.c:486) ==61056== by 0x1183BC: peer_discard (connectd.c:1861) ==61056== by 0x11869E: recv_req (connectd.c:1942) ==61056== by 0x12774B: handle_read (daemon_conn.c:35) ``` So simply make the subds children of `peer` not the `peer->subds` array. The only effect is that drain_peer() can't simply free the subds array but must free the subds one at a time. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 5b721fab97d5..2ae1b75140d9 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -99,8 +99,10 @@ static void drain_peer(struct peer *peer) /* This is a 5-second leak, worst case! */ notleak(peer); - /* We no longer want subds feeding us more messages! */ - peer->subds = tal_free(peer->subds); + /* We no longer want subds feeding us more messages (they + * remove themselves from array when freed) */ + while (tal_count(peer->subds)) + tal_free(peer->subds[0]); peer->draining = true; /* You have 5 seconds to drain... */ @@ -997,7 +999,7 @@ static struct subd *new_subd(struct peer *peer, { struct subd *subd; - subd = tal(peer->subds, struct subd); + subd = tal(peer, struct subd); subd->peer = peer; subd->outq = msg_queue_new(subd, false); subd->channel_id = *channel_id; From 671e66490e8633c49221f37e008ed73b0f18ae0c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:27 +0930 Subject: [PATCH 1043/1530] lightningd: don't kill subds immediately on disconnect. Give them time to process any final messages! If there's a reconnect, then we need to clean them up immediately of course. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 18 ++++++++++++++++-- lightningd/peer_control.h | 3 --- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 1814dde35ff8..785ad32cbfd2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -136,10 +136,16 @@ void maybe_delete_peer(struct peer *peer) delete_peer(peer); } -void peer_channels_cleanup_on_disconnect(struct peer *peer) +static void peer_channels_cleanup(struct lightningd *ld, + const struct node_id *id) { + struct peer *peer; struct channel *c, **channels; + peer = peer_by_id(ld, id); + if (!peer) + return; + /* Freeing channels can free peer, so gather first. */ channels = tal_arr(tmpctx, struct channel *, 0); list_for_each(&peer->channels, c, list) @@ -1297,6 +1303,11 @@ void peer_connected(struct lightningd *ld, const u8 *msg) fatal("Connectd gave bad CONNECT_PEER_CONNECTED message %s", tal_hex(msg, msg)); + /* When a peer disconnects, we give subds time to clean themselves up + * (this lets connectd ensure they've seen the final messages). But + * nowe it's reconnected, we've gotta force them out. */ + peer_channels_cleanup(ld, &id); + /* If we're already dealing with this peer, hand off to correct * subdaemon. Otherwise, we'll hand to openingd to wait there. */ peer = peer_by_id(ld, &id); @@ -1526,7 +1537,10 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) log_peer_debug(ld->log, &id, "peer_disconnect_done"); p->connected = PEER_DISCONNECTED; - peer_channels_cleanup_on_disconnect(p); + /* If there are literally no channels, might as well + * free immediately. */ + if (!p->uncommitted_channel && list_empty(&p->channels)) + tal_free(p); } /* If you were trying to connect, it failed. */ diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 8428b0aa75c6..f734522f3a1c 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -85,9 +85,6 @@ void peer_connected(struct lightningd *ld, const u8 *msg); void peer_disconnect_done(struct lightningd *ld, const u8 *msg); void peer_spoke(struct lightningd *ld, const u8 *msg); -/* May delete peer! */ -void peer_channels_cleanup_on_disconnect(struct peer *peer); - /* Could be configurable. */ #define OUR_CHANNEL_FLAGS CHANNEL_FLAGS_ANNOUNCE_CHANNEL From 9cff125590a30328660db9abacb6de1c524ca8c3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:27 +0930 Subject: [PATCH 1044/1530] common/gossip_store: fix leak on partial read. Very unusual, but it can happen, and we don't free: ``` lightningd-1 2022-07-12T04:21:22.591Z DEBUG gossipd: REPLY WIRE_GOSSIPD_DEV_MEMLEAK_REPLY with 0 fds lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: MEMLEAK: 0x55e73123d008 lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: label=common/gossip_store.c:92:u8[] lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: backtrace: lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: ccan/ccan/tal/tal.c:442 (tal_alloc_) lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: ccan/ccan/tal/tal.c:471 (tal_alloc_arr_) lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: common/gossip_store.c:92 (gossip_store_next) lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: connectd/multiplex.c:433 (maybe_from_gossip_store) lightningd-1 2022-07-12T04:21:22.645Z **BROKEN** connectd: connectd/multiplex.c:856 (write_to_peer) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: ccan/ccan/io/io.c:59 (next_plan) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: ccan/ccan/io/io.c:407 (do_plan) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: ccan/ccan/io/io.c:423 (io_ready) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: ccan/ccan/io/poll.c:453 (io_loop) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: connectd/connectd.c:2083 (main) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: ../sysdeps/nptl/libc_start_call_main.h:58 (__libc_start_call_main) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: ../csu/libc-start.c:392 (__libc_start_main_impl) lightningd-1 2022-07-12T04:21:22.646Z **BROKEN** connectd: parents: ``` --- common/gossip_store.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index c4f167a3d719..2522904e3a81 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -150,7 +150,7 @@ u8 *gossip_store_next(const tal_t *ctx, msg = tal_arr(ctx, u8, msglen); r = pread(*gossip_store_fd, msg, msglen, *off + r); if (r != msglen) - return NULL; + return tal_free(msg); if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) status_failed(STATUS_FAIL_INTERNAL_ERROR, From 719d1384d15b3bb782a7f09c14aec6d68edb7ed9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:27 +0930 Subject: [PATCH 1045/1530] connectd: give connections a chance to drain when lightningd says to disconnect, or peer disconnects. We want to avoid lost messages in the common cases. This generalizes our drain code, by giving the subds each 5 seconds to close themselves, but continue to allow them to send us traffic (if peer is still connected) and continue to send them traffic. We continue to send traffic *out* to the peer (if it's still connected), until all subds are gone. We still have a 5 second timer to close the connection to peer. On reconnects, we don't do this "drain period" on reconnects: we kill immediately. We fix up one test which was looking for the "disconnect" message explicitly. Signed-off-by: Rusty Russell --- connectd/connectd.c | 19 +++++-- connectd/multiplex.c | 104 +++++++++++++++++++++++++++------------ connectd/multiplex.h | 4 ++ tests/test_connection.py | 4 +- tests/test_opening.py | 2 +- 5 files changed, 94 insertions(+), 39 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 5d57fd8f717f..08a229ef0142 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -211,10 +211,15 @@ static void peer_connected_in(struct daemon *daemon, tal_free(connect); } -/*~ When we free a peer, we remove it from the daemon's hashtable */ +/*~ When we free a peer, we remove it from the daemon's hashtable. + * We also call this manually if we want to elegantly drain peer's + * queues. */ void destroy_peer(struct peer *peer) { - peer_htable_del(&peer->daemon->peers, peer); + assert(!peer->draining); + + if (!peer_htable_del(&peer->daemon->peers, peer)) + abort(); /* Tell gossipd to stop asking this peer gossip queries */ daemon_conn_send(peer->daemon->gossipd, @@ -225,6 +230,10 @@ void destroy_peer(struct peer *peer) take(towire_connectd_peer_disconnect_done(NULL, &peer->id, peer->counter))); + /* This makes multiplex.c routines not feed us more, but + * *also* means that if we're freed directly, the ->to_peer + * destructor won't call drain_peer(). */ + peer->draining = true; } /*~ This is where we create a new peer. */ @@ -282,7 +291,7 @@ struct io_plan *peer_connected(struct io_conn *conn, int subd_fd; bool option_gossip_queries; - /* We remove any previous connection, on the assumption it's dead */ + /* We remove any previous connection immediately, on the assumption it's dead */ peer = peer_htable_get(&daemon->peers, id); if (peer) tal_free(peer); @@ -1858,8 +1867,8 @@ static void peer_discard(struct daemon *daemon, const u8 *msg) /* If it's reconnected already, it will learn soon. */ if (peer->counter != counter) return; - status_peer_debug(&id, "disconnect"); - tal_free(peer); + status_peer_debug(&id, "discard_peer"); + drain_peer(peer); } /* lightningd tells us to send a msg and disconnect. */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 2ae1b75140d9..6d583cc0b32c 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -81,39 +81,76 @@ static struct subd *find_subd(struct peer *peer, return NULL; } +/* Except for a reconnection, we finally free a peer when the io_conn + * is closed and all subds are gone. */ +static void maybe_free_peer(struct peer *peer) +{ + if (peer->to_peer) + return; + if (tal_count(peer->subds) != 0) + return; + status_debug("maybe_free_peer freeing peer!"); + tal_free(peer); +} + /* We try to send the final messages, but if buffer is full and they're * not reading, we have to give up. */ -static void close_timeout(struct peer *peer) +static void close_peer_io_timeout(struct peer *peer) { /* BROKEN means we'll trigger CI if we see it, though it's possible */ status_peer_broken(&peer->id, "Peer did not close, forcing close"); - tal_free(peer); + io_close(peer->to_peer); } -/* We just want to send these messages out to the peer's connection, - * then close. We consider the peer dead to us (can be freed). */ -static void drain_peer(struct peer *peer) +static void close_subd_timeout(struct subd *subd) { + /* BROKEN means we'll trigger CI if we see it, though it's possible */ + status_peer_broken(&subd->peer->id, "Subd did not close, forcing close"); + io_close(subd->conn); +} + +void drain_peer(struct peer *peer) +{ + status_debug("drain_peer"); assert(!peer->draining); - /* This is a 5-second leak, worst case! */ - notleak(peer); + /* Since we immediately free any subds we didn't connect yet, + * we need peer->to_peer set so it won't free peer! */ + assert(peer->to_peer); + + /* Give the subds 5 seconds to close their fds to us. */ + for (size_t i = 0; i < tal_count(peer->subds); i++) { + if (!peer->subds[i]->conn) { + /* Deletes itself from array, so be careful! */ + tal_free(peer->subds[i]); + i--; + continue; + } + status_debug("drain_peer draining subd!"); + notleak(new_reltimer(&peer->daemon->timers, + peer->subds[i], time_from_sec(5), + close_subd_timeout, peer->subds[i])); + /* Wake any outgoing queued on subd */ + io_wake(peer->subds[i]->outq); + } - /* We no longer want subds feeding us more messages (they - * remove themselves from array when freed) */ - while (tal_count(peer->subds)) - tal_free(peer->subds[0]); - peer->draining = true; + /* Wake them to ensure they notice the close! */ + io_wake(&peer->subds); - /* You have 5 seconds to drain... */ - notleak(new_reltimer(&peer->daemon->timers, - peer, time_from_sec(5), - close_timeout, peer)); + if (peer->to_peer) { + /* You have 5 seconds to drain... */ + notleak(new_reltimer(&peer->daemon->timers, + peer->to_peer, time_from_sec(5), + close_peer_io_timeout, peer)); + } /* Clean peer from hashtable; we no longer exist. */ destroy_peer(peer); tal_del_destructor(peer, destroy_peer); + /* This is a 5-second leak, worst case! */ + notleak(peer); + /* Start draining process! */ io_wake(peer->peer_outq); } @@ -899,12 +936,13 @@ static struct io_plan *write_to_peer(struct io_conn *peer_conn, /* Still nothing to send? */ if (!msg) { - /* Draining? We're done. */ - if (peer->draining) + /* Draining? We're done when subds are done. */ + if (peer->draining && tal_count(peer->subds) == 0) return io_sock_shutdown(peer_conn); /* If they want us to send gossip, do so now. */ - msg = maybe_from_gossip_store(NULL, peer); + if (!peer->draining) + msg = maybe_from_gossip_store(NULL, peer); if (!msg) { /* Tell them to read again, */ io_wake(&peer->subds); @@ -992,6 +1030,9 @@ static void destroy_subd(struct subd *subd) /* Make sure we try to keep reading from peer (might * have been waiting for write_to_subd) */ io_wake(&peer->peer_in); + + /* Maybe we were last subd out? */ + maybe_free_peer(peer); } static struct subd *new_subd(struct peer *peer, @@ -1132,6 +1173,9 @@ static struct io_plan *subd_conn_init(struct io_conn *subd_conn, struct subd *subd) { subd->conn = subd_conn; + + /* subd is a child of the conn: free when it closes! */ + tal_steal(subd->conn, subd); return io_duplex(subd_conn, read_from_subd(subd_conn, subd), write_to_subd(subd_conn, subd)); @@ -1140,18 +1184,15 @@ static struct io_plan *subd_conn_init(struct io_conn *subd_conn, static void destroy_peer_conn(struct io_conn *peer_conn, struct peer *peer) { assert(peer->to_peer == peer_conn); - peer->to_peer = NULL; - /* Flush internal connections if any: last one out will free peer. */ - if (tal_count(peer->subds) != 0) { - for (size_t i = 0; i < tal_count(peer->subds); i++) - msg_wake(peer->subds[i]->outq); - return; - } + /* If subds need cleaning, this will do it */ + if (!peer->draining) + drain_peer(peer); - /* We never had any subds? Free peer (might already be being freed, - * as it's our parent, but that's allowed by tal). */ - tal_free(peer); + peer->to_peer = NULL; + + /* Or if there were no subds, this will free the peer. */ + maybe_free_peer(peer); } struct io_plan *multiplex_peer_setup(struct io_conn *peer_conn, @@ -1203,8 +1244,9 @@ void peer_connect_subd(struct daemon *daemon, const u8 *msg, int fd) subd = new_subd(peer, &channel_id); assert(!subd->conn); - /* This sets subd->conn inside subd_conn_init */ - io_new_conn(subd, fd, subd_conn_init, subd); + + /* This sets subd->conn inside subd_conn_init, and reparents subd! */ + io_new_conn(peer, fd, subd_conn_init, subd); } /* Lightningd says to send a ping */ diff --git a/connectd/multiplex.h b/connectd/multiplex.h index 77a41fa2aae4..a3bda8ad3d24 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -22,6 +22,10 @@ void multiplex_final_msg(struct peer *peer, * this does io logging. */ void inject_peer_msg(struct peer *peer, const u8 *msg TAKES); +/* Start closing the peer: removes itself from hash table, frees itself + * once done. */ +void drain_peer(struct peer *peer); + void setup_peer_gossip_store(struct peer *peer, const struct feature_set *our_features, const u8 *their_features); diff --git a/tests/test_connection.py b/tests/test_connection.py index 24c7cec89814..6b6d33ba2f03 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2869,8 +2869,8 @@ def test_opener_feerate_reconnect(node_factory, bitcoind): l2.daemon.wait_for_log(r'dev_disconnect: \-WIRE_COMMITMENT_SIGNED') # Wait until they reconnect. - l1.daemon.wait_for_log('Peer transient failure in CHANNELD_NORMAL') - l1.daemon.wait_for_log('peer_disconnect_done') + l1.daemon.wait_for_logs(['Peer transient failure in CHANNELD_NORMAL', + 'peer_disconnect_done']) wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected']) # Should work normally. diff --git a/tests/test_opening.py b/tests/test_opening.py index 3a454d50e95b..8d63b2a929db 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -354,7 +354,7 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): # l1 leases a channel from l2 l1.rpc.connect(l2.info['id'], 'localhost', l2.port) rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) - l1.daemon.wait_for_log('disconnect') + wait_for(lambda: l1.rpc.listpeers()['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) chan_id = l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, feerate='{}perkw'.format(feerate), From c415c80d487ead3ce4338a818f0070be504f001a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1046/1530] connectd: spelling and typo fixes. From @niftynei. Signed-off-by: Rusty Russell --- connectd/multiplex.c | 2 +- lightningd/peer_control.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/connectd/multiplex.c b/connectd/multiplex.c index 6d583cc0b32c..b4f95107d0c4 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -1112,7 +1112,7 @@ static struct io_plan *read_body_from_peer_done(struct io_conn *peer_conn, return read_hdr_from_peer(peer_conn, peer); } - /* If we don't find a subdaemon for this, crteat a new one. */ + /* If we don't find a subdaemon for this, create a new one. */ subd = find_subd(peer, &channel_id); if (!subd) { enum peer_wire t = fromwire_peektype(decrypted); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 785ad32cbfd2..3dfebb77c131 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1305,7 +1305,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* When a peer disconnects, we give subds time to clean themselves up * (this lets connectd ensure they've seen the final messages). But - * nowe it's reconnected, we've gotta force them out. */ + * now it's reconnected, we've gotta force them out. */ peer_channels_cleanup(ld, &id); /* If we're already dealing with this peer, hand off to correct @@ -1320,7 +1320,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) * already reconnected: we would tell it again when we read the * "peer_connected" message, and it would get upset (plus, our first * subd wouldn't die as expected. So we echo this back to connectd - * on peer commands, and it knows to ignore if it wrong. */ + * on peer commands, and it knows to ignore if it's wrong. */ peer->connectd_counter = connectd_counter; /* We mark peer in "connecting" state until hooks have passed. */ From c57a5a0a06c9fd5e8b4b8674f6bebaeddb6eea98 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1047/1530] gossipd: downgrade broken message that can actually happen. I saw this in test_bech32_funding: the peer disconnected and told gossipd before lightningd relayed a local_channel_announcement from the subd: ``` lightningd-1 2022-07-14T08:46:32.352Z DEBUG 020ba48216be53051ba8c661c641b5d9c3547c44bfcc43bf4d8362f0dfce0e950d-channeld-chan#1: billboard: Funding transaction locked. Channel announced. lightningd-1 2022-07-14T08:46:33.353Z DEBUG connectd: drain_peer lightningd-1 2022-07-14T08:46:33.353Z DEBUG connectd: drain_peer draining subd! lightningd-1 2022-07-14T08:46:33.353Z DEBUG 020ba48216be53051ba8c661c641b5d9c3547c44bfcc43bf4d8362f0dfce0e950d-lightningd: peer_disconnect_done lightningd-1 2022-07-14T08:46:33.354Z INFO 020ba48216be53051ba8c661c641b5d9c3547c44bfcc43bf4d8362f0dfce0e950d-channeld-chan#1: Peer connection lost lightningd-1 2022-07-14T08:46:33.354Z INFO 020ba48216be53051ba8c661c641b5d9c3547c44bfcc43bf4d8362f0dfce0e950d-chan#1: Peer transient failure in CHANNELD_NORMAL: channeld: Owning subdaemon channeld died (62208) lightningd-1 2022-07-14T08:46:33.354Z DEBUG connectd: maybe_free_peer freeing peer! lightningd-1 2022-07-14T08:46:33.354Z DEBUG 0228af54fd951097caa2ceea5546a37bcc7d7f746e1cb7cb549e3edcd1797a1d80-hsmd: Got WIRE_HSMD_CUPDATE_SIG_REQ lightningd-1 2022-07-14T08:46:33.354Z DEBUG plugin-funder: Cleaning up inflights for peer id 020ba48216be53051ba8c661c641b5d9c3547c44bfcc43bf4d8362f0dfce0e950d lightningd-1 2022-07-14T08:46:33.354Z **BROKEN** gossipd: Unknown peer 020ba48216be53051ba8c661c641b5d9c3547c44bfcc43bf4d8362f0dfce0e950d for local_channel_announcement ``` Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index e27f96920717..55321e1dbce2 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -300,8 +300,8 @@ static void handle_local_channel_announcement(struct daemon *daemon, const u8 *m /* We treat it OK even if peer has disconnected since (unlikely though!) */ peer = find_peer(daemon, &id); if (!peer) - status_broken("Unknown peer %s for local_channel_announcement", - type_to_string(tmpctx, struct node_id, &id)); + status_debug("Unknown peer %s for local_channel_announcement", + type_to_string(tmpctx, struct node_id, &id)); err = handle_channel_announcement_msg(daemon, peer, cannouncement); if (err) { From e15e55190b8ce35440f626e8648fa3844203bdb5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1048/1530] lightningd: provide peer address for reconnect if connect fails. It usually works out due to other reconnections, but I noticed this diagnosing another test. Signed-off-by: Rusty Russell --- lightningd/connect_control.c | 6 ++++-- lightningd/connect_control.h | 4 +++- lightningd/peer_control.c | 5 +++-- lightningd/test/run-invoice-select-inchan.c | 4 +++- wallet/test/run-wallet.c | 4 +++- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index aaf2f30babc8..b7bff6888f95 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -366,10 +366,12 @@ static void connect_failed(struct lightningd *ld, } } -void connect_failed_disconnect(struct lightningd *ld, const struct node_id *id) +void connect_failed_disconnect(struct lightningd *ld, + const struct node_id *id, + const struct wireaddr_internal *addrhint) { connect_failed(ld, id, CONNECT_DISCONNECTED_DURING, - "disconnected during connection", 1, NULL); + "disconnected during connection", 1, addrhint); } static void handle_connect_failed(struct lightningd *ld, const u8 *msg) diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 6e836478f077..ab63b59acc7c 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -20,7 +20,9 @@ void try_reconnect(const tal_t *ctx, void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr); -void connect_failed_disconnect(struct lightningd *ld, const struct node_id *id); +void connect_failed_disconnect(struct lightningd *ld, + const struct node_id *id, + const struct wireaddr_internal *addr); /* Disconnect a peer (if no subds want to talk any more) */ void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 3dfebb77c131..db86e3869acf 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1540,11 +1540,12 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) /* If there are literally no channels, might as well * free immediately. */ if (!p->uncommitted_channel && list_empty(&p->channels)) - tal_free(p); + p = tal_free(p); } /* If you were trying to connect, it failed. */ - connect_failed_disconnect(ld, &id); + connect_failed_disconnect(ld, &id, + p && !p->connected_incoming ? &p->addr : NULL); /* Fire off plugin notifications */ notify_disconnect(ld, &id); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 71606b0fb9dd..dcb0bb7b10fd 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -148,7 +148,9 @@ struct command_result *command_success(struct command *cmd UNNEEDED, { fprintf(stderr, "command_success called!\n"); abort(); } /* Generated stub for connect_failed_disconnect */ -void connect_failed_disconnect(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED) +void connect_failed_disconnect(struct lightningd *ld UNNEEDED, + const struct node_id *id UNNEEDED, + const struct wireaddr_internal *addr UNNEEDED) { fprintf(stderr, "connect_failed_disconnect called!\n"); abort(); } /* Generated stub for connect_succeeded */ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 8e0384eccb96..6b3fda3f5a67 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -107,7 +107,9 @@ struct command_result *command_success(struct command *cmd UNNEEDED, { fprintf(stderr, "command_success called!\n"); abort(); } /* Generated stub for connect_failed_disconnect */ -void connect_failed_disconnect(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED) +void connect_failed_disconnect(struct lightningd *ld UNNEEDED, + const struct node_id *id UNNEEDED, + const struct wireaddr_internal *addr UNNEEDED) { fprintf(stderr, "connect_failed_disconnect called!\n"); abort(); } /* Generated stub for connect_succeeded */ void connect_succeeded(struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED, From e59e12dcb64c92667619e9bed8b414741f726d0a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1049/1530] lightningd: don't forget peer if it's still connected. In particular, when onchaind finishes with a channel, and we delete it, we would forget about the peer, even if it's still connected. That leads to a surprise if we are activated because of something it sends: ``` 2022-07-16T09:07:51.8668176Z lightningd-1 2022-07-16T08:54:32.497Z INFO 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-chan#1: Peer transient failure in AWAITING_UNILATERAL: Disconnected 2022-07-16T09:07:51.8668717Z lightningd-1 2022-07-16T08:54:32.497Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-lightningd: Will try reconnect in 1 seconds 2022-07-16T09:07:51.8669323Z lightningd-1 2022-07-16T08:54:32.497Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-chan#1: Peer has reconnected, state AWAITING_UNILATERAL: connecting subd 2022-07-16T09:07:51.8671225Z lightningd-1 2022-07-16T08:54:32.497Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-chan#1: Telling connectd to send error 001185a48d443eae6fbcc679accd4d497c4183b711f2cd204c0b50acd3cd76fda08d00936368616e6e656c643a207265636569766564204552524f52206572726f72206368616e6e656c20383561343864343433656165366662636336373961636364346434393763343138336237313166326364323034633062353061636433636437366664613038643a20466f726369626c7920636c6f7365642062792060636c6f73656020636f6d6d616e642074696d656f7574 2022-07-16T09:07:51.8671786Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_in WIRE_GOSSIP_TIMESTAMP_FILTER 2022-07-16T09:07:51.8672270Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-hsmd: Got WIRE_HSMD_ECDH_REQ 2022-07-16T09:07:51.8673027Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-onchaind-chan#1: billboard: All outputs resolved: waiting 0 more blocks before forgetting channel 2022-07-16T09:07:51.8673419Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG gossipd: REPLY WIRE_GOSSIPD_GET_ADDRS_REPLY with 0 fds 2022-07-16T09:07:51.8673954Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: peer_out WIRE_GOSSIP_TIMESTAMP_FILTER 2022-07-16T09:07:51.8674298Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG hsmd: Client: Received message 1 from client 2022-07-16T09:07:51.8674811Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-gossipd: seeker: chosen as startup peer 2022-07-16T09:07:51.8675330Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-connectd: Activating for message WIRE_ERROR 2022-07-16T09:07:51.8675825Z lightningd-2 2022-07-16T08:54:32.497Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-hsmd: Got WIRE_HSMD_ECDH_REQ ... 2022-07-16T09:07:51.9503144Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: FATAL SIGNAL 11 (version 6e6b41d) 2022-07-16T09:07:51.9503563Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: common/daemon.c:38 (send_backtrace) 0x5620882dbffb 2022-07-16T09:07:51.9503970Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: common/daemon.c:46 (crashdump) 0x5620882dc04d 2022-07-16T09:07:51.9504534Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: /build/glibc-SzIz7B/glibc-2.31/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 ((null)) 0x7ffb3abdf08f 2022-07-16T09:07:51.9504973Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: lightningd/peer_control.c:1378 (peer_spoke) 0x5620882aedd7 2022-07-16T09:07:51.9505418Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: lightningd/connect_control.c:492 (connectd_msg) 0x56208828e9db 2022-07-16T09:07:51.9505835Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: lightningd/subd.c:557 (sd_msg_read) 0x5620882bd89a 2022-07-16T09:07:51.9506236Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:59 (next_plan) 0x5620883318d4 2022-07-16T09:07:51.9506618Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:407 (do_plan) 0x562088331da1 2022-07-16T09:07:51.9507021Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: ccan/ccan/io/io.c:417 (io_ready) 0x562088331e3e 2022-07-16T09:07:51.9507428Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: ccan/ccan/io/poll.c:453 (io_loop) 0x5620883337d3 2022-07-16T09:07:51.9507945Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: lightningd/io_loop_with_timers.c:22 (io_loop_with_timers) 0x5620882969f5 2022-07-16T09:07:51.9508368Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: lightningd/lightningd.c:1190 (main) 0x56208829a7bb 2022-07-16T09:07:51.9508804Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: ../csu/libc-start.c:308 (__libc_start_main) 0x7ffb3abc0082 2022-07-16T09:07:51.9509172Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0x56208827d32d 2022-07-16T09:07:51.9509552Z lightningd-2 2022-07-16T08:54:32.737Z **BROKEN** lightningd: backtrace: (null):0 ((null)) 0xffffffffffffffff ``` Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index db86e3869acf..3bcd45eea3b2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -133,6 +133,9 @@ void maybe_delete_peer(struct peer *peer) } return; } + /* Maybe it's reconnected / reconnecting? */ + if (peer->connected != PEER_DISCONNECTED) + return; delete_peer(peer); } From aec307f7ba760efcf1eea3d2ce87a9012188625a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1050/1530] multifundchannel: fix race where we restart fundchannel. Disconnecting a peer after openingd fails is not instantaneous: we abort the open, so openingd sends out a WIRE_ERROR which makes connectd close the connection. As a result this test fails often. The simplest fix is to wait for a second in multifundchannel before retrying, which is also robust against behaviour changes if we decide *not* to disconnect in future. Also make sure that addrhint ownership is correct, since this can lead to a use-after-free if we filter dests. ``` tests/test_connection.py::test_multifunding_best_effort FAILED [100%] ======================================================= FAILURES ======================================================== _____________________________________________ test_multifunding_best_effort _____________________________________________ node_factory = bitcoind = @pytest.mark.openchannel('v1') @pytest.mark.developer("disconnect=... needs DEVELOPER=1") def test_multifunding_best_effort(node_factory, bitcoind): ''' Check that best_effort flag works. ''' disconnects = ["-WIRE_INIT", "-WIRE_ACCEPT_CHANNEL", "-WIRE_FUNDING_SIGNED"] l1 = node_factory.get_node() l2 = node_factory.get_node() l3 = node_factory.get_node(disconnect=disconnects) l4 = node_factory.get_node() l1.fundwallet(2000000) destinations = [{"id": '{}@localhost:{}'.format(l2.info['id'], l2.port), "amount": 50000}, {"id": '{}@localhost:{}'.format(l3.info['id'], l3.port), "amount": 50000}, {"id": '{}@localhost:{}'.format(l4.info['id'], l4.port), "amount": 50000}] for i, d in enumerate(disconnects): # Should succeed due to best-effort flag. > l1.rpc.multifundchannel(destinations, minchannels=2) tests/test_connection.py:2070: ... > raise RpcError(method, payload, resp['error']) E pyln.client.lightning.RpcError: RPC call failed: method: multifundchannel, payload: {'destinations': [{'id': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59@localhost:41023', 'amount': 50000}, {'id': '035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d@localhost:41977', 'amount': 50000}, {'id': '0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199@localhost:34943', 'amount': 50000}], 'minchannels': 2}, error: {'code': 305, 'message': 'Peer not connected at start', 'data': {'id': '0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199', 'method': 'fundchannel_start'}} ``` Signed-off-by: Rusty Russell --- plugins/spender/multifundchannel.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 2c8a02ce577e..4b9c1c6c33fe 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -1702,10 +1703,10 @@ perform_multiconnect(struct multifundchannel_command *mfc) /* Initiate the multifundchannel execution. */ -static struct command_result * +static void perform_multifundchannel(struct multifundchannel_command *mfc) { - return perform_multiconnect(mfc); + perform_multiconnect(mfc); } @@ -1792,6 +1793,10 @@ post_cleanup_redo_multifundchannel(struct multifundchannel_redo *redo) */ /* Re-add to new destinations. */ tal_arr_expand(&new_destinations, *dest); + /* FIXME: If this were an array of pointers, + * we could make dest itself the parent of + * ->addrhint and not need this wart! */ + tal_steal(new_destinations, dest->addrhint); } } mfc->destinations = new_destinations; @@ -1825,8 +1830,11 @@ post_cleanup_redo_multifundchannel(struct multifundchannel_redo *redo) return mfc_finished(mfc, out); } - /* Okay, we still have destinations to try --- reinvoke. */ - return perform_multifundchannel(mfc); + /* Okay, we still have destinations to try: wait a second in case it + * takes that long to disconnect from peer, then retry. */ + notleak(plugin_timer(mfc->cmd->plugin, time_from_sec(1), + perform_multifundchannel, mfc)); + return command_still_pending(mfc->cmd); } struct command_result * @@ -2059,7 +2067,8 @@ json_multifundchannel(struct command *cmd, /* Stop memleak from complaining */ tal_free(minconf); - return perform_multifundchannel(mfc); + perform_multifundchannel(mfc); + return command_still_pending(mfc->cmd); } const struct plugin_command multifundchannel_commands[] = { From 2962b931990f3ad2db28ca44737e52872e1a586e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1051/1530] pytest: don't assume disconnect finished atomically, and suppress interfering redirects. In various places, we assumed that when `connected` is false, everything is finished. This is not true: we should wait for the state we expect. In addition, various places allows reconnections, which interfered with the logic; suppress them. Signed-off-by: Rusty Russell --- tests/test_closing.py | 31 ++++++++++++++++++------------- tests/test_connection.py | 38 ++++++++++++++++++++++++++------------ tests/test_gossip.py | 2 +- tests/test_misc.py | 7 +++++-- tests/test_opening.py | 13 +++++++++++-- 5 files changed, 61 insertions(+), 30 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index d7e678eab2b1..bb3d479d0e92 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -173,8 +173,10 @@ def test_closing_id(node_factory): l1.fundchannel(l2, 10**6) cid = l2.rpc.listpeers()['peers'][0]['channels'][0]['channel_id'] l2.rpc.close(cid) - wait_for(lambda: not only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']) - wait_for(lambda: not only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected']) + # Technically, l2 disconnects before l1 finishes analyzing the final msg. + # Wait for them to both consider it closed! + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']])) + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']])) # Close by peer ID. l2.rpc.connect(l1.info['id'], 'localhost', l1.port) @@ -182,8 +184,8 @@ def test_closing_id(node_factory): l2.fundchannel(l1, 10**6) pid = l1.info['id'] l2.rpc.close(pid) - wait_for(lambda: not only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']) - wait_for(lambda: not only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected']) + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']])) + wait_for(lambda: any([c['state'] == 'CLOSINGD_COMPLETE' for c in only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']])) @unittest.skipIf(TEST_NETWORK != 'regtest', 'FIXME: broken under elements') @@ -776,7 +778,8 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') opts = {'funder-policy': 'match', 'funder-policy-mod': 100, 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, - 'may_reconnect': True, 'plugin': coin_mvt_plugin} + 'may_reconnect': True, 'plugin': coin_mvt_plugin, + 'dev-no-reconnect': None} l1, l2, = node_factory.get_nodes(2, opts=opts) @@ -1056,17 +1059,18 @@ def test_channel_lease_lessor_cheat(node_factory, bitcoind, chainparams): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") -@pytest.mark.developer("requres 'dev-queryrates'") +@pytest.mark.developer("requres 'dev-queryrates', dev-no-reconnect") def test_channel_lease_lessee_cheat(node_factory, bitcoind, chainparams): ''' Check that lessor can recover funds if lessee cheats ''' opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, - 'may_reconnect': True, 'allow_broken_log': True}, + 'may_reconnect': True, 'dev-no-reconnect': None, + 'allow_broken_log': True}, {'funder-policy': 'match', 'funder-policy-mod': 100, 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, - 'may_reconnect': True}] + 'may_reconnect': True, 'dev-no-reconnect': None}] l1, l2, = node_factory.get_nodes(2, opts=opts) amount = 500000 feerate = 2000 @@ -1098,7 +1102,7 @@ def test_channel_lease_lessee_cheat(node_factory, bitcoind, chainparams): l1_db_path_bak = os.path.join(l1.daemon.lightning_dir, chainparams['name'], 'lightningd.sqlite3.bak') copyfile(l1_db_path, l1_db_path_bak) l1.start() - l1.rpc.connect(l1.info['id'], 'localhost', l1.port) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) sync_blockheight(bitcoind, [l1]) # push some money from l2->l1, so the commit counter advances @@ -2669,8 +2673,8 @@ def test_onchain_different_fees(node_factory, bitcoind, executor): # Now, 100 blocks it should be done. bitcoind.generate_block(100) - wait_for(lambda: l1.rpc.listpeers()['peers'] == []) - wait_for(lambda: l2.rpc.listpeers()['peers'] == []) + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) + wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) @pytest.mark.developer("needs DEVELOPER=1") @@ -3443,9 +3447,10 @@ def test_you_forgot_closed_channel(node_factory, executor): 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' + # l2 closes on us. + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is False) + # l1 reconnects, it should succeed. - if only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']: - l1.rpc.disconnect(l2.info['id'], force=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) fut.result(TIMEOUT) diff --git a/tests/test_connection.py b/tests/test_connection.py index 6b6d33ba2f03..52d33d484a5d 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -284,7 +284,7 @@ def test_bad_opening(node_factory): l2.daemon.wait_for_log('to_self_delay 100 larger than 99') -@pytest.mark.developer("gossip without DEVELOPER=1 is slow") +@pytest.mark.developer("gossip without DEVELOPER=1 is slow, need dev-no-reconnect") @unittest.skipIf(TEST_NETWORK != 'regtest', "Fee computation and limits are network specific") @pytest.mark.slow_test @pytest.mark.openchannel('v1') @@ -319,10 +319,10 @@ def test_opening_tiny_channel(node_factory): l3_min_capacity = 10000 # the current default l4_min_capacity = 20000 # a server with more than default minimum - opts = [{'min-capacity-sat': 0}, - {'min-capacity-sat': l2_min_capacity}, - {'min-capacity-sat': l3_min_capacity}, - {'min-capacity-sat': l4_min_capacity}] + opts = [{'min-capacity-sat': 0, 'dev-no-reconnect': None}, + {'min-capacity-sat': l2_min_capacity, 'dev-no-reconnect': None}, + {'min-capacity-sat': l3_min_capacity, 'dev-no-reconnect': None}, + {'min-capacity-sat': l4_min_capacity, 'dev-no-reconnect': None}] l1, l2, l3, l4 = node_factory.get_nodes(4, opts=opts) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.connect(l3.info['id'], 'localhost', l3.port) @@ -330,16 +330,19 @@ def test_opening_tiny_channel(node_factory): with pytest.raises(RpcError, match=r'They sent [error|warning].*channel capacity is .*, which is below .*sat'): l1.fundchannel(l2, l2_min_capacity + overhead - 1) + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.fundchannel(l2, l2_min_capacity + overhead) with pytest.raises(RpcError, match=r'They sent [error|warning].*channel capacity is .*, which is below .*sat'): l1.fundchannel(l3, l3_min_capacity + overhead - 1) + wait_for(lambda: l1.rpc.listpeers(l3.info['id'])['peers'] == []) l1.rpc.connect(l3.info['id'], 'localhost', l3.port) l1.fundchannel(l3, l3_min_capacity + overhead) with pytest.raises(RpcError, match=r'They sent [error|warning].*channel capacity is .*, which is below .*sat'): l1.fundchannel(l4, l4_min_capacity + overhead - 1) + wait_for(lambda: l1.rpc.listpeers(l4.info['id'])['peers'] == []) l1.rpc.connect(l4.info['id'], 'localhost', l4.port) l1.fundchannel(l4, l4_min_capacity + overhead) @@ -348,6 +351,7 @@ def test_opening_tiny_channel(node_factory): l3.rpc.connect(l2.info['id'], 'localhost', l2.port) with pytest.raises(RpcError, match=r"channel capacity is .*, which is below .*sat"): l3.fundchannel(l2, l3_min_capacity + overhead - 1) + wait_for(lambda: l3.rpc.listpeers(l2.info['id'])['peers'] == []) l3.rpc.connect(l2.info['id'], 'localhost', l2.port) l3.fundchannel(l2, l3_min_capacity + overhead) @@ -1213,8 +1217,8 @@ def test_funding_by_utxos(node_factory, bitcoind): @pytest.mark.developer("needs dev_forget_channel") @pytest.mark.openchannel('v1') def test_funding_external_wallet_corners(node_factory, bitcoind): - l1 = node_factory.get_node(may_reconnect=True) - l2 = node_factory.get_node(may_reconnect=True) + l1, l2 = node_factory.get_nodes(2, opts={'may_reconnect': True, + 'dev-no-reconnect': None}) amount = 2**24 l1.fundwallet(amount + 10000000) @@ -1265,6 +1269,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): l1.rpc.fundchannel_cancel(l2.info['id']) # Cancelling causes disconnection. + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount2 = 1000000 funding_addr = l1.rpc.fundchannel_start(l2.info['id'], amount2)['funding_address'] @@ -1286,6 +1291,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): # But must unreserve inputs manually. l1.rpc.txdiscard(prep['txid']) + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) funding_addr = l1.rpc.fundchannel_start(l2.info['id'], amount)['funding_address'] prep = l1.rpc.txprepare([{funding_addr: amount}]) @@ -1307,6 +1313,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): == 'CHANNELD_AWAITING_LOCKIN') # on reconnect, channel should get destroyed + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_log('Unknown channel .* for WIRE_CHANNEL_REESTABLISH') wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 0) @@ -1316,6 +1323,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): l1.rpc.txdiscard(prep['txid']) # we have to connect again, because we got disconnected when everything errored + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) funding_addr = l1.rpc.fundchannel_start(l2.info['id'], amount)['funding_address'] prep = l1.rpc.txprepare([{funding_addr: amount}]) @@ -1891,9 +1899,10 @@ def test_multifunding_disconnect(node_factory): disconnects = ["-WIRE_INIT", "-WIRE_ACCEPT_CHANNEL", "+WIRE_ACCEPT_CHANNEL"] - l1 = node_factory.get_node() - l2 = node_factory.get_node(disconnect=disconnects) - l3 = node_factory.get_node() + l1, l2, l3 = node_factory.get_nodes(3, opts=[{'dev-no-reconnect': None}, + {'dev-no-reconnect': None, + 'disconnect': disconnects}, + {'dev-no-reconnect': None}]) l1.fundwallet(2000000) @@ -1907,6 +1916,7 @@ def test_multifunding_disconnect(node_factory): for d in disconnects: with pytest.raises(RpcError): l1.rpc.multifundchannel(destinations) + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) # TODO: failing at the fundchannel_complete phase # (-WIRE_FUNDING_SIGNED +-WIRE_FUNDING_SIGNED) @@ -1946,6 +1956,9 @@ def test_multifunding_wumbo(node_factory): with pytest.raises(RpcError, match='Amount exceeded'): l1.rpc.multifundchannel(destinations) + # Make sure it's disconnected from l2 before retrying. + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) + # This should succeed. destinations = [{"id": '{}@localhost:{}'.format(l2.info['id'], l2.port), "amount": 1 << 24}, @@ -2538,6 +2551,7 @@ def test_multiple_channels(node_factory): l2.daemon.wait_for_log( r'State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE' ) + wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) channels = only_one(l1.rpc.listpeers()['peers'])['channels'] assert len(channels) == 3 @@ -2567,7 +2581,7 @@ def test_forget_channel(node_factory): # Forcing should work l1.rpc.dev_forget_channel(l2.info['id'], True) - assert len(l1.rpc.listpeers()['peers']) == 0 + wait_for(lambda: l1.rpc.listpeers()['peers'] == []) # And restarting should keep that peer forgotten l1.restart() @@ -2715,7 +2729,7 @@ def mock_donothing(r): l2.daemon.wait_for_log(r'Forgetting channel: It has been {}\d blocks'.format(str(blocks)[:-1])) # fundee will also forget and disconnect from peer. - assert len(l2.rpc.listpeers(l1.info['id'])['peers']) == 0 + wait_for(lambda: l2.rpc.listpeers(l1.info['id'])['peers'] == []) @pytest.mark.developer("needs --dev-max-funding-unconfirmed-blocks") diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 0a217e7e1bda..adb72faadf4e 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -265,7 +265,7 @@ def test_announce_dns_without_port(node_factory, bitcoind): def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): # Updates get backdated 5 seconds with --dev-fast-gossip. backdate = 5 - l1, l2, l3, l4 = node_factory.line_graph(4, fundchannel=False) + l1, l2, l3, l4 = node_factory.line_graph(4, fundchannel=False, opts={'log-level': 'io'}) genesis_blockhash = chainparams['chain_hash'] before_anything = int(time.time()) diff --git a/tests/test_misc.py b/tests/test_misc.py index 7b6bad0cdd38..630e110f6b2c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1286,9 +1286,12 @@ def test_bitcoind_goes_backwards(node_factory, bitcoind): @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') +@pytest.mark.developer("needs dev-no-reconnect") def test_reserve_enforcement(node_factory, executor): """Channeld should disallow you spending into your reserve""" - l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True, 'allow_warning': True}) + l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True, + 'dev-no-reconnect': None, + 'allow_warning': True}) # Pay 1000 satoshi to l2. l1.pay(l2, 1000000) @@ -1302,7 +1305,7 @@ def test_reserve_enforcement(node_factory, executor): l2.db.execute('UPDATE channel_configs SET channel_reserve_satoshis=0') l2.start() - wait_for(lambda: only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected']) + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) # This should be impossible to pay entire thing back: l1 should warn and # close connection for trying to violate reserve. diff --git a/tests/test_opening.py b/tests/test_opening.py index 8d63b2a929db..29f3ee54c251 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -21,7 +21,7 @@ def find_next_feerate(node, peer): @pytest.mark.openchannel('v2') @pytest.mark.developer("requres 'dev-queryrates'") def test_queryrates(node_factory, bitcoind): - l1, l2 = node_factory.get_nodes(2) + l1, l2 = node_factory.get_nodes(2, opts={'dev-no-reconnect': None}) amount = 10 ** 6 @@ -42,6 +42,7 @@ def test_queryrates(node_factory, bitcoind): 'channel_fee_max_base_msat': '3sat', 'channel_fee_max_proportional_thousandths': 101}) + wait_for(lambda: l1.rpc.listpeers()['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) result = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) assert result['our_funding_msat'] == Millisatoshi(amount * 1000) @@ -128,6 +129,10 @@ def get_funded_channel_scid(n1, n2): for node in node_list: node.daemon.wait_for_log(r'to CLOSINGD_COMPLETE') + # Make sure disconnections are complete + if not failed_sign: + wait_for(lambda: all([c['connected'] is False for c in l1.rpc.listpeers()['peers']])) + # With 2 down, it will fail to fund channel l2.stop() l3.stop() @@ -416,10 +421,12 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.developer("uses dev-no-reconnect") @pytest.mark.openchannel('v2') def test_v2_rbf_multi(node_factory, bitcoind, chainparams): l1, l2 = node_factory.get_nodes(2, opts={'may_reconnect': True, + 'dev-no-reconnect': None, 'allow_warning': True}) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -463,6 +470,7 @@ def test_v2_rbf_multi(node_factory, bitcoind, chainparams): # Abort this open attempt! We will re-try aborted = l1.rpc.openchannel_abort(chan_id) assert not aborted['channel_canceled'] + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is False) # Do the bump, again, same feerate l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -653,7 +661,7 @@ def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) with pytest.raises(RpcError): l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) - assert l1.rpc.getpeer(l2.info['id']) is not None + wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected'] is False) # Now we finish off the completes failure check for d in disconnects[-2:]: @@ -661,6 +669,7 @@ def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) with pytest.raises(RpcError): update = l1.rpc.openchannel_update(chan_id, bump['psbt']) + wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected'] is False) # Now we succeed l1.rpc.connect(l2.info['id'], 'localhost', l2.port) From a3c4908f4a33aae31c433106e1069bc761a7202f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1052/1530] lightningd: don't explicitly tell connectd to disconnect, have it do it on sending error/warning. Connectd already does this when we *receive* an error or warning, but now do it on send. This causes some slight behavior change: we don't disconnect when we close a channel, for example (our behaviour here has been inconsistent across versions, depending on the code). When connectd is told to disconnect, it now does so immediately, and doesn't wait for subds to drain etc. That simplifies the manual disconnect case, which now cleans up as it would from any other disconnection when connectd says it's disconnected. Signed-off-by: Rusty Russell --- connectd/connectd.c | 2 +- connectd/multiplex.c | 23 ++++++- connectd/multiplex.h | 4 -- lightningd/channel.c | 6 +- lightningd/connect_control.c | 27 -------- lightningd/connect_control.h | 3 - lightningd/opening_common.c | 1 - lightningd/peer_control.c | 69 +++------------------ lightningd/test/run-invoice-select-inchan.c | 10 +-- lightningd/test/run-log-pruning.c | 4 +- plugins/test/run-route-overlong.c | 10 +-- tests/test_closing.py | 13 ++-- tests/test_connection.py | 32 ++++------ tests/test_db.py | 4 +- tests/test_plugin.py | 3 +- wallet/test/run-wallet.c | 10 +-- 16 files changed, 75 insertions(+), 146 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index 08a229ef0142..ac52cc42175f 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1868,7 +1868,7 @@ static void peer_discard(struct daemon *daemon, const u8 *msg) if (peer->counter != counter) return; status_peer_debug(&id, "discard_peer"); - drain_peer(peer); + tal_free(peer); } /* lightningd tells us to send a msg and disconnect. */ diff --git a/connectd/multiplex.c b/connectd/multiplex.c index b4f95107d0c4..ae7f7585b73d 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -109,7 +109,7 @@ static void close_subd_timeout(struct subd *subd) io_close(subd->conn); } -void drain_peer(struct peer *peer) +static void drain_peer(struct peer *peer) { status_debug("drain_peer"); assert(!peer->draining); @@ -403,6 +403,12 @@ static bool is_urgent(enum peer_wire type) return false; } +/* io_sock_shutdown, but in format suitable for an io_plan callback */ +static struct io_plan *io_sock_shutdown_cb(struct io_conn *conn, struct peer *unused) +{ + return io_sock_shutdown(conn); +} + static struct io_plan *encrypt_and_send(struct peer *peer, const u8 *msg TAKES, struct io_plan *(*next) @@ -438,6 +444,21 @@ static struct io_plan *encrypt_and_send(struct peer *peer, #endif set_urgent_flag(peer, is_urgent(type)); + /* BOLT #1: + * + * A sending node: + *... + * - MAY close the connection after sending. + */ + if (type == WIRE_ERROR || type == WIRE_WARNING) { + /* Might already be draining... */ + if (!peer->draining) + drain_peer(peer); + + /* Close as soon as we've sent this. */ + next = io_sock_shutdown_cb; + } + /* We free this and the encrypted version in next write_to_peer */ peer->sent_to_peer = cryptomsg_encrypt_msg(peer, &peer->cs, msg); return io_write(peer->to_peer, diff --git a/connectd/multiplex.h b/connectd/multiplex.h index a3bda8ad3d24..77a41fa2aae4 100644 --- a/connectd/multiplex.h +++ b/connectd/multiplex.h @@ -22,10 +22,6 @@ void multiplex_final_msg(struct peer *peer, * this does io logging. */ void inject_peer_msg(struct peer *peer, const u8 *msg TAKES); -/* Start closing the peer: removes itself from hash table, frees itself - * once done. */ -void drain_peer(struct peer *peer); - void setup_peer_gossip_store(struct peer *peer, const struct feature_set *our_features, const u8 *their_features); diff --git a/lightningd/channel.c b/lightningd/channel.c index 735b9ab68505..b190aec5a443 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -22,14 +22,10 @@ void channel_set_owner(struct channel *channel, struct subd *owner) { struct subd *old_owner = channel->owner; - bool was_connected = channel_is_connected(channel); channel->owner = owner; - if (old_owner) { + if (old_owner) subd_release_channel(old_owner, channel); - if (was_connected && !channel_is_connected(channel)) - maybe_disconnect_peer(channel->peer->ld, channel->peer); - } } struct htlc_out *channel_has_htlc_out(struct channel *channel) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index b7bff6888f95..9c0454d57aa9 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -641,33 +641,6 @@ void connectd_activate(struct lightningd *ld) assert(ret == ld->connectd); } -void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer) -{ - struct channel *channel; - - /* Any channels left which want to talk? */ - if (peer->uncommitted_channel) - return; - - list_for_each(&peer->channels, channel, list) - if (channel_is_connected(channel)) - return; - - /* If shutting down, connectd no longer exists. - * FIXME: Call peer_disconnect_done(), but nobody cares. */ - if (!ld->connectd) { - peer->connected = PEER_DISCONNECTED; - return; - } - - /* If connectd was the one who told us to cleanup peer, don't - * tell it to discard again: it might have reconnected! */ - if (peer->connected == PEER_CONNECTED) - subd_send_msg(ld->connectd, - take(towire_connectd_discard_peer(NULL, &peer->id, - peer->connectd_counter))); -} - static struct command_result *json_sendcustommsg(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index ab63b59acc7c..4a3adb07bef2 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -24,7 +24,4 @@ void connect_failed_disconnect(struct lightningd *ld, const struct node_id *id, const struct wireaddr_internal *addr); -/* Disconnect a peer (if no subds want to talk any more) */ -void maybe_disconnect_peer(struct lightningd *ld, struct peer *peer); - #endif /* LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H */ diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index f3c455e92855..778648b0a55b 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -31,7 +31,6 @@ static void destroy_uncommitted_channel(struct uncommitted_channel *uc) uc->peer->uncommitted_channel = NULL; - maybe_disconnect_peer(uc->peer->ld, uc->peer); maybe_delete_peer(uc->peer); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 3bcd45eea3b2..3e0f78074a3a 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1539,11 +1539,6 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) assert(p->connectd_counter == connectd_counter); log_peer_debug(ld->log, &id, "peer_disconnect_done"); p->connected = PEER_DISCONNECTED; - - /* If there are literally no channels, might as well - * free immediately. */ - if (!p->uncommitted_channel && list_empty(&p->channels)) - p = tal_free(p); } /* If you were trying to connect, it failed. */ @@ -1561,6 +1556,10 @@ void peer_disconnect_done(struct lightningd *ld, const u8 *msg) was_pending(command_success(i->cmd, json_stream_success(i->cmd))); } + + /* If connection was only thing keeping it, this will delete it. */ + if (p) + maybe_delete_peer(p); } static bool check_funding_details(const struct bitcoin_tx *tx, @@ -2084,9 +2083,8 @@ static struct command_result *json_disconnect(struct command *cmd, struct node_id *id; struct disconnect_command *dc; struct peer *peer; - struct channel *channel, **channels; + struct channel *channel; bool *force; - bool disconnected = false; if (!param(cmd, buffer, params, p_req("id", param_node_id, &id), @@ -2109,58 +2107,11 @@ static struct command_result *json_disconnect(struct command *cmd, channel_state_name(channel)); } - /* Careful here! Disconnecting can free peer! */ - channels = tal_arr(cmd, struct channel *, 0); - list_for_each(&peer->channels, channel, list) { - if (!channel->owner) - continue; - if (!channel->owner->talks_to_peer) - continue; - - switch (channel->state) { - case DUALOPEND_OPEN_INIT: - case CHANNELD_AWAITING_LOCKIN: - case CHANNELD_NORMAL: - case CHANNELD_SHUTTING_DOWN: - case DUALOPEND_AWAITING_LOCKIN: - case CLOSINGD_SIGEXCHANGE: - tal_arr_expand(&channels, channel); - continue; - case CLOSINGD_COMPLETE: - case AWAITING_UNILATERAL: - case FUNDING_SPEND_SEEN: - case ONCHAIN: - case CLOSED: - /* We don't expect these to have owners who connect! */ - log_broken(channel->log, - "Don't expect owner %s in state %s", - channel->owner->name, - channel_state_name(channel)); - continue; - } - abort(); - } - - /* This can free peer too! */ - if (peer->uncommitted_channel) { - kill_uncommitted_channel(peer->uncommitted_channel, - "disconnect command"); - disconnected = true; - } - - for (size_t i = 0; i < tal_count(channels); i++) { - if (channel_unsaved(channels[i])) - channel_unsaved_close_conn(channels[i], - "disconnect command"); - else - channel_fail_reconnect(channels[i], - "disconnect command"); - disconnected = true; - } - - /* It's just sitting in connectd? */ - if (!disconnected) - maybe_disconnect_peer(cmd->ld, peer); + /* If it's not already disconnecting, tell connectd to disconnect */ + if (peer->connected == PEER_CONNECTED) + subd_send_msg(peer->ld->connectd, + take(towire_connectd_discard_peer(NULL, &peer->id, + peer->connectd_counter))); /* Connectd tells us when it's finally disconnected */ dc = tal(cmd, struct disconnect_command); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index dcb0bb7b10fd..05f66fe13d22 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -387,7 +387,9 @@ void json_add_short_channel_id(struct json_stream *response UNNEEDED, const struct short_channel_id *id UNNEEDED) { fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); } /* Generated stub for json_add_string */ -void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) +void json_add_string(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + const char *str TAKES UNNEEDED) { fprintf(stderr, "json_add_string called!\n"); abort(); } /* Generated stub for json_add_stringn */ void json_add_stringn(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, @@ -509,9 +511,6 @@ void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "log_ called!\n"); abort(); } -/* Generated stub for maybe_disconnect_peer */ -void maybe_disconnect_peer(struct lightningd *ld UNNEEDED, struct peer *peer UNNEEDED) -{ fprintf(stderr, "maybe_disconnect_peer called!\n"); abort(); } /* Generated stub for merkle_tlv */ void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle UNNEEDED) { fprintf(stderr, "merkle_tlv called!\n"); abort(); } @@ -716,6 +715,9 @@ u8 *towire_channeld_dev_memleak(const tal_t *ctx UNNEEDED) /* Generated stub for towire_channeld_dev_reenable_commit */ u8 *towire_channeld_dev_reenable_commit(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_dev_reenable_commit called!\n"); abort(); } +/* Generated stub for towire_connectd_discard_peer */ +u8 *towire_connectd_discard_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED) +{ fprintf(stderr, "towire_connectd_discard_peer called!\n"); abort(); } /* Generated stub for towire_connectd_peer_connect_subd */ u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_connectd_peer_connect_subd called!\n"); abort(); } diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index 390368835f39..f663a9f26414 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -47,7 +47,9 @@ void json_add_str_fmt(struct json_stream *js UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "json_add_str_fmt called!\n"); abort(); } /* Generated stub for json_add_string */ -void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) +void json_add_string(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + const char *str TAKES UNNEEDED) { fprintf(stderr, "json_add_string called!\n"); abort(); } /* Generated stub for json_add_time */ void json_add_time(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index dd59da1cd0d6..82dfdf7eb7d5 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -75,7 +75,9 @@ void json_add_short_channel_id(struct json_stream *response UNNEEDED, const struct short_channel_id *id UNNEEDED) { fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); } /* Generated stub for json_add_string */ -void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) +void json_add_string(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + const char *str TAKES UNNEEDED) { fprintf(stderr, "json_add_string called!\n"); abort(); } /* Generated stub for json_add_timeabs */ void json_add_timeabs(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, @@ -155,12 +157,10 @@ bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint16_t *num UNNEEDED) { fprintf(stderr, "json_to_u16 called!\n"); abort(); } /* Generated stub for json_to_u32 */ -bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - uint32_t *num UNNEEDED) +bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32 *num UNNEEDED) { fprintf(stderr, "json_to_u32 called!\n"); abort(); } /* Generated stub for json_to_u64 */ -bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - uint64_t *num UNNEEDED) +bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) { fprintf(stderr, "json_to_u64 called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) diff --git a/tests/test_closing.py b/tests/test_closing.py index bb3d479d0e92..d8f8f58f7f5d 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -550,7 +550,7 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): bitcoind.generate_block(100) sync_blockheight(bitcoind, [l1, l2]) - wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) + wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) # Do one last pass over the logs to extract the reactions l2 sent l2.daemon.logsearch_start = needle @@ -679,7 +679,7 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): bitcoind.generate_block(100) sync_blockheight(bitcoind, [l1, l2]) - wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) + wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) # Do one last pass over the logs to extract the reactions l2 sent l2.daemon.logsearch_start = needle @@ -3447,10 +3447,8 @@ def test_you_forgot_closed_channel(node_factory, executor): 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' - # l2 closes on us. - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is False) - - # l1 reconnects, it should succeed. + # l1 won't send anything else until we reconnect, then it should succeed. + l1.rpc.disconnect(l2.info['id'], force=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) fut.result(TIMEOUT) @@ -3486,8 +3484,7 @@ def no_new_blocks(req): wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] == 'ONCHAIN') # l1 reconnects, it should succeed. - # l1 will disconnect once it sees block - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['connected'] is False) + l1.rpc.disconnect(l2.info['id'], force=True) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) fut.result(TIMEOUT) diff --git a/tests/test_connection.py b/tests/test_connection.py index 52d33d484a5d..0106efcde286 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -956,8 +956,10 @@ def test_shutdown_awaiting_lockin(node_factory, bitcoind): l2.daemon.wait_for_log(' to ONCHAIN') bitcoind.generate_block(100) - wait_for(lambda: l1.rpc.listpeers()['peers'] == []) - wait_for(lambda: l2.rpc.listpeers()['peers'] == []) + + # Won't disconnect! + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) + wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) @pytest.mark.openchannel('v1') @@ -1308,12 +1310,7 @@ def test_funding_external_wallet_corners(node_factory, bitcoind): assert l1.rpc.fundchannel_cancel(l2.info['id'])['cancelled'] assert len(l1.rpc.listpeers()['peers']) == 0 - # l2 still has the channel open/waiting - wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['state'] - == 'CHANNELD_AWAITING_LOCKIN') - # on reconnect, channel should get destroyed - wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_log('Unknown channel .* for WIRE_CHANNEL_REESTABLISH') wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 0) @@ -2535,13 +2532,10 @@ def test_multiple_channels(node_factory): l1 = node_factory.get_node() l2 = node_factory.get_node() - for i in range(3): - # FIXME: we shouldn't disconnect on close? - ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - assert ret['id'] == l2.info['id'] + ret = l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + assert ret['id'] == l2.info['id'] - l1.daemon.wait_for_log('Handed peer, entering loop') - l2.daemon.wait_for_log('Handed peer, entering loop') + for i in range(3): chan, _ = l1.fundchannel(l2, 10**6) l1.rpc.close(chan) @@ -2551,7 +2545,6 @@ def test_multiple_channels(node_factory): l2.daemon.wait_for_log( r'State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE' ) - wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) channels = only_one(l1.rpc.listpeers()['peers'])['channels'] assert len(channels) == 3 @@ -2581,7 +2574,7 @@ def test_forget_channel(node_factory): # Forcing should work l1.rpc.dev_forget_channel(l2.info['id'], True) - wait_for(lambda: l1.rpc.listpeers()['peers'] == []) + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) # And restarting should keep that peer forgotten l1.restart() @@ -2637,13 +2630,12 @@ def test_peerinfo(node_factory, bitcoind): # Close the channel to forget the peer l1.rpc.close(chan) - wait_for(lambda: not only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']) - wait_for(lambda: not only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected']) - # Make sure close tx hits mempool before we mine blocks. bitcoind.generate_block(100, wait_for_mempool=1) l1.daemon.wait_for_log('onchaind complete, forgetting peer') l2.daemon.wait_for_log('onchaind complete, forgetting peer') + assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'] == [] + assert only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels'] == [] # The only channel was closed, everybody should have forgotten the nodes assert l1.rpc.listnodes()['nodes'] == [] @@ -2728,8 +2720,8 @@ def mock_donothing(r): # (Note that we let the last number be anything (hence the {}\d) l2.daemon.wait_for_log(r'Forgetting channel: It has been {}\d blocks'.format(str(blocks)[:-1])) - # fundee will also forget and disconnect from peer. - wait_for(lambda: l2.rpc.listpeers(l1.info['id'])['peers'] == []) + # fundee will also forget, but not disconnect from peer. + wait_for(lambda: only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels'] == []) @pytest.mark.developer("needs --dev-max-funding-unconfirmed-blocks") diff --git a/tests/test_db.py b/tests/test_db.py index 05379dd1e5ed..95ae6fad283a 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -117,8 +117,8 @@ def test_max_channel_id(node_factory, bitcoind): l2.wait_for_channel_onchain(l1.info['id']) bitcoind.generate_block(101) - wait_for(lambda: l1.rpc.listpeers()['peers'] == []) - wait_for(lambda: l2.rpc.listpeers()['peers'] == []) + wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) + wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) # Stop l2, and restart l2.stop() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3d7a76ca048f..a3f0264a5cfe 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -740,10 +740,11 @@ def test_openchannel_hook_chaining(node_factory, bitcoind): # the third plugin must now not be called anymore assert not l2.daemon.is_in_log("reject on principle") + wait_for(lambda: l1.rpc.listpeers()['peers'] == []) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) # 100000sat is good for hook_accepter, so it should fail 'on principle' # at third hook openchannel_reject.py with pytest.raises(RpcError, match=r'reject on principle'): - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.rpc.fundchannel(l2.info['id'], 100000) assert l2.daemon.wait_for_log(hook_msg + "reject on principle") diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 6b3fda3f5a67..8dd612b80e85 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -361,7 +361,9 @@ void json_add_short_channel_id(struct json_stream *response UNNEEDED, const struct short_channel_id *id UNNEEDED) { fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); } /* Generated stub for json_add_string */ -void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value TAKES UNNEEDED) +void json_add_string(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + const char *str TAKES UNNEEDED) { fprintf(stderr, "json_add_string called!\n"); abort(); } /* Generated stub for json_add_timeabs */ void json_add_timeabs(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, @@ -454,9 +456,6 @@ bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, void kill_uncommitted_channel(struct uncommitted_channel *uc UNNEEDED, const char *why UNNEEDED) { fprintf(stderr, "kill_uncommitted_channel called!\n"); abort(); } -/* Generated stub for maybe_disconnect_peer */ -void maybe_disconnect_peer(struct lightningd *ld UNNEEDED, struct peer *peer UNNEEDED) -{ fprintf(stderr, "maybe_disconnect_peer called!\n"); abort(); } /* Generated stub for new_channel_mvt_invoice_hin */ struct channel_coin_mvt *new_channel_mvt_invoice_hin(const tal_t *ctx UNNEEDED, struct htlc_in *hin UNNEEDED, @@ -743,6 +742,9 @@ u8 *towire_channeld_offer_htlc(const tal_t *ctx UNNEEDED, struct amount_msat amo /* Generated stub for towire_channeld_sending_commitsig_reply */ u8 *towire_channeld_sending_commitsig_reply(const tal_t *ctx UNNEEDED) { fprintf(stderr, "towire_channeld_sending_commitsig_reply called!\n"); abort(); } +/* Generated stub for towire_connectd_discard_peer */ +u8 *towire_connectd_discard_peer(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED) +{ fprintf(stderr, "towire_connectd_discard_peer called!\n"); abort(); } /* Generated stub for towire_connectd_peer_connect_subd */ u8 *towire_connectd_peer_connect_subd(const tal_t *ctx UNNEEDED, const struct node_id *id UNNEEDED, u64 counter UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_connectd_peer_connect_subd called!\n"); abort(); } From 02e169fd2727a75a1a27a14b7d924287c91eb626 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1053/1530] lightningd: drive all reconnections out of disconnections. The only places which should call try_reconnect now are the "connect" command, and the disconnect path when it decides there's still an active channel. This introduces one subtlety: if we disconnect when there's no active channel, but then the subd makes one, we have to catch that case! This temporarily reverts "slow" reconnections to fast ones: see next patch. Signed-off-by: Rusty Russell --- lightningd/channel.c | 20 +++++-------- lightningd/channel.h | 6 ++-- lightningd/channel_control.c | 8 +++-- lightningd/closing_control.c | 7 +++-- lightningd/dual_open_control.c | 33 +++++++++++++-------- lightningd/opening_control.c | 11 +++++++ lightningd/peer_control.c | 24 +++++++++++---- lightningd/test/run-invoice-select-inchan.c | 14 ++++----- tests/test_closing.py | 3 +- tests/test_connection.py | 26 +++++++++------- tests/test_misc.py | 6 ++-- tests/test_opening.py | 4 --- 12 files changed, 98 insertions(+), 64 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index b190aec5a443..83adfc86a44c 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -929,9 +929,9 @@ void channel_set_billboard(struct channel *channel, bool perm, const char *str) } } -static void err_and_reconnect(struct channel *channel, - const char *why, - u32 seconds_before_reconnect) +static void channel_err(struct channel *channel, + const char *why, + u32 seconds_before_reconnect /* FIXME: use this! */) { log_info(channel->log, "Peer transient failure in %s: %s", channel_state_name(channel), why); @@ -946,29 +946,23 @@ static void err_and_reconnect(struct channel *channel, #endif channel_set_owner(channel, NULL); - - /* Their address only useful if we connected to them */ - try_reconnect(channel, channel->peer, seconds_before_reconnect, - channel->peer->connected_incoming - ? NULL - : &channel->peer->addr); } -void channel_fail_reconnect_later(struct channel *channel, const char *fmt, ...) +void channel_fail_transient_delayreconnect(struct channel *channel, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - err_and_reconnect(channel, tal_vfmt(tmpctx, fmt, ap), 60); + channel_err(channel, tal_vfmt(tmpctx, fmt, ap), 60); va_end(ap); } -void channel_fail_reconnect(struct channel *channel, const char *fmt, ...) +void channel_fail_transient(struct channel *channel, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - err_and_reconnect(channel, tal_vfmt(tmpctx, fmt, ap), 1); + channel_err(channel, tal_vfmt(tmpctx, fmt, ap), 1); va_end(ap); } diff --git a/lightningd/channel.h b/lightningd/channel.h index c872bd1cc340..a005f80178a0 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -374,11 +374,11 @@ const char *channel_state_str(enum channel_state state); void channel_set_owner(struct channel *channel, struct subd *owner); /* Channel has failed, but can try again. */ -void channel_fail_reconnect(struct channel *channel, +void channel_fail_transient(struct channel *channel, const char *fmt, ...) PRINTF_FMT(2,3); /* Channel has failed, but can try again after a minute. */ -void channel_fail_reconnect_later(struct channel *channel, - const char *fmt,...) PRINTF_FMT(2,3); +void channel_fail_transient_delayreconnect(struct channel *channel, + const char *fmt,...) PRINTF_FMT(2,3); /* Channel has failed, give up on it. */ void channel_fail_permanent(struct channel *channel, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 48d4f1dd7114..d7bde69d8b0a 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -319,7 +319,7 @@ static void peer_got_shutdown(struct channel *channel, const u8 *msg) &channel->peer->id, channel->peer->connectd_counter, warning))); - channel_fail_reconnect(channel, "Bad shutdown scriptpubkey %s", + channel_fail_transient(channel, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); return; } @@ -638,8 +638,10 @@ bool peer_start_channeld(struct channel *channel, if (!channel->owner) { log_broken(channel->log, "Could not subdaemon channel: %s", strerror(errno)); - channel_fail_reconnect_later(channel, - "Failed to subdaemon channel"); + /* Disconnect it. */ + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, &channel->peer->id, + channel->peer->connectd_counter))); return false; } diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 47b9e39e4f51..057df16b75af 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -377,8 +378,10 @@ void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd) if (!channel->owner) { log_broken(channel->log, "Could not subdaemon closing: %s", strerror(errno)); - channel_fail_reconnect_later(channel, - "Failed to subdaemon closing"); + /* Disconnect it. */ + subd_send_msg(ld->connectd, + take(towire_connectd_discard_peer(NULL, &channel->peer->id, + channel->peer->connectd_counter))); return; } diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 09f41fd75e46..ba8321f06db9 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -46,14 +47,11 @@ static void channel_disconnect(struct channel *channel, log_(channel->log, level, NULL, false, "%s", desc); channel_cleanup_commands(channel, desc); - if (!reconnect) - channel_set_owner(channel, NULL); - else - channel_fail_reconnect(channel, "%s: %s", - channel->owner ? - channel->owner->name : - "dualopend-dead", - desc); + channel_fail_transient(channel, "%s: %s", + channel->owner ? + channel->owner->name : + "dualopend-dead", + desc); } void channel_unsaved_close_conn(struct channel *channel, const char *why) @@ -1179,6 +1177,7 @@ wallet_commit_channel(struct lightningd *ld, { struct amount_msat our_msat, lease_fee_msat; struct channel_inflight *inflight; + bool any_active = peer_any_active_channel(channel->peer, NULL); if (!amount_sat_to_msat(&our_msat, our_funding)) { log_broken(channel->log, "Unable to convert funds"); @@ -1292,6 +1291,14 @@ wallet_commit_channel(struct lightningd *ld, channel->push); wallet_inflight_add(ld->wallet, inflight); + /* We might have disconnected and decided we didn't need to + * reconnect because no channels are active. But the subd + * just made it active! */ + if (!any_active && channel->peer->connected == PEER_DISCONNECTED) { + try_reconnect(channel->peer, channel->peer, 1, + &channel->peer->addr); + } + return inflight; } @@ -1348,13 +1355,13 @@ static void handle_peer_wants_to_close(struct subd *dualopend, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); - /* Get connectd to send warning, and then allow reconnect. */ + /* Get connectd to send warning, and kill subd. */ subd_send_msg(ld->connectd, take(towire_connectd_peer_final_msg(NULL, &channel->peer->id, channel->peer->connectd_counter, warning))); - channel_fail_reconnect(channel, "Bad shutdown scriptpubkey %s", + channel_fail_transient(channel, "Bad shutdown scriptpubkey %s", tal_hex(tmpctx, scriptpubkey)); return; } @@ -3408,8 +3415,10 @@ bool peer_restart_dualopend(struct peer *peer, if (!channel->owner) { log_broken(channel->log, "Could not subdaemon channel: %s", strerror(errno)); - channel_fail_reconnect_later(channel, - "Failed to subdaemon channel"); + /* Disconnect it. */ + subd_send_msg(peer->ld->connectd, + take(towire_connectd_discard_peer(NULL, &channel->peer->id, + channel->peer->connectd_counter))); return false; } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index ec7ea81c56dc..effdd07d983d 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +101,7 @@ wallet_commit_channel(struct lightningd *ld, u32 lease_start_blockheight = 0; /* No leases on v1 */ struct short_channel_id *alias_local; struct timeabs timestamp; + bool any_active = peer_any_active_channel(uc->peer, NULL); /* We cannot both be the fundee *and* have a `fundchannel_start` * command running! @@ -233,6 +235,15 @@ wallet_commit_channel(struct lightningd *ld, channel->state_change_cause, "new channel opened"); + + /* We might have disconnected and decided we didn't need to + * reconnect because no channels are active. But the subd + * just made it active! */ + if (!any_active && channel->peer->connected == PEER_DISCONNECTED) { + try_reconnect(channel->peer, channel->peer, 1, + &channel->peer->addr); + } + return channel; } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 3e0f78074a3a..66a1a27fdaf8 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -166,7 +166,7 @@ static void peer_channels_cleanup(struct lightningd *ld, c = channels[i]; if (channel_active(c)) { channel_cleanup_commands(c, "Disconnected"); - channel_fail_reconnect(c, "Disconnected"); + channel_fail_transient(c, "Disconnected"); } else if (channel_unsaved(c)) { channel_unsaved_close_conn(c, "Disconnected"); } @@ -357,7 +357,7 @@ void channel_errmsg(struct channel *channel, /* No peer_fd means a subd crash or disconnection. */ if (!peer_fd) { /* If the channel is unsaved, we forget it */ - channel_fail_reconnect(channel, "%s: %s", + channel_fail_transient(channel, "%s: %s", channel->owner->name, desc); return; } @@ -371,8 +371,8 @@ void channel_errmsg(struct channel *channel, * and we would close the channel on them. We now support warnings * for this case. */ if (warning) { - channel_fail_reconnect_later(channel, "%s WARNING: %s", - channel->owner->name, desc); + channel_fail_transient_delayreconnect(channel, "%s WARNING: %s", + channel->owner->name, desc); return; } @@ -1731,9 +1731,21 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, } else if (!short_channel_id_eq(channel->scid, &scid) && !is_stub_scid(channel->scid)) { - /* This normally restarts channeld, initialized with updated scid + /* Send warning: that will make connectd disconnect, and then we'll + * try to reconnect. */ + u8 *warning = towire_warningfmt(tmpctx, &channel->cid, + "short_channel_id changed to %s (was %s)", + short_channel_id_to_str(tmpctx, &scid), + short_channel_id_to_str(tmpctx, channel->scid)); + if (channel->peer->connected != PEER_DISCONNECTED) + subd_send_msg(ld->connectd, + take(towire_connectd_peer_final_msg(NULL, + &channel->peer->id, + channel->peer->connectd_counter, + warning))); + /* When we restart channeld, it will be initialized with updated scid * and also adds it (at least our halve_chan) to rtable. */ - channel_fail_reconnect(channel, + channel_fail_transient_delayreconnect(channel, "short_channel_id changed to %s (was %s)", short_channel_id_to_str(tmpctx, &scid), short_channel_id_to_str(tmpctx, channel->scid)); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 05f66fe13d22..699564d665d6 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -68,14 +68,14 @@ void channel_fail_permanent(struct channel *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "channel_fail_permanent called!\n"); abort(); } -/* Generated stub for channel_fail_reconnect */ -void channel_fail_reconnect(struct channel *channel UNNEEDED, +/* Generated stub for channel_fail_transient */ +void channel_fail_transient(struct channel *channel UNNEEDED, const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "channel_fail_reconnect called!\n"); abort(); } -/* Generated stub for channel_fail_reconnect_later */ -void channel_fail_reconnect_later(struct channel *channel UNNEEDED, - const char *fmt UNNEEDED,...) -{ fprintf(stderr, "channel_fail_reconnect_later called!\n"); abort(); } +{ fprintf(stderr, "channel_fail_transient called!\n"); abort(); } +/* Generated stub for channel_fail_transient_delayreconnect */ +void channel_fail_transient_delayreconnect(struct channel *channel UNNEEDED, + const char *fmt UNNEEDED,...) +{ fprintf(stderr, "channel_fail_transient_delayreconnect called!\n"); abort(); } /* Generated stub for channel_has_htlc_in */ struct htlc_in *channel_has_htlc_in(struct channel *channel UNNEEDED) { fprintf(stderr, "channel_has_htlc_in called!\n"); abort(); } diff --git a/tests/test_closing.py b/tests/test_closing.py index d8f8f58f7f5d..763008a22fcc 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3667,8 +3667,7 @@ def test_onchain_close_upstream(node_factory, bitcoind): with pytest.raises(RpcError, match=r'WIRE_TEMPORARY_CHANNEL_FAILURE \(reply from remote\)'): l1.rpc.waitsendpay(ph2, timeout=TIMEOUT) - # l3 closes unilaterally. - wait_for(lambda: only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + # Make close unilaterally. l3.rpc.close(l2.info['id'], 1) l3.daemon.wait_for_log('sendrawtransaction') diff --git a/tests/test_connection.py b/tests/test_connection.py index 0106efcde286..be7eb5069038 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -451,7 +451,8 @@ def test_disconnect_opener(node_factory): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) with pytest.raises(RpcError): l1.rpc.fundchannel(l2.info['id'], 25000) - assert l1.rpc.getpeer(l2.info['id']) is None + # First peer valishes, but later it just disconnects + wait_for(lambda: all([p['connected'] is False for p in l1.rpc.listpeers()['peers']])) # This one will succeed. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -495,7 +496,8 @@ def test_disconnect_fundee(node_factory): l1.rpc.connect(l2.info['id'], 'localhost', l2.port) with pytest.raises(RpcError): l1.rpc.fundchannel(l2.info['id'], 25000) - assert l1.rpc.getpeer(l2.info['id']) is None + # First peer valishes, but later it just disconnects + wait_for(lambda: all([p['connected'] is False for p in l1.rpc.listpeers()['peers']])) # This one will succeed. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -541,8 +543,8 @@ def test_disconnect_fundee_v2(node_factory): l1.rpc.fundchannel(l2.info['id'], 25000) # Should still only have one peer! - assert len(l1.rpc.listpeers()) == 1 - assert len(l2.rpc.listpeers()) == 1 + assert len(l1.rpc.listpeers()['peers']) == 1 + assert len(l2.rpc.listpeers()['peers']) == 1 @pytest.mark.developer @@ -564,8 +566,8 @@ def test_disconnect_half_signed(node_factory): l1.rpc.fundchannel(l2.info['id'], 25000) # Peer remembers, opener doesn't. - assert l1.rpc.getpeer(l2.info['id']) is None - assert l2.rpc.getpeer(l1.info['id'])['id'] == l1.info['id'] + wait_for(lambda: l1.rpc.listpeers(l2.info['id'])['peers'] == []) + assert len(only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']) == 1 @pytest.mark.developer @@ -3606,7 +3608,8 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): l2.wait_for_onchaind_broadcast('OUR_PENALTY_TX', 'THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM') bitcoind.generate_block(100) - wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) + # This works even if they disconnect and listpeers() is empty: + wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) # TEST 2: Cheat from post-upgrade. node_factory.join_nodes([l1, l2]) @@ -3630,7 +3633,8 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): l2.wait_for_onchaind_broadcast('OUR_PENALTY_TX', 'THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM') bitcoind.generate_block(100) - wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) + # This works even if they disconnect and listpeers() is empty: + wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) # TEST 3: Unilateral close from pre-upgrade node_factory.join_nodes([l1, l2]) @@ -3658,7 +3662,8 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): bitcoind.generate_block(5) bitcoind.generate_block(100, wait_for_mempool=1) - wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) + # This works even if they disconnect and listpeers() is empty: + wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) # TEST 4: Unilateral close from post-upgrade node_factory.join_nodes([l1, l2]) @@ -3683,7 +3688,8 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): bitcoind.generate_block(5) bitcoind.generate_block(100, wait_for_mempool=1) - wait_for(lambda: len(l2.rpc.listpeers()['peers']) == 0) + # This works even if they disconnect and listpeers() is empty: + wait_for(lambda: all([p['channels'] == [] for p in l2.rpc.listpeers()['peers']])) @unittest.skipIf(not EXPERIMENTAL_FEATURES, "upgrade protocol not available") diff --git a/tests/test_misc.py b/tests/test_misc.py index 630e110f6b2c..b1946f1f8d06 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1095,7 +1095,8 @@ def test_funding_reorg_private(node_factory, bitcoind): opts = {'funding-confirms': 2, 'rescan': 10, 'may_reconnect': True, 'allow_bad_gossip': True, # gossipd send lightning update for original channel. - 'allow_broken_log': True} + 'allow_broken_log': True, + 'allow_warning': True} l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=opts) l1.fundwallet(10000000) sync_blockheight(bitcoind, [l1]) # height 102 @@ -1138,7 +1139,8 @@ def test_funding_reorg_remote_lags(node_factory, bitcoind): """Nodes may disagree about short_channel_id before channel announcement """ # may_reconnect so channeld will restart; bad gossip can happen due to reorg - opts = {'funding-confirms': 1, 'may_reconnect': True, 'allow_bad_gossip': True} + opts = {'funding-confirms': 1, 'may_reconnect': True, 'allow_bad_gossip': True, + 'allow_warning': True} l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=opts) l1.fundwallet(10000000) sync_blockheight(bitcoind, [l1]) # height 102 diff --git a/tests/test_opening.py b/tests/test_opening.py index 29f3ee54c251..ad01674e5576 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -129,10 +129,6 @@ def get_funded_channel_scid(n1, n2): for node in node_list: node.daemon.wait_for_log(r'to CLOSINGD_COMPLETE') - # Make sure disconnections are complete - if not failed_sign: - wait_for(lambda: all([c['connected'] is False for c in l1.rpc.listpeers()['peers']])) - # With 2 down, it will fail to fund channel l2.stop() l3.stop() From a08728497bdd6c7fa882360356cd0700b6061545 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1054/1530] lightningd: reintroduce "slow connect" logic. Just keep a flag on the peer, and delay connection longer if that is set. Signed-off-by: Rusty Russell --- lightningd/channel.c | 7 ++++--- lightningd/connect_control.c | 20 +++++++++++++------- lightningd/peer_control.c | 2 ++ lightningd/peer_control.h | 3 +++ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 83adfc86a44c..2f79551911bc 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -931,7 +931,7 @@ void channel_set_billboard(struct channel *channel, bool perm, const char *str) static void channel_err(struct channel *channel, const char *why, - u32 seconds_before_reconnect /* FIXME: use this! */) + bool delay_reconnect) { log_info(channel->log, "Peer transient failure in %s: %s", channel_state_name(channel), why); @@ -944,6 +944,7 @@ static void channel_err(struct channel *channel, return; } #endif + channel->peer->delay_reconnect = delay_reconnect; channel_set_owner(channel, NULL); } @@ -953,7 +954,7 @@ void channel_fail_transient_delayreconnect(struct channel *channel, const char * va_list ap; va_start(ap, fmt); - channel_err(channel, tal_vfmt(tmpctx, fmt, ap), 60); + channel_err(channel, tal_vfmt(tmpctx, fmt, ap), true); va_end(ap); } @@ -962,7 +963,7 @@ void channel_fail_transient(struct channel *channel, const char *fmt, ...) va_list ap; va_start(ap, fmt); - channel_err(channel, tal_vfmt(tmpctx, fmt, ap), 1); + channel_err(channel, tal_vfmt(tmpctx, fmt, ap), false); va_end(ap); } diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 9c0454d57aa9..3370200e2f96 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -346,7 +346,7 @@ static void connect_failed(struct lightningd *ld, const struct node_id *id, errcode_t errcode, const char *errmsg, - u32 seconds_to_delay, + const u32 *seconds_to_delay, const struct wireaddr_internal *addrhint) { struct peer *peer; @@ -360,10 +360,16 @@ static void connect_failed(struct lightningd *ld, /* If we have an active channel, then reconnect. */ peer = peer_by_id(ld, id); - if (peer) { - if (peer_any_active_channel(peer, NULL)) - try_reconnect(peer, peer, seconds_to_delay, addrhint); - } + if (peer && peer_any_active_channel(peer, NULL)) { + u32 delay; + if (seconds_to_delay) + delay = *seconds_to_delay; + else + delay = peer->delay_reconnect ? 60 : 1; + log_peer_debug(ld->log, id, "Reconnecting in %u seconds", delay); + try_reconnect(peer, peer, delay, addrhint); + } else + log_peer_debug(ld->log, id, "Not reconnecting: %s", peer ? "no active channel" : "no channels"); } void connect_failed_disconnect(struct lightningd *ld, @@ -371,7 +377,7 @@ void connect_failed_disconnect(struct lightningd *ld, const struct wireaddr_internal *addrhint) { connect_failed(ld, id, CONNECT_DISCONNECTED_DURING, - "disconnected during connection", 1, addrhint); + "disconnected during connection", NULL, addrhint); } static void handle_connect_failed(struct lightningd *ld, const u8 *msg) @@ -387,7 +393,7 @@ static void handle_connect_failed(struct lightningd *ld, const u8 *msg) fatal("Connect gave bad CONNECTD_CONNECT_FAILED message %s", tal_hex(msg, msg)); - connect_failed(ld, &id, errcode, errmsg, seconds_to_delay, addrhint); + connect_failed(ld, &id, errcode, errmsg, &seconds_to_delay, addrhint); } void connect_succeeded(struct lightningd *ld, const struct peer *peer, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 66a1a27fdaf8..bdfb45628a94 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -100,6 +100,7 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, list_head_init(&peer->channels); peer->direction = node_id_idx(&peer->ld->id, &peer->id); peer->connected = PEER_DISCONNECTED; + peer->delay_reconnect = false; #if DEVELOPER peer->ignore_htlcs = false; #endif @@ -1329,6 +1330,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* We mark peer in "connecting" state until hooks have passed. */ assert(peer->connected == PEER_DISCONNECTED); peer->connected = PEER_CONNECTING; + peer->delay_reconnect = false; /* Update peer address and direction */ peer->addr = hook_payload->addr; diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index f734522f3a1c..4609ca54b9b7 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -30,6 +30,9 @@ struct peer { /* Connection counter from connectd. */ u64 connectd_counter; + /* Did we fail badly last time? Don't reconnect too fast. */ + bool delay_reconnect; + /* Our channels */ struct list_head channels; From 099d1491044d24d59e856e0904e17a3ac349e942 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1055/1530] pytest: work around dualopend issue. Dualopend is not listening to the peer fd when it hangs up, so doesn't notice it's gone. We don't clean up the channel until it's done (usually a good thing: it could be about to lock it in), but this harms us here. Fix the test failure and make a comment. Signed-off-by: Rusty Russell --- tests/test_connection.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index be7eb5069038..4a8a04dce94f 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1391,6 +1391,10 @@ def test_funding_v2_corners(node_factory, bitcoind): # Disconnect peer. l1.rpc.disconnect(l2.info['id'], force=True) + # FIXME: dualopend doesn't notice that connectd has closed peer conn + # (until we reconnect!) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.rpc.disconnect(l2.info['id']) wait_for(lambda: len(l1.rpc.listpeers()['peers']) == 0) with pytest.raises(RpcError, match=r'Unknown channel'): From acc9dc4852f7775705db33388890c3d9eadef6a4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:28 +0930 Subject: [PATCH 1056/1530] pytest: fix flake in test_channel_lease_post_expiry We close the channel because the payment fulfilment is not totally done, and we generate 6 blocks, causing it to hit CLTV deadline. ``` # send some payments, mine a block or two inv = l2.rpc.invoice(10**4, '1', 'no_1') l1.rpc.pay(inv['bolt11']) # l2 attempts to close a channel that it leased, should fail with pytest.raises(RpcError, match=r'Peer leased this channel from us'): l2.rpc.close(l1.get_channel_scid(l2)) bitcoind.generate_block(6) sync_blockheight(bitcoind, [l1, l2]) # make sure we're at the right place for the csv lock > l2.daemon.wait_for_log('Blockheight: SENT_ADD_ACK_COMMIT->RCVD_ADD_ACK_REVOCATION LOCAL now 115') tests/test_closing.py:823: ... lightningd-2 2022-07-17T13:15:34.242Z DEBUG lightningd: Adding block 115: 39d95061935e9fc42b04c86ae60d0cf157765aff4c040f3a8d0b7888db19e015 lightningd-2 2022-07-17T13:15:34.244Z UNUSUAL 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-chan#2: Peer permanent failure in CHANNELD_NORMAL: Fulfilled HTLC 0 SENT_REMOVE_COMMIT cltv 115 hit deadline ``` Signed-off-by: Rusty Russell --- tests/test_closing.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index 763008a22fcc..3b06066671f2 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -813,6 +813,10 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): inv = l2.rpc.invoice(10**4, '1', 'no_1') l1.rpc.pay(inv['bolt11']) + # make sure it's completely resolved before we generate blocks, + # otherwise it can close HTLC! + wait_for(lambda: only_one(only_one(l2.rpc.listpeers()['peers'])['channels'])['htlcs'] == []) + # l2 attempts to close a channel that it leased, should fail with pytest.raises(RpcError, match=r'Peer leased this channel from us'): l2.rpc.close(l1.get_channel_scid(l2)) From 0363c628abdadba8031853213909c526916fdc7e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 18 Jul 2022 21:42:29 +0930 Subject: [PATCH 1057/1530] channeld: exit after we send an error at lightningd's request. Otherwise connectd complains we didn't close, eg tests/test_connection.py::test_funding_cancel_race: ``` lightningd-1 2022-07-17T14:43:56.813Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: Send error reason: Cancel channel by our RPC command before funding transaction broadcast. lightningd-1 2022-07-17T14:43:56.867Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: peer_out WIRE_ERROR lightningd-2 2022-07-17T14:43:56.926Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-channeld-chan#1: peer_in WIRE_ERROR lightningd-2 2022-07-17T14:43:56.951Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-channeld-chan#1: billboard perm: Received error channel d11396cbb8de10f02ee8d76ff6265bad0eefa7e43b4f540f14dfaab851aa3606: Cancel channel by our RPC command before funding transaction broadcast. lightningd-1 2022-07-17T14:43:56.952Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-channeld-chan#1: Status closed, but not exited. Killing lightningd-2 2022-07-17T14:43:56.976Z UNUSUAL 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-chan#1: Peer permanent failure in CHANNELD_AWAITING_LOCKIN: channeld: received ERROR error channel d11396cbb8de10f02ee8d76ff6265bad0eefa7e43b4f540f14dfaab851aa3606: Cancel channel by our RPC command before funding transaction broadcast., forget channel DEBUG:root:Received response for fundchannel_cancel call: {'jsonrpc': '2.0', 'id': 1, 'result': {'cancelled': 'Channel open canceled by RPC(after fundchannel_complete)'}} DEBUG:root:Received response for fundchannel_cancel call: {'jsonrpc': '2.0', 'id': 1, 'result': {'cancelled': 'Channel open canceled by RPC(after fundchannel_complete)'}} DEBUG:root:{ "id": 1, "result": { "cancelled": "Channel open canceled by RPC(after fundchannel_complete)" } } DEBUG:root:{ "id": 1, "result": { "cancelled": "Channel open canceled by RPC(after fundchannel_complete)" } } DEBUG:root:{ "id": 1, "method": "txdiscard", "params": { "txid": "0736aa51b8aadf140f544f3be4a7ef0ead5b26f66fd7e82ef010deb8cb9613d1" } } lightningd-1 2022-07-17T14:43:57.022Z DEBUG connectd: drain_peer DEBUG:root:Calling txdiscard with payload {'txid': '0736aa51b8aadf140f544f3be4a7ef0ead5b26f66fd7e82ef010deb8cb9613d1'} lightningd-1 2022-07-17T14:43:57.024Z DEBUG connectd: drain_peer draining subd! lightningd-1 2022-07-17T14:43:57.069Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-lightningd: peer_disconnect_done lightningd-1 2022-07-17T14:43:57.082Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-lightningd: Not reconnecting: no active channel ... lightningd-1 2022-07-17T14:44:01.877Z **BROKEN** 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: Subd did not close, forcing close ``` Signed-off-by: Rusty Russell --- channeld/channeld.c | 1 + 1 file changed, 1 insertion(+) diff --git a/channeld/channeld.c b/channeld/channeld.c index 0399fc0c3c0f..ef2293c36062 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -3632,6 +3632,7 @@ static void handle_send_error(struct peer *peer, const u8 *msg) wire_sync_write(MASTER_FD, take(towire_channeld_send_error_reply(NULL))); + exit(0); } #if DEVELOPER From 5abed486d0f691e20e057701d3ffa67fccd7555a Mon Sep 17 00:00:00 2001 From: adi2011 Date: Mon, 18 Jul 2022 18:16:40 +0530 Subject: [PATCH 1058/1530] Add rune and commando to gitignore. Changelog-None: Small fix --- devtools/.gitignore | 1 + plugins/.gitignore | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/devtools/.gitignore b/devtools/.gitignore index b3ec165fdcd6..bd7ae9f1aac4 100644 --- a/devtools/.gitignore +++ b/devtools/.gitignore @@ -18,3 +18,4 @@ onion route topology fp16 +rune \ No newline at end of file diff --git a/plugins/.gitignore b/plugins/.gitignore index 3c340b67de4f..7d87bb04986b 100644 --- a/plugins/.gitignore +++ b/plugins/.gitignore @@ -10,4 +10,5 @@ pay spenderp topology txprepare -chanbackup \ No newline at end of file +chanbackup +commando \ No newline at end of file From 1d671a23804ffb4934dd7080a75adbbe28b1d81c Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 24 May 2022 10:17:27 +0200 Subject: [PATCH 1059/1530] rpc: checkmessage return an error if pubkey is not found Returning an warning message when the pub key is not specified and there is no node in the graph. We try to help people that use core lightning as a signer and nothings else. Changelog-Deprecated: rpc: checkmessage return an error when the pubkey is not specified and it is unknown in the network graph. --- common/jsonrpc_errors.h | 3 +++ doc/lightning-checkmessage.7.md | 4 ++++ lightningd/signmessage.c | 7 +++++++ tests/test_misc.py | 18 +++++++++++++++++- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index d52229582f66..ebf45c4eb07f 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -100,6 +100,9 @@ static const errcode_t DATASTORE_UPDATE_WRONG_GENERATION = 1204; static const errcode_t DATASTORE_UPDATE_HAS_CHILDREN = 1205; static const errcode_t DATASTORE_UPDATE_NO_CHILDREN = 1206; +/* Errors from signmessage command */ +static const errcode_t SIGNMESSAGE_PUBKEY_NOT_FOUND = 1301; + /* Errors from wait* commands */ static const errcode_t WAIT_TIMEOUT = 2000; diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index 010caba3db46..3980f8c5155e 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -20,6 +20,10 @@ known node key (as per *listnodes*), and verification succeeds if it matches for any one of them. Note: this is implemented far more efficiently than trying each one, so performance is not a concern. +On failure, an error is returned and core lightning exit with the following error code: +- -32602: Parameter missed or malformed; +- 1301: *pubkey* not found in the graph. + RETURN VALUE ------------ diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 1e0195a1d8c7..81b584543810 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -133,6 +134,12 @@ static void listnodes_done(const char *buffer, if (t) t = json_get_member(buffer, t, "nodes"); + if (!deprecated_apis && (!t || t->size == 0)) { + was_pending(command_fail(can->cmd, SIGNMESSAGE_PUBKEY_NOT_FOUND, + "pub key not found in the graph, expected pubkey is %s", + node_id_to_hexstr(tmpctx, &can->id))); + return; + } response = json_stream_success(can->cmd); json_add_node_id(response, "pubkey", &can->id); json_add_bool(response, "verified", t && t->size == 1); diff --git a/tests/test_misc.py b/tests/test_misc.py index b1946f1f8d06..059eacdce6ff 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1850,7 +1850,8 @@ def test_relative_config_dir(node_factory): def test_signmessage(node_factory): - l1, l2 = node_factory.line_graph(2, wait_for_announce=True) + l1, l2 = node_factory.line_graph(2, wait_for_announce=True, + opts={'allow-deprecated-apis': True}) corpus = [[None, "this is a test!", @@ -2712,3 +2713,18 @@ def test_torv2_in_db(node_factory): l1.stop() l1.db_manip("UPDATE peers SET address='3fyb44wdhnd2ghhl.onion:1234';") l1.start() + + +def test_checkmessage_pubkey_not_found(node_factory): + l1 = node_factory.get_node() + + msg = "testcase to check new rpc error" + pubkey = "03be3b0e9992153b1d5a6e1623670b6c3663f72ce6cf2e0dd39c0a373a7de5a3b7" + zbase = "d66bqz3qsku5fxtqsi37j11pci47ydxa95iusphutggz9ezaxt56neh77kxe5hyr41kwgkncgiu94p9ecxiexgpgsz8daoq4tw8kj8yx" + + with pytest.raises(RpcError, match="not found in the graph, expected pubkey is {}".format(pubkey)): + l1.rpc.checkmessage(msg, zbase) + + check_result = l1.rpc.checkmessage(msg, zbase, pubkey=pubkey) + assert check_result["pubkey"] == pubkey + assert check_result["verified"] is True \ No newline at end of file From 7ae616ef60413428f40a5f77bffdf9576d49dc30 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 12 Jul 2022 10:55:02 +0100 Subject: [PATCH 1060/1530] rpc: improve error format Signed-off-by: Vincenzo Palazzo --- doc/lightning-checkmessage.7.md | 11 ++--- doc/schemas/checkmessage.schema.json | 66 +++++----------------------- lightningd/signmessage.c | 9 ++-- tests/test_misc.py | 7 ++- 4 files changed, 25 insertions(+), 68 deletions(-) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index 3980f8c5155e..bccdb107385d 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -29,13 +29,8 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **verified** (boolean): Whether the signature was valid - -If **verified** is *true*: - - **pubkey** (pubkey): the *pubkey* parameter, or the pubkey found by looking for known nodes - -If **verified** is *false*: - - **pubkey** (pubkey): the *pubkey* (if any) which could have signed this; this is usually not useful! +- **verified** (boolean): whether the signature was valid (always *true*) +- **pubkey** (pubkey): the *pubkey* parameter, or the pubkey found by looking for known nodes [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -54,4 +49,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7dcca1fd1708d93b4a0c9b83955630fc4f551c4ffd452fb866c624c72aeaa44d) +[comment]: # ( SHA256STAMP:af2feeb4eddafc509dff150ec4b11225618f1cbbea06ef81f6d97a1bece3e94c) diff --git a/doc/schemas/checkmessage.schema.json b/doc/schemas/checkmessage.schema.json index c4bd81988416..0bc52e7e667a 100644 --- a/doc/schemas/checkmessage.schema.json +++ b/doc/schemas/checkmessage.schema.json @@ -2,65 +2,21 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [ - "verified" + "verified", + "pubkey" ], - "additionalProperties": true, + "additionalProperties": false, "properties": { "verified": { "type": "boolean", - "description": "Whether the signature was valid" - } - }, - "allOf": [ - { - "if": { - "properties": { - "verified": { - "type": "boolean", - "enum": [ - true - ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ - "pubkey" - ], - "properties": { - "verified": {}, - "pubkey": { - "type": "pubkey", - "description": "the *pubkey* parameter, or the pubkey found by looking for known nodes" - } - } - } + "enum": [ + true + ], + "description": "whether the signature was valid" }, - { - "if": { - "properties": { - "verified": { - "type": "boolean", - "enum": [ - false - ] - } - } - }, - "then": { - "additionalProperties": false, - "required": [ - "pubkey" - ], - "properties": { - "verified": {}, - "pubkey": { - "type": "pubkey", - "description": "the *pubkey* (if any) which could have signed this; this is usually not useful!" - } - } - } + "pubkey": { + "type": "pubkey", + "description": "the *pubkey* parameter, or the pubkey found by looking for known nodes" } - ] + } } diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 81b584543810..368e18ba31e2 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -135,9 +135,12 @@ static void listnodes_done(const char *buffer, t = json_get_member(buffer, t, "nodes"); if (!deprecated_apis && (!t || t->size == 0)) { - was_pending(command_fail(can->cmd, SIGNMESSAGE_PUBKEY_NOT_FOUND, - "pub key not found in the graph, expected pubkey is %s", - node_id_to_hexstr(tmpctx, &can->id))); + struct json_stream *response; + response = json_stream_fail(can->cmd, SIGNMESSAGE_PUBKEY_NOT_FOUND, + "pubkey not found in the graph"); + json_add_node_id(response, "claimed_key", &can->id); + json_object_end(response); + was_pending(command_failed(can->cmd, response)); return; } response = json_stream_success(can->cmd); diff --git a/tests/test_misc.py b/tests/test_misc.py index 059eacdce6ff..20ff77156388 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2722,9 +2722,12 @@ def test_checkmessage_pubkey_not_found(node_factory): pubkey = "03be3b0e9992153b1d5a6e1623670b6c3663f72ce6cf2e0dd39c0a373a7de5a3b7" zbase = "d66bqz3qsku5fxtqsi37j11pci47ydxa95iusphutggz9ezaxt56neh77kxe5hyr41kwgkncgiu94p9ecxiexgpgsz8daoq4tw8kj8yx" - with pytest.raises(RpcError, match="not found in the graph, expected pubkey is {}".format(pubkey)): + with pytest.raises(RpcError) as exception: l1.rpc.checkmessage(msg, zbase) + err = exception.value + assert err.error['message'] == "pubkey not found in the graph" + assert err.error['data']['claimed_key'] == pubkey check_result = l1.rpc.checkmessage(msg, zbase, pubkey=pubkey) assert check_result["pubkey"] == pubkey - assert check_result["verified"] is True \ No newline at end of file + assert check_result["verified"] is True From ba4e870a1c332cfeab04c1433cedb5d671d593d9 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Wed, 13 Jul 2022 10:52:42 +0100 Subject: [PATCH 1061/1530] test: disable schema check of `checkmessage` with deprecated API Signed-off-by: Vincenzo Palazzo --- doc/lightning-checkmessage.7.md | 2 +- tests/test_misc.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index bccdb107385d..dd010c2d806e 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -49,4 +49,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:af2feeb4eddafc509dff150ec4b11225618f1cbbea06ef81f6d97a1bece3e94c) +[comment]: # ( SHA256STAMP:733247e44d555f9c480a684ceb30440f4f33daf5755253249b5c7b9269c96e49) diff --git a/tests/test_misc.py b/tests/test_misc.py index 20ff77156388..a0a834cceb37 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1852,6 +1852,7 @@ def test_relative_config_dir(node_factory): def test_signmessage(node_factory): l1, l2 = node_factory.line_graph(2, wait_for_announce=True, opts={'allow-deprecated-apis': True}) + l1.rpc.jsonschemas = {} corpus = [[None, "this is a test!", From e70729b04befe44d603370b96fbb10dc00bd342d Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Mon, 18 Jul 2022 10:00:55 +0000 Subject: [PATCH 1062/1530] rust: upgrade model with new checkmessage requirements Signed-off-by: Vincenzo Palazzo --- cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index cc47a976a04b..cbaf889a3259 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -388,7 +388,7 @@ message CheckmessageRequest { message CheckmessageResponse { bool verified = 1; - optional bytes pubkey = 2; + bytes pubkey = 2; } message CloseRequest { diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index e6d33b9ab545..144a0b1117c5 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -299,7 +299,7 @@ impl From<&responses::CheckmessageResponse> for pb::CheckmessageResponse { fn from(c: &responses::CheckmessageResponse) -> Self { Self { verified: c.verified.clone(), // Rule #2 for type boolean - pubkey: c.pubkey.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? + pubkey: c.pubkey.to_vec(), // Rule #2 for type pubkey } } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 3a595deed0c8..1ec55e6bf22d 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1441,8 +1441,8 @@ pub mod responses { pub struct CheckmessageResponse { #[serde(alias = "verified")] pub verified: bool, - #[serde(alias = "pubkey", skip_serializing_if = "Option::is_none")] - pub pubkey: Option, + #[serde(alias = "pubkey")] + pub pubkey: Pubkey, } /// Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel From e96eb07ef417a260bc8edd2a7202d83dbad61b9d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Jul 2022 11:58:25 +0930 Subject: [PATCH 1063/1530] lightningd: test that hsm_secret is as expected, at startup. If you get the wrong hsm_secret, your node_id will change, and peers won't know who you are, bitcoind will reject your transaction signatures, and other madness. Catch this as soon as it happens, by storing our node_id in the db. Suggested-by: @cdecker, @fiatjaf Signed-off-by: Rusty Russell Changelog-Changed: Config: `lightningd` will refuse to start with the wrong node_id (i.e. hsm_secret changes). --- lightningd/lightningd.c | 9 +++++--- lightningd/test/run-find_my_abspath.c | 6 +++--- tests/test_db.py | 29 +++++++++++++++++++++++++- tests/test_wallet.py | 4 ++-- wallet/wallet.c | 30 ++++++++++++++++++++++++++- wallet/wallet.h | 10 +++++---- 6 files changed, 74 insertions(+), 14 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index e6b70c675498..07d79128fbb1 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -1048,9 +1048,12 @@ int main(int argc, char *argv[]) /*~ Our default names, eg. for the database file, are not dependent on * the network. Instead, the db knows what chain it belongs to, and we - * simple barf here if it's wrong. */ - if (!wallet_network_check(ld->wallet)) - errx(1, "Wallet network check failed."); + * simple barf here if it's wrong. + * + * We also check that our node_id is what we expect: otherwise a change + * in hsm_secret will have strange consequences! */ + if (!wallet_sanity_check(ld->wallet)) + errx(1, "Wallet sanity check failed."); /*~ Initialize the transaction filter with our pubkeys. */ init_txfilter(ld->wallet, ld->owned_txfilter); diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 278c6d0c7b87..585eb5328dc4 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -223,13 +223,13 @@ void waitblockheight_notify_new_block(struct lightningd *ld UNNEEDED, /* Generated stub for wallet_blocks_heights */ void wallet_blocks_heights(struct wallet *w UNNEEDED, u32 def UNNEEDED, u32 *min UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "wallet_blocks_heights called!\n"); abort(); } -/* Generated stub for wallet_network_check */ -bool wallet_network_check(struct wallet *w UNNEEDED) -{ fprintf(stderr, "wallet_network_check called!\n"); abort(); } /* Generated stub for wallet_new */ struct wallet *wallet_new(struct lightningd *ld UNNEEDED, struct timers *timers UNNEEDED, struct ext_key *bip32_base UNNEEDED) { fprintf(stderr, "wallet_new called!\n"); abort(); } +/* Generated stub for wallet_sanity_check */ +bool wallet_sanity_check(struct wallet *w UNNEEDED) +{ fprintf(stderr, "wallet_sanity_check called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ struct log *crashlog; diff --git a/tests/test_db.py b/tests/test_db.py index 95ae6fad283a..1c4b7c35e700 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -2,7 +2,7 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from pyln.client import RpcError -from utils import wait_for, sync_blockheight, COMPAT, VALGRIND, DEVELOPER, only_one +from utils import wait_for, sync_blockheight, COMPAT, VALGRIND, DEVELOPER, TIMEOUT, only_one import base64 import os @@ -413,3 +413,30 @@ def test_sqlite3_builtin_backup(bitcoind, node_factory): # Should still see the funds. assert(len(l1.rpc.listfunds()['outputs']) == 1) + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Don't know how to swap dbs in Postgres") +def test_db_sanity_checks(bitcoind, node_factory): + l1, l2 = node_factory.get_nodes(2, opts=[{'allow_broken_log': True, + 'may_fail': True}, {}]) + + l1.stop() + l2.stop() + + # Provide the --wallet option and start with wrong db + l1.daemon.opts['wallet'] = "sqlite3://" + l2.db.path + l1.daemon.start(wait_for_initialized=False) + l1.daemon.wait_for_log(r'\*\*BROKEN\*\* wallet: Wallet node_id does not match HSM') + # Will have exited with non-zero status. + assert l1.daemon.proc.wait(TIMEOUT) != 0 + assert l1.daemon.is_in_stderr('Wallet sanity check failed') + + # Now try wrong network, + l1.daemon.opts['wallet'] = "sqlite3://" + l1.db.path + l1.daemon.opts['network'] = "bitcoin" + + l1.daemon.start(wait_for_initialized=False) + l1.daemon.wait_for_log(r'\*\*BROKEN\*\* wallet: Wallet blockchain hash does not match network blockchain hash') + # Will have exited with non-zero status. + assert l1.daemon.proc.wait(TIMEOUT) != 0 + assert l1.daemon.is_in_stderr('Wallet sanity check failed') diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 893d2f6e0c0b..c50c39b404f5 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1220,8 +1220,7 @@ def test_hsmtool_dump_descriptors(node_factory, bitcoind): @unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.") def test_hsmtool_generatehsm(node_factory): - l1 = node_factory.get_node() - l1.stop() + l1 = node_factory.get_node(start=False) hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, "hsm_secret") @@ -1248,6 +1247,7 @@ def test_hsmtool_generatehsm(node_factory): # We can start the node with this hsm_secret l1.start() + assert l1.info['id'] == '02244b73339edd004bc6dfbb953a87984c88e9e7c02ca14ef6ec593ca6be622ba7' # this test does a 'listtransactions' on a yet unconfirmed channel diff --git a/wallet/wallet.c b/wallet/wallet.c index b9d670ad2dc6..aae846635ec4 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3643,7 +3643,7 @@ void wallet_htlc_sigs_save(struct wallet *w, u64 channel_id, } } -bool wallet_network_check(struct wallet *w) +bool wallet_sanity_check(struct wallet *w) { struct bitcoin_blkid chainhash; struct db_stmt *stmt = db_prepare_v2( @@ -3676,6 +3676,34 @@ bool wallet_network_check(struct wallet *w) db_bind_sha256d(stmt, 0, &chainparams->genesis_blockhash.shad); db_exec_prepared_v2(take(stmt)); } + + stmt = db_prepare_v2(w->db, + SQL("SELECT blobval FROM vars WHERE name='node_id'")); + db_query_prepared(stmt); + + if (db_step(stmt)) { + struct node_id id; + db_col_node_id(stmt, "blobval", &id); + tal_free(stmt); + + if (!node_id_eq(&id, &w->ld->id)) { + log_broken(w->log, "Wallet node_id does not " + "match HSM: %s " + "!= %s. " + "Did your hsm_secret change?", + type_to_string(tmpctx, struct node_id, &id), + type_to_string(tmpctx, struct node_id, + &w->ld->id)); + return false; + } + } else { + tal_free(stmt); + /* Still a pristine wallet, claim it for the node_id we are now */ + stmt = db_prepare_v2(w->db, SQL("INSERT INTO vars (name, blobval) " + "VALUES ('node_id', ?);")); + db_bind_node_id(stmt, 0, &w->ld->id); + db_exec_prepared_v2(take(stmt)); + } return true; } diff --git a/wallet/wallet.h b/wallet/wallet.h index 4003adc749a5..425c8637db68 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1198,13 +1198,15 @@ void wallet_htlc_sigs_save(struct wallet *w, u64 channel_id, const struct bitcoin_signature *htlc_sigs); /** - * wallet_network_check - Check that the wallet is setup for this chain + * wallet_sanity_check - Check that the wallet is setup for this node_id and chain * * Ensure that the genesis_hash from the chainparams matches the - * genesis_hash with which the DB was initialized. Returns false if - * the check failed, i.e., if the genesis hashes do not match. + * genesis_hash with which the DB was initialized, and that the HSM + * gave us the same node_id as the one is the db. + * + * Returns false if the checks failed. */ -bool wallet_network_check(struct wallet *w); +bool wallet_sanity_check(struct wallet *w); /** * wallet_block_add - Add a block to the blockchain tracked by this wallet From 5979a7778fc3cebe65e26cff5b6a33ec13ba59a1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Jul 2022 11:59:25 +0930 Subject: [PATCH 1064/1530] lightningd: expand exit codes for various failures. Most unexpected ones are still 1, but there are a few recognizable error codes worth documenting. Rename the HSM ones to put ERRCODE_ at the front, since we have non-HSM ones too now. Signed-off-by: Rusty Russell --- common/errcode.h | 17 ++++++++++++----- common/hsm_encryption.c | 18 +++++++++--------- doc/lightningd.8.md | 3 +++ lightningd/hsm_control.c | 12 ++++++------ lightningd/lightningd.c | 12 ++++++------ lightningd/options.c | 2 +- tools/hsmtool.c | 24 ++++++++++++------------ 7 files changed, 49 insertions(+), 39 deletions(-) diff --git a/common/errcode.h b/common/errcode.h index d9784e962fd6..eb4da10c7423 100644 --- a/common/errcode.h +++ b/common/errcode.h @@ -9,11 +9,18 @@ typedef s32 errcode_t; #define PRIerrcode PRId32 +// Setup errors +#define EXITCODE_SUBDAEMON_FAIL 10 +#define EXITCODE_PIDFILE_LOCK 11 + // HSM errors code -#define HSM_GENERIC_ERROR 20 -#define HSM_ERROR_IS_ENCRYPT 21 -#define HSM_BAD_PASSWORD 22 -#define HSM_PASSWORD_INPUT_ERR 23 -#define ERROR_HSM_FILE 24 +#define EXITCODE_HSM_GENERIC_ERROR 20 +#define EXITCODE_HSM_ERROR_IS_ENCRYPT 21 +#define EXITCODE_HSM_BAD_PASSWORD 22 +#define EXITCODE_HSM_PASSWORD_INPUT_ERR 23 +#define EXITCODE_ERROR_HSM_FILE 24 + +// Wallet error +#define EXITCODE_WALLET_DB_MISMATCH 30 #endif /* LIGHTNING_COMMON_ERRCODE_H */ diff --git a/common/hsm_encryption.c b/common/hsm_encryption.c index b2f7c813f63d..796fc33cc88e 100644 --- a/common/hsm_encryption.c +++ b/common/hsm_encryption.c @@ -14,16 +14,16 @@ int hsm_secret_encryption_key_with_exitcode(const char *pass, struct secret *key /* Don't swap the encryption key ! */ if (sodium_mlock(key->data, sizeof(key->data)) != 0) { *err_msg = "Could not lock hsm_secret encryption key memory."; - return HSM_GENERIC_ERROR; + return EXITCODE_HSM_GENERIC_ERROR; } /* Check bounds. */ if (strlen(pass) < crypto_pwhash_argon2id_PASSWD_MIN) { *err_msg = "Password too short to be able to derive a key from it."; - return HSM_BAD_PASSWORD; + return EXITCODE_HSM_BAD_PASSWORD; } else if (strlen(pass) > crypto_pwhash_argon2id_PASSWD_MAX) { *err_msg = "Password too long to be able to derive a key from it."; - return HSM_BAD_PASSWORD; + return EXITCODE_HSM_BAD_PASSWORD; } /* Now derive the key. */ @@ -34,7 +34,7 @@ int hsm_secret_encryption_key_with_exitcode(const char *pass, struct secret *key crypto_pwhash_argon2id_MEMLIMIT_MODERATE, crypto_pwhash_ALG_ARGON2ID13) != 0) { *err_msg = "Could not derive a key from the password."; - return HSM_BAD_PASSWORD; + return EXITCODE_HSM_BAD_PASSWORD; } return 0; @@ -122,20 +122,20 @@ char *read_stdin_pass_with_exit_code(char **reason, int *exit_code) /* 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."; - *exit_code = HSM_PASSWORD_INPUT_ERR; + *exit_code = EXITCODE_HSM_PASSWORD_INPUT_ERR; return NULL; } temp_term = current_term; temp_term.c_lflag &= ~ECHO; if (tcsetattr(fileno(stdin), TCSANOW, &temp_term) != 0) { *reason = "Could not disable pass echoing."; - *exit_code = HSM_PASSWORD_INPUT_ERR; + *exit_code = EXITCODE_HSM_PASSWORD_INPUT_ERR; return NULL; } if (!getline_stdin_pass(&passwd, &passwd_size)) { *reason = "Could not read pass from stdin."; - *exit_code = HSM_PASSWORD_INPUT_ERR; + *exit_code = EXITCODE_HSM_PASSWORD_INPUT_ERR; return NULL; } @@ -143,12 +143,12 @@ char *read_stdin_pass_with_exit_code(char **reason, int *exit_code) if (tcsetattr(fileno(stdin), TCSANOW, ¤t_term) != 0) { *reason = "Could not restore terminal options."; free(passwd); - *exit_code = HSM_PASSWORD_INPUT_ERR; + *exit_code = EXITCODE_HSM_PASSWORD_INPUT_ERR; return NULL; } } else if (!getline_stdin_pass(&passwd, &passwd_size)) { *reason = "Could not read pass from stdin."; - *exit_code = HSM_PASSWORD_INPUT_ERR; + *exit_code = EXITCODE_HSM_PASSWORD_INPUT_ERR; return NULL; } return passwd; diff --git a/doc/lightningd.8.md b/doc/lightningd.8.md index 2f3a68a7fbd0..552c8c3030d1 100644 --- a/doc/lightningd.8.md +++ b/doc/lightningd.8.md @@ -156,10 +156,13 @@ ERRORS CODE --- - 1: Generic lightning-cli error +- 10: Error executing subdaemons +- 11: Error locking pidfile (often another lightningd running) - 20: Generic error related to HSM secret - 21: HSM secret is encrypted - 22: Bad password used to decrypt the HSM secred - 23: Error caused from the I/O operation during a HSM decryption/encryption operation +- 30: Wallet database does not match (network or hsm secret) BUGS diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index c4b2a0690bf4..c5252fc3bd09 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -83,21 +83,21 @@ struct ext_key *hsm_init(struct lightningd *ld) /* We actually send requests synchronously: only status is async. */ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) - err(HSM_GENERIC_ERROR, "Could not create hsm socketpair"); + err(EXITCODE_HSM_GENERIC_ERROR, "Could not create hsm socketpair"); ld->hsm = new_global_subd(ld, "lightning_hsmd", hsmd_wire_name, hsm_msg, take(&fds[1]), NULL); if (!ld->hsm) - err(HSM_GENERIC_ERROR, "Could not subd hsm"); + err(EXITCODE_HSM_GENERIC_ERROR, "Could not subd hsm"); /* If hsm_secret is encrypted and the --encrypted-hsm startup option is * not passed, don't let hsmd use the first 32 bytes of the cypher as the * actual secret. */ if (!ld->config.keypass) { if (is_hsm_secret_encrypted("hsm_secret") == 1) - errx(HSM_ERROR_IS_ENCRYPT, "hsm_secret is encrypted, you need to pass the " + errx(EXITCODE_HSM_ERROR_IS_ENCRYPT, "hsm_secret is encrypted, you need to pass the " "--encrypted-hsm startup option."); } @@ -110,7 +110,7 @@ struct ext_key *hsm_init(struct lightningd *ld) IFDEV(ld->dev_force_bip32_seed, NULL), IFDEV(ld->dev_force_channel_secrets, NULL), IFDEV(ld->dev_force_channel_secrets_shaseed, NULL)))) - err(HSM_GENERIC_ERROR, "Writing init msg to hsm"); + err(EXITCODE_HSM_GENERIC_ERROR, "Writing init msg to hsm"); bip32_base = tal(ld, struct ext_key); msg = wire_sync_read(tmpctx, ld->hsm_fd); @@ -119,8 +119,8 @@ struct ext_key *hsm_init(struct lightningd *ld) &ld->bolt12_base, &ld->onion_reply_secret)) { if (ld->config.keypass) - errx(HSM_BAD_PASSWORD, "Wrong password for encrypted hsm_secret."); - errx(HSM_GENERIC_ERROR, "HSM did not give init reply"); + errx(EXITCODE_HSM_BAD_PASSWORD, "Wrong password for encrypted hsm_secret."); + errx(EXITCODE_HSM_GENERIC_ERROR, "HSM did not give init reply"); } return bip32_base; diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 07d79128fbb1..e15a0309a301 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -400,10 +400,10 @@ void test_subdaemons(const struct lightningd *ld) /*~ ccan/err is a wrapper around BSD's err.h, which defines * the convenience functions err() (error with message - * followed by a string based on errno) and errx() (same, + * followed by a string based on errno) and errx() (same,x * but no errno string). */ if (pid == -1) - err(1, "Could not run %s", dpath); + err(EXITCODE_SUBDAEMON_FAIL, "Could not run %s", dpath); /*~ CCAN's grab_file module contains a routine to read into a * tallocated buffer until EOF */ @@ -415,7 +415,7 @@ void test_subdaemons(const struct lightningd *ld) /*~ strstarts is from CCAN/str. */ if (!strstarts(verstring, version()) || verstring[strlen(version())] != '\n') - errx(1, "%s: bad version '%s'", + errx(EXITCODE_SUBDAEMON_FAIL, "%s: bad version '%s'", subdaemons[i], verstring); /*~ The child will be reaped by sigchld_rfd_in, so we don't * need to waitpid() here. */ @@ -654,7 +654,7 @@ static void pidfile_create(const struct lightningd *ld) /* Lock PID file, so future lockf will fail. */ if (lockf(pid_fd, F_TLOCK, 0) < 0) /* Problem locking file */ - err(1, "lightningd already running? Error locking PID file"); + err(EXITCODE_PIDFILE_LOCK, "lightningd already running? Error locking PID file"); /*~ As closing the file will remove the lock, we need to keep it open; * the OS will close it implicitly when we exit for any reason. */ @@ -959,7 +959,7 @@ int main(int argc, char *argv[]) /* Figure out where our daemons are first. */ ld->daemon_dir = find_daemon_dir(ld, argv[0]); if (!ld->daemon_dir) - errx(1, "Could not find daemons"); + errx(EXITCODE_SUBDAEMON_FAIL, "Could not find daemons"); /* Set up the feature bits for what we support */ ld->our_features = default_features(ld); @@ -1053,7 +1053,7 @@ int main(int argc, char *argv[]) * We also check that our node_id is what we expect: otherwise a change * in hsm_secret will have strange consequences! */ if (!wallet_sanity_check(ld->wallet)) - errx(1, "Wallet sanity check failed."); + errx(EXITCODE_WALLET_DB_MISMATCH, "Wallet sanity check failed."); /*~ Initialize the transaction filter with our pubkeys. */ init_txfilter(ld->wallet, ld->owned_txfilter); diff --git a/lightningd/options.c b/lightningd/options.c index 9333d21dabb6..7fc77e26b131 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -541,7 +541,7 @@ static char *opt_set_hsm_password(struct lightningd *ld) return err_msg; if (!streq(passwd, passwd_confirmation)) { - opt_exitcode = HSM_BAD_PASSWORD; + opt_exitcode = EXITCODE_HSM_BAD_PASSWORD; return "Passwords confirmation mismatch."; } free(passwd_confirmation); diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 41eb514aba2e..e6ae1e36c0b7 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -73,9 +73,9 @@ static void get_hsm_secret(struct secret *hsm_secret, fd = open(hsm_secret_path, O_RDONLY); if (fd < 0) - errx(ERROR_HSM_FILE, "Could not open hsm_secret"); + errx(EXITCODE_ERROR_HSM_FILE, "Could not open hsm_secret"); if (!read_all(fd, hsm_secret, sizeof(*hsm_secret))) - errx(ERROR_HSM_FILE, "Could not read hsm_secret"); + errx(EXITCODE_ERROR_HSM_FILE, "Could not read hsm_secret"); close(fd); } @@ -93,10 +93,10 @@ static void get_encrypted_hsm_secret(struct secret *hsm_secret, fd = open(hsm_secret_path, O_RDONLY); if (fd < 0) - errx(ERROR_HSM_FILE, "Could not open hsm_secret"); + errx(EXITCODE_ERROR_HSM_FILE, "Could not open hsm_secret"); if (!read_all(fd, encrypted_secret.data, ENCRYPTED_HSM_SECRET_LEN)) - errx(ERROR_HSM_FILE, "Could not read encrypted hsm_secret"); + errx(EXITCODE_ERROR_HSM_FILE, "Could not read encrypted hsm_secret"); exit_code = hsm_secret_encryption_key_with_exitcode(passwd, &key, &err); if (exit_code > 0) @@ -148,7 +148,7 @@ static bool hsm_secret_is_encrypted(const char *hsm_secret_path) { switch (is_hsm_secret_encrypted(hsm_secret_path)) { case -1: - err(ERROR_HSM_FILE, "Cannot open '%s'", hsm_secret_path); + err(EXITCODE_ERROR_HSM_FILE, "Cannot open '%s'", hsm_secret_path); case 1: return true; case 0: { @@ -156,7 +156,7 @@ static bool hsm_secret_is_encrypted(const char *hsm_secret_path) struct stat st; stat(hsm_secret_path, &st); if (st.st_size != 32) - errx(ERROR_HSM_FILE, + errx(EXITCODE_ERROR_HSM_FILE, "Invalid hsm_secret '%s' (neither plaintext " "nor encrypted).", hsm_secret_path); return false; @@ -198,13 +198,13 @@ static int decrypt_hsm(const char *hsm_secret_path) rename(hsm_secret_path, backup); fd = open(hsm_secret_path, O_CREAT|O_EXCL|O_WRONLY, 0400); if (fd < 0) - errx(ERROR_HSM_FILE, "Could not open new hsm_secret"); + errx(EXITCODE_ERROR_HSM_FILE, "Could not open new hsm_secret"); if (!write_all(fd, &hsm_secret, sizeof(hsm_secret))) { unlink_noerr(hsm_secret_path); close(fd); rename("hsm_secret.backup", hsm_secret_path); - errx(ERROR_HSM_FILE, + errx(EXITCODE_ERROR_HSM_FILE, "Failure writing plaintext seed to hsm_secret."); } @@ -212,7 +212,7 @@ static int decrypt_hsm(const char *hsm_secret_path) if (!ensure_hsm_secret_exists(fd, hsm_secret_path)) { unlink_noerr(hsm_secret_path); rename(backup, hsm_secret_path); - errx(ERROR_HSM_FILE, + errx(EXITCODE_ERROR_HSM_FILE, "Could not ensure hsm_secret existence."); } unlink_noerr(backup); @@ -272,7 +272,7 @@ static int encrypt_hsm(const char *hsm_secret_path) rename(hsm_secret_path, backup); fd = open(hsm_secret_path, O_CREAT|O_EXCL|O_WRONLY, 0400); if (fd < 0) - errx(ERROR_HSM_FILE, "Could not open new hsm_secret"); + errx(EXITCODE_ERROR_HSM_FILE, "Could not open new hsm_secret"); /* Write the encrypted hsm_secret. */ if (!write_all(fd, encrypted_hsm_secret.data, @@ -280,14 +280,14 @@ static int encrypt_hsm(const char *hsm_secret_path) unlink_noerr(hsm_secret_path); close(fd); rename(backup, hsm_secret_path); - errx(ERROR_HSM_FILE, "Failure writing cipher to hsm_secret."); + errx(EXITCODE_ERROR_HSM_FILE, "Failure writing cipher to hsm_secret."); } /* Be as paranoïd as in hsmd with the file state on disk. */ if (!ensure_hsm_secret_exists(fd, hsm_secret_path)) { unlink_noerr(hsm_secret_path); rename(backup, hsm_secret_path); - errx(ERROR_HSM_FILE, "Could not ensure hsm_secret existence."); + errx(EXITCODE_ERROR_HSM_FILE, "Could not ensure hsm_secret existence."); } unlink_noerr(backup); tal_free(dir); From 2d35c9a9298513da88be6fd357774a178b485a85 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 30 Jun 2022 10:38:32 +0200 Subject: [PATCH 1065/1530] msggen: Do not override method names when loading Schema We were overriding the name right when loading, which is bad since in some languages we use the method name as tag in the requests, thus renaming causes us to call something that isn't defined. Changelog-Fixed: cln-rpc: Fixed a naming mismatch for `ConnectPeer` causing `connectpeer` to be called on the JSON-RPC --- cln-grpc/src/server.rs | 8 ++++---- cln-rpc/src/model.rs | 4 ++-- contrib/msggen/msggen/utils/utils.py | 8 +------- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 5ebdfeaefddd..bd4183a0aa09 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -325,20 +325,20 @@ async fn connect_peer( let mut rpc = ClnRpc::new(&self.rpc_path) .await .map_err(|e| Status::new(Code::Internal, e.to_string()))?; - let result = rpc.call(Request::ConnectPeer(req)) + let result = rpc.call(Request::Connect(req)) .await .map_err(|e| Status::new( Code::Unknown, - format!("Error calling method ConnectPeer: {:?}", e)))?; + format!("Error calling method Connect: {:?}", e)))?; match result { - Response::ConnectPeer(r) => { + Response::Connect(r) => { trace!("connect_peer response: {:?}", r); Ok(tonic::Response::new((&r).into())) }, r => Err(Status::new( Code::Internal, format!( - "Unexpected result {:?} to method call ConnectPeer", + "Unexpected result {:?} to method call Connect", r ) )), diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 1ec55e6bf22d..c727466c359f 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -25,7 +25,7 @@ pub enum Request { AutoCleanInvoice(requests::AutocleaninvoiceRequest), CheckMessage(requests::CheckmessageRequest), Close(requests::CloseRequest), - ConnectPeer(requests::ConnectRequest), + Connect(requests::ConnectRequest), CreateInvoice(requests::CreateinvoiceRequest), Datastore(requests::DatastoreRequest), CreateOnion(requests::CreateonionRequest), @@ -75,7 +75,7 @@ pub enum Response { AutoCleanInvoice(responses::AutocleaninvoiceResponse), CheckMessage(responses::CheckmessageResponse), Close(responses::CloseResponse), - ConnectPeer(responses::ConnectResponse), + Connect(responses::ConnectResponse), CreateInvoice(responses::CreateinvoiceResponse), Datastore(responses::DatastoreResponse), CreateOnion(responses::CreateonionResponse), diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py index e68a1ce5e498..88fe1f6d4280 100644 --- a/contrib/msggen/msggen/utils/utils.py +++ b/contrib/msggen/msggen/utils/utils.py @@ -4,12 +4,6 @@ from msggen.model import Method, CompositeField, Service -# Sometimes we want to rename a method, due to a name clash -# FIXME: need to be generalized? -method_name_override = { - "Connect": "ConnectPeer", -} - def repo_root(): path = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]) @@ -34,7 +28,7 @@ def load_jsonrpc_method(name, schema_dir: str = None): response.typename += "Response" return Method( - name=method_name_override.get(name, name), + name, request=request, response=response, ) From bac322ccdbedb93df649a9dd245c20d18e324116 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 30 Jun 2022 11:08:50 +0200 Subject: [PATCH 1066/1530] pytest: Move generated grpc bindings to pyln-testing These may eventually end up in pyln-client, as they allow talking to the GRPC interface exposed by cln-grpc, however for now they are used for testing only. Once we have sufficient API and test coverage we can move them and leave imports in their place. --- Makefile | 14 +- contrib/pyln-testing/pyln/testing/node_pb2.py | 1350 +++++++++++++++ .../pyln/testing/node_pb2_grpc.py | 1485 +++++++++++++++++ .../pyln/testing/primitives_pb2.py | 146 ++ tests/test_cln_rs.py | 18 +- 5 files changed, 2998 insertions(+), 15 deletions(-) create mode 100644 contrib/pyln-testing/pyln/testing/node_pb2.py create mode 100644 contrib/pyln-testing/pyln/testing/node_pb2_grpc.py create mode 100644 contrib/pyln-testing/pyln/testing/primitives_pb2.py diff --git a/Makefile b/Makefile index 6c804b954884..40dcde7b53b3 100644 --- a/Makefile +++ b/Makefile @@ -383,15 +383,19 @@ ifneq ($(RUST),0) include cln-rpc/Makefile include cln-grpc/Makefile -GRPC_GEN = tests/node_pb2.py \ - tests/node_pb2_grpc.py \ - tests/primitives_pb2.py +GRPC_GEN = contrib/pyln-testing/pyln/testing/node_pb2.py \ + contrib/pyln-testing/pyln/testing/node_pb2_grpc.py \ + contrib/pyln-testing/pyln/testing/primitives_pb2.py ALL_TEST_GEN += $(GRPC_GEN) $(GRPC_GEN): cln-grpc/proto/node.proto cln-grpc/proto/primitives.proto - python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/node.proto --python_out=tests/ --grpc_python_out=tests/ --experimental_allow_proto3_optional - python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/primitives.proto --python_out=tests/ --grpc_python_out=tests/ --experimental_allow_proto3_optional + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/node.proto --python_out=contrib/pyln-testing/pyln/testing/ --grpc_python_out=contrib/pyln-testing/pyln/testing/ --experimental_allow_proto3_optional + python -m grpc_tools.protoc -I cln-grpc/proto cln-grpc/proto/primitives.proto --python_out=contrib/pyln-testing/pyln/testing/ --experimental_allow_proto3_optional + # The compiler assumes that the proto files are in the same + # directory structure as the generated files will be. Since we + # don't do that we need to path the files up. + find contrib/pyln-testing/pyln/testing/ -type f -name "*.py" -print0 | xargs -0 sed -i 's/^import \(.*\)_pb2 as .*__pb2/from . import \1_pb2 as \1__pb2/g' endif diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py new file mode 100644 index 000000000000..adfdf835414e --- /dev/null +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -0,0 +1,1350 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: node.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import primitives_pb2 as primitives__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t2\xfd\x15\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x62\x06proto3') + + + +_GETINFOREQUEST = DESCRIPTOR.message_types_by_name['GetinfoRequest'] +_GETINFORESPONSE = DESCRIPTOR.message_types_by_name['GetinfoResponse'] +_GETINFOOUR_FEATURES = DESCRIPTOR.message_types_by_name['GetinfoOur_features'] +_GETINFOADDRESS = DESCRIPTOR.message_types_by_name['GetinfoAddress'] +_GETINFOBINDING = DESCRIPTOR.message_types_by_name['GetinfoBinding'] +_LISTPEERSREQUEST = DESCRIPTOR.message_types_by_name['ListpeersRequest'] +_LISTPEERSRESPONSE = DESCRIPTOR.message_types_by_name['ListpeersResponse'] +_LISTPEERSPEERS = DESCRIPTOR.message_types_by_name['ListpeersPeers'] +_LISTPEERSPEERSLOG = DESCRIPTOR.message_types_by_name['ListpeersPeersLog'] +_LISTPEERSPEERSCHANNELS = DESCRIPTOR.message_types_by_name['ListpeersPeersChannels'] +_LISTPEERSPEERSCHANNELSFEERATE = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsFeerate'] +_LISTPEERSPEERSCHANNELSINFLIGHT = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsInflight'] +_LISTPEERSPEERSCHANNELSFUNDING = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsFunding'] +_LISTPEERSPEERSCHANNELSHTLCS = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsHtlcs'] +_LISTFUNDSREQUEST = DESCRIPTOR.message_types_by_name['ListfundsRequest'] +_LISTFUNDSRESPONSE = DESCRIPTOR.message_types_by_name['ListfundsResponse'] +_LISTFUNDSOUTPUTS = DESCRIPTOR.message_types_by_name['ListfundsOutputs'] +_LISTFUNDSCHANNELS = DESCRIPTOR.message_types_by_name['ListfundsChannels'] +_SENDPAYREQUEST = DESCRIPTOR.message_types_by_name['SendpayRequest'] +_SENDPAYRESPONSE = DESCRIPTOR.message_types_by_name['SendpayResponse'] +_SENDPAYROUTE = DESCRIPTOR.message_types_by_name['SendpayRoute'] +_LISTCHANNELSREQUEST = DESCRIPTOR.message_types_by_name['ListchannelsRequest'] +_LISTCHANNELSRESPONSE = DESCRIPTOR.message_types_by_name['ListchannelsResponse'] +_LISTCHANNELSCHANNELS = DESCRIPTOR.message_types_by_name['ListchannelsChannels'] +_ADDGOSSIPREQUEST = DESCRIPTOR.message_types_by_name['AddgossipRequest'] +_ADDGOSSIPRESPONSE = DESCRIPTOR.message_types_by_name['AddgossipResponse'] +_AUTOCLEANINVOICEREQUEST = DESCRIPTOR.message_types_by_name['AutocleaninvoiceRequest'] +_AUTOCLEANINVOICERESPONSE = DESCRIPTOR.message_types_by_name['AutocleaninvoiceResponse'] +_CHECKMESSAGEREQUEST = DESCRIPTOR.message_types_by_name['CheckmessageRequest'] +_CHECKMESSAGERESPONSE = DESCRIPTOR.message_types_by_name['CheckmessageResponse'] +_CLOSEREQUEST = DESCRIPTOR.message_types_by_name['CloseRequest'] +_CLOSERESPONSE = DESCRIPTOR.message_types_by_name['CloseResponse'] +_CONNECTREQUEST = DESCRIPTOR.message_types_by_name['ConnectRequest'] +_CONNECTRESPONSE = DESCRIPTOR.message_types_by_name['ConnectResponse'] +_CONNECTADDRESS = DESCRIPTOR.message_types_by_name['ConnectAddress'] +_CREATEINVOICEREQUEST = DESCRIPTOR.message_types_by_name['CreateinvoiceRequest'] +_CREATEINVOICERESPONSE = DESCRIPTOR.message_types_by_name['CreateinvoiceResponse'] +_DATASTOREREQUEST = DESCRIPTOR.message_types_by_name['DatastoreRequest'] +_DATASTORERESPONSE = DESCRIPTOR.message_types_by_name['DatastoreResponse'] +_CREATEONIONREQUEST = DESCRIPTOR.message_types_by_name['CreateonionRequest'] +_CREATEONIONRESPONSE = DESCRIPTOR.message_types_by_name['CreateonionResponse'] +_CREATEONIONHOPS = DESCRIPTOR.message_types_by_name['CreateonionHops'] +_DELDATASTOREREQUEST = DESCRIPTOR.message_types_by_name['DeldatastoreRequest'] +_DELDATASTORERESPONSE = DESCRIPTOR.message_types_by_name['DeldatastoreResponse'] +_DELEXPIREDINVOICEREQUEST = DESCRIPTOR.message_types_by_name['DelexpiredinvoiceRequest'] +_DELEXPIREDINVOICERESPONSE = DESCRIPTOR.message_types_by_name['DelexpiredinvoiceResponse'] +_DELINVOICEREQUEST = DESCRIPTOR.message_types_by_name['DelinvoiceRequest'] +_DELINVOICERESPONSE = DESCRIPTOR.message_types_by_name['DelinvoiceResponse'] +_INVOICEREQUEST = DESCRIPTOR.message_types_by_name['InvoiceRequest'] +_INVOICERESPONSE = DESCRIPTOR.message_types_by_name['InvoiceResponse'] +_LISTDATASTOREREQUEST = DESCRIPTOR.message_types_by_name['ListdatastoreRequest'] +_LISTDATASTORERESPONSE = DESCRIPTOR.message_types_by_name['ListdatastoreResponse'] +_LISTDATASTOREDATASTORE = DESCRIPTOR.message_types_by_name['ListdatastoreDatastore'] +_LISTINVOICESREQUEST = DESCRIPTOR.message_types_by_name['ListinvoicesRequest'] +_LISTINVOICESRESPONSE = DESCRIPTOR.message_types_by_name['ListinvoicesResponse'] +_LISTINVOICESINVOICES = DESCRIPTOR.message_types_by_name['ListinvoicesInvoices'] +_SENDONIONREQUEST = DESCRIPTOR.message_types_by_name['SendonionRequest'] +_SENDONIONRESPONSE = DESCRIPTOR.message_types_by_name['SendonionResponse'] +_SENDONIONFIRST_HOP = DESCRIPTOR.message_types_by_name['SendonionFirst_hop'] +_LISTSENDPAYSREQUEST = DESCRIPTOR.message_types_by_name['ListsendpaysRequest'] +_LISTSENDPAYSRESPONSE = DESCRIPTOR.message_types_by_name['ListsendpaysResponse'] +_LISTSENDPAYSPAYMENTS = DESCRIPTOR.message_types_by_name['ListsendpaysPayments'] +_LISTTRANSACTIONSREQUEST = DESCRIPTOR.message_types_by_name['ListtransactionsRequest'] +_LISTTRANSACTIONSRESPONSE = DESCRIPTOR.message_types_by_name['ListtransactionsResponse'] +_LISTTRANSACTIONSTRANSACTIONS = DESCRIPTOR.message_types_by_name['ListtransactionsTransactions'] +_LISTTRANSACTIONSTRANSACTIONSINPUTS = DESCRIPTOR.message_types_by_name['ListtransactionsTransactionsInputs'] +_LISTTRANSACTIONSTRANSACTIONSOUTPUTS = DESCRIPTOR.message_types_by_name['ListtransactionsTransactionsOutputs'] +_PAYREQUEST = DESCRIPTOR.message_types_by_name['PayRequest'] +_PAYRESPONSE = DESCRIPTOR.message_types_by_name['PayResponse'] +_LISTNODESREQUEST = DESCRIPTOR.message_types_by_name['ListnodesRequest'] +_LISTNODESRESPONSE = DESCRIPTOR.message_types_by_name['ListnodesResponse'] +_LISTNODESNODES = DESCRIPTOR.message_types_by_name['ListnodesNodes'] +_LISTNODESNODESADDRESSES = DESCRIPTOR.message_types_by_name['ListnodesNodesAddresses'] +_WAITANYINVOICEREQUEST = DESCRIPTOR.message_types_by_name['WaitanyinvoiceRequest'] +_WAITANYINVOICERESPONSE = DESCRIPTOR.message_types_by_name['WaitanyinvoiceResponse'] +_WAITINVOICEREQUEST = DESCRIPTOR.message_types_by_name['WaitinvoiceRequest'] +_WAITINVOICERESPONSE = DESCRIPTOR.message_types_by_name['WaitinvoiceResponse'] +_WAITSENDPAYREQUEST = DESCRIPTOR.message_types_by_name['WaitsendpayRequest'] +_WAITSENDPAYRESPONSE = DESCRIPTOR.message_types_by_name['WaitsendpayResponse'] +_NEWADDRREQUEST = DESCRIPTOR.message_types_by_name['NewaddrRequest'] +_NEWADDRRESPONSE = DESCRIPTOR.message_types_by_name['NewaddrResponse'] +_WITHDRAWREQUEST = DESCRIPTOR.message_types_by_name['WithdrawRequest'] +_WITHDRAWRESPONSE = DESCRIPTOR.message_types_by_name['WithdrawResponse'] +_KEYSENDREQUEST = DESCRIPTOR.message_types_by_name['KeysendRequest'] +_KEYSENDRESPONSE = DESCRIPTOR.message_types_by_name['KeysendResponse'] +_KEYSENDEXTRATLVS = DESCRIPTOR.message_types_by_name['KeysendExtratlvs'] +_FUNDPSBTREQUEST = DESCRIPTOR.message_types_by_name['FundpsbtRequest'] +_FUNDPSBTRESPONSE = DESCRIPTOR.message_types_by_name['FundpsbtResponse'] +_FUNDPSBTRESERVATIONS = DESCRIPTOR.message_types_by_name['FundpsbtReservations'] +_SENDPSBTREQUEST = DESCRIPTOR.message_types_by_name['SendpsbtRequest'] +_SENDPSBTRESPONSE = DESCRIPTOR.message_types_by_name['SendpsbtResponse'] +_SIGNPSBTREQUEST = DESCRIPTOR.message_types_by_name['SignpsbtRequest'] +_SIGNPSBTRESPONSE = DESCRIPTOR.message_types_by_name['SignpsbtResponse'] +_UTXOPSBTREQUEST = DESCRIPTOR.message_types_by_name['UtxopsbtRequest'] +_UTXOPSBTRESPONSE = DESCRIPTOR.message_types_by_name['UtxopsbtResponse'] +_UTXOPSBTRESERVATIONS = DESCRIPTOR.message_types_by_name['UtxopsbtReservations'] +_TXDISCARDREQUEST = DESCRIPTOR.message_types_by_name['TxdiscardRequest'] +_TXDISCARDRESPONSE = DESCRIPTOR.message_types_by_name['TxdiscardResponse'] +_TXPREPAREREQUEST = DESCRIPTOR.message_types_by_name['TxprepareRequest'] +_TXPREPARERESPONSE = DESCRIPTOR.message_types_by_name['TxprepareResponse'] +_TXSENDREQUEST = DESCRIPTOR.message_types_by_name['TxsendRequest'] +_TXSENDRESPONSE = DESCRIPTOR.message_types_by_name['TxsendResponse'] +_DISCONNECTREQUEST = DESCRIPTOR.message_types_by_name['DisconnectRequest'] +_DISCONNECTRESPONSE = DESCRIPTOR.message_types_by_name['DisconnectResponse'] +_FEERATESREQUEST = DESCRIPTOR.message_types_by_name['FeeratesRequest'] +_FEERATESRESPONSE = DESCRIPTOR.message_types_by_name['FeeratesResponse'] +_FEERATESPERKB = DESCRIPTOR.message_types_by_name['FeeratesPerkb'] +_FEERATESPERKW = DESCRIPTOR.message_types_by_name['FeeratesPerkw'] +_FEERATESONCHAIN_FEE_ESTIMATES = DESCRIPTOR.message_types_by_name['FeeratesOnchain_fee_estimates'] +_GETROUTEREQUEST = DESCRIPTOR.message_types_by_name['GetrouteRequest'] +_GETROUTERESPONSE = DESCRIPTOR.message_types_by_name['GetrouteResponse'] +_GETROUTEROUTE = DESCRIPTOR.message_types_by_name['GetrouteRoute'] +_LISTFORWARDSREQUEST = DESCRIPTOR.message_types_by_name['ListforwardsRequest'] +_LISTFORWARDSRESPONSE = DESCRIPTOR.message_types_by_name['ListforwardsResponse'] +_LISTFORWARDSFORWARDS = DESCRIPTOR.message_types_by_name['ListforwardsForwards'] +_LISTPAYSREQUEST = DESCRIPTOR.message_types_by_name['ListpaysRequest'] +_LISTPAYSRESPONSE = DESCRIPTOR.message_types_by_name['ListpaysResponse'] +_LISTPAYSPAYS = DESCRIPTOR.message_types_by_name['ListpaysPays'] +_PINGREQUEST = DESCRIPTOR.message_types_by_name['PingRequest'] +_PINGRESPONSE = DESCRIPTOR.message_types_by_name['PingResponse'] +_SIGNMESSAGEREQUEST = DESCRIPTOR.message_types_by_name['SignmessageRequest'] +_SIGNMESSAGERESPONSE = DESCRIPTOR.message_types_by_name['SignmessageResponse'] +_GETINFOADDRESS_GETINFOADDRESSTYPE = _GETINFOADDRESS.enum_types_by_name['GetinfoAddressType'] +_GETINFOBINDING_GETINFOBINDINGTYPE = _GETINFOBINDING.enum_types_by_name['GetinfoBindingType'] +_LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE = _LISTPEERSPEERSLOG.enum_types_by_name['ListpeersPeersLogType'] +_LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE = _LISTPEERSPEERSCHANNELS.enum_types_by_name['ListpeersPeersChannelsState'] +_LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION = _LISTPEERSPEERSCHANNELSHTLCS.enum_types_by_name['ListpeersPeersChannelsHtlcsDirection'] +_LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS = _LISTFUNDSOUTPUTS.enum_types_by_name['ListfundsOutputsStatus'] +_SENDPAYRESPONSE_SENDPAYSTATUS = _SENDPAYRESPONSE.enum_types_by_name['SendpayStatus'] +_CLOSERESPONSE_CLOSETYPE = _CLOSERESPONSE.enum_types_by_name['CloseType'] +_CONNECTRESPONSE_CONNECTDIRECTION = _CONNECTRESPONSE.enum_types_by_name['ConnectDirection'] +_CONNECTADDRESS_CONNECTADDRESSTYPE = _CONNECTADDRESS.enum_types_by_name['ConnectAddressType'] +_CREATEINVOICERESPONSE_CREATEINVOICESTATUS = _CREATEINVOICERESPONSE.enum_types_by_name['CreateinvoiceStatus'] +_DATASTOREREQUEST_DATASTOREMODE = _DATASTOREREQUEST.enum_types_by_name['DatastoreMode'] +_DELINVOICEREQUEST_DELINVOICESTATUS = _DELINVOICEREQUEST.enum_types_by_name['DelinvoiceStatus'] +_DELINVOICERESPONSE_DELINVOICESTATUS = _DELINVOICERESPONSE.enum_types_by_name['DelinvoiceStatus'] +_LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS = _LISTINVOICESINVOICES.enum_types_by_name['ListinvoicesInvoicesStatus'] +_SENDONIONRESPONSE_SENDONIONSTATUS = _SENDONIONRESPONSE.enum_types_by_name['SendonionStatus'] +_LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS = _LISTSENDPAYSREQUEST.enum_types_by_name['ListsendpaysStatus'] +_LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS = _LISTSENDPAYSPAYMENTS.enum_types_by_name['ListsendpaysPaymentsStatus'] +_LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE = _LISTTRANSACTIONSTRANSACTIONSINPUTS.enum_types_by_name['ListtransactionsTransactionsInputsType'] +_LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE = _LISTTRANSACTIONSTRANSACTIONSOUTPUTS.enum_types_by_name['ListtransactionsTransactionsOutputsType'] +_PAYRESPONSE_PAYSTATUS = _PAYRESPONSE.enum_types_by_name['PayStatus'] +_LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE = _LISTNODESNODESADDRESSES.enum_types_by_name['ListnodesNodesAddressesType'] +_WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS = _WAITANYINVOICERESPONSE.enum_types_by_name['WaitanyinvoiceStatus'] +_WAITINVOICERESPONSE_WAITINVOICESTATUS = _WAITINVOICERESPONSE.enum_types_by_name['WaitinvoiceStatus'] +_WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS = _WAITSENDPAYRESPONSE.enum_types_by_name['WaitsendpayStatus'] +_NEWADDRREQUEST_NEWADDRADDRESSTYPE = _NEWADDRREQUEST.enum_types_by_name['NewaddrAddresstype'] +_KEYSENDRESPONSE_KEYSENDSTATUS = _KEYSENDRESPONSE.enum_types_by_name['KeysendStatus'] +_FEERATESREQUEST_FEERATESSTYLE = _FEERATESREQUEST.enum_types_by_name['FeeratesStyle'] +_GETROUTEROUTE_GETROUTEROUTESTYLE = _GETROUTEROUTE.enum_types_by_name['GetrouteRouteStyle'] +_LISTFORWARDSREQUEST_LISTFORWARDSSTATUS = _LISTFORWARDSREQUEST.enum_types_by_name['ListforwardsStatus'] +_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS = _LISTFORWARDSFORWARDS.enum_types_by_name['ListforwardsForwardsStatus'] +_LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE = _LISTFORWARDSFORWARDS.enum_types_by_name['ListforwardsForwardsStyle'] +_LISTPAYSREQUEST_LISTPAYSSTATUS = _LISTPAYSREQUEST.enum_types_by_name['ListpaysStatus'] +_LISTPAYSPAYS_LISTPAYSPAYSSTATUS = _LISTPAYSPAYS.enum_types_by_name['ListpaysPaysStatus'] +GetinfoRequest = _reflection.GeneratedProtocolMessageType('GetinfoRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETINFOREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetinfoRequest) + }) +_sym_db.RegisterMessage(GetinfoRequest) + +GetinfoResponse = _reflection.GeneratedProtocolMessageType('GetinfoResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETINFORESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetinfoResponse) + }) +_sym_db.RegisterMessage(GetinfoResponse) + +GetinfoOur_features = _reflection.GeneratedProtocolMessageType('GetinfoOur_features', (_message.Message,), { + 'DESCRIPTOR' : _GETINFOOUR_FEATURES, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetinfoOur_features) + }) +_sym_db.RegisterMessage(GetinfoOur_features) + +GetinfoAddress = _reflection.GeneratedProtocolMessageType('GetinfoAddress', (_message.Message,), { + 'DESCRIPTOR' : _GETINFOADDRESS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetinfoAddress) + }) +_sym_db.RegisterMessage(GetinfoAddress) + +GetinfoBinding = _reflection.GeneratedProtocolMessageType('GetinfoBinding', (_message.Message,), { + 'DESCRIPTOR' : _GETINFOBINDING, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetinfoBinding) + }) +_sym_db.RegisterMessage(GetinfoBinding) + +ListpeersRequest = _reflection.GeneratedProtocolMessageType('ListpeersRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersRequest) + }) +_sym_db.RegisterMessage(ListpeersRequest) + +ListpeersResponse = _reflection.GeneratedProtocolMessageType('ListpeersResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersResponse) + }) +_sym_db.RegisterMessage(ListpeersResponse) + +ListpeersPeers = _reflection.GeneratedProtocolMessageType('ListpeersPeers', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeers) + }) +_sym_db.RegisterMessage(ListpeersPeers) + +ListpeersPeersLog = _reflection.GeneratedProtocolMessageType('ListpeersPeersLog', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERSLOG, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeersLog) + }) +_sym_db.RegisterMessage(ListpeersPeersLog) + +ListpeersPeersChannels = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannels', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannels) + }) +_sym_db.RegisterMessage(ListpeersPeersChannels) + +ListpeersPeersChannelsFeerate = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsFeerate', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSFEERATE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsFeerate) + }) +_sym_db.RegisterMessage(ListpeersPeersChannelsFeerate) + +ListpeersPeersChannelsInflight = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsInflight', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSINFLIGHT, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsInflight) + }) +_sym_db.RegisterMessage(ListpeersPeersChannelsInflight) + +ListpeersPeersChannelsFunding = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsFunding', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSFUNDING, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsFunding) + }) +_sym_db.RegisterMessage(ListpeersPeersChannelsFunding) + +ListpeersPeersChannelsHtlcs = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsHtlcs', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSHTLCS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsHtlcs) + }) +_sym_db.RegisterMessage(ListpeersPeersChannelsHtlcs) + +ListfundsRequest = _reflection.GeneratedProtocolMessageType('ListfundsRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTFUNDSREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListfundsRequest) + }) +_sym_db.RegisterMessage(ListfundsRequest) + +ListfundsResponse = _reflection.GeneratedProtocolMessageType('ListfundsResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTFUNDSRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListfundsResponse) + }) +_sym_db.RegisterMessage(ListfundsResponse) + +ListfundsOutputs = _reflection.GeneratedProtocolMessageType('ListfundsOutputs', (_message.Message,), { + 'DESCRIPTOR' : _LISTFUNDSOUTPUTS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListfundsOutputs) + }) +_sym_db.RegisterMessage(ListfundsOutputs) + +ListfundsChannels = _reflection.GeneratedProtocolMessageType('ListfundsChannels', (_message.Message,), { + 'DESCRIPTOR' : _LISTFUNDSCHANNELS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListfundsChannels) + }) +_sym_db.RegisterMessage(ListfundsChannels) + +SendpayRequest = _reflection.GeneratedProtocolMessageType('SendpayRequest', (_message.Message,), { + 'DESCRIPTOR' : _SENDPAYREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendpayRequest) + }) +_sym_db.RegisterMessage(SendpayRequest) + +SendpayResponse = _reflection.GeneratedProtocolMessageType('SendpayResponse', (_message.Message,), { + 'DESCRIPTOR' : _SENDPAYRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendpayResponse) + }) +_sym_db.RegisterMessage(SendpayResponse) + +SendpayRoute = _reflection.GeneratedProtocolMessageType('SendpayRoute', (_message.Message,), { + 'DESCRIPTOR' : _SENDPAYROUTE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendpayRoute) + }) +_sym_db.RegisterMessage(SendpayRoute) + +ListchannelsRequest = _reflection.GeneratedProtocolMessageType('ListchannelsRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTCHANNELSREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListchannelsRequest) + }) +_sym_db.RegisterMessage(ListchannelsRequest) + +ListchannelsResponse = _reflection.GeneratedProtocolMessageType('ListchannelsResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTCHANNELSRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListchannelsResponse) + }) +_sym_db.RegisterMessage(ListchannelsResponse) + +ListchannelsChannels = _reflection.GeneratedProtocolMessageType('ListchannelsChannels', (_message.Message,), { + 'DESCRIPTOR' : _LISTCHANNELSCHANNELS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListchannelsChannels) + }) +_sym_db.RegisterMessage(ListchannelsChannels) + +AddgossipRequest = _reflection.GeneratedProtocolMessageType('AddgossipRequest', (_message.Message,), { + 'DESCRIPTOR' : _ADDGOSSIPREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.AddgossipRequest) + }) +_sym_db.RegisterMessage(AddgossipRequest) + +AddgossipResponse = _reflection.GeneratedProtocolMessageType('AddgossipResponse', (_message.Message,), { + 'DESCRIPTOR' : _ADDGOSSIPRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.AddgossipResponse) + }) +_sym_db.RegisterMessage(AddgossipResponse) + +AutocleaninvoiceRequest = _reflection.GeneratedProtocolMessageType('AutocleaninvoiceRequest', (_message.Message,), { + 'DESCRIPTOR' : _AUTOCLEANINVOICEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.AutocleaninvoiceRequest) + }) +_sym_db.RegisterMessage(AutocleaninvoiceRequest) + +AutocleaninvoiceResponse = _reflection.GeneratedProtocolMessageType('AutocleaninvoiceResponse', (_message.Message,), { + 'DESCRIPTOR' : _AUTOCLEANINVOICERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.AutocleaninvoiceResponse) + }) +_sym_db.RegisterMessage(AutocleaninvoiceResponse) + +CheckmessageRequest = _reflection.GeneratedProtocolMessageType('CheckmessageRequest', (_message.Message,), { + 'DESCRIPTOR' : _CHECKMESSAGEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CheckmessageRequest) + }) +_sym_db.RegisterMessage(CheckmessageRequest) + +CheckmessageResponse = _reflection.GeneratedProtocolMessageType('CheckmessageResponse', (_message.Message,), { + 'DESCRIPTOR' : _CHECKMESSAGERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CheckmessageResponse) + }) +_sym_db.RegisterMessage(CheckmessageResponse) + +CloseRequest = _reflection.GeneratedProtocolMessageType('CloseRequest', (_message.Message,), { + 'DESCRIPTOR' : _CLOSEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CloseRequest) + }) +_sym_db.RegisterMessage(CloseRequest) + +CloseResponse = _reflection.GeneratedProtocolMessageType('CloseResponse', (_message.Message,), { + 'DESCRIPTOR' : _CLOSERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CloseResponse) + }) +_sym_db.RegisterMessage(CloseResponse) + +ConnectRequest = _reflection.GeneratedProtocolMessageType('ConnectRequest', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ConnectRequest) + }) +_sym_db.RegisterMessage(ConnectRequest) + +ConnectResponse = _reflection.GeneratedProtocolMessageType('ConnectResponse', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ConnectResponse) + }) +_sym_db.RegisterMessage(ConnectResponse) + +ConnectAddress = _reflection.GeneratedProtocolMessageType('ConnectAddress', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTADDRESS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ConnectAddress) + }) +_sym_db.RegisterMessage(ConnectAddress) + +CreateinvoiceRequest = _reflection.GeneratedProtocolMessageType('CreateinvoiceRequest', (_message.Message,), { + 'DESCRIPTOR' : _CREATEINVOICEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CreateinvoiceRequest) + }) +_sym_db.RegisterMessage(CreateinvoiceRequest) + +CreateinvoiceResponse = _reflection.GeneratedProtocolMessageType('CreateinvoiceResponse', (_message.Message,), { + 'DESCRIPTOR' : _CREATEINVOICERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CreateinvoiceResponse) + }) +_sym_db.RegisterMessage(CreateinvoiceResponse) + +DatastoreRequest = _reflection.GeneratedProtocolMessageType('DatastoreRequest', (_message.Message,), { + 'DESCRIPTOR' : _DATASTOREREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DatastoreRequest) + }) +_sym_db.RegisterMessage(DatastoreRequest) + +DatastoreResponse = _reflection.GeneratedProtocolMessageType('DatastoreResponse', (_message.Message,), { + 'DESCRIPTOR' : _DATASTORERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DatastoreResponse) + }) +_sym_db.RegisterMessage(DatastoreResponse) + +CreateonionRequest = _reflection.GeneratedProtocolMessageType('CreateonionRequest', (_message.Message,), { + 'DESCRIPTOR' : _CREATEONIONREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CreateonionRequest) + }) +_sym_db.RegisterMessage(CreateonionRequest) + +CreateonionResponse = _reflection.GeneratedProtocolMessageType('CreateonionResponse', (_message.Message,), { + 'DESCRIPTOR' : _CREATEONIONRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CreateonionResponse) + }) +_sym_db.RegisterMessage(CreateonionResponse) + +CreateonionHops = _reflection.GeneratedProtocolMessageType('CreateonionHops', (_message.Message,), { + 'DESCRIPTOR' : _CREATEONIONHOPS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.CreateonionHops) + }) +_sym_db.RegisterMessage(CreateonionHops) + +DeldatastoreRequest = _reflection.GeneratedProtocolMessageType('DeldatastoreRequest', (_message.Message,), { + 'DESCRIPTOR' : _DELDATASTOREREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DeldatastoreRequest) + }) +_sym_db.RegisterMessage(DeldatastoreRequest) + +DeldatastoreResponse = _reflection.GeneratedProtocolMessageType('DeldatastoreResponse', (_message.Message,), { + 'DESCRIPTOR' : _DELDATASTORERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DeldatastoreResponse) + }) +_sym_db.RegisterMessage(DeldatastoreResponse) + +DelexpiredinvoiceRequest = _reflection.GeneratedProtocolMessageType('DelexpiredinvoiceRequest', (_message.Message,), { + 'DESCRIPTOR' : _DELEXPIREDINVOICEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DelexpiredinvoiceRequest) + }) +_sym_db.RegisterMessage(DelexpiredinvoiceRequest) + +DelexpiredinvoiceResponse = _reflection.GeneratedProtocolMessageType('DelexpiredinvoiceResponse', (_message.Message,), { + 'DESCRIPTOR' : _DELEXPIREDINVOICERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DelexpiredinvoiceResponse) + }) +_sym_db.RegisterMessage(DelexpiredinvoiceResponse) + +DelinvoiceRequest = _reflection.GeneratedProtocolMessageType('DelinvoiceRequest', (_message.Message,), { + 'DESCRIPTOR' : _DELINVOICEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DelinvoiceRequest) + }) +_sym_db.RegisterMessage(DelinvoiceRequest) + +DelinvoiceResponse = _reflection.GeneratedProtocolMessageType('DelinvoiceResponse', (_message.Message,), { + 'DESCRIPTOR' : _DELINVOICERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DelinvoiceResponse) + }) +_sym_db.RegisterMessage(DelinvoiceResponse) + +InvoiceRequest = _reflection.GeneratedProtocolMessageType('InvoiceRequest', (_message.Message,), { + 'DESCRIPTOR' : _INVOICEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.InvoiceRequest) + }) +_sym_db.RegisterMessage(InvoiceRequest) + +InvoiceResponse = _reflection.GeneratedProtocolMessageType('InvoiceResponse', (_message.Message,), { + 'DESCRIPTOR' : _INVOICERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.InvoiceResponse) + }) +_sym_db.RegisterMessage(InvoiceResponse) + +ListdatastoreRequest = _reflection.GeneratedProtocolMessageType('ListdatastoreRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTDATASTOREREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListdatastoreRequest) + }) +_sym_db.RegisterMessage(ListdatastoreRequest) + +ListdatastoreResponse = _reflection.GeneratedProtocolMessageType('ListdatastoreResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTDATASTORERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListdatastoreResponse) + }) +_sym_db.RegisterMessage(ListdatastoreResponse) + +ListdatastoreDatastore = _reflection.GeneratedProtocolMessageType('ListdatastoreDatastore', (_message.Message,), { + 'DESCRIPTOR' : _LISTDATASTOREDATASTORE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListdatastoreDatastore) + }) +_sym_db.RegisterMessage(ListdatastoreDatastore) + +ListinvoicesRequest = _reflection.GeneratedProtocolMessageType('ListinvoicesRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTINVOICESREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListinvoicesRequest) + }) +_sym_db.RegisterMessage(ListinvoicesRequest) + +ListinvoicesResponse = _reflection.GeneratedProtocolMessageType('ListinvoicesResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTINVOICESRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListinvoicesResponse) + }) +_sym_db.RegisterMessage(ListinvoicesResponse) + +ListinvoicesInvoices = _reflection.GeneratedProtocolMessageType('ListinvoicesInvoices', (_message.Message,), { + 'DESCRIPTOR' : _LISTINVOICESINVOICES, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListinvoicesInvoices) + }) +_sym_db.RegisterMessage(ListinvoicesInvoices) + +SendonionRequest = _reflection.GeneratedProtocolMessageType('SendonionRequest', (_message.Message,), { + 'DESCRIPTOR' : _SENDONIONREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendonionRequest) + }) +_sym_db.RegisterMessage(SendonionRequest) + +SendonionResponse = _reflection.GeneratedProtocolMessageType('SendonionResponse', (_message.Message,), { + 'DESCRIPTOR' : _SENDONIONRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendonionResponse) + }) +_sym_db.RegisterMessage(SendonionResponse) + +SendonionFirst_hop = _reflection.GeneratedProtocolMessageType('SendonionFirst_hop', (_message.Message,), { + 'DESCRIPTOR' : _SENDONIONFIRST_HOP, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendonionFirst_hop) + }) +_sym_db.RegisterMessage(SendonionFirst_hop) + +ListsendpaysRequest = _reflection.GeneratedProtocolMessageType('ListsendpaysRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTSENDPAYSREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListsendpaysRequest) + }) +_sym_db.RegisterMessage(ListsendpaysRequest) + +ListsendpaysResponse = _reflection.GeneratedProtocolMessageType('ListsendpaysResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTSENDPAYSRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListsendpaysResponse) + }) +_sym_db.RegisterMessage(ListsendpaysResponse) + +ListsendpaysPayments = _reflection.GeneratedProtocolMessageType('ListsendpaysPayments', (_message.Message,), { + 'DESCRIPTOR' : _LISTSENDPAYSPAYMENTS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListsendpaysPayments) + }) +_sym_db.RegisterMessage(ListsendpaysPayments) + +ListtransactionsRequest = _reflection.GeneratedProtocolMessageType('ListtransactionsRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTTRANSACTIONSREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListtransactionsRequest) + }) +_sym_db.RegisterMessage(ListtransactionsRequest) + +ListtransactionsResponse = _reflection.GeneratedProtocolMessageType('ListtransactionsResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTTRANSACTIONSRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListtransactionsResponse) + }) +_sym_db.RegisterMessage(ListtransactionsResponse) + +ListtransactionsTransactions = _reflection.GeneratedProtocolMessageType('ListtransactionsTransactions', (_message.Message,), { + 'DESCRIPTOR' : _LISTTRANSACTIONSTRANSACTIONS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListtransactionsTransactions) + }) +_sym_db.RegisterMessage(ListtransactionsTransactions) + +ListtransactionsTransactionsInputs = _reflection.GeneratedProtocolMessageType('ListtransactionsTransactionsInputs', (_message.Message,), { + 'DESCRIPTOR' : _LISTTRANSACTIONSTRANSACTIONSINPUTS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListtransactionsTransactionsInputs) + }) +_sym_db.RegisterMessage(ListtransactionsTransactionsInputs) + +ListtransactionsTransactionsOutputs = _reflection.GeneratedProtocolMessageType('ListtransactionsTransactionsOutputs', (_message.Message,), { + 'DESCRIPTOR' : _LISTTRANSACTIONSTRANSACTIONSOUTPUTS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListtransactionsTransactionsOutputs) + }) +_sym_db.RegisterMessage(ListtransactionsTransactionsOutputs) + +PayRequest = _reflection.GeneratedProtocolMessageType('PayRequest', (_message.Message,), { + 'DESCRIPTOR' : _PAYREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.PayRequest) + }) +_sym_db.RegisterMessage(PayRequest) + +PayResponse = _reflection.GeneratedProtocolMessageType('PayResponse', (_message.Message,), { + 'DESCRIPTOR' : _PAYRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.PayResponse) + }) +_sym_db.RegisterMessage(PayResponse) + +ListnodesRequest = _reflection.GeneratedProtocolMessageType('ListnodesRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTNODESREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListnodesRequest) + }) +_sym_db.RegisterMessage(ListnodesRequest) + +ListnodesResponse = _reflection.GeneratedProtocolMessageType('ListnodesResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTNODESRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListnodesResponse) + }) +_sym_db.RegisterMessage(ListnodesResponse) + +ListnodesNodes = _reflection.GeneratedProtocolMessageType('ListnodesNodes', (_message.Message,), { + 'DESCRIPTOR' : _LISTNODESNODES, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListnodesNodes) + }) +_sym_db.RegisterMessage(ListnodesNodes) + +ListnodesNodesAddresses = _reflection.GeneratedProtocolMessageType('ListnodesNodesAddresses', (_message.Message,), { + 'DESCRIPTOR' : _LISTNODESNODESADDRESSES, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListnodesNodesAddresses) + }) +_sym_db.RegisterMessage(ListnodesNodesAddresses) + +WaitanyinvoiceRequest = _reflection.GeneratedProtocolMessageType('WaitanyinvoiceRequest', (_message.Message,), { + 'DESCRIPTOR' : _WAITANYINVOICEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WaitanyinvoiceRequest) + }) +_sym_db.RegisterMessage(WaitanyinvoiceRequest) + +WaitanyinvoiceResponse = _reflection.GeneratedProtocolMessageType('WaitanyinvoiceResponse', (_message.Message,), { + 'DESCRIPTOR' : _WAITANYINVOICERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WaitanyinvoiceResponse) + }) +_sym_db.RegisterMessage(WaitanyinvoiceResponse) + +WaitinvoiceRequest = _reflection.GeneratedProtocolMessageType('WaitinvoiceRequest', (_message.Message,), { + 'DESCRIPTOR' : _WAITINVOICEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WaitinvoiceRequest) + }) +_sym_db.RegisterMessage(WaitinvoiceRequest) + +WaitinvoiceResponse = _reflection.GeneratedProtocolMessageType('WaitinvoiceResponse', (_message.Message,), { + 'DESCRIPTOR' : _WAITINVOICERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WaitinvoiceResponse) + }) +_sym_db.RegisterMessage(WaitinvoiceResponse) + +WaitsendpayRequest = _reflection.GeneratedProtocolMessageType('WaitsendpayRequest', (_message.Message,), { + 'DESCRIPTOR' : _WAITSENDPAYREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WaitsendpayRequest) + }) +_sym_db.RegisterMessage(WaitsendpayRequest) + +WaitsendpayResponse = _reflection.GeneratedProtocolMessageType('WaitsendpayResponse', (_message.Message,), { + 'DESCRIPTOR' : _WAITSENDPAYRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WaitsendpayResponse) + }) +_sym_db.RegisterMessage(WaitsendpayResponse) + +NewaddrRequest = _reflection.GeneratedProtocolMessageType('NewaddrRequest', (_message.Message,), { + 'DESCRIPTOR' : _NEWADDRREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.NewaddrRequest) + }) +_sym_db.RegisterMessage(NewaddrRequest) + +NewaddrResponse = _reflection.GeneratedProtocolMessageType('NewaddrResponse', (_message.Message,), { + 'DESCRIPTOR' : _NEWADDRRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.NewaddrResponse) + }) +_sym_db.RegisterMessage(NewaddrResponse) + +WithdrawRequest = _reflection.GeneratedProtocolMessageType('WithdrawRequest', (_message.Message,), { + 'DESCRIPTOR' : _WITHDRAWREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WithdrawRequest) + }) +_sym_db.RegisterMessage(WithdrawRequest) + +WithdrawResponse = _reflection.GeneratedProtocolMessageType('WithdrawResponse', (_message.Message,), { + 'DESCRIPTOR' : _WITHDRAWRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.WithdrawResponse) + }) +_sym_db.RegisterMessage(WithdrawResponse) + +KeysendRequest = _reflection.GeneratedProtocolMessageType('KeysendRequest', (_message.Message,), { + 'DESCRIPTOR' : _KEYSENDREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.KeysendRequest) + }) +_sym_db.RegisterMessage(KeysendRequest) + +KeysendResponse = _reflection.GeneratedProtocolMessageType('KeysendResponse', (_message.Message,), { + 'DESCRIPTOR' : _KEYSENDRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.KeysendResponse) + }) +_sym_db.RegisterMessage(KeysendResponse) + +KeysendExtratlvs = _reflection.GeneratedProtocolMessageType('KeysendExtratlvs', (_message.Message,), { + 'DESCRIPTOR' : _KEYSENDEXTRATLVS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.KeysendExtratlvs) + }) +_sym_db.RegisterMessage(KeysendExtratlvs) + +FundpsbtRequest = _reflection.GeneratedProtocolMessageType('FundpsbtRequest', (_message.Message,), { + 'DESCRIPTOR' : _FUNDPSBTREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FundpsbtRequest) + }) +_sym_db.RegisterMessage(FundpsbtRequest) + +FundpsbtResponse = _reflection.GeneratedProtocolMessageType('FundpsbtResponse', (_message.Message,), { + 'DESCRIPTOR' : _FUNDPSBTRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FundpsbtResponse) + }) +_sym_db.RegisterMessage(FundpsbtResponse) + +FundpsbtReservations = _reflection.GeneratedProtocolMessageType('FundpsbtReservations', (_message.Message,), { + 'DESCRIPTOR' : _FUNDPSBTRESERVATIONS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FundpsbtReservations) + }) +_sym_db.RegisterMessage(FundpsbtReservations) + +SendpsbtRequest = _reflection.GeneratedProtocolMessageType('SendpsbtRequest', (_message.Message,), { + 'DESCRIPTOR' : _SENDPSBTREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendpsbtRequest) + }) +_sym_db.RegisterMessage(SendpsbtRequest) + +SendpsbtResponse = _reflection.GeneratedProtocolMessageType('SendpsbtResponse', (_message.Message,), { + 'DESCRIPTOR' : _SENDPSBTRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SendpsbtResponse) + }) +_sym_db.RegisterMessage(SendpsbtResponse) + +SignpsbtRequest = _reflection.GeneratedProtocolMessageType('SignpsbtRequest', (_message.Message,), { + 'DESCRIPTOR' : _SIGNPSBTREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SignpsbtRequest) + }) +_sym_db.RegisterMessage(SignpsbtRequest) + +SignpsbtResponse = _reflection.GeneratedProtocolMessageType('SignpsbtResponse', (_message.Message,), { + 'DESCRIPTOR' : _SIGNPSBTRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SignpsbtResponse) + }) +_sym_db.RegisterMessage(SignpsbtResponse) + +UtxopsbtRequest = _reflection.GeneratedProtocolMessageType('UtxopsbtRequest', (_message.Message,), { + 'DESCRIPTOR' : _UTXOPSBTREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.UtxopsbtRequest) + }) +_sym_db.RegisterMessage(UtxopsbtRequest) + +UtxopsbtResponse = _reflection.GeneratedProtocolMessageType('UtxopsbtResponse', (_message.Message,), { + 'DESCRIPTOR' : _UTXOPSBTRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.UtxopsbtResponse) + }) +_sym_db.RegisterMessage(UtxopsbtResponse) + +UtxopsbtReservations = _reflection.GeneratedProtocolMessageType('UtxopsbtReservations', (_message.Message,), { + 'DESCRIPTOR' : _UTXOPSBTRESERVATIONS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.UtxopsbtReservations) + }) +_sym_db.RegisterMessage(UtxopsbtReservations) + +TxdiscardRequest = _reflection.GeneratedProtocolMessageType('TxdiscardRequest', (_message.Message,), { + 'DESCRIPTOR' : _TXDISCARDREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.TxdiscardRequest) + }) +_sym_db.RegisterMessage(TxdiscardRequest) + +TxdiscardResponse = _reflection.GeneratedProtocolMessageType('TxdiscardResponse', (_message.Message,), { + 'DESCRIPTOR' : _TXDISCARDRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.TxdiscardResponse) + }) +_sym_db.RegisterMessage(TxdiscardResponse) + +TxprepareRequest = _reflection.GeneratedProtocolMessageType('TxprepareRequest', (_message.Message,), { + 'DESCRIPTOR' : _TXPREPAREREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.TxprepareRequest) + }) +_sym_db.RegisterMessage(TxprepareRequest) + +TxprepareResponse = _reflection.GeneratedProtocolMessageType('TxprepareResponse', (_message.Message,), { + 'DESCRIPTOR' : _TXPREPARERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.TxprepareResponse) + }) +_sym_db.RegisterMessage(TxprepareResponse) + +TxsendRequest = _reflection.GeneratedProtocolMessageType('TxsendRequest', (_message.Message,), { + 'DESCRIPTOR' : _TXSENDREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.TxsendRequest) + }) +_sym_db.RegisterMessage(TxsendRequest) + +TxsendResponse = _reflection.GeneratedProtocolMessageType('TxsendResponse', (_message.Message,), { + 'DESCRIPTOR' : _TXSENDRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.TxsendResponse) + }) +_sym_db.RegisterMessage(TxsendResponse) + +DisconnectRequest = _reflection.GeneratedProtocolMessageType('DisconnectRequest', (_message.Message,), { + 'DESCRIPTOR' : _DISCONNECTREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DisconnectRequest) + }) +_sym_db.RegisterMessage(DisconnectRequest) + +DisconnectResponse = _reflection.GeneratedProtocolMessageType('DisconnectResponse', (_message.Message,), { + 'DESCRIPTOR' : _DISCONNECTRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.DisconnectResponse) + }) +_sym_db.RegisterMessage(DisconnectResponse) + +FeeratesRequest = _reflection.GeneratedProtocolMessageType('FeeratesRequest', (_message.Message,), { + 'DESCRIPTOR' : _FEERATESREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FeeratesRequest) + }) +_sym_db.RegisterMessage(FeeratesRequest) + +FeeratesResponse = _reflection.GeneratedProtocolMessageType('FeeratesResponse', (_message.Message,), { + 'DESCRIPTOR' : _FEERATESRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FeeratesResponse) + }) +_sym_db.RegisterMessage(FeeratesResponse) + +FeeratesPerkb = _reflection.GeneratedProtocolMessageType('FeeratesPerkb', (_message.Message,), { + 'DESCRIPTOR' : _FEERATESPERKB, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FeeratesPerkb) + }) +_sym_db.RegisterMessage(FeeratesPerkb) + +FeeratesPerkw = _reflection.GeneratedProtocolMessageType('FeeratesPerkw', (_message.Message,), { + 'DESCRIPTOR' : _FEERATESPERKW, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FeeratesPerkw) + }) +_sym_db.RegisterMessage(FeeratesPerkw) + +FeeratesOnchain_fee_estimates = _reflection.GeneratedProtocolMessageType('FeeratesOnchain_fee_estimates', (_message.Message,), { + 'DESCRIPTOR' : _FEERATESONCHAIN_FEE_ESTIMATES, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FeeratesOnchain_fee_estimates) + }) +_sym_db.RegisterMessage(FeeratesOnchain_fee_estimates) + +GetrouteRequest = _reflection.GeneratedProtocolMessageType('GetrouteRequest', (_message.Message,), { + 'DESCRIPTOR' : _GETROUTEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetrouteRequest) + }) +_sym_db.RegisterMessage(GetrouteRequest) + +GetrouteResponse = _reflection.GeneratedProtocolMessageType('GetrouteResponse', (_message.Message,), { + 'DESCRIPTOR' : _GETROUTERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetrouteResponse) + }) +_sym_db.RegisterMessage(GetrouteResponse) + +GetrouteRoute = _reflection.GeneratedProtocolMessageType('GetrouteRoute', (_message.Message,), { + 'DESCRIPTOR' : _GETROUTEROUTE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.GetrouteRoute) + }) +_sym_db.RegisterMessage(GetrouteRoute) + +ListforwardsRequest = _reflection.GeneratedProtocolMessageType('ListforwardsRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTFORWARDSREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListforwardsRequest) + }) +_sym_db.RegisterMessage(ListforwardsRequest) + +ListforwardsResponse = _reflection.GeneratedProtocolMessageType('ListforwardsResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTFORWARDSRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListforwardsResponse) + }) +_sym_db.RegisterMessage(ListforwardsResponse) + +ListforwardsForwards = _reflection.GeneratedProtocolMessageType('ListforwardsForwards', (_message.Message,), { + 'DESCRIPTOR' : _LISTFORWARDSFORWARDS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListforwardsForwards) + }) +_sym_db.RegisterMessage(ListforwardsForwards) + +ListpaysRequest = _reflection.GeneratedProtocolMessageType('ListpaysRequest', (_message.Message,), { + 'DESCRIPTOR' : _LISTPAYSREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpaysRequest) + }) +_sym_db.RegisterMessage(ListpaysRequest) + +ListpaysResponse = _reflection.GeneratedProtocolMessageType('ListpaysResponse', (_message.Message,), { + 'DESCRIPTOR' : _LISTPAYSRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpaysResponse) + }) +_sym_db.RegisterMessage(ListpaysResponse) + +ListpaysPays = _reflection.GeneratedProtocolMessageType('ListpaysPays', (_message.Message,), { + 'DESCRIPTOR' : _LISTPAYSPAYS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpaysPays) + }) +_sym_db.RegisterMessage(ListpaysPays) + +PingRequest = _reflection.GeneratedProtocolMessageType('PingRequest', (_message.Message,), { + 'DESCRIPTOR' : _PINGREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.PingRequest) + }) +_sym_db.RegisterMessage(PingRequest) + +PingResponse = _reflection.GeneratedProtocolMessageType('PingResponse', (_message.Message,), { + 'DESCRIPTOR' : _PINGRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.PingResponse) + }) +_sym_db.RegisterMessage(PingResponse) + +SignmessageRequest = _reflection.GeneratedProtocolMessageType('SignmessageRequest', (_message.Message,), { + 'DESCRIPTOR' : _SIGNMESSAGEREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SignmessageRequest) + }) +_sym_db.RegisterMessage(SignmessageRequest) + +SignmessageResponse = _reflection.GeneratedProtocolMessageType('SignmessageResponse', (_message.Message,), { + 'DESCRIPTOR' : _SIGNMESSAGERESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SignmessageResponse) + }) +_sym_db.RegisterMessage(SignmessageResponse) + +_NODE = DESCRIPTOR.services_by_name['Node'] +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _GETINFOREQUEST._serialized_start=37 + _GETINFOREQUEST._serialized_end=53 + _GETINFORESPONSE._serialized_start=56 + _GETINFORESPONSE._serialized_end=548 + _GETINFOOUR_FEATURES._serialized_start=550 + _GETINFOOUR_FEATURES._serialized_end=633 + _GETINFOADDRESS._serialized_start=636 + _GETINFOADDRESS._serialized_end=847 + _GETINFOADDRESS_GETINFOADDRESSTYPE._serialized_start=749 + _GETINFOADDRESS_GETINFOADDRESSTYPE._serialized_end=835 + _GETINFOBINDING._serialized_start=850 + _GETINFOBINDING._serialized_end=1101 + _GETINFOBINDING_GETINFOBINDINGTYPE._serialized_start=989 + _GETINFOBINDING_GETINFOBINDINGTYPE._serialized_end=1069 + _LISTPEERSREQUEST._serialized_start=1103 + _LISTPEERSREQUEST._serialized_end=1175 + _LISTPEERSRESPONSE._serialized_start=1177 + _LISTPEERSRESPONSE._serialized_end=1232 + _LISTPEERSPEERS._serialized_start=1235 + _LISTPEERSPEERS._serialized_end=1461 + _LISTPEERSPEERSLOG._serialized_start=1464 + _LISTPEERSPEERSLOG._serialized_end=1845 + _LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE._serialized_start=1675 + _LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE._serialized_end=1780 + _LISTPEERSPEERSCHANNELS._serialized_start=1848 + _LISTPEERSPEERSCHANNELS._serialized_end=4674 + _LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE._serialized_start=3578 + _LISTPEERSPEERSCHANNELS_LISTPEERSPEERSCHANNELSSTATE._serialized_end=3867 + _LISTPEERSPEERSCHANNELSFEERATE._serialized_start=4676 + _LISTPEERSPEERSCHANNELSFEERATE._serialized_end=4737 + _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_start=4740 + _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_end=4937 + _LISTPEERSPEERSCHANNELSFUNDING._serialized_start=4940 + _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5072 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5075 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=5413 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5329 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=5384 + _LISTFUNDSREQUEST._serialized_start=5415 + _LISTFUNDSREQUEST._serialized_end=5463 + _LISTFUNDSRESPONSE._serialized_start=5465 + _LISTFUNDSRESPONSE._serialized_end=5566 + _LISTFUNDSOUTPUTS._serialized_start=5569 + _LISTFUNDSOUTPUTS._serialized_end=5942 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=5830 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=5897 + _LISTFUNDSCHANNELS._serialized_start=5945 + _LISTFUNDSCHANNELS._serialized_end=6204 + _SENDPAYREQUEST._serialized_start=6207 + _SENDPAYREQUEST._serialized_end=6554 + _SENDPAYRESPONSE._serialized_start=6557 + _SENDPAYRESPONSE._serialized_end=7106 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=6944 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=6986 + _SENDPAYROUTE._serialized_start=7108 + _SENDPAYROUTE._serialized_end=7200 + _LISTCHANNELSREQUEST._serialized_start=7203 + _LISTCHANNELSREQUEST._serialized_end=7350 + _LISTCHANNELSRESPONSE._serialized_start=7352 + _LISTCHANNELSRESPONSE._serialized_end=7419 + _LISTCHANNELSCHANNELS._serialized_start=7422 + _LISTCHANNELSCHANNELS._serialized_end=7838 + _ADDGOSSIPREQUEST._serialized_start=7840 + _ADDGOSSIPREQUEST._serialized_end=7875 + _ADDGOSSIPRESPONSE._serialized_start=7877 + _ADDGOSSIPRESPONSE._serialized_end=7896 + _AUTOCLEANINVOICEREQUEST._serialized_start=7898 + _AUTOCLEANINVOICEREQUEST._serialized_end=8009 + _AUTOCLEANINVOICERESPONSE._serialized_start=8012 + _AUTOCLEANINVOICERESPONSE._serialized_end=8141 + _CHECKMESSAGEREQUEST._serialized_start=8143 + _CHECKMESSAGEREQUEST._serialized_end=8228 + _CHECKMESSAGERESPONSE._serialized_start=8230 + _CHECKMESSAGERESPONSE._serialized_end=8302 + _CLOSEREQUEST._serialized_start=8305 + _CLOSEREQUEST._serialized_end=8621 + _CLOSERESPONSE._serialized_start=8624 + _CLOSERESPONSE._serialized_end=8795 + _CLOSERESPONSE_CLOSETYPE._serialized_start=8726 + _CLOSERESPONSE_CLOSETYPE._serialized_end=8779 + _CONNECTREQUEST._serialized_start=8797 + _CONNECTREQUEST._serialized_end=8881 + _CONNECTRESPONSE._serialized_start=8884 + _CONNECTRESPONSE._serialized_end=9026 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=8991 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9026 + _CONNECTADDRESS._serialized_start=9029 + _CONNECTADDRESS._serialized_end=9280 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9168 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9248 + _CREATEINVOICEREQUEST._serialized_start=9282 + _CREATEINVOICEREQUEST._serialized_end=9356 + _CREATEINVOICERESPONSE._serialized_start=9359 + _CREATEINVOICERESPONSE._serialized_end=9986 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=9786 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=9842 + _DATASTOREREQUEST._serialized_start=9989 + _DATASTOREREQUEST._serialized_end=10297 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10142 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10254 + _DATASTORERESPONSE._serialized_start=10300 + _DATASTORERESPONSE._serialized_end=10430 + _CREATEONIONREQUEST._serialized_start=10433 + _CREATEONIONREQUEST._serialized_end=10590 + _CREATEONIONRESPONSE._serialized_start=10592 + _CREATEONIONRESPONSE._serialized_end=10652 + _CREATEONIONHOPS._serialized_start=10654 + _CREATEONIONHOPS._serialized_end=10704 + _DELDATASTOREREQUEST._serialized_start=10706 + _DELDATASTOREREQUEST._serialized_end=10780 + _DELDATASTORERESPONSE._serialized_start=10783 + _DELDATASTORERESPONSE._serialized_end=10916 + _DELEXPIREDINVOICEREQUEST._serialized_start=10918 + _DELEXPIREDINVOICEREQUEST._serialized_end=10990 + _DELEXPIREDINVOICERESPONSE._serialized_start=10992 + _DELEXPIREDINVOICERESPONSE._serialized_end=11019 + _DELINVOICEREQUEST._serialized_start=11022 + _DELINVOICEREQUEST._serialized_end=11204 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11138 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11191 + _DELINVOICERESPONSE._serialized_start=11207 + _DELINVOICERESPONSE._serialized_end=11646 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11138 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11191 + _INVOICEREQUEST._serialized_start=11649 + _INVOICEREQUEST._serialized_end=11961 + _INVOICERESPONSE._serialized_start=11964 + _INVOICERESPONSE._serialized_end=12323 + _LISTDATASTOREREQUEST._serialized_start=12325 + _LISTDATASTOREREQUEST._serialized_end=12360 + _LISTDATASTORERESPONSE._serialized_start=12362 + _LISTDATASTORERESPONSE._serialized_end=12433 + _LISTDATASTOREDATASTORE._serialized_start=12436 + _LISTDATASTOREDATASTORE._serialized_end=12571 + _LISTINVOICESREQUEST._serialized_start=12574 + _LISTINVOICESREQUEST._serialized_end=12743 + _LISTINVOICESRESPONSE._serialized_start=12745 + _LISTINVOICESRESPONSE._serialized_end=12812 + _LISTINVOICESINVOICES._serialized_start=12815 + _LISTINVOICESINVOICES._serialized_end=13475 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13252 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13315 + _SENDONIONREQUEST._serialized_start=13478 + _SENDONIONREQUEST._serialized_end=13826 + _SENDONIONRESPONSE._serialized_start=13829 + _SENDONIONRESPONSE._serialized_end=14352 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14200 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14244 + _SENDONIONFIRST_HOP._serialized_start=14354 + _SENDONIONFIRST_HOP._serialized_end=14435 + _LISTSENDPAYSREQUEST._serialized_start=14438 + _LISTSENDPAYSREQUEST._serialized_end=14673 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14575 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14634 + _LISTSENDPAYSRESPONSE._serialized_start=14675 + _LISTSENDPAYSRESPONSE._serialized_end=14742 + _LISTSENDPAYSPAYMENTS._serialized_start=14745 + _LISTSENDPAYSPAYMENTS._serialized_end=15358 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15163 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15230 + _LISTTRANSACTIONSREQUEST._serialized_start=15360 + _LISTTRANSACTIONSREQUEST._serialized_end=15385 + _LISTTRANSACTIONSRESPONSE._serialized_start=15387 + _LISTTRANSACTIONSRESPONSE._serialized_end=15470 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15473 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=15755 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=15758 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16274 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=15970 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16248 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16277 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=16821 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16516 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=16795 + _PAYREQUEST._serialized_start=16824 + _PAYREQUEST._serialized_end=17296 + _PAYRESPONSE._serialized_start=17299 + _PAYRESPONSE._serialized_end=17678 + _PAYRESPONSE_PAYSTATUS._serialized_start=17581 + _PAYRESPONSE_PAYSTATUS._serialized_end=17631 + _LISTNODESREQUEST._serialized_start=17680 + _LISTNODESREQUEST._serialized_end=17722 + _LISTNODESRESPONSE._serialized_start=17724 + _LISTNODESRESPONSE._serialized_end=17779 + _LISTNODESNODES._serialized_start=17782 + _LISTNODESNODES._serialized_end=18007 + _LISTNODESNODESADDRESSES._serialized_start=18010 + _LISTNODESNODESADDRESSES._serialized_end=18257 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18150 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18245 + _WAITANYINVOICEREQUEST._serialized_start=18259 + _WAITANYINVOICEREQUEST._serialized_end=18362 + _WAITANYINVOICERESPONSE._serialized_start=18365 + _WAITANYINVOICERESPONSE._serialized_end=18896 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=18741 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=18786 + _WAITINVOICEREQUEST._serialized_start=18898 + _WAITINVOICEREQUEST._serialized_end=18933 + _WAITINVOICERESPONSE._serialized_start=18936 + _WAITINVOICERESPONSE._serialized_end=19455 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19303 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19345 + _WAITSENDPAYREQUEST._serialized_start=19458 + _WAITSENDPAYREQUEST._serialized_end=19600 + _WAITSENDPAYRESPONSE._serialized_start=19603 + _WAITSENDPAYRESPONSE._serialized_end=20121 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=19980 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20013 + _NEWADDRREQUEST._serialized_start=20124 + _NEWADDRREQUEST._serialized_end=20282 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20208 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20266 + _NEWADDRRESPONSE._serialized_start=20284 + _NEWADDRRESPONSE._serialized_end=20375 + _WITHDRAWREQUEST._serialized_start=20378 + _WITHDRAWREQUEST._serialized_end=20580 + _WITHDRAWRESPONSE._serialized_start=20582 + _WITHDRAWRESPONSE._serialized_end=20640 + _KEYSENDREQUEST._serialized_start=20643 + _KEYSENDREQUEST._serialized_end=20975 + _KEYSENDRESPONSE._serialized_start=20978 + _KEYSENDRESPONSE._serialized_end=21348 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21272 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21301 + _KEYSENDEXTRATLVS._serialized_start=21350 + _KEYSENDEXTRATLVS._serialized_end=21368 + _FUNDPSBTREQUEST._serialized_start=21371 + _FUNDPSBTREQUEST._serialized_end=21682 + _FUNDPSBTRESPONSE._serialized_start=21685 + _FUNDPSBTRESPONSE._serialized_end=21902 + _FUNDPSBTRESERVATIONS._serialized_start=21904 + _FUNDPSBTRESERVATIONS._serialized_end=22021 + _SENDPSBTREQUEST._serialized_start=22023 + _SENDPSBTREQUEST._serialized_end=22088 + _SENDPSBTRESPONSE._serialized_start=22090 + _SENDPSBTRESPONSE._serialized_end=22134 + _SIGNPSBTREQUEST._serialized_start=22136 + _SIGNPSBTREQUEST._serialized_end=22185 + _SIGNPSBTRESPONSE._serialized_start=22187 + _SIGNPSBTRESPONSE._serialized_end=22226 + _UTXOPSBTREQUEST._serialized_start=22229 + _UTXOPSBTREQUEST._serialized_end=22576 + _UTXOPSBTRESPONSE._serialized_start=22579 + _UTXOPSBTRESPONSE._serialized_end=22796 + _UTXOPSBTRESERVATIONS._serialized_start=22798 + _UTXOPSBTRESERVATIONS._serialized_end=22915 + _TXDISCARDREQUEST._serialized_start=22917 + _TXDISCARDREQUEST._serialized_end=22949 + _TXDISCARDRESPONSE._serialized_start=22951 + _TXDISCARDRESPONSE._serialized_end=23005 + _TXPREPAREREQUEST._serialized_start=23008 + _TXPREPAREREQUEST._serialized_end=23172 + _TXPREPARERESPONSE._serialized_start=23174 + _TXPREPARERESPONSE._serialized_end=23242 + _TXSENDREQUEST._serialized_start=23244 + _TXSENDREQUEST._serialized_end=23273 + _TXSENDRESPONSE._serialized_start=23275 + _TXSENDRESPONSE._serialized_end=23331 + _DISCONNECTREQUEST._serialized_start=23333 + _DISCONNECTREQUEST._serialized_end=23394 + _DISCONNECTRESPONSE._serialized_start=23396 + _DISCONNECTRESPONSE._serialized_end=23416 + _FEERATESREQUEST._serialized_start=23418 + _FEERATESREQUEST._serialized_end=23525 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23488 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23525 + _FEERATESRESPONSE._serialized_start=23527 + _FEERATESRESPONSE._serialized_end=23613 + _FEERATESPERKB._serialized_start=23616 + _FEERATESPERKB._serialized_end=23939 + _FEERATESPERKW._serialized_start=23942 + _FEERATESPERKW._serialized_end=24265 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24268 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24461 + _GETROUTEREQUEST._serialized_start=24464 + _GETROUTEREQUEST._serialized_end=24700 + _GETROUTERESPONSE._serialized_start=24702 + _GETROUTERESPONSE._serialized_end=24755 + _GETROUTEROUTE._serialized_start=24758 + _GETROUTEROUTE._serialized_end=24955 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=24926 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=24955 + _LISTFORWARDSREQUEST._serialized_start=24958 + _LISTFORWARDSREQUEST._serialized_end=25216 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25098 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25174 + _LISTFORWARDSRESPONSE._serialized_start=25218 + _LISTFORWARDSRESPONSE._serialized_end=25285 + _LISTFORWARDSFORWARDS._serialized_start=25288 + _LISTFORWARDSFORWARDS._serialized_end=25856 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=25653 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=25737 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=25739 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=25787 + _LISTPAYSREQUEST._serialized_start=25859 + _LISTPAYSREQUEST._serialized_end=26078 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=25984 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26039 + _LISTPAYSRESPONSE._serialized_start=26080 + _LISTPAYSRESPONSE._serialized_end=26131 + _LISTPAYSPAYS._serialized_start=26134 + _LISTPAYSPAYS._serialized_end=26643 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=26468 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=26527 + _PINGREQUEST._serialized_start=26645 + _PINGREQUEST._serialized_end=26734 + _PINGRESPONSE._serialized_start=26736 + _PINGRESPONSE._serialized_end=26766 + _SIGNMESSAGEREQUEST._serialized_start=26768 + _SIGNMESSAGEREQUEST._serialized_end=26805 + _SIGNMESSAGERESPONSE._serialized_start=26807 + _SIGNMESSAGERESPONSE._serialized_end=26877 + _NODE._serialized_start=26880 + _NODE._serialized_end=29693 +# @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py new file mode 100644 index 000000000000..b3c3646926d9 --- /dev/null +++ b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py @@ -0,0 +1,1485 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from . import node_pb2 as node__pb2 + + +class NodeStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Getinfo = channel.unary_unary( + '/cln.Node/Getinfo', + request_serializer=node__pb2.GetinfoRequest.SerializeToString, + response_deserializer=node__pb2.GetinfoResponse.FromString, + ) + self.ListPeers = channel.unary_unary( + '/cln.Node/ListPeers', + request_serializer=node__pb2.ListpeersRequest.SerializeToString, + response_deserializer=node__pb2.ListpeersResponse.FromString, + ) + self.ListFunds = channel.unary_unary( + '/cln.Node/ListFunds', + request_serializer=node__pb2.ListfundsRequest.SerializeToString, + response_deserializer=node__pb2.ListfundsResponse.FromString, + ) + self.SendPay = channel.unary_unary( + '/cln.Node/SendPay', + request_serializer=node__pb2.SendpayRequest.SerializeToString, + response_deserializer=node__pb2.SendpayResponse.FromString, + ) + self.ListChannels = channel.unary_unary( + '/cln.Node/ListChannels', + request_serializer=node__pb2.ListchannelsRequest.SerializeToString, + response_deserializer=node__pb2.ListchannelsResponse.FromString, + ) + self.AddGossip = channel.unary_unary( + '/cln.Node/AddGossip', + request_serializer=node__pb2.AddgossipRequest.SerializeToString, + response_deserializer=node__pb2.AddgossipResponse.FromString, + ) + self.AutoCleanInvoice = channel.unary_unary( + '/cln.Node/AutoCleanInvoice', + request_serializer=node__pb2.AutocleaninvoiceRequest.SerializeToString, + response_deserializer=node__pb2.AutocleaninvoiceResponse.FromString, + ) + self.CheckMessage = channel.unary_unary( + '/cln.Node/CheckMessage', + request_serializer=node__pb2.CheckmessageRequest.SerializeToString, + response_deserializer=node__pb2.CheckmessageResponse.FromString, + ) + self.Close = channel.unary_unary( + '/cln.Node/Close', + request_serializer=node__pb2.CloseRequest.SerializeToString, + response_deserializer=node__pb2.CloseResponse.FromString, + ) + self.ConnectPeer = channel.unary_unary( + '/cln.Node/ConnectPeer', + request_serializer=node__pb2.ConnectRequest.SerializeToString, + response_deserializer=node__pb2.ConnectResponse.FromString, + ) + self.CreateInvoice = channel.unary_unary( + '/cln.Node/CreateInvoice', + request_serializer=node__pb2.CreateinvoiceRequest.SerializeToString, + response_deserializer=node__pb2.CreateinvoiceResponse.FromString, + ) + self.Datastore = channel.unary_unary( + '/cln.Node/Datastore', + request_serializer=node__pb2.DatastoreRequest.SerializeToString, + response_deserializer=node__pb2.DatastoreResponse.FromString, + ) + self.CreateOnion = channel.unary_unary( + '/cln.Node/CreateOnion', + request_serializer=node__pb2.CreateonionRequest.SerializeToString, + response_deserializer=node__pb2.CreateonionResponse.FromString, + ) + self.DelDatastore = channel.unary_unary( + '/cln.Node/DelDatastore', + request_serializer=node__pb2.DeldatastoreRequest.SerializeToString, + response_deserializer=node__pb2.DeldatastoreResponse.FromString, + ) + self.DelExpiredInvoice = channel.unary_unary( + '/cln.Node/DelExpiredInvoice', + request_serializer=node__pb2.DelexpiredinvoiceRequest.SerializeToString, + response_deserializer=node__pb2.DelexpiredinvoiceResponse.FromString, + ) + self.DelInvoice = channel.unary_unary( + '/cln.Node/DelInvoice', + request_serializer=node__pb2.DelinvoiceRequest.SerializeToString, + response_deserializer=node__pb2.DelinvoiceResponse.FromString, + ) + self.Invoice = channel.unary_unary( + '/cln.Node/Invoice', + request_serializer=node__pb2.InvoiceRequest.SerializeToString, + response_deserializer=node__pb2.InvoiceResponse.FromString, + ) + self.ListDatastore = channel.unary_unary( + '/cln.Node/ListDatastore', + request_serializer=node__pb2.ListdatastoreRequest.SerializeToString, + response_deserializer=node__pb2.ListdatastoreResponse.FromString, + ) + self.ListInvoices = channel.unary_unary( + '/cln.Node/ListInvoices', + request_serializer=node__pb2.ListinvoicesRequest.SerializeToString, + response_deserializer=node__pb2.ListinvoicesResponse.FromString, + ) + self.SendOnion = channel.unary_unary( + '/cln.Node/SendOnion', + request_serializer=node__pb2.SendonionRequest.SerializeToString, + response_deserializer=node__pb2.SendonionResponse.FromString, + ) + self.ListSendPays = channel.unary_unary( + '/cln.Node/ListSendPays', + request_serializer=node__pb2.ListsendpaysRequest.SerializeToString, + response_deserializer=node__pb2.ListsendpaysResponse.FromString, + ) + self.ListTransactions = channel.unary_unary( + '/cln.Node/ListTransactions', + request_serializer=node__pb2.ListtransactionsRequest.SerializeToString, + response_deserializer=node__pb2.ListtransactionsResponse.FromString, + ) + self.Pay = channel.unary_unary( + '/cln.Node/Pay', + request_serializer=node__pb2.PayRequest.SerializeToString, + response_deserializer=node__pb2.PayResponse.FromString, + ) + self.ListNodes = channel.unary_unary( + '/cln.Node/ListNodes', + request_serializer=node__pb2.ListnodesRequest.SerializeToString, + response_deserializer=node__pb2.ListnodesResponse.FromString, + ) + self.WaitAnyInvoice = channel.unary_unary( + '/cln.Node/WaitAnyInvoice', + request_serializer=node__pb2.WaitanyinvoiceRequest.SerializeToString, + response_deserializer=node__pb2.WaitanyinvoiceResponse.FromString, + ) + self.WaitInvoice = channel.unary_unary( + '/cln.Node/WaitInvoice', + request_serializer=node__pb2.WaitinvoiceRequest.SerializeToString, + response_deserializer=node__pb2.WaitinvoiceResponse.FromString, + ) + self.WaitSendPay = channel.unary_unary( + '/cln.Node/WaitSendPay', + request_serializer=node__pb2.WaitsendpayRequest.SerializeToString, + response_deserializer=node__pb2.WaitsendpayResponse.FromString, + ) + self.NewAddr = channel.unary_unary( + '/cln.Node/NewAddr', + request_serializer=node__pb2.NewaddrRequest.SerializeToString, + response_deserializer=node__pb2.NewaddrResponse.FromString, + ) + self.Withdraw = channel.unary_unary( + '/cln.Node/Withdraw', + request_serializer=node__pb2.WithdrawRequest.SerializeToString, + response_deserializer=node__pb2.WithdrawResponse.FromString, + ) + self.KeySend = channel.unary_unary( + '/cln.Node/KeySend', + request_serializer=node__pb2.KeysendRequest.SerializeToString, + response_deserializer=node__pb2.KeysendResponse.FromString, + ) + self.FundPsbt = channel.unary_unary( + '/cln.Node/FundPsbt', + request_serializer=node__pb2.FundpsbtRequest.SerializeToString, + response_deserializer=node__pb2.FundpsbtResponse.FromString, + ) + self.SendPsbt = channel.unary_unary( + '/cln.Node/SendPsbt', + request_serializer=node__pb2.SendpsbtRequest.SerializeToString, + response_deserializer=node__pb2.SendpsbtResponse.FromString, + ) + self.SignPsbt = channel.unary_unary( + '/cln.Node/SignPsbt', + request_serializer=node__pb2.SignpsbtRequest.SerializeToString, + response_deserializer=node__pb2.SignpsbtResponse.FromString, + ) + self.UtxoPsbt = channel.unary_unary( + '/cln.Node/UtxoPsbt', + request_serializer=node__pb2.UtxopsbtRequest.SerializeToString, + response_deserializer=node__pb2.UtxopsbtResponse.FromString, + ) + self.TxDiscard = channel.unary_unary( + '/cln.Node/TxDiscard', + request_serializer=node__pb2.TxdiscardRequest.SerializeToString, + response_deserializer=node__pb2.TxdiscardResponse.FromString, + ) + self.TxPrepare = channel.unary_unary( + '/cln.Node/TxPrepare', + request_serializer=node__pb2.TxprepareRequest.SerializeToString, + response_deserializer=node__pb2.TxprepareResponse.FromString, + ) + self.TxSend = channel.unary_unary( + '/cln.Node/TxSend', + request_serializer=node__pb2.TxsendRequest.SerializeToString, + response_deserializer=node__pb2.TxsendResponse.FromString, + ) + self.Disconnect = channel.unary_unary( + '/cln.Node/Disconnect', + request_serializer=node__pb2.DisconnectRequest.SerializeToString, + response_deserializer=node__pb2.DisconnectResponse.FromString, + ) + self.Feerates = channel.unary_unary( + '/cln.Node/Feerates', + request_serializer=node__pb2.FeeratesRequest.SerializeToString, + response_deserializer=node__pb2.FeeratesResponse.FromString, + ) + self.GetRoute = channel.unary_unary( + '/cln.Node/GetRoute', + request_serializer=node__pb2.GetrouteRequest.SerializeToString, + response_deserializer=node__pb2.GetrouteResponse.FromString, + ) + self.ListForwards = channel.unary_unary( + '/cln.Node/ListForwards', + request_serializer=node__pb2.ListforwardsRequest.SerializeToString, + response_deserializer=node__pb2.ListforwardsResponse.FromString, + ) + self.ListPays = channel.unary_unary( + '/cln.Node/ListPays', + request_serializer=node__pb2.ListpaysRequest.SerializeToString, + response_deserializer=node__pb2.ListpaysResponse.FromString, + ) + self.Ping = channel.unary_unary( + '/cln.Node/Ping', + request_serializer=node__pb2.PingRequest.SerializeToString, + response_deserializer=node__pb2.PingResponse.FromString, + ) + self.SignMessage = channel.unary_unary( + '/cln.Node/SignMessage', + request_serializer=node__pb2.SignmessageRequest.SerializeToString, + response_deserializer=node__pb2.SignmessageResponse.FromString, + ) + + +class NodeServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Getinfo(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListPeers(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListFunds(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SendPay(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListChannels(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def AddGossip(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def AutoCleanInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CheckMessage(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Close(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ConnectPeer(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CreateInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Datastore(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CreateOnion(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DelDatastore(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DelExpiredInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def DelInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Invoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListDatastore(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListInvoices(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SendOnion(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListSendPays(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListTransactions(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Pay(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListNodes(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def WaitAnyInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def WaitInvoice(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def WaitSendPay(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def NewAddr(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Withdraw(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def KeySend(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def FundPsbt(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SendPsbt(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SignPsbt(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def UtxoPsbt(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def TxDiscard(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def TxPrepare(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def TxSend(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Disconnect(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Feerates(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetRoute(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListForwards(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ListPays(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Ping(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SignMessage(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_NodeServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Getinfo': grpc.unary_unary_rpc_method_handler( + servicer.Getinfo, + request_deserializer=node__pb2.GetinfoRequest.FromString, + response_serializer=node__pb2.GetinfoResponse.SerializeToString, + ), + 'ListPeers': grpc.unary_unary_rpc_method_handler( + servicer.ListPeers, + request_deserializer=node__pb2.ListpeersRequest.FromString, + response_serializer=node__pb2.ListpeersResponse.SerializeToString, + ), + 'ListFunds': grpc.unary_unary_rpc_method_handler( + servicer.ListFunds, + request_deserializer=node__pb2.ListfundsRequest.FromString, + response_serializer=node__pb2.ListfundsResponse.SerializeToString, + ), + 'SendPay': grpc.unary_unary_rpc_method_handler( + servicer.SendPay, + request_deserializer=node__pb2.SendpayRequest.FromString, + response_serializer=node__pb2.SendpayResponse.SerializeToString, + ), + 'ListChannels': grpc.unary_unary_rpc_method_handler( + servicer.ListChannels, + request_deserializer=node__pb2.ListchannelsRequest.FromString, + response_serializer=node__pb2.ListchannelsResponse.SerializeToString, + ), + 'AddGossip': grpc.unary_unary_rpc_method_handler( + servicer.AddGossip, + request_deserializer=node__pb2.AddgossipRequest.FromString, + response_serializer=node__pb2.AddgossipResponse.SerializeToString, + ), + 'AutoCleanInvoice': grpc.unary_unary_rpc_method_handler( + servicer.AutoCleanInvoice, + request_deserializer=node__pb2.AutocleaninvoiceRequest.FromString, + response_serializer=node__pb2.AutocleaninvoiceResponse.SerializeToString, + ), + 'CheckMessage': grpc.unary_unary_rpc_method_handler( + servicer.CheckMessage, + request_deserializer=node__pb2.CheckmessageRequest.FromString, + response_serializer=node__pb2.CheckmessageResponse.SerializeToString, + ), + 'Close': grpc.unary_unary_rpc_method_handler( + servicer.Close, + request_deserializer=node__pb2.CloseRequest.FromString, + response_serializer=node__pb2.CloseResponse.SerializeToString, + ), + 'ConnectPeer': grpc.unary_unary_rpc_method_handler( + servicer.ConnectPeer, + request_deserializer=node__pb2.ConnectRequest.FromString, + response_serializer=node__pb2.ConnectResponse.SerializeToString, + ), + 'CreateInvoice': grpc.unary_unary_rpc_method_handler( + servicer.CreateInvoice, + request_deserializer=node__pb2.CreateinvoiceRequest.FromString, + response_serializer=node__pb2.CreateinvoiceResponse.SerializeToString, + ), + 'Datastore': grpc.unary_unary_rpc_method_handler( + servicer.Datastore, + request_deserializer=node__pb2.DatastoreRequest.FromString, + response_serializer=node__pb2.DatastoreResponse.SerializeToString, + ), + 'CreateOnion': grpc.unary_unary_rpc_method_handler( + servicer.CreateOnion, + request_deserializer=node__pb2.CreateonionRequest.FromString, + response_serializer=node__pb2.CreateonionResponse.SerializeToString, + ), + 'DelDatastore': grpc.unary_unary_rpc_method_handler( + servicer.DelDatastore, + request_deserializer=node__pb2.DeldatastoreRequest.FromString, + response_serializer=node__pb2.DeldatastoreResponse.SerializeToString, + ), + 'DelExpiredInvoice': grpc.unary_unary_rpc_method_handler( + servicer.DelExpiredInvoice, + request_deserializer=node__pb2.DelexpiredinvoiceRequest.FromString, + response_serializer=node__pb2.DelexpiredinvoiceResponse.SerializeToString, + ), + 'DelInvoice': grpc.unary_unary_rpc_method_handler( + servicer.DelInvoice, + request_deserializer=node__pb2.DelinvoiceRequest.FromString, + response_serializer=node__pb2.DelinvoiceResponse.SerializeToString, + ), + 'Invoice': grpc.unary_unary_rpc_method_handler( + servicer.Invoice, + request_deserializer=node__pb2.InvoiceRequest.FromString, + response_serializer=node__pb2.InvoiceResponse.SerializeToString, + ), + 'ListDatastore': grpc.unary_unary_rpc_method_handler( + servicer.ListDatastore, + request_deserializer=node__pb2.ListdatastoreRequest.FromString, + response_serializer=node__pb2.ListdatastoreResponse.SerializeToString, + ), + 'ListInvoices': grpc.unary_unary_rpc_method_handler( + servicer.ListInvoices, + request_deserializer=node__pb2.ListinvoicesRequest.FromString, + response_serializer=node__pb2.ListinvoicesResponse.SerializeToString, + ), + 'SendOnion': grpc.unary_unary_rpc_method_handler( + servicer.SendOnion, + request_deserializer=node__pb2.SendonionRequest.FromString, + response_serializer=node__pb2.SendonionResponse.SerializeToString, + ), + 'ListSendPays': grpc.unary_unary_rpc_method_handler( + servicer.ListSendPays, + request_deserializer=node__pb2.ListsendpaysRequest.FromString, + response_serializer=node__pb2.ListsendpaysResponse.SerializeToString, + ), + 'ListTransactions': grpc.unary_unary_rpc_method_handler( + servicer.ListTransactions, + request_deserializer=node__pb2.ListtransactionsRequest.FromString, + response_serializer=node__pb2.ListtransactionsResponse.SerializeToString, + ), + 'Pay': grpc.unary_unary_rpc_method_handler( + servicer.Pay, + request_deserializer=node__pb2.PayRequest.FromString, + response_serializer=node__pb2.PayResponse.SerializeToString, + ), + 'ListNodes': grpc.unary_unary_rpc_method_handler( + servicer.ListNodes, + request_deserializer=node__pb2.ListnodesRequest.FromString, + response_serializer=node__pb2.ListnodesResponse.SerializeToString, + ), + 'WaitAnyInvoice': grpc.unary_unary_rpc_method_handler( + servicer.WaitAnyInvoice, + request_deserializer=node__pb2.WaitanyinvoiceRequest.FromString, + response_serializer=node__pb2.WaitanyinvoiceResponse.SerializeToString, + ), + 'WaitInvoice': grpc.unary_unary_rpc_method_handler( + servicer.WaitInvoice, + request_deserializer=node__pb2.WaitinvoiceRequest.FromString, + response_serializer=node__pb2.WaitinvoiceResponse.SerializeToString, + ), + 'WaitSendPay': grpc.unary_unary_rpc_method_handler( + servicer.WaitSendPay, + request_deserializer=node__pb2.WaitsendpayRequest.FromString, + response_serializer=node__pb2.WaitsendpayResponse.SerializeToString, + ), + 'NewAddr': grpc.unary_unary_rpc_method_handler( + servicer.NewAddr, + request_deserializer=node__pb2.NewaddrRequest.FromString, + response_serializer=node__pb2.NewaddrResponse.SerializeToString, + ), + 'Withdraw': grpc.unary_unary_rpc_method_handler( + servicer.Withdraw, + request_deserializer=node__pb2.WithdrawRequest.FromString, + response_serializer=node__pb2.WithdrawResponse.SerializeToString, + ), + 'KeySend': grpc.unary_unary_rpc_method_handler( + servicer.KeySend, + request_deserializer=node__pb2.KeysendRequest.FromString, + response_serializer=node__pb2.KeysendResponse.SerializeToString, + ), + 'FundPsbt': grpc.unary_unary_rpc_method_handler( + servicer.FundPsbt, + request_deserializer=node__pb2.FundpsbtRequest.FromString, + response_serializer=node__pb2.FundpsbtResponse.SerializeToString, + ), + 'SendPsbt': grpc.unary_unary_rpc_method_handler( + servicer.SendPsbt, + request_deserializer=node__pb2.SendpsbtRequest.FromString, + response_serializer=node__pb2.SendpsbtResponse.SerializeToString, + ), + 'SignPsbt': grpc.unary_unary_rpc_method_handler( + servicer.SignPsbt, + request_deserializer=node__pb2.SignpsbtRequest.FromString, + response_serializer=node__pb2.SignpsbtResponse.SerializeToString, + ), + 'UtxoPsbt': grpc.unary_unary_rpc_method_handler( + servicer.UtxoPsbt, + request_deserializer=node__pb2.UtxopsbtRequest.FromString, + response_serializer=node__pb2.UtxopsbtResponse.SerializeToString, + ), + 'TxDiscard': grpc.unary_unary_rpc_method_handler( + servicer.TxDiscard, + request_deserializer=node__pb2.TxdiscardRequest.FromString, + response_serializer=node__pb2.TxdiscardResponse.SerializeToString, + ), + 'TxPrepare': grpc.unary_unary_rpc_method_handler( + servicer.TxPrepare, + request_deserializer=node__pb2.TxprepareRequest.FromString, + response_serializer=node__pb2.TxprepareResponse.SerializeToString, + ), + 'TxSend': grpc.unary_unary_rpc_method_handler( + servicer.TxSend, + request_deserializer=node__pb2.TxsendRequest.FromString, + response_serializer=node__pb2.TxsendResponse.SerializeToString, + ), + 'Disconnect': grpc.unary_unary_rpc_method_handler( + servicer.Disconnect, + request_deserializer=node__pb2.DisconnectRequest.FromString, + response_serializer=node__pb2.DisconnectResponse.SerializeToString, + ), + 'Feerates': grpc.unary_unary_rpc_method_handler( + servicer.Feerates, + request_deserializer=node__pb2.FeeratesRequest.FromString, + response_serializer=node__pb2.FeeratesResponse.SerializeToString, + ), + 'GetRoute': grpc.unary_unary_rpc_method_handler( + servicer.GetRoute, + request_deserializer=node__pb2.GetrouteRequest.FromString, + response_serializer=node__pb2.GetrouteResponse.SerializeToString, + ), + 'ListForwards': grpc.unary_unary_rpc_method_handler( + servicer.ListForwards, + request_deserializer=node__pb2.ListforwardsRequest.FromString, + response_serializer=node__pb2.ListforwardsResponse.SerializeToString, + ), + 'ListPays': grpc.unary_unary_rpc_method_handler( + servicer.ListPays, + request_deserializer=node__pb2.ListpaysRequest.FromString, + response_serializer=node__pb2.ListpaysResponse.SerializeToString, + ), + 'Ping': grpc.unary_unary_rpc_method_handler( + servicer.Ping, + request_deserializer=node__pb2.PingRequest.FromString, + response_serializer=node__pb2.PingResponse.SerializeToString, + ), + 'SignMessage': grpc.unary_unary_rpc_method_handler( + servicer.SignMessage, + request_deserializer=node__pb2.SignmessageRequest.FromString, + response_serializer=node__pb2.SignmessageResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'cln.Node', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class Node(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Getinfo(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Getinfo', + node__pb2.GetinfoRequest.SerializeToString, + node__pb2.GetinfoResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListPeers(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListPeers', + node__pb2.ListpeersRequest.SerializeToString, + node__pb2.ListpeersResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListFunds(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListFunds', + node__pb2.ListfundsRequest.SerializeToString, + node__pb2.ListfundsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SendPay(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/SendPay', + node__pb2.SendpayRequest.SerializeToString, + node__pb2.SendpayResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListChannels(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListChannels', + node__pb2.ListchannelsRequest.SerializeToString, + node__pb2.ListchannelsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def AddGossip(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/AddGossip', + node__pb2.AddgossipRequest.SerializeToString, + node__pb2.AddgossipResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def AutoCleanInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/AutoCleanInvoice', + node__pb2.AutocleaninvoiceRequest.SerializeToString, + node__pb2.AutocleaninvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def CheckMessage(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/CheckMessage', + node__pb2.CheckmessageRequest.SerializeToString, + node__pb2.CheckmessageResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Close(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Close', + node__pb2.CloseRequest.SerializeToString, + node__pb2.CloseResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ConnectPeer(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ConnectPeer', + node__pb2.ConnectRequest.SerializeToString, + node__pb2.ConnectResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def CreateInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/CreateInvoice', + node__pb2.CreateinvoiceRequest.SerializeToString, + node__pb2.CreateinvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Datastore(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Datastore', + node__pb2.DatastoreRequest.SerializeToString, + node__pb2.DatastoreResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def CreateOnion(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/CreateOnion', + node__pb2.CreateonionRequest.SerializeToString, + node__pb2.CreateonionResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def DelDatastore(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/DelDatastore', + node__pb2.DeldatastoreRequest.SerializeToString, + node__pb2.DeldatastoreResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def DelExpiredInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/DelExpiredInvoice', + node__pb2.DelexpiredinvoiceRequest.SerializeToString, + node__pb2.DelexpiredinvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def DelInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/DelInvoice', + node__pb2.DelinvoiceRequest.SerializeToString, + node__pb2.DelinvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Invoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Invoice', + node__pb2.InvoiceRequest.SerializeToString, + node__pb2.InvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListDatastore(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListDatastore', + node__pb2.ListdatastoreRequest.SerializeToString, + node__pb2.ListdatastoreResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListInvoices(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListInvoices', + node__pb2.ListinvoicesRequest.SerializeToString, + node__pb2.ListinvoicesResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SendOnion(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/SendOnion', + node__pb2.SendonionRequest.SerializeToString, + node__pb2.SendonionResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListSendPays(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListSendPays', + node__pb2.ListsendpaysRequest.SerializeToString, + node__pb2.ListsendpaysResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListTransactions(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListTransactions', + node__pb2.ListtransactionsRequest.SerializeToString, + node__pb2.ListtransactionsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Pay(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Pay', + node__pb2.PayRequest.SerializeToString, + node__pb2.PayResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListNodes(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListNodes', + node__pb2.ListnodesRequest.SerializeToString, + node__pb2.ListnodesResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def WaitAnyInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/WaitAnyInvoice', + node__pb2.WaitanyinvoiceRequest.SerializeToString, + node__pb2.WaitanyinvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def WaitInvoice(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/WaitInvoice', + node__pb2.WaitinvoiceRequest.SerializeToString, + node__pb2.WaitinvoiceResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def WaitSendPay(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/WaitSendPay', + node__pb2.WaitsendpayRequest.SerializeToString, + node__pb2.WaitsendpayResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def NewAddr(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/NewAddr', + node__pb2.NewaddrRequest.SerializeToString, + node__pb2.NewaddrResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Withdraw(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Withdraw', + node__pb2.WithdrawRequest.SerializeToString, + node__pb2.WithdrawResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def KeySend(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/KeySend', + node__pb2.KeysendRequest.SerializeToString, + node__pb2.KeysendResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def FundPsbt(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/FundPsbt', + node__pb2.FundpsbtRequest.SerializeToString, + node__pb2.FundpsbtResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SendPsbt(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/SendPsbt', + node__pb2.SendpsbtRequest.SerializeToString, + node__pb2.SendpsbtResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SignPsbt(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/SignPsbt', + node__pb2.SignpsbtRequest.SerializeToString, + node__pb2.SignpsbtResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def UtxoPsbt(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/UtxoPsbt', + node__pb2.UtxopsbtRequest.SerializeToString, + node__pb2.UtxopsbtResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def TxDiscard(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/TxDiscard', + node__pb2.TxdiscardRequest.SerializeToString, + node__pb2.TxdiscardResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def TxPrepare(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/TxPrepare', + node__pb2.TxprepareRequest.SerializeToString, + node__pb2.TxprepareResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def TxSend(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/TxSend', + node__pb2.TxsendRequest.SerializeToString, + node__pb2.TxsendResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Disconnect(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Disconnect', + node__pb2.DisconnectRequest.SerializeToString, + node__pb2.DisconnectResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Feerates(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Feerates', + node__pb2.FeeratesRequest.SerializeToString, + node__pb2.FeeratesResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetRoute(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/GetRoute', + node__pb2.GetrouteRequest.SerializeToString, + node__pb2.GetrouteResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListForwards(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListForwards', + node__pb2.ListforwardsRequest.SerializeToString, + node__pb2.ListforwardsResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListPays(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/ListPays', + node__pb2.ListpaysRequest.SerializeToString, + node__pb2.ListpaysResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Ping(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Ping', + node__pb2.PingRequest.SerializeToString, + node__pb2.PingResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SignMessage(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/SignMessage', + node__pb2.SignmessageRequest.SerializeToString, + node__pb2.SignmessageResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/contrib/pyln-testing/pyln/testing/primitives_pb2.py b/contrib/pyln-testing/pyln/testing/primitives_pb2.py new file mode 100644 index 000000000000..bc180d942093 --- /dev/null +++ b/contrib/pyln-testing/pyln/testing/primitives_pb2.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: primitives.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10primitives.proto\x12\x03\x63ln\"\x16\n\x06\x41mount\x12\x0c\n\x04msat\x18\x01 \x01(\x04\"D\n\x0b\x41mountOrAll\x12\x1d\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x12\r\n\x03\x61ll\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\"D\n\x0b\x41mountOrAny\x12\x1d\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x12\r\n\x03\x61ny\x18\x02 \x01(\x08H\x00\x42\x07\n\x05value\"\x19\n\x17\x43hannelStateChangeCause\"(\n\x08Outpoint\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06outnum\x18\x02 \x01(\r\"h\n\x07\x46\x65\x65rate\x12\x0e\n\x04slow\x18\x01 \x01(\x08H\x00\x12\x10\n\x06normal\x18\x02 \x01(\x08H\x00\x12\x10\n\x06urgent\x18\x03 \x01(\x08H\x00\x12\x0f\n\x05perkb\x18\x04 \x01(\rH\x00\x12\x0f\n\x05perkw\x18\x05 \x01(\rH\x00\x42\x07\n\x05style\":\n\nOutputDesc\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x1b\n\x06\x61mount\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\"t\n\x08RouteHop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x02 \x01(\t\x12\x1c\n\x07\x66\x65\x65\x62\x61se\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0f\n\x07\x66\x65\x65prop\x18\x04 \x01(\r\x12\x13\n\x0b\x65xpirydelta\x18\x05 \x01(\r\"(\n\tRoutehint\x12\x1b\n\x04hops\x18\x01 \x03(\x0b\x32\r.cln.RouteHop\".\n\rRoutehintList\x12\x1d\n\x05hints\x18\x02 \x03(\x0b\x32\x0e.cln.Routehint*\x1e\n\x0b\x43hannelSide\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01*\x84\x02\n\x0c\x43hannelState\x12\x0c\n\x08Openingd\x10\x00\x12\x1a\n\x16\x43hanneldAwaitingLockin\x10\x01\x12\x12\n\x0e\x43hanneldNormal\x10\x02\x12\x18\n\x14\x43hanneldShuttingDown\x10\x03\x12\x17\n\x13\x43losingdSigexchange\x10\x04\x12\x14\n\x10\x43losingdComplete\x10\x05\x12\x16\n\x12\x41waitingUnilateral\x10\x06\x12\x14\n\x10\x46undingSpendSeen\x10\x07\x12\x0b\n\x07Onchain\x10\x08\x12\x15\n\x11\x44ualopendOpenInit\x10\t\x12\x1b\n\x17\x44ualopendAwaitingLockin\x10\nb\x06proto3') + +_CHANNELSIDE = DESCRIPTOR.enum_types_by_name['ChannelSide'] +ChannelSide = enum_type_wrapper.EnumTypeWrapper(_CHANNELSIDE) +_CHANNELSTATE = DESCRIPTOR.enum_types_by_name['ChannelState'] +ChannelState = enum_type_wrapper.EnumTypeWrapper(_CHANNELSTATE) +IN = 0 +OUT = 1 +Openingd = 0 +ChanneldAwaitingLockin = 1 +ChanneldNormal = 2 +ChanneldShuttingDown = 3 +ClosingdSigexchange = 4 +ClosingdComplete = 5 +AwaitingUnilateral = 6 +FundingSpendSeen = 7 +Onchain = 8 +DualopendOpenInit = 9 +DualopendAwaitingLockin = 10 + + +_AMOUNT = DESCRIPTOR.message_types_by_name['Amount'] +_AMOUNTORALL = DESCRIPTOR.message_types_by_name['AmountOrAll'] +_AMOUNTORANY = DESCRIPTOR.message_types_by_name['AmountOrAny'] +_CHANNELSTATECHANGECAUSE = DESCRIPTOR.message_types_by_name['ChannelStateChangeCause'] +_OUTPOINT = DESCRIPTOR.message_types_by_name['Outpoint'] +_FEERATE = DESCRIPTOR.message_types_by_name['Feerate'] +_OUTPUTDESC = DESCRIPTOR.message_types_by_name['OutputDesc'] +_ROUTEHOP = DESCRIPTOR.message_types_by_name['RouteHop'] +_ROUTEHINT = DESCRIPTOR.message_types_by_name['Routehint'] +_ROUTEHINTLIST = DESCRIPTOR.message_types_by_name['RoutehintList'] +Amount = _reflection.GeneratedProtocolMessageType('Amount', (_message.Message,), { + 'DESCRIPTOR' : _AMOUNT, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.Amount) + }) +_sym_db.RegisterMessage(Amount) + +AmountOrAll = _reflection.GeneratedProtocolMessageType('AmountOrAll', (_message.Message,), { + 'DESCRIPTOR' : _AMOUNTORALL, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.AmountOrAll) + }) +_sym_db.RegisterMessage(AmountOrAll) + +AmountOrAny = _reflection.GeneratedProtocolMessageType('AmountOrAny', (_message.Message,), { + 'DESCRIPTOR' : _AMOUNTORANY, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.AmountOrAny) + }) +_sym_db.RegisterMessage(AmountOrAny) + +ChannelStateChangeCause = _reflection.GeneratedProtocolMessageType('ChannelStateChangeCause', (_message.Message,), { + 'DESCRIPTOR' : _CHANNELSTATECHANGECAUSE, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.ChannelStateChangeCause) + }) +_sym_db.RegisterMessage(ChannelStateChangeCause) + +Outpoint = _reflection.GeneratedProtocolMessageType('Outpoint', (_message.Message,), { + 'DESCRIPTOR' : _OUTPOINT, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.Outpoint) + }) +_sym_db.RegisterMessage(Outpoint) + +Feerate = _reflection.GeneratedProtocolMessageType('Feerate', (_message.Message,), { + 'DESCRIPTOR' : _FEERATE, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.Feerate) + }) +_sym_db.RegisterMessage(Feerate) + +OutputDesc = _reflection.GeneratedProtocolMessageType('OutputDesc', (_message.Message,), { + 'DESCRIPTOR' : _OUTPUTDESC, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.OutputDesc) + }) +_sym_db.RegisterMessage(OutputDesc) + +RouteHop = _reflection.GeneratedProtocolMessageType('RouteHop', (_message.Message,), { + 'DESCRIPTOR' : _ROUTEHOP, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.RouteHop) + }) +_sym_db.RegisterMessage(RouteHop) + +Routehint = _reflection.GeneratedProtocolMessageType('Routehint', (_message.Message,), { + 'DESCRIPTOR' : _ROUTEHINT, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.Routehint) + }) +_sym_db.RegisterMessage(Routehint) + +RoutehintList = _reflection.GeneratedProtocolMessageType('RoutehintList', (_message.Message,), { + 'DESCRIPTOR' : _ROUTEHINTLIST, + '__module__' : 'primitives_pb2' + # @@protoc_insertion_point(class_scope:cln.RoutehintList) + }) +_sym_db.RegisterMessage(RoutehintList) + +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _CHANNELSIDE._serialized_start=632 + _CHANNELSIDE._serialized_end=662 + _CHANNELSTATE._serialized_start=665 + _CHANNELSTATE._serialized_end=925 + _AMOUNT._serialized_start=25 + _AMOUNT._serialized_end=47 + _AMOUNTORALL._serialized_start=49 + _AMOUNTORALL._serialized_end=117 + _AMOUNTORANY._serialized_start=119 + _AMOUNTORANY._serialized_end=187 + _CHANNELSTATECHANGECAUSE._serialized_start=189 + _CHANNELSTATECHANGECAUSE._serialized_end=214 + _OUTPOINT._serialized_start=216 + _OUTPOINT._serialized_end=256 + _FEERATE._serialized_start=258 + _FEERATE._serialized_end=362 + _OUTPUTDESC._serialized_start=364 + _OUTPUTDESC._serialized_end=422 + _ROUTEHOP._serialized_start=424 + _ROUTEHOP._serialized_end=540 + _ROUTEHINT._serialized_start=542 + _ROUTEHINT._serialized_end=582 + _ROUTEHINTLIST._serialized_start=584 + _ROUTEHINTLIST._serialized_end=630 +# @@protoc_insertion_point(module_scope) diff --git a/tests/test_cln_rs.py b/tests/test_cln_rs.py index 7524ad3864c0..6d408d9cb846 100644 --- a/tests/test_cln_rs.py +++ b/tests/test_cln_rs.py @@ -1,7 +1,10 @@ +from ephemeral_port_reserve import reserve from fixtures import * # noqa: F401,F403 from pathlib import Path +from pyln.testing import node_pb2 as nodepb +from pyln.testing import node_pb2_grpc as nodegrpc +from pyln.testing import primitives_pb2 as primitivespb from pyln.testing.utils import env, TEST_NETWORK, wait_for -from ephemeral_port_reserve import reserve import grpc import pytest import subprocess @@ -72,9 +75,6 @@ def test_plugin_start(node_factory): def test_grpc_connect(node_factory): """Attempts to connect to the grpc interface and call getinfo""" # These only exist if we have rust! - from node_pb2_grpc import NodeStub # noqa: E402 - import node_pb2 as nodepb # noqa: E402 - from primitives_pb2 import AmountOrAny, Amount # noqa: E402 grpc_port = reserve() l1 = node_factory.get_node(options={"grpc-port": str(grpc_port)}) @@ -95,7 +95,7 @@ def test_grpc_connect(node_factory): creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) - stub = NodeStub(channel) + stub = nodegrpc.NodeStub(channel) response = stub.Getinfo(nodepb.GetinfoRequest()) print(response) @@ -104,7 +104,7 @@ def test_grpc_connect(node_factory): print(response) inv = stub.Invoice(nodepb.InvoiceRequest( - amount_msat=AmountOrAny(any=True), + amount_msat=primitivespb.AmountOrAny(any=True), description="hello", label="lbl1", preimage=b"\x00" * 32, @@ -119,7 +119,7 @@ def test_grpc_connect(node_factory): with pytest.raises(Exception, match=r'Duplicate label'): # This request creates a label collision stub.Invoice(nodepb.InvoiceRequest( - amount_msat=AmountOrAny(amount=Amount(msat=12345)), + amount_msat=primitivespb.AmountOrAny(amount=primitivespb.Amount(msat=12345)), description="hello", label="lbl1", )) @@ -181,8 +181,6 @@ def test_grpc_wrong_auth(node_factory): and then we try to cross the wires. """ # These only exist if we have rust! - from node_pb2_grpc import NodeStub # noqa: E402 - import node_pb2 as nodepb # noqa: E402 grpc_port = reserve() l1, l2 = node_factory.get_nodes(2, opts={ @@ -210,7 +208,7 @@ def connect(node): creds, options=(('grpc.ssl_target_name_override', 'cln'),) ) - return NodeStub(channel) + return nodegrpc.NodeStub(channel) stub = connect(l1) # This should work, it's the correct node From 5307586d4d7a064b0fa6a45dc6f91752da348c44 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 30 Jun 2022 11:22:49 +0200 Subject: [PATCH 1067/1530] msggen: Add a new generator for grpc -> python converter To test the grpc interface we'll want to emulate the JSON-RPC interface as best we can, hence when talking to the grpc interface we want to convert back into a parsed JSON format as LightningRpc would have returned it. This is just the simplest way of closing the loop here: ``` pyln-testing --grpc-> cln-grpc --grpc2json ^ | | v | JSON-RPC | | TEST v ^ CLN | | | v pyln-testing <-grpc2py-- cln-grpc <- json2grpc ``` --- contrib/msggen/msggen/__main__.py | 8 + contrib/msggen/msggen/gen/grpc2py.py | 154 ++++ contrib/pyln-testing/pyln/testing/grpc2py.py | 842 +++++++++++++++++++ 3 files changed, 1004 insertions(+) create mode 100644 contrib/msggen/msggen/gen/grpc2py.py create mode 100644 contrib/pyln-testing/pyln/testing/grpc2py.py diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index 1bb345dac999..e6257efcf052 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,5 +1,6 @@ import json from msggen.gen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator +from msggen.gen.grpc2py import Grpc2PyGenerator from msggen.gen.rust import RustGenerator from msggen.gen.generator import GeneratorChain from msggen.utils import repo_root, load_jsonrpc_service @@ -22,6 +23,12 @@ def add_handler_gen_grpc(generator_chain: GeneratorChain, meta): generator_chain.add_generator(GrpcServerGenerator(dest)) +def add_handler_get_grpc2py(generator_chain: GeneratorChain): + fname = repo_root() / "contrib" / "pyln-testing" / "pyln" / "testing" / "grpc2py.py" + dest = open(fname, "w") + generator_chain.add_generator(Grpc2PyGenerator(dest)) + + def add_handler_gen_rust_jsonrpc(generator_chain: GeneratorChain): fname = repo_root() / "cln-rpc" / "src" / "model.rs" dest = open(fname, "w") @@ -45,6 +52,7 @@ def run(): add_handler_gen_grpc(generator_chain, meta) add_handler_gen_rust_jsonrpc(generator_chain) + add_handler_get_grpc2py(generator_chain) generator_chain.generate(service) diff --git a/contrib/msggen/msggen/gen/grpc2py.py b/contrib/msggen/msggen/gen/grpc2py.py new file mode 100644 index 000000000000..e0fe5959ee84 --- /dev/null +++ b/contrib/msggen/msggen/gen/grpc2py.py @@ -0,0 +1,154 @@ +"""Converts the GRPC messages back to parsed JSON dicts in python. + +This can be used to expose a local JSON-RPC socket but then talk to +the node over the GRPC interface. + +""" +from msggen.model import ArrayField, CompositeField, EnumField, PrimitiveField, Service +from msggen.gen import IGenerator +import logging +from textwrap import dedent +from typing import TextIO +import re + + +def decamelcase(c): + return re.sub(r'(? None: + if cleanup: + self.dest.write(dedent(text)) + else: + self.dest.write(text) + + def generate(self, service: Service) -> None: + self.write("""\ + # This file was automatically derived from the JSON-RPC schemas in + # `doc/schemas`. Do not edit this file manually as it would get + # overwritten. + + import json + + + def hexlify(b): + return b if b is None else b.hex() + + def amount2msat(a): + return a.msat + + def amount_or_all2msat(a): + breakpoint() + + + def remove_default(d): + # grpc is really not good at empty values, they get replaced with the type's default value... + return {k: v for k, v in d.items() if v is not None and v != ""} + """) + + self.generate_responses(service) + + def generate_enum(self, prefix, field: EnumField): + name = field.name.normalized() + prefix = f"{prefix}_{str(name).lower()}" + if field.path.endswith("[]"): + self.converters[field.path] = "str(i)" + else: + self.converters[field.path] = "str(m.{{name}})" + + def generate_composite(self, prefix, field: CompositeField): + name = field.name.normalized() + if prefix: + prefix = f"{prefix}_{str(name).lower()}" + else: + prefix = f"{str(name).lower()}" + + for f in field.fields: + if isinstance(f, CompositeField): + self.generate_composite(prefix, f) + + elif isinstance(f, ArrayField) and isinstance(f.itemtype, CompositeField): + self.generate_composite(prefix, f.itemtype) + + elif isinstance(f, ArrayField) and isinstance(f.itemtype, EnumField): + self.generate_enum(prefix, f.itemtype) + + converter_name = f"{prefix}2py" + self.write(f""" + + def {converter_name}(m): + return remove_default({{ + """) + + for f in field.fields: + name = f.normalized() + if isinstance(f, PrimitiveField): + typ = f.typename + + rhs = self.converters[typ].format(name=f.name) + + self.write(f' "{name}": {rhs}, # PrimitiveField in generate_composite\n', cleanup=False) + + elif isinstance(f, ArrayField) and isinstance(f.itemtype, PrimitiveField): + rhs = self.converters[f.itemtype.typename].format(name=name) + self.write(f' "{name}": [{rhs} for i in {rhs}], # ArrayField[primitive] in generate_composite\n', cleanup=False) + + elif isinstance(f, ArrayField): + rhs = self.converters[f.path] + + self.write(f' "{name}": [{rhs} for i in m.{name}], # ArrayField[composite] in generate_composite\n', cleanup=False) + + elif isinstance(f, CompositeField): + rhs = self.converters[f.path].format(name=f.name) + # self.write(f' "{name}": {rhs}, # CompositeField in generate_composite\n', cleanup=False) + + elif isinstance(f, EnumField): + name = f.name + self.write(f' "{name}": str(m.{f.name.normalized()}), # EnumField in generate_composite\n', cleanup=False) + + self.write(f" }})\n", cleanup=False) + + # Add ourselves to the converters so if we were generated as a + # dependency for a composite they can find us again. We have + # two variants: an array one where the items are going to be + # called "i" so we don't clobber and one-of where the field is + # "m.{name}" which will be filled by the caller. + if field.path.endswith("[]"): + self.converters[field.path] = f"{converter_name}(i)" + else: + self.converters[field.path] = f"{converter_name}(m.{{name}})" diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py new file mode 100644 index 000000000000..a0bb67b47f7e --- /dev/null +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -0,0 +1,842 @@ +# This file was automatically derived from the JSON-RPC schemas in +# `doc/schemas`. Do not edit this file manually as it would get +# overwritten. + +import json + + +def hexlify(b): + return b if b is None else b.hex() + +def amount2msat(a): + return a.msat + +def amount_or_all2msat(a): + breakpoint() + + +def remove_default(d): + # grpc is really not good at empty values, they get replaced with the type's default value... + return {k: v for k, v in d.items() if v is not None and v != ""} + + +def getinfo_our_features2py(m): + return remove_default({ + "init": hexlify(m.init), # PrimitiveField in generate_composite + "node": hexlify(m.node), # PrimitiveField in generate_composite + "channel": hexlify(m.channel), # PrimitiveField in generate_composite + "invoice": hexlify(m.invoice), # PrimitiveField in generate_composite + }) + + +def getinfo_address2py(m): + return remove_default({ + "type": str(m.item_type), # EnumField in generate_composite + "port": m.port, # PrimitiveField in generate_composite + "address": m.address, # PrimitiveField in generate_composite + }) + + +def getinfo_binding2py(m): + return remove_default({ + "type": str(m.item_type), # EnumField in generate_composite + "address": m.address, # PrimitiveField in generate_composite + "port": m.port, # PrimitiveField in generate_composite + "socket": m.socket, # PrimitiveField in generate_composite + }) + + +def getinfo2py(m): + return remove_default({ + "id": hexlify(m.id), # PrimitiveField in generate_composite + "alias": m.alias, # PrimitiveField in generate_composite + "color": hexlify(m.color), # PrimitiveField in generate_composite + "num_peers": m.num_peers, # PrimitiveField in generate_composite + "num_pending_channels": m.num_pending_channels, # PrimitiveField in generate_composite + "num_active_channels": m.num_active_channels, # PrimitiveField in generate_composite + "num_inactive_channels": m.num_inactive_channels, # PrimitiveField in generate_composite + "version": m.version, # PrimitiveField in generate_composite + "lightning_dir": m.lightning_dir, # PrimitiveField in generate_composite + "blockheight": m.blockheight, # PrimitiveField in generate_composite + "network": m.network, # PrimitiveField in generate_composite + "fees_collected_msat": amount2msat(m.fees_collected_msat), # PrimitiveField in generate_composite + "address": [getinfo_address2py(i) for i in m.address], # ArrayField[composite] in generate_composite + "binding": [getinfo_binding2py(i) for i in m.binding], # ArrayField[composite] in generate_composite + "warning_bitcoind_sync": m.warning_bitcoind_sync, # PrimitiveField in generate_composite + "warning_lightningd_sync": m.warning_lightningd_sync, # PrimitiveField in generate_composite + }) + + +def listpeers_peers_log2py(m): + return remove_default({ + "type": str(m.item_type), # EnumField in generate_composite + "num_skipped": m.num_skipped, # PrimitiveField in generate_composite + "time": m.time, # PrimitiveField in generate_composite + "source": m.source, # PrimitiveField in generate_composite + "log": m.log, # PrimitiveField in generate_composite + "node_id": hexlify(m.node_id), # PrimitiveField in generate_composite + "data": hexlify(m.data), # PrimitiveField in generate_composite + }) + + +def listpeers_peers_channels_feerate2py(m): + return remove_default({ + "perkw": m.perkw, # PrimitiveField in generate_composite + "perkb": m.perkb, # PrimitiveField in generate_composite + }) + + +def listpeers_peers_channels_inflight2py(m): + return remove_default({ + "funding_txid": hexlify(m.funding_txid), # PrimitiveField in generate_composite + "funding_outnum": m.funding_outnum, # PrimitiveField in generate_composite + "feerate": m.feerate, # PrimitiveField in generate_composite + "total_funding_msat": amount2msat(m.total_funding_msat), # PrimitiveField in generate_composite + "our_funding_msat": amount2msat(m.our_funding_msat), # PrimitiveField in generate_composite + "scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite + }) + + +def listpeers_peers_channels_funding2py(m): + return remove_default({ + "local_msat": amount2msat(m.local_msat), # PrimitiveField in generate_composite + "remote_msat": amount2msat(m.remote_msat), # PrimitiveField in generate_composite + "pushed_msat": amount2msat(m.pushed_msat), # PrimitiveField in generate_composite + }) + + +def listpeers_peers_channels_state_changes2py(m): + return remove_default({ + "timestamp": m.timestamp, # PrimitiveField in generate_composite + "old_state": str(m.old_state), # EnumField in generate_composite + "new_state": str(m.new_state), # EnumField in generate_composite + "cause": str(m.cause), # EnumField in generate_composite + "message": m.message, # PrimitiveField in generate_composite + }) + + +def listpeers_peers_channels_htlcs2py(m): + return remove_default({ + "direction": str(m.direction), # EnumField in generate_composite + "id": m.id, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "expiry": m.expiry, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "local_trimmed": m.local_trimmed, # PrimitiveField in generate_composite + "status": m.status, # PrimitiveField in generate_composite + "state": str(m.state), # EnumField in generate_composite + }) + + +def listpeers_peers_channels2py(m): + return remove_default({ + "state": str(m.state), # EnumField in generate_composite + "scratch_txid": hexlify(m.scratch_txid), # PrimitiveField in generate_composite + "owner": m.owner, # PrimitiveField in generate_composite + "short_channel_id": m.short_channel_id, # PrimitiveField in generate_composite + "channel_id": hexlify(m.channel_id), # PrimitiveField in generate_composite + "funding_txid": hexlify(m.funding_txid), # PrimitiveField in generate_composite + "funding_outnum": m.funding_outnum, # PrimitiveField in generate_composite + "initial_feerate": m.initial_feerate, # PrimitiveField in generate_composite + "last_feerate": m.last_feerate, # PrimitiveField in generate_composite + "next_feerate": m.next_feerate, # PrimitiveField in generate_composite + "next_fee_step": m.next_fee_step, # PrimitiveField in generate_composite + "inflight": [listpeers_peers_channels_inflight2py(i) for i in m.inflight], # ArrayField[composite] in generate_composite + "close_to": hexlify(m.close_to), # PrimitiveField in generate_composite + "private": m.private, # PrimitiveField in generate_composite + "opener": str(m.opener), # EnumField in generate_composite + "closer": str(m.closer), # EnumField in generate_composite + "features": [str(i) for i in m.features], # ArrayField[composite] in generate_composite + "to_us_msat": amount2msat(m.to_us_msat), # PrimitiveField in generate_composite + "min_to_us_msat": amount2msat(m.min_to_us_msat), # PrimitiveField in generate_composite + "max_to_us_msat": amount2msat(m.max_to_us_msat), # PrimitiveField in generate_composite + "total_msat": amount2msat(m.total_msat), # PrimitiveField in generate_composite + "fee_base_msat": amount2msat(m.fee_base_msat), # PrimitiveField in generate_composite + "fee_proportional_millionths": m.fee_proportional_millionths, # PrimitiveField in generate_composite + "dust_limit_msat": amount2msat(m.dust_limit_msat), # PrimitiveField in generate_composite + "max_total_htlc_in_msat": amount2msat(m.max_total_htlc_in_msat), # PrimitiveField in generate_composite + "their_reserve_msat": amount2msat(m.their_reserve_msat), # PrimitiveField in generate_composite + "our_reserve_msat": amount2msat(m.our_reserve_msat), # PrimitiveField in generate_composite + "spendable_msat": amount2msat(m.spendable_msat), # PrimitiveField in generate_composite + "receivable_msat": amount2msat(m.receivable_msat), # PrimitiveField in generate_composite + "minimum_htlc_in_msat": amount2msat(m.minimum_htlc_in_msat), # PrimitiveField in generate_composite + "minimum_htlc_out_msat": amount2msat(m.minimum_htlc_out_msat), # PrimitiveField in generate_composite + "maximum_htlc_out_msat": amount2msat(m.maximum_htlc_out_msat), # PrimitiveField in generate_composite + "their_to_self_delay": m.their_to_self_delay, # PrimitiveField in generate_composite + "our_to_self_delay": m.our_to_self_delay, # PrimitiveField in generate_composite + "max_accepted_htlcs": m.max_accepted_htlcs, # PrimitiveField in generate_composite + "state_changes": [listpeers_peers_channels_state_changes2py(i) for i in m.state_changes], # ArrayField[composite] in generate_composite + "status": [m.status for i in m.status], # ArrayField[primitive] in generate_composite + "in_payments_offered": m.in_payments_offered, # PrimitiveField in generate_composite + "in_offered_msat": amount2msat(m.in_offered_msat), # PrimitiveField in generate_composite + "in_payments_fulfilled": m.in_payments_fulfilled, # PrimitiveField in generate_composite + "in_fulfilled_msat": amount2msat(m.in_fulfilled_msat), # PrimitiveField in generate_composite + "out_payments_offered": m.out_payments_offered, # PrimitiveField in generate_composite + "out_offered_msat": amount2msat(m.out_offered_msat), # PrimitiveField in generate_composite + "out_payments_fulfilled": m.out_payments_fulfilled, # PrimitiveField in generate_composite + "out_fulfilled_msat": amount2msat(m.out_fulfilled_msat), # PrimitiveField in generate_composite + "htlcs": [listpeers_peers_channels_htlcs2py(i) for i in m.htlcs], # ArrayField[composite] in generate_composite + "close_to_addr": m.close_to_addr, # PrimitiveField in generate_composite + }) + + +def listpeers_peers2py(m): + return remove_default({ + "id": hexlify(m.id), # PrimitiveField in generate_composite + "connected": m.connected, # PrimitiveField in generate_composite + "log": [listpeers_peers_log2py(i) for i in m.log], # ArrayField[composite] in generate_composite + "channels": [listpeers_peers_channels2py(i) for i in m.channels], # ArrayField[composite] in generate_composite + "netaddr": [m.netaddr for i in m.netaddr], # ArrayField[primitive] in generate_composite + "remote_addr": m.remote_addr, # PrimitiveField in generate_composite + "features": hexlify(m.features), # PrimitiveField in generate_composite + }) + + +def listpeers2py(m): + return remove_default({ + "peers": [listpeers_peers2py(i) for i in m.peers], # ArrayField[composite] in generate_composite + }) + + +def listfunds_outputs2py(m): + return remove_default({ + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + "output": m.output, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "scriptpubkey": hexlify(m.scriptpubkey), # PrimitiveField in generate_composite + "address": m.address, # PrimitiveField in generate_composite + "redeemscript": hexlify(m.redeemscript), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "reserved": m.reserved, # PrimitiveField in generate_composite + "blockheight": m.blockheight, # PrimitiveField in generate_composite + }) + + +def listfunds_channels2py(m): + return remove_default({ + "peer_id": hexlify(m.peer_id), # PrimitiveField in generate_composite + "our_amount_msat": amount2msat(m.our_amount_msat), # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "funding_txid": hexlify(m.funding_txid), # PrimitiveField in generate_composite + "funding_output": m.funding_output, # PrimitiveField in generate_composite + "connected": m.connected, # PrimitiveField in generate_composite + "state": str(m.state), # EnumField in generate_composite + "short_channel_id": m.short_channel_id, # PrimitiveField in generate_composite + }) + + +def listfunds2py(m): + return remove_default({ + "outputs": [listfunds_outputs2py(i) for i in m.outputs], # ArrayField[composite] in generate_composite + "channels": [listfunds_channels2py(i) for i in m.channels], # ArrayField[composite] in generate_composite + }) + + +def sendpay2py(m): + return remove_default({ + "id": m.id, # PrimitiveField in generate_composite + "groupid": m.groupid, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "created_at": m.created_at, # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "label": m.label, # PrimitiveField in generate_composite + "partid": m.partid, # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + "message": m.message, # PrimitiveField in generate_composite + }) + + +def listchannels_channels2py(m): + return remove_default({ + "source": hexlify(m.source), # PrimitiveField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "short_channel_id": m.short_channel_id, # PrimitiveField in generate_composite + "public": m.public, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "message_flags": m.message_flags, # PrimitiveField in generate_composite + "channel_flags": m.channel_flags, # PrimitiveField in generate_composite + "active": m.active, # PrimitiveField in generate_composite + "last_update": m.last_update, # PrimitiveField in generate_composite + "base_fee_millisatoshi": m.base_fee_millisatoshi, # PrimitiveField in generate_composite + "fee_per_millionth": m.fee_per_millionth, # PrimitiveField in generate_composite + "delay": m.delay, # PrimitiveField in generate_composite + "htlc_minimum_msat": amount2msat(m.htlc_minimum_msat), # PrimitiveField in generate_composite + "htlc_maximum_msat": amount2msat(m.htlc_maximum_msat), # PrimitiveField in generate_composite + "features": hexlify(m.features), # PrimitiveField in generate_composite + }) + + +def listchannels2py(m): + return remove_default({ + "channels": [listchannels_channels2py(i) for i in m.channels], # ArrayField[composite] in generate_composite + }) + + +def addgossip2py(m): + return remove_default({ + }) + + +def autocleaninvoice2py(m): + return remove_default({ + "enabled": m.enabled, # PrimitiveField in generate_composite + "expired_by": m.expired_by, # PrimitiveField in generate_composite + "cycle_seconds": m.cycle_seconds, # PrimitiveField in generate_composite + }) + + +def checkmessage2py(m): + return remove_default({ + "verified": m.verified, # PrimitiveField in generate_composite + "pubkey": hexlify(m.pubkey), # PrimitiveField in generate_composite + }) + + +def close2py(m): + return remove_default({ + "type": str(m.item_type), # EnumField in generate_composite + "tx": hexlify(m.tx), # PrimitiveField in generate_composite + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + }) + + +def connect_address2py(m): + return remove_default({ + "type": str(m.item_type), # EnumField in generate_composite + "socket": m.socket, # PrimitiveField in generate_composite + "address": m.address, # PrimitiveField in generate_composite + "port": m.port, # PrimitiveField in generate_composite + }) + + +def connect2py(m): + return remove_default({ + "id": hexlify(m.id), # PrimitiveField in generate_composite + "features": hexlify(m.features), # PrimitiveField in generate_composite + "direction": str(m.direction), # EnumField in generate_composite + }) + + +def createinvoice2py(m): + return remove_default({ + "label": m.label, # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "expires_at": m.expires_at, # PrimitiveField in generate_composite + "pay_index": m.pay_index, # PrimitiveField in generate_composite + "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite + "paid_at": m.paid_at, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + "local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite + "payer_note": m.payer_note, # PrimitiveField in generate_composite + }) + + +def datastore2py(m): + return remove_default({ + "key": [m.key for i in m.key], # ArrayField[primitive] in generate_composite + "generation": m.generation, # PrimitiveField in generate_composite + "hex": hexlify(m.hex), # PrimitiveField in generate_composite + "string": m.string, # PrimitiveField in generate_composite + }) + + +def createonion2py(m): + return remove_default({ + "onion": hexlify(m.onion), # PrimitiveField in generate_composite + "shared_secrets": [hexlify(m.shared_secrets) for i in hexlify(m.shared_secrets)], # ArrayField[primitive] in generate_composite + }) + + +def deldatastore2py(m): + return remove_default({ + "key": [m.key for i in m.key], # ArrayField[primitive] in generate_composite + "generation": m.generation, # PrimitiveField in generate_composite + "hex": hexlify(m.hex), # PrimitiveField in generate_composite + "string": m.string, # PrimitiveField in generate_composite + }) + + +def delexpiredinvoice2py(m): + return remove_default({ + }) + + +def delinvoice2py(m): + return remove_default({ + "label": m.label, # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "expires_at": m.expires_at, # PrimitiveField in generate_composite + "local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite + "payer_note": m.payer_note, # PrimitiveField in generate_composite + }) + + +def invoice2py(m): + return remove_default({ + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "payment_secret": hexlify(m.payment_secret), # PrimitiveField in generate_composite + "expires_at": m.expires_at, # PrimitiveField in generate_composite + "warning_capacity": m.warning_capacity, # PrimitiveField in generate_composite + "warning_offline": m.warning_offline, # PrimitiveField in generate_composite + "warning_deadends": m.warning_deadends, # PrimitiveField in generate_composite + "warning_private_unused": m.warning_private_unused, # PrimitiveField in generate_composite + "warning_mpp": m.warning_mpp, # PrimitiveField in generate_composite + }) + + +def listdatastore_datastore2py(m): + return remove_default({ + "key": [m.key for i in m.key], # ArrayField[primitive] in generate_composite + "generation": m.generation, # PrimitiveField in generate_composite + "hex": hexlify(m.hex), # PrimitiveField in generate_composite + "string": m.string, # PrimitiveField in generate_composite + }) + + +def listdatastore2py(m): + return remove_default({ + "datastore": [listdatastore_datastore2py(i) for i in m.datastore], # ArrayField[composite] in generate_composite + }) + + +def listinvoices_invoices2py(m): + return remove_default({ + "label": m.label, # PrimitiveField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "expires_at": m.expires_at, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "local_offer_id": hexlify(m.local_offer_id), # PrimitiveField in generate_composite + "payer_note": m.payer_note, # PrimitiveField in generate_composite + "pay_index": m.pay_index, # PrimitiveField in generate_composite + "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite + "paid_at": m.paid_at, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + }) + + +def listinvoices2py(m): + return remove_default({ + "invoices": [listinvoices_invoices2py(i) for i in m.invoices], # ArrayField[composite] in generate_composite + }) + + +def sendonion2py(m): + return remove_default({ + "id": m.id, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "created_at": m.created_at, # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "label": m.label, # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "partid": m.partid, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + "message": m.message, # PrimitiveField in generate_composite + }) + + +def listsendpays_payments2py(m): + return remove_default({ + "id": m.id, # PrimitiveField in generate_composite + "groupid": m.groupid, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "created_at": m.created_at, # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "label": m.label, # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + "erroronion": hexlify(m.erroronion), # PrimitiveField in generate_composite + }) + + +def listsendpays2py(m): + return remove_default({ + "payments": [listsendpays_payments2py(i) for i in m.payments], # ArrayField[composite] in generate_composite + }) + + +def listtransactions_transactions_inputs2py(m): + return remove_default({ + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + "index": m.index, # PrimitiveField in generate_composite + "sequence": m.sequence, # PrimitiveField in generate_composite + "type": str(m.item_type), # EnumField in generate_composite + "channel": m.channel, # PrimitiveField in generate_composite + }) + + +def listtransactions_transactions_outputs2py(m): + return remove_default({ + "index": m.index, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "script_pub_key": hexlify(m.script_pub_key), # PrimitiveField in generate_composite + "type": str(m.item_type), # EnumField in generate_composite + "channel": m.channel, # PrimitiveField in generate_composite + }) + + +def listtransactions_transactions2py(m): + return remove_default({ + "hash": hexlify(m.hash), # PrimitiveField in generate_composite + "rawtx": hexlify(m.rawtx), # PrimitiveField in generate_composite + "blockheight": m.blockheight, # PrimitiveField in generate_composite + "txindex": m.txindex, # PrimitiveField in generate_composite + "type": [str(i) for i in m.type], # ArrayField[composite] in generate_composite + "channel": m.channel, # PrimitiveField in generate_composite + "locktime": m.locktime, # PrimitiveField in generate_composite + "version": m.version, # PrimitiveField in generate_composite + "inputs": [listtransactions_transactions_inputs2py(i) for i in m.inputs], # ArrayField[composite] in generate_composite + "outputs": [listtransactions_transactions_outputs2py(i) for i in m.outputs], # ArrayField[composite] in generate_composite + }) + + +def listtransactions2py(m): + return remove_default({ + "transactions": [listtransactions_transactions2py(i) for i in m.transactions], # ArrayField[composite] in generate_composite + }) + + +def pay2py(m): + return remove_default({ + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "created_at": m.created_at, # PrimitiveField in generate_composite + "parts": m.parts, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "warning_partial_completion": m.warning_partial_completion, # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + }) + + +def listnodes_nodes_addresses2py(m): + return remove_default({ + "type": str(m.item_type), # EnumField in generate_composite + "port": m.port, # PrimitiveField in generate_composite + "address": m.address, # PrimitiveField in generate_composite + }) + + +def listnodes_nodes2py(m): + return remove_default({ + "nodeid": hexlify(m.nodeid), # PrimitiveField in generate_composite + "last_timestamp": m.last_timestamp, # PrimitiveField in generate_composite + "alias": m.alias, # PrimitiveField in generate_composite + "color": hexlify(m.color), # PrimitiveField in generate_composite + "features": hexlify(m.features), # PrimitiveField in generate_composite + "addresses": [listnodes_nodes_addresses2py(i) for i in m.addresses], # ArrayField[composite] in generate_composite + }) + + +def listnodes2py(m): + return remove_default({ + "nodes": [listnodes_nodes2py(i) for i in m.nodes], # ArrayField[composite] in generate_composite + }) + + +def waitanyinvoice2py(m): + return remove_default({ + "label": m.label, # PrimitiveField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "expires_at": m.expires_at, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "pay_index": m.pay_index, # PrimitiveField in generate_composite + "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite + "paid_at": m.paid_at, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + }) + + +def waitinvoice2py(m): + return remove_default({ + "label": m.label, # PrimitiveField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "expires_at": m.expires_at, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "pay_index": m.pay_index, # PrimitiveField in generate_composite + "amount_received_msat": amount2msat(m.amount_received_msat), # PrimitiveField in generate_composite + "paid_at": m.paid_at, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + }) + + +def waitsendpay2py(m): + return remove_default({ + "id": m.id, # PrimitiveField in generate_composite + "groupid": m.groupid, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "created_at": m.created_at, # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "label": m.label, # PrimitiveField in generate_composite + "partid": m.partid, # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + }) + + +def newaddr2py(m): + return remove_default({ + "bech32": m.bech32, # PrimitiveField in generate_composite + "p2sh_segwit": m.p2sh_segwit, # PrimitiveField in generate_composite + }) + + +def withdraw2py(m): + return remove_default({ + "tx": hexlify(m.tx), # PrimitiveField in generate_composite + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + "psbt": m.psbt, # PrimitiveField in generate_composite + }) + + +def keysend2py(m): + return remove_default({ + "payment_preimage": hexlify(m.payment_preimage), # PrimitiveField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "created_at": m.created_at, # PrimitiveField in generate_composite + "parts": m.parts, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "warning_partial_completion": m.warning_partial_completion, # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + }) + + +def fundpsbt_reservations2py(m): + return remove_default({ + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + "vout": m.vout, # PrimitiveField in generate_composite + "was_reserved": m.was_reserved, # PrimitiveField in generate_composite + "reserved": m.reserved, # PrimitiveField in generate_composite + "reserved_to_block": m.reserved_to_block, # PrimitiveField in generate_composite + }) + + +def fundpsbt2py(m): + return remove_default({ + "psbt": m.psbt, # PrimitiveField in generate_composite + "feerate_per_kw": m.feerate_per_kw, # PrimitiveField in generate_composite + "estimated_final_weight": m.estimated_final_weight, # PrimitiveField in generate_composite + "excess_msat": amount2msat(m.excess_msat), # PrimitiveField in generate_composite + "change_outnum": m.change_outnum, # PrimitiveField in generate_composite + "reservations": [fundpsbt_reservations2py(i) for i in m.reservations], # ArrayField[composite] in generate_composite + }) + + +def sendpsbt2py(m): + return remove_default({ + "tx": hexlify(m.tx), # PrimitiveField in generate_composite + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + }) + + +def signpsbt2py(m): + return remove_default({ + "signed_psbt": m.signed_psbt, # PrimitiveField in generate_composite + }) + + +def utxopsbt_reservations2py(m): + return remove_default({ + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + "vout": m.vout, # PrimitiveField in generate_composite + "was_reserved": m.was_reserved, # PrimitiveField in generate_composite + "reserved": m.reserved, # PrimitiveField in generate_composite + "reserved_to_block": m.reserved_to_block, # PrimitiveField in generate_composite + }) + + +def utxopsbt2py(m): + return remove_default({ + "psbt": m.psbt, # PrimitiveField in generate_composite + "feerate_per_kw": m.feerate_per_kw, # PrimitiveField in generate_composite + "estimated_final_weight": m.estimated_final_weight, # PrimitiveField in generate_composite + "excess_msat": amount2msat(m.excess_msat), # PrimitiveField in generate_composite + "change_outnum": m.change_outnum, # PrimitiveField in generate_composite + "reservations": [utxopsbt_reservations2py(i) for i in m.reservations], # ArrayField[composite] in generate_composite + }) + + +def txdiscard2py(m): + return remove_default({ + "unsigned_tx": hexlify(m.unsigned_tx), # PrimitiveField in generate_composite + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + }) + + +def txprepare2py(m): + return remove_default({ + "psbt": m.psbt, # PrimitiveField in generate_composite + "unsigned_tx": hexlify(m.unsigned_tx), # PrimitiveField in generate_composite + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + }) + + +def txsend2py(m): + return remove_default({ + "psbt": m.psbt, # PrimitiveField in generate_composite + "tx": hexlify(m.tx), # PrimitiveField in generate_composite + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + }) + + +def disconnect2py(m): + return remove_default({ + }) + + +def feerates_perkb2py(m): + return remove_default({ + "min_acceptable": m.min_acceptable, # PrimitiveField in generate_composite + "max_acceptable": m.max_acceptable, # PrimitiveField in generate_composite + "opening": m.opening, # PrimitiveField in generate_composite + "mutual_close": m.mutual_close, # PrimitiveField in generate_composite + "unilateral_close": m.unilateral_close, # PrimitiveField in generate_composite + "delayed_to_us": m.delayed_to_us, # PrimitiveField in generate_composite + "htlc_resolution": m.htlc_resolution, # PrimitiveField in generate_composite + "penalty": m.penalty, # PrimitiveField in generate_composite + }) + + +def feerates_perkw2py(m): + return remove_default({ + "min_acceptable": m.min_acceptable, # PrimitiveField in generate_composite + "max_acceptable": m.max_acceptable, # PrimitiveField in generate_composite + "opening": m.opening, # PrimitiveField in generate_composite + "mutual_close": m.mutual_close, # PrimitiveField in generate_composite + "unilateral_close": m.unilateral_close, # PrimitiveField in generate_composite + "delayed_to_us": m.delayed_to_us, # PrimitiveField in generate_composite + "htlc_resolution": m.htlc_resolution, # PrimitiveField in generate_composite + "penalty": m.penalty, # PrimitiveField in generate_composite + }) + + +def feerates_onchain_fee_estimates2py(m): + return remove_default({ + "opening_channel_satoshis": m.opening_channel_satoshis, # PrimitiveField in generate_composite + "mutual_close_satoshis": m.mutual_close_satoshis, # PrimitiveField in generate_composite + "unilateral_close_satoshis": m.unilateral_close_satoshis, # PrimitiveField in generate_composite + "htlc_timeout_satoshis": m.htlc_timeout_satoshis, # PrimitiveField in generate_composite + "htlc_success_satoshis": m.htlc_success_satoshis, # PrimitiveField in generate_composite + }) + + +def feerates2py(m): + return remove_default({ + "warning_missing_feerates": m.warning_missing_feerates, # PrimitiveField in generate_composite + }) + + +def getroute_route2py(m): + return remove_default({ + "id": hexlify(m.id), # PrimitiveField in generate_composite + "channel": m.channel, # PrimitiveField in generate_composite + "direction": m.direction, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "delay": m.delay, # PrimitiveField in generate_composite + "style": str(m.style), # EnumField in generate_composite + }) + + +def getroute2py(m): + return remove_default({ + "route": [getroute_route2py(i) for i in m.route], # ArrayField[composite] in generate_composite + }) + + +def listforwards_forwards2py(m): + return remove_default({ + "in_channel": m.in_channel, # PrimitiveField in generate_composite + "in_msat": amount2msat(m.in_msat), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "received_time": m.received_time, # PrimitiveField in generate_composite + "out_channel": m.out_channel, # PrimitiveField in generate_composite + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "style": str(m.style), # EnumField in generate_composite + "fee_msat": amount2msat(m.fee_msat), # PrimitiveField in generate_composite + "out_msat": amount2msat(m.out_msat), # PrimitiveField in generate_composite + }) + + +def listforwards2py(m): + return remove_default({ + "forwards": [listforwards_forwards2py(i) for i in m.forwards], # ArrayField[composite] in generate_composite + }) + + +def listpays_pays2py(m): + return remove_default({ + "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "status": str(m.status), # EnumField in generate_composite + "destination": hexlify(m.destination), # PrimitiveField in generate_composite + "created_at": m.created_at, # PrimitiveField in generate_composite + "label": m.label, # PrimitiveField in generate_composite + "bolt11": m.bolt11, # PrimitiveField in generate_composite + "description": m.description, # PrimitiveField in generate_composite + "bolt12": m.bolt12, # PrimitiveField in generate_composite + "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite + "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "erroronion": hexlify(m.erroronion), # PrimitiveField in generate_composite + }) + + +def listpays2py(m): + return remove_default({ + "pays": [listpays_pays2py(i) for i in m.pays], # ArrayField[composite] in generate_composite + }) + + +def ping2py(m): + return remove_default({ + "totlen": m.totlen, # PrimitiveField in generate_composite + }) + + +def signmessage2py(m): + return remove_default({ + "signature": hexlify(m.signature), # PrimitiveField in generate_composite + "recid": hexlify(m.recid), # PrimitiveField in generate_composite + "zbase": m.zbase, # PrimitiveField in generate_composite + }) From b8bcc7d13f50779fd54490681bf57317ffe431ee Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 30 Jun 2022 11:29:57 +0200 Subject: [PATCH 1068/1530] pytest: Add a new RPC interface to talk to grpc This allows us to re-use existing tests (assuming the call and fields are covered by `cln-rpc` and `cln-grpc`) to test the full roundtrip from test over the grpc interface to the json-rpc interface and back again. You can switch to the grpc interface by setting the `CLN_TEST_GRPC` environment variable to 1, but for now only very few shims are implemented (due to the non-generated nature of LightningRpc). --- contrib/pyln-testing/pyln/testing/grpc.py | 84 ++++++++++++++++++++++ contrib/pyln-testing/pyln/testing/utils.py | 64 +++++++++++++++-- tools/check-spelling.sh | 2 +- 3 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 contrib/pyln-testing/pyln/testing/grpc.py diff --git a/contrib/pyln-testing/pyln/testing/grpc.py b/contrib/pyln-testing/pyln/testing/grpc.py new file mode 100644 index 000000000000..593f1ee09148 --- /dev/null +++ b/contrib/pyln-testing/pyln/testing/grpc.py @@ -0,0 +1,84 @@ +"""A drop-in replacement for the JSON-RPC LightningRpc +""" + +from pyln.testing import node_pb2_grpc as pbgrpc +from pyln.testing import node_pb2 as pb +import grpc +import json +from google.protobuf.json_format import MessageToJson +from pyln.testing import grpc2py + + +DUMMY_CA_PEM = b"""-----BEGIN CERTIFICATE----- +MIIBcTCCARigAwIBAgIJAJhah1bqO05cMAoGCCqGSM49BAMCMBYxFDASBgNVBAMM +C2NsbiBSb290IENBMCAXDTc1MDEwMTAwMDAwMFoYDzQwOTYwMTAxMDAwMDAwWjAW +MRQwEgYDVQQDDAtjbG4gUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA +BPF4JrGsOsksgsYM1NNdUdLESwOxkzyD75Rnj/g7sFEVYXewcmyB3MRGCBx2a3/7 +ft2Xu2ED6WigajaHlnSvfUyjTTBLMBkGA1UdEQQSMBCCA2NsboIJbG9jYWxob3N0 +MB0GA1UdDgQWBBRcTjvqVodamGirO6sX1rOR02LwXzAPBgNVHRMBAf8EBTADAQH/ +MAoGCCqGSM49BAMCA0cAMEQCICDvV5iFw/nmJdl6rlEEGAdBdZqjxD0tV6U/FvuL +7PycAiASEMtsFtpfiUvxveBkOGt7AN32GP/Z75l+GhYXh7L1ig== +-----END CERTIFICATE-----""" + + +DUMMY_CA_KEY_PEM = b"""-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqbU7LQsRcvmI5vE5 +MBBNK3imhIU2jmAczgvLuBi/Ys+hRANCAATxeCaxrDrJLILGDNTTXVHSxEsDsZM8 +g++UZ4/4O7BRFWF3sHJsgdzERggcdmt/+37dl7thA+looGo2h5Z0r31M +-----END PRIVATE KEY-----""" + + +DUMMY_CLIENT_KEY_PEM = b"""-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgIEdQyKso8PaD1kiz +xxFEcKiTvTg+bej4Nc/GqnXipcGhRANCAARGoUNSnWx1qgt4RiVG8tOMX1vpKvhr +OLcUJ92T++kIFZchZvcTXwnlNiTAQg3ukL+RYyG5Q1PaYrYRVlOtl1T0 +-----END PRIVATE KEY-----""" + + +DUMMY_CLIENT_PEM = b"""-----BEGIN CERTIFICATE----- +MIIBRDCB7KADAgECAgkA8SsXq7IZfi8wCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwL +Y2xuIFJvb3QgQ0EwIBcNNzUwMTAxMDAwMDAwWhgPNDA5NjAxMDEwMDAwMDBaMBox +GDAWBgNVBAMMD2NsbiBncnBjIFNlcnZlcjBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABEahQ1KdbHWqC3hGJUby04xfW+kq+Gs4txQn3ZP76QgVlyFm9xNfCeU2JMBC +De6Qv5FjIblDU9pithFWU62XVPSjHTAbMBkGA1UdEQQSMBCCA2NsboIJbG9jYWxo +b3N0MAoGCCqGSM49BAMCA0cAMEQCICTU/YAs35cb6DRdZNzO1YbEt77uEjcqMRca +Hh6kK99RAiAKOQOkGnoAICjBmBJeC/iC4/+hhhkWZtFgbC3Jg5JD0w== +-----END CERTIFICATE-----""" + + +class LightningGrpc(object): + def __init__( + self, + host: str, + port: int, + root_certificates: bytes = DUMMY_CA_PEM, + private_key: bytes = DUMMY_CLIENT_KEY_PEM, + certificate_chain: bytes = DUMMY_CLIENT_PEM, + ): + self.credentials = grpc.ssl_channel_credentials( + root_certificates=root_certificates, + private_key=private_key, + certificate_chain=certificate_chain, + ) + self.channel = grpc.secure_channel( + f"{host}:{port}", + self.credentials, + options=(("grpc.ssl_target_name_override", "cln"),), + ) + self.stub = pbgrpc.NodeStub(self.channel) + + def getinfo(self): + return grpc2py.getinfo2py( + self.stub.Getinfo(pb.GetinfoRequest()) + ) + + def connect(self, peer_id, host=None, port=None): + """ + Connect to {peer_id} at {host} and {port}. + """ + payload = pb.ConnectRequest( + id=peer_id, + host=host, + port=port + ) + return grpc2py.connect2py(self.stub.ConnectPeer(payload)) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index a5f12632f1a4..e9d903995897 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -10,6 +10,7 @@ from decimal import Decimal from pyln.client import LightningRpc from pyln.client import Millisatoshi +from pyln.testing import grpc import ephemeral_port_reserve # type: ignore import json @@ -538,7 +539,15 @@ def getnewaddress(self): class LightningD(TailableProc): - def __init__(self, lightning_dir, bitcoindproxy, port=9735, random_hsm=False, node_id=0): + def __init__( + self, + lightning_dir, + bitcoindproxy, + port=9735, + random_hsm=False, + node_id=0, + grpc_port=None + ): # We handle our own version of verbose, below. TailableProc.__init__(self, lightning_dir, verbose=False) self.executable = 'lightningd' @@ -564,6 +573,9 @@ def __init__(self, lightning_dir, bitcoindproxy, port=9735, random_hsm=False, no 'bitcoin-datadir': lightning_dir, } + if grpc_port is not None: + opts['grpc-port'] = grpc_port + for k, v in opts.items(): self.opts[k] = v @@ -693,19 +705,22 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai self.allow_bad_gossip = allow_bad_gossip self.allow_warning = allow_warning self.db = db + self.lightning_dir = Path(lightning_dir) # Assume successful exit self.rc = 0 - socket_path = os.path.join(lightning_dir, TEST_NETWORK, "lightning-rpc").format(node_id) - self.rpc = PrettyPrintingLightningRpc(socket_path, self.executor, jsonschemas=jsonschemas) + # Ensure we have an RPC we can use to talk to the node + self._create_rpc(jsonschemas) self.gossip_store = GossipStore(Path(lightning_dir, TEST_NETWORK, "gossip_store")) self.daemon = LightningD( lightning_dir, bitcoindproxy=bitcoind.get_proxy(), - port=port, random_hsm=random_hsm, node_id=node_id + port=port, random_hsm=random_hsm, node_id=node_id, + grpc_port=self.grpc_port, ) + # If we have a disconnect string, dump it to a file for daemon. if disconnect: self.daemon.disconnect_file = os.path.join(lightning_dir, TEST_NETWORK, "dev_disconnect") @@ -751,6 +766,47 @@ def __init__(self, node_id, lightning_dir, bitcoind, executor, valgrind, may_fai if SLOW_MACHINE: self.daemon.cmd_prefix += ['--read-inline-info=no'] + def _create_rpc(self, jsonschemas): + """Prepares anything related to the RPC. + """ + if os.environ.get('CLN_TEST_GRPC') == '1': + logging.info("Switching to GRPC based RPC for tests") + self._create_grpc_rpc() + else: + self._create_jsonrpc_rpc(jsonschemas) + + def _create_grpc_rpc(self): + self.grpc_port = reserve_unused_port() + d = self.lightning_dir / TEST_NETWORK + d.mkdir(parents=True, exist_ok=True) + + # Copy all the certificates and keys into place: + with (d / "ca.pem").open(mode='wb') as f: + f.write(grpc.DUMMY_CA_PEM) + + with (d / "ca-key.pem").open(mode='wb') as f: + f.write(grpc.DUMMY_CA_KEY_PEM) + + # Now the node will actually start up and use them, so we can + # create the RPC instance. + self.rpc = grpc.LightningGrpc( + host='localhost', + port=self.grpc_port, + root_certificates=grpc.DUMMY_CA_PEM, + private_key=grpc.DUMMY_CLIENT_KEY_PEM, + certificate_chain=grpc.DUMMY_CLIENT_PEM + ) + + def _create_jsonrpc_rpc(self, jsonschemas): + socket_path = self.lightning_dir / TEST_NETWORK / "lightning-rpc" + self.grpc_port = None + + self.rpc = PrettyPrintingLightningRpc( + str(socket_path), + self.executor, + jsonschemas=jsonschemas + ) + def connect(self, remote_node): self.rpc.connect(remote_node.info['id'], '127.0.0.1', remote_node.daemon.port) diff --git a/tools/check-spelling.sh b/tools/check-spelling.sh index 900fd87e4af8..c96764841983 100755 --- a/tools/check-spelling.sh +++ b/tools/check-spelling.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -if git --no-pager grep -nHiE 'l[ightn]{6}g|l[ightn]{8}g|ilghtning|lgihtning|lihgtning|ligthning|lighnting|lightinng|lightnnig|lightnign' -- . ':!tools/check-spelling.sh' | grep -vE "highlighting"; then +if git --no-pager grep -nHiE 'l[ightn]{6}g|l[ightn]{8}g|ilghtning|lgihtning|lihgtning|ligthning|lighnting|lightinng|lightnnig|lightnign' -- . ':!tools/check-spelling.sh' | grep -vE "highlighting|LightningGrpc"; then echo "Identified a likely misspelling of the word \"lightning\" (see above). Please fix." echo "Is this warning incorrect? Please teach tools/check-spelling.sh about the exciting new word." exit 1 From 77f5eb556bab084b2a184c09dfaf46654cae7724 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 7 Jun 2022 21:30:58 +0200 Subject: [PATCH 1069/1530] msggen: Add fundchannel request --- .msggen.json | 20 ++++ cln-grpc/proto/node.proto | 22 ++++ cln-grpc/src/convert.rs | 31 ++++++ cln-grpc/src/server.rs | 32 ++++++ cln-rpc/src/model.rs | 40 +++++++ contrib/msggen/msggen/utils/utils.py | 2 +- contrib/pyln-testing/pyln/testing/grpc2py.py | 10 ++ contrib/pyln-testing/pyln/testing/node_pb2.py | 102 +++++++++++------- .../pyln/testing/node_pb2_grpc.py | 33 ++++++ doc/schemas/fundchannel.request.json | 45 ++++++++ 10 files changed, 295 insertions(+), 42 deletions(-) create mode 100644 doc/schemas/fundchannel.request.json diff --git a/.msggen.json b/.msggen.json index 101427244356..5a397d7fdb7f 100644 --- a/.msggen.json +++ b/.msggen.json @@ -381,6 +381,26 @@ "Feerates.perkw": 3, "Feerates.warning_missing_feerates": 1 }, + "FundchannelRequest": { + "FundChannel.amount": 1, + "FundChannel.announce": 3, + "FundChannel.close_to": 6, + "FundChannel.compact_lease": 8, + "FundChannel.feerate": 2, + "FundChannel.id": 9, + "FundChannel.minconf": 10, + "FundChannel.minconf[]": 4, + "FundChannel.push_msat": 5, + "FundChannel.request_amt": 7, + "FundChannel.utxos[]": 11 + }, + "FundchannelResponse": { + "FundChannel.channel_id": 4, + "FundChannel.close_to": 5, + "FundChannel.outnum": 3, + "FundChannel.tx": 1, + "FundChannel.txid": 2 + }, "FundpsbtRequest": { "FundPsbt.excess_as_change": 8, "FundPsbt.feerate": 2, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index cbaf889a3259..4235e5ef7db7 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -47,6 +47,7 @@ service Node { rpc TxSend(TxsendRequest) returns (TxsendResponse) {} rpc Disconnect(DisconnectRequest) returns (DisconnectResponse) {} rpc Feerates(FeeratesRequest) returns (FeeratesResponse) {} + rpc FundChannel(FundchannelRequest) returns (FundchannelResponse) {} rpc GetRoute(GetrouteRequest) returns (GetrouteResponse) {} rpc ListForwards(ListforwardsRequest) returns (ListforwardsResponse) {} rpc ListPays(ListpaysRequest) returns (ListpaysResponse) {} @@ -1138,6 +1139,27 @@ message FeeratesOnchain_fee_estimates { uint64 htlc_success_satoshis = 5; } +message FundchannelRequest { + bytes id = 9; + AmountOrAll amount = 1; + optional Feerate feerate = 2; + optional bool announce = 3; + optional double minconf = 10; + optional Amount push_msat = 5; + optional string close_to = 6; + optional Amount request_amt = 7; + optional string compact_lease = 8; + repeated Outpoint utxos = 11; +} + +message FundchannelResponse { + bytes tx = 1; + bytes txid = 2; + uint32 outnum = 3; + bytes channel_id = 4; + optional bytes close_to = 5; +} + message GetrouteRequest { bytes id = 1; Amount amount_msat = 9; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 144a0b1117c5..f062809ab095 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -853,6 +853,19 @@ impl From<&responses::FeeratesResponse> for pb::FeeratesResponse { } } +#[allow(unused_variables)] +impl From<&responses::FundchannelResponse> for pb::FundchannelResponse { + fn from(c: &responses::FundchannelResponse) -> Self { + Self { + tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex + txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid + outnum: c.outnum.clone(), // Rule #2 for type u32 + channel_id: hex::decode(&c.channel_id).unwrap(), // Rule #2 for type hex + close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + } + } +} + #[allow(unused_variables)] impl From<&responses::GetrouteRoute> for pb::GetrouteRoute { fn from(c: &responses::GetrouteRoute) -> Self { @@ -1424,6 +1437,24 @@ impl From<&pb::FeeratesRequest> for requests::FeeratesRequest { } } +#[allow(unused_variables)] +impl From<&pb::FundchannelRequest> for requests::FundchannelRequest { + fn from(c: &pb::FundchannelRequest) -> Self { + Self { + id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + amount: c.amount.as_ref().unwrap().into(), // Rule #1 for type msat_or_all + feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? + announce: c.announce.clone(), // Rule #1 for type boolean? + minconf: c.minconf.clone(), // Rule #1 for type number? + push_msat: c.push_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? + close_to: c.close_to.clone(), // Rule #1 for type string? + request_amt: c.request_amt.as_ref().map(|a| a.into()), // Rule #1 for type msat? + compact_lease: c.compact_lease.clone(), // Rule #1 for type string? + utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 + } + } +} + #[allow(unused_variables)] impl From<&pb::GetrouteRequest> for requests::GetrouteRequest { fn from(c: &pb::GetrouteRequest) -> Self { diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index bd4183a0aa09..d7982d433797 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -1274,6 +1274,38 @@ async fn feerates( } +async fn fund_channel( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::FundchannelRequest = (&req).into(); + debug!("Client asked for fund_channel"); + trace!("fund_channel request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::FundChannel(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method FundChannel: {:?}", e)))?; + match result { + Response::FundChannel(r) => { + trace!("fund_channel response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call FundChannel", + r + ) + )), + } + +} + async fn get_route( &self, request: tonic::Request, diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index c727466c359f..61a759592e3b 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -55,6 +55,7 @@ pub enum Request { TxSend(requests::TxsendRequest), Disconnect(requests::DisconnectRequest), Feerates(requests::FeeratesRequest), + FundChannel(requests::FundchannelRequest), GetRoute(requests::GetrouteRequest), ListForwards(requests::ListforwardsRequest), ListPays(requests::ListpaysRequest), @@ -105,6 +106,7 @@ pub enum Response { TxSend(responses::TxsendResponse), Disconnect(responses::DisconnectResponse), Feerates(responses::FeeratesResponse), + FundChannel(responses::FundchannelResponse), GetRoute(responses::GetrouteResponse), ListForwards(responses::ListforwardsResponse), ListPays(responses::ListpaysResponse), @@ -698,6 +700,30 @@ pub mod requests { pub style: FeeratesStyle, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FundchannelRequest { + #[serde(alias = "id")] + pub id: Pubkey, + #[serde(alias = "amount")] + pub amount: AmountOrAll, + #[serde(alias = "feerate", skip_serializing_if = "Option::is_none")] + pub feerate: Option, + #[serde(alias = "announce", skip_serializing_if = "Option::is_none")] + pub announce: Option, + #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] + pub minconf: Option, + #[serde(alias = "push_msat", skip_serializing_if = "Option::is_none")] + pub push_msat: Option, + #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] + pub close_to: Option, + #[serde(alias = "request_amt", skip_serializing_if = "Option::is_none")] + pub request_amt: Option, + #[serde(alias = "compact_lease", skip_serializing_if = "Option::is_none")] + pub compact_lease: Option, + #[serde(alias = "utxos", skip_serializing_if = "Option::is_none")] + pub utxos: Option>, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetrouteRequest { #[serde(alias = "id")] @@ -2522,6 +2548,20 @@ pub mod responses { pub warning_missing_feerates: Option, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct FundchannelResponse { + #[serde(alias = "tx")] + pub tx: String, + #[serde(alias = "txid")] + pub txid: String, + #[serde(alias = "outnum")] + pub outnum: u32, + #[serde(alias = "channel_id")] + pub channel_id: String, + #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] + pub close_to: Option, + } + /// The features understood by the destination node #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum GetrouteRouteStyle { diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py index 88fe1f6d4280..18224d68458a 100644 --- a/contrib/msggen/msggen/utils/utils.py +++ b/contrib/msggen/msggen/utils/utils.py @@ -82,7 +82,7 @@ def load_jsonrpc_service(schema_dir: str = None): # "fetchinvoice", # "fundchannel_cancel", # "fundchannel_complete", - # "fundchannel", + "FundChannel", # "fundchannel_start", # "funderupdate", # "getlog", diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index a0bb67b47f7e..c0daf26642f4 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -769,6 +769,16 @@ def feerates2py(m): }) +def fundchannel2py(m): + return remove_default({ + "tx": hexlify(m.tx), # PrimitiveField in generate_composite + "txid": hexlify(m.txid), # PrimitiveField in generate_composite + "outnum": m.outnum, # PrimitiveField in generate_composite + "channel_id": hexlify(m.channel_id), # PrimitiveField in generate_composite + "close_to": hexlify(m.close_to), # PrimitiveField in generate_composite + }) + + def getroute_route2py(m): return remove_default({ "id": hexlify(m.id), # PrimitiveField in generate_composite diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index adfdf835414e..68a189fdd032 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t2\xfd\x15\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\x92\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\x01H\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"w\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x42\x0b\n\t_close_to\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t2\xc1\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x62\x06proto3') @@ -128,6 +128,8 @@ _FEERATESPERKB = DESCRIPTOR.message_types_by_name['FeeratesPerkb'] _FEERATESPERKW = DESCRIPTOR.message_types_by_name['FeeratesPerkw'] _FEERATESONCHAIN_FEE_ESTIMATES = DESCRIPTOR.message_types_by_name['FeeratesOnchain_fee_estimates'] +_FUNDCHANNELREQUEST = DESCRIPTOR.message_types_by_name['FundchannelRequest'] +_FUNDCHANNELRESPONSE = DESCRIPTOR.message_types_by_name['FundchannelResponse'] _GETROUTEREQUEST = DESCRIPTOR.message_types_by_name['GetrouteRequest'] _GETROUTERESPONSE = DESCRIPTOR.message_types_by_name['GetrouteResponse'] _GETROUTEROUTE = DESCRIPTOR.message_types_by_name['GetrouteRoute'] @@ -938,6 +940,20 @@ }) _sym_db.RegisterMessage(FeeratesOnchain_fee_estimates) +FundchannelRequest = _reflection.GeneratedProtocolMessageType('FundchannelRequest', (_message.Message,), { + 'DESCRIPTOR' : _FUNDCHANNELREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FundchannelRequest) + }) +_sym_db.RegisterMessage(FundchannelRequest) + +FundchannelResponse = _reflection.GeneratedProtocolMessageType('FundchannelResponse', (_message.Message,), { + 'DESCRIPTOR' : _FUNDCHANNELRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.FundchannelResponse) + }) +_sym_db.RegisterMessage(FundchannelResponse) + GetrouteRequest = _reflection.GeneratedProtocolMessageType('GetrouteRequest', (_message.Message,), { 'DESCRIPTOR' : _GETROUTEREQUEST, '__module__' : 'node_pb2' @@ -1307,44 +1323,48 @@ _FEERATESPERKW._serialized_end=24265 _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24268 _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24461 - _GETROUTEREQUEST._serialized_start=24464 - _GETROUTEREQUEST._serialized_end=24700 - _GETROUTERESPONSE._serialized_start=24702 - _GETROUTERESPONSE._serialized_end=24755 - _GETROUTEROUTE._serialized_start=24758 - _GETROUTEROUTE._serialized_end=24955 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=24926 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=24955 - _LISTFORWARDSREQUEST._serialized_start=24958 - _LISTFORWARDSREQUEST._serialized_end=25216 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25098 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25174 - _LISTFORWARDSRESPONSE._serialized_start=25218 - _LISTFORWARDSRESPONSE._serialized_end=25285 - _LISTFORWARDSFORWARDS._serialized_start=25288 - _LISTFORWARDSFORWARDS._serialized_end=25856 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=25653 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=25737 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=25739 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=25787 - _LISTPAYSREQUEST._serialized_start=25859 - _LISTPAYSREQUEST._serialized_end=26078 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=25984 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26039 - _LISTPAYSRESPONSE._serialized_start=26080 - _LISTPAYSRESPONSE._serialized_end=26131 - _LISTPAYSPAYS._serialized_start=26134 - _LISTPAYSPAYS._serialized_end=26643 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=26468 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=26527 - _PINGREQUEST._serialized_start=26645 - _PINGREQUEST._serialized_end=26734 - _PINGRESPONSE._serialized_start=26736 - _PINGRESPONSE._serialized_end=26766 - _SIGNMESSAGEREQUEST._serialized_start=26768 - _SIGNMESSAGEREQUEST._serialized_end=26805 - _SIGNMESSAGERESPONSE._serialized_start=26807 - _SIGNMESSAGERESPONSE._serialized_end=26877 - _NODE._serialized_start=26880 - _NODE._serialized_end=29693 + _FUNDCHANNELREQUEST._serialized_start=24464 + _FUNDCHANNELREQUEST._serialized_end=24866 + _FUNDCHANNELRESPONSE._serialized_start=24868 + _FUNDCHANNELRESPONSE._serialized_end=24987 + _GETROUTEREQUEST._serialized_start=24990 + _GETROUTEREQUEST._serialized_end=25226 + _GETROUTERESPONSE._serialized_start=25228 + _GETROUTERESPONSE._serialized_end=25281 + _GETROUTEROUTE._serialized_start=25284 + _GETROUTEROUTE._serialized_end=25481 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25452 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25481 + _LISTFORWARDSREQUEST._serialized_start=25484 + _LISTFORWARDSREQUEST._serialized_end=25742 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25624 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25700 + _LISTFORWARDSRESPONSE._serialized_start=25744 + _LISTFORWARDSRESPONSE._serialized_end=25811 + _LISTFORWARDSFORWARDS._serialized_start=25814 + _LISTFORWARDSFORWARDS._serialized_end=26382 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26179 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26263 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26265 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26313 + _LISTPAYSREQUEST._serialized_start=26385 + _LISTPAYSREQUEST._serialized_end=26604 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26510 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26565 + _LISTPAYSRESPONSE._serialized_start=26606 + _LISTPAYSRESPONSE._serialized_end=26657 + _LISTPAYSPAYS._serialized_start=26660 + _LISTPAYSPAYS._serialized_end=27169 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=26994 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27053 + _PINGREQUEST._serialized_start=27171 + _PINGREQUEST._serialized_end=27260 + _PINGRESPONSE._serialized_start=27262 + _PINGRESPONSE._serialized_end=27292 + _SIGNMESSAGEREQUEST._serialized_start=27294 + _SIGNMESSAGEREQUEST._serialized_end=27331 + _SIGNMESSAGERESPONSE._serialized_start=27333 + _SIGNMESSAGERESPONSE._serialized_end=27403 + _NODE._serialized_start=27406 + _NODE._serialized_end=30287 # @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py index b3c3646926d9..8acee98761d3 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py @@ -209,6 +209,11 @@ def __init__(self, channel): request_serializer=node__pb2.FeeratesRequest.SerializeToString, response_deserializer=node__pb2.FeeratesResponse.FromString, ) + self.FundChannel = channel.unary_unary( + '/cln.Node/FundChannel', + request_serializer=node__pb2.FundchannelRequest.SerializeToString, + response_deserializer=node__pb2.FundchannelResponse.FromString, + ) self.GetRoute = channel.unary_unary( '/cln.Node/GetRoute', request_serializer=node__pb2.GetrouteRequest.SerializeToString, @@ -473,6 +478,12 @@ def Feerates(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def FundChannel(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def GetRoute(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -701,6 +712,11 @@ def add_NodeServicer_to_server(servicer, server): request_deserializer=node__pb2.FeeratesRequest.FromString, response_serializer=node__pb2.FeeratesResponse.SerializeToString, ), + 'FundChannel': grpc.unary_unary_rpc_method_handler( + servicer.FundChannel, + request_deserializer=node__pb2.FundchannelRequest.FromString, + response_serializer=node__pb2.FundchannelResponse.SerializeToString, + ), 'GetRoute': grpc.unary_unary_rpc_method_handler( servicer.GetRoute, request_deserializer=node__pb2.GetrouteRequest.FromString, @@ -1399,6 +1415,23 @@ def Feerates(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def FundChannel(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/FundChannel', + node__pb2.FundchannelRequest.SerializeToString, + node__pb2.FundchannelResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def GetRoute(request, target, diff --git a/doc/schemas/fundchannel.request.json b/doc/schemas/fundchannel.request.json new file mode 100644 index 000000000000..7d0e0de17059 --- /dev/null +++ b/doc/schemas/fundchannel.request.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "amount" + ], + "properties": { + "id": { + "type": "pubkey", + "description": "id is the peer id obtained from connect." + }, + "amount": { + "type": "msat_or_all" + }, + "feerate": { + "type": "feerate" + }, + "announce": { + "type": "boolean" + }, + "minconf": { + "type": "number" + }, + "push_msat": { + "type": "msat" + }, + "close_to": { + "type": "string" + }, + "request_amt": { + "type": "msat" + }, + "compact_lease": { + "type": "string" + }, + "utxos": { + "type": "array", + "items": { + "type": "outpoint" + } + } + } +} From 12275d0bfe0e0745aa5c3d9507f534cb0e2e39a2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 30 Jun 2022 13:44:37 +0200 Subject: [PATCH 1070/1530] cln-grpc: Skip serializing fields when Option> is empty too The CLN API is rather strict about the fact that we should skip providing a field whenever it is empty. Checking for `is_none` would still include empty arrays. Changelog-Fixed cln-rpc: Optional empty arrays will no longer be serialized in requests --- cln-rpc/src/lib.rs | 10 ++++++++ cln-rpc/src/model.rs | 42 +++++++++++++++---------------- contrib/msggen/msggen/gen/rust.py | 2 +- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs index f2c34ba23bcc..d53ad0cad39b 100644 --- a/cln-rpc/src/lib.rs +++ b/cln-rpc/src/lib.rs @@ -106,6 +106,16 @@ impl ClnRpc { } } +/// Used to skip optional arrays when serializing requests. +fn is_none_or_empty(f: &Option>) -> bool +where + T: Clone, +{ + // TODO Find a better way to check, possibly without cloning + let f = f.clone(); + f.is_none() || f.unwrap().is_empty() +} + #[cfg(test)] mod test { use super::*; diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 61a759592e3b..15b1b02d83a0 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -220,7 +220,7 @@ pub mod requests { pub wrong_funding: Option, #[serde(alias = "force_lease_closed", skip_serializing_if = "Option::is_none")] pub force_lease_closed: Option, - #[serde(alias = "feerange", skip_serializing_if = "Option::is_none")] + #[serde(alias = "feerange", skip_serializing_if = "crate::is_none_or_empty")] pub feerange: Option>, } @@ -361,7 +361,7 @@ pub mod requests { pub label: String, #[serde(alias = "expiry", skip_serializing_if = "Option::is_none")] pub expiry: Option, - #[serde(alias = "fallbacks", skip_serializing_if = "Option::is_none")] + #[serde(alias = "fallbacks", skip_serializing_if = "crate::is_none_or_empty")] pub fallbacks: Option>, #[serde(alias = "preimage", skip_serializing_if = "Option::is_none")] pub preimage: Option, @@ -375,7 +375,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListdatastoreRequest { - #[serde(alias = "key", skip_serializing_if = "Option::is_none")] + #[serde(alias = "key", skip_serializing_if = "crate::is_none_or_empty")] pub key: Option>, } @@ -409,7 +409,7 @@ pub mod requests { pub payment_hash: Sha256, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, - #[serde(alias = "shared_secrets", skip_serializing_if = "Option::is_none")] + #[serde(alias = "shared_secrets", skip_serializing_if = "crate::is_none_or_empty")] pub shared_secrets: Option>, #[serde(alias = "partid", skip_serializing_if = "Option::is_none")] pub partid: Option, @@ -480,7 +480,7 @@ pub mod requests { pub exemptfee: Option, #[serde(alias = "localofferid", skip_serializing_if = "Option::is_none")] pub localofferid: Option, - #[serde(alias = "exclude", skip_serializing_if = "Option::is_none")] + #[serde(alias = "exclude", skip_serializing_if = "crate::is_none_or_empty")] pub exclude: Option>, #[serde(alias = "maxfee", skip_serializing_if = "Option::is_none")] pub maxfee: Option, @@ -557,7 +557,7 @@ pub mod requests { pub feerate: Option, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] pub minconf: Option, - #[serde(alias = "utxos", skip_serializing_if = "Option::is_none")] + #[serde(alias = "utxos", skip_serializing_if = "crate::is_none_or_empty")] pub utxos: Option>, } @@ -617,7 +617,7 @@ pub mod requests { pub struct SignpsbtRequest { #[serde(alias = "psbt")] pub psbt: String, - #[serde(alias = "signonly", skip_serializing_if = "Option::is_none")] + #[serde(alias = "signonly", skip_serializing_if = "crate::is_none_or_empty")] pub signonly: Option>, } @@ -657,7 +657,7 @@ pub mod requests { pub feerate: Option, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] pub minconf: Option, - #[serde(alias = "utxos", skip_serializing_if = "Option::is_none")] + #[serde(alias = "utxos", skip_serializing_if = "crate::is_none_or_empty")] pub utxos: Option>, } @@ -720,7 +720,7 @@ pub mod requests { pub request_amt: Option, #[serde(alias = "compact_lease", skip_serializing_if = "Option::is_none")] pub compact_lease: Option, - #[serde(alias = "utxos", skip_serializing_if = "Option::is_none")] + #[serde(alias = "utxos", skip_serializing_if = "crate::is_none_or_empty")] pub utxos: Option>, } @@ -738,7 +738,7 @@ pub mod requests { pub fromid: Option, #[serde(alias = "fuzzpercent", skip_serializing_if = "Option::is_none")] pub fuzzpercent: Option, - #[serde(alias = "exclude", skip_serializing_if = "Option::is_none")] + #[serde(alias = "exclude", skip_serializing_if = "crate::is_none_or_empty")] pub exclude: Option>, #[serde(alias = "maxhops", skip_serializing_if = "Option::is_none")] pub maxhops: Option, @@ -955,9 +955,9 @@ pub mod responses { pub network: String, #[serde(alias = "fees_collected_msat")] pub fees_collected_msat: Amount, - #[serde(alias = "address", skip_serializing_if = "Option::is_none")] + #[serde(alias = "address", skip_serializing_if = "crate::is_none_or_empty")] pub address: Option>, - #[serde(alias = "binding", skip_serializing_if = "Option::is_none")] + #[serde(alias = "binding", skip_serializing_if = "crate::is_none_or_empty")] pub binding: Option>, #[serde(alias = "warning_bitcoind_sync", skip_serializing_if = "Option::is_none")] pub warning_bitcoind_sync: Option, @@ -1185,7 +1185,7 @@ pub mod responses { pub next_feerate: Option, #[serde(alias = "next_fee_step", skip_serializing_if = "Option::is_none")] pub next_fee_step: Option, - #[serde(alias = "inflight", skip_serializing_if = "Option::is_none")] + #[serde(alias = "inflight", skip_serializing_if = "crate::is_none_or_empty")] pub inflight: Option>, #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] pub close_to: Option, @@ -1234,9 +1234,9 @@ pub mod responses { pub our_to_self_delay: Option, #[serde(alias = "max_accepted_htlcs", skip_serializing_if = "Option::is_none")] pub max_accepted_htlcs: Option, - #[serde(alias = "state_changes", skip_serializing_if = "Option::is_none")] + #[serde(alias = "state_changes", skip_serializing_if = "crate::is_none_or_empty")] pub state_changes: Option>, - #[serde(alias = "status", skip_serializing_if = "Option::is_none")] + #[serde(alias = "status", skip_serializing_if = "crate::is_none_or_empty")] pub status: Option>, #[serde(alias = "in_payments_offered", skip_serializing_if = "Option::is_none")] pub in_payments_offered: Option, @@ -1254,7 +1254,7 @@ pub mod responses { pub out_payments_fulfilled: Option, #[serde(alias = "out_fulfilled_msat", skip_serializing_if = "Option::is_none")] pub out_fulfilled_msat: Option, - #[serde(alias = "htlcs", skip_serializing_if = "Option::is_none")] + #[serde(alias = "htlcs", skip_serializing_if = "crate::is_none_or_empty")] pub htlcs: Option>, #[serde(alias = "close_to_addr", skip_serializing_if = "Option::is_none")] pub close_to_addr: Option, @@ -1266,11 +1266,11 @@ pub mod responses { pub id: Pubkey, #[serde(alias = "connected")] pub connected: bool, - #[serde(alias = "log", skip_serializing_if = "Option::is_none")] + #[serde(alias = "log", skip_serializing_if = "crate::is_none_or_empty")] pub log: Option>, #[serde(alias = "channels")] pub channels: Vec, - #[serde(alias = "netaddr", skip_serializing_if = "Option::is_none")] + #[serde(alias = "netaddr", skip_serializing_if = "crate::is_none_or_empty")] pub netaddr: Option>, #[serde(alias = "remote_addr", skip_serializing_if = "Option::is_none")] pub remote_addr: Option, @@ -2172,7 +2172,7 @@ pub mod responses { pub color: Option, #[serde(alias = "features", skip_serializing_if = "Option::is_none")] pub features: Option, - #[serde(alias = "addresses", skip_serializing_if = "Option::is_none")] + #[serde(alias = "addresses", skip_serializing_if = "crate::is_none_or_empty")] pub addresses: Option>, } @@ -2408,7 +2408,7 @@ pub mod responses { pub excess_msat: Amount, #[serde(alias = "change_outnum", skip_serializing_if = "Option::is_none")] pub change_outnum: Option, - #[serde(alias = "reservations", skip_serializing_if = "Option::is_none")] + #[serde(alias = "reservations", skip_serializing_if = "crate::is_none_or_empty")] pub reservations: Option>, } @@ -2452,7 +2452,7 @@ pub mod responses { pub excess_msat: Amount, #[serde(alias = "change_outnum", skip_serializing_if = "Option::is_none")] pub change_outnum: Option, - #[serde(alias = "reservations", skip_serializing_if = "Option::is_none")] + #[serde(alias = "reservations", skip_serializing_if = "crate::is_none_or_empty")] pub reservations: Option>, } diff --git a/contrib/msggen/msggen/gen/rust.py b/contrib/msggen/msggen/gen/rust.py index 2fbe3278fdf1..7eac8ecf669a 100644 --- a/contrib/msggen/msggen/gen/rust.py +++ b/contrib/msggen/msggen/gen/rust.py @@ -176,7 +176,7 @@ def gen_array(a): if a.required: defi = f" #[serde(alias = \"{alias}\")]\n pub {name}: {'Vec<'*a.dims}{itemtype}{'>'*a.dims},\n" else: - defi = f" #[serde(alias = \"{alias}\", skip_serializing_if = \"Option::is_none\")]\n pub {name}: Option<{'Vec<'*a.dims}{itemtype}{'>'*a.dims}>,\n" + defi = f" #[serde(alias = \"{alias}\", skip_serializing_if = \"crate::is_none_or_empty\")]\n pub {name}: Option<{'Vec<'*a.dims}{itemtype}{'>'*a.dims}>,\n" return (defi, decl) From 1efa5c37be5e7bfc806eaf48e49ba0056eaa2e62 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 30 Jun 2022 17:24:35 +0200 Subject: [PATCH 1071/1530] cln-plugin: Notify waiting tasks if the lightningd connection closes This is usually a signal that lightningd is shutting down, so notify any instance that is waiting on `plugin.join()`. Changelog-Fixed: cln-plugin: Fixed an issue where plugins would hang indefinitely despite `lightningd` closing the connection --- plugins/grpc-plugin/src/main.rs | 19 ++++++++++------ plugins/src/lib.rs | 39 +++++++++++++++++++-------------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index a83f0131dad6..70dffa5d336f 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -14,7 +14,7 @@ struct PluginState { ca_cert: Vec, } -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { debug!("Starting grpc plugin"); let path = Path::new("lightning-rpc"); @@ -58,13 +58,18 @@ async fn main() -> Result<()> { let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); - tokio::spawn(async move { - if let Err(e) = run_interface(bind_addr, state).await { - warn!("Error running the grpc interface: {}", e); + tokio::select! { + _ = plugin.join() => { + // This will likely never be shown, if we got here our + // parent process is exiting and not processing out log + // messages anymore. + debug!("Plugin loop terminated") } - }); - - plugin.join().await + e = run_interface(bind_addr, state) => { + warn!("Error running grpc interface: {:?}", e) + } + } + Ok(()) } async fn run_interface(bind_addr: SocketAddr, state: PluginState) -> Result<()> { diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 53720790c7fd..1da5f379c21c 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -427,13 +427,19 @@ where )) .await .context("sending init response")?; + + let joiner = plugin.wait_handle.clone(); // Start the PluginDriver to handle plugin IO - tokio::spawn( - driver.run(receiver, input, output), - // TODO Use the broadcast to distribute any error that we - // might receive here to anyone listening. (Shutdown - // signal) - ); + tokio::spawn(async move { + if let Err(e) = driver.run(receiver, input, output).await { + log::warn!("Plugin loop returned error {:?}", e); + } + + // Now that we have left the reader loop its time to + // notify any waiting tasks. This most likely will cause + // the main task to exit and the plugin to terminate. + joiner.send(()) + }); Ok(plugin) } @@ -502,16 +508,17 @@ where // the user-code, which may require some cleanups or // similar. tokio::select! { - e = self.dispatch_one(&mut input, &self.plugin) => { - //Hand any error up. - e?; - }, - v = receiver.recv() => { - output.lock().await.send( - v.context("internal communication error")? - ).await?; - }, - } + e = self.dispatch_one(&mut input, &self.plugin) => { + if let Err(e) = e { + return Err(e) + } + }, + v = receiver.recv() => { + output.lock().await.send( + v.context("internal communication error")? + ).await?; + }, + } } } From 18a9eb2feb79502b93cfa9d219e5a8c552506ebe Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Jul 2022 13:51:18 +0200 Subject: [PATCH 1072/1530] msggen: Add `stop` method to generators We'll need this when testing the grpc interface in pyln-testing, otherwise tests just slowly die and wither. --- cln-grpc/proto/node.proto | 7 ++++ cln-grpc/src/convert.rs | 16 +++++++++ cln-grpc/src/server.rs | 32 ++++++++++++++++++ cln-rpc/src/model.rs | 10 ++++++ contrib/msggen/msggen/utils/utils.py | 2 +- contrib/pyln-testing/pyln/testing/grpc2py.py | 5 +++ contrib/pyln-testing/pyln/testing/node_pb2.py | 26 +++++++++++++-- .../pyln/testing/node_pb2_grpc.py | 33 +++++++++++++++++++ doc/schemas/stop.request.json | 7 ++++ 9 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 doc/schemas/stop.request.json diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 4235e5ef7db7..2208fe074e7d 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -53,6 +53,7 @@ service Node { rpc ListPays(ListpaysRequest) returns (ListpaysResponse) {} rpc Ping(PingRequest) returns (PingResponse) {} rpc SignMessage(SignmessageRequest) returns (SignmessageResponse) {} + rpc Stop(StopRequest) returns (StopResponse) {} } message GetinfoRequest { @@ -1284,3 +1285,9 @@ message SignmessageResponse { bytes recid = 2; string zbase = 3; } + +message StopRequest { +} + +message StopResponse { +} diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index f062809ab095..4cbbe960b66f 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -963,6 +963,14 @@ impl From<&responses::SignmessageResponse> for pb::SignmessageResponse { } } +#[allow(unused_variables)] +impl From<&responses::StopResponse> for pb::StopResponse { + fn from(c: &responses::StopResponse) -> Self { + Self { + } + } +} + #[allow(unused_variables)] impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { fn from(c: &pb::GetinfoRequest) -> Self { @@ -1513,3 +1521,11 @@ impl From<&pb::SignmessageRequest> for requests::SignmessageRequest { } } +#[allow(unused_variables)] +impl From<&pb::StopRequest> for requests::StopRequest { + fn from(c: &pb::StopRequest) -> Self { + Self { + } + } +} + diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index d7982d433797..16f75b5613df 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -1466,4 +1466,36 @@ async fn sign_message( } +async fn stop( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::StopRequest = (&req).into(); + debug!("Client asked for stop"); + trace!("stop request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::Stop(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method Stop: {:?}", e)))?; + match result { + Response::Stop(r) => { + trace!("stop response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call Stop", + r + ) + )), + } + +} + } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 15b1b02d83a0..b939e6944ded 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -61,6 +61,7 @@ pub enum Request { ListPays(requests::ListpaysRequest), Ping(requests::PingRequest), SignMessage(requests::SignmessageRequest), + Stop(requests::StopRequest), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -112,6 +113,7 @@ pub enum Response { ListPays(responses::ListpaysResponse), Ping(responses::PingResponse), SignMessage(responses::SignmessageResponse), + Stop(responses::StopResponse), } pub mod requests { @@ -825,6 +827,10 @@ pub mod requests { pub message: String, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct StopRequest { + } + } @@ -2745,5 +2751,9 @@ pub mod responses { pub zbase: String, } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct StopResponse { + } + } diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py index 18224d68458a..bc13d4c165b6 100644 --- a/contrib/msggen/msggen/utils/utils.py +++ b/contrib/msggen/msggen/utils/utils.py @@ -112,7 +112,7 @@ def load_jsonrpc_service(schema_dir: str = None): # "waitblockheight", # "ListConfigs", # "check", # No point in mapping this one - # "Stop", # Breaks a core assumption (root is an object) can't map unless we change this + "Stop", # "notifications", # No point in mapping this # "help", ] diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index c0daf26642f4..396a83389dc2 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -850,3 +850,8 @@ def signmessage2py(m): "recid": hexlify(m.recid), # PrimitiveField in generate_composite "zbase": m.zbase, # PrimitiveField in generate_composite }) + + +def stop2py(m): + return remove_default({ + }) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 68a189fdd032..94ccd8474c14 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\x92\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\x01H\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"w\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x42\x0b\n\t_close_to\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t2\xc1\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\x92\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"w\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x42\x0b\n\t_close_to\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -143,6 +143,8 @@ _PINGRESPONSE = DESCRIPTOR.message_types_by_name['PingResponse'] _SIGNMESSAGEREQUEST = DESCRIPTOR.message_types_by_name['SignmessageRequest'] _SIGNMESSAGERESPONSE = DESCRIPTOR.message_types_by_name['SignmessageResponse'] +_STOPREQUEST = DESCRIPTOR.message_types_by_name['StopRequest'] +_STOPRESPONSE = DESCRIPTOR.message_types_by_name['StopResponse'] _GETINFOADDRESS_GETINFOADDRESSTYPE = _GETINFOADDRESS.enum_types_by_name['GetinfoAddressType'] _GETINFOBINDING_GETINFOBINDINGTYPE = _GETINFOBINDING.enum_types_by_name['GetinfoBindingType'] _LISTPEERSPEERSLOG_LISTPEERSPEERSLOGTYPE = _LISTPEERSPEERSLOG.enum_types_by_name['ListpeersPeersLogType'] @@ -1045,6 +1047,20 @@ }) _sym_db.RegisterMessage(SignmessageResponse) +StopRequest = _reflection.GeneratedProtocolMessageType('StopRequest', (_message.Message,), { + 'DESCRIPTOR' : _STOPREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.StopRequest) + }) +_sym_db.RegisterMessage(StopRequest) + +StopResponse = _reflection.GeneratedProtocolMessageType('StopResponse', (_message.Message,), { + 'DESCRIPTOR' : _STOPRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.StopResponse) + }) +_sym_db.RegisterMessage(StopResponse) + _NODE = DESCRIPTOR.services_by_name['Node'] if _descriptor._USE_C_DESCRIPTORS == False: @@ -1365,6 +1381,10 @@ _SIGNMESSAGEREQUEST._serialized_end=27331 _SIGNMESSAGERESPONSE._serialized_start=27333 _SIGNMESSAGERESPONSE._serialized_end=27403 - _NODE._serialized_start=27406 - _NODE._serialized_end=30287 + _STOPREQUEST._serialized_start=27405 + _STOPREQUEST._serialized_end=27418 + _STOPRESPONSE._serialized_start=27420 + _STOPRESPONSE._serialized_end=27434 + _NODE._serialized_start=27437 + _NODE._serialized_end=30365 # @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py index 8acee98761d3..f9a0e10955c8 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py @@ -239,6 +239,11 @@ def __init__(self, channel): request_serializer=node__pb2.SignmessageRequest.SerializeToString, response_deserializer=node__pb2.SignmessageResponse.FromString, ) + self.Stop = channel.unary_unary( + '/cln.Node/Stop', + request_serializer=node__pb2.StopRequest.SerializeToString, + response_deserializer=node__pb2.StopResponse.FromString, + ) class NodeServicer(object): @@ -514,6 +519,12 @@ def SignMessage(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def Stop(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_NodeServicer_to_server(servicer, server): rpc_method_handlers = { @@ -742,6 +753,11 @@ def add_NodeServicer_to_server(servicer, server): request_deserializer=node__pb2.SignmessageRequest.FromString, response_serializer=node__pb2.SignmessageResponse.SerializeToString, ), + 'Stop': grpc.unary_unary_rpc_method_handler( + servicer.Stop, + request_deserializer=node__pb2.StopRequest.FromString, + response_serializer=node__pb2.StopResponse.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'cln.Node', rpc_method_handlers) @@ -1516,3 +1532,20 @@ def SignMessage(request, node__pb2.SignmessageResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Stop(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/Stop', + node__pb2.StopRequest.SerializeToString, + node__pb2.StopResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/doc/schemas/stop.request.json b/doc/schemas/stop.request.json new file mode 100644 index 000000000000..65571ad4c703 --- /dev/null +++ b/doc/schemas/stop.request.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": {} +} From ca8c46c286b03d17849051d06ed2f5bc7a5bbfad Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Jul 2022 13:52:25 +0200 Subject: [PATCH 1073/1530] schema: `minconf` should be an integer (u32) not a float (number) --- cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 2 +- doc/schemas/fundchannel.request.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 2208fe074e7d..26ee36a674ba 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1145,7 +1145,7 @@ message FundchannelRequest { AmountOrAll amount = 1; optional Feerate feerate = 2; optional bool announce = 3; - optional double minconf = 10; + optional uint32 minconf = 10; optional Amount push_msat = 5; optional string close_to = 6; optional Amount request_amt = 7; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 4cbbe960b66f..0c120b0bb9bc 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1453,7 +1453,7 @@ impl From<&pb::FundchannelRequest> for requests::FundchannelRequest { amount: c.amount.as_ref().unwrap().into(), // Rule #1 for type msat_or_all feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? announce: c.announce.clone(), // Rule #1 for type boolean? - minconf: c.minconf.clone(), // Rule #1 for type number? + minconf: c.minconf.clone(), // Rule #1 for type u32? push_msat: c.push_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? close_to: c.close_to.clone(), // Rule #1 for type string? request_amt: c.request_amt.as_ref().map(|a| a.into()), // Rule #1 for type msat? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index b939e6944ded..7c81d395f49c 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -713,7 +713,7 @@ pub mod requests { #[serde(alias = "announce", skip_serializing_if = "Option::is_none")] pub announce: Option, #[serde(alias = "minconf", skip_serializing_if = "Option::is_none")] - pub minconf: Option, + pub minconf: Option, #[serde(alias = "push_msat", skip_serializing_if = "Option::is_none")] pub push_msat: Option, #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] diff --git a/doc/schemas/fundchannel.request.json b/doc/schemas/fundchannel.request.json index 7d0e0de17059..0023124882e6 100644 --- a/doc/schemas/fundchannel.request.json +++ b/doc/schemas/fundchannel.request.json @@ -21,7 +21,7 @@ "type": "boolean" }, "minconf": { - "type": "number" + "type": "u32" }, "push_msat": { "type": "msat" From 6df0a9281f7386e4e25167221f72a6c15237e64c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 1 Jul 2022 13:52:55 +0200 Subject: [PATCH 1074/1530] pyln-testing: Add a couple of methods used in tests These are the most used methods in tests, so we can start getting our test coverage up. --- contrib/pyln-testing/pyln/testing/grpc.py | 164 ++++++++++++++++++++-- 1 file changed, 151 insertions(+), 13 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/grpc.py b/contrib/pyln-testing/pyln/testing/grpc.py index 593f1ee09148..4a65bdfa1b2b 100644 --- a/contrib/pyln-testing/pyln/testing/grpc.py +++ b/contrib/pyln-testing/pyln/testing/grpc.py @@ -1,13 +1,15 @@ """A drop-in replacement for the JSON-RPC LightningRpc """ -from pyln.testing import node_pb2_grpc as pbgrpc -from pyln.testing import node_pb2 as pb +import logging +from binascii import unhexlify +from typing import List, Optional, Tuple + import grpc -import json -from google.protobuf.json_format import MessageToJson from pyln.testing import grpc2py - +from pyln.testing import node_pb2 as pb +from pyln.testing import node_pb2_grpc as pbgrpc +from pyln.testing import primitives_pb2 as primpb DUMMY_CA_PEM = b"""-----BEGIN CERTIFICATE----- MIIBcTCCARigAwIBAgIJAJhah1bqO05cMAoGCCqGSM49BAMCMBYxFDASBgNVBAMM @@ -46,6 +48,26 @@ -----END CERTIFICATE-----""" +def int2msat(amount: int) -> primpb.Amount: + return primpb.Amount(msat=amount) + + +def int2amount_or_all(amount: Tuple[int, str]) -> primpb.AmountOrAll: + if amount == "all": + return primpb.AmountOrAll(all=True) + else: + assert isinstance(amount, int) + return primpb.AmountOrAll(amount=int2msat(amount)) + + +def int2amount_or_any(amount: Tuple[int, str]) -> primpb.AmountOrAny: + if amount == "any": + return primpb.AmountOrAny(any=True) + else: + assert isinstance(amount, int) + return primpb.AmountOrAny(amount=int2msat(amount)) + + class LightningGrpc(object): def __init__( self, @@ -55,11 +77,13 @@ def __init__( private_key: bytes = DUMMY_CLIENT_KEY_PEM, certificate_chain: bytes = DUMMY_CLIENT_PEM, ): + self.logger = logging.getLogger("LightningGrpc") self.credentials = grpc.ssl_channel_credentials( root_certificates=root_certificates, private_key=private_key, certificate_chain=certificate_chain, ) + self.logger.debug(f"Connecting to grpc interface at {host}:{port}") self.channel = grpc.secure_channel( f"{host}:{port}", self.credentials, @@ -68,17 +92,131 @@ def __init__( self.stub = pbgrpc.NodeStub(self.channel) def getinfo(self): - return grpc2py.getinfo2py( - self.stub.Getinfo(pb.GetinfoRequest()) - ) + return grpc2py.getinfo2py(self.stub.Getinfo(pb.GetinfoRequest())) def connect(self, peer_id, host=None, port=None): """ Connect to {peer_id} at {host} and {port}. """ - payload = pb.ConnectRequest( - id=peer_id, - host=host, - port=port - ) + payload = pb.ConnectRequest(id=peer_id, host=host, port=port) return grpc2py.connect2py(self.stub.ConnectPeer(payload)) + + def listpeers(self, peerid=None, level=None): + payload = pb.ListpeersRequest( + id=unhexlify(peerid) if peerid is not None else None, + level=level, + ) + return grpc2py.listpeers2py(self.stub.ListPeers(payload)) + + def getpeer(self, peer_id, level=None): + """ + Show peer with {peer_id}, if {level} is set, include {log}s. + """ + res = self.listpeers(peer_id, level) + return res.get("peers") and res["peers"][0] or None + + def newaddr(self, addresstype=None): + """Get a new address of type {addresstype} of the internal wallet.""" + enum = { + None: 0, + "BECH32": 0, + "P2SH_SEGWIT": 1, + "P2SH-SEGWIT": 1, + "ALL": 2 + } + if addresstype is not None: + addresstype = addresstype.upper() + atype = enum.get(addresstype, None) + if atype is None: + raise ValueError( + f"Unknown addresstype {addresstype}, known values are {enum.values()}" + ) + + payload = pb.NewaddrRequest(addresstype=atype) + res = grpc2py.newaddr2py(self.stub.NewAddr(payload)) + + # Need to remap the bloody spelling of p2sh-segwit to match + # addresstype. + if 'p2sh_segwit' in res: + res['p2sh-segwit'] = res['p2sh_segwit'] + del res['p2sh_segwit'] + return res + + def listfunds(self, spent=None): + payload = pb.ListfundsRequest(spent=spent) + return grpc2py.listfunds2py(self.stub.ListFunds(payload)) + + def fundchannel( + self, + node_id: str, + amount: int, + # TODO map the following arguments + # feerate=None, + announce: Optional[bool] = True, + minconf: Optional[int] = None, + # utxos=None, + # push_msat=None, + close_to: Optional[str] = None, + # request_amt=None, + compact_lease: Optional[str] = None, + ): + payload = pb.FundchannelRequest( + id=unhexlify(node_id), + amount=int2amount_or_all(amount * 1000), # This is satoshis after all + # TODO Parse and insert `feerate` + announce=announce, + utxos=None, + minconf=minconf, + close_to=close_to, + compact_lease=compact_lease, + ) + return grpc2py.fundchannel2py(self.stub.FundChannel(payload)) + + def listchannels(self, short_channel_id=None, source=None, destination=None): + payload = pb.ListchannelsRequest( + short_channel_id=short_channel_id, + source=unhexlify(source) if source else None, + destination=unhexlify(destination) if destination else None, + ) + return grpc2py.listchannels2py(self.stub.ListChannels(payload)) + + def pay( + self, + bolt11: str, + amount_msat: Optional[int] = None, + label: Optional[str] = None, + riskfactor: Optional[float] = None, + maxfeepercent: Optional[float] = None, + retry_for: Optional[int] = None, + maxdelay: Optional[int] = None, + exemptfee: Optional[int] = None, + localofferid: Optional[str] = None, + # TODO map the following arguments + # exclude: Optional[List[str]] = None, + # maxfee=None, + description: Optional[str] = None, + msatoshi: Optional[int] = None, + ): + payload = pb.PayRequest( + bolt11=bolt11, + amount_msat=int2msat(amount_msat), + label=label, + riskfactor=riskfactor, + maxfeepercent=maxfeepercent, + retry_for=retry_for, + maxdelay=maxdelay, + exemptfee=exemptfee, + localofferid=localofferid, + # Needs conversion + # exclude=exclude, + # maxfee=maxfee + description=description, + ) + return grpc2py.pay2py(self.stub.Pay(payload)) + + def stop(self): + payload = pb.StopRequest() + try: + self.stub.Stop(payload) + except Exception: + pass From b6a4cbbf98728b6cd0508c11d91e9535c6d0423c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 8 Jul 2022 10:46:08 +0200 Subject: [PATCH 1075/1530] cln-rpc: Add mindepth after rebase on `master` --- .msggen.json | 1 + cln-grpc/proto/node.proto | 1 + cln-grpc/src/convert.rs | 1 + cln-rpc/src/model.rs | 2 + contrib/pyln-testing/pyln/testing/grpc2py.py | 8 + contrib/pyln-testing/pyln/testing/node_pb2.py | 588 +++++++++--------- 6 files changed, 312 insertions(+), 289 deletions(-) diff --git a/.msggen.json b/.msggen.json index 5a397d7fdb7f..9a2cd64157be 100644 --- a/.msggen.json +++ b/.msggen.json @@ -397,6 +397,7 @@ "FundchannelResponse": { "FundChannel.channel_id": 4, "FundChannel.close_to": 5, + "FundChannel.mindepth": 6, "FundChannel.outnum": 3, "FundChannel.tx": 1, "FundChannel.txid": 2 diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 26ee36a674ba..b45b614cc7a1 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1159,6 +1159,7 @@ message FundchannelResponse { uint32 outnum = 3; bytes channel_id = 4; optional bytes close_to = 5; + optional uint32 mindepth = 6; } message GetrouteRequest { diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 0c120b0bb9bc..6ca08f45d5da 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -862,6 +862,7 @@ impl From<&responses::FundchannelResponse> for pb::FundchannelResponse { outnum: c.outnum.clone(), // Rule #2 for type u32 channel_id: hex::decode(&c.channel_id).unwrap(), // Rule #2 for type hex close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + mindepth: c.mindepth.clone(), // Rule #2 for type u32? } } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 7c81d395f49c..08f730754ec6 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -2566,6 +2566,8 @@ pub mod responses { pub channel_id: String, #[serde(alias = "close_to", skip_serializing_if = "Option::is_none")] pub close_to: Option, + #[serde(alias = "mindepth", skip_serializing_if = "Option::is_none")] + pub mindepth: Option, } /// The features understood by the destination node diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index 396a83389dc2..dcde4d744841 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -105,6 +105,13 @@ def listpeers_peers_channels_funding2py(m): }) +def listpeers_peers_channels_alias2py(m): + return remove_default({ + "local": m.local, # PrimitiveField in generate_composite + "remote": m.remote, # PrimitiveField in generate_composite + }) + + def listpeers_peers_channels_state_changes2py(m): return remove_default({ "timestamp": m.timestamp, # PrimitiveField in generate_composite @@ -776,6 +783,7 @@ def fundchannel2py(m): "outnum": m.outnum, # PrimitiveField in generate_composite "channel_id": hexlify(m.channel_id), # PrimitiveField in generate_composite "close_to": hexlify(m.close_to), # PrimitiveField in generate_composite + "mindepth": m.mindepth, # PrimitiveField in generate_composite }) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 94ccd8474c14..b93c4eb0cb84 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\x92\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"w\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x42\x0b\n\t_close_to\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\x92\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -32,6 +32,7 @@ _LISTPEERSPEERSCHANNELSFEERATE = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsFeerate'] _LISTPEERSPEERSCHANNELSINFLIGHT = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsInflight'] _LISTPEERSPEERSCHANNELSFUNDING = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsFunding'] +_LISTPEERSPEERSCHANNELSALIAS = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsAlias'] _LISTPEERSPEERSCHANNELSHTLCS = DESCRIPTOR.message_types_by_name['ListpeersPeersChannelsHtlcs'] _LISTFUNDSREQUEST = DESCRIPTOR.message_types_by_name['ListfundsRequest'] _LISTFUNDSRESPONSE = DESCRIPTOR.message_types_by_name['ListfundsResponse'] @@ -270,6 +271,13 @@ }) _sym_db.RegisterMessage(ListpeersPeersChannelsFunding) +ListpeersPeersChannelsAlias = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsAlias', (_message.Message,), { + 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSALIAS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.ListpeersPeersChannelsAlias) + }) +_sym_db.RegisterMessage(ListpeersPeersChannelsAlias) + ListpeersPeersChannelsHtlcs = _reflection.GeneratedProtocolMessageType('ListpeersPeersChannelsHtlcs', (_message.Message,), { 'DESCRIPTOR' : _LISTPEERSPEERSCHANNELSHTLCS, '__module__' : 'node_pb2' @@ -1099,292 +1107,294 @@ _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_end=4937 _LISTPEERSPEERSCHANNELSFUNDING._serialized_start=4940 _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5072 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5075 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=5413 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5329 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=5384 - _LISTFUNDSREQUEST._serialized_start=5415 - _LISTFUNDSREQUEST._serialized_end=5463 - _LISTFUNDSRESPONSE._serialized_start=5465 - _LISTFUNDSRESPONSE._serialized_end=5566 - _LISTFUNDSOUTPUTS._serialized_start=5569 - _LISTFUNDSOUTPUTS._serialized_end=5942 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=5830 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=5897 - _LISTFUNDSCHANNELS._serialized_start=5945 - _LISTFUNDSCHANNELS._serialized_end=6204 - _SENDPAYREQUEST._serialized_start=6207 - _SENDPAYREQUEST._serialized_end=6554 - _SENDPAYRESPONSE._serialized_start=6557 - _SENDPAYRESPONSE._serialized_end=7106 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=6944 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=6986 - _SENDPAYROUTE._serialized_start=7108 - _SENDPAYROUTE._serialized_end=7200 - _LISTCHANNELSREQUEST._serialized_start=7203 - _LISTCHANNELSREQUEST._serialized_end=7350 - _LISTCHANNELSRESPONSE._serialized_start=7352 - _LISTCHANNELSRESPONSE._serialized_end=7419 - _LISTCHANNELSCHANNELS._serialized_start=7422 - _LISTCHANNELSCHANNELS._serialized_end=7838 - _ADDGOSSIPREQUEST._serialized_start=7840 - _ADDGOSSIPREQUEST._serialized_end=7875 - _ADDGOSSIPRESPONSE._serialized_start=7877 - _ADDGOSSIPRESPONSE._serialized_end=7896 - _AUTOCLEANINVOICEREQUEST._serialized_start=7898 - _AUTOCLEANINVOICEREQUEST._serialized_end=8009 - _AUTOCLEANINVOICERESPONSE._serialized_start=8012 - _AUTOCLEANINVOICERESPONSE._serialized_end=8141 - _CHECKMESSAGEREQUEST._serialized_start=8143 - _CHECKMESSAGEREQUEST._serialized_end=8228 - _CHECKMESSAGERESPONSE._serialized_start=8230 - _CHECKMESSAGERESPONSE._serialized_end=8302 - _CLOSEREQUEST._serialized_start=8305 - _CLOSEREQUEST._serialized_end=8621 - _CLOSERESPONSE._serialized_start=8624 - _CLOSERESPONSE._serialized_end=8795 - _CLOSERESPONSE_CLOSETYPE._serialized_start=8726 - _CLOSERESPONSE_CLOSETYPE._serialized_end=8779 - _CONNECTREQUEST._serialized_start=8797 - _CONNECTREQUEST._serialized_end=8881 - _CONNECTRESPONSE._serialized_start=8884 - _CONNECTRESPONSE._serialized_end=9026 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=8991 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9026 - _CONNECTADDRESS._serialized_start=9029 - _CONNECTADDRESS._serialized_end=9280 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9168 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9248 - _CREATEINVOICEREQUEST._serialized_start=9282 - _CREATEINVOICEREQUEST._serialized_end=9356 - _CREATEINVOICERESPONSE._serialized_start=9359 - _CREATEINVOICERESPONSE._serialized_end=9986 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=9786 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=9842 - _DATASTOREREQUEST._serialized_start=9989 - _DATASTOREREQUEST._serialized_end=10297 - _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10142 - _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10254 - _DATASTORERESPONSE._serialized_start=10300 - _DATASTORERESPONSE._serialized_end=10430 - _CREATEONIONREQUEST._serialized_start=10433 - _CREATEONIONREQUEST._serialized_end=10590 - _CREATEONIONRESPONSE._serialized_start=10592 - _CREATEONIONRESPONSE._serialized_end=10652 - _CREATEONIONHOPS._serialized_start=10654 - _CREATEONIONHOPS._serialized_end=10704 - _DELDATASTOREREQUEST._serialized_start=10706 - _DELDATASTOREREQUEST._serialized_end=10780 - _DELDATASTORERESPONSE._serialized_start=10783 - _DELDATASTORERESPONSE._serialized_end=10916 - _DELEXPIREDINVOICEREQUEST._serialized_start=10918 - _DELEXPIREDINVOICEREQUEST._serialized_end=10990 - _DELEXPIREDINVOICERESPONSE._serialized_start=10992 - _DELEXPIREDINVOICERESPONSE._serialized_end=11019 - _DELINVOICEREQUEST._serialized_start=11022 - _DELINVOICEREQUEST._serialized_end=11204 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11138 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11191 - _DELINVOICERESPONSE._serialized_start=11207 - _DELINVOICERESPONSE._serialized_end=11646 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11138 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11191 - _INVOICEREQUEST._serialized_start=11649 - _INVOICEREQUEST._serialized_end=11961 - _INVOICERESPONSE._serialized_start=11964 - _INVOICERESPONSE._serialized_end=12323 - _LISTDATASTOREREQUEST._serialized_start=12325 - _LISTDATASTOREREQUEST._serialized_end=12360 - _LISTDATASTORERESPONSE._serialized_start=12362 - _LISTDATASTORERESPONSE._serialized_end=12433 - _LISTDATASTOREDATASTORE._serialized_start=12436 - _LISTDATASTOREDATASTORE._serialized_end=12571 - _LISTINVOICESREQUEST._serialized_start=12574 - _LISTINVOICESREQUEST._serialized_end=12743 - _LISTINVOICESRESPONSE._serialized_start=12745 - _LISTINVOICESRESPONSE._serialized_end=12812 - _LISTINVOICESINVOICES._serialized_start=12815 - _LISTINVOICESINVOICES._serialized_end=13475 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13252 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13315 - _SENDONIONREQUEST._serialized_start=13478 - _SENDONIONREQUEST._serialized_end=13826 - _SENDONIONRESPONSE._serialized_start=13829 - _SENDONIONRESPONSE._serialized_end=14352 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14200 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14244 - _SENDONIONFIRST_HOP._serialized_start=14354 - _SENDONIONFIRST_HOP._serialized_end=14435 - _LISTSENDPAYSREQUEST._serialized_start=14438 - _LISTSENDPAYSREQUEST._serialized_end=14673 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14575 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14634 - _LISTSENDPAYSRESPONSE._serialized_start=14675 - _LISTSENDPAYSRESPONSE._serialized_end=14742 - _LISTSENDPAYSPAYMENTS._serialized_start=14745 - _LISTSENDPAYSPAYMENTS._serialized_end=15358 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15163 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15230 - _LISTTRANSACTIONSREQUEST._serialized_start=15360 - _LISTTRANSACTIONSREQUEST._serialized_end=15385 - _LISTTRANSACTIONSRESPONSE._serialized_start=15387 - _LISTTRANSACTIONSRESPONSE._serialized_end=15470 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15473 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=15755 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=15758 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16274 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=15970 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16248 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16277 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=16821 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16516 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=16795 - _PAYREQUEST._serialized_start=16824 - _PAYREQUEST._serialized_end=17296 - _PAYRESPONSE._serialized_start=17299 - _PAYRESPONSE._serialized_end=17678 - _PAYRESPONSE_PAYSTATUS._serialized_start=17581 - _PAYRESPONSE_PAYSTATUS._serialized_end=17631 - _LISTNODESREQUEST._serialized_start=17680 - _LISTNODESREQUEST._serialized_end=17722 - _LISTNODESRESPONSE._serialized_start=17724 - _LISTNODESRESPONSE._serialized_end=17779 - _LISTNODESNODES._serialized_start=17782 - _LISTNODESNODES._serialized_end=18007 - _LISTNODESNODESADDRESSES._serialized_start=18010 - _LISTNODESNODESADDRESSES._serialized_end=18257 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18150 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18245 - _WAITANYINVOICEREQUEST._serialized_start=18259 - _WAITANYINVOICEREQUEST._serialized_end=18362 - _WAITANYINVOICERESPONSE._serialized_start=18365 - _WAITANYINVOICERESPONSE._serialized_end=18896 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=18741 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=18786 - _WAITINVOICEREQUEST._serialized_start=18898 - _WAITINVOICEREQUEST._serialized_end=18933 - _WAITINVOICERESPONSE._serialized_start=18936 - _WAITINVOICERESPONSE._serialized_end=19455 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19303 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19345 - _WAITSENDPAYREQUEST._serialized_start=19458 - _WAITSENDPAYREQUEST._serialized_end=19600 - _WAITSENDPAYRESPONSE._serialized_start=19603 - _WAITSENDPAYRESPONSE._serialized_end=20121 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=19980 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20013 - _NEWADDRREQUEST._serialized_start=20124 - _NEWADDRREQUEST._serialized_end=20282 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20208 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20266 - _NEWADDRRESPONSE._serialized_start=20284 - _NEWADDRRESPONSE._serialized_end=20375 - _WITHDRAWREQUEST._serialized_start=20378 - _WITHDRAWREQUEST._serialized_end=20580 - _WITHDRAWRESPONSE._serialized_start=20582 - _WITHDRAWRESPONSE._serialized_end=20640 - _KEYSENDREQUEST._serialized_start=20643 - _KEYSENDREQUEST._serialized_end=20975 - _KEYSENDRESPONSE._serialized_start=20978 - _KEYSENDRESPONSE._serialized_end=21348 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21272 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21301 - _KEYSENDEXTRATLVS._serialized_start=21350 - _KEYSENDEXTRATLVS._serialized_end=21368 - _FUNDPSBTREQUEST._serialized_start=21371 - _FUNDPSBTREQUEST._serialized_end=21682 - _FUNDPSBTRESPONSE._serialized_start=21685 - _FUNDPSBTRESPONSE._serialized_end=21902 - _FUNDPSBTRESERVATIONS._serialized_start=21904 - _FUNDPSBTRESERVATIONS._serialized_end=22021 - _SENDPSBTREQUEST._serialized_start=22023 - _SENDPSBTREQUEST._serialized_end=22088 - _SENDPSBTRESPONSE._serialized_start=22090 - _SENDPSBTRESPONSE._serialized_end=22134 - _SIGNPSBTREQUEST._serialized_start=22136 - _SIGNPSBTREQUEST._serialized_end=22185 - _SIGNPSBTRESPONSE._serialized_start=22187 - _SIGNPSBTRESPONSE._serialized_end=22226 - _UTXOPSBTREQUEST._serialized_start=22229 - _UTXOPSBTREQUEST._serialized_end=22576 - _UTXOPSBTRESPONSE._serialized_start=22579 - _UTXOPSBTRESPONSE._serialized_end=22796 - _UTXOPSBTRESERVATIONS._serialized_start=22798 - _UTXOPSBTRESERVATIONS._serialized_end=22915 - _TXDISCARDREQUEST._serialized_start=22917 - _TXDISCARDREQUEST._serialized_end=22949 - _TXDISCARDRESPONSE._serialized_start=22951 - _TXDISCARDRESPONSE._serialized_end=23005 - _TXPREPAREREQUEST._serialized_start=23008 - _TXPREPAREREQUEST._serialized_end=23172 - _TXPREPARERESPONSE._serialized_start=23174 - _TXPREPARERESPONSE._serialized_end=23242 - _TXSENDREQUEST._serialized_start=23244 - _TXSENDREQUEST._serialized_end=23273 - _TXSENDRESPONSE._serialized_start=23275 - _TXSENDRESPONSE._serialized_end=23331 - _DISCONNECTREQUEST._serialized_start=23333 - _DISCONNECTREQUEST._serialized_end=23394 - _DISCONNECTRESPONSE._serialized_start=23396 - _DISCONNECTRESPONSE._serialized_end=23416 - _FEERATESREQUEST._serialized_start=23418 - _FEERATESREQUEST._serialized_end=23525 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23488 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23525 - _FEERATESRESPONSE._serialized_start=23527 - _FEERATESRESPONSE._serialized_end=23613 - _FEERATESPERKB._serialized_start=23616 - _FEERATESPERKB._serialized_end=23939 - _FEERATESPERKW._serialized_start=23942 - _FEERATESPERKW._serialized_end=24265 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24268 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24461 - _FUNDCHANNELREQUEST._serialized_start=24464 - _FUNDCHANNELREQUEST._serialized_end=24866 - _FUNDCHANNELRESPONSE._serialized_start=24868 - _FUNDCHANNELRESPONSE._serialized_end=24987 - _GETROUTEREQUEST._serialized_start=24990 - _GETROUTEREQUEST._serialized_end=25226 - _GETROUTERESPONSE._serialized_start=25228 - _GETROUTERESPONSE._serialized_end=25281 - _GETROUTEROUTE._serialized_start=25284 - _GETROUTEROUTE._serialized_end=25481 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25452 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25481 - _LISTFORWARDSREQUEST._serialized_start=25484 - _LISTFORWARDSREQUEST._serialized_end=25742 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25624 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25700 - _LISTFORWARDSRESPONSE._serialized_start=25744 - _LISTFORWARDSRESPONSE._serialized_end=25811 - _LISTFORWARDSFORWARDS._serialized_start=25814 - _LISTFORWARDSFORWARDS._serialized_end=26382 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26179 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26263 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26265 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26313 - _LISTPAYSREQUEST._serialized_start=26385 - _LISTPAYSREQUEST._serialized_end=26604 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26510 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26565 - _LISTPAYSRESPONSE._serialized_start=26606 - _LISTPAYSRESPONSE._serialized_end=26657 - _LISTPAYSPAYS._serialized_start=26660 - _LISTPAYSPAYS._serialized_end=27169 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=26994 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27053 - _PINGREQUEST._serialized_start=27171 - _PINGREQUEST._serialized_end=27260 - _PINGRESPONSE._serialized_start=27262 - _PINGRESPONSE._serialized_end=27292 - _SIGNMESSAGEREQUEST._serialized_start=27294 - _SIGNMESSAGEREQUEST._serialized_end=27331 - _SIGNMESSAGERESPONSE._serialized_start=27333 - _SIGNMESSAGERESPONSE._serialized_end=27403 - _STOPREQUEST._serialized_start=27405 - _STOPREQUEST._serialized_end=27418 - _STOPRESPONSE._serialized_start=27420 - _STOPRESPONSE._serialized_end=27434 - _NODE._serialized_start=27437 - _NODE._serialized_end=30365 + _LISTPEERSPEERSCHANNELSALIAS._serialized_start=5074 + _LISTPEERSPEERSCHANNELSALIAS._serialized_end=5165 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5168 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=5506 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5422 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=5477 + _LISTFUNDSREQUEST._serialized_start=5508 + _LISTFUNDSREQUEST._serialized_end=5556 + _LISTFUNDSRESPONSE._serialized_start=5558 + _LISTFUNDSRESPONSE._serialized_end=5659 + _LISTFUNDSOUTPUTS._serialized_start=5662 + _LISTFUNDSOUTPUTS._serialized_end=6035 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=5923 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=5990 + _LISTFUNDSCHANNELS._serialized_start=6038 + _LISTFUNDSCHANNELS._serialized_end=6297 + _SENDPAYREQUEST._serialized_start=6300 + _SENDPAYREQUEST._serialized_end=6647 + _SENDPAYRESPONSE._serialized_start=6650 + _SENDPAYRESPONSE._serialized_end=7199 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7037 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7079 + _SENDPAYROUTE._serialized_start=7201 + _SENDPAYROUTE._serialized_end=7293 + _LISTCHANNELSREQUEST._serialized_start=7296 + _LISTCHANNELSREQUEST._serialized_end=7443 + _LISTCHANNELSRESPONSE._serialized_start=7445 + _LISTCHANNELSRESPONSE._serialized_end=7512 + _LISTCHANNELSCHANNELS._serialized_start=7515 + _LISTCHANNELSCHANNELS._serialized_end=7931 + _ADDGOSSIPREQUEST._serialized_start=7933 + _ADDGOSSIPREQUEST._serialized_end=7968 + _ADDGOSSIPRESPONSE._serialized_start=7970 + _ADDGOSSIPRESPONSE._serialized_end=7989 + _AUTOCLEANINVOICEREQUEST._serialized_start=7991 + _AUTOCLEANINVOICEREQUEST._serialized_end=8102 + _AUTOCLEANINVOICERESPONSE._serialized_start=8105 + _AUTOCLEANINVOICERESPONSE._serialized_end=8234 + _CHECKMESSAGEREQUEST._serialized_start=8236 + _CHECKMESSAGEREQUEST._serialized_end=8321 + _CHECKMESSAGERESPONSE._serialized_start=8323 + _CHECKMESSAGERESPONSE._serialized_end=8395 + _CLOSEREQUEST._serialized_start=8398 + _CLOSEREQUEST._serialized_end=8714 + _CLOSERESPONSE._serialized_start=8717 + _CLOSERESPONSE._serialized_end=8888 + _CLOSERESPONSE_CLOSETYPE._serialized_start=8819 + _CLOSERESPONSE_CLOSETYPE._serialized_end=8872 + _CONNECTREQUEST._serialized_start=8890 + _CONNECTREQUEST._serialized_end=8974 + _CONNECTRESPONSE._serialized_start=8977 + _CONNECTRESPONSE._serialized_end=9119 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9084 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9119 + _CONNECTADDRESS._serialized_start=9122 + _CONNECTADDRESS._serialized_end=9373 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9261 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9341 + _CREATEINVOICEREQUEST._serialized_start=9375 + _CREATEINVOICEREQUEST._serialized_end=9449 + _CREATEINVOICERESPONSE._serialized_start=9452 + _CREATEINVOICERESPONSE._serialized_end=10079 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=9879 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=9935 + _DATASTOREREQUEST._serialized_start=10082 + _DATASTOREREQUEST._serialized_end=10390 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10235 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10347 + _DATASTORERESPONSE._serialized_start=10393 + _DATASTORERESPONSE._serialized_end=10523 + _CREATEONIONREQUEST._serialized_start=10526 + _CREATEONIONREQUEST._serialized_end=10683 + _CREATEONIONRESPONSE._serialized_start=10685 + _CREATEONIONRESPONSE._serialized_end=10745 + _CREATEONIONHOPS._serialized_start=10747 + _CREATEONIONHOPS._serialized_end=10797 + _DELDATASTOREREQUEST._serialized_start=10799 + _DELDATASTOREREQUEST._serialized_end=10873 + _DELDATASTORERESPONSE._serialized_start=10876 + _DELDATASTORERESPONSE._serialized_end=11009 + _DELEXPIREDINVOICEREQUEST._serialized_start=11011 + _DELEXPIREDINVOICEREQUEST._serialized_end=11083 + _DELEXPIREDINVOICERESPONSE._serialized_start=11085 + _DELEXPIREDINVOICERESPONSE._serialized_end=11112 + _DELINVOICEREQUEST._serialized_start=11115 + _DELINVOICEREQUEST._serialized_end=11297 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11231 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11284 + _DELINVOICERESPONSE._serialized_start=11300 + _DELINVOICERESPONSE._serialized_end=11739 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11231 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11284 + _INVOICEREQUEST._serialized_start=11742 + _INVOICEREQUEST._serialized_end=12054 + _INVOICERESPONSE._serialized_start=12057 + _INVOICERESPONSE._serialized_end=12416 + _LISTDATASTOREREQUEST._serialized_start=12418 + _LISTDATASTOREREQUEST._serialized_end=12453 + _LISTDATASTORERESPONSE._serialized_start=12455 + _LISTDATASTORERESPONSE._serialized_end=12526 + _LISTDATASTOREDATASTORE._serialized_start=12529 + _LISTDATASTOREDATASTORE._serialized_end=12664 + _LISTINVOICESREQUEST._serialized_start=12667 + _LISTINVOICESREQUEST._serialized_end=12836 + _LISTINVOICESRESPONSE._serialized_start=12838 + _LISTINVOICESRESPONSE._serialized_end=12905 + _LISTINVOICESINVOICES._serialized_start=12908 + _LISTINVOICESINVOICES._serialized_end=13568 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13345 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13408 + _SENDONIONREQUEST._serialized_start=13571 + _SENDONIONREQUEST._serialized_end=13919 + _SENDONIONRESPONSE._serialized_start=13922 + _SENDONIONRESPONSE._serialized_end=14445 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14293 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14337 + _SENDONIONFIRST_HOP._serialized_start=14447 + _SENDONIONFIRST_HOP._serialized_end=14528 + _LISTSENDPAYSREQUEST._serialized_start=14531 + _LISTSENDPAYSREQUEST._serialized_end=14766 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14668 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14727 + _LISTSENDPAYSRESPONSE._serialized_start=14768 + _LISTSENDPAYSRESPONSE._serialized_end=14835 + _LISTSENDPAYSPAYMENTS._serialized_start=14838 + _LISTSENDPAYSPAYMENTS._serialized_end=15451 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15256 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15323 + _LISTTRANSACTIONSREQUEST._serialized_start=15453 + _LISTTRANSACTIONSREQUEST._serialized_end=15478 + _LISTTRANSACTIONSRESPONSE._serialized_start=15480 + _LISTTRANSACTIONSRESPONSE._serialized_end=15563 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15566 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=15848 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=15851 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16367 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16063 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16341 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16370 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=16914 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16609 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=16888 + _PAYREQUEST._serialized_start=16917 + _PAYREQUEST._serialized_end=17389 + _PAYRESPONSE._serialized_start=17392 + _PAYRESPONSE._serialized_end=17771 + _PAYRESPONSE_PAYSTATUS._serialized_start=17674 + _PAYRESPONSE_PAYSTATUS._serialized_end=17724 + _LISTNODESREQUEST._serialized_start=17773 + _LISTNODESREQUEST._serialized_end=17815 + _LISTNODESRESPONSE._serialized_start=17817 + _LISTNODESRESPONSE._serialized_end=17872 + _LISTNODESNODES._serialized_start=17875 + _LISTNODESNODES._serialized_end=18100 + _LISTNODESNODESADDRESSES._serialized_start=18103 + _LISTNODESNODESADDRESSES._serialized_end=18350 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18243 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18338 + _WAITANYINVOICEREQUEST._serialized_start=18352 + _WAITANYINVOICEREQUEST._serialized_end=18455 + _WAITANYINVOICERESPONSE._serialized_start=18458 + _WAITANYINVOICERESPONSE._serialized_end=18989 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=18834 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=18879 + _WAITINVOICEREQUEST._serialized_start=18991 + _WAITINVOICEREQUEST._serialized_end=19026 + _WAITINVOICERESPONSE._serialized_start=19029 + _WAITINVOICERESPONSE._serialized_end=19548 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19396 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19438 + _WAITSENDPAYREQUEST._serialized_start=19551 + _WAITSENDPAYREQUEST._serialized_end=19693 + _WAITSENDPAYRESPONSE._serialized_start=19696 + _WAITSENDPAYRESPONSE._serialized_end=20214 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20073 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20106 + _NEWADDRREQUEST._serialized_start=20217 + _NEWADDRREQUEST._serialized_end=20375 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20301 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20359 + _NEWADDRRESPONSE._serialized_start=20377 + _NEWADDRRESPONSE._serialized_end=20468 + _WITHDRAWREQUEST._serialized_start=20471 + _WITHDRAWREQUEST._serialized_end=20673 + _WITHDRAWRESPONSE._serialized_start=20675 + _WITHDRAWRESPONSE._serialized_end=20733 + _KEYSENDREQUEST._serialized_start=20736 + _KEYSENDREQUEST._serialized_end=21068 + _KEYSENDRESPONSE._serialized_start=21071 + _KEYSENDRESPONSE._serialized_end=21441 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21365 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21394 + _KEYSENDEXTRATLVS._serialized_start=21443 + _KEYSENDEXTRATLVS._serialized_end=21461 + _FUNDPSBTREQUEST._serialized_start=21464 + _FUNDPSBTREQUEST._serialized_end=21775 + _FUNDPSBTRESPONSE._serialized_start=21778 + _FUNDPSBTRESPONSE._serialized_end=21995 + _FUNDPSBTRESERVATIONS._serialized_start=21997 + _FUNDPSBTRESERVATIONS._serialized_end=22114 + _SENDPSBTREQUEST._serialized_start=22116 + _SENDPSBTREQUEST._serialized_end=22181 + _SENDPSBTRESPONSE._serialized_start=22183 + _SENDPSBTRESPONSE._serialized_end=22227 + _SIGNPSBTREQUEST._serialized_start=22229 + _SIGNPSBTREQUEST._serialized_end=22278 + _SIGNPSBTRESPONSE._serialized_start=22280 + _SIGNPSBTRESPONSE._serialized_end=22319 + _UTXOPSBTREQUEST._serialized_start=22322 + _UTXOPSBTREQUEST._serialized_end=22669 + _UTXOPSBTRESPONSE._serialized_start=22672 + _UTXOPSBTRESPONSE._serialized_end=22889 + _UTXOPSBTRESERVATIONS._serialized_start=22891 + _UTXOPSBTRESERVATIONS._serialized_end=23008 + _TXDISCARDREQUEST._serialized_start=23010 + _TXDISCARDREQUEST._serialized_end=23042 + _TXDISCARDRESPONSE._serialized_start=23044 + _TXDISCARDRESPONSE._serialized_end=23098 + _TXPREPAREREQUEST._serialized_start=23101 + _TXPREPAREREQUEST._serialized_end=23265 + _TXPREPARERESPONSE._serialized_start=23267 + _TXPREPARERESPONSE._serialized_end=23335 + _TXSENDREQUEST._serialized_start=23337 + _TXSENDREQUEST._serialized_end=23366 + _TXSENDRESPONSE._serialized_start=23368 + _TXSENDRESPONSE._serialized_end=23424 + _DISCONNECTREQUEST._serialized_start=23426 + _DISCONNECTREQUEST._serialized_end=23487 + _DISCONNECTRESPONSE._serialized_start=23489 + _DISCONNECTRESPONSE._serialized_end=23509 + _FEERATESREQUEST._serialized_start=23511 + _FEERATESREQUEST._serialized_end=23618 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23581 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23618 + _FEERATESRESPONSE._serialized_start=23620 + _FEERATESRESPONSE._serialized_end=23706 + _FEERATESPERKB._serialized_start=23709 + _FEERATESPERKB._serialized_end=24032 + _FEERATESPERKW._serialized_start=24035 + _FEERATESPERKW._serialized_end=24358 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24361 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24554 + _FUNDCHANNELREQUEST._serialized_start=24557 + _FUNDCHANNELREQUEST._serialized_end=24959 + _FUNDCHANNELRESPONSE._serialized_start=24962 + _FUNDCHANNELRESPONSE._serialized_end=25117 + _GETROUTEREQUEST._serialized_start=25120 + _GETROUTEREQUEST._serialized_end=25356 + _GETROUTERESPONSE._serialized_start=25358 + _GETROUTERESPONSE._serialized_end=25411 + _GETROUTEROUTE._serialized_start=25414 + _GETROUTEROUTE._serialized_end=25611 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25582 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25611 + _LISTFORWARDSREQUEST._serialized_start=25614 + _LISTFORWARDSREQUEST._serialized_end=25872 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25754 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25830 + _LISTFORWARDSRESPONSE._serialized_start=25874 + _LISTFORWARDSRESPONSE._serialized_end=25941 + _LISTFORWARDSFORWARDS._serialized_start=25944 + _LISTFORWARDSFORWARDS._serialized_end=26512 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26309 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26393 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26395 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26443 + _LISTPAYSREQUEST._serialized_start=26515 + _LISTPAYSREQUEST._serialized_end=26734 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26640 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26695 + _LISTPAYSRESPONSE._serialized_start=26736 + _LISTPAYSRESPONSE._serialized_end=26787 + _LISTPAYSPAYS._serialized_start=26790 + _LISTPAYSPAYS._serialized_end=27299 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27124 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27183 + _PINGREQUEST._serialized_start=27301 + _PINGREQUEST._serialized_end=27390 + _PINGRESPONSE._serialized_start=27392 + _PINGRESPONSE._serialized_end=27422 + _SIGNMESSAGEREQUEST._serialized_start=27424 + _SIGNMESSAGEREQUEST._serialized_end=27461 + _SIGNMESSAGERESPONSE._serialized_start=27463 + _SIGNMESSAGERESPONSE._serialized_end=27533 + _STOPREQUEST._serialized_start=27535 + _STOPREQUEST._serialized_end=27548 + _STOPRESPONSE._serialized_start=27550 + _STOPRESPONSE._serialized_end=27564 + _NODE._serialized_start=27567 + _NODE._serialized_end=30495 # @@protoc_insertion_point(module_scope) From 3f795364376e2eb3d43dd8a19019e0de8b6948ce Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 8 Jul 2022 10:47:24 +0200 Subject: [PATCH 1076/1530] msggen: Ignore `state_changes` in grpc2py It's being skipped in grpc so we don't have it later anyway. --- contrib/msggen/msggen/gen/grpc2py.py | 6 +++++- contrib/pyln-testing/pyln/testing/grpc2py.py | 11 ----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/contrib/msggen/msggen/gen/grpc2py.py b/contrib/msggen/msggen/gen/grpc2py.py index e0fe5959ee84..8bc791823fe1 100644 --- a/contrib/msggen/msggen/gen/grpc2py.py +++ b/contrib/msggen/msggen/gen/grpc2py.py @@ -17,7 +17,7 @@ def decamelcase(c): override = { - + 'ListPeers.peers[].channels[].state_changes[]': None, } @@ -92,6 +92,8 @@ def generate_enum(self, prefix, field: EnumField): self.converters[field.path] = "str(m.{{name}})" def generate_composite(self, prefix, field: CompositeField): + if override.get(field.path, "") is None: + return name = field.name.normalized() if prefix: prefix = f"{prefix}_{str(name).lower()}" @@ -129,6 +131,8 @@ def {converter_name}(m): self.write(f' "{name}": [{rhs} for i in {rhs}], # ArrayField[primitive] in generate_composite\n', cleanup=False) elif isinstance(f, ArrayField): + if override.get(f.path, "") is None: + continue rhs = self.converters[f.path] self.write(f' "{name}": [{rhs} for i in m.{name}], # ArrayField[composite] in generate_composite\n', cleanup=False) diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index dcde4d744841..ae0a63940833 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -112,16 +112,6 @@ def listpeers_peers_channels_alias2py(m): }) -def listpeers_peers_channels_state_changes2py(m): - return remove_default({ - "timestamp": m.timestamp, # PrimitiveField in generate_composite - "old_state": str(m.old_state), # EnumField in generate_composite - "new_state": str(m.new_state), # EnumField in generate_composite - "cause": str(m.cause), # EnumField in generate_composite - "message": m.message, # PrimitiveField in generate_composite - }) - - def listpeers_peers_channels_htlcs2py(m): return remove_default({ "direction": str(m.direction), # EnumField in generate_composite @@ -172,7 +162,6 @@ def listpeers_peers_channels2py(m): "their_to_self_delay": m.their_to_self_delay, # PrimitiveField in generate_composite "our_to_self_delay": m.our_to_self_delay, # PrimitiveField in generate_composite "max_accepted_htlcs": m.max_accepted_htlcs, # PrimitiveField in generate_composite - "state_changes": [listpeers_peers_channels_state_changes2py(i) for i in m.state_changes], # ArrayField[composite] in generate_composite "status": [m.status for i in m.status], # ArrayField[primitive] in generate_composite "in_payments_offered": m.in_payments_offered, # PrimitiveField in generate_composite "in_offered_msat": amount2msat(m.in_offered_msat), # PrimitiveField in generate_composite From ed51c164c01823d31619781e43d062c483c587db Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 8 Jul 2022 10:47:59 +0200 Subject: [PATCH 1077/1530] pyln-testing: Add `invoice` RPC method --- contrib/pyln-testing/pyln/testing/grpc.py | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/contrib/pyln-testing/pyln/testing/grpc.py b/contrib/pyln-testing/pyln/testing/grpc.py index 4a65bdfa1b2b..bb4c25d7fe56 100644 --- a/contrib/pyln-testing/pyln/testing/grpc.py +++ b/contrib/pyln-testing/pyln/testing/grpc.py @@ -214,9 +214,60 @@ def pay( ) return grpc2py.pay2py(self.stub.Pay(payload)) + def invoice( + self, + amount_msat: Optional[int] = None, + label: str = None, + description: str = None, + expiry: Optional[int] = None, + fallbacks: Optional[List[str]] = None, + preimage: Optional[str] = None, + exposeprivatechannels: Optional[bool] = None, + cltv: Optional[int] = None, + deschashonly: Optional[bool] = None, + # msatoshi=None + ): + payload = pb.InvoiceRequest( + amount_msat=int2amount_or_any(amount_msat), + label=label, + description=description, + expiry=expiry, + fallbacks=fallbacks, + preimage=unhexlify(preimage) if preimage else None, + exposeprivatechannels=exposeprivatechannels, + cltv=cltv, + deschashonly=deschashonly, + ) + return grpc2py.invoice2py(self.stub.Invoice(payload)) + def stop(self): payload = pb.StopRequest() try: self.stub.Stop(payload) except Exception: pass + + def listnodes(self, node_id=None): + payload = pb.ListnodesRequest(id=unhexlify(node_id) if node_id else None) + return grpc2py.listnodes2py(self.stub.ListNodes(payload)) + + def close( + self, + peer_id: str, + unilateraltimeout: Optional[int] = None, + destination: Optional[str] = None, + fee_negotiation_step: Optional[str] = None, + force_lease_closed: Optional[bool] = None, + # TODO: not mapped yet + # feerange: Optional[List[str]]=None + ): + payload = pb.CloseRequest( + id=peer_id, + unilateraltimeout=unilateraltimeout, + destination=destination, + fee_negotiation_step=fee_negotiation_step, + # wrong_funding, + force_lease_closed=force_lease_closed, + # feerange, + ) + return grpc2py.close2py(self.stub.Close(payload)) From e586a612282cb851d89dbd7c26792d843c650072 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 13 May 2022 17:14:27 +0200 Subject: [PATCH 1078/1530] cln-plugin: Add metadata required by crates.io --- plugins/Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/Cargo.toml b/plugins/Cargo.toml index 8acda04350fc..ad859ecd14e5 100644 --- a/plugins/Cargo.toml +++ b/plugins/Cargo.toml @@ -2,6 +2,9 @@ name = "cln-plugin" version = "0.1.0" edition = "2021" +license = "MIT" +repository = "https://github.com/ElementsProject/lightning/tree/master/plugins" +description = "A CLN plugin library. Write your plugin in Rust." [[example]] name = "cln-plugin-startup" @@ -17,7 +20,7 @@ tokio-util = { version = "0.6.9", features = ["codec"] } tokio = { version="1", features = ['io-std', 'rt', 'sync'] } tokio-stream = "*" futures = "0.3" -cln-rpc = { path = "../cln-rpc" } +cln-rpc = { path = "../cln-rpc", version = "0.1.0" } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread", ] } From aa82a96034def8bab3bb0e6faed273d5d9d78a15 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 13 May 2022 17:20:02 +0200 Subject: [PATCH 1079/1530] cln-plugin: Fix plugin dependencies --- plugins/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/Cargo.toml b/plugins/Cargo.toml index ad859ecd14e5..44cca27f956d 100644 --- a/plugins/Cargo.toml +++ b/plugins/Cargo.toml @@ -17,12 +17,12 @@ log = { version = "0.4.14", features = ['std'] } serde = { version = "1.0.131", features = ["derive"] } serde_json = "1.0.72" tokio-util = { version = "0.6.9", features = ["codec"] } -tokio = { version="1", features = ['io-std', 'rt', 'sync'] } -tokio-stream = "*" +tokio = { version="1", features = ['io-std', 'rt', 'sync', 'macros', 'io-util'] } +tokio-stream = "0.1" futures = "0.3" cln-rpc = { path = "../cln-rpc", version = "0.1.0" } [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread", ] } -env_logger = "*" +env_logger = "0.9" cln-grpc = { path = "../cln-grpc" } From 20b2f0af855e78175e46e4661e0fc72fc5e2cd8c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 11 Jul 2022 18:11:47 +0200 Subject: [PATCH 1080/1530] pyln: Ignore generated files when linting --- Makefile | 6 +++++- contrib/pyln-testing/Makefile | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 40dcde7b53b3..83e9d4e0fbfa 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,11 @@ endif PYTEST_OPTS := -v -p no:logging $(PYTEST_OPTS) MY_CHECK_PYTHONPATH=$${PYTHONPATH}$${PYTHONPATH:+:}$(shell pwd)/contrib/pyln-client:$(shell pwd)/contrib/pyln-testing:$(shell pwd)/contrib/pyln-proto/:$(shell pwd)/external/lnprototest:$(shell pwd)/contrib/pyln-spec/bolt1:$(shell pwd)/contrib/pyln-spec/bolt2:$(shell pwd)/contrib/pyln-spec/bolt4:$(shell pwd)/contrib/pyln-spec/bolt7 # Collect generated python files to be excluded from lint checks -PYTHON_GENERATED= +PYTHON_GENERATED= \ + contrib/pyln-testing/pyln/testing/primitives_pb2.py \ + contrib/pyln-testing/pyln/testing/node_pb2_grpc.py \ + contrib/pyln-testing/pyln/testing/node_pb2.py \ + contrib/pyln-testing/pyln/testing/grpc2py.py # Options to pass to cppcheck. Mostly used to exclude files that are # generated with external tools that we don't have control over diff --git a/contrib/pyln-testing/Makefile b/contrib/pyln-testing/Makefile index 4ad0ba390bb7..9f19b9d5918c 100644 --- a/contrib/pyln-testing/Makefile +++ b/contrib/pyln-testing/Makefile @@ -19,7 +19,7 @@ check: check-source check-pytest check-source: check-flake8 check-mypy check-flake8: - flake8 --ignore=E501,E731,W503,E741 pyln tests + flake8 --ignore=E501,E731,W503,E741 --exclude '*_pb2*.py,grpc2py.py' pyln tests check-pytest: pytest tests From 217ce4c03cc741cb83cfedbad9f4293c279d9e46 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 14 Jul 2022 11:14:29 +0200 Subject: [PATCH 1081/1530] schema: Add missing `mindepth` argument to `fundchannel` This was likely missed during the zeroconf PR. Changelog-None --- .msggen.json | 1 + cln-grpc/proto/node.proto | 1 + cln-grpc/src/convert.rs | 1 + cln-rpc/src/model.rs | 2 + contrib/pyln-testing/pyln/testing/node_pb2.py | 500 +++++++++--------- doc/schemas/fundchannel.request.json | 4 + 6 files changed, 259 insertions(+), 250 deletions(-) diff --git a/.msggen.json b/.msggen.json index 9a2cd64157be..e07b0fb3d011 100644 --- a/.msggen.json +++ b/.msggen.json @@ -390,6 +390,7 @@ "FundChannel.id": 9, "FundChannel.minconf": 10, "FundChannel.minconf[]": 4, + "FundChannel.mindepth": 12, "FundChannel.push_msat": 5, "FundChannel.request_amt": 7, "FundChannel.utxos[]": 11 diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index b45b614cc7a1..9af73f0f3c32 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1151,6 +1151,7 @@ message FundchannelRequest { optional Amount request_amt = 7; optional string compact_lease = 8; repeated Outpoint utxos = 11; + optional uint32 mindepth = 12; } message FundchannelResponse { diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 6ca08f45d5da..d2e22fb31843 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1460,6 +1460,7 @@ impl From<&pb::FundchannelRequest> for requests::FundchannelRequest { request_amt: c.request_amt.as_ref().map(|a| a.into()), // Rule #1 for type msat? compact_lease: c.compact_lease.clone(), // Rule #1 for type string? utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 + mindepth: c.mindepth.clone(), // Rule #1 for type u32? } } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 08f730754ec6..26df14c91554 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -724,6 +724,8 @@ pub mod requests { pub compact_lease: Option, #[serde(alias = "utxos", skip_serializing_if = "crate::is_none_or_empty")] pub utxos: Option>, + #[serde(alias = "mindepth", skip_serializing_if = "Option::is_none")] + pub mindepth: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index b93c4eb0cb84..a83101dcbc2d 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"H\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x13\n\x06pubkey\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\x92\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_lease\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xb6\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepth\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1148,253 +1148,253 @@ _CHECKMESSAGEREQUEST._serialized_start=8236 _CHECKMESSAGEREQUEST._serialized_end=8321 _CHECKMESSAGERESPONSE._serialized_start=8323 - _CHECKMESSAGERESPONSE._serialized_end=8395 - _CLOSEREQUEST._serialized_start=8398 - _CLOSEREQUEST._serialized_end=8714 - _CLOSERESPONSE._serialized_start=8717 - _CLOSERESPONSE._serialized_end=8888 - _CLOSERESPONSE_CLOSETYPE._serialized_start=8819 - _CLOSERESPONSE_CLOSETYPE._serialized_end=8872 - _CONNECTREQUEST._serialized_start=8890 - _CONNECTREQUEST._serialized_end=8974 - _CONNECTRESPONSE._serialized_start=8977 - _CONNECTRESPONSE._serialized_end=9119 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9084 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9119 - _CONNECTADDRESS._serialized_start=9122 - _CONNECTADDRESS._serialized_end=9373 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9261 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9341 - _CREATEINVOICEREQUEST._serialized_start=9375 - _CREATEINVOICEREQUEST._serialized_end=9449 - _CREATEINVOICERESPONSE._serialized_start=9452 - _CREATEINVOICERESPONSE._serialized_end=10079 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=9879 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=9935 - _DATASTOREREQUEST._serialized_start=10082 - _DATASTOREREQUEST._serialized_end=10390 - _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10235 - _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10347 - _DATASTORERESPONSE._serialized_start=10393 - _DATASTORERESPONSE._serialized_end=10523 - _CREATEONIONREQUEST._serialized_start=10526 - _CREATEONIONREQUEST._serialized_end=10683 - _CREATEONIONRESPONSE._serialized_start=10685 - _CREATEONIONRESPONSE._serialized_end=10745 - _CREATEONIONHOPS._serialized_start=10747 - _CREATEONIONHOPS._serialized_end=10797 - _DELDATASTOREREQUEST._serialized_start=10799 - _DELDATASTOREREQUEST._serialized_end=10873 - _DELDATASTORERESPONSE._serialized_start=10876 - _DELDATASTORERESPONSE._serialized_end=11009 - _DELEXPIREDINVOICEREQUEST._serialized_start=11011 - _DELEXPIREDINVOICEREQUEST._serialized_end=11083 - _DELEXPIREDINVOICERESPONSE._serialized_start=11085 - _DELEXPIREDINVOICERESPONSE._serialized_end=11112 - _DELINVOICEREQUEST._serialized_start=11115 - _DELINVOICEREQUEST._serialized_end=11297 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11231 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11284 - _DELINVOICERESPONSE._serialized_start=11300 - _DELINVOICERESPONSE._serialized_end=11739 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11231 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11284 - _INVOICEREQUEST._serialized_start=11742 - _INVOICEREQUEST._serialized_end=12054 - _INVOICERESPONSE._serialized_start=12057 - _INVOICERESPONSE._serialized_end=12416 - _LISTDATASTOREREQUEST._serialized_start=12418 - _LISTDATASTOREREQUEST._serialized_end=12453 - _LISTDATASTORERESPONSE._serialized_start=12455 - _LISTDATASTORERESPONSE._serialized_end=12526 - _LISTDATASTOREDATASTORE._serialized_start=12529 - _LISTDATASTOREDATASTORE._serialized_end=12664 - _LISTINVOICESREQUEST._serialized_start=12667 - _LISTINVOICESREQUEST._serialized_end=12836 - _LISTINVOICESRESPONSE._serialized_start=12838 - _LISTINVOICESRESPONSE._serialized_end=12905 - _LISTINVOICESINVOICES._serialized_start=12908 - _LISTINVOICESINVOICES._serialized_end=13568 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13345 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13408 - _SENDONIONREQUEST._serialized_start=13571 - _SENDONIONREQUEST._serialized_end=13919 - _SENDONIONRESPONSE._serialized_start=13922 - _SENDONIONRESPONSE._serialized_end=14445 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14293 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14337 - _SENDONIONFIRST_HOP._serialized_start=14447 - _SENDONIONFIRST_HOP._serialized_end=14528 - _LISTSENDPAYSREQUEST._serialized_start=14531 - _LISTSENDPAYSREQUEST._serialized_end=14766 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14668 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14727 - _LISTSENDPAYSRESPONSE._serialized_start=14768 - _LISTSENDPAYSRESPONSE._serialized_end=14835 - _LISTSENDPAYSPAYMENTS._serialized_start=14838 - _LISTSENDPAYSPAYMENTS._serialized_end=15451 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15256 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15323 - _LISTTRANSACTIONSREQUEST._serialized_start=15453 - _LISTTRANSACTIONSREQUEST._serialized_end=15478 - _LISTTRANSACTIONSRESPONSE._serialized_start=15480 - _LISTTRANSACTIONSRESPONSE._serialized_end=15563 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15566 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=15848 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=15851 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16367 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16063 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16341 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16370 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=16914 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16609 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=16888 - _PAYREQUEST._serialized_start=16917 - _PAYREQUEST._serialized_end=17389 - _PAYRESPONSE._serialized_start=17392 - _PAYRESPONSE._serialized_end=17771 - _PAYRESPONSE_PAYSTATUS._serialized_start=17674 - _PAYRESPONSE_PAYSTATUS._serialized_end=17724 - _LISTNODESREQUEST._serialized_start=17773 - _LISTNODESREQUEST._serialized_end=17815 - _LISTNODESRESPONSE._serialized_start=17817 - _LISTNODESRESPONSE._serialized_end=17872 - _LISTNODESNODES._serialized_start=17875 - _LISTNODESNODES._serialized_end=18100 - _LISTNODESNODESADDRESSES._serialized_start=18103 - _LISTNODESNODESADDRESSES._serialized_end=18350 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18243 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18338 - _WAITANYINVOICEREQUEST._serialized_start=18352 - _WAITANYINVOICEREQUEST._serialized_end=18455 - _WAITANYINVOICERESPONSE._serialized_start=18458 - _WAITANYINVOICERESPONSE._serialized_end=18989 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=18834 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=18879 - _WAITINVOICEREQUEST._serialized_start=18991 - _WAITINVOICEREQUEST._serialized_end=19026 - _WAITINVOICERESPONSE._serialized_start=19029 - _WAITINVOICERESPONSE._serialized_end=19548 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19396 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19438 - _WAITSENDPAYREQUEST._serialized_start=19551 - _WAITSENDPAYREQUEST._serialized_end=19693 - _WAITSENDPAYRESPONSE._serialized_start=19696 - _WAITSENDPAYRESPONSE._serialized_end=20214 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20073 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20106 - _NEWADDRREQUEST._serialized_start=20217 - _NEWADDRREQUEST._serialized_end=20375 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20301 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20359 - _NEWADDRRESPONSE._serialized_start=20377 - _NEWADDRRESPONSE._serialized_end=20468 - _WITHDRAWREQUEST._serialized_start=20471 - _WITHDRAWREQUEST._serialized_end=20673 - _WITHDRAWRESPONSE._serialized_start=20675 - _WITHDRAWRESPONSE._serialized_end=20733 - _KEYSENDREQUEST._serialized_start=20736 - _KEYSENDREQUEST._serialized_end=21068 - _KEYSENDRESPONSE._serialized_start=21071 - _KEYSENDRESPONSE._serialized_end=21441 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21365 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21394 - _KEYSENDEXTRATLVS._serialized_start=21443 - _KEYSENDEXTRATLVS._serialized_end=21461 - _FUNDPSBTREQUEST._serialized_start=21464 - _FUNDPSBTREQUEST._serialized_end=21775 - _FUNDPSBTRESPONSE._serialized_start=21778 - _FUNDPSBTRESPONSE._serialized_end=21995 - _FUNDPSBTRESERVATIONS._serialized_start=21997 - _FUNDPSBTRESERVATIONS._serialized_end=22114 - _SENDPSBTREQUEST._serialized_start=22116 - _SENDPSBTREQUEST._serialized_end=22181 - _SENDPSBTRESPONSE._serialized_start=22183 - _SENDPSBTRESPONSE._serialized_end=22227 - _SIGNPSBTREQUEST._serialized_start=22229 - _SIGNPSBTREQUEST._serialized_end=22278 - _SIGNPSBTRESPONSE._serialized_start=22280 - _SIGNPSBTRESPONSE._serialized_end=22319 - _UTXOPSBTREQUEST._serialized_start=22322 - _UTXOPSBTREQUEST._serialized_end=22669 - _UTXOPSBTRESPONSE._serialized_start=22672 - _UTXOPSBTRESPONSE._serialized_end=22889 - _UTXOPSBTRESERVATIONS._serialized_start=22891 - _UTXOPSBTRESERVATIONS._serialized_end=23008 - _TXDISCARDREQUEST._serialized_start=23010 - _TXDISCARDREQUEST._serialized_end=23042 - _TXDISCARDRESPONSE._serialized_start=23044 - _TXDISCARDRESPONSE._serialized_end=23098 - _TXPREPAREREQUEST._serialized_start=23101 - _TXPREPAREREQUEST._serialized_end=23265 - _TXPREPARERESPONSE._serialized_start=23267 - _TXPREPARERESPONSE._serialized_end=23335 - _TXSENDREQUEST._serialized_start=23337 - _TXSENDREQUEST._serialized_end=23366 - _TXSENDRESPONSE._serialized_start=23368 - _TXSENDRESPONSE._serialized_end=23424 - _DISCONNECTREQUEST._serialized_start=23426 - _DISCONNECTREQUEST._serialized_end=23487 - _DISCONNECTRESPONSE._serialized_start=23489 - _DISCONNECTRESPONSE._serialized_end=23509 - _FEERATESREQUEST._serialized_start=23511 - _FEERATESREQUEST._serialized_end=23618 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23581 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23618 - _FEERATESRESPONSE._serialized_start=23620 - _FEERATESRESPONSE._serialized_end=23706 - _FEERATESPERKB._serialized_start=23709 - _FEERATESPERKB._serialized_end=24032 - _FEERATESPERKW._serialized_start=24035 - _FEERATESPERKW._serialized_end=24358 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24361 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24554 - _FUNDCHANNELREQUEST._serialized_start=24557 - _FUNDCHANNELREQUEST._serialized_end=24959 - _FUNDCHANNELRESPONSE._serialized_start=24962 - _FUNDCHANNELRESPONSE._serialized_end=25117 - _GETROUTEREQUEST._serialized_start=25120 - _GETROUTEREQUEST._serialized_end=25356 - _GETROUTERESPONSE._serialized_start=25358 - _GETROUTERESPONSE._serialized_end=25411 - _GETROUTEROUTE._serialized_start=25414 - _GETROUTEROUTE._serialized_end=25611 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25582 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25611 - _LISTFORWARDSREQUEST._serialized_start=25614 - _LISTFORWARDSREQUEST._serialized_end=25872 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25754 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25830 - _LISTFORWARDSRESPONSE._serialized_start=25874 - _LISTFORWARDSRESPONSE._serialized_end=25941 - _LISTFORWARDSFORWARDS._serialized_start=25944 - _LISTFORWARDSFORWARDS._serialized_end=26512 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26309 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26393 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26395 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26443 - _LISTPAYSREQUEST._serialized_start=26515 - _LISTPAYSREQUEST._serialized_end=26734 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26640 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26695 - _LISTPAYSRESPONSE._serialized_start=26736 - _LISTPAYSRESPONSE._serialized_end=26787 - _LISTPAYSPAYS._serialized_start=26790 - _LISTPAYSPAYS._serialized_end=27299 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27124 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27183 - _PINGREQUEST._serialized_start=27301 - _PINGREQUEST._serialized_end=27390 - _PINGRESPONSE._serialized_start=27392 - _PINGRESPONSE._serialized_end=27422 - _SIGNMESSAGEREQUEST._serialized_start=27424 - _SIGNMESSAGEREQUEST._serialized_end=27461 - _SIGNMESSAGERESPONSE._serialized_start=27463 - _SIGNMESSAGERESPONSE._serialized_end=27533 - _STOPREQUEST._serialized_start=27535 - _STOPREQUEST._serialized_end=27548 - _STOPRESPONSE._serialized_start=27550 - _STOPRESPONSE._serialized_end=27564 - _NODE._serialized_start=27567 - _NODE._serialized_end=30495 + _CHECKMESSAGERESPONSE._serialized_end=8379 + _CLOSEREQUEST._serialized_start=8382 + _CLOSEREQUEST._serialized_end=8698 + _CLOSERESPONSE._serialized_start=8701 + _CLOSERESPONSE._serialized_end=8872 + _CLOSERESPONSE_CLOSETYPE._serialized_start=8803 + _CLOSERESPONSE_CLOSETYPE._serialized_end=8856 + _CONNECTREQUEST._serialized_start=8874 + _CONNECTREQUEST._serialized_end=8958 + _CONNECTRESPONSE._serialized_start=8961 + _CONNECTRESPONSE._serialized_end=9103 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9068 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9103 + _CONNECTADDRESS._serialized_start=9106 + _CONNECTADDRESS._serialized_end=9357 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9245 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9325 + _CREATEINVOICEREQUEST._serialized_start=9359 + _CREATEINVOICEREQUEST._serialized_end=9433 + _CREATEINVOICERESPONSE._serialized_start=9436 + _CREATEINVOICERESPONSE._serialized_end=10063 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=9863 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=9919 + _DATASTOREREQUEST._serialized_start=10066 + _DATASTOREREQUEST._serialized_end=10374 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10219 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10331 + _DATASTORERESPONSE._serialized_start=10377 + _DATASTORERESPONSE._serialized_end=10507 + _CREATEONIONREQUEST._serialized_start=10510 + _CREATEONIONREQUEST._serialized_end=10667 + _CREATEONIONRESPONSE._serialized_start=10669 + _CREATEONIONRESPONSE._serialized_end=10729 + _CREATEONIONHOPS._serialized_start=10731 + _CREATEONIONHOPS._serialized_end=10781 + _DELDATASTOREREQUEST._serialized_start=10783 + _DELDATASTOREREQUEST._serialized_end=10857 + _DELDATASTORERESPONSE._serialized_start=10860 + _DELDATASTORERESPONSE._serialized_end=10993 + _DELEXPIREDINVOICEREQUEST._serialized_start=10995 + _DELEXPIREDINVOICEREQUEST._serialized_end=11067 + _DELEXPIREDINVOICERESPONSE._serialized_start=11069 + _DELEXPIREDINVOICERESPONSE._serialized_end=11096 + _DELINVOICEREQUEST._serialized_start=11099 + _DELINVOICEREQUEST._serialized_end=11281 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11215 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11268 + _DELINVOICERESPONSE._serialized_start=11284 + _DELINVOICERESPONSE._serialized_end=11723 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11215 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11268 + _INVOICEREQUEST._serialized_start=11726 + _INVOICEREQUEST._serialized_end=12038 + _INVOICERESPONSE._serialized_start=12041 + _INVOICERESPONSE._serialized_end=12400 + _LISTDATASTOREREQUEST._serialized_start=12402 + _LISTDATASTOREREQUEST._serialized_end=12437 + _LISTDATASTORERESPONSE._serialized_start=12439 + _LISTDATASTORERESPONSE._serialized_end=12510 + _LISTDATASTOREDATASTORE._serialized_start=12513 + _LISTDATASTOREDATASTORE._serialized_end=12648 + _LISTINVOICESREQUEST._serialized_start=12651 + _LISTINVOICESREQUEST._serialized_end=12820 + _LISTINVOICESRESPONSE._serialized_start=12822 + _LISTINVOICESRESPONSE._serialized_end=12889 + _LISTINVOICESINVOICES._serialized_start=12892 + _LISTINVOICESINVOICES._serialized_end=13552 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13329 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13392 + _SENDONIONREQUEST._serialized_start=13555 + _SENDONIONREQUEST._serialized_end=13903 + _SENDONIONRESPONSE._serialized_start=13906 + _SENDONIONRESPONSE._serialized_end=14429 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14277 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14321 + _SENDONIONFIRST_HOP._serialized_start=14431 + _SENDONIONFIRST_HOP._serialized_end=14512 + _LISTSENDPAYSREQUEST._serialized_start=14515 + _LISTSENDPAYSREQUEST._serialized_end=14750 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14652 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14711 + _LISTSENDPAYSRESPONSE._serialized_start=14752 + _LISTSENDPAYSRESPONSE._serialized_end=14819 + _LISTSENDPAYSPAYMENTS._serialized_start=14822 + _LISTSENDPAYSPAYMENTS._serialized_end=15435 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15240 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15307 + _LISTTRANSACTIONSREQUEST._serialized_start=15437 + _LISTTRANSACTIONSREQUEST._serialized_end=15462 + _LISTTRANSACTIONSRESPONSE._serialized_start=15464 + _LISTTRANSACTIONSRESPONSE._serialized_end=15547 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15550 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=15832 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=15835 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16351 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16047 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16325 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16354 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=16898 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16593 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=16872 + _PAYREQUEST._serialized_start=16901 + _PAYREQUEST._serialized_end=17373 + _PAYRESPONSE._serialized_start=17376 + _PAYRESPONSE._serialized_end=17755 + _PAYRESPONSE_PAYSTATUS._serialized_start=17658 + _PAYRESPONSE_PAYSTATUS._serialized_end=17708 + _LISTNODESREQUEST._serialized_start=17757 + _LISTNODESREQUEST._serialized_end=17799 + _LISTNODESRESPONSE._serialized_start=17801 + _LISTNODESRESPONSE._serialized_end=17856 + _LISTNODESNODES._serialized_start=17859 + _LISTNODESNODES._serialized_end=18084 + _LISTNODESNODESADDRESSES._serialized_start=18087 + _LISTNODESNODESADDRESSES._serialized_end=18334 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18227 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18322 + _WAITANYINVOICEREQUEST._serialized_start=18336 + _WAITANYINVOICEREQUEST._serialized_end=18439 + _WAITANYINVOICERESPONSE._serialized_start=18442 + _WAITANYINVOICERESPONSE._serialized_end=18973 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=18818 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=18863 + _WAITINVOICEREQUEST._serialized_start=18975 + _WAITINVOICEREQUEST._serialized_end=19010 + _WAITINVOICERESPONSE._serialized_start=19013 + _WAITINVOICERESPONSE._serialized_end=19532 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19380 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19422 + _WAITSENDPAYREQUEST._serialized_start=19535 + _WAITSENDPAYREQUEST._serialized_end=19677 + _WAITSENDPAYRESPONSE._serialized_start=19680 + _WAITSENDPAYRESPONSE._serialized_end=20198 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20057 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20090 + _NEWADDRREQUEST._serialized_start=20201 + _NEWADDRREQUEST._serialized_end=20359 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20285 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20343 + _NEWADDRRESPONSE._serialized_start=20361 + _NEWADDRRESPONSE._serialized_end=20452 + _WITHDRAWREQUEST._serialized_start=20455 + _WITHDRAWREQUEST._serialized_end=20657 + _WITHDRAWRESPONSE._serialized_start=20659 + _WITHDRAWRESPONSE._serialized_end=20717 + _KEYSENDREQUEST._serialized_start=20720 + _KEYSENDREQUEST._serialized_end=21052 + _KEYSENDRESPONSE._serialized_start=21055 + _KEYSENDRESPONSE._serialized_end=21425 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21349 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21378 + _KEYSENDEXTRATLVS._serialized_start=21427 + _KEYSENDEXTRATLVS._serialized_end=21445 + _FUNDPSBTREQUEST._serialized_start=21448 + _FUNDPSBTREQUEST._serialized_end=21759 + _FUNDPSBTRESPONSE._serialized_start=21762 + _FUNDPSBTRESPONSE._serialized_end=21979 + _FUNDPSBTRESERVATIONS._serialized_start=21981 + _FUNDPSBTRESERVATIONS._serialized_end=22098 + _SENDPSBTREQUEST._serialized_start=22100 + _SENDPSBTREQUEST._serialized_end=22165 + _SENDPSBTRESPONSE._serialized_start=22167 + _SENDPSBTRESPONSE._serialized_end=22211 + _SIGNPSBTREQUEST._serialized_start=22213 + _SIGNPSBTREQUEST._serialized_end=22262 + _SIGNPSBTRESPONSE._serialized_start=22264 + _SIGNPSBTRESPONSE._serialized_end=22303 + _UTXOPSBTREQUEST._serialized_start=22306 + _UTXOPSBTREQUEST._serialized_end=22653 + _UTXOPSBTRESPONSE._serialized_start=22656 + _UTXOPSBTRESPONSE._serialized_end=22873 + _UTXOPSBTRESERVATIONS._serialized_start=22875 + _UTXOPSBTRESERVATIONS._serialized_end=22992 + _TXDISCARDREQUEST._serialized_start=22994 + _TXDISCARDREQUEST._serialized_end=23026 + _TXDISCARDRESPONSE._serialized_start=23028 + _TXDISCARDRESPONSE._serialized_end=23082 + _TXPREPAREREQUEST._serialized_start=23085 + _TXPREPAREREQUEST._serialized_end=23249 + _TXPREPARERESPONSE._serialized_start=23251 + _TXPREPARERESPONSE._serialized_end=23319 + _TXSENDREQUEST._serialized_start=23321 + _TXSENDREQUEST._serialized_end=23350 + _TXSENDRESPONSE._serialized_start=23352 + _TXSENDRESPONSE._serialized_end=23408 + _DISCONNECTREQUEST._serialized_start=23410 + _DISCONNECTREQUEST._serialized_end=23471 + _DISCONNECTRESPONSE._serialized_start=23473 + _DISCONNECTRESPONSE._serialized_end=23493 + _FEERATESREQUEST._serialized_start=23495 + _FEERATESREQUEST._serialized_end=23602 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23565 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23602 + _FEERATESRESPONSE._serialized_start=23604 + _FEERATESRESPONSE._serialized_end=23690 + _FEERATESPERKB._serialized_start=23693 + _FEERATESPERKB._serialized_end=24016 + _FEERATESPERKW._serialized_start=24019 + _FEERATESPERKW._serialized_end=24342 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24345 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24538 + _FUNDCHANNELREQUEST._serialized_start=24541 + _FUNDCHANNELREQUEST._serialized_end=24979 + _FUNDCHANNELRESPONSE._serialized_start=24982 + _FUNDCHANNELRESPONSE._serialized_end=25137 + _GETROUTEREQUEST._serialized_start=25140 + _GETROUTEREQUEST._serialized_end=25376 + _GETROUTERESPONSE._serialized_start=25378 + _GETROUTERESPONSE._serialized_end=25431 + _GETROUTEROUTE._serialized_start=25434 + _GETROUTEROUTE._serialized_end=25631 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25602 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25631 + _LISTFORWARDSREQUEST._serialized_start=25634 + _LISTFORWARDSREQUEST._serialized_end=25892 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25774 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25850 + _LISTFORWARDSRESPONSE._serialized_start=25894 + _LISTFORWARDSRESPONSE._serialized_end=25961 + _LISTFORWARDSFORWARDS._serialized_start=25964 + _LISTFORWARDSFORWARDS._serialized_end=26532 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26329 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26413 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26415 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26463 + _LISTPAYSREQUEST._serialized_start=26535 + _LISTPAYSREQUEST._serialized_end=26754 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26660 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26715 + _LISTPAYSRESPONSE._serialized_start=26756 + _LISTPAYSRESPONSE._serialized_end=26807 + _LISTPAYSPAYS._serialized_start=26810 + _LISTPAYSPAYS._serialized_end=27319 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27144 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27203 + _PINGREQUEST._serialized_start=27321 + _PINGREQUEST._serialized_end=27410 + _PINGRESPONSE._serialized_start=27412 + _PINGRESPONSE._serialized_end=27442 + _SIGNMESSAGEREQUEST._serialized_start=27444 + _SIGNMESSAGEREQUEST._serialized_end=27481 + _SIGNMESSAGERESPONSE._serialized_start=27483 + _SIGNMESSAGERESPONSE._serialized_end=27553 + _STOPREQUEST._serialized_start=27555 + _STOPREQUEST._serialized_end=27568 + _STOPRESPONSE._serialized_start=27570 + _STOPRESPONSE._serialized_end=27584 + _NODE._serialized_start=27587 + _NODE._serialized_end=30515 # @@protoc_insertion_point(module_scope) diff --git a/doc/schemas/fundchannel.request.json b/doc/schemas/fundchannel.request.json index 0023124882e6..1ebc44b3dfa9 100644 --- a/doc/schemas/fundchannel.request.json +++ b/doc/schemas/fundchannel.request.json @@ -40,6 +40,10 @@ "items": { "type": "outpoint" } + }, + "mindepth": { + "description": "Number of confirmations required before we consider the channel active", + "type": "u32" } } } From 76d05483fa2544f3ec463bb15c9e5ee8b9a58d4e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 19 Jul 2022 09:41:06 +0200 Subject: [PATCH 1082/1530] pyln-testing: Add listinvoices to grpc shim --- contrib/pyln-testing/pyln/testing/grpc.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/contrib/pyln-testing/pyln/testing/grpc.py b/contrib/pyln-testing/pyln/testing/grpc.py index bb4c25d7fe56..d8ff7bc08925 100644 --- a/contrib/pyln-testing/pyln/testing/grpc.py +++ b/contrib/pyln-testing/pyln/testing/grpc.py @@ -271,3 +271,18 @@ def close( # feerange, ) return grpc2py.close2py(self.stub.Close(payload)) + + def listinvoices( + self, + label=None, + payment_hash=None, + invstring=None, + offer_id=None + ): + payload = pb.ListinvoicesRequest( + label=label, + invstring=invstring, + payment_hash=unhexlify(payment_hash) if payment_hash else None, + offer_id=offer_id, + ) + return grpc2py.listinvoices2py(self.stub.ListInvoices(payload)) From 08bef48d5c37867f14f68cbdfd1d21972033c8f6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2022 14:19:14 +0930 Subject: [PATCH 1083/1530] pytest: disable autoreconnect in test_rbf_reconnect_tx_construct It reconnects itself, so we don't see the ['connected'] is False. ``` 2022-07-20T20:37:06.3808161Z @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') 2022-07-20T20:37:06.3809031Z @pytest.mark.developer("uses dev-disconnect") 2022-07-20T20:37:06.3809547Z @pytest.mark.openchannel('v2') 2022-07-20T20:37:06.3810058Z def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): ... 2022-07-20T20:37:06.3864163Z # Now we finish off the completes failure check 2022-07-20T20:37:06.3864604Z for d in disconnects[-2:]: 2022-07-20T20:37:06.3865162Z l1.rpc.connect(l2.info['id'], 'localhost', l2.port) 2022-07-20T20:37:06.3865711Z bump = l1.rpc.openchannel_bump(chan_id, chan_amount, initpsbt['psbt']) 2022-07-20T20:37:06.3866169Z with pytest.raises(RpcError): 2022-07-20T20:37:06.3866674Z update = l1.rpc.openchannel_update(chan_id, bump['psbt']) 2022-07-20T20:37:06.3867367Z > wait_for(lambda: l1.rpc.getpeer(l2.info['id'])['connected'] is False) ... 2022-07-20T20:37:06.5215961Z lightningd-1 2022-07-20T20:21:49.691Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: dev_disconnect: -WIRE_TX_COMPLETE (WIRE_TX_COMPLETE) 2022-07-20T20:37:06.5216482Z lightningd-1 2022-07-20T20:21:49.691Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#1: peer_out WIRE_TX_COMPLETE 2022-07-20T20:37:06.5216756Z lightningd-1 2022-07-20T20:21:49.691Z DEBUG connectd: drain_peer 2022-07-20T20:37:06.5217064Z lightningd-1 2022-07-20T20:21:49.692Z DEBUG connectd: drain_peer draining subd! 2022-07-20T20:37:06.5217549Z lightningd-1 2022-07-20T20:21:49.692Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-lightningd: peer_disconnect_done 2022-07-20T20:37:06.5218482Z lightningd-1 2022-07-20T20:21:49.692Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-lightningd: Reconnecting in 1 seconds 2022-07-20T20:37:06.5219110Z lightningd-1 2022-07-20T20:21:49.692Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-lightningd: Will try reconnect in 1 seconds 2022-07-20T20:37:06.5219427Z lightningd-1 2022-07-20T20:21:49.692Z DEBUG gossipd: REPLY WIRE_GOSSIPD_GET_ADDRS_REPLY with 0 fds 2022-07-20T20:37:06.5219994Z lightningd-1 2022-07-20T20:21:49.696Z DEBUG plugin-funder: Cleaning up inflights for peer id 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59 2022-07-20T20:37:06.5220305Z lightningd-1 2022-07-20T20:21:49.696Z DEBUG connectd: maybe_free_peer freeing peer! 2022-07-20T20:37:06.5220743Z lightningd-2 2022-07-20T20:21:49.699Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-gossipd: seeker: chosen as startup peer 2022-07-20T20:37:06.5221136Z lightningd-2 2022-07-20T20:21:49.699Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-hsmd: Got WIRE_HSMD_ECDH_REQ 2022-07-20T20:37:06.5221380Z lightningd-2 2022-07-20T20:21:49.699Z DEBUG connectd: drain_peer 2022-07-20T20:37:06.5221805Z lightningd-1 2022-07-20T20:21:49.700Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-hsmd: Got WIRE_HSMD_READY_CHANNEL 2022-07-20T20:37:06.5223761Z lightningd-1 2022-07-20T20:21:49.700Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#1: signature 30440220338460c0e75c08c21f4f4f96806d81426aee48e06ccc54e87892b332f55fe49e0220527573286f801a0d23317978a9aabbbbcb95c2ceb614596c0ab50bb72b96158b01 on tx 020000000105f2aa5f346f46007b9f8b128e8c6e4d40d0477aefd81250ba99adc9349aabe300000000009db0e280024a01000000000000220020be7935a77ca9ab70a4b8b1906825637767fed3c00824aa90c988983587d684881e6301000000000022002047021684129f8aa1c0b5b97dc99607f5f0850b548813a5da346fda22511284759a3ed620 using key 02324266de8403b3ab157a09f1f784d587af61831c998c151bcc21bb74c2b2314b 2022-07-20T20:37:06.5224194Z lightningd-1 2022-07-20T20:21:49.700Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: Connected out, starting crypto 2022-07-20T20:37:06.5224723Z lightningd-1 2022-07-20T20:21:49.700Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-gossipd: seeker: chosen as startup peer 2022-07-20T20:37:06.5225006Z lightningd-1 2022-07-20T20:21:49.700Z DEBUG hsmd: Client: Received message 31 from client 2022-07-20T20:37:06.5225461Z lightningd-1 2022-07-20T20:21:49.700Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-dualopend-chan#1: peer_out WIRE_COMMITMENT_SIGNED 2022-07-20T20:37:06.5225852Z lightningd-1 2022-07-20T20:21:49.700Z DEBUG 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59-connectd: Connect OUT ``` --- tests/test_opening.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_opening.py b/tests/test_opening.py index ad01674e5576..ada746c8295a 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -623,8 +623,10 @@ def test_rbf_reconnect_tx_construct(node_factory, bitcoind, chainparams): l1, l2 = node_factory.get_nodes(2, opts=[{'disconnect': disconnects, - 'may_reconnect': True}, - {'may_reconnect': True}]) + 'may_reconnect': True, + 'dev-no-reconnect': None}, + {'may_reconnect': True, + 'dev-no-reconnect': None}]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) amount = 2**24 From 8d9c181e3b9e91d68b1d6eb3ced2030c07137d82 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2022 14:28:08 +0930 Subject: [PATCH 1084/1530] dualopend: plug memleak. 1. fromwire now allocates TLVs, so this was actually a leak. 2. We can simply hand "NULL" to towire_, since that is the same as this empty tlv. ``` ...89221a0054c11c1e3ca31d59-dualopend-chan#1: MEMLEAK: 0x56148649c458 ...89221a0054c11c1e3ca31d59-dualopend-chan#1: label=wire/peer_exp_wiregen.c:1041:struct tlv_channel_reestablish_tlvs ...89221a0054c11c1e3ca31d59-dualopend-chan#1: backtrace: ...89221a0054c11c1e3ca31d59-dualopend-chan#1: /home/rusty/devel/cvs/lightning/ccan/ccan/tal/tal.c:442 (tal_alloc_) ...89221a0054c11c1e3ca31d59-dualopend-chan#1: /home/rusty/devel/cvs/lightning/wire/peer_exp_wiregen.c:1041 (tlv_channel_reestablish_tlvs_new) ...89221a0054c11c1e3ca31d59-dualopend-chan#1: /home/rusty/devel/cvs/lightning/openingd/dualopend.c:3536 (do_reconnect_dance) ...89221a0054c11c1e3ca31d59-dualopend-chan#1: /home/rusty/devel/cvs/lightning/openingd/dualopend.c:3955 (main) ...89221a0054c11c1e3ca31d59-dualopend-chan#1: ../sysdeps/nptl/libc_start_call_main.h:58 (__libc_start_call_main) ...89221a0054c11c1e3ca31d59-dualopend-chan#1: ../csu/libc-start.c:392 (__libc_start_main_impl) ...89221a0054c11c1e3ca31d59-dualopend-chan#1: parents: ``` Signed-off-by: Rusty Russell --- openingd/dualopend.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openingd/dualopend.c b/openingd/dualopend.c index e754b800fd44..1a418e100916 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3533,7 +3533,7 @@ static void do_reconnect_dance(struct state *state) struct pubkey remote_current_per_commit_point; struct tx_state *tx_state = state->tx_state; #if EXPERIMENTAL_FEATURES - struct tlv_channel_reestablish_tlvs *tlvs = tlv_channel_reestablish_tlvs_new(NULL); + struct tlv_channel_reestablish_tlvs *tlvs; #endif /* BOLT #2: @@ -3550,7 +3550,7 @@ static void do_reconnect_dance(struct state *state) &last_remote_per_commit_secret, &state->first_per_commitment_point[LOCAL] #if EXPERIMENTAL_FEATURES - , tlvs + , NULL #endif ); peer_write(state->pps, take(msg)); From 9c945dbc68fa268f7995005dc747e23bfd94bdbc Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 13 Jul 2022 12:26:16 +0200 Subject: [PATCH 1085/1530] reprobuild: Add Rust compiler to repro build docker images Changelog-Added: build: Reproducible builds now include rust binaries such as the `cln-grpc` plugin --- contrib/reprobuild/Dockerfile.bionic | 8 +++++++- contrib/reprobuild/Dockerfile.focal | 7 +++++++ contrib/reprobuild/Dockerfile.jammy | 7 +++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/contrib/reprobuild/Dockerfile.bionic b/contrib/reprobuild/Dockerfile.bionic index f01857481bf7..782631de3c6b 100644 --- a/contrib/reprobuild/Dockerfile.bionic +++ b/contrib/reprobuild/Dockerfile.bionic @@ -2,6 +2,8 @@ FROM bionic ENV TZ=UTC RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +ENV RUST_PROFILE=release +ENV PATH=/root/.cargo/bin:/root/.pyenv/shims:/root/.pyenv/bin:$PATH RUN sed -i '/updates/d' /etc/apt/sources.list && \ sed -i '/security/d' /etc/apt/sources.list @@ -25,7 +27,6 @@ RUN apt-get update \ # Need to fetch a python version that is >= 3.7 since that's the # lowest version supported by pyln. This is just temporary until we # drop support for ubuntu 18.04 -ENV PATH=/root/.pyenv/shims:/root/.pyenv/bin:$PATH RUN git clone https://github.com/pyenv/pyenv.git /root/.pyenv \ && apt-get install -y --no-install-recommends \ libbz2-dev \ @@ -41,6 +42,11 @@ RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp && rm /tmp/get-pip.py \ && pip install poetry +RUN wget https://sh.rustup.rs -O rustup-install.sh && \ + bash rustup-install.sh --default-toolchain none --quiet -y && \ + rm rustup-install.sh && \ + /root/.cargo/bin/rustup install 1.62 + RUN mkdir /build WORKDIR /build diff --git a/contrib/reprobuild/Dockerfile.focal b/contrib/reprobuild/Dockerfile.focal index 774295a42b4d..b0a20f10d7f0 100644 --- a/contrib/reprobuild/Dockerfile.focal +++ b/contrib/reprobuild/Dockerfile.focal @@ -2,6 +2,8 @@ FROM focal ENV TZ=UTC RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +ENV RUST_PROFILE=release +ENV PATH=/root/.cargo/bin:$PATH RUN sed -i '/updates/d' /etc/apt/sources.list && \ sed -i '/security/d' /etc/apt/sources.list @@ -29,6 +31,11 @@ RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp && rm /tmp/get-pip.py \ && pip install poetry +RUN wget https://sh.rustup.rs -O rustup-install.sh && \ + bash rustup-install.sh --default-toolchain none --quiet -y && \ + rm rustup-install.sh && \ + /root/.cargo/bin/rustup install 1.62 + RUN mkdir /build WORKDIR /build diff --git a/contrib/reprobuild/Dockerfile.jammy b/contrib/reprobuild/Dockerfile.jammy index a935938a4906..880df5cfe985 100644 --- a/contrib/reprobuild/Dockerfile.jammy +++ b/contrib/reprobuild/Dockerfile.jammy @@ -2,6 +2,8 @@ FROM jammy ENV TZ=UTC RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +ENV RUST_PROFILE=release +ENV PATH=/root/.cargo/bin:$PATH RUN sed -i '/updates/d' /etc/apt/sources.list && \ sed -i '/security/d' /etc/apt/sources.list @@ -29,6 +31,11 @@ RUN wget https://bootstrap.pypa.io/get-pip.py -O /tmp/get-pip.py && python3 /tmp && rm /tmp/get-pip.py \ && pip install poetry +RUN wget https://sh.rustup.rs -O rustup-install.sh && \ + bash rustup-install.sh --default-toolchain none --quiet -y && \ + rm rustup-install.sh && \ + /root/.cargo/bin/rustup install 1.62 + RUN mkdir /build WORKDIR /build From b48ae58b56d73d86fb747af3a38a853923354bc6 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 13 Jul 2022 14:49:58 +0200 Subject: [PATCH 1086/1530] repro: Update ubuntu jammy reprobuild --- contrib/reprobuild/Dockerfile.jammy | 3 ++- tools/repro-build.sh | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/contrib/reprobuild/Dockerfile.jammy b/contrib/reprobuild/Dockerfile.jammy index 880df5cfe985..f76d2ea64706 100644 --- a/contrib/reprobuild/Dockerfile.jammy +++ b/contrib/reprobuild/Dockerfile.jammy @@ -16,7 +16,8 @@ RUN apt-get update \ file \ gettext \ git \ - libgmp-dev \ + libgmp-dev \ + libsqlite3-dev \ libpq-dev \ libsodium23 \ libtool \ diff --git a/tools/repro-build.sh b/tools/repro-build.sh index 1f88ecd1f8cc..9ff53937f841 100755 --- a/tools/repro-build.sh +++ b/tools/repro-build.sh @@ -161,6 +161,9 @@ d8b8653388e676a3ae2fcf565c2b1a42a01a1104062317f641e8d24f0eaff9c3 /var/cache/apt 572a544d2c18bf49d25c465720c570cd8e6e38731386ac9c0a7f29bed2486f3e /var/cache/apt/archives/m4_1.4.18-5ubuntu2_amd64.deb 080b79a1a1623a2e6c6eead37d62b15fdf2c3dbfeafe8ecf5e31c54eb09eadcc /var/cache/apt/archives/make_4.3-4.1build1_amd64.deb 52449467942cc943d651fd16867014e9339f3657935fc09b75b3347aa5a78066 /var/cache/apt/archives/zlib1g_1%3a1.2.11.dfsg-2ubuntu9_amd64.deb +5722d6ef8435a9dc3736e474040b4c7e6512b889ad9f74b6d52cdf11eec7e219 /var/cache/apt/archives/libsqlite3-dev_3.37.2-2_amd64.deb +ddbadadcbfe2669de79eabac36a990f0f1666bb86a87d1a9cd56fd72620ca2db /var/cache/apt/archives/zlib1g-dev_1%3a1.2.11.dfsg-2ubuntu9_amd64.deb +59e3890fc8407bcf8ccc9f709d6513156346d5c942e8c624dc90435e58f6f978 /var/cache/apt/archives/automake_1%3a1.16.5-1.3_all.deb EOF ;; *) From 0fac9d3082bb753cbadd936814e4cbe7fa4dba58 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 13 Jul 2022 16:40:16 +0200 Subject: [PATCH 1087/1530] py: Update poetry.lock using poetry update Changelog-None --- poetry.lock | 613 ++++++++++++++++++---------------------------------- 1 file changed, 214 insertions(+), 399 deletions(-) diff --git a/poetry.lock b/poetry.lock index eda0ac167407..a519fd01e0c9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -8,7 +8,7 @@ python-versions = "*" [[package]] name = "atomicwrites" -version = "1.4.0" +version = "1.4.1" description = "Atomic file writes." category = "dev" optional = false @@ -49,7 +49,7 @@ python-versions = "*" [[package]] name = "cffi" -version = "1.15.0" +version = "1.15.1" description = "Foreign Function Interface for Python calling C code." category = "main" optional = false @@ -76,11 +76,11 @@ docs = ["sphinx (>=1.8.2)", "jaraco.packaging (>=3.2)", "sphinx-tabs (>=1.1.0)", [[package]] name = "click" -version = "8.0.4" +version = "8.1.3" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -100,7 +100,7 @@ cffi = ">=1.3.0" [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.5" description = "Cross-platform colored terminal text." category = "dev" optional = false @@ -108,8 +108,8 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "crc32c" -version = "2.2.post0" -description = "A python package implementing the crc32c algorithmin hardware and software" +version = "2.3" +description = "A python package implementing the crc32c algorithm in hardware and software" category = "dev" optional = false python-versions = "*" @@ -168,14 +168,15 @@ pyflakes = ">=2.4.0,<2.5.0" [[package]] name = "flask" -version = "2.0.3" +version = "2.1.2" description = "A simple framework for building complex web applications." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -click = ">=7.1.2" +click = ">=8.0" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.0" Jinja2 = ">=3.0" Werkzeug = ">=2.0" @@ -186,7 +187,7 @@ dotenv = ["python-dotenv"] [[package]] name = "grpcio" -version = "1.44.0" +version = "1.47.0" description = "HTTP/2-based RPC framework" category = "main" optional = false @@ -196,19 +197,19 @@ python-versions = ">=3.6" six = ">=1.5.2" [package.extras] -protobuf = ["grpcio-tools (>=1.44.0)"] +protobuf = ["grpcio-tools (>=1.47.0)"] [[package]] name = "grpcio-tools" -version = "1.44.0" +version = "1.47.0" description = "Protobuf code generator for gRPC" category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -grpcio = ">=1.44.0" -protobuf = ">=3.5.0.post1,<4.0dev" +grpcio = ">=1.47.0" +protobuf = ">=3.12.0,<4.0dev" [[package]] name = "importlib-metadata" @@ -228,18 +229,18 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [[package]] name = "importlib-resources" -version = "5.4.0" +version = "5.8.0" description = "Read resources from Python packages" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [[package]] name = "iniconfig" @@ -251,7 +252,7 @@ python-versions = "*" [[package]] name = "itsdangerous" -version = "2.1.1" +version = "2.1.2" description = "Safely pass data to untrusted environments and back." category = "dev" optional = false @@ -274,11 +275,11 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [[package]] name = "jinja2" -version = "3.0.3" +version = "3.1.2" description = "A very fast and expressive template engine." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] MarkupSafe = ">=2.0" @@ -288,7 +289,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.4.0" +version = "4.7.2" description = "An implementation of JSON Schema validation for Python" category = "dev" optional = false @@ -303,11 +304,11 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""} [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] [[package]] name = "mako" -version = "1.2.0" +version = "1.2.1" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." category = "main" optional = false @@ -348,7 +349,7 @@ python-versions = "*" [[package]] name = "more-itertools" -version = "8.12.0" +version = "8.13.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -428,22 +429,22 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "protobuf" -version = "3.19.4" +version = "3.20.1" description = "Protocol Buffers" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [[package]] name = "psutil" -version = "5.9.0" +version = "5.9.1" description = "Cross-platform lib for process and system monitoring in Python." category = "dev" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] +test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"] [[package]] name = "psycopg2-binary" @@ -487,11 +488,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.11.2" +version = "2.12.0" description = "Pygments is a syntax highlighting package written in Python." category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [[package]] name = "pyln-bolt7" @@ -503,7 +504,7 @@ python-versions = ">=3.7,<4.0" [[package]] name = "pyln-client" -version = "0.10.2.post1" +version = "0.11.1" description = "Client library and plugin library for Core Lightning" category = "main" optional = false @@ -511,8 +512,8 @@ python-versions = "^3.7" develop = true [package.dependencies] -pyln-bolt7 = "^1.0.186" -pyln-proto = "^0.10.2" +pyln-bolt7 = "^1.0" +pyln-proto = "^0.11" [package.source] type = "directory" @@ -520,7 +521,7 @@ url = "contrib/pyln-client" [[package]] name = "pyln-proto" -version = "0.10.2.post1" +version = "0.11.1" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." category = "main" optional = false @@ -540,7 +541,7 @@ url = "contrib/pyln-proto" [[package]] name = "pyln-testing" -version = "0.10.2" +version = "0.11.1" description = "Test your Core Lightning integration, plugins or whatever you want" category = "dev" optional = false @@ -554,7 +555,7 @@ Flask = "^2.0.3" jsonschema = "^4.4.0" psutil = "^5.9.0" psycopg2-binary = "^2.9.3" -pyln-client = "^0.10.2" +pyln-client = "^0.11" pytest = "^7.0.1" python-bitcoinlib = "^0.11.0" @@ -564,14 +565,14 @@ url = "contrib/pyln-testing" [[package]] name = "pyparsing" -version = "3.0.7" -description = "Python parsing module" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.8" [package.extras] -diagrams = ["jinja2", "railroad-diagrams"] +diagrams = ["railroad-diagrams", "jinja2"] [[package]] name = "pyrsistent" @@ -591,7 +592,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pytest" -version = "7.1.1" +version = "7.1.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -700,7 +701,7 @@ python-versions = ">=3.7" [[package]] name = "typed-ast" -version = "1.5.2" +version = "1.5.4" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false @@ -708,19 +709,19 @@ python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.1.1" -description = "Backported and Experimental Type Hints for Python 3.6+" +version = "4.3.0" +description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "websocket-client" -version = "1.3.1" +version = "1.3.3" description = "WebSocket client for Python with low level API options" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] @@ -729,41 +730,38 @@ test = ["websockets"] [[package]] name = "werkzeug" -version = "2.0.3" +version = "2.1.2" description = "The comprehensive WSGI web application library." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] watchdog = ["watchdog"] [[package]] name = "zipp" -version = "3.7.0" +version = "3.8.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "45e8b4302761ec3b83181b506daba46f587227fab83bf597bc0228da82c24eb0" +content-hash = "6c86f252c45a49ebb8dae1df12623ae1ff80040e7434a8050729df72503502bc" [metadata.files] asn1crypto = [ {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, ] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] +atomicwrites = [] attrs = [ {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, @@ -778,64 +776,78 @@ bitstring = [ {file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"}, ] cffi = [ - {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, - {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, - {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, - {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, - {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, - {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, - {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, - {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, - {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, - {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, - {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, - {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, - {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, - {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, - {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, - {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, - {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, - {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, - {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, - {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, - {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, - {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, - {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, - {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, - {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, - {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] cheroot = [ {file = "cheroot-8.6.0-py2.py3-none-any.whl", hash = "sha256:62cbced16f07e8aaf512673987cd6b1fc5ad00073345e9ed6c4e2a5cc2a3a22d"}, {file = "cheroot-8.6.0.tar.gz", hash = "sha256:366adf6e7cac9555486c2d1be6297993022eff6f8c4655c1443268cca3f08e25"}, ] click = [ - {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, - {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] coincurve = [ {file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"}, @@ -874,72 +886,10 @@ coincurve = [ {file = "coincurve-17.0.0.tar.gz", hash = "sha256:68da55aff898702952fda3ee04fd6ed60bb6b91f919c69270786ed766b548b93"}, ] colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -crc32c = [ - {file = "crc32c-2.2.post0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:67f45df0a21d3efeea2875098466c23248bf2f9ed1eefd487a74c44f87f8f542"}, - {file = "crc32c-2.2.post0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1a1b91448eed19d210eb47e3b2a5f25a05c0c1880ad5a4a8b73bc2d74b597869"}, - {file = "crc32c-2.2.post0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:762e0f149d722d544d097dd43e00c0040116130d49345daa9d923c899bcea2b0"}, - {file = "crc32c-2.2.post0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:364d3c7a5d3e6ad592c01d781ad74d810303892a730f2e071a0abb7953c35f68"}, - {file = "crc32c-2.2.post0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:28eb6732020e3eb498702ea350159bebf79a96f1ebde12e4489cb087b408781d"}, - {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:69fed9a5781c931c43613cf1e011217cb3ac53a728d41bf557f368e007cfebac"}, - {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:945399dca5df68db90a5a63d3b05527c9058435720f213beabd805b4447d6f4e"}, - {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ad737c928f01361ff29a9ab23adce6da109093a334e47c9a507caaee0c953f65"}, - {file = "crc32c-2.2.post0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ea98089bb706d4e070ac841abd0555dc741ac331c1c779e7d89e3061f8dc0d15"}, - {file = "crc32c-2.2.post0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:e07c8324908c258820bd38c4b05fc3f4e58b45edd4e71b2341b4eef8cfa0a2fd"}, - {file = "crc32c-2.2.post0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:b5d332f5825920074868407ab3b9751521fc160716f47e92b93680931f235650"}, - {file = "crc32c-2.2.post0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:4498fb95c48b2a2ab61974b65ec67730085fd58baf379e8373a50d8cf8042f6f"}, - {file = "crc32c-2.2.post0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:3cb208d6a1541cdfadd6ff9c719e51fb9a4800289fd901718d0dee2f6da4eb54"}, - {file = "crc32c-2.2.post0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3fec7da0d62d9e7461a73dbfd1064fdb42094cceba793d81d20cb568b7f33445"}, - {file = "crc32c-2.2.post0-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:5eb70b6d91b2c4cbaf5b61e4951d04967703704e247bf42b2eba6c79c6cb3eac"}, - {file = "crc32c-2.2.post0-cp35-cp35m-win32.whl", hash = "sha256:e3327496b1f671357f7d8d7637cd73c0e48770b9228694109ae844a6c67efdb9"}, - {file = "crc32c-2.2.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:966e069d7d9e5d87d00e6b0a14b6e5715940002a55839e0272df10e53f9be5f2"}, - {file = "crc32c-2.2.post0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:71cf48bd0461d35465a79491058abdf5243453046375cee1fce62dc87c12da18"}, - {file = "crc32c-2.2.post0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6bdcfefc43406afe27e2015fe1b959378eb44a7b498b12bfa0d1e41436e1fe42"}, - {file = "crc32c-2.2.post0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:a1b0c8edb20f0d2a942341cb591826360991cbcc53364d7083595386adeeba04"}, - {file = "crc32c-2.2.post0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:4048370db1890245fa601dfa1b41f6c4a06c02b95d15e1e20ccd2c4b41c7e3e7"}, - {file = "crc32c-2.2.post0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:b36a1cb1de66efc0211cc41bfd0e95410e333f3ca167eb0fff4db599a597dd6c"}, - {file = "crc32c-2.2.post0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:de97cc65a6765741194e5f26055dbe4976498fd0082783d1c5e80bd7ce6c5a1b"}, - {file = "crc32c-2.2.post0-cp36-cp36m-win32.whl", hash = "sha256:36786a4482c14e1f3b073e31cf50031009088c115e5bb3022eea4ef2506bbdbb"}, - {file = "crc32c-2.2.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:3c430d64da293bf4cd00b8c6252f40944050c6581150e1a2e483546e95bb87d9"}, - {file = "crc32c-2.2.post0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a45e1a35d664a730f166a7e634385c3be0126aba2366c94f5960790ae21f10cb"}, - {file = "crc32c-2.2.post0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1c409c147e0bee644ae0586540cc1332d9b3421f196f3d57361ab3dd61318724"}, - {file = "crc32c-2.2.post0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c14f2aa3ee5c3ec746731392781826537730c351fdd769d814cd86dd9577bda"}, - {file = "crc32c-2.2.post0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:da84bdf61a9302854c939cb7c9b3885980cf1f98a789048f55fd00aea2acb156"}, - {file = "crc32c-2.2.post0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1e149d6c44e04bf2edb5c677cf294b12fea431df0c37e9d4d90ad9acc2358864"}, - {file = "crc32c-2.2.post0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:fbb4afa4d6c6ee5205c8a5d9a4722c27a47e1035b34562f7aefc4408c0a94088"}, - {file = "crc32c-2.2.post0-cp37-cp37m-win32.whl", hash = "sha256:04804688634dce8691e7e0b2aecead339f2be15f4c0997fdb92c2a1d79ade826"}, - {file = "crc32c-2.2.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:5faa1c72c5688581ebd71946f34dd937777cdcb59c4e5500a7f61e038b9e7cdc"}, - {file = "crc32c-2.2.post0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:98e1a8907797519e187e5461bcc97b3180977439f77c5d416b51dbfa5fe7bb6b"}, - {file = "crc32c-2.2.post0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:f82b1848956796fdfbfd9296f858f109aebacaea0b344cbd5708a0a54a668759"}, - {file = "crc32c-2.2.post0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ed5b2097257213bb34a9d9c9160e86980a0c378cd0e70febadec9841d7f93380"}, - {file = "crc32c-2.2.post0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:32cbaea35b37bc4a88f4c4d84ecc3d1c6cfa04f12d49130d36c6e3b11b13f2f7"}, - {file = "crc32c-2.2.post0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0ecb5be3eaaa6c6f14d1f7584b6e828a68f5f633c46e2ea30ab0307922ac496a"}, - {file = "crc32c-2.2.post0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3ea5714e6bbb3d03adec83c68f85b2ac67a01461b8131ff3065c6312c52e17b0"}, - {file = "crc32c-2.2.post0-cp38-cp38-win32.whl", hash = "sha256:213bdf9e982a287c067f5542a6024e576bfa116b136c3d26cbc10aa133fb4f9c"}, - {file = "crc32c-2.2.post0-cp38-cp38-win_amd64.whl", hash = "sha256:b6828dab82609659bf3bcef9e0cbf6aac11c5d243e7ebcff6516df965ee889cf"}, - {file = "crc32c-2.2.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9cf12a0eaeb151a569670065374b318f1af355c15261c9a74f03a19306ac677"}, - {file = "crc32c-2.2.post0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:0f6e7ce03fd6104b1f9b9a427eb26fa0eb88cafd7faf5ae0d7f4d2fa35d34242"}, - {file = "crc32c-2.2.post0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b47e710c1a35f785323a73f28a91e4da312f6f806d1e3bce62f60a35937af05a"}, - {file = "crc32c-2.2.post0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:fa812a49462e26a600877e0af4ca719a3bc4e85e986d34336d7abc1d0641b37c"}, - {file = "crc32c-2.2.post0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9b3cf77fde6bd2e88423c248f0d35d2549115e9548b0e5a04ee53306a0a39296"}, - {file = "crc32c-2.2.post0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:fa7392a2c159805ef9d6711dd3090628d567a8fb349e81019cff48f4caeeaa36"}, - {file = "crc32c-2.2.post0-cp39-cp39-win32.whl", hash = "sha256:078cbe11f48e3ace4bb01fe9fa6ef00becb4016ff71b0de3fe794ad0511cff8a"}, - {file = "crc32c-2.2.post0-cp39-cp39-win_amd64.whl", hash = "sha256:0f5284371a0a8eb178ee8dbc157ae1e40cddce2c04719ad8bc2e162a1c37831c"}, - {file = "crc32c-2.2.post0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:0a7bbffaebb8bd91b5039965ef0bc23383dee5078e98e5799de88c75d94f88ca"}, - {file = "crc32c-2.2.post0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:7dad7f0ad3ea71e1e74be29b3312d6902774ef3e91510b0b2bdcd4b612b7797e"}, - {file = "crc32c-2.2.post0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:ad4de5af5494892ba40c73d32337eebf407760dcd25cb94d949dd9b98b662f8c"}, - {file = "crc32c-2.2.post0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9ecf707b09c1f3cb3d4a082ad8912e14175b99b4644fd1069106b4727c179903"}, - {file = "crc32c-2.2.post0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:c69f1e0c6e70927395fdcbfda5f7bed9408cf453c57b609e4b40b04079d86f1f"}, - {file = "crc32c-2.2.post0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:cae9c893ec0eb67f09923a37b40c07eec3f0adfb70bc795276d9fff03b21488e"}, - {file = "crc32c-2.2.post0-pp36-pypy36_pp73-win32.whl", hash = "sha256:43b50ca2ab32267136e5b9e92cb756abb327480d0d477583d0322f10b86dd630"}, - {file = "crc32c-2.2.post0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b3da5d99a8adf07bab4366a94830bad08cb0f4177e483d8bb9e61b157607341d"}, - {file = "crc32c-2.2.post0-pp37-pypy37_pp73-manylinux1_x86_64.whl", hash = "sha256:0a250a257292ed0eb1156a6e0898409f5fdf723314261b8d74fbe52302d232af"}, - {file = "crc32c-2.2.post0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:b6e99e9a93c968b7aedca3e1486c9e6cf57e14516c8316af46e9327f003d66f2"}, - {file = "crc32c-2.2.post0-pp37-pypy37_pp73-win32.whl", hash = "sha256:262b24d51b28bb81e35d6d120d0b440d5a55fda730ced2aff5a9bdd564091397"}, - {file = "crc32c-2.2.post0.tar.gz", hash = "sha256:3d058e7a5e37e4985d1a7ad4cb702bca56b490daa658d4851377d13ead8b435e"}, + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] +crc32c = [] cryptography = [ {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"}, {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"}, @@ -975,133 +925,34 @@ flake8 = [ {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] flask = [ - {file = "Flask-2.0.3-py3-none-any.whl", hash = "sha256:59da8a3170004800a2837844bfa84d49b022550616070f7cb1a659682b2e7c9f"}, - {file = "Flask-2.0.3.tar.gz", hash = "sha256:e1120c228ca2f553b470df4a5fa927ab66258467526069981b3eb0a91902687d"}, -] -grpcio = [ - {file = "grpcio-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:11f811c0fffd84fca747fbc742464575e5eb130fd4fb4d6012ccc34febd001db"}, - {file = "grpcio-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:9a86a91201f8345502ea81dee0a55ae13add5fafadf109b17acd858fe8239651"}, - {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:5f3c54ebb5d9633a557335c01d88d3d4928e9b1b131692283b6184da1edbec0b"}, - {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d47553b8e86ab1e59b0185ba6491a187f94a0239f414c8fc867a22b0405b798"}, - {file = "grpcio-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1e22d3a510438b7f3365c0071b810672d09febac6e8ca8a47eab657ae5f347b"}, - {file = "grpcio-1.44.0-cp310-cp310-win32.whl", hash = "sha256:41036a574cab3468f24d41d6ed2b52588fb85ed60f8feaa925d7e424a250740b"}, - {file = "grpcio-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:4ee51964edfd0a1293a95bb0d72d134ecf889379d90d2612cbf663623ce832b4"}, - {file = "grpcio-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:e2149077d71e060678130644670389ddf1491200bcea16c5560d4ccdc65e3f2e"}, - {file = "grpcio-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:0ac72d4b953b76924f8fa21436af060d7e6d8581e279863f30ee14f20751ac27"}, - {file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:5c30a9a7d3a05920368a60b080cbbeaf06335303be23ac244034c71c03a0fd24"}, - {file = "grpcio-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:05467acd391e3fffb05991c76cb2ed2fa1309d0e3815ac379764bc5670b4b5d4"}, - {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b81dc7894062ed2d25b74a2725aaa0a6895ce97ce854f432fe4e87cad5a07316"}, - {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:46d4843192e7d36278884282e100b8f305cf37d1b3d8c6b4f736d4454640a069"}, - {file = "grpcio-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:898c159148f27e23c08a337fb80d31ece6b76bb24f359d83929460d813665b74"}, - {file = "grpcio-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:b8d852329336c584c636caa9c2db990f3a332b19bc86a80f4646b58d27c142db"}, - {file = "grpcio-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:790d7493337558ae168477d1be3178f4c9b8f91d8cd9b8b719d06fd9b2d48836"}, - {file = "grpcio-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:cd61b52d9cf8fcf8d9628c0b640b9e44fdc5e93d989cc268086a858540ed370c"}, - {file = "grpcio-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:14eefcf623890f3f7dd7831decd2a2116652b5ce1e0f1d4b464b8f52110743b0"}, - {file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:bebe90b8020b4248e5a2076b56154cc6ff45691bbbe980579fc9db26717ac968"}, - {file = "grpcio-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89b390b1c0de909965280d175c53128ce2f0f4f5c0f011382243dd7f2f894060"}, - {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:c122dac5cb299b8ad7308d61bd9fe0413de13b0347cce465398436b3fdf1f609"}, - {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6641a28cc826a92ef717201cca9a035c34a0185e38b0c93f3ce5f01a01a1570a"}, - {file = "grpcio-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb0a3e0e64843441793923d9532a3a23907b07b2a1e0a7a31f186dc185bb772"}, - {file = "grpcio-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:be857b7ec2ac43455156e6ba89262f7d7ae60227049427d01a3fecd218a3f88d"}, - {file = "grpcio-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:f6a9cf0e77f72f2ac30c9c6e086bc7446c984c51bebc6c7f50fbcd718037edba"}, - {file = "grpcio-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:19e54f0c7083c8332b5a75a9081fc5127f1dbb67b6c1a32bd7fe896ef0934918"}, - {file = "grpcio-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:bfd36b959c3c4e945119387baed1414ea46f7116886aa23de0172302b49d7ff1"}, - {file = "grpcio-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:ccd388b8f37b19d06e4152189726ce309e36dc03b53f2216a4ea49f09a7438e6"}, - {file = "grpcio-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:9075c0c003c1ff14ebce8f0ba55cc692158cb55c68da09cf8b0f9fc5b749e343"}, - {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e898194f76212facbaeb6d7545debff29351afa23b53ff8f0834d66611af5139"}, - {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fa6584046a7cf281649975a363673fa5d9c6faf9dc923f261cc0e56713b5892"}, - {file = "grpcio-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36a7bdd6ef9bca050c7ade8cba5f0e743343ea0756d5d3d520e915098a9dc503"}, - {file = "grpcio-1.44.0-cp38-cp38-win32.whl", hash = "sha256:dc3290d0411ddd2bd49adba5793223de8de8b01588d45e9376f1a9f7d25414f4"}, - {file = "grpcio-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:13343e7b840c20f43b44f0e6d3bbdc037c964f0aec9735d7cb685c407731c9ff"}, - {file = "grpcio-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:c5c2f8417d13386e18ccc8c61467cb6a6f9667a1ff7000a2d7d378e5d7df693f"}, - {file = "grpcio-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:cf220199b7b4992729ad4d55d5d3f652f4ccfe1a35b5eacdbecf189c245e1859"}, - {file = "grpcio-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4201c597e5057a9bfef9ea5777a6d83f6252cb78044db7d57d941ec2300734a5"}, - {file = "grpcio-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:e2de61005118ae59d48d5d749283ebfd1ba4ca68cc1000f8a395cd2bdcff7ceb"}, - {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:871078218fa9117e2a378678f327e32fda04e363ed6bc0477275444273255d4d"}, - {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8d610b7b557a7609fecee80b6dd793ecb7a9a3c3497fbdce63ce7d151cdd705"}, - {file = "grpcio-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fcb53e4eb8c271032c91b8981df5fc1bb974bc73e306ec2c27da41bd95c44b5"}, - {file = "grpcio-1.44.0-cp39-cp39-win32.whl", hash = "sha256:e50ddea6de76c09b656df4b5a55ae222e2a56e625c44250e501ff3c904113ec1"}, - {file = "grpcio-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:d2ec124a986093e26420a5fb10fa3f02b2c232f924cdd7b844ddf7e846c020cd"}, - {file = "grpcio-1.44.0.tar.gz", hash = "sha256:4bae1c99896045d3062ab95478411c8d5a52cb84b91a1517312629fa6cfeb50e"}, -] -grpcio-tools = [ - {file = "grpcio-tools-1.44.0.tar.gz", hash = "sha256:be37f458ea510c9a8f1caabbc2b258d12e55d189a567f5edcace90f27dc0efbf"}, - {file = "grpcio_tools-1.44.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:9f58529e24f613019a85c258a274d441d89e0cad8cf7fca21ef3807ba5840c5d"}, - {file = "grpcio_tools-1.44.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:1d120082236f8d2877f8a19366476b82c3562423b877b7c471a142432e31c2c4"}, - {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:65c2fe3cdc5425180f01dd303e28d4f363d38f4c2e3a7e1a87caedd5417e23bb"}, - {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5caef118deb8cdee1978fd3d8e388a9b256cd8d34e4a8895731ac0e86fa5e47c"}, - {file = "grpcio_tools-1.44.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121c9765cee8636201cf0d4e80bc7b509813194919bccdb66e9671c4ece6dac3"}, - {file = "grpcio_tools-1.44.0-cp310-cp310-win32.whl", hash = "sha256:90d1fac188bac838c4169eb3b67197887fa0572ea8a90519a20cddb080800549"}, - {file = "grpcio_tools-1.44.0-cp310-cp310-win_amd64.whl", hash = "sha256:3e16260dfe6e997330473863e01466b0992369ae2337a0249b390b4651cff424"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:608414cc1093e1e9e5980c97a6ee78e51dffff359e7a3f123d1fb9d95b8763a5"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:395609c06f69fbc79518b30a01931127088a3f9ef2cc2a35269c5f187eefd38c"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f7ce16766b24b88ec0e4355f5dd66c2eee6af210e889fcb7961c9c4634c687de"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3c9abc4a40c62f46d5e43e49c7afc567dedf12eeef95933ac9ea2986baa2420b"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:b73fd87a44ba1b91866b0254193c37cdb001737759b77b637cebe0c816d38342"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b211f12e4cbc0fde8e0f982b0f581cce38874666a02ebfed93c23dcaeb8a4e0"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b421dc9b27bcaff4c73644cd3801e4893b11ba3eb39729246fd3de98d9f685b"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-win32.whl", hash = "sha256:33d93027840a873c7b59402fe6db8263b88c56e2f84aa0b6281c05cc8bd314a1"}, - {file = "grpcio_tools-1.44.0-cp36-cp36m-win_amd64.whl", hash = "sha256:71fb6e7e66b918803b1bebd0231560981ab86c2546a3318a45822ce94de5e83d"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:614c427ff235d92f103e9189f0230197c8f2f817d0dd9fd078f5d2ea4d920d02"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:c13e0cb486cfa15320ddcd70452a4d736e6ce319c03d6b3c0c2513ec8d2748fb"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:5ade6b13dc4e148f400c8f55a6ef0b14216a3371d7a9e559571d5981b6cec36b"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6138d2c7eec7ed57585bc58e2dbcb65635a2d574ac632abd29949d3e68936bab"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:3d6c8548b199591757dbfe89ed14e23782d6079d6d201c6c314c72f4086883aa"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b41c419829f01734d65958ba9b01b759061d8f7e0698f9612ba6b8837269f7a9"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9f0c5b4567631fec993826e694e83d86a972b3e2e9b05cb0c56839b0316d26c"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-win32.whl", hash = "sha256:3f0e1d1f3f5a6f0c9f8b5441819dbec831ce7e9ffe04768e4b0d965a95fbbe5e"}, - {file = "grpcio_tools-1.44.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f87fc86d0b4181b6b4da6ec6a29511dca000e6b5694fdd6bbf87d125128bc41"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:cb8baa1d4cea35ca662c24098377bdd9514c56f227da0e38b43cd9b8223bfcc6"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:ea36a294f7c70fd2f2bfb5dcf08602006304aa65b055ebd4f7c709e2a89deba7"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1972caf8f695b91edc6444134445798692fe71276f0cde7604d55e65179adf93"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:674fb8d9c0e2d75166c4385753962485b757897223fc92a19c9e513ab80b96f7"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:37045ba850d423cdacede77b266b127025818a5a36d80f1fd7a5a1614a6a0de5"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cdf72947c6b0b03aa6dac06117a095947d02d43a5c6343051f4ce161fd0abcb"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69bfa6fc1515c202fe428ba9f99e2b2f947b01bafc15d868798235b2e2d36baa"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-win32.whl", hash = "sha256:2c516124356476d9afa126acce10ce568733120afbd9ae17ee01d44b9da20a67"}, - {file = "grpcio_tools-1.44.0-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6441c24176705c5ab056e65a8b330e107107c5a492ba094d1b862a136d15d"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:398eda759194d355eb09f7beabae6e4fb45b3877cf7efe505b49095fa4889cef"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:a169bfd7a1fe8cc11472eeeeab3088b3c5d56caac12b2192a920b73adcbc974c"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:a58aaaec0d846d142edd8e794ebb80aa429abfd581f4493a60a603aac0c50ac8"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c3253bee8b68fe422754faf0f286aa068861c926a7b11e4daeb44b9af767c7f1"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:3c0be60721ae1ba09c4f29572a145f412e561b9201e19428758893709827f472"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e44b9572c2226b85976e0d6054e22d7c59ebd6c9425ee71e5bc8910434aee3e1"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c04ec47905c4f6d6dad34d29f6ace652cc1ddc986f55aaa5559b72104c3f5cf"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-win32.whl", hash = "sha256:fb8c7b9d24e2c4dc77e7800e83b68081729ac6094b781b2afdabf08af18c3b28"}, - {file = "grpcio_tools-1.44.0-cp39-cp39-win_amd64.whl", hash = "sha256:4eb93619c8cb3773fb899504e3e30a0dc79d3904fd7a84091d15552178e1e920"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, - {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, + {file = "Flask-2.1.2-py3-none-any.whl", hash = "sha256:fad5b446feb0d6db6aec0c3184d16a8c1f6c3e464b511649c8918a9be100b4fe"}, + {file = "Flask-2.1.2.tar.gz", hash = "sha256:315ded2ddf8a6281567edb27393010fe3406188bafbfe65a3339d5787d89e477"}, ] +grpcio = [] +grpcio-tools = [] +importlib-metadata = [] importlib-resources = [ - {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, - {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, + {file = "importlib_resources-5.8.0-py3-none-any.whl", hash = "sha256:7952325ffd516c05a8ad0858c74dff2c3343f136fe66a6002b2623dd1d43f223"}, + {file = "importlib_resources-5.8.0.tar.gz", hash = "sha256:568c9f16cb204f9decc8d6d24a572eeea27dacbb4cee9e6b03a8025736769751"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] itsdangerous = [ - {file = "itsdangerous-2.1.1-py3-none-any.whl", hash = "sha256:935642cd4b987cdbee7210080004033af76306757ff8b4c0a506a4b6e06f02cf"}, - {file = "itsdangerous-2.1.1.tar.gz", hash = "sha256:7b7d3023cd35d9cb0c1fd91392f8c95c6fa02c59bf8ad64b8849be3401b95afb"}, + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, ] "jaraco.functools" = [ {file = "jaraco.functools-3.5.0-py3-none-any.whl", hash = "sha256:141f95c490a18eb8aab86caf7a2728f02f604988a26dc36652e3d9fa9e4c49fa"}, {file = "jaraco.functools-3.5.0.tar.gz", hash = "sha256:31e0e93d1027592b7b0bec6ad468db850338981ebee76ba5e212e235f4c7dda0"}, ] jinja2 = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, -] -jsonschema = [ - {file = "jsonschema-4.4.0-py3-none-any.whl", hash = "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823"}, - {file = "jsonschema-4.4.0.tar.gz", hash = "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83"}, -] -mako = [ - {file = "Mako-1.2.0-py3-none-any.whl", hash = "sha256:23aab11fdbbb0f1051b93793a58323ff937e98e34aece1c4219675122e57e4ba"}, - {file = "Mako-1.2.0.tar.gz", hash = "sha256:9a7c7e922b87db3686210cf49d5d767033a41d4010b284e747682c92bddd8b39"}, + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] +jsonschema = [] +mako = [] markupsafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, @@ -1153,8 +1004,8 @@ mistune = [ {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, ] more-itertools = [ - {file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"}, - {file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"}, + {file = "more-itertools-8.13.0.tar.gz", hash = "sha256:a42901a0a5b169d925f6f217cd5a190e32ef54360905b9c39ee7db5313bfec0f"}, + {file = "more_itertools-8.13.0-py3-none-any.whl", hash = "sha256:c5122bffc5f104d37c1626b8615b511f3427aa5389b94d61e5ef8236bfbc3ddb"}, ] mrkd = [] mypy = [ @@ -1192,66 +1043,64 @@ pluggy = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] protobuf = [ - {file = "protobuf-3.19.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f51d5a9f137f7a2cec2d326a74b6e3fc79d635d69ffe1b036d39fc7d75430d37"}, - {file = "protobuf-3.19.4-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:09297b7972da685ce269ec52af761743714996b4381c085205914c41fcab59fb"}, - {file = "protobuf-3.19.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072fbc78d705d3edc7ccac58a62c4c8e0cec856987da7df8aca86e647be4e35c"}, - {file = "protobuf-3.19.4-cp310-cp310-win32.whl", hash = "sha256:7bb03bc2873a2842e5ebb4801f5c7ff1bfbdf426f85d0172f7644fcda0671ae0"}, - {file = "protobuf-3.19.4-cp310-cp310-win_amd64.whl", hash = "sha256:f358aa33e03b7a84e0d91270a4d4d8f5df6921abe99a377828839e8ed0c04e07"}, - {file = "protobuf-3.19.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1c91ef4110fdd2c590effb5dca8fdbdcb3bf563eece99287019c4204f53d81a4"}, - {file = "protobuf-3.19.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c438268eebb8cf039552897d78f402d734a404f1360592fef55297285f7f953f"}, - {file = "protobuf-3.19.4-cp36-cp36m-win32.whl", hash = "sha256:835a9c949dc193953c319603b2961c5c8f4327957fe23d914ca80d982665e8ee"}, - {file = "protobuf-3.19.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4276cdec4447bd5015453e41bdc0c0c1234eda08420b7c9a18b8d647add51e4b"}, - {file = "protobuf-3.19.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6cbc312be5e71869d9d5ea25147cdf652a6781cf4d906497ca7690b7b9b5df13"}, - {file = "protobuf-3.19.4-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:54a1473077f3b616779ce31f477351a45b4fef8c9fd7892d6d87e287a38df368"}, - {file = "protobuf-3.19.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:435bb78b37fc386f9275a7035fe4fb1364484e38980d0dd91bc834a02c5ec909"}, - {file = "protobuf-3.19.4-cp37-cp37m-win32.whl", hash = "sha256:16f519de1313f1b7139ad70772e7db515b1420d208cb16c6d7858ea989fc64a9"}, - {file = "protobuf-3.19.4-cp37-cp37m-win_amd64.whl", hash = "sha256:cdc076c03381f5c1d9bb1abdcc5503d9ca8b53cf0a9d31a9f6754ec9e6c8af0f"}, - {file = "protobuf-3.19.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:69da7d39e39942bd52848438462674c463e23963a1fdaa84d88df7fbd7e749b2"}, - {file = "protobuf-3.19.4-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:48ed3877fa43e22bcacc852ca76d4775741f9709dd9575881a373bd3e85e54b2"}, - {file = "protobuf-3.19.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd95d1dfb9c4f4563e6093a9aa19d9c186bf98fa54da5252531cc0d3a07977e7"}, - {file = "protobuf-3.19.4-cp38-cp38-win32.whl", hash = "sha256:b38057450a0c566cbd04890a40edf916db890f2818e8682221611d78dc32ae26"}, - {file = "protobuf-3.19.4-cp38-cp38-win_amd64.whl", hash = "sha256:7ca7da9c339ca8890d66958f5462beabd611eca6c958691a8fe6eccbd1eb0c6e"}, - {file = "protobuf-3.19.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:36cecbabbda242915529b8ff364f2263cd4de7c46bbe361418b5ed859677ba58"}, - {file = "protobuf-3.19.4-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:c1068287025f8ea025103e37d62ffd63fec8e9e636246b89c341aeda8a67c934"}, - {file = "protobuf-3.19.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96bd766831596d6014ca88d86dc8fe0fb2e428c0b02432fd9db3943202bf8c5e"}, - {file = "protobuf-3.19.4-cp39-cp39-win32.whl", hash = "sha256:84123274d982b9e248a143dadd1b9815049f4477dc783bf84efe6250eb4b836a"}, - {file = "protobuf-3.19.4-cp39-cp39-win_amd64.whl", hash = "sha256:3112b58aac3bac9c8be2b60a9daf6b558ca3f7681c130dcdd788ade7c9ffbdca"}, - {file = "protobuf-3.19.4-py2.py3-none-any.whl", hash = "sha256:8961c3a78ebfcd000920c9060a262f082f29838682b1f7201889300c1fbe0616"}, - {file = "protobuf-3.19.4.tar.gz", hash = "sha256:9df0c10adf3e83015ced42a9a7bd64e13d06c4cf45c340d2c63020ea04499d0a"}, + {file = "protobuf-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996"}, + {file = "protobuf-3.20.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"}, + {file = "protobuf-3.20.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde"}, + {file = "protobuf-3.20.1-cp310-cp310-win32.whl", hash = "sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c"}, + {file = "protobuf-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7"}, + {file = "protobuf-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153"}, + {file = "protobuf-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f"}, + {file = "protobuf-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20"}, + {file = "protobuf-3.20.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531"}, + {file = "protobuf-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e"}, + {file = "protobuf-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c"}, + {file = "protobuf-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067"}, + {file = "protobuf-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf"}, + {file = "protobuf-3.20.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab"}, + {file = "protobuf-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c"}, + {file = "protobuf-3.20.1-cp38-cp38-win32.whl", hash = "sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7"}, + {file = "protobuf-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739"}, + {file = "protobuf-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7"}, + {file = "protobuf-3.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f"}, + {file = "protobuf-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9"}, + {file = "protobuf-3.20.1-cp39-cp39-win32.whl", hash = "sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8"}, + {file = "protobuf-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91"}, + {file = "protobuf-3.20.1-py2.py3-none-any.whl", hash = "sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388"}, + {file = "protobuf-3.20.1.tar.gz", hash = "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9"}, ] psutil = [ - {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:55ce319452e3d139e25d6c3f85a1acf12d1607ddedea5e35fb47a552c051161b"}, - {file = "psutil-5.9.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7336292a13a80eb93c21f36bde4328aa748a04b68c13d01dfddd67fc13fd0618"}, - {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:cb8d10461c1ceee0c25a64f2dd54872b70b89c26419e147a05a10b753ad36ec2"}, - {file = "psutil-5.9.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:7641300de73e4909e5d148e90cc3142fb890079e1525a840cf0dfd39195239fd"}, - {file = "psutil-5.9.0-cp27-none-win32.whl", hash = "sha256:ea42d747c5f71b5ccaa6897b216a7dadb9f52c72a0fe2b872ef7d3e1eacf3ba3"}, - {file = "psutil-5.9.0-cp27-none-win_amd64.whl", hash = "sha256:ef216cc9feb60634bda2f341a9559ac594e2eeaadd0ba187a4c2eb5b5d40b91c"}, - {file = "psutil-5.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90a58b9fcae2dbfe4ba852b57bd4a1dded6b990a33d6428c7614b7d48eccb492"}, - {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff0d41f8b3e9ebb6b6110057e40019a432e96aae2008951121ba4e56040b84f3"}, - {file = "psutil-5.9.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:742c34fff804f34f62659279ed5c5b723bb0195e9d7bd9907591de9f8f6558e2"}, - {file = "psutil-5.9.0-cp310-cp310-win32.whl", hash = "sha256:8293942e4ce0c5689821f65ce6522ce4786d02af57f13c0195b40e1edb1db61d"}, - {file = "psutil-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:9b51917c1af3fa35a3f2dabd7ba96a2a4f19df3dec911da73875e1edaf22a40b"}, - {file = "psutil-5.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e9805fed4f2a81de98ae5fe38b75a74c6e6ad2df8a5c479594c7629a1fe35f56"}, - {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c51f1af02334e4b516ec221ee26b8fdf105032418ca5a5ab9737e8c87dafe203"}, - {file = "psutil-5.9.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32acf55cb9a8cbfb29167cd005951df81b567099295291bcfd1027365b36591d"}, - {file = "psutil-5.9.0-cp36-cp36m-win32.whl", hash = "sha256:e5c783d0b1ad6ca8a5d3e7b680468c9c926b804be83a3a8e95141b05c39c9f64"}, - {file = "psutil-5.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d62a2796e08dd024b8179bd441cb714e0f81226c352c802fca0fd3f89eeacd94"}, - {file = "psutil-5.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3d00a664e31921009a84367266b35ba0aac04a2a6cad09c550a89041034d19a0"}, - {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7779be4025c540d1d65a2de3f30caeacc49ae7a2152108adeaf42c7534a115ce"}, - {file = "psutil-5.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:072664401ae6e7c1bfb878c65d7282d4b4391f1bc9a56d5e03b5a490403271b5"}, - {file = "psutil-5.9.0-cp37-cp37m-win32.whl", hash = "sha256:df2c8bd48fb83a8408c8390b143c6a6fa10cb1a674ca664954de193fdcab36a9"}, - {file = "psutil-5.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1d7b433519b9a38192dfda962dd8f44446668c009833e1429a52424624f408b4"}, - {file = "psutil-5.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c3400cae15bdb449d518545cbd5b649117de54e3596ded84aacabfbb3297ead2"}, - {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2237f35c4bbae932ee98902a08050a27821f8f6dfa880a47195e5993af4702d"}, - {file = "psutil-5.9.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1070a9b287846a21a5d572d6dddd369517510b68710fca56b0e9e02fd24bed9a"}, - {file = "psutil-5.9.0-cp38-cp38-win32.whl", hash = "sha256:76cebf84aac1d6da5b63df11fe0d377b46b7b500d892284068bacccf12f20666"}, - {file = "psutil-5.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:3151a58f0fbd8942ba94f7c31c7e6b310d2989f4da74fcbf28b934374e9bf841"}, - {file = "psutil-5.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:539e429da49c5d27d5a58e3563886057f8fc3868a5547b4f1876d9c0f007bccf"}, - {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58c7d923dc209225600aec73aa2c4ae8ea33b1ab31bc11ef8a5933b027476f07"}, - {file = "psutil-5.9.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611e87eea393f779a35b192b46a164b1d01167c9d323dda9b1e527ea69d697d"}, - {file = "psutil-5.9.0-cp39-cp39-win32.whl", hash = "sha256:4e2fb92e3aeae3ec3b7b66c528981fd327fb93fd906a77215200404444ec1845"}, - {file = "psutil-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:7d190ee2eaef7831163f254dc58f6d2e2a22e27382b936aab51c835fc080c3d3"}, - {file = "psutil-5.9.0.tar.gz", hash = "sha256:869842dbd66bb80c3217158e629d6fceaecc3a3166d3d1faee515b05dd26ca25"}, + {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, + {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, + {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, + {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, + {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, + {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, + {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, + {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, + {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, + {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, + {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, + {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, + {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, + {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, + {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, + {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, + {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, + {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, + {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, + {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, + {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, + {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, + {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, + {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, + {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, + {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, + {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, + {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, + {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, + {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, + {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, + {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"}, ] psycopg2-binary = [ {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, @@ -1315,21 +1164,15 @@ py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] -pycodestyle = [ - {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, - {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, -] +pycodestyle = [] pycparser = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] -pyflakes = [ - {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, - {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, -] +pyflakes = [] pygments = [ - {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, - {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, + {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, + {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, ] pyln-bolt7 = [ {file = "pyln-bolt7-1.0.186.post0.tar.gz", hash = "sha256:950f788869df138599abea7643b752c16ae8ddfa91c3a31b64647c45d08c0407"}, @@ -1339,8 +1182,8 @@ pyln-client = [] pyln-proto = [] pyln-testing = [] pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] pyrsistent = [ {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, @@ -1371,8 +1214,8 @@ pysocks = [ {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, ] pytest = [ - {file = "pytest-7.1.1-py3-none-any.whl", hash = "sha256:92f723789a8fdd7180b6b06483874feca4c48a5c76968e03bb3e7f806a1869ea"}, - {file = "pytest-7.1.1.tar.gz", hash = "sha256:841132caef6b1ad17a9afde46dc4f6cfa59a05f9555aae5151f73bdf2820ca63"}, + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, ] pytest-custom-exit-code = [ {file = "pytest-custom_exit_code-0.3.0.tar.gz", hash = "sha256:51ffff0ee2c1ddcc1242e2ddb2a5fd02482717e33a2326ef330e3aa430244635"}, @@ -1405,45 +1248,17 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -typed-ast = [ - {file = "typed_ast-1.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:183b183b7771a508395d2cbffd6db67d6ad52958a5fdc99f450d954003900266"}, - {file = "typed_ast-1.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:676d051b1da67a852c0447621fdd11c4e104827417bf216092ec3e286f7da596"}, - {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc2542e83ac8399752bc16e0b35e038bdb659ba237f4222616b4e83fb9654985"}, - {file = "typed_ast-1.5.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74cac86cc586db8dfda0ce65d8bcd2bf17b58668dfcc3652762f3ef0e6677e76"}, - {file = "typed_ast-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:18fe320f354d6f9ad3147859b6e16649a0781425268c4dde596093177660e71a"}, - {file = "typed_ast-1.5.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:31d8c6b2df19a777bc8826770b872a45a1f30cfefcfd729491baa5237faae837"}, - {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:963a0ccc9a4188524e6e6d39b12c9ca24cc2d45a71cfdd04a26d883c922b4b78"}, - {file = "typed_ast-1.5.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0eb77764ea470f14fcbb89d51bc6bbf5e7623446ac4ed06cbd9ca9495b62e36e"}, - {file = "typed_ast-1.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:294a6903a4d087db805a7656989f613371915fc45c8cc0ddc5c5a0a8ad9bea4d"}, - {file = "typed_ast-1.5.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:26a432dc219c6b6f38be20a958cbe1abffcc5492821d7e27f08606ef99e0dffd"}, - {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7407cfcad702f0b6c0e0f3e7ab876cd1d2c13b14ce770e412c0c4b9728a0f88"}, - {file = "typed_ast-1.5.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f30ddd110634c2d7534b2d4e0e22967e88366b0d356b24de87419cc4410c41b7"}, - {file = "typed_ast-1.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8c08d6625bb258179b6e512f55ad20f9dfef019bbfbe3095247401e053a3ea30"}, - {file = "typed_ast-1.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:90904d889ab8e81a956f2c0935a523cc4e077c7847a836abee832f868d5c26a4"}, - {file = "typed_ast-1.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bbebc31bf11762b63bf61aaae232becb41c5bf6b3461b80a4df7e791fabb3aca"}, - {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c29dd9a3a9d259c9fa19d19738d021632d673f6ed9b35a739f48e5f807f264fb"}, - {file = "typed_ast-1.5.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:58ae097a325e9bb7a684572d20eb3e1809802c5c9ec7108e85da1eb6c1a3331b"}, - {file = "typed_ast-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:da0a98d458010bf4fe535f2d1e367a2e2060e105978873c04c04212fb20543f7"}, - {file = "typed_ast-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33b4a19ddc9fc551ebabca9765d54d04600c4a50eda13893dadf67ed81d9a098"}, - {file = "typed_ast-1.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1098df9a0592dd4c8c0ccfc2e98931278a6c6c53cb3a3e2cf7e9ee3b06153344"}, - {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c47c3b43fe3a39ddf8de1d40dbbfca60ac8530a36c9b198ea5b9efac75c09e"}, - {file = "typed_ast-1.5.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f290617f74a610849bd8f5514e34ae3d09eafd521dceaa6cf68b3f4414266d4e"}, - {file = "typed_ast-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:df05aa5b241e2e8045f5f4367a9f6187b09c4cdf8578bb219861c4e27c443db5"}, - {file = "typed_ast-1.5.2.tar.gz", hash = "sha256:525a2d4088e70a9f75b08b3f87a51acc9cde640e19cc523c7e41aa355564ae27"}, -] +typed-ast = [] typing-extensions = [ - {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, - {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, ] websocket-client = [ - {file = "websocket-client-1.3.1.tar.gz", hash = "sha256:6278a75065395418283f887de7c3beafb3aa68dada5cacbe4b214e8d26da499b"}, - {file = "websocket_client-1.3.1-py3-none-any.whl", hash = "sha256:074e2ed575e7c822fc0940d31c3ac9bb2b1142c303eafcf3e304e6ce035522e8"}, + {file = "websocket-client-1.3.3.tar.gz", hash = "sha256:d58c5f284d6a9bf8379dab423259fe8f85b70d5fa5d2916d5791a84594b122b1"}, + {file = "websocket_client-1.3.3-py3-none-any.whl", hash = "sha256:5d55652dc1d0b3c734f044337d929aaf83f4f9138816ec680c1aefefb4dc4877"}, ] werkzeug = [ - {file = "Werkzeug-2.0.3-py3-none-any.whl", hash = "sha256:1421ebfc7648a39a5c58c601b154165d05cf47a3cd0ccb70857cbdacf6c8f2b8"}, - {file = "Werkzeug-2.0.3.tar.gz", hash = "sha256:b863f8ff057c522164b6067c9e28b041161b4be5ba4d0daceeaa50a163822d3c"}, -] -zipp = [ - {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, - {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, + {file = "Werkzeug-2.1.2-py3-none-any.whl", hash = "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"}, + {file = "Werkzeug-2.1.2.tar.gz", hash = "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6"}, ] +zipp = [] From 9c3f4ffd44569b08ab6f377768ccc55cab8949d4 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 13 Jul 2022 16:40:37 +0200 Subject: [PATCH 1088/1530] rs: Strip binaries when compiling them for release --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index d7161c019400..c1662d06de85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[profile.release] +strip = "debuginfo" + [workspace] members = [ "cln-rpc", From 84675218222f0f9164227c3580a1b0b940a11117 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 21 Jul 2022 13:21:25 +0200 Subject: [PATCH 1089/1530] rs: Add Cargo.lock for reproducible builds --- .gitignore | 1 - Cargo.lock | 1440 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1440 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index cf5ccb4d3cd3..4e4588023bd5 100644 --- a/.gitignore +++ b/.gitignore @@ -72,5 +72,4 @@ tests/primitives_pb2_grpc.py # Rust targets target -Cargo.lock plugins/cln-grpc diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000000..22686ed31a04 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1440 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" + +[[package]] +name = "async-stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad5c83079eae9969be7fadefe640a1c566901f05ff91ab221de4b6f68d9507e" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f203db73a71dfa2fb6dd22763990fa26f3d2625a6da2da900d23b87d26be27" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "cln-grpc" +version = "0.0.1" +dependencies = [ + "anyhow", + "cln-rpc", + "hex", + "log", + "prost", + "serde_json", + "tonic", + "tonic-build", +] + +[[package]] +name = "cln-grpc-plugin" +version = "0.1.0" +dependencies = [ + "anyhow", + "cln-grpc", + "cln-plugin", + "cln-rpc", + "log", + "prost", + "rcgen", + "tokio", + "tonic", +] + +[[package]] +name = "cln-plugin" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytes", + "cln-grpc", + "cln-rpc", + "env_logger", + "futures", + "log", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tokio-util 0.6.9", +] + +[[package]] +name = "cln-rpc" +version = "0.1.0" +dependencies = [ + "anyhow", + "bytes", + "env_logger", + "futures-util", + "hex", + "log", + "serde", + "serde_json", + "tokio", + "tokio-util 0.6.9", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "der-oid-macro" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c73af209b6a5dc8ca7cbaba720732304792cddc933cfea3d74509c2b1ef2f436" +dependencies = [ + "num-bigint", + "num-traits", + "syn", +] + +[[package]] +name = "der-parser" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cddf120f700b411b2b02ebeb7f04dc0b7c8835909a6c2f52bf72ed0dd3433b2" +dependencies = [ + "der-oid-macro", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" + +[[package]] +name = "futures-executor" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" + +[[package]] +name = "futures-macro" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" + +[[package]] +name = "futures-task" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" + +[[package]] +name = "futures-util" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.10.2+wasi-snapshot-preview1", +] + +[[package]] +name = "h2" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util 0.7.1", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "indexmap" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oid-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe554cb2393bc784fd678c82c84cc0599c31ceadc7f03a594911f822cb8d1815" +dependencies = [ + "der-parser", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + +[[package]] +name = "pem" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947" +dependencies = [ + "base64", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" +dependencies = [ + "bytes", + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "quote" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rcgen" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5911d1403f4143c9d56a702069d593e8d0f3fab880a85e103604d0893ea31ba7" +dependencies = [ + "chrono", + "pem", + "ring", + "x509-parser", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" + +[[package]] +name = "socket2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "syn" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" +dependencies = [ + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "socket2", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796c5e1cd49905e65dd8e700d4cb1dffcbfdb4fc9d017de08c1a537afd83627c" +dependencies = [ + "async-stream", + "async-trait", + "base64", + "bytes", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost", + "prost-derive", + "tokio", + "tokio-rustls", + "tokio-stream", + "tokio-util 0.6.9", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b52d07035516c2b74337d2ac7746075e7dcae7643816c1b12c5ff8a7484c08" +dependencies = [ + "proc-macro2", + "prost-build", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util 0.7.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + +[[package]] +name = "unicode-xid" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" + +[[package]] +name = "web-sys" +version = "0.3.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "x509-parser" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc90836a84cb72e6934137b1504d0cae304ef5d83904beb0c8d773bbfe256ed" +dependencies = [ + "base64", + "chrono", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror", +] + +[[package]] +name = "yasna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" +dependencies = [ + "chrono", +] From b3fde870639494cd97d33b09c0a5d56ace4031b5 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 21 Jul 2022 14:50:53 +0200 Subject: [PATCH 1090/1530] doc: Spell out the reprobuild instructions for each distro We also had to switch around the directories, so now the instructions assume you're in the repo root directory --- doc/REPRODUCIBLE.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/REPRODUCIBLE.md b/doc/REPRODUCIBLE.md index 1d2daf9b1b79..b92fb5c63e2b 100644 --- a/doc/REPRODUCIBLE.md +++ b/doc/REPRODUCIBLE.md @@ -70,7 +70,7 @@ The following table lists the codenames of distributions that we currently support: | Distribution Version | Codename | -|----------------------|----------| +|:---------------------|:---------| | Ubuntu 18.04 | bionic | | Ubuntu 20.04 | focal | | Ubuntu 22.04 | jammy | @@ -116,7 +116,9 @@ We can then build the builder image by calling `docker build` and passing it the `Dockerfile`: ```bash -sudo docker build -t cl-repro-bionic - < Dockerfile.bionic +sudo docker build -t cl-repro-bionic - < contrib/reprobuild/Dockerfile.bionic +sudo docker build -t cl-repro-focal - < contrib/reprobuild/Dockerfile.focal +sudo docker build -t cl-repro-jammy - < contrib/reprobuild/Dockerfile.jammy ``` Since we pass the `Dockerfile` through `stdin` the build command will not @@ -146,6 +148,8 @@ repository (remember to checkout the tag you are trying to build): ```bash sudo docker run --rm -v $(pwd):/repo -ti cl-repro-bionic +sudo docker run --rm -v $(pwd):/repo -ti cl-repro-focal +sudo docker run --rm -v $(pwd):/repo -ti cl-repro-jammy ``` The last few lines of output also contain the `sha256sum` hashes of all From 43e5ef3cc462732529dba76645a198be1700a879 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2022 11:19:30 +0930 Subject: [PATCH 1091/1530] libplugin: don't call callbacks if cmd completed before response. This can particularly happen with commando: ``` commando: FATAL SIGNAL 11 (version 06b36d3) 0x55609e953d51 send_backtrace common/daemon.c:33 0x55609e953dfb crashdump common/daemon.c:46 0x7f665e3b908f ??? /build/glibc-SzIz7B/glibc-2.31/signal/../sysdeps/unix/sysv/linux/x86_64/sigaction.c:0 0x55609e9387a3 send_more_cmd plugins/commando.c:632 0x55609e93b270 handle_rpc_reply plugins/libplugin.c:669 0x55609e93bd50 rpc_read_response_one plugins/libplugin.c:842 0x55609e93be86 rpc_conn_read_response plugins/libplugin.c:862 0x55609e9f4f68 next_plan ccan/ccan/io/io.c:59 0x55609e9f5b70 do_plan ccan/ccan/io/io.c:407 0x55609e9f5bb2 io_ready ccan/ccan/io/io.c:417 0x55609e9f7ea5 io_loop ccan/ccan/io/poll.c:453 0x55609e93eb20 plugin_main plugins/libplugin.c:1676 0x55609e9397ab main plugins/commando.c:922 0x7f665e39a082 __libc_start_main ../csu/libc-start.c:308 0x55609e93677d ??? ???:0 0xffffffffffffffff ??? ???:0 ``` Reported-by: @adi2011 Signed-off-by: Rusty Russell --- plugins/libplugin.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 6225289bc85c..bbbfaa669ff7 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -135,6 +135,22 @@ static void ld_rpc_send(struct plugin *plugin, struct json_stream *stream) io_wake(plugin->io_rpc_conn); } + +/* When cmd for request is gone, we use this as noop callback */ +static struct command_result *ignore_cb(struct command *command, + const char *buf, + const jsmntok_t *result, + void *arg) +{ + return command_done(); +} + +static void disable_request_cb(struct command *cmd, struct out_req *out) +{ + out->errcb = NULL; + out->cb = ignore_cb; +} + /* FIXME: Move lightningd/jsonrpc to common/ ? */ struct out_req * @@ -160,6 +176,10 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, out->arg = arg; uintmap_add(&plugin->out_reqs, out->id, out); + /* If command goes away, don't call callbacks! */ + if (out->cmd) + tal_add_destructor2(out->cmd, disable_request_cb, out); + out->js = new_json_stream(NULL, cmd, NULL); json_object_start(out->js, NULL); json_add_string(out->js, "jsonrpc", "2.0"); @@ -646,6 +666,10 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) json_tok_full_len(toks), json_tok_full(plugin->rpc_buffer, toks), id); + /* Remove destructor if one existed */ + if (out->cmd) + tal_del_destructor2(out->cmd, disable_request_cb, out); + /* We want to free this if callback doesn't. */ tal_steal(tmpctx, out); uintmap_del(&plugin->out_reqs, out->id); From aaf743e43849b7697ba4e2a8261d261963aa0605 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2022 11:19:38 +0930 Subject: [PATCH 1092/1530] commando: fix crash when rune is completely bogus. The error routine returns a string literal in this case, which we can't take(). Reported-by: @jb55 Signed-off-by: Rusty Russell --- plugins/commando.c | 5 +++-- tests/test_plugin.py | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/commando.c b/plugins/commando.c index 2f39079a2665..c748599c8535 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -344,10 +344,11 @@ static const char *check_rune(const tal_t *ctx, cinfo.params = params; cinfo.usage = NULL; strmap_init(&cinfo.cached_params); - err = rune_test(ctx, master_rune, rune, check_condition, &cinfo); + err = rune_test(tmpctx, master_rune, rune, check_condition, &cinfo); /* Just in case they manage to make us speak non-JSON, escape! */ if (err) - err = json_escape(ctx, take(err))->s; + err = json_escape(ctx, err)->s; + strmap_clear(&cinfo.cached_params); /* If it succeeded, *now* we increment any associated usage counter. */ diff --git a/tests/test_plugin.py b/tests/test_plugin.py index a3f0264a5cfe..caf939246a99 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2559,6 +2559,14 @@ def test_commando(node_factory, executor): fut.result(10) rune = l1.rpc.commando_rune()['rune'] + + # Bad rune fails + with pytest.raises(RpcError, match="Not authorized: Not derived from master"): + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': 'VXY4AAkrPyH2vzSvOHnI7PDVfS6O04bRQLUCIUFJD5Y9NjQmbWV0aG9kPWludm9pY2UmcmF0ZT0yMZ==', + 'method': 'listpeers'}) + # This works res = l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], From 4cada557ba8dfeb4ba7f0220d68e91ec2bb5c8b1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2022 14:09:30 +0930 Subject: [PATCH 1093/1530] pytest: don't redirect stderr by default. Some tests need to inspect it, but most don't, and I suspect I'm missing some error messages due to this. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 34 +++++++++++++--------- tests/test_db.py | 4 +-- tests/test_gossip.py | 3 +- tests/test_misc.py | 8 ++--- tests/test_plugin.py | 12 ++++---- tests/test_wallet.py | 2 +- 6 files changed, 35 insertions(+), 28 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index e9d903995897..5ca70e01af6e 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -201,7 +201,7 @@ def __init__(self, outputDir, verbose=True): # pass it to the log matcher and not print it to stdout). self.log_filter = lambda line: False - def start(self, stdin=None, stdout_redir=True): + def start(self, stdin=None, stdout_redir=True, stderr_redir=True): """Start the underlying process and start monitoring it. If stdout_redir is false, you have to make sure logs go into outputDir/log @@ -209,16 +209,21 @@ def start(self, stdin=None, stdout_redir=True): """ logging.debug("Starting '%s'", " ".join(self.cmd_line)) if stdout_redir: - self.proc = subprocess.Popen(self.cmd_line, - stdin=stdin, - stdout=self.stdout_write, - stderr=self.stderr_write, - env=self.env) + stdout = self.stdout_write else: - self.proc = subprocess.Popen(self.cmd_line, - stdin=stdin, - stderr=self.stderr_write, - env=self.env) + stdout = None + if stderr_redir: + stderr = self.stderr_write + self.stderr_redir = True + else: + stderr = None + self.stderr_redir = False + + self.proc = subprocess.Popen(self.cmd_line, + stdin=stdin, + stdout=stdout, + stderr=stderr, + env=self.env) def stop(self, timeout=10): self.proc.terminate() @@ -268,6 +273,7 @@ def is_in_log(self, regex, start=0): def is_in_stderr(self, regex): """Look for `regex` in stderr.""" + assert self.stderr_redir self.logs_catchup() ex = re.compile(regex) for l in self.err_logs: @@ -618,9 +624,9 @@ def cmd_line(self): return self.cmd_prefix + [self.executable] + self.early_opts + opts - def start(self, stdin=None, wait_for_initialized=True): + def start(self, stdin=None, wait_for_initialized=True, stderr_redir=False): self.opts['bitcoin-rpcport'] = self.rpcproxy.rpcport - TailableProc.start(self, stdin, stdout_redir=False) + TailableProc.start(self, stdin, stdout_redir=False, stderr_redir=stderr_redir) if wait_for_initialized: self.wait_for_log("Server started with public key") logging.info("LightningD started") @@ -895,8 +901,8 @@ def is_synced_with_bitcoin(self, info=None): info = self.rpc.getinfo() return 'warning_bitcoind_sync' not in info and 'warning_lightningd_sync' not in info - def start(self, wait_for_bitcoind_sync=True): - self.daemon.start() + def start(self, wait_for_bitcoind_sync=True, stderr_redir=False): + self.daemon.start(stderr_redir=stderr_redir) # Cache `getinfo`, we'll be using it a lot self.info = self.rpc.getinfo() # This shortcut is sufficient for our simple tests. diff --git a/tests/test_db.py b/tests/test_db.py index 1c4b7c35e700..c21f5b4a88ad 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -425,7 +425,7 @@ def test_db_sanity_checks(bitcoind, node_factory): # Provide the --wallet option and start with wrong db l1.daemon.opts['wallet'] = "sqlite3://" + l2.db.path - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) l1.daemon.wait_for_log(r'\*\*BROKEN\*\* wallet: Wallet node_id does not match HSM') # Will have exited with non-zero status. assert l1.daemon.proc.wait(TIMEOUT) != 0 @@ -435,7 +435,7 @@ def test_db_sanity_checks(bitcoind, node_factory): l1.daemon.opts['wallet'] = "sqlite3://" + l1.db.path l1.daemon.opts['network'] = "bitcoin" - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) l1.daemon.wait_for_log(r'\*\*BROKEN\*\* wallet: Wallet blockchain hash does not match network blockchain hash') # Will have exited with non-zero status. assert l1.daemon.proc.wait(TIMEOUT) != 0 diff --git a/tests/test_gossip.py b/tests/test_gossip.py index adb72faadf4e..4ce2f7967ef2 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -241,8 +241,9 @@ def test_announce_and_connect_via_dns(node_factory, bitcoind): @unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") def test_only_announce_one_dns(node_factory, bitcoind): # and test that we can't announce more than one DNS address - l1 = node_factory.get_node(may_fail=True, expect_fail=True, + l1 = node_factory.get_node(expect_fail=True, start=False, options={'announce-addr': ['localhost.localdomain:12345', 'example.com:12345']}) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) wait_for(lambda: l1.daemon.is_in_stderr("Only one DNS can be announced")) diff --git a/tests/test_misc.py b/tests/test_misc.py index a0a834cceb37..83862b994cb8 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -109,7 +109,7 @@ def crash_bitcoincli(r): # Ignore BROKEN log message about blocksonly mode. l2 = node_factory.get_node(start=False, expect_fail=True, allow_broken_log=True) - l2.daemon.start(wait_for_initialized=False) + l2.daemon.start(wait_for_initialized=False, stderr_redir=True) # Will exit with failure code. assert l2.daemon.wait() == 1 assert l2.daemon.is_in_stderr(r".*deactivating transaction relay is not" @@ -1212,7 +1212,7 @@ def test_rescan(node_factory, bitcoind): l1.daemon.opts['rescan'] = -500000 l1.stop() bitcoind.generate_block(4) - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) # Will exit with failure code. assert l1.daemon.wait() == 1 assert l1.daemon.is_in_stderr(r"bitcoind has gone backwards from 500000 to 105 blocks!") @@ -1243,7 +1243,7 @@ def test_bitcoind_goes_backwards(node_factory, bitcoind): bitcoind.start() # Will simply refuse to start. - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) # Will exit with failure code. assert l1.daemon.wait() == 1 assert l1.daemon.is_in_stderr('bitcoind has gone backwards') @@ -1252,7 +1252,7 @@ def test_bitcoind_goes_backwards(node_factory, bitcoind): l1.daemon.opts['rescan'] = 3 # Will simply refuse to start. - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) # Will exit with failure code. assert l1.daemon.wait() == 1 assert l1.daemon.is_in_stderr('bitcoind has gone backwards') diff --git a/tests/test_plugin.py b/tests/test_plugin.py index caf939246a99..87341b38e3a5 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -109,7 +109,7 @@ def test_option_types(node_factory): }, may_fail=True, start=False) # the node should fail after start, and we get a stderr msg - n.daemon.start(wait_for_initialized=False) + n.daemon.start(wait_for_initialized=False, stderr_redir=True) assert n.daemon.wait() == 1 wait_for(lambda: n.daemon.is_in_stderr('bool_opt: ! does not parse as type bool')) @@ -122,7 +122,7 @@ def test_option_types(node_factory): }, may_fail=True, start=False) # the node should fail after start, and we get a stderr msg - n.daemon.start(wait_for_initialized=False) + n.daemon.start(wait_for_initialized=False, stderr_redir=True) assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr('--int_opt: notok does not parse as type int') @@ -136,7 +136,7 @@ def test_option_types(node_factory): }, may_fail=True, start=False) # the node should fail after start, and we get a stderr msg - n.daemon.start(wait_for_initialized=False) + n.daemon.start(wait_for_initialized=False, stderr_redir=True) assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr("--flag_opt: doesn't allow an argument") @@ -1522,7 +1522,7 @@ def test_libplugin(node_factory): l1.stop() l1.daemon.opts["name-deprecated"] = "test_opt" - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) # Will exit with failure code. assert l1.daemon.wait() == 1 assert l1.daemon.is_in_stderr(r"name-deprecated: deprecated option") @@ -1669,7 +1669,7 @@ def test_bitcoin_backend(node_factory, bitcoind): # We don't start if we haven't all the required methods registered. plugin = os.path.join(os.getcwd(), "tests/plugins/bitcoin/part1.py") l1.daemon.opts["plugin"] = plugin - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) l1.daemon.wait_for_log("Missing a Bitcoin plugin command") # Will exit with failure code. assert l1.daemon.wait() == 1 @@ -2094,7 +2094,7 @@ def test_important_plugin(node_factory): may_fail=True, expect_fail=True, allow_broken_log=True, start=False) - n.daemon.start(wait_for_initialized=False) + n.daemon.start(wait_for_initialized=False, stderr_redir=True) # Will exit with failure code. assert n.daemon.wait() == 1 assert n.daemon.is_in_stderr(r"Failed to register .*nonexistent: No such file or directory") diff --git a/tests/test_wallet.py b/tests/test_wallet.py index c50c39b404f5..ddee4c3d853d 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1038,7 +1038,7 @@ def test_hsm_secret_encryption(node_factory): # Test we cannot restore the same wallet with another password l1.daemon.opts.update({"encrypted-hsm": None}) - l1.daemon.start(stdin=slave_fd, wait_for_initialized=False) + l1.daemon.start(stdin=slave_fd, wait_for_initialized=False, stderr_redir=True) l1.daemon.wait_for_log(r'Enter hsm_secret password') write_all(master_fd, password[2:].encode("utf-8")) assert(l1.daemon.proc.wait(WAIT_TIMEOUT) == HSM_BAD_PASSWORD) From 05a666e424bb9afc9bb0a128e512b82485d49627 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2022 14:10:48 +0930 Subject: [PATCH 1094/1530] commando: limit to 16 partially-received incoming commands at a time. Signed-off-by: Rusty Russell --- plugins/commando.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/commando.c b/plugins/commando.c index c748599c8535..9cfe41147821 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -463,6 +463,10 @@ static void handle_incmd(struct node_id *peer, incmd->contents = tal_arr(incmd, u8, 0); tal_arr_expand(&incoming_commands, incmd); tal_add_destructor2(incmd, destroy_commando, &incoming_commands); + + /* More than 16 partial commands at once? Free oldest */ + if (tal_count(incoming_commands) > 16) + tal_free(incoming_commands[0]); } /* 1MB should be enough for anybody! */ From c10e385612654246fea16cb32b9826af35bd8eb7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 21 Jul 2022 14:40:10 +0930 Subject: [PATCH 1095/1530] commando: add stress test, fix memleak report. Signed-off-by: Rusty Russell --- plugins/commando.c | 6 +++++- tests/test_plugin.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/plugins/commando.c b/plugins/commando.c index 9cfe41147821..c2b32239e635 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -452,8 +452,11 @@ static void handle_incmd(struct node_id *peer, incmd = find_commando(incoming_commands, peer, NULL); /* Don't let them buffer multiple commands: discard old. */ - if (incmd && incmd->id != idnum) + if (incmd && incmd->id != idnum) { + plugin_log(plugin, LOG_DBG, "New cmd from %s, replacing old", + node_id_to_hexstr(tmpctx, peer)); incmd = tal_free(incmd); + } if (!incmd) { incmd = tal(plugin, struct commando); @@ -705,6 +708,7 @@ static struct command_result *json_commando(struct command *cmd, tal_free(peer); tal_free(method); tal_free(cparams); + tal_free(rune); return send_more_cmd(cmd, NULL, NULL, outgoing); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 87341b38e3a5..92157a50ca36 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2804,3 +2804,36 @@ def test_commando_rune(node_factory): 'rune': rune['rune'], 'method': cmd, 'params': params}) + + +@pytest.mark.slow_test +def test_commando_stress(node_factory, executor): + """Stress test to slam commando with many large queries""" + nodes = node_factory.get_nodes(5) + + rune = nodes[0].rpc.commando_rune()['rune'] + for n in nodes[1:]: + n.connect(nodes[0]) + + futs = [] + for i in range(1000): + node = random.choice(nodes[1:]) + futs.append(executor.submit(node.rpc.call, method='commando', + payload={'peer_id': nodes[0].info['id'], + 'rune': rune, + 'method': 'invoice', + 'params': {'amount_msat': 'any', + 'label': 'label{}'.format(i), + 'description': 'A' * 200000, + 'deschashonly': True}})) + discards = 0 + for f in futs: + try: + f.result(TIMEOUT) + except RpcError as e: + assert(e.error['code'] == 0x4c50) + assert(e.error['message'] == "Invalid JSON") + discards += 1 + + # Should have exactly one discard msg from each discard + nodes[0].daemon.wait_for_logs([r"New cmd from .*, replacing old"] * discards) From 8c38302ab812e83236bad93302e9d2b7ba984824 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 20 Jul 2022 12:00:25 +0930 Subject: [PATCH 1096/1530] hsmtool: implement checkhsm. This gives a nice way to ensure your secret is the correct one. Also, we don't need to suppress VALGRIND for this test, now the output races are fixed. Changelog-Added: `hsmtool`: new command `checkhsm` to check BIP39 passphrase against hsm_secret. Signed-off-by: Rusty Russell --- doc/lightning-hsmtool.8.md | 3 ++ tests/test_wallet.py | 42 ++++++++++++++++++++++++-- tools/hsmtool.c | 61 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/doc/lightning-hsmtool.8.md b/doc/lightning-hsmtool.8.md index 3da0727a7269..02ed83e82bff 100644 --- a/doc/lightning-hsmtool.8.md +++ b/doc/lightning-hsmtool.8.md @@ -51,6 +51,9 @@ Specify *password* if the `hsm_secret` is encrypted. **generatehsm** *hsm\_secret\_path* Generates a new hsm_secret using BIP39. +**checkhsm** *hsm\_secret\_path* +Checks that hsm_secret matchs a BIP39 pass phrase. + **dumponchaindescriptors** *hsm_secret* \[*password*\] \[*network*\] Dump output descriptors for our onchain wallet. The descriptors can be used by external services to be able to generate diff --git a/tests/test_wallet.py b/tests/test_wallet.py index ddee4c3d853d..ac5fd0e7da76 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -1218,7 +1218,6 @@ def test_hsmtool_dump_descriptors(node_factory, bitcoind): assert len(bitcoind.rpc.listunspent(1, 1, [addr])) == 1 -@unittest.skipIf(VALGRIND, "It does not play well with prompt and key derivation.") def test_hsmtool_generatehsm(node_factory): l1 = node_factory.get_node(start=False) hsm_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, @@ -1242,9 +1241,48 @@ def test_hsmtool_generatehsm(node_factory): "cake have wedding\n".encode("utf-8")) hsmtool.wait_for_log(r"Enter your passphrase:") write_all(master_fd, "This is actually not a passphrase\n".encode("utf-8")) - hsmtool.proc.wait(WAIT_TIMEOUT) + assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 hsmtool.is_in_log(r"New hsm_secret file created") + # Check should pass. + hsmtool = HsmTool(node_factory.directory, "checkhsm", hsm_path) + master_fd, slave_fd = os.openpty() + hsmtool.start(stdin=slave_fd) + hsmtool.wait_for_log(r"Enter your passphrase:") + write_all(master_fd, "This is actually not a passphrase\n".encode("utf-8")) + hsmtool.wait_for_log(r"Select your language:") + write_all(master_fd, "0\n".encode("utf-8")) + hsmtool.wait_for_log(r"Introduce your BIP39 word list") + write_all(master_fd, "ritual idle hat sunny universe pluck key alpha wing " + "cake have wedding\n".encode("utf-8")) + assert hsmtool.proc.wait(WAIT_TIMEOUT) == 0 + hsmtool.is_in_log(r"OK") + + # Wrong mnemonic will fail. + master_fd, slave_fd = os.openpty() + hsmtool.start(stdin=slave_fd) + hsmtool.wait_for_log(r"Enter your passphrase:") + write_all(master_fd, "This is actually not a passphrase\n".encode("utf-8")) + hsmtool.wait_for_log(r"Select your language:") + write_all(master_fd, "0\n".encode("utf-8")) + hsmtool.wait_for_log(r"Introduce your BIP39 word list") + write_all(master_fd, "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\n".encode("utf-8")) + assert hsmtool.proc.wait(WAIT_TIMEOUT) == 5 + hsmtool.is_in_log(r"resulting hsm_secret did not match") + + # Wrong passphrase will fail. + master_fd, slave_fd = os.openpty() + hsmtool.start(stdin=slave_fd) + hsmtool.wait_for_log(r"Enter your passphrase:") + write_all(master_fd, "This is actually not a passphrase \n".encode("utf-8")) + hsmtool.wait_for_log(r"Select your language:") + write_all(master_fd, "0\n".encode("utf-8")) + hsmtool.wait_for_log(r"Introduce your BIP39 word list") + write_all(master_fd, "ritual idle hat sunny universe pluck key alpha wing " + "cake have wedding\n".encode("utf-8")) + assert hsmtool.proc.wait(WAIT_TIMEOUT) == 5 + hsmtool.is_in_log(r"resulting hsm_secret did not match") + # We can start the node with this hsm_secret l1.start() assert l1.info['id'] == '02244b73339edd004bc6dfbb953a87984c88e9e7c02ca14ef6ec593ca6be622ba7' diff --git a/tools/hsmtool.c b/tools/hsmtool.c index e6ae1e36c0b7..226046365c96 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -39,6 +39,7 @@ static void show_usage(const char *progname) printf(" - guesstoremote " "\n"); printf(" - generatehsm \n"); + printf(" - checkhsm \n"); printf(" - dumponchaindescriptors [network]\n"); exit(0); } @@ -595,6 +596,60 @@ static int dumponchaindescriptors(const char *hsm_secret_path, const char *old_p return 0; } +static int check_hsm(const char *hsm_secret_path) +{ + char mnemonic[BIP39_WORDLIST_LEN]; + struct secret hsm_secret; + u8 bip32_seed[BIP39_SEED_LEN_512]; + size_t bip32_seed_len; + int exit_code; + char *passphrase, *err; + + /* This checks the file existence, too. */ + if (hsm_secret_is_encrypted(hsm_secret_path)) { + char *passwd; + + printf("Enter hsm_secret password:\n"); + fflush(stdout); + passwd = read_stdin_pass_with_exit_code(&err, &exit_code); + if (!passwd) + errx(exit_code, "%s", err); + + if (sodium_init() == -1) + errx(ERROR_LIBSODIUM, + "Could not initialize libsodium. Not enough entropy ?"); + + get_encrypted_hsm_secret(&hsm_secret, hsm_secret_path, passwd); + /* Once the encryption key derived, we don't need it anymore. */ + free(passwd); + } else + get_hsm_secret(&hsm_secret, hsm_secret_path); + + printf("Warning: remember that different passphrases yield different " + "bitcoin wallets.\n"); + printf("If left empty, no password is used (echo is disabled).\n"); + printf("Enter your passphrase: \n"); + fflush(stdout); + passphrase = read_stdin_pass_with_exit_code(&err, &exit_code); + if (!passphrase) + errx(exit_code, "%s", err); + if (strlen(passphrase) == 0) { + free(passphrase); + passphrase = NULL; + } + + read_mnemonic(mnemonic); + if (bip39_mnemonic_to_seed(mnemonic, passphrase, bip32_seed, sizeof(bip32_seed), &bip32_seed_len) != WALLY_OK) + errx(ERROR_LIBWALLY, "Unable to derive BIP32 seed from BIP39 mnemonic"); + + /* We only use first 32 bytes */ + if (memcmp(bip32_seed, hsm_secret.data, sizeof(hsm_secret.data)) != 0) + errx(ERROR_KEYDERIV, "resulting hsm_secret did not match"); + + printf("OK\n"); + return 0; +} + int main(int argc, char *argv[]) { const char *method; @@ -682,5 +737,11 @@ int main(int argc, char *argv[]) return dumponchaindescriptors(argv[2], NULL, is_testnet); } + if (streq(method, "checkhsm")) { + if (argc < 3) + show_usage(argv[0]); + return check_hsm(argv[2]); + } + show_usage(argv[0]); } From 53c333a01bbc00132090b7f57bc76c07063f03f7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 22 Jul 2022 11:14:43 +0930 Subject: [PATCH 1097/1530] pytest: fix flake in test_zeroconf_forward pay failed (non-DEVELOPER) because one node didn't see blocks in time: ``` inv = l3.rpc.invoice(42 * 10**6, 'inv1', 'desc')['bolt11'] > l1.rpc.pay(inv) tests/test_opening.py:1394: ... > raise RpcError(method, payload, resp['error']) E pyln.client.lightning.RpcError: RPC call failed: method: pay, payload: {'bolt11': 'lnbcrt420u1p3dnwv7sp5qnquuwndgz35ywfg3p3dtu07ywmju78r8s0379eaxjxkv5d8jueqpp5kmaxgsye02dzmdlkqkedqvrh2evdl45sz7njrm5dff42dvp4v5qsdq8v3jhxccxqyjw5qcqp9rzjqgkjyd3q5dv6gllh77kygly9c3kfy0d9xwyjyxsq2nq3c83u5vw4n0wkf0y9gwfwhgqqqqqpqqqqqzsqqc9qyysgqad2x2zv0axa3hrfz7nurw4plvspvxlld9wtcg3xxjyxqlzm773a4fkyl09gs8uskj4m7len8r4pf4rh7v9snh3grrpawhk9qsd7vwmcqa9rgxg'}, error: {'code': 210, 'message': 'Ran out of routes to try after 176 attempts: see `paystatus`', 'attempts': [{'status': 'pending', 'partid': 1, 'amount_msat': 42000000msat}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 2, 'amount_msat': 9278783msat, 'parent_partid': 1}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 10, 'amount_msat': 9278783msat, 'parent_partid': 2}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 15, 'amount_msat': 9278783msat, 'parent_partid': 10}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 18, 'amount_msat': 9278783msat, 'parent_partid': 15}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 23, 'amount_msat': 9278783msat, 'parent_partid': 18}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 29, 'amount_msat': 9278783msat, 'parent_partid': 23}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 33, 'amount_msat': 9278783msat, 'parent_partid': 29}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 39, 'amount_msat': 9278783msat, 'parent_partid': 33}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 43, 'amount_msat': 9278783msat, 'parent_partid': 39}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 48, 'amount_msat': 9278783msat, 'parent_partid': 43}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 54, 'amount_msat': 9278783msat, 'parent_partid': 48}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 59, 'amount_msat': 4659837msat, 'parent_partid': 54}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 69, 'amount_msat': 4659837msat, 'parent_partid': 59}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 82, 'amount_msat': 4659837msat, 'parent_partid': 69}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 92, 'amount_msat': 4659837msat, 'parent_partid': 82}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 102, 'amount_msat': 4659837msat, 'parent_partid': 92}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 112, 'amount_msat': 4659837msat, 'parent_partid': 102}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 122, 'amount_msat': 4659837msat, 'parent_partid': 112}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 131, 'amount_msat': 4659837msat, 'parent_partid': 122}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 141, 'amount_msat': 4659837msat, 'parent_partid': 131}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 147, 'amount_msat': 4659837msat, 'parent_partid': 141}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 158, 'amount_msat': 4659837msat, 'parent_partid': 147}, {'status': 'pending', 'partid': 175, 'amount_msat': 2250620msat, 'parent_partid': 158}, {'status': 'pending', 'partid': 176, 'amount_msat': 2409217msat, 'parent_partid': 158}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 60, 'amount_msat': 4618946msat, 'parent_partid': 54}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 70, 'amount_msat': 4618946msat, 'parent_partid': 60}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 81, 'amount_msat': 4618946msat, 'parent_partid': 70}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 91, 'amount_msat': 4618946msat, 'parent_partid': 81}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 101, 'amount_msat': 4618946msat, 'parent_partid': 91}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 111, 'amount_msat': 4618946msat, 'parent_partid': 101}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 121, 'amount_msat': 4618946msat, 'parent_partid': 111}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 132, 'amount_msat': 4618946msat, 'parent_partid': 121}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 142, 'amount_msat': 4618946msat, 'parent_partid': 132}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 148, 'amount_msat': 4618946msat, 'parent_partid': 142}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 157, 'amount_msat': 4618946msat, 'parent_partid': 148}, {'status': 'pending', 'partid': 168, 'amount_msat': 2320055msat, 'parent_partid': 157}, {'status': 'pending', 'partid': 169, 'amount_msat': 2298891msat, 'parent_partid': 157}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 3, 'amount_msat': 9016551msat, 'parent_partid': 1}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 7, 'amount_msat': 9016551msat, 'parent_partid': 3}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 12, 'amount_msat': 9016551msat, 'parent_partid': 7}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 17, 'amount_msat': 9016551msat, 'parent_partid': 12}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 21, 'amount_msat': 9016551msat, 'parent_partid': 17}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 25, 'amount_msat': 9016551msat, 'parent_partid': 21}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 30, 'amount_msat': 9016551msat, 'parent_partid': 25}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 36, 'amount_msat': 9016551msat, 'parent_partid': 30}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 40, 'amount_msat': 9016551msat, 'parent_partid': 36}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 47, 'amount_msat': 9016551msat, 'parent_partid': 40}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 51, 'amount_msat': 9016551msat, 'parent_partid': 47}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 56, 'amount_msat': 4458645msat, 'parent_partid': 51}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 63, 'amount_msat': 4458645msat, 'parent_partid': 56}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 77, 'amount_msat': 4458645msat, 'parent_partid': 63}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 87, 'amount_msat': 4458645msat, 'parent_partid': 77}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 94, 'amount_msat': 4458645msat, 'parent_partid': 87}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 105, 'amount_msat': 4458645msat, 'parent_partid': 94}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 120, 'amount_msat': 4458645msat, 'parent_partid': 105}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 124, 'amount_msat': 4458645msat, 'parent_partid': 120}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 134, 'amount_msat': 4458645msat, 'parent_partid': 124}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 143, 'amount_msat': 4458645msat, 'parent_partid': 134}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 154, 'amount_msat': 4458645msat, 'parent_partid': 143}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 164, 'amount_msat': 2306527msat, 'parent_partid': 154}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 165, 'amount_msat': 2152118msat, 'parent_partid': 154}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 57, 'amount_msat': 4557906msat, 'parent_partid': 51}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 68, 'amount_msat': 4557906msat, 'parent_partid': 57}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 76, 'amount_msat': 4557906msat, 'parent_partid': 68}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 86, 'amount_msat': 4557906msat, 'parent_partid': 76}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 96, 'amount_msat': 4557906msat, 'parent_partid': 86}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 103, 'amount_msat': 4557906msat, 'parent_partid': 96}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 115, 'amount_msat': 4557906msat, 'parent_partid': 103}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 125, 'amount_msat': 4557906msat, 'parent_partid': 115}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 136, 'amount_msat': 4557906msat, 'parent_partid': 125}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 146, 'amount_msat': 4557906msat, 'parent_partid': 136}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 161, 'amount_msat': 4557906msat, 'parent_partid': 146}, {'status': 'pending', 'partid': 173, 'amount_msat': 2208152msat, 'parent_partid': 161}, {'status': 'pending', 'partid': 174, 'amount_msat': 2349754msat, 'parent_partid': 161}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 4, 'amount_msat': 10655305msat, 'parent_partid': 1}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 9, 'amount_msat': 10655305msat, 'parent_partid': 4}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 13, 'amount_msat': 10655305msat, 'parent_partid': 9}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 19, 'amount_msat': 10655305msat, 'parent_partid': 13}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 24, 'amount_msat': 10655305msat, 'parent_partid': 19}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 28, 'amount_msat': 10655305msat, 'parent_partid': 24}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 34, 'amount_msat': 10655305msat, 'parent_partid': 28}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 38, 'amount_msat': 10655305msat, 'parent_partid': 34}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 44, 'amount_msat': 10655305msat, 'parent_partid': 38}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 49, 'amount_msat': 10655305msat, 'parent_partid': 44}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 53, 'amount_msat': 10655305msat, 'parent_partid': 49}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 61, 'amount_msat': 4872267msat, 'parent_partid': 53}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 71, 'amount_msat': 4872267msat, 'parent_partid': 61}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 79, 'amount_msat': 4872267msat, 'parent_partid': 71}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 89, 'amount_msat': 4872267msat, 'parent_partid': 79}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 100, 'amount_msat': 4872267msat, 'parent_partid': 89}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 109, 'amount_msat': 4872267msat, 'parent_partid': 100}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 114, 'amount_msat': 4872267msat, 'parent_partid': 109}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 126, 'amount_msat': 4872267msat, 'parent_partid': 114}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 139, 'amount_msat': 4872267msat, 'parent_partid': 126}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 149, 'amount_msat': 4872267msat, 'parent_partid': 139}, {'status': 'failed', 'failreason': 'Cannot split payment any further without exceeding the maximum number of HTLCs allowed by our channels', 'partid': 162, 'amount_msat': 4872267msat, 'parent_partid': 149}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 62, 'amount_msat': 5783038msat, 'parent_partid': 53}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 72, 'amount_msat': 5783038msat, 'parent_partid': 62}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 80, 'amount_msat': 5783038msat, 'parent_partid': 72}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 90, 'amount_msat': 5783038msat, 'parent_partid': 80}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 99, 'amount_msat': 5783038msat, 'parent_partid': 90}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 110, 'amount_msat': 5783038msat, 'parent_partid': 99}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 117, 'amount_msat': 5783038msat, 'parent_partid': 110}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 128, 'amount_msat': 5783038msat, 'parent_partid': 117}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 138, 'amount_msat': 5783038msat, 'parent_partid': 128}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 150, 'amount_msat': 5783038msat, 'parent_partid': 138}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 156, 'amount_msat': 5783038msat, 'parent_partid': 150}, {'status': 'pending', 'partid': 170, 'amount_msat': 2902801msat, 'parent_partid': 156}, {'status': 'pending', 'partid': 171, 'amount_msat': 2880237msat, 'parent_partid': 156}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 5, 'amount_msat': 8942693msat, 'parent_partid': 1}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 8, 'amount_msat': 8942693msat, 'parent_partid': 5}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 16, 'amount_msat': 8942693msat, 'parent_partid': 8}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 20, 'amount_msat': 8942693msat, 'parent_partid': 16}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 27, 'amount_msat': 8942693msat, 'parent_partid': 20}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 32, 'amount_msat': 8942693msat, 'parent_partid': 27}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 35, 'amount_msat': 8942693msat, 'parent_partid': 32}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 42, 'amount_msat': 8942693msat, 'parent_partid': 35}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 45, 'amount_msat': 8942693msat, 'parent_partid': 42}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 50, 'amount_msat': 8942693msat, 'parent_partid': 45}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 58, 'amount_msat': 8942693msat, 'parent_partid': 50}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 64, 'amount_msat': 4159394msat, 'parent_partid': 58}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 78, 'amount_msat': 4159394msat, 'parent_partid': 64}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 84, 'amount_msat': 4159394msat, 'parent_partid': 78}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 98, 'amount_msat': 4159394msat, 'parent_partid': 84}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 108, 'amount_msat': 4159394msat, 'parent_partid': 98}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 116, 'amount_msat': 4159394msat, 'parent_partid': 108}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 123, 'amount_msat': 4159394msat, 'parent_partid': 116}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 135, 'amount_msat': 4159394msat, 'parent_partid': 123}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 144, 'amount_msat': 4159394msat, 'parent_partid': 135}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 155, 'amount_msat': 4159394msat, 'parent_partid': 144}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 163, 'amount_msat': 4159394msat, 'parent_partid': 155}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 65, 'amount_msat': 4783299msat, 'parent_partid': 58}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 73, 'amount_msat': 4783299msat, 'parent_partid': 65}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 83, 'amount_msat': 4783299msat, 'parent_partid': 73}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 93, 'amount_msat': 4783299msat, 'parent_partid': 83}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 104, 'amount_msat': 4783299msat, 'parent_partid': 93}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 119, 'amount_msat': 4783299msat, 'parent_partid': 104}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 127, 'amount_msat': 4783299msat, 'parent_partid': 119}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 140, 'amount_msat': 4783299msat, 'parent_partid': 127}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 151, 'amount_msat': 4783299msat, 'parent_partid': 140}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 160, 'amount_msat': 4783299msat, 'parent_partid': 151}, {'status': 'pending', 'partid': 172, 'amount_msat': 4783299msat, 'parent_partid': 160}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 6, 'amount_msat': 4106668msat, 'parent_partid': 1}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 11, 'amount_msat': 4106668msat, 'parent_partid': 6}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 14, 'amount_msat': 4106668msat, 'parent_partid': 11}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 22, 'amount_msat': 4106668msat, 'parent_partid': 14}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 26, 'amount_msat': 4106668msat, 'parent_partid': 22}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 31, 'amount_msat': 4106668msat, 'parent_partid': 26}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 37, 'amount_msat': 4106668msat, 'parent_partid': 31}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 41, 'amount_msat': 4106668msat, 'parent_partid': 37}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 46, 'amount_msat': 4106668msat, 'parent_partid': 41}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 52, 'amount_msat': 4106668msat, 'parent_partid': 46}, {'status': 'pending', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 55, 'amount_msat': 4106668msat, 'parent_partid': 52}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 66, 'amount_msat': 2165538msat, 'parent_partid': 55}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 75, 'amount_msat': 2165538msat, 'parent_partid': 66}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 85, 'amount_msat': 2165538msat, 'parent_partid': 75}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 95, 'amount_msat': 2165538msat, 'parent_partid': 85}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 107, 'amount_msat': 2165538msat, 'parent_partid': 95}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 113, 'amount_msat': 2165538msat, 'parent_partid': 107}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 130, 'amount_msat': 2165538msat, 'parent_partid': 113}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 137, 'amount_msat': 2165538msat, 'parent_partid': 130}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 152, 'amount_msat': 2165538msat, 'parent_partid': 137}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 159, 'amount_msat': 2165538msat, 'parent_partid': 152}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 167, 'amount_msat': 2165538msat, 'parent_partid': 159}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 67, 'amount_msat': 1941130msat, 'parent_partid': 55}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 74, 'amount_msat': 1941130msat, 'parent_partid': 67}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 88, 'amount_msat': 1941130msat, 'parent_partid': 74}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 97, 'amount_msat': 1941130msat, 'parent_partid': 88}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 106, 'amount_msat': 1941130msat, 'parent_partid': 97}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 118, 'amount_msat': 1941130msat, 'parent_partid': 106}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 129, 'amount_msat': 1941130msat, 'parent_partid': 118}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 133, 'amount_msat': 1941130msat, 'parent_partid': 129}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 145, 'amount_msat': 1941130msat, 'parent_partid': 133}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 153, 'amount_msat': 1941130msat, 'parent_partid': 145}, {'status': 'failed', 'failreason': 'failed: WIRE_EXPIRY_TOO_SOON (reply from remote)', 'partid': 166, 'amount_msat': 1941130msat, 'parent_partid': 153}]} ``` Signed-off-by: Rusty Russell --- tests/test_opening.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index ada746c8295a..1033df533af6 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1390,6 +1390,8 @@ def test_zeroconf_forward(node_factory, bitcoind): l2.rpc.fundchannel(l3.info['id'], 10**6, mindepth=0) wait_for(lambda: l3.rpc.listincoming()['incoming'] != []) + # Make sure (esp in non-dev-mode) blockheights agree so we don't WIRE_EXPIRY_TOO_SOON... + sync_blockheight(bitcoind, [l1, l2, l3]) inv = l3.rpc.invoice(42 * 10**6, 'inv1', 'desc')['bolt11'] l1.rpc.pay(inv) From b6bf352503d3aa4e301a7d189103fdc65ce56a32 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 22 Jul 2022 12:25:31 -0500 Subject: [PATCH 1098/1530] contrib startup-regtest: turn off deprecated apis, update deprecated Update an out of date config, turn off deprecated apis by default for all regtest tests. (Makes sense, this is a devtool) --- contrib/startup_regtest.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index bf4d978c60fd..db04a3eb8fbf 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -84,6 +84,7 @@ start_nodes() { log-level=debug log-file=/tmp/l$i-$network/log addr=localhost:$socket + allow-deprecated-apis=false EOF # If we've configured to use developer, add dev options @@ -98,7 +99,7 @@ start_nodes() { funder-min-their-funding=10000 funder-per-channel-max=100000 funder-fuzz-percent=0 - lease-fee-base-msat=2sat + lease-fee-base-sat=2sat lease-fee-basis=50 EOF fi From bb4da47131c9c578fdea52640653b2d8bce62256 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 22 Jul 2022 12:26:00 -0500 Subject: [PATCH 1099/1530] msat: cleanup msat outputs for apis Don't use the _str() option for msat outputs, use the built-in helpers which are deprecation aware. Fixes #5447 Reported-By: @fiatjaf --- plugins/pay.c | 7 +++---- plugins/spender/multifundchannel.c | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 0e309b33be16..fc39b8bb3a90 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -399,11 +399,10 @@ static void add_new_entry(struct json_stream *ret, /* This is only tallied for pending and successful payments, not * failures. */ if (pm->amount != NULL && pm->num_nonfailed_parts > 0) - json_add_string(ret, "amount_msat", - fmt_amount_msat(tmpctx, *pm->amount)); + json_add_amount_msat_only(ret, "amount_msat", *pm->amount); - json_add_string(ret, "amount_sent_msat", - fmt_amount_msat(tmpctx, pm->amount_sent)); + json_add_amount_msat_only(ret, "amount_sent_msat", + pm->amount_sent); if (pm->num_nonfailed_parts > 1) json_add_u64(ret, "number_of_parts", diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 4b9c1c6c33fe..e67baed0eff7 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1117,8 +1117,8 @@ fundchannel_start_dest(struct multifundchannel_destination *dest) json_add_string(req->js, "feerate", mfc->feerate_str); json_add_bool(req->js, "announce", dest->announce); - json_add_string(req->js, "push_msat", - fmt_amount_msat(tmpctx, dest->push_msat)); + json_add_amount_msat_only(req->js, "push_msat", dest->push_msat); + if (dest->close_to_str) json_add_string(req->js, "close_to", dest->close_to_str); From bed00754adc5f410232ac0537c32029e4f3f0ea7 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 21 Jul 2022 14:22:50 -0500 Subject: [PATCH 1100/1530] test-flake: dont let `l1` send their unilateral tx `l1` got their tx in before `l2`, but we're waiting for `l2`'s commitment tx. (l2 sends an error message to l1 when we call dev-fail, l1 broadcasts their commitment tx when they get the error) Instead, we let l1 send their commitment tx, except we blackhole it. ``` l2.rpc.dev_fail(l1.info['id']) l2.daemon.wait_for_log('Failing due to dev-fail command') > l2.wait_for_channel_onchain(l1.info['id']) tests/test_connection.py:2275: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-testing/pyln/testing/utils.py:1043: in wait_for_channel_onchain wait_for(lambda: txid inself.bitcoin.rpc.getrawmempool()) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ success = . at 0x7f0f5f7577a0> timeout = 900 defwait_for(success, timeout=TIMEOUT): start_time = time.time() interval = 0.25 whilenot success(): time_left = start_time + timeout - time.time() if time_left <= 0: > raiseValueError("Timeout while waiting for {}", success) E ValueError: ('Timeout while waiting for {}', . at 0x7f0f5f7577a0>) contrib/pyln-testing/pyln/testing/utils.py:93: ValueError ``` --- tests/test_connection.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 4a8a04dce94f..53c08223ca92 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2269,6 +2269,12 @@ def test_channel_persistence(node_factory, bitcoind, executor): l1.restart() assert only_one(l1.rpc.listpeers()['peers'][0]['channels'])['to_us_msat'] == 99980000 + # Keep l1 from sending its onchain tx + def censoring_sendrawtx(r): + return {'id': r['id'], 'result': {}} + + l1.daemon.rpcproxy.mock_rpc('sendrawtransaction', censoring_sendrawtx) + # Now make sure l1 is watching for unilateral closes l2.rpc.dev_fail(l1.info['id']) l2.daemon.wait_for_log('Failing due to dev-fail command') From 9adf5f17de900a88f93b055a5ef14f01db11c02e Mon Sep 17 00:00:00 2001 From: niftynei Date: Sat, 23 Jul 2022 10:42:55 -0500 Subject: [PATCH 1101/1530] tests:redirect output, so test log passes --- tests/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 83862b994cb8..924960e19399 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1727,7 +1727,7 @@ def mock_fail(*args): l1.daemon.rpcproxy.mock_rpc('getblockhash', mock_fail) l1.daemon.rpcproxy.mock_rpc('estimatesmartfee', mock_fail) - l1.daemon.start(wait_for_initialized=False) + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) l1.daemon.wait_for_logs([r'getblockhash [a-z0-9]* exited with status 1', r'Unable to estimate opening fees', r'BROKEN.*we have been retrying command for --bitcoin-retry-timeout=60 seconds']) From 4cc0da743286351063d566c11a0d44790912f720 Mon Sep 17 00:00:00 2001 From: niftynei Date: Sat, 23 Jul 2022 10:42:20 -0500 Subject: [PATCH 1102/1530] nit: speedup retry timeout test --- tests/test_misc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 924960e19399..263c9e4392f6 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1716,9 +1716,11 @@ def test_bitcoind_fail_first(node_factory, bitcoind): """ # Do not start the lightning node since we need to instrument bitcoind # first. + timeout = 5 if 5 < TIMEOUT // 3 else TIMEOUT // 3 l1 = node_factory.get_node(start=False, allow_broken_log=True, - may_fail=True) + may_fail=True, + options={'bitcoin-retry-timeout': timeout}) # Instrument bitcoind to fail some queries first. def mock_fail(*args): @@ -1730,7 +1732,7 @@ def mock_fail(*args): l1.daemon.start(wait_for_initialized=False, stderr_redir=True) l1.daemon.wait_for_logs([r'getblockhash [a-z0-9]* exited with status 1', r'Unable to estimate opening fees', - r'BROKEN.*we have been retrying command for --bitcoin-retry-timeout=60 seconds']) + r'BROKEN.*we have been retrying command for --bitcoin-retry-timeout={} seconds'.format(timeout)]) # Will exit with failure code. assert l1.daemon.wait() == 1 From 7bbfef5054ec222d6675b52fa6953e06aba6fef0 Mon Sep 17 00:00:00 2001 From: niftynei Date: Sat, 23 Jul 2022 11:21:31 -0500 Subject: [PATCH 1103/1530] tests: flake fix; l1 was waiting too long to reconnect We were waiting too long for the reconnect to happen (60s default), which caused this test to timeout. When testing, let's speed up the reconnect. L2 tried to reconnect but didn't have connection information in its gossip -- is there a way to ask/save connection data from a node you're making a channel with that doesn't rely on their node_announcement? --- lightningd/connect_control.c | 4 +++- lightningd/connect_control.h | 5 +++++ lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 +++ lightningd/options.c | 4 ++++ tests/test_misc.py | 4 ++-- 6 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 3370200e2f96..36ec83bdcced 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -364,8 +364,10 @@ static void connect_failed(struct lightningd *ld, u32 delay; if (seconds_to_delay) delay = *seconds_to_delay; + else if (peer->delay_reconnect) + delay = DEV_FAST_RECONNECT(ld->dev_fast_reconnect, 3, 60); else - delay = peer->delay_reconnect ? 60 : 1; + delay = 1; log_peer_debug(ld->log, id, "Reconnecting in %u seconds", delay); try_reconnect(peer, peer, delay, addrhint); } else diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 4a3adb07bef2..5eaa3c98daff 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -3,12 +3,17 @@ #include "config.h" #include #include +#include struct lightningd; struct peer; struct pubkey; struct wireaddr_internal; +/* Speedy reconnect timeout! */ +#define DEV_FAST_RECONNECT(dev_fast_reconnect_flag, fast, normal) \ + IFDEV((dev_fast_reconnect_flag) ? (fast) : (normal), (normal)) + /* Returns fd for gossipd to talk to connectd */ int connectd_init(struct lightningd *ld); void connectd_activate(struct lightningd *ld); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index e15a0309a301..eccf43800d30 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -125,6 +125,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->dev_gossip_time = 0; ld->dev_fast_gossip = false; ld->dev_fast_gossip_prune = false; + ld->dev_fast_reconnect = false; ld->dev_force_privkey = NULL; ld->dev_force_bip32_seed = NULL; ld->dev_force_channel_secrets = NULL; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 5440384b756f..401dba808015 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -244,6 +244,9 @@ struct lightningd { bool dev_fast_gossip; bool dev_fast_gossip_prune; + /* Speedup reconnect delay, for testing. */ + bool dev_fast_reconnect; + /* This is the forced private key for the node. */ struct privkey *dev_force_privkey; diff --git a/lightningd/options.c b/lightningd/options.c index 7fc77e26b131..45a01ca45052 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -691,6 +691,10 @@ static void dev_register_opts(struct lightningd *ld) opt_register_noarg("--dev-no-reconnect", opt_set_invbool, &ld->reconnect, "Disable automatic reconnect-attempts by this node, but accept incoming"); + opt_register_noarg("--dev-fast-reconnect", opt_set_bool, + &ld->dev_fast_reconnect, + "Make default reconnect delay 3 (not 60) seconds"); + opt_register_noarg("--dev-fail-on-subdaemon-fail", opt_set_bool, &ld->dev_subdaemon_fail, opt_hidden); opt_register_arg("--dev-disconnect=", opt_subd_dev_disconnect, diff --git a/tests/test_misc.py b/tests/test_misc.py index 263c9e4392f6..8607a6c9d1c1 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1132,7 +1132,7 @@ def test_funding_reorg_private(node_factory, bitcoind): l2.daemon.wait_for_log(r'Deleting channel') -@pytest.mark.developer("needs DEVELOPER=1") +@pytest.mark.developer("needs DEVELOPER=1", "uses --dev-fast-reconnect") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_funding_reorg_remote_lags(node_factory, bitcoind): @@ -1140,7 +1140,7 @@ def test_funding_reorg_remote_lags(node_factory, bitcoind): """ # may_reconnect so channeld will restart; bad gossip can happen due to reorg opts = {'funding-confirms': 1, 'may_reconnect': True, 'allow_bad_gossip': True, - 'allow_warning': True} + 'allow_warning': True, 'dev-fast-reconnect': None} l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=opts) l1.fundwallet(10000000) sync_blockheight(bitcoind, [l1]) # height 102 From 8f6afedafe1af00eb91ab42ad5c09b3cfccbe0b4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 16:29:09 +0930 Subject: [PATCH 1104/1530] fuzz: fix fuzzing compilation. It had bitrotted. Signed-off-by: Rusty Russell --- Makefile | 2 +- tests/fuzz/Makefile | 4 +++- tests/fuzz/fuzz-close_tx.c | 2 +- tests/fuzz/fuzz-hsm_encryption.c | 3 ++- tests/fuzz/fuzz-initial_channel.c | 1 + 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 83e9d4e0fbfa..6730f97cdafe 100644 --- a/Makefile +++ b/Makefile @@ -661,7 +661,7 @@ $(ALL_PROGRAMS) $(ALL_TEST_PROGRAMS): # which brings its own main(). FUZZ_LDFLAGS = -fsanitize=fuzzer $(ALL_FUZZ_TARGETS): - @$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) $(FUZZ_LDFLAGS) -o $@) + @$(call VERBOSE, "ld $@", $(LINK.o) $(filter-out %.a,$^) $(LOADLIBES) $(EXTERNAL_LDLIBS) $(LDLIBS) libccan.a $(FUZZ_LDFLAGS) -o $@) # Everything depends on the CCAN headers, and Makefile diff --git a/tests/fuzz/Makefile b/tests/fuzz/Makefile index 6a6c35fa7798..cf980ea1c536 100644 --- a/tests/fuzz/Makefile +++ b/tests/fuzz/Makefile @@ -19,6 +19,7 @@ FUZZ_COMMON_OBJS := \ common/blockheight_states.o \ common/channel_config.o \ common/close_tx.o \ + common/configdir.o \ common/channel_id.o \ common/channel_type.o \ common/daemon.o \ @@ -32,13 +33,14 @@ FUZZ_COMMON_OBJS := \ common/permute_tx.o \ common/initial_channel.o \ common/initial_commit_tx.o \ - common/json.o \ + common/json_parse_simple.o \ common/json_stream.o \ common/key_derive.o \ common/keyset.o \ common/msg_queue.o \ common/memleak.o \ common/node_id.o \ + common/psbt_keypath.o \ common/wireaddr.o \ common/setup.o \ common/status.o \ diff --git a/tests/fuzz/fuzz-close_tx.c b/tests/fuzz/fuzz-close_tx.c index a0919d3bfd33..271515068bf4 100644 --- a/tests/fuzz/fuzz-close_tx.c +++ b/tests/fuzz/fuzz-close_tx.c @@ -77,7 +77,7 @@ void run(const uint8_t *data, size_t size) PUBKEY_CMPR_LEN, pk2); funding_script = bitcoin_redeem_2of2(tmpctx, pk1, pk2); - create_close_tx(tmpctx, chainparams, our_script, + create_close_tx(tmpctx, chainparams, NULL, NULL, our_script, their_script, funding_script, &outpoint, funding, to_us, to_them, dust_limit); diff --git a/tests/fuzz/fuzz-hsm_encryption.c b/tests/fuzz/fuzz-hsm_encryption.c index 1f0a861e2500..d0a18f736136 100644 --- a/tests/fuzz/fuzz-hsm_encryption.c +++ b/tests/fuzz/fuzz-hsm_encryption.c @@ -17,6 +17,7 @@ void run(const uint8_t *data, size_t size) struct secret *hsm_secret, decrypted_hsm_secret, encryption_key; char *passphrase; struct encrypted_hsm_secret encrypted_secret; + char *emsg; /* Take the first 32 bytes as the plaintext hsm_secret seed, * and the remaining ones as the passphrase. */ @@ -24,7 +25,7 @@ void run(const uint8_t *data, size_t size) passphrase = to_string(NULL, data + 32, size - 32); /* A valid seed, a valid passphrase. This should not fail. */ - assert(!hsm_secret_encryption_key(passphrase, &encryption_key)); + assert(!hsm_secret_encryption_key_with_exitcode(passphrase, &encryption_key, &emsg)); /* Roundtrip */ assert(encrypt_hsm_secret(&encryption_key, hsm_secret, &encrypted_secret)); diff --git a/tests/fuzz/fuzz-initial_channel.c b/tests/fuzz/fuzz-initial_channel.c index 0eed31095a10..e76b1fc15dde 100644 --- a/tests/fuzz/fuzz-initial_channel.c +++ b/tests/fuzz/fuzz-initial_channel.c @@ -95,6 +95,7 @@ void run(const uint8_t *data, size_t size) wumbo, opener); /* TODO: make initial_channel_tx() work with ASAN.. */ + (void)channel; } clean_tmpctx(); From d0c321b43a26596ff2a47f4d8ca808816f5a3b7e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 16:30:01 +0930 Subject: [PATCH 1105/1530] CI: fix CI scripts to fail if a command fails. If compilation failed, we didn't stop (though except for fuzzing, we would fail when we try to run the tests). Also use make -s instead of redirecting make ooutput. Signed-off-by: Rusty Russell --- .github/scripts/build.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 7ac1279dfcef..17e4c90fd3bf 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -21,6 +21,9 @@ export VALGRIND=${VALGRIND:-0} export FUZZING=${FUZZING:-0} export LIGHTNINGD_POSTGRES_NO_VACUUM=1 +# Fail if any commands fail. +set -e + pip3 install --user poetry poetry config virtualenvs.create false --local poetry install @@ -89,7 +92,7 @@ then ./configure CC="$TARGET_HOST-gcc" --enable-static - make -j32 CC="$TARGET_HOST-gcc" > /dev/null + make -s -j32 CC="$TARGET_HOST-gcc" else eatmydata make -j32 # shellcheck disable=SC2086 From 0fd8a6492e1e233aa03dc5246b044a58a01cb309 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 13:33:59 +0930 Subject: [PATCH 1106/1530] lightningd: fix fatal() log message in log. The one to stderr is fine, the log one gets corrupted, like so: ``` 2022-07-24T07:20:08.6250702Z lightningd-2 2022-07-24T06:49:19.494Z **BROKEN** lightningd: Plugin '????UH??SH??8H?}?H?u?H?U?H?M?H?M?H?E?H?????' returned an invalid response to the db_write hook: (F???U ``` Signed-off-by: Rusty Russell --- lightningd/log.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lightningd/log.c b/lightningd/log.c index dd44636b101c..ff7f1602eb26 100644 --- a/lightningd/log.c +++ b/lightningd/log.c @@ -840,14 +840,20 @@ void log_backtrace_exit(void) void fatal_vfmt(const char *fmt, va_list ap) { + va_list ap2; + + /* You are not allowed to re-use va_lists, so make a copy. */ + va_copy(ap2, ap); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); if (!crashlog) exit(1); - logv(crashlog, LOG_BROKEN, NULL, true, fmt, ap); + logv(crashlog, LOG_BROKEN, NULL, true, fmt, ap2); abort(); + /* va_copy() must be matched with va_end(), even if unreachable. */ + va_end(ap2); } void fatal(const char *fmt, ...) From da4e33cd0d17b84779aa2257e1df30f4fade6bde Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 10:53:30 +0930 Subject: [PATCH 1107/1530] decode: fix crash when decoding invalid rune. If rune contains invalid UTF-8, offers (which implements decode) would produce JSON with invalid UTF-8, which causes lightningd to complain and kill it, and then die because it's an important plugin. So don't decode invalid UTF-8! Reported-by: @jb55 Signed-off-by: Rusty Russell --- doc/lightning-decode.7.md | 12 ++++++-- doc/schemas/decode.schema.json | 50 +++++++++++++++++++++++++++++++++- plugins/offers.c | 17 +++++++++++- tests/test_plugin.py | 21 +++++++++++++- 4 files changed, 94 insertions(+), 6 deletions(-) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 01b3a3c6ea4e..0bd81ad05825 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -163,7 +163,8 @@ If **type** is "bolt11 invoice", and **valid** is *true*: - **tag** (string): The bech32 letter which identifies this field (always 1 characters) - **data** (string): The bech32 data for this field -If **type** is "rune": +If **type** is "rune", and **valid** is *true*: + - **valid** (boolean) (always *true*) - **string** (string): the string encoding of the rune - **restrictions** (array of objects): restrictions built into the rune: all must pass: - **alternatives** (array of strings): each way restriction can be met: any can pass: @@ -171,7 +172,12 @@ If **type** is "rune": - **summary** (string): human-readable summary of this restriction - **unique_id** (string, optional): unique id (always a numeric id on runes we create) - **version** (string, optional): rune version, not currently set on runes we create - - **valid** (boolean, optional) (always *true*) + +If **type** is "rune", and **valid** is *false*: + - **valid** (boolean) (always *false*) + - **hex** (hex, optional): the raw rune in hex + - the following warnings are possible: + - **warning_rune_invalid_utf8**: the rune contains invalid UTF-8 strings [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -195,4 +201,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d1e1f044c2e67ec169728dbc551903c97f9a9daa1f42e9d2f1686fc692d25be8) +[comment]: # ( SHA256STAMP:a3963c3e0061b0d42a1f9e2f2a9012df780fce0264c6785f0311909b01f78af2) diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json index 1ff631d28882..70d3054602c3 100644 --- a/doc/schemas/decode.schema.json +++ b/doc/schemas/decode.schema.json @@ -919,13 +919,20 @@ "enum": [ "rune" ] + }, + "valid": { + "type": "boolean", + "enum": [ + true + ] } } }, "then": { "required": [ "string", - "restrictions" + "restrictions", + "valid" ], "additionalProperties": false, "properties": { @@ -976,6 +983,47 @@ } } } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "rune" + ] + }, + "valid": { + "type": "boolean", + "enum": [ + false + ] + } + } + }, + "then": { + "required": [ + "valid" + ], + "additionalProperties": false, + "properties": { + "valid": { + "type": "boolean", + "enum": [ + false + ] + }, + "type": {}, + "warning_rune_invalid_utf8": { + "type": "string", + "description": "the rune contains invalid UTF-8 strings" + }, + "hex": { + "type": "hex", + "description": "the raw rune in hex" + } + } + } } ] } diff --git a/plugins/offers.c b/plugins/offers.c index 72442234c112..a1495bd2f9c6 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -819,11 +819,26 @@ static void json_add_invoice_request(struct json_stream *js, static void json_add_rune(struct command *cmd, struct json_stream *js, const struct rune *rune) { + const char *string; + + /* Simplest to check everything for UTF-8 compliance at once. + * Since separators are | and & (which cannot appear inside + * UTF-8 multichars), if the entire thing is valid UTF-8 then + * each part is. */ + string = rune_to_string(tmpctx, rune); + if (!utf8_check(string, strlen(string))) { + json_add_hex(js, "hex", string, strlen(string)); + json_add_string(js, "warning_rune_invalid_utf8", + "Rune contains invalid UTF-8 strings"); + json_add_bool(js, "valid", false); + return; + } + if (rune->unique_id) json_add_string(js, "unique_id", rune->unique_id); if (rune->version) json_add_string(js, "version", rune->version); - json_add_string(js, "string", take(rune_to_string(NULL, rune))); + json_add_string(js, "string", take(string)); json_array_start(js, "restrictions"); for (size_t i = rune->unique_id ? 1 : 0; i < tal_count(rune->restrs); i++) { diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 92157a50ca36..ac8bbda9fbbe 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -13,6 +13,7 @@ ) import ast +import base64 import concurrent.futures import json import os @@ -2806,7 +2807,6 @@ def test_commando_rune(node_factory): 'params': params}) -@pytest.mark.slow_test def test_commando_stress(node_factory, executor): """Stress test to slam commando with many large queries""" nodes = node_factory.get_nodes(5) @@ -2837,3 +2837,22 @@ def test_commando_stress(node_factory, executor): # Should have exactly one discard msg from each discard nodes[0].daemon.wait_for_logs([r"New cmd from .*, replacing old"] * discards) + + +def test_commando_badrune(node_factory): + """Test invalid UTF-8 encodings in rune: used to make us kill the offers plugin which implements decode, as it gave bad utf8!""" + l1 = node_factory.get_node() + l1.rpc.decode('5zi6-ugA6hC4_XZ0R7snl5IuiQX4ugL4gm9BQKYaKUU9gCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl') + rune = l1.rpc.commando_rune(restrictions="readonly") + + binrune = base64.urlsafe_b64decode(rune['rune']) + # Mangle each part, try decode. Skip most of the boring chars + # (just '|', '&', '#'). + for i in range(32, len(binrune)): + for span in (range(0, 32), (124, 38, 35), range(127, 256)): + for c in span: + modrune = binrune[:i] + bytes([c]) + binrune[i + 1:] + try: + l1.rpc.decode(base64.urlsafe_b64encode(modrune).decode('utf8')) + except RpcError: + pass From 9498e14530fea53167b4fa2488446643b60e7595 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 24 Jul 2022 15:48:55 +0930 Subject: [PATCH 1108/1530] connectd: two logging cleanups. Don't log_io final messages twice (multiplex_final_message already does this, so it's confusing to see us send e.g. WIRE_ERROR twice!). And report that the peer has failed to connect out *before* telling lightningd, otherwise we get a very confusing ordering, e.g.: ``` 2022-07-23T05:17:36.096Z DEBUG 027d0de66d08f956a8d606c0d1c34e59bda38c05a3b1cc738fdd6378716c644997-lightningd: Reconnecting in 4 seconds 2022-07-23T05:17:36.096Z DEBUG 027d0de66d08f956a8d606c0d1c34e59bda38c05a3b1cc738fdd6378716c644997-lightningd: Will try reconnect in 4 seconds 2022-07-23T05:17:36.096Z DEBUG 027d0de66d08f956a8d606c0d1c34e59bda38c05a3b1cc738fdd6378716c644997-connectd: Failed connected out: ``` Signed-off-by: Rusty Russell --- connectd/connectd.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index ac52cc42175f..dbf03d039bec 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -652,6 +652,8 @@ static void connect_failed(struct daemon *daemon, if (wait_seconds < INITIAL_WAIT_SECONDS) wait_seconds = INITIAL_WAIT_SECONDS; + status_peer_debug(id, "Failed connected out: %s", errmsg); + /* lightningd may have a connect command waiting to know what * happened. We leave it to lightningd to decide if it wants to try * again, with the wait_seconds as a hint of how long before @@ -659,8 +661,6 @@ static void connect_failed(struct daemon *daemon, msg = towire_connectd_connect_failed(NULL, id, errcode, errmsg, wait_seconds, addrhint); daemon_conn_send(daemon->master, take(msg)); - - status_peer_debug(id, "Failed connected out: %s", errmsg); } /* add errors to error list */ @@ -1887,11 +1887,8 @@ static void peer_final_msg(struct io_conn *conn, /* This can happen if peer hung up on us (or wrong counter * if it reconnected). */ peer = peer_htable_get(&daemon->peers, &id); - if (peer && peer->counter == counter) { - /* Log message for peer. */ - status_peer_io(LOG_IO_OUT, &id, finalmsg); + if (peer && peer->counter == counter) multiplex_final_msg(peer, take(finalmsg)); - } } #if DEVELOPER From 1480257644dfd8cda2405678b9b7be8c6629254c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Jul 2022 13:41:59 +0930 Subject: [PATCH 1109/1530] pytest: set dblog-file when adding the dblog plugin (TEST_CHECK_DBSTMTS=1) As we'll see in the next patch, this wasn't *supposed* to work wihtout dblog-file, but it did, creating a dblog called "null". Signed-off-by: Rusty Russell --- tests/fixtures.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/fixtures.py b/tests/fixtures.py index 84258367d445..04f691d92331 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -40,6 +40,7 @@ def __init__(self, *args, **kwargs): if not has_dblog: # Add as an expanded option so we don't clobber other options. self.daemon.opts['plugin={}'.format(dblog)] = None + self.daemon.opts['dblog-file'] = 'dblog.sqlite3' # Yes, we really want to test the local development version, not # something in out path. From 008a59b004486053c07bc269a928da00e807bc74 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Jul 2022 13:42:03 +0930 Subject: [PATCH 1110/1530] lightningd: ignore default if it's a literal 'null' JSON token. I wondered how `tests/plugins/dblog.py` worked, since it is supposed to fail unless the `dblog-file` arg is set: ``` @plugin.init() def init(configuration, options, plugin): if not plugin.get_option('dblog-file'): raise RpcError("No dblog-file specified") ``` But it was set to "null". That's because 'None' in python is turned into a literal JSON "null", and we take that as the default value. We also cleanup the popt->description double-assignment (a leftover from when this was optional). Signed-off-by: Rusty Russell Changelog-Fixed: plugins: setting the default value of a parameter to `null` is the same as not setting it (pyln plugins did this!). --- lightningd/plugin.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index f69258633b2c..ea9fb5c75872 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -940,7 +940,7 @@ static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, if (strchr(popt->name, '|')) return tal_fmt(plugin, "Option \"name\" may not contain '|'"); - popt->description = NULL; + popt->description = json_strdup(popt, buffer, desctok); if (deptok) { if (!json_to_bool(buffer, deptok, &popt->deprecated)) return tal_fmt(plugin, @@ -981,7 +981,7 @@ static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, "Only \"string\", \"int\", \"bool\", and \"flag\" options are supported"); } - if (defaulttok) { + if (defaulttok && !json_tok_is_null(buffer, defaulttok)) { popt->def = plugin_opt_value(popt, popt->type, json_strdup(tmpctx, buffer, defaulttok)); if (!popt->def) @@ -991,8 +991,6 @@ static const char *plugin_opt_add(struct plugin *plugin, const char *buffer, popt->type); } - if (!popt->description) - popt->description = json_strdup(popt, buffer, desctok); list_add_tail(&plugin->plugin_opts, &popt->list); From 8da361b49b4a989d3a9ed16bb1151e7c9effe4a4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Jul 2022 13:42:03 +0930 Subject: [PATCH 1111/1530] pytest: fix flake in test_channel_persistence w/ TEST_CHECK_DBSTMTS This was weird. Here is the message (with \n turned into real new lines): ``` 2022-07-24T07:20:08.9144998Z Plugin '/home/runner/work/lightning/lightning/tests/plugins/dblog.py' returned an invalid response to the db_write hook: {"jsonrpc": "2.0", "id": 40, "error": {"code": -32600, "message": "Error while processing db_write: UNIQUE constraint failed: shachain_known.shachain_id, shachain_known.pos", "traceback": "Traceback (most recent call last): File \"/home/runner/work/lightning/lightning/contrib/pyln-client/pyln/client/plugin.py\", line 631, in _dispatch_request result = self._exec_func(method.func, request) File \"/home/runner/work/lightning/lightning/contrib/pyln-client/pyln/client/plugin.py\", line 616, in _exec_func return func(*ba.args, **ba.kwargs) File \"/home/runner/work/lightning/lightning/tests/plugins/dblog.py\", line 45, in db_write plugin.conn.execute(c) sqlite3.IntegrityError: UNIQUE constraint failed: shachain_known.shachain_id, shachain_known.pos "}} ``` Finally, I realized that we *kill* l2: this means it has updated the plugin db but not the real db. This is expected: a real backup plugin would handle this case. Simply disable the test for this case. Signed-off-by: Rusty Russell --- tests/test_connection.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index 53c08223ca92..8d867a8af356 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2203,6 +2203,8 @@ def test_funding_while_offline(node_factory, bitcoind): @pytest.mark.developer @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') +@unittest.skipIf(os.environ.get("TEST_CHECK_DBSTMTS", None) == "1", + "We kill l2, dblog plugin replay will be unreliable") def test_channel_persistence(node_factory, bitcoind, executor): # Start two nodes and open a channel (to remember). l2 will # mysteriously die while committing the first HTLC so we can From 9aa9a8236f47f5721014546d051d1fc28c9758df Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Jul 2022 13:42:03 +0930 Subject: [PATCH 1112/1530] commando: free incmd as soon as we use it. Otherwise we left it in the cache, causing "New cmd replacing old" messages. --- plugins/commando.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/commando.c b/plugins/commando.c index c2b32239e635..83379aafbf74 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -485,6 +485,7 @@ static void handle_incmd(struct node_id *peer, } try_command(peer, idnum, incmd->contents, tal_bytelen(incmd->contents)); + tal_free(incmd); } static struct command_result *handle_reply(struct node_id *peer, From 0a9a87ec10a6f88e03d6117d4ea57c9d2c27fc62 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Jul 2022 13:42:03 +0930 Subject: [PATCH 1113/1530] pytest: fix test_commando_stress On fast machines, we don't get failures sometimes on commando commands. (*But* we still got "New cmd replacing old" messages, which is how I realized we weren't freeing them promptly, hence the previous fix). ``` # Should have exactly one discard msg from each discard > nodes[0].daemon.wait_for_logs([r"New cmd from .*, replacing old"] * discards) tests/test_plugin.py:2839: ... > raise TimeoutError('Unable to find "{}" in logs.'.format(exs)) E TimeoutError: Unable to find "[]" in logs. ``` Signed-off-by: Rusty Russell --- tests/test_plugin.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ac8bbda9fbbe..fb0790d61c4e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2835,8 +2835,12 @@ def test_commando_stress(node_factory, executor): assert(e.error['message'] == "Invalid JSON") discards += 1 - # Should have exactly one discard msg from each discard - nodes[0].daemon.wait_for_logs([r"New cmd from .*, replacing old"] * discards) + # Should have at least one discard msg from each failure (we can have + # more, if they kept replacing each other, as happens!) + if discards > 0: + nodes[0].daemon.wait_for_logs([r"New cmd from .*, replacing old"] * discards) + else: + assert not nodes[0].daemon.is_in_log(r"New cmd from .*, replacing old") def test_commando_badrune(node_factory): From 85180dbfeeee2ce3f159551b340e481339fd0944 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Jul 2022 13:53:50 +0930 Subject: [PATCH 1114/1530] pytest: fix flake in test_feerates As the comment in set_feerates says: "Technically, this waits until it's called, not until it's processed.". And the wait_for() line doesn't work, since that condition is already true. ``` @unittest.skipIf(TEST_NETWORK == 'liquid-regtest', "Fees on elements are different") @unittest.skipIf( not DEVELOPER or DEPRECATED_APIS, "Without DEVELOPER=1 we snap to " "FEERATE_FLOOR on testnets, and we test the new API." ) def test_feerates(node_factory): l1 = node_factory.get_node(options={'log-level': 'io', 'dev-no-fake-fees': True}, start=False) l1.daemon.rpcproxy.mock_rpc('estimatesmartfee', { 'error': {"errors": ["Insufficient data or no feerate found"], "blocks": 0} }) l1.start() # All estimation types types = ["opening", "mutual_close", "unilateral_close", "delayed_to_us", "htlc_resolution", "penalty"] # Try parsing the feerates, won't work because can't estimate for t in types: with pytest.raises(RpcError, match=r'Cannot estimate fees'): feerate = l1.rpc.parsefeerate(t) # Query feerates (shouldn't give any!) wait_for(lambda: len(l1.rpc.feerates('perkw')['perkw']) == 2) feerates = l1.rpc.feerates('perkw') assert feerates['warning_missing_feerates'] == 'Some fee estimates unavailable: bitcoind startup?' assert 'perkb' not in feerates assert feerates['perkw']['max_acceptable'] == 2**32 - 1 assert feerates['perkw']['min_acceptable'] == 253 for t in types: assert t not in feerates['perkw'] wait_for(lambda: len(l1.rpc.feerates('perkb')['perkb']) == 2) feerates = l1.rpc.feerates('perkb') assert feerates['warning_missing_feerates'] == 'Some fee estimates unavailable: bitcoind startup?' assert 'perkw' not in feerates assert feerates['perkb']['max_acceptable'] == (2**32 - 1) assert feerates['perkb']['min_acceptable'] == 253 * 4 for t in types: assert t not in feerates['perkb'] # Now try setting them, one at a time. # Set CONSERVATIVE/2 feerate, for max l1.set_feerates((15000, 0, 0, 0), True) wait_for(lambda: len(l1.rpc.feerates('perkw')['perkw']) == 2) feerates = l1.rpc.feerates('perkw') assert feerates['warning_missing_feerates'] == 'Some fee estimates unavailable: bitcoind startup?' assert 'perkb' not in feerates > assert feerates['perkw']['max_acceptable'] == 15000 * 10 E assert 4294967295 == (15000 * 10) tests/test_misc.py:1392: AssertionError ``` Signed-off-by: Rusty Russell --- tests/test_misc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 8607a6c9d1c1..2f67ee17bfac 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1384,11 +1384,10 @@ def test_feerates(node_factory): # Now try setting them, one at a time. # Set CONSERVATIVE/2 feerate, for max l1.set_feerates((15000, 0, 0, 0), True) - wait_for(lambda: len(l1.rpc.feerates('perkw')['perkw']) == 2) + wait_for(lambda: l1.rpc.feerates('perkw')['perkw']['max_acceptable'] == 15000 * 10) feerates = l1.rpc.feerates('perkw') assert feerates['warning_missing_feerates'] == 'Some fee estimates unavailable: bitcoind startup?' assert 'perkb' not in feerates - assert feerates['perkw']['max_acceptable'] == 15000 * 10 assert feerates['perkw']['min_acceptable'] == 253 # Set ECONOMICAL/6 feerate, for unilateral_close and htlc_resolution From 17b9bd5ca366dbf9bbb1e10f083945f98fcae8f1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 26 Jul 2022 14:22:53 +0930 Subject: [PATCH 1115/1530] pytest: fix test_commando_rune flake. We reset counters every minute, so ratelimit tests can flake since we might hit that boundary. Instead, wait for the reset then test explicitly, assuming that takes less than 60 seconds. ``` for rune, cmd, params in failures: print("{} {}".format(cmd, params)) with pytest.raises(RpcError, match='Not authorized:') as exc_info: l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], 'rune': rune['rune'], 'method': cmd, > 'params': params}) E Failed: DID NOT RAISE ... DEBUG:root:Calling commando with payload {'peer_id': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518', 'rune': 'O8Zr-ULTBKO3_pKYz0QKE9xYl1vQ4Xx9PtlHuist9Rk9NCZwbnVtPTAmcmF0ZT0zJnJhdGU9MQ==', 'method': 'getinfo', 'params': {}} ``` Signed-off-by: Rusty Russell --- tests/test_plugin.py | 59 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index fb0790d61c4e..67b242bc1266 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2739,9 +2739,7 @@ def test_commando_rune(node_factory): # Replace rune3 with a more useful timestamp! expiry = int(time.time()) + 15 rune3 = l1.rpc.commando_rune(restrictions="time<{}".format(expiry)) - ratelimit_successes = ((rune9, "getinfo", {}), - (rune8, "getinfo", {}), - (rune8, "getinfo", {})) + successes = ((rune1, "listpeers", {}), (rune2, "listpeers", {}), (rune2, "getinfo", {}), @@ -2753,7 +2751,11 @@ def test_commando_rune(node_factory): (rune6, "listpeers", [l2.info['id'], 'broken']), (rune6, "listpeers", [l2.info['id']]), (rune7, "listpeers", []), - (rune7, "getinfo", {})) + ratelimit_successes + (rune7, "getinfo", {}), + (rune9, "getinfo", {}), + (rune8, "getinfo", {}), + (rune8, "getinfo", {})) + failures = ((rune2, "withdraw", {}), (rune2, "plugin", {'subcommand': 'list'}), (rune3, "getinfo", {}), @@ -2761,9 +2763,7 @@ def test_commando_rune(node_factory): (rune5, "listpeers", {'id': l2.info['id'], 'level': 'io'}), (rune6, "listpeers", [l2.info['id'], 'io']), (rune7, "listpeers", [l2.info['id']]), - (rune7, "listpeers", {'id': l2.info['id']}), - (rune9, "getinfo", {}), - (rune8, "getinfo", {})) + (rune7, "listpeers", {'id': l2.info['id']})) for rune, cmd, params in successes: l2.rpc.call(method='commando', @@ -2785,6 +2785,47 @@ def test_commando_rune(node_factory): 'params': params}) assert exc_info.value.error['code'] == 0x4c51 + # Now, this can flake if we cross a minute boundary! So wait until + # It succeeds again. + while True: + try: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune8['rune'], + 'method': 'getinfo', + 'params': {}}) + break + except RpcError as e: + assert e.error['code'] == 0x4c51 + time.sleep(1) + + # This fails immediately, since we've done one. + with pytest.raises(RpcError, match='Not authorized:') as exc_info: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune9['rune'], + 'method': 'getinfo', + 'params': {}}) + assert exc_info.value.error['code'] == 0x4c51 + + # Two more succeed for rune8. + for _ in range(2): + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune8['rune'], + 'method': 'getinfo', + 'params': {}}) + assert exc_info.value.error['code'] == 0x4c51 + + # Now we've had 3 in one minute, this will fail. + with pytest.raises(RpcError, match='Not authorized:') as exc_info: + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': rune8['rune'], + 'method': 'getinfo', + 'params': {}}) + assert exc_info.value.error['code'] == 0x4c51 + # rune5 can only be used by l2: l3 = node_factory.get_node() l3.connect(l1) @@ -2799,7 +2840,9 @@ def test_commando_rune(node_factory): # Now wait for ratelimit expiry, ratelimits should reset. time.sleep(61) - for rune, cmd, params in ratelimit_successes: + for rune, cmd, params in ((rune9, "getinfo", {}), + (rune8, "getinfo", {}), + (rune8, "getinfo", {})): l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], 'rune': rune['rune'], From b55df5c62648ceaae5bd8558c7a3ebaecfe5f3a2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 26 Jul 2022 13:20:03 +0200 Subject: [PATCH 1116/1530] msggen: Use tempfile + rename to make changes to .msggen.json atomic This was causing issues when multiple instances of msggen were running in parallel on CI. Changelog-None --- contrib/msggen/msggen/__main__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/msggen/msggen/__main__.py b/contrib/msggen/msggen/__main__.py index e6257efcf052..cb221f3b6b42 100644 --- a/contrib/msggen/msggen/__main__.py +++ b/contrib/msggen/msggen/__main__.py @@ -1,4 +1,5 @@ import json +import os from msggen.gen.grpc import GrpcGenerator, GrpcConverterGenerator, GrpcUnconverterGenerator, GrpcServerGenerator from msggen.gen.grpc2py import Grpc2PyGenerator from msggen.gen.rust import RustGenerator @@ -41,8 +42,10 @@ def load_msggen_meta(): def write_msggen_meta(meta): - with open('.msggen.json', 'w') as f: + pid = os.getpid() + with open(f'.msggen.json.tmp.{pid}', 'w') as f: json.dump(meta, f, sort_keys=True, indent=4) + os.rename(f'.msggen.json.tmp.{pid}', '.msggen.json') def run(): From f4abc3a661fbc30bfd40debf7d4fd379d7627b3e Mon Sep 17 00:00:00 2001 From: niftynei Date: Sat, 23 Jul 2022 12:03:27 -0500 Subject: [PATCH 1117/1530] tests: local flake fix; l1 was waiting too long to reconnect Impacts local tests, when TIMEOUT is set low... --- tests/test_misc.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 2f67ee17bfac..ee8a2b57c3b7 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1084,7 +1084,7 @@ def chan_active(node, scid, is_active): return [c['active'] for c in chans] == [is_active, is_active] -@pytest.mark.developer("needs DEVELOPER=1") +@pytest.mark.developer("needs DEVELOPER=1", "uses dev-fast-reconnect") @pytest.mark.openchannel('v2') @pytest.mark.openchannel('v1') def test_funding_reorg_private(node_factory, bitcoind): @@ -1096,7 +1096,8 @@ def test_funding_reorg_private(node_factory, bitcoind): 'allow_bad_gossip': True, # gossipd send lightning update for original channel. 'allow_broken_log': True, - 'allow_warning': True} + 'allow_warning': True, + 'dev-fast-reconnect': None} l1, l2 = node_factory.line_graph(2, fundchannel=False, opts=opts) l1.fundwallet(10000000) sync_blockheight(bitcoind, [l1]) # height 102 From 282ab72e2da5462cc431ed86f78b3c235aef91b1 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 25 Jul 2022 14:15:33 -0500 Subject: [PATCH 1118/1530] tests: valgrind barfing on uninitialized value ------------------------------- Valgrind errors -------------------------------- Valgrind error file: valgrind-errors.493330 ==493330== Conditional jump or move depends on uninitialised value(s) ==493330== at 0x154051: opt_add_addr_withtype (options.c:275) ==493330== by 0x154406: opt_add_announce_addr (options.c:302) ==493330== by 0x2696E6: parse_one (parse.c:121) ==493330== by 0x25CFB5: opt_parse (opt.c:228) ==493330== by 0x155DB6: handle_opts (options.c:1413) ==493330== by 0x127317: main (lightningd.c:994) ==493330== { Memcheck:Cond fun:opt_add_addr_withtype fun:opt_add_announce_addr fun:parse_one fun:opt_parse fun:handle_opts fun:main } -------------------------------------------------------------------------------- Leaving base_dir /tmp/ltests-iyf2dw3n intact, it still has test sub-directories with failure details: ['test_announce_dns_without_port_1'] ====================================== short test summary info ====================================== ERROR tests/test_gossip.py::test_announce_dns_without_port - ValueError: --- common/wireaddr.c | 1 + lightningd/options.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/common/wireaddr.c b/common/wireaddr.c index f43368400489..4246a460cdff 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -337,6 +337,7 @@ bool separate_address_and_port(const tal_t *ctx, const char *arg, *port = strtol(portcolon + 1, &endp, 10); return *port != 0 && *endp == '\0'; } + return true; } diff --git a/lightningd/options.c b/lightningd/options.c index 45a01ca45052..be6c159c4604 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -226,6 +226,8 @@ static char *opt_add_addr_withtype(const char *arg, assert(arg != NULL); dns_ok = !ld->always_use_proxy && ld->config.use_dns; + /* Will be overridden in next call iff has port */ + port = 0; if (!separate_address_and_port(tmpctx, arg, &address, &port)) return tal_fmt(NULL, "Unable to parse address:port '%s'", arg); From d3ba01767279405e4202088d300ce05b0f0dc0b6 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 25 Jul 2022 14:51:32 -0500 Subject: [PATCH 1119/1530] valgrind: rm ref to cmd when cmd is free'd We were cmd was getting free'd but holding on to reference of the thing was causing problems. ==523280== Invalid read of size 8 ==523280== at 0x1B3E14: del_notifier_property (tal.c:326) ==523280== by 0x1B3E14: tal_del_notifier_ (tal.c:569) ==523280== by 0x1123E7: handle_rpc_reply (libplugin.c:671) ==523280== by 0x1123E7: rpc_read_response_one (libplugin.c:866) ==523280== by 0x1123E7: rpc_conn_read_response (libplugin.c:886) ==523280== by 0x1A7B53: next_plan (io.c:59) ==523280== by 0x1A7B53: do_plan (io.c:407) ==523280== by 0x1A7B53: io_ready (io.c:417) ==523280== by 0x1A9BDB: io_loop (poll.c:453) ==523280== by 0x1141D0: plugin_main (libplugin.c:1708) ==523280== by 0x10D7E4: main (commando.c:937) ==523280== Address 0x52de928 is 8 bytes inside a block of size 40 free'd ==523280== at 0x483F0C3: free (vg_replace_malloc.c:872) ==523280== by 0x1B2CDD: del_tree (tal.c:419) ==523280== by 0x1B37BB: tal_free (tal.c:486) ==523280== by 0x1B37BB: tal_free (tal.c:474) ==523280== by 0x110CB2: command_complete (libplugin.c:255) ==523280== by 0x110CB2: command_done_err (libplugin.c:390) ==523280== by 0x10F511: handle_reply (commando.c:560) ==523280== by 0x10F511: handle_custommsg (commando.c:609) ==523280== by 0x113877: ld_command_handle (libplugin.c:1441) ==523280== by 0x113877: ld_read_json_one (libplugin.c:1491) ==523280== by 0x113877: ld_read_json (libplugin.c:1511) ==523280== by 0x1A7B53: next_plan (io.c:59) ==523280== by 0x1A7B53: do_plan (io.c:407) ==523280== by 0x1A7B53: io_ready (io.c:417) ==523280== by 0x1A9BDB: io_loop (poll.c:453) ==523280== by 0x1141D0: plugin_main (libplugin.c:1708) ==523280== by 0x10D7E4: main (commando.c:937) ==523280== Block was alloc'd at ==523280== at 0x483C855: malloc (vg_replace_malloc.c:381) ==523280== by 0x1B3BBD: allocate (tal.c:250) ==523280== by 0x1B3BBD: add_notifier_property (tal.c:303) ==523280== by 0x1B3BBD: tal_add_destructor2_ (tal.c:529) ==523280== by 0x110725: jsonrpc_request_start_ (libplugin.c:181) ==523280== by 0x10E0EA: send_more_cmd (commando.c:643) ==523280== by 0x11243C: handle_rpc_reply (libplugin.c:696) ==523280== by 0x11243C: rpc_read_response_one (libplugin.c:866) ==523280== by 0x11243C: rpc_conn_read_response (libplugin.c:886) ==523280== by 0x1A7B53: next_plan (io.c:59) ==523280== by 0x1A7B53: do_plan (io.c:407) ==523280== by 0x1A7B53: io_ready (io.c:417) ==523280== by 0x1A9BDB: io_loop (poll.c:453) ==523280== by 0x1141D0: plugin_main (libplugin.c:1708) ==523280== by 0x10D7E4: main (commando.c:937) ==523280== { --- plugins/libplugin.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index bbbfaa669ff7..9098506ae2e8 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -149,6 +149,8 @@ static void disable_request_cb(struct command *cmd, struct out_req *out) { out->errcb = NULL; out->cb = ignore_cb; + /* Called because cmd got free'd */ + out->cmd = NULL; } /* FIXME: Move lightningd/jsonrpc to common/ ? */ From 2c2bcc8eb42ab798074a328696f9aa507ea665ed Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 26 Jul 2022 17:07:39 -0500 Subject: [PATCH 1120/1530] flake: permit test_v2_open_sigs_restart_while_dead to succeed/fail There's a race btw disconnecting and returning a successful RPC call for openchannel_signed; if we disconnect quickly, we get an RPC error back. Too slow and it returns w/o an error. This needs to be cleaned up on a whole, work that I'm planning to get into as part of a funder re-write. For now, let's just let the test continue whether this call succeeds or fails. --- tests/test_opening.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/test_opening.py b/tests/test_opening.py index 1033df533af6..af632cdda2ce 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -170,10 +170,12 @@ def test_v2_open_sigs_restart(node_factory, bitcoind): assert log psbt = re.search("psbt (.*)", log).group(1) - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_log('Peer has reconnected, state DUALOPEND_OPEN_INIT') - with pytest.raises(RpcError): + try: + # FIXME: why do we need to retry signed? l1.rpc.openchannel_signed(chan_id, psbt) + except RpcError: + pass l2.daemon.wait_for_log('Broadcasting funding tx') txid = l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0]['funding_txid'] @@ -219,10 +221,12 @@ def test_v2_open_sigs_restart_while_dead(node_factory, bitcoind): assert log psbt = re.search("psbt (.*)", log).group(1) - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l1.daemon.wait_for_log('Peer has reconnected, state DUALOPEND_OPEN_INIT') - with pytest.raises(RpcError): + try: + # FIXME: why do we need to retry signed? l1.rpc.openchannel_signed(chan_id, psbt) + except RpcError: + pass l2.daemon.wait_for_log('Broadcasting funding tx') l2.daemon.wait_for_log('sendrawtx exit 0') From 79a76a96f7a60616464937acf7e18b75b51e1061 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 26 Jul 2022 20:00:07 -0500 Subject: [PATCH 1121/1530] v2open: dont rely on ordering of interprocess messages Originally I (incorrectly?) assumed that since TX_COMMITMENT_SIGNED always came before TX_SIGNATURES, we would always receive a response from openchannel_update (w/ commitment_secured = true) before getting notification of receipt of the peer's signatures. But it's observable in the logs of hung tests that this in fact is a wrong assumption -- the notification for the tx_sigs arrives at our spender plugin before the callback from our openchannel_update RPC. This mis-ordering causes a hang. Luckily we're pretty much setup to handle this race already w/ states etc, minus actually calling the method advance the plot in case we're ready. 2022-07-26T05:37:59.4529095Z lightningd-1 2022-07-26T05:10:07.395Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-dualopend-chan#2: peer_in WIRE_COMMITMENT_SIGNED 2022-07-26T05:37:59.4530452Z lightningd-1 2022-07-26T05:10:07.396Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-hsmd: Got WIRE_HSMD_VALIDATE_COMMITMENT_TX 2022-07-26T05:37:59.4530719Z lightningd-1 2022-07-26T05:10:07.396Z DEBUG hsmd: Client: Received message 35 from client 2022-07-26T05:37:59.4531386Z lightningd-1 2022-07-26T05:10:07.396Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-dualopend-chan#2: billboard: channel open: commitment received, sending to lightningd to save 2022-07-26T05:37:59.4531856Z lightningd-1 2022-07-26T05:10:07.398Z DEBUG 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d-dualopend-chan#2: peer_in WIRE_TX_SIGNATURES >>> 2022-07-26T05:37:59.4532553Z lightningd-1 2022-07-26T05:10:07.400Z DEBUG plugin-spenderp: mfc 60:`openchannel_peer_sigs` notice received for channel 9d145e763f08ee6f715ba7677f869cbb9580c7406f4d0b0ff3a0987efe501e13 <<<< THIS ONE WAS ASSUMED TO COME AFTER openchannel_update (next line) 2022-07-26T05:37:59.4533048Z lightningd-1 2022-07-26T05:10:07.400Z DEBUG plugin-spenderp: mfc 60, dest 0: openchannel_update 035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d returned. 2022-07-26T05:37:59.4554292Z lightningd-1 2022-07-26T05:10:07.400Z DEBUG plugin-spenderp: mfc 60: parallel `openchannel_update`. 2022-07-26T05:37:59.4555485Z lightningd-1 2022-07-26T05:10:07.400Z DEBUG plugin-spenderp: mfc 60: funding tx 50425e20dbf0ca6fe112a8811b8048edb5bfa8d2922079668c5f353b859b45cb 2022-07-26T05:37:59.4557934Z lightningd-1 2022-07-26T05:10:07.508Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-hsmd: Got WIRE_HSMD_CUPDATE_SIG_REQ 2022-07-26T05:37:59.4558244Z lightningd-1 2022-07-26T05:10:07.508Z DEBUG hsmd: Client: Received message 3 from client 2022-07-26T05:37:59.4558738Z lightningd-3 2022-07-26T05:11:03.234Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-gossipd: seeker: startup peer finished 2022-07-26T05:37:59.4559209Z lightningd-3 2022-07-26T05:11:03.234Z DEBUG 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518-gossipd: seeker: state = PROBING_SCIDS Seeking scids 1 - 105 (The last 2 log messages (from a different node) are >1min after the last log line from lightning-1, because lightning-1 hung) Hacked lightningd up to test this (such that notification always sent before the RPC response, works as intended w/ patch) --- plugins/spender/openchannel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/spender/openchannel.c b/plugins/spender/openchannel.c index 0143c70704e2..41a5bc804dc5 100644 --- a/plugins/spender/openchannel.c +++ b/plugins/spender/openchannel.c @@ -621,10 +621,9 @@ funding_transaction_established(struct multifundchannel_command *mfc) /* If all we've got is v2 destinations, we're just waiting * for all of our peers to send us their sigs. - * That callback triggers separately, so we just return - * a 'still pending' here */ + * Let's check if we've gotten them yet */ if (dest_count(mfc, FUND_CHANNEL) == 0) - return command_still_pending(mfc->cmd); + return check_sigs_ready(mfc); /* For any v1 destination, we need to update the destination * outnum with the correct outnum on the now-known From 1c26ebdb31c32cb4b853067bb262b3dca4cb150d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 27 Jul 2022 13:55:10 +0930 Subject: [PATCH 1122/1530] pytest: fix flake in test_wumbo_channels We might only have seen one side of the channel, as shown below. Wait for both: ``` _____________________________ test_wumbo_channels ______________________________ [gw2] linux -- Python 3.7.13 /opt/hostedtoolcache/Python/3.7.13/x64/bin/python3 node_factory = bitcoind = @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_wumbo_channels(node_factory, bitcoind): l1, l2, l3 = node_factory.get_nodes(3, opts=[{'large-channels': None}, {'large-channels': None}, {}]) conn = l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) expected_features = expected_peer_features(wumbo_channels=True) if l1.config('experimental-dual-fund'): expected_features = expected_peer_features(wumbo_channels=True, extra=[21, 29]) assert conn['features'] == expected_features assert only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['features'] == expected_features # Now, can we open a giant channel? l1.fundwallet(1 << 26) l1.rpc.fundchannel(l2.info['id'], 1 << 24) # Get that mined, and announced. bitcoind.generate_block(6, wait_for_mempool=1) # Connect l3, get gossip. l3.rpc.connect(l1.info['id'], 'localhost', port=l1.port) wait_for(lambda: len(l3.rpc.listnodes(l1.info['id'])['nodes']) == 1) wait_for(lambda: 'features' in only_one(l3.rpc.listnodes(l1.info['id'])['nodes'])) # Make sure channel capacity is what we expected. > assert ([c['amount_msat'] for c in l3.rpc.listchannels()['channels']] == [Millisatoshi(str(1 << 24) + "sat")] * 2) E assert [16777216000msat] == [16777216000m...777216000msat] E Right contains one more item: 16777216000msat E Full diff: E - [16777216000msat, 16777216000msat] E + [16777216000msat] ``` Signed-off-by: Rusty Russell --- tests/test_connection.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 8d867a8af356..1dc6fbfb9671 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3358,12 +3358,11 @@ def test_wumbo_channels(node_factory, bitcoind): # Connect l3, get gossip. l3.rpc.connect(l1.info['id'], 'localhost', port=l1.port) - wait_for(lambda: len(l3.rpc.listnodes(l1.info['id'])['nodes']) == 1) - wait_for(lambda: 'features' in only_one(l3.rpc.listnodes(l1.info['id'])['nodes'])) - # Make sure channel capacity is what we expected. - assert ([c['amount_msat'] for c in l3.rpc.listchannels()['channels']] - == [Millisatoshi(str(1 << 24) + "sat")] * 2) + # Make sure channel capacity is what we expected (might need to wait for + # both channel updates! + wait_for(lambda: [c['amount_msat'] for c in l3.rpc.listchannels()['channels']] + == [Millisatoshi(str(1 << 24) + "sat")] * 2) # Make sure channel features are right from channel_announcement assert ([c['features'] for c in l3.rpc.listchannels()['channels']] From 75c89f0b8e39a45cd1d2fdaa1b718e1f6ed0c3de Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 27 Jul 2022 13:43:02 +0930 Subject: [PATCH 1123/1530] doc: fix bolt 12 link (it's not in master), update bolt 11 to new "bolts" repo. It's weird that readthedocs.io drops the ".md" suffix though; let's see if this fixes it. Signed-off-by: Rusty Russell --- doc/lightning-decode.7.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 0bd81ad05825..108aed713b1d 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -191,9 +191,9 @@ SEE ALSO lightning-pay(7), lightning-offer(7), lightning-offerout(7), lightning-fetchinvoice(7), lightning-sendinvoice(7), lightning-commando-rune(7) -[BOLT \#11](https://github.com/lightningnetwork/lightning-rfc/blob/master/11-payment-encoding.md). +[BOLT #11](https://github.com/lightningnetwork/bolts/blob/master/11-payment-encoding.md). -[BOLT \#12](https://github.com/lightningnetwork/lightning-rfc/blob/master/12-offer-encoding.md). +[BOLT #12](https://github.com/rustyrussell/lightning-rfc/blob/guilt/offers/12-offer-encoding.md). RESOURCES From 967c56859f6cb15f13d077f7238458e1914e0c99 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:35:26 +0930 Subject: [PATCH 1124/1530] sql: use last " as " to find name token for column We were using the first ' as ', which causes problems for the following types of lines CAST(SUM(of.debit) AS BIGINT) as debit --- devtools/sql-rewrite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py index 476cdc147d4d..4306d0de1432 100755 --- a/devtools/sql-rewrite.py +++ b/devtools/sql-rewrite.py @@ -110,7 +110,7 @@ def colname_htable(query): for colnum, colname in enumerate(colnames): colname = colname.strip() # SELECT xxx AS yyy -> Y - as_clause = colname.upper().find(" AS ") + as_clause = colname.upper().rfind(" AS ") if as_clause != -1: colname = colname[as_clause + 4:].strip() From 1a3bfc479fb44dbd9f512790ef72c18544355d51 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:36:26 +0930 Subject: [PATCH 1125/1530] bookkeep: first commit, stub of new plugin Will manage bookkeeping for cln --- plugins/.gitignore | 1 + plugins/Makefile | 9 ++ plugins/bookkeeper.c | 339 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 349 insertions(+) create mode 100644 plugins/bookkeeper.c diff --git a/plugins/.gitignore b/plugins/.gitignore index 7d87bb04986b..149755c9dd9c 100644 --- a/plugins/.gitignore +++ b/plugins/.gitignore @@ -1,5 +1,6 @@ autoclean bcli +bookkeeper fetchinvoice fundchannel funder diff --git a/plugins/Makefile b/plugins/Makefile index e0236e58e702..c9c845519263 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -1,3 +1,7 @@ +PLUGIN_BOOKKEEPER_SRC := plugins/bookkeeper.c +PLUGIN_BOOKKEEPER_HEADER := +PLUGIN_BOOKKEEPER_OBJS := $(PLUGIN_BOOKKEEPER_SRC:.c=.o) + PLUGIN_PAY_SRC := plugins/pay.c PLUGIN_PAY_OBJS := $(PLUGIN_PAY_SRC:.c=.o) @@ -60,6 +64,7 @@ PLUGIN_FUNDER_HEADER := \ PLUGIN_FUNDER_OBJS := $(PLUGIN_FUNDER_SRC:.c=.o) PLUGIN_ALL_SRC := \ + $(PLUGIN_BOOKKEEPER_SRC) \ $(PLUGIN_AUTOCLEAN_SRC) \ $(PLUGIN_chanbackup_SRC) \ $(PLUGIN_BCLI_SRC) \ @@ -76,6 +81,7 @@ PLUGIN_ALL_SRC := \ $(PLUGIN_SPENDER_SRC) PLUGIN_ALL_HEADER := \ + $(PLUGIN_BOOKKEEPER_HEADER) \ $(PLUGIN_LIB_HEADER) \ $(PLUGIN_FUNDER_HEADER) \ $(PLUGIN_PAY_LIB_HEADER) \ @@ -87,6 +93,7 @@ C_PLUGINS := \ plugins/autoclean \ plugins/chanbackup \ plugins/bcli \ + plugins/bookkeeper \ plugins/commando \ plugins/fetchinvoice \ plugins/funder \ @@ -166,6 +173,8 @@ PLUGIN_COMMON_OBJS := \ # Make all plugins depend on all plugin headers, for simplicity. $(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER) +plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(PLUGIN_BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) + plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) diff --git a/plugins/bookkeeper.c b/plugins/bookkeeper.c new file mode 100644 index 000000000000..5326f0e5dbd2 --- /dev/null +++ b/plugins/bookkeeper.c @@ -0,0 +1,339 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include + +#define CHAIN_MOVE "chain_mvt" +#define CHANNEL_MOVE "channel_mvt" + +static struct command_result *json_list_balances(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + + if (!param(cmd, buf, params, NULL)) + return command_param_failed(); + + res = jsonrpc_stream_success(cmd); + return command_finished(cmd, res); +} + +struct account_snap { + char *name; + struct amount_msat amt; + char *coin_type; +}; + +static struct command_result *json_balance_snapshot(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const char *err; + size_t i; + struct node_id node_id; + u32 blockheight; + u64 timestamp; + struct account_snap *snaps; + const jsmntok_t *accounts_tok, *acct_tok, + *snap_tok = json_get_member(buf, params, "balance_snapshot"); + + if (snap_tok == NULL || snap_tok->type != JSMN_OBJECT) + plugin_err(cmd->plugin, + "`balance_snapshot` payload did not scan %s: %.*s", + "no 'balance_snapshot'", json_tok_full_len(params), + json_tok_full(buf, params)); + + err = json_scan(cmd, buf, snap_tok, + "{node_id:%" + ",blockheight:%" + ",timestamp:%}", + JSON_SCAN(json_to_node_id, &node_id), + JSON_SCAN(json_to_number, &blockheight), + JSON_SCAN(json_to_u64, ×tamp)); + + if (err) + plugin_err(cmd->plugin, + "`balance_snapshot` payload did not scan %s: %.*s", + err, json_tok_full_len(params), + json_tok_full(buf, params)); + + plugin_log(cmd->plugin, LOG_DBG, "balances for node %s at %d" + " (%"PRIu64")", + type_to_string(tmpctx, struct node_id, &node_id), + blockheight, timestamp); + + accounts_tok = json_get_member(buf, snap_tok, "accounts"); + if (accounts_tok == NULL || accounts_tok->type != JSMN_ARRAY) + plugin_err(cmd->plugin, + "`balance_snapshot` payload did not scan %s: %.*s", + "no 'balance_snapshot.accounts'", + json_tok_full_len(params), + json_tok_full(buf, params)); + + snaps = tal_arr(cmd, struct account_snap, accounts_tok->size); + json_for_each_arr(i, acct_tok, accounts_tok) { + struct account_snap s = snaps[i]; + err = json_scan(cmd, buf, acct_tok, + "{account_id:%" + ",balance_msat:%" + ",coin_type:%}", + JSON_SCAN_TAL(tmpctx, json_strdup, &s.name), + JSON_SCAN(json_to_msat, &s.amt), + JSON_SCAN_TAL(tmpctx, json_strdup, + &s.coin_type)); + if (err) + plugin_err(cmd->plugin, + "`balance_snapshot` payload did not" + " scan %s: %.*s", + err, json_tok_full_len(params), + json_tok_full(buf, params)); + + plugin_log(cmd->plugin, LOG_DBG, "account %s has balance %s", + s.name, + type_to_string(tmpctx, struct amount_msat, &s.amt)); + } + + // FIXME: check balances are ok! + + return notification_handled(cmd); +} + +static const char *parse_and_log_chain_move(struct command *cmd, + const char *buf, + const jsmntok_t *params, + const struct node_id *node_id, + const char *acct_name STEALS, + const struct amount_msat credit, + const struct amount_msat debit, + const char *coin_type STEALS, + const u32 timestamp, + const enum mvt_tag *tags) +{ + struct bitcoin_outpoint outpt; + static struct amount_msat output_value; + struct sha256 *payment_hash = tal(tmpctx, struct sha256); + struct bitcoin_txid *spending_txid = tal(tmpctx, struct bitcoin_txid); + u32 blockheight; + const char *err; + + /* Fields we expect on *every* chain movement */ + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{utxo_txid:%" + ",vout:%" + ",output_msat:%" + ",blockheight:%" + "}}", + JSON_SCAN(json_to_txid, &outpt.txid), + JSON_SCAN(json_to_number, &outpt.n), + JSON_SCAN(json_to_msat, &output_value), + JSON_SCAN(json_to_number, &blockheight)); + + if (err) + return err; + + /* Now try to get out the optional parts */ + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{txid:%" + "}}", + JSON_SCAN(json_to_txid, spending_txid)); + + if (err) + spending_txid = tal_free(spending_txid); + + /* Now try to get out the optional parts */ + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{payment_hash:%" + "}}", + JSON_SCAN(json_to_sha256, payment_hash)); + + if (err) + payment_hash = tal_free(payment_hash); + + // FIXME: enter into database + return NULL; +} + +static const char *parse_and_log_channel_move(struct command *cmd, + const char *buf, + const jsmntok_t *params, + const struct node_id *node_id, + const char *acct_name STEALS, + const struct amount_msat credit, + const struct amount_msat debit, + const char *coin_type STEALS, + const u32 timestamp, + const enum mvt_tag *tags) +{ + struct sha256 payment_hash; + u64 part_id; + struct amount_msat fees; + + const char *err; + + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{payment_hash:%" + ",fees:%" + "}}", + JSON_SCAN(json_to_sha256, &payment_hash), + JSON_SCAN(json_to_msat, &fees)); + + if (err) + return err; + + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{part_id:%}}", + JSON_SCAN(json_to_u64, &part_id)); + if (err) + part_id = 0; + + // FIXME: enter into database? + + return NULL; +} + +static char *parse_tags(const tal_t *ctx, + const char *buf, + const jsmntok_t *tok, + enum mvt_tag **tags) +{ + size_t i; + const jsmntok_t *tag_tok, + *tags_tok = json_get_member(buf, tok, "tags"); + + if (tags_tok == NULL || tags_tok->type != JSMN_ARRAY) + return "Invalid/missing 'tags' field"; + + *tags = tal_arr(ctx, enum mvt_tag, tags_tok->size); + json_for_each_arr(i, tag_tok, tags_tok) { + if (!json_to_coin_mvt_tag(buf, tag_tok, &(*tags)[i])) + return "Unable to parse 'tags'"; + } + + return NULL; +} + +static struct command_result * json_coin_moved(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + const char *err, *mvt_type, *acct_name, *coin_type; + struct node_id node_id; + u32 version; + u64 timestamp; + struct amount_msat credit, debit; + enum mvt_tag *tags; + + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{version:%" + ",node_id:%" + ",type:%" + ",account_id:%" + ",credit_msat:%" + ",debit_msat:%" + ",coin_type:%" + ",timestamp:%" + "}}", + JSON_SCAN(json_to_number, &version), + JSON_SCAN(json_to_node_id, &node_id), + JSON_SCAN_TAL(tmpctx, json_strdup, &mvt_type), + JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), + JSON_SCAN(json_to_msat, &credit), + JSON_SCAN(json_to_msat, &debit), + JSON_SCAN_TAL(tmpctx, json_strdup, &coin_type), + JSON_SCAN(json_to_u64, ×tamp)); + + if (err) + plugin_err(cmd->plugin, + "`coin_movement` payload did not scan %s: %.*s", + err, json_tok_full_len(params), + json_tok_full(buf, params)); + + err = parse_tags(cmd, buf, + json_get_member(buf, params, "coin_movement"), + &tags); + if (err) + plugin_err(cmd->plugin, + "`coin_movement` payload did not scan %s: %.*s", + err, json_tok_full_len(params), + json_tok_full(buf, params)); + + /* We expect version 2 of coin movements */ + assert(version == 2); + + plugin_log(cmd->plugin, LOG_DBG, "coin_move %d %s -%s %s %"PRIu64, + version, + type_to_string(tmpctx, struct amount_msat, &credit), + type_to_string(tmpctx, struct amount_msat, &debit), + mvt_type, timestamp); + + if (streq(mvt_type, CHAIN_MOVE)) + err = parse_and_log_chain_move(cmd, buf, params, &node_id, + acct_name, credit, debit, + coin_type, timestamp, + tags); + else { + assert(streq(mvt_type, CHANNEL_MOVE)); + err = parse_and_log_channel_move(cmd, buf, params, &node_id, + acct_name, credit, debit, + coin_type, timestamp, + tags); + } + + if (err) + plugin_err(cmd->plugin, + "`coin_movement` payload did not scan %s: %.*s", + err, json_tok_full_len(params), + json_tok_full(buf, params)); + + return notification_handled(cmd); +} + +const struct plugin_notification notifs[] = { + { + "coin_movement", + json_coin_moved, + }, + { + "balance_snapshot", + json_balance_snapshot, + } +}; + +static const struct plugin_command commands[] = { + { + "listbalances", + "bookkeeping", + "List current account balances", + "List of current accounts and their balances", + json_list_balances + }, +}; + +static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) +{ + // FIXME set up database, run migrations + return NULL; +} + +int main(int argc, char *argv[]) +{ + setup_locale(); + + plugin_main(argv, init, PLUGIN_STATIC, true, NULL, + commands, ARRAY_SIZE(commands), + notifs, ARRAY_SIZE(notifs), + NULL, 0, + NULL, 0, + NULL); + return 0; +} From fb951dbbd6809656716b6768d11c8da73e12f74b Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:37:26 +0930 Subject: [PATCH 1126/1530] bkpr: first attempt at database code for accounting A database scheme and first attempt at drivers for the bookkeeper database. Also moves bookkeeper plugin into its own subdirectory --- plugins/Makefile | 11 +-- plugins/bkpr/Makefile | 43 ++++++++ plugins/{ => bkpr}/bookkeeper.c | 12 ++- plugins/bkpr/db.c | 168 ++++++++++++++++++++++++++++++++ plugins/bkpr/db.h | 11 +++ 5 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 plugins/bkpr/Makefile rename plugins/{ => bkpr}/bookkeeper.c (96%) create mode 100644 plugins/bkpr/db.c create mode 100644 plugins/bkpr/db.h diff --git a/plugins/Makefile b/plugins/Makefile index c9c845519263..8259eb64cb88 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -1,7 +1,3 @@ -PLUGIN_BOOKKEEPER_SRC := plugins/bookkeeper.c -PLUGIN_BOOKKEEPER_HEADER := -PLUGIN_BOOKKEEPER_OBJS := $(PLUGIN_BOOKKEEPER_SRC:.c=.o) - PLUGIN_PAY_SRC := plugins/pay.c PLUGIN_PAY_OBJS := $(PLUGIN_PAY_SRC:.c=.o) @@ -64,7 +60,6 @@ PLUGIN_FUNDER_HEADER := \ PLUGIN_FUNDER_OBJS := $(PLUGIN_FUNDER_SRC:.c=.o) PLUGIN_ALL_SRC := \ - $(PLUGIN_BOOKKEEPER_SRC) \ $(PLUGIN_AUTOCLEAN_SRC) \ $(PLUGIN_chanbackup_SRC) \ $(PLUGIN_BCLI_SRC) \ @@ -81,7 +76,6 @@ PLUGIN_ALL_SRC := \ $(PLUGIN_SPENDER_SRC) PLUGIN_ALL_HEADER := \ - $(PLUGIN_BOOKKEEPER_HEADER) \ $(PLUGIN_LIB_HEADER) \ $(PLUGIN_FUNDER_HEADER) \ $(PLUGIN_PAY_LIB_HEADER) \ @@ -93,7 +87,6 @@ C_PLUGINS := \ plugins/autoclean \ plugins/chanbackup \ plugins/bcli \ - plugins/bookkeeper \ plugins/commando \ plugins/fetchinvoice \ plugins/funder \ @@ -170,11 +163,11 @@ PLUGIN_COMMON_OBJS := \ wire/tlvstream.o \ wire/towire.o +include plugins/bkpr/Makefile + # Make all plugins depend on all plugin headers, for simplicity. $(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER) -plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(PLUGIN_BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) - plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile new file mode 100644 index 000000000000..1644feaf4907 --- /dev/null +++ b/plugins/bkpr/Makefile @@ -0,0 +1,43 @@ +#! /usr/bin/make + +BOOKKEEPER_PLUGIN_SRC := \ + plugins/bkpr/bookkeeper.c \ + plugins/bkpr/db.c + +BOOKKEEPER_DB_QUERIES := \ + plugins/bkpr/db_sqlite3_sqlgen.c \ + plugins/bkpr/db_postgres_sqlgen.c + +BOOKKEEPER_SRC := $(BOOKKEEPER_PLUGIN_SRC) $(BOOKKEEPER_DB_QUERIES) +BOOKKEEPER_HEADER := plugins/bkpr/db.h +BOOKKEEPER_OBJS := $(BOOKKEEPER_SRC:.c=.o) + +$(BOOKKEEPER_OBJS): $(PLUGIN_LIB_HEADER) $(BOOKKEEPER_HEADER) + +ALL_C_SOURCES += $(BOOKKEEPER_SRC) +ALL_C_HEADERS += $(BOOKKEEPER_HEADER) +ALL_PROGRAMS += plugins/bookkeeper + +plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(DB_OBJS) + +# The following files contain SQL-annotated statements that we need to extact +BOOKKEEPER_SQL_FILES := \ + $(DB_SQL_FILES) \ + plugins/bkpr/db.c + +plugins/bkpr/statements_gettextgen.po: $(BOOKKEEPER_SQL_FILES) $(FORCE) + @if $(call SHA256STAMP_CHANGED_ALL); then \ + $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(BOOKKEEPER_SQL_FILES) && $(call SHA256STAMP_ALL,# ,)); \ + fi + +plugins/bkpr/db_%_sqlgen.c: plugins/bkpr/statements_gettextgen.po devtools/sql-rewrite.py $(BOOKKEEPER_SQL_FILES) $(FORCE) + @if $(call SHA256STAMP_CHANGED); then \ + $(call VERBOSE,"sql-rewrite $@",devtools/sql-rewrite.py plugins/bkpr/statements_gettextgen.po $* > $@ && $(call SHA256STAMP,//,)); \ + fi + +maintainer-clean: clean +clean: bkpr-maintainer-clean +bkpr-maintainer-clean: + $(RM) plugins/bkpr/statements.po + $(RM) plugins/bkpr/statements_gettextgen.po + $(RM) $(BOOKKEEPER_DB_QUERIES) diff --git a/plugins/bookkeeper.c b/plugins/bkpr/bookkeeper.c similarity index 96% rename from plugins/bookkeeper.c rename to plugins/bkpr/bookkeeper.c index 5326f0e5dbd2..7b04ed61fda8 100644 --- a/plugins/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -2,13 +2,21 @@ #include #include #include +#include #include #include +#include #include #define CHAIN_MOVE "chain_mvt" #define CHANNEL_MOVE "channel_mvt" +/* The database that we store all the accounting data in */ +static struct db *db ; + +// FIXME: make relative to directory we're loaded into +static char *db_dsn = "sqlite3://accounts.sqlite3"; + static struct command_result *json_list_balances(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -321,7 +329,9 @@ static const struct plugin_command commands[] = { static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) { - // FIXME set up database, run migrations + // FIXME: pass in database DSN as an option?? + db = notleak(db_setup(p, p, db_dsn)); + return NULL; } diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c new file mode 100644 index 000000000000..8fce4f3988d6 --- /dev/null +++ b/plugins/bkpr/db.c @@ -0,0 +1,168 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +struct migration { + const char *sql; + void (*func)(struct plugin *p, struct db *db); +}; + +/* Do not reorder or remove elements from this array. + * It is used to migrate existing databases from a prevoius state, based on + * string indicies */ +static struct migration db_migrations[] = { + {SQL("CREATE TABLE version (version INTEGER);"), NULL}, + {SQL("INSERT INTO version VALUES (1);"), NULL}, + {SQL("CREATE TABLE vars (" + " name TEXT" + ", val TEXT" + ", intval INTEGER" + ", blobval BLOB" + ", PRIMARY KEY (name)" + ");"), + NULL}, + {SQL("INSERT INTO vars (" + " name" + ", intval" + ") VALUES (" + " 'data_version'" + ", 0" + ");"), + NULL}, + {SQL("CREATE TABLE accounts (" + " id BIGSERIAL" + ", name TEXT" + ", peer_id BLOB" + ", opened_event_id BIGINT" + ", closed_event_id BIGINT" + ", onchain_resolved_block INTEGER" + ", is_wallet INTEGER" + ", we_opened INTEGER" + ", leased INTEGER" + ", last_updated_ts BIGINT" + ", last_updated_block INTEGER" + ", PRIMARY KEY (id)" + ");"), + NULL}, + {SQL("CREATE TABLE chain_events (" + " id BIGSERIAL" + ", account_id BIGINT REFERENCES accounts(id)" + ", tag TEXT" + ", credit BIGINT" + ", debit BIGINT" + ", output_value BIGINT" + ", currency TEXT" + ", timestamp BIGINT" + ", blockheight INTEGER" + ", utxo_txid BLOB" + ", outnum INTEGER" + ", spending_txid BLOB" + ", PRIMARY KEY (id)" + ");"), + NULL}, + {SQL("CREATE TABLE channel_events (" + " id BIGSERIAL" + ", account_id BIGINT REFERENCES accounts(id)" + ", tag TEXT" + ", credit BIGINT" + ", debit BIGINT" + ", fees BIGINT" + ", currency TEXT" + ", payment_id BLOB" + ", part_id INTEGER" + ", timestamp BIGINT" + ", PRIMARY KEY (id)" + ");"), + NULL}, + {SQL("CREATE TABLE onchain_fees (" + "account_id BIGINT REFERENCES accounts(id)" + ", txid BLOB" + ", amount BIGINT" + ", PRIMARY KEY (account_id, txid)" + ");"), + NULL}, +}; + +static bool db_migrate(struct plugin *p, struct db *db) +{ + /* Read current version from database */ + int current, orig, available; + struct db_stmt *stmt; + + orig = current = db_get_version(db); + available = ARRAY_SIZE(db_migrations) - 1; + + if (current == -1) + plugin_log(p, LOG_INFORM, "Creating database"); + else if (available < current) + plugin_err(p, + "Refusing to migrate down from version %u to %u", + current, available); + else if (current != available) + plugin_log(p, LOG_INFORM, + "Updating database from version %u to %u", + current, available); + + while (current < available) { + current++; + if (db_migrations[current].sql) { + struct db_stmt *stmt = + db_prepare_v2(db, db_migrations[current].sql); + db_exec_prepared_v2(take(stmt)); + } + if (db_migrations[current].func) + db_migrations[current].func(p, db); + } + + /* Finally, update the version number in the version table */ + stmt = db_prepare_v2(db, SQL("UPDATE version SET version=?;")); + db_bind_int(stmt, 0, available); + db_exec_prepared_v2(take(stmt)); + + return current != orig; +} + +/* Implement db_fatal, as a wrapper around fatal. + * We use a ifndef block so that it can get be + * implemented in a test file first, if necessary */ +#ifndef DB_FATAL +#define DB_FATAL +void db_fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + + exit(1); +} +#endif /* DB_FATAL */ + +struct db *db_setup(const tal_t *ctx, struct plugin *p, char *db_dsn) +{ + bool migrated; + struct db *db = db_open(ctx, db_dsn); + + db->report_changes_fn = NULL; + + db_begin_transaction(db); + migrated = db_migrate(p, db); + db->data_version = db_data_version_get(db); + db_commit_transaction(db); + + /* This needs to be done outside a transaction, apparently. + * It's a good idea to do this every so often, and on db + * upgrade is a reasonable time. */ + if (migrated && !db->config->vacuum_fn(db)) + db_fatal("Error vacuuming db: %s", db->error); + + return db; +} diff --git a/plugins/bkpr/db.h b/plugins/bkpr/db.h new file mode 100644 index 000000000000..0c9663e941b5 --- /dev/null +++ b/plugins/bkpr/db.h @@ -0,0 +1,11 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_DB_H +#define LIGHTNING_PLUGINS_BKPR_DB_H +#include "config.h" +#include + +struct plugin; +struct db; + +struct db *db_setup(const tal_t *ctx, struct plugin *p, char *db_dsn); + +#endif /* LIGHTNING_PLUGINS_BKPR_DB_H */ From cd95d91ed593b9b0a2fbe78d4c4a10d1f64a8df9 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:38:26 +0930 Subject: [PATCH 1127/1530] bkpr-tests: first test of plugin bkpr database First test of bookkeeper database, for just the migrations. --- .gitignore | 2 + plugins/bkpr/Makefile | 2 + plugins/bkpr/test/Makefile | 33 ++++ plugins/bkpr/test/run-bkpr_db.c | 270 ++++++++++++++++++++++++++++++++ plugins/bkpr/test/test_utils.h | 26 +++ 5 files changed, 333 insertions(+) create mode 100644 plugins/bkpr/test/Makefile create mode 100644 plugins/bkpr/test/run-bkpr_db.c create mode 100644 plugins/bkpr/test/test_utils.h diff --git a/.gitignore b/.gitignore index 4e4588023bd5..4af97d0e4233 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,8 @@ monkeytype.sqlite3 !*/test/run-*.c */test/exp-run-* !*/test/exp-run-*.c +*/*/test/run-* +!*/*/test/run-*.c test/test_protocol test/test_sphinx tests/.pytest.restart diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index 1644feaf4907..075f0a7f3ed4 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -41,3 +41,5 @@ bkpr-maintainer-clean: $(RM) plugins/bkpr/statements.po $(RM) plugins/bkpr/statements_gettextgen.po $(RM) $(BOOKKEEPER_DB_QUERIES) + +include plugins/bkpr/test/Makefile diff --git a/plugins/bkpr/test/Makefile b/plugins/bkpr/test/Makefile new file mode 100644 index 000000000000..5eb076ee3816 --- /dev/null +++ b/plugins/bkpr/test/Makefile @@ -0,0 +1,33 @@ +BOOKKEEPER_TEST_SRC := $(wildcard plugins/bkpr/test/run-*.c) +BOOKKEEPER_TEST_OBJS := $(BOOKKEEPER_TEST_SRC:.c=.o) +BOOKKEEPER_TEST_PROGRAMS := $(BOOKKEEPER_TEST_OBJS:.o=) + +ALL_C_SOURCES += $(BOOKKEEPER_TEST_SRC) +ALL_TEST_PROGRAMS += $(BOOKKEEPER_TEST_PROGRAMS) + +BOOKKEEPER_TEST_COMMON_OBJS := \ + common/amount.o \ + common/autodata.o \ + common/base32.o \ + common/blockheight_states.o \ + common/channel_type.o \ + common/features.o \ + common/json_stream.o \ + common/key_derive.o \ + common/memleak.o \ + common/node_id.o \ + common/setup.o \ + common/timeout.o \ + common/type_to_string.o \ + common/utils.o \ + common/version.o \ + db/bindings.o \ + db/db_sqlite3.o \ + db/exec.o \ + db/utils.o \ + plugins/bkpr/db_sqlite3_sqlgen.o + +$(BOOKKEEPER_TEST_PROGRAMS): $(BITCOIN_OBJS) $(BOOKKEEPER_TEST_COMMON_OBJS) +$(BOOKKEEPER_TEST_OBJS): $(BOOKKEEPER_HEADER) $(BOOKKEEPER_SRC) $(BOOKKEEPER_TEST_COMMON_OBJS) + +check-units: $(BOOKKEEPER_TEST_PROGRAMS:%=unittest/%) diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c new file mode 100644 index 000000000000..7bf7baad71b1 --- /dev/null +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -0,0 +1,270 @@ +#include "config.h" + +#include +#include + +#ifndef DB_FATAL +#define DB_FATAL +static char *db_err; +void db_fatal(const char *fmt, ...) +{ + va_list ap; + + /* Fail hard if we're complaining about not being in transaction */ + assert(!strstarts(fmt, "No longer in transaction")); + + va_start(ap, fmt); + db_err = tal_vfmt(NULL, fmt, ap); + va_end(ap); +} +#endif /* DB_FATAL */ + +#include "plugins/bkpr/db.c" +#include "plugins/libplugin.c" + +#include "test_utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for daemon_maybe_debug */ +void daemon_maybe_debug(char *argv[]) +{ fprintf(stderr, "daemon_maybe_debug called!\n"); abort(); } +/* Generated stub for daemon_setup */ +void daemon_setup(const char *argv0 UNNEEDED, + void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED, + void (*backtrace_exit)(void)) +{ fprintf(stderr, "daemon_setup called!\n"); abort(); } +/* Generated stub for first_fee_state */ +enum htlc_state first_fee_state(enum side opener UNNEEDED) +{ fprintf(stderr, "first_fee_state called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for htlc_state_flags */ +int htlc_state_flags(enum htlc_state state UNNEEDED) +{ fprintf(stderr, "htlc_state_flags called!\n"); abort(); } +/* Generated stub for htlc_state_name */ +const char *htlc_state_name(enum htlc_state s UNNEEDED) +{ fprintf(stderr, "htlc_state_name called!\n"); abort(); } +/* Generated stub for json_get_member */ +const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED) +{ fprintf(stderr, "json_get_member called!\n"); abort(); } +/* Generated stub for json_next */ +const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_next called!\n"); abort(); } +/* Generated stub for json_parse_input */ +bool json_parse_input(jsmn_parser *parser UNNEEDED, + jsmntok_t **toks UNNEEDED, + const char *input UNNEEDED, int len UNNEEDED, + bool *complete UNNEEDED) +{ fprintf(stderr, "json_parse_input called!\n"); abort(); } +/* Generated stub for json_parse_simple */ +jsmntok_t *json_parse_simple(const tal_t *ctx UNNEEDED, const char *input UNNEEDED, int len UNNEEDED) +{ fprintf(stderr, "json_parse_simple called!\n"); abort(); } +/* Generated stub for json_scan */ +const char *json_scan(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + ...) +{ fprintf(stderr, "json_scan called!\n"); abort(); } +/* Generated stub for json_scanv */ +const char *json_scanv(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + va_list ap UNNEEDED) +{ fprintf(stderr, "json_scanv called!\n"); abort(); } +/* Generated stub for json_strdup */ +char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_strdup called!\n"); abort(); } +/* Generated stub for json_to_bool */ +bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) +{ fprintf(stderr, "json_to_bool called!\n"); abort(); } +/* Generated stub for json_to_int */ +bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED) +{ fprintf(stderr, "json_to_int called!\n"); abort(); } +/* Generated stub for json_to_msat */ +bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat *msat UNNEEDED) +{ fprintf(stderr, "json_to_msat called!\n"); abort(); } +/* Generated stub for json_to_node_id */ +bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } +/* Generated stub for json_to_secret */ +bool json_to_secret(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct secret *dest UNNEEDED) +{ fprintf(stderr, "json_to_secret called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_to_txid */ +bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_txid *txid UNNEEDED) +{ fprintf(stderr, "json_to_txid called!\n"); abort(); } +/* Generated stub for json_to_u64 */ +bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) +{ fprintf(stderr, "json_to_u64 called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } +/* Generated stub for json_tok_full */ +const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full called!\n"); abort(); } +/* Generated stub for json_tok_full_len */ +int json_tok_full_len(const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); } +/* Generated stub for json_tok_streq */ +bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "json_tok_streq called!\n"); abort(); } +/* Generated stub for last_fee_state */ +enum htlc_state last_fee_state(enum side opener UNNEEDED) +{ fprintf(stderr, "last_fee_state called!\n"); abort(); } +/* Generated stub for log_level_name */ +const char *log_level_name(enum log_level level UNNEEDED) +{ fprintf(stderr, "log_level_name called!\n"); abort(); } +/* Generated stub for toks_alloc */ +jsmntok_t *toks_alloc(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "toks_alloc called!\n"); abort(); } +/* Generated stub for toks_reset */ +void toks_reset(jsmntok_t *toks UNNEEDED) +{ fprintf(stderr, "toks_reset called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static char *tmp_dsn(const tal_t *ctx) +{ + char *dsn, *filename; + int fd = tmpdir_mkstemp(ctx, "lacct-db-XXXXXX", &filename); + if (fd == -1) + return NULL; + close(fd); + + dsn = tal_fmt(NULL, "sqlite3://%s", filename); + tal_free(filename); + + return dsn; +} + +static struct db *create_test_db(void) +{ + struct db *db; + char *dsn; + + dsn = tmp_dsn(NULL); + db = db_open(NULL, dsn); + db->data_version = 0; + db->report_changes_fn = NULL; + + tal_free(dsn); + return db; +} + +static bool test_empty_db_migrate(struct plugin *plugin) +{ + struct db *db = create_test_db(); + CHECK(db); + db_begin_transaction(db); + CHECK(db_get_version(db) == -1); + db_migrate(plugin, db); + db_commit_transaction(db); + + db_begin_transaction(db); + CHECK(db_get_version(db) == ARRAY_SIZE(db_migrations) - 1); + db_commit_transaction(db); + + tal_free(db); + return true; +} + +int main(int argc, char *argv[]) +{ + bool ok = true; + /* Dummy for migration hooks */ + struct plugin *plugin = tal(NULL, struct plugin); + plugin->js_arr = tal_arr(plugin, struct json_stream *, 0); + + common_setup(argv[0]); + + ok &= test_empty_db_migrate(plugin); + + tal_free(plugin); + common_shutdown(); + return !ok; +} diff --git a/plugins/bkpr/test/test_utils.h b/plugins/bkpr/test/test_utils.h new file mode 100644 index 000000000000..d54b5b3ee4c5 --- /dev/null +++ b/plugins/bkpr/test/test_utils.h @@ -0,0 +1,26 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_TEST_TEST_UTILS_H +#define LIGHTNING_PLUGINS_BKPR_TEST_TEST_UTILS_H + +#include "config.h" + +/* Definitions "inspired" by libsecp256k1 */ +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \ + return false; \ +} while(0) + +#ifdef HAVE_BUILTIN_EXPECT +#define EXPECT(x,c) __builtin_expect((x),(c)) +#else +#define EXPECT(x,c) (x) +#endif + +#define CHECK_MSG(cond,msg) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE(msg); \ + } \ +} while(0) + +#define CHECK(cond) CHECK_MSG(cond,"test condition failed"); + +#endif /* LIGHTNING_PLUGINS_BKPR_TEST_TEST_UTILS_H */ From b08ccfec1e2c05c7c1b305b36bd0be795556178f Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:39:26 +0930 Subject: [PATCH 1128/1530] bookkeeper: initial crud (no tests) --- plugins/bkpr/Makefile | 16 +- plugins/bkpr/account.c | 23 ++ plugins/bkpr/account.h | 43 +++ plugins/bkpr/bookkeeper.c | 109 ++++--- plugins/bkpr/chain_event.h | 50 ++++ plugins/bkpr/channel_event.h | 43 +++ plugins/bkpr/db.c | 4 +- plugins/bkpr/onchain_fee.h | 25 ++ plugins/bkpr/recorder.c | 545 +++++++++++++++++++++++++++++++++++ plugins/bkpr/recorder.h | 56 ++++ 10 files changed, 871 insertions(+), 43 deletions(-) create mode 100644 plugins/bkpr/account.c create mode 100644 plugins/bkpr/account.h create mode 100644 plugins/bkpr/chain_event.h create mode 100644 plugins/bkpr/channel_event.h create mode 100644 plugins/bkpr/onchain_fee.h create mode 100644 plugins/bkpr/recorder.c create mode 100644 plugins/bkpr/recorder.h diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index 075f0a7f3ed4..e076863153ca 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -1,15 +1,24 @@ #! /usr/bin/make BOOKKEEPER_PLUGIN_SRC := \ + plugins/bkpr/account.c \ plugins/bkpr/bookkeeper.c \ - plugins/bkpr/db.c + plugins/bkpr/db.c \ + plugins/bkpr/recorder.c BOOKKEEPER_DB_QUERIES := \ plugins/bkpr/db_sqlite3_sqlgen.c \ plugins/bkpr/db_postgres_sqlgen.c BOOKKEEPER_SRC := $(BOOKKEEPER_PLUGIN_SRC) $(BOOKKEEPER_DB_QUERIES) -BOOKKEEPER_HEADER := plugins/bkpr/db.h +BOOKKEEPER_HEADER := \ + plugins/bkpr/account.h \ + plugins/bkpr/chain_event.h \ + plugins/bkpr/channel_event.h \ + plugins/bkpr/db.h \ + plugins/bkpr/onchain_fee.h \ + plugins/bkpr/recorder.h + BOOKKEEPER_OBJS := $(BOOKKEEPER_SRC:.c=.o) $(BOOKKEEPER_OBJS): $(PLUGIN_LIB_HEADER) $(BOOKKEEPER_HEADER) @@ -23,7 +32,8 @@ plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(BOOKKEEPER_OBJS) $ # The following files contain SQL-annotated statements that we need to extact BOOKKEEPER_SQL_FILES := \ $(DB_SQL_FILES) \ - plugins/bkpr/db.c + plugins/bkpr/db.c \ + plugins/bkpr/recorder.c plugins/bkpr/statements_gettextgen.po: $(BOOKKEEPER_SQL_FILES) $(FORCE) @if $(call SHA256STAMP_CHANGED_ALL); then \ diff --git a/plugins/bkpr/account.c b/plugins/bkpr/account.c new file mode 100644 index 000000000000..c971087293b0 --- /dev/null +++ b/plugins/bkpr/account.c @@ -0,0 +1,23 @@ +#include "config.h" + +#include +#include +#include + +struct account *new_account(const tal_t *ctx, + const char *name STEALS, + struct node_id *peer_id) +{ + struct account *a = tal(ctx, struct account); + + a->name = tal_steal(a, name); + a->peer_id = peer_id; + a->is_wallet = streq(a->name, WALLET); + a->we_opened = false; + a->leased = false; + a->onchain_resolved_block = 0; + a->open_event_db_id = NULL; + a->closed_event_db_id = NULL; + + return a; +} diff --git a/plugins/bkpr/account.h b/plugins/bkpr/account.h new file mode 100644 index 000000000000..f73d8ca72cd8 --- /dev/null +++ b/plugins/bkpr/account.h @@ -0,0 +1,43 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_ACCOUNT_H +#define LIGHTNING_PLUGINS_BKPR_ACCOUNT_H + +#include "config.h" +#include + +struct node_id; + +struct account { + + /* Id of this account in the database */ + u64 db_id; + + /* Name of account, typically channel id */ + const char *name; + + /* Peer we have this account with (NULL if not a channel) */ + struct node_id *peer_id; + + /* Is this our internal wallet account? */ + bool is_wallet; + + /* Is this an account we initiated open for? */ + bool we_opened; + + /* Was any portion of this account's funds leased? */ + bool leased; + + /* Block account was totally resolved at */ + u64 onchain_resolved_block; + + /* db_id of chain event that opened this account */ + u64 *open_event_db_id; + + /* db_id of chain event that closed this account */ + u64 *closed_event_db_id; +}; + +/* Get a new account */ +struct account *new_account(const tal_t *ctx, + const char *name STEALS, + struct node_id *peer_id); +#endif /* LIGHTNING_PLUGINS_BKPR_ACCOUNT_H */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 7b04ed61fda8..c51831c93d89 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -3,9 +3,13 @@ #include #include #include -#include #include +#include #include +#include +#include +#include +#include #include #define CHAIN_MOVE "chain_mvt" @@ -42,7 +46,6 @@ static struct command_result *json_balance_snapshot(struct command *cmd, { const char *err; size_t i; - struct node_id node_id; u32 blockheight; u64 timestamp; struct account_snap *snaps; @@ -56,10 +59,8 @@ static struct command_result *json_balance_snapshot(struct command *cmd, json_tok_full(buf, params)); err = json_scan(cmd, buf, snap_tok, - "{node_id:%" - ",blockheight:%" + "{blockheight:%" ",timestamp:%}", - JSON_SCAN(json_to_node_id, &node_id), JSON_SCAN(json_to_number, &blockheight), JSON_SCAN(json_to_u64, ×tamp)); @@ -69,11 +70,6 @@ static struct command_result *json_balance_snapshot(struct command *cmd, err, json_tok_full_len(params), json_tok_full(buf, params)); - plugin_log(cmd->plugin, LOG_DBG, "balances for node %s at %d" - " (%"PRIu64")", - type_to_string(tmpctx, struct node_id, &node_id), - blockheight, timestamp); - accounts_tok = json_get_member(buf, snap_tok, "accounts"); if (accounts_tok == NULL || accounts_tok->type != JSMN_ARRAY) plugin_err(cmd->plugin, @@ -113,19 +109,17 @@ static struct command_result *json_balance_snapshot(struct command *cmd, static const char *parse_and_log_chain_move(struct command *cmd, const char *buf, const jsmntok_t *params, - const struct node_id *node_id, const char *acct_name STEALS, const struct amount_msat credit, const struct amount_msat debit, const char *coin_type STEALS, - const u32 timestamp, + const u64 timestamp, const enum mvt_tag *tags) { - struct bitcoin_outpoint outpt; - static struct amount_msat output_value; - struct sha256 *payment_hash = tal(tmpctx, struct sha256); - struct bitcoin_txid *spending_txid = tal(tmpctx, struct bitcoin_txid); - u32 blockheight; + struct chain_event *e = tal(cmd, struct chain_event); + struct sha256 *payment_hash = tal(cmd, struct sha256); + struct bitcoin_txid *spending_txid = tal(cmd, struct bitcoin_txid); + struct account *acct; const char *err; /* Fields we expect on *every* chain movement */ @@ -136,10 +130,10 @@ static const char *parse_and_log_chain_move(struct command *cmd, ",output_msat:%" ",blockheight:%" "}}", - JSON_SCAN(json_to_txid, &outpt.txid), - JSON_SCAN(json_to_number, &outpt.n), - JSON_SCAN(json_to_msat, &output_value), - JSON_SCAN(json_to_number, &blockheight)); + JSON_SCAN(json_to_txid, &e->outpoint.txid), + JSON_SCAN(json_to_number, &e->outpoint.n), + JSON_SCAN(json_to_msat, &e->output_value), + JSON_SCAN(json_to_number, &e->blockheight)); if (err) return err; @@ -154,6 +148,8 @@ static const char *parse_and_log_chain_move(struct command *cmd, if (err) spending_txid = tal_free(spending_txid); + e->spending_txid = tal_steal(e, spending_txid); + /* Now try to get out the optional parts */ err = json_scan(tmpctx, buf, params, "{coin_movement:" @@ -164,25 +160,49 @@ static const char *parse_and_log_chain_move(struct command *cmd, if (err) payment_hash = tal_free(payment_hash); - // FIXME: enter into database + e->payment_id = tal_steal(e, payment_hash); + + e->credit = credit; + e->debit = debit; + e->currency = tal_steal(e, coin_type); + e->timestamp = timestamp; + e->tag = mvt_tag_str(tags[0]); + + db_begin_transaction(db); + acct = find_account(cmd, db, acct_name); + + if (!acct) { + /* FIXME: lookup the peer id for this channel! */ + acct = new_account(cmd, acct_name, NULL); + account_add(db, acct); + } + + log_chain_event(db, acct, e); + + /* This event *might* have implications for account; + * update as necessary */ + maybe_update_account(db, acct, e, tags); + + /* Can we calculate any onchain fees now? */ + + /* FIXME: maybe mark channel as 'onchain_resolved' */ + db_commit_transaction(db); + return NULL; } static const char *parse_and_log_channel_move(struct command *cmd, const char *buf, const jsmntok_t *params, - const struct node_id *node_id, const char *acct_name STEALS, const struct amount_msat credit, const struct amount_msat debit, const char *coin_type STEALS, - const u32 timestamp, + const u64 timestamp, const enum mvt_tag *tags) { - struct sha256 payment_hash; - u64 part_id; - struct amount_msat fees; - + struct channel_event *e = tal(cmd, struct channel_event); + struct account *acct; const char *err; err = json_scan(tmpctx, buf, params, @@ -190,8 +210,8 @@ static const char *parse_and_log_channel_move(struct command *cmd, "{payment_hash:%" ",fees:%" "}}", - JSON_SCAN(json_to_sha256, &payment_hash), - JSON_SCAN(json_to_msat, &fees)); + JSON_SCAN(json_to_sha256, &e->payment_id), + JSON_SCAN(json_to_msat, &e->fees)); if (err) return err; @@ -199,11 +219,27 @@ static const char *parse_and_log_channel_move(struct command *cmd, err = json_scan(tmpctx, buf, params, "{coin_movement:" "{part_id:%}}", - JSON_SCAN(json_to_u64, &part_id)); + JSON_SCAN(json_to_number, &e->part_id)); if (err) - part_id = 0; + e->part_id = 0; + + e->credit = credit; + e->debit = debit; + e->currency = tal_steal(e, coin_type); + e->timestamp = timestamp; + e->tag = mvt_tag_str(tags[0]); + + /* Go find the account for this event */ + db_begin_transaction(db); + acct = find_account(cmd, db, acct_name); + if (!acct) + plugin_err(cmd->plugin, + "Received channel event," + " but no account exists %s", + acct_name); - // FIXME: enter into database? + log_channel_event(db, acct, e); + db_commit_transaction(db); return NULL; } @@ -234,7 +270,6 @@ static struct command_result * json_coin_moved(struct command *cmd, const jsmntok_t *params) { const char *err, *mvt_type, *acct_name, *coin_type; - struct node_id node_id; u32 version; u64 timestamp; struct amount_msat credit, debit; @@ -243,7 +278,6 @@ static struct command_result * json_coin_moved(struct command *cmd, err = json_scan(tmpctx, buf, params, "{coin_movement:" "{version:%" - ",node_id:%" ",type:%" ",account_id:%" ",credit_msat:%" @@ -252,7 +286,6 @@ static struct command_result * json_coin_moved(struct command *cmd, ",timestamp:%" "}}", JSON_SCAN(json_to_number, &version), - JSON_SCAN(json_to_node_id, &node_id), JSON_SCAN_TAL(tmpctx, json_strdup, &mvt_type), JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), JSON_SCAN(json_to_msat, &credit), @@ -285,13 +318,13 @@ static struct command_result * json_coin_moved(struct command *cmd, mvt_type, timestamp); if (streq(mvt_type, CHAIN_MOVE)) - err = parse_and_log_chain_move(cmd, buf, params, &node_id, + err = parse_and_log_chain_move(cmd, buf, params, acct_name, credit, debit, coin_type, timestamp, tags); else { assert(streq(mvt_type, CHANNEL_MOVE)); - err = parse_and_log_channel_move(cmd, buf, params, &node_id, + err = parse_and_log_channel_move(cmd, buf, params, acct_name, credit, debit, coin_type, timestamp, tags); diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h new file mode 100644 index 000000000000..764c85410c7c --- /dev/null +++ b/plugins/bkpr/chain_event.h @@ -0,0 +1,50 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H +#define LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H + +#include "config.h" +#include + +struct amount_msat; +struct bitcoin_outpoint; +struct bitcoin_txid; + +struct chain_event { + + /* Id of this chain event in the database */ + u64 db_id; + + /* db_id of account this event belongs to */ + u64 acct_db_id; + + /* Tag describing the event */ + const char *tag; + + /* Amount we received in this event */ + struct amount_msat credit; + + /* Amount we paid in this event */ + struct amount_msat debit; + + /* Total 'amount' of output on this chain event */ + struct amount_msat output_value; + + /* What token are the credit/debits? */ + const char *currency; + + /* What time did the event happen */ + u64 timestamp; + + /* What block did the event happen */ + u32 blockheight; + + /* What txo did this event concern */ + struct bitcoin_outpoint outpoint; + + /* What tx was the outpoint spent in (if spent) */ + struct bitcoin_txid *spending_txid; + + /* Sometimes chain events resolve payments */ + struct sha256 *payment_id; +}; + +#endif /* LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H */ diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h new file mode 100644 index 000000000000..d6a9533fc59c --- /dev/null +++ b/plugins/bkpr/channel_event.h @@ -0,0 +1,43 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H +#define LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H + +#include "config.h" +#include + +struct amount_msat; +struct sha256; + +struct channel_event { + + /* Id of this chain event in the database */ + u64 db_id; + + /* db_id of account this event belongs to */ + u64 acct_db_id; + + /* Tag describing the event */ + const char *tag; + + /* Amount we received in this event */ + struct amount_msat credit; + + /* Amount we paid in this event */ + struct amount_msat debit; + + /* Total 'fees' related to this channel event */ + struct amount_msat fees; + + /* What token are the credit/debits? */ + const char *currency; + + /* Payment identifier (typically the preimage hash) */ + struct sha256 payment_id; + + /* Some payments share a payment_id, and are differentiable via id */ + u32 part_id; + + /* What time did the event happen */ + u64 timestamp; +}; + +#endif /* LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H */ diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 8fce4f3988d6..0b44de6095aa 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -45,8 +45,6 @@ static struct migration db_migrations[] = { ", is_wallet INTEGER" ", we_opened INTEGER" ", leased INTEGER" - ", last_updated_ts BIGINT" - ", last_updated_block INTEGER" ", PRIMARY KEY (id)" ");"), NULL}, @@ -62,6 +60,7 @@ static struct migration db_migrations[] = { ", blockheight INTEGER" ", utxo_txid BLOB" ", outnum INTEGER" + ", payment_id BLOB" ", spending_txid BLOB" ", PRIMARY KEY (id)" ");"), @@ -84,6 +83,7 @@ static struct migration db_migrations[] = { "account_id BIGINT REFERENCES accounts(id)" ", txid BLOB" ", amount BIGINT" + ", currency TEXT" ", PRIMARY KEY (account_id, txid)" ");"), NULL}, diff --git a/plugins/bkpr/onchain_fee.h b/plugins/bkpr/onchain_fee.h new file mode 100644 index 000000000000..3b2085abb226 --- /dev/null +++ b/plugins/bkpr/onchain_fee.h @@ -0,0 +1,25 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H +#define LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H + +#include "config.h" +#include + +struct amount_msat; +struct bitcoin_txid; + +struct onchain_fee { + + /* db_id of account this event belongs to */ + u64 acct_db_id; + + /* Transaction that we're recording fees for */ + struct bitcoin_txid txid; + + /* Total amount of onchain fees we paid for this txid */ + struct amount_msat amount; + + /* What token are fees? */ + char *currency; +}; + +#endif /* LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H */ diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c new file mode 100644 index 000000000000..6a1f01605d53 --- /dev/null +++ b/plugins/bkpr/recorder.c @@ -0,0 +1,545 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt) +{ + struct chain_event *e = tal(ctx, struct chain_event); + e->db_id = db_col_u64(stmt, "id"); + e->acct_db_id = db_col_u64(stmt, "account_id"); + + e->tag = db_col_strdup(e, stmt, "tag"); + + db_col_amount_msat(stmt, "credit", &e->credit); + db_col_amount_msat(stmt, "debit", &e->debit); + db_col_amount_msat(stmt, "output_value", &e->output_value); + + e->currency = db_col_strdup(e, stmt, "currency"); + e->timestamp = db_col_u64(stmt, "timestamp"); + e->blockheight = db_col_int(stmt, "blockheight"); + + db_col_txid(stmt, "utxo_txid", &e->outpoint.txid); + e->outpoint.n = db_col_int(stmt, "outnum"); + + if (!db_col_is_null(stmt, "payment_id")) { + e->payment_id = tal(e, struct sha256); + db_col_sha256(stmt, "payment_id", e->payment_id); + } else + e->payment_id = NULL; + + if (!db_col_is_null(stmt, "spending_txid")) { + e->spending_txid = tal(e, struct bitcoin_txid); + db_col_txid(stmt, "spending_txid", e->spending_txid); + } else + e->spending_txid = NULL; + + return e; +} + +static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt *stmt) +{ + struct channel_event *e = tal(ctx, struct channel_event); + + e->db_id = db_col_u64(stmt, "id"); + e->acct_db_id = db_col_u64(stmt, "account_id"); + + e->tag = db_col_strdup(e, stmt, "tag"); + + db_col_amount_msat(stmt, "credit", &e->credit); + db_col_amount_msat(stmt, "debit", &e->debit); + db_col_amount_msat(stmt, "fees", &e->fees); + + e->currency = db_col_strdup(e, stmt, "currency"); + db_col_sha256(stmt, "payment_id", &e->payment_id); + e->part_id = db_col_int(stmt, "part_id"); + e->timestamp = db_col_u64(stmt, "timestamp"); + + return e; +} + +struct chain_event **account_get_chain_events(const tal_t *ctx, + struct db *db, + struct account *acct) +{ + struct db_stmt *stmt; + struct chain_event **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + ", account_id" + ", tag" + ", credit" + ", debit" + ", output_value" + ", currency" + ", timestamp" + ", blockheight" + ", utxo_txid" + ", outnum" + ", spending_txid" + ", payment_id" + " FROM chain_events" + " WHERE account_id = ?;")); + + db_bind_int(stmt, 0, acct->db_id); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct chain_event *, 0); + while (db_step(stmt)) { + struct chain_event *e = stmt2chain_event(results, stmt); + tal_arr_expand(&results, e); + } + tal_free(stmt); + + return results; +} + +static struct chain_event *find_chain_event(const tal_t *ctx, + struct db *db, + const struct account *acct, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *spending_txid) + +{ + struct db_stmt *stmt; + struct chain_event *e; + + if (spending_txid) { + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + ", account_id" + ", tag" + ", credit" + ", debit" + ", output_value" + ", currency" + ", timestamp" + ", blockheight" + ", utxo_txid" + ", outnum" + ", spending_txid" + ", payment_id" + " FROM chain_events" + " WHERE " + " account_id = ?" + " AND utxo_txid = ?" + " AND outnum = ?" + " AND spending_txid = ?")); + db_bind_txid(stmt, 3, spending_txid); + } else { + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + ", account_id" + ", tag" + ", credit" + ", debit" + ", output_value" + ", currency" + ", timestamp" + ", blockheight" + ", utxo_txid" + ", outnum" + ", spending_txid" + ", payment_id" + " FROM chain_events" + " WHERE " + " account_id = ?" + " AND utxo_txid = ?" + " AND outnum = ?" + " AND spending_txid IS NULL")); + } + + db_bind_u64(stmt, 0, acct->db_id); + db_bind_txid(stmt, 1, &outpoint->txid); + db_bind_int(stmt, 2, outpoint->n); + + db_query_prepared(stmt); + if (db_step(stmt)) + e = stmt2chain_event(ctx, stmt); + else + e = NULL; + + tal_free(stmt); + return e; +} + +struct channel_event **account_get_channel_events(const tal_t *ctx, + struct db *db, + struct account *acct) +{ + struct db_stmt *stmt; + struct channel_event **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + ", account_id" + ", tag" + ", credit" + ", debit" + ", fees" + ", currency" + ", payment_id" + ", part_id" + ", timestamp" + " FROM channel_events" + " WHERE account_id = ?;")); + + db_bind_u64(stmt, 0, acct->db_id); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct channel_event *, 0); + while (db_step(stmt)) { + struct channel_event *e = stmt2channel_event(results, stmt); + tal_arr_expand(&results, e); + } + tal_free(stmt); + + return results; +} + +static struct account *stmt2account(const tal_t *ctx, struct db_stmt *stmt) +{ + struct account *a = tal(ctx, struct account); + + a->db_id = db_col_u64(stmt, "id"); + a->name = db_col_strdup(a, stmt, "name"); + + if (!db_col_is_null(stmt, "peer_id")) { + a->peer_id = tal(a, struct node_id); + db_col_node_id(stmt, "peer_id", a->peer_id); + } else + a->peer_id = NULL; + a->is_wallet = db_col_int(stmt, "is_wallet") != 0; + a->we_opened = db_col_int(stmt, "we_opened") != 0; + a->leased = db_col_int(stmt, "leased") != 0; + + if (!db_col_is_null(stmt, "onchain_resolved_block")) { + a->onchain_resolved_block = db_col_int(stmt, "onchain_resolved_block"); + } else + a->onchain_resolved_block = 0; + + if (!db_col_is_null(stmt, "opened_event_id")) { + a->open_event_db_id = tal(a, u64); + *a->open_event_db_id = db_col_u64(stmt, "opened_event_id"); + } else + a->open_event_db_id = NULL; + + if (!db_col_is_null(stmt, "closed_event_id")) { + a->closed_event_db_id = tal(a, u64); + *a->closed_event_db_id = db_col_u64(stmt, "closed_event_id"); + } else + a->closed_event_db_id = NULL; + + return a; +} + +struct account *find_account(const tal_t *ctx, + struct db *db, + const char *name) +{ + struct db_stmt *stmt; + struct account *a; + + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + ", name" + ", peer_id" + ", opened_event_id" + ", closed_event_id" + ", onchain_resolved_block" + ", is_wallet" + ", we_opened" + ", leased" + " FROM accounts" + " WHERE name = ?")); + + db_bind_text(stmt, 0, name); + db_query_prepared(stmt); + + if (db_step(stmt)) + a = stmt2account(ctx, stmt); + else + a = NULL; + + tal_free(stmt); + + return a; +} + +static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, struct db_stmt *stmt) +{ + struct onchain_fee *of = tal(ctx, struct onchain_fee); + + of->acct_db_id = db_col_u64(stmt, "account_id"); + db_col_txid(stmt, "txid", &of->txid); + db_col_amount_msat(stmt, "amount", &of->amount); + of->currency = db_col_strdup(of, stmt, "currency"); + + return of; +} + +struct onchain_fee **account_onchain_fees(const tal_t *ctx, + struct db *db, + struct account *acct) +{ + struct db_stmt *stmt; + struct onchain_fee **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " account_id" + ", txid" + ", amount" + ", currency" + " FROM onchain_fees" + " WHERE account_id = ?;")); + + db_bind_int(stmt, 0, acct->db_id); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct onchain_fee*, 0); + while (db_step(stmt)) { + struct onchain_fee *of = stmt2onchain_fee(results, stmt); + tal_arr_expand(&results, of); + } + tal_free(stmt); + + return results; +} + +struct account **list_accounts(const tal_t *ctx, struct db *db) +{ + struct db_stmt *stmt; + struct account **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + ", name" + ", peer_id" + ", opened_event_id" + ", closed_event_id" + ", onchain_resolved_block" + ", is_wallet" + ", we_opened" + ", leased" + " FROM accounts;")); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct account *, 0); + while (db_step(stmt)) { + struct account *a = stmt2account(results, stmt); + tal_arr_expand(&results, a); + } + tal_free(stmt); + + return results; +} + +void account_add(struct db *db, struct account *acct) +{ + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("INSERT INTO accounts" + " (" + " name" + ", peer_id" + ", is_wallet" + ", we_opened" + ", leased" + ")" + " VALUES" + " (?, ?, ?, ?, ?);")); + + db_bind_text(stmt, 0, acct->name); + if (acct->peer_id) + db_bind_node_id(stmt, 1, acct->peer_id); + else + db_bind_null(stmt, 1); + db_bind_int(stmt, 2, acct->is_wallet ? 1 : 0); + db_bind_int(stmt, 3, acct->we_opened ? 1 : 0); + db_bind_int(stmt, 4, acct->leased ? 1 : 0); + + db_exec_prepared_v2(stmt); + acct->db_id = db_last_insert_id_v2(stmt); + tal_free(stmt); +} + +void maybe_update_account(struct db *db, + struct account *acct, + struct chain_event *e, + const enum mvt_tag *tags) +{ + struct db_stmt *stmt; + bool updated = false; + + for (size_t i = 0; i < tal_count(tags); i++) { + switch (tags[i]) { + case CHANNEL_OPEN: + updated = true; + acct->open_event_db_id = tal(acct, u64); + *acct->open_event_db_id = e->db_id; + break; + case CHANNEL_CLOSE: + updated = true; + acct->closed_event_db_id = tal(acct, u64); + *acct->closed_event_db_id = e->db_id; + break; + case LEASED: + updated = true; + acct->leased = true; + break; + case OPENER: + updated = true; + acct->we_opened = true; + break; + case DEPOSIT: + case WITHDRAWAL: + case PENALTY: + case INVOICE: + case ROUTED: + case PUSHED: + case CHANNEL_TO_US: + case HTLC_TIMEOUT: + case HTLC_FULFILL: + case HTLC_TX: + case TO_WALLET: + case IGNORED: + case ANCHOR: + case TO_THEM: + case PENALIZED: + case STOLEN: + case TO_MINER: + case LEASE_FEE: + /* Ignored */ + break; + } + } + + /* Nothing new here */ + if (!updated) + return; + + /* Otherwise, we update the account ! */ + stmt = db_prepare_v2(db, SQL("UPDATE accounts SET" + " opened_event_id = ?" + ", closed_event_id = ?" + ", we_opened = ?" + ", leased = ?" + " WHERE" + " name = ?")); + + if (acct->open_event_db_id) + db_bind_u64(stmt, 0, *acct->open_event_db_id); + else + db_bind_null(stmt, 0); + + if (acct->closed_event_db_id) + db_bind_u64(stmt, 1, *acct->closed_event_db_id); + else + db_bind_null(stmt, 1); + + db_bind_int(stmt, 2, acct->we_opened ? 1 : 0); + db_bind_int(stmt, 3, acct->leased ? 1 : 0); + + db_bind_text(stmt, 4, acct->name); + + db_exec_prepared_v2(take(stmt)); +} + +void log_channel_event(struct db *db, + const struct account *acct, + struct channel_event *e) +{ + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("INSERT INTO channel_events" + " (" + " account_id" + ", tag" + ", credit" + ", debit" + ", fees" + ", currency" + ", payment_id" + ", part_id" + ", timestamp" + ")" + " VALUES" + " (?, ?, ?, ?, ?, ?, ?, ?, ?);")); + + db_bind_u64(stmt, 0, acct->db_id); + db_bind_text(stmt, 1, e->tag); + db_bind_amount_msat(stmt, 2, &e->credit); + db_bind_amount_msat(stmt, 3, &e->debit); + db_bind_amount_msat(stmt, 4, &e->fees); + db_bind_text(stmt, 5, e->currency); + db_bind_sha256(stmt, 6, &e->payment_id); + db_bind_int(stmt, 7, e->part_id); + db_bind_u64(stmt, 8, e->timestamp); + + db_exec_prepared_v2(stmt); + e->db_id = db_last_insert_id_v2(stmt); + e->acct_db_id = acct->db_id; + tal_free(stmt); +} + +void log_chain_event(struct db *db, + const struct account *acct, + struct chain_event *e) +{ + struct db_stmt *stmt; + + /* We're responsible for de-duping chain events! */ + if (find_chain_event(e, db, acct, + &e->outpoint, e->spending_txid)) + return; + + stmt = db_prepare_v2(db, SQL("INSERT INTO chain_events" + " (" + " account_id" + ", tag" + ", credit" + ", debit" + ", output_value" + ", currency" + ", timestamp" + ", blockheight" + ", utxo_txid" + ", outnum" + ", payment_id" + ", spending_txid" + ")" + " VALUES" + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + + db_bind_u64(stmt, 0, acct->db_id); + db_bind_text(stmt, 1, e->tag); + db_bind_amount_msat(stmt, 2, &e->credit); + db_bind_amount_msat(stmt, 3, &e->debit); + db_bind_amount_msat(stmt, 4, &e->output_value); + db_bind_text(stmt, 5, e->currency); + db_bind_u64(stmt, 6, e->timestamp); + db_bind_int(stmt, 7, e->blockheight); + db_bind_txid(stmt, 8, &e->outpoint.txid); + db_bind_int(stmt, 9, e->outpoint.n); + db_bind_sha256(stmt, 10, e->payment_id); + + if (e->spending_txid) + db_bind_txid(stmt, 11, e->spending_txid); + else + db_bind_null(stmt, 11); + + db_exec_prepared_v2(stmt); + e->db_id = db_last_insert_id_v2(stmt); + e->acct_db_id = acct->db_id; + tal_free(stmt); +} diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h new file mode 100644 index 000000000000..abe32823a83e --- /dev/null +++ b/plugins/bkpr/recorder.h @@ -0,0 +1,56 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_RECORDER_H +#define LIGHTNING_PLUGINS_BKPR_RECORDER_H + +#include "config.h" +#include + +struct account; +struct chain_event; +struct channel_event; +struct db; +enum mvt_tag; +struct onchain_fee; + +/* Get all accounts */ +struct account **list_accounts(const tal_t *ctx, struct db *db); + +/* Get all onchain fee records for this account */ +struct onchain_fee **account_onchain_fees(const tal_t *ctx, + struct db *db, + struct account *acct); + +/* Get all channel events for this account */ +struct channel_event **account_get_channel_events(const tal_t *ctx, + struct db *db, + struct account *acct); + +/* Get all chain events for this account */ +struct chain_event **account_get_chain_events(const tal_t *ctx, + struct db *db, + struct account *acct); + +/* Add the given account to the database */ +void account_add(struct db *db, struct account *acct); + +/* Given an account name, find that account record */ +struct account *find_account(const tal_t *ctx, + struct db *db, + const char *name); + +/* Some events update account information */ +void maybe_update_account(struct db *db, + struct account *acct, + struct chain_event *e, + const enum mvt_tag *tags); + +/* Log a channel event */ +void log_channel_event(struct db *db, + const struct account *acct, + struct channel_event *e); + +/* Log a chain event. */ +void log_chain_event(struct db *db, + const struct account *acct, + struct chain_event *e); + +#endif /* LIGHTNING_PLUGINS_BKPR_RECORDER_H */ From c12cd99039174336f3c3f2da7d9e0bad45ef8f1c Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:52:01 +0930 Subject: [PATCH 1129/1530] bkpr: tests for db crud --- plugins/bkpr/recorder.c | 2 +- plugins/bkpr/test/Makefile | 4 +- plugins/bkpr/test/run-recorder.c | 609 +++++++++++++++++++++++++++++++ 3 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 plugins/bkpr/test/run-recorder.c diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 6a1f01605d53..c1b7826dc19d 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -519,7 +519,7 @@ void log_chain_event(struct db *db, ", spending_txid" ")" " VALUES" - " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); db_bind_text(stmt, 1, e->tag); diff --git a/plugins/bkpr/test/Makefile b/plugins/bkpr/test/Makefile index 5eb076ee3816..642e35214b69 100644 --- a/plugins/bkpr/test/Makefile +++ b/plugins/bkpr/test/Makefile @@ -25,7 +25,9 @@ BOOKKEEPER_TEST_COMMON_OBJS := \ db/db_sqlite3.o \ db/exec.o \ db/utils.o \ - plugins/bkpr/db_sqlite3_sqlgen.o + plugins/bkpr/account.o \ + plugins/bkpr/db_sqlite3_sqlgen.o \ + plugins/bkpr/recorder.o $(BOOKKEEPER_TEST_PROGRAMS): $(BITCOIN_OBJS) $(BOOKKEEPER_TEST_COMMON_OBJS) $(BOOKKEEPER_TEST_OBJS): $(BOOKKEEPER_HEADER) $(BOOKKEEPER_SRC) $(BOOKKEEPER_TEST_COMMON_OBJS) diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c new file mode 100644 index 000000000000..bff4a17a3bf1 --- /dev/null +++ b/plugins/bkpr/test/run-recorder.c @@ -0,0 +1,609 @@ +#include "config.h" +#include "test_utils.h" + +#include "plugins/libplugin.c" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *db_err; +#ifndef DB_FATAL +#define DB_FATAL +void db_fatal(const char *fmt, ...) +{ + va_list ap; + + /* Fail hard if we're complaining about not being in transaction */ + assert(!strstarts(fmt, "No longer in transaction")); + + va_start(ap, fmt); + db_err = tal_vfmt(NULL, fmt, ap); + va_end(ap); +} +#endif /* DB_FATAL */ + +#include "plugins/bkpr/db.c" + + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for daemon_maybe_debug */ +void daemon_maybe_debug(char *argv[]) +{ fprintf(stderr, "daemon_maybe_debug called!\n"); abort(); } +/* Generated stub for daemon_setup */ +void daemon_setup(const char *argv0 UNNEEDED, + void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED, + void (*backtrace_exit)(void)) +{ fprintf(stderr, "daemon_setup called!\n"); abort(); } +/* Generated stub for first_fee_state */ +enum htlc_state first_fee_state(enum side opener UNNEEDED) +{ fprintf(stderr, "first_fee_state called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for htlc_state_flags */ +int htlc_state_flags(enum htlc_state state UNNEEDED) +{ fprintf(stderr, "htlc_state_flags called!\n"); abort(); } +/* Generated stub for htlc_state_name */ +const char *htlc_state_name(enum htlc_state s UNNEEDED) +{ fprintf(stderr, "htlc_state_name called!\n"); abort(); } +/* Generated stub for json_get_member */ +const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED) +{ fprintf(stderr, "json_get_member called!\n"); abort(); } +/* Generated stub for json_next */ +const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_next called!\n"); abort(); } +/* Generated stub for json_parse_input */ +bool json_parse_input(jsmn_parser *parser UNNEEDED, + jsmntok_t **toks UNNEEDED, + const char *input UNNEEDED, int len UNNEEDED, + bool *complete UNNEEDED) +{ fprintf(stderr, "json_parse_input called!\n"); abort(); } +/* Generated stub for json_parse_simple */ +jsmntok_t *json_parse_simple(const tal_t *ctx UNNEEDED, const char *input UNNEEDED, int len UNNEEDED) +{ fprintf(stderr, "json_parse_simple called!\n"); abort(); } +/* Generated stub for json_scan */ +const char *json_scan(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + ...) +{ fprintf(stderr, "json_scan called!\n"); abort(); } +/* Generated stub for json_scanv */ +const char *json_scanv(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + va_list ap UNNEEDED) +{ fprintf(stderr, "json_scanv called!\n"); abort(); } +/* Generated stub for json_strdup */ +char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_strdup called!\n"); abort(); } +/* Generated stub for json_to_bool */ +bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) +{ fprintf(stderr, "json_to_bool called!\n"); abort(); } +/* Generated stub for json_to_int */ +bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED) +{ fprintf(stderr, "json_to_int called!\n"); abort(); } +/* Generated stub for json_to_msat */ +bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat *msat UNNEEDED) +{ fprintf(stderr, "json_to_msat called!\n"); abort(); } +/* Generated stub for json_to_node_id */ +bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } +/* Generated stub for json_to_secret */ +bool json_to_secret(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct secret *dest UNNEEDED) +{ fprintf(stderr, "json_to_secret called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_to_txid */ +bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_txid *txid UNNEEDED) +{ fprintf(stderr, "json_to_txid called!\n"); abort(); } +/* Generated stub for json_to_u64 */ +bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) +{ fprintf(stderr, "json_to_u64 called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } +/* Generated stub for json_tok_full */ +const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full called!\n"); abort(); } +/* Generated stub for json_tok_full_len */ +int json_tok_full_len(const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); } +/* Generated stub for json_tok_streq */ +bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "json_tok_streq called!\n"); abort(); } +/* Generated stub for last_fee_state */ +enum htlc_state last_fee_state(enum side opener UNNEEDED) +{ fprintf(stderr, "last_fee_state called!\n"); abort(); } +/* Generated stub for log_level_name */ +const char *log_level_name(enum log_level level UNNEEDED) +{ fprintf(stderr, "log_level_name called!\n"); abort(); } +/* Generated stub for toks_alloc */ +jsmntok_t *toks_alloc(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "toks_alloc called!\n"); abort(); } +/* Generated stub for toks_reset */ +void toks_reset(jsmntok_t *toks UNNEEDED) +{ fprintf(stderr, "toks_reset called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static char *tmp_dsn(const tal_t *ctx) +{ + char *dsn, *filename; + int fd = tmpdir_mkstemp(ctx, "lacct-db-XXXXXX", &filename); + if (fd == -1) + return NULL; + close(fd); + + dsn = tal_fmt(ctx, "sqlite3://%s", filename); + tal_free(filename); + + return dsn; +} + +static bool accountseq(struct account *a1, struct account *a2) +{ + CHECK(a1->db_id == a2->db_id); + CHECK(streq(a1->name, a2->name)); + CHECK(node_id_eq(a1->peer_id, a2->peer_id)); + CHECK(a1->is_wallet == a2->is_wallet); + CHECK(a1->we_opened == a2->we_opened); + CHECK(a1->leased == a2->leased); + CHECK(a1->onchain_resolved_block == a2->onchain_resolved_block); + + CHECK((a1->open_event_db_id != NULL) == (a2->open_event_db_id != NULL)); + if (a1->open_event_db_id) + CHECK(*a1->open_event_db_id == *a2->open_event_db_id); + + CHECK((a1->closed_event_db_id != NULL) == (a2->closed_event_db_id != NULL)); + if (a1->closed_event_db_id) + CHECK(*a1->closed_event_db_id == *a2->closed_event_db_id); + + return true; +} + +static bool channel_events_eq(struct channel_event *e1, struct channel_event *e2) +{ + CHECK(e1->db_id == e2->db_id); + CHECK(e1->acct_db_id == e2->acct_db_id); + CHECK(streq(e1->tag, e2->tag)); + CHECK(amount_msat_eq(e1->credit, e2->credit)); + CHECK(amount_msat_eq(e1->debit, e2->debit)); + CHECK(amount_msat_eq(e1->fees, e2->fees)); + CHECK(streq(e1->currency, e2->currency)); + CHECK(sha256_eq(&e1->payment_id, &e2->payment_id)); + CHECK(e1->part_id == e2->part_id); + CHECK(e1->timestamp == e2->timestamp); + + return true; +} + +static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) +{ + CHECK(e1->db_id == e2->db_id); + CHECK(e1->acct_db_id == e2->acct_db_id); + CHECK(streq(e1->tag, e2->tag)); + CHECK(amount_msat_eq(e1->credit, e2->credit)); + CHECK(amount_msat_eq(e1->debit, e2->debit)); + CHECK(amount_msat_eq(e1->output_value, e2->output_value)); + CHECK(streq(e1->currency, e2->currency)); + CHECK(e1->timestamp == e2->timestamp); + CHECK(e1->blockheight == e2->blockheight); + CHECK(bitcoin_outpoint_eq(&e1->outpoint, &e2->outpoint)); + + CHECK((e1->spending_txid != NULL) == (e2->spending_txid != NULL)); + if (e1->spending_txid) + CHECK(bitcoin_txid_eq(e1->spending_txid, e2->spending_txid)); + + CHECK((e1->payment_id != NULL) == (e2->payment_id != NULL)); + if (e1->payment_id) + CHECK(sha256_eq(e1->payment_id, e2->payment_id)); + + return true; +} + +/* +static bool onchain_fees_eq(struct onchain_fee *of1, struct onchain_fee *of2) +{ + CHECK(of1->acct_db_id == of2->acct_db_id); + CHECK(bitcoin_txid_eq(&of1->txid, &of2->txid)); + CHECK(amount_msat_eq(of1->amount, of2->amount)); + CHECK(streq(of1->currency, of2->currency)); + + return true; +} +*/ + +static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id peer_id; + struct account *acct, *acct2; + struct channel_event ev1, ev2, ev3, **chan_evs; + char *name = tal_fmt(ctx, "example"); + + memset(&peer_id, 3, sizeof(struct node_id)); + + acct = new_account(ctx, name, &peer_id); + acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + db_begin_transaction(db); + account_add(db, acct); + account_add(db, acct2); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + memset(&ev1.payment_id, 'B', sizeof(struct sha256)); + ev1.credit = AMOUNT_MSAT(100); + ev1.debit = AMOUNT_MSAT(102); + ev1.fees = AMOUNT_MSAT(104); + ev1.currency = "btc"; + ev1.timestamp = 1919191; + ev1.part_id = 19; + + /* Passing unknown tags in should be ok */ + ev1.tag = "hello"; + + memset(&ev2.payment_id, 'C', sizeof(struct sha256)); + ev2.credit = AMOUNT_MSAT(200); + ev2.debit = AMOUNT_MSAT(202); + ev2.fees = AMOUNT_MSAT(204); + ev2.currency = "brct"; + ev2.timestamp = 1818181; + ev2.part_id = 0; + ev2.tag = tal_fmt(ctx, "deposit"); + + memset(&ev3.payment_id, 'D', sizeof(struct sha256)); + ev3.credit = AMOUNT_MSAT(300); + ev3.debit = AMOUNT_MSAT(302); + ev3.fees = AMOUNT_MSAT(304); + ev3.currency = "brct"; + ev3.timestamp = 1717171; + ev3.part_id = 5; + ev3.tag = tal_fmt(ctx, "routed"); + + db_begin_transaction(db); + log_channel_event(db, acct, &ev1); + log_channel_event(db, acct, &ev2); + + /* log a channel event to a different acct */ + log_channel_event(db, acct2, &ev3); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + db_begin_transaction(db); + chan_evs = account_get_channel_events(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK_MSG(!db_err, db_err); + + CHECK(tal_count(chan_evs) == 2); + channel_events_eq(&ev1, chan_evs[0]); + channel_events_eq(&ev2, chan_evs[1]); + + return true; +} + +static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id peer_id; + struct account *acct, *acct2; + struct chain_event ev1, *ev2, ev3, **chain_evs; + char *name = tal_fmt(ctx, "example"); + + ev2 = tal(ctx, struct chain_event); + memset(&peer_id, 3, sizeof(struct node_id)); + + acct = new_account(ctx, name, &peer_id); + acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + db_begin_transaction(db); + account_add(db, acct); + account_add(db, acct2); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* This event spends the second inserted event */ + ev1.tag = tal_fmt(ctx, "withdrawal"); + ev1.credit = AMOUNT_MSAT(100); + ev1.debit = AMOUNT_MSAT(102); + ev1.output_value = AMOUNT_MSAT(104); + ev1.currency = "btc"; + ev1.timestamp = 1919191; + ev1.blockheight = 1919191; + memset(&ev1.outpoint.txid, 'D', sizeof(struct bitcoin_txid)); + ev1.outpoint.n = 1; + ev1.spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev1.spending_txid, 'C', sizeof(struct bitcoin_txid)); + ev1.payment_id = NULL; + + db_begin_transaction(db); + log_chain_event(db, acct, &ev1); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + ev2->tag = tal_fmt(ctx, "deposit"); + ev2->credit = AMOUNT_MSAT(200); + ev2->debit = AMOUNT_MSAT(202); + ev2->output_value = AMOUNT_MSAT(104); + ev2->currency = "btc"; + ev2->timestamp = 1919191; + ev2->blockheight = 1919191; + memset(&ev2->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); + ev2->outpoint.n = 1; + ev2->spending_txid = NULL; + ev2->payment_id = tal(ctx, struct sha256); + memset(ev2->payment_id, 'B', sizeof(struct sha256)); + + /* Dummy event, logged to separate account */ + ev3.tag = tal_fmt(ctx, "deposit"); + ev3.credit = AMOUNT_MSAT(300); + ev3.debit = AMOUNT_MSAT(302); + ev3.output_value = AMOUNT_MSAT(304); + ev3.currency = "btc"; + ev3.timestamp = 3939393; + ev3.blockheight = 3939393; + memset(&ev3.outpoint.txid, 'E', sizeof(struct bitcoin_txid)); + ev3.outpoint.n = 1; + ev3.spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev3.spending_txid, 'D', sizeof(struct bitcoin_txid)); + ev3.payment_id = NULL; + + db_begin_transaction(db); + log_chain_event(db, acct, ev2); + + /* log new event to a different account.. */ + log_chain_event(db, acct2, &ev3); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Try to add an already exiting event */ + db_begin_transaction(db); + log_chain_event(db, acct, ev2); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Ok now we ge the list, there should only be two */ + db_begin_transaction(db); + chain_evs = account_get_chain_events(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK(tal_count(chain_evs) == 2); + + chain_events_eq(&ev1, chain_evs[0]); + chain_events_eq(ev2, chain_evs[1]); + + /* Now insert a utxo create and spend, in that order */ + ev1.db_id = 0; + memset(&ev1.outpoint.txid, 'A', sizeof(struct bitcoin_txid)); + ev1.outpoint.n = 10; + ev1.spending_txid = tal_free(ev1.spending_txid); + + ev2->db_id = 0; + memset(&ev2->outpoint.txid, 'A', sizeof(struct bitcoin_txid)); + ev2->outpoint.n = 10; + ev2->spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev2->spending_txid, 'B', sizeof(struct bitcoin_txid)); + + + db_begin_transaction(db); + log_chain_event(db, acct, &ev1); + log_chain_event(db, acct, ev2); + chain_evs = account_get_chain_events(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* There should be four now */ + CHECK(tal_count(chain_evs) == 4); + + chain_events_eq(&ev1, chain_evs[2]); + chain_events_eq(ev2, chain_evs[3]); + + return true; +} + +static bool test_account_crud(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id peer_id; + struct account *acct, *acct2, **acct_list; + struct chain_event ev1; + enum mvt_tag *tags; + char *name = tal_fmt(ctx, "example"); + + memset(&peer_id, 3, sizeof(struct node_id)); + + acct = new_account(ctx, name, &peer_id); + CHECK(!acct->is_wallet); + + db_begin_transaction(db); + account_add(db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + db_begin_transaction(db); + acct_list = list_accounts(ctx, db); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK(tal_count(acct_list) == 1); + accountseq(acct_list[0], acct); + + acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + CHECK(acct->is_wallet); + + db_begin_transaction(db); + account_add(db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + db_begin_transaction(db); + acct_list = list_accounts(ctx, db); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK(tal_count(acct_list) == 2); + + /* Can we find an account ok? */ + db_begin_transaction(db); + acct2 = find_account(ctx, db, "wallet"); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + accountseq(acct, acct2); + + /* Will we update an account's properties + * correctly, given an event and tag list? */ + ev1.tag = tal_fmt(ctx, "withdrawal"); + ev1.credit = AMOUNT_MSAT(100); + ev1.debit = AMOUNT_MSAT(102); + ev1.output_value = AMOUNT_MSAT(104); + ev1.currency = "btc"; + ev1.timestamp = 1919191; + ev1.blockheight = 1919191; + memset(&ev1.outpoint.txid, 'D', sizeof(struct bitcoin_txid)); + ev1.outpoint.n = 1; + ev1.spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev1.spending_txid, 'C', sizeof(struct bitcoin_txid)); + ev1.payment_id = NULL; + + db_begin_transaction(db); + log_chain_event(db, acct, &ev1); + + tags = tal_arr(ctx, enum mvt_tag, 2); + + /* should not update the account info */ + tags[0] = PUSHED; + tags[1] = PENALTY; + maybe_update_account(db, acct, &ev1, tags); + acct2 = find_account(ctx, db, "wallet"); + accountseq(acct, acct2); + + /* channel_open -> open event db updated */ + CHECK(!acct->leased); + CHECK(acct->open_event_db_id == NULL); + tags[0] = CHANNEL_OPEN; + tags[1] = LEASED; + maybe_update_account(db, acct, &ev1, tags); + acct2 = find_account(ctx, db, "wallet"); + accountseq(acct, acct2); + CHECK(acct->leased); + CHECK(acct->open_event_db_id != NULL); + + tags[0] = CHANNEL_CLOSE; + tags[1] = OPENER; + CHECK(acct->closed_event_db_id == NULL); + CHECK(!acct->we_opened); + maybe_update_account(db, acct, &ev1, tags); + acct2 = find_account(ctx, db, "wallet"); + accountseq(acct, acct2); + CHECK(acct->closed_event_db_id != NULL); + CHECK(acct->we_opened); + + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + return true; +} + +int main(int argc, char *argv[]) +{ + bool ok = true; + /* Dummy for migration hooks */ + struct plugin *plugin = tal(NULL, struct plugin); + plugin->js_arr = tal_arr(plugin, struct json_stream *, 0); + + common_setup(argv[0]); + + ok &= test_account_crud(tmpctx, plugin); + ok &= test_channel_event_crud(tmpctx, plugin); + ok &= test_chain_event_crud(tmpctx, plugin); + + tal_free(plugin); + common_shutdown(); + return !ok; +} From dc113d0a3fd8bfe13616367f529c4bd1525e680c Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 15:04:40 +0930 Subject: [PATCH 1130/1530] bkpr: create onchain fee records for events clightning doesn't give us any info about onchain fees (how could it? it only knows about utxo object levels, and doesn't keep track of how/when those are all related) Instead, we keep running totals of the onchain fees for utxos. This implements the master method for accounting for them, plus includes tests to account for channel opens (across two accounts) as well as a htlc-tx channel close. Missing: we don't currently emit an event from cln for `withdraw` initiated removal of funds, so the accounting for wallet -> external funds is a bit janky. We don't account for the fees on these transactions since we don't have the resulting 'external' event to register them against! --- plugins/bkpr/recorder.c | 344 ++++++++++++++++++++++++++--- plugins/bkpr/recorder.h | 9 + plugins/bkpr/test/run-recorder.c | 359 ++++++++++++++++++++++++++++++- 3 files changed, 678 insertions(+), 34 deletions(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index c1b7826dc19d..d48e5f2105a8 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -1,7 +1,10 @@ #include "config.h" +#include #include +#include #include #include +#include #include #include #include @@ -12,6 +15,9 @@ #include #include +#define EXTERNAL_ACCT "external" +#define WALLET_ACCT WALLET + static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt) { struct chain_event *e = tal(ctx, struct chain_event); @@ -46,6 +52,24 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st return e; } +static struct chain_event **find_chain_events(const tal_t *ctx, + struct db_stmt *stmt TAKES) +{ + struct chain_event **results; + + db_query_prepared(stmt); + results = tal_arr(ctx, struct chain_event *, 0); + while (db_step(stmt)) { + struct chain_event *e = stmt2chain_event(results, stmt); + tal_arr_expand(&results, e); + } + + if (taken(stmt)) + tal_free(stmt); + + return results; +} + static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt *stmt) { struct channel_event *e = tal(ctx, struct channel_event); @@ -72,7 +96,6 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, struct account *acct) { struct db_stmt *stmt; - struct chain_event **results; stmt = db_prepare_v2(db, SQL("SELECT" " id" @@ -92,16 +115,7 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, " WHERE account_id = ?;")); db_bind_int(stmt, 0, acct->db_id); - db_query_prepared(stmt); - - results = tal_arr(ctx, struct chain_event *, 0); - while (db_step(stmt)) { - struct chain_event *e = stmt2chain_event(results, stmt); - tal_arr_expand(&results, e); - } - tal_free(stmt); - - return results; + return find_chain_events(ctx, take(stmt)); } static struct chain_event *find_chain_event(const tal_t *ctx, @@ -207,6 +221,43 @@ struct channel_event **account_get_channel_events(const tal_t *ctx, return results; } +static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, + struct db_stmt *stmt) +{ + struct onchain_fee *of = tal(ctx, struct onchain_fee); + + of->acct_db_id = db_col_u64(stmt, "account_id"); + db_col_txid(stmt, "txid", &of->txid); + db_col_amount_msat(stmt, "amount", &of->amount); + of->currency = db_col_strdup(of, stmt, "currency"); + + return of; +} + +struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) +{ + struct db_stmt *stmt; + struct onchain_fee **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " account_id" + ", txid" + ", amount" + ", currency" + " FROM onchain_fees" + " ORDER BY account_id")); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct onchain_fee *, 0); + while (db_step(stmt)) { + struct onchain_fee *of = stmt2onchain_fee(results, stmt); + tal_arr_expand(&results, of); + } + tal_free(stmt); + + return results; +} + static struct account *stmt2account(const tal_t *ctx, struct db_stmt *stmt) { struct account *a = tal(ctx, struct account); @@ -276,18 +327,6 @@ struct account *find_account(const tal_t *ctx, return a; } -static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, struct db_stmt *stmt) -{ - struct onchain_fee *of = tal(ctx, struct onchain_fee); - - of->acct_db_id = db_col_u64(stmt, "account_id"); - db_col_txid(stmt, "txid", &of->txid); - db_col_amount_msat(stmt, "amount", &of->amount); - of->currency = db_col_strdup(of, stmt, "currency"); - - return of; -} - struct onchain_fee **account_onchain_fees(const tal_t *ctx, struct db *db, struct account *acct) @@ -303,10 +342,10 @@ struct onchain_fee **account_onchain_fees(const tal_t *ctx, " FROM onchain_fees" " WHERE account_id = ?;")); - db_bind_int(stmt, 0, acct->db_id); + db_bind_u64(stmt, 0, acct->db_id); db_query_prepared(stmt); - results = tal_arr(ctx, struct onchain_fee*, 0); + results = tal_arr(ctx, struct onchain_fee *, 0); while (db_step(stmt)) { struct onchain_fee *of = stmt2onchain_fee(results, stmt); tal_arr_expand(&results, of); @@ -492,6 +531,261 @@ void log_channel_event(struct db *db, tal_free(stmt); } +static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db *db, + struct bitcoin_txid *txid) +{ + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("SELECT " + " id" + ", account_id" + ", tag" + ", credit" + ", debit" + ", output_value" + ", currency" + ", timestamp" + ", blockheight" + ", utxo_txid" + ", outnum" + ", spending_txid" + ", payment_id" + " FROM chain_events" + " WHERE spending_txid = ?" + " OR (utxo_txid = ? AND spending_txid IS NULL)" + " ORDER BY account_id")); + + db_bind_txid(stmt, 0, txid); + db_bind_txid(stmt, 1, txid); + return find_chain_events(ctx, take(stmt)); +} + +static u64 find_acct_id(struct db *db, const char *name) +{ + u64 acct_id; + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + " FROM accounts" + " WHERE name = ?")); + + db_bind_text(stmt, 0, name); + db_query_prepared(stmt); + if (db_step(stmt)) + acct_id = db_col_u64(stmt, "id"); + else + acct_id = 0; + + tal_free(stmt); + return acct_id; +} + +static void update_or_insert_chain_fees(struct db *db, + u64 acct_id, + struct bitcoin_txid *txid, + struct amount_msat *amount, + const char *currency) +{ + struct db_stmt *stmt; + + /* First, look to see if there's an already existing + * record to update */ + stmt = db_prepare_v2(db, SQL("SELECT" + " 1" + " FROM onchain_fees" + " WHERE txid = ?" + " AND account_id = ?")); + + db_bind_txid(stmt, 0, txid); + db_bind_u64(stmt, 1, acct_id); + db_query_prepared(stmt); + + /* If there's no current record, add it */ + if (!db_step(stmt)) { + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("INSERT INTO onchain_fees" + " (" + " account_id" + ", txid" + ", amount" + ", currency" + ") VALUES" + " (?, ?, ?, ?);")); + + db_bind_u64(stmt, 0, acct_id); + db_bind_txid(stmt, 1, txid); + db_bind_amount_msat(stmt, 2, amount); + db_bind_text(stmt, 3, currency); + db_exec_prepared_v2(take(stmt)); + return; + } + + /* Otherwise, we update the existing record */ + db_col_ignore(stmt, "1"); + tal_free(stmt); + stmt = db_prepare_v2(db, SQL("UPDATE onchain_fees SET" + " amount = ?" + " WHERE txid = ?" + " AND account_id = ?")); + + db_bind_amount_msat(stmt, 0, amount); + db_bind_txid(stmt, 1, txid); + db_bind_u64(stmt, 2, acct_id); + db_exec_prepared_v2(take(stmt)); +} + +char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, + struct bitcoin_txid *txid) +{ + size_t no_accts = 0, plus_ones; + u64 last_id = 0, wallet_id, extern_id; + bool contains_wallet = false, skip_wallet = true; + struct chain_event **events; + struct amount_msat deposit_msat = AMOUNT_MSAT(0), + withdraw_msat = AMOUNT_MSAT(0), + fees_msat, fee_part_msat; + char *err = NULL; + u8 *inner_ctx = tal(NULL, u8); + + /* Find all the deposits/withdrawals for this txid */ + events = find_chain_events_bytxid(inner_ctx, db, txid); + wallet_id = find_acct_id(db, WALLET_ACCT); + extern_id = find_acct_id(db, EXTERNAL_ACCT); + + /* If we don't even have two events, skip */ + if (tal_count(events) < 2) + goto finished; + + for (size_t i = 0; i < tal_count(events); i++) { + if (events[i]->spending_txid) { + if (!amount_msat_add(&withdraw_msat, withdraw_msat, + events[i]->debit)) { + err = tal_fmt(ctx, "Overflow adding withdrawal debits for" + " txid: %s", + type_to_string(ctx, struct bitcoin_txid, + txid)); + goto finished; + } + } else { + if (!amount_msat_add(&deposit_msat, deposit_msat, + events[i]->credit)) { + err = tal_fmt(ctx, "Overflow adding deposit credits for" + " txid: %s", + type_to_string(ctx, struct bitcoin_txid, + txid)); + goto finished; + } + } + + /* While we're here, also count number of accts + * that were involved! Two little tricks here. + * + * One) we sorted the output + * by acct id, so we can cheat how we count: if + * it's a different acct_id than the last seen, we inc + * the counter. + * + * Two) who "gets" fee attribution is complicated + * and requires knowing if the wallet/external accts + * were involved (everything else is channel accts) + * */ + if (last_id != events[i]->acct_db_id) { + last_id = events[i]->acct_db_id; + /* Don't count external accts */ + if (last_id != extern_id) + no_accts++; + + contains_wallet |= (last_id == wallet_id); + } + } + + /* If either is zero, keep waiting */ + if (amount_msat_zero(withdraw_msat) + || amount_msat_zero(deposit_msat)) + goto finished; + + /* If our withdraws < deposits, wait for more data */ + if (amount_msat_less(withdraw_msat, deposit_msat)) + goto finished; + + /* At this point, we have no way to know we've gotten all the data. + * But that's what the 'onchain_resolved_block' marker on + * accounts is for */ + if (!amount_msat_sub(&fees_msat, withdraw_msat, deposit_msat)) { + err = tal_fmt(ctx, "Err subtracting withdraw %s from deposit %s" + " for txid %s", + type_to_string(ctx, struct amount_msat, &withdraw_msat), + type_to_string(ctx, struct amount_msat, &deposit_msat), + type_to_string(ctx, struct bitcoin_txid, txid)); + goto finished; + } + + /* Now we need to figure out how to allocate fees to each account + * that was involved in the tx. This is a lil complex, buckle up*/ + + /* If the wallet's involved + there were any other accounts, decr by one */ + if (no_accts > 1 && contains_wallet) { + skip_wallet = true; + no_accts--; + } + + /* Now we divide by the number of accts involved, to figure out the + * value to log for each account! */ + fee_part_msat = amount_msat_div(fees_msat, no_accts); + + /* So we don't lose any msats b/c of rounding, find the number of + * accts to add an extra msat onto */ + plus_ones = fees_msat.millisatoshis % no_accts; /* Raw: mod calc */ + + /* Now we log (or update the existing record) for each acct */ + last_id = 0; + for (size_t i = 0; i < tal_count(events); i++) { + struct amount_msat fees; + + if (last_id == events[i]->acct_db_id) + continue; + + last_id = events[i]->acct_db_id; + + /* We *never assign fees to external accounts; + * if external funds were contributed to a tx + * we wouldn't record it -- fees are solely ours */ + if (last_id == extern_id) + continue; + + /* We only attribute fees to the wallet + * if the wallet is the only game in town */ + if (skip_wallet && last_id == wallet_id) + continue; + + /* Add an extra msat onto plus_ones accts + * so we don't lose any precision in + * our accounting */ + if (plus_ones > 0) { + plus_ones--; + if (!amount_msat_add(&fees, fee_part_msat, + AMOUNT_MSAT(1))) { + err = "Overflow adding 1 ... yeah right"; + /* We're gonna keep going, yolo */ + fees = fee_part_msat; + } + } else + fees = fee_part_msat; + + /* FIXME: fee_currency property of acct? */ + update_or_insert_chain_fees(db, last_id, + txid, &fees, + events[i]->currency); + + } + +finished: + tal_free(inner_ctx); + return err; +} + void log_chain_event(struct db *db, const struct account *acct, struct chain_event *e) diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index abe32823a83e..888c349d3c2d 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -5,6 +5,7 @@ #include struct account; +struct bitcoin_txid; struct chain_event; struct channel_event; struct db; @@ -29,6 +30,9 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, struct db *db, struct account *acct); +/* List all chain fees, for all accounts */ +struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db); + /* Add the given account to the database */ void account_add(struct db *db, struct account *acct); @@ -43,6 +47,11 @@ void maybe_update_account(struct db *db, struct chain_event *e, const enum mvt_tag *tags); +/* Update our onchain fees now? */ +char *maybe_update_onchain_fees(const tal_t *ctx, + struct db *db, + struct bitcoin_txid *txid); + /* Log a channel event */ void log_channel_event(struct db *db, const struct account *acct, diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index bff4a17a3bf1..7ab02b79ab2f 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -290,17 +290,356 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) return true; } -/* -static bool onchain_fees_eq(struct onchain_fee *of1, struct onchain_fee *of2) +static struct chain_event *make_chain_event(const tal_t *ctx, + char *tag, + struct amount_msat credit, + struct amount_msat debit, + char outpoint_char, + u32 outnum, + /* Note that '*' is magic */ + char spend_char) + + +{ + struct chain_event *ev = tal(ctx, struct chain_event); + + /* This event spends the second inserted event */ + ev->tag = tal_fmt(ctx, "%s", tag); + ev->credit = credit; + ev->debit = debit; + ev->output_value = AMOUNT_MSAT(1000); + ev->currency = "btc"; + ev->timestamp = 1919191; + ev->blockheight = 1919191; + memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid)); + ev->outpoint.n = outnum; + + if (spend_char != '*') { + ev->spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev->spending_txid, spend_char, + sizeof(struct bitcoin_txid)); + } else + ev->spending_txid = NULL; + + ev->payment_id = NULL; + + return ev; +} + +static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id node_id, peer_id; + struct account *wal_acct, *ext_acct; + struct bitcoin_txid txid; + struct onchain_fee **ofs; + + memset(&node_id, 2, sizeof(struct node_id)); + memset(&peer_id, 3, sizeof(struct node_id)); + + wal_acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); + + db_begin_transaction(db); + account_add(db, wal_acct); + account_add(db, ext_acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Send funds to an external address + * tag utxo_id vout txid debits credits acct_id + * withdr XXXX 0 1111 1000 wallet + * deposit 1111 1 200 wallet + * *screms* + */ + db_begin_transaction(db); + log_chain_event(db, wal_acct, + make_chain_event(ctx, "withdrawal", + AMOUNT_MSAT(0), + AMOUNT_MSAT(1000), + 'X', 0, '1')); + memset(&txid, '1', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + + log_chain_event(db, wal_acct, + make_chain_event(ctx, "deposit", + AMOUNT_MSAT(200), + AMOUNT_MSAT(0), + '1', 1, '*')); + memset(&txid, '1', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Send some funds to an external acct? */ + db_begin_transaction(db); + ofs = list_chain_fees(ctx, db); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* FIXME: track onchain wallet withdrawals?? */ + CHECK(tal_count(ofs) == 0); + + return true; +} + +static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id node_id, peer_id; + struct account *acct, *wal_acct, *ext_acct; + struct onchain_fee **ofs, **ofs1; + struct bitcoin_txid txid; + + memset(&node_id, 2, sizeof(struct node_id)); + memset(&peer_id, 3, sizeof(struct node_id)); + + wal_acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); + acct = new_account(ctx, tal_fmt(ctx, "chan-1"), &peer_id); + + db_begin_transaction(db); + account_add(db, wal_acct); + account_add(db, ext_acct); + account_add(db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Close a channel */ + /* tag utxo_id vout txid debits credits acct_id + * close XXXX 0 1111 1000 wallet + * delay 1111 1 2222 200 chan-1 + * htlc_tx 1111 2 3333 600 chan-1 + * to_them 1111 3 external + * to_wall 3333 0 500 wallet + * to_wall 2222 0 150 wallet + */ + db_begin_transaction(db); + log_chain_event(db, wal_acct, + make_chain_event(ctx, "close", + AMOUNT_MSAT(0), + AMOUNT_MSAT(1000), + 'X', 0, '1')); + + log_chain_event(db, acct, + make_chain_event(ctx, "delayed_to_us", + AMOUNT_MSAT(200), + AMOUNT_MSAT(0), + '1', 1, '*')); + memset(&txid, '1', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + + /* Should be one fees now */ + ofs = list_chain_fees(ctx, db); + CHECK_MSG(!db_err, db_err); + + CHECK(tal_count(ofs) == 1); + CHECK(ofs[0]->acct_db_id == acct->db_id); + CHECK(amount_msat_eq(ofs[0]->amount, AMOUNT_MSAT(800))); + + log_chain_event(db, acct, + make_chain_event(ctx, "htlc_tx", + AMOUNT_MSAT(600), + AMOUNT_MSAT(0), + '1', 2, '*')); + + log_chain_event(db, ext_acct, + make_chain_event(ctx, "to_them", + AMOUNT_MSAT(0), + AMOUNT_MSAT(0), + '1', 3, '*')); + + memset(&txid, '1', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* txid 2222 */ + db_begin_transaction(db); + log_chain_event(db, acct, + make_chain_event(ctx, "withdraw", + AMOUNT_MSAT(0), + AMOUNT_MSAT(200), + '1', 1, '2')); + + log_chain_event(db, wal_acct, + make_chain_event(ctx, "to_wallet", + AMOUNT_MSAT(150), + AMOUNT_MSAT(0), + '2', 0, '*')); + memset(&txid, '2', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Expect: 2 onchain fee records, all for chan-1 */ + db_begin_transaction(db); + ofs = list_chain_fees(ctx, db); + ofs1 = account_onchain_fees(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + CHECK(tal_count(ofs) == tal_count(ofs1)); + CHECK(tal_count(ofs) == 2); + + /* txid 3333 */ + db_begin_transaction(db); + log_chain_event(db, acct, + make_chain_event(ctx, "htlc_tx", + AMOUNT_MSAT(0), + AMOUNT_MSAT(600), + '1', 2, '3')); + + log_chain_event(db, wal_acct, + make_chain_event(ctx, "to_wallet", + AMOUNT_MSAT(500), + AMOUNT_MSAT(0), + '3', 0, '*')); + memset(&txid, '3', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Expect: 3 onchain fee records, all for chan-1 */ + db_begin_transaction(db); + ofs = list_chain_fees(ctx, db); + ofs1 = account_onchain_fees(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + CHECK(tal_count(ofs) == tal_count(ofs1)); + CHECK(tal_count(ofs) == 3); + + /* Expect: fees as follows + * + * chan-1, 1111, 200 + * chan-1, 3333, 100 + * chan-1, 2222, 50 + */ + for (size_t i = 0; i < tal_count(ofs); i++) { + CHECK(ofs[i]->acct_db_id == acct->db_id); + CHECK(streq(ofs[i]->currency, "btc")); + + memset(&txid, '1', sizeof(struct bitcoin_txid)); + if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { + CHECK(200 == ofs[i]->amount.millisatoshis); /* Raw: test eq */ + continue; + } + memset(&txid, '2', sizeof(struct bitcoin_txid)); + if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { + CHECK(50 == ofs[i]->amount.millisatoshis); /* Raw: test eq */ + continue; + } + memset(&txid, '3', sizeof(struct bitcoin_txid)); + if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { + CHECK(100 == ofs[i]->amount.millisatoshis); /* Raw: test eq */ + continue; + } + + CHECK_MSG(false, "txid didn't match"); + } + + return true; +} + +static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) { - CHECK(of1->acct_db_id == of2->acct_db_id); - CHECK(bitcoin_txid_eq(&of1->txid, &of2->txid)); - CHECK(amount_msat_eq(of1->amount, of2->amount)); - CHECK(streq(of1->currency, of2->currency)); + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id node_id, peer_id; + struct account *acct, *acct2, *wal_acct, *ext_acct; + struct bitcoin_txid txid; + struct onchain_fee **ofs; + + memset(&node_id, 2, sizeof(struct node_id)); + memset(&peer_id, 3, sizeof(struct node_id)); + + wal_acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); + acct = new_account(ctx, tal_fmt(ctx, "chan-1"), &peer_id); + acct2 = new_account(ctx, tal_fmt(ctx, "chan-2"), &peer_id); + + db_begin_transaction(db); + account_add(db, wal_acct); + account_add(db, ext_acct); + account_add(db, acct); + account_add(db, acct2); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Assumption that we rely on later */ + CHECK(acct->db_id < acct2->db_id); + + /* Open two channels from wallet */ + /* tag utxo_id vout txid debits credits acct_id + * withd XXXX 0 AAAA 1000 wallet + * withd YYYY 0 AAAA 3000 wallet + * open AAAA 0 500 chan-1 + * open AAAA 1 1000 chan-2 + * depo AAAA 2 2200 wallet + */ + memset(&txid, 'A', sizeof(struct bitcoin_txid)); + db_begin_transaction(db); + log_chain_event(db, acct, + make_chain_event(ctx, "deposit", + AMOUNT_MSAT(500), + AMOUNT_MSAT(0), + 'A', 0, '*')); + + log_chain_event(db, acct2, + make_chain_event(ctx, "deposit", + AMOUNT_MSAT(1000), + AMOUNT_MSAT(0), + 'A', 1, '*')); + + log_chain_event(db, wal_acct, + make_chain_event(ctx, "deposit", + AMOUNT_MSAT(2200), + AMOUNT_MSAT(0), + 'A', 2, '*')); + maybe_update_onchain_fees(ctx, db, &txid); + + /* Should be no fee records yet! */ + ofs = list_chain_fees(ctx, db); + CHECK_MSG(tal_count(ofs) == 0, + "no fees counted yet"); + + log_chain_event(db, wal_acct, + make_chain_event(ctx, "withdrawal", + AMOUNT_MSAT(0), + AMOUNT_MSAT(1000), + 'X', 0, 'A')); + log_chain_event(db, wal_acct, + make_chain_event(ctx, "withdrawal", + AMOUNT_MSAT(0), + AMOUNT_MSAT(3001), + 'Y', 0, 'A')); + + maybe_update_onchain_fees(ctx, db, &txid); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Expect: 2 onchain fee records of 151/150msat ea, + * none for wallet */ + db_begin_transaction(db); + ofs = list_chain_fees(ctx, db); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + CHECK(tal_count(ofs) == 2); + /* Since these are sorted by acct_id on fetch, + * this *should* be stable */ + CHECK(ofs[0]->acct_db_id == acct->db_id); + CHECK(amount_msat_eq(ofs[0]->amount, AMOUNT_MSAT(151))); + CHECK(streq(ofs[0]->currency, "btc")); + CHECK(bitcoin_txid_eq(&ofs[0]->txid, &txid)); + + CHECK(ofs[1]->acct_db_id == acct2->db_id); + CHECK(amount_msat_eq(ofs[1]->amount, AMOUNT_MSAT(150))); + CHECK(streq(ofs[1]->currency, "btc")); + CHECK(bitcoin_txid_eq(&ofs[1]->txid, &txid)); return true; } -*/ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) { @@ -308,11 +647,10 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) struct node_id peer_id; struct account *acct, *acct2; struct channel_event ev1, ev2, ev3, **chan_evs; - char *name = tal_fmt(ctx, "example"); memset(&peer_id, 3, sizeof(struct node_id)); - acct = new_account(ctx, name, &peer_id); + acct = new_account(ctx, tal_fmt(ctx, "example"), &peer_id); acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); db_begin_transaction(db); account_add(db, acct); @@ -602,6 +940,9 @@ int main(int argc, char *argv[]) ok &= test_account_crud(tmpctx, plugin); ok &= test_channel_event_crud(tmpctx, plugin); ok &= test_chain_event_crud(tmpctx, plugin); + ok &= test_onchain_fee_chan_close(tmpctx, plugin); + ok &= test_onchain_fee_chan_open(tmpctx, plugin); + ok &= test_onchain_fee_wallet_spend(tmpctx, plugin); tal_free(plugin); common_shutdown(); From 351dc17e4602d20cd1a75323a14fa49cb87b38bb Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 15:26:04 +0930 Subject: [PATCH 1131/1530] bkpr: add bookkeeper to PLUGINS list This makes it start up automatically --- plugins/Makefile | 10 +++++----- plugins/bkpr/Makefile | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index 8259eb64cb88..e38312c14de9 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -108,11 +108,6 @@ plugins/cln-grpc: target/${RUST_PROFILE}/cln-grpc PLUGINS += plugins/cln-grpc endif -# Make sure these depend on everything. -ALL_C_SOURCES += $(PLUGIN_ALL_SRC) -ALL_C_HEADERS += $(PLUGIN_ALL_HEADER) -ALL_PROGRAMS += $(C_PLUGINS) - PLUGIN_COMMON_OBJS := \ bitcoin/base58.o \ bitcoin/block.o \ @@ -165,6 +160,11 @@ PLUGIN_COMMON_OBJS := \ include plugins/bkpr/Makefile +# Make sure these depend on everything. +ALL_C_SOURCES += $(PLUGIN_ALL_SRC) +ALL_C_HEADERS += $(PLUGIN_ALL_HEADER) +ALL_PROGRAMS += $(C_PLUGINS) + # Make all plugins depend on all plugin headers, for simplicity. $(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER) diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index e076863153ca..a205c46fcbdc 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -23,9 +23,10 @@ BOOKKEEPER_OBJS := $(BOOKKEEPER_SRC:.c=.o) $(BOOKKEEPER_OBJS): $(PLUGIN_LIB_HEADER) $(BOOKKEEPER_HEADER) -ALL_C_SOURCES += $(BOOKKEEPER_SRC) -ALL_C_HEADERS += $(BOOKKEEPER_HEADER) -ALL_PROGRAMS += plugins/bookkeeper +PLUGIN_ALL_SRC += $(BOOKKEEPER_SRC) $(BOOKKEEPER_DB_QUERIES) +PLUGIN_ALL_HEADER += $(BOOKKEEPER_HEADER) +C_PLUGINS += plugins/bookkeeper +PLUGINS += plugins/bookkeeper plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(DB_OBJS) From 721ceb7519ab4e26efe51524b62f68bd110c2e1c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Jul 2022 17:04:31 +0930 Subject: [PATCH 1132/1530] patch db-fatal-plugin_err.patch --- plugins/bkpr/db.c | 14 ++++++++------ plugins/libplugin.c | 20 ++++++++++++++------ plugins/libplugin.h | 4 ++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 0b44de6095aa..9d89c17b97e1 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -13,6 +13,8 @@ struct migration { void (*func)(struct plugin *p, struct db *db); }; +static struct plugin *plugin; + /* Do not reorder or remove elements from this array. * It is used to migrate existing databases from a prevoius state, based on * string indicies */ @@ -136,21 +138,21 @@ static bool db_migrate(struct plugin *p, struct db *db) void db_fatal(const char *fmt, ...) { va_list ap; - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); + plugin_errv(plugin, fmt, ap); + /* Won't actually exit, but va_end() required to balance va_start in standard. */ va_end(ap); - - exit(1); } #endif /* DB_FATAL */ struct db *db_setup(const tal_t *ctx, struct plugin *p, char *db_dsn) { bool migrated; - struct db *db = db_open(ctx, db_dsn); + struct db *db; + /* Set global for db_fatal */ + plugin = p; + db = db_open(ctx, db_dsn); db->report_changes_fn = NULL; db_begin_transaction(db); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 9098506ae2e8..900b1cc2fe33 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1288,18 +1288,26 @@ void NORETURN plugin_exit(struct plugin *p, int exitcode) exit(exitcode); } +void NORETURN plugin_errv(struct plugin *p, const char *fmt, va_list ap) +{ + va_list ap2; + + /* In case it gets consumed, make a copy. */ + va_copy(ap2, ap); + + plugin_logv(p, LOG_BROKEN, fmt, ap); + vfprintf(stderr, fmt, ap2); + plugin_exit(p, 1); + va_end(ap2); +} + void NORETURN plugin_err(struct plugin *p, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - plugin_logv(p, LOG_BROKEN, fmt, ap); + plugin_errv(p, fmt, ap); va_end(ap); - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - plugin_exit(p, 1); } void plugin_log(struct plugin *p, enum log_level l, const char *fmt, ...) diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 6342460cdc5e..d0e9336f5f16 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -17,6 +17,7 @@ #include #include #include +#include struct json_out; struct plugin; @@ -226,6 +227,9 @@ struct command_result *command_param_failed(void); /* Call this on fatal error. */ void NORETURN plugin_err(struct plugin *p, const char *fmt, ...); +/* Call this on fatal error. */ +void NORETURN plugin_errv(struct plugin *p, const char *fmt, va_list ap); + /* Normal exit (makes sure to flush output!). */ void NORETURN plugin_exit(struct plugin *p, int exitcode); From b7d85f1d0b6412b8894b3c365c9b0ffa8b375ca0 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1133/1530] bkpr: wire up our chain fee accting to chain event reception When we get a chain event, check to see if this updates any onchain fee records that we have. --- plugins/bkpr/bookkeeper.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index c51831c93d89..91e9ceb88223 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -184,10 +184,18 @@ static const char *parse_and_log_chain_move(struct command *cmd, maybe_update_account(db, acct, e, tags); /* Can we calculate any onchain fees now? */ + err = maybe_update_onchain_fees(cmd, db, + e->spending_txid ? + e->spending_txid : + &e->outpoint.txid); - /* FIXME: maybe mark channel as 'onchain_resolved' */ db_commit_transaction(db); + if (err) + return err; + + /* FIXME: maybe mark channel as 'onchain_resolved' */ + return NULL; } From 29c6884468face50146b6f9a6c215d52c729f25d Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1134/1530] bkpr: add journal entry for offset account balances; report listbalances When the node starts up, it records missing/updated account balances to the 'channel' events... which is kinda fucked for wallet + external events now that i think about it but these are all treated the same anyway so it's fine. This is the magic piece that lets your bookkeeping data startup ok on an already running/established node. --- plugins/bkpr/bookkeeper.c | 114 ++++++++++++++++++++++++++++++- plugins/bkpr/recorder.c | 97 ++++++++++++++++++++++++++ plugins/bkpr/recorder.h | 13 ++++ plugins/bkpr/test/run-recorder.c | 106 ++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 91e9ceb88223..338abdf8b3d6 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1,7 +1,9 @@ #include "config.h" #include +#include #include #include +#include #include #include #include @@ -26,11 +28,51 @@ static struct command_result *json_list_balances(struct command *cmd, const jsmntok_t *params) { struct json_stream *res; + struct account **accts; + char *err; if (!param(cmd, buf, params, NULL)) return command_param_failed(); res = jsonrpc_stream_success(cmd); + /* List of accts */ + db_begin_transaction(db); + accts = list_accounts(cmd, db); + + json_array_start(res, "accounts"); + for (size_t i = 0; i < tal_count(accts); i++) { + struct acct_balance **balances; + + err = account_get_balance(cmd, db, + accts[i]->name, + &balances); + + if (err) + plugin_err(cmd->plugin, + "Get account balance returned err" + " for account %s: %s", + accts[i]->name, err); + + /* Add it to the result data */ + json_object_start(res, NULL); + + json_add_string(res, "account_id", accts[i]->name); + json_array_start(res, "balances"); + for (size_t j = 0; j < tal_count(balances); j++) { + json_object_start(res, NULL); + json_add_amount_msat_only(res, "balance", + balances[j]->balance); + json_add_string(res, "coin_type", + balances[j]->currency); + json_object_end(res); + } + json_array_end(res); + + json_object_end(res); + } + json_array_end(res); + db_commit_transaction(db); + return command_finished(cmd, res); } @@ -79,8 +121,13 @@ static struct command_result *json_balance_snapshot(struct command *cmd, json_tok_full(buf, params)); snaps = tal_arr(cmd, struct account_snap, accounts_tok->size); + + db_begin_transaction(db); json_for_each_arr(i, acct_tok, accounts_tok) { + struct acct_balance **balances; + struct amount_msat balance; struct account_snap s = snaps[i]; + err = json_scan(cmd, buf, acct_tok, "{account_id:%" ",balance_msat:%" @@ -99,9 +146,72 @@ static struct command_result *json_balance_snapshot(struct command *cmd, plugin_log(cmd->plugin, LOG_DBG, "account %s has balance %s", s.name, type_to_string(tmpctx, struct amount_msat, &s.amt)); - } - // FIXME: check balances are ok! + /* Find the account and verify the balance */ + err = account_get_balance(cmd, db, s.name, + &balances); + + if (err) + plugin_err(cmd->plugin, + "Get account balance returned err" + " for account %s: %s", + s.name, err); + + /* FIXME: multiple currency balances */ + balance = AMOUNT_MSAT(0); + for (size_t j = 0; j < tal_count(balances); j++) { + bool ok; + ok = amount_msat_add(&balance, balance, + balances[j]->balance); + assert(ok); + } + + if (!amount_msat_eq(s.amt, balance)) { + struct account *acct; + struct channel_event ev; + + plugin_log(cmd->plugin, LOG_UNUSUAL, + "Snapshot balance does not equal ondisk" + " reported %s, on disk %s (account %s)." + " Logging journal entry.", + type_to_string(tmpctx, struct amount_msat, &s.amt), + type_to_string(tmpctx, struct amount_msat, &balance), + s.name); + + if (!amount_msat_sub(&ev.credit, s.amt, balance)) { + ev.credit = AMOUNT_MSAT(0); + if (!amount_msat_sub(&ev.debit, balance, s.amt)) + plugin_err(cmd->plugin, + "Unable to sub amt"); + } else + ev.debit = AMOUNT_MSAT(0); + + /* Log a channel "journal entry" to get + * the balances inline */ + acct = find_account(cmd, db, s.name); + if (!acct) { + plugin_log(cmd->plugin, LOG_INFORM, + "account %s not found, adding" + " along with new balance", + s.name); + /* FIXME: lookup peer id for channel? */ + acct = new_account(cmd, s.name, NULL); + account_add(db, acct); + } + + /* This is *not* a coin_mvt tag type */ + ev.tag = "journal_entry"; + ev.fees = AMOUNT_MSAT(0); + ev.currency = s.coin_type; + ev.part_id = 0; + memset(&ev.payment_id, 0, sizeof(struct sha256)); + /* Use current time for this */ + ev.timestamp = time_now().ts.tv_sec; + + log_channel_event(db, acct, &ev); + } + } + db_commit_transaction(db); return notification_handled(cmd); } diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index d48e5f2105a8..481ccfe3f3c6 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -187,6 +187,103 @@ static struct chain_event *find_chain_event(const tal_t *ctx, return e; } +char *account_get_balance(const tal_t *ctx, + struct db *db, + const char *acct_name, + struct acct_balance ***balances) +{ + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("SELECT" + " CAST(SUM(ce.credit) AS BIGINT) as credit" + ", CAST(SUM(ce.debit) AS BIGINT) as debit" + ", ce.currency" + " FROM chain_events ce" + " LEFT OUTER JOIN accounts a" + " ON a.id = ce.account_id" + " WHERE a.name = ?" + " GROUP BY ce.currency")); + + db_bind_text(stmt, 0, acct_name); + db_query_prepared(stmt); + *balances = tal_arr(ctx, struct acct_balance *, 0); + + while (db_step(stmt)) { + struct acct_balance *bal; + + bal = tal(*balances, struct acct_balance); + + bal->currency = db_col_strdup(bal, stmt, "ce.currency"); + db_col_amount_msat(stmt, "credit", &bal->credit); + db_col_amount_msat(stmt, "debit", &bal->debit); + tal_arr_expand(balances, bal); + } + tal_free(stmt); + + stmt = db_prepare_v2(db, SQL("SELECT" + " CAST(SUM(ce.credit) AS BIGINT) as credit" + ", CAST(SUM(ce.debit) AS BIGINT) as debit" + ", ce.currency" + " FROM channel_events ce" + " LEFT OUTER JOIN accounts a" + " ON a.id = ce.account_id" + " WHERE a.name = ?" + " GROUP BY ce.currency")); + db_bind_text(stmt, 0, acct_name); + db_query_prepared(stmt); + + while (db_step(stmt)) { + struct amount_msat amt; + struct acct_balance *bal = NULL; + char *currency; + + currency = db_col_strdup(ctx, stmt, "ce.currency"); + + /* Find the currency entry from above */ + for (size_t i = 0; i < tal_count(*balances); i++) { + if (streq((*balances)[i]->currency, currency)) { + bal = (*balances)[i]; + break; + } + } + + if (!bal) { + bal = tal(*balances, struct acct_balance); + bal->credit = AMOUNT_MSAT(0); + bal->debit = AMOUNT_MSAT(0); + bal->currency = tal_steal(bal, currency); + tal_arr_expand(balances, bal); + } + + db_col_amount_msat(stmt, "credit", &amt); + if (!amount_msat_add(&bal->credit, bal->credit, amt)) { + tal_free(stmt); + return "overflow adding channel_event credits"; + } + + db_col_amount_msat(stmt, "debit", &amt); + if (!amount_msat_add(&bal->debit, bal->debit, amt)) { + tal_free(stmt); + return "overflow adding channel_event debits"; + } + } + tal_free(stmt); + + for (size_t i = 0; i < tal_count(*balances); i++) { + struct acct_balance *bal = (*balances)[i]; + if (!amount_msat_sub(&bal->balance, bal->credit, bal->debit)) + return tal_fmt(ctx, + "%s channel balance is negative? %s - %s", + bal->currency, + type_to_string(ctx, struct amount_msat, + &bal->credit), + type_to_string(ctx, struct amount_msat, + &bal->debit)); + } + + return NULL; +} + struct channel_event **account_get_channel_events(const tal_t *ctx, struct db *db, struct account *acct) diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 888c349d3c2d..449d4775b75c 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -12,6 +12,13 @@ struct db; enum mvt_tag; struct onchain_fee; +struct acct_balance { + char *currency; + struct amount_msat credit; + struct amount_msat debit; + struct amount_msat balance; +}; + /* Get all accounts */ struct account **list_accounts(const tal_t *ctx, struct db *db); @@ -30,6 +37,12 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, struct db *db, struct account *acct); +/* Calculate the balances for an account */ +char *account_get_balance(const tal_t *ctx, + struct db *db, + const char *acct_name, + struct acct_balance ***balances); + /* List all chain fees, for all accounts */ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db); diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 7ab02b79ab2f..01ffa6ba3f79 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -290,6 +290,25 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) return true; } +static struct channel_event *make_channel_event(const tal_t *ctx, + char *tag, + struct amount_msat credit, + struct amount_msat debit, + char payment_char) +{ + struct channel_event *ev = tal(ctx, struct channel_event); + + memset(&ev->payment_id, payment_char, sizeof(struct sha256)); + ev->credit = credit; + ev->debit = debit; + ev->fees = AMOUNT_MSAT(104); + ev->currency = "btc"; + ev->timestamp = 1919191; + ev->part_id = 19; + ev->tag = tag; + return ev; +} + static struct chain_event *make_chain_event(const tal_t *ctx, char *tag, struct amount_msat credit, @@ -827,6 +846,92 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) return true; } +static bool test_account_balances(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id peer_id; + struct account *acct, *acct2; + struct chain_event *ev1; + struct acct_balance **balances; + char *err; + + memset(&peer_id, 3, sizeof(struct node_id)); + + acct = new_account(ctx, tal_fmt(ctx, "example"), &peer_id); + acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + + db_begin_transaction(db); + account_add(db, acct); + account_add(db, acct2); + + /* +1000btc */ + log_chain_event(db, acct, + make_chain_event(ctx, "one", + AMOUNT_MSAT(1000), + AMOUNT_MSAT(0), + 'A', 1, '*')); + + /* -999btc */ + log_chain_event(db, acct, + make_chain_event(ctx, "two", + AMOUNT_MSAT(0), + AMOUNT_MSAT(999), + 'A', 2, '*')); + + /* -440btc */ + log_channel_event(db, acct, + make_channel_event(ctx, "chan", + AMOUNT_MSAT(0), + AMOUNT_MSAT(440), + 'C')); + + /* 500btc */ + log_channel_event(db, acct, + make_channel_event(ctx, "chan", + AMOUNT_MSAT(500), + AMOUNT_MSAT(0), + 'D')); + + /* +5000chf */ + ev1 = make_chain_event(ctx, "two", AMOUNT_MSAT(5000), AMOUNT_MSAT(0), + 'A', 3, '*'); + ev1->currency = "chf"; + log_chain_event(db, acct, ev1); + + /* Add same chain event to a different account, shouldn't show */ + log_chain_event(db, acct2, ev1); + + err = account_get_balance(ctx, db, acct->name, + &balances); + CHECK_MSG(!err, err); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Should have 2 balances */ + CHECK(tal_count(balances) == 2); + CHECK(streq(balances[0]->currency, "btc")); + CHECK(amount_msat_eq(balances[0]->balance, AMOUNT_MSAT(500 - 440 + 1))); + CHECK(streq(balances[1]->currency, "chf")); + CHECK(amount_msat_eq(balances[1]->balance, AMOUNT_MSAT(5000))); + + /* Should error if account balance is negative */ + db_begin_transaction(db); + /* -5001chf */ + ev1 = make_chain_event(ctx, "two", + AMOUNT_MSAT(0), AMOUNT_MSAT(5001), + 'A', 4, '*'); + ev1->currency = "chf"; + log_chain_event(db, acct, ev1); + + err = account_get_balance(ctx, db, acct->name, + &balances); + CHECK_MSG(err != NULL, "Expected err message"); + CHECK(streq(err, "chf channel balance is negative? 5000msat - 5001msat")); + db_commit_transaction(db); + + return true; +} + static bool test_account_crud(const tal_t *ctx, struct plugin *p) { struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); @@ -940,6 +1045,7 @@ int main(int argc, char *argv[]) ok &= test_account_crud(tmpctx, plugin); ok &= test_channel_event_crud(tmpctx, plugin); ok &= test_chain_event_crud(tmpctx, plugin); + ok &= test_account_balances(tmpctx, plugin); ok &= test_onchain_fee_chan_close(tmpctx, plugin); ok &= test_onchain_fee_chan_open(tmpctx, plugin); ok &= test_onchain_fee_wallet_spend(tmpctx, plugin); From 899d54edd0c77fc539578b7dbfe7d403d58a7680 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1135/1530] bkpr: have onchain_fee records be write-only, don't update in place One really rough thing about how we did onchain fees is that the records update every time a new event comes in. The better way to do this is to create new entries for every adjustment, so that reconciliation between printouts isn't a misery. We add a timestamp and `update_count` to these records, so you can roughly order them now (and have a good idea of the last time an event that updated an onchain_fee occurred). --- plugins/bkpr/db.c | 7 +- plugins/bkpr/onchain_fee.h | 11 ++- plugins/bkpr/recorder.c | 114 ++++++++++++++++++++----------- plugins/bkpr/test/run-recorder.c | 104 ++++++++++++++++++---------- 4 files changed, 155 insertions(+), 81 deletions(-) diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 9d89c17b97e1..6064d3229adb 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -84,9 +84,12 @@ static struct migration db_migrations[] = { {SQL("CREATE TABLE onchain_fees (" "account_id BIGINT REFERENCES accounts(id)" ", txid BLOB" - ", amount BIGINT" + ", credit BIGINT" + ", debit BIGINT" ", currency TEXT" - ", PRIMARY KEY (account_id, txid)" + ", timestamp BIGINT" + ", update_count INT" + ", PRIMARY KEY (account_id, txid, update_count)" ");"), NULL}, }; diff --git a/plugins/bkpr/onchain_fee.h b/plugins/bkpr/onchain_fee.h index 3b2085abb226..0d31e4d595f0 100644 --- a/plugins/bkpr/onchain_fee.h +++ b/plugins/bkpr/onchain_fee.h @@ -15,11 +15,18 @@ struct onchain_fee { /* Transaction that we're recording fees for */ struct bitcoin_txid txid; - /* Total amount of onchain fees we paid for this txid */ - struct amount_msat amount; + /* Incremental change in onchain fees */ + struct amount_msat credit; + struct amount_msat debit; /* What token are fees? */ char *currency; + + /* Timestamp of the event that created this fee update */ + u64 timestamp; + + /* Count of records we've recorded for this tx */ + u32 update_count; }; #endif /* LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H */ diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 481ccfe3f3c6..51c7332ef387 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -325,8 +325,11 @@ static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, of->acct_db_id = db_col_u64(stmt, "account_id"); db_col_txid(stmt, "txid", &of->txid); - db_col_amount_msat(stmt, "amount", &of->amount); + db_col_amount_msat(stmt, "credit", &of->credit); + db_col_amount_msat(stmt, "debit", &of->debit); of->currency = db_col_strdup(of, stmt, "currency"); + of->timestamp = db_col_u64(stmt, "timestamp"); + of->update_count = db_col_int(stmt, "update_count"); return of; } @@ -339,10 +342,15 @@ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) stmt = db_prepare_v2(db, SQL("SELECT" " account_id" ", txid" - ", amount" + ", credit" + ", debit" ", currency" + ", timestamp" + ", update_count" " FROM onchain_fees" - " ORDER BY account_id")); + " ORDER BY account_id" + ", txid" + ", update_count")); db_query_prepared(stmt); results = tal_arr(ctx, struct onchain_fee *, 0); @@ -434,8 +442,11 @@ struct onchain_fee **account_onchain_fees(const tal_t *ctx, stmt = db_prepare_v2(db, SQL("SELECT" " account_id" ", txid" - ", amount" + ", credit" + ", debit" ", currency" + ", timestamp" + ", update_count" " FROM onchain_fees" " WHERE account_id = ?;")); @@ -678,58 +689,81 @@ static u64 find_acct_id(struct db *db, const char *name) return acct_id; } -static void update_or_insert_chain_fees(struct db *db, - u64 acct_id, - struct bitcoin_txid *txid, - struct amount_msat *amount, - const char *currency) +static void insert_chain_fees_diff(struct db *db, + u64 acct_id, + struct bitcoin_txid *txid, + struct amount_msat amount, + const char *currency, + u64 timestamp) { struct db_stmt *stmt; + u32 update_count; + struct amount_msat current_amt, credit, debit; /* First, look to see if there's an already existing * record to update */ stmt = db_prepare_v2(db, SQL("SELECT" - " 1" + " update_count" + ", credit" + ", debit" " FROM onchain_fees" " WHERE txid = ?" - " AND account_id = ?")); + " AND account_id = ?" + " ORDER BY update_count")); db_bind_txid(stmt, 0, txid); db_bind_u64(stmt, 1, acct_id); db_query_prepared(stmt); /* If there's no current record, add it */ - if (!db_step(stmt)) { - tal_free(stmt); + current_amt = AMOUNT_MSAT(0); + update_count = 0; + while (db_step(stmt)) { + update_count = db_col_int(stmt, "update_count"); + db_col_amount_msat(stmt, "credit", &credit); + db_col_amount_msat(stmt, "debit", &debit); - stmt = db_prepare_v2(db, SQL("INSERT INTO onchain_fees" - " (" - " account_id" - ", txid" - ", amount" - ", currency" - ") VALUES" - " (?, ?, ?, ?);")); - - db_bind_u64(stmt, 0, acct_id); - db_bind_txid(stmt, 1, txid); - db_bind_amount_msat(stmt, 2, amount); - db_bind_text(stmt, 3, currency); - db_exec_prepared_v2(take(stmt)); - return; - } + /* These should apply perfectly, as we sorted them by + * insert order */ + if (!amount_msat_add(¤t_amt, current_amt, credit)) + db_fatal("Overflow when adding onchain fees"); - /* Otherwise, we update the existing record */ - db_col_ignore(stmt, "1"); + if (!amount_msat_sub(¤t_amt, current_amt, debit)) + db_fatal("Underflow when subtracting onchain fees"); + + } tal_free(stmt); - stmt = db_prepare_v2(db, SQL("UPDATE onchain_fees SET" - " amount = ?" - " WHERE txid = ?" - " AND account_id = ?")); - db_bind_amount_msat(stmt, 0, amount); + /* If they're already equal, no need to update */ + if (amount_msat_eq(current_amt, amount)) + return; + + if (!amount_msat_sub(&credit, amount, current_amt)) { + credit = AMOUNT_MSAT(0); + if (!amount_msat_sub(&debit, current_amt, amount)) + db_fatal("shouldn't happen, unable to subtract"); + } else + debit = AMOUNT_MSAT(0); + + stmt = db_prepare_v2(db, SQL("INSERT INTO onchain_fees" + " (" + " account_id" + ", txid" + ", credit" + ", debit" + ", currency" + ", timestamp" + ", update_count" + ") VALUES" + " (?, ?, ?, ?, ?, ?, ?);")); + + db_bind_u64(stmt, 0, acct_id); db_bind_txid(stmt, 1, txid); - db_bind_u64(stmt, 2, acct_id); + db_bind_amount_msat(stmt, 2, &credit); + db_bind_amount_msat(stmt, 3, &debit); + db_bind_text(stmt, 4, currency); + db_bind_u64(stmt, 5, timestamp); + db_bind_int(stmt, 6, ++update_count); db_exec_prepared_v2(take(stmt)); } @@ -872,9 +906,9 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, fees = fee_part_msat; /* FIXME: fee_currency property of acct? */ - update_or_insert_chain_fees(db, last_id, - txid, &fees, - events[i]->currency); + insert_chain_fees_diff(db, last_id, txid, fees, + events[i]->currency, + events[i]->timestamp); } diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 01ffa6ba3f79..f7ed501baf9c 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -454,7 +454,9 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) CHECK(tal_count(ofs) == 1); CHECK(ofs[0]->acct_db_id == acct->db_id); - CHECK(amount_msat_eq(ofs[0]->amount, AMOUNT_MSAT(800))); + CHECK(amount_msat_eq(ofs[0]->credit, AMOUNT_MSAT(800))); + CHECK(amount_msat_zero(ofs[0]->debit)); + CHECK(ofs[0]->update_count == 1); log_chain_event(db, acct, make_chain_event(ctx, "htlc_tx", @@ -491,7 +493,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - /* Expect: 2 onchain fee records, all for chan-1 */ + /* Expect: 3 onchain fee records, all for chan-1 */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); ofs1 = account_onchain_fees(ctx, db, acct); @@ -499,7 +501,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); CHECK(tal_count(ofs) == tal_count(ofs1)); - CHECK(tal_count(ofs) == 2); + CHECK(tal_count(ofs) == 3); /* txid 3333 */ db_begin_transaction(db); @@ -527,11 +529,11 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); CHECK(tal_count(ofs) == tal_count(ofs1)); - CHECK(tal_count(ofs) == 3); + CHECK(tal_count(ofs) == 4); /* Expect: fees as follows * - * chan-1, 1111, 200 + * chan-1, 1111, 800,-600 * chan-1, 3333, 100 * chan-1, 2222, 50 */ @@ -541,17 +543,31 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) memset(&txid, '1', sizeof(struct bitcoin_txid)); if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { - CHECK(200 == ofs[i]->amount.millisatoshis); /* Raw: test eq */ + CHECK(ofs[i]->update_count == 1 + || ofs[i]->update_count == 2); + + if (ofs[i]->update_count == 1) { + CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(800))); + CHECK(amount_msat_zero(ofs[i]->debit)); + + } else { + CHECK(amount_msat_eq(ofs[i]->debit, AMOUNT_MSAT(600))); + CHECK(amount_msat_zero(ofs[i]->credit)); + } continue; } memset(&txid, '2', sizeof(struct bitcoin_txid)); if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { - CHECK(50 == ofs[i]->amount.millisatoshis); /* Raw: test eq */ + CHECK(ofs[i]->update_count == 1); + CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(50))); + CHECK(amount_msat_zero(ofs[i]->debit)); continue; } memset(&txid, '3', sizeof(struct bitcoin_txid)); if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { - CHECK(100 == ofs[i]->amount.millisatoshis); /* Raw: test eq */ + CHECK(ofs[i]->update_count == 1); + CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(100))); + CHECK(amount_msat_zero(ofs[i]->debit)); continue; } @@ -591,24 +607,37 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) /* Open two channels from wallet */ /* tag utxo_id vout txid debits credits acct_id * withd XXXX 0 AAAA 1000 wallet - * withd YYYY 0 AAAA 3000 wallet + * withd YYYY 0 AAAA 3001 wallet * open AAAA 0 500 chan-1 * open AAAA 1 1000 chan-2 * depo AAAA 2 2200 wallet */ memset(&txid, 'A', sizeof(struct bitcoin_txid)); db_begin_transaction(db); + log_chain_event(db, wal_acct, + make_chain_event(ctx, "withdrawal", + AMOUNT_MSAT(0), + AMOUNT_MSAT(1000), + 'X', 0, 'A')); + log_chain_event(db, wal_acct, + make_chain_event(ctx, "withdrawal", + AMOUNT_MSAT(0), + AMOUNT_MSAT(3001), + 'Y', 0, 'A')); + log_chain_event(db, acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(500), AMOUNT_MSAT(0), 'A', 0, '*')); + maybe_update_onchain_fees(ctx, db, &txid); log_chain_event(db, acct2, make_chain_event(ctx, "deposit", AMOUNT_MSAT(1000), AMOUNT_MSAT(0), 'A', 1, '*')); + maybe_update_onchain_fees(ctx, db, &txid); log_chain_event(db, wal_acct, make_chain_event(ctx, "deposit", @@ -617,45 +646,46 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) 'A', 2, '*')); maybe_update_onchain_fees(ctx, db, &txid); - /* Should be no fee records yet! */ - ofs = list_chain_fees(ctx, db); - CHECK_MSG(tal_count(ofs) == 0, - "no fees counted yet"); - - log_chain_event(db, wal_acct, - make_chain_event(ctx, "withdrawal", - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000), - 'X', 0, 'A')); - log_chain_event(db, wal_acct, - make_chain_event(ctx, "withdrawal", - AMOUNT_MSAT(0), - AMOUNT_MSAT(3001), - 'Y', 0, 'A')); - maybe_update_onchain_fees(ctx, db, &txid); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - /* Expect: 2 onchain fee records of 151/150msat ea, + /* Expect: 5 onchain fee records, totaling to 151/150msat ea, * none for wallet */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - CHECK(tal_count(ofs) == 2); - /* Since these are sorted by acct_id on fetch, + CHECK(tal_count(ofs) == 5); + + struct exp_result { + u32 credit; + u32 debit; + u32 update_count; + }; + + struct exp_result exp_results[] = { + { .credit = 3501, .debit = 0, .update_count = 1 }, + { .credit = 0, .debit = 2250, .update_count = 2 }, + { .credit = 0, .debit = 1100, .update_count = 3 }, + { .credit = 1250, .debit = 0, .update_count = 1 }, + { .credit = 0, .debit = 1100, .update_count = 2 }, + }; + + /* Since these are sorted on fetch, * this *should* be stable */ - CHECK(ofs[0]->acct_db_id == acct->db_id); - CHECK(amount_msat_eq(ofs[0]->amount, AMOUNT_MSAT(151))); - CHECK(streq(ofs[0]->currency, "btc")); - CHECK(bitcoin_txid_eq(&ofs[0]->txid, &txid)); - - CHECK(ofs[1]->acct_db_id == acct2->db_id); - CHECK(amount_msat_eq(ofs[1]->amount, AMOUNT_MSAT(150))); - CHECK(streq(ofs[1]->currency, "btc")); - CHECK(bitcoin_txid_eq(&ofs[1]->txid, &txid)); + for (size_t i = 0; i < tal_count(ofs); i++) { + CHECK(i < ARRAY_SIZE(exp_results)); + CHECK(amount_msat_eq(ofs[i]->credit, + amount_msat(exp_results[i].credit))); + CHECK(amount_msat_eq(ofs[i]->debit, + amount_msat(exp_results[i].debit))); + CHECK(ofs[i]->update_count == exp_results[i].update_count); + + CHECK(streq(ofs[i]->currency, "btc")); + CHECK(bitcoin_txid_eq(&ofs[i]->txid, &txid)); + } return true; } From 8ec35b7eb1d6023442da506ec369d8c9e00c8b21 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1136/1530] bkpr: turns out these fields are optional pushes and channel leases don't provide fees or payment_id info --- plugins/bkpr/bookkeeper.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 338abdf8b3d6..dccc75d43c06 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -324,23 +324,23 @@ static const char *parse_and_log_channel_move(struct command *cmd, const char *err; err = json_scan(tmpctx, buf, params, - "{coin_movement:" - "{payment_hash:%" - ",fees:%" - "}}", - JSON_SCAN(json_to_sha256, &e->payment_id), - JSON_SCAN(json_to_msat, &e->fees)); - + "{coin_movement:{payment_hash:%}}", + JSON_SCAN(json_to_sha256, &e->payment_id)); if (err) - return err; + memset(&e->payment_id, 0, sizeof(struct sha256)); err = json_scan(tmpctx, buf, params, - "{coin_movement:" - "{part_id:%}}", + "{coin_movement:{part_id:%}}", JSON_SCAN(json_to_number, &e->part_id)); if (err) e->part_id = 0; + err = json_scan(tmpctx, buf, params, + "{coin_movement:{fees_msat:%}}", + JSON_SCAN(json_to_msat, &e->fees)); + if (err) + e->fees = AMOUNT_MSAT(0); + e->credit = credit; e->debit = debit; e->currency = tal_steal(e, coin_type); From d943e5e85cabae7f2611bd754f91fb0855cf78c3 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1137/1530] bkpr: use pointer for payment_id for channel events sometimes these are null! --- plugins/bkpr/bookkeeper.c | 7 ++++--- plugins/bkpr/channel_event.h | 2 +- plugins/bkpr/recorder.c | 17 ++++++++++++++--- plugins/bkpr/test/run-recorder.c | 27 +++++++++++++++++++-------- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index dccc75d43c06..160b06c306b9 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -204,7 +204,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, ev.fees = AMOUNT_MSAT(0); ev.currency = s.coin_type; ev.part_id = 0; - memset(&ev.payment_id, 0, sizeof(struct sha256)); + ev.payment_id = NULL; /* Use current time for this */ ev.timestamp = time_now().ts.tv_sec; @@ -323,11 +323,12 @@ static const char *parse_and_log_channel_move(struct command *cmd, struct account *acct; const char *err; + e->payment_id = tal(e, struct sha256); err = json_scan(tmpctx, buf, params, "{coin_movement:{payment_hash:%}}", - JSON_SCAN(json_to_sha256, &e->payment_id)); + JSON_SCAN(json_to_sha256, e->payment_id)); if (err) - memset(&e->payment_id, 0, sizeof(struct sha256)); + e->payment_id = tal_free(e->payment_id); err = json_scan(tmpctx, buf, params, "{coin_movement:{part_id:%}}", diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h index d6a9533fc59c..c64c21d4b300 100644 --- a/plugins/bkpr/channel_event.h +++ b/plugins/bkpr/channel_event.h @@ -31,7 +31,7 @@ struct channel_event { const char *currency; /* Payment identifier (typically the preimage hash) */ - struct sha256 payment_id; + struct sha256 *payment_id; /* Some payments share a payment_id, and are differentiable via id */ u32 part_id; diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 51c7332ef387..57e38e388d6f 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -84,7 +84,11 @@ static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt db_col_amount_msat(stmt, "fees", &e->fees); e->currency = db_col_strdup(e, stmt, "currency"); - db_col_sha256(stmt, "payment_id", &e->payment_id); + if (!db_col_is_null(stmt, "payment_id")) { + e->payment_id = tal(e, struct sha256); + db_col_sha256(stmt, "payment_id", e->payment_id); + } else + e->payment_id = NULL; e->part_id = db_col_int(stmt, "part_id"); e->timestamp = db_col_u64(stmt, "timestamp"); @@ -629,7 +633,10 @@ void log_channel_event(struct db *db, db_bind_amount_msat(stmt, 3, &e->debit); db_bind_amount_msat(stmt, 4, &e->fees); db_bind_text(stmt, 5, e->currency); - db_bind_sha256(stmt, 6, &e->payment_id); + if (e->payment_id) + db_bind_sha256(stmt, 6, e->payment_id); + else + db_bind_null(stmt, 6); db_bind_int(stmt, 7, e->part_id); db_bind_u64(stmt, 8, e->timestamp); @@ -956,7 +963,11 @@ void log_chain_event(struct db *db, db_bind_int(stmt, 7, e->blockheight); db_bind_txid(stmt, 8, &e->outpoint.txid); db_bind_int(stmt, 9, e->outpoint.n); - db_bind_sha256(stmt, 10, e->payment_id); + + if (e->payment_id) + db_bind_sha256(stmt, 10, e->payment_id); + else + db_bind_null(stmt, 10); if (e->spending_txid) db_bind_txid(stmt, 11, e->spending_txid); diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index f7ed501baf9c..06140746db09 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -259,7 +259,9 @@ static bool channel_events_eq(struct channel_event *e1, struct channel_event *e2 CHECK(amount_msat_eq(e1->debit, e2->debit)); CHECK(amount_msat_eq(e1->fees, e2->fees)); CHECK(streq(e1->currency, e2->currency)); - CHECK(sha256_eq(&e1->payment_id, &e2->payment_id)); + CHECK((e1->payment_id != NULL) == (e2->payment_id != NULL)); + if (e1->payment_id) + CHECK(sha256_eq(e1->payment_id, e2->payment_id)); CHECK(e1->part_id == e2->part_id); CHECK(e1->timestamp == e2->timestamp); @@ -298,7 +300,8 @@ static struct channel_event *make_channel_event(const tal_t *ctx, { struct channel_event *ev = tal(ctx, struct channel_event); - memset(&ev->payment_id, payment_char, sizeof(struct sha256)); + ev->payment_id = tal(ev, struct sha256); + memset(ev->payment_id, payment_char, sizeof(struct sha256)); ev->credit = credit; ev->debit = debit; ev->fees = AMOUNT_MSAT(104); @@ -707,32 +710,35 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - memset(&ev1.payment_id, 'B', sizeof(struct sha256)); + ev1.payment_id = tal(ctx, struct sha256); + memset(ev1.payment_id, 'B', sizeof(struct sha256)); ev1.credit = AMOUNT_MSAT(100); ev1.debit = AMOUNT_MSAT(102); ev1.fees = AMOUNT_MSAT(104); ev1.currency = "btc"; - ev1.timestamp = 1919191; + ev1.timestamp = 11111; ev1.part_id = 19; /* Passing unknown tags in should be ok */ ev1.tag = "hello"; - memset(&ev2.payment_id, 'C', sizeof(struct sha256)); + ev2.payment_id = tal(ctx, struct sha256); + memset(ev2.payment_id, 'C', sizeof(struct sha256)); ev2.credit = AMOUNT_MSAT(200); ev2.debit = AMOUNT_MSAT(202); ev2.fees = AMOUNT_MSAT(204); ev2.currency = "brct"; - ev2.timestamp = 1818181; + ev2.timestamp = 22222; ev2.part_id = 0; ev2.tag = tal_fmt(ctx, "deposit"); - memset(&ev3.payment_id, 'D', sizeof(struct sha256)); + ev3.payment_id = tal(ctx, struct sha256); + memset(ev3.payment_id, 'D', sizeof(struct sha256)); ev3.credit = AMOUNT_MSAT(300); ev3.debit = AMOUNT_MSAT(302); ev3.fees = AMOUNT_MSAT(304); ev3.currency = "brct"; - ev3.timestamp = 1717171; + ev3.timestamp = 33333; ev3.part_id = 5; ev3.tag = tal_fmt(ctx, "routed"); @@ -742,6 +748,11 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) /* log a channel event to a different acct */ log_channel_event(db, acct2, &ev3); + + /* log a channel event without a payment id */ + ev3.payment_id = NULL; + log_channel_event(db, acct2, &ev3); + db_commit_transaction(db); CHECK_MSG(!db_err, db_err); From ccffac8208fb3f83e241981ec0cb1672aa4d94b9 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1138/1530] bkpr: put the account name on the event When we print events out, we need to know the account name. This makes our lookup a lot easier, since we just pull it out from the database every time we query for these. --- plugins/bkpr/chain_event.h | 3 + plugins/bkpr/channel_event.h | 3 + plugins/bkpr/onchain_fee.h | 3 + plugins/bkpr/recorder.c | 296 +++++++++++++++++-------------- plugins/bkpr/test/run-recorder.c | 188 ++++++++++---------- 5 files changed, 271 insertions(+), 222 deletions(-) diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index 764c85410c7c..9359de8f8900 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -16,6 +16,9 @@ struct chain_event { /* db_id of account this event belongs to */ u64 acct_db_id; + /* Name of the account this belongs to */ + char *acct_name; + /* Tag describing the event */ const char *tag; diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h index c64c21d4b300..7e231dce22a9 100644 --- a/plugins/bkpr/channel_event.h +++ b/plugins/bkpr/channel_event.h @@ -15,6 +15,9 @@ struct channel_event { /* db_id of account this event belongs to */ u64 acct_db_id; + /* Name of the account this belongs to */ + char *acct_name; + /* Tag describing the event */ const char *tag; diff --git a/plugins/bkpr/onchain_fee.h b/plugins/bkpr/onchain_fee.h index 0d31e4d595f0..b57c9f18a6f9 100644 --- a/plugins/bkpr/onchain_fee.h +++ b/plugins/bkpr/onchain_fee.h @@ -12,6 +12,9 @@ struct onchain_fee { /* db_id of account this event belongs to */ u64 acct_db_id; + /* Name of the account this belongs to */ + char *acct_name; + /* Transaction that we're recording fees for */ struct bitcoin_txid txid; diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 57e38e388d6f..f7cc03093f71 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -21,31 +21,32 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt) { struct chain_event *e = tal(ctx, struct chain_event); - e->db_id = db_col_u64(stmt, "id"); - e->acct_db_id = db_col_u64(stmt, "account_id"); + e->db_id = db_col_u64(stmt, "e.id"); + e->acct_db_id = db_col_u64(stmt, "e.account_id"); + e->acct_name = db_col_strdup(e, stmt, "a.name"); - e->tag = db_col_strdup(e, stmt, "tag"); + e->tag = db_col_strdup(e, stmt, "e.tag"); - db_col_amount_msat(stmt, "credit", &e->credit); - db_col_amount_msat(stmt, "debit", &e->debit); - db_col_amount_msat(stmt, "output_value", &e->output_value); + db_col_amount_msat(stmt, "e.credit", &e->credit); + db_col_amount_msat(stmt, "e.debit", &e->debit); + db_col_amount_msat(stmt, "e.output_value", &e->output_value); - e->currency = db_col_strdup(e, stmt, "currency"); - e->timestamp = db_col_u64(stmt, "timestamp"); - e->blockheight = db_col_int(stmt, "blockheight"); + e->currency = db_col_strdup(e, stmt, "e.currency"); + e->timestamp = db_col_u64(stmt, "e.timestamp"); + e->blockheight = db_col_int(stmt, "e.blockheight"); - db_col_txid(stmt, "utxo_txid", &e->outpoint.txid); - e->outpoint.n = db_col_int(stmt, "outnum"); + db_col_txid(stmt, "e.utxo_txid", &e->outpoint.txid); + e->outpoint.n = db_col_int(stmt, "e.outnum"); - if (!db_col_is_null(stmt, "payment_id")) { + if (!db_col_is_null(stmt, "e.payment_id")) { e->payment_id = tal(e, struct sha256); - db_col_sha256(stmt, "payment_id", e->payment_id); + db_col_sha256(stmt, "e.payment_id", e->payment_id); } else e->payment_id = NULL; - if (!db_col_is_null(stmt, "spending_txid")) { + if (!db_col_is_null(stmt, "e.spending_txid")) { e->spending_txid = tal(e, struct bitcoin_txid); - db_col_txid(stmt, "spending_txid", e->spending_txid); + db_col_txid(stmt, "e.spending_txid", e->spending_txid); } else e->spending_txid = NULL; @@ -74,23 +75,24 @@ static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt { struct channel_event *e = tal(ctx, struct channel_event); - e->db_id = db_col_u64(stmt, "id"); - e->acct_db_id = db_col_u64(stmt, "account_id"); + e->db_id = db_col_u64(stmt, "e.id"); + e->acct_db_id = db_col_u64(stmt, "e.account_id"); + e->acct_name = db_col_strdup(e, stmt, "a.name"); - e->tag = db_col_strdup(e, stmt, "tag"); + e->tag = db_col_strdup(e, stmt, "e.tag"); - db_col_amount_msat(stmt, "credit", &e->credit); - db_col_amount_msat(stmt, "debit", &e->debit); - db_col_amount_msat(stmt, "fees", &e->fees); + db_col_amount_msat(stmt, "e.credit", &e->credit); + db_col_amount_msat(stmt, "e.debit", &e->debit); + db_col_amount_msat(stmt, "e.fees", &e->fees); - e->currency = db_col_strdup(e, stmt, "currency"); - if (!db_col_is_null(stmt, "payment_id")) { + e->currency = db_col_strdup(e, stmt, "e.currency"); + if (!db_col_is_null(stmt, "e.payment_id")) { e->payment_id = tal(e, struct sha256); - db_col_sha256(stmt, "payment_id", e->payment_id); + db_col_sha256(stmt, "e.payment_id", e->payment_id); } else e->payment_id = NULL; - e->part_id = db_col_int(stmt, "part_id"); - e->timestamp = db_col_u64(stmt, "timestamp"); + e->part_id = db_col_int(stmt, "e.part_id"); + e->timestamp = db_col_u64(stmt, "e.timestamp"); return e; } @@ -102,21 +104,25 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, struct db_stmt *stmt; stmt = db_prepare_v2(db, SQL("SELECT" - " id" - ", account_id" - ", tag" - ", credit" - ", debit" - ", output_value" - ", currency" - ", timestamp" - ", blockheight" - ", utxo_txid" - ", outnum" - ", spending_txid" - ", payment_id" - " FROM chain_events" - " WHERE account_id = ?;")); + " e.id" + ", e.account_id" + ", a.name" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE e.account_id = ?" + " ORDER BY e.timestamp, e.id")); db_bind_int(stmt, 0, acct->db_id); return find_chain_events(ctx, take(stmt)); @@ -134,47 +140,53 @@ static struct chain_event *find_chain_event(const tal_t *ctx, if (spending_txid) { stmt = db_prepare_v2(db, SQL("SELECT" - " id" - ", account_id" - ", tag" - ", credit" - ", debit" - ", output_value" - ", currency" - ", timestamp" - ", blockheight" - ", utxo_txid" - ", outnum" - ", spending_txid" - ", payment_id" - " FROM chain_events" + " e.id" + ", e.account_id" + ", a.name" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" " WHERE " - " account_id = ?" - " AND utxo_txid = ?" - " AND outnum = ?" - " AND spending_txid = ?")); + " e.account_id = ?" + " AND e.utxo_txid = ?" + " AND e.outnum = ?" + " AND e.spending_txid = ?")); db_bind_txid(stmt, 3, spending_txid); } else { stmt = db_prepare_v2(db, SQL("SELECT" - " id" - ", account_id" - ", tag" - ", credit" - ", debit" - ", output_value" - ", currency" - ", timestamp" - ", blockheight" - ", utxo_txid" - ", outnum" - ", spending_txid" - ", payment_id" - " FROM chain_events" + " e.id" + ", e.account_id" + ", a.name" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" " WHERE " - " account_id = ?" - " AND utxo_txid = ?" - " AND outnum = ?" - " AND spending_txid IS NULL")); + " e.account_id = ?" + " AND e.utxo_txid = ?" + " AND e.outnum = ?" + " AND e.spending_txid IS NULL")); } db_bind_u64(stmt, 0, acct->db_id); @@ -296,18 +308,22 @@ struct channel_event **account_get_channel_events(const tal_t *ctx, struct channel_event **results; stmt = db_prepare_v2(db, SQL("SELECT" - " id" - ", account_id" - ", tag" - ", credit" - ", debit" - ", fees" - ", currency" - ", payment_id" - ", part_id" - ", timestamp" - " FROM channel_events" - " WHERE account_id = ?;")); + " e.id" + ", a.name" + ", e.account_id" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.fees" + ", e.currency" + ", e.payment_id" + ", e.part_id" + ", e.timestamp" + " FROM channel_events e" + " LEFT OUTER JOIN accounts a" + " ON a.id = e.account_id" + " WHERE e.account_id = ?" + " ORDER BY e.timestamp, e.id")); db_bind_u64(stmt, 0, acct->db_id); db_query_prepared(stmt); @@ -327,13 +343,14 @@ static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, { struct onchain_fee *of = tal(ctx, struct onchain_fee); - of->acct_db_id = db_col_u64(stmt, "account_id"); - db_col_txid(stmt, "txid", &of->txid); - db_col_amount_msat(stmt, "credit", &of->credit); - db_col_amount_msat(stmt, "debit", &of->debit); - of->currency = db_col_strdup(of, stmt, "currency"); - of->timestamp = db_col_u64(stmt, "timestamp"); - of->update_count = db_col_int(stmt, "update_count"); + of->acct_db_id = db_col_u64(stmt, "of.account_id"); + of->acct_name = db_col_strdup(of, stmt, "a.name"); + db_col_txid(stmt, "of.txid", &of->txid); + db_col_amount_msat(stmt, "of.credit", &of->credit); + db_col_amount_msat(stmt, "of.debit", &of->debit); + of->currency = db_col_strdup(of, stmt, "of.currency"); + of->timestamp = db_col_u64(stmt, "of.timestamp"); + of->update_count = db_col_int(stmt, "of.update_count"); return of; } @@ -344,17 +361,22 @@ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) struct onchain_fee **results; stmt = db_prepare_v2(db, SQL("SELECT" - " account_id" - ", txid" - ", credit" - ", debit" - ", currency" - ", timestamp" - ", update_count" - " FROM onchain_fees" - " ORDER BY account_id" - ", txid" - ", update_count")); + " of.account_id" + ", a.name" + ", of.txid" + ", of.credit" + ", of.debit" + ", of.currency" + ", of.timestamp" + ", of.update_count" + " FROM onchain_fees of" + " LEFT OUTER JOIN accounts a" + " ON a.id = of.account_id" + " ORDER BY " + " of.timestamp" + ", of.account_id" + ", of.txid" + ", of.update_count")); db_query_prepared(stmt); results = tal_arr(ctx, struct onchain_fee *, 0); @@ -444,15 +466,18 @@ struct onchain_fee **account_onchain_fees(const tal_t *ctx, struct onchain_fee **results; stmt = db_prepare_v2(db, SQL("SELECT" - " account_id" - ", txid" - ", credit" - ", debit" - ", currency" - ", timestamp" - ", update_count" - " FROM onchain_fees" - " WHERE account_id = ?;")); + " of.account_id" + ", a.name" + ", of.txid" + ", of.credit" + ", of.debit" + ", of.currency" + ", of.timestamp" + ", of.update_count" + " FROM onchain_fees of" + " LEFT OUTER JOIN accounts a" + " ON a.id = of.account_id" + " WHERE of.account_id = ?;")); db_bind_u64(stmt, 0, acct->db_id); db_query_prepared(stmt); @@ -643,6 +668,7 @@ void log_channel_event(struct db *db, db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); e->acct_db_id = acct->db_id; + e->acct_name = tal_strdup(e, acct->name); tal_free(stmt); } @@ -652,23 +678,26 @@ static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db struct db_stmt *stmt; stmt = db_prepare_v2(db, SQL("SELECT " - " id" - ", account_id" - ", tag" - ", credit" - ", debit" - ", output_value" - ", currency" - ", timestamp" - ", blockheight" - ", utxo_txid" - ", outnum" - ", spending_txid" - ", payment_id" - " FROM chain_events" - " WHERE spending_txid = ?" - " OR (utxo_txid = ? AND spending_txid IS NULL)" - " ORDER BY account_id")); + " e.id" + ", a.name" + ", e.account_id" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON a.id = e.account_id" + " WHERE e.spending_txid = ?" + " OR (e.utxo_txid = ? AND e.spending_txid IS NULL)" + " ORDER BY e.account_id")); db_bind_txid(stmt, 0, txid); db_bind_txid(stmt, 1, txid); @@ -977,5 +1006,6 @@ void log_chain_event(struct db *db, db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); e->acct_db_id = acct->db_id; + e->acct_name = tal_strdup(e, acct->name); tal_free(stmt); } diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 06140746db09..ad55c6eca1ea 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -698,7 +698,7 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); struct node_id peer_id; struct account *acct, *acct2; - struct channel_event ev1, ev2, ev3, **chan_evs; + struct channel_event *ev1, *ev2, *ev3, **chan_evs; memset(&peer_id, 3, sizeof(struct node_id)); @@ -710,48 +710,51 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - ev1.payment_id = tal(ctx, struct sha256); - memset(ev1.payment_id, 'B', sizeof(struct sha256)); - ev1.credit = AMOUNT_MSAT(100); - ev1.debit = AMOUNT_MSAT(102); - ev1.fees = AMOUNT_MSAT(104); - ev1.currency = "btc"; - ev1.timestamp = 11111; - ev1.part_id = 19; + ev1 = tal(ctx, struct channel_event); + ev1->payment_id = tal(ev1, struct sha256); + memset(ev1->payment_id, 'B', sizeof(struct sha256)); + ev1->credit = AMOUNT_MSAT(100); + ev1->debit = AMOUNT_MSAT(102); + ev1->fees = AMOUNT_MSAT(104); + ev1->currency = "btc"; + ev1->timestamp = 11111; + ev1->part_id = 19; /* Passing unknown tags in should be ok */ - ev1.tag = "hello"; - - ev2.payment_id = tal(ctx, struct sha256); - memset(ev2.payment_id, 'C', sizeof(struct sha256)); - ev2.credit = AMOUNT_MSAT(200); - ev2.debit = AMOUNT_MSAT(202); - ev2.fees = AMOUNT_MSAT(204); - ev2.currency = "brct"; - ev2.timestamp = 22222; - ev2.part_id = 0; - ev2.tag = tal_fmt(ctx, "deposit"); - - ev3.payment_id = tal(ctx, struct sha256); - memset(ev3.payment_id, 'D', sizeof(struct sha256)); - ev3.credit = AMOUNT_MSAT(300); - ev3.debit = AMOUNT_MSAT(302); - ev3.fees = AMOUNT_MSAT(304); - ev3.currency = "brct"; - ev3.timestamp = 33333; - ev3.part_id = 5; - ev3.tag = tal_fmt(ctx, "routed"); + ev1->tag = "hello"; + + ev2 = tal(ctx, struct channel_event); + ev2->payment_id = tal(ev2, struct sha256); + memset(ev2->payment_id, 'C', sizeof(struct sha256)); + ev2->credit = AMOUNT_MSAT(200); + ev2->debit = AMOUNT_MSAT(202); + ev2->fees = AMOUNT_MSAT(204); + ev2->currency = "brct"; + ev2->timestamp = 22222; + ev2->part_id = 0; + ev2->tag = tal_fmt(ev2, "deposit"); + + ev3 = tal(ctx, struct channel_event); + ev3->payment_id = tal(ev3, struct sha256); + memset(ev3->payment_id, 'D', sizeof(struct sha256)); + ev3->credit = AMOUNT_MSAT(300); + ev3->debit = AMOUNT_MSAT(302); + ev3->fees = AMOUNT_MSAT(304); + ev3->currency = "brct"; + ev3->timestamp = 33333; + ev3->part_id = 5; + ev3->tag = tal_fmt(ev3, "routed"); db_begin_transaction(db); - log_channel_event(db, acct, &ev1); - log_channel_event(db, acct, &ev2); + log_channel_event(db, acct, ev1); + log_channel_event(db, acct, ev2); /* log a channel event to a different acct */ - log_channel_event(db, acct2, &ev3); + log_channel_event(db, acct2, ev3); /* log a channel event without a payment id */ - ev3.payment_id = NULL; - log_channel_event(db, acct2, &ev3); + ev3->payment_id = NULL; + log_channel_event(db, acct2, ev3); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); @@ -762,9 +765,12 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); CHECK_MSG(!db_err, db_err); + CHECK(streq(acct->name, chan_evs[0]->acct_name)); + CHECK(streq(acct->name, chan_evs[1]->acct_name)); + CHECK(tal_count(chan_evs) == 2); - channel_events_eq(&ev1, chan_evs[0]); - channel_events_eq(&ev2, chan_evs[1]); + channel_events_eq(ev1, chan_evs[0]); + channel_events_eq(ev2, chan_evs[1]); return true; } @@ -774,7 +780,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); struct node_id peer_id; struct account *acct, *acct2; - struct chain_event ev1, *ev2, ev3, **chain_evs; + struct chain_event *ev1, *ev2, *ev3, **chain_evs; char *name = tal_fmt(ctx, "example"); ev2 = tal(ctx, struct chain_event); @@ -789,21 +795,22 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); /* This event spends the second inserted event */ - ev1.tag = tal_fmt(ctx, "withdrawal"); - ev1.credit = AMOUNT_MSAT(100); - ev1.debit = AMOUNT_MSAT(102); - ev1.output_value = AMOUNT_MSAT(104); - ev1.currency = "btc"; - ev1.timestamp = 1919191; - ev1.blockheight = 1919191; - memset(&ev1.outpoint.txid, 'D', sizeof(struct bitcoin_txid)); - ev1.outpoint.n = 1; - ev1.spending_txid = tal(ctx, struct bitcoin_txid); - memset(ev1.spending_txid, 'C', sizeof(struct bitcoin_txid)); - ev1.payment_id = NULL; + ev1 = tal(ctx, struct chain_event); + ev1->tag = tal_fmt(ev1, "withdrawal"); + ev1->credit = AMOUNT_MSAT(100); + ev1->debit = AMOUNT_MSAT(102); + ev1->output_value = AMOUNT_MSAT(104); + ev1->currency = "btc"; + ev1->timestamp = 1919191; + ev1->blockheight = 1919191; + memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); + ev1->outpoint.n = 1; + ev1->spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid)); + ev1->payment_id = NULL; db_begin_transaction(db); - log_chain_event(db, acct, &ev1); + log_chain_event(db, acct, ev1); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); @@ -821,24 +828,25 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) memset(ev2->payment_id, 'B', sizeof(struct sha256)); /* Dummy event, logged to separate account */ - ev3.tag = tal_fmt(ctx, "deposit"); - ev3.credit = AMOUNT_MSAT(300); - ev3.debit = AMOUNT_MSAT(302); - ev3.output_value = AMOUNT_MSAT(304); - ev3.currency = "btc"; - ev3.timestamp = 3939393; - ev3.blockheight = 3939393; - memset(&ev3.outpoint.txid, 'E', sizeof(struct bitcoin_txid)); - ev3.outpoint.n = 1; - ev3.spending_txid = tal(ctx, struct bitcoin_txid); - memset(ev3.spending_txid, 'D', sizeof(struct bitcoin_txid)); - ev3.payment_id = NULL; + ev3 = tal(ctx, struct chain_event); + ev3->tag = tal_fmt(ev3, "deposit"); + ev3->credit = AMOUNT_MSAT(300); + ev3->debit = AMOUNT_MSAT(302); + ev3->output_value = AMOUNT_MSAT(304); + ev3->currency = "btc"; + ev3->timestamp = 3939393; + ev3->blockheight = 3939393; + memset(&ev3->outpoint.txid, 'E', sizeof(struct bitcoin_txid)); + ev3->outpoint.n = 1; + ev3->spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev3->spending_txid, 'D', sizeof(struct bitcoin_txid)); + ev3->payment_id = NULL; db_begin_transaction(db); log_chain_event(db, acct, ev2); /* log new event to a different account.. */ - log_chain_event(db, acct2, &ev3); + log_chain_event(db, acct2, ev3); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); @@ -855,14 +863,16 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); CHECK(tal_count(chain_evs) == 2); - chain_events_eq(&ev1, chain_evs[0]); + CHECK(streq(acct->name, chain_evs[0]->acct_name)); + CHECK(streq(acct->name, chain_evs[1]->acct_name)); + chain_events_eq(ev1, chain_evs[0]); chain_events_eq(ev2, chain_evs[1]); /* Now insert a utxo create and spend, in that order */ - ev1.db_id = 0; - memset(&ev1.outpoint.txid, 'A', sizeof(struct bitcoin_txid)); - ev1.outpoint.n = 10; - ev1.spending_txid = tal_free(ev1.spending_txid); + ev1->db_id = 0; + memset(&ev1->outpoint.txid, 'A', sizeof(struct bitcoin_txid)); + ev1->outpoint.n = 10; + ev1->spending_txid = tal_free(ev1->spending_txid); ev2->db_id = 0; memset(&ev2->outpoint.txid, 'A', sizeof(struct bitcoin_txid)); @@ -872,7 +882,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) db_begin_transaction(db); - log_chain_event(db, acct, &ev1); + log_chain_event(db, acct, ev1); log_chain_event(db, acct, ev2); chain_evs = account_get_chain_events(ctx, db, acct); db_commit_transaction(db); @@ -881,7 +891,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) /* There should be four now */ CHECK(tal_count(chain_evs) == 4); - chain_events_eq(&ev1, chain_evs[2]); + chain_events_eq(ev1, chain_evs[2]); chain_events_eq(ev2, chain_evs[3]); return true; @@ -978,7 +988,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); struct node_id peer_id; struct account *acct, *acct2, **acct_list; - struct chain_event ev1; + struct chain_event *ev1; enum mvt_tag *tags; char *name = tal_fmt(ctx, "example"); @@ -1022,29 +1032,29 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) /* Will we update an account's properties * correctly, given an event and tag list? */ - ev1.tag = tal_fmt(ctx, "withdrawal"); - ev1.credit = AMOUNT_MSAT(100); - ev1.debit = AMOUNT_MSAT(102); - ev1.output_value = AMOUNT_MSAT(104); - ev1.currency = "btc"; - ev1.timestamp = 1919191; - ev1.blockheight = 1919191; - memset(&ev1.outpoint.txid, 'D', sizeof(struct bitcoin_txid)); - ev1.outpoint.n = 1; - ev1.spending_txid = tal(ctx, struct bitcoin_txid); - memset(ev1.spending_txid, 'C', sizeof(struct bitcoin_txid)); - ev1.payment_id = NULL; + ev1 = tal(ctx, struct chain_event); + ev1->tag = tal_fmt(ctx, "withdrawal"); + ev1->credit = AMOUNT_MSAT(100); + ev1->debit = AMOUNT_MSAT(102); + ev1->output_value = AMOUNT_MSAT(104); + ev1->currency = "btc"; + ev1->timestamp = 1919191; + ev1->blockheight = 1919191; + memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); + ev1->outpoint.n = 1; + ev1->spending_txid = tal(ctx, struct bitcoin_txid); + memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid)); + ev1->payment_id = NULL; db_begin_transaction(db); - log_chain_event(db, acct, &ev1); + log_chain_event(db, acct, ev1); tags = tal_arr(ctx, enum mvt_tag, 2); /* should not update the account info */ tags[0] = PUSHED; tags[1] = PENALTY; - maybe_update_account(db, acct, &ev1, tags); - acct2 = find_account(ctx, db, "wallet"); + maybe_update_account(db, acct, ev1, tags); accountseq(acct, acct2); /* channel_open -> open event db updated */ @@ -1052,7 +1062,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) CHECK(acct->open_event_db_id == NULL); tags[0] = CHANNEL_OPEN; tags[1] = LEASED; - maybe_update_account(db, acct, &ev1, tags); + maybe_update_account(db, acct, ev1, tags); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); CHECK(acct->leased); @@ -1062,7 +1072,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) tags[1] = OPENER; CHECK(acct->closed_event_db_id == NULL); CHECK(!acct->we_opened); - maybe_update_account(db, acct, &ev1, tags); + maybe_update_account(db, acct, ev1, tags); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); CHECK(acct->closed_event_db_id != NULL); From 8039fde5ab308d2335f77d949413f9b9968fad26 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1139/1530] bkpr: if we're missing info about an account, add in journal entry There's two situations where we're missing info. One is we get a 'channel_closed' event (but there's no 'channel_open') The other is a balance_snapshot arrives with information about accounts that doesn't match what's already on disk. (For some of these cases, we may be missing 'channel_open' events..) In the easy case (no channel_open missing), we just figure out what the --- plugins/bkpr/Makefile | 1 + plugins/bkpr/account.c | 7 + plugins/bkpr/account.h | 3 + plugins/bkpr/bookkeeper.c | 583 ++++++++++++++++++++++++++----- plugins/bkpr/channel_event.c | 30 ++ plugins/bkpr/channel_event.h | 11 + plugins/bkpr/recorder.c | 9 +- plugins/bkpr/recorder.h | 12 +- plugins/bkpr/test/run-recorder.c | 8 +- 9 files changed, 561 insertions(+), 103 deletions(-) create mode 100644 plugins/bkpr/channel_event.c diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index a205c46fcbdc..3a1f5146236d 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -3,6 +3,7 @@ BOOKKEEPER_PLUGIN_SRC := \ plugins/bkpr/account.c \ plugins/bkpr/bookkeeper.c \ + plugins/bkpr/channel_event.c \ plugins/bkpr/db.c \ plugins/bkpr/recorder.c diff --git a/plugins/bkpr/account.c b/plugins/bkpr/account.c index c971087293b0..b734078ea8f9 100644 --- a/plugins/bkpr/account.c +++ b/plugins/bkpr/account.c @@ -1,5 +1,6 @@ #include "config.h" +#include #include #include #include @@ -21,3 +22,9 @@ struct account *new_account(const tal_t *ctx, return a; } + +bool is_channel_account(const struct account *acct) +{ + return !streq(acct->name, WALLET) + && !streq(acct->name, "external"); +} diff --git a/plugins/bkpr/account.h b/plugins/bkpr/account.h index f73d8ca72cd8..5f83090c8538 100644 --- a/plugins/bkpr/account.h +++ b/plugins/bkpr/account.h @@ -40,4 +40,7 @@ struct account { struct account *new_account(const tal_t *ctx, const char *name STEALS, struct node_id *peer_id); + +/* Is this a channel account? */ +bool is_channel_account(const struct account *acct); #endif /* LIGHTNING_PLUGINS_BKPR_ACCOUNT_H */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 160b06c306b9..823026b1a92d 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -45,6 +46,7 @@ static struct command_result *json_list_balances(struct command *cmd, err = account_get_balance(cmd, db, accts[i]->name, + true, &balances); if (err) @@ -76,12 +78,318 @@ static struct command_result *json_list_balances(struct command *cmd, return command_finished(cmd, res); } -struct account_snap { - char *name; - struct amount_msat amt; - char *coin_type; +struct new_account_info { + struct account *acct; + struct amount_msat curr_bal; + u32 timestamp; + char *currency; +}; + +static bool new_missed_channel_account(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct account *acct, + const char *currency, + u64 timestamp) +{ + struct chain_event *chain_ev; + size_t i, j; + const jsmntok_t *curr_peer, *curr_chan, + *peer_arr_tok, *chan_arr_tok; + + peer_arr_tok = json_get_member(buf, result, "peers"); + assert(peer_arr_tok->type == JSMN_ARRAY); + /* There should only be one peer */ + json_for_each_arr(i, curr_peer, peer_arr_tok) { + chan_arr_tok = json_get_member(buf, curr_peer, + "channels"); + assert(chan_arr_tok->type == JSMN_ARRAY); + json_for_each_arr(j, curr_chan, chan_arr_tok) { + struct bitcoin_outpoint opt; + struct amount_msat amt; + char *opener, *chan_id; + const char *err; + enum mvt_tag *tags; + + err = json_scan(tmpctx, buf, curr_chan, + "{channel_id:%," + "funding_txid:%," + "funding_outnum:%," + "funding:{local_msat:%}," + "opener:%}", + JSON_SCAN_TAL(tmpctx, json_strdup, &chan_id), + JSON_SCAN(json_to_txid, &opt.txid), + JSON_SCAN(json_to_number, &opt.n), + JSON_SCAN(json_to_msat, &amt), + JSON_SCAN_TAL(tmpctx, json_strdup, &opener)); + if (err) + plugin_err(cmd->plugin, + "failure scanning listpeer" + " result: %s", err); + + if (!streq(chan_id, acct->name)) + continue; + + chain_ev = tal(cmd, struct chain_event); + chain_ev->tag = "channel_open"; + chain_ev->credit = amt; + chain_ev->debit = AMOUNT_MSAT(0); + chain_ev->output_value = AMOUNT_MSAT(0); + chain_ev->currency = tal_strdup(chain_ev, currency); + /* 2s before the channel opened, minimum */ + chain_ev->timestamp = timestamp - 2; + chain_ev->blockheight = 0; + chain_ev->outpoint = opt; + chain_ev->spending_txid = NULL; + chain_ev->payment_id = NULL; + + /* Update the account info too */ + tags = tal_arr(chain_ev, enum mvt_tag, 1); + tags[1] = CHANNEL_OPEN; + if (streq(opener, "local")) + tal_arr_expand(&tags, OPENER); + + db_begin_transaction(db); + log_chain_event(db, acct, chain_ev); + maybe_update_account(db, acct, chain_ev, tags); + db_commit_transaction(db); + return true; + } + } + + return false; +} + +/* Net out credit/debit --> basically find the diff */ +static char *msat_net(const tal_t *ctx, + struct amount_msat credit, + struct amount_msat debit, + struct amount_msat *credit_net, + struct amount_msat *debit_net) +{ + if (amount_msat_eq(credit, debit)) { + *credit_net = AMOUNT_MSAT(0); + *debit_net = AMOUNT_MSAT(0); + } else if (amount_msat_greater(credit, debit)) { + if (!amount_msat_sub(credit_net, credit, debit)) + return tal_fmt(ctx, "unexpected fail, can't sub." + " %s - %s", + type_to_string(ctx, struct amount_msat, + &credit), + type_to_string(ctx, struct amount_msat, + &debit)); + *debit_net = AMOUNT_MSAT(0); + } else { + if (!amount_msat_sub(debit_net, debit, credit)) { + return tal_fmt(ctx, "unexpected fail, can't sub." + " %s - %s", + type_to_string(ctx, + struct amount_msat, + &debit), + type_to_string(ctx, + struct amount_msat, + &credit)); + } + *credit_net = AMOUNT_MSAT(0); + } + + return NULL; +} + +static char *msat_find_diff(struct amount_msat balance, + struct amount_msat credits, + struct amount_msat debits, + struct amount_msat *credit_diff, + struct amount_msat *debit_diff) +{ + struct amount_msat net_credit, net_debit; + char *err; + + err = msat_net(tmpctx, credits, debits, + &net_credit, &net_debit); + if (err) + return err; + + /* If we're not missing events, debits == 0 */ + if (!amount_msat_zero(net_debit)) { + assert(amount_msat_zero(net_credit)); + if (!amount_msat_add(credit_diff, net_debit, balance)) + return "Overflow finding credit_diff"; + *debit_diff = AMOUNT_MSAT(0); + } else { + assert(amount_msat_zero(net_debit)); + if (amount_msat_greater(net_credit, balance)) { + if (!amount_msat_sub(debit_diff, net_credit, + balance)) + return "Err net_credit - amt"; + *credit_diff = AMOUNT_MSAT(0); + } else { + if (!amount_msat_sub(credit_diff, balance, + net_credit)) + return "Err amt - net_credit"; + + *debit_diff = AMOUNT_MSAT(0); + } + } + + return NULL; +} + +static void log_journal_entry(struct account *acct, + const char *currency, + u64 timestamp, + struct amount_msat credit_diff, + struct amount_msat debit_diff) +{ + struct channel_event *chan_ev; + + /* No diffs to register, no journal needed */ + if (amount_msat_zero(credit_diff) + && amount_msat_zero(debit_diff)) + return; + + chan_ev = new_channel_event(tmpctx, + tal_fmt(tmpctx, "journal_entry"), + credit_diff, + debit_diff, + AMOUNT_MSAT(0), + currency, + NULL, 0, + timestamp); + db_begin_transaction(db); + log_channel_event(db, acct, chan_ev); + db_commit_transaction(db); +} + +static struct command_result *log_error(struct command *cmd, + const char *buf, + const jsmntok_t *error, + void *arg UNNEEDED) +{ + plugin_log(cmd->plugin, LOG_BROKEN, + "error calling `listpeers`: %.*s", + json_tok_full_len(error), + json_tok_full(buf, error)); + + return notification_handled(cmd); +} + +static struct command_result * +listpeers_multi_done(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct new_account_info **new_accts) +{ + /* Let's register all these accounts! */ + for (size_t i = 0; i < tal_count(new_accts); i++) { + struct new_account_info *info = new_accts[i]; + struct acct_balance **balances, *bal; + struct amount_msat credit_diff, debit_diff; + char *err; + + if (!new_missed_channel_account(cmd, buf, result, + info->acct, + info->currency, + info->timestamp)) { + plugin_log(cmd->plugin, LOG_BROKEN, + "Unable to find account %s in listpeers", + info->acct->name); + continue; + } + + db_begin_transaction(db); + err = account_get_balance(tmpctx, db, info->acct->name, + false, &balances); + db_commit_transaction(db); + + if (err) + plugin_err(cmd->plugin, err); + + /* FIXME: multiple currencies */ + if (tal_count(balances) > 0) + bal = balances[0]; + else { + bal = tal(tmpctx, struct acct_balance); + bal->credit = AMOUNT_MSAT(0); + bal->debit= AMOUNT_MSAT(0); + } + + err = msat_find_diff(info->curr_bal, + bal->credit, + bal->debit, + &credit_diff, &debit_diff); + if (err) + plugin_err(cmd->plugin, err); + + log_journal_entry(info->acct, + info->currency, + info->timestamp - 1, + credit_diff, debit_diff); + } + + return notification_handled(cmd); +} + +struct event_info { + struct chain_event *ev; + struct account *acct; }; +static struct command_result * +listpeers_done(struct command *cmd, const char *buf, + const jsmntok_t *result, struct event_info *info) +{ + struct acct_balance **balances, *bal; + struct amount_msat credit_diff, debit_diff; + const char *err; + /* Make sure to clean up when we're done */ + tal_steal(cmd, info); + + if (new_missed_channel_account(cmd, buf, result, + info->acct, + info->ev->currency, + info->ev->timestamp)) { + db_begin_transaction(db); + err = account_get_balance(tmpctx, db, info->acct->name, + false, &balances); + db_commit_transaction(db); + + if (err) + plugin_err(cmd->plugin, err); + + /* FIXME: multiple currencies per account? */ + if (tal_count(balances) > 0) + bal = balances[0]; + else { + bal = tal(balances, struct acct_balance); + bal->credit = AMOUNT_MSAT(0); + bal->debit = AMOUNT_MSAT(0); + } + assert(tal_count(balances) == 1); + + /* The expected current balance is zero, since + * we just got the channel close event */ + err = msat_find_diff(AMOUNT_MSAT(0), + bal->credit, + bal->debit, + &credit_diff, &debit_diff); + if (err) + plugin_err(cmd->plugin, err); + + log_journal_entry(info->acct, + info->ev->currency, + info->ev->timestamp - 1, + credit_diff, debit_diff); + } else + plugin_log(cmd->plugin, LOG_BROKEN, + "Unable to find account %s in listpeers", + info->acct->name); + + /* FIXME: maybe mark channel as 'onchain_resolved' */ + return notification_handled(cmd); +} + + static struct command_result *json_balance_snapshot(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -90,7 +398,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, size_t i; u32 blockheight; u64 timestamp; - struct account_snap *snaps; + struct new_account_info **new_accts; const jsmntok_t *accounts_tok, *acct_tok, *snap_tok = json_get_member(buf, params, "balance_snapshot"); @@ -120,22 +428,22 @@ static struct command_result *json_balance_snapshot(struct command *cmd, json_tok_full_len(params), json_tok_full(buf, params)); - snaps = tal_arr(cmd, struct account_snap, accounts_tok->size); + new_accts = tal_arr(cmd, struct new_account_info *, 0); db_begin_transaction(db); json_for_each_arr(i, acct_tok, accounts_tok) { - struct acct_balance **balances; - struct amount_msat balance; - struct account_snap s = snaps[i]; + struct acct_balance **balances, *bal; + struct amount_msat snap_balance, credit_diff, debit_diff; + char *acct_name, *currency; err = json_scan(cmd, buf, acct_tok, "{account_id:%" ",balance_msat:%" ",coin_type:%}", - JSON_SCAN_TAL(tmpctx, json_strdup, &s.name), - JSON_SCAN(json_to_msat, &s.amt), + JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name), + JSON_SCAN(json_to_msat, &snap_balance), JSON_SCAN_TAL(tmpctx, json_strdup, - &s.coin_type)); + ¤cy)); if (err) plugin_err(cmd->plugin, "`balance_snapshot` payload did not" @@ -144,87 +452,131 @@ static struct command_result *json_balance_snapshot(struct command *cmd, json_tok_full(buf, params)); plugin_log(cmd->plugin, LOG_DBG, "account %s has balance %s", - s.name, - type_to_string(tmpctx, struct amount_msat, &s.amt)); - - /* Find the account and verify the balance */ - err = account_get_balance(cmd, db, s.name, + acct_name, + type_to_string(tmpctx, struct amount_msat, + &snap_balance)); + + /* Find the account balances */ + err = account_get_balance(cmd, db, acct_name, + /* Don't error if negative */ + false, &balances); if (err) plugin_err(cmd->plugin, "Get account balance returned err" " for account %s: %s", - s.name, err); + acct_name, err); /* FIXME: multiple currency balances */ - balance = AMOUNT_MSAT(0); - for (size_t j = 0; j < tal_count(balances); j++) { - bool ok; - ok = amount_msat_add(&balance, balance, - balances[j]->balance); - assert(ok); + if (tal_count(balances) > 0) + bal = balances[0]; + else { + bal = tal(balances, struct acct_balance); + bal->credit = AMOUNT_MSAT(0); + bal->debit = AMOUNT_MSAT(0); } - if (!amount_msat_eq(s.amt, balance)) { + /* Figure out what the net diff is btw reported & actual */ + err = msat_find_diff(snap_balance, + bal->credit, + bal->debit, + &credit_diff, &debit_diff); + if (err) + plugin_err(cmd->plugin, + "Unable to find_diff for amounts: %s", + err); + + if (!amount_msat_zero(credit_diff) + || !amount_msat_zero(debit_diff)) { struct account *acct; - struct channel_event ev; + struct channel_event *ev; + u64 timestamp; plugin_log(cmd->plugin, LOG_UNUSUAL, "Snapshot balance does not equal ondisk" - " reported %s, on disk %s (account %s)." + " reported %s, off by (+%s/-%s) (account %s)" " Logging journal entry.", - type_to_string(tmpctx, struct amount_msat, &s.amt), - type_to_string(tmpctx, struct amount_msat, &balance), - s.name); - - if (!amount_msat_sub(&ev.credit, s.amt, balance)) { - ev.credit = AMOUNT_MSAT(0); - if (!amount_msat_sub(&ev.debit, balance, s.amt)) - plugin_err(cmd->plugin, - "Unable to sub amt"); - } else - ev.debit = AMOUNT_MSAT(0); + type_to_string(tmpctx, struct amount_msat, + &snap_balance), + type_to_string(tmpctx, struct amount_msat, + &debit_diff), + type_to_string(tmpctx, struct amount_msat, + &credit_diff), + acct_name); + + timestamp = time_now().ts.tv_sec; /* Log a channel "journal entry" to get * the balances inline */ - acct = find_account(cmd, db, s.name); + acct = find_account(cmd, db, acct_name); if (!acct) { + struct new_account_info *info; + plugin_log(cmd->plugin, LOG_INFORM, "account %s not found, adding" " along with new balance", - s.name); + acct_name); + /* FIXME: lookup peer id for channel? */ - acct = new_account(cmd, s.name, NULL); + acct = new_account(cmd, acct_name, NULL); account_add(db, acct); + + /* If we're entering a channel account, + * from a balance entry, we need to + * go find the channel open info*/ + if (is_channel_account(acct)) { + info = tal(new_accts, struct new_account_info); + info->acct = tal_steal(info, acct); + info->curr_bal = snap_balance; + info->timestamp = timestamp; + info->currency = + tal_strdup(info, currency); + + tal_arr_expand(&new_accts, info); + continue; + } } - /* This is *not* a coin_mvt tag type */ - ev.tag = "journal_entry"; - ev.fees = AMOUNT_MSAT(0); - ev.currency = s.coin_type; - ev.part_id = 0; - ev.payment_id = NULL; - /* Use current time for this */ - ev.timestamp = time_now().ts.tv_sec; + ev = new_channel_event(cmd, + tal_fmt(tmpctx, "journal_entry"), + credit_diff, + debit_diff, + AMOUNT_MSAT(0), + currency, + NULL, 0, + timestamp); - log_channel_event(db, acct, &ev); + log_channel_event(db, acct, ev); } } db_commit_transaction(db); + if (tal_count(new_accts) > 0) { + struct out_req *req; + + req = jsonrpc_request_start(cmd->plugin, NULL, + "listpeers", + listpeers_multi_done, + log_error, + new_accts); + send_outreq(cmd->plugin, req); + return command_still_pending(cmd); + } + return notification_handled(cmd); } -static const char *parse_and_log_chain_move(struct command *cmd, - const char *buf, - const jsmntok_t *params, - const char *acct_name STEALS, - const struct amount_msat credit, - const struct amount_msat debit, - const char *coin_type STEALS, - const u64 timestamp, - const enum mvt_tag *tags) +static struct command_result * +parse_and_log_chain_move(struct command *cmd, + const char *buf, + const jsmntok_t *params, + const char *acct_name STEALS, + const struct amount_msat credit, + const struct amount_msat debit, + const char *coin_type STEALS, + const u64 timestamp, + const enum mvt_tag *tags) { struct chain_event *e = tal(cmd, struct chain_event); struct sha256 *payment_hash = tal(cmd, struct sha256); @@ -246,7 +598,11 @@ static const char *parse_and_log_chain_move(struct command *cmd, JSON_SCAN(json_to_number, &e->blockheight)); if (err) - return err; + plugin_err(cmd->plugin, + "`coin_movement` payload did" + " not scan %s: %.*s", + err, json_tok_full_len(params), + json_tok_full(buf, params)); /* Now try to get out the optional parts */ err = json_scan(tmpctx, buf, params, @@ -255,8 +611,10 @@ static const char *parse_and_log_chain_move(struct command *cmd, "}}", JSON_SCAN(json_to_txid, spending_txid)); - if (err) + if (err) { spending_txid = tal_free(spending_txid); + err = tal_free(err); + } e->spending_txid = tal_steal(e, spending_txid); @@ -267,8 +625,10 @@ static const char *parse_and_log_chain_move(struct command *cmd, "}}", JSON_SCAN(json_to_sha256, payment_hash)); - if (err) + if (err) { payment_hash = tal_free(payment_hash); + err = tal_free(err); + } e->payment_id = tal_steal(e, payment_hash); @@ -287,7 +647,11 @@ static const char *parse_and_log_chain_move(struct command *cmd, account_add(db, acct); } - log_chain_event(db, acct, e); + if (!log_chain_event(db, acct, e)) { + db_commit_transaction(db); + /* This is not a new event, do nothing */ + return notification_handled(cmd); + } /* This event *might* have implications for account; * update as necessary */ @@ -302,22 +666,55 @@ static const char *parse_and_log_chain_move(struct command *cmd, db_commit_transaction(db); if (err) - return err; + plugin_err(cmd->plugin, + "Unable to update onchain fees %s", + err); + + /* If this is an account close event, it's possible + * that we *never* got the open event. (This happens + * if you add the plugin *after* you've closed the channel) */ + if (!acct->open_event_db_id + && acct->closed_event_db_id + && *acct->closed_event_db_id == e->db_id) { + /* Find the channel open info for this peer */ + struct out_req *req; + struct event_info *info; + + plugin_log(cmd->plugin, LOG_DBG, + "`channel_close` but no open for channel %s." + " Calling `listpeers` to fetch missing info", + acct->name); + + info = tal(NULL, struct event_info); + info->ev = tal_steal(info, e); + info->acct = tal_steal(info, acct); + req = jsonrpc_request_start(cmd->plugin, NULL, + "listpeers", + listpeers_done, + log_error, + info); + /* FIXME: use the peer_id to reduce work here */ + send_outreq(cmd->plugin, req); + return command_still_pending(cmd); + } /* FIXME: maybe mark channel as 'onchain_resolved' */ + if (err) + plugin_err(cmd->plugin, err); - return NULL; + return notification_handled(cmd);; } -static const char *parse_and_log_channel_move(struct command *cmd, - const char *buf, - const jsmntok_t *params, - const char *acct_name STEALS, - const struct amount_msat credit, - const struct amount_msat debit, - const char *coin_type STEALS, - const u64 timestamp, - const enum mvt_tag *tags) +static struct command_result * +parse_and_log_channel_move(struct command *cmd, + const char *buf, + const jsmntok_t *params, + const char *acct_name STEALS, + const struct amount_msat credit, + const struct amount_msat debit, + const char *coin_type STEALS, + const u64 timestamp, + const enum mvt_tag *tags) { struct channel_event *e = tal(cmd, struct channel_event); struct account *acct; @@ -327,20 +724,26 @@ static const char *parse_and_log_channel_move(struct command *cmd, err = json_scan(tmpctx, buf, params, "{coin_movement:{payment_hash:%}}", JSON_SCAN(json_to_sha256, e->payment_id)); - if (err) + if (err) { e->payment_id = tal_free(e->payment_id); + err = tal_free(err); + } err = json_scan(tmpctx, buf, params, "{coin_movement:{part_id:%}}", JSON_SCAN(json_to_number, &e->part_id)); - if (err) + if (err) { e->part_id = 0; + err = tal_free(err); + } err = json_scan(tmpctx, buf, params, "{coin_movement:{fees_msat:%}}", JSON_SCAN(json_to_msat, &e->fees)); - if (err) + if (err) { e->fees = AMOUNT_MSAT(0); + err = tal_free(err); + } e->credit = credit; e->debit = debit; @@ -360,7 +763,7 @@ static const char *parse_and_log_channel_move(struct command *cmd, log_channel_event(db, acct, e); db_commit_transaction(db); - return NULL; + return notification_handled(cmd); } static char *parse_tags(const tal_t *ctx, @@ -437,25 +840,15 @@ static struct command_result * json_coin_moved(struct command *cmd, mvt_type, timestamp); if (streq(mvt_type, CHAIN_MOVE)) - err = parse_and_log_chain_move(cmd, buf, params, - acct_name, credit, debit, - coin_type, timestamp, - tags); - else { - assert(streq(mvt_type, CHANNEL_MOVE)); - err = parse_and_log_channel_move(cmd, buf, params, - acct_name, credit, debit, - coin_type, timestamp, - tags); - } + return parse_and_log_chain_move(cmd, buf, params, + acct_name, credit, debit, + coin_type, timestamp, tags); - if (err) - plugin_err(cmd->plugin, - "`coin_movement` payload did not scan %s: %.*s", - err, json_tok_full_len(params), - json_tok_full(buf, params)); - return notification_handled(cmd); + assert(streq(mvt_type, CHANNEL_MOVE)); + return parse_and_log_channel_move(cmd, buf, params, + acct_name, credit, debit, + coin_type, timestamp, tags); } const struct plugin_notification notifs[] = { diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c new file mode 100644 index 000000000000..55ae4686e548 --- /dev/null +++ b/plugins/bkpr/channel_event.c @@ -0,0 +1,30 @@ +#include "config.h" + +#include +#include +#include +#include + +struct channel_event *new_channel_event(const tal_t *ctx, + const char *tag, + struct amount_msat credit, + struct amount_msat debit, + struct amount_msat fees, + const char *currency, + struct sha256 *payment_id STEALS, + u32 part_id, + u64 timestamp) +{ + struct channel_event *ev = tal(ctx, struct channel_event); + + ev->tag = tal_strdup(ev, tag); + ev->credit = credit; + ev->debit = debit; + ev->fees = fees; + ev->currency = tal_strdup(ev, currency); + ev->payment_id = tal_steal(ev, payment_id); + ev->part_id = part_id; + ev->timestamp = timestamp; + + return ev; +} diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h index 7e231dce22a9..8b0066ab963c 100644 --- a/plugins/bkpr/channel_event.h +++ b/plugins/bkpr/channel_event.h @@ -3,6 +3,7 @@ #include "config.h" #include +#include struct amount_msat; struct sha256; @@ -43,4 +44,14 @@ struct channel_event { u64 timestamp; }; +struct channel_event *new_channel_event(const tal_t *ctx, + const char *tag, + struct amount_msat credit, + struct amount_msat debit, + struct amount_msat fees, + const char *currency, + struct sha256 *payment_id STEALS, + u32 part_id, + u64 timestamp); + #endif /* LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H */ diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index f7cc03093f71..8f9175f7eb21 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -206,6 +206,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, char *account_get_balance(const tal_t *ctx, struct db *db, const char *acct_name, + bool calc_sum, struct acct_balance ***balances) { struct db_stmt *stmt; @@ -285,6 +286,9 @@ char *account_get_balance(const tal_t *ctx, } tal_free(stmt); + if (!calc_sum) + return NULL; + for (size_t i = 0; i < tal_count(*balances); i++) { struct acct_balance *bal = (*balances)[i]; if (!amount_msat_sub(&bal->balance, bal->credit, bal->debit)) @@ -953,7 +957,7 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, return err; } -void log_chain_event(struct db *db, +bool log_chain_event(struct db *db, const struct account *acct, struct chain_event *e) { @@ -962,7 +966,7 @@ void log_chain_event(struct db *db, /* We're responsible for de-duping chain events! */ if (find_chain_event(e, db, acct, &e->outpoint, e->spending_txid)) - return; + return false; stmt = db_prepare_v2(db, SQL("INSERT INTO chain_events" " (" @@ -1008,4 +1012,5 @@ void log_chain_event(struct db *db, e->acct_db_id = acct->db_id; e->acct_name = tal_strdup(e, acct->name); tal_free(stmt); + return true; } diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 449d4775b75c..03aa09c5fdce 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -36,11 +36,14 @@ struct channel_event **account_get_channel_events(const tal_t *ctx, struct chain_event **account_get_chain_events(const tal_t *ctx, struct db *db, struct account *acct); - -/* Calculate the balances for an account */ +/* Calculate the balances for an account + * + * @calc_sum - compute the total balance. error if negative + * */ char *account_get_balance(const tal_t *ctx, struct db *db, const char *acct_name, + bool calc_sum, struct acct_balance ***balances); /* List all chain fees, for all accounts */ @@ -70,8 +73,9 @@ void log_channel_event(struct db *db, const struct account *acct, struct channel_event *e); -/* Log a chain event. */ -void log_chain_event(struct db *db, +/* Log a chain event. + * Returns true if inserted, false if already exists */ +bool log_chain_event(struct db *db, const struct account *acct, struct chain_event *e); diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index ad55c6eca1ea..46dc28896ccb 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -952,7 +952,7 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) /* Add same chain event to a different account, shouldn't show */ log_chain_event(db, acct2, ev1); - err = account_get_balance(ctx, db, acct->name, + err = account_get_balance(ctx, db, acct->name, true, &balances); CHECK_MSG(!err, err); db_commit_transaction(db); @@ -974,10 +974,14 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) ev1->currency = "chf"; log_chain_event(db, acct, ev1); - err = account_get_balance(ctx, db, acct->name, + err = account_get_balance(ctx, db, acct->name, true, &balances); CHECK_MSG(err != NULL, "Expected err message"); CHECK(streq(err, "chf channel balance is negative? 5000msat - 5001msat")); + + err = account_get_balance(ctx, db, acct->name, false, + &balances); + CHECK_MSG(!err, err); db_commit_transaction(db); return true; From a1d72cef06f6809e7ec4cf5ea867d66dc8f00074 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1140/1530] bkpr: add a new command `listaccountevents` Prints all the events for the requested account. If no account requested, prints out all the events. Ordered by timestamp. Changelog-Added: bookkeeper: new command `listaccountevents` --- plugins/bkpr/bookkeeper.c | 162 ++++++++++++++++++++++++++++++++++++++ plugins/bkpr/recorder.c | 98 +++++++++++++++++++++++ plugins/bkpr/recorder.h | 11 +++ 3 files changed, 271 insertions(+) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 823026b1a92d..33e0c4ba0fc7 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include @@ -24,6 +26,158 @@ static struct db *db ; // FIXME: make relative to directory we're loaded into static char *db_dsn = "sqlite3://accounts.sqlite3"; +static void json_add_channel_event(struct json_stream *out, + struct channel_event *ev) +{ + json_object_start(out, NULL); + json_add_string(out, "account", ev->acct_name); + json_add_string(out, "type", "channel"); + json_add_string(out, "tag", ev->tag); + json_add_amount_msat_only(out, "credit", ev->credit); + json_add_amount_msat_only(out, "debit", ev->debit); + json_add_string(out, "currency", ev->currency); + if (ev->payment_id) + json_add_sha256(out, "payment_id", ev->payment_id); + json_add_u64(out, "timestamp", ev->timestamp); + json_object_end(out); +} + +static void json_add_chain_event(struct json_stream *out, + struct chain_event *ev) +{ + json_object_start(out, NULL); + json_add_string(out, "account", ev->acct_name); + json_add_string(out, "type", "chain"); + json_add_string(out, "tag", ev->tag); + json_add_amount_msat_only(out, "credit", ev->credit); + json_add_amount_msat_only(out, "debit", ev->debit); + json_add_string(out, "currency", ev->currency); + json_add_outpoint(out, "outpoint", &ev->outpoint); + if (ev->spending_txid) + json_add_txid(out, "txid", ev->spending_txid); + if (ev->payment_id) + json_add_sha256(out, "payment_id", ev->payment_id); + json_add_u64(out, "timestamp", ev->timestamp); + json_add_u32(out, "blockheight", ev->blockheight); + json_object_end(out); +} + +static void json_add_onchain_fee(struct json_stream *out, + struct onchain_fee *fee) +{ + json_object_start(out, NULL); + json_add_string(out, "account", fee->acct_name); + json_add_string(out, "type", "onchain_fee"); + json_add_string(out, "tag", "onchain_fee"); + json_add_amount_msat_only(out, "credit", fee->credit); + json_add_amount_msat_only(out, "debit", fee->debit); + json_add_string(out, "currency", fee->currency); + json_add_u64(out, "timestamp", fee->timestamp); + json_add_txid(out, "txid", &fee->txid); + json_object_end(out); +} + +/* Find all the events for this account, ordered by timestamp */ +static struct command_result *json_list_account_events(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + struct account *acct; + const char *acct_name; + struct channel_event **channel_events; + struct chain_event **chain_events; + struct onchain_fee **onchain_fees; + + if (!param(cmd, buf, params, + p_opt("account", param_string, &acct_name), + NULL)) + return command_param_failed(); + + if (acct_name) { + db_begin_transaction(db); + acct = find_account(cmd, db, acct_name); + db_commit_transaction(db); + + if (!acct) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Account '%s' not found", + acct_name); + } else + acct = NULL; + + db_begin_transaction(db); + if (acct) { + channel_events = account_get_channel_events(cmd, db, acct); + chain_events = account_get_chain_events(cmd, db, acct); + onchain_fees = account_get_chain_fees(cmd, db, acct); + } else { + channel_events = list_channel_events(cmd, db); + chain_events = list_chain_events(cmd, db); + onchain_fees = list_chain_fees(cmd, db); + } + db_commit_transaction(db); + + res = jsonrpc_stream_success(cmd); + json_array_start(res, "events"); + for (size_t i = 0, j = 0, k = 0; + i < tal_count(channel_events) + || j < tal_count(chain_events) + || k < tal_count(onchain_fees); + /* Incrementing happens inside loop */) { + struct channel_event *chan; + struct chain_event *chain; + struct onchain_fee *fee; + u64 lowest = 0; + + if (i < tal_count(channel_events)) + chan = channel_events[i]; + else + chan = NULL; + if (j < tal_count(chain_events)) + chain = chain_events[j]; + else + chain = NULL; + if (k < tal_count(onchain_fees)) + fee = onchain_fees[k]; + else + fee = NULL; + + if (chan) + lowest = chan->timestamp; + + if (chain + && (lowest == 0 || lowest > chain->timestamp)) + lowest = chain->timestamp; + + if (fee + && (lowest == 0 || lowest > fee->timestamp)) + lowest = fee->timestamp; + + /* channel events first, then chain events, then fees. + * (channel events contain journal entries, which + * are the starting balance for accounts created + * before the accountant plugin was installed) */ + if (chan && chan->timestamp == lowest) { + json_add_channel_event(res, chan); + i++; + continue; + } + + if (chain && chain->timestamp == lowest) { + json_add_chain_event(res, chain); + j++; + continue; + } + + /* Last thing left is the fee */ + json_add_onchain_fee(res, fee); + k++; + } + json_array_end(res); + return command_finished(cmd, res); +} + static struct command_result *json_list_balances(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -870,6 +1024,14 @@ static const struct plugin_command commands[] = { "List of current accounts and their balances", json_list_balances }, + { + "listaccountevents", + "bookkeeping", + "List all events for an {account}", + "List all events for an {account} (or all accounts, if" + " no account specified) in {format}. Sorted by timestamp", + json_list_account_events + }, }; static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 8f9175f7eb21..1a8b45d4482f 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -97,6 +97,33 @@ static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt return e; } +struct chain_event **list_chain_events(const tal_t *ctx, struct db *db) +{ + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " ORDER BY e.timestamp, e.id;")); + + return find_chain_events(ctx, take(stmt)); +} + struct chain_event **account_get_chain_events(const tal_t *ctx, struct db *db, struct account *acct) @@ -304,6 +331,40 @@ char *account_get_balance(const tal_t *ctx, return NULL; } +struct channel_event **list_channel_events(const tal_t *ctx, struct db *db) +{ + struct db_stmt *stmt; + struct channel_event **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.fees" + ", e.currency" + ", e.payment_id" + ", e.part_id" + ", e.timestamp" + " FROM channel_events e" + " LEFT OUTER JOIN accounts a" + " ON a.id = e.account_id" + " ORDER BY e.timestamp, e.id;")); + + db_query_prepared(stmt); + + results = tal_arr(ctx, struct channel_event *, 0); + while (db_step(stmt)) { + struct channel_event *e = stmt2channel_event(results, stmt); + tal_arr_expand(&results, e); + } + tal_free(stmt); + + return results; +} + struct channel_event **account_get_channel_events(const tal_t *ctx, struct db *db, struct account *acct) @@ -359,6 +420,43 @@ static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, return of; } +struct onchain_fee **account_get_chain_fees(const tal_t *ctx, struct db *db, + struct account *acct) +{ + struct db_stmt *stmt; + struct onchain_fee **results; + + stmt = db_prepare_v2(db, SQL("SELECT" + " of.account_id" + ", a.name" + ", of.txid" + ", of.credit" + ", of.debit" + ", of.currency" + ", of.timestamp" + ", of.update_count" + " FROM onchain_fees of" + " LEFT OUTER JOIN accounts a" + " ON a.id = of.account_id" + " WHERE of.account_id = ?" + " ORDER BY " + " of.timestamp" + ", of.txid" + ", of.update_count")); + + db_bind_u64(stmt, 0, acct->db_id); + db_query_prepared(stmt); + + results = tal_arr(ctx, struct onchain_fee *, 0); + while (db_step(stmt)) { + struct onchain_fee *of = stmt2onchain_fee(results, stmt); + tal_arr_expand(&results, of); + } + tal_free(stmt); + + return results; +} + struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) { struct db_stmt *stmt; diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 03aa09c5fdce..2f41e0232b2d 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -32,10 +32,17 @@ struct channel_event **account_get_channel_events(const tal_t *ctx, struct db *db, struct account *acct); +/* Get all channel events, ordered by timestamp */ +struct channel_event **list_channel_events(const tal_t *ctx, struct db *db); + /* Get all chain events for this account */ struct chain_event **account_get_chain_events(const tal_t *ctx, struct db *db, struct account *acct); + +/* Get all chain events, ordered by timestamp */ +struct chain_event **list_chain_events(const tal_t *ctx, struct db *db); + /* Calculate the balances for an account * * @calc_sum - compute the total balance. error if negative @@ -46,6 +53,10 @@ char *account_get_balance(const tal_t *ctx, bool calc_sum, struct acct_balance ***balances); +/* Get chain fees for account */ +struct onchain_fee **account_get_chain_fees(const tal_t *ctx, struct db *db, + struct account *acct); + /* List all chain fees, for all accounts */ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db); From 8089f246c19b6ad2b93126d932ae6fcbc0dc7695 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1141/1530] bkpr: use tags not str for tag originations --- plugins/bkpr/Makefile | 2 ++ plugins/bkpr/account_entry.c | 26 ++++++++++++++++++++++++++ plugins/bkpr/account_entry.h | 15 +++++++++++++++ plugins/bkpr/bookkeeper.c | 11 +++++++---- 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 plugins/bkpr/account_entry.c create mode 100644 plugins/bkpr/account_entry.h diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index 3a1f5146236d..34b54ae1d98b 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -2,6 +2,7 @@ BOOKKEEPER_PLUGIN_SRC := \ plugins/bkpr/account.c \ + plugins/bkpr/account_entry.c \ plugins/bkpr/bookkeeper.c \ plugins/bkpr/channel_event.c \ plugins/bkpr/db.c \ @@ -14,6 +15,7 @@ BOOKKEEPER_DB_QUERIES := \ BOOKKEEPER_SRC := $(BOOKKEEPER_PLUGIN_SRC) $(BOOKKEEPER_DB_QUERIES) BOOKKEEPER_HEADER := \ plugins/bkpr/account.h \ + plugins/bkpr/account_entry.h \ plugins/bkpr/chain_event.h \ plugins/bkpr/channel_event.h \ plugins/bkpr/db.h \ diff --git a/plugins/bkpr/account_entry.c b/plugins/bkpr/account_entry.c new file mode 100644 index 000000000000..10cd29a7581e --- /dev/null +++ b/plugins/bkpr/account_entry.c @@ -0,0 +1,26 @@ +#include "config.h" + +#include +#include +#include + +static const char *tags[] = { + "journal_entry", +}; + +const char *account_entry_tag_str(enum account_entry_tag tag) +{ + return tags[tag]; +} + +bool account_entry_tag_find(char *str, enum account_entry_tag *tag) +{ + for (size_t i = 0; i < NUM_ACCOUNT_ENTRY_TAGS; i++) { + if (streq(str, tags[i])) { + *tag = (enum account_entry_tag) i; + return true; + } + } + + return false; +} diff --git a/plugins/bkpr/account_entry.h b/plugins/bkpr/account_entry.h new file mode 100644 index 000000000000..d0e608ba8aad --- /dev/null +++ b/plugins/bkpr/account_entry.h @@ -0,0 +1,15 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_ACCOUNT_ENTRY_H +#define LIGHTNING_PLUGINS_BKPR_ACCOUNT_ENTRY_H +#include "config.h" + +#define NUM_ACCOUNT_ENTRY_TAGS (JOURNAL_ENTRY + 1) +enum account_entry_tag { + JOURNAL_ENTRY = 0, +}; + +/* Convert an enum into a string */ +const char *account_entry_tag_str(enum account_entry_tag tag); + +/* True if entry tag found, false otherwise */ +bool account_entry_tag_find(char *str, enum account_entry_tag *tag); +#endif /* LIGHTNING_PLUGINS_BKPR_ACCOUNT_ENTRY_H */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 33e0c4ba0fc7..71cacb692550 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -9,10 +9,11 @@ #include #include #include -#include #include +#include #include #include +#include #include #include #include @@ -285,7 +286,7 @@ static bool new_missed_channel_account(struct command *cmd, continue; chain_ev = tal(cmd, struct chain_event); - chain_ev->tag = "channel_open"; + chain_ev->tag = mvt_tag_str(CHANNEL_OPEN); chain_ev->credit = amt; chain_ev->debit = AMOUNT_MSAT(0); chain_ev->output_value = AMOUNT_MSAT(0); @@ -403,7 +404,8 @@ static void log_journal_entry(struct account *acct, return; chan_ev = new_channel_event(tmpctx, - tal_fmt(tmpctx, "journal_entry"), + tal_fmt(tmpctx, "%s", + account_entry_tag_str(JOURNAL_ENTRY)), credit_diff, debit_diff, AMOUNT_MSAT(0), @@ -693,7 +695,8 @@ static struct command_result *json_balance_snapshot(struct command *cmd, } ev = new_channel_event(cmd, - tal_fmt(tmpctx, "journal_entry"), + tal_fmt(tmpctx, "%s", + account_entry_tag_str(JOURNAL_ENTRY)), credit_diff, debit_diff, AMOUNT_MSAT(0), From 307ea9359201f7c3f160eff4a1a6b93bd92f44c4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1142/1530] bkpr: invert channel + chain event printouts We now add chain events for starting channel balances, so print these out with chain first then channel events. Makes it less confusing for channel lease fee events. --- plugins/bkpr/bookkeeper.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 71cacb692550..4ad3288549e2 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -122,8 +122,8 @@ static struct command_result *json_list_account_events(struct command *cmd, res = jsonrpc_stream_success(cmd); json_array_start(res, "events"); for (size_t i = 0, j = 0, k = 0; - i < tal_count(channel_events) - || j < tal_count(chain_events) + i < tal_count(chain_events) + || j < tal_count(channel_events) || k < tal_count(onchain_fees); /* Incrementing happens inside loop */) { struct channel_event *chan; @@ -131,42 +131,39 @@ static struct command_result *json_list_account_events(struct command *cmd, struct onchain_fee *fee; u64 lowest = 0; - if (i < tal_count(channel_events)) - chan = channel_events[i]; - else - chan = NULL; - if (j < tal_count(chain_events)) - chain = chain_events[j]; + if (i < tal_count(chain_events)) + chain = chain_events[i]; else chain = NULL; + if (j < tal_count(channel_events)) + chan = channel_events[j]; + else + chan = NULL; if (k < tal_count(onchain_fees)) fee = onchain_fees[k]; else fee = NULL; - if (chan) - lowest = chan->timestamp; - - if (chain - && (lowest == 0 || lowest > chain->timestamp)) + if (chain) lowest = chain->timestamp; + if (chan + && (lowest == 0 || lowest > chan->timestamp)) + lowest = chan->timestamp; + if (fee && (lowest == 0 || lowest > fee->timestamp)) lowest = fee->timestamp; - /* channel events first, then chain events, then fees. - * (channel events contain journal entries, which - * are the starting balance for accounts created - * before the accountant plugin was installed) */ - if (chan && chan->timestamp == lowest) { - json_add_channel_event(res, chan); + /* chain events first, then channel events, then fees. */ + if (chain && chain->timestamp == lowest) { + json_add_chain_event(res, chain); i++; continue; } - if (chain && chain->timestamp == lowest) { - json_add_chain_event(res, chain); + if (chan && chan->timestamp == lowest) { + json_add_channel_event(res, chan); j++; continue; } From 2a3875204a562ebed60af4270f77368d9599d757 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1143/1530] bkpr: parse the 'originating_account' field, save to event It's useful to know which account an 'external' event impacted, so we save this data now --- plugins/bkpr/bookkeeper.c | 8 ++++++ plugins/bkpr/chain_event.h | 3 ++ plugins/bkpr/db.c | 1 + plugins/bkpr/recorder.c | 48 ++++++++++++++++++++------------ plugins/bkpr/recorder.h | 3 ++ plugins/bkpr/test/run-recorder.c | 9 ++++++ 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 4ad3288549e2..b0464f3e1c84 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -784,6 +784,14 @@ parse_and_log_chain_move(struct command *cmd, err = tal_free(err); } + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{originating_account:%}}", + JSON_SCAN_TAL(e, json_strdup, &e->origin_acct)); + + if (err) + e->origin_acct = NULL; + e->payment_id = tal_steal(e, payment_hash); e->credit = credit; diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index 9359de8f8900..1ebd69523974 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -19,6 +19,9 @@ struct chain_event { /* Name of the account this belongs to */ char *acct_name; + /* Name of account this originated from */ + char *origin_acct; + /* Tag describing the event */ const char *tag; diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 6064d3229adb..8edda43c8d85 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -92,6 +92,7 @@ static struct migration db_migrations[] = { ", PRIMARY KEY (account_id, txid, update_count)" ");"), NULL}, + {SQL("ALTER TABLE chain_events ADD origin TEXT;"), NULL}, }; static bool db_migrate(struct plugin *p, struct db *db) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 1a8b45d4482f..29f52ea97be4 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -15,9 +15,6 @@ #include #include -#define EXTERNAL_ACCT "external" -#define WALLET_ACCT WALLET - static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt) { struct chain_event *e = tal(ctx, struct chain_event); @@ -25,6 +22,11 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st e->acct_db_id = db_col_u64(stmt, "e.account_id"); e->acct_name = db_col_strdup(e, stmt, "a.name"); + if (!db_col_is_null(stmt, "e.origin")) + e->origin_acct = db_col_strdup(e, stmt, "e.origin"); + else + e->origin_acct = NULL; + e->tag = db_col_strdup(e, stmt, "e.tag"); db_col_amount_msat(stmt, "e.credit", &e->credit); @@ -105,6 +107,7 @@ struct chain_event **list_chain_events(const tal_t *ctx, struct db *db) " e.id" ", e.account_id" ", a.name" + ", e.origin" ", e.tag" ", e.credit" ", e.debit" @@ -134,6 +137,7 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, " e.id" ", e.account_id" ", a.name" + ", e.origin" ", e.tag" ", e.credit" ", e.debit" @@ -170,6 +174,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, " e.id" ", e.account_id" ", a.name" + ", e.origin" ", e.tag" ", e.credit" ", e.debit" @@ -195,6 +200,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, " e.id" ", e.account_id" ", a.name" + ", e.origin" ", e.tag" ", e.credit" ", e.debit" @@ -781,8 +787,9 @@ static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db stmt = db_prepare_v2(db, SQL("SELECT " " e.id" - ", a.name" ", e.account_id" + ", a.name" + ", e.origin" ", e.tag" ", e.credit" ", e.debit" @@ -1069,6 +1076,7 @@ bool log_chain_event(struct db *db, stmt = db_prepare_v2(db, SQL("INSERT INTO chain_events" " (" " account_id" + ", origin" ", tag" ", credit" ", debit" @@ -1082,28 +1090,32 @@ bool log_chain_event(struct db *db, ", spending_txid" ")" " VALUES" - " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); - db_bind_text(stmt, 1, e->tag); - db_bind_amount_msat(stmt, 2, &e->credit); - db_bind_amount_msat(stmt, 3, &e->debit); - db_bind_amount_msat(stmt, 4, &e->output_value); - db_bind_text(stmt, 5, e->currency); - db_bind_u64(stmt, 6, e->timestamp); - db_bind_int(stmt, 7, e->blockheight); - db_bind_txid(stmt, 8, &e->outpoint.txid); - db_bind_int(stmt, 9, e->outpoint.n); + if (e->origin_acct) + db_bind_text(stmt, 1, e->origin_acct); + else + db_bind_null(stmt, 1); + db_bind_text(stmt, 2, e->tag); + db_bind_amount_msat(stmt, 3, &e->credit); + db_bind_amount_msat(stmt, 4, &e->debit); + db_bind_amount_msat(stmt, 5, &e->output_value); + db_bind_text(stmt, 6, e->currency); + db_bind_u64(stmt, 7, e->timestamp); + db_bind_int(stmt, 8, e->blockheight); + db_bind_txid(stmt, 9, &e->outpoint.txid); + db_bind_int(stmt, 10, e->outpoint.n); if (e->payment_id) - db_bind_sha256(stmt, 10, e->payment_id); + db_bind_sha256(stmt, 11, e->payment_id); else - db_bind_null(stmt, 10); + db_bind_null(stmt, 11); if (e->spending_txid) - db_bind_txid(stmt, 11, e->spending_txid); + db_bind_txid(stmt, 12, e->spending_txid); else - db_bind_null(stmt, 11); + db_bind_null(stmt, 12); db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 2f41e0232b2d..6fe47b59cbb2 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -12,6 +12,9 @@ struct db; enum mvt_tag; struct onchain_fee; +#define EXTERNAL_ACCT "external" +#define WALLET_ACCT WALLET + struct acct_balance { char *currency; struct amount_msat credit; diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 46dc28896ccb..400a8fc35dae 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -272,6 +272,9 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) { CHECK(e1->db_id == e2->db_id); CHECK(e1->acct_db_id == e2->acct_db_id); + CHECK((e1->origin_acct != NULL) == (e2->origin_acct != NULL)); + if (e1->origin_acct) + CHECK(streq(e1->origin_acct, e2->origin_acct)); CHECK(streq(e1->tag, e2->tag)); CHECK(amount_msat_eq(e1->credit, e2->credit)); CHECK(amount_msat_eq(e1->debit, e2->debit)); @@ -289,6 +292,7 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) if (e1->payment_id) CHECK(sha256_eq(e1->payment_id, e2->payment_id)); + return true; } @@ -327,6 +331,7 @@ static struct chain_event *make_chain_event(const tal_t *ctx, /* This event spends the second inserted event */ ev->tag = tal_fmt(ctx, "%s", tag); + ev->origin_acct = NULL; ev->credit = credit; ev->debit = debit; ev->output_value = AMOUNT_MSAT(1000); @@ -797,6 +802,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) /* This event spends the second inserted event */ ev1 = tal(ctx, struct chain_event); ev1->tag = tal_fmt(ev1, "withdrawal"); + ev1->origin_acct = NULL; ev1->credit = AMOUNT_MSAT(100); ev1->debit = AMOUNT_MSAT(102); ev1->output_value = AMOUNT_MSAT(104); @@ -815,6 +821,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); ev2->tag = tal_fmt(ctx, "deposit"); + ev2->origin_acct = tal_fmt(ctx, "wallet"); ev2->credit = AMOUNT_MSAT(200); ev2->debit = AMOUNT_MSAT(202); ev2->output_value = AMOUNT_MSAT(104); @@ -830,6 +837,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) /* Dummy event, logged to separate account */ ev3 = tal(ctx, struct chain_event); ev3->tag = tal_fmt(ev3, "deposit"); + ev3->origin_acct = NULL; ev3->credit = AMOUNT_MSAT(300); ev3->debit = AMOUNT_MSAT(302); ev3->output_value = AMOUNT_MSAT(304); @@ -1038,6 +1046,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) * correctly, given an event and tag list? */ ev1 = tal(ctx, struct chain_event); ev1->tag = tal_fmt(ctx, "withdrawal"); + ev1->origin_acct = NULL; ev1->credit = AMOUNT_MSAT(100); ev1->debit = AMOUNT_MSAT(102); ev1->output_value = AMOUNT_MSAT(104); From ee8c04a63a30a5581bee4743cc67fe1d578844d2 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:35 +0930 Subject: [PATCH 1144/1530] bkpr: update tests for wallet/external onchain fees Working as intended; had a bool switched etc --- plugins/bkpr/recorder.c | 2 +- plugins/bkpr/test/run-recorder.c | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 29f52ea97be4..9c184f6eeba7 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -917,7 +917,7 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, { size_t no_accts = 0, plus_ones; u64 last_id = 0, wallet_id, extern_id; - bool contains_wallet = false, skip_wallet = true; + bool contains_wallet = false, skip_wallet = false; struct chain_event **events; struct amount_msat deposit_msat = AMOUNT_MSAT(0), withdraw_msat = AMOUNT_MSAT(0), diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 400a8fc35dae..8033d0507eb2 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -366,6 +366,7 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) wal_acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); + memset(&txid, '1', sizeof(struct bitcoin_txid)); db_begin_transaction(db); account_add(db, wal_acct); @@ -377,7 +378,7 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) * tag utxo_id vout txid debits credits acct_id * withdr XXXX 0 1111 1000 wallet * deposit 1111 1 200 wallet - * *screms* + * deposit 1111 0 700 external */ db_begin_transaction(db); log_chain_event(db, wal_acct, @@ -385,7 +386,6 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) AMOUNT_MSAT(0), AMOUNT_MSAT(1000), 'X', 0, '1')); - memset(&txid, '1', sizeof(struct bitcoin_txid)); maybe_update_onchain_fees(ctx, db, &txid); log_chain_event(db, wal_acct, @@ -393,19 +393,32 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) AMOUNT_MSAT(200), AMOUNT_MSAT(0), '1', 1, '*')); - memset(&txid, '1', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + + log_chain_event(db, ext_acct, + make_chain_event(ctx, "deposit", + AMOUNT_MSAT(700), + AMOUNT_MSAT(0), + '1', 0, '*')); maybe_update_onchain_fees(ctx, db, &txid); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - /* Send some funds to an external acct? */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - /* FIXME: track onchain wallet withdrawals?? */ - CHECK(tal_count(ofs) == 0); + CHECK(tal_count(ofs) == 2); + + /* we expect 800, then -700 */ + CHECK(amount_msat_eq(ofs[0]->credit, AMOUNT_MSAT(800))); + CHECK(amount_msat_zero(ofs[0]->debit)); + CHECK(ofs[0]->update_count == 1); + + CHECK(amount_msat_zero(ofs[1]->credit)); + CHECK(amount_msat_eq(ofs[1]->debit, AMOUNT_MSAT(700))); + CHECK(ofs[1]->update_count == 2); return true; } From 8c3347d1295b8508be9209a053b717128cb5e6a5 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1145/1530] bkpr: dont count fees for channel closes if we're not the opener The opener pays fees on close, so if we're not the opener we should ignore any fees on the closing tx. --- plugins/bkpr/recorder.c | 74 +++++++++++++++++++++++++++++++++++++++++ plugins/bkpr/recorder.h | 6 +++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 9c184f6eeba7..6ffeeddfd9be 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -159,6 +160,46 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, return find_chain_events(ctx, take(stmt)); } +struct chain_event *find_chain_event_by_id(const tal_t *ctx, + struct db *db, + u64 event_db_id) +{ + struct db_stmt *stmt; + struct chain_event *e; + + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.origin" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE " + " e.id = ?")); + + db_bind_u64(stmt, 0, event_db_id); + db_query_prepared(stmt); + if (db_step(stmt)) + e = stmt2chain_event(ctx, stmt); + else + e = NULL; + + tal_free(stmt); + return e; +} + static struct chain_event *find_chain_event(const tal_t *ctx, struct db *db, const struct account *acct, @@ -936,6 +977,39 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, for (size_t i = 0; i < tal_count(events); i++) { if (events[i]->spending_txid) { + struct account *acct; + /* Figure out if this is a channel close + * that we're not the opener for */ + acct = find_account(inner_ctx, db, + events[i]->acct_name); + assert(acct); + + /* If any of the spending_txid accounts are + * close accounts and we're not the opener, + * we end things */ + if (acct->closed_event_db_id && !acct->we_opened) { + struct chain_event *closed; + /* is the closed utxo the same as the one + * we're trying to find fees for now */ + closed = find_chain_event_by_id(inner_ctx, + db, *acct->closed_event_db_id); + if (!closed) { + err = tal_fmt(ctx, "Unable to find" + " db record (chain_evt)" + " with id %"PRIu64, + *acct->closed_event_db_id); + goto finished; + } + if (!closed->spending_txid) { + err = tal_fmt(ctx, "Marked a closing" + " event that's not" + " actually a spend"); + goto finished; + } + + if (bitcoin_txid_eq(txid, closed->spending_txid)) + goto finished; + } if (!amount_msat_add(&withdraw_msat, withdraw_msat, events[i]->debit)) { err = tal_fmt(ctx, "Overflow adding withdrawal debits for" diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 6fe47b59cbb2..0d2cdfad48ec 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -60,12 +60,16 @@ char *account_get_balance(const tal_t *ctx, struct onchain_fee **account_get_chain_fees(const tal_t *ctx, struct db *db, struct account *acct); +/* Find a chain event by its database id */ +struct chain_event *find_chain_event_by_id(const tal_t *ctx, + struct db *db, + u64 event_db_id); + /* List all chain fees, for all accounts */ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db); /* Add the given account to the database */ void account_add(struct db *db, struct account *acct); - /* Given an account name, find that account record */ struct account *find_account(const tal_t *ctx, struct db *db, From 08d8de8e45f300670df11c811927bbf074a6698e Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1146/1530] bkpr: don't try to add fees if this tx didn't touch any accts Dividing by zero causes problems. --- plugins/bkpr/recorder.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 6ffeeddfd9be..9754f01a865d 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -1051,6 +1051,10 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, } } + /* Only affects external accounts, we can ignore */ + if (no_accts == 0) + goto finished; + /* If either is zero, keep waiting */ if (amount_msat_zero(withdraw_msat) || amount_msat_zero(deposit_msat)) @@ -1099,7 +1103,7 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, last_id = events[i]->acct_db_id; - /* We *never assign fees to external accounts; + /* We *never* assign fees to external accounts; * if external funds were contributed to a tx * we wouldn't record it -- fees are solely ours */ if (last_id == extern_id) From 791c1a7526cf8ad961e2b6b93022f7f9d57dd325 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1147/1530] bkpr: cleanup wallet fee entries if decide they belong to a channel Because we update the onchain_fee table every time a new event comes in, it's possible (and in fact happens) that we get a wallet withdraw/deposit event and then the channel open output event. What we'd expect in this case is that the fees for the tx were credited to the channel's account, not the wallet. But since we got the two in/out events first, the fees were accumulated there first. Our existing logic will add the channel's fees correctly, but we weren't zeroing out the wallet's balance once it'd been determined that they were 'ineligble' so to speak, for being included in the fees that round. --- plugins/bkpr/recorder.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 9754f01a865d..929e2628a259 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -1111,8 +1111,16 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, /* We only attribute fees to the wallet * if the wallet is the only game in town */ - if (skip_wallet && last_id == wallet_id) + if (skip_wallet && last_id == wallet_id) { + /* But we might need to clean up any fees assigned + * to the wallet from a previous round, where it + * *was* the only game in town */ + insert_chain_fees_diff(db, last_id, txid, + AMOUNT_MSAT(0), + events[i]->currency, + events[i]->timestamp); continue; + } /* Add an extra msat onto plus_ones accts * so we don't lose any precision in From f767e417552de228f24260752419b154fad99c16 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1148/1530] bkpr: listbalances, skip the external account it's essentialy meaningless --- plugins/bkpr/bookkeeper.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index b0464f3e1c84..40388a7e7eba 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -207,6 +207,11 @@ static struct command_result *json_list_balances(struct command *cmd, " for account %s: %s", accts[i]->name, err); + /* Skip the external acct balance, it's effectively + * meaningless */ + if (streq(accts[i]->name, EXTERNAL_ACCT)) + continue; + /* Add it to the result data */ json_object_start(res, NULL); From 2385d8d6138598302d7e8471522972f2fd1d43e1 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1149/1530] bkpr: listbalances, rename 'account_id' => 'account' --- plugins/bkpr/bookkeeper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 40388a7e7eba..897066bb9f95 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -215,7 +215,7 @@ static struct command_result *json_list_balances(struct command *cmd, /* Add it to the result data */ json_object_start(res, NULL); - json_add_string(res, "account_id", accts[i]->name); + json_add_string(res, "account", accts[i]->name); json_array_start(res, "balances"); for (size_t j = 0; j < tal_count(balances); j++) { json_object_start(res, NULL); From 8827423556efae882f95224ada5d67f23ff014d1 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1150/1530] bkpr: account for pushed amounts and record the output_value We need the total output_value, and we can figure this out if we look at the remote amount also. We also need to account for the pushed/leased amount, as for leased channels this really messes with onchain fee calculations. We copy basically the events that lightningd emits for leased channels: an open event with the 'lease fee' (pushed?) amount credited to the side that made the lease/push; then an in-channel event to effectively push the pushed/leased amount over to the peer it was paid to. We run the journal entry info after this, so the journal snapshot will take the pushed amount into account when figuring out what the further missed in-channel payments were (if any) --- plugins/bkpr/bookkeeper.c | 56 ++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 897066bb9f95..5273a4a61434 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -263,21 +263,27 @@ static bool new_missed_channel_account(struct command *cmd, assert(chan_arr_tok->type == JSMN_ARRAY); json_for_each_arr(j, curr_chan, chan_arr_tok) { struct bitcoin_outpoint opt; - struct amount_msat amt; + struct amount_msat amt, remote_amt, push_amt, + push_credit, push_debit; char *opener, *chan_id; const char *err; enum mvt_tag *tags; + bool ok; err = json_scan(tmpctx, buf, curr_chan, "{channel_id:%," "funding_txid:%," "funding_outnum:%," - "funding:{local_msat:%}," + "funding:{local_msat:%," + "remote_msat:%," + "pushed_msat:%}," "opener:%}", JSON_SCAN_TAL(tmpctx, json_strdup, &chan_id), JSON_SCAN(json_to_txid, &opt.txid), JSON_SCAN(json_to_number, &opt.n), JSON_SCAN(json_to_msat, &amt), + JSON_SCAN(json_to_msat, &remote_amt), + JSON_SCAN(json_to_msat, &push_amt), JSON_SCAN_TAL(tmpctx, json_strdup, &opener)); if (err) plugin_err(cmd->plugin, @@ -289,10 +295,11 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev = tal(cmd, struct chain_event); chain_ev->tag = mvt_tag_str(CHANNEL_OPEN); - chain_ev->credit = amt; chain_ev->debit = AMOUNT_MSAT(0); - chain_ev->output_value = AMOUNT_MSAT(0); + ok = amount_msat_add(&chain_ev->output_value, amt, remote_amt); + assert(ok); chain_ev->currency = tal_strdup(chain_ev, currency); + chain_ev->origin_acct = NULL; /* 2s before the channel opened, minimum */ chain_ev->timestamp = timestamp - 2; chain_ev->blockheight = 0; @@ -302,13 +309,50 @@ static bool new_missed_channel_account(struct command *cmd, /* Update the account info too */ tags = tal_arr(chain_ev, enum mvt_tag, 1); - tags[1] = CHANNEL_OPEN; - if (streq(opener, "local")) + tags[0] = CHANNEL_OPEN; + + /* Leased/pushed channels have some extra work */ + if (streq(opener, "local")) { tal_arr_expand(&tags, OPENER); + ok = amount_msat_add(&amt, amt, push_amt); + push_credit = AMOUNT_MSAT(0); + push_debit = push_amt; + } else { + ok = amount_msat_sub(&amt, amt, push_amt); + push_credit = push_amt; + push_debit = AMOUNT_MSAT(0); + } + + /* We assume pushes are all leases, even + * though they might just be pushes */ + if (!amount_msat_zero(push_amt)) + tal_arr_expand(&tags, LEASED); + assert(ok); + chain_ev->credit = amt; db_begin_transaction(db); log_chain_event(db, acct, chain_ev); maybe_update_account(db, acct, chain_ev, tags); + + /* We log a channel event for the push amt */ + if (!amount_msat_zero(push_amt)) { + struct channel_event *chan_ev; + char *chan_tag; + + chan_tag = tal_fmt(tmpctx, "%s", + mvt_tag_str(LEASE_FEE)); + + chan_ev = new_channel_event(tmpctx, + chan_tag, + push_credit, + push_debit, + AMOUNT_MSAT(0), + currency, + NULL, 0, + timestamp - 1); + log_channel_event(db, acct, chan_ev); + } + db_commit_transaction(db); return true; } From fea33221d4f3d6cd0fd7f6e9b077150194280e68 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1151/1530] bkpr: once we get channel_opens, we might need to update the fee records Also note that we might have ignored/missed fees for the channel closed's spending txid, so we attempt to update those as well. Backfilling for missed events is a beast. --- plugins/bkpr/bookkeeper.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 5273a4a61434..88f7f406b701 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -242,6 +242,24 @@ struct new_account_info { char *currency; }; +static void try_update_open_fees(struct command *cmd, + struct account *acct) +{ + struct chain_event *ev; + char *err; + + assert(acct->closed_event_db_id); + ev = find_chain_event_by_id(cmd, db, *acct->closed_event_db_id); + assert(ev); + + err = maybe_update_onchain_fees(cmd, db, ev->spending_txid); + if (err) + plugin_err(cmd->plugin, + "failure updating chain fees:" + " %s", err); + +} + static bool new_missed_channel_account(struct command *cmd, const char *buf, const jsmntok_t *result, @@ -333,6 +351,14 @@ static bool new_missed_channel_account(struct command *cmd, db_begin_transaction(db); log_chain_event(db, acct, chain_ev); maybe_update_account(db, acct, chain_ev, tags); + maybe_update_onchain_fees(cmd, db, &opt.txid); + + /* We won't count the close's fees if we're + * *not* the opener, which we didn't know + * until now, so now try to update the + * fees for the close tx's spending_txid..*/ + if (acct->closed_event_db_id) + try_update_open_fees(cmd, acct); /* We log a channel event for the push amt */ if (!amount_msat_zero(push_amt)) { From b33bd0552422c1676773c5e058ef78fcdb5f5484 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1152/1530] bkpr: add an 'inspect' command to the bookkeeper Pass in an account id, get out a utxo chain of the channel open and close (and any other related htlc txs etc). Note that this prints all wallet deposits that occurred in any of the tx's that touched this channel. This is fine and expected for any tx that's not the open; when considerig the tx open event, the wallet deposit that's present is typically the change. If there were other channels opened in the same tx then the change won't match up exactly... --- plugins/bkpr/bookkeeper.c | 150 +++++++++++++++++++++++++ plugins/bkpr/recorder.c | 225 ++++++++++++++++++++++++++++++++++++++ plugins/bkpr/recorder.h | 31 ++++++ 3 files changed, 406 insertions(+) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 88f7f406b701..3da169e4153c 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -78,6 +78,149 @@ static void json_add_onchain_fee(struct json_stream *out, json_object_end(out); } +static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, + struct bitcoin_txid *txid) +{ + for (size_t i = 0; i < tal_count(sums); i++) { + if (bitcoin_txid_eq(txid, sums[i]->txid)) + return sums[i]; + } + return NULL; +} + +static struct command_result *json_inspect(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + struct account *acct; + const char *acct_name; + struct fee_sum **fee_sums; + struct txo_set **txos; + + /* Only available for channel accounts? */ + if (!param(cmd, buf, params, + p_opt("account", param_string, &acct_name), + NULL)) + return command_param_failed(); + + if (!acct_name) + return command_fail(cmd, PLUGIN_ERROR, + "Account not provided"); + + if (streq(acct_name, WALLET_ACCT) + || streq(acct_name, EXTERNAL_ACCT)) + return command_fail(cmd, PLUGIN_ERROR, + "`inspect` not supported for" + " non-channel accounts"); + + db_begin_transaction(db); + acct = find_account(cmd, db, acct_name); + db_commit_transaction(db); + + if (!acct) + return command_fail(cmd, PLUGIN_ERROR, + "Account %s not found", + acct_name); + + db_begin_transaction(db); + find_txo_chain(cmd, db, acct, &txos); + fee_sums = find_account_onchain_fees(cmd, db, acct); + db_commit_transaction(db); + + res = jsonrpc_stream_success(cmd); + json_array_start(res, "txs"); + for (size_t i = 0; i < tal_count(txos); i++) { + struct txo_set *set = txos[i]; + struct fee_sum *fee_sum; + + json_object_start(res, NULL); + json_add_txid(res, "txid", set->txid); + + /* annoyting, but we can only add the block height + * if we have a txo for it */ + for (size_t j = 0; j < tal_count(set->pairs); j++) { + if (set->pairs[j]->txo + && set->pairs[j]->txo->blockheight > 0) { + json_add_num(res, "blockheight", + set->pairs[j]->txo->blockheight); + break; + } + } + + fee_sum = find_sum_for_txid(fee_sums, set->txid); + if (fee_sum) + json_add_amount_msat_only(res, "fees_paid", + fee_sum->fees_paid); + else + json_add_amount_msat_only(res, "fees_paid", + AMOUNT_MSAT(0)); + + json_array_start(res, "outputs"); + for (size_t j = 0; j < tal_count(set->pairs); j++) { + struct txo_pair *pr = set->pairs[j]; + + /* Is this an event that belongs to this account? */ + if (pr->txo) { + if (pr->txo->origin_acct) { + if (!streq(pr->txo->origin_acct, acct->name)) + continue; + } else if (pr->txo->acct_db_id != acct->db_id + /* We make an exception for wallet events */ + && !streq(pr->txo->acct_name, WALLET_ACCT)) + continue; + } else if (pr->spend + && pr->spend->acct_db_id != acct->db_id) + continue; + + json_object_start(res, NULL); + if (set->pairs[j]->txo) { + struct chain_event *ev = set->pairs[j]->txo; + + json_add_string(res, "account", ev->acct_name); + json_add_num(res, "outnum", + ev->outpoint.n); + json_add_string(res, "output_tag", ev->tag); + json_add_amount_msat_only(res, "output_value", + ev->output_value); + json_add_amount_msat_only(res, "credit", + ev->credit); + json_add_string(res, "currency", ev->currency); + if (ev->origin_acct) + json_add_string(res, "originating_account", + ev->origin_acct); + } + if (set->pairs[j]->spend) { + struct chain_event *ev = set->pairs[j]->spend; + /* If we didn't already populate this info */ + if (!set->pairs[j]->txo) { + json_add_string(res, "account", + ev->acct_name); + json_add_num(res, "outnum", + ev->outpoint.n); + json_add_amount_msat_only(res, "output_value", + ev->output_value); + json_add_string(res, "currency", + ev->currency); + } + json_add_string(res, "spend_tag", ev->tag); + json_add_txid(res, "spending_txid", + ev->spending_txid); + json_add_amount_msat_only(res, "debit", ev->debit); + if (ev->payment_id) + json_add_sha256(res, "payment_id", + ev->payment_id); + } + json_object_end(res); + } + json_array_end(res); + json_object_end(res); + } + json_array_end(res); + + return command_finished(cmd, res); +} + /* Find all the events for this account, ordered by timestamp */ static struct command_result *json_list_account_events(struct command *cmd, const char *buf, @@ -1115,6 +1258,13 @@ static const struct plugin_command commands[] = { " no account specified) in {format}. Sorted by timestamp", json_list_account_events }, + { + "inspect", + "utilities", + "See the current on-chain graph of an {account}", + "Prints out the on-chain footprint of a given {account}.", + json_inspect + }, }; static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 929e2628a259..c03e4824aaa8 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -160,6 +160,231 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, return find_chain_events(ctx, take(stmt)); } +static struct chain_event **find_txos_for_tx(const tal_t *ctx, + struct db *db, + struct bitcoin_txid *txid) +{ + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("SELECT" + " e.id" + ", e.account_id" + ", a.name" + ", e.origin" + ", e.tag" + ", e.credit" + ", e.debit" + ", e.output_value" + ", e.currency" + ", e.timestamp" + ", e.blockheight" + ", e.utxo_txid" + ", e.outnum" + ", e.spending_txid" + ", e.payment_id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE e.utxo_txid = ?" + " ORDER BY " + " e.utxo_txid" + ", e.outnum" + ", e.spending_txid NULLS FIRST")); + + db_bind_txid(stmt, 0, txid); + return find_chain_events(ctx, take(stmt)); +} + +struct fee_sum **find_account_onchain_fees(const tal_t *ctx, + struct db *db, + struct account *acct) +{ + struct db_stmt *stmt; + struct fee_sum **sums; + stmt = db_prepare_v2(db, SQL("SELECT" + " txid" + ", CAST(SUM(credit) AS BIGINT) as credit" + ", CAST(SUM(debit) AS BIGINT) as debit" + " FROM onchain_fees" + " WHERE account_id = ?" + " GROUP BY txid" + " ORDER BY txid, update_count")); + + db_bind_u64(stmt, 0, acct->db_id); + db_query_prepared(stmt); + + sums = tal_arr(ctx, struct fee_sum *, 0); + while (db_step(stmt)) { + struct fee_sum *sum; + struct amount_msat amt; + bool ok; + + sum = tal(sums, struct fee_sum); + sum->txid = tal(sum, struct bitcoin_txid); + db_col_txid(stmt, "txid", sum->txid); + + db_col_amount_msat(stmt, "credit", &sum->fees_paid); + db_col_amount_msat(stmt, "debit", &amt); + ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, amt); + assert(ok); + tal_arr_expand(&sums, sum); + } + + return sums; +} + +static struct txo_pair *new_txo_pair(const tal_t *ctx) +{ + struct txo_pair *pr = tal(ctx, struct txo_pair); + pr->txo = NULL; + pr->spend = NULL; + return pr; +} + +static struct txo_set *find_txo_set(const tal_t *ctx, + struct db *db, + struct bitcoin_txid *txid, + u64 *acct_db_id, + bool *is_complete) +{ + struct txo_pair *pr; + struct chain_event **evs; + struct txo_set *txos = tal(ctx, struct txo_set); + + /* In some special cases (the opening tx), we only + * want the outputs that pertain to a given account, + * most other times we want all utxos, regardless of account */ + evs = find_txos_for_tx(ctx, db, txid); + txos->pairs = tal_arr(txos, struct txo_pair *, 0); + txos->txid = tal_dup(txos, struct bitcoin_txid, txid); + + pr = NULL; + + /* If there's nothing for this txid, we're missing data */ + if (is_complete) + *is_complete = tal_count(evs) > 0; + + for (size_t i = 0; i < tal_count(evs); i++) { + struct chain_event *ev = evs[i]; + + if (acct_db_id && ev->acct_db_id != *acct_db_id) + continue; + + if (ev->spending_txid) { + if (!pr) { + /* We're missing data!! */ + pr = new_txo_pair(txos->pairs); + if (is_complete) + *is_complete = false; + } else { + assert(pr->txo); + /* Make sure it's the same txo */ + assert(bitcoin_outpoint_eq(&pr->txo->outpoint, + &ev->outpoint)); + } + + pr->spend = tal_steal(pr, ev); + tal_arr_expand(&txos->pairs, pr); + pr = NULL; + } else { + /* We might not have a spend event + * for everything */ + if (pr) + tal_arr_expand(&txos->pairs, pr); + pr = new_txo_pair(txos->pairs); + pr->txo = tal_steal(pr, ev); + } + } + + /* Might have a single entry 'pr' left over */ + if (pr) + tal_arr_expand(&txos->pairs, pr); + + return txos; +} + +static bool is_channel_acct(struct chain_event *ev) +{ + return !streq(ev->acct_name, WALLET_ACCT) + && !streq(ev->acct_name, EXTERNAL_ACCT); +} + +static bool txid_in_list(struct bitcoin_txid **list, + struct bitcoin_txid *txid) +{ + for (size_t i = 0; i < tal_count(list); i++) { + if (bitcoin_txid_eq(list[i], txid)) + return true; + } + + return false; +} + +bool find_txo_chain(const tal_t *ctx, + struct db *db, + struct account *acct, + struct txo_set ***sets) +{ + struct bitcoin_txid **txids; + struct chain_event *open_ev; + bool is_complete = true; + u64 *start_acct_id = tal(NULL, u64); + + assert(acct->open_event_db_id); + open_ev = find_chain_event_by_id(ctx, db, + *acct->open_event_db_id); + + *sets = tal_arr(ctx, struct txo_set *, 0); + txids = tal_arr(ctx, struct bitcoin_txid *, 0); + tal_arr_expand(&txids, &open_ev->outpoint.txid); + + /* We only want to filter by the account for the very + * first utxo that we get the tree for, so we + * start w/ this acct id... */ + *start_acct_id = open_ev->acct_db_id; + + for (size_t i = 0; i < tal_count(txids); i++) { + struct txo_set *set; + bool set_complete; + + set = find_txo_set(ctx, db, txids[i], + start_acct_id, + &set_complete); + + /* After first use, we free the acct dbid ptr, + * which will pass in NULL and not filter by + * account for any subsequent txo_set hunt */ + if (start_acct_id) + start_acct_id = tal_free(start_acct_id); + + is_complete &= set_complete; + for (size_t j = 0; j < tal_count(set->pairs); j++) { + struct txo_pair *pr = set->pairs[j]; + + /* Has this been resolved? */ + if ((pr->txo + && is_channel_acct(pr->txo)) + && !pr->spend) + is_complete = false; + + /* wallet accts and zero-fee-htlc anchors + * might overlap txids */ + if (pr->spend + && pr->spend->spending_txid + && !txid_in_list(txids, pr->spend->spending_txid) + /* We dont trace utxos for non related accts */ + && pr->spend->acct_db_id == acct->db_id) { + tal_arr_expand(&txids, + pr->spend->spending_txid); + } + } + + tal_arr_expand(sets, set); + } + + return is_complete; +} + struct chain_event *find_chain_event_by_id(const tal_t *ctx, struct db *db, u64 event_db_id) diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 0d2cdfad48ec..e1468c76f334 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -22,6 +22,21 @@ struct acct_balance { struct amount_msat balance; }; +struct fee_sum { + struct bitcoin_txid *txid; + struct amount_msat fees_paid; +}; + +struct txo_pair { + struct chain_event *txo; + struct chain_event *spend; +}; + +struct txo_set { + struct bitcoin_txid *txid; + struct txo_pair **pairs; +}; + /* Get all accounts */ struct account **list_accounts(const tal_t *ctx, struct db *db); @@ -65,9 +80,25 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, struct db *db, u64 event_db_id); +/* Find the utxos for this account. + * + * Returns true if chain is complete: + * (all outputs terminate either to wallet or external) + */ +bool find_txo_chain(const tal_t *ctx, + struct db *db, + struct account *acct, + struct txo_set ***sets); + /* List all chain fees, for all accounts */ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db); +/* Returns a list of sums of the fees we've recorded for every txid + * for the given account */ +struct fee_sum **find_account_onchain_fees(const tal_t *ctx, + struct db *db, + struct account *acct); + /* Add the given account to the database */ void account_add(struct db *db, struct account *acct); /* Given an account name, find that account record */ From 7b6956e4f9af5e4281dd988458ccb6662107263a Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1153/1530] bkpr: annotate an account with the block at which it's been resolved Due to the way that onchain channel closes work, there is often a delay between when the funding output is spent and the channel is considered 'closed'. Once *every* downstream utxo of a channel has landed on chain, we annotate the account with the resolving blockheight. This gives us some insight into whether or not the chain fees etc of a channel are going to update further and allows for a natural marker to prune data (at a later date) --- plugins/bkpr/bookkeeper.c | 14 +++++++++---- plugins/bkpr/recorder.c | 44 +++++++++++++++++++++++++++++++++++++-- plugins/bkpr/recorder.h | 7 +++++++ 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 3da169e4153c..1e5fa696255d 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -756,7 +756,11 @@ listpeers_done(struct command *cmd, const char *buf, "Unable to find account %s in listpeers", info->acct->name); - /* FIXME: maybe mark channel as 'onchain_resolved' */ + /* Maybe mark acct as onchain resolved */ + db_begin_transaction(db); + if (info->acct->closed_event_db_id) + maybe_mark_account_onchain(db, info->acct); + db_commit_transaction(db); return notification_handled(cmd); } @@ -1078,9 +1082,11 @@ parse_and_log_chain_move(struct command *cmd, return command_still_pending(cmd); } - /* FIXME: maybe mark channel as 'onchain_resolved' */ - if (err) - plugin_err(cmd->plugin, err); + /* Maybe mark acct as onchain resolved */ + db_begin_transaction(db); + if (acct->closed_event_db_id) + maybe_mark_account_onchain(db, acct); + db_commit_transaction(db); return notification_handled(cmd);; } diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index c03e4824aaa8..dab6d477a5e5 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -334,7 +334,8 @@ bool find_txo_chain(const tal_t *ctx, open_ev = find_chain_event_by_id(ctx, db, *acct->open_event_db_id); - *sets = tal_arr(ctx, struct txo_set *, 0); + if (sets) + *sets = tal_arr(ctx, struct txo_set *, 0); txids = tal_arr(ctx, struct bitcoin_txid *, 0); tal_arr_expand(&txids, &open_ev->outpoint.txid); @@ -379,12 +380,51 @@ bool find_txo_chain(const tal_t *ctx, } } - tal_arr_expand(sets, set); + if (sets) + tal_arr_expand(sets, set); } return is_complete; } +void maybe_mark_account_onchain(struct db *db, struct account *acct) +{ + const u8 *ctx = tal(NULL, u8); + if (find_txo_chain(ctx, db, acct, NULL)) { + /* Ok now we find the max block height of the + * spending chain_events for this channel */ + bool ok; + + struct db_stmt *stmt = db_prepare_v2(db, + SQL("SELECT" + " blockheight" + " FROM chain_events" + " WHERE account_id = ?" + " AND spending_txid IS NOT NULL" + " ORDER BY blockheight DESC" + " LIMIT 1")); + + db_bind_u64(stmt, 0, acct->db_id); + db_query_prepared(stmt); + ok = db_step(stmt); + assert(ok); + + acct->onchain_resolved_block = db_col_int(stmt, "blockheight"); + tal_free(stmt); + + /* Ok, now we update the account with this blockheight */ + stmt = db_prepare_v2(db, SQL("UPDATE accounts SET" + " onchain_resolved_block = ?" + " WHERE" + " id = ?")); + db_bind_int(stmt, 0, acct->onchain_resolved_block); + db_bind_u64(stmt, 1, acct->db_id); + db_exec_prepared_v2(take(stmt)); + } + + tal_free(ctx); +} + struct chain_event *find_chain_event_by_id(const tal_t *ctx, struct db *db, u64 event_db_id) diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index e1468c76f334..0375ae6d791d 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -117,6 +117,13 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, struct bitcoin_txid *txid); +/* Have all the outputs for this account's close tx + * been resolved onchain? If so, update the account with the + * highest blockheight that has a resolving tx in it. + * + * The point of this is to allow us to prune data, eventually */ +void maybe_mark_account_onchain(struct db *db, struct account *acct); + /* Log a channel event */ void log_channel_event(struct db *db, const struct account *acct, From 7d5a0988db4cc0196ad6b6bab3eb69a9c2dfab21 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1154/1530] bkpr: save closed_count for account, when known When the closing channel event tells you how many outputs to expect, save it to the account so we can verify the account is fully closed/onchain. --- plugins/bkpr/account.c | 1 + plugins/bkpr/account.h | 3 +++ plugins/bkpr/bookkeeper.c | 19 ++++++++++++++++--- plugins/bkpr/db.c | 1 + plugins/bkpr/recorder.c | 16 ++++++++++++++-- plugins/bkpr/recorder.h | 3 ++- plugins/bkpr/test/run-recorder.c | 10 +++++++--- 7 files changed, 44 insertions(+), 9 deletions(-) diff --git a/plugins/bkpr/account.c b/plugins/bkpr/account.c index b734078ea8f9..61739462cfb0 100644 --- a/plugins/bkpr/account.c +++ b/plugins/bkpr/account.c @@ -19,6 +19,7 @@ struct account *new_account(const tal_t *ctx, a->onchain_resolved_block = 0; a->open_event_db_id = NULL; a->closed_event_db_id = NULL; + a->closed_count = 0; return a; } diff --git a/plugins/bkpr/account.h b/plugins/bkpr/account.h index 5f83090c8538..afce6e5f1f3b 100644 --- a/plugins/bkpr/account.h +++ b/plugins/bkpr/account.h @@ -34,6 +34,9 @@ struct account { /* db_id of chain event that closed this account */ u64 *closed_event_db_id; + + /* Number of outputs to expect on close */ + u32 closed_count; }; /* Get a new account */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 1e5fa696255d..6d74c2b7a21d 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -493,7 +493,7 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev->credit = amt; db_begin_transaction(db); log_chain_event(db, acct, chain_ev); - maybe_update_account(db, acct, chain_ev, tags); + maybe_update_account(db, acct, chain_ev, tags, 0); maybe_update_onchain_fees(cmd, db, &opt.txid); /* We won't count the close's fees if we're @@ -958,6 +958,7 @@ parse_and_log_chain_move(struct command *cmd, struct sha256 *payment_hash = tal(cmd, struct sha256); struct bitcoin_txid *spending_txid = tal(cmd, struct bitcoin_txid); struct account *acct; + u32 closed_count; const char *err; /* Fields we expect on *every* chain movement */ @@ -1011,8 +1012,20 @@ parse_and_log_chain_move(struct command *cmd, "{originating_account:%}}", JSON_SCAN_TAL(e, json_strdup, &e->origin_acct)); - if (err) + if (err) { e->origin_acct = NULL; + err = tal_free(err); + } + + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{output_count:%}}", + JSON_SCAN(json_to_number, &closed_count)); + + if (err) { + closed_count = 0; + err = tal_free(err); + } e->payment_id = tal_steal(e, payment_hash); @@ -1039,7 +1052,7 @@ parse_and_log_chain_move(struct command *cmd, /* This event *might* have implications for account; * update as necessary */ - maybe_update_account(db, acct, e, tags); + maybe_update_account(db, acct, e, tags, closed_count); /* Can we calculate any onchain fees now? */ err = maybe_update_onchain_fees(cmd, db, diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 8edda43c8d85..7a7f2aca2f3f 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -93,6 +93,7 @@ static struct migration db_migrations[] = { ");"), NULL}, {SQL("ALTER TABLE chain_events ADD origin TEXT;"), NULL}, + {SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL}, }; static bool db_migrate(struct plugin *p, struct db *db) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index dab6d477a5e5..2c51721842df 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -836,6 +836,8 @@ static struct account *stmt2account(const tal_t *ctx, struct db_stmt *stmt) } else a->closed_event_db_id = NULL; + a->closed_count = db_col_int(stmt, "closed_count"); + return a; } @@ -856,6 +858,7 @@ struct account *find_account(const tal_t *ctx, ", is_wallet" ", we_opened" ", leased" + ", closed_count" " FROM accounts" " WHERE name = ?")); @@ -921,6 +924,7 @@ struct account **list_accounts(const tal_t *ctx, struct db *db) ", is_wallet" ", we_opened" ", leased" + ", closed_count" " FROM accounts;")); db_query_prepared(stmt); @@ -966,7 +970,8 @@ void account_add(struct db *db, struct account *acct) void maybe_update_account(struct db *db, struct account *acct, struct chain_event *e, - const enum mvt_tag *tags) + const enum mvt_tag *tags, + u32 closed_count) { struct db_stmt *stmt; bool updated = false; @@ -1014,6 +1019,11 @@ void maybe_update_account(struct db *db, } } + if (closed_count > 0) { + updated = true; + acct->closed_count = closed_count; + } + /* Nothing new here */ if (!updated) return; @@ -1024,6 +1034,7 @@ void maybe_update_account(struct db *db, ", closed_event_id = ?" ", we_opened = ?" ", leased = ?" + ", closed_count = ?" " WHERE" " name = ?")); @@ -1039,8 +1050,9 @@ void maybe_update_account(struct db *db, db_bind_int(stmt, 2, acct->we_opened ? 1 : 0); db_bind_int(stmt, 3, acct->leased ? 1 : 0); + db_bind_int(stmt, 4, acct->closed_count); - db_bind_text(stmt, 4, acct->name); + db_bind_text(stmt, 5, acct->name); db_exec_prepared_v2(take(stmt)); } diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 0375ae6d791d..4989d5f12f79 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -110,7 +110,8 @@ struct account *find_account(const tal_t *ctx, void maybe_update_account(struct db *db, struct account *acct, struct chain_event *e, - const enum mvt_tag *tags); + const enum mvt_tag *tags, + u32 closed_count); /* Update our onchain fees now? */ char *maybe_update_onchain_fees(const tal_t *ctx, diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 8033d0507eb2..1693f089e491 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -247,6 +247,8 @@ static bool accountseq(struct account *a1, struct account *a2) if (a1->closed_event_db_id) CHECK(*a1->closed_event_db_id == *a2->closed_event_db_id); + CHECK(a1->closed_count == a2->closed_count); + return true; } @@ -1080,7 +1082,8 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) /* should not update the account info */ tags[0] = PUSHED; tags[1] = PENALTY; - maybe_update_account(db, acct, ev1, tags); + maybe_update_account(db, acct, ev1, tags, 0); + acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); /* channel_open -> open event db updated */ @@ -1088,17 +1091,18 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) CHECK(acct->open_event_db_id == NULL); tags[0] = CHANNEL_OPEN; tags[1] = LEASED; - maybe_update_account(db, acct, ev1, tags); + maybe_update_account(db, acct, ev1, tags, 2); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); CHECK(acct->leased); CHECK(acct->open_event_db_id != NULL); + CHECK(acct->closed_count == 2); tags[0] = CHANNEL_CLOSE; tags[1] = OPENER; CHECK(acct->closed_event_db_id == NULL); CHECK(!acct->we_opened); - maybe_update_account(db, acct, ev1, tags); + maybe_update_account(db, acct, ev1, tags, 0); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); CHECK(acct->closed_event_db_id != NULL); From 8f869ade3cc4cbbbcb1132601d24f4e1e4306cba Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1155/1530] bkpr: use chain_closed count to do mark account closed More robust than inspecting the returned utxo treeset -- if the tree is complete but missing outputs we'll get the wrong result --- plugins/bkpr/recorder.c | 43 ++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 2c51721842df..b00d353f6f54 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -390,19 +390,44 @@ bool find_txo_chain(const tal_t *ctx, void maybe_mark_account_onchain(struct db *db, struct account *acct) { const u8 *ctx = tal(NULL, u8); - if (find_txo_chain(ctx, db, acct, NULL)) { + struct txo_set **sets; + struct chain_event *close_ev; + struct db_stmt *stmt; + + assert(acct->closed_count > 0); + + close_ev = find_chain_event_by_id(ctx, db, + *acct->closed_event_db_id); + + if (find_txo_chain(ctx, db, acct, &sets)) { /* Ok now we find the max block height of the * spending chain_events for this channel */ bool ok; - struct db_stmt *stmt = db_prepare_v2(db, - SQL("SELECT" - " blockheight" - " FROM chain_events" - " WHERE account_id = ?" - " AND spending_txid IS NOT NULL" - " ORDER BY blockheight DESC" - " LIMIT 1")); + /* Have we accounted for all the outputs */ + ok = false; + for (size_t i = 0; i < tal_count(sets); i++) { + if (bitcoin_txid_eq(sets[i]->txid, + close_ev->spending_txid)) { + + ok = tal_count(sets[i]->pairs) + == acct->closed_count; + break; + } + } + + if (!ok) { + tal_free(ctx); + return; + } + + stmt = db_prepare_v2(db, SQL("SELECT" + " blockheight" + " FROM chain_events" + " WHERE account_id = ?" + " AND spending_txid IS NOT NULL" + " ORDER BY blockheight DESC" + " LIMIT 1")); db_bind_u64(stmt, 0, acct->db_id); db_query_prepared(stmt); From 5f41d9247ef14d188ff04c8865dae5148a3574ec Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:36 +0930 Subject: [PATCH 1156/1530] bkpr: properly account for onchain fees for channel closes onchain fees are weird at channel close because: - you may be missing an trimmed htlc (which went to fees) - the balance from close may have been rounded (msats cant land on chain) - the close might have been a past state and you've actually ended up with more money onchain than you had in the channel. wut This commit accounts for all of this appropriately, with some tests. channel_close.debit should equal onchain_fee.credit (for that txid) plus sum(chain_event.credit [wallet/channel_acct]). In the penalty case, channel_close.debit becomes channel_close.debit + penalty_adj.debit, i.e. channel-close.debit + (penalty_adj.debit) = onchain_fee.credit + sum(chain_event.credit [wallet/channel_acct]) --- plugins/bkpr/account_entry.c | 1 + plugins/bkpr/account_entry.h | 1 + plugins/bkpr/bookkeeper.c | 44 ++++-- plugins/bkpr/recorder.c | 223 ++++++++++++++++++++++++----- plugins/bkpr/recorder.h | 11 ++ plugins/bkpr/test/run-bkpr_db.c | 19 +++ plugins/bkpr/test/run-recorder.c | 236 +++++++++++++++++++++++++------ tests/test_closing.py | 101 +++++++++++++ 8 files changed, 546 insertions(+), 90 deletions(-) diff --git a/plugins/bkpr/account_entry.c b/plugins/bkpr/account_entry.c index 10cd29a7581e..7179f761fc1f 100644 --- a/plugins/bkpr/account_entry.c +++ b/plugins/bkpr/account_entry.c @@ -6,6 +6,7 @@ static const char *tags[] = { "journal_entry", + "penalty_adj", }; const char *account_entry_tag_str(enum account_entry_tag tag) diff --git a/plugins/bkpr/account_entry.h b/plugins/bkpr/account_entry.h index d0e608ba8aad..fed529847740 100644 --- a/plugins/bkpr/account_entry.h +++ b/plugins/bkpr/account_entry.h @@ -5,6 +5,7 @@ #define NUM_ACCOUNT_ENTRY_TAGS (JOURNAL_ENTRY + 1) enum account_entry_tag { JOURNAL_ENTRY = 0, + PENALTY_ADJ = 1, }; /* Convert an enum into a string */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 6d74c2b7a21d..afe7b47171de 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -701,6 +701,35 @@ listpeers_multi_done(struct command *cmd, return notification_handled(cmd); } +static char *do_account_close_checks(const tal_t *ctx, + struct chain_event *e, + struct account *acct) +{ + struct account *closed_acct; + + db_begin_transaction(db); + + /* If is an external acct event, might be close channel related */ + if (!is_channel_account(acct) && !e->spending_txid) + closed_acct = find_close_account(ctx, db, &e->outpoint.txid); + else + closed_acct = acct; + + if (closed_acct && closed_acct->closed_event_db_id) { + maybe_mark_account_onchain(db, closed_acct); + if (closed_acct->onchain_resolved_block > 0) { + char *err; + err = update_channel_onchain_fees(ctx, db, closed_acct); + if (err) + return err; + } + } + + db_commit_transaction(db); + + return NULL; +} + struct event_info { struct chain_event *ev; struct account *acct; @@ -757,10 +786,10 @@ listpeers_done(struct command *cmd, const char *buf, info->acct->name); /* Maybe mark acct as onchain resolved */ - db_begin_transaction(db); - if (info->acct->closed_event_db_id) - maybe_mark_account_onchain(db, info->acct); - db_commit_transaction(db); + err = do_account_close_checks(cmd, info->ev, info->acct); + if (err) + plugin_err(cmd->plugin, err); + return notification_handled(cmd); } @@ -1096,10 +1125,9 @@ parse_and_log_chain_move(struct command *cmd, } /* Maybe mark acct as onchain resolved */ - db_begin_transaction(db); - if (acct->closed_event_db_id) - maybe_mark_account_onchain(db, acct); - db_commit_transaction(db); + err = do_account_close_checks(cmd, e, acct); + if (err) + plugin_err(cmd->plugin, err); return notification_handled(cmd);; } diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index b00d353f6f54..f5bbc4bbed84 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -11,11 +11,13 @@ #include #include #include +#include #include #include #include #include + static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt) { struct chain_event *e = tal(ctx, struct chain_event); @@ -387,6 +389,37 @@ bool find_txo_chain(const tal_t *ctx, return is_complete; } +struct account *find_close_account(const tal_t *ctx, + struct db *db, + struct bitcoin_txid *txid) +{ + struct db_stmt *stmt; + struct account *close_acct; + char *acct_name; + + stmt = db_prepare_v2(db, SQL("SELECT" + " a.name" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE " + " e.tag = ?" + " AND e.spending_txid = ?")); + + db_bind_text(stmt, 0, mvt_tag_str(CHANNEL_CLOSE)); + db_bind_txid(stmt, 1, txid); + db_query_prepared(stmt); + + if (db_step(stmt)) { + acct_name = db_col_strdup(stmt, stmt, "a.name"); + close_acct = find_account(ctx, db, acct_name); + } else + close_acct = NULL; + + tal_free(stmt); + return close_acct; +} + void maybe_mark_account_onchain(struct db *db, struct account *acct) { const u8 *ctx = tal(NULL, u8); @@ -1255,6 +1288,148 @@ static void insert_chain_fees_diff(struct db *db, db_exec_prepared_v2(take(stmt)); } +char *update_channel_onchain_fees(const tal_t *ctx, + struct db *db, + struct account *acct) +{ + struct chain_event *close_ev, **events; + struct amount_msat onchain_amt; + + assert(acct->onchain_resolved_block); + close_ev = find_chain_event_by_id(ctx, db, + *acct->closed_event_db_id); + events = find_chain_events_bytxid(ctx, db, + close_ev->spending_txid); + + /* Starting balance is close-ev's debit amount */ + onchain_amt = AMOUNT_MSAT(0); + for (size_t i = 0; i < tal_count(events); i++) { + struct chain_event *ev = events[i]; + + /* Ignore: + - htlc_fufill (to me) + - anchors (already exlc from output) + - to_external (if !htlc_fulfill) + */ + if (is_channel_acct(ev) + && streq("htlc_fulfill", ev->tag)) + continue; + + if (streq("anchor", ev->tag)) + continue; + + /* Ignore stuff it's paid to + * the peer's account (external), + * except for fulfilled htlcs (which originated + * in our balance) */ + if (streq(ev->acct_name, EXTERNAL_ACCT) + && !streq("htlc_fulfill", ev->tag)) + continue; + + /* anything else we count? */ + if (!amount_msat_add(&onchain_amt, onchain_amt, + ev->credit)) + return tal_fmt(ctx, "Unable to add" + "onchain + %s's credit", + ev->tag); + } + + /* Was this an 'old state' tx, where we ended up + * with more sats than we had on record? */ + if (amount_msat_greater(onchain_amt, close_ev->debit)) { + struct channel_event *ev; + struct amount_msat diff; + + if (!amount_msat_sub(&diff, onchain_amt, + close_ev->debit)) + return tal_fmt(ctx, "Unable to sub" + "close debit from onchain_amt"); + /* Add in/out journal entries for it */ + ev = new_channel_event(ctx, + tal_fmt(tmpctx, "%s", + account_entry_tag_str(PENALTY_ADJ)), + diff, + AMOUNT_MSAT(0), + AMOUNT_MSAT(0), + close_ev->currency, + NULL, 0, + close_ev->timestamp); + log_channel_event(db, acct, ev); + ev = new_channel_event(ctx, + tal_fmt(tmpctx, "%s", + account_entry_tag_str(PENALTY_ADJ)), + AMOUNT_MSAT(0), + diff, + AMOUNT_MSAT(0), + close_ev->currency, + NULL, 0, + close_ev->timestamp); + log_channel_event(db, acct, ev); + } else { + struct amount_msat fees; + if (!amount_msat_sub(&fees, close_ev->debit, + onchain_amt)) + return tal_fmt(ctx, "Unable to sub" + "onchain sum from %s", + close_ev->tag); + + insert_chain_fees_diff(db, acct->db_id, + close_ev->spending_txid, + fees, close_ev->currency, + close_ev->timestamp); + } + + return NULL; +} + +static char *is_closed_channel_txid(const tal_t *ctx, struct db *db, + struct chain_event *ev, + struct bitcoin_txid *txid, + bool *is_channel_close_tx) +{ + struct account *acct; + struct chain_event *closed; + u8 *inner_ctx = tal(NULL, u8); + + /* Figure out if this is a channel close tx */ + acct = find_account(inner_ctx, db, ev->acct_name); + assert(acct); + + /* There's a separate process for figuring out + * our onchain fees for channel closures */ + if (!acct->closed_event_db_id) { + *is_channel_close_tx = false; + tal_free(inner_ctx); + return NULL; + } + + /* is the closed utxo the same as the one + * we're trying to find fees for now */ + closed = find_chain_event_by_id(inner_ctx, db, + *acct->closed_event_db_id); + if (!closed) { + *is_channel_close_tx = false; + tal_free(inner_ctx); + return tal_fmt(ctx, "Unable to find" + " db record (chain_evt)" + " with id %"PRIu64, + *acct->closed_event_db_id); + } + + if (!closed->spending_txid) { + *is_channel_close_tx = false; + tal_free(inner_ctx); + return tal_fmt(ctx, "Marked a closing" + " event that's not" + " actually a spend"); + } + + *is_channel_close_tx = + bitcoin_txid_eq(txid, closed->spending_txid); + tal_free(inner_ctx); + return NULL; +} + char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, struct bitcoin_txid *txid) { @@ -1278,40 +1453,19 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, goto finished; for (size_t i = 0; i < tal_count(events); i++) { + bool is_channel_close_tx; + err = is_closed_channel_txid(ctx, db, + events[i], txid, + &is_channel_close_tx); + + if (err) + goto finished; + + /* We skip channel close txs here! */ + if (is_channel_close_tx) + goto finished; + if (events[i]->spending_txid) { - struct account *acct; - /* Figure out if this is a channel close - * that we're not the opener for */ - acct = find_account(inner_ctx, db, - events[i]->acct_name); - assert(acct); - - /* If any of the spending_txid accounts are - * close accounts and we're not the opener, - * we end things */ - if (acct->closed_event_db_id && !acct->we_opened) { - struct chain_event *closed; - /* is the closed utxo the same as the one - * we're trying to find fees for now */ - closed = find_chain_event_by_id(inner_ctx, - db, *acct->closed_event_db_id); - if (!closed) { - err = tal_fmt(ctx, "Unable to find" - " db record (chain_evt)" - " with id %"PRIu64, - *acct->closed_event_db_id); - goto finished; - } - if (!closed->spending_txid) { - err = tal_fmt(ctx, "Marked a closing" - " event that's not" - " actually a spend"); - goto finished; - } - - if (bitcoin_txid_eq(txid, closed->spending_txid)) - goto finished; - } if (!amount_msat_add(&withdraw_msat, withdraw_msat, events[i]->debit)) { err = tal_fmt(ctx, "Overflow adding withdrawal debits for" @@ -1366,9 +1520,6 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, if (amount_msat_less(withdraw_msat, deposit_msat)) goto finished; - /* At this point, we have no way to know we've gotten all the data. - * But that's what the 'onchain_resolved_block' marker on - * accounts is for */ if (!amount_msat_sub(&fees_msat, withdraw_msat, deposit_msat)) { err = tal_fmt(ctx, "Err subtracting withdraw %s from deposit %s" " for txid %s", diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 4989d5f12f79..5eb623a6c732 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -106,6 +106,12 @@ struct account *find_account(const tal_t *ctx, struct db *db, const char *name); +/* Find the account that was closed by this txid. + * Returns NULL if none */ +struct account *find_close_account(const tal_t *ctx, + struct db *db, + struct bitcoin_txid *txid); + /* Some events update account information */ void maybe_update_account(struct db *db, struct account *acct, @@ -118,6 +124,11 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, struct bitcoin_txid *txid); +/* We calculate onchain fees for channel closes a bit different */ +char *update_channel_onchain_fees(const tal_t *ctx, + struct db *db, + struct account *acct); + /* Have all the outputs for this account's close tx * been resolved onchain? If so, update the account with the * highest blockheight that has a resolving tx in it. diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index 7bf7baad71b1..cab5ff9fb9a0 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -29,11 +29,16 @@ void db_fatal(const char *fmt, ...) #include #include #include +#include +#include #include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for account_entry_tag_str */ +const char *account_entry_tag_str(enum account_entry_tag tag UNNEEDED) +{ fprintf(stderr, "account_entry_tag_str called!\n"); abort(); } /* Generated stub for daemon_maybe_debug */ void daemon_maybe_debug(char *argv[]) { fprintf(stderr, "daemon_maybe_debug called!\n"); abort(); } @@ -172,6 +177,20 @@ enum htlc_state last_fee_state(enum side opener UNNEEDED) /* Generated stub for log_level_name */ const char *log_level_name(enum log_level level UNNEEDED) { fprintf(stderr, "log_level_name called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for new_channel_event */ +struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED, + const char *tag UNNEEDED, + struct amount_msat credit UNNEEDED, + struct amount_msat debit UNNEEDED, + struct amount_msat fees UNNEEDED, + const char *currency UNNEEDED, + struct sha256 *payment_id STEALS UNNEEDED, + u32 part_id UNNEEDED, + u64 timestamp UNNEEDED) +{ fprintf(stderr, "new_channel_event called!\n"); abort(); } /* Generated stub for toks_alloc */ jsmntok_t *toks_alloc(const tal_t *ctx UNNEEDED) { fprintf(stderr, "toks_alloc called!\n"); abort(); } diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 1693f089e491..8480d528851d 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,9 @@ void db_fatal(const char *fmt, ...) /* AUTOGENERATED MOCKS START */ +/* Generated stub for account_entry_tag_str */ +const char *account_entry_tag_str(enum account_entry_tag tag UNNEEDED) +{ fprintf(stderr, "account_entry_tag_str called!\n"); abort(); } /* Generated stub for daemon_maybe_debug */ void daemon_maybe_debug(char *argv[]) { fprintf(stderr, "daemon_maybe_debug called!\n"); abort(); } @@ -179,6 +183,20 @@ enum htlc_state last_fee_state(enum side opener UNNEEDED) /* Generated stub for log_level_name */ const char *log_level_name(enum log_level level UNNEEDED) { fprintf(stderr, "log_level_name called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for new_channel_event */ +struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED, + const char *tag UNNEEDED, + struct amount_msat credit UNNEEDED, + struct amount_msat debit UNNEEDED, + struct amount_msat fees UNNEEDED, + const char *currency UNNEEDED, + struct sha256 *payment_id STEALS UNNEEDED, + u32 part_id UNNEEDED, + u64 timestamp UNNEEDED) +{ fprintf(stderr, "new_channel_event called!\n"); abort(); } /* Generated stub for toks_alloc */ jsmntok_t *toks_alloc(const tal_t *ctx UNNEEDED) { fprintf(stderr, "toks_alloc called!\n"); abort(); } @@ -322,6 +340,8 @@ static struct chain_event *make_chain_event(const tal_t *ctx, char *tag, struct amount_msat credit, struct amount_msat debit, + struct amount_msat output_val, + u32 blockheight, char outpoint_char, u32 outnum, /* Note that '*' is magic */ @@ -336,10 +356,10 @@ static struct chain_event *make_chain_event(const tal_t *ctx, ev->origin_acct = NULL; ev->credit = credit; ev->debit = debit; - ev->output_value = AMOUNT_MSAT(1000); + ev->output_value = output_val; ev->currency = "btc"; ev->timestamp = 1919191; - ev->blockheight = 1919191; + ev->blockheight = blockheight; memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid)); ev->outpoint.n = outnum; @@ -362,6 +382,7 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) struct account *wal_acct, *ext_acct; struct bitcoin_txid txid; struct onchain_fee **ofs; + u32 blockheight = 100000; memset(&node_id, 2, sizeof(struct node_id)); memset(&peer_id, 3, sizeof(struct node_id)); @@ -387,6 +408,8 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(1000), + AMOUNT_MSAT(1000), + blockheight, 'X', 0, '1')); maybe_update_onchain_fees(ctx, db, &txid); @@ -394,6 +417,8 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "deposit", AMOUNT_MSAT(200), AMOUNT_MSAT(0), + AMOUNT_MSAT(200), + blockheight, '1', 1, '*')); maybe_update_onchain_fees(ctx, db, &txid); @@ -401,6 +426,8 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "deposit", AMOUNT_MSAT(700), AMOUNT_MSAT(0), + AMOUNT_MSAT(700), + blockheight, '1', 0, '*')); maybe_update_onchain_fees(ctx, db, &txid); db_commit_transaction(db); @@ -432,9 +459,16 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) struct account *acct, *wal_acct, *ext_acct; struct onchain_fee **ofs, **ofs1; struct bitcoin_txid txid; + struct chain_event *ev; + enum mvt_tag *tags; + u32 close_output_count; + u32 blockheight = 100000; + char *err; memset(&node_id, 2, sizeof(struct node_id)); memset(&peer_id, 3, sizeof(struct node_id)); + /* to_us, to_them, 1 htlc, 2 anchors */ + close_output_count = 5; wal_acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); @@ -450,47 +484,88 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) /* Close a channel */ /* tag utxo_id vout txid debits credits acct_id * close XXXX 0 1111 1000 wallet - * delay 1111 1 2222 200 chan-1 - * htlc_tx 1111 2 3333 600 chan-1 - * to_them 1111 3 external - * to_wall 3333 0 500 wallet - * to_wall 2222 0 150 wallet + * delay 1111 1 200 chan-1 + * anchor 1111 0 30 chan-1 + * anchor 1111 4 30 external + * to_wall 1111 1 2222 200 chan-1 + * htlc_tim1111 2 600 chan-1 + * htlc_tim1111 2 3333 600 chan-1 + * to_them 1111 3 300 external + * deposit 2222 0 150 chan-1 + * htlc_tx 3333 0 450 chan-1 + * to_wall 3333 0 4444 450 chan-1 + * deposit 4444 0 350 wallet */ + tags = tal_arr(ctx, enum mvt_tag, 1); db_begin_transaction(db); - log_chain_event(db, wal_acct, - make_chain_event(ctx, "close", - AMOUNT_MSAT(0), - AMOUNT_MSAT(1000), - 'X', 0, '1')); + ev = make_chain_event(ctx, "channel_open", + AMOUNT_MSAT(1000), + AMOUNT_MSAT(0), + AMOUNT_MSAT(1660), + blockheight, + 'X', 0, '*'); + log_chain_event(db, acct, ev); + tags[0] = CHANNEL_OPEN; + maybe_update_account(db, acct, ev, tags, 0); + + ev = make_chain_event(ctx, "channel_close", + AMOUNT_MSAT(0), + AMOUNT_MSAT(1000), + AMOUNT_MSAT(1660), + blockheight, + 'X', 0, '1'); + log_chain_event(db, acct, ev); + + /* Update the account to have the right info! */ + tags[0] = CHANNEL_CLOSE; + maybe_update_account(db, acct, ev, tags, close_output_count); log_chain_event(db, acct, make_chain_event(ctx, "delayed_to_us", AMOUNT_MSAT(200), AMOUNT_MSAT(0), + AMOUNT_MSAT(200), + blockheight, '1', 1, '*')); memset(&txid, '1', sizeof(struct bitcoin_txid)); maybe_update_onchain_fees(ctx, db, &txid); - /* Should be one fees now */ + log_chain_event(db, wal_acct, + make_chain_event(ctx, "anchor", + AMOUNT_MSAT(30), + AMOUNT_MSAT(0), + AMOUNT_MSAT(30), + blockheight, + '1', 0, '*')); + log_chain_event(db, ext_acct, + make_chain_event(ctx, "anchor", + AMOUNT_MSAT(30), + AMOUNT_MSAT(0), + AMOUNT_MSAT(30), + blockheight, + '1', 4, '*')); + memset(&txid, '1', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + + /* Should be no fees yet */ ofs = list_chain_fees(ctx, db); CHECK_MSG(!db_err, db_err); - - CHECK(tal_count(ofs) == 1); - CHECK(ofs[0]->acct_db_id == acct->db_id); - CHECK(amount_msat_eq(ofs[0]->credit, AMOUNT_MSAT(800))); - CHECK(amount_msat_zero(ofs[0]->debit)); - CHECK(ofs[0]->update_count == 1); + CHECK(tal_count(ofs) == 0); log_chain_event(db, acct, - make_chain_event(ctx, "htlc_tx", + make_chain_event(ctx, "htlc_timeout", AMOUNT_MSAT(600), AMOUNT_MSAT(0), + AMOUNT_MSAT(600), + blockheight, '1', 2, '*')); log_chain_event(db, ext_acct, make_chain_event(ctx, "to_them", + AMOUNT_MSAT(300), AMOUNT_MSAT(0), - AMOUNT_MSAT(0), + AMOUNT_MSAT(300), + blockheight, '1', 3, '*')); memset(&txid, '1', sizeof(struct bitcoin_txid)); @@ -501,22 +576,30 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) /* txid 2222 */ db_begin_transaction(db); log_chain_event(db, acct, - make_chain_event(ctx, "withdraw", + make_chain_event(ctx, "to_wallet", AMOUNT_MSAT(0), AMOUNT_MSAT(200), + AMOUNT_MSAT(200), + blockheight + 1, '1', 1, '2')); log_chain_event(db, wal_acct, - make_chain_event(ctx, "to_wallet", + make_chain_event(ctx, "deposit", AMOUNT_MSAT(150), AMOUNT_MSAT(0), + AMOUNT_MSAT(150), + blockheight + 1, '2', 0, '*')); memset(&txid, '2', sizeof(struct bitcoin_txid)); maybe_update_onchain_fees(ctx, db, &txid); + CHECK(acct->onchain_resolved_block == 0); + + maybe_mark_account_onchain(db, acct); + CHECK(acct->onchain_resolved_block == 0); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - /* Expect: 3 onchain fee records, all for chan-1 */ + /* Expect: 1 onchain fee records, all for chan-1 */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); ofs1 = account_onchain_fees(ctx, db, acct); @@ -524,27 +607,59 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); CHECK(tal_count(ofs) == tal_count(ofs1)); - CHECK(tal_count(ofs) == 3); + CHECK(tal_count(ofs) == 1); - /* txid 3333 */ + /* txid 4444 */ db_begin_transaction(db); + log_chain_event(db, wal_acct, + make_chain_event(ctx, "deposit", + AMOUNT_MSAT(350), + AMOUNT_MSAT(0), + AMOUNT_MSAT(350), + blockheight + 2, + '4', 0, '*')); + + memset(&txid, '4', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + + /* txid 3333 */ log_chain_event(db, acct, - make_chain_event(ctx, "htlc_tx", + make_chain_event(ctx, "htlc_timeout", AMOUNT_MSAT(0), AMOUNT_MSAT(600), + AMOUNT_MSAT(600), + blockheight + 2, '1', 2, '3')); - log_chain_event(db, wal_acct, - make_chain_event(ctx, "to_wallet", - AMOUNT_MSAT(500), + maybe_mark_account_onchain(db, acct); + CHECK(acct->onchain_resolved_block == 0); + + log_chain_event(db, acct, + make_chain_event(ctx, "htlc_tx", + AMOUNT_MSAT(450), AMOUNT_MSAT(0), + AMOUNT_MSAT(450), + blockheight + 2, '3', 0, '*')); + memset(&txid, '3', sizeof(struct bitcoin_txid)); maybe_update_onchain_fees(ctx, db, &txid); + + log_chain_event(db, acct, + make_chain_event(ctx, "to_wallet", + AMOUNT_MSAT(0), + AMOUNT_MSAT(450), + AMOUNT_MSAT(450), + blockheight + 2, + '3', 0, '4')); + + memset(&txid, '4', sizeof(struct bitcoin_txid)); + maybe_update_onchain_fees(ctx, db, &txid); + db_commit_transaction(db); CHECK_MSG(!db_err, db_err); - /* Expect: 3 onchain fee records, all for chan-1 */ + /* Expect: onchain fee records for tx except channel close */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); ofs1 = account_onchain_fees(ctx, db, acct); @@ -552,31 +667,35 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) CHECK_MSG(!db_err, db_err); CHECK(tal_count(ofs) == tal_count(ofs1)); - CHECK(tal_count(ofs) == 4); + CHECK(tal_count(ofs) == 3); + + /* Now we update the channel's onchain fees */ + CHECK(acct->onchain_resolved_block == 0); + db_begin_transaction(db); + maybe_mark_account_onchain(db, acct); + CHECK(acct->onchain_resolved_block == blockheight + 2); + err = update_channel_onchain_fees(ctx, db, acct); + CHECK_MSG(!err, err); + ofs = account_onchain_fees(ctx, db, acct); + db_commit_transaction(db); /* Expect: fees as follows * - * chan-1, 1111, 800,-600 - * chan-1, 3333, 100 + * chan-1, 1111, 200 (anchor outs ignored) * chan-1, 2222, 50 + * chan-1, 3333, 150 + * chan-1, 4444, 100 */ + CHECK(tal_count(ofs) == 4); for (size_t i = 0; i < tal_count(ofs); i++) { CHECK(ofs[i]->acct_db_id == acct->db_id); CHECK(streq(ofs[i]->currency, "btc")); memset(&txid, '1', sizeof(struct bitcoin_txid)); if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { - CHECK(ofs[i]->update_count == 1 - || ofs[i]->update_count == 2); - - if (ofs[i]->update_count == 1) { - CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(800))); - CHECK(amount_msat_zero(ofs[i]->debit)); - - } else { - CHECK(amount_msat_eq(ofs[i]->debit, AMOUNT_MSAT(600))); - CHECK(amount_msat_zero(ofs[i]->credit)); - } + CHECK(ofs[i]->update_count == 1); + CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(200))); + CHECK(amount_msat_zero(ofs[i]->debit)); continue; } memset(&txid, '2', sizeof(struct bitcoin_txid)); @@ -587,6 +706,13 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) continue; } memset(&txid, '3', sizeof(struct bitcoin_txid)); + if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { + CHECK(ofs[i]->update_count == 1); + CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(150))); + CHECK(amount_msat_zero(ofs[i]->debit)); + continue; + } + memset(&txid, '4', sizeof(struct bitcoin_txid)); if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) { CHECK(ofs[i]->update_count == 1); CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(100))); @@ -607,6 +733,7 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) struct account *acct, *acct2, *wal_acct, *ext_acct; struct bitcoin_txid txid; struct onchain_fee **ofs; + u32 blockheight = 100000; memset(&node_id, 2, sizeof(struct node_id)); memset(&peer_id, 3, sizeof(struct node_id)); @@ -641,17 +768,23 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(1000), + AMOUNT_MSAT(1000), + blockheight, 'X', 0, 'A')); log_chain_event(db, wal_acct, make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(3001), + AMOUNT_MSAT(3001), + blockheight, 'Y', 0, 'A')); log_chain_event(db, acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(500), AMOUNT_MSAT(0), + AMOUNT_MSAT(500), + blockheight, 'A', 0, '*')); maybe_update_onchain_fees(ctx, db, &txid); @@ -659,6 +792,8 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "deposit", AMOUNT_MSAT(1000), AMOUNT_MSAT(0), + AMOUNT_MSAT(1000), + blockheight, 'A', 1, '*')); maybe_update_onchain_fees(ctx, db, &txid); @@ -666,6 +801,8 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "deposit", AMOUNT_MSAT(2200), AMOUNT_MSAT(0), + AMOUNT_MSAT(2200), + blockheight, 'A', 2, '*')); maybe_update_onchain_fees(ctx, db, &txid); @@ -943,6 +1080,8 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "one", AMOUNT_MSAT(1000), AMOUNT_MSAT(0), + AMOUNT_MSAT(1000), + 1019, 'A', 1, '*')); /* -999btc */ @@ -950,6 +1089,8 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) make_chain_event(ctx, "two", AMOUNT_MSAT(0), AMOUNT_MSAT(999), + AMOUNT_MSAT(999), + 1020, 'A', 2, '*')); /* -440btc */ @@ -967,7 +1108,9 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) 'D')); /* +5000chf */ - ev1 = make_chain_event(ctx, "two", AMOUNT_MSAT(5000), AMOUNT_MSAT(0), + ev1 = make_chain_event(ctx, "two", + AMOUNT_MSAT(5000), AMOUNT_MSAT(0), + AMOUNT_MSAT(5000), 1999, 'A', 3, '*'); ev1->currency = "chf"; log_chain_event(db, acct, ev1); @@ -993,6 +1136,7 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) /* -5001chf */ ev1 = make_chain_event(ctx, "two", AMOUNT_MSAT(0), AMOUNT_MSAT(5001), + AMOUNT_MSAT(5001), 2020, 'A', 4, '*'); ev1->currency = "chf"; log_chain_event(db, acct, ev1); diff --git a/tests/test_closing.py b/tests/test_closing.py index 3b06066671f2..e58b23e08021 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -479,6 +479,107 @@ def test_closing_negotiation_step_700sat(node_factory, bitcoind, chainparams): closing_negotiation_step(node_factory, bitcoind, chainparams, opts) +# FIXME: move to bookkeeper tests +@pytest.mark.developer("dev-ignore-htlcs") +def test_closing_trimmed_htlcs(node_factory, bitcoind, executor): + l1, l2 = node_factory.line_graph(2) + + # give l2 an output!? + l1.pay(l2, 11000000) + + l1.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True) + # This will get stuck due to l3 ignoring htlcs + executor.submit(l2.pay, l1, 100001) + l1.daemon.wait_for_log('their htlc 0 dev_ignore_htlcs') + + l1.rpc.dev_fail(l2.info['id']) + l1.wait_for_channel_onchain(l2.info['id']) + + bitcoind.generate_block(1) + l1.daemon.wait_for_log(' to ONCHAIN') + l2.daemon.wait_for_log(' to ONCHAIN') + + bitcoind.generate_block(5) + l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') + bitcoind.generate_block(20) + l1.daemon.wait_for_log('All outputs resolved') + + def _find_tags(evs, tag): + return [e for e in evs if e['tag'] == tag] + + def _find_first_tag(evs, tag): + ev = _find_tags(evs, tag) + assert len(ev) > 0 + return ev[0] + + evs = l1.rpc.listaccountevents()['events'] + close = _find_first_tag(evs, 'channel_close') + delayed_to = _find_first_tag(evs, 'delayed_to_us') + + # find the chain fee entry for the channel close + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) + + # l2's fees should equal the trimmed htlc out + evs = l2.rpc.listaccountevents()['events'] + close = _find_first_tag(evs, 'channel_close') + deposit = _find_first_tag(evs, 'deposit') + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + # sent htlc was too small, we lose it, rounded up to nearest sat + assert close_fee[0]['credit'] == '101000msat' + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) + + +# FIXME: move to bookkeeper tests +def test_closing_subsat_htlcs(node_factory, bitcoind, chainparams): + """Test closing balances when HTLCs are: sub 1-satoshi""" + l1, l2 = node_factory.line_graph(2) + + l1.pay(l2, 111) + l1.pay(l2, 222) + l1.pay(l2, 4000000) + + l2.stop() + l1.rpc.close(l2.info['id'], 1) + bitcoind.generate_block(5) + l2.start() + sync_blockheight(bitcoind, [l1]) + l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') + bitcoind.generate_block(80) + + def _find_tags(evs, tag): + return [e for e in evs if e['tag'] == tag] + + def _find_first_tag(evs, tag): + ev = _find_tags(evs, tag) + assert len(ev) > 0 + return ev[0] + + sync_blockheight(bitcoind, [l1, l2]) + evs = l1.rpc.listaccountevents()['events'] + # check that closing equals onchain deposits + fees + close = _find_first_tag(evs, 'channel_close') + delayed_to = _find_first_tag(evs, 'delayed_to_us') + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) + + evs = l2.rpc.listaccountevents()['events'] + close = _find_first_tag(evs, 'channel_close') + deposit = _find_first_tag(evs, 'deposit') + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + # too small to fit, we lose them as miner fees + assert close_fee[0]['credit'] == '333msat' + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) + + @pytest.mark.developer("needs dev-disable-commit-after") def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): """Test penalty transaction with an incoming HTLC""" From 462fa20c17dbf6e2b4db2a0f911ad061bd16eb10 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1157/1530] bkpr: move json_to functions to respective type files --- plugins/bkpr/Makefile | 2 ++ plugins/bkpr/bookkeeper.c | 51 ------------------------------------ plugins/bkpr/chain_event.c | 26 ++++++++++++++++++ plugins/bkpr/chain_event.h | 5 ++++ plugins/bkpr/channel_event.c | 17 ++++++++++++ plugins/bkpr/channel_event.h | 4 +++ plugins/bkpr/onchain_fee.c | 20 ++++++++++++++ plugins/bkpr/onchain_fee.h | 4 +++ 8 files changed, 78 insertions(+), 51 deletions(-) create mode 100644 plugins/bkpr/chain_event.c create mode 100644 plugins/bkpr/onchain_fee.c diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index 34b54ae1d98b..a9b123613c74 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -4,8 +4,10 @@ BOOKKEEPER_PLUGIN_SRC := \ plugins/bkpr/account.c \ plugins/bkpr/account_entry.c \ plugins/bkpr/bookkeeper.c \ + plugins/bkpr/chain_event.c \ plugins/bkpr/channel_event.c \ plugins/bkpr/db.c \ + plugins/bkpr/onchain_fee.c \ plugins/bkpr/recorder.c BOOKKEEPER_DB_QUERIES := \ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index afe7b47171de..69f248cf3fb0 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -27,57 +27,6 @@ static struct db *db ; // FIXME: make relative to directory we're loaded into static char *db_dsn = "sqlite3://accounts.sqlite3"; -static void json_add_channel_event(struct json_stream *out, - struct channel_event *ev) -{ - json_object_start(out, NULL); - json_add_string(out, "account", ev->acct_name); - json_add_string(out, "type", "channel"); - json_add_string(out, "tag", ev->tag); - json_add_amount_msat_only(out, "credit", ev->credit); - json_add_amount_msat_only(out, "debit", ev->debit); - json_add_string(out, "currency", ev->currency); - if (ev->payment_id) - json_add_sha256(out, "payment_id", ev->payment_id); - json_add_u64(out, "timestamp", ev->timestamp); - json_object_end(out); -} - -static void json_add_chain_event(struct json_stream *out, - struct chain_event *ev) -{ - json_object_start(out, NULL); - json_add_string(out, "account", ev->acct_name); - json_add_string(out, "type", "chain"); - json_add_string(out, "tag", ev->tag); - json_add_amount_msat_only(out, "credit", ev->credit); - json_add_amount_msat_only(out, "debit", ev->debit); - json_add_string(out, "currency", ev->currency); - json_add_outpoint(out, "outpoint", &ev->outpoint); - if (ev->spending_txid) - json_add_txid(out, "txid", ev->spending_txid); - if (ev->payment_id) - json_add_sha256(out, "payment_id", ev->payment_id); - json_add_u64(out, "timestamp", ev->timestamp); - json_add_u32(out, "blockheight", ev->blockheight); - json_object_end(out); -} - -static void json_add_onchain_fee(struct json_stream *out, - struct onchain_fee *fee) -{ - json_object_start(out, NULL); - json_add_string(out, "account", fee->acct_name); - json_add_string(out, "type", "onchain_fee"); - json_add_string(out, "tag", "onchain_fee"); - json_add_amount_msat_only(out, "credit", fee->credit); - json_add_amount_msat_only(out, "debit", fee->debit); - json_add_string(out, "currency", fee->currency); - json_add_u64(out, "timestamp", fee->timestamp); - json_add_txid(out, "txid", &fee->txid); - json_object_end(out); -} - static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, struct bitcoin_txid *txid) { diff --git a/plugins/bkpr/chain_event.c b/plugins/bkpr/chain_event.c new file mode 100644 index 000000000000..9b055782e792 --- /dev/null +++ b/plugins/bkpr/chain_event.c @@ -0,0 +1,26 @@ +#include "config.h" + +#include +#include + +void json_add_chain_event(struct json_stream *out, struct chain_event *ev) +{ + json_object_start(out, NULL); + json_add_string(out, "account", ev->acct_name); + if (ev->origin_acct) + json_add_string(out, "origin", ev->origin_acct); + json_add_string(out, "type", "chain"); + json_add_string(out, "tag", ev->tag); + json_add_amount_msat_only(out, "credit", ev->credit); + json_add_amount_msat_only(out, "debit", ev->debit); + json_add_string(out, "currency", ev->currency); + json_add_outpoint(out, "outpoint", &ev->outpoint); + + if (ev->spending_txid) + json_add_txid(out, "txid", ev->spending_txid); + if (ev->payment_id) + json_add_sha256(out, "payment_id", ev->payment_id); + json_add_u64(out, "timestamp", ev->timestamp); + json_add_u32(out, "blockheight", ev->blockheight); + json_object_end(out); +} diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index 1ebd69523974..d9d12c27a71e 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -2,11 +2,13 @@ #define LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H #include "config.h" +#include #include struct amount_msat; struct bitcoin_outpoint; struct bitcoin_txid; +struct json_stream; struct chain_event { @@ -53,4 +55,7 @@ struct chain_event { struct sha256 *payment_id; }; +void json_add_chain_event(struct json_stream *out, + struct chain_event *ev); + #endif /* LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H */ diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index 55ae4686e548..782aacf6b1c9 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -3,6 +3,7 @@ #include #include #include +#include #include struct channel_event *new_channel_event(const tal_t *ctx, @@ -28,3 +29,19 @@ struct channel_event *new_channel_event(const tal_t *ctx, return ev; } + +void json_add_channel_event(struct json_stream *out, + struct channel_event *ev) +{ + json_object_start(out, NULL); + json_add_string(out, "account", ev->acct_name); + json_add_string(out, "type", "channel"); + json_add_string(out, "tag", ev->tag); + json_add_amount_msat_only(out, "credit", ev->credit); + json_add_amount_msat_only(out, "debit", ev->debit); + json_add_string(out, "currency", ev->currency); + if (ev->payment_id) + json_add_sha256(out, "payment_id", ev->payment_id); + json_add_u64(out, "timestamp", ev->timestamp); + json_object_end(out); +} diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h index 8b0066ab963c..28a0787028fd 100644 --- a/plugins/bkpr/channel_event.h +++ b/plugins/bkpr/channel_event.h @@ -6,6 +6,7 @@ #include struct amount_msat; +struct json_stream; struct sha256; struct channel_event { @@ -54,4 +55,7 @@ struct channel_event *new_channel_event(const tal_t *ctx, u32 part_id, u64 timestamp); +void json_add_channel_event(struct json_stream *out, + struct channel_event *ev); + #endif /* LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H */ diff --git a/plugins/bkpr/onchain_fee.c b/plugins/bkpr/onchain_fee.c new file mode 100644 index 000000000000..57c1002fb703 --- /dev/null +++ b/plugins/bkpr/onchain_fee.c @@ -0,0 +1,20 @@ +#include "config.h" +#include +#include + + +void json_add_onchain_fee(struct json_stream *out, + struct onchain_fee *fee) +{ + json_object_start(out, NULL); + json_add_string(out, "account", fee->acct_name); + json_add_string(out, "type", "onchain_fee"); + json_add_string(out, "tag", "onchain_fee"); + json_add_amount_msat_only(out, "credit", fee->credit); + json_add_amount_msat_only(out, "debit", fee->debit); + json_add_string(out, "currency", fee->currency); + json_add_u64(out, "timestamp", fee->timestamp); + json_add_txid(out, "txid", &fee->txid); + json_object_end(out); +} + diff --git a/plugins/bkpr/onchain_fee.h b/plugins/bkpr/onchain_fee.h index b57c9f18a6f9..34f95407dec2 100644 --- a/plugins/bkpr/onchain_fee.h +++ b/plugins/bkpr/onchain_fee.h @@ -2,6 +2,7 @@ #define LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H #include "config.h" +#include #include struct amount_msat; @@ -32,4 +33,7 @@ struct onchain_fee { u32 update_count; }; +void json_add_onchain_fee(struct json_stream *out, + struct onchain_fee *fee); + #endif /* LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H */ From 595c52f611aff9131c97cf4f088b76608cecb683 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1158/1530] bkpr: add timestamp filters to event lists --- plugins/bkpr/recorder.c | 43 ++++++++++++++++++++++++++++++++++++++--- plugins/bkpr/recorder.h | 37 ++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 4 deletions(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index f5bbc4bbed84..cb4ac7b0fb9f 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -102,7 +102,10 @@ static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt return e; } -struct chain_event **list_chain_events(const tal_t *ctx, struct db *db) +struct chain_event **list_chain_events_timebox(const tal_t *ctx, + struct db *db, + u64 start_time, + u64 end_time) { struct db_stmt *stmt; @@ -125,11 +128,20 @@ struct chain_event **list_chain_events(const tal_t *ctx, struct db *db) " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" + " WHERE e.timestamp > ?" + " AND e.timestamp <= ?" " ORDER BY e.timestamp, e.id;")); + db_bind_u64(stmt, 0, start_time); + db_bind_u64(stmt, 1, end_time); return find_chain_events(ctx, take(stmt)); } +struct chain_event **list_chain_events(const tal_t *ctx, struct db *db) +{ + return list_chain_events_timebox(ctx, db, 0, SQLITE_MAX_UINT); +} + struct chain_event **account_get_chain_events(const tal_t *ctx, struct db *db, struct account *acct) @@ -701,7 +713,11 @@ char *account_get_balance(const tal_t *ctx, return NULL; } -struct channel_event **list_channel_events(const tal_t *ctx, struct db *db) +struct channel_event **list_channel_events_timebox(const tal_t *ctx, + struct db *db, + u64 start_time, + u64 end_time) + { struct db_stmt *stmt; struct channel_event **results; @@ -721,8 +737,12 @@ struct channel_event **list_channel_events(const tal_t *ctx, struct db *db) " FROM channel_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" + " WHERE e.timestamp > ?" + " AND e.timestamp <= ?" " ORDER BY e.timestamp, e.id;")); + db_bind_u64(stmt, 0, start_time); + db_bind_u64(stmt, 1, end_time); db_query_prepared(stmt); results = tal_arr(ctx, struct channel_event *, 0); @@ -735,6 +755,12 @@ struct channel_event **list_channel_events(const tal_t *ctx, struct db *db) return results; } +struct channel_event **list_channel_events(const tal_t *ctx, struct db *db) +{ + return list_channel_events_timebox(ctx, db, 0, SQLITE_MAX_UINT); +} + + struct channel_event **account_get_channel_events(const tal_t *ctx, struct db *db, struct account *acct) @@ -827,7 +853,8 @@ struct onchain_fee **account_get_chain_fees(const tal_t *ctx, struct db *db, return results; } -struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) +struct onchain_fee **list_chain_fees_timebox(const tal_t *ctx, struct db *db, + u64 start_time, u64 end_time) { struct db_stmt *stmt; struct onchain_fee **results; @@ -844,11 +871,16 @@ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) " FROM onchain_fees of" " LEFT OUTER JOIN accounts a" " ON a.id = of.account_id" + " WHERE timestamp > ?" + " AND timestamp <= ?" " ORDER BY " " of.timestamp" ", of.account_id" ", of.txid" ", of.update_count")); + + db_bind_u64(stmt, 0, start_time); + db_bind_u64(stmt, 1, end_time); db_query_prepared(stmt); results = tal_arr(ctx, struct onchain_fee *, 0); @@ -861,6 +893,11 @@ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) return results; } +struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db) +{ + return list_chain_fees_timebox(ctx, db, 0, SQLITE_MAX_UINT); +} + static struct account *stmt2account(const tal_t *ctx, struct db_stmt *stmt) { struct account *a = tal(ctx, struct account); diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 5eb623a6c732..f54c74c4505b 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -14,6 +14,7 @@ struct onchain_fee; #define EXTERNAL_ACCT "external" #define WALLET_ACCT WALLET +#define SQLITE_MAX_UINT 0x7FFFFFFFFFFFFFFF struct acct_balance { char *currency; @@ -53,14 +54,38 @@ struct channel_event **account_get_channel_events(const tal_t *ctx, /* Get all channel events, ordered by timestamp */ struct channel_event **list_channel_events(const tal_t *ctx, struct db *db); +/* Get all channel events, order by timestamp. + * + * @ctx - context to allocate from + * @db - database to query + * @start_time - UNIX timestamp to query after (exclusive) + * @end_time - UNIX timestamp to query until (inclusive) + */ +struct channel_event **list_channel_events_timebox(const tal_t *ctx, + struct db *db, + u64 start_time, + u64 end_time); + /* Get all chain events for this account */ struct chain_event **account_get_chain_events(const tal_t *ctx, struct db *db, struct account *acct); -/* Get all chain events, ordered by timestamp */ +/* Get all chain events, order by timestamp. */ struct chain_event **list_chain_events(const tal_t *ctx, struct db *db); +/* Get all chain events, order by timestamp. + * + * @ctx - context to allocate from + * @db - database to query + * @start_time - UNIX timestamp to query after (exclusive) + * @end_time - UNIX timestamp to query until (inclusive) + */ +struct chain_event **list_chain_events_timebox(const tal_t *ctx, + struct db *db, + u64 start_time, + u64 end_time); + /* Calculate the balances for an account * * @calc_sum - compute the total balance. error if negative @@ -93,6 +118,16 @@ bool find_txo_chain(const tal_t *ctx, /* List all chain fees, for all accounts */ struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db); +/* Get all chain fees, order by timestamp. + * + * @ctx - context to allocate from + * @db - database to query + * @start_time - UNIX timestamp to query after (exclusive) + * @end_time - UNIX timestamp to query until (inclusive) + */ +struct onchain_fee **list_chain_fees_timebox(const tal_t *ctx, struct db *db, + u64 start_time, u64 end_time); + /* Returns a list of sums of the fees we've recorded for every txid * for the given account */ struct fee_sum **find_account_onchain_fees(const tal_t *ctx, From 593f1e73651fccc574859934d32acaa6c63f47fc Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1159/1530] bkpr: add a 'listincome' event list the income/expense events for a node. --- plugins/bkpr/Makefile | 3 + plugins/bkpr/bookkeeper.c | 34 ++++ plugins/bkpr/incomestmt.c | 324 ++++++++++++++++++++++++++++++++++++++ plugins/bkpr/incomestmt.h | 33 ++++ 4 files changed, 394 insertions(+) create mode 100644 plugins/bkpr/incomestmt.c create mode 100644 plugins/bkpr/incomestmt.h diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index a9b123613c74..f7c53e1a02b7 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -7,6 +7,7 @@ BOOKKEEPER_PLUGIN_SRC := \ plugins/bkpr/chain_event.c \ plugins/bkpr/channel_event.c \ plugins/bkpr/db.c \ + plugins/bkpr/incomestmt.c \ plugins/bkpr/onchain_fee.c \ plugins/bkpr/recorder.c @@ -21,6 +22,7 @@ BOOKKEEPER_HEADER := \ plugins/bkpr/chain_event.h \ plugins/bkpr/channel_event.h \ plugins/bkpr/db.h \ + plugins/bkpr/incomestmt.h \ plugins/bkpr/onchain_fee.h \ plugins/bkpr/recorder.h @@ -39,6 +41,7 @@ plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(BOOKKEEPER_OBJS) $ BOOKKEEPER_SQL_FILES := \ $(DB_SQL_FILES) \ plugins/bkpr/db.c \ + plugins/bkpr/incomestmt.c \ plugins/bkpr/recorder.c plugins/bkpr/statements_gettextgen.po: $(BOOKKEEPER_SQL_FILES) $(FORCE) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 69f248cf3fb0..e23dd708ec15 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,32 @@ static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, return NULL; } +static struct command_result *json_list_income(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + struct income_event **evs; + + if (!param(cmd, buf, params, + NULL)) + return command_param_failed(); + + /* Ok, go find me some income events! */ + db_begin_transaction(db); + evs = list_income_events_all(cmd, db); + db_commit_transaction(db); + + res = jsonrpc_stream_success(cmd); + + json_array_start(res, "income_events"); + for (size_t i = 0; i < tal_count(evs); i++) + json_add_income_event(res, evs[i]); + + json_array_end(res); + return command_finished(cmd, res); +} + static struct command_result *json_inspect(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -1261,6 +1288,13 @@ static const struct plugin_command commands[] = { "Prints out the on-chain footprint of a given {account}.", json_inspect }, + { + "listincome", + "bookkeeping", + "List all income impacting events", + "List all events for this node that impacted income", + json_list_income + }, }; static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c new file mode 100644 index 000000000000..7139aaceca5d --- /dev/null +++ b/plugins/bkpr/incomestmt.c @@ -0,0 +1,324 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct account *get_account(struct account **accts, + u64 acct_db_id) +{ + for (size_t i = 0; i < tal_count(accts); i++) { + if (accts[i]->db_id == acct_db_id) + return accts[i]; + } + return NULL; +} + +static struct income_event *chain_to_income(const tal_t *ctx, + struct chain_event *ev, + char *acct_to_attribute, + struct amount_msat credit, + struct amount_msat debit) +{ + struct income_event *inc = tal(ctx, struct income_event); + + inc->acct_name = tal_strdup(inc, acct_to_attribute); + inc->tag = tal_strdup(inc, ev->tag); + inc->credit = credit; + inc->debit = debit; + inc->currency = tal_strdup(inc, ev->currency); + inc->timestamp = ev->timestamp; + inc->outpoint = tal_dup(inc, struct bitcoin_outpoint, &ev->outpoint); + + if (ev->spending_txid) + inc->txid = tal_dup(inc, struct bitcoin_txid, + ev->spending_txid); + else + inc->txid = NULL; + + if (ev->payment_id) + inc->payment_id = tal_dup(inc, struct sha256, ev->payment_id); + else + inc->payment_id = NULL; + + return inc; +} + +static struct income_event *channel_to_income(const tal_t *ctx, + struct channel_event *ev, + struct amount_msat credit, + struct amount_msat debit) +{ + struct income_event *inc = tal(ctx, struct income_event); + + inc->acct_name = tal_strdup(inc, ev->acct_name); + inc->tag = tal_strdup(inc, ev->tag); + inc->credit = credit; + inc->debit = debit; + inc->currency = tal_strdup(inc, ev->currency); + inc->timestamp = ev->timestamp; + inc->outpoint = NULL; + inc->txid = NULL; + if (ev->payment_id) + inc->payment_id = tal_dup(inc, struct sha256, ev->payment_id); + else + inc->payment_id = NULL; + + return inc; +} + +static struct income_event *onchainfee_to_income(const tal_t *ctx, + struct onchain_fee *fee) +{ + struct income_event *inc = tal(ctx, struct income_event); + + inc->acct_name = tal_strdup(inc, fee->acct_name); + inc->tag = tal_fmt(inc, "%s", "onchain_fee"); + /* We swap these, as they're actually opposite */ + inc->credit = fee->debit; + inc->debit = fee->credit; + inc->currency = tal_strdup(inc, fee->currency); + inc->timestamp = fee->timestamp; + inc->txid = tal_dup(inc, struct bitcoin_txid, &fee->txid); + inc->outpoint = NULL; + inc->payment_id = NULL; + + return inc; +} + +static struct income_event *maybe_chain_income(const tal_t *ctx, + struct db *db, + struct account *acct, + struct chain_event *ev) +{ + if (streq(ev->tag, "htlc_fulfill")) { + if (streq(ev->acct_name, EXTERNAL_ACCT)) + /* Swap the credit/debit as it went to external */ + return chain_to_income(ctx, ev, + ev->origin_acct, + ev->debit, + ev->credit); + /* Normal credit/debit as it originated from external */ + return chain_to_income(ctx, ev, + ev->acct_name, + ev->credit, ev->debit); + } + + /* expenses */ + if (streq(ev->tag, "anchor")) { + if (acct->we_opened) + /* for now, we count all anchors as expenses */ + return chain_to_income(ctx, ev, + ev->acct_name, + ev->debit, + ev->credit); + /* non-openers dont spend/gain anything on anchors */ + return NULL; + } + + /* income */ + if (streq(ev->tag, "deposit")) { + struct db_stmt *stmt; + + /* deposit to external is cost to us */ + if (streq(ev->acct_name, EXTERNAL_ACCT)) { + struct income_event *iev; + iev = chain_to_income(ctx, ev, + ev->origin_acct, + ev->debit, + ev->credit); + /* Also, really a withdrawal.. phh */ + iev->tag = tal_strdup(iev, mvt_tag_str(WITHDRAWAL)); + return iev; + } + + + /* Did this deposit originate from within our + * wallet? */ + /*FIXME: there's an edge case where we put funds + * into a tx that included funds from a 3rd party + * coming to us... eg. a splice out from the peer + * to our onchain wallet */ + stmt = db_prepare_v2(db, SQL("SELECT" + " 1" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE " + " e.spending_txid = ?")); + + db_bind_txid(stmt, 0, &ev->outpoint.txid); + db_query_prepared(stmt); + if (!db_step(stmt)) { + tal_free(stmt); + /* no matching withdrawal from internal, + * so must be new deposit (external) */ + return chain_to_income(ctx, ev, + ev->acct_name, + ev->credit, + ev->debit); + } + + db_col_ignore(stmt, "1"); + tal_free(stmt); + return NULL; + } + + return NULL; +} + +static struct income_event *maybe_channel_income(const tal_t *ctx, + struct channel_event *ev) +{ + /* We record a +/- penalty adj, but we only count the credit */ + if (streq(ev->tag, "penalty_adj")) { + if (!amount_msat_zero(ev->credit)) + return channel_to_income(ctx, ev, + ev->credit, + ev->debit); + return NULL; + } + + if (streq(ev->tag, "invoice")) { + /* FIXME: add a sub-category for fees paid */ + return channel_to_income(ctx, ev, + ev->credit, + ev->debit); + } + + /* for routed payments, we only record the fees on the + * debiting side -- the side the $$ was made on! */ + if (streq(ev->tag, "routed")) { + if (!amount_msat_zero(ev->debit)) + return channel_to_income(ctx, ev, + ev->fees, + AMOUNT_MSAT(0)); + return NULL; + } + + /* For everything else, it's straight forward */ + /* (lease_fee, pushed, journal_entry) */ + return channel_to_income(ctx, ev, ev->credit, ev->debit); +} + +struct income_event **list_income_events(const tal_t *ctx, + struct db *db, + u64 start_time, + u64 end_time) +{ + struct channel_event **channel_events; + struct chain_event **chain_events; + struct onchain_fee **onchain_fees; + struct account **accts; + + struct income_event **evs; + + channel_events = list_channel_events_timebox(ctx, db, + start_time, end_time); + chain_events = list_chain_events_timebox(ctx, db, start_time, end_time); + onchain_fees = list_chain_fees_timebox(ctx, db, start_time, end_time); + accts = list_accounts(ctx, db); + + evs = tal_arr(ctx, struct income_event *, 0); + + for (size_t i = 0, j = 0, k = 0; + i < tal_count(chain_events) + || j < tal_count(channel_events) + || k < tal_count(onchain_fees); + /* Incrementing happens inside loop */) { + struct channel_event *chan; + struct chain_event *chain; + struct onchain_fee *fee; + u64 lowest = 0; + + if (i < tal_count(chain_events)) + chain = chain_events[i]; + else + chain = NULL; + if (j < tal_count(channel_events)) + chan = channel_events[j]; + else + chan = NULL; + if (k < tal_count(onchain_fees)) + fee = onchain_fees[k]; + else + fee = NULL; + + if (chain) + lowest = chain->timestamp; + + if (chan + && (lowest == 0 || lowest > chan->timestamp)) + lowest = chan->timestamp; + + if (fee + && (lowest == 0 || lowest > fee->timestamp)) + lowest = fee->timestamp; + + /* chain events first, then channel events, then fees. */ + if (chain && chain->timestamp == lowest) { + struct income_event *ev; + struct account *acct = + get_account(accts, chain->acct_db_id); + + ev = maybe_chain_income(evs, db, acct, chain); + if (ev) + tal_arr_expand(&evs, ev); + i++; + continue; + } + + if (chan && chan->timestamp == lowest) { + struct income_event *ev; + ev = maybe_channel_income(evs, chan); + if (ev) + tal_arr_expand(&evs, ev); + + j++; + continue; + } + + /* Last thing left is the fee */ + tal_arr_expand(&evs, onchainfee_to_income(evs, fee)); + k++; + } + + return evs; +} + +struct income_event **list_income_events_all(const tal_t *ctx, struct db *db) +{ + return list_income_events(ctx, db, 0, SQLITE_MAX_UINT); +} + +void json_add_income_event(struct json_stream *out, struct income_event *ev) +{ + json_object_start(out, NULL); + json_add_string(out, "account", ev->acct_name); + json_add_string(out, "tag", ev->tag); + json_add_amount_msat_only(out, "credit", ev->credit); + json_add_amount_msat_only(out, "debit", ev->debit); + json_add_string(out, "currency", ev->currency); + json_add_u64(out, "timestamp", ev->timestamp); + + if (ev->outpoint) + json_add_outpoint(out, "outpoint", ev->outpoint); + + if (ev->txid) + json_add_txid(out, "txid", ev->txid); + + if (ev->payment_id) + json_add_sha256(out, "payment_id", ev->payment_id); + + json_object_end(out); +} diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h new file mode 100644 index 000000000000..e24f3ecb93f0 --- /dev/null +++ b/plugins/bkpr/incomestmt.h @@ -0,0 +1,33 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_INCOMESTMT_H +#define LIGHTNING_PLUGINS_BKPR_INCOMESTMT_H + +#include "config.h" +#include + +struct income_event { + char *acct_name; + char *tag; + struct amount_msat credit; + struct amount_msat debit; + char *currency; + u64 timestamp; + + struct bitcoin_outpoint *outpoint; + struct bitcoin_txid *txid; + struct sha256 *payment_id; +}; + +/* List all the events that are income related (gain/loss) */ +struct income_event **list_income_events_all(const tal_t *ctx, struct db *db); + +/* List all the events that are income related (gain/loss), + * by a start and end date */ +struct income_event **list_income_events(const tal_t *ctx, + struct db *db, + u64 start_time, + u64 end_time); + +/* Given an event and a json_stream, add a new event object to the stream */ +void json_add_income_event(struct json_stream *str, struct income_event *ev); + +#endif /* LIGHTNING_PLUGINS_BKPR_INCOMESTMT_H */ From 25f0c76c9a30b209b4d7113c82ce88d6b7daf259 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1160/1530] tests: move 'bookkeeper' centric tests to their own file Should we pull these out into a separate test suite? --- tests/test_bookkeeper.py | 109 +++++++++++++++++++++++++++++++++++++++ tests/test_closing.py | 101 ------------------------------------ 2 files changed, 109 insertions(+), 101 deletions(-) create mode 100644 tests/test_bookkeeper.py diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py new file mode 100644 index 000000000000..091fe60a7e00 --- /dev/null +++ b/tests/test_bookkeeper.py @@ -0,0 +1,109 @@ +from fixtures import * # noqa: F401,F403 +from pyln.client import Millisatoshi +from utils import ( + sync_blockheight +) + +import pytest + + +@pytest.mark.developer("dev-ignore-htlcs") +def test_closing_trimmed_htlcs(node_factory, bitcoind, executor): + l1, l2 = node_factory.line_graph(2) + + # give l2 an output!? + l1.pay(l2, 11000000) + + l1.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True) + # This will get stuck due to l3 ignoring htlcs + executor.submit(l2.pay, l1, 100001) + l1.daemon.wait_for_log('their htlc 0 dev_ignore_htlcs') + + l1.rpc.dev_fail(l2.info['id']) + l1.wait_for_channel_onchain(l2.info['id']) + + bitcoind.generate_block(1) + l1.daemon.wait_for_log(' to ONCHAIN') + l2.daemon.wait_for_log(' to ONCHAIN') + + bitcoind.generate_block(5) + sync_blockheight(bitcoind, [l1]) + l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') + bitcoind.generate_block(20) + sync_blockheight(bitcoind, [l1]) + l1.daemon.wait_for_log(r'All outputs resolved.*') + + def _find_tags(evs, tag): + return [e for e in evs if e['tag'] == tag] + + def _find_first_tag(evs, tag): + ev = _find_tags(evs, tag) + assert len(ev) > 0 + return ev[0] + + evs = l1.rpc.listaccountevents()['events'] + close = _find_first_tag(evs, 'channel_close') + delayed_to = _find_first_tag(evs, 'delayed_to_us') + + # find the chain fee entry for the channel close + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) + + # l2's fees should equal the trimmed htlc out + evs = l2.rpc.listaccountevents()['events'] + close = _find_first_tag(evs, 'channel_close') + deposit = _find_first_tag(evs, 'deposit') + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + # sent htlc was too small, we lose it, rounded up to nearest sat + assert close_fee[0]['credit'] == '101000msat' + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) + + +def test_closing_subsat_htlcs(node_factory, bitcoind, chainparams): + """Test closing balances when HTLCs are: sub 1-satoshi""" + l1, l2 = node_factory.line_graph(2) + + l1.pay(l2, 111) + l1.pay(l2, 222) + l1.pay(l2, 4000000) + + l2.stop() + l1.rpc.close(l2.info['id'], 1) + bitcoind.generate_block(5, wait_for_mempool=1) + + l2.start() + sync_blockheight(bitcoind, [l1]) + l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') + bitcoind.generate_block(80) + + def _find_tags(evs, tag): + return [e for e in evs if e['tag'] == tag] + + def _find_first_tag(evs, tag): + ev = _find_tags(evs, tag) + assert len(ev) > 0 + return ev[0] + + sync_blockheight(bitcoind, [l1, l2]) + evs = l1.rpc.listaccountevents()['events'] + # check that closing equals onchain deposits + fees + close = _find_first_tag(evs, 'channel_close') + delayed_to = _find_first_tag(evs, 'delayed_to_us') + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) + + evs = l2.rpc.listaccountevents()['events'] + close = _find_first_tag(evs, 'channel_close') + deposit = _find_first_tag(evs, 'deposit') + fees = _find_tags(evs, 'onchain_fee') + close_fee = [e for e in fees if e['txid'] == close['txid']] + assert len(close_fee) == 1 + # too small to fit, we lose them as miner fees + assert close_fee[0]['credit'] == '333msat' + assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) diff --git a/tests/test_closing.py b/tests/test_closing.py index e58b23e08021..3b06066671f2 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -479,107 +479,6 @@ def test_closing_negotiation_step_700sat(node_factory, bitcoind, chainparams): closing_negotiation_step(node_factory, bitcoind, chainparams, opts) -# FIXME: move to bookkeeper tests -@pytest.mark.developer("dev-ignore-htlcs") -def test_closing_trimmed_htlcs(node_factory, bitcoind, executor): - l1, l2 = node_factory.line_graph(2) - - # give l2 an output!? - l1.pay(l2, 11000000) - - l1.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True) - # This will get stuck due to l3 ignoring htlcs - executor.submit(l2.pay, l1, 100001) - l1.daemon.wait_for_log('their htlc 0 dev_ignore_htlcs') - - l1.rpc.dev_fail(l2.info['id']) - l1.wait_for_channel_onchain(l2.info['id']) - - bitcoind.generate_block(1) - l1.daemon.wait_for_log(' to ONCHAIN') - l2.daemon.wait_for_log(' to ONCHAIN') - - bitcoind.generate_block(5) - l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') - bitcoind.generate_block(20) - l1.daemon.wait_for_log('All outputs resolved') - - def _find_tags(evs, tag): - return [e for e in evs if e['tag'] == tag] - - def _find_first_tag(evs, tag): - ev = _find_tags(evs, tag) - assert len(ev) > 0 - return ev[0] - - evs = l1.rpc.listaccountevents()['events'] - close = _find_first_tag(evs, 'channel_close') - delayed_to = _find_first_tag(evs, 'delayed_to_us') - - # find the chain fee entry for the channel close - fees = _find_tags(evs, 'onchain_fee') - close_fee = [e for e in fees if e['txid'] == close['txid']] - assert len(close_fee) == 1 - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) - - # l2's fees should equal the trimmed htlc out - evs = l2.rpc.listaccountevents()['events'] - close = _find_first_tag(evs, 'channel_close') - deposit = _find_first_tag(evs, 'deposit') - fees = _find_tags(evs, 'onchain_fee') - close_fee = [e for e in fees if e['txid'] == close['txid']] - assert len(close_fee) == 1 - # sent htlc was too small, we lose it, rounded up to nearest sat - assert close_fee[0]['credit'] == '101000msat' - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) - - -# FIXME: move to bookkeeper tests -def test_closing_subsat_htlcs(node_factory, bitcoind, chainparams): - """Test closing balances when HTLCs are: sub 1-satoshi""" - l1, l2 = node_factory.line_graph(2) - - l1.pay(l2, 111) - l1.pay(l2, 222) - l1.pay(l2, 4000000) - - l2.stop() - l1.rpc.close(l2.info['id'], 1) - bitcoind.generate_block(5) - l2.start() - sync_blockheight(bitcoind, [l1]) - l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') - bitcoind.generate_block(80) - - def _find_tags(evs, tag): - return [e for e in evs if e['tag'] == tag] - - def _find_first_tag(evs, tag): - ev = _find_tags(evs, tag) - assert len(ev) > 0 - return ev[0] - - sync_blockheight(bitcoind, [l1, l2]) - evs = l1.rpc.listaccountevents()['events'] - # check that closing equals onchain deposits + fees - close = _find_first_tag(evs, 'channel_close') - delayed_to = _find_first_tag(evs, 'delayed_to_us') - fees = _find_tags(evs, 'onchain_fee') - close_fee = [e for e in fees if e['txid'] == close['txid']] - assert len(close_fee) == 1 - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) - - evs = l2.rpc.listaccountevents()['events'] - close = _find_first_tag(evs, 'channel_close') - deposit = _find_first_tag(evs, 'deposit') - fees = _find_tags(evs, 'onchain_fee') - close_fee = [e for e in fees if e['txid'] == close['txid']] - assert len(close_fee) == 1 - # too small to fit, we lose them as miner fees - assert close_fee[0]['credit'] == '333msat' - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) - - @pytest.mark.developer("needs dev-disable-commit-after") def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): """Test penalty transaction with an incoming HTLC""" From 1dd52ba0034c9ed86e8fb90ef6fc08ad58bf257f Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1161/1530] bkpr: mark external deposits (withdraws) via blockheight when confirmed We issue events for external deposits (withdrawals) before the tx is confirmed in a block. To avoid double counting these, we don't count them as confirmed/included until after they're confirmed. We do this by keeping the blockheight as zero until the withdraw for the input for them comes through. Note that since we don't have any way to note when RBF'd withdraws aren't eligible for block inclusion anymore, we don't really have a good heuristic to trim them. Which is fine, they *will* show up in account events however. --- plugins/bkpr/bookkeeper.c | 12 ++ plugins/bkpr/incomestmt.c | 6 + plugins/bkpr/recorder.c | 38 +++++ plugins/bkpr/recorder.h | 8 ++ tests/test_bookkeeper.py | 284 ++++++++++++++++++++++++++++++++++---- 5 files changed, 318 insertions(+), 30 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index e23dd708ec15..964dc70fc5c0 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1072,6 +1072,18 @@ parse_and_log_chain_move(struct command *cmd, "Unable to update onchain fees %s", err); + /* If this is a spend confirmation event, it's possible + * that it we've got an external deposit that's now + * confirmed */ + if (e->spending_txid) { + db_begin_transaction(db); + /* Go see if there's any deposits to an external + * that are now confirmed */ + /* FIXME: might need updating when we can splice? */ + maybe_closeout_external_deposits(db, e); + db_commit_transaction(db); + } + /* If this is an account close event, it's possible * that we *never* got the open event. (This happens * if you add the plugin *after* you've closed the channel) */ diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 7139aaceca5d..ffc074a9455b 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -133,6 +133,12 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, /* deposit to external is cost to us */ if (streq(ev->acct_name, EXTERNAL_ACCT)) { struct income_event *iev; + + /* External deposits w/o a blockheight + * aren't confirmed yet */ + if (ev->blockheight == 0) + return NULL; + iev = chain_to_income(ctx, ev, ev->origin_acct, ev->debit, diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index cb4ac7b0fb9f..7cfd7a6435ea 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -1638,6 +1638,44 @@ char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, return err; } +void maybe_closeout_external_deposits(struct db *db, + struct chain_event *ev) +{ + struct db_stmt *stmt; + + assert(ev->spending_txid); + stmt = db_prepare_v2(db, SQL("SELECT " + " e.id" + " FROM chain_events e" + " LEFT OUTER JOIN accounts a" + " ON e.account_id = a.id" + " WHERE e.blockheight = ?" + " AND e.utxo_txid = ?" + " AND a.name = ?")); + + /* Blockheight for unconfirmeds is zero */ + db_bind_int(stmt, 0, 0); + db_bind_txid(stmt, 1, ev->spending_txid); + db_bind_text(stmt, 2, EXTERNAL_ACCT); + db_query_prepared(stmt); + + while (db_step(stmt)) { + struct db_stmt *update_stmt; + u64 id; + + id = db_col_u64(stmt, "e.id"); + update_stmt = db_prepare_v2(db, SQL("UPDATE chain_events SET" + " blockheight = ?" + " WHERE id = ?")); + + db_bind_int(update_stmt, 0, ev->blockheight); + db_bind_u64(update_stmt, 1, id); + db_exec_prepared_v2(take(update_stmt)); + } + + tal_free(stmt); +} + bool log_chain_event(struct db *db, const struct account *acct, struct chain_event *e) diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index f54c74c4505b..228ce07b7aa1 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -171,6 +171,14 @@ char *update_channel_onchain_fees(const tal_t *ctx, * The point of this is to allow us to prune data, eventually */ void maybe_mark_account_onchain(struct db *db, struct account *acct); +/* When we make external deposits from the wallet, we don't + * count them until any output that was spent *into* them is + * confirmed onchain. + * + * This method updates the blockheight on these events to the + * height an input was spent into */ +void maybe_closeout_external_deposits(struct db *db, struct chain_event *ev); + /* Log a channel event */ void log_channel_event(struct db *db, const struct account *acct, diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 091fe60a7e00..cb0a12920cd5 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -1,17 +1,31 @@ from fixtures import * # noqa: F401,F403 +from decimal import Decimal from pyln.client import Millisatoshi +from fixtures import TEST_NETWORK from utils import ( - sync_blockheight + sync_blockheight, wait_for, only_one ) +import os import pytest +import unittest + + +def find_tags(evs, tag): + return [e for e in evs if e['tag'] == tag] + + +def find_first_tag(evs, tag): + ev = find_tags(evs, tag) + assert len(ev) > 0 + return ev[0] @pytest.mark.developer("dev-ignore-htlcs") def test_closing_trimmed_htlcs(node_factory, bitcoind, executor): l1, l2 = node_factory.line_graph(2) - # give l2 an output!? + # Send l2 funds via the channel l1.pay(l2, 11000000) l1.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True) @@ -33,29 +47,21 @@ def test_closing_trimmed_htlcs(node_factory, bitcoind, executor): sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(r'All outputs resolved.*') - def _find_tags(evs, tag): - return [e for e in evs if e['tag'] == tag] - - def _find_first_tag(evs, tag): - ev = _find_tags(evs, tag) - assert len(ev) > 0 - return ev[0] - evs = l1.rpc.listaccountevents()['events'] - close = _find_first_tag(evs, 'channel_close') - delayed_to = _find_first_tag(evs, 'delayed_to_us') + close = find_first_tag(evs, 'channel_close') + delayed_to = find_first_tag(evs, 'delayed_to_us') # find the chain fee entry for the channel close - fees = _find_tags(evs, 'onchain_fee') + fees = find_tags(evs, 'onchain_fee') close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) # l2's fees should equal the trimmed htlc out evs = l2.rpc.listaccountevents()['events'] - close = _find_first_tag(evs, 'channel_close') - deposit = _find_first_tag(evs, 'deposit') - fees = _find_tags(evs, 'onchain_fee') + close = find_first_tag(evs, 'channel_close') + deposit = find_first_tag(evs, 'deposit') + fees = find_tags(evs, 'onchain_fee') close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 # sent htlc was too small, we lose it, rounded up to nearest sat @@ -80,30 +86,248 @@ def test_closing_subsat_htlcs(node_factory, bitcoind, chainparams): l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') bitcoind.generate_block(80) - def _find_tags(evs, tag): - return [e for e in evs if e['tag'] == tag] - - def _find_first_tag(evs, tag): - ev = _find_tags(evs, tag) - assert len(ev) > 0 - return ev[0] - sync_blockheight(bitcoind, [l1, l2]) evs = l1.rpc.listaccountevents()['events'] # check that closing equals onchain deposits + fees - close = _find_first_tag(evs, 'channel_close') - delayed_to = _find_first_tag(evs, 'delayed_to_us') - fees = _find_tags(evs, 'onchain_fee') + close = find_first_tag(evs, 'channel_close') + delayed_to = find_first_tag(evs, 'delayed_to_us') + fees = find_tags(evs, 'onchain_fee') close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) evs = l2.rpc.listaccountevents()['events'] - close = _find_first_tag(evs, 'channel_close') - deposit = _find_first_tag(evs, 'deposit') - fees = _find_tags(evs, 'onchain_fee') + close = find_first_tag(evs, 'channel_close') + deposit = find_first_tag(evs, 'deposit') + fees = find_tags(evs, 'onchain_fee') close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 # too small to fit, we lose them as miner fees assert close_fee[0]['credit'] == '333msat' assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) + + +def test_bookkeeping_external_withdraws(node_factory, bitcoind): + """ Withdrawals to an external address shouldn't be included + in the income statements until confirmed""" + l1 = node_factory.get_node() + addr = l1.rpc.newaddr()['bech32'] + + amount = 1111111 + amount_msat = Millisatoshi(amount * 1000) + bitcoind.rpc.sendtoaddress(addr, amount / 10**8) + bitcoind.rpc.sendtoaddress(addr, amount / 10**8) + + bitcoind.generate_block(1) + wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2) + + waddr = l1.bitcoin.rpc.getnewaddress() + + # Ok, now we send some funds to an external address + out = l1.rpc.withdraw(waddr, amount // 2) + + # Make sure bitcoind received the withdrawal + unspent = l1.bitcoin.rpc.listunspent(0) + withdrawal = [u for u in unspent if u['txid'] == out['txid']] + + assert withdrawal[0]['amount'] == Decimal('0.00555555') + incomes = l1.rpc.listincome()['income_events'] + # There should only be two income events: deposits to wallet + # for {amount} + assert len(incomes) == 2 + for inc in incomes: + assert inc['account'] == 'wallet' + assert inc['tag'] == 'deposit' + assert Millisatoshi(inc['credit']) == amount_msat + # The event should show up in the 'listaccountevents' however + events = l1.rpc.listaccountevents()['events'] + assert len(events) == 3 + external = [e for e in events if e['account'] == 'external'][0] + assert Millisatoshi(external['credit']) == Millisatoshi(amount // 2 * 1000) + + btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 + + # Restart the node, issues a balance snapshot + # If we were counting these incorrectly, + # we'd have a new journal_entry + l1.restart() + + # the number of account + income events should be unchanged + incomes = l1.rpc.listincome()['income_events'] + assert len(find_tags(incomes, 'journal_entry')) == 0 + assert len(incomes) == 2 + events = l1.rpc.listaccountevents()['events'] + assert len(events) == 3 + assert len(find_tags(events, 'journal_entry')) == 0 + + # the wallet balance should be unchanged + btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 + + # ok now we mine a block + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l1]) + + # expect the withdrawal to appear in the incomes + # and there should be an onchain fee + incomes = l1.rpc.listincome()['income_events'] + # 2 wallet deposits, 1 wallet withdrawal, 2 onchain_fees + assert len(incomes) == 5 + withdraw_amt = Millisatoshi(find_tags(incomes, 'withdrawal')[0]['debit']) + assert withdraw_amt == Millisatoshi(amount // 2 * 1000) + + fee_events = find_tags(incomes, 'onchain_fee') + fees = 0 + for e in fee_events: + # Millisatoshis cant be negative, so we reverse + # the credit/debit on the chain fee events and + # deal with them as ints instead of millisatoshis + fees -= int(e['credit'][:-4]) + fees += int(e['debit'][:-4]) + + # In sum, the fees are a net loss + assert fees > 0 + fees = Millisatoshi(fees) + + # wallet balance is decremented now + btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 - withdraw_amt - fees + + +@unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Depends on sqlite3 database location") +def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): + """ Withdrawals to an external address turn up as + extremely large onchain_fees when they happen before + our accounting plugin is attached""" + l1 = node_factory.get_node() + + basedir = l1.daemon.opts.get("lightning-dir") + addr = l1.rpc.newaddr()['bech32'] + + amount = 1111111 + amount_msat = Millisatoshi(amount * 1000) + bitcoind.rpc.sendtoaddress(addr, amount / 10**8) + bitcoind.rpc.sendtoaddress(addr, amount / 10**8) + + bitcoind.generate_block(1) + wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2) + + waddr = l1.bitcoin.rpc.getnewaddress() + + # Ok, now we send some funds to an external address + l1.rpc.withdraw(waddr, amount // 2) + + # There should only be two income events: deposits to wallet + assert len(l1.rpc.listincome()['income_events']) == 2 + # There are three account events: 2 wallet deposits, 1 external deposit + assert len(l1.rpc.listaccountevents()['events']) == 3 + + # Stop node and remove the accounts data + l1.stop() + os.remove(os.path.join(basedir, TEST_NETWORK, 'accounts.sqlite3')) + l1.start() + + # the number of income events should be unchanged + assert len(l1.rpc.listincome()['income_events']) == 2 + # we're now missing the external deposit + events = l1.rpc.listaccountevents()['events'] + assert len(events) == 2 + assert len([e for e in events if e['account'] == 'external']) == 0 + assert len(find_tags(events, 'journal_entry')) == 0 + + # the wallet balance should be unchanged + btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 + + # ok now we mine a block + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l1]) + + # expect the withdrawal to appear in the incomes + # and there should be an onchain fee + incomes = l1.rpc.listincome()['income_events'] + # 2 wallet deposits, 1 onchain_fee + assert len(incomes) == 3 + assert len(find_tags(incomes, 'withdrawal')) == 0 + + fee_events = find_tags(incomes, 'onchain_fee') + fees = 0 + for e in fee_events: + # Millisatoshis cant be negative, so we reverse + # the credit/debit on the chain fee events and + # deal with them as ints instead of millisatoshis + fees -= int(e['credit'][:-4]) + fees += int(e['debit'][:-4]) + + # In sum, the fees are a net loss + assert fees > 0 + fees = Millisatoshi(fees) + assert fees > Millisatoshi(amount // 2 * 1000) + + # wallet balance is decremented now + bal = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + assert Millisatoshi(bal['balance']) == amount_msat * 2 - fees + + +def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): + """ If a withdraw to an external gets RBF'd, + it should *not* show up in our income ever. + (but it will show up in our account events) + """ + l1 = node_factory.get_node() + addr = l1.rpc.newaddr()['bech32'] + + amount = 1111111 + bitcoind.rpc.sendtoaddress(addr, amount / 10**8) + bitcoind.generate_block(1) + wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1) + assert len(l1.rpc.listaccountevents()['events']) == 1 + assert len(l1.rpc.listincome()['income_events']) == 1 + + # Ok, now we send some funds to an external address + waddr = l1.bitcoin.rpc.getnewaddress() + out1 = l1.rpc.withdraw(waddr, amount // 2, feerate='253perkw') + mempool = bitcoind.rpc.getrawmempool(True) + assert len(list(mempool.keys())) == 1 + assert out1['txid'] in list(mempool.keys()) + + # another account event, still one income event + assert len(l1.rpc.listaccountevents()['events']) == 2 + assert len(l1.rpc.listincome()['income_events']) == 1 + + # unreserve the existing output + l1.rpc.unreserveinputs(out1['psbt'], 200) + + # resend the tx + out2 = l1.rpc.withdraw(waddr, amount // 2, feerate='1000perkw') + mempool = bitcoind.rpc.getrawmempool(True) + assert len(list(mempool.keys())) == 1 + assert out2['txid'] in list(mempool.keys()) + + # another account event, still one income event + assert len(l1.rpc.listaccountevents()['events']) == 3 + assert len(l1.rpc.listincome()['income_events']) == 1 + + # ok now we mine a block + bitcoind.generate_block(1) + sync_blockheight(bitcoind, [l1]) + + acct_evs = l1.rpc.listaccountevents()['events'] + externs = [e for e in acct_evs if e['account'] == 'external'] + assert len(externs) == 2 + assert externs[0]['outpoint'][:-2] == out1['txid'] + assert externs[0]['blockheight'] == 0 + assert externs[1]['outpoint'][:-2] == out2['txid'] + assert externs[1]['blockheight'] > 0 + + withdraws = find_tags(l1.rpc.listincome()['income_events'], 'withdrawal') + assert len(withdraws) == 1 + assert withdraws[0]['outpoint'][:-2] == out2['txid'] + + # make sure no onchain fees are counted for the replaced tx + fees = find_tags(acct_evs, 'onchain_fee') + assert len(fees) > 0 + for fee in fees: + assert fee['txid'] == out2['txid'] From 83c6cf25d2f773eef8db1fa830a53beaaebcab9e Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1162/1530] bkpr: 'to_miner' spends are considered terminal This is a rare case where we RBF the output of a penalty until it no longer has an output value we can reclaim. We ignore the txid for these events when closing a channel. --- plugins/bkpr/recorder.c | 2 ++ tests/test_closing.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 7cfd7a6435ea..68969baf4533 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -386,6 +386,8 @@ bool find_txo_chain(const tal_t *ctx, * might overlap txids */ if (pr->spend && pr->spend->spending_txid + /* 'to_miner' outputs are skipped */ + && !streq(pr->spend->tag, "to_miner") && !txid_in_list(txids, pr->spend->spending_txid) /* We dont trace utxos for non related accts */ && pr->spend->acct_db_id == acct->db_id) { diff --git a/tests/test_closing.py b/tests/test_closing.py index 3b06066671f2..a443d4240dfb 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1755,6 +1755,10 @@ def get_rbf_tx(self, depth, name, resolve): check_utxos_channel(l2, [channel_id], expected_2) + # Make sure that l2's account is considered closed (has a fee output) + fees = [e for e in l2.rpc.listincome()['income_events'] if e['tag'] == 'onchain_fee'] + assert len(fees) == 1 + @pytest.mark.developer("needs DEVELOPER=1") def test_onchain_first_commit(node_factory, bitcoind): From a7b7ea5d493fef2a6f62d498fd9293cd985c46a9 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1163/1530] bkpr: add a 'consolidate-fees' flag to the income stmt Defaults to true. This is actually what you want to see -- the fees for an account's txid collapsed into a single entry. --- plugins/bkpr/bookkeeper.c | 5 +++- plugins/bkpr/incomestmt.c | 49 ++++++++++++++++++++++++++++++++++++--- plugins/bkpr/incomestmt.h | 6 +++-- plugins/bkpr/recorder.c | 39 +++++++++++++++++++++++++++++++ plugins/bkpr/recorder.h | 4 ++++ tests/test_bookkeeper.py | 41 +++++++++++--------------------- 6 files changed, 111 insertions(+), 33 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 964dc70fc5c0..95c55b0f48bc 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -44,14 +44,17 @@ static struct command_result *json_list_income(struct command *cmd, { struct json_stream *res; struct income_event **evs; + bool *consolidate_fees; if (!param(cmd, buf, params, + p_opt_def("consolidate_fees", param_bool, + &consolidate_fees, true), NULL)) return command_param_failed(); /* Ok, go find me some income events! */ db_begin_transaction(db); - evs = list_income_events_all(cmd, db); + evs = list_income_events_all(cmd, db, *consolidate_fees); db_commit_transaction(db); res = jsonrpc_stream_success(cmd); diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index ffc074a9455b..18866a89f64d 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -217,10 +217,47 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, return channel_to_income(ctx, ev, ev->credit, ev->debit); } +static struct onchain_fee **find_consolidated_fees(const tal_t *ctx, + struct db *db, + struct onchain_fee **fees TAKES) +{ + struct fee_sum **sums; + struct onchain_fee **updated_fees + = tal_arr(ctx, struct onchain_fee *, 0); + + sums = calculate_onchain_fee_sums(ctx, db); + + for (size_t i = 0; i < tal_count(sums); i++) { + /* Find the last matching feerate's data */ + for (size_t j = tal_count(fees); j > 0; j--) { + struct onchain_fee *fee; + if (bitcoin_txid_eq(&fees[j - 1]->txid, + sums[i]->txid) + && fees[j - 1]->acct_db_id == sums[i]->acct_db_id) { + fee = tal_steal(updated_fees, fees[j - 1]); + fee->credit = sums[i]->fees_paid; + fee->debit = AMOUNT_MSAT(0); + tal_arr_expand(&updated_fees, fee); + fees[j - 1] = NULL; + break; + } + } + + /* It's possible there weren't any fee events + * for this txid in the time period we've selected */ + } + + if (taken(fees)) + tal_free(fees); + + return updated_fees; +} + struct income_event **list_income_events(const tal_t *ctx, struct db *db, u64 start_time, - u64 end_time) + u64 end_time, + bool consolidate_fees) { struct channel_event **channel_events; struct chain_event **chain_events; @@ -235,6 +272,10 @@ struct income_event **list_income_events(const tal_t *ctx, onchain_fees = list_chain_fees_timebox(ctx, db, start_time, end_time); accts = list_accounts(ctx, db); + if (consolidate_fees) + onchain_fees = find_consolidated_fees(ctx, db, + take(onchain_fees)); + evs = tal_arr(ctx, struct income_event *, 0); for (size_t i = 0, j = 0, k = 0; @@ -302,9 +343,11 @@ struct income_event **list_income_events(const tal_t *ctx, return evs; } -struct income_event **list_income_events_all(const tal_t *ctx, struct db *db) +struct income_event **list_income_events_all(const tal_t *ctx, struct db *db, + bool consolidate_fees) { - return list_income_events(ctx, db, 0, SQLITE_MAX_UINT); + return list_income_events(ctx, db, 0, SQLITE_MAX_UINT, + consolidate_fees); } void json_add_income_event(struct json_stream *out, struct income_event *ev) diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h index e24f3ecb93f0..50b42b4ef5b2 100644 --- a/plugins/bkpr/incomestmt.h +++ b/plugins/bkpr/incomestmt.h @@ -18,14 +18,16 @@ struct income_event { }; /* List all the events that are income related (gain/loss) */ -struct income_event **list_income_events_all(const tal_t *ctx, struct db *db); +struct income_event **list_income_events_all(const tal_t *ctx, struct db *db, + bool consolidate_fees); /* List all the events that are income related (gain/loss), * by a start and end date */ struct income_event **list_income_events(const tal_t *ctx, struct db *db, u64 start_time, - u64 end_time); + u64 end_time, + bool consolidate_fees); /* Given an event and a json_stream, add a new event object to the stream */ void json_add_income_event(struct json_stream *str, struct income_event *ev); diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 68969baf4533..bd5712f43d00 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -209,6 +209,43 @@ static struct chain_event **find_txos_for_tx(const tal_t *ctx, return find_chain_events(ctx, take(stmt)); } +struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) +{ + struct db_stmt *stmt; + struct fee_sum **sums; + stmt = db_prepare_v2(db, SQL("SELECT" + " txid" + ", account_id" + ", SUM(credit)" + ", SUM(debit)" + " FROM onchain_fees" + " GROUP BY txid, account_id" + " ORDER BY txid, account_id")); + + db_query_prepared(stmt); + + sums = tal_arr(ctx, struct fee_sum *, 0); + while (db_step(stmt)) { + struct fee_sum *sum; + struct amount_msat amt; + bool ok; + + sum = tal(sums, struct fee_sum); + sum->txid = tal(sum, struct bitcoin_txid); + db_col_txid(stmt, "txid", sum->txid); + sum->acct_db_id = db_col_u64(stmt, "account_id"); + + db_col_amount_msat(stmt, "SUM(credit)", &sum->fees_paid); + db_col_amount_msat(stmt, "SUM(debit)", &amt); + ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, amt); + assert(ok); + tal_arr_expand(&sums, sum); + } + + tal_free(stmt); + return sums; +} + struct fee_sum **find_account_onchain_fees(const tal_t *ctx, struct db *db, struct account *acct) @@ -234,6 +271,7 @@ struct fee_sum **find_account_onchain_fees(const tal_t *ctx, bool ok; sum = tal(sums, struct fee_sum); + sum->acct_db_id = acct->db_id; sum->txid = tal(sum, struct bitcoin_txid); db_col_txid(stmt, "txid", sum->txid); @@ -244,6 +282,7 @@ struct fee_sum **find_account_onchain_fees(const tal_t *ctx, tal_arr_expand(&sums, sum); } + tal_free(stmt); return sums; } diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 228ce07b7aa1..c71daab6119e 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -24,6 +24,7 @@ struct acct_balance { }; struct fee_sum { + u64 acct_db_id; struct bitcoin_txid *txid; struct amount_msat fees_paid; }; @@ -134,6 +135,9 @@ struct fee_sum **find_account_onchain_fees(const tal_t *ctx, struct db *db, struct account *acct); +/* Final all the onchain fees */ +struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db); + /* Add the given account to the database */ void account_add(struct db *db, struct account *acct); /* Given an account name, find that account record */ diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index cb0a12920cd5..620cde37ea6e 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -22,7 +22,7 @@ def find_first_tag(evs, tag): @pytest.mark.developer("dev-ignore-htlcs") -def test_closing_trimmed_htlcs(node_factory, bitcoind, executor): +def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): l1, l2 = node_factory.line_graph(2) # Send l2 funds via the channel @@ -69,7 +69,7 @@ def test_closing_trimmed_htlcs(node_factory, bitcoind, executor): assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) -def test_closing_subsat_htlcs(node_factory, bitcoind, chainparams): +def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): """Test closing balances when HTLCs are: sub 1-satoshi""" l1, l2 = node_factory.line_graph(2) @@ -172,23 +172,14 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): # expect the withdrawal to appear in the incomes # and there should be an onchain fee incomes = l1.rpc.listincome()['income_events'] - # 2 wallet deposits, 1 wallet withdrawal, 2 onchain_fees - assert len(incomes) == 5 + # 2 wallet deposits, 1 wallet withdrawal, 1 onchain_fee + assert len(incomes) == 4 withdraw_amt = Millisatoshi(find_tags(incomes, 'withdrawal')[0]['debit']) assert withdraw_amt == Millisatoshi(amount // 2 * 1000) fee_events = find_tags(incomes, 'onchain_fee') - fees = 0 - for e in fee_events: - # Millisatoshis cant be negative, so we reverse - # the credit/debit on the chain fee events and - # deal with them as ints instead of millisatoshis - fees -= int(e['credit'][:-4]) - fees += int(e['debit'][:-4]) - - # In sum, the fees are a net loss - assert fees > 0 - fees = Millisatoshi(fees) + assert len(fee_events) == 1 + fees = Millisatoshi(fee_events[0]['debit']) # wallet balance is decremented now btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) @@ -253,17 +244,8 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): assert len(find_tags(incomes, 'withdrawal')) == 0 fee_events = find_tags(incomes, 'onchain_fee') - fees = 0 - for e in fee_events: - # Millisatoshis cant be negative, so we reverse - # the credit/debit on the chain fee events and - # deal with them as ints instead of millisatoshis - fees -= int(e['credit'][:-4]) - fees += int(e['debit'][:-4]) - - # In sum, the fees are a net loss - assert fees > 0 - fees = Millisatoshi(fees) + assert len(fee_events) == 1 + fees = Millisatoshi(fee_events[0]['debit']) assert fees > Millisatoshi(amount // 2 * 1000) # wallet balance is decremented now @@ -328,6 +310,11 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): # make sure no onchain fees are counted for the replaced tx fees = find_tags(acct_evs, 'onchain_fee') - assert len(fees) > 0 + assert len(fees) > 1 for fee in fees: assert fee['txid'] == out2['txid'] + + fees = find_tags(l1.rpc.listincome(consolidate_fees=False)['income_events'], 'onchain_fee') + assert len(fees) == 2 + fees = find_tags(l1.rpc.listincome(consolidate_fees=True)['income_events'], 'onchain_fee') + assert len(fees) == 1 From a0b34066e0b41d47e6a893ce998a697ce546491d Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1164/1530] bkpr: add `dumpincomecsv` command Prints out the `listincome` events as a CSV formatted file Current csv_format options: - koinly - cointracker - harmony (https://github.com/harmony-csv/harmony) - quickbooks* *Quickbooks expects values in 'USD', whereas we print values out in (will be noted in the Description field). This won't work how you'd expect -> you might need to do some conversion etc before uploading it. All amounts emitted as 'btc' w/ *11* decimals. --- plugins/bkpr/bookkeeper.c | 68 +++++++ plugins/bkpr/incomestmt.c | 418 +++++++++++++++++++++++++++++++++++++- plugins/bkpr/incomestmt.h | 21 ++ 3 files changed, 506 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 95c55b0f48bc..565384638300 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -38,6 +38,64 @@ static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, return NULL; } +static struct command_result *param_csv_format(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct csv_fmt **csv_fmt) +{ + *csv_fmt = cast_const(struct csv_fmt *, + csv_match_token(buffer, tok)); + if (*csv_fmt) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + tal_fmt(cmd, + "should be one of: %s", + csv_list_fmts(cmd))); +} + +static struct command_result *json_dump_income(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + struct income_event **evs; + struct csv_fmt *csv_fmt; + const char *filename; + bool *consolidate_fees; + char *err; + u64 *start_time, *end_time; + + if (!param(cmd, buf, params, + p_req("csv_format", param_csv_format, &csv_fmt), + p_opt("csv_file", param_string, &filename), + p_opt_def("consolidate_fees", param_bool, + &consolidate_fees, true), + p_opt_def("start_time", param_u64, &start_time, 0), + p_opt_def("end_time", param_u64, &end_time, SQLITE_MAX_UINT), + NULL)) + return command_param_failed(); + + /* Ok, go find me some income events! */ + db_begin_transaction(db); + evs = list_income_events(cmd, db, *start_time, *end_time, + *consolidate_fees); + db_commit_transaction(db); + + if (!filename) + filename = csv_filename(cmd, csv_fmt); + + err = csv_print_income_events(cmd, csv_fmt, filename, evs); + if (err) + return command_fail(cmd, PLUGIN_ERROR, + "Unable to create csv file: %s", + err); + + res = jsonrpc_stream_success(cmd); + json_add_string(res, "csv_file", filename); + json_add_string(res, "csv_format", csv_fmt->fmt_name); + return command_finished(cmd, res); +} + static struct command_result *json_list_income(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -1310,6 +1368,16 @@ static const struct plugin_command commands[] = { "List all events for this node that impacted income", json_list_income }, + { + "dumpincomecsv", + "bookkeeping", + "Print out all the income events to a csv file in " + " {csv_format", + "Dump income statment data to {csv_file} in {csv_format}." + " Optionally, {consolidate_fee}s into single entries" + " (default: true)", + json_dump_income + }, }; static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 18866a89f64d..d5d02d347fd5 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -1,11 +1,15 @@ #include "config.h" +#include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include @@ -13,6 +17,9 @@ #include #include #include +#include + +#define ONCHAIN_FEE "onchain_fee" static struct account *get_account(struct account **accts, u64 acct_db_id) @@ -83,7 +90,7 @@ static struct income_event *onchainfee_to_income(const tal_t *ctx, struct income_event *inc = tal(ctx, struct income_event); inc->acct_name = tal_strdup(inc, fee->acct_name); - inc->tag = tal_fmt(inc, "%s", "onchain_fee"); + inc->tag = tal_fmt(inc, "%s", ONCHAIN_FEE); /* We swap these, as they're actually opposite */ inc->credit = fee->debit; inc->debit = fee->credit; @@ -371,3 +378,412 @@ void json_add_income_event(struct json_stream *out, struct income_event *ev) json_object_end(out); } + +const char *csv_filename(const tal_t *ctx, const struct csv_fmt *fmt) +{ + return tal_fmt(ctx, "cln_incomestmt_%s_%zu.csv", + fmt->fmt_name, + time_now().ts.tv_sec); +} + +static char *convert_asset_type(struct income_event *ev) +{ + /* We use the bech32 human readable part which is "bc" + * for mainnet -> map to 'BTC' for cointracker */ + if (streq(ev->currency, "bc")) + return "btc"; + + return ev->currency; +} + +static void cointrack_header(FILE *csvf) +{ + fprintf(csvf, + "Date" + ",Received Quantity" + ",Received Currency" + ",Sent Quantity" + ",Sent Currency" + ",Fee Amount" + ",Fee Currency" + ",Tag" + ",Account"); +} + +static char *income_event_cointrack_type(const struct income_event *ev) +{ + /* ['gift', 'lost', 'mined', 'airdrop', 'payment', + * 'fork', 'donation', 'staked'] */ + if (!amount_msat_zero(ev->debit) + && streq(ev->tag, "penalty")) + return "lost"; + + if (streq(ev->tag, "invoice") + || streq(ev->tag, "routed")) + return "payment"; + + /* Default to empty */ + return ""; +} + +static void cointrack_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) +{ + /* Date mm/dd/yyyy HH:MM:SS UTC */ + time_t tv; + tv = ev->timestamp; + char timebuf[sizeof("mm/dd/yyyy HH:MM:SS")]; + strftime(timebuf, sizeof(timebuf), "%m/%d/%Y %T", gmtime(&tv)); + fprintf(csvf, "%s", timebuf); + fprintf(csvf, ","); + + /* Received Quantity + Received Currency */ + if (!amount_msat_zero(ev->credit)) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->credit, false)); + fprintf(csvf, ","); + fprintf(csvf, "%s", convert_asset_type(ev)); + } else + fprintf(csvf, ","); + + fprintf(csvf, ","); + + /* "Sent Quantity,Sent Currency," */ + if (!amount_msat_zero(ev->debit) + && !streq(ev->tag, ONCHAIN_FEE)) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + fprintf(csvf, ","); + fprintf(csvf, "%s", convert_asset_type(ev)); + } else + fprintf(csvf, ","); + + fprintf(csvf, ","); + + /* "Fee Amount,Fee Currency," */ + if (!amount_msat_zero(ev->debit) + && streq(ev->tag, ONCHAIN_FEE)) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + fprintf(csvf, ","); + fprintf(csvf, "%s", convert_asset_type(ev)); + } else + fprintf(csvf, ","); + + fprintf(csvf, ","); + + /* Tag */ + fprintf(csvf, "%s", income_event_cointrack_type(ev)); + fprintf(csvf, ","); + + /* Account */ + fprintf(csvf, "%s", ev->acct_name); +} + +static void koinly_header(FILE *csvf) +{ + fprintf(csvf, + "Date" + ",Sent Amount" + ",Sent Currency" + ",Received Amount" + ",Received Currency" + ",Fee Amount" + ",Fee Currency" + ",Label" + ",Description" + ",TxHash"); +} + +static void koinly_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) +{ + /* Date */ + time_t tv; + tv = ev->timestamp; + /* 2018-01-01 14:25 UTC */ + char timebuf[sizeof("yyyy-mm-dd HH:MM UTC")]; + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M UTC", gmtime(&tv)); + fprintf(csvf, "%s", timebuf); + fprintf(csvf, ","); + + /* "Sent Amount,Sent Currency," */ + if (!amount_msat_zero(ev->debit) + && !streq(ev->tag, ONCHAIN_FEE)) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + fprintf(csvf, ","); + fprintf(csvf, "%s", convert_asset_type(ev)); + } else + fprintf(csvf, ","); + + fprintf(csvf, ","); + + /* Received Amount, Received Currency */ + if (!amount_msat_zero(ev->credit)) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->credit, false)); + fprintf(csvf, ","); + fprintf(csvf, "%s", convert_asset_type(ev)); + } else + fprintf(csvf, ","); + + fprintf(csvf, ","); + + + /* "Fee Amount,Fee Currency," */ + if (!amount_msat_zero(ev->debit) + && streq(ev->tag, ONCHAIN_FEE)) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + fprintf(csvf, ","); + fprintf(csvf, "%s", convert_asset_type(ev)); + } else + fprintf(csvf, ","); + + fprintf(csvf, ","); + + /* Label */ + fprintf(csvf, "%s", ev->tag); + fprintf(csvf, ","); + + /* Description */ + fprintf(csvf, "%s: account %s", ev->tag, ev->acct_name); + fprintf(csvf, ","); + + /* TxHash */ + if (ev->txid) + fprintf(csvf, "%s", + type_to_string(ctx, struct bitcoin_txid, ev->txid)); + else if (ev->payment_id) + fprintf(csvf, "%s", + type_to_string(ctx, struct sha256, ev->payment_id)); + else if (ev->outpoint) + fprintf(csvf, "%s", + type_to_string(ctx, struct bitcoin_outpoint, + ev->outpoint)); +} + +static void harmony_header(FILE *csvf) +{ + /* Type Declaration */ + fprintf(csvf, "HarmonyCSV v0.2"); + /* Add 9 extra blank cols + * (so ea row has same # cols, which is csv spec) */ + fprintf(csvf, ",,,,,,,,,\n"); + + /* Header Declarations */ + fprintf(csvf, "Provenance,cln-bookkeeper"); + /* Only 8 extra blank cols */ + fprintf(csvf, ",,,,,,,,\n"); + + /* Blank Line */ + fprintf(csvf, ",,,,,,,,,\n"); + /* Entries */ + fprintf(csvf, + "Timestamp" /* ISO-8601 */ + ",Venue" + ",Type" + ",Amount" + ",Asset" /* currency */ + ",Transaction ID" + ",Order ID" /* payment hash, if any */ + ",Account" + ",Network ID" /* outpoint */ + ",Note" /* tag */ + ); +} + +static char *income_event_harmony_type(const struct income_event *ev) +{ + /* From the v0.2 version of types:subtypes + * https://github.com/harmony-csv/harmony#entry-types */ + if (streq(ONCHAIN_FEE, ev->tag)) + return "fee:network"; + + if (!amount_msat_zero(ev->credit)) { + if (streq(WALLET_ACCT, ev->acct_name)) + return tal_fmt(ev, "transfer:%s", ev->tag); + + return tal_fmt(ev, "income:%s", ev->tag); + } + + /* Ok otherwise it's a debit */ + if (streq("penalty", ev->tag)) { + return "loss:penalty"; + } + if (streq(WALLET_ACCT, ev->acct_name)) + return tal_fmt(ev, "transfer:%s", ev->tag); + + /* FIXME: add "fee:transfer" to invoice routing fees */ + + return tal_fmt(ev, "expense:%s", ev->tag); +} + +static void harmony_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) +{ + time_t tv; + tv = ev->timestamp; + /* datefmt: ISO-8601 */ + char timebuf[sizeof("yyyy-mm-ddTHH:MM:SSZ")]; + strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%TZ", gmtime(&tv)); + fprintf(csvf, "%s", timebuf); + fprintf(csvf, ","); + + /* ",Venue" */ + /* FIXME: use node_id ? */ + fprintf(csvf, "cln"); + fprintf(csvf, ","); + + /* ",Type" */ + fprintf(csvf, "%s", income_event_harmony_type(ev)); + fprintf(csvf, ","); + + /* ",Amount" */ + if (!amount_msat_zero(ev->debit)) { + /* Debits are negative */ + fprintf(csvf, "-"); + fprintf(csvf, "%s", + fmt_amount_msat_btc(ctx, ev->debit, false)); + } else + fprintf(csvf, "%s", + fmt_amount_msat_btc(ctx, ev->credit, false)); + + fprintf(csvf, ","); + + /* ",Asset" */ + fprintf(csvf, "%s", convert_asset_type(ev)); + fprintf(csvf, ","); + + /* ",Transaction ID" */ + /* Some of this data is duplicated in other fields. + * We don't have a standard 'txid' for every event though */ + if (ev->txid) + fprintf(csvf, "%s", + type_to_string(ctx, struct bitcoin_txid, ev->txid)); + else if (ev->payment_id) + fprintf(csvf, "%s", + type_to_string(ctx, struct sha256, ev->payment_id)); + else if (ev->outpoint) + fprintf(csvf, "%s", + type_to_string(ctx, struct bitcoin_outpoint, + ev->outpoint)); + fprintf(csvf, ","); + + /* ",Order ID" payment hash, if any */ + if (ev->payment_id) + fprintf(csvf, "%s", + type_to_string(ctx, struct sha256, ev->payment_id)); + fprintf(csvf, ","); + + /* ",Account" */ + fprintf(csvf, "%s", ev->acct_name); + fprintf(csvf, ","); + + /* ",Network ID" outpoint */ + if (ev->outpoint) + fprintf(csvf, "%s", + type_to_string(ctx, struct bitcoin_outpoint, + ev->outpoint)); + fprintf(csvf, ","); + + /* ",Note" account tag */ + fprintf(csvf, "%s %s", ev->acct_name, ev->tag); +} + +static void quickbooks_header(FILE *csvf) +{ + fprintf(csvf, + "Date" + ",Description" + ",Credit" + ",Debit" + ); +} + +static void quickbooks_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) +{ + /* "Make sure the dates are in one format. + * We recommend you use: dd/mm/yyyy." + * from: https://quickbooks.intuit.com/learn-support/global/bank-transactions/import-bank-transactions-using-excel-csv-files/00/381530 */ + time_t tv; + tv = ev->timestamp; + /* datefmt: dd/mm/yyyy */ + char timebuf[sizeof("dd/mm/yyyy")]; + strftime(timebuf, sizeof(timebuf), "%d/%m/%Y", gmtime(&tv)); + fprintf(csvf, "%s", timebuf); + fprintf(csvf, ","); + + /* Description */ + fprintf(csvf, "%s (%s) in %s", + ev->tag, ev->acct_name, ev->currency); + fprintf(csvf, ","); + + /* Credit */ + if (!amount_msat_zero(ev->credit)) + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->credit, false)); + + fprintf(csvf, ","); + + /* Debit */ + if (!amount_msat_zero(ev->debit)) + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); +} + +const struct csv_fmt csv_fmts[] = { + { + .fmt_name = "cointracker", + .emit_header = cointrack_header, + .emit_entry = cointrack_entry, + }, + { + .fmt_name = "koinly", + .emit_header = koinly_header, + .emit_entry = koinly_entry, + }, + { + .fmt_name = "harmony", + .emit_header = harmony_header, + .emit_entry = harmony_entry, + }, + { + .fmt_name = "quickbooks", + .emit_header = quickbooks_header, + .emit_entry = quickbooks_entry, + }, +}; + +const struct csv_fmt *csv_match_token(const char *buffer, const jsmntok_t *tok) +{ + for (size_t i = 0; i < ARRAY_SIZE(csv_fmts); i++) { + if (json_tok_streq(buffer, tok, csv_fmts[i].fmt_name)) + return &csv_fmts[i]; + } + + return NULL; +} + +const char *csv_list_fmts(const tal_t *ctx) +{ + char *fmtlist = tal(ctx, char); + for (size_t i = 0; i < ARRAY_SIZE(csv_fmts); i++) { + if (i > 0) + tal_append_fmt(&fmtlist, ","); + tal_append_fmt(&fmtlist, "\"%s\"", csv_fmts[i].fmt_name); + } + return (const char *) fmtlist; +} + + +char *csv_print_income_events(const tal_t *ctx, + const struct csv_fmt *csvfmt, + const char *filename, + struct income_event **evs) +{ + FILE *csvf; + + csvf = fopen(filename, "w"); + if (!csvf) + return tal_fmt(ctx, "Failed to open csv file %s", filename); + + csvfmt->emit_header(csvf); + for (size_t i = 0; i < tal_count(evs); i++) { + fprintf(csvf, "\n"); + csvfmt->emit_entry(ctx, csvf, evs[i]); + } + + fclose(csvf); + return NULL; +} diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h index 50b42b4ef5b2..3092e513bd79 100644 --- a/plugins/bkpr/incomestmt.h +++ b/plugins/bkpr/incomestmt.h @@ -3,6 +3,7 @@ #include "config.h" #include +#include struct income_event { char *acct_name; @@ -17,6 +18,13 @@ struct income_event { struct sha256 *payment_id; }; +/* Each csv format has a header and a 'row print' function */ +struct csv_fmt { + char *fmt_name; + void (*emit_header)(FILE *); + void (*emit_entry)(const tal_t *, FILE *, struct income_event *); +}; + /* List all the events that are income related (gain/loss) */ struct income_event **list_income_events_all(const tal_t *ctx, struct db *db, bool consolidate_fees); @@ -32,4 +40,17 @@ struct income_event **list_income_events(const tal_t *ctx, /* Given an event and a json_stream, add a new event object to the stream */ void json_add_income_event(struct json_stream *str, struct income_event *ev); +char *csv_print_income_events(const tal_t *ctx, + const struct csv_fmt *csvfmt, + const char *filename, + struct income_event **evs); + +const struct csv_fmt *csv_match_token(const char *buffer, const jsmntok_t *tok); + +/* Returns concatenated string of all available fmts */ +const char *csv_list_fmts(const tal_t *ctx); + +/* Generic income statement filename generator */ +const char *csv_filename(const tal_t *ctx, const struct csv_fmt *fmt); + #endif /* LIGHTNING_PLUGINS_BKPR_INCOMESTMT_H */ From 12b5c06219cd09801349a90787b6fe6b747b4d4f Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1165/1530] bkpr: add 'start_time' and 'end_time' to `listincome` Add ability to filter results by timestamp. Note that "start_time" is everything *after* that timestamp; and "end_time" is everything *up to and including* that timestamp. --- plugins/bkpr/bookkeeper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 565384638300..6253345050fc 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -103,16 +103,20 @@ static struct command_result *json_list_income(struct command *cmd, struct json_stream *res; struct income_event **evs; bool *consolidate_fees; + u64 *start_time, *end_time; if (!param(cmd, buf, params, p_opt_def("consolidate_fees", param_bool, &consolidate_fees, true), + p_opt_def("start_time", param_u64, &start_time, 0), + p_opt_def("end_time", param_u64, &end_time, SQLITE_MAX_UINT), NULL)) return command_param_failed(); /* Ok, go find me some income events! */ db_begin_transaction(db); - evs = list_income_events_all(cmd, db, *consolidate_fees); + evs = list_income_events(cmd, db, *start_time, *end_time, + *consolidate_fees); db_commit_transaction(db); res = jsonrpc_stream_success(cmd); From c05900c6767c476fe40a1aac318277a9358132b3 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1166/1530] bkpr: add option --bookkeeper-dir Allow setting custom directory for bookkeeper's database and default directory for any csv file creation --- doc/lightning-listconfigs.7 | 4 +++- doc/lightning-listconfigs.7.md | 3 ++- doc/lightningd-config.5.md | 4 ++++ doc/schemas/listconfigs.schema.json | 4 ++++ plugins/bkpr/bookkeeper.c | 23 +++++++++++++++++++++-- 5 files changed, 34 insertions(+), 4 deletions(-) diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index 2d5e89bbd00c..7b0af2ed4a27 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -78,6 +78,8 @@ On success, an object is returned, containing: .RE +.IP \[bu] +\fBbookkeeper-dir\fR (string, optional): \fBbookkeeper-dir\fR field from config or cmdline, or default .IP \[bu] \fBalways-use-proxy\fR (boolean, optional): \fBalways-use-proxy\fR field from config or cmdline, or default .IP \[bu] @@ -284,4 +286,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:7ac9ef3477f64fd3a4181cb27b8810ecf2c1a082688180716adfe79843ab09aa +\" SHA256STAMP:b1148d7469f7bac293482be6f501031e4cac02f9bc113cf372a31747eb7a6055 diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index f42a96a500b9..2405cf602cbc 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -50,6 +50,7 @@ On success, an object is returned, containing: - **rpc-file** (string, optional): `rpc-file` field from config or cmdline, or default - **disable-plugin** (array of strings, optional): - `disable-plugin` field from config or cmdline +- **bookkeeper-dir** (string, optional): `bookkeeper-dir` field from config or cmdline, or default - **always-use-proxy** (boolean, optional): `always-use-proxy` field from config or cmdline, or default - **daemon** (boolean, optional): `daemon` field from config or cmdline, or default - **wallet** (string, optional): `wallet` field from config or cmdline, or default @@ -212,4 +213,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:4645e3f14c53ccc23b2ed42c2886064e5d718a8eef5e6bd6dd3a932cadfd8e8f) +[comment]: # ( SHA256STAMP:4a4cacd309d9593b45cb27563c25d9d3df7df8aef2b281145abeee27eae57fa9) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 433c38018da5..eb8bae77a6c1 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -217,6 +217,10 @@ authenticate with username `user` and password `pass`, and then use the database `db_name`. The database must exist, but the schema will be managed automatically by `lightningd`. + **bookkeeper-dir**=*DIR* +Directory to keep the accounts.sqlite3 database file in. +Defaults to lightning-dir. + **encrypted-hsm** If set, you will be prompted to enter a password used to encrypt the `hsm_secret`. Note that once you encrypt the `hsm_secret` this option will be mandatory for diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index c3215e8e9ed2..46feb13caa3c 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -93,6 +93,10 @@ "description": "`disable-plugin` field from config or cmdline" } }, + "bookkeeper-dir": { + "type": "string", + "description": "`bookkeeper-dir` field from config or cmdline, or default" + }, "always-use-proxy": { "type": "boolean", "description": "`always-use-proxy` field from config or cmdline, or default" diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 6253345050fc..1f2defd473ea 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,8 @@ #include #include #include +#include +#include #define CHAIN_MOVE "chain_mvt" #define CHANNEL_MOVE "channel_mvt" @@ -25,8 +28,8 @@ /* The database that we store all the accounting data in */ static struct db *db ; -// FIXME: make relative to directory we're loaded into static char *db_dsn = "sqlite3://accounts.sqlite3"; +static char *datadir; static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, struct bitcoin_txid *txid) @@ -1386,7 +1389,17 @@ static const struct plugin_command commands[] = { static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) { - // FIXME: pass in database DSN as an option?? + /* Switch to bookkeeper-dir, if specified */ + if (datadir && chdir(datadir) != 0) { + if (mkdir(datadir, 0700) != 0 && errno != EEXIST) + plugin_err(p, + "Unable to create 'bookkeeper-dir'=%s", + datadir); + if (chdir(datadir) != 0) + plugin_err(p, + "Unable to switch to 'bookkeeper-dir'=%s", + datadir); + } db = notleak(db_setup(p, p, db_dsn)); return NULL; @@ -1396,11 +1409,17 @@ int main(int argc, char *argv[]) { setup_locale(); + /* No datadir is default */ + datadir = NULL; plugin_main(argv, init, PLUGIN_STATIC, true, NULL, commands, ARRAY_SIZE(commands), notifs, ARRAY_SIZE(notifs), NULL, 0, NULL, 0, + plugin_option("bookkeeper-dir", + "string", + "Location for bookkeeper records.", + charp_option, &datadir), NULL); return 0; } From ee47715a1b0981c6103b398cd16dfc046a69e404 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1167/1530] bkpr: command to calculate some APYs/stats on channel routing fees Computes some stats from the net routed fees etc ``` { "channel_apys": [ { "account": "7de277250f8003c35378c1eb20aacf8fa6a69dd4bb22077266329a9ad812cd5d", "routed_out": "2000msat", "routed_in": "125100101msat", "lease_fee_paid": "0msat", "lease_fee_earned": "670000msat", "pushed_out": "0msat", "pushed_in": "0msat", "our_start_balance": "100670000msat", "channel_start_balance": "1100670000msat", "fees_out": "0msat", "fees_in": "10100101msat", "utilization_out": "0.0002%", "utilization_out_initial": "0.0020%", "utilization_in": "11.3658%", "utilization_in_initial": "12.5100%", "apy_out": "0.0000%", "apy_out_initial": "0.0000%", "apy_in": "3203.3924%", "apy_in_initial": "3525.8779%", "apy_total": "3203.3924%", "apy_total_initial": "3203.3924%", "apy_lease": "8.7014%" }, { "account": "b71937a02eb7694d946c311297ca2da454057c5c3e97ac67500739cc2afbc0c5", "routed_out": "110000000msat", "routed_in": "0msat", "lease_fee_paid": "670000msat", "lease_fee_earned": "0msat", "pushed_out": "0msat", "pushed_in": "0msat", "our_start_balance": "4000000000msat", "channel_start_balance": "4100670000msat", "fees_out": "10100101msat", "fees_in": "0msat", "utilization_out": "2.6825%", "utilization_out_initial": "2.7500%", "utilization_in": "0.0000%", "utilization_in_initial": "0.0000%", "apy_out": "2579.4892%", "apy_out_initial": "2644.4084%", "apy_in": "0.0000%", "apy_in_initial": "0.0000%", "apy_total": "2579.4892%", "apy_total_initial": "2579.4892%" }, { "account": "net", "routed_out": "110002000msat", "routed_in": "125100101msat", "lease_fee_paid": "670000msat", "lease_fee_earned": "670000msat", "pushed_out": "0msat", "pushed_in": "0msat", "our_start_balance": "4100670000msat", "channel_start_balance": "5201340000msat", "fees_out": "10100101msat", "fees_in": "10100101msat", "utilization_out": "2.1149%", "utilization_out_initial": "2.6825%", "utilization_in": "2.4052%", "utilization_in_initial": "11.3658%", "apy_out": "677.8788%", "apy_out_initial": "859.8297%", "apy_in": "677.8788%", "apy_in_initial": "3203.3924%", "apy_total": "1355.7575%", "apy_total_initial": "1355.7575%", "apy_lease": "0.2122%" } ] } ``` --- plugins/bkpr/Makefile | 2 + plugins/bkpr/bookkeeper.c | 57 +++++ plugins/bkpr/channelsapy.c | 413 +++++++++++++++++++++++++++++++++++++ plugins/bkpr/channelsapy.h | 43 ++++ 4 files changed, 515 insertions(+) create mode 100644 plugins/bkpr/channelsapy.c create mode 100644 plugins/bkpr/channelsapy.h diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index f7c53e1a02b7..7e7730e79f17 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -6,6 +6,7 @@ BOOKKEEPER_PLUGIN_SRC := \ plugins/bkpr/bookkeeper.c \ plugins/bkpr/chain_event.c \ plugins/bkpr/channel_event.c \ + plugins/bkpr/channelsapy.c \ plugins/bkpr/db.c \ plugins/bkpr/incomestmt.c \ plugins/bkpr/onchain_fee.c \ @@ -21,6 +22,7 @@ BOOKKEEPER_HEADER := \ plugins/bkpr/account_entry.h \ plugins/bkpr/chain_event.h \ plugins/bkpr/channel_event.h \ + plugins/bkpr/channelsapy.h \ plugins/bkpr/db.h \ plugins/bkpr/incomestmt.h \ plugins/bkpr/onchain_fee.h \ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 1f2defd473ea..a9ebdd65c516 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,55 @@ static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, return NULL; } +static struct command_result *json_channel_apy(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct json_stream *res; + struct channel_apy **apys, *net_apys; + u64 *start_time, *end_time; + + if (!param(cmd, buf, params, + p_opt_def("start_time", param_u64, &start_time, 0), + p_opt_def("end_time", param_u64, &end_time, SQLITE_MAX_UINT), + NULL)) + return command_param_failed(); + + /* Get the income events */ + db_begin_transaction(db); + apys = compute_channel_apys(cmd, db, *start_time, *end_time, + /* FIXME: current blockheight */ + 1414); + db_commit_transaction(db); + + /* Setup the net_apys entry */ + net_apys = new_channel_apy(cmd); + net_apys->end_blockheight = 0; + net_apys->start_blockheight = UINT_MAX; + net_apys->our_start_bal = AMOUNT_MSAT(0); + net_apys->total_start_bal = AMOUNT_MSAT(0); + + res = jsonrpc_stream_success(cmd); + json_array_start(res, "channel_apys"); + for (size_t i = 0; i < tal_count(apys); i++) { + json_add_channel_apy(res, apys[i]); + + /* Add to net/rollup APY */ + if (!channel_apy_sum(net_apys, apys[i])) + return command_fail(cmd, PLUGIN_ERROR, + "Overflow adding APYs net"); + } + + /* Append a net/rollup entry */ + if (!amount_msat_zero(net_apys->total_start_bal)) { + net_apys->acct_name = tal_fmt(net_apys, "net"); + json_add_channel_apy(res, net_apys); + } + json_array_end(res); + + return command_finished(cmd, res); +} + static struct command_result *param_csv_format(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct csv_fmt **csv_fmt) @@ -1385,6 +1435,13 @@ static const struct plugin_command commands[] = { " (default: true)", json_dump_income }, + { + "channelsapy", + "bookkeeping", + "Stats on channel fund usage", + "Print out stats on chanenl fund usage", + json_channel_apy + }, }; static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) diff --git a/plugins/bkpr/channelsapy.c b/plugins/bkpr/channelsapy.c new file mode 100644 index 000000000000..429fbffcbdaf --- /dev/null +++ b/plugins/bkpr/channelsapy.c @@ -0,0 +1,413 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLOCK_YEAR 52364 + +static int cmp_channel_event_acct(struct channel_event *const *ev1, + struct channel_event *const *ev2, + void *unused UNUSED) +{ + if ((*ev1)->acct_db_id < (*ev2)->acct_db_id) + return -1; + else if ((*ev1)->acct_db_id > (*ev2)->acct_db_id) + return 1; + return 0; +} + +static int cmp_acct(struct account *const *a1, + struct account *const *a2, + void *unused UNUSED) +{ + if ((*a1)->db_id < (*a2)->db_id) + return -1; + else if ((*a1)->db_id > (*a2)->db_id) + return 1; + return 0; +} + +struct channel_apy *new_channel_apy(const tal_t *ctx) +{ + struct channel_apy *apy = tal(ctx, struct channel_apy); + + apy->routed_in = AMOUNT_MSAT(0); + apy->routed_out = AMOUNT_MSAT(0); + apy->fees_in = AMOUNT_MSAT(0); + apy->fees_out = AMOUNT_MSAT(0); + apy->push_in = AMOUNT_MSAT(0); + apy->push_out = AMOUNT_MSAT(0); + apy->lease_in = AMOUNT_MSAT(0); + apy->lease_out = AMOUNT_MSAT(0); + return apy; +} + +bool channel_apy_sum(struct channel_apy *sum_apy, + const struct channel_apy *entry) +{ + bool ok; + ok = amount_msat_add(&sum_apy->routed_in, + sum_apy->routed_in, + entry->routed_in); + ok &= amount_msat_add(&sum_apy->routed_out, + sum_apy->routed_out, + entry->routed_out); + ok &= amount_msat_add(&sum_apy->fees_in, + sum_apy->fees_in, + entry->fees_in); + ok &= amount_msat_add(&sum_apy->fees_out, + sum_apy->fees_out, + entry->fees_out); + ok &= amount_msat_add(&sum_apy->push_in, + sum_apy->push_in, + entry->push_in); + ok &= amount_msat_add(&sum_apy->push_out, + sum_apy->push_out, + entry->push_out); + ok &= amount_msat_add(&sum_apy->lease_in, + sum_apy->lease_in, + entry->lease_in); + ok &= amount_msat_add(&sum_apy->lease_out, + sum_apy->lease_out, + entry->lease_out); + + ok &= amount_msat_add(&sum_apy->our_start_bal, + sum_apy->our_start_bal, + entry->our_start_bal); + + ok &= amount_msat_add(&sum_apy->total_start_bal, + sum_apy->total_start_bal, + entry->total_start_bal); + + if (sum_apy->start_blockheight > entry->start_blockheight) + sum_apy->start_blockheight = entry->start_blockheight; + + if (sum_apy->end_blockheight < entry->end_blockheight) + sum_apy->end_blockheight = entry->end_blockheight; + + return ok; +} + +static struct account *search_account(struct account **accts, u64 acct_id) +{ + for (size_t i = 0; i < tal_count(accts); i++) { + if (accts[i]->db_id == acct_id) + return accts[i]; + } + + return NULL; +} + +static void fillin_apy_acct_details(struct db *db, + const struct account *acct, + u32 current_blockheight, + struct channel_apy *apy) +{ + struct chain_event *ev; + bool ok; + + apy->acct_name = tal_strdup(apy, acct->name); + + assert(acct->open_event_db_id); + ev = find_chain_event_by_id(acct, db, *acct->open_event_db_id); + assert(ev); + + apy->start_blockheight = ev->blockheight; + apy->our_start_bal = ev->credit; + apy->total_start_bal = ev->output_value; + + /* if this account is closed, add closing blockheight */ + if (acct->closed_event_db_id) { + ev = find_chain_event_by_id(acct, db, + *acct->closed_event_db_id); + assert(ev); + apy->end_blockheight = ev->blockheight; + } else + apy->end_blockheight = current_blockheight; + + /* If there is any push_out or lease_fees_out, we subtract + * from starting balance */ + ok = amount_msat_sub(&apy->our_start_bal, apy->our_start_bal, + apy->push_out); + assert(ok); + ok = amount_msat_sub(&apy->our_start_bal, apy->our_start_bal, + apy->lease_out); + assert(ok); + + /* we add values in to starting balance */ + ok = amount_msat_add(&apy->our_start_bal, apy->our_start_bal, + apy->push_in); + assert(ok); + ok = amount_msat_add(&apy->our_start_bal, apy->our_start_bal, + apy->lease_in); + assert(ok); +} + +struct channel_apy **compute_channel_apys(const tal_t *ctx, struct db *db, + u64 start_time, + u64 end_time, + u32 current_blockheight) +{ + struct channel_event **evs; + struct channel_apy *apy, **apys; + struct account *acct, **accts; + + evs = list_channel_events_timebox(ctx, db, start_time, end_time); + accts = list_accounts(ctx, db); + + apys = tal_arr(ctx, struct channel_apy *, 0); + + /* Sort events by acct_name */ + asort(evs, tal_count(evs), cmp_channel_event_acct, NULL); + /* Sort accounts by name also */ + asort(accts, tal_count(accts), cmp_acct, NULL); + + acct = NULL; + apy = new_channel_apy(apys); + for (size_t i = 0; i < tal_count(evs); i++) { + struct channel_event *ev = evs[i]; + bool ok; + + if (!acct || acct->db_id != ev->acct_db_id) { + if (acct && is_channel_account(acct)) { + fillin_apy_acct_details(db, acct, + current_blockheight, + apy); + /* Save current apy, make new */ + tal_arr_expand(&apys, apy); + apy = new_channel_apy(apys); + } + acct = search_account(accts, ev->acct_db_id); + assert(acct); + } + + /* No entry for external or wallet accts */ + if (!is_channel_account(acct)) + continue; + + /* Accumulate routing stats */ + if (streq("routed", ev->tag) + || streq("invoice", ev->tag)) { + ok = amount_msat_add(&apy->routed_in, + apy->routed_in, + ev->credit); + assert(ok); + ok = amount_msat_add(&apy->routed_out, + apy->routed_out, + ev->debit); + assert(ok); + + /* No fees for invoices */ + if (streq("invoice", ev->tag)) + continue; + + if (!amount_msat_zero(ev->credit)) + ok = amount_msat_add(&apy->fees_in, + apy->fees_in, + ev->fees); + else + ok = amount_msat_add(&apy->fees_out, + apy->fees_out, + ev->fees); + assert(ok); + } + else if (streq("pushed", ev->tag)) { + ok = amount_msat_add(&apy->push_in, + apy->push_in, + ev->credit); + assert(ok); + ok = amount_msat_add(&apy->push_out, + apy->push_out, + ev->debit); + assert(ok); + } else if (streq("lease_fee", ev->tag)) { + ok = amount_msat_add(&apy->lease_in, + apy->lease_in, + ev->credit); + assert(ok); + ok = amount_msat_add(&apy->lease_out, + apy->lease_out, + ev->debit); + assert(ok); + } + + /* Note: we ignore 'journal_entry's because there's no + * relevant fee data attached to them */ + } + + if (acct && is_channel_account(acct)) { + fillin_apy_acct_details(db, acct, + current_blockheight, + apy); + /* Save current apy, make new */ + tal_arr_expand(&apys, apy); + } + + return apys; +} + +WARN_UNUSED_RESULT static bool calc_apy(struct amount_msat earned, + struct amount_msat capital, + u32 blocks_elapsed, + double *result) +{ + double apy; + + assert(!amount_msat_zero(capital)); + assert(blocks_elapsed > 0); + + apy = amount_msat_ratio(earned, capital) * BLOCK_YEAR / blocks_elapsed; + + /* convert to percent */ + apy *= 100; + + /* If mantissa is < 64 bits, a naive "if (scaled > + * UINT64_MAX)" doesn't work. Stick to powers of 2. */ + if (apy >= (double)((u64)1 << 63) * 2) + return false; + + *result = apy; + return true; +} + +void json_add_channel_apy(struct json_stream *res, + const struct channel_apy *apy) +{ + bool ok; + u32 blocks_elapsed; + double apy_result, utilization; + struct amount_msat total_fees, their_start_bal; + + ok = amount_msat_sub(&their_start_bal, apy->total_start_bal, + apy->our_start_bal); + assert(ok); + + json_object_start(res, NULL); + + json_add_string(res, "account", apy->acct_name); + + json_add_amount_msat_only(res, "routed_out", apy->routed_out); + json_add_amount_msat_only(res, "routed_in", apy->routed_in); + json_add_amount_msat_only(res, "lease_fee_paid", apy->lease_out); + json_add_amount_msat_only(res, "lease_fee_earned", apy->lease_in); + json_add_amount_msat_only(res, "pushed_out", apy->push_out); + json_add_amount_msat_only(res, "pushed_in", apy->push_in); + + json_add_amount_msat_only(res, "our_start_balance", apy->our_start_bal); + json_add_amount_msat_only(res, "channel_start_balance", + apy->total_start_bal); + + ok = amount_msat_add(&total_fees, apy->fees_in, apy->fees_out); + assert(ok); + json_add_amount_msat_only(res, "fees_out", apy->fees_out); + json_add_amount_msat_only(res, "fees_in", apy->fees_in); + + /* utilization (out): routed_out/total_balance */ + assert(!amount_msat_zero(apy->total_start_bal)); + utilization = amount_msat_ratio(apy->routed_out, apy->total_start_bal); + json_add_string(res, "utilization_out", + tal_fmt(apy, "%.4f%%", utilization * 100)); + + if (!amount_msat_zero(apy->our_start_bal)) { + utilization = amount_msat_ratio(apy->routed_out, + apy->our_start_bal); + json_add_string(res, "utilization_out_initial", + tal_fmt(apy, "%.4f%%", utilization * 100)); + } + + /* utilization (in): routed_in/total_balance */ + utilization = amount_msat_ratio(apy->routed_in, apy->total_start_bal); + json_add_string(res, "utilization_in", + tal_fmt(apy, "%.4f%%", utilization * 100)); + + if (!amount_msat_zero(their_start_bal)) { + utilization = amount_msat_ratio(apy->routed_in, + their_start_bal); + json_add_string(res, "utilization_in_initial", + tal_fmt(apy, "%.4f%%", utilization * 100)); + } + + blocks_elapsed = apy->end_blockheight - apy->start_blockheight; + assert(blocks_elapsed > 0); + + /* APY (outbound) */ + ok = calc_apy(apy->fees_out, apy->total_start_bal, + blocks_elapsed, &apy_result); + assert(ok); + json_add_string(res, "apy_out", tal_fmt(apy, "%.4f%%", apy_result)); + + /* APY (outbound, initial) */ + if (!amount_msat_zero(apy->our_start_bal)) { + ok = calc_apy(apy->fees_out, apy->our_start_bal, + blocks_elapsed, &apy_result); + assert(ok); + json_add_string(res, "apy_out_initial", + tal_fmt(apy, "%.4f%%", apy_result)); + } + + /* APY (inbound) */ + ok = calc_apy(apy->fees_in, apy->total_start_bal, + blocks_elapsed, &apy_result); + assert(ok); + json_add_string(res, "apy_in", tal_fmt(apy, "%.4f%%", apy_result)); + + if (!amount_msat_zero(their_start_bal)) { + ok = calc_apy(apy->fees_in, their_start_bal, + blocks_elapsed, &apy_result); + assert(ok); + json_add_string(res, "apy_in_initial", + tal_fmt(apy, "%.4f%%", apy_result)); + } + + /* APY (total) */ + ok = calc_apy(total_fees, apy->total_start_bal, + blocks_elapsed, &apy_result); + assert(ok); + json_add_string(res, "apy_total", tal_fmt(apy, "%.4f%%", apy_result)); + + if (!amount_msat_zero(apy->our_start_bal)) { + ok = calc_apy(total_fees, apy->total_start_bal, + blocks_elapsed, &apy_result); + assert(ok); + json_add_string(res, "apy_total_initial", + tal_fmt(apy, "%.4f%%", apy_result)); + } + + /* If you earned fees for leasing funds, calculate APY + * Note that this is a bit higher than it *should* be, + * given that the onchainfees are partly covered here */ + if (!amount_msat_zero(apy->lease_in)) { + struct amount_msat start_no_lease_in; + + /* We added the lease in to the starting balance, so we + * should subtract it out again before finding APY */ + ok = amount_msat_sub(&start_no_lease_in, + apy->our_start_bal, + apy->lease_in); + assert(ok); + ok = calc_apy(apy->lease_in, start_no_lease_in, + /* we use the lease rate duration here! */ + LEASE_RATE_DURATION, &apy_result); + assert(ok); + json_add_string(res, "apy_lease", + tal_fmt(apy, "%.4f%%", apy_result)); + } + + json_object_end(res); +} + diff --git a/plugins/bkpr/channelsapy.h b/plugins/bkpr/channelsapy.h new file mode 100644 index 000000000000..1e4ccef2d641 --- /dev/null +++ b/plugins/bkpr/channelsapy.h @@ -0,0 +1,43 @@ +#ifndef LIGHTNING_PLUGINS_BKPR_CHANNELSAPY_H +#define LIGHTNING_PLUGINS_BKPR_CHANNELSAPY_H +#include "config.h" + +#include + +struct channel_apy { + char *acct_name; + + struct amount_msat routed_in; + struct amount_msat routed_out; + struct amount_msat fees_in; + struct amount_msat fees_out; + + struct amount_msat push_in; + struct amount_msat push_out; + struct amount_msat lease_in; + struct amount_msat lease_out; + + struct amount_msat our_start_bal; + struct amount_msat total_start_bal; + + /* Blockheight the channel opened */ + u32 start_blockheight; + + /* If channel_close, the channel_close event's blockheight, + * otherwise the current blockheight */ + u32 end_blockheight; +}; + +struct channel_apy *new_channel_apy(const tal_t *ctx); + +WARN_UNUSED_RESULT bool channel_apy_sum(struct channel_apy *sum_apy, + const struct channel_apy *entry); + +struct channel_apy **compute_channel_apys(const tal_t *ctx, struct db *db, + u64 start_time, + u64 end_time, + u32 current_blockheight); + +void json_add_channel_apy(struct json_stream *res, + const struct channel_apy *apy); +#endif /* LIGHTNING_PLUGINS_BKPR_CHANNELSAPY_H */ From 91ebddeb781aef221922dd42ad4a4a97df5473c4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:37 +0930 Subject: [PATCH 1168/1530] bkpr: actually fill in the current blockheight for `channelsapy` I had hardcoded something for the tests, but really we want this to be dynamically fetched from lightningd via `getinfo`. This does the trick! --- plugins/bkpr/bookkeeper.c | 59 +++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index a9ebdd65c516..8cba95954320 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -42,25 +42,38 @@ static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, return NULL; } -static struct command_result *json_channel_apy(struct command *cmd, - const char *buf, - const jsmntok_t *params) +struct apy_req { + u64 *start_time; + u64 *end_time; +}; + +static struct command_result * +getblockheight_done(struct command *cmd, const char *buf, + const jsmntok_t *result, + struct apy_req *req) { + const jsmntok_t *blockheight_tok; + u32 blockheight; struct json_stream *res; struct channel_apy **apys, *net_apys; - u64 *start_time, *end_time; - if (!param(cmd, buf, params, - p_opt_def("start_time", param_u64, &start_time, 0), - p_opt_def("end_time", param_u64, &end_time, SQLITE_MAX_UINT), - NULL)) - return command_param_failed(); + blockheight_tok = json_get_member(buf, result, "blockheight"); + if (!blockheight_tok) + plugin_err(cmd->plugin, "getblockheight: " + "getinfo gave no 'blockheight'? '%.*s'", + result->end - result->start, buf); + + if (!json_to_u32(buf, blockheight_tok, &blockheight)) + plugin_err(cmd->plugin, "getblockheight: " + "getinfo gave non-unsigned-32-bit 'blockheight'? '%.*s'", + result->end - result->start, buf); /* Get the income events */ db_begin_transaction(db); - apys = compute_channel_apys(cmd, db, *start_time, *end_time, - /* FIXME: current blockheight */ - 1414); + apys = compute_channel_apys(cmd, db, + *req->start_time, + *req->end_time, + blockheight); db_commit_transaction(db); /* Setup the net_apys entry */ @@ -91,6 +104,28 @@ static struct command_result *json_channel_apy(struct command *cmd, return command_finished(cmd, res); } +static struct command_result *json_channel_apy(struct command *cmd, + const char *buf, + const jsmntok_t *params) +{ + struct out_req *req; + struct apy_req *apyreq = tal(cmd, struct apy_req); + + if (!param(cmd, buf, params, + p_opt_def("start_time", param_u64, &apyreq->start_time, 0), + p_opt_def("end_time", param_u64, &apyreq->end_time, + SQLITE_MAX_UINT), + NULL)) + return command_param_failed(); + + /* First get the current blockheight */ + req = jsonrpc_request_start(cmd->plugin, cmd, "getinfo", + &getblockheight_done, + forward_error, + apyreq); + return send_outreq(cmd->plugin, req); +} + static struct command_result *param_csv_format(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct csv_fmt **csv_fmt) From d87bdeeacea73e7708f0fde24748c450df99da8b Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1169/1530] coin_mvt: log channel_open for channels that close before they're locked We don't push out a coin_move for a channel open until it's locked in, but this causes problems for channels that close before they're locked. So if we go the "close before locked in" route, we push out a channel open event. These will get a blockheight of 0, if we haven't seen the funding transaction in a block yet. --- lightningd/channel_control.c | 11 +++++------ lightningd/channel_control.h | 2 +- lightningd/dual_open_control.c | 4 +++- lightningd/onchain_control.c | 12 ++++++++++++ 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index d7bde69d8b0a..52eef8c2208b 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -127,16 +127,13 @@ void notify_feerate_change(struct lightningd *ld) * peer. We *could* do so, however. */ } -void channel_record_open(struct channel *channel) +void channel_record_open(struct channel *channel, u32 blockheight) { struct chain_coin_mvt *mvt; - u32 blockheight; struct amount_msat start_balance; bool is_pushed = !amount_msat_zero(channel->push); bool is_leased = channel->lease_expiry > 0; - blockheight = short_channel_id_blocknum(channel->scid); - /* If funds were pushed, add/sub them from the starting balance */ if (channel->opener == LOCAL) { if (!amount_msat_add(&start_balance, @@ -210,7 +207,8 @@ static void lockin_complete(struct channel *channel) /* Only record this once we get a real confirmation. */ if (channel->scid) - channel_record_open(channel); + channel_record_open(channel, + short_channel_id_blocknum(channel->scid)); } bool channel_on_funding_locked(struct channel *channel, @@ -869,7 +867,8 @@ bool channel_tell_depth(struct lightningd *ld, get_block_height(channel->peer->ld->topology)); /* Only record this once we get a real confirmation. */ - channel_record_open(channel); + channel_record_open(channel, + short_channel_id_blocknum(channel->scid)); } return true; } diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index 8f3b8ab190b3..420754706126 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -35,7 +35,7 @@ bool channel_on_funding_locked(struct channel *channel, struct pubkey *next_per_commitment_point); /* Record channel open (coin movement notifications) */ -void channel_record_open(struct channel *channel); +void channel_record_open(struct channel *channel, u32 blockheight); /* A channel has unrecoverably fallen behind */ void channel_fallen_behind(struct channel *channel, const u8 *msg); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index ba8321f06db9..fba6ff1a4cc2 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -3,6 +3,7 @@ * saves and funding tx watching for a channel open */ #include "config.h" +#include #include #include #include @@ -1748,7 +1749,8 @@ static void handle_channel_locked(struct subd *dualopend, CHANNELD_NORMAL, REASON_UNKNOWN, "Lockin complete"); - channel_record_open(channel); + channel_record_open(channel, + short_channel_id_blocknum(channel->scid)); /* Empty out the inflights */ wallet_channel_clear_inflights(dualopend->ld->wallet, channel); diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 2e8919751509..02a7281572c5 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -622,6 +623,17 @@ enum watch_result onchaind_funding_spent(struct channel *channel, channel_fail_permanent(channel, reason, "Funding transaction spent"); + /* If we haven't posted the open event yet, post an open */ + if (!channel->scid || !channel->remote_funding_locked) { + u32 blkh; + /* Note that blockheight will be zero if it's not in chain + * yet */ + blkh = wallet_transaction_height(channel->peer->ld->wallet, + &channel->funding.txid); + channel_record_open(channel, blkh); + } + + /* We could come from almost any state. */ /* NOTE(mschmoock) above comment is wrong, since we failed above! */ channel_set_state(channel, From 4326c089279511f818caffc656104fec5171708a Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1170/1530] test nit: wait_for_mempool cleanup Wait for mempool=1 before making a block --- tests/test_connection.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_connection.py b/tests/test_connection.py index 1dc6fbfb9671..af0421142773 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -934,8 +934,7 @@ def test_shutdown_awaiting_lockin(node_factory, bitcoind): chanid = l1.rpc.fundchannel(l2.info['id'], 10**6)['channel_id'] # Technically, this is async to fundchannel. - l1.daemon.wait_for_log('sendrawtx exit 0') - bitcoind.generate_block(1) + bitcoind.generate_block(1, wait_for_mempool=1) l1.rpc.close(chanid) @@ -951,9 +950,8 @@ def test_shutdown_awaiting_lockin(node_factory, bitcoind): # CLOSINGD_COMPLETE may come first). l1.daemon.wait_for_logs(['sendrawtx exit 0', ' to CLOSINGD_COMPLETE']) l2.daemon.wait_for_logs(['sendrawtx exit 0', ' to CLOSINGD_COMPLETE']) - assert bitcoind.rpc.getmempoolinfo()['size'] == 1 - bitcoind.generate_block(1) + bitcoind.generate_block(1, wait_for_mempool=1) l1.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log(' to ONCHAIN') From aa7ffb78bd8d3dcd9f3d04f69a81b8b3403e76b5 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1171/1530] wallet: resolve crash when blockheight is null We weren't ignoring the 'txindex' field --- wallet/wallet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index aae846635ec4..1c276c2e8d01 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4197,9 +4197,10 @@ struct txlocator *wallet_transaction_locate(const tal_t *ctx, struct wallet *w, return NULL; } - if (db_col_is_null(stmt, "blockheight")) + if (db_col_is_null(stmt, "blockheight")) { + db_col_ignore(stmt, "txindex"); loc = NULL; - else { + } else { loc = tal(ctx, struct txlocator); loc->blkheight = db_col_int(stmt, "blockheight"); loc->index = db_col_int(stmt, "txindex"); From e7ed196f8737dc13479dec99bb676e8fe7c651b7 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1172/1530] bkpr: separate the invoice_fees from the invoice paid For income events, break out the amount paid in routing fees vs the total amount of the *invoice* that is paid. Also printout these fees, when available, on listaccountevents --- plugins/bkpr/account_entry.c | 1 + plugins/bkpr/account_entry.h | 3 ++- plugins/bkpr/channel_event.c | 2 ++ plugins/bkpr/incomestmt.c | 29 ++++++++++++++++++++++++++++- tests/test_pay.py | 15 +++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/account_entry.c b/plugins/bkpr/account_entry.c index 7179f761fc1f..4aa6d5489e3e 100644 --- a/plugins/bkpr/account_entry.c +++ b/plugins/bkpr/account_entry.c @@ -7,6 +7,7 @@ static const char *tags[] = { "journal_entry", "penalty_adj", + "invoice_fee", }; const char *account_entry_tag_str(enum account_entry_tag tag) diff --git a/plugins/bkpr/account_entry.h b/plugins/bkpr/account_entry.h index fed529847740..c04f4dafabc3 100644 --- a/plugins/bkpr/account_entry.h +++ b/plugins/bkpr/account_entry.h @@ -2,10 +2,11 @@ #define LIGHTNING_PLUGINS_BKPR_ACCOUNT_ENTRY_H #include "config.h" -#define NUM_ACCOUNT_ENTRY_TAGS (JOURNAL_ENTRY + 1) +#define NUM_ACCOUNT_ENTRY_TAGS (INVOICEFEE + 1) enum account_entry_tag { JOURNAL_ENTRY = 0, PENALTY_ADJ = 1, + INVOICEFEE = 2, }; /* Convert an enum into a string */ diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index 782aacf6b1c9..58ca871d0a5a 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -39,6 +39,8 @@ void json_add_channel_event(struct json_stream *out, json_add_string(out, "tag", ev->tag); json_add_amount_msat_only(out, "credit", ev->credit); json_add_amount_msat_only(out, "debit", ev->debit); + if (!amount_msat_zero(ev->fees)) + json_add_amount_msat_only(out, "fees", ev->fees); json_add_string(out, "currency", ev->currency); if (ev->payment_id) json_add_sha256(out, "payment_id", ev->payment_id); diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index d5d02d347fd5..b9b1ecb3d548 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -190,6 +190,16 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, return NULL; } +static struct income_event *paid_invoice_fee(const tal_t *ctx, + struct channel_event *ev) +{ + struct income_event *iev; + iev = channel_to_income(ctx, ev, AMOUNT_MSAT(0), ev->fees); + iev->tag = tal_free(ev->tag); + iev->tag = (char *)account_entry_tag_str(INVOICEFEE); + return iev; +} + static struct income_event *maybe_channel_income(const tal_t *ctx, struct channel_event *ev) { @@ -203,7 +213,17 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, } if (streq(ev->tag, "invoice")) { - /* FIXME: add a sub-category for fees paid */ + /* If it's a payment, we note fees separately */ + if (!amount_msat_zero(ev->debit)) { + struct amount_msat paid; + bool ok; + ok = amount_msat_sub(&paid, ev->debit, ev->fees); + assert(ok); + return channel_to_income(ctx, ev, + ev->credit, + paid); + } + return channel_to_income(ctx, ev, ev->credit, ev->debit); @@ -338,6 +358,13 @@ struct income_event **list_income_events(const tal_t *ctx, if (ev) tal_arr_expand(&evs, ev); + /* Breakout fees on sent payments */ + if (streq(chan->tag, "invoice") + && !amount_msat_zero(chan->debit)) { + ev = paid_invoice_fee(evs, chan); + tal_arr_expand(&evs, ev); + } + j++; continue; } diff --git a/tests/test_pay.py b/tests/test_pay.py index 686faa9dfd92..dc4bd15fe76b 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1303,6 +1303,21 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): l1.rpc.waitsendpay(rhash) assert only_one(l3.rpc.listinvoices('test_forward_pad_fees_and_cltv')['invoices'])['status'] == 'paid' + # Do some checks of the bookkeeper's records + def _income_tagset(node, tagset): + incomes = node.rpc.listincome()['income_events'] + return [e for e in incomes if e['tag'] in tagset] + + tags = ['invoice', 'invoice_fee'] + wait_for(lambda: len(_income_tagset(l1, tags)) == 2) + incomes = _income_tagset(l1, tags) + # the balance on l3 should equal the invoice + bal = only_one(only_one(l3.rpc.listbalances()['accounts'])['balances'])['balance'] + assert incomes[0]['tag'] == 'invoice' + assert bal == incomes[0]['debit'] + inve = only_one([e for e in l1.rpc.listaccountevents()['events'] if e['tag'] == 'invoice']) + assert Millisatoshi(inve['debit']) == Millisatoshi(incomes[0]['debit']) + Millisatoshi(incomes[1]['debit']) + @pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs") def test_forward_stats(node_factory, bitcoind): From eae1236db7deec275424957a554a33e07f196fd2 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1173/1530] tests,bkpr: liquid fails all these for different reasons - external wallet not supported yet for elements - the close fails to propagate b/c the outputs are dusty (FIXME) --- tests/test_bookkeeper.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 620cde37ea6e..026be33db1dd 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -22,6 +22,7 @@ def find_first_tag(evs, tag): @pytest.mark.developer("dev-ignore-htlcs") +@unittest.skipIf(TEST_NETWORK != 'regtest', "fixme: broadcast fails, dusty") def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): l1, l2 = node_factory.line_graph(2) @@ -69,6 +70,7 @@ def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) +@unittest.skipIf(TEST_NETWORK != 'regtest', "fixme: broadcast fails, dusty") def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): """Test closing balances when HTLCs are: sub 1-satoshi""" l1, l2 = node_factory.line_graph(2) @@ -107,6 +109,7 @@ def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) +@unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") def test_bookkeeping_external_withdraws(node_factory, bitcoind): """ Withdrawals to an external address shouldn't be included in the income statements until confirmed""" @@ -253,6 +256,7 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): assert Millisatoshi(bal['balance']) == amount_msat * 2 - fees +@unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): """ If a withdraw to an external gets RBF'd, it should *not* show up in our income ever. From 10e58a3788ad5a55031007c3ad5423dc68f3cf4d Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1174/1530] nit,bkpr: add a note about what the tag was to this printout Makes it much easier to debug when something goes wrong! --- plugins/bkpr/bookkeeper.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 8cba95954320..8f6a260e6c47 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1401,8 +1401,9 @@ static struct command_result * json_coin_moved(struct command *cmd, /* We expect version 2 of coin movements */ assert(version == 2); - plugin_log(cmd->plugin, LOG_DBG, "coin_move %d %s -%s %s %"PRIu64, + plugin_log(cmd->plugin, LOG_DBG, "coin_move %d (%s) %s -%s %s %"PRIu64, version, + mvt_tag_str(tags[0]), type_to_string(tmpctx, struct amount_msat, &credit), type_to_string(tmpctx, struct amount_msat, &debit), mvt_type, timestamp); From fec818641393eca0ec49f0cc104638d9340bbf6f Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1175/1530] bkpr: get rid of crash in `listincome` Our consolidate fees had a crash bug (and was pretty convoluted). This makes it less convoluted and resolves the crash. The only kinda meh thing is that we have to look up the most recent timestamp data for the onchain fee entry separately, because of the way SQL sums work. --- plugins/bkpr/incomestmt.c | 51 +++++++++++++++++---------------- plugins/bkpr/recorder.c | 60 ++++++++++++++++++++++++++++++--------- plugins/bkpr/recorder.h | 6 ++++ 3 files changed, 79 insertions(+), 38 deletions(-) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index b9b1ecb3d548..fcc529c6d551 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -246,38 +246,38 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, static struct onchain_fee **find_consolidated_fees(const tal_t *ctx, struct db *db, - struct onchain_fee **fees TAKES) + u64 start_time, + u64 end_time) { struct fee_sum **sums; - struct onchain_fee **updated_fees + struct onchain_fee **fee_sums = tal_arr(ctx, struct onchain_fee *, 0); sums = calculate_onchain_fee_sums(ctx, db); for (size_t i = 0; i < tal_count(sums); i++) { /* Find the last matching feerate's data */ - for (size_t j = tal_count(fees); j > 0; j--) { - struct onchain_fee *fee; - if (bitcoin_txid_eq(&fees[j - 1]->txid, - sums[i]->txid) - && fees[j - 1]->acct_db_id == sums[i]->acct_db_id) { - fee = tal_steal(updated_fees, fees[j - 1]); - fee->credit = sums[i]->fees_paid; - fee->debit = AMOUNT_MSAT(0); - tal_arr_expand(&updated_fees, fee); - fees[j - 1] = NULL; - break; - } - } + struct onchain_fee *fee; - /* It's possible there weren't any fee events - * for this txid in the time period we've selected */ - } + if (amount_msat_zero(sums[i]->fees_paid)) + continue; - if (taken(fees)) - tal_free(fees); + fee = tal(fee_sums, struct onchain_fee); + fee->credit = sums[i]->fees_paid; + fee->debit = AMOUNT_MSAT(0); + fee->currency = tal_steal(fee, sums[i]->currency); + fee->acct_name = tal_steal(fee, sums[i]->acct_name); + fee->txid = *sums[i]->txid; - return updated_fees; + fee->timestamp = + onchain_fee_last_timestamp(db, sums[i]->acct_db_id, + sums[i]->txid); + + tal_arr_expand(&fee_sums, fee); + } + + tal_free(sums); + return fee_sums; } struct income_event **list_income_events(const tal_t *ctx, @@ -296,12 +296,15 @@ struct income_event **list_income_events(const tal_t *ctx, channel_events = list_channel_events_timebox(ctx, db, start_time, end_time); chain_events = list_chain_events_timebox(ctx, db, start_time, end_time); - onchain_fees = list_chain_fees_timebox(ctx, db, start_time, end_time); accts = list_accounts(ctx, db); - if (consolidate_fees) + if (consolidate_fees) { onchain_fees = find_consolidated_fees(ctx, db, - take(onchain_fees)); + start_time, + end_time); + } else + onchain_fees = list_chain_fees_timebox(ctx, db, + start_time, end_time); evs = tal_arr(ctx, struct income_event *, 0); diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index bd5712f43d00..5aac67278121 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -214,12 +214,18 @@ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) struct db_stmt *stmt; struct fee_sum **sums; stmt = db_prepare_v2(db, SQL("SELECT" - " txid" - ", account_id" - ", SUM(credit)" - ", SUM(debit)" - " FROM onchain_fees" - " GROUP BY txid, account_id" + " of.txid" + ", of.account_id" + ", a.name" + ", of.currency" + ", CAST(SUM(of.credit) AS BIGINT) - CAST(SUM(of.debit) AS BIGINT) as fees" + " FROM onchain_fees of" + " LEFT OUTER JOIN accounts a" + " ON of.account_id = a.id" + " GROUP BY of.txid" + ", of.account_id" + ", a.name" + ", of.currency" " ORDER BY txid, account_id")); db_query_prepared(stmt); @@ -227,18 +233,16 @@ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) sums = tal_arr(ctx, struct fee_sum *, 0); while (db_step(stmt)) { struct fee_sum *sum; - struct amount_msat amt; - bool ok; sum = tal(sums, struct fee_sum); sum->txid = tal(sum, struct bitcoin_txid); - db_col_txid(stmt, "txid", sum->txid); - sum->acct_db_id = db_col_u64(stmt, "account_id"); - db_col_amount_msat(stmt, "SUM(credit)", &sum->fees_paid); - db_col_amount_msat(stmt, "SUM(debit)", &amt); - ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, amt); - assert(ok); + db_col_txid(stmt, "of.txid", sum->txid); + sum->acct_db_id = db_col_u64(stmt, "of.account_id"); + sum->acct_name = db_col_strdup(sum, stmt, "a.name"); + sum->currency = db_col_strdup(sum, stmt, "of.currency"); + db_col_amount_msat(stmt, "fees", &sum->fees_paid); + tal_arr_expand(&sums, sum); } @@ -246,6 +250,34 @@ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) return sums; } +u64 onchain_fee_last_timestamp(struct db *db, + u64 acct_db_id, + struct bitcoin_txid *txid) +{ + struct db_stmt *stmt; + u64 timestamp; + + stmt = db_prepare_v2(db, SQL("SELECT" + " timestamp" + " FROM onchain_fees" + " WHERE account_id = ?" + " AND txid = ?" + " ORDER BY timestamp DESC")); + + + db_bind_u64(stmt, 0, acct_db_id); + db_bind_txid(stmt, 1, txid); + db_query_prepared(stmt); + + if (db_step(stmt)) + timestamp = db_col_u64(stmt, "timestamp"); + else + timestamp = 0; + + tal_free(stmt); + return timestamp; +} + struct fee_sum **find_account_onchain_fees(const tal_t *ctx, struct db *db, struct account *acct) diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index c71daab6119e..05bcb41536e7 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -25,6 +25,8 @@ struct acct_balance { struct fee_sum { u64 acct_db_id; + char *acct_name; + char *currency; struct bitcoin_txid *txid; struct amount_msat fees_paid; }; @@ -138,6 +140,10 @@ struct fee_sum **find_account_onchain_fees(const tal_t *ctx, /* Final all the onchain fees */ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db); +/* Find the last timestamp for the onchain fees for this txid + account */ +u64 onchain_fee_last_timestamp(struct db *db, + u64 acct_db_id, + struct bitcoin_txid *txid); /* Add the given account to the database */ void account_add(struct db *db, struct account *acct); /* Given an account name, find that account record */ From a3d82d5a0161fa7f2e0960a8817423cea9ee75b9 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1176/1530] bkpr: exclude non-wallet events in the balance snapshot Anchor outputs are ignored by the clightning wallet, but we keep track of them in the bookkeeper. This causes problems when we do the balance checks on restart w/ the balance_snapshot -- it results in us printing out a journal_entry to 'get rid of' the anchors that the clightning node doesnt know about. Instead, we mark some outputs as 'ignored' and exclude these from our account balance sums when we're comparing to the clightning snapshot. --- common/coin_mvt.c | 13 ++++++++ common/coin_mvt.h | 7 +++++ onchaind/onchaind.c | 7 +++-- onchaind/test/run-grind_feerate-bug.c | 19 +++++++----- onchaind/test/run-grind_feerate.c | 17 ++++++----- plugins/bkpr/bookkeeper.c | 13 ++++++-- plugins/bkpr/chain_event.h | 3 ++ plugins/bkpr/db.c | 1 + plugins/bkpr/recorder.c | 20 ++++++++++-- plugins/bkpr/recorder.h | 4 ++- plugins/bkpr/test/run-recorder.c | 33 ++++++++++++++------ tests/test_closing.py | 44 +++++++++++++++------------ 12 files changed, 129 insertions(+), 52 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 12d758f4d846..9680b873fde4 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -288,6 +288,19 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, amount, true); } +struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag *tags TAKES) +{ + return new_chain_coin_mvt_sat(ctx, WALLET, NULL, + outpoint, NULL, + blockheight, + take(tags), + amount, true); +} + struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, const struct bitcoin_txid *spend_txid, const struct bitcoin_outpoint *outpoint, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index c34f03792d7a..760d848bce1c 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -211,6 +211,13 @@ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx, enum mvt_tag tag) NON_NULL_ARGS(2); +struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag *tags TAKES) + NON_NULL_ARGS(2); + struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, const struct bitcoin_txid *spend_txid, const struct bitcoin_outpoint *outpoint, diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 6c7049a42c9f..f144f04ce611 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -335,10 +335,13 @@ static void record_ignored_wallet_deposit(struct tracked_output *out) static void record_anchor(struct tracked_output *out) { - send_coin_mvt(take(new_coin_wallet_deposit(NULL, + enum mvt_tag *tags = new_tag_arr(NULL, ANCHOR); + tal_arr_expand(&tags, IGNORED); + send_coin_mvt(take(new_coin_wallet_deposit_tagged(NULL, &out->outpoint, out->tx_blockheight, - out->sat, ANCHOR))); + out->sat, + tags))); } static void record_coin_movements(struct tracked_output *out, diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 8726dbaf3782..7d50d53f5698 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -140,14 +140,14 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } -/* Generated stub for new_coin_wallet_deposit */ -struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED, - enum mvt_tag tag) - -{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } +/* Generated stub for new_coin_wallet_deposit_tagged */ +struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag *tags TAKES) + +{ fprintf(stderr, "new_coin_wallet_deposit_tagged called!\n"); abort(); } /* Generated stub for new_onchain_htlc_deposit */ struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, @@ -181,6 +181,9 @@ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); } +/* Generated stub for new_tag_arr */ +enum mvt_tag *new_tag_arr(const tal_t *ctx UNNEEDED, enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_tag_arr called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index e800b371cefa..bfe625f315b9 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -163,14 +163,14 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } -/* Generated stub for new_coin_wallet_deposit */ -struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, - const struct bitcoin_outpoint *outpoint UNNEEDED, - u32 blockheight UNNEEDED, - struct amount_sat amount UNNEEDED, - enum mvt_tag tag) +/* Generated stub for new_coin_wallet_deposit_tagged */ +struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag *tags TAKES) -{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); } +{ fprintf(stderr, "new_coin_wallet_deposit_tagged called!\n"); abort(); } /* Generated stub for new_onchain_htlc_deposit */ struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, @@ -204,6 +204,9 @@ struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); } +/* Generated stub for new_tag_arr */ +enum mvt_tag *new_tag_arr(const tal_t *ctx UNNEEDED, enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "new_tag_arr called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 8f6a260e6c47..4fa323885220 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -471,6 +471,7 @@ static struct command_result *json_list_balances(struct command *cmd, err = account_get_balance(cmd, db, accts[i]->name, true, + false, /* don't skip ignored */ &balances); if (err) @@ -596,6 +597,7 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev->outpoint = opt; chain_ev->spending_txid = NULL; chain_ev->payment_id = NULL; + chain_ev->ignored = false; /* Update the account info too */ tags = tal_arr(chain_ev, enum mvt_tag, 1); @@ -799,7 +801,7 @@ listpeers_multi_done(struct command *cmd, db_begin_transaction(db); err = account_get_balance(tmpctx, db, info->acct->name, - false, &balances); + false, false, &balances); db_commit_transaction(db); if (err) @@ -880,7 +882,7 @@ listpeers_done(struct command *cmd, const char *buf, info->ev->timestamp)) { db_begin_transaction(db); err = account_get_balance(tmpctx, db, info->acct->name, - false, &balances); + false, false, &balances); db_commit_transaction(db); if (err) @@ -993,6 +995,9 @@ static struct command_result *json_balance_snapshot(struct command *cmd, err = account_get_balance(cmd, db, acct_name, /* Don't error if negative */ false, + /* Ignore non-clightning + * balances items */ + true, &balances); if (err) @@ -1193,6 +1198,10 @@ parse_and_log_chain_move(struct command *cmd, e->timestamp = timestamp; e->tag = mvt_tag_str(tags[0]); + e->ignored = false; + for (size_t i = 0; i < tal_count(tags); i++) + e->ignored |= tags[i] == IGNORED; + db_begin_transaction(db); acct = find_account(cmd, db, acct_name); diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index d9d12c27a71e..b9f7b444ba23 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -27,6 +27,9 @@ struct chain_event { /* Tag describing the event */ const char *tag; + /* Is the node's wallet ignoring this? */ + bool ignored; + /* Amount we received in this event */ struct amount_msat credit; diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 7a7f2aca2f3f..1708691bd310 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -94,6 +94,7 @@ static struct migration db_migrations[] = { NULL}, {SQL("ALTER TABLE chain_events ADD origin TEXT;"), NULL}, {SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL}, + {SQL("ALTER TABLE chain_events ADD ignored INTEGER;"), NULL}, }; static bool db_migrate(struct plugin *p, struct db *db) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 5aac67278121..baccbf2304bf 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -55,6 +55,8 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st } else e->spending_txid = NULL; + e->ignored = db_col_int(stmt, "e.ignored") == 1; + return e; } @@ -125,6 +127,7 @@ struct chain_event **list_chain_events_timebox(const tal_t *ctx, ", e.outnum" ", e.spending_txid" ", e.payment_id" + ", e.ignored" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -164,6 +167,7 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, ", e.outnum" ", e.spending_txid" ", e.payment_id" + ", e.ignored" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -196,6 +200,7 @@ static struct chain_event **find_txos_for_tx(const tal_t *ctx, ", e.outnum" ", e.spending_txid" ", e.payment_id" + ", e.ignored" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -591,6 +596,7 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, ", e.outnum" ", e.spending_txid" ", e.payment_id" + ", e.ignored" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -635,6 +641,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.outnum" ", e.spending_txid" ", e.payment_id" + ", e.ignored" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -661,6 +668,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.outnum" ", e.spending_txid" ", e.payment_id" + ", e.ignored" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -689,6 +697,7 @@ char *account_get_balance(const tal_t *ctx, struct db *db, const char *acct_name, bool calc_sum, + bool skip_ignored, struct acct_balance ***balances) { struct db_stmt *stmt; @@ -701,9 +710,13 @@ char *account_get_balance(const tal_t *ctx, " LEFT OUTER JOIN accounts a" " ON a.id = ce.account_id" " WHERE a.name = ?" + " AND ce.ignored != ?" " GROUP BY ce.currency")); db_bind_text(stmt, 0, acct_name); + /* We populate ignored with a 0 or 1, + * if we want both 0+1, we just ignore everything with a 2 */ + db_bind_int(stmt, 1, skip_ignored ? 1 : 2); db_query_prepared(stmt); *balances = tal_arr(ctx, struct acct_balance *, 0); @@ -1287,6 +1300,7 @@ static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db ", e.outnum" ", e.spending_txid" ", e.payment_id" + ", e.ignored" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -1775,9 +1789,10 @@ bool log_chain_event(struct db *db, ", outnum" ", payment_id" ", spending_txid" + ", ignored" ")" - " VALUES" - " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " VALUES " + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); if (e->origin_acct) @@ -1804,6 +1819,7 @@ bool log_chain_event(struct db *db, else db_bind_null(stmt, 12); + db_bind_int(stmt, 13, e->ignored ? 1 : 0); db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); e->acct_db_id = acct->db_id; diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 05bcb41536e7..b29d69d068e3 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -91,12 +91,14 @@ struct chain_event **list_chain_events_timebox(const tal_t *ctx, /* Calculate the balances for an account * - * @calc_sum - compute the total balance. error if negative + * @calc_sum - compute the total balance. error if negative + * @skip_ignored - don't include ignored payments in the balance sum * */ char *account_get_balance(const tal_t *ctx, struct db *db, const char *acct_name, bool calc_sum, + bool skip_ignored, struct acct_balance ***balances); /* Get chain fees for account */ diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 8480d528851d..f76078185343 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -360,6 +360,7 @@ static struct chain_event *make_chain_event(const tal_t *ctx, ev->currency = "btc"; ev->timestamp = 1919191; ev->blockheight = blockheight; + ev->ignored = false; memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid)); ev->outpoint.n = outnum; @@ -961,6 +962,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev1->currency = "btc"; ev1->timestamp = 1919191; ev1->blockheight = 1919191; + ev1->ignored = false; memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev1->outpoint.n = 1; ev1->spending_txid = tal(ctx, struct bitcoin_txid); @@ -980,6 +982,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev2->currency = "btc"; ev2->timestamp = 1919191; ev2->blockheight = 1919191; + ev2->ignored = false; memset(&ev2->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev2->outpoint.n = 1; ev2->spending_txid = NULL; @@ -996,6 +999,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev3->currency = "btc"; ev3->timestamp = 3939393; ev3->blockheight = 3939393; + ev3->ignored = false; memset(&ev3->outpoint.txid, 'E', sizeof(struct bitcoin_txid)); ev3->outpoint.n = 1; ev3->spending_txid = tal(ctx, struct bitcoin_txid); @@ -1083,15 +1087,16 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) AMOUNT_MSAT(1000), 1019, 'A', 1, '*')); + ev1 = make_chain_event(ctx, "two", + AMOUNT_MSAT(0), AMOUNT_MSAT(999), + AMOUNT_MSAT(999), + 1020, 'A', 2, '*'); + + /* Make this an ignored event */ + ev1->ignored = true; /* -999btc */ - log_chain_event(db, acct, - make_chain_event(ctx, "two", - AMOUNT_MSAT(0), - AMOUNT_MSAT(999), - AMOUNT_MSAT(999), - 1020, - 'A', 2, '*')); + log_chain_event(db, acct, ev1); /* -440btc */ log_channel_event(db, acct, @@ -1118,7 +1123,7 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) /* Add same chain event to a different account, shouldn't show */ log_chain_event(db, acct2, ev1); - err = account_get_balance(ctx, db, acct->name, true, + err = account_get_balance(ctx, db, acct->name, true, false, &balances); CHECK_MSG(!err, err); db_commit_transaction(db); @@ -1141,14 +1146,21 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) ev1->currency = "chf"; log_chain_event(db, acct, ev1); - err = account_get_balance(ctx, db, acct->name, true, + err = account_get_balance(ctx, db, acct->name, true, false, &balances); CHECK_MSG(err != NULL, "Expected err message"); CHECK(streq(err, "chf channel balance is negative? 5000msat - 5001msat")); - err = account_get_balance(ctx, db, acct->name, false, + err = account_get_balance(ctx, db, acct->name, false, false, &balances); CHECK_MSG(!err, err); + + /* Now with ignored events */ + err = account_get_balance(ctx, db, acct->name, true, true, + &balances); + CHECK(streq(balances[0]->currency, "btc")); + CHECK(amount_msat_eq(balances[0]->balance, + AMOUNT_MSAT(500 - 440 + 1000))); db_commit_transaction(db); return true; @@ -1212,6 +1224,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) ev1->currency = "btc"; ev1->timestamp = 1919191; ev1->blockheight = 1919191; + ev1->ignored = false; memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev1->outpoint.n = 1; ev1->spending_txid = tal(ctx, struct bitcoin_txid); diff --git a/tests/test_closing.py b/tests/test_closing.py index a443d4240dfb..d6b3af838c72 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -593,8 +593,8 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams): if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) - expected_1['B'].append(('wallet', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) # We use a subset of tags in expected_2 that are used in expected_1 tags = check_utxos_channel(l1, [channel_id], expected_1) @@ -720,8 +720,8 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) - expected_1['B'].append(('wallet', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) # We use a subset of tags in expected_2 that are used in expected_1 tags = check_utxos_channel(l1, [channel_id], expected_1) @@ -983,6 +983,10 @@ def test_channel_lease_unilat_closes(node_factory, bitcoind): # l3 cleans up their to-self after their lease expires assert l3.daemon.is_in_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') + # We were making a journal_entry for anchors, but now we ignore them + incomes = l2.rpc.listincome()['income_events'] + assert 'journal_entry' not in [x['tag'] for x in incomes] + @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.openchannel('v2') @@ -1284,8 +1288,8 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): if anchor_expected(): expected_2['B'].append(('external', ['anchor'], None, None)) expected_3['B'].append(('external', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) - expected_3['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_3['B'].append(('wallet', ['anchor', 'ignored'], None, None)) tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) @@ -1504,8 +1508,8 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): if anchor_expected(): expected_2['B'].append(('external', ['anchor'], None, None)) expected_3['B'].append(('external', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) - expected_3['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_3['B'].append(('wallet', ['anchor', 'ignored'], None, None)) tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) @@ -1630,7 +1634,7 @@ def get_rbf_tx(self, depth, name, resolve): if anchor_expected(): expected_2['B'].append(('external', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) check_utxos_channel(l2, [channel_id], expected_2) @@ -1751,7 +1755,7 @@ def get_rbf_tx(self, depth, name, resolve): if anchor_expected(): expected_2['B'].append(('external', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) check_utxos_channel(l2, [channel_id], expected_2) @@ -2083,8 +2087,8 @@ def test_onchain_timeout(node_factory, bitcoind, executor): if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) - expected_1['B'].append(('wallet', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) # We use a subset of tags in expected_2 that are used in expected_1 tags = check_utxos_channel(l1, [channel_id], expected_1) @@ -2202,8 +2206,8 @@ def try_pay(): if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) - expected_1['B'].append(('wallet', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) chan2_id = first_channel_id(l2, l3) tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2) @@ -2322,8 +2326,8 @@ def try_pay(): if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) - expected_1['B'].append(('wallet', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) chan2_id = first_channel_id(l2, l3) tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2) @@ -2412,8 +2416,8 @@ def try_pay(): if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) - expected_1['B'].append(('wallet', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) tags = check_utxos_channel(l1, [channel_id], expected_1) check_utxos_channel(l2, [channel_id], expected_2, tags) @@ -2614,8 +2618,8 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): if anchor_expected(): expected_1['B'].append(('external', ['anchor'], None, None)) expected_2['B'].append(('external', ['anchor'], None, None)) - expected_1['B'].append(('wallet', ['anchor'], None, None)) - expected_2['B'].append(('wallet', ['anchor'], None, None)) + expected_1['B'].append(('wallet', ['anchor', 'ignored'], None, None)) + expected_2['B'].append(('wallet', ['anchor', 'ignored'], None, None)) tags = check_utxos_channel(l1, [channel_id], expected_1) check_utxos_channel(l2, [channel_id], expected_2, tags) From a45da6328010c3bdc441e51f8f9fbbe035ce6266 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1177/1530] bkpr: pass the node_id over for channel_opens, add to account it's nice to know what node your channel was opened with. in theory we could use listpeers to merge the data after the fact, except that channels disappear after they've been closed for a bit. it's better to just save the info. we print it out in `listbalances`, as that's a great place account level information --- common/coin_mvt.c | 20 +++++++++++++++++ common/coin_mvt.h | 8 +++++++ lightningd/channel_control.c | 1 + lightningd/notification.c | 2 ++ plugins/bkpr/bookkeeper.c | 38 +++++++++++++++++++++++++++++--- plugins/bkpr/recorder.c | 15 +++++++++++-- plugins/bkpr/recorder.h | 3 ++- plugins/bkpr/test/run-recorder.c | 23 ++++++++++--------- 8 files changed, 94 insertions(+), 16 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 9680b873fde4..4893d0e55aad 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -105,6 +105,9 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, mvt->outpoint = outpoint; mvt->originating_acct = NULL; + /* Most chain event's don't have a peer (only channel_opens) */ + mvt->peer_id = NULL; + /* for htlc's that are filled onchain, we also have a * preimage, NULL otherwise */ mvt->payment_hash = tal_dup_or_null(mvt, struct sha256, payment_hash); @@ -195,6 +198,7 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct channel_id *chan_id, const struct bitcoin_outpoint *out, + const struct node_id *peer_id, u32 blockheight, const struct amount_msat amount, const struct amount_sat output_val, @@ -207,6 +211,7 @@ struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, take(new_tag_arr(NULL, CHANNEL_OPEN)), amount, true, output_val, 0); mvt->account_name = type_to_string(mvt, struct channel_id, chan_id); + mvt->peer_id = tal_dup(mvt, struct node_id, peer_id); /* If we're the opener, add to the tag list */ if (is_opener) @@ -359,6 +364,7 @@ struct coin_mvt *finalize_chain_mvt(const tal_t *ctx, mvt->blockheight = chain_mvt->blockheight; mvt->version = COIN_MVT_VERSION; mvt->node_id = node_id; + mvt->peer_id = chain_mvt->peer_id; return mvt; } @@ -391,6 +397,7 @@ struct coin_mvt *finalize_channel_mvt(const tal_t *ctx, mvt->timestamp = timestamp; mvt->version = COIN_MVT_VERSION; mvt->node_id = tal_dup(mvt, struct node_id, node_id); + mvt->peer_id = NULL; return mvt; } @@ -432,6 +439,12 @@ void towire_chain_coin_mvt(u8 **pptr, const struct chain_coin_mvt *mvt) towire_amount_msat(pptr, mvt->debit); towire_amount_sat(pptr, mvt->output_val); towire_u32(pptr, mvt->output_count); + + if (mvt->peer_id) { + towire_bool(pptr, true); + towire_node_id(pptr, mvt->peer_id); + } else + towire_bool(pptr, false); } void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_mvt *mvt) @@ -475,4 +488,11 @@ void fromwire_chain_coin_mvt(const u8 **cursor, size_t *max, struct chain_coin_m mvt->debit = fromwire_amount_msat(cursor, max); mvt->output_val = fromwire_amount_sat(cursor, max); mvt->output_count = fromwire_u32(cursor, max); + + if (fromwire_bool(cursor, max)) { + struct node_id peer_id; + fromwire_node_id(cursor, max, &peer_id); + mvt->peer_id = tal_dup(mvt, struct node_id, &peer_id); + } else + mvt->peer_id = NULL; } diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 760d848bce1c..aaecb3f86be6 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -68,6 +68,10 @@ struct chain_coin_mvt { const struct bitcoin_txid *tx_txid; const struct bitcoin_outpoint *outpoint; + /* The id of the peer we have this channel with. + * Only on our channel_open events */ + const struct node_id *peer_id; + /* some on-chain movements have a payment hash */ struct sha256 *payment_hash; @@ -105,6 +109,9 @@ struct coin_mvt { /* name of 'account': wallet, external, */ const char *account_id; + /* Peer that this event occurred with */ + const struct node_id *peer_id; + /* if account_id is external, the account this 'impacted' */ const char *originating_acct; @@ -183,6 +190,7 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct channel_id *chan_id, const struct bitcoin_outpoint *out, + const struct node_id *peer_id, u32 blockheight, const struct amount_msat amount, const struct amount_sat output_val, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 52eef8c2208b..67a56b853d84 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -156,6 +156,7 @@ void channel_record_open(struct channel *channel, u32 blockheight) mvt = new_coin_channel_open(tmpctx, &channel->cid, &channel->funding, + &channel->peer->id, blockheight, start_balance, channel->funding_sats, diff --git a/lightningd/notification.c b/lightningd/notification.c index a11602c7299b..1b28a08ab3d8 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -478,6 +478,8 @@ static void coin_movement_notification_serialize(struct json_stream *stream, json_object_start(stream, "coin_movement"); json_add_num(stream, "version", mvt->version); json_add_node_id(stream, "node_id", mvt->node_id); + if (mvt->peer_id) + json_add_node_id(stream, "peer_id", mvt->peer_id); json_add_string(stream, "type", mvt_type_str(mvt->type)); json_add_string(stream, "account_id", mvt->account_id); if (mvt->originating_acct) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 4fa323885220..8c2a79208c43 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -489,6 +490,12 @@ static struct command_result *json_list_balances(struct command *cmd, json_object_start(res, NULL); json_add_string(res, "account", accts[i]->name); + if (accts[i]->peer_id) { + json_add_node_id(res, "peer_id", accts[i]->peer_id); + json_add_bool(res, "account_resolved", + accts[i]->onchain_resolved_block > 0); + } + json_array_start(res, "balances"); for (size_t j = 0; j < tal_count(balances); j++) { json_object_start(res, NULL); @@ -549,6 +556,18 @@ static bool new_missed_channel_account(struct command *cmd, assert(peer_arr_tok->type == JSMN_ARRAY); /* There should only be one peer */ json_for_each_arr(i, curr_peer, peer_arr_tok) { + const char *err; + struct node_id peer_id; + + err = json_scan(cmd, buf, curr_peer, "{id:%}", + JSON_SCAN(json_to_node_id, &peer_id)); + + if (err) + plugin_err(cmd->plugin, + "failure scanning listpeer" + " result: %s", err); + + json_get_member(buf, curr_peer, "id"); chan_arr_tok = json_get_member(buf, curr_peer, "channels"); assert(chan_arr_tok->type == JSMN_ARRAY); @@ -557,7 +576,6 @@ static bool new_missed_channel_account(struct command *cmd, struct amount_msat amt, remote_amt, push_amt, push_credit, push_debit; char *opener, *chan_id; - const char *err; enum mvt_tag *tags; bool ok; @@ -624,7 +642,8 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev->credit = amt; db_begin_transaction(db); log_chain_event(db, acct, chain_ev); - maybe_update_account(db, acct, chain_ev, tags, 0); + maybe_update_account(db, acct, chain_ev, + tags, 0, &peer_id); maybe_update_onchain_fees(cmd, db, &opt.txid); /* We won't count the close's fees if we're @@ -1120,6 +1139,7 @@ parse_and_log_chain_move(struct command *cmd, struct chain_event *e = tal(cmd, struct chain_event); struct sha256 *payment_hash = tal(cmd, struct sha256); struct bitcoin_txid *spending_txid = tal(cmd, struct bitcoin_txid); + struct node_id *peer_id; struct account *acct; u32 closed_count; const char *err; @@ -1180,6 +1200,17 @@ parse_and_log_chain_move(struct command *cmd, err = tal_free(err); } + peer_id = tal(cmd, struct node_id); + err = json_scan(tmpctx, buf, params, + "{coin_movement:" + "{peer_id:%}}", + JSON_SCAN(json_to_node_id, peer_id)); + + if (err) { + peer_id = tal_free(peer_id); + err = tal_free(err); + } + err = json_scan(tmpctx, buf, params, "{coin_movement:" "{output_count:%}}", @@ -1219,7 +1250,8 @@ parse_and_log_chain_move(struct command *cmd, /* This event *might* have implications for account; * update as necessary */ - maybe_update_account(db, acct, e, tags, closed_count); + maybe_update_account(db, acct, e, tags, closed_count, + peer_id); /* Can we calculate any onchain fees now? */ err = maybe_update_onchain_fees(cmd, db, diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index baccbf2304bf..3fc57fa63e7f 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -1152,7 +1152,8 @@ void maybe_update_account(struct db *db, struct account *acct, struct chain_event *e, const enum mvt_tag *tags, - u32 closed_count) + u32 closed_count, + struct node_id *peer_id) { struct db_stmt *stmt; bool updated = false; @@ -1200,6 +1201,11 @@ void maybe_update_account(struct db *db, } } + if (peer_id) { + updated = true; + acct->peer_id = tal_dup(acct, struct node_id, peer_id); + } + if (closed_count > 0) { updated = true; acct->closed_count = closed_count; @@ -1216,6 +1222,7 @@ void maybe_update_account(struct db *db, ", we_opened = ?" ", leased = ?" ", closed_count = ?" + ", peer_id = ?" " WHERE" " name = ?")); @@ -1232,8 +1239,12 @@ void maybe_update_account(struct db *db, db_bind_int(stmt, 2, acct->we_opened ? 1 : 0); db_bind_int(stmt, 3, acct->leased ? 1 : 0); db_bind_int(stmt, 4, acct->closed_count); + if (acct->peer_id) + db_bind_node_id(stmt, 5, acct->peer_id); + else + db_bind_null(stmt, 5); - db_bind_text(stmt, 5, acct->name); + db_bind_text(stmt, 6, acct->name); db_exec_prepared_v2(take(stmt)); } diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index b29d69d068e3..c7c4209ca426 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -164,7 +164,8 @@ void maybe_update_account(struct db *db, struct account *acct, struct chain_event *e, const enum mvt_tag *tags, - u32 closed_count); + u32 closed_count, + struct node_id *peer_id); /* Update our onchain fees now? */ char *maybe_update_onchain_fees(const tal_t *ctx, diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index f76078185343..3c5fbffb2eb0 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -251,7 +251,9 @@ static bool accountseq(struct account *a1, struct account *a2) { CHECK(a1->db_id == a2->db_id); CHECK(streq(a1->name, a2->name)); - CHECK(node_id_eq(a1->peer_id, a2->peer_id)); + CHECK((a1->peer_id != NULL) == (a2->peer_id != NULL)); + if (a1->peer_id) + CHECK(node_id_eq(a1->peer_id, a2->peer_id)); CHECK(a1->is_wallet == a2->is_wallet); CHECK(a1->we_opened == a2->we_opened); CHECK(a1->leased == a2->leased); @@ -507,7 +509,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) 'X', 0, '*'); log_chain_event(db, acct, ev); tags[0] = CHANNEL_OPEN; - maybe_update_account(db, acct, ev, tags, 0); + maybe_update_account(db, acct, ev, tags, 0, NULL); ev = make_chain_event(ctx, "channel_close", AMOUNT_MSAT(0), @@ -519,7 +521,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) /* Update the account to have the right info! */ tags[0] = CHANNEL_CLOSE; - maybe_update_account(db, acct, ev, tags, close_output_count); + maybe_update_account(db, acct, ev, tags, close_output_count, NULL); log_chain_event(db, acct, make_chain_event(ctx, "delayed_to_us", @@ -1169,15 +1171,16 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) static bool test_account_crud(const tal_t *ctx, struct plugin *p) { struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); - struct node_id peer_id; + struct node_id *peer_id; struct account *acct, *acct2, **acct_list; struct chain_event *ev1; enum mvt_tag *tags; char *name = tal_fmt(ctx, "example"); - memset(&peer_id, 3, sizeof(struct node_id)); + peer_id = tal(ctx, struct node_id); + memset(peer_id, 3, sizeof(struct node_id)); - acct = new_account(ctx, name, &peer_id); + acct = new_account(ctx, name, NULL); CHECK(!acct->is_wallet); db_begin_transaction(db); @@ -1192,7 +1195,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) CHECK(tal_count(acct_list) == 1); accountseq(acct_list[0], acct); - acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + acct = new_account(ctx, tal_fmt(ctx, "wallet"), NULL); CHECK(acct->is_wallet); db_begin_transaction(db); @@ -1239,7 +1242,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) /* should not update the account info */ tags[0] = PUSHED; tags[1] = PENALTY; - maybe_update_account(db, acct, ev1, tags, 0); + maybe_update_account(db, acct, ev1, tags, 0, peer_id); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); @@ -1248,7 +1251,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) CHECK(acct->open_event_db_id == NULL); tags[0] = CHANNEL_OPEN; tags[1] = LEASED; - maybe_update_account(db, acct, ev1, tags, 2); + maybe_update_account(db, acct, ev1, tags, 2, peer_id); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); CHECK(acct->leased); @@ -1259,7 +1262,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) tags[1] = OPENER; CHECK(acct->closed_event_db_id == NULL); CHECK(!acct->we_opened); - maybe_update_account(db, acct, ev1, tags, 0); + maybe_update_account(db, acct, ev1, tags, 0, NULL); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); CHECK(acct->closed_event_db_id != NULL); From 2b3ef37590c40cf3704d0c92961ff8d2eceaae0c Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1178/1530] bkpr: don't use a minus in a sql stmt Avoid rounding errors in sql by doing it in code. --- plugins/bkpr/recorder.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 3fc57fa63e7f..7e6a241ad059 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -223,7 +223,8 @@ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) ", of.account_id" ", a.name" ", of.currency" - ", CAST(SUM(of.credit) AS BIGINT) - CAST(SUM(of.debit) AS BIGINT) as fees" + ", CAST(SUM(of.credit) AS BIGINT) as credit" + ", CAST(SUM(of.debit) AS BIGINT) as debit" " FROM onchain_fees of" " LEFT OUTER JOIN accounts a" " ON of.account_id = a.id" @@ -238,6 +239,8 @@ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) sums = tal_arr(ctx, struct fee_sum *, 0); while (db_step(stmt)) { struct fee_sum *sum; + struct amount_msat debit; + bool ok; sum = tal(sums, struct fee_sum); sum->txid = tal(sum, struct bitcoin_txid); @@ -246,8 +249,12 @@ struct fee_sum **calculate_onchain_fee_sums(const tal_t *ctx, struct db *db) sum->acct_db_id = db_col_u64(stmt, "of.account_id"); sum->acct_name = db_col_strdup(sum, stmt, "a.name"); sum->currency = db_col_strdup(sum, stmt, "of.currency"); - db_col_amount_msat(stmt, "fees", &sum->fees_paid); + db_col_amount_msat(stmt, "credit", &sum->fees_paid); + db_col_amount_msat(stmt, "debit", &debit); + ok = amount_msat_sub(&sum->fees_paid, sum->fees_paid, + debit); + assert(ok); tal_arr_expand(&sums, sum); } From d885407e3e91fb64b5402aab46299dfc322726a6 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:38 +0930 Subject: [PATCH 1179/1530] bkpr, elements: elements tx have one extra output for fees if it's an elements chain, subtract one from the output count we wait to calculate fees for a channel close until all the outputs are accounted for, but elements chains create a separate output for the amount of fees that are paid on a tx. fixes crash in `test_penalty_rbf_burn` --- onchaind/onchaind.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index f144f04ce611..4cd20023f8fc 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -3915,6 +3915,9 @@ int main(int argc, char *argv[]) &funding, tx_blockheight, our_msat, funding_sats, + is_elements(chainparams) ? + /* Minus 1, fee output */ + tal_count(tx->outputs) - 1 : tal_count(tx->outputs)))); status_debug("Remote per-commit point: %s", From e2ef44c043fff54eccc8a70b80a479c6c7b7292f Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1180/1530] bkpr: add 'msat' suffix to all msat denominated fields Makes our json schema parsing work as expected. --- plugins/bkpr/bookkeeper.c | 14 +++++++------- plugins/bkpr/chain_event.c | 4 ++-- plugins/bkpr/channel_event.c | 6 +++--- plugins/bkpr/channelsapy.c | 22 +++++++++++----------- plugins/bkpr/incomestmt.c | 4 ++-- plugins/bkpr/onchain_fee.c | 4 ++-- tests/test_bookkeeper.py | 32 ++++++++++++++++---------------- tests/test_pay.py | 4 ++-- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 8c2a79208c43..60535373f7cd 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -280,10 +280,10 @@ static struct command_result *json_inspect(struct command *cmd, fee_sum = find_sum_for_txid(fee_sums, set->txid); if (fee_sum) - json_add_amount_msat_only(res, "fees_paid", + json_add_amount_msat_only(res, "fees_paid_msat", fee_sum->fees_paid); else - json_add_amount_msat_only(res, "fees_paid", + json_add_amount_msat_only(res, "fees_paid_msat", AMOUNT_MSAT(0)); json_array_start(res, "outputs"); @@ -311,9 +311,9 @@ static struct command_result *json_inspect(struct command *cmd, json_add_num(res, "outnum", ev->outpoint.n); json_add_string(res, "output_tag", ev->tag); - json_add_amount_msat_only(res, "output_value", + json_add_amount_msat_only(res, "output_value_msat", ev->output_value); - json_add_amount_msat_only(res, "credit", + json_add_amount_msat_only(res, "credit_msat", ev->credit); json_add_string(res, "currency", ev->currency); if (ev->origin_acct) @@ -328,7 +328,7 @@ static struct command_result *json_inspect(struct command *cmd, ev->acct_name); json_add_num(res, "outnum", ev->outpoint.n); - json_add_amount_msat_only(res, "output_value", + json_add_amount_msat_only(res, "output_value_msat", ev->output_value); json_add_string(res, "currency", ev->currency); @@ -336,7 +336,7 @@ static struct command_result *json_inspect(struct command *cmd, json_add_string(res, "spend_tag", ev->tag); json_add_txid(res, "spending_txid", ev->spending_txid); - json_add_amount_msat_only(res, "debit", ev->debit); + json_add_amount_msat_only(res, "debit_msat", ev->debit); if (ev->payment_id) json_add_sha256(res, "payment_id", ev->payment_id); @@ -499,7 +499,7 @@ static struct command_result *json_list_balances(struct command *cmd, json_array_start(res, "balances"); for (size_t j = 0; j < tal_count(balances); j++) { json_object_start(res, NULL); - json_add_amount_msat_only(res, "balance", + json_add_amount_msat_only(res, "balance_msat", balances[j]->balance); json_add_string(res, "coin_type", balances[j]->currency); diff --git a/plugins/bkpr/chain_event.c b/plugins/bkpr/chain_event.c index 9b055782e792..633aedb1db71 100644 --- a/plugins/bkpr/chain_event.c +++ b/plugins/bkpr/chain_event.c @@ -11,8 +11,8 @@ void json_add_chain_event(struct json_stream *out, struct chain_event *ev) json_add_string(out, "origin", ev->origin_acct); json_add_string(out, "type", "chain"); json_add_string(out, "tag", ev->tag); - json_add_amount_msat_only(out, "credit", ev->credit); - json_add_amount_msat_only(out, "debit", ev->debit); + json_add_amount_msat_only(out, "credit_msat", ev->credit); + json_add_amount_msat_only(out, "debit_msat", ev->debit); json_add_string(out, "currency", ev->currency); json_add_outpoint(out, "outpoint", &ev->outpoint); diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index 58ca871d0a5a..ca71f9f672a2 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -37,10 +37,10 @@ void json_add_channel_event(struct json_stream *out, json_add_string(out, "account", ev->acct_name); json_add_string(out, "type", "channel"); json_add_string(out, "tag", ev->tag); - json_add_amount_msat_only(out, "credit", ev->credit); - json_add_amount_msat_only(out, "debit", ev->debit); + json_add_amount_msat_only(out, "credit_msat", ev->credit); + json_add_amount_msat_only(out, "debit_msat", ev->debit); if (!amount_msat_zero(ev->fees)) - json_add_amount_msat_only(out, "fees", ev->fees); + json_add_amount_msat_only(out, "fees_msat", ev->fees); json_add_string(out, "currency", ev->currency); if (ev->payment_id) json_add_sha256(out, "payment_id", ev->payment_id); diff --git a/plugins/bkpr/channelsapy.c b/plugins/bkpr/channelsapy.c index 429fbffcbdaf..bbf17c121084 100644 --- a/plugins/bkpr/channelsapy.c +++ b/plugins/bkpr/channelsapy.c @@ -301,21 +301,21 @@ void json_add_channel_apy(struct json_stream *res, json_add_string(res, "account", apy->acct_name); - json_add_amount_msat_only(res, "routed_out", apy->routed_out); - json_add_amount_msat_only(res, "routed_in", apy->routed_in); - json_add_amount_msat_only(res, "lease_fee_paid", apy->lease_out); - json_add_amount_msat_only(res, "lease_fee_earned", apy->lease_in); - json_add_amount_msat_only(res, "pushed_out", apy->push_out); - json_add_amount_msat_only(res, "pushed_in", apy->push_in); - - json_add_amount_msat_only(res, "our_start_balance", apy->our_start_bal); - json_add_amount_msat_only(res, "channel_start_balance", + json_add_amount_msat_only(res, "routed_out_msat", apy->routed_out); + json_add_amount_msat_only(res, "routed_in_msat", apy->routed_in); + json_add_amount_msat_only(res, "lease_fee_paid_msat", apy->lease_out); + json_add_amount_msat_only(res, "lease_fee_earned_msat", apy->lease_in); + json_add_amount_msat_only(res, "pushed_out_msat", apy->push_out); + json_add_amount_msat_only(res, "pushed_in_msat", apy->push_in); + + json_add_amount_msat_only(res, "our_start_balance_msat", apy->our_start_bal); + json_add_amount_msat_only(res, "channel_start_balance_msat", apy->total_start_bal); ok = amount_msat_add(&total_fees, apy->fees_in, apy->fees_out); assert(ok); - json_add_amount_msat_only(res, "fees_out", apy->fees_out); - json_add_amount_msat_only(res, "fees_in", apy->fees_in); + json_add_amount_msat_only(res, "fees_out_msat", apy->fees_out); + json_add_amount_msat_only(res, "fees_in_msat", apy->fees_in); /* utilization (out): routed_out/total_balance */ assert(!amount_msat_zero(apy->total_start_bal)); diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index fcc529c6d551..0a2eab100503 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -392,8 +392,8 @@ void json_add_income_event(struct json_stream *out, struct income_event *ev) json_object_start(out, NULL); json_add_string(out, "account", ev->acct_name); json_add_string(out, "tag", ev->tag); - json_add_amount_msat_only(out, "credit", ev->credit); - json_add_amount_msat_only(out, "debit", ev->debit); + json_add_amount_msat_only(out, "credit_msat", ev->credit); + json_add_amount_msat_only(out, "debit_msat", ev->debit); json_add_string(out, "currency", ev->currency); json_add_u64(out, "timestamp", ev->timestamp); diff --git a/plugins/bkpr/onchain_fee.c b/plugins/bkpr/onchain_fee.c index 57c1002fb703..b4667c14b391 100644 --- a/plugins/bkpr/onchain_fee.c +++ b/plugins/bkpr/onchain_fee.c @@ -10,8 +10,8 @@ void json_add_onchain_fee(struct json_stream *out, json_add_string(out, "account", fee->acct_name); json_add_string(out, "type", "onchain_fee"); json_add_string(out, "tag", "onchain_fee"); - json_add_amount_msat_only(out, "credit", fee->credit); - json_add_amount_msat_only(out, "debit", fee->debit); + json_add_amount_msat_only(out, "credit_msat", fee->credit); + json_add_amount_msat_only(out, "debit_msat", fee->debit); json_add_string(out, "currency", fee->currency); json_add_u64(out, "timestamp", fee->timestamp); json_add_txid(out, "txid", &fee->txid); diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 026be33db1dd..08ecd74a14b9 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -56,7 +56,7 @@ def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): fees = find_tags(evs, 'onchain_fee') close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) + assert close_fee[0]['credit_msat'] + delayed_to['credit_msat'] == close['debit_msat'] # l2's fees should equal the trimmed htlc out evs = l2.rpc.listaccountevents()['events'] @@ -66,8 +66,8 @@ def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 # sent htlc was too small, we lose it, rounded up to nearest sat - assert close_fee[0]['credit'] == '101000msat' - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) + assert close_fee[0]['credit_msat'] == Millisatoshi('101000msat') + assert close_fee[0]['credit_msat'] + deposit['credit_msat'] == close['debit_msat'] @unittest.skipIf(TEST_NETWORK != 'regtest', "fixme: broadcast fails, dusty") @@ -96,7 +96,7 @@ def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): fees = find_tags(evs, 'onchain_fee') close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(delayed_to['credit']) == Millisatoshi(close['debit']) + assert close_fee[0]['credit_msat'] + delayed_to['credit_msat'] == close['debit_msat'] evs = l2.rpc.listaccountevents()['events'] close = find_first_tag(evs, 'channel_close') @@ -105,8 +105,8 @@ def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): close_fee = [e for e in fees if e['txid'] == close['txid']] assert len(close_fee) == 1 # too small to fit, we lose them as miner fees - assert close_fee[0]['credit'] == '333msat' - assert Millisatoshi(close_fee[0]['credit']) + Millisatoshi(deposit['credit']) == Millisatoshi(close['debit']) + assert close_fee[0]['credit_msat'] == Millisatoshi('333msat') + assert close_fee[0]['credit_msat'] + deposit['credit_msat'] == close['debit_msat'] @unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") @@ -141,15 +141,15 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): for inc in incomes: assert inc['account'] == 'wallet' assert inc['tag'] == 'deposit' - assert Millisatoshi(inc['credit']) == amount_msat + assert inc['credit_msat'] == amount_msat # The event should show up in the 'listaccountevents' however events = l1.rpc.listaccountevents()['events'] assert len(events) == 3 external = [e for e in events if e['account'] == 'external'][0] - assert Millisatoshi(external['credit']) == Millisatoshi(amount // 2 * 1000) + assert external['credit_msat'] == Millisatoshi(amount // 2 * 1000) btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) - assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 + assert btc_balance['balance_msat'] == amount_msat * 2 # Restart the node, issues a balance snapshot # If we were counting these incorrectly, @@ -166,7 +166,7 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): # the wallet balance should be unchanged btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) - assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 + assert btc_balance['balance_msat'] == amount_msat * 2 # ok now we mine a block bitcoind.generate_block(1) @@ -177,16 +177,16 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): incomes = l1.rpc.listincome()['income_events'] # 2 wallet deposits, 1 wallet withdrawal, 1 onchain_fee assert len(incomes) == 4 - withdraw_amt = Millisatoshi(find_tags(incomes, 'withdrawal')[0]['debit']) + withdraw_amt = find_tags(incomes, 'withdrawal')[0]['debit_msat'] assert withdraw_amt == Millisatoshi(amount // 2 * 1000) fee_events = find_tags(incomes, 'onchain_fee') assert len(fee_events) == 1 - fees = Millisatoshi(fee_events[0]['debit']) + fees = fee_events[0]['debit_msat'] # wallet balance is decremented now btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) - assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 - withdraw_amt - fees + assert btc_balance['balance_msat'] == amount_msat * 2 - withdraw_amt - fees @unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") @@ -233,7 +233,7 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): # the wallet balance should be unchanged btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) - assert Millisatoshi(btc_balance['balance']) == amount_msat * 2 + assert btc_balance['balance_msat'] == amount_msat * 2 # ok now we mine a block bitcoind.generate_block(1) @@ -248,12 +248,12 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): fee_events = find_tags(incomes, 'onchain_fee') assert len(fee_events) == 1 - fees = Millisatoshi(fee_events[0]['debit']) + fees = fee_events[0]['debit_msat'] assert fees > Millisatoshi(amount // 2 * 1000) # wallet balance is decremented now bal = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) - assert Millisatoshi(bal['balance']) == amount_msat * 2 - fees + assert bal['balance_msat'] == amount_msat * 2 - fees @unittest.skipIf(TEST_NETWORK != 'regtest', "External wallet support doesn't work with elements yet.") diff --git a/tests/test_pay.py b/tests/test_pay.py index dc4bd15fe76b..7a5556250244 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1314,9 +1314,9 @@ def _income_tagset(node, tagset): # the balance on l3 should equal the invoice bal = only_one(only_one(l3.rpc.listbalances()['accounts'])['balances'])['balance'] assert incomes[0]['tag'] == 'invoice' - assert bal == incomes[0]['debit'] + assert Millisatoshi(bal) == incomes[0]['debit_msat'] inve = only_one([e for e in l1.rpc.listaccountevents()['events'] if e['tag'] == 'invoice']) - assert Millisatoshi(inve['debit']) == Millisatoshi(incomes[0]['debit']) + Millisatoshi(incomes[1]['debit']) + assert inve['debit_msat'] == incomes[0]['debit_msat'] + incomes[1]['debit_msat'] @pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs") From 6dfba2468ab60e78c717e4a8b5079286aead9222 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1181/1530] json-schema: allow 'required' to not be present in if switches --- tools/fromschema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/fromschema.py b/tools/fromschema.py index a8708c5fe215..a1291e897058 100755 --- a/tools/fromschema.py +++ b/tools/fromschema.py @@ -148,13 +148,13 @@ def output_members(sub, indent=''): for p in sub['properties']: if p.startswith('warning'): continue - if p in sub['required']: + if 'required' in sub and 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']: + if 'required' not in sub or p not in sub['required']: output_member(p, sub['properties'][p], True, indent) if warnings != []: From 563910e66798c18bab6a97be59660fc6ab601bab Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1182/1530] bkpr: add docs, change names to 'bkpr-*' Adds schema definitions and manpages for bkpr- commands; also renames the commands to all start with 'bkpr-', so they're easier to identify/ make runes about. --- doc/Makefile | 6 + doc/index.rst | 6 + doc/lightning-bkpr-channelsapy.7.md | 67 +++++++ doc/lightning-bkpr-dumpincomecsv.7.md | 59 ++++++ doc/lightning-bkpr-inspect.7.md | 54 ++++++ doc/lightning-bkpr-listaccountevents.7.md | 68 +++++++ doc/lightning-bkpr-listbalances.7.md | 54 ++++++ doc/lightning-bkpr-listincome.7.md | 58 ++++++ doc/schemas/bkpr-channelsapy.schema.json | 124 ++++++++++++ doc/schemas/bkpr-dumpincomecsv.schema.json | 25 +++ doc/schemas/bkpr-inspect.schema.json | 150 +++++++++++++++ .../bkpr-listaccountevents.schema.json | 178 ++++++++++++++++++ doc/schemas/bkpr-listbalances.schema.json | 95 ++++++++++ doc/schemas/bkpr-listincome.schema.json | 63 +++++++ plugins/bkpr/bookkeeper.c | 14 +- tests/test_bookkeeper.py | 60 +++--- tests/test_closing.py | 4 +- tests/test_pay.py | 6 +- 18 files changed, 1049 insertions(+), 42 deletions(-) create mode 100644 doc/lightning-bkpr-channelsapy.7.md create mode 100644 doc/lightning-bkpr-dumpincomecsv.7.md create mode 100644 doc/lightning-bkpr-inspect.7.md create mode 100644 doc/lightning-bkpr-listaccountevents.7.md create mode 100644 doc/lightning-bkpr-listbalances.7.md create mode 100644 doc/lightning-bkpr-listincome.7.md create mode 100644 doc/schemas/bkpr-channelsapy.schema.json create mode 100644 doc/schemas/bkpr-dumpincomecsv.schema.json create mode 100644 doc/schemas/bkpr-inspect.schema.json create mode 100644 doc/schemas/bkpr-listaccountevents.schema.json create mode 100644 doc/schemas/bkpr-listbalances.schema.json create mode 100644 doc/schemas/bkpr-listincome.schema.json diff --git a/doc/Makefile b/doc/Makefile index 04be559e9cd8..743bf05c4bf2 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -9,6 +9,12 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightningd-config.5 \ doc/lightning-addgossip.7 \ doc/lightning-autocleaninvoice.7 \ + doc/lightning-bkpr-channelsapy.7 \ + doc/lightning-bkpr-dumpincomecsv.7 \ + doc/lightning-bkpr-inspect.7 \ + doc/lightning-bkpr-listaccountevents.7 \ + doc/lightning-bkpr-listbalances.7 \ + doc/lightning-bkpr-listincome.7 \ doc/lightning-check.7 \ doc/lightning-checkmessage.7 \ doc/lightning-close.7 \ diff --git a/doc/index.rst b/doc/index.rst index b60ced919aca..593bc8624650 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -31,6 +31,12 @@ Core Lightning Documentation lightning-addgossip lightning-autocleaninvoice + lightning-bkpr-channelsapy + lightning-bkpr-dumpincomecsv + lightning-bkpr-inspect + lightning-bkpr-listaccountevents + lightning-bkpr-listbalances + lightning-bkpr-listincome lightning-check lightning-checkmessage lightning-cli diff --git a/doc/lightning-bkpr-channelsapy.7.md b/doc/lightning-bkpr-channelsapy.7.md new file mode 100644 index 000000000000..7eac494ce1dc --- /dev/null +++ b/doc/lightning-bkpr-channelsapy.7.md @@ -0,0 +1,67 @@ +lightning-bkpr-channelsapy -- Command to list stats on channel earnings +================================================================== + +SYNOPSIS +-------- + +**bkpr-channelsapy** \[*start_time*\] \[*end_time*\] + +DESCRIPTION +----------- + +The **bkpr-channelsapy** RPC command lists stats on routing income, leasing income, +and various calculated APYs for channel routed funds. + +The **start_time** is a UNIX timestamp (in seconds) that filters events after the provided timestamp. Defaults to zero. + +The **end_time** is a UNIX timestamp (in seconds) that filters events up to and at the provided timestamp. Defaults to max-int. + + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **channels_apy** is returned. It is an array of objects, where each object contains: +- **account** (string): The account name. If the account is a channel, the channel_id. The 'net' entry is the rollup of all channel accounts +- **routed_out_msat** (msat): Sats routed (outbound) +- **routed_in_msat** (msat): Sats routed (inbound) +- **lease_fee_paid_msat** (msat): Sats paid for leasing inbound (liquidity ads) +- **lease_fee_earned_msat** (msat): Sats earned for leasing outbound (liquidity ads) +- **pushed_out_msat** (msat): Sats pushed to peer at open +- **pushed_in_msat** (msat): Sats pushed in from peer at open +- **our_start_balance_msat** (msat): Starting balance in channel at funding. Note that if our start ballance is zero, any _initial field will be omitted (can't divide by zero) +- **channel_start_balance_msat** (msat): Total starting balance at funding +- **fees_out_msat** (msat): Fees earned on routed outbound +- **utilization_out** (string): Sats routed outbound / total start balance +- **utilization_in** (string): Sats routed inbound / total start balance +- **apy_out** (string): Fees earned on outbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) +- **apy_in** (string): Fees earned on inbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) +- **apy_total** (string): Total fees earned on routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) +- **fees_in_msat** (msat, optional): Fees earned on routed inbound +- **utilization_out_initial** (string, optional): Sats routed outbound / our start balance +- **utilization_in_initial** (string, optional): Sats routed inbound / our start balance +- **apy_out_initial** (string, optional): Fees earned on outbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) +- **apy_in_initial** (string, optional): Fees earned on inbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) +- **apy_total_initial** (string, optional): Total fees earned on routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) +- **apy_lease** (string, optional): Lease fees earned over total amount leased for the lease term, amortized to a year (APY). Only appears if channel was leased out by us + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +niftynei is mainly responsible. + +SEE ALSO +-------- + +lightning-bkpr-listincome(7), lightning-bkpr-listfunds(7), +lightning-bkpr-listaccountevents(7), +lightning-bkpr-dumpincomecsv(7), lightning-listpeers(7). + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:435fd03765ef0a8bcaef7f309673cdac9cb7c8ba776ac77de21aea8d702998a3) diff --git a/doc/lightning-bkpr-dumpincomecsv.7.md b/doc/lightning-bkpr-dumpincomecsv.7.md new file mode 100644 index 000000000000..70c8a7458637 --- /dev/null +++ b/doc/lightning-bkpr-dumpincomecsv.7.md @@ -0,0 +1,59 @@ +lightning-bkpr-dumpincomecsv -- Command to emit a CSV of income events +================================================================= + +SYNOPSIS +-------- + +**bkpr-dumpincomecsv** *csv_format* \[*csv_file*\] \[*consolidate_fees*\] \[*start_time*\] \[*end_time*\] + +DESCRIPTION +----------- + +The **bkpr-dumpincomcsv** RPC command writes a CSV file to disk at *csv_file* +location. This is a formatted output of the **listincome** RPC command. + +**csv_format** is which CSV format to use. See RETURN VALUE for options. + +**csv_file** is the on-disk destination of the generated CSV file. + +If **consolidate_fees** is true, we emit a single, consolidated event for +any onchain-fees for a txid and account. Otherwise, events for every update to +the onchain fee calculation for this account and txid will be printed. +Defaults to true. Note that this means that the events emitted are +non-stable, i.e. calling **dumpincomecsv** twice may result in different +onchain fee events being emitted, depending on how much information we've +logged for that transaction. + +The **start_time** is a UNIX timestamp (in seconds) that filters events after the provided timestamp. Defaults to zero. + +The **end_time** is a UNIX timestamp (in seconds) that filters events up to and at the provided timestamp. Defaults to max-int. + + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **csv_file** (string): File that the csv was generated to +- **csv_format** (string): Format to print csv as (one of "cointracker", "koinly", "harmony", "quickbooks") + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +niftynei is mainly responsible. + +SEE ALSO +-------- + +lightning-bkpr-listincome(7), lightning-bkpr-listfunds(7), +lightning-bkpr-listaccountevents(7), +lightning-bkpr-channelsapy(7), lightning-listpeers(7). + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:e6000f40905c4fa23a5115e8bc82f4f1556f55118fb4ede41dd5e54957da0fa3) diff --git a/doc/lightning-bkpr-inspect.7.md b/doc/lightning-bkpr-inspect.7.md new file mode 100644 index 000000000000..4f5785bf7ff9 --- /dev/null +++ b/doc/lightning-bkpr-inspect.7.md @@ -0,0 +1,54 @@ +lightning-bkpr-inspect -- Command to show onchain footprint of a channel +=================================================================== + +SYNOPSIS +-------- + +**bkpr-inspect** *account* + +DESCRIPTION +----------- + +The **bkpr-inspect** RPC command lists all known on-chain transactions and +associated events for the provided account. Useful for inspecting unilateral +closes for a given channel account. Only valid for channel accounts. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **txs** is returned. It is an array of objects, where each object contains: +- **txid** (txid): transaction id +- **fees_paid_msat** (msat): Amount paid in sats for this tx +- **outputs** (array of objects): + - **account** (string): Account this output affected + - **outnum** (u32): Index of output + - **output_value_msat** (msat): Value of the output + - **currency** (string): human-readable bech32 part for this coin type + - **credit_msat** (msat, optional): Amount credited to account + - **debit_msat** (msat, optional): Amount debited from account + - **originating_account** (string, optional): Account this output originated from + - **output_tag** (string, optional): Description of output creation event + - **spend_tag** (string, optional): Description of output spend event + - **spending_txid** (txid, optional): Transaction this output was spent in + - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. +- **blockheight** (u32, optional): Blockheight of transaction + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +niftynei is mainly responsible. + +SEE ALSO +-------- + +lightning-listbalances(7), lightning-listfunds(7), lightning-listpeers(7). + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:9df98d40e1ed1b0c72f4a4e8c00d243e10f159b99c534818f04631ec3d17a445) diff --git a/doc/lightning-bkpr-listaccountevents.7.md b/doc/lightning-bkpr-listaccountevents.7.md new file mode 100644 index 000000000000..b2b393aa70b5 --- /dev/null +++ b/doc/lightning-bkpr-listaccountevents.7.md @@ -0,0 +1,68 @@ +lightning-bkpr-listaccountevents -- Command for listing recorded bookkeeping events +============================================================================= + +SYNOPSIS +-------- + +**bkpr-listaccountevents** [\*account\*] + +DESCRIPTION +----------- + +The **bkpr-listaccountevents** RPC command is a list of all bookkeeping events that have been recorded for this node. + +If the optional parameter **account** is set, we only emit events for the +specified account, if exists. + +Note that the type **onchain_fees** that are emitted are of opposite credit/debit than as they appear in **listincome**, as **listincome** shows all events from the perspective of the node, whereas **listaccountevents** just dumps the event data as we've got it. Onchain fees are updated/recorded as we get more information about input and output spends -- the total onchain fees that were recorded for a transaction for an account can be found by summing all onchain fee events and taking the difference between the **credit_msat** and **debit_msat** for these events. We do this so that successive calls to **listaccountevents** always +produce the same list of events -- no previously emitted event will be +subsequently updated, rather we add a new event to the list. + + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **events** is returned. It is an array of objects, where each object contains: +- **account** (string): The account name. If the account is a channel, the channel_id +- **type** (string): Coin movement type (one of "onchain_fee", "chain", "channel") +- **tag** (string): Description of movement +- **credit_msat** (msat): Amount credited +- **debit_msat** (msat): Amount debited +- **currency** (string): human-readable bech32 part for this coin type +- **timestamp** (u32): Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp + +If **type** is "chain": + - **outpoint** (string): The txid:outnum for this event + - **blockheight** (u32): For chain events, blockheight this occured at + - **origin** (string, optional): The account this movement originated from + - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. + - **txid** (txid, optional): The txid of the transaction that created this event + +If **type** is "onchain_fee": + - **txid** (txid): The txid of the transaction that created this event + +If **type** is "channel": + - **fees_msat** (msat, optional): Amount paid in fees + - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. + - **part_id** (u32, optional): Counter for multi-part payments + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +niftynei is mainly responsible. + +SEE ALSO +-------- + +lightning-bkpr-listincome(7), lightning-listfunds(7), +lightning-bkpr-listbalances(7), lightning-bkpr-channelsapy(7). + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:dd72cc73e685daa6877984be8edede76dfec2f9d85df9a88ab1b031a93b20549) diff --git a/doc/lightning-bkpr-listbalances.7.md b/doc/lightning-bkpr-listbalances.7.md new file mode 100644 index 000000000000..80f12297a5a1 --- /dev/null +++ b/doc/lightning-bkpr-listbalances.7.md @@ -0,0 +1,54 @@ +lightning-bkpr-listbalances -- Command for listing current channel + wallet balances +=============================================================================== + +SYNOPSIS +-------- + +**bkpr-listbalances** + +DESCRIPTION +----------- + +The **bkpr-listbalances** RPC command is a list of all current and historical account balances. An account is either the on-chain *wallet* or a channel balance. +Any funds sent to an *external* account will not be accounted for here. + +Note that any channel that was recorded will be listed. Closed channel balances +will be 0msat. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **accounts** is returned. It is an array of objects, where each object contains: +- **account** (string): The account name. If the account is a channel, the channel_id +- **balances** (array of objects): + - **balance_msat** (msat): Current account balance + - **coin_type** (string): coin type, same as HRP for bech32 + +If **peer_id** is present: + - **peer_id** (pubkey): Node id for the peer this account is with + - **we_opened** (boolean): Did we initiate this account open (open the channel) + - **account_closed** (boolean): + - **account_resolved** (boolean): Has this channel been closed and all outputs resolved? + - **resolved_at_block** (u32, optional): Blockheight account resolved on chain + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +niftynei is mainly responsible. + +SEE ALSO +-------- + +lightning-bkpr-listincome(7), lightning-listfunds(7), +lightning-bkpr-listaccountevents(7), +lightning-bkpr-channelsapy(7), lightning-listpeers(7). + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:a3d1423f12bffc76fd1f2fdb5a07ff8a881290f2ea5eefa528cbb04fc3a7c639) diff --git a/doc/lightning-bkpr-listincome.7.md b/doc/lightning-bkpr-listincome.7.md new file mode 100644 index 000000000000..d7a3d87da143 --- /dev/null +++ b/doc/lightning-bkpr-listincome.7.md @@ -0,0 +1,58 @@ +lightning-bkpr-listincome -- Command for listing all income impacting events +======================================================================= + +SYNOPSIS +-------- + +**bkpr-listincome** \[*consolidate_fees*\] \[*start_time*\] \[*end_time*\] + +DESCRIPTION +----------- + +The **bkpr-listincome** RPC command is a list of all income impacting events that the bookkeeper plugin has recorded for this node. + +If **consolidate_fees** is true, we emit a single, consolidated event for +any onchain-fees for a txid and account. Otherwise, events for every update to +the onchain fee calculation for this account and txid will be printed. Defaults to true. Note that this means that the events emitted are non-stable, +i.e. calling **listincome** twice may result in different onchain fee events +being emitted, depending on how much information we've logged for that +transaction. + +The **start_time** is a UNIX timestamp (in seconds) that filters events after the provided timestamp. Defaults to zero. + +The **end_time** is a UNIX timestamp (in seconds) that filters events up to and at the provided timestamp. Defaults to max-int. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **income_events** is returned. It is an array of objects, where each object contains: +- **account** (string): The account name. If the account is a channel, the channel_id +- **tag** (string): Type of income event +- **credit_msat** (msat): Amount earned (income) +- **debit_msat** (msat): Amount spent (expenses) +- **currency** (string): human-readable bech32 part for this coin type +- **timestamp** (u32): Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp +- **outpoint** (string, optional): The txid:outnum for this event, if applicable +- **txid** (txid, optional): The txid of the transaction that created this event, if applicable +- **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +niftynei is mainly responsible. + +SEE ALSO +-------- + +lightning-bkpr-listaccountevents(7), lightning-listfunds(7), +lightning-bkpr-listbalances(7). + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:400ac5e6719a7ae5ec7078a2cd220d91ab7e66ad45f08b46257e6ec04dcdeb4c) diff --git a/doc/schemas/bkpr-channelsapy.schema.json b/doc/schemas/bkpr-channelsapy.schema.json new file mode 100644 index 000000000000..0264a1f14b18 --- /dev/null +++ b/doc/schemas/bkpr-channelsapy.schema.json @@ -0,0 +1,124 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "channels_apy" + ], + "properties": { + "channels_apy": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "account", + "routed_out_msat", + "routed_in_msat", + "lease_fee_paid_msat", + "lease_fee_earned_msat", + "pushed_out_msat", + "pushed_in_msat", + "our_start_balance_msat", + "channel_start_balance_msat", + "fees_out_msat", + "utilization_out", + "utilization_in", + "apy_out", + "apy_in", + "apy_total" + ], + "properties": { + "account": { + "type": "string", + "description": "The account name. If the account is a channel, the channel_id. The 'net' entry is the rollup of all channel accounts" + }, + "routed_out_msat": { + "type": "msat", + "description": "Sats routed (outbound)" + }, + "routed_in_msat": { + "type": "msat", + "description": "Sats routed (inbound)" + }, + "lease_fee_paid_msat": { + "type": "msat", + "description": "Sats paid for leasing inbound (liquidity ads)" + }, + "lease_fee_earned_msat": { + "type": "msat", + "description": "Sats earned for leasing outbound (liquidity ads)" + }, + "pushed_out_msat": { + "type": "msat", + "description": "Sats pushed to peer at open" + }, + "pushed_in_msat": { + "type": "msat", + "description": "Sats pushed in from peer at open" + }, + "our_start_balance_msat": { + "type": "msat", + "description": "Starting balance in channel at funding. Note that if our start ballance is zero, any _initial field will be omitted (can't divide by zero)" + }, + "channel_start_balance_msat": { + "type": "msat", + "description": "Total starting balance at funding" + }, + "fees_out_msat": { + "type": "msat", + "description": "Fees earned on routed outbound" + }, + "fees_in_msat": { + "type": "msat", + "description": "Fees earned on routed inbound" + }, + "utilization_out": { + "type": "string", + "description": "Sats routed outbound / total start balance" + }, + "utilization_out_initial": { + "type": "string", + "description": "Sats routed outbound / our start balance" + }, + "utilization_in": { + "type": "string", + "description": "Sats routed inbound / total start balance" + }, + "utilization_in_initial": { + "type": "string", + "description": "Sats routed inbound / our start balance" + }, + "apy_out": { + "type": "string", + "description": "Fees earned on outbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY)" + }, + "apy_out_initial": { + "type": "string", + "description": "Fees earned on outbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY)" + }, + "apy_in": { + "type": "string", + "description": "Fees earned on inbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY)" + }, + "apy_in_initial": { + "type": "string", + "description": "Fees earned on inbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY)" + }, + "apy_total": { + "type": "string", + "description": "Total fees earned on routed payments / total start balance for the length of time this channel has been open amortized to a year (APY)" + }, + "apy_total_initial": { + "type": "string", + "description": "Total fees earned on routed payments / our start balance for the length of time this channel has been open amortized to a year (APY)" + }, + "apy_lease": { + "type": "string", + "description": "Lease fees earned over total amount leased for the lease term, amortized to a year (APY). Only appears if channel was leased out by us" + } + } + } + } + } +} diff --git a/doc/schemas/bkpr-dumpincomecsv.schema.json b/doc/schemas/bkpr-dumpincomecsv.schema.json new file mode 100644 index 000000000000..fe3da41d536a --- /dev/null +++ b/doc/schemas/bkpr-dumpincomecsv.schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "unevaluatedProperties": false, + "required": [ + "csv_file", + "csv_format" + ], + "properties": { + "csv_file": { + "type": "string", + "description": "File that the csv was generated to" + }, + "csv_format": { + "type": "string", + "enum": [ + "cointracker", + "koinly", + "harmony", + "quickbooks" + ], + "description": "Format to print csv as" + } + } +} diff --git a/doc/schemas/bkpr-inspect.schema.json b/doc/schemas/bkpr-inspect.schema.json new file mode 100644 index 000000000000..24ab7504f200 --- /dev/null +++ b/doc/schemas/bkpr-inspect.schema.json @@ -0,0 +1,150 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "txs" + ], + "properties": { + "txs": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "txid", + "fees_paid_msat", + "outputs" + ], + "properties": { + "txid": { + "type": "txid", + "description": "transaction id" + }, + "blockheight": { + "type": "u32", + "description": "Blockheight of transaction" + }, + "fees_paid_msat": { + "type": "msat", + "description": "Amount paid in sats for this tx" + }, + "outputs": { + "type": "array", + "items": { + "type": "object", + "required": [ + "account", + "outnum", + "output_value_msat", + "currency" + ], + "additionalProperties": false, + "properties": { + "account": { + "type": "string", + "description": "Account this output affected" + }, + "outnum": { + "type": "u32", + "description": "Index of output" + }, + "output_value_msat": { + "type": "msat", + "description": "Value of the output" + }, + "currency": { + "type": "string", + "description": "human-readable bech32 part for this coin type" + }, + "credit_msat": { + "type": "msat", + "description": "Amount credited to account" + }, + "debit_msat": { + "type": "msat", + "description": "Amount debited from account" + }, + "originating_account": { + "type": "string", + "description": "Account this output originated from" + }, + "output_tag": { + "type": "string", + "description": "Description of output creation event" + }, + "spend_tag": { + "type": "string", + "description": "Description of output spend event" + }, + "spending_txid": { + "type": "txid", + "description": "Transaction this output was spent in" + }, + "payment_id": { + "type": "hex", + "description": "lightning payment identifier. For an htlc, this will be the preimage." + } + }, + "allOf": [ + { + "if": { + "required": [ + "credit_msat" + ] + }, + "then": { + "required": [ + "output_tag" + ], + "additionalProperties": false, + "properties": { + "account": {}, + "outnum": {}, + "output_value_msat": {}, + "currency": {}, + "credit_msat": {}, + "originating_account": {}, + "debit_msat": {}, + "output_tag": {}, + "spend_tag": {}, + "spending_txid": {}, + "payment_id": {} + } + } + }, + { + "if": { + "required": [ + "spending_txid" + ] + }, + "then": { + "required": [ + "spend_tag", + "debit_msat" + ], + "additionalProperties": false, + "properties": { + "account": {}, + "outnum": {}, + "output_value_msat": {}, + "currency": {}, + "credit_msat": {}, + "originating_account": {}, + "debit_msat": {}, + "output_tag": {}, + "spend_tag": {}, + "spending_txid": {}, + "payment_id": {} + } + } + } + ] + } + } + } + } + } + } +} diff --git a/doc/schemas/bkpr-listaccountevents.schema.json b/doc/schemas/bkpr-listaccountevents.schema.json new file mode 100644 index 000000000000..2a72cbf8e1ff --- /dev/null +++ b/doc/schemas/bkpr-listaccountevents.schema.json @@ -0,0 +1,178 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "events" + ], + "properties": { + "events": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "account", + "type", + "tag", + "credit_msat", + "debit_msat", + "currency", + "timestamp" + ], + "properties": { + "account": { + "type": "string", + "description": "The account name. If the account is a channel, the channel_id" + }, + "type": { + "type": "string", + "enum": [ + "onchain_fee", + "chain", + "channel" + ], + "description": "Coin movement type" + }, + "tag": { + "type": "string", + "description": "Description of movement" + }, + "credit_msat": { + "type": "msat", + "description": "Amount credited" + }, + "debit_msat": { + "type": "msat", + "description": "Amount debited" + }, + "currency": { + "type": "string", + "description": "human-readable bech32 part for this coin type" + }, + "timestamp": { + "type": "u32", + "description": "Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "chain" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "outpoint": { + "type": "string", + "description": "The txid:outnum for this event" + }, + "blockheight": { + "type": "u32", + "description": "For chain events, blockheight this occured at" + }, + "origin": { + "type": "string", + "description": "The account this movement originated from" + }, + "payment_id": { + "type": "hex", + "description": "lightning payment identifier. For an htlc, this will be the preimage." + }, + "txid": { + "type": "txid", + "description": "The txid of the transaction that created this event" + } + }, + "required": [ + "outpoint", + "blockheight" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "onchain_fee" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "txid": { + "type": "txid", + "description": "The txid of the transaction that created this event" + } + }, + "required": [ + "txid" + ], + "additionalProperties": false + } + }, + { + "if": { + "properties": { + "type": { + "type": "string", + "enum": [ + "channel" + ] + } + } + }, + "then": { + "properties": { + "account": {}, + "type": {}, + "tag": {}, + "credit_msat": {}, + "debit_msat": {}, + "currency": {}, + "timestamp": {}, + "fees_msat": { + "type": "msat", + "description": "Amount paid in fees" + }, + "payment_id": { + "type": "hex", + "description": "lightning payment identifier. For an htlc, this will be the preimage." + }, + "part_id": { + "type": "u32", + "description": "Counter for multi-part payments" + } + }, + "additionalProperties": false + } + } + ] + } + } + } +} diff --git a/doc/schemas/bkpr-listbalances.schema.json b/doc/schemas/bkpr-listbalances.schema.json new file mode 100644 index 000000000000..85c4395522b7 --- /dev/null +++ b/doc/schemas/bkpr-listbalances.schema.json @@ -0,0 +1,95 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "accounts" + ], + "properties": { + "accounts": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "required": [ + "account", + "balances" + ], + "properties": { + "account": { + "type": "string", + "description": "The account name. If the account is a channel, the channel_id" + }, + "balances": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "balance_msat", + "coin_type" + ], + "properties": { + "balance_msat": { + "type": "msat", + "description": "Current account balance" + }, + "coin_type": { + "type": "string", + "description": "coin type, same as HRP for bech32" + } + } + } + } + }, + "if": { + "required": [ + "peer_id" + ] + }, + "then": { + "required": [ + "account", + "balances", + "peer_id", + "we_opened", + "account_closed", + "account_resolved" + ], + "additionalProperties": false, + "properties": { + "account": {}, + "balances": {}, + "peer_id": { + "type": "pubkey", + "description": "Node id for the peer this account is with" + }, + "we_opened": { + "type": "boolean", + "description": "Did we initiate this account open (open the channel)" + }, + "account_closed": { + "type": "boolean", + "description": "" + }, + "account_resolved": { + "type": "boolean", + "description": "Has this channel been closed and all outputs resolved?" + }, + "resolved_at_block": { + "type": "u32", + "description": "Blockheight account resolved on chain" + } + } + }, + "else": { + "properties": { + "account": {}, + "balances": {} + }, + "additionalProperties": false + } + } + } + } +} diff --git a/doc/schemas/bkpr-listincome.schema.json b/doc/schemas/bkpr-listincome.schema.json new file mode 100644 index 000000000000..b572bf80bfab --- /dev/null +++ b/doc/schemas/bkpr-listincome.schema.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "income_events" + ], + "properties": { + "income_events": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "account", + "tag", + "credit_msat", + "debit_msat", + "currency", + "timestamp" + ], + "properties": { + "account": { + "type": "string", + "description": "The account name. If the account is a channel, the channel_id" + }, + "tag": { + "type": "string", + "description": "Type of income event" + }, + "credit_msat": { + "type": "msat", + "description": "Amount earned (income)" + }, + "debit_msat": { + "type": "msat", + "description": "Amount spent (expenses)" + }, + "currency": { + "type": "string", + "description": "human-readable bech32 part for this coin type" + }, + "timestamp": { + "type": "u32", + "description": "Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp" + }, + "outpoint": { + "type": "string", + "description": "The txid:outnum for this event, if applicable" + }, + "txid": { + "type": "txid", + "description": "The txid of the transaction that created this event, if applicable" + }, + "payment_id": { + "type": "hex", + "description": "lightning payment identifier. For an htlc, this will be the preimage." + } + } + } + } + } +} diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 60535373f7cd..be91bc3a8e54 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -85,7 +85,7 @@ getblockheight_done(struct command *cmd, const char *buf, net_apys->total_start_bal = AMOUNT_MSAT(0); res = jsonrpc_stream_success(cmd); - json_array_start(res, "channel_apys"); + json_array_start(res, "channels_apy"); for (size_t i = 0; i < tal_count(apys); i++) { json_add_channel_apy(res, apys[i]); @@ -1474,14 +1474,14 @@ const struct plugin_notification notifs[] = { static const struct plugin_command commands[] = { { - "listbalances", + "bkpr-listbalances", "bookkeeping", "List current account balances", "List of current accounts and their balances", json_list_balances }, { - "listaccountevents", + "bkpr-listaccountevents", "bookkeeping", "List all events for an {account}", "List all events for an {account} (or all accounts, if" @@ -1489,21 +1489,21 @@ static const struct plugin_command commands[] = { json_list_account_events }, { - "inspect", + "bkpr-inspect", "utilities", "See the current on-chain graph of an {account}", "Prints out the on-chain footprint of a given {account}.", json_inspect }, { - "listincome", + "bkpr-listincome", "bookkeeping", "List all income impacting events", "List all events for this node that impacted income", json_list_income }, { - "dumpincomecsv", + "bkpr-dumpincomecsv", "bookkeeping", "Print out all the income events to a csv file in " " {csv_format", @@ -1513,7 +1513,7 @@ static const struct plugin_command commands[] = { json_dump_income }, { - "channelsapy", + "bkpr-channelsapy", "bookkeeping", "Stats on channel fund usage", "Print out stats on chanenl fund usage", diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 08ecd74a14b9..747fec5649f1 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -48,7 +48,7 @@ def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(r'All outputs resolved.*') - evs = l1.rpc.listaccountevents()['events'] + evs = l1.rpc.bkpr_listaccountevents()['events'] close = find_first_tag(evs, 'channel_close') delayed_to = find_first_tag(evs, 'delayed_to_us') @@ -59,7 +59,7 @@ def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): assert close_fee[0]['credit_msat'] + delayed_to['credit_msat'] == close['debit_msat'] # l2's fees should equal the trimmed htlc out - evs = l2.rpc.listaccountevents()['events'] + evs = l2.rpc.bkpr_listaccountevents()['events'] close = find_first_tag(evs, 'channel_close') deposit = find_first_tag(evs, 'deposit') fees = find_tags(evs, 'onchain_fee') @@ -89,7 +89,7 @@ def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): bitcoind.generate_block(80) sync_blockheight(bitcoind, [l1, l2]) - evs = l1.rpc.listaccountevents()['events'] + evs = l1.rpc.bkpr_listaccountevents()['events'] # check that closing equals onchain deposits + fees close = find_first_tag(evs, 'channel_close') delayed_to = find_first_tag(evs, 'delayed_to_us') @@ -98,7 +98,7 @@ def test_bookkeeping_closing_subsat_htlcs(node_factory, bitcoind, chainparams): assert len(close_fee) == 1 assert close_fee[0]['credit_msat'] + delayed_to['credit_msat'] == close['debit_msat'] - evs = l2.rpc.listaccountevents()['events'] + evs = l2.rpc.bkpr_listaccountevents()['events'] close = find_first_tag(evs, 'channel_close') deposit = find_first_tag(evs, 'deposit') fees = find_tags(evs, 'onchain_fee') @@ -134,7 +134,7 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): withdrawal = [u for u in unspent if u['txid'] == out['txid']] assert withdrawal[0]['amount'] == Decimal('0.00555555') - incomes = l1.rpc.listincome()['income_events'] + incomes = l1.rpc.bkpr_listincome()['income_events'] # There should only be two income events: deposits to wallet # for {amount} assert len(incomes) == 2 @@ -142,13 +142,13 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): assert inc['account'] == 'wallet' assert inc['tag'] == 'deposit' assert inc['credit_msat'] == amount_msat - # The event should show up in the 'listaccountevents' however - events = l1.rpc.listaccountevents()['events'] + # The event should show up in the 'bkpr_listaccountevents' however + events = l1.rpc.bkpr_listaccountevents()['events'] assert len(events) == 3 external = [e for e in events if e['account'] == 'external'][0] assert external['credit_msat'] == Millisatoshi(amount // 2 * 1000) - btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + btc_balance = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) assert btc_balance['balance_msat'] == amount_msat * 2 # Restart the node, issues a balance snapshot @@ -157,15 +157,15 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): l1.restart() # the number of account + income events should be unchanged - incomes = l1.rpc.listincome()['income_events'] + incomes = l1.rpc.bkpr_listincome()['income_events'] assert len(find_tags(incomes, 'journal_entry')) == 0 assert len(incomes) == 2 - events = l1.rpc.listaccountevents()['events'] + events = l1.rpc.bkpr_listaccountevents()['events'] assert len(events) == 3 assert len(find_tags(events, 'journal_entry')) == 0 # the wallet balance should be unchanged - btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + btc_balance = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) assert btc_balance['balance_msat'] == amount_msat * 2 # ok now we mine a block @@ -174,7 +174,7 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): # expect the withdrawal to appear in the incomes # and there should be an onchain fee - incomes = l1.rpc.listincome()['income_events'] + incomes = l1.rpc.bkpr_listincome()['income_events'] # 2 wallet deposits, 1 wallet withdrawal, 1 onchain_fee assert len(incomes) == 4 withdraw_amt = find_tags(incomes, 'withdrawal')[0]['debit_msat'] @@ -185,7 +185,7 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): fees = fee_events[0]['debit_msat'] # wallet balance is decremented now - btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + btc_balance = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) assert btc_balance['balance_msat'] == amount_msat * 2 - withdraw_amt - fees @@ -214,9 +214,9 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): l1.rpc.withdraw(waddr, amount // 2) # There should only be two income events: deposits to wallet - assert len(l1.rpc.listincome()['income_events']) == 2 + assert len(l1.rpc.bkpr_listincome()['income_events']) == 2 # There are three account events: 2 wallet deposits, 1 external deposit - assert len(l1.rpc.listaccountevents()['events']) == 3 + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 3 # Stop node and remove the accounts data l1.stop() @@ -224,15 +224,15 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): l1.start() # the number of income events should be unchanged - assert len(l1.rpc.listincome()['income_events']) == 2 + assert len(l1.rpc.bkpr_listincome()['income_events']) == 2 # we're now missing the external deposit - events = l1.rpc.listaccountevents()['events'] + events = l1.rpc.bkpr_listaccountevents()['events'] assert len(events) == 2 assert len([e for e in events if e['account'] == 'external']) == 0 assert len(find_tags(events, 'journal_entry')) == 0 # the wallet balance should be unchanged - btc_balance = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + btc_balance = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) assert btc_balance['balance_msat'] == amount_msat * 2 # ok now we mine a block @@ -241,7 +241,7 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): # expect the withdrawal to appear in the incomes # and there should be an onchain fee - incomes = l1.rpc.listincome()['income_events'] + incomes = l1.rpc.bkpr_listincome()['income_events'] # 2 wallet deposits, 1 onchain_fee assert len(incomes) == 3 assert len(find_tags(incomes, 'withdrawal')) == 0 @@ -252,7 +252,7 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): assert fees > Millisatoshi(amount // 2 * 1000) # wallet balance is decremented now - bal = only_one(only_one(l1.rpc.listbalances()['accounts'])['balances']) + bal = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) assert bal['balance_msat'] == amount_msat * 2 - fees @@ -269,8 +269,8 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): bitcoind.rpc.sendtoaddress(addr, amount / 10**8) bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1) - assert len(l1.rpc.listaccountevents()['events']) == 1 - assert len(l1.rpc.listincome()['income_events']) == 1 + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 1 + assert len(l1.rpc.bkpr_listincome()['income_events']) == 1 # Ok, now we send some funds to an external address waddr = l1.bitcoin.rpc.getnewaddress() @@ -280,8 +280,8 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert out1['txid'] in list(mempool.keys()) # another account event, still one income event - assert len(l1.rpc.listaccountevents()['events']) == 2 - assert len(l1.rpc.listincome()['income_events']) == 1 + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 2 + assert len(l1.rpc.bkpr_listincome()['income_events']) == 1 # unreserve the existing output l1.rpc.unreserveinputs(out1['psbt'], 200) @@ -293,14 +293,14 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert out2['txid'] in list(mempool.keys()) # another account event, still one income event - assert len(l1.rpc.listaccountevents()['events']) == 3 - assert len(l1.rpc.listincome()['income_events']) == 1 + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 3 + assert len(l1.rpc.bkpr_listincome()['income_events']) == 1 # ok now we mine a block bitcoind.generate_block(1) sync_blockheight(bitcoind, [l1]) - acct_evs = l1.rpc.listaccountevents()['events'] + acct_evs = l1.rpc.bkpr_listaccountevents()['events'] externs = [e for e in acct_evs if e['account'] == 'external'] assert len(externs) == 2 assert externs[0]['outpoint'][:-2] == out1['txid'] @@ -308,7 +308,7 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert externs[1]['outpoint'][:-2] == out2['txid'] assert externs[1]['blockheight'] > 0 - withdraws = find_tags(l1.rpc.listincome()['income_events'], 'withdrawal') + withdraws = find_tags(l1.rpc.bkpr_listincome()['income_events'], 'withdrawal') assert len(withdraws) == 1 assert withdraws[0]['outpoint'][:-2] == out2['txid'] @@ -318,7 +318,7 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): for fee in fees: assert fee['txid'] == out2['txid'] - fees = find_tags(l1.rpc.listincome(consolidate_fees=False)['income_events'], 'onchain_fee') + fees = find_tags(l1.rpc.bkpr_listincome(consolidate_fees=False)['income_events'], 'onchain_fee') assert len(fees) == 2 - fees = find_tags(l1.rpc.listincome(consolidate_fees=True)['income_events'], 'onchain_fee') + fees = find_tags(l1.rpc.bkpr_listincome(consolidate_fees=True)['income_events'], 'onchain_fee') assert len(fees) == 1 diff --git a/tests/test_closing.py b/tests/test_closing.py index d6b3af838c72..9006cbab12f9 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -984,7 +984,7 @@ def test_channel_lease_unilat_closes(node_factory, bitcoind): assert l3.daemon.is_in_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') # We were making a journal_entry for anchors, but now we ignore them - incomes = l2.rpc.listincome()['income_events'] + incomes = l2.rpc.bkpr_listincome()['income_events'] assert 'journal_entry' not in [x['tag'] for x in incomes] @@ -1760,7 +1760,7 @@ def get_rbf_tx(self, depth, name, resolve): check_utxos_channel(l2, [channel_id], expected_2) # Make sure that l2's account is considered closed (has a fee output) - fees = [e for e in l2.rpc.listincome()['income_events'] if e['tag'] == 'onchain_fee'] + fees = [e for e in l2.rpc.bkpr_listincome()['income_events'] if e['tag'] == 'onchain_fee'] assert len(fees) == 1 diff --git a/tests/test_pay.py b/tests/test_pay.py index 7a5556250244..fd2ca3d1ed18 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1305,17 +1305,17 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): # Do some checks of the bookkeeper's records def _income_tagset(node, tagset): - incomes = node.rpc.listincome()['income_events'] + incomes = node.rpc.bkpr_listincome()['income_events'] return [e for e in incomes if e['tag'] in tagset] tags = ['invoice', 'invoice_fee'] wait_for(lambda: len(_income_tagset(l1, tags)) == 2) incomes = _income_tagset(l1, tags) # the balance on l3 should equal the invoice - bal = only_one(only_one(l3.rpc.listbalances()['accounts'])['balances'])['balance'] + bal = only_one(only_one(l3.rpc.bkpr_listbalances()['accounts'])['balances'])['balance_msat'] assert incomes[0]['tag'] == 'invoice' assert Millisatoshi(bal) == incomes[0]['debit_msat'] - inve = only_one([e for e in l1.rpc.listaccountevents()['events'] if e['tag'] == 'invoice']) + inve = only_one([e for e in l1.rpc.bkpr_listaccountevents()['events'] if e['tag'] == 'invoice']) assert inve['debit_msat'] == incomes[0]['debit_msat'] + incomes[1]['debit_msat'] From 3dcfd2d0e48c0a8704518aebf3f119337f6b1888 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1183/1530] bkpr: account name is required for bkpr-inspect --- plugins/bkpr/bookkeeper.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index be91bc3a8e54..076219b6783d 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -230,14 +230,10 @@ static struct command_result *json_inspect(struct command *cmd, /* Only available for channel accounts? */ if (!param(cmd, buf, params, - p_opt("account", param_string, &acct_name), + p_req("account", param_string, &acct_name), NULL)) return command_param_failed(); - if (!acct_name) - return command_fail(cmd, PLUGIN_ERROR, - "Account not provided"); - if (streq(acct_name, WALLET_ACCT) || streq(acct_name, EXTERNAL_ACCT)) return command_fail(cmd, PLUGIN_ERROR, From cf8b30d2e8116c9697a17f1a5b63d6d04c85f8ec Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1184/1530] bkpr: print more info in bkpr-listbalances --- plugins/bkpr/bookkeeper.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 076219b6783d..e8ea7284e097 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -488,8 +488,14 @@ static struct command_result *json_list_balances(struct command *cmd, json_add_string(res, "account", accts[i]->name); if (accts[i]->peer_id) { json_add_node_id(res, "peer_id", accts[i]->peer_id); + json_add_bool(res, "we_opened", accts[i]->we_opened); + json_add_bool(res, "account_closed", + !(!accts[i]->closed_event_db_id)); json_add_bool(res, "account_resolved", accts[i]->onchain_resolved_block > 0); + if (accts[i]->onchain_resolved_block > 0) + json_add_u32(res, "resolved_at_block", + accts[i]->onchain_resolved_block); } json_array_start(res, "balances"); From d72033882f3237e7e3704128e05dc0080253aaa0 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1185/1530] bkpr: check for channel resolution for any "originated" event We were failing to mark channels as resolved b/c we weren't using later events to external (but originated from this account) events as signals to run the channel resolution check. This fixes that, and adds a test. --- plugins/bkpr/bookkeeper.c | 4 ++- tests/test_closing.py | 25 ++++++++++++++++- tests/utils.py | 58 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index e8ea7284e097..f4edf9dc828f 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -862,7 +862,9 @@ static char *do_account_close_checks(const tal_t *ctx, db_begin_transaction(db); /* If is an external acct event, might be close channel related */ - if (!is_channel_account(acct) && !e->spending_txid) + if (!is_channel_account(acct) && e->origin_acct) { + closed_acct = find_account(ctx, db, e->origin_acct); + } else if (!is_channel_account(acct) && !e->spending_txid) closed_acct = find_close_account(ctx, db, &e->outpoint.txid); else closed_acct = acct; diff --git a/tests/test_closing.py b/tests/test_closing.py index 9006cbab12f9..489930dd2e52 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -7,7 +7,7 @@ account_balance, first_channel_id, closing_fee, TEST_NETWORK, scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES, check_utxos_channel, anchor_expected, check_coin_moves, - check_balance_snaps, mine_funding_to_announce + check_balance_snaps, mine_funding_to_announce, check_inspect_channel ) import os @@ -1514,6 +1514,15 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id) check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id) + # Check that it's marked as resolved + for node in [l2, l3]: + bals = node.rpc.bkpr_listbalances()['accounts'] + for acc in bals: + if acc['account'] == channel_id: + assert acc['account_closed'] + assert acc['account_resolved'] + assert acc['resolved_at_block'] > 0 + @pytest.mark.developer("uses dev_sign_last_tx") def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams): @@ -2422,6 +2431,20 @@ def try_pay(): tags = check_utxos_channel(l1, [channel_id], expected_1) check_utxos_channel(l2, [channel_id], expected_2, tags) + # Check 'bkpr-inspect' and 'bkpr-listbalances' + # The wallet events aren't in the channel's events + del expected_1['0'] + expected_1['A'] = expected_1['A'][1:] + check_inspect_channel(l1, channel_id, expected_1) + + for node in [l1, l2]: + bals = node.rpc.bkpr_listbalances()['accounts'] + for acc in bals: + if acc['account'] == channel_id: + assert acc['account_closed'] + assert acc['account_resolved'] + assert acc['resolved_at_block'] > 0 + def test_listfunds_after_their_unilateral(node_factory, bitcoind): """We keep spending info around for their unilateral closes. diff --git a/tests/utils.py b/tests/utils.py index ef1a18b152fa..ce14fcbf1255 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -315,6 +315,64 @@ def dedupe_moves(moves): return deduped_moves +def inspect_check_actual(txids, channel_id, actual, exp): + assert len(actual['outputs']) == len(exp) + for e in exp: + # find the event in actual that matches + found = False + for a in actual['outputs']: + if e[0].startswith('cid'): + if a['account'] != channel_id: + continue + elif a['account'] != e[0]: + continue + + if e[1][0] != a['output_tag']: + continue + if e[2]: + assert e[2][0] == a['spend_tag'] + txids.append((e[3], a['spending_txid'])) + else: + assert 'spend_tag' not in a + found = True + break + assert found + + return txids + + +def check_inspect_channel(n, channel_id, expected_txs): + actual_txs = n.rpc.bkpr_inspect(channel_id)['txs'] + assert len(actual_txs) == len(expected_txs.keys()) + # start at the top + exp = list(expected_txs.values())[0] + actual = actual_txs[0] + + txids = [] + + exp_counter = 1 + inspect_check_actual(txids, channel_id, actual, exp) + actual_txs.remove(actual) + + for (marker, txid) in txids: + actual = None + for a in actual_txs: + if a['txid'] == txid: + actual = a + break + assert actual + exp = expected_txs[marker] + inspect_check_actual(txids, channel_id, actual, exp) + + # after we've inspected it, remove it + actual_txs.remove(actual) + exp_counter += 1 + + # Did we inspect everything? + assert len(actual_txs) == 0 + assert exp_counter == len(expected_txs.keys()) + + def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=None): tag_list = {} moves = n.rpc.call('listcoinmoves_plugin')['coin_moves'] From 0c2b43b6b2a5a27785108bf7519de53b6306261e Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1186/1530] bkpr: add more data to listaccountevents printout --- plugins/bkpr/channel_event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index ca71f9f672a2..f127c6b7c853 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -42,8 +42,10 @@ void json_add_channel_event(struct json_stream *out, if (!amount_msat_zero(ev->fees)) json_add_amount_msat_only(out, "fees_msat", ev->fees); json_add_string(out, "currency", ev->currency); - if (ev->payment_id) + if (ev->payment_id) { json_add_sha256(out, "payment_id", ev->payment_id); + json_add_u32(out, "part_id", ev->part_id); + } json_add_u64(out, "timestamp", ev->timestamp); json_object_end(out); } From c1cef773ca500484c0242ee317e1d81c6c3a4e74 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1187/1530] bkpr: make sure there's always at least on difference in blockheights We can't divide by zero, so make sure it's always 1 (with a small loss of precision for other cases, ce la vie) --- plugins/bkpr/channelsapy.c | 4 ++-- tests/test_pay.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/channelsapy.c b/plugins/bkpr/channelsapy.c index bbf17c121084..aba855667488 100644 --- a/plugins/bkpr/channelsapy.c +++ b/plugins/bkpr/channelsapy.c @@ -342,8 +342,8 @@ void json_add_channel_apy(struct json_stream *res, tal_fmt(apy, "%.4f%%", utilization * 100)); } - blocks_elapsed = apy->end_blockheight - apy->start_blockheight; - assert(blocks_elapsed > 0); + /* Can't divide by zero */ + blocks_elapsed = apy->end_blockheight - apy->start_blockheight + 1; /* APY (outbound) */ ok = calc_apy(apy->fees_out, apy->total_start_bal, diff --git a/tests/test_pay.py b/tests/test_pay.py index fd2ca3d1ed18..64d170447d70 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -70,6 +70,16 @@ def test_pay(node_factory): payments = l1.rpc.listsendpays(inv)['payments'] assert len(payments) == 1 and payments[0]['payment_preimage'] == preimage + # Check channels apy summary view of channel activity + apys_1 = l1.rpc.bkpr_channelsapy()['channels_apy'] + apys_2 = l2.rpc.bkpr_channelsapy()['channels_apy'] + + assert apys_1[0]['channel_start_balance_msat'] == apys_2[0]['channel_start_balance_msat'] + assert apys_1[0]['channel_start_balance_msat'] == apys_1[0]['our_start_balance_msat'] + assert apys_2[0]['our_start_balance_msat'] == Millisatoshi(0) + assert apys_1[0]['routed_out_msat'] == apys_2[0]['routed_in_msat'] + assert apys_1[0]['routed_in_msat'] == apys_2[0]['routed_out_msat'] + @pytest.mark.developer("needs to deactivate shadow routing") def test_pay_amounts(node_factory): From 0617690981e3ff8c49ddaff9c6a581bbe90fcff5 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1188/1530] coin_mvt/bkpr: add "stealable" tag to stealable outputs If we expect further events for an onchain output (because we can steal it away from the 'external'/rightful owner), we mark them. This prevents us from marking a channel as 'onchain-resolved' before all events that we're interested in have actually hit the chain. Case that this matters: Peer publishes a (cheating) unilateral close and a timeout htlc (which we can steal). We then steal the timeout htlc. W/o the stealable flag, we'd have marked the channel as resolved when the peer published the timeout htlc, which is incorrect as we're still waiting for the resolution of that timeout htlc (b/c we *can* steal it). --- common/coin_mvt.c | 35 +++++++++++-- common/coin_mvt.h | 18 ++++++- common/test/run-bolt12_merkle-json.c | 6 +-- lightningd/onchain_control.c | 3 +- onchaind/onchaind.c | 75 +++++++++++++++++++++------ onchaind/test/run-grind_feerate-bug.c | 17 ++++++ onchaind/test/run-grind_feerate.c | 17 ++++++ plugins/bkpr/bookkeeper.c | 7 ++- plugins/bkpr/chain_event.h | 4 ++ plugins/bkpr/db.c | 1 + plugins/bkpr/recorder.c | 19 +++++-- plugins/bkpr/test/run-recorder.c | 5 ++ tests/test_closing.py | 4 +- 13 files changed, 177 insertions(+), 34 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index 4893d0e55aad..bb2caa14fff4 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -39,6 +39,7 @@ static const char *mvt_tags[] = { "opener", "lease_fee", "leased", + "stealable", }; const char *mvt_tag_str(enum mvt_tag tag) @@ -88,7 +89,7 @@ static struct chain_coin_mvt *new_chain_coin_mvt(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, const struct sha256 *payment_hash TAKES, u32 blockheight, - enum mvt_tag *tags TAKES, + enum mvt_tag *tags, struct amount_msat amount, bool is_credit, struct amount_sat output_val, @@ -252,6 +253,19 @@ struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx, amount, true); } +struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *txid, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag *tags TAKES) +{ + return new_chain_coin_mvt(ctx, EXTERNAL, txid, + outpoint, NULL, blockheight, + take(tags), + AMOUNT_MSAT(0), true, amount, 0); +} + struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, const struct bitcoin_txid *txid, @@ -259,12 +273,23 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, struct amount_sat amount, enum mvt_tag tag) { - return new_chain_coin_mvt(ctx, EXTERNAL, txid, - outpoint, NULL, blockheight, - take(new_tag_arr(NULL, tag)), - AMOUNT_MSAT(0), true, amount, 0); + return new_coin_external_spend_tags(ctx, outpoint, + txid, blockheight, amount, + new_tag_arr(NULL, tag)); } +struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag *tags TAKES) +{ + return new_chain_coin_mvt_sat(ctx, EXTERNAL, NULL, outpoint, NULL, + blockheight, take(tags), + amount, true); +} + + struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index aaecb3f86be6..1f75641cb6bf 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -14,7 +14,7 @@ enum mvt_type { CHANNEL_MVT = 1, }; -#define NUM_MVT_TAGS (LEASED + 1) +#define NUM_MVT_TAGS (STEALABLE + 1) enum mvt_tag { DEPOSIT = 0, WITHDRAWAL = 1, @@ -38,6 +38,7 @@ enum mvt_tag { OPENER = 19, LEASE_FEE = 20, LEASED = 21, + STEALABLE = 22, }; struct channel_coin_mvt { @@ -234,6 +235,14 @@ struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx, enum mvt_tag tag) NON_NULL_ARGS(2, 3); +struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + const struct bitcoin_txid *txid, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag *tags) + NON_NULL_ARGS(2, 3); + struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, const struct bitcoin_txid *txid, @@ -242,6 +251,13 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx, enum mvt_tag tag) NON_NULL_ARGS(2, 3); +struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx, + const struct bitcoin_outpoint *outpoint, + u32 blockheight, + struct amount_sat amount, + enum mvt_tag *tags) + NON_NULL_ARGS(2, 5); + struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx, const struct bitcoin_outpoint *outpoint, u32 blockheight, diff --git a/common/test/run-bolt12_merkle-json.c b/common/test/run-bolt12_merkle-json.c index 495796da496b..5e67f33e40ac 100644 --- a/common/test/run-bolt12_merkle-json.c +++ b/common/test/run-bolt12_merkle-json.c @@ -47,12 +47,10 @@ const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_strdup called!\n"); abort(); } /* Generated stub for json_to_u32 */ -bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - uint32_t *num UNNEEDED) +bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32 *num UNNEEDED) { fprintf(stderr, "json_to_u32 called!\n"); abort(); } /* Generated stub for json_to_u64 */ -bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - uint64_t *num UNNEEDED) +bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) { fprintf(stderr, "json_to_u64 called!\n"); abort(); } /* Generated stub for json_tok_full */ const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 02a7281572c5..322487cd3812 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -640,7 +641,7 @@ enum watch_result onchaind_funding_spent(struct channel *channel, channel->state, FUNDING_SPEND_SEEN, reason, - "Onchain funding spend"); + tal_fmt(tmpctx, "Onchain funding spend")); hsmfd = hsm_get_client_fd(ld, &channel->peer->id, channel->dbid, diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 4cd20023f8fc..4a15b841be05 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -235,6 +235,16 @@ static void record_external_spend(const struct bitcoin_txid *txid, out->sat, tag))); } +static void record_external_spend_tags(const struct bitcoin_txid *txid, + struct tracked_output *out, + u32 blockheight, + enum mvt_tag *tags TAKES) +{ + send_coin_mvt(take(new_coin_external_spend_tags(NULL, &out->outpoint, + txid, blockheight, + out->sat, tags))); +} + static void record_external_output(const struct bitcoin_outpoint *out, struct amount_sat amount, u32 blockheight, @@ -251,6 +261,15 @@ static void record_external_deposit(const struct tracked_output *out, record_external_output(&out->outpoint, out->sat, blockheight, tag); } +static void record_external_deposit_tags(const struct tracked_output *out, + u32 blockheight, + enum mvt_tag *tags TAKES) +{ + send_coin_mvt(take(new_coin_external_deposit_tags(NULL, &out->outpoint, + blockheight, out->sat, + tags))); +} + static void record_mutual_close(const struct tx_parts *tx, const u8 *remote_scriptpubkey, u32 blockheight) @@ -358,32 +377,36 @@ static void record_coin_movements(struct tracked_output *out, * AND so we can accurately calculate our on-chain fee burden */ if (out->tx_type == OUR_HTLC_TIMEOUT_TX || out->tx_type == OUR_HTLC_SUCCESS_TX) - record_channel_deposit(out, blockheight, HTLC_TX); + record_channel_deposit(out, out->tx_blockheight, HTLC_TX); if (out->resolved->tx_type == OUR_HTLC_TIMEOUT_TO_US) - record_channel_deposit(out, blockheight, HTLC_TIMEOUT); + record_channel_deposit(out, out->tx_blockheight, HTLC_TIMEOUT); /* there is a case where we've fulfilled an htlc onchain, * in which case we log a deposit to the channel */ if (out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US || out->resolved->tx_type == OUR_HTLC_SUCCESS_TX) - record_to_us_htlc_fulfilled(out, blockheight); + record_to_us_htlc_fulfilled(out, out->tx_blockheight); /* If it's our to-us and our close, we publish *another* tx * which spends the output when the timeout ends */ if (out->tx_type == OUR_UNILATERAL) { if (out->output_type == DELAYED_OUTPUT_TO_US) - record_channel_deposit(out, blockheight, CHANNEL_TO_US); + record_channel_deposit(out, out->tx_blockheight, + CHANNEL_TO_US); else if (out->output_type == OUR_HTLC) { - record_channel_deposit(out, blockheight, HTLC_TIMEOUT); - record_channel_withdrawal(txid, out, blockheight, HTLC_TIMEOUT); + record_channel_deposit(out, out->tx_blockheight, + HTLC_TIMEOUT); + record_channel_withdrawal(txid, out, blockheight, + HTLC_TIMEOUT); } else if (out->output_type == THEIR_HTLC) - record_channel_withdrawal(txid, out, blockheight, HTLC_FULFILL); + record_channel_withdrawal(txid, out, blockheight, + HTLC_FULFILL); } if (out->tx_type == THEIR_REVOKED_UNILATERAL || out->resolved->tx_type == OUR_PENALTY_TX) - record_channel_deposit(out, blockheight, PENALTY); + record_channel_deposit(out, out->tx_blockheight, PENALTY); if (out->resolved->tx_type == OUR_DELAYED_RETURN_TO_WALLET || out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US @@ -1726,15 +1749,25 @@ static void output_spent(struct tracked_output ***outs, case OUTPUT_TO_US: case DELAYED_OUTPUT_TO_US: unknown_spend(out, tx_parts); - record_external_deposit(out, tx_blockheight, PENALIZED); + record_external_deposit(out, out->tx_blockheight, + PENALIZED); break; case THEIR_HTLC: if (out->tx_type == THEIR_REVOKED_UNILATERAL) { - record_external_deposit(out, out->tx_blockheight, - HTLC_TIMEOUT); - record_external_spend(&tx_parts->txid, out, - tx_blockheight, HTLC_TIMEOUT); + enum mvt_tag *tags; + tags = new_tag_arr(NULL, HTLC_TIMEOUT); + tal_arr_expand(&tags, STEALABLE); + + record_external_deposit_tags(out, out->tx_blockheight, + /* This takes tags */ + tal_dup_talarr(NULL, + enum mvt_tag, + tags)); + record_external_spend_tags(&tx_parts->txid, + out, + tx_blockheight, + tags); /* we've actually got a 'new' output here */ steal_htlc_tx(out, outs, tx_parts, @@ -1768,16 +1801,24 @@ static void output_spent(struct tracked_output ***outs, handle_htlc_onchain_fulfill(out, tx_parts, &htlc_outpoint); - record_to_them_htlc_fulfilled(out, tx_blockheight); - record_external_spend(&tx_parts->txid, out, - tx_blockheight, HTLC_FULFILL); + record_to_them_htlc_fulfilled(out, out->tx_blockheight); if (out->tx_type == THEIR_REVOKED_UNILATERAL) { + enum mvt_tag *tags = new_tag_arr(NULL, + HTLC_FULFILL); + tal_arr_expand(&tags, STEALABLE); + record_external_spend_tags(&tx_parts->txid, + out, + tx_blockheight, + tags); steal_htlc_tx(out, outs, tx_parts, tx_blockheight, OUR_HTLC_FULFILL_TO_THEM, &htlc_outpoint); } else { + record_external_spend(&tx_parts->txid, out, + tx_blockheight, + HTLC_FULFILL); /* BOLT #5: * * ## HTLC Output Handling: Local Commitment, @@ -1806,7 +1847,7 @@ static void output_spent(struct tracked_output ***outs, resolved_by_other(out, &tx_parts->txid, THEIR_DELAYED_CHEAT); - record_external_deposit(out, tx_blockheight, STOLEN); + record_external_deposit(out, out->tx_blockheight, STOLEN); break; /* Um, we don't track these! */ case OUTPUT_TO_THEM: diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 7d50d53f5698..b7a1a215caf9 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -131,6 +131,14 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); } +/* Generated stub for new_coin_external_deposit_tags */ +struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag *tags) + +{ fprintf(stderr, "new_coin_external_deposit_tags called!\n"); abort(); } /* Generated stub for new_coin_external_spend */ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, @@ -140,6 +148,15 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } +/* Generated stub for new_coin_external_spend_tags */ +struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag *tags) + +{ fprintf(stderr, "new_coin_external_spend_tags called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit_tagged */ struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index bfe625f315b9..e88f79bd5f3f 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -154,6 +154,14 @@ struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); } +/* Generated stub for new_coin_external_deposit_tags */ +struct chain_coin_mvt *new_coin_external_deposit_tags(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag *tags) + +{ fprintf(stderr, "new_coin_external_deposit_tags called!\n"); abort(); } /* Generated stub for new_coin_external_spend */ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, @@ -163,6 +171,15 @@ struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED, enum mvt_tag tag) { fprintf(stderr, "new_coin_external_spend called!\n"); abort(); } +/* Generated stub for new_coin_external_spend_tags */ +struct chain_coin_mvt *new_coin_external_spend_tags(const tal_t *ctx UNNEEDED, + const struct bitcoin_outpoint *outpoint UNNEEDED, + const struct bitcoin_txid *txid UNNEEDED, + u32 blockheight UNNEEDED, + struct amount_sat amount UNNEEDED, + enum mvt_tag *tags) + +{ fprintf(stderr, "new_coin_external_spend_tags called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit_tagged */ struct chain_coin_mvt *new_coin_wallet_deposit_tagged(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index f4edf9dc828f..51550eeef67b 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -618,6 +618,7 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev->spending_txid = NULL; chain_ev->payment_id = NULL; chain_ev->ignored = false; + chain_ev->stealable = false; /* Update the account info too */ tags = tal_arr(chain_ev, enum mvt_tag, 1); @@ -1234,8 +1235,12 @@ parse_and_log_chain_move(struct command *cmd, e->tag = mvt_tag_str(tags[0]); e->ignored = false; - for (size_t i = 0; i < tal_count(tags); i++) + e->stealable = false; + for (size_t i = 0; i < tal_count(tags); i++) { e->ignored |= tags[i] == IGNORED; + e->stealable |= tags[i] == STEALABLE; + } + db_begin_transaction(db); acct = find_account(cmd, db, acct_name); diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index b9f7b444ba23..0a2771c515c8 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -30,6 +30,10 @@ struct chain_event { /* Is the node's wallet ignoring this? */ bool ignored; + /* Is this chain output stealable? If so + * we'll need to watch it for longer */ + bool stealable; + /* Amount we received in this event */ struct amount_msat credit; diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 1708691bd310..75795c8f23cb 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -95,6 +95,7 @@ static struct migration db_migrations[] = { {SQL("ALTER TABLE chain_events ADD origin TEXT;"), NULL}, {SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL}, {SQL("ALTER TABLE chain_events ADD ignored INTEGER;"), NULL}, + {SQL("ALTER TABLE chain_events ADD stealable INTEGER;"), NULL}, }; static bool db_migrate(struct plugin *p, struct db *db) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 7e6a241ad059..43f2ccc3a4db 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -56,6 +56,7 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st e->spending_txid = NULL; e->ignored = db_col_int(stmt, "e.ignored") == 1; + e->stealable = db_col_int(stmt, "e.stealable") == 1; return e; } @@ -128,6 +129,7 @@ struct chain_event **list_chain_events_timebox(const tal_t *ctx, ", e.spending_txid" ", e.payment_id" ", e.ignored" + ", e.stealable" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -168,6 +170,7 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, ", e.spending_txid" ", e.payment_id" ", e.ignored" + ", e.stealable" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -201,6 +204,7 @@ static struct chain_event **find_txos_for_tx(const tal_t *ctx, ", e.spending_txid" ", e.payment_id" ", e.ignored" + ", e.stealable" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -473,10 +477,12 @@ bool find_txo_chain(const tal_t *ctx, && !streq(pr->spend->tag, "to_miner") && !txid_in_list(txids, pr->spend->spending_txid) /* We dont trace utxos for non related accts */ - && pr->spend->acct_db_id == acct->db_id) { + && (pr->spend->acct_db_id == acct->db_id + /* Unless it's stealable, in which case + * we track the resolution of the htlc tx */ + || pr->spend->stealable)) tal_arr_expand(&txids, pr->spend->spending_txid); - } } if (sets) @@ -604,6 +610,7 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, ", e.spending_txid" ", e.payment_id" ", e.ignored" + ", e.stealable" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -649,6 +656,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.spending_txid" ", e.payment_id" ", e.ignored" + ", e.stealable" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -676,6 +684,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.spending_txid" ", e.payment_id" ", e.ignored" + ", e.stealable" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -1203,6 +1212,7 @@ void maybe_update_account(struct db *db, case STOLEN: case TO_MINER: case LEASE_FEE: + case STEALABLE: /* Ignored */ break; } @@ -1319,6 +1329,7 @@ static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db ", e.spending_txid" ", e.payment_id" ", e.ignored" + ", e.stealable" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -1808,9 +1819,10 @@ bool log_chain_event(struct db *db, ", payment_id" ", spending_txid" ", ignored" + ", stealable" ")" " VALUES " - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); if (e->origin_acct) @@ -1838,6 +1850,7 @@ bool log_chain_event(struct db *db, db_bind_null(stmt, 12); db_bind_int(stmt, 13, e->ignored ? 1 : 0); + db_bind_int(stmt, 14, e->stealable ? 1 : 0); db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); e->acct_db_id = acct->db_id; diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 3c5fbffb2eb0..6af1ea6a6c55 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -363,6 +363,7 @@ static struct chain_event *make_chain_event(const tal_t *ctx, ev->timestamp = 1919191; ev->blockheight = blockheight; ev->ignored = false; + ev->stealable = false; memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid)); ev->outpoint.n = outnum; @@ -965,6 +966,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev1->timestamp = 1919191; ev1->blockheight = 1919191; ev1->ignored = false; + ev1->stealable = false; memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev1->outpoint.n = 1; ev1->spending_txid = tal(ctx, struct bitcoin_txid); @@ -985,6 +987,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev2->timestamp = 1919191; ev2->blockheight = 1919191; ev2->ignored = false; + ev2->stealable = false; memset(&ev2->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev2->outpoint.n = 1; ev2->spending_txid = NULL; @@ -1002,6 +1005,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev3->timestamp = 3939393; ev3->blockheight = 3939393; ev3->ignored = false; + ev3->stealable = false; memset(&ev3->outpoint.txid, 'E', sizeof(struct bitcoin_txid)); ev3->outpoint.n = 1; ev3->spending_txid = tal(ctx, struct bitcoin_txid); @@ -1228,6 +1232,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) ev1->timestamp = 1919191; ev1->blockheight = 1919191; ev1->ignored = false; + ev1->stealable = false; memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid)); ev1->outpoint.n = 1; ev1->spending_txid = tal(ctx, struct bitcoin_txid); diff --git a/tests/test_closing.py b/tests/test_closing.py index 489930dd2e52..be7f82f403ae 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1279,7 +1279,7 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): expected_3 = { 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], - 'B': [('wallet', ['deposit'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'E')], + 'B': [('wallet', ['deposit'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill', 'stealable'], 'C'), ('cid1', ['penalty'], ['to_wallet'], 'E')], 'C': [('cid1', ['penalty'], ['to_wallet'], 'D')], 'D': [('wallet', ['deposit'], None, None)], 'E': [('wallet', ['deposit'], None, None)] @@ -1499,7 +1499,7 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): expected_3 = { 'A': [('cid1', ['channel_open'], ['channel_close'], 'B')], - 'B': [('wallet', ['deposit'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill'], 'E'), ('external', ['stolen'], None, None), ('external', ['htlc_timeout'], ['htlc_timeout'], 'C')], + 'B': [('wallet', ['deposit'], None, None), ('external', ['htlc_fulfill'], ['htlc_fulfill', 'stealable'], 'E'), ('external', ['stolen'], None, None), ('external', ['htlc_timeout', 'stealable'], ['htlc_timeout', 'stealable'], 'C')], 'C': [('cid1', ['penalty'], ['to_wallet'], 'D')], 'D': [('wallet', ['deposit'], None, None)], 'E': [('external', ['stolen'], None, None)] From 97204d6e0ad91b33506df2cd352678756b282961 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:39 +0930 Subject: [PATCH 1189/1530] bkpr: duplicate the name, dont steal it --- plugins/bkpr/account.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/account.c b/plugins/bkpr/account.c index 61739462cfb0..f36fcb3300f6 100644 --- a/plugins/bkpr/account.c +++ b/plugins/bkpr/account.c @@ -1,17 +1,18 @@ #include "config.h" #include +#include #include #include #include struct account *new_account(const tal_t *ctx, - const char *name STEALS, + const char *name, struct node_id *peer_id) { struct account *a = tal(ctx, struct account); - a->name = tal_steal(a, name); + a->name = tal_strdup(a, name); a->peer_id = peer_id; a->is_wallet = streq(a->name, WALLET); a->we_opened = false; From 9346158290d715d3471b510bc1c99b325b0d4ecc Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1190/1530] bkpr: new method, "is_external_account" Utility method to figure out if an account is "external" --- plugins/bkpr/account.c | 5 +++++ plugins/bkpr/account.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/plugins/bkpr/account.c b/plugins/bkpr/account.c index f36fcb3300f6..ba47a8504851 100644 --- a/plugins/bkpr/account.c +++ b/plugins/bkpr/account.c @@ -30,3 +30,8 @@ bool is_channel_account(const struct account *acct) return !streq(acct->name, WALLET) && !streq(acct->name, "external"); } + +bool is_external_account(const struct account *acct) +{ + return streq(acct->name, "external"); +} diff --git a/plugins/bkpr/account.h b/plugins/bkpr/account.h index afce6e5f1f3b..7cbc79a3ad4a 100644 --- a/plugins/bkpr/account.h +++ b/plugins/bkpr/account.h @@ -46,4 +46,6 @@ struct account *new_account(const tal_t *ctx, /* Is this a channel account? */ bool is_channel_account(const struct account *acct); +/* is this the 'external' account */ +bool is_external_account(const struct account *acct); #endif /* LIGHTNING_PLUGINS_BKPR_ACCOUNT_H */ From 38eb95ffee50fc1596e263e1ed08b7c1a3204203 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1191/1530] bkpr: more logging --- plugins/bkpr/bookkeeper.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 51550eeef67b..1209c1d1fbdc 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -604,6 +604,10 @@ static bool new_missed_channel_account(struct command *cmd, if (!streq(chan_id, acct->name)) continue; + plugin_log(cmd->plugin, LOG_DBG, + "Logging channel account from list %s", + acct->name); + chain_ev = tal(cmd, struct chain_event); chain_ev->tag = mvt_tag_str(CHANNEL_OPEN); chain_ev->debit = AMOUNT_MSAT(0); @@ -845,12 +849,14 @@ listpeers_multi_done(struct command *cmd, if (err) plugin_err(cmd->plugin, err); + plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); log_journal_entry(info->acct, info->currency, info->timestamp - 1, credit_diff, debit_diff); } + plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); return notification_handled(cmd); } @@ -1127,6 +1133,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, return command_still_pending(cmd); } + plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); return notification_handled(cmd); } From 67ce0ee3b2950445f85f6f19fcf95bfd6c8fa766 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1192/1530] bkpr: prevent crash when updating same account all at once If two events for the same (unlogged) account come in and get run through the "lookup peer" code, we should anticipate that. We do two things here: - one, if it's a duplicate "event" to create the channel open we check for that and just exit early - two, we were using a copy of the account that was fetched/pulled from before the RPC ran, so instead we just re-pull the most up to date account info for the close checks. This fixes the crash I was getting in re-running these things --- plugins/bkpr/bookkeeper.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 1209c1d1fbdc..ec1ba8d01c24 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -648,7 +648,9 @@ static bool new_missed_channel_account(struct command *cmd, assert(ok); chain_ev->credit = amt; db_begin_transaction(db); - log_chain_event(db, acct, chain_ev); + if (!log_chain_event(db, acct, chain_ev)) + goto done; + maybe_update_account(db, acct, chain_ev, tags, 0, &peer_id); maybe_update_onchain_fees(cmd, db, &opt.txid); @@ -679,6 +681,7 @@ static bool new_missed_channel_account(struct command *cmd, log_channel_event(db, acct, chan_ev); } +done: db_commit_transaction(db); return true; } @@ -874,15 +877,19 @@ static char *do_account_close_checks(const tal_t *ctx, } else if (!is_channel_account(acct) && !e->spending_txid) closed_acct = find_close_account(ctx, db, &e->outpoint.txid); else - closed_acct = acct; + /* Get most up to date account entry */ + closed_acct = find_account(ctx, db, acct->name); + if (closed_acct && closed_acct->closed_event_db_id) { maybe_mark_account_onchain(db, closed_acct); if (closed_acct->onchain_resolved_block > 0) { char *err; err = update_channel_onchain_fees(ctx, db, closed_acct); - if (err) + if (err) { + db_commit_transaction(db); return err; + } } } From 55e15c5f10ceef410e247450c29c3ae7c6230874 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1193/1530] bkpr: correctly pass in command for `jsonrpc_request_start` We weren't passing the command, which meant that it wasn't getting populated correctly. We do that now, it fixes some crashes --- plugins/bkpr/bookkeeper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index ec1ba8d01c24..301289274a9e 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1131,7 +1131,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, if (tal_count(new_accts) > 0) { struct out_req *req; - req = jsonrpc_request_start(cmd->plugin, NULL, + req = jsonrpc_request_start(cmd->plugin, cmd, "listpeers", listpeers_multi_done, log_error, @@ -1319,7 +1319,7 @@ parse_and_log_chain_move(struct command *cmd, info = tal(NULL, struct event_info); info->ev = tal_steal(info, e); info->acct = tal_steal(info, acct); - req = jsonrpc_request_start(cmd->plugin, NULL, + req = jsonrpc_request_start(cmd->plugin, cmd, "listpeers", listpeers_done, log_error, From 2d22bab87922cce431e1c0e7fb4e81fc8b913b18 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1194/1530] bkpr: fetch originating account, if exists, and make sure is populated It's possible we'll get an "external" event for an account/channel and then try to do close checks on it and it'll bomb out because we didn't go look up the originating account to make sure that that had open info Now, we make sure and do that when we hear about an originating account --- plugins/bkpr/bookkeeper.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 301289274a9e..cd20bdf4d164 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1159,7 +1159,7 @@ parse_and_log_chain_move(struct command *cmd, struct sha256 *payment_hash = tal(cmd, struct sha256); struct bitcoin_txid *spending_txid = tal(cmd, struct bitcoin_txid); struct node_id *peer_id; - struct account *acct; + struct account *acct, *orig_acct; u32 closed_count; const char *err; @@ -1265,6 +1265,18 @@ parse_and_log_chain_move(struct command *cmd, account_add(db, acct); } + if (e->origin_acct) { + orig_acct = find_account(cmd, db, e->origin_acct); + /* Go fetch the originating account + * (we might not have it) */ + if (!orig_acct) { + orig_acct = new_account(cmd, e->origin_acct, NULL); + account_add(db, orig_acct); + } + } else + orig_acct = NULL; + + if (!log_chain_event(db, acct, e)) { db_commit_transaction(db); /* This is not a new event, do nothing */ @@ -1301,24 +1313,27 @@ parse_and_log_chain_move(struct command *cmd, db_commit_transaction(db); } - /* If this is an account close event, it's possible + /* If this is a channel account event, it's possible * that we *never* got the open event. (This happens * if you add the plugin *after* you've closed the channel) */ - if (!acct->open_event_db_id - && acct->closed_event_db_id - && *acct->closed_event_db_id == e->db_id) { + if ((!acct->open_event_db_id && is_channel_account(acct)) + || (orig_acct && is_channel_account(orig_acct) + && !orig_acct->open_event_db_id)) { /* Find the channel open info for this peer */ struct out_req *req; struct event_info *info; plugin_log(cmd->plugin, LOG_DBG, - "`channel_close` but no open for channel %s." + "channel event received but no open for channel %s." " Calling `listpeers` to fetch missing info", acct->name); info = tal(NULL, struct event_info); info->ev = tal_steal(info, e); - info->acct = tal_steal(info, acct); + info->acct = tal_steal(info, + is_channel_account(acct) ? + acct : orig_acct); + req = jsonrpc_request_start(cmd->plugin, cmd, "listpeers", listpeers_done, From ab94c557c7967f9843b74e2c2e35233c58acdf49 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1195/1530] bkpr: add test for bookkeeper being added after channel has closed We rescan and pick up the channel's close tx, but can't go back and get the open info, since we've already deleted the channel from the database. --- tests/test_bookkeeper.py | 56 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 747fec5649f1..b082504e517a 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -1,6 +1,6 @@ from fixtures import * # noqa: F401,F403 from decimal import Decimal -from pyln.client import Millisatoshi +from pyln.client import Millisatoshi, RpcError from fixtures import TEST_NETWORK from utils import ( sync_blockheight, wait_for, only_one @@ -322,3 +322,57 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert len(fees) == 2 fees = find_tags(l1.rpc.bkpr_listincome(consolidate_fees=True)['income_events'], 'onchain_fee') assert len(fees) == 1 + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") +def test_bookkeeping_onchaind_txs(node_factory, bitcoind): + """ + Test for a channel that's closed, but whose close tx + re-appears in the rescan + """ + l1, l2 = node_factory.line_graph(2, + wait_for_announce=True, + opts={'disable-plugin': 'bookkeeper'}) + + # Double check there's no bookkeeper plugin on + assert l1.daemon.opts['disable-plugin'] == 'bookkeeper' + with pytest.raises(RpcError): + l1.rpc.listincome() + + # Send l2 funds via the channel + l1.pay(l2, 11000000) + bitcoind.generate_block(10) + + # Amicably close the channel, mine 101 blocks (channel forgotten) + l1.rpc.close(l2.info['id']) + l1.wait_for_channel_onchain(l2.info['id']) + + bitcoind.generate_block(101) + sync_blockheight(bitcoind, [l1]) + + l1.daemon.wait_for_log('onchaind complete, forgetting peer') + + # Now turn the bookkeeper on and restart + l1.stop() + del l1.daemon.opts['disable-plugin'] + # Roll back -- close is picked up for a forgotten channel + l1.daemon.opts['rescan'] = 102 + l1.start() + + # Wait for the balance snapshot to fire/finish + l1.daemon.wait_for_log('Snapshot balances updated') + + # We should have the deposit and then the journal entry + events = l1.rpc.bkpr_listaccountevents()['events'] + assert len(events) == 2 + assert events[0]['account'] == 'wallet' + assert events[0]['tag'] == 'deposit' + assert events[1]['account'] == 'wallet' + assert events[1]['tag'] == 'journal_entry' + + wallet_bal = only_one(l1.rpc.bkpr_listbalances()['accounts']) + assert wallet_bal['account'] == 'wallet' + funds = l1.rpc.listfunds() + assert len(funds['channels']) == 0 + outs = sum([out['amount_msat'] for out in funds['outputs']]) + assert outs == only_one(wallet_bal['balances'])['balance_msat'] From 5c45939acf6ec48864dd13352b6ae3a450edc3f4 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1196/1530] test-utils: add the bolt11 invoice nice to have for making tests that assert on description/bolt data --- contrib/pyln-testing/pyln/testing/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 5ca70e01af6e..dfff1eb74581 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1110,7 +1110,7 @@ def pay(self, dst, amt, label=None): } # sendpay is async now - self.rpc.sendpay([routestep], rhash, payment_secret=psecret) + self.rpc.sendpay([routestep], rhash, payment_secret=psecret, bolt11=inv['bolt11']) # wait for sendpay to comply result = self.rpc.waitsendpay(rhash) assert(result.get('status') == 'complete') From 352b419755b82c48b7596e3f22ee88dd5c98074e Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1197/1530] bkpr: save invoice description data to the database and display it It'll be really nice to be able to read description data about an invoice, if we've got it! --- doc/lightning-bkpr-listaccountevents.7.md | 3 +- doc/lightning-bkpr-listincome.7.md | 3 +- .../bkpr-listaccountevents.schema.json | 6 + doc/schemas/bkpr-listincome.schema.json | 4 + plugins/bkpr/Makefile | 2 +- plugins/bkpr/bookkeeper.c | 351 ++++++++++++++---- plugins/bkpr/chain_event.c | 2 + plugins/bkpr/chain_event.h | 3 + plugins/bkpr/channel_event.c | 3 + plugins/bkpr/channel_event.h | 3 + plugins/bkpr/db.c | 2 + plugins/bkpr/incomestmt.c | 40 +- plugins/bkpr/incomestmt.h | 1 + plugins/bkpr/recorder.c | 58 ++- plugins/bkpr/recorder.h | 8 + plugins/bkpr/test/run-recorder.c | 17 + tests/test_bookkeeper.py | 42 +++ tests/test_invoices.py | 5 + 18 files changed, 459 insertions(+), 94 deletions(-) diff --git a/doc/lightning-bkpr-listaccountevents.7.md b/doc/lightning-bkpr-listaccountevents.7.md index b2b393aa70b5..4e132eedf54c 100644 --- a/doc/lightning-bkpr-listaccountevents.7.md +++ b/doc/lightning-bkpr-listaccountevents.7.md @@ -38,6 +38,7 @@ If **type** is "chain": - **origin** (string, optional): The account this movement originated from - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. - **txid** (txid, optional): The txid of the transaction that created this event + - **description** (string, optional): The description of this event If **type** is "onchain_fee": - **txid** (txid): The txid of the transaction that created this event @@ -65,4 +66,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:dd72cc73e685daa6877984be8edede76dfec2f9d85df9a88ab1b031a93b20549) +[comment]: # ( SHA256STAMP:f8538b1d1e6cda7cd801690e5c09741c8a843b27cc922065598914516c16d2b3) diff --git a/doc/lightning-bkpr-listincome.7.md b/doc/lightning-bkpr-listincome.7.md index d7a3d87da143..dfc3647e1079 100644 --- a/doc/lightning-bkpr-listincome.7.md +++ b/doc/lightning-bkpr-listincome.7.md @@ -33,6 +33,7 @@ On success, an object containing **income_events** is returned. It is an array - **debit_msat** (msat): Amount spent (expenses) - **currency** (string): human-readable bech32 part for this coin type - **timestamp** (u32): Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp +- **description** (string, optional): More information about this event. If a `invoice` type, typically the bolt11/bolt12 description - **outpoint** (string, optional): The txid:outnum for this event, if applicable - **txid** (txid, optional): The txid of the transaction that created this event, if applicable - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. @@ -55,4 +56,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:400ac5e6719a7ae5ec7078a2cd220d91ab7e66ad45f08b46257e6ec04dcdeb4c) +[comment]: # ( SHA256STAMP:ab8508af0f40587c5a804f6981591564fe2d18b4fe3fbe7793e6a489607f7e0a) diff --git a/doc/schemas/bkpr-listaccountevents.schema.json b/doc/schemas/bkpr-listaccountevents.schema.json index 2a72cbf8e1ff..ca0fa6c8770f 100644 --- a/doc/schemas/bkpr-listaccountevents.schema.json +++ b/doc/schemas/bkpr-listaccountevents.schema.json @@ -95,6 +95,10 @@ "txid": { "type": "txid", "description": "The txid of the transaction that created this event" + }, + "description": { + "type": "string", + "description": "The description of this event" } }, "required": [ @@ -124,6 +128,7 @@ "debit_msat": {}, "currency": {}, "timestamp": {}, + "description": {}, "txid": { "type": "txid", "description": "The txid of the transaction that created this event" @@ -155,6 +160,7 @@ "debit_msat": {}, "currency": {}, "timestamp": {}, + "description": {}, "fees_msat": { "type": "msat", "description": "Amount paid in fees" diff --git a/doc/schemas/bkpr-listincome.schema.json b/doc/schemas/bkpr-listincome.schema.json index b572bf80bfab..147dc484c350 100644 --- a/doc/schemas/bkpr-listincome.schema.json +++ b/doc/schemas/bkpr-listincome.schema.json @@ -44,6 +44,10 @@ "type": "u32", "description": "Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp" }, + "description": { + "type": "string", + "description": "More information about this event. If a `invoice` type, typically the bolt11/bolt12 description" + }, "outpoint": { "type": "string", "description": "The txid:outnum for this event, if applicable" diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index 7e7730e79f17..ac30fafe2c63 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -37,7 +37,7 @@ PLUGIN_ALL_HEADER += $(BOOKKEEPER_HEADER) C_PLUGINS += plugins/bookkeeper PLUGINS += plugins/bookkeeper -plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(DB_OBJS) +plugins/bookkeeper: bitcoin/chainparams.o common/bolt12.o common/bolt12_merkle.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(DB_OBJS) # The following files contain SQL-annotated statements that we need to extact BOOKKEEPER_SQL_FILES := \ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index cd20bdf4d164..5321fd87ec4e 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -2,7 +2,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -623,6 +626,7 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev->payment_id = NULL; chain_ev->ignored = false; chain_ev->stealable = false; + chain_ev->desc = NULL; /* Update the account info too */ tags = tal_arr(chain_ev, enum mvt_tag, 1); @@ -798,7 +802,7 @@ static struct command_result *log_error(struct command *cmd, void *arg UNNEEDED) { plugin_log(cmd->plugin, LOG_BROKEN, - "error calling `listpeers`: %.*s", + "error calling rpc: %.*s", json_tok_full_len(error), json_tok_full(buf, error)); @@ -898,70 +902,6 @@ static char *do_account_close_checks(const tal_t *ctx, return NULL; } -struct event_info { - struct chain_event *ev; - struct account *acct; -}; - -static struct command_result * -listpeers_done(struct command *cmd, const char *buf, - const jsmntok_t *result, struct event_info *info) -{ - struct acct_balance **balances, *bal; - struct amount_msat credit_diff, debit_diff; - const char *err; - /* Make sure to clean up when we're done */ - tal_steal(cmd, info); - - if (new_missed_channel_account(cmd, buf, result, - info->acct, - info->ev->currency, - info->ev->timestamp)) { - db_begin_transaction(db); - err = account_get_balance(tmpctx, db, info->acct->name, - false, false, &balances); - db_commit_transaction(db); - - if (err) - plugin_err(cmd->plugin, err); - - /* FIXME: multiple currencies per account? */ - if (tal_count(balances) > 0) - bal = balances[0]; - else { - bal = tal(balances, struct acct_balance); - bal->credit = AMOUNT_MSAT(0); - bal->debit = AMOUNT_MSAT(0); - } - assert(tal_count(balances) == 1); - - /* The expected current balance is zero, since - * we just got the channel close event */ - err = msat_find_diff(AMOUNT_MSAT(0), - bal->credit, - bal->debit, - &credit_diff, &debit_diff); - if (err) - plugin_err(cmd->plugin, err); - - log_journal_entry(info->acct, - info->ev->currency, - info->ev->timestamp - 1, - credit_diff, debit_diff); - } else - plugin_log(cmd->plugin, LOG_BROKEN, - "Unable to find account %s in listpeers", - info->acct->name); - - /* Maybe mark acct as onchain resolved */ - err = do_account_close_checks(cmd, info->ev, info->acct); - if (err) - plugin_err(cmd->plugin, err); - - return notification_handled(cmd); -} - - static struct command_result *json_balance_snapshot(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -1144,6 +1084,242 @@ static struct command_result *json_balance_snapshot(struct command *cmd, return notification_handled(cmd); } +/* Returns true if "fatal" error, otherwise just a normal error */ +static char *fetch_out_desc_invstr(const tal_t *ctx, const char *buf, + const jsmntok_t *tok, char **err) +{ + char *bolt, *desc, *fail; + + /* It's a bolt11! Parse it out to a desc */ + if (!json_scan(ctx, buf, tok, "{bolt11:%}", + JSON_SCAN_TAL(ctx, json_strdup, &bolt))) { + struct bolt11 *bolt11; + u5 *sigdata; + struct sha256 hash; + bool have_n; + + bolt11 = bolt11_decode_nosig(ctx, bolt, + /* No desc/features/chain checks */ + NULL, NULL, NULL, + &hash, &sigdata, &have_n, + &fail); + + if (bolt11) { + if (bolt11->description) + desc = tal_strdup(ctx, bolt11->description); + else if (bolt11->description_hash) + desc = tal_fmt(ctx, "%s", + type_to_string(ctx, + struct sha256, + bolt11->description_hash)); + else + desc = NULL; + } else { + *err = tal_fmt(ctx, "failed to parse bolt11 %s: %s", + bolt, fail); + return NULL; + } + } else if (!json_scan(ctx, buf, tok, "{bolt12:%}", + JSON_SCAN_TAL(ctx, json_strdup, &bolt))) { + struct tlv_invoice *bolt12; + + bolt12 = invoice_decode_nosig(ctx, bolt, strlen(bolt), + /* No features/chain checks */ + NULL, NULL, + &fail); + if (!bolt12) { + *err = tal_fmt(ctx, "failed to parse" + " bolt12 %s: %s", + bolt, fail); + return NULL; + } + + if (bolt12->description) + desc = tal_strndup(ctx, + cast_signed(char *, bolt12->description), + tal_bytelen(bolt12->description)); + else + desc = NULL; + } else + desc = NULL; + + *err = NULL; + return desc; +} + +static struct command_result * +listinvoice_done(struct command *cmd, const char *buf, + const jsmntok_t *result, struct sha256 *payment_hash) +{ + size_t i; + const jsmntok_t *inv_arr_tok, *inv_tok; + const char *desc; + inv_arr_tok = json_get_member(buf, result, "invoices"); + assert(inv_arr_tok->type == JSMN_ARRAY); + + desc = NULL; + json_for_each_arr(i, inv_tok, inv_arr_tok) { + char *err; + + /* Found desc in "description" */ + if (!json_scan(cmd, buf, inv_tok, "{description:%}", + JSON_SCAN_TAL(cmd, json_strdup, &desc))) + break; + + /* if 'description' doesn't exist, try bolt11/bolt12 */ + desc = fetch_out_desc_invstr(cmd, buf, inv_tok, &err); + if (desc || err) { + if (err) + plugin_log(cmd->plugin, + LOG_BROKEN, "%s", err); + break; + } + } + + if (desc) { + db_begin_transaction(db); + add_payment_hash_desc(db, payment_hash, desc); + db_commit_transaction(db); + } else + plugin_log(cmd->plugin, LOG_DBG, + "listinvoices:" + " description/bolt11/bolt12" + " not found (%.*s)", + result->end - result->start, buf); + + return notification_handled(cmd); +} + +static struct command_result * +listsendpays_done(struct command *cmd, const char *buf, + const jsmntok_t *result, struct sha256 *payment_hash) +{ + size_t i; + const jsmntok_t *pays_arr_tok, *pays_tok; + const char *desc; + pays_arr_tok = json_get_member(buf, result, "payments"); + assert(pays_arr_tok->type == JSMN_ARRAY); + + /* Did we find a matching entry? */ + desc = NULL; + json_for_each_arr(i, pays_tok, pays_arr_tok) { + char *err; + + desc = fetch_out_desc_invstr(cmd, buf, pays_tok, &err); + if (desc || err) { + if (err) + plugin_log(cmd->plugin, + LOG_BROKEN, "%s", err); + break; + } + } + + if (desc) { + db_begin_transaction(db); + add_payment_hash_desc(db, payment_hash, desc); + db_commit_transaction(db); + } else + plugin_log(cmd->plugin, LOG_DBG, + "listpays: bolt11/bolt12 not found:" + "(%.*s)", + result->end - result->start, buf); + + return notification_handled(cmd); +} + +static struct command_result *lookup_invoice_desc(struct command *cmd, + struct amount_msat credit, + struct amount_msat debit, + struct sha256 *payment_hash) +{ + struct out_req *req; + + if (!amount_msat_zero(credit)) + req = jsonrpc_request_start(cmd->plugin, cmd, + "listinvoices", + listinvoice_done, + log_error, + payment_hash); + else + req = jsonrpc_request_start(cmd->plugin, cmd, + "listsendpays", + listsendpays_done, + log_error, + payment_hash); + + json_add_sha256(req->js, "payment_hash", payment_hash); + send_outreq(cmd->plugin, req); + return command_still_pending(cmd); +} + +struct event_info { + struct chain_event *ev; + struct account *acct; +}; + +static struct command_result * +listpeers_done(struct command *cmd, const char *buf, + const jsmntok_t *result, struct event_info *info) +{ + struct acct_balance **balances, *bal; + struct amount_msat credit_diff, debit_diff; + const char *err; + /* Make sure to clean up when we're done */ + tal_steal(cmd, info); + + if (new_missed_channel_account(cmd, buf, result, + info->acct, + info->ev->currency, + info->ev->timestamp)) { + db_begin_transaction(db); + err = account_get_balance(tmpctx, db, info->acct->name, + false, false, &balances); + db_commit_transaction(db); + + if (err) + plugin_err(cmd->plugin, err); + + /* FIXME: multiple currencies per account? */ + if (tal_count(balances) > 0) + bal = balances[0]; + else { + bal = tal(balances, struct acct_balance); + bal->credit = AMOUNT_MSAT(0); + bal->debit = AMOUNT_MSAT(0); + } + assert(tal_count(balances) == 1); + + /* The expected current balance is zero, since + * we just got the channel close event */ + err = msat_find_diff(AMOUNT_MSAT(0), + bal->credit, + bal->debit, + &credit_diff, &debit_diff); + if (err) + plugin_err(cmd->plugin, err); + + log_journal_entry(info->acct, + info->ev->currency, + info->ev->timestamp - 1, + credit_diff, debit_diff); + } else + plugin_log(cmd->plugin, LOG_BROKEN, + "Unable to find account %s in listpeers", + info->acct->name); + + /* Maybe mark acct as onchain resolved */ + err = do_account_close_checks(cmd, info->ev, info->acct); + if (err) + plugin_err(cmd->plugin, err); + + if (info->ev->payment_id && + streq(info->ev->tag, mvt_tag_str(INVOICE))) + return lookup_invoice_desc(cmd, info->ev->credit, + info->ev->debit, + info->ev->payment_id); + + return notification_handled(cmd); +} static struct command_result * parse_and_log_chain_move(struct command *cmd, const char *buf, @@ -1153,7 +1329,8 @@ parse_and_log_chain_move(struct command *cmd, const struct amount_msat debit, const char *coin_type STEALS, const u64 timestamp, - const enum mvt_tag *tags) + const enum mvt_tag *tags, + const char *desc) { struct chain_event *e = tal(cmd, struct chain_event); struct sha256 *payment_hash = tal(cmd, struct sha256); @@ -1247,6 +1424,7 @@ parse_and_log_chain_move(struct command *cmd, e->currency = tal_steal(e, coin_type); e->timestamp = timestamp; e->tag = mvt_tag_str(tags[0]); + e->desc = tal_steal(e, desc); e->ignored = false; e->stealable = false; @@ -1255,7 +1433,6 @@ parse_and_log_chain_move(struct command *cmd, e->stealable |= tags[i] == STEALABLE; } - db_begin_transaction(db); acct = find_account(cmd, db, acct_name); @@ -1349,6 +1526,18 @@ parse_and_log_chain_move(struct command *cmd, if (err) plugin_err(cmd->plugin, err); + /* Check for invoice desc data, necessary */ + if (e->payment_id) { + for (size_t i = 0; i < tal_count(tags); i++) { + if (tags[i] != INVOICE) + continue; + + return lookup_invoice_desc(cmd, e->credit, + e->debit, + e->payment_id); + } + } + return notification_handled(cmd);; } @@ -1361,7 +1550,8 @@ parse_and_log_channel_move(struct command *cmd, const struct amount_msat debit, const char *coin_type STEALS, const u64 timestamp, - const enum mvt_tag *tags) + const enum mvt_tag *tags, + const char *desc) { struct channel_event *e = tal(cmd, struct channel_event); struct account *acct; @@ -1397,6 +1587,7 @@ parse_and_log_channel_move(struct command *cmd, e->currency = tal_steal(e, coin_type); e->timestamp = timestamp; e->tag = mvt_tag_str(tags[0]); + e->desc = tal_steal(e, desc); /* Go find the account for this event */ db_begin_transaction(db); @@ -1410,6 +1601,18 @@ parse_and_log_channel_move(struct command *cmd, log_channel_event(db, acct, e); db_commit_transaction(db); + /* Check for invoice desc data, necessary */ + if (e->payment_id) { + for (size_t i = 0; i < tal_count(tags); i++) { + if (tags[i] != INVOICE) + continue; + + return lookup_invoice_desc(cmd, e->credit, + e->debit, + e->payment_id); + } + } + return notification_handled(cmd); } @@ -1434,9 +1637,9 @@ static char *parse_tags(const tal_t *ctx, return NULL; } -static struct command_result * json_coin_moved(struct command *cmd, - const char *buf, - const jsmntok_t *params) +static struct command_result *json_coin_moved(struct command *cmd, + const char *buf, + const jsmntok_t *params) { const char *err, *mvt_type, *acct_name, *coin_type; u32 version; @@ -1490,13 +1693,15 @@ static struct command_result * json_coin_moved(struct command *cmd, if (streq(mvt_type, CHAIN_MOVE)) return parse_and_log_chain_move(cmd, buf, params, acct_name, credit, debit, - coin_type, timestamp, tags); + coin_type, timestamp, tags, + NULL); assert(streq(mvt_type, CHANNEL_MOVE)); return parse_and_log_channel_move(cmd, buf, params, acct_name, credit, debit, - coin_type, timestamp, tags); + coin_type, timestamp, tags, + NULL); } const struct plugin_notification notifs[] = { diff --git a/plugins/bkpr/chain_event.c b/plugins/bkpr/chain_event.c index 633aedb1db71..639930333d75 100644 --- a/plugins/bkpr/chain_event.c +++ b/plugins/bkpr/chain_event.c @@ -22,5 +22,7 @@ void json_add_chain_event(struct json_stream *out, struct chain_event *ev) json_add_sha256(out, "payment_id", ev->payment_id); json_add_u64(out, "timestamp", ev->timestamp); json_add_u32(out, "blockheight", ev->blockheight); + if (ev->desc) + json_add_string(out, "description", ev->desc); json_object_end(out); } diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index 0a2771c515c8..3fea8f29dca5 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -60,6 +60,9 @@ struct chain_event { /* Sometimes chain events resolve payments */ struct sha256 *payment_id; + + /* Desc of event (maybe useful for printing notes) */ + const char *desc; }; void json_add_chain_event(struct json_stream *out, diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index f127c6b7c853..3952a0b9f3f5 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -26,6 +26,7 @@ struct channel_event *new_channel_event(const tal_t *ctx, ev->payment_id = tal_steal(ev, payment_id); ev->part_id = part_id; ev->timestamp = timestamp; + ev->desc = NULL; return ev; } @@ -47,5 +48,7 @@ void json_add_channel_event(struct json_stream *out, json_add_u32(out, "part_id", ev->part_id); } json_add_u64(out, "timestamp", ev->timestamp); + if (ev->desc) + json_add_string(out, "description", ev->desc); json_object_end(out); } diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h index 28a0787028fd..ec09858bbe91 100644 --- a/plugins/bkpr/channel_event.h +++ b/plugins/bkpr/channel_event.h @@ -43,6 +43,9 @@ struct channel_event { /* What time did the event happen */ u64 timestamp; + + /* Description, usually from invoice */ + const char *desc; }; struct channel_event *new_channel_event(const tal_t *ctx, diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 75795c8f23cb..fdd9ebd2b455 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -96,6 +96,8 @@ static struct migration db_migrations[] = { {SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL}, {SQL("ALTER TABLE chain_events ADD ignored INTEGER;"), NULL}, {SQL("ALTER TABLE chain_events ADD stealable INTEGER;"), NULL}, + {SQL("ALTER TABLE chain_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE channel_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, }; static bool db_migrate(struct plugin *p, struct db *db) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 0a2eab100503..7e586d3de5a6 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -47,16 +47,13 @@ static struct income_event *chain_to_income(const tal_t *ctx, inc->timestamp = ev->timestamp; inc->outpoint = tal_dup(inc, struct bitcoin_outpoint, &ev->outpoint); - if (ev->spending_txid) - inc->txid = tal_dup(inc, struct bitcoin_txid, - ev->spending_txid); + if (ev->desc) + inc->desc = tal_strdup(inc, ev->desc); else - inc->txid = NULL; + inc->desc = NULL; - if (ev->payment_id) - inc->payment_id = tal_dup(inc, struct sha256, ev->payment_id); - else - inc->payment_id = NULL; + inc->txid = tal_dup_or_null(inc, struct bitcoin_txid, ev->spending_txid); + inc->payment_id = tal_dup_or_null(inc, struct sha256, ev->payment_id); return inc; } @@ -76,10 +73,11 @@ static struct income_event *channel_to_income(const tal_t *ctx, inc->timestamp = ev->timestamp; inc->outpoint = NULL; inc->txid = NULL; - if (ev->payment_id) - inc->payment_id = tal_dup(inc, struct sha256, ev->payment_id); + if (ev->desc) + inc->desc = tal_strdup(inc, ev->desc); else - inc->payment_id = NULL; + inc->desc = NULL; + inc->payment_id = tal_dup_or_null(inc, struct sha256, ev->payment_id); return inc; } @@ -99,6 +97,7 @@ static struct income_event *onchainfee_to_income(const tal_t *ctx, inc->txid = tal_dup(inc, struct bitcoin_txid, &fee->txid); inc->outpoint = NULL; inc->payment_id = NULL; + inc->desc = NULL; return inc; } @@ -397,6 +396,9 @@ void json_add_income_event(struct json_stream *out, struct income_event *ev) json_add_string(out, "currency", ev->currency); json_add_u64(out, "timestamp", ev->timestamp); + if (ev->desc) + json_add_string(out, "description", ev->desc); + if (ev->outpoint) json_add_outpoint(out, "outpoint", ev->outpoint); @@ -570,7 +572,8 @@ static void koinly_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) fprintf(csvf, ","); /* Description */ - fprintf(csvf, "%s: account %s", ev->tag, ev->acct_name); + if (ev->desc) + fprintf(csvf, "%s", ev->desc); fprintf(csvf, ","); /* TxHash */ @@ -709,8 +712,8 @@ static void harmony_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) ev->outpoint)); fprintf(csvf, ","); - /* ",Note" account tag */ - fprintf(csvf, "%s %s", ev->acct_name, ev->tag); + /* ",Note" description (may be NULL) */ + fprintf(csvf, "%s", ev->desc ? ev->desc : ""); } static void quickbooks_header(FILE *csvf) @@ -733,12 +736,17 @@ static void quickbooks_entry(const tal_t *ctx, FILE *csvf, struct income_event * /* datefmt: dd/mm/yyyy */ char timebuf[sizeof("dd/mm/yyyy")]; strftime(timebuf, sizeof(timebuf), "%d/%m/%Y", gmtime(&tv)); + + /* New line! */ + fprintf(csvf, "\n"); + fprintf(csvf, "%s", timebuf); fprintf(csvf, ","); /* Description */ - fprintf(csvf, "%s (%s) in %s", - ev->tag, ev->acct_name, ev->currency); + fprintf(csvf, "%s (%s) %s: %s", + ev->tag, ev->acct_name, ev->currency, + ev->desc ? ev->desc : "no desc"); fprintf(csvf, ","); /* Credit */ diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h index 3092e513bd79..12f288d599ed 100644 --- a/plugins/bkpr/incomestmt.h +++ b/plugins/bkpr/incomestmt.h @@ -8,6 +8,7 @@ struct income_event { char *acct_name; char *tag; + char *desc; struct amount_msat credit; struct amount_msat debit; char *currency; diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 43f2ccc3a4db..9b1ab449f4da 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -58,6 +58,11 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st e->ignored = db_col_int(stmt, "e.ignored") == 1; e->stealable = db_col_int(stmt, "e.stealable") == 1; + if (!db_col_is_null(stmt, "e.ev_desc")) + e->desc = db_col_strdup(e, stmt, "e.ev_desc"); + else + e->desc = NULL; + return e; } @@ -102,6 +107,11 @@ static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt e->part_id = db_col_int(stmt, "e.part_id"); e->timestamp = db_col_u64(stmt, "e.timestamp"); + if (!db_col_is_null(stmt, "e.ev_desc")) + e->desc = db_col_strdup(e, stmt, "e.ev_desc"); + else + e->desc = NULL; + return e; } @@ -130,6 +140,7 @@ struct chain_event **list_chain_events_timebox(const tal_t *ctx, ", e.payment_id" ", e.ignored" ", e.stealable" + ", e.ev_desc" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -171,6 +182,7 @@ struct chain_event **account_get_chain_events(const tal_t *ctx, ", e.payment_id" ", e.ignored" ", e.stealable" + ", e.ev_desc" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -205,6 +217,7 @@ static struct chain_event **find_txos_for_tx(const tal_t *ctx, ", e.payment_id" ", e.ignored" ", e.stealable" + ", e.ev_desc" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -586,6 +599,31 @@ void maybe_mark_account_onchain(struct db *db, struct account *acct) tal_free(ctx); } +void add_payment_hash_desc(struct db *db, + struct sha256 *payment_hash, + const char *desc) +{ + struct db_stmt *stmt; + + /* Ok, now we update the account with this blockheight */ + stmt = db_prepare_v2(db, SQL("UPDATE channel_events SET" + " ev_desc = ?" + " WHERE" + " payment_id = ?")); + db_bind_text(stmt, 0, desc); + db_bind_sha256(stmt, 1, payment_hash); + db_exec_prepared_v2(take(stmt)); + + /* Ok, now we update the account with this blockheight */ + stmt = db_prepare_v2(db, SQL("UPDATE chain_events SET" + " ev_desc = ?" + " WHERE" + " payment_id = ?")); + db_bind_text(stmt, 0, desc); + db_bind_sha256(stmt, 1, payment_hash); + db_exec_prepared_v2(take(stmt)); +} + struct chain_event *find_chain_event_by_id(const tal_t *ctx, struct db *db, u64 event_db_id) @@ -611,6 +649,7 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, ", e.payment_id" ", e.ignored" ", e.stealable" + ", e.ev_desc" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -657,6 +696,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.payment_id" ", e.ignored" ", e.stealable" + ", e.ev_desc" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -685,6 +725,7 @@ static struct chain_event *find_chain_event(const tal_t *ctx, ", e.payment_id" ", e.ignored" ", e.stealable" + ", e.ev_desc" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON e.account_id = a.id" @@ -836,6 +877,7 @@ struct channel_event **list_channel_events_timebox(const tal_t *ctx, ", e.payment_id" ", e.part_id" ", e.timestamp" + ", e.ev_desc" " FROM channel_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -882,6 +924,7 @@ struct channel_event **account_get_channel_events(const tal_t *ctx, ", e.payment_id" ", e.part_id" ", e.timestamp" + ", e.ev_desc" " FROM channel_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -1283,9 +1326,10 @@ void log_channel_event(struct db *db, ", payment_id" ", part_id" ", timestamp" + ", ev_desc" ")" " VALUES" - " (?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); db_bind_text(stmt, 1, e->tag); @@ -1299,6 +1343,10 @@ void log_channel_event(struct db *db, db_bind_null(stmt, 6); db_bind_int(stmt, 7, e->part_id); db_bind_u64(stmt, 8, e->timestamp); + if (e->desc) + db_bind_text(stmt, 9, e->desc); + else + db_bind_null(stmt, 9); db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); @@ -1330,6 +1378,7 @@ static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db ", e.payment_id" ", e.ignored" ", e.stealable" + ", e.ev_desc" " FROM chain_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -1820,9 +1869,10 @@ bool log_chain_event(struct db *db, ", spending_txid" ", ignored" ", stealable" + ", ev_desc" ")" " VALUES " - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); if (e->origin_acct) @@ -1851,6 +1901,10 @@ bool log_chain_event(struct db *db, db_bind_int(stmt, 13, e->ignored ? 1 : 0); db_bind_int(stmt, 14, e->stealable ? 1 : 0); + if (e->desc) + db_bind_text(stmt, 15, e->desc); + else + db_bind_null(stmt, 15); db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); e->acct_db_id = acct->db_id; diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index c7c4209ca426..83714c05e451 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -184,6 +184,14 @@ char *update_channel_onchain_fees(const tal_t *ctx, * The point of this is to allow us to prune data, eventually */ void maybe_mark_account_onchain(struct db *db, struct account *acct); +/* We fetch invoice desc data after the fact and then update it + * Updates both the chain_event and channel_event tables for all + * matching payment_hashes + * */ +void add_payment_hash_desc(struct db *db, + struct sha256 *payment_hash, + const char *desc); + /* When we make external deposits from the wallet, we don't * count them until any output that was spent *into* them is * confirmed onchain. diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 6af1ea6a6c55..b62cedb6176b 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -287,6 +287,10 @@ static bool channel_events_eq(struct channel_event *e1, struct channel_event *e2 CHECK(e1->part_id == e2->part_id); CHECK(e1->timestamp == e2->timestamp); + CHECK((e1->desc != NULL) == (e2->desc != NULL)); + if (e1->desc) + CHECK(streq(e1->desc, e2->desc)); + return true; } @@ -314,6 +318,9 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) if (e1->payment_id) CHECK(sha256_eq(e1->payment_id, e2->payment_id)); + CHECK((e1->desc != NULL) == (e2->desc != NULL)); + if (e1->desc) + CHECK(streq(e1->desc, e2->desc)); return true; } @@ -335,6 +342,7 @@ static struct channel_event *make_channel_event(const tal_t *ctx, ev->timestamp = 1919191; ev->part_id = 19; ev->tag = tag; + ev->desc = tal_fmt(ev, "description"); return ev; } @@ -364,6 +372,7 @@ static struct chain_event *make_chain_event(const tal_t *ctx, ev->blockheight = blockheight; ev->ignored = false; ev->stealable = false; + ev->desc = tal_fmt(ev, "hello hello"); memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid)); ev->outpoint.n = outnum; @@ -880,9 +889,11 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev1->currency = "btc"; ev1->timestamp = 11111; ev1->part_id = 19; + ev1->desc = tal_strdup(ev1, "hello desc1"); /* Passing unknown tags in should be ok */ ev1->tag = "hello"; + ev1->desc = tal_fmt(ev1, "desc"); ev2 = tal(ctx, struct channel_event); ev2->payment_id = tal(ev2, struct sha256); @@ -894,6 +905,7 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev2->timestamp = 22222; ev2->part_id = 0; ev2->tag = tal_fmt(ev2, "deposit"); + ev2->desc = NULL; ev3 = tal(ctx, struct channel_event); ev3->payment_id = tal(ev3, struct sha256); @@ -905,6 +917,7 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev3->timestamp = 33333; ev3->part_id = 5; ev3->tag = tal_fmt(ev3, "routed"); + ev3->desc = NULL; db_begin_transaction(db); log_channel_event(db, acct, ev1); @@ -972,6 +985,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev1->spending_txid = tal(ctx, struct bitcoin_txid); memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid)); ev1->payment_id = NULL; + ev1->desc = tal_fmt(ev1, "description"); db_begin_transaction(db); log_chain_event(db, acct, ev1); @@ -992,6 +1006,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev2->outpoint.n = 1; ev2->spending_txid = NULL; ev2->payment_id = tal(ctx, struct sha256); + ev2->desc = NULL; memset(ev2->payment_id, 'B', sizeof(struct sha256)); /* Dummy event, logged to separate account */ @@ -1011,6 +1026,7 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) ev3->spending_txid = tal(ctx, struct bitcoin_txid); memset(ev3->spending_txid, 'D', sizeof(struct bitcoin_txid)); ev3->payment_id = NULL; + ev3->desc = NULL; db_begin_transaction(db); log_chain_event(db, acct, ev2); @@ -1238,6 +1254,7 @@ static bool test_account_crud(const tal_t *ctx, struct plugin *p) ev1->spending_txid = tal(ctx, struct bitcoin_txid); memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid)); ev1->payment_id = NULL; + ev1->desc = tal_fmt(ev1, "oh hello"); db_begin_transaction(db); log_chain_event(db, acct, ev1); diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index b082504e517a..1cd013d8ff4f 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -376,3 +376,45 @@ def test_bookkeeping_onchaind_txs(node_factory, bitcoind): assert len(funds['channels']) == 0 outs = sum([out['amount_msat'] for out in funds['outputs']]) assert outs == only_one(wallet_bal['balances'])['balance_msat'] + + +def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): + """ + When an 'invoice' type event comes through, we look up the description details + to include about the item. Particularly useful for CSV outputs etc. + """ + l1, l2 = node_factory.line_graph(2, opts={'experimental-offers': None}) + + # Send l2 funds via the channel + bolt11_desc = "test bolt11 description" + l1.pay(l2, 11000000, label=bolt11_desc) + l1.daemon.wait_for_log('coin_move .* [(]invoice[)] 0msat -11000000msat') + l2.daemon.wait_for_log('coin_move .* [(]invoice[)] 11000000msat') + + # Test paying an bolt11 invoice (rcvr) + l1_inc_ev = l1.rpc.bkpr_listincome()['income_events'] + inv = only_one([ev for ev in l1_inc_ev if ev['tag'] == 'invoice']) + assert inv['description'] == bolt11_desc + + # Test paying an bolt11 invoice (sender) + l2_inc_ev = l2.rpc.bkpr_listincome()['income_events'] + inv = only_one([ev for ev in l2_inc_ev if ev['tag'] == 'invoice']) + assert inv['description'] == bolt11_desc + + # Make an offer (l1) + bolt12_desc = "test bolt12 description" + offer = l1.rpc.call('offer', [100, bolt12_desc]) + invoice = l2.rpc.call('fetchinvoice', {'offer': offer['bolt12']}) + paid = l2.rpc.pay(invoice['invoice']) + l1.daemon.wait_for_log('coin_move .* [(]invoice[)] 100msat') + l2.daemon.wait_for_log('coin_move .* [(]invoice[)] 0msat -100msat') + + # Test paying an offer (bolt12) (rcvr) + l1_inc_ev = l1.rpc.bkpr_listincome()['income_events'] + inv = only_one([ev for ev in l1_inc_ev if 'payment_id' in ev and ev['payment_id'] == paid['payment_hash']]) + assert inv['description'] == bolt12_desc + + # Test paying an offer (bolt12) (sender) + l2_inc_ev = l2.rpc.bkpr_listincome()['income_events'] + inv = only_one([ev for ev in l2_inc_ev if 'payment_id' in ev and ev['payment_id'] == paid['payment_hash'] and ev['tag'] == 'invoice']) + assert inv['description'] == bolt12_desc diff --git a/tests/test_invoices.py b/tests/test_invoices.py index ccce092183bf..9cfd63ade396 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -737,3 +737,8 @@ def test_invoice_deschash(node_factory, chainparams): with pytest.raises(RpcError, match=r'description already removed'): l2.rpc.delinvoice('label', "paid", desconly=True) + + # desc-hashes lands in bookkeeper data (description) + wait_for(lambda: len([ev for ev in l1.rpc.bkpr_listincome()['income_events'] if ev['tag'] == 'invoice']) == 1) + inv = only_one([ev for ev in l1.rpc.bkpr_listincome()['income_events'] if ev['tag'] == 'invoice']) + assert inv['description'] == b11['description_hash'] From 5146baa00bed5823617c3663a7d7f9acff177106 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1198/1530] bkpr csvs: koinly + cointracker only accept fees on the same line So we print out invoice fees on the same line for those CSVs! This means we have to do a little bit of gymnastics (but not too bad): - we save the fee amount onto the income event now so we can use it later - we ignore every "invoice_fee" event for the koinly/cointracker Note that since we're not skipping income events in the loops we also move the newline character to the start of every `_entry` function so skipped records dont incur empth lines. Changelog-Added: bkpr: print out invoice fees on the same line for `koinly` and `cointracker` csv types --- plugins/bkpr/incomestmt.c | 48 +++++++++++++++++++++++++++------------ plugins/bkpr/incomestmt.h | 3 +++ tests/test_pay.py | 9 ++++++++ 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 7e586d3de5a6..49d0121c3a0f 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -43,6 +43,7 @@ static struct income_event *chain_to_income(const tal_t *ctx, inc->tag = tal_strdup(inc, ev->tag); inc->credit = credit; inc->debit = debit; + inc->fees = AMOUNT_MSAT(0); inc->currency = tal_strdup(inc, ev->currency); inc->timestamp = ev->timestamp; inc->outpoint = tal_dup(inc, struct bitcoin_outpoint, &ev->outpoint); @@ -69,6 +70,7 @@ static struct income_event *channel_to_income(const tal_t *ctx, inc->tag = tal_strdup(inc, ev->tag); inc->credit = credit; inc->debit = debit; + inc->fees = ev->fees; inc->currency = tal_strdup(inc, ev->currency); inc->timestamp = ev->timestamp; inc->outpoint = NULL; @@ -92,6 +94,7 @@ static struct income_event *onchainfee_to_income(const tal_t *ctx, /* We swap these, as they're actually opposite */ inc->credit = fee->debit; inc->debit = fee->credit; + inc->fees = AMOUNT_MSAT(0); inc->currency = tal_strdup(inc, fee->currency); inc->timestamp = fee->timestamp; inc->txid = tal_dup(inc, struct bitcoin_txid, &fee->txid); @@ -360,9 +363,10 @@ struct income_event **list_income_events(const tal_t *ctx, if (ev) tal_arr_expand(&evs, ev); - /* Breakout fees on sent payments */ + /* Breakout fees on sent payments, if present */ if (streq(chan->tag, "invoice") - && !amount_msat_zero(chan->debit)) { + && !amount_msat_zero(chan->debit) + && !amount_msat_zero(chan->fees)) { ev = paid_invoice_fee(evs, chan); tal_arr_expand(&evs, ev); } @@ -464,6 +468,13 @@ static void cointrack_entry(const tal_t *ctx, FILE *csvf, struct income_event *e time_t tv; tv = ev->timestamp; char timebuf[sizeof("mm/dd/yyyy HH:MM:SS")]; + + /* Cointrack counts invoice fee events inline */ + if (streq(ev->tag, account_entry_tag_str(INVOICEFEE))) + return; + + fprintf(csvf, "\n"); + strftime(timebuf, sizeof(timebuf), "%m/%d/%Y %T", gmtime(&tv)); fprintf(csvf, "%s", timebuf); fprintf(csvf, ","); @@ -479,8 +490,7 @@ static void cointrack_entry(const tal_t *ctx, FILE *csvf, struct income_event *e fprintf(csvf, ","); /* "Sent Quantity,Sent Currency," */ - if (!amount_msat_zero(ev->debit) - && !streq(ev->tag, ONCHAIN_FEE)) { + if (!amount_msat_zero(ev->debit)) { fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); @@ -490,9 +500,9 @@ static void cointrack_entry(const tal_t *ctx, FILE *csvf, struct income_event *e fprintf(csvf, ","); /* "Fee Amount,Fee Currency," */ - if (!amount_msat_zero(ev->debit) - && streq(ev->tag, ONCHAIN_FEE)) { - fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + if (!amount_msat_zero(ev->fees) + && streq(ev->tag, mvt_tag_str(INVOICE))) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->fees, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); } else @@ -530,13 +540,19 @@ static void koinly_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) tv = ev->timestamp; /* 2018-01-01 14:25 UTC */ char timebuf[sizeof("yyyy-mm-dd HH:MM UTC")]; + + /* Koinly counts invoice fee events inline */ + if (streq(ev->tag, account_entry_tag_str(INVOICEFEE))) + return; + + fprintf(csvf, "\n"); + strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M UTC", gmtime(&tv)); fprintf(csvf, "%s", timebuf); fprintf(csvf, ","); /* "Sent Amount,Sent Currency," */ - if (!amount_msat_zero(ev->debit) - && !streq(ev->tag, ONCHAIN_FEE)) { + if (!amount_msat_zero(ev->debit)) { fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); @@ -557,9 +573,9 @@ static void koinly_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) /* "Fee Amount,Fee Currency," */ - if (!amount_msat_zero(ev->debit) - && streq(ev->tag, ONCHAIN_FEE)) { - fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->debit, false)); + if (!amount_msat_zero(ev->fees) + && streq(ev->tag, mvt_tag_str(INVOICE))) { + fprintf(csvf, "%s", fmt_amount_msat_btc(ctx, ev->fees, false)); fprintf(csvf, ","); fprintf(csvf, "%s", convert_asset_type(ev)); } else @@ -652,6 +668,10 @@ static void harmony_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) /* datefmt: ISO-8601 */ char timebuf[sizeof("yyyy-mm-ddTHH:MM:SSZ")]; strftime(timebuf, sizeof(timebuf), "%Y-%m-%dT%TZ", gmtime(&tv)); + + /* New line! */ + fprintf(csvf, "\n"); + fprintf(csvf, "%s", timebuf); fprintf(csvf, ","); @@ -817,10 +837,8 @@ char *csv_print_income_events(const tal_t *ctx, return tal_fmt(ctx, "Failed to open csv file %s", filename); csvfmt->emit_header(csvf); - for (size_t i = 0; i < tal_count(evs); i++) { - fprintf(csvf, "\n"); + for (size_t i = 0; i < tal_count(evs); i++) csvfmt->emit_entry(ctx, csvf, evs[i]); - } fclose(csvf); return NULL; diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h index 12f288d599ed..1919ee8a1e30 100644 --- a/plugins/bkpr/incomestmt.h +++ b/plugins/bkpr/incomestmt.h @@ -11,6 +11,9 @@ struct income_event { char *desc; struct amount_msat credit; struct amount_msat debit; + /* Some CSVs require us to put fees on the + * same line as another entry */ + struct amount_msat fees; char *currency; u64 timestamp; diff --git a/tests/test_pay.py b/tests/test_pay.py index 64d170447d70..eb15416ee351 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1126,6 +1126,15 @@ def test_forward(node_factory, bitcoind): l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) + # Check that invoice payment and fee are tracked appropriately + l1.daemon.wait_for_log('coin_move .* [(]invoice[)]') + l1.rpc.bkpr_dumpincomecsv('koinly', 'koinly.csv') + + koinly_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'koinly.csv') + koinly_csv = open(koinly_path, 'rb').read() + expected_line = r'0.00100000000,.*,,,0.00000001001,.*,invoice' + assert only_one(re.findall(expected_line, str(koinly_csv))) + @pytest.mark.developer("needs --dev-fast-gossip") def test_forward_different_fees_and_cltv(node_factory, bitcoind): From e5d3ce3b1f1373f6b4e136f5272739a38b92c56d Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1199/1530] bkpr incomestmt: properly escape things for the CSVs First off, when we pull data out of JSON, unescape it so we don't end up with extraneous escapes in our bookkeeping data. I promise, it's worth it. Then, when we print descriptions out to the csvs, we gotta wrap everything in quotes... but also we have to change all the double-quotes to singles so that adding the quotes doesn't do anything untoward. We also just pass it thru json_escape to get rid of linebreaks etc. Note that in the tests we do a byte comparison instead of converting the CSV dumps to strings because python will escape the strings on conversion... --- plugins/bkpr/bookkeeper.c | 5 ++++- plugins/bkpr/incomestmt.c | 26 +++++++++++++++++++++++--- tests/test_bookkeeper.py | 20 ++++++++++++++++++-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 5321fd87ec4e..95a5fe8dacd5 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -1178,7 +1179,9 @@ listinvoice_done(struct command *cmd, const char *buf, if (desc) { db_begin_transaction(db); - add_payment_hash_desc(db, payment_hash, desc); + add_payment_hash_desc(db, payment_hash, + json_escape_unescape(cmd, + (struct json_escape *)desc)); db_commit_transaction(db); } else plugin_log(cmd->plugin, LOG_DBG, diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index 49d0121c3a0f..f20880249778 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -105,6 +105,26 @@ static struct income_event *onchainfee_to_income(const tal_t *ctx, return inc; } +/* CSVs don't like ',' in the middle. We short circuit this + * by wrapping the desc in double-quotes ("). But what if + * there's already double-quotes? Well we swap these to + * single-quotes (') and then use the json_escape function */ +static char *csv_safe_str(const tal_t *ctx, char *input TAKES) +{ + struct json_escape *esc; + char *dupe; + + /* Update the double-quotes in place */ + dupe = tal_strdup(ctx, input); + for (size_t i = 0; dupe[i] != '\0'; i++) { + if (dupe[i] == '"') + dupe[i] = '\''; + } + + esc = json_escape(ctx, dupe); + return tal_fmt(ctx, "\"%s\"", esc->s); +} + static struct income_event *maybe_chain_income(const tal_t *ctx, struct db *db, struct account *acct, @@ -589,7 +609,7 @@ static void koinly_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) /* Description */ if (ev->desc) - fprintf(csvf, "%s", ev->desc); + fprintf(csvf, "%s", csv_safe_str(ev, ev->desc)); fprintf(csvf, ","); /* TxHash */ @@ -733,7 +753,7 @@ static void harmony_entry(const tal_t *ctx, FILE *csvf, struct income_event *ev) fprintf(csvf, ","); /* ",Note" description (may be NULL) */ - fprintf(csvf, "%s", ev->desc ? ev->desc : ""); + fprintf(csvf, "%s", ev->desc ? csv_safe_str(ev, ev->desc) : ""); } static void quickbooks_header(FILE *csvf) @@ -766,7 +786,7 @@ static void quickbooks_entry(const tal_t *ctx, FILE *csvf, struct income_event * /* Description */ fprintf(csvf, "%s (%s) %s: %s", ev->tag, ev->acct_name, ev->currency, - ev->desc ? ev->desc : "no desc"); + ev->desc ? csv_safe_str(ev, ev->desc) : "no desc"); fprintf(csvf, ","); /* Credit */ diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 1cd013d8ff4f..0f10272fe7de 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -386,7 +386,7 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): l1, l2 = node_factory.line_graph(2, opts={'experimental-offers': None}) # Send l2 funds via the channel - bolt11_desc = "test bolt11 description" + bolt11_desc = 'test "bolt11" description, 🥰🪢' l1.pay(l2, 11000000, label=bolt11_desc) l1.daemon.wait_for_log('coin_move .* [(]invoice[)] 0msat -11000000msat') l2.daemon.wait_for_log('coin_move .* [(]invoice[)] 11000000msat') @@ -402,7 +402,7 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): assert inv['description'] == bolt11_desc # Make an offer (l1) - bolt12_desc = "test bolt12 description" + bolt12_desc = 'test "bolt12" description, 🥰🪢' offer = l1.rpc.call('offer', [100, bolt12_desc]) invoice = l2.rpc.call('fetchinvoice', {'offer': offer['bolt12']}) paid = l2.rpc.pay(invoice['invoice']) @@ -418,3 +418,19 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): l2_inc_ev = l2.rpc.bkpr_listincome()['income_events'] inv = only_one([ev for ev in l2_inc_ev if 'payment_id' in ev and ev['payment_id'] == paid['payment_hash'] and ev['tag'] == 'invoice']) assert inv['description'] == bolt12_desc + + # Check the CSVs look groovy + l1.rpc.bkpr_dumpincomecsv('koinly', 'koinly.csv') + l2.rpc.bkpr_dumpincomecsv('koinly', 'koinly.csv') + koinly_path = os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'koinly.csv') + l1_koinly_csv = open(koinly_path, 'rb').read() + bolt11_exp = bytes('invoice,"test \'bolt11\' description, 🥰🪢",', 'utf-8') + bolt12_exp = bytes('invoice,"test \'bolt12\' description, 🥰🪢",', 'utf-8') + + assert l1_koinly_csv.find(bolt11_exp) >= 0 + assert l1_koinly_csv.find(bolt12_exp) >= 0 + + koinly_path = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, 'koinly.csv') + l2_koinly_csv = open(koinly_path, 'rb').read() + assert l2_koinly_csv.find(bolt11_exp) >= 0 + assert l2_koinly_csv.find(bolt12_exp) >= 0 From 3c79a456c0c15abe8c980153d4fe8d6c8fccd6fe Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1200/1530] test-db-provider: if postgres in tests, startup a bookkeeper db FXIME: Has a edge case where if you disable the bookkeeper, it'll blowup because you've got an option that isn't present anywhere... --- contrib/pyln-testing/pyln/testing/db.py | 2 ++ contrib/pyln-testing/pyln/testing/utils.py | 1 + tests/db.py | 2 ++ tests/fixtures.py | 9 +++++++-- tests/test_misc.py | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/db.py b/contrib/pyln-testing/pyln/testing/db.py index 5ee90ce7944d..7110a8e52135 100644 --- a/contrib/pyln-testing/pyln/testing/db.py +++ b/contrib/pyln-testing/pyln/testing/db.py @@ -17,6 +17,7 @@ class Sqlite3Db(object): def __init__(self, path: str) -> None: self.path = path + self.provider = None def get_dsn(self) -> None: """SQLite3 doesn't provide a DSN, resulting in no CLI-option. @@ -59,6 +60,7 @@ class PostgresDb(object): def __init__(self, dbname, port): self.dbname = dbname self.port = port + self.provider = None self.conn = psycopg2.connect("dbname={dbname} user=postgres host=localhost port={port}".format( dbname=dbname, port=port diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index dfff1eb74581..749bfcde2645 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1447,6 +1447,7 @@ def get_node(self, node_id=None, options=None, dbfile=None, # Get the DB backend DSN we should be using for this test and this # node. db = self.db_provider.get_db(os.path.join(lightning_dir, TEST_NETWORK), self.testname, node_id) + db.provider = self.db_provider 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, diff --git a/tests/db.py b/tests/db.py index 905b8d4234ad..364beef44886 100644 --- a/tests/db.py +++ b/tests/db.py @@ -16,6 +16,7 @@ class Sqlite3Db(object): def __init__(self, path): self.path = path + self.provider = None def get_dsn(self): """SQLite3 doesn't provide a DSN, resulting in no CLI-option. @@ -54,6 +55,7 @@ class PostgresDb(object): def __init__(self, dbname, port): self.dbname = dbname self.port = port + self.provider = None self.conn = psycopg2.connect("dbname={dbname} user=postgres host=localhost port={port}".format( dbname=dbname, port=port diff --git a/tests/fixtures.py b/tests/fixtures.py index 04f691d92331..7667713b2653 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -33,8 +33,8 @@ def __init__(self, *args, **kwargs): # If we opted into checking the DB statements we will attach the dblog # plugin before starting the node check_dblog = os.environ.get("TEST_CHECK_DBSTMTS", None) == "1" - db = os.environ.get("TEST_DB_PROVIDER", "sqlite3") - if db == 'sqlite3' and check_dblog: + db_type = os.environ.get("TEST_DB_PROVIDER", "sqlite3") + if db_type == 'sqlite3' and check_dblog: dblog = os.path.join(os.path.dirname(__file__), 'plugins', 'dblog.py') has_dblog = len([o for o in self.daemon.cmd_line if 'dblog.py' in o]) > 0 if not has_dblog: @@ -42,6 +42,11 @@ def __init__(self, *args, **kwargs): self.daemon.opts['plugin={}'.format(dblog)] = None self.daemon.opts['dblog-file'] = 'dblog.sqlite3' + # FIXME: make sure bookkeeper is not disabled + if db_type == 'postgres': + accts_db = self.db.provider.get_db('', 'accounts', 0) + self.daemon.opts['bookkeeper-db'] = accts_db.get_dsn() + # Yes, we really want to test the local development version, not # something in out path. self.daemon.executable = 'lightningd/lightningd' diff --git a/tests/test_misc.py b/tests/test_misc.py index ee8a2b57c3b7..a29fc8778274 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2126,6 +2126,7 @@ def test_unix_socket_path_length(node_factory, bitcoind, directory, executor, db lightning_dir = os.path.join(directory, "anode" + "far" * 30 + "away") os.makedirs(lightning_dir) db = db_provider.get_db(lightning_dir, "test_unix_socket_path_length", 1) + db.provider = db_provider l1 = LightningNode(1, lightning_dir, bitcoind, executor, VALGRIND, db=db, port=reserve()) From 71c03bc082d82c96ea81071ed741222756b5fdba Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 17:04:40 +0930 Subject: [PATCH 1201/1530] bkpr: Add an option to set the database to something else (postgres) `lightningd` has an option --wallet that lets you supply a database dsn string to connect to a sqlite3/postgres database that's hosted/stored elsewhere. This adds the `--bookkeeper-db` option which does the same, except for the bookkeeping data for a node! Note that the default is to go in the `lightning-dir` in a database called `accounts.sqlite3` --- doc/lightning-listconfigs.7 | 4 +++- doc/lightning-listconfigs.7.md | 3 ++- doc/lightningd-config.5.md | 7 +++++++ doc/schemas/listconfigs.schema.json | 4 ++++ plugins/bkpr/bookkeeper.c | 16 +++++++++++++++- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 index 7b0af2ed4a27..068fb615e954 100644 --- a/doc/lightning-listconfigs.7 +++ b/doc/lightning-listconfigs.7 @@ -81,6 +81,8 @@ On success, an object is returned, containing: .IP \[bu] \fBbookkeeper-dir\fR (string, optional): \fBbookkeeper-dir\fR field from config or cmdline, or default .IP \[bu] +\fBbookkeeper-db\fR (string, optional): \fBbookkeeper-db\fR field from config or cmdline, or default +.IP \[bu] \fBalways-use-proxy\fR (boolean, optional): \fBalways-use-proxy\fR field from config or cmdline, or default .IP \[bu] \fBdaemon\fR (boolean, optional): \fBdaemon\fR field from config or cmdline, or default @@ -286,4 +288,4 @@ Vincenzo Palazzo \fI wrote the initial versi Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:b1148d7469f7bac293482be6f501031e4cac02f9bc113cf372a31747eb7a6055 +\" SHA256STAMP:acf490bbc9c764f67bf72de7383ea05422ce892dbf70de5ba8918fad15900bd8 diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 2405cf602cbc..a5cdf61732c6 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -51,6 +51,7 @@ On success, an object is returned, containing: - **disable-plugin** (array of strings, optional): - `disable-plugin` field from config or cmdline - **bookkeeper-dir** (string, optional): `bookkeeper-dir` field from config or cmdline, or default +- **bookkeeper-db** (string, optional): `bookkeeper-db` field from config or cmdline, or default - **always-use-proxy** (boolean, optional): `always-use-proxy` field from config or cmdline, or default - **daemon** (boolean, optional): `daemon` field from config or cmdline, or default - **wallet** (string, optional): `wallet` field from config or cmdline, or default @@ -213,4 +214,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:4a4cacd309d9593b45cb27563c25d9d3df7df8aef2b281145abeee27eae57fa9) +[comment]: # ( SHA256STAMP:999502771ada48f32011ea4df2443a2a3385d27377d8e55ec82cf283f9acd0a6) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index eb8bae77a6c1..115a2d09567c 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -221,6 +221,13 @@ automatically by `lightningd`. Directory to keep the accounts.sqlite3 database file in. Defaults to lightning-dir. + **bookkeeper-db**=*DSN* +Identify the location of the bookkeeper data. This is a fully qualified data source +name, including a scheme such as `sqlite3` or `postgres` followed by the +connection parameters. +Defaults to `sqlite3://accounts.sqlite3` in the `bookkeeper-dir`. + + **encrypted-hsm** If set, you will be prompted to enter a password used to encrypt the `hsm_secret`. Note that once you encrypt the `hsm_secret` this option will be mandatory for diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index 46feb13caa3c..56a1cd7b1d12 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -97,6 +97,10 @@ "type": "string", "description": "`bookkeeper-dir` field from config or cmdline, or default" }, + "bookkeeper-db": { + "type": "string", + "description": "`bookkeeper-db` field from config or cmdline, or default" + }, "always-use-proxy": { "type": "boolean", "description": "`always-use-proxy` field from config or cmdline, or default" diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 95a5fe8dacd5..ef7f25c12159 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -34,7 +34,7 @@ /* The database that we store all the accounting data in */ static struct db *db ; -static char *db_dsn = "sqlite3://accounts.sqlite3"; +static char *db_dsn; static char *datadir; static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, @@ -1780,7 +1780,14 @@ static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) "Unable to switch to 'bookkeeper-dir'=%s", datadir); } + + /* No user suppled db_dsn, set one up here */ + if (!db_dsn) + db_dsn = tal_fmt(NULL, "sqlite3://accounts.sqlite3"); + + plugin_log(p, LOG_DBG, "Setting up database at %s", db_dsn); db = notleak(db_setup(p, p, db_dsn)); + db_dsn = tal_free(db_dsn); return NULL; } @@ -1791,6 +1798,8 @@ int main(int argc, char *argv[]) /* No datadir is default */ datadir = NULL; + db_dsn = NULL; + plugin_main(argv, init, PLUGIN_STATIC, true, NULL, commands, ARRAY_SIZE(commands), notifs, ARRAY_SIZE(notifs), @@ -1800,6 +1809,11 @@ int main(int argc, char *argv[]) "string", "Location for bookkeeper records.", charp_option, &datadir), + plugin_option("bookkeeper-db", + "string", + "Location of the bookkeeper database", + charp_option, &db_dsn), NULL); + return 0; } From 305a2388108e638a7c24c097be4a5814d081dc52 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Jul 2022 17:04:41 +0930 Subject: [PATCH 1202/1530] plugins/Makefile: put bitcoin/chainparams.o in PLUGIN_COMMON_OBJS since everyone needs it. This was originally done as part of the bookkeeper introduction, but it deserves its own patch (and that one didn't remove the bitcoin/chainparams.o from the individual requirements lines). Suggested-by: @niftynei Signed-off-by: Rusty Russell --- plugins/Makefile | 25 +++++++++++++------------ plugins/bkpr/Makefile | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/Makefile b/plugins/Makefile index e38312c14de9..426b9e848bee 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -111,6 +111,7 @@ endif PLUGIN_COMMON_OBJS := \ bitcoin/base58.o \ bitcoin/block.o \ + bitcoin/chainparams.o \ bitcoin/feerate.o \ bitcoin/preimage.o \ bitcoin/privkey.o \ @@ -168,32 +169,32 @@ ALL_PROGRAMS += $(C_PLUGINS) # Make all plugins depend on all plugin headers, for simplicity. $(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER) -plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o +plugins/pay: $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o -plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/autoclean: $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/chanbackup: bitcoin/chainparams.o $(PLUGIN_chanbackup_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/chanbackup: $(PLUGIN_chanbackup_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/commando: $(PLUGIN_COMMANDO_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) bitcoin/chainparams.o +plugins/commando: $(PLUGIN_COMMANDO_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) # Topology wants to decode node_announcement, and peer_wiregen which # pulls in some of bitcoin/. -plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o bitcoin/chainparams.o wire/peer$(EXP)_wiregen.o wire/channel_type_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o wire/peer$(EXP)_wiregen.o wire/channel_type_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/txprepare: bitcoin/chainparams.o $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/txprepare: $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/bcli: $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/keysend: bitcoin/chainparams.o wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o +plugins/keysend: wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o $(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER) -plugins/spenderp: bitcoin/block.o bitcoin/chainparams.o bitcoin/preimage.o bitcoin/psbt.o common/psbt_open.o wire/peer${EXP}_wiregen.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/spenderp: bitcoin/block.o bitcoin/preimage.o bitcoin/psbt.o common/psbt_open.o wire/peer${EXP}_wiregen.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) -plugins/offers: bitcoin/chainparams.o $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/bolt11_json.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) +plugins/offers: $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/bolt11_json.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) -plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) common/gossmap.o common/fp16.o common/dijkstra.o common/route.o common/blindedpath.o common/hmac.o common/blinding.o +plugins/fetchinvoice: $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) common/gossmap.o common/fp16.o common/dijkstra.o common/route.o common/blindedpath.o common/hmac.o common/blinding.o -plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/funder: bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) # Generated from PLUGINS definition in plugins/Makefile ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index ac30fafe2c63..ee6c96af82ab 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -37,7 +37,7 @@ PLUGIN_ALL_HEADER += $(BOOKKEEPER_HEADER) C_PLUGINS += plugins/bookkeeper PLUGINS += plugins/bookkeeper -plugins/bookkeeper: bitcoin/chainparams.o common/bolt12.o common/bolt12_merkle.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(DB_OBJS) +plugins/bookkeeper: common/bolt12.o common/bolt12_merkle.o $(BOOKKEEPER_OBJS) $(PLUGIN_LIB_OBJS) $(JSMN_OBJTS) $(PLUGIN_COMMON_OBJS) $(WIRE_OBJS) $(DB_OBJS) # The following files contain SQL-annotated statements that we need to extact BOOKKEEPER_SQL_FILES := \ From 6a22411f7e3daa8287c5ec88eeb498d3a09c5a63 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Jul 2022 17:04:41 +0930 Subject: [PATCH 1203/1530] doc: note that bookkeeper-dir and bookkeeper-db are in bookkeeper plugin. We do this for bcli options, too. Good to note if people disable/replace the plugin. Signed-off-by: Rusty Russell --- doc/lightningd-config.5.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 115a2d09567c..d038754b31a0 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -217,11 +217,11 @@ authenticate with username `user` and password `pass`, and then use the database `db_name`. The database must exist, but the schema will be managed automatically by `lightningd`. - **bookkeeper-dir**=*DIR* + **bookkeeper-dir**=*DIR* [plugin `bookkeeper`] Directory to keep the accounts.sqlite3 database file in. Defaults to lightning-dir. - **bookkeeper-db**=*DSN* + **bookkeeper-db**=*DSN* [plugin `bookkeeper`] Identify the location of the bookkeeper data. This is a fully qualified data source name, including a scheme such as `sqlite3` or `postgres` followed by the connection parameters. From 1b5dc4409ae7d47b6e7d51d6224b24db58768af4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Jul 2022 17:04:41 +0930 Subject: [PATCH 1204/1530] doc: remove two more generated manpages. Somehow these two were missed in 79e09b92ef639d8a94a42f0e7359085899f09b89. Signed-off-by: Rusty Russell --- doc/lightning-listconfigs.7 | 291 ------------------------------------ doc/lightning-newaddr.7 | 59 -------- 2 files changed, 350 deletions(-) delete mode 100644 doc/lightning-listconfigs.7 delete mode 100644 doc/lightning-newaddr.7 diff --git a/doc/lightning-listconfigs.7 b/doc/lightning-listconfigs.7 deleted file mode 100644 index 068fb615e954..000000000000 --- a/doc/lightning-listconfigs.7 +++ /dev/null @@ -1,291 +0,0 @@ -.TH "LIGHTNING-LISTCONFIGS" "7" "" "" "lightning-listconfigs" -.SH NAME -lightning-listconfigs - Command to list all configuration options\. -.SH SYNOPSIS - -\fBlistconfigs\fR [\fIconfig\fR] - -.SH DESCRIPTION - -\fIconfig\fR (optional) is a configuration option name, or "plugin" to show plugin options - - -The \fBlistconfigs\fR RPC command to list all configuration options, or with \fIconfig\fR only a selection\. - - -The returned values reflect the current configuration, including -showing default values (\fBdev-\fR options are not shown)\. - -.SH EXAMPLE JSON REQUEST -.nf -.RS -{ - "id": 82, - "method": "listconfigs", - "params": { - "config": "network" - } -} -.RE - -.fi -.SH RETURN VALUE - -On success, an object is returned, containing: - -.RS -.IP \[bu] -\fB# version\fR (string, optional): Special field indicating the current version -.IP \[bu] -\fBplugins\fR (array of objects, optional): -.RS -.IP \[bu] -\fBpath\fR (string): Full path of the plugin -.IP \[bu] -\fBname\fR (string): short name of the plugin -.IP \[bu] -\fBoptions\fR (object, optional): Specific options set for this plugin: - -.RE - -.IP \[bu] -\fBimportant-plugins\fR (array of objects, optional): -.RS -.IP \[bu] -\fBpath\fR (string): Full path of the plugin -.IP \[bu] -\fBname\fR (string): short name of the plugin -.IP \[bu] -\fBoptions\fR (object, optional): Specific options set for this plugin: - -.RE - -.IP \[bu] -\fBconf\fR (string, optional): \fBconf\fR field from cmdline, or default -.IP \[bu] -\fBlightning-dir\fR (string, optional): \fBlightning-dir\fR field from config or cmdline, or default -.IP \[bu] -\fBnetwork\fR (string, optional): \fBnetwork\fR field from config or cmdline, or default -.IP \[bu] -\fBallow-deprecated-apis\fR (boolean, optional): \fBallow-deprecated-apis\fR field from config or cmdline, or default -.IP \[bu] -\fBrpc-file\fR (string, optional): \fBrpc-file\fR field from config or cmdline, or default -.IP \[bu] -\fBdisable-plugin\fR (array of strings, optional): -.RS -.IP \[bu] -\fBdisable-plugin\fR field from config or cmdline - -.RE - -.IP \[bu] -\fBbookkeeper-dir\fR (string, optional): \fBbookkeeper-dir\fR field from config or cmdline, or default -.IP \[bu] -\fBbookkeeper-db\fR (string, optional): \fBbookkeeper-db\fR field from config or cmdline, or default -.IP \[bu] -\fBalways-use-proxy\fR (boolean, optional): \fBalways-use-proxy\fR field from config or cmdline, or default -.IP \[bu] -\fBdaemon\fR (boolean, optional): \fBdaemon\fR field from config or cmdline, or default -.IP \[bu] -\fBwallet\fR (string, optional): \fBwallet\fR field from config or cmdline, or default -.IP \[bu] -\fBlarge-channels\fR (boolean, optional): \fBlarge-channels\fR field from config or cmdline, or default -.IP \[bu] -\fBexperimental-dual-fund\fR (boolean, optional): \fBexperimental-dual-fund\fR field from config or cmdline, or default -.IP \[bu] -\fBexperimental-onion-messages\fR (boolean, optional): \fBexperimental-onion-messages\fR field from config or cmdline, or default -.IP \[bu] -\fBexperimental-offers\fR (boolean, optional): \fBexperimental-offers\fR field from config or cmdline, or default -.IP \[bu] -\fBexperimental-shutdown-wrong-funding\fR (boolean, optional): \fBexperimental-shutdown-wrong-funding\fR field from config or cmdline, or default -.IP \[bu] -\fBexperimental-websocket-port\fR (u16, optional): \fBexperimental-websocket-port\fR field from config or cmdline, or default -.IP \[bu] -\fBrgb\fR (hex, optional): \fBrgb\fR field from config or cmdline, or default (always 6 characters) -.IP \[bu] -\fBalias\fR (string, optional): \fBalias\fR field from config or cmdline, or default -.IP \[bu] -\fBpid-file\fR (string, optional): \fBpid-file\fR field from config or cmdline, or default -.IP \[bu] -\fBignore-fee-limits\fR (boolean, optional): \fBignore-fee-limits\fR field from config or cmdline, or default -.IP \[bu] -\fBwatchtime-blocks\fR (u32, optional): \fBwatchtime-blocks\fR field from config or cmdline, or default -.IP \[bu] -\fBmax-locktime-blocks\fR (u32, optional): \fBmax-locktime-blocks\fR field from config or cmdline, or default -.IP \[bu] -\fBfunding-confirms\fR (u32, optional): \fBfunding-confirms\fR field from config or cmdline, or default -.IP \[bu] -\fBcltv-delta\fR (u32, optional): \fBcltv-delta\fR field from config or cmdline, or default -.IP \[bu] -\fBcltv-final\fR (u32, optional): \fBcltv-final\fR field from config or cmdline, or default -.IP \[bu] -\fBcommit-time\fR (u32, optional): \fBcommit-time\fR field from config or cmdline, or default -.IP \[bu] -\fBfee-base\fR (u32, optional): \fBfee-base\fR field from config or cmdline, or default -.IP \[bu] -\fBrescan\fR (integer, optional): \fBrescan\fR field from config or cmdline, or default -.IP \[bu] -\fBfee-per-satoshi\fR (u32, optional): \fBfee-per-satoshi\fR field from config or cmdline, or default -.IP \[bu] -\fBmax-concurrent-htlcs\fR (u32, optional): \fBmax-concurrent-htlcs\fR field from config or cmdline, or default -.IP \[bu] -\fBhtlc-minimum-msat\fR (msat, optional): \fBhtlc-minimum-msat\fR field from config or cmdline, or default -.IP \[bu] -\fBhtlc-maximum-msat\fR (msat, optional): \fBhtlc-maximum-msat\fR field from config or cmdline, or default -.IP \[bu] -\fBmax-dust-htlc-exposure-msat\fR (msat, optional): \fBmax-dust-htlc-exposure-mast\fR field from config or cmdline, or default -.IP \[bu] -\fBmin-capacity-sat\fR (u64, optional): \fBmin-capacity-sat\fR field from config or cmdline, or default -.IP \[bu] -\fBaddr\fR (string, optional): \fBaddr\fR field from config or cmdline (can be more than one) -.IP \[bu] -\fBannounce-addr\fR (string, optional): \fBannounce-addr\fR field from config or cmdline (can be more than one) -.IP \[bu] -\fBbind-addr\fR (string, optional): \fBbind-addr\fR field from config or cmdline (can be more than one) -.IP \[bu] -\fBoffline\fR (boolean, optional): \fBtrue\fR if \fBoffline\fR was set in config or cmdline -.IP \[bu] -\fBautolisten\fR (boolean, optional): \fBautolisten\fR field from config or cmdline, or default -.IP \[bu] -\fBproxy\fR (string, optional): \fBproxy\fR field from config or cmdline, or default -.IP \[bu] -\fBdisable-dns\fR (boolean, optional): \fBtrue\fR if \fBdisable-dns\fR was set in config or cmdline -.IP \[bu] -\fBdisable-ip-discovery\fR (boolean, optional): \fBtrue\fR if \fBdisable-ip-discovery\fR was set in config or cmdline -.IP \[bu] -\fBencrypted-hsm\fR (boolean, optional): \fBtrue\fR if \fBencrypted-hsm\fR was set in config or cmdline -.IP \[bu] -\fBrpc-file-mode\fR (string, optional): \fBrpc-file-mode\fR field from config or cmdline, or default -.IP \[bu] -\fBlog-level\fR (string, optional): \fBlog-level\fR field from config or cmdline, or default -.IP \[bu] -\fBlog-prefix\fR (string, optional): \fBlog-prefix\fR field from config or cmdline, or default -.IP \[bu] -\fBlog-file\fR (string, optional): \fBlog-file\fR field from config or cmdline, or default -.IP \[bu] -\fBlog-timestamps\fR (boolean, optional): \fBlog-timestamps\fR field from config or cmdline, or default -.IP \[bu] -\fBforce-feerates\fR (string, optional): force-feerate configuration setting, if any -.IP \[bu] -\fBsubdaemon\fR (string, optional): \fBsubdaemon\fR fields from config or cmdline if any (can be more than one) -.IP \[bu] -\fBfetchinvoice-noconnect\fR (boolean, optional): \fBfeatchinvoice-noconnect\fR fileds from config or cmdline, or default -.IP \[bu] -\fBtor-service-password\fR (string, optional): \fBtor-service-password\fR field from config or cmdline, if any - -.RE - -On failure, one of the following error codes may be returned: - -.RS -.IP \[bu] --32602: Error in given parameters or field with \fIconfig\fR name doesn't exist\. - -.RE -.SH EXAMPLE JSON RESPONSE -.nf -.RS -{ - "# version": "v0.9.0-1", - "lightning-dir": "/media/vincent/Maxtor/sanboxTestWrapperRPC/lightning_dir_dev", - "network": "testnet", - "allow-deprecated-apis": true, - "rpc-file": "lightning-rpc", - "plugins": [ - { - "path": "/home/vincent/Github/plugins/sauron/sauron.py", - "name": "sauron.py", - "options": { - "sauron-api-endpoint": "http://blockstream.info/testnet/api/", - "sauron-tor-proxy": "" - } - }, - { - "path": "/home/vincent/Github/reckless/reckless.py", - "name": "reckless.py" - } - ], - "important-plugins": [ - { - "path": "/home/vincent/Github/lightning/lightningd/../plugins/autoclean", - "name": "autoclean", - "options": { - "autocleaninvoice-cycle": null, - "autocleaninvoice-expired-by": null - } - }, - { - "path": "/home/vincent/Github/lightning/lightningd/../plugins/fundchannel", - "name": "fundchannel" - }, - { - "path": "/home/vincent/Github/lightning/lightningd/../plugins/keysend", - "name": "keysend" - }, - { - "path": "/home/vincent/Github/lightning/lightningd/../plugins/pay", - "name": "pay", - "options": { - "disable-mpp": false - } - } - ], - "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/autoclean", - "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/fundchannel", - "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/keysend", - "important-plugin": "/home/vincent/Github/lightning/lightningd/../plugins/pay", - "plugin": "/home/vincent/Github/plugins/sauron/sauron.py", - "plugin": "/home/vincent/Github/reckless/reckless.py", - "disable-plugin": [ - "bcli" - ], - "always-use-proxy": false, - "daemon": "false", - "wallet": "sqlite3:///media/vincent/Maxtor/sanboxTestWrapperRPC/lightning_dir_dev/testnet/lightningd.sqlite3", - "wumbo": false, - "wumbo": false, - "rgb": "03ad98", - "alias": "BRUCEWAYN-TES-DEV", - "pid-file": "/media/vincent/Maxtor/sanboxTestWrapperRPC/lightning_dir_dev/lightningd-testne...", - "ignore-fee-limits": true, - "watchtime-blocks": 6, - "max-locktime-blocks": 2016, - "funding-confirms": 1, - "commit-fee-min": 0, - "commit-fee-max": 0, - "cltv-delta": 6, - "cltv-final": 10, - "commit-time": 10, - "fee-base": 1, - "rescan": 30, - "fee-per-satoshi": 10, - "max-concurrent-htlcs": 483, - "min-capacity-sat": 10000, - "addr": "autotor:127.0.0.1:9051", - "bind-addr": "127.0.0.1:9735", - "announce-addr": "fp463inc4w3lamhhduytrwdwq6q6uzugtaeapylqfc43agrdnnqsheyd.onion:9735", - "offline": "false", - "autolisten": true, - "proxy": "127.0.0.1:9050", - "disable-dns": "false", - "encrypted-hsm": false, - "rpc-file-mode": "0600", - "log-level": "DEBUG", - "log-prefix": "lightningd", -} -.RE - -.fi -.SH AUTHOR - -Vincenzo Palazzo \fI wrote the initial version of this man page, but many others did the hard work of actually implementing this rpc command\. - -.SH SEE ALSO - -\fBlightning-getinfo\fR(7), \fBlightningd-config\fR(5) - -.SH RESOURCES - -Main web site: \fIhttps://github.com/ElementsProject/lightning\fR - -\" SHA256STAMP:acf490bbc9c764f67bf72de7383ea05422ce892dbf70de5ba8918fad15900bd8 diff --git a/doc/lightning-newaddr.7 b/doc/lightning-newaddr.7 deleted file mode 100644 index 2d76029e774b..000000000000 --- a/doc/lightning-newaddr.7 +++ /dev/null @@ -1,59 +0,0 @@ -.TH "LIGHTNING-NEWADDR" "7" "" "" "lightning-newaddr" -.SH NAME -lightning-newaddr - Command for generating a new address to be used by Core Lightning -.SH SYNOPSIS - -\fBnewaddr\fR [ \fIaddresstype\fR ] - -.SH DESCRIPTION - -The \fBnewaddr\fR RPC command generates a new address which can -subsequently be used to fund channels managed by the Core Lightning node\. - - -The funding transaction needs to be confirmed before funds can be used\. - - -\fIaddresstype\fR specifies the type of address wanted; i\.e\. \fIp2sh-segwit\fR -(e\.g\. \fB2MxaozoqWwiUcuD9KKgUSrLFDafLqimT9Ta\fR on bitcoin testnet or -\fB3MZxzq3jBSKNQ2e7dzneo9hy4FvNzmMmt3\fR on bitcoin mainnet) or \fIbech32\fR -(e\.g\. \fBtb1qu9j4lg5f9rgjyfhvfd905vw46eg39czmktxqgg\fR on bitcoin testnet -or \fBbc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej\fR on -bitcoin mainnet)\. The special value \fIall\fR generates both address types -for the same underlying key\. - - -If no \fIaddresstype\fR is specified the address generated is a \fIbech32\fR address\. - - -To send an on-chain payment \fIfrom\fR the Core Lightning node wallet, use \fBwithdraw\fR\. - -.SH RETURN VALUE - -On success, an object is returned, containing: - -.RS -.IP \[bu] -\fBbech32\fR (string, optional): The bech32 (native segwit) address -.IP \[bu] -\fBp2sh-segwit\fR (string, optional): The p2sh-wrapped address - -.RE -.SH ERRORS - -If an unrecognized address type is requested an error message will be -returned\. - -.SH AUTHOR - -Felix \fI is mainly responsible\. - -.SH SEE ALSO - -\fBlightning-listfunds\fR(7), \fBlightning-fundchannel\fR(7), \fBlightning-withdraw\fR(7), \fBlightning-listtransactions\fR(7) - -.SH RESOURCES - -Main web site: \fIhttps://github.com/ElementsProject/lightning\fR - -\" SHA256STAMP:62fcbc384a244851f34f9d30a7e5ae79c767fc6bca96b818ec8e11619afc397d From 30aa1d79fb48a2f28c04e6af191e31b6fd686165 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:35:56 -0500 Subject: [PATCH 1205/1530] bkpr: for zerconfs, we still wanna know you're opening a channel We need a record of the channel account before you start sending payments through it. Normally we don't start allowing payments to be sent until after the channel has locked in but zeroconf does away with this assumption. Instead we push out a "channel_proposed" event, which should only show up for zeroconfs. --- common/coin_mvt.c | 28 ++++++++++++++++++++ common/coin_mvt.h | 13 ++++++++- lightningd/channel_control.c | 48 ++++++++++++++++++++++------------ lightningd/channel_control.h | 2 +- lightningd/dual_open_control.c | 3 ++- lightningd/onchain_control.c | 5 ++-- plugins/bkpr/recorder.c | 1 + tests/test_opening.py | 20 +++++++++++--- 8 files changed, 94 insertions(+), 26 deletions(-) diff --git a/common/coin_mvt.c b/common/coin_mvt.c index bb2caa14fff4..4b087a73a190 100644 --- a/common/coin_mvt.c +++ b/common/coin_mvt.c @@ -40,6 +40,7 @@ static const char *mvt_tags[] = { "lease_fee", "leased", "stealable", + "channel_proposed", }; const char *mvt_tag_str(enum mvt_tag tag) @@ -196,6 +197,33 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, output_count); } +struct chain_coin_mvt *new_coin_channel_open_proposed(const tal_t *ctx, + const struct channel_id *chan_id, + const struct bitcoin_outpoint *out, + const struct node_id *peer_id, + const struct amount_msat amount, + const struct amount_sat output_val, + bool is_opener, + bool is_leased) +{ + struct chain_coin_mvt *mvt; + + mvt = new_chain_coin_mvt(ctx, NULL, NULL, out, NULL, 0, + take(new_tag_arr(NULL, CHANNEL_PROPOSED)), + amount, true, output_val, 0); + mvt->account_name = type_to_string(mvt, struct channel_id, chan_id); + mvt->peer_id = tal_dup(mvt, struct node_id, peer_id); + + /* If we're the opener, add to the tag list */ + if (is_opener) + tal_arr_expand(&mvt->tags, OPENER); + + if (is_leased) + tal_arr_expand(&mvt->tags, LEASED); + + return mvt; +} + struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct channel_id *chan_id, const struct bitcoin_outpoint *out, diff --git a/common/coin_mvt.h b/common/coin_mvt.h index 1f75641cb6bf..d35340073cff 100644 --- a/common/coin_mvt.h +++ b/common/coin_mvt.h @@ -14,7 +14,7 @@ enum mvt_type { CHANNEL_MVT = 1, }; -#define NUM_MVT_TAGS (STEALABLE + 1) +#define NUM_MVT_TAGS (CHANNEL_PROPOSED + 1) enum mvt_tag { DEPOSIT = 0, WITHDRAWAL = 1, @@ -39,6 +39,7 @@ enum mvt_tag { LEASE_FEE = 20, LEASED = 21, STEALABLE = 22, + CHANNEL_PROPOSED = 23, }; struct channel_coin_mvt { @@ -188,6 +189,16 @@ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx, u32 output_count) NON_NULL_ARGS(2, 3); +struct chain_coin_mvt *new_coin_channel_open_proposed(const tal_t *ctx, + const struct channel_id *chan_id, + const struct bitcoin_outpoint *out, + const struct node_id *peer_id, + const struct amount_msat amount, + const struct amount_sat output_val, + bool is_opener, + bool is_leased) + NON_NULL_ARGS(2, 3); + struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx, const struct channel_id *chan_id, const struct bitcoin_outpoint *out, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 67a56b853d84..8d2cbb24457e 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -127,7 +127,7 @@ void notify_feerate_change(struct lightningd *ld) * peer. We *could* do so, however. */ } -void channel_record_open(struct channel *channel, u32 blockheight) +void channel_record_open(struct channel *channel, u32 blockheight, bool record_push) { struct chain_coin_mvt *mvt; struct amount_msat start_balance; @@ -153,20 +153,31 @@ void channel_record_open(struct channel *channel, u32 blockheight) &channel->push)); } - mvt = new_coin_channel_open(tmpctx, - &channel->cid, - &channel->funding, - &channel->peer->id, - blockheight, - start_balance, - channel->funding_sats, - channel->opener == LOCAL, - is_leased); + /* If it's not in a block yet, send a proposal */ + if (blockheight > 0) + mvt = new_coin_channel_open(tmpctx, + &channel->cid, + &channel->funding, + &channel->peer->id, + blockheight, + start_balance, + channel->funding_sats, + channel->opener == LOCAL, + is_leased); + else + mvt = new_coin_channel_open_proposed(tmpctx, + &channel->cid, + &channel->funding, + &channel->peer->id, + start_balance, + channel->funding_sats, + channel->opener == LOCAL, + is_leased); notify_chain_mvt(channel->peer->ld, mvt); /* If we pushed sats, *now* record them */ - if (is_pushed) + if (is_pushed && record_push) notify_channel_mvt(channel->peer->ld, new_coin_channel_push(tmpctx, &channel->cid, channel->push, @@ -206,10 +217,12 @@ static void lockin_complete(struct channel *channel) try_update_blockheight(channel->peer->ld, channel, get_block_height(channel->peer->ld->topology)); - /* Only record this once we get a real confirmation. */ - if (channel->scid) - channel_record_open(channel, - short_channel_id_blocknum(channel->scid)); + /* Emit an event for the channel open (or channel proposal if blockheight + * is zero) */ + channel_record_open(channel, + channel->scid ? + short_channel_id_blocknum(channel->scid) : 0, + true); } bool channel_on_funding_locked(struct channel *channel, @@ -867,9 +880,10 @@ bool channel_tell_depth(struct lightningd *ld, channel->peer->ld, channel, get_block_height(channel->peer->ld->topology)); - /* Only record this once we get a real confirmation. */ + /* Emit channel_open event */ channel_record_open(channel, - short_channel_id_blocknum(channel->scid)); + short_channel_id_blocknum(channel->scid), + false); } return true; } diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index 420754706126..9008bed1d055 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -35,7 +35,7 @@ bool channel_on_funding_locked(struct channel *channel, struct pubkey *next_per_commitment_point); /* Record channel open (coin movement notifications) */ -void channel_record_open(struct channel *channel, u32 blockheight); +void channel_record_open(struct channel *channel, u32 blockheight, bool record_push); /* A channel has unrecoverably fallen behind */ void channel_fallen_behind(struct channel *channel, const u8 *msg); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index fba6ff1a4cc2..71133b8fb699 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1750,7 +1750,8 @@ static void handle_channel_locked(struct subd *dualopend, REASON_UNKNOWN, "Lockin complete"); channel_record_open(channel, - short_channel_id_blocknum(channel->scid)); + short_channel_id_blocknum(channel->scid), + true); /* Empty out the inflights */ wallet_channel_clear_inflights(dualopend->ld->wallet, channel); diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 322487cd3812..27ceafe4ea23 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -627,11 +627,10 @@ enum watch_result onchaind_funding_spent(struct channel *channel, /* If we haven't posted the open event yet, post an open */ if (!channel->scid || !channel->remote_funding_locked) { u32 blkh; - /* Note that blockheight will be zero if it's not in chain - * yet */ + /* Blockheight will be zero if it's not in chain */ blkh = wallet_transaction_height(channel->peer->ld->wallet, &channel->funding.txid); - channel_record_open(channel, blkh); + channel_record_open(channel, blkh, true); } diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 9b1ab449f4da..2eb500c5e6a8 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -1219,6 +1219,7 @@ void maybe_update_account(struct db *db, for (size_t i = 0; i < tal_count(tags); i++) { switch (tags[i]) { + case CHANNEL_PROPOSED: case CHANNEL_OPEN: updated = true; acct->open_event_db_id = tal(acct, u64); diff --git a/tests/test_opening.py b/tests/test_opening.py index af632cdda2ce..f90a18a1bfc3 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -2,7 +2,7 @@ from fixtures import TEST_NETWORK from pyln.client import RpcError, Millisatoshi from utils import ( - only_one, wait_for, sync_blockheight, first_channel_id, calc_lease_fee + only_one, wait_for, sync_blockheight, first_channel_id, calc_lease_fee, check_coin_moves ) from pathlib import Path @@ -1312,7 +1312,7 @@ def test_zeroconf_open(bitcoind, node_factory): l2.rpc.pay(inv) -def test_zeroconf_public(bitcoind, node_factory): +def test_zeroconf_public(bitcoind, node_factory, chainparams): """Test that we transition correctly from zeroconf to public The differences being that a public channel MUST use the public @@ -1321,9 +1321,10 @@ def test_zeroconf_public(bitcoind, node_factory): """ plugin_path = Path(__file__).parent / "plugins" / "zeroconf-selective.py" + coin_mvt_plugin = Path(__file__).parent / "plugins" / "coin_movements.py" l1, l2, l3 = node_factory.get_nodes(3, opts=[ - {}, + {'plugin': str(coin_mvt_plugin)}, { 'plugin': str(plugin_path), 'zeroconf-allow': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518' @@ -1346,6 +1347,13 @@ def test_zeroconf_public(bitcoind, node_factory): assert('short_channel_id' not in l1chan) assert('short_channel_id' not in l2chan) + # Channel is "proposed" + chan_val = 993198000 if chainparams['elements'] else 995673000 + l1_mvts = [ + {'type': 'chain_mvt', 'credit_msat': chan_val, 'debit_msat': 0, 'tags': ['channel_proposed', 'opener']}, + ] + check_coin_moves(l1, l1chan['channel_id'], l1_mvts, chainparams) + # Now add 1 confirmation, we should get a `short_channel_id` bitcoind.generate_block(1) l1.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') @@ -1356,6 +1364,12 @@ def test_zeroconf_public(bitcoind, node_factory): assert('short_channel_id' in l1chan) assert('short_channel_id' in l2chan) + # We also now have an 'open' event + l1_mvts += [ + {'type': 'chain_mvt', 'credit_msat': chan_val, 'debit_msat': 0, 'tags': ['channel_open', 'opener']}, + ] + check_coin_moves(l1, l1chan['channel_id'], l1_mvts, chainparams) + # Now make it public, we should be switching over to the real # scid. bitcoind.generate_block(5) From e048292fdf0a50854da40079af8f01f1a89e8537 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 27 Jul 2022 16:18:05 -0500 Subject: [PATCH 1206/1530] bkpr-zeroconf: Zeroconfs will emit 'channel_proposed' event Keep the accounts as an 'append only' log, instead we move the marker for the 'channel_open' forward when a 'channel_open' comes out. We also neatly hide the 'channel_proposed' events in 'inspect' if there's a 'channel_open' for that same event. If you call inspect before the 'channel_open' is confirmed, you'll see the tag as 'channel_proposed', afterwards it shows up as 'channel_open'. However the event log rolls forward -- listaccountevents will show the correct history of the proposal then open confirming (plus any routing that happened before the channel confirmed). --- plugins/bkpr/recorder.c | 24 ++++++++++++----- plugins/bkpr/test/Makefile | 1 + plugins/bkpr/test/run-bkpr_db.c | 9 ++++--- plugins/bkpr/test/run-recorder.c | 9 ++++--- tests/test_opening.py | 44 +++++++++++++++++++++++++++++--- 5 files changed, 71 insertions(+), 16 deletions(-) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 2eb500c5e6a8..f2a0f4f4c777 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -225,7 +225,8 @@ static struct chain_event **find_txos_for_tx(const tal_t *ctx, " ORDER BY " " e.utxo_txid" ", e.outnum" - ", e.spending_txid NULLS FIRST")); + ", e.spending_txid NULLS FIRST" + ", e.blockheight")); db_bind_txid(stmt, 0, txid); return find_chain_events(ctx, take(stmt)); @@ -403,8 +404,14 @@ static struct txo_set *find_txo_set(const tal_t *ctx, } else { /* We might not have a spend event * for everything */ - if (pr) - tal_arr_expand(&txos->pairs, pr); + if (pr) { + /* Disappear "channel_proposed" events */ + if (streq(pr->txo->tag, + mvt_tag_str(CHANNEL_PROPOSED))) + pr = tal_free(pr); + else + tal_arr_expand(&txos->pairs, pr); + } pr = new_txo_pair(txos->pairs); pr->txo = tal_steal(pr, ev); } @@ -671,7 +678,8 @@ static struct chain_event *find_chain_event(const tal_t *ctx, struct db *db, const struct account *acct, const struct bitcoin_outpoint *outpoint, - const struct bitcoin_txid *spending_txid) + const struct bitcoin_txid *spending_txid, + const char *tag) { struct db_stmt *stmt; @@ -733,7 +741,10 @@ static struct chain_event *find_chain_event(const tal_t *ctx, " e.account_id = ?" " AND e.utxo_txid = ?" " AND e.outnum = ?" - " AND e.spending_txid IS NULL")); + " AND e.spending_txid IS NULL" + " AND e.tag = ?")); + + db_bind_text(stmt, 3, tag); } db_bind_u64(stmt, 0, acct->db_id); @@ -1850,7 +1861,8 @@ bool log_chain_event(struct db *db, /* We're responsible for de-duping chain events! */ if (find_chain_event(e, db, acct, - &e->outpoint, e->spending_txid)) + &e->outpoint, e->spending_txid, + e->tag)) return false; stmt = db_prepare_v2(db, SQL("INSERT INTO chain_events" diff --git a/plugins/bkpr/test/Makefile b/plugins/bkpr/test/Makefile index 642e35214b69..e6f16016ff3f 100644 --- a/plugins/bkpr/test/Makefile +++ b/plugins/bkpr/test/Makefile @@ -11,6 +11,7 @@ BOOKKEEPER_TEST_COMMON_OBJS := \ common/base32.o \ common/blockheight_states.o \ common/channel_type.o \ + common/coin_mvt.o \ common/features.o \ common/json_stream.o \ common/key_derive.o \ diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index cab5ff9fb9a0..198512849a5b 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -88,6 +88,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for fromwire_wirestring */ +char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_wirestring called!\n"); abort(); } /* Generated stub for htlc_state_flags */ int htlc_state_flags(enum htlc_state state UNNEEDED) { fprintf(stderr, "htlc_state_flags called!\n"); abort(); } @@ -177,9 +180,6 @@ enum htlc_state last_fee_state(enum side opener UNNEEDED) /* Generated stub for log_level_name */ const char *log_level_name(enum log_level level UNNEEDED) { fprintf(stderr, "log_level_name called!\n"); abort(); } -/* Generated stub for mvt_tag_str */ -const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) -{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } /* Generated stub for new_channel_event */ struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED, const char *tag UNNEEDED, @@ -225,6 +225,9 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) /* Generated stub for towire_u8_array */ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for towire_wirestring */ +void towire_wirestring(u8 **pptr UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "towire_wirestring called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static char *tmp_dsn(const tal_t *ctx) diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index b62cedb6176b..c71f040fe71b 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -94,6 +94,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for fromwire_wirestring */ +char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_wirestring called!\n"); abort(); } /* Generated stub for htlc_state_flags */ int htlc_state_flags(enum htlc_state state UNNEEDED) { fprintf(stderr, "htlc_state_flags called!\n"); abort(); } @@ -183,9 +186,6 @@ enum htlc_state last_fee_state(enum side opener UNNEEDED) /* Generated stub for log_level_name */ const char *log_level_name(enum log_level level UNNEEDED) { fprintf(stderr, "log_level_name called!\n"); abort(); } -/* Generated stub for mvt_tag_str */ -const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) -{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } /* Generated stub for new_channel_event */ struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED, const char *tag UNNEEDED, @@ -231,6 +231,9 @@ void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) /* Generated stub for towire_u8_array */ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for towire_wirestring */ +void towire_wirestring(u8 **pptr UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "towire_wirestring called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static char *tmp_dsn(const tal_t *ctx) diff --git a/tests/test_opening.py b/tests/test_opening.py index f90a18a1bfc3..96db12fc08c7 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1331,9 +1331,11 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): }, {} ]) + # Advances blockheight to 102 l1.fundwallet(10**6) + push_msat = 20000 * 1000 l1.connect(l2) - l1.rpc.fundchannel(l2.info['id'], 'all', mindepth=0) + l1.rpc.fundchannel(l2.info['id'], 'all', mindepth=0, push_msat=push_msat) # Wait for the update to be signed (might not be the most reliable # signal) @@ -1342,6 +1344,7 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): l1chan = l1.rpc.listpeers()['peers'][0]['channels'][0] l2chan = l2.rpc.listpeers()['peers'][0]['channels'][0] + channel_id = l1chan['channel_id'] # We have no confirmation yet, so no `short_channel_id` assert('short_channel_id' not in l1chan) @@ -1351,10 +1354,22 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): chan_val = 993198000 if chainparams['elements'] else 995673000 l1_mvts = [ {'type': 'chain_mvt', 'credit_msat': chan_val, 'debit_msat': 0, 'tags': ['channel_proposed', 'opener']}, + {'type': 'channel_mvt', 'credit_msat': 0, 'debit_msat': 20000000, 'tags': ['pushed'], 'fees_msat': '0msat'}, ] check_coin_moves(l1, l1chan['channel_id'], l1_mvts, chainparams) - # Now add 1 confirmation, we should get a `short_channel_id` + # Check that the channel_open event has blockheight of zero + for n in [l1, l2]: + evs = n.rpc.bkpr_listaccountevents(channel_id)['events'] + open_ev = only_one([e for e in evs if e['tag'] == 'channel_proposed']) + assert open_ev['blockheight'] == 0 + + # Call inspect, should have pending event in it + tx = only_one(n.rpc.bkpr_inspect(channel_id)['txs']) + assert 'blockheight' not in tx + assert only_one(tx['outputs'])['output_tag'] == 'channel_proposed' + + # Now add 1 confirmation, we should get a `short_channel_id` (block 103) bitcoind.generate_block(1) l1.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') l2.daemon.wait_for_log(r'Funding tx [a-f0-9]{64} depth 1 of 0') @@ -1364,11 +1379,24 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): assert('short_channel_id' in l1chan) assert('short_channel_id' in l2chan) - # We also now have an 'open' event + # We also now have an 'open' event, the push event isn't re-recorded l1_mvts += [ {'type': 'chain_mvt', 'credit_msat': chan_val, 'debit_msat': 0, 'tags': ['channel_open', 'opener']}, ] - check_coin_moves(l1, l1chan['channel_id'], l1_mvts, chainparams) + check_coin_moves(l1, channel_id, l1_mvts, chainparams) + + # Check that there is a channel_open event w/ real blockheight + for n in [l1, l2]: + evs = n.rpc.bkpr_listaccountevents(channel_id)['events'] + # Still has the channel-proposed event + only_one([e for e in evs if e['tag'] == 'channel_proposed']) + open_ev = only_one([e for e in evs if e['tag'] == 'channel_open']) + assert open_ev['blockheight'] == 103 + + # Call inspect, should have open event in it + tx = only_one(n.rpc.bkpr_inspect(channel_id)['txs']) + assert tx['blockheight'] == 103 + assert only_one(tx['outputs'])['output_tag'] == 'channel_open' # Now make it public, we should be switching over to the real # scid. @@ -1378,6 +1406,14 @@ def test_zeroconf_public(bitcoind, node_factory, chainparams): l3.connect(l1) wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 2) + # Close the zerconf channel, check that we mark it as onchain_resolved ok + l1.rpc.close(l2.info['id']) + bitcoind.generate_block(1, wait_for_mempool=1) + + # Channel should be marked resolved + for n in [l1, l2]: + wait_for(lambda: only_one([x for x in n.rpc.bkpr_listbalances()['accounts'] if x['account'] == channel_id])['account_resolved']) + def test_zeroconf_forward(node_factory, bitcoind): """Ensure that we can use zeroconf channels in forwards. From 2f72bbbbc512a1b88e8eef1948dfb3b8004a0338 Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 22 Jul 2022 19:04:37 -0500 Subject: [PATCH 1207/1530] nit: send_outreq returns &pending, no need to call sep command --- plugins/bkpr/bookkeeper.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index ef7f25c12159..999cbe349059 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1077,8 +1077,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, listpeers_multi_done, log_error, new_accts); - send_outreq(cmd->plugin, req); - return command_still_pending(cmd); + return send_outreq(cmd->plugin, req); } plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); @@ -1251,8 +1250,7 @@ static struct command_result *lookup_invoice_desc(struct command *cmd, payment_hash); json_add_sha256(req->js, "payment_hash", payment_hash); - send_outreq(cmd->plugin, req); - return command_still_pending(cmd); + return send_outreq(cmd->plugin, req); } struct event_info { @@ -1520,8 +1518,7 @@ parse_and_log_chain_move(struct command *cmd, log_error, info); /* FIXME: use the peer_id to reduce work here */ - send_outreq(cmd->plugin, req); - return command_still_pending(cmd); + return send_outreq(cmd->plugin, req); } /* Maybe mark acct as onchain resolved */ From c83c1521ced54a5a7c72ab0033576c45fc234d18 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 25 Jul 2022 16:44:54 -0500 Subject: [PATCH 1208/1530] memleak: throw away things when we're done with them memleak was complaining about dangling refs; they were all allocated off of `cmd` but not technically unreachabe from any in-context memory. Instead we move things over to tmpctx to clean up (in the paths where we're going to move the objects out of reach via intermediate RPC call) ALSO: cleans up unused param in `lookup_invoice_desc` lightningd-1 2022-07-23T19:17:11.192Z **BROKEN** plugin-bookkeeper: MEMLEAK: 0x1511f68 lightningd-1 2022-07-23T19:17:11.194Z **BROKEN** plugin-bookkeeper: label=plugins/bkpr/recorder.c:1048:struct account lightningd-1 2022-07-23T19:17:11.194Z **BROKEN** plugin-bookkeeper: backtrace: lightningd-1 2022-07-23T19:17:11.194Z **BROKEN** plugin-bookkeeper: ccan/ccan/tal/tal.c:442 (tal_alloc_) lightningd-1 2022-07-23T19:17:11.194Z **BROKEN** plugin-bookkeeper: plugins/bkpr/recorder.c:1048 (stmt2account) lightningd-1 2022-07-23T19:17:11.194Z **BROKEN** plugin-bookkeeper: plugins/bkpr/recorder.c:1109 (find_account) lightningd-1 2022-07-23T19:17:11.196Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1606 (parse_and_log_channel_move) lightningd-1 2022-07-23T19:17:11.197Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1713 (json_coin_moved) lightningd-1 2022-07-23T19:17:11.198Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1421 (ld_command_handle) lightningd-1 2022-07-23T19:17:11.198Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1491 (ld_read_json_one) lightningd-1 2022-07-23T19:17:11.199Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1511 (ld_read_json) lightningd-1 2022-07-23T19:17:11.199Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:59 (next_plan) lightningd-1 2022-07-23T19:17:11.199Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:407 (do_plan) lightningd-1 2022-07-23T19:17:11.199Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:417 (io_ready) lightningd-1 2022-07-23T19:17:11.199Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/poll.c:453 (io_loop) lightningd-1 2022-07-23T19:17:11.199Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1708 (plugin_main) lightningd-1 2022-07-23T19:17:11.199Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1812 (main) lightningd-1 2022-07-23T19:17:11.200Z **BROKEN** plugin-bookkeeper: ../csu/libc-start.c:308 (__libc_start_main) lightningd-1 2022-07-23T19:17:11.200Z **BROKEN** plugin-bookkeeper: parents: lightningd-1 2022-07-23T19:17:11.200Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1378:struct command lightningd-1 2022-07-23T19:17:11.200Z **BROKEN** plugin-bookkeeper: MEMLEAK: 0x15305b8 lightningd-1 2022-07-23T19:17:11.200Z **BROKEN** plugin-bookkeeper: label=plugins/bkpr/bookkeeper.c:1643:enum mvt_tag[] lightningd-1 2022-07-23T19:17:11.200Z **BROKEN** plugin-bookkeeper: backtrace: lightningd-1 2022-07-23T19:17:11.200Z **BROKEN** plugin-bookkeeper: ccan/ccan/tal/tal.c:442 (tal_alloc_) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: ccan/ccan/tal/tal.c:471 (tal_alloc_arr_) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1643 (parse_tags) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1686 (json_coin_moved) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1421 (ld_command_handle) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1491 (ld_read_json_one) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1511 (ld_read_json) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:59 (next_plan) lightningd-1 2022-07-23T19:17:11.201Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:407 (do_plan) lightningd-1 2022-07-23T19:17:11.202Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:417 (io_ready) lightningd-1 2022-07-23T19:17:11.202Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/poll.c:453 (io_loop) lightningd-1 2022-07-23T19:17:11.202Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1708 (plugin_main) lightningd-1 2022-07-23T19:17:11.203Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1812 (main) lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: ../csu/libc-start.c:308 (__libc_start_main) lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: parents: lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1378:struct command lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: MEMLEAK: 0x1508da8 lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: label=plugins/bkpr/bookkeeper.c:1568:struct channel_event lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: backtrace: lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: ccan/ccan/tal/tal.c:442 (tal_alloc_) lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1568 (parse_and_log_channel_move) lightningd-1 2022-07-23T19:17:11.204Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1713 (json_coin_moved) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1421 (ld_command_handle) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1491 (ld_read_json_one) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1511 (ld_read_json) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:59 (next_plan) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:407 (do_plan) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/io.c:417 (io_ready) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: ccan/ccan/io/poll.c:453 (io_loop) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1708 (plugin_main) lightningd-1 2022-07-23T19:17:11.205Z **BROKEN** plugin-bookkeeper: plugins/bkpr/bookkeeper.c:1812 (main) lightningd-1 2022-07-23T19:17:11.206Z **BROKEN** plugin-bookkeeper: ../csu/libc-start.c:308 (__libc_start_main) lightningd-1 2022-07-23T19:17:11.206Z **BROKEN** plugin-bookkeeper: parents: lightningd-1 2022-07-23T19:17:11.206Z **BROKEN** plugin-bookkeeper: plugins/libplugin.c:1378:struct command --- plugins/bkpr/bookkeeper.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 999cbe349059..ed0d4f80f56f 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1231,11 +1231,12 @@ listsendpays_done(struct command *cmd, const char *buf, static struct command_result *lookup_invoice_desc(struct command *cmd, struct amount_msat credit, - struct amount_msat debit, - struct sha256 *payment_hash) + struct sha256 *payment_hash STEALS) { struct out_req *req; + /* Otherwise will go away when event is cleaned up */ + notleak(tal_steal(cmd, payment_hash)); if (!amount_msat_zero(credit)) req = jsonrpc_request_start(cmd->plugin, cmd, "listinvoices", @@ -1265,8 +1266,6 @@ listpeers_done(struct command *cmd, const char *buf, struct acct_balance **balances, *bal; struct amount_msat credit_diff, debit_diff; const char *err; - /* Make sure to clean up when we're done */ - tal_steal(cmd, info); if (new_missed_channel_account(cmd, buf, result, info->acct, @@ -1314,10 +1313,12 @@ listpeers_done(struct command *cmd, const char *buf, plugin_err(cmd->plugin, err); if (info->ev->payment_id && - streq(info->ev->tag, mvt_tag_str(INVOICE))) + streq(info->ev->tag, mvt_tag_str(INVOICE))) { + /* Make memleak happy */ + tal_steal(tmpctx, info); return lookup_invoice_desc(cmd, info->ev->credit, - info->ev->debit, info->ev->payment_id); + } return notification_handled(cmd); } @@ -1435,20 +1436,20 @@ parse_and_log_chain_move(struct command *cmd, } db_begin_transaction(db); - acct = find_account(cmd, db, acct_name); + acct = find_account(tmpctx, db, acct_name); if (!acct) { /* FIXME: lookup the peer id for this channel! */ - acct = new_account(cmd, acct_name, NULL); + acct = new_account(tmpctx, acct_name, NULL); account_add(db, acct); } if (e->origin_acct) { - orig_acct = find_account(cmd, db, e->origin_acct); + orig_acct = find_account(tmpctx, db, e->origin_acct); /* Go fetch the originating account * (we might not have it) */ if (!orig_acct) { - orig_acct = new_account(cmd, e->origin_acct, NULL); + orig_acct = new_account(tmpctx, e->origin_acct, NULL); account_add(db, orig_acct); } } else @@ -1506,7 +1507,7 @@ parse_and_log_chain_move(struct command *cmd, " Calling `listpeers` to fetch missing info", acct->name); - info = tal(NULL, struct event_info); + info = tal(cmd, struct event_info); info->ev = tal_steal(info, e); info->acct = tal_steal(info, is_channel_account(acct) ? @@ -1532,8 +1533,9 @@ parse_and_log_chain_move(struct command *cmd, if (tags[i] != INVOICE) continue; + /* Keep memleak happy */ + tal_steal(tmpctx, e); return lookup_invoice_desc(cmd, e->credit, - e->debit, e->payment_id); } } @@ -1591,7 +1593,7 @@ parse_and_log_channel_move(struct command *cmd, /* Go find the account for this event */ db_begin_transaction(db); - acct = find_account(cmd, db, acct_name); + acct = find_account(tmpctx, db, acct_name); if (!acct) plugin_err(cmd->plugin, "Received channel event," @@ -1607,8 +1609,9 @@ parse_and_log_channel_move(struct command *cmd, if (tags[i] != INVOICE) continue; + /* Keep memleak happy */ + tal_steal(tmpctx, e); return lookup_invoice_desc(cmd, e->credit, - e->debit, e->payment_id); } } @@ -1671,7 +1674,7 @@ static struct command_result *json_coin_moved(struct command *cmd, err, json_tok_full_len(params), json_tok_full(buf, params)); - err = parse_tags(cmd, buf, + err = parse_tags(tmpctx, buf, json_get_member(buf, params, "coin_movement"), &tags); if (err) From 6e9af1ef3ec51f93417e375b2f52144f3c5e78cb Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 27 Jul 2022 19:34:59 -0500 Subject: [PATCH 1209/1530] bkpr: cleanup csv_safe_str --- ccan/ccan/tal/tal.c | 2 +- plugins/bkpr/incomestmt.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ccan/ccan/tal/tal.c b/ccan/ccan/tal/tal.c index 061fc2907aff..e0ca30d72c32 100644 --- a/ccan/ccan/tal/tal.c +++ b/ccan/ccan/tal/tal.c @@ -777,7 +777,7 @@ void *tal_dup_(const tal_t *ctx, const void *p, size_t size, (void)taken(p); return NULL; } - + if (!adjust_size(&nbytes, n)) { if (taken(p)) tal_free(p); diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index f20880249778..ff65fec5544b 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -115,13 +115,13 @@ static char *csv_safe_str(const tal_t *ctx, char *input TAKES) char *dupe; /* Update the double-quotes in place */ - dupe = tal_strdup(ctx, input); + dupe = tal_strdup(tmpctx, input); for (size_t i = 0; dupe[i] != '\0'; i++) { if (dupe[i] == '"') dupe[i] = '\''; } - esc = json_escape(ctx, dupe); + esc = json_escape(tmpctx, take(dupe)); return tal_fmt(ctx, "\"%s\"", esc->s); } From a2596989062b32dbc1cfbeeb05db20a2c2a612f9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 27 Jul 2022 19:32:04 +0930 Subject: [PATCH 1210/1530] pytest: test that we don't try to reconnect in AWAITING_UNILATERAL. We would just send an error, and it's annoying. Signed-off-by: Rusty Russell --- tests/test_connection.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_connection.py b/tests/test_connection.py index af0421142773..ebfb00133d05 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4136,3 +4136,20 @@ def send_many_payments(): wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected']) inv = l2.rpc.invoice(100000000, "invoice4", "invoice4") l1.rpc.pay(inv['bolt11']) + + +@pytest.mark.xfail(strict=True, reason="Still connects when AWAITING_UNILATERAL") +def test_no_reconnect_awating_unilateral(node_factory, bitcoind): + l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True}) + l2.stop() + + # Close immediately. + l1.rpc.close(l2.info['id'], 1) + + wait_for(lambda: only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['state'] == 'AWAITING_UNILATERAL') + + # After switching to AWAITING_UNILATERAL it will *not* try to reconnect. + l1.daemon.wait_for_log("State changed from CHANNELD_SHUTTING_DOWN to AWAITING_UNILATERAL") + time.sleep(10) + + assert not l1.daemon.is_in_log('Will try reconnect', start=l1.daemon.logsearch_start) From 9cad7d6a6a71f13cc7665c295da3c57747ae51c9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 27 Jul 2022 19:32:32 +0930 Subject: [PATCH 1211/1530] lightningd: don't consider AWAITING_UNILATERAL to be "active". It's not active: we don't want to connect. Signed-off-by: Rusty Russell --- lightningd/channel.h | 1 + tests/test_connection.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lightningd/channel.h b/lightningd/channel.h index a005f80178a0..4e7f85d46156 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -467,6 +467,7 @@ static inline bool channel_active(const struct channel *channel) { return channel->state != FUNDING_SPEND_SEEN && channel->state != CLOSINGD_COMPLETE + && channel->state != AWAITING_UNILATERAL && !channel_unsaved(channel) && !channel_on_chain(channel); } diff --git a/tests/test_connection.py b/tests/test_connection.py index ebfb00133d05..7c4548aea222 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4138,7 +4138,6 @@ def send_many_payments(): l1.rpc.pay(inv['bolt11']) -@pytest.mark.xfail(strict=True, reason="Still connects when AWAITING_UNILATERAL") def test_no_reconnect_awating_unilateral(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True}) l2.stop() From 22ff007d642d6b716fbbe6c2b210878775a9d760 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 28 Jul 2022 11:00:36 +0930 Subject: [PATCH 1212/1530] connectd: control connect backoff from lightningd. We used to tell connectd to remember our connect delay, and hand it back (increased if necessary). Instead, simply record when we last tried to connect. If it was less than 10 minutes ago, double delay (up to 5 minutes max), otherwise reset delay to 1 second. This covers all scenarios: whether we reconnect then immediately disconnect, or never successfully connect, it doesn't matter. Signed-off-by: Rusty Russell Fixes: #5453 --- connectd/connectd.c | 38 +++-------------- connectd/connectd_wire.csv | 2 - lightningd/channel.c | 10 ++--- lightningd/connect_control.c | 46 +++++++++++---------- lightningd/connect_control.h | 1 - lightningd/dual_open_control.c | 2 +- lightningd/opening_control.c | 2 +- lightningd/options.c | 2 +- lightningd/peer_control.c | 15 ++++--- lightningd/peer_control.h | 6 ++- lightningd/test/run-invoice-select-inchan.c | 1 - tests/test_connection.py | 5 ++- wallet/test/run-wallet.c | 1 - 13 files changed, 54 insertions(+), 77 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index dbf03d039bec..ae514cc287f3 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -56,13 +56,6 @@ #define HSM_FD 3 #define GOSSIPCTL_FD 4 -/*~ In C convention, constants are UPPERCASE macros. Not everything needs to - * be a constant, but it soothes the programmer's conscience to encapsulate - * arbitrary decisions like these in one place. */ -#define MAX_CONNECT_ATTEMPTS 10 -#define INITIAL_WAIT_SECONDS 1 -#define MAX_WAIT_SECONDS 300 - /* Peers we're trying to reach: we iterate through addrs until we succeed * or fail. */ struct connecting { @@ -88,9 +81,6 @@ struct connecting { /* Accumulated errors */ char *errors; - - /* How many seconds did we wait this time? */ - u32 seconds_waited; }; /*~ C programs should generally be written bottom-to-top, with the root @@ -623,15 +613,13 @@ struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect) */ static void connect_failed(struct daemon *daemon, const struct node_id *id, - u32 seconds_waited, const struct wireaddr_internal *addrhint, errcode_t errcode, const char *errfmt, ...) - PRINTF_FMT(6,7); + PRINTF_FMT(5,6); static void connect_failed(struct daemon *daemon, const struct node_id *id, - u32 seconds_waited, const struct wireaddr_internal *addrhint, errcode_t errcode, const char *errfmt, ...) @@ -639,27 +627,18 @@ static void connect_failed(struct daemon *daemon, u8 *msg; va_list ap; char *errmsg; - u32 wait_seconds; va_start(ap, errfmt); errmsg = tal_vfmt(tmpctx, errfmt, ap); va_end(ap); - /* Wait twice as long to reconnect, between min and max. */ - wait_seconds = seconds_waited * 2; - if (wait_seconds > MAX_WAIT_SECONDS) - wait_seconds = MAX_WAIT_SECONDS; - if (wait_seconds < INITIAL_WAIT_SECONDS) - wait_seconds = INITIAL_WAIT_SECONDS; - status_peer_debug(id, "Failed connected out: %s", errmsg); /* lightningd may have a connect command waiting to know what * happened. We leave it to lightningd to decide if it wants to try - * again, with the wait_seconds as a hint of how long before - * asking. */ + * again. */ msg = towire_connectd_connect_failed(NULL, id, errcode, errmsg, - wait_seconds, addrhint); + addrhint); daemon_conn_send(daemon->master, take(msg)); } @@ -801,7 +780,6 @@ static void try_connect_one_addr(struct connecting *connect) /* Out of addresses? */ if (connect->addrnum == tal_count(connect->addrs)) { connect_failed(connect->daemon, &connect->id, - connect->seconds_waited, connect->addrhint, CONNECT_ALL_ADDRESSES_FAILED, "All addresses failed: %s", connect->errors); @@ -1748,7 +1726,6 @@ static void add_gossip_addrs(struct wireaddr_internal **addrs, * caller so it's marginal. */ static void try_connect_peer(struct daemon *daemon, const struct node_id *id, - u32 seconds_waited, struct wireaddr *gossip_addrs, struct wireaddr_internal *addrhint STEALS) { @@ -1805,7 +1782,7 @@ static void try_connect_peer(struct daemon *daemon, /* Still no address? Fail immediately. Lightningd can still choose * to retry; an address may get gossiped or appear on the DNS seed. */ if (tal_count(addrs) == 0) { - connect_failed(daemon, id, seconds_waited, addrhint, + connect_failed(daemon, id, addrhint, CONNECT_NO_KNOWN_ADDRESS, "Unable to connect, no address known for peer"); return; @@ -1822,7 +1799,6 @@ static void try_connect_peer(struct daemon *daemon, * errors which occur. We miss it in a few places; would be nice to * fix! */ connect->connstate = "Connection establishment"; - connect->seconds_waited = seconds_waited; connect->addrhint = tal_steal(connect, addrhint); connect->errors = tal_strdup(connect, ""); connect->conn = NULL; @@ -1837,16 +1813,14 @@ static void try_connect_peer(struct daemon *daemon, static void connect_to_peer(struct daemon *daemon, const u8 *msg) { struct node_id id; - u32 seconds_waited; struct wireaddr_internal *addrhint; struct wireaddr *addrs; if (!fromwire_connectd_connect_to_peer(tmpctx, msg, - &id, &seconds_waited, - &addrs, &addrhint)) + &id, &addrs, &addrhint)) master_badmsg(WIRE_CONNECTD_CONNECT_TO_PEER, msg); - try_connect_peer(daemon, &id, seconds_waited, addrs, addrhint); + try_connect_peer(daemon, &id, addrs, addrhint); } /* lightningd tells us a peer should be disconnected. */ diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 6536be9ea9b6..1dc10bb928a2 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -47,7 +47,6 @@ msgdata,connectd_activate_reply,failmsg,?wirestring, # Master -> connectd: connect to a peer. msgtype,connectd_connect_to_peer,2001 msgdata,connectd_connect_to_peer,id,node_id, -msgdata,connectd_connect_to_peer,seconds_waited,u32, msgdata,connectd_connect_to_peer,len,u32, msgdata,connectd_connect_to_peer,addrs,wireaddr,len msgdata,connectd_connect_to_peer,addrhint,?wireaddr_internal, @@ -57,7 +56,6 @@ msgtype,connectd_connect_failed,2020 msgdata,connectd_connect_failed,id,node_id, msgdata,connectd_connect_failed,failcode,errcode_t, msgdata,connectd_connect_failed,failreason,wirestring, -msgdata,connectd_connect_failed,seconds_to_delay,u32, msgdata,connectd_connect_failed,addrhint,?wireaddr_internal, # Connectd -> master: we got a peer. diff --git a/lightningd/channel.c b/lightningd/channel.c index 2f79551911bc..caede53b9d60 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -929,9 +929,7 @@ void channel_set_billboard(struct channel *channel, bool perm, const char *str) } } -static void channel_err(struct channel *channel, - const char *why, - bool delay_reconnect) +static void channel_err(struct channel *channel, const char *why) { log_info(channel->log, "Peer transient failure in %s: %s", channel_state_name(channel), why); @@ -944,8 +942,6 @@ static void channel_err(struct channel *channel, return; } #endif - channel->peer->delay_reconnect = delay_reconnect; - channel_set_owner(channel, NULL); } @@ -954,7 +950,7 @@ void channel_fail_transient_delayreconnect(struct channel *channel, const char * va_list ap; va_start(ap, fmt); - channel_err(channel, tal_vfmt(tmpctx, fmt, ap), true); + channel_err(channel, tal_vfmt(tmpctx, fmt, ap)); va_end(ap); } @@ -963,7 +959,7 @@ void channel_fail_transient(struct channel *channel, const char *fmt, ...) va_list ap; va_start(ap, fmt); - channel_err(channel, tal_vfmt(tmpctx, fmt, ap), false); + channel_err(channel, tal_vfmt(tmpctx, fmt, ap)); va_end(ap); } diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 36ec83bdcced..a52117e90429 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -247,7 +247,6 @@ AUTODATA(json_command, &connect_command); struct delayed_reconnect { struct lightningd *ld; struct node_id id; - u32 seconds_delayed; struct wireaddr_internal *addrhint; }; @@ -265,7 +264,6 @@ static void gossipd_got_addrs(struct subd *subd, connectmsg = towire_connectd_connect_to_peer(NULL, &d->id, - d->seconds_delayed, addrs, d->addrhint); subd_send_msg(d->ld->connectd, take(connectmsg)); @@ -280,7 +278,6 @@ static void do_connect(struct delayed_reconnect *d) subd_req(d, d->ld->gossip, take(msg), -1, 0, gossipd_got_addrs, d); } -/* peer may be NULL here */ static void try_connect(const tal_t *ctx, struct lightningd *ld, const struct node_id *id, @@ -293,7 +290,6 @@ static void try_connect(const tal_t *ctx, d = tal(ctx, struct delayed_reconnect); d->ld = ld; d->id = *id; - d->seconds_delayed = seconds_delay; d->addrhint = tal_dup_or_null(d, struct wireaddr_internal, addrhint); if (!seconds_delay) { @@ -316,6 +312,7 @@ static void try_connect(const tal_t *ctx, "in %u seconds", seconds_delay)); } + peer->last_connect_attempt = time_now(); } /* We fuzz the timer by up to 1 second, to avoid getting into @@ -326,18 +323,34 @@ static void try_connect(const tal_t *ctx, do_connect, d)); } +/*~ In C convention, constants are UPPERCASE macros. Not everything needs to + * be a constant, but it soothes the programmer's conscience to encapsulate + * arbitrary decisions like these in one place. */ +#define INITIAL_WAIT_SECONDS 1 +#define MAX_WAIT_SECONDS 300 + void try_reconnect(const tal_t *ctx, struct peer *peer, - u32 seconds_delay, const struct wireaddr_internal *addrhint) { if (!peer->ld->reconnect) return; + /* Did we last attempt to connect recently? Enter backoff mode. */ + if (time_less(time_between(time_now(), peer->last_connect_attempt), + time_from_sec(MAX_WAIT_SECONDS * 2))) { + u32 max = DEV_FAST_RECONNECT(peer->ld->dev_fast_reconnect, + 3, MAX_WAIT_SECONDS); + peer->reconnect_delay *= 2; + if (peer->reconnect_delay > max) + peer->reconnect_delay = max; + } else + peer->reconnect_delay = INITIAL_WAIT_SECONDS; + try_connect(ctx, peer->ld, &peer->id, - seconds_delay, + peer->reconnect_delay, addrhint); } @@ -346,7 +359,6 @@ static void connect_failed(struct lightningd *ld, const struct node_id *id, errcode_t errcode, const char *errmsg, - const u32 *seconds_to_delay, const struct wireaddr_internal *addrhint) { struct peer *peer; @@ -361,17 +373,10 @@ static void connect_failed(struct lightningd *ld, /* If we have an active channel, then reconnect. */ peer = peer_by_id(ld, id); if (peer && peer_any_active_channel(peer, NULL)) { - u32 delay; - if (seconds_to_delay) - delay = *seconds_to_delay; - else if (peer->delay_reconnect) - delay = DEV_FAST_RECONNECT(ld->dev_fast_reconnect, 3, 60); - else - delay = 1; - log_peer_debug(ld->log, id, "Reconnecting in %u seconds", delay); - try_reconnect(peer, peer, delay, addrhint); + try_reconnect(peer, peer, addrhint); } else - log_peer_debug(ld->log, id, "Not reconnecting: %s", peer ? "no active channel" : "no channels"); + log_peer_debug(ld->log, id, "Not reconnecting: %s", + peer ? "no active channel" : "no channels"); } void connect_failed_disconnect(struct lightningd *ld, @@ -379,7 +384,7 @@ void connect_failed_disconnect(struct lightningd *ld, const struct wireaddr_internal *addrhint) { connect_failed(ld, id, CONNECT_DISCONNECTED_DURING, - "disconnected during connection", NULL, addrhint); + "disconnected during connection", addrhint); } static void handle_connect_failed(struct lightningd *ld, const u8 *msg) @@ -387,15 +392,14 @@ static void handle_connect_failed(struct lightningd *ld, const u8 *msg) struct node_id id; errcode_t errcode; char *errmsg; - u32 seconds_to_delay; struct wireaddr_internal *addrhint; if (!fromwire_connectd_connect_failed(tmpctx, msg, &id, &errcode, &errmsg, - &seconds_to_delay, &addrhint)) + &addrhint)) fatal("Connect gave bad CONNECTD_CONNECT_FAILED message %s", tal_hex(msg, msg)); - connect_failed(ld, &id, errcode, errmsg, &seconds_to_delay, addrhint); + connect_failed(ld, &id, errcode, errmsg, addrhint); } void connect_succeeded(struct lightningd *ld, const struct peer *peer, diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 5eaa3c98daff..6b64b72d0b12 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -20,7 +20,6 @@ void connectd_activate(struct lightningd *ld); void try_reconnect(const tal_t *ctx, struct peer *peer, - u32 seconds_delay, const struct wireaddr_internal *addrhint); void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 71133b8fb699..05faaa19d1d9 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1296,7 +1296,7 @@ wallet_commit_channel(struct lightningd *ld, * reconnect because no channels are active. But the subd * just made it active! */ if (!any_active && channel->peer->connected == PEER_DISCONNECTED) { - try_reconnect(channel->peer, channel->peer, 1, + try_reconnect(channel->peer, channel->peer, &channel->peer->addr); } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index effdd07d983d..3ba43fac122f 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -240,7 +240,7 @@ wallet_commit_channel(struct lightningd *ld, * reconnect because no channels are active. But the subd * just made it active! */ if (!any_active && channel->peer->connected == PEER_DISCONNECTED) { - try_reconnect(channel->peer, channel->peer, 1, + try_reconnect(channel->peer, channel->peer, &channel->peer->addr); } diff --git a/lightningd/options.c b/lightningd/options.c index be6c159c4604..54f5213b118a 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -695,7 +695,7 @@ static void dev_register_opts(struct lightningd *ld) "Disable automatic reconnect-attempts by this node, but accept incoming"); opt_register_noarg("--dev-fast-reconnect", opt_set_bool, &ld->dev_fast_reconnect, - "Make default reconnect delay 3 (not 60) seconds"); + "Make max default reconnect delay 3 (not 300) seconds"); opt_register_noarg("--dev-fail-on-subdaemon-fail", opt_set_bool, &ld->dev_subdaemon_fail, opt_hidden); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index bdfb45628a94..edf1240351cf 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -100,7 +100,8 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, list_head_init(&peer->channels); peer->direction = node_id_idx(&peer->ld->id, &peer->id); peer->connected = PEER_DISCONNECTED; - peer->delay_reconnect = false; + peer->last_connect_attempt.ts.tv_sec + = peer->last_connect_attempt.ts.tv_nsec = 0; #if DEVELOPER peer->ignore_htlcs = false; #endif @@ -1330,7 +1331,6 @@ void peer_connected(struct lightningd *ld, const u8 *msg) /* We mark peer in "connecting" state until hooks have passed. */ assert(peer->connected == PEER_DISCONNECTED); peer->connected = PEER_CONNECTING; - peer->delay_reconnect = false; /* Update peer address and direction */ peer->addr = hook_payload->addr; @@ -2025,9 +2025,14 @@ static void setup_peer(struct peer *peer, u32 delay) } /* Make sure connectd knows to try reconnecting. */ - if (connect) - try_reconnect(peer, peer, delay, &peer->addr); - + if (connect) { + /* To delay, make it seem like we just connected. */ + if (delay > 0) { + peer->reconnect_delay = delay; + peer->last_connect_attempt = time_now(); + } + try_reconnect(peer, peer, &peer->addr); + } } void setup_peers(struct lightningd *ld) diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 4609ca54b9b7..5bdcef569098 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -30,8 +30,10 @@ struct peer { /* Connection counter from connectd. */ u64 connectd_counter; - /* Did we fail badly last time? Don't reconnect too fast. */ - bool delay_reconnect; + /* Last reconnect: if it's recent, we delay by reconnect_delay, + * doubling each time. */ + struct timeabs last_connect_attempt; + u32 reconnect_delay; /* Our channels */ struct list_head channels; diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 699564d665d6..915a2c55c044 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -764,7 +764,6 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, /* Generated stub for try_reconnect */ void try_reconnect(const tal_t *ctx UNNEEDED, struct peer *peer UNNEEDED, - u32 seconds_delay UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "try_reconnect called!\n"); abort(); } /* Generated stub for version */ diff --git a/tests/test_connection.py b/tests/test_connection.py index 7c4548aea222..a570a0497d01 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3242,11 +3242,12 @@ def test_feerate_spam(node_factory, chainparams): l1.daemon.wait_for_log('peer_out WIRE_UPDATE_FEE', timeout=5) -@pytest.mark.developer("need dev-feerate") +@pytest.mark.developer("need dev-feerate, dev-fast-reconnect") def test_feerate_stress(node_factory, executor): # Third node makes HTLC traffic less predictable. l1, l2, l3 = node_factory.line_graph(3, opts={'commit-time': 100, - 'may_reconnect': True}) + 'may_reconnect': True, + 'dev-fast-reconnect': None}) l1.pay(l2, 10**9 // 2) scid12 = l1.get_channel_scid(l2) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 8dd612b80e85..0715963d658b 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -839,7 +839,6 @@ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, /* Generated stub for try_reconnect */ void try_reconnect(const tal_t *ctx UNNEEDED, struct peer *peer UNNEEDED, - u32 seconds_delay UNNEEDED, const struct wireaddr_internal *addrhint UNNEEDED) { fprintf(stderr, "try_reconnect called!\n"); abort(); } /* Generated stub for watch_txid */ From 8c9fa457babd8ac09009fb93fe7a1a6409aba911 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 28 Jul 2022 11:00:57 +0930 Subject: [PATCH 1213/1530] pytest: fix flake in test_gossip_timestamp_filter ``` # 0x0100 = channel_announcement # 0x0102 = channel_update # (Node announcement may have any timestamp) types = Counter([m[0:4] for m in msgs]) assert types['0100'] == 1 > assert types['0102'] == 2 E assert 1 == 2 tests/test_gossip.py:324: AssertionError ``` Examining the logs shows that we ask l4 for timestamps "first_timestamp=1658892115 timestamp_range=13", and the timestamp on the missing update is exactly `1658892128`. So round the end time up by 1 for filtering. Signed-off-by: Rusty Russell --- tests/test_gossip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 4ce2f7967ef2..fcd154b2a181 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -285,7 +285,7 @@ def test_gossip_timestamp_filter(node_factory, bitcoind, chainparams): bitcoind.generate_block(5) l1.wait_for_channel_updates([chan23]) - after_23 = int(time.time()) + after_23 = int(time.time()) + 1 # Make sure l4 has received all the gossip. wait_for(lambda: ['alias' in node for node in l4.rpc.listnodes()['nodes']] == [True, True, True]) From 78804d9ea82b66dfd5095e745c3ca08d1a5c6d30 Mon Sep 17 00:00:00 2001 From: CC Date: Thu, 28 Jul 2022 09:04:12 +0200 Subject: [PATCH 1214/1530] fix doc: deschashonly --- doc/lightning-invoice.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index c08f2dfec3e5..36fc455727fc 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -67,7 +67,7 @@ payment. If specified, *cltv* sets the *min_final_cltv_expiry* for the invoice. Otherwise, it's set to the parameter **cltv-final**. -If *deschash* is true (default false), then the bolt11 returned +If *deschashonly* is true (default false), then the bolt11 returned contains a hash of the *description*, rather than the *description* itself: this allows much longer descriptions, but they must be communicated via some other mechanism. From 10d66c25c406b713c36aa9e10001ebb25bc69f2f Mon Sep 17 00:00:00 2001 From: William Casarin Date: Thu, 28 Jul 2022 07:05:51 -0700 Subject: [PATCH 1215/1530] commando-rune: show warning when creating runes with no restrictions Changelog-Added: Show warning when creating runes with no restrictions Signed-off-by: William Casarin --- doc/lightning-commando-rune.7.md | 5 ++++- doc/schemas/commando-rune.schema.json | 4 ++++ plugins/commando.c | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index 699cab88dd2f..abd71efb4431 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -193,6 +193,9 @@ On success, an object is returned, containing: - **rune** (string): the resulting rune - **unique_id** (string): the id of this rune: this is set at creation and cannot be changed (even as restrictions are added) +The following warnings may also be returned: +- **warning_unrestricted_rune**: A warning shown when runes are created with powers that could drain your node + [comment]: # (GENERATE-FROM-SCHEMA-END) AUTHOR @@ -214,4 +217,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:598337212d2e8a6833698e931f838d8cb424c353af4d7adf6891803ff0ee604b) +[comment]: # ( SHA256STAMP:34c6d5222fee79f4648be4a717041d32004b5bb3644364dc6569b87b16ed2ebe) diff --git a/doc/schemas/commando-rune.schema.json b/doc/schemas/commando-rune.schema.json index c0519e51cb40..2bb8483aa21e 100644 --- a/doc/schemas/commando-rune.schema.json +++ b/doc/schemas/commando-rune.schema.json @@ -14,6 +14,10 @@ "unique_id": { "type": "string", "description": "the id of this rune: this is set at creation and cannot be changed (even as restrictions are added)" + }, + "warning_unrestricted_rune": { + "type": "string", + "description": "A warning shown when runes are created with powers that could drain your node" } } } diff --git a/plugins/commando.c b/plugins/commando.c index 83379aafbf74..7ed017163b92 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -804,6 +804,10 @@ static struct command_result *reply_with_rune(struct command *cmd, json_add_string(js, "rune", rune_to_base64(tmpctx, rune)); json_add_string(js, "unique_id", rune->unique_id); + + if (tal_count(rune->restrs) <= 1) { + json_add_string(js, "warning_unrestricted_rune", "WARNING: This rune has no restrictions! Anyone who has access to this rune could drain funds from your node. Be careful when giving this to apps that you don't trust. Consider using the restrictions parameter to only allow access to specific rpc methods."); + } return command_finished(cmd, js); } From a675f4c24e55fe40f558be40e286bb6615c2f5e7 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 28 Jul 2022 22:53:25 -0500 Subject: [PATCH 1216/1530] balance_snapshot: emit balances for channels that are awaiting_lockin 9cad7d6a6a changed the behavior of `channel_active`, which slightly broke how our balance snapshots work (we need info about channels that aren't on-chain yet). This patches adds AWAITING_UNILATERAL back in. --- lightningd/coin_mvts.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 4ba2d9e58339..7ff18015b544 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -121,7 +121,8 @@ void send_account_balance_snapshot(struct lightningd *ld, u32 blockheight) /* Add channel balances */ list_for_each(&ld->peers, p, list) { list_for_each(&p->channels, chan, list) { - if (channel_active(chan)) { + if (channel_active(chan) + || chan->state == AWAITING_UNILATERAL) { bal = tal(snap, struct account_balance); bal->bip173_name = chainparams->lightning_hrp; bal->acct_id = type_to_string(bal, From 4e503f7d0a27655edbc0db5af7ca4fb87da1d2d9 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 28 Jul 2022 22:44:17 -0500 Subject: [PATCH 1217/1530] bkpr/listpeeers: add lease_fees back to funds; separate out in listpeers First, how we record "our_funds" and then apply pushes vs lease_fees (for liquidity ad buys/sales) was exactly opposite. For pushes we were reporting the total funded into the channel, with the push representing how much we'd later moved to the peer. For lease_fees we were rerporting the total in the channel, with the push representing how much was already moved to the peer. We fix this (from a view perspective) by re-adding lease fees to what's reported in the channel funding totals. Since this is now new behavior (for leased channel values), we added new fields so we can take the old field names thru a deprecation cycle. We also make it possible to differentiate btw a push and a lease_fee (before they were all the same), by adding to new fields to `listpeers`: `fee_paid_msat` and `fee_rcvd_msat`. This allows us to avoid math in the bookkeeper, instead we just pick the numbers out directly and record them. Fixes #5472 Changelog-Added: JSON-RPC: `listpeers` now has a few new fields for `funding` (`remote_funds_msat`, `local_funds_msat`, `fee_paid_msat`, `fee_rcvd_msat`). Changelog-Deprecated: JSON-RPC: `listpeers`.`funded` fields `local_msat` and `remote_msat` are now deprecated. --- .msggen.json | 4 + cln-grpc/proto/node.proto | 10 +- cln-rpc/src/model.rs | 20 +- contrib/pyln-testing/pyln/testing/grpc2py.py | 4 + contrib/pyln-testing/pyln/testing/node_pb2.py | 584 +++++++++--------- doc/lightning-listpeers.7.md | 12 +- doc/schemas/listpeers.schema.json | 25 +- lightningd/peer_control.c | 82 ++- plugins/bkpr/bookkeeper.c | 98 ++- tests/test_bookkeeper.py | 145 ++++- tests/test_closing.py | 7 +- tests/test_connection.py | 2 +- tests/test_opening.py | 15 +- 13 files changed, 645 insertions(+), 363 deletions(-) diff --git a/.msggen.json b/.msggen.json index e07b0fb3d011..6a65191962eb 100644 --- a/.msggen.json +++ b/.msggen.json @@ -752,8 +752,12 @@ "ListPeers.peers[].channels[].feerate.perkw": 1 }, "ListpeersPeersChannelsFunding": { + "ListPeers.peers[].channels[].funding.fee_paid_msat": 5, + "ListPeers.peers[].channels[].funding.fee_rcvd_msat": 6, + "ListPeers.peers[].channels[].funding.local_funds_msat": 4, "ListPeers.peers[].channels[].funding.local_msat": 1, "ListPeers.peers[].channels[].funding.pushed_msat": 3, + "ListPeers.peers[].channels[].funding.remote_funds_msat": 7, "ListPeers.peers[].channels[].funding.remote_msat": 2 }, "ListpeersPeersChannelsHtlcs": { diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 9af73f0f3c32..ef484ab0f37c 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -232,9 +232,13 @@ message ListpeersPeersChannelsInflight { } message ListpeersPeersChannelsFunding { - Amount local_msat = 1; - Amount remote_msat = 2; - Amount pushed_msat = 3; + optional Amount local_msat = 1; + optional Amount remote_msat = 2; + optional Amount pushed_msat = 3; + Amount local_funds_msat = 4; + Amount remote_funds_msat = 7; + optional Amount fee_paid_msat = 5; + optional Amount fee_rcvd_msat = 6; } message ListpeersPeersChannelsAlias { diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 26df14c91554..1607a2d4e776 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1097,12 +1097,20 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeersChannelsFunding { - #[serde(alias = "local_msat")] - pub local_msat: Amount, - #[serde(alias = "remote_msat")] - pub remote_msat: Amount, - #[serde(alias = "pushed_msat")] - pub pushed_msat: Amount, + #[serde(alias = "local_msat", skip_serializing_if = "Option::is_none")] + pub local_msat: Option, + #[serde(alias = "remote_msat", skip_serializing_if = "Option::is_none")] + pub remote_msat: Option, + #[serde(alias = "pushed_msat", skip_serializing_if = "Option::is_none")] + pub pushed_msat: Option, + #[serde(alias = "local_funds_msat")] + pub local_funds_msat: Amount, + #[serde(alias = "remote_funds_msat")] + pub remote_funds_msat: Amount, + #[serde(alias = "fee_paid_msat", skip_serializing_if = "Option::is_none")] + pub fee_paid_msat: Option, + #[serde(alias = "fee_rcvd_msat", skip_serializing_if = "Option::is_none")] + pub fee_rcvd_msat: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index ae0a63940833..fe619c6cb4f7 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -102,6 +102,10 @@ def listpeers_peers_channels_funding2py(m): "local_msat": amount2msat(m.local_msat), # PrimitiveField in generate_composite "remote_msat": amount2msat(m.remote_msat), # PrimitiveField in generate_composite "pushed_msat": amount2msat(m.pushed_msat), # PrimitiveField in generate_composite + "local_funds_msat": amount2msat(m.local_funds_msat), # PrimitiveField in generate_composite + "remote_funds_msat": amount2msat(m.remote_funds_msat), # PrimitiveField in generate_composite + "fee_paid_msat": amount2msat(m.fee_paid_msat), # PrimitiveField in generate_composite + "fee_rcvd_msat": amount2msat(m.fee_rcvd_msat), # PrimitiveField in generate_composite }) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index a83101dcbc2d..8f5b615a09f7 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x84\x01\n\x1dListpeersPeersChannelsFunding\x12\x1f\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xb6\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepth\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xb6\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepth\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1106,295 +1106,295 @@ _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_start=4740 _LISTPEERSPEERSCHANNELSINFLIGHT._serialized_end=4937 _LISTPEERSPEERSCHANNELSFUNDING._serialized_start=4940 - _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5072 - _LISTPEERSPEERSCHANNELSALIAS._serialized_start=5074 - _LISTPEERSPEERSCHANNELSALIAS._serialized_end=5165 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5168 - _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=5506 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5422 - _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=5477 - _LISTFUNDSREQUEST._serialized_start=5508 - _LISTFUNDSREQUEST._serialized_end=5556 - _LISTFUNDSRESPONSE._serialized_start=5558 - _LISTFUNDSRESPONSE._serialized_end=5659 - _LISTFUNDSOUTPUTS._serialized_start=5662 - _LISTFUNDSOUTPUTS._serialized_end=6035 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=5923 - _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=5990 - _LISTFUNDSCHANNELS._serialized_start=6038 - _LISTFUNDSCHANNELS._serialized_end=6297 - _SENDPAYREQUEST._serialized_start=6300 - _SENDPAYREQUEST._serialized_end=6647 - _SENDPAYRESPONSE._serialized_start=6650 - _SENDPAYRESPONSE._serialized_end=7199 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7037 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7079 - _SENDPAYROUTE._serialized_start=7201 - _SENDPAYROUTE._serialized_end=7293 - _LISTCHANNELSREQUEST._serialized_start=7296 - _LISTCHANNELSREQUEST._serialized_end=7443 - _LISTCHANNELSRESPONSE._serialized_start=7445 - _LISTCHANNELSRESPONSE._serialized_end=7512 - _LISTCHANNELSCHANNELS._serialized_start=7515 - _LISTCHANNELSCHANNELS._serialized_end=7931 - _ADDGOSSIPREQUEST._serialized_start=7933 - _ADDGOSSIPREQUEST._serialized_end=7968 - _ADDGOSSIPRESPONSE._serialized_start=7970 - _ADDGOSSIPRESPONSE._serialized_end=7989 - _AUTOCLEANINVOICEREQUEST._serialized_start=7991 - _AUTOCLEANINVOICEREQUEST._serialized_end=8102 - _AUTOCLEANINVOICERESPONSE._serialized_start=8105 - _AUTOCLEANINVOICERESPONSE._serialized_end=8234 - _CHECKMESSAGEREQUEST._serialized_start=8236 - _CHECKMESSAGEREQUEST._serialized_end=8321 - _CHECKMESSAGERESPONSE._serialized_start=8323 - _CHECKMESSAGERESPONSE._serialized_end=8379 - _CLOSEREQUEST._serialized_start=8382 - _CLOSEREQUEST._serialized_end=8698 - _CLOSERESPONSE._serialized_start=8701 - _CLOSERESPONSE._serialized_end=8872 - _CLOSERESPONSE_CLOSETYPE._serialized_start=8803 - _CLOSERESPONSE_CLOSETYPE._serialized_end=8856 - _CONNECTREQUEST._serialized_start=8874 - _CONNECTREQUEST._serialized_end=8958 - _CONNECTRESPONSE._serialized_start=8961 - _CONNECTRESPONSE._serialized_end=9103 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9068 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9103 - _CONNECTADDRESS._serialized_start=9106 - _CONNECTADDRESS._serialized_end=9357 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9245 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9325 - _CREATEINVOICEREQUEST._serialized_start=9359 - _CREATEINVOICEREQUEST._serialized_end=9433 - _CREATEINVOICERESPONSE._serialized_start=9436 - _CREATEINVOICERESPONSE._serialized_end=10063 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=9863 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=9919 - _DATASTOREREQUEST._serialized_start=10066 - _DATASTOREREQUEST._serialized_end=10374 - _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10219 - _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10331 - _DATASTORERESPONSE._serialized_start=10377 - _DATASTORERESPONSE._serialized_end=10507 - _CREATEONIONREQUEST._serialized_start=10510 - _CREATEONIONREQUEST._serialized_end=10667 - _CREATEONIONRESPONSE._serialized_start=10669 - _CREATEONIONRESPONSE._serialized_end=10729 - _CREATEONIONHOPS._serialized_start=10731 - _CREATEONIONHOPS._serialized_end=10781 - _DELDATASTOREREQUEST._serialized_start=10783 - _DELDATASTOREREQUEST._serialized_end=10857 - _DELDATASTORERESPONSE._serialized_start=10860 - _DELDATASTORERESPONSE._serialized_end=10993 - _DELEXPIREDINVOICEREQUEST._serialized_start=10995 - _DELEXPIREDINVOICEREQUEST._serialized_end=11067 - _DELEXPIREDINVOICERESPONSE._serialized_start=11069 - _DELEXPIREDINVOICERESPONSE._serialized_end=11096 - _DELINVOICEREQUEST._serialized_start=11099 - _DELINVOICEREQUEST._serialized_end=11281 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11215 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11268 - _DELINVOICERESPONSE._serialized_start=11284 - _DELINVOICERESPONSE._serialized_end=11723 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11215 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11268 - _INVOICEREQUEST._serialized_start=11726 - _INVOICEREQUEST._serialized_end=12038 - _INVOICERESPONSE._serialized_start=12041 - _INVOICERESPONSE._serialized_end=12400 - _LISTDATASTOREREQUEST._serialized_start=12402 - _LISTDATASTOREREQUEST._serialized_end=12437 - _LISTDATASTORERESPONSE._serialized_start=12439 - _LISTDATASTORERESPONSE._serialized_end=12510 - _LISTDATASTOREDATASTORE._serialized_start=12513 - _LISTDATASTOREDATASTORE._serialized_end=12648 - _LISTINVOICESREQUEST._serialized_start=12651 - _LISTINVOICESREQUEST._serialized_end=12820 - _LISTINVOICESRESPONSE._serialized_start=12822 - _LISTINVOICESRESPONSE._serialized_end=12889 - _LISTINVOICESINVOICES._serialized_start=12892 - _LISTINVOICESINVOICES._serialized_end=13552 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13329 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13392 - _SENDONIONREQUEST._serialized_start=13555 - _SENDONIONREQUEST._serialized_end=13903 - _SENDONIONRESPONSE._serialized_start=13906 - _SENDONIONRESPONSE._serialized_end=14429 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14277 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14321 - _SENDONIONFIRST_HOP._serialized_start=14431 - _SENDONIONFIRST_HOP._serialized_end=14512 - _LISTSENDPAYSREQUEST._serialized_start=14515 - _LISTSENDPAYSREQUEST._serialized_end=14750 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14652 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14711 - _LISTSENDPAYSRESPONSE._serialized_start=14752 - _LISTSENDPAYSRESPONSE._serialized_end=14819 - _LISTSENDPAYSPAYMENTS._serialized_start=14822 - _LISTSENDPAYSPAYMENTS._serialized_end=15435 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15240 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15307 - _LISTTRANSACTIONSREQUEST._serialized_start=15437 - _LISTTRANSACTIONSREQUEST._serialized_end=15462 - _LISTTRANSACTIONSRESPONSE._serialized_start=15464 - _LISTTRANSACTIONSRESPONSE._serialized_end=15547 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15550 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=15832 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=15835 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16351 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16047 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16325 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16354 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=16898 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16593 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=16872 - _PAYREQUEST._serialized_start=16901 - _PAYREQUEST._serialized_end=17373 - _PAYRESPONSE._serialized_start=17376 - _PAYRESPONSE._serialized_end=17755 - _PAYRESPONSE_PAYSTATUS._serialized_start=17658 - _PAYRESPONSE_PAYSTATUS._serialized_end=17708 - _LISTNODESREQUEST._serialized_start=17757 - _LISTNODESREQUEST._serialized_end=17799 - _LISTNODESRESPONSE._serialized_start=17801 - _LISTNODESRESPONSE._serialized_end=17856 - _LISTNODESNODES._serialized_start=17859 - _LISTNODESNODES._serialized_end=18084 - _LISTNODESNODESADDRESSES._serialized_start=18087 - _LISTNODESNODESADDRESSES._serialized_end=18334 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18227 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18322 - _WAITANYINVOICEREQUEST._serialized_start=18336 - _WAITANYINVOICEREQUEST._serialized_end=18439 - _WAITANYINVOICERESPONSE._serialized_start=18442 - _WAITANYINVOICERESPONSE._serialized_end=18973 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=18818 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=18863 - _WAITINVOICEREQUEST._serialized_start=18975 - _WAITINVOICEREQUEST._serialized_end=19010 - _WAITINVOICERESPONSE._serialized_start=19013 - _WAITINVOICERESPONSE._serialized_end=19532 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19380 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19422 - _WAITSENDPAYREQUEST._serialized_start=19535 - _WAITSENDPAYREQUEST._serialized_end=19677 - _WAITSENDPAYRESPONSE._serialized_start=19680 - _WAITSENDPAYRESPONSE._serialized_end=20198 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20057 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20090 - _NEWADDRREQUEST._serialized_start=20201 - _NEWADDRREQUEST._serialized_end=20359 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20285 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20343 - _NEWADDRRESPONSE._serialized_start=20361 - _NEWADDRRESPONSE._serialized_end=20452 - _WITHDRAWREQUEST._serialized_start=20455 - _WITHDRAWREQUEST._serialized_end=20657 - _WITHDRAWRESPONSE._serialized_start=20659 - _WITHDRAWRESPONSE._serialized_end=20717 - _KEYSENDREQUEST._serialized_start=20720 - _KEYSENDREQUEST._serialized_end=21052 - _KEYSENDRESPONSE._serialized_start=21055 - _KEYSENDRESPONSE._serialized_end=21425 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21349 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21378 - _KEYSENDEXTRATLVS._serialized_start=21427 - _KEYSENDEXTRATLVS._serialized_end=21445 - _FUNDPSBTREQUEST._serialized_start=21448 - _FUNDPSBTREQUEST._serialized_end=21759 - _FUNDPSBTRESPONSE._serialized_start=21762 - _FUNDPSBTRESPONSE._serialized_end=21979 - _FUNDPSBTRESERVATIONS._serialized_start=21981 - _FUNDPSBTRESERVATIONS._serialized_end=22098 - _SENDPSBTREQUEST._serialized_start=22100 - _SENDPSBTREQUEST._serialized_end=22165 - _SENDPSBTRESPONSE._serialized_start=22167 - _SENDPSBTRESPONSE._serialized_end=22211 - _SIGNPSBTREQUEST._serialized_start=22213 - _SIGNPSBTREQUEST._serialized_end=22262 - _SIGNPSBTRESPONSE._serialized_start=22264 - _SIGNPSBTRESPONSE._serialized_end=22303 - _UTXOPSBTREQUEST._serialized_start=22306 - _UTXOPSBTREQUEST._serialized_end=22653 - _UTXOPSBTRESPONSE._serialized_start=22656 - _UTXOPSBTRESPONSE._serialized_end=22873 - _UTXOPSBTRESERVATIONS._serialized_start=22875 - _UTXOPSBTRESERVATIONS._serialized_end=22992 - _TXDISCARDREQUEST._serialized_start=22994 - _TXDISCARDREQUEST._serialized_end=23026 - _TXDISCARDRESPONSE._serialized_start=23028 - _TXDISCARDRESPONSE._serialized_end=23082 - _TXPREPAREREQUEST._serialized_start=23085 - _TXPREPAREREQUEST._serialized_end=23249 - _TXPREPARERESPONSE._serialized_start=23251 - _TXPREPARERESPONSE._serialized_end=23319 - _TXSENDREQUEST._serialized_start=23321 - _TXSENDREQUEST._serialized_end=23350 - _TXSENDRESPONSE._serialized_start=23352 - _TXSENDRESPONSE._serialized_end=23408 - _DISCONNECTREQUEST._serialized_start=23410 - _DISCONNECTREQUEST._serialized_end=23471 - _DISCONNECTRESPONSE._serialized_start=23473 - _DISCONNECTRESPONSE._serialized_end=23493 - _FEERATESREQUEST._serialized_start=23495 - _FEERATESREQUEST._serialized_end=23602 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23565 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23602 - _FEERATESRESPONSE._serialized_start=23604 - _FEERATESRESPONSE._serialized_end=23690 - _FEERATESPERKB._serialized_start=23693 - _FEERATESPERKB._serialized_end=24016 - _FEERATESPERKW._serialized_start=24019 - _FEERATESPERKW._serialized_end=24342 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24345 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24538 - _FUNDCHANNELREQUEST._serialized_start=24541 - _FUNDCHANNELREQUEST._serialized_end=24979 - _FUNDCHANNELRESPONSE._serialized_start=24982 - _FUNDCHANNELRESPONSE._serialized_end=25137 - _GETROUTEREQUEST._serialized_start=25140 - _GETROUTEREQUEST._serialized_end=25376 - _GETROUTERESPONSE._serialized_start=25378 - _GETROUTERESPONSE._serialized_end=25431 - _GETROUTEROUTE._serialized_start=25434 - _GETROUTEROUTE._serialized_end=25631 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25602 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25631 - _LISTFORWARDSREQUEST._serialized_start=25634 - _LISTFORWARDSREQUEST._serialized_end=25892 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=25774 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=25850 - _LISTFORWARDSRESPONSE._serialized_start=25894 - _LISTFORWARDSRESPONSE._serialized_end=25961 - _LISTFORWARDSFORWARDS._serialized_start=25964 - _LISTFORWARDSFORWARDS._serialized_end=26532 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26329 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26413 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26415 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26463 - _LISTPAYSREQUEST._serialized_start=26535 - _LISTPAYSREQUEST._serialized_end=26754 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26660 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26715 - _LISTPAYSRESPONSE._serialized_start=26756 - _LISTPAYSRESPONSE._serialized_end=26807 - _LISTPAYSPAYS._serialized_start=26810 - _LISTPAYSPAYS._serialized_end=27319 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27144 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27203 - _PINGREQUEST._serialized_start=27321 - _PINGREQUEST._serialized_end=27410 - _PINGRESPONSE._serialized_start=27412 - _PINGRESPONSE._serialized_end=27442 - _SIGNMESSAGEREQUEST._serialized_start=27444 - _SIGNMESSAGEREQUEST._serialized_end=27481 - _SIGNMESSAGERESPONSE._serialized_start=27483 - _SIGNMESSAGERESPONSE._serialized_end=27553 - _STOPREQUEST._serialized_start=27555 - _STOPREQUEST._serialized_end=27568 - _STOPRESPONSE._serialized_start=27570 - _STOPRESPONSE._serialized_end=27584 - _NODE._serialized_start=27587 - _NODE._serialized_end=30515 + _LISTPEERSPEERSCHANNELSFUNDING._serialized_end=5331 + _LISTPEERSPEERSCHANNELSALIAS._serialized_start=5333 + _LISTPEERSPEERSCHANNELSALIAS._serialized_end=5424 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_start=5427 + _LISTPEERSPEERSCHANNELSHTLCS._serialized_end=5765 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_start=5681 + _LISTPEERSPEERSCHANNELSHTLCS_LISTPEERSPEERSCHANNELSHTLCSDIRECTION._serialized_end=5736 + _LISTFUNDSREQUEST._serialized_start=5767 + _LISTFUNDSREQUEST._serialized_end=5815 + _LISTFUNDSRESPONSE._serialized_start=5817 + _LISTFUNDSRESPONSE._serialized_end=5918 + _LISTFUNDSOUTPUTS._serialized_start=5921 + _LISTFUNDSOUTPUTS._serialized_end=6294 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_start=6182 + _LISTFUNDSOUTPUTS_LISTFUNDSOUTPUTSSTATUS._serialized_end=6249 + _LISTFUNDSCHANNELS._serialized_start=6297 + _LISTFUNDSCHANNELS._serialized_end=6556 + _SENDPAYREQUEST._serialized_start=6559 + _SENDPAYREQUEST._serialized_end=6906 + _SENDPAYRESPONSE._serialized_start=6909 + _SENDPAYRESPONSE._serialized_end=7458 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7296 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7338 + _SENDPAYROUTE._serialized_start=7460 + _SENDPAYROUTE._serialized_end=7552 + _LISTCHANNELSREQUEST._serialized_start=7555 + _LISTCHANNELSREQUEST._serialized_end=7702 + _LISTCHANNELSRESPONSE._serialized_start=7704 + _LISTCHANNELSRESPONSE._serialized_end=7771 + _LISTCHANNELSCHANNELS._serialized_start=7774 + _LISTCHANNELSCHANNELS._serialized_end=8190 + _ADDGOSSIPREQUEST._serialized_start=8192 + _ADDGOSSIPREQUEST._serialized_end=8227 + _ADDGOSSIPRESPONSE._serialized_start=8229 + _ADDGOSSIPRESPONSE._serialized_end=8248 + _AUTOCLEANINVOICEREQUEST._serialized_start=8250 + _AUTOCLEANINVOICEREQUEST._serialized_end=8361 + _AUTOCLEANINVOICERESPONSE._serialized_start=8364 + _AUTOCLEANINVOICERESPONSE._serialized_end=8493 + _CHECKMESSAGEREQUEST._serialized_start=8495 + _CHECKMESSAGEREQUEST._serialized_end=8580 + _CHECKMESSAGERESPONSE._serialized_start=8582 + _CHECKMESSAGERESPONSE._serialized_end=8638 + _CLOSEREQUEST._serialized_start=8641 + _CLOSEREQUEST._serialized_end=8957 + _CLOSERESPONSE._serialized_start=8960 + _CLOSERESPONSE._serialized_end=9131 + _CLOSERESPONSE_CLOSETYPE._serialized_start=9062 + _CLOSERESPONSE_CLOSETYPE._serialized_end=9115 + _CONNECTREQUEST._serialized_start=9133 + _CONNECTREQUEST._serialized_end=9217 + _CONNECTRESPONSE._serialized_start=9220 + _CONNECTRESPONSE._serialized_end=9362 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9327 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9362 + _CONNECTADDRESS._serialized_start=9365 + _CONNECTADDRESS._serialized_end=9616 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9504 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9584 + _CREATEINVOICEREQUEST._serialized_start=9618 + _CREATEINVOICEREQUEST._serialized_end=9692 + _CREATEINVOICERESPONSE._serialized_start=9695 + _CREATEINVOICERESPONSE._serialized_end=10322 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10122 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10178 + _DATASTOREREQUEST._serialized_start=10325 + _DATASTOREREQUEST._serialized_end=10633 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10478 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10590 + _DATASTORERESPONSE._serialized_start=10636 + _DATASTORERESPONSE._serialized_end=10766 + _CREATEONIONREQUEST._serialized_start=10769 + _CREATEONIONREQUEST._serialized_end=10926 + _CREATEONIONRESPONSE._serialized_start=10928 + _CREATEONIONRESPONSE._serialized_end=10988 + _CREATEONIONHOPS._serialized_start=10990 + _CREATEONIONHOPS._serialized_end=11040 + _DELDATASTOREREQUEST._serialized_start=11042 + _DELDATASTOREREQUEST._serialized_end=11116 + _DELDATASTORERESPONSE._serialized_start=11119 + _DELDATASTORERESPONSE._serialized_end=11252 + _DELEXPIREDINVOICEREQUEST._serialized_start=11254 + _DELEXPIREDINVOICEREQUEST._serialized_end=11326 + _DELEXPIREDINVOICERESPONSE._serialized_start=11328 + _DELEXPIREDINVOICERESPONSE._serialized_end=11355 + _DELINVOICEREQUEST._serialized_start=11358 + _DELINVOICEREQUEST._serialized_end=11540 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11474 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11527 + _DELINVOICERESPONSE._serialized_start=11543 + _DELINVOICERESPONSE._serialized_end=11982 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11474 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11527 + _INVOICEREQUEST._serialized_start=11985 + _INVOICEREQUEST._serialized_end=12297 + _INVOICERESPONSE._serialized_start=12300 + _INVOICERESPONSE._serialized_end=12659 + _LISTDATASTOREREQUEST._serialized_start=12661 + _LISTDATASTOREREQUEST._serialized_end=12696 + _LISTDATASTORERESPONSE._serialized_start=12698 + _LISTDATASTORERESPONSE._serialized_end=12769 + _LISTDATASTOREDATASTORE._serialized_start=12772 + _LISTDATASTOREDATASTORE._serialized_end=12907 + _LISTINVOICESREQUEST._serialized_start=12910 + _LISTINVOICESREQUEST._serialized_end=13079 + _LISTINVOICESRESPONSE._serialized_start=13081 + _LISTINVOICESRESPONSE._serialized_end=13148 + _LISTINVOICESINVOICES._serialized_start=13151 + _LISTINVOICESINVOICES._serialized_end=13811 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13588 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13651 + _SENDONIONREQUEST._serialized_start=13814 + _SENDONIONREQUEST._serialized_end=14162 + _SENDONIONRESPONSE._serialized_start=14165 + _SENDONIONRESPONSE._serialized_end=14688 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14536 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14580 + _SENDONIONFIRST_HOP._serialized_start=14690 + _SENDONIONFIRST_HOP._serialized_end=14771 + _LISTSENDPAYSREQUEST._serialized_start=14774 + _LISTSENDPAYSREQUEST._serialized_end=15009 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14911 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14970 + _LISTSENDPAYSRESPONSE._serialized_start=15011 + _LISTSENDPAYSRESPONSE._serialized_end=15078 + _LISTSENDPAYSPAYMENTS._serialized_start=15081 + _LISTSENDPAYSPAYMENTS._serialized_end=15694 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15499 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15566 + _LISTTRANSACTIONSREQUEST._serialized_start=15696 + _LISTTRANSACTIONSREQUEST._serialized_end=15721 + _LISTTRANSACTIONSRESPONSE._serialized_start=15723 + _LISTTRANSACTIONSRESPONSE._serialized_end=15806 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15809 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16091 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16094 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16610 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16306 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16584 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16613 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17157 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16852 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17131 + _PAYREQUEST._serialized_start=17160 + _PAYREQUEST._serialized_end=17632 + _PAYRESPONSE._serialized_start=17635 + _PAYRESPONSE._serialized_end=18014 + _PAYRESPONSE_PAYSTATUS._serialized_start=17917 + _PAYRESPONSE_PAYSTATUS._serialized_end=17967 + _LISTNODESREQUEST._serialized_start=18016 + _LISTNODESREQUEST._serialized_end=18058 + _LISTNODESRESPONSE._serialized_start=18060 + _LISTNODESRESPONSE._serialized_end=18115 + _LISTNODESNODES._serialized_start=18118 + _LISTNODESNODES._serialized_end=18343 + _LISTNODESNODESADDRESSES._serialized_start=18346 + _LISTNODESNODESADDRESSES._serialized_end=18593 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18486 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18581 + _WAITANYINVOICEREQUEST._serialized_start=18595 + _WAITANYINVOICEREQUEST._serialized_end=18698 + _WAITANYINVOICERESPONSE._serialized_start=18701 + _WAITANYINVOICERESPONSE._serialized_end=19232 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19077 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19122 + _WAITINVOICEREQUEST._serialized_start=19234 + _WAITINVOICEREQUEST._serialized_end=19269 + _WAITINVOICERESPONSE._serialized_start=19272 + _WAITINVOICERESPONSE._serialized_end=19791 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19639 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19681 + _WAITSENDPAYREQUEST._serialized_start=19794 + _WAITSENDPAYREQUEST._serialized_end=19936 + _WAITSENDPAYRESPONSE._serialized_start=19939 + _WAITSENDPAYRESPONSE._serialized_end=20457 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20316 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20349 + _NEWADDRREQUEST._serialized_start=20460 + _NEWADDRREQUEST._serialized_end=20618 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20544 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20602 + _NEWADDRRESPONSE._serialized_start=20620 + _NEWADDRRESPONSE._serialized_end=20711 + _WITHDRAWREQUEST._serialized_start=20714 + _WITHDRAWREQUEST._serialized_end=20916 + _WITHDRAWRESPONSE._serialized_start=20918 + _WITHDRAWRESPONSE._serialized_end=20976 + _KEYSENDREQUEST._serialized_start=20979 + _KEYSENDREQUEST._serialized_end=21311 + _KEYSENDRESPONSE._serialized_start=21314 + _KEYSENDRESPONSE._serialized_end=21684 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21608 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21637 + _KEYSENDEXTRATLVS._serialized_start=21686 + _KEYSENDEXTRATLVS._serialized_end=21704 + _FUNDPSBTREQUEST._serialized_start=21707 + _FUNDPSBTREQUEST._serialized_end=22018 + _FUNDPSBTRESPONSE._serialized_start=22021 + _FUNDPSBTRESPONSE._serialized_end=22238 + _FUNDPSBTRESERVATIONS._serialized_start=22240 + _FUNDPSBTRESERVATIONS._serialized_end=22357 + _SENDPSBTREQUEST._serialized_start=22359 + _SENDPSBTREQUEST._serialized_end=22424 + _SENDPSBTRESPONSE._serialized_start=22426 + _SENDPSBTRESPONSE._serialized_end=22470 + _SIGNPSBTREQUEST._serialized_start=22472 + _SIGNPSBTREQUEST._serialized_end=22521 + _SIGNPSBTRESPONSE._serialized_start=22523 + _SIGNPSBTRESPONSE._serialized_end=22562 + _UTXOPSBTREQUEST._serialized_start=22565 + _UTXOPSBTREQUEST._serialized_end=22912 + _UTXOPSBTRESPONSE._serialized_start=22915 + _UTXOPSBTRESPONSE._serialized_end=23132 + _UTXOPSBTRESERVATIONS._serialized_start=23134 + _UTXOPSBTRESERVATIONS._serialized_end=23251 + _TXDISCARDREQUEST._serialized_start=23253 + _TXDISCARDREQUEST._serialized_end=23285 + _TXDISCARDRESPONSE._serialized_start=23287 + _TXDISCARDRESPONSE._serialized_end=23341 + _TXPREPAREREQUEST._serialized_start=23344 + _TXPREPAREREQUEST._serialized_end=23508 + _TXPREPARERESPONSE._serialized_start=23510 + _TXPREPARERESPONSE._serialized_end=23578 + _TXSENDREQUEST._serialized_start=23580 + _TXSENDREQUEST._serialized_end=23609 + _TXSENDRESPONSE._serialized_start=23611 + _TXSENDRESPONSE._serialized_end=23667 + _DISCONNECTREQUEST._serialized_start=23669 + _DISCONNECTREQUEST._serialized_end=23730 + _DISCONNECTRESPONSE._serialized_start=23732 + _DISCONNECTRESPONSE._serialized_end=23752 + _FEERATESREQUEST._serialized_start=23754 + _FEERATESREQUEST._serialized_end=23861 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23824 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23861 + _FEERATESRESPONSE._serialized_start=23863 + _FEERATESRESPONSE._serialized_end=23949 + _FEERATESPERKB._serialized_start=23952 + _FEERATESPERKB._serialized_end=24275 + _FEERATESPERKW._serialized_start=24278 + _FEERATESPERKW._serialized_end=24601 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24604 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24797 + _FUNDCHANNELREQUEST._serialized_start=24800 + _FUNDCHANNELREQUEST._serialized_end=25238 + _FUNDCHANNELRESPONSE._serialized_start=25241 + _FUNDCHANNELRESPONSE._serialized_end=25396 + _GETROUTEREQUEST._serialized_start=25399 + _GETROUTEREQUEST._serialized_end=25635 + _GETROUTERESPONSE._serialized_start=25637 + _GETROUTERESPONSE._serialized_end=25690 + _GETROUTEROUTE._serialized_start=25693 + _GETROUTEROUTE._serialized_end=25890 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25861 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25890 + _LISTFORWARDSREQUEST._serialized_start=25893 + _LISTFORWARDSREQUEST._serialized_end=26151 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26033 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26109 + _LISTFORWARDSRESPONSE._serialized_start=26153 + _LISTFORWARDSRESPONSE._serialized_end=26220 + _LISTFORWARDSFORWARDS._serialized_start=26223 + _LISTFORWARDSFORWARDS._serialized_end=26791 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26588 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26672 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26674 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26722 + _LISTPAYSREQUEST._serialized_start=26794 + _LISTPAYSREQUEST._serialized_end=27013 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26919 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26974 + _LISTPAYSRESPONSE._serialized_start=27015 + _LISTPAYSRESPONSE._serialized_end=27066 + _LISTPAYSPAYS._serialized_start=27069 + _LISTPAYSPAYS._serialized_end=27578 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27403 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27462 + _PINGREQUEST._serialized_start=27580 + _PINGREQUEST._serialized_end=27669 + _PINGRESPONSE._serialized_start=27671 + _PINGRESPONSE._serialized_end=27701 + _SIGNMESSAGEREQUEST._serialized_start=27703 + _SIGNMESSAGEREQUEST._serialized_end=27740 + _SIGNMESSAGERESPONSE._serialized_start=27742 + _SIGNMESSAGERESPONSE._serialized_end=27812 + _STOPREQUEST._serialized_start=27814 + _STOPREQUEST._serialized_end=27827 + _STOPRESPONSE._serialized_start=27829 + _STOPRESPONSE._serialized_end=27843 + _NODE._serialized_start=27846 + _NODE._serialized_end=30774 # @@protoc_insertion_point(module_scope) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 1a5961802349..34cb73ef9ff0 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -71,9 +71,13 @@ On success, an object containing **peers** is returned. It is an array of objec - **private** (boolean, optional): if False, we will not announce this channel - **closer** (string, optional): Who initiated the channel close (one of "local", "remote") - **funding** (object, optional): - - **local_msat** (msat): Amount of channel we funded - - **remote_msat** (msat): Amount of channel they funded - - **pushed_msat** (msat): Amount pushed from opener to peer + - **local_funds_msat** (msat): Amount of channel we funded + - **remote_funds_msat** (msat): Amount of channel they funded + - **local_msat** (msat, optional): Amount of channel we funded (deprecated) + - **remote_msat** (msat, optional): Amount of channel they funded (deprecated) + - **pushed_msat** (msat, optional): Amount pushed from opener to peer + - **fee_paid_msat** (msat, optional): Amount we paid peer at open + - **fee_rcvd_msat** (msat, optional): Amount we were paid by peer at open - **to_us_msat** (msat, optional): how much of channel is owed to us - **min_to_us_msat** (msat, optional): least amount owed to us ever - **max_to_us_msat** (msat, optional): most amount owed to us ever @@ -384,4 +388,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:c26d3094925387ee935efc6351400bca374c361e5956ff13f10b228773f8e389) +[comment]: # ( SHA256STAMP:971c71befa1b6968a66b65e2aab4a2d1707f14c9af5d6ebc2dc00604d1d91294) diff --git a/doc/schemas/listpeers.schema.json b/doc/schemas/listpeers.schema.json index c526744f17ec..7a5bf6ece874 100644 --- a/doc/schemas/listpeers.schema.json +++ b/doc/schemas/listpeers.schema.json @@ -337,22 +337,37 @@ "type": "object", "additionalProperties": false, "required": [ - "local_msat", - "remote_msat", - "pushed_msat" + "local_funds_msat", + "remote_funds_msat" ], "properties": { "local_msat": { "type": "msat", - "description": "Amount of channel we funded" + "description": "Amount of channel we funded (deprecated)" }, "remote_msat": { "type": "msat", - "description": "Amount of channel they funded" + "description": "Amount of channel they funded (deprecated)" }, "pushed_msat": { "type": "msat", "description": "Amount pushed from opener to peer" + }, + "local_funds_msat": { + "type": "msat", + "description": "Amount of channel we funded" + }, + "remote_funds_msat": { + "type": "msat", + "description": "Amount of channel they funded" + }, + "fee_paid_msat": { + "type": "msat", + "description": "Amount we paid peer at open" + }, + "fee_rcvd_msat": { + "type": "msat", + "description": "Amount we were paid by peer at open" } } }, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index edf1240351cf..2b2365247fee 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -679,7 +679,7 @@ static void json_add_channel(struct lightningd *ld, const struct channel *channel) { struct channel_stats channel_stats; - struct amount_msat funding_msat, peer_msats, our_msats; + struct amount_msat funding_msat; struct amount_sat peer_funded_sats; struct state_change_entry *state_changes; u32 feerate; @@ -831,25 +831,73 @@ static void json_add_channel(struct lightningd *ld, &channel->our_funds)); peer_funded_sats = AMOUNT_SAT(0); } - if (!amount_sat_to_msat(&peer_msats, peer_funded_sats)) { - log_broken(channel->log, - "Overflow converting peer sats %s to msat", - type_to_string(tmpctx, struct amount_sat, - &peer_funded_sats)); - peer_msats = AMOUNT_MSAT(0); + + json_object_start(response, "funding"); + + if (deprecated_apis) { + json_add_sat_only(response, "local_msat", channel->our_funds); + json_add_sat_only(response, "remote_msat", peer_funded_sats); + json_add_amount_msat_only(response, "pushed_msat", channel->push); } - if (!amount_sat_to_msat(&our_msats, channel->our_funds)) { - log_broken(channel->log, - "Overflow converting peer sats %s to msat", - type_to_string(tmpctx, struct amount_sat, - &channel->our_funds)); - our_msats = AMOUNT_MSAT(0); + + if (channel->lease_commit_sig) { + struct amount_sat funds, total; + if (!amount_msat_to_sat(&funds, channel->push)) { + log_broken(channel->log, + "Can't convert channel->push %s to sats" + " (lease fees?)", + type_to_string(tmpctx, struct amount_msat, + &channel->push)); + funds = AMOUNT_SAT(0); + } + + if (channel->opener == LOCAL) { + if (!amount_sat_add(&total, funds, channel->our_funds)) { + log_broken(channel->log, + "Overflow adding our_funds to push"); + total = channel->our_funds; + } + json_add_sat_only(response, "local_funds_msat", total); + + if (!amount_sat_sub(&total, peer_funded_sats, funds)) { + log_broken(channel->log, + "Underflow sub'ing push from" + " peer's funds"); + total = peer_funded_sats; + } + json_add_sat_only(response, "remote_funds_msat", total); + + json_add_amount_msat_only(response, "fee_paid_msat", + channel->push); + } else { + if (!amount_sat_add(&total, peer_funded_sats, funds)) { + log_broken(channel->log, + "Overflow adding peer funds to push"); + total = peer_funded_sats; + } + json_add_sat_only(response, "remote_funds_msat", total); + + if (!amount_sat_sub(&total, channel->our_funds, funds)) { + log_broken(channel->log, + "Underflow sub'ing push from" + " our_funds"); + total = channel->our_funds; + } + json_add_sat_only(response, "local_funds_msat", total); + json_add_amount_msat_only(response, "fee_rcvd_msat", + channel->push); + } + + } else { + json_add_sat_only(response, "local_funds_msat", + channel->our_funds); + json_add_sat_only(response, "remote_funds_msat", + peer_funded_sats); + if (!deprecated_apis) + json_add_amount_msat_only(response, "pushed_msat", + channel->push); } - json_object_start(response, "funding"); - json_add_sat_only(response, "local_msat", channel->our_funds); - json_add_sat_only(response, "remote_msat", peer_funded_sats); - json_add_amount_msat_only(response, "pushed_msat", channel->push); json_object_end(response); if (!amount_sat_to_msat(&funding_msat, channel->funding_sats)) { diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index ed0d4f80f56f..7af8916a23b9 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -546,6 +546,62 @@ static void try_update_open_fees(struct command *cmd, } +static void find_push_amts(const char *buf, + const jsmntok_t *curr_chan, + bool is_opener, + struct amount_msat *push_credit, + struct amount_msat *push_debit, + bool *is_leased) +{ + const char *err; + struct amount_msat push_amt; + + /* Try to pull out fee_rcvd_msat */ + err = json_scan(tmpctx, buf, curr_chan, + "{funding:{fee_rcvd_msat:%}}", + JSON_SCAN(json_to_msat, + push_credit)); + + if (!err) { + *is_leased = true; + *push_debit = AMOUNT_MSAT(0); + return; + } + + /* Try to pull out fee_paid_msat */ + err = json_scan(tmpctx, buf, curr_chan, + "{funding:{fee_paid_msat:%}}", + JSON_SCAN(json_to_msat, + push_debit)); + if (!err) { + *is_leased = true; + *push_credit = AMOUNT_MSAT(0); + return; + } + + /* Try to pull out pushed amt? */ + err = json_scan(tmpctx, buf, curr_chan, + "{funding:{pushed_msat:%}}", + JSON_SCAN(json_to_msat, &push_amt)); + + if (!err) { + *is_leased = false; + if (is_opener) { + *push_credit = AMOUNT_MSAT(0); + *push_debit = push_amt; + } else { + *push_credit = push_amt; + *push_debit = AMOUNT_MSAT(0); + } + return; + } + + /* Nothing pushed nor fees paid */ + *is_leased = false; + *push_credit = AMOUNT_MSAT(0); + *push_debit = AMOUNT_MSAT(0); +} + static bool new_missed_channel_account(struct command *cmd, const char *buf, const jsmntok_t *result, @@ -579,26 +635,24 @@ static bool new_missed_channel_account(struct command *cmd, assert(chan_arr_tok->type == JSMN_ARRAY); json_for_each_arr(j, curr_chan, chan_arr_tok) { struct bitcoin_outpoint opt; - struct amount_msat amt, remote_amt, push_amt, + struct amount_msat amt, remote_amt, push_credit, push_debit; char *opener, *chan_id; enum mvt_tag *tags; - bool ok; + bool ok, is_opener, is_leased; err = json_scan(tmpctx, buf, curr_chan, "{channel_id:%," "funding_txid:%," "funding_outnum:%," - "funding:{local_msat:%," - "remote_msat:%," - "pushed_msat:%}," + "funding:{local_funds_msat:%," + "remote_funds_msat:%}," "opener:%}", JSON_SCAN_TAL(tmpctx, json_strdup, &chan_id), JSON_SCAN(json_to_txid, &opt.txid), JSON_SCAN(json_to_number, &opt.n), JSON_SCAN(json_to_msat, &amt), JSON_SCAN(json_to_msat, &remote_amt), - JSON_SCAN(json_to_msat, &push_amt), JSON_SCAN_TAL(tmpctx, json_strdup, &opener)); if (err) plugin_err(cmd->plugin, @@ -615,7 +669,8 @@ static bool new_missed_channel_account(struct command *cmd, chain_ev = tal(cmd, struct chain_event); chain_ev->tag = mvt_tag_str(CHANNEL_OPEN); chain_ev->debit = AMOUNT_MSAT(0); - ok = amount_msat_add(&chain_ev->output_value, amt, remote_amt); + ok = amount_msat_add(&chain_ev->output_value, + amt, remote_amt); assert(ok); chain_ev->currency = tal_strdup(chain_ev, currency); chain_ev->origin_acct = NULL; @@ -633,24 +688,18 @@ static bool new_missed_channel_account(struct command *cmd, tags = tal_arr(chain_ev, enum mvt_tag, 1); tags[0] = CHANNEL_OPEN; + is_opener = streq(opener, "local"); + /* Leased/pushed channels have some extra work */ - if (streq(opener, "local")) { - tal_arr_expand(&tags, OPENER); - ok = amount_msat_add(&amt, amt, push_amt); - push_credit = AMOUNT_MSAT(0); - push_debit = push_amt; - } else { - ok = amount_msat_sub(&amt, amt, push_amt); - push_credit = push_amt; - push_debit = AMOUNT_MSAT(0); - } + find_push_amts(buf, curr_chan, is_opener, + &push_credit, &push_debit, + &is_leased); - /* We assume pushes are all leases, even - * though they might just be pushes */ - if (!amount_msat_zero(push_amt)) + if (is_leased) tal_arr_expand(&tags, LEASED); + if (is_opener) + tal_arr_expand(&tags, OPENER); - assert(ok); chain_ev->credit = amt; db_begin_transaction(db); if (!log_chain_event(db, acct, chain_ev)) @@ -668,12 +717,15 @@ static bool new_missed_channel_account(struct command *cmd, try_update_open_fees(cmd, acct); /* We log a channel event for the push amt */ - if (!amount_msat_zero(push_amt)) { + if (!amount_msat_zero(push_credit) + || !amount_msat_zero(push_debit)) { struct channel_event *chan_ev; char *chan_tag; chan_tag = tal_fmt(tmpctx, "%s", - mvt_tag_str(LEASE_FEE)); + mvt_tag_str( + is_leased ? + LEASE_FEE : PUSHED)); chan_ev = new_channel_event(tmpctx, chan_tag, diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 0f10272fe7de..4a18813fb0fd 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -1,11 +1,12 @@ from fixtures import * # noqa: F401,F403 from decimal import Decimal -from pyln.client import Millisatoshi, RpcError +from pyln.client import Millisatoshi from fixtures import TEST_NETWORK from utils import ( - sync_blockheight, wait_for, only_one + sync_blockheight, wait_for, only_one, first_channel_id ) +from pathlib import Path import os import pytest import unittest @@ -324,6 +325,144 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert len(fees) == 1 +@pytest.mark.openchannel('v2') +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") +@unittest.skipIf(TEST_NETWORK != 'regtest', "network fees hardcoded") +def test_bookkeeping_missed_chans_leases(node_factory, bitcoind): + """ + Test that a lease is correctly recorded if bookkeeper was off + """ + + coin_mvt_plugin = Path(__file__).parent / "plugins" / "coin_movements.py" + opts = {'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, + 'plugin': str(coin_mvt_plugin), + 'disable-plugin': 'bookkeeper'} + l1, l2 = node_factory.get_nodes(2, opts=opts) + + open_amt = 500000 + feerate = 2000 + lease_fee = 6432000 + invoice_msat = 11000000 + + l1.fundwallet(open_amt * 1000) + l2.fundwallet(open_amt * 1000) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + # l1 leases a channel from l2 + compact_lease = l2.rpc.funderupdate()['compact_lease'] + txid = l1.rpc.fundchannel(l2.info['id'], open_amt, request_amt=open_amt, + feerate='{}perkw'.format(feerate), + compact_lease=compact_lease)['txid'] + bitcoind.generate_block(1, wait_for_mempool=[txid]) + wait_for(lambda: l1.channel_state(l2) == 'CHANNELD_NORMAL') + scid = l1.get_channel_scid(l2) + l1.wait_channel_active(scid) + channel_id = first_channel_id(l1, l2) + + l1.pay(l2, invoice_msat) + l1.daemon.wait_for_log(r'coin movement:.*\'invoice\'') + + # Now turn the bookkeeper on and restart + l1.stop() + l2.stop() + del l1.daemon.opts['disable-plugin'] + del l2.daemon.opts['disable-plugin'] + l1.start() + l2.start() + + # Wait for the balance snapshot to fire/finish + l1.daemon.wait_for_log('Snapshot balances updated') + l2.daemon.wait_for_log('Snapshot balances updated') + + def _check_events(node, channel_id, exp_events): + chan_events = [ev for ev in node.rpc.bkpr_listaccountevents()['events'] if ev['account'] == channel_id] + assert len(chan_events) == len(exp_events) + for ev, exp in zip(chan_events, exp_events): + assert ev['tag'] == exp[0] + assert ev['credit_msat'] == Millisatoshi(exp[1]) + assert ev['debit_msat'] == Millisatoshi(exp[2]) + + # l1 events + exp_events = [('channel_open', open_amt * 1000 + lease_fee, 0), + ('onchain_fee', 1408000, 0), + ('lease_fee', 0, lease_fee), + ('journal_entry', 0, invoice_msat)] + _check_events(l1, channel_id, exp_events) + + exp_events = [('channel_open', open_amt * 1000, 0), + ('onchain_fee', 980000, 0), + ('lease_fee', lease_fee, 0), + ('journal_entry', invoice_msat, 0)] + _check_events(l2, channel_id, exp_events) + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") +@unittest.skipIf(TEST_NETWORK != 'regtest', "network fees hardcoded") +@pytest.mark.openchannel('v1', 'Uses push-msat') +def test_bookkeeping_missed_chans_pushed(node_factory, bitcoind): + """ + Test for a push_msat value in a missed channel open. + """ + coin_mvt_plugin = Path(__file__).parent / "plugins" / "coin_movements.py" + l1, l2 = node_factory.get_nodes(2, opts={'disable-plugin': 'bookkeeper', + 'plugin': str(coin_mvt_plugin)}) + + # Double check there's no bookkeeper plugin on + assert l1.daemon.opts['disable-plugin'] == 'bookkeeper' + assert l2.daemon.opts['disable-plugin'] == 'bookkeeper' + + open_amt = 10**7 + push_amt = 10**6 * 1000 + invoice_msat = 11000000 + + l1.fundwallet(200000000) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + txid = l1.rpc.fundchannel(l2.info['id'], open_amt, push_msat=push_amt)['txid'] + bitcoind.generate_block(1, wait_for_mempool=[txid]) + wait_for(lambda: l1.channel_state(l2) == 'CHANNELD_NORMAL') + scid = l1.get_channel_scid(l2) + l1.wait_channel_active(scid) + channel_id = first_channel_id(l1, l2) + + # Send l2 funds via the channel + l1.pay(l2, invoice_msat) + l1.daemon.wait_for_log(r'coin movement:.*\'invoice\'') + + # Now turn the bookkeeper on and restart + l1.stop() + l2.stop() + del l1.daemon.opts['disable-plugin'] + del l2.daemon.opts['disable-plugin'] + l1.start() + l2.start() + + # Wait for the balance snapshot to fire/finish + l1.daemon.wait_for_log('Snapshot balances updated') + l2.daemon.wait_for_log('Snapshot balances updated') + + def _check_events(node, channel_id, exp_events): + chan_events = [ev for ev in node.rpc.bkpr_listaccountevents()['events'] if ev['account'] == channel_id] + assert len(chan_events) == len(exp_events) + for ev, exp in zip(chan_events, exp_events): + assert ev['tag'] == exp[0] + assert ev['credit_msat'] == Millisatoshi(exp[1]) + assert ev['debit_msat'] == Millisatoshi(exp[2]) + + # l1 events + exp_events = [('channel_open', open_amt * 1000, 0), + ('onchain_fee', 5257000, 0), + ('pushed', 0, push_amt), + ('journal_entry', 0, invoice_msat)] + _check_events(l1, channel_id, exp_events) + + # l2 events + exp_events = [('channel_open', 0, 0), + ('pushed', push_amt, 0), + ('journal_entry', invoice_msat, 0)] + _check_events(l2, channel_id, exp_events) + + @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") def test_bookkeeping_onchaind_txs(node_factory, bitcoind): """ @@ -336,8 +475,6 @@ def test_bookkeeping_onchaind_txs(node_factory, bitcoind): # Double check there's no bookkeeper plugin on assert l1.daemon.opts['disable-plugin'] == 'bookkeeper' - with pytest.raises(RpcError): - l1.rpc.listincome() # Send l2 funds via the channel l1.pay(l2, 11000000) diff --git a/tests/test_closing.py b/tests/test_closing.py index be7f82f403ae..bf3804aec893 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -801,7 +801,9 @@ def test_channel_lease_post_expiry(node_factory, bitcoind, chainparams): # This should be the accepter's amount fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] - assert Millisatoshi(est_fees + amount * 1000) == Millisatoshi(fundings['remote_msat']) + assert Millisatoshi(amount * 1000) == fundings['remote_funds_msat'] + assert Millisatoshi(est_fees + amount * 1000) == fundings['local_funds_msat'] + assert Millisatoshi(est_fees) == fundings['fee_paid_msat'] bitcoind.generate_block(6) l1.daemon.wait_for_log('to CHANNELD_NORMAL') @@ -925,7 +927,8 @@ def test_channel_lease_unilat_closes(node_factory, bitcoind): # This should be the accepter's amount fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] - assert Millisatoshi(est_fees + amount * 1000) == Millisatoshi(fundings['remote_msat']) + assert Millisatoshi(amount * 1000) == Millisatoshi(fundings['remote_funds_msat']) + assert Millisatoshi(est_fees + amount * 1000) == Millisatoshi(fundings['local_funds_msat']) bitcoind.generate_block(6) l1.daemon.wait_for_log('to CHANNELD_NORMAL') diff --git a/tests/test_connection.py b/tests/test_connection.py index a570a0497d01..477a9775f6ed 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3391,7 +3391,7 @@ def test_wumbo_channels(node_factory, bitcoind): wait_for(lambda: 'CHANNELD_NORMAL' in [c['state'] for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']]) # Exact amount depends on fees, but it will be wumbo! - amount = [c['funding']['local_msat'] for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'] if c['state'] == 'CHANNELD_NORMAL'][0] + amount = [c['funding']['local_funds_msat'] for c in only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'] if c['state'] == 'CHANNELD_NORMAL'][0] assert amount > Millisatoshi(str((1 << 24) - 1) + "sat") diff --git a/tests/test_opening.py b/tests/test_opening.py index 96db12fc08c7..6a49bd814b9b 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -376,7 +376,10 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): # This should be the accepter's amount fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] - assert Millisatoshi(est_fees + amount * 1000) == Millisatoshi(fundings['remote_msat']) + assert Millisatoshi(amount * 1000) == fundings['remote_funds_msat'] + assert Millisatoshi(est_fees + amount * 1000) == fundings['local_funds_msat'] + assert Millisatoshi(est_fees) == fundings['fee_paid_msat'] + assert 'fee_rcvd_msat' not in fundings # rbf the lease with a higher amount rate = int(find_next_feerate(l1, l2)[:-5]) @@ -406,7 +409,7 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams): # This should be the accepter's amount fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding'] # FIXME: The lease goes away :( - assert Millisatoshi(0) == Millisatoshi(fundings['remote_msat']) + assert Millisatoshi(0) == Millisatoshi(fundings['remote_funds_msat']) wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(l1.get_channel_scid(l2))['channels']] == [True, True]) @@ -1103,8 +1106,8 @@ def test_funder_options(node_factory, bitcoind): l2.fundchannel(l1, 10**6) chan_info = only_one(only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['channels']) # l1 contributed nothing - assert chan_info['funding']['remote_msat'] == Millisatoshi('0msat') - assert chan_info['funding']['local_msat'] != Millisatoshi('0msat') + assert chan_info['funding']['remote_funds_msat'] == Millisatoshi('0msat') + assert chan_info['funding']['local_funds_msat'] != Millisatoshi('0msat') # Change all the options funder_opts = l1.rpc.call('funderupdate', @@ -1136,8 +1139,8 @@ def test_funder_options(node_factory, bitcoind): l3.fundchannel(l1, 10**6) chan_info = only_one(only_one(l3.rpc.listpeers(l1.info['id'])['peers'])['channels']) # l1 contributed all its funds! - assert chan_info['funding']['remote_msat'] == Millisatoshi('9994255000msat') - assert chan_info['funding']['local_msat'] == Millisatoshi('1000000000msat') + assert chan_info['funding']['remote_funds_msat'] == Millisatoshi('9994255000msat') + assert chan_info['funding']['local_funds_msat'] == Millisatoshi('1000000000msat') @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') From 3445882ee450eb4cc26f895f5222c4714112cf2a Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 28 Jul 2022 23:01:00 -0500 Subject: [PATCH 1218/1530] bkpr: use long-uint not size_t for time_t Reported-By: @endothermicdev --- plugins/bkpr/incomestmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index ff65fec5544b..e26dcf452ce5 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -437,9 +437,9 @@ void json_add_income_event(struct json_stream *out, struct income_event *ev) const char *csv_filename(const tal_t *ctx, const struct csv_fmt *fmt) { - return tal_fmt(ctx, "cln_incomestmt_%s_%zu.csv", + return tal_fmt(ctx, "cln_incomestmt_%s_%lu.csv", fmt->fmt_name, - time_now().ts.tv_sec); + (unsigned long)time_now().ts.tv_sec); } static char *convert_asset_type(struct income_event *ev) From 2971b2af79d7ab5a3e67cfe27ac735cf7e9612ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 1 Aug 2022 09:55:05 +0930 Subject: [PATCH 1219/1530] bkpr: insert obscure 60s pop references. The initial snapshots on an already-running lightningd are expected to be unbalanced, but this shouldn't cause users to long for the green, green grass of home. This controls the Art of Noise. Signed-off-by: Rusty Russell --- db/utils.c | 2 +- db/utils.h | 2 +- plugins/bkpr/bookkeeper.c | 8 ++++++-- plugins/bkpr/db.c | 13 ++++++++----- plugins/bkpr/db.h | 3 ++- plugins/bkpr/test/run-bkpr_db.c | 14 +++++++++++--- plugins/bkpr/test/run-recorder.c | 21 ++++++++++++++------- 7 files changed, 43 insertions(+), 20 deletions(-) diff --git a/db/utils.c b/db/utils.c index 11d2654f655d..96f442fbbcec 100644 --- a/db/utils.c +++ b/db/utils.c @@ -290,7 +290,7 @@ void db_prepare_for_changes(struct db *db) db->changes = tal_arr(db, const char *, 0); } -struct db *db_open(const tal_t *ctx, char *filename) +struct db *db_open(const tal_t *ctx, const char *filename) { struct db *db; diff --git a/db/utils.h b/db/utils.h index 2b5a21dd7571..308a302f9091 100644 --- a/db/utils.h +++ b/db/utils.h @@ -80,7 +80,7 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, /** * db_open - Open or create a database */ -struct db *db_open(const tal_t *ctx, char *filename); +struct db *db_open(const tal_t *ctx, const char *filename); /** * Report a statement that changes the wallet diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 7af8916a23b9..84fade03687f 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -36,6 +36,7 @@ static struct db *db ; static char *db_dsn; static char *datadir; +static bool tom_jones; static struct fee_sum *find_sum_for_txid(struct fee_sum **sums, struct bitcoin_txid *txid) @@ -1061,7 +1062,9 @@ static struct command_result *json_balance_snapshot(struct command *cmd, struct channel_event *ev; u64 timestamp; - plugin_log(cmd->plugin, LOG_UNUSUAL, + /* This is *expected* on first run of bookkeeper! */ + plugin_log(cmd->plugin, + tom_jones ? LOG_DBG : LOG_UNUSUAL, "Snapshot balance does not equal ondisk" " reported %s, off by (+%s/-%s) (account %s)" " Logging journal entry.", @@ -1838,7 +1841,8 @@ static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) db_dsn = tal_fmt(NULL, "sqlite3://accounts.sqlite3"); plugin_log(p, LOG_DBG, "Setting up database at %s", db_dsn); - db = notleak(db_setup(p, p, db_dsn)); + /* Final flag tells us What's New, Pussycat. */ + db = notleak(db_setup(p, p, db_dsn, &tom_jones)); db_dsn = tal_free(db_dsn); return NULL; diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index fdd9ebd2b455..1db77327b06f 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -100,7 +100,7 @@ static struct migration db_migrations[] = { {SQL("ALTER TABLE channel_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, }; -static bool db_migrate(struct plugin *p, struct db *db) +static bool db_migrate(struct plugin *p, struct db *db, bool *created) { /* Read current version from database */ int current, orig, available; @@ -109,9 +109,11 @@ static bool db_migrate(struct plugin *p, struct db *db) orig = current = db_get_version(db); available = ARRAY_SIZE(db_migrations) - 1; - if (current == -1) + *created = false; + if (current == -1) { + *created = true; plugin_log(p, LOG_INFORM, "Creating database"); - else if (available < current) + } else if (available < current) plugin_err(p, "Refusing to migrate down from version %u to %u", current, available); @@ -154,7 +156,8 @@ void db_fatal(const char *fmt, ...) } #endif /* DB_FATAL */ -struct db *db_setup(const tal_t *ctx, struct plugin *p, char *db_dsn) +struct db *db_setup(const tal_t *ctx, struct plugin *p, + const char *db_dsn, bool *created) { bool migrated; struct db *db; @@ -165,7 +168,7 @@ struct db *db_setup(const tal_t *ctx, struct plugin *p, char *db_dsn) db->report_changes_fn = NULL; db_begin_transaction(db); - migrated = db_migrate(p, db); + migrated = db_migrate(p, db, created); db->data_version = db_data_version_get(db); db_commit_transaction(db); diff --git a/plugins/bkpr/db.h b/plugins/bkpr/db.h index 0c9663e941b5..b4eb50273690 100644 --- a/plugins/bkpr/db.h +++ b/plugins/bkpr/db.h @@ -6,6 +6,7 @@ struct plugin; struct db; -struct db *db_setup(const tal_t *ctx, struct plugin *p, char *db_dsn); +struct db *db_setup(const tal_t *ctx, struct plugin *p, const char *db_dsn, + bool *created); #endif /* LIGHTNING_PLUGINS_BKPR_DB_H */ diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index 198512849a5b..09116bcb0884 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -258,19 +258,27 @@ static struct db *create_test_db(void) return db; } -static bool test_empty_db_migrate(struct plugin *plugin) +static bool test_db_migrate(struct plugin *plugin) { struct db *db = create_test_db(); + bool created; + CHECK(db); db_begin_transaction(db); CHECK(db_get_version(db) == -1); - db_migrate(plugin, db); + CHECK(db_migrate(plugin, db, &created) == true); + CHECK(created); db_commit_transaction(db); db_begin_transaction(db); CHECK(db_get_version(db) == ARRAY_SIZE(db_migrations) - 1); db_commit_transaction(db); + db_begin_transaction(db); + CHECK(db_migrate(plugin, db, &created) == false); + CHECK(!created); + db_commit_transaction(db); + tal_free(db); return true; } @@ -284,7 +292,7 @@ int main(int argc, char *argv[]) common_setup(argv[0]); - ok &= test_empty_db_migrate(plugin); + ok &= test_db_migrate(plugin); tal_free(plugin); common_shutdown(); diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index c71f040fe71b..762b99dbb085 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -393,7 +393,8 @@ static struct chain_event *make_chain_event(const tal_t *ctx, static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); struct node_id node_id, peer_id; struct account *wal_acct, *ext_acct; struct bitcoin_txid txid; @@ -470,7 +471,8 @@ static bool test_onchain_fee_wallet_spend(const tal_t *ctx, struct plugin *p) static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); struct node_id node_id, peer_id; struct account *acct, *wal_acct, *ext_acct; struct onchain_fee **ofs, **ofs1; @@ -744,7 +746,8 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); struct node_id node_id, peer_id; struct account *acct, *acct2, *wal_acct, *ext_acct; struct bitcoin_txid txid; @@ -868,7 +871,8 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); struct node_id peer_id; struct account *acct, *acct2; struct channel_event *ev1, *ev2, *ev3, **chan_evs; @@ -954,7 +958,8 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); struct node_id peer_id; struct account *acct, *acct2; struct chain_event *ev1, *ev2, *ev3, **chain_evs; @@ -1088,7 +1093,8 @@ static bool test_chain_event_crud(const tal_t *ctx, struct plugin *p) static bool test_account_balances(const tal_t *ctx, struct plugin *p) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); struct node_id peer_id; struct account *acct, *acct2; struct chain_event *ev1; @@ -1193,7 +1199,8 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) static bool test_account_crud(const tal_t *ctx, struct plugin *p) { - struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); struct node_id *peer_id; struct account *acct, *acct2, **acct_list; struct chain_event *ev1; From dfa325dc4dc711479cbc7f9402e96e46b26950d8 Mon Sep 17 00:00:00 2001 From: niftynei Date: Sun, 31 Jul 2022 13:03:04 -0500 Subject: [PATCH 1220/1530] bkpr: make unit tests not fail if !HAVE_SQLITE3 We rely on sqlite3 being present to run unit tests for some bookkeeper tests; here we effectively disable these tests if not available Fixes report in #4928 Reported-By: @whitslack --- plugins/bkpr/test/run-bkpr_db.c | 4 +++- plugins/bkpr/test/run-recorder.c | 16 +++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index 09116bcb0884..dae0352404f7 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -292,7 +292,9 @@ int main(int argc, char *argv[]) common_setup(argv[0]); - ok &= test_db_migrate(plugin); + if (HAVE_SQLITE3) { + ok &= test_db_migrate(plugin); + } tal_free(plugin); common_shutdown(); diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 762b99dbb085..e2380b1a3e81 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -1315,13 +1315,15 @@ int main(int argc, char *argv[]) common_setup(argv[0]); - ok &= test_account_crud(tmpctx, plugin); - ok &= test_channel_event_crud(tmpctx, plugin); - ok &= test_chain_event_crud(tmpctx, plugin); - ok &= test_account_balances(tmpctx, plugin); - ok &= test_onchain_fee_chan_close(tmpctx, plugin); - ok &= test_onchain_fee_chan_open(tmpctx, plugin); - ok &= test_onchain_fee_wallet_spend(tmpctx, plugin); + if (HAVE_SQLITE3) { + ok &= test_account_crud(tmpctx, plugin); + ok &= test_channel_event_crud(tmpctx, plugin); + ok &= test_chain_event_crud(tmpctx, plugin); + ok &= test_account_balances(tmpctx, plugin); + ok &= test_onchain_fee_chan_close(tmpctx, plugin); + ok &= test_onchain_fee_chan_open(tmpctx, plugin); + ok &= test_onchain_fee_wallet_spend(tmpctx, plugin); + } tal_free(plugin); common_shutdown(); From 75ccce7808aa4acacbc9357bc3cb331190688e73 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 1 Aug 2022 17:10:45 -0500 Subject: [PATCH 1221/1530] devtools: if there's a message in the API call, print and exit If you've got bad credentials, you'll get an error message. --- devtools/changelog.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devtools/changelog.py b/devtools/changelog.py index dac8b535f7fa..73a6512ec3da 100755 --- a/devtools/changelog.py +++ b/devtools/changelog.py @@ -66,6 +66,10 @@ def get_log_entries(commitrange): url = 'https://api.github.com/repos/{repo}/commits/{commit}/pulls'.format(repo=repo, commit=commit) content = requests.get(url, headers=headers).json() if len(content): + # Check for bad credentials + if 'message' in content: + print(content) + exit() pullreq = content[0]['number'] else: pullreq = None From 31ba3007bdeecebecaa356948ca5d1249a672f95 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 1 Aug 2022 17:44:58 -0500 Subject: [PATCH 1222/1530] changelog: v0.12.0rc1 release notes Changelog for v0.12.0rc1 Changelog-None --- CHANGELOG.md | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7380f3b01b8..7ebead910280 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,172 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> +## [0.12.0rc1] - 2022-08-01: "TBD" + +This release named by @adi2011. + +### Added + + - *NEW*: `commando` a new builtin plugin to send/recv peer commands over the lightning network, using runes. ([#5370]) + - *NEW*: New built-in plugin`bookkeeper` w/ commands `bkpr-listaccountevents`, `bkpr-listbalances, bkpr-listincome, bkpr-channelsapy, bkpr-dumpincomecsv, bkpr-inspect ([#5071]) + - *NEW*: Static channel backup, to enable smooth fund recovery in case of complete data loss ([#5422]) + - logging: `log-level=debug:` supported to get debug-level logs for everything about a peer. ([#5349]) + - JSON-RPC: `connect` use the standard port derivation when the port is not specified. ([#5242]) + - JSON-RPC: `fetchinvoice` `changes` `amount_msat` ([#5306]) + - JSON-RPC: Added `mindepth` argument to specify the number of confirmations we require for `fundchannel` and `multifundchannel` ([#5275]) + - JSON-RPC: `listpeers` new fields for `funding` (`remote_funds_msat`, `local_funds_msat`, `fee_paid_msat`, `fee_rcvd_msat`). ([#5477]) + - JSON-RPC: `listpeers` add optional `remote_addr` ([#5244]) + - JSON-RPC: `listforwards` now shows `out_channel` in more cases: even if it couldn't actually send to it. ([#5330]) + - JSON-RPC: `pay` `attempts` `amount_msat` field. ([#5306]) + - Plugins: `channel_state_changed` now triggers for a v1 channel's initial "CHANNELD_AWAITING_LOCKIN" state transition (from prior state "unknown") ([#5381]) + - Plugins: `htlc_accepted_hook` `amount_msat` field. ([#5306]) + - Plugins: `htlc_accepted` now exposes the `short_channel_id` for the channel from which that HTLC is coming from and the low-level per-channel HTLC `id`, which are necessary for bridging two different Lightning Networks when MPP is involved. ([#5303]) + - Plugins: The `openchannel` hook may return a `mindepth` indicating how many confirmations are required. ([#5275]) + - msggen: introduce chain of responsibility pattern to make msggen extensible ([#5216]) + - cln_plugin: persist cln configuration from init msg ([#5279]) + - pyln-testing: Added utilities to read and parse `gossip_store` file for nodes. ([#5275]) + - `hsmtool`: new command `checkhsm` to check BIP39 passphrase against hsm_secret. ([#5441]) + - contrib: Added `fund_ln` to the contrib/startup\_regtest.sh ([#5062]) + - build: Added m1 architecture support for macos ([#4988]) + - build: Reproducible builds now include rust binaries such as the `cln-grpc` plugin ([#5421]) + + +### Changed + + - `lightningd`: will refuse to start with the wrong node_id (i.e. hsm_secret changes). ([#5425]) + - `connectd`: prefer IPv6 connections when available. ([#5244]) + - `connectd`: Only use IP discovery as fallback when no addresses would be announced ([#5344]) + - `connectd`: give busy peers more time to respond to pings. ([#5347]) + - `gossipd`: now accepts spam gossip, but squelches it for ([#5239]) + - gossip: gossip\_store updated to version 10. ([#5239]) + - logging: `log-file` option specified multiple times opens multiple log files. ([#5281]) + - JSON-RPC: `plugin start` now assumes relative path to default plugins dir if the path is not found in absolute context. i.e. lightning-cli plugin start my_plugin.py ([#5211]) + - JSON-RPC: `fundchannel`: now errors if you try to buy a liquidity ad but dont' have `experimental-dual-fund` enabled ([#5389]) + - JSON-RPC: "\_msat" fields can be raw numbers, not "123msat" strings (please handle both!) ([#5306]) + - JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice`: `msatoshi` argument is now called `amount_msat` to match other fields. ([#5306]) + + +### Deprecated + +Note: You should always set `allow-deprecated-apis=false` to test for changes. + + - JSON-RPC: `listpeers`.`funded` fields `local_msat` and `remote_msat`. ([#5477]) + - JSON-RPC: `listtransactions` `msat` (use `amount_msat`) ([#5306]) + - JSON-RPC: checkmessage return an error when the pubkey is not specified and it is unknown in the network graph. ([#5252]) + - JSON-RPC: "_msat" fields as "123msat" strings (will be only numbers) ([#5306]) + - JSONRPC: `sendpay` `route` elements `msatoshi` (use `amount_msat`) ([#5306]) + - JSON-RPC: `pay`, `decode`, `decodepay`, `getroute`, `listinvoices`, `listpays` and `listsendpays` `msatoshi` fields (use `amount_msat`). ([#5306]) + - JSON-RPC: `getinfo` `msatoshi_fees_collected` field (use `fees_collected_msat`). ([#5306]) + - JSON-RPC: `listpeers` `channels`: `msatoshi_to_us`, `msatoshi_to_us_min`, `msatoshi_to_us_max`, `msatoshi_total`, `dust_limit_satoshis`, `our_channel_reserve_satoshis`, `their_channel_reserve_satoshis`, `spendable_msatoshi`, `receivable_msatoshi`, `in_msatoshi_offered`, `in_msatoshi_fulfilled`, `out_msatoshi_offered`, `out_msatoshi_fulfilled`, `max_htlc_value_in_flight_msat` and `htlc_minimum_msat` (use `to_us_msat`, `min_to_us_msat`, `max_to_us_msat`, `total_msat`, `dust_limit_msat`, `our_reserve_msat`, `their_reserve_msat`, `spendable_msat`, `receivable_msat`, `in_offered_msat`, `in_fulfilled_msat`, `out_offered_msat`, `out_fulfilled_msat`, `max_total_htlc_in_msat` and `minimum_htlc_in_msat`). ([#5306]) + - JSON-RPC: `listinvoices` and `pay` `msatoshi_received` and `msatoshi_sent` (use `amount_received_msat`, `amount_sent_msat`) ([#5306]) + - JSON-RPC: `listpays` and `listsendpays` `msatoshi_sent` (use `amount_sent_msat`) ([#5306]) + - JSON-RPC: `listforwards` `in_msatoshi`, `out_msatoshi` and `fee` (use `in_msat`, `out_msat` and `fee_msat`) ([#5306]) + - JSON-RPC: `listfunds` `outputs` `value` (use `amount_msat`) ([#5306]) + - JSON-RPC: `fetchinvoice` `changes` `msat` (use `amount_msat`) ([#5306]) + - JSON-RPC: `pay` `attempts` `amount` field (use `amount_msat`). ([#5306]) + - JSON-RPC: `invoice`, `sendonion`, `sendpay`, `pay`, `keysend`, `fetchinvoice`, `sendinvoice` `msatoshi` (use `amount_msat`) ([#5306]) + - `listconfigs` `plugins` `options` which are not set are omitted, not `null`. ([#5306]) + - Plugins: `htlc_accepted_hook` `amount` field (use `amount_msat`) ([#5306]) + - Plugins: `coin_movement` notification: `balance`, `credit`, `debit` and `fees` (use `balance_msat`, `credit_msat`, `debit_msat` and `fees_msat`) ([#5306]) + - Plugins: `rbf_channel` and `openchannel2` hooks `their_funding` (use `their_funding_msat`) ([#5306]) + - Plugins: `openchannel2` hook `dust_limit_satoshis` (use `dust_limit_msat`) ([#5306]) + - Plugins: `openchannel` hook `funding_satoshis` (use `funding_msat`) ([#5306]) + - Plugins: `openchannel` hook `dust_limit_satoshis` (use `dust_limit_msat`) ([#5306]) + - Plugins: `openchannel` hook `channel_reserve_satoshis` (use `channel_reserve_msat`) ([#5306]) + - Plugins: `channel_opened` notification `amount` (use `funding_msat`) ([#5306]) + - Plugins: `htlc_accepted` `forward_amount` (use `forward_msat`) ([#5306]) + + +### Removed + + - Protocol: We no longer create gossip messages which use zlib encoding (we still understand them, for now!) ([#5226]) + - JSON-RPC: `getsharedsecret` API: use `makesecret` ([#5430]) + - JSON-RPC: removed `listtransactions` `outputs` `satoshis` field (deprecated v0.10.1) ([#5264]) + - JSON-RPC: removed `listpeers` `channels` deprecated fields (deprecated v0.10.1) ([#5264]) + - JSON-RPC: removed `listpeers` `channels` `closer` now omitted, rather than `null` (deprecated v0.10.1) ([#5264]) + - libhsmd: Removed the `libhsmd_python` wrapper as it was unused ([#5415]) + - lightningd: removed `enable-autotor-v2-mode` option (deprecated v0.10.1) ([#5264]) + + +### Fixed + + - `connectd`: various crashes and issues fixed by simplification and rewrite. ([#5261]) + - `connectd`: Port of a DNS announcement can be 0 if unspecified ([#5434]) + - `connectd`: no longer crashes when peers reconnect. ([#5300]) + - `connectd`: occasional crash when we reconnect to a peer quickly. ([#5340]) + - `connectd`: reduce initial CPU load when connecting to peers. ([#5328]) + - `connectd`: large memory usage with many peers fixed. ([#5312]) + - `dualopend`: Issue if the number of outputs decreases in a dualopen RBF or splice. ([#5378]) + - `channeld`: Enforce our own `minimum_depth` beyond just confirming ([#5275]) + - routing: Fixed an issue where we would exclude the entire channel if either direction was disabled, or we hadn't seen an update yet. ([#5286]) + - topology: Under some circumstances we were considering the limits on the wrong direction for a channel ([#5286]) + - logging: `log-prefix` now correctly prefixes *all* log messages. ([#5349]) + - logging: `log-level` `io` shows JSONRPC output, as well as input. ([#5306]) + - interop: treat LND "internal error" as warnings, not force close events (as we did in v0.10). ([#5326]) + - PSBT: Fix signature encoding to comply with BIP-0171. ([#5307]) + - signmessage: improve the UX of the rpc command when zbase is not a valid one ([#5297]) + - JSON-RPC: Adds dynamically detected public IP addresses to `getinfo` ([#5244]) + - cln-rpc: naming mismatch for `ConnectPeer` causing `connectpeer` to be called on the JSON-RPC ([#5362]) + - pyln-spec: update the bolts implementation ([#5168]) + - Plugins: setting the default value of a parameter to `null` is the same as not setting it (pyln plugins did this!). ([#5460]) + - Plugins: plugins would hang indefinitely despite `lightningd` closing the connection ([#5362]) + - Upgrade docker base image from Debian buster to bullseye to work with glibc 2.29+ #5276 ([#5278]) + - docker: The docker images are now built with the rust plugins `cln-grpc` ([#5270]) + + +### EXPERIMENTAL + + - None. + +[#4988]: https://github.com/ElementsProject/lightning/pull/4988 +[#5062]: https://github.com/ElementsProject/lightning/pull/5062 +[#5071]: https://github.com/ElementsProject/lightning/pull/5071 +[#5168]: https://github.com/ElementsProject/lightning/pull/5168 +[#5211]: https://github.com/ElementsProject/lightning/pull/5211 +[#5216]: https://github.com/ElementsProject/lightning/pull/5216 +[#5226]: https://github.com/ElementsProject/lightning/pull/5226 +[#5239]: https://github.com/ElementsProject/lightning/pull/5239 +[#5242]: https://github.com/ElementsProject/lightning/pull/5242 +[#5244]: https://github.com/ElementsProject/lightning/pull/5244 +[#5252]: https://github.com/ElementsProject/lightning/pull/5252 +[#5261]: https://github.com/ElementsProject/lightning/pull/5261 +[#5264]: https://github.com/ElementsProject/lightning/pull/5264 +[#5270]: https://github.com/ElementsProject/lightning/pull/5270 +[#5275]: https://github.com/ElementsProject/lightning/pull/5275 +[#5278]: https://github.com/ElementsProject/lightning/pull/5278 +[#5279]: https://github.com/ElementsProject/lightning/pull/5279 +[#5281]: https://github.com/ElementsProject/lightning/pull/5281 +[#5286]: https://github.com/ElementsProject/lightning/pull/5286 +[#5297]: https://github.com/ElementsProject/lightning/pull/5297 +[#5300]: https://github.com/ElementsProject/lightning/pull/5300 +[#5303]: https://github.com/ElementsProject/lightning/pull/5303 +[#5306]: https://github.com/ElementsProject/lightning/pull/5306 +[#5307]: https://github.com/ElementsProject/lightning/pull/5307 +[#5312]: https://github.com/ElementsProject/lightning/pull/5312 +[#5326]: https://github.com/ElementsProject/lightning/pull/5326 +[#5328]: https://github.com/ElementsProject/lightning/pull/5328 +[#5330]: https://github.com/ElementsProject/lightning/pull/5330 +[#5340]: https://github.com/ElementsProject/lightning/pull/5340 +[#5344]: https://github.com/ElementsProject/lightning/pull/5344 +[#5347]: https://github.com/ElementsProject/lightning/pull/5347 +[#5349]: https://github.com/ElementsProject/lightning/pull/5349 +[#5362]: https://github.com/ElementsProject/lightning/pull/5362 +[#5370]: https://github.com/ElementsProject/lightning/pull/5370 +[#5378]: https://github.com/ElementsProject/lightning/pull/5378 +[#5381]: https://github.com/ElementsProject/lightning/pull/5381 +[#5389]: https://github.com/ElementsProject/lightning/pull/5389 +[#5415]: https://github.com/ElementsProject/lightning/pull/5415 +[#5421]: https://github.com/ElementsProject/lightning/pull/5421 +[#5422]: https://github.com/ElementsProject/lightning/pull/5422 +[#5425]: https://github.com/ElementsProject/lightning/pull/5425 +[#5430]: https://github.com/ElementsProject/lightning/pull/5430 +[#5434]: https://github.com/ElementsProject/lightning/pull/5434 +[#5441]: https://github.com/ElementsProject/lightning/pull/5441 +[#5460]: https://github.com/ElementsProject/lightning/pull/5460 +[#5475]: https://github.com/ElementsProject/lightning/pull/5475 +[#5477]: https://github.com/ElementsProject/lightning/pull/5477 + + ## [0.11.1] - 2022-05-13: Simon's Carefully Chosen Release Name II Single change which fixed a bug introduced in 0.11.0 which could cause @@ -1670,6 +1836,10 @@ There predate the BOLT specifications, and are only of vague historic interest: 6. [0.5.1] - 2016-10-21 7. [0.5.2] - 2016-11-21: "Bitcoin Savings & Trust Daily Interest II" +[0.12.0rc1]: https://github.com/ElementsProject/lightning/releases/tag/v0.12.0rc1 +[0.11.2]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.2 +[0.11.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.1 +[0.11.0.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0.1 [0.10.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.10.1 [0.10.0]: https://github.com/ElementsProject/lightning/releases/tag/v0.10.0 [0.9.2]: https://github.com/ElementsProject/lightning/releases/tag/v0.9.2 From 071b1bc4f1ee90480ba29ada771cbf9ae6007eb2 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 1 Aug 2022 17:47:26 -0500 Subject: [PATCH 1223/1530] pyln: update versions to v0.12.0 --- contrib/pyln-client/pyln/client/__init__.py | 2 +- contrib/pyln-proto/pyln/proto/__init__.py | 2 +- contrib/pyln-testing/pyln/testing/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-client/pyln/client/__init__.py b/contrib/pyln-client/pyln/client/__init__.py index bc830632fbfb..382c32991fda 100644 --- a/contrib/pyln-client/pyln/client/__init__.py +++ b/contrib/pyln-client/pyln/client/__init__.py @@ -2,7 +2,7 @@ from .plugin import Plugin, monkey_patch, RpcException from .gossmap import Gossmap, GossmapNode, GossmapChannel, GossmapNodeId -__version__ = "0.11.0" +__version__ = "0.12.0" __all__ = [ "LightningRpc", diff --git a/contrib/pyln-proto/pyln/proto/__init__.py b/contrib/pyln-proto/pyln/proto/__init__.py index 41e77980cc75..f41912275101 100644 --- a/contrib/pyln-proto/pyln/proto/__init__.py +++ b/contrib/pyln-proto/pyln/proto/__init__.py @@ -4,7 +4,7 @@ from .onion import OnionPayload, TlvPayload, LegacyOnionPayload from .wire import LightningConnection, LightningServerSocket -__version__ = "0.11.0" +__version__ = "0.12.0" __all__ = [ "Invoice", diff --git a/contrib/pyln-testing/pyln/testing/__init__.py b/contrib/pyln-testing/pyln/testing/__init__.py index 5fc027556733..1d1e55bbdbd5 100644 --- a/contrib/pyln-testing/pyln/testing/__init__.py +++ b/contrib/pyln-testing/pyln/testing/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.11.0" +__version__ = "0.12.0" __all__ = [ "__version__", From aea2ec5c0d33f5cc76f75cc0b43e8eba52dd8bca Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Aug 2022 10:05:48 +0930 Subject: [PATCH 1224/1530] CHANGELOG.md: add in CHANGELOG from 0.11.2 branch. And manually remove those which were claimed for 0.12. Signed-off-by: Rusty Russell --- CHANGELOG.md | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ebead910280..fa4a00aead14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,17 +99,10 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. - `connectd`: various crashes and issues fixed by simplification and rewrite. ([#5261]) - `connectd`: Port of a DNS announcement can be 0 if unspecified ([#5434]) - - `connectd`: no longer crashes when peers reconnect. ([#5300]) - - `connectd`: occasional crash when we reconnect to a peer quickly. ([#5340]) - - `connectd`: reduce initial CPU load when connecting to peers. ([#5328]) - - `connectd`: large memory usage with many peers fixed. ([#5312]) - `dualopend`: Issue if the number of outputs decreases in a dualopen RBF or splice. ([#5378]) - `channeld`: Enforce our own `minimum_depth` beyond just confirming ([#5275]) - - routing: Fixed an issue where we would exclude the entire channel if either direction was disabled, or we hadn't seen an update yet. ([#5286]) - - topology: Under some circumstances we were considering the limits on the wrong direction for a channel ([#5286]) - logging: `log-prefix` now correctly prefixes *all* log messages. ([#5349]) - logging: `log-level` `io` shows JSONRPC output, as well as input. ([#5306]) - - interop: treat LND "internal error" as warnings, not force close events (as we did in v0.10). ([#5326]) - PSBT: Fix signature encoding to comply with BIP-0171. ([#5307]) - signmessage: improve the UX of the rpc command when zbase is not a valid one ([#5297]) - JSON-RPC: Adds dynamically detected public IP addresses to `getinfo` ([#5244]) @@ -143,17 +136,11 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#5278]: https://github.com/ElementsProject/lightning/pull/5278 [#5279]: https://github.com/ElementsProject/lightning/pull/5279 [#5281]: https://github.com/ElementsProject/lightning/pull/5281 -[#5286]: https://github.com/ElementsProject/lightning/pull/5286 [#5297]: https://github.com/ElementsProject/lightning/pull/5297 -[#5300]: https://github.com/ElementsProject/lightning/pull/5300 [#5303]: https://github.com/ElementsProject/lightning/pull/5303 [#5306]: https://github.com/ElementsProject/lightning/pull/5306 [#5307]: https://github.com/ElementsProject/lightning/pull/5307 -[#5312]: https://github.com/ElementsProject/lightning/pull/5312 -[#5326]: https://github.com/ElementsProject/lightning/pull/5326 -[#5328]: https://github.com/ElementsProject/lightning/pull/5328 [#5330]: https://github.com/ElementsProject/lightning/pull/5330 -[#5340]: https://github.com/ElementsProject/lightning/pull/5340 [#5344]: https://github.com/ElementsProject/lightning/pull/5344 [#5347]: https://github.com/ElementsProject/lightning/pull/5347 [#5349]: https://github.com/ElementsProject/lightning/pull/5349 @@ -174,6 +161,31 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#5477]: https://github.com/ElementsProject/lightning/pull/5477 +## [0.11.2] - 2022-06-24: Simon's Carefully Chosen Release Name III + +Regressions since 0.10.2 which could not wait for the 0.12 release, +which especially hurt larger nodes. + +### Fixed + + - Protocol: treat LND "internal error" as warnings, not force close events (like v0.10) ([#5326]) + - connectd: no longer occasional crashes when peers reconnect. ([#5300]) + - connectd: another crash fix on trying to reconnect to disconnecting peer. ([#5340]) + - topology: Under some circumstances we were considering the limits on the wrong direction for a channel ([#5286]) + - routing: Fixed an issue where we would exclude the entire channel if either direction was disabled, or we hadn't seen an update yet. ([#5286]) + - connectd: large memory usage with many peers fixed. ([#5312]) + - connectd: reduce initial CPU load when connecting to peers. ([#5328]) + - lightnind: fix failed startup "Could not load channels from the database" if old TORv2 addresses were present. ([#5331]) + +[#5286]: https://github.com/ElementsProject/lightning/pull/5286 +[#5300]: https://github.com/ElementsProject/lightning/pull/5300 +[#5312]: https://github.com/ElementsProject/lightning/pull/5312 +[#5326]: https://github.com/ElementsProject/lightning/pull/5326 +[#5328]: https://github.com/ElementsProject/lightning/pull/5328 +[#5331]: https://github.com/ElementsProject/lightning/pull/5331 +[#5340]: https://github.com/ElementsProject/lightning/pull/5340 +[0.11.2]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.2 + ## [0.11.1] - 2022-05-13: Simon's Carefully Chosen Release Name II Single change which fixed a bug introduced in 0.11.0 which could cause From cb496acb491496d0de5058ae3fa137f92df7b603 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Aug 2022 10:10:35 +0930 Subject: [PATCH 1225/1530] CHANGELOG.md: neaten entries. 1. Unbalanced backticks and missing space in bookkeeper line. 2. Rephrase emergency backup to not overpromise. 3. "logging" and "lightnignd" are not correct title, use "Config". 4. JSON-RPC not JSONRPC in one place. 5. Don't include empty EXPERIMENTAL section (I checked, it really was empty!) Signed-off-by: Rusty Russell --- CHANGELOG.md | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa4a00aead14..ee907a5d3ecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,9 @@ This release named by @adi2011. ### Added - *NEW*: `commando` a new builtin plugin to send/recv peer commands over the lightning network, using runes. ([#5370]) - - *NEW*: New built-in plugin`bookkeeper` w/ commands `bkpr-listaccountevents`, `bkpr-listbalances, bkpr-listincome, bkpr-channelsapy, bkpr-dumpincomecsv, bkpr-inspect ([#5071]) - - *NEW*: Static channel backup, to enable smooth fund recovery in case of complete data loss ([#5422]) - - logging: `log-level=debug:` supported to get debug-level logs for everything about a peer. ([#5349]) + - *NEW*: New built-in plugin `bookkeeper` w/ commands `bkpr-listaccountevents`, `bkpr-listbalances`, `bkpr-listincome`, `bkpr-channelsapy`, `bkpr-dumpincomecsv`, `bkpr-inspect` ([#5071]) + - *NEW*: Emergency channel backup ("static backup") which allows us to seek fund recovery from honest peers in case of complete data loss ([#5422]) + - Config: `log-level=debug:` supported to get debug-level logs for everything about a peer. ([#5349]) - JSON-RPC: `connect` use the standard port derivation when the port is not specified. ([#5242]) - JSON-RPC: `fetchinvoice` `changes` `amount_msat` ([#5306]) - JSON-RPC: Added `mindepth` argument to specify the number of confirmations we require for `fundchannel` and `multifundchannel` ([#5275]) @@ -46,7 +46,7 @@ This release named by @adi2011. - `connectd`: give busy peers more time to respond to pings. ([#5347]) - `gossipd`: now accepts spam gossip, but squelches it for ([#5239]) - gossip: gossip\_store updated to version 10. ([#5239]) - - logging: `log-file` option specified multiple times opens multiple log files. ([#5281]) + - Options: `log-file` option specified multiple times opens multiple log files. ([#5281]) - JSON-RPC: `plugin start` now assumes relative path to default plugins dir if the path is not found in absolute context. i.e. lightning-cli plugin start my_plugin.py ([#5211]) - JSON-RPC: `fundchannel`: now errors if you try to buy a liquidity ad but dont' have `experimental-dual-fund` enabled ([#5389]) - JSON-RPC: "\_msat" fields can be raw numbers, not "123msat" strings (please handle both!) ([#5306]) @@ -61,7 +61,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. - JSON-RPC: `listtransactions` `msat` (use `amount_msat`) ([#5306]) - JSON-RPC: checkmessage return an error when the pubkey is not specified and it is unknown in the network graph. ([#5252]) - JSON-RPC: "_msat" fields as "123msat" strings (will be only numbers) ([#5306]) - - JSONRPC: `sendpay` `route` elements `msatoshi` (use `amount_msat`) ([#5306]) + - JSON-RPC: `sendpay` `route` elements `msatoshi` (use `amount_msat`) ([#5306]) - JSON-RPC: `pay`, `decode`, `decodepay`, `getroute`, `listinvoices`, `listpays` and `listsendpays` `msatoshi` fields (use `amount_msat`). ([#5306]) - JSON-RPC: `getinfo` `msatoshi_fees_collected` field (use `fees_collected_msat`). ([#5306]) - JSON-RPC: `listpeers` `channels`: `msatoshi_to_us`, `msatoshi_to_us_min`, `msatoshi_to_us_max`, `msatoshi_total`, `dust_limit_satoshis`, `our_channel_reserve_satoshis`, `their_channel_reserve_satoshis`, `spendable_msatoshi`, `receivable_msatoshi`, `in_msatoshi_offered`, `in_msatoshi_fulfilled`, `out_msatoshi_offered`, `out_msatoshi_fulfilled`, `max_htlc_value_in_flight_msat` and `htlc_minimum_msat` (use `to_us_msat`, `min_to_us_msat`, `max_to_us_msat`, `total_msat`, `dust_limit_msat`, `our_reserve_msat`, `their_reserve_msat`, `spendable_msat`, `receivable_msat`, `in_offered_msat`, `in_fulfilled_msat`, `out_offered_msat`, `out_fulfilled_msat`, `max_total_htlc_in_msat` and `minimum_htlc_in_msat`). ([#5306]) @@ -92,7 +92,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. - JSON-RPC: removed `listpeers` `channels` deprecated fields (deprecated v0.10.1) ([#5264]) - JSON-RPC: removed `listpeers` `channels` `closer` now omitted, rather than `null` (deprecated v0.10.1) ([#5264]) - libhsmd: Removed the `libhsmd_python` wrapper as it was unused ([#5415]) - - lightningd: removed `enable-autotor-v2-mode` option (deprecated v0.10.1) ([#5264]) + - Options: removed `enable-autotor-v2-mode` option (deprecated v0.10.1) ([#5264]) ### Fixed @@ -113,11 +113,6 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. - Upgrade docker base image from Debian buster to bullseye to work with glibc 2.29+ #5276 ([#5278]) - docker: The docker images are now built with the rust plugins `cln-grpc` ([#5270]) - -### EXPERIMENTAL - - - None. - [#4988]: https://github.com/ElementsProject/lightning/pull/4988 [#5062]: https://github.com/ElementsProject/lightning/pull/5062 [#5071]: https://github.com/ElementsProject/lightning/pull/5071 From 4ffae2a8c69bb6739707fb8539ee2a6f4eea6c16 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Aug 2022 10:25:08 +0930 Subject: [PATCH 1226/1530] CHANGELOG.md: note the Great Msat Migration! Signed-off-by: Rusty Russell --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee907a5d3ecc..0d57c856e925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ TODO: Insert version codename, and username of the contributor that named the re This release named by @adi2011. +Developers please note the Great Msat Migration has begun: +1. All JSON amount field names now end in "_msat" (others are deprecated) +2. Their values are strings ending in "msat", but will soon be normal integers. +3. You should accept both: set `allow-deprecated-apis=false` to test! + ### Added - *NEW*: `commando` a new builtin plugin to send/recv peer commands over the lightning network, using runes. ([#5370]) From 498f9b75f25a0778a6f3025565adeb8092a117c4 Mon Sep 17 00:00:00 2001 From: Seth For Privacy Date: Fri, 29 Jul 2022 14:09:59 -0400 Subject: [PATCH 1227/1530] Correct basics of backup plugin usage --- doc/BACKUP.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/BACKUP.md b/doc/BACKUP.md index d0c76c1fefe3..720b49208297 100644 --- a/doc/BACKUP.md +++ b/doc/BACKUP.md @@ -175,12 +175,15 @@ like fire or computer confiscation. `/!\` WHO SHOULD DO THIS: Casual users. -You can get the `backup` plugin here: +You can find the full source for the `backup` plugin here: https://github.com/lightningd/plugins/tree/master/backup The `backup` plugin requires Python 3. +* Download the source for the plugin. + * `git clone https://github.com/lightningd/plugins.git` * `cd` into its directory and install requirements. + * `cd plugins/backup` * `pip3 install -r requirements.txt` * Figure out where you will put the backup files. * Ideally you have an NFS or other network-based mount on your system, From 645b1b505b6d0a93da45007c6825abe032f84ac6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 3 Aug 2022 10:43:10 +0930 Subject: [PATCH 1228/1530] lightningd: fix funding_locked in channel_opened notification. Previously, "funding_locked" was always "true"! (It's actually been wrong since its introduction in v0.7.3!) Changelog-Fixed: Plugins: `channel_opened` notification `funding_locked` field is now accurate: was always `true`. Signed-off-by: Rusty Russell --- lightningd/dual_open_control.c | 4 ++-- lightningd/notification.c | 6 +++--- lightningd/notification.h | 2 +- lightningd/opening_control.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 05faaa19d1d9..fa458bca3a35 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1631,7 +1631,7 @@ static void handle_peer_tx_sigs_sent(struct subd *dualopend, &channel->peer->id, &channel->funding_sats, &channel->funding.txid, - &channel->remote_funding_locked); + channel->remote_funding_locked); /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2 * The receiving node: ... @@ -1997,7 +1997,7 @@ static void handle_peer_tx_sigs_msg(struct subd *dualopend, &channel->peer->id, &channel->funding_sats, &channel->funding.txid, - &channel->remote_funding_locked); + channel->remote_funding_locked); /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2 * The receiving node: ... diff --git a/lightningd/notification.c b/lightningd/notification.c index 1b28a08ab3d8..82ad1f616aed 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -201,7 +201,7 @@ static void channel_opened_notification_serialize(struct json_stream *stream, struct node_id *node_id, struct amount_sat *funding_sat, struct bitcoin_txid *funding_txid, - bool *funding_locked) + bool funding_locked) { json_object_start(stream, "channel_opened"); json_add_node_id(stream, "id", node_id); @@ -216,13 +216,13 @@ REGISTER_NOTIFICATION(channel_opened, void notify_channel_opened(struct lightningd *ld, struct node_id *node_id, struct amount_sat *funding_sat, struct bitcoin_txid *funding_txid, - bool *funding_locked) + bool funding_locked) { void (*serialize)(struct json_stream *, struct node_id *, struct amount_sat *, struct bitcoin_txid *, - bool *) = channel_opened_notification_gen.serialize; + bool) = channel_opened_notification_gen.serialize; struct jsonrpc_notification *n = jsonrpc_notification_start(NULL, channel_opened_notification_gen.topic); diff --git a/lightningd/notification.h b/lightningd/notification.h index c1e314864eb9..8eb00c0a3057 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -47,7 +47,7 @@ void notify_invoice_creation(struct lightningd *ld, struct amount_msat *amount, void notify_channel_opened(struct lightningd *ld, struct node_id *node_id, struct amount_sat *funding_sat, struct bitcoin_txid *funding_txid, - bool *funding_locked); + bool funding_locked); void notify_channel_state_changed(struct lightningd *ld, struct node_id *peer_id, diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 3ba43fac122f..8a24ee69cbad 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -539,7 +539,7 @@ static void opening_fundee_finished(struct subd *openingd, /* Tell plugins about the success */ notify_channel_opened(ld, &channel->peer->id, &channel->funding_sats, - &channel->funding.txid, &channel->remote_funding_locked); + &channel->funding.txid, channel->remote_funding_locked); if (pbase) wallet_penalty_base_add(ld->wallet, channel->dbid, pbase); From 2632cddfe46bc35f76796ff1c1154b82a6870cef Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 5 Jul 2022 12:10:41 +0200 Subject: [PATCH 1229/1530] pay: Fix a memory leak when retrying getroute --- plugins/libplugin-pay.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index e27305fa9361..b44716d7a239 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -809,6 +809,10 @@ static struct command_result *payment_getroute(struct payment *p) const char *errstr; struct gossmap *gossmap; + /* If we retry the getroute call we might already have a route, so + * free an eventual stale route. */ + p->route = tal_free(p->route); + gossmap = get_gossmap(p->plugin); dst = gossmap_find_node(gossmap, p->getroute->destination); From 66d8ce7c8c183ed1b84052ca3531616e56f9b87b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 30 Jul 2022 09:12:44 +0930 Subject: [PATCH 1230/1530] pytest: clarify test_onchain_multihtlc_our_unilateral / test_onchain_multihtlc_their_unilateral Use the names, not calculations on an array. It's simply clearer, especially when debugging. Signed-off-by: Rusty Russell --- tests/test_closing.py | 168 ++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 90 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index bf3804aec893..8cf1fb4711b1 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2830,192 +2830,180 @@ def setup_multihtlc_test(node_factory, bitcoind): @pytest.mark.slow_test def test_onchain_multihtlc_our_unilateral(node_factory, bitcoind): """Node pushes a channel onchain with multiple HTLCs with same payment_hash """ - h, nodes = setup_multihtlc_test(node_factory, bitcoind) + h, (l1, l2, l3, l4, l5, l6, l7) = setup_multihtlc_test(node_factory, bitcoind) - mid = len(nodes) // 2 - - for i in range(len(nodes) - 1): - assert only_one(nodes[i].rpc.listpeers(nodes[i + 1].info['id'])['peers'])['connected'] - - # Now midnode goes onchain with n+1 channel. - nodes[mid].rpc.dev_fail(nodes[mid + 1].info['id']) - nodes[mid].wait_for_channel_onchain(nodes[mid + 1].info['id']) + # Now l4 goes onchain with l4-l5 channel. + l4.rpc.dev_fail(l5.info['id']) + l4.wait_for_channel_onchain(l5.info['id']) bitcoind.generate_block(1) - nodes[mid].daemon.wait_for_log(' to ONCHAIN') - nodes[mid + 1].daemon.wait_for_log(' to ONCHAIN') + l4.daemon.wait_for_log(' to ONCHAIN') + l5.daemon.wait_for_log(' to ONCHAIN') # Now, restart and manually reconnect end nodes (so they don't ignore HTLCs) # In fact, they'll fail them with WIRE_TEMPORARY_NODE_FAILURE. # TODO Remove our reliance on HTLCs failing on startup and the need for # this plugin - nodes[0].daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') - nodes[-1].daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') - nodes[0].restart() - nodes[-1].restart() + l1.daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') + l7.daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') + l1.restart() + l7.restart() # We disabled auto-reconnect so we'd detect breakage, so manually reconnect. - nodes[0].rpc.connect(nodes[1].info['id'], 'localhost', nodes[1].port) - nodes[-1].rpc.connect(nodes[-2].info['id'], 'localhost', nodes[-2].port) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l7.rpc.connect(l6.info['id'], 'localhost', l6.port) # Wait for HTLCs to stabilize. - nodes[0].daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) - nodes[0].daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') - nodes[0].daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') - nodes[-1].daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) - nodes[-1].daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') - nodes[-1].daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') + l1.daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) + l1.daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') + l1.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') + l7.daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) + l7.daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') + l7.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') # After at depth 5, midnode will spend its own to-self output. bitcoind.generate_block(4) - nodes[mid].wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_UNILATERAL/DELAYED_OUTPUT_TO_US') + l4.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_UNILATERAL/DELAYED_OUTPUT_TO_US') # The three outgoing HTLCs time out at 21, 21 and 22 blocks. bitcoind.generate_block(16) - nodes[mid].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') - nodes[mid].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') + l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', + 'OUR_UNILATERAL/OUR_HTLC') + l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', + 'OUR_UNILATERAL/OUR_HTLC') bitcoind.generate_block(1) - nodes[mid].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') + l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', + 'OUR_UNILATERAL/OUR_HTLC') # And three more for us to consider them all settled. bitcoind.generate_block(3) # Now, those nodes should have correctly failed the HTLCs - for n in nodes[:mid - 1]: + for n in l1, l2: with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): n.rpc.waitsendpay(h, TIMEOUT) # Other timeouts are 27,27,28 blocks. bitcoind.generate_block(2) - nodes[mid].daemon.wait_for_logs(['Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC'] * 2) + l4.daemon.wait_for_logs(['Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC'] * 2) for _ in range(2): - nodes[mid + 1].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') + l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', + 'THEIR_UNILATERAL/OUR_HTLC') bitcoind.generate_block(1) - nodes[mid].daemon.wait_for_log('Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC') - nodes[mid + 1].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') + l4.daemon.wait_for_log('Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC') + l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', + 'THEIR_UNILATERAL/OUR_HTLC') # Depth 3 to consider it settled. bitcoind.generate_block(3) - for n in nodes[mid + 1:]: + for n in l5, l6, l7: with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): n.rpc.waitsendpay(h, TIMEOUT) # At depth 100 it's all done (we didn't bother waiting for mid+1's # spends, so that might still be going) bitcoind.generate_block(97) - nodes[mid].daemon.wait_for_logs(['onchaind complete, forgetting peer']) + l4.daemon.wait_for_logs(['onchaind complete, forgetting peer']) # No other channels should have failed. - for i in range(len(nodes) - 1): - if i != mid: - assert only_one(nodes[i].rpc.listpeers(nodes[i + 1].info['id'])['peers'])['connected'] + for n in l1, l2, l3, l6, l7: + assert all([p['connected'] for p in n.rpc.listpeers()['peers']]) @pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs") @pytest.mark.slow_test def test_onchain_multihtlc_their_unilateral(node_factory, bitcoind): """Node pushes a channel onchain with multiple HTLCs with same payment_hash """ - h, nodes = setup_multihtlc_test(node_factory, bitcoind) + h, (l1, l2, l3, l4, l5, l6, l7) = setup_multihtlc_test(node_factory, bitcoind) - mid = len(nodes) // 2 - - for i in range(len(nodes) - 1): - assert only_one(nodes[i].rpc.listpeers(nodes[i + 1].info['id'])['peers'])['connected'] - - # Now midnode+1 goes onchain with midnode channel. - nodes[mid + 1].rpc.dev_fail(nodes[mid].info['id']) - nodes[mid + 1].wait_for_channel_onchain(nodes[mid].info['id']) + # Now l5 goes onchain with l4-l5 channel. + l5.rpc.dev_fail(l4.info['id']) + l5.wait_for_channel_onchain(l4.info['id']) bitcoind.generate_block(1) - nodes[mid].daemon.wait_for_log(' to ONCHAIN') - nodes[mid + 1].daemon.wait_for_log(' to ONCHAIN') + l4.daemon.wait_for_log(' to ONCHAIN') + l5.daemon.wait_for_log(' to ONCHAIN') # Now, restart and manually reconnect end nodes (so they don't ignore HTLCs) # In fact, they'll fail them with WIRE_TEMPORARY_NODE_FAILURE. # TODO Remove our reliance on HTLCs failing on startup and the need for # this plugin - nodes[0].daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') - nodes[-1].daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') - nodes[0].restart() - nodes[-1].restart() + l1.daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') + l7.daemon.opts['plugin'] = os.path.join(os.getcwd(), 'tests/plugins/fail_htlcs.py') + l1.restart() + l7.restart() # We disabled auto-reconnect so we'd detect breakage, so manually reconnect. - nodes[0].rpc.connect(nodes[1].info['id'], 'localhost', nodes[1].port) - nodes[-1].rpc.connect(nodes[-2].info['id'], 'localhost', nodes[-2].port) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l7.rpc.connect(l6.info['id'], 'localhost', l6.port) # Wait for HTLCs to stabilize. - nodes[0].daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) - nodes[0].daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') - nodes[0].daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') - nodes[-1].daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) - nodes[-1].daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') - nodes[-1].daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') + l1.daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) + l1.daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') + l1.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') + l7.daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) + l7.daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') + l7.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') # At depth 5, midnode+1 will spend its own to-self output. bitcoind.generate_block(4) - nodes[mid + 1].wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET') + l5.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET') # The three outgoing HTLCs time out at depth 21, 21 and 22 blocks. bitcoind.generate_block(16) - nodes[mid].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') - nodes[mid].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') + l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', + 'THEIR_UNILATERAL/OUR_HTLC') + l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', + 'THEIR_UNILATERAL/OUR_HTLC') bitcoind.generate_block(1) - nodes[mid].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') + l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', + 'THEIR_UNILATERAL/OUR_HTLC') # At depth 3 we consider them all settled. bitcoind.generate_block(3) # Now, those nodes should have correctly failed the HTLCs - for n in nodes[:mid - 1]: + for n in l1, l2: with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): n.rpc.waitsendpay(h, TIMEOUT) # Other timeouts are at depths 27,27,28 blocks. bitcoind.generate_block(2) - nodes[mid].daemon.wait_for_logs(['Ignoring output.*: THEIR_UNILATERAL/THEIR_HTLC'] * 2) + l4.daemon.wait_for_logs(['Ignoring output.*: THEIR_UNILATERAL/THEIR_HTLC'] * 2) for _ in range(2): - nodes[mid + 1].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') + l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', + 'OUR_UNILATERAL/OUR_HTLC') bitcoind.generate_block(1) - nodes[mid].daemon.wait_for_log('Ignoring output.*: THEIR_UNILATERAL/THEIR_HTLC') - nodes[mid + 1].wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') + l4.daemon.wait_for_log('Ignoring output.*: THEIR_UNILATERAL/THEIR_HTLC') + l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', + 'OUR_UNILATERAL/OUR_HTLC') # At depth 3 we consider them all settled. bitcoind.generate_block(3) - for n in nodes[mid + 1:]: + for n in l5, l6, l7: with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): n.rpc.waitsendpay(h, TIMEOUT) # At depth 5, mid+1 can spend HTLC_TIMEOUT_TX output. bitcoind.generate_block(1) for _ in range(2): - nodes[mid + 1].wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') + l5.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') bitcoind.generate_block(1) - nodes[mid + 1].wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') + l5.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') # At depth 100 they're all done. bitcoind.generate_block(100) - nodes[mid].daemon.wait_for_logs(['onchaind complete, forgetting peer']) - nodes[mid + 1].daemon.wait_for_logs(['onchaind complete, forgetting peer']) + l4.daemon.wait_for_logs(['onchaind complete, forgetting peer']) + l5.daemon.wait_for_logs(['onchaind complete, forgetting peer']) # No other channels should have failed. - for i in range(len(nodes) - 1): - if i != mid: - assert only_one(nodes[i].rpc.listpeers(nodes[i + 1].info['id'])['peers'])['connected'] + for n in l1, l2, l3, l6, l7: + assert all([p['connected'] for p in n.rpc.listpeers()['peers']]) @pytest.mark.developer("needs DEVELOPER=1") From b5ee5e7fb1f30fa2e366903fb5bbbab2e7ef5909 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 30 Jul 2022 09:12:46 +0930 Subject: [PATCH 1231/1530] pytest: simplify test_onchain_multihtlc_our_unilateral/their_unilateral We don't actually care how it does it, just that it ends the HTLCs, so simplify this logic which tries to match it exactly. This also fixes the flake where we would sometimes close the upstream channels, simply because we now wait at least one second per block. Signed-off-by: Rusty Russell --- tests/test_closing.py | 105 ++++++++---------------------------------- 1 file changed, 19 insertions(+), 86 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 8cf1fb4711b1..cd5f1777e6eb 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2861,50 +2861,20 @@ def test_onchain_multihtlc_our_unilateral(node_factory, bitcoind): l7.daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') l7.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') - # After at depth 5, midnode will spend its own to-self output. - bitcoind.generate_block(4) - l4.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_UNILATERAL/DELAYED_OUTPUT_TO_US') - - # The three outgoing HTLCs time out at 21, 21 and 22 blocks. - bitcoind.generate_block(16) - l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') - l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') - bitcoind.generate_block(1) - l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') - - # And three more for us to consider them all settled. - bitcoind.generate_block(3) - - # Now, those nodes should have correctly failed the HTLCs - for n in l1, l2: - with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): - n.rpc.waitsendpay(h, TIMEOUT) - - # Other timeouts are 27,27,28 blocks. - bitcoind.generate_block(2) - l4.daemon.wait_for_logs(['Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC'] * 2) - for _ in range(2): - l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') - bitcoind.generate_block(1) - l4.daemon.wait_for_log('Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC') - l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') - - # Depth 3 to consider it settled. - bitcoind.generate_block(3) + # Rather than track exact complex logic here, simply mine a block + # until l4 says 'all outputs resolved'. + while not l4.daemon.is_in_log('All outputs resolved'): + bitcoind.generate_block(1) + assert bitcoind.rpc.getblockcount() < 200 + sync_blockheight(bitcoind, [l4, l5]) - for n in l5, l6, l7: + # All payments should be long resolved. + for n in l1, l2, l3, l5, l6, l7: with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): n.rpc.waitsendpay(h, TIMEOUT) - # At depth 100 it's all done (we didn't bother waiting for mid+1's - # spends, so that might still be going) - bitcoind.generate_block(97) + # At depth 100 it's all done + bitcoind.generate_block(100) l4.daemon.wait_for_logs(['onchaind complete, forgetting peer']) # No other channels should have failed. @@ -2947,56 +2917,19 @@ def test_onchain_multihtlc_their_unilateral(node_factory, bitcoind): l7.daemon.wait_for_log('peer_out WIRE_COMMITMENT_SIGNED') l7.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK') - # At depth 5, midnode+1 will spend its own to-self output. - bitcoind.generate_block(4) - l5.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET') - - # The three outgoing HTLCs time out at depth 21, 21 and 22 blocks. - bitcoind.generate_block(16) - l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') - l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') - bitcoind.generate_block(1) - l4.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TO_US', - 'THEIR_UNILATERAL/OUR_HTLC') - - # At depth 3 we consider them all settled. - bitcoind.generate_block(3) - - # Now, those nodes should have correctly failed the HTLCs - for n in l1, l2: - with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): - n.rpc.waitsendpay(h, TIMEOUT) - - # Other timeouts are at depths 27,27,28 blocks. - bitcoind.generate_block(2) - l4.daemon.wait_for_logs(['Ignoring output.*: THEIR_UNILATERAL/THEIR_HTLC'] * 2) - for _ in range(2): - l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') - bitcoind.generate_block(1) - l4.daemon.wait_for_log('Ignoring output.*: THEIR_UNILATERAL/THEIR_HTLC') - l5.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', - 'OUR_UNILATERAL/OUR_HTLC') - - # At depth 3 we consider them all settled. - bitcoind.generate_block(3) + # Rather than track exact complex logic here, simply mine a block + # until l5 says 'all outputs resolved'. + while not l5.daemon.is_in_log('All outputs resolved'): + bitcoind.generate_block(1) + assert bitcoind.rpc.getblockcount() < 200 + sync_blockheight(bitcoind, [l4, l5]) - for n in l5, l6, l7: + # All payments should be long resolved. + for n in l1, l2, l3, l5, l6, l7: with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE'): n.rpc.waitsendpay(h, TIMEOUT) - # At depth 5, mid+1 can spend HTLC_TIMEOUT_TX output. - bitcoind.generate_block(1) - for _ in range(2): - l5.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') - bitcoind.generate_block(1) - l5.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') - - # At depth 100 they're all done. + # At depth 100 it's all done bitcoind.generate_block(100) l4.daemon.wait_for_logs(['onchaind complete, forgetting peer']) l5.daemon.wait_for_logs(['onchaind complete, forgetting peer']) From 93303ffdad050ad76398380130918f10a1b0c873 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 30 Jul 2022 14:00:40 +0930 Subject: [PATCH 1232/1530] pytest: change multihtlc topology for simpler testing. We were seeing flakes due to channel closures on intermidiary nodes if the blocks came too fast. If we use a star topology it's actually what we want and it's much simpler to figure out what's going on. Signed-off-by: Rusty Russell --- tests/test_closing.py | 149 +++++++++++++++++++++++++++++------------- 1 file changed, 102 insertions(+), 47 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index cd5f1777e6eb..8d351094e0ad 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2757,80 +2757,135 @@ def test_permfail_new_commit(node_factory, bitcoind, executor): def setup_multihtlc_test(node_factory, bitcoind): - # l1 -> l2 -> l3 -> l4 -> l5 -> l6 -> l7 + # l1 --\ /-> l6 + # v / + # l2 -> l4 -> l5 -> l7 + # ^ + # l3 --/ # l1 and l7 ignore and HTLCs they're sent. # For each direction, we create these HTLCs with same payment_hash: # 1 failed (CLTV1) # 1 failed (CLTV2) # 2 live (CLTV2) # 1 live (CLTV3) - nodes = node_factory.line_graph(7, wait_for_announce=True, - opts={'dev-no-reconnect': None, - 'may_reconnect': True}) - - # Balance by pushing half the funds. - b11 = nodes[-1].rpc.invoice(10**9 // 2, '1', 'balancer')['bolt11'] - nodes[0].rpc.pay(b11) - - nodes[0].rpc.dev_ignore_htlcs(id=nodes[1].info['id'], ignore=True) - nodes[-1].rpc.dev_ignore_htlcs(id=nodes[-2].info['id'], ignore=True) + l1, l2, l3, l4, l5, l6, l7 = node_factory.get_nodes(7, + opts={'dev-no-reconnect': None, + 'may_reconnect': True}) + + l4.fundwallet(10**6 * 4 + 100000) + l5.fundwallet(10**6 * 2 + 100000) + + # They need to be connected. + for n in l1, l2, l3, l5: + l4.rpc.connect(n.info['id'], 'localhost', n.port) + for n in l6, l7: + l5.rpc.connect(n.info['id'], 'localhost', n.port) + + # Efficient way to establish the channels. + l4.rpc.multifundchannel([{'id': l1.info['id'], 'amount': 10**6}, + {'id': l2.info['id'], 'amount': 10**6}, + {'id': l3.info['id'], 'amount': 10**6}, + {'id': l5.info['id'], 'amount': 10**6}]) + l5.rpc.multifundchannel([{'id': l6.info['id'], 'amount': 10**6}, + {'id': l7.info['id'], 'amount': 10**6}]) + + # Make sure they're all in normal state. + bitcoind.generate_block(1) + wait_for(lambda: all([only_one(p['channels'])['state'] == 'CHANNELD_NORMAL' + for p in l4.rpc.listpeers()['peers']])) + wait_for(lambda: all([only_one(p['channels'])['state'] == 'CHANNELD_NORMAL' + for p in l5.rpc.listpeers()['peers']])) + + # Balance them + for n in l1, l2, l3, l5: + l4.pay(n, 10**9 // 2) + for n in l6, l7: + l5.pay(n, 10**9 // 2) + + def route_to_l7(src): + """Route from l1, l2 or l3 to l7""" + return [{'id': l4.info['id'], 'channel': src.get_channel_scid(l4), 'amount_msat': "100002002msat", 'delay': 21}, + {'id': l5.info['id'], 'channel': l4.get_channel_scid(l5), 'amount_msat': "100001001msat", 'delay': 15}, + {'id': l7.info['id'], 'channel': l5.get_channel_scid(l7), 'amount_msat': "100000000msat", 'delay': 9}] + + def route_to_l1(src): + """Route from l6 or l7 to l1""" + return [{'id': l5.info['id'], 'channel': src.get_channel_scid(l5), 'amount_msat': "100002002msat", 'delay': 21}, + {'id': l4.info['id'], 'channel': l5.get_channel_scid(l4), 'amount_msat': "100001001msat", 'delay': 15}, + {'id': l1.info['id'], 'channel': l4.get_channel_scid(l1), 'amount_msat': "100000000msat", 'delay': 9}] + + # Freeze the HTLCs in place. + l1.rpc.dev_ignore_htlcs(id=l4.info['id'], ignore=True) + l7.rpc.dev_ignore_htlcs(id=l5.info['id'], ignore=True) preimage = "0" * 64 - inv = nodes[0].rpc.invoice(amount_msat=10**8, label='x', description='desc', - preimage=preimage) + inv = l1.rpc.invoice(amount_msat=10**8, label='x', description='desc', + preimage=preimage) h = inv['payment_hash'] - nodes[-1].rpc.invoice(amount_msat=10**8, label='x', description='desc', - preimage=preimage)['payment_hash'] + l7.rpc.invoice(amount_msat=10**8, label='x', description='desc', + preimage=preimage)['payment_hash'] # First, the failed attempts (paying wrong node). CLTV1 - r = nodes[0].rpc.getroute(nodes[-2].info['id'], 10**8, 1)["route"] - nodes[0].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + r = route_to_l7(l1) + r[2]['id'] = l6.info['id'] + r[2]['channel'] = l5.get_channel_scid(l6) + l1.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match=r'INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): - nodes[0].rpc.waitsendpay(h) + l1.rpc.waitsendpay(h) - r = nodes[-1].rpc.getroute(nodes[1].info['id'], 10**8, 1)["route"] - nodes[-1].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + r = route_to_l1(l7) + r[2]['id'] = l2.info['id'] + r[2]['channel'] = l4.get_channel_scid(l2) + l7.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match=r'INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): - nodes[-1].rpc.waitsendpay(h) + l7.rpc.waitsendpay(h) # Now increment CLTV -> CLTV2 bitcoind.generate_block(1) - sync_blockheight(bitcoind, nodes) + sync_blockheight(bitcoind, (l1, l2, l3, l4, l5, l6, l7)) # Now, the live attempts with CLTV2 (blackholed by end nodes) - r = nodes[0].rpc.getroute(nodes[-1].info['id'], 10**8, 1)["route"] - nodes[0].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) - r = nodes[-1].rpc.getroute(nodes[0].info['id'], 10**8, 1)["route"] - nodes[-1].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + r = route_to_l7(l1) + l1.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + r = route_to_l1(l7) + l7.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # We send second HTLC from different node, since they refuse to send # multiple with same hash. - r = nodes[1].rpc.getroute(nodes[-1].info['id'], 10**8, 1)["route"] - nodes[1].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) - r = nodes[-2].rpc.getroute(nodes[0].info['id'], 10**8, 1)["route"] - nodes[-2].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + r = route_to_l7(l2) + l2.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + r = route_to_l1(l6) + l6.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Now increment CLTV -> CLTV3. bitcoind.generate_block(1) - sync_blockheight(bitcoind, nodes) + sync_blockheight(bitcoind, (l1, l2, l3, l4, l5, l6, l7)) - r = nodes[2].rpc.getroute(nodes[-1].info['id'], 10**8, 1)["route"] - nodes[2].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) - r = nodes[-3].rpc.getroute(nodes[0].info['id'], 10**8, 1)["route"] - nodes[-3].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + r = route_to_l7(l3) + l3.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) + # Final HTLC is actually from l5 itself... + r = route_to_l1(l7)[1:] + l5.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) - # Make sure HTLCs have reached the end. - nodes[0].daemon.wait_for_logs(['peer_in WIRE_UPDATE_ADD_HTLC'] * 3) - nodes[-1].daemon.wait_for_logs(['peer_in WIRE_UPDATE_ADD_HTLC'] * 3) + # Make sure HTLCs have reached the ends (including balance payment!) + l7.daemon.wait_for_logs(['peer_in WIRE_UPDATE_ADD_HTLC'] * 4) + l1.daemon.wait_for_logs(['peer_in WIRE_UPDATE_ADD_HTLC'] * 4) + + # We have 6 HTLCs trapped in l4-l5 channel. + assert len(only_one(only_one(l4.rpc.listpeers(l5.info['id'])['peers'])['channels'])['htlcs']) == 6 + + # We are all connected. + for n in l1, l2, l3, l4, l5, l6, l7: + assert all([p['connected'] for p in n.rpc.listpeers()['peers']]) - return h, nodes + return h, l1, l2, l3, l4, l5, l6, l7 @pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs") @pytest.mark.slow_test def test_onchain_multihtlc_our_unilateral(node_factory, bitcoind): """Node pushes a channel onchain with multiple HTLCs with same payment_hash """ - h, (l1, l2, l3, l4, l5, l6, l7) = setup_multihtlc_test(node_factory, bitcoind) + h, l1, l2, l3, l4, l5, l6, l7 = setup_multihtlc_test(node_factory, bitcoind) # Now l4 goes onchain with l4-l5 channel. l4.rpc.dev_fail(l5.info['id']) @@ -2850,8 +2905,8 @@ def test_onchain_multihtlc_our_unilateral(node_factory, bitcoind): l7.restart() # We disabled auto-reconnect so we'd detect breakage, so manually reconnect. - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - l7.rpc.connect(l6.info['id'], 'localhost', l6.port) + l1.rpc.connect(l4.info['id'], 'localhost', l4.port) + l7.rpc.connect(l5.info['id'], 'localhost', l5.port) # Wait for HTLCs to stabilize. l1.daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) @@ -2879,14 +2934,14 @@ def test_onchain_multihtlc_our_unilateral(node_factory, bitcoind): # No other channels should have failed. for n in l1, l2, l3, l6, l7: - assert all([p['connected'] for p in n.rpc.listpeers()['peers']]) + assert only_one(n.rpc.listpeers()['peers'])['connected'] @pytest.mark.developer("needs DEVELOPER=1 for dev_ignore_htlcs") @pytest.mark.slow_test def test_onchain_multihtlc_their_unilateral(node_factory, bitcoind): """Node pushes a channel onchain with multiple HTLCs with same payment_hash """ - h, (l1, l2, l3, l4, l5, l6, l7) = setup_multihtlc_test(node_factory, bitcoind) + h, l1, l2, l3, l4, l5, l6, l7 = setup_multihtlc_test(node_factory, bitcoind) # Now l5 goes onchain with l4-l5 channel. l5.rpc.dev_fail(l4.info['id']) @@ -2906,8 +2961,8 @@ def test_onchain_multihtlc_their_unilateral(node_factory, bitcoind): l7.restart() # We disabled auto-reconnect so we'd detect breakage, so manually reconnect. - l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - l7.rpc.connect(l6.info['id'], 'localhost', l6.port) + l1.rpc.connect(l4.info['id'], 'localhost', l4.port) + l7.rpc.connect(l5.info['id'], 'localhost', l5.port) # Wait for HTLCs to stabilize. l1.daemon.wait_for_logs(['peer_out WIRE_UPDATE_FAIL_HTLC'] * 3) @@ -2936,7 +2991,7 @@ def test_onchain_multihtlc_their_unilateral(node_factory, bitcoind): # No other channels should have failed. for n in l1, l2, l3, l6, l7: - assert all([p['connected'] for p in n.rpc.listpeers()['peers']]) + assert only_one(n.rpc.listpeers()['peers'])['connected'] @pytest.mark.developer("needs DEVELOPER=1") From 5260ea29114f9bf0c4bc99295231447218a4f896 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 30 Jul 2022 14:00:42 +0930 Subject: [PATCH 1233/1530] pytest: make sure we never break channels in multhtlc test. It's perfectlty safe to extend the timeout to 100 blocks, so let's do it. Signed-off-by: Rusty Russell --- tests/test_closing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 8d351094e0ad..66594687d8c1 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2804,13 +2804,15 @@ def setup_multihtlc_test(node_factory, bitcoind): def route_to_l7(src): """Route from l1, l2 or l3 to l7""" - return [{'id': l4.info['id'], 'channel': src.get_channel_scid(l4), 'amount_msat': "100002002msat", 'delay': 21}, + # We give extra CLTV on first hop, so we never break channel. + return [{'id': l4.info['id'], 'channel': src.get_channel_scid(l4), 'amount_msat': "100002002msat", 'delay': 115}, {'id': l5.info['id'], 'channel': l4.get_channel_scid(l5), 'amount_msat': "100001001msat", 'delay': 15}, {'id': l7.info['id'], 'channel': l5.get_channel_scid(l7), 'amount_msat': "100000000msat", 'delay': 9}] def route_to_l1(src): """Route from l6 or l7 to l1""" - return [{'id': l5.info['id'], 'channel': src.get_channel_scid(l5), 'amount_msat': "100002002msat", 'delay': 21}, + # We give extra CLTV on first hop, so we never break channel. + return [{'id': l5.info['id'], 'channel': src.get_channel_scid(l5), 'amount_msat': "100002002msat", 'delay': 115}, {'id': l4.info['id'], 'channel': l5.get_channel_scid(l4), 'amount_msat': "100001001msat", 'delay': 15}, {'id': l1.info['id'], 'channel': l4.get_channel_scid(l1), 'amount_msat': "100000000msat", 'delay': 9}] From ffb3217ea8c2f533345708e097432fc654d05058 Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Fri, 29 Jul 2022 15:14:21 +0100 Subject: [PATCH 1234/1530] docs: clarify the different way to build cln Signed-off-by: Vincenzo Palazzo --- doc/INSTALL.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index e841b8d36af8..7ba064839db4 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -69,13 +69,29 @@ If you want to build the Rust plugins (currently, cln-grpc): sudo apt-get install -y cargo rustfmt -Build lightning: +There are two ways to build core lightning, and this depends on how you want use it. - poetry install +To build cln to just install a tagged or master version you can use the following commands: + + pip3 install --upgrade pip + pip3 install mako mistune==0.8.4 mrkd ./configure - poetry run make + make sudo make install +N.B: if you want disable Rust because you do not want use it or simple you do not want the grpc-plugin, you can use `./configure --disable-rust`. + +To build core lightning for development purpose you can use the following commands: + + pip3 install poetry + poetry shell + poetry install + ./configure --enable-developer + make + make check VALGRIND=0 + +optionaly you can consider to use the `-j$(nproc)` in front of the `make` command to speed up the compilation. + Running lightning: bitcoind & From e0a48c2aa7b50391c200b414fbb4cdb1b08188b6 Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Wed, 3 Aug 2022 16:15:26 -0400 Subject: [PATCH 1235/1530] am_opener unused --- openingd/common.c | 1 - openingd/common.h | 1 - openingd/dualopend.c | 5 +---- openingd/openingd.c | 2 -- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/openingd/common.c b/openingd/common.c index c31bb7774d72..150b8faa5a46 100644 --- a/openingd/common.c +++ b/openingd/common.c @@ -19,7 +19,6 @@ bool check_config_bounds(const tal_t *ctx, struct amount_msat min_effective_htlc_capacity, const struct channel_config *remoteconf, const struct channel_config *localconf, - bool am_opener, bool option_anchor_outputs, char **err_reason) { diff --git a/openingd/common.h b/openingd/common.h index 838321867dda..99914cf7cee7 100644 --- a/openingd/common.h +++ b/openingd/common.h @@ -16,7 +16,6 @@ bool check_config_bounds(const tal_t *ctx, struct amount_msat min_effective_htlc_capacity, const struct channel_config *remoteconf, const struct channel_config *localconf, - bool am_opener, bool option_anchor_outputs, char **err_reason); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 1a418e100916..c2de0716b3ff 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -2260,7 +2260,6 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) state->min_effective_htlc_capacity, &tx_state->remoteconf, &tx_state->localconf, - false, true, /* v2 means we use anchor outputs */ &err_reason)) { negotiation_failed(state, "%s", err_reason); @@ -2966,7 +2965,7 @@ static void opener_start(struct state *state, u8 *msg) state->min_effective_htlc_capacity, &tx_state->remoteconf, &tx_state->localconf, - true, true, /* v2 means we use anchor outputs */ + true, /* v2 means we use anchor outputs */ &err_reason)) { negotiation_failed(state, "%s", err_reason); return; @@ -3217,7 +3216,6 @@ static void rbf_local_start(struct state *state, u8 *msg) state->min_effective_htlc_capacity, &tx_state->remoteconf, &tx_state->localconf, - false, true, /* v2 means we use anchor outputs */ &err_reason)) { open_err_warn(state, "%s", err_reason); @@ -3354,7 +3352,6 @@ static void rbf_remote_start(struct state *state, const u8 *rbf_msg) state->min_effective_htlc_capacity, &tx_state->remoteconf, &tx_state->localconf, - false, true, /* v2 means we use anchor outputs */ &err_reason)) { open_err_warn(state, "%s", err_reason); diff --git a/openingd/openingd.c b/openingd/openingd.c index fd9d5fa844e8..d2ae1cada07f 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -458,7 +458,6 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) state->min_effective_htlc_capacity, &state->remoteconf, &state->localconf, - true, feature_negotiated(state->our_features, state->their_features, OPT_ANCHOR_OUTPUTS), @@ -974,7 +973,6 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) state->min_effective_htlc_capacity, &state->remoteconf, &state->localconf, - false, feature_negotiated(state->our_features, state->their_features, OPT_ANCHOR_OUTPUTS), From d5d4e7b0195b8a6ad2e1065f605b2c2990fc0c0d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 23 Jul 2022 16:24:19 +0930 Subject: [PATCH 1236/1530] common/features: add channel_type definition, neaten header. Put all the normal spec-defined ones together. Signed-off-by: Rusty Russell --- common/features.c | 7 ++++++- common/features.h | 36 +++++++++++++++++------------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/common/features.c b/common/features.c index 5040c39accb3..f14b298f9e63 100644 --- a/common/features.c +++ b/common/features.c @@ -125,6 +125,11 @@ static const struct feature_style feature_styles[] = { * we refuse to parse it. */ [BOLT11_FEATURE] = FEATURE_REPRESENT, [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } }, + { OPT_CHANNEL_TYPE, + .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, + [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, + [BOLT11_FEATURE] = FEATURE_DONT_REPRESENT, + [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } }, }; struct dependency { @@ -441,7 +446,7 @@ const char *feature_name(const tal_t *ctx, size_t f) "option_onion_messages", /* https://github.com/lightningnetwork/lightning-rfc/pull/759 */ "option_want_peer_backup", /* 40/41 */ /* https://github.com/lightningnetwork/lightning-rfc/pull/881 */ "option_provide_peer_backup", /* https://github.com/lightningnetwork/lightning-rfc/pull/881 */ - NULL, + "option_channel_type", "option_scid_alias", /* https://github.com/lightning/bolts/pull/910 */ "option_payment_metadata", "option_zeroconf", /* 50/51, https://github.com/lightning/bolts/pull/910 */ diff --git a/common/features.h b/common/features.h index d5da02fdf79f..ccb914020bfd 100644 --- a/common/features.h +++ b/common/features.h @@ -99,14 +99,22 @@ struct feature_set *feature_set_dup(const tal_t *ctx, /* BOLT #9: * - * | Bits | Name |... - * | 0/1 | `option_data_loss_protect` |... - * | 3 | `initial_routing_sync` |... - * | 4/5 | `option_upfront_shutdown_script` |... - * | 6/7 | `gossip_queries` |... - * | 8/9 | `var_onion_optin` |... - * | 10/11 | `gossip_queries_ex` |... - * | 12/13 | `option_static_remotekey` |... + * | Bits | Name |... + * | 0/1 | `option_data_loss_protect` |... IN ... + * | 3 | `initial_routing_sync` |... I ... + * | 4/5 | `option_upfront_shutdown_script` |... IN ... + * | 6/7 | `gossip_queries` |... IN ... + * | 8/9 | `var_onion_optin` |... IN9 ... + * | 10/11 | `gossip_queries_ex` |... IN ... + * | 12/13 | `option_static_remotekey` |... IN ... + * | 14/15 | `payment_secret` |... IN9 ... + * | 16/17 | `basic_mpp` |... IN9 ... + * | 18/19 | `option_support_large_channel` |... IN ... + * | 20/21 | `option_anchor_outputs` |... IN ... + * | 22/23 | `option_anchors_zero_fee_htlc_tx` |... IN ... + * | 26/27 | `option_shutdown_anysegwit` |... IN ... + * | 44/45 | `option_channel_type` |... IN ... + * | 48/49 | `option_payment_metadata` |... 9 ... */ #define OPT_DATA_LOSS_PROTECT 0 #define OPT_INITIAL_ROUTING_SYNC 2 @@ -115,23 +123,13 @@ struct feature_set *feature_set_dup(const tal_t *ctx, #define OPT_VAR_ONION 8 #define OPT_GOSSIP_QUERIES_EX 10 #define OPT_STATIC_REMOTEKEY 12 - -/* BOLT #9: - * - * | 14/15 | `payment_secret` |... IN9 ... - * | 16/17 | `basic_mpp` |... IN9 ... - * | 18/19 | `option_support_large_channel` |... IN ... - * | 20/21 | `option_anchor_outputs` |... IN ... - * | 22/23 | `option_anchors_zero_fee_htlc_tx` |... IN ... - * | 26/27 | `option_shutdown_anysegwit` |... IN ... - * | 48/49 | `option_payment_metadata` |... 9 ... - */ #define OPT_PAYMENT_SECRET 14 #define OPT_BASIC_MPP 16 #define OPT_LARGE_CHANNELS 18 #define OPT_ANCHOR_OUTPUTS 20 #define OPT_ANCHORS_ZERO_FEE_HTLC_TX 22 #define OPT_SHUTDOWN_ANYSEGWIT 26 +#define OPT_CHANNEL_TYPE 44 #define OPT_PAYMENT_METADATA 48 /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9: From 80a6d9b58e3f3dd9e5fbb2087c83cb717e1e5469 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 23 Jul 2022 16:25:48 +0930 Subject: [PATCH 1237/1530] lightningd: set the channel_type feature. AFAICT we should have been doing this since we started sending and receiving it, but didn't. Signed-off-by: Rusty Russell Changelog-Added: Protocol: we now advertize the `option_channel_type` feature (which we actually supported since v0.10.2) --- lightningd/lightningd.c | 1 + tests/test_misc.py | 2 ++ tests/utils.py | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index eccf43800d30..39e561e02ddf 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -827,6 +827,7 @@ static struct feature_set *default_features(const tal_t *ctx) OPTIONAL_FEATURE(OPT_PAYMENT_METADATA), OPTIONAL_FEATURE(OPT_SCID_ALIAS), OPTIONAL_FEATURE(OPT_ZEROCONF), + OPTIONAL_FEATURE(OPT_CHANNEL_TYPE), #if EXPERIMENTAL_FEATURES OPTIONAL_FEATURE(OPT_ANCHOR_OUTPUTS), OPTIONAL_FEATURE(OPT_QUIESCE), diff --git a/tests/test_misc.py b/tests/test_misc.py index a29fc8778274..9a7cf873811a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1828,11 +1828,13 @@ def test_list_features_only(node_factory): expected += ['option_shutdown_anysegwit/odd'] expected += ['option_quiesce/odd'] expected += ['option_onion_messages/odd'] + expected += ['option_channel_type/odd'] expected += ['option_scid_alias/odd'] expected += ['option_zeroconf/odd'] expected += ['supports_open_accept_channel_type'] else: expected += ['option_shutdown_anysegwit/odd'] + expected += ['option_channel_type/odd'] expected += ['option_scid_alias/odd'] expected += ['option_zeroconf/odd'] assert features == expected diff --git a/tests/utils.py b/tests/utils.py index ce14fcbf1255..799fbccf431c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -37,7 +37,7 @@ def hex_bits(features): def expected_peer_features(wumbo_channels=False, extra=[]): """Return the expected peer features hexstring for this configuration""" - features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 47, 51] + features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 45, 47, 51] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39] @@ -59,7 +59,7 @@ def expected_peer_features(wumbo_channels=False, extra=[]): # features for the 'node' and the 'peer' feature sets def expected_node_features(wumbo_channels=False, extra=[]): """Return the expected node features hexstring for this configuration""" - features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 47, 51, 55] + features = [1, 5, 7, 8, 11, 13, 14, 17, 27, 45, 47, 51, 55] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39] From aca9c7d49ca787ad478dbbe19846b7206fa73a6d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 3 Aug 2022 16:45:44 +0200 Subject: [PATCH 1238/1530] pay: Annotate suspended payments with the groupid they mirror This should prevent us from accidentally completing a payment twice, when replaying the result of an actual attempt against pay call that was suspended due to it still being pending. --- plugins/pay.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/pay.c b/plugins/pay.c index fc39b8bb3a90..5bf33031a6d5 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -787,6 +787,10 @@ payment_listsendpays_previous(struct command *cmd, const char *buf, u64 last_group = 0; /* Do we have pending sendpays for the previous attempt? */ bool pending = false; + + /* Group ID of the first pending payment, this will be the one + * who's result gets replayed if we end up suspending. */ + u64 pending_group_id = 0; /* Did a prior attempt succeed? */ bool completed = false; @@ -855,6 +859,11 @@ payment_listsendpays_previous(struct command *cmd, const char *buf, status = json_get_member(buf, t, "status"); completed |= json_tok_streq(buf, status, "complete"); pending |= json_tok_streq(buf, status, "pending"); + + /* Remember the group id of the first pending group so + * we can replay its result later. */ + if (!pending_group_id && pending) + pending_group_id = groupid; } if (completed) { @@ -874,7 +883,9 @@ payment_listsendpays_previous(struct command *cmd, const char *buf, /* We suspend this call and wait for the * `on_payment_success` or `on_payment_failure` * handler of the currently running payment to notify - * us about its completion. */ + * us about its completion. We latch on to the result + * from the call we extracted above. */ + p->groupid = pending_group_id; return command_still_pending(cmd); } p->groupid = last_group + 1; From 093933b14d4d199267eb414072721704fc78428d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 3 Aug 2022 16:47:18 +0200 Subject: [PATCH 1239/1530] pay: Do not replay results against payments that were not suspended Suspended payments now have the same groupid as the actual attempt, this allows us to identify pay calls that were suspended due to us and terminate only those. Changelog-Fixed pay: Fixed a crash when `pay` was called multiple times while an attempt was already in progress. --- plugins/pay.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 5bf33031a6d5..d6efa10e1fc5 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -589,7 +589,12 @@ static void on_payment_success(struct payment *payment) * `payment_finished`. */ if (payment == p) continue; - if (!sha256_eq(payment->payment_hash, p->payment_hash)) + + /* Both groupid and payment_hash must match. This is + * because when we suspended the payment itself, we + * set the groupid to match. */ + if (!sha256_eq(payment->payment_hash, p->payment_hash) || + payment->groupid != p->groupid) continue; if (p->cmd == NULL) continue; @@ -675,7 +680,11 @@ static void on_payment_failure(struct payment *payment) * `payment_finished`. */ if (payment == p) continue; - if (!sha256_eq(payment->payment_hash, p->payment_hash)) + + /* When we suspended we've set the groupid to match so + * we'd know which calls were duplicates. */ + if (!sha256_eq(payment->payment_hash, p->payment_hash) || + payment->groupid != p->groupid) continue; if (p->cmd == NULL) continue; From 65a449e2c384ec1b86a7b437d6df82054b4c75a0 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 3 Aug 2022 16:50:39 +0200 Subject: [PATCH 1240/1530] pay: Remove use-after-free bug Technically this is a use-after-free since `command_finished` frees the `cmd` which is also the parent of `p`, so reset it early. All paths lead to `command_finished` so setting it early is ok. Reported-by: Rusty Russell <@rustyrussell> --- plugins/pay.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index d6efa10e1fc5..3c25c70632cf 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -577,6 +577,7 @@ static void on_payment_success(struct payment *payment) struct payment *p; struct payment_tree_result result = payment_collect_result(payment); struct json_stream *ret; + struct command *cmd; assert(result.treestates & PAYMENT_STEP_SUCCESS); assert(result.leafstates & PAYMENT_STEP_SUCCESS); assert(result.preimage != NULL); @@ -599,7 +600,10 @@ static void on_payment_success(struct payment *payment) if (p->cmd == NULL) continue; - ret = jsonrpc_stream_success(p->cmd); + cmd = p->cmd; + p->cmd = NULL; + + ret = jsonrpc_stream_success(cmd); json_add_node_id(ret, "destination", p->destination); json_add_sha256(ret, "payment_hash", p->payment_hash); json_add_timeabs(ret, "created_at", p->start_time); @@ -619,8 +623,7 @@ static void on_payment_success(struct payment *payment) json_add_preimage(ret, "payment_preimage", result.preimage); json_add_string(ret, "status", "complete"); - if (command_finished(p->cmd, ret)) {/* Ignore result. */} - p->cmd = NULL; + if (command_finished(cmd, ret)) {/* Ignore result. */} } } @@ -690,7 +693,7 @@ static void on_payment_failure(struct payment *payment) continue; cmd = p->cmd; - + p->cmd = NULL; if (p->aborterror != NULL) { /* We set an explicit toplevel error message, * so let's report that. */ @@ -777,7 +780,6 @@ static void on_payment_failure(struct payment *payment) if (command_finished(cmd, ret)) { /* Ignore result. */} } - p->cmd = NULL; } } From da0b651803f47b3e45eb5be55d728d5e6398e298 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 3 Aug 2022 16:56:55 +0200 Subject: [PATCH 1241/1530] pay: Use safe list traversal when completing suspended payments When traversing the list we call `command_finished` which modifies the list we are traversing. This ensures we don't end up advancing in the list iteration. Reported-by: Rusty Russell <@rustyrussell> --- plugins/pay.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/pay.c b/plugins/pay.c index 3c25c70632cf..ed0994182a20 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -574,7 +574,7 @@ static const char *init(struct plugin *p, static void on_payment_success(struct payment *payment) { - struct payment *p; + struct payment *p, *nxt; struct payment_tree_result result = payment_collect_result(payment); struct json_stream *ret; struct command *cmd; @@ -585,7 +585,7 @@ static void on_payment_success(struct payment *payment) /* Iterate through any pending payments we suspended and * terminate them. */ - list_for_each(&payments, p, list) { + list_for_each_safe(&payments, p, nxt, list) { /* The result for the active payment is returned in * `payment_finished`. */ if (payment == p) @@ -672,9 +672,9 @@ static void payment_json_add_attempts(struct json_stream *s, static void on_payment_failure(struct payment *payment) { - struct payment *p; + struct payment *p, *nxt; struct payment_tree_result result = payment_collect_result(payment); - list_for_each(&payments, p, list) + list_for_each_safe(&payments, p, nxt, list) { struct json_stream *ret; struct command *cmd; From 3fcf60ab7c2450c46551c1f00b52d2b17d9f7018 Mon Sep 17 00:00:00 2001 From: niftynei Date: Sun, 31 Jul 2022 16:55:58 -0500 Subject: [PATCH 1242/1530] bkpr: track channel rebalances, display in listincome Track rebalances, and report income events for them. Previously `listincome` would report: - invoice event, debit, outgoing channel - invoice_fee event, debit, outgoing channel - invoice event, credit, inbound channel Now reports: - rebalance_fee, debit, outgoing channel (same value as invoice_fee above) Note: only applies on channel events; if a rebalance falls to chain we'll use the older style of accounting. Changelog-None --- doc/lightning-bkpr-listaccountevents.7.md | 3 +- .../bkpr-listaccountevents.schema.json | 4 + plugins/bkpr/account_entry.c | 1 + plugins/bkpr/account_entry.h | 3 +- plugins/bkpr/bookkeeper.c | 9 +- plugins/bkpr/chain_event.h | 3 + plugins/bkpr/channel_event.c | 2 + plugins/bkpr/channel_event.h | 3 + plugins/bkpr/db.c | 1 + plugins/bkpr/incomestmt.c | 21 +++- plugins/bkpr/recorder.c | 117 +++++++++++++++++- plugins/bkpr/recorder.h | 17 +++ plugins/bkpr/test/run-recorder.c | 97 +++++++++++++++ tests/test_bookkeeper.py | 65 +++++++++- 14 files changed, 339 insertions(+), 7 deletions(-) diff --git a/doc/lightning-bkpr-listaccountevents.7.md b/doc/lightning-bkpr-listaccountevents.7.md index 4e132eedf54c..ad350854f440 100644 --- a/doc/lightning-bkpr-listaccountevents.7.md +++ b/doc/lightning-bkpr-listaccountevents.7.md @@ -45,6 +45,7 @@ If **type** is "onchain_fee": If **type** is "channel": - **fees_msat** (msat, optional): Amount paid in fees + - **is_rebalance** (boolean, optional): Is this payment part of a rebalance - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. - **part_id** (u32, optional): Counter for multi-part payments @@ -66,4 +67,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f8538b1d1e6cda7cd801690e5c09741c8a843b27cc922065598914516c16d2b3) +[comment]: # ( SHA256STAMP:8568188808cb649d7182ffb628950b93b18406a0498b5b6768371bc94375e258) diff --git a/doc/schemas/bkpr-listaccountevents.schema.json b/doc/schemas/bkpr-listaccountevents.schema.json index ca0fa6c8770f..45c98c0f3f37 100644 --- a/doc/schemas/bkpr-listaccountevents.schema.json +++ b/doc/schemas/bkpr-listaccountevents.schema.json @@ -165,6 +165,10 @@ "type": "msat", "description": "Amount paid in fees" }, + "is_rebalance": { + "type": "boolean", + "description": "Is this payment part of a rebalance" + }, "payment_id": { "type": "hex", "description": "lightning payment identifier. For an htlc, this will be the preimage." diff --git a/plugins/bkpr/account_entry.c b/plugins/bkpr/account_entry.c index 4aa6d5489e3e..0606c69b80aa 100644 --- a/plugins/bkpr/account_entry.c +++ b/plugins/bkpr/account_entry.c @@ -8,6 +8,7 @@ static const char *tags[] = { "journal_entry", "penalty_adj", "invoice_fee", + "rebalance_fee", }; const char *account_entry_tag_str(enum account_entry_tag tag) diff --git a/plugins/bkpr/account_entry.h b/plugins/bkpr/account_entry.h index c04f4dafabc3..8823f672ea86 100644 --- a/plugins/bkpr/account_entry.h +++ b/plugins/bkpr/account_entry.h @@ -2,11 +2,12 @@ #define LIGHTNING_PLUGINS_BKPR_ACCOUNT_ENTRY_H #include "config.h" -#define NUM_ACCOUNT_ENTRY_TAGS (INVOICEFEE + 1) +#define NUM_ACCOUNT_ENTRY_TAGS (REBALANCEFEE + 1) enum account_entry_tag { JOURNAL_ENTRY = 0, PENALTY_ADJ = 1, INVOICEFEE = 2, + REBALANCEFEE= 3, }; /* Convert an enum into a string */ diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 84fade03687f..b823a39594f0 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1645,6 +1645,7 @@ parse_and_log_channel_move(struct command *cmd, e->timestamp = timestamp; e->tag = mvt_tag_str(tags[0]); e->desc = tal_steal(e, desc); + e->rebalance_id = NULL; /* Go find the account for this event */ db_begin_transaction(db); @@ -1656,7 +1657,6 @@ parse_and_log_channel_move(struct command *cmd, acct_name); log_channel_event(db, acct, e); - db_commit_transaction(db); /* Check for invoice desc data, necessary */ if (e->payment_id) { @@ -1664,6 +1664,12 @@ parse_and_log_channel_move(struct command *cmd, if (tags[i] != INVOICE) continue; + /* We only do rebalance checks for debits, + * the credit event always arrives first */ + if (!amount_msat_zero(e->debit)) + maybe_record_rebalance(db, e); + + db_commit_transaction(db); /* Keep memleak happy */ tal_steal(tmpctx, e); return lookup_invoice_desc(cmd, e->credit, @@ -1671,6 +1677,7 @@ parse_and_log_channel_move(struct command *cmd, } } + db_commit_transaction(db); return notification_handled(cmd); } diff --git a/plugins/bkpr/chain_event.h b/plugins/bkpr/chain_event.h index 3fea8f29dca5..a9c5f3a6a71c 100644 --- a/plugins/bkpr/chain_event.h +++ b/plugins/bkpr/chain_event.h @@ -34,6 +34,9 @@ struct chain_event { * we'll need to watch it for longer */ bool stealable; + /* Is this a rebalance event? */ + bool rebalance; + /* Amount we received in this event */ struct amount_msat credit; diff --git a/plugins/bkpr/channel_event.c b/plugins/bkpr/channel_event.c index 3952a0b9f3f5..89646b964724 100644 --- a/plugins/bkpr/channel_event.c +++ b/plugins/bkpr/channel_event.c @@ -27,6 +27,7 @@ struct channel_event *new_channel_event(const tal_t *ctx, ev->part_id = part_id; ev->timestamp = timestamp; ev->desc = NULL; + ev->rebalance_id = NULL; return ev; } @@ -50,5 +51,6 @@ void json_add_channel_event(struct json_stream *out, json_add_u64(out, "timestamp", ev->timestamp); if (ev->desc) json_add_string(out, "description", ev->desc); + json_add_bool(out, "is_rebalance", ev->rebalance_id != NULL); json_object_end(out); } diff --git a/plugins/bkpr/channel_event.h b/plugins/bkpr/channel_event.h index ec09858bbe91..3cd8c67b3080 100644 --- a/plugins/bkpr/channel_event.h +++ b/plugins/bkpr/channel_event.h @@ -46,6 +46,9 @@ struct channel_event { /* Description, usually from invoice */ const char *desc; + + /* ID of paired event, iff is a rebalance */ + u64 *rebalance_id; }; struct channel_event *new_channel_event(const tal_t *ctx, diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 1db77327b06f..c47dfe866644 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -98,6 +98,7 @@ static struct migration db_migrations[] = { {SQL("ALTER TABLE chain_events ADD stealable INTEGER;"), NULL}, {SQL("ALTER TABLE chain_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, {SQL("ALTER TABLE channel_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE channel_events ADD rebalance_id BIGINT DEFAULT NULL;"), NULL}, }; static bool db_migrate(struct plugin *p, struct db *db, bool *created) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index e26dcf452ce5..c017f34d01d7 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -222,6 +222,16 @@ static struct income_event *paid_invoice_fee(const tal_t *ctx, return iev; } +static struct income_event *rebalance_fee(const tal_t *ctx, + struct channel_event *ev) +{ + struct income_event *iev; + iev = channel_to_income(ctx, ev, AMOUNT_MSAT(0), ev->fees); + iev->tag = tal_free(ev->tag); + iev->tag = (char *)account_entry_tag_str(REBALANCEFEE); + return iev; +} + static struct income_event *maybe_channel_income(const tal_t *ctx, struct channel_event *ev) { @@ -235,6 +245,10 @@ static struct income_event *maybe_channel_income(const tal_t *ctx, } if (streq(ev->tag, "invoice")) { + /* Skip events for rebalances */ + if (ev->rebalance_id) + return NULL; + /* If it's a payment, we note fees separately */ if (!amount_msat_zero(ev->debit)) { struct amount_msat paid; @@ -383,11 +397,14 @@ struct income_event **list_income_events(const tal_t *ctx, if (ev) tal_arr_expand(&evs, ev); - /* Breakout fees on sent payments, if present */ + /* Report fees on payments, if present */ if (streq(chan->tag, "invoice") && !amount_msat_zero(chan->debit) && !amount_msat_zero(chan->fees)) { - ev = paid_invoice_fee(evs, chan); + if (!chan->rebalance_id) + ev = paid_invoice_fee(evs, chan); + else + ev = rebalance_fee(evs, chan); tal_arr_expand(&evs, ev); } diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index f2a0f4f4c777..301cac060f45 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -112,9 +112,29 @@ static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt else e->desc = NULL; + if (!db_col_is_null(stmt, "e.rebalance_id")) { + e->rebalance_id = tal(e, u64); + *e->rebalance_id = db_col_u64(stmt, "e.rebalance_id"); + } else + e->rebalance_id = NULL; + return e; } +static struct rebalance *stmt2rebalance(const tal_t *ctx, struct db_stmt *stmt) +{ + struct rebalance *r = tal(ctx, struct rebalance); + + r->in_ev_id = db_col_u64(stmt, "in_e.id"); + r->out_ev_id = db_col_u64(stmt, "out_e.id"); + r->in_acct_name = db_col_strdup(r, stmt, "in_acct.name"); + r->out_acct_name = db_col_strdup(r, stmt, "out_acct.name"); + db_col_amount_msat(stmt, "in_e.credit", &r->rebal_msat); + db_col_amount_msat(stmt, "out_e.fees", &r->fee_msat); + + return r; +} + struct chain_event **list_chain_events_timebox(const tal_t *ctx, struct db *db, u64 start_time, @@ -889,6 +909,7 @@ struct channel_event **list_channel_events_timebox(const tal_t *ctx, ", e.part_id" ", e.timestamp" ", e.ev_desc" + ", e.rebalance_id" " FROM channel_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -936,6 +957,7 @@ struct channel_event **account_get_channel_events(const tal_t *ctx, ", e.part_id" ", e.timestamp" ", e.ev_desc" + ", e.rebalance_id" " FROM channel_events e" " LEFT OUTER JOIN accounts a" " ON a.id = e.account_id" @@ -1339,9 +1361,10 @@ void log_channel_event(struct db *db, ", part_id" ", timestamp" ", ev_desc" + ", rebalance_id" ")" " VALUES" - " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); db_bind_text(stmt, 1, e->tag); @@ -1360,6 +1383,11 @@ void log_channel_event(struct db *db, else db_bind_null(stmt, 9); + if (e->rebalance_id) + db_bind_u64(stmt, 10, *e->rebalance_id); + else + db_bind_null(stmt, 10); + db_exec_prepared_v2(stmt); e->db_id = db_last_insert_id_v2(stmt); e->acct_db_id = acct->db_id; @@ -1644,6 +1672,93 @@ static char *is_closed_channel_txid(const tal_t *ctx, struct db *db, return NULL; } +void maybe_record_rebalance(struct db *db, + struct channel_event *out) +{ + /* If there's a matching credit event, this is + * a rebalance. Mark everything with the payment_id + * and amt as such. If you repeat a payment_id + * with the same amt, they'll be marked as rebalances + * also */ + struct db_stmt *stmt; + struct amount_msat credit; + bool ok; + + /* The amount of we were credited is debit - fees */ + ok = amount_msat_sub(&credit, out->debit, out->fees); + assert(ok); + + stmt = db_prepare_v2(db, SQL("SELECT " + " e.id" + " FROM channel_events e" + " WHERE e.payment_id = ?" + " AND e.credit = ?" + " AND e.rebalance_id IS NULL")); + + db_bind_sha256(stmt, 0, out->payment_id); + db_bind_amount_msat(stmt, 1, &credit); + db_query_prepared(stmt); + + if (!db_step(stmt)) { + /* No matching invoice found */ + tal_free(stmt); + return; + } + + /* We just take the first one */ + out->rebalance_id = tal(out, u64); + *out->rebalance_id = db_col_u64(stmt, "e.id"); + tal_free(stmt); + + /* Set rebalance flag on both records */ + stmt = db_prepare_v2(db, SQL("UPDATE channel_events SET" + " rebalance_id = ?" + " WHERE" + " id = ?")); + db_bind_u64(stmt, 0, *out->rebalance_id); + db_bind_u64(stmt, 1, out->db_id); + db_exec_prepared_v2(take(stmt)); + + stmt = db_prepare_v2(db, SQL("UPDATE channel_events SET" + " rebalance_id = ?" + " WHERE" + " id = ?")); + db_bind_u64(stmt, 0, out->db_id); + db_bind_u64(stmt, 1, *out->rebalance_id); + db_exec_prepared_v2(take(stmt)); +} + +struct rebalance **list_rebalances(const tal_t *ctx, struct db *db) +{ + struct rebalance **result; + struct db_stmt *stmt; + + stmt = db_prepare_v2(db, SQL("SELECT " + " in_e.id" + ", out_e.id" + ", in_acct.name" + ", out_acct.name" + ", in_e.credit" + ", out_e.fees" + " FROM channel_events in_e" + " LEFT OUTER JOIN channel_events out_e" + " ON in_e.rebalance_id = out_e.id" + " LEFT OUTER JOIN accounts out_acct" + " ON out_acct.id = out_e.account_id" + " LEFT OUTER JOIN accounts in_acct" + " ON in_acct.id = in_e.account_id" + " WHERE in_e.rebalance_id IS NOT NULL" + " AND in_e.credit > 0")); + db_query_prepared(stmt); + result = tal_arr(ctx, struct rebalance *, 0); + while (db_step(stmt)) { + struct rebalance *r = stmt2rebalance(result, stmt); + tal_arr_expand(&result, r); + } + tal_free(stmt); + return result; +} + char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db, struct bitcoin_txid *txid) { diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 83714c05e451..ece697fde954 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -41,6 +41,15 @@ struct txo_set { struct txo_pair **pairs; }; +struct rebalance { + u64 in_ev_id; + u64 out_ev_id; + char *in_acct_name; + char *out_acct_name; + struct amount_msat rebal_msat; + struct amount_msat fee_msat; +}; + /* Get all accounts */ struct account **list_accounts(const tal_t *ctx, struct db *db); @@ -200,6 +209,14 @@ void add_payment_hash_desc(struct db *db, * height an input was spent into */ void maybe_closeout_external_deposits(struct db *db, struct chain_event *ev); +/* Keep track of rebalancing payments (payments paid to/from ourselves. + * Returns true if was rebalance */ +void maybe_record_rebalance(struct db *db, + struct channel_event *out); + +/* List all rebalances */ +struct rebalance **list_rebalances(const tal_t *ctx, struct db *db); + /* Log a channel event */ void log_channel_event(struct db *db, const struct account *acct, diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index e2380b1a3e81..3382f6a9a81c 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -283,6 +283,9 @@ static bool channel_events_eq(struct channel_event *e1, struct channel_event *e2 CHECK(amount_msat_eq(e1->credit, e2->credit)); CHECK(amount_msat_eq(e1->debit, e2->debit)); CHECK(amount_msat_eq(e1->fees, e2->fees)); + CHECK((e1->rebalance_id != NULL) == (e2->rebalance_id != NULL)); + if (e1->rebalance_id) + CHECK(*e1->rebalance_id == *e2->rebalance_id); CHECK(streq(e1->currency, e2->currency)); CHECK((e1->payment_id != NULL) == (e2->payment_id != NULL)); if (e1->payment_id) @@ -311,6 +314,8 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2) CHECK(streq(e1->currency, e2->currency)); CHECK(e1->timestamp == e2->timestamp); CHECK(e1->blockheight == e2->blockheight); + CHECK(e1->stealable == e2->stealable); + CHECK(e1->ignored == e2->ignored); CHECK(bitcoin_outpoint_eq(&e1->outpoint, &e2->outpoint)); CHECK((e1->spending_txid != NULL) == (e2->spending_txid != NULL)); @@ -346,6 +351,7 @@ static struct channel_event *make_channel_event(const tal_t *ctx, ev->part_id = 19; ev->tag = tag; ev->desc = tal_fmt(ev, "description"); + ev->rebalance_id = NULL; return ev; } @@ -869,6 +875,92 @@ static bool test_onchain_fee_chan_open(const tal_t *ctx, struct plugin *p) return true; } +static bool test_channel_rebalances(const tal_t *ctx, struct plugin *p) +{ + bool created; + struct db *db = db_setup(ctx, p, tmp_dsn(ctx), &created); + struct channel_event *ev1, *ev2, *ev3, **chan_evs; + struct rebalance **rebals; + struct account *acct1, *acct2, *acct3; + struct node_id peer_id; + + memset(&peer_id, 3, sizeof(struct node_id)); + acct1 = new_account(ctx, tal_fmt(ctx, "one"), &peer_id); + acct2 = new_account(ctx, tal_fmt(ctx, "two"), &peer_id); + acct3 = new_account(ctx, tal_fmt(ctx, "three"), &peer_id); + + db_begin_transaction(db); + + account_add(db, acct1); + account_add(db, acct2); + account_add(db, acct3); + + /* Simulate a rebalance of 100msats, w/ a 12msat fee */ + ev1 = make_channel_event(ctx, "invoice", + AMOUNT_MSAT(100), + AMOUNT_MSAT(0), + 'A'); + ev1->fees = AMOUNT_MSAT(0); + log_channel_event(db, acct1, ev1); + + ev2 = make_channel_event(ctx, "invoice", + AMOUNT_MSAT(0), + AMOUNT_MSAT(112), + 'A'); + ev2->fees = AMOUNT_MSAT(12); + log_channel_event(db, acct2, ev2); + + /* Third event w/ same preimage but diff amounts */ + ev3 = make_channel_event(ctx, "invoice", + AMOUNT_MSAT(105), + AMOUNT_MSAT(0), + 'A'); + log_channel_event(db, acct3, ev3); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + db_begin_transaction(db); + chan_evs = account_get_channel_events(ctx, db, acct1); + CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + + chan_evs = account_get_channel_events(ctx, db, acct2); + CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + + chan_evs = account_get_channel_events(ctx, db, acct3); + CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + + maybe_record_rebalance(db, ev2); + CHECK(ev2->rebalance_id != NULL); + + /* Both events should be marked as rebalance */ + chan_evs = account_get_channel_events(ctx, db, acct1); + CHECK(tal_count(chan_evs) == 1 && chan_evs[0]->rebalance_id); + + chan_evs = account_get_channel_events(ctx, db, acct2); + CHECK(tal_count(chan_evs) == 1 && chan_evs[0]->rebalance_id); + + /* Third event is not a rebalance though */ + chan_evs = account_get_channel_events(ctx, db, acct3); + CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); + + /* Did we get an accurate rebalances entry? */ + rebals = list_rebalances(ctx, db); + + CHECK(tal_count(rebals) == 1); + + CHECK(rebals[0]->in_ev_id == ev1->db_id); + CHECK(rebals[0]->out_ev_id == ev2->db_id); + CHECK(streq(rebals[0]->in_acct_name, "one")); + CHECK(streq(rebals[0]->out_acct_name, "two")); + CHECK(amount_msat_eq(rebals[0]->rebal_msat, AMOUNT_MSAT(100))); + CHECK(amount_msat_eq(rebals[0]->fee_msat, AMOUNT_MSAT(12))); + + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + return true; +} + static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) { bool created; @@ -897,6 +989,7 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev1->timestamp = 11111; ev1->part_id = 19; ev1->desc = tal_strdup(ev1, "hello desc1"); + ev1->rebalance_id = NULL; /* Passing unknown tags in should be ok */ ev1->tag = "hello"; @@ -913,6 +1006,8 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev2->part_id = 0; ev2->tag = tal_fmt(ev2, "deposit"); ev2->desc = NULL; + ev2->rebalance_id = tal(ev2, u64); + *ev2->rebalance_id = 1; ev3 = tal(ctx, struct channel_event); ev3->payment_id = tal(ev3, struct sha256); @@ -925,6 +1020,7 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) ev3->part_id = 5; ev3->tag = tal_fmt(ev3, "routed"); ev3->desc = NULL; + ev3->rebalance_id = NULL; db_begin_transaction(db); log_channel_event(db, acct, ev1); @@ -1322,6 +1418,7 @@ int main(int argc, char *argv[]) ok &= test_account_balances(tmpctx, plugin); ok &= test_onchain_fee_chan_close(tmpctx, plugin); ok &= test_onchain_fee_chan_open(tmpctx, plugin); + ok &= test_channel_rebalances(tmpctx, plugin); ok &= test_onchain_fee_wallet_spend(tmpctx, plugin); } diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 4a18813fb0fd..9453b362de64 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -3,7 +3,7 @@ from pyln.client import Millisatoshi from fixtures import TEST_NETWORK from utils import ( - sync_blockheight, wait_for, only_one, first_channel_id + sync_blockheight, wait_for, only_one, first_channel_id, TIMEOUT ) from pathlib import Path @@ -571,3 +571,66 @@ def test_bookkeeping_descriptions(node_factory, bitcoind, chainparams): l2_koinly_csv = open(koinly_path, 'rb').read() assert l2_koinly_csv.find(bolt11_exp) >= 0 assert l2_koinly_csv.find(bolt12_exp) >= 0 + + +def test_rebalance_tracking(node_factory, bitcoind): + """ + We identify rebalances (invoices paid and received by our node), + this allows us to filter them out of "incomes" (self-transfers are not income/exp) + and instead only display the cost incurred to move the payment (correctly + marked as a rebalance) + + 1 -> 2 -> 3 -> 1 + """ + + rebal_amt = 3210 + l1, l2, l3 = node_factory.get_nodes(3) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l3.rpc.connect(l1.info['id'], 'localhost', l1.port) + c12, _ = l1.fundchannel(l2, 10**7, wait_for_active=True) + c23, _ = l2.fundchannel(l3, 10**7, wait_for_active=True) + c31, _ = l3.fundchannel(l1, 10**7, wait_for_active=True) + + # Build a rebalance payment + invoice = l1.rpc.invoice(rebal_amt, 'to_self', 'to_self') + pay_hash = invoice['payment_hash'] + pay_sec = invoice['payment_secret'] + + route = [{ + 'id': l2.info['id'], + 'channel': c12, + 'direction': int(not l1.info['id'] < l2.info['id']), + 'amount_msat': rebal_amt + 1001, + 'style': 'tlv', + 'delay': 24, + }, { + 'id': l3.info['id'], + 'channel': c23, + 'direction': int(not l2.info['id'] < l3.info['id']), + 'amount_msat': rebal_amt + 500, + 'style': 'tlv', + 'delay': 16, + }, { + 'id': l1.info['id'], + 'channel': c31, + 'direction': int(not l3.info['id'] < l1.info['id']), + 'amount_msat': rebal_amt, + 'style': 'tlv', + 'delay': 8, + }] + + l1.rpc.sendpay(route, pay_hash, payment_secret=pay_sec) + result = l1.rpc.waitsendpay(pay_hash, TIMEOUT) + assert result['status'] == 'complete' + + wait_for(lambda: 'invoice' not in [ev['tag'] for ev in l1.rpc.bkpr_listincome()['income_events']]) + inc_evs = l1.rpc.bkpr_listincome()['income_events'] + outbound_chan_id = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['channel_id'] + + outbound_ev = only_one([ev for ev in inc_evs if ev['tag'] == 'rebalance_fee']) + assert outbound_ev['account'] == outbound_chan_id + assert outbound_ev['debit_msat'] == Millisatoshi(1001) + assert outbound_ev['credit_msat'] == Millisatoshi(0) + assert outbound_ev['payment_id'] == pay_hash From 016ce2b925c9d864017ffb6e65e37957b56128fd Mon Sep 17 00:00:00 2001 From: niftynei Date: Sun, 31 Jul 2022 17:09:53 -0500 Subject: [PATCH 1243/1530] build: ignore docker/image build directories Changelog-None --- .gitignore | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4af97d0e4233..8ddca96722c1 100644 --- a/.gitignore +++ b/.gitignore @@ -53,7 +53,6 @@ contrib/pyln-*/dist/ contrib/pyln-*/pyln_*.egg-info/ contrib/pyln-*/.eggs/ contrib/pyln-*/pyln/*/__version__.py -release/ tests/plugins/test_selfdisable_after_getmanifest # Ignore generated files @@ -75,3 +74,9 @@ tests/primitives_pb2_grpc.py # Rust targets target plugins/cln-grpc + +# Build directories +bionic/ +focal/ +jammy/ +release/ From 25b4249f540cc85b8b6c44ca17637548997f27b7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 Aug 2022 11:31:19 +0930 Subject: [PATCH 1244/1530] doc: fix decode schema for bolt11 routehints. I decode a routehint in the next patch, and it barfed: ``` > assert only_one(l1.rpc.decode(inv['bolt11'])['routes'])['short_channel_id'] == alias23 tests/test_opening.py:1515: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ contrib/pyln-client/pyln/client/lightning.py:321: in wrapper return self.call(name, payload=args) contrib/pyln-testing/pyln/testing/utils.py:691: in call schemas[1].validate(res) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = Validator(schema={'$schema': 'http://json-...ft-07/schema#', 'allOf': [{'if': {'properties': {'type': {'enum': [...], ...iption': 'if this is f... diagnostics!', 'type': 'boolean'}}, 'required': ['type', 'valid'], ...}, format_checker=None) args = ({'amount_msat': 10msat, 'created_at': 1659923931, 'currency': 'bcrt', 'description': 'desc', ...},), kwargs = {} error = def validate(self, *args, **kwargs): for error in self.iter_errors(*args, **kwargs): > raise error E jsonschema.exceptions.ValidationError: 1msat is not of type 'u32' E E Failed validating 'type' in schema['allOf'][6]['then']['properties']['routes']['items']['items']['properties']['fee_base_msat']: E {'description': 'the base fee for payments', 'type': 'u32'} E E On instance['routes'][0][0]['fee_base_msat']: E 1msat ``` Signed-off-by: Rusty Russell --- doc/lightning-decode.7.md | 4 ++-- doc/schemas/decode.schema.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 108aed713b1d..078e45a12b7c 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -156,7 +156,7 @@ If **type** is "bolt11 invoice", and **valid** is *true*: - 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_base_msat** (msat): 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: @@ -201,4 +201,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a3963c3e0061b0d42a1f9e2f2a9012df780fce0264c6785f0311909b01f78af2) +[comment]: # ( SHA256STAMP:3e522a9788bb79302e4c4386c3937b7dcd8359d1b894364ac3e884bd3f695850) diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json index 70d3054602c3..66b86a4f2a9a 100644 --- a/doc/schemas/decode.schema.json +++ b/doc/schemas/decode.schema.json @@ -869,7 +869,7 @@ "description": "a channel to the next peer" }, "fee_base_msat": { - "type": "u32", + "type": "msat", "description": "the base fee for payments" }, "fee_proportional_millionths": { From b479e9a9faf02ce2ea5c8ae22474d9f641b1d0a2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 8 Aug 2022 12:12:52 +0930 Subject: [PATCH 1245/1530] pytest: test that we implement option_scid_alias privacy. Spoiler: we don't! :( Signed-off-by: Rusty Russell --- tests/test_opening.py | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 6a49bd814b9b..1a078c66d31a 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1476,3 +1476,68 @@ def test_buy_liquidity_ad_no_v2(node_factory, bitcoind): l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, feerate='{}perkw'.format(feerate), compact_lease='029a002d000000004b2003e8') + + +@pytest.mark.xfail(strict=True, reason="We don't implement yet") +def test_scid_alias_private(node_factory, bitcoind): + """Test that we don't allow use of real scid for scid_alias-type channels""" + l1, l2, l3 = node_factory.line_graph(3, fundchannel=False, opts=[{}, {}, + {'log-level': 'io'}]) + + l2.fundwallet(5000000) + l2.rpc.fundchannel(l3.info['id'], 'all', announce=False) + + bitcoind.generate_block(1, wait_for_mempool=1) + wait_for(lambda: only_one(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'])['state'] == 'CHANNELD_NORMAL') + + chan = only_one(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) + assert chan['private'] is True + scid23 = chan['short_channel_id'] + alias23 = chan['alias']['local'] + + # Create l1<->l2 channel, make sure l3 sees it so it will routehint via + # l2 (otherwise it sees it as a deadend!) + l1.fundwallet(5000000) + l1.rpc.fundchannel(l2.info['id'], 'all') + bitcoind.generate_block(6, wait_for_mempool=1) + wait_for(lambda: len(l3.rpc.listchannels(source=l1.info['id'])['channels']) == 1) + + chan = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels']) + assert chan['private'] is False + scid12 = chan['short_channel_id'] + + # Make sure it sees both sides of private channel in gossmap! + wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4) + + # BOLT #2: + # - if `channel_type` has `option_scid_alias` set: + # - MUST NOT use the real `short_channel_id` in BOLT 11 `r` fields. + inv = l3.rpc.invoice(10, 'test_scid_alias_private', 'desc') + assert only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))['short_channel_id'] == alias23 + + # BOLT #2: + # - if `channel_type` has `option_scid_alias` set: + # - MUST NOT allow incoming HTLCs to this channel using the real `short_channel_id` + route = [{'amount_msat': 11, + 'id': l2.info['id'], + 'delay': 12, + 'channel': scid12}, + {'amount_msat': 10, + 'id': l3.info['id'], + 'delay': 6, + 'channel': scid23}] + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) + with pytest.raises(RpcError) as err: + l1.rpc.waitsendpay(inv['payment_hash']) + + # PERM|10 + WIRE_UNKNOWN_NEXT_PEER = 0x4000 | 10 + assert err.value.error['data']['failcode'] == WIRE_UNKNOWN_NEXT_PEER + assert err.value.error['data']['erring_node'] == l2.info['id'] + assert err.value.error['data']['erring_channel'] == scid23 + + # BOLT #2 + # - MUST always recognize the `alias` as a `short_channel_id` for incoming HTLCs to this channel. + route[1]['channel'] = alias23 + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) + l1.rpc.waitsendpay(inv['payment_hash']) From cfe6b06fb567236c7c7041ff66598053c8effe84 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Aug 2022 05:28:07 +0930 Subject: [PATCH 1246/1530] lightnind: use aliases in routehints for private channels. We *should* remember the channel type, since this is only required if they set the channel_type to include option_scid_alias. However, since we support channel upgrade, channel_type really needs a new table. I have a patch for that, from my abandoned original "fastopen" branch for aliases, but it's too big a chance for rc2 IMHO. Meanwhile, we allow exposeprivatechannels's scids to be either real or the aliases. Signed-off-by: Rusty Russell Changelog-Added: Protocol: invoice routehints will use fake short-channel-ids for private channels if channel opened with option_scid_alias-supporting peer. --- doc/lightning-invoice.7.md | 2 +- lightningd/routehint.c | 21 ++++++++++++++++++++- tests/test_invoices.py | 17 ++++++++++------- tests/test_pay.py | 3 ++- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 36fc455727fc..77d445fdcd7c 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -54,7 +54,7 @@ If specified, *exposeprivatechannels* overrides the default route hint logic, which will use unpublished channels only if there are no published channels. If *true* unpublished channels are always considered as a route hint candidate; if *false*, never. If it is a short channel id -(e.g. *1x1x3*) or array of short channel ids, only those specific channels +(e.g. *1x1x3*) or array of short channel ids (or a remote alias), only those specific channels will be considered candidates, even if they are public or dead-ends. The route hint is selected from the set of incoming channels of which: diff --git a/lightningd/routehint.c b/lightningd/routehint.c index ce15ee27cd08..f6c5fb960fc6 100644 --- a/lightningd/routehint.c +++ b/lightningd/routehint.c @@ -171,7 +171,10 @@ routehint_candidates(const tal_t *ctx, /* Consider only hints they gave */ if (hints) { log_debug(ld->log, "We have hints!"); - if (!scid_in_arr(hints, &r->short_channel_id)) { + /* Allow specification by alias, too */ + if (!scid_in_arr(hints, &r->short_channel_id) + && (!candidate.c->alias[REMOTE] + || !scid_in_arr(hints, candidate.c->alias[REMOTE]))) { log_debug(ld->log, "scid %s not in hints", type_to_string(tmpctx, struct short_channel_id, @@ -204,6 +207,22 @@ routehint_candidates(const tal_t *ctx, continue; } + /* BOLT-channel-type #2: + * - if `channel_type` has `option_scid_alias` set: + * - MUST NOT use the real `short_channel_id` in + * BOLT 11 `r` fields. + */ + /* FIXME: We don't remember the type explicitly, so + * we just assume all private channels negotiated since + * we had alias support want this. */ + + /* Note explicit flag test here: if we're told to expose all + * private channels, then "is_public" is forced true */ + if (!(candidate.c->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL) + && candidate.c->alias[REMOTE]) { + r->short_channel_id = *candidate.c->alias[REMOTE]; + } + /* OK, finish it and append to one of the arrays. */ if (is_public) { log_debug(ld->log, "%s: added to public", diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 9cfd63ade396..84315bc42628 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -226,6 +226,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Make sure channel is totally public. wait_for(lambda: [c['public'] for c in l2.rpc.listchannels(scid_dummy)['channels']] == [True, True]) + alias = only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])['alias']['local'] # Since there's only one route, it will reluctantly hint that even # though it's private inv = l2.rpc.invoice(amount_msat=123456, label="inv0", description="?") @@ -237,7 +238,9 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == l1.rpc.listchannels()['channels'][0]['short_channel_id'] + # It uses our private alias! + assert r['short_channel_id'] != l1.rpc.listchannels()['channels'][0]['short_channel_id'] + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -261,7 +264,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == l1.rpc.listchannels()['channels'][0]['short_channel_id'] + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -276,7 +279,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == l1.rpc.listchannels()['channels'][0]['short_channel_id'] + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -308,7 +311,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == l1.rpc.listchannels()['channels'][0]['short_channel_id'] + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -322,7 +325,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == scid + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -345,7 +348,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == scid + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 @@ -365,7 +368,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # Route array has single route with single element. r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])) assert r['pubkey'] == l1.info['id'] - assert r['short_channel_id'] == l1.rpc.listchannels()['channels'][0]['short_channel_id'] + assert r['short_channel_id'] == alias assert r['fee_base_msat'] == 1 assert r['fee_proportional_millionths'] == 10 assert r['cltv_expiry_delta'] == 6 diff --git a/tests/test_pay.py b/tests/test_pay.py index eb15416ee351..a35f2193ffaa 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -5062,7 +5062,8 @@ def test_routehint_tous(node_factory, bitcoind): inv = l3.rpc.invoice(10, "test", "test")['bolt11'] decoded = l3.rpc.decodepay(inv) - assert(only_one(only_one(decoded['routes']))['short_channel_id'] == scid23) + assert(only_one(only_one(decoded['routes']))['short_channel_id'] + == only_one(only_one(l3.rpc.listpeers()['peers'])['channels'])['alias']['remote']) l3.stop() with pytest.raises(RpcError, match=r'Destination .* is not reachable directly and all routehints were unusable'): From 8a9ce55345b2a0e73758dcaac574d3f9dc2acafa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Aug 2022 05:28:09 +0930 Subject: [PATCH 1247/1530] lightningd: don't route private channels via real scid. Again, we should use the real channel_type, but we approximate. Signed-off-by: Rusty Russell Changelog-Added: Protocol: private channels will only route using short-channel-ids if channel opened with option_scid_alias-supporting peer. --- lightningd/channel.c | 28 ++++++++++++++++----- lightningd/channel.h | 5 +++- lightningd/gossip_control.c | 2 +- lightningd/peer_control.c | 2 +- lightningd/peer_htlcs.c | 2 +- lightningd/test/run-invoice-select-inchan.c | 3 ++- tests/test_opening.py | 1 - tests/test_pay.py | 6 ++--- 8 files changed, 34 insertions(+), 15 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index caede53b9d60..fbf1f4ba8f95 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -17,6 +17,7 @@ #include #include #include +#include #include void channel_set_owner(struct channel *channel, struct subd *owner) @@ -601,20 +602,35 @@ struct channel_inflight *channel_inflight_find(struct channel *channel, } struct channel *any_channel_by_scid(struct lightningd *ld, - const struct short_channel_id *scid) + const struct short_channel_id *scid, + bool privacy_leak_ok) { struct peer *p; struct channel *chan; list_for_each(&ld->peers, p, list) { list_for_each(&p->channels, chan, list) { - if (chan->scid - && short_channel_id_eq(scid, chan->scid)) - return chan; - /* We also want to find the channel by its local alias - * when we forward. */ + /* BOLT-channel-type #2: + * - MUST always recognize the `alias` as a + * `short_channel_id` for incoming HTLCs to this + * channel. + */ if (chan->alias[LOCAL] && short_channel_id_eq(scid, chan->alias[LOCAL])) return chan; + /* BOLT-channel-type #2: + * - if `channel_type` has `option_scid_alias` set: + * - MUST NOT allow incoming HTLCs to this channel + * using the real `short_channel_id` + */ + /* FIXME: We don't keep type is db, so assume all + * private channels which support aliases want this! */ + if (!privacy_leak_ok + && chan->alias[REMOTE] + && !(chan->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)) + continue; + if (chan->scid + && short_channel_id_eq(scid, chan->scid)) + return chan; } } return NULL; diff --git a/lightningd/channel.h b/lightningd/channel.h index 4e7f85d46156..eba34ffa0836 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -411,8 +411,11 @@ struct channel *peer_any_unsaved_channel(struct peer *peer, bool *others); struct channel *channel_by_dbid(struct lightningd *ld, const u64 dbid); +/* Includes both real scids and aliases. If !privacy_leak_ok, then private + * channels' real scids are not included. */ struct channel *any_channel_by_scid(struct lightningd *ld, - const struct short_channel_id *scid); + const struct short_channel_id *scid, + bool privacy_leak_ok); /* Get channel by channel_id */ struct channel *channel_by_cid(struct lightningd *ld, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 0dfd9f32cbde..a240e11accdc 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -123,7 +123,7 @@ static void handle_local_channel_update(struct lightningd *ld, const u8 *msg) /* In theory this could vanish before gossipd gets around to telling * us. */ - channel = any_channel_by_scid(ld, &scid); + channel = any_channel_by_scid(ld, &scid, true); if (!channel) { log_broken(ld->log, "Local update for bad scid %s", type_to_string(tmpctx, struct short_channel_id, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 2b2365247fee..d13b53ff7204 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2027,7 +2027,7 @@ command_find_channel(struct command *cmd, tok->end - tok->start, buffer + tok->start); } else if (json_to_short_channel_id(buffer, tok, &scid)) { - *channel = any_channel_by_scid(ld, &scid); + *channel = any_channel_by_scid(ld, &scid, true); if (!*channel) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Short channel ID not found: '%.*s'", diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index db062591a43f..80d81745c236 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -663,7 +663,7 @@ static void forward_htlc(struct htlc_in *hin, /* This is a shortcut for specifying next peer; doesn't mean * the actual channel! */ - next = any_channel_by_scid(ld, scid); + next = any_channel_by_scid(ld, scid, false); if (next) { struct peer *peer = next->peer; struct channel *channel; diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 915a2c55c044..52308bedf989 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -10,7 +10,8 @@ /* AUTOGENERATED MOCKS START */ /* Generated stub for any_channel_by_scid */ struct channel *any_channel_by_scid(struct lightningd *ld UNNEEDED, - const struct short_channel_id *scid UNNEEDED) + const struct short_channel_id *scid UNNEEDED, + bool privacy_leak_ok UNNEEDED) { fprintf(stderr, "any_channel_by_scid called!\n"); abort(); } /* Generated stub for bitcoind_getutxout_ */ void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, diff --git a/tests/test_opening.py b/tests/test_opening.py index 1a078c66d31a..71da4a1f0fe4 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1478,7 +1478,6 @@ def test_buy_liquidity_ad_no_v2(node_factory, bitcoind): compact_lease='029a002d000000004b2003e8') -@pytest.mark.xfail(strict=True, reason="We don't implement yet") def test_scid_alias_private(node_factory, bitcoind): """Test that we don't allow use of real scid for scid_alias-type channels""" l1, l2, l3 = node_factory.line_graph(3, fundchannel=False, opts=[{}, {}, diff --git a/tests/test_pay.py b/tests/test_pay.py index a35f2193ffaa..7eabe1547b33 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1857,7 +1857,7 @@ def test_pay_routeboost(node_factory, bitcoind): assert 'routehint_modifications' not in only_one(status['pay']) assert 'local_exclusions' not in only_one(status['pay']) attempts = only_one(status['pay'])['attempts'] - scid34 = only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['channels'][0]['short_channel_id'] + scid34 = only_one(l3.rpc.listpeers(l4.info['id'])['peers'])['channels'][0]['alias']['local'] assert(len(attempts) == 1) a = attempts[0] assert(a['strategy'] == "Initial attempt") @@ -1866,7 +1866,7 @@ def test_pay_routeboost(node_factory, bitcoind): # With dev-route option we can test longer routehints. if DEVELOPER: - scid45 = only_one(l4.rpc.listpeers(l5.info['id'])['peers'])['channels'][0]['short_channel_id'] + scid45 = only_one(l4.rpc.listpeers(l5.info['id'])['peers'])['channels'][0]['alias']['local'] routel3l4l5 = [{'id': l3.info['id'], 'short_channel_id': scid34, 'fee_base_msat': 1000, @@ -3606,7 +3606,7 @@ def test_keysend_routehint(node_factory): routehints = [ [ { - 'scid': l3.rpc.listpeers()['peers'][0]['channels'][0]['short_channel_id'], + 'scid': l3.rpc.listpeers()['peers'][0]['channels'][0]['alias']['remote'], 'id': l2.info['id'], 'feebase': '1msat', 'feeprop': 10, From 9543204b7922d813418d52756d25c912e671e08d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Aug 2022 07:19:29 +0930 Subject: [PATCH 1248/1530] pytest: don't use bogus scids for first hop of route. This was a legacy from when it was redundant: with multiple channels, it no longer is! Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 6 +++++- tests/test_closing.py | 13 +++++++------ tests/test_connection.py | 18 +++++++++--------- tests/test_pay.py | 6 +++--- tests/utils.py | 4 ++++ 5 files changed, 28 insertions(+), 19 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 749bfcde2645..28889bc6fd74 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1102,11 +1102,15 @@ def pay(self, dst, amt, label=None): invoices = dst.rpc.listinvoices(label)['invoices'] assert len(invoices) == 1 and invoices[0]['status'] == 'unpaid' + # Pick first normal channel. + scid = [c['short_channel_id'] for c in only_one(self.rpc.listpeers(dst_id)['peers'])['channels'] + if c['state'] == 'CHANNELD_NORMAL'][0] + routestep = { 'amount_msat': amt, 'id': dst_id, 'delay': 5, - 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments + 'channel': scid } # sendpay is async now diff --git a/tests/test_closing.py b/tests/test_closing.py index 66594687d8c1..e2cf90dac9f0 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -7,7 +7,8 @@ account_balance, first_channel_id, closing_fee, TEST_NETWORK, scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES, check_utxos_channel, anchor_expected, check_coin_moves, - check_balance_snaps, mine_funding_to_announce, check_inspect_channel + check_balance_snaps, mine_funding_to_announce, check_inspect_channel, + first_scid ) import os @@ -1887,7 +1888,7 @@ def test_onchaind_replay(node_factory, bitcoind): 'amount_msat': 10**8 - 1, 'id': l2.info['id'], 'delay': 101, - 'channel': '1x1x1' + 'channel': first_scid(l1, l2) } l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) l1.daemon.wait_for_log('sendrawtx exit 0') @@ -1947,7 +1948,7 @@ def test_onchain_dust_out(node_factory, bitcoind, executor): 'amount_msat': 1, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' + 'channel': first_scid(l1, l2) } l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) @@ -2019,7 +2020,7 @@ def test_onchain_timeout(node_factory, bitcoind, executor): 'amount_msat': 10**8 - 1, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' + 'channel': first_scid(l1, l2) } l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret'], groupid=1) @@ -2505,7 +2506,7 @@ def test_onchain_feechange(node_factory, bitcoind, executor): 'amount_msat': 10**8 - 1, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' + 'channel': first_scid(l1, l2) } executor.submit(l1.rpc.sendpay, [routestep], rhash, payment_secret=inv['payment_secret']) @@ -2589,7 +2590,7 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): 'amount_msat': 10**7 - 1, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' + 'channel': first_scid(l1, l2) } executor.submit(l1.rpc.sendpay, [routestep], rhash, payment_secret=inv['payment_secret']) diff --git a/tests/test_connection.py b/tests/test_connection.py index 477a9775f6ed..906e8c80cd0f 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -9,7 +9,7 @@ expected_channel_features, check_coin_moves, first_channel_id, account_balance, basic_fee, scriptpubkey_addr, default_ln_port, - EXPERIMENTAL_FEATURES, mine_funding_to_announce + EXPERIMENTAL_FEATURES, mine_funding_to_announce, first_scid ) from pyln.testing.utils import SLOW_MACHINE, VALGRIND, EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT @@ -749,7 +749,7 @@ def test_reconnect_sender_add1(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('test_reconnect_sender_add1')['invoices'])['status'] == 'unpaid' - route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}] for i in range(0, len(disconnects)): with pytest.raises(RpcError): @@ -788,7 +788,7 @@ def test_reconnect_sender_add(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment')['invoices'])['status'] == 'unpaid' - route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}] # This will send commit, so will reconnect as required. l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) @@ -822,7 +822,7 @@ def test_reconnect_receiver_add(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' - route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}] l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') @@ -852,7 +852,7 @@ def test_reconnect_receiver_fulfill(node_factory): rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' - route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] + route = [{'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}] l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') @@ -3480,7 +3480,7 @@ def test_htlc_retransmit_order(node_factory, executor): 'amount_msat': 1000, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments + 'channel': first_scid(l1, l2) } for inv in invoices: executor.submit(l1.rpc.sendpay, [routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) @@ -3600,7 +3600,7 @@ def test_upgrade_statickey_onchaind(node_factory, executor, bitcoind): 'amount_msat': 1, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments + 'channel': first_scid(l1, l2) } l1.rpc.sendpay([routestep], '00' * 32, payment_secret='00' * 32) with pytest.raises(RpcError, match=r'WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): @@ -3725,7 +3725,7 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): 'hold-result': 'fail'}]) # This HTLC will fail - l1.rpc.sendpay([{'amount_msat': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}], '00' * 32, payment_secret='00' * 32) + l1.rpc.sendpay([{'amount_msat': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)}], '00' * 32, payment_secret='00' * 32) # Each one should cause one disconnection, no upgrade. for d in l1_disconnects + l2_disconnects: @@ -3801,7 +3801,7 @@ def test_htlc_failed_noclose(node_factory): 'amount_msat': FUNDAMOUNT * 1000, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments + 'channel': first_scid(l1, l2) } # This fails at channeld diff --git a/tests/test_pay.py b/tests/test_pay.py index 7eabe1547b33..6042ddf8a4f6 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -6,7 +6,7 @@ from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT from utils import ( DEVELOPER, wait_for, only_one, sync_blockheight, TIMEOUT, - EXPERIMENTAL_FEATURES, VALGRIND, mine_funding_to_announce + EXPERIMENTAL_FEATURES, VALGRIND, mine_funding_to_announce, first_scid ) import copy import os @@ -572,7 +572,7 @@ def invoice_unpaid(dst, label): 'amount_msat': amt, 'id': l2.info['id'], 'delay': 5, - 'channel': '1x1x1' + 'channel': first_scid(l1, l2) } # Insufficient funds. @@ -671,7 +671,7 @@ def check_balances(): inv = l2.rpc.invoice(amt, 'testpayment3', 'desc') rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'unpaid' - routestep = {'amount_msat': amt * 2, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'} + routestep = {'amount_msat': amt * 2, 'id': l2.info['id'], 'delay': 5, 'channel': first_scid(l1, l2)} l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) preimage3 = l1.rpc.waitsendpay(rhash)['payment_preimage'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'paid' diff --git a/tests/utils.py b/tests/utils.py index 799fbccf431c..5d3cfe515698 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -412,6 +412,10 @@ def first_channel_id(n1, n2): return only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['channel_id'] +def first_scid(n1, n2): + return only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['short_channel_id'] + + def basic_fee(feerate): if EXPERIMENTAL_FEATURES or EXPERIMENTAL_DUAL_FUND: # option_anchor_outputs From 054339e0cb66d0fa89ee205ab869e4f91c780d32 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Aug 2022 07:21:36 +0930 Subject: [PATCH 1249/1530] lightningd: obey first hop channel id. Reported-by: Warren Togami Changelog-Changed: `sendpay` and `sendonion` now obey the first hop "channel" short_channel_id, if specified. Changelog-Deprecated: `sendpay` and `sendonion` could take a bogus first hop "channel" short_channel_id --- lightningd/pay.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index eae50a53d128..44f0bf1c7644 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -827,19 +828,26 @@ static struct command_result *check_offer_usage(struct command *cmd, } static struct channel *find_channel_for_htlc_add(struct lightningd *ld, - const struct node_id *node) + const struct node_id *node, + const struct short_channel_id *scid) { struct channel *channel; struct peer *peer = peer_by_id(ld, node); if (!peer) return NULL; - list_for_each(&peer->channels, channel, list) { - if (channel_can_add_htlc(channel)) { - return channel; + channel = find_channel_by_scid(peer, scid); + if (channel && channel_can_add_htlc(channel)) + return channel; + + /* We used to ignore scid: now all-zero means "any" */ + if (!channel && (deprecated_apis || memeqzero(scid, sizeof(*scid)))) { + list_for_each(&peer->channels, channel, list) { + if (channel_can_add_htlc(channel)) { + return channel; + } } } - return NULL; } @@ -1032,7 +1040,8 @@ send_payment_core(struct lightningd *ld, if (offer_err) return offer_err; - channel = find_channel_for_htlc_add(ld, &first_hop->node_id); + channel = find_channel_for_htlc_add(ld, &first_hop->node_id, + &first_hop->scid); if (!channel) { struct json_stream *data = json_stream_fail(cmd, PAY_TRY_OTHER_ROUTE, From fcba09dc335a20d313d7e24c5e81f07e9745dda8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 9 Aug 2022 07:49:18 +0930 Subject: [PATCH 1250/1530] doc: document that sendonion doesn't have to specify (but can!) the first_hop channel. This was always the case, though it was ignored. Signed-off-by: Rusty Russell --- doc/lightning-sendonion.7.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index 321a4ad4a339..1e64d5335464 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -42,6 +42,10 @@ to add an HTLC for 1002 millisatoshis and a delay of 21 blocks on top of the cur } ``` +If the first element of *route* does not have "channel" set, a +suitable channel (if any) will be chosen, otherwise that specific +short-channel-id is used. + The *payment_hash* parameter specifies the 32 byte hex-encoded hash to use as a challenge to the HTLC that we are sending. It is specific to the onion and has to match the one the onion was created with. From 1ff8e1bacb1e90f14ed72e3d52919f8185458324 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Aug 2022 06:53:44 +0930 Subject: [PATCH 1251/1530] doc: note that setchannel maxhtlc/minhtlc only apply to *forwards*. Reported-by: Warren Togami Signed-off-by: Rusty Russell --- doc/lightning-setchannel.7.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index e39691f252d4..76bfc0bfbea2 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -38,17 +38,20 @@ and 1,000,000 satoshi is being routed through the channel, an proportional fee of 1,000 satoshi is added, resulting in a 0.1% fee. *htlcmin* is an optional value that limits how small an HTLC we will -send: if omitted, it is unchanged (the default is no lower limit). It +forward: if omitted, it is unchanged (the default is no lower limit). It can be a whole number, or a whole number ending in *msat* or *sat*, or a number with three decimal places ending in *sat*, or a number with 1 -to 11 decimal places ending in *btc*. The peer also enforces a -minimum for the channel: setting it below will be ignored. +to 11 decimal places ending in *btc*. Note that the peer also enforces a +minimum for the channel: setting it below that will simply set it to +that value with a warning. Also note that *htlcmin* only applies to forwarded +HTLCs: we can still send smaller payments ourselves. *htlcmax* is an optional value that limits how large an HTLC we will -send: if omitted, it is unchanged (the default is no effective +forward: if omitted, it is unchanged (the default is no effective limit). It can be a whole number, or a whole number ending in *msat* or *sat*, or a number with three decimal places ending in *sat*, or a -number with 1 to 11 decimal places ending in *btc*. +number with 1 to 11 decimal places ending in *btc*. Note that *htlcmax* +only applies to forwarded HTLCs: we can still send larger payments ourselves. *enforcedelay* is the number of seconds to delay before enforcing the new fees/htlc max (default 600, which is ten minutes). This gives the From 23cd58402a9185fc7d4e03e9e973e816187109c6 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 8 Aug 2022 13:40:08 -0500 Subject: [PATCH 1252/1530] bkpr: create accounts for zero sat channels we weren't making records for 'missed' accounts that had a zero balance at snapshot time (if peer opens channel and is unused) Fixes: #5502 Reported-By: https://github.com/niftynei/cln-logmaid --- plugins/bkpr/bookkeeper.c | 14 +++-- plugins/bkpr/incomestmt.c | 4 ++ plugins/bkpr/recorder.c | 8 ++- plugins/bkpr/recorder.h | 3 +- plugins/bkpr/test/run-recorder.c | 17 ++++-- tests/test_bookkeeper.py | 92 ++++++++++++++++++++++++++++---- tests/test_pay.py | 9 +++- 7 files changed, 123 insertions(+), 24 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index b823a39594f0..e1ebdbd13954 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -474,7 +474,8 @@ static struct command_result *json_list_balances(struct command *cmd, accts[i]->name, true, false, /* don't skip ignored */ - &balances); + &balances, + NULL); if (err) plugin_err(cmd->plugin, @@ -888,7 +889,7 @@ listpeers_multi_done(struct command *cmd, db_begin_transaction(db); err = account_get_balance(tmpctx, db, info->acct->name, - false, false, &balances); + false, false, &balances, NULL); db_commit_transaction(db); if (err) @@ -1001,6 +1002,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, struct acct_balance **balances, *bal; struct amount_msat snap_balance, credit_diff, debit_diff; char *acct_name, *currency; + bool exists; err = json_scan(cmd, buf, acct_tok, "{account_id:%" @@ -1029,7 +1031,8 @@ static struct command_result *json_balance_snapshot(struct command *cmd, /* Ignore non-clightning * balances items */ true, - &balances); + &balances, + &exists); if (err) plugin_err(cmd->plugin, @@ -1056,7 +1059,8 @@ static struct command_result *json_balance_snapshot(struct command *cmd, "Unable to find_diff for amounts: %s", err); - if (!amount_msat_zero(credit_diff) + if (!exists + || !amount_msat_zero(credit_diff) || !amount_msat_zero(debit_diff)) { struct account *acct; struct channel_event *ev; @@ -1328,7 +1332,7 @@ listpeers_done(struct command *cmd, const char *buf, info->ev->timestamp)) { db_begin_transaction(db); err = account_get_balance(tmpctx, db, info->acct->name, - false, false, &balances); + false, false, &balances, NULL); db_commit_transaction(db); if (err) diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index c017f34d01d7..1a9502a48caf 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -235,6 +235,10 @@ static struct income_event *rebalance_fee(const tal_t *ctx, static struct income_event *maybe_channel_income(const tal_t *ctx, struct channel_event *ev) { + if (amount_msat_zero(ev->credit) + && amount_msat_zero(ev->debit)) + return NULL; + /* We record a +/- penalty adj, but we only count the credit */ if (streq(ev->tag, "penalty_adj")) { if (!amount_msat_zero(ev->credit)) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 301cac060f45..79a70f8b6211 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -786,7 +786,8 @@ char *account_get_balance(const tal_t *ctx, const char *acct_name, bool calc_sum, bool skip_ignored, - struct acct_balance ***balances) + struct acct_balance ***balances, + bool *account_exists) { struct db_stmt *stmt; @@ -807,6 +808,8 @@ char *account_get_balance(const tal_t *ctx, db_bind_int(stmt, 1, skip_ignored ? 1 : 2); db_query_prepared(stmt); *balances = tal_arr(ctx, struct acct_balance *, 0); + if (account_exists) + *account_exists = false; while (db_step(stmt)) { struct acct_balance *bal; @@ -817,6 +820,9 @@ char *account_get_balance(const tal_t *ctx, db_col_amount_msat(stmt, "credit", &bal->credit); db_col_amount_msat(stmt, "debit", &bal->debit); tal_arr_expand(balances, bal); + + if (account_exists) + *account_exists = true; } tal_free(stmt); diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index ece697fde954..1e3278281257 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -108,7 +108,8 @@ char *account_get_balance(const tal_t *ctx, const char *acct_name, bool calc_sum, bool skip_ignored, - struct acct_balance ***balances); + struct acct_balance ***balances, + bool *account_exists); /* Get chain fees for account */ struct onchain_fee **account_get_chain_fees(const tal_t *ctx, struct db *db, diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 3382f6a9a81c..393c052a1488 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -1195,6 +1195,7 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) struct account *acct, *acct2; struct chain_event *ev1; struct acct_balance **balances; + bool exists; char *err; memset(&peer_id, 3, sizeof(struct node_id)); @@ -1203,6 +1204,13 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); db_begin_transaction(db); + /* Check that account does not exist yet */ + err = account_get_balance(ctx, db, acct->name, true, false, + &balances, &exists); + + CHECK(!err); + CHECK_MSG(!exists, "expected account not to exist"); + account_add(db, acct); account_add(db, acct2); @@ -1251,7 +1259,7 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) log_chain_event(db, acct2, ev1); err = account_get_balance(ctx, db, acct->name, true, false, - &balances); + &balances, NULL); CHECK_MSG(!err, err); db_commit_transaction(db); CHECK_MSG(!db_err, db_err); @@ -1274,17 +1282,18 @@ static bool test_account_balances(const tal_t *ctx, struct plugin *p) log_chain_event(db, acct, ev1); err = account_get_balance(ctx, db, acct->name, true, false, - &balances); + &balances, &exists); CHECK_MSG(err != NULL, "Expected err message"); CHECK(streq(err, "chf channel balance is negative? 5000msat - 5001msat")); + CHECK_MSG(exists, "expected account to exist"); err = account_get_balance(ctx, db, acct->name, false, false, - &balances); + &balances, NULL); CHECK_MSG(!err, err); /* Now with ignored events */ err = account_get_balance(ctx, db, acct->name, true, true, - &balances); + &balances, NULL); CHECK(streq(balances[0]->currency, "btc")); CHECK(amount_msat_eq(balances[0]->balance, AMOUNT_MSAT(500 - 440 + 1000))); diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 9453b362de64..ec53035ed512 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -136,7 +136,7 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): assert withdrawal[0]['amount'] == Decimal('0.00555555') incomes = l1.rpc.bkpr_listincome()['income_events'] - # There should only be two income events: deposits to wallet + # There are two income events: deposits to wallet # for {amount} assert len(incomes) == 2 for inc in incomes: @@ -145,7 +145,7 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): assert inc['credit_msat'] == amount_msat # The event should show up in the 'bkpr_listaccountevents' however events = l1.rpc.bkpr_listaccountevents()['events'] - assert len(events) == 3 + assert len(events) == 4 external = [e for e in events if e['account'] == 'external'][0] assert external['credit_msat'] == Millisatoshi(amount // 2 * 1000) @@ -162,8 +162,8 @@ def test_bookkeeping_external_withdraws(node_factory, bitcoind): assert len(find_tags(incomes, 'journal_entry')) == 0 assert len(incomes) == 2 events = l1.rpc.bkpr_listaccountevents()['events'] - assert len(events) == 3 - assert len(find_tags(events, 'journal_entry')) == 0 + assert len(events) == 4 + assert len(find_tags(events, 'journal_entry')) == 1 # the wallet balance should be unchanged btc_balance = only_one(only_one(l1.rpc.bkpr_listbalances()['accounts'])['balances']) @@ -214,17 +214,17 @@ def test_bookkeeping_external_withdraw_missing(node_factory, bitcoind): # Ok, now we send some funds to an external address l1.rpc.withdraw(waddr, amount // 2) - # There should only be two income events: deposits to wallet + # Only two income events: deposits assert len(l1.rpc.bkpr_listincome()['income_events']) == 2 - # There are three account events: 2 wallet deposits, 1 external deposit - assert len(l1.rpc.bkpr_listaccountevents()['events']) == 3 + # 4 account events: empty wallet start, 2 wallet deposits, 1 external deposit + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 4 # Stop node and remove the accounts data l1.stop() os.remove(os.path.join(basedir, TEST_NETWORK, 'accounts.sqlite3')) l1.start() - # the number of income events should be unchanged + # Number of income events should be unchanged assert len(l1.rpc.bkpr_listincome()['income_events']) == 2 # we're now missing the external deposit events = l1.rpc.bkpr_listaccountevents()['events'] @@ -270,7 +270,7 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): bitcoind.rpc.sendtoaddress(addr, amount / 10**8) bitcoind.generate_block(1) wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 1) - assert len(l1.rpc.bkpr_listaccountevents()['events']) == 1 + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 2 assert len(l1.rpc.bkpr_listincome()['income_events']) == 1 # Ok, now we send some funds to an external address @@ -281,7 +281,7 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert out1['txid'] in list(mempool.keys()) # another account event, still one income event - assert len(l1.rpc.bkpr_listaccountevents()['events']) == 2 + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 3 assert len(l1.rpc.bkpr_listincome()['income_events']) == 1 # unreserve the existing output @@ -294,7 +294,7 @@ def test_bookkeeping_rbf_withdraw(node_factory, bitcoind): assert out2['txid'] in list(mempool.keys()) # another account event, still one income event - assert len(l1.rpc.bkpr_listaccountevents()['events']) == 3 + assert len(l1.rpc.bkpr_listaccountevents()['events']) == 4 assert len(l1.rpc.bkpr_listincome()['income_events']) == 1 # ok now we mine a block @@ -463,6 +463,76 @@ def _check_events(node, channel_id, exp_events): _check_events(l2, channel_id, exp_events) +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") +@unittest.skipIf(TEST_NETWORK != 'regtest', "network fees hardcoded") +@pytest.mark.openchannel('v1', 'Uses push-msat') +def test_bookkeeping_missed_chans_pay_after(node_factory, bitcoind): + """ + Route a payment through a channel that we didn't have open when the bookkeeper + was around + """ + coin_mvt_plugin = Path(__file__).parent / "plugins" / "coin_movements.py" + l1, l2 = node_factory.get_nodes(2, opts={'disable-plugin': 'bookkeeper', + 'plugin': str(coin_mvt_plugin)}) + + # Double check there's no bookkeeper plugin on + assert l1.daemon.opts['disable-plugin'] == 'bookkeeper' + assert l2.daemon.opts['disable-plugin'] == 'bookkeeper' + + open_amt = 10**7 + invoice_msat = 11000000 + + l1.fundwallet(200000000) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + txid = l1.rpc.fundchannel(l2.info['id'], open_amt)['txid'] + bitcoind.generate_block(1, wait_for_mempool=[txid]) + wait_for(lambda: l1.channel_state(l2) == 'CHANNELD_NORMAL') + scid = l1.get_channel_scid(l2) + l1.wait_channel_active(scid) + channel_id = first_channel_id(l1, l2) + + # Now turn the bookkeeper on and restart + l1.stop() + l2.stop() + del l1.daemon.opts['disable-plugin'] + del l2.daemon.opts['disable-plugin'] + l1.start() + l2.start() + + # Wait for the balance snapshot to fire/finish + l1.daemon.wait_for_log('Snapshot balances updated') + l2.daemon.wait_for_log('Snapshot balances updated') + + # Should have channel in both, with balances + for n in [l1, l2]: + accts = [ba['account'] for ba in n.rpc.bkpr_listbalances()['accounts']] + assert channel_id in accts + + # Send a payment, should be ok. + l1.wait_channel_active(scid) + l1.pay(l2, invoice_msat) + l1.daemon.wait_for_log(r'coin movement:.*\'invoice\'') + + def _check_events(node, channel_id, exp_events): + chan_events = [ev for ev in node.rpc.bkpr_listaccountevents()['events'] if ev['account'] == channel_id] + assert len(chan_events) == len(exp_events) + for ev, exp in zip(chan_events, exp_events): + assert ev['tag'] == exp[0] + assert ev['credit_msat'] == Millisatoshi(exp[1]) + assert ev['debit_msat'] == Millisatoshi(exp[2]) + + # l1 events + exp_events = [('channel_open', open_amt * 1000, 0), + ('onchain_fee', 5257000, 0), + ('invoice', 0, invoice_msat)] + _check_events(l1, channel_id, exp_events) + + # l2 events + exp_events = [('channel_open', 0, 0), + ('invoice', invoice_msat, 0)] + _check_events(l2, channel_id, exp_events) + + @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "turns off bookkeeper at start") def test_bookkeeping_onchaind_txs(node_factory, bitcoind): """ diff --git a/tests/test_pay.py b/tests/test_pay.py index 6042ddf8a4f6..8360b81f59f0 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1331,9 +1331,14 @@ def _income_tagset(node, tagset): wait_for(lambda: len(_income_tagset(l1, tags)) == 2) incomes = _income_tagset(l1, tags) # the balance on l3 should equal the invoice - bal = only_one(only_one(l3.rpc.bkpr_listbalances()['accounts'])['balances'])['balance_msat'] + accts = l3.rpc.bkpr_listbalances()['accounts'] + assert len(accts) == 2 + wallet = accts[0] + chan_acct = accts[1] + assert wallet['account'] == 'wallet' + assert only_one(wallet['balances'])['balance_msat'] == Millisatoshi(0) assert incomes[0]['tag'] == 'invoice' - assert Millisatoshi(bal) == incomes[0]['debit_msat'] + assert only_one(chan_acct['balances'])['balance_msat'] == incomes[0]['debit_msat'] inve = only_one([e for e in l1.rpc.bkpr_listaccountevents()['events'] if e['tag'] == 'invoice']) assert inve['debit_msat'] == incomes[0]['debit_msat'] + incomes[1]['debit_msat'] From 256044081f92916c098978ccbb63fb4211fcf520 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 8 Aug 2022 13:41:56 -0500 Subject: [PATCH 1253/1530] bkpr: remove duplicate log stmt --- plugins/bkpr/bookkeeper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index e1ebdbd13954..eddff8b18586 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -911,7 +911,6 @@ listpeers_multi_done(struct command *cmd, if (err) plugin_err(cmd->plugin, err); - plugin_log(cmd->plugin, LOG_DBG, "Snapshot balances updated"); log_journal_entry(info->acct, info->currency, info->timestamp - 1, From 72a30fc7505e7fd927b19cea0fbfcbf5550ac07c Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 8 Aug 2022 13:42:23 -0500 Subject: [PATCH 1254/1530] bkpr: dont flake, wait til pay done before mining blocks --- tests/test_bookkeeper.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index ec53035ed512..5d4766e21cd0 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -539,15 +539,18 @@ def test_bookkeeping_onchaind_txs(node_factory, bitcoind): Test for a channel that's closed, but whose close tx re-appears in the rescan """ + coin_mvt_plugin = Path(__file__).parent / "plugins" / "coin_movements.py" l1, l2 = node_factory.line_graph(2, wait_for_announce=True, - opts={'disable-plugin': 'bookkeeper'}) + opts={'disable-plugin': 'bookkeeper', + 'plugin': str(coin_mvt_plugin)}) # Double check there's no bookkeeper plugin on assert l1.daemon.opts['disable-plugin'] == 'bookkeeper' # Send l2 funds via the channel l1.pay(l2, 11000000) + l1.daemon.wait_for_log(r'coin movement:.*\'invoice\'') bitcoind.generate_block(10) # Amicably close the channel, mine 101 blocks (channel forgotten) From 9d3bf4a1b5565441730804850a899b6a3ea4cfdf Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 9 Aug 2022 23:08:25 -0500 Subject: [PATCH 1255/1530] bkpr: let channel reconnect, flake? --- tests/test_bookkeeper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index 5d4766e21cd0..b9b9f00387cf 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -473,6 +473,7 @@ def test_bookkeeping_missed_chans_pay_after(node_factory, bitcoind): """ coin_mvt_plugin = Path(__file__).parent / "plugins" / "coin_movements.py" l1, l2 = node_factory.get_nodes(2, opts={'disable-plugin': 'bookkeeper', + 'may_reconnect': True, 'plugin': str(coin_mvt_plugin)}) # Double check there's no bookkeeper plugin on From b173b2934687d076b37c888924501e3d86382bd5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Aug 2022 21:58:29 +0930 Subject: [PATCH 1256/1530] pytest: test loading lease_chan_max_msat from channel_funding_inflights It crashes with: ``` s32 field doesn't match size: expected 4, actual 8 ``` Reported-by: @zerofeerouting Signed-off-by: Rusty Russell --- tests/test_opening.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 71da4a1f0fe4..d4b48ed12506 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1205,6 +1205,41 @@ def test_funder_contribution_limits(node_factory, bitcoind): assert l3.daemon.is_in_log(r'calling `signpsbt` .* 7 inputs') +@pytest.mark.openchannel('v2') +@pytest.mark.developer("requres 'dev-disconnect'") +def test_inflight_dbload(node_factory, bitcoind): + """Bad db field access breaks Postgresql on startup with opening leases""" + disconnects = ["@WIRE_COMMITMENT_SIGNED"] + l1, l2 = node_factory.get_nodes(2, opts=[{'experimental-dual-fund': None, + 'dev-no-reconnect': None, + 'may_reconnect': True, + 'disconnect': disconnects}, + {'experimental-dual-fund': None, + 'dev-no-reconnect': None, + 'may_reconnect': True, + 'funder-policy': 'match', + 'funder-policy-mod': 100, + 'lease-fee-base-sat': '100sat', + 'lease-fee-basis': 100}]) + + feerate = 2000 + amount = 500000 + l1.fundwallet(20000000) + l2.fundwallet(20000000) + + # l1 leases a channel from l2 + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + wait_for(lambda: len(l1.rpc.listpeers(l2.info['id'])['peers']) == 0) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + l1.daemon.wait_for_log(r'dev_disconnect: @WIRE_COMMITMENT_SIGNED') + + l1.restart() + + def test_zeroconf_mindepth(bitcoind, node_factory): """Check that funder/fundee can customize mindepth. From eb006dcaddd2539cfa5413798131833df1072ae8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Aug 2022 21:58:30 +0930 Subject: [PATCH 1257/1530] wallet: fix incorrect column-width access. Postgresql actually checks, and fails. It's unclear why this field is an INTEGER (and u32) when it's a BIGINT in db here (it's an INTEGER in the channels table, just a BIGINT in the channel_funding_inflights table). Changelog-Fixed: db: postgresql crash on startup when dual-funding lease open is pending with "s32 field doesn't match size: expected 4, actual 8" Signed-off-by: Rusty Russell --- wallet/wallet.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index 1c276c2e8d01..c287a3934017 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1118,7 +1118,8 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, struct channel_inflight *inflight; secp256k1_ecdsa_signature *lease_commit_sig; - u32 lease_chan_max_msat, lease_blockheight_start; + u32 lease_blockheight_start; + u64 lease_chan_max_msat; u16 lease_chan_max_ppt; db_col_txid(stmt, "funding_tx_id", &funding.txid); @@ -1133,7 +1134,7 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, if (!db_col_is_null(stmt, "lease_commit_sig")) { lease_commit_sig = tal(tmpctx, secp256k1_ecdsa_signature); db_col_signature(stmt, "lease_commit_sig", lease_commit_sig); - lease_chan_max_msat = db_col_int(stmt, "lease_chan_max_msat"); + lease_chan_max_msat = db_col_u64(stmt, "lease_chan_max_msat"); lease_chan_max_ppt = db_col_int(stmt, "lease_chan_max_ppt"); lease_blockheight_start = db_col_int(stmt, "lease_blockheight_start"); db_col_amount_msat(stmt, "lease_fee", &lease_fee); From 0878002fe6669f3ebf23e907c6cdd402f2c96e44 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Tue, 9 Aug 2022 21:09:57 -0700 Subject: [PATCH 1258/1530] Fix derived_secret, use correct size of secretstuff.derived secret [ Updated tests to match -- RR] --- hsmd/libhsmd.c | 2 +- tests/test_misc.py | 2 +- tests/test_plugin.py | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index a641e3f87d2e..4dc90f65539e 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -271,7 +271,7 @@ static u8 *handle_derive_secret(struct hsmd_client *c, const u8 *msg_in) return hsmd_status_malformed_request(c, msg_in); hkdf_sha256(&secret, sizeof(struct secret), NULL, 0, - &secretstuff.derived_secret, sizeof(&secretstuff.derived_secret), + &secretstuff.derived_secret, sizeof(secretstuff.derived_secret), info, tal_bytelen(info)); return towire_hsmd_derive_secret_reply(NULL, &secret); diff --git a/tests/test_misc.py b/tests/test_misc.py index 9a7cf873811a..9deeba63c5ed 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2268,7 +2268,7 @@ def test_makesecret(node_factory): l1 = node_factory.get_node(options={"dev-force-privkey": "1212121212121212121212121212121212121212121212121212121212121212"}) secret = l1.rpc.makesecret("73636220736563726574")["secret"] - assert (secret == "04fe01631fcedc8d91f39ab43244e63afebaed68ee21d2f1c325fd1242726a18") + assert (secret == "a9a2e742405c28f059349132923a99337ae7f71168b7485496e3365f5bc664ed") # Same if we do it by parameter name assert l1.rpc.makesecret(hex="73636220736563726574")["secret"] == secret diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 67b242bc1266..8ff19370d291 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2640,9 +2640,13 @@ def test_commando(node_factory, executor): def test_commando_rune(node_factory): - l1, l2 = node_factory.line_graph(2, fundchannel=False) + l1, l2 = node_factory.get_nodes(2) + + # Force l1's commando secret + l1.rpc.datastore(key=['commando', 'secret'], hex='1241faef85297127c2ac9bde95421b2c51e5218498ae4901dc670c974af4284b') + l1.restart() + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) - # l1's commando secret is 1241faef85297127c2ac9bde95421b2c51e5218498ae4901dc670c974af4284b. # I put that into a test node's commando.py to generate these runes (modified readonly to match ours): # $ l1-cli commando-rune # "rune": "zKc2W88jopslgUBl0UE77aEe5PNCLn5WwqSusU_Ov3A9MA==" From 04cb6316d5dfa57af63f23a3126cc1717928108d Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 10 Aug 2022 12:49:39 -0500 Subject: [PATCH 1259/1530] CHANGELOG: v0.12.0rc2 --- CHANGELOG.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d57c856e925..49975fb2bd20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [0.12.0rc1] - 2022-08-01: "TBD" +## [0.12.0rc2] - 2022-08-10: "TBD" This release named by @adi2011. @@ -30,6 +30,9 @@ Developers please note the Great Msat Migration has begun: - JSON-RPC: `listpeers` add optional `remote_addr` ([#5244]) - JSON-RPC: `listforwards` now shows `out_channel` in more cases: even if it couldn't actually send to it. ([#5330]) - JSON-RPC: `pay` `attempts` `amount_msat` field. ([#5306]) + - Protocol: private channels will only route using short-channel-ids if channel opened with option_scid_alias-supporting peer. ([#5501]) + - Protocol: invoice routehints will use fake short-channel-ids for private channels if channel opened with option_scid_alias-supporting peer. ([#5501]) + - Protocol: we now advertize the `option_channel_type` feature (which we actually supported since v0.10.2) ([#5455]) - Plugins: `channel_state_changed` now triggers for a v1 channel's initial "CHANNELD_AWAITING_LOCKIN" state transition (from prior state "unknown") ([#5381]) - Plugins: `htlc_accepted_hook` `amount_msat` field. ([#5306]) - Plugins: `htlc_accepted` now exposes the `short_channel_id` for the channel from which that HTLC is coming from and the low-level per-channel HTLC `id`, which are necessary for bridging two different Lightning Networks when MPP is involved. ([#5303]) @@ -52,6 +55,7 @@ Developers please note the Great Msat Migration has begun: - `gossipd`: now accepts spam gossip, but squelches it for ([#5239]) - gossip: gossip\_store updated to version 10. ([#5239]) - Options: `log-file` option specified multiple times opens multiple log files. ([#5281]) + - JSON-RPC: `sendpay` and `sendonion` now obey the first hop "channel" short_channel_id, if specified. ([#5505]) - JSON-RPC: `plugin start` now assumes relative path to default plugins dir if the path is not found in absolute context. i.e. lightning-cli plugin start my_plugin.py ([#5211]) - JSON-RPC: `fundchannel`: now errors if you try to buy a liquidity ad but dont' have `experimental-dual-fund` enabled ([#5389]) - JSON-RPC: "\_msat" fields can be raw numbers, not "123msat" strings (please handle both!) ([#5306]) @@ -102,6 +106,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. ### Fixed + - db: postgresql crash on startup when dual-funding lease open is pending with "s32 field doesn't match size: expected 4, actual 8" ([#5513]) - `connectd`: various crashes and issues fixed by simplification and rewrite. ([#5261]) - `connectd`: Port of a DNS announcement can be 0 if unspecified ([#5434]) - `dualopend`: Issue if the number of outputs decreases in a dualopen RBF or splice. ([#5378]) @@ -115,6 +120,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. - pyln-spec: update the bolts implementation ([#5168]) - Plugins: setting the default value of a parameter to `null` is the same as not setting it (pyln plugins did this!). ([#5460]) - Plugins: plugins would hang indefinitely despite `lightningd` closing the connection ([#5362]) + - Plugins: `channel_opened` notification `funding_locked` field is now accurate: was always `true`. ([#5489]) - Upgrade docker base image from Debian buster to bullseye to work with glibc 2.29+ #5276 ([#5278]) - docker: The docker images are now built with the rust plugins `cln-grpc` ([#5270]) @@ -156,9 +162,15 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#5430]: https://github.com/ElementsProject/lightning/pull/5430 [#5434]: https://github.com/ElementsProject/lightning/pull/5434 [#5441]: https://github.com/ElementsProject/lightning/pull/5441 +[#5455]: https://github.com/ElementsProject/lightning/pull/5455 [#5460]: https://github.com/ElementsProject/lightning/pull/5460 [#5475]: https://github.com/ElementsProject/lightning/pull/5475 [#5477]: https://github.com/ElementsProject/lightning/pull/5477 +[#5489]: https://github.com/ElementsProject/lightning/pull/5489 +[#5501]: https://github.com/ElementsProject/lightning/pull/5501 +[#5505]: https://github.com/ElementsProject/lightning/pull/5505 +[#5513]: https://github.com/ElementsProject/lightning/pull/5513 + ## [0.11.2] - 2022-06-24: Simon's Carefully Chosen Release Name III @@ -1848,7 +1860,7 @@ There predate the BOLT specifications, and are only of vague historic interest: 6. [0.5.1] - 2016-10-21 7. [0.5.2] - 2016-11-21: "Bitcoin Savings & Trust Daily Interest II" -[0.12.0rc1]: https://github.com/ElementsProject/lightning/releases/tag/v0.12.0rc1 +[0.12.0rc2]: https://github.com/ElementsProject/lightning/releases/tag/v0.12.0rc2 [0.11.2]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.2 [0.11.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.1 [0.11.0.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0.1 From 63f8c74da90430066999481322a78ec46a3106a8 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 8 Aug 2022 14:44:42 -0500 Subject: [PATCH 1260/1530] psbt: dont crash when printing psbt to log We call `tal_wally_start` and then `tal_wally_start` again in `type_to_string` for psbt. end the last tal before calling type to string. Fixes: #5499 Reported-By: @fiatjaf lightning_hsmd: FATAL SIGNAL 6 (version v0.11.0.1-231-gddf8fbd-modded) 0x5574ca0d87ef send_backtrace common/daemon.c:33 0x5574ca0d8877 crashdump common/daemon.c:46 0x7f76ef63e8df ??? ???:0 0x7f76ef68e36c ??? ???:0 0x7f76ef63e837 ??? ???:0 0x7f76ef628534 ??? ???:0 0x5574ca0df55e tal_wally_start common/utils.c:27 0x5574ca0e4024 psbt_to_b64 bitcoin/psbt.c:687 0x5574ca0e4093 fmt_wally_psbt_ bitcoin/psbt.c:694 0x5574ca0df4b9 type_to_string_ common/type_to_string.c:32 0x5574ca0d5139 sign_our_inputs hsmd/libhsmd.c:486 0x5574ca0d5206 handle_sign_withdrawal_tx hsmd/libhsmd.c:1029 0x5574ca0d63c4 hsmd_handle_client_message hsmd/libhsmd.c:1575 0x5574ca0ce763 handle_client hsmd/hsmd.c:671 0x5574ca100032 next_plan ccan/ccan/io/io.c:59 0x5574ca1004b9 do_plan ccan/ccan/io/io.c:407 0x5574ca100552 io_ready ccan/ccan/io/io.c:417 0x5574ca101daf io_loop ccan/ccan/io/poll.c:453 0x5574ca0ceb7b main hsmd/hsmd.c:739 0x7f76ef62928f ??? ???:0 0x7f76ef629349 ??? ???:0 0x5574ca0cda04 ??? ../sysdeps/x86_64/start.S:115 --- hsmd/libhsmd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index 4dc90f65539e..ad2cb627f3e1 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -502,7 +502,8 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt) tal_wally_start(); if (wally_psbt_sign(psbt, privkey.secret.data, sizeof(privkey.secret.data), - EC_FLAG_GRIND_R) != WALLY_OK) + EC_FLAG_GRIND_R) != WALLY_OK) { + tal_wally_end(psbt); hsmd_status_broken( "Received wally_err attempting to " "sign utxo with key %s. PSBT: %s", @@ -510,6 +511,7 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt) &pubkey), type_to_string(tmpctx, struct wally_psbt, psbt)); + } tal_wally_end(psbt); } } From 498401457843aceeea7b952209bafba1a66d8337 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 11 Aug 2022 16:29:58 -0500 Subject: [PATCH 1261/1530] signpsbt: add utxo info to inputs If you build a PSBT externally from CLN and attempt to sign for the output, we would crash. Now we don't crash. Changelog-Changed: JSON-RPC: `signpsbt` will now add redeemscript + witness-utxo to the PSBT for an input that we can sign for, before signing it. Fixes #5499 ? --- tests/test_wallet.py | 29 +++++++++++++++++++++++++++++ wallet/walletrpc.c | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index ac5fd0e7da76..a093c390f01b 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -695,6 +695,35 @@ def test_utxopsbt(node_factory, bitcoind, chainparams): reservedok=True) +def test_sign_external_psbt(node_factory, bitcoind, chainparams): + """ + A PSBT w/ one of our inputs should be signable (we can fill + in the required UTXO data). + """ + l1 = node_factory.get_node(feerates=(7500, 7500, 7500, 7500)) + amount = 1000000 + total_outs = 4 + + # Add a medley of funds to withdraw later, bech32 + p2sh-p2wpkh + for i in range(total_outs // 2): + bitcoind.rpc.sendtoaddress(l1.rpc.newaddr()['bech32'], + amount / 10**8) + bitcoind.rpc.sendtoaddress(l1.rpc.newaddr('p2sh-segwit')['p2sh-segwit'], amount / 10**8) + + bitcoind.generate_block(1) + wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs) + + # Build a PSBT using all our inputs, externally + inputs = [] + for inp in l1.rpc.listfunds()['outputs']: + inputs.append({'txid': inp['txid'], 'vout': inp['output']}) + addr = l1.rpc.newaddr()['bech32'] + psbt = bitcoind.rpc.createpsbt(inputs, [{addr: (amount * 3) / 10**8}]) + + l1.rpc.reserveinputs(psbt) + l1.rpc.signpsbt(psbt) + + def test_sign_and_send_psbt(node_factory, bitcoind, chainparams): """ Tests for the sign + send psbt RPCs diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 809bd8ef4f7c..47f38e13db9c 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -648,6 +648,33 @@ static struct command_result *match_psbt_inputs_to_utxos(struct command *cmd, "Aborting PSBT signing. UTXO %s is not reserved", type_to_string(tmpctx, struct bitcoin_outpoint, &utxo->outpoint)); + + /* If the psbt doesn't have the UTXO info yet, add it. + * We only add the witness_utxo for this */ + if (!psbt->inputs[i].utxo && !psbt->inputs[i].witness_utxo) { + u8 *scriptPubKey; + + if (utxo->is_p2sh) { + struct pubkey key; + u8 *redeemscript; + int wally_err; + + bip32_pubkey(cmd->ld->wallet->bip32_base, &key, + utxo->keyindex); + redeemscript = bitcoin_redeem_p2sh_p2wpkh(tmpctx, &key); + scriptPubKey = scriptpubkey_p2sh(tmpctx, redeemscript); + + tal_wally_start(); + wally_err = wally_psbt_input_set_redeem_script(&psbt->inputs[i], + redeemscript, + tal_bytelen(redeemscript)); + assert(wally_err == WALLY_OK); + tal_wally_end(psbt); + } else + scriptPubKey = utxo->scriptPubkey; + + psbt_input_set_wit_utxo(psbt, i, scriptPubKey, utxo->amount); + } tal_arr_expand(utxos, utxo); } From 4e7f89f211523e2fac8ce0435137b17a255133c0 Mon Sep 17 00:00:00 2001 From: niftynei Date: Thu, 11 Aug 2022 16:37:11 -0500 Subject: [PATCH 1262/1530] signpsbt: don't crash if HSM doesn't like your psbt, just return err Changelog-Changed: JSON-RPC: `signpsbt` no longer crashes if it doesn't like what your PSBT is --- wallet/walletrpc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 47f38e13db9c..4d2ab3c1c03a 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -789,8 +789,9 @@ static struct command_result *json_signpsbt(struct command *cmd, msg = wire_sync_read(cmd, cmd->ld->hsm_fd); if (!fromwire_hsmd_sign_withdrawal_reply(cmd, msg, &signed_psbt)) - fatal("HSM gave bad sign_withdrawal_reply %s", - tal_hex(tmpctx, msg)); + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "HSM gave bad sign_withdrawal_reply %s", + tal_hex(tmpctx, msg)); response = json_stream_success(cmd); json_add_psbt(response, "signed_psbt", signed_psbt); From fdfca9e7212eb7a4521ab4517a85964c9c65be29 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 15 Aug 2022 14:38:46 -0500 Subject: [PATCH 1263/1530] sqlite3: no NULLS FIRST sqlite3 added NULLS FIRST to their schema as of v3.30.1 but some older boxes aren't updated (notably the author's own node box). The default behavior for sqlite3 is to sort nulls first however, this is *really* only needed for postgres. As a simple fix (which will allow older boxes to not upgrade their sqlite yet), simply strip out the NULLS FIRST keywords from sqlite3 queries. Reported-By: Simon Vrouwe @SimonVrouwe Fixes: #5517 --- devtools/sql-rewrite.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devtools/sql-rewrite.py b/devtools/sql-rewrite.py index 4306d0de1432..7ae209403c3b 100755 --- a/devtools/sql-rewrite.py +++ b/devtools/sql-rewrite.py @@ -51,6 +51,8 @@ def rewrite_single(self, query): r'decode\((.*),\s*[\'\"]hex[\'\"]\)': 'x\\1', # GREATEST() of multiple columns is simple MAX in sqlite3. r'GREATEST\(([^)]*)\)': "MAX(\\1)", + # NULLS FIRST is default behavior on sqlite, make it disappear + r' NULLS FIRST': '', } return self.rewrite_types(query, typemapping) From 1a2565118240c25389645ef8b0108869241bcfb5 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 15 Aug 2022 14:41:21 -0500 Subject: [PATCH 1264/1530] bkpr-recorder: fatal if there's an error with a database statement We were silently failing when NULLS FIRST wasn't found on older boxes (running sqlite3 < v3.30.1) --- plugins/bkpr/recorder.c | 2 ++ plugins/bkpr/test/run-recorder.c | 1 + 2 files changed, 3 insertions(+) diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 79a70f8b6211..0d8ab5b4742f 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -72,6 +72,8 @@ static struct chain_event **find_chain_events(const tal_t *ctx, struct chain_event **results; db_query_prepared(stmt); + if (stmt->error) + db_fatal("find_chain_events err: %s", stmt->error); results = tal_arr(ctx, struct chain_event *, 0); while (db_step(stmt)) { struct chain_event *e = stmt2chain_event(results, stmt); diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 393c052a1488..01b33af1cd89 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -697,6 +697,7 @@ static bool test_onchain_fee_chan_close(const tal_t *ctx, struct plugin *p) CHECK(acct->onchain_resolved_block == 0); db_begin_transaction(db); maybe_mark_account_onchain(db, acct); + CHECK_MSG(!db_err, db_err); CHECK(acct->onchain_resolved_block == blockheight + 2); err = update_channel_onchain_fees(ctx, db, acct); CHECK_MSG(!err, err); From ab1ca7f1591fa842aab7544f3bf9027b6bcf7332 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 16 Aug 2022 15:32:09 +0200 Subject: [PATCH 1265/1530] pytest: Reproduce a crash when we have multiple channels and 0conf This is a case where we assume that the `channel->scid` is set, which is no longer true with `zeroconf` Changelog-None: Zeroconf was not yet released at the time the discovery was made Reported-by: Yaacov Akiba Slama <@yaslama> Reported-by: Roei Erez <@roeierez> --- tests/test_opening.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index d4b48ed12506..edad2f181faa 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1575,3 +1575,41 @@ def test_scid_alias_private(node_factory, bitcoind): route[1]['channel'] = alias23 l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(inv['payment_hash']) + + +@pytest.mark.xfail("Reproducing a crash", strict=True) +def test_zeroconf_multichan_forward(node_factory): + """The freedom to choose the forward channel bytes us when it is 0conf + + Reported by Breez, we crashed when logging in `forward_htlc` when + the replacement channel was a zeroconf channel. + + l2 -> l3 is a double channel with the zeroconf channel having a + higher spendable msat, which should cause it to be chosen instead. + + """ + node_id = '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59' + plugin_path = Path(__file__).parent / "plugins" / "zeroconf-selective.py" + l1, l2, l3 = node_factory.line_graph(3, opts=[ + {}, + {}, + { + 'plugin': str(plugin_path), + 'zeroconf-allow': node_id, + } + ], fundamount=10**6, wait_for_announce=True) + + # Just making sure the allowlisted node_id matches. + assert l2.info['id'] == node_id + + # Now create a channel that is twice as large as the real channel, + # and don't announce it. + l2.fundwallet(10**7) + l2.rpc.fundchannel(l3.info['id'], 2 * 10**6, mindepth=0) + + l2.daemon.wait_for_log(r'peer_in WIRE_FUNDING_LOCKED') + l3.daemon.wait_for_log(r'peer_in WIRE_FUNDING_LOCKED') + + inv = l3.rpc.invoice(amount_msat=10000, label='lbl1', description='desc')['bolt11'] + l1.rpc.pay(inv) + assert l2.daemon.is_in_log(r'Chose a better channel: .*') From 65549a293197894c641831c3c5b051a9f3724e8e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 16 Aug 2022 15:35:08 +0200 Subject: [PATCH 1266/1530] ld: Fix a log message assuming that the `channel->scid` was set This is no longer true after we introduce `zeroconf`, so use the alias local alias instead if not set. Signed-off-by: Christian Decker <@cdecker> --- lightningd/peer_htlcs.c | 9 +++++++-- tests/test_opening.py | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 80d81745c236..a9d74e6c9879 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -660,6 +660,7 @@ static void forward_htlc(struct htlc_in *hin, struct lightningd *ld = hin->key.channel->peer->ld; struct channel *next; struct htlc_out *hout = NULL; + struct short_channel_id *altscid; /* This is a shortcut for specifying next peer; doesn't mean * the actual channel! */ @@ -687,10 +688,14 @@ static void forward_htlc(struct htlc_in *hin, channel->channel_info.their_config.htlc_minimum)) continue; + altscid = channel->scid != NULL ? channel->scid + : channel->alias[LOCAL]; + /* OK, it's better! */ log_debug(next->log, "Chose a better channel: %s", - type_to_string(tmpctx, struct short_channel_id, - channel->scid)); + type_to_string(tmpctx, + struct short_channel_id, + altscid)); next = channel; } } diff --git a/tests/test_opening.py b/tests/test_opening.py index edad2f181faa..abbdac3658d8 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1577,7 +1577,6 @@ def test_scid_alias_private(node_factory, bitcoind): l1.rpc.waitsendpay(inv['payment_hash']) -@pytest.mark.xfail("Reproducing a crash", strict=True) def test_zeroconf_multichan_forward(node_factory): """The freedom to choose the forward channel bytes us when it is 0conf From 82f703552455549f7470e6c9fab136c5a95b2225 Mon Sep 17 00:00:00 2001 From: Ken Sedgwick Date: Tue, 16 Aug 2022 11:27:06 -0700 Subject: [PATCH 1267/1530] Renumber hsmd_derive_secret for consistency with other hsmd messages --- hsmd/hsmd_wire.csv | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 26d552e5b354..ba29d2b8675c 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -282,10 +282,11 @@ msgdata,hsmd_sign_option_will_fund_offer,channel_fee_proportional_basis_max,u16, msgtype,hsmd_sign_option_will_fund_offer_reply,126 msgdata,hsmd_sign_option_will_fund_offer_reply,rsig,secp256k1_ecdsa_signature, -# Reply with the derived secret -msgtype,hsmd_derive_secret_reply,27 -msgdata,hsmd_derive_secret_reply,secret,secret, - -msgtype,hsmd_derive_secret,127 +# Derive pseudorandom secret +msgtype,hsmd_derive_secret,27 msgdata,hsmd_derive_secret,len,u16, msgdata,hsmd_derive_secret,info,u8,len + +# Reply with the derived secret +msgtype,hsmd_derive_secret_reply,127 +msgdata,hsmd_derive_secret_reply,secret,secret, From 9219e778a1e76335942173597f59e80974389780 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 18 Aug 2022 10:52:20 +0930 Subject: [PATCH 1268/1530] doc: update usage of lightning-invoice. The new parameter name (this version) is amount_msat: msatoshi is deprecated. The doc/schemas/invoice.request.json has this correct (but we don't generate teh *usage* line from that yet!) Signed-off-by: Rusty Russell --- doc/lightning-invoice.7.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 77d445fdcd7c..19f649d3e668 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -4,7 +4,7 @@ lightning-invoice -- Command for accepting payments SYNOPSIS -------- -**invoice** *msatoshi* *label* *description* [*expiry*] +**invoice** *amount_msat* *label* *description* [*expiry*] [*fallbacks*] [*preimage*] [*exposeprivatechannels*] [*cltv*] [*deschashonly*] DESCRIPTION @@ -16,7 +16,7 @@ lightning daemon can use to pay this invoice. This token includes a *route hint* description of an incoming channel with capacity to pay the invoice, if any exists. -The *msatoshi* parameter can be the string "any", which creates an +The *amount_msat* parameter can be the string "any", which creates an invoice that can be paid with any amount. Otherwise it is a positive value in millisatoshi precision; it can be a whole number, or a whole number ending in *msat* or *sat*, or a number with three decimal places ending From 23b675922ff6d3c8b346a3c145d48e64750fb101 Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 19 Aug 2022 00:30:13 +0530 Subject: [PATCH 1269/1530] lightningd/opening_control.c: Skip over channels which are already stored, and don't create new peer if it already exits. Changelog-None --- lightningd/opening_control.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 8a24ee69cbad..3e740844c3f5 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1299,11 +1299,21 @@ static struct channel *stub_chan(struct command *cmd, "647de4443938ae2dbafe2b9" "01", 144); - peer = new_peer(cmd->ld, - 0, - &nodeid, - &addr, - false); + /* If the channel is already stored, return NULL. */ + peer = peer_by_id(cmd->ld, &nodeid); + if (peer) { + if (find_channel_by_id(peer, &cid)) { + log_debug(cmd->ld->log, "channel %s already exists!", + type_to_string(tmpctx, struct channel_id, &cid)); + return NULL; + } + } else { + peer = new_peer(cmd->ld, + 0, + &nodeid, + &addr, + false); + } ld = cmd->ld; feerate = FEERATE_FLOOR; @@ -1453,6 +1463,10 @@ static struct command_result *json_recoverchannel(struct command *cmd, scb_chan->funding_sats, scb_chan->type); + /* Returns NULL only when channel already exists, so we skip over it. */ + if (channel == NULL) + continue; + /* Now we put this in the database. */ wallet_channel_insert(ld->wallet, channel); From 0b737fd7e57749d7ae8864ba1d8ac6e3f93ab4db Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 19 Aug 2022 00:30:53 +0530 Subject: [PATCH 1270/1530] doc: Typo --- doc/lightning-staticbackup.7.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lightning-staticbackup.7.md b/doc/lightning-staticbackup.7.md index 8cb5bfa1ceaa..81e7734cf666 100644 --- a/doc/lightning-staticbackup.7.md +++ b/doc/lightning-staticbackup.7.md @@ -1,4 +1,4 @@ -lightning-staticbacup -- Command for deriving getting SCB of all the existing channels +lightning-staticbackup -- Command for deriving getting SCB of all the existing channels ====================================================================================== SYNOPSIS From 8f78a76d1a4aaf82b112804946fcf6bd21bea28f Mon Sep 17 00:00:00 2001 From: adi2011 Date: Fri, 19 Aug 2022 01:23:12 +0530 Subject: [PATCH 1271/1530] tests/test_misc.py: check logs for already existing channel. --- tests/test_misc.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_misc.py b/tests/test_misc.py index 9deeba63c5ed..b63f84c7414e 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2312,6 +2312,8 @@ def test_emergencyrecover(node_factory, bitcoind): l1, l2 = node_factory.get_nodes(2, opts=[{}, {}]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) c12, _ = l1.fundchannel(l2, 10**5) + stubs = l1.rpc.emergencyrecover()["stubs"] + assert l1.daemon.is_in_log('channel {} already exists!'.format(_['channel_id'])) l1.stop() @@ -2322,6 +2324,7 @@ def test_emergencyrecover(node_factory, bitcoind): stubs = l1.rpc.emergencyrecover()["stubs"] assert len(stubs) == 1 assert stubs[0] == _["channel_id"] + assert l1.daemon.is_in_log('channel {} already exists!'.format(_['channel_id'])) listfunds = l1.rpc.listfunds()["channels"][0] assert listfunds["short_channel_id"] == "1x1x1" From aabf38a11dccfed11ae54a150fcde3b8000e4b5b Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 19 Aug 2022 14:44:03 +0200 Subject: [PATCH 1272/1530] ci: Fix the Mac OS build test --- .github/workflows/macos.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yaml b/.github/workflows/macos.yaml index c763c829c0ee..58ba7bae5997 100644 --- a/.github/workflows/macos.yaml +++ b/.github/workflows/macos.yaml @@ -19,7 +19,7 @@ jobs: - name: Install dependencies run: | - export PATH="/usr/local/opt:/Users/runner/.local/bin:/Users/runner/Library/Python/3.9/bin:$PATH" + export PATH="/usr/local/opt:/Users/runner/.local/bin:/Users/runner/Library/Python/3.10/bin:$PATH" export BITCOIN_VERSION=0.20.1 brew install wget python autoconf automake libtool python3 gmp gnu-sed gettext libsodium @@ -53,7 +53,7 @@ jobs: TEST_GROUP_COUNT: ${{ matrix.TEST_GROUP_COUNT }} TEST_GROUP: ${{ matrix.TEST_GROUP }} run: | - export PATH="/usr/local/opt:/Users/runner/.local/bin:/Users/runner/Library/Python/3.9/bin:$PATH" + export PATH="/usr/local/opt:/Users/runner/.local/bin:/Users/runner/Library/Python/3.10/bin:$PATH" export LDFLAGS="-L/usr/local/opt/sqlite/lib" export CPPFLAGS="-I/usr/local/opt/sqlite/include" From 7c9985fd9c0c532581a46a75a1fe43ecb9013850 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 19 Aug 2022 11:54:04 +0930 Subject: [PATCH 1273/1530] channeld: correct reversed shutdown message in billboard. It was backwards, which made #5496 confusing. Signed-off-by: Rusty Russell Changelog-Fixed: `listpeers` `status` "They've sent shutdown" and "We've sent shutdown" were backwards. --- common/billboard.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/billboard.c b/common/billboard.c index 04a685e369d8..5a1e62542b58 100644 --- a/common/billboard.c +++ b/common/billboard.c @@ -42,9 +42,9 @@ char *billboard_message(const tal_t *ctx, if (!shutdown_sent[LOCAL] && !shutdown_sent[REMOTE]) shutdown_status = ""; else if (!shutdown_sent[LOCAL] && shutdown_sent[REMOTE]) - shutdown_status = " We've send shutdown, waiting for theirs"; - else if (shutdown_sent[LOCAL] && !shutdown_sent[REMOTE]) shutdown_status = " They've sent shutdown, waiting for ours"; + else if (shutdown_sent[LOCAL] && !shutdown_sent[REMOTE]) + shutdown_status = " We've send shutdown, waiting for theirs"; else if (shutdown_sent[LOCAL] && shutdown_sent[REMOTE]) { if (num_htlcs) shutdown_status = tal_fmt(ctx, From 8cdbe97d9174737117d5ef7481d4e88539496425 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 16 Aug 2022 14:57:14 -0500 Subject: [PATCH 1274/1530] v0.12.0rc3 - Release Candidate numero tres Release Candidate 3 for v0.12.0. Now with more bugfixes! --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49975fb2bd20..2326d0567068 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [0.12.0rc2] - 2022-08-10: "TBD" +## [0.12.0rc3] - 2022-08-10: "TBD" This release named by @adi2011. @@ -56,6 +56,8 @@ Developers please note the Great Msat Migration has begun: - gossip: gossip\_store updated to version 10. ([#5239]) - Options: `log-file` option specified multiple times opens multiple log files. ([#5281]) - JSON-RPC: `sendpay` and `sendonion` now obey the first hop "channel" short_channel_id, if specified. ([#5505]) + - JSON-RPC: `signpsbt` no longer crashes if it doesn't like what your PSBT is ([#5506]) + - JSON-RPC: `signpsbt` will now add redeemscript + witness-utxo to the PSBT for an input that we can sign for, before signing it. ([#5506]) - JSON-RPC: `plugin start` now assumes relative path to default plugins dir if the path is not found in absolute context. i.e. lightning-cli plugin start my_plugin.py ([#5211]) - JSON-RPC: `fundchannel`: now errors if you try to buy a liquidity ad but dont' have `experimental-dual-fund` enabled ([#5389]) - JSON-RPC: "\_msat" fields can be raw numbers, not "123msat" strings (please handle both!) ([#5306]) @@ -169,6 +171,7 @@ Note: You should always set `allow-deprecated-apis=false` to test for changes. [#5489]: https://github.com/ElementsProject/lightning/pull/5489 [#5501]: https://github.com/ElementsProject/lightning/pull/5501 [#5505]: https://github.com/ElementsProject/lightning/pull/5505 +[#5506]: https://github.com/ElementsProject/lightning/pull/5506 [#5513]: https://github.com/ElementsProject/lightning/pull/5513 @@ -1860,7 +1863,7 @@ There predate the BOLT specifications, and are only of vague historic interest: 6. [0.5.1] - 2016-10-21 7. [0.5.2] - 2016-11-21: "Bitcoin Savings & Trust Daily Interest II" -[0.12.0rc2]: https://github.com/ElementsProject/lightning/releases/tag/v0.12.0rc2 +[0.12.0rc3]: https://github.com/ElementsProject/lightning/releases/tag/v0.12.0rc3 [0.11.2]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.2 [0.11.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.1 [0.11.0.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0.1 From c13dffe980938228dda15b7c3ce3288cf6caef55 Mon Sep 17 00:00:00 2001 From: greggzigler Date: Wed, 10 Aug 2022 12:40:58 -0700 Subject: [PATCH 1275/1530] fix MD link where text and url are swapped --- doc/FAQ.md | 2 +- doc/STYLE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/FAQ.md b/doc/FAQ.md index 3618a063999a..12e69f407440 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -115,7 +115,7 @@ In summary: as a Bitcoin user, one may be familiar with a file or a seed it can recover all its funds. Core Lightning has an internal bitcoin wallet, which you can use to make "on-chain" -transactions, (see [withdraw](https://lightning.readthedocs.io/lightning-withdraw.7.html). +transactions, (see [withdraw](https://lightning.readthedocs.io/lightning-withdraw.7.html)). These on-chain funds are backed up via the HD wallet seed, stored in byte-form in `hsm_secret`. `lightningd` also stores information for funds locked in Lightning Network channels, which are stored diff --git a/doc/STYLE.md b/doc/STYLE.md index 78c0ba84b257..9936fa0cdd16 100644 --- a/doc/STYLE.md +++ b/doc/STYLE.md @@ -198,7 +198,7 @@ for programs to deal with them sanely, and also perform translations. We use JSON schemas to validate that JSON-RPC returns are in the correct form, and also to generate documentation. See -[doc/schemas/WRITING_SCHEMAS.md](WRITING_SCHEMAS.md). +[WRITING_SCHEMAS.md](schemas/WRITING_SCHEMAS.md). ## Changing JSON APIs From ce9232af37a75d773cce9518236398c00888466d Mon Sep 17 00:00:00 2001 From: greggzigler Date: Wed, 10 Aug 2022 13:49:32 -0700 Subject: [PATCH 1276/1530] Changelog-None --- doc/STYLE.md | 2 +- doc/schemas/WRITING_SCHEMAS.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/STYLE.md b/doc/STYLE.md index 9936fa0cdd16..78422daf1aae 100644 --- a/doc/STYLE.md +++ b/doc/STYLE.md @@ -198,7 +198,7 @@ for programs to deal with them sanely, and also perform translations. We use JSON schemas to validate that JSON-RPC returns are in the correct form, and also to generate documentation. See -[WRITING_SCHEMAS.md](schemas/WRITING_SCHEMAS.md). +[writing schemas manpage](schemas/WRITING_SCHEMAS.md). ## Changing JSON APIs diff --git a/doc/schemas/WRITING_SCHEMAS.md b/doc/schemas/WRITING_SCHEMAS.md index 2303ad668572..bf42a5fff666 100644 --- a/doc/schemas/WRITING_SCHEMAS.md +++ b/doc/schemas/WRITING_SCHEMAS.md @@ -5,10 +5,10 @@ look like; in our case we use it in our testsuite to check that they match command responses, and also use it to generate our documentation. -Yes, schemas are horrible to write, but they're damn useful. We can -only use a subset of the full [https://json-schema.org/](JSON Schema -Specification), but if you find that limiting it's probably a sign -that you should simplify your JSON output. +Yes, schemas are horrible to write, but they're damn useful. We can only +use a subset of the full [JSON Schema Specification](https://json-schema.org/), +but if you find that limiting it's probably a sign that you should simplify +your JSON output. ## How to Write a Schema From fd8b667e6a087edebe8907d249180c4e26c58337 Mon Sep 17 00:00:00 2001 From: Orb Date: Mon, 15 Aug 2022 07:24:37 +0800 Subject: [PATCH 1277/1530] use 'postgres' driver (not postgresql). Include plain url to guide, as link is broken on RTD. --- doc/BACKUP.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/BACKUP.md b/doc/BACKUP.md index 720b49208297..6fd1e1ac5714 100644 --- a/doc/BACKUP.md +++ b/doc/BACKUP.md @@ -394,13 +394,13 @@ it just gains the option to use a PostgreSQL database as well. If you just want to use PostgreSQL without using a cluster (for example, as an initial test without risking any significant funds), then after setting up a PostgreSQL database, you just need to add -`--wallet=postgresql://${USER}:${PASSWORD}@${HOST}:${PORT}/${DB}` +`--wallet=postgres://${USER}:${PASSWORD}@${HOST}:${PORT}/${DB}` to your `lightningd` config or invocation. To set up a cluster for a brand new node, follow this (external) [guide by @gabridome][gabridomeguide]. -[gabridomeguide]: https://github.com/gabridome/docs/blob/master/c-lightning_with_postgresql_reliability.md +[gabridomeguide]: https://bit.ly/3KffmN3 The above guide assumes you are setting up a new node from scratch. It is also specific to PostgreSQL 12, and setting up for other versions From 3ce26735e5143fa9532247156f8e97291cb2a225 Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 22 Aug 2022 14:52:56 -0500 Subject: [PATCH 1278/1530] v0.12.0 release changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2326d0567068..486ce6457620 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 TODO: Insert version codename, and username of the contributor that named the release. --> -## [0.12.0rc3] - 2022-08-10: "TBD" +## [0.12.0] - 2022-08-23: Web-8 init This release named by @adi2011. @@ -1863,7 +1863,7 @@ There predate the BOLT specifications, and are only of vague historic interest: 6. [0.5.1] - 2016-10-21 7. [0.5.2] - 2016-11-21: "Bitcoin Savings & Trust Daily Interest II" -[0.12.0rc3]: https://github.com/ElementsProject/lightning/releases/tag/v0.12.0rc3 +[0.12.0]: https://github.com/ElementsProject/lightning/releases/tag/v0.12.0 [0.11.2]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.2 [0.11.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.1 [0.11.0.1]: https://github.com/ElementsProject/lightning/releases/tag/v0.11.0.1 From 0abe2e3af1ccd9941992266b65f85aa32d3ccb23 Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Thu, 21 Jul 2022 14:04:04 -0500 Subject: [PATCH 1279/1530] pytest: Add debugging to test_gossip_store_compact_on_load This flake has been difficult to reproduce, so let's dump the gossip store to aid in debugging. See issue #5410. Changelog-None --- tests/test_gossip.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index fcd154b2a181..aa871dccaa2d 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1716,6 +1716,11 @@ def test_gossip_store_load_no_channel_update(node_factory): def test_gossip_store_compact_on_load(node_factory, bitcoind): l2 = setup_gossip_store_test(node_factory, bitcoind) + gs_path = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, 'gossip_store') + gs = subprocess.run(['devtools/dump-gossipstore', '--print-deleted', gs_path], + check=True, timeout=TIMEOUT, stdout=subprocess.PIPE) + print(gs.stdout.decode()) + l2.restart() wait_for(lambda: l2.daemon.is_in_log(r'gossip_store_compact_offline: [5-8] deleted, 9 copied')) From 397c8804261690e1e8864c533b263d273fbb6f4b Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Wed, 22 Jun 2022 22:14:20 +0700 Subject: [PATCH 1280/1530] Add Arch Linux build instructions --- doc/INSTALL.md | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 7ba064839db4..1778985e0850 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -8,11 +8,12 @@ Install 5. [OpenBSD](#to-build-on-openbsd) 6. [NixOS](#to-build-on-nixos) 7. [macOS](#to-build-on-macos) -8. [Android](#to-cross-compile-for-android) -9. [Raspberry Pi](#to-cross-compile-for-raspberry-pi) -10. [Armbian](#to-compile-for-armbian) -11. [Alpine](#to-compile-for-alpine) -12. [Additional steps](#additional-steps) +8. [Arch Linux](#to-build-on-arch-linux) +9. [Android](#to-cross-compile-for-android) +10. [Raspberry Pi](#to-cross-compile-for-raspberry-pi) +11. [Armbian](#to-compile-for-armbian) +12. [Alpine](#to-compile-for-alpine) +13. [Additional steps](#additional-steps) Library Requirements -------------------- @@ -305,6 +306,36 @@ need to include `testnet=1` ./lightningd/lightningd & ./cli/lightning-cli help +To Build on Arch Linux +--------------------- + +Install dependencies: + +``` +pacman --sync autoconf automake gcc git make python-pip +pip install --user poetry +``` + +Clone Core Lightning: +``` +$ git clone https://github.com/ElementsProject/lightning.git +$ cd lightning +``` + +Build Core Lightning: + +``` +~/.local/bin/poetry install +./configure +~/.local/bin/poetry run make +``` + +Launch Core Lightning: + +``` +./lightningd/lightningd +``` + To cross-compile for Android -------------------- From ba76854e2964365cdb534fd55e7f60f647761ce9 Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Wed, 22 Jun 2022 22:17:13 +0700 Subject: [PATCH 1281/1530] Add Arch Linux build instructions --- doc/INSTALL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 1778985e0850..5a1f9ea6290a 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -317,6 +317,7 @@ pip install --user poetry ``` Clone Core Lightning: + ``` $ git clone https://github.com/ElementsProject/lightning.git $ cd lightning From 299a99ed6745bf618d9897700fd45f21ba1539c0 Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Sat, 16 Jul 2022 20:36:02 +0700 Subject: [PATCH 1282/1530] Use unordered list --- doc/INSTALL.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 5a1f9ea6290a..041485a02d72 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -1,19 +1,19 @@ Install ======= -1. [Library Requirements](#library-requirements) -2. [Ubuntu](#to-build-on-ubuntu) -3. [Fedora](#to-build-on-fedora) -4. [FreeBSD](#to-build-on-freebsd) -5. [OpenBSD](#to-build-on-openbsd) -6. [NixOS](#to-build-on-nixos) -7. [macOS](#to-build-on-macos) -8. [Arch Linux](#to-build-on-arch-linux) -9. [Android](#to-cross-compile-for-android) -10. [Raspberry Pi](#to-cross-compile-for-raspberry-pi) -11. [Armbian](#to-compile-for-armbian) -12. [Alpine](#to-compile-for-alpine) -13. [Additional steps](#additional-steps) +- [Library Requirements](#library-requirements) +- [Ubuntu](#to-build-on-ubuntu) +- [Fedora](#to-build-on-fedora) +- [FreeBSD](#to-build-on-freebsd) +- [OpenBSD](#to-build-on-openbsd) +- [NixOS](#to-build-on-nixos) +- [macOS](#to-build-on-macos) +- [Arch Linux](#to-build-on-arch-linux) +- [Android](#to-cross-compile-for-android) +- [Raspberry Pi](#to-cross-compile-for-raspberry-pi) +- [Armbian](#to-compile-for-armbian) +- [Alpine](#to-compile-for-alpine) +- [Additional steps](#additional-steps) Library Requirements -------------------- From d4a04ba8b316e7653c909eb0af0490d41f966bc8 Mon Sep 17 00:00:00 2001 From: Igor Bubelov Date: Sat, 16 Jul 2022 21:06:48 +0700 Subject: [PATCH 1283/1530] Simplify poetry interactions --- doc/INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 041485a02d72..768d7073d71d 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -326,9 +326,9 @@ $ cd lightning Build Core Lightning: ``` -~/.local/bin/poetry install +python -m poetry install ./configure -~/.local/bin/poetry run make +python -m poetry run make ``` Launch Core Lightning: From 6a7d40f51a9ba74f1bbb1621cc5a7b1d06580cca Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 16:29:09 +0930 Subject: [PATCH 1284/1530] ccan: update to get -Wshadow=local clean build. Signed-off-by: Rusty Russell --- ccan/README | 2 +- ccan/ccan/tal/tal.c | 8 ++++---- ccan/tools/configurator/configurator.c | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ccan/README b/ccan/README index acdc78064c9a..4fab455bc633 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2541-g52b86922 +CCAN version: init-2545-g7b11e744 diff --git a/ccan/ccan/tal/tal.c b/ccan/ccan/tal/tal.c index e0ca30d72c32..69270c6ffb8c 100644 --- a/ccan/ccan/tal/tal.c +++ b/ccan/ccan/tal/tal.c @@ -594,12 +594,12 @@ bool tal_set_name_(tal_t *ctx, const char *name, bool literal) /* Get rid of any old name */ if (prop) { - struct name *name = (struct name *)*prop; - if (is_literal(&name->hdr)) + struct name *oldname = (struct name *)*prop; + if (is_literal(&oldname->hdr)) *prop = NULL; else { - *prop = name->hdr.next; - freefn(name); + *prop = oldname->hdr.next; + freefn(oldname); } } diff --git a/ccan/tools/configurator/configurator.c b/ccan/tools/configurator/configurator.c index 57779f29905b..f830cbca14eb 100644 --- a/ccan/tools/configurator/configurator.c +++ b/ccan/tools/configurator/configurator.c @@ -797,12 +797,12 @@ static bool run_test(const char *cmd, const char *wrapper, struct test *test) /* We run INSIDE_MAIN tests for sanity checking. */ if (strstr(test->style, "EXECUTE") || strstr(test->style, "INSIDE_MAIN")) { - char *cmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1); + char *runcmd = malloc(strlen(wrapper) + strlen(" ." DIR_SEP OUTPUT_FILE) + 1); - strcpy(cmd, wrapper); - strcat(cmd, " ." DIR_SEP OUTPUT_FILE); - output = run(cmd, &status); - free(cmd); + strcpy(runcmd, wrapper); + strcat(runcmd, " ." DIR_SEP OUTPUT_FILE); + output = run(runcmd, &status); + free(runcmd); if (!strstr(test->style, "EXECUTE") && status != 0) c12r_errx(EXIT_BAD_TEST, "Test for %s failed with %i:\n%s", From 6fe570820e66491778b9e60d2b2942bbffbee813 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 16:30:09 +0930 Subject: [PATCH 1285/1530] Remove general shadowed variables. We shadow local variables in several places: generally, these changes make the code clearer. Signed-off-by: Rusty Russell --- channeld/channeld.c | 19 ++++++---------- channeld/commit_tx.c | 8 +++---- cli/lightning-cli.c | 6 ++--- common/test/run-bolt12_period.c | 2 +- .../test/run-route_blinding_override_test.c | 12 +++++----- devtools/bolt11-cli.c | 6 ++--- devtools/decodemsg.c | 4 ++-- devtools/gossipwith.c | 2 +- devtools/route.c | 3 +-- gossipd/gossip_store.c | 6 ++--- gossipd/queries.c | 6 ++--- gossipd/queries.h | 9 ++++---- gossipd/test/run-next_block_range.c | 9 ++++---- lightningd/bitcoind.c | 6 ++--- lightningd/bitcoind.h | 6 ++--- lightningd/chaintopology.c | 6 ++--- lightningd/memdump.c | 15 +++++++------ lightningd/onchain_control.c | 6 ++--- lightningd/plugin_hook.c | 10 ++++----- lightningd/signmessage.c | 1 - plugins/libplugin-pay.c | 22 +++++++++---------- plugins/libplugin.c | 10 ++++----- plugins/pay.c | 2 +- plugins/txprepare.c | 12 +++++----- wallet/db.c | 3 +-- 25 files changed, 92 insertions(+), 99 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index ef2293c36062..15aabc5b984f 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1270,8 +1270,6 @@ static void send_commit(struct peer *peer) * don't stress things by having more than one feerate change * in-flight! */ if (feerate_changes_done(peer->channel->fee_states, false)) { - u8 *msg; - /* BOLT-919 #2: * * A sending node: @@ -1309,8 +1307,6 @@ static void send_commit(struct peer *peer) if (want_blockheight_update(peer, &our_blockheight)) { if (blockheight_changes_done(peer->channel->blockheight_states, false)) { - u8 *msg; - channel_update_blockheight(peer->channel, our_blockheight); @@ -2442,14 +2438,14 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) } else tlvs = NULL; #endif - u8 *msg = towire_update_add_htlc(NULL, &peer->channel_id, - h->id, h->amount, - &h->rhash, - abs_locktime_to_blocks( - &h->expiry), - h->routing + msg = towire_update_add_htlc(NULL, &peer->channel_id, + h->id, h->amount, + &h->rhash, + abs_locktime_to_blocks( + &h->expiry), + h->routing #if EXPERIMENTAL_FEATURES - , tlvs + , tlvs #endif ); peer_write(peer->pps, take(msg)); @@ -2952,7 +2948,6 @@ static void peer_reconnect(struct peer *peer, if (peer->funding_locked[LOCAL] && peer->next_index[LOCAL] == 1 && next_commitment_number == 1) { - u8 *msg; struct tlv_funding_locked_tlvs *tlvs = tlv_funding_locked_tlvs_new(tmpctx); status_debug("Retransmitting funding_locked for channel %s", diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index 54116c42944e..6f8c41c8320b 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -120,7 +120,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, struct amount_sat base_fee; struct amount_msat total_pay; struct bitcoin_tx *tx; - size_t i, n, untrimmed; + size_t n, untrimmed; /* Is this the lessor ? */ enum side lessor = !opener; u32 *cltvs; @@ -181,7 +181,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, { struct amount_sat out = AMOUNT_SAT(0); bool ok = true; - for (i = 0; i < tal_count(htlcs); i++) { + for (size_t i = 0; i < tal_count(htlcs); i++) { if (!trim(htlcs[i], feerate_per_kw, dust_limit, option_anchor_outputs, side)) ok &= amount_sat_add(&out, out, amount_msat_to_sat_round_down(htlcs[i]->amount)); @@ -215,7 +215,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * 4. For every offered HTLC, if it is not trimmed, add an * [offered HTLC output](#offered-htlc-outputs). */ - for (i = 0; i < tal_count(htlcs); i++) { + for (size_t i = 0; i < tal_count(htlcs); i++) { if (htlc_owner(htlcs[i]) != side) continue; if (trim(htlcs[i], feerate_per_kw, dust_limit, @@ -233,7 +233,7 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * 5. For every received HTLC, if it is not trimmed, add an * [received HTLC output](#received-htlc-outputs). */ - for (i = 0; i < tal_count(htlcs); i++) { + for (size_t i = 0; i < tal_count(htlcs); i++) { if (htlc_owner(htlcs[i]) == side) continue; if (trim(htlcs[i], feerate_per_kw, dust_limit, diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index ede08a6b4485..12f3ad7db3fb 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -599,7 +599,7 @@ int main(int argc, char *argv[]) { setup_locale(); - int fd, i; + int fd; size_t off; const char *method; char *cmd, *resp, *idstr; @@ -715,7 +715,7 @@ int main(int argc, char *argv[]) if (input == KEYWORDS) { tal_append_fmt(&cmd, "{ "); - for (i = 2; i < argc; i++) { + for (size_t i = 2; i < argc; i++) { const char *eq = strchr(argv[i], '='); if (!eq) @@ -730,7 +730,7 @@ int main(int argc, char *argv[]) tal_append_fmt(&cmd, "} }"); } else { tal_append_fmt(&cmd, "[ "); - for (i = 2; i < argc; i++) + for (size_t i = 2; i < argc; i++) add_input(&cmd, argv[i], i, argc); tal_append_fmt(&cmd, "] }"); } diff --git a/common/test/run-bolt12_period.c b/common/test/run-bolt12_period.c index 1b8bd553f4c3..c7e10d1d780a 100644 --- a/common/test/run-bolt12_period.c +++ b/common/test/run-bolt12_period.c @@ -215,7 +215,7 @@ int main(int argc, char *argv[]) /* Replace day */ tparts[0] = "UTC"; /* Now each part should appear. */ - for (size_t i = 0; tparts[i]; i++) + for (i = 0; tparts[i]; i++) assert(strstr(timestring, tparts[i])); assert(offer_period_start(base, n, &recurrence) == secs); } diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c index 4692b73d3610..ffc0aebeed8a 100644 --- a/common/test/run-route_blinding_override_test.c +++ b/common/test/run-route_blinding_override_test.c @@ -327,7 +327,7 @@ int main(int argc, char *argv[]) json_for_each_arr(i, t, unblinding_hops) { struct privkey me; struct secret ss; - struct pubkey blinding, expected_blinding; + struct pubkey blindingpub, expected_blinding; struct pubkey onion_key, next_node; assert(json_to_secret(json, @@ -337,20 +337,20 @@ int main(int argc, char *argv[]) mykey = &me; assert(json_to_pubkey(json, json_get_member(json, t, "ephemeral_pubkey"), - &blinding)); + &blindingpub)); - assert(unblind_onion(&blinding, test_ecdh, &onion_key, &ss)); + assert(unblind_onion(&blindingpub, test_ecdh, &onion_key, &ss)); if (i != unblinding_hops->size - 1) { - assert(decrypt_enctlv(&blinding, &ss, encrypted_data[i], &next_node, &blinding)); + assert(decrypt_enctlv(&blindingpub, &ss, encrypted_data[i], &next_node, &blindingpub)); assert(json_to_pubkey(json, json_get_member(json, t, "next_ephemeral_pubkey"), &expected_blinding)); - assert(pubkey_eq(&blinding, &expected_blinding)); + assert(pubkey_eq(&blindingpub, &expected_blinding)); } else { struct secret *path_id; struct pubkey my_id, alias; assert(pubkey_from_privkey(&me, &my_id)); - assert(decrypt_final_enctlv(tmpctx, &blinding, &ss, + assert(decrypt_final_enctlv(tmpctx, &blindingpub, &ss, encrypted_data[i], &my_id, &alias, &path_id)); diff --git a/devtools/bolt11-cli.c b/devtools/bolt11-cli.c index 018d0e8af7f8..4d3339d39b8a 100644 --- a/devtools/bolt11-cli.c +++ b/devtools/bolt11-cli.c @@ -54,7 +54,6 @@ int main(int argc, char *argv[]) const char *method; struct bolt11 *b11; struct bolt11_field *extra; - size_t i; char *fail, *description = NULL; common_setup(argv[0]); @@ -120,7 +119,7 @@ int main(int argc, char *argv[]) } printf("\n"); } - for (i = 0; i < tal_count(b11->fallbacks); i++) { + for (size_t i = 0; i < tal_count(b11->fallbacks); i++) { struct bitcoin_address pkh; struct ripemd160 sh; struct sha256 wsh; @@ -148,7 +147,7 @@ int main(int argc, char *argv[]) } } - for (i = 0; i < tal_count(b11->routes); i++) { + for (size_t i = 0; i < tal_count(b11->routes); i++) { printf("route: (node/chanid/fee/expirydelta) "); for (size_t n = 0; n < tal_count(b11->routes[i]); n++) { printf(" %s/%s/%u/%u/%u", @@ -169,6 +168,7 @@ int main(int argc, char *argv[]) list_for_each(&b11->extra_fields, extra, list) { char *data = tal_arr(ctx, char, tal_count(extra->data)+1); + size_t i; for (i = 0; i < tal_count(extra->data); i++) data[i] = bech32_charset[extra->data[i]]; diff --git a/devtools/decodemsg.c b/devtools/decodemsg.c index 6559ca290015..fdead6bf9e92 100644 --- a/devtools/decodemsg.c +++ b/devtools/decodemsg.c @@ -105,8 +105,8 @@ int main(int argc, char *argv[]) } m = tal_dup_arr(f, u8, f + off, be16_to_cpu(len), 0); if (printtlv) { - size_t len = tal_bytelen(m); - ok &= printtlv("", &m, &len); + size_t mlen = tal_bytelen(m); + ok &= printtlv("", &m, &mlen); } else { ok &= printwire(m); } diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index 1303b8c82d8f..ada35b14cacc 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -170,7 +170,6 @@ static struct io_plan *handshake_success(struct io_conn *conn, struct oneshot *timer, char **args) { - u8 *msg; int peer_fd = io_conn_fd(conn); struct pollfd pollfd[2]; @@ -179,6 +178,7 @@ static struct io_plan *handshake_success(struct io_conn *conn, OPTIONAL_FEATURE(OPT_INITIAL_ROUTING_SYNC)); if (!no_init) { + u8 *msg; struct tlv_init_tlvs *tlvs = NULL; if (explicit_network) { tlvs = tlv_init_tlvs_new(NULL); diff --git a/devtools/route.c b/devtools/route.c index 68c1633d7373..ebf809301a72 100644 --- a/devtools/route.c +++ b/devtools/route.c @@ -103,10 +103,9 @@ int main(int argc, char *argv[]) for (n = gossmap_first_node(map); n; n = gossmap_next_node(map, n)) { - struct node_id srcid, dstid; + struct node_id srcid; gossmap_node_get_id(map, n, &srcid); - gossmap_node_get_id(map, dst, &dstid); printf("# %s->%s\n", type_to_string(tmpctx, struct node_id, &srcid), type_to_string(tmpctx, struct node_id, &dstid)); diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 5adf99b7bb44..3eed656529ad 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -785,17 +785,17 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) switch (fromwire_peektype(msg)) { case WIRE_GOSSIP_STORE_PRIVATE_CHANNEL: { - u8 *chan_ann; + u8 *priv_chan_ann; struct amount_sat sat; if (!fromwire_gossip_store_private_channel(msg, msg, &sat, - &chan_ann)) { + &priv_chan_ann)) { bad = "Bad private_channel"; goto badmsg; } if (!routing_add_private_channel(rstate, NULL, - sat, chan_ann, + sat, priv_chan_ann, gs->len)) { bad = "Bad add_private_channel"; goto badmsg; diff --git a/gossipd/queries.c b/gossipd/queries.c index cb5a61d115ba..4c894f436803 100644 --- a/gossipd/queries.c +++ b/gossipd/queries.c @@ -1069,9 +1069,9 @@ bool query_channel_range(struct daemon *daemon, struct peer *peer, u32 first_blocknum, u32 number_of_blocks, enum query_option_flags qflags, - void (*cb)(struct peer *peer, - u32 first_blocknum, u32 number_of_blocks, - const struct range_query_reply *replies)) + void (*cb)(struct peer *, + u32, u32, + const struct range_query_reply *)) { u8 *msg; struct tlv_query_channel_range_tlvs *tlvs; diff --git a/gossipd/queries.h b/gossipd/queries.h index 1e4019f11115..2a5f63d3010b 100644 --- a/gossipd/queries.h +++ b/gossipd/queries.h @@ -40,16 +40,17 @@ bool query_channel_range(struct daemon *daemon, struct peer *peer, u32 first_blocknum, u32 number_of_blocks, enum query_option_flags qflags, - void (*cb)(struct peer *peer, - u32 first_blocknum, u32 number_of_blocks, - const struct range_query_reply *replies)); + void (*cb)(struct peer *peer_, + u32 first_blocknum_, + u32 number_of_blocks_, + const struct range_query_reply *replies_)); /* Ask this peer for info about an array of scids, with optional query_flags */ bool query_short_channel_ids(struct daemon *daemon, struct peer *peer, const struct short_channel_id *scids, const u8 *query_flags, - void (*cb)(struct peer *peer, bool complete)); + void (*cb)(struct peer *peer_, bool complete)); #if DEVELOPER struct io_plan *query_scids_req(struct io_conn *conn, diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index 8eeb85107a01..b7f39e6fe2cc 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -43,16 +43,17 @@ bool query_channel_range(struct daemon *daemon UNNEEDED, struct peer *peer UNNEEDED, u32 first_blocknum UNNEEDED, u32 number_of_blocks UNNEEDED, enum query_option_flags qflags UNNEEDED, - void (*cb)(struct peer *peer UNNEEDED, - u32 first_blocknum UNNEEDED, u32 number_of_blocks UNNEEDED, - const struct range_query_reply *replies)) + void (*cb)(struct peer *peer_ UNNEEDED, + u32 first_blocknum_ UNNEEDED, + u32 number_of_blocks_ UNNEEDED, + const struct range_query_reply *replies_)) { fprintf(stderr, "query_channel_range called!\n"); abort(); } /* Generated stub for query_short_channel_ids */ bool query_short_channel_ids(struct daemon *daemon UNNEEDED, struct peer *peer UNNEEDED, const struct short_channel_id *scids UNNEEDED, const u8 *query_flags UNNEEDED, - void (*cb)(struct peer *peer UNNEEDED, bool complete)) + void (*cb)(struct peer *peer_ UNNEEDED, bool complete)) { fprintf(stderr, "query_short_channel_ids called!\n"); abort(); } /* Generated stub for queue_peer_msg */ void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 72472ccb09d2..d19968e3a7ac 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -549,9 +549,9 @@ static void getutxout_callback(const char *buf, const jsmntok_t *toks, void bitcoind_getutxout_(struct bitcoind *bitcoind, const struct bitcoin_outpoint *outpoint, - void (*cb)(struct bitcoind *bitcoind, - const struct bitcoin_tx_output *txout, - void *arg), + void (*cb)(struct bitcoind *, + const struct bitcoin_tx_output *, + void *), void *cb_arg) { struct jsonrpc_request *req; diff --git a/lightningd/bitcoind.h b/lightningd/bitcoind.h index ef3f1876e6b5..20be28a56964 100644 --- a/lightningd/bitcoind.h +++ b/lightningd/bitcoind.h @@ -150,9 +150,9 @@ void bitcoind_getrawblockbyheight_(struct bitcoind *bitcoind, void bitcoind_getutxout_(struct bitcoind *bitcoind, const struct bitcoin_outpoint *outpoint, - void (*cb)(struct bitcoind *bitcoind, - const struct bitcoin_tx_output *txout, - void *arg), + void (*cb)(struct bitcoind *, + const struct bitcoin_tx_output *, + void *), void *arg); #define bitcoind_getutxout(bitcoind_, outpoint_, cb, arg) \ bitcoind_getutxout_((bitcoind_), (outpoint_), \ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 597a6a4b8b8d..9bcc27cefd5b 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -761,7 +761,7 @@ static void remove_tip(struct chain_topology *topo) { struct block *b = topo->tip; struct bitcoin_txid *txs; - size_t i, n; + size_t n; const struct short_channel_id *removed_scids; log_debug(topo->log, "Removing stale block %u: %s", @@ -780,7 +780,7 @@ static void remove_tip(struct chain_topology *topo) n = tal_count(txs); /* Notify that txs are kicked out (their height will be set NULL in db) */ - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) txwatch_fire(topo, &txs[i], 0); /* Grab these before we delete block from db */ @@ -795,7 +795,7 @@ static void remove_tip(struct chain_topology *topo) /* These no longer exist, so gossipd drops any reference to them just * as if they were spent. */ - for (size_t i=0; ibitcoind->ld, &removed_scids[i]); } diff --git a/lightningd/memdump.c b/lightningd/memdump.c index d0deabaa481e..8adb83b51857 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -29,14 +29,13 @@ static void json_add_ptr(struct json_stream *response, const char *name, } static size_t add_memdump(struct json_stream *response, - const char *name, const tal_t *root, - struct command *cmd) + const char *fieldname, const tal_t *root, + struct command *cmd) { - const tal_t *i; size_t cumulative_size = 0; - json_array_start(response, name); - for (i = tal_first(root); i; i = tal_next(i)) { + json_array_start(response, fieldname); + for (const tal_t *i = tal_first(root); i; i = tal_next(i)) { const char *name = tal_name(i); size_t size = tal_bytelen(i); @@ -176,9 +175,11 @@ static void finish_report(const struct leak_detect *leaks) json_object_end(response); } - for (size_t i = 0; i < tal_count(leaks->leakers); i++) { + for (size_t num_leakers = 0; + num_leakers < tal_count(leaks->leakers); + num_leakers++) { json_object_start(response, NULL); - json_add_string(response, "subdaemon", leaks->leakers[i]); + json_add_string(response, "subdaemon", leaks->leakers[num_leakers]); json_object_end(response); } json_array_end(response); diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 27ceafe4ea23..4eb634cfeb81 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -704,10 +704,10 @@ enum watch_result onchaind_funding_spent(struct channel *channel, if (!feerates[i]) { /* We have at least one data point: the last tx's feerate. */ struct amount_sat fee = channel->funding_sats; - for (size_t i = 0; - i < channel->last_tx->wtx->num_outputs; i++) { + for (size_t j = 0; + j < channel->last_tx->wtx->num_outputs; j++) { struct amount_asset asset = - bitcoin_tx_output_get_amount(channel->last_tx, i); + bitcoin_tx_output_get_amount(channel->last_tx, j); struct amount_sat amt; assert(amount_asset_is_main(&asset)); amt = amount_asset_to_sat(&asset); diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index afdffca88da5..afab5785f59b 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -379,8 +379,8 @@ void plugin_hook_db_sync(struct db *db) db_data_version_get(db)); json_array_start(req->stream, "writes"); - for (size_t i = 0; i < tal_count(changes); i++) - json_add_string(req->stream, NULL, changes[i]); + for (size_t j = 0; j < tal_count(changes); j++) + json_add_string(req->stream, NULL, changes[j]); json_array_end(req->stream); jsonrpc_request_end(req); @@ -489,8 +489,7 @@ static struct plugin **plugin_hook_make_ordered(const tal_t *ctx, /* Add edges. */ for (size_t i = 0; i < tal_count(graph); i++) { for (size_t j = 0; j < tal_count(graph[i].hook->before); j++) { - struct hook_node *n = find_hook(graph, - graph[i].hook->before[j]); + n = find_hook(graph, graph[i].hook->before[j]); if (!n) { /* This is useful for typos! */ log_debug(graph[i].hook->plugin->log, @@ -503,8 +502,7 @@ static struct plugin **plugin_hook_make_ordered(const tal_t *ctx, n->num_incoming++; } for (size_t j = 0; j < tal_count(graph[i].hook->after); j++) { - struct hook_node *n = find_hook(graph, - graph[i].hook->after[j]); + n = find_hook(graph, graph[i].hook->after[j]); if (!n) { /* This is useful for typos! */ log_debug(graph[i].hook->plugin->log, diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 368e18ba31e2..b3ecab5a16bf 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -135,7 +135,6 @@ static void listnodes_done(const char *buffer, t = json_get_member(buffer, t, "nodes"); if (!deprecated_apis && (!t || t->size == 0)) { - struct json_stream *response; response = json_stream_fail(can->cmd, SIGNMESSAGE_PUBKEY_NOT_FOUND, "pubkey not found in the graph"); json_add_node_id(response, "claimed_key", &can->id); diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index b44716d7a239..14ab6effafe5 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -326,7 +326,7 @@ static void channel_hints_update(struct payment *p, u16 *htlc_budget) { struct payment *root = payment_root(p); - struct channel_hint hint; + struct channel_hint newhint; /* If the channel is marked as enabled it must have an estimate. */ assert(!enabled || estimated_capacity != NULL); @@ -372,25 +372,25 @@ static void channel_hints_update(struct payment *p, } /* No hint found, create one. */ - hint.enabled = enabled; - hint.scid.scid = scid; - hint.scid.dir = direction; - hint.local = local; + newhint.enabled = enabled; + newhint.scid.scid = scid; + newhint.scid.dir = direction; + newhint.local = local; if (estimated_capacity != NULL) - hint.estimated_capacity = *estimated_capacity; + newhint.estimated_capacity = *estimated_capacity; if (htlc_budget != NULL) - hint.htlc_budget = *htlc_budget; + newhint.htlc_budget = *htlc_budget; - tal_arr_expand(&root->channel_hints, hint); + tal_arr_expand(&root->channel_hints, newhint); paymod_log( p, LOG_DBG, "Added a channel hint for %s: enabled %s, estimated capacity %s", - type_to_string(tmpctx, struct short_channel_id_dir, &hint.scid), - hint.enabled ? "true" : "false", + type_to_string(tmpctx, struct short_channel_id_dir, &newhint.scid), + newhint.enabled ? "true" : "false", type_to_string(tmpctx, struct amount_msat, - &hint.estimated_capacity)); + &newhint.estimated_capacity)); } static void payment_exclude_most_expensive(struct payment *p) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 900b1cc2fe33..87bafe965e90 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1021,15 +1021,15 @@ static struct command_result *handle_init(struct command *cmd, opttok = json_get_member(buf, params, "options"); json_for_each_obj(i, t, opttok) { char *opt = json_strdup(NULL, buf, t); - for (size_t i = 0; i < tal_count(p->opts); i++) { + for (size_t optnum = 0; optnum < tal_count(p->opts); optnum++) { char *problem; - if (!streq(p->opts[i].name, opt)) + if (!streq(p->opts[optnum].name, opt)) continue; - problem = p->opts[i].handle(json_strdup(opt, buf, t+1), - p->opts[i].arg); + problem = p->opts[optnum].handle(json_strdup(opt, buf, t+1), + p->opts[optnum].arg); if (problem) plugin_err(p, "option '%s': %s", - p->opts[i].name, problem); + p->opts[optnum].name, problem); break; } tal_free(opt); diff --git a/plugins/pay.c b/plugins/pay.c index ed0994182a20..57a68ead80cd 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -506,7 +506,7 @@ static struct command_result *listsendpays_done(struct command *cmd, ret = jsonrpc_stream_success(cmd); json_array_start(ret, "pays"); - for (size_t i = 0; i < tal_count(order); i++) { + for (i = 0; i < tal_count(order); i++) { pm = pay_map_get(&pay_map, &order[i]); assert(pm != NULL); add_new_entry(ret, buf, pm); diff --git a/plugins/txprepare.c b/plugins/txprepare.c index 2a767d95a3ba..669daef98de1 100644 --- a/plugins/txprepare.c +++ b/plugins/txprepare.c @@ -177,7 +177,7 @@ static struct command_result *signpsbt_done(struct command *cmd, static struct command_result *finish_txprepare(struct command *cmd, struct txprepare *txp) { - struct json_stream *out; + struct json_stream *js; struct unreleased_tx *utx; /* Add the outputs they gave us */ @@ -225,11 +225,11 @@ static struct command_result *finish_txprepare(struct command *cmd, } list_add(&unreleased_txs, &utx->list); - out = jsonrpc_stream_success(cmd); - json_add_hex_talarr(out, "unsigned_tx", linearize_wtx(tmpctx, utx->tx)); - json_add_txid(out, "txid", &utx->txid); - json_add_psbt(out, "psbt", utx->psbt); - return command_finished(cmd, out); + js = jsonrpc_stream_success(cmd); + json_add_hex_talarr(js, "unsigned_tx", linearize_wtx(tmpctx, utx->tx)); + json_add_txid(js, "txid", &utx->txid); + json_add_psbt(js, "psbt", utx->psbt); + return command_finished(cmd, js); } /* newaddr has given us a change address. */ diff --git a/wallet/db.c b/wallet/db.c index 5ed908d3ed6c..41010d84d3cb 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -914,8 +914,7 @@ static bool db_migrate(struct lightningd *ld, struct db *db, while (current < available) { current++; if (dbmigrations[current].sql) { - struct db_stmt *stmt = - db_prepare_v2(db, dbmigrations[current].sql); + stmt = db_prepare_v2(db, dbmigrations[current].sql); db_exec_prepared_v2(stmt); tal_free(stmt); } From 44d9e8d9c5e738f99eba29d194e8972799c57e7e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 16:31:09 +0930 Subject: [PATCH 1286/1530] Remove names of parameters of callbacks which confuse gcc. We annotate them with UNNEEDED, which is legal but weird, but it makes gcc (at least 11.2.0) complain about shadowing: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106424 I simply removed the names. Signed-off-by: Rusty Russell --- lightningd/chaintopology.h | 8 ++++---- lightningd/test/run-invoice-select-inchan.c | 12 ++++++------ lightningd/watch.c | 8 ++++---- lightningd/watch.h | 6 +++--- wallet/test/run-wallet.c | 16 ++++++++-------- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index 65ad3086a64d..0e6a771f0d64 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -155,7 +155,7 @@ u32 penalty_feerate(struct chain_topology *topo); * If failed is non-NULL, call that and don't rebroadcast. */ void broadcast_tx(struct chain_topology *topo, struct channel *channel, const struct bitcoin_tx *tx, - void (*failed)(struct channel *channel, + void (*failed)(struct channel *, bool success, const char *err)); /* Like the above, but with an additional `allowhighfees` parameter. @@ -163,7 +163,7 @@ void broadcast_tx(struct chain_topology *topo, void broadcast_tx_ahf(struct chain_topology *topo, struct channel *channel, const struct bitcoin_tx *tx, bool allowhighfees, - void (*failed)(struct channel *channel, + void (*failed)(struct channel *, bool success, const char *err)); @@ -194,8 +194,8 @@ static inline bool topology_synced(const struct chain_topology *topo) */ void topology_add_sync_waiter_(const tal_t *ctx, struct chain_topology *topo, - void (*cb)(struct chain_topology *topo, - void *arg), + void (*cb)(struct chain_topology *, + void *), void *arg); #define topology_add_sync_waiter(ctx, topo, cb, arg) \ topology_add_sync_waiter_((ctx), (topo), \ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 52308bedf989..b3e7395a248b 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -16,9 +16,9 @@ struct channel *any_channel_by_scid(struct lightningd *ld UNNEEDED, /* Generated stub for bitcoind_getutxout_ */ void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, - void (*cb)(struct bitcoind *bitcoind UNNEEDED, - const struct bitcoin_tx_output *txout UNNEEDED, - void *arg) UNNEEDED, + void (*cb)(struct bitcoind * UNNEEDED, + const struct bitcoin_tx_output * UNNEEDED, + void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "bitcoind_getutxout_ called!\n"); abort(); } /* Generated stub for bolt11_decode */ @@ -50,7 +50,7 @@ char *bolt11_encode_(const tal_t *ctx UNNEEDED, /* Generated stub for broadcast_tx */ void broadcast_tx(struct chain_topology *topo UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, - void (*failed)(struct channel *channel UNNEEDED, + void (*failed)(struct channel * UNNEEDED, bool success UNNEEDED, const char *err)) { fprintf(stderr, "broadcast_tx called!\n"); abort(); } @@ -911,7 +911,7 @@ struct txwatch *watch_txid(const tal_t *ctx UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, enum watch_result (*cb)(struct lightningd *ld UNNEEDED, - struct channel *channel UNNEEDED, + struct channel * UNNEEDED, const struct bitcoin_txid * UNNEEDED, const struct bitcoin_tx * UNNEEDED, unsigned int depth)) @@ -921,7 +921,7 @@ struct txowatch *watch_txo(const tal_t *ctx UNNEEDED, struct chain_topology *topo UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, - enum watch_result (*cb)(struct channel *channel UNNEEDED, + enum watch_result (*cb)(struct channel * UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, size_t input_num UNNEEDED, const struct block *block)) diff --git a/lightningd/watch.c b/lightningd/watch.c index 48b5d7d834ea..05a54c65127a 100644 --- a/lightningd/watch.c +++ b/lightningd/watch.c @@ -123,9 +123,9 @@ struct txwatch *watch_txid(const tal_t *ctx, struct channel *channel, const struct bitcoin_txid *txid, enum watch_result (*cb)(struct lightningd *ld, - struct channel *channel, - const struct bitcoin_txid *txid, - const struct bitcoin_tx *tx, + struct channel *, + const struct bitcoin_txid *, + const struct bitcoin_tx *, unsigned int depth)) { struct txwatch *w; @@ -189,7 +189,7 @@ struct txowatch *watch_txo(const tal_t *ctx, struct chain_topology *topo, struct channel *channel, const struct bitcoin_outpoint *outpoint, - enum watch_result (*cb)(struct channel *channel, + enum watch_result (*cb)(struct channel *channel_, const struct bitcoin_tx *tx, size_t input_num, const struct block *block)) diff --git a/lightningd/watch.h b/lightningd/watch.h index 35d818e892c5..dbdd43d4ecac 100644 --- a/lightningd/watch.h +++ b/lightningd/watch.h @@ -35,7 +35,7 @@ struct txwatch *watch_txid(const tal_t *ctx, struct channel *channel, const struct bitcoin_txid *txid, enum watch_result (*cb)(struct lightningd *ld, - struct channel *channel, + struct channel *, const struct bitcoin_txid *, const struct bitcoin_tx *, unsigned int depth)); @@ -45,7 +45,7 @@ struct txwatch *watch_tx(const tal_t *ctx, struct channel *channel, const struct bitcoin_tx *tx, enum watch_result (*cb)(struct lightningd *ld, - struct channel *channel, + struct channel *, const struct bitcoin_txid *, const struct bitcoin_tx *, unsigned int depth)); @@ -54,7 +54,7 @@ struct txowatch *watch_txo(const tal_t *ctx, struct chain_topology *topo, struct channel *channel, const struct bitcoin_outpoint *outpoint, - enum watch_result (*cb)(struct channel *channel, + enum watch_result (*cb)(struct channel *, const struct bitcoin_tx *tx, size_t input_num, const struct block *block)); diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 0715963d658b..29593e16fe00 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -52,9 +52,9 @@ size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED) /* Generated stub for bitcoind_getutxout_ */ void bitcoind_getutxout_(struct bitcoind *bitcoind UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, - void (*cb)(struct bitcoind *bitcoind UNNEEDED, - const struct bitcoin_tx_output *txout UNNEEDED, - void *arg) UNNEEDED, + void (*cb)(struct bitcoind * UNNEEDED, + const struct bitcoin_tx_output * UNNEEDED, + void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "bitcoind_getutxout_ called!\n"); abort(); } /* Generated stub for blinding_hash_e_and_ss */ @@ -70,7 +70,7 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, /* Generated stub for broadcast_tx */ void broadcast_tx(struct chain_topology *topo UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, - void (*failed)(struct channel *channel UNNEEDED, + void (*failed)(struct channel * UNNEEDED, bool success UNNEEDED, const char *err)) { fprintf(stderr, "broadcast_tx called!\n"); abort(); } @@ -711,8 +711,8 @@ void subkey_from_hmac(const char *prefix UNNEEDED, /* Generated stub for topology_add_sync_waiter_ */ void topology_add_sync_waiter_(const tal_t *ctx UNNEEDED, struct chain_topology *topo UNNEEDED, - void (*cb)(struct chain_topology *topo UNNEEDED, - void *arg) UNNEEDED, + void (*cb)(struct chain_topology * UNNEEDED, + void *) UNNEEDED, void *arg UNNEEDED) { fprintf(stderr, "topology_add_sync_waiter_ called!\n"); abort(); } /* Generated stub for towire_channeld_config_channel */ @@ -847,7 +847,7 @@ struct txwatch *watch_txid(const tal_t *ctx UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, enum watch_result (*cb)(struct lightningd *ld UNNEEDED, - struct channel *channel UNNEEDED, + struct channel * UNNEEDED, const struct bitcoin_txid * UNNEEDED, const struct bitcoin_tx * UNNEEDED, unsigned int depth)) @@ -857,7 +857,7 @@ struct txowatch *watch_txo(const tal_t *ctx UNNEEDED, struct chain_topology *topo UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, - enum watch_result (*cb)(struct channel *channel UNNEEDED, + enum watch_result (*cb)(struct channel * UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, size_t input_num UNNEEDED, const struct block *block)) From 2c722291069dd5cf5a65288ed3131f96daed5b05 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 25 Jul 2022 16:35:05 +0930 Subject: [PATCH 1287/1530] configure: add -Wshadow=local flag. This will warn us if there are local variables which shadow the same-named variable. Signed-off-by: Rusty Russell --- configure | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/configure b/configure index 4f35f15537a4..bd7aae297f0d 100755 --- a/configure +++ b/configure @@ -62,6 +62,9 @@ default_cwarnflags() if [ -n "${1##*-O3*}" ] || [ "$3" != "1" ]; then F="$F -Wno-maybe-uninitialized" fi + # Recent clang understands that, but at least our MacOS CI doesn't, so keep + # this gcc-only. + F="$F -Wshadow=local" fi echo "$F" } @@ -223,6 +226,11 @@ for opt in "$@"; do if [ x"$COPTFLAGS" = x"$DEFAULT_COPTFLAGS" ]; then unset COPTFLAGS fi + # If they didn't have -Wshadow=local, add it on --reconfigure + # (we only added it later) + if [ x"${CWARNFLAGS%*-Wshadow=local}" = x"$CWARNFLAGS" ]; then + unset CWARNFLAGS + fi ;; CC=*) CC="${opt#CC=}";; CONFIGURATOR_CC=*) CONFIGURATOR_CC="${opt#CONFIGURATOR_CC=}";; From 0d5808b6f6ed23e724e8b1c283c557d69a4adaa6 Mon Sep 17 00:00:00 2001 From: Matt Morehouse Date: Wed, 17 Aug 2022 11:26:29 -0500 Subject: [PATCH 1288/1530] pytest: fix test_channel_state_change_history The test fails because the closed channel is deleted by the time we check the state change history. It is unnecessary to mine any blocks after the close, since the state changes up to CLOSINGD_COMPLETE occur without onchain changes. --- tests/test_plugin.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 8ff19370d291..55f39f9c6312 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1011,10 +1011,7 @@ def test_channel_state_change_history(node_factory, bitcoind): """ l1, l2 = node_factory.line_graph(2) scid = l1.get_channel_scid(l2) - l1.rpc.close(scid) - bitcoind.generate_block(100) # so it gets settled - bitcoind.generate_block(100) # so it gets settled history = l1.rpc.listpeers()['peers'][0]['channels'][0]['state_changes'] if l1.config('experimental-dual-fund'): From ea414320a3db540889eb9ec0854638a685850ad2 Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 31 Aug 2022 07:06:40 -0500 Subject: [PATCH 1289/1530] build, shadows: fix broken build (no shadows) Merged a commit that broke everything with shadows, which turned out to be the bookkeeper code. --- plugins/bkpr/bookkeeper.c | 6 +++--- plugins/bkpr/db.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index eddff8b18586..bece4a959796 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1063,7 +1063,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, || !amount_msat_zero(debit_diff)) { struct account *acct; struct channel_event *ev; - u64 timestamp; + u64 timestamp_now; /* This is *expected* on first run of bookkeeper! */ plugin_log(cmd->plugin, @@ -1079,7 +1079,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, &credit_diff), acct_name); - timestamp = time_now().ts.tv_sec; + timestamp_now = time_now().ts.tv_sec; /* Log a channel "journal entry" to get * the balances inline */ @@ -1103,7 +1103,7 @@ static struct command_result *json_balance_snapshot(struct command *cmd, info = tal(new_accts, struct new_account_info); info->acct = tal_steal(info, acct); info->curr_bal = snap_balance; - info->timestamp = timestamp; + info->timestamp = timestamp_now; info->currency = tal_strdup(info, currency); diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index c47dfe866644..b27e217e8d39 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -126,8 +126,7 @@ static bool db_migrate(struct plugin *p, struct db *db, bool *created) while (current < available) { current++; if (db_migrations[current].sql) { - struct db_stmt *stmt = - db_prepare_v2(db, db_migrations[current].sql); + stmt = db_prepare_v2(db, db_migrations[current].sql); db_exec_prepared_v2(take(stmt)); } if (db_migrations[current].func) From c4203e7de6f35ed1cd1658d7c898b8b6bfaac2df Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 09:31:50 +0930 Subject: [PATCH 1290/1530] pyln-client: allow 'msat' fields to be 'null' This happens with deprecated-apis and listconfigs, breaking some python plugins! Fixes: #5546 Fixes: #5563 Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 5 +++ tests/test_misc.py | 42 ++++++++++---------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 38fc7563f4ac..0013b89a3347 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -455,6 +455,11 @@ def replace_amounts(obj): if k.endswith('msat'): if isinstance(v, list): obj[k] = [Millisatoshi(e) for e in v] + # FIXME: Deprecated "listconfigs" gives two 'null' fields: + # "lease-fee-base-msat": null, + # "channel-fee-max-base-msat": null, + elif v is None: + obj[k] = None else: obj[k] = Millisatoshi(v) else: diff --git a/tests/test_misc.py b/tests/test_misc.py index b63f84c7414e..9e656a0166a3 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -725,29 +725,29 @@ def test_address(node_factory): l2.rpc.connect(l1.info['id'], l1.daemon.opts['addr']) -@unittest.skipIf(DEPRECATED_APIS, "Tests the --allow-deprecated-apis config") def test_listconfigs(node_factory, bitcoind, chainparams): # Make extremely long entry, check it works - l1 = node_factory.get_node(options={'log-prefix': 'lightning1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}) - - configs = l1.rpc.listconfigs() - # See utils.py - assert configs['allow-deprecated-apis'] is False - assert configs['network'] == chainparams['name'] - assert configs['ignore-fee-limits'] is False - assert configs['ignore-fee-limits'] is False - assert configs['log-prefix'] == 'lightning1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...' - - # These are aliases, but we don't print the (unofficial!) wumbo. - assert 'wumbo' not in configs - assert configs['large-channels'] is False - - # Test one at a time. - for c in configs.keys(): - if c.startswith('#') or c.startswith('plugins') or c == 'important-plugins': - continue - oneconfig = l1.rpc.listconfigs(config=c) - assert(oneconfig[c] == configs[c]) + for deprecated in (True, False): + l1 = node_factory.get_node(options={'log-prefix': 'lightning1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + 'allow-deprecated-apis': deprecated}) + + configs = l1.rpc.listconfigs() + # See utils.py + assert configs['allow-deprecated-apis'] == deprecated + assert configs['network'] == chainparams['name'] + assert configs['ignore-fee-limits'] is False + assert configs['log-prefix'] == 'lightning1-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...' + + # These are aliases, but we don't print the (unofficial!) wumbo. + assert 'wumbo' not in configs + assert configs['large-channels'] is False + + # Test one at a time. + for c in configs.keys(): + if c.startswith('#') or c.startswith('plugins') or c == 'important-plugins': + continue + oneconfig = l1.rpc.listconfigs(config=c) + assert(oneconfig[c] == configs[c]) def test_listconfigs_plugins(node_factory, bitcoind, chainparams): From dc56b2a9ac05ccf810ec27b31ba4f87bd0ee32cb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 5 Sep 2022 16:10:33 +0930 Subject: [PATCH 1291/1530] connectd: better diagnostics on invalid gossip_store entries. Should help diagnose https://github.com/ElementsProject/lightning/issues/5572 which hit the invalid csum on a >64MB entry, if it happens again. Signed-off-by: Rusty Russell --- common/gossip_store.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index 2522904e3a81..086232c2953f 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -115,6 +115,7 @@ u8 *gossip_store_next(const tal_t *ctx, size_t *off, size_t *end) { u8 *msg = NULL; + size_t initial_off = *off; while (!msg) { struct gossip_hdr hdr; @@ -146,6 +147,14 @@ u8 *gossip_store_next(const tal_t *ctx, continue; } + /* Messages can be up to 64k, but we also have internal ones: + * 128k is plenty. */ + if (msglen > 128 * 1024) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "gossip_store: oversize msg len %u at" + " offset %zu (was at %zu)", + msglen, *off, initial_off); + checksum = be32_to_cpu(hdr.crc); msg = tal_arr(ctx, u8, msglen); r = pread(*gossip_store_fd, msg, msglen, *off + r); @@ -155,8 +164,8 @@ u8 *gossip_store_next(const tal_t *ctx, if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) status_failed(STATUS_FAIL_INTERNAL_ERROR, "gossip_store: bad checksum at offset %zu" - ": %s", - *off, tal_hex(tmpctx, msg)); + "(was at %zu): %s", + *off, initial_off, tal_hex(tmpctx, msg)); /* Definitely processing it now */ *off += sizeof(hdr) + msglen; From 8e57bf379661648bdcaf3d42b17986393734e14e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Sep 2022 04:11:41 +0930 Subject: [PATCH 1292/1530] tools: add md2man.sh tool, using lowdown. Signed-off-by: Rusty Russell --- tools/md2man.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100755 tools/md2man.sh diff --git a/tools/md2man.sh b/tools/md2man.sh new file mode 100755 index 000000000000..7dea4cb70b1f --- /dev/null +++ b/tools/md2man.sh @@ -0,0 +1,15 @@ +#! /bin/sh + +if [ $# != 1 ]; then + echo "Usage: $0 " >&2 + exit 1 +fi +SOURCE=$1 +SECTION="$(basename "$SOURCE" .md | cut -d. -f2-)" +TITLE="$(basename "$(basename "$SOURCE" .md)" ."$SECTION" | tr '[:lower:]' '[:upper:]')" + +# First two lines are title, which needs to be turned into NAME for proper manpage +# format. mrkd used to do this for us, lowdown(1) doesn't. +TITLELINE="$(head -n1 "$SOURCE")" + +(echo "NAME"; echo "----"; echo "$TITLELINE"; tail -n +3 "$SOURCE") | lowdown -s --out-no-smarty -Tman -m "title:$TITLE" -m "section:$SECTION" -m "source:Core Lightning $VERSION" -m "shiftheadinglevelby:-1" From 2e48722f378657332691e33fc3fe868ad19dc57c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 1 Sep 2022 04:12:41 +0930 Subject: [PATCH 1293/1530] Makefile: replace mrkd with lowdown(1). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Here's the before-vs-after comparison (ignoring whitespace changes): ```diff --- /tmp/before 2022-07-20 21:52:44.336641810 +0930 +++ /tmp/after 2022-07-20 21:55:54.355487769 +0930 @@ -1,7 +1,7 @@ -LIGHTNING-CLI(1) lightning-cli LIGHTNING-CLI(1) +LIGHTNING-CLI(1) LIGHTNING-CLI(1) NAME - lightning-cli - Control lightning daemon + lightning-cli -- Control lightning daemon SYNOPSIS lightning-cli [OPTIONS] command @@ -14,10 +14,7 @@ --conf=PATH Sets configuration file (default: lightning-dir/config ). - --network=network - --mainnet - --testnet - --signet Sets network explicitly. + --network=network --mainnet --testnet --signet Sets network explicitly. --rpc-file=FILE Named pipe to use to talk to lightning daemon: default is lightning-rpc in the lightning directory. @@ -43,8 +40,8 @@ --version/-V Print version number to standard output and exit. - allow-deprecated-apis=BOOL Enable deprecated options. It defaults to true, but you should set it to false when testing - to ensure that an upgrade won't break your configuration. + allow-deprecated-apis=BOOL Enable deprecated options. It defaults to true, but you should set it to false when testing to + ensure that an upgrade won't break your configuration. COMMANDS lightning-cli simply uses the JSON RPC interface to talk to lightningd, and prints the results. Thus the commands avail‐ @@ -67,7 +64,7 @@ lightning-cli help - 1. Fund a 10k sat channel using uncomfirmed outputs + 2. Fund a 10k sat channel using uncomfirmed outputs lightning-cli --keywords fundchannel id=028f...ae7d amount=10000sat minconf=0 @@ -84,4 +81,4 @@ Note: the modules in the ccan/ directory have their own licenses, but the rest of the code is covered by the BSD-style MIT license. - LIGHTNING-CLI(1) +Core Lightning v0.11.0.1-350-gac2e137 LIGHTNING-CLI(1) ``` Signed-off-by: Rusty Russell Fixes: #5437 --- doc/Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/Makefile b/doc/Makefile index 743bf05c4bf2..959ca52e31bb 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -125,10 +125,8 @@ 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): doc/%: doc/%.md tools/md2man.sh version_gen.h + @if $(call SHA256STAMP_CHANGED_ALL); then $(call VERBOSE, "md2man $<", VERSION=$(VERSION) tools/md2man.sh $< > $@ && $(call SHA256STAMP_ALL,\\\",)); else touch $@; fi $(MANPAGES): $(FORCE) $(MARKDOWN_WITH_SCHEMA): $(FORCE) From 50056ce9182d4c8865395e5a50e294c59124be6b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 06:57:55 +0930 Subject: [PATCH 1294/1530] doc: remove mrkd requirement, add lowdown requirement. I guessed it's called "lowdown" for everyone? Signed-off-by: Rusty Russell --- .github/workflows/bsd.yml | 2 +- contrib/docker/Dockerfile.alpine | 3 +-- contrib/docker/Dockerfile.builder | 4 ++-- contrib/docker/Dockerfile.builder.fedora | 2 +- contrib/docker/linuxarm32v7.Dockerfile | 3 +-- contrib/docker/linuxarm64v8.Dockerfile | 3 +-- doc/INSTALL.md | 10 ++-------- pyproject.toml | 1 - 8 files changed, 9 insertions(+), 19 deletions(-) diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 603ad8c0a954..dbc59fe085e1 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -39,6 +39,7 @@ jobs: bash \ gettext \ sqlite3 \ + lowdown \ curl curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain nightly-2021-08-3z1 @@ -63,7 +64,6 @@ jobs: blinker \ flake8 \ mako \ - mrkd \ pytest-sentry \ pytest-test-groups==1.0.3 \ pytest-custom-exit-code==0.3.0 \ diff --git a/contrib/docker/Dockerfile.alpine b/contrib/docker/Dockerfile.alpine index d1ab045f9f05..ceb873823eae 100644 --- a/contrib/docker/Dockerfile.alpine +++ b/contrib/docker/Dockerfile.alpine @@ -6,7 +6,7 @@ WORKDIR /build RUN apk update && \ apk add ca-certificates alpine-sdk autoconf automake git libtool \ gmp-dev sqlite-dev python3 py3-mako net-tools zlib-dev libsodium gettext su-exec \ - python3 py3-pip #&& \ + python3 py3-pip lowdown #&& \ #apk add --upgrade fortify-headers RUN mkdir lightning @@ -15,7 +15,6 @@ COPY . lightning RUN cd lightning && \ git submodule update --init --recursive && \ ./configure && \ - pip3 install mrkd mistune==0.8.4 && \ make -j$(nproc) && \ make install diff --git a/contrib/docker/Dockerfile.builder b/contrib/docker/Dockerfile.builder index ff4d15fed2b0..26e3be204baa 100644 --- a/contrib/docker/Dockerfile.builder +++ b/contrib/docker/Dockerfile.builder @@ -33,6 +33,7 @@ RUN apt-get -qq update && \ python-pkg-resources \ shellcheck \ libxml2-utils \ + lowdown \ wget \ gettext \ xsltproc \ @@ -65,5 +66,4 @@ RUN pip3 install --upgrade pip && \ tqdm==4.26.0 \ pytest-test-groups==1.0.3 \ flake8==3.5.0 \ - pytest-rerunfailures==3.1 \ - mrkd==0.1.6 + pytest-rerunfailures==3.1 diff --git a/contrib/docker/Dockerfile.builder.fedora b/contrib/docker/Dockerfile.builder.fedora index 897e4d0c3949..4a43c033e2e8 100644 --- a/contrib/docker/Dockerfile.builder.fedora +++ b/contrib/docker/Dockerfile.builder.fedora @@ -32,4 +32,4 @@ RUN wget https://bitcoin.org/bin/bitcoin-core-$BITCOIN_VERSION/bitcoin-$BITCOIN_ rm -rf bitcoin.tar.gz bitcoin-$BITCOIN_VERSION RUN python3 -m pip install --upgrade pip && \ - python3 -m pip install python-bitcoinlib==0.10.2 pytest==3.0.5 setuptools==36.6.0 pytest-test-groups==1.0.3 flake8==3.5.0 pytest-rerunfailures==3.1 ephemeral-port-reserve==1.1.0 mrkd==0.1.6 + python3 -m pip install python-bitcoinlib==0.10.2 pytest==3.0.5 setuptools==36.6.0 pytest-test-groups==1.0.3 flake8==3.5.0 pytest-rerunfailures==3.1 ephemeral-port-reserve==1.1.0 diff --git a/contrib/docker/linuxarm32v7.Dockerfile b/contrib/docker/linuxarm32v7.Dockerfile index ffcfb490792c..acf2fa1784f8 100644 --- a/contrib/docker/linuxarm32v7.Dockerfile +++ b/contrib/docker/linuxarm32v7.Dockerfile @@ -48,7 +48,7 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:buster-slim as builder ENV LIGHTNINGD_VERSION=master -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git \ +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git lowdown \ libc6-armhf-cross gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf ENV target_host=arm-linux-gnueabihf @@ -92,7 +92,6 @@ RUN git clone --recursive /tmp/lightning . && \ ARG DEVELOPER=0 ENV PYTHON_VERSION=3 -RUN pip3 install mrkd RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install FROM arm32v7/debian:buster-slim as final diff --git a/contrib/docker/linuxarm64v8.Dockerfile b/contrib/docker/linuxarm64v8.Dockerfile index 3e8fe5d3f982..82741674e932 100644 --- a/contrib/docker/linuxarm64v8.Dockerfile +++ b/contrib/docker/linuxarm64v8.Dockerfile @@ -48,7 +48,7 @@ RUN mkdir /opt/litecoin && cd /opt/litecoin \ FROM debian:buster-slim as builder ENV LIGHTNINGD_VERSION=master -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git \ +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates autoconf automake build-essential gettext git libtool python3 python3-pip python3-setuptools python3-mako wget gnupg dirmngr git lowdown \ libc6-arm64-cross gcc-aarch64-linux-gnu g++-aarch64-linux-gnu ENV target_host=aarch64-linux-gnu @@ -91,7 +91,6 @@ RUN git clone --recursive /tmp/lightning . && \ ARG DEVELOPER=0 ENV PYTHON_VERSION=3 -RUN pip3 install mrkd RUN ./configure --prefix=/tmp/lightning_install --enable-static && make -j3 DEVELOPER=${DEVELOPER} && make install FROM arm64v8/debian:buster-slim as final diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 768d7073d71d..f53017d5ecc9 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -64,7 +64,7 @@ Checkout a release tag: For development or running tests, get additional dependencies: sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ - libsecp256k1-dev jq + libsecp256k1-dev jq lowdown If you want to build the Rust plugins (currently, cln-grpc): @@ -174,12 +174,6 @@ fiddle with compile time options: # cd /usr/ports/net-p2p/c-lightning && make install -mrkd is required to build man pages from markdown files (not done by the port): - - # cd /usr/ports/devel/py-pip && make install - $ pip install --user poetry - $ poetry install - See `/usr/ports/net-p2p/c-lightning/Makefile` for instructions on how to build from an arbitrary git commit, instead of the latest release tag. @@ -209,7 +203,7 @@ pkg_add git python gmake py3-pip libtool gmp pkg_add automake # (select highest version, automake1.16.2 at time of writing) pkg_add autoconf # (select highest version, autoconf-2.69p2 at time of writing) ``` -Install `mako` and `mrkd` otherwise we run into build errors: +Install `mako` otherwise we run into build errors: ``` pip3.7 install --user poetry poetry install diff --git a/pyproject.toml b/pyproject.toml index da609986289c..ac70997c146b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,6 @@ python = "^3.7" pyln-client = { path = "./contrib/pyln-client", develop = true } pyln-proto = { path = "./contrib/pyln-proto", develop = true } Mako = "^1.1.6" -mrkd = { git = "https://github.com/refi64/mrkd.git", rev = "781f05eb9898ca652f18eed29b3c956389e6a2a7" } websocket-client = "^1.2.3" grpcio-tools = "^1.44.0" From 3c3f4731bd31624250464a5ce0819a53cb3c9495 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 07:02:09 +0930 Subject: [PATCH 1295/1530] doc: format markdown correctly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no definition lists in Markdown, and lists get mangled if they follow immediately: they need a line between them. So use bullets for options, and use an indent so the text gets in the line below. Here's a before-and-after example: ```diff --- /tmp/after 2022-07-20 21:55:54.355487769 +0930 +++ /tmp/after2 2022-07-20 21:58:17.305642576 +0930 @@ -10,38 +10,71 @@ lightning-cli sends commands to the lightning daemon. OPTIONS - --lightning-dir=DIR Set the directory for the lightning daemon we're talking to; defaults to $HOME/.lightning. + • --lightning-dir=DIR - --conf=PATH Sets configuration file (default: lightning-dir/config ). + Set the directory for the lightning daemon we're talking to; defaults to $HOME/.lightning. - --network=network --mainnet --testnet --signet Sets network explicitly. + • --conf=PATH - --rpc-file=FILE Named pipe to use to talk to lightning daemon: default is lightning-rpc in the lightning directory. + Sets configuration file (default: lightning-dir/config ). - --keywords/-k Use format key=value for parameters in any order + • --network=network - --order/-o Follow strictly the order of parameters for the command + • --mainnet - --json/-J Return result in JSON format (default unless help command, or result contains a format-hint field). + • --testnet - --raw/-R Return raw JSON directly as lightningd replies; this can be faster for large requests. + • --signet - --human-readable/-H Return result in human-readable output. + Sets network explicitly. - --flat/-F Return JSON result in flattened one-per-line output, e.g. { "help": [ { "command": "check" } ] } would become + • --rpc-file=FILE + + Named pipe to use to talk to lightning daemon: default is lightning-rpc in the lightning directory. + + • --keywords/-k + + Use format key=value for parameters in any order + + • --order/-o + + Follow strictly the order of parameters for the command + + • --json/-J + + Return result in JSON format (default unless help command, or result contains a format-hint field). + + • --raw/-R + + Return raw JSON directly as lightningd replies; this can be faster for large requests. + + • --human-readable/-H + + Return result in human-readable output. + + • --flat/-F + + Return JSON result in flattened one-per-line output, e.g. { "help": [ { "command": "check" } ] } would become help[0].command=check. This is useful for simple scripts which want to find a specific output field without parsing JSON. - --notifications/-N=LEVEL If LEVEL is 'none', then never print out notifications. Otherwise, print out notifications of - LEVEL or above (one of io, debug, info (the default), unusual or broken: they are prefixed with # . + • --notifications/-N=LEVEL + + If LEVEL is 'none', then never print out notifications. Otherwise, print out notifications of LEVEL or above (one of + io, debug, info (the default), unusual or broken: they are prefixed with # . + + • --help/-h + + Pretty-print summary of options to standard output and exit. The format can be changed using -F, -R, -J, -H etc. + + • --version/-V - --help/-h Pretty-print summary of options to standard output and exit. The format can be changed using -F, -R, -J, -H - etc. + Print version number to standard output and exit. - --version/-V Print version number to standard output and exit. + • allow-deprecated-apis=BOOL - allow-deprecated-apis=BOOL Enable deprecated options. It defaults to true, but you should set it to false when testing to - ensure that an upgrade won't break your configuration. + Enable deprecated options. It defaults to true, but you should set it to false when testing to ensure that an upgrade + won't break your configuration. COMMANDS lightning-cli simply uses the JSON RPC interface to talk to lightningd, and prints the results. Thus the commands avail‐ @@ -60,13 +93,13 @@ this is not encouraged. EXAMPLES - 1. List commands + 1. List commands: - lightning-cli help + • lightning-cli help - 2. Fund a 10k sat channel using uncomfirmed outputs + 2. Fund a 10k sat channel using uncomfirmed outputs: - lightning-cli --keywords fundchannel id=028f...ae7d amount=10000sat minconf=0 + • lightning-cli --keywords fundchannel id=028f...ae7d amount=10000sat minconf=0 BUGS This manpage documents how it should work, not how it does work. The pretty printing of results isn't pretty. ``` Signed-off-by: Rusty Russell --- doc/lightning-cli.1.md | 86 ++++--- doc/lightning-hsmtool.8.md | 28 ++- doc/lightningd-config.5.md | 463 +++++++++++++++++++++---------------- doc/lightningd.8.md | 40 ++-- tools/check-manpage.sh | 2 +- 5 files changed, 353 insertions(+), 266 deletions(-) diff --git a/doc/lightning-cli.1.md b/doc/lightning-cli.1.md index 263340bbe4f6..8981c238a81e 100644 --- a/doc/lightning-cli.1.md +++ b/doc/lightning-cli.1.md @@ -14,61 +14,75 @@ DESCRIPTION OPTIONS ------- - **--lightning-dir**=*DIR* -Set the directory for the lightning daemon we're talking to; defaults to +* **--lightning-dir**=*DIR* + + Set the directory for the lightning daemon we're talking to; defaults to *$HOME/.lightning*. - **--conf**=*PATH* -Sets configuration file (default: **lightning-dir**/*config* ). +* **--conf**=*PATH* + + Sets configuration file (default: **lightning-dir**/*config* ). + +* **--network**=*network* +* **--mainnet** +* **--testnet** +* **--signet** + + Sets network explicitly. - **--network**=*network* - **--mainnet** - **--testnet** - **--signet** -Sets network explicitly. +* **--rpc-file**=*FILE* - **--rpc-file**=*FILE* -Named pipe to use to talk to lightning daemon: default is + Named pipe to use to talk to lightning daemon: default is *lightning-rpc* in the lightning directory. - **--keywords**/**-k** -Use format *key*=*value* for parameters in any order +* **--keywords**/**-k** + + Use format *key*=*value* for parameters in any order + +* **--order**/**-o** + + Follow strictly the order of parameters for the command - **--order**/**-o** -Follow strictly the order of parameters for the command +* **--json**/**-J** - **--json**/**-J** -Return result in JSON format (default unless *help* command, + Return result in JSON format (default unless *help* command, or result contains a `format-hint` field). - **--raw**/**-R** -Return raw JSON directly as lightningd replies; this can be faster for +* **--raw**/**-R** + + Return raw JSON directly as lightningd replies; this can be faster for large requests. - **--human-readable**/**-H** -Return result in human-readable output. +* **--human-readable**/**-H** + + Return result in human-readable output. - **--flat**/**-F** -Return JSON result in flattened one-per-line output, e.g. `{ "help": +* **--flat**/**-F** + + Return JSON result in flattened one-per-line output, e.g. `{ "help": [ { "command": "check" } ] }` would become `help[0].command=check`. This is useful for simple scripts which want to find a specific output field without parsing JSON. - **--notifications**/**-N**=*LEVEL* -If *LEVEL* is 'none', then never print out notifications. Otherwise, +* **--notifications**/**-N**=*LEVEL* + + If *LEVEL* is 'none', then never print out notifications. Otherwise, print out notifications of *LEVEL* or above (one of `io`, `debug`, `info` (the default), `unusual` or `broken`: they are prefixed with `# `. - **--help**/**-h** -Pretty-print summary of options to standard output and exit. The format can -be changed using -F, -R, -J, -H etc. +* **--help**/**-h** + + Pretty-print summary of options to standard output and exit. The format can +be changed using `-F`, `-R`, `-J`, `-H` etc. + +* **--version**/**-V** + + Print version number to standard output and exit. - **--version**/**-V** -Print version number to standard output and exit. +* **allow-deprecated-apis**=*BOOL* - **allow-deprecated-apis**=*BOOL* -Enable deprecated options. It defaults to *true*, but you should set + Enable deprecated options. It defaults to *true*, but you should set it to *false* when testing to ensure that an upgrade won't break your configuration. @@ -98,13 +112,13 @@ optional arguments to provide later arguments, although this is not encouraged. EXAMPLES -------- -1. List commands +1. List commands: -lightning-cli help + * `lightning-cli help` -2. Fund a 10k sat channel using uncomfirmed outputs +2. Fund a 10k sat channel using uncomfirmed outputs: -lightning-cli --keywords fundchannel id=028f...ae7d amount=10000sat minconf=0 + * `lightning-cli --keywords fundchannel id=028f...ae7d amount=10000sat minconf=0` BUGS ---- diff --git a/doc/lightning-hsmtool.8.md b/doc/lightning-hsmtool.8.md index 02ed83e82bff..22463c06f99f 100644 --- a/doc/lightning-hsmtool.8.md +++ b/doc/lightning-hsmtool.8.md @@ -19,25 +19,29 @@ as well as derive secrets used in channel commitments. METHODS ------- - **encrypt** *hsm\_secret* *password* -Encrypt the `hsm_secret` file so that it can only be decrypted at +**encrypt** *hsm\_secret* *password* + + Encrypt the `hsm_secret` file so that it can only be decrypted at **lightningd** startup. You must give the option **--encrypted-hsm** to **lightningd**. The password of the `hsm_secret` file will be asked whenever you start **lightningd**. - **decrypt** *hsm\_secret* *password* -Decrypt the `hsm_secret` file that was encrypted with the **encrypt** +**decrypt** *hsm\_secret* *password* + + Decrypt the `hsm_secret` file that was encrypted with the **encrypt** method. - **dumpcommitments** *node\_id* *channel\_dbid* *depth* *hsm\_secret* \[*password*\] -Show the per-commitment secret and point of up to *depth* commitments, +**dumpcommitments** *node\_id* *channel\_dbid* *depth* *hsm\_secret* \[*password*\] + + Show the per-commitment secret and point of up to *depth* commitments, of the specified channel with the specified peer, identified by the channel database index. Specify *password* if the `hsm_secret` is encrypted. - **guesstoremote** *p2wpkh* *node\_id* *max\_channel\_dbid* *hsm\_secret* \[*password*\] -Brute-force the private key to our funds from a remote unilateral close +**guesstoremote** *p2wpkh* *node\_id* *max\_channel\_dbid* *hsm\_secret* \[*password*\] + + Brute-force the private key to our funds from a remote unilateral close of a channel, in a case where we have lost all database data except for our `hsm_secret`. The peer must be the one to close the channel (and the funds will remain @@ -49,13 +53,13 @@ ever had. Specify *password* if the `hsm_secret` is encrypted. **generatehsm** *hsm\_secret\_path* -Generates a new hsm_secret using BIP39. + Generates a new hsm_secret using BIP39. **checkhsm** *hsm\_secret\_path* -Checks that hsm_secret matchs a BIP39 pass phrase. + Checks that hsm_secret matchs a BIP39 pass phrase. - **dumponchaindescriptors** *hsm_secret* \[*password*\] \[*network*\] -Dump output descriptors for our onchain wallet. +**dumponchaindescriptors** *hsm_secret* \[*password*\] \[*network*\] + Dump output descriptors for our onchain wallet. The descriptors can be used by external services to be able to generate addresses for our onchain wallet. (for example on `bitcoind` using the `importmulti` or `importdescriptors` RPC calls) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index d038754b31a0..a1cd54a8e886 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -19,14 +19,14 @@ ones, then command line options: later options override earlier ones except *addr* options and *log-level* with subsystems, which accumulate. -*include * followed by a filename includes another configuration file at that +`include` followed by a filename includes another configuration file at that point, relative to the current configuration file. All these options are mirrored as commandline arguments to -lightningd(8), so *--foo* becomes simply *foo* in the configuration -file, and *--foo=bar* becomes *foo=bar* in the configuration file. +lightningd(8), so `--foo` becomes simply `foo` in the configuration +file, and `--foo=bar` becomes `foo=bar` in the configuration file. -Blank lines and lines beginning with *\#* are ignored. +Blank lines and lines beginning with `#` are ignored. DEBUGGING --------- @@ -43,231 +43,268 @@ OPTIONS ### General options - **allow-deprecated-apis**=*BOOL* -Enable deprecated options, JSONRPC commands, fields, etc. It defaults to +* **allow-deprecated-apis**=*BOOL* + + Enable deprecated options, JSONRPC commands, fields, etc. It defaults to *true*, but you should set it to *false* when testing to ensure that an upgrade won't break your configuration. - **help** -Print help and exit. Not very useful inside a configuration file, but +* **help** + + Print help and exit. Not very useful inside a configuration file, but fun to put in other's config files while their computer is unattended. - **version** -Print version and exit. Also useless inside a configuration file, but +* **version** + + Print version and exit. Also useless inside a configuration file, but putting this in someone's config file may convince them to read this man page. -Bitcoin control options: +### Bitcoin control options: - **network**=*NETWORK* -Select the network parameters (*bitcoin*, *testnet*, *signet*, or *regtest*). +* **network**=*NETWORK* + + Select the network parameters (*bitcoin*, *testnet*, *signet*, or *regtest*). This is not valid within the per-network configuration file. - **mainnet** -Alias for *network=bitcoin*. +* **mainnet** + + Alias for *network=bitcoin*. + +* **testnet** + + Alias for *network=testnet*. + +* **signet** + + Alias for *network=signet*. + +* **bitcoin-cli**=*PATH* [plugin `bcli`] - **testnet** -Alias for *network=testnet*. + The name of *bitcoin-cli* executable to run. - **signet** -Alias for *network=signet*. +* **bitcoin-datadir**=*DIR* [plugin `bcli`] - **bitcoin-cli**=*PATH* [plugin `bcli`] -The name of *bitcoin-cli* executable to run. + *-datadir* argument to supply to bitcoin-cli(1). - **bitcoin-datadir**=*DIR* [plugin `bcli`] -*-datadir* argument to supply to bitcoin-cli(1). +* **bitcoin-rpcuser**=*USER* [plugin `bcli`] - **bitcoin-rpcuser**=*USER* [plugin `bcli`] -The RPC username for talking to bitcoind(1). + The RPC username for talking to bitcoind(1). - **bitcoin-rpcpassword**=*PASSWORD* [plugin `bcli`] -The RPC password for talking to bitcoind(1). +* **bitcoin-rpcpassword**=*PASSWORD* [plugin `bcli`] - **bitcoin-rpcconnect**=*HOST* [plugin `bcli`] -The bitcoind(1) RPC host to connect to. + The RPC password for talking to bitcoind(1). - **bitcoin-rpcport**=*PORT* [plugin `bcli`] -The bitcoind(1) RPC port to connect to. +* **bitcoin-rpcconnect**=*HOST* [plugin `bcli`] - **bitcoin-retry-timeout**=*SECONDS* [plugin `bcli`] -Number of seconds to keep trying a bitcoin-cli(1) command. If the + The bitcoind(1) RPC host to connect to. + +* **bitcoin-rpcport**=*PORT* [plugin `bcli`] + + The bitcoind(1) RPC port to connect to. + +* **bitcoin-retry-timeout**=*SECONDS* [plugin `bcli`] + + Number of seconds to keep trying a bitcoin-cli(1) command. If the command keeps failing after this time, exit with a fatal error. - **rescan**=*BLOCKS* -Number of blocks to rescan from the current head, or absolute +* **rescan**=*BLOCKS* + + Number of blocks to rescan from the current head, or absolute blockheight if negative. This is only needed if something goes badly wrong. ### Lightning daemon options - **lightning-dir**=*DIR* -Sets the working directory. All files (except *--conf* and +* **lightning-dir**=*DIR* + + Sets the working directory. All files (except *--conf* and *--lightning-dir* on the command line) are relative to this. This is only valid on the command-line, or in a configuration file specified by *--conf*. - **subdaemon**=*SUBDAEMON*:*PATH* -Specifies an alternate subdaemon binary. +* **subdaemon**=*SUBDAEMON*:*PATH* + + Specifies an alternate subdaemon binary. Current subdaemons are *channeld*, *closingd*, *connectd*, *gossipd*, *hsmd*, *onchaind*, and *openingd*. If the supplied path is relative the subdaemon binary is found in the working directory. This option may be specified multiple times. - So, **subdaemon=hsmd:remote_signer** would use a + So, **subdaemon=hsmd:remote_signer** would use a hypothetical remote signing proxy instead of the standard *lightning_hsmd* binary. - **pid-file**=*PATH* -Specify pid file to write to. +* **pid-file**=*PATH* + + Specify pid file to write to. - **log-level**=*LEVEL*\[:*SUBSYSTEM*\] -What log level to print out: options are io, debug, info, unusual, +* **log-level**=*LEVEL*\[:*SUBSYSTEM*\] + + What log level to print out: options are io, debug, info, unusual, broken. If *SUBSYSTEM* is supplied, this sets the logging level for any subsystem (or *nodeid*) containing that string. This option may be specified multiple times. Subsystems include: -* *lightningd*: The main lightning daemon -* *database*: The database subsystem -* *wallet*: The wallet subsystem -* *gossipd*: The gossip daemon -* *plugin-manager*: The plugin subsystem -* *plugin-P*: Each plugin, P = plugin path without directory -* *hsmd*: The secret-holding daemon -* *connectd*: The network connection daemon -* *jsonrpc#FD*: Each JSONRPC connection, FD = file descriptor number + * *lightningd*: The main lightning daemon + + * *database*: The database subsystem + * *wallet*: The wallet subsystem - The following subsystems exist for each channel, where N is an incrementing -internal integer id assigned for the lifetime of the channel: -* *openingd-chan#N*: Each opening / idling daemon -* *channeld-chan#N*: Each channel management daemon -* *closingd-chan#N*: Each closing negotiation daemon -* *onchaind-chan#N*: Each onchain close handling daemon + * *gossipd*: The gossip daemon + * *plugin-manager*: The plugin subsystem + * *plugin-P*: Each plugin, P = plugin path without directory + + * *hsmd*: The secret-holding daemon + + * *connectd*: The network connection daemon + + * *jsonrpc#FD*: Each JSONRPC connection, FD = file descriptor number + + + The following subsystems exist for each channel, where N is an incrementing internal integer id assigned for the lifetime of the channel: + + * *openingd-chan#N*: Each opening / idling daemon + + * *channeld-chan#N*: Each channel management daemon + + * *closingd-chan#N*: Each closing negotiation daemon + + * *onchaind-chan#N*: Each onchain close handling daemon + + So, **log-level=debug:plugin** would set debug level logging on all plugins and the plugin manager. **log-level=io:chan#55** would set IO logging on channel number 55 (or 550, for that matter). **log-level=debug:024b9a1fa8** would set debug logging for that channel (or any node id containing that string). - **log-prefix**=*PREFIX* -Prefix for all log lines: this can be customized if you want to merge logs +* **log-prefix**=*PREFIX* + + Prefix for all log lines: this can be customized if you want to merge logs with multiple daemons. Usually you want to include a space at the end of *PREFIX*, as the timestamp follows immediately. - **log-file**=*PATH* -Log to this file (instead of stdout). If you specify this more than once +* **log-file**=*PATH* + + Log to this file (instead of stdout). If you specify this more than once you'll get more than one log file: **-** is used to mean stdout. Sending lightningd(8) SIGHUP will cause it to reopen each file (useful for log rotation). - **log-timestamps**=*BOOL* -Set this to false to turn off timestamp prefixes (they will still appear +* **log-timestamps**=*BOOL* + + Set this to false to turn off timestamp prefixes (they will still appear in crash log files). - **rpc-file**=*PATH* -Set JSON-RPC socket (or /dev/tty), such as for lightning-cli(1). +* **rpc-file**=*PATH* + + Set JSON-RPC socket (or /dev/tty), such as for lightning-cli(1). - **rpc-file-mode**=*MODE* -Set JSON-RPC socket file mode, as a 4-digit octal number. +* **rpc-file-mode**=*MODE* + + Set JSON-RPC socket file mode, as a 4-digit octal number. Default is 0600, meaning only the user that launched lightningd can command it. Set to 0660 to allow users with the same group to access the RPC as well. - **daemon** -Run in the background, suppress stdout and stderr. Note that you need +* **daemon** + + Run in the background, suppress stdout and stderr. Note that you need to specify **log-file** for this case. - **conf**=*PATH* -Sets configuration file, and disable reading the normal general and network +* **conf**=*PATH* + + Sets configuration file, and disable reading the normal general and network ones. If this is a relative path, it is relative to the starting directory, not **lightning-dir** (unlike other paths). *PATH* must exist and be readable (we allow missing files in the default case). Using this inside a configuration file is invalid. - **wallet**=*DSN* -Identify the location of the wallet. This is a fully qualified data source +* **wallet**=*DSN* + + Identify the location of the wallet. This is a fully qualified data source name, including a scheme such as `sqlite3` or `postgres` followed by the connection parameters. -The default wallet corresponds to the following DSN: + The default wallet corresponds to the following DSN: + `--wallet=sqlite3://$HOME/.lightning/bitcoin/lightningd.sqlite31` -``` ---wallet=sqlite3://$HOME/.lightning/bitcoin/lightningd.sqlite3 -``` - -For the `sqlite3` scheme, you can specify a single backup database file + For the `sqlite3` scheme, you can specify a single backup database file by separating it with a `:` character, like so: + `--wallet=sqlite3://$HOME/.lightning/bitcoin/lightningd.sqlite3:/backup/lightningd.sqlite3` -``` ---wallet=sqlite3://$HOME/.lightning/bitcoin/lightningd.sqlite3:/backup/lightningd.sqlite3 -``` - -The following is an example of a postgresql wallet DSN: + The following is an example of a postgresql wallet DSN: -``` ---wallet=postgres://user:pass@localhost:5432/db_name -``` + `--wallet=postgres://user:pass@localhost:5432/db_name` -This will connect to a DB server running on `localhost` port `5432`, + This will connect to a DB server running on `localhost` port `5432`, authenticate with username `user` and password `pass`, and then use the database `db_name`. The database must exist, but the schema will be managed automatically by `lightningd`. - **bookkeeper-dir**=*DIR* [plugin `bookkeeper`] -Directory to keep the accounts.sqlite3 database file in. +* **bookkeeper-dir**=*DIR* [plugin `bookkeeper`] + + Directory to keep the accounts.sqlite3 database file in. Defaults to lightning-dir. - **bookkeeper-db**=*DSN* [plugin `bookkeeper`] -Identify the location of the bookkeeper data. This is a fully qualified data source +* **bookkeeper-db**=*DSN* [plugin `bookkeeper`] + + Identify the location of the bookkeeper data. This is a fully qualified data source name, including a scheme such as `sqlite3` or `postgres` followed by the connection parameters. Defaults to `sqlite3://accounts.sqlite3` in the `bookkeeper-dir`. +* **encrypted-hsm** - **encrypted-hsm** -If set, you will be prompted to enter a password used to encrypt the `hsm_secret`. + If set, you will be prompted to enter a password used to encrypt the `hsm_secret`. Note that once you encrypt the `hsm_secret` this option will be mandatory for `lightningd` to start. If there is no `hsm_secret` yet, `lightningd` will create a new encrypted secret. If you have an unencrypted `hsm_secret` you want to encrypt on-disk, or vice versa, see lightning-hsmtool(8). - **grpc-port**=*portnum* [plugin `cln-grpc`] +* **grpc-port**=*portnum* [plugin `cln-grpc`] -The port number for the GRPC plugin to listen for incoming + The port number for the GRPC plugin to listen for incoming connections; default is not to activate the plugin at all. ### Lightning node customization options - **alias**=*NAME* -Up to 32 bytes of UTF-8 characters to tag your node. Completely silly, since +* **alias**=*NAME* + + Up to 32 bytes of UTF-8 characters to tag your node. Completely silly, since anyone can call their node anything they want. The default is an NSA-style codename derived from your public key, but "Peter Todd" and "VAULTERO" are good options, too. - **rgb**=*RRGGBB* -Your favorite color as a hex code. +* **rgb**=*RRGGBB* + + Your favorite color as a hex code. + +* **fee-base**=*MILLISATOSHI* - **fee-base**=*MILLISATOSHI* -Default: 1000. The base fee to charge for every payment which passes + Default: 1000. The base fee to charge for every payment which passes through. Note that millisatoshis are a very, very small unit! Changing this value will only affect new channels and not existing ones. If you want to change fees for existing channels, use the RPC call lightning-setchannel(7). - **fee-per-satoshi**=*MILLIONTHS* -Default: 10 (0.001%). This is the proportional fee to charge for every +* **fee-per-satoshi**=*MILLIONTHS* + + Default: 10 (0.001%). This is the proportional fee to charge for every payment which passes through. As percentages are too coarse, it's in millionths, so 10000 is 1%, 1000 is 0.1%. Changing this value will only affect new channels and not existing ones. If you want to change fees for existing channels, use the RPC call lightning-setchannel(7). - **min-capacity-sat**=*SATOSHI* -Default: 10000. This value defines the minimal effective channel +* **min-capacity-sat**=*SATOSHI* + + Default: 10000. This value defines the minimal effective channel capacity in satoshi to accept for channel opening requests. This will reject any opening of a channel which can't pass an HTLC of least this value. Usually this prevents a peer opening a tiny channel, but it @@ -275,19 +312,22 @@ can also prevent a channel you open with a reasonable amount and the peer requesting such a large reserve that the capacity of the channel falls below this. - **ignore-fee-limits**=*BOOL* -Allow nodes which establish channels to us to set any fee they want. +* **ignore-fee-limits**=*BOOL* + + Allow nodes which establish channels to us to set any fee they want. This may result in a channel which cannot be closed, should fees increase, but make channels far more reliable since we never close it due to unreasonable fees. - **commit-time**=*MILLISECONDS* -How long to wait before sending commitment messages to the peer: in +* **commit-time**=*MILLISECONDS* + + How long to wait before sending commitment messages to the peer: in theory increasing this would reduce load, but your node would have to be extremely busy node for you to even notice. - **force-feerates**==*VALUES* -Networks like regtest and testnet have unreliable fee estimates: we +* **force-feerates**==*VALUES* + + Networks like regtest and testnet have unreliable fee estimates: we usually treat them as the minimum (253 sats/kw) if we can't get them. This allows override of one or more of our standard feerates (see lightning-feerates(7)). Up to 5 values, separated by '/' can be @@ -296,21 +336,24 @@ remainder. The values are in per-kw (roughly 1/4 of bitcoind's per-kb values), and the order is "opening", "mutual_close", "unilateral_close", "delayed_to_us", "htlc_resolution", and "penalty". -You would usually put this option in the per-chain config file, to avoid + You would usually put this option in the per-chain config file, to avoid setting it on Bitcoin mainnet! e.g. `~rusty/.lightning/regtest/config`. - **htlc-minimum-msat**=*MILLISATOSHI* -Default: 0. Sets the minimal allowed HTLC value for newly created channels. +* **htlc-minimum-msat**=*MILLISATOSHI* + + Default: 0. Sets the minimal allowed HTLC value for newly created channels. If you want to change the `htlc_minimum_msat` for existing channels, use the RPC call lightning-setchannel(7). - **htlc-maximum-msat**=*MILLISATOSHI* -Default: unset (no limit). Sets the maximum allowed HTLC value for newly created +* **htlc-maximum-msat**=*MILLISATOSHI* + + Default: unset (no limit). Sets the maximum allowed HTLC value for newly created channels. If you want to change the `htlc_maximum_msat` for existing channels, use the RPC call lightning-setchannel(7). - **disable-ip-discovery** -Turn off public IP discovery to send `node_announcement` updates that contain +* **disable-ip-discovery** + + Turn off public IP discovery to send `node_announcement` updates that contain the discovered IP with TCP port 9735 as announced address. If unset and you open TCP port 9735 on your router towords your node, your node will remain connectable on changing IP addresses. Note: Will always be disabled if you use @@ -318,65 +361,77 @@ connectable on changing IP addresses. Note: Will always be disabled if you use ### Lightning channel and HTLC options - **large-channels** -Removes capacity limits for channel creation. Version 1.0 of the specification +* **large-channels** + + Removes capacity limits for channel creation. Version 1.0 of the specification limited channel sizes to 16777215 satoshi. With this option (which your node will advertize to peers), your node will accept larger incoming channels and if the peer supports it, will open larger channels. Note: this option is spelled **large-channels** but it's pronounced **wumbo**. - **watchtime-blocks**=*BLOCKS* -How long we need to spot an outdated close attempt: on opening a channel +* **watchtime-blocks**=*BLOCKS* + + How long we need to spot an outdated close attempt: on opening a channel we tell our peer that this is how long they'll have to wait if they perform a unilateral close. - **max-locktime-blocks**=*BLOCKS* -The longest our funds can be delayed (ie. the longest +* **max-locktime-blocks**=*BLOCKS* + + The longest our funds can be delayed (ie. the longest **watchtime-blocks** our peer can ask for, and also the longest HTLC timeout we will accept). If our peer asks for longer, we'll refuse to create a channel, and if an HTLC asks for longer, we'll refuse it. - **funding-confirms**=*BLOCKS* -Confirmations required for the funding transaction when the other side +* **funding-confirms**=*BLOCKS* + + Confirmations required for the funding transaction when the other side opens a channel before the channel is usable. - **commit-fee**=*PERCENT* [plugin `bcli`] -The percentage of *estimatesmartfee 2/CONSERVATIVE* to use for the commitment +* **commit-fee**=*PERCENT* [plugin `bcli`] + + The percentage of *estimatesmartfee 2/CONSERVATIVE* to use for the commitment transactions: default is 100. - **max-concurrent-htlcs**=*INTEGER* -Number of HTLCs one channel can handle concurrently in each direction. +* **max-concurrent-htlcs**=*INTEGER* + + Number of HTLCs one channel can handle concurrently in each direction. Should be between 1 and 483 (default 30). - **max-dust-htlc-exposure-msat**=*MILLISATOSHI* -Option which limits the total amount of sats to be allowed as dust on a channel. +* **max-dust-htlc-exposure-msat**=*MILLISATOSHI* - **cltv-delta**=*BLOCKS* -The number of blocks between incoming payments and outgoing payments: + Option which limits the total amount of sats to be allowed as dust on a channel. + +* **cltv-delta**=*BLOCKS* + + The number of blocks between incoming payments and outgoing payments: this needs to be enough to make sure that if we have to, we can close the outgoing payment before the incoming, or redeem the incoming once the outgoing is redeemed. - **cltv-final**=*BLOCKS* -The number of blocks to allow for payments we receive: if we have to, we +* **cltv-final**=*BLOCKS* + + The number of blocks to allow for payments we receive: if we have to, we might need to redeem this on-chain, so this is the number of blocks we have to do that. -Invoice control options: +### Invoice control options: - **autocleaninvoice-cycle**=*SECONDS* [plugin `autoclean`] -Perform cleanup of expired invoices every *SECONDS* seconds, or disable +* **autocleaninvoice-cycle**=*SECONDS* [plugin `autoclean`] + + Perform cleanup of expired invoices every *SECONDS* seconds, or disable if 0. Usually unpaid expired invoices are uninteresting, and just take up space in the database. - **autocleaninvoice-expired-by**=*SECONDS* [plugin `autoclean`] -Control how long invoices must have been expired before they are cleaned +* **autocleaninvoice-expired-by**=*SECONDS* [plugin `autoclean`] + + Control how long invoices must have been expired before they are cleaned (if *autocleaninvoice-cycle* is non-zero). -Payment control options: +### Payment control options: - **disable-mpp** [plugin `pay`] -Disable the multi-part payment sending support in the `pay` plugin. By default +* **disable-mpp** [plugin `pay`] + + Disable the multi-part payment sending support in the `pay` plugin. By default the MPP support is enabled, but it can be desirable to disable in situations in which each payment should result in a single HTLC being forwarded in the network. @@ -399,22 +454,23 @@ precisely control where to bind and what to announce with the *bind-addr* and *announce-addr* options. These will **disable** the *autolisten* logic, so you must specifiy exactly what you want! - **addr**=*\[IPADDRESS\[:PORT\]\]|autotor:TORIPADDRESS\[:SERVICEPORT\]\[/torport=TORPORT\]|statictor:TORIPADDRESS\[:SERVICEPORT\]\[/torport=TORPORT\]\[/torblob=\[blob\]\]* +* **addr**=*\[IPADDRESS\[:PORT\]\]|autotor:TORIPADDRESS\[:SERVICEPORT\]\[/torport=TORPORT\]|statictor:TORIPADDRESS\[:SERVICEPORT\]\[/torport=TORPORT\]\[/torblob=\[blob\]\]* -Set an IP address (v4 or v6) or automatic Tor address to listen on and + + Set an IP address (v4 or v6) or automatic Tor address to listen on and (maybe) announce as our node address. -An empty 'IPADDRESS' is a special value meaning bind to IPv4 and/or + An empty 'IPADDRESS' is a special value meaning bind to IPv4 and/or IPv6 on all interfaces, '0.0.0.0' means bind to all IPv4 interfaces, '::' means 'bind to all IPv6 interfaces' (if you want to specify an IPv6 address *and* a port, use `[]` around the IPv6 address, like `[::]:9750`). -If 'PORT' is not specified, the default port 9735 is used for mainnet + If 'PORT' is not specified, the default port 9735 is used for mainnet (testnet: 19735, signet: 39735, regtest: 19846). If we can determine a public IP address from the resulting binding, the address is announced. -If the argument begins with 'autotor:' then it is followed by the + If the argument begins with 'autotor:' then it is followed by the IPv4 or IPv6 address of the Tor control port (default port 9051), and this will be used to configure a Tor hidden service for port 9735 in case of mainnet (bitcoin) network whereas other networks (testnet, @@ -424,7 +480,7 @@ The Tor hidden service will be configured to point to the first IPv4 or IPv6 address we bind to and is by default unique to your node's id. -If the argument begins with 'statictor:' then it is followed by the + If the argument begins with 'statictor:' then it is followed by the IPv4 or IPv6 address of the Tor control port (default port 9051), and this will be used to configure a static Tor hidden service. You can add the text '/torblob=BLOB' followed by up to @@ -434,63 +490,71 @@ You can also use an postfix '/torport=TORPORT' to select the external tor binding. The result is that over tor your node is accessible by a port defined by you and possibly different from your local node port assignment. -This option can be used multiple times to add more addresses, and + This option can be used multiple times to add more addresses, and its use disables autolisten. If necessary, and 'always-use-proxy' is not specified, a DNS lookup may be done to resolve 'IPADDRESS' or 'TORIPADDRESS'. - **bind-addr**=*\[IPADDRESS\[:PORT\]\]|SOCKETPATH* -Set an IP address or UNIX domain socket to listen to, but do not +* **bind-addr**=*\[IPADDRESS\[:PORT\]\]|SOCKETPATH* + + Set an IP address or UNIX domain socket to listen to, but do not announce. A UNIX domain socket is distinguished from an IP address by beginning with a */*. -An empty 'IPADDRESS' is a special value meaning bind to IPv4 and/or + An empty 'IPADDRESS' is a special value meaning bind to IPv4 and/or IPv6 on all interfaces, '0.0.0.0' means bind to all IPv4 interfaces, '::' means 'bind to all IPv6 interfaces'. 'PORT' is not specified, 9735 is used. -This option can be used multiple times to add more addresses, and + This option can be used multiple times to add more addresses, and its use disables autolisten. If necessary, and 'always-use-proxy' is not specified, a DNS lookup may be done to resolve 'IPADDRESS'. - **announce-addr**=*IPADDRESS\[:PORT\]|TORADDRESS.onion\[:PORT\]* -Set an IP (v4 or v6) address or Tor address to announce; a Tor address +* **announce-addr**=*IPADDRESS\[:PORT\]|TORADDRESS.onion\[:PORT\]* + + Set an IP (v4 or v6) address or Tor address to announce; a Tor address is distinguished by ending in *.onion*. *PORT* defaults to 9735. -Empty or wildcard IPv4 and IPv6 addresses don't make sense here. + Empty or wildcard IPv4 and IPv6 addresses don't make sense here. Also, unlike the 'addr' option, there is no checking that your announced addresses are public (e.g. not localhost). -This option can be used multiple times to add more addresses, and + This option can be used multiple times to add more addresses, and its use disables autolisten. -If necessary, and 'always-use-proxy' is not specified, a DNS + If necessary, and 'always-use-proxy' is not specified, a DNS lookup may be done to resolve 'IPADDRESS'. - **offline** -Do not bind to any ports, and do not try to reconnect to any peers. This +* **offline** + + Do not bind to any ports, and do not try to reconnect to any peers. This can be useful for maintenance and forensics, so is usually specified on the command line. Overrides all *addr* and *bind-addr* options. - **autolisten**=*BOOL* -By default, we bind (and maybe announce) on IPv4 and IPv6 interfaces if +* **autolisten**=*BOOL* + + By default, we bind (and maybe announce) on IPv4 and IPv6 interfaces if no *addr*, *bind-addr* or *announce-addr* options are specified. Setting this to *false* disables that. - **proxy**=*IPADDRESS\[:PORT\]* -Set a socks proxy to use to connect to Tor nodes (or for all connections +* **proxy**=*IPADDRESS\[:PORT\]* + + Set a socks proxy to use to connect to Tor nodes (or for all connections if **always-use-proxy** is set). The port defaults to 9050 if not specified. - **always-use-proxy**=*BOOL* -Always use the **proxy**, even to connect to normal IP addresses (you +* **always-use-proxy**=*BOOL* + + Always use the **proxy**, even to connect to normal IP addresses (you can still connect to Unix domain sockets manually). This also disables all DNS lookups, to avoid leaking information. - **disable-dns** -Disable the DNS bootstrapping mechanism to find a node by its node ID. +* **disable-dns** + + Disable the DNS bootstrapping mechanism to find a node by its node ID. - **tor-service-password**=*PASSWORD* -Set a Tor control password, which may be needed for *autotor:* to +* **tor-service-password**=*PASSWORD* + + Set a Tor control password, which may be needed for *autotor:* to authenticate to the Tor control port. ### Lightning Plugins @@ -502,35 +566,40 @@ by default (usually located in **libexec/c-lightning/plugins/**). If a plugins along with any immediate subdirectories). You can specify additional paths too: - **plugin**=*PATH* -Specify a plugin to run as part of Core Lightning. This can be specified +* **plugin**=*PATH* + + Specify a plugin to run as part of Core Lightning. This can be specified multiple times to add multiple plugins. Note that unless plugins themselves specify ordering requirements for being called on various hooks, plugins will be ordered by commandline, then config file. - **plugin-dir**=*DIRECTORY* -Specify a directory to look for plugins; all executable files not +* **plugin-dir**=*DIRECTORY* + + Specify a directory to look for plugins; all executable files not containing punctuation (other than *.*, *-* or *\_) in 'DIRECTORY* are loaded. *DIRECTORY* must exist; this can be specified multiple times to add multiple directories. The ordering of plugins within a directory is currently unspecified. - **clear-plugins** -This option clears all *plugin*, *important-plugin*, and *plugin-dir* options +* **clear-plugins** + + This option clears all *plugin*, *important-plugin*, and *plugin-dir* options preceeding it, including the default built-in plugin directory. You can still add *plugin-dir*, *plugin*, and *important-plugin* options following this and they will have the normal effect. - **disable-plugin**=*PLUGIN* -If *PLUGIN* contains a /, plugins with the same path as *PLUGIN* will +* **disable-plugin**=*PLUGIN* + + If *PLUGIN* contains a /, plugins with the same path as *PLUGIN* will not be loaded at startup. Otherwise, no plugin with that base name will be loaded at startup, whatever directory it is in. This option is useful for disabling a single plugin inside a directory. You can still explicitly load plugins which have been disabled, using lightning-plugin(7) `start`. - **important-plugin**=*PLUGIN* -Speciy a plugin to run as part of Core Lightning. +* **important-plugin**=*PLUGIN* + + Speciy a plugin to run as part of Core Lightning. This can be specified multiple times to add multiple plugins. Plugins specified via this option are considered so important, that if the plugin stops for any reason (including via lightning-plugin(7) `stop`), @@ -550,41 +619,41 @@ A build _with_ `--enable-experimental-features` enables some of below options by default and also adds support for even more features. Supported features can be listed with `lightningd --list-features-only`. - **experimental-onion-messages** +* **experimental-onion-messages** -Specifying this enables sending, forwarding and receiving onion messages, + Specifying this enables sending, forwarding and receiving onion messages, which are in draft status in the BOLT specifications. - **experimental-offers** +* **experimental-offers** -Specifying this enables the `offers` and `fetchinvoice` plugins and + Specifying this enables the `offers` and `fetchinvoice` plugins and corresponding functionality, which are in draft status as BOLT12. This usually requires **experimental-onion-messages** as well. See lightning-offer(7) and lightning-fetchinvoice(7). - **fetchinvoice-noconnect** +* **fetchinvoice-noconnect** -Specifying this prevents `fetchinvoice` and `sendinvoice` from + Specifying this prevents `fetchinvoice` and `sendinvoice` from trying to connect directly to the offering node as a last resort. - **experimental-shutdown-wrong-funding** +* **experimental-shutdown-wrong-funding** -Specifying this allows the `wrong_funding` field in shutdown: if a + Specifying this allows the `wrong_funding` field in shutdown: if a remote node has opened a channel but claims it used the incorrect txid (and the channel hasn't been used yet at all) this allows them to negotiate a clean shutdown with the txid they offer. - **experimental-dual-fund** +* **experimental-dual-fund** -Specifying this enables support for the dual funding protocol, + Specifying this enables support for the dual funding protocol, allowing both parties to contribute funds to a channel. The decision about whether to add funds or not to a proposed channel is handled automatically by a plugin that implements the appropriate logic for your needs. The default behavior is to not contribute funds. - **experimental-websocket-port**=*PORT* +* **experimental-websocket-port**=*PORT* -Specifying this enables support for accepting incoming WebSocket + Specifying this enables support for accepting incoming WebSocket connections on that port, on any IPv4 and IPv6 addresses you listen to. The normal protocol is expected to be sent over WebSocket binary frames once the connection is upgraded. diff --git a/doc/lightningd.8.md b/doc/lightningd.8.md index 552c8c3030d1..c16429689b39 100644 --- a/doc/lightningd.8.md +++ b/doc/lightningd.8.md @@ -10,47 +10,47 @@ lightningd [--conf=] [OPTIONS] DESCRIPTION ----------- -**lightningd** starts the C-Lightning daemon, which implements a +**lightningd** starts the Core Lightning daemon, which implements a standards-compliant Lightning Network node. CONFIGURATION OPTIONS --------------------- - **--conf**=*FILE* +* **--conf**=*FILE* Specify configuration file. If not an absolute path, will be relative from the lightning-dir location. Defaults to *config*. - **--lightning-dir**=*DIR* -Set the directory for the C-Lightning daemon. Defaults to +* **--lightning-dir**=*DIR* +Set the directory for the Core Lightning daemon. Defaults to *$HOME/.lightning*. MORE OPTIONS ------------ Command line options are mirrored as configuration options in the -configuration file, so *foo* in the configuration file simply becomes -**--foo** on the command line, and **foo=bar** becomes **--foo=bar**. +configuration file, so `foo` in the configuration file simply becomes +`--foo` on the command line, and `foo=bar` becomes `--foo=bar`. See lightningd-config(5) for a comprehensive list of all available options. -LOGGING AND COMMANDING C-LIGHTNING ----------------------------------- +LOGGING AND COMMANDING CORE LIGHTNING +------------------------------------- -By default, C-Lightning will log to the standard output. +By default, Core Lightning will log to the standard output. To log to a specific file, use **--log-file**=*PATH*. -Sending SIGHUP will cause C-Lightning to reopen this file, +Sending SIGHUP will cause Core Lightning to reopen this file, for example to do log rotation. -C-Lightning will set up a Unix domain socket for receiving +Core Lightning will set up a Unix domain socket for receiving commands. By default this will be the file **lightning-rpc** in your specified **lightning-dir**. -You can use lightning-cli(1) to send commands to C-Lightning +You can use lightning-cli(1) to send commands to Core Lightning once **lightningd** has started; you need to match the **--lightning-dir** and **--rpc-file** options between them. -Commands for C-Lightning are described in various manpages +Commands for Core Lightning are described in various manpages in section 7, with the common prefix **lightning-**. QUICK START @@ -63,9 +63,9 @@ directory containing your configuration. Your other main preparation would be to set up a mainnet Bitcoin fullnode, i.e. run a bitcoind(1) instance. The rest of this quick start guide will assume you are reckless and want to spend real funds on -Lightning. Indicate *network=bitcoin* in your *config* file explicitly. +Lightning: otherwise indicate *network=testnet* in your *config* file explicitly. -C-Lightning needs to communicate with the Bitcoin Core RPC. You can set +Core Lightning needs to communicate with the Bitcoin Core RPC. You can set this up using *bitcoin-datadir*, *bitcoin-rpcconnect*, *bitcoin-rpcport*, *bitcoin-rpcuser*, and *bitcoin-rpcpassword* options in your *config* file. @@ -84,8 +84,8 @@ option. Check if things are working: - $ lightning-cli --lightning-dir=%HOME/.lightning help - $ lightning-cli --lightning-dir=%HOME/.lightning getinfo + $ lightning-cli --lightning-dir=$HOME/.lightning help + $ lightning-cli --lightning-dir=$HOME/.lightning getinfo The **getinfo** command in particular will return a *blockheight* field, which indicates the block height to which **lightningd** has been @@ -142,11 +142,11 @@ and look at the *state* of the channel: $ lightning-cli listpeers $PUBLICKEY The channel will initially start with a *state* of -*CHANNELD\_AWAITING\_LOCKIN*. You need to wait for the channel *state* -to become *CHANNELD\_NORMAL*, meaning the funding transaction has been +*CHANNELD\_AWAITING_LOCKIN*. You need to wait for the channel *state* +to become *CHANNELD_NORMAL*, meaning the funding transaction has been confirmed deeply. -Once the channel *state* is *CHANNELD\_NORMAL*, you can start paying +Once the channel *state* is *CHANNELD_NORMAL*, you can start paying merchants over Lightning. Acquire a Lightning invoice from your favorite merchant, and use lightning-pay(7) to pay it: diff --git a/tools/check-manpage.sh b/tools/check-manpage.sh index 09ad861af95d..2084cbe3c44d 100755 --- a/tools/check-manpage.sh +++ b/tools/check-manpage.sh @@ -9,7 +9,7 @@ fi get_cmd_opts() { # Trim out -- after first one (--option mentioned in help!) - $1 --help | grep '^-' | sed 's/[ ].*--.*//' | while IFS=$'\n' read -r opt; do + $1 --help | grep '^\*' | sed 's/[ ].*--.*//' | while IFS=$'\n' read -r opt; do case "$opt" in # We don't document dev options. --dev*) From 8f1164365e7688ac923ddde6ea5b4a7cdf2ced3e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 07:03:09 +0930 Subject: [PATCH 1296/1530] doc: generate correct markdown from schemas. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit You can't start a list without a paragraph separator. ```diff --- /tmp/before 2022-07-20 22:02:23.485372596 +0930 +++ /tmp/after 2022-07-20 22:02:33.745528456 +0930 @@ -21,12 +21,16 @@ On startup of the daemon, no autoclean is set up. RETURN VALUE - On success, an object is returned, containing: - enabled (boolean): - whether invoice autocleaning is active + On success, an object is returned, containing: - If enabled is true: - expired_by (u64): how long an invoice must be ex‐ - pired (seconds) before we delete it - cycle_seconds (u64): how long an - invoice must be expired (seconds) before we delete it + • enabled (boolean): whether invoice autocleaning is active + + If enabled is true: + + • expired_by (u64): how long an invoice must be expired (seconds) be‐ + fore we delete it + • cycle_seconds (u64): how long an invoice must be expired (seconds) + before we delete it AUTHOR ZmnSCPxj is mainly responsible. ``` Signed-off-by: Rusty Russell --- doc/lightning-autocleaninvoice.7.md | 2 ++ doc/lightning-check.7.md | 1 + doc/lightning-checkmessage.7.md | 1 + doc/lightning-close.7.md | 2 ++ doc/lightning-commando-rune.7.md | 1 + doc/lightning-connect.7.md | 3 +++ doc/lightning-createinvoice.7.md | 1 + doc/lightning-createonion.7.md | 1 + doc/lightning-datastore.7.md | 1 + doc/lightning-decode.7.md | 8 ++++++++ doc/lightning-decodepay.7.md | 1 + doc/lightning-deldatastore.7.md | 1 + doc/lightning-delinvoice.7.md | 3 +++ doc/lightning-delpay.7.md | 1 + doc/lightning-disableoffer.7.md | 1 + doc/lightning-feerates.7.md | 2 ++ doc/lightning-fetchinvoice.7.md | 1 + doc/lightning-fundchannel.7.md | 1 + doc/lightning-fundchannel_cancel.7.md | 1 + doc/lightning-fundchannel_complete.7.md | 1 + doc/lightning-fundchannel_start.7.md | 2 ++ doc/lightning-funderupdate.7.md | 1 + doc/lightning-fundpsbt.7.md | 1 + doc/lightning-getinfo.7.md | 3 +++ doc/lightning-getlog.7.md | 4 ++++ doc/lightning-getroute.7.md | 1 + doc/lightning-help.7.md | 1 + doc/lightning-invoice.7.md | 2 ++ doc/lightning-keysend.7.md | 2 ++ doc/lightning-listchannels.7.md | 1 + doc/lightning-listconfigs.7.md | 1 + doc/lightning-listdatastore.7.md | 1 + doc/lightning-listforwards.7.md | 4 ++++ doc/lightning-listfunds.7.md | 5 +++++ doc/lightning-listinvoices.7.md | 2 ++ doc/lightning-listnodes.7.md | 4 ++++ doc/lightning-listoffers.7.md | 1 + doc/lightning-listpays.7.md | 4 ++++ doc/lightning-listpeers.7.md | 11 +++++++++++ doc/lightning-listsendpays.7.md | 3 +++ doc/lightning-listtransactions.7.md | 1 + doc/lightning-makesecret.7.md | 1 + doc/lightning-multifundchannel.7.md | 1 + doc/lightning-multiwithdraw.7.md | 1 + doc/lightning-newaddr.7.md | 1 + doc/lightning-offer.7.md | 1 + doc/lightning-offerout.7.md | 1 + doc/lightning-openchannel_abort.7.md | 1 + doc/lightning-openchannel_bump.7.md | 1 + doc/lightning-openchannel_init.7.md | 1 + doc/lightning-openchannel_signed.7.md | 1 + doc/lightning-openchannel_update.7.md | 1 + doc/lightning-parsefeerate.7.md | 1 + doc/lightning-pay.7.md | 2 ++ doc/lightning-ping.7.md | 1 + doc/lightning-plugin.7.md | 3 +++ doc/lightning-reserveinputs.7.md | 1 + doc/lightning-sendcustommsg.7.md | 1 + doc/lightning-sendinvoice.7.md | 2 ++ doc/lightning-sendonion.7.md | 3 +++ doc/lightning-sendpay.7.md | 3 +++ doc/lightning-sendpsbt.7.md | 1 + doc/lightning-setchannel.7.md | 1 + doc/lightning-setchannelfee.7.md | 1 + doc/lightning-signmessage.7.md | 1 + doc/lightning-signpsbt.7.md | 1 + doc/lightning-txdiscard.7.md | 1 + doc/lightning-txprepare.7.md | 1 + doc/lightning-txsend.7.md | 1 + doc/lightning-unreserveinputs.7.md | 2 ++ doc/lightning-utxopsbt.7.md | 1 + doc/lightning-waitanyinvoice.7.md | 2 ++ doc/lightning-waitblockheight.7.md | 1 + doc/lightning-waitinvoice.7.md | 2 ++ doc/lightning-waitsendpay.7.md | 2 ++ doc/lightning-withdraw.7.md | 1 + tools/fromschema.py | 10 +++++----- 77 files changed, 141 insertions(+), 5 deletions(-) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index 8dffb14781d9..5f629dc30ddf 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -27,9 +27,11 @@ RETURN VALUE [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 diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index 243160cf8f68..13858422d3c5 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -25,6 +25,7 @@ RETURN VALUE [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) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index dd010c2d806e..0ca51979e466 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -29,6 +29,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **verified** (boolean): whether the signature was valid (always *true*) - **pubkey** (pubkey): the *pubkey* parameter, or the pubkey found by looking for known nodes diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 97ded08c8e02..5757bc156fc4 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -104,9 +104,11 @@ RETURN VALUE [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 diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index abd71efb4431..c9561a6ad8cb 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -190,6 +190,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **rune** (string): the resulting rune - **unique_id** (string): the id of this rune: this is set at creation and cannot be changed (even as restrictions are added) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index 5f8274924417..d68d08aae300 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -49,6 +49,7 @@ RETURN VALUE [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") @@ -56,9 +57,11 @@ On success, an object is returned, containing: - **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 diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index f960b936f193..a6dfe3bd21db 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -32,6 +32,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **label** (string): the label for the invoice - **payment_hash** (hash): 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") diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 38184588635b..f2f4286063fb 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -91,6 +91,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **onion** (hex): the onion packet (*onion_size* bytes) - **shared_secrets** (array of secrets): one shared secret for each node in the *hops* parameter: - the shared secret with this hop (always 64 characters) diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index 95e7df0e0701..39bce1bbeea0 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -34,6 +34,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **key** (array of strings): - Part of the key added to the datastore - **generation** (u64, optional): The number of times this has been updated diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 078e45a12b7c..b6464ab9b2b1 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -23,10 +23,12 @@ RETURN VALUE [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", "rune") - **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** (point32): x-only public key of the offering node - **description** (string): the description of the purpose of the offer @@ -64,10 +66,12 @@ If **type** is "bolt12 offer", and **valid** is *true*: - **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** (point32): 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 @@ -100,6 +104,7 @@ If **type** is "bolt12 invoice", and **valid** is *true*: - **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 @@ -116,6 +121,7 @@ If **type** is "bolt12 invoice", and **valid** is *false*: - **warning_invoice_refund_missing_signature**: No **refund_signature** If **type** is "bolt12 invoice_request", and **valid** is *true*: + - **offer_id** (hex): the id of the offer this is requesting (merkle hash of non-signature fields) (always 64 characters) - **payer_key** (point32): the transient key which identifies the payer - **chain** (hex, optional): which blockchain this invoice_request is for (missing implies bitcoin mainnet only) (always 64 characters) @@ -128,6 +134,7 @@ If **type** is "bolt12 invoice_request", and **valid** is *true*: - **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** @@ -135,6 +142,7 @@ If **type** is "bolt12 invoice_request", and **valid** is *false*: - **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* diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index 336508033225..42f26c39e7f1 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -17,6 +17,7 @@ RETURN VALUE [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* diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index d35ccf971751..16339e752eea 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -20,6 +20,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **key** (array of strings): - Part of the key added to the datastore - **generation** (u64, optional): The number of times this has been updated diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 7e5ca099a9f8..0762528b38e6 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -26,6 +26,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 - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): State of invoice (one of "paid", "expired", "unpaid") @@ -36,10 +37,12 @@ On success, an object is returned, containing: - **description** (string, optional): description used in the invoice If **bolt12** is present: + - **local_offer_id** (hex, optional): offer for which this invoice was created - **payer_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice 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 diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index 3a4f9a05d3b9..f242685448b3 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -37,6 +37,7 @@ 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") diff --git a/doc/lightning-disableoffer.7.md b/doc/lightning-disableoffer.7.md index 0d0702e2a8b5..c42bfcc8422a 100644 --- a/doc/lightning-disableoffer.7.md +++ b/doc/lightning-disableoffer.7.md @@ -36,6 +36,7 @@ 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 diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index 345e1fe1ab5b..bc5cd90fc110 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -46,6 +46,7 @@ RETURN VALUE [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). @@ -72,6 +73,7 @@ On success, an object is returned, containing: - **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) diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index d0bc02d6502a..dbf168090973 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -50,6 +50,7 @@ RETURN VALUE [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. diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index 420892526988..ae0bb1521bb7 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -84,6 +84,7 @@ RETURN VALUE [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 diff --git a/doc/lightning-fundchannel_cancel.7.md b/doc/lightning-fundchannel_cancel.7.md index fbc49b7054b5..474f1fbb8a00 100644 --- a/doc/lightning-fundchannel_cancel.7.md +++ b/doc/lightning-fundchannel_cancel.7.md @@ -27,6 +27,7 @@ RETURN VALUE [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) diff --git a/doc/lightning-fundchannel_complete.7.md b/doc/lightning-fundchannel_complete.7.md index 5a71e2490af5..b4949c085d09 100644 --- a/doc/lightning-fundchannel_complete.7.md +++ b/doc/lightning-fundchannel_complete.7.md @@ -28,6 +28,7 @@ RETURN VALUE [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*) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index 490650442fb8..6ef71d3e8c38 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -43,12 +43,14 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **funding_address** (string): The address to send funding to for the channel. DO NOT SEND COINS TO THIS ADDRESS YET. - **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` - **mindepth** (u32, optional): Number of confirmations before we consider the channel active. The following warnings may also be returned: + - **warning_usage**: A warning not to prematurely broadcast the funding transaction (always present!) [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-funderupdate.7.md b/doc/lightning-funderupdate.7.md index 2e8999cfc891..842957e00a11 100644 --- a/doc/lightning-funderupdate.7.md +++ b/doc/lightning-funderupdate.7.md @@ -106,6 +106,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **summary** (string): Summary of the current funding policy e.g. (match 100) - **policy** (string): Policy funder plugin will use to decide how much captial to commit to a v2 open channel request (one of "match", "available", "fixed") - **policy_mod** (u32): The *policy_mod* is the number or 'modification' to apply to the policy. diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 0b138e9fc852..5a5aa8e2d571 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -72,6 +72,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **psbt** (string): Unsigned PSBT which fulfills the parameters given - **feerate_per_kw** (u32): The feerate used to create the PSBT, in satoshis-per-kiloweight - **estimated_final_weight** (u32): The estimated weight of the transaction once fully signed diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 234e9da8641f..8575c99c8c45 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -27,6 +27,7 @@ RETURN VALUE [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) @@ -49,6 +50,7 @@ On success, an object is returned, containing: - **port** (u16): port number If **type** is "dns", "ipv4", "ipv6", "torv2" or "torv3": + - **address** (string): address in expected format for **type** - **binding** (array of objects, optional): The addresses we are listening on: - **type** (string): Type of connection (one of "local socket", "ipv4", "ipv6", "torv2", "torv3") @@ -57,6 +59,7 @@ On success, an object is returned, containing: - **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. diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index 5b625f04ec74..c0c3c22b578f 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -30,6 +30,7 @@ RETURN VALUE [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 @@ -37,15 +38,18 @@ On success, an object is returned, containing: - **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 diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index 1e7fb5d48b3b..ef2837170800 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -279,6 +279,7 @@ RETURN VALUE [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 diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index 5c5dbc2e36e5..9e0e2ec8e110 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -30,6 +30,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **help** (array of objects): - **command** (string): the command - **category** (string): the category for this command (useful for grouping) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 19f649d3e668..bc20985b9f07 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -77,12 +77,14 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **bolt11** (string): the bolt11 string - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **payment_secret** (secret): the *payment_secret* to place in the onion (always 64 characters) - **expires_at** (u64): UNIX timestamp of when invoice expires The following warnings may also be returned: + - **warning_capacity**: even using all possible channels, there's not enough incoming capacity to pay this invoice. - **warning_offline**: there would be enough incoming capacity, but some channels are offline, so there isn't. - **warning_deadends**: there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't. diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index 515998e9b8c2..90e4d404bb96 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -67,6 +67,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **created_at** (number): the UNIX timestamp showing when this payment was initiated @@ -77,6 +78,7 @@ On success, an object is returned, containing: - **destination** (pubkey, optional): the final destination of the payment The following warnings may also be returned: + - **warning_partial_completion**: Not all parts of a multi-part payment have completed [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index fff0edda2972..ae63825a0c90 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -32,6 +32,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **channels** is returned. It is an array of objects, where each object contains: + - **source** (pubkey): the source node - **destination** (pubkey): the destination node - **short_channel_id** (short_channel_id): short channel id of channel diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index a5cdf61732c6..3820f6945412 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -34,6 +34,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **# version** (string, optional): Special field indicating the current version - **plugins** (array of objects, optional): - **path** (string): Full path of the plugin diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index b3e65602f234..0498c7155f0d 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -20,6 +20,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **datastore** is returned. It is an array of objects, where each object contains: + - **key** (array of strings): - Part of the key added to the datastore - **generation** (u64, optional): The number of times this has been updated diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 8a2ae806053d..463bee4f3fb7 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -23,6 +23,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **forwards** is returned. It is an array of objects, where each object contains: + - **in_channel** (short_channel_id): the channel that received the HTLC - **in_msat** (msat): the value of the incoming HTLC - **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed") @@ -32,13 +33,16 @@ On success, an object containing **forwards** is returned. It is an array of ob - **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") If **out_msat** is present: + - **fee_msat** (msat): the amount this paid in fees - **out_msat** (msat): the amount we sent out the *out_channel* If **status** is "settled" or "failed": + - **resolved_time** (number): the UNIX timestamp when this was resolved If **status** is "local_failed" or "failed": + - **failcode** (u32, optional): the numeric onion code returned - **failreason** (string, optional): the name of the onion code returned diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index 12d060c9e22a..a992902935a6 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -21,6 +21,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **outputs** (array of objects): - **txid** (txid): the ID of the spendable transaction - **output** (u32): the index within *txid* @@ -32,9 +33,11 @@ On success, an object is returned, containing: - **redeemscript** (hex, optional): the redeemscript, only if it's p2sh-wrapped If **status** is "confirmed": + - **blockheight** (u32): Block height where it was confirmed If **reserved** is "true": + - **reserved_to_block** (u32): Block height where reservation will expire - **channels** (array of objects): - **peer_id** (pubkey): the peer with which the channel is opened @@ -46,9 +49,11 @@ On success, an object is returned, containing: - **state** (string): the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") If **state** is "CHANNELD_NORMAL": + - **short_channel_id** (short_channel_id): short channel id of channel If **state** is "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN" or "ONCHAIN": + - **short_channel_id** (short_channel_id, optional): short channel id of channel (only if funding reached lockin depth before closing) [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index 52ab2d14fc8f..983b68575166 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -22,6 +22,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **invoices** is returned. It is an array of objects, where each object contains: + - **label** (string): unique label supplied at invoice creation - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid, unpaid or unpayable (one of "unpaid", "paid", "expired") @@ -34,6 +35,7 @@ On success, an object containing **invoices** is returned. It is an array of ob - **payer_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only). If **status** is "paid": + - **pay_index** (u64): Unique incrementing index for this payment - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - **paid_at** (u64): UNIX timestamp of when it was paid diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index 7a36dd71a55d..f8d1bb95c172 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -28,10 +28,12 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **nodes** is returned. It is an array of objects, where each object contains: + - **nodeid** (pubkey): the public key of the node - **last_timestamp** (u32, optional): A node_announcement has been received for this node (UNIX timestamp) If **last_timestamp** is present: + - **alias** (string): The fun alias this node advertized (up to 32 characters) - **color** (hex): The favorite RGB color this node advertized (always 6 characters) - **features** (hex): BOLT #9 features bitmap this node advertized @@ -40,9 +42,11 @@ If **last_timestamp** is present: - **port** (u16): port number If **type** is "dns", "ipv4", "ipv6", "torv2" or "torv3": + - **address** (string): address in expected format for **type** If **option_will_fund** is present: + - **option_will_fund** (object): - **lease_fee_base_msat** (msat): the fixed fee for a lease (whole number of satoshis) - **lease_fee_basis** (u32): the proportional fee in basis points (parts per 10,000) for a lease diff --git a/doc/lightning-listoffers.7.md b/doc/lightning-listoffers.7.md index b684d749ec9a..4dcd47e751ca 100644 --- a/doc/lightning-listoffers.7.md +++ b/doc/lightning-listoffers.7.md @@ -31,6 +31,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **offers** is returned. It is an array of objects, where each object contains: + - **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) - **active** (boolean): whether this can still be used - **single_use** (boolean): whether this expires as soon as it's paid diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index f8c44bfb85f8..b1a355e13e96 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -18,6 +18,7 @@ RETURN VALUE [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 @@ -28,14 +29,17 @@ On success, an object containing **pays** is returned. It is an array of object - **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) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 34cb73ef9ff0..b42256839012 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -40,6 +40,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **peers** is returned. It is an array of objects, where each object contains: + - **id** (pubkey): the public key of the peer - **connected** (boolean): True if the peer is currently connected - **channels** (array of objects): @@ -125,21 +126,27 @@ On success, an object containing **peers** is returned. It is an array of objec - **status** (string, optional): set if this HTLC is currently waiting on a hook (and shows what plugin) If **direction** is "out": + - **state** (string): Status of the HTLC (one of "SENT_ADD_HTLC", "SENT_ADD_COMMIT", "RCVD_ADD_REVOCATION", "RCVD_ADD_ACK_COMMIT", "SENT_ADD_ACK_REVOCATION", "RCVD_REMOVE_HTLC", "RCVD_REMOVE_COMMIT", "SENT_REMOVE_REVOCATION", "SENT_REMOVE_ACK_COMMIT", "RCVD_REMOVE_ACK_REVOCATION") If **direction** is "in": + - **state** (string): Status of the HTLC (one of "RCVD_ADD_HTLC", "RCVD_ADD_COMMIT", "SENT_ADD_REVOCATION", "SENT_ADD_ACK_COMMIT", "RCVD_ADD_ACK_REVOCATION", "SENT_REMOVE_HTLC", "SENT_REMOVE_COMMIT", "RCVD_REMOVE_REVOCATION", "RCVD_REMOVE_ACK_COMMIT", "SENT_REMOVE_ACK_REVOCATION") If **close_to** is present: + - **close_to_addr** (string, optional): The bitcoin address we will close to If **scratch_txid** is present: + - **last_tx_fee_msat** (msat): fee attached to this the current tx If **short_channel_id** is present: + - **direction** (u32): 0 if we're the lesser node_id, 1 if we're the greater If **inflight** is present: + - **initial_feerate** (string): The feerate for the initial funding transaction in per-1000-weight, with "kpw" appended - **last_feerate** (string): The feerate for the latest funding transaction in per-1000-weight, with "kpw" appended - **next_feerate** (string): The minimum feerate for the next funding transaction in per-1000-weight, with "kpw" appended @@ -147,15 +154,18 @@ On success, an object containing **peers** is returned. It is an array of objec - **type** (string) (one of "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO_IN", "IO_OUT") If **type** is "SKIPPED": + - **num_skipped** (u32): number of deleted/omitted entries If **type** is "BROKEN", "UNUSUAL", "INFO" or "DEBUG": + - **time** (string): UNIX timestamp with 9 decimal places - **source** (string): The particular logbook this was found in - **log** (string): The actual log message - **node_id** (pubkey): The peer this is associated with If **type** is "IO_IN" or "IO_OUT": + - **time** (string): UNIX timestamp with 9 decimal places - **source** (string): The particular logbook this was found in - **log** (string): The actual log message @@ -163,6 +173,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **data** (hex): The IO which occurred If **connected** is *true*: + - **netaddr** (array of strings): A single entry array: - address, e.g. 1.2.3.4:1234 - **features** (hex): bitmap of BOLT #9 features from peer's INIT message diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 174ebde0d294..6d785936841b 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -24,6 +24,7 @@ Note that the returned array is ordered by increasing *id*. [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** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (one of "pending", "failed", "complete") @@ -38,9 +39,11 @@ On success, an object containing **payments** is returned. It is an array of ob - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "complete": + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "failed": + - **erroronion** (hex, optional): the onion message returned [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index f90eb7c04380..42d5d2196d2b 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -26,6 +26,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **transactions** is returned. It is an array of objects, where each object contains: + - **hash** (txid): the transaction id - **rawtx** (hex): the raw transaction - **blockheight** (u32): the block height of this tx diff --git a/doc/lightning-makesecret.7.md b/doc/lightning-makesecret.7.md index 7170061cb6b3..7e5c4798240f 100644 --- a/doc/lightning-makesecret.7.md +++ b/doc/lightning-makesecret.7.md @@ -18,6 +18,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **secret** (secret): the pseudorandom key derived from HSM_secret (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index a9e75bb6283e..c2f544264970 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -99,6 +99,7 @@ do not have a channel even if **multifundchannel** succeeded. [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 - **channel_ids** (array of objects): diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index 2e39c8876df2..d0817b87a0fd 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -42,6 +42,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **tx** (hex): The raw transaction which was sent - **txid** (txid): The txid of the **tx** diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index 80865b8ddfe6..95effec8c2e2 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -31,6 +31,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **bech32** (string, optional): The bech32 (native segwit) address - **p2sh-segwit** (string, optional): The p2sh-wrapped address diff --git a/doc/lightning-offer.7.md b/doc/lightning-offer.7.md index 5bc01dfa858a..23f6a0c3f8b7 100644 --- a/doc/lightning-offer.7.md +++ b/doc/lightning-offer.7.md @@ -96,6 +96,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) - **active** (boolean): whether this can still be used (always *true*) - **single_use** (boolean): whether this expires as soon as it's paid (reflects the *single_use* parameter) diff --git a/doc/lightning-offerout.7.md b/doc/lightning-offerout.7.md index f96d19149508..d07d4d827f4a 100644 --- a/doc/lightning-offerout.7.md +++ b/doc/lightning-offerout.7.md @@ -55,6 +55,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) - **active** (boolean): whether this will pay a matching incoming invoice (always *true*) - **single_use** (boolean): whether this expires as soon as it's paid out (always *true*) diff --git a/doc/lightning-openchannel_abort.7.md b/doc/lightning-openchannel_abort.7.md index f05fba23f54e..1c6259c2fac1 100644 --- a/doc/lightning-openchannel_abort.7.md +++ b/doc/lightning-openchannel_abort.7.md @@ -21,6 +21,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **channel_id** (hex): the channel id of the aborted channel (always 64 characters) - **channel_canceled** (boolean): whether this is completely canceled (there may be remaining in-flight transactions) - **reason** (string): usually "Abort requested", but if it happened to fail at the same time it could be different diff --git a/doc/lightning-openchannel_bump.7.md b/doc/lightning-openchannel_bump.7.md index 66028426b0c3..cd5c5c2ca6ae 100644 --- a/doc/lightning-openchannel_bump.7.md +++ b/doc/lightning-openchannel_bump.7.md @@ -37,6 +37,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **channel_id** (hex): the channel id of the channel (always 64 characters) - **psbt** (string): the (incomplete) PSBT of the RBF transaction - **commitments_secured** (boolean): whether the *psbt* is complete (always *false*) diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index d8df70e08b97..2ea017ecadf3 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -52,6 +52,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **channel_id** (hex): the channel id of the channel (always 64 characters) - **psbt** (string): the (incomplete) PSBT of the funding transaction - **commitments_secured** (boolean): whether the *psbt* is complete (always *false*) diff --git a/doc/lightning-openchannel_signed.7.md b/doc/lightning-openchannel_signed.7.md index 72972f6eda48..33346a11e5c9 100644 --- a/doc/lightning-openchannel_signed.7.md +++ b/doc/lightning-openchannel_signed.7.md @@ -31,6 +31,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **channel_id** (hex): the channel id of the channel (always 64 characters) - **tx** (hex): the funding transaction - **txid** (txid): The txid of the **tx** diff --git a/doc/lightning-openchannel_update.7.md b/doc/lightning-openchannel_update.7.md index aa01a08a8147..ea279667aef4 100644 --- a/doc/lightning-openchannel_update.7.md +++ b/doc/lightning-openchannel_update.7.md @@ -30,6 +30,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **channel_id** (hex): the channel id of the channel (always 64 characters) - **psbt** (string): the PSBT of the funding transaction - **commitments_secured** (boolean): whether the *psbt* is complete (if true, sign *psbt* and call `openchannel_signed` to complete the channel open) diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md index b521e8cdc1e1..9006e687746b 100644 --- a/doc/lightning-parsefeerate.7.md +++ b/doc/lightning-parsefeerate.7.md @@ -18,6 +18,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **perkw** (u32, optional): Value of *feerate_str* in kilosipa [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index 8301fbb602ff..d473b3bf932c 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -94,6 +94,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **created_at** (number): the UNIX timestamp showing when this payment was initiated @@ -104,6 +105,7 @@ On success, an object is returned, containing: - **destination** (pubkey, optional): the final destination of the payment The following warnings may also be returned: + - **warning_partial_completion**: Not all parts of a multi-part payment have completed [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index d77129a4a5ef..5e71d4948a3f 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -37,6 +37,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **totlen** (u16): the answer length of the reply message (including header: 0 means no reply expected) [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index 82177d676207..51bea0c22c02 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -50,15 +50,18 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **command** (string): the subcommand this is responding to (one of "start", "stop", "rescan", "startdir", "list") If **command** is "start", "startdir", "rescan" or "list": + - **plugins** (array of objects): - **name** (string): full pathname of the plugin - **active** (boolean): status; plugin completed init and is operational, plugins are configured asynchronously. - **dynamic** (boolean): plugin can be stopped or started without restarting lightningd If **command** is "stop": + - **result** (string): A message saying it successfully stopped [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index c8562d838158..a76b0737acd3 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -26,6 +26,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **reservations** is returned. It is an array of objects, where each object contains: + - **txid** (txid): the transaction id - **vout** (u32): the output number which was reserved - **was_reserved** (boolean): whether the input was already reserved diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index 601e91e43cfa..56a99c212f1e 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -49,6 +49,7 @@ explicit error message stating the issue. [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **status** (string): Information about where message was queued [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-sendinvoice.7.md b/doc/lightning-sendinvoice.7.md index 8f3e262b1465..ad6ae3b5a9dd 100644 --- a/doc/lightning-sendinvoice.7.md +++ b/doc/lightning-sendinvoice.7.md @@ -40,6 +40,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice - **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) @@ -49,6 +50,7 @@ On success, an object is returned, containing: - **bolt12** (string, optional): the BOLT12 string If **status** is "paid": + - **pay_index** (u64): Unique incrementing index for this payment - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - **paid_at** (u64): UNIX timestamp of when it was paid diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index 1e64d5335464..fa7a72c3dd96 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -92,6 +92,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **id** (u64): unique ID for this payment attempt - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (could be complete if already sent previously) (one of "pending", "complete") @@ -105,9 +106,11 @@ On success, an object is returned, containing: - **partid** (u64, optional): the partid (if supplied) to sendonion/sendpay If **status** is "complete": + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "pending": + - **message** (string, optional): Monitor status with listpays or waitsendpay [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 57b4f24e4366..62228cc1e182 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -67,6 +67,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **id** (u64): unique ID for this payment attempt - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (could be complete if already sent previously) (one of "pending", "complete") @@ -81,9 +82,11 @@ On success, an object is returned, containing: - **bolt12** (string, optional): the bolt12 string (if supplied: **experimental-offers** only). If **status** is "complete": + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "pending": + - **message** (string): Monitor status with listpays or waitsendpay [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index b6473480bfb8..7c99b6524592 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -32,6 +32,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **tx** (hex): The raw transaction which was sent - **txid** (txid): The txid of the **tx** diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 76bfc0bfbea2..bc5951f440a2 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -67,6 +67,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **channels** is returned. It is an array of objects, where each object contains: + - **peer_id** (pubkey): The node_id of the peer - **channel_id** (hex): The channel_id of the channel (always 64 characters) - **fee_base_msat** (msat): The resulting feebase (this is the BOLT #7 name) diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index a9a23070b01e..7e649c98d0e5 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -48,6 +48,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **base** (u32): The fee_base_msat value - **ppm** (u32): The fee_proportional_millionths value - **channels** (array of objects): channel(s) whose rate is now set: diff --git a/doc/lightning-signmessage.7.md b/doc/lightning-signmessage.7.md index 93608b1dbdf5..8ebb95910a2e 100644 --- a/doc/lightning-signmessage.7.md +++ b/doc/lightning-signmessage.7.md @@ -20,6 +20,7 @@ RETURN VALUE ------------ [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **signature** (hex): The signature (always 128 characters) - **recid** (hex): The recovery id (0, 1, 2 or 3) (always 2 characters) - **zbase** (string): *signature* and *recid* encoded in a style compatible with **lnd**'s [SignMessageRequest](https://api.lightning.community/#grpc-request-signmessagerequest) diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index 6ebc481dd830..46c6f71b32e0 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -40,6 +40,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **signed_psbt** (string): The fully signed PSBT [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-txdiscard.7.md b/doc/lightning-txdiscard.7.md index b0475e342e4a..7757ecb10cc3 100644 --- a/doc/lightning-txdiscard.7.md +++ b/doc/lightning-txdiscard.7.md @@ -17,6 +17,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **unsigned_tx** (hex): the unsigned transaction - **txid** (txid): the transaction id of *unsigned_tx* diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index c22437fd5c37..a1d159d473a4 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -54,6 +54,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **psbt** (string): the PSBT representing the unsigned transaction - **unsigned_tx** (hex): the unsigned transaction - **txid** (txid): the transaction id of *unsigned_tx*; you hand this to lightning-txsend(7) or lightning-txdiscard(7), as the inputs of this transaction are reserved. diff --git a/doc/lightning-txsend.7.md b/doc/lightning-txsend.7.md index 2d2673093a92..31e3a1c4f745 100644 --- a/doc/lightning-txsend.7.md +++ b/doc/lightning-txsend.7.md @@ -17,6 +17,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **psbt** (string): the completed PSBT representing the signed transaction - **tx** (hex): the fully signed transaction - **txid** (txid): the transaction id of *tx* diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index 323f24f63e0b..1a7750befe61 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -23,12 +23,14 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **reservations** is returned. It is an array of objects, where each object contains: + - **txid** (txid): the transaction id - **vout** (u32): the output number which was reserved - **was_reserved** (boolean): whether the input was already reserved (usually `true`) - **reserved** (boolean): whether the input is now reserved (may still be `true` if it was reserved for a long time) If **reserved** is *true*: + - **reserved_to_block** (u32): what blockheight the reservation will expire [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index 1bf65387c3ae..129e0d53749e 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -45,6 +45,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **psbt** (string): Unsigned PSBT which fulfills the parameters given - **feerate_per_kw** (u32): The feerate used to create the PSBT, in satoshis-per-kiloweight - **estimated_final_weight** (u32): The estimated weight of the transaction once fully signed diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index a19b6850ddff..87c2888e87a1 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -35,6 +35,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) @@ -45,6 +46,7 @@ On success, an object is returned, containing: - **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is) If **status** is "paid": + - **pay_index** (u64): Unique incrementing index for this payment - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - **paid_at** (u64): UNIX timestamp of when it was paid diff --git a/doc/lightning-waitblockheight.7.md b/doc/lightning-waitblockheight.7.md index b3405d2ba238..254c65fb7b2e 100644 --- a/doc/lightning-waitblockheight.7.md +++ b/doc/lightning-waitblockheight.7.md @@ -21,6 +21,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **blockheight** (u32): The current block height (>= *blockheight* parameter) [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-waitinvoice.7.md b/doc/lightning-waitinvoice.7.md index b3b645b3fcb2..4a14473d466f 100644 --- a/doc/lightning-waitinvoice.7.md +++ b/doc/lightning-waitinvoice.7.md @@ -17,6 +17,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) @@ -27,6 +28,7 @@ On success, an object is returned, containing: - **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is) If **status** is "paid": + - **pay_index** (u64): Unique incrementing index for this payment - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - **paid_at** (u64): UNIX timestamp of when it was paid diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index 1801159b33cf..708e6068dc60 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -34,6 +34,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **id** (u64): unique ID for this payment attempt - **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (always "complete") @@ -48,6 +49,7 @@ On success, an object is returned, containing: - **bolt12** (string, optional): the bolt12 string (if supplied for pay: **experimental-offers** only). If **status** is "complete": + - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index e71f951348aa..4d16430d6cc5 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -42,6 +42,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **tx** (hex): the fully signed bitcoin transaction - **txid** (txid): the transaction id of *tx* - **psbt** (string): the PSBT representing the unsigned transaction diff --git a/tools/fromschema.py b/tools/fromschema.py index a1291e897058..ce818a3b1b62 100755 --- a/tools/fromschema.py +++ b/tools/fromschema.py @@ -195,7 +195,7 @@ def output_members(sub, indent=''): json_value(whichvalues[-1])) conditions.append(cond) - sentence = indent + "If " + ", and ".join(conditions) + ":\n" + sentence = indent + "If " + ", and ".join(conditions) + ":\n\n" if has_members(ifclause['then']): # Prefix with blank line. @@ -229,23 +229,23 @@ def generate_from_schema(schema): 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])) + output('On success, an object containing **{}** is returned. It is an object containing:\n\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])) + output('On success, an object containing **{}** is returned. It is an array of objects, where each object contains:\n\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') + output('On success, an object is returned, containing:\n\n') sub = schema output_members(sub) if warnings: - outputs(['\n', 'The following warnings may also be returned:\n']) + outputs(['\n', 'The following warnings may also be returned:\n\n']) for w, desc in warnings: output("- **{}**: {}\n".format(w, desc)) From bcabb3825fd9daa351f1d56db23eb7dde8c9ab21 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 07:15:04 +0930 Subject: [PATCH 1297/1530] Makefile: Revert ba7d4a8f6bab5a3d5e5832d1f9e36749e695320a (make-schema: don't include tools/fromschema.py in SHASUMS) 1. If the tool changes, you need to regenerate since the output may change. 2. This didn't just filter that out, ignored all but the first dependency, which made bisecting the bookkeeper plugin a nightmare: it didn't regenerate the .po file, causing random crashes. If we want this, try $(filter-out tools/fromschema.py) instead. But I don't think we want that. Signed-off-by: Rusty Russell --- Makefile | 10 ++-------- doc/Makefile | 2 +- doc/lightning-addgossip.7.md | 2 +- doc/lightning-autocleaninvoice.7.md | 2 +- doc/lightning-bkpr-channelsapy.7.md | 3 ++- doc/lightning-bkpr-dumpincomecsv.7.md | 3 ++- doc/lightning-bkpr-inspect.7.md | 3 ++- doc/lightning-bkpr-listaccountevents.7.md | 6 +++++- doc/lightning-bkpr-listbalances.7.md | 4 +++- doc/lightning-bkpr-listincome.7.md | 3 ++- doc/lightning-check.7.md | 2 +- doc/lightning-checkmessage.7.md | 2 +- doc/lightning-close.7.md | 2 +- doc/lightning-commando-rune.7.md | 3 ++- doc/lightning-connect.7.md | 2 +- doc/lightning-createinvoice.7.md | 2 +- doc/lightning-createonion.7.md | 2 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-decode.7.md | 4 +++- doc/lightning-decodepay.7.md | 2 +- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-delexpiredinvoice.7.md | 2 +- doc/lightning-delinvoice.7.md | 2 +- doc/lightning-delpay.7.md | 2 +- doc/lightning-disableoffer.7.md | 2 +- doc/lightning-disconnect.7.md | 2 +- doc/lightning-feerates.7.md | 2 +- doc/lightning-fetchinvoice.7.md | 2 +- doc/lightning-fundchannel.7.md | 2 +- doc/lightning-fundchannel_cancel.7.md | 2 +- doc/lightning-fundchannel_complete.7.md | 2 +- doc/lightning-fundchannel_start.7.md | 2 +- doc/lightning-funderupdate.7.md | 2 +- doc/lightning-fundpsbt.7.md | 2 +- doc/lightning-getinfo.7.md | 2 +- doc/lightning-getlog.7.md | 2 +- doc/lightning-getroute.7.md | 2 +- doc/lightning-help.7.md | 2 +- doc/lightning-invoice.7.md | 2 +- doc/lightning-keysend.7.md | 2 +- doc/lightning-listchannels.7.md | 2 +- doc/lightning-listconfigs.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listforwards.7.md | 2 +- doc/lightning-listfunds.7.md | 2 +- doc/lightning-listinvoices.7.md | 2 +- doc/lightning-listnodes.7.md | 2 +- doc/lightning-listoffers.7.md | 2 +- doc/lightning-listpays.7.md | 2 +- doc/lightning-listpeers.7.md | 2 +- doc/lightning-listsendpays.7.md | 2 +- doc/lightning-listtransactions.7.md | 2 +- doc/lightning-makesecret.7.md | 2 +- doc/lightning-multifundchannel.7.md | 2 +- doc/lightning-multiwithdraw.7.md | 2 +- doc/lightning-newaddr.7.md | 2 +- doc/lightning-notifications.7.md | 2 +- doc/lightning-offer.7.md | 2 +- doc/lightning-offerout.7.md | 2 +- doc/lightning-openchannel_abort.7.md | 2 +- doc/lightning-openchannel_bump.7.md | 2 +- doc/lightning-openchannel_init.7.md | 2 +- doc/lightning-openchannel_signed.7.md | 2 +- doc/lightning-openchannel_update.7.md | 2 +- doc/lightning-parsefeerate.7.md | 2 +- doc/lightning-pay.7.md | 2 +- doc/lightning-ping.7.md | 2 +- doc/lightning-plugin.7.md | 2 +- doc/lightning-reserveinputs.7.md | 2 +- doc/lightning-sendcustommsg.7.md | 2 +- doc/lightning-sendinvoice.7.md | 2 +- doc/lightning-sendonion.7.md | 2 +- doc/lightning-sendonionmessage.7.md | 2 +- doc/lightning-sendpay.7.md | 2 +- doc/lightning-sendpsbt.7.md | 2 +- doc/lightning-setchannel.7.md | 2 +- doc/lightning-setchannelfee.7.md | 2 +- doc/lightning-signmessage.7.md | 2 +- doc/lightning-signpsbt.7.md | 2 +- doc/lightning-stop.7.md | 2 +- doc/lightning-txdiscard.7.md | 2 +- doc/lightning-txprepare.7.md | 2 +- doc/lightning-txsend.7.md | 2 +- doc/lightning-unreserveinputs.7.md | 2 +- doc/lightning-utxopsbt.7.md | 2 +- doc/lightning-waitanyinvoice.7.md | 2 +- doc/lightning-waitblockheight.7.md | 2 +- doc/lightning-waitinvoice.7.md | 2 +- doc/lightning-waitsendpay.7.md | 2 +- doc/lightning-withdraw.7.md | 2 +- plugins/bkpr/Makefile | 4 ++-- wallet/Makefile | 4 ++-- 92 files changed, 108 insertions(+), 101 deletions(-) diff --git a/Makefile b/Makefile index 6730f97cdafe..5d21d7f51e5b 100644 --- a/Makefile +++ b/Makefile @@ -317,19 +317,13 @@ endif ifeq ($(SUPPRESS_GENERATION),1) SHA256STAMP_CHANGED = false SHA256STAMP = exit 1 -SHA256STAMP_CHANGED_ALL = false -SHA256STAMP_ALL = exit 1 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:\([a-f0-9]*\).*/\1/p' $@ 2>/dev/null`" != x"`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)" >> $@ - -SHA256STAMP_CHANGED_ALL = [ 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_ALL = echo "$(1) SHA256STAMP:"`cat $(sort $(filter-out FORCE,$^)) | $(SHA256SUM) | cut -c1-64`"$(2)" >> $@ +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 diff --git a/doc/Makefile b/doc/Makefile index 959ca52e31bb..a9b0b61d1ac1 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -126,7 +126,7 @@ $(MARKDOWN_WITH_SCHEMA): doc/lightning-%.7.md: doc/schemas/%.schema.json tools/f @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 $(MANPAGES): doc/%: doc/%.md tools/md2man.sh version_gen.h - @if $(call SHA256STAMP_CHANGED_ALL); then $(call VERBOSE, "md2man $<", VERSION=$(VERSION) tools/md2man.sh $< > $@ && $(call SHA256STAMP_ALL,\\\",)); else touch $@; fi + @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "md2man $<", VERSION=$(VERSION) tools/md2man.sh $< > $@ && $(call SHA256STAMP,\\\",)); else touch $@; fi $(MANPAGES): $(FORCE) $(MARKDOWN_WITH_SCHEMA): $(FORCE) diff --git a/doc/lightning-addgossip.7.md b/doc/lightning-addgossip.7.md index a23abf111ce4..44620dde0cd6 100644 --- a/doc/lightning-addgossip.7.md +++ b/doc/lightning-addgossip.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c801b02463804504c2387387a36a6739351330a2c496aaa10de2b1f49c36ed32) +[comment]: # ( SHA256STAMP:bdc678495e0e0e626cdc71e73e8179dc8350de9862c4458933607fa62d8a16f7) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index 5f629dc30ddf..241026c79c9a 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -52,4 +52,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c5aedf100597dd0bd1bbbdf82964327035cd49df00d7b0aef6454e3b1ef39dbc) +[comment]: # ( SHA256STAMP:43767e7e2dde51d1d5a250d3ffac510cf53305f11d9984a9f37ceb6b28c386ea) diff --git a/doc/lightning-bkpr-channelsapy.7.md b/doc/lightning-bkpr-channelsapy.7.md index 7eac494ce1dc..5be11265ea55 100644 --- a/doc/lightning-bkpr-channelsapy.7.md +++ b/doc/lightning-bkpr-channelsapy.7.md @@ -22,6 +22,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **channels_apy** is returned. It is an array of objects, where each object contains: + - **account** (string): The account name. If the account is a channel, the channel_id. The 'net' entry is the rollup of all channel accounts - **routed_out_msat** (msat): Sats routed (outbound) - **routed_in_msat** (msat): Sats routed (inbound) @@ -64,4 +65,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:435fd03765ef0a8bcaef7f309673cdac9cb7c8ba776ac77de21aea8d702998a3) +[comment]: # ( SHA256STAMP:c86f82cd78e639f70e640c4d36e0a28a5087a74c931207501d9121049ff9b5d2) diff --git a/doc/lightning-bkpr-dumpincomecsv.7.md b/doc/lightning-bkpr-dumpincomecsv.7.md index 70c8a7458637..42967be3068b 100644 --- a/doc/lightning-bkpr-dumpincomecsv.7.md +++ b/doc/lightning-bkpr-dumpincomecsv.7.md @@ -34,6 +34,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: + - **csv_file** (string): File that the csv was generated to - **csv_format** (string): Format to print csv as (one of "cointracker", "koinly", "harmony", "quickbooks") @@ -56,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e6000f40905c4fa23a5115e8bc82f4f1556f55118fb4ede41dd5e54957da0fa3) +[comment]: # ( SHA256STAMP:13920567775dacf15991bc281ccac0bf3f80599e87c3df9e73ca4564445ed6eb) diff --git a/doc/lightning-bkpr-inspect.7.md b/doc/lightning-bkpr-inspect.7.md index 4f5785bf7ff9..865d314845e0 100644 --- a/doc/lightning-bkpr-inspect.7.md +++ b/doc/lightning-bkpr-inspect.7.md @@ -18,6 +18,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **txs** is returned. It is an array of objects, where each object contains: + - **txid** (txid): transaction id - **fees_paid_msat** (msat): Amount paid in sats for this tx - **outputs** (array of objects): @@ -51,4 +52,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9df98d40e1ed1b0c72f4a4e8c00d243e10f159b99c534818f04631ec3d17a445) +[comment]: # ( SHA256STAMP:a89c90315d210fcf1fd45bf4edef04a72165834867f860447e33d89840eac59f) diff --git a/doc/lightning-bkpr-listaccountevents.7.md b/doc/lightning-bkpr-listaccountevents.7.md index ad350854f440..113d3d4c1b7b 100644 --- a/doc/lightning-bkpr-listaccountevents.7.md +++ b/doc/lightning-bkpr-listaccountevents.7.md @@ -24,6 +24,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **events** is returned. It is an array of objects, where each object contains: + - **account** (string): The account name. If the account is a channel, the channel_id - **type** (string): Coin movement type (one of "onchain_fee", "chain", "channel") - **tag** (string): Description of movement @@ -33,6 +34,7 @@ On success, an object containing **events** is returned. It is an array of obje - **timestamp** (u32): Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp If **type** is "chain": + - **outpoint** (string): The txid:outnum for this event - **blockheight** (u32): For chain events, blockheight this occured at - **origin** (string, optional): The account this movement originated from @@ -41,9 +43,11 @@ If **type** is "chain": - **description** (string, optional): The description of this event If **type** is "onchain_fee": + - **txid** (txid): The txid of the transaction that created this event If **type** is "channel": + - **fees_msat** (msat, optional): Amount paid in fees - **is_rebalance** (boolean, optional): Is this payment part of a rebalance - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. @@ -67,4 +71,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8568188808cb649d7182ffb628950b93b18406a0498b5b6768371bc94375e258) +[comment]: # ( SHA256STAMP:50051a63881ea83154a36bbdccaf3915601574ed193ade6a5522260c85fa941c) diff --git a/doc/lightning-bkpr-listbalances.7.md b/doc/lightning-bkpr-listbalances.7.md index 80f12297a5a1..c0e4977ccd9a 100644 --- a/doc/lightning-bkpr-listbalances.7.md +++ b/doc/lightning-bkpr-listbalances.7.md @@ -20,12 +20,14 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **accounts** is returned. It is an array of objects, where each object contains: + - **account** (string): The account name. If the account is a channel, the channel_id - **balances** (array of objects): - **balance_msat** (msat): Current account balance - **coin_type** (string): coin type, same as HRP for bech32 If **peer_id** is present: + - **peer_id** (pubkey): Node id for the peer this account is with - **we_opened** (boolean): Did we initiate this account open (open the channel) - **account_closed** (boolean): @@ -51,4 +53,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a3d1423f12bffc76fd1f2fdb5a07ff8a881290f2ea5eefa528cbb04fc3a7c639) +[comment]: # ( SHA256STAMP:42302ecee2bab01fca04477e5b4d793f11757e0b7a2f43c6878ff8ecf857cc34) diff --git a/doc/lightning-bkpr-listincome.7.md b/doc/lightning-bkpr-listincome.7.md index dfc3647e1079..d35db63aeb46 100644 --- a/doc/lightning-bkpr-listincome.7.md +++ b/doc/lightning-bkpr-listincome.7.md @@ -27,6 +27,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **income_events** is returned. It is an array of objects, where each object contains: + - **account** (string): The account name. If the account is a channel, the channel_id - **tag** (string): Type of income event - **credit_msat** (msat): Amount earned (income) @@ -56,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ab8508af0f40587c5a804f6981591564fe2d18b4fe3fbe7793e6a489607f7e0a) +[comment]: # ( SHA256STAMP:67cedc19eeb5d684cda2efd025e42d1d89fd1d899d8b66ac0d9b663139cc3517) diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index 13858422d3c5..cd5fc4a0f5dd 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -41,4 +41,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c676fc0dbfdabc729b3fdbee7e64c91f8180100c9945db06cbaed955a3d63cfc) +[comment]: # ( SHA256STAMP:74e2865a8316ad266499fc9a0ce2aa7ce9794da90d7039f91150c0560df901f1) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index 0ca51979e466..c0058e557c2e 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -50,4 +50,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:733247e44d555f9c480a684ceb30440f4f33daf5755253249b5c7b9269c96e49) +[comment]: # ( SHA256STAMP:aa3f01d9f9c1fa61f8b4fc850c3a982e3ea57abcb33338ad36930bb5c70124ce) diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 5757bc156fc4..61a33083f55b 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -135,4 +135,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3b645a2105f2b428889d097283cb56ba9c8d6cba90343ac779f2fb6e26a1c202) +[comment]: # ( SHA256STAMP:54eec92e876d0e05041f708cd8453a16cc1a178df8c966620f8bf6536ba6e36d) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index c9561a6ad8cb..1304a715ac7f 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -195,6 +195,7 @@ On success, an object is returned, containing: - **unique_id** (string): the id of this rune: this is set at creation and cannot be changed (even as restrictions are added) The following warnings may also be returned: + - **warning_unrestricted_rune**: A warning shown when runes are created with powers that could drain your node [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -218,4 +219,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:34c6d5222fee79f4648be4a717041d32004b5bb3644364dc6569b87b16ed2ebe) +[comment]: # ( SHA256STAMP:b9cf90aabe3e87fb41bc1da93d9efd3e6dc629be487f65fb815bed3bad4adb0b) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index d68d08aae300..345d5c99bd96 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -104,4 +104,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ad52e3b3042a8910c106e0730d9d54d09ebdd3cffdb6ba3fd776f8ec4be57e46) +[comment]: # ( SHA256STAMP:98dfa22262e5192ef0fa14b0a9ede95f932a6f0966f847bdfd87b1d1a1b37789) diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index a6dfe3bd21db..abb6c925138a 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -75,4 +75,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:34ba2cc01db3e516b257dbe6a47cf764d1e34b85ee89fd4b49aed1fc2e0bb0ba) +[comment]: # ( SHA256STAMP:56c128b40e64b4433487be292f99e4f68576e80466cc4a5b544572465901bb64) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index f2f4286063fb..e8f136ba4ea2 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -133,4 +133,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d313a15317ee4b09d151312071fb6aa2f7fc16128ca4485096783347bffdeca2) +[comment]: # ( SHA256STAMP:9c19a1960c2d7db91b312ab7e738395e36260130e093a576c1c79dba40d25b59) diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index 39bce1bbeea0..796abce1ee0e 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -66,4 +66,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ce6a72f08dd7f6026eab58bb6ea732aead4512e714fab4f9a1cf54cdb1374f59) +[comment]: # ( SHA256STAMP:3319dbcf6a05a45cb0a28fddb27050ddd7139c8ed1537c8204e8c722b5acd65e) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index b6464ab9b2b1..ad2b21006fab 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -172,6 +172,7 @@ If **type** is "bolt11 invoice", and **valid** is *true*: - **data** (string): The bech32 data for this field If **type** is "rune", and **valid** is *true*: + - **valid** (boolean) (always *true*) - **string** (string): the string encoding of the rune - **restrictions** (array of objects): restrictions built into the rune: all must pass: @@ -182,6 +183,7 @@ If **type** is "rune", and **valid** is *true*: - **version** (string, optional): rune version, not currently set on runes we create If **type** is "rune", and **valid** is *false*: + - **valid** (boolean) (always *false*) - **hex** (hex, optional): the raw rune in hex - the following warnings are possible: @@ -209,4 +211,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3e522a9788bb79302e4c4386c3937b7dcd8359d1b894364ac3e884bd3f695850) +[comment]: # ( SHA256STAMP:2a6cc144b0f2436cc87886cf80f635f7c37e70c5a82e88ad7439ba660046523a) diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index 42f26c39e7f1..e7bb66745393 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -71,4 +71,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e6e27ff57a8e26db5d4bba8be2a5faa6f1c58b20ef625f371e6e66d17932f8a0) +[comment]: # ( SHA256STAMP:120150143d95a3f3b9685fc07c9630b5721d903ce4b85159242df3a804201698) diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index 16339e752eea..b8e0d882ef86 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -49,4 +49,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:83a7e89ed44c6dbf744d1327337e29393f9095628bd95fffe59ec5014ee0a483) +[comment]: # ( SHA256STAMP:6037971086a82e77e86621112e806e7b061c17cd5d5f9f82dff0af734f3965e5) diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index 67b15092a3a2..5689eb61c48a 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -38,4 +38,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c71baf4b5863fd6f4d2ba21a97d4106195ba10c5add21087142b1a5ee533da91) +[comment]: # ( SHA256STAMP:23050bf2eaaf606c63069c7686f45b69133cb2c0827537eee4367921b897842f) diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 0762528b38e6..c8d9f352268f 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -81,4 +81,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a24e80716475dea15f9762cf50382f21e3e09b803a669e217923d7019cd526e0) +[comment]: # ( SHA256STAMP:48ff64ba8dcb03b0e727f962b7b16a3768dd2382e5a3414c7ad243884b674667) diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index f242685448b3..919b1e5d722d 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:ecdf9fe432142054328abb6aa3f76b726968dd28db1fc26875a81f291e10135a) +[comment]: # ( SHA256STAMP:dbe8ab5ab8d0d5ab602fac4af3a567bd1f7899f25304683323da6621b8196213) diff --git a/doc/lightning-disableoffer.7.md b/doc/lightning-disableoffer.7.md index c42bfcc8422a..6e7c5541f64a 100644 --- a/doc/lightning-disableoffer.7.md +++ b/doc/lightning-disableoffer.7.md @@ -75,4 +75,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:d91f424d5374fd26d4d85df10f9e5eb092e5b0e1bac8dae44b98d844a55b6e22) +[comment]: # ( SHA256STAMP:17db907802f8417021d203710d1f4bb9e171f6d64952e8b6608d2875a947edb0) diff --git a/doc/lightning-disconnect.7.md b/doc/lightning-disconnect.7.md index 8f3fe0e163cd..feac6b7770de 100644 --- a/doc/lightning-disconnect.7.md +++ b/doc/lightning-disconnect.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c801b02463804504c2387387a36a6739351330a2c496aaa10de2b1f49c36ed32) +[comment]: # ( SHA256STAMP:bdc678495e0e0e626cdc71e73e8179dc8350de9862c4458933607fa62d8a16f7) diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index bc5cd90fc110..b29ba413c761 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -121,4 +121,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:96fde8cf67c9b0dda5a1866dfadfb1d7da7e593b3080948662070382d4a9537f) +[comment]: # ( SHA256STAMP:25001249ab0ced5ea1fa3520fd1f090eb1b16b1a43a49452d39bffa33a49f009) diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index dbf168090973..696114f473df 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -89,4 +89,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:83fcb7141eefc0e5d5bd1d23f6d05f1d32514bad53ed52fc61604ce049c75d54) +[comment]: # ( SHA256STAMP:336245434880c274390e89e464ee7731508059c2e4614e1e0dbaeccdc6a315bc) diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index ae0bb1521bb7..ed27a4b80527 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -115,4 +115,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d396512cad4bfd533dda947a12aee0aecda05e39c2a7ad7e6b04a3602fbae85d) +[comment]: # ( SHA256STAMP:c4e05ea6ec3e86b5506c7531d60809a165a2b84e97c92e57387868f34dd0b9db) diff --git a/doc/lightning-fundchannel_cancel.7.md b/doc/lightning-fundchannel_cancel.7.md index 474f1fbb8a00..d7911199731a 100644 --- a/doc/lightning-fundchannel_cancel.7.md +++ b/doc/lightning-fundchannel_cancel.7.md @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a670eb1f4475ad33b3fc20994205adbf12fad8bb93e5e805e0b8a8ea1db15136) +[comment]: # ( SHA256STAMP:ac234f1fece4dd8d1a9b9af2e053088e83fa4c7e10fb5f9b99db9c4607df3228) diff --git a/doc/lightning-fundchannel_complete.7.md b/doc/lightning-fundchannel_complete.7.md index b4949c085d09..f6f46fa66e1c 100644 --- a/doc/lightning-fundchannel_complete.7.md +++ b/doc/lightning-fundchannel_complete.7.md @@ -62,4 +62,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f7b14faee0218a2eee7c0df17c52b1d4502c898d1767644bd891027116eb8868) +[comment]: # ( SHA256STAMP:7d1740964e0509e5bc36b22dd9612cb8fc4e5d345cbb8e92b1e03295c7d36075) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index 6ef71d3e8c38..e697c9bed8ac 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -85,4 +85,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5062025dc172a552f1a33fd1e3600c0fbfc1405f030ea049da88da30a5035456) +[comment]: # ( SHA256STAMP:56c7001a34cee3ee79c18c8d0b71979a1f140fdc247222c0b05c4bd2b7f142e5) diff --git a/doc/lightning-funderupdate.7.md b/doc/lightning-funderupdate.7.md index 842957e00a11..424a943a8c51 100644 --- a/doc/lightning-funderupdate.7.md +++ b/doc/lightning-funderupdate.7.md @@ -147,4 +147,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7fb486ed4c0ec0e72c51bc5a167a1280a9572c61c9467b5819e6624a54877cfb) +[comment]: # ( SHA256STAMP:b07236a4cbe24e7aa13324002b044dc66b5457405cb9e448eaeeafe3c729cab4) diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 5a5aa8e2d571..07740e217058 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -115,4 +115,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:0cd239969d70c9261f0d7309762e3d2e646ddd0c62dc7dd8b08515c03cc5385b) +[comment]: # ( SHA256STAMP:f687ab47b409b04ff97ad3bdb42880b23dfe4e0bb38f54a64b74e1a16c07ee81) diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 8575c99c8c45..20ae5b46db61 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -131,4 +131,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:aedad2e6949f96d8d331e39c17effe5786816729562987b058d146b4b94286cb) +[comment]: # ( SHA256STAMP:1d3258e769579e140755acc9808b9008b0f81fc0ded50311ee64ba628aee04ad) diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index c0c3c22b578f..0163bc58b9d5 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -94,4 +94,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:46482f3cc6b332b284cae1b1e5477b19e227cb6c1cd1086452d28f0887433cb1) +[comment]: # ( SHA256STAMP:778a192de637d247689c270b3cdc5b100baa749a866093b7a5c709b546e53c2c) diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index ef2837170800..7ab5efa77fc7 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -310,4 +310,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:db8834d9a318688d2c4801b85f06aa5afb3120e064b3654433469bca09e776a0) +[comment]: # ( SHA256STAMP:95668bff3a9ec23bd7cebfc60a6af56bce0e6559fa8218b14390017b1348ef61) diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index 9e0e2ec8e110..f6621a39fdbf 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -68,4 +68,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:881f00fb7943bc1655c054488643a4da37742b6705924f0ba33366e8cf3f2b93) +[comment]: # ( SHA256STAMP:288d00779f245c6e07fb7f6826e44b78a6dad296e0cd92855bf419523bbe2024) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index bc20985b9f07..5e04421fd8a8 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -119,4 +119,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ea76df1915a45de45039cbbe8add3fe86416f7cba133d8f0d364d28ef276198c) +[comment]: # ( SHA256STAMP:c040289df896608002a560a3bf4213f155ca81aacfe2de45771c2dcba1517b98) diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index 90e4d404bb96..148b54d21262 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -116,4 +116,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f8e12220302756c5a95eef2ec428c1d7adaba3025b0716e6b6581f783d92b648) +[comment]: # ( SHA256STAMP:17d0a04f94d00f4f842cd1924f490678167498559098b27bc4ac29dd907c3af1) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index ae63825a0c90..648b097c0fc0 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -79,4 +79,4 @@ Lightning RFC site - BOLT \#7: -[comment]: # ( SHA256STAMP:43c6c45c0672482610c1bdd607d5bf6ed26d6c42cfc4fbde4a5d24f2eb1d9a2d) +[comment]: # ( SHA256STAMP:332e2a7c3f544a1cfc2a1a5e0728d54756249852f4060ca9fd47c137263b1e9d) diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 3820f6945412..9ebe710a02d6 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -215,4 +215,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:999502771ada48f32011ea4df2443a2a3385d27377d8e55ec82cf283f9acd0a6) +[comment]: # ( SHA256STAMP:6df5cfe511b7223a7a39fab1f36dcb46d8caef2726d9dbdf6c892a03cf4369f0) diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index 0498c7155f0d..5b4125e2efda 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -47,4 +47,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6c6f4c08a88b2a8c8cdc4c36783da86d8e8de0d5ee39261f3304e0eb41f0eb41) +[comment]: # ( SHA256STAMP:3b6eb48324fe4bab31a40460af1d5c98b8a938f27c1af758b15c955bb2204e58) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 463bee4f3fb7..269aa8e25aca 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -63,4 +63,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:de237318dfea0b02d6ca34710432a3b739012beb84f74e41e720cd9889675954) +[comment]: # ( SHA256STAMP:cc82cc624fd377f957a83e1d3a49607a7cfa3c87505ba70a3f3fa07d6d922089) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index a992902935a6..02cc9860586b 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:61be4b00c0485edccb986e1f066cf18a5c9fdb8bca94adc12f0a87a36041a93a) +[comment]: # ( SHA256STAMP:999fb813822e397fdb22dc79cfb5b766e79766073a8c2803a47b56ea4786fb8e) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index 983b68575166..04eff6ca1e9e 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7e45fcb50a446f35e441df4a6c04626a045d237407231bde044c95aabc689519) +[comment]: # ( SHA256STAMP:06dceb4701a60fc36d9823c107fc34d2b2d5d13cb7c60ae0ec1db3128cffa55f) diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index f8d1bb95c172..a46fdfb08aba 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -99,4 +99,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:df4e056ac91672041b38811329eb7555636c7b4d4985456894255a7b8e11bf54) +[comment]: # ( SHA256STAMP:eca3d33813f3b3169317b8c4f573663416dd0e87d050a14feff64b754a70c2cb) diff --git a/doc/lightning-listoffers.7.md b/doc/lightning-listoffers.7.md index 4dcd47e751ca..037a215f53b8 100644 --- a/doc/lightning-listoffers.7.md +++ b/doc/lightning-listoffers.7.md @@ -81,4 +81,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:aad235e8255da495032f638a7066b05d651e2c99b668f7b3afffb21d908c788a) +[comment]: # ( SHA256STAMP:c8335d88dde3b056b69d4d4b3cb819844dbbb219410fb0277a148fcf7b4d2ed6) diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index b1a355e13e96..e04180edaf03 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -61,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ec7234aa1ec9979cdc71a5bfe861296ba90efdf30236922a822b2ecab6fd9635) +[comment]: # ( SHA256STAMP:074ced29c00a0aff31098ccaae1c1f56364cc3aaaad34485a7c6b1c3cd9b3670) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index b42256839012..ab9dbe7a15a7 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -399,4 +399,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:971c71befa1b6968a66b65e2aab4a2d1707f14c9af5d6ebc2dc00604d1d91294) +[comment]: # ( SHA256STAMP:c99d53a4b3ada3bae6972f97eea600b7a039958313772cd6370eec818c2e4a99) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 6d785936841b..bf36a15e1d2d 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:fa2e28ae1fdc4df5357ecc12984b2ec478cd591868484613dccf455cbc502fd9) +[comment]: # ( SHA256STAMP:0a901adcb1b30622fdab38aa39a5073087488b2ad446fb1b9efdc791ac954591) diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index 42d5d2196d2b..f02023e8ad66 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -105,4 +105,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:4d5d2f1cea0668b3e58e73a93fe6d217ac4a8c740bed09fcdce21c9e72daae99) +[comment]: # ( SHA256STAMP:9cb66a3febe7c37e1892e2fc9f486a6e896e9406a1d9514e5283d4c5f8f6b707) diff --git a/doc/lightning-makesecret.7.md b/doc/lightning-makesecret.7.md index 7e5c4798240f..532a16c0c4e9 100644 --- a/doc/lightning-makesecret.7.md +++ b/doc/lightning-makesecret.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1bd94ffa8440041efafe93440d9828be6baca199b0f5cb73220e4482582bf01d) +[comment]: # ( SHA256STAMP:c011f9e0b3ebd79b4172daf518d216dda9149c86e23918d0fe979f83b09fd117) diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index c2f544264970..0b60d6e599ac 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -159,4 +159,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:49c7f69eb4b532802c465ac5d0421cdad176f6a28c4c8e4c1c3ee0a94faad5bf) +[comment]: # ( SHA256STAMP:19cdc2bffdec420ae1fc48591af22a49d2d85104d6a14dcc166909f23e86ecfa) diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index d0817b87a0fd..0d81e230831b 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:1fe5147036f714c8f9185dd482a1bfa3e3ac5c4d0b6603fba1bc2b78de591b8f) +[comment]: # ( SHA256STAMP:e2b8a4f88c61ef7823e1b8541719d2d5ccb05181116785ac8e9be02816d65c0d) diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index 95effec8c2e2..98b8d4ed3478 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:550089858649865ed4d23384dcc5deeef314f5a1976a9610e611dbe17c1063d6) +[comment]: # ( SHA256STAMP:7bf0734c6f42effdd936443ddc7eace065616bb345efcb3d76850fe7d07052c0) diff --git a/doc/lightning-notifications.7.md b/doc/lightning-notifications.7.md index 0bf4ddcbf03f..9d03be3bf449 100644 --- a/doc/lightning-notifications.7.md +++ b/doc/lightning-notifications.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:c801b02463804504c2387387a36a6739351330a2c496aaa10de2b1f49c36ed32) +[comment]: # ( SHA256STAMP:bdc678495e0e0e626cdc71e73e8179dc8350de9862c4458933607fa62d8a16f7) diff --git a/doc/lightning-offer.7.md b/doc/lightning-offer.7.md index 23f6a0c3f8b7..01675a63e426 100644 --- a/doc/lightning-offer.7.md +++ b/doc/lightning-offer.7.md @@ -135,4 +135,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3b7b337e724de4cd867dbbf65700a20d92db728892394f4d0229af70659fd7e2) +[comment]: # ( SHA256STAMP:b4a362fe13f683f82bda5a293805d5d506719382c846ad363c357fef4a5cef7c) diff --git a/doc/lightning-offerout.7.md b/doc/lightning-offerout.7.md index d07d4d827f4a..5430a81182a4 100644 --- a/doc/lightning-offerout.7.md +++ b/doc/lightning-offerout.7.md @@ -100,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4ef2ac36e19f11e81645fb0b94fe7f4d7dad74faf7c77628e29967d9f1192154) +[comment]: # ( SHA256STAMP:1aeec6f596cae41738c39a12c02ee1c899569953f5be9378a06c9911082de1db) diff --git a/doc/lightning-openchannel_abort.7.md b/doc/lightning-openchannel_abort.7.md index 1c6259c2fac1..e088c1d6f1c6 100644 --- a/doc/lightning-openchannel_abort.7.md +++ b/doc/lightning-openchannel_abort.7.md @@ -55,4 +55,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:014c926a7202b951a2407b1a9996c90d4dd68aadfdbdb04311b280c016d538dc) +[comment]: # ( SHA256STAMP:ea00b648aba89bc585ae67bfcc821dabda2c2ffea6b0dffab80ba46edaf2e3ba) diff --git a/doc/lightning-openchannel_bump.7.md b/doc/lightning-openchannel_bump.7.md index cd5c5c2ca6ae..2160673728da 100644 --- a/doc/lightning-openchannel_bump.7.md +++ b/doc/lightning-openchannel_bump.7.md @@ -81,4 +81,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:2846dc5350c8a1ac5d0e33fc52a37f5e535e6db5b4080ae2be9c3df428ccf122) +[comment]: # ( SHA256STAMP:ba49815f17a30831671c7253051b15192c55882b524f4999ae0928829bd3fff7) diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index 2ea017ecadf3..7c48a3b1fb7a 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -103,4 +103,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:70ebfff5ce81962cf574b39cb3d95206b1f1541d24b8a3a0380b6f0b594d0ca4) +[comment]: # ( SHA256STAMP:1dad89ace6e2434e6f1baeed18c4813799a3e560708de28c9ef2b34120dc903e) diff --git a/doc/lightning-openchannel_signed.7.md b/doc/lightning-openchannel_signed.7.md index 33346a11e5c9..5576e110b74f 100644 --- a/doc/lightning-openchannel_signed.7.md +++ b/doc/lightning-openchannel_signed.7.md @@ -67,4 +67,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:304d819b092e1d9ce7b4cd8ef08ecdd7952b454fed59e908ab1af02b50e9a0b0) +[comment]: # ( SHA256STAMP:35d894ae39df25214656482a1680639fb2660783b4f95d1a042ae74c716c595f) diff --git a/doc/lightning-openchannel_update.7.md b/doc/lightning-openchannel_update.7.md index ea279667aef4..73b038cc82a7 100644 --- a/doc/lightning-openchannel_update.7.md +++ b/doc/lightning-openchannel_update.7.md @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:1a29b970038901773c6712c5e9267ae2c0ed3e9d4b11543d287c272a031003c1) +[comment]: # ( SHA256STAMP:92a20c9731a38c03189d7396e196c0439ef4311407529542b41af967e365c43b) diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md index 9006e687746b..af34e7ce0acd 100644 --- a/doc/lightning-parsefeerate.7.md +++ b/doc/lightning-parsefeerate.7.md @@ -44,4 +44,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f3498aa6f16d5be0a49896ae67144558df1baa223ac396a90ddf2d89a42ff395) +[comment]: # ( SHA256STAMP:9c3b23f12660f53e954687b1fcb1b4bfc629bd0714363c0f21c44958c499fd51) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index d473b3bf932c..8fc84ed797f3 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -167,4 +167,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:aa9dad9f5f97e1ad3a1a79c923dd57a685d7fada1545987f8ca8021ed92aa4eb) +[comment]: # ( SHA256STAMP:13926b44871e0a8d805eed668c371401a97f112a50481d49838975dcb7593329) diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index 5e71d4948a3f..e7aeeb5c842f 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -70,4 +70,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:59da52931ba9cb4c192ab81973efed2246baa9a97aded29e2c4cf280972c9a9a) +[comment]: # ( SHA256STAMP:8fd43121667f09f763914b27e53b8aecd855e1a611681f34de5e19f4ed13aabe) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index 51bea0c22c02..ae123f6472e0 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -84,4 +84,4 @@ RESOURCES Main web site: [writing plugins]: PLUGINS.md -[comment]: # ( SHA256STAMP:4ed30fb62c37111ea06b55d1252727c9c56fabd40e9e1f50083c99de74b8dfa1) +[comment]: # ( SHA256STAMP:f19b2a53065fd2e571593330112e30e99845b88acbd58237d25c94c224c64686) diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index a76b0737acd3..8a604e7b6a17 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:45c13e920465d313c693db6bfea8ac2f66d1e40226ef78241f82f51df9b715b2) +[comment]: # ( SHA256STAMP:14873cda2c903c231c6707234b6b161b3cabf84ebe48c274e4c7f5f980207d13) diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index 56a99c212f1e..1d8cf01b1b01 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -69,4 +69,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:29749d2978455477670d15627d51a6c1e9493136431539640a81e85ab7104d4e) +[comment]: # ( SHA256STAMP:ad5ceedef94690f4332e8b7b0fe5b639df6f38c6776d3c3be6f6f833f1eb954a) diff --git a/doc/lightning-sendinvoice.7.md b/doc/lightning-sendinvoice.7.md index ad6ae3b5a9dd..3a4ff7370c24 100644 --- a/doc/lightning-sendinvoice.7.md +++ b/doc/lightning-sendinvoice.7.md @@ -80,4 +80,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d227800f16140429a3352bc28f023df8d03c93a54012efcdfdd1f307511c4356) +[comment]: # ( SHA256STAMP:2eb62b2da6067b6d6b2f5585d521600b722a45d7a6c74da882891fd5aa00bc77) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index fa7a72c3dd96..d2d18196300f 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -135,4 +135,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:4e329c9768eb9d53d3b1068b18c7eaad46f75fa1a57724be4cbb64dc3c324a04) +[comment]: # ( SHA256STAMP:84195897945d1b1f8b4aabff611ded8198aa7a1aa959fcf903ec7bd3de1d1ebf) diff --git a/doc/lightning-sendonionmessage.7.md b/doc/lightning-sendonionmessage.7.md index 6e1db0a41204..d045fbbd5ab7 100644 --- a/doc/lightning-sendonionmessage.7.md +++ b/doc/lightning-sendonionmessage.7.md @@ -43,4 +43,4 @@ Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:19732c05461b56bb430b3fe568deba807f29a31324252fe748b859b028e649f3) +[comment]: # ( SHA256STAMP:267a098af220600b0c3ed3e50e2704c24d7330c428fd80aaa22d0aec59a4efe1) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 62228cc1e182..11b0db6554e0 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -142,4 +142,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9d998118234ab83c68d5add16a10b870edf4530c4a2c9e904b0f581b261611d1) +[comment]: # ( SHA256STAMP:08f06099569549e627f675c647aba1b7576c8662c4ad6906ca1ff56062fc9afc) diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index 7c99b6524592..42baad2d482a 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -66,4 +66,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:1fe5147036f714c8f9185dd482a1bfa3e3ac5c4d0b6603fba1bc2b78de591b8f) +[comment]: # ( SHA256STAMP:e2b8a4f88c61ef7823e1b8541719d2d5ccb05181116785ac8e9be02816d65c0d) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index bc5951f440a2..1ee6b417103c 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -107,4 +107,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:53cf2c58a961078e501b6034e3eed1c5c2d70ed02fc5b167b26ff43c2e2d196f) +[comment]: # ( SHA256STAMP:f133a8053cf5b0d5f6d8823c62fceda134af24c43411307b764db3f3e2f4d261) diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index 7e649c98d0e5..ddd6a006a3b2 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -83,4 +83,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ac1cbc87d916cec68cea18d43dc561b3dd655a7f6e2bdaec1a4ebf47bb32b4ad) +[comment]: # ( SHA256STAMP:165a54776eba3c727efc309a2e9d22bec204a4bd909b002b83082a97aa29c400) diff --git a/doc/lightning-signmessage.7.md b/doc/lightning-signmessage.7.md index 8ebb95910a2e..8fa4891eb3a1 100644 --- a/doc/lightning-signmessage.7.md +++ b/doc/lightning-signmessage.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:137e80fcb45c2e93058a869cb991c4aac31dace621f5941e91b9039290659648) +[comment]: # ( SHA256STAMP:163cdae723e4b50715255b233ff2a817481ccff0b778d56c2ed0b88bd0cdc8a6) diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index 46c6f71b32e0..fea7c48f02b1 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a64f3742f0c66ddf203afa3f69859d4385c4156fe99c30f0931e41ce95d944b1) +[comment]: # ( SHA256STAMP:9604daeee442beb457ce32f5f66fceb337e92bf7a613f8ca2e81214c5d3200db) diff --git a/doc/lightning-stop.7.md b/doc/lightning-stop.7.md index d3c452b42e94..d82b8309898c 100644 --- a/doc/lightning-stop.7.md +++ b/doc/lightning-stop.7.md @@ -42,4 +42,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a83ac745b36e0e3d00ba11f036cec93973773f47e1265eb5c70a3d3298e58e4b) +[comment]: # ( SHA256STAMP:2af4e0809f45c5a980e95a85d95295df42d9fa25660315fc73e6b38c193a91ce) diff --git a/doc/lightning-txdiscard.7.md b/doc/lightning-txdiscard.7.md index 7757ecb10cc3..7cf342aeaed9 100644 --- a/doc/lightning-txdiscard.7.md +++ b/doc/lightning-txdiscard.7.md @@ -45,4 +45,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8a6857f312d202e3328443d6ca25b5318d6b5d8fe5655ff6c70466e54c5cd42d) +[comment]: # ( SHA256STAMP:79152df2e06a1da8e6da7693b967b5e0187fd0281418839048c7453acac1c839) diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index a1d159d473a4..b5834804539f 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -85,4 +85,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c32c4fe613e5fa173d26bb144fb173d34d2c9181f4eb5da7ef413d41f44f95d7) +[comment]: # ( SHA256STAMP:1b5693f74931f9a5746415a81268bdbd229e88250498b6ff86e3a76f8f2991c3) diff --git a/doc/lightning-txsend.7.md b/doc/lightning-txsend.7.md index 31e3a1c4f745..6ebfe5c8ca01 100644 --- a/doc/lightning-txsend.7.md +++ b/doc/lightning-txsend.7.md @@ -45,4 +45,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1934c53b0448f872c17b35cfd76b887f3599924213d29ba249b076ff5503be42) +[comment]: # ( SHA256STAMP:5ae95391f068ecafbb0fb742454ea4bf31e1cd47b56f7a82120f2b5b08aea070) diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index 1a7750befe61..6665a3c14a75 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -55,4 +55,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:86bf213ca69b042fec2cf4241875a923db21aef9830e690bd1fb495ffd120966) +[comment]: # ( SHA256STAMP:9952e49efe40455558a4f1660e5e823570308c92c1762e74581ca5596961c7c6) diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index 129e0d53749e..deaf5b74cc56 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -100,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5037d26b92d3481a9eac98f509ac2dcee3f3185d6783f3a549d06a9e2e5e1a5f) +[comment]: # ( SHA256STAMP:1ccbb9c0a0d791e155ebc027465b908c3b2d5f9bd56eba7f5d22003c8e8172e0) diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index 87c2888e87a1..2e7d452804fc 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -75,4 +75,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b781ec3594dc6b0ac3091fc2d6561aae91f93a00a922f90285bf8828270ae26c) +[comment]: # ( SHA256STAMP:ce034f1801260480032a98348aae928055f3d9edf12f45cefcd8b6007289ff83) diff --git a/doc/lightning-waitblockheight.7.md b/doc/lightning-waitblockheight.7.md index 254c65fb7b2e..ef57b6c1d101 100644 --- a/doc/lightning-waitblockheight.7.md +++ b/doc/lightning-waitblockheight.7.md @@ -39,4 +39,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c0440c779f4274b2e2bf4c9e5889a3ef9855c9e9a698d5a50dd8d80eb35c0683) +[comment]: # ( SHA256STAMP:7db14bcda8226b184a2cdccb41e0db717d257683bb69b684a61e065af7ab076d) diff --git a/doc/lightning-waitinvoice.7.md b/doc/lightning-waitinvoice.7.md index 4a14473d466f..6ec5f86eb31e 100644 --- a/doc/lightning-waitinvoice.7.md +++ b/doc/lightning-waitinvoice.7.md @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b781ec3594dc6b0ac3091fc2d6561aae91f93a00a922f90285bf8828270ae26c) +[comment]: # ( SHA256STAMP:ce034f1801260480032a98348aae928055f3d9edf12f45cefcd8b6007289ff83) diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index 708e6068dc60..d3d07631cb84 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -103,4 +103,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8b5a3a8ce1f3c2dcd3e2b8a262d15bcd61700f971830e939a841352aa9d0f849) +[comment]: # ( SHA256STAMP:470c164c81adea9053cf276b92c3faf2d0fc4937c747a4c2d06524c601afd1c3) diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index 4d16430d6cc5..0e95f1368329 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -74,4 +74,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ee610c5e83e620125e8583022768f0c3293db2326e879b248e67eaddd655c18e) +[comment]: # ( SHA256STAMP:2d79ddd7910ea1f5b3a1a0a0d9494229fa600c9a1669ce0904a364f550d847dd) diff --git a/plugins/bkpr/Makefile b/plugins/bkpr/Makefile index ee6c96af82ab..47d622c84831 100644 --- a/plugins/bkpr/Makefile +++ b/plugins/bkpr/Makefile @@ -47,8 +47,8 @@ BOOKKEEPER_SQL_FILES := \ plugins/bkpr/recorder.c plugins/bkpr/statements_gettextgen.po: $(BOOKKEEPER_SQL_FILES) $(FORCE) - @if $(call SHA256STAMP_CHANGED_ALL); then \ - $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(BOOKKEEPER_SQL_FILES) && $(call SHA256STAMP_ALL,# ,)); \ + @if $(call SHA256STAMP_CHANGED); then \ + $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(BOOKKEEPER_SQL_FILES) && $(call SHA256STAMP,# ,)); \ fi plugins/bkpr/db_%_sqlgen.c: plugins/bkpr/statements_gettextgen.po devtools/sql-rewrite.py $(BOOKKEEPER_SQL_FILES) $(FORCE) diff --git a/wallet/Makefile b/wallet/Makefile index 13c0e7d71137..05ce68de4593 100644 --- a/wallet/Makefile +++ b/wallet/Makefile @@ -33,8 +33,8 @@ WALLET_SQL_FILES := \ wallet/test/run-wallet.c \ wallet/statements_gettextgen.po: $(WALLET_SQL_FILES) $(FORCE) - @if $(call SHA256STAMP_CHANGED_ALL); then \ - $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(WALLET_SQL_FILES) && $(call SHA256STAMP_ALL,# ,)); \ + @if $(call SHA256STAMP_CHANGED); then \ + $(call VERBOSE,"xgettext $@",xgettext -kNAMED_SQL -kSQL --add-location --no-wrap --omit-header -o $@ $(WALLET_SQL_FILES) && $(call SHA256STAMP,# ,)); \ fi wallet/db_%_sqlgen.c: wallet/statements_gettextgen.po devtools/sql-rewrite.py $(FORCE) From 04b59d991ab2e643e9b1eab204a96ec820e3f906 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 07:15:06 +0930 Subject: [PATCH 1298/1530] doc: always escape underscores in property names If there's only a single underscore, lowdown ignores it, but if there are multiple (see min_final_cltv_expiry) it decides we're trying to highlight part of the word. Reported-by: @wtogami Signed-off-by: Rusty Russell --- doc/lightning-addgossip.7.md | 2 +- doc/lightning-autocleaninvoice.7.md | 6 +- doc/lightning-bkpr-channelsapy.7.md | 46 +++---- doc/lightning-bkpr-dumpincomecsv.7.md | 6 +- doc/lightning-bkpr-inspect.7.md | 20 +-- doc/lightning-bkpr-listaccountevents.7.md | 16 +-- doc/lightning-bkpr-listbalances.7.md | 18 +-- doc/lightning-bkpr-listincome.7.md | 10 +- doc/lightning-check.7.md | 4 +- doc/lightning-checkmessage.7.md | 2 +- doc/lightning-close.7.md | 2 +- doc/lightning-commando-rune.7.md | 6 +- doc/lightning-connect.7.md | 2 +- doc/lightning-createinvoice.7.md | 20 +-- doc/lightning-createonion.7.md | 4 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-decode.7.md | 144 +++++++++++----------- doc/lightning-decodepay.7.md | 24 ++-- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-delexpiredinvoice.7.md | 2 +- doc/lightning-delinvoice.7.md | 20 +-- doc/lightning-delpay.7.md | 12 +- doc/lightning-disableoffer.7.md | 8 +- doc/lightning-disconnect.7.md | 2 +- doc/lightning-feerates.7.md | 40 +++--- doc/lightning-fetchinvoice.7.md | 14 +-- doc/lightning-fundchannel.7.md | 6 +- doc/lightning-fundchannel_cancel.7.md | 2 +- doc/lightning-fundchannel_complete.7.md | 6 +- doc/lightning-fundchannel_start.7.md | 8 +- doc/lightning-funderupdate.7.md | 32 ++--- doc/lightning-fundpsbt.7.md | 14 +-- doc/lightning-getinfo.7.md | 18 +-- doc/lightning-getlog.7.md | 14 +-- doc/lightning-getroute.7.md | 4 +- doc/lightning-help.7.md | 2 +- doc/lightning-invoice.7.md | 18 +-- doc/lightning-keysend.7.md | 14 +-- doc/lightning-listchannels.7.md | 20 +-- doc/lightning-listconfigs.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listforwards.7.md | 20 +-- doc/lightning-listfunds.7.md | 20 +-- doc/lightning-listinvoices.7.md | 20 +-- doc/lightning-listnodes.7.md | 22 ++-- doc/lightning-listoffers.7.md | 8 +- doc/lightning-listpays.7.md | 12 +- doc/lightning-listpeers.7.md | 134 ++++++++++---------- doc/lightning-listsendpays.7.md | 12 +- doc/lightning-listtransactions.7.md | 4 +- doc/lightning-makesecret.7.md | 2 +- doc/lightning-multifundchannel.7.md | 8 +- doc/lightning-multiwithdraw.7.md | 2 +- doc/lightning-newaddr.7.md | 2 +- doc/lightning-notifications.7.md | 2 +- doc/lightning-offer.7.md | 8 +- doc/lightning-offerout.7.md | 8 +- doc/lightning-openchannel_abort.7.md | 6 +- doc/lightning-openchannel_bump.7.md | 8 +- doc/lightning-openchannel_init.7.md | 8 +- doc/lightning-openchannel_signed.7.md | 4 +- doc/lightning-openchannel_update.7.md | 10 +- doc/lightning-parsefeerate.7.md | 2 +- doc/lightning-pay.7.md | 14 +-- doc/lightning-ping.7.md | 2 +- doc/lightning-plugin.7.md | 2 +- doc/lightning-reserveinputs.7.md | 6 +- doc/lightning-sendcustommsg.7.md | 2 +- doc/lightning-sendinvoice.7.md | 16 +-- doc/lightning-sendonion.7.md | 12 +- doc/lightning-sendonionmessage.7.md | 2 +- doc/lightning-sendpay.7.md | 12 +- doc/lightning-sendpsbt.7.md | 2 +- doc/lightning-setchannel.7.md | 20 +-- doc/lightning-setchannelfee.7.md | 8 +- doc/lightning-signmessage.7.md | 2 +- doc/lightning-signpsbt.7.md | 4 +- doc/lightning-stop.7.md | 2 +- doc/lightning-txdiscard.7.md | 4 +- doc/lightning-txprepare.7.md | 4 +- doc/lightning-txsend.7.md | 2 +- doc/lightning-unreserveinputs.7.md | 6 +- doc/lightning-utxopsbt.7.md | 14 +-- doc/lightning-waitanyinvoice.7.md | 16 +-- doc/lightning-waitblockheight.7.md | 2 +- doc/lightning-waitinvoice.7.md | 16 +-- doc/lightning-waitsendpay.7.md | 12 +- doc/lightning-withdraw.7.md | 2 +- tools/fromschema.py | 17 ++- 89 files changed, 562 insertions(+), 557 deletions(-) diff --git a/doc/lightning-addgossip.7.md b/doc/lightning-addgossip.7.md index 44620dde0cd6..97bcc008f150 100644 --- a/doc/lightning-addgossip.7.md +++ b/doc/lightning-addgossip.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bdc678495e0e0e626cdc71e73e8179dc8350de9862c4458933607fa62d8a16f7) +[comment]: # ( SHA256STAMP:4d9f888d10faca2bf94d1b52510cf21fbeebae4efda0946f03d04b0ef4bc88a2) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index 241026c79c9a..f3d2dfe33878 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -32,8 +32,8 @@ On success, an object is returned, containing: 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 + - **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) @@ -52,4 +52,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:43767e7e2dde51d1d5a250d3ffac510cf53305f11d9984a9f37ceb6b28c386ea) +[comment]: # ( SHA256STAMP:06bb1ef610c0f82d3d370f468575f2d6e837a11473acd1267baab829aa505052) diff --git a/doc/lightning-bkpr-channelsapy.7.md b/doc/lightning-bkpr-channelsapy.7.md index 5be11265ea55..4a4b8a10f8ae 100644 --- a/doc/lightning-bkpr-channelsapy.7.md +++ b/doc/lightning-bkpr-channelsapy.7.md @@ -21,30 +21,30 @@ RETURN VALUE ------------ [comment]: # (GENERATE-FROM-SCHEMA-START) -On success, an object containing **channels_apy** is returned. It is an array of objects, where each object contains: +On success, an object containing **channels\_apy** is returned. It is an array of objects, where each object contains: - **account** (string): The account name. If the account is a channel, the channel_id. The 'net' entry is the rollup of all channel accounts -- **routed_out_msat** (msat): Sats routed (outbound) -- **routed_in_msat** (msat): Sats routed (inbound) -- **lease_fee_paid_msat** (msat): Sats paid for leasing inbound (liquidity ads) -- **lease_fee_earned_msat** (msat): Sats earned for leasing outbound (liquidity ads) -- **pushed_out_msat** (msat): Sats pushed to peer at open -- **pushed_in_msat** (msat): Sats pushed in from peer at open -- **our_start_balance_msat** (msat): Starting balance in channel at funding. Note that if our start ballance is zero, any _initial field will be omitted (can't divide by zero) -- **channel_start_balance_msat** (msat): Total starting balance at funding -- **fees_out_msat** (msat): Fees earned on routed outbound -- **utilization_out** (string): Sats routed outbound / total start balance -- **utilization_in** (string): Sats routed inbound / total start balance -- **apy_out** (string): Fees earned on outbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) -- **apy_in** (string): Fees earned on inbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) -- **apy_total** (string): Total fees earned on routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) -- **fees_in_msat** (msat, optional): Fees earned on routed inbound -- **utilization_out_initial** (string, optional): Sats routed outbound / our start balance -- **utilization_in_initial** (string, optional): Sats routed inbound / our start balance -- **apy_out_initial** (string, optional): Fees earned on outbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) -- **apy_in_initial** (string, optional): Fees earned on inbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) -- **apy_total_initial** (string, optional): Total fees earned on routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) -- **apy_lease** (string, optional): Lease fees earned over total amount leased for the lease term, amortized to a year (APY). Only appears if channel was leased out by us +- **routed\_out\_msat** (msat): Sats routed (outbound) +- **routed\_in\_msat** (msat): Sats routed (inbound) +- **lease\_fee\_paid\_msat** (msat): Sats paid for leasing inbound (liquidity ads) +- **lease\_fee\_earned\_msat** (msat): Sats earned for leasing outbound (liquidity ads) +- **pushed\_out\_msat** (msat): Sats pushed to peer at open +- **pushed\_in\_msat** (msat): Sats pushed in from peer at open +- **our\_start\_balance\_msat** (msat): Starting balance in channel at funding. Note that if our start ballance is zero, any _initial field will be omitted (can't divide by zero) +- **channel\_start\_balance\_msat** (msat): Total starting balance at funding +- **fees\_out\_msat** (msat): Fees earned on routed outbound +- **utilization\_out** (string): Sats routed outbound / total start balance +- **utilization\_in** (string): Sats routed inbound / total start balance +- **apy\_out** (string): Fees earned on outbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) +- **apy\_in** (string): Fees earned on inbound routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) +- **apy\_total** (string): Total fees earned on routed payments / total start balance for the length of time this channel has been open amortized to a year (APY) +- **fees\_in\_msat** (msat, optional): Fees earned on routed inbound +- **utilization\_out\_initial** (string, optional): Sats routed outbound / our start balance +- **utilization\_in\_initial** (string, optional): Sats routed inbound / our start balance +- **apy\_out\_initial** (string, optional): Fees earned on outbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) +- **apy\_in\_initial** (string, optional): Fees earned on inbound routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) +- **apy\_total\_initial** (string, optional): Total fees earned on routed payments / our start balance for the length of time this channel has been open amortized to a year (APY) +- **apy\_lease** (string, optional): Lease fees earned over total amount leased for the lease term, amortized to a year (APY). Only appears if channel was leased out by us [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -65,4 +65,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c86f82cd78e639f70e640c4d36e0a28a5087a74c931207501d9121049ff9b5d2) +[comment]: # ( SHA256STAMP:296069892023c371e26305e8ab54d04a50aefeeff37a8a465d07893a8c39e61c) diff --git a/doc/lightning-bkpr-dumpincomecsv.7.md b/doc/lightning-bkpr-dumpincomecsv.7.md index 42967be3068b..20980fd2627e 100644 --- a/doc/lightning-bkpr-dumpincomecsv.7.md +++ b/doc/lightning-bkpr-dumpincomecsv.7.md @@ -35,8 +35,8 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **csv_file** (string): File that the csv was generated to -- **csv_format** (string): Format to print csv as (one of "cointracker", "koinly", "harmony", "quickbooks") +- **csv\_file** (string): File that the csv was generated to +- **csv\_format** (string): Format to print csv as (one of "cointracker", "koinly", "harmony", "quickbooks") [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -57,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:13920567775dacf15991bc281ccac0bf3f80599e87c3df9e73ca4564445ed6eb) +[comment]: # ( SHA256STAMP:4d9326acde55e8124ea61a7e2430f035d3a4957995a76ddef61ecb2a3debd042) diff --git a/doc/lightning-bkpr-inspect.7.md b/doc/lightning-bkpr-inspect.7.md index 865d314845e0..faa45103c688 100644 --- a/doc/lightning-bkpr-inspect.7.md +++ b/doc/lightning-bkpr-inspect.7.md @@ -20,19 +20,19 @@ RETURN VALUE On success, an object containing **txs** is returned. It is an array of objects, where each object contains: - **txid** (txid): transaction id -- **fees_paid_msat** (msat): Amount paid in sats for this tx +- **fees\_paid\_msat** (msat): Amount paid in sats for this tx - **outputs** (array of objects): - **account** (string): Account this output affected - **outnum** (u32): Index of output - - **output_value_msat** (msat): Value of the output + - **output\_value\_msat** (msat): Value of the output - **currency** (string): human-readable bech32 part for this coin type - - **credit_msat** (msat, optional): Amount credited to account - - **debit_msat** (msat, optional): Amount debited from account - - **originating_account** (string, optional): Account this output originated from - - **output_tag** (string, optional): Description of output creation event - - **spend_tag** (string, optional): Description of output spend event - - **spending_txid** (txid, optional): Transaction this output was spent in - - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. + - **credit\_msat** (msat, optional): Amount credited to account + - **debit\_msat** (msat, optional): Amount debited from account + - **originating\_account** (string, optional): Account this output originated from + - **output\_tag** (string, optional): Description of output creation event + - **spend\_tag** (string, optional): Description of output spend event + - **spending\_txid** (txid, optional): Transaction this output was spent in + - **payment\_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. - **blockheight** (u32, optional): Blockheight of transaction [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -52,4 +52,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a89c90315d210fcf1fd45bf4edef04a72165834867f860447e33d89840eac59f) +[comment]: # ( SHA256STAMP:022a11cd4bdaec7b1b39e30faaf5489de1db08684a3952b3a3152343e91c8c4c) diff --git a/doc/lightning-bkpr-listaccountevents.7.md b/doc/lightning-bkpr-listaccountevents.7.md index 113d3d4c1b7b..aede7dccd875 100644 --- a/doc/lightning-bkpr-listaccountevents.7.md +++ b/doc/lightning-bkpr-listaccountevents.7.md @@ -28,8 +28,8 @@ On success, an object containing **events** is returned. It is an array of obje - **account** (string): The account name. If the account is a channel, the channel_id - **type** (string): Coin movement type (one of "onchain_fee", "chain", "channel") - **tag** (string): Description of movement -- **credit_msat** (msat): Amount credited -- **debit_msat** (msat): Amount debited +- **credit\_msat** (msat): Amount credited +- **debit\_msat** (msat): Amount debited - **currency** (string): human-readable bech32 part for this coin type - **timestamp** (u32): Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp @@ -38,7 +38,7 @@ If **type** is "chain": - **outpoint** (string): The txid:outnum for this event - **blockheight** (u32): For chain events, blockheight this occured at - **origin** (string, optional): The account this movement originated from - - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. + - **payment\_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. - **txid** (txid, optional): The txid of the transaction that created this event - **description** (string, optional): The description of this event @@ -48,10 +48,10 @@ If **type** is "onchain_fee": If **type** is "channel": - - **fees_msat** (msat, optional): Amount paid in fees - - **is_rebalance** (boolean, optional): Is this payment part of a rebalance - - **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. - - **part_id** (u32, optional): Counter for multi-part payments + - **fees\_msat** (msat, optional): Amount paid in fees + - **is\_rebalance** (boolean, optional): Is this payment part of a rebalance + - **payment\_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. + - **part\_id** (u32, optional): Counter for multi-part payments [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -71,4 +71,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:50051a63881ea83154a36bbdccaf3915601574ed193ade6a5522260c85fa941c) +[comment]: # ( SHA256STAMP:3b120f28969d50351823707c11f77a0264031b1ddec07794ac117b0e8e2d7a75) diff --git a/doc/lightning-bkpr-listbalances.7.md b/doc/lightning-bkpr-listbalances.7.md index c0e4977ccd9a..a98b3780c21a 100644 --- a/doc/lightning-bkpr-listbalances.7.md +++ b/doc/lightning-bkpr-listbalances.7.md @@ -23,16 +23,16 @@ On success, an object containing **accounts** is returned. It is an array of ob - **account** (string): The account name. If the account is a channel, the channel_id - **balances** (array of objects): - - **balance_msat** (msat): Current account balance - - **coin_type** (string): coin type, same as HRP for bech32 + - **balance\_msat** (msat): Current account balance + - **coin\_type** (string): coin type, same as HRP for bech32 -If **peer_id** is present: +If **peer\_id** is present: - - **peer_id** (pubkey): Node id for the peer this account is with - - **we_opened** (boolean): Did we initiate this account open (open the channel) - - **account_closed** (boolean): - - **account_resolved** (boolean): Has this channel been closed and all outputs resolved? - - **resolved_at_block** (u32, optional): Blockheight account resolved on chain + - **peer\_id** (pubkey): Node id for the peer this account is with + - **we\_opened** (boolean): Did we initiate this account open (open the channel) + - **account\_closed** (boolean): + - **account\_resolved** (boolean): Has this channel been closed and all outputs resolved? + - **resolved\_at\_block** (u32, optional): Blockheight account resolved on chain [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -53,4 +53,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:42302ecee2bab01fca04477e5b4d793f11757e0b7a2f43c6878ff8ecf857cc34) +[comment]: # ( SHA256STAMP:ae354f9ee2c0f3805923c7c1fbbf490d43c838449fe25758a6884c2f1686d20e) diff --git a/doc/lightning-bkpr-listincome.7.md b/doc/lightning-bkpr-listincome.7.md index d35db63aeb46..d18a784ebf4b 100644 --- a/doc/lightning-bkpr-listincome.7.md +++ b/doc/lightning-bkpr-listincome.7.md @@ -26,18 +26,18 @@ RETURN VALUE ------------ [comment]: # (GENERATE-FROM-SCHEMA-START) -On success, an object containing **income_events** is returned. It is an array of objects, where each object contains: +On success, an object containing **income\_events** is returned. It is an array of objects, where each object contains: - **account** (string): The account name. If the account is a channel, the channel_id - **tag** (string): Type of income event -- **credit_msat** (msat): Amount earned (income) -- **debit_msat** (msat): Amount spent (expenses) +- **credit\_msat** (msat): Amount earned (income) +- **debit\_msat** (msat): Amount spent (expenses) - **currency** (string): human-readable bech32 part for this coin type - **timestamp** (u32): Timestamp this event was recorded by the node. For consolidated events such as onchain_fees, the most recent timestamp - **description** (string, optional): More information about this event. If a `invoice` type, typically the bolt11/bolt12 description - **outpoint** (string, optional): The txid:outnum for this event, if applicable - **txid** (txid, optional): The txid of the transaction that created this event, if applicable -- **payment_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. +- **payment\_id** (hex, optional): lightning payment identifier. For an htlc, this will be the preimage. [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -57,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:67cedc19eeb5d684cda2efd025e42d1d89fd1d899d8b66ac0d9b663139cc3517) +[comment]: # ( SHA256STAMP:23bcaf41a3451f716cbeedc992c84b88a823d3aa219fd848e3c6c47ae9bbca55) diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index cd5fc4a0f5dd..d4fa3f1675a2 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -26,7 +26,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **command_to_check** (string): the *command_to_check* argument +- **command\_to\_check** (string): the *command_to_check* argument [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -41,4 +41,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:74e2865a8316ad266499fc9a0ce2aa7ce9794da90d7039f91150c0560df901f1) +[comment]: # ( SHA256STAMP:bd1993b3b95a6c1a9e21873632a829514eb4e9d00ad5084bb099798c8977de06) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index c0058e557c2e..853cedec68b4 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -50,4 +50,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:aa3f01d9f9c1fa61f8b4fc850c3a982e3ea57abcb33338ad36930bb5c70124ce) +[comment]: # ( SHA256STAMP:9642945217c12b5ce41af5aef0547a42b6db5baa97ed88e6fde91345bb9a75f3) diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 61a33083f55b..349f6a73bb5b 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -135,4 +135,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:54eec92e876d0e05041f708cd8453a16cc1a178df8c966620f8bf6536ba6e36d) +[comment]: # ( SHA256STAMP:89f0bd65ba94f07afaddac259a766d89e26a9acf646c0491c869d923db3091eb) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index 1304a715ac7f..5708f883c060 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -192,11 +192,11 @@ RETURN VALUE On success, an object is returned, containing: - **rune** (string): the resulting rune -- **unique_id** (string): the id of this rune: this is set at creation and cannot be changed (even as restrictions are added) +- **unique\_id** (string): the id of this rune: this is set at creation and cannot be changed (even as restrictions are added) The following warnings may also be returned: -- **warning_unrestricted_rune**: A warning shown when runes are created with powers that could drain your node +- **warning\_unrestricted\_rune**: A warning shown when runes are created with powers that could drain your node [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -219,4 +219,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b9cf90aabe3e87fb41bc1da93d9efd3e6dc629be487f65fb815bed3bad4adb0b) +[comment]: # ( SHA256STAMP:843aaf64ccded63baf949f0103dd0f18b9652f259f1d35e0fd67f45ee58c4e8f) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index 345d5c99bd96..ae5b2fa5104c 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -104,4 +104,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:98dfa22262e5192ef0fa14b0a9ede95f932a6f0966f847bdfd87b1d1a1b37789) +[comment]: # ( SHA256STAMP:27f1e0ad88e7908bbb450ffc1ba5c53855225adf126155cd62ddbd61a599c3ea) diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index abb6c925138a..b8130b3f8d71 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -34,19 +34,19 @@ RETURN VALUE On success, an object is returned, containing: - **label** (string): the label for the invoice -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): 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) +- **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** (secret, 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) -- **payer_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**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** (secret, 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) +- **payer\_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only). [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -75,4 +75,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:56c128b40e64b4433487be292f99e4f68576e80466cc4a5b544572465901bb64) +[comment]: # ( SHA256STAMP:1d552c5038b6062cd384b7f143ad139ab5275a2b44be09e6d51d7111109caca2) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index e8f136ba4ea2..9e6768eabee8 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -93,7 +93,7 @@ RETURN VALUE On success, an object is returned, containing: - **onion** (hex): the onion packet (*onion_size* bytes) -- **shared_secrets** (array of secrets): one shared secret for each node in the *hops* parameter: +- **shared\_secrets** (array of secrets): one shared secret for each node in the *hops* parameter: - the shared secret with this hop (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -133,4 +133,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9c19a1960c2d7db91b312ab7e738395e36260130e093a576c1c79dba40d25b59) +[comment]: # ( SHA256STAMP:719bc9feedb006638b14f3e6cd01aba479620f771868d8d954b34166224f1c8f) diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index 796abce1ee0e..7313b59255d6 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -66,4 +66,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3319dbcf6a05a45cb0a28fddb27050ddd7139c8ed1537c8204e8c722b5acd65e) +[comment]: # ( SHA256STAMP:f3f48ed1c5c5ccab128c4ce782b2f209896fe0ca6440e00799935dff9bef3f2e) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index ad2b21006fab..8a642cebbd45 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -29,133 +29,133 @@ On success, an object is returned, containing: 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** (point32): x-only public key of the offering node + - **offer\_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) + - **node\_id** (point32): x-only public key of the offering node - **description** (string): the description of the purpose of the offer - **signature** (bip340sig, optional): BIP-340 signature of the *node_id* on this 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) + - **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) + - **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 + - **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 - - **encrypted_recipient_data** (hex): encrypted TLV entry for this hop - - **quantity_min** (u64, optional): the minimum quantity - - **quantity_max** (u64, optional): the maximum quantity + - **node\_id** (pubkey): node_id of the hop + - **encrypted\_recipient\_data** (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 + - **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) + - **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) + - **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*) + - **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**) + - **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** + - **warning\_offer\_missing\_description**: No **description** If **type** is "bolt12 invoice", and **valid** is *true*: - - **node_id** (point32): x-only public key of the offering node + - **node\_id** (point32): 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 + - **amount\_msat** (msat): the amount in bitcoin - **description** (string): the description of the purpose of the offer - - **created_at** (u64): the UNIX timestamp of invoice creation - - **payment_hash** (hex): the hash of the *payment_preimage* (always 64 characters) - - **relative_expiry** (u32): the number of seconds after *created_at* 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) + - **created\_at** (u64): the UNIX timestamp of invoice creation + - **payment\_hash** (hex): the hash of the *payment_preimage* (always 64 characters) + - **relative\_expiry** (u32): the number of seconds after *created_at* 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) - **chain** (hex, optional): which blockchain this invoice is for (missing implies bitcoin mainnet only) (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) + - **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 - - **encrypted_recipient_data** (hex): encrypted TLV entry for this hop + - **node\_id** (pubkey): node_id of the hop + - **encrypted\_recipient\_data** (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** (point32, optional): the transient key which identifies the payer - - **payer_info** (hex, optional): the payer-provided blob to derive payer_key + - **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** (point32, 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 + - **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 + - **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_created_at**: Missing **created_at** - - **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** + - **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\_created\_at**: Missing **created_at** + - **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 the offer this is requesting (merkle hash of non-signature fields) (always 64 characters) - - **payer_key** (point32): the transient key which identifies the payer + - **offer\_id** (hex): the id of the offer this is requesting (merkle hash of non-signature fields) (always 64 characters) + - **payer\_key** (point32): the transient key which identifies the payer - **chain** (hex, optional): which blockchain this invoice_request is for (missing implies bitcoin mainnet only) (always 64 characters) - - **amount_msat** (msat, optional): the amount in bitcoin + - **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 + - **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 + - **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 + - **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) + - **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 + - **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) + - **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 - - **payment_metadata** (hex, optional): the payment_metadata to put in the payment + - **payment\_metadata** (hex, optional): the payment_metadata to put in the payment - **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 @@ -163,10 +163,10 @@ If **type** is "bolt11 invoice", and **valid** is *true*: - **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** (msat): 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 + - **short\_channel\_id** (short_channel_id): a channel to the next peer + - **fee\_base\_msat** (msat): 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 @@ -179,7 +179,7 @@ If **type** is "rune", and **valid** is *true*: - **alternatives** (array of strings): each way restriction can be met: any can pass: - the alternative of form fieldname condition fieldname - **summary** (string): human-readable summary of this restriction - - **unique_id** (string, optional): unique id (always a numeric id on runes we create) + - **unique\_id** (string, optional): unique id (always a numeric id on runes we create) - **version** (string, optional): rune version, not currently set on runes we create If **type** is "rune", and **valid** is *false*: @@ -187,7 +187,7 @@ If **type** is "rune", and **valid** is *false*: - **valid** (boolean) (always *false*) - **hex** (hex, optional): the raw rune in hex - the following warnings are possible: - - **warning_rune_invalid_utf8**: the rune contains invalid UTF-8 strings + - **warning\_rune\_invalid\_utf8**: the rune contains invalid UTF-8 strings [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -211,4 +211,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:2a6cc144b0f2436cc87886cf80f635f7c37e70c5a82e88ad7439ba660046523a) +[comment]: # ( SHA256STAMP:7497fbb2f802f2eef2b6c1a8005d31f4778af94c9d0384192a8ba1a4af936549) diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index e7bb66745393..25433d5a22c6 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -19,18 +19,18 @@ RETURN VALUE 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 +- **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) +- **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 +- **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) +- **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 -- **payment_metadata** (hex, optional): the payment_metadata to put in the payment +- **payment\_metadata** (hex, optional): the payment_metadata to put in the payment - **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 @@ -38,10 +38,10 @@ On success, an object is returned, containing: - **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** (msat): 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 + - **short\_channel\_id** (short_channel_id): a channel to the next peer + - **fee\_base\_msat** (msat): 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 @@ -71,4 +71,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:120150143d95a3f3b9685fc07c9630b5721d903ce4b85159242df3a804201698) +[comment]: # ( SHA256STAMP:b2a483f6fcb07e4bcddb088520049eee1d33447136e2634bf4207d54c21022e9) diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index b8e0d882ef86..e8cfc4513e31 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -49,4 +49,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6037971086a82e77e86621112e806e7b061c17cd5d5f9f82dff0af734f3965e5) +[comment]: # ( SHA256STAMP:3d70f21bc0945835995b820ab6d65b1ac6e88a9f13240f30acffd32315d41e70) diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index 5689eb61c48a..4f3fc644e7c7 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -38,4 +38,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:23050bf2eaaf606c63069c7686f45b69133cb2c0827537eee4367921b897842f) +[comment]: # ( SHA256STAMP:c2aef71377094e4c4ab09d3c18b3b8448e5fed1fcc2a6afd47ab218afb011b5e) diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index c8d9f352268f..3bb95d720250 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -28,25 +28,25 @@ Note: The return is the same as an object from lightning-listinvoice(7). On success, an object is returned, containing: - **label** (string): Unique label given at creation time -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): State of invoice (one of "paid", "expired", "unpaid") -- **expires_at** (u64): UNIX timestamp when invoice expires (or expired) +- **expires\_at** (u64): UNIX timestamp when invoice expires (or expired) - **bolt11** (string, optional): BOLT11 string - **bolt12** (string, optional): BOLT12 string -- **amount_msat** (msat, optional): the amount required to pay this invoice +- **amount\_msat** (msat, optional): the amount required to pay this invoice - **description** (string, optional): description used in the invoice If **bolt12** is present: - - **local_offer_id** (hex, optional): offer for which this invoice was created - - **payer_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice + - **local\_offer\_id** (hex, optional): offer for which this invoice was created + - **payer\_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice 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** (secret): SHA256 of this is the *payment_hash* offered in the invoice (always 64 characters) + - **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** (secret): SHA256 of this is the *payment_hash* offered in the invoice (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -81,4 +81,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:48ff64ba8dcb03b0e727f962b7b16a3768dd2382e5a3414c7ad243884b674667) +[comment]: # ( SHA256STAMP:a869dc2b48e763c47f3af1c27143d194db96948f19fbcec82df343bd3b6c4468) diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index 919b1e5d722d..a3bf29c3057d 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -39,15 +39,15 @@ payments will be returned -- one payment object for each partid. 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) +- **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 +- **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 +- **amount\_msat** (msat, optional): the amount the destination received, if known - **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash -- **payment_preimage** (hex, optional): proof of payment (always 64 characters) +- **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). @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:dbe8ab5ab8d0d5ab602fac4af3a567bd1f7899f25304683323da6621b8196213) +[comment]: # ( SHA256STAMP:f687e5248b633be5e6e3bb515b7b006858a3f2499bb5955240b3e18dd80794a8) diff --git a/doc/lightning-disableoffer.7.md b/doc/lightning-disableoffer.7.md index 6e7c5541f64a..842ec0d24704 100644 --- a/doc/lightning-disableoffer.7.md +++ b/doc/lightning-disableoffer.7.md @@ -37,11 +37,11 @@ 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) +- **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 +- **single\_use** (boolean): Whether the offer is disabled after first successful use - **bolt12** (string): The bolt12 string representing this offer -- **bolt12_unsigned** (string): The bolt12 string representing this offer, without signature +- **bolt12\_unsigned** (string): The bolt12 string representing this offer, without signature - **used** (boolean): Whether the offer has had an invoice paid / payment made - **label** (string, optional): The label provided when offer was created @@ -75,4 +75,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:17db907802f8417021d203710d1f4bb9e171f6d64952e8b6608d2875a947edb0) +[comment]: # ( SHA256STAMP:a2489ae6d986292861555d66a1fb0c8c39efe460db5e324ddbb27b9be1650296) diff --git a/doc/lightning-disconnect.7.md b/doc/lightning-disconnect.7.md index feac6b7770de..2a7b4f67964d 100644 --- a/doc/lightning-disconnect.7.md +++ b/doc/lightning-disconnect.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bdc678495e0e0e626cdc71e73e8179dc8350de9862c4458933607fa62d8a16f7) +[comment]: # ( SHA256STAMP:4d9f888d10faca2bf94d1b52510cf21fbeebae4efda0946f03d04b0ef4bc88a2) diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index b29ba413c761..0328e8fa49a1 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -48,33 +48,33 @@ RETURN VALUE 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). + - **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 + - **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). + - **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 + - **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 +- **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 +- **warning\_missing\_feerates**: Some fee estimates are missing [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -121,4 +121,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:25001249ab0ced5ea1fa3520fd1f090eb1b16b1a43a49452d39bffa33a49f009) +[comment]: # ( SHA256STAMP:8b9053a3047bc47e9cb86d8ae059e9b955194d1761d07b33ed7b78c85f434c34) diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index 696114f473df..dd870179034a 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -53,17 +53,17 @@ 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\_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\_removed** (string, optional): The *vendor* from the offer, which is missing in the invoice - **vendor** (string, optional): a completely replaced *vendor* field - - **amount_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*: + - **amount\_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 + - **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) @@ -89,4 +89,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:336245434880c274390e89e464ee7731508059c2e4614e1e0dbaeccdc6a315bc) +[comment]: # ( SHA256STAMP:003592ea0876d08b2109a0daa5e10328e049013f8e44d3eccc5805885a16a375) diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index ed27a4b80527..babc38327986 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -88,8 +88,8 @@ 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` +- **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` - **mindepth** (u32, optional): Number of confirmations before we consider the channel active. [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -115,4 +115,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c4e05ea6ec3e86b5506c7531d60809a165a2b84e97c92e57387868f34dd0b9db) +[comment]: # ( SHA256STAMP:f1c2b762c111ecb6d569075377e1a79cd9fceb60863fc56aeddcdbcd51445812) diff --git a/doc/lightning-fundchannel_cancel.7.md b/doc/lightning-fundchannel_cancel.7.md index d7911199731a..ab68df060ef8 100644 --- a/doc/lightning-fundchannel_cancel.7.md +++ b/doc/lightning-fundchannel_cancel.7.md @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ac234f1fece4dd8d1a9b9af2e053088e83fa4c7e10fb5f9b99db9c4607df3228) +[comment]: # ( SHA256STAMP:6e9501860847adfa98cf725e65297322428986f6345aeb04cd0ece150780ec66) diff --git a/doc/lightning-fundchannel_complete.7.md b/doc/lightning-fundchannel_complete.7.md index f6f46fa66e1c..248571b904f0 100644 --- a/doc/lightning-fundchannel_complete.7.md +++ b/doc/lightning-fundchannel_complete.7.md @@ -29,8 +29,8 @@ RETURN VALUE [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*) +- **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) @@ -62,4 +62,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7d1740964e0509e5bc36b22dd9612cb8fc4e5d345cbb8e92b1e03295c7d36075) +[comment]: # ( SHA256STAMP:b0602c5c3ab5370199776b56656451538712b989e9bfc169934ddabeff56894c) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index e697c9bed8ac..c6239df49ac0 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -44,14 +44,14 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **funding_address** (string): The address to send funding to for the channel. DO NOT SEND COINS TO THIS ADDRESS YET. +- **funding\_address** (string): The address to send funding to for the channel. DO NOT SEND COINS TO THIS ADDRESS YET. - **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` +- **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` - **mindepth** (u32, optional): Number of confirmations before we consider the channel active. The following warnings may also be returned: -- **warning_usage**: A warning not to prematurely broadcast the funding transaction (always present!) +- **warning\_usage**: A warning not to prematurely broadcast the funding transaction (always present!) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -85,4 +85,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:56c7001a34cee3ee79c18c8d0b71979a1f140fdc247222c0b05c4bd2b7f142e5) +[comment]: # ( SHA256STAMP:d76e01b4e00915ca84e6960c8718028bc220712a4c387711c01977c3ee80ddf4) diff --git a/doc/lightning-funderupdate.7.md b/doc/lightning-funderupdate.7.md index 424a943a8c51..4419642fefad 100644 --- a/doc/lightning-funderupdate.7.md +++ b/doc/lightning-funderupdate.7.md @@ -109,21 +109,21 @@ On success, an object is returned, containing: - **summary** (string): Summary of the current funding policy e.g. (match 100) - **policy** (string): Policy funder plugin will use to decide how much captial to commit to a v2 open channel request (one of "match", "available", "fixed") -- **policy_mod** (u32): The *policy_mod* is the number or 'modification' to apply to the policy. -- **leases_only** (boolean): Only contribute funds to `option_will_fund` lease requests. -- **min_their_funding_msat** (msat): The minimum funding sats that we require from peer to activate our funding policy. -- **max_their_funding_msat** (msat): The maximum funding sats that we'll allow from peer to activate our funding policy. -- **per_channel_min_msat** (msat): The minimum amount that we will fund a channel open with. -- **per_channel_max_msat** (msat): The maximum amount that we will fund a channel open with. -- **reserve_tank_msat** (msat): Amount of sats to leave available in the node wallet. -- **fuzz_percent** (u32): Percentage to fuzz our funding amount by. -- **fund_probability** (u32): Percent of opens to consider funding. 100 means we'll consider funding every requested open channel request. -- **lease_fee_base_msat** (msat, optional): Flat fee to charge for a channel lease. -- **lease_fee_basis** (u32, optional): Proportional fee to charge for a channel lease, calculated as 1/10,000th of requested funds. -- **funding_weight** (u32, optional): Transaction weight the channel opener will pay us for a leased funding transaction. -- **channel_fee_max_base_msat** (msat, optional): Maximum channel_fee_base_msat we'll charge for routing funds leased on this channel. -- **channel_fee_max_proportional_thousandths** (u32, optional): Maximum channel_fee_proportional_millitionths we'll charge for routing funds leased on this channel, in thousandths. -- **compact_lease** (hex, optional): Compact description of the channel lease parameters. +- **policy\_mod** (u32): The *policy_mod* is the number or 'modification' to apply to the policy. +- **leases\_only** (boolean): Only contribute funds to `option_will_fund` lease requests. +- **min\_their\_funding\_msat** (msat): The minimum funding sats that we require from peer to activate our funding policy. +- **max\_their\_funding\_msat** (msat): The maximum funding sats that we'll allow from peer to activate our funding policy. +- **per\_channel\_min\_msat** (msat): The minimum amount that we will fund a channel open with. +- **per\_channel\_max\_msat** (msat): The maximum amount that we will fund a channel open with. +- **reserve\_tank\_msat** (msat): Amount of sats to leave available in the node wallet. +- **fuzz\_percent** (u32): Percentage to fuzz our funding amount by. +- **fund\_probability** (u32): Percent of opens to consider funding. 100 means we'll consider funding every requested open channel request. +- **lease\_fee\_base\_msat** (msat, optional): Flat fee to charge for a channel lease. +- **lease\_fee\_basis** (u32, optional): Proportional fee to charge for a channel lease, calculated as 1/10,000th of requested funds. +- **funding\_weight** (u32, optional): Transaction weight the channel opener will pay us for a leased funding transaction. +- **channel\_fee\_max\_base\_msat** (msat, optional): Maximum channel_fee_base_msat we'll charge for routing funds leased on this channel. +- **channel\_fee\_max\_proportional\_thousandths** (u32, optional): Maximum channel_fee_proportional_millitionths we'll charge for routing funds leased on this channel, in thousandths. +- **compact\_lease** (hex, optional): Compact description of the channel lease parameters. [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -147,4 +147,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b07236a4cbe24e7aa13324002b044dc66b5457405cb9e448eaeeafe3c729cab4) +[comment]: # ( SHA256STAMP:c5895e85860542fa6d71d4acf8b1e0d43acaf395331b357064150ee1d52ddcca) diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 07740e217058..3402dcce7f5e 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -74,16 +74,16 @@ RETURN VALUE On success, an object is returned, containing: - **psbt** (string): Unsigned PSBT which fulfills the parameters given -- **feerate_per_kw** (u32): The feerate used to create the PSBT, in satoshis-per-kiloweight -- **estimated_final_weight** (u32): The estimated weight of the transaction once fully signed -- **excess_msat** (msat): 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** (u32, optional): The 0-based output number where change was placed (only if parameter *excess_as_change* was true and there was sufficient funds) +- **feerate\_per\_kw** (u32): The feerate used to create the PSBT, in satoshis-per-kiloweight +- **estimated\_final\_weight** (u32): The estimated weight of the transaction once fully signed +- **excess\_msat** (msat): 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** (u32, optional): The 0-based output number where change was placed (only if parameter *excess_as_change* was true and there was sufficient funds) - **reservations** (array of objects, optional): If *reserve* was true or a non-zero number, just as per lightning-reserveinputs(7): - **txid** (txid): The txid of the transaction - **vout** (u32): The 0-based output number - - **was_reserved** (boolean): Whether this output was previously reserved (always *false*) + - **was\_reserved** (boolean): Whether this output was previously reserved (always *false*) - **reserved** (boolean): Whether this output is now reserved (always *true*) - - **reserved_to_block** (u32): The blockheight the reservation will expire + - **reserved\_to\_block** (u32): The blockheight the reservation will expire [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -115,4 +115,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f687ab47b409b04ff97ad3bdb42880b23dfe4e0bb38f54a64b74e1a16c07ee81) +[comment]: # ( SHA256STAMP:7503bff4054024adf29a560f8612e2c504fe1252c71c3a3bb08282808fb299a7) diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 20ae5b46db61..0a546473dfcb 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -31,16 +31,16 @@ 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 +- **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 -- **our_features** (object, optional): Our BOLT #9 feature bits (as hexstring) for various contexts: +- **fees\_collected\_msat** (msat): Total routing fees collected by this node +- **our\_features** (object, optional): Our BOLT #9 feature bits (as hexstring) for various contexts: - **init** (hex): features (incl. globalfeatures) in our init message, these also restrict what we offer in open_channel or accept in accept_channel - **node** (hex): features in our node_announcement message - **channel** (hex): negotiated channel features we (as channel initiator) publish in the channel_announcement message @@ -60,8 +60,8 @@ On success, an object is returned, containing: 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. +- **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) @@ -131,4 +131,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:1d3258e769579e140755acc9808b9008b0f81fc0ded50311ee64ba628aee04ad) +[comment]: # ( SHA256STAMP:a517e840e673273a35128e8168d8f58f7795bf0bcfca00f25b859f6e171b08a0) diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index 0163bc58b9d5..130f037f08f8 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -31,22 +31,22 @@ RETURN VALUE [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 +- **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) + - **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 + - **node\_id** (pubkey, optional): The peer this is associated with If **type** is "IO_IN" or "IO_OUT": @@ -54,7 +54,7 @@ On success, an object is returned, containing: - **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 + - **node\_id** (pubkey, optional): The peer this is associated with [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -94,4 +94,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:778a192de637d247689c270b3cdc5b100baa749a866093b7a5c709b546e53c2c) +[comment]: # ( SHA256STAMP:95995a847dd9f3c2d51494a396761b42051e44bb6376de2e3c3e72634e7fd079) diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index 7ab5efa77fc7..4d0dd03ad3a1 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -283,7 +283,7 @@ On success, an object containing **route** is returned. It is an array of objec - **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 +- **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 (always "tlv") @@ -310,4 +310,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:95668bff3a9ec23bd7cebfc60a6af56bce0e6559fa8218b14390017b1348ef61) +[comment]: # ( SHA256STAMP:7aca069c04bac7139f82387d91e0ea148cc42f375c4cb11232189c874be87dcf) diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index f6621a39fdbf..48f460045abf 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -68,4 +68,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:288d00779f245c6e07fb7f6826e44b78a6dad296e0cd92855bf419523bbe2024) +[comment]: # ( SHA256STAMP:a87a95be3154ca98238cea4abdcb0524a831a0f78998b82eed1041da742ffa4c) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 5e04421fd8a8..e3350ffc3596 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -79,17 +79,17 @@ RETURN VALUE On success, an object is returned, containing: - **bolt11** (string): the bolt11 string -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) -- **payment_secret** (secret): the *payment_secret* to place in the onion (always 64 characters) -- **expires_at** (u64): UNIX timestamp of when invoice expires +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_secret** (secret): the *payment_secret* to place in the onion (always 64 characters) +- **expires\_at** (u64): UNIX timestamp of when invoice expires The following warnings may also be returned: -- **warning_capacity**: even using all possible channels, there's not enough incoming capacity to pay this invoice. -- **warning_offline**: there would be enough incoming capacity, but some channels are offline, so there isn't. -- **warning_deadends**: there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't. -- **warning_private_unused**: there would be enough incoming capacity, but some channels are unannounced and *exposeprivatechannels* is *false*, so there isn't. -- **warning_mpp**: there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments. +- **warning\_capacity**: even using all possible channels, there's not enough incoming capacity to pay this invoice. +- **warning\_offline**: there would be enough incoming capacity, but some channels are offline, so there isn't. +- **warning\_deadends**: there would be enough incoming capacity, but some channels are dead-ends (no other public channels from those peers), so there isn't. +- **warning\_private\_unused**: there would be enough incoming capacity, but some channels are unannounced and *exposeprivatechannels* is *false*, so there isn't. +- **warning\_mpp**: there is sufficient capacity, but not in a single channel, so the payer will have to use multi-part payments. [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -119,4 +119,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c040289df896608002a560a3bf4213f155ca81aacfe2de45771c2dcba1517b98) +[comment]: # ( SHA256STAMP:8e72905b6a11d025e5955b9997213d8219e1290cc26307fe517a3e0f19cc818e) diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index 148b54d21262..f69e8c103dc3 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -68,18 +68,18 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) -- **created_at** (number): the UNIX timestamp showing when this payment was initiated +- **payment\_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **created\_at** (number): the UNIX timestamp showing when this payment was initiated - **parts** (u32): how many attempts this took -- **amount_msat** (msat): Amount the recipient received -- **amount_sent_msat** (msat): Total amount we sent (including fees) +- **amount\_msat** (msat): Amount the recipient received +- **amount\_sent\_msat** (msat): Total amount we sent (including fees) - **status** (string): status of payment (always "complete") - **destination** (pubkey, optional): the final destination of the payment The following warnings may also be returned: -- **warning_partial_completion**: Not all parts of a multi-part payment have completed +- **warning\_partial\_completion**: Not all parts of a multi-part payment have completed [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -116,4 +116,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:17d0a04f94d00f4f842cd1924f490678167498559098b27bc4ac29dd907c3af1) +[comment]: # ( SHA256STAMP:6aca5a797d5e9bca63bbdc478d5ee0ce1dde592d3a4a4247f75d1831c8e6f38a) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index 648b097c0fc0..c23459e26c73 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -35,19 +35,19 @@ On success, an object containing **channels** is returned. It is an array of ob - **source** (pubkey): the source node - **destination** (pubkey): the destination node -- **short_channel_id** (short_channel_id): short channel id of channel +- **short\_channel\_id** (short_channel_id): short channel id of channel - **public** (boolean): true if this is announced (otherwise it must be our channel) -- **amount_msat** (msat): the total capacity of this channel (always a whole number of satoshis) -- **message_flags** (u8): as defined by BOLT #7 -- **channel_flags** (u8): as defined by BOLT #7 +- **amount\_msat** (msat): the total capacity of this channel (always a whole number of satoshis) +- **message\_flags** (u8): as defined by BOLT #7 +- **channel\_flags** (u8): as defined by BOLT #7 - **active** (boolean): true unless source has disabled it, or it's a local channel and the peer is disconnected or it's still opening or closing -- **last_update** (u32): UNIX timestamp on the last channel_update from *source* -- **base_fee_millisatoshi** (u32): Base fee changed by *source* to use this channel -- **fee_per_millionth** (u32): Proportional fee changed by *source* to use this channel, in parts-per-million +- **last\_update** (u32): UNIX timestamp on the last channel_update from *source* +- **base\_fee\_millisatoshi** (u32): Base fee changed by *source* to use this channel +- **fee\_per\_millionth** (u32): Proportional fee changed by *source* to use this channel, in parts-per-million - **delay** (u32): The number of blocks delay required by *source* to use this channel -- **htlc_minimum_msat** (msat): The smallest payment *source* will allow via this channel +- **htlc\_minimum\_msat** (msat): The smallest payment *source* will allow via this channel - **features** (hex): BOLT #9 features bitmap for this channel -- **htlc_maximum_msat** (msat, optional): The largest payment *source* will allow via this channel +- **htlc\_maximum\_msat** (msat, optional): The largest payment *source* will allow via this channel [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -79,4 +79,4 @@ Lightning RFC site - BOLT \#7: -[comment]: # ( SHA256STAMP:332e2a7c3f544a1cfc2a1a5e0728d54756249852f4060ca9fd47c137263b1e9d) +[comment]: # ( SHA256STAMP:2c4d604d1cec7372129bc863b1e056943abb24d6a354c0e9ae76d8fdecfd4953) diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 9ebe710a02d6..237a147b70f5 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -215,4 +215,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:6df5cfe511b7223a7a39fab1f36dcb46d8caef2726d9dbdf6c892a03cf4369f0) +[comment]: # ( SHA256STAMP:633de58c810a3d0692e4fe7c58265cf4eec1fe85dffc0139a9af1b23936b61e8) diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index 5b4125e2efda..ed71f94bec28 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -47,4 +47,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3b6eb48324fe4bab31a40460af1d5c98b8a938f27c1af758b15c955bb2204e58) +[comment]: # ( SHA256STAMP:45448ccd8947127b2008b4b7d8ab30bb348a45c9ddbced2c690d8b08a102d058) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 269aa8e25aca..b661b045673e 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -24,22 +24,22 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **forwards** is returned. It is an array of objects, where each object contains: -- **in_channel** (short_channel_id): the channel that received the HTLC -- **in_msat** (msat): the value of the incoming HTLC +- **in\_channel** (short_channel_id): the channel that received the HTLC +- **in\_msat** (msat): the value of the incoming HTLC - **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed") -- **received_time** (number): the UNIX timestamp when this was received -- **out_channel** (short_channel_id, optional): the channel that the HTLC (trying to) forward to -- **payment_hash** (hex, optional): payment hash sought by HTLC (always 64 characters) +- **received\_time** (number): the UNIX timestamp when this was received +- **out\_channel** (short_channel_id, optional): the channel that the HTLC (trying to) forward to +- **payment\_hash** (hex, optional): payment hash sought by HTLC (always 64 characters) - **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") -If **out_msat** is present: +If **out\_msat** is present: - - **fee_msat** (msat): the amount this paid in fees - - **out_msat** (msat): the amount we sent out the *out_channel* + - **fee\_msat** (msat): the amount this paid in fees + - **out\_msat** (msat): the amount we sent out the *out_channel* If **status** is "settled" or "failed": - - **resolved_time** (number): the UNIX timestamp when this was resolved + - **resolved\_time** (number): the UNIX timestamp when this was resolved If **status** is "local_failed" or "failed": @@ -63,4 +63,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cc82cc624fd377f957a83e1d3a49607a7cfa3c87505ba70a3f3fa07d6d922089) +[comment]: # ( SHA256STAMP:e3c26f73040deb441a572d848eda3d6da924c490e5b9ac55c5d87432f4144646) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index 02cc9860586b..d2596e263251 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -25,7 +25,7 @@ On success, an object is returned, containing: - **outputs** (array of objects): - **txid** (txid): the ID of the spendable transaction - **output** (u32): the index within *txid* - - **amount_msat** (msat): the amount of the output + - **amount\_msat** (msat): the amount of the output - **scriptpubkey** (hex): the scriptPubkey of the output - **status** (string) (one of "unconfirmed", "confirmed", "spent") - **reserved** (boolean): whether this UTXO is currently reserved for an in-flight tx @@ -38,23 +38,23 @@ On success, an object is returned, containing: If **reserved** is "true": - - **reserved_to_block** (u32): Block height where reservation will expire + - **reserved\_to\_block** (u32): Block height where reservation will expire - **channels** (array of objects): - - **peer_id** (pubkey): the peer with which the channel is opened - - **our_amount_msat** (msat): available satoshis on our node's end of the channel - - **amount_msat** (msat): total channel value - - **funding_txid** (txid): funding transaction id - - **funding_output** (u32): the 0-based index of the output in the funding transaction + - **peer\_id** (pubkey): the peer with which the channel is opened + - **our\_amount\_msat** (msat): available satoshis on our node's end of the channel + - **amount\_msat** (msat): total channel value + - **funding\_txid** (txid): funding transaction id + - **funding\_output** (u32): the 0-based index of the output in the funding transaction - **connected** (boolean): whether the channel peer is connected - **state** (string): the channel state, in particular "CHANNELD_NORMAL" means the channel can be used normally (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") If **state** is "CHANNELD_NORMAL": - - **short_channel_id** (short_channel_id): short channel id of channel + - **short\_channel\_id** (short_channel_id): short channel id of channel If **state** is "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN" or "ONCHAIN": - - **short_channel_id** (short_channel_id, optional): short channel id of channel (only if funding reached lockin depth before closing) + - **short\_channel\_id** (short_channel_id, optional): short channel id of channel (only if funding reached lockin depth before closing) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:999fb813822e397fdb22dc79cfb5b766e79766073a8c2803a47b56ea4786fb8e) +[comment]: # ( SHA256STAMP:319598cb35f3a9c6f336191363b1e38df74b467c0989942b5f4e0a998d0f6339) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index 04eff6ca1e9e..df0c33dc19b3 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -24,22 +24,22 @@ RETURN VALUE On success, an object containing **invoices** is returned. It is an array of objects, where each object contains: - **label** (string): unique label supplied at invoice creation -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid, unpaid or unpayable (one of "unpaid", "paid", "expired") -- **expires_at** (u64): UNIX timestamp of when it will become / became unpayable +- **expires\_at** (u64): UNIX timestamp of when it will become / became unpayable - **description** (string, optional): description used in the invoice -- **amount_msat** (msat, optional): the amount required to pay this invoice +- **amount\_msat** (msat, optional): the amount required to pay this invoice - **bolt11** (string, optional): the BOLT11 string (always present unless *bolt12* is) - **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is) -- **local_offer_id** (hex, optional): the *id* of our offer which created this invoice (**experimental-offers** only). (always 64 characters) -- **payer_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only). +- **local\_offer\_id** (hex, optional): the *id* of our offer which created this invoice (**experimental-offers** only). (always 64 characters) +- **payer\_note** (string, optional): the optional *payer_note* from invoice_request which created this invoice (**experimental-offers** only). If **status** is "paid": - - **pay_index** (u64): Unique incrementing index for this payment - - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - - **paid_at** (u64): UNIX timestamp of when it was paid - - **payment_preimage** (secret): proof of payment (always 64 characters) + - **pay\_index** (u64): Unique incrementing index for this payment + - **amount\_received\_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) + - **paid\_at** (u64): UNIX timestamp of when it was paid + - **payment\_preimage** (secret): proof of payment (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:06dceb4701a60fc36d9823c107fc34d2b2d5d13cb7c60ae0ec1db3128cffa55f) +[comment]: # ( SHA256STAMP:bfa699310cf025668febb42385b52e6238dfbbc0b5303a28989433d3645495c2) diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index a46fdfb08aba..f13eed35cee2 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -30,9 +30,9 @@ RETURN VALUE On success, an object containing **nodes** is returned. It is an array of objects, where each object contains: - **nodeid** (pubkey): the public key of the node -- **last_timestamp** (u32, optional): A node_announcement has been received for this node (UNIX timestamp) +- **last\_timestamp** (u32, optional): A node_announcement has been received for this node (UNIX timestamp) -If **last_timestamp** is present: +If **last\_timestamp** is present: - **alias** (string): The fun alias this node advertized (up to 32 characters) - **color** (hex): The favorite RGB color this node advertized (always 6 characters) @@ -45,15 +45,15 @@ If **last_timestamp** is present: - **address** (string): address in expected format for **type** -If **option_will_fund** is present: +If **option\_will\_fund** is present: - - **option_will_fund** (object): - - **lease_fee_base_msat** (msat): the fixed fee for a lease (whole number of satoshis) - - **lease_fee_basis** (u32): the proportional fee in basis points (parts per 10,000) for a lease - - **funding_weight** (u32): the onchain weight you'll have to pay for a lease - - **channel_fee_max_base_msat** (msat): the maximum base routing fee this node will charge during the lease - - **channel_fee_max_proportional_thousandths** (u32): the maximum proportional routing fee this node will charge during the lease (in thousandths, not millionths like channel_update) - - **compact_lease** (hex): the lease as represented in the node_announcement + - **option\_will\_fund** (object): + - **lease\_fee\_base\_msat** (msat): the fixed fee for a lease (whole number of satoshis) + - **lease\_fee\_basis** (u32): the proportional fee in basis points (parts per 10,000) for a lease + - **funding\_weight** (u32): the onchain weight you'll have to pay for a lease + - **channel\_fee\_max\_base\_msat** (msat): the maximum base routing fee this node will charge during the lease + - **channel\_fee\_max\_proportional\_thousandths** (u32): the maximum proportional routing fee this node will charge during the lease (in thousandths, not millionths like channel_update) + - **compact\_lease** (hex): the lease as represented in the node_announcement [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -99,4 +99,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:eca3d33813f3b3169317b8c4f573663416dd0e87d050a14feff64b754a70c2cb) +[comment]: # ( SHA256STAMP:452467d60a45d9d685b054644c94a8b7998d5e9f5dd30474006fe9f4f002eb67) diff --git a/doc/lightning-listoffers.7.md b/doc/lightning-listoffers.7.md index 037a215f53b8..6d1a14a7b848 100644 --- a/doc/lightning-listoffers.7.md +++ b/doc/lightning-listoffers.7.md @@ -32,11 +32,11 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **offers** is returned. It is an array of objects, where each object contains: -- **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) +- **offer\_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) - **active** (boolean): whether this can still be used -- **single_use** (boolean): whether this expires as soon as it's paid +- **single\_use** (boolean): whether this expires as soon as it's paid - **bolt12** (string): the bolt12 encoding of the offer -- **bolt12_unsigned** (string): the bolt12 encoding of the offer, without signature +- **bolt12\_unsigned** (string): the bolt12 encoding of the offer, without signature - **used** (boolean): True if an associated invoice has been paid - **label** (string, optional): the (optional) user-specified label @@ -81,4 +81,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:c8335d88dde3b056b69d4d4b3cb819844dbbb219410fb0277a148fcf7b4d2ed6) +[comment]: # ( SHA256STAMP:61677f64d10f6ffad32d19f98273925c5f2a1d9cda47bdb5c44cf5a30a34a62d) diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index e04180edaf03..6ae899217852 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -19,9 +19,9 @@ RETURN VALUE [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) +- **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 +- **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) @@ -30,13 +30,13 @@ On success, an object containing **pays** is returned. It is an array of object 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 + - **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). + - **number\_of\_parts** (u64, optional): the number of parts for a successful payment (only if more than one). If **status** is "failed": @@ -61,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:074ced29c00a0aff31098ccaae1c1f56364cc3aaaad34485a7c6b1c3cd9b3670) +[comment]: # ( SHA256STAMP:fd723be0ea9b4d6722adad5d325de6cf9c53e1842281205a82ee74ab702a17bd) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index ab9dbe7a15a7..6445cff5de5a 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -48,81 +48,81 @@ On success, an object containing **peers** is returned. It is an array of objec - **opener** (string): Who initiated the channel (one of "local", "remote") - **features** (array of strings): - BOLT #9 features which apply to this channel (one of "option_static_remotekey", "option_anchor_outputs", "option_zeroconf") - - **scratch_txid** (txid, optional): The txid we would use if we went onchain now + - **scratch\_txid** (txid, optional): The txid we would use if we went onchain now - **feerate** (object, optional): Feerates for the current tx: - **perkw** (u32): Feerate per 1000 weight (i.e kSipa) - **perkb** (u32): Feerate per 1000 virtual bytes - **owner** (string, optional): The current subdaemon controlling this connection - - **short_channel_id** (short_channel_id, optional): The short_channel_id (once locked in) - - **channel_id** (hash, optional): The full channel_id (always 64 characters) - - **funding_txid** (txid, optional): ID of the funding transaction - - **funding_outnum** (u32, optional): The 0-based output number of the funding transaction which opens the channel - - **initial_feerate** (string, optional): For inflight opens, the first feerate used to initiate the channel open - - **last_feerate** (string, optional): For inflight opens, the most recent feerate used on the channel open - - **next_feerate** (string, optional): For inflight opens, the next feerate we'll use for the channel open - - **next_fee_step** (u32, optional): For inflight opens, the next feerate step we'll use for the channel open + - **short\_channel\_id** (short_channel_id, optional): The short_channel_id (once locked in) + - **channel\_id** (hash, optional): The full channel_id (always 64 characters) + - **funding\_txid** (txid, optional): ID of the funding transaction + - **funding\_outnum** (u32, optional): The 0-based output number of the funding transaction which opens the channel + - **initial\_feerate** (string, optional): For inflight opens, the first feerate used to initiate the channel open + - **last\_feerate** (string, optional): For inflight opens, the most recent feerate used on the channel open + - **next\_feerate** (string, optional): For inflight opens, the next feerate we'll use for the channel open + - **next\_fee\_step** (u32, optional): For inflight opens, the next feerate step we'll use for the channel open - **inflight** (array of objects, optional): Current candidate funding transactions (only for dual-funding): - - **funding_txid** (txid): ID of the funding transaction - - **funding_outnum** (u32): The 0-based output number of the funding transaction which opens the channel + - **funding\_txid** (txid): ID of the funding transaction + - **funding\_outnum** (u32): The 0-based output number of the funding transaction which opens the channel - **feerate** (string): The feerate for this funding transaction in per-1000-weight, with "kpw" appended - - **total_funding_msat** (msat): total amount in the channel - - **our_funding_msat** (msat): amount we have in the channel - - **scratch_txid** (txid): The commitment transaction txid we would use if we went onchain now - - **close_to** (hex, optional): scriptPubkey which we have to close to if we mutual close + - **total\_funding\_msat** (msat): total amount in the channel + - **our\_funding\_msat** (msat): amount we have in the channel + - **scratch\_txid** (txid): The commitment transaction txid we would use if we went onchain now + - **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close - **private** (boolean, optional): if False, we will not announce this channel - **closer** (string, optional): Who initiated the channel close (one of "local", "remote") - **funding** (object, optional): - - **local_funds_msat** (msat): Amount of channel we funded - - **remote_funds_msat** (msat): Amount of channel they funded - - **local_msat** (msat, optional): Amount of channel we funded (deprecated) - - **remote_msat** (msat, optional): Amount of channel they funded (deprecated) - - **pushed_msat** (msat, optional): Amount pushed from opener to peer - - **fee_paid_msat** (msat, optional): Amount we paid peer at open - - **fee_rcvd_msat** (msat, optional): Amount we were paid by peer at open - - **to_us_msat** (msat, optional): how much of channel is owed to us - - **min_to_us_msat** (msat, optional): least amount owed to us ever - - **max_to_us_msat** (msat, optional): most amount owed to us ever - - **total_msat** (msat, optional): total amount in the channel - - **fee_base_msat** (msat, optional): amount we charge to use the channel - - **fee_proportional_millionths** (u32, optional): amount we charge to use the channel in parts-per-million - - **dust_limit_msat** (msat, optional): minimum amount for an output on the channel transactions - - **max_total_htlc_in_msat** (msat, optional): max amount accept in a single payment - - **their_reserve_msat** (msat, optional): minimum we insist they keep in channel - - **our_reserve_msat** (msat, optional): minimum they insist we keep in channel - - **spendable_msat** (msat, optional): total we could send through channel - - **receivable_msat** (msat, optional): total peer could send through channel - - **minimum_htlc_in_msat** (msat, optional): the minimum amount HTLC we accept - - **minimum_htlc_out_msat** (msat, optional): the minimum amount HTLC we will send - - **maximum_htlc_out_msat** (msat, optional): the maximum amount HTLC we will send - - **their_to_self_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close - - **our_to_self_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close - - **max_accepted_htlcs** (u32, optional): Maximum number of incoming HTLC we will accept at once + - **local\_funds\_msat** (msat): Amount of channel we funded + - **remote\_funds\_msat** (msat): Amount of channel they funded + - **local\_msat** (msat, optional): Amount of channel we funded (deprecated) + - **remote\_msat** (msat, optional): Amount of channel they funded (deprecated) + - **pushed\_msat** (msat, optional): Amount pushed from opener to peer + - **fee\_paid\_msat** (msat, optional): Amount we paid peer at open + - **fee\_rcvd\_msat** (msat, optional): Amount we were paid by peer at open + - **to\_us\_msat** (msat, optional): how much of channel is owed to us + - **min\_to\_us\_msat** (msat, optional): least amount owed to us ever + - **max\_to\_us\_msat** (msat, optional): most amount owed to us ever + - **total\_msat** (msat, optional): total amount in the channel + - **fee\_base\_msat** (msat, optional): amount we charge to use the channel + - **fee\_proportional\_millionths** (u32, optional): amount we charge to use the channel in parts-per-million + - **dust\_limit\_msat** (msat, optional): minimum amount for an output on the channel transactions + - **max\_total\_htlc\_in\_msat** (msat, optional): max amount accept in a single payment + - **their\_reserve\_msat** (msat, optional): minimum we insist they keep in channel + - **our\_reserve\_msat** (msat, optional): minimum they insist we keep in channel + - **spendable\_msat** (msat, optional): total we could send through channel + - **receivable\_msat** (msat, optional): total peer could send through channel + - **minimum\_htlc\_in\_msat** (msat, optional): the minimum amount HTLC we accept + - **minimum\_htlc\_out\_msat** (msat, optional): the minimum amount HTLC we will send + - **maximum\_htlc\_out\_msat** (msat, optional): the maximum amount HTLC we will send + - **their\_to\_self\_delay** (u32, optional): the number of blocks before they can take their funds if they unilateral close + - **our\_to\_self\_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close + - **max\_accepted\_htlcs** (u32, optional): Maximum number of incoming HTLC we will accept at once - **alias** (object, optional): - **local** (short_channel_id, optional): An alias assigned by this node to this channel, used for outgoing payments - **remote** (short_channel_id, optional): An alias assigned by the remote node to this channel, usable in routehints and invoices - - **state_changes** (array of objects, optional): Prior state changes: + - **state\_changes** (array of objects, optional): Prior state changes: - **timestamp** (string): UTC timestamp of form YYYY-mm-ddTHH:MM:SS.%03dZ - - **old_state** (string): Previous state (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") - - **new_state** (string): New state (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") + - **old\_state** (string): Previous state (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") + - **new\_state** (string): New state (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") - **cause** (string): What caused the change (one of "unknown", "local", "user", "remote", "protocol", "onchain") - **message** (string): Human-readable explanation - **status** (array of strings, optional): - Billboard log of significant changes - - **in_payments_offered** (u64, optional): Number of incoming payment attempts - - **in_offered_msat** (msat, optional): Total amount of incoming payment attempts - - **in_payments_fulfilled** (u64, optional): Number of successful incoming payment attempts - - **in_fulfilled_msat** (msat, optional): Total amount of successful incoming payment attempts - - **out_payments_offered** (u64, optional): Number of outgoing payment attempts - - **out_offered_msat** (msat, optional): Total amount of outgoing payment attempts - - **out_payments_fulfilled** (u64, optional): Number of successful outgoing payment attempts - - **out_fulfilled_msat** (msat, optional): Total amount of successful outgoing payment attempts + - **in\_payments\_offered** (u64, optional): Number of incoming payment attempts + - **in\_offered\_msat** (msat, optional): Total amount of incoming payment attempts + - **in\_payments\_fulfilled** (u64, optional): Number of successful incoming payment attempts + - **in\_fulfilled\_msat** (msat, optional): Total amount of successful incoming payment attempts + - **out\_payments\_offered** (u64, optional): Number of outgoing payment attempts + - **out\_offered\_msat** (msat, optional): Total amount of outgoing payment attempts + - **out\_payments\_fulfilled** (u64, optional): Number of successful outgoing payment attempts + - **out\_fulfilled\_msat** (msat, optional): Total amount of successful outgoing payment attempts - **htlcs** (array of objects, optional): current HTLCs in this channel: - **direction** (string): Whether it came from peer, or is going to peer (one of "in", "out") - **id** (u64): Unique ID for this htlc on this channel in this direction - - **amount_msat** (msat): Amount send/received for this HTLC + - **amount\_msat** (msat): Amount send/received for this HTLC - **expiry** (u32): Block this HTLC expires at - - **payment_hash** (hash): the hash of the payment_preimage which will prove payment (always 64 characters) - - **local_trimmed** (boolean, optional): if this is too small to enforce onchain (always *true*) + - **payment\_hash** (hash): the hash of the payment_preimage which will prove payment (always 64 characters) + - **local\_trimmed** (boolean, optional): if this is too small to enforce onchain (always *true*) - **status** (string, optional): set if this HTLC is currently waiting on a hook (and shows what plugin) If **direction** is "out": @@ -133,43 +133,43 @@ On success, an object containing **peers** is returned. It is an array of objec - **state** (string): Status of the HTLC (one of "RCVD_ADD_HTLC", "RCVD_ADD_COMMIT", "SENT_ADD_REVOCATION", "SENT_ADD_ACK_COMMIT", "RCVD_ADD_ACK_REVOCATION", "SENT_REMOVE_HTLC", "SENT_REMOVE_COMMIT", "RCVD_REMOVE_REVOCATION", "RCVD_REMOVE_ACK_COMMIT", "SENT_REMOVE_ACK_REVOCATION") - If **close_to** is present: + If **close\_to** is present: - - **close_to_addr** (string, optional): The bitcoin address we will close to + - **close\_to\_addr** (string, optional): The bitcoin address we will close to - If **scratch_txid** is present: + If **scratch\_txid** is present: - - **last_tx_fee_msat** (msat): fee attached to this the current tx + - **last\_tx\_fee\_msat** (msat): fee attached to this the current tx - If **short_channel_id** is present: + If **short\_channel\_id** is present: - **direction** (u32): 0 if we're the lesser node_id, 1 if we're the greater If **inflight** is present: - - **initial_feerate** (string): The feerate for the initial funding transaction in per-1000-weight, with "kpw" appended - - **last_feerate** (string): The feerate for the latest funding transaction in per-1000-weight, with "kpw" appended - - **next_feerate** (string): The minimum feerate for the next funding transaction in per-1000-weight, with "kpw" appended + - **initial\_feerate** (string): The feerate for the initial funding transaction in per-1000-weight, with "kpw" appended + - **last\_feerate** (string): The feerate for the latest funding transaction in per-1000-weight, with "kpw" appended + - **next\_feerate** (string): The minimum feerate for the next funding transaction in per-1000-weight, with "kpw" appended - **log** (array of objects, optional): if *level* is specified, logs for this peer: - **type** (string) (one of "SKIPPED", "BROKEN", "UNUSUAL", "INFO", "DEBUG", "IO_IN", "IO_OUT") If **type** is "SKIPPED": - - **num_skipped** (u32): number of deleted/omitted entries + - **num\_skipped** (u32): number of deleted/omitted entries If **type** is "BROKEN", "UNUSUAL", "INFO" or "DEBUG": - **time** (string): UNIX timestamp with 9 decimal places - **source** (string): The particular logbook this was found in - **log** (string): The actual log message - - **node_id** (pubkey): The peer this is associated with + - **node\_id** (pubkey): The peer this is associated with If **type** is "IO_IN" or "IO_OUT": - **time** (string): UNIX timestamp with 9 decimal places - **source** (string): The particular logbook this was found in - **log** (string): The actual log message - - **node_id** (pubkey): The peer this is associated with + - **node\_id** (pubkey): The peer this is associated with - **data** (hex): The IO which occurred If **connected** is *true*: @@ -177,7 +177,7 @@ If **connected** is *true*: - **netaddr** (array of strings): A single entry array: - address, e.g. 1.2.3.4:1234 - **features** (hex): bitmap of BOLT #9 features from peer's INIT message - - **remote_addr** (string, optional): The public IPv4/6 address the peer sees us from, e.g. 1.2.3.4:1234 + - **remote\_addr** (string, optional): The public IPv4/6 address the peer sees us from, e.g. 1.2.3.4:1234 [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -399,4 +399,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:c99d53a4b3ada3bae6972f97eea600b7a039958313772cd6370eec818c2e4a99) +[comment]: # ( SHA256STAMP:e80b24123fdeea4eacda71bb7d824b9234bf17c68239ebf7915d2ab747c58fdc) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index bf36a15e1d2d..09d873857177 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -26,12 +26,12 @@ Note that the returned array is ordered by increasing *id*. 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** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): 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 -- **amount_sent_msat** (msat): The amount sent +- **created\_at** (u64): the UNIX timestamp showing when this payment was initiated +- **amount\_sent\_msat** (msat): The amount sent - **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash -- **amount_msat** (msat, optional): The amount delivered to destination (if known) +- **amount\_msat** (msat, optional): The amount delivered to destination (if known) - **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) @@ -40,7 +40,7 @@ On success, an object containing **payments** is returned. It is an array of ob If **status** is "complete": - - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment\_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "failed": @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:0a901adcb1b30622fdab38aa39a5073087488b2ad446fb1b9efdc791ac954591) +[comment]: # ( SHA256STAMP:c5d98a40542cb43881bea138bf3da404eba5bc73ed9e0acc8343e0423cc9c602) diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index f02023e8ad66..7aae61bb6a90 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -41,7 +41,7 @@ On success, an object containing **transactions** is returned. It is an array o - **channel** (short_channel_id, optional): the channel this input is associated with (*EXPERIMENTAL_FEATURES* only) - **outputs** (array of objects): Each output, in order: - **index** (u32): the 0-based output number - - **amount_msat** (msat): the amount of the output + - **amount\_msat** (msat): the amount of the output - **scriptPubKey** (hex): the scriptPubKey - **type** (string, optional): the purpose of this output (*EXPERIMENTAL_FEATURES* only) (one of "theirs", "deposit", "withdraw", "channel_funding", "channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat") - **channel** (short_channel_id, optional): the channel this output is associated with (*EXPERIMENTAL_FEATURES* only) @@ -105,4 +105,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:9cb66a3febe7c37e1892e2fc9f486a6e896e9406a1d9514e5283d4c5f8f6b707) +[comment]: # ( SHA256STAMP:e1f4e86db0fd48d695ad0c30ace5dea6f40e1502847a258ef9580e30b52712f7) diff --git a/doc/lightning-makesecret.7.md b/doc/lightning-makesecret.7.md index 532a16c0c4e9..283b204cc08f 100644 --- a/doc/lightning-makesecret.7.md +++ b/doc/lightning-makesecret.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c011f9e0b3ebd79b4172daf518d216dda9149c86e23918d0fe979f83b09fd117) +[comment]: # ( SHA256STAMP:47f98983bc74e75b5e9ad55ebc84771a7819717e8e41f2398d0e0227a8670044) diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index 0b60d6e599ac..93a8190c67cf 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -102,11 +102,11 @@ 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 -- **channel_ids** (array of objects): +- **channel\_ids** (array of objects): - **id** (pubkey): The peer we opened the channel with - **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` + - **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` - **failed** (array of objects, optional): any peers we failed to open with (if *minchannels* was specified less than the number of destinations): - **id** (pubkey): The peer we failed to open the channel with - **method** (string): What stage we failed at (one of "connect", "openchannel_init", "fundchannel_start", "fundchannel_complete") @@ -159,4 +159,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:19cdc2bffdec420ae1fc48591af22a49d2d85104d6a14dcc166909f23e86ecfa) +[comment]: # ( SHA256STAMP:d5af7087b3e31d80df0ce5c09c55b51141bfbf158802d7f5cd82caa21110453e) diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index 0d81e230831b..b5a63c0fa2b2 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:e2b8a4f88c61ef7823e1b8541719d2d5ccb05181116785ac8e9be02816d65c0d) +[comment]: # ( SHA256STAMP:3c79c00a7115ea353d0eb27b8fb6db0203dbd40226291fc0b5f57bc29bd8acc8) diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index 98b8d4ed3478..6ac38029ec04 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7bf0734c6f42effdd936443ddc7eace065616bb345efcb3d76850fe7d07052c0) +[comment]: # ( SHA256STAMP:0d4165582ac09b6f130bd6910873f5073ab7ff852711fa1fb45177726a978899) diff --git a/doc/lightning-notifications.7.md b/doc/lightning-notifications.7.md index 9d03be3bf449..fefa8de8a5b1 100644 --- a/doc/lightning-notifications.7.md +++ b/doc/lightning-notifications.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:bdc678495e0e0e626cdc71e73e8179dc8350de9862c4458933607fa62d8a16f7) +[comment]: # ( SHA256STAMP:4d9f888d10faca2bf94d1b52510cf21fbeebae4efda0946f03d04b0ef4bc88a2) diff --git a/doc/lightning-offer.7.md b/doc/lightning-offer.7.md index 01675a63e426..8818c6f3c9e5 100644 --- a/doc/lightning-offer.7.md +++ b/doc/lightning-offer.7.md @@ -97,11 +97,11 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) +- **offer\_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) - **active** (boolean): whether this can still be used (always *true*) -- **single_use** (boolean): whether this expires as soon as it's paid (reflects the *single_use* parameter) +- **single\_use** (boolean): whether this expires as soon as it's paid (reflects the *single_use* parameter) - **bolt12** (string): the bolt12 encoding of the offer -- **bolt12_unsigned** (string): the bolt12 encoding of the offer, without a signature +- **bolt12\_unsigned** (string): the bolt12 encoding of the offer, without a signature - **used** (boolean): True if an associated invoice has been paid - **created** (boolean): false if the offer already existed - **label** (string, optional): the (optional) user-specified label @@ -135,4 +135,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b4a362fe13f683f82bda5a293805d5d506719382c846ad363c357fef4a5cef7c) +[comment]: # ( SHA256STAMP:62546f8bd62788615d20aa1881fc55328bea3fd3793d4395984972cbf33c7219) diff --git a/doc/lightning-offerout.7.md b/doc/lightning-offerout.7.md index 5430a81182a4..9d632356ec9d 100644 --- a/doc/lightning-offerout.7.md +++ b/doc/lightning-offerout.7.md @@ -56,11 +56,11 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **offer_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) +- **offer\_id** (hex): the id of this offer (merkle hash of non-signature fields) (always 64 characters) - **active** (boolean): whether this will pay a matching incoming invoice (always *true*) -- **single_use** (boolean): whether this expires as soon as it's paid out (always *true*) +- **single\_use** (boolean): whether this expires as soon as it's paid out (always *true*) - **bolt12** (string): the bolt12 encoding of the offer -- **bolt12_unsigned** (string): the bolt12 encoding of the offer, without a signature +- **bolt12\_unsigned** (string): the bolt12 encoding of the offer, without a signature - **used** (boolean): True if an incoming invoice has been paid (always *false*) - **created** (boolean): false if the offer already existed - **label** (string, optional): the (optional) user-specified label @@ -100,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1aeec6f596cae41738c39a12c02ee1c899569953f5be9378a06c9911082de1db) +[comment]: # ( SHA256STAMP:e13d4ef2c90ce11239857da6cab6489ddbaae473b31f2f10d8e5d1cd46e952a3) diff --git a/doc/lightning-openchannel_abort.7.md b/doc/lightning-openchannel_abort.7.md index e088c1d6f1c6..a88ed1045995 100644 --- a/doc/lightning-openchannel_abort.7.md +++ b/doc/lightning-openchannel_abort.7.md @@ -22,8 +22,8 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **channel_id** (hex): the channel id of the aborted channel (always 64 characters) -- **channel_canceled** (boolean): whether this is completely canceled (there may be remaining in-flight transactions) +- **channel\_id** (hex): the channel id of the aborted channel (always 64 characters) +- **channel\_canceled** (boolean): whether this is completely canceled (there may be remaining in-flight transactions) - **reason** (string): usually "Abort requested", but if it happened to fail at the same time it could be different [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -55,4 +55,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:ea00b648aba89bc585ae67bfcc821dabda2c2ffea6b0dffab80ba46edaf2e3ba) +[comment]: # ( SHA256STAMP:b02ee781aadf998bf031e8e797622ae9a6307c6c3a0c6d1fdaa3760cbbc6c0c6) diff --git a/doc/lightning-openchannel_bump.7.md b/doc/lightning-openchannel_bump.7.md index 2160673728da..2e9846de33d7 100644 --- a/doc/lightning-openchannel_bump.7.md +++ b/doc/lightning-openchannel_bump.7.md @@ -38,10 +38,10 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **channel_id** (hex): the channel id of the channel (always 64 characters) +- **channel\_id** (hex): the channel id of the channel (always 64 characters) - **psbt** (string): the (incomplete) PSBT of the RBF transaction -- **commitments_secured** (boolean): whether the *psbt* is complete (always *false*) -- **funding_serial** (u64): the serial_id of the funding output in the *psbt* +- **commitments\_secured** (boolean): whether the *psbt* is complete (always *false*) +- **funding\_serial** (u64): the serial_id of the funding output in the *psbt* [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -81,4 +81,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:ba49815f17a30831671c7253051b15192c55882b524f4999ae0928829bd3fff7) +[comment]: # ( SHA256STAMP:40407dc22bff8d157f1f87683e8b97ab6a0495ec1454e5ef05d4a050eb10dfc8) diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index 7c48a3b1fb7a..2b9916ce5197 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -53,10 +53,10 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **channel_id** (hex): the channel id of the channel (always 64 characters) +- **channel\_id** (hex): the channel id of the channel (always 64 characters) - **psbt** (string): the (incomplete) PSBT of the funding transaction -- **commitments_secured** (boolean): whether the *psbt* is complete (always *false*) -- **funding_serial** (u64): the serial_id of the funding output in the *psbt* +- **commitments\_secured** (boolean): whether the *psbt* is complete (always *false*) +- **funding\_serial** (u64): the serial_id of the funding output in the *psbt* [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -103,4 +103,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:1dad89ace6e2434e6f1baeed18c4813799a3e560708de28c9ef2b34120dc903e) +[comment]: # ( SHA256STAMP:f78d8a4c015cad1e5065d8ae23375aa33bd9f318cd526ee88f65cccb6cda6ad9) diff --git a/doc/lightning-openchannel_signed.7.md b/doc/lightning-openchannel_signed.7.md index 5576e110b74f..9111eab70d96 100644 --- a/doc/lightning-openchannel_signed.7.md +++ b/doc/lightning-openchannel_signed.7.md @@ -32,7 +32,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **channel_id** (hex): the channel id of the channel (always 64 characters) +- **channel\_id** (hex): the channel id of the channel (always 64 characters) - **tx** (hex): the funding transaction - **txid** (txid): The txid of the **tx** @@ -67,4 +67,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:35d894ae39df25214656482a1680639fb2660783b4f95d1a042ae74c716c595f) +[comment]: # ( SHA256STAMP:7e31569a2b356664ff818c9afc8347aa2dcd1ba128ff9a5f74fda6f441aa5904) diff --git a/doc/lightning-openchannel_update.7.md b/doc/lightning-openchannel_update.7.md index 73b038cc82a7..f05b35060814 100644 --- a/doc/lightning-openchannel_update.7.md +++ b/doc/lightning-openchannel_update.7.md @@ -31,11 +31,11 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **channel_id** (hex): the channel id of the channel (always 64 characters) +- **channel\_id** (hex): the channel id of the channel (always 64 characters) - **psbt** (string): the PSBT of the funding transaction -- **commitments_secured** (boolean): whether the *psbt* is complete (if true, sign *psbt* and call `openchannel_signed` to complete the channel open) -- **funding_outnum** (u32): The index of the funding output in the psbt -- **close_to** (hex, optional): scriptPubkey which we have to close to if we mutual close +- **commitments\_secured** (boolean): whether the *psbt* is complete (if true, sign *psbt* and call `openchannel_signed` to complete the channel open) +- **funding\_outnum** (u32): The index of the funding output in the psbt +- **close\_to** (hex, optional): scriptPubkey which we have to close to if we mutual close [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:92a20c9731a38c03189d7396e196c0439ef4311407529542b41af967e365c43b) +[comment]: # ( SHA256STAMP:b1a1c34c05ff7f47d9ff790eb17d8df4ccfcfabf8471958fec8bf97ddebfc45d) diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md index af34e7ce0acd..139fb704a41b 100644 --- a/doc/lightning-parsefeerate.7.md +++ b/doc/lightning-parsefeerate.7.md @@ -44,4 +44,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9c3b23f12660f53e954687b1fcb1b4bfc629bd0714363c0f21c44958c499fd51) +[comment]: # ( SHA256STAMP:cf5309537ecb4aa57a6a74e607d1c0d581df22c43aa5d62a90d4eb43e63237f0) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index 8fc84ed797f3..f7f6add17d30 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -95,18 +95,18 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) -- **created_at** (number): the UNIX timestamp showing when this payment was initiated +- **payment\_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **created\_at** (number): the UNIX timestamp showing when this payment was initiated - **parts** (u32): how many attempts this took -- **amount_msat** (msat): Amount the recipient received -- **amount_sent_msat** (msat): Total amount we sent (including fees) +- **amount\_msat** (msat): Amount the recipient received +- **amount\_sent\_msat** (msat): Total amount we sent (including fees) - **status** (string): status of payment (one of "complete", "pending", "failed") - **destination** (pubkey, optional): the final destination of the payment The following warnings may also be returned: -- **warning_partial_completion**: Not all parts of a multi-part payment have completed +- **warning\_partial\_completion**: Not all parts of a multi-part payment have completed [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -167,4 +167,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:13926b44871e0a8d805eed668c371401a97f112a50481d49838975dcb7593329) +[comment]: # ( SHA256STAMP:24e27fab68719a3d6bae0e845cba95223d8c18a4f4c9a045225898cbd6abc2ef) diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index e7aeeb5c842f..8f03facb2bea 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -70,4 +70,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:8fd43121667f09f763914b27e53b8aecd855e1a611681f34de5e19f4ed13aabe) +[comment]: # ( SHA256STAMP:6c9c92f2387bb0108495d45cf2919203a805bd78db8a2d2a88ada80e881c04e3) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index ae123f6472e0..1579bb5b1398 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -84,4 +84,4 @@ RESOURCES Main web site: [writing plugins]: PLUGINS.md -[comment]: # ( SHA256STAMP:f19b2a53065fd2e571593330112e30e99845b88acbd58237d25c94c224c64686) +[comment]: # ( SHA256STAMP:17dc5bb65dc652ab4dee5fda0c3c9a909edc931c357773cbc988aede3d9fb49a) diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index 8a604e7b6a17..98e161d656f2 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -29,9 +29,9 @@ On success, an object containing **reservations** is returned. It is an array o - **txid** (txid): the transaction id - **vout** (u32): the output number which was reserved -- **was_reserved** (boolean): whether the input was already reserved +- **was\_reserved** (boolean): whether the input was already reserved - **reserved** (boolean): whether the input is now reserved (always *true*) -- **reserved_to_block** (u32): what blockheight the reservation will expire +- **reserved\_to\_block** (u32): what blockheight the reservation will expire [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:14873cda2c903c231c6707234b6b161b3cabf84ebe48c274e4c7f5f980207d13) +[comment]: # ( SHA256STAMP:f0aed27b4a1de3f76f62ba9882df18c596e017491ff04614666ce05bc8bb2535) diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index 1d8cf01b1b01..436183d08c11 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -69,4 +69,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ad5ceedef94690f4332e8b7b0fe5b639df6f38c6776d3c3be6f6f833f1eb954a) +[comment]: # ( SHA256STAMP:a4314cf59d13dee507086aa5353a1456238a4e57e4a166bdd0a2fa66a4be3736) diff --git a/doc/lightning-sendinvoice.7.md b/doc/lightning-sendinvoice.7.md index 3a4ff7370c24..37f27df9a6f2 100644 --- a/doc/lightning-sendinvoice.7.md +++ b/doc/lightning-sendinvoice.7.md @@ -43,18 +43,18 @@ On success, an object is returned, containing: - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice -- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid, unpaid or unpayable (one of "unpaid", "paid", "expired") -- **expires_at** (u64): UNIX timestamp of when it will become / became unpayable -- **amount_msat** (msat, optional): the amount required to pay this invoice +- **expires\_at** (u64): UNIX timestamp of when it will become / became unpayable +- **amount\_msat** (msat, optional): the amount required to pay this invoice - **bolt12** (string, optional): the BOLT12 string If **status** is "paid": - - **pay_index** (u64): Unique incrementing index for this payment - - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - - **paid_at** (u64): UNIX timestamp of when it was paid - - **payment_preimage** (hex): proof of payment (always 64 characters) + - **pay\_index** (u64): Unique incrementing index for this payment + - **amount\_received\_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) + - **paid\_at** (u64): UNIX timestamp of when it was paid + - **payment\_preimage** (hex): proof of payment (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -80,4 +80,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:2eb62b2da6067b6d6b2f5585d521600b722a45d7a6c74da882891fd5aa00bc77) +[comment]: # ( SHA256STAMP:adff5e5f321dc5245067f4ed6ad9a404f5f4a783ae5ab2715a9064cbd570f7f1) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index d2d18196300f..8bc0152eefff 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -94,11 +94,11 @@ RETURN VALUE On success, an object is returned, containing: - **id** (u64): unique ID for this payment attempt -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (could be complete if already sent previously) (one of "pending", "complete") -- **created_at** (u64): the UNIX timestamp showing when this payment was initiated -- **amount_sent_msat** (msat): The amount sent -- **amount_msat** (msat, optional): The amount delivered to destination (if known) +- **created\_at** (u64): the UNIX timestamp showing when this payment was initiated +- **amount\_sent\_msat** (msat): The amount sent +- **amount\_msat** (msat, optional): The amount delivered to destination (if known) - **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 supplied) @@ -107,7 +107,7 @@ On success, an object is returned, containing: If **status** is "complete": - - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment\_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "pending": @@ -135,4 +135,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:84195897945d1b1f8b4aabff611ded8198aa7a1aa959fcf903ec7bd3de1d1ebf) +[comment]: # ( SHA256STAMP:f98e537a7fe2e1b3f9bc10366317c1f1663e0fc6c6618564c38cc10181161658) diff --git a/doc/lightning-sendonionmessage.7.md b/doc/lightning-sendonionmessage.7.md index d045fbbd5ab7..1549d25d579a 100644 --- a/doc/lightning-sendonionmessage.7.md +++ b/doc/lightning-sendonionmessage.7.md @@ -43,4 +43,4 @@ Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:267a098af220600b0c3ed3e50e2704c24d7330c428fd80aaa22d0aec59a4efe1) +[comment]: # ( SHA256STAMP:8e7f2c4372f12ee7f79df114e9ac9539ad6b19821e6c808e47d1ba9f7981e8ea) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index 11b0db6554e0..b834bd391753 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -69,12 +69,12 @@ RETURN VALUE On success, an object is returned, containing: - **id** (u64): unique ID for this payment attempt -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (could be complete if already sent previously) (one of "pending", "complete") -- **created_at** (u64): the UNIX timestamp showing when this payment was initiated -- **amount_sent_msat** (msat): The amount sent +- **created\_at** (u64): the UNIX timestamp showing when this payment was initiated +- **amount\_sent\_msat** (msat): The amount sent - **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash -- **amount_msat** (msat, optional): The amount delivered to destination (if known) +- **amount\_msat** (msat, optional): The amount delivered to destination (if known) - **destination** (pubkey, optional): the final destination of the payment if known - **label** (string, optional): the *label*, if given to sendpay - **partid** (u64, optional): the *partid*, if given to sendpay @@ -83,7 +83,7 @@ On success, an object is returned, containing: If **status** is "complete": - - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment\_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) If **status** is "pending": @@ -142,4 +142,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:08f06099569549e627f675c647aba1b7576c8662c4ad6906ca1ff56062fc9afc) +[comment]: # ( SHA256STAMP:9b56a1eedebe426c6ed6018fa92f963884b8aea2787d602e1e413c2f79884ce0) diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index 42baad2d482a..2dd0cfe97464 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -66,4 +66,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:e2b8a4f88c61ef7823e1b8541719d2d5ccb05181116785ac8e9be02816d65c0d) +[comment]: # ( SHA256STAMP:3c79c00a7115ea353d0eb27b8fb6db0203dbd40226291fc0b5f57bc29bd8acc8) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 1ee6b417103c..98f93ed3c4dd 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -68,16 +68,16 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **channels** is returned. It is an array of objects, where each object contains: -- **peer_id** (pubkey): The node_id of the peer -- **channel_id** (hex): The channel_id of the channel (always 64 characters) -- **fee_base_msat** (msat): The resulting feebase (this is the BOLT #7 name) -- **fee_proportional_millionths** (u32): The resulting feeppm (this is the BOLT #7 name) -- **minimum_htlc_out_msat** (msat): The resulting htlcmin we will advertize (the BOLT #7 name is htlc_minimum_msat) -- **maximum_htlc_out_msat** (msat): The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat) -- **short_channel_id** (short_channel_id, optional): the short_channel_id (if locked in) +- **peer\_id** (pubkey): The node_id of the peer +- **channel\_id** (hex): The channel_id of the channel (always 64 characters) +- **fee\_base\_msat** (msat): The resulting feebase (this is the BOLT #7 name) +- **fee\_proportional\_millionths** (u32): The resulting feeppm (this is the BOLT #7 name) +- **minimum\_htlc\_out\_msat** (msat): The resulting htlcmin we will advertize (the BOLT #7 name is htlc_minimum_msat) +- **maximum\_htlc\_out\_msat** (msat): The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat) +- **short\_channel\_id** (short_channel_id, optional): the short_channel_id (if locked in) - the following warnings are possible: - - **warning_htlcmin_too_low**: The requested htlcmin was too low for this peer, so we set it to the minimum they will allow - - **warning_htlcmax_too_high**: The requested htlcmax was greater than the channel capacity, so we set it to the channel capacity + - **warning\_htlcmin\_too\_low**: The requested htlcmin was too low for this peer, so we set it to the minimum they will allow + - **warning\_htlcmax\_too\_high**: The requested htlcmax was greater than the channel capacity, so we set it to the channel capacity [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -107,4 +107,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f133a8053cf5b0d5f6d8823c62fceda134af24c43411307b764db3f3e2f4d261) +[comment]: # ( SHA256STAMP:28a16d5b1392dd72f9d7a040ce8e37907114fbaf1aed106314fab3f9126afba2) diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index ddd6a006a3b2..30c39072d127 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -52,9 +52,9 @@ On success, an object is returned, containing: - **base** (u32): The fee_base_msat value - **ppm** (u32): The fee_proportional_millionths value - **channels** (array of objects): channel(s) whose rate is now set: - - **peer_id** (pubkey): The node_id of the peer - - **channel_id** (hex): The channel_id of the channel (always 64 characters) - - **short_channel_id** (short_channel_id, optional): the short_channel_id (if locked in) + - **peer\_id** (pubkey): The node_id of the peer + - **channel\_id** (hex): The channel_id of the channel (always 64 characters) + - **short\_channel\_id** (short_channel_id, optional): the short_channel_id (if locked in) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -83,4 +83,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:165a54776eba3c727efc309a2e9d22bec204a4bd909b002b83082a97aa29c400) +[comment]: # ( SHA256STAMP:bd5af1e2190426012541af0eb582ca22461fb38ec70c7a235f4e7e92f7fc565d) diff --git a/doc/lightning-signmessage.7.md b/doc/lightning-signmessage.7.md index 8fa4891eb3a1..d94b2c3980be 100644 --- a/doc/lightning-signmessage.7.md +++ b/doc/lightning-signmessage.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:163cdae723e4b50715255b233ff2a817481ccff0b778d56c2ed0b88bd0cdc8a6) +[comment]: # ( SHA256STAMP:cf7372c221ee6846fb8e7aca3e841b687e4b4f21262a0cc8a430b375e17e70cb) diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index fea7c48f02b1..197a219da81a 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -41,7 +41,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **signed_psbt** (string): The fully signed PSBT +- **signed\_psbt** (string): The fully signed PSBT [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:9604daeee442beb457ce32f5f66fceb337e92bf7a613f8ca2e81214c5d3200db) +[comment]: # ( SHA256STAMP:79690a411b6db6801192bd462e2cf2c627a0a10e65a326e2801dc0e35e734714) diff --git a/doc/lightning-stop.7.md b/doc/lightning-stop.7.md index d82b8309898c..198d3eb0bf35 100644 --- a/doc/lightning-stop.7.md +++ b/doc/lightning-stop.7.md @@ -42,4 +42,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:2af4e0809f45c5a980e95a85d95295df42d9fa25660315fc73e6b38c193a91ce) +[comment]: # ( SHA256STAMP:cd6076ec79da278cca6f68be0560d71dc1b46651c3db48d81aafe1d19da0ff96) diff --git a/doc/lightning-txdiscard.7.md b/doc/lightning-txdiscard.7.md index 7cf342aeaed9..51f3da93bd81 100644 --- a/doc/lightning-txdiscard.7.md +++ b/doc/lightning-txdiscard.7.md @@ -18,7 +18,7 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object is returned, containing: -- **unsigned_tx** (hex): the unsigned transaction +- **unsigned\_tx** (hex): the unsigned transaction - **txid** (txid): the transaction id of *unsigned_tx* [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -45,4 +45,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:79152df2e06a1da8e6da7693b967b5e0187fd0281418839048c7453acac1c839) +[comment]: # ( SHA256STAMP:d763d6dda590b36227f606a404223327147606b495a20926d14a0f8444effdd7) diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index b5834804539f..7cb6f754c795 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -56,7 +56,7 @@ RETURN VALUE On success, an object is returned, containing: - **psbt** (string): the PSBT representing the unsigned transaction -- **unsigned_tx** (hex): the unsigned transaction +- **unsigned\_tx** (hex): the unsigned transaction - **txid** (txid): the transaction id of *unsigned_tx*; you hand this to lightning-txsend(7) or lightning-txdiscard(7), as the inputs of this transaction are reserved. [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -85,4 +85,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1b5693f74931f9a5746415a81268bdbd229e88250498b6ff86e3a76f8f2991c3) +[comment]: # ( SHA256STAMP:d0570b649a356bb7575da21dfe2f6287fe0a0eac388411470fa22897f7175b83) diff --git a/doc/lightning-txsend.7.md b/doc/lightning-txsend.7.md index 6ebfe5c8ca01..d020c4f5aea6 100644 --- a/doc/lightning-txsend.7.md +++ b/doc/lightning-txsend.7.md @@ -45,4 +45,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5ae95391f068ecafbb0fb742454ea4bf31e1cd47b56f7a82120f2b5b08aea070) +[comment]: # ( SHA256STAMP:8a96f2a5dd68a060602176385238fc7a9255928eef6d5b7f68916eaeb60428f3) diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index 6665a3c14a75..82ddb5831083 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -26,12 +26,12 @@ On success, an object containing **reservations** is returned. It is an array o - **txid** (txid): the transaction id - **vout** (u32): the output number which was reserved -- **was_reserved** (boolean): whether the input was already reserved (usually `true`) +- **was\_reserved** (boolean): whether the input was already reserved (usually `true`) - **reserved** (boolean): whether the input is now reserved (may still be `true` if it was reserved for a long time) If **reserved** is *true*: - - **reserved_to_block** (u32): what blockheight the reservation will expire + - **reserved\_to\_block** (u32): what blockheight the reservation will expire [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -55,4 +55,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9952e49efe40455558a4f1660e5e823570308c92c1762e74581ca5596961c7c6) +[comment]: # ( SHA256STAMP:d5ec7b9dba4387f8f1c59f1d97fe00cc8abcab793c0bb23f3bc32b02b7e2e882) diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index deaf5b74cc56..0be62bad7d1c 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -47,16 +47,16 @@ RETURN VALUE On success, an object is returned, containing: - **psbt** (string): Unsigned PSBT which fulfills the parameters given -- **feerate_per_kw** (u32): The feerate used to create the PSBT, in satoshis-per-kiloweight -- **estimated_final_weight** (u32): The estimated weight of the transaction once fully signed -- **excess_msat** (msat): 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** (u32, optional): The 0-based output number where change was placed (only if parameter *excess_as_change* was true and there was sufficient funds) +- **feerate\_per\_kw** (u32): The feerate used to create the PSBT, in satoshis-per-kiloweight +- **estimated\_final\_weight** (u32): The estimated weight of the transaction once fully signed +- **excess\_msat** (msat): 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** (u32, optional): The 0-based output number where change was placed (only if parameter *excess_as_change* was true and there was sufficient funds) - **reservations** (array of objects, optional): If *reserve* was true or a non-zero number, just as per lightning-reserveinputs(7): - **txid** (txid): The txid of the transaction - **vout** (u32): The 0-based output number - - **was_reserved** (boolean): Whether this output was previously reserved + - **was\_reserved** (boolean): Whether this output was previously reserved - **reserved** (boolean): Whether this output is now reserved (always *true*) - - **reserved_to_block** (u32): The blockheight the reservation will expire + - **reserved\_to\_block** (u32): The blockheight the reservation will expire [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -100,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1ccbb9c0a0d791e155ebc027465b908c3b2d5f9bd56eba7f5d22003c8e8172e0) +[comment]: # ( SHA256STAMP:0fa67418b5d62dc8dd0ccdbda4113da73a3d7761e23c2ad697a533783c43c37d) diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index 2e7d452804fc..cc6859b7b590 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -38,19 +38,19 @@ On success, an object is returned, containing: - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid or expired (one of "paid", "expired") -- **expires_at** (u64): UNIX timestamp of when it will become / became unpayable -- **amount_msat** (msat, optional): the amount required to pay this invoice +- **expires\_at** (u64): UNIX timestamp of when it will become / became unpayable +- **amount\_msat** (msat, optional): the amount required to pay this invoice - **bolt11** (string, optional): the BOLT11 string (always present unless *bolt12* is) - **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is) If **status** is "paid": - - **pay_index** (u64): Unique incrementing index for this payment - - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - - **paid_at** (u64): UNIX timestamp of when it was paid - - **payment_preimage** (secret): proof of payment (always 64 characters) + - **pay\_index** (u64): Unique incrementing index for this payment + - **amount\_received\_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) + - **paid\_at** (u64): UNIX timestamp of when it was paid + - **payment\_preimage** (secret): proof of payment (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -75,4 +75,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ce034f1801260480032a98348aae928055f3d9edf12f45cefcd8b6007289ff83) +[comment]: # ( SHA256STAMP:016afebade3ee5a7ac5abbd125f8db78f6c8b41f0e510c8f4c3b6a385e6f3a26) diff --git a/doc/lightning-waitblockheight.7.md b/doc/lightning-waitblockheight.7.md index ef57b6c1d101..f773a5178365 100644 --- a/doc/lightning-waitblockheight.7.md +++ b/doc/lightning-waitblockheight.7.md @@ -39,4 +39,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7db14bcda8226b184a2cdccb41e0db717d257683bb69b684a61e065af7ab076d) +[comment]: # ( SHA256STAMP:4419a83c7852353e07eaa8ac3e6786c6b1d714a9a3d981fc78adfe4a73008514) diff --git a/doc/lightning-waitinvoice.7.md b/doc/lightning-waitinvoice.7.md index 6ec5f86eb31e..94207e966d5d 100644 --- a/doc/lightning-waitinvoice.7.md +++ b/doc/lightning-waitinvoice.7.md @@ -20,19 +20,19 @@ On success, an object is returned, containing: - **label** (string): unique label supplied at invoice creation - **description** (string): description used in the invoice -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): Whether it's paid or expired (one of "paid", "expired") -- **expires_at** (u64): UNIX timestamp of when it will become / became unpayable -- **amount_msat** (msat, optional): the amount required to pay this invoice +- **expires\_at** (u64): UNIX timestamp of when it will become / became unpayable +- **amount\_msat** (msat, optional): the amount required to pay this invoice - **bolt11** (string, optional): the BOLT11 string (always present unless *bolt12* is) - **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is) If **status** is "paid": - - **pay_index** (u64): Unique incrementing index for this payment - - **amount_received_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) - - **paid_at** (u64): UNIX timestamp of when it was paid - - **payment_preimage** (secret): proof of payment (always 64 characters) + - **pay\_index** (u64): Unique incrementing index for this payment + - **amount\_received\_msat** (msat): the amount actually received (could be slightly greater than *amount_msat*, since clients may overpay) + - **paid\_at** (u64): UNIX timestamp of when it was paid + - **payment\_preimage** (secret): proof of payment (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ce034f1801260480032a98348aae928055f3d9edf12f45cefcd8b6007289ff83) +[comment]: # ( SHA256STAMP:016afebade3ee5a7ac5abbd125f8db78f6c8b41f0e510c8f4c3b6a385e6f3a26) diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index d3d07631cb84..0cd670e3b455 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -36,12 +36,12 @@ RETURN VALUE On success, an object is returned, containing: - **id** (u64): unique ID for this payment attempt -- **payment_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment\_hash** (hash): the hash of the *payment_preimage* which will prove payment (always 64 characters) - **status** (string): status of the payment (always "complete") -- **created_at** (u64): the UNIX timestamp showing when this payment was initiated -- **amount_sent_msat** (msat): The amount sent +- **created\_at** (u64): the UNIX timestamp showing when this payment was initiated +- **amount\_sent\_msat** (msat): The amount sent - **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash -- **amount_msat** (msat, optional): The amount delivered to destination (if known) +- **amount\_msat** (msat, optional): The amount delivered to destination (if known) - **destination** (pubkey, optional): the final destination of the payment if known - **label** (string, optional): the label, if given to sendpay - **partid** (u64, optional): the *partid*, if given to sendpay @@ -50,7 +50,7 @@ On success, an object is returned, containing: If **status** is "complete": - - **payment_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) + - **payment\_preimage** (secret): the proof of payment: SHA256 of this **payment_hash** (always 64 characters) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -103,4 +103,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:470c164c81adea9053cf276b92c3faf2d0fc4937c747a4c2d06524c601afd1c3) +[comment]: # ( SHA256STAMP:30601f9dab9b0168e7db387cdd9d6750116f9adad8f125f0bac507a79f9bcf67) diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index 0e95f1368329..74301e97111c 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -74,4 +74,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:2d79ddd7910ea1f5b3a1a0a0d9494229fa600c9a1669ce0904a364f550d847dd) +[comment]: # ( SHA256STAMP:bbb1e5637721b3415ff3498d141f9da7d38bd75b297f5c0d262e838314dd2f37) diff --git a/tools/fromschema.py b/tools/fromschema.py index ce818a3b1b62..b5cbc91000b3 100755 --- a/tools/fromschema.py +++ b/tools/fromschema.py @@ -69,11 +69,16 @@ def output_range(properties): output(' (one of {})'.format(', '.join([json_value(p) for p in properties['enum']]))) +def fmt_propname(propname): + """Pretty-print format a property name""" + return '**{}**'.format(propname.replace('_', '\\_')) + + def output_member(propname, properties, is_optional, indent, print_type=True, prefix=None): """Generate description line(s) for this member""" if prefix is None: - prefix = '- **{}**'.format(propname) + prefix = '- ' + fmt_propname(propname) output(indent + prefix) # We make them explicitly note if they don't want a type! @@ -179,7 +184,7 @@ def output_members(sub, indent=''): # "required" are fields that simply must be present for r in ifclause['if'].get('required', []): - conditions.append('**{}** is present'.format(r)) + conditions.append(fmt_propname(r) + ' is present') # "properties" are enums of field values for tag, vals in ifclause['if'].get('properties', {}).items(): @@ -187,7 +192,7 @@ def output_members(sub, indent=''): assert 'description' not in vals whichvalues = vals['enum'] - cond = "**{}** is".format(tag) + cond = fmt_propname(tag) + " is" if len(whichvalues) == 1: cond += " {}".format(json_value(whichvalues[0])) else: @@ -229,12 +234,12 @@ def generate_from_schema(schema): 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\n'.format(toplevels[0])) + output('On success, an object containing {} is returned. It is an object containing:\n\n'.format(fmt_propname(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\n'.format(toplevels[0])) + output('On success, an object containing {} is returned. It is an array of objects, where each object contains:\n\n'.format(fmt_propname(toplevels[0]))) # Don't have a description field here, it's not used. assert 'description' not in toplevels[0] sub = props[toplevels[0]]['items'] @@ -247,7 +252,7 @@ def generate_from_schema(schema): if warnings: outputs(['\n', 'The following warnings may also be returned:\n\n']) for w, desc in warnings: - output("- **{}**: {}\n".format(w, desc)) + output("- {}: {}\n".format(fmt_propname(w), desc)) # GH markdown rendering gets upset if there isn't a blank line # between a list and the end comment. From 5112329a6b4692974770aaed0e098444f348e599 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 10:24:57 +0930 Subject: [PATCH 1299/1530] external/lowdown: local import of lowdown source. Signed-off-by: Rusty Russell --- .gitmodules | 3 +++ external/lowdown | 1 + 2 files changed, 4 insertions(+) create mode 160000 external/lowdown diff --git a/.gitmodules b/.gitmodules index 095547408073..8b8aa0660d5c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "external/lnprototest"] path = external/lnprototest url = https://github.com/rustyrussell/lnprototest.git +[submodule "external/lowdown"] + path = external/lowdown + url = https://github.com/kristapsdz/lowdown.git diff --git a/external/lowdown b/external/lowdown new file mode 160000 index 000000000000..edca6ce6d533 --- /dev/null +++ b/external/lowdown @@ -0,0 +1 @@ +Subproject commit edca6ce6d5336efb147321a43c47a698de41bb7c From 50d1043a91db4343eb3a445e6f0611afc19c4032 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 13:40:32 +0930 Subject: [PATCH 1300/1530] external: build lowdown if not already found. Signed-off-by: Rusty Russell --- .gitmodules | 1 + configure | 9 ++++++++- doc/Makefile | 13 ++++++++++++- external/Makefile | 14 ++++++++++++++ tools/md2man.sh | 9 +++++---- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/.gitmodules b/.gitmodules index 8b8aa0660d5c..6dae35a54444 100644 --- a/.gitmodules +++ b/.gitmodules @@ -20,3 +20,4 @@ [submodule "external/lowdown"] path = external/lowdown url = https://github.com/kristapsdz/lowdown.git + ignore = dirty diff --git a/configure b/configure index bd7aae297f0d..a943baedfc87 100755 --- a/configure +++ b/configure @@ -105,7 +105,7 @@ check_command() shift 1 echo -n "checking for $name... " - if "$@" >/dev/null 2>&1; then + if "$@" >/dev/null 2>&1 $@.tmp && grep -v SHA256STAMP: $@.tmp > $@ && rm -f $@.tmp && $(call SHA256STAMP,[comment]: # $(LBRACKET),$(RBRACKET))); else touch $@; fi +# If we need to build lowdown, make tools/md2man.sh depend on it. +# That way it's not used in SHA256STAMP (which only uses direct +# dependencies), but make will be forced to build it. +ifeq ($(HAVE_LOWDOWN),0) +LOWDOWN := $(TARGET_DIR)/lowdown-build/bin/lowdown +tools/md2man.sh: $(LOWDOWN) + touch $@ +else +LOWDOWN := lowdown +endif + $(MANPAGES): doc/%: doc/%.md tools/md2man.sh version_gen.h - @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "md2man $<", VERSION=$(VERSION) tools/md2man.sh $< > $@ && $(call SHA256STAMP,\\\",)); else touch $@; fi + @if $(call SHA256STAMP_CHANGED); then $(call VERBOSE, "md2man $<", VERSION=$(VERSION) tools/md2man.sh $(LOWDOWN) $< > $@ && $(call SHA256STAMP,\\\",)); else touch $@; fi $(MANPAGES): $(FORCE) $(MARKDOWN_WITH_SCHEMA): $(FORCE) diff --git a/external/Makefile b/external/Makefile index 92e976092a09..60b8068fe264 100644 --- a/external/Makefile +++ b/external/Makefile @@ -6,6 +6,10 @@ SUBMODULES = \ external/libbacktrace \ external/lnprototest +ifeq ($(HAVE_LOWDOWN),0) +SUBMODULES += external/lowdown +endif + TOP := ../.. ifdef BUILD CROSSCOMPILE_OPTS := --host="$(MAKE_HOST)" --build="$(BUILD)" @@ -29,6 +33,10 @@ LIBBACKTRACE_HEADERS := external/libbacktrace/backtrace.h EXTERNAL_HEADERS := $(LIBSODIUM_HEADERS) $(LIBWALLY_HEADERS) $(LIBSECP_HEADERS) $(JSMN_HEADERS) $(GHEAP_HEADERS) $(LIBBACKTRACE_HEADERS) EXTERNAL_LIBS := ${TARGET_DIR}/libwallycore.a ${TARGET_DIR}/libsecp256k1.a ${TARGET_DIR}/libjsmn.a ${TARGET_DIR}/libbacktrace.a +ifeq ($(HAVE_LOWDOWN),0) +EXTERNAL_HEADERS += external/lowdown/lowdown.h +endif + EXTERNAL_INCLUDE_FLAGS := \ -I external/libwally-core/include/ \ -I external/libwally-core/src/secp256k1/include/ \ @@ -106,6 +114,10 @@ $(TARGET_DIR)/libbacktrace.a: external/libbacktrace/backtrace.h cd $(TARGET_DIR)/libbacktrace-build && $(TOP)/libbacktrace/configure CC="$(CC)" --enable-static=yes $(CROSSCOMPILE_OPTS) --enable-shared=no --prefix=/ --libdir=/ && $(MAKE) $(MAKE) -C $(TARGET_DIR)/libbacktrace-build DESTDIR=$$(pwd)/$(TARGET_DIR) install-exec +$(TARGET_DIR)/lowdown-build/bin/lowdown: external/lowdown/lowdown.h + cd external/lowdown && CC="$(CC)" ./configure PREFIX=`pwd`/$(TOP)/$(TARGET_DIR)/lowdown-build/ + $(MAKE) -C external/lowdown install + distclean: external-distclean clean: external-clean @@ -116,8 +128,10 @@ external-clean: if [ -f ${TARGET_DIR}/libwally-core-build/Makefile ]; then make -C ${TARGET_DIR}/libwally-core-build clean; fi if [ -f ${TARGET_DIR}/libwally-core-build/src/Makefile ]; then make -C ${TARGET_DIR}/libwally-core-build/src clean; fi if [ -f ${TARGET_DIR}/libbacktrace-build/Makefile ]; then make -C ${TARGET_DIR}/libbacktrace-build clean; fi + [ -f external/lowdown/Makefile.configure ] && $(MAKE) -C external/lowdown clean external-distclean: make -C external/libsodium distclean || true + [ -f external/lowdown/Makefile.configure ] && $(MAKE) -C external/lowdown distclean $(RM) -rf ${TARGET_DIR}/libbacktrace-build ${TARGET_DIR}/libsodium-build ${TARGET_DIR}/libwally-core-build ${TARGET_DIR}/jsmn-build $(RM) -r `git status --ignored --porcelain external/libwally-core | grep '^!! ' | cut -c3-` diff --git a/tools/md2man.sh b/tools/md2man.sh index 7dea4cb70b1f..5052459eb46b 100755 --- a/tools/md2man.sh +++ b/tools/md2man.sh @@ -1,10 +1,11 @@ #! /bin/sh -if [ $# != 1 ]; then - echo "Usage: $0 " >&2 +if [ $# != 2 ]; then + echo "Usage: $0 " >&2 exit 1 fi -SOURCE=$1 +LOWDOWN="$1" +SOURCE="$2" SECTION="$(basename "$SOURCE" .md | cut -d. -f2-)" TITLE="$(basename "$(basename "$SOURCE" .md)" ."$SECTION" | tr '[:lower:]' '[:upper:]')" @@ -12,4 +13,4 @@ TITLE="$(basename "$(basename "$SOURCE" .md)" ."$SECTION" | tr '[:lower:]' '[:up # format. mrkd used to do this for us, lowdown(1) doesn't. TITLELINE="$(head -n1 "$SOURCE")" -(echo "NAME"; echo "----"; echo "$TITLELINE"; tail -n +3 "$SOURCE") | lowdown -s --out-no-smarty -Tman -m "title:$TITLE" -m "section:$SECTION" -m "source:Core Lightning $VERSION" -m "shiftheadinglevelby:-1" +(echo "NAME"; echo "----"; echo "$TITLELINE"; tail -n +3 "$SOURCE") | $LOWDOWN -s --out-no-smarty -Tman -m "title:$TITLE" -m "section:$SECTION" -m "source:Core Lightning $VERSION" -m "shiftheadinglevelby:-1" From cde93ab703b43189fe2559f3867bd524c9ae8c28 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 6 Sep 2022 13:46:18 +0930 Subject: [PATCH 1301/1530] doc: document that we can build lowdown, remove from Alpine. Signed-off-by: Rusty Russell --- contrib/docker/Dockerfile.alpine | 2 +- doc/INSTALL.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/docker/Dockerfile.alpine b/contrib/docker/Dockerfile.alpine index ceb873823eae..62261ef3d3d4 100644 --- a/contrib/docker/Dockerfile.alpine +++ b/contrib/docker/Dockerfile.alpine @@ -6,7 +6,7 @@ WORKDIR /build RUN apk update && \ apk add ca-certificates alpine-sdk autoconf automake git libtool \ gmp-dev sqlite-dev python3 py3-mako net-tools zlib-dev libsodium gettext su-exec \ - python3 py3-pip lowdown #&& \ + python3 py3-pip #&& \ #apk add --upgrade fortify-headers RUN mkdir lightning diff --git a/doc/INSTALL.md b/doc/INSTALL.md index f53017d5ecc9..53bec336bb80 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -66,6 +66,8 @@ For development or running tests, get additional dependencies: sudo apt-get install -y valgrind libpq-dev shellcheck cppcheck \ libsecp256k1-dev jq lowdown +If you can't install `lowdown`, a version will be built in-tree. + If you want to build the Rust plugins (currently, cln-grpc): sudo apt-get install -y cargo rustfmt From 9de458b23b5b761cb98307f62c5fa8fceea1e55c Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Tue, 30 Aug 2022 17:20:49 -0500 Subject: [PATCH 1302/1530] docs: Clear up Ubutu documentation People who copy paste the commands might not realize this chunk is not copy paste-able. --- doc/INSTALL.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 53bec336bb80..4f8169433a99 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -88,6 +88,9 @@ To build core lightning for development purpose you can use the following comman pip3 install poetry poetry shell + +This will put you in a new shell to enter the following commands: + poetry install ./configure --enable-developer make From 34a0d7083acf66ca69ff3e9e66e73ff86b4de22c Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 9 Sep 2022 18:32:41 -0500 Subject: [PATCH 1303/1530] build: use ubuntu 22.04 LTS Requires us to update to latest lnproto which is now using the most up to date python-bitcoinlib, as well as updating our python lock files (which pin the grpcio deps, because of locking problems h/t @cdecker) --- .github/workflows/ci.yaml | 2 +- .gitmodules | 3 +- contrib/docker/Dockerfile.ubuntu | 2 +- poetry.lock | 655 +++++-------------------------- pyproject.toml | 3 +- 5 files changed, 95 insertions(+), 570 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 60f1dd523a28..f4a3efb8a088 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -99,7 +99,7 @@ jobs: proto-test: name: Protocol Test Config - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 timeout-minutes: 300 needs: [smoke-test] strategy: diff --git a/.gitmodules b/.gitmodules index 6dae35a54444..100691cdbde8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,7 +16,8 @@ url = https://github.com/valyala/gheap [submodule "external/lnprototest"] path = external/lnprototest - url = https://github.com/rustyrussell/lnprototest.git + url = https://github.com/niftynei/lnprototest.git + branch = nifty/ripemd160-fallback [submodule "external/lowdown"] path = external/lowdown url = https://github.com/kristapsdz/lowdown.git diff --git a/contrib/docker/Dockerfile.ubuntu b/contrib/docker/Dockerfile.ubuntu index 93ffee4b662a..56aa674fe902 100644 --- a/contrib/docker/Dockerfile.ubuntu +++ b/contrib/docker/Dockerfile.ubuntu @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 LABEL mantainer="Vincenzo Palazzo vincenzopalazzodev@gmail.com" WORKDIR /work diff --git a/poetry.lock b/poetry.lock index a519fd01e0c9..b655ffa02431 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,27 +6,19 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "atomicwrites" -version = "1.4.1" -description = "Atomic file writes." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - [[package]] name = "attrs" -version = "21.4.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] [[package]] name = "base58" @@ -37,7 +29,7 @@ optional = false python-versions = ">=3.5" [package.extras] -tests = ["mypy", "PyHamcrest (>=2.0.2)", "pytest (>=4.6)", "pytest-benchmark", "pytest-cov", "pytest-flake8"] +tests = ["pytest-flake8", "pytest-cov", "pytest-benchmark", "pytest (>=4.6)", "PyHamcrest (>=2.0.2)", "mypy"] [[package]] name = "bitstring" @@ -168,7 +160,7 @@ pyflakes = ">=2.4.0,<2.5.0" [[package]] name = "flask" -version = "2.1.2" +version = "2.2.2" description = "A simple framework for building complex web applications." category = "dev" optional = false @@ -179,7 +171,7 @@ click = ">=8.0" importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} itsdangerous = ">=2.0" Jinja2 = ">=3.0" -Werkzeug = ">=2.0" +Werkzeug = ">=2.2.2" [package.extras] async = ["asgiref (>=3.2)"] @@ -229,7 +221,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [[package]] name = "importlib-resources" -version = "5.8.0" +version = "5.9.0" description = "Read resources from Python packages" category = "dev" optional = false @@ -239,8 +231,8 @@ python-versions = ">=3.7" zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [[package]] name = "iniconfig" @@ -260,7 +252,7 @@ python-versions = ">=3.7" [[package]] name = "jaraco.functools" -version = "3.5.0" +version = "3.5.1" description = "Functools like those found in stdlib" category = "dev" optional = false @@ -270,14 +262,14 @@ python-versions = ">=3.7" more-itertools = "*" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.classes", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.classes", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [[package]] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" +category = "dev" optional = false python-versions = ">=3.7" @@ -289,7 +281,7 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.7.2" +version = "4.16.0" description = "An implementation of JSON Schema validation for Python" category = "dev" optional = false @@ -299,6 +291,7 @@ python-versions = ">=3.7" attrs = ">=17.4.0" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" typing-extensions = {version = "*", markers = "python_version < \"3.8\""} @@ -308,7 +301,7 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "mako" -version = "1.2.1" +version = "1.2.2" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." category = "main" optional = false @@ -339,42 +332,14 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "mistune" -version = "0.8.4" -description = "The fastest markdown parser in pure Python" -category = "main" -optional = false -python-versions = "*" - [[package]] name = "more-itertools" -version = "8.13.0" +version = "8.14.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false python-versions = ">=3.5" -[[package]] -name = "mrkd" -version = "0.2.0" -description = "" -category = "main" -optional = false -python-versions = "*" -develop = false - -[package.dependencies] -Jinja2 = "*" -mistune = "0.8.4" -pygments = "*" - -[package.source] -type = "git" -url = "https://github.com/refi64/mrkd.git" -reference = "781f05eb9898ca652f18eed29b3c956389e6a2a7" -resolved_reference = "781f05eb9898ca652f18eed29b3c956389e6a2a7" - [[package]] name = "mypy" version = "0.931" @@ -412,6 +377,14 @@ python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +description = "Resolve a name to an object." +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "pluggy" version = "1.0.0" @@ -424,8 +397,8 @@ python-versions = ">=3.6" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["pytest-benchmark", "pytest"] +dev = ["tox", "pre-commit"] [[package]] name = "protobuf" @@ -437,7 +410,7 @@ python-versions = ">=3.7" [[package]] name = "psutil" -version = "5.9.1" +version = "5.9.2" description = "Cross-platform lib for process and system monitoring in Python." category = "dev" optional = false @@ -486,17 +459,9 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -[[package]] -name = "pygments" -version = "2.12.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "main" -optional = false -python-versions = ">=3.6" - [[package]] name = "pyln-bolt7" -version = "1.0.186.post0" +version = "1.0.246" description = "BOLT7" category = "main" optional = false @@ -592,14 +557,13 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pytest" -version = "7.1.2" +version = "7.1.3" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} @@ -677,7 +641,7 @@ testing = ["filelock"] [[package]] name = "python-bitcoinlib" -version = "0.11.0" +version = "0.11.2" description = "The Swiss Army Knife of the Bitcoin protocol." category = "dev" optional = false @@ -717,25 +681,28 @@ python-versions = ">=3.7" [[package]] name = "websocket-client" -version = "1.3.3" +version = "1.4.1" description = "WebSocket client for Python with low level API options" category = "main" optional = false python-versions = ">=3.7" [package.extras] -docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] -optional = ["python-socks", "wsaccel"] test = ["websockets"] +optional = ["wsaccel", "python-socks"] +docs = ["sphinx-rtd-theme (>=0.5)", "Sphinx (>=3.4)"] [[package]] name = "werkzeug" -version = "2.1.2" +version = "2.2.2" description = "The comprehensive WSGI web application library." category = "dev" optional = false python-versions = ">=3.7" +[package.dependencies] +MarkupSafe = ">=2.1.1" + [package.extras] watchdog = ["watchdog"] @@ -754,511 +721,67 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "6c86f252c45a49ebb8dae1df12623ae1ff80040e7434a8050729df72503502bc" +content-hash = "f4e15a93f81fb31c14bbbe889b7d4e42927de94e88fbcef4730594d65fa5a4e3" [metadata.files] -asn1crypto = [ - {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"}, - {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"}, -] -atomicwrites = [] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] -base58 = [ - {file = "base58-2.1.1-py3-none-any.whl", hash = "sha256:11a36f4d3ce51dfc1043f3218591ac4eb1ceb172919cebe05b52a5bcc8d245c2"}, - {file = "base58-2.1.1.tar.gz", hash = "sha256:c5d0cb3f5b6e81e8e35da5754388ddcc6d0d14b6c6a132cb93d69ed580a7278c"}, -] -bitstring = [ - {file = "bitstring-3.1.9-py2-none-any.whl", hash = "sha256:e3e340e58900a948787a05e8c08772f1ccbe133f6f41fe3f0fa19a18a22bbf4f"}, - {file = "bitstring-3.1.9-py3-none-any.whl", hash = "sha256:0de167daa6a00c9386255a7cac931b45e6e24e0ad7ea64f1f92a64ac23ad4578"}, - {file = "bitstring-3.1.9.tar.gz", hash = "sha256:a5848a3f63111785224dca8bb4c0a75b62ecdef56a042c8d6be74b16f7e860e7"}, -] -cffi = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, -] -cheroot = [ - {file = "cheroot-8.6.0-py2.py3-none-any.whl", hash = "sha256:62cbced16f07e8aaf512673987cd6b1fc5ad00073345e9ed6c4e2a5cc2a3a22d"}, - {file = "cheroot-8.6.0.tar.gz", hash = "sha256:366adf6e7cac9555486c2d1be6297993022eff6f8c4655c1443268cca3f08e25"}, -] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -coincurve = [ - {file = "coincurve-17.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac8c87d6fd080faa74e7ecf64a6ed20c11a254863238759eb02c3f13ad12b0c4"}, - {file = "coincurve-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25dfa105beba24c8de886f8ed654bb1133866e4e22cfd7ea5ad8438cae6ed924"}, - {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:698efdd53e4fe1bbebaee9b75cbc851be617974c1c60098e9145cb7198ae97fb"}, - {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30dd44d1039f1d237aaa2da6d14a455ca88df3bcb00610b41f3253fdca1be97b"}, - {file = "coincurve-17.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d154e2eb5711db8c5ef52fcd80935b5a0e751c057bc6ffb215a7bb409aedef03"}, - {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c71caffb97dd3d0c243beb62352669b1e5dafa3a4bccdbb27d36bd82f5e65d20"}, - {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:747215254e51dd4dfbe6dded9235491263da5d88fe372d66541ca16b51ea078f"}, - {file = "coincurve-17.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad2f6df39ba1e2b7b14bb984505ffa7d0a0ecdd697e8d7dbd19e04bc245c87ed"}, - {file = "coincurve-17.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0503326963916c85b61d16f611ea0545f03c9e418fa8007c233c815429e381e8"}, - {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1013c1597b65684ae1c3e42497f9ef5a04527fa6136a84a16b34602606428c74"}, - {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4beef321fd6434448aab03a0c245f31c4e77f43b54b82108c0948d29852ac7e"}, - {file = "coincurve-17.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f47806527d3184da3e8b146fac92a8ed567bbd225194f4517943d8cdc85f9542"}, - {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:51e56373ac79f4ec1cfc5da53d72c55f5e5ac28d848b0849ef5e687ace857888"}, - {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d694ad194bee9e8792e2e75879dc5238d8a184010cde36c5ad518fcfe2cd8f2"}, - {file = "coincurve-17.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:74cedb3d3a1dc5abe0c9c2396e1b82cc64496babc5b42e007e72e185cb1edad8"}, - {file = "coincurve-17.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:db874c5c1dcb1f3a19379773b5e8cffc777625a7a7a60dd9a67206e31e62e2e9"}, - {file = "coincurve-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:896b01941254f0a218cf331a9bddfe2d43892f7f1ba10d6e372e2eb744a744c2"}, - {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6aec70238dbe7a5d66b5f9438ff45b08eb5e0990d49c32ebb65247c5d5b89d7a"}, - {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d24284d17162569df917a640f19d9654ba3b43cf560ced8864f270da903f73a5"}, - {file = "coincurve-17.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ea057f777842396d387103c606babeb3a1b4c6126769cc0a12044312fc6c465"}, - {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b88642edf7f281649b0c0b6ffade051945ccceae4b885e40445634877d0b3049"}, - {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a80a207131813b038351c5bdae8f20f5f774bbf53622081f208d040dd2b7528f"}, - {file = "coincurve-17.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f1ef72574aa423bc33665ef4be859164a478bad24d48442da874ef3dc39a474d"}, - {file = "coincurve-17.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfd4fab857bcd975edc39111cb5f5c104f138dac2e9ace35ea8434d37bcea3be"}, - {file = "coincurve-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:73f39579dd651a9fc29da5a8fc0d8153d872bcbc166f876457baced1a1c01501"}, - {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8852dc01af4f0fe941ffd04069f7e4fecdce9b867a016f823a02286a1a1f07b5"}, - {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1bef812da1da202cdd601a256825abcf26d86e8634fac3ec3e615e3bb3ff08c"}, - {file = "coincurve-17.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abbefc9ccb170cb255a31df32457c2e43084b9f37589d0694dacc2dea6ddaf7c"}, - {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:abbd9d017a7638dc38a3b9bb4851f8801b7818d4e5ac22e0c75e373b3c1dbff0"}, - {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e2c2e8a1f0b1f8e48049c891af4ae3cad65d115d358bde72f6b8abdbb8a23170"}, - {file = "coincurve-17.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8c571445b166c714af4f8155e38a894376c16c0431e88963f2fff474a9985d87"}, - {file = "coincurve-17.0.0-py3-none-win32.whl", hash = "sha256:b956b0b2c85e25a7d00099970ff5d8338254b45e46f0a940f4a2379438ce0dde"}, - {file = "coincurve-17.0.0-py3-none-win_amd64.whl", hash = "sha256:630388080da3026e0b0176cc6762eaabecba857ee3fc85767577dea063ea7c6e"}, - {file = "coincurve-17.0.0.tar.gz", hash = "sha256:68da55aff898702952fda3ee04fd6ed60bb6b91f919c69270786ed766b548b93"}, -] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] +asn1crypto = [] +attrs = [] +base58 = [] +bitstring = [] +cffi = [] +cheroot = [] +click = [] +coincurve = [] +colorama = [] crc32c = [] -cryptography = [ - {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"}, - {file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"}, - {file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"}, - {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"}, - {file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"}, - {file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"}, - {file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"}, - {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"}, - {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"}, - {file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"}, - {file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"}, - {file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"}, -] -ephemeral-port-reserve = [ - {file = "ephemeral_port_reserve-1.1.4-py2.py3-none-any.whl", hash = "sha256:dae8da99422c643bb52478ed55d5a8428099092391656ba3726ff30c801600c8"}, - {file = "ephemeral_port_reserve-1.1.4.tar.gz", hash = "sha256:b8f7da2c97090cb0801949dec1d6d40c97220505b742a70935ffbd43234c14b2"}, -] -execnet = [ - {file = "execnet-1.9.0-py2.py3-none-any.whl", hash = "sha256:a295f7cc774947aac58dde7fdc85f4aa00c42adf5d8f5468fc630c1acf30a142"}, - {file = "execnet-1.9.0.tar.gz", hash = "sha256:8f694f3ba9cc92cab508b152dcfe322153975c29bda272e2fd7f3f00f36e47c5"}, -] -flake8 = [ - {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, - {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, -] -flask = [ - {file = "Flask-2.1.2-py3-none-any.whl", hash = "sha256:fad5b446feb0d6db6aec0c3184d16a8c1f6c3e464b511649c8918a9be100b4fe"}, - {file = "Flask-2.1.2.tar.gz", hash = "sha256:315ded2ddf8a6281567edb27393010fe3406188bafbfe65a3339d5787d89e477"}, -] +cryptography = [] +ephemeral-port-reserve = [] +execnet = [] +flake8 = [] +flask = [] grpcio = [] grpcio-tools = [] importlib-metadata = [] -importlib-resources = [ - {file = "importlib_resources-5.8.0-py3-none-any.whl", hash = "sha256:7952325ffd516c05a8ad0858c74dff2c3343f136fe66a6002b2623dd1d43f223"}, - {file = "importlib_resources-5.8.0.tar.gz", hash = "sha256:568c9f16cb204f9decc8d6d24a572eeea27dacbb4cee9e6b03a8025736769751"}, -] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -itsdangerous = [ - {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, - {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, -] -"jaraco.functools" = [ - {file = "jaraco.functools-3.5.0-py3-none-any.whl", hash = "sha256:141f95c490a18eb8aab86caf7a2728f02f604988a26dc36652e3d9fa9e4c49fa"}, - {file = "jaraco.functools-3.5.0.tar.gz", hash = "sha256:31e0e93d1027592b7b0bec6ad468db850338981ebee76ba5e212e235f4c7dda0"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] +importlib-resources = [] +iniconfig = [] +itsdangerous = [] +"jaraco.functools" = [] +jinja2 = [] jsonschema = [] mako = [] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mistune = [ - {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, - {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, -] -more-itertools = [ - {file = "more-itertools-8.13.0.tar.gz", hash = "sha256:a42901a0a5b169d925f6f217cd5a190e32ef54360905b9c39ee7db5313bfec0f"}, - {file = "more_itertools-8.13.0-py3-none-any.whl", hash = "sha256:c5122bffc5f104d37c1626b8615b511f3427aa5389b94d61e5ef8236bfbc3ddb"}, -] -mrkd = [] -mypy = [ - {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, - {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, - {file = "mypy-0.931-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714"}, - {file = "mypy-0.931-cp310-cp310-win_amd64.whl", hash = "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc"}, - {file = "mypy-0.931-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d"}, - {file = "mypy-0.931-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d"}, - {file = "mypy-0.931-cp36-cp36m-win_amd64.whl", hash = "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c"}, - {file = "mypy-0.931-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0"}, - {file = "mypy-0.931-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05"}, - {file = "mypy-0.931-cp37-cp37m-win_amd64.whl", hash = "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7"}, - {file = "mypy-0.931-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0"}, - {file = "mypy-0.931-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069"}, - {file = "mypy-0.931-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799"}, - {file = "mypy-0.931-cp38-cp38-win_amd64.whl", hash = "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a"}, - {file = "mypy-0.931-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166"}, - {file = "mypy-0.931-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266"}, - {file = "mypy-0.931-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd"}, - {file = "mypy-0.931-cp39-cp39-win_amd64.whl", hash = "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697"}, - {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, - {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -protobuf = [ - {file = "protobuf-3.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996"}, - {file = "protobuf-3.20.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"}, - {file = "protobuf-3.20.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde"}, - {file = "protobuf-3.20.1-cp310-cp310-win32.whl", hash = "sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c"}, - {file = "protobuf-3.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7"}, - {file = "protobuf-3.20.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153"}, - {file = "protobuf-3.20.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f"}, - {file = "protobuf-3.20.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20"}, - {file = "protobuf-3.20.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531"}, - {file = "protobuf-3.20.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e"}, - {file = "protobuf-3.20.1-cp37-cp37m-win32.whl", hash = "sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c"}, - {file = "protobuf-3.20.1-cp37-cp37m-win_amd64.whl", hash = "sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067"}, - {file = "protobuf-3.20.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf"}, - {file = "protobuf-3.20.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab"}, - {file = "protobuf-3.20.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c"}, - {file = "protobuf-3.20.1-cp38-cp38-win32.whl", hash = "sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7"}, - {file = "protobuf-3.20.1-cp38-cp38-win_amd64.whl", hash = "sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739"}, - {file = "protobuf-3.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7"}, - {file = "protobuf-3.20.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f"}, - {file = "protobuf-3.20.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9"}, - {file = "protobuf-3.20.1-cp39-cp39-win32.whl", hash = "sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8"}, - {file = "protobuf-3.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91"}, - {file = "protobuf-3.20.1-py2.py3-none-any.whl", hash = "sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388"}, - {file = "protobuf-3.20.1.tar.gz", hash = "sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9"}, -] -psutil = [ - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87"}, - {file = "psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af"}, - {file = "psutil-5.9.1-cp27-cp27m-win32.whl", hash = "sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc"}, - {file = "psutil-5.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0"}, - {file = "psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22"}, - {file = "psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8"}, - {file = "psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de"}, - {file = "psutil-5.9.1-cp310-cp310-win32.whl", hash = "sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329"}, - {file = "psutil-5.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021"}, - {file = "psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453"}, - {file = "psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685"}, - {file = "psutil-5.9.1-cp36-cp36m-win32.whl", hash = "sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36"}, - {file = "psutil-5.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d"}, - {file = "psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"}, - {file = "psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4"}, - {file = "psutil-5.9.1-cp37-cp37m-win32.whl", hash = "sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b"}, - {file = "psutil-5.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680"}, - {file = "psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4"}, - {file = "psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b"}, - {file = "psutil-5.9.1-cp38-cp38-win32.whl", hash = "sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689"}, - {file = "psutil-5.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0"}, - {file = "psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e"}, - {file = "psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537"}, - {file = "psutil-5.9.1-cp39-cp39-win32.whl", hash = "sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574"}, - {file = "psutil-5.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5"}, - {file = "psutil-5.9.1.tar.gz", hash = "sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954"}, -] -psycopg2-binary = [ - {file = "psycopg2-binary-2.9.3.tar.gz", hash = "sha256:761df5313dc15da1502b21453642d7599d26be88bff659382f8f9747c7ebea4e"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:539b28661b71da7c0e428692438efbcd048ca21ea81af618d845e06ebfd29478"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e82d38390a03da28c7985b394ec3f56873174e2c88130e6966cb1c946508e65"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57804fc02ca3ce0dbfbef35c4b3a4a774da66d66ea20f4bda601294ad2ea6092"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_aarch64.whl", hash = "sha256:083a55275f09a62b8ca4902dd11f4b33075b743cf0d360419e2051a8a5d5ff76"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-manylinux_2_24_ppc64le.whl", hash = "sha256:0a29729145aaaf1ad8bafe663131890e2111f13416b60e460dae0a96af5905c9"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a79d622f5206d695d7824cbf609a4f5b88ea6d6dab5f7c147fc6d333a8787e4"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:090f3348c0ab2cceb6dfbe6bf721ef61262ddf518cd6cc6ecc7d334996d64efa"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a9e1f75f96ea388fbcef36c70640c4efbe4650658f3d6a2967b4cc70e907352e"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c3ae8e75eb7160851e59adc77b3a19a976e50622e44fd4fd47b8b18208189d42"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-win32.whl", hash = "sha256:7b1e9b80afca7b7a386ef087db614faebbf8839b7f4db5eb107d0f1a53225029"}, - {file = "psycopg2_binary-2.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:8b344adbb9a862de0c635f4f0425b7958bf5a4b927c8594e6e8d261775796d53"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:e847774f8ffd5b398a75bc1c18fbb56564cda3d629fe68fd81971fece2d3c67e"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68641a34023d306be959101b345732360fc2ea4938982309b786f7be1b43a4a1"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3303f8807f342641851578ee7ed1f3efc9802d00a6f83c101d21c608cb864460"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:e3699852e22aa68c10de06524a3721ade969abf382da95884e6a10ff798f9281"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-manylinux_2_24_ppc64le.whl", hash = "sha256:526ea0378246d9b080148f2d6681229f4b5964543c170dd10bf4faaab6e0d27f"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:b1c8068513f5b158cf7e29c43a77eb34b407db29aca749d3eb9293ee0d3103ca"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:15803fa813ea05bef089fa78835118b5434204f3a17cb9f1e5dbfd0b9deea5af"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:152f09f57417b831418304c7f30d727dc83a12761627bb826951692cc6491e57"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:404224e5fef3b193f892abdbf8961ce20e0b6642886cfe1fe1923f41aaa75c9d"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-win32.whl", hash = "sha256:1f6b813106a3abdf7b03640d36e24669234120c72e91d5cbaeb87c5f7c36c65b"}, - {file = "psycopg2_binary-2.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:2d872e3c9d5d075a2e104540965a1cf898b52274a5923936e5bfddb58c59c7c2"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:10bb90fb4d523a2aa67773d4ff2b833ec00857f5912bafcfd5f5414e45280fb1"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a52ecab70af13e899f7847b3e074eeb16ebac5615665db33bce8a1009cf33"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a29b3ca4ec9defec6d42bf5feb36bb5817ba3c0230dd83b4edf4bf02684cd0ae"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:12b11322ea00ad8db8c46f18b7dfc47ae215e4df55b46c67a94b4effbaec7094"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-manylinux_2_24_ppc64le.whl", hash = "sha256:53293533fcbb94c202b7c800a12c873cfe24599656b341f56e71dd2b557be063"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c381bda330ddf2fccbafab789d83ebc6c53db126e4383e73794c74eedce855ef"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9d29409b625a143649d03d0fd7b57e4b92e0ecad9726ba682244b73be91d2fdb"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:183a517a3a63503f70f808b58bfbf962f23d73b6dccddae5aa56152ef2bcb232"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:15c4e4cfa45f5a60599d9cec5f46cd7b1b29d86a6390ec23e8eebaae84e64554"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-win32.whl", hash = "sha256:adf20d9a67e0b6393eac162eb81fb10bc9130a80540f4df7e7355c2dd4af9fba"}, - {file = "psycopg2_binary-2.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f9ffd643bc7349eeb664eba8864d9e01f057880f510e4681ba40a6532f93c71"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:def68d7c21984b0f8218e8a15d514f714d96904265164f75f8d3a70f9c295667"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dffc08ca91c9ac09008870c9eb77b00a46b3378719584059c034b8945e26b272"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:280b0bb5cbfe8039205c7981cceb006156a675362a00fe29b16fbc264e242834"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:af9813db73395fb1fc211bac696faea4ca9ef53f32dc0cfa27e4e7cf766dcf24"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-manylinux_2_24_ppc64le.whl", hash = "sha256:63638d875be8c2784cfc952c9ac34e2b50e43f9f0a0660b65e2a87d656b3116c"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ffb7a888a047696e7f8240d649b43fb3644f14f0ee229077e7f6b9f9081635bd"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0c9d5450c566c80c396b7402895c4369a410cab5a82707b11aee1e624da7d004"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:d1c1b569ecafe3a69380a94e6ae09a4789bbb23666f3d3a08d06bbd2451f5ef1"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8fc53f9af09426a61db9ba357865c77f26076d48669f2e1bb24d85a22fb52307"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-win32.whl", hash = "sha256:6472a178e291b59e7f16ab49ec8b4f3bdada0a879c68d3817ff0963e722a82ce"}, - {file = "psycopg2_binary-2.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:35168209c9d51b145e459e05c31a9eaeffa9a6b0fd61689b48e07464ffd1a83e"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-macosx_10_14_x86_64.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:47133f3f872faf28c1e87d4357220e809dfd3fa7c64295a4a148bcd1e6e34ec9"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91920527dea30175cc02a1099f331aa8c1ba39bf8b7762b7b56cbf54bc5cce42"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887dd9aac71765ac0d0bac1d0d4b4f2c99d5f5c1382d8b770404f0f3d0ce8a39"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:1f14c8b0942714eb3c74e1e71700cbbcb415acbc311c730370e70c578a44a25c"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-manylinux_2_24_ppc64le.whl", hash = "sha256:7af0dd86ddb2f8af5da57a976d27cd2cd15510518d582b478fbb2292428710b4"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:93cd1967a18aa0edd4b95b1dfd554cf15af657cb606280996d393dadc88c3c35"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bda845b664bb6c91446ca9609fc69f7db6c334ec5e4adc87571c34e4f47b7ddb"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:01310cf4cf26db9aea5158c217caa92d291f0500051a6469ac52166e1a16f5b7"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:99485cab9ba0fa9b84f1f9e1fef106f44a46ef6afdeec8885e0b88d0772b49e8"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-win32.whl", hash = "sha256:46f0e0a6b5fa5851bbd9ab1bc805eef362d3a230fbdfbc209f4a236d0a7a990d"}, - {file = "psycopg2_binary-2.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:accfe7e982411da3178ec690baaceaad3c278652998b2c45828aaac66cd8285f"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] +markupsafe = [] +mccabe = [] +more-itertools = [] +mypy = [] +mypy-extensions = [] +packaging = [] +pkgutil-resolve-name = [] +pluggy = [] +protobuf = [] +psutil = [] +psycopg2-binary = [] +py = [] pycodestyle = [] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] +pycparser = [] pyflakes = [] -pygments = [ - {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, - {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, -] -pyln-bolt7 = [ - {file = "pyln-bolt7-1.0.186.post0.tar.gz", hash = "sha256:950f788869df138599abea7643b752c16ae8ddfa91c3a31b64647c45d08c0407"}, - {file = "pyln_bolt7-1.0.186.post0-py3-none-any.whl", hash = "sha256:9b65cbaa4fd9db19f30ead7bb5eb61793d2157038237430ff9759e3abc84d739"}, -] +pyln-bolt7 = [] pyln-client = [] pyln-proto = [] pyln-testing = [] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] -pyrsistent = [ - {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, - {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, - {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, - {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"}, - {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"}, - {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"}, - {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"}, - {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"}, - {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"}, - {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"}, - {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"}, - {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"}, - {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"}, - {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"}, - {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, - {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, -] -pysocks = [ - {file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"}, - {file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"}, - {file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"}, -] -pytest = [ - {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, - {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, -] -pytest-custom-exit-code = [ - {file = "pytest-custom_exit_code-0.3.0.tar.gz", hash = "sha256:51ffff0ee2c1ddcc1242e2ddb2a5fd02482717e33a2326ef330e3aa430244635"}, - {file = "pytest_custom_exit_code-0.3.0-py3-none-any.whl", hash = "sha256:6e0ce6e57ce3a583cb7e5023f7d1021e19dfec22be41d9ad345bae2fc61caf3b"}, -] -pytest-forked = [ - {file = "pytest-forked-1.4.0.tar.gz", hash = "sha256:8b67587c8f98cbbadfdd804539ed5455b6ed03802203485dd2f53c1422d7440e"}, - {file = "pytest_forked-1.4.0-py3-none-any.whl", hash = "sha256:bbbb6717efc886b9d64537b41fb1497cfaf3c9601276be8da2cccfea5a3c8ad8"}, -] -pytest-test-groups = [ - {file = "pytest-test-groups-1.0.3.tar.gz", hash = "sha256:a93ee8ae8605ad290965508d13efc975de64f80429465837af5f3dd5bc93fd96"}, -] -pytest-timeout = [ - {file = "pytest-timeout-2.1.0.tar.gz", hash = "sha256:c07ca07404c612f8abbe22294b23c368e2e5104b521c1790195561f37e1ac3d9"}, - {file = "pytest_timeout-2.1.0-py3-none-any.whl", hash = "sha256:f6f50101443ce70ad325ceb4473c4255e9d74e3c7cd0ef827309dfa4c0d975c6"}, -] -pytest-xdist = [ - {file = "pytest-xdist-2.5.0.tar.gz", hash = "sha256:4580deca3ff04ddb2ac53eba39d76cb5dd5edeac050cb6fbc768b0dd712b4edf"}, - {file = "pytest_xdist-2.5.0-py3-none-any.whl", hash = "sha256:6fe5c74fec98906deb8f2d2b616b5c782022744978e7bd4695d39c8f42d0ce65"}, -] -python-bitcoinlib = [ - {file = "python-bitcoinlib-0.11.0.tar.gz", hash = "sha256:3daafd63cb755f6e2067b7c9c514053856034c9f9363c80c37007744d54a2e06"}, - {file = "python_bitcoinlib-0.11.0-py3-none-any.whl", hash = "sha256:6e7982734637135599e2136d3c88d622f147e3b29201636665f799365784cd9e"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] +pyparsing = [] +pyrsistent = [] +pysocks = [] +pytest = [] +pytest-custom-exit-code = [] +pytest-forked = [] +pytest-test-groups = [] +pytest-timeout = [] +pytest-xdist = [] +python-bitcoinlib = [] +six = [] +tomli = [] typed-ast = [] -typing-extensions = [ - {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, - {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, -] -websocket-client = [ - {file = "websocket-client-1.3.3.tar.gz", hash = "sha256:d58c5f284d6a9bf8379dab423259fe8f85b70d5fa5d2916d5791a84594b122b1"}, - {file = "websocket_client-1.3.3-py3-none-any.whl", hash = "sha256:5d55652dc1d0b3c734f044337d929aaf83f4f9138816ec680c1aefefb4dc4877"}, -] -werkzeug = [ - {file = "Werkzeug-2.1.2-py3-none-any.whl", hash = "sha256:72a4b735692dd3135217911cbeaa1be5fa3f62bffb8745c5215420a03dc55255"}, - {file = "Werkzeug-2.1.2.tar.gz", hash = "sha256:1ce08e8093ed67d638d63879fd1ba3735817f7a80de3674d293f5984f25fb6e6"}, -] +typing-extensions = [] +websocket-client = [] +werkzeug = [] zipp = [] diff --git a/pyproject.toml b/pyproject.toml index ac70997c146b..623e5cad90e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,8 @@ pyln-client = { path = "./contrib/pyln-client", develop = true } pyln-proto = { path = "./contrib/pyln-proto", develop = true } Mako = "^1.1.6" websocket-client = "^1.2.3" -grpcio-tools = "^1.44.0" +grpcio-tools = "1.47.0" +grpcio = "1.47.0" [tool.poetry.dev-dependencies] # Test dependencies and inherited dependencies belong here From 7df530d18463d3937cc5b188d2158b56dcf6cb2d Mon Sep 17 00:00:00 2001 From: niftynei Date: Fri, 9 Sep 2022 18:34:22 -0500 Subject: [PATCH 1304/1530] builds: cleanup duplicate and unused code, fix spelling Few extremely minor updates to the ubuntu dockerfile and ci builds --- .github/workflows/ci.yaml | 2 +- contrib/docker/Dockerfile.ubuntu | 4 +--- contrib/docker/scripts/setup.sh | 5 ----- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f4a3efb8a088..e568e936d182 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -122,7 +122,7 @@ jobs: -e VALGRIND=${{ matrix.valgrind }} \ -e DEVELOPER=1 \ -e EXPERIMENTAL_FEATURES=1 \ - -e COMPA=0 \ + -e COMPAT=0 \ -e PYTEST_PAR=2 \ -e PYTEST_OPTS="--timeout=300" \ -e TEST_CMD="make check-protos" \ diff --git a/contrib/docker/Dockerfile.ubuntu b/contrib/docker/Dockerfile.ubuntu index 56aa674fe902..6fe54f37a0ef 100644 --- a/contrib/docker/Dockerfile.ubuntu +++ b/contrib/docker/Dockerfile.ubuntu @@ -3,8 +3,6 @@ LABEL mantainer="Vincenzo Palazzo vincenzopalazzodev@gmail.com" WORKDIR /work -COPY . . - ENV DEBIAN_FRONTEND=noninteractive ENV LANGUAGE=en_US.UTF-8 ENV LANG=en_US.UTF-8 @@ -23,7 +21,7 @@ RUN locale-gen en_US.UTF-8 && dpkg-reconfigure --frontend noninteractive tzdata COPY . . -# install package for pytho cryptography lib +# install package for python cryptography lib # https://cryptography.io/en/latest/installation/#debian-ubuntu RUN apt-get -qq update && \ diff --git a/contrib/docker/scripts/setup.sh b/contrib/docker/scripts/setup.sh index 208571e1d89c..18cf124f3f22 100755 --- a/contrib/docker/scripts/setup.sh +++ b/contrib/docker/scripts/setup.sh @@ -6,8 +6,6 @@ export ELEMENTS_VERSION=0.18.1.8 export RUST_VERSION=nightly export TZ="Europe/London" -sudo useradd -ms /bin/bash tester - sudo apt-get update -qq sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ @@ -54,9 +52,6 @@ sudo apt-get -qq install --no-install-recommends --allow-unauthenticated -yy \ zlib1g-dev -echo "tester ALL=(root) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/tester -sudo chmod 0440 /etc/sudoers.d/tester - ( cd /tmp/ || exit 1 wget https://storage.googleapis.com/c-lightning-tests/bitcoin-$BITCOIN_VERSION-x86_64-linux-gnu.tar.bz2 From ec95c7c18c9200145545de0b491db3cc2f51bb28 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sun, 11 Sep 2022 11:01:42 +0200 Subject: [PATCH 1305/1530] peer_control: fix getinfo showing unannounced addr Currently discovered IPs are only announced when we don't have any usable addresses detected or configured already. However, the cli command `getinfo` still showed theses unannounced addr as if they were announced. Changelog-Fixed: peer_control: getinfo showing unannounced addresses. --- lightningd/peer_control.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index d13b53ff7204..4e32c2dee23d 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2208,6 +2208,7 @@ static struct command_result *json_getinfo(struct command *cmd, struct channel *channel; unsigned int pending_channels = 0, active_channels = 0, inactive_channels = 0, num_peers = 0; + size_t count_announceable; if (!param(cmd, buffer, params, NULL)) return command_param_failed(); @@ -2241,15 +2242,22 @@ static struct command_result *json_getinfo(struct command *cmd, /* Add network info */ if (cmd->ld->listen) { /* These are the addresses we're announcing */ + count_announceable = tal_count(cmd->ld->announceable); json_array_start(response, "address"); - for (size_t i = 0; i < tal_count(cmd->ld->announceable); i++) + for (size_t i = 0; i < count_announceable; i++) json_add_address(response, NULL, cmd->ld->announceable+i); - if (cmd->ld->remote_addr_v4 != NULL && - !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v4)) - json_add_address(response, NULL, cmd->ld->remote_addr_v4); - if (cmd->ld->remote_addr_v6 != NULL && - !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v6)) - json_add_address(response, NULL, cmd->ld->remote_addr_v6); + + /* Currently, IP discovery will only be announced by gossipd, if we + * don't already have usable addresses. + * See `create_node_announcement` in `gossip_generation.c`. */ + if (count_announceable == 0) { + if (cmd->ld->remote_addr_v4 != NULL && + !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v4)) + json_add_address(response, NULL, cmd->ld->remote_addr_v4); + if (cmd->ld->remote_addr_v6 != NULL && + !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v6)) + json_add_address(response, NULL, cmd->ld->remote_addr_v6); + } json_array_end(response); /* This is what we're actually bound to. */ From c6858748bb7d9446d45275ab3f86f7cc8cabb28b Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Sun, 11 Sep 2022 11:08:22 +0200 Subject: [PATCH 1306/1530] cleanup: fix mixed indentation of json_getinfo This one got badly messed up over time. I know we usually don't fix these to have easier git-bisect. I can remove this commit if required. --- lightningd/peer_control.c | 186 ++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 89 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4e32c2dee23d..5dfd104f5bbd 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2203,96 +2203,104 @@ static struct command_result *json_getinfo(struct command *cmd, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { - struct json_stream *response; - struct peer *peer; - struct channel *channel; - unsigned int pending_channels = 0, active_channels = 0, - inactive_channels = 0, num_peers = 0; - size_t count_announceable; - - if (!param(cmd, buffer, params, NULL)) - return command_param_failed(); + struct json_stream *response; + struct peer *peer; + struct channel *channel; + unsigned int pending_channels = 0, active_channels = 0, + inactive_channels = 0, num_peers = 0; + size_t count_announceable; + + if (!param(cmd, buffer, params, NULL)) + return command_param_failed(); + + response = json_stream_success(cmd); + json_add_node_id(response, "id", &cmd->ld->id); + json_add_string(response, "alias", (const char *)cmd->ld->alias); + json_add_hex_talarr(response, "color", cmd->ld->rgb); - response = json_stream_success(cmd); - json_add_node_id(response, "id", &cmd->ld->id); - json_add_string(response, "alias", (const char *)cmd->ld->alias); - json_add_hex_talarr(response, "color", cmd->ld->rgb); - - /* Add some peer and channel stats */ - list_for_each(&cmd->ld->peers, peer, list) { - num_peers++; - - list_for_each(&peer->channels, channel, list) { - if (channel->state == CHANNELD_AWAITING_LOCKIN - || channel->state == DUALOPEND_AWAITING_LOCKIN - || channel->state == DUALOPEND_OPEN_INIT) { - pending_channels++; - } else if (channel_active(channel)) { - active_channels++; - } else { - inactive_channels++; - } - } - } - json_add_num(response, "num_peers", num_peers); - json_add_num(response, "num_pending_channels", pending_channels); - json_add_num(response, "num_active_channels", active_channels); - json_add_num(response, "num_inactive_channels", inactive_channels); - - /* Add network info */ - if (cmd->ld->listen) { - /* These are the addresses we're announcing */ - count_announceable = tal_count(cmd->ld->announceable); - json_array_start(response, "address"); - for (size_t i = 0; i < count_announceable; i++) - json_add_address(response, NULL, cmd->ld->announceable+i); - - /* Currently, IP discovery will only be announced by gossipd, if we - * don't already have usable addresses. - * See `create_node_announcement` in `gossip_generation.c`. */ - if (count_announceable == 0) { - if (cmd->ld->remote_addr_v4 != NULL && - !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v4)) - json_add_address(response, NULL, cmd->ld->remote_addr_v4); - if (cmd->ld->remote_addr_v6 != NULL && - !wireaddr_arr_contains(cmd->ld->announceable, cmd->ld->remote_addr_v6)) - json_add_address(response, NULL, cmd->ld->remote_addr_v6); - } - json_array_end(response); - - /* This is what we're actually bound to. */ - json_array_start(response, "binding"); - for (size_t i = 0; i < tal_count(cmd->ld->binding); i++) - json_add_address_internal(response, NULL, - cmd->ld->binding+i); - json_array_end(response); - } - json_add_string(response, "version", version()); - json_add_num(response, "blockheight", cmd->ld->blockheight); - json_add_string(response, "network", chainparams->network_name); - json_add_amount_msat_compat(response, - wallet_total_forward_fees(cmd->ld->wallet), - "msatoshi_fees_collected", - "fees_collected_msat"); - json_add_string(response, "lightning-dir", cmd->ld->config_netdir); - - if (!cmd->ld->topology->bitcoind->synced) - json_add_string(response, "warning_bitcoind_sync", - "Bitcoind is not up-to-date with network."); - else if (!topology_synced(cmd->ld->topology)) - json_add_string(response, "warning_lightningd_sync", - "Still loading latest blocks from bitcoind."); - - u8 **bits = cmd->ld->our_features->bits; - json_object_start(response, "our_features"); - json_add_hex_talarr(response, "init", - featurebits_or(cmd, bits[INIT_FEATURE], bits[GLOBAL_INIT_FEATURE])); - json_add_hex_talarr(response, "node", bits[NODE_ANNOUNCE_FEATURE]); - json_add_hex_talarr(response, "channel", bits[CHANNEL_FEATURE]); - json_add_hex_talarr(response, "invoice", bits[BOLT11_FEATURE]); - json_object_end(response); - - return command_success(cmd, response); + /* Add some peer and channel stats */ + list_for_each(&cmd->ld->peers, peer, list) { + num_peers++; + + list_for_each(&peer->channels, channel, list) { + if (channel->state == CHANNELD_AWAITING_LOCKIN + || channel->state == DUALOPEND_AWAITING_LOCKIN + || channel->state == DUALOPEND_OPEN_INIT) { + pending_channels++; + } else if (channel_active(channel)) { + active_channels++; + } else { + inactive_channels++; + } + } + } + json_add_num(response, "num_peers", num_peers); + json_add_num(response, "num_pending_channels", pending_channels); + json_add_num(response, "num_active_channels", active_channels); + json_add_num(response, "num_inactive_channels", inactive_channels); + + /* Add network info */ + if (cmd->ld->listen) { + /* These are the addresses we're announcing */ + count_announceable = tal_count(cmd->ld->announceable); + json_array_start(response, "address"); + for (size_t i = 0; i < count_announceable; i++) + json_add_address(response, NULL, cmd->ld->announceable+i); + + /* Currently, IP discovery will only be announced by gossipd, if we + * don't already have usable addresses. + * See `create_node_announcement` in `gossip_generation.c`. */ + if (count_announceable == 0) { + if (cmd->ld->remote_addr_v4 != NULL && + !wireaddr_arr_contains( + cmd->ld->announceable, + cmd->ld->remote_addr_v4)) + json_add_address(response, NULL, + cmd->ld->remote_addr_v4); + if (cmd->ld->remote_addr_v6 != NULL && + !wireaddr_arr_contains( + cmd->ld->announceable, + cmd->ld->remote_addr_v6)) + json_add_address(response, NULL, + cmd->ld->remote_addr_v6); + } + json_array_end(response); + + /* This is what we're actually bound to. */ + json_array_start(response, "binding"); + for (size_t i = 0; i < tal_count(cmd->ld->binding); i++) + json_add_address_internal(response, NULL, + cmd->ld->binding+i); + json_array_end(response); + } + json_add_string(response, "version", version()); + json_add_num(response, "blockheight", cmd->ld->blockheight); + json_add_string(response, "network", chainparams->network_name); + json_add_amount_msat_compat(response, + wallet_total_forward_fees(cmd->ld->wallet), + "msatoshi_fees_collected", + "fees_collected_msat"); + json_add_string(response, "lightning-dir", cmd->ld->config_netdir); + + if (!cmd->ld->topology->bitcoind->synced) + json_add_string(response, "warning_bitcoind_sync", + "Bitcoind is not up-to-date with network."); + else if (!topology_synced(cmd->ld->topology)) + json_add_string(response, "warning_lightningd_sync", + "Still loading latest blocks from bitcoind."); + + u8 **bits = cmd->ld->our_features->bits; + json_object_start(response, "our_features"); + json_add_hex_talarr(response, "init", + featurebits_or(cmd, + bits[INIT_FEATURE], + bits[GLOBAL_INIT_FEATURE])); + json_add_hex_talarr(response, "node", bits[NODE_ANNOUNCE_FEATURE]); + json_add_hex_talarr(response, "channel", bits[CHANNEL_FEATURE]); + json_add_hex_talarr(response, "invoice", bits[BOLT11_FEATURE]); + json_object_end(response); + + return command_success(cmd, response); } static const struct json_command getinfo_command = { From e0259b246e6f372cf585446ce239b3a7bdc6d4b1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:25:31 +0930 Subject: [PATCH 1307/1530] test: fix tlvs test in funding_locked tlv. This FIXME caught my eye, as it's wrong: TLVs are canonical, so they cannot differ in bits and be equal. The equality function needs to be written correctly, however, otherwise it will crash! Signed-off-by: Rusty Russell --- wire/test/run-peer-wire.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index e8de0a6adb7a..173032973486 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -811,9 +811,25 @@ static bool channel_announcement_eq(const struct msg_channel_announcement *a, static bool funding_locked_eq(const struct msg_funding_locked *a, const struct msg_funding_locked *b) { - return eq_upto(a, b, tlvs) && - memeq(a->tlvs->alias, sizeof(a->tlvs->alias), b->tlvs->alias, - sizeof(b->tlvs->alias)); + if (!eq_upto(a, b, tlvs)) + return false; + + /* Both or neither */ + if (!a->tlvs != !b->tlvs) + return false; + + if (!a->tlvs) + return true; + + /* Both or neither */ + if (!a->tlvs->alias != !b->tlvs->alias) + return false; + + if (!a->tlvs->alias) + return true; + + return memeq(a->tlvs->alias, sizeof(a->tlvs->alias), + b->tlvs->alias, sizeof(b->tlvs->alias)); } static bool announcement_signatures_eq(const struct msg_announcement_signatures *a, @@ -1064,8 +1080,7 @@ int main(int argc, char *argv[]) msg = towire_struct_funding_locked(ctx, &fl); fl2 = fromwire_struct_funding_locked(ctx, msg); assert(funding_locked_eq(&fl, fl2)); - /* FIXME: Corruptions in the TLV can still parse correctly, but won't be equal. */ - /*test_corruption_tlv(&fl, fl2, funding_locked);*/ + test_corruption_tlv(&fl, fl2, funding_locked); memset(&as, 2, sizeof(as)); From a56b17c759c53e7705fd6f002d53e809c03e4c26 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:26:31 +0930 Subject: [PATCH 1308/1530] wire/test: neaten and complete tlv checks. In particular, we didn't check the remote_addr in the init msg. Signed-off-by: Rusty Russell --- wire/test/run-peer-wire.c | 104 ++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 61 deletions(-) diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 173032973486..1be1c016b93f 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -72,6 +72,14 @@ static void set_scid(struct short_channel_id *scid) (tal_count((p1)->field) == tal_count((p2)->field) \ && (tal_count((p1)->field) == 0 || memcmp((p1)->field, (p2)->field, tal_bytelen((p1)->field)) == 0)) +/* TLV field comparison: both unset, or both set and eqfn ok */ +#define eq_tlv(p1, p2, tlvname, eqfn) \ + (((p1)->tlvs == NULL && (p2)->tlvs == NULL) \ + || ((p1)->tlvs && (p2)->tlvs \ + && (((p1)->tlvs->tlvname == NULL && (p2)->tlvs->tlvname == NULL) \ + || ((p1)->tlvs->tlvname && (p2)->tlvs->tlvname && \ + eqfn((p1)->tlvs->tlvname, (p2)->tlvs->tlvname))))) + /* Convenience structs for everyone! */ struct msg_error { struct channel_id channel_id; @@ -814,22 +822,7 @@ static bool funding_locked_eq(const struct msg_funding_locked *a, if (!eq_upto(a, b, tlvs)) return false; - /* Both or neither */ - if (!a->tlvs != !b->tlvs) - return false; - - if (!a->tlvs) - return true; - - /* Both or neither */ - if (!a->tlvs->alias != !b->tlvs->alias) - return false; - - if (!a->tlvs->alias) - return true; - - return memeq(a->tlvs->alias, sizeof(a->tlvs->alias), - b->tlvs->alias, sizeof(b->tlvs->alias)); + return eq_tlv(a, b, alias, short_channel_id_eq); } static bool announcement_signatures_eq(const struct msg_announcement_signatures *a, @@ -878,34 +871,31 @@ static bool error_eq(const struct msg_error *a, && eq_var(a, b, data); } -static bool init_eq(const struct msg_init *a, - const struct msg_init *b) +static bool tlv_networks_eq(const struct bitcoin_blkid *a, + const struct bitcoin_blkid *b) { - if (!eq_var(a, b, globalfeatures) || !eq_var(a, b, localfeatures)) + if (tal_count(a) != tal_count(b)) return false; - /* Both or neither */ - if (!a->tlvs != !b->tlvs) - return false; + for (size_t i = 0; i < tal_count(a); i++) + if (!bitcoin_blkid_eq(&a[i], &b[i])) + return false; + return true; +} - if (!a->tlvs) - return true; +static bool talarr_eq(const void *a, const void *b) +{ + return memeq(a, tal_bytelen(a), b, tal_bytelen(b)); +} - /* Both or neither */ - if (!a->tlvs->networks != !b->tlvs->networks) +static bool init_eq(const struct msg_init *a, + const struct msg_init *b) +{ + if (!eq_var(a, b, globalfeatures) || !eq_var(a, b, localfeatures)) return false; - if (!a->tlvs->networks) - return true; - - if (tal_count(a->tlvs->networks) - != tal_count(b->tlvs->networks)) - return false; - for (size_t i = 0; i < tal_count(a->tlvs->networks); i++) - if (!bitcoin_blkid_eq(&a->tlvs->networks[i], - &b->tlvs->networks[i])) - return false; - return true; + return eq_tlv(a, b, networks, tlv_networks_eq) + && eq_tlv(a, b, remote_addr, talarr_eq); } static bool update_fee_eq(const struct msg_update_fee *a, @@ -982,28 +972,14 @@ lease_rates_eq(const struct lease_rates *a, static bool node_announcement_eq(const struct msg_node_announcement *a, const struct msg_node_announcement *b) { - /* Both or neither */ - if (!a->tlvs != !b->tlvs) - return false; - - if (!a->tlvs) - goto body_check; - - /* Both or neither */ - if (!a->tlvs->option_will_fund != !b->tlvs->option_will_fund) + if (!eq_with(a, b, node_id) + || !eq_field(a, b, rgb_color) + || !eq_field(a, b, alias) + || !eq_var(a, b, features) + || !eq_var(a, b, addresses)) return false; - if (a->tlvs->option_will_fund - && !lease_rates_eq(a->tlvs->option_will_fund, - b->tlvs->option_will_fund)) - return false; - -body_check: - return eq_with(a, b, node_id) - && eq_field(a, b, rgb_color) - && eq_field(a, b, alias) - && eq_var(a, b, features) - && eq_var(a, b, addresses); + return eq_tlv(a, b, option_will_fund, lease_rates_eq); } /* Try flipping each bit, try running short. */ @@ -1146,10 +1122,16 @@ int main(int argc, char *argv[]) init.tlvs = tlv_init_tlvs_new(ctx); init.tlvs->networks = tal_arr(init.tlvs, struct bitcoin_blkid, 1); init.tlvs->networks[0] = networks[i].genesis_blockhash; - msg = towire_struct_init(ctx, &init); - init2 = fromwire_struct_init(ctx, msg); - assert(init_eq(&init, init2)); - test_corruption_tlv(&init, init2, init); + for (size_t j = 0; j < 5; j++) { + if (j) { + init.tlvs->remote_addr = tal_arr(init.tlvs, u8, j); + memset(init.tlvs->remote_addr, j, j); + } + msg = towire_struct_init(ctx, &init); + init2 = fromwire_struct_init(ctx, msg); + assert(init_eq(&init, init2)); + test_corruption_tlv(&init, init2, init); + } } memset(&uf, 2, sizeof(uf)); From 6c33f7db65caf619c2c9ceea1071b38c05e3357d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:27:31 +0930 Subject: [PATCH 1309/1530] common: remove unused parameter "allow_deprecated" from parse_wireaddr_internal. Signed-off-by: Rusty Russell --- common/test/run-ip_port_parsing.c | 5 +---- common/test/run-wireaddr.c | 36 +++++++++++++++---------------- common/wireaddr.c | 2 +- common/wireaddr.h | 2 +- devtools/gossipwith.c | 2 +- lightningd/connect_control.c | 2 +- lightningd/options.c | 8 +++---- wallet/test/run-wallet.c | 6 +++--- wallet/wallet.c | 4 ++-- 9 files changed, 31 insertions(+), 36 deletions(-) diff --git a/common/test/run-ip_port_parsing.c b/common/test/run-ip_port_parsing.c index 6e386dff4bdd..f8150c511033 100644 --- a/common/test/run-ip_port_parsing.c +++ b/common/test/run-ip_port_parsing.c @@ -214,11 +214,8 @@ int main(int argc, char *argv[]) assert(!parse_wireaddr("odpzvneidqdf5hdq.onion:49150", &addr, 1, false, NULL)); assert(!parse_wireaddr("odpzvneidqdf5hdq.onion", &addr, 1, false, NULL)); - /* Neither allow_deprecated = true nor false will parse it now */ assert(!parse_wireaddr_internal("odpzvneidqdf5hdq.onion", &addr_int, 1, - false, false, false, false, NULL)); - assert(!parse_wireaddr_internal("odpzvneidqdf5hdq.onion", &addr_int, 1, - false, false, false, true, NULL)); + false, false, false, NULL)); assert(wireaddr_from_hostname(tmpctx, "odpzvneidqdf5hdq.onion", 1, NULL, NULL, NULL) == NULL); assert(wireaddr_from_hostname(tmpctx, "aaa.onion", 1, NULL, NULL, NULL) == NULL); diff --git a/common/test/run-wireaddr.c b/common/test/run-wireaddr.c index 2eec576ba192..8712563b009a 100644 --- a/common/test/run-wireaddr.c +++ b/common/test/run-wireaddr.c @@ -127,59 +127,59 @@ int main(int argc, char *argv[]) common_setup(argv[0]); /* Simple IPv4 address. */ - assert(parse_wireaddr_internal("127.0.0.1", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("127.0.0.1", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("127.0.0.1:9735", &expect->u.wireaddr, 0, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* IPv4 address with port. */ - assert(parse_wireaddr_internal("127.0.0.1:1", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("127.0.0.1:1", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("127.0.0.1:1", &expect->u.wireaddr, 0, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* Simple IPv6 address. */ - assert(parse_wireaddr_internal("::1", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("::1", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("::1", &expect->u.wireaddr, DEFAULT_PORT, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* IPv6 address with port. */ - assert(parse_wireaddr_internal("[::1]:1", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("[::1]:1", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_WIREADDR; assert(parse_wireaddr("::1", &expect->u.wireaddr, 1, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address */ - assert(parse_wireaddr_internal("autotor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("autotor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = DEFAULT_PORT; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with port */ - assert(parse_wireaddr_internal("autotor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("autotor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = DEFAULT_PORT; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with torport */ - assert(parse_wireaddr_internal("autotor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("autotor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = 9055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* autotor address with port and torport */ - assert(parse_wireaddr_internal("autotor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("autotor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_AUTOTOR; expect->u.torservice.port = 10055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address */ - assert(parse_wireaddr_internal("statictor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("statictor:127.0.0.1", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = DEFAULT_PORT; memset(expect->u.torservice.blob, 0, sizeof(expect->u.torservice.blob)); @@ -188,28 +188,28 @@ int main(int argc, char *argv[]) assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port */ - assert(parse_wireaddr_internal("statictor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("statictor:127.0.0.1:9055", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = DEFAULT_PORT; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with torport */ - assert(parse_wireaddr_internal("statictor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("statictor:127.0.0.1/torport=9055", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 9055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9051, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port and torport */ - assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 10055; assert(parse_wireaddr("127.0.0.1", &expect->u.torservice.address, 9055, NULL, &err)); assert(wireaddr_internal_eq(&addr, expect)); /* statictor address with port and torport and torblob */ - assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055/torblob=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("statictor:127.0.0.1:9055/torport=10055/torblob=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_STATICTOR; expect->u.torservice.port = 10055; /* This is actually nul terminated */ @@ -218,24 +218,24 @@ int main(int argc, char *argv[]) assert(wireaddr_internal_eq(&addr, expect)); /* local socket path */ - assert(parse_wireaddr_internal("/tmp/foo.sock", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(parse_wireaddr_internal("/tmp/foo.sock", &addr, DEFAULT_PORT, false, false, false, &err)); expect->itype = ADDR_INTERNAL_SOCKNAME; strcpy(expect->u.sockname, "/tmp/foo.sock"); assert(wireaddr_internal_eq(&addr, expect)); /* Unresolved */ - assert(!parse_wireaddr_internal("ozlabs.org", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(!parse_wireaddr_internal("ozlabs.org", &addr, DEFAULT_PORT, false, false, false, &err)); assert(streq(err, "Needed DNS, but lookups suppressed")); - assert(parse_wireaddr_internal("ozlabs.org", &addr, DEFAULT_PORT, false, false, true, false, &err)); + assert(parse_wireaddr_internal("ozlabs.org", &addr, DEFAULT_PORT, false, false, true, &err)); expect->itype = ADDR_INTERNAL_FORPROXY; strcpy(expect->u.unresolved.name, "ozlabs.org"); expect->u.unresolved.port = DEFAULT_PORT; assert(wireaddr_internal_eq(&addr, expect)); /* Unresolved with port */ - assert(!parse_wireaddr_internal("ozlabs.org:1234", &addr, DEFAULT_PORT, false, false, false, false, &err)); + assert(!parse_wireaddr_internal("ozlabs.org:1234", &addr, DEFAULT_PORT, false, false, false, &err)); assert(streq(err, "Needed DNS, but lookups suppressed")); - assert(parse_wireaddr_internal("ozlabs.org:1234", &addr, DEFAULT_PORT, false, false, true, false, &err)); + assert(parse_wireaddr_internal("ozlabs.org:1234", &addr, DEFAULT_PORT, false, false, true, &err)); expect->itype = ADDR_INTERNAL_FORPROXY; strcpy(expect->u.unresolved.name, "ozlabs.org"); expect->u.unresolved.port = 1234; diff --git a/common/wireaddr.c b/common/wireaddr.c index 4246a460cdff..4950d1510aed 100644 --- a/common/wireaddr.c +++ b/common/wireaddr.c @@ -586,7 +586,7 @@ bool wireaddr_internal_eq(const struct wireaddr_internal *a, bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, bool wildcard_ok, bool dns_ok, - bool unresolved_ok, bool allow_deprecated, + bool unresolved_ok, const char **err_msg) { u16 splitport; diff --git a/common/wireaddr.h b/common/wireaddr.h index 751cd8c8abfa..bdae7788fb30 100644 --- a/common/wireaddr.h +++ b/common/wireaddr.h @@ -158,7 +158,7 @@ bool is_dnsaddr(const char *arg); bool parse_wireaddr_internal(const char *arg, struct wireaddr_internal *addr, u16 port, bool wildcard_ok, bool dns_ok, - bool unresolved_ok, bool allow_deprecated, + bool unresolved_ok, const char **err_msg); void towire_wireaddr_internal(u8 **pptr, diff --git a/devtools/gossipwith.c b/devtools/gossipwith.c index ada35b14cacc..365fb4f04646 100644 --- a/devtools/gossipwith.c +++ b/devtools/gossipwith.c @@ -326,7 +326,7 @@ int main(int argc, char *argv[]) (int)(at - argv[1]), argv[1]); if (!parse_wireaddr_internal(at+1, &addr, chainparams_get_ln_port(chainparams), NULL, - true, false, true, &err_msg)) + true, false, &err_msg)) opt_usage_exit_fail("%s '%s'", err_msg, argv[1]); switch (addr.itype) { diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index a52117e90429..dcc8da297e91 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -201,7 +201,7 @@ static struct command_result *json_connect(struct command *cmd, if (!parse_wireaddr_internal(id_addr.host, addr, port, false, !cmd->ld->always_use_proxy && !cmd->ld->pure_tor_setup, - true, deprecated_apis, + true, &err_msg)) { return command_fail(cmd, LIGHTNINGD, "Host %s:%u not valid: %s", diff --git a/lightningd/options.c b/lightningd/options.c index 54f5213b118a..0bec067046e6 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -237,7 +237,7 @@ static char *opt_add_addr_withtype(const char *arg, || ala != ADDR_ANNOUNCE) { if (!parse_wireaddr_internal(arg, &wi, ld->portnum, wildcard_ok, dns_ok, false, - deprecated_apis, &err_msg)) { + &err_msg)) { return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } @@ -318,8 +318,7 @@ static char *opt_add_addr(const char *arg, struct lightningd *ld) struct wireaddr_internal addr; /* handle in case you used the addr option with an .onion */ - if (parse_wireaddr_internal(arg, &addr, 0, true, false, true, - deprecated_apis, NULL)) { + if (parse_wireaddr_internal(arg, &addr, 0, true, false, true, NULL)) { if (addr.itype == ADDR_INTERNAL_WIREADDR && addr.u.wireaddr.type == ADDR_TYPE_TOR_V3) { log_unusual(ld->log, "You used `--addr=%s` option with an .onion address, please use" @@ -365,8 +364,7 @@ static char *opt_add_bind_addr(const char *arg, struct lightningd *ld) struct wireaddr_internal addr; /* handle in case you used the bind option with an .onion */ - if (parse_wireaddr_internal(arg, &addr, 0, true, false, true, - deprecated_apis, NULL)) { + if (parse_wireaddr_internal(arg, &addr, 0, true, false, true, NULL)) { if (addr.itype == ADDR_INTERNAL_WIREADDR && addr.u.wireaddr.type == ADDR_TYPE_TOR_V3) { log_unusual(ld->log, "You used `--bind-addr=%s` option with an .onion address," diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 29593e16fe00..a3aa98eb6f24 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1098,7 +1098,7 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx) /* Add another utxo that's CSV-locked for 5 blocks */ parse_wireaddr_internal("localhost:1234", &addr, 0, false, false, false, - true, NULL); + NULL); channel.peer = new_peer(ld, 0, &id, &addr, false); channel.dbid = 1; channel.type = channel_type_anchor_outputs(tmpctx); @@ -1395,7 +1395,7 @@ static bool test_channel_crud(struct lightningd *ld, const tal_t *ctx) mempat(scriptpubkey, tal_count(scriptpubkey)); c1.first_blocknum = 1; parse_wireaddr_internal("localhost:1234", &addr, 0, false, false, false, - true, NULL); + NULL); c1.final_key_idx = 1337; p = new_peer(ld, 0, &id, &addr, false); c1.peer = p; @@ -1558,7 +1558,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) pubkey_from_der(tal_hexdata(w, "02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc", 66), 33, &pk); node_id_from_pubkey(&id, &pk); parse_wireaddr_internal("localhost:1234", &addr, 0, false, false, false, - true, NULL); + NULL); /* new channel! */ p = new_peer(ld, 0, &id, &addr, false); diff --git a/wallet/wallet.c b/wallet/wallet.c index c287a3934017..f4f740a75509 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -839,11 +839,11 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) /* This can happen for peers last seen on Torv2! */ addrstr = db_col_strdup(tmpctx, stmt, "address"); if (!parse_wireaddr_internal(addrstr, &addr, chainparams_get_ln_port(chainparams), - false, false, true, true, NULL)) { + false, false, true, NULL)) { log_unusual(w->log, "Unparsable peer address %s: replacing", addrstr); parse_wireaddr_internal("127.0.0.1:1", &addr, chainparams_get_ln_port(chainparams), - false, false, true, true, NULL); + false, false, true, NULL); } /* FIXME: save incoming in db! */ From bfe342c64b5fb298f6739e940ddcaf778af3ba33 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:28:31 +0930 Subject: [PATCH 1310/1530] lightningd: remove double-wrapped rpc_command hook. Somehow we missed this deprecation, found by grep. Changelog-Removed: JSON API: Removed double wrapping of `rpc_command` payload in `rpc_command` JSON field (deprecated v0.8.2) Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 357297f2f3a4..d48ec8509289 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -664,11 +664,6 @@ static void rpc_command_hook_serialize(struct rpc_command_hook_payload *p, char *key; json_object_start(s, "rpc_command"); -#ifdef COMPAT_V081 - if (deprecated_apis) - json_add_tok(s, "rpc_command", p->request, p->buffer); -#endif - json_for_each_obj(i, tok, p->request) { key = tal_strndup(NULL, p->buffer + tok->start, tok->end - tok->start); From 43b037ab0b372397cecc477a50fef201b0b313ed Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:29:31 +0930 Subject: [PATCH 1311/1530] lightningd: always require "jsonrpc": "2.0" in request. Changelog-Removed: JSONRPC: RPC framework now requires the `"jsonrpc"` property inside the request (deprecated in v0.10.2) Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index d48ec8509289..3b758ddcf22a 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -677,7 +677,7 @@ static void replace_command(struct rpc_command_hook_payload *p, const char *buffer, const jsmntok_t *replacetok) { - const jsmntok_t *method = NULL, *params = NULL; + const jsmntok_t *method = NULL, *params = NULL, *jsonrpc; const char *bad; /* Must contain "method", "params" and "id" */ @@ -709,14 +709,10 @@ static void replace_command(struct rpc_command_hook_payload *p, goto fail; } - // deprecated phase to give the possibility to all to migrate and stay safe - // from this more restrictive change. - if (!deprecated_apis) { - const jsmntok_t *jsonrpc = json_get_member(buffer, replacetok, "jsonrpc"); - if (!jsonrpc || jsonrpc->type != JSMN_STRING || !json_tok_streq(buffer, jsonrpc, "2.0")) { - bad = "jsonrpc: \"2.0\" must be specified in the request"; - goto fail; - } + jsonrpc = json_get_member(buffer, replacetok, "jsonrpc"); + if (!jsonrpc || jsonrpc->type != JSMN_STRING || !json_tok_streq(buffer, jsonrpc, "2.0")) { + bad = "jsonrpc: \"2.0\" must be specified in the request"; + goto fail; } was_pending(command_exec(p->cmd->jcon, p->cmd, buffer, replacetok, @@ -853,7 +849,7 @@ REGISTER_PLUGIN_HOOK(rpc_command, static struct command_result * parse_request(struct json_connection *jcon, const jsmntok_t tok[]) { - const jsmntok_t *method, *id, *params; + const jsmntok_t *method, *id, *params, *jsonrpc; struct command *c; struct rpc_command_hook_payload *rpc_hook; bool completed; @@ -881,13 +877,11 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) // Adding a deprecated phase to make sure that all the Core Lightning wrapper // can migrate all the frameworks - if (!deprecated_apis) { - const jsmntok_t *jsonrpc = json_get_member(jcon->buffer, tok, "jsonrpc"); + jsonrpc = json_get_member(jcon->buffer, tok, "jsonrpc"); - if (!jsonrpc || jsonrpc->type != JSMN_STRING || !json_tok_streq(jcon->buffer, jsonrpc, "2.0")) { - json_command_malformed(jcon, "null", "jsonrpc: \"2.0\" must be specified in the request"); - return NULL; - } + if (!jsonrpc || jsonrpc->type != JSMN_STRING || !json_tok_streq(jcon->buffer, jsonrpc, "2.0")) { + json_command_malformed(jcon, "null", "jsonrpc: \"2.0\" must be specified in the request"); + return NULL; } /* Allocate the command off of the `jsonrpc` object and not From a45ec78c36c869534b78cbfa238befdb64107666 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:30:31 +0930 Subject: [PATCH 1312/1530] lightningd: don't allow old listforwards arg order. Changelog-Removed: Old order of the `status` parameter in the `listforwards` rpc command (deprecated in v0.10.2) Signed-off-by: Rusty Russell --- lightningd/peer_htlcs.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index a9d74e6c9879..dac8545496cd 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2853,30 +2853,13 @@ static struct command_result *json_listforwards(struct command *cmd, const char *status_str; enum forward_status status = FORWARD_ANY; - // TODO: We will remove soon after the deprecated period. - if (params && deprecated_apis && params->type == JSMN_ARRAY) { - struct short_channel_id scid; - /* We need to catch [ null, null, "settled" ], and - * [ "1x2x3" ] as old-style */ - if ((params->size > 0 && json_to_short_channel_id(buffer, params + 1, &scid)) || - (params->size == 3 && !json_to_short_channel_id(buffer, params + 3, &scid))) { - if (!param(cmd, buffer, params, - p_opt("in_channel", param_short_channel_id, &chan_in), - p_opt("out_channel", param_short_channel_id, &chan_out), - p_opt("status", param_string, &status_str), - NULL)) - return command_param_failed(); - goto parsed; - } - } - if (!param(cmd, buffer, params, p_opt("status", param_string, &status_str), p_opt("in_channel", param_short_channel_id, &chan_in), p_opt("out_channel", param_short_channel_id, &chan_out), NULL)) return command_param_failed(); - parsed: + if (status_str && !string_to_forward_status(status_str, &status)) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str); From 15751ea1b8c10186dd3ac4fec679a6699d1d7d01 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:31:31 +0930 Subject: [PATCH 1313/1530] lightningd: do inline parsing for listforwards status parameter We can do this now the function is cleaned up. Always better to do the work inside param() since then `check` gets the benefit. Signed-off-by: Rusty Russell --- lightningd/peer_htlcs.c | 34 ++++++++++++++++++++++------------ wallet/wallet.c | 12 +++++++----- wallet/wallet.h | 3 ++- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index dac8545496cd..62a6993ad71f 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2839,6 +2839,22 @@ static void listforwardings_add_forwardings(struct json_stream *response, tal_free(forwardings); } +static struct command_result *param_forward_status(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + enum forward_status **status) +{ + *status = tal(cmd, enum forward_status); + if (string_to_forward_status(buffer + tok->start, + tok->end - tok->start, + *status)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "Unrecognized status"); +} + static struct command_result *json_listforwards(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -2846,25 +2862,19 @@ static struct command_result *json_listforwards(struct command *cmd, { struct json_stream *response; - - struct short_channel_id *chan_in; - struct short_channel_id *chan_out; - - const char *status_str; - enum forward_status status = FORWARD_ANY; + struct short_channel_id *chan_in, *chan_out; + enum forward_status *status; if (!param(cmd, buffer, params, - p_opt("status", param_string, &status_str), + p_opt_def("status", param_forward_status, &status, + FORWARD_ANY), p_opt("in_channel", param_short_channel_id, &chan_in), p_opt("out_channel", param_short_channel_id, &chan_out), NULL)) return command_param_failed(); - if (status_str && !string_to_forward_status(status_str, &status)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str); - response = json_stream_success(cmd); - listforwardings_add_forwardings(response, cmd->ld->wallet, status, chan_in, chan_out); + listforwardings_add_forwardings(response, cmd->ld->wallet, *status, chan_in, chan_out); return command_success(cmd, response); } @@ -2873,6 +2883,6 @@ static const struct json_command listforwards_command = { "listforwards", "channels", json_listforwards, - "List all forwarded payments and their information optionally filtering by [in_channel] [out_channel] and [state]" + "List all forwarded payments and their information optionally filtering by [status], [in_channel] and [out_channel]" }; AUTODATA(json_command, &listforwards_command); diff --git a/wallet/wallet.c b/wallet/wallet.c index f4f740a75509..9cc9e8e58caa 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4485,18 +4485,20 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w) return total; } -bool string_to_forward_status(const char *status_str, enum forward_status *status) +bool string_to_forward_status(const char *status_str, + size_t len, + enum forward_status *status) { - if (streq(status_str, "offered")) { + if (memeqstr(status_str, len, "offered")) { *status = FORWARD_OFFERED; return true; - } else if (streq(status_str, "settled")) { + } else if (memeqstr(status_str, len, "settled")) { *status = FORWARD_SETTLED; return true; - } else if (streq(status_str, "failed")) { + } else if (memeqstr(status_str, len, "failed")) { *status = FORWARD_FAILED; return true; - } else if (streq(status_str, "local_failed")) { + } else if (memeqstr(status_str, len, "local_failed")) { *status = FORWARD_LOCAL_FAILED; return true; } diff --git a/wallet/wallet.h b/wallet/wallet.h index 425c8637db68..f6cf987bb94d 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -158,7 +158,8 @@ static inline const char* forward_status_name(enum forward_status status) abort(); } -bool string_to_forward_status(const char *status_str, enum forward_status *status); +bool string_to_forward_status(const char *status_str, size_t len, + enum forward_status *status); /* /!\ This is a DB ENUM, please do not change the numbering of any * already defined elements (adding is ok) /!\ */ From 733ce81bd4d115779450f87a8e2287af0c55574e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:32:31 +0930 Subject: [PATCH 1314/1530] plugins: require usage for plugin APIs. Changelog-Removed: JSON-RPC: plugins must supply `usage` parameter (deprecated v0.7) Signed-off-by: Rusty Russell --- lightningd/plugin.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index ea9fb5c75872..5ac7abcb8810 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1205,11 +1205,9 @@ static const char *plugin_rpcmethod_add(struct plugin *plugin, cmd->verbose = cmd->description; if (usagetok) usage = json_strdup(tmpctx, buffer, usagetok); - else if (!deprecated_apis) { + else return tal_fmt(plugin, "\"usage\" not provided by plugin"); - } else - usage = "[params]"; if (deptok) { if (!json_to_bool(buffer, deptok, &cmd->deprecated)) From 29264e83fbac59771164ece2361a135a15140b20 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:33:31 +0930 Subject: [PATCH 1315/1530] lightningd: remove `use_proxy_always` parameter to plugin init. Changelog-Removed: Plugins: plugin init `use_proxy_always` (deprecated v0.10.2) Signed-off-by: Rusty Russell --- lightningd/plugin.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 5ac7abcb8810..c0bd267242e4 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1884,9 +1884,6 @@ plugin_populate_init_request(struct plugin *plugin, struct jsonrpc_request *req) json_add_address(req->stream, "proxy", ld->proxyaddr); json_add_bool(req->stream, "torv3-enabled", true); json_add_bool(req->stream, "always_use_proxy", ld->always_use_proxy); - if (deprecated_apis) - json_add_bool(req->stream, "use_proxy_always", - ld->always_use_proxy); } json_object_start(req->stream, "feature_set"); for (enum feature_place fp = 0; fp < NUM_FEATURE_PLACE; fp++) { From 318650a6275d7246c4a2aa6c663aecac5070dd5e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:34:31 +0930 Subject: [PATCH 1316/1530] listchannels: don't show "htlc_maximum_msat" if channel_update didn't set it. It was deprecated in v0.10.1, but only one channel on the network doesn't set it now anyway, and we'll be ignoring that soon. Signed-off-by: Rusty Russell --- plugins/topology.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/plugins/topology.c b/plugins/topology.c index dc4f2fc55854..56f8154704a5 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -281,17 +281,6 @@ static void json_add_halfchan(struct json_stream *response, json_add_amount_msat_only(response, "htlc_minimum_msat", htlc_minimum_msat); - /* We used to always print this, but that's weird */ - if (deprecated_apis && !(message_flags & 1)) { - if (!amount_sat_to_msat(&htlc_maximum_msat, capacity)) - plugin_err(plugin, - "Channel with impossible capacity %s", - type_to_string(tmpctx, - struct amount_sat, - &capacity)); - message_flags = 1; - } - if (message_flags & 1) json_add_amount_msat_only(response, "htlc_maximum_msat", htlc_maximum_msat); From 136d0c8005ed56d9a36a91656bb00d8f3b4a86f4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:35:31 +0930 Subject: [PATCH 1317/1530] offers: update to remove "vendor" and "timestamp" fields. Changelog-EXPERIMENTAL: remove "vendor" (use "issuer") and "timestamp" (use "created_at") fields (deprecated v0.10.2). --- plugins/offers.c | 17 ++------------ plugins/offers_offer.c | 50 ------------------------------------------ 2 files changed, 2 insertions(+), 65 deletions(-) diff --git a/plugins/offers.c b/plugins/offers.c index a1495bd2f9c6..5fd2cf11df7a 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -412,14 +412,9 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer valid = false; } - if (offer->issuer) { + if (offer->issuer) json_add_stringn(js, "issuer", offer->issuer, tal_bytelen(offer->issuer)); - if (deprecated_apis) { - json_add_stringn(js, "vendor", offer->issuer, - tal_bytelen(offer->issuer)); - } - } if (offer->features) json_add_hex_talarr(js, "features", offer->features); if (offer->absolute_expiry) @@ -579,14 +574,9 @@ static void json_add_b12_invoice(struct json_stream *js, valid = false; } - if (invoice->issuer) { + if (invoice->issuer) json_add_stringn(js, "issuer", invoice->issuer, tal_bytelen(invoice->issuer)); - if (deprecated_apis) { - json_add_stringn(js, "vendor", invoice->issuer, - tal_bytelen(invoice->issuer)); - } - } if (invoice->features) json_add_hex_talarr(js, "features", invoice->features); if (invoice->paths) { @@ -644,9 +634,6 @@ static void json_add_b12_invoice(struct json_stream *js, * - MUST reject the invoice if `created_at` is not present. */ if (invoice->created_at) { - /* FIXME: Remove soon! */ - if (deprecated_apis) - json_add_u64(js, "timestamp", *invoice->created_at); json_add_u64(js, "created_at", *invoice->created_at); } else { json_add_string(js, "warning_invoice_missing_created_at", diff --git a/plugins/offers_offer.c b/plugins/offers_offer.c index ee096883071b..520513daddfe 100644 --- a/plugins/offers_offer.c +++ b/plugins/offers_offer.c @@ -322,36 +322,6 @@ struct command_result *json_offer(struct command *cmd, offinfo->offer = offer = tlv_offer_new(offinfo); - /* "issuer" used to be called "vendor" */ - if (deprecated_apis - && params - && params->type == JSMN_OBJECT - && json_get_member(buffer, params, "vendor")) { - if (!param(cmd, buffer, params, - p_req("amount", param_amount, offer), - p_req("description", param_escaped_string, &desc), - p_opt("vendor", param_escaped_string, &issuer), - p_opt("label", param_escaped_string, &offinfo->label), - p_opt("quantity_min", param_u64, &offer->quantity_min), - p_opt("quantity_max", param_u64, &offer->quantity_max), - p_opt("absolute_expiry", param_u64, &offer->absolute_expiry), - p_opt("recurrence", param_recurrence, &offer->recurrence), - p_opt("recurrence_base", - param_recurrence_base, - &offer->recurrence_base), - p_opt("recurrence_paywindow", - param_recurrence_paywindow, - &offer->recurrence_paywindow), - p_opt("recurrence_limit", - param_number, - &offer->recurrence_limit), - p_opt_def("single_use", param_bool, - &offinfo->single_use, false), - NULL)) - return command_param_failed(); - goto after_params; - } - if (!param(cmd, buffer, params, p_req("amount", param_amount, offer), p_req("description", param_escaped_string, &desc), @@ -376,7 +346,6 @@ struct command_result *json_offer(struct command *cmd, NULL)) return command_param_failed(); -after_params: if (!offers_enabled) return command_fail(cmd, LIGHTNINGD, "experimental-offers not enabled"); @@ -466,24 +435,6 @@ struct command_result *json_offerout(struct command *cmd, offer = tlv_offer_new(cmd); - /* "issuer" used to be called "vendor" */ - if (deprecated_apis - && params - && params->type == JSMN_OBJECT - && json_get_member(buffer, params, "vendor")) { - if (!param(cmd, buffer, params, - p_req("amount", param_msat_or_any, offer), - p_req("description", param_escaped_string, &desc), - p_opt("vendor", param_escaped_string, &issuer), - p_opt("label", param_escaped_string, &label), - p_opt("absolute_expiry", param_u64, &offer->absolute_expiry), - p_opt("refund_for", param_invoice_payment_hash, &offer->refund_for), - /* FIXME: hints support! */ - NULL)) - return command_param_failed(); - goto after_params; - } - if (!param(cmd, buffer, params, p_req("amount", param_msat_or_any, offer), p_req("description", param_escaped_string, &desc), @@ -495,7 +446,6 @@ struct command_result *json_offerout(struct command *cmd, NULL)) return command_param_failed(); -after_params: if (!offers_enabled) return command_fail(cmd, LIGHTNINGD, "experimental-offers not enabled"); From 6cf3d4750526a6d4f5d70bd6a47e16a8aab30529 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:36:31 +0930 Subject: [PATCH 1318/1530] offers: remove backwards-compatiblity invoice_request signatures. We changed the field name in v0.11.0, so this breaks compat with v0.10.2. Signed-off-by: Rusty Russell --- lightningd/offer.c | 11 +++-------- plugins/fetchinvoice.c | 5 +---- plugins/offers.c | 21 +++------------------ plugins/offers_invreq_hook.c | 18 +++--------------- tests/test_pay.py | 14 -------------- 5 files changed, 10 insertions(+), 59 deletions(-) diff --git a/lightningd/offer.c b/lightningd/offer.c index f80a7b39c442..67ca0e10fbad 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -473,14 +473,9 @@ static struct command_result *json_createinvoicerequest(struct command *cmd, invreq->fields = tlv_make_fields(invreq, tlv_invoice_request); merkle_tlv(invreq->fields, &merkle); invreq->signature = tal(invreq, struct bip340sig); - if (deprecated_apis) - hsm_sign_b12(cmd->ld, "invoice_request", "payer_signature", - &merkle, invreq->payer_info, invreq->payer_key, - invreq->signature); - else - hsm_sign_b12(cmd->ld, "invoice_request", "signature", - &merkle, invreq->payer_info, invreq->payer_key, - invreq->signature); + hsm_sign_b12(cmd->ld, "invoice_request", "signature", + &merkle, invreq->payer_info, invreq->payer_key, + invreq->signature); response = json_stream_success(cmd); json_add_string(response, "bolt12", invrequest_encode(tmpctx, invreq)); diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index b7f3abe70cec..cbf00bfc3188 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -1196,10 +1196,7 @@ force_payer_secret(struct command *cmd, "Could not remarshall invreq %s", tal_hex(tmpctx, msg)); merkle_tlv(sent->invreq->fields, &merkle); - if (deprecated_apis) - sighash_from_merkle("invoice_request", "payer_signature", &merkle, &sha); - else - sighash_from_merkle("invoice_request", "signature", &merkle, &sha); + sighash_from_merkle("invoice_request", "signature", &merkle, &sha); sent->invreq->signature = tal(invreq, struct bip340sig); if (!secp256k1_schnorrsig_sign32(secp256k1_ctx, diff --git a/plugins/offers.c b/plugins/offers.c index 5fd2cf11df7a..4a64e3729abd 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -776,24 +776,9 @@ static void json_add_invoice_request(struct json_stream *js, "signature", invreq->payer_key, invreq->signature)) { - bool sig_valid; - - if (deprecated_apis) { - /* The old name? */ - sig_valid = bolt12_check_signature(invreq->fields, - "invoice_request", - "payer_signature", - invreq->payer_key, - invreq->signature); - } else { - sig_valid = false; - } - - if (!sig_valid) { - json_add_string(js, "warning_invoice_request_invalid_signature", - "Bad signature"); - valid = false; - } + json_add_string(js, "warning_invoice_request_invalid_signature", + "Bad signature"); + valid = false; } } else { json_add_string(js, "warning_invoice_request_missing_signature", diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index a75817bbae98..ff6a0fc3a5e5 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -431,23 +431,11 @@ static bool check_payer_sig(struct command *cmd, merkle_tlv(invreq->fields, &merkle); sighash_from_merkle("invoice_request", "signature", &merkle, &sighash); - if (secp256k1_schnorrsig_verify(secp256k1_ctx, - sig->u8, - sighash.u.u8, sizeof(sighash.u.u8), &payer_key->pubkey) == 1) - return true; - - if (!deprecated_apis) - return false; - - /* Try old name */ - plugin_log(cmd->plugin, LOG_DBG, - "Testing invoice_request with old name 'payer_signature'"); - sighash_from_merkle("invoice_request", "payer_signature", - &merkle, &sighash); - return secp256k1_schnorrsig_verify(secp256k1_ctx, sig->u8, - sighash.u.u8, sizeof(sighash.u.u8), &payer_key->pubkey) == 1; + sighash.u.u8, + sizeof(sighash.u.u8), + &payer_key->pubkey) == 1; } static struct command_result *invreq_amount_by_quantity(struct command *cmd, diff --git a/tests/test_pay.py b/tests/test_pay.py index 8360b81f59f0..e10cf0afb07b 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4540,20 +4540,6 @@ def test_offer(node_factory, bitcoind): assert 'recurrence: every 600 seconds paywindow -10 to +600 (pay proportional)\n' in output -def test_deprecated_offer(node_factory, bitcoind): - """Test that we allow old invreq name `payer_signature` with deprecated_apis""" - l1, l2 = node_factory.line_graph(2, opts={'experimental-offers': None, - 'allow-deprecated-apis': True}) - - offer = l2.rpc.call('offer', {'amount': 10000, - 'description': 'test'})['bolt12'] - - inv = l1.rpc.call('fetchinvoice', {'offer': offer})['invoice'] - l2.daemon.wait_for_log("Testing invoice_request with old name 'payer_signature'") - - l1.rpc.pay(inv) - - @pytest.mark.developer("dev-no-modern-onion is DEVELOPER-only") def test_fetchinvoice_3hop(node_factory, bitcoind): l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True, From 1a0f7ddb0dd501d5eb69a9a836018b33f0f71c1c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:37:31 +0930 Subject: [PATCH 1319/1530] hsmtool: remove hsm_secret passwords on cmdline support in `dumponchaindescriptors`. Changelog-Removed: `hsmtool`: hsm_secret (ignored) on cmdline for dumponchaindescriptors (deprecated in v0.9.3) Signed-off-by: Rusty Russell --- tools/hsmtool.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/hsmtool.c b/tools/hsmtool.c index 226046365c96..4b392affc2c6 100644 --- a/tools/hsmtool.c +++ b/tools/hsmtool.c @@ -719,11 +719,6 @@ int main(int argc, char *argv[]) if (argc > 3) net = argv[3]; - /* Previously, we accepted hsm_secret passwords on the command line. - * This shifted the location of the network parameter. - * TODO: remove this 3 releases after v0.9.3 */ - if (deprecated_apis && argc > 4) - net = argv[4]; if (net && streq(net, "testnet")) is_testnet = true; From fbcdf2c565b6b5fb59c761ca36bf8d5637d7b0f5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:38:31 +0930 Subject: [PATCH 1320/1530] devtools/bolt-catchup.sh: a tool to update the specs, one commit at a time. This would be more effective if we didn't *merge* in the specs repo, but still. Usage: ./devtools/bolt-catchup.sh It goes through one commit at a time, up to current HEAD, and stops when there are changes, or quotes change. Signed-off-by: Rusty Russell --- devtools/bolt-catchup.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 devtools/bolt-catchup.sh diff --git a/devtools/bolt-catchup.sh b/devtools/bolt-catchup.sh new file mode 100755 index 000000000000..60f5662e57d8 --- /dev/null +++ b/devtools/bolt-catchup.sh @@ -0,0 +1,25 @@ +#! /bin/sh +# A script to upgrade spec versions one at a time, stop when something changes. + +set -e +BOLTDIR=${1:-../bolts} + +HEAD=$(git -C "$BOLTDIR" show --format=%H -s) +VERSION=$(sed -n 's/^DEFAULT_BOLTVERSION[ ]*:=[ ]*\([0-9a-f]*\)/\1/p' < Makefile) + +# We only change Makefile at exit, otherwise git diff shows the difference, of course! +finalize_and_exit() +{ + sed "s/^DEFAULT_BOLTVERSION[ ]*:=[ ]*\([0-9a-f]*\)/DEFAULT_BOLTVERSION := $v/" < Makefile > Makefile.$$ && mv Makefile.$$ Makefile + exit 0 +} + +for v in $(git -C "$BOLTDIR" show "$VERSION..$HEAD" --format=%H -s | tac); do + echo "Trying $v..." + make -s extract-bolt-csv DEFAULT_BOLTVERSION="$v" || finalize_and_exit + git diff --exit-code || finalize_and_exit + make -s check-source-bolt DEFAULT_BOLTVERSION="$v" || finalize_and_exit +done + +echo "No changes, simply upgrading to $v..." +finalize_and_exit From 341bbdfcbe75331d12d84410a5893d199d19b552 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:39:31 +0930 Subject: [PATCH 1321/1530] doc: increase BOLT level to 03468e17563650fb9bfe58b2da4d1e5d28e92009 `flags` in `channel_disabled` gets renamed. We don't use it anyway. Signed-off-by: Rusty Russell --- Makefile | 2 +- wire/onion_wire.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5d21d7f51e5b..39b600335f0f 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := 105c2e5e9f17c68e8c19dc4ca548600a0b8f66f0 +DEFAULT_BOLTVERSION := 03468e17563650fb9bfe58b2da4d1e5d28e92009 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index 9f255bdaf017..09f7920fdbce 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -99,7 +99,7 @@ msgdata,final_incorrect_cltv_expiry,cltv_expiry,u32, msgtype,final_incorrect_htlc_amount,19 msgdata,final_incorrect_htlc_amount,incoming_htlc_amt,u64, msgtype,channel_disabled,UPDATE|20 -msgdata,channel_disabled,flags,u16, +msgdata,channel_disabled,disabled_flags,u16, msgdata,channel_disabled,len,u16, msgdata,channel_disabled,channel_update,byte,len msgtype,expiry_too_far,21 From 1b30ea4b82b1fe5adbdedfc31322bcf3e0c8ac08 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:40:31 +0930 Subject: [PATCH 1322/1530] doc: update BOLTs to bc86304b4b0af5fd5ce9d24f74e2ebbceb7e2730 This contains the zeroconf stuff, with funding_locked renamed to channel_ready. I change that everywhere, and try to fix up the comments. Also the `alias` field is called `short_channel_id`. Signed-off-by: Rusty Russell Changelog-Changed: Protocol: `funding_locked` is now called `channel_ready` as per latest BOLTs. --- Makefile | 2 +- channeld/channeld.c | 90 +++++++++++----------- channeld/channeld_wire.csv | 12 +-- closingd/closingd.c | 4 +- common/channel_type.c | 9 ++- common/gossip_constants.h | 2 +- common/gossip_store.c | 2 +- connectd/gossip_rcvd_filter.c | 2 +- connectd/multiplex.c | 2 +- gossipd/gossipd.c | 2 +- lightningd/channel.c | 6 +- lightningd/channel.h | 4 +- lightningd/channel_control.c | 43 +++++------ lightningd/channel_control.h | 6 +- lightningd/dual_open_control.c | 20 +++-- lightningd/onchain_control.c | 2 +- lightningd/opening_common.c | 9 ++- lightningd/opening_control.c | 14 ++-- openingd/dualopend.c | 70 ++++++++--------- openingd/openingd.c | 5 +- tests/test_closing.py | 2 +- tests/test_connection.py | 8 +- tests/test_opening.py | 14 ++-- wallet/wallet.c | 2 +- wire/extracted_peer_03_openchannelv2.patch | 6 +- wire/extracted_peer_06_zeroconf.patch | 14 ---- wire/peer_wire.c | 8 +- wire/peer_wire.csv | 12 +-- wire/test/run-peer-wire.c | 38 ++++----- 29 files changed, 205 insertions(+), 205 deletions(-) delete mode 100644 wire/extracted_peer_06_zeroconf.patch diff --git a/Makefile b/Makefile index 39b600335f0f..3d11b3299ba0 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := 03468e17563650fb9bfe58b2da4d1e5d28e92009 +DEFAULT_BOLTVERSION := bc86304b4b0af5fd5ce9d24f74e2ebbceb7e2730 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/channeld/channeld.c b/channeld/channeld.c index 15aabc5b984f..9777959a6d9b 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1,4 +1,4 @@ -/* Main channel operation daemon: runs from funding_locked to shutdown_complete. +/* Main channel operation daemon: runs from channel_ready to shutdown_complete. * * We're fairly synchronous: our main loop looks for master or * peer requests and services them synchronously. @@ -52,7 +52,7 @@ struct peer { struct per_peer_state *pps; - bool funding_locked[NUM_SIDES]; + bool channel_ready[NUM_SIDES]; u64 next_index[NUM_SIDES]; /* Features peer supports. */ @@ -182,7 +182,7 @@ struct peer { /* Penalty bases for this channel / peer. */ struct penalty_base **pbases; - /* We allow a 'tx-sigs' message between reconnect + funding_locked */ + /* We allow a 'tx-sigs' message between reconnect + channel_ready */ bool tx_sigs_allowed; /* Have we announced the real scid with a @@ -204,7 +204,7 @@ static void start_commit_timer(struct peer *peer); static void billboard_update(const struct peer *peer) { - const char *update = billboard_message(tmpctx, peer->funding_locked, + const char *update = billboard_message(tmpctx, peer->channel_ready, peer->have_sigs, peer->shutdown_sent, peer->depth_togo, @@ -539,7 +539,7 @@ static void channel_announcement_negotiate(struct peer *peer) return; /* Can't do anything until funding is locked. */ - if (!peer->funding_locked[LOCAL] || !peer->funding_locked[REMOTE]) + if (!peer->channel_ready[LOCAL] || !peer->channel_ready[REMOTE]) return; if (!peer->channel_local_active) { @@ -560,7 +560,7 @@ static void channel_announcement_negotiate(struct peer *peer) * A node: * - if the `open_channel` message has the `announce_channel` bit set AND a `shutdown` message has not been sent: * - MUST send the `announcement_signatures` message. - * - MUST NOT send `announcement_signatures` messages until `funding_locked` + * - MUST NOT send `announcement_signatures` messages until `channel_ready` * has been sent and received AND the funding transaction has at least six confirmations. * - otherwise: * - MUST NOT send the `announcement_signatures` message. @@ -570,7 +570,7 @@ static void channel_announcement_negotiate(struct peer *peer) /* BOLT #7: * - * - MUST NOT send `announcement_signatures` messages until `funding_locked` + * - MUST NOT send `announcement_signatures` messages until `channel_ready` * has been sent and received AND the funding transaction has at least six confirmations. */ if (peer->announce_depth_reached && !peer->have_sigs[LOCAL]) { @@ -602,18 +602,18 @@ static void channel_announcement_negotiate(struct peer *peer) } } -static void handle_peer_funding_locked(struct peer *peer, const u8 *msg) +static void handle_peer_channel_ready(struct peer *peer, const u8 *msg) { struct channel_id chanid; - struct tlv_funding_locked_tlvs *tlvs; + struct tlv_channel_ready_tlvs *tlvs; /* BOLT #2: * * A node: *... * - upon reconnection: - * - MUST ignore any redundant `funding_locked` it receives. + * - MUST ignore any redundant `channel_ready` it receives. */ - if (peer->funding_locked[REMOTE]) + if (peer->channel_ready[REMOTE]) return; /* Too late, we're shutting down! */ @@ -621,10 +621,10 @@ static void handle_peer_funding_locked(struct peer *peer, const u8 *msg) return; peer->old_remote_per_commit = peer->remote_per_commit; - if (!fromwire_funding_locked(msg, msg, &chanid, + if (!fromwire_channel_ready(msg, msg, &chanid, &peer->remote_per_commit, &tlvs)) peer_failed_warn(peer->pps, &peer->channel_id, - "Bad funding_locked %s", tal_hex(msg, msg)); + "Bad channel_ready %s", tal_hex(msg, msg)); if (!channel_id_eq(&chanid, &peer->channel_id)) peer_failed_err(peer->pps, &chanid, @@ -634,17 +634,17 @@ static void handle_peer_funding_locked(struct peer *peer, const u8 *msg) &peer->channel_id)); peer->tx_sigs_allowed = false; - peer->funding_locked[REMOTE] = true; - if (tlvs->alias != NULL) { + peer->channel_ready[REMOTE] = true; + if (tlvs->short_channel_id != NULL) { status_debug( "Peer told us that they'll use alias=%s for this channel", type_to_string(tmpctx, struct short_channel_id, - tlvs->alias)); - peer->short_channel_ids[REMOTE] = *tlvs->alias; + tlvs->short_channel_id)); + peer->short_channel_ids[REMOTE] = *tlvs->short_channel_id; } wire_sync_write(MASTER_FD, - take(towire_channeld_got_funding_locked( - NULL, &peer->remote_per_commit, tlvs->alias))); + take(towire_channeld_got_channel_ready( + NULL, &peer->remote_per_commit, tlvs->short_channel_id))); channel_announcement_negotiate(peer); billboard_update(peer); @@ -2119,8 +2119,8 @@ static void handle_unexpected_tx_sigs(struct peer *peer, const u8 *msg) struct bitcoin_txid txid; /* In a rare case, a v2 peer may re-send a tx_sigs message. - * This happens when they've/we've exchanged funding_locked, - * but they did not receive our funding_locked. */ + * This happens when they've/we've exchanged channel_ready, + * but they did not receive our channel_ready. */ if (!fromwire_tx_signatures(tmpctx, msg, &cid, &txid, cast_const3(struct witness_stack ***, &ws))) peer_failed_warn(peer->pps, &peer->channel_id, @@ -2212,9 +2212,9 @@ static void peer_in(struct peer *peer, const u8 *msg) if (handle_peer_error(peer->pps, &peer->channel_id, msg)) return; - /* Must get funding_locked before almost anything. */ - if (!peer->funding_locked[REMOTE]) { - if (type != WIRE_FUNDING_LOCKED + /* Must get channel_ready before almost anything. */ + if (!peer->channel_ready[REMOTE]) { + if (type != WIRE_CHANNEL_READY && type != WIRE_SHUTDOWN /* We expect these for v2 !! */ && type != WIRE_TX_SIGNATURES @@ -2228,8 +2228,8 @@ static void peer_in(struct peer *peer, const u8 *msg) } switch (type) { - case WIRE_FUNDING_LOCKED: - handle_peer_funding_locked(peer, msg); + case WIRE_CHANNEL_READY: + handle_peer_channel_ready(peer, msg); return; case WIRE_ANNOUNCEMENT_SIGNATURES: handle_peer_announcement_signatures(peer, msg); @@ -2664,7 +2664,7 @@ static void check_current_dataloss_fields(struct peer *peer, status_debug("option_data_loss_protect: fields are correct"); } -/* Older LND sometimes sends funding_locked before reestablish! */ +/* Older LND sometimes sends channel_ready before reestablish! */ /* ... or announcement_signatures. Sigh, let's handle whatever they send. */ static bool capture_premature_msg(const u8 ***shit_lnd_says, const u8 *msg) { @@ -2941,19 +2941,21 @@ static void peer_reconnect(struct peer *peer, * * - if `next_commitment_number` is 1 in both the * `channel_reestablish` it sent and received: - * - MUST retransmit `funding_locked`. + * - MUST retransmit `channel_ready`. * - otherwise: - * - MUST NOT retransmit `funding_locked`. + * - MUST NOT retransmit `channel_ready`, but MAY send + * `channel_ready` with a different `short_channel_id` + * `alias` field. */ - if (peer->funding_locked[LOCAL] + if (peer->channel_ready[LOCAL] && peer->next_index[LOCAL] == 1 && next_commitment_number == 1) { - struct tlv_funding_locked_tlvs *tlvs = tlv_funding_locked_tlvs_new(tmpctx); + struct tlv_channel_ready_tlvs *tlvs = tlv_channel_ready_tlvs_new(tmpctx); status_debug("Retransmitting funding_locked for channel %s", type_to_string(tmpctx, struct channel_id, &peer->channel_id)); /* Contains per commit point #1, for first post-opening commit */ - msg = towire_funding_locked(NULL, + msg = towire_channel_ready(NULL, &peer->channel_id, &peer->next_local_per_commit, tlvs); peer_write(peer->pps, take(msg)); @@ -3229,7 +3231,7 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) { u32 depth; struct short_channel_id *scid, *alias_local; - struct tlv_funding_locked_tlvs *tlvs; + struct tlv_channel_ready_tlvs *tlvs; struct pubkey point; if (!fromwire_channeld_funding_depth(tmpctx, @@ -3258,25 +3260,25 @@ static void handle_funding_depth(struct peer *peer, const u8 *msg) else if (alias_local) peer->short_channel_ids[LOCAL] = *alias_local; - if (!peer->funding_locked[LOCAL]) { - status_debug("funding_locked: sending commit index" + if (!peer->channel_ready[LOCAL]) { + status_debug("channel_ready: sending commit index" " %"PRIu64": %s", peer->next_index[LOCAL], type_to_string(tmpctx, struct pubkey, &peer->next_local_per_commit)); - tlvs = tlv_funding_locked_tlvs_new(tmpctx); - tlvs->alias = alias_local; + tlvs = tlv_channel_ready_tlvs_new(tmpctx); + tlvs->short_channel_id = alias_local; /* Need to retrieve the first point again, even if we - * moved on, as funding_locked explicitly includes the + * moved on, as channel_ready explicitly includes the * first one. */ get_per_commitment_point(1, &point, NULL); - msg = towire_funding_locked(NULL, &peer->channel_id, + msg = towire_channel_ready(NULL, &peer->channel_id, &point, tlvs); peer_write(peer->pps, take(msg)); - peer->funding_locked[LOCAL] = true; + peer->channel_ready[LOCAL] = true; } peer->announce_depth_reached = (depth >= ANNOUNCE_MIN_DEPTH); @@ -3310,7 +3312,7 @@ static void handle_offer_htlc(struct peer *peer, const u8 *inmsg) struct amount_sat htlc_fee; struct pubkey *blinding; - if (!peer->funding_locked[LOCAL] || !peer->funding_locked[REMOTE]) + if (!peer->channel_ready[LOCAL] || !peer->channel_ready[REMOTE]) status_failed(STATUS_FAIL_MASTER_IO, "funding not locked for offer_htlc"); @@ -3745,7 +3747,7 @@ static void req_in(struct peer *peer, const u8 *msg) case WIRE_CHANNELD_SENDING_COMMITSIG_REPLY: case WIRE_CHANNELD_GOT_COMMITSIG_REPLY: case WIRE_CHANNELD_GOT_REVOKE_REPLY: - case WIRE_CHANNELD_GOT_FUNDING_LOCKED: + case WIRE_CHANNELD_GOT_CHANNEL_READY: case WIRE_CHANNELD_GOT_ANNOUNCEMENT: case WIRE_CHANNELD_GOT_SHUTDOWN: case WIRE_CHANNELD_SHUTDOWN_COMPLETE: @@ -3837,8 +3839,8 @@ static void init_channel(struct peer *peer) &peer->revocations_received, &peer->htlc_id, &htlcs, - &peer->funding_locked[LOCAL], - &peer->funding_locked[REMOTE], + &peer->channel_ready[LOCAL], + &peer->channel_ready[REMOTE], &peer->short_channel_ids[LOCAL], &reconnected, &peer->send_shutdown, diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 31d8237a2e1d..78abd73416ae 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -53,8 +53,8 @@ msgdata,channeld_init,revocations_received,u64, msgdata,channeld_init,next_htlc_id,u64, msgdata,channeld_init,num_existing_htlcs,u16, msgdata,channeld_init,htlcs,existing_htlc,num_existing_htlcs -msgdata,channeld_init,local_funding_locked,bool, -msgdata,channeld_init,remote_funding_locked,bool, +msgdata,channeld_init,local_channel_ready,bool, +msgdata,channeld_init,remote_channel_ready,bool, msgdata,channeld_init,funding_short_id,short_channel_id, msgdata,channeld_init,reestablish,bool, msgdata,channeld_init,send_shutdown,bool, @@ -117,10 +117,10 @@ msgdata,channeld_fulfill_htlc,fulfilled_htlc,fulfilled_htlc, msgtype,channeld_fail_htlc,1006 msgdata,channeld_fail_htlc,failed_htlc,failed_htlc, -# When we receive funding_locked. -msgtype,channeld_got_funding_locked,1019 -msgdata,channeld_got_funding_locked,next_per_commit_point,pubkey, -msgdata,channeld_got_funding_locked,alias,?short_channel_id, +# When we receive channel_ready. +msgtype,channeld_got_channel_ready,1019 +msgdata,channeld_got_channel_ready,next_per_commit_point,pubkey, +msgdata,channeld_got_channel_ready,alias,?short_channel_id, #include diff --git a/closingd/closingd.c b/closingd/closingd.c index 30739e0a5486..73b22ab34343 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -260,11 +260,11 @@ receive_offer(struct per_peer_state *pps, /* BOLT #2: * * - upon reconnection: - * - MUST ignore any redundant `funding_locked` it receives. + * - MUST ignore any redundant `channel_ready` it receives. */ /* This should only happen if we've made no commitments, but * we don't have to check that: it's their problem. */ - if (fromwire_peektype(msg) == WIRE_FUNDING_LOCKED) + if (fromwire_peektype(msg) == WIRE_CHANNEL_READY) msg = tal_free(msg); /* BOLT #2: * - if it has sent a previous `shutdown`: diff --git a/common/channel_type.c b/common/channel_type.c index a2b1cab37788..07c815217c40 100644 --- a/common/channel_type.c +++ b/common/channel_type.c @@ -8,7 +8,7 @@ * arbitrary combination (they represent the persistent features which * affect the channel operation). * - * The currently defined types are: + * The currently defined basic types are: * - no features (no bits set) * - `option_static_remotekey` (bit 12) * - `option_anchor_outputs` and `option_static_remotekey` (bits 20 and 12) @@ -118,8 +118,11 @@ struct channel_type *channel_type_accept(const tal_t *ctx, OPT_ZEROCONF, }; - /* The basic channel_types can have any number of the - * following optional bits. */ + /* BOLT #2: + * Each basic type has the following variations allowed: + * - `option_scid_alias` (bit 46) + * - `option_zeroconf` (bit 50) + */ static const size_t variants[] = { OPT_ZEROCONF, }; diff --git a/common/gossip_constants.h b/common/gossip_constants.h index dac2562eb9fb..3ce395eac70e 100644 --- a/common/gossip_constants.h +++ b/common/gossip_constants.h @@ -32,7 +32,7 @@ /* BOLT #7: * - * - MUST NOT send `announcement_signatures` messages until `funding_locked` + * - MUST NOT send `announcement_signatures` messages until `channel_ready` * has been sent and received AND the funding transaction has at least six * confirmations. */ diff --git a/common/gossip_store.c b/common/gossip_store.c index 086232c2953f..2f3f329a2d2e 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -69,7 +69,7 @@ static bool public_msg_type(enum peer_wire type) case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: diff --git a/connectd/gossip_rcvd_filter.c b/connectd/gossip_rcvd_filter.c index 13ff0a2f7c0c..7e3c72331001 100644 --- a/connectd/gossip_rcvd_filter.c +++ b/connectd/gossip_rcvd_filter.c @@ -71,7 +71,7 @@ static bool is_msg_gossip_broadcast(const u8 *cursor) case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: case WIRE_UPDATE_ADD_HTLC: diff --git a/connectd/multiplex.c b/connectd/multiplex.c index ae7f7585b73d..96a32fce017e 100644 --- a/connectd/multiplex.c +++ b/connectd/multiplex.c @@ -361,7 +361,7 @@ static bool is_urgent(enum peer_wire type) case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: case WIRE_INIT_RBF: diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 55321e1dbce2..8c42eec96bc7 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -529,7 +529,7 @@ static void handle_recv_gossip(struct daemon *daemon, const u8 *outermsg) case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: case WIRE_UPDATE_ADD_HTLC: diff --git a/lightningd/channel.c b/lightningd/channel.c index fbf1f4ba8f95..8a35558c0502 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -221,7 +221,7 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->open_attempt = NULL; channel->last_htlc_sigs = NULL; - channel->remote_funding_locked = false; + channel->remote_channel_ready = false; channel->scid = NULL; channel->next_index[LOCAL] = 1; channel->next_index[REMOTE] = 1; @@ -345,7 +345,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, struct amount_sat funding_sats, struct amount_msat push, struct amount_sat our_funds, - bool remote_funding_locked, + bool remote_channel_ready, /* NULL or stolen */ struct short_channel_id *scid, struct short_channel_id *alias_local STEALS, @@ -441,7 +441,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->funding_sats = funding_sats; channel->push = push; channel->our_funds = our_funds; - channel->remote_funding_locked = remote_funding_locked; + channel->remote_channel_ready = remote_channel_ready; channel->scid = tal_steal(channel, scid); channel->alias[LOCAL] = tal_steal(channel, alias_local); channel->alias[REMOTE] = tal_steal(channel, alias_remote); /* Haven't gotten one yet. */ diff --git a/lightningd/channel.h b/lightningd/channel.h index eba34ffa0836..a971b012e42f 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -138,7 +138,7 @@ struct channel { struct amount_sat our_funds; struct amount_msat push; - bool remote_funding_locked; + bool remote_channel_ready; /* Channel if locked locally. */ struct short_channel_id *scid; @@ -291,7 +291,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, struct amount_sat funding_sats, struct amount_msat push, struct amount_sat our_funds, - bool remote_funding_locked, + bool remote_channel_ready, /* NULL or stolen */ struct short_channel_id *scid STEALS, struct short_channel_id *alias_local STEALS, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 8d2cbb24457e..5d4873d8c5ad 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -195,7 +195,7 @@ static void lockin_complete(struct channel *channel) } /* We set this once they're locked in. */ - assert(channel->remote_funding_locked); + assert(channel->remote_channel_ready); /* We might have already started shutting down */ if (channel->state != CHANNELD_AWAITING_LOCKIN) { @@ -225,41 +225,37 @@ static void lockin_complete(struct channel *channel) true); } -bool channel_on_funding_locked(struct channel *channel, - struct pubkey *next_per_commitment_point) +bool channel_on_channel_ready(struct channel *channel, + struct pubkey *next_per_commitment_point) { - if (channel->remote_funding_locked) { + if (channel->remote_channel_ready) { channel_internal_error(channel, - "channel_got_funding_locked twice"); + "channel_got_channel_ready twice"); return false; } update_per_commit_point(channel, next_per_commitment_point); - log_debug(channel->log, "Got funding_locked"); - channel->remote_funding_locked = true; + log_debug(channel->log, "Got channel_ready"); + channel->remote_channel_ready = true; return true; } -/* We were informed by channeld that it announced the channel and sent - * an update, so we can now start sending a node_announcement. The - * first step is to build the provisional announcement and ask the HSM - * to sign it. */ - -static void peer_got_funding_locked(struct channel *channel, const u8 *msg) +/* We were informed by channeld that channel is ready (reached mindepth) */ +static void peer_got_channel_ready(struct channel *channel, const u8 *msg) { struct pubkey next_per_commitment_point; struct short_channel_id *alias_remote; - if (!fromwire_channeld_got_funding_locked(tmpctx, + if (!fromwire_channeld_got_channel_ready(tmpctx, msg, &next_per_commitment_point, &alias_remote)) { channel_internal_error(channel, - "bad channel_got_funding_locked %s", + "bad channel_got_channel_ready %s", tal_hex(channel, msg)); return; } - if (!channel_on_funding_locked(channel, &next_per_commitment_point)) + if (!channel_on_channel_ready(channel, &next_per_commitment_point)) return; if (channel->alias[REMOTE] == NULL) @@ -538,8 +534,8 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_GOT_REVOKE: peer_got_revoke(sd->channel, msg); break; - case WIRE_CHANNELD_GOT_FUNDING_LOCKED: - peer_got_funding_locked(sd->channel, msg); + case WIRE_CHANNELD_GOT_CHANNEL_READY: + peer_got_channel_ready(sd->channel, msg); break; case WIRE_CHANNELD_GOT_ANNOUNCEMENT: peer_got_announcement(sd->channel, msg); @@ -765,7 +761,7 @@ bool peer_start_channeld(struct channel *channel, channel->next_htlc_id, htlcs, channel->scid != NULL, - channel->remote_funding_locked, + channel->remote_channel_ready, &scid, reconnected, /* Anything that indicates we are or have @@ -859,12 +855,11 @@ bool channel_tell_depth(struct lightningd *ld, take(towire_channeld_funding_depth( NULL, channel->scid, channel->alias[LOCAL], depth))); - if (channel->remote_funding_locked && - channel->state == CHANNELD_AWAITING_LOCKIN && - depth >= channel->minimum_depth) + if (channel->remote_channel_ready && + channel->state == CHANNELD_AWAITING_LOCKIN && + depth >= channel->minimum_depth) { lockin_complete(channel); - - else if (depth == 1 && channel->minimum_depth == 0) { + } else if (depth == 1 && channel->minimum_depth == 0) { /* If we have a zeroconf channel, i.e., no scid yet * but have exchange `channel_ready` messages, then we * need to fire a second time, in order to trigger the diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index 9008bed1d055..2f8b356e863d 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -30,9 +30,9 @@ void channel_notify_new_block(struct lightningd *ld, struct command_result *cancel_channel_before_broadcast(struct command *cmd, struct peer *peer); -/* Update the channel info on funding locked */ -bool channel_on_funding_locked(struct channel *channel, - struct pubkey *next_per_commitment_point); +/* Update the channel info on channel_ready */ +bool channel_on_channel_ready(struct channel *channel, + struct pubkey *next_per_commitment_point); /* Record channel open (coin movement notifications) */ void channel_record_open(struct channel *channel, u32 blockheight, bool record_push); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index fa458bca3a35..a2f84ee61da0 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1631,7 +1631,7 @@ static void handle_peer_tx_sigs_sent(struct subd *dualopend, &channel->peer->id, &channel->funding_sats, &channel->funding.txid, - channel->remote_funding_locked); + channel->remote_channel_ready); /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2 * The receiving node: ... @@ -1714,7 +1714,7 @@ static void handle_peer_locked(struct subd *dualopend, const u8 *msg) /* Updates channel with the next per-commit point etc, calls * channel_internal_error on failure */ - if (!channel_on_funding_locked(channel, &remote_per_commit)) + if (!channel_on_channel_ready(channel, &remote_per_commit)) return; /* Remember that we got the lock-in */ @@ -1737,7 +1737,7 @@ static void handle_channel_locked(struct subd *dualopend, peer_fd = new_peer_fd_arr(tmpctx, fds); assert(channel->scid); - assert(channel->remote_funding_locked); + assert(channel->remote_channel_ready); /* This can happen if we missed their sigs, for some reason */ if (channel->state != DUALOPEND_AWAITING_LOCKIN) @@ -1997,7 +1997,7 @@ static void handle_peer_tx_sigs_msg(struct subd *dualopend, &channel->peer->id, &channel->funding_sats, &channel->funding.txid, - channel->remote_funding_locked); + channel->remote_channel_ready); /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2 * The receiving node: ... @@ -3364,10 +3364,14 @@ bool peer_start_dualopend(struct peer *peer, /* BOLT #2: * * The sender: - * - SHOULD set `minimum_depth` to a number of blocks it - * considers reasonable to avoid double-spending of the - * funding transaction. + * - if `channel_type` includes `option_zeroconf`: + * - MUST set `minimum_depth` to zero. + * - otherwise: + * - SHOULD set `minimum_depth` to a number of blocks it + * considers reasonable to avoid double-spending of the + * funding transaction. */ + /* FIXME: We should override this to 0 in the openchannel2 hook of we want zeroconf*/ channel->minimum_depth = peer->ld->config.anchor_confirms; msg = towire_dualopend_init(NULL, chainparams, @@ -3470,7 +3474,7 @@ bool peer_restart_dualopend(struct peer *peer, inflight->funding_psbt, channel->opener, channel->scid != NULL, - channel->remote_funding_locked, + channel->remote_channel_ready, channel->state == CHANNELD_SHUTTING_DOWN, channel->shutdown_scriptpubkey[REMOTE] != NULL, channel->shutdown_scriptpubkey[LOCAL], diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 4eb634cfeb81..232dc2a11dae 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -625,7 +625,7 @@ enum watch_result onchaind_funding_spent(struct channel *channel, channel_fail_permanent(channel, reason, "Funding transaction spent"); /* If we haven't posted the open event yet, post an open */ - if (!channel->scid || !channel->remote_funding_locked) { + if (!channel->scid || !channel->remote_channel_ready) { u32 blkh; /* Blockheight will be zero if it's not in chain */ blkh = wallet_transaction_height(channel->peer->ld->wallet, diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 778648b0a55b..5f2f848d8c83 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -55,9 +55,14 @@ new_uncommitted_channel(struct peer *peer) /* BOLT #2: * * The sender: - * - SHOULD set `minimum_depth` to a number of blocks it considers - * reasonable to avoid double-spending of the funding transaction. + * - if `channel_type` includes `option_zeroconf`: + * - MUST set `minimum_depth` to zero. + * - otherwise: + * - SHOULD set `minimum_depth` to a number of blocks it + * considers reasonable to avoid double-spending of the + * funding transaction. */ + /* We override this in openchannel hook if we want zeroconf */ uc->minimum_depth = ld->config.anchor_confirms; /* Declare the new channel to the HSM. */ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 3e740844c3f5..f059a7eb7200 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -178,7 +178,7 @@ wallet_commit_channel(struct lightningd *ld, funding_sats, push, local_funding, - false, /* !remote_funding_locked */ + false, /* !remote_channel_ready */ NULL, /* no scid yet */ alias_local, /* But maybe we have an alias we want to use? */ NULL, /* They haven't told us an alias yet */ @@ -539,7 +539,7 @@ static void opening_fundee_finished(struct subd *openingd, /* Tell plugins about the success */ notify_channel_opened(ld, &channel->peer->id, &channel->funding_sats, - &channel->funding.txid, channel->remote_funding_locked); + &channel->funding.txid, channel->remote_channel_ready); if (pbase) wallet_penalty_base_add(ld->wallet, channel->dbid, pbase); @@ -1213,8 +1213,12 @@ static struct command_result *json_fundchannel_start(struct command *cmd, /* BOLT #2: * * The sender: - * - SHOULD set `minimum_depth` to a number of blocks it considers - * reasonable to avoid double-spending of the funding transaction. + * - if `channel_type` includes `option_zeroconf`: + * - MUST set `minimum_depth` to zero. + * - otherwise: + * - SHOULD set `minimum_depth` to a number of blocks it + * considers reasonable to avoid double-spending of the + * funding transaction. */ assert(mindepth != NULL); fc->uc->minimum_depth = *mindepth; @@ -1376,7 +1380,7 @@ static struct channel *stub_chan(struct command *cmd, funding_sats, AMOUNT_MSAT(0), AMOUNT_SAT(0), - true, /* !remote_funding_locked */ + true, /* remote_channel_ready */ scid, scid, scid, diff --git a/openingd/dualopend.c b/openingd/dualopend.c index c2de0716b3ff..0c40c0fdc6aa 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -195,7 +195,7 @@ struct state { struct feature_set *our_features; /* Tally of which sides are locked, or not */ - bool funding_locked[NUM_SIDES]; + bool channel_ready[NUM_SIDES]; /* Are we shutting down? */ bool shutdown_sent[NUM_SIDES]; @@ -380,7 +380,7 @@ static void negotiation_failed(struct state *state, static void billboard_update(struct state *state) { - const char *update = billboard_message(tmpctx, state->funding_locked, + const char *update = billboard_message(tmpctx, state->channel_ready, NULL, state->shutdown_sent, 0, /* Always zero? */ @@ -930,9 +930,9 @@ static void handle_tx_sigs(struct state *state, const u8 *msg) open_err_fatal(state, "Bad tx_signatures %s", tal_hex(msg, msg)); - /* Maybe they didn't get our funding_locked message ? */ - if (state->funding_locked[LOCAL] && !state->reconnected) { - status_broken("Got WIRE_TX_SIGNATURES after funding locked " + /* Maybe they didn't get our channel_ready message ? */ + if (state->channel_ready[LOCAL] && !state->reconnected) { + status_broken("Got WIRE_TX_SIGNATURES after channel_ready " "for channel %s, ignoring: %s", type_to_string(tmpctx, struct channel_id, &state->channel_id), @@ -941,10 +941,10 @@ static void handle_tx_sigs(struct state *state, const u8 *msg) } /* On reconnect, we expect them to resend tx_sigs if they haven't - * gotten our funding_locked yet */ - if (state->funding_locked[REMOTE] && !state->reconnected) + * gotten our channel_ready yet */ + if (state->channel_ready[REMOTE] && !state->reconnected) open_err_warn(state, - "tx_signatures sent after funding_locked %s", + "tx_signatures sent after channel_ready %s", tal_hex(msg, msg)); if (!tx_state->psbt) @@ -1128,18 +1128,18 @@ static void init_changeset(struct tx_state *tx_state, struct wally_psbt *psbt) tx_state->changeset = psbt_get_changeset(tx_state, empty_psbt, psbt); } -static u8 *handle_funding_locked(struct state *state, u8 *msg) +static u8 *handle_channel_ready(struct state *state, u8 *msg) { struct channel_id cid; struct pubkey remote_per_commit; - struct tlv_funding_locked_tlvs *tlvs; + struct tlv_channel_ready_tlvs *tlvs; - if (!fromwire_funding_locked(tmpctx, msg, &cid, &remote_per_commit, &tlvs)) - open_err_fatal(state, "Bad funding_locked %s", + if (!fromwire_channel_ready(tmpctx, msg, &cid, &remote_per_commit, &tlvs)) + open_err_fatal(state, "Bad channel_ready %s", tal_hex(msg, msg)); if (!channel_id_eq(&cid, &state->channel_id)) - open_err_fatal(state, "funding_locked ids don't match:" + open_err_fatal(state, "channel_ready ids don't match:" " expected %s, got %s", type_to_string(msg, struct channel_id, &state->channel_id), @@ -1148,21 +1148,21 @@ static u8 *handle_funding_locked(struct state *state, u8 *msg) /* If we haven't gotten their tx_sigs yet, this is a protocol error */ if (!state->tx_state->remote_funding_sigs_rcvd) { open_err_warn(state, - "funding_locked sent before tx_signatures %s", + "channel_ready sent before tx_signatures %s", tal_hex(msg, msg)); } /* We save when the peer locks, so we do the right * thing on reconnects */ - if (!state->funding_locked[REMOTE]) { + if (!state->channel_ready[REMOTE]) { msg = towire_dualopend_peer_locked(NULL, &remote_per_commit); wire_sync_write(REQ_FD, take(msg)); } - state->funding_locked[REMOTE] = true; + state->channel_ready[REMOTE] = true; billboard_update(state); - if (state->funding_locked[LOCAL]) + if (state->channel_ready[LOCAL]) return towire_dualopend_channel_locked(state); return NULL; @@ -1247,8 +1247,8 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) * startup an RBF */ handle_tx_sigs(state, msg); continue; - case WIRE_FUNDING_LOCKED: - handle_funding_locked(state, msg); + case WIRE_CHANNEL_READY: + handle_channel_ready(state, msg); return NULL; case WIRE_SHUTDOWN: handle_peer_shutdown(state, msg); @@ -1608,7 +1608,7 @@ static bool run_tx_interactive(struct state *state, case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: case WIRE_UPDATE_ADD_HTLC: @@ -3388,19 +3388,19 @@ static void hsm_per_commitment_point(u64 index, struct pubkey *point) tal_hex(tmpctx, msg)); } -static void send_funding_locked(struct state *state) +static void send_channel_ready(struct state *state) { u8 *msg; struct pubkey next_local_per_commit; - struct tlv_funding_locked_tlvs *tlvs = tlv_funding_locked_tlvs_new(tmpctx); + struct tlv_channel_ready_tlvs *tlvs = tlv_channel_ready_tlvs_new(tmpctx); /* Figure out the next local commit */ hsm_per_commitment_point(1, &next_local_per_commit); - msg = towire_funding_locked(NULL, &state->channel_id, + msg = towire_channel_ready(NULL, &state->channel_id, &next_local_per_commit, tlvs); peer_write(state->pps, take(msg)); - state->funding_locked[LOCAL] = true; + state->channel_ready[LOCAL] = true; billboard_update(state); } @@ -3453,8 +3453,8 @@ static u8 *handle_funding_depth(struct state *state, u8 *msg) /* Tell gossipd the new channel exists before we tell peer. */ tell_gossipd_new_channel(state); - send_funding_locked(state); - if (state->funding_locked[REMOTE]) + send_channel_ready(state); + if (state->channel_ready[REMOTE]) return towire_dualopend_channel_locked(state); return NULL; @@ -3614,17 +3614,17 @@ static void do_reconnect_dance(struct state *state) /* It's possible we sent our sigs, but they didn't get them. * Resend our signatures, just in case */ if (psbt_side_finalized(tx_state->psbt, state->our_role) - && !state->funding_locked[REMOTE]) { + && !state->channel_ready[REMOTE]) { msg = psbt_to_tx_sigs_msg(NULL, state, tx_state->psbt); peer_write(state->pps, take(msg)); } - if (state->funding_locked[LOCAL]) { + if (state->channel_ready[LOCAL]) { status_debug("Retransmitting funding_locked for channel %s", type_to_string(tmpctx, struct channel_id, &state->channel_id)); - send_funding_locked(state); + send_channel_ready(state); } peer_billboard(true, "Reconnected, and reestablished."); @@ -3714,8 +3714,8 @@ static u8 *handle_peer_in(struct state *state) case WIRE_TX_SIGNATURES: handle_tx_sigs(state, msg); return NULL; - case WIRE_FUNDING_LOCKED: - return handle_funding_locked(state, msg); + case WIRE_CHANNEL_READY: + return handle_channel_ready(state, msg); case WIRE_SHUTDOWN: handle_peer_shutdown(state, msg); return NULL; @@ -3833,8 +3833,8 @@ int main(int argc, char *argv[]) = NULL; /*~ We're not locked or shutting down quite yet */ - state->funding_locked[LOCAL] - = state->funding_locked[REMOTE] + state->channel_ready[LOCAL] + = state->channel_ready[REMOTE] = false; state->shutdown_sent[LOCAL] = state->shutdown_sent[REMOTE] @@ -3861,8 +3861,8 @@ int main(int argc, char *argv[]) &state->first_per_commitment_point[REMOTE], &state->tx_state->psbt, &opener, - &state->funding_locked[LOCAL], - &state->funding_locked[REMOTE], + &state->channel_ready[LOCAL], + &state->channel_ready[REMOTE], &state->shutdown_sent[LOCAL], &state->shutdown_sent[REMOTE], &state->upfront_shutdown_script[LOCAL], diff --git a/openingd/openingd.c b/openingd/openingd.c index d2ae1cada07f..3e9272a43f8d 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -839,8 +839,9 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) /* BOLT #2: * The receiving node MUST fail the channel if: *... - * - It supports `channel_type`, `channel_type` was set, and the - * `type` is not suitable. + * - It supports `channel_type` and `channel_type` was set: + * - if `type` is not suitable. + * - if `type` includes `option_zeroconf` and it does not trust the sender to open an unconfirmed channel. */ if (open_tlvs->channel_type) { state->channel_type = diff --git a/tests/test_closing.py b/tests/test_closing.py index e2cf90dac9f0..f278d9c93a19 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1785,7 +1785,7 @@ def test_onchain_first_commit(node_factory, bitcoind): coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py') # HTLC 1->2, 1 fails just after funding. - disconnects = ['+WIRE_FUNDING_LOCKED', 'permfail'] + disconnects = ['+WIRE_CHANNEL_READY', 'permfail'] # Make locktime different, as we once had them reversed! l1, l2 = node_factory.line_graph(2, opts=[{'disconnect': disconnects, 'plugin': coin_mvt_plugin}, diff --git a/tests/test_connection.py b/tests/test_connection.py index 906e8c80cd0f..3f915c8c6fb4 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -679,7 +679,7 @@ def test_reconnect_no_update(node_factory, executor, bitcoind): reconnects. See comments for details. """ - disconnects = ["-WIRE_FUNDING_LOCKED", "-WIRE_SHUTDOWN"] + disconnects = ["-WIRE_CHANNEL_READY", "-WIRE_SHUTDOWN"] # Allow bad gossip because it might receive WIRE_CHANNEL_UPDATE before # announcement of the disconnection l1 = node_factory.get_node(may_reconnect=True, allow_bad_gossip=True) @@ -715,8 +715,8 @@ def test_reconnect_no_update(node_factory, executor, bitcoind): @pytest.mark.openchannel('v2') def test_reconnect_normal(node_factory): # Should reconnect fine even if locked message gets lost. - disconnects = ['-WIRE_FUNDING_LOCKED', - '+WIRE_FUNDING_LOCKED'] + disconnects = ['-WIRE_CHANNEL_READY', + '+WIRE_CHANNEL_READY'] l1 = node_factory.get_node(disconnect=disconnects, may_reconnect=True) l2 = node_factory.get_node(may_reconnect=True) @@ -2234,7 +2234,7 @@ def test_channel_persistence(node_factory, bitcoind, executor): # Fire off a sendpay request, it'll get interrupted by a restart executor.submit(l1.pay, l2, 10000) # Wait for it to be committed to, i.e., stored in the DB - l1.daemon.wait_for_log('peer_in WIRE_FUNDING_LOCKED') + l1.daemon.wait_for_log('peer_in WIRE_CHANNEL_READY') l1.daemon.wait_for_log('peer_in WIRE_COMMITMENT_SIGNED') # Stop l2, l1 will reattempt to connect diff --git a/tests/test_opening.py b/tests/test_opening.py index abbdac3658d8..7072e82165d4 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1274,13 +1274,13 @@ def test_zeroconf_mindepth(bitcoind, node_factory): assert l2.db.query('SELECT minimum_depth FROM channels') == [{'minimum_depth': 2}] bitcoind.generate_block(2, wait_for_mempool=1) # Confirm on the l2 side. - l2.daemon.wait_for_log(r'peer_out WIRE_FUNDING_LOCKED') + l2.daemon.wait_for_log(r'peer_out WIRE_CHANNEL_READY') # l1 should not be sending funding_locked/channel_ready yet, it is # configured to wait for 6 confirmations. - assert not l1.daemon.is_in_log(r'peer_out WIRE_FUNDING_LOCKED') + assert not l1.daemon.is_in_log(r'peer_out WIRE_CHANNEL_READY') bitcoind.generate_block(4) # Confirm on the l2 side. - l1.daemon.wait_for_log(r'peer_out WIRE_FUNDING_LOCKED') + l1.daemon.wait_for_log(r'peer_out WIRE_CHANNEL_READY') wait_for(lambda: l1.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") wait_for(lambda: l2.rpc.listpeers()['peers'][0]['channels'][0]['state'] == "CHANNELD_NORMAL") @@ -1318,11 +1318,11 @@ def test_zeroconf_open(bitcoind, node_factory): assert l2.db.query('SELECT minimum_depth FROM channels') == [{'minimum_depth': 0}] l1.daemon.wait_for_logs([ - r'peer_in WIRE_FUNDING_LOCKED', + r'peer_in WIRE_CHANNEL_READY', r'Peer told us that they\'ll use alias=[0-9x]+ for this channel', ]) l2.daemon.wait_for_logs([ - r'peer_in WIRE_FUNDING_LOCKED', + r'peer_in WIRE_CHANNEL_READY', r'Peer told us that they\'ll use alias=[0-9x]+ for this channel', ]) @@ -1606,8 +1606,8 @@ def test_zeroconf_multichan_forward(node_factory): l2.fundwallet(10**7) l2.rpc.fundchannel(l3.info['id'], 2 * 10**6, mindepth=0) - l2.daemon.wait_for_log(r'peer_in WIRE_FUNDING_LOCKED') - l3.daemon.wait_for_log(r'peer_in WIRE_FUNDING_LOCKED') + l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_READY') + l3.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_READY') inv = l3.rpc.invoice(amount_msat=10000, label='lbl1', description='desc')['bolt11'] l1.rpc.pay(inv) diff --git a/wallet/wallet.c b/wallet/wallet.c index 9cc9e8e58caa..8ba81f9ffa73 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1922,7 +1922,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_int(stmt, 11, chan->funding.n); db_bind_amount_sat(stmt, 12, &chan->funding_sats); db_bind_amount_sat(stmt, 13, &chan->our_funds); - db_bind_int(stmt, 14, chan->remote_funding_locked); + db_bind_int(stmt, 14, chan->remote_channel_ready); db_bind_amount_msat(stmt, 15, &chan->push); db_bind_amount_msat(stmt, 16, &chan->our_msat); diff --git a/wire/extracted_peer_03_openchannelv2.patch b/wire/extracted_peer_03_openchannelv2.patch index c32c95529883..81b5ce8cfce0 100644 --- a/wire/extracted_peer_03_openchannelv2.patch +++ b/wire/extracted_peer_03_openchannelv2.patch @@ -42,9 +42,9 @@ msgdata,open_channel,chain_hash,chain_hash, msgdata,open_channel,temporary_channel_id,byte,32 @@ -86,6 +116,56 @@ - msgtype,funding_locked,36 - msgdata,funding_locked,channel_id,channel_id, - msgdata,funding_locked,next_per_commitment_point,point, + msgdata,channel_ready,tlvs,channel_ready_tlvs, + tlvtype,channel_ready_tlvs,short_channel_id,1 + tlvdata,channel_ready_tlvs,short_channel_id,alias,short_channel_id, +msgtype,open_channel2,64 +msgdata,open_channel2,chain_hash,chain_hash, +msgdata,open_channel2,channel_id,channel_id, diff --git a/wire/extracted_peer_06_zeroconf.patch b/wire/extracted_peer_06_zeroconf.patch deleted file mode 100644 index 5084b00cb7e3..000000000000 --- a/wire/extracted_peer_06_zeroconf.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv -index a028ddc66..fc24b61ef 100644 ---- a/wire/peer_wire.csv -+++ b/wire/peer_wire.csv -@@ -126,6 +126,9 @@ msgdata,funding_signed,signature,signature, - msgtype,funding_locked,36 - msgdata,funding_locked,channel_id,channel_id, - msgdata,funding_locked,next_per_commitment_point,point, -+msgdata,funding_locked,tlvs,funding_locked_tlvs, -+tlvtype,funding_locked_tlvs,alias,1 -+tlvdata,funding_locked_tlvs,alias,scid,short_channel_id, - msgtype,open_channel2,64 - msgdata,open_channel2,chain_hash,chain_hash, - msgdata,open_channel2,channel_id,channel_id, diff --git a/wire/peer_wire.c b/wire/peer_wire.c index 0aca55a5d488..2bd0eea71119 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -12,7 +12,7 @@ static bool unknown_type(enum peer_wire t) case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: case WIRE_UPDATE_ADD_HTLC: @@ -75,7 +75,7 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_ACCEPT_CHANNEL: case WIRE_FUNDING_CREATED: case WIRE_FUNDING_SIGNED: - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: case WIRE_SHUTDOWN: case WIRE_CLOSING_SIGNED: case WIRE_UPDATE_ADD_HTLC: @@ -242,9 +242,9 @@ bool extract_channel_id(const u8 *in_pkt, struct channel_id *channel_id) * 2. data: * * [`channel_id`:`channel_id`] */ - case WIRE_FUNDING_LOCKED: + case WIRE_CHANNEL_READY: /* BOLT #2: - * 1. type: 36 (`funding_locked`) + * 1. type: 36 (`channel_ready`) * 2. data: * * [`channel_id`:`channel_id`] */ diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index d400da1b8a67..54f48115666a 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -123,12 +123,12 @@ msgdata,funding_created,signature,signature, msgtype,funding_signed,35 msgdata,funding_signed,channel_id,channel_id, msgdata,funding_signed,signature,signature, -msgtype,funding_locked,36 -msgdata,funding_locked,channel_id,channel_id, -msgdata,funding_locked,next_per_commitment_point,point, -msgdata,funding_locked,tlvs,funding_locked_tlvs, -tlvtype,funding_locked_tlvs,alias,1 -tlvdata,funding_locked_tlvs,alias,scid,short_channel_id, +msgtype,channel_ready,36 +msgdata,channel_ready,channel_id,channel_id, +msgdata,channel_ready,second_per_commitment_point,point, +msgdata,channel_ready,tlvs,channel_ready_tlvs, +tlvtype,channel_ready_tlvs,short_channel_id,1 +tlvdata,channel_ready_tlvs,short_channel_id,alias,short_channel_id, msgtype,open_channel2,64 msgdata,open_channel2,chain_hash,chain_hash, msgdata,open_channel2,channel_id,channel_id, diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 1be1c016b93f..d9df608b812b 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -157,10 +157,10 @@ struct msg_channel_update_opt_htlc_max { struct bitcoin_blkid chain_hash; struct short_channel_id short_channel_id; }; -struct msg_funding_locked { +struct msg_channel_ready { struct channel_id channel_id; struct pubkey next_per_commitment_point; - struct tlv_funding_locked_tlvs *tlvs; + struct tlv_channel_ready_tlvs *tlvs; }; struct msg_announcement_signatures { struct channel_id channel_id; @@ -477,20 +477,20 @@ static struct msg_channel_update_opt_htlc_max return tal_free(s); } -static void *towire_struct_funding_locked(const tal_t *ctx, - const struct msg_funding_locked *s) +static void *towire_struct_channel_ready(const tal_t *ctx, + const struct msg_channel_ready *s) { - return towire_funding_locked(ctx, + return towire_channel_ready(ctx, &s->channel_id, &s->next_per_commitment_point, s->tlvs); } -static struct msg_funding_locked *fromwire_struct_funding_locked(const tal_t *ctx, const void *p) +static struct msg_channel_ready *fromwire_struct_channel_ready(const tal_t *ctx, const void *p) { - struct msg_funding_locked *s = tal(ctx, struct msg_funding_locked); + struct msg_channel_ready *s = tal(ctx, struct msg_channel_ready); - if (fromwire_funding_locked(ctx, + if (fromwire_channel_ready(ctx, p, &s->channel_id, &s->next_per_commitment_point, @@ -816,13 +816,13 @@ static bool channel_announcement_eq(const struct msg_channel_announcement *a, && eq_between(a, b, bitcoin_key_1, bitcoin_key_2); } -static bool funding_locked_eq(const struct msg_funding_locked *a, - const struct msg_funding_locked *b) +static bool channel_ready_eq(const struct msg_channel_ready *a, + const struct msg_channel_ready *b) { if (!eq_upto(a, b, tlvs)) return false; - return eq_tlv(a, b, alias, short_channel_id_eq); + return eq_tlv(a, b, short_channel_id, short_channel_id_eq); } static bool announcement_signatures_eq(const struct msg_announcement_signatures *a, @@ -1009,7 +1009,7 @@ static bool node_announcement_eq(const struct msg_node_announcement *a, int main(int argc, char *argv[]) { struct msg_channel_announcement ca, *ca2; - struct msg_funding_locked fl, *fl2; + struct msg_channel_ready fl, *fl2; struct msg_announcement_signatures as, *as2; struct msg_update_fail_htlc ufh, *ufh2; struct msg_commitment_signed cs, *cs2; @@ -1048,15 +1048,15 @@ int main(int argc, char *argv[]) test_corruption(&ca, ca2, channel_announcement); memset(&fl, 2, sizeof(fl)); - fl.tlvs = tlv_funding_locked_tlvs_new(ctx); - fl.tlvs->alias = tal(ctx, struct short_channel_id); - set_scid(fl.tlvs->alias); + fl.tlvs = tlv_channel_ready_tlvs_new(ctx); + fl.tlvs->short_channel_id = tal(ctx, struct short_channel_id); + set_scid(fl.tlvs->short_channel_id); set_pubkey(&fl.next_per_commitment_point); - msg = towire_struct_funding_locked(ctx, &fl); - fl2 = fromwire_struct_funding_locked(ctx, msg); - assert(funding_locked_eq(&fl, fl2)); - test_corruption_tlv(&fl, fl2, funding_locked); + msg = towire_struct_channel_ready(ctx, &fl); + fl2 = fromwire_struct_channel_ready(ctx, msg); + assert(channel_ready_eq(&fl, fl2)); + test_corruption_tlv(&fl, fl2, channel_ready); memset(&as, 2, sizeof(as)); From 5b7f14a7cb2cf4fd097a9169a115efb6575dd48e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:41:31 +0930 Subject: [PATCH 1323/1530] channeld/dualopend/lightningd: use channel_ready everywhere. This alters the billboard, but that's a human-readable thing so not noted in CHANGELOG. Signed-off-by: Rusty Russell Changelog-Changed: JSON-RPC: `listpeers` `status` now refers to "channel ready" rather than "funding locked" (BOLT language change for zeroconf channels) Changelog-Added: JSON-RPC: `channel_opened` notification `channel_ready` flag. Changelog-Deprecated: JSON-RPC: `channel_opened` notification `funding_locked` flag (use `channel_ready`: BOLTs namechange). --- channeld/channeld.c | 2 +- common/billboard.c | 18 +++++++++--------- common/billboard.h | 2 +- doc/PLUGINS.md | 2 +- lightningd/dual_open_control.c | 2 +- lightningd/notification.c | 10 ++++++---- lightningd/notification.h | 2 +- openingd/dualopend.c | 2 +- openingd/dualopend_wire.csv | 4 ++-- tests/test_closing.py | 6 +++--- tests/test_connection.py | 14 +++++++------- tests/test_misc.py | 6 +++--- tests/test_opening.py | 2 +- 13 files changed, 37 insertions(+), 35 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 9777959a6d9b..541f9460cd36 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2952,7 +2952,7 @@ static void peer_reconnect(struct peer *peer, && next_commitment_number == 1) { struct tlv_channel_ready_tlvs *tlvs = tlv_channel_ready_tlvs_new(tmpctx); - status_debug("Retransmitting funding_locked for channel %s", + status_debug("Retransmitting channel_ready for channel %s", type_to_string(tmpctx, struct channel_id, &peer->channel_id)); /* Contains per commit point #1, for first post-opening commit */ msg = towire_channel_ready(NULL, diff --git a/common/billboard.c b/common/billboard.c index 5a1e62542b58..e67efe4a0ff1 100644 --- a/common/billboard.c +++ b/common/billboard.c @@ -4,7 +4,7 @@ #include char *billboard_message(const tal_t *ctx, - const bool funding_locked[NUM_SIDES], + const bool channel_ready[NUM_SIDES], const bool have_sigs[NUM_SIDES], const bool shutdown_sent[NUM_SIDES], u32 depth_togo, @@ -13,17 +13,17 @@ char *billboard_message(const tal_t *ctx, const char *funding_status, *announce_status, *shutdown_status COMPILER_WANTS_INIT("gcc 8.3.0"); - if (funding_locked[LOCAL] && funding_locked[REMOTE]) - funding_status = "Funding transaction locked."; - else if (!funding_locked[LOCAL] && !funding_locked[REMOTE]) + if (channel_ready[LOCAL] && channel_ready[REMOTE]) + funding_status = "Channel ready for use."; + else if (!channel_ready[LOCAL] && !channel_ready[REMOTE]) funding_status = tal_fmt(ctx, "Funding needs %d more" - " confirmations for lockin.", + " confirmations to be ready.", depth_togo); - else if (funding_locked[LOCAL] && !funding_locked[REMOTE]) - funding_status = "We've confirmed funding, they haven't yet."; - else if (!funding_locked[LOCAL] && funding_locked[REMOTE]) - funding_status = "They've confirmed funding, we haven't yet."; + else if (channel_ready[LOCAL] && !channel_ready[REMOTE]) + funding_status = "We've confirmed channel ready, they haven't yet."; + else if (!channel_ready[LOCAL] && channel_ready[REMOTE]) + funding_status = "They've confirmed channel ready, we haven't yet."; if (have_sigs) { if (have_sigs[LOCAL] && have_sigs[REMOTE]) diff --git a/common/billboard.h b/common/billboard.h index c6d8ded8cd3b..38106e5fff54 100644 --- a/common/billboard.h +++ b/common/billboard.h @@ -5,7 +5,7 @@ #include char *billboard_message(const tal_t *ctx, - const bool funding_locked[NUM_SIDES], + const bool channel_ready[NUM_SIDES], const bool have_sigs[NUM_SIDES], const bool shutdown_sent[NUM_SIDES], u32 depth_togo, diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 7751f348abf6..b7e6d17a4cc3 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -441,7 +441,7 @@ if the funding transaction has been included into a block. "id": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f", "funding_msat": 100000000, "funding_txid": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", - "funding_locked": false + "channel_ready": false } } ``` diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index a2f84ee61da0..2a552017c2d3 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1790,7 +1790,7 @@ void dualopen_tell_depth(struct subd *dualopend, } else channel_set_billboard(channel, false, tal_fmt(tmpctx, "Funding needs %d more" - " confirmations for lockin.", + " confirmations to be ready.", to_go)); } diff --git a/lightningd/notification.c b/lightningd/notification.c index 82ad1f616aed..5c0c339817d2 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -201,13 +201,15 @@ static void channel_opened_notification_serialize(struct json_stream *stream, struct node_id *node_id, struct amount_sat *funding_sat, struct bitcoin_txid *funding_txid, - bool funding_locked) + bool channel_ready) { json_object_start(stream, "channel_opened"); json_add_node_id(stream, "id", node_id); json_add_amount_sats_deprecated(stream, "amount", "funding_msat", *funding_sat); json_add_txid(stream, "funding_txid", funding_txid); - json_add_bool(stream, "funding_locked", funding_locked); + if (deprecated_apis) + json_add_bool(stream, "funding_locked", channel_ready); + json_add_bool(stream, "channel_ready", channel_ready); json_object_end(stream); } @@ -216,7 +218,7 @@ REGISTER_NOTIFICATION(channel_opened, void notify_channel_opened(struct lightningd *ld, struct node_id *node_id, struct amount_sat *funding_sat, struct bitcoin_txid *funding_txid, - bool funding_locked) + bool channel_ready) { void (*serialize)(struct json_stream *, struct node_id *, @@ -226,7 +228,7 @@ void notify_channel_opened(struct lightningd *ld, struct node_id *node_id, struct jsonrpc_notification *n = jsonrpc_notification_start(NULL, channel_opened_notification_gen.topic); - serialize(n->stream, node_id, funding_sat, funding_txid, funding_locked); + serialize(n->stream, node_id, funding_sat, funding_txid, channel_ready); jsonrpc_notification_end(n); plugins_notify(ld->plugins, take(n)); } diff --git a/lightningd/notification.h b/lightningd/notification.h index 8eb00c0a3057..ab9bddd607b5 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -47,7 +47,7 @@ void notify_invoice_creation(struct lightningd *ld, struct amount_msat *amount, void notify_channel_opened(struct lightningd *ld, struct node_id *node_id, struct amount_sat *funding_sat, struct bitcoin_txid *funding_txid, - bool funding_locked); + bool channel_ready); void notify_channel_state_changed(struct lightningd *ld, struct node_id *peer_id, diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 0c40c0fdc6aa..03e47911c3d0 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -3620,7 +3620,7 @@ static void do_reconnect_dance(struct state *state) } if (state->channel_ready[LOCAL]) { - status_debug("Retransmitting funding_locked for channel %s", + status_debug("Retransmitting channel_ready for channel %s", type_to_string(tmpctx, struct channel_id, &state->channel_id)); diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 7f49f37fe1ea..45defc1f4ead 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -50,8 +50,8 @@ msgdata,dualopend_reinit,their_basepoints,basepoints, msgdata,dualopend_reinit,remote_per_commit,pubkey, msgdata,dualopend_reinit,funding_psbt,wally_psbt, msgdata,dualopend_reinit,opener,enum side, -msgdata,dualopend_reinit,local_funding_locked,bool, -msgdata,dualopend_reinit,remote_funding_locked,bool, +msgdata,dualopend_reinit,local_channel_ready,bool, +msgdata,dualopend_reinit,remote_channel_ready,bool, msgdata,dualopend_reinit,send_shutdown,bool, msgdata,dualopend_reinit,remote_shutdown_received,bool, msgdata,dualopend_reinit,local_shutdown_len,u16, diff --git a/tests/test_closing.py b/tests/test_closing.py index f278d9c93a19..f635a17c4e84 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -33,9 +33,9 @@ def test_closing_simple(node_factory, bitcoind, chainparams): assert bitcoind.rpc.getmempoolinfo()['size'] == 0 billboard = only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] - assert billboard == ['CHANNELD_NORMAL:Funding transaction locked.'] + assert billboard == ['CHANNELD_NORMAL:Channel ready for use.'] billboard = only_one(l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'])['status'] - assert billboard == ['CHANNELD_NORMAL:Funding transaction locked.'] + assert billboard == ['CHANNELD_NORMAL:Channel ready for use.'] bitcoind.generate_block(5) @@ -44,7 +44,7 @@ def test_closing_simple(node_factory, bitcoind, chainparams): billboard = only_one(l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'])['status'] # This may either be from a local_update or an announce, so just # check for the substring - assert 'CHANNELD_NORMAL:Funding transaction locked.' in billboard[0] + assert 'CHANNELD_NORMAL:Channel ready for use.' in billboard[0] l1.rpc.close(chan) diff --git a/tests/test_connection.py b/tests/test_connection.py index 3f915c8c6fb4..7040d4dcd08c 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -669,9 +669,9 @@ def test_reconnect_gossiping(node_factory): @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_reconnect_no_update(node_factory, executor, bitcoind): - """Test that funding_locked is retransmitted on reconnect if new channel + """Test that channel_ready is retransmitted on reconnect if new channel - This tests if the `funding_locked` is sent if we receive a + This tests if the `channel_ready` is sent if we receive a `channel_reestablish` message with `next_commitment_number` == 1 and our `next_commitment_number` == 1. @@ -689,14 +689,14 @@ def test_reconnect_no_update(node_factory, executor, bitcoind): l1.rpc.connect(l2.info["id"], "localhost", l2.port) # LightningNode.fundchannel will fund the channel and generate a - # block. The block triggers the funding_locked message, which + # block. The block triggers the channel_ready message, which # causes a disconnect. The retransmission is then caused by the # automatic retry. fundchannel_exec = executor.submit(l1.fundchannel, l2, 10**6, False) if l1.config('experimental-dual-fund'): - l1.daemon.wait_for_log(r"dualopend.* Retransmitting funding_locked for channel") + l1.daemon.wait_for_log(r"dualopend.* Retransmitting channel_ready for channel") else: - l1.daemon.wait_for_log(r"channeld.* Retransmitting funding_locked for channel") + l1.daemon.wait_for_log(r"channeld.* Retransmitting channel_ready for channel") sync_blockheight(bitcoind, [l1, l2]) fundchannel_exec.result() l1.stop() @@ -706,7 +706,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"channeld.* Retransmitting funding_locked for channel") + l2.daemon.wait_for_log(r"channeld.* Retransmitting channel_ready for channel") l1.daemon.wait_for_log(r"CLOSINGD_COMPLETE") @@ -913,7 +913,7 @@ def no_blocks_above(req): # l2 will now uses (REMOTE's) announcement_signatures it has stored wait_for(lambda: only_one(l2.rpc.listpeers()['peers'][0]['channels'])['status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', - 'CHANNELD_NORMAL:Funding transaction locked. Channel announced.']) + 'CHANNELD_NORMAL:Channel ready for use. Channel announced.']) # But l2 still sends its own sigs on reconnect l2.daemon.wait_for_logs([r'peer_out WIRE_ANNOUNCEMENT_SIGNATURES', diff --git a/tests/test_misc.py b/tests/test_misc.py index 9e656a0166a3..d505820be3f3 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1108,7 +1108,7 @@ def test_funding_reorg_private(node_factory, bitcoind): daemon = 'DUALOPEND' if l1.config('experimental-dual-fund') else 'CHANNELD' wait_for(lambda: only_one(l1.rpc.listpeers()['peers'][0]['channels'])['status'] - == ['{}_AWAITING_LOCKIN:Funding needs 1 more confirmations for lockin.'.format(daemon)]) + == ['{}_AWAITING_LOCKIN:Funding needs 1 more confirmations to be ready.'.format(daemon)]) bitcoind.generate_block(1) # height 107 l1.wait_channel_active('106x1x0') l2.wait_channel_active('106x1x0') @@ -1166,7 +1166,7 @@ def no_more_blocks(req): wait_for(lambda: only_one(l2.rpc.listpeers()['peers'][0]['channels'])['status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', - 'CHANNELD_NORMAL:Funding transaction locked. They need our announcement signatures.']) + 'CHANNELD_NORMAL:Channel ready for use. They need our announcement signatures.']) # Unblinding l2 brings it back in sync, restarts channeld and sends its announce sig l2.daemon.rpcproxy.mock_rpc('getblockhash', None) @@ -1176,7 +1176,7 @@ def no_more_blocks(req): wait_for(lambda: only_one(l2.rpc.listpeers()['peers'][0]['channels'])['status'] == [ 'CHANNELD_NORMAL:Reconnected, and reestablished.', - 'CHANNELD_NORMAL:Funding transaction locked. Channel announced.']) + 'CHANNELD_NORMAL:Channel ready for use. Channel announced.']) l1.rpc.close(l2.info['id']) bitcoind.generate_block(1, True) diff --git a/tests/test_opening.py b/tests/test_opening.py index 7072e82165d4..207ff508633d 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1275,7 +1275,7 @@ def test_zeroconf_mindepth(bitcoind, node_factory): bitcoind.generate_block(2, wait_for_mempool=1) # Confirm on the l2 side. l2.daemon.wait_for_log(r'peer_out WIRE_CHANNEL_READY') - # l1 should not be sending funding_locked/channel_ready yet, it is + # l1 should not be sending channel_ready yet, it is # configured to wait for 6 confirmations. assert not l1.daemon.is_in_log(r'peer_out WIRE_CHANNEL_READY') From b208c0d8dd4bc6d7b0291183c3e36cd87dac0de8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:42:31 +0930 Subject: [PATCH 1324/1530] doc: upgrade to BOLTs 2ecc091f3484f7a3450e7f5543ae851edd1e0761 I disagree with this change, so I've commented and added a FIXME. Signed-off-by: Rusty Russell --- Makefile | 2 +- common/gossip_constants.h | 2 +- gossipd/gossipd.c | 2 +- gossipd/routing.c | 4 +++- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 3d11b3299ba0..6c8a99fef662 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := bc86304b4b0af5fd5ce9d24f74e2ebbceb7e2730 +DEFAULT_BOLTVERSION := 2ecc091f3484f7a3450e7f5543ae851edd1e0761 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/common/gossip_constants.h b/common/gossip_constants.h index 3ce395eac70e..436db62a4ab6 100644 --- a/common/gossip_constants.h +++ b/common/gossip_constants.h @@ -62,7 +62,7 @@ /* BOLT #7: * * A node: - * - if a channel's oldest `channel_update`s `timestamp` is older than two weeks + * - if a channel's latest `channel_update`s `timestamp` is older than two weeks * (1209600 seconds): * - MAY prune the channel. * - MAY ignore the channel. diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 8c42eec96bc7..84a588775b46 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -606,7 +606,7 @@ static struct io_plan *connectd_req(struct io_conn *conn, /* BOLT #7: * * A node: - * - if a channel's oldest `channel_update`s `timestamp` is older than two weeks + * - if a channel's latest `channel_update`s `timestamp` is older than two weeks * (1209600 seconds): * - MAY prune the channel. * - MAY ignore the channel. diff --git a/gossipd/routing.c b/gossipd/routing.c index 6bdc0020d64f..253fffd06ea6 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1926,10 +1926,12 @@ void route_prune(struct routing_state *rstate) continue; /* BOLT #7: - * - if a channel's oldest `channel_update`s `timestamp` is + * - if a channel's latest `channel_update`s `timestamp` is * older than two weeks (1209600 seconds): * - MAY prune the channel. */ + /* FIXME: I disagree with the above quote: it used to say "oldest", which is what we + use here: */ /* This is a fancy way of saying "both ends must refresh!" */ if (!is_halfchan_defined(&chan->half[0]) || chan->half[0].bcast.timestamp < highwater From 3cc6d0ec2c9cdcce975309382cf5c115c4a9c113 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:43:31 +0930 Subject: [PATCH 1325/1530] doc: upgrade to BOLTs 341ec844f13c0c0abc4fe849059fbb98173f9766 This is a slightly looser behavior, so no change needed. Signed-off-by: Rusty Russell --- Makefile | 2 +- channeld/channeld.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 6c8a99fef662..2bbfe8894132 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := 2ecc091f3484f7a3450e7f5543ae851edd1e0761 +DEFAULT_BOLTVERSION := 341ec844f13c0c0abc4fe849059fbb98173f9766 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/channeld/channeld.c b/channeld/channeld.c index 541f9460cd36..c4f2a744dbb9 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -1254,8 +1254,9 @@ static void send_commit(struct peer *peer) /* BOLT #2: * - * - if no HTLCs remain in either commitment transaction: - * - MUST NOT send any `update` message after a `shutdown`. + * - if no HTLCs remain in either commitment transaction (including dust HTLCs) + * and neither side has a pending `revoke_and_ack` to send: + * - MUST NOT send any `update` message after that point. */ if (peer->shutdown_sent[LOCAL] && !num_channel_htlcs(peer->channel)) { status_debug("Can't send commit: final shutdown phase"); From 4ca1203eb8c5d5d447b1972722c98980e9b9111a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 10 Sep 2022 11:44:31 +0930 Subject: [PATCH 1326/1530] doc: include recent BOLT recommendation on grace period. This includes the recommendation that we use 10 minute grace period, so add quotes to where we use that. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 4 ++++ lightningd/peer_htlcs.c | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 5dfd104f5bbd..648fcf61532f 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -2601,6 +2601,10 @@ static struct command_result *json_setchannelfee(struct command *cmd, &base, cmd->ld->config.fee_base), p_opt_def("ppm", param_number, &ppm, cmd->ld->config.fee_per_satoshi), + /* BOLT #7: + * If it creates a new `channel_update` with updated channel parameters: + * - SHOULD keep accepting the previous channel parameters for 10 minutes + */ p_opt_def("enforcedelay", param_number, &delaysecs, 600), NULL)) return command_param_failed(); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 62a6993ad71f..1333d4be693a 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -720,7 +720,10 @@ static void forward_htlc(struct htlc_in *hin, if (!check_fwd_amount(hin, amt_to_forward, hin->msat, next->feerate_base, next->feerate_ppm)) { - /* Are we in old-fee grace-period? */ + /* BOLT #7: + * - If it creates a new `channel_update` with updated channel parameters: + * - SHOULD keep accepting the previous channel parameters for 10 minutes + */ if (!time_before(time_now(), next->old_feerate_timeout) || !check_fwd_amount(hin, amt_to_forward, hin->msat, next->old_feerate_base, From 2ac775f9f4343338a0782a07d446920582f576b8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 8 Sep 2022 10:26:31 +0930 Subject: [PATCH 1327/1530] lightningd: fix crash with -O3 -flto. It's foolish to ban passing NULL, 0 to memcpy, memset et al, but it's been done. At high level of optimization, GCC assumes this doesn't happen, and yep, assumes "if (ctx)" inside tal_free() must be true. So when a psbt is NULL, and psbt_get_bytes returns NULL, a crash ensues: ``` lightningd: FATAL SIGNAL 6 (version v0.12.0rc2-6-g47efa5d-modded) 0x5557dfc42fef send_backtrace common/daemon.c:33 0x5557dfc42fef crashdump common/daemon.c:46 0x7fe93ef5851f ??? ./signal/../sysdeps/unix/sysv/linux/x86_64/libc_sigaction.c:0 0x7fe93efaca7c __pthread_kill_implementation ./nptl/pthread_kill.c:44 0x7fe93efaca7c __pthread_kill_internal ./nptl/pthread_kill.c:78 0x7fe93efaca7c __GI___pthread_kill ./nptl/pthread_kill.c:89 0x7fe93ef58475 __GI_raise ../sysdeps/posix/raise.c:26 0x7fe93ef3e7f2 __GI_abort ./stdlib/abort.c:79 0x5557dfbb0c28 call_error ccan/ccan/tal/tal.c:93 0x5557dfbb0c34 check_bounds ccan/ccan/tal/tal.c:165 0x5557dfbb0c34 to_tal_hdr ccan/ccan/tal/tal.c:178 0x5557dfc7a1d3 tal_free ccan/ccan/tal/tal.c:482 0x5557dfc609d3 tal_free ccan/ccan/tal/tal.c:477 0x5557dfc609d3 towire_wally_psbt bitcoin/psbt.c:743 0x5557dfbc5dfc towire_dualopend_got_offer_reply openingd/dualopend_wiregen.c:358 0x5557dfbc5dfc openchannel2_hook_cb lightningd/dual_open_control.c:671 0x5557dfc22f4f plugin_hook_callback lightningd/plugin_hook.c:210 0x5557dfc1dfbe plugin_response_handle lightningd/plugin.c:591 0x5557dfc1dfbe plugin_read_json_one lightningd/plugin.c:702 0x5557dfc1dfbe plugin_read_json lightningd/plugin.c:747 0x5557dfc71756 next_plan ccan/ccan/io/io.c:59 0x5557dfc775d5 io_ready ccan/ccan/io/io.c:417 0x5557dfc775d5 io_loop ccan/ccan/io/poll.c:453 0x5557dfbdb1ce io_loop ccan/ccan/io/poll.c:380 0x5557dfbdb1ce io_loop_with_timers lightningd/io_loop_with_timers.c:22 0x5557dfbb37d1 main lightningd/lightningd.c:1195 0x7fe93ef3fd8f __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 0x7fe93ef3fe3f __libc_start_main_impl ../csu/libc-start.c:392 0x5557dfbb6e84 ??? ???:0 0xffffffffffffffff ??? ???:0 ``` Signed-off-by: Rusty Russell --- wire/towire.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wire/towire.c b/wire/towire.c index 6b104dd603d8..c4bfc64478c3 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -11,7 +11,9 @@ void towire(u8 **pptr, const void *data, size_t len) size_t oldsize = tal_count(*pptr); tal_resize(pptr, oldsize + len); - memcpy(*pptr + oldsize, memcheck(data, len), len); + /* The C standards committee has a lot to answer for :( */ + if (len) + memcpy(*pptr + oldsize, memcheck(data, len), len); } void towire_u8(u8 **pptr, u8 v) From 2c3d4e46fcb16e89c48df7ecc54056bc5cdde317 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 8 Sep 2022 10:26:31 +0930 Subject: [PATCH 1328/1530] tools/test: fix very confused code. This broke with COPTFLAGS="-flto -O3", and so I took a look (it complains more than normal because main isn't there). We should never be running update-mocks except on programs expected to compile: in this case, that's tools/test/run-test-wire.c. Remove the code which tries to run this, which also means non-developers won't be running update-mocks! Signed-off-by: Rusty Russell --- tools/test/Makefile | 9 ++---- tools/test/run-test-wire.c | 65 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 7 deletions(-) diff --git a/tools/test/Makefile b/tools/test/Makefile index c39d3ba0a01d..59380b2ebc01 100644 --- a/tools/test/Makefile +++ b/tools/test/Makefile @@ -33,19 +33,14 @@ TOOL_TEST_COMMON_OBJS := \ TOOLS_WIRE_DEPS := $(BOLT_DEPS) tools/test/test_cases $(wildcard tools/gen/*_template) $(TOOL_TEST_OBJS) $(TOOL_GEN_OBJS): $(TOOL_GEN_HEADER) -$(TOOL_TEST_PROGRAMS): $(TOOL_TEST_COMMON_OBJS) $(TOOL_GEN_SRC:.c=.o) tools/test/enum.o +$(TOOL_TEST_PROGRAMS): $(TOOL_TEST_COMMON_OBJS) $(TOOL_GEN_OBJS) tools/test/enum.o tools/test/test_gen.h: $(TOOLS_WIRE_DEPS) tools/test/Makefile $(BOLT_GEN) --page header $@ test_type < tools/test/test_cases > $@ -.INTERMEDIATE: tools/test/test_gen.c.tmp.c -# Parallel make sometimes tries to use file before update-mocks, so split. -tools/test/test_gen.c.tmp.c: $(TOOLS_WIRE_DEPS) +tools/test/test_gen.c: $(TOOLS_WIRE_DEPS) tools/test/Makefile $(BOLT_GEN) --page impl tools/test/test_gen.h test_type < tools/test/test_cases > $@ -tools/test/test_gen.c: tools/test/test_gen.c.tmp.c $(EXTERNAL_HEADERS) tools/test/test_gen.h $(CCAN_HEADERS) - @MAKE=$(MAKE) tools/update-mocks.sh "$<" $(SUPPRESS_OUTPUT) && mv "$<" "$@" - tools/test/print_gen.h: wire/onion$(EXP)_wiregen.h $(TOOLS_WIRE_DEPS) $(BOLT_GEN) -P --page header $@ test_type < tools/test/test_cases > $@ diff --git a/tools/test/run-test-wire.c b/tools/test/run-test-wire.c index 3e447a7eb4e3..a165cd65c26b 100644 --- a/tools/test/run-test-wire.c +++ b/tools/test/run-test-wire.c @@ -7,9 +7,42 @@ /* AUTOGENERATED MOCKS START */ +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } /* Generated stub for fromwire_peektype */ int fromwire_peektype(const u8 *cursor UNNEEDED) { fprintf(stderr, "fromwire_peektype called!\n"); abort(); } +/* Generated stub for fromwire_tlv */ +bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) +{ fprintf(stderr, "fromwire_tlv called!\n"); abort(); } +/* Generated stub for fromwire_tu32 */ +u32 fromwire_tu32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_tu32 called!\n"); abort(); } +/* Generated stub for fromwire_tu64 */ +u64 fromwire_tu64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_tu64 called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } /* Generated stub for printwire_amount_msat */ bool printwire_amount_msat(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "printwire_amount_msat called!\n"); abort(); } @@ -35,6 +68,38 @@ bool printwire_u64(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, s /* Generated stub for printwire_u8_array */ bool printwire_u8_array(const char *fieldname UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "printwire_u8_array called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_tlv */ +void towire_tlv(u8 **pptr UNNEEDED, + const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, + const void *record UNNEEDED) +{ fprintf(stderr, "towire_tlv called!\n"); abort(); } +/* Generated stub for towire_tu32 */ +void towire_tu32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_tu32 called!\n"); abort(); } +/* Generated stub for towire_tu64 */ +void towire_tu64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_tu64 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int main(void) From bef2a47ab7a1acb01582d33db406cf0b6e06b8ac Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 8 Sep 2022 10:26:32 +0930 Subject: [PATCH 1329/1530] db: fix renaming/deleting cols of DBs when there are UNIQUE(x, b, c) constraints. Get stricter with recognizing real column defs. Signed-off-by: Rusty Russell --- db/db_sqlite3.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/db/db_sqlite3.c b/db/db_sqlite3.c index 90f0a4aa28e3..22cc0be20f37 100644 --- a/db/db_sqlite3.c +++ b/db/db_sqlite3.c @@ -446,6 +446,7 @@ static bool colname_to_delete(const char **colnames, return false; } +/* Returns NULL if this doesn't look like a column definition */ static const char *find_column_name(const tal_t *ctx, const char *sqlpart, size_t *after) @@ -455,7 +456,7 @@ static const char *find_column_name(const tal_t *ctx, while (isspace(sqlpart[start])) start++; *after = strspn(sqlpart + start, "abcdefghijklmnopqrstuvwxyz_0123456789") + start; - if (*after == start) + if (*after == start || !cisspace(sqlpart[*after])) return NULL; return tal_strndup(ctx, sqlpart + start, *after - start); } From fb433a70f8b0de0e771de1f3378ee8f3e3bbef58 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 8 Sep 2022 10:38:00 +0930 Subject: [PATCH 1330/1530] doc: escape output types (esp `short_channel_id`). We can also remove the listpeers closer hack, which was removed from the schema already. Signed-off-by: Rusty Russell --- doc/lightning-addgossip.7.md | 2 +- doc/lightning-autocleaninvoice.7.md | 2 +- doc/lightning-bkpr-channelsapy.7.md | 2 +- doc/lightning-bkpr-dumpincomecsv.7.md | 2 +- doc/lightning-bkpr-inspect.7.md | 2 +- doc/lightning-bkpr-listaccountevents.7.md | 2 +- doc/lightning-bkpr-listbalances.7.md | 2 +- doc/lightning-bkpr-listincome.7.md | 2 +- doc/lightning-check.7.md | 2 +- doc/lightning-checkmessage.7.md | 2 +- doc/lightning-close.7.md | 2 +- doc/lightning-commando-rune.7.md | 2 +- doc/lightning-connect.7.md | 2 +- doc/lightning-createinvoice.7.md | 2 +- doc/lightning-createonion.7.md | 2 +- doc/lightning-datastore.7.md | 2 +- doc/lightning-decode.7.md | 4 ++-- doc/lightning-decodepay.7.md | 4 ++-- doc/lightning-deldatastore.7.md | 2 +- doc/lightning-delexpiredinvoice.7.md | 2 +- doc/lightning-delinvoice.7.md | 2 +- doc/lightning-delpay.7.md | 2 +- doc/lightning-disableoffer.7.md | 2 +- doc/lightning-disconnect.7.md | 2 +- doc/lightning-feerates.7.md | 2 +- doc/lightning-fetchinvoice.7.md | 2 +- doc/lightning-fundchannel.7.md | 2 +- doc/lightning-fundchannel_cancel.7.md | 2 +- doc/lightning-fundchannel_complete.7.md | 2 +- doc/lightning-fundchannel_start.7.md | 2 +- doc/lightning-funderupdate.7.md | 2 +- doc/lightning-fundpsbt.7.md | 2 +- doc/lightning-getinfo.7.md | 2 +- doc/lightning-getlog.7.md | 2 +- doc/lightning-getroute.7.md | 4 ++-- doc/lightning-help.7.md | 2 +- doc/lightning-invoice.7.md | 2 +- doc/lightning-keysend.7.md | 2 +- doc/lightning-listchannels.7.md | 4 ++-- doc/lightning-listconfigs.7.md | 2 +- doc/lightning-listdatastore.7.md | 2 +- doc/lightning-listforwards.7.md | 6 +++--- doc/lightning-listfunds.7.md | 6 +++--- doc/lightning-listinvoices.7.md | 2 +- doc/lightning-listnodes.7.md | 2 +- doc/lightning-listoffers.7.md | 2 +- doc/lightning-listpays.7.md | 2 +- doc/lightning-listpeers.7.md | 8 ++++---- doc/lightning-listsendpays.7.md | 2 +- doc/lightning-listtransactions.7.md | 8 ++++---- doc/lightning-makesecret.7.md | 2 +- doc/lightning-multifundchannel.7.md | 2 +- doc/lightning-multiwithdraw.7.md | 2 +- doc/lightning-newaddr.7.md | 2 +- doc/lightning-notifications.7.md | 2 +- doc/lightning-offer.7.md | 2 +- doc/lightning-offerout.7.md | 2 +- doc/lightning-openchannel_abort.7.md | 2 +- doc/lightning-openchannel_bump.7.md | 2 +- doc/lightning-openchannel_init.7.md | 2 +- doc/lightning-openchannel_signed.7.md | 2 +- doc/lightning-openchannel_update.7.md | 2 +- doc/lightning-parsefeerate.7.md | 2 +- doc/lightning-pay.7.md | 2 +- doc/lightning-ping.7.md | 2 +- doc/lightning-plugin.7.md | 2 +- doc/lightning-reserveinputs.7.md | 2 +- doc/lightning-sendcustommsg.7.md | 2 +- doc/lightning-sendinvoice.7.md | 2 +- doc/lightning-sendonion.7.md | 2 +- doc/lightning-sendonionmessage.7.md | 2 +- doc/lightning-sendpay.7.md | 2 +- doc/lightning-sendpsbt.7.md | 2 +- doc/lightning-setchannel.7.md | 4 ++-- doc/lightning-setchannelfee.7.md | 4 ++-- doc/lightning-signmessage.7.md | 2 +- doc/lightning-signpsbt.7.md | 2 +- doc/lightning-stop.7.md | 2 +- doc/lightning-txdiscard.7.md | 2 +- doc/lightning-txprepare.7.md | 2 +- doc/lightning-txsend.7.md | 2 +- doc/lightning-unreserveinputs.7.md | 2 +- doc/lightning-utxopsbt.7.md | 2 +- doc/lightning-waitanyinvoice.7.md | 2 +- doc/lightning-waitblockheight.7.md | 2 +- doc/lightning-waitinvoice.7.md | 2 +- doc/lightning-waitsendpay.7.md | 2 +- doc/lightning-withdraw.7.md | 2 +- tools/fromschema.py | 8 ++------ 89 files changed, 106 insertions(+), 110 deletions(-) diff --git a/doc/lightning-addgossip.7.md b/doc/lightning-addgossip.7.md index 97bcc008f150..72c662975342 100644 --- a/doc/lightning-addgossip.7.md +++ b/doc/lightning-addgossip.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4d9f888d10faca2bf94d1b52510cf21fbeebae4efda0946f03d04b0ef4bc88a2) +[comment]: # ( SHA256STAMP:326e5801f65998e13e909d8b682e9fbc9824f3a43aa7da1d76b871882e52f293) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md index f3d2dfe33878..1aa59ef1cb4b 100644 --- a/doc/lightning-autocleaninvoice.7.md +++ b/doc/lightning-autocleaninvoice.7.md @@ -52,4 +52,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:06bb1ef610c0f82d3d370f468575f2d6e837a11473acd1267baab829aa505052) +[comment]: # ( SHA256STAMP:994e8f17bf35fc704f13206bf4c6909b525f9edcb1c8c4508345c720b007d34c) diff --git a/doc/lightning-bkpr-channelsapy.7.md b/doc/lightning-bkpr-channelsapy.7.md index 4a4b8a10f8ae..80554c9d035e 100644 --- a/doc/lightning-bkpr-channelsapy.7.md +++ b/doc/lightning-bkpr-channelsapy.7.md @@ -65,4 +65,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:296069892023c371e26305e8ab54d04a50aefeeff37a8a465d07893a8c39e61c) +[comment]: # ( SHA256STAMP:8ec833f8261ab8b559f0d645d6da45322b388905413ef262d95f5039d533fdc8) diff --git a/doc/lightning-bkpr-dumpincomecsv.7.md b/doc/lightning-bkpr-dumpincomecsv.7.md index 20980fd2627e..17b3ae4cb670 100644 --- a/doc/lightning-bkpr-dumpincomecsv.7.md +++ b/doc/lightning-bkpr-dumpincomecsv.7.md @@ -57,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4d9326acde55e8124ea61a7e2430f035d3a4957995a76ddef61ecb2a3debd042) +[comment]: # ( SHA256STAMP:1375c000d025b6cb72daa3b2ea64ec3212ae1aa5552c0d87918fd869d2fc5a0b) diff --git a/doc/lightning-bkpr-inspect.7.md b/doc/lightning-bkpr-inspect.7.md index faa45103c688..0fd01cf2b642 100644 --- a/doc/lightning-bkpr-inspect.7.md +++ b/doc/lightning-bkpr-inspect.7.md @@ -52,4 +52,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:022a11cd4bdaec7b1b39e30faaf5489de1db08684a3952b3a3152343e91c8c4c) +[comment]: # ( SHA256STAMP:ea50ea813e46669b522ebd466619ac6f7a4be5ae38b4f976a7db70a3c01b7fae) diff --git a/doc/lightning-bkpr-listaccountevents.7.md b/doc/lightning-bkpr-listaccountevents.7.md index aede7dccd875..e8c3f4030d17 100644 --- a/doc/lightning-bkpr-listaccountevents.7.md +++ b/doc/lightning-bkpr-listaccountevents.7.md @@ -71,4 +71,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3b120f28969d50351823707c11f77a0264031b1ddec07794ac117b0e8e2d7a75) +[comment]: # ( SHA256STAMP:1ac0919bf29ebc37a92283d15a9ffa06f0f46be5fb55920b335d0c43e02a6ee4) diff --git a/doc/lightning-bkpr-listbalances.7.md b/doc/lightning-bkpr-listbalances.7.md index a98b3780c21a..21cad1db129e 100644 --- a/doc/lightning-bkpr-listbalances.7.md +++ b/doc/lightning-bkpr-listbalances.7.md @@ -53,4 +53,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:ae354f9ee2c0f3805923c7c1fbbf490d43c838449fe25758a6884c2f1686d20e) +[comment]: # ( SHA256STAMP:2801e5f237043c6f85d35e2f4a5f69aab5d1cb6a9fcbea9ead1da2daa93265c8) diff --git a/doc/lightning-bkpr-listincome.7.md b/doc/lightning-bkpr-listincome.7.md index d18a784ebf4b..77378193f8d7 100644 --- a/doc/lightning-bkpr-listincome.7.md +++ b/doc/lightning-bkpr-listincome.7.md @@ -57,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:23bcaf41a3451f716cbeedc992c84b88a823d3aa219fd848e3c6c47ae9bbca55) +[comment]: # ( SHA256STAMP:24a4784f87f26283e8849e525d51b376d3e69ae20c0941cfd745a7d07af8032a) diff --git a/doc/lightning-check.7.md b/doc/lightning-check.7.md index d4fa3f1675a2..c859c61cbd67 100644 --- a/doc/lightning-check.7.md +++ b/doc/lightning-check.7.md @@ -41,4 +41,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bd1993b3b95a6c1a9e21873632a829514eb4e9d00ad5084bb099798c8977de06) +[comment]: # ( SHA256STAMP:22c1ad9baf37cb8c7c4b587047d40ef23f0af3d821feaf1aab6d365d724b08fe) diff --git a/doc/lightning-checkmessage.7.md b/doc/lightning-checkmessage.7.md index 853cedec68b4..6d5c3af4a163 100644 --- a/doc/lightning-checkmessage.7.md +++ b/doc/lightning-checkmessage.7.md @@ -50,4 +50,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9642945217c12b5ce41af5aef0547a42b6db5baa97ed88e6fde91345bb9a75f3) +[comment]: # ( SHA256STAMP:28b7c05443a785461a0134e3c2761a2e2d698cb71044f4d895d15ac7f2ee4316) diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 349f6a73bb5b..5735410cb6ab 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -135,4 +135,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:89f0bd65ba94f07afaddac259a766d89e26a9acf646c0491c869d923db3091eb) +[comment]: # ( SHA256STAMP:6a438338ae697732f0100f9e1566b9b8d189778cdb05681305e060487d68663e) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index 5708f883c060..3311b5fb4c53 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -219,4 +219,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:843aaf64ccded63baf949f0103dd0f18b9652f259f1d35e0fd67f45ee58c4e8f) +[comment]: # ( SHA256STAMP:08ded3c93fea629f414a96f12ac02de1000743a487ec8989ba1510a59861ccc1) diff --git a/doc/lightning-connect.7.md b/doc/lightning-connect.7.md index ae5b2fa5104c..ab6752fa303b 100644 --- a/doc/lightning-connect.7.md +++ b/doc/lightning-connect.7.md @@ -104,4 +104,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:27f1e0ad88e7908bbb450ffc1ba5c53855225adf126155cd62ddbd61a599c3ea) +[comment]: # ( SHA256STAMP:581c6243302c8fa5c9234de97e1f6af842bbfee544850c55281924721b46432f) diff --git a/doc/lightning-createinvoice.7.md b/doc/lightning-createinvoice.7.md index b8130b3f8d71..477f971fbafa 100644 --- a/doc/lightning-createinvoice.7.md +++ b/doc/lightning-createinvoice.7.md @@ -75,4 +75,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:1d552c5038b6062cd384b7f143ad139ab5275a2b44be09e6d51d7111109caca2) +[comment]: # ( SHA256STAMP:9fc2c7cb6e5980774a768dd9ddfe81e254c084554c159e6b07e92e703dc10595) diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 9e6768eabee8..75aa660ccd7f 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -133,4 +133,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:719bc9feedb006638b14f3e6cd01aba479620f771868d8d954b34166224f1c8f) +[comment]: # ( SHA256STAMP:c8bd0abd35904cb009b95a7d345be4cc254cff2a427dcf04679a64a6e62dce1e) diff --git a/doc/lightning-datastore.7.md b/doc/lightning-datastore.7.md index 7313b59255d6..81c7409f790b 100644 --- a/doc/lightning-datastore.7.md +++ b/doc/lightning-datastore.7.md @@ -66,4 +66,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f3f48ed1c5c5ccab128c4ce782b2f209896fe0ca6440e00799935dff9bef3f2e) +[comment]: # ( SHA256STAMP:cb5bccd7efd8438c61b909bda419e0300993b2b2267cb335c1f91d12bd402b3e) diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 8a642cebbd45..53e23c4ea9fb 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -163,7 +163,7 @@ If **type** is "bolt11 invoice", and **valid** is *true*: - **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 + - **short\_channel\_id** (short\_channel\_id): a channel to the next peer - **fee\_base\_msat** (msat): 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 @@ -211,4 +211,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7497fbb2f802f2eef2b6c1a8005d31f4778af94c9d0384192a8ba1a4af936549) +[comment]: # ( SHA256STAMP:610b6f9d61e79b503ff81cc164f79ea90883c6d10f5e7b28555aefabfd6e17bb) diff --git a/doc/lightning-decodepay.7.md b/doc/lightning-decodepay.7.md index 25433d5a22c6..04faa4b14a7a 100644 --- a/doc/lightning-decodepay.7.md +++ b/doc/lightning-decodepay.7.md @@ -38,7 +38,7 @@ On success, an object is returned, containing: - **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 + - **short\_channel\_id** (short\_channel\_id): a channel to the next peer - **fee\_base\_msat** (msat): 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 @@ -71,4 +71,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b2a483f6fcb07e4bcddb088520049eee1d33447136e2634bf4207d54c21022e9) +[comment]: # ( SHA256STAMP:c98fd8cac46b5446ff2d01ede6b082f64a83d4b5745d06e410af3e1dd91be8e2) diff --git a/doc/lightning-deldatastore.7.md b/doc/lightning-deldatastore.7.md index e8cfc4513e31..c991b49a11e3 100644 --- a/doc/lightning-deldatastore.7.md +++ b/doc/lightning-deldatastore.7.md @@ -49,4 +49,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:3d70f21bc0945835995b820ab6d65b1ac6e88a9f13240f30acffd32315d41e70) +[comment]: # ( SHA256STAMP:262227d8b4aaee5cad954afa4335b29bceabd787f346d1e5a990614edac7f5e7) diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index 4f3fc644e7c7..c670f543ede0 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -38,4 +38,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c2aef71377094e4c4ab09d3c18b3b8448e5fed1fcc2a6afd47ab218afb011b5e) +[comment]: # ( SHA256STAMP:01526643e128e75057c668cd5dd36e79f075ca847bc692629e1c773b6c940ae6) diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 3bb95d720250..38408b516081 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -81,4 +81,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a869dc2b48e763c47f3af1c27143d194db96948f19fbcec82df343bd3b6c4468) +[comment]: # ( SHA256STAMP:d754daa61ddb65009fced566338af35ffb23069593f4741e6d8f6f138f60bb4f) diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index a3bf29c3057d..47a1dd9af340 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:f687e5248b633be5e6e3bb515b7b006858a3f2499bb5955240b3e18dd80794a8) +[comment]: # ( SHA256STAMP:08dbf42e6d4bd63f9b6e7a45112b348c84d6192d3ff3087e5e02b4a4788e5605) diff --git a/doc/lightning-disableoffer.7.md b/doc/lightning-disableoffer.7.md index 842ec0d24704..2af2c73060b6 100644 --- a/doc/lightning-disableoffer.7.md +++ b/doc/lightning-disableoffer.7.md @@ -75,4 +75,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a2489ae6d986292861555d66a1fb0c8c39efe460db5e324ddbb27b9be1650296) +[comment]: # ( SHA256STAMP:27200ba49d493cbbb1ea84736ccfaeb05a92c69dab34f48cd3d5bbf46ffc2d64) diff --git a/doc/lightning-disconnect.7.md b/doc/lightning-disconnect.7.md index 2a7b4f67964d..68b5f5277277 100644 --- a/doc/lightning-disconnect.7.md +++ b/doc/lightning-disconnect.7.md @@ -59,4 +59,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4d9f888d10faca2bf94d1b52510cf21fbeebae4efda0946f03d04b0ef4bc88a2) +[comment]: # ( SHA256STAMP:326e5801f65998e13e909d8b682e9fbc9824f3a43aa7da1d76b871882e52f293) diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index 0328e8fa49a1..ff3c6ee31bd0 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -121,4 +121,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8b9053a3047bc47e9cb86d8ae059e9b955194d1761d07b33ed7b78c85f434c34) +[comment]: # ( SHA256STAMP:d448abe4c00efb8cb68edf6f8316f130ed45a26223b151ac0647bf5b69aec4fd) diff --git a/doc/lightning-fetchinvoice.7.md b/doc/lightning-fetchinvoice.7.md index dd870179034a..9f219f141ca9 100644 --- a/doc/lightning-fetchinvoice.7.md +++ b/doc/lightning-fetchinvoice.7.md @@ -89,4 +89,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:003592ea0876d08b2109a0daa5e10328e049013f8e44d3eccc5805885a16a375) +[comment]: # ( SHA256STAMP:18164ef676c71c8d3abde89d974b3c74bd7fdb43356a737f937b2fb060795a47) diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index babc38327986..3f9e80566769 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -115,4 +115,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f1c2b762c111ecb6d569075377e1a79cd9fceb60863fc56aeddcdbcd51445812) +[comment]: # ( SHA256STAMP:bca36e910b93b86fc42c2d047e703e9760250757cbf09d8cacdf4e3fe1a1f605) diff --git a/doc/lightning-fundchannel_cancel.7.md b/doc/lightning-fundchannel_cancel.7.md index ab68df060ef8..2a2ce70e596e 100644 --- a/doc/lightning-fundchannel_cancel.7.md +++ b/doc/lightning-fundchannel_cancel.7.md @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6e9501860847adfa98cf725e65297322428986f6345aeb04cd0ece150780ec66) +[comment]: # ( SHA256STAMP:a6b67ae1ecd3bfe5c41e17710342ce32e93675775653bfcffc5b7413c6a15726) diff --git a/doc/lightning-fundchannel_complete.7.md b/doc/lightning-fundchannel_complete.7.md index 248571b904f0..55d74f9716a8 100644 --- a/doc/lightning-fundchannel_complete.7.md +++ b/doc/lightning-fundchannel_complete.7.md @@ -62,4 +62,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b0602c5c3ab5370199776b56656451538712b989e9bfc169934ddabeff56894c) +[comment]: # ( SHA256STAMP:6852cb54595920fa692b6c0a816b44efa7623a3fd12af90602a137f7de0fc57c) diff --git a/doc/lightning-fundchannel_start.7.md b/doc/lightning-fundchannel_start.7.md index c6239df49ac0..66c51000374e 100644 --- a/doc/lightning-fundchannel_start.7.md +++ b/doc/lightning-fundchannel_start.7.md @@ -85,4 +85,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d76e01b4e00915ca84e6960c8718028bc220712a4c387711c01977c3ee80ddf4) +[comment]: # ( SHA256STAMP:b054bc55f69cc1f23f78f342974a8476eab84146bbcf57ab30095e8eba3ed849) diff --git a/doc/lightning-funderupdate.7.md b/doc/lightning-funderupdate.7.md index 4419642fefad..b02300ca4c52 100644 --- a/doc/lightning-funderupdate.7.md +++ b/doc/lightning-funderupdate.7.md @@ -147,4 +147,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c5895e85860542fa6d71d4acf8b1e0d43acaf395331b357064150ee1d52ddcca) +[comment]: # ( SHA256STAMP:03b74bb9d03466181e3f643f718a07388e722e4a6ea1fbb30350e22a7fc491c3) diff --git a/doc/lightning-fundpsbt.7.md b/doc/lightning-fundpsbt.7.md index 3402dcce7f5e..827bbf6fffcf 100644 --- a/doc/lightning-fundpsbt.7.md +++ b/doc/lightning-fundpsbt.7.md @@ -115,4 +115,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7503bff4054024adf29a560f8612e2c504fe1252c71c3a3bb08282808fb299a7) +[comment]: # ( SHA256STAMP:0f290582f49c6103258b7f781a9e7fa4075ec6c05335a459a91da0b6fd58c68d) diff --git a/doc/lightning-getinfo.7.md b/doc/lightning-getinfo.7.md index 0a546473dfcb..99cc81369b36 100644 --- a/doc/lightning-getinfo.7.md +++ b/doc/lightning-getinfo.7.md @@ -131,4 +131,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a517e840e673273a35128e8168d8f58f7795bf0bcfca00f25b859f6e171b08a0) +[comment]: # ( SHA256STAMP:0e54af449933b833f2e74bab9fde46096a79d69b4d958a548c7c0b7cc5654e99) diff --git a/doc/lightning-getlog.7.md b/doc/lightning-getlog.7.md index 130f037f08f8..d4c3942fc06e 100644 --- a/doc/lightning-getlog.7.md +++ b/doc/lightning-getlog.7.md @@ -94,4 +94,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:95995a847dd9f3c2d51494a396761b42051e44bb6376de2e3c3e72634e7fd079) +[comment]: # ( SHA256STAMP:0f6e346c57e59aa8ebe0aee9bcb7ded6f66776752e55c4c125f4a80d98cf90fd) diff --git a/doc/lightning-getroute.7.md b/doc/lightning-getroute.7.md index 4d0dd03ad3a1..128afe20af9d 100644 --- a/doc/lightning-getroute.7.md +++ b/doc/lightning-getroute.7.md @@ -281,7 +281,7 @@ RETURN VALUE 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 +- **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 @@ -310,4 +310,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:7aca069c04bac7139f82387d91e0ea148cc42f375c4cb11232189c874be87dcf) +[comment]: # ( SHA256STAMP:e592a238b3701399c1e8de45cb7186b9714742daefa2f33287019f860c1cc24d) diff --git a/doc/lightning-help.7.md b/doc/lightning-help.7.md index 48f460045abf..bf46d269b220 100644 --- a/doc/lightning-help.7.md +++ b/doc/lightning-help.7.md @@ -68,4 +68,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:a87a95be3154ca98238cea4abdcb0524a831a0f78998b82eed1041da742ffa4c) +[comment]: # ( SHA256STAMP:48f9ef4d1d73aa13ebd1ffa37107111c35c1a197bbcf00f52c5149847ca57ac1) diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index e3350ffc3596..218374d4ad49 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -119,4 +119,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8e72905b6a11d025e5955b9997213d8219e1290cc26307fe517a3e0f19cc818e) +[comment]: # ( SHA256STAMP:4dd2b9d74116f77ad09ad4162ba8438db79e79d1aa99b23e2c993d754327649d) diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index f69e8c103dc3..39215a7e99ea 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -116,4 +116,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:6aca5a797d5e9bca63bbdc478d5ee0ce1dde592d3a4a4247f75d1831c8e6f38a) +[comment]: # ( SHA256STAMP:d208bd6f3e78b039a4790b8de599ffd819aa169c59430ac487fd7030cd3fe640) diff --git a/doc/lightning-listchannels.7.md b/doc/lightning-listchannels.7.md index c23459e26c73..40076e42b3e8 100644 --- a/doc/lightning-listchannels.7.md +++ b/doc/lightning-listchannels.7.md @@ -35,7 +35,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **source** (pubkey): the source node - **destination** (pubkey): the destination node -- **short\_channel\_id** (short_channel_id): short channel id of channel +- **short\_channel\_id** (short\_channel\_id): short channel id of channel - **public** (boolean): true if this is announced (otherwise it must be our channel) - **amount\_msat** (msat): the total capacity of this channel (always a whole number of satoshis) - **message\_flags** (u8): as defined by BOLT #7 @@ -79,4 +79,4 @@ Lightning RFC site - BOLT \#7: -[comment]: # ( SHA256STAMP:2c4d604d1cec7372129bc863b1e056943abb24d6a354c0e9ae76d8fdecfd4953) +[comment]: # ( SHA256STAMP:baf45b77bd2ba22e245e007b57d8e5f70d06cbf9cebf7ed1431da6a0cf6f367a) diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 237a147b70f5..d611e6ff08cf 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -215,4 +215,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:633de58c810a3d0692e4fe7c58265cf4eec1fe85dffc0139a9af1b23936b61e8) +[comment]: # ( SHA256STAMP:14fa07df1432e7104983afc4fba02cc462237bd1a154ba3f3750355d10ffdadb) diff --git a/doc/lightning-listdatastore.7.md b/doc/lightning-listdatastore.7.md index ed71f94bec28..e3c6e0928d8d 100644 --- a/doc/lightning-listdatastore.7.md +++ b/doc/lightning-listdatastore.7.md @@ -47,4 +47,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:45448ccd8947127b2008b4b7d8ab30bb348a45c9ddbced2c690d8b08a102d058) +[comment]: # ( SHA256STAMP:699f7121a6f1aac9ea8afe39f4bac0e696e97754579d544a141fe2a0e404305f) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index b661b045673e..150ba7a238cc 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -24,11 +24,11 @@ RETURN VALUE [comment]: # (GENERATE-FROM-SCHEMA-START) On success, an object containing **forwards** is returned. It is an array of objects, where each object contains: -- **in\_channel** (short_channel_id): the channel that received the HTLC +- **in\_channel** (short\_channel\_id): the channel that received the HTLC - **in\_msat** (msat): the value of the incoming HTLC - **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed") - **received\_time** (number): the UNIX timestamp when this was received -- **out\_channel** (short_channel_id, optional): the channel that the HTLC (trying to) forward to +- **out\_channel** (short\_channel\_id, optional): the channel that the HTLC (trying to) forward to - **payment\_hash** (hex, optional): payment hash sought by HTLC (always 64 characters) - **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") @@ -63,4 +63,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e3c26f73040deb441a572d848eda3d6da924c490e5b9ac55c5d87432f4144646) +[comment]: # ( SHA256STAMP:39c71b957590f6a9b321120e7f337216833efd94f0144560da5cd55c91fee35c) diff --git a/doc/lightning-listfunds.7.md b/doc/lightning-listfunds.7.md index d2596e263251..7c8b31292761 100644 --- a/doc/lightning-listfunds.7.md +++ b/doc/lightning-listfunds.7.md @@ -50,11 +50,11 @@ On success, an object is returned, containing: If **state** is "CHANNELD_NORMAL": - - **short\_channel\_id** (short_channel_id): short channel id of channel + - **short\_channel\_id** (short\_channel\_id): short channel id of channel If **state** is "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN" or "ONCHAIN": - - **short\_channel\_id** (short_channel_id, optional): short channel id of channel (only if funding reached lockin depth before closing) + - **short\_channel\_id** (short\_channel\_id, optional): short channel id of channel (only if funding reached lockin depth before closing) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -73,4 +73,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:319598cb35f3a9c6f336191363b1e38df74b467c0989942b5f4e0a998d0f6339) +[comment]: # ( SHA256STAMP:e5c1f54c8a5008a30648e0fe5883132759fcdabd72bd7e8a00bedc360363e85e) diff --git a/doc/lightning-listinvoices.7.md b/doc/lightning-listinvoices.7.md index df0c33dc19b3..1af9600ce8ce 100644 --- a/doc/lightning-listinvoices.7.md +++ b/doc/lightning-listinvoices.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bfa699310cf025668febb42385b52e6238dfbbc0b5303a28989433d3645495c2) +[comment]: # ( SHA256STAMP:58de6b2fa9e3e796689618bf92a78dac66bb6cfe941d099abd73da3e41bfa60e) diff --git a/doc/lightning-listnodes.7.md b/doc/lightning-listnodes.7.md index f13eed35cee2..63c5bf4af464 100644 --- a/doc/lightning-listnodes.7.md +++ b/doc/lightning-listnodes.7.md @@ -99,4 +99,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:452467d60a45d9d685b054644c94a8b7998d5e9f5dd30474006fe9f4f002eb67) +[comment]: # ( SHA256STAMP:7f1378c1376ade1c9912c8eef3ebc77b13cbc5194ee813f8f1b4e0061338e0bb) diff --git a/doc/lightning-listoffers.7.md b/doc/lightning-listoffers.7.md index 6d1a14a7b848..1852270a2106 100644 --- a/doc/lightning-listoffers.7.md +++ b/doc/lightning-listoffers.7.md @@ -81,4 +81,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:61677f64d10f6ffad32d19f98273925c5f2a1d9cda47bdb5c44cf5a30a34a62d) +[comment]: # ( SHA256STAMP:ac5b79c1f9b76add7eb08b9940180f2200049509df627cccc1dc892efa488778) diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index 6ae899217852..2aa4d07c01ac 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -61,4 +61,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:fd723be0ea9b4d6722adad5d325de6cf9c53e1842281205a82ee74ab702a17bd) +[comment]: # ( SHA256STAMP:64fd1d2a8673b2a4189623aa42d44384061ff66ba7c8918af40baf92ac29a889) diff --git a/doc/lightning-listpeers.7.md b/doc/lightning-listpeers.7.md index 6445cff5de5a..56598bf34da6 100644 --- a/doc/lightning-listpeers.7.md +++ b/doc/lightning-listpeers.7.md @@ -53,7 +53,7 @@ On success, an object containing **peers** is returned. It is an array of objec - **perkw** (u32): Feerate per 1000 weight (i.e kSipa) - **perkb** (u32): Feerate per 1000 virtual bytes - **owner** (string, optional): The current subdaemon controlling this connection - - **short\_channel\_id** (short_channel_id, optional): The short_channel_id (once locked in) + - **short\_channel\_id** (short\_channel\_id, optional): The short_channel_id (once locked in) - **channel\_id** (hash, optional): The full channel_id (always 64 characters) - **funding\_txid** (txid, optional): ID of the funding transaction - **funding\_outnum** (u32, optional): The 0-based output number of the funding transaction which opens the channel @@ -98,8 +98,8 @@ On success, an object containing **peers** is returned. It is an array of objec - **our\_to\_self\_delay** (u32, optional): the number of blocks before we can take our funds if we unilateral close - **max\_accepted\_htlcs** (u32, optional): Maximum number of incoming HTLC we will accept at once - **alias** (object, optional): - - **local** (short_channel_id, optional): An alias assigned by this node to this channel, used for outgoing payments - - **remote** (short_channel_id, optional): An alias assigned by the remote node to this channel, usable in routehints and invoices + - **local** (short\_channel\_id, optional): An alias assigned by this node to this channel, used for outgoing payments + - **remote** (short\_channel\_id, optional): An alias assigned by the remote node to this channel, usable in routehints and invoices - **state\_changes** (array of objects, optional): Prior state changes: - **timestamp** (string): UTC timestamp of form YYYY-mm-ddTHH:MM:SS.%03dZ - **old\_state** (string): Previous state (one of "OPENINGD", "CHANNELD_AWAITING_LOCKIN", "CHANNELD_NORMAL", "CHANNELD_SHUTTING_DOWN", "CLOSINGD_SIGEXCHANGE", "CLOSINGD_COMPLETE", "AWAITING_UNILATERAL", "FUNDING_SPEND_SEEN", "ONCHAIN", "DUALOPEND_OPEN_INIT", "DUALOPEND_AWAITING_LOCKIN") @@ -399,4 +399,4 @@ Main web site: Lightning RFC site (BOLT \#9): -[comment]: # ( SHA256STAMP:e80b24123fdeea4eacda71bb7d824b9234bf17c68239ebf7915d2ab747c58fdc) +[comment]: # ( SHA256STAMP:108f43815e3475b88fd9b6a4a8f868e9d729c5d7616e0b0cc2c14f8922f54955) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index 09d873857177..ae642b43cab6 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:c5d98a40542cb43881bea138bf3da404eba5bc73ed9e0acc8343e0423cc9c602) +[comment]: # ( SHA256STAMP:2cd3f5a0d494ea4aaef6eb5ea9ff467da14198c0fa4d9fee6b45e72568a2d481) diff --git a/doc/lightning-listtransactions.7.md b/doc/lightning-listtransactions.7.md index 7aae61bb6a90..b43e763ff441 100644 --- a/doc/lightning-listtransactions.7.md +++ b/doc/lightning-listtransactions.7.md @@ -38,16 +38,16 @@ On success, an object containing **transactions** is returned. It is an array o - **index** (u32): the output spent - **sequence** (u32): the nSequence value - **type** (string, optional): the purpose of this input (*EXPERIMENTAL_FEATURES* only) (one of "theirs", "deposit", "withdraw", "channel_funding", "channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat") - - **channel** (short_channel_id, optional): the channel this input is associated with (*EXPERIMENTAL_FEATURES* only) + - **channel** (short\_channel\_id, optional): the channel this input is associated with (*EXPERIMENTAL_FEATURES* only) - **outputs** (array of objects): Each output, in order: - **index** (u32): the 0-based output number - **amount\_msat** (msat): the amount of the output - **scriptPubKey** (hex): the scriptPubKey - **type** (string, optional): the purpose of this output (*EXPERIMENTAL_FEATURES* only) (one of "theirs", "deposit", "withdraw", "channel_funding", "channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat") - - **channel** (short_channel_id, optional): the channel this output is associated with (*EXPERIMENTAL_FEATURES* only) + - **channel** (short\_channel\_id, optional): the channel this output is associated with (*EXPERIMENTAL_FEATURES* only) - **type** (array of strings, optional): - Reason we care about this transaction (*EXPERIMENTAL_FEATURES* only) (one of "theirs", "deposit", "withdraw", "channel_funding", "channel_mutual_close", "channel_unilateral_close", "channel_sweep", "channel_htlc_success", "channel_htlc_timeout", "channel_penalty", "channel_unilateral_cheat") -- **channel** (short_channel_id, optional): the channel this transaction is associated with (*EXPERIMENTAL_FEATURES* only) +- **channel** (short\_channel\_id, optional): the channel this transaction is associated with (*EXPERIMENTAL_FEATURES* only) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -105,4 +105,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:e1f4e86db0fd48d695ad0c30ace5dea6f40e1502847a258ef9580e30b52712f7) +[comment]: # ( SHA256STAMP:f7c39908eaa1a2561597c8f97658b873953daab0a68ed2e9b68e434a55d55efe) diff --git a/doc/lightning-makesecret.7.md b/doc/lightning-makesecret.7.md index 283b204cc08f..2a713ded4c2c 100644 --- a/doc/lightning-makesecret.7.md +++ b/doc/lightning-makesecret.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:47f98983bc74e75b5e9ad55ebc84771a7819717e8e41f2398d0e0227a8670044) +[comment]: # ( SHA256STAMP:0ef7c3e2172219fa647d1c447cb82daa7857c6c53a27fd191bff83f59ce6b9f7) diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index 93a8190c67cf..54f827af5326 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -159,4 +159,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:d5af7087b3e31d80df0ce5c09c55b51141bfbf158802d7f5cd82caa21110453e) +[comment]: # ( SHA256STAMP:a507d57bbf36455924497c8354f41e225bc16f63f12fe01b4f7c4af37f0c6960) diff --git a/doc/lightning-multiwithdraw.7.md b/doc/lightning-multiwithdraw.7.md index b5a63c0fa2b2..c090387d74ee 100644 --- a/doc/lightning-multiwithdraw.7.md +++ b/doc/lightning-multiwithdraw.7.md @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:3c79c00a7115ea353d0eb27b8fb6db0203dbd40226291fc0b5f57bc29bd8acc8) +[comment]: # ( SHA256STAMP:6c0054088c17481dedbedb6a5ed4be7f09ce8783780707432907508ebf4bbd7a) diff --git a/doc/lightning-newaddr.7.md b/doc/lightning-newaddr.7.md index 6ac38029ec04..1c47a13b4129 100644 --- a/doc/lightning-newaddr.7.md +++ b/doc/lightning-newaddr.7.md @@ -58,4 +58,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:0d4165582ac09b6f130bd6910873f5073ab7ff852711fa1fb45177726a978899) +[comment]: # ( SHA256STAMP:e9650b5f1f4374007c8fde63dae2ac9981c952ed8074aabade39fcc0ebe21333) diff --git a/doc/lightning-notifications.7.md b/doc/lightning-notifications.7.md index fefa8de8a5b1..3940d12ab91f 100644 --- a/doc/lightning-notifications.7.md +++ b/doc/lightning-notifications.7.md @@ -102,4 +102,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:4d9f888d10faca2bf94d1b52510cf21fbeebae4efda0946f03d04b0ef4bc88a2) +[comment]: # ( SHA256STAMP:326e5801f65998e13e909d8b682e9fbc9824f3a43aa7da1d76b871882e52f293) diff --git a/doc/lightning-offer.7.md b/doc/lightning-offer.7.md index 8818c6f3c9e5..26e689b923ba 100644 --- a/doc/lightning-offer.7.md +++ b/doc/lightning-offer.7.md @@ -135,4 +135,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:62546f8bd62788615d20aa1881fc55328bea3fd3793d4395984972cbf33c7219) +[comment]: # ( SHA256STAMP:aa7544c07d3d84963e43500a367ceb62ebab8f5ae26de1dd39bb087f928dcaee) diff --git a/doc/lightning-offerout.7.md b/doc/lightning-offerout.7.md index 9d632356ec9d..968a0ffa39cf 100644 --- a/doc/lightning-offerout.7.md +++ b/doc/lightning-offerout.7.md @@ -100,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e13d4ef2c90ce11239857da6cab6489ddbaae473b31f2f10d8e5d1cd46e952a3) +[comment]: # ( SHA256STAMP:7c0f75ca64bdcce2467f42d7671caccf5f7bf6eb97fb3edef1e39f2fdb87b4d8) diff --git a/doc/lightning-openchannel_abort.7.md b/doc/lightning-openchannel_abort.7.md index a88ed1045995..fdfd60c74e63 100644 --- a/doc/lightning-openchannel_abort.7.md +++ b/doc/lightning-openchannel_abort.7.md @@ -55,4 +55,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:b02ee781aadf998bf031e8e797622ae9a6307c6c3a0c6d1fdaa3760cbbc6c0c6) +[comment]: # ( SHA256STAMP:ed449af5b443c981faaff360cb2276816bbc7cd80f85fdb4403987c29d65baed) diff --git a/doc/lightning-openchannel_bump.7.md b/doc/lightning-openchannel_bump.7.md index 2e9846de33d7..1af6590d3e70 100644 --- a/doc/lightning-openchannel_bump.7.md +++ b/doc/lightning-openchannel_bump.7.md @@ -81,4 +81,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:40407dc22bff8d157f1f87683e8b97ab6a0495ec1454e5ef05d4a050eb10dfc8) +[comment]: # ( SHA256STAMP:3cba5d1c16925322754eae979e956132e8b94e40da0dee6925037a8854d9b791) diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index 2b9916ce5197..261afb2719ca 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -103,4 +103,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:f78d8a4c015cad1e5065d8ae23375aa33bd9f318cd526ee88f65cccb6cda6ad9) +[comment]: # ( SHA256STAMP:18421f03dece31aafe32cb1a9b520dd6b898e018cb187de6d666e391232fab4e) diff --git a/doc/lightning-openchannel_signed.7.md b/doc/lightning-openchannel_signed.7.md index 9111eab70d96..e62cad7336e3 100644 --- a/doc/lightning-openchannel_signed.7.md +++ b/doc/lightning-openchannel_signed.7.md @@ -67,4 +67,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:7e31569a2b356664ff818c9afc8347aa2dcd1ba128ff9a5f74fda6f441aa5904) +[comment]: # ( SHA256STAMP:41e2a4aed1aaac01675f99e91326197afa370a05e32b2ef20cbbb8247de57289) diff --git a/doc/lightning-openchannel_update.7.md b/doc/lightning-openchannel_update.7.md index f05b35060814..f48cdd1287c2 100644 --- a/doc/lightning-openchannel_update.7.md +++ b/doc/lightning-openchannel_update.7.md @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:b1a1c34c05ff7f47d9ff790eb17d8df4ccfcfabf8471958fec8bf97ddebfc45d) +[comment]: # ( SHA256STAMP:14632f65d4c44b34762d3fa7e0f5b823a519d3dc5fc7a2a69f677000efd937fb) diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md index 139fb704a41b..1d53de74142b 100644 --- a/doc/lightning-parsefeerate.7.md +++ b/doc/lightning-parsefeerate.7.md @@ -44,4 +44,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cf5309537ecb4aa57a6a74e607d1c0d581df22c43aa5d62a90d4eb43e63237f0) +[comment]: # ( SHA256STAMP:62a45d5091e5bdb4581a2986a66681616315999b8497038864ece8e3308c3f50) diff --git a/doc/lightning-pay.7.md b/doc/lightning-pay.7.md index f7f6add17d30..04e03cef7dc7 100644 --- a/doc/lightning-pay.7.md +++ b/doc/lightning-pay.7.md @@ -167,4 +167,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:24e27fab68719a3d6bae0e845cba95223d8c18a4f4c9a045225898cbd6abc2ef) +[comment]: # ( SHA256STAMP:6f7640af4859e4605f4369a4e17fcfbaead1be53928ad8101cc44fde6f441a97) diff --git a/doc/lightning-ping.7.md b/doc/lightning-ping.7.md index 8f03facb2bea..5ed7c9dc6c8b 100644 --- a/doc/lightning-ping.7.md +++ b/doc/lightning-ping.7.md @@ -70,4 +70,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:6c9c92f2387bb0108495d45cf2919203a805bd78db8a2d2a88ada80e881c04e3) +[comment]: # ( SHA256STAMP:f33aa4d93ca623ff7cd5e4062e0533f617b00372797f8ee0d2498479d2fe08a9) diff --git a/doc/lightning-plugin.7.md b/doc/lightning-plugin.7.md index 1579bb5b1398..9da161408587 100644 --- a/doc/lightning-plugin.7.md +++ b/doc/lightning-plugin.7.md @@ -84,4 +84,4 @@ RESOURCES Main web site: [writing plugins]: PLUGINS.md -[comment]: # ( SHA256STAMP:17dc5bb65dc652ab4dee5fda0c3c9a909edc931c357773cbc988aede3d9fb49a) +[comment]: # ( SHA256STAMP:3d7e6647d7fb3eab2a8c6361bb0cbe60efbd822f30f31e08cce68e2aa41ba532) diff --git a/doc/lightning-reserveinputs.7.md b/doc/lightning-reserveinputs.7.md index 98e161d656f2..0225de9c238a 100644 --- a/doc/lightning-reserveinputs.7.md +++ b/doc/lightning-reserveinputs.7.md @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:f0aed27b4a1de3f76f62ba9882df18c596e017491ff04614666ce05bc8bb2535) +[comment]: # ( SHA256STAMP:7fd7e24084f7e7da57bccd98cbcf511be56e44e282813c964bdd69d0785dfd22) diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index 436183d08c11..90966d78af82 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -69,4 +69,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:a4314cf59d13dee507086aa5353a1456238a4e57e4a166bdd0a2fa66a4be3736) +[comment]: # ( SHA256STAMP:5e21c81225dce83b231d8625e0da8e1fedc17a3b76c0109b6e84bae4a4422905) diff --git a/doc/lightning-sendinvoice.7.md b/doc/lightning-sendinvoice.7.md index 37f27df9a6f2..806594556a08 100644 --- a/doc/lightning-sendinvoice.7.md +++ b/doc/lightning-sendinvoice.7.md @@ -80,4 +80,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:adff5e5f321dc5245067f4ed6ad9a404f5f4a783ae5ab2715a9064cbd570f7f1) +[comment]: # ( SHA256STAMP:23d06329b3d5d2d21639ecc152b541788bb204188c24a0294f97582401b2b3dc) diff --git a/doc/lightning-sendonion.7.md b/doc/lightning-sendonion.7.md index 8bc0152eefff..86306b393ba0 100644 --- a/doc/lightning-sendonion.7.md +++ b/doc/lightning-sendonion.7.md @@ -135,4 +135,4 @@ RESOURCES Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:f98e537a7fe2e1b3f9bc10366317c1f1663e0fc6c6618564c38cc10181161658) +[comment]: # ( SHA256STAMP:84283d16d289b6f72ffac0fdca6791bb49ac9ec1ef2bbb06028c18453bb15f02) diff --git a/doc/lightning-sendonionmessage.7.md b/doc/lightning-sendonionmessage.7.md index 1549d25d579a..69d7157726bc 100644 --- a/doc/lightning-sendonionmessage.7.md +++ b/doc/lightning-sendonionmessage.7.md @@ -43,4 +43,4 @@ Main web site: [bolt04]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md -[comment]: # ( SHA256STAMP:8e7f2c4372f12ee7f79df114e9ac9539ad6b19821e6c808e47d1ba9f7981e8ea) +[comment]: # ( SHA256STAMP:200de829c6635242cb2dd8ec0650c2fa8f5fcbf413f4a704884516df80492fcb) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index b834bd391753..fc9a656311ae 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -142,4 +142,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:9b56a1eedebe426c6ed6018fa92f963884b8aea2787d602e1e413c2f79884ce0) +[comment]: # ( SHA256STAMP:4878733d02711f919c49740652a3723fbcc78a0cd865b71385db965c878d2b77) diff --git a/doc/lightning-sendpsbt.7.md b/doc/lightning-sendpsbt.7.md index 2dd0cfe97464..2829e58f9a65 100644 --- a/doc/lightning-sendpsbt.7.md +++ b/doc/lightning-sendpsbt.7.md @@ -66,4 +66,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:3c79c00a7115ea353d0eb27b8fb6db0203dbd40226291fc0b5f57bc29bd8acc8) +[comment]: # ( SHA256STAMP:6c0054088c17481dedbedb6a5ed4be7f09ce8783780707432907508ebf4bbd7a) diff --git a/doc/lightning-setchannel.7.md b/doc/lightning-setchannel.7.md index 98f93ed3c4dd..602cf3f72b15 100644 --- a/doc/lightning-setchannel.7.md +++ b/doc/lightning-setchannel.7.md @@ -74,7 +74,7 @@ On success, an object containing **channels** is returned. It is an array of ob - **fee\_proportional\_millionths** (u32): The resulting feeppm (this is the BOLT #7 name) - **minimum\_htlc\_out\_msat** (msat): The resulting htlcmin we will advertize (the BOLT #7 name is htlc_minimum_msat) - **maximum\_htlc\_out\_msat** (msat): The resulting htlcmax we will advertize (the BOLT #7 name is htlc_maximum_msat) -- **short\_channel\_id** (short_channel_id, optional): the short_channel_id (if locked in) +- **short\_channel\_id** (short\_channel\_id, optional): the short_channel_id (if locked in) - the following warnings are possible: - **warning\_htlcmin\_too\_low**: The requested htlcmin was too low for this peer, so we set it to the minimum they will allow - **warning\_htlcmax\_too\_high**: The requested htlcmax was greater than the channel capacity, so we set it to the channel capacity @@ -107,4 +107,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:28a16d5b1392dd72f9d7a040ce8e37907114fbaf1aed106314fab3f9126afba2) +[comment]: # ( SHA256STAMP:0f7cd751f329360a8cd957dfc8ea0b7d579aa05f4de4f8577039e50266a04f30) diff --git a/doc/lightning-setchannelfee.7.md b/doc/lightning-setchannelfee.7.md index 30c39072d127..f31f0030b8db 100644 --- a/doc/lightning-setchannelfee.7.md +++ b/doc/lightning-setchannelfee.7.md @@ -54,7 +54,7 @@ On success, an object is returned, containing: - **channels** (array of objects): channel(s) whose rate is now set: - **peer\_id** (pubkey): The node_id of the peer - **channel\_id** (hex): The channel_id of the channel (always 64 characters) - - **short\_channel\_id** (short_channel_id, optional): the short_channel_id (if locked in) + - **short\_channel\_id** (short\_channel\_id, optional): the short_channel_id (if locked in) [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -83,4 +83,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bd5af1e2190426012541af0eb582ca22461fb38ec70c7a235f4e7e92f7fc565d) +[comment]: # ( SHA256STAMP:a7f079e9a25ee5f4c3d8bf3ed2c61d2f807eae99e6bfe02b0737a9692aca503b) diff --git a/doc/lightning-signmessage.7.md b/doc/lightning-signmessage.7.md index d94b2c3980be..71f82901602d 100644 --- a/doc/lightning-signmessage.7.md +++ b/doc/lightning-signmessage.7.md @@ -42,4 +42,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cf7372c221ee6846fb8e7aca3e841b687e4b4f21262a0cc8a430b375e17e70cb) +[comment]: # ( SHA256STAMP:6ff35aee05f86de2c44be50a156afc1e325183d8c9333bff68114c8d846a75e6) diff --git a/doc/lightning-signpsbt.7.md b/doc/lightning-signpsbt.7.md index 197a219da81a..2effa067c9f5 100644 --- a/doc/lightning-signpsbt.7.md +++ b/doc/lightning-signpsbt.7.md @@ -72,4 +72,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:79690a411b6db6801192bd462e2cf2c627a0a10e65a326e2801dc0e35e734714) +[comment]: # ( SHA256STAMP:0daef100b12490126849fcb93a9e861554807d1a5acf68bc17de92a30505e18a) diff --git a/doc/lightning-stop.7.md b/doc/lightning-stop.7.md index 198d3eb0bf35..fb6c3c911cbe 100644 --- a/doc/lightning-stop.7.md +++ b/doc/lightning-stop.7.md @@ -42,4 +42,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:cd6076ec79da278cca6f68be0560d71dc1b46651c3db48d81aafe1d19da0ff96) +[comment]: # ( SHA256STAMP:2103952683449a5aa313eefa9c850dc0ae1cf4aa65edeb7897a8748a010a9349) diff --git a/doc/lightning-txdiscard.7.md b/doc/lightning-txdiscard.7.md index 51f3da93bd81..22d5292a31bd 100644 --- a/doc/lightning-txdiscard.7.md +++ b/doc/lightning-txdiscard.7.md @@ -45,4 +45,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d763d6dda590b36227f606a404223327147606b495a20926d14a0f8444effdd7) +[comment]: # ( SHA256STAMP:f52ad03cccaea672deefada22f0a11acff9d0c4f98ccfedca12759eaa6bac057) diff --git a/doc/lightning-txprepare.7.md b/doc/lightning-txprepare.7.md index 7cb6f754c795..e885f8dafff6 100644 --- a/doc/lightning-txprepare.7.md +++ b/doc/lightning-txprepare.7.md @@ -85,4 +85,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d0570b649a356bb7575da21dfe2f6287fe0a0eac388411470fa22897f7175b83) +[comment]: # ( SHA256STAMP:c13561bf71189143811cd4bd49db69c163b8443f1660931671eb1e95e0a7e3ff) diff --git a/doc/lightning-txsend.7.md b/doc/lightning-txsend.7.md index d020c4f5aea6..abba2b31d199 100644 --- a/doc/lightning-txsend.7.md +++ b/doc/lightning-txsend.7.md @@ -45,4 +45,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:8a96f2a5dd68a060602176385238fc7a9255928eef6d5b7f68916eaeb60428f3) +[comment]: # ( SHA256STAMP:dcb4d5f03b44bf3bc6852f97f56c0ac7d34505df71f042ed86a0daf4927dcaff) diff --git a/doc/lightning-unreserveinputs.7.md b/doc/lightning-unreserveinputs.7.md index 82ddb5831083..93f4fe078054 100644 --- a/doc/lightning-unreserveinputs.7.md +++ b/doc/lightning-unreserveinputs.7.md @@ -55,4 +55,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:d5ec7b9dba4387f8f1c59f1d97fe00cc8abcab793c0bb23f3bc32b02b7e2e882) +[comment]: # ( SHA256STAMP:41e0fba4aea57e12d91366a55663e7331e952b223eeb8fc9f83deb5a948f63b4) diff --git a/doc/lightning-utxopsbt.7.md b/doc/lightning-utxopsbt.7.md index 0be62bad7d1c..d2ea02688443 100644 --- a/doc/lightning-utxopsbt.7.md +++ b/doc/lightning-utxopsbt.7.md @@ -100,4 +100,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:0fa67418b5d62dc8dd0ccdbda4113da73a3d7761e23c2ad697a533783c43c37d) +[comment]: # ( SHA256STAMP:c2c513b40099c9cd2ef7bda1c430fdff055499b67ef2ff9edf7772ea4d87fb2d) diff --git a/doc/lightning-waitanyinvoice.7.md b/doc/lightning-waitanyinvoice.7.md index cc6859b7b590..a2f3c8f9d90d 100644 --- a/doc/lightning-waitanyinvoice.7.md +++ b/doc/lightning-waitanyinvoice.7.md @@ -75,4 +75,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:016afebade3ee5a7ac5abbd125f8db78f6c8b41f0e510c8f4c3b6a385e6f3a26) +[comment]: # ( SHA256STAMP:2b0c9e70bb03f5cf9999731fdf5b8bcd761ea70ef6fc04575a1c2451174ea769) diff --git a/doc/lightning-waitblockheight.7.md b/doc/lightning-waitblockheight.7.md index f773a5178365..801bfb8d3d8c 100644 --- a/doc/lightning-waitblockheight.7.md +++ b/doc/lightning-waitblockheight.7.md @@ -39,4 +39,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4419a83c7852353e07eaa8ac3e6786c6b1d714a9a3d981fc78adfe4a73008514) +[comment]: # ( SHA256STAMP:e84e2ddf33c5abafe434ad0dcd76a3c1e6e2a2bdbba5dcf786f2a2ed80e61061) diff --git a/doc/lightning-waitinvoice.7.md b/doc/lightning-waitinvoice.7.md index 94207e966d5d..6000f5063dce 100644 --- a/doc/lightning-waitinvoice.7.md +++ b/doc/lightning-waitinvoice.7.md @@ -60,4 +60,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:016afebade3ee5a7ac5abbd125f8db78f6c8b41f0e510c8f4c3b6a385e6f3a26) +[comment]: # ( SHA256STAMP:2b0c9e70bb03f5cf9999731fdf5b8bcd761ea70ef6fc04575a1c2451174ea769) diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index 0cd670e3b455..0ec7abca750b 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -103,4 +103,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:30601f9dab9b0168e7db387cdd9d6750116f9adad8f125f0bac507a79f9bcf67) +[comment]: # ( SHA256STAMP:b87ddb42fd2b1182ef11101f0298be2e4de9b942fd3b8b4b169d1bdc849f48a8) diff --git a/doc/lightning-withdraw.7.md b/doc/lightning-withdraw.7.md index 74301e97111c..6a52452e78fa 100644 --- a/doc/lightning-withdraw.7.md +++ b/doc/lightning-withdraw.7.md @@ -74,4 +74,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:bbb1e5637721b3415ff3498d141f9da7d38bd75b297f5c0d262e838314dd2f37) +[comment]: # ( SHA256STAMP:fcfd3c91a3cee9bbd36e86edccb5d6407b19c2beda7de1f51ebba5fbd1c2340a) diff --git a/tools/fromschema.py b/tools/fromschema.py index b5cbc91000b3..e6c4b9c6d70d 100755 --- a/tools/fromschema.py +++ b/tools/fromschema.py @@ -30,13 +30,9 @@ def output(line): def output_type(properties, is_optional): - # FIXME: there's a horrible hack for listpeers' closer which can be NULL - if type(properties['type']) is list: - typename = properties['type'][0] - else: - typename = properties['type'] + typename = properties['type'].replace('_', '\\_') if typename == 'array': - typename += ' of {}s'.format(properties['items']['type']) + typename += ' of {}s'.format(properties['items']['type'].replace('_', '\\_')) if is_optional: typename += ", optional" output(" ({})".format(typename)) From b3b5b740694f28c6daf4846d26ee9e1967ba3596 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 8 Sep 2022 10:39:13 +0930 Subject: [PATCH 1331/1530] libplugin: allow NULL calllbacks for jsonrpc_set_datastore. You often don't care about the reply, so this is quite convenient. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 15 +++++++++++++++ plugins/libplugin.h | 4 +++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 87bafe965e90..d9182c281948 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -617,6 +617,16 @@ bool rpc_scan_datastore_hex(struct plugin *plugin, return ret; } +static struct command_result *datastore_fail(struct command *command, + const char *buf, + const jsmntok_t *result, + void *unused) +{ + plugin_err(command->plugin, "datastore failed: %.*s", + json_tok_full_len(result), + json_tok_full(buf, result)); +} + struct command_result *jsonrpc_set_datastore_(struct plugin *plugin, struct command *cmd, const char *path, @@ -635,6 +645,11 @@ struct command_result *jsonrpc_set_datastore_(struct plugin *plugin, { struct out_req *req; + if (!cb) + cb = ignore_cb; + if (!errcb) + errcb = datastore_fail; + req = jsonrpc_request_start(plugin, cmd, "datastore", cb, errcb, arg); json_add_keypath(req->js->jout, "key", path); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index d0e9336f5f16..c90a680c7d10 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -161,7 +161,9 @@ struct json_stream *jsonrpc_stream_fail_data(struct command *cmd, int code, const char *err); -/* Helper to jsonrpc_request_start() and send_outreq() to update datastore. */ +/* Helper to jsonrpc_request_start() and send_outreq() to update datastore. + * NULL cb means ignore, NULL errcb means plugin_error. +*/ struct command_result *jsonrpc_set_datastore_(struct plugin *plugin, struct command *cmd, const char *path, From 6438ee41267bfb7f7adb1f9fd938ee744dfb3840 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 8 Sep 2022 11:31:11 +0930 Subject: [PATCH 1332/1530] doc: disallow additional properties in sendcustommsg. We have to scatter this everywhere in our schemas, as there's no way to make it the default :( Signed-off-by: Rusty Russell --- doc/lightning-sendcustommsg.7.md | 2 +- doc/schemas/sendcustommsg.schema.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/lightning-sendcustommsg.7.md b/doc/lightning-sendcustommsg.7.md index 90966d78af82..10e15a604e13 100644 --- a/doc/lightning-sendcustommsg.7.md +++ b/doc/lightning-sendcustommsg.7.md @@ -69,4 +69,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5e21c81225dce83b231d8625e0da8e1fedc17a3b76c0109b6e84bae4a4422905) +[comment]: # ( SHA256STAMP:fded86dbe217eacf0c170db87140fd5f10f23c22760ac08b7aa4d2faa4764b3a) diff --git a/doc/schemas/sendcustommsg.schema.json b/doc/schemas/sendcustommsg.schema.json index cc61e6190979..3c9db46a3ceb 100644 --- a/doc/schemas/sendcustommsg.schema.json +++ b/doc/schemas/sendcustommsg.schema.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", + "additionalProperties": false, "required": [ "status" ], From d7aa2749c3f2d596a33f8e86de8d276410b4aa9b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 9 Sep 2022 17:02:15 +0930 Subject: [PATCH 1333/1530] db: fix migrations which write to db. valgrind noticed that this was uninitialized when I tried a complex migration. Signed-off-by: Rusty Russell --- db/exec.c | 7 +++++-- wallet/db.c | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/db/exec.c b/db/exec.c index 13341c1e7d64..96ef3855610e 100644 --- a/db/exec.c +++ b/db/exec.c @@ -45,8 +45,11 @@ u32 db_data_version_get(struct db *db) u32 version; stmt = db_prepare_v2(db, SQL("SELECT intval FROM vars WHERE name = 'data_version'")); db_query_prepared(stmt); - db_step(stmt); - version = db_col_int(stmt, "intval"); + /* This fails on uninitialized db, so "0" */ + if (db_step(stmt)) + version = db_col_int(stmt, "intval"); + else + version = 0; tal_free(stmt); return version; } diff --git a/wallet/db.c b/wallet/db.c index 41010d84d3cb..a0fd0e8da786 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -950,10 +950,10 @@ struct db *db_setup(const tal_t *ctx, struct lightningd *ld, db->report_changes_fn = plugin_hook_db_sync; db_begin_transaction(db); + db->data_version = db_data_version_get(db); migrated = db_migrate(ld, db, bip32_base); - db->data_version = db_data_version_get(db); db_commit_transaction(db); /* This needs to be done outside a transaction, apparently. From e853cdc3ff3732629c912eb5b4ab880944d3ccff Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 11 Sep 2022 15:21:31 +0930 Subject: [PATCH 1334/1530] db: fix sqlite3 code which manipulates columns. Because it used internal routines, it didn't pass operations through the db hook! So make it use the generic routines, with the twist that they are not translated. And when we use this in a migration hook, we're actually in a transaction. This, in turn, introduces an issue: we need to be outside a transaction to "PRAGMA foreign_keys = OFF", but completing the transaction when there is a db hook actually enters the io loop, freeing the tmpctx! Signed-off-by: Rusty Russell --- db/db_postgres.c | 21 ++-------- db/db_sqlite3.c | 94 ++++++++++++++++++-------------------------- db/utils.c | 70 +++++++++++++++++++++------------ db/utils.h | 8 ++++ wallet/test/run-db.c | 15 ++++--- 5 files changed, 105 insertions(+), 103 deletions(-) diff --git a/db/db_postgres.c b/db/db_postgres.c index 7f6f143bc750..19ff4d52ccc8 100644 --- a/db/db_postgres.c +++ b/db/db_postgres.c @@ -277,19 +277,11 @@ static bool db_postgres_rename_column(struct db *db, const char *tablename, const char *from, const char *to) { - PGresult *res; char *cmd; cmd = tal_fmt(db, "ALTER TABLE %s RENAME %s TO %s;", tablename, from, to); - res = PQexec(db->conn, cmd); - if (PQresultStatus(res) != PGRES_COMMAND_OK) { - db->error = tal_fmt(db, "Rename '%s' failed: %s", - cmd, PQerrorMessage(db->conn)); - PQclear(res); - return false; - } - PQclear(res); + db_exec_prepared_v2(take(db_prepare_untranslated(db, cmd))); return true; } @@ -297,7 +289,6 @@ static bool db_postgres_delete_columns(struct db *db, const char *tablename, const char **colnames, size_t num_cols) { - PGresult *res; char *cmd; cmd = tal_fmt(db, "ALTER TABLE %s ", tablename); @@ -307,14 +298,8 @@ static bool db_postgres_delete_columns(struct db *db, tal_append_fmt(&cmd, "DROP %s", colnames[i]); } tal_append_fmt(&cmd, ";"); - res = PQexec(db->conn, cmd); - if (PQresultStatus(res) != PGRES_COMMAND_OK) { - db->error = tal_fmt(db, "Delete '%s' failed: %s", - cmd, PQerrorMessage(db->conn)); - PQclear(res); - return false; - } - PQclear(res); + + db_exec_prepared_v2(take(db_prepare_untranslated(db, cmd))); return true; } diff --git a/db/db_sqlite3.c b/db/db_sqlite3.c index 22cc0be20f37..26cbe2e6af07 100644 --- a/db/db_sqlite3.c +++ b/db/db_sqlite3.c @@ -1,7 +1,9 @@ #include "config.h" #include #include +#include #include +#include #include #if HAVE_SQLITE3 @@ -461,7 +463,8 @@ static const char *find_column_name(const tal_t *ctx, return tal_strndup(ctx, sqlpart + start, *after - start); } -/* Move table out the way, return columns */ +/* Move table out the way, return columns. + * Note: with db_hook, frees tmpctx! */ static char **prepare_table_manip(const tal_t *ctx, struct db *db, const char *tablename) { @@ -488,39 +491,35 @@ static char **prepare_table_manip(const tal_t *ctx, sql = tal_strdup(tmpctx, (const char *)sqlite3_column_text(stmt, 0)); sqlite3_finalize(stmt); + /* We MUST use generic routines to write to db, since they + * mirror changes to the db hook! */ bracket = strchr(sql, '('); - if (!strstarts(sql, "CREATE TABLE") || !bracket) { - db->error = tal_fmt(db, "strange schema for %s: %s", - tablename, sql); - return NULL; - } + if (!strstarts(sql, "CREATE TABLE") || !bracket) + db_fatal("Bad sql from prepare_table_manip %s: %s", + tablename, sql); /* Split after ( by commas: any lower case is assumed to be a field */ parts = tal_strsplit(ctx, bracket + 1, ",", STR_EMPTY_OK); + /* Now, we actually need to turn OFF transactions for a moment, as + * this pragma only has an effect outside a tx! */ + db_commit_transaction(db); + + /* But core insists we're "in a transaction" for all ops, so fake it */ + db->in_transaction = "Not really"; /* Turn off foreign keys first. */ - sqlite3_prepare_v2(wrapper->conn, "PRAGMA foreign_keys = OFF;", -1, &stmt, NULL); - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite_stmt_err; - sqlite3_finalize(stmt); + db_prepare_for_changes(db); + db_exec_prepared_v2(take(db_prepare_untranslated(db, + "PRAGMA foreign_keys = OFF;"))); + db_report_changes(db, NULL, 0); + db->in_transaction = NULL; + db_begin_transaction(db); cmd = tal_fmt(tmpctx, "ALTER TABLE %s RENAME TO temp_%s;", tablename, tablename); - sqlite3_prepare_v2(wrapper->conn, cmd, -1, &stmt, NULL); - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite_stmt_err; - sqlite3_finalize(stmt); - - /* Make sure we do the same to backup! */ - replicate_statement(wrapper, "PRAGMA foreign_keys = OFF;"); - replicate_statement(wrapper, cmd); + db_exec_prepared_v2(take(db_prepare_untranslated(db, cmd))); return parts; - -sqlite_stmt_err: - db->error = tal_fmt(db, "%s", sqlite3_errmsg(wrapper->conn)); - sqlite3_finalize(stmt); - return tal_free(parts); } static bool complete_table_manip(struct db *db, @@ -528,9 +527,7 @@ static bool complete_table_manip(struct db *db, const char **coldefs, const char **oldcolnames) { - sqlite3_stmt *stmt; char *create_cmd, *insert_cmd, *drop_cmd; - struct db_sqlite3 *wrapper = (struct db_sqlite3 *)db->conn; /* Create table */ create_cmd = tal_fmt(tmpctx, "CREATE TABLE %s (", tablename); @@ -541,13 +538,7 @@ static bool complete_table_manip(struct db *db, } tal_append_fmt(&create_cmd, ";"); - sqlite3_prepare_v2(wrapper->conn, create_cmd, -1, &stmt, NULL); - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite_stmt_err; - sqlite3_finalize(stmt); - - /* Make sure we do the same to backup! */ - replicate_statement(wrapper, create_cmd); + db_exec_prepared_v2(take(db_prepare_untranslated(db, create_cmd))); /* Populate table from old one */ insert_cmd = tal_fmt(tmpctx, "INSERT INTO %s SELECT ", tablename); @@ -558,33 +549,24 @@ static bool complete_table_manip(struct db *db, } tal_append_fmt(&insert_cmd, " FROM temp_%s;", tablename); - sqlite3_prepare_v2(wrapper->conn, insert_cmd, -1, &stmt, NULL); - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite_stmt_err; - sqlite3_finalize(stmt); - replicate_statement(wrapper, insert_cmd); + db_exec_prepared_v2(take(db_prepare_untranslated(db, insert_cmd))); /* Cleanup temp table */ drop_cmd = tal_fmt(tmpctx, "DROP TABLE temp_%s;", tablename); - sqlite3_prepare_v2(wrapper->conn, drop_cmd, -1, &stmt, NULL); - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite_stmt_err; - sqlite3_finalize(stmt); - replicate_statement(wrapper, drop_cmd); + db_exec_prepared_v2(take(db_prepare_untranslated(db, drop_cmd))); + db_commit_transaction(db); /* Allow links between them (esp. cascade deletes!) */ - sqlite3_prepare_v2(wrapper->conn, "PRAGMA foreign_keys = ON;", -1, &stmt, NULL); - if (sqlite3_step(stmt) != SQLITE_DONE) - goto sqlite_stmt_err; - sqlite3_finalize(stmt); - replicate_statement(wrapper, "PRAGMA foreign_keys = ON;"); - + db->in_transaction = "Not really"; + db_prepare_for_changes(db); + db_exec_prepared_v2(take(db_prepare_untranslated(db, + "PRAGMA foreign_keys = ON;"))); + db_report_changes(db, NULL, 0); + db->in_transaction = NULL; + + /* migrations are performed inside transactions, so start one. */ + db_begin_transaction(db); return true; - -sqlite_stmt_err: - db->error = tal_fmt(db, "%s", sqlite3_errmsg(wrapper->conn)); - sqlite3_finalize(stmt); - return false; } static bool db_sqlite3_rename_column(struct db *db, @@ -595,10 +577,11 @@ static bool db_sqlite3_rename_column(struct db *db, const char **coldefs, **oldcolnames; bool colname_found = false; - parts = prepare_table_manip(tmpctx, db, tablename); + parts = prepare_table_manip(NULL, db, tablename); if (!parts) return false; + tal_steal(tmpctx, parts); coldefs = tal_arr(tmpctx, const char *, 0); oldcolnames = tal_arr(tmpctx, const char *, 0); @@ -643,10 +626,11 @@ static bool db_sqlite3_delete_columns(struct db *db, const char **coldefs, **oldcolnames; size_t colnames_found = 0; - parts = prepare_table_manip(tmpctx, db, tablename); + parts = prepare_table_manip(NULL, db, tablename); if (!parts) return false; + tal_steal(tmpctx, parts); coldefs = tal_arr(tmpctx, const char *, 0); oldcolnames = tal_arr(tmpctx, const char *, 0); diff --git a/db/utils.c b/db/utils.c index 96f442fbbcec..106aae834905 100644 --- a/db/utils.c +++ b/db/utils.c @@ -61,11 +61,38 @@ static void db_stmt_free(struct db_stmt *stmt) } +static struct db_stmt *db_prepare_core(struct db *db, + const char *location, + const struct db_query *db_query) +{ + struct db_stmt *stmt = tal(db, struct db_stmt); + size_t num_slots = db_query->placeholders; + + /* Allocate the slots for placeholders/bindings, zeroed next since + * that sets the type to DB_BINDING_UNINITIALIZED for later checks. */ + stmt->bindings = tal_arrz(stmt, struct db_binding, num_slots); + stmt->location = location; + stmt->error = NULL; + stmt->db = db; + stmt->query = db_query; + stmt->executed = false; + stmt->inner_stmt = NULL; + + tal_add_destructor(stmt, db_stmt_free); + + list_add(&db->pending_statements, &stmt->list); + +#if DEVELOPER + stmt->cols_used = NULL; +#endif /* DEVELOPER */ + + return stmt; +} + struct db_stmt *db_prepare_v2_(const char *location, struct db *db, const char *query_id) { - struct db_stmt *stmt = tal(db, struct db_stmt); - size_t num_slots, pos; + size_t pos; /* Normalize query_id paths, because unit tests are compiled with this * prefix. */ @@ -81,40 +108,33 @@ struct db_stmt *db_prepare_v2_(const char *location, struct db *db, for (;;) { if (!db->queries->query_table[pos].name) db_fatal("Could not resolve query %s", query_id); - if (streq(query_id, db->queries->query_table[pos].name)) { - stmt->query = &db->queries->query_table[pos]; + if (streq(query_id, db->queries->query_table[pos].name)) break; - } pos = (pos + 1) % db->queries->query_table_size; } - num_slots = stmt->query->placeholders; - /* Allocate the slots for placeholders/bindings, zeroed next since - * that sets the type to DB_BINDING_UNINITIALIZED for later checks. */ - stmt->bindings = tal_arr(stmt, struct db_binding, num_slots); - for (size_t i=0; ibindings[i].type = DB_BINDING_UNINITIALIZED; - - stmt->location = location; - stmt->error = NULL; - stmt->db = db; - stmt->executed = false; - stmt->inner_stmt = NULL; + return db_prepare_core(db, location, &db->queries->query_table[pos]); +} - tal_add_destructor(stmt, db_stmt_free); +/* Provides replication and hook interface for raw SQL too */ +struct db_stmt *db_prepare_untranslated(struct db *db, const char *query) +{ + struct db_query *db_query = tal(NULL, struct db_query); + struct db_stmt *stmt; - list_add(&db->pending_statements, &stmt->list); + db_query->name = db_query->query = query; + db_query->placeholders = strcount(query, "?"); + db_query->readonly = false; -#if DEVELOPER - stmt->cols_used = NULL; -#endif /* DEVELOPER */ + /* Use raw accessors! */ + db_query->colnames = NULL; + db_query->num_colnames = 0; + stmt = db_prepare_core(db, "db_prepare_untranslated", db_query); + tal_steal(stmt, db_query); return stmt; } -#define db_prepare_v2(db,query) \ - db_prepare_v2_(__FILE__ ":" stringify(__LINE__), db, query) - bool db_query_prepared(struct db_stmt *stmt) { /* Make sure we don't accidentally execute a modifying query using a diff --git a/db/utils.h b/db/utils.h index 308a302f9091..e0c1f97f337d 100644 --- a/db/utils.h +++ b/db/utils.h @@ -97,4 +97,12 @@ void db_assert_no_outstanding_statements(struct db *db); */ const char **db_changes(struct db *db); +/** + * Accessor for internal use. + * + * Like db_prepare_v2() but creates temporary noop translation, and + * assumes not a read-only op. Use this inside db-specific backends + * to re-use the normal db hook and replication logic. + */ +struct db_stmt *db_prepare_untranslated(struct db *db, const char *query); #endif /* LIGHTNING_DB_UTILS_H */ diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 054ddc31d3b3..5059d7ae1923 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -201,17 +201,22 @@ static bool test_manip_columns(void) CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); CHECK_MSG(!db_err, "Simple correct SQL command"); tal_free(stmt); - /* Don't let it try to set a version field (we don't have one!) */ - db->dirty = false; - db->changes = tal_arr(db, const char *, 0); - db_commit_transaction(db); + + /* Needs vars table, since this changes db. */ + stmt = db_prepare_v2(db, SQL("CREATE TABLE vars (name VARCHAR(32), intval);")); + CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + tal_free(stmt); + stmt = db_prepare_v2(db, SQL("INSERT INTO vars VALUES ('data_version', 0);")); + CHECK_MSG(db_exec_prepared_v2(stmt), "db_exec_prepared must succeed"); + CHECK_MSG(!db_err, "Simple correct SQL command"); + tal_free(stmt); /* Rename tablea.field1 -> table1.field1a. */ CHECK(db->config->rename_column(db, "tablea", "field1", "field1a")); /* Remove tableb.field1. */ CHECK(db->config->delete_columns(db, "tableb", &field1, 1)); - db_begin_transaction(db); stmt = db_prepare_v2(db, SQL("SELECT id, field1a FROM tablea;")); CHECK_MSG(db_query_prepared(stmt), "db_query_prepared must succeed"); CHECK_MSG(!db_err, "Simple correct SQL command"); From 375215a141f60ed2d458cd8606933098de397152 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 11 Sep 2022 17:21:18 +0930 Subject: [PATCH 1335/1530] lightningd: more graceful shutdown. Be more graceful in shutting down: this should fix the issue where bookkeeper gets upset that its commands are rejected during shutdown, and generally make things more graceful. 1. Stop any new RPC connections. 2. Stop any per-peer daemons (channeld, etc). 3. Shut down plugins. 4. Stop all existing RPC connections. 5. Stop global daemons. 6. Free up peer, chanen HTLC datastructures. 7. Close database. Signed-off-by: Rusty Russell Changelog-Changed: Plugins: RPC operations are now still available during shutdown. --- lightningd/gossip_control.c | 6 ++-- lightningd/jsonrpc.c | 22 ++++++++---- lightningd/jsonrpc.h | 14 +++++++- lightningd/lightningd.c | 51 +++++++++++++++++---------- lightningd/plugin.c | 8 ----- lightningd/plugin_hook.c | 3 +- lightningd/subd.c | 12 +++---- lightningd/subd.h | 9 ++--- lightningd/test/run-find_my_abspath.c | 6 ++++ tests/test_closing.py | 2 +- tests/test_plugin.py | 2 +- 11 files changed, 81 insertions(+), 54 deletions(-) diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index a240e11accdc..5fcd4f798d79 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -125,9 +125,9 @@ static void handle_local_channel_update(struct lightningd *ld, const u8 *msg) * us. */ channel = any_channel_by_scid(ld, &scid, true); if (!channel) { - log_broken(ld->log, "Local update for bad scid %s", - type_to_string(tmpctx, struct short_channel_id, - &scid)); + log_unusual(ld->log, "Local update for bad scid %s", + type_to_string(tmpctx, struct short_channel_id, + &scid)); return; } diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 3b758ddcf22a..14378139fce5 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -922,11 +922,6 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) json_tok_full(jcon->buffer, method)); } - if (jcon->ld->state == LD_STATE_SHUTDOWN) { - return command_fail(c, LIGHTNINGD_SHUTDOWN, - "lightningd is shutting down"); - } - rpc_hook = tal(c, struct rpc_command_hook_payload); rpc_hook->cmd = c; /* Duplicate since we might outlive the connection */ @@ -1257,8 +1252,21 @@ void jsonrpc_listen(struct jsonrpc *jsonrpc, struct lightningd *ld) if (listen(fd, 128) != 0) err(1, "Listening on '%s'", rpc_filename); - jsonrpc->rpc_listener = io_new_listener( - ld->rpc_filename, fd, incoming_jcon_connected, ld); + + /* All conns will be tal children of jsonrpc: good for freeing later! */ + jsonrpc->rpc_listener + = io_new_listener(jsonrpc, fd, incoming_jcon_connected, ld); +} + +void jsonrpc_stop_listening(struct jsonrpc *jsonrpc) +{ + jsonrpc->rpc_listener = tal_free(jsonrpc->rpc_listener); +} + +void jsonrpc_stop_all(struct lightningd *ld) +{ + /* Closes all conns. */ + ld->jsonrpc = tal_free(ld->jsonrpc); } static struct command_result *param_command(struct command *cmd, diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 8f090801e464..43ed29b30f9c 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -173,12 +173,24 @@ void jsonrpc_setup(struct lightningd *ld); /** - * Start listeing on ld->rpc_filename. + * Start listening on ld->rpc_filename. * * Sets up the listener effectively starting the RPC interface. */ void jsonrpc_listen(struct jsonrpc *rpc, struct lightningd *ld); +/** + * Stop listening on ld->rpc_filename. + * + * No new connections from here in. + */ +void jsonrpc_stop_listening(struct jsonrpc *jsonrpc); + +/** + * Kill any remaining JSON-RPC connections. + */ +void jsonrpc_stop_all(struct lightningd *ld); + /** * Add a new command/method to the JSON-RPC interface. * diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 39e561e02ddf..288b589e2bcb 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -515,7 +515,7 @@ static const char *find_daemon_dir(struct lightningd *ld, const char *argv0) * is an awesome runtime memory usage detector for C and C++ programs). In * some ways it would be neater not to do this, but it turns out some * transient objects still need cleaning. */ -static void shutdown_subdaemons(struct lightningd *ld) +static void free_all_channels(struct lightningd *ld) { struct peer *p; @@ -529,19 +529,6 @@ static void shutdown_subdaemons(struct lightningd *ld) * writes, which must be inside a transaction. */ db_begin_transaction(ld->wallet->db); - /* Let everyone shutdown cleanly. */ - close(ld->hsm_fd); - /*~ The three "global" daemons, which we shutdown explicitly: we - * give them 10 seconds to exit gracefully before killing them. */ - ld->connectd = subd_shutdown(ld->connectd, 10); - ld->gossip = subd_shutdown(ld->gossip, 10); - ld->hsm = subd_shutdown(ld->hsm, 10); - - /*~ Closing the hsmd means all other subdaemons should be exiting; - * deal with that cleanly before we start freeing internal - * structures. */ - subd_shutdown_remaining(ld); - /* Now we free all the HTLCs */ free_htlcs(ld, NULL); @@ -579,6 +566,18 @@ static void shutdown_subdaemons(struct lightningd *ld) db_commit_transaction(ld->wallet->db); } +static void shutdown_global_subdaemons(struct lightningd *ld) +{ + /* Let everyone shutdown cleanly. */ + close(ld->hsm_fd); + + /*~ The three "global" daemons, which we shutdown explicitly: we + * give them 10 seconds to exit gracefully before killing them. */ + ld->connectd = subd_shutdown(ld->connectd, 10); + ld->gossip = subd_shutdown(ld->gossip, 10); + ld->hsm = subd_shutdown(ld->hsm, 10); +} + /*~ Our wallet logic needs to know what outputs we might be interested in. We * use BIP32 (a.k.a. "HD wallet") to generate keys from a single seed, so we * keep the maximum-ever-used key index in the db, and add them all to the @@ -1200,7 +1199,10 @@ int main(int argc, char *argv[]) assert(io_loop_ret == ld); log_debug(ld->log, "io_loop_with_timers: %s", __func__); - /* Fail JSON RPC requests and ignore plugin's responses */ + /* Stop *new* JSON RPC requests. */ + jsonrpc_stop_listening(ld->jsonrpc); + + /* Give permission for things to get destroyed without getting upset. */ ld->state = LD_STATE_SHUTDOWN; stop_fd = -1; @@ -1221,13 +1223,24 @@ int main(int argc, char *argv[]) /* We're not going to collect our children. */ remove_sigchild_handler(sigchld_conn); - shutdown_subdaemons(ld); - /* Tell plugins we're shutting down, closes the db. */ + /* Get rid of per-channel subdaemons. */ + subd_shutdown_nonglobals(ld); + + /* Tell plugins we're shutting down, use force if necessary. */ shutdown_plugins(ld); - /* Cleanup JSON RPC separately: destructors assume some list_head * in ld */ - tal_free(ld->jsonrpc); + /* Now kill any remaining connections */ + jsonrpc_stop_all(ld); + + /* Get rid of major subdaemons. */ + shutdown_global_subdaemons(ld); + + /* Clean up internal peer/channel/htlc structures. */ + free_all_channels(ld); + + /* Now close database */ + ld->wallet->db = tal_free(ld->wallet->db); /* Clean our our HTLC maps, since they use malloc. */ htlc_in_map_clear(&ld->htlcs_in); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index c0bd267242e4..7abd04b7a9fb 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -581,11 +581,6 @@ static const char *plugin_response_handle(struct plugin *plugin, "Received a JSON-RPC response for non-existent request"); } - /* Ignore responses when shutting down */ - if (plugin->plugins->ld->state == LD_STATE_SHUTDOWN) { - return NULL; - } - /* We expect the request->cb to copy if needed */ pd = plugin_detect_destruction(plugin); request->response_cb(plugin->buffer, toks, idtok, request->response_cb_arg); @@ -2119,9 +2114,6 @@ void shutdown_plugins(struct lightningd *ld) { struct plugin *p, *next; - /* The next io_loop does not need db access, close it. */ - ld->wallet->db = tal_free(ld->wallet->db); - /* Tell them all to shutdown; if they care. */ list_for_each_safe(&ld->plugins->plugins, p, next, list) { /* Kill immediately, deletes self from list. */ diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index afab5785f59b..b8d7322f4094 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -167,7 +167,8 @@ static void plugin_hook_callback(const char *buffer, const jsmntok_t *toks, tal_del_destructor(last, plugin_hook_killed); tal_free(last); - if (r->ld->state == LD_STATE_SHUTDOWN) { + /* Actually, if it dies during shutdown, *don't* process result! */ + if (!buffer && r->ld->state == LD_STATE_SHUTDOWN) { log_debug(r->ld->log, "Abandoning plugin hook call due to shutdown"); return; diff --git a/lightningd/subd.c b/lightningd/subd.c index 4ee11c1c257e..ac151d184737 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -902,15 +902,13 @@ struct subd *subd_shutdown(struct subd *sd, unsigned int seconds) return tal_free(sd); } -void subd_shutdown_remaining(struct lightningd *ld) +void subd_shutdown_nonglobals(struct lightningd *ld) { - struct subd *subd; + struct subd *subd, *next; - /* We give them a second to finish exiting, before we kill - * them in destroy_subd() */ - sleep(1); - - while ((subd = list_top(&ld->subds, struct subd, list)) != NULL) { + list_for_each_safe(&ld->subds, subd, next, list) { + if (!subd->channel) + continue; /* Destructor removes from list */ io_close(subd->conn); } diff --git a/lightningd/subd.h b/lightningd/subd.h index db0fd6afa219..f43436b2b5d6 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -211,7 +211,7 @@ struct subd_req *subd_req_(const tal_t *ctx, void subd_release_channel(struct subd *owner, const void *channel); /** - * subd_shutdown - try to politely shut down a subdaemon. + * subd_shutdown - try to politely shut down a (global) subdaemon. * @subd: subd to shutdown. * @seconds: maximum seconds to wait for it to exit. * @@ -225,13 +225,10 @@ void subd_release_channel(struct subd *owner, const void *channel); struct subd *subd_shutdown(struct subd *subd, unsigned int seconds); /** - * subd_shutdown_remaining - kill all remaining (per-peer) subds + * subd_shutdown_nonglobals - kill all per-peer subds * @ld: lightningd - * - * They should already be exiting (since we shutdown hsmd), but - * make sure they have. */ -void subd_shutdown_remaining(struct lightningd *ld); +void subd_shutdown_nonglobals(struct lightningd *ld); /* Ugly helper to get full pathname of the current binary. */ const char *find_my_abspath(const tal_t *ctx, const char *argv0); diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 585eb5328dc4..85fd5fcf9029 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -117,6 +117,12 @@ void jsonrpc_listen(struct jsonrpc *rpc UNNEEDED, struct lightningd *ld UNNEEDED /* Generated stub for jsonrpc_setup */ void jsonrpc_setup(struct lightningd *ld UNNEEDED) { fprintf(stderr, "jsonrpc_setup called!\n"); abort(); } +/* Generated stub for jsonrpc_stop_all */ +void jsonrpc_stop_all(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "jsonrpc_stop_all called!\n"); abort(); } +/* Generated stub for jsonrpc_stop_listening */ +void jsonrpc_stop_listening(struct jsonrpc *jsonrpc UNNEEDED) +{ fprintf(stderr, "jsonrpc_stop_listening called!\n"); abort(); } /* Generated stub for load_channels_from_wallet */ struct htlc_in_map *load_channels_from_wallet(struct lightningd *ld UNNEEDED) { fprintf(stderr, "load_channels_from_wallet called!\n"); abort(); } diff --git a/tests/test_closing.py b/tests/test_closing.py index f635a17c4e84..4d18d561ef2d 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3390,7 +3390,7 @@ def test_closing_higherfee(node_factory, bitcoind, executor): l1.daemon.wait_for_log(r'deriving max fee from rate 30000 -> 16440sat \(not 1000000sat\)') # This will fail because l1 restarted! - with pytest.raises(RpcError, match=r'Channel forgotten before proper close.'): + with pytest.raises(RpcError, match=r'Connection to RPC server lost.'): fut.result(TIMEOUT) # But we still complete negotiation! diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 55f39f9c6312..d9f7ad9ac3dc 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2542,7 +2542,7 @@ def test_plugin_shutdown(node_factory): l1.rpc.plugin_start(p, dont_shutdown=True) l1.rpc.stop() l1.daemon.wait_for_logs(['test_libplugin: shutdown called', - 'misc_notifications.py: via lightningd shutdown, datastore failed', + 'misc_notifications.py: .* Connection refused', 'test_libplugin: failed to self-terminate in time, killing.']) From e0d6f3ceb1775a96672d9a7eee730f2b6c6a1e2f Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 2 Aug 2022 20:58:20 +0200 Subject: [PATCH 1336/1530] connectd: DNS Bolt7 #911 no longer EXPERIMENTAL Changelog-Changed: Bolt7 #911 DNS annoucenent support is no longer EXPERIMENTAL --- connectd/connectd.c | 11 ----------- lightningd/options.c | 4 ---- tests/test_gossip.py | 28 ++++++++-------------------- 3 files changed, 8 insertions(+), 35 deletions(-) diff --git a/connectd/connectd.c b/connectd/connectd.c index ae514cc287f3..c57a1646df2c 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -766,14 +766,12 @@ static void try_connect_one_addr(struct connecting *connect) bool use_proxy = connect->daemon->always_use_proxy; const struct wireaddr_internal *addr = &connect->addrs[connect->addrnum]; struct io_conn *conn; -#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ bool use_dns = connect->daemon->use_dns; struct addrinfo hints, *ais, *aii; struct wireaddr_internal addrhint; int gai_err; struct sockaddr_in *sa4; struct sockaddr_in6 *sa6; -#endif assert(!connect->conn); @@ -823,7 +821,6 @@ static void try_connect_one_addr(struct connecting *connect) af = AF_INET6; break; case ADDR_TYPE_DNS: -#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ if (use_proxy) /* hand it to the proxy */ break; if (!use_dns) { /* ignore DNS when we can't use it */ @@ -875,12 +872,6 @@ static void try_connect_one_addr(struct connecting *connect) addr = &connect->addrs[connect->addrnum]; } freeaddrinfo(ais); -#endif - tal_append_fmt(&connect->errors, - "%s: EXPERIMENTAL_FEATURES needed. ", - type_to_string(tmpctx, - struct wireaddr_internal, - addr)); goto next; case ADDR_TYPE_WEBSOCKET: af = -1; @@ -1636,10 +1627,8 @@ static void add_seed_addrs(struct wireaddr_internal **addrs, NULL, broken_reply, NULL); if (new_addrs) { for (size_t j = 0; j < tal_count(new_addrs); j++) { -#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ if (new_addrs[j].type == ADDR_TYPE_DNS) continue; -#endif struct wireaddr_internal a; a.itype = ADDR_INTERNAL_WIREADDR; a.u.wireaddr = new_addrs[j]; diff --git a/lightningd/options.c b/lightningd/options.c index 0bec067046e6..e90e5b07f8ed 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -195,7 +195,6 @@ static char *opt_set_accept_extra_tlv_types(const char *arg, } #endif -#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ /* Returns the number of wireaddr types already announced */ static size_t num_announced_types(enum wire_addr_type type, struct lightningd *ld) { @@ -210,7 +209,6 @@ static size_t num_announced_types(enum wire_addr_type type, struct lightningd *l } return num; } -#endif static char *opt_add_addr_withtype(const char *arg, struct lightningd *ld, @@ -257,7 +255,6 @@ static char *opt_add_addr_withtype(const char *arg, tal_arr_expand(&ld->proposed_wireaddr, wi); } -#if EXPERIMENTAL_FEATURES /* BOLT7 DNS RFC #911 */ /* Add ADDR_TYPE_DNS to announce DNS hostnames */ if (is_dnsaddr(address) && ala & ADDR_ANNOUNCE) { /* BOLT-hostnames #7: @@ -282,7 +279,6 @@ static char *opt_add_addr_withtype(const char *arg, tal_arr_expand(&ld->proposed_listen_announce, ADDR_ANNOUNCE); tal_arr_expand(&ld->proposed_wireaddr, wi); } -#endif return NULL; diff --git a/tests/test_gossip.py b/tests/test_gossip.py index aa871dccaa2d..fec573363712 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -5,7 +5,7 @@ from pyln.client import RpcError, Millisatoshi from utils import ( DEVELOPER, wait_for, TIMEOUT, only_one, sync_blockheight, - expected_node_features, COMPAT, EXPERIMENTAL_FEATURES, + expected_node_features, COMPAT, mine_funding_to_announce, default_ln_port ) @@ -124,13 +124,6 @@ def test_announce_address(node_factory, bitcoind): '::'], 'log-level': 'io', 'dev-allow-localhost': None} - if not EXPERIMENTAL_FEATURES: # BOLT7 DNS RFC #911 - opts = {'disable-dns': None, 'announce-addr': - ['4acth47i6kxnvkewtm6q7ib2s3ufpo5sqbsnzjpbi7utijcltosqemad.onion', - '1.2.3.4:1234', - '::'], - 'log-level': 'io', - 'dev-allow-localhost': None} l1, l2 = node_factory.get_nodes(2, opts=[opts, {}]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -140,14 +133,6 @@ def test_announce_address(node_factory, bitcoind): l1.wait_channel_active(scid) l2.wait_channel_active(scid) - if not EXPERIMENTAL_FEATURES: # BOLT7 DNS RFC #911 - l1.daemon.wait_for_log(r"\[OUT\] 0101.*47" - "010102030404d2" - "017f000001...." - "0200000000000000000000000000000000...." - "04e00533f3e8f2aedaa8969b3d0fa03a96e857bbb28064dca5e147e934244b9ba5023003....") - return - # We should see it send node announce with all addresses (257 = 0x0101) # Note: local ephemeral port is masked out. # Note: Since we `disable-dns` it should not announce a resolved IPv4 @@ -173,7 +158,6 @@ def test_announce_address(node_factory, bitcoind): assert addresses_dns[0]['port'] == 1236 -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") @pytest.mark.developer("gossip without DEVELOPER=1 is slow") def test_announce_and_connect_via_dns(node_factory, bitcoind): """ Test that DNS annoucements propagate and can be used when connecting. @@ -238,7 +222,6 @@ def test_announce_and_connect_via_dns(node_factory, bitcoind): l4.rpc.connect(l1.info['id']) -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") def test_only_announce_one_dns(node_factory, bitcoind): # and test that we can't announce more than one DNS address l1 = node_factory.get_node(expect_fail=True, start=False, @@ -247,7 +230,6 @@ def test_only_announce_one_dns(node_factory, bitcoind): wait_for(lambda: l1.daemon.is_in_stderr("Only one DNS can be announced")) -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "BOLT7 DNS RFC #911") def test_announce_dns_without_port(node_factory, bitcoind): """ Checks that the port of a DNS announcement is set to the corresponding network port. In this case regtest 19846 @@ -259,7 +241,13 @@ def test_announce_dns_without_port(node_factory, bitcoind): info = l1.rpc.getinfo() assert info['address'][0]['type'] == 'dns' assert info['address'][0]['address'] == 'example.com' - assert info['address'][0]['port'] == 19846 + + if TEST_NETWORK == 'regtest': + default_port = 19846 + else: + assert TEST_NETWORK == 'liquid-regtest' + default_port = 20735 + assert info['address'][0]['port'] == default_port @pytest.mark.developer("needs DEVELOPER=1") From 8452d903b4514adcea9b7c554b0dba3669fc1eda Mon Sep 17 00:00:00 2001 From: niftynei Date: Mon, 12 Sep 2022 18:49:26 -0500 Subject: [PATCH 1337/1530] bkpr: failing test for bookkeeper crash Reproduce crash for #5557! If we record the channel open because bookkeeper was added after the channel open request started but the channel confirms later, we end up with re-recording any associated push or leased fees (paid or rcvd). In the case where you've paid for these fees, your channel balance goes negative and the node crashes the next time you call `listbalances`. Reported-by: @chrisguida --- tests/test_opening.py | 55 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 207ff508633d..2ef1df163629 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1513,6 +1513,61 @@ def test_buy_liquidity_ad_no_v2(node_factory, bitcoind): compact_lease='029a002d000000004b2003e8') +@pytest.mark.xfail +@pytest.mark.openchannel('v2') +def test_buy_liquidity_ad_check_bookkeeping(node_factory, bitcoind): + """ Test that your bookkeeping for a liquidity ad is good.""" + + opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, + 'rescan': 10, 'disable-plugin': 'bookkeeper', + 'funding-confirms': 6, 'may_reconnect': True}, + {'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100}] + l1, l2, = node_factory.get_nodes(2, opts=opts) + amount = 500000 + feerate = 2000 + + l1.fundwallet(amount * 100) + l2.fundwallet(amount * 100) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + wait_for(lambda: len(l1.rpc.listpeers(l2.info['id'])['peers']) == 0) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + # l1 leases a channel from l2 + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + + # add the funding transaction + bitcoind.generate_block(4, wait_for_mempool=1) + + l1.stop() + del l1.daemon.opts['disable-plugin'] + l1.start() + + chan_id = first_channel_id(l1, l2) + ev_tags = [e['tag'] for e in l1.rpc.bkpr_listaccountevents(chan_id)['events']] + assert 'lease_fee' in ev_tags + + bitcoind.generate_block(2) + l1.daemon.wait_for_log('to CHANNELD_NORMAL') + + # This should work ok + l1.rpc.bkpr_listbalances() + + l1.rpc.close(l2.info['id'], 1) + bitcoind.generate_block(6, wait_for_mempool=1) + + l1.daemon.wait_for_log(' to ONCHAIN') + l2.daemon.wait_for_log(' to ONCHAIN') + + # This should crash + l1.rpc.bkpr_listbalances() + + def test_scid_alias_private(node_factory, bitcoind): """Test that we don't allow use of real scid for scid_alias-type channels""" l1, l2, l3 = node_factory.line_graph(3, fundchannel=False, opts=[{}, {}, From 1980ba420b1e79dc360add83ee4c2b791ac59131 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 13 Sep 2022 09:53:46 -0500 Subject: [PATCH 1338/1530] notif: dont send balance snapshot for not yet opened channel We were double counting channel lease fees because we were double firing the channel open event sequence (so to speak). If we don't report balances for unopened channels, we don't have this problem? Changelog-Changed: Plugins: `balance_snapshot` notification does not send balances for channels that aren't locked-in/opened yet --- lightningd/channel.h | 7 +++++++ lightningd/coin_mvts.c | 10 ++++++++-- tests/test_opening.py | 12 ++++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lightningd/channel.h b/lightningd/channel.h index a971b012e42f..9f8ec75e52e8 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -466,6 +466,13 @@ static inline bool channel_unsaved(const struct channel *channel) && channel->dbid == 0; } +static inline bool channel_pre_open(const struct channel *channel) +{ + return channel->state == CHANNELD_AWAITING_LOCKIN + || channel->state == DUALOPEND_OPEN_INIT + || channel->state == DUALOPEND_AWAITING_LOCKIN; +} + static inline bool channel_active(const struct channel *channel) { return channel->state != FUNDING_SPEND_SEEN diff --git a/lightningd/coin_mvts.c b/lightningd/coin_mvts.c index 7ff18015b544..b1cf1418beca 100644 --- a/lightningd/coin_mvts.c +++ b/lightningd/coin_mvts.c @@ -81,6 +81,13 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx, hout->fees); } +static bool report_chan_balance(const struct channel *chan) +{ + return (channel_active(chan) + || chan->state == AWAITING_UNILATERAL) + && !channel_pre_open(chan); +} + void send_account_balance_snapshot(struct lightningd *ld, u32 blockheight) { struct balance_snapshot *snap = tal(NULL, struct balance_snapshot); @@ -121,8 +128,7 @@ void send_account_balance_snapshot(struct lightningd *ld, u32 blockheight) /* Add channel balances */ list_for_each(&ld->peers, p, list) { list_for_each(&p->channels, chan, list) { - if (channel_active(chan) - || chan->state == AWAITING_UNILATERAL) { + if (report_chan_balance(chan)) { bal = tal(snap, struct account_balance); bal->bip173_name = chainparams->lightning_hrp; bal->acct_id = type_to_string(bal, diff --git a/tests/test_opening.py b/tests/test_opening.py index 2ef1df163629..7e5801a69dff 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1513,7 +1513,6 @@ def test_buy_liquidity_ad_no_v2(node_factory, bitcoind): compact_lease='029a002d000000004b2003e8') -@pytest.mark.xfail @pytest.mark.openchannel('v2') def test_buy_liquidity_ad_check_bookkeeping(node_factory, bitcoind): """ Test that your bookkeeping for a liquidity ad is good.""" @@ -1523,7 +1522,8 @@ def test_buy_liquidity_ad_check_bookkeeping(node_factory, bitcoind): 'rescan': 10, 'disable-plugin': 'bookkeeper', 'funding-confirms': 6, 'may_reconnect': True}, {'funder-policy': 'match', 'funder-policy-mod': 100, - 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100}] + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, + 'may_reconnect': True}] l1, l2, = node_factory.get_nodes(2, opts=opts) amount = 500000 feerate = 2000 @@ -1548,13 +1548,13 @@ def test_buy_liquidity_ad_check_bookkeeping(node_factory, bitcoind): del l1.daemon.opts['disable-plugin'] l1.start() + bitcoind.generate_block(2) + l1.daemon.wait_for_log('to CHANNELD_NORMAL') + chan_id = first_channel_id(l1, l2) ev_tags = [e['tag'] for e in l1.rpc.bkpr_listaccountevents(chan_id)['events']] assert 'lease_fee' in ev_tags - bitcoind.generate_block(2) - l1.daemon.wait_for_log('to CHANNELD_NORMAL') - # This should work ok l1.rpc.bkpr_listbalances() @@ -1564,7 +1564,7 @@ def test_buy_liquidity_ad_check_bookkeeping(node_factory, bitcoind): l1.daemon.wait_for_log(' to ONCHAIN') l2.daemon.wait_for_log(' to ONCHAIN') - # This should crash + # This should not crash l1.rpc.bkpr_listbalances() From c143914ebf7b4881ec6f1f1b1f66e6ab6947a050 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 13 Sep 2022 10:53:00 -0500 Subject: [PATCH 1339/1530] bkpr: migration to delete any duplicate lease_fee entries Clean up for #5557. If you've got duplicate 'lease_fee' entries, we delete them! --- contrib/pyln-testing/pyln/testing/utils.py | 10 ++++- plugins/bkpr/db.c | 46 +++++++++++++++++++++ tests/data/dupe_lease_fee.sqlite3.xz | Bin 0 -> 1444 bytes tests/test_bookkeeper.py | 15 +++++++ 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/data/dupe_lease_fee.sqlite3.xz diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 28889bc6fd74..aec56baddbbf 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -1435,8 +1435,8 @@ def get_nodes(self, num_nodes, opts=None): return [j.result() for j in jobs] def get_node(self, node_id=None, options=None, dbfile=None, - feerates=(15000, 11000, 7500, 3750), start=True, - wait_for_bitcoind_sync=True, may_fail=False, + bkpr_dbfile=None, feerates=(15000, 11000, 7500, 3750), + start=True, wait_for_bitcoind_sync=True, may_fail=False, expect_fail=False, cleandir=True, **kwargs): self.throttler.wait() node_id = self.get_node_id() if not node_id else node_id @@ -1470,6 +1470,12 @@ def get_node(self, node_id=None, options=None, dbfile=None, with lzma.open(os.path.join('tests/data', dbfile), 'rb') as f: out.write(f.read()) + if bkpr_dbfile: + out = open(os.path.join(node.daemon.lightning_dir, TEST_NETWORK, + 'accounts.sqlite3'), 'xb') + with lzma.open(os.path.join('tests/data', bkpr_dbfile), 'rb') as f: + out.write(f.read()) + if start: try: node.start(wait_for_bitcoind_sync) diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index b27e217e8d39..4efe43b2bbe4 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -15,6 +15,8 @@ struct migration { static struct plugin *plugin; +static void migration_remove_dupe_lease_fees(struct plugin *p, struct db *db); + /* Do not reorder or remove elements from this array. * It is used to migrate existing databases from a prevoius state, based on * string indicies */ @@ -99,6 +101,7 @@ static struct migration db_migrations[] = { {SQL("ALTER TABLE chain_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, {SQL("ALTER TABLE channel_events ADD ev_desc TEXT DEFAULT NULL;"), NULL}, {SQL("ALTER TABLE channel_events ADD rebalance_id BIGINT DEFAULT NULL;"), NULL}, + {NULL, migration_remove_dupe_lease_fees} }; static bool db_migrate(struct plugin *p, struct db *db, bool *created) @@ -141,6 +144,49 @@ static bool db_migrate(struct plugin *p, struct db *db, bool *created) return current != orig; } +static void migration_remove_dupe_lease_fees(struct plugin *p, struct db *db) +{ + struct db_stmt *stmt, *del_stmt; + u64 *last_acct_id; + + stmt = db_prepare_v2(db, SQL("SELECT" + " id" + ", account_id" + " FROM channel_events" + " WHERE tag = 'lease_fee'" + " ORDER BY account_id")); + db_query_prepared(stmt); + last_acct_id = NULL; + while (db_step(stmt)) { + u64 id, acct_id; + id = db_col_u64(stmt, "id"); + acct_id = db_col_u64(stmt, "account_id"); + + if (!last_acct_id) { + last_acct_id = tal(stmt, u64); + *last_acct_id = acct_id; + continue; + } + + if (*last_acct_id != acct_id) { + *last_acct_id = acct_id; + continue; + } + + plugin_log(plugin, LOG_INFORM, + "Duplicate 'lease_fee' found for" + " account %"PRIu64", deleting dupe", + id); + + /* same acct as last, we found a duplicate */ + del_stmt = db_prepare_v2(db, SQL("DELETE FROM channel_events" + " WHERE id=?")); + db_bind_u64(del_stmt, 0, id); + db_exec_prepared_v2(take(del_stmt)); + } + tal_free(stmt); +} + /* Implement db_fatal, as a wrapper around fatal. * We use a ifndef block so that it can get be * implemented in a test file first, if necessary */ diff --git a/tests/data/dupe_lease_fee.sqlite3.xz b/tests/data/dupe_lease_fee.sqlite3.xz new file mode 100644 index 0000000000000000000000000000000000000000..d74c3541e0c7433c382ac12355f4e3cee954d341 GIT binary patch literal 1444 zcmV;V1zY<4H+ooF000E$*0e?f03iVu0001VFXf})kN*W^T>vSRMV(;C8Tck-h>j-y zF_rzthkyo`yX=3c34ZX>jR8;*M5OZjKW(PK=iNo2q$82>jp4Llk@)|kavWHt9c|zj zE*x;4WY24~67@7|D+lJ$CmEo{NeJULx`sw@D_>)k^)2fqF2ov-udrf)ukW%2s5mSr zU-yknDHCgvI`gj?pX+lbtlplCXcX!1m=GaO)Im<9(-Rgd2?(#lgGfIi^CW2>lWPLJ z=e;@0#oV_-!TdGuJP2Pl#M&yO!`|%83s6t!)DT&B|LJP0o`(}6#0x4_ zUvN)aTD<&Dqm;dDEL}#1Zt)nTdGUUuT#r6D9-D~P9IH@k6+pgX1L)|e=UL1=R7m!I z8;|LlK<%q3xJN-*A!JX3rgn33Lps`ffa+$4@-n`#JIkkR#H^G49%;W~9sfZQZdUZ- zmQkKWuuk#hj*AXtPhi-pRIC?qK=EV?udaL5*V-hk+fu8CkgR(qdHrNxncq##=W@h+x*un*0ALATn-SM zYT#Sf;1mkaGF`nEe4zR$GdwN1sIW?Z7{-j^F8HfWdNvoDj5PsVF!(uPcH7&JqPM^5 zHjZhnHj)DdI-ZvZA`x>4XJhwIaUx`mrtyleS74GhwE~^IPh_zi1r@(_z1|xXY=;F0#$- z{3}v^BftD!pE}%4D)*o0!E`*Bi~@GCXu^^jjI&h(3GvRA~$%v#UrJ5tl-$rLGeOgfmB?Pz(>dUhyJ=7#D ywOG6nb6yf=s0`qZhoAu7eHQZsnNFJk0e}mDpaKBRcp|K^#Ao{g000001X)_-qQ%z$ literal 0 HcmV?d00001 diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index b9b9f00387cf..be2a01a6d3ee 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -1,6 +1,7 @@ from fixtures import * # noqa: F401,F403 from decimal import Decimal from pyln.client import Millisatoshi +from db import Sqlite3Db from fixtures import TEST_NETWORK from utils import ( sync_blockheight, wait_for, only_one, first_channel_id, TIMEOUT @@ -708,3 +709,17 @@ def test_rebalance_tracking(node_factory, bitcoind): assert outbound_ev['debit_msat'] == Millisatoshi(1001) assert outbound_ev['credit_msat'] == Millisatoshi(0) assert outbound_ev['payment_id'] == pay_hash + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot") +def test_bookkeeper_lease_fee_dupe_migration(node_factory): + """ Check that if there's duplicate lease_fees, we remove them""" + + l1 = node_factory.get_node(bkpr_dbfile='dupe_lease_fee.sqlite3.xz') + + wait_for(lambda: l1.daemon.is_in_log('Duplicate \'lease_fee\' found for account')) + + accts_db_path = os.path.join(l1.lightning_dir, TEST_NETWORK, 'accounts.sqlite3') + accts_db = Sqlite3Db(accts_db_path) + + assert accts_db.query('SELECT tag from channel_events where tag = \'lease_fee\';') == [{'tag': 'lease_fee'}] From efad09f96652557df4f23b8ab71f1c7c1a593ce5 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 13 Sep 2022 11:02:53 -0500 Subject: [PATCH 1340/1530] bkpr: confirm that replaying the open+lock-in txs at start is ok Make sure that we're not issuing duplicate lease_fee events! --- tests/test_opening.py | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 7e5801a69dff..d05b40d35676 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1513,6 +1513,69 @@ def test_buy_liquidity_ad_no_v2(node_factory, bitcoind): compact_lease='029a002d000000004b2003e8') +@pytest.mark.openchannel('v2') +def test_v2_replay_bookkeeping(node_factory, bitcoind): + """ Test that your bookkeeping for a liquidity ad is good + even if we replay the opening and locking tx! + """ + + opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, + 'rescan': 10, 'funding-confirms': 6, 'may_reconnect': True}, + {'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100, + 'may_reconnect': True}] + l1, l2, = node_factory.get_nodes(2, opts=opts) + amount = 500000 + feerate = 2000 + + l1.fundwallet(amount * 100) + l2.fundwallet(amount * 100) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + wait_for(lambda: len(l1.rpc.listpeers(l2.info['id'])['peers']) == 0) + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + + # l1 leases a channel from l2 + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + + # add the funding transaction + bitcoind.generate_block(4, wait_for_mempool=1) + + l1.restart() + + bitcoind.generate_block(2) + l1.daemon.wait_for_log('to CHANNELD_NORMAL') + + chan_id = first_channel_id(l1, l2) + ev_tags = [e['tag'] for e in l1.rpc.bkpr_listaccountevents(chan_id)['events']] + assert 'lease_fee' in ev_tags + + # This should work ok + l1.rpc.bkpr_listbalances() + + bitcoind.generate_block(2) + sync_blockheight(bitcoind, [l1]) + + l1.restart() + + chan_id = first_channel_id(l1, l2) + ev_tags = [e['tag'] for e in l1.rpc.bkpr_listaccountevents(chan_id)['events']] + assert 'lease_fee' in ev_tags + + l1.rpc.close(l2.info['id'], 1) + bitcoind.generate_block(6, wait_for_mempool=1) + + l1.daemon.wait_for_log(' to ONCHAIN') + l2.daemon.wait_for_log(' to ONCHAIN') + + # This should not crash + l1.rpc.bkpr_listbalances() + + @pytest.mark.openchannel('v2') def test_buy_liquidity_ad_check_bookkeeping(node_factory, bitcoind): """ Test that your bookkeeping for a liquidity ad is good.""" From 3ad8347969cce2b6f5b9b16ba8821d8413f9cb42 Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 13 Sep 2022 12:55:44 -0500 Subject: [PATCH 1341/1530] bkpr-test: maybe fix race in test_bookkeeping_closing_trimmed_htlcs test_bookkeeping_closing_trimmed_htlcs fails to find 'all outputs resolved' occassionally, seems like it's because the OUR_DELAYED_TO_WALLET doesn't make it into the mempool before we start mining blocks? So here make sure there's something in the mempool before before we start making new blocks. --- tests/test_bookkeeper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_bookkeeper.py b/tests/test_bookkeeper.py index be2a01a6d3ee..a3931ba5572d 100644 --- a/tests/test_bookkeeper.py +++ b/tests/test_bookkeeper.py @@ -46,7 +46,7 @@ def test_bookkeeping_closing_trimmed_htlcs(node_factory, bitcoind, executor): bitcoind.generate_block(5) sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') - bitcoind.generate_block(20) + bitcoind.generate_block(20, wait_for_mempool=1) sync_blockheight(bitcoind, [l1]) l1.daemon.wait_for_log(r'All outputs resolved.*') From daeec66bd7815d73f0e00b8610bd024d54be7ba8 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 9 Jun 2022 18:14:01 +0200 Subject: [PATCH 1342/1530] db: Add completed_at field to payments We'll want to use these to track durations for `sendpays` and `pays`. --- wallet/db.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wallet/db.c b/wallet/db.c index a0fd0e8da786..b3a5a248aaec 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -883,6 +883,9 @@ static struct migration dbmigrations[] = { * in routehints in invoices. The peer will remember all the * aliases, but we only ever need one. */ {SQL("ALTER TABLE channels ADD alias_remote BIGINT DEFAULT NULL"), NULL}, + /* Cheeky immediate completion as best effort approximation of real completion time */ + {SQL("ALTER TABLE payments ADD completed_at INTEGER DEFAULT NULL;"), NULL}, + {SQL("UPDATE payments SET completed_at = timestamp WHERE status != 0;"), NULL} }; /** From cb3ee0ac2ea126092775e46b56e4ed8650b4c23c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 9 Jun 2022 18:36:00 +0200 Subject: [PATCH 1343/1530] wallet: Load and value `completed_at` timestamp from DB --- .msggen.json | 3 + cln-grpc/proto/node.proto | 3 + cln-grpc/src/convert.rs | 3 + cln-rpc/src/model.rs | 6 + contrib/pyln-testing/pyln/testing/grpc2py.py | 7 +- contrib/pyln-testing/pyln/testing/node_pb2.py | 544 +++++++++--------- doc/lightning-delpay.7.md | 3 +- doc/lightning-listpays.7.md | 8 +- doc/lightning-listsendpays.7.md | 2 +- doc/lightning-sendpay.7.md | 3 +- doc/lightning-waitsendpay.7.md | 3 +- doc/schemas/delpay.schema.json | 4 + doc/schemas/listpays.schema.json | 49 +- doc/schemas/listsendpays.schema.json | 2 + doc/schemas/sendpay.schema.json | 6 + doc/schemas/waitsendpay.schema.json | 5 + lightningd/pay.c | 6 +- tests/test_pay.py | 1 + wallet/wallet.c | 9 + wallet/wallet.h | 1 + 20 files changed, 343 insertions(+), 325 deletions(-) diff --git a/.msggen.json b/.msggen.json index 6a65191962eb..a9180ad84375 100644 --- a/.msggen.json +++ b/.msggen.json @@ -666,6 +666,7 @@ "ListPays.pays[].amount_sent_msat": 9, "ListPays.pays[].bolt11": 6, "ListPays.pays[].bolt12": 7, + "ListPays.pays[].completed_at": 12, "ListPays.pays[].created_at": 4, "ListPays.pays[].description": 11, "ListPays.pays[].destination": 3, @@ -940,6 +941,7 @@ "SendPay.amount_sent_msat": 8, "SendPay.bolt11": 11, "SendPay.bolt12": 12, + "SendPay.completed_at": 15, "SendPay.created_at": 7, "SendPay.destination": 6, "SendPay.groupid": 2, @@ -1080,6 +1082,7 @@ "WaitSendPay.amount_sent_msat": 8, "WaitSendPay.bolt11": 11, "WaitSendPay.bolt12": 12, + "WaitSendPay.completed_at": 14, "WaitSendPay.created_at": 7, "WaitSendPay.destination": 6, "WaitSendPay.groupid": 2, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index ef484ab0f37c..4f10134b3e96 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -324,6 +324,7 @@ message SendpayResponse { optional Amount amount_msat = 5; optional bytes destination = 6; uint64 created_at = 7; + optional uint64 completed_at = 15; Amount amount_sent_msat = 8; optional string label = 9; optional uint64 partid = 10; @@ -918,6 +919,7 @@ message WaitsendpayResponse { optional Amount amount_msat = 5; optional bytes destination = 6; uint64 created_at = 7; + optional double completed_at = 14; Amount amount_sent_msat = 8; optional string label = 9; optional uint64 partid = 10; @@ -1263,6 +1265,7 @@ message ListpaysPays { ListpaysPaysStatus status = 2; optional bytes destination = 3; uint64 created_at = 4; + optional uint64 completed_at = 12; optional string label = 5; optional string bolt11 = 6; optional string description = 11; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index d2e22fb31843..7d3896bf6a52 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -232,6 +232,7 @@ impl From<&responses::SendpayResponse> for pb::SendpayResponse { amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? created_at: c.created_at.clone(), // Rule #2 for type u64 + completed_at: c.completed_at.clone(), // Rule #2 for type u64? amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label.clone(), // Rule #2 for type string? partid: c.partid.clone(), // Rule #2 for type u64? @@ -683,6 +684,7 @@ impl From<&responses::WaitsendpayResponse> for pb::WaitsendpayResponse { amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? created_at: c.created_at.clone(), // Rule #2 for type u64 + completed_at: c.completed_at.clone(), // Rule #2 for type number? amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label.clone(), // Rule #2 for type string? partid: c.partid.clone(), // Rule #2 for type u64? @@ -924,6 +926,7 @@ impl From<&responses::ListpaysPays> for pb::ListpaysPays { status: c.status as i32, destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? created_at: c.created_at.clone(), // Rule #2 for type u64 + completed_at: c.completed_at.clone(), // Rule #2 for type u64? label: c.label.clone(), // Rule #2 for type string? bolt11: c.bolt11.clone(), // Rule #2 for type string? description: c.description.clone(), // Rule #2 for type string? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 1607a2d4e776..817aa0aa66eb 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1409,6 +1409,8 @@ pub mod responses { pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, + #[serde(alias = "completed_at", skip_serializing_if = "Option::is_none")] + pub completed_at: Option, #[serde(alias = "amount_sent_msat")] pub amount_sent_msat: Amount, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] @@ -2327,6 +2329,8 @@ pub mod responses { pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, + #[serde(alias = "completed_at", skip_serializing_if = "Option::is_none")] + pub completed_at: Option, #[serde(alias = "amount_sent_msat")] pub amount_sent_msat: Amount, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] @@ -2725,6 +2729,8 @@ pub mod responses { pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, + #[serde(alias = "completed_at", skip_serializing_if = "Option::is_none")] + pub completed_at: Option, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] pub label: Option, #[serde(alias = "bolt11", skip_serializing_if = "Option::is_none")] diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index fe619c6cb4f7..551b341cca75 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -241,6 +241,7 @@ def sendpay2py(m): "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite "destination": hexlify(m.destination), # PrimitiveField in generate_composite "created_at": m.created_at, # PrimitiveField in generate_composite + "completed_at": m.completed_at, # PrimitiveField in generate_composite "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite "label": m.label, # PrimitiveField in generate_composite "partid": m.partid, # PrimitiveField in generate_composite @@ -606,6 +607,7 @@ def waitsendpay2py(m): "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite "destination": hexlify(m.destination), # PrimitiveField in generate_composite "created_at": m.created_at, # PrimitiveField in generate_composite + "completed_at": m.completed_at, # PrimitiveField in generate_composite "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite "label": m.label, # PrimitiveField in generate_composite "partid": m.partid, # PrimitiveField in generate_composite @@ -823,12 +825,13 @@ def listpays_pays2py(m): "status": str(m.status), # EnumField in generate_composite "destination": hexlify(m.destination), # PrimitiveField in generate_composite "created_at": m.created_at, # PrimitiveField in generate_composite + "completed_at": m.completed_at, # PrimitiveField in generate_composite "label": m.label, # PrimitiveField in generate_composite "bolt11": m.bolt11, # PrimitiveField in generate_composite "description": m.description, # PrimitiveField in generate_composite "bolt12": m.bolt12, # PrimitiveField in generate_composite - "amount_msat": amount2msat(m.amount_msat), # PrimitiveField in generate_composite - "amount_sent_msat": amount2msat(m.amount_sent_msat), # PrimitiveField in generate_composite + "preimage": hexlify(m.preimage), # PrimitiveField in generate_composite + "number_of_parts": m.number_of_parts, # PrimitiveField in generate_composite "erroronion": hexlify(m.erroronion), # PrimitiveField in generate_composite }) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 8f5b615a09f7..32a93228cd20 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xa5\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\x08\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\x86\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x04\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x07\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xb6\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepth\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\xfd\x03\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x12\n\x05label\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x04\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12*\n\x10\x61mount_sent_msat\x18\t \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x07\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x13\n\x11_amount_sent_msatB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xb6\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepth\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1126,275 +1126,275 @@ _SENDPAYREQUEST._serialized_start=6559 _SENDPAYREQUEST._serialized_end=6906 _SENDPAYRESPONSE._serialized_start=6909 - _SENDPAYRESPONSE._serialized_end=7458 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7296 - _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7338 - _SENDPAYROUTE._serialized_start=7460 - _SENDPAYROUTE._serialized_end=7552 - _LISTCHANNELSREQUEST._serialized_start=7555 - _LISTCHANNELSREQUEST._serialized_end=7702 - _LISTCHANNELSRESPONSE._serialized_start=7704 - _LISTCHANNELSRESPONSE._serialized_end=7771 - _LISTCHANNELSCHANNELS._serialized_start=7774 - _LISTCHANNELSCHANNELS._serialized_end=8190 - _ADDGOSSIPREQUEST._serialized_start=8192 - _ADDGOSSIPREQUEST._serialized_end=8227 - _ADDGOSSIPRESPONSE._serialized_start=8229 - _ADDGOSSIPRESPONSE._serialized_end=8248 - _AUTOCLEANINVOICEREQUEST._serialized_start=8250 - _AUTOCLEANINVOICEREQUEST._serialized_end=8361 - _AUTOCLEANINVOICERESPONSE._serialized_start=8364 - _AUTOCLEANINVOICERESPONSE._serialized_end=8493 - _CHECKMESSAGEREQUEST._serialized_start=8495 - _CHECKMESSAGEREQUEST._serialized_end=8580 - _CHECKMESSAGERESPONSE._serialized_start=8582 - _CHECKMESSAGERESPONSE._serialized_end=8638 - _CLOSEREQUEST._serialized_start=8641 - _CLOSEREQUEST._serialized_end=8957 - _CLOSERESPONSE._serialized_start=8960 - _CLOSERESPONSE._serialized_end=9131 - _CLOSERESPONSE_CLOSETYPE._serialized_start=9062 - _CLOSERESPONSE_CLOSETYPE._serialized_end=9115 - _CONNECTREQUEST._serialized_start=9133 - _CONNECTREQUEST._serialized_end=9217 - _CONNECTRESPONSE._serialized_start=9220 - _CONNECTRESPONSE._serialized_end=9362 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9327 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9362 - _CONNECTADDRESS._serialized_start=9365 - _CONNECTADDRESS._serialized_end=9616 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9504 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9584 - _CREATEINVOICEREQUEST._serialized_start=9618 - _CREATEINVOICEREQUEST._serialized_end=9692 - _CREATEINVOICERESPONSE._serialized_start=9695 - _CREATEINVOICERESPONSE._serialized_end=10322 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10122 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10178 - _DATASTOREREQUEST._serialized_start=10325 - _DATASTOREREQUEST._serialized_end=10633 - _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10478 - _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10590 - _DATASTORERESPONSE._serialized_start=10636 - _DATASTORERESPONSE._serialized_end=10766 - _CREATEONIONREQUEST._serialized_start=10769 - _CREATEONIONREQUEST._serialized_end=10926 - _CREATEONIONRESPONSE._serialized_start=10928 - _CREATEONIONRESPONSE._serialized_end=10988 - _CREATEONIONHOPS._serialized_start=10990 - _CREATEONIONHOPS._serialized_end=11040 - _DELDATASTOREREQUEST._serialized_start=11042 - _DELDATASTOREREQUEST._serialized_end=11116 - _DELDATASTORERESPONSE._serialized_start=11119 - _DELDATASTORERESPONSE._serialized_end=11252 - _DELEXPIREDINVOICEREQUEST._serialized_start=11254 - _DELEXPIREDINVOICEREQUEST._serialized_end=11326 - _DELEXPIREDINVOICERESPONSE._serialized_start=11328 - _DELEXPIREDINVOICERESPONSE._serialized_end=11355 - _DELINVOICEREQUEST._serialized_start=11358 - _DELINVOICEREQUEST._serialized_end=11540 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11474 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11527 - _DELINVOICERESPONSE._serialized_start=11543 - _DELINVOICERESPONSE._serialized_end=11982 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11474 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11527 - _INVOICEREQUEST._serialized_start=11985 - _INVOICEREQUEST._serialized_end=12297 - _INVOICERESPONSE._serialized_start=12300 - _INVOICERESPONSE._serialized_end=12659 - _LISTDATASTOREREQUEST._serialized_start=12661 - _LISTDATASTOREREQUEST._serialized_end=12696 - _LISTDATASTORERESPONSE._serialized_start=12698 - _LISTDATASTORERESPONSE._serialized_end=12769 - _LISTDATASTOREDATASTORE._serialized_start=12772 - _LISTDATASTOREDATASTORE._serialized_end=12907 - _LISTINVOICESREQUEST._serialized_start=12910 - _LISTINVOICESREQUEST._serialized_end=13079 - _LISTINVOICESRESPONSE._serialized_start=13081 - _LISTINVOICESRESPONSE._serialized_end=13148 - _LISTINVOICESINVOICES._serialized_start=13151 - _LISTINVOICESINVOICES._serialized_end=13811 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13588 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13651 - _SENDONIONREQUEST._serialized_start=13814 - _SENDONIONREQUEST._serialized_end=14162 - _SENDONIONRESPONSE._serialized_start=14165 - _SENDONIONRESPONSE._serialized_end=14688 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14536 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14580 - _SENDONIONFIRST_HOP._serialized_start=14690 - _SENDONIONFIRST_HOP._serialized_end=14771 - _LISTSENDPAYSREQUEST._serialized_start=14774 - _LISTSENDPAYSREQUEST._serialized_end=15009 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14911 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=14970 - _LISTSENDPAYSRESPONSE._serialized_start=15011 - _LISTSENDPAYSRESPONSE._serialized_end=15078 - _LISTSENDPAYSPAYMENTS._serialized_start=15081 - _LISTSENDPAYSPAYMENTS._serialized_end=15694 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15499 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15566 - _LISTTRANSACTIONSREQUEST._serialized_start=15696 - _LISTTRANSACTIONSREQUEST._serialized_end=15721 - _LISTTRANSACTIONSRESPONSE._serialized_start=15723 - _LISTTRANSACTIONSRESPONSE._serialized_end=15806 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15809 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16091 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16094 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16610 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16306 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16584 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16613 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17157 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16852 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17131 - _PAYREQUEST._serialized_start=17160 - _PAYREQUEST._serialized_end=17632 - _PAYRESPONSE._serialized_start=17635 - _PAYRESPONSE._serialized_end=18014 - _PAYRESPONSE_PAYSTATUS._serialized_start=17917 - _PAYRESPONSE_PAYSTATUS._serialized_end=17967 - _LISTNODESREQUEST._serialized_start=18016 - _LISTNODESREQUEST._serialized_end=18058 - _LISTNODESRESPONSE._serialized_start=18060 - _LISTNODESRESPONSE._serialized_end=18115 - _LISTNODESNODES._serialized_start=18118 - _LISTNODESNODES._serialized_end=18343 - _LISTNODESNODESADDRESSES._serialized_start=18346 - _LISTNODESNODESADDRESSES._serialized_end=18593 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18486 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18581 - _WAITANYINVOICEREQUEST._serialized_start=18595 - _WAITANYINVOICEREQUEST._serialized_end=18698 - _WAITANYINVOICERESPONSE._serialized_start=18701 - _WAITANYINVOICERESPONSE._serialized_end=19232 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19077 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19122 - _WAITINVOICEREQUEST._serialized_start=19234 - _WAITINVOICEREQUEST._serialized_end=19269 - _WAITINVOICERESPONSE._serialized_start=19272 - _WAITINVOICERESPONSE._serialized_end=19791 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19639 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19681 - _WAITSENDPAYREQUEST._serialized_start=19794 - _WAITSENDPAYREQUEST._serialized_end=19936 - _WAITSENDPAYRESPONSE._serialized_start=19939 - _WAITSENDPAYRESPONSE._serialized_end=20457 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20316 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20349 - _NEWADDRREQUEST._serialized_start=20460 - _NEWADDRREQUEST._serialized_end=20618 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20544 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20602 - _NEWADDRRESPONSE._serialized_start=20620 - _NEWADDRRESPONSE._serialized_end=20711 - _WITHDRAWREQUEST._serialized_start=20714 - _WITHDRAWREQUEST._serialized_end=20916 - _WITHDRAWRESPONSE._serialized_start=20918 - _WITHDRAWRESPONSE._serialized_end=20976 - _KEYSENDREQUEST._serialized_start=20979 - _KEYSENDREQUEST._serialized_end=21311 - _KEYSENDRESPONSE._serialized_start=21314 - _KEYSENDRESPONSE._serialized_end=21684 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21608 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21637 - _KEYSENDEXTRATLVS._serialized_start=21686 - _KEYSENDEXTRATLVS._serialized_end=21704 - _FUNDPSBTREQUEST._serialized_start=21707 - _FUNDPSBTREQUEST._serialized_end=22018 - _FUNDPSBTRESPONSE._serialized_start=22021 - _FUNDPSBTRESPONSE._serialized_end=22238 - _FUNDPSBTRESERVATIONS._serialized_start=22240 - _FUNDPSBTRESERVATIONS._serialized_end=22357 - _SENDPSBTREQUEST._serialized_start=22359 - _SENDPSBTREQUEST._serialized_end=22424 - _SENDPSBTRESPONSE._serialized_start=22426 - _SENDPSBTRESPONSE._serialized_end=22470 - _SIGNPSBTREQUEST._serialized_start=22472 - _SIGNPSBTREQUEST._serialized_end=22521 - _SIGNPSBTRESPONSE._serialized_start=22523 - _SIGNPSBTRESPONSE._serialized_end=22562 - _UTXOPSBTREQUEST._serialized_start=22565 - _UTXOPSBTREQUEST._serialized_end=22912 - _UTXOPSBTRESPONSE._serialized_start=22915 - _UTXOPSBTRESPONSE._serialized_end=23132 - _UTXOPSBTRESERVATIONS._serialized_start=23134 - _UTXOPSBTRESERVATIONS._serialized_end=23251 - _TXDISCARDREQUEST._serialized_start=23253 - _TXDISCARDREQUEST._serialized_end=23285 - _TXDISCARDRESPONSE._serialized_start=23287 - _TXDISCARDRESPONSE._serialized_end=23341 - _TXPREPAREREQUEST._serialized_start=23344 - _TXPREPAREREQUEST._serialized_end=23508 - _TXPREPARERESPONSE._serialized_start=23510 - _TXPREPARERESPONSE._serialized_end=23578 - _TXSENDREQUEST._serialized_start=23580 - _TXSENDREQUEST._serialized_end=23609 - _TXSENDRESPONSE._serialized_start=23611 - _TXSENDRESPONSE._serialized_end=23667 - _DISCONNECTREQUEST._serialized_start=23669 - _DISCONNECTREQUEST._serialized_end=23730 - _DISCONNECTRESPONSE._serialized_start=23732 - _DISCONNECTRESPONSE._serialized_end=23752 - _FEERATESREQUEST._serialized_start=23754 - _FEERATESREQUEST._serialized_end=23861 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23824 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23861 - _FEERATESRESPONSE._serialized_start=23863 - _FEERATESRESPONSE._serialized_end=23949 - _FEERATESPERKB._serialized_start=23952 - _FEERATESPERKB._serialized_end=24275 - _FEERATESPERKW._serialized_start=24278 - _FEERATESPERKW._serialized_end=24601 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24604 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24797 - _FUNDCHANNELREQUEST._serialized_start=24800 - _FUNDCHANNELREQUEST._serialized_end=25238 - _FUNDCHANNELRESPONSE._serialized_start=25241 - _FUNDCHANNELRESPONSE._serialized_end=25396 - _GETROUTEREQUEST._serialized_start=25399 - _GETROUTEREQUEST._serialized_end=25635 - _GETROUTERESPONSE._serialized_start=25637 - _GETROUTERESPONSE._serialized_end=25690 - _GETROUTEROUTE._serialized_start=25693 - _GETROUTEROUTE._serialized_end=25890 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25861 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25890 - _LISTFORWARDSREQUEST._serialized_start=25893 - _LISTFORWARDSREQUEST._serialized_end=26151 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26033 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26109 - _LISTFORWARDSRESPONSE._serialized_start=26153 - _LISTFORWARDSRESPONSE._serialized_end=26220 - _LISTFORWARDSFORWARDS._serialized_start=26223 - _LISTFORWARDSFORWARDS._serialized_end=26791 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26588 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26672 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26674 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26722 - _LISTPAYSREQUEST._serialized_start=26794 - _LISTPAYSREQUEST._serialized_end=27013 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=26919 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=26974 - _LISTPAYSRESPONSE._serialized_start=27015 - _LISTPAYSRESPONSE._serialized_end=27066 - _LISTPAYSPAYS._serialized_start=27069 - _LISTPAYSPAYS._serialized_end=27578 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27403 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27462 - _PINGREQUEST._serialized_start=27580 - _PINGREQUEST._serialized_end=27669 - _PINGRESPONSE._serialized_start=27671 - _PINGRESPONSE._serialized_end=27701 - _SIGNMESSAGEREQUEST._serialized_start=27703 - _SIGNMESSAGEREQUEST._serialized_end=27740 - _SIGNMESSAGERESPONSE._serialized_start=27742 - _SIGNMESSAGERESPONSE._serialized_end=27812 - _STOPREQUEST._serialized_start=27814 - _STOPREQUEST._serialized_end=27827 - _STOPRESPONSE._serialized_start=27829 - _STOPRESPONSE._serialized_end=27843 - _NODE._serialized_start=27846 - _NODE._serialized_end=30774 + _SENDPAYRESPONSE._serialized_end=7502 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_start=7323 + _SENDPAYRESPONSE_SENDPAYSTATUS._serialized_end=7365 + _SENDPAYROUTE._serialized_start=7504 + _SENDPAYROUTE._serialized_end=7596 + _LISTCHANNELSREQUEST._serialized_start=7599 + _LISTCHANNELSREQUEST._serialized_end=7746 + _LISTCHANNELSRESPONSE._serialized_start=7748 + _LISTCHANNELSRESPONSE._serialized_end=7815 + _LISTCHANNELSCHANNELS._serialized_start=7818 + _LISTCHANNELSCHANNELS._serialized_end=8234 + _ADDGOSSIPREQUEST._serialized_start=8236 + _ADDGOSSIPREQUEST._serialized_end=8271 + _ADDGOSSIPRESPONSE._serialized_start=8273 + _ADDGOSSIPRESPONSE._serialized_end=8292 + _AUTOCLEANINVOICEREQUEST._serialized_start=8294 + _AUTOCLEANINVOICEREQUEST._serialized_end=8405 + _AUTOCLEANINVOICERESPONSE._serialized_start=8408 + _AUTOCLEANINVOICERESPONSE._serialized_end=8537 + _CHECKMESSAGEREQUEST._serialized_start=8539 + _CHECKMESSAGEREQUEST._serialized_end=8624 + _CHECKMESSAGERESPONSE._serialized_start=8626 + _CHECKMESSAGERESPONSE._serialized_end=8682 + _CLOSEREQUEST._serialized_start=8685 + _CLOSEREQUEST._serialized_end=9001 + _CLOSERESPONSE._serialized_start=9004 + _CLOSERESPONSE._serialized_end=9175 + _CLOSERESPONSE_CLOSETYPE._serialized_start=9106 + _CLOSERESPONSE_CLOSETYPE._serialized_end=9159 + _CONNECTREQUEST._serialized_start=9177 + _CONNECTREQUEST._serialized_end=9261 + _CONNECTRESPONSE._serialized_start=9264 + _CONNECTRESPONSE._serialized_end=9406 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9371 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9406 + _CONNECTADDRESS._serialized_start=9409 + _CONNECTADDRESS._serialized_end=9660 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9548 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9628 + _CREATEINVOICEREQUEST._serialized_start=9662 + _CREATEINVOICEREQUEST._serialized_end=9736 + _CREATEINVOICERESPONSE._serialized_start=9739 + _CREATEINVOICERESPONSE._serialized_end=10366 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10166 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10222 + _DATASTOREREQUEST._serialized_start=10369 + _DATASTOREREQUEST._serialized_end=10677 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10522 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10634 + _DATASTORERESPONSE._serialized_start=10680 + _DATASTORERESPONSE._serialized_end=10810 + _CREATEONIONREQUEST._serialized_start=10813 + _CREATEONIONREQUEST._serialized_end=10970 + _CREATEONIONRESPONSE._serialized_start=10972 + _CREATEONIONRESPONSE._serialized_end=11032 + _CREATEONIONHOPS._serialized_start=11034 + _CREATEONIONHOPS._serialized_end=11084 + _DELDATASTOREREQUEST._serialized_start=11086 + _DELDATASTOREREQUEST._serialized_end=11160 + _DELDATASTORERESPONSE._serialized_start=11163 + _DELDATASTORERESPONSE._serialized_end=11296 + _DELEXPIREDINVOICEREQUEST._serialized_start=11298 + _DELEXPIREDINVOICEREQUEST._serialized_end=11370 + _DELEXPIREDINVOICERESPONSE._serialized_start=11372 + _DELEXPIREDINVOICERESPONSE._serialized_end=11399 + _DELINVOICEREQUEST._serialized_start=11402 + _DELINVOICEREQUEST._serialized_end=11584 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11518 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11571 + _DELINVOICERESPONSE._serialized_start=11587 + _DELINVOICERESPONSE._serialized_end=12026 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11518 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11571 + _INVOICEREQUEST._serialized_start=12029 + _INVOICEREQUEST._serialized_end=12341 + _INVOICERESPONSE._serialized_start=12344 + _INVOICERESPONSE._serialized_end=12703 + _LISTDATASTOREREQUEST._serialized_start=12705 + _LISTDATASTOREREQUEST._serialized_end=12740 + _LISTDATASTORERESPONSE._serialized_start=12742 + _LISTDATASTORERESPONSE._serialized_end=12813 + _LISTDATASTOREDATASTORE._serialized_start=12816 + _LISTDATASTOREDATASTORE._serialized_end=12951 + _LISTINVOICESREQUEST._serialized_start=12954 + _LISTINVOICESREQUEST._serialized_end=13123 + _LISTINVOICESRESPONSE._serialized_start=13125 + _LISTINVOICESRESPONSE._serialized_end=13192 + _LISTINVOICESINVOICES._serialized_start=13195 + _LISTINVOICESINVOICES._serialized_end=13855 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13632 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13695 + _SENDONIONREQUEST._serialized_start=13858 + _SENDONIONREQUEST._serialized_end=14206 + _SENDONIONRESPONSE._serialized_start=14209 + _SENDONIONRESPONSE._serialized_end=14732 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14580 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14624 + _SENDONIONFIRST_HOP._serialized_start=14734 + _SENDONIONFIRST_HOP._serialized_end=14815 + _LISTSENDPAYSREQUEST._serialized_start=14818 + _LISTSENDPAYSREQUEST._serialized_end=15053 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14955 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=15014 + _LISTSENDPAYSRESPONSE._serialized_start=15055 + _LISTSENDPAYSRESPONSE._serialized_end=15122 + _LISTSENDPAYSPAYMENTS._serialized_start=15125 + _LISTSENDPAYSPAYMENTS._serialized_end=15738 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15543 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15610 + _LISTTRANSACTIONSREQUEST._serialized_start=15740 + _LISTTRANSACTIONSREQUEST._serialized_end=15765 + _LISTTRANSACTIONSRESPONSE._serialized_start=15767 + _LISTTRANSACTIONSRESPONSE._serialized_end=15850 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15853 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16135 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16138 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16654 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16350 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16628 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16657 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17201 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16896 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17175 + _PAYREQUEST._serialized_start=17204 + _PAYREQUEST._serialized_end=17676 + _PAYRESPONSE._serialized_start=17679 + _PAYRESPONSE._serialized_end=18058 + _PAYRESPONSE_PAYSTATUS._serialized_start=17961 + _PAYRESPONSE_PAYSTATUS._serialized_end=18011 + _LISTNODESREQUEST._serialized_start=18060 + _LISTNODESREQUEST._serialized_end=18102 + _LISTNODESRESPONSE._serialized_start=18104 + _LISTNODESRESPONSE._serialized_end=18159 + _LISTNODESNODES._serialized_start=18162 + _LISTNODESNODES._serialized_end=18387 + _LISTNODESNODESADDRESSES._serialized_start=18390 + _LISTNODESNODESADDRESSES._serialized_end=18637 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18530 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18625 + _WAITANYINVOICEREQUEST._serialized_start=18639 + _WAITANYINVOICEREQUEST._serialized_end=18742 + _WAITANYINVOICERESPONSE._serialized_start=18745 + _WAITANYINVOICERESPONSE._serialized_end=19276 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19121 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19166 + _WAITINVOICEREQUEST._serialized_start=19278 + _WAITINVOICEREQUEST._serialized_end=19313 + _WAITINVOICERESPONSE._serialized_start=19316 + _WAITINVOICERESPONSE._serialized_end=19835 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19683 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19725 + _WAITSENDPAYREQUEST._serialized_start=19838 + _WAITSENDPAYREQUEST._serialized_end=19980 + _WAITSENDPAYRESPONSE._serialized_start=19983 + _WAITSENDPAYRESPONSE._serialized_end=20545 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20387 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20420 + _NEWADDRREQUEST._serialized_start=20548 + _NEWADDRREQUEST._serialized_end=20706 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20632 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20690 + _NEWADDRRESPONSE._serialized_start=20708 + _NEWADDRRESPONSE._serialized_end=20799 + _WITHDRAWREQUEST._serialized_start=20802 + _WITHDRAWREQUEST._serialized_end=21004 + _WITHDRAWRESPONSE._serialized_start=21006 + _WITHDRAWRESPONSE._serialized_end=21064 + _KEYSENDREQUEST._serialized_start=21067 + _KEYSENDREQUEST._serialized_end=21399 + _KEYSENDRESPONSE._serialized_start=21402 + _KEYSENDRESPONSE._serialized_end=21772 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21696 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21725 + _KEYSENDEXTRATLVS._serialized_start=21774 + _KEYSENDEXTRATLVS._serialized_end=21792 + _FUNDPSBTREQUEST._serialized_start=21795 + _FUNDPSBTREQUEST._serialized_end=22106 + _FUNDPSBTRESPONSE._serialized_start=22109 + _FUNDPSBTRESPONSE._serialized_end=22326 + _FUNDPSBTRESERVATIONS._serialized_start=22328 + _FUNDPSBTRESERVATIONS._serialized_end=22445 + _SENDPSBTREQUEST._serialized_start=22447 + _SENDPSBTREQUEST._serialized_end=22512 + _SENDPSBTRESPONSE._serialized_start=22514 + _SENDPSBTRESPONSE._serialized_end=22558 + _SIGNPSBTREQUEST._serialized_start=22560 + _SIGNPSBTREQUEST._serialized_end=22609 + _SIGNPSBTRESPONSE._serialized_start=22611 + _SIGNPSBTRESPONSE._serialized_end=22650 + _UTXOPSBTREQUEST._serialized_start=22653 + _UTXOPSBTREQUEST._serialized_end=23000 + _UTXOPSBTRESPONSE._serialized_start=23003 + _UTXOPSBTRESPONSE._serialized_end=23220 + _UTXOPSBTRESERVATIONS._serialized_start=23222 + _UTXOPSBTRESERVATIONS._serialized_end=23339 + _TXDISCARDREQUEST._serialized_start=23341 + _TXDISCARDREQUEST._serialized_end=23373 + _TXDISCARDRESPONSE._serialized_start=23375 + _TXDISCARDRESPONSE._serialized_end=23429 + _TXPREPAREREQUEST._serialized_start=23432 + _TXPREPAREREQUEST._serialized_end=23596 + _TXPREPARERESPONSE._serialized_start=23598 + _TXPREPARERESPONSE._serialized_end=23666 + _TXSENDREQUEST._serialized_start=23668 + _TXSENDREQUEST._serialized_end=23697 + _TXSENDRESPONSE._serialized_start=23699 + _TXSENDRESPONSE._serialized_end=23755 + _DISCONNECTREQUEST._serialized_start=23757 + _DISCONNECTREQUEST._serialized_end=23818 + _DISCONNECTRESPONSE._serialized_start=23820 + _DISCONNECTRESPONSE._serialized_end=23840 + _FEERATESREQUEST._serialized_start=23842 + _FEERATESREQUEST._serialized_end=23949 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23912 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23949 + _FEERATESRESPONSE._serialized_start=23951 + _FEERATESRESPONSE._serialized_end=24037 + _FEERATESPERKB._serialized_start=24040 + _FEERATESPERKB._serialized_end=24363 + _FEERATESPERKW._serialized_start=24366 + _FEERATESPERKW._serialized_end=24689 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24692 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24885 + _FUNDCHANNELREQUEST._serialized_start=24888 + _FUNDCHANNELREQUEST._serialized_end=25326 + _FUNDCHANNELRESPONSE._serialized_start=25329 + _FUNDCHANNELRESPONSE._serialized_end=25484 + _GETROUTEREQUEST._serialized_start=25487 + _GETROUTEREQUEST._serialized_end=25723 + _GETROUTERESPONSE._serialized_start=25725 + _GETROUTERESPONSE._serialized_end=25778 + _GETROUTEROUTE._serialized_start=25781 + _GETROUTEROUTE._serialized_end=25978 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25949 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25978 + _LISTFORWARDSREQUEST._serialized_start=25981 + _LISTFORWARDSREQUEST._serialized_end=26239 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26121 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26197 + _LISTFORWARDSRESPONSE._serialized_start=26241 + _LISTFORWARDSRESPONSE._serialized_end=26308 + _LISTFORWARDSFORWARDS._serialized_start=26311 + _LISTFORWARDSFORWARDS._serialized_end=26879 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26676 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26760 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26762 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26810 + _LISTPAYSREQUEST._serialized_start=26882 + _LISTPAYSREQUEST._serialized_end=27101 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27007 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27062 + _LISTPAYSRESPONSE._serialized_start=27103 + _LISTPAYSRESPONSE._serialized_end=27154 + _LISTPAYSPAYS._serialized_start=27157 + _LISTPAYSPAYS._serialized_end=27676 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27488 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27547 + _PINGREQUEST._serialized_start=27678 + _PINGREQUEST._serialized_end=27767 + _PINGRESPONSE._serialized_start=27769 + _PINGRESPONSE._serialized_end=27799 + _SIGNMESSAGEREQUEST._serialized_start=27801 + _SIGNMESSAGEREQUEST._serialized_end=27838 + _SIGNMESSAGERESPONSE._serialized_start=27840 + _SIGNMESSAGERESPONSE._serialized_end=27910 + _STOPREQUEST._serialized_start=27912 + _STOPREQUEST._serialized_end=27925 + _STOPRESPONSE._serialized_start=27927 + _STOPRESPONSE._serialized_end=27941 + _NODE._serialized_start=27944 + _NODE._serialized_end=30872 # @@protoc_insertion_point(module_scope) diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index 47a1dd9af340..55c182c42d29 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -46,6 +46,7 @@ On success, an object containing **payments** is returned. It is an array of ob - **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 +- **completed\_at** (u64, optional): the UNIX timestamp showing when this payment was completed - **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash - **payment\_preimage** (hex, optional): proof of payment (always 64 characters) - **label** (string, optional): the label, if given to sendpay @@ -102,4 +103,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:08dbf42e6d4bd63f9b6e7a45112b348c84d6192d3ff3087e5e02b4a4788e5605) +[comment]: # ( SHA256STAMP:1ce2241eeae759ed5566342fb7810e62fa2c618f2465314f17376ebe9b6d24f8) diff --git a/doc/lightning-listpays.7.md b/doc/lightning-listpays.7.md index 2aa4d07c01ac..2a334a7af260 100644 --- a/doc/lightning-listpays.7.md +++ b/doc/lightning-listpays.7.md @@ -23,16 +23,12 @@ On success, an object containing **pays** is returned. It is an array of object - **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 +- **completed\_at** (u64, optional): the UNIX timestamp showing when this payment was completed - **label** (string, optional): the label, if given to sendpay - **bolt11** (string, optional): the bolt11 string (if pay supplied one) - **description** (string, optional): the description matching the bolt11 description hash (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) @@ -61,4 +57,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:64fd1d2a8673b2a4189623aa42d44384061ff66ba7c8918af40baf92ac29a889) +[comment]: # ( SHA256STAMP:1175415c0f9398e1087d68dd75266bf894249053a4e57f16b8ee16cf5ffa414f) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index ae642b43cab6..e2eee527ae48 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:2cd3f5a0d494ea4aaef6eb5ea9ff467da14198c0fa4d9fee6b45e72568a2d481) +[comment]: # ( SHA256STAMP:68af9f1edf2ddc78a7daad1bb72aa773b82c5103b0ba706ef83de36a11cabe26) diff --git a/doc/lightning-sendpay.7.md b/doc/lightning-sendpay.7.md index fc9a656311ae..08c14bfe0579 100644 --- a/doc/lightning-sendpay.7.md +++ b/doc/lightning-sendpay.7.md @@ -76,6 +76,7 @@ On success, an object is returned, containing: - **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash - **amount\_msat** (msat, optional): The amount delivered to destination (if known) - **destination** (pubkey, optional): the final destination of the payment if known +- **completed\_at** (u64, optional): the UNIX timestamp showing when this payment was completed - **label** (string, optional): the *label*, if given to sendpay - **partid** (u64, optional): the *partid*, if given to sendpay - **bolt11** (string, optional): the bolt11 string (if supplied) @@ -142,4 +143,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:4878733d02711f919c49740652a3723fbcc78a0cd865b71385db965c878d2b77) +[comment]: # ( SHA256STAMP:c129f537b1af8a5dc767a25a72be419634cb21ebc26a9e6b9bb091db8db7e6ca) diff --git a/doc/lightning-waitsendpay.7.md b/doc/lightning-waitsendpay.7.md index 0ec7abca750b..8144833a640d 100644 --- a/doc/lightning-waitsendpay.7.md +++ b/doc/lightning-waitsendpay.7.md @@ -43,6 +43,7 @@ On success, an object is returned, containing: - **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash - **amount\_msat** (msat, optional): The amount delivered to destination (if known) - **destination** (pubkey, optional): the final destination of the payment if known +- **completed\_at** (number, optional): the UNIX timestamp showing when this payment was completed - **label** (string, optional): the label, if given to sendpay - **partid** (u64, optional): the *partid*, if given to sendpay - **bolt11** (string, optional): the bolt11 string (if pay supplied one) @@ -103,4 +104,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:b87ddb42fd2b1182ef11101f0298be2e4de9b942fd3b8b4b169d1bdc849f48a8) +[comment]: # ( SHA256STAMP:f4dbe3ecc88a294f7bb983a2f2b8e9613e440e4564580e51dd30fc83ba218a91) diff --git a/doc/schemas/delpay.schema.json b/doc/schemas/delpay.schema.json index 0f31780f2fbd..fee03413c992 100644 --- a/doc/schemas/delpay.schema.json +++ b/doc/schemas/delpay.schema.json @@ -64,6 +64,10 @@ "type": "u64", "description": "the UNIX timestamp showing when this payment was initiated" }, + "completed_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was completed" + }, "groupid": { "type": "u64", "description": "Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash" diff --git a/doc/schemas/listpays.schema.json b/doc/schemas/listpays.schema.json index ad74829abaa6..9fc6cf83582a 100644 --- a/doc/schemas/listpays.schema.json +++ b/doc/schemas/listpays.schema.json @@ -40,6 +40,10 @@ "type": "u64", "description": "the UNIX timestamp showing when this payment was initiated" }, + "completed_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was completed" + }, "label": { "type": "string", "description": "the label, if given to sendpay" @@ -58,45 +62,6 @@ } }, "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": {}, - "description": {}, - "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" - } - } - } - }, { "if": { "properties": { @@ -111,6 +76,7 @@ "then": { "additionalProperties": false, "required": [ + "amount_sent_msat", "preimage" ], "properties": { @@ -118,6 +84,7 @@ "status": {}, "destination": {}, "created_at": {}, + "completed_at": {}, "label": {}, "bolt11": {}, "description": {}, @@ -150,7 +117,9 @@ }, "then": { "additionalProperties": false, - "required": [], + "required": [ + "amount_sent_msat" + ], "properties": { "payment_hash": {}, "status": {}, diff --git a/doc/schemas/listsendpays.schema.json b/doc/schemas/listsendpays.schema.json index 3a45a3b7de81..dc43fde7f38a 100644 --- a/doc/schemas/listsendpays.schema.json +++ b/doc/schemas/listsendpays.schema.json @@ -108,6 +108,7 @@ "amount_msat": {}, "destination": {}, "created_at": {}, + "completed_at": {}, "msatoshi_sent": {}, "amount_sent_msat": {}, "label": {}, @@ -147,6 +148,7 @@ "amount_msat": {}, "destination": {}, "created_at": {}, + "completed_at": {}, "msatoshi_sent": {}, "amount_sent_msat": {}, "label": {}, diff --git a/doc/schemas/sendpay.schema.json b/doc/schemas/sendpay.schema.json index dc2b7c80a23f..623fc66d4ef0 100644 --- a/doc/schemas/sendpay.schema.json +++ b/doc/schemas/sendpay.schema.json @@ -47,6 +47,10 @@ "type": "u64", "description": "the UNIX timestamp showing when this payment was initiated" }, + "completed_at": { + "type": "u64", + "description": "the UNIX timestamp showing when this payment was completed" + }, "msatoshi_sent": { "deprecated": true }, @@ -97,6 +101,7 @@ "amount_msat": {}, "destination": {}, "created_at": {}, + "completed_at": {}, "msatoshi_sent": {}, "amount_sent_msat": {}, "label": {}, @@ -137,6 +142,7 @@ "amount_msat": {}, "destination": {}, "created_at": {}, + "completed_at": {}, "msatoshi_sent": {}, "amount_sent_msat": {}, "label": {}, diff --git a/doc/schemas/waitsendpay.schema.json b/doc/schemas/waitsendpay.schema.json index 7a086e0fd6af..afb9b8af4363 100644 --- a/doc/schemas/waitsendpay.schema.json +++ b/doc/schemas/waitsendpay.schema.json @@ -46,6 +46,10 @@ "type": "u64", "description": "the UNIX timestamp showing when this payment was initiated" }, + "completed_at": { + "type": "number", + "description": "the UNIX timestamp showing when this payment was completed" + }, "msatoshi_sent": { "deprecated": true }, @@ -96,6 +100,7 @@ "amount_msat": {}, "destination": {}, "created_at": {}, + "completed_at": {}, "msatoshi_sent": {}, "amount_sent_msat": {}, "label": {}, diff --git a/lightningd/pay.c b/lightningd/pay.c index 44f0bf1c7644..3966ab531ecf 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -128,7 +128,9 @@ void json_add_payment_fields(struct json_stream *response, json_add_amount_msat_compat(response, t->msatoshi_sent, "msatoshi_sent", "amount_sent_msat"); - json_add_u64(response, "created_at", t->timestamp); + json_add_u32(response, "created_at", t->timestamp); + if (t->completed_at) + json_add_u32(response, "completed_at", *t->completed_at); switch (t->status) { case PAYMENT_PENDING: @@ -1101,6 +1103,8 @@ send_payment_core(struct lightningd *ld, payment->route_nodes = tal_steal(payment, route_nodes); payment->route_channels = tal_steal(payment, route_channels); payment->failonion = NULL; + payment->completed_at = NULL; + if (label != NULL) payment->label = tal_strdup(payment, label); else diff --git a/tests/test_pay.py b/tests/test_pay.py index e10cf0afb07b..72745d5f4c9d 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3948,6 +3948,7 @@ def test_bolt11_null_after_pay(node_factory, bitcoind): assert(pays[0]["bolt11"] == invl1) assert('amount_msat' in pays[0] and pays[0]['amount_msat'] == amt) assert('created_at' in pays[0]) + assert('completed_at' in pays[0]) def test_mpp_presplit_routehint_conflict(node_factory, bitcoind): diff --git a/wallet/wallet.c b/wallet/wallet.c index 8ba81f9ffa73..5306afdb531e 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3260,6 +3260,12 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, } else payment->local_offer_id = NULL; + if (!db_col_is_null(stmt, "completed_at")) { + payment->completed_at = tal(payment, u32); + *payment->completed_at = db_col_int(stmt, "completed_at"); + } else + payment->completed_at = NULL; + payment->groupid = db_col_u64(stmt, "groupid"); return payment; @@ -3298,6 +3304,7 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, ", partid" ", local_offer_id" ", groupid" + ", completed_at" " FROM payments" " WHERE payment_hash = ?" " AND partid = ? AND groupid=?")); @@ -3535,6 +3542,7 @@ wallet_payment_list(const tal_t *ctx, ", partid" ", local_offer_id" ", groupid" + ", completed_at" " FROM payments" " WHERE" " (payment_hash = ? OR 1=?) AND" @@ -3602,6 +3610,7 @@ wallet_payments_by_offer(const tal_t *ctx, ", partid" ", local_offer_id" ", groupid" + ", completed_at" " FROM payments" " WHERE local_offer_id = ?;")); db_bind_sha256(stmt, 0, local_offer_id); diff --git a/wallet/wallet.h b/wallet/wallet.h index f6cf987bb94d..4ce09a13d16b 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -334,6 +334,7 @@ struct wallet_payment { struct list_node list; u64 id; u32 timestamp; + u32 *completed_at; /* The combination of these three fields is unique: */ struct sha256 payment_hash; From a80211e960f6e8135170d9753953e1703d85403c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 12 Jul 2022 17:03:58 +0200 Subject: [PATCH 1344/1530] doc: Update generated artifacts to match master --- .msggen.json | 2 ++ cln-grpc/proto/node.proto | 4 ++-- cln-grpc/src/convert.rs | 4 ++-- cln-rpc/src/model.rs | 8 ++++---- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.msggen.json b/.msggen.json index a9180ad84375..b56f3dab4c76 100644 --- a/.msggen.json +++ b/.msggen.json @@ -672,7 +672,9 @@ "ListPays.pays[].destination": 3, "ListPays.pays[].erroronion": 10, "ListPays.pays[].label": 5, + "ListPays.pays[].number_of_parts": 14, "ListPays.pays[].payment_hash": 1, + "ListPays.pays[].preimage": 13, "ListPays.pays[].status": 2 }, "ListpaysRequest": { diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 4f10134b3e96..a1bb1545b7b5 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1270,8 +1270,8 @@ message ListpaysPays { optional string bolt11 = 6; optional string description = 11; optional string bolt12 = 7; - optional Amount amount_msat = 8; - optional Amount amount_sent_msat = 9; + optional bytes preimage = 13; + optional uint64 number_of_parts = 14; optional bytes erroronion = 10; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 7d3896bf6a52..c75ac0f2b590 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -931,8 +931,8 @@ impl From<&responses::ListpaysPays> for pb::ListpaysPays { bolt11: c.bolt11.clone(), // Rule #2 for type string? description: c.description.clone(), // Rule #2 for type string? bolt12: c.bolt12.clone(), // Rule #2 for type string? - amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - amount_sent_msat: c.amount_sent_msat.map(|f| f.into()), // Rule #2 for type msat? + preimage: c.preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + number_of_parts: c.number_of_parts.clone(), // Rule #2 for type u64? erroronion: c.erroronion.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 817aa0aa66eb..adcca4b90366 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -2739,10 +2739,10 @@ pub mod responses { pub description: Option, #[serde(alias = "bolt12", skip_serializing_if = "Option::is_none")] pub bolt12: Option, - #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] - pub amount_msat: Option, - #[serde(alias = "amount_sent_msat", skip_serializing_if = "Option::is_none")] - pub amount_sent_msat: Option, + #[serde(alias = "preimage", skip_serializing_if = "Option::is_none")] + pub preimage: Option, + #[serde(alias = "number_of_parts", skip_serializing_if = "Option::is_none")] + pub number_of_parts: Option, #[serde(alias = "erroronion", skip_serializing_if = "Option::is_none")] pub erroronion: Option, } From 246e1fb0b3db11af1c9286e0f0ae635658c14f33 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 4 Jul 2022 15:56:37 +0200 Subject: [PATCH 1345/1530] wallet: Set the `completed_at` timestamp when updating the status --- wallet/wallet.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index 5306afdb531e..058aed4e0346 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3328,6 +3328,10 @@ void wallet_payment_set_status(struct wallet *wallet, { struct db_stmt *stmt; struct wallet_payment *payment; + u32 completed_at = 0; + + if (newstatus != PAYMENT_PENDING) + completed_at = time_now().ts.tv_sec; /* We can only fail an unstored payment! */ payment = find_unstored_payment(wallet, payment_hash, partid); @@ -3338,13 +3342,18 @@ void wallet_payment_set_status(struct wallet *wallet, } stmt = db_prepare_v2(wallet->db, - SQL("UPDATE payments SET status=? " + SQL("UPDATE payments SET status=?, completed_at=? " "WHERE payment_hash=? AND partid=? AND groupid=?")); db_bind_int(stmt, 0, wallet_payment_status_in_db(newstatus)); - db_bind_sha256(stmt, 1, payment_hash); - db_bind_u64(stmt, 2, partid); - db_bind_u64(stmt, 3, groupid); + if (completed_at != 0) { + db_bind_u64(stmt, 1, completed_at); + } else { + db_bind_null(stmt, 1); + } + db_bind_sha256(stmt, 2, payment_hash); + db_bind_u64(stmt, 3, partid); + db_bind_u64(stmt, 4, groupid); db_exec_prepared_v2(take(stmt)); if (preimage) { From f64d755e435dd739b041ff05c062959e4f84c048 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 4 Jul 2022 18:02:10 +0200 Subject: [PATCH 1346/1530] pay: Aggregate `completed_at` in `listpays` and `pay` The completion of a successful payment is defined as the first completion of a successful part, hence we just collect the minimum completion time of successes. Changelog-Added: JSON-RPC: `pay` and `listpays` now lists the completion time. --- plugins/pay.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/plugins/pay.c b/plugins/pay.c index 57a68ead80cd..d086103680e6 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -290,6 +290,10 @@ struct pay_mpp { /* Timestamp of the first part */ u32 timestamp; + /* Completion timestamp. The lowest `completed_at` value for a + * successful part. */ + u64 success_at; + /* The destination of the payment, if specified. */ const jsmntok_t *destination; @@ -391,6 +395,9 @@ static void add_new_entry(struct json_stream *ret, json_add_u32(ret, "created_at", pm->timestamp); + if (pm->success_at < UINT64_MAX) + json_add_u64(ret, "completed_at", pm->success_at); + if (pm->label) json_add_tok(ret, "label", pm->label, buf); if (pm->preimage) @@ -430,10 +437,12 @@ static struct command_result *listsendpays_done(struct command *cmd, "Unexpected non-array result from listsendpays"); json_for_each_arr(i, t, arr) { - const jsmntok_t *status, *invstrtok, *hashtok, *createdtok, *grouptok; + const jsmntok_t *status, *invstrtok, *hashtok, *createdtok, + *completedtok, *grouptok; const char *invstr = invstring; struct sha256 payment_hash; u32 created_at; + u64 completed_at; u64 groupid; struct pay_sort_key key; @@ -442,9 +451,15 @@ static struct command_result *listsendpays_done(struct command *cmd, invstrtok = json_get_member(buf, t, "bolt12"); hashtok = json_get_member(buf, t, "payment_hash"); createdtok = json_get_member(buf, t, "created_at"); + completedtok = json_get_member(buf, t, "completed_at"); assert(hashtok != NULL); assert(createdtok != NULL); + if (completedtok != NULL) + json_to_u64(buf, completedtok, &completed_at); + else + completed_at = UINT64_MAX; + grouptok = json_get_member(buf, t, "groupid"); if (grouptok != NULL) json_to_u64(buf, grouptok, &groupid); @@ -475,6 +490,7 @@ static struct command_result *listsendpays_done(struct command *cmd, pm->timestamp = created_at; pm->sortkey.payment_hash = pm->payment_hash; pm->sortkey.groupid = groupid; + pm->success_at = UINT64_MAX; pay_map_add(&pay_map, pm); // First time we see the groupid we add it to the order // array, so we can retrieve them in the correct order. @@ -495,6 +511,11 @@ static struct command_result *listsendpays_done(struct command *cmd, pm->preimage = json_get_member(buf, t, "payment_preimage"); pm->state |= PAYMENT_COMPLETE; + + /* We count down from UINT64_MAX. */ + if (pm->success_at > completed_at) + pm->success_at = completed_at; + } else if (json_tok_streq(buf, status, "pending")) { add_amount_sent(cmd->plugin, pm->invstring, pm, buf, t); pm->num_nonfailed_parts++; From 4167fe8dd962458c9ceacdb6c79832e3e8fad26f Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Tue, 13 Sep 2022 18:39:59 -0500 Subject: [PATCH 1347/1530] gossip_store: fix offset error The gossip_store version byte was unaccounted for in the initial traversal of gossip_store_end. This lead to an offset and a bogus message length field. As a result, an early portion of the gossip_store could have been skipped, potentially leading to gossip propagation issues downstream. Fixes #5572 #5565 Changelog-fixed: proper gossip_store operation may resolve some previous gossip propagation issues --- common/gossip_store.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/gossip_store.c b/common/gossip_store.c index 2f3f329a2d2e..9cb05951693b 100644 --- a/common/gossip_store.c +++ b/common/gossip_store.c @@ -199,8 +199,8 @@ size_t find_gossip_store_end(int gossip_store_fd, size_t off) } buf; int r; - while ((r = read(gossip_store_fd, &buf, - sizeof(buf.hdr) + sizeof(buf.type))) + while ((r = pread(gossip_store_fd, &buf, + sizeof(buf.hdr) + sizeof(buf.type), off)) == sizeof(buf.hdr) + sizeof(buf.type)) { u32 msglen = be32_to_cpu(buf.hdr.len) & GOSSIP_STORE_LEN_MASK; @@ -209,7 +209,6 @@ size_t find_gossip_store_end(int gossip_store_fd, size_t off) break; off += sizeof(buf.hdr) + msglen; - lseek(gossip_store_fd, off, SEEK_SET); } return off; } From 746b5f3691525a37f837fdabd8bd967e8683834b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:43:20 +0930 Subject: [PATCH 1348/1530] Makefile: fix msggen regeneration when schemas change. 1. It depends on both request and reply schemas. 2. Wildcards aren't natively expanded in make, so use $(wildcard). Signed-off-by: Rusty Russell --- cln-rpc/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cln-rpc/Makefile b/cln-rpc/Makefile index 267b7eadf383..9f2595a926d3 100644 --- a/cln-rpc/Makefile +++ b/cln-rpc/Makefile @@ -4,10 +4,10 @@ cln-rpc-wrongdir: CLN_RPC_EXAMPLES := target/debug/examples/cln-rpc-getinfo CLN_RPC_GENALL = cln-rpc/src/model.rs CLN_RPC_SOURCES = $(shell find cln-rpc -name *.rs) ${CLN_RPC_GENALL} -JSON_SCHEMA = doc/schemas/*.schema.json +JSON_SCHEMAS = $(wildcard doc/schemas/*.request.json doc/schemas/*.schema.json) DEFAULT_TARGETS += $(CLN_RPC_EXAMPLES) $(CLN_RPC_GENALL) -$(CLN_RPC_GENALL): $(JSON_SCHEMA) +$(CLN_RPC_GENALL): $(JSON_SCHEMAS) PYTHONPATH=contrib/msggen python3 contrib/msggen/msggen/__main__.py target/debug/examples/cln-rpc-getinfo: $(shell find cln-rpc -name *.rs) From 897245e3b7646987980a3246de03c089e3e82bd1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:44:20 +0930 Subject: [PATCH 1349/1530] pytest: test for escapes in commando values. Signed-off-by: Rusty Russell --- tests/test_plugin.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index d9f7ad9ac3dc..ddd151317552 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2636,6 +2636,7 @@ def test_commando(node_factory, executor): assert exc_info.value.error['data']['erring_index'] == 0 +@pytest.mark.xfail(reason="Escapes in restrictions are broken", strict=True) def test_commando_rune(node_factory): l1, l2 = node_factory.get_nodes(2) @@ -2684,6 +2685,25 @@ def test_commando_rune(node_factory): assert rune9['rune'] == 'O8Zr-ULTBKO3_pKYz0QKE9xYl1vQ4Xx9PtlHuist9Rk9NCZwbnVtPTAmcmF0ZT0zJnJhdGU9MQ==' assert rune9['unique_id'] == '4' + # Test rune with \|. + weirdrune = l1.rpc.commando_rune(restrictions=["method=invoice", + "pnamedescription=@tipjar\\|jb55@sendsats.lol"]) + with pytest.raises(RpcError, match='Not authorized:'): + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': weirdrune['rune'], + 'method': 'invoice', + 'params': {"amount_msat": "any", + "label": "lbl", + "description": "@tipjar\\|jb55@sendsats.lol"}}) + l2.rpc.call(method='commando', + payload={'peer_id': l1.info['id'], + 'rune': weirdrune['rune'], + 'method': 'invoice', + 'params': {"amount_msat": "any", + "label": "lbl", + "description": "@tipjar|jb55@sendsats.lol"}}) + runedecodes = ((rune1, []), (rune2, [{'alternatives': ['method^list', 'method^get', 'method=summary'], 'summary': "method (of command) starts with 'list' OR method (of command) starts with 'get' OR method (of command) equal to 'summary'"}, From 1f9730748cf747655783adacabba4ae6c65d53aa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 14:59:27 +0930 Subject: [PATCH 1350/1530] CCAN: update to get latest rune decode fix. We didn't handle \ in fields properly, unless they were one-char long. Signed-off-by: Rusty Russell --- ccan/README | 2 +- ccan/ccan/rune/coding.c | 36 +++++++++++++----------- ccan/ccan/rune/rune.h | 21 ++++++++++++++ ccan/ccan/rune/test/run-altern-escape.c | 37 +++++++++++++++++++++++++ ccan/ccan/rune/test/run.c | 8 +++--- ccan/ccan/tal/tal.c | 2 +- 6 files changed, 84 insertions(+), 22 deletions(-) create mode 100644 ccan/ccan/rune/test/run-altern-escape.c diff --git a/ccan/README b/ccan/README index 4fab455bc633..ad5b8d61fff5 100644 --- a/ccan/README +++ b/ccan/README @@ -1,3 +1,3 @@ CCAN imported from http://ccodearchive.net. -CCAN version: init-2545-g7b11e744 +CCAN version: init-2548-gab87e56b diff --git a/ccan/ccan/rune/coding.c b/ccan/ccan/rune/coding.c index b9255b0b5b02..f4d110283245 100644 --- a/ccan/ccan/rune/coding.c +++ b/ccan/ccan/rune/coding.c @@ -98,7 +98,7 @@ static void rune_altern_encode(const struct rune_altern *altern, break; esc[1] = p[len]; cb(esc, 2, arg); - p++; + p += len + 1; } } @@ -184,7 +184,7 @@ static bool pull_char(const char **data, size_t *len, char *c) return true; } -static bool is_valid_cond(enum rune_condition cond) +bool rune_condition_is_valid(enum rune_condition cond) { switch (cond) { case RUNE_COND_IF_MISSING: @@ -203,31 +203,35 @@ static bool is_valid_cond(enum rune_condition cond) return false; } +size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen) +{ + for (size_t i = 0; i < alternstrlen; i++) { + if (cispunct(alternstr[i])) + return i; + } + return alternstrlen; +} + /* Sets *more on success: true if another altern follows */ static struct rune_altern *rune_altern_decode(const tal_t *ctx, const char **data, size_t *len, bool *more) { struct rune_altern *alt = tal(ctx, struct rune_altern); - const char *strstart = *data; char *value; - size_t strlen = 0; + size_t strlen; char c; - /* Swallow field up to conditional */ - for (;;) { - if (!pull_char(data, len, &c)) - return tal_free(alt); - if (cispunct(c)) - break; - strlen++; - } + /* Swallow field up to possible conditional */ + strlen = rune_altern_fieldname_len(*data, *len); + alt->fieldname = tal_strndup(alt, *data, strlen); + *data += strlen; + *len -= strlen; - alt->fieldname = tal_strndup(alt, strstart, strlen); - if (!is_valid_cond(c)) { - pull_invalid(data, len); + /* Grab conditional */ + if (!pull_char(data, len, &c) || !rune_condition_is_valid(c)) return tal_free(alt); - } + alt->condition = c; /* Assign worst case. */ diff --git a/ccan/ccan/rune/rune.h b/ccan/ccan/rune/rune.h index b67b78281eda..c373269aa0c9 100644 --- a/ccan/ccan/rune/rune.h +++ b/ccan/ccan/rune/rune.h @@ -376,4 +376,25 @@ char *rune_to_string(const tal_t *ctx, const struct rune *rune); struct rune_restr *rune_restr_from_string(const tal_t *ctx, const char *str, size_t len); + +/** + * rune_condition_is_valid: is this a valid condition? + * @cond: potential condition character. + * + * Returns true if it's one of enum rune_condition. + */ +bool rune_condition_is_valid(enum rune_condition cond); + +/** + * rune_altern_fieldname_len: how much of this string is condition? + * @alternstr: potential alternative string + * @alternstrlen: length + * + * This helps parsing your own runes. + * + * Returns the first possible condition (check with rune_condition_is_valid) + * or alternstrlen if none found. + */ +size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen); + #endif /* CCAN_RUNE_RUNE_H */ diff --git a/ccan/ccan/rune/test/run-altern-escape.c b/ccan/ccan/rune/test/run-altern-escape.c new file mode 100644 index 000000000000..550cafae4d31 --- /dev/null +++ b/ccan/ccan/rune/test/run-altern-escape.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include + +int main(void) +{ + static const u8 secret_zero[16]; + struct rune *rune; + struct rune_restr *restr; + const tal_t *ctx = tal(NULL, char); + + plan_tests(9); + restr = rune_restr_from_string(ctx, "desc=@tipjar\\|jb55@sendsats.lol", + strlen("desc=@tipjar\\|jb55@sendsats.lol")); + ok1(tal_count(restr->alterns) == 1); + ok1(restr->alterns[0]->condition == '='); + ok1(streq(restr->alterns[0]->fieldname, "desc")); + ok1(streq(restr->alterns[0]->value, "@tipjar|jb55@sendsats.lol")); + + rune = rune_new(ctx, secret_zero, sizeof(secret_zero), NULL); + rune_add_restr(rune, take(restr)); + + /* Converting via base64 should not change it! */ + rune = rune_from_base64(ctx, rune_to_base64(ctx, rune)); + ok1(tal_count(rune->restrs) == 1); + restr = rune->restrs[0]; + ok1(tal_count(restr->alterns) == 1); + ok1(restr->alterns[0]->condition == '='); + ok1(streq(restr->alterns[0]->fieldname, "desc")); + ok1(streq(restr->alterns[0]->value, "@tipjar|jb55@sendsats.lol")); + + tal_free(ctx); + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/rune/test/run.c b/ccan/ccan/rune/test/run.c index 86737b86be11..e532655284db 100644 --- a/ccan/ccan/rune/test/run.c +++ b/ccan/ccan/rune/test/run.c @@ -83,12 +83,12 @@ int main(void) ok1(rune_is_derived_anyversion(rune1, rune2) == NULL); restr = rune_restr_new(NULL); - for (size_t i = 4; parts[i]; i+=3) { + for (size_t j = 4; parts[j]; j+=3) { struct rune_altern *alt; alt = rune_altern_new(NULL, - parts[i], - parts[i+1][0], - parts[i+2]); + parts[j], + parts[j+1][0], + parts[j+2]); rune_restr_add_altern(restr, take(alt)); } rune_add_restr(rune1, take(restr)); diff --git a/ccan/ccan/tal/tal.c b/ccan/ccan/tal/tal.c index 69270c6ffb8c..2d05dd93f73b 100644 --- a/ccan/ccan/tal/tal.c +++ b/ccan/ccan/tal/tal.c @@ -777,7 +777,7 @@ void *tal_dup_(const tal_t *ctx, const void *p, size_t size, (void)taken(p); return NULL; } - + if (!adjust_size(&nbytes, n)) { if (taken(p)) tal_free(p); From d57d87ea3a5f5d30e855cdc6e4a131ee3af70f62 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 14:59:59 +0930 Subject: [PATCH 1351/1530] commando: unmangle JSON. JSON needs to escape \, since it can't be in front of anything unexpected, so no \|. So we need to return \\ to \, and in theory handle \n etc. Changelog-Fixed: JSON-RPC: `commando-rune` now handles \\ escapes properly. Signed-off-by: Rusty Russell --- plugins/commando.c | 26 +++++++++++++++++++------- tests/test_plugin.py | 1 - 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/plugins/commando.c b/plugins/commando.c index 7ed017163b92..e844c319d5f3 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -762,6 +762,21 @@ static struct rune_restr **readonly_restrictions(const tal_t *ctx) return restrs; } +/* \| is not valid JSON, so they use \\|: undo it! */ +static struct rune_restr *rune_restr_from_json(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok) +{ + const char *unescape; + struct json_escape *e = json_escape_string_(tmpctx, + buffer + tok->start, + tok->end - tok->start); + unescape = json_escape_unescape(tmpctx, e); + if (!unescape) + return NULL; + return rune_restr_from_string(ctx, unescape, strlen(unescape)); +} + static struct command_result *param_restrictions(struct command *cmd, const char *name, const char *buffer, @@ -776,18 +791,15 @@ static struct command_result *param_restrictions(struct command *cmd, *restrs = tal_arr(cmd, struct rune_restr *, tok->size); json_for_each_arr(i, t, tok) { - (*restrs)[i] = rune_restr_from_string(*restrs, - buffer + t->start, - t->end - t->start); - if (!(*restrs)[i]) + (*restrs)[i] = rune_restr_from_json(*restrs, buffer, t); + if (!(*restrs)[i]) { return command_fail_badparam(cmd, name, buffer, t, "not a valid restriction"); + } } } else { *restrs = tal_arr(cmd, struct rune_restr *, 1); - (*restrs)[0] = rune_restr_from_string(*restrs, - buffer + tok->start, - tok->end - tok->start); + (*restrs)[0] = rune_restr_from_json(*restrs, buffer, tok); if (!(*restrs)[0]) return command_fail_badparam(cmd, name, buffer, tok, "not a valid restriction"); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ddd151317552..963256105618 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2636,7 +2636,6 @@ def test_commando(node_factory, executor): assert exc_info.value.error['data']['erring_index'] == 0 -@pytest.mark.xfail(reason="Escapes in restrictions are broken", strict=True) def test_commando_rune(node_factory): l1, l2 = node_factory.get_nodes(2) From a6d4756d0876fba4d9739d22b75cc314dce23206 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 15:00:26 +0930 Subject: [PATCH 1352/1530] commando: make rune alternatives a JSON array. This avoids having to escape | or &, though we still allow that for the deprecation period. To detect deprecated usage, we insist that alternatives are *always* an array (which could be loosened later), but that also means that restrictions must *always* be an array for now. Before: ``` # invoice, description either A or B lightning-cli commando-rune '["method=invoice","pnamedescription=A|pnamedescription=B"]' # invoice, description literally 'A|B' lightning-cli commando-rune '["method=invoice","pnamedescription=A\\|B"]' ``` After: ``` # invoice, description either A or B lightning-cli commando-rune '[["method=invoice"],["pnamedescription=A", "pnamedescription=B"]]' # invoice, description literally 'A|B' lightning-cli commando-rune '[["method=invoice"],["pnamedescription=A|B"]]' ``` Changelog-Deprecated: JSON-RPC: `commando-rune` restrictions is always an array, each element an array of alternatives. Replaces a string with `|`-separators, so no escaping necessary except for `\\`. --- doc/lightning-commando-rune.7.md | 25 +++++------ doc/schemas/commando-rune.request.json | 10 ++++- plugins/commando.c | 61 +++++++++++++++++++++++--- tests/test_plugin.py | 26 +++++------ 4 files changed, 87 insertions(+), 35 deletions(-) diff --git a/doc/lightning-commando-rune.7.md b/doc/lightning-commando-rune.7.md index 3311b5fb4c53..04b84b0a7035 100644 --- a/doc/lightning-commando-rune.7.md +++ b/doc/lightning-commando-rune.7.md @@ -23,11 +23,10 @@ If *rune* is supplied, the restrictions are simple appended to that *restrictions* can be the string "readonly" (creates a rune which allows most *get* and *list* commands, and the *summary* command), or -an array of restrictions, or a single resriction. +an array of restrictions. -Each restriction is a set of one or more alternatives, such as "method -is listpeers", or "method is listpeers OR time is before 2023". -Alternatives use a simple language to examine the command which is +Each restriction is an array of one or more alternatives, such as "method +is listpeers", or "method is listpeers OR time is before 2023". Alternatives use a simple language to examine the command which is being run: * time: the current UNIX time, e.g. "time<1656759180". @@ -41,10 +40,10 @@ being run: RESTRICTION FORMAT ------------------ -Restrictions are one or more alternatives, separated by `|`. Each +Restrictions are one or more alternatives. Each alternative is *name* *operator* *value*. The valid names are shown -above. If a value contains `|`, `&` or `\\`, it must be preceeded by -a `\\`. +above. Note that if a value contains `\\`, it must be preceeded by another `\\` +to form valid JSON: * `=`: passes if equal ie. identical. e.g. `method=withdraw` * `/`: not equals, e.g. `method/withdraw` @@ -80,12 +79,12 @@ We can add restrictions to that rune, like so: The "readonly" restriction is a short-cut for two restrictions: -1. `method^list|method^get|method=summary`: You may call list, get or summary. -2. `method/listdatastore`: But not listdatastore: that contains sensitive stuff! +1. `["method^list", "method^get", "method=summary"]`: You may call list, get or summary. +2. `["method/listdatastore"]`: But not listdatastore: that contains sensitive stuff! We can do the same manually, like so: - $ lightning-cli commando-rune rune=KUhZzNlECC7pYsz3QVbF1TqjIUYi3oyESTI7n60hLMs9MA== restrictions='["method^list|method^get|method=summary","method/listdatastore"]' + $ lightning-cli commando-rune rune=KUhZzNlECC7pYsz3QVbF1TqjIUYi3oyESTI7n60hLMs9MA== restrictions='[["method^list", "method^get", "method=summary"],["method/listdatastore"]]' { "rune": "NbL7KkXcPQsVseJ9TdJNjJK2KsPjnt_q4cE_wvc873I9MCZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl", "unique_id": "0" @@ -95,7 +94,7 @@ Let's create a rune which lets a specific peer (024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605) run "listpeers" on themselves: - $ lightning-cli commando-rune restrictions='["id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605","method=listpeers","pnum=1","pnameid=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605|parr0=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605"]' + $ lightning-cli commando-rune restrictions='[["id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605"],["method=listpeers"],["pnum=1"],["pnameid=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605","parr0=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605"]]' { "rune": "FE8GHiGVvxcFqCQcClVRRiNE_XEeLYQzyG2jmqto4jM9MiZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDV8cGFycjA9MDI0YjlhMWZhOGUwMDZmMWUzOTM3ZjY1ZjY2YzQwOGU2ZGE4ZTFjYTcyOGVhNDMyMjJhNzM4MWRmMWNjNDQ5NjA1", "unique_id": "2" @@ -103,7 +102,7 @@ run "listpeers" on themselves: This allows `listpeers` with 1 argument (`pnum=1`), which is either by name (`pnameid`), or position (`parr0`). We could shorten this in several ways: either allowing only positional or named parameters, or by testing the start of the parameters only. Here's an example which only checks the first 9 bytes of the `listpeers` parameter: - $ lightning-cli commando-rune restrictions='["id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605","method=listpeers","pnum=1","pnameid^024b9a1fa8e006f1e393|parr0^024b9a1fa8e006f1e393"]' + $ lightning-cli commando-rune restrictions='[["id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605"],["method=listpeers"],["pnum=1"],["pnameid^024b9a1fa8e006f1e393", "parr0^024b9a1fa8e006f1e393"]' { "rune": "fTQnfL05coEbiBO8SS0cvQwCcPLxE9c02pZCC6HRVEY9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5Mw==", "unique_id": "3" @@ -114,7 +113,7 @@ it only be usable for 24 hours from now (`time<`), and that it can only be used twice a minute (`rate=2`). `date +%s` can give us the current time in seconds: - $ lightning-cli commando-rune rune=fTQnfL05coEbiBO8SS0cvQwCcPLxE9c02pZCC6HRVEY9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5Mw== restrictions='["time<'$(($(date +%s) + 24*60*60))'","rate=2"]' + $ lightning-cli commando-rune rune=fTQnfL05coEbiBO8SS0cvQwCcPLxE9c02pZCC6HRVEY9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5Mw== restrictions='[["time<'$(($(date +%s) + 24*60*60))'","rate=2"]]' { "rune": "tU-RLjMiDpY2U0o3W1oFowar36RFGpWloPbW9-RuZdo9MyZpZD0wMjRiOWExZmE4ZTAwNmYxZTM5MzdmNjVmNjZjNDA4ZTZkYThlMWNhNzI4ZWE0MzIyMmE3MzgxZGYxY2M0NDk2MDUmbWV0aG9kPWxpc3RwZWVycyZwbnVtPTEmcG5hbWVpZF4wMjRiOWExZmE4ZTAwNmYxZTM5M3xwYXJyMF4wMjRiOWExZmE4ZTAwNmYxZTM5MyZ0aW1lPDE2NTY5MjA1MzgmcmF0ZT0y", "unique_id": "3" diff --git a/doc/schemas/commando-rune.request.json b/doc/schemas/commando-rune.request.json index a93ae80adc79..ef5678a9b2bc 100644 --- a/doc/schemas/commando-rune.request.json +++ b/doc/schemas/commando-rune.request.json @@ -14,12 +14,18 @@ "type": "array", "description": "array of restrictions to add to rune", "items": { - "type": "string" + "type": "array", + "items": { + "type": "string" + } } }, { "type": "string", - "description": "single restrictions to add to rune, or readonly." + "enum": [ + "readonly" + ], + "description": "readonly string to indicate standard readonly restrictions." } ] } diff --git a/plugins/commando.c b/plugins/commando.c index e844c319d5f3..820d31ac2b38 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -762,11 +762,14 @@ static struct rune_restr **readonly_restrictions(const tal_t *ctx) return restrs; } -/* \| is not valid JSON, so they use \\|: undo it! */ -static struct rune_restr *rune_restr_from_json(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok) +static struct rune_altern *rune_altern_from_json(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok) { + struct rune_altern *alt; + size_t condoff; + /* We still need to unescape here, for \\ -> \. JSON doesn't + * allow unnecessary \ */ const char *unescape; struct json_escape *e = json_escape_string_(tmpctx, buffer + tok->start, @@ -774,7 +777,51 @@ static struct rune_restr *rune_restr_from_json(const tal_t *ctx, unescape = json_escape_unescape(tmpctx, e); if (!unescape) return NULL; - return rune_restr_from_string(ctx, unescape, strlen(unescape)); + + condoff = rune_altern_fieldname_len(unescape, strlen(unescape)); + if (!rune_condition_is_valid(unescape[condoff])) + return NULL; + + alt = tal(ctx, struct rune_altern); + alt->fieldname = tal_strndup(alt, unescape, condoff); + alt->condition = unescape[condoff]; + alt->value = tal_strdup(alt, unescape + condoff + 1); + return alt; +} + +static struct rune_restr *rune_restr_from_json(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok) +{ + const jsmntok_t *t; + size_t i; + struct rune_restr *restr; + + /* \| is not valid JSON, so they use \\|: undo it! */ + if (deprecated_apis && tok->type == JSMN_STRING) { + const char *unescape; + struct json_escape *e = json_escape_string_(tmpctx, + buffer + tok->start, + tok->end - tok->start); + unescape = json_escape_unescape(tmpctx, e); + if (!unescape) + return NULL; + return rune_restr_from_string(ctx, unescape, strlen(unescape)); + } + + restr = tal(ctx, struct rune_restr); + /* FIXME: after deprecation removed, allow singletons again! */ + if (tok->type != JSMN_ARRAY) + return NULL; + + restr->alterns = tal_arr(restr, struct rune_altern *, tok->size); + json_for_each_arr(i, t, tok) { + restr->alterns[i] = rune_altern_from_json(restr->alterns, + buffer, t); + if (!restr->alterns[i]) + return tal_free(restr); + } + return restr; } static struct command_result *param_restrictions(struct command *cmd, @@ -794,7 +841,7 @@ static struct command_result *param_restrictions(struct command *cmd, (*restrs)[i] = rune_restr_from_json(*restrs, buffer, t); if (!(*restrs)[i]) { return command_fail_badparam(cmd, name, buffer, t, - "not a valid restriction"); + "not a valid restriction (should be array)"); } } } else { @@ -802,7 +849,7 @@ static struct command_result *param_restrictions(struct command *cmd, (*restrs)[0] = rune_restr_from_json(*restrs, buffer, tok); if (!(*restrs)[0]) return command_fail_badparam(cmd, name, buffer, tok, - "not a valid restriction"); + "not a valid restriction (should be array)"); } return NULL; } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 963256105618..bf0704ccacee 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2649,11 +2649,11 @@ def test_commando_rune(node_factory): # "rune": "zKc2W88jopslgUBl0UE77aEe5PNCLn5WwqSusU_Ov3A9MA==" # $ l1-cli commando-rune restrictions=readonly # "rune": "1PJnoR9a7u4Bhglj2s7rVOWqRQnswIwUoZrDVMKcLTY9MSZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl" - # $ l1-cli commando-rune restrictions='time>1656675211' + # $ l1-cli commando-rune restrictions='[[time>1656675211]]' # "rune": "RnlWC4lwBULFaObo6ZP8jfqYRyTbfWPqcMT3qW-Wmso9MiZ0aW1lPjE2NTY2NzUyMTE=" - # $ l1-cli commando-rune restrictions='["id^022d223620a359a47ff7","method=listpeers"]' + # $ l1-cli commando-rune restrictions='[["id^022d223620a359a47ff7"],["method=listpeers"]]' # "rune": "lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz" - # $ l1-cli commando-rune lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz 'pnamelevel!|pnamelevel/io' + # $ l1-cli commando-rune lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz '[pnamelevel!,pnamelevel/io]' # "rune": "Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8=" rune1 = l1.rpc.commando_rune() @@ -2662,31 +2662,31 @@ def test_commando_rune(node_factory): rune2 = l1.rpc.commando_rune(restrictions="readonly") assert rune2['rune'] == '1PJnoR9a7u4Bhglj2s7rVOWqRQnswIwUoZrDVMKcLTY9MSZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl' assert rune2['unique_id'] == '1' - rune3 = l1.rpc.commando_rune(restrictions="time>1656675211") + rune3 = l1.rpc.commando_rune(restrictions=[["time>1656675211"]]) assert rune3['rune'] == 'RnlWC4lwBULFaObo6ZP8jfqYRyTbfWPqcMT3qW-Wmso9MiZ0aW1lPjE2NTY2NzUyMTE=' assert rune3['unique_id'] == '2' - rune4 = l1.rpc.commando_rune(restrictions=["id^022d223620a359a47ff7", "method=listpeers"]) + rune4 = l1.rpc.commando_rune(restrictions=[["id^022d223620a359a47ff7"], ["method=listpeers"]]) assert rune4['rune'] == 'lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz' assert rune4['unique_id'] == '3' - rune5 = l1.rpc.commando_rune(rune4['rune'], "pnamelevel!|pnamelevel/io") + rune5 = l1.rpc.commando_rune(rune4['rune'], [["pnamelevel!", "pnamelevel/io"]]) assert rune5['rune'] == 'Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8=' assert rune5['unique_id'] == '3' - rune6 = l1.rpc.commando_rune(rune5['rune'], "parr1!|parr1/io") + rune6 = l1.rpc.commando_rune(rune5['rune'], [["parr1!", "parr1/io"]]) assert rune6['rune'] == '2Wh6F4R51D3esZzp-7WWG51OhzhfcYKaaI8qiIonaHE9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8mcGFycjEhfHBhcnIxL2lv' assert rune6['unique_id'] == '3' - rune7 = l1.rpc.commando_rune(restrictions="pnum=0") + rune7 = l1.rpc.commando_rune(restrictions=[["pnum=0"]]) assert rune7['rune'] == 'QJonN6ySDFw-P5VnilZxlOGRs_tST1ejtd-bAYuZfjk9NCZwbnVtPTA=' assert rune7['unique_id'] == '4' - rune8 = l1.rpc.commando_rune(rune7['rune'], "rate=3") + rune8 = l1.rpc.commando_rune(rune7['rune'], [["rate=3"]]) assert rune8['rune'] == 'kSYFx6ON9hr_ExcQLwVkm1ABnvc1TcMFBwLrAVee0EA9NCZwbnVtPTAmcmF0ZT0z' assert rune8['unique_id'] == '4' - rune9 = l1.rpc.commando_rune(rune8['rune'], "rate=1") + rune9 = l1.rpc.commando_rune(rune8['rune'], [["rate=1"]]) assert rune9['rune'] == 'O8Zr-ULTBKO3_pKYz0QKE9xYl1vQ4Xx9PtlHuist9Rk9NCZwbnVtPTAmcmF0ZT0zJnJhdGU9MQ==' assert rune9['unique_id'] == '4' # Test rune with \|. - weirdrune = l1.rpc.commando_rune(restrictions=["method=invoice", - "pnamedescription=@tipjar\\|jb55@sendsats.lol"]) + weirdrune = l1.rpc.commando_rune(restrictions=[["method=invoice"], + ["pnamedescription=@tipjar|jb55@sendsats.lol"]]) with pytest.raises(RpcError, match='Not authorized:'): l2.rpc.call(method='commando', payload={'peer_id': l1.info['id'], @@ -2758,7 +2758,7 @@ def test_commando_rune(node_factory): # Replace rune3 with a more useful timestamp! expiry = int(time.time()) + 15 - rune3 = l1.rpc.commando_rune(restrictions="time<{}".format(expiry)) + rune3 = l1.rpc.commando_rune(restrictions=[["time<{}".format(expiry)]]) successes = ((rune1, "listpeers", {}), (rune2, "listpeers", {}), From 023a688e3f1ebdf1b41ea97720b0ccccf7ccbfaa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 14:43:01 +0930 Subject: [PATCH 1353/1530] lightningd: fix spurious leak report. We can (and probably should!) allocate this off tmpctx rather than off the response. Also, json_add_invoice doesn't actually match the normal pattern, so fix that. ``` E ValueError: E Node errors: E Global errors: E - Node /tmp/ltests-_vbj8az8/test_pay_fail_unconfirmed_channel_1/lightning-2/ has memory leaks: [ E { E "backtrace": [ E "ccan/ccan/tal/tal.c:442 (tal_alloc_)", E "wallet/invoices.c:81 (wallet_stmt2invoice_details)", E "wallet/invoices.c:697 (invoices_get_details)", E "wallet/wallet.c:2946 (wallet_invoice_details)", E "lightningd/invoice.c:1296 (json_add_invoices)", E "lightningd/invoice.c:1370 (json_listinvoices)", E "lightningd/jsonrpc.c:625 (command_exec)", E "lightningd/jsonrpc.c:753 (rpc_command_hook_final)", E "lightningd/plugin_hook.c:280 (plugin_hook_call_)", E "lightningd/jsonrpc.c:841 (plugin_hook_call_rpc_command)", E "lightningd/jsonrpc.c:938 (parse_request)", E "lightningd/jsonrpc.c:1035 (read_json)", E "ccan/ccan/io/io.c:59 (next_plan)", E "ccan/ccan/io/io.c:407 (do_plan)", E "ccan/ccan/io/io.c:417 (io_ready)", E "ccan/ccan/io/poll.c:453 (io_loop)", E "lightningd/io_loop_with_timers.c:22 (io_loop_with_timers)", E "lightningd/lightningd.c:1194 (main)", E "../csu/libc-start.c:308 (__libc_start_main)" E ], E "label": "wallet/invoices.c:81:struct invoice_details", E "parents": [ E "common/json_stream.c:41:struct json_stream", E "ccan/ccan/io/io.c:91:struct io_conn **NOTLEAK**", E "lightningd/lightningd.c:107:struct lightningd" E ], E "value": "0x55a57a77de08" E } E ] ``` Signed-off-by: Rusty Russell --- lightningd/invoice.c | 46 ++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index a0097cb61200..ee3d4a0f06b0 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -35,8 +35,8 @@ static const char *invoice_status_str(const struct invoice_details *inv) return "unpaid"; } -static void json_add_invoice(struct json_stream *response, - const struct invoice_details *inv) +static void json_add_invoice_fields(struct json_stream *response, + const struct invoice_details *inv) { json_add_escaped_string(response, "label", inv->label); if (inv->invstring) @@ -76,6 +76,15 @@ static void json_add_invoice(struct json_stream *response, } } +static void json_add_invoice(struct json_stream *response, + const char *fieldname, + const struct invoice_details *inv) +{ + json_object_start(response, fieldname); + json_add_invoice_fields(response, inv); + json_object_end(response); +} + static struct command_result *tell_waiter(struct command *cmd, const struct invoice *inv) { @@ -85,12 +94,12 @@ static struct command_result *tell_waiter(struct command *cmd, details = wallet_invoice_details(cmd, cmd->ld->wallet, *inv); if (details->state == PAID) { response = json_stream_success(cmd); - json_add_invoice(response, details); + json_add_invoice_fields(response, details); return command_success(cmd, response); } else { response = json_stream_fail(cmd, INVOICE_EXPIRED_DURING_WAIT, "invoice expired during wait"); - json_add_invoice(response, details); + json_add_invoice_fields(response, details); json_object_end(response); return command_failed(cmd, response); } @@ -1282,21 +1291,16 @@ static void json_add_invoices(struct json_stream *response, if (label) { if (wallet_invoice_find_by_label(wallet, &invoice, label)) { details = - wallet_invoice_details(response, wallet, invoice); - json_object_start(response, NULL); - json_add_invoice(response, details); - json_object_end(response); + wallet_invoice_details(tmpctx, wallet, invoice); + json_add_invoice(response, NULL, details); } } else if (payment_hash != NULL) { if (wallet_invoice_find_by_rhash(wallet, &invoice, payment_hash)) { - json_object_start(response, NULL); - json_add_invoice( - response, - wallet_invoice_details(response, wallet, invoice)); - json_object_end(response); + details = + wallet_invoice_details(tmpctx, wallet, invoice); + json_add_invoice(response, NULL, details); } - } else { memset(&it, 0, sizeof(it)); while (wallet_invoice_iterate(wallet, &it)) { @@ -1309,9 +1313,7 @@ static void json_add_invoices(struct json_stream *response, details->local_offer_id)) continue; } - json_object_start(response, NULL); - json_add_invoice(response, details); - json_object_end(response); + json_add_invoice(response, NULL, details); } } } @@ -1446,7 +1448,7 @@ static struct command_result *json_delinvoice(struct command *cmd, } response = json_stream_success(cmd); - json_add_invoice(response, details); + json_add_invoice_fields(response, details); return command_success(cmd, response); } @@ -1629,7 +1631,8 @@ static struct command_result *fail_exists(struct command *cmd, fatal("Duplicate invoice %s not found any more?", label->s); - json_add_invoice(data, wallet_invoice_details(cmd, wallet, invoice)); + json_add_invoice_fields(data, + wallet_invoice_details(cmd, wallet, invoice)); json_object_end(data); return command_failed(cmd, data); @@ -1769,8 +1772,9 @@ static struct command_result *json_createinvoice(struct command *cmd, } response = json_stream_success(cmd); - json_add_invoice(response, - wallet_invoice_details(cmd, cmd->ld->wallet, invoice)); + json_add_invoice_fields(response, + wallet_invoice_details(cmd, cmd->ld->wallet, + invoice)); return command_success(cmd, response); } From 112115022c75940035ba7d5d70193ea81456f3c3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 12:27:19 +0930 Subject: [PATCH 1354/1530] gossmap: don't crash if we see a duplicate channel_announce. Apparently we had two private channel announcements (the !private assert failed). While this shouldn't happen, don't crash because of it. Fixes: #5578 Signed-off-by: Rusty Russell Changelog-Fixed: Plugins: topology plugin could crash when it sees duplicate private channel announcements. --- common/gossmap.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/common/gossmap.c b/common/gossmap.c index ce605048e85e..17c5cbe885d4 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -414,11 +414,8 @@ static struct gossmap_chan *add_channel(struct gossmap *map, * that's the only time we get duplicates */ scid.u64 = map_be64(map, cannounce_off + plus_scid_off); chan = gossmap_find_chan(map, &scid); - if (chan) { - assert(chan->private); - assert(!private); + if (chan) gossmap_remove_chan(map, chan); - } /* We carefully map pointers to indexes, since new_node can move them! */ n[0] = gossmap_find_node(map, &node_id[0]); From 9b33a921f04b70a25c0979db3ad4c98f49db98c6 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Fri, 9 Sep 2022 09:48:31 -0300 Subject: [PATCH 1355/1530] Add plugin notification topic "block_processed". Changelog-Added: Plugins: Added notification topic "block_processed". --- doc/PLUGINS.md | 14 ++++++++++++++ lightningd/chaintopology.c | 7 ++++++- lightningd/notification.c | 26 +++++++++++++++++++++++++ lightningd/notification.h | 4 ++++ tests/plugins/block_processed.py | 22 +++++++++++++++++++++ tests/test_plugin.py | 33 ++++++++++++++++++++++++++++++++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100755 tests/plugins/block_processed.py diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index b7e6d17a4cc3..1d481420e0c4 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -873,6 +873,20 @@ current accounts (`account_id` matches the `account_id` emitted from } ``` +### `block_processed` + +Emitted after each block is received from bitcoind, either during the initial sync or +throughout the node's life as new blocks appear. + +```json +{ + "block_processed": { + "hash": "000000000000000000034bdb3c01652a0aa8f63d32f949313d55af2509f9d245", + "height": 753304 + } +} +``` + ### `openchannel_peer_sigs` When opening a channel with a peer using the collaborative transaction protocol diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 9bcc27cefd5b..6ad5aa8a033c 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -818,9 +819,13 @@ static void get_new_block(struct bitcoind *bitcoind, /* Unexpected predecessor? Free predecessor, refetch it. */ if (!bitcoin_blkid_eq(&topo->tip->blkid, &blk->hdr.prev_hash)) remove_tip(topo); - else + else { add_tip(topo, new_block(topo, blk, topo->tip->height + 1)); + /* tell plugins a new block was processed */ + notify_block_processed(topo->ld, topo->tip); + } + /* Try for next one. */ try_extend_tip(topo); } diff --git a/lightningd/notification.c b/lightningd/notification.c index 5c0c339817d2..4e9e1d575726 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -582,6 +582,32 @@ void notify_balance_snapshot(struct lightningd *ld, plugins_notify(ld->plugins, take(n)); } +static void block_processed_notification_serialize(struct json_stream *stream, + struct block *block) +{ + json_object_start(stream, "block"); + json_add_string(stream, "hash", + type_to_string(tmpctx, struct bitcoin_blkid, &block->blkid)); + json_add_u32(stream, "height", block->height); + json_object_end(stream); +} + +REGISTER_NOTIFICATION(block_processed, + block_processed_notification_serialize); + +void notify_block_processed(struct lightningd *ld, + const struct block *block) +{ + void (*serialize)(struct json_stream *, + const struct block *block) = block_processed_notification_gen.serialize; + + struct jsonrpc_notification *n = + jsonrpc_notification_start(NULL, "block_processed"); + serialize(n->stream, block); + jsonrpc_notification_end(n); + plugins_notify(ld->plugins, take(n)); +} + static void openchannel_peer_sigs_serialize(struct json_stream *stream, const struct channel_id *cid, const struct wally_psbt *psbt) diff --git a/lightningd/notification.h b/lightningd/notification.h index ab9bddd607b5..ca8c6a0849a5 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -2,6 +2,7 @@ #define LIGHTNING_LIGHTNINGD_NOTIFICATION_H #include "config.h" #include +#include #include #include @@ -86,6 +87,9 @@ void notify_coin_mvt(struct lightningd *ld, void notify_balance_snapshot(struct lightningd *ld, const struct balance_snapshot *snap); +void notify_block_processed(struct lightningd *ld, + const struct block *block); + void notify_openchannel_peer_sigs(struct lightningd *ld, const struct channel_id *cid, const struct wally_psbt *psbt); diff --git a/tests/plugins/block_processed.py b/tests/plugins/block_processed.py new file mode 100755 index 000000000000..f80e2bd7c40f --- /dev/null +++ b/tests/plugins/block_processed.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +from pyln.client import Plugin + + +plugin = Plugin() + +blocks_catched = [] + + +@plugin.subscribe("block_processed") +def notify_block_processed(plugin, block, **kwargs): + global blocks_catched + blocks_catched.append(block["height"]) + + +@plugin.method("blockscatched") +def return_moves(plugin): + return blocks_catched + + +plugin.run() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index bf0704ccacee..f52453910f1e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2923,3 +2923,36 @@ def test_commando_badrune(node_factory): l1.rpc.decode(base64.urlsafe_b64encode(modrune).decode('utf8')) except RpcError: pass + + +def test_block_processed_notifications(node_factory, bitcoind): + """Test if a plugin gets notifications when a new block is found""" + base = bitcoind.rpc.getblockchaininfo()["blocks"] + plugin = [ + os.path.join(os.getcwd(), "tests/plugins/block_processed.py"), + ] + l1 = node_factory.get_node(options={"plugin": plugin}) + ret = l1.rpc.call("blockscatched") + assert len(ret) == 1 and ret[0] == base + 0 + + bitcoind.generate_block(2) + sync_blockheight(bitcoind, [l1]) + ret = l1.rpc.call("blockscatched") + assert len(ret) == 3 and ret[0] == base + 0 and ret[2] == base + 2 + + l2 = node_factory.get_node(options={"plugin": plugin}) + ret = l2.rpc.call("blockscatched") + assert len(ret) == 1 and ret[0] == base + 2 + + l2.stop() + next_l2_base = bitcoind.rpc.getblockchaininfo()["blocks"] + + bitcoind.generate_block(2) + sync_blockheight(bitcoind, [l1]) + ret = l1.rpc.call("blockscatched") + assert len(ret) == 5 and ret[4] == base + 4 + + l2.start() + sync_blockheight(bitcoind, [l2]) + ret = l2.rpc.call("blockscatched") + assert len(ret) == 3 and ret[1] == next_l2_base + 1 and ret[2] == next_l2_base + 2 From 1ef8fb7ef8b9f52d8167dd35e00776cab8cfb222 Mon Sep 17 00:00:00 2001 From: fiatjaf Date: Mon, 12 Sep 2022 17:43:54 -0300 Subject: [PATCH 1356/1530] rename `block_processed` to `block_added` --- doc/PLUGINS.md | 4 ++-- lightningd/chaintopology.c | 2 +- lightningd/notification.c | 16 ++++++++-------- lightningd/notification.h | 4 ++-- .../{block_processed.py => block_added.py} | 4 ++-- tests/test_plugin.py | 4 ++-- 6 files changed, 17 insertions(+), 17 deletions(-) rename tests/plugins/{block_processed.py => block_added.py} (74%) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 1d481420e0c4..563e34fe09ef 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -873,14 +873,14 @@ current accounts (`account_id` matches the `account_id` emitted from } ``` -### `block_processed` +### `block_added` Emitted after each block is received from bitcoind, either during the initial sync or throughout the node's life as new blocks appear. ```json { - "block_processed": { + "block": { "hash": "000000000000000000034bdb3c01652a0aa8f63d32f949313d55af2509f9d245", "height": 753304 } diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 6ad5aa8a033c..b5d6d71e6a56 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -823,7 +823,7 @@ static void get_new_block(struct bitcoind *bitcoind, add_tip(topo, new_block(topo, blk, topo->tip->height + 1)); /* tell plugins a new block was processed */ - notify_block_processed(topo->ld, topo->tip); + notify_block_added(topo->ld, topo->tip); } /* Try for next one. */ diff --git a/lightningd/notification.c b/lightningd/notification.c index 4e9e1d575726..2935094f2ca2 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -582,8 +582,8 @@ void notify_balance_snapshot(struct lightningd *ld, plugins_notify(ld->plugins, take(n)); } -static void block_processed_notification_serialize(struct json_stream *stream, - struct block *block) +static void block_added_notification_serialize(struct json_stream *stream, + struct block *block) { json_object_start(stream, "block"); json_add_string(stream, "hash", @@ -592,17 +592,17 @@ static void block_processed_notification_serialize(struct json_stream *stream, json_object_end(stream); } -REGISTER_NOTIFICATION(block_processed, - block_processed_notification_serialize); +REGISTER_NOTIFICATION(block_added, + block_added_notification_serialize); -void notify_block_processed(struct lightningd *ld, - const struct block *block) +void notify_block_added(struct lightningd *ld, + const struct block *block) { void (*serialize)(struct json_stream *, - const struct block *block) = block_processed_notification_gen.serialize; + const struct block *block) = block_added_notification_gen.serialize; struct jsonrpc_notification *n = - jsonrpc_notification_start(NULL, "block_processed"); + jsonrpc_notification_start(NULL, "block_added"); serialize(n->stream, block); jsonrpc_notification_end(n); plugins_notify(ld->plugins, take(n)); diff --git a/lightningd/notification.h b/lightningd/notification.h index ca8c6a0849a5..7b1c624b0a33 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -87,8 +87,8 @@ void notify_coin_mvt(struct lightningd *ld, void notify_balance_snapshot(struct lightningd *ld, const struct balance_snapshot *snap); -void notify_block_processed(struct lightningd *ld, - const struct block *block); +void notify_block_added(struct lightningd *ld, + const struct block *block); void notify_openchannel_peer_sigs(struct lightningd *ld, const struct channel_id *cid, diff --git a/tests/plugins/block_processed.py b/tests/plugins/block_added.py similarity index 74% rename from tests/plugins/block_processed.py rename to tests/plugins/block_added.py index f80e2bd7c40f..a85b288e6758 100755 --- a/tests/plugins/block_processed.py +++ b/tests/plugins/block_added.py @@ -8,8 +8,8 @@ blocks_catched = [] -@plugin.subscribe("block_processed") -def notify_block_processed(plugin, block, **kwargs): +@plugin.subscribe("block_added") +def notify_block_added(plugin, block, **kwargs): global blocks_catched blocks_catched.append(block["height"]) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index f52453910f1e..75dd78e12a86 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2925,11 +2925,11 @@ def test_commando_badrune(node_factory): pass -def test_block_processed_notifications(node_factory, bitcoind): +def test_block_added_notifications(node_factory, bitcoind): """Test if a plugin gets notifications when a new block is found""" base = bitcoind.rpc.getblockchaininfo()["blocks"] plugin = [ - os.path.join(os.getcwd(), "tests/plugins/block_processed.py"), + os.path.join(os.getcwd(), "tests/plugins/block_added.py"), ] l1 = node_factory.get_node(options={"plugin": plugin}) ret = l1.rpc.call("blockscatched") From 0a856778bcbf3bfeffc3d0bc6eaa57ce2cdf1f11 Mon Sep 17 00:00:00 2001 From: Otto Sabart Date: Tue, 9 Aug 2022 09:00:00 +0200 Subject: [PATCH 1357/1530] plugins/bcli: load RPC password from stdin instead of an argument Changelog-Fixed: bcli: don't expose bitcoin RPC password on commandline --- plugins/bcli.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/plugins/bcli.c b/plugins/bcli.c index ea98050ffb9c..7946dde76b0e 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -121,8 +122,10 @@ static const char **gather_argsv(const tal_t *ctx, const char *cmd, va_list ap) if (bitcoind->rpcuser) add_arg(&args, tal_fmt(args, "-rpcuser=%s", bitcoind->rpcuser)); if (bitcoind->rpcpass) - add_arg(&args, - tal_fmt(args, "-rpcpassword=%s", bitcoind->rpcpass)); + // Always pipe the rpcpassword via stdin. Do not pass it using an + // `-rpcpassword` argument - secrets in arguments can leak when listing + // system processes. + add_arg(&args, "-stdinrpcpass"); add_arg(&args, cmd); while ((arg = va_arg(ap, char *)) != NULL) @@ -290,6 +293,7 @@ static void next_bcli(enum bitcoind_prio prio) { struct bitcoin_cli *bcli; struct io_conn *conn; + int in; if (bitcoind->num_requests[prio] >= BITCOIND_MAX_PARALLEL) return; @@ -298,8 +302,14 @@ static void next_bcli(enum bitcoind_prio prio) if (!bcli) return; - bcli->pid = pipecmdarr(NULL, &bcli->fd, &bcli->fd, + bcli->pid = pipecmdarr(&in, &bcli->fd, &bcli->fd, cast_const2(char **, bcli->args)); + + if (bitcoind->rpcpass) + write_all(in, bitcoind->rpcpass, strlen(bitcoind->rpcpass)); + + close(in); + if (bcli->pid < 0) plugin_err(bcli->cmd->plugin, "%s exec failed: %s", bcli->args[0], strerror(errno)); @@ -854,7 +864,7 @@ static void parse_getnetworkinfo_result(struct plugin *p, const char *buf) static void wait_and_check_bitcoind(struct plugin *p) { - int from, status, ret; + int in, from, status, ret; pid_t child; const char **cmd = gather_args(bitcoind, "getnetworkinfo", NULL); bool printed = false; @@ -863,7 +873,13 @@ static void wait_and_check_bitcoind(struct plugin *p) for (;;) { tal_free(output); - child = pipecmdarr(NULL, &from, &from, cast_const2(char **,cmd)); + child = pipecmdarr(&in, &from, &from, cast_const2(char **, cmd)); + + if (bitcoind->rpcpass) + write_all(in, bitcoind->rpcpass, strlen(bitcoind->rpcpass)); + + close(in); + if (child < 0) { if (errno == ENOENT) bitcoind_failure(p, "bitcoin-cli not found. Is bitcoin-cli " From 4ca6b36439074f41729da679815792ee73b5fb24 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Aug 2022 19:35:42 +0930 Subject: [PATCH 1358/1530] lightningd: refuse to upgrade db on non-released versions by default. This is a good sanity check that users understand that if they upgrade to master mid-cycle they can't go back! Suggested-by: @wtogami Changelog-Added: Config: `--database-upgrade=true` required if a non-release version wants to (irrevocably!) upgrade the db. Signed-off-by: Rusty Russell --- doc/lightning-listconfigs.7.md | 3 +- doc/lightningd-config.5.md | 10 +++++++ doc/schemas/listconfigs.schema.json | 4 +++ lightningd/lightningd.c | 1 + lightningd/lightningd.h | 3 ++ lightningd/options.c | 15 ++++++++++ tests/test_db.py | 43 +++++++++++++++++++++++------ tests/test_misc.py | 2 +- wallet/db.c | 18 +++++++++++- 9 files changed, 88 insertions(+), 11 deletions(-) diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index d611e6ff08cf..7a4ed25fad00 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -62,6 +62,7 @@ On success, an object is returned, containing: - **experimental-offers** (boolean, optional): `experimental-offers` field from config or cmdline, or default - **experimental-shutdown-wrong-funding** (boolean, optional): `experimental-shutdown-wrong-funding` field from config or cmdline, or default - **experimental-websocket-port** (u16, optional): `experimental-websocket-port` field from config or cmdline, or default +- **database-upgrade** (boolean, optional): `database-upgrade` field from config or cmdline - **rgb** (hex, optional): `rgb` field from config or cmdline, or default (always 6 characters) - **alias** (string, optional): `alias` field from config or cmdline, or default - **pid-file** (string, optional): `pid-file` field from config or cmdline, or default @@ -215,4 +216,4 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:14fa07df1432e7104983afc4fba02cc462237bd1a154ba3f3750355d10ffdadb) +[comment]: # ( SHA256STAMP:dcab86f29b946fed925de5e05cb79faa03cc4421cefeab3561a596ed5e64962d) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index a1cd54a8e886..6a07cd14389f 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -60,8 +60,18 @@ fun to put in other's config files while their computer is unattended. putting this in someone's config file may convince them to read this man page. +* **database-upgrade**=*BOOL* + + Upgrades to Core Lightning often change the database: once this is done, +downgrades are not generally possible. By default, Core Lightning will +exit with an error rather than upgrade, unless this is an official released +version. If you really want to upgrade to a non-release version, you can +set this to *true* (or *false* to never allow a non-reversible upgrade!). + ### Bitcoin control options: +Bitcoin control options: + * **network**=*NETWORK* Select the network parameters (*bitcoin*, *testnet*, *signet*, or *regtest*). diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index 56a1cd7b1d12..ec7eabc5bba0 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -137,6 +137,10 @@ "type": "u16", "description": "`experimental-websocket-port` field from config or cmdline, or default" }, + "database-upgrade": { + "type": "boolean", + "description": "`database-upgrade` field from config or cmdline" + }, "rgb": { "type": "hex", "description": "`rgb` field from config or cmdline, or default", diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 288b589e2bcb..50d71ee31542 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -213,6 +213,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->autolisten = true; ld->reconnect = true; ld->try_reexec = false; + ld->db_upgrade_ok = NULL; /*~ This is from ccan/timer: it is efficient for the case where timers * are deleted before expiry (as is common with timeouts) using an diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 401dba808015..a519e538dd59 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -221,6 +221,9 @@ struct lightningd { * FEERATE_PENALTY). */ u32 *force_feerates; + /* If they force db upgrade on or off this is set. */ + bool *db_upgrade_ok; + #if DEVELOPER /* If we want to debug a subdaemon/plugin. */ const char *dev_debug_subprocess; diff --git a/lightningd/options.c b/lightningd/options.c index e90e5b07f8ed..ef8f7b00830a 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1025,6 +1025,12 @@ static char *opt_set_offers(struct lightningd *ld) return opt_set_onion_messages(ld); } +static char *opt_set_db_upgrade(const char *arg, struct lightningd *ld) +{ + ld->db_upgrade_ok = tal(ld, bool); + return opt_set_bool_arg(arg, ld->db_upgrade_ok); +} + static void register_opts(struct lightningd *ld) { /* This happens before plugins started */ @@ -1205,6 +1211,10 @@ static void register_opts(struct lightningd *ld) ld, "experimental: alternate port for peers to connect" " using WebSockets (RFC6455)"); + opt_register_arg("--database-upgrade", + opt_set_db_upgrade, NULL, + ld, + "Set to true to allow database upgrades even on non-final releases (WARNING: you won't be able to downgrade!)"); opt_register_logging(ld); opt_register_version(); @@ -1629,6 +1639,11 @@ static void add_config(struct lightningd *ld, json_add_u32(response, name0, ld->websocket_port); return; + } else if (opt->cb_arg == (void *)opt_set_db_upgrade) { + if (ld->db_upgrade_ok) + json_add_bool(response, name0, + *ld->db_upgrade_ok); + return; } else if (opt->cb_arg == (void *)opt_important_plugin) { /* Do nothing, this is already handled by * opt_add_plugin. */ diff --git a/tests/test_db.py b/tests/test_db.py index c21f5b4a88ad..58aec294408a 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -7,7 +7,9 @@ import base64 import os import pytest +import re import shutil +import subprocess import time import unittest @@ -17,7 +19,8 @@ def test_db_dangling_peer_fix(node_factory, bitcoind): # Make sure bitcoind doesn't think it's going backwards bitcoind.generate_block(104) # This was taken from test_fail_unconfirmed() node. - l1 = node_factory.get_node(dbfile='dangling-peer.sqlite3.xz') + l1 = node_factory.get_node(dbfile='dangling-peer.sqlite3.xz', + options={'database-upgrade': True}) l2 = node_factory.get_node() # Must match entry in db @@ -138,8 +141,26 @@ def test_scid_upgrade(node_factory, bitcoind): bitcoind.generate_block(1) # Created through the power of sed "s/X'\([0-9]*\)78\([0-9]*\)78\([0-9]*\)'/X'\13A\23A\3'/" - l1 = node_factory.get_node(dbfile='oldstyle-scids.sqlite3.xz') + l1 = node_factory.get_node(dbfile='oldstyle-scids.sqlite3.xz', + start=False, expect_fail=True, + allow_broken_log=True) + + # Will refuse to upgrade (if not in a release!) + version = subprocess.check_output(['lightningd/lightningd', + '--version']).decode('utf-8').splitlines()[0] + if not re.match('^v[0-9.]*$', version): + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) + # Will have exited with non-zero status. + assert l1.daemon.proc.wait(TIMEOUT) != 0 + assert l1.daemon.is_in_stderr('Refusing to irreversibly upgrade db from version [0-9]* to [0-9]* in non-final version ' + version + r' \(use --database-upgrade=true to override\)') + + l1.daemon.opts['database-upgrade'] = False + l1.daemon.start(wait_for_initialized=False, stderr_redir=True) + assert l1.daemon.proc.wait(TIMEOUT) != 0 + assert l1.daemon.is_in_stderr(r'Refusing to upgrade db from version [0-9]* to [0-9]* \(database-upgrade=false\)') + l1.daemon.opts['database-upgrade'] = True + l1.daemon.start() assert l1.db_query('SELECT short_channel_id from channels;') == [{'short_channel_id': '103x1x1'}] assert l1.db_query('SELECT failchannel from payments;') == [{'failchannel': '103x1x1'}] @@ -152,7 +173,8 @@ def test_last_tx_inflight_psbt_upgrade(node_factory, bitcoind): prior_txs = ['02000000019CCCA2E59D863B00B5BD835BF7BA93CC257932D2C7CDBE51EFE2EE4A9D29DFCB01000000009DB0E280024A01000000000000220020BE7935A77CA9AB70A4B8B1906825637767FED3C00824AA90C988983587D68488F0820100000000002200209F4684DDB28ACDC73959BC194D1A25DF906F61ED030F52D163E6F1E247D32CBB9A3ED620', '020000000122F9EBE38F54208545B681AD7F73A7AE3504A09C8201F502673D34E28424687C01000000009DB0E280024A01000000000000220020BE7935A77CA9AB70A4B8B1906825637767FED3C00824AA90C988983587D68488F0820100000000002200209F4684DDB28ACDC73959BC194D1A25DF906F61ED030F52D163E6F1E247D32CBB9A3ED620'] - l1 = node_factory.get_node(dbfile='upgrade_inflight.sqlite3.xz') + l1 = node_factory.get_node(dbfile='upgrade_inflight.sqlite3.xz', + options={'database-upgrade': True}) b64_last_txs = [base64.b64encode(x['last_tx']).decode('utf-8') for x in l1.db_query('SELECT last_tx FROM channel_funding_inflights ORDER BY channel_id, funding_feerate;')] for i in range(len(b64_last_txs)): @@ -174,7 +196,8 @@ def test_last_tx_psbt_upgrade(node_factory, bitcoind): prior_txs = ['02000000018DD699861B00061E50937A233DB584BF8ED4C0BF50B44C0411F71B031A06455000000000000EF7A9800350C300000000000022002073356CFF7E1588F14935EF138E142ABEFB5F7E3D51DE942758DCD5A179449B6250A90600000000002200202DF545EA882889846C52FC5E111AC07CE07E0C09418AC15743A6F6284C2A4FA720A1070000000000160014E89954FAC8F7A2DCE51E095D7BEB5271C3F7DA56EF81DC20', '02000000018A0AE4C63BCDF9D78B07EB4501BB23404FDDBC73973C592793F047BE1495074B010000000074D99980010A2D0F00000000002200203B8CB644781CBECA96BE8B2BF1827AFD908B3CFB5569AC74DAB9395E8DDA39E4C9555420', '020000000135DAB2996E57762E3EC158C0D57D39F43CA657E882D93FC24F5FEBAA8F36ED9A0100000000566D1D800350C30000000000002200205679A7D06E1BD276AA25F56E9E4DF7E07D9837EFB0C5F63604F10CD9F766A03ED4DD0600000000001600147E5B5C8F4FC1A9484E259F92CA4CBB7FA2814EA49A6C070000000000220020AB6226DEBFFEFF4A741C01367FA3C875172483CFB3E327D0F8C7AA4C51EDECAA27AA4720'] - l1 = node_factory.get_node(dbfile='last_tx_upgrade.sqlite3.xz') + l1 = node_factory.get_node(dbfile='last_tx_upgrade.sqlite3.xz', + options={'database-upgrade': True}) b64_last_txs = [base64.b64encode(x['last_tx']).decode('utf-8') for x in l1.db_query('SELECT last_tx FROM channels ORDER BY id;')] for i in range(len(b64_last_txs)): @@ -195,7 +218,8 @@ def test_last_tx_psbt_upgrade(node_factory, bitcoind): # We need to give it a chance to update time.sleep(2) - l2 = node_factory.get_node(dbfile='last_tx_closed.sqlite3.xz') + l2 = node_factory.get_node(dbfile='last_tx_closed.sqlite3.xz', + options={'database-upgrade': True}) last_txs = [x['last_tx'] for x in l2.db_query('SELECT last_tx FROM channels ORDER BY id;')] # The first tx should be psbt, the second should still be hex @@ -234,7 +258,8 @@ def test_backfill_scriptpubkeys(node_factory, bitcoind): ] # Test the first time, all entries are with option_static_remotekey - l1 = node_factory.get_node(node_id=3, dbfile='pubkey_regen.sqlite.xz') + l1 = node_factory.get_node(node_id=3, dbfile='pubkey_regen.sqlite.xz', + options={'database-upgrade': True}) results = l1.db_query('SELECT hex(prev_out_tx) AS txid, hex(scriptpubkey) AS script FROM outputs') scripts = [{'txid': x['txid'], 'scriptpubkey': x['script']} for x in results] for exp, actual in zip(script_map, scripts): @@ -266,7 +291,8 @@ def test_backfill_scriptpubkeys(node_factory, bitcoind): } ] - l2 = node_factory.get_node(node_id=3, dbfile='pubkey_regen_commitment_point.sqlite3.xz') + l2 = node_factory.get_node(node_id=3, dbfile='pubkey_regen_commitment_point.sqlite3.xz', + options={'database-upgrade': True}) results = l2.db_query('SELECT hex(prev_out_tx) AS txid, hex(scriptpubkey) AS script FROM outputs') scripts = [{'txid': x['txid'], 'scriptpubkey': x['script']} for x in results] for exp, actual in zip(script_map_2, scripts): @@ -344,7 +370,8 @@ def test_local_basepoints_cache(bitcoind, node_factory): bitcoind.generate_block(6) l1 = node_factory.get_node( dbfile='no-local-basepoints.sqlite3.xz', - start=False + start=False, + options={'database-upgrade': True} ) fields = [ diff --git a/tests/test_misc.py b/tests/test_misc.py index d505820be3f3..b1d7d26b0e48 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -49,7 +49,7 @@ def test_names(node_factory): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This migration is based on a sqlite3 snapshot") def test_db_upgrade(node_factory): - l1 = node_factory.get_node() + l1 = node_factory.get_node(options={'database-upgrade': True}) l1.stop() version = subprocess.check_output(['lightningd/lightningd', diff --git a/wallet/db.c b/wallet/db.c index b3a5a248aaec..88cd1ed1021e 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -888,6 +888,14 @@ static struct migration dbmigrations[] = { {SQL("UPDATE payments SET completed_at = timestamp WHERE status != 0;"), NULL} }; +/* Released versions are of form v{num}[.{num}]* */ +static bool is_released_version(void) +{ + if (version()[0] != 'v') + return false; + return strcspn(version()+1, ".0123456789") == strlen(version()+1); +} + /** * db_migrate - Apply all remaining migrations from the current version */ @@ -910,9 +918,17 @@ static bool db_migrate(struct lightningd *ld, struct db *db, else if (available < current) db_fatal("Refusing to migrate down from version %u to %u", current, available); - else if (current != available) + else if (current != available) { + if (ld->db_upgrade_ok && *ld->db_upgrade_ok == false) { + db_fatal("Refusing to upgrade db from version %u to %u (database-upgrade=false)", + current, available); + } else if (!ld->db_upgrade_ok && !is_released_version()) { + db_fatal("Refusing to irreversibly upgrade db from version %u to %u in non-final version %s (use --database-upgrade=true to override)", + current, available, version()); + } log_info(ld->log, "Updating database from version %u to %u", current, available); + } while (current < available) { current++; From c8ab8192ca65a2b3ac3153dd881309a4dee76e2c Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Mon, 12 Sep 2022 15:47:44 +0200 Subject: [PATCH 1359/1530] peer_control: getinfo show correct port on discovered IPs Changelog-Fixed: peer_control: getinfo shows the correct port on discovered IPs --- gossipd/gossipd.c | 3 --- lightningd/lightningd.c | 2 ++ lightningd/lightningd.h | 4 ++++ lightningd/peer_control.c | 39 +++++++++++++++++++++++++++------------ tests/test_connection.py | 12 ++++++++++++ 5 files changed, 45 insertions(+), 15 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 84a588775b46..86a268394a80 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -350,9 +350,6 @@ static void handle_remote_addr(struct daemon *daemon, const u8 *msg) if (!fromwire_gossipd_remote_addr(msg, &remote_addr)) master_badmsg(WIRE_GOSSIPD_REMOTE_ADDR, msg); - /* Best guess is that we use default port for the selected network */ - remote_addr.port = chainparams_get_ln_port(chainparams); - switch (remote_addr.type) { case ADDR_TYPE_IPV4: if (daemon->remote_addr_v4 != NULL && diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 50d71ee31542..29d5da76138a 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -209,6 +209,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx) ld->remote_addr_v4 = NULL; ld->remote_addr_v6 = NULL; + ld->discovered_ip_v4 = NULL; + ld->discovered_ip_v6 = NULL; ld->listen = true; ld->autolisten = true; ld->reconnect = true; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index a519e538dd59..bfacc17e76ac 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -158,6 +158,10 @@ struct lightningd { struct node_id remote_addr_v4_peer; struct node_id remote_addr_v6_peer; + /* verified discovered IPs to be used for anouncement */ + struct wireaddr *discovered_ip_v4; + struct wireaddr *discovered_ip_v6; + /* Bearer of all my secrets. */ int hsm_fd; struct subd *hsm; diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 648fcf61532f..c80c17a7bd75 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1274,10 +1274,17 @@ static void update_remote_addr(struct lightningd *ld, const struct wireaddr *remote_addr, const struct node_id peer_id) { + u16 public_port; + /* failsafe to prevent privacy leakage. */ if (ld->always_use_proxy || ld->config.disable_ip_discovery) return; + /* Peers will have likey reported our dynamic outbound TCP port. + * Best guess is that we use default port for the selected network, + * until we add a commandline switch to override this. */ + public_port = chainparams_get_ln_port(chainparams); + switch (remote_addr->type) { case ADDR_TYPE_IPV4: /* init pointers first time */ @@ -1292,10 +1299,14 @@ static void update_remote_addr(struct lightningd *ld, break; } /* tell gossip we have a valid update */ - if (wireaddr_eq_without_port(ld->remote_addr_v4, remote_addr)) + if (wireaddr_eq_without_port(ld->remote_addr_v4, remote_addr)) { + ld->discovered_ip_v4 = tal_dup(ld, struct wireaddr, + ld->remote_addr_v4); + ld->discovered_ip_v4->port = public_port; subd_send_msg(ld->gossip, towire_gossipd_remote_addr( tmpctx, - ld->remote_addr_v4)); + ld->discovered_ip_v4)); + } /* store latest values */ *ld->remote_addr_v4 = *remote_addr; ld->remote_addr_v4_peer = peer_id; @@ -1311,10 +1322,14 @@ static void update_remote_addr(struct lightningd *ld, *ld->remote_addr_v6 = *remote_addr; break; } - if (wireaddr_eq_without_port(ld->remote_addr_v6, remote_addr)) + if (wireaddr_eq_without_port(ld->remote_addr_v6, remote_addr)) { + ld->discovered_ip_v6 = tal_dup(ld, struct wireaddr, + ld->remote_addr_v6); + ld->discovered_ip_v6->port = public_port; subd_send_msg(ld->gossip, towire_gossipd_remote_addr( tmpctx, - ld->remote_addr_v6)); + ld->discovered_ip_v6)); + } *ld->remote_addr_v6 = *remote_addr; ld->remote_addr_v6_peer = peer_id; break; @@ -2247,22 +2262,22 @@ static struct command_result *json_getinfo(struct command *cmd, for (size_t i = 0; i < count_announceable; i++) json_add_address(response, NULL, cmd->ld->announceable+i); - /* Currently, IP discovery will only be announced by gossipd, if we - * don't already have usable addresses. + /* Currently, IP discovery will only be announced by gossipd, + * if we don't already have usable addresses. * See `create_node_announcement` in `gossip_generation.c`. */ if (count_announceable == 0) { - if (cmd->ld->remote_addr_v4 != NULL && + if (cmd->ld->discovered_ip_v4 != NULL && !wireaddr_arr_contains( cmd->ld->announceable, - cmd->ld->remote_addr_v4)) + cmd->ld->discovered_ip_v4)) json_add_address(response, NULL, - cmd->ld->remote_addr_v4); - if (cmd->ld->remote_addr_v6 != NULL && + cmd->ld->discovered_ip_v4); + if (cmd->ld->discovered_ip_v6 != NULL && !wireaddr_arr_contains( cmd->ld->announceable, - cmd->ld->remote_addr_v6)) + cmd->ld->discovered_ip_v6)) json_add_address(response, NULL, - cmd->ld->remote_addr_v6); + cmd->ld->discovered_ip_v6); } json_array_end(response); diff --git a/tests/test_connection.py b/tests/test_connection.py index 7040d4dcd08c..b880e23640ec 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -83,6 +83,7 @@ def test_remote_addr(node_factory, bitcoind): l2.daemon.opts['bind-addr'] = l2.daemon.opts['addr'] del l2.daemon.opts['addr'] l2.start() + assert len(l2.rpc.getinfo()['address']) == 0 l2.rpc.connect(l1.info['id'], 'localhost', l1.port) logmsg = l2.daemon.wait_for_log("Peer says it sees our address as: 127.0.0.1:[0-9]{5}") @@ -95,6 +96,7 @@ def test_remote_addr(node_factory, bitcoind): bitcoind.generate_block(5) l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) + assert len(l2.rpc.getinfo()['address']) == 0 def_port = default_ln_port(l2.info["network"]) @@ -110,6 +112,7 @@ def test_remote_addr(node_factory, bitcoind): # Now l1 sees l2 but without announced addresses. assert(len(l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses']) == 0) assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:{}".format(def_port)) + assert len(l2.rpc.getinfo()['address']) == 0 # connect second node. This will not yet trigger `node_annoucement` update, # as we again do not have a channel at the time we connected. @@ -120,6 +123,7 @@ def test_remote_addr(node_factory, bitcoind): l2.fundchannel(l3, wait_for_active=True) bitcoind.generate_block(5) assert not l2.daemon.is_in_log("Update our node_announcement for discovered address: 127.0.0.1:{}".format(def_port)) + assert len(l2.rpc.getinfo()['address']) == 0 # restart, reconnect and re-check for updated node_annoucement. This time # l2 sees that two different peers with channel reported the same `remote_addr`. @@ -129,11 +133,19 @@ def test_remote_addr(node_factory, bitcoind): l2.daemon.wait_for_log("Update our node_announcement for discovered address: 127.0.0.1:{}".format(def_port)) l1.daemon.wait_for_log(f"Received node_announcement for node {l2.info['id']}") + # check l1 sees the updated node announcement via CLI listnodes address = l1.rpc.listnodes(l2.info['id'])['nodes'][0]['addresses'][0] assert address['type'] == "ipv4" assert address['address'] == "127.0.0.1" assert address['port'] == def_port + # also check l2 returns the announced address (and port) via CLI getinfo + getinfo = l2.rpc.getinfo() + assert len(getinfo['address']) == 1 + assert getinfo['address'][0]['type'] == 'ipv4' + assert getinfo['address'][0]['address'] == '127.0.0.1' + assert getinfo['address'][0]['port'] == def_port + @pytest.mark.developer("needs DEVELOPER=1 for fast gossip and --dev-allow-localhost for local remote_addr") def test_remote_addr_disabled(node_factory, bitcoind): From 532544ce4fa859037faf177b0af4b01a9025e3c1 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Tue, 13 Sep 2022 12:04:45 +0200 Subject: [PATCH 1360/1530] gossipd: rename remote_addr to discovered_ip within gossipd This is cleaner because, the `remote_addr` and `discovered_ip` are related but two different things. Within connectd and lightningd we use the peers `remote_addr` feature to validate (and guess a port) to be used for IP discovery. Also when a peer reports us a `remote_addr`, this is given to the plugin API via the `peer_connected` hook. The network port here is not modified for godd reason! This can be used i.e. to detect if we are behind a NAT. But once lightningd figures enough peers report the same `remote_addr`, it sets the port to the selected network and tells gossipd to use that for `node_announcement` updates. Hence, within gossipd, there is no (should not be) `remote_addr`. Changelog-None --- gossipd/gossip_generation.c | 16 +++---- gossipd/gossipd.c | 46 ++++++++++----------- gossipd/gossipd.h | 4 +- gossipd/gossipd_wire.csv | 6 +-- lightningd/gossip_control.c | 2 +- lightningd/peer_control.c | 4 +- lightningd/test/run-invoice-select-inchan.c | 6 +-- wallet/test/run-wallet.c | 11 ++--- 8 files changed, 45 insertions(+), 50 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index b309e4883a21..e5ab5eeab727 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -43,18 +43,18 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, for (i = 0; i < count_announceable; i++) tal_arr_expand(&was, daemon->announceable[i]); - /* Add IP discovery `remote_addr` v4 and v6 of our self. */ + /* Add discovered IPs v4/v6 verified by peer `remote_addr` feature. */ /* Only do that if we don't have addresses announced. */ if (count_announceable == 0) { - if (daemon->remote_addr_v4 != NULL && - !wireaddr_arr_contains(was, daemon->remote_addr_v4)) - tal_arr_expand(&was, *daemon->remote_addr_v4); - if (daemon->remote_addr_v6 != NULL && - !wireaddr_arr_contains(was, daemon->remote_addr_v6)) - tal_arr_expand(&was, *daemon->remote_addr_v6); + if (daemon->discovered_ip_v4 != NULL && + !wireaddr_arr_contains(was, daemon->discovered_ip_v4)) + tal_arr_expand(&was, *daemon->discovered_ip_v4); + if (daemon->discovered_ip_v6 != NULL && + !wireaddr_arr_contains(was, daemon->discovered_ip_v6)) + tal_arr_expand(&was, *daemon->discovered_ip_v6); } - /* Sort by address type again, as we added dynamic remote_addr v4/v6. */ + /* Sort by address type again, as we added dynamic discovered_ip v4/v6. */ /* BOLT #7: * * The origin node: diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 86a268394a80..224db2220780 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -341,33 +341,33 @@ static void handle_local_private_channel(struct daemon *daemon, const u8 *msg) } } -/* lightningd tells us it has dicovered and verified new `remote_addr`. +/* lightningd tells us it has discovered and verified new `remote_addr`. * We can use this to update our node announcement. */ -static void handle_remote_addr(struct daemon *daemon, const u8 *msg) +static void handle_discovered_ip(struct daemon *daemon, const u8 *msg) { - struct wireaddr remote_addr; + struct wireaddr discovered_ip; - if (!fromwire_gossipd_remote_addr(msg, &remote_addr)) - master_badmsg(WIRE_GOSSIPD_REMOTE_ADDR, msg); + if (!fromwire_gossipd_discovered_ip(msg, &discovered_ip)) + master_badmsg(WIRE_GOSSIPD_DISCOVERED_IP, msg); - switch (remote_addr.type) { + switch (discovered_ip.type) { case ADDR_TYPE_IPV4: - if (daemon->remote_addr_v4 != NULL && - wireaddr_eq_without_port(daemon->remote_addr_v4, - &remote_addr)) + if (daemon->discovered_ip_v4 != NULL && + wireaddr_eq_without_port(daemon->discovered_ip_v4, + &discovered_ip)) break; - tal_free(daemon->remote_addr_v4); - daemon->remote_addr_v4 = tal_dup(daemon, struct wireaddr, - &remote_addr); + tal_free(daemon->discovered_ip_v4); + daemon->discovered_ip_v4 = tal_dup(daemon, struct wireaddr, + &discovered_ip); goto update_node_annoucement; case ADDR_TYPE_IPV6: - if (daemon->remote_addr_v6 != NULL && - wireaddr_eq_without_port(daemon->remote_addr_v6, - &remote_addr)) + if (daemon->discovered_ip_v6 != NULL && + wireaddr_eq_without_port(daemon->discovered_ip_v6, + &discovered_ip)) break; - tal_free(daemon->remote_addr_v6); - daemon->remote_addr_v6 = tal_dup(daemon, struct wireaddr, - &remote_addr); + tal_free(daemon->discovered_ip_v6); + daemon->discovered_ip_v6 = tal_dup(daemon, struct wireaddr, + &discovered_ip); goto update_node_annoucement; /* ignore all other cases */ @@ -381,7 +381,7 @@ static void handle_remote_addr(struct daemon *daemon, const u8 *msg) update_node_annoucement: status_debug("Update our node_announcement for discovered address: %s", - fmt_wireaddr(tmpctx, &remote_addr)); + fmt_wireaddr(tmpctx, &discovered_ip)); maybe_send_own_node_announce(daemon, false); } @@ -1038,8 +1038,8 @@ static struct io_plan *recv_req(struct io_conn *conn, handle_local_private_channel(daemon, msg); goto done; - case WIRE_GOSSIPD_REMOTE_ADDR: - handle_remote_addr(daemon, msg); + case WIRE_GOSSIPD_DISCOVERED_IP: + handle_discovered_ip(daemon, msg); goto done; #if DEVELOPER case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: @@ -1097,8 +1097,8 @@ int main(int argc, char *argv[]) daemon->node_announce_regen_timer = NULL; daemon->current_blockheight = 0; /* i.e. unknown */ daemon->rates = NULL; - daemon->remote_addr_v4 = NULL; - daemon->remote_addr_v6 = NULL; + daemon->discovered_ip_v4 = NULL; + daemon->discovered_ip_v6 = NULL; list_head_init(&daemon->deferred_updates); /* Tell the ecdh() function how to talk to hsmd */ diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 7ed700afa028..2129375f7e8b 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -48,8 +48,8 @@ struct daemon { struct wireaddr *announceable; /* verified remote_addr as reported by recent peers */ - struct wireaddr *remote_addr_v4; - struct wireaddr *remote_addr_v6; + struct wireaddr *discovered_ip_v4; + struct wireaddr *discovered_ip_v6; /* Timer until we can send an updated node_announcement */ struct oneshot *node_announce_timer; diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 7ce110b676b3..c058a1dfb9f9 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -123,6 +123,6 @@ msgdata,gossipd_local_private_channel,features,u8,len msgtype,gossipd_used_local_channel_update,3052 msgdata,gossipd_used_local_channel_update,scid,short_channel_id, -# Tell gossipd we have discovered a new remote_addr -msgtype,gossipd_remote_addr,3009 -msgdata,gossipd_remote_addr,remote_addr,wireaddr, +# Tell gossipd we have verified a new public IP by the remote_addr feature +msgtype,gossipd_discovered_ip,3009 +msgdata,gossipd_discovered_ip,discovered_ip,wireaddr, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 5fcd4f798d79..5f3c933cdb89 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -174,7 +174,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_ADDGOSSIP_REPLY: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: - case WIRE_GOSSIPD_REMOTE_ADDR: + case WIRE_GOSSIPD_DISCOVERED_IP: break; case WIRE_GOSSIPD_GET_TXOUT: diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index c80c17a7bd75..e68e5a1d8740 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1303,7 +1303,7 @@ static void update_remote_addr(struct lightningd *ld, ld->discovered_ip_v4 = tal_dup(ld, struct wireaddr, ld->remote_addr_v4); ld->discovered_ip_v4->port = public_port; - subd_send_msg(ld->gossip, towire_gossipd_remote_addr( + subd_send_msg(ld->gossip, towire_gossipd_discovered_ip( tmpctx, ld->discovered_ip_v4)); } @@ -1326,7 +1326,7 @@ static void update_remote_addr(struct lightningd *ld, ld->discovered_ip_v6 = tal_dup(ld, struct wireaddr, ld->remote_addr_v6); ld->discovered_ip_v6->port = public_port; - subd_send_msg(ld->gossip, towire_gossipd_remote_addr( + subd_send_msg(ld->gossip, towire_gossipd_discovered_ip( tmpctx, ld->discovered_ip_v6)); } diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index b3e7395a248b..d25a7da1b52a 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -733,9 +733,9 @@ u8 *towire_errorfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "towire_errorfmt called!\n"); abort(); } -/* Generated stub for towire_gossipd_remote_addr */ -u8 *towire_gossipd_remote_addr(const tal_t *ctx UNNEEDED, const struct wireaddr *remote_addr UNNEEDED) -{ fprintf(stderr, "towire_gossipd_remote_addr called!\n"); abort(); } +/* Generated stub for towire_gossipd_discovered_ip */ +u8 *towire_gossipd_discovered_ip(const tal_t *ctx UNNEEDED, const struct wireaddr *discovered_ip UNNEEDED) +{ fprintf(stderr, "towire_gossipd_discovered_ip called!\n"); abort(); } /* Generated stub for towire_hsmd_sign_bolt12 */ u8 *towire_hsmd_sign_bolt12(const tal_t *ctx UNNEEDED, const wirestring *messagename UNNEEDED, const wirestring *fieldname UNNEEDED, const struct sha256 *merkleroot UNNEEDED, const u8 *publictweak UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_bolt12 called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index a3aa98eb6f24..b2cb8c0410eb 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -611,11 +611,6 @@ struct command_result *param_short_channel_id(struct command *cmd UNNEEDED, const jsmntok_t *tok UNNEEDED, struct short_channel_id **scid UNNEEDED) { fprintf(stderr, "param_short_channel_id called!\n"); abort(); } -/* Generated stub for param_string */ -struct command_result *param_string(struct command *cmd UNNEEDED, const char *name UNNEEDED, - const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - const char **str UNNEEDED) -{ fprintf(stderr, "param_string called!\n"); abort(); } /* Generated stub for parse_onionpacket */ struct onionpacket *parse_onionpacket(const tal_t *ctx UNNEEDED, const u8 *src UNNEEDED, @@ -774,9 +769,9 @@ u8 *towire_final_incorrect_cltv_expiry(const tal_t *ctx UNNEEDED, u32 cltv_expir /* Generated stub for towire_final_incorrect_htlc_amount */ u8 *towire_final_incorrect_htlc_amount(const tal_t *ctx UNNEEDED, struct amount_msat incoming_htlc_amt UNNEEDED) { fprintf(stderr, "towire_final_incorrect_htlc_amount called!\n"); abort(); } -/* Generated stub for towire_gossipd_remote_addr */ -u8 *towire_gossipd_remote_addr(const tal_t *ctx UNNEEDED, const struct wireaddr *remote_addr UNNEEDED) -{ fprintf(stderr, "towire_gossipd_remote_addr called!\n"); abort(); } +/* Generated stub for towire_gossipd_discovered_ip */ +u8 *towire_gossipd_discovered_ip(const tal_t *ctx UNNEEDED, const struct wireaddr *discovered_ip UNNEEDED) +{ fprintf(stderr, "towire_gossipd_discovered_ip called!\n"); abort(); } /* Generated stub for towire_hsmd_get_output_scriptpubkey */ u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED) { fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); } From 5d259348657324dbfb2e3bcb4bf8a6280e8d6533 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Sep 2022 09:35:52 +0930 Subject: [PATCH 1361/1530] plugins/Makefile: regenerate plugins list when config changes. In particular, if rust is enabled/disabled. Signed-off-by: Rusty Russell --- plugins/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/Makefile b/plugins/Makefile index 426b9e848bee..d9d49c23cded 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -198,7 +198,7 @@ plugins/funder: bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN # Generated from PLUGINS definition in plugins/Makefile ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h -plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile +plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile config.vars @$(call VERBOSE,GEN $@,echo "static const char *list_of_builtin_plugins[] = { $(foreach d,$(notdir $(PLUGINS)),\"$d\",) NULL };" > $@) CLN_PLUGIN_EXAMPLES := target/${RUST_PROFILE}/examples/cln-plugin-startup From 88354b79bd5bd067abea32781865a2644cc74bcd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Sep 2022 09:42:29 +0930 Subject: [PATCH 1362/1530] common: helper to get id field as a string. We'll be doing this quite a bit, so provide common helper. Signed-off-by: Rusty Russell --- common/json_parse_simple.c | 9 +++++++++ common/json_parse_simple.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/common/json_parse_simple.c b/common/json_parse_simple.c index cf781611ec76..f7ce964cae13 100644 --- a/common/json_parse_simple.c +++ b/common/json_parse_simple.c @@ -187,6 +187,15 @@ const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index) return NULL; } +const char *json_get_id(const tal_t *ctx, + const char *buffer, const jsmntok_t *obj) +{ + const jsmntok_t *idtok = json_get_member(buffer, obj, "id"); + if (!idtok) + return NULL; + return json_strdup(ctx, buffer, idtok); +} + /*----------------------------------------------------------------------------- JSMN Result Validation Starts -----------------------------------------------------------------------------*/ diff --git a/common/json_parse_simple.h b/common/json_parse_simple.h index d0670b013b88..4188a4eb4a84 100644 --- a/common/json_parse_simple.h +++ b/common/json_parse_simple.h @@ -62,6 +62,10 @@ const jsmntok_t *json_get_member(const char *buffer, const jsmntok_t tok[], /* Get index'th array member. */ const jsmntok_t *json_get_arr(const jsmntok_t tok[], size_t index); +/* Helper to get "id" field from object. */ +const char *json_get_id(const tal_t *ctx, + const char *buffer, const jsmntok_t *obj); + /* Allocate a starter array of tokens for json_parse_input */ jsmntok_t *toks_alloc(const tal_t *ctx); From 6c07f1365fa8adfb1e8f5f896177c88ec15be924 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Sep 2022 09:42:32 +0930 Subject: [PATCH 1363/1530] lightning-cli: don't consume 100% CPU if lightningd crashes. Signed-off-by: Rusty Russell --- cli/lightning-cli.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index 12f3ad7db3fb..2f6066e75187 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -572,7 +572,7 @@ static void enable_notifications(int fd) memset(rbuf, 0, sizeof(rbuf)); while (!strends(rbuf, "\n\n")) { size_t len = strlen(rbuf); - if (cli_read(fd, rbuf + len, sizeof(rbuf) - len) < 0) + if (cli_read(fd, rbuf + len, sizeof(rbuf) - len) <= 0) err(ERROR_TALKING_TO_LIGHTNINGD, "Reading enable response"); } From 2d7cf153ad4b99656e34eea5e8348d829c77fe23 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 12 Sep 2022 09:42:46 +0930 Subject: [PATCH 1364/1530] lightningd: log JSON request ids. Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 14378139fce5..fea4de13cce2 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -909,6 +909,8 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) "Expected string for method"); } + log_debug(jcon->log, "IN:id=%s", c->id); + c->json_cmd = find_cmd(jcon->ld->jsonrpc, jcon->buffer, method); if (!c->json_cmd) { return command_fail( @@ -1338,6 +1340,8 @@ struct jsonrpc_request *jsonrpc_request_start_( json_add_u64(r->stream, "id", r->id); json_add_string(r->stream, "method", method); json_object_start(r->stream, "params"); + if (log) + log_debug(log, "OUT:id=%"PRIu64, r->id); } return r; From 87112415356ac08ac59de05af3b9a17e418feab3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:45:55 +0930 Subject: [PATCH 1365/1530] lightning-cli: use cli:- for all requests. This is the format we should standardize on. Signed-off-by: Rusty Russell --- cli/lightning-cli.c | 15 +++++++++++++-- cli/test/run-human-mode.c | 2 +- cli/test/run-large-input.c | 2 +- cli/test/run-remove-hint.c | 2 +- tests/test_misc.py | 3 +++ 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index 2f6066e75187..9ef4b81246af 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -562,9 +562,15 @@ static bool handle_notify(const char *buf, jsmntok_t *toks, static void enable_notifications(int fd) { - const char *enable = "{ \"jsonrpc\": \"2.0\", \"method\": \"notifications\", \"id\": 0, \"params\": { \"enable\": true } }"; + const char *enable; char rbuf[100]; + enable = tal_fmt(tmpctx, + "{\"jsonrpc\":\"2.0\"," + "\"method\":\"notifications\"," + "\"id\":\"cli:notifications#%i\"," + "\"params\":{\"enable\":true}}", + getpid()); if (!write_all(fd, enable, strlen(enable))) err(ERROR_TALKING_TO_LIGHTNINGD, "Writing enable command"); @@ -696,7 +702,12 @@ int main(int argc, char *argv[]) err(ERROR_TALKING_TO_LIGHTNINGD, "Connecting to '%s'", rpc_filename); - idstr = tal_fmt(ctx, "lightning-cli-%i", getpid()); + /* We use weird methodnames in test_misc.py::test_cli(), and then + * complain the cln mangles it. So omit method in that case */ + if (json_escape_needed(method, strlen(method))) + idstr = tal_fmt(ctx, "cli:weirdmethod!#%i", getpid()); + else + idstr = tal_fmt(ctx, "cli:%s#%i", method, getpid()); if (notification_level <= LOG_LEVEL_MAX) enable_notifications(fd); diff --git a/cli/test/run-human-mode.c b/cli/test/run-human-mode.c index 078af84652d1..96b1e31c0a0c 100644 --- a/cli/test/run-human-mode.c +++ b/cli/test/run-human-mode.c @@ -134,7 +134,7 @@ int test_chdir(const char *path) static const char *response = "{ \"jsonrpc\":\"2.0\"," - "\"id\":\"lightning-cli-9999\"," + "\"id\":\"cli:listconfigs#9999\"," "\"result\":" "{\"# version\":\"v0.9.0-240-gd7ff74e-modded\",\"lightning-dir\":\"/home/rusty/.lightning\",\"network\":\"testnet\",\"allow-deprecated-apis\":false,\"rpc-file\":\"lightning-rpc\",\"plugins\":[{\"path\":\"/home/rusty/.lightning/plugins/summary/summary.py\",\"name\":\"summary.py\",\"options\":{\"summary-currency\":\"USD\",\"summary-currency-prefix\":\"USD $\",\"summary-availability-interval\":\"300\",\"summary-availability-window\":\"72\"}}],\"important-plugins\":[{\"path\":\"/home/rusty/lightning/lightningd/../plugins/autoclean\",\"name\":\"autoclean\",\"options\":{\"autocleaninvoice-cycle\":null,\"autocleaninvoice-expired-by\":null}},{\"path\":\"/home/rusty/lightning/lightningd/../plugins/bcli\",\"name\":\"bcli\",\"options\":{\"bitcoin-datadir\":null,\"bitcoin-cli\":null,\"bitcoin-rpcuser\":null,\"bitcoin-rpcpassword\":null,\"bitcoin-rpcconnect\":null,\"bitcoin-rpcport\":null,\"bitcoin-retry-timeout\":null,\"commit-fee\":null,\"dev-max-fee-multiplier\":null}},{\"path\":\"/home/rusty/lightning/lightningd/../plugins/fundchannel\",\"name\":\"fundchannel\"},{\"path\":\"/home/rusty/lightning/lightningd/../plugins/keysend\",\"name\":\"keysend\"},{\"path\":\"/home/rusty/lightning/lightningd/../plugins/pay\",\"name\":\"pay\",\"options\":{\"disable-mpp\":false}}],\"disable-plugin\":[],\"always-use-proxy\":false,\"daemon\":\"false\",\"wallet\":\"sqlite3:///home/rusty/.lightning/testnet/lightningd.sqlite3\",\"wumbo\":false,\"wumbo\":false,\"rgb\":\"031a34\",\"alias\":\"SLICKERMASTER-40-gd7ff74e-modded\",\"pid-file\":\"/home/rusty/.lightning/lightningd-testnet.pid\",\"ignore-fee-limits\":true,\"watchtime-blocks\":6,\"max-locktime-blocks\":2016,\"funding-confirms\":1,\"commit-fee-min\":0,\"commit-fee-max\":0,\"cltv-delta\":6,\"cltv-final\":10,\"commit-time\":10,\"fee-base\":1,\"rescan\":30,\"fee-per-satoshi\":10,\"max-concurrent-htlcs\":483,\"min-capacity-sat\":10000,\"bind-addr\":\":9736\",\"announce-addr\":\"128.199.202.168:9736\",\"announce-addr\":\"[2400:6180:0:d0::5cd2:a001]:9736\",\"offline\":\"false\",\"autolisten\":true,\"disable-dns\":\"false\",\"enable-autotor-v2-mode\":\"false\",\"encrypted-hsm\":false,\"rpc-file-mode\":\"0600\",\"log-level\":\"DEBUG\",\"log-prefix\":\"lightningd\",\"log-file\":\"/home/rusty/logs/lightningd-testnet.log\",\"dev-no-reconnect\":\"false\",\"dev-allow-localhost\":\"false\",\"dev-bitcoind-poll\":30,\"dev-fast-gossip\":\"false\",\"dev-fast-gossip-prune\":\"false\",\"dev-gossip-time\":0,\"dev-max-funding-unconfirmed-blocks\":2016,\"dev-no-htlc-timeout\":\"false\",\"dev-fail-process-onionpacket\":\"false\",\"dev-no-version-checks\":\"false\",\"dev-builtin-plugins-unimportant\":\"false\"}" "}\n\n"; diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index bbba588bd359..be4f6eb2183f 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -151,7 +151,7 @@ ssize_t test_read(int fd UNUSED, void *buf, size_t len) #define NUM_ENTRIES (137772/2) #define HEADER "{ \"jsonrpc\": \"2.0\",\n" \ - " \"id\": \"lightning-cli-9999\",\n" \ + " \"id\": \"cli:test#9999\",\n" \ " \"result\" : {\n" \ " \"creation_time\" : \"1515999039.806099043\",\n" \ " \"bytes_used\" : 10787759,\n" \ diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c index e416a2973d66..7d6dcfc3238c 100644 --- a/cli/test/run-remove-hint.c +++ b/cli/test/run-remove-hint.c @@ -125,7 +125,7 @@ int test_getpid(void) return 9999; } -static char *response = "{\"id\": \"lightning-cli-9999\", \"jsonrpc\": \"2.0\", \"result\": {\"channels\": [\"\n\", \"├477308sat OUT/OURS ┼ IN/THEIRS 477308sat┤ SCID FLAG ALIAS\", \"├──────────────────────┼──────────────────────┤ 580612x1826x0 [__] BLUEIRON-v0.7.2rc1\"], \"my_address\": \"02b78caed0f45120acc48efe867aa506e8ea60f0712a23303178471da0ca2213f5@hdco6sxkbisc7s5t.onion\", \"format-hint\": \"simple\", \"avail_in\": \"0.00477308500btc (USD $54.37)\", \"num_utxos\": 0, \"num_gossipers\": 1, \"channels_flags\": \"P:private O:offline\", \"avail_out\": \"0.00477308500btc (USD $54.37)\", \"utxo_amount\": \"0.00000000btc (USD $0.00)\", \"num_channels\": 1, \"num_connected\": 1}}\n\n"; +static char *response = "{\"id\": \"cli:test#9999\", \"jsonrpc\": \"2.0\", \"result\": {\"channels\": [\"\n\", \"├477308sat OUT/OURS ┼ IN/THEIRS 477308sat┤ SCID FLAG ALIAS\", \"├──────────────────────┼──────────────────────┤ 580612x1826x0 [__] BLUEIRON-v0.7.2rc1\"], \"my_address\": \"02b78caed0f45120acc48efe867aa506e8ea60f0712a23303178471da0ca2213f5@hdco6sxkbisc7s5t.onion\", \"format-hint\": \"simple\", \"avail_in\": \"0.00477308500btc (USD $54.37)\", \"num_utxos\": 0, \"num_gossipers\": 1, \"channels_flags\": \"P:private O:offline\", \"avail_out\": \"0.00477308500btc (USD $54.37)\", \"utxo_amount\": \"0.00000000btc (USD $0.00)\", \"num_channels\": 1, \"num_connected\": 1}}\n\n"; static size_t response_off; ssize_t test_read(int fd UNUSED, void *buf, size_t len) diff --git a/tests/test_misc.py b/tests/test_misc.py index b1d7d26b0e48..744469e66948 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -873,6 +873,9 @@ def test_cli(node_factory): # Test some known output. assert 'help [command]\n List available commands, or give verbose help on one {command}' in out + # Check JSON id is as expected + l1.daemon.wait_for_log("jsonrpc#[0-9]*: IN:id=\"cli:help#[0-9]*") + # Test JSON output. out = subprocess.check_output(['cli/lightning-cli', '--network={}'.format(TEST_NETWORK), From db89a3413592eafcf424d7d8cdabc0be2ba52178 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:46:55 +0930 Subject: [PATCH 1366/1530] libplugin: allow lightningd to give us non-numeric ids. Signed-off-by: Rusty Russell --- plugins/bkpr/test/run-bkpr_db.c | 4 ++++ plugins/bkpr/test/run-recorder.c | 4 ++++ plugins/libplugin.c | 17 +++++------------ plugins/libplugin.h | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index dae0352404f7..70248b287f5f 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -97,6 +97,10 @@ int htlc_state_flags(enum htlc_state state UNNEEDED) /* Generated stub for htlc_state_name */ const char *htlc_state_name(enum htlc_state s UNNEEDED) { fprintf(stderr, "htlc_state_name called!\n"); abort(); } +/* Generated stub for json_get_id */ +const char *json_get_id(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *obj UNNEEDED) +{ fprintf(stderr, "json_get_id called!\n"); abort(); } /* Generated stub for json_get_member */ const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, const char *label UNNEEDED) diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 01b33af1cd89..a90035187eb4 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -103,6 +103,10 @@ int htlc_state_flags(enum htlc_state state UNNEEDED) /* Generated stub for htlc_state_name */ const char *htlc_state_name(enum htlc_state s UNNEEDED) { fprintf(stderr, "htlc_state_name called!\n"); abort(); } +/* Generated stub for json_get_id */ +const char *json_get_id(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *obj UNNEEDED) +{ fprintf(stderr, "json_get_id called!\n"); abort(); } /* Generated stub for json_get_member */ const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, const char *label UNNEEDED) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index d9182c281948..0aff78a7fa58 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -211,7 +212,7 @@ static struct json_stream *jsonrpc_stream_start(struct command *cmd) json_object_start(js, NULL); json_add_string(js, "jsonrpc", "2.0"); - json_add_u64(js, "id", *cmd->id); + json_add_string(js, "id", cmd->id); return js; } @@ -1236,7 +1237,7 @@ struct json_stream *plugin_notify_start(struct command *cmd, const char *method) json_add_string(js, "method", method); json_object_start(js, "params"); - json_add_u64(js, "id", *cmd->id); + json_add_string(js, "id", cmd->id); return js; } @@ -1379,10 +1380,9 @@ void plugin_set_memleak_handler(struct plugin *plugin, static void ld_command_handle(struct plugin *plugin, const jsmntok_t *toks) { - const jsmntok_t *idtok, *methtok, *paramstok; + const jsmntok_t *methtok, *paramstok; struct command *cmd; - idtok = json_get_member(plugin->buffer, toks, "id"); methtok = json_get_member(plugin->buffer, toks, "method"); paramstok = json_get_member(plugin->buffer, toks, "params"); @@ -1394,16 +1394,9 @@ static void ld_command_handle(struct plugin *plugin, cmd = tal(plugin, struct command); cmd->plugin = plugin; - cmd->id = NULL; cmd->usage_only = false; cmd->methodname = json_strdup(cmd, plugin->buffer, methtok); - if (idtok) { - cmd->id = tal(cmd, u64); - if (!json_to_u64(plugin->buffer, idtok, cmd->id)) - plugin_err(plugin, "JSON id '%*.s' is not a number", - json_tok_full_len(idtok), - json_tok_full(plugin->buffer, idtok)); - } + cmd->id = json_get_id(cmd, plugin->buffer, toks); if (!plugin->manifested) { if (streq(cmd->methodname, "getmanifest")) { diff --git a/plugins/libplugin.h b/plugins/libplugin.h index c90a680c7d10..50c24da89b3d 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -51,7 +51,7 @@ struct out_req { }; struct command { - u64 *id; + const char *id; const char *methodname; bool usage_only; struct plugin *plugin; From bd18fbc4882e9723f47f185f4b4cf4abcd242371 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:47:55 +0930 Subject: [PATCH 1367/1530] contrib/pyln-client: allow lightningd to give us non-numeric ids. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/plugin.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/contrib/pyln-client/pyln/client/plugin.py b/contrib/pyln-client/pyln/client/plugin.py index efc9b2ac2b69..8e437fc8bd22 100644 --- a/contrib/pyln-client/pyln/client/plugin.py +++ b/contrib/pyln-client/pyln/client/plugin.py @@ -72,7 +72,7 @@ def __init__(self, message: str, code: int = -32600): class Request(dict): """A request object that wraps params and allows async return """ - def __init__(self, plugin: 'Plugin', req_id: Optional[int], method: str, + def __init__(self, plugin: 'Plugin', req_id: Optional[str], method: str, params: Any, background: bool = False): self.method = method self.params = params @@ -700,13 +700,9 @@ def notify_progress(self, request: Request, request.progress(progress, progress_total, stage, stage_total) def _parse_request(self, jsrequest: Dict[str, JSONType]) -> Request: - i = jsrequest.get('id', None) - if not isinstance(i, int) and i is not None: - raise ValueError('Non-integer request id "{i}"'.format(i=i)) - request = Request( plugin=self, - req_id=i, + req_id=jsrequest.get('id', None), method=str(jsrequest['method']), params=jsrequest['params'], background=False, From ce0b765c9604e8bb74a3dc88db1451ace9a3f378 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:48:55 +0930 Subject: [PATCH 1368/1530] cln-rpc: allow id to be any token. Suggested-by: @cdecker Signed-off-by: Rusty Russell --- cln-rpc/src/jsonrpc.rs | 4 ++-- cln-rpc/src/lib.rs | 5 +++-- plugins/src/lib.rs | 8 ++++---- plugins/src/messages.rs | 8 ++++---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/cln-rpc/src/jsonrpc.rs b/cln-rpc/src/jsonrpc.rs index 99a5469b13ac..a3930d9c3dd3 100644 --- a/cln-rpc/src/jsonrpc.rs +++ b/cln-rpc/src/jsonrpc.rs @@ -9,7 +9,7 @@ use std::fmt::Debug; #[derive(Debug)] pub enum JsonRpc { - Request(usize, R), + Request(serde_json::Value, R), Notification(N), } @@ -38,7 +38,7 @@ where { #[derive(Deserialize, Debug)] struct IdHelper { - id: Option, + id: Option, } let v = Value::deserialize(deserializer)?; diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs index d53ad0cad39b..cd2758d0a3d5 100644 --- a/cln-rpc/src/lib.rs +++ b/cln-rpc/src/lib.rs @@ -5,6 +5,7 @@ use anyhow::Result; use futures_util::sink::SinkExt; use futures_util::StreamExt; use log::{debug, trace}; +use serde_json::json; use std::path::Path; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; @@ -60,7 +61,7 @@ impl ClnRpc { // Wrap the raw request in a well-formed JSON-RPC outer dict. let id = self.next_id.fetch_add(1, Ordering::SeqCst); - let req: JsonRpc = JsonRpc::Request(id, req); + let req: JsonRpc = JsonRpc::Request(json!(id), req); let req = serde_json::to_value(req).map_err(|e| RpcError { code: None, message: format!("Error parsing request: {}", e), @@ -137,7 +138,7 @@ mod test { let read_req = dbg!(read.next().await.unwrap().unwrap()); assert_eq!( - json!({"id": 1, "method": "getinfo", "params": {}, "jsonrpc": "2.0"}), + json!({"id": "1", "method": "getinfo", "params": {}, "jsonrpc": "2.0"}), read_req ); } diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 1da5f379c21c..56237d5c0928 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -364,7 +364,7 @@ pub struct ConfiguredPlugin where S: Clone + Send, { - init_id: usize, + init_id: serde_json::Value, input: FramedRead, output: Arc>>, plugin: Plugin, @@ -543,7 +543,7 @@ where self.dispatch_notification(n, plugin).await } messages::JsonRpc::CustomRequest(id, p) => { - match self.dispatch_custom_request(id, p, plugin).await { + match self.dispatch_custom_request(id.clone(), p, plugin).await { Ok(v) => plugin .sender .send(json!({ @@ -575,7 +575,7 @@ where } async fn dispatch_request( - _id: usize, + _id: serde_json::Value, _request: messages::Request, _plugin: &Plugin, ) -> Result<(), Error> { @@ -595,7 +595,7 @@ where async fn dispatch_custom_request( &self, - _id: usize, + _id: serde_json::Value, request: serde_json::Value, plugin: &Plugin, ) -> Result { diff --git a/plugins/src/messages.rs b/plugins/src/messages.rs index 9b23e4395e0c..89722b997b1d 100644 --- a/plugins/src/messages.rs +++ b/plugins/src/messages.rs @@ -94,9 +94,9 @@ pub struct ProxyInfo { #[derive(Debug)] pub enum JsonRpc { - Request(usize, R), + Request(serde_json::Value, R), Notification(N), - CustomRequest(usize, Value), + CustomRequest(serde_json::Value, Value), CustomNotification(Value), } @@ -125,7 +125,7 @@ where { #[derive(Deserialize, Debug)] struct IdHelper { - id: Option, + id: Option, } let v = Value::deserialize(deserializer)?; @@ -203,7 +203,7 @@ mod test { "always_use_proxy": false } }, - "id": 10, + "id": "10", }); let req: JsonRpc = serde_json::from_value(value).unwrap(); match req { From 99f2019a24738fa4f7b003d345e73813c76d393e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:11 +0930 Subject: [PATCH 1369/1530] lightningd: add jsonrpc_request_start_raw instead of NULL method. Since we want to use methodname to create id, don't overload it for a raw request. Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 16 ++++++++-------- lightningd/jsonrpc.h | 18 ++++++++++++++++-- lightningd/plugin.c | 6 +++--- lightningd/test/run-invoice-select-inchan.c | 2 +- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index fea4de13cce2..7654016401fc 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -1312,6 +1312,7 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n) struct jsonrpc_request *jsonrpc_request_start_( const tal_t *ctx, const char *method, struct log *log, + bool add_header, void (*notify_cb)(const char *buffer, const jsmntok_t *methodtok, const jsmntok_t *paramtoks, @@ -1327,22 +1328,21 @@ struct jsonrpc_request *jsonrpc_request_start_( r->notify_cb = notify_cb; r->response_cb = response_cb; r->response_cb_arg = response_cb_arg; - r->method = NULL; + r->method = tal_strdup(r, method); r->stream = new_json_stream(r, NULL, log); - /* If no method is specified we don't prefill the JSON-RPC - * request with the header. This serves as an escape hatch to - * get a raw request, but get a valid request-id assigned. */ - if (method != NULL) { - r->method = tal_strdup(r, method); + /* Disabling this serves as an escape hatch for plugin code to + * get a raw request to paste into, but get a valid request-id + * assigned. */ + if (add_header) { json_object_start(r->stream, NULL); json_add_string(r->stream, "jsonrpc", "2.0"); json_add_u64(r->stream, "id", r->id); json_add_string(r->stream, "method", method); json_object_start(r->stream, "params"); - if (log) - log_debug(log, "OUT:id=%"PRIu64, r->id); } + if (log) + log_debug(log, "OUT:id=%"PRIu64, r->id); return r; } diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 43ed29b30f9c..6339e4c12cab 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -217,7 +217,7 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n); #define jsonrpc_request_start(ctx, method, log, notify_cb, response_cb, response_cb_arg) \ jsonrpc_request_start_( \ - (ctx), (method), (log), \ + (ctx), (method), (log), true, \ typesafe_cb_preargs(void, void *, (notify_cb), (response_cb_arg), \ const char *buffer, \ const jsmntok_t *idtok, \ @@ -229,8 +229,22 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n); const jsmntok_t *idtok), \ (response_cb_arg)) +#define jsonrpc_request_start_raw(ctx, method, log, notify_cb, response_cb, response_cb_arg) \ + jsonrpc_request_start_( \ + (ctx), (method), (log), false, \ + typesafe_cb_preargs(void, void *, (notify_cb), (response_cb_arg), \ + const char *buffer, \ + const jsmntok_t *idtok, \ + const jsmntok_t *methodtok, \ + const jsmntok_t *paramtoks), \ + typesafe_cb_preargs(void, void *, (response_cb), (response_cb_arg), \ + const char *buffer, \ + const jsmntok_t *toks, \ + const jsmntok_t *idtok), \ + (response_cb_arg)) + struct jsonrpc_request *jsonrpc_request_start_( - const tal_t *ctx, const char *method, struct log *log, + const tal_t *ctx, const char *method, struct log *log, bool add_header, void (*notify_cb)(const char *buffer, const jsmntok_t *idtok, const jsmntok_t *methodtok, diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 7abd04b7a9fb..25a8703ba0f2 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1129,9 +1129,9 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, call = tal(plugin, struct plugin_rpccall); call->cmd = cmd; - req = jsonrpc_request_start(plugin, NULL, plugin->log, - plugin_notify_cb, - plugin_rpcmethod_cb, call); + req = jsonrpc_request_start_raw(plugin, cmd->json_cmd->name, plugin->log, + plugin_notify_cb, + plugin_rpcmethod_cb, call); call->request = req; call->plugin = plugin; list_add_tail(&plugin->pending_rpccalls, &call->list); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index d25a7da1b52a..4550e0e0f79c 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -491,7 +491,7 @@ void jsonrpc_request_end(struct jsonrpc_request *request UNNEEDED) { fprintf(stderr, "jsonrpc_request_end called!\n"); abort(); } /* Generated stub for jsonrpc_request_start_ */ struct jsonrpc_request *jsonrpc_request_start_( - const tal_t *ctx UNNEEDED, const char *method UNNEEDED, struct log *log UNNEEDED, + const tal_t *ctx UNNEEDED, const char *method UNNEEDED, struct log *log UNNEEDED, bool add_header UNNEEDED, void (*notify_cb)(const char *buffer UNNEEDED, const jsmntok_t *idtok UNNEEDED, const jsmntok_t *methodtok UNNEEDED, From ed3f700991ce9c8b1a41cdff24d5d250f8190426 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:11 +0930 Subject: [PATCH 1370/1530] lightningd: use string as json req ids when we create them. Signed-off-by: Rusty Russell --- contrib/plugins/cowsay.sh | 4 +-- lightningd/jsonrpc.c | 7 ++--- lightningd/jsonrpc.h | 2 +- lightningd/plugin.c | 57 ++++++++++++++++++++++----------------- lightningd/plugin.h | 2 +- tests/test_invoices.py | 4 +++ 6 files changed, 44 insertions(+), 32 deletions(-) diff --git a/contrib/plugins/cowsay.sh b/contrib/plugins/cowsay.sh index c995420392d0..8d8a40dd48df 100755 --- a/contrib/plugins/cowsay.sh +++ b/contrib/plugins/cowsay.sh @@ -17,14 +17,14 @@ EOF # Eg. {"jsonrpc":"2.0","id":2,"method":"getmanifest","params":{}}\n\n read -r JSON read -r _ -id=$(echo "$JSON" | sed 's/.*"id" *: *\([0-9]*\),.*/\1/') +id=$(echo "$JSON" | sed 's/.*"id" *: *\([^,]*\),.*/\1/') echo '{"jsonrpc":"2.0","id":'"$id"',"result":{"dynamic":true,"options":[],"rpcmethods":[{"name":"cowsay","usage":"","description":"Have a cow, man!"}]}}' # Eg. {"jsonrpc":"2.0","id":5,"method":"init","params":{"options":{},"configuration":{"lightning-dir":"/home/rusty/.lightning","rpc-file":"lightning-rpc","startup":false}}}\n\n read -r JSON read -r _ -id=$(echo "$JSON" | sed 's/.*"id" *: *\([0-9]*\),.*/\1/') +id=$(echo "$JSON" | sed 's/.*"id" *: *\([^,]*\),.*/\1/') echo '{"jsonrpc":"2.0","id":'"$id"',"result":{}}' diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 7654016401fc..621e3147e997 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -1324,7 +1324,8 @@ struct jsonrpc_request *jsonrpc_request_start_( { struct jsonrpc_request *r = tal(ctx, struct jsonrpc_request); static u64 next_request_id = 0; - r->id = next_request_id++; + r->id = tal_fmt(r, "cln:%s#%"PRIu64, method, next_request_id); + next_request_id++; r->notify_cb = notify_cb; r->response_cb = response_cb; r->response_cb_arg = response_cb_arg; @@ -1337,12 +1338,12 @@ struct jsonrpc_request *jsonrpc_request_start_( if (add_header) { json_object_start(r->stream, NULL); json_add_string(r->stream, "jsonrpc", "2.0"); - json_add_u64(r->stream, "id", r->id); + json_add_string(r->stream, "id", r->id); json_add_string(r->stream, "method", method); json_object_start(r->stream, "params"); } if (log) - log_debug(log, "OUT:id=%"PRIu64, r->id); + log_debug(log, "OUT:id=%s", r->id); return r; } diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 6339e4c12cab..659f65e3cac7 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -68,7 +68,7 @@ struct jsonrpc_notification { }; struct jsonrpc_request { - u64 id; + const char *id; const char *method; struct json_stream *stream; void (*notify_cb)(const char *buffer, diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 25a8703ba0f2..075b886e6b6d 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -48,7 +48,7 @@ struct plugin_rpccall { static void memleak_help_pending_requests(struct htable *memtable, struct plugins *plugins) { - memleak_remove_uintmap(memtable, &plugins->pending_requests); + memleak_remove_strmap(memtable, &plugins->pending_requests); } #endif /* DEVELOPER */ @@ -86,7 +86,7 @@ struct plugins *plugins_new(const tal_t *ctx, struct log_book *log_book, #if DEVELOPER p->dev_builtin_plugins_unimportant = false; #endif /* DEVELOPER */ - uintmap_init(&p->pending_requests); + strmap_init(&p->pending_requests); memleak_add_helper(p, memleak_help_pending_requests); return p; @@ -435,17 +435,18 @@ static const char *plugin_notify_handle(struct plugin *plugin, const jsmntok_t *paramstok) { const jsmntok_t *idtok; - u64 id; struct jsonrpc_request *request; /* id inside params tells us which id to redirect to. */ idtok = json_get_member(plugin->buffer, paramstok, "id"); - if (!idtok || !json_to_u64(plugin->buffer, idtok, &id)) { + if (!idtok) { return tal_fmt(plugin, - "JSON-RPC notify \"id\"-field is not a u64"); + "JSON-RPC notify \"id\"-field is not present"); } - request = uintmap_get(&plugin->plugins->pending_requests, id); + request = strmap_getn(&plugin->plugins->pending_requests, + plugin->buffer + idtok->start, + idtok->end - idtok->start); if (!request) { return tal_fmt( plugin, @@ -565,16 +566,10 @@ static const char *plugin_response_handle(struct plugin *plugin, { struct plugin_destroyed *pd; struct jsonrpc_request *request; - u64 id; - /* We only send u64 ids, so if this fails it's a critical error (note - * that this also works if id is inside a JSON string!). */ - if (!json_to_u64(plugin->buffer, idtok, &id)) { - return tal_fmt(plugin, - "JSON-RPC response \"id\"-field is not a u64"); - } - - request = uintmap_get(&plugin->plugins->pending_requests, id); + request = strmap_getn(&plugin->plugins->pending_requests, + plugin->buffer + idtok->start, + idtok->end - idtok->start); if (!request) { return tal_fmt( plugin, @@ -1029,18 +1024,31 @@ static void json_stream_forward_change_id(struct json_stream *stream, const char *buffer, const jsmntok_t *toks, const jsmntok_t *idtok, - const char *new_id) + const char *new_id, + bool add_quotes) { /* We copy everything, but replace the id. Special care has to * be taken when the id that is being replaced is a string. If * we don't crop the quotes off we'll transform a numeric * new_id into a string, or even worse, quote a string id * twice. */ - size_t offset = idtok->type==JSMN_STRING?1:0; + size_t offset = 0; + + if (idtok->type == JSMN_STRING) { + if (add_quotes) + add_quotes = false; + else + offset = 1; + } + json_stream_append(stream, buffer + toks->start, idtok->start - toks->start - offset); + if (add_quotes) + json_stream_append(stream, "\"", 1); json_stream_append(stream, new_id, strlen(new_id)); + if (add_quotes) + json_stream_append(stream, "\"", 1); json_stream_append(stream, buffer + idtok->end + offset, toks->end - idtok->end - offset); } @@ -1054,7 +1062,8 @@ static void plugin_rpcmethod_cb(const char *buffer, struct json_stream *response; response = json_stream_raw_for_cmd(cmd); - json_stream_forward_change_id(response, buffer, toks, idtok, cmd->id); + json_stream_forward_change_id(response, buffer, toks, idtok, cmd->id, + false); json_stream_double_cr(response); command_raw_complete(cmd, response); @@ -1080,7 +1089,7 @@ static void plugin_notify_cb(const char *buffer, json_add_tok(response, "method", methodtok, buffer); json_stream_append(response, ",\"params\":", strlen(",\"params\":")); json_stream_forward_change_id(response, buffer, - paramtoks, idtok, cmd->id); + paramtoks, idtok, cmd->id, false); json_object_end(response); json_stream_double_cr(response); @@ -1112,7 +1121,6 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, const jsmntok_t *idtok; struct plugin *plugin; struct jsonrpc_request *req; - char id[STR_MAX_CHARS(u64)]; struct plugin_rpccall *call; if (cmd->mode == CMD_CHECK) @@ -1136,9 +1144,8 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, call->plugin = plugin; list_add_tail(&plugin->pending_rpccalls, &call->list); - snprintf(id, ARRAY_SIZE(id), "%"PRIu64, req->id); - - json_stream_forward_change_id(req->stream, buffer, toks, idtok, id); + json_stream_forward_change_id(req->stream, buffer, toks, idtok, req->id, + true); json_stream_double_cr(req->stream); plugin_request_send(plugin, req); req->stream = NULL; @@ -2048,7 +2055,7 @@ void plugins_notify(struct plugins *plugins, static void destroy_request(struct jsonrpc_request *req, struct plugin *plugin) { - uintmap_del(&plugin->plugins->pending_requests, req->id); + strmap_del(&plugin->plugins->pending_requests, req->id, NULL); } void plugin_request_send(struct plugin *plugin, @@ -2056,7 +2063,7 @@ void plugin_request_send(struct plugin *plugin, { /* Add to map so we can find it later when routing the response */ tal_steal(plugin, req); - uintmap_add(&plugin->plugins->pending_requests, req->id, req); + strmap_add(&plugin->plugins->pending_requests, req->id, req); /* Add destructor in case plugin dies. */ tal_add_destructor2(req, destroy_request, plugin); plugin_send(plugin, req->stream); diff --git a/lightningd/plugin.h b/lightningd/plugin.h index 14b13ff54a73..4b33b736e0a7 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -100,7 +100,7 @@ struct plugins { bool startup; /* Currently pending requests by their request ID */ - UINTMAP(struct jsonrpc_request *) pending_requests; + STRMAP(struct jsonrpc_request *) pending_requests; struct log *log; struct log_book *log_book; diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 84315bc42628..743caf9bd50c 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -16,6 +16,10 @@ def test_invoice(node_factory, chainparams): addr2 = l2.rpc.newaddr('p2sh-segwit')['p2sh-segwit'] before = int(time.time()) inv = l1.rpc.invoice(123000, 'label', 'description', 3700, [addr1, addr2]) + + # Side note: invoice calls out to listincoming, so check JSON id is as expected + l1.daemon.wait_for_log(": OUT:id=cln:listincoming#[0-9]*") + after = int(time.time()) b11 = l1.rpc.decodepay(inv['bolt11']) assert b11['currency'] == chainparams['bip173_prefix'] From 8fcf880e0ff6acd37e1d887a9e46166c260fb23e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:11 +0930 Subject: [PATCH 1371/1530] lightningd: explicitly remember if JSON id was a string. This lets us use 'cmd->id' as an unquoted string (for building new ids!). Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 19 +++++++++++-------- lightningd/jsonrpc.h | 2 ++ lightningd/plugin.c | 13 +++++++++---- tests/test_misc.py | 2 +- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 621e3147e997..93aec76f3adf 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -192,9 +192,7 @@ static struct command_result *json_stop(struct command *cmd, jout = json_out_new(tmpctx); json_out_start(jout, NULL, '{'); json_out_addstr(jout, "jsonrpc", "2.0"); - /* id may be a string or number, so copy direct. */ - memcpy(json_out_member_direct(jout, "id", strlen(cmd->id)), - cmd->id, strlen(cmd->id)); + json_out_add(jout, "id", cmd->id_is_string, "%s", cmd->id); json_out_addstr(jout, "result", "Shutdown complete"); json_out_end(jout, '}'); json_out_finished(jout); @@ -533,7 +531,10 @@ void json_notify_fmt(struct command *cmd, json_add_string(js, "jsonrpc", "2.0"); json_add_string(js, "method", "message"); json_object_start(js, "params"); - json_add_string(js, "id", cmd->id); + if (cmd->id_is_string) + json_add_string(js, "id", cmd->id); + else + json_add_jsonstr(js, "id", cmd->id, strlen(cmd->id)); json_add_string(js, "level", log_level_name(level)); json_add_string(js, "message", tal_vfmt(tmpctx, fmt, ap)); json_object_end(js); @@ -578,7 +579,10 @@ static struct json_stream *json_start(struct command *cmd) json_object_start(js, NULL); json_add_string(js, "jsonrpc", "2.0"); - json_add_jsonstr(js, "id", cmd->id, strlen(cmd->id)); + if (cmd->id_is_string) + json_add_string(js, "id", cmd->id); + else + json_add_jsonstr(js, "id", cmd->id, strlen(cmd->id)); return js; } @@ -892,9 +896,8 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) c->ld = jcon->ld; c->pending = false; c->json_stream = NULL; - c->id = tal_strndup(c, - json_tok_full(jcon->buffer, id), - json_tok_full_len(id)); + c->id_is_string = (id->type == JSMN_STRING); + c->id = json_strdup(c, jcon->buffer, id); c->mode = CMD_NORMAL; list_add_tail(&jcon->commands, &c->list); tal_add_destructor(c, destroy_command); diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 659f65e3cac7..92b4e7722ff6 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -27,6 +27,8 @@ struct command { struct lightningd *ld; /* The 'id' which we need to include in the response. */ const char *id; + /* If 'id' needs to be quoted (i.e. it's a string) */ + bool id_is_string; /* What command we're running (for logging) */ const struct json_command *json_cmd; /* The connection, or NULL if it closed. */ diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 075b886e6b6d..20c0b1c2f2cc 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1025,7 +1025,7 @@ static void json_stream_forward_change_id(struct json_stream *stream, const jsmntok_t *toks, const jsmntok_t *idtok, const char *new_id, - bool add_quotes) + bool new_id_is_str) { /* We copy everything, but replace the id. Special care has to * be taken when the id that is being replaced is a string. If @@ -1033,12 +1033,16 @@ static void json_stream_forward_change_id(struct json_stream *stream, * new_id into a string, or even worse, quote a string id * twice. */ size_t offset = 0; + bool add_quotes = false; if (idtok->type == JSMN_STRING) { - if (add_quotes) + if (new_id_is_str) add_quotes = false; else offset = 1; + } else { + if (new_id_is_str) + add_quotes = true; } json_stream_append(stream, buffer + toks->start, @@ -1063,7 +1067,7 @@ static void plugin_rpcmethod_cb(const char *buffer, response = json_stream_raw_for_cmd(cmd); json_stream_forward_change_id(response, buffer, toks, idtok, cmd->id, - false); + cmd->id_is_string); json_stream_double_cr(response); command_raw_complete(cmd, response); @@ -1089,7 +1093,8 @@ static void plugin_notify_cb(const char *buffer, json_add_tok(response, "method", methodtok, buffer); json_stream_append(response, ",\"params\":", strlen(",\"params\":")); json_stream_forward_change_id(response, buffer, - paramtoks, idtok, cmd->id, false); + paramtoks, idtok, cmd->id, + cmd->id_is_string); json_object_end(response); json_stream_double_cr(response); diff --git a/tests/test_misc.py b/tests/test_misc.py index 744469e66948..b86449b5fe5f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -874,7 +874,7 @@ def test_cli(node_factory): assert 'help [command]\n List available commands, or give verbose help on one {command}' in out # Check JSON id is as expected - l1.daemon.wait_for_log("jsonrpc#[0-9]*: IN:id=\"cli:help#[0-9]*") + l1.daemon.wait_for_log("jsonrpc#[0-9]*: IN:id=cli:help#[0-9]*") # Test JSON output. out = subprocess.check_output(['cli/lightning-cli', From a9557d5194f8aa6b41c2c35a6e17d47bf9d87e37 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:11 +0930 Subject: [PATCH 1372/1530] lightningd: derive JSONRPC ids from incoming id (append /cln:#NNN). Usually the calls are spontanous, so it's just "cln:#NNN", but json_invoice() calls listincoming, and json_checkmessage calls listnodes, so those become "cli:invoice-/cln:listincoming#NNN". Signed-off-by: Rusty Russell --- lightningd/bitcoind.c | 15 +++++++++------ lightningd/invoice.c | 1 + lightningd/jsonrpc.c | 9 +++++++-- lightningd/jsonrpc.h | 15 ++++++++++----- lightningd/plugin.c | 8 +++++--- lightningd/plugin_hook.c | 6 ++++-- lightningd/signmessage.c | 1 + lightningd/test/run-invoice-select-inchan.c | 3 ++- tests/test_invoices.py | 2 +- 9 files changed, 40 insertions(+), 20 deletions(-) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index d19968e3a7ac..93eef2382e5a 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -49,7 +49,7 @@ static void config_plugin(struct plugin *plugin) struct jsonrpc_request *req; void *ret; - req = jsonrpc_request_start(plugin, "init", plugin->log, + req = jsonrpc_request_start(plugin, "init", NULL, plugin->log, NULL, plugin_config_cb, plugin); plugin_populate_init_request(plugin, req); jsonrpc_request_end(req); @@ -237,7 +237,8 @@ void bitcoind_estimate_fees_(struct bitcoind *bitcoind, call->cb = cb; call->arg = arg; - req = jsonrpc_request_start(bitcoind, "estimatefees", bitcoind->log, + req = jsonrpc_request_start(bitcoind, "estimatefees", NULL, + bitcoind->log, NULL, estimatefees_callback, call); jsonrpc_request_end(req); plugin_request_send(strmap_get(&bitcoind->pluginsmap, @@ -312,7 +313,8 @@ void bitcoind_sendrawtx_ahf_(struct bitcoind *bitcoind, call->cb_arg = cb_arg; log_debug(bitcoind->log, "sendrawtransaction: %s", hextx); - req = jsonrpc_request_start(bitcoind, "sendrawtransaction", + /* FIXME: pass id_prefix from caller! */ + req = jsonrpc_request_start(bitcoind, "sendrawtransaction", NULL, bitcoind->log, NULL, sendrawtx_callback, call); @@ -408,7 +410,7 @@ void bitcoind_getrawblockbyheight_(struct bitcoind *bitcoind, call->cb = cb; call->cb_arg = cb_arg; - req = jsonrpc_request_start(bitcoind, "getrawblockbyheight", + req = jsonrpc_request_start(bitcoind, "getrawblockbyheight", NULL, bitcoind->log, NULL, getrawblockbyheight_callback, call); @@ -489,7 +491,8 @@ void bitcoind_getchaininfo_(struct bitcoind *bitcoind, call->cb_arg = cb_arg; call->first_call = first_call; - req = jsonrpc_request_start(bitcoind, "getchaininfo", bitcoind->log, + req = jsonrpc_request_start(bitcoind, "getchaininfo", NULL, + bitcoind->log, NULL, getchaininfo_callback, call); jsonrpc_request_end(req); bitcoin_plugin_send(bitcoind, req); @@ -561,7 +564,7 @@ void bitcoind_getutxout_(struct bitcoind *bitcoind, call->cb = cb; call->cb_arg = cb_arg; - req = jsonrpc_request_start(bitcoind, "getutxout", bitcoind->log, + req = jsonrpc_request_start(bitcoind, "getutxout", NULL, bitcoind->log, NULL, getutxout_callback, call); json_add_txid(req->stream, "txid", &outpoint->txid); json_add_num(req->stream, "vout", outpoint->n); diff --git a/lightningd/invoice.c b/lightningd/invoice.c index ee3d4a0f06b0..c8a9d817c641 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1250,6 +1250,7 @@ static struct command_result *json_invoice(struct command *cmd, info->b11->fallbacks = tal_steal(info->b11, fallback_scripts); req = jsonrpc_request_start(info, "listincoming", + cmd->id, cmd->ld->log, NULL, listincoming_done, info); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 93aec76f3adf..ec601e082f00 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -1314,7 +1314,8 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n) } struct jsonrpc_request *jsonrpc_request_start_( - const tal_t *ctx, const char *method, struct log *log, + const tal_t *ctx, const char *method, + const char *id_prefix, struct log *log, bool add_header, void (*notify_cb)(const char *buffer, const jsmntok_t *methodtok, @@ -1327,7 +1328,11 @@ struct jsonrpc_request *jsonrpc_request_start_( { struct jsonrpc_request *r = tal(ctx, struct jsonrpc_request); static u64 next_request_id = 0; - r->id = tal_fmt(r, "cln:%s#%"PRIu64, method, next_request_id); + if (id_prefix) + r->id = tal_fmt(r, "%s/cln:%s#%"PRIu64, + id_prefix, method, next_request_id); + else + r->id = tal_fmt(r, "cln:%s#%"PRIu64, method, next_request_id); next_request_id++; r->notify_cb = notify_cb; r->response_cb = response_cb; diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 92b4e7722ff6..2b42a24e6cde 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -217,9 +217,13 @@ struct jsonrpc_notification *jsonrpc_notification_start(const tal_t *ctx, const */ void jsonrpc_notification_end(struct jsonrpc_notification *n); -#define jsonrpc_request_start(ctx, method, log, notify_cb, response_cb, response_cb_arg) \ +/** + * start a JSONRPC request; id_prefix is non-NULL if this was triggered by + * another JSONRPC request. + */ +#define jsonrpc_request_start(ctx, method, id_prefix, log, notify_cb, response_cb, response_cb_arg) \ jsonrpc_request_start_( \ - (ctx), (method), (log), true, \ + (ctx), (method), (id_prefix), (log), true, \ typesafe_cb_preargs(void, void *, (notify_cb), (response_cb_arg), \ const char *buffer, \ const jsmntok_t *idtok, \ @@ -231,9 +235,9 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n); const jsmntok_t *idtok), \ (response_cb_arg)) -#define jsonrpc_request_start_raw(ctx, method, log, notify_cb, response_cb, response_cb_arg) \ +#define jsonrpc_request_start_raw(ctx, method, id_prefix, log, notify_cb, response_cb, response_cb_arg) \ jsonrpc_request_start_( \ - (ctx), (method), (log), false, \ + (ctx), (method), (id_prefix), (log), false, \ typesafe_cb_preargs(void, void *, (notify_cb), (response_cb_arg), \ const char *buffer, \ const jsmntok_t *idtok, \ @@ -246,7 +250,8 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n); (response_cb_arg)) struct jsonrpc_request *jsonrpc_request_start_( - const tal_t *ctx, const char *method, struct log *log, bool add_header, + const tal_t *ctx, const char *method, + const char *id_prefix, struct log *log, bool add_header, void (*notify_cb)(const char *buffer, const jsmntok_t *idtok, const jsmntok_t *methodtok, diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 20c0b1c2f2cc..a158720ba7fd 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1142,7 +1142,8 @@ static struct command_result *plugin_rpcmethod_dispatch(struct command *cmd, call = tal(plugin, struct plugin_rpccall); call->cmd = cmd; - req = jsonrpc_request_start_raw(plugin, cmd->json_cmd->name, plugin->log, + req = jsonrpc_request_start_raw(plugin, cmd->json_cmd->name, cmd->id, + plugin->log, plugin_notify_cb, plugin_rpcmethod_cb, call); call->request = req; @@ -1729,7 +1730,8 @@ const char *plugin_send_getmanifest(struct plugin *p) * write-only on p->stdin */ p->stdout_conn = io_new_conn(p, stdoutfd, plugin_stdout_conn_init, p); p->stdin_conn = io_new_conn(p, stdinfd, plugin_stdin_conn_init, p); - req = jsonrpc_request_start(p, "getmanifest", p->log, + /* FIXME: id_prefix from caller! */ + req = jsonrpc_request_start(p, "getmanifest", NULL, p->log, NULL, plugin_manifest_cb, p); json_add_bool(req->stream, "allow-deprecated-apis", deprecated_apis); jsonrpc_request_end(req); @@ -1910,7 +1912,7 @@ plugin_config(struct plugin *plugin) struct jsonrpc_request *req; plugin_set_timeout(plugin); - req = jsonrpc_request_start(plugin, "init", plugin->log, + req = jsonrpc_request_start(plugin, "init", NULL, plugin->log, NULL, plugin_config_cb, plugin); plugin_populate_init_request(plugin, req); jsonrpc_request_end(req); diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index b8d7322f4094..70b2f2f85a25 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -232,7 +232,8 @@ static void plugin_hook_call_next(struct plugin_hook_request *ph_req) log_debug(ph_req->ld->log, "Calling %s hook of plugin %s", ph_req->hook->name, ph_req->plugin->shortname); - req = jsonrpc_request_start(NULL, hook->name, + /* FIXME: id_prefix from caller! */ + req = jsonrpc_request_start(NULL, hook->name, NULL, plugin_get_log(ph_req->plugin), NULL, plugin_hook_callback, ph_req); @@ -371,8 +372,9 @@ void plugin_hook_db_sync(struct db *db) dwh_req->ph_req = ph_req; dwh_req->num_hooks = &num_hooks; + /* FIXME: id_prefix from caller? */ /* FIXME: do IO logging for this! */ - req = jsonrpc_request_start(NULL, hook->name, NULL, NULL, + req = jsonrpc_request_start(NULL, hook->name, NULL, NULL, NULL, db_hook_response, dwh_req); diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index b3ecab5a16bf..9d0197fac8c8 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -212,6 +212,7 @@ static struct command_result *json_checkmessage(struct command *cmd, node_id_from_pubkey(&can->id, &reckey); can->cmd = cmd; req = jsonrpc_request_start(cmd, "listnodes", + cmd->id, cmd->ld->log, NULL, listnodes_done, can); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 4550e0e0f79c..d8cec618649f 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -491,7 +491,8 @@ void jsonrpc_request_end(struct jsonrpc_request *request UNNEEDED) { fprintf(stderr, "jsonrpc_request_end called!\n"); abort(); } /* Generated stub for jsonrpc_request_start_ */ struct jsonrpc_request *jsonrpc_request_start_( - const tal_t *ctx UNNEEDED, const char *method UNNEEDED, struct log *log UNNEEDED, bool add_header UNNEEDED, + const tal_t *ctx UNNEEDED, const char *method UNNEEDED, + const char *id_prefix UNNEEDED, struct log *log UNNEEDED, bool add_header UNNEEDED, void (*notify_cb)(const char *buffer UNNEEDED, const jsmntok_t *idtok UNNEEDED, const jsmntok_t *methodtok UNNEEDED, diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 743caf9bd50c..bb954367674e 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -18,7 +18,7 @@ def test_invoice(node_factory, chainparams): inv = l1.rpc.invoice(123000, 'label', 'description', 3700, [addr1, addr2]) # Side note: invoice calls out to listincoming, so check JSON id is as expected - l1.daemon.wait_for_log(": OUT:id=cln:listincoming#[0-9]*") + l1.daemon.wait_for_log(": OUT:id=1/cln:listincoming#[0-9]*") after = int(time.time()) b11 = l1.rpc.decodepay(inv['bolt11']) From ea7903f69a2df3ac51e25422f7d111bea9f693c7 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:11 +0930 Subject: [PATCH 1373/1530] lightningd: trace JSON id prefixes through sendrawtx. First, merge the _ahf_ and non-ahf interfaces. Second, remove the always-NULL txs->cmd field. Then, add optional id_prefix for bitcoind_sendrawx, so if it's triggered by a command (e.g. "withdraw") it's shown correctly in logs. Signed-off-by: Rusty Russell --- lightningd/bitcoind.c | 25 +++------- lightningd/bitcoind.h | 23 ++------- lightningd/chaintopology.c | 54 ++++++++++----------- lightningd/chaintopology.h | 21 ++++---- lightningd/channel.c | 3 +- lightningd/closing_control.c | 13 +++-- lightningd/closing_control.h | 7 ++- lightningd/dual_open_control.c | 6 +++ lightningd/jsonrpc.c | 2 + lightningd/jsonrpc.h | 2 +- lightningd/onchain_control.c | 9 ++-- lightningd/peer_control.c | 16 ++++-- lightningd/test/run-invoice-select-inchan.c | 8 +-- tests/test_wallet.py | 3 ++ wallet/test/run-wallet.c | 6 ++- wallet/walletrpc.c | 5 +- 16 files changed, 107 insertions(+), 96 deletions(-) diff --git a/lightningd/bitcoind.c b/lightningd/bitcoind.c index 93eef2382e5a..367abf05f692 100644 --- a/lightningd/bitcoind.c +++ b/lightningd/bitcoind.c @@ -298,12 +298,13 @@ static void sendrawtx_callback(const char *buf, const jsmntok_t *toks, tal_free(call); } -void bitcoind_sendrawtx_ahf_(struct bitcoind *bitcoind, - const char *hextx, - bool allowhighfees, - void (*cb)(struct bitcoind *bitcoind, - bool success, const char *msg, void *), - void *cb_arg) +void bitcoind_sendrawtx_(struct bitcoind *bitcoind, + const char *id_prefix, + const char *hextx, + bool allowhighfees, + void (*cb)(struct bitcoind *bitcoind, + bool success, const char *msg, void *), + void *cb_arg) { struct jsonrpc_request *req; struct sendrawtx_call *call = tal(bitcoind, struct sendrawtx_call); @@ -313,8 +314,7 @@ void bitcoind_sendrawtx_ahf_(struct bitcoind *bitcoind, call->cb_arg = cb_arg; log_debug(bitcoind->log, "sendrawtransaction: %s", hextx); - /* FIXME: pass id_prefix from caller! */ - req = jsonrpc_request_start(bitcoind, "sendrawtransaction", NULL, + req = jsonrpc_request_start(bitcoind, "sendrawtransaction", id_prefix, bitcoind->log, NULL, sendrawtx_callback, call); @@ -324,15 +324,6 @@ void bitcoind_sendrawtx_ahf_(struct bitcoind *bitcoind, bitcoin_plugin_send(bitcoind, req); } -void bitcoind_sendrawtx_(struct bitcoind *bitcoind, - const char *hextx, - void (*cb)(struct bitcoind *bitcoind, - bool success, const char *msg, void *), - void *arg) -{ - return bitcoind_sendrawtx_ahf_(bitcoind, hextx, false, cb, arg); -} - /* `getrawblockbyheight` * * If no block were found at that height, will set each field to `null`. diff --git a/lightningd/bitcoind.h b/lightningd/bitcoind.h index 20be28a56964..f17217d78da0 100644 --- a/lightningd/bitcoind.h +++ b/lightningd/bitcoind.h @@ -71,29 +71,16 @@ void bitcoind_estimate_fees_(struct bitcoind *bitcoind, const u32 *), \ (arg)) -void bitcoind_sendrawtx_ahf_(struct bitcoind *bitcoind, - const char *hextx, - bool allowhighfees, - void (*cb)(struct bitcoind *bitcoind, - bool success, const char *msg, void *), - void *arg); -#define bitcoind_sendrawtx_ahf(bitcoind_, hextx, allowhighfees, cb, arg)\ - bitcoind_sendrawtx_ahf_((bitcoind_), (hextx), \ - (allowhighfees), \ - typesafe_cb_preargs(void, void *, \ - (cb), (arg), \ - struct bitcoind *, \ - bool, const char *),\ - (arg)) - void bitcoind_sendrawtx_(struct bitcoind *bitcoind, + const char *id_prefix TAKES, const char *hextx, + bool allowhighfees, void (*cb)(struct bitcoind *bitcoind, bool success, const char *msg, void *), void *arg); - -#define bitcoind_sendrawtx(bitcoind_, hextx, cb, arg) \ - bitcoind_sendrawtx_((bitcoind_), (hextx), \ +#define bitcoind_sendrawtx(bitcoind_, id_prefix, hextx, allowhighfees, cb, arg) \ + bitcoind_sendrawtx_((bitcoind_), (id_prefix), (hextx), \ + (allowhighfees), \ typesafe_cb_preargs(void, void *, \ (cb), (arg), \ struct bitcoind *, \ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index b5d6d71e6a56..aba7ac5cebaa 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -125,8 +125,8 @@ struct txs_to_broadcast { /* These are hex encoded already, for bitcoind_sendrawtx */ const char **txs; - /* Command to complete when we're done, if and only if dev-broadcast triggered */ - struct command *cmd; + /* IDs to attach to each tx (could be NULL!) */ + const char **cmd_id; }; /* We just sent the last entry in txs[]. Shrink and send the next last. */ @@ -141,28 +141,27 @@ static void broadcast_remainder(struct bitcoind *bitcoind, txs->cursor++; if (txs->cursor == tal_count(txs->txs)) { - if (txs->cmd) - was_pending(command_success(txs->cmd, - json_stream_success(txs->cmd))); tal_free(txs); return; } /* Broadcast next one. */ - bitcoind_sendrawtx(bitcoind, txs->txs[txs->cursor], + bitcoind_sendrawtx(bitcoind, + txs->cmd_id[txs->cursor], txs->txs[txs->cursor], + false, broadcast_remainder, txs); } /* FIXME: This is dumb. We can group txs and avoid bothering bitcoind * if any one tx is in the main chain. */ -static void rebroadcast_txs(struct chain_topology *topo, struct command *cmd) +static void rebroadcast_txs(struct chain_topology *topo) { /* Copy txs now (peers may go away, and they own txs). */ struct txs_to_broadcast *txs; struct outgoing_tx *otx; txs = tal(topo, struct txs_to_broadcast); - txs->cmd = cmd; + txs->cmd_id = tal_arr(txs, const char *, 0); /* Put any txs we want to broadcast in ->txs. */ txs->txs = tal_arr(txs, const char *, 0); @@ -171,6 +170,8 @@ static void rebroadcast_txs(struct chain_topology *topo, struct command *cmd) continue; tal_arr_expand(&txs->txs, tal_strdup(txs, otx->hextx)); + tal_arr_expand(&txs->cmd_id, + otx->cmd_id ? tal_strdup(txs, otx->cmd_id) : NULL); } /* Let this do the dirty work. */ @@ -214,12 +215,12 @@ static void broadcast_done(struct bitcoind *bitcoind, } } -void broadcast_tx_ahf(struct chain_topology *topo, - struct channel *channel, const struct bitcoin_tx *tx, - bool allowhighfees, - void (*failed)(struct channel *channel, - bool success, - const char *err)) +void broadcast_tx(struct chain_topology *topo, + struct channel *channel, const struct bitcoin_tx *tx, + const char *cmd_id, bool allowhighfees, + void (*failed)(struct channel *channel, + bool success, + const char *err)) { /* Channel might vanish: topo owns it to start with. */ struct outgoing_tx *otx = tal(topo, struct outgoing_tx); @@ -229,26 +230,23 @@ void broadcast_tx_ahf(struct chain_topology *topo, bitcoin_txid(tx, &otx->txid); otx->hextx = tal_hex(otx, rawtx); otx->failed_or_success = failed; + if (cmd_id) + otx->cmd_id = tal_strdup(otx, cmd_id); + else + otx->cmd_id = NULL; tal_free(rawtx); tal_add_destructor2(channel, clear_otx_channel, otx); - log_debug(topo->log, "Broadcasting txid %s", - type_to_string(tmpctx, struct bitcoin_txid, &otx->txid)); + log_debug(topo->log, "Broadcasting txid %s%s%s", + type_to_string(tmpctx, struct bitcoin_txid, &otx->txid), + cmd_id ? " for " : "", cmd_id ? cmd_id : ""); wallet_transaction_add(topo->ld->wallet, tx->wtx, 0, 0); - bitcoind_sendrawtx_ahf(topo->bitcoind, otx->hextx, allowhighfees, - broadcast_done, otx); -} -void broadcast_tx(struct chain_topology *topo, - struct channel *channel, const struct bitcoin_tx *tx, - void (*failed)(struct channel *channel, - bool success, - const char *err)) -{ - return broadcast_tx_ahf(topo, channel, tx, false, failed); + bitcoind_sendrawtx(topo->bitcoind, otx->cmd_id, otx->hextx, + allowhighfees, + broadcast_done, otx); } - static enum watch_result closeinfo_txid_confirmed(struct lightningd *ld, struct channel *channel, const struct bitcoin_txid *txid, @@ -599,7 +597,7 @@ static void updates_complete(struct chain_topology *topo) notify_new_block(topo->bitcoind->ld, topo->tip->height); /* Maybe need to rebroadcast. */ - rebroadcast_txs(topo, NULL); + rebroadcast_txs(topo); /* We've processed these UTXOs */ db_set_intvar(topo->bitcoind->ld->wallet->db, diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index 0e6a771f0d64..30566d6ffcfb 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -22,6 +22,7 @@ struct outgoing_tx { struct channel *channel; const char *hextx; struct bitcoin_txid txid; + const char *cmd_id; void (*failed_or_success)(struct channel *channel, bool success, const char *err); }; @@ -151,21 +152,21 @@ u32 delayed_to_us_feerate(struct chain_topology *topo); u32 htlc_resolution_feerate(struct chain_topology *topo); u32 penalty_feerate(struct chain_topology *topo); -/* Broadcast a single tx, and rebroadcast as reqd (copies tx). - * If failed is non-NULL, call that and don't rebroadcast. */ +/** + * broadcast_tx - Broadcast a single tx, and rebroadcast as reqd (copies tx). + * @topo: topology + * @channel: the channel responsible for this (stop broadcasting if freed). + * @tx: the transaction + * @cmd_id: the JSON command id which triggered this (or NULL). + * @allowhighfees: set to true to override the high-fee checks in the backend. + * @failed: if non-NULL, call that and don't rebroadcast. + */ void broadcast_tx(struct chain_topology *topo, struct channel *channel, const struct bitcoin_tx *tx, + const char *cmd_id, bool allowhighfees, void (*failed)(struct channel *, bool success, const char *err)); -/* Like the above, but with an additional `allowhighfees` parameter. - * If true, suppress any high-fee checks in the backend. */ -void broadcast_tx_ahf(struct chain_topology *topo, - struct channel *channel, const struct bitcoin_tx *tx, - bool allowhighfees, - void (*failed)(struct channel *, - bool success, - const char *err)); struct chain_topology *new_topology(struct lightningd *ld, struct log *log); void setup_topology(struct chain_topology *topology, diff --git a/lightningd/channel.c b/lightningd/channel.c index 8a35558c0502..369b27ce4f2d 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -921,7 +921,8 @@ void channel_internal_error(struct channel *channel, const char *fmt, ...) /* Don't expose internal error causes to remove unless doing dev */ #if DEVELOPER - channel_fail_permanent(channel, REASON_LOCAL, "Internal error: %s", why); + channel_fail_permanent(channel, + REASON_LOCAL, "Internal error: %s", why); #else channel_fail_permanent(channel, REASON_LOCAL, "Internal error"); #endif diff --git a/lightningd/closing_control.c b/lightningd/closing_control.c index 057df16b75af..00204067a15c 100644 --- a/lightningd/closing_control.c +++ b/lightningd/closing_control.c @@ -46,6 +46,7 @@ struct close_command { struct command *cmd; /* Channel being closed. */ struct channel *channel; + }; /* Resolve a single close command. */ @@ -68,18 +69,24 @@ resolve_one_close_command(struct close_command *cc, bool cooperative) was_pending(command_success(cc->cmd, result)); } -/* Resolve a close command for a channel that will be closed soon. */ -void resolve_close_command(struct lightningd *ld, struct channel *channel, - bool cooperative) +/* Resolve a close command for a channel that will be closed soon: returns + * the cmd_id of one, if any (allocated off ctx). */ +const char *resolve_close_command(const tal_t *ctx, + struct lightningd *ld, struct channel *channel, + bool cooperative) { struct close_command *cc; struct close_command *n; + const char *cmd_id = NULL; list_for_each_safe(&ld->close_commands, cc, n, list) { if (cc->channel != channel) continue; + if (!cmd_id) + cmd_id = tal_strdup(ctx, cc->cmd->id); resolve_one_close_command(cc, cooperative); } + return cmd_id; } /* Destroy the close command structure in reaction to the diff --git a/lightningd/closing_control.h b/lightningd/closing_control.h index 8e5cef6981b2..c536e9b569c6 100644 --- a/lightningd/closing_control.h +++ b/lightningd/closing_control.h @@ -8,8 +8,11 @@ struct channel; struct lightningd; struct peer_fd; -void resolve_close_command(struct lightningd *ld, struct channel *channel, - bool cooperative); +/* Resolve a close command for a channel that will be closed soon: returns + * the cmd_id of one, if any (allocated off ctx). */ +const char *resolve_close_command(const tal_t *ctx, + struct lightningd *ld, struct channel *channel, + bool cooperative); void peer_start_closingd(struct channel *channel, struct peer_fd *peer_fd); diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 2a552017c2d3..57be2668b72b 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1561,7 +1561,13 @@ static void send_funding_tx(struct channel *channel, type_to_string(tmpctx, struct wally_tx, cs->wtx)); bitcoind_sendrawtx(ld->topology->bitcoind, + channel->open_attempt + ? (channel->open_attempt->cmd + ? channel->open_attempt->cmd->id + : NULL) + : NULL, tal_hex(tmpctx, linearize_wtx(tmpctx, cs->wtx)), + false, sendfunding_done, cs); } diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index ec601e082f00..976d6fe29d22 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -1333,6 +1333,8 @@ struct jsonrpc_request *jsonrpc_request_start_( id_prefix, method, next_request_id); else r->id = tal_fmt(r, "cln:%s#%"PRIu64, method, next_request_id); + if (taken(id_prefix)) + tal_free(id_prefix); next_request_id++; r->notify_cb = notify_cb; r->response_cb = response_cb; diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 2b42a24e6cde..ec52aeab6953 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -251,7 +251,7 @@ void jsonrpc_notification_end(struct jsonrpc_notification *n); struct jsonrpc_request *jsonrpc_request_start_( const tal_t *ctx, const char *method, - const char *id_prefix, struct log *log, bool add_header, + const char *id_prefix TAKES, struct log *log, bool add_header, void (*notify_cb)(const char *buffer, const jsmntok_t *idtok, const jsmntok_t *methodtok, diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 232dc2a11dae..ac54c65033df 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -348,9 +348,9 @@ static void handle_onchain_broadcast_tx(struct channel *channel, /* If the onchaind signals this as RBF-able, then we also * set allowhighfees, as the transaction may be RBFed into * high feerates as protection against the MAD-HTLC attack. */ - broadcast_tx_ahf(channel->peer->ld->topology, channel, - tx, is_rbf, - is_rbf ? &handle_onchain_broadcast_rbf_tx_cb : NULL); + broadcast_tx(channel->peer->ld->topology, channel, + tx, NULL, is_rbf, + is_rbf ? &handle_onchain_broadcast_rbf_tx_cb : NULL); } static void handle_onchain_unwatch_tx(struct channel *channel, const u8 *msg) @@ -622,7 +622,8 @@ enum watch_result onchaind_funding_spent(struct channel *channel, if (channel->closer != NUM_SIDES) reason = REASON_UNKNOWN; /* will use last cause as reason */ - channel_fail_permanent(channel, reason, "Funding transaction spent"); + channel_fail_permanent(channel, reason, + "Funding transaction spent"); /* If we haven't posted the open event yet, post an open */ if (!channel->scid || !channel->remote_channel_ready) { diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index e68e5a1d8740..efd1fdeddeff 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -271,6 +271,7 @@ bool invalid_last_tx(const struct bitcoin_tx *tx) static void sign_and_send_last(struct lightningd *ld, struct channel *channel, + const char *cmd_id, struct bitcoin_tx *last_tx, struct bitcoin_signature *last_sig) { @@ -285,7 +286,7 @@ static void sign_and_send_last(struct lightningd *ld, /* Keep broadcasting until we say stop (can fail due to dup, * if they beat us to the broadcast). */ - broadcast_tx(ld->topology, channel, last_tx, NULL); + broadcast_tx(ld->topology, channel, last_tx, cmd_id, false, NULL); remove_sig(last_tx); } @@ -294,6 +295,11 @@ void drop_to_chain(struct lightningd *ld, struct channel *channel, bool cooperative) { struct channel_inflight *inflight; + const char *cmd_id; + + /* If this was triggered by a close command, get a copy of the cmd id */ + cmd_id = resolve_close_command(tmpctx, ld, channel, cooperative); + /* BOLT #2: * * - if `next_revocation_number` is greater than expected @@ -313,15 +319,14 @@ void drop_to_chain(struct lightningd *ld, struct channel *channel, /* We need to drop *every* commitment transaction to chain */ if (!cooperative && !list_empty(&channel->inflights)) { list_for_each(&channel->inflights, inflight, list) - sign_and_send_last(ld, channel, + sign_and_send_last(ld, channel, cmd_id, inflight->last_tx, &inflight->last_sig); } else - sign_and_send_last(ld, channel, channel->last_tx, + sign_and_send_last(ld, channel, cmd_id, channel->last_tx, &channel->last_sig); } - resolve_close_command(ld, channel, cooperative); } void resend_closing_transactions(struct lightningd *ld) @@ -1759,7 +1764,8 @@ static enum watch_result funding_depth_cb(struct lightningd *ld, if (!list_empty(&channel->inflights)) { inf = channel_inflight_find(channel, txid); if (!inf) { - channel_fail_permanent(channel, REASON_LOCAL, + channel_fail_permanent(channel, + REASON_LOCAL, "Txid %s for channel" " not found in inflights. (peer %s)", type_to_string(tmpctx, diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index d8cec618649f..fe7e39641409 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -50,6 +50,7 @@ char *bolt11_encode_(const tal_t *ctx UNNEEDED, /* Generated stub for broadcast_tx */ void broadcast_tx(struct chain_topology *topo UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, + const char *cmd_id UNNEEDED, bool allowhighfees UNNEEDED, void (*failed)(struct channel * UNNEEDED, bool success UNNEEDED, const char *err)) @@ -492,7 +493,7 @@ void jsonrpc_request_end(struct jsonrpc_request *request UNNEEDED) /* Generated stub for jsonrpc_request_start_ */ struct jsonrpc_request *jsonrpc_request_start_( const tal_t *ctx UNNEEDED, const char *method UNNEEDED, - const char *id_prefix UNNEEDED, struct log *log UNNEEDED, bool add_header UNNEEDED, + const char *id_prefix TAKES UNNEEDED, struct log *log UNNEEDED, bool add_header UNNEEDED, void (*notify_cb)(const char *buffer UNNEEDED, const jsmntok_t *idtok UNNEEDED, const jsmntok_t *methodtok UNNEEDED, @@ -681,8 +682,9 @@ void plugin_request_send(struct plugin *plugin UNNEEDED, void report_subd_memleak(struct leak_detect *leak_detect UNNEEDED, struct subd *leaker UNNEEDED) { fprintf(stderr, "report_subd_memleak called!\n"); abort(); } /* Generated stub for resolve_close_command */ -void resolve_close_command(struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, - bool cooperative UNNEEDED) +const char *resolve_close_command(const tal_t *ctx UNNEEDED, + struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, + bool cooperative UNNEEDED) { fprintf(stderr, "resolve_close_command called!\n"); abort(); } /* Generated stub for start_leak_request */ void start_leak_request(const struct subd_req *req UNNEEDED, diff --git a/tests/test_wallet.py b/tests/test_wallet.py index a093c390f01b..bed459f2a638 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -59,6 +59,9 @@ def test_withdraw(node_factory, bitcoind): out = l1.rpc.withdraw(waddr, 2 * amount) + # Side note: sendrawtransaction will trace back to withdrawl + l1.daemon.wait_for_log(": OUT:id=[0-9]*/cln:sendrawtransaction#[0-9]*") + # Make sure bitcoind received the withdrawal unspent = l1.bitcoin.rpc.listunspent(0) withdrawal = [u for u in unspent if u['txid'] == out['txid']] diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index b2cb8c0410eb..e703081d5e1b 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -70,6 +70,7 @@ bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, /* Generated stub for broadcast_tx */ void broadcast_tx(struct chain_topology *topo UNNEEDED, struct channel *channel UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, + const char *cmd_id UNNEEDED, bool allowhighfees UNNEEDED, void (*failed)(struct channel * UNNEEDED, bool success UNNEEDED, const char *err)) @@ -669,8 +670,9 @@ struct route_step *process_onionpacket( void report_subd_memleak(struct leak_detect *leak_detect UNNEEDED, struct subd *leaker UNNEEDED) { fprintf(stderr, "report_subd_memleak called!\n"); abort(); } /* Generated stub for resolve_close_command */ -void resolve_close_command(struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, - bool cooperative UNNEEDED) +const char *resolve_close_command(const tal_t *ctx UNNEEDED, + struct lightningd *ld UNNEEDED, struct channel *channel UNNEEDED, + bool cooperative UNNEEDED) { fprintf(stderr, "resolve_close_command called!\n"); abort(); } /* Generated stub for serialize_onionpacket */ u8 *serialize_onionpacket( diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 4d2ab3c1c03a..b65e38f3017a 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -898,7 +898,7 @@ static void sendpsbt_done(struct bitcoind *bitcoind UNUSED, static struct command_result *json_sendpsbt(struct command *cmd, const char *buffer, - const jsmntok_t *obj UNNEEDED, + const jsmntok_t *obj, const jsmntok_t *params) { struct command_result *res; @@ -947,9 +947,10 @@ static struct command_result *json_sendpsbt(struct command *cmd, /* Now broadcast the transaction */ bitcoind_sendrawtx(cmd->ld->topology->bitcoind, + cmd->id, tal_hex(tmpctx, linearize_wtx(tmpctx, sending->wtx)), - sendpsbt_done, sending); + false, sendpsbt_done, sending); return command_still_pending(cmd); } From eceb9f432814830b7b84e8a9b1f4c9eefdbe60cd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:11 +0930 Subject: [PATCH 1374/1530] lightningd: wire plugin command JSON id through to plugin commands. Signed-off-by: Rusty Russell --- lightningd/plugin.c | 11 +++++------ lightningd/plugin.h | 6 ++++-- lightningd/plugin_control.c | 6 +++--- tests/test_plugin.py | 3 +++ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index a158720ba7fd..2e006d5b4a95 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -1701,7 +1701,7 @@ static void plugin_set_timeout(struct plugin *p) } } -const char *plugin_send_getmanifest(struct plugin *p) +const char *plugin_send_getmanifest(struct plugin *p, const char *cmd_id) { char **cmd; int stdinfd, stdoutfd; @@ -1730,8 +1730,7 @@ const char *plugin_send_getmanifest(struct plugin *p) * write-only on p->stdin */ p->stdout_conn = io_new_conn(p, stdoutfd, plugin_stdout_conn_init, p); p->stdin_conn = io_new_conn(p, stdinfd, plugin_stdin_conn_init, p); - /* FIXME: id_prefix from caller! */ - req = jsonrpc_request_start(p, "getmanifest", NULL, p->log, + req = jsonrpc_request_start(p, "getmanifest", cmd_id, p->log, NULL, plugin_manifest_cb, p); json_add_bool(req->stream, "allow-deprecated-apis", deprecated_apis); jsonrpc_request_end(req); @@ -1742,7 +1741,7 @@ const char *plugin_send_getmanifest(struct plugin *p) return NULL; } -bool plugins_send_getmanifest(struct plugins *plugins) +bool plugins_send_getmanifest(struct plugins *plugins, const char *cmd_id) { struct plugin *p, *next; bool sent = false; @@ -1753,7 +1752,7 @@ bool plugins_send_getmanifest(struct plugins *plugins) if (p->plugin_state != UNCONFIGURED) continue; - err = plugin_send_getmanifest(p); + err = plugin_send_getmanifest(p, cmd_id); if (!err) { sent = true; continue; @@ -1795,7 +1794,7 @@ void plugins_init(struct plugins *plugins) setenv("LIGHTNINGD_PLUGIN", "1", 1); setenv("LIGHTNINGD_VERSION", version(), 1); - if (plugins_send_getmanifest(plugins)) { + if (plugins_send_getmanifest(plugins, NULL)) { void *ret; ret = io_loop_with_timers(plugins->ld); log_debug(plugins->ld->log, "io_loop_with_timers: %s", __func__); diff --git a/lightningd/plugin.h b/lightningd/plugin.h index 4b33b736e0a7..fbcfbf486645 100644 --- a/lightningd/plugin.h +++ b/lightningd/plugin.h @@ -214,17 +214,19 @@ bool plugin_blacklisted(struct plugins *plugins, const char *name); /** * Kick off initialization of a plugin. + * @p: plugin + * @cmd_id: optional JSON cmd_id which caused this. * * Returns error string, or NULL. */ -const char *plugin_send_getmanifest(struct plugin *p); +const char *plugin_send_getmanifest(struct plugin *p, const char *cmd_id); /** * Kick of initialization of all plugins which need it/ * * Return true if any were started. */ -bool plugins_send_getmanifest(struct plugins *plugins); +bool plugins_send_getmanifest(struct plugins *plugins, const char *cmd_id); /** * Kill a plugin process and free @plugin, with an error message. diff --git a/lightningd/plugin_control.c b/lightningd/plugin_control.c index 743584438c2d..1d334a4cf6eb 100644 --- a/lightningd/plugin_control.c +++ b/lightningd/plugin_control.c @@ -75,7 +75,7 @@ plugin_dynamic_start(struct plugin_command *pcmd, const char *plugin_path, errno ? strerror(errno) : "already registered"); /* This will come back via plugin_cmd_killed or plugin_cmd_succeeded */ - err = plugin_send_getmanifest(p); + err = plugin_send_getmanifest(p, pcmd->cmd->id); if (err) return command_fail(pcmd->cmd, PLUGIN_ERROR, "%s: %s", @@ -103,7 +103,7 @@ plugin_dynamic_startdir(struct plugin_command *pcmd, const char *dir_path) if (res) return res; - plugins_send_getmanifest(pcmd->cmd->ld->plugins); + plugins_send_getmanifest(pcmd->cmd->ld->plugins, pcmd->cmd->id); return command_still_pending(pcmd->cmd); } @@ -194,7 +194,7 @@ plugin_dynamic_rescan_plugins(struct plugin_command *pcmd) if (res) return res; - plugins_send_getmanifest(pcmd->cmd->ld->plugins); + plugins_send_getmanifest(pcmd->cmd->ld->plugins, pcmd->cmd->id); return command_still_pending(pcmd->cmd); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 75dd78e12a86..24b428e1036a 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1486,6 +1486,9 @@ def test_libplugin(node_factory): l1.rpc.plugin_start(plugin) l1.rpc.check("helloworld") + # Side note: getmanifest will trace back to plugin_start + l1.daemon.wait_for_log(": OUT:id=[0-9]*/cln:getmanifest#[0-9]*") + # Test commands assert l1.rpc.call("helloworld") == {"hello": "world"} assert l1.rpc.call("helloworld", {"name": "test"}) == {"hello": "test"} From e8ef42b7418c23e68b9ed8e9f8b990dd5daf05d5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:12 +0930 Subject: [PATCH 1375/1530] plugin: wire JSON id for commands which caused hooks to fire. Most obvious one is the "connect" hook. Signed-off-by: Rusty Russell --- lightningd/connect_control.c | 11 ++++++++++- lightningd/connect_control.h | 4 ++++ lightningd/dual_open_control.c | 8 ++++---- lightningd/invoice.c | 2 +- lightningd/jsonrpc.c | 2 +- lightningd/onion_message.c | 4 ++-- lightningd/opening_control.c | 2 +- lightningd/peer_control.c | 6 +++++- lightningd/peer_htlcs.c | 4 ++-- lightningd/plugin_hook.c | 10 ++++++++-- lightningd/plugin_hook.h | 8 +++++--- lightningd/test/run-invoice-select-inchan.c | 8 +++++++- lightningd/test/run-jsonrpc.c | 4 +++- tests/test_plugin.py | 6 ++++-- wallet/test/run-wallet.c | 8 +++++++- 15 files changed, 64 insertions(+), 23 deletions(-) diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index dcc8da297e91..056ef0329d3f 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -402,6 +402,15 @@ static void handle_connect_failed(struct lightningd *ld, const u8 *msg) connect_failed(ld, &id, errcode, errmsg, addrhint); } +const char *connect_any_cmd_id(const tal_t *ctx, + struct lightningd *ld, const struct peer *peer) +{ + struct connect *c = find_connect(ld, &peer->id); + if (c) + return tal_strdup(ctx, c->cmd->id); + return NULL; +} + void connect_succeeded(struct lightningd *ld, const struct peer *peer, bool incoming, const struct wireaddr_internal *addr) @@ -469,7 +478,7 @@ static void handle_custommsg_in(struct lightningd *ld, const u8 *msg) return; } - plugin_hook_call_custommsg(ld, p); + plugin_hook_call_custommsg(ld, NULL, p); } static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fds) diff --git a/lightningd/connect_control.h b/lightningd/connect_control.h index 6b64b72d0b12..bea039a682aa 100644 --- a/lightningd/connect_control.h +++ b/lightningd/connect_control.h @@ -28,4 +28,8 @@ void connect_failed_disconnect(struct lightningd *ld, const struct node_id *id, const struct wireaddr_internal *addr); +/* Get the id of any connect cmd which applies, to feed to hooks */ +const char *connect_any_cmd_id(const tal_t *ctx, + struct lightningd *ld, const struct peer *peer); + #endif /* LIGHTNING_LIGHTNINGD_CONNECT_CONTROL_H */ diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 57be2668b72b..9e593faff72e 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -1856,7 +1856,7 @@ static void rbf_got_offer(struct subd *dualopend, const u8 *msg) payload->channel_max = AMOUNT_SAT(UINT_MAX); tal_add_destructor2(dualopend, rbf_channel_remove_dualopend, payload); - plugin_hook_call_rbf_channel(dualopend->ld, payload); + plugin_hook_call_rbf_channel(dualopend->ld, NULL, payload); } static void accepter_got_offer(struct subd *dualopend, @@ -1917,7 +1917,7 @@ static void accepter_got_offer(struct subd *dualopend, payload->channel_max = AMOUNT_SAT(UINT64_MAX); tal_add_destructor2(dualopend, openchannel2_remove_dualopend, payload); - plugin_hook_call_openchannel2(dualopend->ld, payload); + plugin_hook_call_openchannel2(dualopend->ld, NULL, payload); } static void handle_peer_tx_sigs_msg(struct subd *dualopend, @@ -2859,7 +2859,7 @@ static void handle_psbt_changed(struct subd *dualopend, payload); payload->psbt = tal_steal(payload, psbt); payload->channel = channel; - plugin_hook_call_openchannel2_changed(dualopend->ld, payload); + plugin_hook_call_openchannel2_changed(dualopend->ld, NULL, payload); return; } abort(); @@ -3042,7 +3042,7 @@ static void handle_commit_received(struct subd *dualopend, payload->channel->openchannel_signed_cmd = NULL; /* We call out to hook who will * provide signatures for us! */ - plugin_hook_call_openchannel2_sign(ld, payload); + plugin_hook_call_openchannel2_sign(ld, NULL, payload); return; } diff --git a/lightningd/invoice.c b/lightningd/invoice.c index c8a9d817c641..b5743f136dc3 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -478,7 +478,7 @@ void invoice_try_pay(struct lightningd *ld, payload->set = set; tal_add_destructor2(set, invoice_payload_remove_set, payload); - plugin_hook_call_invoice_payment(ld, payload); + plugin_hook_call_invoice_payment(ld, NULL, payload); } static bool hsm_sign_b11(const u5 *u5bytes, diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 976d6fe29d22..67dab8285abb 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -940,7 +940,7 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) rpc_hook->custom_buffer = NULL; db_begin_transaction(jcon->ld->wallet->db); - completed = plugin_hook_call_rpc_command(jcon->ld, rpc_hook); + completed = plugin_hook_call_rpc_command(jcon->ld, c->id, rpc_hook); db_commit_transaction(jcon->ld->wallet->db); /* If it's deferred, mark it (otherwise, it's completed) */ diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index b5cdd6d15417..66cbafa70c3a 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -209,9 +209,9 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) /* We'll free this on return */ tal_steal(ld, payload); if (payload->our_alias) - plugin_hook_call_onion_message_ourpath(ld, payload); + plugin_hook_call_onion_message_ourpath(ld, NULL, payload); else - plugin_hook_call_onion_message_blinded(ld, payload); + plugin_hook_call_onion_message_blinded(ld, NULL, payload); } struct onion_hop { diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index f059a7eb7200..593c7b75a911 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -832,7 +832,7 @@ static void opening_got_offer(struct subd *openingd, } tal_add_destructor2(openingd, openchannel_payload_remove_openingd, payload); - plugin_hook_call_openchannel(openingd->ld, payload); + plugin_hook_call_openchannel(openingd->ld, NULL, payload); } static unsigned int openingd_msg(struct subd *openingd, diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index efd1fdeddeff..e4b49569ddd2 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1363,6 +1363,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) struct peer *peer; struct peer_connected_hook_payload *hook_payload; u64 connectd_counter; + const char *cmd_id; hook_payload = tal(NULL, struct peer_connected_hook_payload); hook_payload->ld = ld; @@ -1411,6 +1412,9 @@ void peer_connected(struct lightningd *ld, const u8 *msg) tal_steal(peer, hook_payload); hook_payload->peer = peer; + /* If there's a connect command, use its id as basis for hook id */ + cmd_id = connect_any_cmd_id(tmpctx, ld, peer); + /* Log and update remote_addr for Nat/IP discovery. */ if (hook_payload->remote_addr) { log_peer_debug(ld->log, &id, "Peer says it sees our address as: %s", @@ -1423,7 +1427,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) update_remote_addr(ld, hook_payload->remote_addr, id); } - plugin_hook_call_peer_connected(ld, hook_payload); + plugin_hook_call_peer_connected(ld, cmd_id, hook_payload); } /* connectd tells us a peer has a message and we've not already attached diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 1333d4be693a..f2529fb66302 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1300,7 +1300,7 @@ static bool peer_accepted_htlc(const tal_t *ctx, #endif hook_payload->next_blinding = NULL; - plugin_hook_call_htlc_accepted(ld, hook_payload); + plugin_hook_call_htlc_accepted(ld, NULL, hook_payload); /* Falling through here is ok, after all the HTLC locked */ return true; @@ -2399,7 +2399,7 @@ void peer_got_revoke(struct channel *channel, const u8 *msg) payload->channel_dbid = channel->dbid; payload->commitnum = pbase->commitment_num; payload->channel_id = channel->cid; - plugin_hook_call_commitment_revocation(ld, payload); + plugin_hook_call_commitment_revocation(ld, NULL, payload); } diff --git a/lightningd/plugin_hook.c b/lightningd/plugin_hook.c index 70b2f2f85a25..d167d3a1825f 100644 --- a/lightningd/plugin_hook.c +++ b/lightningd/plugin_hook.c @@ -1,5 +1,6 @@ #include "config.h" #include +#include #include #include #include @@ -11,6 +12,7 @@ struct plugin_hook_request { struct list_head call_chain; struct plugin *plugin; + const char *cmd_id; const struct plugin_hook *hook; void *cb_arg; struct db *db; @@ -232,8 +234,7 @@ static void plugin_hook_call_next(struct plugin_hook_request *ph_req) log_debug(ph_req->ld->log, "Calling %s hook of plugin %s", ph_req->hook->name, ph_req->plugin->shortname); - /* FIXME: id_prefix from caller! */ - req = jsonrpc_request_start(NULL, hook->name, NULL, + req = jsonrpc_request_start(NULL, hook->name, ph_req->cmd_id, plugin_get_log(ph_req->plugin), NULL, plugin_hook_callback, ph_req); @@ -244,6 +245,7 @@ static void plugin_hook_call_next(struct plugin_hook_request *ph_req) } bool plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook, + const char *cmd_id TAKES, tal_t *cb_arg STEALS) { struct plugin_hook_request *ph_req; @@ -259,6 +261,10 @@ bool plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook, ph_req->cb_arg = tal_steal(ph_req, cb_arg); ph_req->db = ld->wallet->db; ph_req->ld = ld; + if (cmd_id) + ph_req->cmd_id = tal_strdup(ph_req, cmd_id); + else + ph_req->cmd_id = NULL; list_head_init(&ph_req->call_chain); for (size_t i=0; ihooks); i++) { diff --git a/lightningd/plugin_hook.h b/lightningd/plugin_hook.h index c1a5f3fd9f4e..f9543b8d336f 100644 --- a/lightningd/plugin_hook.h +++ b/lightningd/plugin_hook.h @@ -59,7 +59,9 @@ AUTODATA_TYPE(hooks, struct plugin_hook); * Returns true if callback called immediately, otherwise false if it's * still waiting on a plugin response. */ -bool plugin_hook_call_(struct lightningd *ld, const struct plugin_hook *hook, +bool plugin_hook_call_(struct lightningd *ld, + const struct plugin_hook *hook, + const char *cmd_id TAKES, tal_t *cb_arg STEALS); /* Generic deserialize_cb: returns true iff 'result': 'continue' */ @@ -73,9 +75,9 @@ bool plugin_hook_continue(void *arg, const char *buffer, const jsmntok_t *toks); /* FIXME: Find a way to avoid back-to-back declaration and definition */ #define PLUGIN_HOOK_CALL_DEF(name, cb_arg_type) \ UNNEEDED static inline bool plugin_hook_call_##name( \ - struct lightningd *ld, cb_arg_type cb_arg STEALS) \ + struct lightningd *ld, const char *cmd_id TAKES, cb_arg_type cb_arg STEALS) \ { \ - return plugin_hook_call_(ld, &name##_hook_gen, cb_arg); \ + return plugin_hook_call_(ld, &name##_hook_gen, cmd_id, cb_arg); \ } /* Typechecked registration of a plugin hook. We check that the diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index fe7e39641409..2af47c1467e8 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -149,6 +149,10 @@ struct command_result *command_success(struct command *cmd UNNEEDED, struct json_stream *response) { fprintf(stderr, "command_success called!\n"); abort(); } +/* Generated stub for connect_any_cmd_id */ +const char *connect_any_cmd_id(const tal_t *ctx UNNEEDED, + struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED) +{ fprintf(stderr, "connect_any_cmd_id called!\n"); abort(); } /* Generated stub for connect_failed_disconnect */ void connect_failed_disconnect(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED, @@ -671,7 +675,9 @@ bool peer_start_openingd(struct peer *peer UNNEEDED, struct peer_fd *peer_fd UNNEEDED) { fprintf(stderr, "peer_start_openingd called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ -bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, +bool plugin_hook_call_(struct lightningd *ld UNNEEDED, + const struct plugin_hook *hook UNNEEDED, + const char *cmd_id TAKES UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) { fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } /* Generated stub for plugin_request_send */ diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 134fc5e7de97..6536ffad810b 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -96,7 +96,9 @@ const char *param_subcommand(struct command *cmd UNNEEDED, const char *buffer UN const char *name UNNEEDED, ...) { fprintf(stderr, "param_subcommand called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ -bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, +bool plugin_hook_call_(struct lightningd *ld UNNEEDED, + const struct plugin_hook *hook UNNEEDED, + const char *cmd_id TAKES UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) { fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } /* Generated stub for towire_bigsize */ diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 24b428e1036a..586fd6cf0111 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1500,9 +1500,11 @@ def test_libplugin(node_factory): # But param takes over! assert l1.rpc.call("helloworld", {"name": "test"}) == {"hello": "test"} - # Test hooks and notifications - l2 = node_factory.get_node() + # Test hooks and notifications (add plugin, so we can test hook id) + l2 = node_factory.get_node(options={"plugin": plugin}) l2.connect(l1) + l2.daemon.wait_for_log(": OUT:id=[0-9]*/cln:peer_connected#[0-9]*") + l1.daemon.wait_for_log("{} peer_connected".format(l2.info["id"])) l1.daemon.wait_for_log("{} connected".format(l2.info["id"])) diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index e703081d5e1b..58d870a50507 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -107,6 +107,10 @@ struct command_result *command_success(struct command *cmd UNNEEDED, struct json_stream *response) { fprintf(stderr, "command_success called!\n"); abort(); } +/* Generated stub for connect_any_cmd_id */ +const char *connect_any_cmd_id(const tal_t *ctx UNNEEDED, + struct lightningd *ld UNNEEDED, const struct peer *peer UNNEEDED) +{ fprintf(stderr, "connect_any_cmd_id called!\n"); abort(); } /* Generated stub for connect_failed_disconnect */ void connect_failed_disconnect(struct lightningd *ld UNNEEDED, const struct node_id *id UNNEEDED, @@ -653,7 +657,9 @@ bool peer_start_openingd(struct peer *peer UNNEEDED, const char *peer_wire_name(int e UNNEEDED) { fprintf(stderr, "peer_wire_name called!\n"); abort(); } /* Generated stub for plugin_hook_call_ */ -bool plugin_hook_call_(struct lightningd *ld UNNEEDED, const struct plugin_hook *hook UNNEEDED, +bool plugin_hook_call_(struct lightningd *ld UNNEEDED, + const struct plugin_hook *hook UNNEEDED, + const char *cmd_id TAKES UNNEEDED, tal_t *cb_arg STEALS UNNEEDED) { fprintf(stderr, "plugin_hook_call_ called!\n"); abort(); } /* Generated stub for process_onionpacket */ From d360075d2231865b7a20645cbd4ec07ed04a27fb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:12 +0930 Subject: [PATCH 1376/1530] libplugin: use string ids correctly. Build them from the command which caused them, and take plugin name as basename with extension stripped. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 50 +++++++++++++++++++++++++++++--------------- plugins/libplugin.h | 2 +- tests/test_wallet.py | 2 +- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 0aff78a7fa58..2de4b0de7b56 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ struct plugin { struct io_conn *stdin_conn; struct io_conn *stdout_conn; + /* to append to all our command ids */ + const char *id; + /* To read from lightningd */ char *buffer; size_t used, len_read; @@ -53,7 +57,7 @@ struct plugin { jsmn_parser rpc_parser; jsmntok_t *rpc_toks; /* Tracking async RPC requests */ - UINTMAP(struct out_req *) out_reqs; + STRMAP(struct out_req *) out_reqs; u64 next_outreq_id; /* Synchronous RPC interaction */ @@ -172,12 +176,20 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, struct out_req *out; out = tal(plugin, struct out_req); - out->id = plugin->next_outreq_id++; + if (cmd) + out->id = tal_fmt(out, "%s/%s:%s#%"PRIu64, cmd->id, + plugin->id, method, + plugin->next_outreq_id); + else + out->id = tal_fmt(out, "%s:%s#%"PRIu64, + plugin->id, method, + plugin->next_outreq_id); + plugin->next_outreq_id++; out->cmd = cmd; out->cb = cb; out->errcb = errcb; out->arg = arg; - uintmap_add(&plugin->out_reqs, out->id, out); + strmap_add(&plugin->out_reqs, out->id, out); /* If command goes away, don't call callbacks! */ if (out->cmd) @@ -186,7 +198,7 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, out->js = new_json_stream(NULL, cmd, NULL); json_object_start(out->js, NULL); json_add_string(out->js, "jsonrpc", "2.0"); - json_add_u64(out->js, "id", out->id); + json_add_string(out->js, "id", out->id); json_add_string(out->js, "method", method); if (out->errcb) json_object_start(out->js, "params"); @@ -667,22 +679,19 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) const jsmntok_t *idtok, *contenttok; struct out_req *out; struct command_result *res; - u64 id; idtok = json_get_member(plugin->rpc_buffer, toks, "id"); if (!idtok) /* FIXME: Don't simply ignore notifications! */ return; - if (!json_to_u64(plugin->rpc_buffer, idtok, &id)) - plugin_err(plugin, "JSON reply without numeric id '%.*s'", - json_tok_full_len(toks), - json_tok_full(plugin->rpc_buffer, toks)); - out = uintmap_get(&plugin->out_reqs, id); + out = strmap_getn(&plugin->out_reqs, + plugin->rpc_buffer + idtok->start, + idtok->end - idtok->start); if (!out) - plugin_err(plugin, "JSON reply with unknown id '%.*s' (%"PRIu64")", + plugin_err(plugin, "JSON reply with unknown id '%.*s'", json_tok_full_len(toks), - json_tok_full(plugin->rpc_buffer, toks), id); + json_tok_full(plugin->rpc_buffer, toks)); /* Remove destructor if one existed */ if (out->cmd) @@ -690,7 +699,7 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) /* We want to free this if callback doesn't. */ tal_steal(tmpctx, out); - uintmap_del(&plugin->out_reqs, out->id); + strmap_del(&plugin->out_reqs, out->id, NULL); contenttok = json_get_member(plugin->rpc_buffer, toks, "error"); if (contenttok) { @@ -1356,8 +1365,8 @@ static void memleak_check(struct plugin *plugin, struct command *cmd) /* Now delete plugin and anything it has pointers to. */ memleak_remove_region(memtable, plugin, sizeof(*plugin)); - /* Memleak needs some help to see into intmaps */ - memleak_remove_uintmap(memtable, &plugin->out_reqs); + /* Memleak needs some help to see into strmaps */ + memleak_remove_strmap(memtable, &plugin->out_reqs); /* We know usage strings are referred to. */ memleak_remove_strmap(memtable, &cmd->plugin->usagemap); @@ -1587,6 +1596,7 @@ static struct io_plan *stdout_conn_init(struct io_conn *conn, } static struct plugin *new_plugin(const tal_t *ctx, + const char *argv0, const char *(*init)(struct plugin *p, const char *buf, const jsmntok_t *), @@ -1605,7 +1615,12 @@ static struct plugin *new_plugin(const tal_t *ctx, { const char *optname; struct plugin *p = tal(ctx, struct plugin); + char *name; + /* id is our name, without extension (not that we expect any, in C!) */ + name = path_basename(p, argv0); + name[path_ext_off(name)] = '\0'; + p->id = name; p->buffer = tal_arr(p, char, 64); p->js_arr = tal_arr(p, struct json_stream *, 0); p->used = 0; @@ -1620,7 +1635,7 @@ static struct plugin *new_plugin(const tal_t *ctx, jsmn_init(&p->rpc_parser); p->rpc_toks = toks_alloc(p); p->next_outreq_id = 0; - uintmap_init(&p->out_reqs); + strmap_init(&p->out_reqs); p->desired_features = tal_steal(p, features); if (init_rpc) { @@ -1698,7 +1713,8 @@ void plugin_main(char *argv[], daemon_setup(argv[0], NULL, NULL); va_start(ap, num_notif_topics); - plugin = new_plugin(NULL, init, restartability, init_rpc, features, commands, + plugin = new_plugin(NULL, argv[0], + init, restartability, init_rpc, features, commands, num_commands, notif_subs, num_notif_subs, hook_subs, num_hook_subs, notif_topics, num_notif_topics, ap); va_end(ap); diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 50c24da89b3d..ba7c6b4ab8a6 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -32,7 +32,7 @@ enum plugin_restartability { struct out_req { /* The unique id of this request. */ - u64 id; + const char *id; /* The command which is why we're calling this rpc. */ struct command *cmd; /* The request stream. */ diff --git a/tests/test_wallet.py b/tests/test_wallet.py index bed459f2a638..5b25d5a4a70b 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -60,7 +60,7 @@ def test_withdraw(node_factory, bitcoind): out = l1.rpc.withdraw(waddr, 2 * amount) # Side note: sendrawtransaction will trace back to withdrawl - l1.daemon.wait_for_log(": OUT:id=[0-9]*/cln:sendrawtransaction#[0-9]*") + l1.daemon.wait_for_log(": OUT:id=[0-9]*/cln:withdraw#[0-9]*/txprepare:sendpsbt#[0-9]*/cln:sendrawtransaction#[0-9]*") # Make sure bitcoind received the withdrawal unspent = l1.bitcoin.rpc.listunspent(0) From f1f2c1322d750361c2751cd63acb8dff51580c6b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:12 +0930 Subject: [PATCH 1377/1530] contrib/pyln-client: construct JSON ID correctly. They can set their name explicitly, but if they don't we extract it from argv[0]. We also set it around callbacks, so it will be expanded by default. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 26 ++++++++++++++++---- contrib/pyln-client/pyln/client/plugin.py | 12 +++++++-- tests/test_invoices.py | 5 +++- tests/test_plugin.py | 7 ++++-- tests/test_wallet.py | 4 ++- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 0013b89a3347..b0fff30336ec 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -2,6 +2,7 @@ import logging import os import socket +import sys from contextlib import contextmanager from decimal import Decimal from json import JSONEncoder @@ -277,13 +278,18 @@ def __del__(self) -> None: class UnixDomainSocketRpc(object): - def __init__(self, socket_path, executor=None, logger=logging, encoder_cls=json.JSONEncoder, decoder=json.JSONDecoder()): + def __init__(self, socket_path, executor=None, logger=logging, encoder_cls=json.JSONEncoder, decoder=json.JSONDecoder(), caller_name=None): self.socket_path = socket_path self.encoder_cls = encoder_cls self.decoder = decoder self.executor = executor self.logger = logger self._notify = None + if caller_name is None: + self.caller_name = os.path.splitext(os.path.basename(sys.argv[0]))[0] + else: + self.caller_name = caller_name + self.cmdprefix = None self.next_id = 1 @@ -323,7 +329,11 @@ def wrapper(*args, **kwargs): return self.call(name, payload=kwargs) return wrapper - def call(self, method, payload=None): + def call(self, method, payload=None, cmdprefix=None): + """Generic call API: you can set cmdprefix here, or set self.cmdprefix + before the call is made. + + """ self.logger.debug("Calling %s with payload %r", method, payload) if payload is None: @@ -332,10 +342,16 @@ def call(self, method, payload=None): if isinstance(payload, dict): payload = {k: v for k, v in payload.items() if v is not None} + this_id = "{}:{}#{}".format(self.caller_name, method, str(self.next_id)) + self.next_id += 1 + # FIXME: we open a new socket for every readobj call... sock = UnixSocket(self.socket_path) - this_id = self.next_id - self.next_id += 0 + if cmdprefix is None: + cmdprefix = self.cmdprefix + if cmdprefix: + this_id = cmdprefix + '/' + this_id + buf = b'' if self._notify is not None: @@ -343,7 +359,7 @@ def call(self, method, payload=None): self._writeobj(sock, { "jsonrpc": "2.0", "method": "notifications", - "id": 0, + "id": this_id + "+notify-enable", "params": { "enable": True }, diff --git a/contrib/pyln-client/pyln/client/plugin.py b/contrib/pyln-client/pyln/client/plugin.py index 8e437fc8bd22..921c9f3041d0 100644 --- a/contrib/pyln-client/pyln/client/plugin.py +++ b/contrib/pyln-client/pyln/client/plugin.py @@ -607,17 +607,25 @@ def _bind_kwargs(self, func: Callable[..., Any], params: Dict[str, Any], def _exec_func(self, func: Callable[..., Any], request: Request) -> JSONType: + # By default, any RPC calls this makes will have JSON id prefixed by incoming id. + if self.rpc: + self.rpc.cmdprefix = request.id params = request.params if isinstance(params, list): ba = self._bind_pos(func, params, request) - return func(*ba.args, **ba.kwargs) + ret = func(*ba.args, **ba.kwargs) elif isinstance(params, dict): ba = self._bind_kwargs(func, params, request) - return func(*ba.args, **ba.kwargs) + ret = func(*ba.args, **ba.kwargs) else: + if self.rpc: + self.rpc.cmdprefix = None raise TypeError( "Parameters to function call must be either a dict or a list." ) + if self.rpc: + self.rpc.cmdprefix = None + return ret def _dispatch_request(self, request: Request) -> None: name = request.method diff --git a/tests/test_invoices.py b/tests/test_invoices.py index bb954367674e..58d2aeecaaa9 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -4,7 +4,9 @@ from utils import only_one, wait_for, wait_channel_quiescent, mine_funding_to_announce +import os import pytest +import sys import time import unittest @@ -18,7 +20,8 @@ def test_invoice(node_factory, chainparams): inv = l1.rpc.invoice(123000, 'label', 'description', 3700, [addr1, addr2]) # Side note: invoice calls out to listincoming, so check JSON id is as expected - l1.daemon.wait_for_log(": OUT:id=1/cln:listincoming#[0-9]*") + myname = os.path.splitext(os.path.basename(sys.argv[0]))[0] + l1.daemon.wait_for_log(": OUT:id={}:invoice#[0-9]*/cln:listincoming#[0-9]*".format(myname)) after = int(time.time()) b11 = l1.rpc.decodepay(inv['bolt11']) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 586fd6cf0111..cb29400dfdca 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -24,6 +24,7 @@ import sqlite3 import stat import subprocess +import sys import time import unittest @@ -1486,8 +1487,10 @@ def test_libplugin(node_factory): l1.rpc.plugin_start(plugin) l1.rpc.check("helloworld") + myname = os.path.splitext(os.path.basename(sys.argv[0]))[0] + # Side note: getmanifest will trace back to plugin_start - l1.daemon.wait_for_log(": OUT:id=[0-9]*/cln:getmanifest#[0-9]*") + l1.daemon.wait_for_log(": OUT:id={}:plugin#[0-9]*/cln:getmanifest#[0-9]*".format(myname)) # Test commands assert l1.rpc.call("helloworld") == {"hello": "world"} @@ -1503,7 +1506,7 @@ def test_libplugin(node_factory): # Test hooks and notifications (add plugin, so we can test hook id) l2 = node_factory.get_node(options={"plugin": plugin}) l2.connect(l1) - l2.daemon.wait_for_log(": OUT:id=[0-9]*/cln:peer_connected#[0-9]*") + l2.daemon.wait_for_log(": OUT:id={}:connect#[0-9]*/cln:peer_connected#[0-9]*".format(myname)) l1.daemon.wait_for_log("{} peer_connected".format(l2.info["id"])) l1.daemon.wait_for_log("{} connected".format(l2.info["id"])) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 5b25d5a4a70b..19211b8d339c 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -12,6 +12,7 @@ import os import pytest import subprocess +import sys import time import unittest @@ -60,7 +61,8 @@ def test_withdraw(node_factory, bitcoind): out = l1.rpc.withdraw(waddr, 2 * amount) # Side note: sendrawtransaction will trace back to withdrawl - l1.daemon.wait_for_log(": OUT:id=[0-9]*/cln:withdraw#[0-9]*/txprepare:sendpsbt#[0-9]*/cln:sendrawtransaction#[0-9]*") + myname = os.path.splitext(os.path.basename(sys.argv[0]))[0] + l1.daemon.wait_for_log(": OUT:id={}:withdraw#[0-9]*/cln:withdraw#[0-9]*/txprepare:sendpsbt#[0-9]*/cln:sendrawtransaction#[0-9]*".format(myname)) # Make sure bitcoind received the withdrawal unspent = l1.bitcoin.rpc.listunspent(0) From fdc59dc94a69066a5b4c4a2209f9a352214a6ddd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:12 +0930 Subject: [PATCH 1378/1530] contrib/pyln-testing: pass through id correctly. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 15 ++++++++++----- contrib/pyln-testing/pyln/testing/utils.py | 6 +++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index b0fff30336ec..d2a64335be0d 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -329,6 +329,15 @@ def wrapper(*args, **kwargs): return self.call(name, payload=kwargs) return wrapper + def get_json_id(self, method, cmdprefix): + """Get a nicely formatted, CLN-compliant JSON ID""" + this_id = "{}:{}#{}".format(self.caller_name, method, str(self.next_id)) + if cmdprefix is None: + cmdprefix = self.cmdprefix + if cmdprefix: + this_id = cmdprefix + '/' + this_id + return this_id + def call(self, method, payload=None, cmdprefix=None): """Generic call API: you can set cmdprefix here, or set self.cmdprefix before the call is made. @@ -342,15 +351,11 @@ def call(self, method, payload=None, cmdprefix=None): if isinstance(payload, dict): payload = {k: v for k, v in payload.items() if v is not None} - this_id = "{}:{}#{}".format(self.caller_name, method, str(self.next_id)) + this_id = self.get_json_id(method, cmdprefix) self.next_id += 1 # FIXME: we open a new socket for every readobj call... sock = UnixSocket(self.socket_path) - if cmdprefix is None: - cmdprefix = self.cmdprefix - if cmdprefix: - this_id = cmdprefix + '/' + this_id buf = b'' diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index aec56baddbbf..b70502b36484 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -661,8 +661,8 @@ def __init__(self, socket_path, executor=None, logger=logging, self.jsonschemas = jsonschemas self.check_request_schemas = True - def call(self, method, payload=None): - id = self.next_id + def call(self, method, payload=None, cmdprefix=None): + id = self.get_json_id(method, cmdprefix) schemas = self.jsonschemas.get(method) self.logger.debug(json.dumps({ "id": id, @@ -681,7 +681,7 @@ def call(self, method, payload=None): testpayload[k] = v schemas[0].validate(testpayload) - res = LightningRpc.call(self, method, payload) + res = LightningRpc.call(self, method, payload, cmdprefix) self.logger.debug(json.dumps({ "id": id, "result": res From bf54d6dcf58733399d240fbddf99380882f49674 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:12 +0930 Subject: [PATCH 1379/1530] libplugin: use proper JSON id for rpc_scan(). And fold start_json_request() and start_json_rpc() into the core function since it's the only caller. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 63 +++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 2de4b0de7b56..171c7d0467b2 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -158,6 +158,21 @@ static void disable_request_cb(struct command *cmd, struct out_req *out) out->cmd = NULL; } +static const char *get_json_id(const tal_t *ctx, + struct plugin *plugin, + const char *cmd_id, + const char *method) +{ + if (cmd_id) + return tal_fmt(ctx, "%s/%s:%s#%"PRIu64, + cmd_id, + plugin->id, method, + plugin->next_outreq_id++); + return tal_fmt(ctx, "%s:%s#%"PRIu64, + plugin->id, method, + plugin->next_outreq_id++); +} + /* FIXME: Move lightningd/jsonrpc to common/ ? */ struct out_req * @@ -176,15 +191,7 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, struct out_req *out; out = tal(plugin, struct out_req); - if (cmd) - out->id = tal_fmt(out, "%s/%s:%s#%"PRIu64, cmd->id, - plugin->id, method, - plugin->next_outreq_id); - else - out->id = tal_fmt(out, "%s:%s#%"PRIu64, - plugin->id, method, - plugin->next_outreq_id); - plugin->next_outreq_id++; + out->id = get_json_id(out, plugin, cmd ? cmd->id : NULL, method); out->cmd = cmd; out->cb = cb; out->errcb = errcb; @@ -337,18 +344,6 @@ static int read_json_from_rpc(struct plugin *p) return end + 2 - membuf_elems(&p->rpc_conn->mb); } -/* This starts a JSON RPC message with boilerplate */ -static struct json_out *start_json_rpc(const tal_t *ctx, u64 id) -{ - struct json_out *jout = json_out_new(ctx); - - json_out_start(jout, NULL, '{'); - json_out_addstr(jout, "jsonrpc", "2.0"); - json_out_add(jout, "id", false, "%"PRIu64, id); - - return jout; -} - /* This closes a JSON response and writes it out. */ static void finish_and_send_json(int fd, struct json_out *jout) { @@ -509,22 +504,6 @@ static const jsmntok_t *read_rpc_reply(const tal_t *ctx, return toks; } -static struct json_out *start_json_request(const tal_t *ctx, - u64 id, - const char *method, - const struct json_out *params TAKES) -{ - struct json_out *jout; - - jout = start_json_rpc(tmpctx, id); - json_out_addstr(jout, "method", method); - json_out_add_splice(jout, "params", params); - if (taken(params)) - tal_free(params); - - return jout; -} - static const char *rpc_scan_core(const tal_t *ctx, struct plugin *plugin, const char *method, @@ -536,9 +515,15 @@ static const char *rpc_scan_core(const tal_t *ctx, const jsmntok_t *contents; int reqlen; const char *p; - struct json_out *jout; + struct json_out *jout = json_out_new(tmpctx); - jout = start_json_request(tmpctx, 0, method, params); + json_out_start(jout, NULL, '{'); + json_out_addstr(jout, "jsonrpc", "2.0"); + json_out_addstr(jout, "id", get_json_id(tmpctx, plugin, "init", method)); + json_out_addstr(jout, "method", method); + json_out_add_splice(jout, "params", params); + if (taken(params)) + tal_free(params); finish_and_send_json(plugin->rpc_conn->fd, jout); read_rpc_reply(tmpctx, plugin, &contents, &error, &reqlen); From 42c9aa1a5f683fbee50b84a1cd677814ba97f98a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 06:49:12 +0930 Subject: [PATCH 1380/1530] libplugin: forget pending requests if associated command freed. This is usually fine, but without this, commando (another branch!) has a race: 1. A command has multiple parts. 2. We start sending them out. 3. We get a response, which completes the cmd. 4. We go to send the next one out, but it's been freed. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 171c7d0467b2..c7b03989c44d 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -173,6 +173,11 @@ static const char *get_json_id(const tal_t *ctx, plugin->next_outreq_id++); } +static void destroy_out_req(struct out_req *out_req, struct plugin *plugin) +{ + strmap_del(&plugin->out_reqs, out_req->id, NULL); +} + /* FIXME: Move lightningd/jsonrpc to common/ ? */ struct out_req * @@ -190,13 +195,14 @@ jsonrpc_request_start_(struct plugin *plugin, struct command *cmd, { struct out_req *out; - out = tal(plugin, struct out_req); + out = tal(cmd, struct out_req); out->id = get_json_id(out, plugin, cmd ? cmd->id : NULL, method); out->cmd = cmd; out->cb = cb; out->errcb = errcb; out->arg = arg; strmap_add(&plugin->out_reqs, out->id, out); + tal_add_destructor2(out, destroy_out_req, plugin); /* If command goes away, don't call callbacks! */ if (out->cmd) @@ -684,7 +690,6 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) /* We want to free this if callback doesn't. */ tal_steal(tmpctx, out); - strmap_del(&plugin->out_reqs, out->id, NULL); contenttok = json_get_member(plugin->rpc_buffer, toks, "error"); if (contenttok) { From caecd1ee0aad1b391b3019c78a7e075165061bcf Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 09:57:42 +0930 Subject: [PATCH 1381/1530] lightningd: don't log JSON ids as debug, use log io. They are cute, sure, but they do spam the logs. @Suggested-by: @niftynei Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 6 ++++-- tests/test_invoices.py | 4 ++-- tests/test_misc.py | 4 ++-- tests/test_plugin.py | 9 +++++---- tests/test_wallet.py | 4 ++-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 67dab8285abb..c5a381956af8 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -912,7 +912,9 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) "Expected string for method"); } - log_debug(jcon->log, "IN:id=%s", c->id); + /* Debug was too chatty, so we use IO here, even though we're + * actually just logging the id */ + log_io(jcon->log, LOG_IO_IN, NULL, c->id, NULL, 0); c->json_cmd = find_cmd(jcon->ld->jsonrpc, jcon->buffer, method); if (!c->json_cmd) { @@ -1353,7 +1355,7 @@ struct jsonrpc_request *jsonrpc_request_start_( json_object_start(r->stream, "params"); } if (log) - log_debug(log, "OUT:id=%s", r->id); + log_io(log, LOG_IO_OUT, NULL, r->id, NULL, 0); return r; } diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 58d2aeecaaa9..277f03d58ed4 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -12,7 +12,7 @@ def test_invoice(node_factory, chainparams): - l1, l2 = node_factory.line_graph(2, fundchannel=False) + l1, l2 = node_factory.line_graph(2, fundchannel=False, opts={'log-level': 'io'}) addr1 = l2.rpc.newaddr('bech32')['bech32'] addr2 = l2.rpc.newaddr('p2sh-segwit')['p2sh-segwit'] @@ -21,7 +21,7 @@ def test_invoice(node_factory, chainparams): # Side note: invoice calls out to listincoming, so check JSON id is as expected myname = os.path.splitext(os.path.basename(sys.argv[0]))[0] - l1.daemon.wait_for_log(": OUT:id={}:invoice#[0-9]*/cln:listincoming#[0-9]*".format(myname)) + l1.daemon.wait_for_log(r": {}:invoice#[0-9]*/cln:listincoming#[0-9]*\[OUT\]".format(myname)) after = int(time.time()) b11 = l1.rpc.decodepay(inv['bolt11']) diff --git a/tests/test_misc.py b/tests/test_misc.py index b86449b5fe5f..9b846347f469 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -863,7 +863,7 @@ def test_malformed_rpc(node_factory): def test_cli(node_factory): - l1 = node_factory.get_node() + l1 = node_factory.get_node(options={'log-level': 'io'}) out = subprocess.check_output(['cli/lightning-cli', '--network={}'.format(TEST_NETWORK), @@ -874,7 +874,7 @@ def test_cli(node_factory): assert 'help [command]\n List available commands, or give verbose help on one {command}' in out # Check JSON id is as expected - l1.daemon.wait_for_log("jsonrpc#[0-9]*: IN:id=cli:help#[0-9]*") + l1.daemon.wait_for_log(r"jsonrpc#[0-9]*: cli:help#[0-9]*\[IN\]") # Test JSON output. out = subprocess.check_output(['cli/lightning-cli', diff --git a/tests/test_plugin.py b/tests/test_plugin.py index cb29400dfdca..53d474c2d8d3 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1478,7 +1478,8 @@ def test_libplugin(node_factory): """Sanity checks for plugins made with libplugin""" plugin = os.path.join(os.getcwd(), "tests/plugins/test_libplugin") l1 = node_factory.get_node(options={"plugin": plugin, - 'allow-deprecated-apis': False}) + 'allow-deprecated-apis': False, + 'log-level': 'io'}) # Test startup assert l1.daemon.is_in_log("test_libplugin initialised!") @@ -1490,7 +1491,7 @@ def test_libplugin(node_factory): myname = os.path.splitext(os.path.basename(sys.argv[0]))[0] # Side note: getmanifest will trace back to plugin_start - l1.daemon.wait_for_log(": OUT:id={}:plugin#[0-9]*/cln:getmanifest#[0-9]*".format(myname)) + l1.daemon.wait_for_log(r": {}:plugin#[0-9]*/cln:getmanifest#[0-9]*\[OUT\]".format(myname)) # Test commands assert l1.rpc.call("helloworld") == {"hello": "world"} @@ -1504,9 +1505,9 @@ def test_libplugin(node_factory): assert l1.rpc.call("helloworld", {"name": "test"}) == {"hello": "test"} # Test hooks and notifications (add plugin, so we can test hook id) - l2 = node_factory.get_node(options={"plugin": plugin}) + l2 = node_factory.get_node(options={"plugin": plugin, 'log-level': 'io'}) l2.connect(l1) - l2.daemon.wait_for_log(": OUT:id={}:connect#[0-9]*/cln:peer_connected#[0-9]*".format(myname)) + l2.daemon.wait_for_log(r": {}:connect#[0-9]*/cln:peer_connected#[0-9]*\[OUT\]".format(myname)) l1.daemon.wait_for_log("{} peer_connected".format(l2.info["id"])) l1.daemon.wait_for_log("{} connected".format(l2.info["id"])) diff --git a/tests/test_wallet.py b/tests/test_wallet.py index 19211b8d339c..a90fca09105c 100644 --- a/tests/test_wallet.py +++ b/tests/test_wallet.py @@ -29,7 +29,7 @@ def test_withdraw(node_factory, bitcoind): amount = 1000000 # Don't get any funds from previous runs. - l1 = node_factory.get_node(random_hsm=True) + l1 = node_factory.get_node(random_hsm=True, options={'log-level': 'io'}) l2 = node_factory.get_node(random_hsm=True) addr = l1.rpc.newaddr()['bech32'] @@ -62,7 +62,7 @@ def test_withdraw(node_factory, bitcoind): # Side note: sendrawtransaction will trace back to withdrawl myname = os.path.splitext(os.path.basename(sys.argv[0]))[0] - l1.daemon.wait_for_log(": OUT:id={}:withdraw#[0-9]*/cln:withdraw#[0-9]*/txprepare:sendpsbt#[0-9]*/cln:sendrawtransaction#[0-9]*".format(myname)) + l1.daemon.wait_for_log(r": {}:withdraw#[0-9]*/cln:withdraw#[0-9]*/txprepare:sendpsbt#[0-9]*/cln:sendrawtransaction#[0-9]*\[OUT\]".format(myname)) # Make sure bitcoind received the withdrawal unspent = l1.bitcoin.rpc.listunspent(0) From bbe1711e1683739dbfea07c81dabe594d7c98b08 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 13 Sep 2022 09:57:43 +0930 Subject: [PATCH 1382/1530] lightningd: use jcon logging for commands where possible. Signed-off-by: Rusty Russell --- lightningd/invoice.c | 2 +- lightningd/jsonrpc.c | 7 +++++++ lightningd/jsonrpc.h | 3 +++ lightningd/signmessage.c | 2 +- lightningd/test/run-invoice-select-inchan.c | 3 +++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index b5743f136dc3..c239a67a910b 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1251,7 +1251,7 @@ static struct command_result *json_invoice(struct command *cmd, req = jsonrpc_request_start(info, "listincoming", cmd->id, - cmd->ld->log, + command_log(cmd), NULL, listincoming_done, info); jsonrpc_request_end(req); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index c5a381956af8..a5524b203152 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -145,6 +145,13 @@ static void destroy_jcon(struct json_connection *jcon) tal_free(jcon->log); } +struct log *command_log(struct command *cmd) +{ + if (cmd->jcon) + return cmd->jcon->log; + return cmd->ld->log; +} + static struct command_result *json_help(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index ec52aeab6953..1964f1db45de 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -138,6 +138,9 @@ void json_stream_log_suppress_for_cmd(struct json_stream *js, struct command_result *command_raw_complete(struct command *cmd, struct json_stream *result); +/* Logging point to use for this command (usually, the JSON connection). */ +struct log *command_log(struct command *cmd); + /* To return if param() fails. */ extern struct command_result *command_param_failed(void) WARN_UNUSED_RESULT; diff --git a/lightningd/signmessage.c b/lightningd/signmessage.c index 9d0197fac8c8..59862caad24d 100644 --- a/lightningd/signmessage.c +++ b/lightningd/signmessage.c @@ -213,7 +213,7 @@ static struct command_result *json_checkmessage(struct command *cmd, can->cmd = cmd; req = jsonrpc_request_start(cmd, "listnodes", cmd->id, - cmd->ld->log, + command_log(cmd), NULL, listnodes_done, can); json_add_node_id(req->stream, "id", &can->id); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 2af47c1467e8..fcf26cbbf741 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -136,6 +136,9 @@ struct command_result *command_failed(struct command *cmd UNNEEDED, /* Generated stub for command_its_complicated */ struct command_result *command_its_complicated(const char *why UNNEEDED) { fprintf(stderr, "command_its_complicated called!\n"); abort(); } +/* Generated stub for command_log */ +struct log *command_log(struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_log called!\n"); abort(); } /* Generated stub for command_param_failed */ struct command_result *command_param_failed(void) From 74cd0a72801967f2c459c7613c4d8ca604f17fb9 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 16 Sep 2022 11:28:00 +0200 Subject: [PATCH 1383/1530] gci: Use stable rust instead of nightly Nightly occasionally breaks, so use stable instead. Changelog-None --- .github/scripts/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/setup.sh b/.github/scripts/setup.sh index 928c6e2ec95c..2523a2dd96b0 100755 --- a/.github/scripts/setup.sh +++ b/.github/scripts/setup.sh @@ -3,7 +3,7 @@ export DEBIAN_FRONTEND=noninteractive export BITCOIN_VERSION=0.20.1 export ELEMENTS_VERSION=0.18.1.8 -export RUST_VERSION=nightly +export RUST_VERSION=stable sudo useradd -ms /bin/bash tester sudo apt-get update -qq From 37c07ddfe5cbe63dadbddb4e6d29ef9ec76221f2 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 16 Sep 2022 13:23:38 +0200 Subject: [PATCH 1384/1530] pyln: Add grpcio and protobuf dependencies to pyln-testing --- contrib/pyln-testing/pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index 784bbe23c403..4aaede93e037 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -21,6 +21,8 @@ pyln-client = "^0.11" Flask = "^2.0.3" cheroot = "^8.6.0" psutil = "^5.9.0" +grpcio = "^1.49.0" +protobuf = "^4.21.6" [tool.poetry.dev-dependencies] pyln-client = { path = "../pyln-client", develop = true} From 37fc0d8c6f9b6b7de6e9dbffe61d3d3db162cb3a Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 13 Sep 2022 14:33:26 -0500 Subject: [PATCH 1385/1530] docker: use pip install + poetry export instead of poetry update I dunno man, poetry just seems like a mess. --- contrib/docker/scripts/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/docker/scripts/build.sh b/contrib/docker/scripts/build.sh index 6670a917b80a..44047b22a122 100755 --- a/contrib/docker/scripts/build.sh +++ b/contrib/docker/scripts/build.sh @@ -23,8 +23,8 @@ export LIGHTNINGD_POSTGRES_NO_VACUUM=1 pip3 install --upgrade pip pip3 install --user poetry -poetry config virtualenvs.create false --local -poetry install +poetry export --dev --without-hashes -o requirements.txt +pip3 install -r requirements.txt git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc git submodule update --init --recursive From 05e23171421eb47466af7920af36eaa74deac21b Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 13 Sep 2022 14:33:54 -0500 Subject: [PATCH 1386/1530] doc/install: get rid of out of date mrkd / mistune install instructions --- doc/INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 4f8169433a99..61bb039d50a3 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -77,7 +77,7 @@ There are two ways to build core lightning, and this depends on how you want use To build cln to just install a tagged or master version you can use the following commands: pip3 install --upgrade pip - pip3 install mako mistune==0.8.4 mrkd + pip3 install mako ./configure make sudo make install From cc206e1f0e281b33cf86e1eede5f3545fdcec2ea Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Fri, 16 Sep 2022 18:11:51 -0400 Subject: [PATCH 1387/1530] connectd+: Flake/race fix for new channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) dualopen has fd to connectd 2) channeld needs to take over 3) dualopen passes fd that leads to a connectd over for channeld to use 4) lightningd must receive the fd transfer request and process 5) dualopen shuts down and closes everything it owns 4 & 5 end up in a race. If 5 happens before 4, channeld ends up with an invalid fd for connectd — leaving it in a position to not receive messages. Lingering for a second makes 4 win the race. Since the daemon is closing anyway, waiting for a second should be alright. Changelog-Fixed: Fixed a condition for newly created channels that could trigger a need for reconnect. --- channeld/channeld.c | 4 ++++ openingd/dualopend.c | 6 ++++++ openingd/openingd.c | 3 +++ 3 files changed, 13 insertions(+) diff --git a/channeld/channeld.c b/channeld/channeld.c index c4f2a744dbb9..f701d24aa6f0 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -955,6 +955,10 @@ static void send_shutdown_complete(struct peer *peer) wire_sync_write(MASTER_FD, take(towire_channeld_shutdown_complete(NULL))); per_peer_state_fdpass_send(MASTER_FD, peer->pps); + + /* Give master a chance to pass the fd along */ + sleep(1); + close(MASTER_FD); } diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 03e47911c3d0..e215cca188d5 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -303,6 +303,9 @@ static void dualopen_shutdown(struct state *state) status_debug("Sent %s with fds", dualopend_wire_name(fromwire_peektype(msg))); + /* Give master a chance to pass the fd along */ + sleep(1); + /* This frees the entire tal tree. */ tal_free(state); daemon_shutdown(); @@ -3989,6 +3992,9 @@ int main(int argc, char *argv[]) dualopend_wire_name(fromwire_peektype(msg))); tal_free(msg); + /* Give master a chance to pass the fd along */ + sleep(1); + /* This frees the entire tal tree. */ tal_free(state); daemon_shutdown(); diff --git a/openingd/openingd.c b/openingd/openingd.c index 3e9272a43f8d..a7bd167f5937 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1485,6 +1485,9 @@ int main(int argc, char *argv[]) status_debug("Sent %s with fd", openingd_wire_name(fromwire_peektype(msg))); + /* Give master a chance to pass the fd along */ + sleep(1); + /* This frees the entire tal tree. */ tal_free(state); From 3aeac0a58c938e0afa26d358ea9fd123d761cdd1 Mon Sep 17 00:00:00 2001 From: Dustin Dettmer Date: Tue, 30 Aug 2022 17:16:20 -0500 Subject: [PATCH 1388/1530] docs: Correct a command typo for Ubuntuu --- doc/INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 61bb039d50a3..6b41cdb459ee 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -96,7 +96,7 @@ This will put you in a new shell to enter the following commands: make make check VALGRIND=0 -optionaly you can consider to use the `-j$(nproc)` in front of the `make` command to speed up the compilation. +optionaly you can consider adding the `-j$(nproc)` flag after the `make` command to speed up the compilation. Running lightning: From 9bba7ba1a43fabc910cad71f93bd825b61bbd33a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 15 Sep 2022 11:36:27 +0200 Subject: [PATCH 1389/1530] Update doc/INSTALL.md Co-authored-by: neil saitug --- doc/INSTALL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/INSTALL.md b/doc/INSTALL.md index 6b41cdb459ee..c4b318593e58 100644 --- a/doc/INSTALL.md +++ b/doc/INSTALL.md @@ -96,7 +96,7 @@ This will put you in a new shell to enter the following commands: make make check VALGRIND=0 -optionaly you can consider adding the `-j$(nproc)` flag after the `make` command to speed up the compilation. +optionally, add `-j$(nproc)` after `make` to speed up compilation. (e.g. `make -j$(nproc)`) Running lightning: From a99a72be9b1e07d5fe2feb1372f58a122da9499c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 19:46:45 +0930 Subject: [PATCH 1390/1530] contrib/startup_regtest.sh: misc fixes and add destroy_ln, print usage. We weren't consistent with passing through regtest, and we should be. Also, destroy_ln is useful for getting rid of all dirs. Finally, use eatmydata if it's available. Signed-off-by: Rusty Russell --- contrib/startup_regtest.sh | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index db04a3eb8fbf..0b7b7eb6c46a 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -23,7 +23,8 @@ ## ## When you're finished, clean up or stop ## -## $ stop_ln # stops the services, keeps the aliases +## $ stop_ln +## $ destroy_ln # clean up the lightning directories ## # Do the Right Thing if we're currently in top of srcdir. @@ -72,6 +73,12 @@ start_nodes() { else network=$2 fi + # This supresses db syncs, for speed. + if type eatmydata >/dev/null 2>&1; then + EATMYDATA=eatmydata + else + EATMYDATA= + fi LN_NODES=$node_count @@ -107,13 +114,16 @@ start_nodes() { # Start the lightning nodes test -f "/tmp/l$i-$network/lightningd-$network.pid" || \ - "$LIGHTNINGD" "--lightning-dir=/tmp/l$i-$network" & + $EATMYDATA "$LIGHTNINGD" "--lightning-dir=/tmp/l$i-$network" & # shellcheck disable=SC2139 disable=SC2086 alias l$i-cli="$LCLI --lightning-dir=/tmp/l$i-$network" # shellcheck disable=SC2139 disable=SC2086 alias l$i-log="less /tmp/l$i-$network/log" done + if [ -z "$EATMYDATA" ]; then + echo "WARNING: eatmydata not found: instal it for faster testing" + fi # Give a hint. echo "Commands: " for i in $(seq $node_count); do @@ -168,7 +178,6 @@ ensure_bitcoind_funds() { } fund_nodes() { - WALLET="default" NODES="" @@ -248,11 +257,7 @@ fund_nodes() { } stop_nodes() { - if [ -z "$2" ]; then - network=regtest - else - network="$2" - fi + network=${1:-regtest} if [ -n "$LN_NODES" ]; then for i in $(seq $LN_NODES); do test ! -f "/tmp/l$i-$network/lightningd-$network.pid" || \ @@ -265,7 +270,7 @@ stop_nodes() { } stop_ln() { - stop_nodes "$1" regtest + stop_nodes "$@" test ! -f "$PATH_TO_BITCOIN/regtest/bitcoind.pid" || \ (kill "$(cat "$PATH_TO_BITCOIN/regtest/bitcoind.pid")"; \ rm "$PATH_TO_BITCOIN/regtest/bitcoind.pid") @@ -274,6 +279,11 @@ stop_ln() { unalias bt-cli } +destroy_ln() { + network=${1:-regtest} + rm -rf /tmp/l[0-9]*-"$network" +} + start_elem() { if [ -z "$PATH_TO_ELEMENTS" ]; then if [ -d "$HOME/.elements" ]; then @@ -324,3 +334,10 @@ connect() { $LCLI --lightning-dir="/tmp/l$1-$network" connect "$to" fi } + +echo Useful commands: +echo " start_ln 3: start three nodes, l1, l2, l3" +echo " connect 1 2: connect l1 and l2" +echo " fund_nodes: connect all nodes with channels, in a row" +echo " stop_ln: shutdown" +echo " destroy_ln: remove ln directories" From d2edeff6984b0a0ac2355f9f071d829257163281 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 19:46:48 +0930 Subject: [PATCH 1391/1530] contrib/giantnode.py: populate three regtest nodes with many invoices/forwards/payments. If you don't want to run this (and it's v slow without the coming optimizations!) you can download a copy from: https://ozlabs.org/~rusty/giantnodes.tar.xz (Unpack in /, then just `start_ln 3`). Signed-off-by: Rusty Russell --- contrib/giantnode.py | 183 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100755 contrib/giantnode.py diff --git a/contrib/giantnode.py b/contrib/giantnode.py new file mode 100755 index 000000000000..0c14eecb3175 --- /dev/null +++ b/contrib/giantnode.py @@ -0,0 +1,183 @@ +#! /usr/bin/env python3 +# Script to create huge numbers of forwards/payments/etc for regtest nodes. + +# To initialize, use contrib/startup_regtest.sh: +# $ . contrib/startup_regtest.sh +# $ start_ln 3 +# $ fund_nodes + +import argparse +import random +import string +import sys +import time +import multiprocessing +from pyln.client import LightningRpc, RpcError + +parser = argparse.ArgumentParser( + description='Flood three funded startup_regtest nodes' +) +parser.add_argument('--fail-forward', action="store_true", + help='Create failed forward attempts', + default=False) +parser.add_argument('--fail-pay', action="store_true", + help='Allow some failed pay attempts (faster!)', + default=False) +parser.add_argument('--inv-runners', type=int, + help='How many invoice-generation processes to run at once', + default=1) +parser.add_argument('--pay-runners', type=int, + help='How many invoice-paying processes to run at once', + default=8) +parser.add_argument('--check-runners', type=int, + help='How many pay-checking processes to run at once', + default=1) +parser.add_argument('--allow-bookkeeper', action="store_true", + help="Don't stop if bookkeeper is running", + default=False) +parser.add_argument('num', type=int, nargs='?', + help='number to attempt', + default=1000000) + +args = parser.parse_args() + +nodes = (LightningRpc('/tmp/l1-regtest/regtest/lightning-rpc'), + LightningRpc('/tmp/l2-regtest/regtest/lightning-rpc'), + LightningRpc('/tmp/l3-regtest/regtest/lightning-rpc')) + +nodes = (LightningRpc('/tmp/l1-regtest/regtest/lightning-rpc'), + LightningRpc('/tmp/l2-regtest/regtest/lightning-rpc'), + LightningRpc('/tmp/l3-regtest/regtest/lightning-rpc')) + +# Convenient aliases +l1 = nodes[0] +l2 = nodes[1] +l3 = nodes[2] + +if not args.allow_bookkeeper: + if any([p['name'].endswith('bookkeeper') for p in l1.plugin_list()['plugins']]): + print(""" +Bookkeeper is running on l1, will slow things down! Run this: + +echo 'disable-plugin=bookkeeper' >> /tmp/l1-regtest/regtest/config +echo 'disable-plugin=bookkeeper' >> /tmp/l2-regtest/regtest/config +echo 'disable-plugin=bookkeeper' >> /tmp/l3-regtest/regtest/config +stop_ln +start_ln 3 +""") + sys.exit(1) + +route = l1.getroute(l3.getinfo()['id'], 1, 1)['route'] +if args.fail_forward: + route[1]['channel'] = '1x1x1' + + +def get_invs(inv_q, num, report_q): + """Runners feed invoices into the queue""" + prefix = ''.join(random.choice(string.ascii_lowercase) for _ in range(10)) + for i in range(num): + inv_q.put(l3.invoice(amount_msat=1, + label='{}-{}'.format(prefix, i), + description='giantnode')) + report_q.put('i') + + +def send_pay(inv_q, num, done_q, report_q): + """Runner to fetch invoices from queue and send""" + for i in range(num): + inv = inv_q.get() + l1.sendpay(route, + inv['payment_hash'], + payment_secret=inv['payment_secret']) + report_q.put('p') + done_q.put(inv) + + +def checker(num, inv_q, done_q, report_q): + """Runner which checks that invs didn't fail (TOO_MANY_HTLCS!), and requeues if they did!""" + for i in range(num): + inv = done_q.get() + try: + res = l1.waitsendpay(inv['payment_hash']) + except RpcError: + report_q.put('f') + if not args.fail_pay and not args.fail_forward: + inv_q.put(inv) + continue + assert res['status'] == 'complete' + report_q.put('w') + + +inv_q = multiprocessing.Queue() +done_q = multiprocessing.Queue() +report_q = multiprocessing.Queue() + +# In case it doesn't divide +extra_prod = (args.num % args.inv_runners,) + (0,) * (args.inv_runners - 1) +inv_producers = [multiprocessing.Process(target=get_invs, args=(inv_q, args.num // args.inv_runners + extra_prod[i], report_q)) for i in range(args.inv_runners)] +extra_cons = (args.num % args.pay_runners,) + (0,) * (args.pay_runners - 1) +inv_consumers = [multiprocessing.Process(target=send_pay, args=(inv_q, args.num // args.pay_runners + extra_cons[i], done_q, report_q)) for i in range(args.pay_runners)] + +extra_checks = (args.num % args.check_runners,) + (0,) * (args.check_runners - 1) +checkers = [multiprocessing.Process(target=checker, args=(args.num // args.check_runners + extra_checks[i], inv_q, done_q, report_q)) for i in range(args.check_runners)] + +prev = start = time.time() +for i in inv_producers + inv_consumers + checkers: + i.start() + +num_total = 0 +num_successes = 0 +num_invs = 0 +num_pays = 0 +num_retries = 0 +prev_total = 0 + + +def timefmt(number, time): + if time == 0: + return '?' + seconds = number / time + minutes = seconds / 60 + hours = minutes / 60 + if hours > 2: + return '{} hours'.format(int(hours)) + if minutes > 2: + return '{} minutes'.format(int(minutes)) + + return '{} seconds'.format(int(seconds)) + + +while num_total < args.num: + letter = report_q.get() + if letter == 'i': + num_invs += 1 + elif letter == 'p': + num_pays += 1 + elif letter == 'f': + num_retries += 1 + elif letter == 'w': + num_successes += 1 + else: + assert False + + if args.fail_pay or args.fail_forward: + num_total = num_pays + else: + num_total = num_successes + + now = time.time() + if now > prev + 10: + current_rate = (num_total - prev_total) / (now - prev) + prev = now + prev_total = num_total + total_rate = num_total / (now - start) + print("{}/{} complete {}/sec ({} invs, {} pays, {} retries) in {} seconds. {}-{} remaining." + .format(num_total, args.num, format(current_rate, ".2f"), + num_invs, num_pays, num_retries, int(now - start), + timefmt(args.num - num_total, total_rate), + timefmt(args.num - num_total, current_rate))) + +for i in inv_producers + inv_consumers + checkers: + i.join() + +print("done") From 7fa13646458418b80bf46c512086700d6b7a7727 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 18 Sep 2022 09:49:50 +0930 Subject: [PATCH 1392/1530] build: allow DEVELOPER builds with -Og and gcc 9.4.0 Removes some warnings. Signed-off-by: Rusty Russell --- channeld/test/run-full_channel.c | 2 ++ common/billboard.c | 8 ++++++-- common/json_param.c | 34 +++++++++++--------------------- wallet/reservation.c | 2 +- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index 2a5112dee2d0..4992d9f9ce54 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -160,6 +160,8 @@ static const struct htlc **include_htlcs(struct channel *channel, enum side side sender = !side; msatoshi = AMOUNT_MSAT(4000000); break; + default: + abort(); } assert(msatoshi.millisatoshis != 0); diff --git a/common/billboard.c b/common/billboard.c index e67efe4a0ff1..155ceb7f0d17 100644 --- a/common/billboard.c +++ b/common/billboard.c @@ -22,8 +22,10 @@ char *billboard_message(const tal_t *ctx, depth_togo); else if (channel_ready[LOCAL] && !channel_ready[REMOTE]) funding_status = "We've confirmed channel ready, they haven't yet."; - else if (!channel_ready[LOCAL] && channel_ready[REMOTE]) + else { + assert(!channel_ready[LOCAL] && channel_ready[REMOTE]); funding_status = "They've confirmed channel ready, we haven't yet."; + } if (have_sigs) { if (have_sigs[LOCAL] && have_sigs[REMOTE]) @@ -34,8 +36,10 @@ char *billboard_message(const tal_t *ctx, else if (!have_sigs[LOCAL] && have_sigs[REMOTE]) announce_status = " They need our announcement" " signatures."; - else if (!have_sigs[LOCAL] && !have_sigs[REMOTE]) + else { + assert(!have_sigs[LOCAL] && !have_sigs[REMOTE]); announce_status = ""; + } } else announce_status = ""; diff --git a/common/json_param.c b/common/json_param.c index 85c80d8e0a9e..0eecf4a497fe 100644 --- a/common/json_param.c +++ b/common/json_param.c @@ -734,15 +734,10 @@ json_to_address_scriptpubkey(const tal_t *ctx, char *addrz; const char *bip173; - bool parsed; - bool right_network; u8 addr_version; - parsed = - ripemd160_from_base58(&addr_version, &destination.addr, - buffer + tok->start, tok->end - tok->start); - - if (parsed) { + if (ripemd160_from_base58(&addr_version, &destination.addr, + buffer + tok->start, tok->end - tok->start)) { if (addr_version == chainparams->p2pkh_version) { *scriptpubkey = scriptpubkey_p2pkh(ctx, &destination); return ADDRESS_PARSE_SUCCESS; @@ -754,10 +749,11 @@ json_to_address_scriptpubkey(const tal_t *ctx, return ADDRESS_PARSE_WRONG_NETWORK; } /* Insert other parsers that accept pointer+len here. */ + return ADDRESS_PARSE_UNRECOGNIZED; } /* Generate null-terminated address. */ - addrz = tal_dup_arr(ctx, char, buffer + tok->start, tok->end - tok->start, 1); + addrz = tal_dup_arr(tmpctx, char, buffer + tok->start, tok->end - tok->start, 1); addrz[tok->end - tok->start] = '\0'; bip173 = segwit_addr_net_decode(&witness_version, witness_program, @@ -772,24 +768,18 @@ json_to_address_scriptpubkey(const tal_t *ctx, } else witness_ok = true; - if (witness_ok) { - *scriptpubkey = scriptpubkey_witness_raw(ctx, witness_version, - witness_program, witness_program_len); - parsed = true; - right_network = streq(bip173, chainparams->onchain_hrp); - } - } - /* Insert other parsers that accept null-terminated string here. */ - - tal_free(addrz); + if (!witness_ok) + return ADDRESS_PARSE_UNRECOGNIZED; - if (parsed) { - if (right_network) - return ADDRESS_PARSE_SUCCESS; - else + if (!streq(bip173, chainparams->onchain_hrp)) return ADDRESS_PARSE_WRONG_NETWORK; + + *scriptpubkey = scriptpubkey_witness_raw(ctx, witness_version, + witness_program, witness_program_len); + return ADDRESS_PARSE_SUCCESS; } + /* Insert other parsers that accept null-terminated string here. */ return ADDRESS_PARSE_UNRECOGNIZED; } diff --git a/wallet/reservation.c b/wallet/reservation.c index 88097636cbf2..41ba958f93c7 100644 --- a/wallet/reservation.c +++ b/wallet/reservation.c @@ -335,7 +335,7 @@ static struct command_result *finish_psbt(struct command *cmd, { struct json_stream *response; struct wally_psbt *psbt; - size_t change_outnum; + size_t change_outnum COMPILER_WANTS_INIT("gcc 9.4.0 -Og"); u32 current_height = get_block_height(cmd->ld->topology); /* Setting the locktime to the next block to be mined has multiple From 2da5244e8390b8d9b5bdb12651a35cd559ad8283 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 18 Sep 2022 09:50:50 +0930 Subject: [PATCH 1393/1530] jsonrpc: make error codes an enum. This allows GDB to print values, but also allows us to use them in 'case' statements. This wasn't allowed before because they're not constant terms. This also made it clear there's a clash between two error codes, so move one. Signed-off-by: Rusty Russell Changelog-Changed: JSON-RPC: Error code from bcli plugin changed from 400 to 500. --- common/errcode.h | 4 - common/json_command.h | 2 +- common/json_parse.c | 3 +- common/json_parse.h | 4 +- common/json_stream.c | 6 +- common/json_stream.h | 6 +- common/jsonrpc_errors.h | 196 ++++++++++---------- common/test/run-json_remove.c | 2 +- common/test/run-param.c | 2 +- connectd/connectd.c | 4 +- connectd/connectd_wire.csv | 2 +- lightningd/connect_control.c | 4 +- lightningd/jsonrpc.c | 18 +- lightningd/jsonrpc.h | 4 +- lightningd/notification.c | 8 +- lightningd/notification.h | 2 +- lightningd/pay.c | 14 +- lightningd/pay.h | 2 +- lightningd/test/run-invoice-select-inchan.c | 4 +- lightningd/test/run-jsonrpc.c | 7 +- lightningd/test/run-log-pruning.c | 2 +- plugins/libplugin.c | 6 +- plugins/libplugin.h | 2 +- plugins/spender/multifundchannel.c | 6 +- plugins/spender/multifundchannel.h | 4 +- plugins/spender/multiwithdraw.c | 4 +- tools/generate-wire.py | 2 - wallet/test/run-wallet.c | 2 +- wire/fromwire.c | 4 +- wire/towire.c | 2 +- wire/wire.h | 6 +- 31 files changed, 167 insertions(+), 167 deletions(-) diff --git a/common/errcode.h b/common/errcode.h index eb4da10c7423..9fcf6fbedc71 100644 --- a/common/errcode.h +++ b/common/errcode.h @@ -5,10 +5,6 @@ #include -typedef s32 errcode_t; - -#define PRIerrcode PRId32 - // Setup errors #define EXITCODE_SUBDAEMON_FAIL 10 #define EXITCODE_PIDFILE_LOCK 11 diff --git a/common/json_command.h b/common/json_command.h index 88cf14aeb9d4..e4a2dbd679b0 100644 --- a/common/json_command.h +++ b/common/json_command.h @@ -11,7 +11,7 @@ struct command; struct command_result; /* Caller supplied this: param assumes it can call it. */ -struct command_result *command_fail(struct command *cmd, errcode_t code, +struct command_result *command_fail(struct command *cmd, enum jsonrpc_errcode code, const char *fmt, ...) PRINTF_FMT(3, 4) WARN_UNUSED_RESULT RETURNS_NONNULL; diff --git a/common/json_parse.c b/common/json_parse.c index 025968d34a41..f60160519521 100644 --- a/common/json_parse.c +++ b/common/json_parse.c @@ -134,7 +134,8 @@ bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num) return true; } -bool json_to_errcode(const char *buffer, const jsmntok_t *tok, errcode_t *errcode) +bool json_to_jsonrpc_errcode(const char *buffer, const jsmntok_t *tok, + enum jsonrpc_errcode *errcode) { s64 tmp; diff --git a/common/json_parse.h b/common/json_parse.h index bcca914b2624..311e3f04b305 100644 --- a/common/json_parse.h +++ b/common/json_parse.h @@ -6,6 +6,7 @@ #include /* Simple helpers are here: this file contains heavier ones */ #include +#include struct json_escape; struct json_stream; @@ -49,7 +50,8 @@ bool json_to_millionths(const char *buffer, const jsmntok_t *tok, bool json_to_int(const char *buffer, const jsmntok_t *tok, int *num); /* Extract an error code from this (may be a string, or a number literal) */ -bool json_to_errcode(const char *buffer, const jsmntok_t *tok, errcode_t *errcode); +bool json_to_jsonrpc_errcode(const char *buffer, const jsmntok_t *tok, + enum jsonrpc_errcode *errcode); /* Split a json token into 2 tokens given a splitting character */ bool split_tok(const char *buffer, const jsmntok_t *tok, diff --git a/common/json_stream.c b/common/json_stream.c index 53b374e24260..5e9401c629b6 100644 --- a/common/json_stream.c +++ b/common/json_stream.c @@ -377,10 +377,10 @@ void json_add_tok(struct json_stream *result, const char *fieldname, memcpy(space, json_tok_full(buffer, tok), json_tok_full_len(tok)); } -void json_add_errcode(struct json_stream *result, const char *fieldname, - errcode_t code) +void json_add_jsonrpc_errcode(struct json_stream *result, const char *fieldname, + enum jsonrpc_errcode code) { - json_add_primitive_fmt(result, fieldname, "%" PRIerrcode, code); + json_add_primitive_fmt(result, fieldname, "%i", code); } void json_add_invstring(struct json_stream *result, const char *invstring) diff --git a/common/json_stream.h b/common/json_stream.h index 316da33f4b42..7b0601e82956 100644 --- a/common/json_stream.h +++ b/common/json_stream.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include struct command; struct io_conn; @@ -260,8 +260,8 @@ void json_add_tok(struct json_stream *result, const char *fieldname, const jsmntok_t *tok, const char *buffer); /* Add an error code */ -void json_add_errcode(struct json_stream *result, const char *fieldname, - errcode_t code); +void json_add_jsonrpc_errcode(struct json_stream *result, const char *fieldname, + enum jsonrpc_errcode code); /* Add "bolt11" or "bolt12" field, depending on invstring. */ void json_add_invstring(struct json_stream *result, const char *invstring); diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index ebf45c4eb07f..cb0f1a49c29e 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -8,102 +8,104 @@ #include -/* Standard errors defined by JSON-RPC 2.0 standard */ -static const errcode_t JSONRPC2_INVALID_REQUEST = -32600; -static const errcode_t JSONRPC2_METHOD_NOT_FOUND = -32601; -static const errcode_t JSONRPC2_INVALID_PARAMS = -32602; - -/* Uncategorized error. - * FIXME: This should be replaced in all places - * with a specific error code, and then removed. - */ -static const errcode_t LIGHTNINGD = -1; - -/* Developer error in the parameters to param() call */ -static const errcode_t PARAM_DEV_ERROR = -2; - -/* Plugin returned an error */ -static const errcode_t PLUGIN_ERROR = -3; - -/* Plugin terminated while handling a request. */ -static const errcode_t PLUGIN_TERMINATED = -4; - -/* Lightningd is shutting down while handling a request. */ -static const errcode_t LIGHTNINGD_SHUTDOWN = -5; - -/* Errors from `pay`, `sendpay`, or `waitsendpay` commands */ -static const errcode_t PAY_IN_PROGRESS = 200; -static const errcode_t PAY_RHASH_ALREADY_USED = 201; -static const errcode_t PAY_UNPARSEABLE_ONION = 202; -static const errcode_t PAY_DESTINATION_PERM_FAIL = 203; -static const errcode_t PAY_TRY_OTHER_ROUTE = 204; -static const errcode_t PAY_ROUTE_NOT_FOUND = 205; -static const errcode_t PAY_ROUTE_TOO_EXPENSIVE = 206; -static const errcode_t PAY_INVOICE_EXPIRED = 207; -static const errcode_t PAY_NO_SUCH_PAYMENT = 208; -static const errcode_t PAY_UNSPECIFIED_ERROR = 209; -static const errcode_t PAY_STOPPED_RETRYING = 210; -static const errcode_t PAY_STATUS_UNEXPECTED = 211; -static const errcode_t PAY_OFFER_INVALID = 212; - -/* `fundchannel` or `withdraw` errors */ -static const errcode_t FUND_MAX_EXCEEDED = 300; -static const errcode_t FUND_CANNOT_AFFORD = 301; -static const errcode_t FUND_OUTPUT_IS_DUST = 302; -static const errcode_t FUNDING_BROADCAST_FAIL = 303; -static const errcode_t FUNDING_STILL_SYNCING_BITCOIN = 304; -static const errcode_t FUNDING_PEER_NOT_CONNECTED = 305; -static const errcode_t FUNDING_UNKNOWN_PEER = 306; -static const errcode_t FUNDING_NOTHING_TO_CANCEL = 307; -static const errcode_t FUNDING_CANCEL_NOT_SAFE = 308; -static const errcode_t FUNDING_PSBT_INVALID = 309; -static const errcode_t FUNDING_V2_NOT_SUPPORTED = 310; -static const errcode_t FUNDING_UNKNOWN_CHANNEL = 311; -static const errcode_t FUNDING_STATE_INVALID = 312; - -/* `connect` errors */ -static const errcode_t CONNECT_NO_KNOWN_ADDRESS = 400; -static const errcode_t CONNECT_ALL_ADDRESSES_FAILED = 401; -static const errcode_t CONNECT_DISCONNECTED_DURING = 402; - -/* bitcoin-cli plugin errors */ -#define BCLI_ERROR 400 - -/* Errors from `invoice` or `delinvoice` commands */ -static const errcode_t INVOICE_LABEL_ALREADY_EXISTS = 900; -static const errcode_t INVOICE_PREIMAGE_ALREADY_EXISTS = 901; -static const errcode_t INVOICE_HINTS_GAVE_NO_ROUTES = 902; -static const errcode_t INVOICE_EXPIRED_DURING_WAIT = 903; -static const errcode_t INVOICE_WAIT_TIMED_OUT = 904; -static const errcode_t INVOICE_NOT_FOUND = 905; -static const errcode_t INVOICE_STATUS_UNEXPECTED = 906; -static const errcode_t INVOICE_OFFER_INACTIVE = 907; -static const errcode_t INVOICE_NO_DESCRIPTION = 908; - -/* Errors from HSM crypto operations. */ -static const errcode_t HSM_ECDH_FAILED = 800; - -/* Errors from `offer` commands */ -static const errcode_t OFFER_ALREADY_EXISTS = 1000; -static const errcode_t OFFER_ALREADY_DISABLED = 1001; -static const errcode_t OFFER_EXPIRED = 1002; -static const errcode_t OFFER_ROUTE_NOT_FOUND = 1003; -static const errcode_t OFFER_BAD_INVREQ_REPLY = 1004; -static const errcode_t OFFER_TIMEOUT = 1005; - -/* Errors from datastore command */ -static const errcode_t DATASTORE_DEL_DOES_NOT_EXIST = 1200; -static const errcode_t DATASTORE_DEL_WRONG_GENERATION = 1201; -static const errcode_t DATASTORE_UPDATE_ALREADY_EXISTS = 1202; -static const errcode_t DATASTORE_UPDATE_DOES_NOT_EXIST = 1203; -static const errcode_t DATASTORE_UPDATE_WRONG_GENERATION = 1204; -static const errcode_t DATASTORE_UPDATE_HAS_CHILDREN = 1205; -static const errcode_t DATASTORE_UPDATE_NO_CHILDREN = 1206; - -/* Errors from signmessage command */ -static const errcode_t SIGNMESSAGE_PUBKEY_NOT_FOUND = 1301; - -/* Errors from wait* commands */ -static const errcode_t WAIT_TIMEOUT = 2000; +enum jsonrpc_errcode { + /* Standard errors defined by JSON-RPC 2.0 standard */ + JSONRPC2_INVALID_REQUEST = -32600, + JSONRPC2_METHOD_NOT_FOUND = -32601, + JSONRPC2_INVALID_PARAMS = -32602, + + /* Uncategorized error. + * FIXME: This should be replaced in all places + * with a specific error code, and then removed. + */ + LIGHTNINGD = -1, + + /* Developer error in the parameters to param() call */ + PARAM_DEV_ERROR = -2, + + /* Plugin returned an error */ + PLUGIN_ERROR = -3, + + /* Plugin terminated while handling a request. */ + PLUGIN_TERMINATED = -4, + + /* Lightningd is shutting down while handling a request. */ + LIGHTNINGD_SHUTDOWN = -5, + + /* Errors from `pay`, `sendpay`, or `waitsendpay` commands */ + PAY_IN_PROGRESS = 200, + PAY_RHASH_ALREADY_USED = 201, + PAY_UNPARSEABLE_ONION = 202, + PAY_DESTINATION_PERM_FAIL = 203, + PAY_TRY_OTHER_ROUTE = 204, + PAY_ROUTE_NOT_FOUND = 205, + PAY_ROUTE_TOO_EXPENSIVE = 206, + PAY_INVOICE_EXPIRED = 207, + PAY_NO_SUCH_PAYMENT = 208, + PAY_UNSPECIFIED_ERROR = 209, + PAY_STOPPED_RETRYING = 210, + PAY_STATUS_UNEXPECTED = 211, + PAY_OFFER_INVALID = 212, + + /* `fundchannel` or `withdraw` errors */ + FUND_MAX_EXCEEDED = 300, + FUND_CANNOT_AFFORD = 301, + FUND_OUTPUT_IS_DUST = 302, + FUNDING_BROADCAST_FAIL = 303, + FUNDING_STILL_SYNCING_BITCOIN = 304, + FUNDING_PEER_NOT_CONNECTED = 305, + FUNDING_UNKNOWN_PEER = 306, + FUNDING_NOTHING_TO_CANCEL = 307, + FUNDING_CANCEL_NOT_SAFE = 308, + FUNDING_PSBT_INVALID = 309, + FUNDING_V2_NOT_SUPPORTED = 310, + FUNDING_UNKNOWN_CHANNEL = 311, + FUNDING_STATE_INVALID = 312, + + /* `connect` errors */ + CONNECT_NO_KNOWN_ADDRESS = 400, + CONNECT_ALL_ADDRESSES_FAILED = 401, + CONNECT_DISCONNECTED_DURING = 402, + + /* bitcoin-cli plugin errors */ + BCLI_ERROR = 500, + + /* Errors from `invoice` or `delinvoice` commands */ + INVOICE_LABEL_ALREADY_EXISTS = 900, + INVOICE_PREIMAGE_ALREADY_EXISTS = 901, + INVOICE_HINTS_GAVE_NO_ROUTES = 902, + INVOICE_EXPIRED_DURING_WAIT = 903, + INVOICE_WAIT_TIMED_OUT = 904, + INVOICE_NOT_FOUND = 905, + INVOICE_STATUS_UNEXPECTED = 906, + INVOICE_OFFER_INACTIVE = 907, + INVOICE_NO_DESCRIPTION = 908, + + /* Errors from HSM crypto operations. */ + HSM_ECDH_FAILED = 800, + + /* Errors from `offer` commands */ + OFFER_ALREADY_EXISTS = 1000, + OFFER_ALREADY_DISABLED = 1001, + OFFER_EXPIRED = 1002, + OFFER_ROUTE_NOT_FOUND = 1003, + OFFER_BAD_INVREQ_REPLY = 1004, + OFFER_TIMEOUT = 1005, + + /* Errors from datastore command */ + DATASTORE_DEL_DOES_NOT_EXIST = 1200, + DATASTORE_DEL_WRONG_GENERATION = 1201, + DATASTORE_UPDATE_ALREADY_EXISTS = 1202, + DATASTORE_UPDATE_DOES_NOT_EXIST = 1203, + DATASTORE_UPDATE_WRONG_GENERATION = 1204, + DATASTORE_UPDATE_HAS_CHILDREN = 1205, + DATASTORE_UPDATE_NO_CHILDREN = 1206, + + /* Errors from signmessage command */ + SIGNMESSAGE_PUBKEY_NOT_FOUND = 1301, + + /* Errors from wait* commands */ + WAIT_TIMEOUT = 2000, +}; #endif /* LIGHTNING_COMMON_JSONRPC_ERRORS_H */ diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index e859dbcb429f..99ae0122a938 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -42,7 +42,7 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) bool command_check_only(const struct command *cmd UNNEEDED) { fprintf(stderr, "command_check_only called!\n"); abort(); } /* Generated stub for command_fail */ -struct command_result *command_fail(struct command *cmd UNNEEDED, errcode_t code UNNEEDED, +struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "command_fail called!\n"); abort(); } diff --git a/common/test/run-param.c b/common/test/run-param.c index 07870864970e..bd450aef3b2d 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -24,7 +24,7 @@ struct command_result { static struct command_result cmd_failed; struct command_result *command_fail(struct command *cmd, - errcode_t code, const char *fmt, ...) + enum jsonrpc_errcode code, const char *fmt, ...) { failed = true; va_list ap; diff --git a/connectd/connectd.c b/connectd/connectd.c index c57a1646df2c..a5fa736aadfa 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -614,14 +614,14 @@ struct io_plan *connection_out(struct io_conn *conn, struct connecting *connect) static void connect_failed(struct daemon *daemon, const struct node_id *id, const struct wireaddr_internal *addrhint, - errcode_t errcode, + enum jsonrpc_errcode errcode, const char *errfmt, ...) PRINTF_FMT(5,6); static void connect_failed(struct daemon *daemon, const struct node_id *id, const struct wireaddr_internal *addrhint, - errcode_t errcode, + enum jsonrpc_errcode errcode, const char *errfmt, ...) { u8 *msg; diff --git a/connectd/connectd_wire.csv b/connectd/connectd_wire.csv index 1dc10bb928a2..8a2ce09c40a1 100644 --- a/connectd/connectd_wire.csv +++ b/connectd/connectd_wire.csv @@ -54,7 +54,7 @@ msgdata,connectd_connect_to_peer,addrhint,?wireaddr_internal, # Connectd->master: connect failed. msgtype,connectd_connect_failed,2020 msgdata,connectd_connect_failed,id,node_id, -msgdata,connectd_connect_failed,failcode,errcode_t, +msgdata,connectd_connect_failed,failcode,enum jsonrpc_errcode, msgdata,connectd_connect_failed,failreason,wirestring, msgdata,connectd_connect_failed,addrhint,?wireaddr_internal, diff --git a/lightningd/connect_control.c b/lightningd/connect_control.c index 056ef0329d3f..61a6bcca856f 100644 --- a/lightningd/connect_control.c +++ b/lightningd/connect_control.c @@ -357,7 +357,7 @@ void try_reconnect(const tal_t *ctx, /* We were trying to connect, but they disconnected. */ static void connect_failed(struct lightningd *ld, const struct node_id *id, - errcode_t errcode, + enum jsonrpc_errcode errcode, const char *errmsg, const struct wireaddr_internal *addrhint) { @@ -390,7 +390,7 @@ void connect_failed_disconnect(struct lightningd *ld, static void handle_connect_failed(struct lightningd *ld, const u8 *msg) { struct node_id id; - errcode_t errcode; + enum jsonrpc_errcode errcode; char *errmsg; struct wireaddr_internal *addrhint; diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index a5524b203152..fbe74bb1ba93 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -475,7 +475,7 @@ struct command_result *command_failed(struct command *cmd, return command_raw_complete(cmd, result); } -struct command_result *command_fail(struct command *cmd, errcode_t code, +struct command_result *command_fail(struct command *cmd, enum jsonrpc_errcode code, const char *fmt, ...) { const char *errmsg; @@ -513,7 +513,7 @@ static void json_command_malformed(struct json_connection *jcon, json_add_string(js, "jsonrpc", "2.0"); json_add_primitive(js, "id", id); json_object_start(js, "error"); - json_add_errcode(js, "code", JSONRPC2_INVALID_REQUEST); + json_add_jsonrpc_errcode(js, "code", JSONRPC2_INVALID_REQUEST); json_add_string(js, "message", error); json_object_end(js); json_object_end(js); @@ -601,7 +601,7 @@ struct json_stream *json_stream_success(struct command *cmd) } struct json_stream *json_stream_fail_nodata(struct command *cmd, - errcode_t code, + enum jsonrpc_errcode code, const char *errmsg) { struct json_stream *js = json_start(cmd); @@ -609,14 +609,14 @@ struct json_stream *json_stream_fail_nodata(struct command *cmd, assert(code); json_object_start(js, "error"); - json_add_errcode(js, "code", code); + json_add_jsonrpc_errcode(js, "code", code); json_add_string(js, "message", errmsg); return js; } struct json_stream *json_stream_fail(struct command *cmd, - errcode_t code, + enum jsonrpc_errcode code, const char *errmsg) { struct json_stream *r = json_stream_fail_nodata(cmd, code, errmsg); @@ -824,11 +824,11 @@ rpc_command_hook_callback(struct rpc_command_hook_payload *p, custom_return = json_get_member(buffer, tok, "error"); if (custom_return) { - errcode_t code; + enum jsonrpc_errcode code; const char *errmsg; - if (!json_to_errcode(buffer, - json_get_member(buffer, custom_return, "code"), - &code)) { + if (!json_to_jsonrpc_errcode(buffer, + json_get_member(buffer, custom_return, "code"), + &code)) { error = "'error' object does not contain a code."; goto log_error_and_skip; } diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index 1964f1db45de..38bff3caedcf 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -103,7 +103,7 @@ struct json_stream *json_stream_success(struct command *cmd); * You need to json_object_end() once you're done! */ struct json_stream *json_stream_fail(struct command *cmd, - errcode_t code, + enum jsonrpc_errcode code, const char *errmsg); /** @@ -115,7 +115,7 @@ struct json_stream *json_stream_fail(struct command *cmd, * This is used by command_fail(), which doesn't add any JSON data. */ struct json_stream *json_stream_fail_nodata(struct command *cmd, - errcode_t code, + enum jsonrpc_errcode code, const char *errmsg); /* These returned values are never NULL. */ diff --git a/lightningd/notification.c b/lightningd/notification.c index 2935094f2ca2..a021a116cfde 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -392,7 +392,7 @@ void notify_sendpay_success(struct lightningd *ld, static void sendpay_failure_notification_serialize(struct json_stream *stream, const struct wallet_payment *payment, - errcode_t pay_errcode, + enum jsonrpc_errcode pay_errcode, const struct onionreply *onionreply, const struct routing_failure *fail, char *errmsg) @@ -401,7 +401,7 @@ static void sendpay_failure_notification_serialize(struct json_stream *stream, /* In line with the format of json error returned * by sendpay_fail(). */ - json_add_errcode(stream, "code", pay_errcode); + json_add_jsonrpc_errcode(stream, "code", pay_errcode); json_add_string(stream, "message", errmsg); json_object_start(stream, "data"); @@ -420,14 +420,14 @@ REGISTER_NOTIFICATION(sendpay_failure, void notify_sendpay_failure(struct lightningd *ld, const struct wallet_payment *payment, - errcode_t pay_errcode, + enum jsonrpc_errcode pay_errcode, const struct onionreply *onionreply, const struct routing_failure *fail, const char *errmsg) { void (*serialize)(struct json_stream *, const struct wallet_payment *, - errcode_t, + enum jsonrpc_errcode, const struct onionreply *, const struct routing_failure *, const char *) = sendpay_failure_notification_gen.serialize; diff --git a/lightningd/notification.h b/lightningd/notification.h index 7b1c624b0a33..d14df085f7e1 100644 --- a/lightningd/notification.h +++ b/lightningd/notification.h @@ -76,7 +76,7 @@ void notify_sendpay_success(struct lightningd *ld, void notify_sendpay_failure(struct lightningd *ld, const struct wallet_payment *payment, - errcode_t pay_errcode, + enum jsonrpc_errcode pay_errcode, const struct onionreply *onionreply, const struct routing_failure *fail, const char *errmsg); diff --git a/lightningd/pay.c b/lightningd/pay.c index 3966ab531ecf..ec2366802a28 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -205,7 +205,7 @@ json_add_routefail_info(struct json_stream *js, void json_sendpay_fail_fields(struct json_stream *js, const struct wallet_payment *payment, - errcode_t pay_errcode, + enum jsonrpc_errcode pay_errcode, const struct onionreply *onionreply, const struct routing_failure *fail) { @@ -224,7 +224,7 @@ void json_sendpay_fail_fields(struct json_stream *js, fail->msg); } -static const char *sendpay_errmsg_fmt(const tal_t *ctx, errcode_t pay_errcode, +static const char *sendpay_errmsg_fmt(const tal_t *ctx, enum jsonrpc_errcode pay_errcode, const struct routing_failure *fail, const char *details) { @@ -243,7 +243,7 @@ static const char *sendpay_errmsg_fmt(const tal_t *ctx, errcode_t pay_errcode, static struct command_result * sendpay_fail(struct command *cmd, const struct wallet_payment *payment, - errcode_t pay_errcode, + enum jsonrpc_errcode pay_errcode, const struct onionreply *onionreply, const struct routing_failure *fail, const char *errmsg) @@ -276,7 +276,7 @@ json_sendpay_in_progress(struct command *cmd, static void tell_waiters_failed(struct lightningd *ld, const struct sha256 *payment_hash, const struct wallet_payment *payment, - errcode_t pay_errcode, + enum jsonrpc_errcode pay_errcode, const struct onionreply *onionreply, const struct routing_failure *fail, const char *details) @@ -417,7 +417,7 @@ remote_routing_failure(const tal_t *ctx, const u8 *failuremsg, int origin_index, struct log *log, - errcode_t *pay_errcode) + enum jsonrpc_errcode *pay_errcode) { enum onion_wire failcode = fromwire_peektype(failuremsg); struct routing_failure *routing_failure; @@ -541,7 +541,7 @@ void payment_failed(struct lightningd *ld, const struct htlc_out *hout, struct wallet_payment *payment; struct routing_failure* fail = NULL; const char *failstr; - errcode_t pay_errcode; + enum jsonrpc_errcode pay_errcode; const u8 *failmsg; int origin_index; @@ -670,7 +670,7 @@ static struct command_result *wait_payment(struct lightningd *ld, char *faildetail; struct routing_failure *fail; int faildirection; - errcode_t rpcerrorcode; + enum jsonrpc_errcode rpcerrorcode; payment = wallet_payment_by_hash(tmpctx, ld->wallet, payment_hash, partid, groupid); diff --git a/lightningd/pay.h b/lightningd/pay.h index 7c933877418d..439a2bd7e448 100644 --- a/lightningd/pay.h +++ b/lightningd/pay.h @@ -29,7 +29,7 @@ void json_add_payment_fields(struct json_stream *response, /* This json will be also used in 'sendpay_failure' notifictaion. */ void json_sendpay_fail_fields(struct json_stream *js, const struct wallet_payment *t, - errcode_t pay_errcode, + enum jsonrpc_errcode pay_errcode, const struct onionreply *onionreply, const struct routing_failure *fail); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index fcf26cbbf741..95b27e1c32ce 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -124,7 +124,7 @@ void channel_update_reserve(struct channel *channel UNNEEDED, struct amount_sat funding_total UNNEEDED) { fprintf(stderr, "channel_update_reserve called!\n"); abort(); } /* Generated stub for command_fail */ -struct command_result *command_fail(struct command *cmd UNNEEDED, errcode_t code UNNEEDED, +struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "command_fail called!\n"); abort(); } @@ -455,7 +455,7 @@ const char *json_scan(const tal_t *ctx UNNEEDED, { fprintf(stderr, "json_scan called!\n"); abort(); } /* Generated stub for json_stream_fail */ struct json_stream *json_stream_fail(struct command *cmd UNNEEDED, - errcode_t code UNNEEDED, + enum jsonrpc_errcode code UNNEEDED, const char *errmsg UNNEEDED) { fprintf(stderr, "json_stream_fail called!\n"); abort(); } /* Generated stub for json_stream_success */ diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 6536ffad810b..8a0a3f9b6028 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -27,9 +27,10 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for json_to_errcode */ -bool json_to_errcode(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, errcode_t *errcode UNNEEDED) -{ fprintf(stderr, "json_to_errcode called!\n"); abort(); } +/* Generated stub for json_to_jsonrpc_errcode */ +bool json_to_jsonrpc_errcode(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + enum jsonrpc_errcode *errcode UNNEEDED) +{ fprintf(stderr, "json_to_jsonrpc_errcode called!\n"); abort(); } /* Generated stub for json_to_number */ bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, unsigned int *num UNNEEDED) diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c index f663a9f26414..12d5d1f723e8 100644 --- a/lightningd/test/run-log-pruning.c +++ b/lightningd/test/run-log-pruning.c @@ -4,7 +4,7 @@ /* AUTOGENERATED MOCKS START */ /* Generated stub for command_fail */ -struct command_result *command_fail(struct command *cmd UNNEEDED, errcode_t code UNNEEDED, +struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "command_fail called!\n"); abort(); } diff --git a/plugins/libplugin.c b/plugins/libplugin.c index c7b03989c44d..e39a507c1980 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -389,14 +389,14 @@ command_success(struct command *cmd, const struct json_out *result) } struct command_result *command_done_err(struct command *cmd, - errcode_t code, + enum jsonrpc_errcode code, const char *errmsg, const struct json_out *data) { struct json_stream *js = jsonrpc_stream_start(cmd); json_object_start(js, "error"); - json_add_errcode(js, "code", code); + json_add_jsonrpc_errcode(js, "code", code); json_add_string(js, "message", errmsg); if (data) @@ -442,7 +442,7 @@ struct command_result *forward_result(struct command *cmd, /* Called by param() directly if it's malformed. */ struct command_result *command_fail(struct command *cmd, - errcode_t code, const char *fmt, ...) + enum jsonrpc_errcode code, const char *fmt, ...) { va_list ap; struct command_result *res; diff --git a/plugins/libplugin.h b/plugins/libplugin.h index ba7c6b4ab8a6..4131f4567f79 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -239,7 +239,7 @@ void NORETURN plugin_exit(struct plugin *p, int exitcode); * NULL, data can be NULL; otherwise it must be a JSON object. */ struct command_result *WARN_UNUSED_RESULT command_done_err(struct command *cmd, - errcode_t code, + enum jsonrpc_errcode code, const char *errmsg, const struct json_out *data); diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index e67baed0eff7..ffb50b053294 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -69,7 +69,7 @@ void fail_destination_tok(struct multifundchannel_destination *dest, } void fail_destination_msg(struct multifundchannel_destination *dest, - errcode_t error_code, + enum jsonrpc_errcode error_code, const char *err_str TAKES) { dest->error_code = error_code; @@ -333,7 +333,7 @@ mfc_cleanup_(struct multifundchannel_command *mfc, struct mfc_fail_object { struct multifundchannel_command *mfc; struct command *cmd; - errcode_t code; + enum jsonrpc_errcode code; const char *msg; }; @@ -347,7 +347,7 @@ mfc_fail_complete(struct mfc_fail_object *obj) /* Use this instead of command_fail. */ static struct command_result * -mfc_fail(struct multifundchannel_command *mfc, errcode_t code, +mfc_fail(struct multifundchannel_command *mfc, enum jsonrpc_errcode code, const char *fmt, ...) { struct mfc_fail_object *obj; diff --git a/plugins/spender/multifundchannel.h b/plugins/spender/multifundchannel.h index e65c2785c8c7..f27b378da34c 100644 --- a/plugins/spender/multifundchannel.h +++ b/plugins/spender/multifundchannel.h @@ -56,7 +56,7 @@ struct multifundchannel_removed { */ const char *method; const char *error_message; - errcode_t error_code; + enum jsonrpc_errcode error_code; /* Optional JSON object containing extra data */ const char *error_data; }; @@ -128,7 +128,7 @@ struct multifundchannel_destination { struct channel_id channel_id; const char *error_message; - errcode_t error_code; + enum jsonrpc_errcode error_code; /* Optional JSON object containing extra data */ const char *error_data; diff --git a/plugins/spender/multiwithdraw.c b/plugins/spender/multiwithdraw.c index 9ab29bb9ef2c..9eb93c30e4bc 100644 --- a/plugins/spender/multiwithdraw.c +++ b/plugins/spender/multiwithdraw.c @@ -288,7 +288,7 @@ mw_forward_error(struct command *cmd UNUSED, } /* Use this instead of command_fail. */ static struct command_result * -mw_fail(struct multiwithdraw_command *mw, errcode_t code, +mw_fail(struct multiwithdraw_command *mw, enum jsonrpc_errcode code, const char *fmt, ...) { va_list ap; @@ -303,7 +303,7 @@ mw_fail(struct multiwithdraw_command *mw, errcode_t code, js = new_json_stream(tmpctx, mw->cmd, NULL); json_object_start(js, NULL); - json_add_errcode(js, "code", code); + json_add_jsonrpc_errcode(js, "code", code); json_add_string(js, "message", message); json_object_end(js); diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 19e658d936a5..24a2a1370801 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -194,7 +194,6 @@ class Type(FieldSet): 'bool', 'amount_sat', 'amount_msat', - 'errcode_t', 'bigsize', 'varint' ] @@ -209,7 +208,6 @@ class Type(FieldSet): 'secp256k1_ecdsa_recoverable_signature', 'utf8', 'wirestring', - 'errcode_t', 'bigsize', 'varint', ] diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 58d870a50507..090e371bad22 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -90,7 +90,7 @@ void channel_update_reserve(struct channel *channel UNNEEDED, struct amount_sat funding_total UNNEEDED) { fprintf(stderr, "channel_update_reserve called!\n"); abort(); } /* Generated stub for command_fail */ -struct command_result *command_fail(struct command *cmd UNNEEDED, errcode_t code UNNEEDED, +struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "command_fail called!\n"); abort(); } diff --git a/wire/fromwire.c b/wire/fromwire.c index e960f6e8e684..97909534db50 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -151,9 +151,9 @@ bool fromwire_bool(const u8 **cursor, size_t *max) return ret; } -errcode_t fromwire_errcode_t(const u8 **cursor, size_t *max) +enum jsonrpc_errcode fromwire_jsonrpc_errcode(const u8 **cursor, size_t *max) { - errcode_t ret; + enum jsonrpc_errcode ret; ret = (s32)fromwire_u32(cursor, max); diff --git a/wire/towire.c b/wire/towire.c index c4bfc64478c3..49eae1523b95 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -77,7 +77,7 @@ void towire_bool(u8 **pptr, bool v) towire(pptr, &val, sizeof(val)); } -void towire_errcode_t(u8 **pptr, errcode_t v) +void towire_jsonrpc_errcode(u8 **pptr, enum jsonrpc_errcode v) { towire_u32(pptr, (u32)v); } diff --git a/wire/wire.h b/wire/wire.h index 1b3d6502e0ad..3b07a021bea9 100644 --- a/wire/wire.h +++ b/wire/wire.h @@ -3,7 +3,7 @@ #include "config.h" #include #include -#include +#include #include #include #include @@ -36,7 +36,7 @@ void towire_tu32(u8 **pptr, u32 v); void towire_tu64(u8 **pptr, u64 v); void towire_pad(u8 **pptr, size_t num); void towire_bool(u8 **pptr, bool v); -void towire_errcode_t(u8 **pptr, errcode_t v); +void towire_jsonrpc_errcode(u8 **pptr, enum jsonrpc_errcode v); void towire_u8_array(u8 **pptr, const u8 *arr, size_t num); void towire_utf8_array(u8 **pptr, const char *arr, size_t num); @@ -53,7 +53,7 @@ u16 fromwire_tu16(const u8 **cursor, size_t *max); u32 fromwire_tu32(const u8 **cursor, size_t *max); u64 fromwire_tu64(const u8 **cursor, size_t *max); bool fromwire_bool(const u8 **cursor, size_t *max); -errcode_t fromwire_errcode_t(const u8 **cursor, size_t *max); +enum jsonrpc_errcode fromwire_jsonrpc_errcode(const u8 **cursor, size_t *max); void fromwire_secp256k1_ecdsa_signature(const u8 **cursor, size_t *max, secp256k1_ecdsa_signature *signature); void fromwire_secp256k1_ecdsa_recoverable_signature(const u8 **cursor, From 6a48ed9e826efed1ea53b18a8308f97c2d5bbe34 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 18 Sep 2022 09:51:50 +0930 Subject: [PATCH 1394/1530] gossmap: fail to get capacity of locally-added chans (don't crash!). Signed-off-by: Rusty Russell --- common/gossmap.c | 4 ++++ common/gossmap.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/common/gossmap.c b/common/gossmap.c index 17c5cbe885d4..7262f22594c5 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -967,6 +967,10 @@ bool gossmap_chan_get_capacity(const struct gossmap *map, size_t off; u16 type; + /* Fail for local channels */ + if (c->cann_off >= map->map_size) + return false; + /* For private, we need to go back WIRE_GOSSIP_STORE_PRIVATE_CHANNEL, * which is 8 (satoshis) + 2 (len) */ if (c->private) { diff --git a/common/gossmap.h b/common/gossmap.h index 728dc8385160..1f06dcb51af8 100644 --- a/common/gossmap.h +++ b/common/gossmap.h @@ -117,7 +117,7 @@ static inline bool gossmap_chan_set(const struct gossmap_chan *chan, int dir) return chan->cupdate_off[dir] != 0; } -/* Return capacity if it's known (fails only on race condition) */ +/* Return capacity if it's known (fails only on race condition, or a local mod) */ bool gossmap_chan_get_capacity(const struct gossmap *map, const struct gossmap_chan *c, struct amount_sat *amount); From 016e332304af6576d44124494c97c0e7c2a1a06c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 18 Sep 2022 09:52:50 +0930 Subject: [PATCH 1395/1530] gossmap: add functions to map index back to node/chan. Useful if you want to store per-chan or per-node side-data in an array. Signed-off-by: Rusty Russell --- common/gossmap.c | 17 +++++++++++++++++ common/gossmap.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/common/gossmap.c b/common/gossmap.c index 7262f22594c5..027fcbe21625 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -176,6 +176,23 @@ u32 gossmap_chan_idx(const struct gossmap *map, const struct gossmap_chan *chan) return chan - map->chan_arr; } +struct gossmap_node *gossmap_node_byidx(const struct gossmap *map, u32 idx) +{ + assert(idx < gossmap_max_node_idx(map)); + if (map->node_arr[idx].chan_idxs == NULL) + return NULL; + return &map->node_arr[idx]; +} + +struct gossmap_chan *gossmap_chan_byidx(const struct gossmap *map, u32 idx) +{ + assert(idx < gossmap_max_chan_idx(map)); + + if (map->chan_arr[idx].plus_scid_off == 0) + return NULL; + return &map->chan_arr[idx]; +} + /* htable can't handle NULL or 1 values, so we add 2 */ static struct gossmap_chan *ptrint2chan(const ptrint_t *pidx) { diff --git a/common/gossmap.h b/common/gossmap.h index 1f06dcb51af8..38cee0425cc3 100644 --- a/common/gossmap.h +++ b/common/gossmap.h @@ -90,6 +90,9 @@ void gossmap_remove_localmods(struct gossmap *map, u32 gossmap_node_idx(const struct gossmap *map, const struct gossmap_node *node); u32 gossmap_chan_idx(const struct gossmap *map, const struct gossmap_chan *chan); +struct gossmap_node *gossmap_node_byidx(const struct gossmap *map, u32 idx); +struct gossmap_chan *gossmap_chan_byidx(const struct gossmap *map, u32 idx); + /* Every node_idx/chan_idx will be < these. * These values can change across calls to gossmap_check. */ u32 gossmap_max_node_idx(const struct gossmap *map); From 4cdb4167d2e3c3052d42e1a38a0dfebef496d48a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 18 Sep 2022 09:53:50 +0930 Subject: [PATCH 1396/1530] gossmap: make local_addchan create private channel_announcement in correct order. Signed-off-by: Rusty Russell --- common/gossmap.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/gossmap.c b/common/gossmap.c index 027fcbe21625..55ea115dd267 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -771,6 +771,16 @@ bool gossmap_local_addchan(struct gossmap_localmods *localmods, if (find_localmod(localmods, scid)) return false; + /* BOLT #7: + * + * - MUST set `node_id_1` and `node_id_2` to the public keys + * of the two nodes operating the channel, such that + * `node_id_1` is the lexicographically-lesser of the two + * compressed keys sorted in ascending lexicographic order. + */ + if (node_id_cmp(n1, n2) > 0) + return gossmap_local_addchan(localmods, n2, n1, scid, features); + mod.scid = *scid; mod.updates_set[0] = mod.updates_set[1] = false; From fd71dfc7f79e71b5e3b8017dbdd11d469415bf7a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 18 Sep 2022 09:54:50 +0930 Subject: [PATCH 1397/1530] gossmap: optimize asserts(). They are surprisingly expensive! Running `time ./plugins/renepay/test/run-not_mcf-gossmap gossip_store-sgl.rustcorp.com.au-2022-04-19 024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605 02ebb3b8a2316b3e876ea3f3d8124a3ab97f30b128f619608eb06b5251235dc2d9 10000000000 0.1`: Before (-Og): real 0m1.495s Before (no opt): real 0m2.552s After (-Og): real 0m0.579s After (no opt): real 0m1.061s Signed-off-by: Rusty Russell --- common/gossmap.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/common/gossmap.c b/common/gossmap.c index 55ea115dd267..5d25b1122cac 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -66,9 +66,13 @@ struct gossmap { /* Array of nodes, so we can use simple index. */ struct gossmap_node *node_arr; + /* This is tal_count(node_arr), which we call very often in assert() */ + size_t num_node_arr; /* Array of chans, so we can use simple index */ struct gossmap_chan *chan_arr; + /* This is tal_count(chan_arr), which we call very often in assert() */ + size_t num_chan_arr; /* Linked list of freed ones, if any. */ u32 freed_nodes, freed_chans; @@ -155,24 +159,26 @@ static int map_feature_test(const struct gossmap *map, /* These values can change across calls to gossmap_check. */ u32 gossmap_max_node_idx(const struct gossmap *map) { - return tal_count(map->node_arr); + assert(tal_count(map->node_arr) == map->num_node_arr); + return map->num_node_arr; } u32 gossmap_max_chan_idx(const struct gossmap *map) { - return tal_count(map->chan_arr); + assert(tal_count(map->chan_arr) == map->num_chan_arr); + return map->num_chan_arr; } /* Each channel has a unique (low) index. */ u32 gossmap_node_idx(const struct gossmap *map, const struct gossmap_node *node) { - assert(node - map->node_arr < tal_count(map->node_arr)); + assert(node - map->node_arr < map->num_node_arr); return node - map->node_arr; } u32 gossmap_chan_idx(const struct gossmap *map, const struct gossmap_chan *chan) { - assert(chan - map->chan_arr < tal_count(map->chan_arr)); + assert(chan - map->chan_arr < map->num_chan_arr); return chan - map->chan_arr; } @@ -265,6 +271,7 @@ static struct gossmap_node *next_free_node(struct gossmap *map) if (map->freed_nodes == UINT_MAX) { /* Double in size, add second half to free list */ size_t n = tal_count(map->node_arr); + map->num_node_arr *= 2; tal_resize(&map->node_arr, n * 2); map->freed_nodes = init_node_arr(map->node_arr, n); } @@ -325,6 +332,7 @@ static struct gossmap_chan *next_free_chan(struct gossmap *map) if (map->freed_chans == UINT_MAX) { /* Double in size, add second half to free list */ size_t n = tal_count(map->chan_arr); + map->num_chan_arr *= 2; tal_resize(&map->chan_arr, n * 2); map->freed_chans = init_chan_arr(map->chan_arr, n); } @@ -680,9 +688,11 @@ static bool load_gossip_store(struct gossmap *map, size_t *num_rejected) chanidx_htable_init_sized(&map->channels, map->map_size / 750 / 2); nodeidx_htable_init_sized(&map->nodes, map->map_size / 2500 / 2); - map->chan_arr = tal_arr(map, struct gossmap_chan, map->map_size / 750 / 2 + 1); + map->num_chan_arr = map->map_size / 750 / 2 + 1; + map->chan_arr = tal_arr(map, struct gossmap_chan, map->num_chan_arr); map->freed_chans = init_chan_arr(map->chan_arr, 0); - map->node_arr = tal_arr(map, struct gossmap_node, map->map_size / 2500 / 2 + 1); + map->num_node_arr = map->map_size / 2500 / 2 + 1; + map->node_arr = tal_arr(map, struct gossmap_node, map->num_node_arr); map->freed_nodes = init_node_arr(map->node_arr, 0); map->map_end = 1; @@ -1031,7 +1041,7 @@ struct gossmap_chan *gossmap_nth_chan(const struct gossmap *map, struct gossmap_chan *chan; assert(n < node->num_chans); - assert(node->chan_idxs[n] < tal_count(map->chan_arr)); + assert(node->chan_idxs[n] < map->num_chan_arr); chan = map->chan_arr + node->chan_idxs[n]; if (which_half) { @@ -1061,7 +1071,7 @@ size_t gossmap_num_nodes(const struct gossmap *map) static struct gossmap_node *node_iter(const struct gossmap *map, size_t start) { - for (size_t i = start; i < tal_count(map->node_arr); i++) { + for (size_t i = start; i < map->num_node_arr; i++) { if (map->node_arr[i].chan_idxs != NULL) return &map->node_arr[i]; } @@ -1086,7 +1096,7 @@ size_t gossmap_num_chans(const struct gossmap *map) static struct gossmap_chan *chan_iter(const struct gossmap *map, size_t start) { - for (size_t i = start; i < tal_count(map->chan_arr); i++) { + for (size_t i = start; i < map->num_chan_arr; i++) { if (map->chan_arr[i].plus_scid_off != 0) return &map->chan_arr[i]; } From 3380f559f9cca7f6085c3b01af6a74fe1eac04a4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 16 Sep 2022 12:44:39 +0930 Subject: [PATCH 1398/1530] memleak: simplify API. Mainly renaming. Signed-off-by: Rusty Russell --- channeld/channeld.c | 4 +- channeld/full_channel.c | 2 +- channeld/test/run-full_channel.c | 6 +- closingd/closingd.c | 12 ++- common/memleak.c | 54 +++++++------ common/memleak.h | 76 ++++++++++++------- connectd/connectd.c | 8 +- gossipd/gossipd.c | 4 +- gossipd/routing.c | 10 +-- gossipd/test/run-check_channel_announcement.c | 12 +-- gossipd/test/run-txout_failure.c | 12 +-- hsmd/hsmd.c | 16 ++-- lightningd/jsonrpc.c | 2 +- lightningd/lightningd.c | 2 +- lightningd/memdump.c | 14 ++-- lightningd/plugin.c | 2 +- onchaind/onchaind.c | 19 ++--- onchaind/test/run-grind_feerate-bug.c | 31 ++++---- onchaind/test/run-grind_feerate.c | 20 ++--- openingd/dualopend.c | 4 +- openingd/openingd.c | 4 +- plugins/bcli.c | 2 +- plugins/commando.c | 10 +-- plugins/funder.c | 4 +- plugins/libplugin.c | 8 +- plugins/pay.c | 2 +- plugins/topology.c | 3 +- plugins/txprepare.c | 2 +- tests/plugins/test_libplugin.c | 2 +- 29 files changed, 186 insertions(+), 161 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index f701d24aa6f0..6fd12783901d 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -3652,10 +3652,10 @@ static void handle_dev_memleak(struct peer *peer, const u8 *msg) struct htable *memtable; bool found_leak; - memtable = memleak_find_allocations(tmpctx, msg, msg); + memtable = memleak_start(tmpctx, msg, msg); /* Now delete peer and things it has pointers to. */ - memleak_remove_region(memtable, peer, tal_bytelen(peer)); + memleak_scan_obj(memtable, peer); found_leak = dump_memleak(memtable, memleak_status_broken); wire_sync_write(MASTER_FD, diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 05448eb25b2c..c991315cd407 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -22,7 +22,7 @@ static void memleak_help_htlcmap(struct htable *memtable, struct htlc_map *htlcs) { - memleak_remove_htable(memtable, &htlcs->raw); + memleak_scan_htable(memtable, &htlcs->raw); } #endif /* DEVELOPER */ diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index 4992d9f9ce54..edc762505926 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -19,9 +19,9 @@ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct n /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, const tal_t *)){ } -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } +/* Generated stub for memleak_scan_htable */ +void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) +{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } /* Generated stub for status_failed */ void status_failed(enum status_failreason code UNNEEDED, const char *fmt UNNEEDED, ...) diff --git a/closingd/closingd.c b/closingd/closingd.c index 73b22ab34343..d3f0623a378a 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -554,14 +554,12 @@ static void closing_dev_memleak(const tal_t *ctx, u8 *scriptpubkey[NUM_SIDES], const u8 *funding_wscript) { - struct htable *memtable; + struct htable *memtable = memleak_start(tmpctx, NULL, NULL); - memtable = memleak_find_allocations(tmpctx, NULL, NULL); - - memleak_remove_pointer(memtable, ctx); - memleak_remove_pointer(memtable, scriptpubkey[LOCAL]); - memleak_remove_pointer(memtable, scriptpubkey[REMOTE]); - memleak_remove_pointer(memtable, funding_wscript); + memleak_ptr(memtable, ctx); + memleak_ptr(memtable, scriptpubkey[LOCAL]); + memleak_ptr(memtable, scriptpubkey[REMOTE]); + memleak_ptr(memtable, funding_wscript); dump_memleak(memtable, memleak_status_broken); } diff --git a/common/memleak.c b/common/memleak.c index 8f03324808ab..2ad2411e3fb1 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +66,7 @@ static size_t hash_ptr(const void *elem, void *unused UNNEEDED) return siphash24(&seed, &elem, sizeof(elem)); } -static bool pointer_referenced(struct htable *memtable, const void *p) +bool memleak_ptr(struct htable *memtable, const void *p) { return htable_del(memtable, hash_ptr(p, NULL), p); } @@ -122,45 +123,54 @@ static void scan_for_pointers(struct htable *memtable, void *ptr; memcpy(&ptr, (char *)p + i * sizeof(void *), sizeof(ptr)); - if (pointer_referenced(memtable, ptr)) + if (memleak_ptr(memtable, ptr)) scan_for_pointers(memtable, ptr, tal_bytelen(ptr)); } } -void memleak_remove_region(struct htable *memtable, - const void *ptr, size_t bytelen) +void memleak_scan_region(struct htable *memtable, const void *ptr, size_t len) { - pointer_referenced(memtable, ptr); - scan_for_pointers(memtable, ptr, bytelen); + scan_for_pointers(memtable, ptr, len); +} + +void memleak_scan_obj(struct htable *memtable, const void *ptr) +{ + memleak_ptr(memtable, ptr); + scan_for_pointers(memtable, ptr, tal_bytelen(ptr)); +} + +void memleak_scan_list_head(struct htable *memtable, const struct list_head *l) +{ + scan_for_pointers(memtable, l, sizeof(*l)); } static void remove_with_children(struct htable *memtable, const tal_t *p) { const tal_t *i; - pointer_referenced(memtable, p); + memleak_ptr(memtable, p); for (i = tal_first(p); i; i = tal_next(i)) remove_with_children(memtable, i); } /* memleak can't see inside hash tables, so do them manually */ -void memleak_remove_htable(struct htable *memtable, const struct htable *ht) +void memleak_scan_htable(struct htable *memtable, const struct htable *ht) { struct htable_iter i; const void *p; for (p = htable_first(ht, &i); p; p = htable_next(ht, &i)) - memleak_remove_region(memtable, p, tal_bytelen(p)); + memleak_scan_obj(memtable, p); } /* FIXME: If uintmap used tal, this wouldn't be necessary! */ -void memleak_remove_intmap_(struct htable *memtable, const struct intmap *m) +void memleak_scan_intmap_(struct htable *memtable, const struct intmap *m) { void *p; intmap_index_t i; for (p = intmap_first_(m, &i); p; p = intmap_after_(m, &i)) - memleak_remove_region(memtable, p, tal_bytelen(p)); + memleak_scan_obj(memtable, p); } static bool handle_strmap(const char *member, void *p, void *memtable_) @@ -168,15 +178,15 @@ static bool handle_strmap(const char *member, void *p, void *memtable_) struct htable *memtable = memtable_; /* membername may *not* be a tal ptr, but it can be! */ - pointer_referenced(memtable, member); - memleak_remove_region(memtable, p, tal_bytelen(p)); + memleak_ptr(memtable, member); + memleak_scan_obj(memtable, p); /* Keep going */ return true; } /* FIXME: If strmap used tal, this wouldn't be necessary! */ -void memleak_remove_strmap_(struct htable *memtable, const struct strmap *m) +void memleak_scan_strmap_(struct htable *memtable, const struct strmap *m) { strmap_iterate_(m, handle_strmap, memtable); } @@ -192,7 +202,7 @@ const void *memleak_get(struct htable *memtable, const uintptr_t **backtrace) const tal_t *i, *p; /* Remove memtable itself */ - pointer_referenced(memtable, memtable); + memleak_ptr(memtable, memtable); i = htable_first(memtable, &it); if (!i) @@ -273,14 +283,12 @@ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) mh->cb(memtable, p); } else if (strends(name, " **NOTLEAK**") || strends(name, "_notleak")) { - pointer_referenced(memtable, i); - memleak_remove_region(memtable, i, - tal_bytelen(i)); + memleak_ptr(memtable, i); + memleak_scan_obj(memtable, i); } else if (strends(name, " **NOTLEAK_IGNORE_CHILDREN**")) { remove_with_children(memtable, i); - memleak_remove_region(memtable, i, - tal_bytelen(i)); + memleak_scan_obj(memtable, i); } } @@ -289,9 +297,9 @@ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) } } -struct htable *memleak_find_allocations(const tal_t *ctx, - const void *exclude1, - const void *exclude2) +struct htable *memleak_start(const tal_t *ctx, + const void *exclude1, + const void *exclude2) { struct htable *memtable = tal(ctx, struct htable); htable_init(memtable, hash_ptr, NULL); diff --git a/common/memleak.h b/common/memleak.h index aa72ba5812cc..c06014ea05f8 100644 --- a/common/memleak.h +++ b/common/memleak.h @@ -6,6 +6,7 @@ #include struct htable; +struct list_head; /** * memleak_init: Initialize memleak detection; you call this at the start! @@ -64,54 +65,71 @@ void memleak_add_helper_(const tal_t *p, void (*cb)(struct htable *memtable, const tal_t *)); /** - * memleak_find_allocations: allocate a htable with all tal objects; + * memleak_start: allocate a htable with all tal objects * @ctx: the context to allocate the htable from * @exclude1: one tal pointer to exclude from adding (if non-NULL) * @exclude2: second tal pointer to exclude from adding (if non-NULL) * * Note that exclude1 and exclude2's tal children are also not added. */ -struct htable *memleak_find_allocations(const tal_t *ctx, - const void *exclude1, - const void *exclude2); +struct htable *memleak_start(const tal_t *ctx, + const void *exclude1, + const void *exclude2); /** - * memleak_remove_region - remove this region and anything it references - * @memtable: the memtable create by memleak_find_allocations. - * @p: the pointer to remove. - * @bytelen: the bytes within it to scan for more pointers. + * memleak_ptr: this pointer is not a memleak. + * @memtable: the memtable create by memleak_start. + * @p: the pointer. * - * This removes @p from the memtable, then looks for any tal pointers - * inside between @p and @p + @bytelen and calls - * memleak_remove_region() on those if not already removed. + * This tells memleak that @p (a tal allocation) is not a leak. Returns + * true if it was in the memleak table (it will no longer be). */ -void memleak_remove_region(struct htable *memtable, - const void *p, size_t bytelen); +bool memleak_ptr(struct htable *memtable, const void *p); /** - * memleak_remove_pointer - remove this pointer - * @memtable: the memtable create by memleak_find_allocations. - * @p: the pointer to remove. + * memleak_scan_obj - this tal object and anything it references are not leaks. + * @memtable: the memtable create by memleak_start. + * @obj: the tal pointer * - * This removes @p from the memtable. + * This removes @obj from the memtable, then looks for any tal pointers + * inside @obj and calls memleak_scan_obj() on those if not already removed. */ -#define memleak_remove_pointer(memtable, p) \ - memleak_remove_region((memtable), (p), 0) +void memleak_scan_obj(struct htable *memtable, const void *obj); -/* Helper to remove objects inside this htable (which is opaque to memleak). */ -void memleak_remove_htable(struct htable *memtable, const struct htable *ht); +/** + * memleak_scan_list_head - this list is not a leak. + * @memtable: the memtable create by memleak_start. + * @l: the list_head pointer + * + * This removes @l from the memtable, and any elements in the list. Usually + * used for file-scope linked lists. + */ +void memleak_scan_list_head(struct htable *memtable, const struct list_head *l); + +/** + * memleak_scan_region - scan a non-tal allocation for references. + * @memtable: the memtable create by memleak_start. + * @p: the tal pointer + * @len: the length in bytes. + * + * Sometimes we have a stack or file-scope object which contains pointers. + */ +void memleak_scan_region(struct htable *memtable, const void *p, size_t len); + +/* Objects inside this htable (which is opaque to memleak) are not leaks. */ +void memleak_scan_htable(struct htable *memtable, const struct htable *ht); -/* Helper to remove objects inside this uintmap (which is opaque to memleak). */ -#define memleak_remove_uintmap(memtable, umap) \ - memleak_remove_intmap_(memtable, uintmap_unwrap_(umap)) +/* Objects inside this uintmap (which is opaque to memleak) are not leaks. */ +#define memleak_scan_uintmap(memtable, umap) \ + memleak_scan_intmap_(memtable, uintmap_unwrap_(umap)) struct intmap; -void memleak_remove_intmap_(struct htable *memtable, const struct intmap *m); +void memleak_scan_intmap_(struct htable *memtable, const struct intmap *m); -/* Remove any pointers inside this strmap (which is opaque to memleak). */ -#define memleak_remove_strmap(memtable, strmap) \ - memleak_remove_strmap_((memtable), tcon_unwrap(strmap)) -void memleak_remove_strmap_(struct htable *memtable, const struct strmap *m); +/* Objects inside this strmap (which is opaque to memleak) are not leaks. */ +#define memleak_scan_strmap(memtable, strmap) \ + memleak_scan_strmap_((memtable), tcon_unwrap(strmap)) +void memleak_scan_strmap_(struct htable *memtable, const struct strmap *m); /** * memleak_get: get (and remove) a leak from memtable, or NULL diff --git a/connectd/connectd.c b/connectd/connectd.c index a5fa736aadfa..49bd268a189a 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1860,11 +1860,11 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) struct htable *memtable; bool found_leak; - memtable = memleak_find_allocations(tmpctx, msg, msg); + memtable = memleak_start(tmpctx, msg, msg); /* Now delete daemon and those which it has pointers to. */ - memleak_remove_region(memtable, daemon, sizeof(daemon)); - memleak_remove_htable(memtable, &daemon->peers.raw); + memleak_scan_obj(memtable, daemon); + memleak_scan_htable(memtable, &daemon->peers.raw); found_leak = dump_memleak(memtable, memleak_status_broken); daemon_conn_send(daemon->master, @@ -2003,7 +2003,7 @@ static struct io_plan *recv_gossip(struct io_conn *conn, #if DEVELOPER static void memleak_daemon_cb(struct htable *memtable, struct daemon *daemon) { - memleak_remove_htable(memtable, &daemon->peers.raw); + memleak_scan_htable(memtable, &daemon->peers.raw); } #endif /* DEVELOPER */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 224db2220780..9cbbfc21dda8 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -807,10 +807,10 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) struct htable *memtable; bool found_leak; - memtable = memleak_find_allocations(tmpctx, msg, msg); + memtable = memleak_start(tmpctx, msg, msg); /* Now delete daemon and those which it has pointers to. */ - memleak_remove_region(memtable, daemon, sizeof(*daemon)); + memleak_scan_obj(memtable, daemon); found_leak = dump_memleak(memtable, memleak_status_broken); daemon_conn_send(daemon->master, diff --git a/gossipd/routing.c b/gossipd/routing.c index 253fffd06ea6..d9dcdf72b61f 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -222,16 +222,16 @@ static void memleak_help_routing_tables(struct htable *memtable, struct node *n; struct node_map_iter nit; - memleak_remove_htable(memtable, &rstate->nodes->raw); - memleak_remove_htable(memtable, &rstate->pending_node_map->raw); - memleak_remove_htable(memtable, &rstate->pending_cannouncements.raw); - memleak_remove_uintmap(memtable, &rstate->unupdated_chanmap); + memleak_scan_htable(memtable, &rstate->nodes->raw); + memleak_scan_htable(memtable, &rstate->pending_node_map->raw); + memleak_scan_htable(memtable, &rstate->pending_cannouncements.raw); + memleak_scan_uintmap(memtable, &rstate->unupdated_chanmap); for (n = node_map_first(rstate->nodes, &nit); n; n = node_map_next(rstate->nodes, &nit)) { if (node_uses_chan_map(n)) - memleak_remove_htable(memtable, &n->chans.map.raw); + memleak_scan_htable(memtable, &n->chans.map.raw); } } #endif /* DEVELOPER */ diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index d095f22b4581..fc8f88884d0f 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -95,12 +95,12 @@ struct gossip_store *gossip_store_new(struct routing_state *rstate UNNEEDED, /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, const tal_t *)){ } -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } +/* Generated stub for memleak_scan_htable */ +void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) +{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } +/* Generated stub for memleak_scan_intmap_ */ +void memleak_scan_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) +{ fprintf(stderr, "memleak_scan_intmap_ called!\n"); abort(); } /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 239cd6c9eb56..2307c1d7deed 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -62,12 +62,12 @@ void gossip_store_mark_channel_deleted(struct gossip_store *gs UNNEEDED, /* Generated stub for memleak_add_helper_ */ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED, const tal_t *)){ } -/* Generated stub for memleak_remove_htable */ -void memleak_remove_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) -{ fprintf(stderr, "memleak_remove_htable called!\n"); abort(); } -/* Generated stub for memleak_remove_intmap_ */ -void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) -{ fprintf(stderr, "memleak_remove_intmap_ called!\n"); abort(); } +/* Generated stub for memleak_scan_htable */ +void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED) +{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); } +/* Generated stub for memleak_scan_intmap_ */ +void memleak_scan_intmap_(struct htable *memtable UNNEEDED, const struct intmap *m UNNEEDED) +{ fprintf(stderr, "memleak_scan_intmap_ called!\n"); abort(); } /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 85875bf30e44..aae54bedda1d 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -555,17 +555,15 @@ static struct io_plan *handle_memleak(struct io_conn *conn, bool found_leak; u8 *reply; - memtable = memleak_find_allocations(tmpctx, msg_in, msg_in); + memtable = memleak_start(tmpctx, msg_in, msg_in); - /* Now delete clients and anything they point to. */ - memleak_remove_region(memtable, - dbid_zero_clients, sizeof(dbid_zero_clients)); - memleak_remove_uintmap(memtable, &clients); - memleak_remove_region(memtable, - status_conn, tal_bytelen(status_conn)); + /* Now note clients and anything they point to. */ + memleak_scan_region(memtable, dbid_zero_clients, sizeof(dbid_zero_clients)); + memleak_scan_uintmap(memtable, &clients); + memleak_scan_obj(memtable, status_conn); - memleak_remove_pointer(memtable, dev_force_privkey); - memleak_remove_pointer(memtable, dev_force_bip32_seed); + memleak_ptr(memtable, dev_force_privkey); + memleak_ptr(memtable, dev_force_bip32_seed); found_leak = dump_memleak(memtable, memleak_status_broken); reply = towire_hsmd_dev_memleak_reply(NULL, found_leak); diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index fbe74bb1ba93..58a1d6011d51 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -1187,7 +1187,7 @@ static void destroy_jsonrpc(struct jsonrpc *jsonrpc) static void memleak_help_jsonrpc(struct htable *memtable, struct jsonrpc *jsonrpc) { - memleak_remove_strmap(memtable, &jsonrpc->usagemap); + memleak_scan_strmap(memtable, &jsonrpc->usagemap); } #endif /* DEVELOPER */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 29d5da76138a..7aaf916877a7 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -335,7 +335,7 @@ static void destroy_alt_subdaemons(struct lightningd *ld) static void memleak_help_alt_subdaemons(struct htable *memtable, struct lightningd *ld) { - memleak_remove_strmap(memtable, &ld->alt_subdaemons); + memleak_scan_strmap(memtable, &ld->alt_subdaemons); } #endif /* DEVELOPER */ diff --git a/lightningd/memdump.c b/lightningd/memdump.c index 8adb83b51857..f279aca8e746 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -143,17 +143,17 @@ static void finish_report(const struct leak_detect *leaks) ld = cmd->ld; /* Enter everything, except this cmd and its jcon */ - memtable = memleak_find_allocations(cmd, cmd, cmd->jcon); + memtable = memleak_start(cmd, cmd, cmd->jcon); /* First delete known false positives. */ - memleak_remove_htable(memtable, &ld->topology->txwatches.raw); - memleak_remove_htable(memtable, &ld->topology->txowatches.raw); - memleak_remove_htable(memtable, &ld->htlcs_in.raw); - memleak_remove_htable(memtable, &ld->htlcs_out.raw); - memleak_remove_htable(memtable, &ld->htlc_sets.raw); + memleak_scan_htable(memtable, &ld->topology->txwatches.raw); + memleak_scan_htable(memtable, &ld->topology->txowatches.raw); + memleak_scan_htable(memtable, &ld->htlcs_in.raw); + memleak_scan_htable(memtable, &ld->htlcs_out.raw); + memleak_scan_htable(memtable, &ld->htlc_sets.raw); /* Now delete ld and those which it has pointers to. */ - memleak_remove_region(memtable, ld, sizeof(*ld)); + memleak_scan_obj(memtable, ld); response = json_stream_success(cmd); json_array_start(response, "leaks"); diff --git a/lightningd/plugin.c b/lightningd/plugin.c index 2e006d5b4a95..b569d28f360e 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -48,7 +48,7 @@ struct plugin_rpccall { static void memleak_help_pending_requests(struct htable *memtable, struct plugins *plugins) { - memleak_remove_strmap(memtable, &plugins->pending_requests); + memleak_scan_strmap(memtable, &plugins->pending_requests); } #endif /* DEVELOPER */ diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 4a15b841be05..3f75dac9f32b 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -2085,15 +2085,12 @@ static void handle_preimage(struct tracked_output **outs, #if DEVELOPER static void memleak_remove_globals(struct htable *memtable, const tal_t *topctx) { - if (keyset) - memleak_remove_region(memtable, keyset, sizeof(*keyset)); - memleak_remove_pointer(memtable, remote_per_commitment_point); - memleak_remove_pointer(memtable, remote_per_commitment_secret); - memleak_remove_pointer(memtable, topctx); - memleak_remove_region(memtable, - missing_htlc_msgs, tal_bytelen(missing_htlc_msgs)); - memleak_remove_region(memtable, - queued_msgs, tal_bytelen(queued_msgs)); + memleak_scan_obj(memtable, keyset); + memleak_ptr(memtable, remote_per_commitment_point); + memleak_ptr(memtable, remote_per_commitment_secret); + memleak_ptr(memtable, topctx); + memleak_scan_obj(memtable, missing_htlc_msgs); + memleak_scan_obj(memtable, queued_msgs); } static bool handle_dev_memleak(struct tracked_output **outs, const u8 *msg) @@ -2104,10 +2101,10 @@ static bool handle_dev_memleak(struct tracked_output **outs, const u8 *msg) if (!fromwire_onchaind_dev_memleak(msg)) return false; - memtable = memleak_find_allocations(tmpctx, msg, msg); + memtable = memleak_start(tmpctx, msg, msg); /* Top-level context is parent of outs */ memleak_remove_globals(memtable, tal_parent(outs)); - memleak_remove_region(memtable, outs, tal_bytelen(outs)); + memleak_scan_obj(memtable, outs); found_leak = dump_memleak(memtable, memleak_status_broken); wire_sync_write(REQ_FD, diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index b7a1a215caf9..8423e972e027 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -310,23 +310,28 @@ void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNE /* AUTOGENERATED MOCKS END */ #if DEVELOPER -/* Generated stub for memleak_find_allocations */ -struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, - const void *exclude1 UNNEEDED, - const void *exclude2 UNNEEDED) -{ fprintf(stderr, "memleak_find_allocations called!\n"); abort(); } -/* Generated stub for memleak_remove_region */ -void memleak_remove_region(struct htable *memtable UNNEEDED, - const void *p UNNEEDED, size_t bytelen UNNEEDED) -{ fprintf(stderr, "memleak_remove_region called!\n"); abort(); } -/* Generated stub for memleak_status_broken */ -void memleak_status_broken(const char *fmt UNNEEDED, ...) -{ fprintf(stderr, "memleak_status_broken called!\n"); abort(); } +/* Generated stub for memleak_ptr */ +bool memleak_ptr(struct htable *memtable UNNEEDED, const void *p UNNEEDED) +{ fprintf(stderr, "memleak_ptr called!\n"); abort(); } /* Generated stub for dump_memleak */ bool dump_memleak(struct htable *memtable UNNEEDED, void (*print)(const char *fmt UNNEEDED, ...)) { fprintf(stderr, "dump_memleak called!\n"); abort(); } -#endif +/* Generated stub for memleak_scan_obj */ +void memleak_scan_obj(struct htable *memtable UNNEEDED, const void *obj UNNEEDED) +{ fprintf(stderr, "memleak_scan_obj called!\n"); abort(); } +/* Generated stub for memleak_scan_region */ +void memleak_scan_region(struct htable *memtable UNNEEDED, const void *p UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "memleak_scan_region called!\n"); abort(); } +/* Generated stub for memleak_start */ +struct htable *memleak_start(const tal_t *ctx UNNEEDED, + const void *exclude1 UNNEEDED, + const void *exclude2 UNNEEDED) +{ fprintf(stderr, "memleak_start called!\n"); abort(); } +/* Generated stub for memleak_status_broken */ +void memleak_status_broken(const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "memleak_status_broken called!\n"); abort(); } +#endif /* DEVELOPER */ /* Stubs which do get called. */ u8 *towire_hsmd_sign_local_htlc_tx(const tal_t *ctx UNNEEDED, u64 commit_num UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, bool option_anchor_outputs UNNEEDED) diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index e88f79bd5f3f..7e59d8fb25b9 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -127,15 +127,14 @@ struct bitcoin_tx *htlc_timeout_tx(const tal_t *ctx UNNEEDED, /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } -/* Generated stub for memleak_find_allocations */ -struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, - const void *exclude1 UNNEEDED, - const void *exclude2 UNNEEDED) -{ fprintf(stderr, "memleak_find_allocations called!\n"); abort(); } -/* Generated stub for memleak_remove_region */ -void memleak_remove_region(struct htable *memtable UNNEEDED, - const void *p UNNEEDED, size_t bytelen UNNEEDED) -{ fprintf(stderr, "memleak_remove_region called!\n"); abort(); } +/* Generated stub for memleak_scan_obj */ +void memleak_scan_obj(struct htable *memtable UNNEEDED, const void *obj UNNEEDED) +{ fprintf(stderr, "memleak_scan_obj called!\n"); abort(); } +/* Generated stub for memleak_start */ +struct htable *memleak_start(const tal_t *ctx UNNEEDED, + const void *exclude1 UNNEEDED, + const void *exclude2 UNNEEDED) +{ fprintf(stderr, "memleak_start called!\n"); abort(); } /* Generated stub for new_coin_channel_close */ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED, @@ -345,6 +344,9 @@ bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) /* AUTOGENERATED MOCKS END */ #if DEVELOPER +/* Generated stub for memleak_ptr */ +bool memleak_ptr(struct htable *memtable UNNEEDED, const void *p UNNEEDED) +{ fprintf(stderr, "memleak_ptr called!\n"); abort(); } /* Generated stub for memleak_status_broken */ void memleak_status_broken(const char *fmt UNNEEDED, ...) { fprintf(stderr, "memleak_status_broken called!\n"); abort(); } diff --git a/openingd/dualopend.c b/openingd/dualopend.c index e215cca188d5..79a2b29f0141 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -890,10 +890,10 @@ static void handle_dev_memleak(struct state *state, const u8 *msg) /* Populate a hash table with all our allocations (except msg, which * is in use right now). */ - memtable = memleak_find_allocations(tmpctx, msg, msg); + memtable = memleak_start(tmpctx, msg, msg); /* Now delete state and things it has pointers to. */ - memleak_remove_region(memtable, state, tal_bytelen(state)); + memleak_scan_obj(memtable, state); /* If there's anything left, dump it to logs, and return true. */ found_leak = dump_memleak(memtable, memleak_status_broken); diff --git a/openingd/openingd.c b/openingd/openingd.c index a7bd167f5937..27a84737d61a 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1301,10 +1301,10 @@ static void handle_dev_memleak(struct state *state, const u8 *msg) /* Populate a hash table with all our allocations (except msg, which * is in use right now). */ - memtable = memleak_find_allocations(tmpctx, msg, msg); + memtable = memleak_start(tmpctx, msg, msg); /* Now delete state and things it has pointers to. */ - memleak_remove_region(memtable, state, sizeof(*state)); + memleak_scan_obj(memtable, state); /* If there's anything left, dump it to logs, and return true. */ found_leak = dump_memleak(memtable, memleak_status_broken); diff --git a/plugins/bcli.c b/plugins/bcli.c index 7946dde76b0e..b5c6a9b80934 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -927,7 +927,7 @@ static void wait_and_check_bitcoind(struct plugin *p) #if DEVELOPER static void memleak_mark_bitcoind(struct plugin *p, struct htable *memtable) { - memleak_remove_region(memtable, bitcoind, sizeof(*bitcoind)); + memleak_scan_obj(memtable, bitcoind); } #endif diff --git a/plugins/commando.c b/plugins/commando.c index 820d31ac2b38..ae770e37609f 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -920,12 +920,12 @@ static struct command_result *json_commando_rune(struct command *cmd, #if DEVELOPER static void memleak_mark_globals(struct plugin *p, struct htable *memtable) { - memleak_remove_region(memtable, outgoing_commands, tal_bytelen(outgoing_commands)); - memleak_remove_region(memtable, incoming_commands, tal_bytelen(incoming_commands)); - memleak_remove_region(memtable, master_rune, sizeof(*master_rune)); - memleak_remove_htable(memtable, &usage_table.raw); + memleak_scan_obj(memtable, outgoing_commands); + memleak_scan_obj(memtable, incoming_commands); + memleak_scan_obj(memtable, master_rune); + memleak_scan_htable(memtable, &usage_table.raw); if (rune_counter) - memleak_remove_region(memtable, rune_counter, sizeof(*rune_counter)); + memleak_scan_obj(memtable, rune_counter); } #endif diff --git a/plugins/funder.c b/plugins/funder.c index e4c1e0a1e4d4..6575f22022ea 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -1084,8 +1084,8 @@ static void tell_lightningd_lease_rates(struct plugin *p, #if DEVELOPER static void memleak_mark(struct plugin *p, struct htable *memtable) { - memleak_remove_region(memtable, &pending_opens, sizeof(pending_opens)); - memleak_remove_region(memtable, current_policy, sizeof(*current_policy)); + memleak_scan_list_head(memtable, &pending_opens); + memleak_scan_obj(memtable, current_policy); } #endif diff --git a/plugins/libplugin.c b/plugins/libplugin.c index e39a507c1980..0d4afafc2372 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1350,16 +1350,16 @@ static void memleak_check(struct plugin *plugin, struct command *cmd) { struct htable *memtable; - memtable = memleak_find_allocations(tmpctx, cmd, cmd); + memtable = memleak_start(tmpctx, cmd, cmd); /* Now delete plugin and anything it has pointers to. */ - memleak_remove_region(memtable, plugin, sizeof(*plugin)); + memleak_scan_obj(memtable, plugin); /* Memleak needs some help to see into strmaps */ - memleak_remove_strmap(memtable, &plugin->out_reqs); + memleak_scan_strmap(memtable, &plugin->out_reqs); /* We know usage strings are referred to. */ - memleak_remove_strmap(memtable, &cmd->plugin->usagemap); + memleak_scan_strmap(memtable, &cmd->plugin->usagemap); if (plugin->mark_mem) plugin->mark_mem(plugin, memtable); diff --git a/plugins/pay.c b/plugins/pay.c index d086103680e6..c67e48bd721b 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -571,7 +571,7 @@ static struct command_result *json_listpays(struct command *cmd, #if DEVELOPER static void memleak_mark_payments(struct plugin *p, struct htable *memtable) { - memleak_remove_region(memtable, &payments, sizeof(payments)); + memleak_scan_list_head(memtable, &payments); } #endif diff --git a/plugins/topology.c b/plugins/topology.c index 56f8154704a5..d20e65a8bcc9 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -615,8 +615,7 @@ static struct command_result *json_listincoming(struct command *cmd, #if DEVELOPER static void memleak_mark(struct plugin *p, struct htable *memtable) { - memleak_remove_region(memtable, global_gossmap, - tal_bytelen(global_gossmap)); + memleak_scan_obj(memtable, global_gossmap); } #endif diff --git a/plugins/txprepare.c b/plugins/txprepare.c index 669daef98de1..d4a35563477f 100644 --- a/plugins/txprepare.c +++ b/plugins/txprepare.c @@ -570,7 +570,7 @@ static const struct plugin_command commands[] = { #if DEVELOPER static void mark_unreleased_txs(struct plugin *plugin, struct htable *memtable) { - memleak_remove_region(memtable, &unreleased_txs, sizeof(unreleased_txs)); + memleak_scan_list_head(memtable, &unreleased_txs); } #endif diff --git a/tests/plugins/test_libplugin.c b/tests/plugins/test_libplugin.c index a93f975ad45e..00f0c3196f06 100644 --- a/tests/plugins/test_libplugin.c +++ b/tests/plugins/test_libplugin.c @@ -107,7 +107,7 @@ static struct command_result *json_testrpc(struct command *cmd, static void memleak_mark(struct plugin *p, struct htable *memtable) { /* name_option is not a leak! */ - memleak_remove_region(memtable, &name_option, sizeof(name_option)); + memleak_ptr(memtable, name_option); } #endif /* DEVELOPER */ From 701dd3dcefd7a005e042c310a902f4b3dea15e61 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 16 Sep 2022 12:45:03 +0930 Subject: [PATCH 1399/1530] memleak: remove exclusions from memleak_start() Add memleak_ignore_children() so callers can do exclusions themselves. Having two exclusions was always such a hack! Signed-off-by: Rusty Russell --- channeld/channeld.c | 3 ++- closingd/closingd.c | 2 +- common/memleak.c | 19 +++++++++---------- common/memleak.h | 18 +++++++++++------- connectd/connectd.c | 3 ++- gossipd/gossipd.c | 4 ++-- hsmd/hsmd.c | 3 ++- lightningd/memdump.c | 6 +++++- onchaind/onchaind.c | 4 +++- onchaind/test/run-grind_feerate-bug.c | 4 +--- onchaind/test/run-grind_feerate.c | 4 +--- openingd/dualopend.c | 3 ++- openingd/openingd.c | 3 ++- plugins/libplugin.c | 6 +++++- 14 files changed, 48 insertions(+), 34 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 6fd12783901d..5e20d71e773d 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -3652,7 +3652,8 @@ static void handle_dev_memleak(struct peer *peer, const u8 *msg) struct htable *memtable; bool found_leak; - memtable = memleak_start(tmpctx, msg, msg); + memtable = memleak_start(tmpctx); + memleak_ptr(memtable, msg); /* Now delete peer and things it has pointers to. */ memleak_scan_obj(memtable, peer); diff --git a/closingd/closingd.c b/closingd/closingd.c index d3f0623a378a..3528f5d3cdd7 100644 --- a/closingd/closingd.c +++ b/closingd/closingd.c @@ -554,7 +554,7 @@ static void closing_dev_memleak(const tal_t *ctx, u8 *scriptpubkey[NUM_SIDES], const u8 *funding_wscript) { - struct htable *memtable = memleak_start(tmpctx, NULL, NULL); + struct htable *memtable = memleak_start(tmpctx); memleak_ptr(memtable, ctx); memleak_ptr(memtable, scriptpubkey[LOCAL]); diff --git a/common/memleak.c b/common/memleak.c index 2ad2411e3fb1..a12b32001b02 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -71,17 +71,13 @@ bool memleak_ptr(struct htable *memtable, const void *p) return htable_del(memtable, hash_ptr(p, NULL), p); } -static void children_into_htable(const void *exclude1, const void *exclude2, - struct htable *memtable, const tal_t *p) +static void children_into_htable(struct htable *memtable, const tal_t *p) { const tal_t *i; for (i = tal_first(p); i; i = tal_next(i)) { const char *name = tal_name(i); - if (i == exclude1 || i == exclude2) - continue; - if (name) { /* Don't add backtrace objects. */ if (streq(name, "backtrace")) @@ -108,7 +104,7 @@ static void children_into_htable(const void *exclude1, const void *exclude2, continue; } htable_add(memtable, hash_ptr(i, NULL), i); - children_into_htable(exclude1, exclude2, memtable, i); + children_into_htable(memtable, i); } } @@ -268,6 +264,11 @@ void memleak_add_helper_(const tal_t *p, mh->cb = cb; } +void memleak_ignore_children(struct htable *memtable, const void *p) +{ + for (const tal_t *i = tal_first(p); i; i = tal_next(i)) + remove_with_children(memtable, i); +} /* Handle allocations marked with helpers or notleak() */ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) @@ -297,16 +298,14 @@ static void call_memleak_helpers(struct htable *memtable, const tal_t *p) } } -struct htable *memleak_start(const tal_t *ctx, - const void *exclude1, - const void *exclude2) +struct htable *memleak_start(const tal_t *ctx) { struct htable *memtable = tal(ctx, struct htable); htable_init(memtable, hash_ptr, NULL); if (memleak_track) { /* First, add all pointers off NULL to table. */ - children_into_htable(exclude1, exclude2, memtable, NULL); + children_into_htable(memtable, NULL); /* Iterate and call helpers to eliminate hard-to-get references. */ call_memleak_helpers(memtable, NULL); diff --git a/common/memleak.h b/common/memleak.h index c06014ea05f8..e28ae53bf806 100644 --- a/common/memleak.h +++ b/common/memleak.h @@ -67,14 +67,8 @@ void memleak_add_helper_(const tal_t *p, void (*cb)(struct htable *memtable, /** * memleak_start: allocate a htable with all tal objects * @ctx: the context to allocate the htable from - * @exclude1: one tal pointer to exclude from adding (if non-NULL) - * @exclude2: second tal pointer to exclude from adding (if non-NULL) - * - * Note that exclude1 and exclude2's tal children are also not added. */ -struct htable *memleak_start(const tal_t *ctx, - const void *exclude1, - const void *exclude2); +struct htable *memleak_start(const tal_t *ctx); /** * memleak_ptr: this pointer is not a memleak. @@ -131,6 +125,16 @@ void memleak_scan_intmap_(struct htable *memtable, const struct intmap *m); memleak_scan_strmap_((memtable), tcon_unwrap(strmap)) void memleak_scan_strmap_(struct htable *memtable, const struct strmap *m); +/** + * memleak_ignore_children - ignore all this tal object's children. + * @memtable: the memtable created by memleak_start + * @p: the tal pointer. + * + * This is equivalent to calling memleak_ptr() on every child of @p + * recursively. This is a big hammer, so be careful! + */ +void memleak_ignore_children(struct htable *memtable, const void *p); + /** * memleak_get: get (and remove) a leak from memtable, or NULL * @memtable: the memtable after all known allocations removed. diff --git a/connectd/connectd.c b/connectd/connectd.c index 49bd268a189a..debe87693bb3 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -1860,7 +1860,8 @@ static void dev_connect_memleak(struct daemon *daemon, const u8 *msg) struct htable *memtable; bool found_leak; - memtable = memleak_start(tmpctx, msg, msg); + memtable = memleak_start(tmpctx); + memleak_ptr(memtable, msg); /* Now delete daemon and those which it has pointers to. */ memleak_scan_obj(memtable, daemon); diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 9cbbfc21dda8..1926c440ec5b 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -807,8 +807,8 @@ static void dev_gossip_memleak(struct daemon *daemon, const u8 *msg) struct htable *memtable; bool found_leak; - memtable = memleak_start(tmpctx, msg, msg); - + memtable = memleak_start(tmpctx); + memleak_ptr(memtable, msg); /* Now delete daemon and those which it has pointers to. */ memleak_scan_obj(memtable, daemon); diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index aae54bedda1d..9e3991c4e814 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -555,7 +555,8 @@ static struct io_plan *handle_memleak(struct io_conn *conn, bool found_leak; u8 *reply; - memtable = memleak_start(tmpctx, msg_in, msg_in); + memtable = memleak_start(tmpctx); + memleak_ptr(memtable, msg_in); /* Now note clients and anything they point to. */ memleak_scan_region(memtable, dbid_zero_clients, sizeof(dbid_zero_clients)); diff --git a/lightningd/memdump.c b/lightningd/memdump.c index f279aca8e746..4c3884c9efbd 100644 --- a/lightningd/memdump.c +++ b/lightningd/memdump.c @@ -143,7 +143,11 @@ static void finish_report(const struct leak_detect *leaks) ld = cmd->ld; /* Enter everything, except this cmd and its jcon */ - memtable = memleak_start(cmd, cmd, cmd->jcon); + memtable = memleak_start(cmd); + + /* This command is not a leak! */ + memleak_ptr(memtable, cmd); + memleak_ignore_children(memtable, cmd); /* First delete known false positives. */ memleak_scan_htable(memtable, &ld->topology->txwatches.raw); diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 3f75dac9f32b..937235cd74e5 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -2101,7 +2101,9 @@ static bool handle_dev_memleak(struct tracked_output **outs, const u8 *msg) if (!fromwire_onchaind_dev_memleak(msg)) return false; - memtable = memleak_start(tmpctx, msg, msg); + memtable = memleak_start(tmpctx); + memleak_ptr(memtable, msg); + /* Top-level context is parent of outs */ memleak_remove_globals(memtable, tal_parent(outs)); memleak_scan_obj(memtable, outs); diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index 8423e972e027..34917cb3a753 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -324,9 +324,7 @@ void memleak_scan_obj(struct htable *memtable UNNEEDED, const void *obj UNNEEDED void memleak_scan_region(struct htable *memtable UNNEEDED, const void *p UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "memleak_scan_region called!\n"); abort(); } /* Generated stub for memleak_start */ -struct htable *memleak_start(const tal_t *ctx UNNEEDED, - const void *exclude1 UNNEEDED, - const void *exclude2 UNNEEDED) +struct htable *memleak_start(const tal_t *ctx UNNEEDED) { fprintf(stderr, "memleak_start called!\n"); abort(); } /* Generated stub for memleak_status_broken */ void memleak_status_broken(const char *fmt UNNEEDED, ...) diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 7e59d8fb25b9..057ef558c99e 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -131,9 +131,7 @@ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) void memleak_scan_obj(struct htable *memtable UNNEEDED, const void *obj UNNEEDED) { fprintf(stderr, "memleak_scan_obj called!\n"); abort(); } /* Generated stub for memleak_start */ -struct htable *memleak_start(const tal_t *ctx UNNEEDED, - const void *exclude1 UNNEEDED, - const void *exclude2 UNNEEDED) +struct htable *memleak_start(const tal_t *ctx UNNEEDED) { fprintf(stderr, "memleak_start called!\n"); abort(); } /* Generated stub for new_coin_channel_close */ struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED, diff --git a/openingd/dualopend.c b/openingd/dualopend.c index 79a2b29f0141..8e747a140a51 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -890,7 +890,8 @@ static void handle_dev_memleak(struct state *state, const u8 *msg) /* Populate a hash table with all our allocations (except msg, which * is in use right now). */ - memtable = memleak_start(tmpctx, msg, msg); + memtable = memleak_start(tmpctx); + memleak_ptr(memtable, msg); /* Now delete state and things it has pointers to. */ memleak_scan_obj(memtable, state); diff --git a/openingd/openingd.c b/openingd/openingd.c index 27a84737d61a..cf3afbefdecb 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1301,7 +1301,8 @@ static void handle_dev_memleak(struct state *state, const u8 *msg) /* Populate a hash table with all our allocations (except msg, which * is in use right now). */ - memtable = memleak_start(tmpctx, msg, msg); + memtable = memleak_start(tmpctx); + memleak_ptr(memtable, msg); /* Now delete state and things it has pointers to. */ memleak_scan_obj(memtable, state); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 0d4afafc2372..5b2701b811d5 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -1350,7 +1350,11 @@ static void memleak_check(struct plugin *plugin, struct command *cmd) { struct htable *memtable; - memtable = memleak_start(tmpctx, cmd, cmd); + memtable = memleak_start(tmpctx); + + /* cmd in use right now */ + memleak_ptr(memtable, cmd); + memleak_ignore_children(memtable, cmd); /* Now delete plugin and anything it has pointers to. */ memleak_scan_obj(memtable, plugin); From 5b58eda7486d2fe613396e197d9a538f84a85820 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 16 Sep 2022 12:45:03 +0930 Subject: [PATCH 1400/1530] libplugin: mark the cmd notleak() whenever command_still_pending() called. This is what we do in lightningd, which makes memleak much more forgiving: you can hang temporaries off cmd without getting reports of leaks (also when send_outreq called). We remove all the notleak() calls in plugins which worked around this! And avoid multiple notleak labels, since both send_outreq() and command_still_pending() can be called multiple times. Signed-off-by: Rusty Russell --- common/memleak.c | 16 ++++++++++------ plugins/bcli.c | 3 --- plugins/bkpr/bookkeeper.c | 8 +------- plugins/commando.c | 11 ++--------- plugins/libplugin.c | 4 ++++ plugins/pay.c | 4 ---- plugins/spender/fundchannel.c | 3 --- plugins/spender/multifundchannel.c | 7 ++----- 8 files changed, 19 insertions(+), 37 deletions(-) diff --git a/common/memleak.c b/common/memleak.c index a12b32001b02..205e3804ef12 100644 --- a/common/memleak.c +++ b/common/memleak.c @@ -50,12 +50,16 @@ void *notleak_(void *ptr, bool plus_children) name = tal_name(ptr); if (!name) name = ""; - if (plus_children) - name = tal_fmt(tmpctx, "%s **NOTLEAK_IGNORE_CHILDREN**", - name); - else - name = tal_fmt(tmpctx, "%s **NOTLEAK**", name); - tal_set_name(ptr, name); + + /* Don't mark more than once! */ + if (!strstr(name, "**NOTLEAK")) { + if (plus_children) + name = tal_fmt(tmpctx, "%s **NOTLEAK_IGNORE_CHILDREN**", + name); + else + name = tal_fmt(tmpctx, "%s **NOTLEAK**", name); + tal_set_name(ptr, name); + } return cast_const(void *, ptr); } diff --git a/plugins/bcli.c b/plugins/bcli.c index b5c6a9b80934..47bc121f5d30 100644 --- a/plugins/bcli.c +++ b/plugins/bcli.c @@ -782,9 +782,6 @@ static struct command_result *sendrawtransaction(struct command *cmd, } else highfeesarg = NULL; - /* Keep memleak happy! */ - tal_free(allowhighfees); - start_bitcoin_cli(NULL, cmd, process_sendrawtransaction, true, BITCOIND_HIGH_PRIO, NULL, "sendrawtransaction", diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index bece4a959796..be0b256ef78c 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -1294,7 +1294,7 @@ static struct command_result *lookup_invoice_desc(struct command *cmd, struct out_req *req; /* Otherwise will go away when event is cleaned up */ - notleak(tal_steal(cmd, payment_hash)); + tal_steal(cmd, payment_hash); if (!amount_msat_zero(credit)) req = jsonrpc_request_start(cmd->plugin, cmd, "listinvoices", @@ -1372,8 +1372,6 @@ listpeers_done(struct command *cmd, const char *buf, if (info->ev->payment_id && streq(info->ev->tag, mvt_tag_str(INVOICE))) { - /* Make memleak happy */ - tal_steal(tmpctx, info); return lookup_invoice_desc(cmd, info->ev->credit, info->ev->payment_id); } @@ -1591,8 +1589,6 @@ parse_and_log_chain_move(struct command *cmd, if (tags[i] != INVOICE) continue; - /* Keep memleak happy */ - tal_steal(tmpctx, e); return lookup_invoice_desc(cmd, e->credit, e->payment_id); } @@ -1673,8 +1669,6 @@ parse_and_log_channel_move(struct command *cmd, maybe_record_rebalance(db, e); db_commit_transaction(db); - /* Keep memleak happy */ - tal_steal(tmpctx, e); return lookup_invoice_desc(cmd, e->credit, e->payment_id); } diff --git a/plugins/commando.c b/plugins/commando.c index ae770e37609f..1f33ffaa6eb0 100644 --- a/plugins/commando.c +++ b/plugins/commando.c @@ -680,13 +680,12 @@ static struct command_result *json_commando(struct command *cmd, tal_append_fmt(&json, ",\"rune\":\"%s\"", rune); tal_append_fmt(&json, "}"); - /* This is not a leak, but we don't keep a pointer. */ - outgoing = notleak(tal(cmd, struct outgoing)); + outgoing = tal(cmd, struct outgoing); outgoing->peer = *peer; outgoing->msg_off = 0; /* 65000 per message gives sufficient headroom. */ jsonlen = tal_bytelen(json)-1; - outgoing->msgs = notleak(tal_arr(cmd, u8 *, (jsonlen + 64999) / 65000)); + outgoing->msgs = tal_arr(cmd, u8 *, (jsonlen + 64999) / 65000); for (size_t i = 0; i < tal_count(outgoing->msgs); i++) { u8 *cmd_msg = tal_arr(outgoing, u8, 0); bool terminal = (i == tal_count(outgoing->msgs) - 1); @@ -705,12 +704,6 @@ static struct command_result *json_commando(struct command *cmd, outgoing->msgs[i] = cmd_msg; } - /* Keep memleak code happy! */ - tal_free(peer); - tal_free(method); - tal_free(cparams); - tal_free(rune); - return send_more_cmd(cmd, NULL, NULL, outgoing); } diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 5b2701b811d5..ad2e59fbc411 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -297,6 +297,8 @@ struct command_result *command_finished(struct command *cmd, struct command_result *WARN_UNUSED_RESULT command_still_pending(struct command *cmd) { + if (cmd) + notleak_with_children(cmd); return &pending; } @@ -728,6 +730,8 @@ send_outreq(struct plugin *plugin, const struct out_req *req) ld_rpc_send(plugin, req->js); + if (req->cmd != NULL) + notleak_with_children(req->cmd); return &pending; } diff --git a/plugins/pay.c b/plugins/pay.c index c67e48bd721b..2f99e702c181 100644 --- a/plugins/pay.c +++ b/plugins/pay.c @@ -1202,10 +1202,6 @@ static struct command_result *json_pay(struct command *cmd, } payment_mod_exemptfee_get_data(p)->amount = exemptfee ? *exemptfee : AMOUNT_MSAT(5000); - - /* We free unneeded params now to keep memleak happy. */ - tal_free(maxfee_pct_millionths); - tal_free(exemptfee); } shadow_route = payment_mod_shadowroute_get_data(p); diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index 51332c6c186b..ba5adadab728 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -108,9 +108,6 @@ json_fundchannel(struct command *cmd, if (utxos) json_add_tok(req->js, "utxos", utxos, buf); - /* Stop memleak from complaining */ - tal_free(id); - return send_outreq(cmd->plugin, req); } diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index ffb50b053294..c639efdc0f4e 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1832,8 +1832,8 @@ post_cleanup_redo_multifundchannel(struct multifundchannel_redo *redo) /* Okay, we still have destinations to try: wait a second in case it * takes that long to disconnect from peer, then retry. */ - notleak(plugin_timer(mfc->cmd->plugin, time_from_sec(1), - perform_multifundchannel, mfc)); + plugin_timer(mfc->cmd->plugin, time_from_sec(1), + perform_multifundchannel, mfc); return command_still_pending(mfc->cmd); } @@ -2064,9 +2064,6 @@ json_multifundchannel(struct command *cmd, mfc->sigs_collected = false; - /* Stop memleak from complaining */ - tal_free(minconf); - perform_multifundchannel(mfc); return command_still_pending(mfc->cmd); } From 248d60d7bd7e9a1961e70a3579ea93a0d496d6cc Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Fri, 9 Sep 2022 16:24:20 -0400 Subject: [PATCH 1401/1530] Don't report redundant feerates to subdaemons --- lightningd/chaintopology.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index aba7ac5cebaa..d521eae4c2e6 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -338,7 +338,7 @@ static void update_feerates(struct bitcoind *bitcoind, * 2 minutes. The following will do that in a polling interval * independent manner. */ double alpha = 1 - pow(0.1,(double)topo->poll_seconds / 120); - bool feerate_changed = false; + bool notify_feerate_changed = false; for (size_t i = 0; i < NUM_FEERATES; i++) { u32 feerate = satoshi_per_kw[i]; @@ -352,7 +352,7 @@ static void update_feerates(struct bitcoind *bitcoind, /* Initial smoothed feerate is the polled feerate */ if (!old_feerates[i]) { - feerate_changed = true; + notify_feerate_changed = true; old_feerates[i] = feerate; init_feerate_history(topo, i, feerate); @@ -360,8 +360,6 @@ static void update_feerates(struct bitcoind *bitcoind, "Smoothed feerate estimate for %s initialized to polled estimate %u", feerate_name(i), feerate); } else { - if (feerate != old_feerates[i]) - feerate_changed = true; add_feerate_history(topo, i, feerate); } @@ -390,6 +388,10 @@ static void update_feerates(struct bitcoind *bitcoind, feerate, topo->feerate[i]); } topo->feerate[i] = feerate; + + /* After adjustment, If any entry doesn't match prior reported, report all */ + if (feerate != old_feerates[i]) + notify_feerate_changed = true; } if (topo->feerate_uninitialized) { @@ -399,7 +401,7 @@ static void update_feerates(struct bitcoind *bitcoind, maybe_completed_init(topo); } - if (feerate_changed) + if (notify_feerate_changed) notify_feerate_change(bitcoind->ld); next_updatefee_timer(topo); From ab95d2718f1dfa1f0190159e2ba56c5ffdfc84fc Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 19 Sep 2022 12:28:07 +0200 Subject: [PATCH 1402/1530] pyln: Reduce dependency strictness for pyln-testing --- contrib/pyln-testing/pyln/testing/__init__.py | 2 +- contrib/pyln-testing/pyproject.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/__init__.py b/contrib/pyln-testing/pyln/testing/__init__.py index 1d1e55bbdbd5..6466edc599df 100644 --- a/contrib/pyln-testing/pyln/testing/__init__.py +++ b/contrib/pyln-testing/pyln/testing/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.12.0" +__version__ = "0.12.0.post1" __all__ = [ "__version__", diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index 4aaede93e037..d5ef469157e8 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-testing" -version = "0.11.1" +version = "0.12.0.post1" description = "Test your Core Lightning integration, plugins or whatever you want" authors = ["Christian Decker "] license = "BSD-MIT" @@ -21,8 +21,8 @@ pyln-client = "^0.11" Flask = "^2.0.3" cheroot = "^8.6.0" psutil = "^5.9.0" -grpcio = "^1.49.0" -protobuf = "^4.21.6" +grpcio = ">=1.47" +protobuf = ">=3" [tool.poetry.dev-dependencies] pyln-client = { path = "../pyln-client", develop = true} From fcd2320de7be182af0ed4c04409165bae912d38d Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 19 Sep 2022 18:41:24 +0200 Subject: [PATCH 1403/1530] gha: Make the setup and build scripts exit if anything fails Caused quite a few headaches and red herrings in the past, so let's be stricter. Changelog-None --- .github/scripts/build.sh | 2 +- .github/scripts/setup.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/build.sh b/.github/scripts/build.sh index 17e4c90fd3bf..e1a18acc937b 100755 --- a/.github/scripts/build.sh +++ b/.github/scripts/build.sh @@ -1,5 +1,5 @@ #!/bin/bash - +set -e echo "Running in $(pwd)" export ARCH=${ARCH:-64} export BOLTDIR=bolts diff --git a/.github/scripts/setup.sh b/.github/scripts/setup.sh index 2523a2dd96b0..83dd289d024b 100755 --- a/.github/scripts/setup.sh +++ b/.github/scripts/setup.sh @@ -1,5 +1,5 @@ #!/bin/bash - +set -e export DEBIAN_FRONTEND=noninteractive export BITCOIN_VERSION=0.20.1 export ELEMENTS_VERSION=0.18.1.8 From 7159a25e7358a6eae4ca2f3d1c64407ac304bb96 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 8 Jun 2022 10:36:30 +0200 Subject: [PATCH 1404/1530] openingd: Add method to set absolute reserve --- openingd/openingd.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/openingd/openingd.c b/openingd/openingd.c index cf3afbefdecb..90a439f15cab 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -138,12 +138,10 @@ static void NORETURN negotiation_failed(struct state *state, negotiation_aborted(state, errmsg); } -/* We always set channel_reserve_satoshis to 1%, rounded down. */ -static void set_reserve(struct state *state, const struct amount_sat dust_limit) +static void set_reserve_absolute(struct state * state, const struct amount_sat dust_limit, struct amount_sat reserve_sat) { - state->localconf.channel_reserve - = amount_sat_div(state->funding_sats, 100); - + status_debug("Setting their reserve to %s", + type_to_string(tmpctx, struct amount_sat, &reserve_sat)); /* BOLT #2: * * The sending node: @@ -151,10 +149,23 @@ static void set_reserve(struct state *state, const struct amount_sat dust_limit) * - MUST set `channel_reserve_satoshis` greater than or equal to * `dust_limit_satoshis` from the `open_channel` message. */ - if (amount_sat_greater(dust_limit, - state->localconf.channel_reserve)) + if (amount_sat_greater(dust_limit, reserve_sat)) { + status_debug( + "Their reserve is too small, bumping to dust_limit: %s < %s", + type_to_string(tmpctx, struct amount_sat, &reserve_sat), + type_to_string(tmpctx, struct amount_sat, &dust_limit)); state->localconf.channel_reserve = dust_limit; + } else { + state->localconf.channel_reserve = reserve_sat; + } +} + +/* We always set channel_reserve_satoshis to 1%, rounded down. */ +static void set_reserve(struct state *state, const struct amount_sat dust_limit) +{ + set_reserve_absolute(state, dust_limit, + amount_sat_div(state->funding_sats, 100)); } /*~ Handle random messages we might get during opening negotiation, (eg. gossip) From 5c1de8029a0b48ab69f394384e750dc89c1a89b3 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 8 Jun 2022 11:05:10 +0200 Subject: [PATCH 1405/1530] openingd: Add `reserve` to `fundchannel` and `multifundchannel` Changelog-Added: JSON-RPC: `fundchannel`, `multifundchannel` and `fundchannel_start` now accept a `reserve` parameter to indicate the absolute reserve to impose on the peer. --- .msggen.json | 1 + cln-grpc/proto/node.proto | 1 + cln-grpc/src/convert.rs | 1 + cln-rpc/src/model.rs | 2 + contrib/pyln-client/pyln/client/lightning.py | 4 +- contrib/pyln-testing/pyln/testing/node_pb2.py | 96 +++++++++---------- doc/schemas/fundchannel.request.json | 4 + lightningd/opening_common.c | 7 ++ lightningd/opening_common.h | 7 ++ lightningd/opening_control.c | 5 +- plugins/spender/fundchannel.c | 5 + plugins/spender/multifundchannel.c | 6 ++ plugins/spender/multifundchannel.h | 2 + 13 files changed, 91 insertions(+), 50 deletions(-) diff --git a/.msggen.json b/.msggen.json index b56f3dab4c76..d4ba473844cd 100644 --- a/.msggen.json +++ b/.msggen.json @@ -393,6 +393,7 @@ "FundChannel.mindepth": 12, "FundChannel.push_msat": 5, "FundChannel.request_amt": 7, + "FundChannel.reserve": 13, "FundChannel.utxos[]": 11 }, "FundchannelResponse": { diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index a1bb1545b7b5..a8ebb1bb49e1 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1158,6 +1158,7 @@ message FundchannelRequest { optional string compact_lease = 8; repeated Outpoint utxos = 11; optional uint32 mindepth = 12; + optional Amount reserve = 13; } message FundchannelResponse { diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index c75ac0f2b590..1f8f4e125ee0 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1464,6 +1464,7 @@ impl From<&pb::FundchannelRequest> for requests::FundchannelRequest { compact_lease: c.compact_lease.clone(), // Rule #1 for type string? utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 mindepth: c.mindepth.clone(), // Rule #1 for type u32? + reserve: c.reserve.as_ref().map(|a| a.into()), // Rule #1 for type msat? } } } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index adcca4b90366..499bb9a7f06a 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -726,6 +726,8 @@ pub mod requests { pub utxos: Option>, #[serde(alias = "mindepth", skip_serializing_if = "Option::is_none")] pub mindepth: Option, + #[serde(alias = "reserve", skip_serializing_if = "Option::is_none")] + pub reserve: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index d2a64335be0d..38193e03c0ec 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -730,7 +730,8 @@ def feerates(self, style, urgent=None, normal=None, slow=None): def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None, utxos=None, push_msat=None, close_to=None, request_amt=None, compact_lease=None, - mindepth: Optional[int] = None): + mindepth: Optional[int] = None, + reserve: Optional[str] = None): """ Fund channel with {id} using {amount} satoshis with feerate of {feerate} (uses default feerate if unset). @@ -756,6 +757,7 @@ def fundchannel(self, node_id, amount, feerate=None, announce=True, "request_amt": request_amt, "compact_lease": compact_lease, "mindepth": mindepth, + "reserve": reserve, } return self.call("fundchannel", payload) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 32a93228cd20..732a5215f552 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xb6\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepth\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1350,51 +1350,51 @@ _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24692 _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24885 _FUNDCHANNELREQUEST._serialized_start=24888 - _FUNDCHANNELREQUEST._serialized_end=25326 - _FUNDCHANNELRESPONSE._serialized_start=25329 - _FUNDCHANNELRESPONSE._serialized_end=25484 - _GETROUTEREQUEST._serialized_start=25487 - _GETROUTEREQUEST._serialized_end=25723 - _GETROUTERESPONSE._serialized_start=25725 - _GETROUTERESPONSE._serialized_end=25778 - _GETROUTEROUTE._serialized_start=25781 - _GETROUTEROUTE._serialized_end=25978 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25949 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=25978 - _LISTFORWARDSREQUEST._serialized_start=25981 - _LISTFORWARDSREQUEST._serialized_end=26239 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26121 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26197 - _LISTFORWARDSRESPONSE._serialized_start=26241 - _LISTFORWARDSRESPONSE._serialized_end=26308 - _LISTFORWARDSFORWARDS._serialized_start=26311 - _LISTFORWARDSFORWARDS._serialized_end=26879 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26676 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26760 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26762 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26810 - _LISTPAYSREQUEST._serialized_start=26882 - _LISTPAYSREQUEST._serialized_end=27101 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27007 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27062 - _LISTPAYSRESPONSE._serialized_start=27103 - _LISTPAYSRESPONSE._serialized_end=27154 - _LISTPAYSPAYS._serialized_start=27157 - _LISTPAYSPAYS._serialized_end=27676 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27488 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27547 - _PINGREQUEST._serialized_start=27678 - _PINGREQUEST._serialized_end=27767 - _PINGRESPONSE._serialized_start=27769 - _PINGRESPONSE._serialized_end=27799 - _SIGNMESSAGEREQUEST._serialized_start=27801 - _SIGNMESSAGEREQUEST._serialized_end=27838 - _SIGNMESSAGERESPONSE._serialized_start=27840 - _SIGNMESSAGERESPONSE._serialized_end=27910 - _STOPREQUEST._serialized_start=27912 - _STOPREQUEST._serialized_end=27925 - _STOPRESPONSE._serialized_start=27927 - _STOPRESPONSE._serialized_end=27941 - _NODE._serialized_start=27944 - _NODE._serialized_end=30872 + _FUNDCHANNELREQUEST._serialized_end=25373 + _FUNDCHANNELRESPONSE._serialized_start=25376 + _FUNDCHANNELRESPONSE._serialized_end=25531 + _GETROUTEREQUEST._serialized_start=25534 + _GETROUTEREQUEST._serialized_end=25770 + _GETROUTERESPONSE._serialized_start=25772 + _GETROUTERESPONSE._serialized_end=25825 + _GETROUTEROUTE._serialized_start=25828 + _GETROUTEROUTE._serialized_end=26025 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25996 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=26025 + _LISTFORWARDSREQUEST._serialized_start=26028 + _LISTFORWARDSREQUEST._serialized_end=26286 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26168 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26244 + _LISTFORWARDSRESPONSE._serialized_start=26288 + _LISTFORWARDSRESPONSE._serialized_end=26355 + _LISTFORWARDSFORWARDS._serialized_start=26358 + _LISTFORWARDSFORWARDS._serialized_end=26926 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26723 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26807 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26809 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26857 + _LISTPAYSREQUEST._serialized_start=26929 + _LISTPAYSREQUEST._serialized_end=27148 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27054 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27109 + _LISTPAYSRESPONSE._serialized_start=27150 + _LISTPAYSRESPONSE._serialized_end=27201 + _LISTPAYSPAYS._serialized_start=27204 + _LISTPAYSPAYS._serialized_end=27723 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27535 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27594 + _PINGREQUEST._serialized_start=27725 + _PINGREQUEST._serialized_end=27814 + _PINGRESPONSE._serialized_start=27816 + _PINGRESPONSE._serialized_end=27846 + _SIGNMESSAGEREQUEST._serialized_start=27848 + _SIGNMESSAGEREQUEST._serialized_end=27885 + _SIGNMESSAGERESPONSE._serialized_start=27887 + _SIGNMESSAGERESPONSE._serialized_end=27957 + _STOPREQUEST._serialized_start=27959 + _STOPREQUEST._serialized_end=27972 + _STOPRESPONSE._serialized_start=27974 + _STOPRESPONSE._serialized_end=27988 + _NODE._serialized_start=27991 + _NODE._serialized_end=30919 # @@protoc_insertion_point(module_scope) diff --git a/doc/schemas/fundchannel.request.json b/doc/schemas/fundchannel.request.json index 1ebc44b3dfa9..bc4b44a4e152 100644 --- a/doc/schemas/fundchannel.request.json +++ b/doc/schemas/fundchannel.request.json @@ -44,6 +44,10 @@ "mindepth": { "description": "Number of confirmations required before we consider the channel active", "type": "u32" + }, + "reserve": { + "type": "msat", + "description": "The amount we want the peer to maintain on its side" } } } diff --git a/lightningd/opening_common.c b/lightningd/opening_common.c index 5f2f848d8c83..e42fd0e558c3 100644 --- a/lightningd/opening_common.c +++ b/lightningd/opening_common.c @@ -65,6 +65,13 @@ new_uncommitted_channel(struct peer *peer) /* We override this in openchannel hook if we want zeroconf */ uc->minimum_depth = ld->config.anchor_confirms; + /* Use default 1% reserve if not otherwise specified. If this + * is not-NULL it will be used by openingd as absolute value + * (clamped to dust limit). */ + uc->reserve = NULL; + + memset(&uc->cid, 0xFF, sizeof(uc->cid)); + /* Declare the new channel to the HSM. */ new_channel_msg = towire_hsmd_new_channel(NULL, &uc->peer->id, uc->dbid); if (!wire_sync_write(ld->hsm_fd, take(new_channel_msg))) diff --git a/lightningd/opening_common.h b/lightningd/opening_common.h index 50a236c50210..907a484ef24b 100644 --- a/lightningd/opening_common.h +++ b/lightningd/opening_common.h @@ -58,6 +58,12 @@ struct uncommitted_channel { /* Our channel config. */ struct channel_config our_config; + + /* Reserve we will impose on the other side. If this is NULL + * we will use our default of 1% of the funding + * amount. Otherwise it will be used by openingd as absolute + * value (clamped to dust limit). */ + struct amount_sat *reserve; }; struct funding_channel { @@ -66,6 +72,7 @@ struct funding_channel { struct wallet_tx *wtx; struct amount_msat push; struct amount_sat funding_sats; + u8 channel_flags; const u8 *our_upfront_shutdown_script; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 593c7b75a911..a82959a5325b 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1095,7 +1095,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, bool *announce_channel; u32 *feerate_per_kw, *mindepth; int fds[2]; - struct amount_sat *amount; + struct amount_sat *amount, *reserve; struct amount_msat *push_msat; u32 *upfront_shutdown_script_wallet_index; struct channel_id tmp_channel_id; @@ -1114,6 +1114,7 @@ static struct command_result *json_fundchannel_start(struct command *cmd, p_opt("close_to", param_bitcoin_address, &fc->our_upfront_shutdown_script), p_opt("push_msat", param_msat, &push_msat), p_opt_def("mindepth", param_u32, &mindepth, cmd->ld->config.anchor_confirms), + p_opt("reserve", param_sat, &reserve), NULL)) return command_param_failed(); @@ -1223,6 +1224,8 @@ static struct command_result *json_fundchannel_start(struct command *cmd, assert(mindepth != NULL); fc->uc->minimum_depth = *mindepth; + fc->uc->reserve = reserve; + /* Needs to be stolen away from cmd */ if (fc->our_upfront_shutdown_script) fc->our_upfront_shutdown_script diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index ba5adadab728..b4ce30ff673a 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -54,6 +54,7 @@ json_fundchannel(struct command *cmd, const jsmntok_t *request_amt; const jsmntok_t *compact_lease; const jsmntok_t *mindepth; + const jsmntok_t *reserve; struct out_req *req; @@ -69,6 +70,7 @@ json_fundchannel(struct command *cmd, p_opt("request_amt", param_tok, &request_amt), p_opt("compact_lease", param_tok, &compact_lease), p_opt("mindepth", param_tok, &mindepth), + p_opt("reserve", param_tok, &reserve), NULL)) return command_param_failed(); @@ -99,6 +101,9 @@ json_fundchannel(struct command *cmd, if (mindepth) json_add_tok(req->js, "mindepth", mindepth, buf); + if (reserve) + json_add_tok(req->js, "reserve", reserve, buf); + json_object_end(req->js); json_array_end(req->js); if (feerate) diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index c639efdc0f4e..2bbd2fbded23 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -1125,6 +1125,11 @@ fundchannel_start_dest(struct multifundchannel_destination *dest) if (dest->mindepth) json_add_u32(req->js, "mindepth", *dest->mindepth); + if (dest->reserve) + json_add_string( + req->js, "reserve", + type_to_string(tmpctx, struct amount_sat, dest->reserve)); + send_outreq(cmd->plugin, req); } @@ -1914,6 +1919,7 @@ param_destinations_array(struct command *cmd, const char *name, AMOUNT_SAT(0)), p_opt("compact_lease", param_lease_hex, &rates), p_opt("mindepth", param_u32, &dest->mindepth), + p_opt("reserve", param_sat, &dest->reserve), NULL)) return command_param_failed(); diff --git a/plugins/spender/multifundchannel.h b/plugins/spender/multifundchannel.h index f27b378da34c..96a7e12956dd 100644 --- a/plugins/spender/multifundchannel.h +++ b/plugins/spender/multifundchannel.h @@ -112,6 +112,8 @@ struct multifundchannel_destination { bool all; struct amount_sat amount; + struct amount_sat *reserve; + /* the output index for this destination. */ unsigned int outnum; From 9a97f8c154dfc84e69d494a1a72bfdc00041de5a Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 8 Jun 2022 13:54:18 +0200 Subject: [PATCH 1406/1530] plugin: Add `reserve` to `openchannel` hook result Changelog-Added: plugin: The `openchannel` hook may return a custom absolute `reserve` value that the peer must not dip below. --- doc/PLUGINS.md | 18 +++++++++++++++++- lightningd/opening_control.c | 11 +++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 563e34fe09ef..6152bed954f8 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1198,13 +1198,29 @@ e.g. { "result": "continue", "close_to": "bc1qlq8srqnz64wgklmqvurv7qnr4rvtq2u96hhfg2" + "mindepth": 0, + "reserve": "1234sat" } ``` Note that `close_to` must be a valid address for the current chain, an invalid address will cause the node to exit with an error. -Note that `openchannel` is a chained hook. Therefore `close_to` will only be + - `mindepth` is the number of confirmations to require before making + the channel usable. Notice that setting this to 0 (`zeroconf`) or + some other low value might expose you to double-spending issues, so + only lower this value from the default if you trust the peer not to + double-spend, or you reject incoming payments, including forwards, + until the funding is confirmed. + + - `reserve` is an absolute value for the amount in the channel that + the peer must keep on their side. This ensures that they always + have something to lose, so only lower this below the 1% of funding + amount if you trust the peer. The protocol requires this to be + larger than the dust limit, hence it will be adjusted to be the + dust limit if the specified value is below. + +Note that `openchannel` is a chained hook. Therefore `close_to`, `reserve` will only be evaluated for the first plugin that sets it. If more than one plugin tries to set a `close_to` address an error will be logged. diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index a82959a5325b..bbdcf7745485 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -732,6 +732,7 @@ openchannel_hook_deserialize(struct openchannel_hook_payload *payload, const jsmntok_t *t_errmsg = json_get_member(buffer, toks, "error_message"); const jsmntok_t *t_closeto = json_get_member(buffer, toks, "close_to"); const jsmntok_t *t_mindepth = json_get_member(buffer, toks, "mindepth"); + const jsmntok_t *t_reserve = json_get_member(buffer, toks, "reserve"); if (!t_result) fatal("Plugin returned an invalid response to the" @@ -793,6 +794,16 @@ openchannel_hook_deserialize(struct openchannel_hook_payload *payload, payload->uc->minimum_depth); } + if (t_reserve != NULL) { + payload->uc->reserve = tal(payload->uc, struct amount_sat); + json_to_sat(buffer, t_reserve, payload->uc->reserve); + log_debug(openingd->ld->log, + "Setting reserve=%s for this channel as requested by " + "the openchannel hook", + type_to_string(tmpctx, struct amount_sat, + payload->uc->reserve)); + } + return true; } From 8d6423389a4d58bafde6af417c7b52add0475d32 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 8 Jun 2022 13:55:47 +0200 Subject: [PATCH 1407/1530] openingd: Wire `reserve` value through to `openingd` --- lightningd/opening_control.c | 3 ++- openingd/openingd.c | 11 +++++++++-- openingd/openingd_wire.csv | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index bbdcf7745485..6ba7c4097589 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -711,7 +711,8 @@ openchannel_hook_final(struct openchannel_hook_payload *payload STEALS) subd_send_msg(openingd, take(towire_openingd_got_offer_reply(NULL, errmsg, our_upfront_shutdown_script, - upfront_shutdown_script_wallet_index))); + upfront_shutdown_script_wallet_index, + payload->uc->reserve))); } static bool diff --git a/openingd/openingd.c b/openingd/openingd.c index 90a439f15cab..d05152e4ccb6 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -814,6 +814,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) struct penalty_base *pbase; struct tlv_accept_channel_tlvs *accept_tlvs; struct tlv_open_channel_tlvs *open_tlvs; + struct amount_sat *reserve; /* BOLT #2: * @@ -1013,8 +1014,9 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) /* We don't allocate off tmpctx, because that's freed inside * opening_negotiate_msg */ if (!fromwire_openingd_got_offer_reply(state, msg, &err_reason, - &state->upfront_shutdown_script[LOCAL], - &state->local_upfront_shutdown_wallet_index)) + &state->upfront_shutdown_script[LOCAL], + &state->local_upfront_shutdown_wallet_index, + &reserve)) master_badmsg(WIRE_OPENINGD_GOT_OFFER_REPLY, msg); /* If they give us a reason to reject, do so. */ @@ -1030,6 +1032,11 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) state->our_features, state->their_features); + if (reserve != NULL) { + set_reserve_absolute(state, state->remoteconf.dust_limit, + *reserve); + } + /* OK, we accept! */ accept_tlvs = tlv_accept_channel_tlvs_new(tmpctx); accept_tlvs->upfront_shutdown_script diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index a759e1a4fddb..d81dfff44534 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -46,6 +46,7 @@ msgdata,openingd_got_offer_reply,rejection,?wirestring, msgdata,openingd_got_offer_reply,shutdown_len,u16, msgdata,openingd_got_offer_reply,our_shutdown_scriptpubkey,?u8,shutdown_len msgdata,openingd_got_offer_reply,our_shutdown_wallet_index,?u32, +msgdata,openingd_got_offer_reply,reserve,?amount_sat, #include # Openingd->master: we've successfully offered channel. From 5a54f450bdc5f659f9e8dfae27d931cebe9d462c Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 9 Jun 2022 13:34:33 +0200 Subject: [PATCH 1408/1530] openingd: Pass `reserve` down to openingd when funding --- lightningd/opening_control.c | 19 ++++++++++--------- openingd/openingd.c | 18 +++++++++++++----- openingd/openingd_wire.csv | 1 + 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 6ba7c4097589..bd0887947cd5 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1256,15 +1256,16 @@ static struct command_result *json_fundchannel_start(struct command *cmd, } else upfront_shutdown_script_wallet_index = NULL; - fc->open_msg - = towire_openingd_funder_start(fc, - *amount, - fc->push, - fc->our_upfront_shutdown_script, - upfront_shutdown_script_wallet_index, - *feerate_per_kw, - &tmp_channel_id, - fc->channel_flags); + fc->open_msg = towire_openingd_funder_start( + fc, + *amount, + fc->push, + fc->our_upfront_shutdown_script, + upfront_shutdown_script_wallet_index, + *feerate_per_kw, + &tmp_channel_id, + fc->channel_flags, + fc->uc->reserve); if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0) { return command_fail(cmd, FUND_MAX_EXCEEDED, diff --git a/openingd/openingd.c b/openingd/openingd.c index d05152e4ccb6..60237f09f40a 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -99,6 +99,8 @@ struct state { struct channel_type *channel_type; struct feature_set *our_features; + + struct amount_sat *reserve; }; /*~ If we can't agree on parameters, we fail to open the channel. @@ -245,10 +247,6 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state, static bool setup_channel_funder(struct state *state) { - /*~ For symmetry, we calculate our own reserve even though lightningd - * could do it for the we-are-funding case. */ - set_reserve(state, state->localconf.dust_limit); - #if DEVELOPER /* --dev-force-tmp-channel-id specified */ if (dev_force_tmp_channel_id) @@ -329,6 +327,15 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) if (!setup_channel_funder(state)) return NULL; + /* If we have a reserve override we use that, otherwise we'll + * use our default of 1% of the funding value. */ + if (state->reserve != NULL) { + set_reserve_absolute(state, state->localconf.dust_limit, + *state->reserve); + } else { + set_reserve(state, state->localconf.dust_limit); + } + if (!state->upfront_shutdown_script[LOCAL]) state->upfront_shutdown_script[LOCAL] = no_upfront_shutdown_script(state, @@ -1352,7 +1359,8 @@ static u8 *handle_master_in(struct state *state) &state->local_upfront_shutdown_wallet_index, &state->feerate_per_kw, &state->channel_id, - &channel_flags)) + &channel_flags, + &state->reserve)) master_badmsg(WIRE_OPENINGD_FUNDER_START, msg); msg = funder_channel_start(state, channel_flags); diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index d81dfff44534..4f77446f2f32 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -80,6 +80,7 @@ msgdata,openingd_funder_start,upfront_shutdown_wallet_index,?u32, msgdata,openingd_funder_start,feerate_per_kw,u32, msgdata,openingd_funder_start,temporary_channel_id,channel_id, msgdata,openingd_funder_start,channel_flags,u8, +msgdata,openingd_funder_start,reserve,?amount_sat, # openingd->master: send back output script for 2-of-2 funding output msgtype,openingd_funder_start_reply,6102 From 2def843dcea8ea70b9b5a80763a10f4cc6378bdd Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 6 Jul 2022 14:56:46 +0200 Subject: [PATCH 1409/1530] pay: Allow using a channel on equality of estimated capacity In the case of the local channel we set the estimation to the exact value spendable, which is important when we want to drain a channel, because there we actually want to get the last msat. --- plugins/libplugin-pay.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 14ab6effafe5..4ca2a8a4c34b 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -584,10 +584,8 @@ payment_get_excluded_channels(const tal_t *ctx, struct payment *p) if (!hint->enabled) tal_arr_expand(&res, hint->scid); - else if (amount_msat_greater_eq(p->amount, - hint->estimated_capacity)) - /* We exclude on equality because we've set the - * estimate to the smallest failed attempt. */ + else if (amount_msat_greater(p->amount, + hint->estimated_capacity)) tal_arr_expand(&res, hint->scid); else if (hint->local && hint->htlc_budget == 0) @@ -1248,6 +1246,7 @@ handle_intermediate_failure(struct command *cmd, enum onion_wire failcode) { struct payment *root = payment_root(p); + struct amount_msat estimated; paymod_log(p, LOG_DBG, "Intermediate node %s reported %04x (%s) at %s on route %s", @@ -1288,11 +1287,20 @@ handle_intermediate_failure(struct command *cmd, break; case WIRE_TEMPORARY_CHANNEL_FAILURE: { + estimated = errchan->amount; + + /* Subtract one msat more, since we know that the amount did not + * work. This allows us to then allow on equality, this is for + * example necessary for local channels where exact matches + * should be allowed. */ + if (!amount_msat_sub(&estimated, estimated, AMOUNT_MSAT(1))) + abort(); + /* These are an indication that the capacity was insufficient, * remember the amount we tried as an estimate. */ channel_hints_update(root, errchan->scid, errchan->direction, true, false, - &errchan->amount, NULL); + &estimated, NULL); goto error; } From c3e9cb7a47a1497a32c0c0fdc49209674d1155de Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 9 Jun 2022 17:04:43 +0200 Subject: [PATCH 1410/1530] openingd: Add zeroconf-no-really-zero mode This is incompatible with the spec as it removes the enforcement for reserves being above dust, but from what I can see from other implementations it seems that others have allowed this as well. This commit just guards the necessary changes with compilation guards, so we can decide either way quickly. This part of the PR is not intended to be final, just as a discussion basis. --- Makefile | 3 +++ openingd/common.c | 2 ++ openingd/openingd.c | 9 +++++++++ 3 files changed, 14 insertions(+) diff --git a/Makefile b/Makefile index 2bbfe8894132..71c3c8daa2dc 100644 --- a/Makefile +++ b/Makefile @@ -257,6 +257,9 @@ endif CPPFLAGS += -DBINTOPKGLIBEXECDIR="\"$(shell sh tools/rel.sh $(bindir) $(pkglibexecdir))\"" CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) $(COPTFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I$(CPATH) $(SQLITE3_CFLAGS) $(POSTGRES_INCLUDE) $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) -DBUILD_ELEMENTS=1 + +CFLAGS += -DZERORESERVE=1 + # If CFLAGS is already set in the environment of make (to whatever value, it # does not matter) then it would export it to subprocesses with the above value # we set, including CWARNFLAGS which by default contains -Wall -Werror. This diff --git a/openingd/common.c b/openingd/common.c index 150b8faa5a46..493ee447250d 100644 --- a/openingd/common.c +++ b/openingd/common.c @@ -180,6 +180,7 @@ bool check_config_bounds(const tal_t *ctx, return false; } +#ifndef ZERORESERVE /* BOLT #2: * * The receiving node MUST fail the channel if: @@ -197,6 +198,7 @@ bool check_config_bounds(const tal_t *ctx, &remoteconf->channel_reserve)); return false; } +#endif return true; } diff --git a/openingd/openingd.c b/openingd/openingd.c index 60237f09f40a..fa1af86784d3 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -144,6 +144,9 @@ static void set_reserve_absolute(struct state * state, const struct amount_sat d { status_debug("Setting their reserve to %s", type_to_string(tmpctx, struct amount_sat, &reserve_sat)); +#ifdef ZERORESERVE + state->localconf.channel_reserve = reserve_sat; +#else /* BOLT #2: * * The sending node: @@ -161,6 +164,7 @@ static void set_reserve_absolute(struct state * state, const struct amount_sat d } else { state->localconf.channel_reserve = reserve_sat; } +#endif } /* We always set channel_reserve_satoshis to 1%, rounded down. */ @@ -458,6 +462,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) type_to_string(msg, struct channel_id, &state->channel_id)); +#ifndef ZERORESERVE if (amount_sat_greater(state->remoteconf.dust_limit, state->localconf.channel_reserve)) { negotiation_failed(state, @@ -469,6 +474,7 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) &state->localconf.channel_reserve)); return NULL; } +#endif if (!check_config_bounds(tmpctx, state->funding_sats, state->feerate_per_kw, @@ -954,6 +960,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) /* This reserves 1% of the channel (rounded up) */ set_reserve(state, state->remoteconf.dust_limit); +#ifndef ZERORESERVE + /* Pending proposal to remove these limits. */ /* BOLT #2: * * The sender: @@ -985,6 +993,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &state->remoteconf.channel_reserve)); return NULL; } +#endif /* These checks are the same whether we're opener or accepter... */ if (!check_config_bounds(tmpctx, state->funding_sats, From c5b2aee5c69071141fb99ead92a437d7b3e46816 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 9 Jun 2022 17:07:03 +0200 Subject: [PATCH 1411/1530] pytest: Add a zeroreserve test --- tests/plugins/zeroreserve.py | 27 +++++++++++++ tests/test_opening.py | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100755 tests/plugins/zeroreserve.py diff --git a/tests/plugins/zeroreserve.py b/tests/plugins/zeroreserve.py new file mode 100755 index 000000000000..4c5c6a13730a --- /dev/null +++ b/tests/plugins/zeroreserve.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +"""Use the openchannel hook to selectively opt-into zeroconf +""" + +from pyln.client import Plugin + +plugin = Plugin() + + +@plugin.hook('openchannel') +def on_openchannel(openchannel, plugin, **kwargs): + plugin.log(repr(openchannel)) + reserve = plugin.options['reserve']['value'] + + if reserve is None: + return {'result': 'continue'} + else: + return {'result': 'continue', 'reserve': reserve} + + +plugin.add_option( + 'reserve', + None, + 'Absolute reserve to require from peers when accepting channels', +) + +plugin.run() diff --git a/tests/test_opening.py b/tests/test_opening.py index d05b40d35676..c3e1a1b21838 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1730,3 +1730,78 @@ def test_zeroconf_multichan_forward(node_factory): inv = l3.rpc.invoice(amount_msat=10000, label='lbl1', description='desc')['bolt11'] l1.rpc.pay(inv) assert l2.daemon.is_in_log(r'Chose a better channel: .*') + + +def test_zeroreserve(node_factory, bitcoind): + """Ensure we can set the reserves. + + 3 nodes: + - l1 enforces zeroreserve + - l2 enforces default reserve + - l3 enforces sub-dust reserves + """ + ZEROCONF = True + plugin_path = Path(__file__).parent / "plugins" / "zeroreserve.py" + opts = [ + { + 'plugin': str(plugin_path), + 'reserve': '0sat', + }, + {}, + { + 'plugin': str(plugin_path), + 'reserve': '123sat' + } + ] + l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + + l1.fundwallet(10**7) + l2.fundwallet(10**7) + l3.fundwallet(10**7) + + l1.connect(l2) + l2.connect(l3) + l3.connect(l1) + + l1.rpc.fundchannel(l2.info['id'], 10**6, reserve='0sat') + l2.rpc.fundchannel(l3.info['id'], 10**6) + l3.rpc.fundchannel(l1.info['id'], 10**6, reserve='321sat') + bitcoind.generate_block(1, wait_for_mempool=3) + wait_for(lambda: l1.channel_state(l2) == 'CHANNELD_NORMAL') + wait_for(lambda: l2.channel_state(l3) == 'CHANNELD_NORMAL') + wait_for(lambda: l3.channel_state(l1) == 'CHANNELD_NORMAL') + + # Now make sure we all agree on each others reserves + l1c1 = l1.rpc.listpeers(l2.info['id'])['peers'][0]['channels'][0] + l2c1 = l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0] + l2c2 = l2.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0] + l3c2 = l3.rpc.listpeers(l2.info['id'])['peers'][0]['channels'][0] + l3c3 = l3.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0] + l1c3 = l1.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0] + + # l1 imposed a 0sat reserve on l2, while l2 imposed the default 1% reserve on l1 + assert l1c1['their_channel_reserve_satoshis'] == l2c1['our_channel_reserve_satoshis'] == (0 if ZEROCONF else 546) + assert l1c1['our_channel_reserve_satoshis'] == l2c1['their_channel_reserve_satoshis'] == 10000 + + # l2 imposed the default 1% on l3, while l3 imposed a custom 123sat fee on l2 + assert l2c2['their_channel_reserve_satoshis'] == l3c2['our_channel_reserve_satoshis'] == 10000 + assert l2c2['our_channel_reserve_satoshis'] == l3c2['their_channel_reserve_satoshis'] == (123 if ZEROCONF else 546) + + # l3 imposed a custom 321sat fee on l1, while l1 imposed a custom 0sat fee on l3 + assert l3c3['their_channel_reserve_satoshis'] == l1c3['our_channel_reserve_satoshis'] == (321 if ZEROCONF else 546) + assert l3c3['our_channel_reserve_satoshis'] == l1c3['their_channel_reserve_satoshis'] == (0 if ZEROCONF else 546) + + # Now do some drain tests on c1, as that should be drainable + # completely by l2 being the fundee + l1.rpc.keysend(l2.info['id'], 10 * 7) # Something above dust for sure + l2.drain(l1) + + # Remember that this is the reserve l1 imposed on l2, so l2 can drain completely + l2c1 = l2.rpc.listpeers(l1.info['id'])['peers'][0]['channels'][0] + + # And despite us briefly being above dust (with a to_us output), + # closing should result in the output being trimmed again since we + # dropped below dust again. + c = l2.rpc.close(l1.info['id']) + decoded = bitcoind.rpc.decoderawtransaction(c['tx']) + assert len(decoded['vout']) == 1 From 67467213cb246386c9615add6ecff3dbca7c2249 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 22 Jun 2022 15:31:27 +0200 Subject: [PATCH 1412/1530] opening: Add `dev-allowdustreserve` option to opt into dust reserves Technically this is a non-conformance with the spec, hence the `dev` flag to opt-in, however I'm being told that it is also implemented in other implementations. I'll follow this up with a proposal to the spec to remove the checks we now bypass. --- lightningd/lightningd.h | 6 ++++++ lightningd/opening_control.c | 25 ++++++++++++------------ lightningd/options.c | 8 ++++++++ openingd/openingd.c | 38 +++++++++++++----------------------- openingd/openingd_wire.csv | 4 ++++ 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index bfacc17e76ac..dbcfc04238c8 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -70,6 +70,12 @@ struct config { /* EXPERIMENTAL: offers support */ bool exp_offers; + + /* Allow dust reserves (including 0) when being called via + * `fundchannel` or in the `openchannel` hook. This is a + * slight spec incompatibility, but implementations do this + * already. */ + bool allowdustreserve; }; typedef STRMAP(const char *) alt_subdaemon_map; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index bd0887947cd5..a64764c8577e 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -946,18 +946,19 @@ bool peer_start_openingd(struct peer *peer, struct peer_fd *peer_fd) msg = towire_openingd_init(NULL, - chainparams, - peer->ld->our_features, - peer->their_features, - &uc->our_config, - max_to_self_delay, - min_effective_htlc_capacity, - &uc->local_basepoints, - &uc->local_funding_pubkey, - uc->minimum_depth, - feerate_min(peer->ld, NULL), - feerate_max(peer->ld, NULL), - IFDEV(peer->ld->dev_force_tmp_channel_id, NULL)); + chainparams, + peer->ld->our_features, + peer->their_features, + &uc->our_config, + max_to_self_delay, + min_effective_htlc_capacity, + &uc->local_basepoints, + &uc->local_funding_pubkey, + uc->minimum_depth, + feerate_min(peer->ld, NULL), + feerate_max(peer->ld, NULL), + IFDEV(peer->ld->dev_force_tmp_channel_id, NULL), + peer->ld->config.allowdustreserve); subd_send_msg(uc->open_daemon, take(msg)); return true; } diff --git a/lightningd/options.c b/lightningd/options.c index ef8f7b00830a..c40f371d75e8 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -811,6 +811,8 @@ static const struct config testnet_config = { .connection_timeout_secs = 60, .exp_offers = IFEXPERIMENTAL(true, false), + + .allowdustreserve = false, }; /* aka. "Dude, where's my coins?" */ @@ -875,6 +877,8 @@ static const struct config mainnet_config = { .connection_timeout_secs = 60, .exp_offers = IFEXPERIMENTAL(true, false), + + .allowdustreserve = false, }; static void check_config(struct lightningd *ld) @@ -1168,6 +1172,10 @@ static void register_opts(struct lightningd *ld) &ld->autolisten, "If true, listen on default port and announce if it seems to be a public interface"); + opt_register_arg("--dev-allowdustreserve", opt_set_bool_arg, opt_show_bool, + &ld->config.allowdustreserve, + "If true, we allow the `fundchannel` RPC command and the `openchannel` plugin hook to set a reserve that is below the dust limit."); + opt_register_arg("--proxy", opt_add_proxy_addr, NULL, ld,"Set a socks v5 proxy IP address and port"); opt_register_arg("--tor-service-password", opt_set_talstr, NULL, diff --git a/openingd/openingd.c b/openingd/openingd.c index fa1af86784d3..40a16a49adad 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -101,6 +101,8 @@ struct state { struct feature_set *our_features; struct amount_sat *reserve; + + bool allowdustreserve; }; /*~ If we can't agree on parameters, we fail to open the channel. @@ -960,7 +962,6 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) /* This reserves 1% of the channel (rounded up) */ set_reserve(state, state->remoteconf.dust_limit); -#ifndef ZERORESERVE /* Pending proposal to remove these limits. */ /* BOLT #2: * @@ -982,18 +983,6 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &state->remoteconf.dust_limit)); return NULL; } - if (amount_sat_greater(state->localconf.dust_limit, - state->remoteconf.channel_reserve)) { - negotiation_failed(state, - "Our dust limit %s" - " would be above their reserve %s", - type_to_string(tmpctx, struct amount_sat, - &state->localconf.dust_limit), - type_to_string(tmpctx, struct amount_sat, - &state->remoteconf.channel_reserve)); - return NULL; - } -#endif /* These checks are the same whether we're opener or accepter... */ if (!check_config_bounds(tmpctx, state->funding_sats, @@ -1434,17 +1423,18 @@ int main(int argc, char *argv[]) /*~ The very first thing we read from lightningd is our init msg */ msg = wire_sync_read(tmpctx, REQ_FD); if (!fromwire_openingd_init(state, msg, - &chainparams, - &state->our_features, - &state->their_features, - &state->localconf, - &state->max_to_self_delay, - &state->min_effective_htlc_capacity, - &state->our_points, - &state->our_funding_pubkey, - &state->minimum_depth, - &state->min_feerate, &state->max_feerate, - &force_tmp_channel_id)) + &chainparams, + &state->our_features, + &state->their_features, + &state->localconf, + &state->max_to_self_delay, + &state->min_effective_htlc_capacity, + &state->our_points, + &state->our_funding_pubkey, + &state->minimum_depth, + &state->min_feerate, &state->max_feerate, + &force_tmp_channel_id, + &state->allowdustreserve)) master_badmsg(WIRE_OPENINGD_INIT, msg); #if DEVELOPER diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index 4f77446f2f32..4ab658773c96 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -24,6 +24,10 @@ msgdata,openingd_init,minimum_depth,u32, msgdata,openingd_init,min_feerate,u32, msgdata,openingd_init,max_feerate,u32, msgdata,openingd_init,dev_temporary_channel_id,?byte,32 +# Do we allow `fundchannel` or the `openchannel` hook to set sub-dust +# reserves? This is explicitly required by the spec for safety +# reasons, but some implementations and users keep asking for it. +msgdata,openingd_init,allowdustreserve,bool, # Openingd->master: they offered channel, should we continue? msgtype,openingd_got_offer,6005 From 1bd3d8d9f98991c1958dda66ffaae3e0887fbd98 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 24 Jun 2022 12:55:09 +0200 Subject: [PATCH 1413/1530] openingd: Remove dust check for reserve imposed on us This check, while in line with the specification, would cause issues in mixed setups when the funder or fundee allows dust reserves, but the counterparty does not. It is not an issue for the non-dust reserve node since in this case it's the peer giving us more flexibility not the other way around. --- openingd/common.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/openingd/common.c b/openingd/common.c index 493ee447250d..92a0e9b22268 100644 --- a/openingd/common.c +++ b/openingd/common.c @@ -180,26 +180,6 @@ bool check_config_bounds(const tal_t *ctx, return false; } -#ifndef ZERORESERVE - /* BOLT #2: - * - * The receiving node MUST fail the channel if: - *... - * - `dust_limit_satoshis` is greater than `channel_reserve_satoshis`. - */ - if (amount_sat_greater(remoteconf->dust_limit, - remoteconf->channel_reserve)) { - *err_reason = tal_fmt(ctx, - "dust_limit_satoshis %s" - " too large for channel_reserve_satoshis %s", - type_to_string(ctx, struct amount_sat, - &remoteconf->dust_limit), - type_to_string(ctx, struct amount_sat, - &remoteconf->channel_reserve)); - return false; - } -#endif - return true; } From 54b4baabb00d046c0725aeabb2b1737143a896c1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 24 Jun 2022 12:58:26 +0200 Subject: [PATCH 1414/1530] opening: Add `dev-allowdustreserve` option to opt into dust reserves Technically this is a non-conformance with the spec, hence the `dev` flag to opt-in, however I'm being told that it is also implemented in other implementations. I'll follow this up with a proposal to the spec to remove the checks we now bypass. --- Makefile | 2 -- doc/lightning-listconfigs.7.md | 4 ++- doc/schemas/listconfigs.schema.json | 4 +++ openingd/openingd.c | 47 +++++++++++++++-------------- tests/test_opening.py | 21 +++++++------ 5 files changed, 43 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 71c3c8daa2dc..5e1d113d18c0 100644 --- a/Makefile +++ b/Makefile @@ -258,8 +258,6 @@ endif CPPFLAGS += -DBINTOPKGLIBEXECDIR="\"$(shell sh tools/rel.sh $(bindir) $(pkglibexecdir))\"" CFLAGS = $(CPPFLAGS) $(CWARNFLAGS) $(CDEBUGFLAGS) $(COPTFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . -I$(CPATH) $(SQLITE3_CFLAGS) $(POSTGRES_INCLUDE) $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DJSMN_PARENT_LINKS $(PIE_CFLAGS) $(COMPAT_CFLAGS) -DBUILD_ELEMENTS=1 -CFLAGS += -DZERORESERVE=1 - # If CFLAGS is already set in the environment of make (to whatever value, it # does not matter) then it would export it to subprocesses with the above value # we set, including CWARNFLAGS which by default contains -Wall -Werror. This diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 7a4ed25fad00..184083134f38 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -99,6 +99,7 @@ On success, an object is returned, containing: - **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one) - **fetchinvoice-noconnect** (boolean, optional): `featchinvoice-noconnect` fileds from config or cmdline, or default - **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any +- **dev-allowdustreserve** (boolean, optional): Whether we allow setting dust reserves [comment]: # (GENERATE-FROM-SCHEMA-END) @@ -216,4 +217,5 @@ RESOURCES --------- Main web site: -[comment]: # ( SHA256STAMP:dcab86f29b946fed925de5e05cb79faa03cc4421cefeab3561a596ed5e64962d) + +[comment]: # ( SHA256STAMP:310cc00ef62e7075d5d2588b0492c2dd96f507cc739f67d56ccc6c4f3135bca5) diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index ec7eabc5bba0..db1d451e06f1 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -286,6 +286,10 @@ "tor-service-password": { "type": "string", "description": "`tor-service-password` field from config or cmdline, if any" + }, + "dev-allowdustreserve": { + "type": "boolean", + "description": "Whether we allow setting dust reserves" } } } diff --git a/openingd/openingd.c b/openingd/openingd.c index 40a16a49adad..9bdc3a264637 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -146,27 +146,28 @@ static void set_reserve_absolute(struct state * state, const struct amount_sat d { status_debug("Setting their reserve to %s", type_to_string(tmpctx, struct amount_sat, &reserve_sat)); -#ifdef ZERORESERVE - state->localconf.channel_reserve = reserve_sat; -#else - /* BOLT #2: - * - * The sending node: - *... - * - MUST set `channel_reserve_satoshis` greater than or equal to - * `dust_limit_satoshis` from the `open_channel` message. - */ - if (amount_sat_greater(dust_limit, reserve_sat)) { - status_debug( - "Their reserve is too small, bumping to dust_limit: %s < %s", - type_to_string(tmpctx, struct amount_sat, &reserve_sat), - type_to_string(tmpctx, struct amount_sat, &dust_limit)); - state->localconf.channel_reserve - = dust_limit; - } else { + if (state->allowdustreserve) { state->localconf.channel_reserve = reserve_sat; + } else { + /* BOLT #2: + * + * The sending node: + *... + * - MUST set `channel_reserve_satoshis` greater than or equal + *to `dust_limit_satoshis` from the `open_channel` message. + */ + if (amount_sat_greater(dust_limit, reserve_sat)) { + status_debug("Their reserve is too small, bumping to " + "dust_limit: %s < %s", + type_to_string(tmpctx, struct amount_sat, + &reserve_sat), + type_to_string(tmpctx, struct amount_sat, + &dust_limit)); + state->localconf.channel_reserve = dust_limit; + } else { + state->localconf.channel_reserve = reserve_sat; + } } -#endif } /* We always set channel_reserve_satoshis to 1%, rounded down. */ @@ -464,8 +465,8 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) type_to_string(msg, struct channel_id, &state->channel_id)); -#ifndef ZERORESERVE - if (amount_sat_greater(state->remoteconf.dust_limit, + if (!state->allowdustreserve && + amount_sat_greater(state->remoteconf.dust_limit, state->localconf.channel_reserve)) { negotiation_failed(state, "dust limit %s" @@ -476,7 +477,6 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) &state->localconf.channel_reserve)); return NULL; } -#endif if (!check_config_bounds(tmpctx, state->funding_sats, state->feerate_per_kw, @@ -972,7 +972,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) * - MUST set `dust_limit_satoshis` less than or equal to * `channel_reserve_satoshis` from the `open_channel` message. */ - if (amount_sat_greater(state->remoteconf.dust_limit, + if (!state->allowdustreserve && + amount_sat_greater(state->remoteconf.dust_limit, state->localconf.channel_reserve)) { negotiation_failed(state, "Our channel reserve %s" diff --git a/tests/test_opening.py b/tests/test_opening.py index c3e1a1b21838..ebc41f4c2c23 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1740,17 +1740,20 @@ def test_zeroreserve(node_factory, bitcoind): - l2 enforces default reserve - l3 enforces sub-dust reserves """ - ZEROCONF = True plugin_path = Path(__file__).parent / "plugins" / "zeroreserve.py" opts = [ { 'plugin': str(plugin_path), 'reserve': '0sat', + 'dev-allowdustreserve': True, + }, + { + 'dev-allowdustreserve': True, }, - {}, { 'plugin': str(plugin_path), - 'reserve': '123sat' + 'reserve': '123sat', + 'dev-allowdustreserve': True, } ] l1, l2, l3 = node_factory.get_nodes(3, opts=opts) @@ -1780,16 +1783,16 @@ def test_zeroreserve(node_factory, bitcoind): l1c3 = l1.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0] # l1 imposed a 0sat reserve on l2, while l2 imposed the default 1% reserve on l1 - assert l1c1['their_channel_reserve_satoshis'] == l2c1['our_channel_reserve_satoshis'] == (0 if ZEROCONF else 546) - assert l1c1['our_channel_reserve_satoshis'] == l2c1['their_channel_reserve_satoshis'] == 10000 + assert l1c1['their_reserve_msat'] == l2c1['our_reserve_msat'] == Millisatoshi('0sat') + assert l1c1['our_reserve_msat'] == l2c1['their_reserve_msat'] == Millisatoshi('10000sat') # l2 imposed the default 1% on l3, while l3 imposed a custom 123sat fee on l2 - assert l2c2['their_channel_reserve_satoshis'] == l3c2['our_channel_reserve_satoshis'] == 10000 - assert l2c2['our_channel_reserve_satoshis'] == l3c2['their_channel_reserve_satoshis'] == (123 if ZEROCONF else 546) + assert l2c2['their_reserve_msat'] == l3c2['our_reserve_msat'] == Millisatoshi('10000sat') + assert l2c2['our_reserve_msat'] == l3c2['their_reserve_msat'] == Millisatoshi('123sat') # l3 imposed a custom 321sat fee on l1, while l1 imposed a custom 0sat fee on l3 - assert l3c3['their_channel_reserve_satoshis'] == l1c3['our_channel_reserve_satoshis'] == (321 if ZEROCONF else 546) - assert l3c3['our_channel_reserve_satoshis'] == l1c3['their_channel_reserve_satoshis'] == (0 if ZEROCONF else 546) + assert l3c3['their_reserve_msat'] == l1c3['our_reserve_msat'] == Millisatoshi('321sat') + assert l3c3['our_reserve_msat'] == l1c3['their_reserve_msat'] == Millisatoshi('0sat') # Now do some drain tests on c1, as that should be drainable # completely by l2 being the fundee From bdda62e1a44ce4be0cc6a3ec9507d21cef291b91 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 24 Jun 2022 12:42:45 +0200 Subject: [PATCH 1415/1530] pytest: Add test for mixed zeroreserve funding Tests that we can have zeroreserve nodes accept and open new channels with non-zeroreserve nodes, without throwing errors --- tests/test_opening.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/test_opening.py b/tests/test_opening.py index ebc41f4c2c23..987151517de6 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1807,4 +1807,35 @@ def test_zeroreserve(node_factory, bitcoind): # dropped below dust again. c = l2.rpc.close(l1.info['id']) decoded = bitcoind.rpc.decoderawtransaction(c['tx']) - assert len(decoded['vout']) == 1 + # Elements has a change output always + assert len(decoded['vout']) == 1 if TEST_NETWORK == 'regtest' else 2 + + +def test_zeroreserve_mixed(node_factory, bitcoind): + """l1 runs with zeroreserve, l2 and l3 without, should still work + + Basically tests that l1 doesn't get upset when l2 allows us to + drop below dust. + + """ + plugin_path = Path(__file__).parent / "plugins" / "zeroreserve.py" + opts = [ + { + 'plugin': str(plugin_path), + 'reserve': '0sat', + 'dev-allowdustreserve': True, + }, { + 'dev-allowdustreserve': False, + }, { + 'dev-allowdustreserve': False, + } + ] + l1, l2, l3 = node_factory.get_nodes(3, opts=opts) + l1.fundwallet(10**7) + l3.fundwallet(10**7) + + l1.connect(l2) + l3.connect(l1) + + l1.rpc.fundchannel(l2.info['id'], 10**6, reserve='0sat') + l3.rpc.fundchannel(l1.info['id'], 10**6) From 759fcb64a8c28a90a7659c2b1791ce9993067796 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 6 Jul 2022 17:38:49 +0200 Subject: [PATCH 1416/1530] pay: If the channel_hint matches our allocation allow it It means we consume the channel completely to the best of our knowledge, so let that through. Changelog-Fixed: pay: Squeezed out the last `msat` from our local view of the network --- plugins/libplugin-pay.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/libplugin-pay.c b/plugins/libplugin-pay.c index 4ca2a8a4c34b..354a18646464 100644 --- a/plugins/libplugin-pay.c +++ b/plugins/libplugin-pay.c @@ -515,8 +515,8 @@ static bool payment_chanhints_apply_route(struct payment *p, bool remove) /* For all channels we check that they have a * sufficiently large estimated capacity to have some * chance of succeeding. */ - apply &= amount_msat_greater(curhint->estimated_capacity, - curhop->amount); + apply &= amount_msat_greater_eq(curhint->estimated_capacity, + curhop->amount); if (!apply) { /* This can happen in case of multiple @@ -530,6 +530,15 @@ static bool payment_chanhints_apply_route(struct payment *p, bool remove) type_to_string(tmpctx, struct short_channel_id_dir, &curhint->scid)); + paymod_log( + p, LOG_DBG, + "Capacity: estimated_capacity=%s, hop_amount=%s. " + "HTLC Budget: htlc_budget=%d, local=%d", + type_to_string(tmpctx, struct amount_msat, + &curhint->estimated_capacity), + type_to_string(tmpctx, struct amount_msat, + &curhop->amount), + curhint->htlc_budget, curhint->local); return false; } } From 493a0dfcd4e4ec8f53389b837b07c85be82efb30 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 16 Aug 2022 17:55:34 +0200 Subject: [PATCH 1417/1530] pytest: Exercise all dust zeroreserve case This just shows we need to add that constraint. Reported-by: Rusty Russell <@rustyrussell> --- tests/test_opening.py | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 987151517de6..8aaa89454f5d 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1839,3 +1839,51 @@ def test_zeroreserve_mixed(node_factory, bitcoind): l1.rpc.fundchannel(l2.info['id'], 10**6, reserve='0sat') l3.rpc.fundchannel(l1.info['id'], 10**6) + + +@pytest.mark.xfail("Restriction not implemented yet", strict=True) +def test_zeroreserve_alldust(node_factory): + """If we allow dust reserves we need larger fundings + + This is because we might have up to + + allhtlcs = (local.max_concurrent_htlcs + remote.max_concurrent_htlcs) + alldust = allhlcs * min(local.dust, remote.dust) + + allocated to HTLCs in flight, reducing both direct outputs to + dust. This could leave us with no outs on the commitment, is + therefore invalid. + + Parameters are as follows: + - Regtest: + - max_concurrent_htlcs = 483 + - dust = 546sat + - minfunding = (483 * 2 + 2) * 546sat = 528528sat + - Mainnet: + - max_concurrent_htlcs = 30 + - dust = 546sat + - minfunding = (30 * 2 + 2) * 546sat = 33852s + """ + plugin_path = Path(__file__).parent / "plugins" / "zeroreserve.py" + l1, l2 = node_factory.get_nodes(2, opts=[{ + 'plugin': plugin_path, + 'reserve': '0sat', + 'dev-allowdustreserve': True + }] * 2) + maxhtlc = 483 + mindust = 546 + minfunding = (maxhtlc * 2 + 2) * mindust + + l1.fundwallet(10**6) + error = (f'channel funding {minfunding}sat too small for chosen parameters: ' + f'a total of {maxhtlc * 2} HTLCs with dust value {mindust}sat would ' + f'result in a commitment_transaction without outputs') + + # This is right on the edge, and should fail + with pytest.raises(RpcError, match=error): + l1.connect(l2) + l1.rpc.fundchannel(l2.info['id'], minfunding) + + # Now try with just a bit more + l1.connect(l2) + l1.rpc.fundchannel(l2.info['id'], minfunding + 1) From 75fe1c1a668cc80e63ca13bb80cd448f2ad7491f Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 17 Aug 2022 09:40:52 +0200 Subject: [PATCH 1418/1530] common: Add multiplication primitives for amount_msat and amount_sat The scale variants weren't usable since they use `double` as a scale factor, which in turn causes imprecisions. --- common/amount.c | 16 ++++++++++++++++ common/amount.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/common/amount.c b/common/amount.c index e17a6804433b..ca48d86e0af9 100644 --- a/common/amount.c +++ b/common/amount.c @@ -507,6 +507,22 @@ struct amount_sat amount_sat_div(struct amount_sat sat, u64 div) return sat; } +bool amount_sat_mul(struct amount_sat *res, struct amount_sat sat, u64 mul) +{ + if ( mul_overflows_u64(sat.satoshis, mul)) + return false; + res->satoshis = sat.satoshis * mul; + return true; +} + +bool amount_msat_mul(struct amount_msat *res, struct amount_msat msat, u64 mul) +{ + if ( mul_overflows_u64(msat.millisatoshis, mul)) + return false; + res->millisatoshis = msat.millisatoshis * mul; + return true; +} + bool amount_msat_fee(struct amount_msat *fee, struct amount_msat amt, u32 fee_base_msat, diff --git a/common/amount.h b/common/amount.h index b16e925930cc..de3dd5a3a764 100644 --- a/common/amount.h +++ b/common/amount.h @@ -92,6 +92,9 @@ WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val, struct amount_msat amount_msat_div(struct amount_msat msat, u64 div); struct amount_sat amount_sat_div(struct amount_sat sat, u64 div); +bool amount_sat_mul(struct amount_sat *res, struct amount_sat sat, u64 mul); +bool amount_msat_mul(struct amount_msat *res, struct amount_msat msat, u64 mul); + /* Is a == b? */ bool amount_sat_eq(struct amount_sat a, struct amount_sat b); bool amount_msat_eq(struct amount_msat a, struct amount_msat b); From 774d16a72e125e4ae4e312b9e3307261983bec0e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Tue, 16 Aug 2022 17:12:37 +0200 Subject: [PATCH 1419/1530] openingd: Fail if dust and max_htlcs result in 0output commitment tx Prior to this we might end up with a commitment transaction without any outputs, if combined with `--dev-allowdustreserve`. Otherwise the reserve being larger than dust means the funder could not drop its direct output to be below dust. Reported-by: Rusty Russell <@rustyrussell> --- openingd/openingd.c | 41 +++++++++++++++++++++++++++++++++++++++++ tests/test_opening.py | 1 - 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/openingd/openingd.c b/openingd/openingd.c index 9bdc3a264637..e9de31a3dc74 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -478,6 +478,47 @@ static u8 *funder_channel_start(struct state *state, u8 channel_flags) return NULL; } + /* If we allow dust reserves, we might end up in a situation + * in which all the channel funds are allocated to HTLCs, + * leaving just dust to_us and to_them outputs. If the HTLCs + * themselves are dust as well, our commitment transaction is + * now invalid since it has no outputs at all, putting us in a + * weird situation where the channel cannot be closed + * unilaterally at all. (Thanks Rusty for identifying this + * edge case). */ + struct amount_sat alldust, mindust = + amount_sat_greater(state->remoteconf.dust_limit, + state->localconf.dust_limit) + ? state->localconf.dust_limit + : state->remoteconf.dust_limit; + size_t maxhtlcs = state->remoteconf.max_accepted_htlcs + + state->localconf.max_accepted_htlcs; + if (!amount_sat_mul(&alldust, mindust, maxhtlcs + 2)) { + negotiation_failed( + state, + "Overflow while computing total possible dust amount"); + return NULL; + } + + if (state->allowdustreserve && + feature_negotiated(state->our_features, state->their_features, + OPT_ZEROCONF) && + amount_sat_greater_eq(alldust, state->funding_sats)) { + negotiation_failed( + state, + "channel funding %s too small for chosen " + "parameters: a total of %zu HTLCs with dust value %s would " + "result in a commitment_transaction without outputs. " + "Please increase the funding amount or reduce the " + "max_accepted_htlcs to ensure at least one non-dust " + "output.", + type_to_string(tmpctx, struct amount_sat, + &state->funding_sats), + maxhtlcs, + type_to_string(tmpctx, struct amount_sat, &mindust)); + return NULL; + } + if (!check_config_bounds(tmpctx, state->funding_sats, state->feerate_per_kw, state->max_to_self_delay, diff --git a/tests/test_opening.py b/tests/test_opening.py index 8aaa89454f5d..c182fefcf547 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1841,7 +1841,6 @@ def test_zeroreserve_mixed(node_factory, bitcoind): l3.rpc.fundchannel(l1.info['id'], 10**6) -@pytest.mark.xfail("Restriction not implemented yet", strict=True) def test_zeroreserve_alldust(node_factory): """If we allow dust reserves we need larger fundings From 5a1c2447cb9d4bfe3970ecac6df6d5849bfab0c4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 20 Sep 2022 07:02:58 +0930 Subject: [PATCH 1420/1530] pyln-client: use f strings to concatenate JSON ids, handle older integer ids. We don't really support using pyln-client with older Core Lightning versions, but this is neater anyway. I checked: f-strings go back to python 3.6, so we can use them (I think this may be the first!). Suggested-by: @MiWCryptAnalytics Fixes: #5609 Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index 38193e03c0ec..d5e42d82d70f 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -335,7 +335,7 @@ def get_json_id(self, method, cmdprefix): if cmdprefix is None: cmdprefix = self.cmdprefix if cmdprefix: - this_id = cmdprefix + '/' + this_id + this_id = f'{cmdprefix}/{this_id}' return this_id def call(self, method, payload=None, cmdprefix=None): From 0db01c882f11de1f683ee1cf00e91c4f7fc5d725 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 21 Sep 2022 14:08:08 +0930 Subject: [PATCH 1421/1530] pytest: fix flake in test_sendcustommsg We assume that because we've told l3 to shut down, l2 already sees it as disconnected. But CI is ...slow... today! ``` # `l3` is disconnected and we can't send messages to it > assert(not l2.rpc.listpeers(l3.info['id'])['peers'][0]['connected']) E assert not True tests/test_misc.py:2218: AssertionError ``` Signed-off-by: Rusty Russell --- tests/test_misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index 9b846347f469..b77e9cbc029a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2215,7 +2215,7 @@ def test_sendcustommsg(node_factory): l1.rpc.sendcustommsg(node_id, msg) # `l3` is disconnected and we can't send messages to it - assert(not l2.rpc.listpeers(l3.info['id'])['peers'][0]['connected']) + wait_for(lambda: l2.rpc.listpeers(l3.info['id'])['peers'][0]['connected'] is False) with pytest.raises(RpcError, match=r'Peer is not connected'): l2.rpc.sendcustommsg(l3.info['id'], msg) From 43556126467126ee60fd629482db6747b15ade08 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 19 Sep 2022 14:50:06 +0200 Subject: [PATCH 1422/1530] gh: Mark some derived files as such These should never be merged manually, rather have the local copy remain untouched and ask them to regenerate altogether. Also not showing in Github so reviewers don't get confused. --- .gitattributes | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitattributes b/.gitattributes index ddec20639f42..3e20865f7058 100644 --- a/.gitattributes +++ b/.gitattributes @@ -12,3 +12,9 @@ statements_gettextgen.po linguist-generated=true *_wiregen.? linguist-generated=true *_printgen.? linguist-generated=true +# The following are marked as binary and generated since they can be +# easily overwritten and don't need deep review. +cln-grpc/proto/node.proto -text -diff linguist-generated=true +cln-grpc/src/convert.rs -text -diff linguist-generated=true +cln-rpc/src/model.rs -text -diff linguist-generated=true +contrib/pyln-testing/pyln/testing/node_pb2.py -text -diff linguist-generated=true \ No newline at end of file From 41502be60ac619dd78b64b15987029182171a134 Mon Sep 17 00:00:00 2001 From: antongalitch <94177756+antongalitch@users.noreply.github.com> Date: Wed, 10 Aug 2022 18:02:43 +0200 Subject: [PATCH 1423/1530] Fix a small typo --- plugins/bkpr/db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/bkpr/db.c b/plugins/bkpr/db.c index 4efe43b2bbe4..e0774f115fda 100644 --- a/plugins/bkpr/db.c +++ b/plugins/bkpr/db.c @@ -19,7 +19,7 @@ static void migration_remove_dupe_lease_fees(struct plugin *p, struct db *db); /* Do not reorder or remove elements from this array. * It is used to migrate existing databases from a prevoius state, based on - * string indicies */ + * string indices */ static struct migration db_migrations[] = { {SQL("CREATE TABLE version (version INTEGER);"), NULL}, {SQL("INSERT INTO version VALUES (1);"), NULL}, From ce0f5440733901984672757c9e4b1f93854c4e97 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 21 Sep 2022 13:27:37 +0930 Subject: [PATCH 1424/1530] keysend: try to find description in TLV. "Who needs specs?" FFS... Signed-off-by: Rusty Russell Changelog-Added: Protocol: `keysend` will now attach the longest valid text field in the onion to the invoice (so you can have Sphinx.chat users spam you!) --- plugins/keysend.c | 27 +++++++++++++++++++++------ tests/test_pay.py | 26 +++++++++++++++++++++----- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/plugins/keysend.c b/plugins/keysend.c index 47fdc8fd5903..1a88b7ca568b 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -341,9 +341,8 @@ static struct command_result *htlc_accepted_call(struct command *cmd, struct sha256 payment_hash; size_t max; struct tlv_tlv_payload *payload; - struct tlv_field *preimage_field = NULL, *unknown_field = NULL; + struct tlv_field *preimage_field = NULL, *unknown_field = NULL, *desc_field = NULL; bigsize_t s; - struct tlv_field *field; struct keysend_in *ki; struct out_req *req; struct timeabs now = time_now(); @@ -387,15 +386,21 @@ static struct command_result *htlc_accepted_call(struct command *cmd, } /* Try looking for the field that contains the preimage */ - for (int i=0; ifields); i++) { - field = &payload->fields[i]; + for (size_t i = 0; i < tal_count(payload->fields); i++) { + struct tlv_field *field = &payload->fields[i]; if (field->numtype == PREIMAGE_TLV_TYPE) { preimage_field = field; - break; + continue; } else if (field->numtype % 2 == 0 && field->meta == NULL) { /* This can only happen with FROMWIRE_TLV_ANY_TYPE! */ unknown_field = field; } + + /* Longest (unknown) text field wins. */ + if (!field->meta + && utf8_check(field->value, field->length) + && (!desc_field || field->length > desc_field->length)) + desc_field = field; } /* If we don't have a preimage field then this is not a keysend, let @@ -464,7 +469,17 @@ static struct command_result *htlc_accepted_call(struct command *cmd, plugin_log(cmd->plugin, LOG_INFORM, "Inserting a new invoice for keysend with payment_hash %s", type_to_string(tmpctx, struct sha256, &payment_hash)); json_add_string(req->js, "amount_msat", "any"); json_add_string(req->js, "label", ki->label); - json_add_string(req->js, "description", "Spontaneous incoming payment through keysend"); + if (desc_field) { + const char *desc = tal_fmt(tmpctx, "keysend: %.*s", + (int)desc_field->length, + (const char *)desc_field->value); + json_add_string(req->js, "description", desc); + /* Don't exceed max possible desc length! */ + if (strlen(desc) > 1023) + json_add_bool(req->js, "deschashonly", true); + } else { + json_add_string(req->js, "description", "keysend"); + } json_add_preimage(req->js, "preimage", &ki->payment_preimage); return send_outreq(cmd->plugin, req); diff --git a/tests/test_pay.py b/tests/test_pay.py index 72745d5f4c9d..706a9cd86193 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3585,17 +3585,33 @@ def test_keysend_extra_tlvs(node_factory): ] ) - # Send an indirect one from l1 to l3 l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: 'FEEDC0DE'}) - invs = l2.rpc.listinvoices()['invoices'] - assert(len(invs) == 1) + inv = only_one(l2.rpc.listinvoices()['invoices']) assert(l2.daemon.is_in_log(r'plugin-sphinx-receiver.py.*extratlvs.*133773310.*feedc0de')) - inv = invs[0] assert(inv['amount_received_msat'] >= Millisatoshi(amt)) + assert inv['description'] == 'keysend' + l2.rpc.delinvoice(inv['label'], 'paid') # Now try again with the TLV type in extra_tlvs as string: - l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: 'FEEDC0DE'}) + l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: b'hello there'.hex()}) + inv = only_one(l2.rpc.listinvoices()['invoices']) + assert inv['description'] == 'keysend: hello there' + l2.rpc.delinvoice(inv['label'], 'paid') + + # We can (just!) fit a giant description in. + l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: (b'a' * 1100).hex()}) + inv = only_one(l2.rpc.listinvoices()['invoices']) + assert inv['description'] == 'keysend: ' + 'a' * 1100 + l2.rpc.delinvoice(inv['label'], 'paid') + + # Now try with some special characters + ksinfo = """💕 ₿"' +More info +""" + l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: bytes(ksinfo, encoding='utf8').hex()}) + inv = only_one(l2.rpc.listinvoices()['invoices']) + assert inv['description'] == 'keysend: ' + ksinfo def test_keysend_routehint(node_factory): From df4b477e88d2b93bb030d27b23ea9f12c9b3031c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 21 Sep 2022 13:39:37 +0930 Subject: [PATCH 1425/1530] keysend: allow extratlvs parameter, even in non-experimental mode. Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `keysend` now has `extratlvs` option in non-EXPERIMENTAL builds. --- doc/lightning-keysend.7.md | 4 +++- plugins/keysend.c | 20 ++------------------ tests/test_pay.py | 2 +- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/doc/lightning-keysend.7.md b/doc/lightning-keysend.7.md index 39215a7e99ea..794934cacb0a 100644 --- a/doc/lightning-keysend.7.md +++ b/doc/lightning-keysend.7.md @@ -4,7 +4,7 @@ lightning-keysend -- Send funds to a node without an invoice SYNOPSIS -------- -**keysend** *destination* *msatoshi* [*label*] [*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*] +**keysend** *destination* *msatoshi* [*label*] [*maxfeepercent*] [*retry\_for*] [*maxdelay*] [*exemptfee*] [*extratlvs*] DESCRIPTION ----------- @@ -36,6 +36,8 @@ Unlike lightning-pay(7), issuing the same `keysend` commands multiple times will Until *retry_for* seconds passes (default: 60), the command will keep finding routes and retrying the payment. However, a payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case. +*extratlvs* is an optional dictionary of additional fields to insert into the final tlv. The format is 'fieldnumber': 'hexstring'. + When using *lightning-cli*, you may skip optional parameters by using *null*. Alternatively, use **-k** option to provide parameters by name. diff --git a/plugins/keysend.c b/plugins/keysend.c index 1a88b7ca568b..d9d298709825 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -49,9 +49,7 @@ static struct keysend_data *keysend_init(struct payment *p) randombytes_buf(&d->preimage, sizeof(d->preimage)); ccan_sha256(&payment_hash, &d->preimage, sizeof(d->preimage)); p->payment_hash = tal_dup(p, struct sha256, &payment_hash); -#if EXPERIMENTAL_FEATURES d->extra_tlvs = NULL; -#endif return d; } else { /* If we are a child payment (retry or split) we copy the @@ -92,7 +90,6 @@ static void keysend_cb(struct keysend_data *d, struct payment *p) { tlvstream_set_raw(&last_payload->tlv_payload->fields, PREIMAGE_TLV_TYPE, &d->preimage, sizeof(struct preimage)); -#if EXPERIMENTAL_FEATURES if (d->extra_tlvs != NULL) { for (size_t i = 0; i < tal_count(d->extra_tlvs); i++) { struct tlv_field *f = &d->extra_tlvs[i]; @@ -100,7 +97,6 @@ static void keysend_cb(struct keysend_data *d, struct payment *p) { f->numtype, f->value, f->length); } } -#endif return payment_continue(p); } @@ -148,9 +144,7 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, u32 *maxdelay; unsigned int *retryfor; struct route_info **hints; -#if EXPERIMENTAL_FEATURES struct tlv_field *extra_fields; -#endif #if DEVELOPER bool *use_shadow; @@ -165,12 +159,10 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, p_opt_def("maxdelay", param_number, &maxdelay, maxdelay_default), p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)), + p_opt("extratlvs", param_extra_tlvs, &extra_fields), + p_opt("routehints", param_routehint_array, &hints), #if DEVELOPER p_opt_def("use_shadow", param_bool, &use_shadow, true), -#endif - p_opt("routehints", param_routehint_array, &hints), -#if EXPERIMENTAL_FEATURES - p_opt("extratlvs", param_extra_tlvs, &extra_fields), #endif NULL)) return command_param_failed(); @@ -210,10 +202,8 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf, p->constraints.cltv_budget = *maxdelay; -#if EXPERIMENTAL_FEATURES payment_mod_keysend_get_data(p)->extra_tlvs = tal_steal(p, extra_fields); -#endif payment_mod_exemptfee_get_data(p)->amount = *exemptfee; #if DEVELOPER @@ -365,14 +355,8 @@ static struct command_result *htlc_accepted_call(struct command *cmd, return htlc_accepted_continue(cmd, NULL); } -#if EXPERIMENTAL_FEATURES /* Note: This is a magic pointer value, not an actual array */ allowed = cast_const(u64 *, FROMWIRE_TLV_ANY_TYPE); -#else - /* We explicitly allow our type. */ - allowed = tal_arr(cmd, u64, 1); - allowed[0] = PREIMAGE_TLV_TYPE; -#endif payload = tlv_tlv_payload_new(cmd); if (!fromwire_tlv(&rawpayload, &max, tlvs_tlv_tlv_payload, TLVS_ARRAY_SIZE_tlv_tlv_payload, diff --git a/tests/test_pay.py b/tests/test_pay.py index 706a9cd86193..5b74135a28e8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3568,7 +3568,7 @@ def test_keysend(node_factory): l3.rpc.keysend(l4.info['id'], amt) -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "Requires extratlvs option") +@unittest.skipIf(not EXPERIMENTAL_FEATURES, "Requires experimental-accept-extra-tlv-types option") def test_keysend_extra_tlvs(node_factory): """Use the extratlvs option to deliver a message with sphinx' TLV type. """ From 0868fa9f1edb2cb7430ca5ecf436ecfead0ac459 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 21 Sep 2022 13:39:51 +0930 Subject: [PATCH 1426/1530] lightningd: allow extra tlv types in non-experimental mode. The old `experimental-accept-extra-tlv-types` is now `accept-htlc-tlv-types`. Signed-off-by: Rusty Russell Changelog-Added: Config: `accept-htlc-tlv-types` lets us accept unknown even HTLC TLV fields we would normally reject on parsing (was EXPERIMENTAL-only `experimental-accept-extra-tlv-types`). --- doc/lightning-listconfigs.7.md | 6 +++--- doc/lightningd-config.5.md | 6 ++++++ doc/schemas/listconfigs.schema.json | 6 +++++- doc/undoc-flags.json | 3 +-- lightningd/options.c | 29 ++++++++++++++++------------- tests/test_pay.py | 12 +++++++++--- 6 files changed, 40 insertions(+), 22 deletions(-) diff --git a/doc/lightning-listconfigs.7.md b/doc/lightning-listconfigs.7.md index 184083134f38..d6f65b12891e 100644 --- a/doc/lightning-listconfigs.7.md +++ b/doc/lightning-listconfigs.7.md @@ -97,7 +97,8 @@ On success, an object is returned, containing: - **log-timestamps** (boolean, optional): `log-timestamps` field from config or cmdline, or default - **force-feerates** (string, optional): force-feerate configuration setting, if any - **subdaemon** (string, optional): `subdaemon` fields from config or cmdline if any (can be more than one) -- **fetchinvoice-noconnect** (boolean, optional): `featchinvoice-noconnect` fileds from config or cmdline, or default +- **fetchinvoice-noconnect** (boolean, optional): `fetchinvoice-noconnect` fields from config or cmdline, or default +- **accept-htlc-tlv-types** (string, optional): `accept-extra-tlvs-type` fields from config or cmdline, or not present - **tor-service-password** (string, optional): `tor-service-password` field from config or cmdline, if any - **dev-allowdustreserve** (boolean, optional): Whether we allow setting dust reserves @@ -217,5 +218,4 @@ RESOURCES --------- Main web site: - -[comment]: # ( SHA256STAMP:310cc00ef62e7075d5d2588b0492c2dd96f507cc739f67d56ccc6c4f3135bca5) +[comment]: # ( SHA256STAMP:5871ac751654339ed65ab905d61f0bc3afbb8576a33a5c4e9a73d2084f438582) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 6a07cd14389f..40815c87525e 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -424,6 +424,12 @@ the outgoing is redeemed. might need to redeem this on-chain, so this is the number of blocks we have to do that. +* **accept-htlc-tlv-types**=*types* + + Normally HTLC onions which contain unknown even fields are rejected. +This option specifies that these (comma-separated) types are to be +accepted, and ignored. + ### Invoice control options: * **autocleaninvoice-cycle**=*SECONDS* [plugin `autoclean`] diff --git a/doc/schemas/listconfigs.schema.json b/doc/schemas/listconfigs.schema.json index db1d451e06f1..4377c4cea2d9 100644 --- a/doc/schemas/listconfigs.schema.json +++ b/doc/schemas/listconfigs.schema.json @@ -281,7 +281,11 @@ }, "fetchinvoice-noconnect": { "type": "boolean", - "description": "`featchinvoice-noconnect` fileds from config or cmdline, or default" + "description": "`fetchinvoice-noconnect` fields from config or cmdline, or default" + }, + "accept-htlc-tlv-types": { + "type": "string", + "description": "`accept-extra-tlvs-type` fields from config or cmdline, or not present" }, "tor-service-password": { "type": "string", diff --git a/doc/undoc-flags.json b/doc/undoc-flags.json index ef8b8fe23694..5f1822cea8b4 100644 --- a/doc/undoc-flags.json +++ b/doc/undoc-flags.json @@ -1,6 +1,5 @@ { "flags": [ - "experimental-accept-extra-tlv-types", "channel-fee-max-base-msat", "channel-fee-max-proportional-thousandths", "funder-fund-probability", @@ -18,4 +17,4 @@ "lease-fee-basis", "lease-funding-weight" ] -} \ No newline at end of file +} diff --git a/lightningd/options.c b/lightningd/options.c index c40f371d75e8..37845da18c2b 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -171,11 +171,10 @@ static char *fmt_force_feerates(const tal_t *ctx, const u32 *force_feerates) return ret; } -#if EXPERIMENTAL_FEATURES static char *opt_set_accept_extra_tlv_types(const char *arg, - struct lightningd *ld) + struct lightningd *ld) { - char *endp, **elements = tal_strsplit(NULL, arg, ",", STR_NO_EMPTY);; + char *endp, **elements = tal_strsplit(NULL, arg, ",", STR_NO_EMPTY); unsigned long long l; u64 u; for (int i = 0; elements[i] != NULL; i++) { @@ -193,7 +192,6 @@ static char *opt_set_accept_extra_tlv_types(const char *arg, tal_free(elements); return NULL; } -#endif /* Returns the number of wireaddr types already announced */ static size_t num_announced_types(enum wire_addr_type type, struct lightningd *ld) @@ -1182,11 +1180,9 @@ static void register_opts(struct lightningd *ld) &ld->tor_service_password, "Set a Tor hidden service password"); -#if EXPERIMENTAL_FEATURES - opt_register_arg("--experimental-accept-extra-tlv-types", + opt_register_arg("--accept-htlc-tlv-types", opt_set_accept_extra_tlv_types, NULL, ld, - "Comma separated list of extra TLV types to accept."); -#endif + "Comma separated list of extra HTLC TLV types to accept."); opt_register_early_noarg("--disable-dns", opt_set_invbool, &ld->config.use_dns, "Disable DNS lookups of peers"); @@ -1509,7 +1505,7 @@ static void add_config(struct lightningd *ld, const char *name, size_t len) { char *name0 = tal_strndup(tmpctx, name, len); - const char *answer = NULL; + char *answer = NULL; char buf[OPT_SHOW_LEN + sizeof("...")]; #if DEVELOPER @@ -1605,7 +1601,7 @@ static void add_config(struct lightningd *ld, if (ld->rgb) answer = tal_hexstr(name0, ld->rgb, 3); } else if (opt->cb_arg == (void *)opt_set_alias) { - answer = (const char *)ld->alias; + answer = (char *)ld->alias; } else if (opt->cb_arg == (void *)arg_log_to_file) { if (ld->logfiles) json_add_opt_log_to_files(response, name0, ld->logfiles); @@ -1668,10 +1664,17 @@ static void add_config(struct lightningd *ld, fmt_amount_msat(tmpctx, *(struct amount_msat *) opt->u.carg)); -#if EXPERIMENTAL_FEATURES } else if (opt->cb_arg == (void *)opt_set_accept_extra_tlv_types) { - /* TODO Actually print the option */ -#endif + for (size_t i = 0; + i < tal_count(ld->accept_extra_tlv_types); + i++) { + if (i == 0) + answer = tal_fmt(name0, "%"PRIu64, + ld->accept_extra_tlv_types[i]); + else + tal_append_fmt(&answer, ",%"PRIu64, + ld->accept_extra_tlv_types[i]); + } #if DEVELOPER } else if (strstarts(name, "dev-")) { /* Ignore dev settings */ diff --git a/tests/test_pay.py b/tests/test_pay.py index 5b74135a28e8..ea54a9eb51d3 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3568,7 +3568,6 @@ def test_keysend(node_factory): l3.rpc.keysend(l4.info['id'], amt) -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "Requires experimental-accept-extra-tlv-types option") def test_keysend_extra_tlvs(node_factory): """Use the extratlvs option to deliver a message with sphinx' TLV type. """ @@ -3577,14 +3576,21 @@ def test_keysend_extra_tlvs(node_factory): 2, wait_for_announce=True, opts=[ - {}, { - 'experimental-accept-extra-tlv-types': '133773310', + # Not needed, just for listconfigs test. + 'accept-htlc-tlv-types': '133773310,99990', + }, + { + 'accept-htlc-tlv-types': '133773310', "plugin": os.path.join(os.path.dirname(__file__), "plugins/sphinx-receiver.py"), }, ] ) + # Make sure listconfigs works here + assert l1.rpc.listconfigs()['accept-htlc-tlv-types'] == '133773310,99990' + assert l2.rpc.listconfigs()['accept-htlc-tlv-types'] == '133773310' + l1.rpc.keysend(l2.info['id'], amt, extratlvs={133773310: 'FEEDC0DE'}) inv = only_one(l2.rpc.listinvoices()['invoices']) assert(l2.daemon.is_in_log(r'plugin-sphinx-receiver.py.*extratlvs.*133773310.*feedc0de')) From 775d6ba193497dac5526fd6e6b6f9bc8eb656dee Mon Sep 17 00:00:00 2001 From: Kristaps Kaupe Date: Wed, 20 Jul 2022 13:29:56 +0300 Subject: [PATCH 1427/1530] doc: Fix wrong_funding description in manpage and type in schema --- cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 2 +- doc/lightning-close.7.md | 2 +- doc/schemas/close.request.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index a8ebb1bb49e1..5be3b60e764d 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -403,7 +403,7 @@ message CloseRequest { optional uint32 unilateraltimeout = 2; optional string destination = 3; optional string fee_negotiation_step = 4; - optional bytes wrong_funding = 5; + optional Outpoint wrong_funding = 5; optional bool force_lease_closed = 6; repeated Feerate feerange = 7; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 1f8f4e125ee0..5636aa3dd556 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1080,7 +1080,7 @@ impl From<&pb::CloseRequest> for requests::CloseRequest { unilateraltimeout: c.unilateraltimeout.clone(), // Rule #1 for type u32? destination: c.destination.clone(), // Rule #1 for type string? fee_negotiation_step: c.fee_negotiation_step.clone(), // Rule #1 for type string? - wrong_funding: c.wrong_funding.clone().map(|v| hex::encode(v)), // Rule #1 for type txid? + wrong_funding: c.wrong_funding.clone(), // Rule #1 for type outpoint? force_lease_closed: c.force_lease_closed.clone(), // Rule #1 for type boolean? feerange: Some(c.feerange.iter().map(|s| s.into()).collect()), // Rule #4 } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 499bb9a7f06a..9d4705a86b78 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -219,7 +219,7 @@ pub mod requests { #[serde(alias = "fee_negotiation_step", skip_serializing_if = "Option::is_none")] pub fee_negotiation_step: Option, #[serde(alias = "wrong_funding", skip_serializing_if = "Option::is_none")] - pub wrong_funding: Option, + pub wrong_funding: Option, #[serde(alias = "force_lease_closed", skip_serializing_if = "Option::is_none")] pub force_lease_closed: Option, #[serde(alias = "feerange", skip_serializing_if = "crate::is_none_or_empty")] diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 5735410cb6ab..ab767266e290 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -50,7 +50,7 @@ we quickly accept the peer's proposal. The default is "50%". -*wrong_funding_txid* can only be specified if both sides have offered +*wrong_funding* can only be specified if both sides have offered the "shutdown_wrong_funding" feature (enabled by the **experimental-shutdown-wrong-funding** option): it must be a transaction id followed by a colon then the output number. Instead of diff --git a/doc/schemas/close.request.json b/doc/schemas/close.request.json index 1a6d8e0dc523..1870b72f2528 100644 --- a/doc/schemas/close.request.json +++ b/doc/schemas/close.request.json @@ -23,7 +23,7 @@ "description": "" }, "wrong_funding": { - "type": "txid", + "type": "outpoint", "description": "" }, "force_lease_closed": { From b9a7f36ab3cc44b3b69711592002490088ceb3f8 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 27 Jul 2022 11:23:32 +0200 Subject: [PATCH 1428/1530] msggen: Add conversion from cln-rpc to cln-grpc for Option We didn't have optional Outpoints as arguments so far, so let's backfill that. Changelog-Changed: cln-rpc: The `wrong_funding` argument for `close` was changed from `bytes` to `outpoint` --- cln-grpc/src/convert.rs | 2 +- contrib/msggen/msggen/gen/grpc.py | 1 + contrib/pyln-testing/pyln/testing/node_pb2.py | 496 +++++++++--------- 3 files changed, 250 insertions(+), 249 deletions(-) diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 5636aa3dd556..a17b6bae77c7 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1080,7 +1080,7 @@ impl From<&pb::CloseRequest> for requests::CloseRequest { unilateraltimeout: c.unilateraltimeout.clone(), // Rule #1 for type u32? destination: c.destination.clone(), // Rule #1 for type string? fee_negotiation_step: c.fee_negotiation_step.clone(), // Rule #1 for type string? - wrong_funding: c.wrong_funding.clone(), // Rule #1 for type outpoint? + wrong_funding: c.wrong_funding.as_ref().map(|a| a.into()), // Rule #1 for type outpoint? force_lease_closed: c.force_lease_closed.clone(), // Rule #1 for type boolean? feerange: Some(c.feerange.iter().map(|s| s.into()).collect()), // Rule #4 } diff --git a/contrib/msggen/msggen/gen/grpc.py b/contrib/msggen/msggen/gen/grpc.py index 4d523a313b00..9f5c963ba80f 100644 --- a/contrib/msggen/msggen/gen/grpc.py +++ b/contrib/msggen/msggen/gen/grpc.py @@ -428,6 +428,7 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'msat_or_any?': f'c.{name}.as_ref().map(|a| a.into())', 'feerate': f'c.{name}.as_ref().unwrap().into()', 'feerate?': f'c.{name}.as_ref().map(|a| a.into())', + 'outpoint?': f'c.{name}.as_ref().map(|a| a.into())', 'RoutehintList?': f'c.{name}.clone().map(|rl| rl.into())', 'short_channel_id': f'cln_rpc::primitives::ShortChannelId::from_str(&c.{name}).unwrap()', 'short_channel_id?': f'c.{name}.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap())', diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 732a5215f552..0c48cfe76a67 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xbc\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x1a\n\rwrong_funding\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1150,251 +1150,251 @@ _CHECKMESSAGERESPONSE._serialized_start=8626 _CHECKMESSAGERESPONSE._serialized_end=8682 _CLOSEREQUEST._serialized_start=8685 - _CLOSEREQUEST._serialized_end=9001 - _CLOSERESPONSE._serialized_start=9004 - _CLOSERESPONSE._serialized_end=9175 - _CLOSERESPONSE_CLOSETYPE._serialized_start=9106 - _CLOSERESPONSE_CLOSETYPE._serialized_end=9159 - _CONNECTREQUEST._serialized_start=9177 - _CONNECTREQUEST._serialized_end=9261 - _CONNECTRESPONSE._serialized_start=9264 - _CONNECTRESPONSE._serialized_end=9406 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9371 - _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9406 - _CONNECTADDRESS._serialized_start=9409 - _CONNECTADDRESS._serialized_end=9660 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9548 - _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9628 - _CREATEINVOICEREQUEST._serialized_start=9662 - _CREATEINVOICEREQUEST._serialized_end=9736 - _CREATEINVOICERESPONSE._serialized_start=9739 - _CREATEINVOICERESPONSE._serialized_end=10366 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10166 - _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10222 - _DATASTOREREQUEST._serialized_start=10369 - _DATASTOREREQUEST._serialized_end=10677 - _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10522 - _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10634 - _DATASTORERESPONSE._serialized_start=10680 - _DATASTORERESPONSE._serialized_end=10810 - _CREATEONIONREQUEST._serialized_start=10813 - _CREATEONIONREQUEST._serialized_end=10970 - _CREATEONIONRESPONSE._serialized_start=10972 - _CREATEONIONRESPONSE._serialized_end=11032 - _CREATEONIONHOPS._serialized_start=11034 - _CREATEONIONHOPS._serialized_end=11084 - _DELDATASTOREREQUEST._serialized_start=11086 - _DELDATASTOREREQUEST._serialized_end=11160 - _DELDATASTORERESPONSE._serialized_start=11163 - _DELDATASTORERESPONSE._serialized_end=11296 - _DELEXPIREDINVOICEREQUEST._serialized_start=11298 - _DELEXPIREDINVOICEREQUEST._serialized_end=11370 - _DELEXPIREDINVOICERESPONSE._serialized_start=11372 - _DELEXPIREDINVOICERESPONSE._serialized_end=11399 - _DELINVOICEREQUEST._serialized_start=11402 - _DELINVOICEREQUEST._serialized_end=11584 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11518 - _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11571 - _DELINVOICERESPONSE._serialized_start=11587 - _DELINVOICERESPONSE._serialized_end=12026 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11518 - _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11571 - _INVOICEREQUEST._serialized_start=12029 - _INVOICEREQUEST._serialized_end=12341 - _INVOICERESPONSE._serialized_start=12344 - _INVOICERESPONSE._serialized_end=12703 - _LISTDATASTOREREQUEST._serialized_start=12705 - _LISTDATASTOREREQUEST._serialized_end=12740 - _LISTDATASTORERESPONSE._serialized_start=12742 - _LISTDATASTORERESPONSE._serialized_end=12813 - _LISTDATASTOREDATASTORE._serialized_start=12816 - _LISTDATASTOREDATASTORE._serialized_end=12951 - _LISTINVOICESREQUEST._serialized_start=12954 - _LISTINVOICESREQUEST._serialized_end=13123 - _LISTINVOICESRESPONSE._serialized_start=13125 - _LISTINVOICESRESPONSE._serialized_end=13192 - _LISTINVOICESINVOICES._serialized_start=13195 - _LISTINVOICESINVOICES._serialized_end=13855 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13632 - _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13695 - _SENDONIONREQUEST._serialized_start=13858 - _SENDONIONREQUEST._serialized_end=14206 - _SENDONIONRESPONSE._serialized_start=14209 - _SENDONIONRESPONSE._serialized_end=14732 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14580 - _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14624 - _SENDONIONFIRST_HOP._serialized_start=14734 - _SENDONIONFIRST_HOP._serialized_end=14815 - _LISTSENDPAYSREQUEST._serialized_start=14818 - _LISTSENDPAYSREQUEST._serialized_end=15053 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14955 - _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=15014 - _LISTSENDPAYSRESPONSE._serialized_start=15055 - _LISTSENDPAYSRESPONSE._serialized_end=15122 - _LISTSENDPAYSPAYMENTS._serialized_start=15125 - _LISTSENDPAYSPAYMENTS._serialized_end=15738 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15543 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15610 - _LISTTRANSACTIONSREQUEST._serialized_start=15740 - _LISTTRANSACTIONSREQUEST._serialized_end=15765 - _LISTTRANSACTIONSRESPONSE._serialized_start=15767 - _LISTTRANSACTIONSRESPONSE._serialized_end=15850 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15853 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16135 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16138 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16654 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16350 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16628 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16657 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17201 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16896 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17175 - _PAYREQUEST._serialized_start=17204 - _PAYREQUEST._serialized_end=17676 - _PAYRESPONSE._serialized_start=17679 - _PAYRESPONSE._serialized_end=18058 - _PAYRESPONSE_PAYSTATUS._serialized_start=17961 - _PAYRESPONSE_PAYSTATUS._serialized_end=18011 - _LISTNODESREQUEST._serialized_start=18060 - _LISTNODESREQUEST._serialized_end=18102 - _LISTNODESRESPONSE._serialized_start=18104 - _LISTNODESRESPONSE._serialized_end=18159 - _LISTNODESNODES._serialized_start=18162 - _LISTNODESNODES._serialized_end=18387 - _LISTNODESNODESADDRESSES._serialized_start=18390 - _LISTNODESNODESADDRESSES._serialized_end=18637 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18530 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18625 - _WAITANYINVOICEREQUEST._serialized_start=18639 - _WAITANYINVOICEREQUEST._serialized_end=18742 - _WAITANYINVOICERESPONSE._serialized_start=18745 - _WAITANYINVOICERESPONSE._serialized_end=19276 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19121 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19166 - _WAITINVOICEREQUEST._serialized_start=19278 - _WAITINVOICEREQUEST._serialized_end=19313 - _WAITINVOICERESPONSE._serialized_start=19316 - _WAITINVOICERESPONSE._serialized_end=19835 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19683 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19725 - _WAITSENDPAYREQUEST._serialized_start=19838 - _WAITSENDPAYREQUEST._serialized_end=19980 - _WAITSENDPAYRESPONSE._serialized_start=19983 - _WAITSENDPAYRESPONSE._serialized_end=20545 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20387 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20420 - _NEWADDRREQUEST._serialized_start=20548 - _NEWADDRREQUEST._serialized_end=20706 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20632 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20690 - _NEWADDRRESPONSE._serialized_start=20708 - _NEWADDRRESPONSE._serialized_end=20799 - _WITHDRAWREQUEST._serialized_start=20802 - _WITHDRAWREQUEST._serialized_end=21004 - _WITHDRAWRESPONSE._serialized_start=21006 - _WITHDRAWRESPONSE._serialized_end=21064 - _KEYSENDREQUEST._serialized_start=21067 - _KEYSENDREQUEST._serialized_end=21399 - _KEYSENDRESPONSE._serialized_start=21402 - _KEYSENDRESPONSE._serialized_end=21772 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21696 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21725 - _KEYSENDEXTRATLVS._serialized_start=21774 - _KEYSENDEXTRATLVS._serialized_end=21792 - _FUNDPSBTREQUEST._serialized_start=21795 - _FUNDPSBTREQUEST._serialized_end=22106 - _FUNDPSBTRESPONSE._serialized_start=22109 - _FUNDPSBTRESPONSE._serialized_end=22326 - _FUNDPSBTRESERVATIONS._serialized_start=22328 - _FUNDPSBTRESERVATIONS._serialized_end=22445 - _SENDPSBTREQUEST._serialized_start=22447 - _SENDPSBTREQUEST._serialized_end=22512 - _SENDPSBTRESPONSE._serialized_start=22514 - _SENDPSBTRESPONSE._serialized_end=22558 - _SIGNPSBTREQUEST._serialized_start=22560 - _SIGNPSBTREQUEST._serialized_end=22609 - _SIGNPSBTRESPONSE._serialized_start=22611 - _SIGNPSBTRESPONSE._serialized_end=22650 - _UTXOPSBTREQUEST._serialized_start=22653 - _UTXOPSBTREQUEST._serialized_end=23000 - _UTXOPSBTRESPONSE._serialized_start=23003 - _UTXOPSBTRESPONSE._serialized_end=23220 - _UTXOPSBTRESERVATIONS._serialized_start=23222 - _UTXOPSBTRESERVATIONS._serialized_end=23339 - _TXDISCARDREQUEST._serialized_start=23341 - _TXDISCARDREQUEST._serialized_end=23373 - _TXDISCARDRESPONSE._serialized_start=23375 - _TXDISCARDRESPONSE._serialized_end=23429 - _TXPREPAREREQUEST._serialized_start=23432 - _TXPREPAREREQUEST._serialized_end=23596 - _TXPREPARERESPONSE._serialized_start=23598 - _TXPREPARERESPONSE._serialized_end=23666 - _TXSENDREQUEST._serialized_start=23668 - _TXSENDREQUEST._serialized_end=23697 - _TXSENDRESPONSE._serialized_start=23699 - _TXSENDRESPONSE._serialized_end=23755 - _DISCONNECTREQUEST._serialized_start=23757 - _DISCONNECTREQUEST._serialized_end=23818 - _DISCONNECTRESPONSE._serialized_start=23820 - _DISCONNECTRESPONSE._serialized_end=23840 - _FEERATESREQUEST._serialized_start=23842 - _FEERATESREQUEST._serialized_end=23949 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23912 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23949 - _FEERATESRESPONSE._serialized_start=23951 - _FEERATESRESPONSE._serialized_end=24037 - _FEERATESPERKB._serialized_start=24040 - _FEERATESPERKB._serialized_end=24363 - _FEERATESPERKW._serialized_start=24366 - _FEERATESPERKW._serialized_end=24689 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24692 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24885 - _FUNDCHANNELREQUEST._serialized_start=24888 - _FUNDCHANNELREQUEST._serialized_end=25373 - _FUNDCHANNELRESPONSE._serialized_start=25376 - _FUNDCHANNELRESPONSE._serialized_end=25531 - _GETROUTEREQUEST._serialized_start=25534 - _GETROUTEREQUEST._serialized_end=25770 - _GETROUTERESPONSE._serialized_start=25772 - _GETROUTERESPONSE._serialized_end=25825 - _GETROUTEROUTE._serialized_start=25828 - _GETROUTEROUTE._serialized_end=26025 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25996 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=26025 - _LISTFORWARDSREQUEST._serialized_start=26028 - _LISTFORWARDSREQUEST._serialized_end=26286 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26168 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26244 - _LISTFORWARDSRESPONSE._serialized_start=26288 - _LISTFORWARDSRESPONSE._serialized_end=26355 - _LISTFORWARDSFORWARDS._serialized_start=26358 - _LISTFORWARDSFORWARDS._serialized_end=26926 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26723 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26807 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26809 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26857 - _LISTPAYSREQUEST._serialized_start=26929 - _LISTPAYSREQUEST._serialized_end=27148 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27054 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27109 - _LISTPAYSRESPONSE._serialized_start=27150 - _LISTPAYSRESPONSE._serialized_end=27201 - _LISTPAYSPAYS._serialized_start=27204 - _LISTPAYSPAYS._serialized_end=27723 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27535 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27594 - _PINGREQUEST._serialized_start=27725 - _PINGREQUEST._serialized_end=27814 - _PINGRESPONSE._serialized_start=27816 - _PINGRESPONSE._serialized_end=27846 - _SIGNMESSAGEREQUEST._serialized_start=27848 - _SIGNMESSAGEREQUEST._serialized_end=27885 - _SIGNMESSAGERESPONSE._serialized_start=27887 - _SIGNMESSAGERESPONSE._serialized_end=27957 - _STOPREQUEST._serialized_start=27959 - _STOPREQUEST._serialized_end=27972 - _STOPRESPONSE._serialized_start=27974 - _STOPRESPONSE._serialized_end=27988 - _NODE._serialized_start=27991 - _NODE._serialized_end=30919 + _CLOSEREQUEST._serialized_end=9016 + _CLOSERESPONSE._serialized_start=9019 + _CLOSERESPONSE._serialized_end=9190 + _CLOSERESPONSE_CLOSETYPE._serialized_start=9121 + _CLOSERESPONSE_CLOSETYPE._serialized_end=9174 + _CONNECTREQUEST._serialized_start=9192 + _CONNECTREQUEST._serialized_end=9276 + _CONNECTRESPONSE._serialized_start=9279 + _CONNECTRESPONSE._serialized_end=9421 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_start=9386 + _CONNECTRESPONSE_CONNECTDIRECTION._serialized_end=9421 + _CONNECTADDRESS._serialized_start=9424 + _CONNECTADDRESS._serialized_end=9675 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_start=9563 + _CONNECTADDRESS_CONNECTADDRESSTYPE._serialized_end=9643 + _CREATEINVOICEREQUEST._serialized_start=9677 + _CREATEINVOICEREQUEST._serialized_end=9751 + _CREATEINVOICERESPONSE._serialized_start=9754 + _CREATEINVOICERESPONSE._serialized_end=10381 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_start=10181 + _CREATEINVOICERESPONSE_CREATEINVOICESTATUS._serialized_end=10237 + _DATASTOREREQUEST._serialized_start=10384 + _DATASTOREREQUEST._serialized_end=10692 + _DATASTOREREQUEST_DATASTOREMODE._serialized_start=10537 + _DATASTOREREQUEST_DATASTOREMODE._serialized_end=10649 + _DATASTORERESPONSE._serialized_start=10695 + _DATASTORERESPONSE._serialized_end=10825 + _CREATEONIONREQUEST._serialized_start=10828 + _CREATEONIONREQUEST._serialized_end=10985 + _CREATEONIONRESPONSE._serialized_start=10987 + _CREATEONIONRESPONSE._serialized_end=11047 + _CREATEONIONHOPS._serialized_start=11049 + _CREATEONIONHOPS._serialized_end=11099 + _DELDATASTOREREQUEST._serialized_start=11101 + _DELDATASTOREREQUEST._serialized_end=11175 + _DELDATASTORERESPONSE._serialized_start=11178 + _DELDATASTORERESPONSE._serialized_end=11311 + _DELEXPIREDINVOICEREQUEST._serialized_start=11313 + _DELEXPIREDINVOICEREQUEST._serialized_end=11385 + _DELEXPIREDINVOICERESPONSE._serialized_start=11387 + _DELEXPIREDINVOICERESPONSE._serialized_end=11414 + _DELINVOICEREQUEST._serialized_start=11417 + _DELINVOICEREQUEST._serialized_end=11599 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_start=11533 + _DELINVOICEREQUEST_DELINVOICESTATUS._serialized_end=11586 + _DELINVOICERESPONSE._serialized_start=11602 + _DELINVOICERESPONSE._serialized_end=12041 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_start=11533 + _DELINVOICERESPONSE_DELINVOICESTATUS._serialized_end=11586 + _INVOICEREQUEST._serialized_start=12044 + _INVOICEREQUEST._serialized_end=12356 + _INVOICERESPONSE._serialized_start=12359 + _INVOICERESPONSE._serialized_end=12718 + _LISTDATASTOREREQUEST._serialized_start=12720 + _LISTDATASTOREREQUEST._serialized_end=12755 + _LISTDATASTORERESPONSE._serialized_start=12757 + _LISTDATASTORERESPONSE._serialized_end=12828 + _LISTDATASTOREDATASTORE._serialized_start=12831 + _LISTDATASTOREDATASTORE._serialized_end=12966 + _LISTINVOICESREQUEST._serialized_start=12969 + _LISTINVOICESREQUEST._serialized_end=13138 + _LISTINVOICESRESPONSE._serialized_start=13140 + _LISTINVOICESRESPONSE._serialized_end=13207 + _LISTINVOICESINVOICES._serialized_start=13210 + _LISTINVOICESINVOICES._serialized_end=13870 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_start=13647 + _LISTINVOICESINVOICES_LISTINVOICESINVOICESSTATUS._serialized_end=13710 + _SENDONIONREQUEST._serialized_start=13873 + _SENDONIONREQUEST._serialized_end=14221 + _SENDONIONRESPONSE._serialized_start=14224 + _SENDONIONRESPONSE._serialized_end=14747 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_start=14595 + _SENDONIONRESPONSE_SENDONIONSTATUS._serialized_end=14639 + _SENDONIONFIRST_HOP._serialized_start=14749 + _SENDONIONFIRST_HOP._serialized_end=14830 + _LISTSENDPAYSREQUEST._serialized_start=14833 + _LISTSENDPAYSREQUEST._serialized_end=15068 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_start=14970 + _LISTSENDPAYSREQUEST_LISTSENDPAYSSTATUS._serialized_end=15029 + _LISTSENDPAYSRESPONSE._serialized_start=15070 + _LISTSENDPAYSRESPONSE._serialized_end=15137 + _LISTSENDPAYSPAYMENTS._serialized_start=15140 + _LISTSENDPAYSPAYMENTS._serialized_end=15753 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15558 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15625 + _LISTTRANSACTIONSREQUEST._serialized_start=15755 + _LISTTRANSACTIONSREQUEST._serialized_end=15780 + _LISTTRANSACTIONSRESPONSE._serialized_start=15782 + _LISTTRANSACTIONSRESPONSE._serialized_end=15865 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15868 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16150 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16153 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16669 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16365 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16643 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16672 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17216 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16911 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17190 + _PAYREQUEST._serialized_start=17219 + _PAYREQUEST._serialized_end=17691 + _PAYRESPONSE._serialized_start=17694 + _PAYRESPONSE._serialized_end=18073 + _PAYRESPONSE_PAYSTATUS._serialized_start=17976 + _PAYRESPONSE_PAYSTATUS._serialized_end=18026 + _LISTNODESREQUEST._serialized_start=18075 + _LISTNODESREQUEST._serialized_end=18117 + _LISTNODESRESPONSE._serialized_start=18119 + _LISTNODESRESPONSE._serialized_end=18174 + _LISTNODESNODES._serialized_start=18177 + _LISTNODESNODES._serialized_end=18402 + _LISTNODESNODESADDRESSES._serialized_start=18405 + _LISTNODESNODESADDRESSES._serialized_end=18652 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18545 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18640 + _WAITANYINVOICEREQUEST._serialized_start=18654 + _WAITANYINVOICEREQUEST._serialized_end=18757 + _WAITANYINVOICERESPONSE._serialized_start=18760 + _WAITANYINVOICERESPONSE._serialized_end=19291 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19136 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19181 + _WAITINVOICEREQUEST._serialized_start=19293 + _WAITINVOICEREQUEST._serialized_end=19328 + _WAITINVOICERESPONSE._serialized_start=19331 + _WAITINVOICERESPONSE._serialized_end=19850 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19698 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19740 + _WAITSENDPAYREQUEST._serialized_start=19853 + _WAITSENDPAYREQUEST._serialized_end=19995 + _WAITSENDPAYRESPONSE._serialized_start=19998 + _WAITSENDPAYRESPONSE._serialized_end=20560 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20402 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20435 + _NEWADDRREQUEST._serialized_start=20563 + _NEWADDRREQUEST._serialized_end=20721 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20647 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20705 + _NEWADDRRESPONSE._serialized_start=20723 + _NEWADDRRESPONSE._serialized_end=20814 + _WITHDRAWREQUEST._serialized_start=20817 + _WITHDRAWREQUEST._serialized_end=21019 + _WITHDRAWRESPONSE._serialized_start=21021 + _WITHDRAWRESPONSE._serialized_end=21079 + _KEYSENDREQUEST._serialized_start=21082 + _KEYSENDREQUEST._serialized_end=21414 + _KEYSENDRESPONSE._serialized_start=21417 + _KEYSENDRESPONSE._serialized_end=21787 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21711 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21740 + _KEYSENDEXTRATLVS._serialized_start=21789 + _KEYSENDEXTRATLVS._serialized_end=21807 + _FUNDPSBTREQUEST._serialized_start=21810 + _FUNDPSBTREQUEST._serialized_end=22121 + _FUNDPSBTRESPONSE._serialized_start=22124 + _FUNDPSBTRESPONSE._serialized_end=22341 + _FUNDPSBTRESERVATIONS._serialized_start=22343 + _FUNDPSBTRESERVATIONS._serialized_end=22460 + _SENDPSBTREQUEST._serialized_start=22462 + _SENDPSBTREQUEST._serialized_end=22527 + _SENDPSBTRESPONSE._serialized_start=22529 + _SENDPSBTRESPONSE._serialized_end=22573 + _SIGNPSBTREQUEST._serialized_start=22575 + _SIGNPSBTREQUEST._serialized_end=22624 + _SIGNPSBTRESPONSE._serialized_start=22626 + _SIGNPSBTRESPONSE._serialized_end=22665 + _UTXOPSBTREQUEST._serialized_start=22668 + _UTXOPSBTREQUEST._serialized_end=23015 + _UTXOPSBTRESPONSE._serialized_start=23018 + _UTXOPSBTRESPONSE._serialized_end=23235 + _UTXOPSBTRESERVATIONS._serialized_start=23237 + _UTXOPSBTRESERVATIONS._serialized_end=23354 + _TXDISCARDREQUEST._serialized_start=23356 + _TXDISCARDREQUEST._serialized_end=23388 + _TXDISCARDRESPONSE._serialized_start=23390 + _TXDISCARDRESPONSE._serialized_end=23444 + _TXPREPAREREQUEST._serialized_start=23447 + _TXPREPAREREQUEST._serialized_end=23611 + _TXPREPARERESPONSE._serialized_start=23613 + _TXPREPARERESPONSE._serialized_end=23681 + _TXSENDREQUEST._serialized_start=23683 + _TXSENDREQUEST._serialized_end=23712 + _TXSENDRESPONSE._serialized_start=23714 + _TXSENDRESPONSE._serialized_end=23770 + _DISCONNECTREQUEST._serialized_start=23772 + _DISCONNECTREQUEST._serialized_end=23833 + _DISCONNECTRESPONSE._serialized_start=23835 + _DISCONNECTRESPONSE._serialized_end=23855 + _FEERATESREQUEST._serialized_start=23857 + _FEERATESREQUEST._serialized_end=23964 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23927 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23964 + _FEERATESRESPONSE._serialized_start=23966 + _FEERATESRESPONSE._serialized_end=24052 + _FEERATESPERKB._serialized_start=24055 + _FEERATESPERKB._serialized_end=24378 + _FEERATESPERKW._serialized_start=24381 + _FEERATESPERKW._serialized_end=24704 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24707 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24900 + _FUNDCHANNELREQUEST._serialized_start=24903 + _FUNDCHANNELREQUEST._serialized_end=25388 + _FUNDCHANNELRESPONSE._serialized_start=25391 + _FUNDCHANNELRESPONSE._serialized_end=25546 + _GETROUTEREQUEST._serialized_start=25549 + _GETROUTEREQUEST._serialized_end=25785 + _GETROUTERESPONSE._serialized_start=25787 + _GETROUTERESPONSE._serialized_end=25840 + _GETROUTEROUTE._serialized_start=25843 + _GETROUTEROUTE._serialized_end=26040 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=26011 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=26040 + _LISTFORWARDSREQUEST._serialized_start=26043 + _LISTFORWARDSREQUEST._serialized_end=26301 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26183 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26259 + _LISTFORWARDSRESPONSE._serialized_start=26303 + _LISTFORWARDSRESPONSE._serialized_end=26370 + _LISTFORWARDSFORWARDS._serialized_start=26373 + _LISTFORWARDSFORWARDS._serialized_end=26941 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26738 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26822 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26824 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26872 + _LISTPAYSREQUEST._serialized_start=26944 + _LISTPAYSREQUEST._serialized_end=27163 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27069 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27124 + _LISTPAYSRESPONSE._serialized_start=27165 + _LISTPAYSRESPONSE._serialized_end=27216 + _LISTPAYSPAYS._serialized_start=27219 + _LISTPAYSPAYS._serialized_end=27738 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27550 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27609 + _PINGREQUEST._serialized_start=27740 + _PINGREQUEST._serialized_end=27829 + _PINGRESPONSE._serialized_start=27831 + _PINGRESPONSE._serialized_end=27861 + _SIGNMESSAGEREQUEST._serialized_start=27863 + _SIGNMESSAGEREQUEST._serialized_end=27900 + _SIGNMESSAGERESPONSE._serialized_start=27902 + _SIGNMESSAGERESPONSE._serialized_end=27972 + _STOPREQUEST._serialized_start=27974 + _STOPREQUEST._serialized_end=27987 + _STOPRESPONSE._serialized_start=27989 + _STOPRESPONSE._serialized_end=28003 + _NODE._serialized_start=28006 + _NODE._serialized_end=30934 # @@protoc_insertion_point(module_scope) From 910116c9e6cd6074864e995da59308aafaa995cd Mon Sep 17 00:00:00 2001 From: niftynei Date: Wed, 21 Sep 2022 20:44:35 -0500 Subject: [PATCH 1429/1530] build-release: configure before submodcheck We added a submod dep (lowdown) that requires config to run first. --- tools/build-release.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/build-release.sh b/tools/build-release.sh index 77c5e3e9e22b..f5d32c304912 100755 --- a/tools/build-release.sh +++ b/tools/build-release.sh @@ -72,6 +72,8 @@ if [ -z "$MTIME" ]; then exit 1 fi +# submodcheck needs to know if we have lowdown +./configure --reconfigure # If it's a completely clean directory, we need submodules! make submodcheck mkdir -p release From 3c80b22d6fd6d6c0e4cfcd550adeaf7da7e3652d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Aug 2022 19:58:36 +0930 Subject: [PATCH 1430/1530] pyln: use poetry version, add target to check version, use poetry publish. Remove non-working test-release target. Signed-off-by: Rusty Russell --- contrib/pyln-client/Makefile | 48 +++++++++++++---------------------- contrib/pyln-proto/Makefile | 47 ++++++++++++++-------------------- contrib/pyln-testing/Makefile | 48 +++++++++++++---------------------- 3 files changed, 55 insertions(+), 88 deletions(-) diff --git a/contrib/pyln-client/Makefile b/contrib/pyln-client/Makefile index c8703965ac72..27d596f4e89f 100644 --- a/contrib/pyln-client/Makefile +++ b/contrib/pyln-client/Makefile @@ -1,8 +1,7 @@ #!/usr/bin/make -.PHONY: bdist sdist release check check-source check-flake8 check-mypy PKG=client -VERSION = $(shell python3 -c 'from pyln import ${PKG};print(${PKG}.__version__)') +VERSION := $(shell poetry version -s) # You can set these variables from the command line. SPHINXOPTS = @@ -16,7 +15,14 @@ ARTEFACTS = $(BDIST_FILE) $(SDIST_FILE) check: check-source check-pytest -check-source: check-flake8 check-mypy +check-source: check-flake8 check-mypy check-version + +# We want to create an env for this directory. +check-version: + poetry env remove -q python3 || true + poetry env use python3 + poetry install + [ "`poetry run python3 -c 'from pyln import $(PKG); print($(PKG).__version__)'`" = "$(VERSION)" ] || exit 1 check-flake8: flake8 --ignore=E501,E731,W503,E741 pyln tests @@ -27,33 +33,15 @@ check-pytest: check-mypy: # MYPYPATH=$(PYTHONPATH) mypy --namespace-packages --follow-imports=skip tests pyln -pyproject.toml: pyln/${PKG}/__init__.py - poetry version ${VERSION} +# Having versions in two places sucks, but so does every other option :( +# See https://github.com/python-poetry/poetry/issues/144 +upgrade-version: + if [ -z "$(NEW_VERSION)" ]; then echo "Set NEW_VERSION!" >&2; exit 1; fi + poetry version $(NEW_VERSION) + sed 's/^__version__ = .*/__version__ = "$(NEW_VERSION)"/' < pyln/$(PKG)/__init__.py > pyln/$(PKG)/__init__.py.new && mv pyln/$(PKG)/__init__.py.new pyln/$(PKG)/__init__.py -$(SDIST_FILE) $(BDIST_FILE): pyproject.toml +$(SDIST_FILE) $(BDIST_FILE): poetry build -test-release: check $(ARTEFACTS) pyproject.toml - # No way of saying "it's ok if files exist" yet - poetry publish --repository testpypi || /bin/true - echo Sleeping for PyPI index to update - sleep 10 - - # Generate a requirements.txt file, needed for us to download requirements from the prod pypi instead of the test pypi, since some packages are not published to test pypi. - poetry export -f requirements.txt --output requirements.txt --without-hashes - - # Create a test virtualenv, install from the testpypi and run the - # tests against it (make sure not to use any virtualenv that may have - # pyln-${PKG} already installed). - virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - testpypi/bin/python3 -m pip install -r requirements.txt pytest-timeout - testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} - testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" - testpypi/bin/pytest tests - rm -rf testpypi - -prod-release: test-release $(ARTEFACTS) - python3 -m twine upload $(ARTEFACTS) - -clean: - rm -rf testpypi +prod-release: check-version $(ARTEFACTS) + poetry publish diff --git a/contrib/pyln-proto/Makefile b/contrib/pyln-proto/Makefile index ef2eeb88c644..373c812d9816 100644 --- a/contrib/pyln-proto/Makefile +++ b/contrib/pyln-proto/Makefile @@ -1,8 +1,7 @@ #!/usr/bin/make -.PHONY: bdist sdist release check check-source check-flake8 check-mypy PKG=proto -VERSION = $(shell python3 -c 'from pyln import ${PKG};print(${PKG}.__version__)') +VERSION := $(shell poetry version -s) # You can set these variables from the command line. SPHINXOPTS = @@ -16,7 +15,14 @@ ARTEFACTS = $(BDIST_FILE) $(SDIST_FILE) check: check-source check-pytest -check-source: check-flake8 check-mypy +check-source: check-flake8 check-mypy check-version + +# We want to create an env for this directory. +check-version: + poetry env remove -q python3 || true + poetry env use python3 + poetry install + [ "`poetry run python3 -c 'from pyln import $(PKG); print($(PKG).__version__)'`" = "$(VERSION)" ] || exit 1 check-flake8: flake8 --ignore=E501,E731,W503,E741 pyln tests @@ -30,30 +36,15 @@ check-mypy: pyproject.toml: pyln/${PKG}/__init__.py poetry version ${VERSION} -$(SDIST_FILE) $(BDIST_FILE): pyproject.toml +# Having versions in two places sucks, but so does every other option :( +# See https://github.com/python-poetry/poetry/issues/144 +upgrade-version: + if [ -z "$(NEW_VERSION)" ]; then echo "Set NEW_VERSION!" >&2; exit 1; fi + poetry version $(NEW_VERSION) + sed 's/^__version__ = .*/__version__ = "$(NEW_VERSION)"/' < pyln/$(PKG)/__init__.py > pyln/$(PKG)/__init__.py.new && mv pyln/$(PKG)/__init__.py.new pyln/$(PKG)/__init__.py + +$(SDIST_FILE) $(BDIST_FILE): poetry build -test-release: check $(ARTEFACTS) pyproject.toml - # No way of saying "it's ok if files exist" yet - poetry publish --repository testpypi || /bin/true - echo Sleeping for PyPI index to update - sleep 10 - - # Generate a requirements.txt file, needed for us to download requirements from the prod pypi instead of the test pypi, since some packages are not published to test pypi. - poetry export -f requirements.txt --output requirements.txt --without-hashes - - # Create a test virtualenv, install from the testpypi and run the - # tests against it (make sure not to use any virtualenv that may have - # pyln-${PKG} already installed). - virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - testpypi/bin/python3 -m pip install -r requirements.txt pytest-timeout - testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} - testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" - testpypi/bin/pytest tests - rm -rf testpypi - -prod-release: test-release $(ARTEFACTS) - python3 -m twine upload $(ARTEFACTS) - -clean: - rm -rf testpypi +prod-release: check-version $(ARTEFACTS) + poetry publish diff --git a/contrib/pyln-testing/Makefile b/contrib/pyln-testing/Makefile index 9f19b9d5918c..2b45e51c241b 100644 --- a/contrib/pyln-testing/Makefile +++ b/contrib/pyln-testing/Makefile @@ -1,8 +1,7 @@ #!/usr/bin/make -.PHONY: bdist sdist release check check-source check-flake8 check-mypy PKG=testing -VERSION = $(shell python3 -c 'from pyln import ${PKG};print(${PKG}.__version__)') +VERSION := $(shell poetry version -s) # You can set these variables from the command line. SPHINXOPTS = @@ -16,7 +15,14 @@ ARTEFACTS = $(BDIST_FILE) $(SDIST_FILE) check: check-source check-pytest -check-source: check-flake8 check-mypy +check-source: check-flake8 check-mypy check-version + +# We want to create an env for this directory. +check-version: + poetry env remove -q python3 || true + poetry env use python3 + poetry install + [ "`poetry run python3 -c 'from pyln import $(PKG); print($(PKG).__version__)'`" = "$(VERSION)" ] || exit 1 check-flake8: flake8 --ignore=E501,E731,W503,E741 --exclude '*_pb2*.py,grpc2py.py' pyln tests @@ -27,33 +33,15 @@ check-pytest: check-mypy: # MYPYPATH=$(PYTHONPATH) mypy --namespace-packages --follow-imports=skip tests pyln -pyproject.toml: pyln/${PKG}/__init__.py - poetry version ${VERSION} +# Having versions in two places sucks, but so does every other option :( +# See https://github.com/python-poetry/poetry/issues/144 +upgrade-version: + if [ -z "$(NEW_VERSION)" ]; then echo "Set NEW_VERSION!" >&2; exit 1; fi + poetry version $(NEW_VERSION) + sed 's/^__version__ = .*/__version__ = "$(NEW_VERSION)"/' < pyln/$(PKG)/__init__.py > pyln/$(PKG)/__init__.py.new && mv pyln/$(PKG)/__init__.py.new pyln/$(PKG)/__init__.py -$(SDIST_FILE) $(BDIST_FILE): pyproject.toml +$(SDIST_FILE) $(BDIST_FILE): poetry build -test-release: check $(ARTEFACTS) pyproject.toml - # No way of saying "it's ok if files exist" yet - poetry publish --repository testpypi || /bin/true - echo Sleeping for PyPI index to update - sleep 10 - - # Generate a requirements.txt file, needed for us to download requirements from the prod pypi instead of the test pypi, since some packages are not published to test pypi. - poetry export -f requirements.txt --output requirements.txt --without-hashes - - # Create a test virtualenv, install from the testpypi and run the - # tests against it (make sure not to use any virtualenv that may have - # pyln-${PKG} already installed). - virtualenv testpypi --python=/usr/bin/python3 --download --always-copy --clear - testpypi/bin/python3 -m pip install -r requirements.txt pytest-timeout - testpypi/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-${PKG}==${VERSION} - testpypi/bin/python3 -c "from pyln import ${PKG};assert(${PKG}.__version__ == '$(VERSION)')" - testpypi/bin/pytest tests - rm -rf testpypi - -prod-release: test-release $(ARTEFACTS) - python3 -m twine upload $(ARTEFACTS) - -clean: - rm -rf testpypi +prod-release: check-version $(ARTEFACTS) + poetry publish From 0514269f48f37c49c2df49a5733fc15dcb09f647 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Aug 2022 19:59:17 +0930 Subject: [PATCH 1431/1530] Makefile: add targets to upgrade pyln versions, push releases. And use those in MAKING-RELEASES.md Signed-off-by: Rusty Russell --- Makefile | 14 ++++++++++++++ doc/MAKING-RELEASES.md | 6 ++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 5e1d113d18c0..f746cdd2d396 100644 --- a/Makefile +++ b/Makefile @@ -708,6 +708,20 @@ clean: obsclean find . -name '*.nccout' -delete if [ "${RUST}" -eq "1" ]; then cargo clean; fi + +PYLNS=client proto testing +# See doc/MAKING-RELEASES.md +update-pyln-versions: $(PYLNS:%=update-pyln-version-%) + +update-pyln-version-%: + @if [ -z "$(NEW_VERSION)" ]; then echo "Set NEW_VERSION!" >&2; exit 1; fi + cd contrib/pyln-$* && $(MAKE) upgrade-version + +pyln-release: $(PYLNS:%=pyln-release-%) + +pyln-release-%: + cd contrib/pyln-$* && $(MAKE) prod-release + # These must both be enabled for update-mocks ifeq ($(DEVELOPER)$(EXPERIMENTAL_FEATURES),11) update-mocks: $(ALL_TEST_PROGRAMS:%=update-mocks/%.c) diff --git a/doc/MAKING-RELEASES.md b/doc/MAKING-RELEASES.md index 519c64d4dd86..d9f4fe670dd4 100644 --- a/doc/MAKING-RELEASES.md +++ b/doc/MAKING-RELEASES.md @@ -30,8 +30,7 @@ Here's a checklist for the release process. 3. Create a new CHANGELOG.md heading to `vrc1`, and create a link at the bottom. Note that you should exactly copy the date and name format from a previous release, as the `build-release.sh` script relies on this. -4. Update the contrib/pyln package __version__ strings, but do not upload - it to pypi! +4. Update the contrib/pyln package versions: `make update-pyln-versions NEW_VERSION=` 5. Create a PR with the above. ### Releasing -rc1 @@ -78,8 +77,7 @@ Here's a checklist for the release process. 10. Append the signatures into a file called `SHA256SUMS.asc`, verify with `gpg --verify SHA256SUMS.asc` and include the file in the draft release. -11. In each contrib/pyln-* directory, `make test-release` and if that succeeds, - `make prod-release` to upload to pypi.org. +11.`make pyln-release` to upload pyln modules to pypi.org. ### Performing the Release From 7c2c100145eea0abacffabe42bbbfde3fead1fd3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 24 Aug 2022 19:59:17 +0930 Subject: [PATCH 1432/1530] pyln: results of make update-pyln-versions NEW_VERSION=0.12.0 But I had to make the pyln-proto requirement of pyln-client less strict. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyproject.toml | 4 ++-- contrib/pyln-proto/pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index a3dfffe8aa36..a9b5c25d2497 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-client" -version = "0.11.1" +version = "0.12.0" description = "Client library and plugin library for Core Lightning" authors = ["Christian Decker "] license = "BSD-MIT" @@ -13,7 +13,7 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" pyln-bolt7 = "^1.0" -pyln-proto = "^0.11" +pyln-proto = ">=0.12" [tool.poetry.dev-dependencies] pytest = "^7.0.1" diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml index 8c7ed3411719..0ccb597516f0 100644 --- a/contrib/pyln-proto/pyproject.toml +++ b/contrib/pyln-proto/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-proto" -version = "0.11.1" +version = "0.12.0" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." authors = ["Christian Decker "] license = "BSD-MIT" From aace5b51efbc10e6908c7e6242de6c1264dee7a6 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 25 Aug 2022 17:10:40 +0200 Subject: [PATCH 1433/1530] pyln: Adjust the auto-publish task to trigger on tags I was wondering why the workflow never published, turns out we were triggering on branch pushes only, not tags. So the branch would get pushed, but the tag is pushed afterwards, and thus not triggering. --- .github/workflows/pypi.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index b6c0bd1f18cb..6fbed25ab9e1 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -5,6 +5,11 @@ on: push: branches: - master + tags: + # Semantic versioning tags + - 'v[0-9]+.[0-9]+.[0-9]+' + # Date style tags + - 'v[0-9]{2}.[0-9]{2}' jobs: deploy: name: Build and publish ${{ matrix.package }} 🐍 From 8ae0af7962b4e19064d5bfa6341ef7816dc749b1 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 25 Aug 2022 17:15:11 +0200 Subject: [PATCH 1434/1530] pyln: Bump pyln-client dependency in pyln-testing They were contradicting each other. --- contrib/pyln-testing/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index d5ef469157e8..f27c1e009d04 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -17,7 +17,7 @@ ephemeral-port-reserve = "^1.1.4" psycopg2-binary = "^2.9.3" python-bitcoinlib = "^0.11.0" jsonschema = "^4.4.0" -pyln-client = "^0.11" +pyln-client = "^0.12" Flask = "^2.0.3" cheroot = "^8.6.0" psutil = "^5.9.0" From 4693803c355db5cfa93ebd188a8a2232ca6e9535 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 25 Aug 2022 17:22:00 +0200 Subject: [PATCH 1435/1530] ci: Use the new make upgrade-version target to manage versions --- .github/workflows/pypi.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 6fbed25ab9e1..3362867d9766 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -24,14 +24,15 @@ jobs: WORKDIR: contrib/pyln-testing - PACKAGE: pyln-proto WORKDIR: contrib/pyln-proto - - PACKAGE: pyn-bolt1 - WORKDIR: contrib/pyln-spec/bolt1/ - - PACKAGE: pyn-bolt2 - WORKDIR: contrib/pyln-spec/bolt2/ - - PACKAGE: pyn-bolt4 - WORKDIR: contrib/pyln-spec/bolt4/ - - PACKAGE: pyn-bolt7 - WORKDIR: contrib/pyln-spec/bolt7/ + # Bolt packages are handled differently + #- PACKAGE: pyn-bolt1 + # WORKDIR: contrib/pyln-spec/bolt1/ + #- PACKAGE: pyn-bolt2 + # WORKDIR: contrib/pyln-spec/bolt2/ + #- PACKAGE: pyn-bolt4 + # WORKDIR: contrib/pyln-spec/bolt4/ + #- PACKAGE: pyn-bolt7 + # WORKDIR: contrib/pyln-spec/bolt7/ steps: - uses: actions/checkout@master with: @@ -47,13 +48,17 @@ jobs: run: >- python -m pip install build poetry --user + - name: Check version tag + run: >- + git describe --always --dirty=-modded --abbrev=7 + - name: Build a binary wheel and a source tarball env: WORKDIR: ${{ matrix.WORKDIR }} run: | export VERSION=$(git describe --abbrev=0).post$(git describe --abbrev=1 | awk -F "-" '{print $2}') cd ${{ env.WORKDIR}} - poetry version $VERSION + make upgrade-version NEW_VERSION=$VERSION poetry build - name: Publish distribution 📦 to Test PyPI @@ -74,6 +79,6 @@ jobs: run: | cd ${{ env.WORKDIR}} export VERSION=$(git describe --abbrev=0) - poetry version $VERSION + make upgrade-version NEW_VERSION=$VERSION poetry config repositories.testpypi https://test.pypi.org/legacy/ poetry publish --repository testpypi --no-interaction From 41a52929f7ac6a2ad0cc771d477efbdf42b9d285 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:52 +0930 Subject: [PATCH 1436/1530] libplugin: handle JSON reply after command freed. This can happen, and in fact does below in our test_autoclean_once test where we update the datastore, and return from the cmd. Signed-off-by: Rusty Russell --- plugins/libplugin.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index ad2e59fbc411..e5cacb5311ec 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -681,10 +681,13 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) out = strmap_getn(&plugin->out_reqs, plugin->rpc_buffer + idtok->start, idtok->end - idtok->start); - if (!out) - plugin_err(plugin, "JSON reply with unknown id '%.*s'", + if (!out) { + /* This can actually happen, if they free req! */ + plugin_log(plugin, LOG_DBG, "JSON reply with unknown id '%.*s'", json_tok_full_len(toks), json_tok_full(plugin->rpc_buffer, toks)); + return; + } /* Remove destructor if one existed */ if (out->cmd) From bd76a196f57773fce539472a1af5118e7fe4c7c9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:52 +0930 Subject: [PATCH 1437/1530] autoclean: new interface In preparation for more things being autocleaned. Signed-off-by: Rusty Russell --- contrib/pyln-client/pyln/client/lightning.py | 9 + doc/lightningd-config.5.md | 14 +- plugins/autoclean.c | 208 +++++++++++++++++-- tests/test_invoices.py | 4 +- tests/test_plugin.py | 48 +++++ 5 files changed, 252 insertions(+), 31 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index d5e42d82d70f..9c9abbbea1c4 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -531,6 +531,15 @@ def autocleaninvoice(self, cycle_seconds=None, expired_by=None): } return self.call("autocleaninvoice", payload) + def autoclean_status(self, subsystem=None): + """ + Print status of autocleaning (optionally, just for {subsystem}). + """ + payload = { + "subsystem": subsystem, + } + return self.call("autoclean-status", payload) + def check(self, command_to_check, **kwargs): """ Checks if a command is valid without running it. diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 40815c87525e..f846ba8082ec 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -430,18 +430,12 @@ have to do that. This option specifies that these (comma-separated) types are to be accepted, and ignored. -### Invoice control options: +### Cleanup control options: -* **autocleaninvoice-cycle**=*SECONDS* [plugin `autoclean`] +* **autoclean-cycle**=*SECONDS* [plugin `autoclean`] - Perform cleanup of expired invoices every *SECONDS* seconds, or disable -if 0. Usually unpaid expired invoices are uninteresting, and just take -up space in the database. - -* **autocleaninvoice-expired-by**=*SECONDS* [plugin `autoclean`] - - Control how long invoices must have been expired before they are cleaned -(if *autocleaninvoice-cycle* is non-zero). + Perform search for things to clean every *SECONDS* seconds (default +3600, or 1 hour, which is usually sufficient). ### Payment control options: diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 27c91e2e42ee..b9b71b833a34 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -1,14 +1,55 @@ #include "config.h" #include +#include +#include #include #include #include -static u64 cycle_seconds = 0, expired_by = 86400; +enum subsystem { + EXPIREDINVOICES, +#define NUM_SUBSYSTEM (EXPIREDINVOICES + 1) +}; +static const char *subsystem_str[] = { + "expiredinvoices", +}; + +static const char *subsystem_to_str(enum subsystem subsystem) +{ + assert(subsystem >= 0 && subsystem < NUM_SUBSYSTEM); + return subsystem_str[subsystem]; +} + +static bool json_to_subsystem(const char *buffer, const jsmntok_t *tok, + enum subsystem *subsystem) +{ + for (size_t i = 0; i < NUM_SUBSYSTEM; i++) { + if (memeqstr(buffer + tok->start, tok->end - tok->start, + subsystem_str[i])) { + *subsystem = i; + return true; + } + } + return false; +} + +/* For deprecated API, setting this to zero disabled autoclean */ +static u64 deprecated_cycle_seconds = UINT64_MAX; +static u64 cycle_seconds = 3600; +static u64 subsystem_age[NUM_SUBSYSTEM]; + static struct plugin_timer *cleantimer; static void do_clean(void *cb_arg); +static const char *datastore_path(const tal_t *ctx, + enum subsystem subsystem, + const char *field) +{ + return tal_fmt(ctx, "autoclean/%s/%s", + subsystem_to_str(subsystem), field); +} + static struct command_result *ignore(struct command *timer, const char *buf, const jsmntok_t *result, @@ -22,11 +63,17 @@ static struct command_result *ignore(struct command *timer, static void do_clean(void *cb_arg) { struct plugin *p = cb_arg; + + if (!subsystem_age[EXPIREDINVOICES]) { + ignore(NULL, NULL, NULL, p); + return; + } + /* FIXME: delexpiredinvoice should be in our plugin too! */ struct out_req *req = jsonrpc_request_start(p, NULL, "delexpiredinvoice", ignore, ignore, p); json_add_u64(req->js, "maxexpirytime", - time_now().ts.tv_sec - expired_by); + time_now().ts.tv_sec - subsystem_age[EXPIREDINVOICES]); send_outreq(p, req); } @@ -45,36 +92,141 @@ static struct command_result *json_autocleaninvoice(struct command *cmd, NULL)) return command_param_failed(); - cycle_seconds = *cycle; - expired_by = *exby; - cleantimer = tal_free(cleantimer); - if (cycle_seconds == 0) { + if (*cycle == 0) { + subsystem_age[EXPIREDINVOICES] = 0; response = jsonrpc_stream_success(cmd); json_add_bool(response, "enabled", false); return command_finished(cmd, response); } + cycle_seconds = *cycle; + subsystem_age[EXPIREDINVOICES] = *exby; cleantimer = plugin_timer(cmd->plugin, time_from_sec(cycle_seconds), do_clean, cmd->plugin); 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); + json_add_u64(response, "expired_by", subsystem_age[EXPIREDINVOICES]); + return command_finished(cmd, response); +} + +static struct command_result *param_age(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + uint64_t **num) +{ + *num = tal(cmd, uint64_t); + if (json_to_u64(buffer, tok, *num) && *num != 0) + return NULL; + + if (json_tok_streq(buffer, tok, "never")) { + **num = 0; + return NULL; + } + + return command_fail_badparam(cmd, name, buffer, tok, + "should be an positive 64 bit integer or 'never'"); +} + +static struct command_result *param_subsystem(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + enum subsystem **subsystem) +{ + *subsystem = tal(cmd, enum subsystem); + if (json_to_subsystem(buffer, tok, *subsystem)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be a valid subsystem name"); +} + +static struct command_result *json_success_subsystems(struct command *cmd, + const enum subsystem *subsystem) +{ + struct json_stream *response = jsonrpc_stream_success(cmd); + + json_object_start(response, "autoclean"); + for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { + if (subsystem && i != *subsystem) + continue; + json_object_start(response, subsystem_to_str(i)); + json_add_bool(response, "enabled", subsystem_age[i] != 0); + if (subsystem_age[i] != 0) + json_add_u64(response, "age", subsystem_age[i]); + /* FIXME: Add stats! */ + json_object_end(response); + } + json_object_end(response); return command_finished(cmd, response); } +static struct command_result *json_autoclean_status(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + enum subsystem *subsystem; + + if (!param(cmd, buffer, params, + p_opt("subsystem", param_subsystem, &subsystem), + NULL)) + return command_param_failed(); + + return json_success_subsystems(cmd, subsystem); +} + +static struct command_result *json_autoclean(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + enum subsystem *subsystem; + u64 *age; + + if (!param(cmd, buffer, params, + p_req("subsystem", param_subsystem, &subsystem), + p_req("age", param_age, &age), + NULL)) + return command_param_failed(); + + subsystem_age[*subsystem] = *age; + jsonrpc_set_datastore_string(cmd->plugin, cmd, + datastore_path(tmpctx, *subsystem, "age"), + tal_fmt(tmpctx, "%"PRIu64, *age), + "create-or-replace", NULL, NULL, NULL); + + return json_success_subsystems(cmd, subsystem); +} + static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { - if (cycle_seconds) { - plugin_log(p, LOG_INFORM, "autocleaning every %"PRIu64" seconds", cycle_seconds); - cleantimer = plugin_timer(p, time_from_sec(cycle_seconds), - do_clean, p); - } else - plugin_log(p, LOG_DBG, "autocleaning not active"); + if (deprecated_cycle_seconds != UINT64_MAX) { + if (deprecated_cycle_seconds == 0) { + plugin_log(p, LOG_DBG, "autocleaning not active"); + return NULL; + } else + cycle_seconds = deprecated_cycle_seconds; + } else { + bool active = false; + for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { + if (!rpc_scan_datastore_str(p, datastore_path(tmpctx, i, "age"), + JSON_SCAN(json_to_u64, &subsystem_age[i]))) + continue; + if (subsystem_age[i]) { + /* Only print this once! */ + if (!active) + plugin_log(p, LOG_INFORM, "autocleaning every %"PRIu64" seconds", cycle_seconds); + active = true; + plugin_log(p, LOG_INFORM, "cleaning %s when age > %"PRIu64" seconds", + subsystem_to_str(i), subsystem_age[i]); + } + } + } + + cleantimer = plugin_timer(p, time_from_sec(cycle_seconds), do_clean, p); return NULL; } @@ -85,8 +237,21 @@ static const struct plugin_command commands[] = { { "Set up autoclean of expired invoices. ", "Perform cleanup every {cycle_seconds} (default 3600), or disable autoclean if 0. " "Clean up expired invoices that have expired for {expired_by} seconds (default 86400). ", - json_autocleaninvoice - } + json_autocleaninvoice, + true, /* deprecated! */ + }, { + "autoclean", + "utility", + "Automatic deletion of old data (invoices, pays, forwards).", + "Takes {subsystem} and {age} in seconds ", + json_autoclean, + }, { + "autoclean-status", + "utility", + "Show status of autocleaning", + "Takes optional {subsystem}", + json_autoclean_status, + }, }; int main(int argc, char *argv[]) @@ -94,16 +259,21 @@ int main(int argc, char *argv[]) setup_locale(); plugin_main(argv, init, PLUGIN_STATIC, true, NULL, commands, ARRAY_SIZE(commands), NULL, 0, NULL, 0, NULL, 0, - plugin_option("autocleaninvoice-cycle", + plugin_option_deprecated("autocleaninvoice-cycle", "string", "Perform cleanup of expired invoices every" " given seconds, or do not autoclean if 0", - u64_option, &cycle_seconds), - plugin_option("autocleaninvoice-expired-by", + u64_option, &deprecated_cycle_seconds), + plugin_option_deprecated("autocleaninvoice-expired-by", "string", "If expired invoice autoclean enabled," " invoices that have expired for at least" " this given seconds are cleaned", - u64_option, &expired_by), + u64_option, &subsystem_age[EXPIREDINVOICES]), + plugin_option("autoclean-cycle", + "string", + "Perform cleanup every" + " given seconds", + u64_option, &cycle_seconds), NULL); } diff --git a/tests/test_invoices.py b/tests/test_invoices.py index 277f03d58ed4..ef74d40401a2 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -569,8 +569,8 @@ def test_waitanyinvoice_reversed(node_factory, executor): assert r['label'] == 'inv1' -def test_autocleaninvoice(node_factory): - l1 = node_factory.get_node() +def test_autocleaninvoice_deprecated(node_factory): + l1 = node_factory.get_node(options={'allow-deprecated-apis': True}) l1.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=4) l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=12) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 53d474c2d8d3..987086a3dc7e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2934,6 +2934,54 @@ def test_commando_badrune(node_factory): pass +def test_autoclean(node_factory): + l1 = node_factory.get_node(options={'autoclean-cycle': 10}) + + assert l1.rpc.autoclean_status('expiredinvoices')['autoclean']['expiredinvoices']['enabled'] is False + l1.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=5) + l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20) + l1.rpc.autoclean(subsystem='expiredinvoices', age=2) + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 + + # Both should still be there. + assert len(l1.rpc.listinvoices('inv1')['invoices']) == 1 + assert len(l1.rpc.listinvoices('inv2')['invoices']) == 1 + assert l1.rpc.listinvoices('inv1')['invoices'][0]['description'] == 'description1' + + # First it expires. + wait_for(lambda: only_one(l1.rpc.listinvoices('inv1')['invoices'])['status'] == 'expired') + # Now will get autocleaned + wait_for(lambda: l1.rpc.listinvoices('inv1')['invoices'] == []) + + # Keeps settings across restarts. + l1.restart() + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 + + # Disabling works + l1.rpc.autoclean(subsystem='expiredinvoices', age='never') + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False + assert 'age' not in l1.rpc.autoclean_status()['autoclean']['expiredinvoices'] + + # Same with inv2. + wait_for(lambda: only_one(l1.rpc.listinvoices('inv2')['invoices'])['status'] == 'expired') + + # Give it time to notice. + time.sleep(15) + + assert l1.rpc.listinvoices('inv2')['invoices'] != [] + + # Restart keeps it disabled. + l1.restart() + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False + assert 'age' not in l1.rpc.autoclean_status()['autoclean']['expiredinvoices'] + + # Now enable: it will get autocleaned + l1.rpc.autoclean(subsystem='expiredinvoices', age=2) + wait_for(lambda: l1.rpc.listinvoices('inv2')['invoices'] == []) + + def test_block_added_notifications(node_factory, bitcoind): """Test if a plugin gets notifications when a new block is found""" base = bitcoind.rpc.getblockchaininfo()["blocks"] From 17858c94902e505e491e16255a8b256bb4ed6b6b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:52 +0930 Subject: [PATCH 1438/1530] lightningd: deprecated "delexpiredinvoice", put functionality in autoclean plugin. Signed-off-by: Rusty Russell Changelog-Deprecated: JSON-RPC: `delexpiredinvoice`: use `autoclean-once`. --- lightningd/invoice.c | 3 +- plugins/autoclean.c | 121 ++++++++++++++++++++++++++++++++++------- tests/test_invoices.py | 11 ---- tests/test_plugin.py | 7 ++- 4 files changed, 108 insertions(+), 34 deletions(-) diff --git a/lightningd/invoice.c b/lightningd/invoice.c index c239a67a910b..459a807044cd 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -1482,7 +1482,8 @@ static const struct json_command delexpiredinvoice_command = { "delexpiredinvoice", "payment", json_delexpiredinvoice, - "Delete all expired invoices that expired as of given {maxexpirytime} (a UNIX epoch time), or all expired invoices if not specified" + "Delete all expired invoices that expired as of given {maxexpirytime} (a UNIX epoch time), or all expired invoices if not specified", + true /*deprecated*/ }; AUTODATA(json_command, &delexpiredinvoice_command); diff --git a/plugins/autoclean.c b/plugins/autoclean.c index b9b71b833a34..51ad7847e92d 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -1,6 +1,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -37,11 +38,23 @@ static bool json_to_subsystem(const char *buffer, const jsmntok_t *tok, static u64 deprecated_cycle_seconds = UINT64_MAX; static u64 cycle_seconds = 3600; static u64 subsystem_age[NUM_SUBSYSTEM]; - +static size_t cleanup_reqs_remaining; +static struct plugin *plugin; static struct plugin_timer *cleantimer; static void do_clean(void *cb_arg); +/* Fatal failures */ +static struct command_result *cmd_failed(struct command *cmd, + const char *buf, + const jsmntok_t *result, + char *cmdname) +{ + plugin_err(plugin, "Failed '%s': '%.*s'", cmdname, + json_tok_full_len(result), + json_tok_full(buf, result)); +} + static const char *datastore_path(const tal_t *ctx, enum subsystem subsystem, const char *field) @@ -50,32 +63,101 @@ static const char *datastore_path(const tal_t *ctx, subsystem_to_str(subsystem), field); } -static struct command_result *ignore(struct command *timer, - const char *buf, - const jsmntok_t *result, - void *arg) +static struct command_result *set_next_timer(struct plugin *plugin) { - struct plugin *p = arg; - cleantimer = plugin_timer(p, time_from_sec(cycle_seconds), do_clean, p); - return timer_complete(p); + plugin_log(plugin, LOG_DBG, "setting next timer"); + cleantimer = plugin_timer(plugin, time_from_sec(cycle_seconds), do_clean, plugin); + return timer_complete(plugin); } -static void do_clean(void *cb_arg) +static struct command_result *del_done(struct command *cmd, + const char *buf, + const jsmntok_t *result, + ptrint_t *unused) { - struct plugin *p = cb_arg; + assert(cleanup_reqs_remaining != 0); + plugin_log(plugin, LOG_DBG, "delinvoice_done: %zu remaining", + cleanup_reqs_remaining-1); + if (--cleanup_reqs_remaining == 0) + return set_next_timer(plugin); + return command_still_pending(cmd); +} - if (!subsystem_age[EXPIREDINVOICES]) { - ignore(NULL, NULL, NULL, p); - return; +static struct command_result *del_failed(struct command *cmd, + const char *buf, + const jsmntok_t *result, + ptrint_t *subsystemp) +{ + plugin_log(plugin, LOG_UNUSUAL, "%s del failed: %.*s", + subsystem_to_str(ptr2int(subsystemp)), + json_tok_full_len(result), + json_tok_full(buf, result)); + assert(cleanup_reqs_remaining != 0); + if (--cleanup_reqs_remaining == 0) + return command_still_pending(cmd); + return set_next_timer(plugin); +} + +static struct command_result *listinvoices_done(struct command *cmd, + const char *buf, + const jsmntok_t *result, + char *unused) +{ + const jsmntok_t *t, *inv = json_get_member(buf, result, "invoices"); + size_t i; + u64 now = time_now().ts.tv_sec; + + json_for_each_arr(i, t, inv) { + const jsmntok_t *status = json_get_member(buf, t, "status"); + const jsmntok_t *time; + u64 invtime; + + if (json_tok_streq(buf, status, "expired")) + time = json_get_member(buf, t, "expires_at"); + else + continue; + + if (!json_to_u64(buf, time, &invtime)) { + plugin_err(plugin, "Bad time '%.*s'", + json_tok_full_len(time), + json_tok_full(buf, time)); + } + + if (invtime <= now - subsystem_age[EXPIREDINVOICES]) { + struct out_req *req; + const jsmntok_t *label = json_get_member(buf, t, "label"); + + req = jsonrpc_request_start(plugin, NULL, "delinvoice", + del_done, del_failed, + int2ptr(EXPIREDINVOICES)); + json_add_tok(req->js, "label", label, buf); + json_add_tok(req->js, "status", status, buf); + send_outreq(plugin, req); + plugin_log(plugin, LOG_DBG, "Expiring %.*s", + json_tok_full_len(label), json_tok_full(buf, label)); + cleanup_reqs_remaining++; + } } - /* FIXME: delexpiredinvoice should be in our plugin too! */ - struct out_req *req = jsonrpc_request_start(p, NULL, "delexpiredinvoice", - ignore, ignore, p); - json_add_u64(req->js, "maxexpirytime", - time_now().ts.tv_sec - subsystem_age[EXPIREDINVOICES]); + if (cleanup_reqs_remaining) + return command_still_pending(cmd); + return set_next_timer(plugin); +} + +static void do_clean(void *unused) +{ + struct out_req *req = NULL; + + assert(cleanup_reqs_remaining == 0); + if (subsystem_age[EXPIREDINVOICES] != 0) { + req = jsonrpc_request_start(plugin, NULL, "listinvoices", + listinvoices_done, cmd_failed, + (char *)"listinvoices"); + send_outreq(plugin, req); + } - send_outreq(p, req); + if (!req) + set_next_timer(plugin); } static struct command_result *json_autocleaninvoice(struct command *cmd, @@ -203,6 +285,7 @@ static struct command_result *json_autoclean(struct command *cmd, static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { + plugin = p; if (deprecated_cycle_seconds != UINT64_MAX) { if (deprecated_cycle_seconds == 0) { plugin_log(p, LOG_DBG, "autocleaning not active"); diff --git a/tests/test_invoices.py b/tests/test_invoices.py index ef74d40401a2..dbb9ab7a4da7 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -411,7 +411,6 @@ def test_invoice_expiry(node_factory, executor): l2.rpc.invoice('any', 'inv1', 'description', 10) l2.rpc.invoice('any', 'inv2', 'description', 4) l2.rpc.invoice('any', 'inv3', 'description', 16) - creation = int(time.time()) # Check waitinvoice correctly waits w1 = executor.submit(l2.rpc.waitinvoice, 'inv1') @@ -437,16 +436,6 @@ def test_invoice_expiry(node_factory, executor): with pytest.raises(RpcError): w3.result() - # Test delexpiredinvoice - l2.rpc.delexpiredinvoice(maxexpirytime=creation + 8) - # only inv2 should have been deleted - assert len(l2.rpc.listinvoices()['invoices']) == 2 - assert len(l2.rpc.listinvoices('inv2')['invoices']) == 0 - # Test delexpiredinvoice all - l2.rpc.delexpiredinvoice() - # all invoices are expired and should be deleted - assert len(l2.rpc.listinvoices()['invoices']) == 0 - start = int(time.time()) inv = l2.rpc.invoice(amount_msat=123000, label='inv_s', description='description', expiry=1)['bolt11'] end = int(time.time()) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 987086a3dc7e..d17c90d7132f 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2940,6 +2940,7 @@ def test_autoclean(node_factory): assert l1.rpc.autoclean_status('expiredinvoices')['autoclean']['expiredinvoices']['enabled'] is False l1.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=5) l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20) + l1.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20) l1.rpc.autoclean(subsystem='expiredinvoices', age=2) assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 @@ -2964,7 +2965,7 @@ def test_autoclean(node_factory): assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False assert 'age' not in l1.rpc.autoclean_status()['autoclean']['expiredinvoices'] - # Same with inv2. + # Same with inv2/3 wait_for(lambda: only_one(l1.rpc.listinvoices('inv2')['invoices'])['status'] == 'expired') # Give it time to notice. @@ -2977,9 +2978,9 @@ def test_autoclean(node_factory): assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False assert 'age' not in l1.rpc.autoclean_status()['autoclean']['expiredinvoices'] - # Now enable: it will get autocleaned + # Now enable: they will get autocleaned l1.rpc.autoclean(subsystem='expiredinvoices', age=2) - wait_for(lambda: l1.rpc.listinvoices('inv2')['invoices'] == []) + wait_for(lambda: l1.rpc.listinvoices()['invoices'] == []) def test_block_added_notifications(node_factory, bitcoind): From 7da51892e828cbf629d79f048697aa9805282c4e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:52 +0930 Subject: [PATCH 1439/1530] autoclean: save stats on how much we cleaned. Signed-off-by: Rusty Russell --- plugins/autoclean.c | 40 +++++++++++++++++++++++++++++----------- tests/test_plugin.py | 4 ++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 51ad7847e92d..f91b10008ba2 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -38,6 +38,7 @@ static bool json_to_subsystem(const char *buffer, const jsmntok_t *tok, static u64 deprecated_cycle_seconds = UINT64_MAX; static u64 cycle_seconds = 3600; static u64 subsystem_age[NUM_SUBSYSTEM]; +static u64 num_cleaned[NUM_SUBSYSTEM]; static size_t cleanup_reqs_remaining; static struct plugin *plugin; static struct plugin_timer *cleantimer; @@ -70,17 +71,33 @@ static struct command_result *set_next_timer(struct plugin *plugin) return timer_complete(plugin); } +static struct command_result *clean_finished_one(struct command *cmd) +{ + assert(cleanup_reqs_remaining != 0); + if (--cleanup_reqs_remaining > 0) + return command_still_pending(cmd); + + for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { + if (num_cleaned[i] == 0) + continue; + + jsonrpc_set_datastore_string(plugin, cmd, + datastore_path(tmpctx, i, "num"), + tal_fmt(tmpctx, "%"PRIu64, num_cleaned[i]), + "create-or-replace", NULL, NULL, NULL); + + } + + return set_next_timer(plugin); +} + static struct command_result *del_done(struct command *cmd, const char *buf, const jsmntok_t *result, ptrint_t *unused) { - assert(cleanup_reqs_remaining != 0); - plugin_log(plugin, LOG_DBG, "delinvoice_done: %zu remaining", - cleanup_reqs_remaining-1); - if (--cleanup_reqs_remaining == 0) - return set_next_timer(plugin); - return command_still_pending(cmd); + num_cleaned[EXPIREDINVOICES]++; + return clean_finished_one(cmd); } static struct command_result *del_failed(struct command *cmd, @@ -92,10 +109,7 @@ static struct command_result *del_failed(struct command *cmd, subsystem_to_str(ptr2int(subsystemp)), json_tok_full_len(result), json_tok_full(buf, result)); - assert(cleanup_reqs_remaining != 0); - if (--cleanup_reqs_remaining == 0) - return command_still_pending(cmd); - return set_next_timer(plugin); + return clean_finished_one(cmd); } static struct command_result *listinvoices_done(struct command *cmd, @@ -239,7 +253,7 @@ static struct command_result *json_success_subsystems(struct command *cmd, json_add_bool(response, "enabled", subsystem_age[i] != 0); if (subsystem_age[i] != 0) json_add_u64(response, "age", subsystem_age[i]); - /* FIXME: Add stats! */ + json_add_u64(response, "cleaned", num_cleaned[i]); json_object_end(response); } json_object_end(response); @@ -311,6 +325,10 @@ static const char *init(struct plugin *p, cleantimer = plugin_timer(p, time_from_sec(cycle_seconds), do_clean, p); + for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { + rpc_scan_datastore_str(plugin, datastore_path(tmpctx, i, "num"), + JSON_SCAN(json_to_u64, &num_cleaned[i])); + } return NULL; } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index d17c90d7132f..32cb13bf8d13 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2946,6 +2946,7 @@ def test_autoclean(node_factory): assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 # Both should still be there. + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 0 assert len(l1.rpc.listinvoices('inv1')['invoices']) == 1 assert len(l1.rpc.listinvoices('inv2')['invoices']) == 1 assert l1.rpc.listinvoices('inv1')['invoices'][0]['description'] == 'description1' @@ -2954,11 +2955,13 @@ def test_autoclean(node_factory): wait_for(lambda: only_one(l1.rpc.listinvoices('inv1')['invoices'])['status'] == 'expired') # Now will get autocleaned wait_for(lambda: l1.rpc.listinvoices('inv1')['invoices'] == []) + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 # Keeps settings across restarts. l1.restart() assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 # Disabling works l1.rpc.autoclean(subsystem='expiredinvoices', age='never') @@ -2981,6 +2984,7 @@ def test_autoclean(node_factory): # Now enable: they will get autocleaned l1.rpc.autoclean(subsystem='expiredinvoices', age=2) wait_for(lambda: l1.rpc.listinvoices()['invoices'] == []) + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 def test_block_added_notifications(node_factory, bitcoind): From 660c9af1d9e1966080cfe066924abdc39e380301 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:52 +0930 Subject: [PATCH 1440/1530] autoclean: allow cleaning of paid invoices too. Signed-off-by: Rusty Russell --- plugins/autoclean.c | 28 ++++++++++++++++++++-------- tests/test_plugin.py | 17 ++++++++++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index f91b10008ba2..cdbbc81cfd9d 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -8,10 +8,12 @@ #include enum subsystem { + PAIDINVOICES, EXPIREDINVOICES, #define NUM_SUBSYSTEM (EXPIREDINVOICES + 1) }; static const char *subsystem_str[] = { + "paidinvoices", "expiredinvoices", }; @@ -94,9 +96,9 @@ static struct command_result *clean_finished_one(struct command *cmd) static struct command_result *del_done(struct command *cmd, const char *buf, const jsmntok_t *result, - ptrint_t *unused) + ptrint_t *subsystemp) { - num_cleaned[EXPIREDINVOICES]++; + num_cleaned[ptr2int(subsystemp)]++; return clean_finished_one(cmd); } @@ -124,11 +126,20 @@ static struct command_result *listinvoices_done(struct command *cmd, json_for_each_arr(i, t, inv) { const jsmntok_t *status = json_get_member(buf, t, "status"); const jsmntok_t *time; + enum subsystem subsys; u64 invtime; - if (json_tok_streq(buf, status, "expired")) + if (json_tok_streq(buf, status, "expired")) { + subsys = EXPIREDINVOICES; time = json_get_member(buf, t, "expires_at"); - else + } else if (json_tok_streq(buf, status, "paid")) { + subsys = PAIDINVOICES; + time = json_get_member(buf, t, "paid_at"); + } else + continue; + + /* Continue if we don't care. */ + if (subsystem_age[subsys] == 0) continue; if (!json_to_u64(buf, time, &invtime)) { @@ -137,17 +148,17 @@ static struct command_result *listinvoices_done(struct command *cmd, json_tok_full(buf, time)); } - if (invtime <= now - subsystem_age[EXPIREDINVOICES]) { + if (invtime <= now - subsystem_age[subsys]) { struct out_req *req; const jsmntok_t *label = json_get_member(buf, t, "label"); req = jsonrpc_request_start(plugin, NULL, "delinvoice", del_done, del_failed, - int2ptr(EXPIREDINVOICES)); + int2ptr(subsys)); json_add_tok(req->js, "label", label, buf); json_add_tok(req->js, "status", status, buf); send_outreq(plugin, req); - plugin_log(plugin, LOG_DBG, "Expiring %.*s", + plugin_log(plugin, LOG_DBG, "Cleaning up %.*s", json_tok_full_len(label), json_tok_full(buf, label)); cleanup_reqs_remaining++; } @@ -163,7 +174,8 @@ static void do_clean(void *unused) struct out_req *req = NULL; assert(cleanup_reqs_remaining == 0); - if (subsystem_age[EXPIREDINVOICES] != 0) { + if (subsystem_age[EXPIREDINVOICES] != 0 + || subsystem_age[PAIDINVOICES] != 0) { req = jsonrpc_request_start(plugin, NULL, "listinvoices", listinvoices_done, cmd_failed, (char *)"listinvoices"); diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 32cb13bf8d13..a9d83a9e4643 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2935,12 +2935,14 @@ def test_commando_badrune(node_factory): def test_autoclean(node_factory): - l1 = node_factory.get_node(options={'autoclean-cycle': 10}) + l0, l1 = node_factory.line_graph(2, opts={'autoclean-cycle': 10, + 'may_reconnect': True}) assert l1.rpc.autoclean_status('expiredinvoices')['autoclean']['expiredinvoices']['enabled'] is False l1.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=5) l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20) l1.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20) + inv4 = l1.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000) l1.rpc.autoclean(subsystem='expiredinvoices', age=2) assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 @@ -2983,8 +2985,21 @@ def test_autoclean(node_factory): # Now enable: they will get autocleaned l1.rpc.autoclean(subsystem='expiredinvoices', age=2) + wait_for(lambda: len(l1.rpc.listinvoices()['invoices']) == 1) + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 + + # Reconnect, l0 pays invoice, we test paid expiry. + l1.rpc.connect(l0.info['id'], 'localhost', l0.port) + l0.rpc.pay(inv4['bolt11']) + + assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is False + assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 0 + l1.rpc.autoclean(subsystem='paidinvoices', age=1) + assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is True + wait_for(lambda: l1.rpc.listinvoices()['invoices'] == []) assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 + assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 1 def test_block_added_notifications(node_factory, bitcoind): From 4cab396cc8f9eb9894f3c5745366058bd058d881 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:52 +0930 Subject: [PATCH 1441/1530] autoclean: handle cleaning of old payments (not just invoices). Signed-off-by: Rusty Russell --- plugins/autoclean.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_plugin.py | 23 +++++++++++++-- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index cdbbc81cfd9d..b1670f20845c 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -8,11 +8,15 @@ #include enum subsystem { + SUCCEEDEDPAYS, + FAILEDPAYS, PAIDINVOICES, EXPIREDINVOICES, #define NUM_SUBSYSTEM (EXPIREDINVOICES + 1) }; static const char *subsystem_str[] = { + "succeededpays", + "failedpays", "paidinvoices", "expiredinvoices", }; @@ -169,11 +173,73 @@ static struct command_result *listinvoices_done(struct command *cmd, return set_next_timer(plugin); } +static struct command_result *listsendpays_done(struct command *cmd, + const char *buf, + const jsmntok_t *result, + char *unused) +{ + const jsmntok_t *t, *pays = json_get_member(buf, result, "payments"); + size_t i; + u64 now = time_now().ts.tv_sec; + + json_for_each_arr(i, t, pays) { + const jsmntok_t *status = json_get_member(buf, t, "status"); + const jsmntok_t *time; + enum subsystem subsys; + u64 paytime; + + if (json_tok_streq(buf, status, "failed")) { + subsys = FAILEDPAYS; + } else if (json_tok_streq(buf, status, "complete")) { + subsys = SUCCEEDEDPAYS; + } else + continue; + + /* Continue if we don't care. */ + if (subsystem_age[subsys] == 0) + continue; + + time = json_get_member(buf, t, "created_at"); + if (!json_to_u64(buf, time, &paytime)) { + plugin_err(plugin, "Bad created_at '%.*s'", + json_tok_full_len(time), + json_tok_full(buf, time)); + } + + if (paytime <= now - subsystem_age[subsys]) { + struct out_req *req; + const jsmntok_t *phash = json_get_member(buf, t, "payment_hash"); + + req = jsonrpc_request_start(plugin, NULL, "delpay", + del_done, del_failed, + int2ptr(subsys)); + json_add_tok(req->js, "payment_hash", phash, buf); + json_add_tok(req->js, "status", status, buf); + send_outreq(plugin, req); + plugin_log(plugin, LOG_DBG, "Cleaning up %.*s", + json_tok_full_len(phash), json_tok_full(buf, phash)); + cleanup_reqs_remaining++; + } + } + + if (cleanup_reqs_remaining) + return command_still_pending(cmd); + return set_next_timer(plugin); +} + static void do_clean(void *unused) { struct out_req *req = NULL; assert(cleanup_reqs_remaining == 0); + if (subsystem_age[SUCCEEDEDPAYS] != 0 + || subsystem_age[FAILEDPAYS] != 0) { + req = jsonrpc_request_start(plugin, NULL, "listsendpays", + listsendpays_done, cmd_failed, + (char *)"listsendpays"); + send_outreq(plugin, req); + } + if (subsystem_age[EXPIREDINVOICES] != 0 || subsystem_age[PAIDINVOICES] != 0) { req = jsonrpc_request_start(plugin, NULL, "listinvoices", diff --git a/tests/test_plugin.py b/tests/test_plugin.py index a9d83a9e4643..3c5dc0504d49 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2943,6 +2943,7 @@ def test_autoclean(node_factory): l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20) l1.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20) inv4 = l1.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000) + inv5 = l1.rpc.invoice(amount_msat=12300, label='inv5', description='description5', expiry=2000) l1.rpc.autoclean(subsystem='expiredinvoices', age=2) assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 @@ -2959,8 +2960,9 @@ def test_autoclean(node_factory): wait_for(lambda: l1.rpc.listinvoices('inv1')['invoices'] == []) assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 - # Keeps settings across restarts. + # Keeps settings across restarts l1.restart() + assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 @@ -2985,13 +2987,18 @@ def test_autoclean(node_factory): # Now enable: they will get autocleaned l1.rpc.autoclean(subsystem='expiredinvoices', age=2) - wait_for(lambda: len(l1.rpc.listinvoices()['invoices']) == 1) + wait_for(lambda: len(l1.rpc.listinvoices()['invoices']) == 2) assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 # Reconnect, l0 pays invoice, we test paid expiry. l1.rpc.connect(l0.info['id'], 'localhost', l0.port) l0.rpc.pay(inv4['bolt11']) + # We manually delete inv5 so we can have l0 fail a payment. + l1.rpc.delinvoice('inv5', 'unpaid') + with pytest.raises(RpcError, match='WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): + l0.rpc.pay(inv5['bolt11']) + assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is False assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 0 l1.rpc.autoclean(subsystem='paidinvoices', age=1) @@ -3001,6 +3008,18 @@ def test_autoclean(node_factory): assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 1 + assert only_one(l0.rpc.listpays(inv5['bolt11'])['pays'])['status'] == 'failed' + assert only_one(l0.rpc.listpays(inv4['bolt11'])['pays'])['status'] == 'complete' + l0.rpc.autoclean(subsystem='failedpays', age=2) + + wait_for(lambda: l0.rpc.listpays(inv5['bolt11'])['pays'] == []) + assert l0.rpc.autoclean_status()['autoclean']['failedpays']['cleaned'] == 1 + assert l0.rpc.autoclean_status()['autoclean']['succeededpays']['cleaned'] == 0 + + l0.rpc.autoclean(subsystem='succeededpays', age=2) + wait_for(lambda: l0.rpc.listpays(inv4['bolt11'])['pays'] == []) + assert l0.rpc.listsendpays() == {'payments': []} + def test_block_added_notifications(node_factory, bitcoind): """Test if a plugin gets notifications when a new block is found""" From 2a5660b3bca78b8f49f5c57ea3ed7083efdfc423 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:52 +0930 Subject: [PATCH 1442/1530] lightningd: index to speed up sendpay / listsendpays contrib/giantnode.py shows we're spending a lot of time looking up payments by payment_hash: sendpays does it to see if we've already paid, and bookkeeper does it in listsendpays: ``` - 94.52% 0.00% lightningd lightningd [.] read_json - 94.52% read_json - 94.48% parse_request - 94.46% plugin_hook_call_rpc_command - plugin_hook_call_ - rpc_command_hook_final - 94.46% command_exec - 49.08% json_sendpay - 49.01% send_payment - 48.86% send_payment_core - 48.84% wallet_payment_list - 48.80% db_step + db_sqlite3_step - 45.38% json_listsendpays - 45.36% wallet_payment_list - 45.30% db_step + 45.30% db_sqlite3_step ``` This doesn't actually make much of a difference, so see next patch. Signed-off-by: Rusty Russell --- wallet/db.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wallet/db.c b/wallet/db.c index 88cd1ed1021e..51a507543e1b 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -885,7 +885,8 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE channels ADD alias_remote BIGINT DEFAULT NULL"), NULL}, /* Cheeky immediate completion as best effort approximation of real completion time */ {SQL("ALTER TABLE payments ADD completed_at INTEGER DEFAULT NULL;"), NULL}, - {SQL("UPDATE payments SET completed_at = timestamp WHERE status != 0;"), NULL} + {SQL("UPDATE payments SET completed_at = timestamp WHERE status != 0;"), NULL}, + {SQL("CREATE INDEX payments_idx ON payments (payment_hash)"), NULL}, }; /* Released versions are of form v{num}[.{num}]* */ From 2022e4a7a9d02473b521a24258c210a2ce357368 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1443/1530] wallet: simplify payments lookup so sqlite3 uses index. Filtering by status is rare, so we can do it in the caller; just let sqlite3 filter by payment_hash. With ~650,000 payments in db: Before: ``` 129/300000 complete 5.60/sec (33078 invs, 169 pays, 0 retries) in 30 seconds. 19 hours-14 hours remaining. 201/300000 complete 7.20/sec (43519 invs, 241 pays, 0 retries) in 40 seconds. 16 hours-11 hours remaining. 257/300000 complete 5.60/sec (54568 invs, 289 pays, 0 retries) in 50 seconds. 16 hours-14 hours remaining. 305/300000 complete 4.80/sec (65772 invs, 337 pays, 0 retries) in 60 seconds. 16 hours-17 hours remaining. 361/300000 complete 5.60/sec (75875 invs, 401 pays, 0 retries) in 70 seconds. 16 hours-14 hours remaining. ``` After: ``` 760/300000 complete 40.00/sec (19955 invs, 824 pays, 0 retries) in 20 seconds. 2 hours-2 hours remaining. 1176/300000 complete 41.60/sec (30082 invs, 1224 pays, 0 retries) in 30 seconds. 2 hours-119 minutes remaining. 1584/300000 complete 40.80/sec (40224 invs, 1640 pays, 0 retries) in 40 seconds. 2 hours-2 hours remaining. 1984/300000 complete 40.00/sec (49938 invs, 2048 pays, 0 retries) in 50 seconds. 2 hours-2 hours remaining. ``` Signed-off-by: Rusty Russell --- lightningd/offer.c | 2 +- lightningd/pay.c | 62 +++++++++++++++++------------- wallet/wallet.c | 94 +++++++++++++++++++++++++--------------------- wallet/wallet.h | 5 ++- 4 files changed, 91 insertions(+), 72 deletions(-) diff --git a/lightningd/offer.c b/lightningd/offer.c index 67ca0e10fbad..2e6b76711ced 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -285,7 +285,7 @@ static struct command_result *prev_payment(struct command *cmd, bool prev_paid = false; assert(!invreq->payer_info); - payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL, NULL); + payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL); for (size_t i = 0; i < tal_count(payments); i++) { const struct tlv_invoice *inv; diff --git a/lightningd/pay.c b/lightningd/pay.c index ec2366802a28..5e6d37ffae75 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -37,15 +37,16 @@ struct sendpay_command { struct command *cmd; }; -static bool string_to_payment_status(const char *status_str, enum wallet_payment_status *status) +static bool string_to_payment_status(const char *status_str, size_t len, + enum wallet_payment_status *status) { - if (streq(status_str, "complete")) { + if (memeqstr(status_str, len, "complete")) { *status = PAYMENT_COMPLETE; return true; - } else if (streq(status_str, "pending")) { + } else if (memeqstr(status_str, len, "pending")) { *status = PAYMENT_PENDING; return true; - } else if (streq(status_str, "failed")) { + } else if (memeqstr(status_str, len, "failed")) { *status = PAYMENT_FAILED; return true; } @@ -883,7 +884,7 @@ send_payment_core(struct lightningd *ld, bool have_complete = false; /* Now, do we already have one or more payments? */ - payments = wallet_payment_list(tmpctx, ld->wallet, rhash, NULL); + payments = wallet_payment_list(tmpctx, ld->wallet, rhash); for (size_t i = 0; i < tal_count(payments); i++) { log_debug(ld->log, "Payment %zu/%zu: %s %s", i, tal_count(payments), @@ -1545,6 +1546,22 @@ static const struct json_command waitsendpay_command = { }; AUTODATA(json_command, &waitsendpay_command); +static struct command_result *param_payment_status(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + enum wallet_payment_status **status) +{ + *status = tal(cmd, enum wallet_payment_status); + if (string_to_payment_status(buffer + tok->start, + tok->end - tok->start, + *status)) + return NULL; + + return command_fail_badparam(cmd, name, buffer, tok, + "should be an invoice status"); +} + static struct command_result *json_listsendpays(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, @@ -1553,13 +1570,14 @@ static struct command_result *json_listsendpays(struct command *cmd, const struct wallet_payment **payments; struct json_stream *response; struct sha256 *rhash; - const char *invstring, *status_str; + const char *invstring; + enum wallet_payment_status *status; if (!param(cmd, buffer, params, /* FIXME: parameter should be invstring now */ p_opt("bolt11", param_string, &invstring), p_opt("payment_hash", param_sha256, &rhash), - p_opt("status", param_string, &status_str), + p_opt("status", param_payment_status, &status), NULL)) return command_param_failed(); @@ -1591,19 +1609,13 @@ static struct command_result *json_listsendpays(struct command *cmd, } } - if (status_str) { - enum wallet_payment_status status; - - if (!string_to_payment_status(status_str, &status)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str); - payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash, &status); - } else - payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash, NULL); - + payments = wallet_payment_list(cmd, cmd->ld->wallet, rhash); response = json_stream_success(cmd); json_array_start(response, "payments"); for (size_t i = 0; i < tal_count(payments); i++) { + if (status && payments[i]->status != *status) + continue; json_object_start(response, NULL); json_add_payment_fields(response, payments[i]); json_object_end(response); @@ -1629,40 +1641,36 @@ static struct command_result *json_delpay(struct command *cmd, { struct json_stream *response; const struct wallet_payment **payments; - const char *status_str; - enum wallet_payment_status status; + enum wallet_payment_status *status; struct sha256 *payment_hash; if (!param(cmd, buffer, params, p_req("payment_hash", param_sha256, &payment_hash), - p_req("status", param_string, &status_str), + p_req("status", param_payment_status, &status), NULL)) return command_param_failed(); - if (!string_to_payment_status(status_str, &status)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unrecognized status: %s", status_str); - - switch(status){ + switch (*status) { case PAYMENT_COMPLETE: case PAYMENT_FAILED: break; case PAYMENT_PENDING: return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid status: %s", - payment_status_to_string(status)); + payment_status_to_string(*status)); } - payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash, NULL); + payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash); if (tal_count(payments) == 0) return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s", type_to_string(tmpctx, struct sha256, payment_hash)); for (int i = 0; i < tal_count(payments); i++) { - if (payments[i]->status != status) { + if (payments[i]->status != *status) { return command_fail(cmd, PAY_STATUS_UNEXPECTED, "Payment with hash %s has %s status but it should be %s", type_to_string(tmpctx, struct sha256, payment_hash), payment_status_to_string(payments[i]->status), - payment_status_to_string(status)); + payment_status_to_string(*status)); } } diff --git a/wallet/wallet.c b/wallet/wallet.c index 058aed4e0346..93ffb96387e1 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3518,8 +3518,7 @@ void wallet_payment_set_failinfo(struct wallet *wallet, const struct wallet_payment ** wallet_payment_list(const tal_t *ctx, struct wallet *wallet, - const struct sha256 *payment_hash, - enum wallet_payment_status *status) + const struct sha256 *payment_hash) { const struct wallet_payment **payments; struct db_stmt *stmt; @@ -3528,47 +3527,58 @@ wallet_payment_list(const tal_t *ctx, payments = tal_arr(ctx, const struct wallet_payment *, 0); - u8 enable_payment_hash = payment_hash != NULL ? 0 : 1; - u8 enable_status = status != NULL ? 0 : 1; - - stmt = db_prepare_v2(wallet->db, SQL("SELECT" - " id" - ", status" - ", destination" - ", msatoshi" - ", payment_hash" - ", timestamp" - ", payment_preimage" - ", path_secrets" - ", route_nodes" - ", route_channels" - ", msatoshi_sent" - ", description" - ", bolt11" - ", paydescription" - ", failonionreply" - ", total_msat" - ", partid" - ", local_offer_id" - ", groupid" - ", completed_at" - " FROM payments" - " WHERE" - " (payment_hash = ? OR 1=?) AND" - " (status = ? OR 1=?)" - " ORDER BY id;")); - - if (payment_hash) + if (payment_hash) { + stmt = db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", status" + ", destination" + ", msatoshi" + ", payment_hash" + ", timestamp" + ", payment_preimage" + ", path_secrets" + ", route_nodes" + ", route_channels" + ", msatoshi_sent" + ", description" + ", bolt11" + ", paydescription" + ", failonionreply" + ", total_msat" + ", partid" + ", local_offer_id" + ", groupid" + ", completed_at" + " FROM payments" + " WHERE" + " payment_hash = ?" + " ORDER BY id;")); db_bind_sha256(stmt, 0, payment_hash); - else - db_bind_null(stmt, 0); - db_bind_int(stmt, 1, enable_payment_hash); - if (status) - db_bind_int(stmt, 2, wallet_payment_status_in_db(*status)); - else - db_bind_null(stmt, 2); - db_bind_int(stmt, 3, enable_status); - + } else { + stmt = db_prepare_v2(wallet->db, SQL("SELECT" + " id" + ", status" + ", destination" + ", msatoshi" + ", payment_hash" + ", timestamp" + ", payment_preimage" + ", path_secrets" + ", route_nodes" + ", route_channels" + ", msatoshi_sent" + ", description" + ", bolt11" + ", paydescription" + ", failonionreply" + ", total_msat" + ", partid" + ", local_offer_id" + ", groupid" + ", completed_at" + " FROM payments" + " ORDER BY id;")); + } db_query_prepared(stmt); for (i = 0; db_step(stmt); i++) { diff --git a/wallet/wallet.h b/wallet/wallet.h index 4ce09a13d16b..dfc66e14b0b7 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1183,8 +1183,9 @@ void wallet_payment_set_failinfo(struct wallet *wallet, */ const struct wallet_payment **wallet_payment_list(const tal_t *ctx, struct wallet *wallet, - const struct sha256 *payment_hash, - enum wallet_payment_status *status); + const struct sha256 *payment_hash) + NON_NULL_ARGS(2); + /** * wallet_payments_by_offer - Retrieve a list of payments for this local_offer_id From 63457229cbe287cb04ddf907898794a12288d721 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1444/1530] wallet: replace forwarded_payments table with forwards table. This one directly contains the scids of the channels involved, not references, so can outlive the channels. As a side-effect, however, it now never lists `payment_hash`. Having it listed (via join) is not possible as it is a *string* in the channels table, and difficult anyway because of channel aliases. Signed-off-by: Rusty Russell --- tests/test_misc.py | 5 ---- tests/test_pay.py | 2 +- tests/test_plugin.py | 4 +++ wallet/db.c | 33 ++++++++++++++++++++++++ wallet/wallet.c | 61 ++++++++++++++++++++------------------------ 5 files changed, 66 insertions(+), 39 deletions(-) diff --git a/tests/test_misc.py b/tests/test_misc.py index b77e9cbc029a..e5251e15e196 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2413,12 +2413,7 @@ def test_listforwards(node_factory, bitcoind): l1.rpc.waitsendpay(failed_inv['payment_hash']) all_forwards = l2.rpc.listforwards()['forwards'] - print(json.dumps(all_forwards, indent=True)) - assert len(all_forwards) == 3 - assert i31['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) - assert i41['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) - assert failed_inv['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) # status=settled settled_forwards = l2.rpc.listforwards(status='settled')['forwards'] diff --git a/tests/test_pay.py b/tests/test_pay.py index ea54a9eb51d3..382957eb5f85 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -1389,7 +1389,7 @@ def test_forward_stats(node_factory, bitcoind): # Select all forwardings, ordered by htlc_id to ensure the order # matches below forwardings = l2.db_query("SELECT *, in_msatoshi - out_msatoshi as fee " - "FROM forwarded_payments " + "FROM forwards " "ORDER BY in_htlc_id;") assert(len(forwardings) == 3) states = [f['state'] for f in forwardings] diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 3c5dc0504d49..d16f80b26e7e 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1345,6 +1345,10 @@ def test_forward_event_notification(node_factory, bitcoind, executor): plugin_stats = l2.rpc.call('listforwards_plugin')['forwards'] assert len(plugin_stats) == 6 + # We don't have payment_hash in listforwards any more. + for p in plugin_stats: + del p['payment_hash'] + # use stats to build what we expect went to plugin. expect = stats[0].copy() # First event won't have conclusion. diff --git a/wallet/db.c b/wallet/db.c index 51a507543e1b..ee9e940258a4 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -887,6 +887,39 @@ static struct migration dbmigrations[] = { {SQL("ALTER TABLE payments ADD completed_at INTEGER DEFAULT NULL;"), NULL}, {SQL("UPDATE payments SET completed_at = timestamp WHERE status != 0;"), NULL}, {SQL("CREATE INDEX payments_idx ON payments (payment_hash)"), NULL}, + /* forwards table outlives the channels, so we move there from old forwarded_payments table; + * but here the ids are the HTLC numbers, not the internal db ids. */ + {SQL("CREATE TABLE forwards (" + "in_channel_scid BIGINT" + ", in_htlc_id BIGINT" + ", out_channel_scid BIGINT" + ", out_htlc_id BIGINT" + ", in_msatoshi BIGINT" + ", out_msatoshi BIGINT" + ", state INTEGER" + ", received_time BIGINT" + ", resolved_time BIGINT" + ", failcode INTEGER" + ", forward_style INTEGER" + ", PRIMARY KEY(in_channel_scid, in_htlc_id))"), NULL}, + {SQL("INSERT INTO forwards SELECT" + " in_channel_scid" + ", (SELECT channel_htlc_id FROM channel_htlcs WHERE id = forwarded_payments.in_htlc_id)" + ", out_channel_scid" + ", (SELECT channel_htlc_id FROM channel_htlcs WHERE id = forwarded_payments.out_htlc_id)" + ", in_msatoshi" + ", out_msatoshi" + ", state" + ", received_time" + ", resolved_time" + ", failcode" + ", forward_style" + " FROM forwarded_payments" + " WHERE" + " in_htlc_id IS NOT NULL"), NULL}, + {SQL("DROP INDEX forwarded_payments_state;"), NULL}, + {SQL("DROP INDEX forwarded_payments_out_htlc_id;"), NULL}, + {SQL("DROP TABLE forwarded_payments;"), NULL}, }; /* Released versions are of form v{num}[.{num}]* */ diff --git a/wallet/wallet.c b/wallet/wallet.c index 93ffb96387e1..4e88bc5fed1d 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4356,14 +4356,14 @@ static bool wallet_forwarded_payment_update(struct wallet *w, * having to have two versions of the update statement (one with and * one without the htlc_out restriction).*/ stmt = db_prepare_v2(w->db, - SQL("UPDATE forwarded_payments SET" + SQL("UPDATE forwards SET" " in_msatoshi=?" ", out_msatoshi=?" ", state=?" ", resolved_time=?" ", failcode=?" ", forward_style=?" - " WHERE in_htlc_id=?")); + " WHERE in_htlc_id=? AND in_channel_scid=?")); db_bind_amount_msat(stmt, 0, &in->msat); if (out) { @@ -4392,7 +4392,9 @@ static bool wallet_forwarded_payment_update(struct wallet *w, db_bind_null(stmt, 5); else db_bind_int(stmt, 5, forward_style_in_db(forward_style)); - db_bind_u64(stmt, 6, in->dbid); + db_bind_u64(stmt, 6, in->key.id); + db_bind_u64(stmt, 7, + channel_scid_or_local_alias(in->key.channel)->u64); db_exec_prepared_v2(stmt); changed = db_count_changes(stmt) != 0; tal_free(stmt); @@ -4421,7 +4423,7 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, goto notify; stmt = db_prepare_v2(w->db, - SQL("INSERT INTO forwarded_payments (" + SQL("INSERT INTO forwards (" " in_htlc_id" ", out_htlc_id" ", in_channel_scid" @@ -4434,7 +4436,7 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, ", failcode" ", forward_style" ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); - db_bind_u64(stmt, 0, in->dbid); + db_bind_u64(stmt, 0, in->key.id); /* FORWARD_LOCAL_FAILED may occur before we get htlc_out */ if (!out || !scid_out) { @@ -4443,7 +4445,7 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, } if (out) - db_bind_u64(stmt, 1, out->dbid); + db_bind_u64(stmt, 1, out->key.id); else db_bind_null(stmt, 1); @@ -4499,7 +4501,7 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w) stmt = db_prepare_v2(w->db, SQL("SELECT" " CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)" - " FROM forwarded_payments " + " FROM forwards " "WHERE state = ?;")); db_bind_int(stmt, 0, wallet_forward_status_in_db(FORWARD_SETTLED)); db_query_prepared(stmt); @@ -4549,21 +4551,19 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, stmt = db_prepare_v2( w->db, SQL("SELECT" - " f.state" + " 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 " - ", f.forward_style " - "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 = ?)")); + ", received_time" + ", resolved_time" + ", failcode " + ", forward_style " + "FROM forwards " + "WHERE (1 = ? OR state = ?) AND " + "(1 = ? OR in_channel_scid = ?) AND " + "(1 = ? OR out_channel_scid = ?)")); if (status == FORWARD_ANY) { // any status @@ -4600,7 +4600,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, for (count=0; db_step(stmt); count++) { tal_resize(&results, count+1); struct forwarding *cur = &results[count]; - cur->status = db_col_int(stmt, "f.state"); + cur->status = db_col_int(stmt, "state"); db_col_amount_msat(stmt, "in_msatoshi", &cur->msat_in); if (!db_col_is_null(stmt, "out_msatoshi")) { @@ -4622,13 +4622,8 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, cur->fee = AMOUNT_MSAT(0); } - if (!db_col_is_null(stmt, "payment_hash")) { - cur->payment_hash = tal(ctx, struct sha256); - db_col_sha256(stmt, "payment_hash", cur->payment_hash); - } else { - cur->payment_hash = NULL; - } - + /* FIXME: This now requires complex join to determine! */ + cur->payment_hash = NULL; cur->channel_in.u64 = db_col_u64(stmt, "in_channel_scid"); if (!db_col_is_null(stmt, "out_channel_scid")) { @@ -4639,28 +4634,28 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, cur->channel_out.u64 = 0; } - cur->received_time = db_col_timeabs(stmt, "f.received_time"); + cur->received_time = db_col_timeabs(stmt, "received_time"); - if (!db_col_is_null(stmt, "f.resolved_time")) { + if (!db_col_is_null(stmt, "resolved_time")) { cur->resolved_time = tal(ctx, struct timeabs); *cur->resolved_time - = db_col_timeabs(stmt, "f.resolved_time"); + = db_col_timeabs(stmt, "resolved_time"); } else { cur->resolved_time = NULL; } - if (!db_col_is_null(stmt, "f.failcode")) { + if (!db_col_is_null(stmt, "failcode")) { assert(cur->status == FORWARD_FAILED || cur->status == FORWARD_LOCAL_FAILED); - cur->failcode = db_col_int(stmt, "f.failcode"); + cur->failcode = db_col_int(stmt, "failcode"); } else { cur->failcode = 0; } - if (db_col_is_null(stmt, "f.forward_style")) { + if (db_col_is_null(stmt, "forward_style")) { cur->forward_style = FORWARD_STYLE_UNKNOWN; } else { cur->forward_style - = forward_style_in_db(db_col_int(stmt, "f.forward_style")); + = forward_style_in_db(db_col_int(stmt, "forward_style")); } } tal_free(stmt); From 33a6b188919ffb6f0abfc5902eb50f8bc564641e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1445/1530] db/bindings: rename db_bind_short_channel_id to db_bind_short_channel_id_str, add db_bind_scid. Although it's deprecated already (because it stores as string), it's better to make the name explicit. And create a new helper which stores as BIGINT. Signed-off-by: Rusty Russell --- db/bindings.c | 16 ++++++++++++++-- db/bindings.h | 10 ++++++++-- wallet/wallet.c | 4 ++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/db/bindings.c b/db/bindings.c index df8b3827c2df..2f3546a2bd6c 100644 --- a/db/bindings.c +++ b/db/bindings.c @@ -159,13 +159,19 @@ void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *pk) db_bind_blob(stmt, pos, der, PUBKEY_CMPR_LEN); } -void db_bind_short_channel_id(struct db_stmt *stmt, int col, - const struct short_channel_id *id) +void db_bind_short_channel_id_str(struct db_stmt *stmt, int col, + const struct short_channel_id *id) { char *ser = short_channel_id_to_str(stmt, id); db_bind_text(stmt, col, ser); } +void db_bind_scid(struct db_stmt *stmt, int col, + const struct short_channel_id *id) +{ + db_bind_u64(stmt, col, id->u64); +} + void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, const struct short_channel_id *id) { @@ -373,6 +379,12 @@ bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, return short_channel_id_from_str(source, sourcelen, dest); } +void db_col_scid(struct db_stmt *stmt, const char *colname, + struct short_channel_id *dest) +{ + dest->u64 = db_col_u64(stmt, colname); +} + struct short_channel_id * db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname) { diff --git a/db/bindings.h b/db/bindings.h index 298dc6d4838d..74febcf83f01 100644 --- a/db/bindings.h +++ b/db/bindings.h @@ -37,8 +37,11 @@ void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *ni); void db_bind_node_id_arr(struct db_stmt *stmt, int col, const struct node_id *ids); void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *p); -void db_bind_short_channel_id(struct db_stmt *stmt, int col, - const struct short_channel_id *id); +/* DO NOT USE, deprecated! */ +void db_bind_short_channel_id_str(struct db_stmt *stmt, int col, + const struct short_channel_id *id); +void db_bind_scid(struct db_stmt *stmt, int col, + const struct short_channel_id *id); void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, const struct short_channel_id *id); void db_bind_signature(struct db_stmt *stmt, int col, @@ -83,8 +86,11 @@ struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); void db_col_pubkey(struct db_stmt *stmt, const char *colname, struct pubkey *p); +/* DO NOT USE: deprecated */ bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, struct short_channel_id *dest); +void db_col_scid(struct db_stmt *stmt, const char *colname, + struct short_channel_id *dest); struct short_channel_id * db_col_short_channel_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); bool db_col_signature(struct db_stmt *stmt, const char *colname, diff --git a/wallet/wallet.c b/wallet/wallet.c index 4e88bc5fed1d..9a457d09fb6a 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1903,7 +1903,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " WHERE id=?")); // 46 db_bind_u64(stmt, 0, chan->their_shachain.id); if (chan->scid) - db_bind_short_channel_id(stmt, 1, chan->scid); + db_bind_short_channel_id_str(stmt, 1, chan->scid); else db_bind_null(stmt, 1); @@ -3495,7 +3495,7 @@ void wallet_payment_set_failinfo(struct wallet *wallet, db_bind_null(stmt, 4); if (failchannel) { - db_bind_short_channel_id(stmt, 5, failchannel); + db_bind_short_channel_id_str(stmt, 5, failchannel); db_bind_int(stmt, 8, faildirection); } else { db_bind_null(stmt, 5); From e286c38c6ff66680638384e0c22210ccd86fb49d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1446/1530] wallet: use db_col_scid / db_bind_scid where possible. Without helpers, we were using the u64 functions. Signed-off-by: Rusty Russell --- wallet/wallet.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/wallet/wallet.c b/wallet/wallet.c index 9a457d09fb6a..6d6269678896 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1303,14 +1303,14 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm if (!db_col_is_null(stmt, "alias_local")) { alias[LOCAL] = tal(tmpctx, struct short_channel_id); - alias[LOCAL]->u64 = db_col_u64(stmt, "alias_local"); + db_col_scid(stmt, "alias_local", alias[LOCAL]); } else { alias[LOCAL] = NULL; } if (!db_col_is_null(stmt, "alias_remote")) { alias[REMOTE] = tal(tmpctx, struct short_channel_id); - alias[REMOTE]->u64 = db_col_u64(stmt, "alias_remote"); + db_col_scid(stmt, "alias_remote", alias[REMOTE]); } else { alias[REMOTE] = NULL; } @@ -1970,12 +1970,12 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_amount_msat(stmt, 43, &chan->htlc_maximum_msat); if (chan->alias[LOCAL] != NULL) - db_bind_u64(stmt, 44, chan->alias[LOCAL]->u64); + db_bind_scid(stmt, 44, chan->alias[LOCAL]); else db_bind_null(stmt, 44); if (chan->alias[REMOTE] != NULL) - db_bind_u64(stmt, 45, chan->alias[REMOTE]->u64); + db_bind_scid(stmt, 45, chan->alias[REMOTE]); else db_bind_null(stmt, 45); @@ -4393,8 +4393,7 @@ static bool wallet_forwarded_payment_update(struct wallet *w, else db_bind_int(stmt, 5, forward_style_in_db(forward_style)); db_bind_u64(stmt, 6, in->key.id); - db_bind_u64(stmt, 7, - channel_scid_or_local_alias(in->key.channel)->u64); + db_bind_scid(stmt, 7, channel_scid_or_local_alias(in->key.channel)); db_exec_prepared_v2(stmt); changed = db_count_changes(stmt) != 0; tal_free(stmt); @@ -4454,10 +4453,10 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, * the sender used to specify the channel, but that's under * control of the remote end. */ assert(in->key.channel->scid != NULL || in->key.channel->alias[LOCAL]); - db_bind_u64(stmt, 2, channel_scid_or_local_alias(in->key.channel)->u64); + db_bind_scid(stmt, 2, channel_scid_or_local_alias(in->key.channel)); if (scid_out) - db_bind_u64(stmt, 3, scid_out->u64); + db_bind_scid(stmt, 3, scid_out); else db_bind_null(stmt, 3); db_bind_amount_msat(stmt, 4, &in->msat); @@ -4578,7 +4577,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, if (chan_in) { // specific in_channel db_bind_int(stmt, 2, 0); - db_bind_u64(stmt, 3, chan_in->u64); + db_bind_scid(stmt, 3, chan_in); } else { // any in_channel db_bind_int(stmt, 2, 1); @@ -4588,7 +4587,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, if (chan_out) { // specific out_channel db_bind_int(stmt, 4, 0); - db_bind_u64(stmt, 5, chan_out->u64); + db_bind_scid(stmt, 5, chan_out); } else { // any out_channel db_bind_int(stmt, 4, 1); @@ -4624,11 +4623,10 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, /* FIXME: This now requires complex join to determine! */ cur->payment_hash = NULL; - cur->channel_in.u64 = db_col_u64(stmt, "in_channel_scid"); + db_col_scid(stmt, "in_channel_scid", &cur->channel_in); if (!db_col_is_null(stmt, "out_channel_scid")) { - cur->channel_out.u64 - = db_col_u64(stmt, "out_channel_scid"); + db_col_scid(stmt, "out_channel_scid", &cur->channel_out); } else { assert(cur->status == FORWARD_LOCAL_FAILED); cur->channel_out.u64 = 0; From 2752e04f8f24f68c7e55244fe39d6fc27677222f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1447/1530] db: add `scid` field to channels table. Normally, we'd use the delete_columns function to remove the old `short_channel_id` string field, *but* we can't do that for sqlite, as there are other tables with references to it. So add a FIXME to do it once everyone has upgraded to an sqlite3 which has native support for column deletion. Signed-off-by: Rusty Russell --- contrib/pyln-testing/pyln/testing/utils.py | 16 +++++++ tests/test_db.py | 4 +- tests/test_pay.py | 4 +- tests/utils.py | 2 +- wallet/db.c | 49 ++++++++++++++++++++++ wallet/wallet.c | 26 +++++------- 6 files changed, 81 insertions(+), 20 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index b70502b36484..f9e8c639f841 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -107,6 +107,22 @@ def write_config(filename, opts, regtest_opts=None, section_name='regtest'): f.write("{}={}\n".format(k, v)) +def scid_to_int(scid): + """Convert a 1x2x3 scid to a raw integer""" + blocknum, txnum, outnum = scid.split("x") + + # 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 (int(blocknum) << 40) | (int(txnum) << 16) | int(outnum) + + def only_one(arr): """Many JSON RPC calls return an array; often we only expect a single entry """ diff --git a/tests/test_db.py b/tests/test_db.py index 58aec294408a..de3e2ae3de7b 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -2,7 +2,7 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from pyln.client import RpcError -from utils import wait_for, sync_blockheight, COMPAT, VALGRIND, DEVELOPER, TIMEOUT, only_one +from utils import wait_for, sync_blockheight, COMPAT, VALGRIND, DEVELOPER, TIMEOUT, only_one, scid_to_int import base64 import os @@ -161,7 +161,7 @@ def test_scid_upgrade(node_factory, bitcoind): l1.daemon.opts['database-upgrade'] = True l1.daemon.start() - assert l1.db_query('SELECT short_channel_id from channels;') == [{'short_channel_id': '103x1x1'}] + assert l1.db_query('SELECT scid FROM channels;') == [{'scid': scid_to_int('103x1x1')}] assert l1.db_query('SELECT failchannel from payments;') == [{'failchannel': '103x1x1'}] diff --git a/tests/test_pay.py b/tests/test_pay.py index 382957eb5f85..dba8e6eb989d 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3,7 +3,7 @@ from io import BytesIO from pyln.client import RpcError, Millisatoshi from pyln.proto.onion import TlvPayload -from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT +from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT, scid_to_int from utils import ( DEVELOPER, wait_for, only_one, sync_blockheight, TIMEOUT, EXPERIMENTAL_FEATURES, VALGRIND, mine_funding_to_announce, first_scid @@ -1957,7 +1957,7 @@ def test_setchannel_usage(node_factory, bitcoind): def channel_get_config(scid): return l1.db.query( 'SELECT feerate_base, feerate_ppm, htlc_minimum_msat, htlc_maximum_msat FROM channels ' - 'WHERE short_channel_id=\'{}\';'.format(scid)) + 'WHERE scid={};'.format(scid_to_int(scid))) # get short channel id scid = l1.get_channel_scid(l2) diff --git a/tests/utils.py b/tests/utils.py index 5d3cfe515698..c1380d02c14c 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,5 +1,5 @@ from pyln.testing.utils import TEST_NETWORK, TIMEOUT, VALGRIND, DEVELOPER, DEPRECATED_APIS # noqa: F401 -from pyln.testing.utils import env, only_one, wait_for, write_config, TailableProc, sync_blockheight, wait_channel_quiescent, get_tx_p2wsh_outnum, mine_funding_to_announce # noqa: F401 +from pyln.testing.utils import env, only_one, wait_for, write_config, TailableProc, sync_blockheight, wait_channel_quiescent, get_tx_p2wsh_outnum, mine_funding_to_announce, scid_to_int # noqa: F401 import bitstring from pyln.client import Millisatoshi from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND diff --git a/wallet/db.c b/wallet/db.c index ee9e940258a4..0e206b255909 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -59,6 +59,10 @@ static void fillin_missing_channel_blockheights(struct lightningd *ld, struct db *db, const struct migration_context *mc); +static void migrate_channels_scids_as_integers(struct lightningd *ld, + struct db *db, + const struct migration_context *mc); + /* Do not reorder or remove elements from this array, it is used to * migrate existing databases from a previous state, based on the * string indices */ @@ -920,6 +924,8 @@ static struct migration dbmigrations[] = { {SQL("DROP INDEX forwarded_payments_state;"), NULL}, {SQL("DROP INDEX forwarded_payments_out_htlc_id;"), NULL}, {SQL("DROP TABLE forwarded_payments;"), NULL}, + /* Adds scid column, then moves short_channel_id across to it */ + {SQL("ALTER TABLE channels ADD scid BIGINT;"), migrate_channels_scids_as_integers}, }; /* Released versions are of form v{num}[.{num}]* */ @@ -1455,3 +1461,46 @@ void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db, tal_free(stmt); } + +/* We used to store scids as strings... */ +static void migrate_channels_scids_as_integers(struct lightningd *ld, + struct db *db, + const struct migration_context *mc) +{ + struct db_stmt *stmt; + char **scids = tal_arr(tmpctx, char *, 0); + + stmt = db_prepare_v2(db, SQL("SELECT short_channel_id FROM channels")); + db_query_prepared(stmt); + while (db_step(stmt)) { + if (db_col_is_null(stmt, "short_channel_id")) + continue; + tal_arr_expand(&scids, + db_col_strdup(scids, stmt, "short_channel_id")); + } + tal_free(stmt); + + for (size_t i = 0; i < tal_count(scids); i++) { + struct short_channel_id scid; + if (!short_channel_id_from_str(scids[i], strlen(scids[i]), &scid)) + db_fatal("Cannot convert invalid channels.short_channel_id '%s'", + scids[i]); + + stmt = db_prepare_v2(db, SQL("UPDATE channels" + " SET scid = ?" + " WHERE short_channel_id = ?")); + db_bind_scid(stmt, 0, &scid); + db_bind_text(stmt, 1, scids[i]); + db_exec_prepared_v2(stmt); + if (db_count_changes(stmt) != 1) + db_fatal("Converting channels.short_channel_id '%s' gave %zu changes != 1?", + scids[i], db_count_changes(stmt)); + tal_free(stmt); + } + + /* FIXME: We cannot use ->delete_columns to remove + * short_channel_id, as other tables reference the channels + * (and sqlite3 has them referencing a now-deleted table!). + * When we can assume sqlite3 2021-04-19 (3.35.5), we can + * simply use DROP COLUMN (yay!) */ +} diff --git a/wallet/wallet.c b/wallet/wallet.c index 6d6269678896..b6bbd5af60bb 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1293,10 +1293,9 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm } } - if (!db_col_is_null(stmt, "short_channel_id")) { + if (!db_col_is_null(stmt, "scid")) { scid = tal(tmpctx, struct short_channel_id); - if (!db_col_short_channel_id_str(stmt, "short_channel_id", scid)) - return NULL; + db_col_scid(stmt, "scid", scid); } else { scid = NULL; } @@ -1552,7 +1551,7 @@ static bool wallet_channels_load_active(struct wallet *w) stmt = db_prepare_v2(w->db, SQL("SELECT" " id" ", peer_id" - ", short_channel_id" + ", scid" ", full_channel_id" ", channel_config_local" ", channel_config_remote" @@ -1856,7 +1855,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) stmt = db_prepare_v2(w->db, SQL("UPDATE channels SET" " shachain_remote_id=?," // 0 - " short_channel_id=?," // 1 + " scid=?," // 1 " full_channel_id=?," // 2 " state=?," // 3 " funder=?," // 4 @@ -1903,7 +1902,7 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " WHERE id=?")); // 46 db_bind_u64(stmt, 0, chan->their_shachain.id); if (chan->scid) - db_bind_short_channel_id_str(stmt, 1, chan->scid); + db_bind_scid(stmt, 1, chan->scid); else db_bind_null(stmt, 1); @@ -4678,11 +4677,11 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t ", t.blockheight" ", t.txindex" ", t.type as txtype" - ", c2.short_channel_id as txchan" + ", c2.scid as txchan" ", a.location" ", a.idx as ann_idx" ", a.type as annotation_type" - ", c.short_channel_id" + ", c.scid" " FROM" " transactions t LEFT JOIN" " transaction_annotations a ON (a.txid = t.id) LEFT JOIN" @@ -4725,8 +4724,7 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t else cur->annotation.type = 0; if (!db_col_is_null(stmt, "txchan")) - db_col_short_channel_id_str(stmt, "txchan", - &cur->annotation.channel); + db_col_scid(stmt, "txchan", &cur->annotation.channel); else cur->annotation.channel.u64 = 0; @@ -4756,16 +4754,14 @@ struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t /* cppcheck-suppress uninitvar - false positive on fatal() above */ ann->type = db_col_int(stmt, "annotation_type"); - if (!db_col_is_null(stmt, "c.short_channel_id")) - db_col_short_channel_id_str(stmt, - "c.short_channel_id", - &ann->channel); + if (!db_col_is_null(stmt, "c.scid")) + db_col_scid(stmt, "c.scid", &ann->channel); else ann->channel.u64 = 0; } else { db_col_ignore(stmt, "ann_idx"); db_col_ignore(stmt, "annotation_type"); - db_col_ignore(stmt, "c.short_channel_id"); + db_col_ignore(stmt, "c.scid"); } } tal_free(stmt); From d7c1325e38dfa15ccb2021430d118ee6a14dd1ee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1448/1530] wallet: use scid not string for failchannel (now failscid) in payments table. And remove the now-unused string-based helper functions. Signed-off-by: Rusty Russell --- db/bindings.c | 18 ----------------- db/bindings.h | 6 ------ tests/test_db.py | 2 +- wallet/db.c | 42 ++++++++++++++++++++++++++++++++++++++++ wallet/test/run-wallet.c | 2 +- wallet/wallet.c | 12 +++++------- 6 files changed, 49 insertions(+), 33 deletions(-) diff --git a/db/bindings.c b/db/bindings.c index 2f3546a2bd6c..fc95ca52f031 100644 --- a/db/bindings.c +++ b/db/bindings.c @@ -159,13 +159,6 @@ void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *pk) db_bind_blob(stmt, pos, der, PUBKEY_CMPR_LEN); } -void db_bind_short_channel_id_str(struct db_stmt *stmt, int col, - const struct short_channel_id *id) -{ - char *ser = short_channel_id_to_str(stmt, id); - db_bind_text(stmt, col, ser); -} - void db_bind_scid(struct db_stmt *stmt, int col, const struct short_channel_id *id) { @@ -368,17 +361,6 @@ void db_col_pubkey(struct db_stmt *stmt, assert(ok); } -/* Yes, we put this in as a string. Past mistakes; do not use! */ -bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, - struct short_channel_id *dest) -{ - size_t col = db_query_colnum(stmt, colname); - const char *source = db_column_blob(stmt, col); - size_t sourcelen = db_column_bytes(stmt, col); - db_column_null_warn(stmt, colname, col); - return short_channel_id_from_str(source, sourcelen, dest); -} - void db_col_scid(struct db_stmt *stmt, const char *colname, struct short_channel_id *dest) { diff --git a/db/bindings.h b/db/bindings.h index 74febcf83f01..cc7707cf5c4f 100644 --- a/db/bindings.h +++ b/db/bindings.h @@ -37,9 +37,6 @@ void db_bind_node_id(struct db_stmt *stmt, int pos, const struct node_id *ni); void db_bind_node_id_arr(struct db_stmt *stmt, int col, const struct node_id *ids); void db_bind_pubkey(struct db_stmt *stmt, int pos, const struct pubkey *p); -/* DO NOT USE, deprecated! */ -void db_bind_short_channel_id_str(struct db_stmt *stmt, int col, - const struct short_channel_id *id); void db_bind_scid(struct db_stmt *stmt, int col, const struct short_channel_id *id); void db_bind_short_channel_id_arr(struct db_stmt *stmt, int col, @@ -86,9 +83,6 @@ struct node_id *db_col_node_id_arr(const tal_t *ctx, struct db_stmt *stmt, const char *colname); void db_col_pubkey(struct db_stmt *stmt, const char *colname, struct pubkey *p); -/* DO NOT USE: deprecated */ -bool db_col_short_channel_id_str(struct db_stmt *stmt, const char *colname, - struct short_channel_id *dest); void db_col_scid(struct db_stmt *stmt, const char *colname, struct short_channel_id *dest); struct short_channel_id * diff --git a/tests/test_db.py b/tests/test_db.py index de3e2ae3de7b..677a5d290dfb 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -162,7 +162,7 @@ def test_scid_upgrade(node_factory, bitcoind): l1.daemon.opts['database-upgrade'] = True l1.daemon.start() assert l1.db_query('SELECT scid FROM channels;') == [{'scid': scid_to_int('103x1x1')}] - assert l1.db_query('SELECT failchannel from payments;') == [{'failchannel': '103x1x1'}] + assert l1.db_query('SELECT failscid FROM payments;') == [{'failscid': scid_to_int('103x1x1')}] @unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete db") diff --git a/wallet/db.c b/wallet/db.c index 0e206b255909..a634bfea235d 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -63,6 +63,10 @@ static void migrate_channels_scids_as_integers(struct lightningd *ld, struct db *db, const struct migration_context *mc); +static void migrate_payments_scids_as_integers(struct lightningd *ld, + struct db *db, + const struct migration_context *mc); + /* Do not reorder or remove elements from this array, it is used to * migrate existing databases from a previous state, based on the * string indices */ @@ -926,6 +930,7 @@ static struct migration dbmigrations[] = { {SQL("DROP TABLE forwarded_payments;"), NULL}, /* Adds scid column, then moves short_channel_id across to it */ {SQL("ALTER TABLE channels ADD scid BIGINT;"), migrate_channels_scids_as_integers}, + {SQL("ALTER TABLE payments ADD failscid BIGINT;"), migrate_payments_scids_as_integers}, }; /* Released versions are of form v{num}[.{num}]* */ @@ -1504,3 +1509,40 @@ static void migrate_channels_scids_as_integers(struct lightningd *ld, * When we can assume sqlite3 2021-04-19 (3.35.5), we can * simply use DROP COLUMN (yay!) */ } + +static void migrate_payments_scids_as_integers(struct lightningd *ld, + struct db *db, + const struct migration_context *mc) +{ + struct db_stmt *stmt; + const char *colnames[] = {"failchannel"}; + + stmt = db_prepare_v2(db, SQL("SELECT id, failchannel FROM payments")); + db_query_prepared(stmt); + while (db_step(stmt)) { + struct db_stmt *update_stmt; + struct short_channel_id scid; + const char *str; + + if (db_col_is_null(stmt, "failchannel")) { + db_col_ignore(stmt, "id"); + continue; + } + + str = db_col_strdup(tmpctx, stmt, "failchannel"); + if (!short_channel_id_from_str(str, strlen(str), &scid)) + db_fatal("Cannot convert invalid payments.failchannel '%s'", + str); + update_stmt = db_prepare_v2(db, SQL("UPDATE payments SET" + " failscid = ?" + " WHERE id = ?")); + db_bind_scid(update_stmt, 0, &scid); + db_bind_u64(update_stmt, 1, db_col_u64(stmt, "id")); + db_exec_prepared_v2(update_stmt); + tal_free(update_stmt); + } + tal_free(stmt); + + if (!db->config->delete_columns(db, "payments", colnames, ARRAY_SIZE(colnames))) + db_fatal("Could not delete payments.failchannel"); +} diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 090e371bad22..5c2a71ed49b2 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -977,9 +977,9 @@ static struct wallet *create_test_wallet(struct lightningd *ld, const tal_t *ctx w->bip32_base) == WALLY_OK); CHECK_MSG(w->db, "Failed opening the db"); + w->db->data_version = 0; db_begin_transaction(w->db); db_migrate(ld, w->db, bip32_base); - w->db->data_version = 0; db_commit_transaction(w->db); CHECK_MSG(!wallet_err, wallet_err); w->max_channel_dbid = 0; diff --git a/wallet/wallet.c b/wallet/wallet.c index b6bbd5af60bb..bca98eb4dd4b 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3403,7 +3403,7 @@ void wallet_payment_get_failinfo(const tal_t *ctx, stmt = db_prepare_v2(wallet->db, SQL("SELECT failonionreply, faildestperm" ", failindex, failcode" - ", failnode, failchannel" + ", failnode, failscid" ", failupdate, faildetail, faildirection" " FROM payments" " WHERE payment_hash=? AND partid=? AND groupid=?;")); @@ -3428,14 +3428,12 @@ void wallet_payment_get_failinfo(const tal_t *ctx, *failnode = tal(ctx, struct node_id); db_col_node_id(stmt, "failnode", *failnode); } - if (db_col_is_null(stmt, "failchannel")) { + if (db_col_is_null(stmt, "failscid")) { db_col_ignore(stmt, "faildirection"); *failchannel = NULL; } else { *failchannel = tal(ctx, struct short_channel_id); - resb = db_col_short_channel_id_str(stmt, "failchannel", - *failchannel); - assert(resb); + db_col_scid(stmt, "failscid", *failchannel); /* For pre-0.6.2 dbs, direction will be 0 */ *faildirection = db_col_int(stmt, "faildirection"); @@ -3474,7 +3472,7 @@ void wallet_payment_set_failinfo(struct wallet *wallet, " , failindex=?" " , failcode=?" " , failnode=?" - " , failchannel=?" + " , failscid=?" " , failupdate=?" " , faildetail=?" " , faildirection=?" @@ -3494,7 +3492,7 @@ void wallet_payment_set_failinfo(struct wallet *wallet, db_bind_null(stmt, 4); if (failchannel) { - db_bind_short_channel_id_str(stmt, 5, failchannel); + db_bind_scid(stmt, 5, failchannel); db_bind_int(stmt, 8, faildirection); } else { db_bind_null(stmt, 5); From 311807ff1f4f3574413b2ef86f63bf28e5363ee2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1449/1530] lightningd: add in_htlc_id / out_htlc_id to listforwards. And document that we never know payment_hash. Changelog-Added: JSON-RPC: `listforwards` now shows `in_htlc_id` and `out_htlc_id` Changelog-Changed: JSON-RPC: `listforwards` now never shows `payment_hash`; use `listhtlcs`. --- .msggen.json | 2 + cln-grpc/proto/node.proto | 3 +- cln-grpc/src/convert.rs | 3 +- cln-rpc/src/model.rs | 6 +- contrib/pyln-testing/pyln/testing/grpc2py.py | 3 +- contrib/pyln-testing/pyln/testing/node_pb2.py | 60 +++++++++---------- doc/lightning-listforwards.7.md | 5 +- doc/schemas/listforwards.schema.json | 31 ++++++---- lightningd/notification.c | 6 +- lightningd/peer_htlcs.c | 21 ++++--- lightningd/peer_htlcs.h | 6 +- tests/test_misc.py | 15 +++++ tests/test_plugin.py | 6 ++ wallet/wallet.c | 10 +++- wallet/wallet.h | 4 +- 15 files changed, 118 insertions(+), 63 deletions(-) diff --git a/.msggen.json b/.msggen.json index d4ba473844cd..0b77b209d9ae 100644 --- a/.msggen.json +++ b/.msggen.json @@ -574,8 +574,10 @@ "ListforwardsForwards": { "ListForwards.forwards[].fee_msat": 7, "ListForwards.forwards[].in_channel": 1, + "ListForwards.forwards[].in_htlc_id": 10, "ListForwards.forwards[].in_msat": 2, "ListForwards.forwards[].out_channel": 5, + "ListForwards.forwards[].out_htlc_id": 11, "ListForwards.forwards[].out_msat": 8, "ListForwards.forwards[].payment_hash": 6, "ListForwards.forwards[].received_time": 4, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 5be3b60e764d..0dc01a80c157 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1229,11 +1229,12 @@ message ListforwardsForwards { TLV = 1; } string in_channel = 1; + uint64 in_htlc_id = 10; Amount in_msat = 2; ListforwardsForwardsStatus status = 3; double received_time = 4; optional string out_channel = 5; - optional bytes payment_hash = 6; + optional uint64 out_htlc_id = 11; optional ListforwardsForwardsStyle style = 9; optional Amount fee_msat = 7; optional Amount out_msat = 8; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index a17b6bae77c7..2c28e896c542 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -897,11 +897,12 @@ impl From<&responses::ListforwardsForwards> for pb::ListforwardsForwards { fn from(c: &responses::ListforwardsForwards) -> Self { Self { in_channel: c.in_channel.to_string(), // Rule #2 for type short_channel_id + in_htlc_id: c.in_htlc_id.clone(), // Rule #2 for type u64 in_msat: Some(c.in_msat.into()), // Rule #2 for type msat status: c.status as i32, received_time: c.received_time.clone(), // Rule #2 for type number out_channel: c.out_channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? - payment_hash: c.payment_hash.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + out_htlc_id: c.out_htlc_id.clone(), // Rule #2 for type u64? style: c.style.map(|v| v as i32), fee_msat: c.fee_msat.map(|f| f.into()), // Rule #2 for type msat? out_msat: c.out_msat.map(|f| f.into()), // Rule #2 for type msat? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 9d4705a86b78..38d1cdbf65fd 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -2673,6 +2673,8 @@ pub mod responses { pub struct ListforwardsForwards { #[serde(alias = "in_channel")] pub in_channel: ShortChannelId, + #[serde(alias = "in_htlc_id")] + pub in_htlc_id: u64, #[serde(alias = "in_msat")] pub in_msat: Amount, // Path `ListForwards.forwards[].status` @@ -2682,8 +2684,8 @@ pub mod responses { pub received_time: f64, #[serde(alias = "out_channel", skip_serializing_if = "Option::is_none")] pub out_channel: Option, - #[serde(alias = "payment_hash", skip_serializing_if = "Option::is_none")] - pub payment_hash: Option, + #[serde(alias = "out_htlc_id", skip_serializing_if = "Option::is_none")] + pub out_htlc_id: Option, #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, #[serde(alias = "fee_msat", skip_serializing_if = "Option::is_none")] diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index 551b341cca75..162f47ba1bee 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -802,11 +802,12 @@ def getroute2py(m): def listforwards_forwards2py(m): return remove_default({ "in_channel": m.in_channel, # PrimitiveField in generate_composite + "in_htlc_id": m.in_htlc_id, # PrimitiveField in generate_composite "in_msat": amount2msat(m.in_msat), # PrimitiveField in generate_composite "status": str(m.status), # EnumField in generate_composite "received_time": m.received_time, # PrimitiveField in generate_composite "out_channel": m.out_channel, # PrimitiveField in generate_composite - "payment_hash": hexlify(m.payment_hash), # PrimitiveField in generate_composite + "out_htlc_id": m.out_htlc_id, # PrimitiveField in generate_composite "style": str(m.style), # EnumField in generate_composite "fee_msat": amount2msat(m.fee_msat), # PrimitiveField in generate_composite "out_msat": amount2msat(m.out_msat), # PrimitiveField in generate_composite diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 0c48cfe76a67..93eec9bd1566 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xb8\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0f\n\r_payment_hashB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xca\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x12\n\nin_htlc_id\x18\n \x01(\x04\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1368,33 +1368,33 @@ _LISTFORWARDSRESPONSE._serialized_start=26303 _LISTFORWARDSRESPONSE._serialized_end=26370 _LISTFORWARDSFORWARDS._serialized_start=26373 - _LISTFORWARDSFORWARDS._serialized_end=26941 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26738 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26822 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26824 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26872 - _LISTPAYSREQUEST._serialized_start=26944 - _LISTPAYSREQUEST._serialized_end=27163 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27069 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27124 - _LISTPAYSRESPONSE._serialized_start=27165 - _LISTPAYSRESPONSE._serialized_end=27216 - _LISTPAYSPAYS._serialized_start=27219 - _LISTPAYSPAYS._serialized_end=27738 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27550 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27609 - _PINGREQUEST._serialized_start=27740 - _PINGREQUEST._serialized_end=27829 - _PINGRESPONSE._serialized_start=27831 - _PINGRESPONSE._serialized_end=27861 - _SIGNMESSAGEREQUEST._serialized_start=27863 - _SIGNMESSAGEREQUEST._serialized_end=27900 - _SIGNMESSAGERESPONSE._serialized_start=27902 - _SIGNMESSAGERESPONSE._serialized_end=27972 - _STOPREQUEST._serialized_start=27974 - _STOPREQUEST._serialized_end=27987 - _STOPRESPONSE._serialized_start=27989 - _STOPRESPONSE._serialized_end=28003 - _NODE._serialized_start=28006 - _NODE._serialized_end=30934 + _LISTFORWARDSFORWARDS._serialized_end=26959 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26757 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26841 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26843 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26891 + _LISTPAYSREQUEST._serialized_start=26962 + _LISTPAYSREQUEST._serialized_end=27181 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27087 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27142 + _LISTPAYSRESPONSE._serialized_start=27183 + _LISTPAYSRESPONSE._serialized_end=27234 + _LISTPAYSPAYS._serialized_start=27237 + _LISTPAYSPAYS._serialized_end=27756 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27568 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27627 + _PINGREQUEST._serialized_start=27758 + _PINGREQUEST._serialized_end=27847 + _PINGRESPONSE._serialized_start=27849 + _PINGRESPONSE._serialized_end=27879 + _SIGNMESSAGEREQUEST._serialized_start=27881 + _SIGNMESSAGEREQUEST._serialized_end=27918 + _SIGNMESSAGERESPONSE._serialized_start=27920 + _SIGNMESSAGERESPONSE._serialized_end=27990 + _STOPREQUEST._serialized_start=27992 + _STOPREQUEST._serialized_end=28005 + _STOPRESPONSE._serialized_start=28007 + _STOPRESPONSE._serialized_end=28021 + _NODE._serialized_start=28024 + _NODE._serialized_end=30952 # @@protoc_insertion_point(module_scope) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 150ba7a238cc..08ab60bfdef7 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -25,11 +25,12 @@ RETURN VALUE On success, an object containing **forwards** is returned. It is an array of objects, where each object contains: - **in\_channel** (short\_channel\_id): the channel that received the HTLC +- **in\_htlc\_id** (u64): the unique HTLC id the sender gave this - **in\_msat** (msat): the value of the incoming HTLC - **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed") - **received\_time** (number): the UNIX timestamp when this was received - **out\_channel** (short\_channel\_id, optional): the channel that the HTLC (trying to) forward to -- **payment\_hash** (hex, optional): payment hash sought by HTLC (always 64 characters) +- **out\_htlc\_id** (u64, optional): the unique HTLC id we gave this when sending - **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") If **out\_msat** is present: @@ -63,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:39c71b957590f6a9b321120e7f337216833efd94f0144560da5cd55c91fee35c) +[comment]: # ( SHA256STAMP:5b2da52b7f3a28563d0103d3853b9d8f717dc41a9e9c6b395ff19f1b975ca5fd) diff --git a/doc/schemas/listforwards.schema.json b/doc/schemas/listforwards.schema.json index 7702a9f7e08d..f133c2dabbb8 100644 --- a/doc/schemas/listforwards.schema.json +++ b/doc/schemas/listforwards.schema.json @@ -14,6 +14,7 @@ "required": [ "in_channel", "in_msat", + "in_htlc_id", "status", "received_time" ], @@ -22,6 +23,10 @@ "type": "short_channel_id", "description": "the channel that received the HTLC" }, + "in_htlc_id": { + "type": "u64", + "description": "the unique HTLC id the sender gave this" + }, "in_msatoshi": { "deprecated": true }, @@ -47,11 +52,9 @@ "type": "short_channel_id", "description": "the channel that the HTLC (trying to) forward to" }, - "payment_hash": { - "type": "hex", - "description": "payment hash sought by HTLC", - "maxLength": 64, - "minLength": 64 + "out_htlc_id": { + "type": "u64", + "description": "the unique HTLC id we gave this when sending" }, "style": { "type": "string", @@ -74,10 +77,12 @@ "required": [ "fee_msat", "out_msat", + "out_htlc_id", "out_channel" ], "properties": { "in_channel": {}, + "in_htlc_id": {}, "in_msatoshi": {}, "in_msat": {}, "status": {}, @@ -85,7 +90,7 @@ "received_time": {}, "resolved_time": {}, "out_channel": {}, - "payment_hash": {}, + "out_htlc_id": {}, "failcode": {}, "failreason": {}, "fee": { @@ -109,13 +114,13 @@ "required": [], "properties": { "in_channel": {}, + "in_htlc_id": {}, "in_msatoshi": {}, "in_msat": {}, "status": {}, "style": {}, "received_time": {}, "resolved_time": {}, - "payment_hash": {}, "failcode": {}, "failreason": {}, "out_channel": {} @@ -141,13 +146,14 @@ ], "properties": { "in_channel": {}, + "in_htlc_id": {}, "in_msatoshi": {}, "in_msat": {}, "status": {}, "style": {}, "received_time": {}, "out_channel": {}, - "payment_hash": {}, + "out_htlc_id": {}, "fee": {}, "fee_msat": {}, "out_msatoshi": {}, @@ -164,13 +170,14 @@ "additionalProperties": false, "properties": { "in_channel": {}, + "in_htlc_id": {}, "in_msatoshi": {}, "in_msat": {}, "status": {}, "style": {}, "received_time": {}, "out_channel": {}, - "payment_hash": {}, + "out_htlc_id": {}, "fee": {}, "fee_msat": {}, "failcode": {}, @@ -197,13 +204,14 @@ "required": [], "properties": { "in_channel": {}, + "in_htlc_id": {}, "in_msatoshi": {}, "in_msat": {}, "status": {}, "style": {}, "received_time": {}, "out_channel": {}, - "payment_hash": {}, + "out_htlc_id": {}, "fee": {}, "fee_msat": {}, "out_msatoshi": {}, @@ -224,13 +232,14 @@ "required": [], "properties": { "in_channel": {}, + "in_htlc_id": {}, "in_msatoshi": {}, "in_msat": {}, "status": {}, "style": {}, "received_time": {}, "out_channel": {}, - "payment_hash": {}, + "out_htlc_id": {}, "fee": {}, "fee_msat": {}, "out_msatoshi": {}, diff --git a/lightningd/notification.c b/lightningd/notification.c index a021a116cfde..2f01bf40da46 100644 --- a/lightningd/notification.c +++ b/lightningd/notification.c @@ -328,14 +328,16 @@ static void forward_event_notification_serialize(struct json_stream *stream, cur->msat_out = AMOUNT_MSAT(0); cur->fee = AMOUNT_MSAT(0); } - cur->payment_hash = tal_dup(cur, struct sha256, &in->payment_hash); + cur->htlc_id_out = NULL; cur->status = state; cur->failcode = failcode; cur->received_time = in->received_time; cur->resolved_time = tal_steal(cur, resolved_time); cur->forward_style = forward_style; + cur->htlc_id_in = in->key.id; - json_format_forwarding_object(stream, "forward_event", cur); + json_add_forwarding_object(stream, "forward_event", + cur, &in->payment_hash); } REGISTER_NOTIFICATION(forward_event, diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index f2529fb66302..2dbb182659f9 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2765,21 +2765,26 @@ AUTODATA(json_command, &dev_ignore_htlcs); /* Warp this process to ensure the consistent json object structure * between 'listforwards' API and 'forward_event' notification. */ -void json_format_forwarding_object(struct json_stream *response, - const char *fieldname, - const struct forwarding *cur) +void json_add_forwarding_object(struct json_stream *response, + const char *fieldname, + const struct forwarding *cur, + const struct sha256 *payment_hash) { json_object_start(response, fieldname); - /* See 6d333f16cc0f3aac7097269bf0985b5fa06d59b4: we may have deleted HTLC. */ - if (cur->payment_hash) - json_add_sha256(response, "payment_hash", cur->payment_hash); + /* Only for forward_event */ + if (payment_hash) + json_add_sha256(response, "payment_hash", payment_hash); json_add_short_channel_id(response, "in_channel", &cur->channel_in); + json_add_u64(response, "in_htlc_id", cur->htlc_id_in); /* This can be unknown if we failed before channel lookup */ - if (cur->channel_out.u64 != 0) + if (cur->channel_out.u64 != 0) { json_add_short_channel_id(response, "out_channel", &cur->channel_out); + if (cur->htlc_id_out) + json_add_u64(response, "out_htlc_id", *cur->htlc_id_out); + } json_add_amount_msat_compat(response, cur->msat_in, "in_msatoshi", "in_msat"); @@ -2835,7 +2840,7 @@ static void listforwardings_add_forwardings(struct json_stream *response, json_array_start(response, "forwards"); for (size_t i=0; ifee = AMOUNT_MSAT(0); } - /* FIXME: This now requires complex join to determine! */ - cur->payment_hash = NULL; db_col_scid(stmt, "in_channel_scid", &cur->channel_in); + cur->htlc_id_in = db_col_u64(stmt, "in_htlc_id"); if (!db_col_is_null(stmt, "out_channel_scid")) { db_col_scid(stmt, "out_channel_scid", &cur->channel_out); @@ -4628,6 +4629,11 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, assert(cur->status == FORWARD_LOCAL_FAILED); cur->channel_out.u64 = 0; } + if (!db_col_is_null(stmt, "out_htlc_id")) { + cur->htlc_id_out = tal(results, u64); + *cur->htlc_id_out = db_col_u64(stmt, "out_htlc_id"); + } else + cur->htlc_id_out = NULL; cur->received_time = db_col_timeabs(stmt, "received_time"); diff --git a/wallet/wallet.h b/wallet/wallet.h index dfc66e14b0b7..94ec5c0825f9 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -272,9 +272,11 @@ static inline enum htlc_state htlc_state_in_db(enum htlc_state s) } struct forwarding { + /* channel_out is all-zero if unknown. */ struct short_channel_id channel_in, channel_out; + /* htlc_id_out is NULL if unknown. */ + u64 htlc_id_in, *htlc_id_out; struct amount_msat msat_in, msat_out, fee; - struct sha256 *payment_hash; enum forward_style forward_style; enum forward_status status; enum onion_wire failcode; From 7420a7021f4805d8b8058e86eec791f4ce3e27fa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:19:53 +0930 Subject: [PATCH 1450/1530] lightningd: add `listhtlcs` to list all the HTLCs we know about. Using `listfowards` for this wrong; expose this directly if people care (and unlike listforwards, which could be deleted, we have to remember these while the channel is still open!). Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `listhtlcs` new command to list all known HTLCS. --- doc/Makefile | 1 + doc/index.rst | 1 + doc/lightning-listhtlcs.7.md | 49 ++++++++++++++++ doc/schemas/listhtlcs.request.json | 11 ++++ doc/schemas/listhtlcs.schema.json | 84 +++++++++++++++++++++++++++ lightningd/peer_htlcs.c | 80 +++++++++++++++++++++++++ tests/test_connection.py | 67 +++++++++++++++++---- tests/test_misc.py | 56 ++++++++++++++++-- wallet/wallet.c | 93 ++++++++++++++++++++++++++++++ wallet/wallet.h | 36 ++++++++++++ 10 files changed, 461 insertions(+), 17 deletions(-) create mode 100644 doc/lightning-listhtlcs.7.md create mode 100644 doc/schemas/listhtlcs.request.json create mode 100644 doc/schemas/listhtlcs.schema.json diff --git a/doc/Makefile b/doc/Makefile index a0665fd19ad8..79a0b8622665 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -49,6 +49,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-listdatastore.7 \ doc/lightning-listforwards.7 \ doc/lightning-listfunds.7 \ + doc/lightning-listhtlcs.7 \ doc/lightning-listinvoices.7 \ doc/lightning-listoffers.7 \ doc/lightning-listpays.7 \ diff --git a/doc/index.rst b/doc/index.rst index 593bc8624650..c7bd4bacc6f5 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -76,6 +76,7 @@ Core Lightning Documentation lightning-listdatastore lightning-listforwards lightning-listfunds + lightning-listhtlcs lightning-listinvoices lightning-listnodes lightning-listoffers diff --git a/doc/lightning-listhtlcs.7.md b/doc/lightning-listhtlcs.7.md new file mode 100644 index 000000000000..6c8f265c563e --- /dev/null +++ b/doc/lightning-listhtlcs.7.md @@ -0,0 +1,49 @@ +lightning-listhtlcs -- Command for querying HTLCs +================================================= + +SYNOPSIS +-------- + +**listhtlcs** [*id*] + +DESCRIPTION +----------- + +The **listhtlcs** RPC command gets all HTLCs (which, generally, we +remember for as long as a channel is open, even if they've completed +long ago). If given a short channel id (e.g. 1x2x3) or full 64-byte +hex channel id, it will only list htlcs for that channel (which +must be known). + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **htlcs** is returned. It is an array of objects, where each object contains: + +- **short\_channel\_id** (short\_channel\_id): the channel that contains/contained the HTLC +- **id** (u64): the unique, incrementing HTLC id the creator gave this +- **expiry** (u32): the block number where this HTLC expires/expired +- **amount\_msat** (msat): the value of the HTLC +- **direction** (string): out if we offered this to the peer, in if they offered it (one of "out", "in") +- **payment\_hash** (hex): payment hash sought by HTLC (always 64 characters) +- **state** (string): The first 10 states are for `in`, the next 10 are for `out`. (one of "SENT_ADD_HTLC", "SENT_ADD_COMMIT", "RCVD_ADD_REVOCATION", "RCVD_ADD_ACK_COMMIT", "SENT_ADD_ACK_REVOCATION", "RCVD_REMOVE_HTLC", "RCVD_REMOVE_COMMIT", "SENT_REMOVE_REVOCATION", "SENT_REMOVE_ACK_COMMIT", "RCVD_REMOVE_ACK_REVOCATION", "RCVD_ADD_HTLC", "RCVD_ADD_COMMIT", "SENT_ADD_REVOCATION", "SENT_ADD_ACK_COMMIT", "RCVD_ADD_ACK_REVOCATION", "SENT_REMOVE_HTLC", "SENT_REMOVE_COMMIT", "RCVD_REMOVE_REVOCATION", "RCVD_REMOVE_ACK_COMMIT", "SENT_REMOVE_ACK_REVOCATION") + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +Rusty Russell <> is mainly responsible. + +SEE ALSO +-------- + +lightning-listforwards(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:6ef16f6e1f54522435130d99f224ca41a38fb3c5bc26886ccdaddc69f1abb946) diff --git a/doc/schemas/listhtlcs.request.json b/doc/schemas/listhtlcs.request.json new file mode 100644 index 000000000000..df03123299a8 --- /dev/null +++ b/doc/schemas/listhtlcs.request.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "properties": { + "id": { + "type": "string", + "description": "channel id or short_channel_id" + } + } +} diff --git a/doc/schemas/listhtlcs.schema.json b/doc/schemas/listhtlcs.schema.json new file mode 100644 index 000000000000..469eb1588bbe --- /dev/null +++ b/doc/schemas/listhtlcs.schema.json @@ -0,0 +1,84 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "htlcs" + ], + "properties": { + "htlcs": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "required": [ + "short_channel_id", + "id", + "expiry", + "direction", + "amount_msat", + "payment_hash", + "state" + ], + "properties": { + "short_channel_id": { + "type": "short_channel_id", + "description": "the channel that contains/contained the HTLC" + }, + "id": { + "type": "u64", + "description": "the unique, incrementing HTLC id the creator gave this" + }, + "expiry": { + "type": "u32", + "description": "the block number where this HTLC expires/expired" + }, + "amount_msat": { + "type": "msat", + "description": "the value of the HTLC" + }, + "direction": { + "type": "string", + "enum": [ + "out", + "in" + ], + "description": "out if we offered this to the peer, in if they offered it" + }, + "payment_hash": { + "type": "hex", + "description": "payment hash sought by HTLC", + "maxLength": 64, + "minLength": 64 + }, + "state": { + "type": "string", + "enum": [ + "SENT_ADD_HTLC", + "SENT_ADD_COMMIT", + "RCVD_ADD_REVOCATION", + "RCVD_ADD_ACK_COMMIT", + "SENT_ADD_ACK_REVOCATION", + "RCVD_REMOVE_HTLC", + "RCVD_REMOVE_COMMIT", + "SENT_REMOVE_REVOCATION", + "SENT_REMOVE_ACK_COMMIT", + "RCVD_REMOVE_ACK_REVOCATION", + "RCVD_ADD_HTLC", + "RCVD_ADD_COMMIT", + "SENT_ADD_REVOCATION", + "SENT_ADD_ACK_COMMIT", + "RCVD_ADD_ACK_REVOCATION", + "SENT_REMOVE_HTLC", + "SENT_REMOVE_COMMIT", + "RCVD_REMOVE_REVOCATION", + "RCVD_REMOVE_ACK_COMMIT", + "SENT_REMOVE_ACK_REVOCATION" + ], + "description": "The first 10 states are for `in`, the next 10 are for `out`." + } + } + } + } + } +} diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 2dbb182659f9..7eadf672aaef 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2894,3 +2894,83 @@ static const struct json_command listforwards_command = { "List all forwarded payments and their information optionally filtering by [status], [in_channel] and [out_channel]" }; AUTODATA(json_command, &listforwards_command); + +static struct command_result *param_channel(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct channel **chan) +{ + struct channel_id cid; + struct short_channel_id scid; + + if (json_tok_channel_id(buffer, tok, &cid)) { + *chan = channel_by_cid(cmd->ld, &cid); + if (!*chan) + return command_fail_badparam(cmd, name, buffer, tok, + "unknown channel"); + return NULL; + } else if (json_to_short_channel_id(buffer, tok, &scid)) { + *chan = any_channel_by_scid(cmd->ld, &scid, true); + if (!*chan) + return command_fail_badparam(cmd, name, buffer, tok, + "unknown channel"); + return NULL; + } + return command_fail_badparam(cmd, name, buffer, tok, + "must be channel id or short channel id"); +} + +static struct command_result *json_listhtlcs(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + struct channel *chan; + struct wallet_htlc_iter *i; + struct short_channel_id scid; + u64 htlc_id; + int cltv_expiry; + enum side owner; + struct amount_msat msat; + struct sha256 payment_hash; + enum htlc_state hstate; + + if (!param(cmd, buffer, params, + p_opt("id", param_channel, &chan), + NULL)) + return command_param_failed(); + + response = json_stream_success(cmd); + json_array_start(response, "htlcs"); + for (i = wallet_htlcs_first(cmd, cmd->ld->wallet, chan, + &scid, &htlc_id, &cltv_expiry, &owner, &msat, + &payment_hash, &hstate); + i; + i = wallet_htlcs_next(cmd->ld->wallet, i, + &scid, &htlc_id, &cltv_expiry, &owner, &msat, + &payment_hash, &hstate)) { + json_object_start(response, NULL); + json_add_short_channel_id(response, "short_channel_id", &scid); + json_add_u64(response, "id", htlc_id); + json_add_u32(response, "expiry", cltv_expiry); + json_add_string(response, "direction", + owner == LOCAL ? "out": "in"); + json_add_amount_msat_only(response, "amount_msat", msat); + json_add_sha256(response, "payment_hash", &payment_hash); + json_add_string(response, "state", htlc_state_name(hstate)); + json_object_end(response); + } + json_array_end(response); + + return command_success(cmd, response); +} + +static const struct json_command listhtlcs_command = { + "listhtlcs", + "channels", + json_listhtlcs, + "List all known HTLCS (optionally, just for [id] (scid or channel id))" +}; +AUTODATA(json_command, &listhtlcs_command); diff --git a/tests/test_connection.py b/tests/test_connection.py index b880e23640ec..3ed032d4a81e 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -3992,12 +3992,13 @@ def test_multichan(node_factory, executor, bitcoind): # Now fund *second* channel l2->l3 (slightly larger) bitcoind.rpc.sendtoaddress(l2.rpc.newaddr()['bech32'], 0.1) bitcoind.generate_block(1) - sync_blockheight(bitcoind, [l2]) + sync_blockheight(bitcoind, [l1, l2, l3]) l2.rpc.fundchannel(l3.info['id'], '0.01001btc') assert(len(only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']) == 2) assert(len(only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['channels']) == 2) bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1, l2, l3]) # Make sure new channel is also CHANNELD_NORMAL wait_for(lambda: [c['state'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == ["CHANNELD_NORMAL", "CHANNELD_NORMAL"]) @@ -4023,9 +4024,9 @@ def test_multichan(node_factory, executor, bitcoind): 'delay': 5, 'channel': scid23a}] before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] - inv = l3.rpc.invoice(100000000, "invoice", "invoice") - l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) - l1.rpc.waitsendpay(inv['payment_hash']) + inv1 = l3.rpc.invoice(100000000, "invoice", "invoice") + l1.rpc.sendpay(route, inv1['payment_hash'], payment_secret=inv1['payment_secret']) + l1.rpc.waitsendpay(inv1['payment_hash']) # Wait until HTLCs fully settled wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []]) after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] @@ -4049,9 +4050,9 @@ def test_multichan(node_factory, executor, bitcoind): before = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] route[1]['channel'] = scid23b - inv = l3.rpc.invoice(100000000, "invoice2", "invoice2") - l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) - l1.rpc.waitsendpay(inv['payment_hash']) + inv2 = l3.rpc.invoice(100000000, "invoice2", "invoice2") + l1.rpc.sendpay(route, inv2['payment_hash'], payment_secret=inv2['payment_secret']) + l1.rpc.waitsendpay(inv2['payment_hash']) # Wait until HTLCs fully settled wait_for(lambda: [c['htlcs'] for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']] == [[], []]) after = only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'] @@ -4062,6 +4063,7 @@ def test_multichan(node_factory, executor, bitcoind): # Make sure gossip works. bitcoind.generate_block(5) + sync_blockheight(bitcoind, [l1, l2, l3]) wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 2) @@ -4084,6 +4086,7 @@ def test_multichan(node_factory, executor, bitcoind): l2.rpc.close(scid23b) bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l1, l2, l3]) # Gossip works as expected. wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 1) @@ -4091,9 +4094,9 @@ def test_multichan(node_factory, executor, bitcoind): # We can actually pay by *closed* scid (at least until it's completely forgotten) route[1]['channel'] = scid23a - inv = l3.rpc.invoice(100000000, "invoice3", "invoice3") - l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) - l1.rpc.waitsendpay(inv['payment_hash']) + inv3 = l3.rpc.invoice(100000000, "invoice3", "invoice3") + l1.rpc.sendpay(route, inv3['payment_hash'], payment_secret=inv3['payment_secret']) + l1.rpc.waitsendpay(inv3['payment_hash']) # Restart with multiple channels works. l3.restart() @@ -4103,8 +4106,48 @@ def test_multichan(node_factory, executor, bitcoind): except RpcError: wait_for(lambda: only_one(l3.rpc.listpeers(l2.info['id'])['peers'])['connected']) - inv = l3.rpc.invoice(100000000, "invoice4", "invoice4") - l1.rpc.pay(inv['bolt11']) + inv4 = l3.rpc.invoice(100000000, "invoice4", "invoice4") + l1.rpc.pay(inv4['bolt11']) + + # A good place to test listhtlcs! + wait_for(lambda: all([h['state'] == 'RCVD_REMOVE_ACK_REVOCATION' for h in l1.rpc.listhtlcs()['htlcs']])) + + l1htlcs = l1.rpc.listhtlcs()['htlcs'] + assert l1htlcs == l1.rpc.listhtlcs(scid12)['htlcs'] + assert l1htlcs == [{"short_channel_id": scid12, + "id": 0, + "expiry": 117, + "direction": "out", + "amount_msat": Millisatoshi(100001001), + "payment_hash": inv1['payment_hash'], + "state": "RCVD_REMOVE_ACK_REVOCATION"}, + {"short_channel_id": scid12, + "id": 1, + "expiry": 117, + "direction": "out", + "amount_msat": Millisatoshi(100001001), + "payment_hash": inv2['payment_hash'], + "state": "RCVD_REMOVE_ACK_REVOCATION"}, + {"short_channel_id": scid12, + "id": 2, + "expiry": 123, + "direction": "out", + "amount_msat": Millisatoshi(100001001), + "payment_hash": inv3['payment_hash'], + "state": "RCVD_REMOVE_ACK_REVOCATION"}, + {"short_channel_id": scid12, + "id": 3, + "expiry": 123, + "direction": "out", + "amount_msat": Millisatoshi(100001001), + "payment_hash": inv4['payment_hash'], + "state": "RCVD_REMOVE_ACK_REVOCATION"}] + + # Reverse direction, should match l2's view of channel. + for h in l1htlcs: + h['direction'] = 'in' + h['state'] = 'SENT_REMOVE_ACK_REVOCATION' + assert l2.rpc.listhtlcs(scid12)['htlcs'] == l1htlcs @pytest.mark.developer("dev-no-reconnect required") diff --git a/tests/test_misc.py b/tests/test_misc.py index b2c809c1171f..03f7605de15f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2,7 +2,7 @@ from decimal import Decimal from fixtures import * # noqa: F401,F403 from fixtures import LightningNode, TEST_NETWORK -from pyln.client import RpcError +from pyln.client import RpcError, Millisatoshi from threading import Event from pyln.testing.utils import ( DEVELOPER, TIMEOUT, VALGRIND, DEPRECATED_APIS, sync_blockheight, only_one, @@ -2379,15 +2379,15 @@ def test_listfunds(node_factory): assert open_txid in txids -def test_listforwards(node_factory, bitcoind): - """Test listfunds command.""" +def test_listforwards_and_listhtlcs(node_factory, bitcoind): + """Test listforwards and listhtlcs commands.""" l1, l2, l3, l4 = node_factory.get_nodes(4, opts=[{}, {}, {}, {}]) l1.rpc.connect(l2.info['id'], 'localhost', l2.port) l2.rpc.connect(l3.info['id'], 'localhost', l3.port) l2.rpc.connect(l4.info['id'], 'localhost', l4.port) - c12, _ = l1.fundchannel(l2, 10**5) + c12, c12res = l1.fundchannel(l2, 10**5) c23, _ = l2.fundchannel(l3, 10**5) c24, _ = l2.fundchannel(l4, 10**5) @@ -2406,7 +2406,7 @@ def test_listforwards(node_factory, bitcoind): failed_inv = l3.rpc.invoice(4000, 'failed', 'desc') failed_route = l1.rpc.getroute(l3.info['id'], 4000, 1)['route'] - l2.rpc.close(c23, 1) + l2.rpc.close(c23) with pytest.raises(RpcError): l1.rpc.sendpay(failed_route, failed_inv['payment_hash'], payment_secret=failed_inv['payment_secret']) @@ -2447,6 +2447,52 @@ def test_listforwards(node_factory, bitcoind): c24_forwards = l2.rpc.listforwards(out_channel=c24)['forwards'] assert len(c24_forwards) == 1 + # listhtlcs on l1 is the same with or without id specifiers + c1htlcs = l1.rpc.listhtlcs()['htlcs'] + assert l1.rpc.listhtlcs(c12)['htlcs'] == c1htlcs + assert l1.rpc.listhtlcs(c12res['channel_id'])['htlcs'] == c1htlcs + c1htlcs.sort(key=lambda h: h['id']) + assert [h['id'] for h in c1htlcs] == [0, 1, 2] + assert [h['short_channel_id'] for h in c1htlcs] == [c12] * 3 + assert [h['amount_msat'] for h in c1htlcs] == [Millisatoshi(1001), + Millisatoshi(2001), + Millisatoshi(4001)] + assert [h['direction'] for h in c1htlcs] == ['out'] * 3 + assert [h['state'] for h in c1htlcs] == ['RCVD_REMOVE_ACK_REVOCATION'] * 3 + + # These should be a mirror! + c2c1htlcs = l2.rpc.listhtlcs(c12)['htlcs'] + for h in c2c1htlcs: + assert h['state'] == 'SENT_REMOVE_ACK_REVOCATION' + assert h['direction'] == 'in' + h['state'] = 'RCVD_REMOVE_ACK_REVOCATION' + h['direction'] = 'out' + assert c2c1htlcs == c1htlcs + + # One channel at a time should result in all htlcs. + allhtlcs = l2.rpc.listhtlcs()['htlcs'] + parthtlcs = (l2.rpc.listhtlcs(c12)['htlcs'] + + l2.rpc.listhtlcs(c23)['htlcs'] + + l2.rpc.listhtlcs(c24)['htlcs']) + assert len(allhtlcs) == len(parthtlcs) + for h in allhtlcs: + assert h in parthtlcs + + # Now, close and forget. + l2.rpc.close(c24) + l2.rpc.close(c12) + + bitcoind.generate_block(100, wait_for_mempool=3) + + # Once channels are gone, htlcs are gone. + for n in (l1, l2, l3, l4): + # They might reconnect, but still will have no channels + wait_for(lambda: all(p['channels'] == [] for p in n.rpc.listpeers()['peers'])) + assert n.rpc.listhtlcs() == {'htlcs': []} + + # But forwards are not forgotten! + assert l2.rpc.listforwards()['forwards'] == all_forwards + @pytest.mark.openchannel('v1') def test_version_reexec(node_factory, bitcoind): diff --git a/wallet/wallet.c b/wallet/wallet.c index 7e7f84c7d14c..1674a722270e 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -5146,3 +5146,96 @@ struct db_stmt *wallet_datastore_next(const tal_t *ctx, return stmt; } + +/* We use a different query form if we only care about a single channel. */ +struct wallet_htlc_iter { + struct db_stmt *stmt; + /* Non-zero if they specified it */ + struct short_channel_id scid; +}; + +struct wallet_htlc_iter *wallet_htlcs_first(const tal_t *ctx, + struct wallet *w, + const struct channel *chan, + struct short_channel_id *scid, + u64 *htlc_id, + int *cltv_expiry, + enum side *owner, + struct amount_msat *msat, + struct sha256 *payment_hash, + enum htlc_state *hstate) +{ + struct wallet_htlc_iter *i = tal(ctx, struct wallet_htlc_iter); + + if (chan) { + i->scid = *channel_scid_or_local_alias(chan); + assert(i->scid.u64 != 0); + assert(chan->dbid != 0); + + i->stmt = db_prepare_v2(w->db, + SQL("SELECT h.channel_htlc_id" + ", h.cltv_expiry" + ", h.direction" + ", h.msatoshi" + ", h.payment_hash" + ", h.hstate" + " FROM channel_htlcs h" + " WHERE channel_id = ?")); + db_bind_u64(i->stmt, 0, chan->dbid); + } else { + i->scid.u64 = 0; + i->stmt = db_prepare_v2(w->db, + SQL("SELECT channels.scid" + ", channels.alias_local" + ", h.channel_htlc_id" + ", h.cltv_expiry" + ", h.direction" + ", h.msatoshi" + ", h.payment_hash" + ", h.hstate" + " FROM channel_htlcs h" + " JOIN channels ON channels.id = h.channel_id")); + } + /* FIXME: db_prepare should take ctx! */ + tal_steal(i, i->stmt); + db_query_prepared(i->stmt); + + return wallet_htlcs_next(w, i, + scid, htlc_id, cltv_expiry, owner, msat, + payment_hash, hstate); +} + +struct wallet_htlc_iter *wallet_htlcs_next(struct wallet *w, + struct wallet_htlc_iter *iter, + struct short_channel_id *scid, + u64 *htlc_id, + int *cltv_expiry, + enum side *owner, + struct amount_msat *msat, + struct sha256 *payment_hash, + enum htlc_state *hstate) +{ + if (!db_step(iter->stmt)) + return tal_free(iter); + + if (iter->scid.u64 != 0) + *scid = iter->scid; + else { + if (db_col_is_null(iter->stmt, "channels.scid")) + db_col_scid(iter->stmt, "channels.alias_local", scid); + else { + db_col_scid(iter->stmt, "channels.scid", scid); + db_col_ignore(iter->stmt, "channels.alias_local"); + } + } + *htlc_id = db_col_u64(iter->stmt, "h.channel_htlc_id"); + if (db_col_int(iter->stmt, "h.direction") == DIRECTION_INCOMING) + *owner = REMOTE; + else + *owner = LOCAL; + db_col_amount_msat(iter->stmt, "h.msatoshi", msat); + db_col_sha256(iter->stmt, "h.payment_hash", payment_hash); + *cltv_expiry = db_col_int(iter->stmt, "h.cltv_expiry"); + *hstate = db_col_int(iter->stmt, "h.hstate"); + return iter; +} diff --git a/wallet/wallet.h b/wallet/wallet.h index 94ec5c0825f9..09b253148539 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1632,4 +1632,40 @@ struct db_stmt *wallet_datastore_next(const tal_t *ctx, const u8 **data, u64 *generation); +/** + * Iterate through the htlcs table. + * @w: the wallet + * @chan: optional channel to filter by + * + * Returns pointer to hand as @iter to wallet_htlcs_next(), or NULL. + * If you choose not to call wallet_htlcs_next() you must free it! + */ +struct wallet_htlc_iter *wallet_htlcs_first(const tal_t *ctx, + struct wallet *w, + const struct channel *chan, + struct short_channel_id *scid, + u64 *htlc_id, + int *cltv_expiry, + enum side *owner, + struct amount_msat *msat, + struct sha256 *payment_hash, + enum htlc_state *hstate); + +/** + * Iterate through the htlcs table. + * @w: the wallet + * @iter: the previous iter. + * + * Returns pointer to hand as @iter to wallet_htlcs_next(), or NULL. + * If you choose not to call wallet_htlcs_next() you must free it! + */ +struct wallet_htlc_iter *wallet_htlcs_next(struct wallet *w, + struct wallet_htlc_iter *iter, + struct short_channel_id *scid, + u64 *htlc_id, + int *cltv_expiry, + enum side *owner, + struct amount_msat *msat, + struct sha256 *payment_hash, + enum htlc_state *hstate); #endif /* LIGHTNING_WALLET_WALLET_H */ From 3079afb024e9307a696046d0e936ff240a5f4c86 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:22:58 +0930 Subject: [PATCH 1451/1530] lightningd: add `delforward` command. Changelog-Added: JSON-RPC: `delforward` command to delete listforwards entries. Signed-off-by: Rusty Russell --- common/jsonrpc_errors.h | 3 ++ doc/Makefile | 1 + doc/index.rst | 1 + doc/lightning-delforward.7.md | 53 ++++++++++++++++++++++++++ doc/schemas/delforward.request.json | 21 ++++++++++ doc/schemas/delforward.schema.json | 7 ++++ lightningd/peer_htlcs.c | 59 +++++++++++++++++++++++++++++ tests/test_misc.py | 11 ++++++ wallet/test/run-wallet.c | 5 +++ wallet/wallet.c | 23 +++++++++++ wallet/wallet.h | 9 +++++ 11 files changed, 193 insertions(+) create mode 100644 doc/lightning-delforward.7.md create mode 100644 doc/schemas/delforward.request.json create mode 100644 doc/schemas/delforward.schema.json diff --git a/common/jsonrpc_errors.h b/common/jsonrpc_errors.h index cb0f1a49c29e..ff678f417082 100644 --- a/common/jsonrpc_errors.h +++ b/common/jsonrpc_errors.h @@ -104,6 +104,9 @@ enum jsonrpc_errcode { /* Errors from signmessage command */ SIGNMESSAGE_PUBKEY_NOT_FOUND = 1301, + /* Errors from delforward command */ + DELFORWARD_NOT_FOUND = 1401, + /* Errors from wait* commands */ WAIT_TIMEOUT = 2000, }; diff --git a/doc/Makefile b/doc/Makefile index 79a0b8622665..6beb0d8527b3 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -28,6 +28,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-decode.7 \ doc/lightning-deldatastore.7 \ doc/lightning-delexpiredinvoice.7 \ + doc/lightning-delforward.7 \ doc/lightning-delinvoice.7 \ doc/lightning-delpay.7 \ doc/lightning-disableoffer.7 \ diff --git a/doc/index.rst b/doc/index.rst index c7bd4bacc6f5..80331e07981e 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -51,6 +51,7 @@ Core Lightning Documentation lightning-decodepay lightning-deldatastore lightning-delexpiredinvoice + lightning-delforward lightning-delinvoice lightning-delpay lightning-disableoffer diff --git a/doc/lightning-delforward.7.md b/doc/lightning-delforward.7.md new file mode 100644 index 000000000000..5ae6ee51b7e0 --- /dev/null +++ b/doc/lightning-delforward.7.md @@ -0,0 +1,53 @@ +lightning-delforward -- Command for removing a forwarding entry +=============================================================== + +SYNOPSIS +-------- + +**delforward** *in_channel* *in_htlc_id* *status* + +DESCRIPTION +----------- + +The **delforward** RPC command removes a single forward from **listforwards**, +using the uniquely-identifying *in_channel* and *in_htlc_id* (and, as a sanity +check, the *status*) given by that command. + +This command is mainly used by the *autoclean* plugin (see lightningd-config(7)), +As these database entries are only kept for your own analysis, removing them +has no effect on the running of your node. + +You cannot delete forwards which have status *offered* (i.e. are +currently active). + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an empty object is returned. + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +ERRORS +------ + +The following errors may be reported: + +- 1401: The forward specified does not exist. + +AUTHOR +------ + +Rusty Russell <> is mainly responsible. + +SEE ALSO +-------- + +lightning-autoclean(7) + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:200de829c6635242cb2dd8ec0650c2fa8f5fcbf413f4a704884516df80492fcb) diff --git a/doc/schemas/delforward.request.json b/doc/schemas/delforward.request.json new file mode 100644 index 000000000000..163c79909af6 --- /dev/null +++ b/doc/schemas/delforward.request.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [], + "properties": { + "in_channel": { + "type": "short_channel_id" + }, + "in_htlc_id": { + "type": "u64" + }, + "status": { + "type": "string", + "enum": [ + "settled", + "local_failed", + "failed" + ] + } + } +} diff --git a/doc/schemas/delforward.schema.json b/doc/schemas/delforward.schema.json new file mode 100644 index 000000000000..65571ad4c703 --- /dev/null +++ b/doc/schemas/delforward.schema.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": {} +} diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 7eadf672aaef..e1764bb27c62 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2895,6 +2895,65 @@ static const struct json_command listforwards_command = { }; AUTODATA(json_command, &listforwards_command); +static struct command_result *param_forward_delstatus(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + enum forward_status **status) +{ + struct command_result *ret; + + ret = param_forward_status(cmd, name, buffer, tok, status); + if (ret) + return ret; + + switch (**status) { + case FORWARD_OFFERED: + return command_fail_badparam(cmd, name, buffer, tok, + "delforward status cannot be offered"); + case FORWARD_ANY: + return command_fail_badparam(cmd, name, buffer, tok, + "delforward status cannot be any"); + case FORWARD_SETTLED: + case FORWARD_FAILED: + case FORWARD_LOCAL_FAILED: + return NULL; + } + abort(); +} + +static struct command_result *json_delforward(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct short_channel_id *chan_in; + u64 *htlc_id; + enum forward_status *status; + + if (!param(cmd, buffer, params, + p_req("in_channel", param_short_channel_id, &chan_in), + p_req("in_htlc_id", param_u64, &htlc_id), + p_req("status", param_forward_delstatus, &status), + NULL)) + return command_param_failed(); + + if (!wallet_forward_delete(cmd->ld->wallet, + chan_in, *htlc_id, *status)) + return command_fail(cmd, DELFORWARD_NOT_FOUND, + "Could not find that forward"); + + return command_success(cmd, json_stream_success(cmd)); +} + +static const struct json_command delforward_command = { + "delforward", + "channels", + json_delforward, + "Delete a forwarded payment by [in_channel], [in_htlc_id] and [status]" +}; +AUTODATA(json_command, &delforward_command); + static struct command_result *param_channel(struct command *cmd, const char *name, const char *buffer, diff --git a/tests/test_misc.py b/tests/test_misc.py index 03f7605de15f..cb54b58d3b7f 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2493,6 +2493,17 @@ def test_listforwards_and_listhtlcs(node_factory, bitcoind): # But forwards are not forgotten! assert l2.rpc.listforwards()['forwards'] == all_forwards + # Now try delforward! + with pytest.raises(RpcError, match="Could not find that forward") as exc_info: + l2.rpc.delforward(in_channel=c12, in_htlc_id=3, status='settled') + # static const errcode_t DELFORWARD_NOT_FOUND = 1401; + assert exc_info.value.error['code'] == 1401 + + l2.rpc.delforward(in_channel=c12, in_htlc_id=0, status='settled') + l2.rpc.delforward(in_channel=c12, in_htlc_id=1, status='settled') + l2.rpc.delforward(in_channel=c12, in_htlc_id=2, status='local_failed') + assert l2.rpc.listforwards() == {'forwards': []} + @pytest.mark.openchannel('v1') def test_version_reexec(node_factory, bitcoind): diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 5c2a71ed49b2..7157358acf1e 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -616,6 +616,11 @@ struct command_result *param_short_channel_id(struct command *cmd UNNEEDED, const jsmntok_t *tok UNNEEDED, struct short_channel_id **scid UNNEEDED) { fprintf(stderr, "param_short_channel_id called!\n"); abort(); } +/* Generated stub for param_u64 */ +struct command_result *param_u64(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint64_t **num UNNEEDED) +{ fprintf(stderr, "param_u64 called!\n"); abort(); } /* Generated stub for parse_onionpacket */ struct onionpacket *parse_onionpacket(const tal_t *ctx UNNEEDED, const u8 *src UNNEEDED, diff --git a/wallet/wallet.c b/wallet/wallet.c index 1674a722270e..8458f42bca98 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4663,6 +4663,29 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, return results; } +bool wallet_forward_delete(struct wallet *w, + const struct short_channel_id *chan_in, + u64 htlc_id, + enum forward_status state) +{ + struct db_stmt *stmt; + bool changed; + + stmt = db_prepare_v2(w->db, + SQL("DELETE FROM forwards" + " WHERE in_channel_scid = ?" + " AND in_htlc_id = ?" + " AND state = ?")); + db_bind_scid(stmt, 0, chan_in); + db_bind_u64(stmt, 1, htlc_id); + db_bind_int(stmt, 2, wallet_forward_status_in_db(state)); + db_exec_prepared_v2(stmt); + changed = db_count_changes(stmt) != 0; + tal_free(stmt); + + return changed; +} + struct wallet_transaction *wallet_transactions_get(struct wallet *w, const tal_t *ctx) { struct db_stmt *stmt; diff --git a/wallet/wallet.h b/wallet/wallet.h index 09b253148539..d5315eb1e514 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1379,6 +1379,15 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, const struct short_channel_id *chan_in, const struct short_channel_id *chan_out); +/** + * Delete a particular forward entry + * Returns false if not found + */ +bool wallet_forward_delete(struct wallet *w, + const struct short_channel_id *chan_in, + u64 htlc_id, + enum forward_status state); + /** * Load remote_ann_node_sig and remote_ann_bitcoin_sig * From a15f1be5f88bdae5a6e816026e35a035791875e8 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1452/1530] autoclean: clean up listforwards as well. And take the opportunity to rename l0 and l1 in the tests to the more natural l1 l2 (since we add l3). Signed-off-by: Rusty Russell --- plugins/autoclean.c | 79 ++++++++++++++++++++++++ tests/test_plugin.py | 143 +++++++++++++++++++++++++------------------ 2 files changed, 161 insertions(+), 61 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index b1670f20845c..a7fdf4908a9f 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -8,6 +8,8 @@ #include enum subsystem { + SUCCEEDEDFORWARDS, + FAILEDFORWARDS, SUCCEEDEDPAYS, FAILEDPAYS, PAIDINVOICES, @@ -15,6 +17,8 @@ enum subsystem { #define NUM_SUBSYSTEM (EXPIREDINVOICES + 1) }; static const char *subsystem_str[] = { + "succeededforwards", + "failedforwards", "succeededpays", "failedpays", "paidinvoices", @@ -227,6 +231,73 @@ static struct command_result *listsendpays_done(struct command *cmd, return set_next_timer(plugin); } +static struct command_result *listforwards_done(struct command *cmd, + const char *buf, + const jsmntok_t *result, + char *unused) +{ + const jsmntok_t *t, *fwds = json_get_member(buf, result, "forwards"); + size_t i; + u64 now = time_now().ts.tv_sec; + + json_for_each_arr(i, t, fwds) { + const jsmntok_t *status = json_get_member(buf, t, "status"); + jsmntok_t time; + enum subsystem subsys; + u64 restime; + + if (json_tok_streq(buf, status, "settled")) { + subsys = SUCCEEDEDFORWARDS; + } else if (json_tok_streq(buf, status, "failed") + || json_tok_streq(buf, status, "local_failed")) { + subsys = FAILEDFORWARDS; + } else + continue; + + /* Continue if we don't care. */ + if (subsystem_age[subsys] == 0) + continue; + + time = *json_get_member(buf, t, "resolved_time"); + /* This is a float, so truncate at '.' */ + for (int off = time.start; off < time.end; off++) { + if (buf[off] == '.') + time.end = off; + } + if (!json_to_u64(buf, &time, &restime)) { + plugin_err(plugin, "Bad time '%.*s'", + json_tok_full_len(&time), + json_tok_full(buf, &time)); + } + + if (restime <= now - subsystem_age[subsys]) { + struct out_req *req; + const jsmntok_t *inchan, *inid; + + inchan = json_get_member(buf, t, "in_channel"); + inid = json_get_member(buf, t, "in_htlc_id"); + + req = jsonrpc_request_start(plugin, NULL, "delforward", + del_done, del_failed, + int2ptr(subsys)); + json_add_tok(req->js, "in_channel", inchan, buf); + json_add_tok(req->js, "in_htlc_id", inid, buf); + json_add_tok(req->js, "status", status, buf); + send_outreq(plugin, req); + plugin_log(plugin, LOG_DBG, "Cleaning up fwd %.*s/%.*s", + json_tok_full_len(inchan), + json_tok_full(buf, inchan), + json_tok_full_len(inid), + json_tok_full(buf, inid)); + cleanup_reqs_remaining++; + } + } + + if (cleanup_reqs_remaining) + return command_still_pending(cmd); + return set_next_timer(plugin); +} + static void do_clean(void *unused) { struct out_req *req = NULL; @@ -248,6 +319,14 @@ static void do_clean(void *unused) send_outreq(plugin, req); } + if (subsystem_age[SUCCEEDEDFORWARDS] != 0 + || subsystem_age[FAILEDFORWARDS] != 0) { + req = jsonrpc_request_start(plugin, NULL, "listforwards", + listforwards_done, cmd_failed, + (char *)"listforwards"); + send_outreq(plugin, req); + } + if (!req) set_next_timer(plugin); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index cd2c0ffc6994..89849b6a8382 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2945,90 +2945,111 @@ def test_commando_badrune(node_factory): def test_autoclean(node_factory): - l0, l1 = node_factory.line_graph(2, opts={'autoclean-cycle': 10, - 'may_reconnect': True}) - - assert l1.rpc.autoclean_status('expiredinvoices')['autoclean']['expiredinvoices']['enabled'] is False - l1.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=5) - l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20) - l1.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20) - inv4 = l1.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000) - inv5 = l1.rpc.invoice(amount_msat=12300, label='inv5', description='description5', expiry=2000) - l1.rpc.autoclean(subsystem='expiredinvoices', age=2) - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 + l1, l2, l3 = node_factory.line_graph(3, opts={'autoclean-cycle': 10, + 'may_reconnect': True}, + wait_for_announce=True) + + assert l3.rpc.autoclean_status('expiredinvoices')['autoclean']['expiredinvoices']['enabled'] is False + l3.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=5) + l3.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20) + l3.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20) + inv4 = l3.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000) + inv5 = l3.rpc.invoice(amount_msat=12300, label='inv5', description='description5', expiry=2000) + l3.rpc.autoclean(subsystem='expiredinvoices', age=2) + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 # Both should still be there. - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 0 - assert len(l1.rpc.listinvoices('inv1')['invoices']) == 1 - assert len(l1.rpc.listinvoices('inv2')['invoices']) == 1 - assert l1.rpc.listinvoices('inv1')['invoices'][0]['description'] == 'description1' + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 0 + assert len(l3.rpc.listinvoices('inv1')['invoices']) == 1 + assert len(l3.rpc.listinvoices('inv2')['invoices']) == 1 + assert l3.rpc.listinvoices('inv1')['invoices'][0]['description'] == 'description1' # First it expires. - wait_for(lambda: only_one(l1.rpc.listinvoices('inv1')['invoices'])['status'] == 'expired') + wait_for(lambda: only_one(l3.rpc.listinvoices('inv1')['invoices'])['status'] == 'expired') # Now will get autocleaned - wait_for(lambda: l1.rpc.listinvoices('inv1')['invoices'] == []) - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 + wait_for(lambda: l3.rpc.listinvoices('inv1')['invoices'] == []) + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 # Keeps settings across restarts - l1.restart() + l3.restart() - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 # Disabling works - l1.rpc.autoclean(subsystem='expiredinvoices', age='never') - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False - assert 'age' not in l1.rpc.autoclean_status()['autoclean']['expiredinvoices'] + l3.rpc.autoclean(subsystem='expiredinvoices', age='never') + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False + assert 'age' not in l3.rpc.autoclean_status()['autoclean']['expiredinvoices'] # Same with inv2/3 - wait_for(lambda: only_one(l1.rpc.listinvoices('inv2')['invoices'])['status'] == 'expired') + wait_for(lambda: only_one(l3.rpc.listinvoices('inv2')['invoices'])['status'] == 'expired') # Give it time to notice. time.sleep(15) - assert l1.rpc.listinvoices('inv2')['invoices'] != [] + assert l3.rpc.listinvoices('inv2')['invoices'] != [] # Restart keeps it disabled. - l1.restart() - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False - assert 'age' not in l1.rpc.autoclean_status()['autoclean']['expiredinvoices'] + l3.restart() + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False + assert 'age' not in l3.rpc.autoclean_status()['autoclean']['expiredinvoices'] # Now enable: they will get autocleaned - l1.rpc.autoclean(subsystem='expiredinvoices', age=2) - wait_for(lambda: len(l1.rpc.listinvoices()['invoices']) == 2) - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 + l3.rpc.autoclean(subsystem='expiredinvoices', age=2) + wait_for(lambda: len(l3.rpc.listinvoices()['invoices']) == 2) + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 - # Reconnect, l0 pays invoice, we test paid expiry. - l1.rpc.connect(l0.info['id'], 'localhost', l0.port) - l0.rpc.pay(inv4['bolt11']) + # Reconnect, l1 pays invoice, we test paid expiry. + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l1.rpc.pay(inv4['bolt11']) - # We manually delete inv5 so we can have l0 fail a payment. - l1.rpc.delinvoice('inv5', 'unpaid') + # We manually delete inv5 so we can have l1 fail a payment. + l3.rpc.delinvoice('inv5', 'unpaid') with pytest.raises(RpcError, match='WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): - l0.rpc.pay(inv5['bolt11']) - - assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is False - assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 0 - l1.rpc.autoclean(subsystem='paidinvoices', age=1) - assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is True - - wait_for(lambda: l1.rpc.listinvoices()['invoices'] == []) - assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 - assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 1 - - assert only_one(l0.rpc.listpays(inv5['bolt11'])['pays'])['status'] == 'failed' - assert only_one(l0.rpc.listpays(inv4['bolt11'])['pays'])['status'] == 'complete' - l0.rpc.autoclean(subsystem='failedpays', age=2) - - wait_for(lambda: l0.rpc.listpays(inv5['bolt11'])['pays'] == []) - assert l0.rpc.autoclean_status()['autoclean']['failedpays']['cleaned'] == 1 - assert l0.rpc.autoclean_status()['autoclean']['succeededpays']['cleaned'] == 0 - - l0.rpc.autoclean(subsystem='succeededpays', age=2) - wait_for(lambda: l0.rpc.listpays(inv4['bolt11'])['pays'] == []) - assert l0.rpc.listsendpays() == {'payments': []} + l1.rpc.pay(inv5['bolt11']) + + assert l3.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is False + assert l3.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 0 + l3.rpc.autoclean(subsystem='paidinvoices', age=1) + assert l3.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is True + + wait_for(lambda: l3.rpc.listinvoices()['invoices'] == []) + assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 + assert l3.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 1 + + assert only_one(l1.rpc.listpays(inv5['bolt11'])['pays'])['status'] == 'failed' + assert only_one(l1.rpc.listpays(inv4['bolt11'])['pays'])['status'] == 'complete' + l1.rpc.autoclean(subsystem='failedpays', age=2) + + wait_for(lambda: l1.rpc.listpays(inv5['bolt11'])['pays'] == []) + assert l1.rpc.autoclean_status()['autoclean']['failedpays']['cleaned'] == 1 + assert l1.rpc.autoclean_status()['autoclean']['succeededpays']['cleaned'] == 0 + + l1.rpc.autoclean(subsystem='succeededpays', age=2) + wait_for(lambda: l1.rpc.listpays(inv4['bolt11'])['pays'] == []) + assert l1.rpc.listsendpays() == {'payments': []} + + # Now, we should have 1 failed forward, 1 success. + assert len(l2.rpc.listforwards(status='failed')['forwards']) == 1 + assert len(l2.rpc.listforwards(status='settled')['forwards']) == 1 + assert len(l2.rpc.listforwards()['forwards']) == 2 + + # Clean failed ones. + l2.rpc.autoclean(subsystem='failedforwards', age=2) + wait_for(lambda: l2.rpc.listforwards(status='failed')['forwards'] == []) + + assert len(l2.rpc.listforwards(status='settled')['forwards']) == 1 + assert l2.rpc.autoclean_status()['autoclean']['failedforwards']['cleaned'] == 1 + assert l2.rpc.autoclean_status()['autoclean']['succeededforwards']['cleaned'] == 0 + + # Clean succeeded ones + l2.rpc.autoclean(subsystem='succeededforwards', age=2) + wait_for(lambda: l2.rpc.listforwards(status='settled')['forwards'] == []) + assert l2.rpc.listforwards() == {'forwards': []} + assert l2.rpc.autoclean_status()['autoclean']['failedforwards']['cleaned'] == 1 + assert l2.rpc.autoclean_status()['autoclean']['succeededforwards']['cleaned'] == 1 def test_block_added_notifications(node_factory, bitcoind): From 399288db3f2a90e52fcea587424c1287705c69d1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1453/1530] autoclean: use config variables, not commands. It's more natural: we will eventually support dynamic config variables, so this will be quite nice. Signed-off-by: Rusty Russell Changelog-Added: Plugins: `autoclean` can now delete old forwards, payments, and invoices automatically. --- doc/lightningd-config.5.md | 24 +++++++++++ plugins/autoclean.c | 87 ++++++++++++-------------------------- tests/test_plugin.py | 33 +++++++++++---- 3 files changed, 75 insertions(+), 69 deletions(-) diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index f846ba8082ec..61c038b823aa 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -437,6 +437,30 @@ accepted, and ignored. Perform search for things to clean every *SECONDS* seconds (default 3600, or 1 hour, which is usually sufficient). +* **autoclean-succeededforwards-age**=*SECONDS* [plugin `autoclean`] + + How old successful forwards (`settled` in listforwards `status`) have to be before deletion (default 0, meaning never). + +* **autoclean-failedforwards-age**=*SECONDS* [plugin `autoclean`] + + How old failed forwards (`failed` or `local_failed` in listforwards `status`) have to be before deletion (default 0, meaning never). + +* **autoclean-succeededpays-age**=*SECONDS* [plugin `autoclean`] + + How old successful payments (`complete` in listpays `status`) have to be before deletion (default 0, meaning never). + +* **autoclean-failedpays-age**=*SECONDS* [plugin `autoclean`] + + How old failed payment attempts (`failed` in listpays `status`) have to be before deletion (default 0, meaning never). + +* **autoclean-paidinvoices-age**=*SECONDS* [plugin `autoclean`] + + How old invoices which were paid (`paid` in listinvoices `status`) have to be before deletion (default 0, meaning never). + +* **autoclean-expiredinvoices-age**=*SECONDS* [plugin `autoclean`] + + How old invoices which were not paid (and cannot be) (`expired` in listinvoices `status`) before deletion (default 0, meaning never). + ### Payment control options: * **disable-mpp** [plugin `pay`] diff --git a/plugins/autoclean.c b/plugins/autoclean.c index a7fdf4908a9f..12e7bf889426 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -16,6 +16,7 @@ enum subsystem { EXPIREDINVOICES, #define NUM_SUBSYSTEM (EXPIREDINVOICES + 1) }; + static const char *subsystem_str[] = { "succeededforwards", "failedforwards", @@ -366,23 +367,6 @@ static struct command_result *json_autocleaninvoice(struct command *cmd, return command_finished(cmd, response); } -static struct command_result *param_age(struct command *cmd, const char *name, - const char *buffer, const jsmntok_t *tok, - uint64_t **num) -{ - *num = tal(cmd, uint64_t); - if (json_to_u64(buffer, tok, *num) && *num != 0) - return NULL; - - if (json_tok_streq(buffer, tok, "never")) { - **num = 0; - return NULL; - } - - return command_fail_badparam(cmd, name, buffer, tok, - "should be an positive 64 bit integer or 'never'"); -} - static struct command_result *param_subsystem(struct command *cmd, const char *name, const char *buffer, @@ -431,28 +415,6 @@ static struct command_result *json_autoclean_status(struct command *cmd, return json_success_subsystems(cmd, subsystem); } -static struct command_result *json_autoclean(struct command *cmd, - const char *buffer, - const jsmntok_t *params) -{ - enum subsystem *subsystem; - u64 *age; - - if (!param(cmd, buffer, params, - p_req("subsystem", param_subsystem, &subsystem), - p_req("age", param_age, &age), - NULL)) - return command_param_failed(); - - subsystem_age[*subsystem] = *age; - jsonrpc_set_datastore_string(cmd->plugin, cmd, - datastore_path(tmpctx, *subsystem, "age"), - tal_fmt(tmpctx, "%"PRIu64, *age), - "create-or-replace", NULL, NULL, NULL); - - return json_success_subsystems(cmd, subsystem); -} - static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { @@ -463,21 +425,6 @@ static const char *init(struct plugin *p, return NULL; } else cycle_seconds = deprecated_cycle_seconds; - } else { - bool active = false; - for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { - if (!rpc_scan_datastore_str(p, datastore_path(tmpctx, i, "age"), - JSON_SCAN(json_to_u64, &subsystem_age[i]))) - continue; - if (subsystem_age[i]) { - /* Only print this once! */ - if (!active) - plugin_log(p, LOG_INFORM, "autocleaning every %"PRIu64" seconds", cycle_seconds); - active = true; - plugin_log(p, LOG_INFORM, "cleaning %s when age > %"PRIu64" seconds", - subsystem_to_str(i), subsystem_age[i]); - } - } } cleantimer = plugin_timer(p, time_from_sec(cycle_seconds), do_clean, p); @@ -498,12 +445,6 @@ static const struct plugin_command commands[] = { { json_autocleaninvoice, true, /* deprecated! */ }, { - "autoclean", - "utility", - "Automatic deletion of old data (invoices, pays, forwards).", - "Takes {subsystem} and {age} in seconds ", - json_autoclean, - }, { "autoclean-status", "utility", "Show status of autocleaning", @@ -529,9 +470,33 @@ int main(int argc, char *argv[]) " this given seconds are cleaned", u64_option, &subsystem_age[EXPIREDINVOICES]), plugin_option("autoclean-cycle", - "string", + "int", "Perform cleanup every" " given seconds", u64_option, &cycle_seconds), + plugin_option("autoclean-succeededforwards-age", + "int", + "How old do successful forwards have to be before deletion (0 = never)", + u64_option, &subsystem_age[SUCCEEDEDFORWARDS]), + plugin_option("autoclean-failedforwards-age", + "int", + "How old do failed forwards have to be before deletion (0 = never)", + u64_option, &subsystem_age[FAILEDFORWARDS]), + plugin_option("autoclean-succeededpays-age", + "int", + "How old do successful pays have to be before deletion (0 = never)", + u64_option, &subsystem_age[SUCCEEDEDPAYS]), + plugin_option("autoclean-failedpays-age", + "int", + "How old do failed pays have to be before deletion (0 = never)", + u64_option, &subsystem_age[FAILEDPAYS]), + plugin_option("autoclean-paidinvoices-age", + "int", + "How old do paid invoices have to be before deletion (0 = never)", + u64_option, &subsystem_age[PAIDINVOICES]), + plugin_option("autoclean-expiredinvoices-age", + "int", + "How old do expired invoices have to be before deletion (0 = never)", + u64_option, &subsystem_age[EXPIREDINVOICES]), NULL); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 89849b6a8382..2d874595a92d 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2955,7 +2955,10 @@ def test_autoclean(node_factory): l3.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20) inv4 = l3.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000) inv5 = l3.rpc.invoice(amount_msat=12300, label='inv5', description='description5', expiry=2000) - l3.rpc.autoclean(subsystem='expiredinvoices', age=2) + + l3.stop() + l3.daemon.opts['autoclean-expiredinvoices-age'] = 2 + l3.start() assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2 @@ -2979,7 +2982,9 @@ def test_autoclean(node_factory): assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 # Disabling works - l3.rpc.autoclean(subsystem='expiredinvoices', age='never') + l3.stop() + l3.daemon.opts['autoclean-expiredinvoices-age'] = 0 + l3.start() assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is False assert 'age' not in l3.rpc.autoclean_status()['autoclean']['expiredinvoices'] @@ -2997,7 +3002,9 @@ def test_autoclean(node_factory): assert 'age' not in l3.rpc.autoclean_status()['autoclean']['expiredinvoices'] # Now enable: they will get autocleaned - l3.rpc.autoclean(subsystem='expiredinvoices', age=2) + l3.stop() + l3.daemon.opts['autoclean-expiredinvoices-age'] = 2 + l3.start() wait_for(lambda: len(l3.rpc.listinvoices()['invoices']) == 2) assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3 @@ -3012,7 +3019,9 @@ def test_autoclean(node_factory): assert l3.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is False assert l3.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 0 - l3.rpc.autoclean(subsystem='paidinvoices', age=1) + l3.stop() + l3.daemon.opts['autoclean-paidinvoices-age'] = 1 + l3.start() assert l3.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is True wait_for(lambda: l3.rpc.listinvoices()['invoices'] == []) @@ -3021,13 +3030,17 @@ def test_autoclean(node_factory): assert only_one(l1.rpc.listpays(inv5['bolt11'])['pays'])['status'] == 'failed' assert only_one(l1.rpc.listpays(inv4['bolt11'])['pays'])['status'] == 'complete' - l1.rpc.autoclean(subsystem='failedpays', age=2) + l1.stop() + l1.daemon.opts['autoclean-failedpays-age'] = 1 + l1.start() wait_for(lambda: l1.rpc.listpays(inv5['bolt11'])['pays'] == []) assert l1.rpc.autoclean_status()['autoclean']['failedpays']['cleaned'] == 1 assert l1.rpc.autoclean_status()['autoclean']['succeededpays']['cleaned'] == 0 - l1.rpc.autoclean(subsystem='succeededpays', age=2) + l1.stop() + l1.daemon.opts['autoclean-succeededpays-age'] = 2 + l1.start() wait_for(lambda: l1.rpc.listpays(inv4['bolt11'])['pays'] == []) assert l1.rpc.listsendpays() == {'payments': []} @@ -3037,7 +3050,9 @@ def test_autoclean(node_factory): assert len(l2.rpc.listforwards()['forwards']) == 2 # Clean failed ones. - l2.rpc.autoclean(subsystem='failedforwards', age=2) + l2.stop() + l2.daemon.opts['autoclean-failedforwards-age'] = 2 + l2.start() wait_for(lambda: l2.rpc.listforwards(status='failed')['forwards'] == []) assert len(l2.rpc.listforwards(status='settled')['forwards']) == 1 @@ -3045,7 +3060,9 @@ def test_autoclean(node_factory): assert l2.rpc.autoclean_status()['autoclean']['succeededforwards']['cleaned'] == 0 # Clean succeeded ones - l2.rpc.autoclean(subsystem='succeededforwards', age=2) + l2.stop() + l2.daemon.opts['autoclean-succeededforwards-age'] = 2 + l2.start() wait_for(lambda: l2.rpc.listforwards(status='settled')['forwards'] == []) assert l2.rpc.listforwards() == {'forwards': []} assert l2.rpc.autoclean_status()['autoclean']['failedforwards']['cleaned'] == 1 From 612f3de0d4a07851c8f25ed8e2c359ecea7e3e2f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1454/1530] doc: manpages and schemas for autoclean-status. And remove deprecated autocleaninvoice docs. Signed-off-by: Rusty Russell Changelog-Added: Plugins: `autoclean-status` command to see what autoclean is doing. Changelog-Deprecated: JSON-RPC: `autocleaninvoice` (use option `autoclean-expiredinvoices-age`) --- doc/Makefile | 2 +- doc/index.rst | 2 +- doc/lightning-autoclean-status.7.md | 94 ++++++ doc/lightning-autocleaninvoice.7.md | 55 ---- doc/lightning-delexpiredinvoice.7.md | 2 +- doc/lightning-delinvoice.7.md | 2 +- doc/lightning-listforwards.7.md | 2 +- doc/schemas/autoclean-status.request.json | 20 ++ doc/schemas/autoclean-status.schema.json | 346 ++++++++++++++++++++++ 9 files changed, 465 insertions(+), 60 deletions(-) create mode 100644 doc/lightning-autoclean-status.7.md delete mode 100644 doc/lightning-autocleaninvoice.7.md create mode 100644 doc/schemas/autoclean-status.request.json create mode 100644 doc/schemas/autoclean-status.schema.json diff --git a/doc/Makefile b/doc/Makefile index 6beb0d8527b3..cb3b3d411efb 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -8,7 +8,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightningd.8 \ doc/lightningd-config.5 \ doc/lightning-addgossip.7 \ - doc/lightning-autocleaninvoice.7 \ + doc/lightning-autoclean-status.7 \ doc/lightning-bkpr-channelsapy.7 \ doc/lightning-bkpr-dumpincomecsv.7 \ doc/lightning-bkpr-inspect.7 \ diff --git a/doc/index.rst b/doc/index.rst index 80331e07981e..13bdbb48a7a3 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -30,7 +30,7 @@ Core Lightning Documentation :caption: Manpages lightning-addgossip - lightning-autocleaninvoice + lightning-autoclean-status lightning-bkpr-channelsapy lightning-bkpr-dumpincomecsv lightning-bkpr-inspect diff --git a/doc/lightning-autoclean-status.7.md b/doc/lightning-autoclean-status.7.md new file mode 100644 index 000000000000..ca22f2675fbe --- /dev/null +++ b/doc/lightning-autoclean-status.7.md @@ -0,0 +1,94 @@ +lightning-autoclean-status -- Examine auto-delete of old invoices/payments/forwards +=================================================================================== + +SYNOPSIS +-------- + +**autoclean-status** [*subsystem*] + +DESCRIPTION +----------- + +The **autoclean-status** RPC command tells you about the status of +the autclean plugin, optionally for only one subsystem. + +The subsystems currently supported are: + +* `failedforwards`: routed payments which did not succeed (`failed` or `local_failed` in listforwards `status`). +* `succeededforwards`: routed payments which succeeded (`settled` in listforwards `status`). +* `failedpays`: payment attempts which did not succeed (`failed` in listpays `status`). +* `succededpays`: payment attempts which succeeded (`complete` in listpays `status`). +* `expiredinvoices`: invoices which were not paid (and cannot be) (`expired` in listinvoices `status`). +* `paidinvoices`: invoices which were paid (`paid` in listinvoices `status). + +RETURN VALUE +------------ + +Note that the ages parameters are set by various `autoclean-...-age` +parameters in your configuration: see lightningd-config(5). + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object containing **autoclean** is returned. It is an object containing: + +- **succeededforwards** (object, optional): + - **enabled** (boolean): whether autocleaning is enabled for successful listforwards + - **cleaned** (u64): total number of deletions done (ever) + + If **enabled** is *true*: + + - **age** (u64): age (in seconds) to delete successful listforwards +- **failedforwards** (object, optional): + - **enabled** (boolean): whether autocleaning is enabled for failed listforwards + - **cleaned** (u64): total number of deletions done (ever) + + If **enabled** is *true*: + + - **age** (u64): age (in seconds) to delete failed listforwards +- **succeededpays** (object, optional): + - **enabled** (boolean): whether autocleaning is enabled for successful listpays/listsendpays + - **cleaned** (u64): total number of deletions done (ever) + + If **enabled** is *true*: + + - **age** (u64): age (in seconds) to delete successful listpays/listsendpays +- **failedpays** (object, optional): + - **enabled** (boolean): whether autocleaning is enabled for failed listpays/listsendpays + - **cleaned** (u64): total number of deletions done (ever) + + If **enabled** is *true*: + + - **age** (u64): age (in seconds) to delete failed listpays/listsendpays +- **paidinvoices** (object, optional): + - **enabled** (boolean): whether autocleaning is enabled for paid listinvoices + - **cleaned** (u64): total number of deletions done (ever) + + If **enabled** is *true*: + + - **age** (u64): age (in seconds) to paid listinvoices +- **expiredinvoices** (object, optional): + - **enabled** (boolean): whether autocleaning is enabled for expired (unpaid) listinvoices + - **cleaned** (u64): total number of deletions done (ever) + + If **enabled** is *true*: + + - **age** (u64): age (in seconds) to expired listinvoices + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +AUTHOR +------ + +Rusty Russell <> is mainly responsible. + +SEE ALSO +-------- + +lightningd-config(5), lightning-listinvoices(7), +lightning-listpays(7), lightning-listforwards(7). + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:9431024693a7c26f9519ef24bdfb8b5c26902bdc0631d427f89c9e49ecd88e13) diff --git a/doc/lightning-autocleaninvoice.7.md b/doc/lightning-autocleaninvoice.7.md deleted file mode 100644 index 1aa59ef1cb4b..000000000000 --- a/doc/lightning-autocleaninvoice.7.md +++ /dev/null @@ -1,55 +0,0 @@ -lightning-autocleaninvoice -- Set up auto-delete of expired invoice -=================================================================== - -SYNOPSIS --------- - -**autocleaninvoice** [*cycle\_seconds*] [*expired\_by*] - -DESCRIPTION ------------ - -The **autocleaninvoice** RPC command sets up automatic cleaning of -expired invoices. - -Autoclean will be done every *cycle\_seconds* seconds. Setting -*cycle\_seconds* to 0 disables autoclean. If not specified, this -defaults to 3600 (one hour). - -Every autoclean cycle, expired invoices, which have already been expired -for at least *expired\_by* seconds, will be deleted. If *expired\_by* is -not specified, this defaults to 86400 (one day). - -On startup of the daemon, no autoclean is set up. - -RETURN VALUE ------------- - -[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 ------- - -ZmnSCPxj <> is mainly responsible. - -SEE ALSO --------- - -lightning-delexpiredinvoice(7), lightning-delinvoice(7) - -RESOURCES ---------- - -Main web site: - -[comment]: # ( SHA256STAMP:994e8f17bf35fc704f13206bf4c6909b525f9edcb1c8c4508345c720b007d34c) diff --git a/doc/lightning-delexpiredinvoice.7.md b/doc/lightning-delexpiredinvoice.7.md index c670f543ede0..e2a9c3c45924 100644 --- a/doc/lightning-delexpiredinvoice.7.md +++ b/doc/lightning-delexpiredinvoice.7.md @@ -31,7 +31,7 @@ ZmnSCPxj <> is mainly responsible. SEE ALSO -------- -lightning-delinvoice(7), lightning-autocleaninvoice(7) +lightning-delinvoice(7), lightning-autoclean-status(7) RESOURCES --------- diff --git a/doc/lightning-delinvoice.7.md b/doc/lightning-delinvoice.7.md index 38408b516081..71f34b78d03e 100644 --- a/doc/lightning-delinvoice.7.md +++ b/doc/lightning-delinvoice.7.md @@ -74,7 +74,7 @@ SEE ALSO lightning-listinvoice(7), lightning-waitinvoice(7), lightning-invoice(7), lightning-delexpiredinvoice(7), -lightning-autocleaninvoice(7) +lightning-autoclean-status(7) RESOURCES --------- diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index 08ab60bfdef7..d4b7cefe4ba8 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -57,7 +57,7 @@ Rene Pickhardt <> is mainly responsible. SEE ALSO -------- -lightning-getinfo(7) +lightning-autoclean-status(7), lightning-getinfo(7) RESOURCES --------- diff --git a/doc/schemas/autoclean-status.request.json b/doc/schemas/autoclean-status.request.json new file mode 100644 index 000000000000..b289de135241 --- /dev/null +++ b/doc/schemas/autoclean-status.request.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [], + "properties": { + "subsystem": { + "type": "string", + "enum": [ + "succeededforwards", + "failedforwards", + "succeededpays", + "failedpays", + "paidinvoices", + "expiredinvoices" + ], + "description": "What subsystem to ask about" + } + } +} diff --git a/doc/schemas/autoclean-status.schema.json b/doc/schemas/autoclean-status.schema.json new file mode 100644 index 000000000000..bb5234f7b299 --- /dev/null +++ b/doc/schemas/autoclean-status.schema.json @@ -0,0 +1,346 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "autoclean" + ], + "properties": { + "autoclean": { + "type": "object", + "additionalProperties": false, + "properties": { + "succeededforwards": { + "type": "object", + "additionalProperties": true, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": { + "type": "boolean", + "description": "whether autocleaning is enabled for successful listforwards" + }, + "cleaned": { + "type": "u64", + "description": "total number of deletions done (ever)" + } + }, + "if": { + "properties": { + "enabled": { + "type": "boolean", + "enum": [ + true + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "enabled", + "age", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {}, + "age": { + "type": "u64", + "description": "age (in seconds) to delete successful listforwards" + } + } + }, + "else": { + "additionalProperties": false, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {} + } + } + }, + "failedforwards": { + "type": "object", + "additionalProperties": true, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": { + "type": "boolean", + "description": "whether autocleaning is enabled for failed listforwards" + }, + "cleaned": { + "type": "u64", + "description": "total number of deletions done (ever)" + } + }, + "if": { + "properties": { + "enabled": { + "type": "boolean", + "enum": [ + true + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "enabled", + "age", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {}, + "age": { + "type": "u64", + "description": "age (in seconds) to delete failed listforwards" + } + } + }, + "else": { + "additionalProperties": false, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {} + } + } + }, + "succeededpays": { + "type": "object", + "additionalProperties": true, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": { + "type": "boolean", + "description": "whether autocleaning is enabled for successful listpays/listsendpays" + }, + "cleaned": { + "type": "u64", + "description": "total number of deletions done (ever)" + } + }, + "if": { + "properties": { + "enabled": { + "type": "boolean", + "enum": [ + true + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "enabled", + "age", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {}, + "age": { + "type": "u64", + "description": "age (in seconds) to delete successful listpays/listsendpays" + } + } + }, + "else": { + "additionalProperties": false, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {} + } + } + }, + "failedpays": { + "type": "object", + "additionalProperties": true, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": { + "type": "boolean", + "description": "whether autocleaning is enabled for failed listpays/listsendpays" + }, + "cleaned": { + "type": "u64", + "description": "total number of deletions done (ever)" + } + }, + "if": { + "properties": { + "enabled": { + "type": "boolean", + "enum": [ + true + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "enabled", + "age", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {}, + "age": { + "type": "u64", + "description": "age (in seconds) to delete failed listpays/listsendpays" + } + } + }, + "else": { + "additionalProperties": false, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {} + } + } + }, + "paidinvoices": { + "type": "object", + "additionalProperties": true, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": { + "type": "boolean", + "description": "whether autocleaning is enabled for paid listinvoices" + }, + "cleaned": { + "type": "u64", + "description": "total number of deletions done (ever)" + } + }, + "if": { + "properties": { + "enabled": { + "type": "boolean", + "enum": [ + true + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "enabled", + "age", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {}, + "age": { + "type": "u64", + "description": "age (in seconds) to paid listinvoices" + } + } + }, + "else": { + "additionalProperties": false, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {} + } + } + }, + "expiredinvoices": { + "type": "object", + "additionalProperties": true, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": { + "type": "boolean", + "description": "whether autocleaning is enabled for expired (unpaid) listinvoices" + }, + "cleaned": { + "type": "u64", + "description": "total number of deletions done (ever)" + } + }, + "if": { + "properties": { + "enabled": { + "type": "boolean", + "enum": [ + true + ] + } + } + }, + "then": { + "additionalProperties": false, + "required": [ + "enabled", + "age", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {}, + "age": { + "type": "u64", + "description": "age (in seconds) to expired listinvoices" + } + } + }, + "else": { + "additionalProperties": false, + "required": [ + "enabled", + "cleaned" + ], + "properties": { + "enabled": {}, + "cleaned": {} + } + } + } + } + } + } +} From 13e10877de9dde1b5aa784894a15a4611b62a46a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1455/1530] autoclean: add autoclean-once command. Changelog-Added: Plugins: `autoclean-once` command for a single cleanup. Signed-off-by: Rusty Russell --- plugins/autoclean.c | 318 ++++++++++++++++++++++++++++++------------- tests/test_plugin.py | 101 ++++++++++++++ 2 files changed, 327 insertions(+), 92 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 12e7bf889426..96ae8b8faf18 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -45,22 +45,31 @@ static bool json_to_subsystem(const char *buffer, const jsmntok_t *tok, return false; } +/* Usually this refers to the global one, but for autoclean-once + * it's a temporary. */ +struct clean_info { + struct command *cmd; + size_t cleanup_reqs_remaining; + u64 subsystem_age[NUM_SUBSYSTEM]; + u64 num_cleaned[NUM_SUBSYSTEM]; + u64 num_uncleaned; +}; + /* For deprecated API, setting this to zero disabled autoclean */ static u64 deprecated_cycle_seconds = UINT64_MAX; static u64 cycle_seconds = 3600; -static u64 subsystem_age[NUM_SUBSYSTEM]; -static u64 num_cleaned[NUM_SUBSYSTEM]; -static size_t cleanup_reqs_remaining; +static struct clean_info timer_cinfo; +static u64 total_cleaned[NUM_SUBSYSTEM]; static struct plugin *plugin; static struct plugin_timer *cleantimer; -static void do_clean(void *cb_arg); +static void do_clean_timer(void *unused); /* Fatal failures */ static struct command_result *cmd_failed(struct command *cmd, const char *buf, const jsmntok_t *result, - char *cmdname) + const char *cmdname) { plugin_err(plugin, "Failed '%s': '%.*s'", cmdname, json_tok_full_len(result), @@ -75,58 +84,103 @@ static const char *datastore_path(const tal_t *ctx, subsystem_to_str(subsystem), field); } -static struct command_result *set_next_timer(struct plugin *plugin) -{ - plugin_log(plugin, LOG_DBG, "setting next timer"); - cleantimer = plugin_timer(plugin, time_from_sec(cycle_seconds), do_clean, plugin); - return timer_complete(plugin); -} - -static struct command_result *clean_finished_one(struct command *cmd) +static struct command_result *clean_finished(struct clean_info *cinfo) { - assert(cleanup_reqs_remaining != 0); - if (--cleanup_reqs_remaining > 0) - return command_still_pending(cmd); - for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { - if (num_cleaned[i] == 0) + if (!cinfo->num_cleaned[i]) continue; - jsonrpc_set_datastore_string(plugin, cmd, + plugin_log(plugin, LOG_DBG, "cleaned %"PRIu64" from %s", + cinfo->num_cleaned[i], subsystem_to_str(i)); + total_cleaned[i] += cinfo->num_cleaned[i]; + jsonrpc_set_datastore_string(plugin, cinfo->cmd, datastore_path(tmpctx, i, "num"), - tal_fmt(tmpctx, "%"PRIu64, num_cleaned[i]), + tal_fmt(tmpctx, "%"PRIu64, total_cleaned[i]), "create-or-replace", NULL, NULL, NULL); } - return set_next_timer(plugin); + /* autoclean-once? */ + if (cinfo->cmd) { + struct json_stream *response = jsonrpc_stream_success(cinfo->cmd); + + json_object_start(response, "autoclean"); + for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { + if (cinfo->subsystem_age[i] == 0) + continue; + json_object_start(response, subsystem_to_str(i)); + json_add_u64(response, "cleaned", cinfo->num_cleaned[i]); + json_add_u64(response, "uncleaned", cinfo->num_uncleaned); + json_object_end(response); + } + json_object_end(response); + return command_finished(cinfo->cmd, response); + } else { /* timer */ + plugin_log(plugin, LOG_DBG, "setting next timer"); + cleantimer = plugin_timer(plugin, time_from_sec(cycle_seconds), + do_clean_timer, NULL); + return timer_complete(plugin); + } +} + +static struct command_result *clean_finished_one(struct clean_info *cinfo) +{ + assert(cinfo->cleanup_reqs_remaining != 0); + if (--cinfo->cleanup_reqs_remaining > 0) + return command_still_pending(cinfo->cmd); + + return clean_finished(cinfo); } +struct del_data { + enum subsystem subsystem; + struct clean_info *cinfo; +}; + static struct command_result *del_done(struct command *cmd, const char *buf, const jsmntok_t *result, - ptrint_t *subsystemp) + struct del_data *del_data) { - num_cleaned[ptr2int(subsystemp)]++; - return clean_finished_one(cmd); + struct clean_info *cinfo = del_data->cinfo; + + cinfo->num_cleaned[del_data->subsystem]++; + tal_free(del_data); + return clean_finished_one(cinfo); } static struct command_result *del_failed(struct command *cmd, - const char *buf, - const jsmntok_t *result, - ptrint_t *subsystemp) + const char *buf, + const jsmntok_t *result, + struct del_data *del_data) { + struct clean_info *cinfo = del_data->cinfo; + plugin_log(plugin, LOG_UNUSUAL, "%s del failed: %.*s", - subsystem_to_str(ptr2int(subsystemp)), + subsystem_to_str(del_data->subsystem), json_tok_full_len(result), json_tok_full(buf, result)); - return clean_finished_one(cmd); + tal_free(del_data); + return clean_finished_one(cinfo); +} + +static struct out_req *del_request_start(const char *method, + struct clean_info *cinfo, + enum subsystem subsystem) +{ + struct del_data *del_data = tal(plugin, struct del_data); + + del_data->cinfo = cinfo; + del_data->subsystem = subsystem; + cinfo->cleanup_reqs_remaining++; + return jsonrpc_request_start(plugin, NULL, method, + del_done, del_failed, del_data); } static struct command_result *listinvoices_done(struct command *cmd, const char *buf, const jsmntok_t *result, - char *unused) + struct clean_info *cinfo) { const jsmntok_t *t, *inv = json_get_member(buf, result, "invoices"); size_t i; @@ -144,12 +198,16 @@ static struct command_result *listinvoices_done(struct command *cmd, } else if (json_tok_streq(buf, status, "paid")) { subsys = PAIDINVOICES; time = json_get_member(buf, t, "paid_at"); - } else + } else { + cinfo->num_uncleaned++; continue; + } /* Continue if we don't care. */ - if (subsystem_age[subsys] == 0) + if (cinfo->subsystem_age[subsys] == 0) { + cinfo->num_uncleaned++; continue; + } if (!json_to_u64(buf, time, &invtime)) { plugin_err(plugin, "Bad time '%.*s'", @@ -157,31 +215,28 @@ static struct command_result *listinvoices_done(struct command *cmd, json_tok_full(buf, time)); } - if (invtime <= now - subsystem_age[subsys]) { + if (invtime <= now - cinfo->subsystem_age[subsys]) { struct out_req *req; const jsmntok_t *label = json_get_member(buf, t, "label"); - req = jsonrpc_request_start(plugin, NULL, "delinvoice", - del_done, del_failed, - int2ptr(subsys)); + req = del_request_start("delinvoice", cinfo, subsys); json_add_tok(req->js, "label", label, buf); json_add_tok(req->js, "status", status, buf); send_outreq(plugin, req); plugin_log(plugin, LOG_DBG, "Cleaning up %.*s", json_tok_full_len(label), json_tok_full(buf, label)); - cleanup_reqs_remaining++; } } - if (cleanup_reqs_remaining) + if (cinfo->cleanup_reqs_remaining) return command_still_pending(cmd); - return set_next_timer(plugin); + return clean_finished(cinfo); } static struct command_result *listsendpays_done(struct command *cmd, const char *buf, const jsmntok_t *result, - char *unused) + struct clean_info *cinfo) { const jsmntok_t *t, *pays = json_get_member(buf, result, "payments"); size_t i; @@ -197,12 +252,16 @@ static struct command_result *listsendpays_done(struct command *cmd, subsys = FAILEDPAYS; } else if (json_tok_streq(buf, status, "complete")) { subsys = SUCCEEDEDPAYS; - } else + } else { + cinfo->num_uncleaned++; continue; + } /* Continue if we don't care. */ - if (subsystem_age[subsys] == 0) + if (cinfo->subsystem_age[subsys] == 0) { + cinfo->num_uncleaned++; continue; + } time = json_get_member(buf, t, "created_at"); if (!json_to_u64(buf, time, &paytime)) { @@ -211,31 +270,28 @@ static struct command_result *listsendpays_done(struct command *cmd, json_tok_full(buf, time)); } - if (paytime <= now - subsystem_age[subsys]) { + if (paytime <= now - cinfo->subsystem_age[subsys]) { struct out_req *req; const jsmntok_t *phash = json_get_member(buf, t, "payment_hash"); - req = jsonrpc_request_start(plugin, NULL, "delpay", - del_done, del_failed, - int2ptr(subsys)); + req = del_request_start("delpay", cinfo, subsys); json_add_tok(req->js, "payment_hash", phash, buf); json_add_tok(req->js, "status", status, buf); send_outreq(plugin, req); plugin_log(plugin, LOG_DBG, "Cleaning up %.*s", json_tok_full_len(phash), json_tok_full(buf, phash)); - cleanup_reqs_remaining++; } } - if (cleanup_reqs_remaining) + if (cinfo->cleanup_reqs_remaining) return command_still_pending(cmd); - return set_next_timer(plugin); + return clean_finished(cinfo); } static struct command_result *listforwards_done(struct command *cmd, const char *buf, const jsmntok_t *result, - char *unused) + struct clean_info *cinfo) { const jsmntok_t *t, *fwds = json_get_member(buf, result, "forwards"); size_t i; @@ -252,12 +308,16 @@ static struct command_result *listforwards_done(struct command *cmd, } else if (json_tok_streq(buf, status, "failed") || json_tok_streq(buf, status, "local_failed")) { subsys = FAILEDFORWARDS; - } else + } else { + cinfo->num_uncleaned++; continue; + } /* Continue if we don't care. */ - if (subsystem_age[subsys] == 0) + if (cinfo->subsystem_age[subsys] == 0) { + cinfo->num_uncleaned++; continue; + } time = *json_get_member(buf, t, "resolved_time"); /* This is a float, so truncate at '.' */ @@ -271,16 +331,14 @@ static struct command_result *listforwards_done(struct command *cmd, json_tok_full(buf, &time)); } - if (restime <= now - subsystem_age[subsys]) { + if (restime <= now - cinfo->subsystem_age[subsys]) { struct out_req *req; const jsmntok_t *inchan, *inid; inchan = json_get_member(buf, t, "in_channel"); inid = json_get_member(buf, t, "in_htlc_id"); - req = jsonrpc_request_start(plugin, NULL, "delforward", - del_done, del_failed, - int2ptr(subsys)); + req = del_request_start("delforward", cinfo, subsys); json_add_tok(req->js, "in_channel", inchan, buf); json_add_tok(req->js, "in_htlc_id", inid, buf); json_add_tok(req->js, "status", status, buf); @@ -290,46 +348,81 @@ static struct command_result *listforwards_done(struct command *cmd, json_tok_full(buf, inchan), json_tok_full_len(inid), json_tok_full(buf, inid)); - cleanup_reqs_remaining++; } } - if (cleanup_reqs_remaining) + if (cinfo->cleanup_reqs_remaining) return command_still_pending(cmd); - return set_next_timer(plugin); + return clean_finished(cinfo); +} + +static struct command_result *listsendpays_failed(struct command *cmd, + const char *buf, + const jsmntok_t *result, + void *unused) +{ + return cmd_failed(cmd, buf, result, "listsendpays"); +} + +static struct command_result *listinvoices_failed(struct command *cmd, + const char *buf, + const jsmntok_t *result, + void *unused) +{ + return cmd_failed(cmd, buf, result, "listinvoices"); } -static void do_clean(void *unused) +static struct command_result *listforwards_failed(struct command *cmd, + const char *buf, + const jsmntok_t *result, + void *unused) +{ + return cmd_failed(cmd, buf, result, "listforwards"); +} + +static struct command_result *do_clean(struct clean_info *cinfo) { struct out_req *req = NULL; - assert(cleanup_reqs_remaining == 0); - if (subsystem_age[SUCCEEDEDPAYS] != 0 - || subsystem_age[FAILEDPAYS] != 0) { + cinfo->cleanup_reqs_remaining = 0; + cinfo->num_uncleaned = 0; + memset(cinfo->num_cleaned, 0, sizeof(cinfo->num_cleaned)); + + if (cinfo->subsystem_age[SUCCEEDEDPAYS] != 0 + || cinfo->subsystem_age[FAILEDPAYS] != 0) { req = jsonrpc_request_start(plugin, NULL, "listsendpays", - listsendpays_done, cmd_failed, - (char *)"listsendpays"); + listsendpays_done, listsendpays_failed, + cinfo); send_outreq(plugin, req); } - if (subsystem_age[EXPIREDINVOICES] != 0 - || subsystem_age[PAIDINVOICES] != 0) { + if (cinfo->subsystem_age[EXPIREDINVOICES] != 0 + || cinfo->subsystem_age[PAIDINVOICES] != 0) { req = jsonrpc_request_start(plugin, NULL, "listinvoices", - listinvoices_done, cmd_failed, - (char *)"listinvoices"); + listinvoices_done, listinvoices_failed, + cinfo); send_outreq(plugin, req); } - if (subsystem_age[SUCCEEDEDFORWARDS] != 0 - || subsystem_age[FAILEDFORWARDS] != 0) { + if (cinfo->subsystem_age[SUCCEEDEDFORWARDS] != 0 + || cinfo->subsystem_age[FAILEDFORWARDS] != 0) { req = jsonrpc_request_start(plugin, NULL, "listforwards", - listforwards_done, cmd_failed, - (char *)"listforwards"); + listforwards_done, listforwards_failed, + cinfo); send_outreq(plugin, req); } - if (!req) - set_next_timer(plugin); + if (req) + return command_still_pending(NULL); + else + return clean_finished(cinfo); +} + +/* Needs a different signature than do_clean */ +static void do_clean_timer(void *unused) +{ + assert(timer_cinfo.cleanup_reqs_remaining == 0); + do_clean(&timer_cinfo); } static struct command_result *json_autocleaninvoice(struct command *cmd, @@ -349,21 +442,21 @@ static struct command_result *json_autocleaninvoice(struct command *cmd, cleantimer = tal_free(cleantimer); if (*cycle == 0) { - subsystem_age[EXPIREDINVOICES] = 0; + timer_cinfo.subsystem_age[EXPIREDINVOICES] = 0; response = jsonrpc_stream_success(cmd); json_add_bool(response, "enabled", false); return command_finished(cmd, response); } cycle_seconds = *cycle; - subsystem_age[EXPIREDINVOICES] = *exby; + timer_cinfo.subsystem_age[EXPIREDINVOICES] = *exby; cleantimer = plugin_timer(cmd->plugin, time_from_sec(cycle_seconds), - do_clean, cmd->plugin); + do_clean_timer, NULL); 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", subsystem_age[EXPIREDINVOICES]); + json_add_u64(response, "expired_by", timer_cinfo.subsystem_age[EXPIREDINVOICES]); return command_finished(cmd, response); } @@ -391,10 +484,10 @@ static struct command_result *json_success_subsystems(struct command *cmd, if (subsystem && i != *subsystem) continue; json_object_start(response, subsystem_to_str(i)); - json_add_bool(response, "enabled", subsystem_age[i] != 0); - if (subsystem_age[i] != 0) - json_add_u64(response, "age", subsystem_age[i]); - json_add_u64(response, "cleaned", num_cleaned[i]); + json_add_bool(response, "enabled", timer_cinfo.subsystem_age[i] != 0); + if (timer_cinfo.subsystem_age[i] != 0) + json_add_u64(response, "age", timer_cinfo.subsystem_age[i]); + json_add_u64(response, "cleaned", total_cleaned[i]); json_object_end(response); } json_object_end(response); @@ -415,6 +508,41 @@ static struct command_result *json_autoclean_status(struct command *cmd, return json_success_subsystems(cmd, subsystem); } +static struct command_result *param_u64_nonzero(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + u64 **val) +{ + struct command_result *res = param_u64(cmd, name, buffer, tok, val); + if (res == NULL && *val == 0) + res = command_fail_badparam(cmd, name, buffer, tok, + "Must be non-zero"); + return res; +} + +static struct command_result *json_autoclean_once(struct command *cmd, + const char *buffer, + const jsmntok_t *params) +{ + enum subsystem *subsystem; + u64 *age; + struct clean_info *cinfo; + + if (!param(cmd, buffer, params, + p_req("subsystem", param_subsystem, &subsystem), + p_req("age", param_u64_nonzero, &age), + NULL)) + return command_param_failed(); + + cinfo = tal(cmd, struct clean_info); + cinfo->cmd = cmd; + memset(cinfo->subsystem_age, 0, sizeof(cinfo->subsystem_age)); + cinfo->subsystem_age[*subsystem] = *age; + + return do_clean(cinfo); +} + static const char *init(struct plugin *p, const char *buf UNUSED, const jsmntok_t *config UNUSED) { @@ -427,11 +555,11 @@ static const char *init(struct plugin *p, cycle_seconds = deprecated_cycle_seconds; } - cleantimer = plugin_timer(p, time_from_sec(cycle_seconds), do_clean, p); + cleantimer = plugin_timer(p, time_from_sec(cycle_seconds), do_clean_timer, NULL); for (enum subsystem i = 0; i < NUM_SUBSYSTEM; i++) { rpc_scan_datastore_str(plugin, datastore_path(tmpctx, i, "num"), - JSON_SCAN(json_to_u64, &num_cleaned[i])); + JSON_SCAN(json_to_u64, &total_cleaned[i])); } return NULL; } @@ -450,6 +578,12 @@ static const struct plugin_command commands[] = { { "Show status of autocleaning", "Takes optional {subsystem}", json_autoclean_status, + }, { + "autoclean-once", + "utility", + "Perform a single run of autocleaning on one subsystem", + "Requires {subsystem} and {age}", + json_autoclean_once, }, }; @@ -468,7 +602,7 @@ int main(int argc, char *argv[]) "If expired invoice autoclean enabled," " invoices that have expired for at least" " this given seconds are cleaned", - u64_option, &subsystem_age[EXPIREDINVOICES]), + u64_option, &timer_cinfo.subsystem_age[EXPIREDINVOICES]), plugin_option("autoclean-cycle", "int", "Perform cleanup every" @@ -477,26 +611,26 @@ int main(int argc, char *argv[]) plugin_option("autoclean-succeededforwards-age", "int", "How old do successful forwards have to be before deletion (0 = never)", - u64_option, &subsystem_age[SUCCEEDEDFORWARDS]), + u64_option, &timer_cinfo.subsystem_age[SUCCEEDEDFORWARDS]), plugin_option("autoclean-failedforwards-age", "int", "How old do failed forwards have to be before deletion (0 = never)", - u64_option, &subsystem_age[FAILEDFORWARDS]), + u64_option, &timer_cinfo.subsystem_age[FAILEDFORWARDS]), plugin_option("autoclean-succeededpays-age", "int", "How old do successful pays have to be before deletion (0 = never)", - u64_option, &subsystem_age[SUCCEEDEDPAYS]), + u64_option, &timer_cinfo.subsystem_age[SUCCEEDEDPAYS]), plugin_option("autoclean-failedpays-age", "int", "How old do failed pays have to be before deletion (0 = never)", - u64_option, &subsystem_age[FAILEDPAYS]), + u64_option, &timer_cinfo.subsystem_age[FAILEDPAYS]), plugin_option("autoclean-paidinvoices-age", "int", "How old do paid invoices have to be before deletion (0 = never)", - u64_option, &subsystem_age[PAIDINVOICES]), + u64_option, &timer_cinfo.subsystem_age[PAIDINVOICES]), plugin_option("autoclean-expiredinvoices-age", "int", "How old do expired invoices have to be before deletion (0 = never)", - u64_option, &subsystem_age[EXPIREDINVOICES]), + u64_option, &timer_cinfo.subsystem_age[EXPIREDINVOICES]), NULL); } diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 2d874595a92d..bb19f561a764 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -3069,6 +3069,107 @@ def test_autoclean(node_factory): assert l2.rpc.autoclean_status()['autoclean']['succeededforwards']['cleaned'] == 1 +def test_autoclean_once(node_factory): + l1, l2, l3 = node_factory.line_graph(3, opts={'may_reconnect': True}, + wait_for_announce=True) + + l3.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=1) + inv2 = l3.rpc.invoice(amount_msat=12300, label='inv2', description='description4') + inv3 = l3.rpc.invoice(amount_msat=12300, label='inv3', description='description5') + + l1.rpc.pay(inv2['bolt11']) + l3.rpc.delinvoice('inv3', 'unpaid') + with pytest.raises(RpcError, match='WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): + l1.rpc.pay(inv3['bolt11']) + + # Make sure > 1 second old! + time.sleep(2) + assert (l1.rpc.autoclean_once('failedpays', 1) + == {'autoclean': {'failedpays': {'cleaned': 1, 'uncleaned': 1}}}) + assert l1.rpc.autoclean_status() == {'autoclean': {'failedpays': {'enabled': False, + 'cleaned': 1}, + 'succeededpays': {'enabled': False, + 'cleaned': 0}, + 'failedforwards': {'enabled': False, + 'cleaned': 0}, + 'succeededforwards': {'enabled': False, + 'cleaned': 0}, + 'expiredinvoices': {'enabled': False, + 'cleaned': 0}, + 'paidinvoices': {'enabled': False, + 'cleaned': 0}}} + assert (l1.rpc.autoclean_once('succeededpays', 1) + == {'autoclean': {'succeededpays': {'cleaned': 1, 'uncleaned': 0}}}) + assert l1.rpc.autoclean_status() == {'autoclean': {'failedpays': {'enabled': False, + 'cleaned': 1}, + 'succeededpays': {'enabled': False, + 'cleaned': 1}, + 'failedforwards': {'enabled': False, + 'cleaned': 0}, + 'succeededforwards': {'enabled': False, + 'cleaned': 0}, + 'expiredinvoices': {'enabled': False, + 'cleaned': 0}, + 'paidinvoices': {'enabled': False, + 'cleaned': 0}}} + assert (l2.rpc.autoclean_once('failedforwards', 1) + == {'autoclean': {'failedforwards': {'cleaned': 1, 'uncleaned': 1}}}) + assert l2.rpc.autoclean_status() == {'autoclean': {'failedpays': {'enabled': False, + 'cleaned': 0}, + 'succeededpays': {'enabled': False, + 'cleaned': 0}, + 'failedforwards': {'enabled': False, + 'cleaned': 1}, + 'succeededforwards': {'enabled': False, + 'cleaned': 0}, + 'expiredinvoices': {'enabled': False, + 'cleaned': 0}, + 'paidinvoices': {'enabled': False, + 'cleaned': 0}}} + assert (l2.rpc.autoclean_once('succeededforwards', 1) + == {'autoclean': {'succeededforwards': {'cleaned': 1, 'uncleaned': 0}}}) + assert l2.rpc.autoclean_status() == {'autoclean': {'failedpays': {'enabled': False, + 'cleaned': 0}, + 'succeededpays': {'enabled': False, + 'cleaned': 0}, + 'failedforwards': {'enabled': False, + 'cleaned': 1}, + 'succeededforwards': {'enabled': False, + 'cleaned': 1}, + 'expiredinvoices': {'enabled': False, + 'cleaned': 0}, + 'paidinvoices': {'enabled': False, + 'cleaned': 0}}} + assert (l3.rpc.autoclean_once('expiredinvoices', 1) + == {'autoclean': {'expiredinvoices': {'cleaned': 1, 'uncleaned': 1}}}) + assert l3.rpc.autoclean_status() == {'autoclean': {'failedpays': {'enabled': False, + 'cleaned': 0}, + 'succeededpays': {'enabled': False, + 'cleaned': 0}, + 'failedforwards': {'enabled': False, + 'cleaned': 0}, + 'succeededforwards': {'enabled': False, + 'cleaned': 0}, + 'expiredinvoices': {'enabled': False, + 'cleaned': 1}, + 'paidinvoices': {'enabled': False, + 'cleaned': 0}}} + assert (l3.rpc.autoclean_once('paidinvoices', 1) + == {'autoclean': {'paidinvoices': {'cleaned': 1, 'uncleaned': 0}}}) + assert l3.rpc.autoclean_status() == {'autoclean': {'failedpays': {'enabled': False, + 'cleaned': 0}, + 'succeededpays': {'enabled': False, + 'cleaned': 0}, + 'failedforwards': {'enabled': False, + 'cleaned': 0}, + 'succeededforwards': {'enabled': False, + 'cleaned': 0}, + 'expiredinvoices': {'enabled': False, + 'cleaned': 1}, + 'paidinvoices': {'enabled': False, + 'cleaned': 1}}} + + def test_block_added_notifications(node_factory, bitcoind): """Test if a plugin gets notifications when a new block is found""" base = bitcoind.rpc.getblockchaininfo()["blocks"] From 540a6e4b99c5c0b5b49dbd6b1c604f599eb45718 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1456/1530] autoclean: remove per-delete debugging messages. They slow down benchmarking, which is kind of unfair! Signed-off-by: Rusty Russell --- plugins/autoclean.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 96ae8b8faf18..8993e1078742 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -223,8 +223,6 @@ static struct command_result *listinvoices_done(struct command *cmd, json_add_tok(req->js, "label", label, buf); json_add_tok(req->js, "status", status, buf); send_outreq(plugin, req); - plugin_log(plugin, LOG_DBG, "Cleaning up %.*s", - json_tok_full_len(label), json_tok_full(buf, label)); } } @@ -278,8 +276,6 @@ static struct command_result *listsendpays_done(struct command *cmd, json_add_tok(req->js, "payment_hash", phash, buf); json_add_tok(req->js, "status", status, buf); send_outreq(plugin, req); - plugin_log(plugin, LOG_DBG, "Cleaning up %.*s", - json_tok_full_len(phash), json_tok_full(buf, phash)); } } @@ -343,11 +339,6 @@ static struct command_result *listforwards_done(struct command *cmd, json_add_tok(req->js, "in_htlc_id", inid, buf); json_add_tok(req->js, "status", status, buf); send_outreq(plugin, req); - plugin_log(plugin, LOG_DBG, "Cleaning up fwd %.*s/%.*s", - json_tok_full_len(inchan), - json_tok_full(buf, inchan), - json_tok_full_len(inid), - json_tok_full(buf, inid)); } } From 4d8c3215174e1436dccb66d60fa69536f3b4d31a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1457/1530] libplugin: optimize parsing lightningd rpc responses. autoclean was using 98% of its time in memmove; we should simply keep an offset, and memmove when it's empty. And also, only memmove the used region, not the entire buffer! Running on product of giantnodes.py: $ time l1-cli autoclean-once failedpays 1 { "autoclean": { "failedpays": { "cleaned": 26895, "uncleaned": 1000000 } } } Before: real 20m46.579s user 0m0.000s sys 0m0.001s After: real 2m10.568s user 0m0.001s sys 0m0.000s Signed-off-by: Rusty Russell --- plugins/libplugin.c | 60 ++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/plugins/libplugin.c b/plugins/libplugin.c index e5cacb5311ec..5506196fd8f4 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -53,7 +53,7 @@ struct plugin { struct io_conn *io_rpc_conn; struct json_stream **rpc_js_arr; char *rpc_buffer; - size_t rpc_used, rpc_len_read; + size_t rpc_used, rpc_len_read, rpc_read_offset; jsmn_parser rpc_parser; jsmntok_t *rpc_toks; /* Tracking async RPC requests */ @@ -672,20 +672,21 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) const jsmntok_t *idtok, *contenttok; struct out_req *out; struct command_result *res; + const char *buf = plugin->rpc_buffer + plugin->rpc_read_offset; - idtok = json_get_member(plugin->rpc_buffer, toks, "id"); + idtok = json_get_member(buf, toks, "id"); if (!idtok) /* FIXME: Don't simply ignore notifications! */ return; out = strmap_getn(&plugin->out_reqs, - plugin->rpc_buffer + idtok->start, + buf + idtok->start, idtok->end - idtok->start); if (!out) { /* This can actually happen, if they free req! */ plugin_log(plugin, LOG_DBG, "JSON reply with unknown id '%.*s'", json_tok_full_len(toks), - json_tok_full(plugin->rpc_buffer, toks)); + json_tok_full(buf, toks)); return; } @@ -696,27 +697,23 @@ static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) /* We want to free this if callback doesn't. */ tal_steal(tmpctx, out); - contenttok = json_get_member(plugin->rpc_buffer, toks, "error"); + contenttok = json_get_member(buf, toks, "error"); if (contenttok) { if (out->errcb) - res = out->errcb(out->cmd, plugin->rpc_buffer, - contenttok, out->arg); + res = out->errcb(out->cmd, buf, contenttok, out->arg); else - res = out->cb(out->cmd, plugin->rpc_buffer, - toks, out->arg); + res = out->cb(out->cmd, buf, toks, out->arg); } else { - contenttok = json_get_member(plugin->rpc_buffer, toks, "result"); + contenttok = json_get_member(buf, toks, "result"); if (!contenttok) plugin_err(plugin, "Bad JSONRPC, no 'error' nor 'result': '%.*s'", json_tok_full_len(toks), - json_tok_full(plugin->rpc_buffer, toks)); + json_tok_full(buf, toks)); /* errcb is NULL if it's a single whole-object callback */ if (out->errcb) - res = out->cb(out->cmd, plugin->rpc_buffer, contenttok, - out->arg); + res = out->cb(out->cmd, buf, contenttok, out->arg); else - res = out->cb(out->cmd, plugin->rpc_buffer, toks, - out->arg); + res = out->cb(out->cmd, buf, toks, out->arg); } assert(res == &pending || res == &complete); @@ -861,42 +858,48 @@ static bool rpc_read_response_one(struct plugin *plugin) bool complete; if (!json_parse_input(&plugin->rpc_parser, &plugin->rpc_toks, - plugin->rpc_buffer, plugin->rpc_used, &complete)) { + plugin->rpc_buffer + plugin->rpc_read_offset, + plugin->rpc_used - plugin->rpc_read_offset, + &complete)) { plugin_err(plugin, "Failed to parse RPC JSON response '%.*s'", - (int)plugin->rpc_used, plugin->rpc_buffer); - return false; + (int)(plugin->rpc_used - plugin->rpc_read_offset), + plugin->rpc_buffer + plugin->rpc_read_offset); } if (!complete) { /* We need more. */ - return false; + goto compact; } /* Empty buffer? (eg. just whitespace). */ if (tal_count(plugin->rpc_toks) == 1) { - plugin->rpc_used = 0; jsmn_init(&plugin->rpc_parser); toks_reset(plugin->rpc_toks); - return false; + goto compact; } - jrtok = json_get_member(plugin->rpc_buffer, plugin->rpc_toks, "jsonrpc"); + jrtok = json_get_member(plugin->rpc_buffer + plugin->rpc_read_offset, + plugin->rpc_toks, "jsonrpc"); if (!jrtok) { plugin_err(plugin, "JSON-RPC message does not contain \"jsonrpc\" field: '%.*s'", - (int)plugin->rpc_used, plugin->rpc_buffer); - return false; + (int)(plugin->rpc_used - plugin->rpc_read_offset), + plugin->rpc_buffer + plugin->rpc_read_offset); } handle_rpc_reply(plugin, plugin->rpc_toks); /* Move this object out of the buffer */ - memmove(plugin->rpc_buffer, plugin->rpc_buffer + plugin->rpc_toks[0].end, - tal_count(plugin->rpc_buffer) - plugin->rpc_toks[0].end); - plugin->rpc_used -= plugin->rpc_toks[0].end; + plugin->rpc_read_offset += plugin->rpc_toks[0].end; jsmn_init(&plugin->rpc_parser); toks_reset(plugin->rpc_toks); - return true; + +compact: + memmove(plugin->rpc_buffer, plugin->rpc_buffer + plugin->rpc_read_offset, + plugin->rpc_used - plugin->rpc_read_offset); + plugin->rpc_used -= plugin->rpc_read_offset; + plugin->rpc_read_offset = 0; + return false; } static struct io_plan *rpc_conn_read_response(struct io_conn *conn, @@ -1632,6 +1635,7 @@ static struct plugin *new_plugin(const tal_t *ctx, p->rpc_buffer = tal_arr(p, char, 64); p->rpc_js_arr = tal_arr(p, struct json_stream *, 0); p->rpc_used = 0; + p->rpc_read_offset = 0; p->rpc_len_read = 0; jsmn_init(&p->rpc_parser); p->rpc_toks = toks_alloc(p); From 8b7a8265e7ad80bb0e1882ad5dffada14f7425df Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1458/1530] libplugin: avoid memmove if we have many outputs to lightningd. Use linked list, not an array. ``` + 97.89% 0.01% autoclean autoclean [.] next_plan - 97.08% 0.01% autoclean autoclean [.] json_stream_output_write - 97.06% json_stream_output_write - 84.29% ld_stream_complete - 83.87% tal_arr_remove_ + 83.71% __memcpy_avx_unaligned_erms (inlined) + 12.76% rpc_stream_complete + 96.59% 0.03% autoclean autoclean [.] tal_arr_remove_ + 96.48% 0.00% autoclean libc.so.6 [.] __memcpy_avx_unaligned_erms (inlined) + 94.98% 94.98% autoclean libc.so.6 [.] __memmove_avx_unaligned_erms + 84.29% 0.01% autoclean autoclean [.] ld_stream_complete + 12.76% 0.00% autoclean autoclean [.] rpc_stream_complete ``` Signed-off-by: Rusty Russell --- plugins/bkpr/test/run-bkpr_db.c | 2 +- plugins/bkpr/test/run-recorder.c | 2 +- plugins/libplugin.c | 54 ++++++++++++++++++-------------- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index 70248b287f5f..9cfccc5398e7 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -292,7 +292,7 @@ int main(int argc, char *argv[]) bool ok = true; /* Dummy for migration hooks */ struct plugin *plugin = tal(NULL, struct plugin); - plugin->js_arr = tal_arr(plugin, struct json_stream *, 0); + list_head_init(&plugin->js_list); common_setup(argv[0]); diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index a90035187eb4..b569eeeb7e4c 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -1421,7 +1421,7 @@ int main(int argc, char *argv[]) bool ok = true; /* Dummy for migration hooks */ struct plugin *plugin = tal(NULL, struct plugin); - plugin->js_arr = tal_arr(plugin, struct json_stream *, 0); + list_head_init(&plugin->js_list); common_setup(argv[0]); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 5506196fd8f4..36d1877989aa 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -32,6 +32,12 @@ struct rpc_conn { MEMBUF(char) mb; }; +/* We can have more than one of these pending at once. */ +struct jstream { + struct list_node list; + struct json_stream *js; +}; + struct plugin { /* lightningd interaction */ struct io_conn *stdin_conn; @@ -47,11 +53,11 @@ struct plugin { jsmntok_t *toks; /* To write to lightningd */ - struct json_stream **js_arr; + struct list_head js_list; /* Asynchronous RPC interaction */ struct io_conn *io_rpc_conn; - struct json_stream **rpc_js_arr; + struct list_head rpc_js_list; char *rpc_buffer; size_t rpc_used, rpc_len_read, rpc_read_offset; jsmn_parser rpc_parser; @@ -128,15 +134,17 @@ struct command_result *command_done(void) static void ld_send(struct plugin *plugin, struct json_stream *stream) { - tal_steal(plugin->js_arr, stream); - tal_arr_expand(&plugin->js_arr, stream); + struct jstream *jstr = tal(plugin, struct jstream); + jstr->js = tal_steal(jstr, stream); + list_add_tail(&plugin->js_list, &jstr->list); io_wake(plugin); } static void ld_rpc_send(struct plugin *plugin, struct json_stream *stream) { - tal_steal(plugin->rpc_js_arr, stream); - tal_arr_expand(&plugin->rpc_js_arr, stream); + struct jstream *jstr = tal(plugin, struct jstream); + jstr->js = tal_steal(jstr, stream); + list_add_tail(&plugin->rpc_js_list, &jstr->list); io_wake(plugin->io_rpc_conn); } @@ -928,12 +936,10 @@ static struct io_plan * rpc_stream_complete(struct io_conn *conn, struct json_stream *js, struct plugin *plugin) { - assert(tal_count(plugin->rpc_js_arr) > 0); - /* Remove js and shift all remaining over */ - tal_arr_remove(&plugin->rpc_js_arr, 0); - - /* It got dropped off the queue, free it. */ - tal_free(js); + struct jstream *jstr = list_pop(&plugin->rpc_js_list, struct jstream, list); + assert(jstr); + assert(jstr->js == js); + tal_free(jstr); return rpc_conn_write_request(conn, plugin); } @@ -941,8 +947,9 @@ rpc_stream_complete(struct io_conn *conn, struct json_stream *js, static struct io_plan *rpc_conn_write_request(struct io_conn *conn, struct plugin *plugin) { - if (tal_count(plugin->rpc_js_arr) > 0) - return json_stream_output(plugin->rpc_js_arr[0], conn, + struct jstream *jstr = list_top(&plugin->rpc_js_list, struct jstream, list); + if (jstr) + return json_stream_output(jstr->js, conn, rpc_stream_complete, plugin); return io_out_wait(conn, plugin->io_rpc_conn, @@ -1548,12 +1555,10 @@ static struct io_plan * ld_stream_complete(struct io_conn *conn, struct json_stream *js, struct plugin *plugin) { - assert(tal_count(plugin->js_arr) > 0); - /* Remove js and shift all remainig over */ - tal_arr_remove(&plugin->js_arr, 0); - - /* It got dropped off the queue, free it. */ - tal_free(js); + struct jstream *jstr = list_pop(&plugin->js_list, struct jstream, list); + assert(jstr); + assert(jstr->js == js); + tal_free(jstr); return ld_write_json(conn, plugin); } @@ -1561,8 +1566,9 @@ ld_stream_complete(struct io_conn *conn, struct json_stream *js, static struct io_plan *ld_write_json(struct io_conn *conn, struct plugin *plugin) { - if (tal_count(plugin->js_arr) > 0) - return json_stream_output(plugin->js_arr[0], plugin->stdout_conn, + struct jstream *jstr = list_top(&plugin->js_list, struct jstream, list); + if (jstr) + return json_stream_output(jstr->js, plugin->stdout_conn, ld_stream_complete, plugin); /* If we were simply flushing final output, stop now. */ @@ -1626,14 +1632,14 @@ static struct plugin *new_plugin(const tal_t *ctx, name[path_ext_off(name)] = '\0'; p->id = name; p->buffer = tal_arr(p, char, 64); - p->js_arr = tal_arr(p, struct json_stream *, 0); + list_head_init(&p->js_list); p->used = 0; p->len_read = 0; jsmn_init(&p->parser); p->toks = toks_alloc(p); /* Async RPC */ p->rpc_buffer = tal_arr(p, char, 64); - p->rpc_js_arr = tal_arr(p, struct json_stream *, 0); + list_head_init(&p->rpc_js_list); p->rpc_used = 0; p->rpc_read_offset = 0; p->rpc_len_read = 0; From 555b8a2f7a2bd728efa15dda8302084e477aa8c9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:23:00 +0930 Subject: [PATCH 1459/1530] lightningd: don't always wrap each command in a db transaction. This shows the benefits of batching when being slammed by autoclean: Before: ``` plugin-autoclean: last 1000 deletes took 6699022 nsec each plugin-autoclean: last 1000 deletes took 6734025 nsec each plugin-autoclean: last 1000 deletes took 6681189 nsec each plugin-autoclean: last 1000 deletes took 6597148 nsec each plugin-autoclean: last 1000 deletes took 6637085 nsec each plugin-autoclean: last 1000 deletes took 6801425 nsec each plugin-autoclean: last 1000 deletes took 6788572 nsec each plugin-autoclean: last 1000 deletes took 6603641 nsec each plugin-autoclean: last 1000 deletes took 6642947 nsec each plugin-autoclean: last 1000 deletes took 6590495 nsec each plugin-autoclean: last 1000 deletes took 6695076 nsec each plugin-autoclean: last 1000 deletes took 6477981 nsec each ``` After: ``` plugin-autoclean: last 1000 deletes took 342764 nsec each plugin-autoclean: last 1000 deletes took 375031 nsec each plugin-autoclean: last 1000 deletes took 357564 nsec each plugin-autoclean: last 1000 deletes took 381581 nsec each plugin-autoclean: last 1000 deletes took 337989 nsec each plugin-autoclean: last 1000 deletes took 329391 nsec each plugin-autoclean: last 1000 deletes took 328322 nsec each plugin-autoclean: last 1000 deletes took 372810 nsec each plugin-autoclean: last 1000 deletes took 351228 nsec each plugin-autoclean: last 1000 deletes took 413885 nsec each plugin-autoclean: last 1000 deletes took 348317 nsec each ``` Signed-off-by: Rusty Russell --- lightningd/jsonrpc.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 58a1d6011d51..474928409546 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -948,9 +948,7 @@ parse_request(struct json_connection *jcon, const jsmntok_t tok[]) rpc_hook->custom_replace = NULL; rpc_hook->custom_buffer = NULL; - db_begin_transaction(jcon->ld->wallet->db); completed = plugin_hook_call_rpc_command(jcon->ld, c->id, rpc_hook); - db_commit_transaction(jcon->ld->wallet->db); /* If it's deferred, mark it (otherwise, it's completed) */ if (!completed) @@ -1007,6 +1005,7 @@ static struct io_plan *read_json(struct io_conn *conn, struct json_connection *jcon) { bool complete; + bool in_transaction = false; if (jcon->len_read) log_io(jcon->log, LOG_IO_IN, NULL, "", @@ -1023,6 +1022,7 @@ static struct io_plan *read_json(struct io_conn *conn, return io_wait(conn, conn, read_json, jcon); } +again: if (!json_parse_input(&jcon->input_parser, &jcon->input_toks, jcon->buffer, jcon->used, &complete)) { @@ -1030,6 +1030,8 @@ static struct io_plan *read_json(struct io_conn *conn, jcon, "null", tal_fmt(tmpctx, "Invalid token in json input: '%s'", tal_strndup(tmpctx, jcon->buffer, jcon->used))); + if (in_transaction) + db_commit_transaction(jcon->ld->wallet->db); return io_halfclose(conn); } @@ -1046,6 +1048,10 @@ static struct io_plan *read_json(struct io_conn *conn, goto read_more; } + if (!in_transaction) { + db_begin_transaction(jcon->ld->wallet->db); + in_transaction = true; + } parse_request(jcon, jcon->input_toks); /* Remove first {}. */ @@ -1057,16 +1063,12 @@ static struct io_plan *read_json(struct io_conn *conn, jsmn_init(&jcon->input_parser); toks_reset(jcon->input_toks); - /* If we have more to process, try again. FIXME: this still gets - * first priority in io_loop, so can starve others. Hack would be - * a (non-zero) timer, but better would be to have io_loop avoid - * such livelock */ - if (jcon->used) { - jcon->len_read = 0; - return io_always(conn, read_json, jcon); - } + if (jcon->used) + goto again; read_more: + if (in_transaction) + db_commit_transaction(jcon->ld->wallet->db); return io_read_partial(conn, jcon->buffer + jcon->used, tal_count(jcon->buffer) - jcon->used, &jcon->len_read, read_json, jcon); From fa7d732ba6f6cbba035f8162df3ad32ec8cabbd9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:24:23 +0930 Subject: [PATCH 1460/1530] lightningd: allow a connection to specify db batching. Previous commit was a hack which *always* batched where possible, this is a more sophisticated opt-in varaint, with a timeout sanity check. Final performance for cleaning up 1M pays/forwards/invoices: ``` $ time l1-cli autoclean-once succeededpays 1 { "autoclean": { "succeededpays": { "cleaned": 1000000, "uncleaned": 26895 } } } real 6m9.828s user 0m0.003s sys 0m0.001s $ time l2-cli autoclean-once succeededforwards 1 { "autoclean": { "succeededforwards": { "cleaned": 1000000, "uncleaned": 40 } } } real 3m20.789s user 0m0.004s sys 0m0.001s $ time l3-cli autoclean-once paidinvoices 1 { "autoclean": { "paidinvoices": { "cleaned": 1000000, "uncleaned": 0 } } } real 6m47.941s user 0m0.001s sys 0m0.000s ``` Signed-off-by: Rusty Russell Changelog-Added: JSON-RPC: `batching` command to allow database transactions to cross multiple back-to-back JSON commands. --- doc/Makefile | 1 + doc/index.rst | 1 + doc/lightning-batching.7.md | 55 +++++++++++++++++++++++++++++++ doc/schemas/batching.request.json | 14 ++++++++ doc/schemas/batching.schema.json | 6 ++++ lightningd/jsonrpc.c | 53 ++++++++++++++++++++++++++--- plugins/autoclean.c | 3 ++ plugins/libplugin.c | 43 ++++++++++++++++++++---- plugins/libplugin.h | 2 ++ 9 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 doc/lightning-batching.7.md create mode 100644 doc/schemas/batching.request.json create mode 100644 doc/schemas/batching.schema.json diff --git a/doc/Makefile b/doc/Makefile index cb3b3d411efb..527b47bca268 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -9,6 +9,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightningd-config.5 \ doc/lightning-addgossip.7 \ doc/lightning-autoclean-status.7 \ + doc/lightning-batching.7 \ doc/lightning-bkpr-channelsapy.7 \ doc/lightning-bkpr-dumpincomecsv.7 \ doc/lightning-bkpr-inspect.7 \ diff --git a/doc/index.rst b/doc/index.rst index 13bdbb48a7a3..ad6b847f939b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -31,6 +31,7 @@ Core Lightning Documentation lightning-addgossip lightning-autoclean-status + lightning-batching lightning-bkpr-channelsapy lightning-bkpr-dumpincomecsv lightning-bkpr-inspect diff --git a/doc/lightning-batching.7.md b/doc/lightning-batching.7.md new file mode 100644 index 000000000000..d69d8b290130 --- /dev/null +++ b/doc/lightning-batching.7.md @@ -0,0 +1,55 @@ +lightning-batching -- Command to allow database batching. +========================================================= + +SYNOPSIS +-------- + +**batching** *enable* + +DESCRIPTION +----------- + +The **batching** RPC command allows (but does not guarantee!) database +commitments to be deferred when multiple commands are issued on this RPC +connection. This is only useful if many commands are being given at once, in +which case it can offer a performance improvement (the cost being that if +there is a crash, it's unclear how many of the commands will have been +persisted). + +*enable* is *true* to enable batching, *false* to disable it (the +default). + +EXAMPLE JSON REQUEST +-------------------- +```json +{ + "id": 82, + "method": "batching", + "params": { + "enable": true + } +} +``` + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an empty object is returned. + +[comment]: # (GENERATE-FROM-SCHEMA-END) + +On failure, one of the following error codes may be returned: + +- -32602: Error in given parameters. + +AUTHOR +------ + +Rusty Russell <> wrote the initial version of this man page. + +RESOURCES +--------- + +Main web site: +[comment]: # ( SHA256STAMP:326e5801f65998e13e909d8b682e9fbc9824f3a43aa7da1d76b871882e52f293) diff --git a/doc/schemas/batching.request.json b/doc/schemas/batching.request.json new file mode 100644 index 000000000000..03c794d4a92b --- /dev/null +++ b/doc/schemas/batching.request.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "enable" + ], + "properties": { + "enable": { + "type": "boolean", + "description": "Whether to enable or disable transaction batching" + } + } +} diff --git a/doc/schemas/batching.schema.json b/doc/schemas/batching.schema.json new file mode 100644 index 000000000000..1aad2dcae935 --- /dev/null +++ b/doc/schemas/batching.schema.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "properties": {} +} diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 474928409546..5c799e4eae5b 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -83,6 +83,9 @@ struct json_connection { /* Are notifications enabled? */ bool notifications_enabled; + /* Are we allowed to batch database commitments? */ + bool db_batching; + /* Our json_streams (owned by the commands themselves while running). * Since multiple streams could start returning data at once, we * always service these in order, freeing once empty. */ @@ -1006,6 +1009,7 @@ static struct io_plan *read_json(struct io_conn *conn, { bool complete; bool in_transaction = false; + struct timemono start_time = time_mono(); if (jcon->len_read) log_io(jcon->log, LOG_IO_IN, NULL, "", @@ -1063,8 +1067,24 @@ static struct io_plan *read_json(struct io_conn *conn, jsmn_init(&jcon->input_parser); toks_reset(jcon->input_toks); - if (jcon->used) + /* Do we have more already read? */ + if (jcon->used) { + if (!jcon->db_batching) { + db_commit_transaction(jcon->ld->wallet->db); + in_transaction = false; + } else { + /* FIXME: io_always() should interleave with + * real IO, and then we should rotate order we + * service fds in, to avoid starvation. */ + if (time_greater(timemono_between(time_mono(), + start_time), + time_from_msec(250))) { + db_commit_transaction(jcon->ld->wallet->db); + return io_always(conn, read_json, jcon); + } + } goto again; + } read_more: if (in_transaction) @@ -1090,6 +1110,7 @@ static struct io_plan *jcon_connected(struct io_conn *conn, jsmn_init(&jcon->input_parser); jcon->input_toks = toks_alloc(jcon); jcon->notifications_enabled = false; + jcon->db_batching = false; list_head_init(&jcon->commands); /* We want to log on destruction, so we free this in destructor. */ @@ -1436,7 +1457,6 @@ static const struct json_command check_command = { "Don't run {command_to_check}, just verify parameters.", .verbose = "check command_to_check [parameters...]\n" }; - AUTODATA(json_command, &check_command); static struct command_result *json_notifications(struct command *cmd, @@ -1461,7 +1481,32 @@ static const struct json_command notifications_command = { "notifications", "utility", json_notifications, - "Enable notifications for {level} (or 'false' to disable)", + "{enable} notifications", }; - AUTODATA(json_command, ¬ifications_command); + +static struct command_result *json_batching(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + bool *enable; + + if (!param(cmd, buffer, params, + p_req("enable", param_bool, &enable), + NULL)) + return command_param_failed(); + + /* Catch the case where they sent this command then hung up. */ + if (cmd->jcon) + cmd->jcon->db_batching = *enable; + return command_success(cmd, json_stream_success(cmd)); +} + +static const struct json_command batching_command = { + "batching", + "utility", + json_batching, + "Database transaction batching {enable}", +}; +AUTODATA(json_command, &batching_command); diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 8993e1078742..dcbb5a64d5b3 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -552,6 +552,9 @@ static const char *init(struct plugin *p, rpc_scan_datastore_str(plugin, datastore_path(tmpctx, i, "num"), JSON_SCAN(json_to_u64, &total_cleaned[i])); } + + /* Optimization FTW! */ + rpc_enable_batching(p); return NULL; } diff --git a/plugins/libplugin.c b/plugins/libplugin.c index 36d1877989aa..ef68530eb629 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -520,17 +520,16 @@ static const jsmntok_t *read_rpc_reply(const tal_t *ctx, return toks; } -static const char *rpc_scan_core(const tal_t *ctx, +/* Send request, return response, set resp/len to reponse */ +static const jsmntok_t *sync_req(const tal_t *ctx, struct plugin *plugin, const char *method, const struct json_out *params TAKES, - const char *guide, - va_list ap) + const char **resp) { bool error; const jsmntok_t *contents; int reqlen; - const char *p; struct json_out *jout = json_out_new(tmpctx); json_out_start(jout, NULL, '{'); @@ -542,12 +541,27 @@ static const char *rpc_scan_core(const tal_t *ctx, tal_free(params); finish_and_send_json(plugin->rpc_conn->fd, jout); - read_rpc_reply(tmpctx, plugin, &contents, &error, &reqlen); + read_rpc_reply(ctx, plugin, &contents, &error, &reqlen); if (error) plugin_err(plugin, "Got error reply to %s: '%.*s'", - method, reqlen, membuf_elems(&plugin->rpc_conn->mb)); + method, reqlen, membuf_elems(&plugin->rpc_conn->mb)); - p = membuf_consume(&plugin->rpc_conn->mb, reqlen); + *resp = membuf_consume(&plugin->rpc_conn->mb, reqlen); + return contents; +} + +/* Returns contents of scanning guide on 'result' */ +static const char *rpc_scan_core(const tal_t *ctx, + struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + va_list ap) +{ + const jsmntok_t *contents; + const char *p; + + contents = sync_req(tmpctx, plugin, method, params, &p); return json_scanv(ctx, p, contents, guide, ap); } @@ -631,6 +645,21 @@ bool rpc_scan_datastore_hex(struct plugin *plugin, return ret; } +void rpc_enable_batching(struct plugin *plugin) +{ + const char *p; + struct json_out *params; + + params = json_out_new(NULL); + json_out_start(params, NULL, '{'); + json_out_add(params, "enable", false, "true"); + json_out_end(params, '}'); + json_out_finished(params); + + /* We don't actually care about (empty) response */ + sync_req(tmpctx, plugin, "batching", take(params), &p); +} + static struct command_result *datastore_fail(struct command *command, const char *buf, const jsmntok_t *result, diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 4131f4567f79..3e9b0f29584c 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -283,6 +283,8 @@ bool rpc_scan_datastore_hex(struct plugin *plugin, const char *path, ...); +/* This sets batching of database commitments */ +void rpc_enable_batching(struct plugin *plugin); /* Send an async rpc request to lightningd. */ struct command_result *send_outreq(struct plugin *plugin, From 05095313f5c30a2fc20e1b188f78398c7a717863 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:24:26 +0930 Subject: [PATCH 1461/1530] lightningd: listsendpays always has groupid. Schema was too loose since we did deprecations. Signed-off-by: Rusty Russell --- cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 4 +- contrib/pyln-testing/pyln/testing/node_pb2.py | 332 +++++++++--------- doc/lightning-listsendpays.7.md | 4 +- doc/schemas/listsendpays.schema.json | 1 + 6 files changed, 173 insertions(+), 172 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 0dc01a80c157..fcbea785fa6c 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -708,7 +708,7 @@ message ListsendpaysPayments { COMPLETE = 2; } uint64 id = 1; - optional uint64 groupid = 2; + uint64 groupid = 2; bytes payment_hash = 3; ListsendpaysPaymentsStatus status = 4; optional Amount amount_msat = 5; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 2c28e896c542..e5545c85cab7 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -504,7 +504,7 @@ impl From<&responses::ListsendpaysPayments> for pb::ListsendpaysPayments { fn from(c: &responses::ListsendpaysPayments) -> Self { Self { id: c.id.clone(), // Rule #2 for type u64 - groupid: c.groupid.clone(), // Rule #2 for type u64? + groupid: c.groupid.clone(), // Rule #2 for type u64 payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 38d1cdbf65fd..dcad0497f0cb 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1910,8 +1910,8 @@ pub mod responses { pub struct ListsendpaysPayments { #[serde(alias = "id")] pub id: u64, - #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] - pub groupid: Option, + #[serde(alias = "groupid")] + pub groupid: u64, #[serde(alias = "payment_hash")] pub payment_hash: Sha256, // Path `ListSendPays.payments[].status` diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 93eec9bd1566..aa2f78030f3b 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xe5\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x04\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x05\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x08\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xca\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x12\n\nin_htlc_id\x18\n \x01(\x04\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xca\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x12\n\nin_htlc_id\x18\n \x01(\x04\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1232,169 +1232,169 @@ _LISTSENDPAYSRESPONSE._serialized_start=15070 _LISTSENDPAYSRESPONSE._serialized_end=15137 _LISTSENDPAYSPAYMENTS._serialized_start=15140 - _LISTSENDPAYSPAYMENTS._serialized_end=15753 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15558 - _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15625 - _LISTTRANSACTIONSREQUEST._serialized_start=15755 - _LISTTRANSACTIONSREQUEST._serialized_end=15780 - _LISTTRANSACTIONSRESPONSE._serialized_start=15782 - _LISTTRANSACTIONSRESPONSE._serialized_end=15865 - _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15868 - _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16150 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16153 - _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16669 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16365 - _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16643 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16672 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17216 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16911 - _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17190 - _PAYREQUEST._serialized_start=17219 - _PAYREQUEST._serialized_end=17691 - _PAYRESPONSE._serialized_start=17694 - _PAYRESPONSE._serialized_end=18073 - _PAYRESPONSE_PAYSTATUS._serialized_start=17976 - _PAYRESPONSE_PAYSTATUS._serialized_end=18026 - _LISTNODESREQUEST._serialized_start=18075 - _LISTNODESREQUEST._serialized_end=18117 - _LISTNODESRESPONSE._serialized_start=18119 - _LISTNODESRESPONSE._serialized_end=18174 - _LISTNODESNODES._serialized_start=18177 - _LISTNODESNODES._serialized_end=18402 - _LISTNODESNODESADDRESSES._serialized_start=18405 - _LISTNODESNODESADDRESSES._serialized_end=18652 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18545 - _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18640 - _WAITANYINVOICEREQUEST._serialized_start=18654 - _WAITANYINVOICEREQUEST._serialized_end=18757 - _WAITANYINVOICERESPONSE._serialized_start=18760 - _WAITANYINVOICERESPONSE._serialized_end=19291 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19136 - _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19181 - _WAITINVOICEREQUEST._serialized_start=19293 - _WAITINVOICEREQUEST._serialized_end=19328 - _WAITINVOICERESPONSE._serialized_start=19331 - _WAITINVOICERESPONSE._serialized_end=19850 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19698 - _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19740 - _WAITSENDPAYREQUEST._serialized_start=19853 - _WAITSENDPAYREQUEST._serialized_end=19995 - _WAITSENDPAYRESPONSE._serialized_start=19998 - _WAITSENDPAYRESPONSE._serialized_end=20560 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20402 - _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20435 - _NEWADDRREQUEST._serialized_start=20563 - _NEWADDRREQUEST._serialized_end=20721 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20647 - _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20705 - _NEWADDRRESPONSE._serialized_start=20723 - _NEWADDRRESPONSE._serialized_end=20814 - _WITHDRAWREQUEST._serialized_start=20817 - _WITHDRAWREQUEST._serialized_end=21019 - _WITHDRAWRESPONSE._serialized_start=21021 - _WITHDRAWRESPONSE._serialized_end=21079 - _KEYSENDREQUEST._serialized_start=21082 - _KEYSENDREQUEST._serialized_end=21414 - _KEYSENDRESPONSE._serialized_start=21417 - _KEYSENDRESPONSE._serialized_end=21787 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21711 - _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21740 - _KEYSENDEXTRATLVS._serialized_start=21789 - _KEYSENDEXTRATLVS._serialized_end=21807 - _FUNDPSBTREQUEST._serialized_start=21810 - _FUNDPSBTREQUEST._serialized_end=22121 - _FUNDPSBTRESPONSE._serialized_start=22124 - _FUNDPSBTRESPONSE._serialized_end=22341 - _FUNDPSBTRESERVATIONS._serialized_start=22343 - _FUNDPSBTRESERVATIONS._serialized_end=22460 - _SENDPSBTREQUEST._serialized_start=22462 - _SENDPSBTREQUEST._serialized_end=22527 - _SENDPSBTRESPONSE._serialized_start=22529 - _SENDPSBTRESPONSE._serialized_end=22573 - _SIGNPSBTREQUEST._serialized_start=22575 - _SIGNPSBTREQUEST._serialized_end=22624 - _SIGNPSBTRESPONSE._serialized_start=22626 - _SIGNPSBTRESPONSE._serialized_end=22665 - _UTXOPSBTREQUEST._serialized_start=22668 - _UTXOPSBTREQUEST._serialized_end=23015 - _UTXOPSBTRESPONSE._serialized_start=23018 - _UTXOPSBTRESPONSE._serialized_end=23235 - _UTXOPSBTRESERVATIONS._serialized_start=23237 - _UTXOPSBTRESERVATIONS._serialized_end=23354 - _TXDISCARDREQUEST._serialized_start=23356 - _TXDISCARDREQUEST._serialized_end=23388 - _TXDISCARDRESPONSE._serialized_start=23390 - _TXDISCARDRESPONSE._serialized_end=23444 - _TXPREPAREREQUEST._serialized_start=23447 - _TXPREPAREREQUEST._serialized_end=23611 - _TXPREPARERESPONSE._serialized_start=23613 - _TXPREPARERESPONSE._serialized_end=23681 - _TXSENDREQUEST._serialized_start=23683 - _TXSENDREQUEST._serialized_end=23712 - _TXSENDRESPONSE._serialized_start=23714 - _TXSENDRESPONSE._serialized_end=23770 - _DISCONNECTREQUEST._serialized_start=23772 - _DISCONNECTREQUEST._serialized_end=23833 - _DISCONNECTRESPONSE._serialized_start=23835 - _DISCONNECTRESPONSE._serialized_end=23855 - _FEERATESREQUEST._serialized_start=23857 - _FEERATESREQUEST._serialized_end=23964 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23927 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23964 - _FEERATESRESPONSE._serialized_start=23966 - _FEERATESRESPONSE._serialized_end=24052 - _FEERATESPERKB._serialized_start=24055 - _FEERATESPERKB._serialized_end=24378 - _FEERATESPERKW._serialized_start=24381 - _FEERATESPERKW._serialized_end=24704 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24707 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24900 - _FUNDCHANNELREQUEST._serialized_start=24903 - _FUNDCHANNELREQUEST._serialized_end=25388 - _FUNDCHANNELRESPONSE._serialized_start=25391 - _FUNDCHANNELRESPONSE._serialized_end=25546 - _GETROUTEREQUEST._serialized_start=25549 - _GETROUTEREQUEST._serialized_end=25785 - _GETROUTERESPONSE._serialized_start=25787 - _GETROUTERESPONSE._serialized_end=25840 - _GETROUTEROUTE._serialized_start=25843 - _GETROUTEROUTE._serialized_end=26040 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=26011 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=26040 - _LISTFORWARDSREQUEST._serialized_start=26043 - _LISTFORWARDSREQUEST._serialized_end=26301 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26183 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26259 - _LISTFORWARDSRESPONSE._serialized_start=26303 - _LISTFORWARDSRESPONSE._serialized_end=26370 - _LISTFORWARDSFORWARDS._serialized_start=26373 - _LISTFORWARDSFORWARDS._serialized_end=26959 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26757 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26841 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26843 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26891 - _LISTPAYSREQUEST._serialized_start=26962 - _LISTPAYSREQUEST._serialized_end=27181 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27087 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27142 - _LISTPAYSRESPONSE._serialized_start=27183 - _LISTPAYSRESPONSE._serialized_end=27234 - _LISTPAYSPAYS._serialized_start=27237 - _LISTPAYSPAYS._serialized_end=27756 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27568 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27627 - _PINGREQUEST._serialized_start=27758 - _PINGREQUEST._serialized_end=27847 - _PINGRESPONSE._serialized_start=27849 - _PINGRESPONSE._serialized_end=27879 - _SIGNMESSAGEREQUEST._serialized_start=27881 - _SIGNMESSAGEREQUEST._serialized_end=27918 - _SIGNMESSAGERESPONSE._serialized_start=27920 - _SIGNMESSAGERESPONSE._serialized_end=27990 - _STOPREQUEST._serialized_start=27992 - _STOPREQUEST._serialized_end=28005 - _STOPRESPONSE._serialized_start=28007 - _STOPRESPONSE._serialized_end=28021 - _NODE._serialized_start=28024 - _NODE._serialized_end=30952 + _LISTSENDPAYSPAYMENTS._serialized_end=15736 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_start=15553 + _LISTSENDPAYSPAYMENTS_LISTSENDPAYSPAYMENTSSTATUS._serialized_end=15620 + _LISTTRANSACTIONSREQUEST._serialized_start=15738 + _LISTTRANSACTIONSREQUEST._serialized_end=15763 + _LISTTRANSACTIONSRESPONSE._serialized_start=15765 + _LISTTRANSACTIONSRESPONSE._serialized_end=15848 + _LISTTRANSACTIONSTRANSACTIONS._serialized_start=15851 + _LISTTRANSACTIONSTRANSACTIONS._serialized_end=16133 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_start=16136 + _LISTTRANSACTIONSTRANSACTIONSINPUTS._serialized_end=16652 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_start=16348 + _LISTTRANSACTIONSTRANSACTIONSINPUTS_LISTTRANSACTIONSTRANSACTIONSINPUTSTYPE._serialized_end=16626 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_start=16655 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS._serialized_end=17199 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_start=16894 + _LISTTRANSACTIONSTRANSACTIONSOUTPUTS_LISTTRANSACTIONSTRANSACTIONSOUTPUTSTYPE._serialized_end=17173 + _PAYREQUEST._serialized_start=17202 + _PAYREQUEST._serialized_end=17674 + _PAYRESPONSE._serialized_start=17677 + _PAYRESPONSE._serialized_end=18056 + _PAYRESPONSE_PAYSTATUS._serialized_start=17959 + _PAYRESPONSE_PAYSTATUS._serialized_end=18009 + _LISTNODESREQUEST._serialized_start=18058 + _LISTNODESREQUEST._serialized_end=18100 + _LISTNODESRESPONSE._serialized_start=18102 + _LISTNODESRESPONSE._serialized_end=18157 + _LISTNODESNODES._serialized_start=18160 + _LISTNODESNODES._serialized_end=18385 + _LISTNODESNODESADDRESSES._serialized_start=18388 + _LISTNODESNODESADDRESSES._serialized_end=18635 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_start=18528 + _LISTNODESNODESADDRESSES_LISTNODESNODESADDRESSESTYPE._serialized_end=18623 + _WAITANYINVOICEREQUEST._serialized_start=18637 + _WAITANYINVOICEREQUEST._serialized_end=18740 + _WAITANYINVOICERESPONSE._serialized_start=18743 + _WAITANYINVOICERESPONSE._serialized_end=19274 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_start=19119 + _WAITANYINVOICERESPONSE_WAITANYINVOICESTATUS._serialized_end=19164 + _WAITINVOICEREQUEST._serialized_start=19276 + _WAITINVOICEREQUEST._serialized_end=19311 + _WAITINVOICERESPONSE._serialized_start=19314 + _WAITINVOICERESPONSE._serialized_end=19833 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_start=19681 + _WAITINVOICERESPONSE_WAITINVOICESTATUS._serialized_end=19723 + _WAITSENDPAYREQUEST._serialized_start=19836 + _WAITSENDPAYREQUEST._serialized_end=19978 + _WAITSENDPAYRESPONSE._serialized_start=19981 + _WAITSENDPAYRESPONSE._serialized_end=20543 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_start=20385 + _WAITSENDPAYRESPONSE_WAITSENDPAYSTATUS._serialized_end=20418 + _NEWADDRREQUEST._serialized_start=20546 + _NEWADDRREQUEST._serialized_end=20704 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_start=20630 + _NEWADDRREQUEST_NEWADDRADDRESSTYPE._serialized_end=20688 + _NEWADDRRESPONSE._serialized_start=20706 + _NEWADDRRESPONSE._serialized_end=20797 + _WITHDRAWREQUEST._serialized_start=20800 + _WITHDRAWREQUEST._serialized_end=21002 + _WITHDRAWRESPONSE._serialized_start=21004 + _WITHDRAWRESPONSE._serialized_end=21062 + _KEYSENDREQUEST._serialized_start=21065 + _KEYSENDREQUEST._serialized_end=21397 + _KEYSENDRESPONSE._serialized_start=21400 + _KEYSENDRESPONSE._serialized_end=21770 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_start=21694 + _KEYSENDRESPONSE_KEYSENDSTATUS._serialized_end=21723 + _KEYSENDEXTRATLVS._serialized_start=21772 + _KEYSENDEXTRATLVS._serialized_end=21790 + _FUNDPSBTREQUEST._serialized_start=21793 + _FUNDPSBTREQUEST._serialized_end=22104 + _FUNDPSBTRESPONSE._serialized_start=22107 + _FUNDPSBTRESPONSE._serialized_end=22324 + _FUNDPSBTRESERVATIONS._serialized_start=22326 + _FUNDPSBTRESERVATIONS._serialized_end=22443 + _SENDPSBTREQUEST._serialized_start=22445 + _SENDPSBTREQUEST._serialized_end=22510 + _SENDPSBTRESPONSE._serialized_start=22512 + _SENDPSBTRESPONSE._serialized_end=22556 + _SIGNPSBTREQUEST._serialized_start=22558 + _SIGNPSBTREQUEST._serialized_end=22607 + _SIGNPSBTRESPONSE._serialized_start=22609 + _SIGNPSBTRESPONSE._serialized_end=22648 + _UTXOPSBTREQUEST._serialized_start=22651 + _UTXOPSBTREQUEST._serialized_end=22998 + _UTXOPSBTRESPONSE._serialized_start=23001 + _UTXOPSBTRESPONSE._serialized_end=23218 + _UTXOPSBTRESERVATIONS._serialized_start=23220 + _UTXOPSBTRESERVATIONS._serialized_end=23337 + _TXDISCARDREQUEST._serialized_start=23339 + _TXDISCARDREQUEST._serialized_end=23371 + _TXDISCARDRESPONSE._serialized_start=23373 + _TXDISCARDRESPONSE._serialized_end=23427 + _TXPREPAREREQUEST._serialized_start=23430 + _TXPREPAREREQUEST._serialized_end=23594 + _TXPREPARERESPONSE._serialized_start=23596 + _TXPREPARERESPONSE._serialized_end=23664 + _TXSENDREQUEST._serialized_start=23666 + _TXSENDREQUEST._serialized_end=23695 + _TXSENDRESPONSE._serialized_start=23697 + _TXSENDRESPONSE._serialized_end=23753 + _DISCONNECTREQUEST._serialized_start=23755 + _DISCONNECTREQUEST._serialized_end=23816 + _DISCONNECTRESPONSE._serialized_start=23818 + _DISCONNECTRESPONSE._serialized_end=23838 + _FEERATESREQUEST._serialized_start=23840 + _FEERATESREQUEST._serialized_end=23947 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23910 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23947 + _FEERATESRESPONSE._serialized_start=23949 + _FEERATESRESPONSE._serialized_end=24035 + _FEERATESPERKB._serialized_start=24038 + _FEERATESPERKB._serialized_end=24361 + _FEERATESPERKW._serialized_start=24364 + _FEERATESPERKW._serialized_end=24687 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24690 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24883 + _FUNDCHANNELREQUEST._serialized_start=24886 + _FUNDCHANNELREQUEST._serialized_end=25371 + _FUNDCHANNELRESPONSE._serialized_start=25374 + _FUNDCHANNELRESPONSE._serialized_end=25529 + _GETROUTEREQUEST._serialized_start=25532 + _GETROUTEREQUEST._serialized_end=25768 + _GETROUTERESPONSE._serialized_start=25770 + _GETROUTERESPONSE._serialized_end=25823 + _GETROUTEROUTE._serialized_start=25826 + _GETROUTEROUTE._serialized_end=26023 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25994 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=26023 + _LISTFORWARDSREQUEST._serialized_start=26026 + _LISTFORWARDSREQUEST._serialized_end=26284 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26166 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26242 + _LISTFORWARDSRESPONSE._serialized_start=26286 + _LISTFORWARDSRESPONSE._serialized_end=26353 + _LISTFORWARDSFORWARDS._serialized_start=26356 + _LISTFORWARDSFORWARDS._serialized_end=26942 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26740 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26824 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26826 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26874 + _LISTPAYSREQUEST._serialized_start=26945 + _LISTPAYSREQUEST._serialized_end=27164 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27070 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27125 + _LISTPAYSRESPONSE._serialized_start=27166 + _LISTPAYSRESPONSE._serialized_end=27217 + _LISTPAYSPAYS._serialized_start=27220 + _LISTPAYSPAYS._serialized_end=27739 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27551 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27610 + _PINGREQUEST._serialized_start=27741 + _PINGREQUEST._serialized_end=27830 + _PINGRESPONSE._serialized_start=27832 + _PINGRESPONSE._serialized_end=27862 + _SIGNMESSAGEREQUEST._serialized_start=27864 + _SIGNMESSAGEREQUEST._serialized_end=27901 + _SIGNMESSAGERESPONSE._serialized_start=27903 + _SIGNMESSAGERESPONSE._serialized_end=27973 + _STOPREQUEST._serialized_start=27975 + _STOPREQUEST._serialized_end=27988 + _STOPRESPONSE._serialized_start=27990 + _STOPRESPONSE._serialized_end=28004 + _NODE._serialized_start=28007 + _NODE._serialized_end=30935 # @@protoc_insertion_point(module_scope) diff --git a/doc/lightning-listsendpays.7.md b/doc/lightning-listsendpays.7.md index e2eee527ae48..fb575eb59a55 100644 --- a/doc/lightning-listsendpays.7.md +++ b/doc/lightning-listsendpays.7.md @@ -26,11 +26,11 @@ Note that the returned array is ordered by increasing *id*. 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 +- **groupid** (u64): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash - **payment\_hash** (hash): 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 - **amount\_sent\_msat** (msat): The amount sent -- **groupid** (u64, optional): Grouping key to disambiguate multiple attempts to pay an invoice or the same payment_hash - **amount\_msat** (msat, optional): The amount delivered to destination (if known) - **destination** (pubkey, optional): the final destination of the payment if known - **label** (string, optional): the label, if given to sendpay @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:68af9f1edf2ddc78a7daad1bb72aa773b82c5103b0ba706ef83de36a11cabe26) +[comment]: # ( SHA256STAMP:eddbf227775b367fbea5d90dfc1d06bc87b9301e4b862b0d755592432ef58f89) diff --git a/doc/schemas/listsendpays.schema.json b/doc/schemas/listsendpays.schema.json index dc43fde7f38a..3c57246e2776 100644 --- a/doc/schemas/listsendpays.schema.json +++ b/doc/schemas/listsendpays.schema.json @@ -15,6 +15,7 @@ "id", "payment_hash", "status", + "groupid", "created_at", "amount_sent_msat" ], From f52ff07558709bd1f7ed0cdca65c891d80b1a785 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:24:26 +0930 Subject: [PATCH 1462/1530] lightningd: allow delpay to delete a specific payment. This is actually what the autoclean plugin wants, especially since you can't otherwise delete a payment which has failed then succeeded. But insist on neither or both being specified, at least for now. Changelog-Added: JSON-RPC: `delpay` takes optional `groupid` and `partid` parameters to specify exactly what payment to delete. Signed-off-by: Rusty Russell --- doc/lightning-delpay.7.md | 7 ++-- doc/schemas/delpay.request.json | 30 ++++++++++++++++ lightningd/pay.c | 62 ++++++++++++++++++++++++++------- plugins/autoclean.c | 9 +++++ tests/test_pay.py | 4 +++ wallet/wallet.c | 24 ++++++++++--- wallet/wallet.h | 12 ++++--- 7 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 doc/schemas/delpay.request.json diff --git a/doc/lightning-delpay.7.md b/doc/lightning-delpay.7.md index 55c182c42d29..4568ebfbb9c0 100644 --- a/doc/lightning-delpay.7.md +++ b/doc/lightning-delpay.7.md @@ -4,15 +4,18 @@ lightning-delpay -- Command for removing a completed or failed payment SYNOPSIS -------- -**delpay** *payment\_hash* *status* +**delpay** *payment\_hash* *status* [*partid* *groupid*] DESCRIPTION ----------- -The **delpay** RPC command deletes a payment with the given `payment_hash` if its status is either `complete` or `failed`. Deleting a `pending` payment is an error. +The **delpay** RPC command deletes a payment with the given `payment_hash` if its status is either `complete` or `failed`. Deleting a `pending` payment is an error. If *partid* and *groupid* are not specified, all payment parts are deleted. - *payment\_hash*: The unique identifier of a payment. - *status*: Expected status of the payment. +- *partid*: Specific partid to delete (must be paired with *groupid*) +- *groupid*: Specific groupid to delete (must be paired with *partid*) + Only deletes if the payment status matches. EXAMPLE JSON REQUEST diff --git a/doc/schemas/delpay.request.json b/doc/schemas/delpay.request.json new file mode 100644 index 000000000000..0b341c3041ec --- /dev/null +++ b/doc/schemas/delpay.request.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "required": [ + "payment_hash", + "status" + ], + "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": [ + "complete", + "failed" + ] + }, + "partid": { + "type": "u64" + }, + "groupid": { + "type": "u64" + } + } +} diff --git a/lightningd/pay.c b/lightningd/pay.c index 5e6d37ffae75..07b2d09820fd 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1633,6 +1633,29 @@ static const struct json_command listsendpays_command = { }; AUTODATA(json_command, &listsendpays_command); +static struct command_result * +param_payment_status_nopending(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + enum wallet_payment_status **status) +{ + struct command_result *res; + + res = param_payment_status(cmd, name, buffer, tok, status); + if (res) + return res; + + switch (**status) { + case PAYMENT_COMPLETE: + case PAYMENT_FAILED: + break; + case PAYMENT_PENDING: + return command_fail_badparam(cmd, name, buffer, tok, + "Cannot delete pending status"); + } + return NULL; +} static struct command_result *json_delpay(struct command *cmd, const char *buffer, @@ -1643,21 +1666,20 @@ static struct command_result *json_delpay(struct command *cmd, const struct wallet_payment **payments; enum wallet_payment_status *status; struct sha256 *payment_hash; + u64 *groupid, *partid; + bool found; if (!param(cmd, buffer, params, - p_req("payment_hash", param_sha256, &payment_hash), - p_req("status", param_payment_status, &status), - NULL)) + p_req("payment_hash", param_sha256, &payment_hash), + p_req("status", param_payment_status_nopending, &status), + p_opt("partid", param_u64, &partid), + p_opt("groupid", param_u64, &groupid), + NULL)) return command_param_failed(); - switch (*status) { - case PAYMENT_COMPLETE: - case PAYMENT_FAILED: - break; - case PAYMENT_PENDING: - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid status: %s", - payment_status_to_string(*status)); - } + if ((partid != NULL) != (groupid != NULL)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Must set both partid and groupid, or neither"); payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash); @@ -1665,7 +1687,14 @@ static struct command_result *json_delpay(struct command *cmd, return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s", type_to_string(tmpctx, struct sha256, payment_hash)); + found = false; for (int i = 0; i < tal_count(payments); i++) { + if (groupid && payments[i]->groupid != *groupid) + continue; + if (partid && payments[i]->partid != *partid) + continue; + + found = true; if (payments[i]->status != *status) { return command_fail(cmd, PAY_STATUS_UNEXPECTED, "Payment with hash %s has %s status but it should be %s", type_to_string(tmpctx, struct sha256, payment_hash), @@ -1674,11 +1703,20 @@ static struct command_result *json_delpay(struct command *cmd, } } - wallet_payment_delete_by_hash(cmd->ld->wallet, payment_hash); + if (!found) { + return command_fail(cmd, PAY_NO_SUCH_PAYMENT, + "No payment for that payment_hash with that partid and groupid"); + } + + wallet_payment_delete(cmd->ld->wallet, payment_hash, partid, groupid); response = json_stream_success(cmd); json_array_start(response, "payments"); for (int i = 0; i < tal_count(payments); i++) { + if (groupid && payments[i]->groupid != *groupid) + continue; + if (partid && payments[i]->partid != *partid) + continue; json_object_start(response, NULL); json_add_payment_fields(response, payments[i]); json_object_end(response); diff --git a/plugins/autoclean.c b/plugins/autoclean.c index dcbb5a64d5b3..1f3de4e8cc02 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -271,10 +271,19 @@ static struct command_result *listsendpays_done(struct command *cmd, if (paytime <= now - cinfo->subsystem_age[subsys]) { struct out_req *req; const jsmntok_t *phash = json_get_member(buf, t, "payment_hash"); + const jsmntok_t *groupid = json_get_member(buf, t, "groupid"); + const jsmntok_t *partidtok = json_get_member(buf, t, "partid"); + u64 partid; + if (partidtok) + json_to_u64(buf, partidtok, &partid); + else + partid = 0; req = del_request_start("delpay", cinfo, subsys); json_add_tok(req->js, "payment_hash", phash, buf); json_add_tok(req->js, "status", status, buf); + json_add_tok(req->js, "groupid", groupid, buf); + json_add_u64(req->js, "partid", partid); send_outreq(plugin, req); } } diff --git a/tests/test_pay.py b/tests/test_pay.py index dba8e6eb989d..465e0bedf1b8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -4009,8 +4009,10 @@ def test_delpay_argument_invalid(node_factory, bitcoind): # Create the line graph l2 -> l1 with a channel of 10 ** 5 sat! l2, l1 = node_factory.line_graph(2, fundamount=10**5, wait_for_announce=True) + l2.rpc.check_request_schemas = False with pytest.raises(RpcError): l2.rpc.delpay() + l2.rpc.check_request_schemas = True # sanity check inv = l1.rpc.invoice(10 ** 5, 'inv', 'inv') @@ -4025,11 +4027,13 @@ def test_delpay_argument_invalid(node_factory, bitcoind): payment_hash = inv['payment_hash'] # payment paid with wrong status (pending status is a illegal input) + l2.rpc.check_request_schemas = False with pytest.raises(RpcError): l2.rpc.delpay(payment_hash, 'pending') with pytest.raises(RpcError): l2.rpc.delpay(payment_hash, 'invalid_status') + l2.rpc.check_request_schemas = True with pytest.raises(RpcError): l2.rpc.delpay(payment_hash, 'failed') diff --git a/wallet/wallet.c b/wallet/wallet.c index 8458f42bca98..a5f6b81a6f57 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -3163,13 +3163,27 @@ u64 wallet_payment_get_groupid(struct wallet *wallet, return groupid; } -void wallet_payment_delete_by_hash(struct wallet *wallet, - const struct sha256 *payment_hash) +void wallet_payment_delete(struct wallet *wallet, + const struct sha256 *payment_hash, + const u64 *groupid, + const u64 *partid) { struct db_stmt *stmt; - stmt = db_prepare_v2( - wallet->db, SQL("DELETE FROM payments WHERE payment_hash = ?")); - + if (groupid) { + assert(partid); + stmt = db_prepare_v2(wallet->db, + SQL("DELETE FROM payments" + " WHERE payment_hash = ?" + " AND groupid = ?" + " AND partid = ?")); + db_bind_u64(stmt, 1, *groupid); + db_bind_u64(stmt, 2, *partid); + } else { + assert(!partid); + stmt = db_prepare_v2(wallet->db, + SQL("DELETE FROM payments" + " WHERE payment_hash = ?")); + } db_bind_sha256(stmt, 0, payment_hash); db_exec_prepared_v2(take(stmt)); } diff --git a/wallet/wallet.h b/wallet/wallet.h index d5315eb1e514..917d7f2aa9f9 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1089,12 +1089,16 @@ void wallet_payment_store(struct wallet *wallet, struct wallet_payment *payment TAKES); /** - * wallet_payment_delete_by_hash - Remove a payment + * wallet_payment_delete - Remove a payment * - * Removes the payment from the database by hash; if it is a MPP payment - * it remove all parts with a single query. + * Removes the payment from the database by hash; groupid and partid + * may both be NULL to delete all entries, otherwise deletes only that + * group/partid. */ -void wallet_payment_delete_by_hash(struct wallet *wallet, const struct sha256 *payment_hash); +void wallet_payment_delete(struct wallet *wallet, + const struct sha256 *payment_hash, + const u64 *groupid, + const u64 *partid); /** * wallet_local_htlc_out_delete - Remove a local outgoing failed HTLC From 939a7b2b1881a0658ee9f9711cf6b808aedc9f29 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:24:26 +0930 Subject: [PATCH 1463/1530] db/postgres: avoid memleak. ``` "label": "db/db_postgres.c:294:char[]", "backtrace": [ "ccan/ccan/tal/tal.c:442 (tal_alloc_)", "ccan/ccan/tal/tal.c:471 (tal_alloc_arr_)", "ccan/ccan/tal/str/str.c:91 (tal_vfmt_)", "ccan/ccan/tal/str/str.c:44 (tal_fmt_)", "db/db_postgres.c:294 (db_postgres_delete_columns)", "wallet/db.c:1546 (migrate_payments_scids_as_integers)", "wallet/db.c:986 (db_migrate)", "wallet/db.c:1019 (db_setup)", "wallet/wallet.c:101 (wallet_new)", "lightningd/lightningd.c:1023 (main)", "../csu/libc-start.c:308 (__libc_start_main)" ], "parents": [ "db/utils.c:317:struct db", "lightningd/lightningd.c:107:struct lightningd" ] ``` Signed-off-by: Rusty Russell --- db/db_postgres.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/db_postgres.c b/db/db_postgres.c index 19ff4d52ccc8..0d3eb7f0bd2a 100644 --- a/db/db_postgres.c +++ b/db/db_postgres.c @@ -291,7 +291,7 @@ static bool db_postgres_delete_columns(struct db *db, { char *cmd; - cmd = tal_fmt(db, "ALTER TABLE %s ", tablename); + cmd = tal_fmt(tmpctx, "ALTER TABLE %s ", tablename); for (size_t i = 0; i < num_cols; i++) { if (i != 0) tal_append_fmt(&cmd, ", "); From e0218841c26015322b6a223894d8b2742af26b4d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 19 Sep 2022 10:24:26 +0930 Subject: [PATCH 1464/1530] db: set now-unused channels.short_channel_id text column to NULL after migration Suggested-by: @cdecker Signed-off-by: Rusty Russell --- wallet/db.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wallet/db.c b/wallet/db.c index a634bfea235d..9c56412d660f 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1508,6 +1508,11 @@ static void migrate_channels_scids_as_integers(struct lightningd *ld, * (and sqlite3 has them referencing a now-deleted table!). * When we can assume sqlite3 2021-04-19 (3.35.5), we can * simply use DROP COLUMN (yay!) */ + + /* So null-out the unused column, at least! */ + stmt = db_prepare_v2(db, SQL("UPDATE channels" + " SET short_channel_id = NULL;")); + db_exec_prepared_v2(take(stmt)); } static void migrate_payments_scids_as_integers(struct lightningd *ld, From 651753bbd57b4f02769f102aa3531271d90eb63a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 22 Sep 2022 13:11:15 +0930 Subject: [PATCH 1465/1530] pytest: slow down test_autoclean. CI is really slow: it sees all three expire at once. But making the timeouts too long is painful in non-VALGRIND, so I ended up making it conditional. ``` # First it expires. wait_for(lambda: only_one(l3.rpc.listinvoices('inv1')['invoices'])['status'] == 'expired') # Now will get autocleaned wait_for(lambda: l3.rpc.listinvoices('inv1')['invoices'] == []) > assert l3.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1 E assert 3 == 1 tests/test_plugin.py:2975: AssertionError ``` Signed-off-by: Rusty Russell --- tests/test_plugin.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index bb19f561a764..a4a488608fed 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2949,10 +2949,19 @@ def test_autoclean(node_factory): 'may_reconnect': True}, wait_for_announce=True) + # Under valgrind in CI, it can 50 seconds between creating invoice + # and restarting. + if node_factory.valgrind: + short_timeout = 10 + longer_timeout = 60 + else: + short_timeout = 5 + longer_timeout = 20 + assert l3.rpc.autoclean_status('expiredinvoices')['autoclean']['expiredinvoices']['enabled'] is False - l3.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=5) - l3.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20) - l3.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20) + l3.rpc.invoice(amount_msat=12300, label='inv1', description='description1', expiry=short_timeout) + l3.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=longer_timeout) + l3.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=longer_timeout) inv4 = l3.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000) inv5 = l3.rpc.invoice(amount_msat=12300, label='inv5', description='description5', expiry=2000) @@ -2990,11 +2999,14 @@ def test_autoclean(node_factory): # Same with inv2/3 wait_for(lambda: only_one(l3.rpc.listinvoices('inv2')['invoices'])['status'] == 'expired') + wait_for(lambda: only_one(l3.rpc.listinvoices('inv3')['invoices'])['status'] == 'expired') - # Give it time to notice. + # Give it time to notice (runs every 10 seconds, give it 15) time.sleep(15) + # They're still there! assert l3.rpc.listinvoices('inv2')['invoices'] != [] + assert l3.rpc.listinvoices('inv3')['invoices'] != [] # Restart keeps it disabled. l3.restart() From c04de577abfa5bd4e2acfab0dd4f24c7373e22ae Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 22 Sep 2022 13:02:16 +0930 Subject: [PATCH 1466/1530] pyln-spec: update Makefile to use poetry for release, generate versions 1. Update default specdir to the new modern name. 2. Don't use python to extract version, use sed. 3. Use poetry publish. Signed-off-by: Rusty Russell --- contrib/pyln-spec/Makefile | 45 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/contrib/pyln-spec/Makefile b/contrib/pyln-spec/Makefile index 5ed10647ca2e..14ad23aa7f0b 100755 --- a/contrib/pyln-spec/Makefile +++ b/contrib/pyln-spec/Makefile @@ -1,6 +1,6 @@ #! /usr/bin/make -SPECDIR := ../../../lightning-rfc +SPECDIR := ../../../bolts # This gives us something like 'v1.0-137-gae2d248b7ad8b0965f224c303019ba04c661008f' GITDESCRIBE := $(shell git -C $(SPECDIR) describe --abbrev=40) # -> 1.0 @@ -31,12 +31,14 @@ check-source-mypy-%: cd $* && mypy --ignore-missing-imports `find * -name '*.py'` # Given a bolt number and a variable, get the value from inside the package. -extract = $(shell python3 -c 'from pyln.spec import bolt$1 as bolt;print(bolt.$2)') -# Get the version for this bolt -version = $(call extract,$1,__version__) +extract = $(shell cat bolt$1/pyln/spec/bolt$1/gen_*version.py | sed -n 's/^$2 = \"\(.*\)\"/\1/p') -# Given a direc the csv version for this bolt. +# Get the version for this bolt +base_version = $(call extract,$1,__base_version__) csv_version = $(call extract,$1,__csv_version__) +post_version = $(call extract,$1,__post_version__) + +version = $(call base_version,$1).$(call csv_version,$1).$(call post_version,$1) # Given a bolt number, get the current version. sdistfiles = $(foreach b,$(BOLTS),bolt$b/dist/pyln-bolt$b-$(call version,$b).tar.gz) @@ -50,26 +52,27 @@ bdistfiles = $(foreach b,$(BOLTS),bolt$b/dist/pyln_bolt$b-$(call version,$b)-py3 ARTEFACTS := $(foreach b,$(BOLTS),$(call bdistfiles,$b) $(call sdistfiles,$b)) -test-release-bolt%: $(ARTEFACTS) - python3 -m twine upload --repository testpypi --skip-existing $(call bdistfiles,$*) $(call sdistfiles,$*) +test-release-%: + cd bolt$b && poetry publish --repository testpypi + +test-release: $(BOLTS:%=prod-release-%) - # Create a test virtualenv, install from the testpypi and run the - # tests against it (make sure not to use any virtualenv that may have - # pyln-proto already installed). - virtualenv testpypi-$* --python=/usr/bin/python3 --download --always-copy --clear - # Install the requirements from the prod repo, they are not being kept up to date on the test repo - testpypi-$*/bin/python3 -m pip install -r requirements.txt pytest pytest-timeout - testpypi-$*/bin/python3 -m pip install -I --index-url https://test.pypi.org/simple/ --no-deps pyln-bolt$* - testpypi-$*/bin/python3 -c "from pyln.spec import bolt$* as bolt;assert(bolt.__version__ == '$(call version,$*)')" - testpypi-$*/bin/pytest bolt$*/tests - rm -rf testpypi-$* +prod-release-%: + cd bolt$b && poetry publish -test-release: check $(foreach b,$(BOLTS),test-release-bolt$b) +prod-release: $(BOLTS:%=prod-release-%) -prod-release: test-release $(ARTEFACTS) - python3 -m twine upload $(ARTEFACTS) +# Pattern rules don't work reliably with multiple % in prereqs! +refresh-1: bolt1/pyln/spec/bolt1/gen_csv_version.py bolt1/pyln/spec/bolt1/gen_version.py + cd bolt1 && poetry version $(call version,1) +refresh-2: bolt2/pyln/spec/bolt2/gen_csv_version.py bolt2/pyln/spec/bolt2/gen_version.py + cd bolt2 && poetry version $(call version,2) +refresh-4: bolt4/pyln/spec/bolt4/gen_csv_version.py bolt4/pyln/spec/bolt4/gen_version.py + cd bolt4 && poetry version $(call version,4) +refresh-7: bolt7/pyln/spec/bolt7/gen_csv_version.py bolt7/pyln/spec/bolt7/gen_version.py + cd bolt7 && poetry version $(call version,7) -refresh: $(CODE_DIRS:%=%/gen_csv_version.py) $(CODE_DIRS:%=%/gen_version.py) +refresh: $(BOLTS:%=refresh-%) bolt1/pyln/spec/bolt1/csv.py bolt1/pyln/spec/bolt1/text.py: $(SPECDIR)/01-messaging.md Makefile bolt2/pyln/spec/bolt2/csv.py bolt2/pyln/spec/bolt2/text.py: $(SPECDIR)/02-peer-protocol.md Makefile From 264b4e02fa86e122c6a6d941a56db8ccfc57254d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 22 Sep 2022 13:07:11 +0930 Subject: [PATCH 1467/1530] pyln-spec: update to latest spec. In particular, some field names have changed. lnprototest in master has already upgraded names, so this brings us into compatibility again. Signed-off-by: Rusty Russell Changelog-Changed: pyln-spec: package updated to latest spec version. --- .../bolt1/pyln/spec/bolt1/gen_version.py | 4 +- .../pyln-spec/bolt1/pyln/spec/bolt1/text.py | 8 +- contrib/pyln-spec/bolt1/pyproject.toml | 2 +- .../pyln-spec/bolt2/pyln/spec/bolt2/csv.py | 9 +- .../bolt2/pyln/spec/bolt2/gen_csv_version.py | 2 +- .../bolt2/pyln/spec/bolt2/gen_version.py | 4 +- .../pyln-spec/bolt2/pyln/spec/bolt2/text.py | 138 +++++++++++++----- contrib/pyln-spec/bolt2/pyproject.toml | 2 +- .../pyln-spec/bolt4/pyln/spec/bolt4/csv.py | 2 +- .../bolt4/pyln/spec/bolt4/gen_csv_version.py | 2 +- .../bolt4/pyln/spec/bolt4/gen_version.py | 4 +- .../pyln-spec/bolt4/pyln/spec/bolt4/text.py | 17 ++- contrib/pyln-spec/bolt4/pyproject.toml | 2 +- .../pyln-spec/bolt7/pyln/spec/bolt7/csv.py | 2 +- .../bolt7/pyln/spec/bolt7/gen_csv_version.py | 2 +- .../bolt7/pyln/spec/bolt7/gen_version.py | 4 +- .../pyln-spec/bolt7/pyln/spec/bolt7/text.py | 110 +++++++------- contrib/pyln-spec/bolt7/pyproject.toml | 2 +- 18 files changed, 203 insertions(+), 113 deletions(-) diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py index 3377756caf44..fe86b5a5abaf 100644 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "222" -__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" +__post_version__ = "246" +__gitversion__ = "f32c6ddb5f11b431c9bb4f501cdec604172a90de" diff --git a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py index 721c6ac76a35..96c27eabf2e7 100644 --- a/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py +++ b/contrib/pyln-spec/bolt1/pyln/spec/bolt1/text.py @@ -6,7 +6,13 @@ This protocol assumes an underlying authenticated and ordered transport mechanism that takes care of framing individual messages. [BOLT #8](08-transport.md) specifies the canonical transport layer used in Lightning, though it can be replaced by any transport that fulfills the above guarantees. -The default TCP port is 9735. This corresponds to hexadecimal `0x2607`: the Unicode code point for LIGHTNING.[1](#reference-1) +The default TCP port depends on the network used. The most common networks are: + +- Bitcoin mainet with port number 9735 or the corresponding hexadecimal `0x2607`; +- Bitcoin testnet with port number 19735 (`0x4D17`); +- Bitcoin signet with port number 39735 (`0xF87`). + +The Unicode code point for LIGHTNING [1](#reference-1), and the port convention try to follow the Bitcoin Core convention. All data fields are unsigned big-endian unless otherwise specified. diff --git a/contrib/pyln-spec/bolt1/pyproject.toml b/contrib/pyln-spec/bolt1/pyproject.toml index c6ee8219eb12..743fa0a5d64b 100644 --- a/contrib/pyln-spec/bolt1/pyproject.toml +++ b/contrib/pyln-spec/bolt1/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-bolt1" -version = "1.0.1.187.post0" +version = "1.0.2.246" description = "" authors = ["Rusty Russell "] license = "MIT" diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/csv.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/csv.py index b24b32454e4e..78233b9641bb 100644 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/csv.py +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/csv.py @@ -51,9 +51,12 @@ "msgtype,funding_signed,35", "msgdata,funding_signed,channel_id,channel_id,", "msgdata,funding_signed,signature,signature,", - "msgtype,funding_locked,36", - "msgdata,funding_locked,channel_id,channel_id,", - "msgdata,funding_locked,next_per_commitment_point,point,", + "msgtype,channel_ready,36", + "msgdata,channel_ready,channel_id,channel_id,", + "msgdata,channel_ready,second_per_commitment_point,point,", + "msgdata,channel_ready,tlvs,channel_ready_tlvs,", + "tlvtype,channel_ready_tlvs,short_channel_id,1", + "tlvdata,channel_ready_tlvs,short_channel_id,alias,short_channel_id,", "msgtype,shutdown,38", "msgdata,shutdown,channel_id,channel_id,", "msgdata,shutdown,len,u16,", diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_csv_version.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_csv_version.py index 2140fc8212ff..fec7e9616295 100644 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_csv_version.py +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_csv_version.py @@ -1 +1 @@ -__csv_version__ = "2" +__csv_version__ = "3" diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py index 3377756caf44..fe86b5a5abaf 100644 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "222" -__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" +__post_version__ = "246" +__gitversion__ = "f32c6ddb5f11b431c9bb4f501cdec604172a90de" diff --git a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py index 66ba1fbb4e22..3c63f4c4f9d4 100644 --- a/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py +++ b/contrib/pyln-spec/bolt2/pyln/spec/bolt2/text.py @@ -13,7 +13,7 @@ * [The `accept_channel` Message](#the-accept_channel-message) * [The `funding_created` Message](#the-funding_created-message) * [The `funding_signed` Message](#the-funding_signed-message) - * [The `funding_locked` Message](#the-funding_locked-message) + * [The `channel_ready` Message](#the-channel_ready-message) * [Channel Close](#channel-close) * [Closing Initiation: `shutdown`](#closing-initiation-shutdown) * [Closing Negotiation: `closing_signed`](#closing-negotiation-closing_signed) @@ -71,8 +71,8 @@ the `funding_signed` message is sent/received, both sides should wait for the funding transaction to enter the blockchain and reach the specified depth (number of confirmations). After both sides have sent -the `funding_locked` message, the channel is established and can begin -normal operation. The `funding_locked` message includes information +the `channel_ready` message, the channel is established and can begin +normal operation. The `channel_ready` message includes information that will be used to construct channel authentication proofs. @@ -83,8 +83,8 @@ | A |--(3)-- funding_created --->| B | | |<-(4)-- funding_signed -----| | | | | | - | |--(5)--- funding_locked ---->| | - | |<-(6)--- funding_locked -----| | + | |--(5)--- channel_ready ---->| | + | |<-(6)--- channel_ready -----| | +-------+ +-------+ - where node A is 'funder' and node B is 'fundee' @@ -208,12 +208,16 @@ arbitrary combination (they represent the persistent features which affect the channel operation). -The currently defined types are: +The currently defined basic types are: - no features (no bits set) - `option_static_remotekey` (bit 12) - `option_anchor_outputs` and `option_static_remotekey` (bits 20 and 12) - `option_anchors_zero_fee_htlc_tx` and `option_static_remotekey` (bits 22 and 12) +Each basic type has the following variations allowed: + - `option_scid_alias` (bit 46) + - `option_zeroconf` (bit 50) + #### Requirements The sending node: @@ -240,6 +244,8 @@ - MUST set it to a defined type representing the type it wants. - MUST use the smallest bitmap possible to represent the channel type. - SHOULD NOT set it to a type containing a feature which was not negotiated. + - if `announce_channel` is `true` (not `0`): + - MUST NOT send `channel_type` with the `option_scid_alias` bit set. The sending node SHOULD: - set `to_self_delay` sufficient to ensure the sender can irreversibly spend a commitment transaction output, in case of misbehavior by the receiver. @@ -277,7 +283,9 @@ - the funder's amount for the initial commitment transaction is not sufficient for full [fee payment](03-transactions.md#fee-payment). - both `to_local` and `to_remote` amounts for the initial commitment transaction are less than or equal to `channel_reserve_satoshis` (see [BOLT 3](03-transactions.md#commitment-transaction-outputs)). - `funding_satoshis` is greater than or equal to 2^24 and the receiver does not support `option_support_large_channel`. - - It supports `channel_type`, `channel_type` was set, and the `type` is not suitable. + - It supports `channel_type` and `channel_type` was set: + - if `type` is not suitable. + - if `type` includes `option_zeroconf` and it does not trust the sender to open an unconfirmed channel. The receiving node MUST NOT: - consider funds received, using `push_msat`, to be received until the funding transaction has reached sufficient depth. @@ -345,8 +353,10 @@ the `open_channel` message. The sender: - - SHOULD set `minimum_depth` to a number of blocks it considers reasonable to -avoid double-spending of the funding transaction. + - if `channel_type` includes `option_zeroconf`: + - MUST set `minimum_depth` to zero. + - otherwise: + - SHOULD set `minimum_depth` to a number of blocks it considers reasonable to avoid double-spending of the funding transaction. - MUST set `channel_reserve_satoshis` greater than or equal to `dust_limit_satoshis` from the `open_channel` message. - MUST set `dust_limit_satoshis` less than or equal to `channel_reserve_satoshis` from the `open_channel` message. - if `option_channel_type` was negotiated: @@ -464,31 +474,67 @@ `option_static_remotekey`, and the superior one is favored if more than one is negotiated. -### The `funding_locked` Message +### The `channel_ready` Message + +This message (which used to be called `funding_locked`) indicates that the funding transaction has sufficient confirms for channel use. Once both nodes have sent this, the channel enters normal operating mode. -This message indicates that the funding transaction has reached the `minimum_depth` asked for in `accept_channel`. Once both nodes have sent this, the channel enters normal operating mode. +Note that the opener is free to send this message at any time (since it presumably trusts itself), but the +accepter would usually wait until the funding has reached the `minimum_depth` asked for in `accept_channel`. -1. type: 36 (`funding_locked`) +1. type: 36 (`channel_ready`) 2. data: * [`channel_id`:`channel_id`] - * [`point`:`next_per_commitment_point`] + * [`point`:`second_per_commitment_point`] + * [`channel_ready_tlvs`:`tlvs`] + +1. `tlv_stream`: `channel_ready_tlvs` +2. types: + 1. type: 1 (`short_channel_id`) + 2. data: + * [`short_channel_id`:`alias`] #### Requirements -The sender MUST: - - NOT send `funding_locked` unless outpoint of given by `funding_txid` and +The sender: + - MUST NOT send `channel_ready` unless outpoint of given by `funding_txid` and `funding_output_index` in the `funding_created` message pays exactly `funding_satoshis` to the scriptpubkey specified in [BOLT #3](03-transactions.md#funding-transaction-output). - - wait until the funding transaction has reached `minimum_depth` before - sending this message. - - set `next_per_commitment_point` to the per-commitment point to be used - for the following commitment transaction, derived as specified in + - if it is not the node opening the channel: + - SHOULD wait until the funding transaction has reached `minimum_depth` before + sending this message. + - MUST set `second_per_commitment_point` to the per-commitment point to be used + for commitment transaction #1, derived as specified in [BOLT #3](03-transactions.md#per-commitment-secret-requirements). + - if `option_scid_alias` was negotiated: + - MUST set `short_channel_id` `alias`. + - otherwise: + - MAY set `short_channel_id` `alias`. + - if it sets `alias`: + - if the `announce_channel` bit was set in `open_channel`: + - SHOULD initially set `alias` to value not related to the real `short_channel_id`. + - otherwise: + - MUST set `alias` to a value not related to the real `short_channel_id`. + - MUST NOT send the same `alias` for multiple peers or use an alias which + collides with a `short_channel_id` of a channel on the same node. + - MUST always recognize the `alias` as a `short_channel_id` for incoming HTLCs to this channel. + - if `channel_type` has `option_scid_alias` set: + - MUST NOT allow incoming HTLCs to this channel using the real `short_channel_id` + - MAY send multiple `channel_ready` messages to the same peer with different `alias` values. + - otherwise: + - MUST wait until the funding transaction has reached `minimum_depth` before sending this message. + + +The sender: A non-funding node (fundee): - SHOULD forget the channel if it does not see the correct funding - transaction after a timeout of 2016 blocks. + transaction after a timeout of 2016 blocks. -From the point of waiting for `funding_locked` onward, either node MAY +The receiver: + - MAY use any of the `alias` it received, in BOLT 11 `r` fields. + - if `channel_type` has `option_scid_alias` set: + - MUST NOT use the real `short_channel_id` in BOLT 11 `r` fields. + +From the point of waiting for `channel_ready` onward, either node MAY send an `error` and fail the channel if it does not receive a required response from the other node after a reasonable timeout. @@ -504,6 +550,17 @@ channel. To avoid this, the funder should ensure the funding transaction confirms in the next 2016 blocks. +The `alias` here is required for two distinct use cases. The first one is +for routing payments through channels that are not confirmed yet (since +the real `short_channel_id` is unknown until confirmation). The second one +is to provide one or more aliases to use for private channels (even once +a real `short_channel_id` is available). + +While a node can send multiple `alias`, it must remember all of the +ones it has sent so it can use them should they be requested by +incoming HTLCs. The recipient only need remember one, for use in +`r` route hints in BOLT 11 invoices. + ## Channel Close Nodes can negotiate a mutual close of the connection, which unlike a @@ -544,12 +601,14 @@ A sending node: - if it hasn't sent a `funding_created` (if it is a funder) or a `funding_signed` (if it is a fundee): - MUST NOT send a `shutdown` - - MAY send a `shutdown` before a `funding_locked`, i.e. before the funding transaction has reached `minimum_depth`. + - MAY send a `shutdown` before a `channel_ready`, i.e. before the funding transaction has reached `minimum_depth`. - if there are updates pending on the receiving node's commitment transaction: - MUST NOT send a `shutdown`. + - MUST NOT send multiple `shutdown` messages. - MUST NOT send an `update_add_htlc` after a `shutdown`. - - if no HTLCs remain in either commitment transaction: - - MUST NOT send any `update` message after a `shutdown`. + - if no HTLCs remain in either commitment transaction (including dust HTLCs) + and neither side has a pending `revoke_and_ack` to send: + - MUST NOT send any `update` message after that point. - SHOULD fail to route any HTLC added after it has sent `shutdown`. - if it sent a non-zero-length `shutdown_scriptpubkey` in `open_channel` or `accept_channel`: - MUST send the same value in `scriptpubkey`. @@ -566,7 +625,7 @@ - SHOULD send an `error` and fail the channel. - if the `scriptpubkey` is not in one of the above forms: - SHOULD send a `warning`. - - if it hasn't sent a `funding_locked` yet: + - if it hasn't sent a `channel_ready` yet: - MAY reply to a `shutdown` message with a `shutdown` - once there are no outstanding updates on the peer, UNLESS it has already sent a `shutdown`: - MUST reply to a `shutdown` message with a `shutdown` @@ -581,10 +640,13 @@ the sender always sends a `commitment_signed` first. As shutdown implies a desire to terminate, it implies that no new -HTLCs will be added or accepted. Once any HTLCs are cleared, the peer -may immediately begin closing negotiation, so we ban further updates -to the commitment transaction (in particular, `update_fee` would be -possible otherwise). +HTLCs will be added or accepted. Once any HTLCs are cleared, there are no commitments +for which a revocation is owed, and all updates are included on both commitment +transactions, the peer may immediately begin closing negotiation, so we ban further +updates to the commitment transaction (in particular, `update_fee` would be +possible otherwise). However, while there are HTLCs on the commitment transaction, +the initiator may find it desirable to increase the feerate as there may be pending +HTLCs on the commitment which could timeout. The `scriptpubkey` forms include only standard segwit forms accepted by the Bitcoin network, which ensures the resulting transaction will @@ -603,8 +665,9 @@ ### Closing Negotiation: `closing_signed` -Once shutdown is complete and the channel is empty of HTLCs, the final -current commitment transactions will have no HTLCs, and closing fee +Once shutdown is complete, the channel is empty of HTLCs, there are no commitments +for which a revocation is owed, and all updates are included on both commitments, +the final current commitment transactions will have no HTLCs, and closing fee negotiation begins. The funder chooses a fee it thinks is fair, and signs the closing transaction with the `scriptpubkey` fields from the `shutdown` messages (along with its chosen fee) and sends the signature; @@ -716,7 +779,7 @@ ## Normal Operation -Once both nodes have exchanged `funding_locked` (and optionally [`announcement_signatures`](07-routing-gossip.md#the-announcement_signatures-message)), the channel can be used to make payments via Hashed Time Locked Contracts. +Once both nodes have exchanged `channel_ready` (and optionally [`announcement_signatures`](07-routing-gossip.md#the-announcement_signatures-message)), the channel can be used to make payments via Hashed Time Locked Contracts. Changes are sent in batches: one or more `update_` messages are sent before a `commitment_signed` message, as in the following diagram: @@ -1370,11 +1433,12 @@ A node: - if `next_commitment_number` is 1 in both the `channel_reestablish` it sent and received: - - MUST retransmit `funding_locked`. + - MUST retransmit `channel_ready`. - otherwise: - - MUST NOT retransmit `funding_locked`. + - MUST NOT retransmit `channel_ready`, but MAY send `channel_ready` with + a different `short_channel_id` `alias` field. - upon reconnection: - - MUST ignore any redundant `funding_locked` it receives. + - MUST ignore any redundant `channel_ready` it receives. - if `next_commitment_number` is equal to the commitment number of the last `commitment_signed` message the receiving node has sent: - MUST reuse the same commitment number for its next `commitment_signed`. @@ -1442,7 +1506,7 @@ is if the `funding_signed` message is sent but not received. In this case, the funder will forget the channel, and presumably open a new one upon reconnection; meanwhile, the other node will eventually forget -the original channel, due to never receiving `funding_locked` or seeing +the original channel, due to never receiving `channel_ready` or seeing the funding transaction on-chain. There's no acknowledgment for `error`, so if a reconnect occurs it's @@ -1478,7 +1542,7 @@ `commitment_signed` for commitment number 1 is send and then the revocation for commitment number 0 is received. -`funding_locked` is implicitly acknowledged by the start of normal +`channel_ready` is implicitly acknowledged by the start of normal operation, which is known to have begun after a `commitment_signed` has been received — hence, the test for a `next_commitment_number` greater than 1. diff --git a/contrib/pyln-spec/bolt2/pyproject.toml b/contrib/pyln-spec/bolt2/pyproject.toml index 10367daa99b8..bbb708866440 100644 --- a/contrib/pyln-spec/bolt2/pyproject.toml +++ b/contrib/pyln-spec/bolt2/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-bolt2" -version = "1.0.2.187.post0" +version = "1.0.3.246" description = "A pure python implementation of BOLT2" authors = ["Rusty Russell "] license = "MIT" diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py index b6458142ab21..ca65ecd52202 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/csv.py @@ -49,7 +49,7 @@ "msgtype,final_incorrect_htlc_amount,19", "msgdata,final_incorrect_htlc_amount,incoming_htlc_amt,u64,", "msgtype,channel_disabled,UPDATE|20", - "msgdata,channel_disabled,flags,u16,", + "msgdata,channel_disabled,disabled_flags,u16,", "msgdata,channel_disabled,len,u16,", "msgdata,channel_disabled,channel_update,byte,len", "msgtype,expiry_too_far,21", diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py index fec7e9616295..85d6a77f6912 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_csv_version.py @@ -1 +1 @@ -__csv_version__ = "3" +__csv_version__ = "4" diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py index 3377756caf44..fe86b5a5abaf 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "222" -__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" +__post_version__ = "246" +__gitversion__ = "f32c6ddb5f11b431c9bb4f501cdec604172a90de" diff --git a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py index 398e908a8659..fe6454e2b7d0 100644 --- a/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py +++ b/contrib/pyln-spec/bolt4/pyln/spec/bolt4/text.py @@ -854,7 +854,9 @@ * 0x1000 (UPDATE): new channel update enclosed Please note that the `channel_update` field is mandatory in messages whose -`failure_code` includes the `UPDATE` flag. +`failure_code` includes the `UPDATE` flag. It is encoded *with* the message +type prefix, i.e. it should always start with `0x0102`. Note that historical +lightning implementations serialized this without the `0x0102` message type. The following `failure_code`s are defined: @@ -999,11 +1001,13 @@ 1. type: UPDATE|20 (`channel_disabled`) 2. data: - * [`u16`:`flags`] + * [`u16`:`disabled_flags`] * [`u16`:`len`] * [`len*byte`:`channel_update`] The channel from the processing node has been disabled. +No flags for `disabled_flags` are currently defined, thus it is currently +always two zero bytes. 1. type: 21 (`expiry_too_far`) @@ -1115,6 +1119,15 @@ - if the `amt_to_forward` does NOT correspond with the `incoming_htlc_amt` from the final node's HTLC: - MUST return a `final_incorrect_htlc_amount` error. + - if it returns a `channel_update`: + - MUST set `short_channel_id` to the `short_channel_id` used by the incoming onion. + +### Rationale + +In the case of multiple short_channel_id aliases, the `channel_update` +`short_channel_id` should refer to the one the original sender is +expecting, to both avoid confusion and to avoid leaking information +about other aliases (or the real location of the channel UTXO). ## Receiving Failure Codes diff --git a/contrib/pyln-spec/bolt4/pyproject.toml b/contrib/pyln-spec/bolt4/pyproject.toml index 1d3361de1b91..eb261be16ae9 100644 --- a/contrib/pyln-spec/bolt4/pyproject.toml +++ b/contrib/pyln-spec/bolt4/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-bolt4" -version = "1.0.2.187.post0" +version = "1.0.4.246" description = "A pure python implementation of BOLT4" authors = ["Rusty Russell "] license = "MIT" diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py index d94248c545a7..80d60c1a286b 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/csv.py @@ -38,7 +38,7 @@ "msgdata,channel_update,htlc_minimum_msat,u64,", "msgdata,channel_update,fee_base_msat,u32,", "msgdata,channel_update,fee_proportional_millionths,u32,", - "msgdata,channel_update,htlc_maximum_msat,u64,,option_channel_htlc_max", + "msgdata,channel_update,htlc_maximum_msat,u64,", "msgtype,query_short_channel_ids,261,gossip_queries", "msgdata,query_short_channel_ids,chain_hash,chain_hash,", "msgdata,query_short_channel_ids,len,u16,", diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py index fec7e9616295..85d6a77f6912 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_csv_version.py @@ -1 +1 @@ -__csv_version__ = "3" +__csv_version__ = "4" diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py index 3377756caf44..fe86b5a5abaf 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/gen_version.py @@ -1,3 +1,3 @@ __base_version__ = "1.0" -__post_version__ = "222" -__gitversion__ = "f1c797df2966237244527c1c6343dbe9bc765342" +__post_version__ = "246" +__gitversion__ = "f32c6ddb5f11b431c9bb4f501cdec604172a90de" diff --git a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py index 84c430ec8a2b..404303a26ddd 100644 --- a/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py +++ b/contrib/pyln-spec/bolt7/pyln/spec/bolt7/text.py @@ -86,7 +86,7 @@ A node: - if the `open_channel` message has the `announce_channel` bit set AND a `shutdown` message has not been sent: - MUST send the `announcement_signatures` message. - - MUST NOT send `announcement_signatures` messages until `funding_locked` + - MUST NOT send `announcement_signatures` messages until `channel_ready` has been sent and received AND the funding transaction has at least six confirmations. - otherwise: - MUST NOT send the `announcement_signatures` message. @@ -105,8 +105,8 @@ `error` and fail the channel. - if it has sent AND received a valid `announcement_signatures` message: - SHOULD queue the `channel_announcement` message for its peers. - - if it has not sent funding_locked: - - MAY defer handling the announcement_signatures until after it has sent funding_locked + - if it has not sent `channel_ready`: + - MAY defer handling the announcement_signatures until after it has sent `channel_ready` - otherwise: - MUST ignore it. @@ -168,7 +168,7 @@ - for the _Bitcoin blockchain_: - MUST set `chain_hash` value (encoded in hex) equal to `6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000`. - MUST set `short_channel_id` to refer to the confirmed funding transaction, - as specified in [BOLT #2](02-peer-protocol.md#the-funding_locked-message). + as specified in [BOLT #2](02-peer-protocol.md#the-channel_ready-message). - Note: the corresponding output MUST be a P2WSH, as described in [BOLT #3](03-transactions.md#funding-transaction-output). - MUST set `node_id_1` and `node_id_2` to the public keys of the two nodes operating the channel, such that `node_id_1` is the lexicographically-lesser of the @@ -225,7 +225,7 @@ - otherwise: - SHOULD store this `channel_announcement`. - once its funding output has been spent OR reorganized out: - - SHOULD forget a channel. + - SHOULD forget a channel after a 12-block delay. ### Rationale @@ -250,6 +250,10 @@ will have _even_ feature bits (["It's OK to be odd!"](00-introduction.md#glossary-and-terminology-guide)). +A delay of 12-blocks is used when forgetting a channel on funding output spend +as to permit a new `channel_announcement` to propagate which indicates this +channel was spliced. + ## The `node_announcement` Message This gossip message allows a node to indicate extra data associated with it, in @@ -287,6 +291,10 @@ onion service addresses; Encodes: `[32:32_byte_ed25519_pubkey] || [2:checksum] || [1:version]`, where `checksum = sha3(".onion checksum" | pubkey || version)[:2]`. + * `5`: DNS hostname; data = `[1:hostname_len][hostname_len:hostname][2:port]` (length up to 258) + * `hostname` bytes MUST be ASCII characters. + * Non-ASCII characters MUST be encoded using Punycode: + https://en.wikipedia.org/wiki/Punycode ### Requirements @@ -308,13 +316,14 @@ - MUST place address descriptors in ascending order. - SHOULD NOT place any zero-typed address descriptors anywhere. - SHOULD use placement only for aligning fields that follow `addresses`. - - MUST NOT create a `type 1` OR `type 2` address descriptor with `port` equal - to 0. + - MUST NOT create a `type 1`, `type 2` or `type 5` address descriptor with + `port` equal to 0. - SHOULD ensure `ipv4_addr` AND `ipv6_addr` are routable addresses. - MUST set `features` according to [BOLT #9](09-features.md#assigned-features-flags) - SHOULD set `flen` to the minimum length required to hold the `features` bits it sets. - SHOULD not announce a Tor v2 onion service. + - MUST NOT announce more than one `type 5` DNS hostname. The receiving node: - if `node_id` is NOT a valid compressed public key: @@ -339,7 +348,7 @@ - SHOULD send a `warning`. - MAY close the connection. - if `port` is equal to 0: - - SHOULD ignore `ipv6_addr` OR `ipv4_addr`. + - SHOULD ignore `ipv6_addr` OR `ipv4_addr` OR `hostname`. - if `node_id` is NOT previously known from a `channel_announcement` message, OR if `timestamp` is NOT greater than the last-received `node_announcement` from this `node_id`: @@ -352,6 +361,9 @@ - MAY use `rgb_color` AND `alias` to reference nodes in interfaces. - SHOULD insinuate their self-signed origins. - SHOULD ignore Tor v2 onion services. + - if more than one `type 5` address is announced: + - SHOULD ignore the additional data. + - MUST not forward the `node_announcement`. ### Rationale @@ -414,7 +426,7 @@ * [`u64`:`htlc_minimum_msat`] * [`u32`:`fee_base_msat`] * [`u32`:`fee_proportional_millionths`] - * [`u64`:`htlc_maximum_msat`] (option_channel_htlc_max) + * [`u64`:`htlc_maximum_msat`] The `channel_flags` bitfield is used to indicate the direction of the channel: it identifies the node that this update originated from and signals various options @@ -426,18 +438,12 @@ | 0 | `direction` | Direction this update refers to. | | 1 | `disable` | Disable the channel. | -The `message_flags` bitfield is used to indicate the presence of optional -fields in the `channel_update` message: - -| Bit Position | Name | Field | -| ------------- | ------------------------- | -------------------------------- | -| 0 | `option_channel_htlc_max` | `htlc_maximum_msat` | +The `message_flags` bitfield is used to provide additional details about the message: -Note that the `htlc_maximum_msat` field is static in the current -protocol over the life of the channel: it is *not* designed to be -indicative of real-time channel capacity in each direction, which -would be both a massive data leak and uselessly spam the network (it -takes an average of 30 seconds for gossip to propagate each hop). +| Bit Position | Name | +| ------------- | ---------------| +| 0 | `must_be_one` | +| 1 | `dont_forward` | The `node_id` for the signature verification is taken from the corresponding `channel_announcement`: `node_id_1` if the least-significant bit of flags is 0 @@ -446,10 +452,13 @@ ### Requirements The origin node: - - MUST NOT send a created `channel_update` before `funding_locked` has been received. + - MUST NOT send a created `channel_update` before `channel_ready` has been received. - MAY create a `channel_update` to communicate the channel parameters to the channel peer, even though the channel has not yet been announced (i.e. the `announce_channel` bit was not set). + - MUST set the `short_channel_id` to either an `alias` it has + received from the peer, or the real channel `short_channel_id`. + - MUST set `dont_forward` to 1 in `message_flags` - MUST NOT forward such a `channel_update` to other peers, for privacy reasons. - Note: such a `channel_update`, one not preceded by a @@ -463,15 +472,8 @@ - MUST set the `direction` bit of `channel_flags` to 0. - otherwise: - MUST set the `direction` bit of `channel_flags` to 1. - - if the `htlc_maximum_msat` field is present: - - MUST set the `option_channel_htlc_max` bit of `message_flags` to 1. - - MUST set `htlc_maximum_msat` to the maximum value it will send through this channel for a single HTLC. - - MUST set this to less than or equal to the channel capacity. - - MUST set this to less than or equal to `max_htlc_value_in_flight_msat` - it received from the peer. - - otherwise: - - MUST set the `option_channel_htlc_max` bit of `message_flags` to 0. - - MUST set bits in `channel_flags` and `message_flags `that are not assigned a meaning to 0. + - MUST set `must_be_one` in `message_flags` to 1. + - MUST set bits in `channel_flags` and `message_flags` that are not assigned a meaning to 0. - MAY create and send a `channel_update` with the `disable` bit set to 1, to signal a channel's temporary unavailability (e.g. due to a loss of connectivity) OR permanent unavailability (e.g. prior to an on-chain @@ -490,6 +492,8 @@ - MUST set `fee_proportional_millionths` to the amount (in millionths of a satoshi) it will charge per transferred satoshi. - SHOULD NOT create redundant `channel_update`s + - If it creates a new `channel_update` with updated channel parameters: + - SHOULD keep accepting the previous channel parameters for 10 minutes The receiving node: - if the `short_channel_id` does NOT match a previous `channel_announcement`, @@ -522,14 +526,11 @@ - otherwise: - SHOULD queue the message for rebroadcasting. - MAY choose NOT to for messages longer than the minimum expected length. - - if the `option_channel_htlc_max` bit of `message_flags` is 0: - - MUST consider `htlc_maximum_msat` not to be present. + - if `htlc_maximum_msat` is greater than channel capacity: + - MAY blacklist this `node_id` + - SHOULD ignore this channel during route considerations. - otherwise: - - if `htlc_maximum_msat` is not present or greater than channel capacity: - - MAY blacklist this `node_id` - - SHOULD ignore this channel during route considerations. - - otherwise: - - SHOULD consider the `htlc_maximum_msat` when routing. + - SHOULD consider the `htlc_maximum_msat` when routing. ### Rationale @@ -539,23 +540,17 @@ 1970-01-01). This cannot be a hard requirement, however, given the possible case of two `channel_update`s within a single second. -It is assumed that more than one `channel_update` message changing the channel -parameters in the same second may be a DoS attempt, and therefore, the node responsible -for signing such messages may be blacklisted. However, a node may send a same -`channel_update` message with a different signature (changing the nonce in signature -signing), and hence fields apart from signature are checked to see if the channel -parameters have changed for the same timestamp. It is also important to note that -ECDSA signatures are malleable. So, an intermediate node who received the `channel_update` -message can rebroadcast it just by changing the `s` component of signature with `-s`. +It is assumed that more than one `channel_update` message changing the channel +parameters in the same second may be a DoS attempt, and therefore, the node responsible +for signing such messages may be blacklisted. However, a node may send a same +`channel_update` message with a different signature (changing the nonce in signature +signing), and hence fields apart from signature are checked to see if the channel +parameters have changed for the same timestamp. It is also important to note that +ECDSA signatures are malleable. So, an intermediate node who received the `channel_update` +message can rebroadcast it just by changing the `s` component of signature with `-s`. This should however not result in the blacklist of the `node_id` from where the message originated. -The explicit `option_channel_htlc_max` flag to indicate the presence -of `htlc_maximum_msat` (rather than having `htlc_maximum_msat` implied -by the message length) allows us to extend the `channel_update` -with different fields in future. Since channels are limited to 2^32-1 -millisatoshis in Bitcoin, the `htlc_maximum_msat` has the same restriction. - The recommendation against redundant `channel_update`s minimizes spamming the network, however it is sometimes inevitable. For example, a channel with a peer which is unreachable will eventually cause a `channel_update` to @@ -564,6 +559,15 @@ messages are batched and replace previous ones, the result may be a single seemingly-redundant update. +When a node creates a new `channel_update` to change its channel parameters, +it will take some time to propagate through the network and payers may use +older parameters. It is recommended to keep accepting older parameters for +at least 10 minutes to improve payment latency and reliability. + +The `must_be_one` field in `message_flags` was previously used to indicate +the presence of the `htlc_maximum_msat` field. This field must now always +be present, so `must_be_one` is a constant value, and ignored by receivers. + ## Query Messages Negotiating the `gossip_queries` option via `init` enables a number @@ -986,8 +990,8 @@ #### Requirements A node: - - if a channel's oldest `channel_update`s `timestamp` is older than two weeks - (1209600 seconds): + - if the `timestamp` of the latest `channel_update` in either direction is + older than two weeks (1209600 seconds): - MAY prune the channel. - MAY ignore the channel. - Note: this is an individual node policy and MUST NOT be enforced by diff --git a/contrib/pyln-spec/bolt7/pyproject.toml b/contrib/pyln-spec/bolt7/pyproject.toml index bc6b128ea567..1048f43bd1e1 100644 --- a/contrib/pyln-spec/bolt7/pyproject.toml +++ b/contrib/pyln-spec/bolt7/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-bolt7" -version = "1.0.2.186.post0" +version = "1.0.4.246" description = "BOLT7" authors = ["Rusty Russell"] license = "BSD-MIT" From 9b5bc81541e30e6a9bda98e15d5495ca77aff457 Mon Sep 17 00:00:00 2001 From: Greg Sanders Date: Thu, 22 Sep 2022 16:42:58 -0400 Subject: [PATCH 1468/1530] onchaind/onchaind_wire.c duplicated in ONCHAIND_SRC --- onchaind/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/onchaind/Makefile b/onchaind/Makefile index 28c8b6a32f10..e875af751717 100644 --- a/onchaind/Makefile +++ b/onchaind/Makefile @@ -8,8 +8,7 @@ ONCHAIND_HEADERS := \ ONCHAIND_SRC := onchaind/onchaind.c \ onchaind/onchaind_wiregen.c \ - onchaind/onchaind_wire.c \ - onchaind/onchaind.c + onchaind/onchaind_wire.c onchaind/onchain_types_names_gen.h: onchaind/onchain_types.h ccan/ccan/cdump/tools/cdump-enumstr ccan/ccan/cdump/tools/cdump-enumstr onchaind/onchain_types.h > $@ From 57002f3381a9aafbee4cce400cdb4b83430c0850 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 23 Sep 2022 14:32:30 +0930 Subject: [PATCH 1469/1530] pytest: fix flake in test_onchain_different_fees Sometimes, we haven't reconnected, and so the peer does not exist at all after the channel is forgotten: ``` 2022-09-22T22:49:59.3985374Z # Now, 100 blocks it should be done. 2022-09-22T22:49:59.3985656Z bitcoind.generate_block(100) 2022-09-22T22:49:59.3986112Z > wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) 2022-09-22T22:49:59.3986338Z 2022-09-22T22:49:59.3986523Z tests/test_closing.py:2715: 2022-09-22T22:49:59.3986810Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 2022-09-22T22:49:59.3987241Z contrib/pyln-testing/pyln/testing/utils.py:90: in wait_for 2022-09-22T22:49:59.3987568Z while not success(): 2022-09-22T22:49:59.3987917Z tests/test_closing.py:2715: in 2022-09-22T22:49:59.3988389Z wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) 2022-09-22T22:49:59.3988737Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 2022-09-22T22:49:59.3988908Z 2022-09-22T22:49:59.3988979Z arr = [] 2022-09-22T22:49:59.3989106Z 2022-09-22T22:49:59.3989209Z def only_one(arr): 2022-09-22T22:49:59.3989545Z """Many JSON RPC calls return an array; often we only expect a single entry 2022-09-22T22:49:59.3989849Z """ 2022-09-22T22:49:59.3990063Z > assert len(arr) == 1 2022-09-22T22:49:59.3990388Z E AssertionError ``` You can see it's empty from the call here: ``` 2022-09-22T22:49:59.6697941Z DEBUG:root:{ 2022-09-22T22:49:59.6698106Z "id": "-c:listpeers#42", 2022-09-22T22:49:59.6698179Z "result": { 2022-09-22T22:49:59.6698270Z "peers": [] 2022-09-22T22:49:59.6698346Z } 2022-09-22T22:49:59.6698422Z } ``` Signed-off-by: Rusty Russell --- tests/test_closing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_closing.py b/tests/test_closing.py index 4d18d561ef2d..cd6e5242b951 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -2712,8 +2712,10 @@ def test_onchain_different_fees(node_factory, bitcoind, executor): # Now, 100 blocks it should be done. bitcoind.generate_block(100) - wait_for(lambda: only_one(l1.rpc.listpeers()['peers'])['channels'] == []) - wait_for(lambda: only_one(l2.rpc.listpeers()['peers'])['channels'] == []) + + # May reconnect, may not: if not, peer does not exist! + wait_for(lambda: all(p['channels'] == [] for p in l1.rpc.listpeers()['peers'])) + wait_for(lambda: all(p['channels'] == [] for p in l2.rpc.listpeers()['peers'])) @pytest.mark.developer("needs DEVELOPER=1") From 9be6ed62360904e65e41c4a4fc6823f29ec59263 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 23 Sep 2022 14:32:35 +0930 Subject: [PATCH 1470/1530] pytest: fix flake in test_pay_disconnect If channeld hasn't exited yet, it's possible we'll send the message (we would fail later, in waitsendpay, but just not immediately). So wait for that explicitly. ``` 2022-09-22T22:49:59.6737296Z with pytest.raises(RpcError, match=r'failed: WIRE_TEMPORARY_CHANNEL_FAILURE \(First peer not ready\)'): 2022-09-22T22:49:59.6737566Z > l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) 2022-09-22T22:49:59.6737865Z E Failed: DID NOT RAISE 2022-09-22T22:49:59.6737873Z ``` And from the listpeers output, ou can see "connected" false, but owner channeld: ``` 2022-09-22T22:49:59.7493163Z DEBUG:root:{ 2022-09-22T22:49:59.7493320Z "id": "-c:listpeers#26", 2022-09-22T22:49:59.7493397Z "result": { 2022-09-22T22:49:59.7493477Z "peers": [ 2022-09-22T22:49:59.7493548Z { 2022-09-22T22:49:59.7493709Z "id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", 2022-09-22T22:49:59.7493801Z "connected": false, 2022-09-22T22:49:59.7493884Z "channels": [ 2022-09-22T22:49:59.7493955Z { 2022-09-22T22:49:59.7494058Z "state": "CHANNELD_NORMAL", 2022-09-22T22:49:59.7494250Z "scratch_txid": "4b95a3b1b5e1a970401a169a3697f3a9bfbfbcb59d3d21434aa1f3fb2980db8d", 2022-09-22T22:49:59.7494365Z "last_tx_fee_msat": "7965000msat", 2022-09-22T22:49:59.7494437Z "feerate": { 2022-09-22T22:49:59.7494529Z "perkw": 11000, 2022-09-22T22:49:59.7494618Z "perkb": 44000 2022-09-22T22:49:59.7494690Z }, 2022-09-22T22:49:59.7494785Z "owner": "channeld", 2022-09-22T22:49:59.7494894Z "short_channel_id": "103x1x0", ``` Signed-off-by: Rusty Russell --- tests/test_pay.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_pay.py b/tests/test_pay.py index 465e0bedf1b8..2eec94c914df 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -266,7 +266,8 @@ def test_pay_disconnect(node_factory, bitcoind): route = l1.rpc.getroute(l2.info['id'], 123000, 1)["route"] l2.stop() - wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'] is False) + # Make sure channeld has exited! + wait_for(lambda: 'owner' not in only_one(only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['channels'])) # Can't pay while its offline. with pytest.raises(RpcError, match=r'failed: WIRE_TEMPORARY_CHANNEL_FAILURE \(First peer not ready\)'): From 45cdfd2ff75c8d0f73deb2a24c81575760619fce Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 12:16:34 +0930 Subject: [PATCH 1471/1530] BOLT: update to fix gossip pruning quote. Which I disagreed with, and has been fixed. Signed-off-by: Rusty Russell --- Makefile | 2 +- common/gossip_constants.h | 4 ++-- gossipd/gossipd.c | 4 ++-- gossipd/routing.c | 6 ++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index f746cdd2d396..ae3b544a94da 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := 341ec844f13c0c0abc4fe849059fbb98173f9766 +DEFAULT_BOLTVERSION := 48fed66e26b80031d898c6492434fa9926237d64 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/common/gossip_constants.h b/common/gossip_constants.h index 436db62a4ab6..345f9126b231 100644 --- a/common/gossip_constants.h +++ b/common/gossip_constants.h @@ -62,8 +62,8 @@ /* BOLT #7: * * A node: - * - if a channel's latest `channel_update`s `timestamp` is older than two weeks - * (1209600 seconds): + * - if the `timestamp` of the latest `channel_update` in + * either direction is older than two weeks (1209600 seconds): * - MAY prune the channel. * - MAY ignore the channel. */ diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 1926c440ec5b..26be95bd6090 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -603,8 +603,8 @@ static struct io_plan *connectd_req(struct io_conn *conn, /* BOLT #7: * * A node: - * - if a channel's latest `channel_update`s `timestamp` is older than two weeks - * (1209600 seconds): + * - if the `timestamp` of the latest `channel_update` in + * either direction is older than two weeks (1209600 seconds): * - MAY prune the channel. * - MAY ignore the channel. */ diff --git a/gossipd/routing.c b/gossipd/routing.c index d9dcdf72b61f..8e569dec8a89 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1926,12 +1926,10 @@ void route_prune(struct routing_state *rstate) continue; /* BOLT #7: - * - if a channel's latest `channel_update`s `timestamp` is - * older than two weeks (1209600 seconds): + * - if the `timestamp` of the latest `channel_update` in + * either direction is older than two weeks (1209600 seconds): * - MAY prune the channel. */ - /* FIXME: I disagree with the above quote: it used to say "oldest", which is what we - use here: */ /* This is a fancy way of saying "both ends must refresh!" */ if (!is_halfchan_defined(&chan->half[0]) || chan->half[0].bcast.timestamp < highwater From fe556d1ed9b11087ecfdfc86f00e9397c2db7973 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:28 +0930 Subject: [PATCH 1472/1530] gossipd: don't try to upgrade ancient gossip_store. If they really upgrade directly from 0.9.2, it will simply delete the store and re-fetch it. We still update from v9 (which could be v0.11), since it's a noop. Signed-off-by: Rusty Russell --- gossipd/gossip_store.c | 66 ++---------------------------- gossipd/gossip_store_wire.csv | 9 ----- tests/test_gossip.py | 75 +---------------------------------- 3 files changed, 4 insertions(+), 146 deletions(-) diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 3eed656529ad..873be7f8818a 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -101,26 +101,10 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, return true; } -#ifdef COMPAT_V082 -static u8 *mk_private_channelmsg(const tal_t *ctx, - struct routing_state *rstate, - const struct short_channel_id *scid, - const struct node_id *remote_node_id, - struct amount_sat sat, - const u8 *features) -{ - const u8 *ann = private_channel_announcement(tmpctx, scid, - &rstate->local_id, - remote_node_id, - features); - - return towire_gossip_store_private_channel(ctx, sat, ann); -} - -/* The upgrade from version 7 is trivial */ +/* The upgrade from version 9 is a noop: we added the spam flag. */ static bool can_upgrade(u8 oldversion) { - return oldversion == 7 || oldversion == 8 || oldversion == 9; + return oldversion == 9; } static bool upgrade_field(u8 oldversion, @@ -128,52 +112,8 @@ static bool upgrade_field(u8 oldversion, u8 **msg) { assert(can_upgrade(oldversion)); - - if (fromwire_peektype(*msg) == WIRE_GOSSIPD_LOCAL_ADD_CHANNEL_OBS - && oldversion == 7) { - /* Append two 0 bytes, for (empty) feature bits */ - tal_resizez(msg, tal_bytelen(*msg) + 2); - } - - /* We turn these (v8) into a WIRE_GOSSIP_STORE_PRIVATE_CHANNEL */ - if (fromwire_peektype(*msg) == WIRE_GOSSIPD_LOCAL_ADD_CHANNEL_OBS) { - struct short_channel_id scid; - struct node_id remote_node_id; - struct amount_sat satoshis; - u8 *features; - u8 *storemsg; - - if (!fromwire_gossipd_local_add_channel_obs(tmpctx, *msg, - &scid, - &remote_node_id, - &satoshis, - &features)) - return false; - - storemsg = mk_private_channelmsg(tal_parent(*msg), - rstate, - &scid, - &remote_node_id, - satoshis, - features); - tal_free(*msg); - *msg = storemsg; - } return true; } -#else -static bool can_upgrade(u8 oldversion) -{ - return false; -} - -static bool upgrade_field(u8 oldversion, - struct routing_state *rstate, - u8 **msg) -{ - abort(); -} -#endif /* !COMPAT_V082 */ /* Read gossip store entries, copy non-deleted ones. This code is written * as simply and robustly as possible! */ @@ -771,7 +711,7 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) } if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) { - bad = "Checksum verification failed"; + bad = tal_fmt(tmpctx, "Checksum verification failed: should be %08x", crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)); goto badmsg; } diff --git a/gossipd/gossip_store_wire.csv b/gossipd/gossip_store_wire.csv index 1b90e38e9073..6781757dcec5 100644 --- a/gossipd/gossip_store_wire.csv +++ b/gossipd/gossip_store_wire.csv @@ -23,12 +23,3 @@ msgdata,gossip_store_delete_chan,scid,short_channel_id, msgtype,gossip_store_ended,4105 msgdata,gossip_store_ended,equivalent_offset,u64, - -# FIXME: Here for COMPAT with v0.9.0 and before only. -msgtype,gossipd_local_add_channel_obs,3503 -msgdata,gossipd_local_add_channel_obs,short_channel_id,short_channel_id, -msgdata,gossipd_local_add_channel_obs,remote_node_id,node_id, -msgdata,gossipd_local_add_channel_obs,satoshis,amount_sat, -msgdata,gossipd_local_add_channel_obs,flen,u16, -msgdata,gossipd_local_add_channel_obs,features,u8,flen - diff --git a/tests/test_gossip.py b/tests/test_gossip.py index fec573363712..1f0d35b3095c 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -5,7 +5,7 @@ from pyln.client import RpcError, Millisatoshi from utils import ( DEVELOPER, wait_for, TIMEOUT, only_one, sync_blockheight, - expected_node_features, COMPAT, + expected_node_features, mine_funding_to_announce, default_ln_port ) @@ -2001,79 +2001,6 @@ def test_torport_onions(node_factory): assert l2.daemon.is_in_log('x2y4zvh4fn5q3eouuh7nxnc7zeawrqoutljrup2xjtiyxgx3emgkemad.onion:45321,127.0.0.1:{}'.format(l2.port)) -@unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete gossip_store") -def test_gossip_store_upgrade_v7_v8(node_factory): - """Version 8 added feature bits to local channel announcements""" - - # We get BROKEN logs because gossipd talks about non-existent channels to - # lightningd ("**BROKEN** lightningd: Local update for bad scid 103x1x1"). - l1 = node_factory.get_node(start=False, - allow_broken_log=True) - - # A channel announcement with no channel_update. - with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: - f.write(bytearray.fromhex("07000000428ce4d2d8000000000daf00" - "00670000010001022d223620a359a47f" - "f7f7ac447c85c46c923da53389221a00" - "54c11c1e3ca31d5900000000000f4240" - "000d8000000000000000000000000000" - "00008e3af3badf000000001006008a01" - "02005a9911d425effd461f803a380f05" - "e72d3332eb6e9a7c6c58405ae61eacde" - "4e2da18240ffb3d5c595f85e4f78b594" - "c59e4d01c0470edd4f5afe645026515e" - "fe06226e46111a0b59caaf126043eb5b" - "bf28c34f3a5e332a1fc7b2b73cf18891" - "0f00006700000100015eaa5eb0010100" - "06000000000000000000000001000000" - "0a000000003b0233800000008e074a6e" - "0f000000001006008a0102463de636b2" - "f46ccd6c23259787fc39dc4fdb983510" - "1651879325b18cf1bb26330127e51ce8" - "7a111b05ef92fe00a9a089979dc49178" - "200f49139a541e7078cdc506226e4611" - "1a0b59caaf126043eb5bbf28c34f3a5e" - "332a1fc7b2b73cf188910f0000670000" - "0100015eaa5eb0010000060000000000" - "000000000000010000000a000000003b" - "023380")) - - l1.start() - - assert l1.rpc.listchannels()['channels'] == [ - {'source': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', - 'destination': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518', - 'short_channel_id': '103x1x1', - 'public': False, - 'amount_msat': Millisatoshi(1000000000), - 'message_flags': 1, - 'channel_flags': 0, - 'active': False, - 'last_update': 1588223664, - 'base_fee_millisatoshi': 1, - 'fee_per_millionth': 10, - 'delay': 6, - 'htlc_minimum_msat': Millisatoshi(0), - 'htlc_maximum_msat': Millisatoshi(990000000), - # This store was created on an experimental branch (OPT_ONION_MESSAGES) - 'features': '80000000000000000000000000'}, - {'source': '0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518', - 'destination': '022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59', - 'short_channel_id': '103x1x1', - 'public': False, - 'amount_msat': Millisatoshi(1000000000), - 'message_flags': 1, - 'channel_flags': 1, - 'active': False, - 'last_update': 1588223664, - 'base_fee_millisatoshi': 1, - 'fee_per_millionth': 10, - 'delay': 6, - 'htlc_minimum_msat': Millisatoshi(0), - 'htlc_maximum_msat': Millisatoshi(990000000), - 'features': '80000000000000000000000000'}] - - @pytest.mark.developer("devtools are for devs anyway") def test_routetool(node_factory): """Test that route tool can see unpublished channels""" From 3817a690c9708b1873264582d66a39bdf358e0cc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:31 +0930 Subject: [PATCH 1473/1530] gossipd: actually validate gossip_store checksums at startup. We rewrite the file to compact it, but as a side effect we recalculate the checksums! Signed-off-by: Rusty Russell --- gossipd/gossip_store.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 873be7f8818a..29d3bc11032a 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -175,6 +175,16 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) continue; } + /* Check checksum (upgrade would overwrite, so do it now) */ + if (be32_to_cpu(hdr.crc) + != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) { + status_broken("gossip_store_compact_offline: checksum verification failed? %08x should be %08x", + be32_to_cpu(hdr.crc), + crc32c(be32_to_cpu(hdr.timestamp), msg + sizeof(hdr), msglen)); + tal_free(msg); + goto close_and_delete; + } + if (oldversion != version) { if (!upgrade_field(oldversion, rstate, &msg)) { tal_free(msg); From 6338758018ee5b199c56fbae8ffb09813d94dd6c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:31 +0930 Subject: [PATCH 1474/1530] gossmap: make API more robust against future changes. Many changes to gossmap (including the pending ones!) don't actually concern readers, as long as they obey certain rules: 1. Ignore unknown messages. 2. Treat all 16 upper bits of length as flags, ignore unknown ones. So now we split the version byte into MAJOR and MINOR, and you can ignore MINOR changes. We don't expose the internal version (for creating the map) programmatically: you should really hardcode what major version you understand! Signed-off-by: Rusty Russell --- common/gossip_store.h | 23 ++++++++++++++++++---- common/gossmap.c | 3 ++- common/test/run-route-specific.c | 2 +- common/test/run-route.c | 2 +- contrib/pyln-client/pyln/client/gossmap.py | 9 ++++----- devtools/create-gossipstore.c | 2 +- devtools/dump-gossipstore.c | 20 +++++++++++++++---- gossipd/gossip_store.c | 10 ++++++---- plugins/test/run-route-overlong.c | 2 +- 9 files changed, 51 insertions(+), 22 deletions(-) diff --git a/common/gossip_store.h b/common/gossip_store.h index c4f6f52bd01f..514f32096f54 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -11,7 +11,18 @@ struct gossip_rcvd_filter; /** * gossip_store -- On-disk storage related information */ -#define GOSSIP_STORE_VERSION 10 + +/* First byte of file is the version. + * + * Top three bits mean incompatible change. + * As of this writing, major == 0, minor == 10. + */ +#define GOSSIP_STORE_MAJOR_VERSION_MASK 0xE0 +#define GOSSIP_STORE_MINOR_VERSION_MASK 0x1F + +/* Extract version from first byte */ +#define GOSSIP_STORE_MAJOR_VERSION(verbyte) (((u8)(verbyte)) >> 5) +#define GOSSIP_STORE_MINOR_VERSION(verbyte) ((verbyte) & GOSSIP_STORE_MINOR_VERSION_MASK) /** * Bit of length we use to mark a deleted record. @@ -26,12 +37,16 @@ struct gossip_rcvd_filter; /** * Bit of length used to define a rate-limited record (do not rebroadcast) */ - #define GOSSIP_STORE_LEN_RATELIMIT_BIT 0x20000000U +#define GOSSIP_STORE_LEN_RATELIMIT_BIT 0x20000000U + +/** + * Full flags mask + */ +#define GOSSIP_STORE_FLAGS_MASK 0xFFFF0000U /* Mask for extracting just the length part of len field */ #define GOSSIP_STORE_LEN_MASK \ - (~(GOSSIP_STORE_LEN_PUSH_BIT | GOSSIP_STORE_LEN_DELETED_BIT | \ - GOSSIP_STORE_LEN_RATELIMIT_BIT)) + (~(GOSSIP_STORE_FLAGS_MASK)) /** * gossip_hdr -- On-disk format header. diff --git a/common/gossmap.c b/common/gossmap.c index 5d25b1122cac..e64fd983f3a3 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -672,7 +672,8 @@ static bool load_gossip_store(struct gossmap *map, size_t *num_rejected) if (map->mmap == MAP_FAILED) map->mmap = NULL; - if (map_u8(map, 0) != GOSSIP_STORE_VERSION) { + /* We only support major version 0 */ + if (GOSSIP_STORE_MAJOR_VERSION(map_u8(map, 0)) != 0) { close(map->fd); if (map->mmap) munmap(map->mmap, map->map_size); diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 3a1488c49377..8ced38d36cfc 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -179,7 +179,7 @@ int main(int argc, char *argv[]) int store_fd; struct gossmap *gossmap; const double riskfactor = 1.0; - char gossip_version = GOSSIP_STORE_VERSION; + char gossip_version = 10; char *gossipfilename; common_setup(argv[0]); diff --git a/common/test/run-route.c b/common/test/run-route.c index 33b3668c3760..398200d88bc5 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -176,7 +176,7 @@ int main(int argc, char *argv[]) int store_fd; struct gossmap *gossmap; const double riskfactor = 1.0; - char gossip_version = GOSSIP_STORE_VERSION; + char gossip_version = 10; char *gossipfilename; chainparams = chainparams_for_network("regtest"); diff --git a/contrib/pyln-client/pyln/client/gossmap.py b/contrib/pyln-client/pyln/client/gossmap.py index f58d49add0e7..4d72209e8e0c 100755 --- a/contrib/pyln-client/pyln/client/gossmap.py +++ b/contrib/pyln-client/pyln/client/gossmap.py @@ -9,13 +9,12 @@ import struct # These duplicate constants in lightning/common/gossip_store.h -GOSSIP_STORE_VERSIONS = [0x09, 0x0a] +GOSSIP_STORE_MAJOR_VERSION = (0 << 5) +GOSSIP_STORE_MAJOR_VERSION_MASK = 0xE0 GOSSIP_STORE_LEN_DELETED_BIT = 0x80000000 GOSSIP_STORE_LEN_PUSH_BIT = 0x40000000 GOSSIP_STORE_LEN_RATELIMIT_BIT = 0x20000000 -GOSSIP_STORE_LEN_MASK = (~(GOSSIP_STORE_LEN_PUSH_BIT - | GOSSIP_STORE_LEN_DELETED_BIT - | GOSSIP_STORE_LEN_RATELIMIT_BIT)) +GOSSIP_STORE_LEN_MASK = (0x0000FFFF) # These duplicate constants in lightning/gossipd/gossip_store_wiregen.h WIRE_GOSSIP_STORE_PRIVATE_CHANNEL = 4104 @@ -174,7 +173,7 @@ def __init__(self, store_filename: str = "gossip_store"): self.channels: Dict[ShortChannelId, GossmapChannel] = {} self._last_scid: Optional[str] = None version = self.store_file.read(1)[0] - if version not in GOSSIP_STORE_VERSIONS: + if (version & GOSSIP_STORE_MAJOR_VERSION_MASK) != GOSSIP_STORE_MAJOR_VERSION: raise ValueError("Invalid gossip store version {}".format(version)) self.bytes_read = 1 self.refresh() diff --git a/devtools/create-gossipstore.c b/devtools/create-gossipstore.c index 28d6f0843e98..d72de28e0efe 100644 --- a/devtools/create-gossipstore.c +++ b/devtools/create-gossipstore.c @@ -155,7 +155,7 @@ int main(int argc, char *argv[]) } else outfd = STDOUT_FILENO; - version = GOSSIP_STORE_VERSION; + version = ((0 << 5) | 10); if (!write_all(outfd, &version, sizeof(version))) err(1, "Writing version"); diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 1d5100d0a052..7370b9c22721 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -10,6 +10,10 @@ #include #include +/* Current versions we support */ +#define GSTORE_MAJOR 0 +#define GSTORE_MINOR 10 + int main(int argc, char *argv[]) { int fd; @@ -43,11 +47,19 @@ int main(int argc, char *argv[]) if (read(fd, &version, sizeof(version)) != sizeof(version)) errx(1, "Empty file"); - if (version != GOSSIP_STORE_VERSION) - warnx("UNSUPPORTED GOSSIP VERSION %u (expected %u)", - version, GOSSIP_STORE_VERSION); + if (GOSSIP_STORE_MAJOR_VERSION(version) != GSTORE_MAJOR) + errx(1, "Unsupported major gossip_version %u (expected %u)", + GOSSIP_STORE_MAJOR_VERSION(version), GSTORE_MAJOR); + + /* Unsupported minor just means we might not understand all fields, + * or all flags. */ + if (GOSSIP_STORE_MINOR_VERSION(version) != GSTORE_MINOR) + warnx("UNKNOWN GOSSIP minor VERSION %u (expected %u)", + GOSSIP_STORE_MINOR_VERSION(version), GSTORE_MINOR); - printf("GOSSIP VERSION %u\n", version); + printf("GOSSIP VERSION %u/%u\n", + GOSSIP_STORE_MINOR_VERSION(version), + GOSSIP_STORE_MAJOR_VERSION(version)); off = 1; while (read(fd, &hdr, sizeof(hdr)) == sizeof(hdr)) { diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 29d3bc11032a..64bc313bb74d 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -17,6 +17,8 @@ #include #define GOSSIP_STORE_TEMP_FILENAME "gossip_store.tmp" +/* We write it as major version 0, minor version 10 */ +#define GOSSIP_STORE_VER ((0 << 5) | 10) struct gossip_store { /* This is false when we're loading */ @@ -123,7 +125,7 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) int old_fd, new_fd; u64 oldlen, newlen; struct gossip_hdr hdr; - u8 oldversion, version = GOSSIP_STORE_VERSION; + u8 oldversion, version = GOSSIP_STORE_VER; struct stat st; old_fd = open(GOSSIP_STORE_FILENAME, O_RDWR); @@ -267,11 +269,11 @@ struct gossip_store *gossip_store_new(struct routing_state *rstate, if (read(gs->fd, &gs->version, sizeof(gs->version)) == sizeof(gs->version)) { /* Version match? All good */ - if (gs->version == GOSSIP_STORE_VERSION) + if (gs->version == GOSSIP_STORE_VER) return gs; status_unusual("Gossip store version %u not %u: removing", - gs->version, GOSSIP_STORE_VERSION); + gs->version, GOSSIP_STORE_VER); if (ftruncate(gs->fd, 0) != 0) status_failed(STATUS_FAIL_INTERNAL_ERROR, "Truncating store: %s", strerror(errno)); @@ -282,7 +284,7 @@ struct gossip_store *gossip_store_new(struct routing_state *rstate, strerror(errno)); } /* Empty file, write version byte */ - gs->version = GOSSIP_STORE_VERSION; + gs->version = GOSSIP_STORE_VER; if (write(gs->fd, &gs->version, sizeof(gs->version)) != sizeof(gs->version)) status_failed(STATUS_FAIL_INTERNAL_ERROR, diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 82dfdf7eb7d5..52faac965a6a 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -335,7 +335,7 @@ int main(int argc, char *argv[]) int store_fd; struct payment *p; struct payment_modifier **mods; - char gossip_version = GOSSIP_STORE_VERSION; + char gossip_version = 10; char *gossipfilename; common_setup(argv[0]); From daa5269ea259a855dac282c17c83e0eff09821b5 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:31 +0930 Subject: [PATCH 1475/1530] gossipd: bump gossip_store to indicate all channel_update have htlc_max. And in the next patch, gossipd will no longer put new ones in. Signed-off-by: Rusty Russell --- common/gossip_store.h | 2 +- gossipd/gossip_store.c | 30 ++++++++++++++++++----- tests/test_gossip.py | 54 ++++++++++++++++++++++++++++++++---------- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/common/gossip_store.h b/common/gossip_store.h index 514f32096f54..e74f7cba4718 100644 --- a/common/gossip_store.h +++ b/common/gossip_store.h @@ -15,7 +15,7 @@ struct gossip_rcvd_filter; /* First byte of file is the version. * * Top three bits mean incompatible change. - * As of this writing, major == 0, minor == 10. + * As of this writing, major == 0, minor == 11. */ #define GOSSIP_STORE_MAJOR_VERSION_MASK 0xE0 #define GOSSIP_STORE_MINOR_VERSION_MASK 0x1F diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 64bc313bb74d..5a3e9cd27206 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -17,8 +17,8 @@ #include #define GOSSIP_STORE_TEMP_FILENAME "gossip_store.tmp" -/* We write it as major version 0, minor version 10 */ -#define GOSSIP_STORE_VER ((0 << 5) | 10) +/* We write it as major version 0, minor version 11 */ +#define GOSSIP_STORE_VER ((0 << 5) | 11) struct gossip_store { /* This is false when we're loading */ @@ -103,10 +103,12 @@ static bool append_msg(int fd, const u8 *msg, u32 timestamp, return true; } -/* The upgrade from version 9 is a noop: we added the spam flag. */ +/* v9 added the GOSSIP_STORE_LEN_RATELIMIT_BIT. + * v10 removed any remaining non-htlc-max channel_update. + */ static bool can_upgrade(u8 oldversion) { - return oldversion == 9; + return oldversion == 9 || oldversion == 10; } static bool upgrade_field(u8 oldversion, @@ -114,6 +116,15 @@ static bool upgrade_field(u8 oldversion, u8 **msg) { assert(can_upgrade(oldversion)); + + if (oldversion == 10) { + /* Remove old channel_update with no htlc_maximum_msat */ + if (fromwire_peektype(*msg) == WIRE_CHANNEL_UPDATE + && tal_bytelen(*msg) == 130) { + *msg = tal_free(*msg); + } + } + return true; } @@ -182,7 +193,7 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) { status_broken("gossip_store_compact_offline: checksum verification failed? %08x should be %08x", be32_to_cpu(hdr.crc), - crc32c(be32_to_cpu(hdr.timestamp), msg + sizeof(hdr), msglen)); + crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)); tal_free(msg); goto close_and_delete; } @@ -193,6 +204,12 @@ static u32 gossip_store_compact_offline(struct routing_state *rstate) goto close_and_delete; } + /* It can tell us to delete record entirely. */ + if (msg == NULL) { + deleted++; + continue; + } + /* Recalc msglen and header */ msglen = tal_bytelen(msg); hdr.len = cpu_to_be32(msglen); @@ -723,7 +740,8 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) } if (checksum != crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)) { - bad = tal_fmt(tmpctx, "Checksum verification failed: should be %08x", crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)); + bad = tal_fmt(tmpctx, "Checksum verification failed: %08x should be %08x", + checksum, crc32c(be32_to_cpu(hdr.timestamp), msg, msglen)); goto badmsg; } diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 1f0d35b3095c..a4bacc9ff243 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1141,11 +1141,11 @@ def test_gossip_store_load(node_factory): "00000000" # timestamp "1005" # WIRE_GOSSIP_STORE_CHANNEL_AMOUNT "0000000001000000" - "00000082" # len - "fd421aeb" # csum + "0000008a" # len + "0c6aca0e" # csum "5b8d9b44" # timestamp "0102" # WIRE_CHANNEL_UPDATE - "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440000009000000000000003e8000003e800000001" + "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440100009000000000000003e8000003e8000000010000000000FFFFFF" "00000095" # len "f036515e" # csum "5aab817c" # timestamp @@ -1154,10 +1154,36 @@ def test_gossip_store_load(node_factory): l1.start() # May preceed the Started msg waited for in 'start'. - wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 770 bytes')) + wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 778 bytes')) assert not l1.daemon.is_in_log('gossip_store.*truncating') +def test_gossip_store_v10_upgrade(node_factory): + """We remove a channel_update without an htlc_maximum_msat""" + l1 = node_factory.get_node(start=False) + with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: + f.write(bytearray.fromhex("0a" # GOSSIP_STORE_VERSION + "000001b0" # len + "fea676e8" # csum + "5b8d9b44" # timestamp + "0100" # WIRE_CHANNEL_ANNOUNCEMENT + "bb8d7b6998cca3c2b3ce12a6bd73a8872c808bb48de2a30c5ad9cdf835905d1e27505755087e675fb517bbac6beb227629b694ea68f49d357458327138978ebfd7adfde1c69d0d2f497154256f6d5567a5cf2317c589e0046c0cc2b3e986cf9b6d3b44742bd57bce32d72cd1180a7f657795976130b20508b239976d3d4cdc4d0d6e6fbb9ab6471f664a662972e406f519eab8bce87a8c0365646df5acbc04c91540b4c7c518cec680a4a6af14dae1aca0fd5525220f7f0e96fcd2adef3c803ac9427fe71034b55a50536638820ef21903d09ccddd38396675b598587fa886ca711415c813fc6d69f46552b9a0a539c18f265debd0e2e286980a118ba349c216000043497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b50001021bf3de4e84e3d52f9a3e36fbdcd2c4e8dbf203b9ce4fc07c2f03be6c21d0c67503f113414ebdc6c1fb0f33c99cd5a1d09dd79e7fdf2468cf1fe1af6674361695d203801fd8ab98032f11cc9e4916dd940417082727077609d5c7f8cc6e9a3ad25dd102517164b97ab46cee3826160841a36c46a2b7b9c74da37bdc070ed41ba172033a" + "0000000a" # len + "99dc98b4" # csum + "00000000" # timestamp + "1005" # WIRE_GOSSIP_STORE_CHANNEL_AMOUNT + "0000000001000000" + "00000082" # len + "fd421aeb" # csum + "5b8d9b44" # timestamp + "0102" # WIRE_CHANNEL_UPDATE + "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440000009000000000000003e8000003e800000001")) + + l1.start() + # May preceed the Started msg waited for in 'start'. + wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Unupdated channel_announcement at 1.')) + + def test_gossip_store_load_announce_before_update(node_factory): """Make sure we can read canned gossip store with node_announce before update. This happens when a channel_update gets replaced, leaving node_announce before it""" l1 = node_factory.get_node(start=False) @@ -1173,25 +1199,27 @@ def test_gossip_store_load_announce_before_update(node_factory): "00000000" # timestamp "1005" # WIRE_GOSSIP_STORE_CHANNEL_AMOUNT "0000000001000000" - "80000082" # len (DELETED) - "fd421aeb" # csum + "8000008a" # len (DELETED) + "ca01ed56" # csum "5b8d9b44" # timestamp "0102" # WIRE_CHANNEL_UPDATE - "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440000009000000000000003e8000003e800000001" + # Note - msgflags set and htlc_max added by hand, so signature doesn't match (gossipd ignores) + "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440100009000000000000003e8000003e8000000010000000000FFFFFF" "00000095" # len "f036515e" # csum "5aab817c" # timestamp "0101" # WIRE_NODE_ANNOUNCEMENT "cf5d870bc7ecabcb7cd16898ef66891e5f0c6c5851bd85b670f03d325bc44d7544d367cd852e18ec03f7f4ff369b06860a3b12b07b29f36fb318ca11348bf8ec00005aab817c03f113414ebdc6c1fb0f33c99cd5a1d09dd79e7fdf2468cf1fe1af6674361695d23974b250757a7a6c6549544300000000000000000000000000000000000000000000000007010566933e2607" - "00000082" # len - "fd421aeb" # csum + "0000008a" # len + "0c6aca0e" # csum "5b8d9b44" # timestamp "0102" # WIRE_CHANNEL_UPDATE - "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440000009000000000000003e8000003e800000001")) + # Note - msgflags set and htlc_max added by hand, so signature doesn't match (gossipd ignores) + "1ea7c2eadf8a29eb8690511a519b5656e29aa0a853771c4e38e65c5abf43d907295a915e69e451f4c7a0c3dc13dd943cfbe3ae88c0b96667cd7d58955dbfedcf43497fd7f826957108f4a30fd9cec3aeba79972084e90ead01ea33090000000013a63c0000b500015b8d9b440100009000000000000003e8000003e8000000010000000000FFFFFF")) l1.start() # May preceed the Started msg waited for in 'start'. - wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 770 bytes')) + wait_for(lambda: l1.daemon.is_in_log(r'gossip_store: Read 1/1/1/0 cannounce/cupdate/nannounce/cdelete from store \(0 deleted\) in 778 bytes')) assert not l1.daemon.is_in_log('gossip_store.*truncating') # Extra sanity check if we can. @@ -1670,7 +1698,7 @@ def test_gossip_store_load_no_channel_update(node_factory): # A channel announcement with no channel_update. with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: - f.write(bytearray.fromhex("0a" # GOSSIP_STORE_VERSION + f.write(bytearray.fromhex("0b" # GOSSIP_STORE_VERSION "000001b0" # len "fea676e8" # csum "5b8d9b44" # timestamp @@ -1697,7 +1725,7 @@ def test_gossip_store_load_no_channel_update(node_factory): l1.rpc.call('dev-compact-gossip-store') with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), "rb") as f: - assert bytearray(f.read()) == bytearray.fromhex("0a") + assert bytearray(f.read()) == bytearray.fromhex("0b") @pytest.mark.developer("gossip without DEVELOPER=1 is slow") From 253b25522b4c0eb33064e0d35070d6148e053776 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:31 +0930 Subject: [PATCH 1476/1530] BOLT: update to version which requires option_channel_htlc_max. We will now simply reject old-style ones as invalid. Turns out the only trace we could find is a channel between two nodes unconnected to the rest of the network. Signed-off-by: Rusty Russell Changelog-Changed: Protocol: We now require all channel_update messages include htlc_maximum_msat (as per latest BOLTs) --- Makefile | 2 +- common/gossip_constants.h | 12 +++--- common/gossmap.c | 14 ++----- common/test/run-route-specific.c | 24 +++++------ common/test/run-route.c | 24 +++++------ devtools/create-gossipstore.c | 6 +-- devtools/mkgossip.c | 36 ++++------------ gossipd/gossip_generation.c | 17 ++++---- gossipd/routing.c | 16 ++----- hsmd/libhsmd.c | 4 +- plugins/test/run-route-overlong.c | 24 +++++------ tests/test_gossip.py | 2 +- wire/peer_wire.csv | 2 +- wire/test/run-peer-wire.c | 69 ++----------------------------- 14 files changed, 80 insertions(+), 172 deletions(-) diff --git a/Makefile b/Makefile index ae3b544a94da..29e3fb8a582c 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := 48fed66e26b80031d898c6492434fa9926237d64 +DEFAULT_BOLTVERSION := 6fee63fc342736a2eb7f6e856ae0d85807cc50ec # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/common/gossip_constants.h b/common/gossip_constants.h index 345f9126b231..c407f302381e 100644 --- a/common/gossip_constants.h +++ b/common/gossip_constants.h @@ -22,13 +22,15 @@ /* BOLT #7: * - * The `message_flags` bitfield is used to indicate the presence of optional - * fields in the `channel_update` message: - * | Bit Position | Name | Field | - * | ------------- | ------------------------- | ----------------...- | - * | 0 | `option_channel_htlc_max` | `htlc_maximum_msat` | + * The `message_flags` bitfield is used to provide additional details about the message: + * | Bit Position | Name | + * | ------------- | ---------------| + * | 0 | `must_be_one` | + * | 1 | `dont_forward` | */ +/* FIXME: This is the old name */ #define ROUTING_OPT_HTLC_MAX_MSAT (1 << 0) +#define ROUTING_OPT_DONT_FORWARD (1 << 1) /* BOLT #7: * diff --git a/common/gossmap.c b/common/gossmap.c index e64fd983f3a3..0849fcfc62b9 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -482,7 +482,7 @@ static struct gossmap_chan *add_channel(struct gossmap *map, * * [`u64`:`htlc_minimum_msat`] * * [`u32`:`fee_base_msat`] * * [`u32`:`fee_proportional_millionths`] - * * [`u64`:`htlc_maximum_msat`] (option_channel_htlc_max) + * * [`u64`:`htlc_maximum_msat`] */ static bool update_channel(struct gossmap *map, size_t cupdate_off) { @@ -509,15 +509,7 @@ static bool update_channel(struct gossmap *map, size_t cupdate_off) /* We round this *down*, since too-low min is more conservative */ hc.htlc_min = u64_to_fp16(map_be64(map, htlc_minimum_off), false); - /* I checked my node: 60189 of 62358 channel_update have - * htlc_maximum_msat, so we don't bother setting the rest to the - * channel size (which we don't even read from the gossip_store, let - * alone give up precious bytes to remember) */ - if (map_u8(map, message_flags_off) & 1) - hc.htlc_max - = u64_to_fp16(map_be64(map, htlc_maximum_off), true); - else - hc.htlc_max = 0xFFFF; + hc.htlc_max = u64_to_fp16(map_be64(map, htlc_maximum_off), true); chanflags = map_u8(map, channel_flags_off); hc.enabled = !(chanflags & 2); @@ -1225,7 +1217,7 @@ u8 *gossmap_chan_get_features(const tal_t *ctx, * * [`u64`:`htlc_minimum_msat`] * * [`u32`:`fee_base_msat`] * * [`u32`:`fee_proportional_millionths`] - * * [`u64`:`htlc_maximum_msat`] (option_channel_htlc_max) + * * [`u64`:`htlc_maximum_msat`] */ void gossmap_chan_get_update_details(const struct gossmap *map, const struct gossmap_chan *chan, diff --git a/common/test/run-route-specific.c b/common/test/run-route-specific.c index 8ced38d36cfc..eddf83ba4d06 100644 --- a/common/test/run-route-specific.c +++ b/common/test/run-route-specific.c @@ -79,18 +79,18 @@ static void update_connection(int store_fd, if (!short_channel_id_from_str(shortid, strlen(shortid), &scid)) abort(); - msg = towire_channel_update_option_channel_htlc_max(tmpctx, - &dummy_sig, - &chainparams->genesis_blockhash, - &scid, 0, - ROUTING_OPT_HTLC_MAX_MSAT, - node_id_idx(from, to) - + (disable ? ROUTING_FLAGS_DISABLED : 0), - delay, - min, - base_fee, - proportional_fee, - max); + msg = towire_channel_update(tmpctx, + &dummy_sig, + &chainparams->genesis_blockhash, + &scid, 0, + ROUTING_OPT_HTLC_MAX_MSAT, + node_id_idx(from, to) + + (disable ? ROUTING_FLAGS_DISABLED : 0), + delay, + min, + base_fee, + proportional_fee, + max); write_to_store(store_fd, msg); } diff --git a/common/test/run-route.c b/common/test/run-route.c index 398200d88bc5..e41b141264dc 100644 --- a/common/test/run-route.c +++ b/common/test/run-route.c @@ -70,18 +70,18 @@ static void update_connection(int store_fd, memcpy(&scid, from, sizeof(scid) / 2); memcpy((char *)&scid + sizeof(scid) / 2, to, sizeof(scid) / 2); - msg = towire_channel_update_option_channel_htlc_max(tmpctx, - &dummy_sig, - &chainparams->genesis_blockhash, - &scid, 0, - ROUTING_OPT_HTLC_MAX_MSAT, - node_id_idx(from, to) - + (disable ? ROUTING_FLAGS_DISABLED : 0), - delay, - AMOUNT_MSAT(0), - base_fee, - proportional_fee, - AMOUNT_MSAT(100000 * 1000)); + msg = towire_channel_update(tmpctx, + &dummy_sig, + &chainparams->genesis_blockhash, + &scid, 0, + ROUTING_OPT_HTLC_MAX_MSAT, + node_id_idx(from, to) + + (disable ? ROUTING_FLAGS_DISABLED : 0), + delay, + AMOUNT_MSAT(0), + base_fee, + proportional_fee, + AMOUNT_MSAT(100000 * 1000)); write_to_store(store_fd, msg); } diff --git a/devtools/create-gossipstore.c b/devtools/create-gossipstore.c index d72de28e0efe..45b61b09f587 100644 --- a/devtools/create-gossipstore.c +++ b/devtools/create-gossipstore.c @@ -64,12 +64,12 @@ static u32 get_update_timestamp(const u8 *msg, struct short_channel_id *scid) u8 u8_ignore; u16 u16_ignore; u32 u32_ignore; - struct amount_msat msat; + struct amount_msat msat_ignore; if (fromwire_channel_update(msg, &sig, &chain_hash, scid, ×tamp, &u8_ignore, &u8_ignore, - &u16_ignore, &msat, &u32_ignore, - &u32_ignore)) + &u16_ignore, &msat_ignore, &u32_ignore, + &u32_ignore, &msat_ignore)) return timestamp; errx(1, "Invalid channel_update"); } diff --git a/devtools/mkgossip.c b/devtools/mkgossip.c index 791bd797146b..74fca27b6749 100644 --- a/devtools/mkgossip.c +++ b/devtools/mkgossip.c @@ -23,10 +23,9 @@ static bool verbose = false; struct update_opts { u32 timestamp; u32 cltv_expiry_delta; - struct amount_msat min; + struct amount_msat min, max; struct amount_msat feebase; u32 fee_proportional_millionths; - struct amount_msat *max; u8 *addresses; }; @@ -52,14 +51,9 @@ static int parse_options(char *argv[], struct update_opts *opts, if (!opts->fee_proportional_millionths) errx(1, "Bad %s.fee_proportional_millionths", desc); - if (streq(argv[argnum], "")) - opts->max = NULL; - else { - opts->max = tal(NULL, struct amount_msat); - if (!parse_amount_msat(opts->max, - argv[argnum], strlen(argv[argnum]))) - errx(1, "Bad %s.max", desc); - } + if (!parse_amount_msat(&opts->max, + argv[argnum], strlen(argv[argnum]))) + errx(1, "Bad %s.max", desc); argnum++; opts->addresses = tal_hexdata(NULL, argv[argnum], strlen(argv[argnum])); if (!opts->addresses) @@ -139,8 +133,7 @@ static void print_update(const struct bitcoin_blkid *chainhash, u8 *cupdate; memset(&sig, 0, sizeof(sig)); - if (opts->max) { - cupdate = towire_channel_update_option_channel_htlc_max + cupdate = towire_channel_update (NULL, &sig, chainhash, scid, opts->timestamp, ROUTING_OPT_HTLC_MAX_MSAT, is_lesser_key ? 0 : ROUTING_FLAGS_DIRECTION, @@ -148,17 +141,7 @@ static void print_update(const struct bitcoin_blkid *chainhash, opts->min, opts->feebase.millisatoshis, /* Raw: devtools code */ opts->fee_proportional_millionths, - *opts->max); - } else { - cupdate = towire_channel_update - (NULL, &sig, chainhash, scid, opts->timestamp, - 0, - is_lesser_key ? 0 : ROUTING_FLAGS_DIRECTION, - opts->cltv_expiry_delta, - opts->min, - opts->feebase.millisatoshis, /* Raw: devtools code */ - opts->fee_proportional_millionths); - } + opts->max); sha256_double(&hash, cupdate + channel_update_offset, tal_count(cupdate) - channel_update_offset); sign_hash(privkey, &hash, &sig); @@ -169,7 +152,7 @@ static void print_update(const struct bitcoin_blkid *chainhash, printf(" short_channel_id=%s\n", short_channel_id_to_str(NULL, scid)); printf(" timestamp=%u\n", opts->timestamp); printf(" message_flags=%u\n", - opts->max ? ROUTING_OPT_HTLC_MAX_MSAT : 0); + ROUTING_OPT_HTLC_MAX_MSAT); printf(" channel_flags=%u\n", is_lesser_key ? 0 : ROUTING_FLAGS_DIRECTION); printf(" cltv_expiry_delta=%u\n", @@ -180,9 +163,8 @@ static void print_update(const struct bitcoin_blkid *chainhash, opts->feebase.millisatoshis); /* Raw: devtools code */ printf(" fee_proportional_millionths=%u\n", opts->fee_proportional_millionths); - if (opts->max) - printf(" htlc_maximum_msat=%"PRIu64"\n", - opts->max->millisatoshis); /* Raw: devtools code */ + printf(" htlc_maximum_msat=%"PRIu64"\n", + opts->max.millisatoshis); /* Raw: devtools code */ printf("# crc32c checksum: %08x\n", crc32_of_update(cupdate)); } diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index e5ab5eeab727..1ac944110169 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -539,17 +539,18 @@ static u8 *create_unsigned_update(const tal_t *ctx, /* BOLT #7: * - * The `message_flags` bitfield is used to indicate the presence of - * optional fields in the `channel_update` message: + * The `message_flags` bitfield is used to provide additional + * details about the message: * - *| Bit Position | Name | Field | - *... - *| 0 | `option_channel_htlc_max` | `htlc_maximum_msat` | + * | Bit Position | Name | + * | ------------- | ---------------| + * | 0 | `must_be_one` | + * | 1 | `dont_forward` | */ - message_flags = 0 | ROUTING_OPT_HTLC_MAX_MSAT; + message_flags = ROUTING_OPT_HTLC_MAX_MSAT; /* We create an update with a dummy signature and timestamp. */ - return towire_channel_update_option_channel_htlc_max(ctx, + return towire_channel_update(ctx, &dummy_sig, /* sig set later */ &chainparams->genesis_blockhash, scid, @@ -730,7 +731,7 @@ void refresh_local_channel(struct daemon *daemon, if (!prev) return; - if (!fromwire_channel_update_option_channel_htlc_max(prev, + if (!fromwire_channel_update(prev, &signature, &chain_hash, &short_channel_id, ×tamp, &message_flags, &channel_flags, diff --git a/gossipd/routing.c b/gossipd/routing.c index 8e569dec8a89..8e8a913ad4e6 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1298,16 +1298,7 @@ bool routing_add_channel_update(struct routing_state *rstate, if (taken(update)) tal_steal(tmpctx, update); - if (!fromwire_channel_update(update, &signature, &chain_hash, - &short_channel_id, ×tamp, - &message_flags, &channel_flags, - &expiry, &htlc_minimum, &fee_base_msat, - &fee_proportional_millionths)) - return false; - /* If it's flagged as containing the optional field, reparse for - * the optional field */ - if ((message_flags & ROUTING_OPT_HTLC_MAX_MSAT) && - !fromwire_channel_update_option_channel_htlc_max( + if (!fromwire_channel_update( update, &signature, &chain_hash, &short_channel_id, ×tamp, &message_flags, &channel_flags, @@ -1557,7 +1548,7 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 timestamp; u8 message_flags, channel_flags; u16 expiry; - struct amount_msat htlc_minimum; + struct amount_msat htlc_minimum, htlc_maximum; u32 fee_base_msat; u32 fee_proportional_millionths; struct bitcoin_blkid chain_hash; @@ -1571,7 +1562,8 @@ u8 *handle_channel_update(struct routing_state *rstate, const u8 *update TAKES, ×tamp, &message_flags, &channel_flags, &expiry, &htlc_minimum, &fee_base_msat, - &fee_proportional_millionths)) { + &fee_proportional_millionths, + &htlc_maximum)) { warn = towire_warningfmt(rstate, NULL, "Malformed channel_update %s", tal_hex(tmpctx, serialized)); diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index ad2cb627f3e1..aedb6021fb61 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -972,7 +972,7 @@ static u8 *handle_channel_update_sig(struct hsmd_client *c, const u8 *msg_in) if (!fromwire_hsmd_cupdate_sig_req(tmpctx, msg_in, &cu)) return hsmd_status_malformed_request(c, msg_in); - if (!fromwire_channel_update_option_channel_htlc_max(cu, &sig, + if (!fromwire_channel_update(cu, &sig, &chain_hash, &scid, ×tamp, &message_flags, &channel_flags, &cltv_expiry_delta, &htlc_minimum, &fee_base_msat, @@ -989,7 +989,7 @@ static u8 *handle_channel_update_sig(struct hsmd_client *c, const u8 *msg_in) sign_hash(&node_pkey, &hash, &sig); - cu = towire_channel_update_option_channel_htlc_max(tmpctx, &sig, &chain_hash, + cu = towire_channel_update(tmpctx, &sig, &chain_hash, &scid, timestamp, message_flags, channel_flags, cltv_expiry_delta, htlc_minimum, fee_base_msat, fee_proportional_mill, diff --git a/plugins/test/run-route-overlong.c b/plugins/test/run-route-overlong.c index 52faac965a6a..870356fa3294 100644 --- a/plugins/test/run-route-overlong.c +++ b/plugins/test/run-route-overlong.c @@ -260,18 +260,18 @@ static void update_connection(int store_fd, /* So valgrind doesn't complain */ memset(&dummy_sig, 0, sizeof(dummy_sig)); - msg = towire_channel_update_option_channel_htlc_max(tmpctx, - &dummy_sig, - &chainparams->genesis_blockhash, - scid, 0, - ROUTING_OPT_HTLC_MAX_MSAT, - node_id_idx(from, to) - + (disable ? ROUTING_FLAGS_DISABLED : 0), - delay, - min, - base_fee, - proportional_fee, - max); + msg = towire_channel_update(tmpctx, + &dummy_sig, + &chainparams->genesis_blockhash, + scid, 0, + ROUTING_OPT_HTLC_MAX_MSAT, + node_id_idx(from, to) + + (disable ? ROUTING_FLAGS_DISABLED : 0), + delay, + min, + base_fee, + proportional_fee, + max); write_to_store(store_fd, msg); } diff --git a/tests/test_gossip.py b/tests/test_gossip.py index a4bacc9ff243..959baac91b6f 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -1160,7 +1160,7 @@ def test_gossip_store_load(node_factory): def test_gossip_store_v10_upgrade(node_factory): """We remove a channel_update without an htlc_maximum_msat""" - l1 = node_factory.get_node(start=False) + l1 = node_factory.get_node(start=False, allow_broken_log=True) with open(os.path.join(l1.daemon.lightning_dir, TEST_NETWORK, 'gossip_store'), 'wb') as f: f.write(bytearray.fromhex("0a" # GOSSIP_STORE_VERSION "000001b0" # len diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 54f48115666a..d9c7dcb590b1 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -291,7 +291,7 @@ msgdata,channel_update,cltv_expiry_delta,u16, msgdata,channel_update,htlc_minimum_msat,u64, msgdata,channel_update,fee_base_msat,u32, msgdata,channel_update,fee_proportional_millionths,u32, -msgdata,channel_update,htlc_maximum_msat,u64,,option_channel_htlc_max +msgdata,channel_update,htlc_maximum_msat,u64, msgtype,query_short_channel_ids,261,gossip_queries msgdata,query_short_channel_ids,chain_hash,chain_hash, msgdata,query_short_channel_ids,len,u16, diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index d9df608b812b..c6e5393cc897 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -133,18 +133,6 @@ struct msg_revoke_and_ack { struct pubkey next_per_commitment_point; }; struct msg_channel_update { - secp256k1_ecdsa_signature signature; - u32 timestamp; - u8 message_flags; - u8 channel_flags; - u16 expiry; - struct amount_msat htlc_minimum_msat; - u32 fee_base_msat; - u32 fee_proportional_millionths; - struct bitcoin_blkid chain_hash; - struct short_channel_id short_channel_id; -}; -struct msg_channel_update_opt_htlc_max { secp256k1_ecdsa_signature signature; u32 timestamp; u8 message_flags; @@ -406,25 +394,9 @@ static struct msg_node_announcement *fromwire_struct_node_announcement(const tal } static void *towire_struct_channel_update(const tal_t *ctx, - const struct msg_channel_update *s) + const struct msg_channel_update *s) { return towire_channel_update(ctx, - &s->signature, - &s->chain_hash, - &s->short_channel_id, - s->timestamp, - s->message_flags, - s->channel_flags, - s->expiry, - s->htlc_minimum_msat, - s->fee_base_msat, - s->fee_proportional_millionths); -} - -static void *towire_struct_channel_update_opt_htlc_max(const tal_t *ctx, - const struct msg_channel_update_opt_htlc_max *s) -{ - return towire_channel_update_option_channel_htlc_max(ctx, &s->signature, &s->chain_hash, &s->short_channel_id, @@ -437,31 +409,13 @@ static void *towire_struct_channel_update_opt_htlc_max(const tal_t *ctx, s->fee_proportional_millionths, s->htlc_maximum_msat); } -static struct msg_channel_update *fromwire_struct_channel_update(const tal_t *ctx, const void *p) + +static struct msg_channel_update +*fromwire_struct_channel_update(const tal_t *ctx, const void *p) { struct msg_channel_update *s = tal(ctx, struct msg_channel_update); if (fromwire_channel_update(p, - &s->signature, - &s->chain_hash, - &s->short_channel_id, - &s->timestamp, - &s->message_flags, - &s->channel_flags, - &s->expiry, - &s->htlc_minimum_msat, - &s->fee_base_msat, - &s->fee_proportional_millionths)) - return s; - return tal_free(s); -} - -static struct msg_channel_update_opt_htlc_max -*fromwire_struct_channel_update_opt_htlc_max(const tal_t *ctx, const void *p) -{ - struct msg_channel_update_opt_htlc_max *s = tal(ctx, struct msg_channel_update_opt_htlc_max); - - if (fromwire_channel_update_option_channel_htlc_max(p, &s->signature, &s->chain_hash, &s->short_channel_id, @@ -938,13 +892,6 @@ static bool channel_update_eq(const struct msg_channel_update *a, short_channel_id_eq(&a->short_channel_id, &b->short_channel_id); } -static bool channel_update_opt_htlc_max_eq(const struct msg_channel_update_opt_htlc_max *a, - const struct msg_channel_update_opt_htlc_max *b) -{ - return eq_upto(a, b, short_channel_id) && - short_channel_id_eq(&a->short_channel_id, &b->short_channel_id); -} - static bool accept_channel_eq(const struct msg_accept_channel *a, const struct msg_accept_channel *b) { @@ -1024,7 +971,6 @@ int main(int argc, char *argv[]) struct msg_revoke_and_ack raa, *raa2; struct msg_open_channel oc, *oc2; struct msg_channel_update cu, *cu2; - struct msg_channel_update_opt_htlc_max cu_opt_htlc_max, *cu_opt_htlc_max2; struct msg_accept_channel ac, *ac2; struct msg_update_add_htlc uah, *uah2; struct msg_node_announcement na, *na2; @@ -1185,13 +1131,6 @@ int main(int argc, char *argv[]) assert(channel_update_eq(&cu, cu2)); test_corruption(&cu, cu2, channel_update); - memset(&cu_opt_htlc_max, 2, sizeof(cu_opt_htlc_max)); - - msg = towire_struct_channel_update_opt_htlc_max(ctx, &cu_opt_htlc_max); - cu_opt_htlc_max2 = fromwire_struct_channel_update_opt_htlc_max(ctx, msg); - assert(channel_update_opt_htlc_max_eq(&cu_opt_htlc_max, cu_opt_htlc_max2)); - test_corruption(&cu_opt_htlc_max, cu_opt_htlc_max2, channel_update_opt_htlc_max); - memset(&ac, 2, sizeof(ac)); set_pubkey(&ac.funding_pubkey); set_pubkey(&ac.revocation_basepoint); From bb49e1bea586991f6e4cedeb277c3aece2593b25 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:31 +0930 Subject: [PATCH 1477/1530] common: assume htlc_maximum_msat, don't check bit any more. Signed-off-by: Rusty Russell --- common/gossmap.c | 10 +++------- common/gossmap.h | 1 - contrib/pyln-testing/pyln/testing/gossip.py | 6 +----- gossipd/routing.c | 19 ++++--------------- plugins/topology.c | 6 ++---- 5 files changed, 10 insertions(+), 32 deletions(-) diff --git a/common/gossmap.c b/common/gossmap.c index 0849fcfc62b9..b5d326938d99 100644 --- a/common/gossmap.c +++ b/common/gossmap.c @@ -1228,7 +1228,6 @@ void gossmap_chan_get_update_details(const struct gossmap *map, u32 *fee_base_msat, u32 *fee_proportional_millionths, struct amount_msat *htlc_minimum_msat, - /* iff message_flags & 1 */ struct amount_msat *htlc_maximum_msat) { /* Note that first two bytes are message type */ @@ -1241,18 +1240,15 @@ void gossmap_chan_get_update_details(const struct gossmap *map, const size_t fee_base_off = htlc_minimum_off + 8; const size_t fee_prop_off = fee_base_off + 4; const size_t htlc_maximum_off = fee_prop_off + 4; - u8 mflags; assert(gossmap_chan_set(chan, dir)); if (timestamp) *timestamp = map_be32(map, timestamp_off); - /* We need this (below), even if they don't want it */ - mflags = map_u8(map, message_flags_off); - if (message_flags) - *message_flags = mflags; if (channel_flags) *channel_flags = map_u8(map, channel_flags_off); + if (message_flags) + *message_flags = map_u8(map, message_flags_off); if (fee_base_msat) *fee_base_msat = map_be32(map, fee_base_off); if (fee_proportional_millionths) @@ -1260,7 +1256,7 @@ void gossmap_chan_get_update_details(const struct gossmap *map, if (htlc_minimum_msat) *htlc_minimum_msat = amount_msat(map_be64(map, htlc_minimum_off)); - if (htlc_maximum_msat && (mflags & 1)) + if (htlc_maximum_msat) *htlc_maximum_msat = amount_msat(map_be64(map, htlc_maximum_off)); } diff --git a/common/gossmap.h b/common/gossmap.h index 38cee0425cc3..6b08ef684a87 100644 --- a/common/gossmap.h +++ b/common/gossmap.h @@ -161,7 +161,6 @@ void gossmap_chan_get_update_details(const struct gossmap *map, u32 *fee_base_msat, u32 *fee_proportional_millionths, struct amount_msat *htlc_minimum_msat, - /* iff message_flags & 1 */ struct amount_msat *htlc_maximum_msat); /* Given a struct node, get the nth channel, and tell us if we're half[0/1]. diff --git a/contrib/pyln-testing/pyln/testing/gossip.py b/contrib/pyln-testing/pyln/testing/gossip.py index 36976e462220..eb7ce99f4480 100644 --- a/contrib/pyln-testing/pyln/testing/gossip.py +++ b/contrib/pyln-testing/pyln/testing/gossip.py @@ -251,11 +251,7 @@ def from_bytes(cls, b: io.BytesIO) -> "ChannelUpdate": (cu.htlc_minimum_msat,) = struct.unpack("!Q", b.read(8)) (cu.fee_base_msat,) = struct.unpack("!I", b.read(4)) (cu.fee_proportional_millionths,) = struct.unpack("!I", b.read(4)) - t = b.read(8) - if len(t) == 8: - (cu.htlc_maximum_msat,) = struct.unpack("!Q", t) - else: - cu.htlc_maximum_msat = None + (cu.htlc_maximum_msat,) = struct.unpack("!Q", b.read(8)) return cu diff --git a/gossipd/routing.c b/gossipd/routing.c index 8e8a913ad4e6..08aef559ebf9 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1322,21 +1322,10 @@ bool routing_add_channel_update(struct routing_state *rstate, sat = uc->sat; } - if (message_flags & ROUTING_OPT_HTLC_MAX_MSAT) { - /* Reject update if the `htlc_maximum_msat` is greater - * than the total available channel satoshis */ - if (amount_msat_greater_sat(htlc_maximum, sat)) - return false; - } else { - /* If not indicated, set htlc_max_msat to channel capacity */ - if (!amount_sat_to_msat(&htlc_maximum, sat)) { - status_peer_broken(peer ? &peer->id : NULL, - "Channel capacity %s overflows!", - type_to_string(tmpctx, struct amount_sat, - &sat)); - return false; - } - } + /* Reject update if the `htlc_maximum_msat` is greater + * than the total available channel satoshis */ + if (amount_msat_greater_sat(htlc_maximum, sat)) + return false; /* Check timestamp is sane (unless from store). */ if (!index && !timestamp_reasonable(rstate, timestamp)) { diff --git a/plugins/topology.c b/plugins/topology.c index d20e65a8bcc9..116ba81e6846 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -280,10 +280,8 @@ static void json_add_halfchan(struct json_stream *response, json_add_num(response, "delay", c->half[dir].delay); json_add_amount_msat_only(response, "htlc_minimum_msat", htlc_minimum_msat); - - if (message_flags & 1) - json_add_amount_msat_only(response, "htlc_maximum_msat", - htlc_maximum_msat); + json_add_amount_msat_only(response, "htlc_maximum_msat", + htlc_maximum_msat); json_add_hex_talarr(response, "features", chanfeatures); json_object_end(response); } From bfc21cbb55ac5b5a423ccf1e6144948594363ede Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:32 +0930 Subject: [PATCH 1478/1530] gossipd: set no_forward bit on channel_update for private channels. Signed-off-by: Rusty Russell Changelog-Added: Protocol: We now set the `dont_forward` bit on private channel_update's message_flags (as per latest BOLTs). --- channeld/channeld.c | 18 ++++++++++-------- channeld/channeld_wire.csv | 1 + gossipd/gossip_generation.c | 15 +++++++++++---- gossipd/gossipd_wire.csv | 1 + gossipd/test/run-check_node_announcement.c | 2 +- gossipd/test/run-crc32_of_update.c | 2 +- lightningd/gossip_control.c | 8 +++++--- 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 5e20d71e773d..51a39c6fe99f 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -369,14 +369,16 @@ static void send_channel_update(struct peer *peer, int disable_flag) assert(peer->short_channel_ids[LOCAL].u64); msg = towire_channeld_local_channel_update(NULL, - &peer->short_channel_ids[LOCAL], - disable_flag - == ROUTING_FLAGS_DISABLED, - peer->cltv_delta, - peer->htlc_minimum_msat, - peer->fee_base, - peer->fee_per_satoshi, - peer->htlc_maximum_msat); + &peer->short_channel_ids[LOCAL], + disable_flag + == ROUTING_FLAGS_DISABLED, + peer->cltv_delta, + peer->htlc_minimum_msat, + peer->fee_base, + peer->fee_per_satoshi, + peer->htlc_maximum_msat, + peer->channel_flags + & CHANNEL_FLAGS_ANNOUNCE_CHANNEL); wire_sync_write(MASTER_FD, take(msg)); } diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 78abd73416ae..8827fa7b8cd1 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -256,6 +256,7 @@ msgdata,channeld_local_channel_update,htlc_minimum_msat,amount_msat, msgdata,channeld_local_channel_update,fee_base_msat,u32, msgdata,channeld_local_channel_update,fee_proportional_millionths,u32, msgdata,channeld_local_channel_update,htlc_maximum_msat,amount_msat, +msgdata,channeld_local_channel_update,public,bool, # Channeld: tell gossipd about our channel_announcement msgtype,channeld_local_channel_announcement,1014 diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 1ac944110169..44c4afa8bc93 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -513,7 +513,8 @@ static u8 *create_unsigned_update(const tal_t *ctx, struct amount_msat htlc_minimum, struct amount_msat htlc_maximum, u32 fee_base_msat, - u32 fee_proportional_millionths) + u32 fee_proportional_millionths, + bool public) { secp256k1_ecdsa_signature dummy_sig; u8 message_flags, channel_flags; @@ -548,6 +549,8 @@ static u8 *create_unsigned_update(const tal_t *ctx, * | 1 | `dont_forward` | */ message_flags = ROUTING_OPT_HTLC_MAX_MSAT; + if (!public) + message_flags |= ROUTING_OPT_DONT_FORWARD; /* We create an update with a dummy signature and timestamp. */ return towire_channel_update(ctx, @@ -771,7 +774,8 @@ void refresh_local_channel(struct daemon *daemon, false, cltv_expiry_delta, htlc_minimum, htlc_maximum, fee_base_msat, - fee_proportional_millionths); + fee_proportional_millionths, + !(message_flags & ROUTING_OPT_DONT_FORWARD)); sign_timestamp_and_apply_update(daemon, chan, direction, take(update)); } @@ -788,6 +792,7 @@ void handle_local_channel_update(struct daemon *daemon, const u8 *msg) int direction; u8 *unsigned_update; const struct half_chan *hc; + bool public; if (!fromwire_gossipd_local_channel_update(msg, &id, @@ -797,7 +802,8 @@ void handle_local_channel_update(struct daemon *daemon, const u8 *msg) &htlc_minimum, &fee_base_msat, &fee_proportional_millionths, - &htlc_maximum)) { + &htlc_maximum, + &public)) { master_badmsg(WIRE_GOSSIPD_LOCAL_CHANNEL_UPDATE, msg); } @@ -822,7 +828,8 @@ void handle_local_channel_update(struct daemon *daemon, const u8 *msg) disable, cltv_expiry_delta, htlc_minimum, htlc_maximum, fee_base_msat, - fee_proportional_millionths); + fee_proportional_millionths, + public); hc = &chan->half[direction]; diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index c058a1dfb9f9..35594b63364d 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -103,6 +103,7 @@ msgdata,gossipd_local_channel_update,htlc_minimum_msat,amount_msat, msgdata,gossipd_local_channel_update,fee_base_msat,u32, msgdata,gossipd_local_channel_update,fee_proportional_millionths,u32, msgdata,gossipd_local_channel_update,htlc_maximum_msat,amount_msat, +msgdata,gossipd_local_channel_update,public,bool, # Send this channel_announcement msgtype,gossipd_local_channel_announcement,3006 diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index b7245bee3975..a1119f77549a 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -34,7 +34,7 @@ void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_update */ -bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) +bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED, bool *public UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } /* Generated stub for fromwire_gossipd_used_local_channel_update */ bool fromwire_gossipd_used_local_channel_update(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED) diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 0420958edd28..2a8c197f64b7 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -55,7 +55,7 @@ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id bool fromwire_gossipd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED) { fprintf(stderr, "fromwire_gossipd_dev_set_max_scids_encode_size called!\n"); abort(); } /* Generated stub for fromwire_gossipd_local_channel_update */ -bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) +bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct node_id *id UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED, bool *public UNNEEDED) { fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } /* Generated stub for fromwire_gossipd_used_local_channel_update */ bool fromwire_gossipd_used_local_channel_update(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED) diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 5f3c933cdb89..23b5eb8ee17c 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -286,7 +286,7 @@ void tell_gossipd_local_channel_update(struct lightningd *ld, const u8 *msg) { struct short_channel_id scid; - bool disable; + bool disable, public; u16 cltv_expiry_delta; struct amount_msat htlc_minimum_msat; u32 fee_base_msat, fee_proportional_millionths; @@ -297,7 +297,7 @@ void tell_gossipd_local_channel_update(struct lightningd *ld, &htlc_minimum_msat, &fee_base_msat, &fee_proportional_millionths, - &htlc_maximum_msat)) { + &htlc_maximum_msat, &public)) { channel_internal_error(channel, "bad channeld_local_channel_update %s", tal_hex(channel, msg)); @@ -317,7 +317,9 @@ void tell_gossipd_local_channel_update(struct lightningd *ld, cltv_expiry_delta, htlc_minimum_msat, fee_base_msat, - fee_proportional_millionths, htlc_maximum_msat))); + fee_proportional_millionths, + htlc_maximum_msat, + public))); } void tell_gossipd_local_channel_announce(struct lightningd *ld, From 0d94d2d269c760721a49d2e0c8951371e9a435c2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:32 +0930 Subject: [PATCH 1479/1530] gossipd: batch outpoints spent, add block height. It's a bit more optimal, and tells gossipd exactly what height the spend occurred at (with multiple blocks, it's not always the current height!). It will need that next patch. Signed-off-by: Rusty Russell --- gossipd/gossipd.c | 27 ++++++++++++++++----------- gossipd/gossipd_wire.csv | 8 +++++--- lightningd/chaintopology.c | 11 +++-------- lightningd/gossip_control.c | 14 +++++++++----- lightningd/gossip_control.h | 5 +++-- 5 files changed, 36 insertions(+), 29 deletions(-) diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 26be95bd6090..395f303b4cc9 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -927,22 +927,27 @@ static void handle_new_lease_rates(struct daemon *daemon, const u8 *msg) /*~ This is where lightningd tells us that a channel's funding transaction has * been spent. */ -static void handle_outpoint_spent(struct daemon *daemon, const u8 *msg) +static void handle_outpoints_spent(struct daemon *daemon, const u8 *msg) { - struct short_channel_id scid; - struct chan *chan; + struct short_channel_id *scids; + u32 blockheight; struct routing_state *rstate = daemon->rstate; - if (!fromwire_gossipd_outpoint_spent(msg, &scid)) - master_badmsg(WIRE_GOSSIPD_OUTPOINT_SPENT, msg); - chan = get_channel(rstate, &scid); - if (chan) { + if (!fromwire_gossipd_outpoints_spent(msg, msg, &blockheight, &scids)) + master_badmsg(WIRE_GOSSIPD_OUTPOINTS_SPENT, msg); + + for (size_t i = 0; i < tal_count(scids); i++) { + struct chan *chan = get_channel(rstate, &scids[i]); + + if (!chan) + continue; + status_debug( "Deleting channel %s due to the funding outpoint being " "spent", - type_to_string(msg, struct short_channel_id, &scid)); + type_to_string(msg, struct short_channel_id, &scids[i])); /* Suppress any now-obsolete updates/announcements */ - add_to_txout_failures(rstate, &scid); + add_to_txout_failures(rstate, &scids[i]); remove_channel_from_store(rstate, chan); /* Freeing is sufficient since everything else is allocated off * of the channel and this takes care of unregistering @@ -999,8 +1004,8 @@ static struct io_plan *recv_req(struct io_conn *conn, handle_txout_reply(daemon, msg); goto done; - case WIRE_GOSSIPD_OUTPOINT_SPENT: - handle_outpoint_spent(daemon, msg); + case WIRE_GOSSIPD_OUTPOINTS_SPENT: + handle_outpoints_spent(daemon, msg); goto done; case WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE: diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 35594b63364d..2c266584e1b0 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -42,9 +42,11 @@ msgdata,gossipd_get_txout_reply,satoshis,amount_sat, msgdata,gossipd_get_txout_reply,len,u16, msgdata,gossipd_get_txout_reply,outscript,u8,len -# master -> gossipd: a potential funding outpoint was spent, please forget the eventual channel -msgtype,gossipd_outpoint_spent,3024 -msgdata,gossipd_outpoint_spent,short_channel_id,short_channel_id, +# master -> gossipd: these potential funding outpoints were spent, please forget any channels +msgtype,gossipd_outpoints_spent,3024 +msgdata,gossipd_outpoints_spent,blockheight,u32, +msgdata,gossipd_outpoints_spent,len,u32, +msgdata,gossipd_outpoints_spent,short_channel_id,short_channel_id,len # master -> gossipd: do you have a memleak? msgtype,gossipd_dev_memleak,3033 diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index d521eae4c2e6..103752c46c8b 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -682,11 +682,7 @@ static void topo_update_spends(struct chain_topology *topo, struct block *b) * tell gossipd about them. */ spent_scids = wallet_utxoset_get_spent(tmpctx, topo->ld->wallet, b->height); - - for (size_t i=0; ibitcoind->ld, &spent_scids[i]); - } - tal_free(spent_scids); + gossipd_notify_spends(topo->bitcoind->ld, b->height, spent_scids); } static void topo_add_utxos(struct chain_topology *topo, struct block *b) @@ -792,12 +788,11 @@ static void remove_tip(struct chain_topology *topo) /* This may have unconfirmed txs: reconfirm as we add blocks. */ watch_for_utxo_reconfirmation(topo, topo->ld->wallet); block_map_del(&topo->block_map, b); - tal_free(b); /* These no longer exist, so gossipd drops any reference to them just * as if they were spent. */ - for (size_t i = 0; i < tal_count(removed_scids); i++) - gossipd_notify_spend(topo->bitcoind->ld, &removed_scids[i]); + gossipd_notify_spends(topo->bitcoind->ld, b->height, removed_scids); + tal_free(b); } static void get_new_block(struct bitcoind *bitcoind, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 23b5eb8ee17c..b4665cddb151 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -153,7 +153,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) /* These are messages we send, not them. */ case WIRE_GOSSIPD_INIT: case WIRE_GOSSIPD_GET_TXOUT_REPLY: - case WIRE_GOSSIPD_OUTPOINT_SPENT: + case WIRE_GOSSIPD_OUTPOINTS_SPENT: case WIRE_GOSSIPD_NEW_LEASE_RATES: case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPD_LOCAL_CHANNEL_CLOSE: @@ -273,11 +273,15 @@ void gossip_init(struct lightningd *ld, int connectd_fd) assert(ret == ld->gossip); } -void gossipd_notify_spend(struct lightningd *ld, - const struct short_channel_id *scid) +/* We save these so we always tell gossipd about new blockheight first. */ +void gossipd_notify_spends(struct lightningd *ld, + u32 blockheight, + const struct short_channel_id *scids) { - u8 *msg = towire_gossipd_outpoint_spent(tmpctx, scid); - subd_send_msg(ld->gossip, msg); + subd_send_msg(ld->gossip, + take(towire_gossipd_outpoints_spent(NULL, + blockheight, + scids))); } /* We unwrap, add the peer id, and send to gossipd. */ diff --git a/lightningd/gossip_control.h b/lightningd/gossip_control.h index c1d587902158..82f2643869ac 100644 --- a/lightningd/gossip_control.h +++ b/lightningd/gossip_control.h @@ -10,8 +10,9 @@ struct lightningd; void gossip_init(struct lightningd *ld, int connectd_fd); -void gossipd_notify_spend(struct lightningd *ld, - const struct short_channel_id *scid); +void gossipd_notify_spends(struct lightningd *ld, + u32 blockheight, + const struct short_channel_id *scids); void gossip_notify_new_block(struct lightningd *ld, u32 blockheight); From f0fa42bd7370c9ee9a7b6e1d34d6bc824cc65a2e Mon Sep 17 00:00:00 2001 From: Vincenzo Palazzo Date: Tue, 20 Sep 2022 20:39:42 +0100 Subject: [PATCH 1480/1530] lnprototest: update gossip test including 12 blocks delay Signed-off-by: Vincenzo Palazzo --- external/lnprototest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/lnprototest b/external/lnprototest index 8efd45555ef3..265bac0d5809 160000 --- a/external/lnprototest +++ b/external/lnprototest @@ -1 +1 @@ -Subproject commit 8efd45555ef3d1223f3b9f6a8c009419c9510747 +Subproject commit 265bac0d5809e196c842f05b488b291a22119be1 From a1f62ba0e70b28ea82862aebc8ff776850073df2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 14 Sep 2022 13:20:32 +0930 Subject: [PATCH 1481/1530] gossipd: don't close non-local channels immediately, add 12 block delay. This adds a new "chan_dying" message to the gossip_store, but since we already changed the minor version in this PR, we don't bump it again. Signed-off-by: Rusty Russell Changelog-Added: Protocol: We now delay forgetting funding-spent channels for 12 blocks (as per latest BOLTs, to support splicing in future). --- Makefile | 2 +- devtools/dump-gossipstore.c | 6 ++ gossipd/gossip_store.c | 11 ++++ gossipd/gossip_store_wire.csv | 4 ++ gossipd/gossipd.c | 19 ++----- gossipd/gossipd.h | 1 + gossipd/routing.c | 100 ++++++++++++++++++++++++++++++++-- gossipd/routing.h | 35 +++++++++--- tests/test_connection.py | 6 +- tests/test_db.py | 2 +- tests/test_gossip.py | 51 +++++++++++++++-- tests/test_invoices.py | 2 +- 12 files changed, 204 insertions(+), 35 deletions(-) diff --git a/Makefile b/Makefile index 29e3fb8a582c..6e58c15ca626 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := 6fee63fc342736a2eb7f6e856ae0d85807cc50ec +DEFAULT_BOLTVERSION := 47d325c6ac50587d9974651a7b2a692f85c9a068 # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/devtools/dump-gossipstore.c b/devtools/dump-gossipstore.c index 7370b9c22721..6d4d8523c40f 100644 --- a/devtools/dump-gossipstore.c +++ b/devtools/dump-gossipstore.c @@ -68,6 +68,7 @@ int main(int argc, char *argv[]) u32 msglen = be32_to_cpu(hdr.len); u8 *msg, *inner; bool deleted, push, ratelimit; + u32 blockheight; deleted = (msglen & GOSSIP_STORE_LEN_DELETED_BIT); push = (msglen & GOSSIP_STORE_LEN_PUSH_BIT); @@ -121,6 +122,11 @@ int main(int argc, char *argv[]) printf("delete channel: %s\n", type_to_string(tmpctx, struct short_channel_id, &scid)); + } else if (fromwire_gossip_store_chan_dying(msg, &scid, &blockheight)) { + printf("dying channel: %s (deadline %u)\n", + type_to_string(tmpctx, struct short_channel_id, + &scid), + blockheight); } else { warnx("Unknown message %u", fromwire_peektype(msg)); diff --git a/gossipd/gossip_store.c b/gossipd/gossip_store.c index 5a3e9cd27206..e1f781b455ee 100644 --- a/gossipd/gossip_store.c +++ b/gossipd/gossip_store.c @@ -802,6 +802,17 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs) chan_ann = tal_steal(gs, msg); chan_ann_off = gs->len; break; + case WIRE_GOSSIP_STORE_CHAN_DYING: { + struct short_channel_id scid; + u32 deadline; + + if (!fromwire_gossip_store_chan_dying(msg, &scid, &deadline)) { + bad = "Bad gossip_store_chan_dying"; + goto badmsg; + } + remember_chan_dying(rstate, &scid, deadline, gs->len); + break; + } case WIRE_GOSSIP_STORE_PRIVATE_UPDATE: if (!fromwire_gossip_store_private_update(tmpctx, msg, &msg)) { bad = "invalid gossip_store_private_update"; diff --git a/gossipd/gossip_store_wire.csv b/gossipd/gossip_store_wire.csv index 6781757dcec5..55e62c3bd61f 100644 --- a/gossipd/gossip_store_wire.csv +++ b/gossipd/gossip_store_wire.csv @@ -23,3 +23,7 @@ msgdata,gossip_store_delete_chan,scid,short_channel_id, msgtype,gossip_store_ended,4105 msgdata,gossip_store_ended,equivalent_offset,u64, + +msgtype,gossip_store_chan_dying,4106 +msgdata,gossip_store_chan_dying,scid,short_channel_id, +msgdata,gossip_store_chan_dying,blockheight,u32, diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 395f303b4cc9..23b47385d1c2 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -797,6 +797,8 @@ static void new_blockheight(struct daemon *daemon, const u8 *msg) i--; } + routing_expire_channels(daemon->rstate, daemon->current_blockheight); + daemon_conn_send(daemon->master, take(towire_gossipd_new_blockheight_reply(NULL))); } @@ -931,28 +933,19 @@ static void handle_outpoints_spent(struct daemon *daemon, const u8 *msg) { struct short_channel_id *scids; u32 blockheight; - struct routing_state *rstate = daemon->rstate; if (!fromwire_gossipd_outpoints_spent(msg, msg, &blockheight, &scids)) master_badmsg(WIRE_GOSSIPD_OUTPOINTS_SPENT, msg); for (size_t i = 0; i < tal_count(scids); i++) { - struct chan *chan = get_channel(rstate, &scids[i]); + struct chan *chan = get_channel(daemon->rstate, &scids[i]); if (!chan) continue; - status_debug( - "Deleting channel %s due to the funding outpoint being " - "spent", - type_to_string(msg, struct short_channel_id, &scids[i])); - /* Suppress any now-obsolete updates/announcements */ - add_to_txout_failures(rstate, &scids[i]); - remove_channel_from_store(rstate, chan); - /* Freeing is sufficient since everything else is allocated off - * of the channel and this takes care of unregistering - * the channel */ - free_chan(rstate, chan); + /* We have a current_blockheight, but it's not necessarily + * updated first. */ + routing_channel_spent(daemon->rstate, blockheight, chan); } } diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index 2129375f7e8b..650d87a5c66f 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -16,6 +16,7 @@ struct channel_update_timestamps; struct broadcastable; struct lease_rates; struct seeker; +struct dying_channel; /*~ The core daemon structure: */ struct daemon { diff --git a/gossipd/routing.c b/gossipd/routing.c index 08aef559ebf9..efffaed0e79e 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -33,6 +33,16 @@ struct pending_node_announce { struct peer *peer_softref; }; +/* As per the below BOLT #7 quote, we delay forgetting a channel until 12 + * blocks after we see it close. This gives time for splicing (or even other + * opens) to replace the channel, and broadcast it after 6 blocks. */ +struct dying_channel { + struct short_channel_id scid; + u32 deadline_blockheight; + /* Where the dying_channel marker is in the store. */ + struct broadcastable marker; +}; + /* We consider a reasonable gossip rate to be 2 per day, with burst of * 4 per day. So we use a granularity of one hour. */ #define TOKENS_PER_MSG 12 @@ -249,8 +259,8 @@ static void txout_failure_age(struct routing_state *rstate) txout_failure_age, rstate); } -void add_to_txout_failures(struct routing_state *rstate, - const struct short_channel_id *scid) +static void add_to_txout_failures(struct routing_state *rstate, + const struct short_channel_id *scid) { if (uintmap_add(&rstate->txout_failures, scid->u64, true) && ++rstate->num_txout_failures == 10000) { @@ -288,6 +298,7 @@ struct routing_state *new_routing_state(const tal_t *ctx, rstate->gs = gossip_store_new(rstate, peers); rstate->local_channel_announced = false; rstate->last_timestamp = 0; + rstate->dying_channels = tal_arr(rstate, struct dying_channel, 0); pending_cannouncement_map_init(&rstate->pending_cannouncements); @@ -852,8 +863,8 @@ static void delete_chan_messages_from_store(struct routing_state *rstate, &chan->half[1].bcast, update_type); } -void remove_channel_from_store(struct routing_state *rstate, - struct chan *chan) +static void remove_channel_from_store(struct routing_state *rstate, + struct chan *chan) { /* Put in tombstone marker */ gossip_store_mark_channel_deleted(rstate->gs, &chan->scid); @@ -2087,3 +2098,84 @@ void remove_all_gossip(struct routing_state *rstate) /* Freeing unupdated chanmaps should empty this */ assert(pending_node_map_first(rstate->pending_node_map, &pnait) == NULL); } + +static void channel_spent(struct routing_state *rstate, + struct chan *chan STEALS) +{ + status_debug("Deleting channel %s due to the funding outpoint being " + "spent", + type_to_string(tmpctx, struct short_channel_id, + &chan->scid)); + /* Suppress any now-obsolete updates/announcements */ + add_to_txout_failures(rstate, &chan->scid); + remove_channel_from_store(rstate, chan); + /* Freeing is sufficient since everything else is allocated off + * of the channel and this takes care of unregistering + * the channel */ + free_chan(rstate, chan); +} + +void routing_expire_channels(struct routing_state *rstate, u32 blockheight) +{ + struct chan *chan; + + for (size_t i = 0; i < tal_count(rstate->dying_channels); i++) { + struct dying_channel *d = rstate->dying_channels + i; + + if (blockheight < d->deadline_blockheight) + continue; + chan = get_channel(rstate, &d->scid); + if (chan) + channel_spent(rstate, chan); + /* Delete dying marker itself */ + gossip_store_delete(rstate->gs, + &d->marker, WIRE_GOSSIP_STORE_CHAN_DYING); + tal_arr_remove(&rstate->dying_channels, i); + i--; + } +} + +void remember_chan_dying(struct routing_state *rstate, + const struct short_channel_id *scid, + u32 deadline_blockheight, + u64 index) +{ + struct dying_channel d; + d.scid = *scid; + d.deadline_blockheight = deadline_blockheight; + d.marker.index = index; + tal_arr_expand(&rstate->dying_channels, d); +} + +void routing_channel_spent(struct routing_state *rstate, + u32 current_blockheight, + struct chan *chan) +{ + u64 index; + u32 deadline; + u8 *msg; + + /* FIXME: We should note that delay is not necessary (or even + * sensible) for local channels! */ + if (local_direction(rstate, chan, NULL)) { + channel_spent(rstate, chan); + return; + } + + /* BOLT #7: + * - once its funding output has been spent OR reorganized out: + * - SHOULD forget a channel after a 12-block delay. + */ + deadline = current_blockheight + 12; + + /* Save to gossip_store in case we restart */ + msg = towire_gossip_store_chan_dying(tmpctx, &chan->scid, deadline); + index = gossip_store_add(rstate->gs, msg, 0, false, false, NULL); + + /* Remember locally so we can kill it in 12 blocks */ + status_debug("channel %s closing soon due" + " to the funding outpoint being spent", + type_to_string(msg, struct short_channel_id, &chan->scid)); + remember_chan_dying(rstate, &chan->scid, deadline, index); +} + diff --git a/gossipd/routing.h b/gossipd/routing.h index e9a2bc0f6ae6..ea7feabe75cc 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -230,6 +230,9 @@ struct routing_state { /* Highest timestamp of gossip we accepted (before now) */ u32 last_timestamp; + /* Channels which are closed, but we're waiting 12 blocks */ + struct dying_channel *dying_channels; + #if DEVELOPER /* Override local time for gossip messages */ struct timeabs *gossip_time; @@ -399,21 +402,37 @@ bool routing_add_private_channel(struct routing_state *rstate, */ struct timeabs gossip_time_now(const struct routing_state *rstate); +/** + * Add to rstate->dying_channels + * + * Exposed here for when we load the gossip_store. + */ +void remember_chan_dying(struct routing_state *rstate, + const struct short_channel_id *scid, + u32 deadline_blockheight, + u64 index); + +/** + * When a channel's funding has been spent. + */ +void routing_channel_spent(struct routing_state *rstate, + u32 current_blockheight, + struct chan *chan); + +/** + * Clean up any dying channels. + * + * This finally deletes channel past their deadline. + */ +void routing_expire_channels(struct routing_state *rstate, u32 blockheight); + /* Would we ratelimit a channel_update with this timestamp? */ bool would_ratelimit_cupdate(struct routing_state *rstate, const struct half_chan *hc, u32 timestamp); -/* Remove channel from store: announcement and any updates. */ -void remove_channel_from_store(struct routing_state *rstate, - struct chan *chan); - /* Returns an error string if there are unfinalized entries after load */ const char *unfinalized_entries(const tal_t *ctx, struct routing_state *rstate); void remove_all_gossip(struct routing_state *rstate); - -/* This scid is dead to us. */ -void add_to_txout_failures(struct routing_state *rstate, - const struct short_channel_id *scid); #endif /* LIGHTNING_GOSSIPD_ROUTING_H */ diff --git a/tests/test_connection.py b/tests/test_connection.py index 3ed032d4a81e..38884edd5082 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -4085,7 +4085,7 @@ def test_multichan(node_factory, executor, bitcoind): l2.rpc.close(l3.info['id']) l2.rpc.close(scid23b) - bitcoind.generate_block(1, wait_for_mempool=1) + bitcoind.generate_block(13, wait_for_mempool=1) sync_blockheight(bitcoind, [l1, l2, l3]) # Gossip works as expected. @@ -4130,14 +4130,14 @@ def test_multichan(node_factory, executor, bitcoind): "state": "RCVD_REMOVE_ACK_REVOCATION"}, {"short_channel_id": scid12, "id": 2, - "expiry": 123, + "expiry": 135, "direction": "out", "amount_msat": Millisatoshi(100001001), "payment_hash": inv3['payment_hash'], "state": "RCVD_REMOVE_ACK_REVOCATION"}, {"short_channel_id": scid12, "id": 3, - "expiry": 123, + "expiry": 135, "direction": "out", "amount_msat": Millisatoshi(100001001), "payment_hash": inv4['payment_hash'], diff --git a/tests/test_db.py b/tests/test_db.py index 677a5d290dfb..414bc066c89e 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -96,7 +96,7 @@ def test_block_backfill(node_factory, bitcoind, chainparams): # Now close the channel and make sure `l3` cleans up correctly: txid = l1.rpc.close(l2.info['id'])['txid'] - bitcoind.generate_block(1, wait_for_mempool=txid) + bitcoind.generate_block(13, wait_for_mempool=txid) wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 0) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index 959baac91b6f..a1cccf215630 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -559,8 +559,9 @@ def non_public(node): # channel from their network view l1.rpc.dev_fail(l2.info['id']) - # We need to wait for the unilateral close to hit the mempool - bitcoind.generate_block(1, wait_for_mempool=1) + # We need to wait for the unilateral close to hit the mempool, + # and 12 blocks for nodes to actually forget it. + bitcoind.generate_block(13, wait_for_mempool=1) wait_for(lambda: active(l1) == [scid23, scid23]) wait_for(lambda: active(l2) == [scid23, scid23]) @@ -1391,7 +1392,7 @@ def test_gossip_notices_close(node_factory, bitcoind): txid = l2.rpc.close(l3.info['id'])['txid'] wait_for(lambda: only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels'][0]['state'] == 'CLOSINGD_COMPLETE') - bitcoind.generate_block(1, txid) + bitcoind.generate_block(13, txid) wait_for(lambda: l1.rpc.listchannels()['channels'] == []) wait_for(lambda: l1.rpc.listnodes()['nodes'] == []) @@ -2097,7 +2098,7 @@ def test_topology_leak(node_factory, bitcoind): # Close and wait for gossip to catchup. txid = l2.rpc.close(l3.info['id'])['txid'] - bitcoind.generate_block(1, txid) + bitcoind.generate_block(13, txid) wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 2) @@ -2122,3 +2123,45 @@ def test_parms_listforwards(node_factory): assert len(forwards_new) == 0 assert len(forwards_dep) == 0 + + +@pytest.mark.developer("gossip without DEVELOPER=1 is slow") +def test_close_12_block_delay(node_factory, bitcoind): + l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True) + + # Close l1-l2 + txid = l1.rpc.close(l2.info['id'])['txid'] + bitcoind.generate_block(1, txid) + + # But l4 doesn't believe it immediately. + l4.daemon.wait_for_log("channel .* closing soon due to the funding outpoint being spent") + + # Close l2-l3 one block later. + txid = l2.rpc.close(l3.info['id'])['txid'] + bitcoind.generate_block(1, txid) + l4.daemon.wait_for_log("channel .* closing soon due to the funding outpoint being spent") + + # BOLT #7: + # - once its funding output has been spent OR reorganized out: + # - SHOULD forget a channel after a 12-block delay. + + # That implies 12 blocks *after* spending, i.e. 13 blocks deep! + + # 12 blocks deep, l4 still sees it + bitcoind.generate_block(10) + sync_blockheight(bitcoind, [l4]) + assert len(l4.rpc.listchannels(source=l1.info['id'])['channels']) == 1 + + # 13 blocks deep does it. + bitcoind.generate_block(1) + wait_for(lambda: l4.rpc.listchannels(source=l1.info['id'])['channels'] == []) + + # Other channel still visible. + assert len(l4.rpc.listchannels(source=l2.info['id'])['channels']) == 1 + + # Restart: it remembers channel is dying. + l4.restart() + + # One more block, it's forgotten too. + bitcoind.generate_block(1) + wait_for(lambda: l4.rpc.listchannels(source=l2.info['id'])['channels'] == []) diff --git a/tests/test_invoices.py b/tests/test_invoices.py index dbb9ab7a4da7..195864d44aa1 100644 --- a/tests/test_invoices.py +++ b/tests/test_invoices.py @@ -363,7 +363,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind): # It will use an explicit exposeprivatechannels even if it thinks its a dead-end l0.rpc.close(l1.info['id']) l0.wait_for_channel_onchain(l1.info['id']) - bitcoind.generate_block(1) + bitcoind.generate_block(13) wait_for(lambda: l2.rpc.listchannels(scid_dummy)['channels'] == []) inv = l2.rpc.invoice(amount_msat=123456, label="inv7", description="?", exposeprivatechannels=scid) From f53155d93b5eef9f4353c68e623cd54134aa7e2b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 24 Sep 2022 09:07:37 +0930 Subject: [PATCH 1482/1530] BOLT: update to clarify HTLC tx amount calculation. Simple quote update. Signed-off-by: Rusty Russell --- Makefile | 2 +- common/htlc_tx.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 6e58c15ca626..54913ec021bc 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ CCANDIR := ccan # Where we keep the BOLT RFCs BOLTDIR := ../bolts/ -DEFAULT_BOLTVERSION := 47d325c6ac50587d9974651a7b2a692f85c9a068 +DEFAULT_BOLTVERSION := f32c6ddb5f11b431c9bb4f501cdec604172a90de # Can be overridden on cmdline. BOLTVERSION := $(DEFAULT_BOLTVERSION) diff --git a/common/htlc_tx.c b/common/htlc_tx.c index e80ea1375813..5be4b47f2ba2 100644 --- a/common/htlc_tx.c +++ b/common/htlc_tx.c @@ -52,8 +52,9 @@ static struct bitcoin_tx *htlc_tx(const tal_t *ctx, /* BOLT #3: * * txout count: 1 - * * `txout[0]` amount: the HTLC amount minus fees - * (see [Fee Calculation](#fee-calculation)) + * * `txout[0]` amount: the HTLC `amount_msat` divided by 1000 + * (rounding down) minus fees in satoshis (see + * [Fee Calculation](#fee-calculation)) * * `txout[0]` script: version-0 P2WSH with witness script as shown * below */ From 3f5ff0c148b33433b8a7a8db56d3caee363d17dd Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 24 Sep 2022 09:07:38 +0930 Subject: [PATCH 1483/1530] doc/GOSSIP_STORE.md: document the gossip_store file format. Mainly for @vincenzopalazzo who is thinking of writing a Rust version! Signed-off-by: Rusty Russell --- doc/GOSSIP_STORE.md | 146 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 doc/GOSSIP_STORE.md diff --git a/doc/GOSSIP_STORE.md b/doc/GOSSIP_STORE.md new file mode 100644 index 000000000000..54a6728eb757 --- /dev/null +++ b/doc/GOSSIP_STORE.md @@ -0,0 +1,146 @@ +# gossip_store: Direct Access To Lightning Gossip + +Hi! + +The lightning_gossipd dameon stores the gossip messages, along with +some internal data, in a file called the "gossip_store". Various +plugins and daemons access this (in a read-only manner), and the +format is documented here. + +## The File Header + +``` +u8 version; +``` + +The gossmap header consists of one byte. The top 3 bits are the major +version: if these are not all zero, you need to re-read this (updated) +document to see what changed. The lower 5 bits are the minor version, +which won't worry you: currently they will be 11. + +After the file header comes a number of records. + +## The Record Header + +``` +be16 flags; +be16 len; +be32 crc; +be32 timestamp; +``` + +Each record consists of a header and a message. The header is +big-endian, containing flags, the length (of the following body), the +crc32c (of the following message, starting with the timestamp field in +the header) and a timestamp extracted from certain messages (zero +where not relevant, but ignore it in those cases). + +The flags currently defined are: + +``` +#define DELETED 0x8000 +#define PUSH 0x4000 +#define RATELIMIT 0x2000 +``` + +Deleted fields should be ignored: on restart, they will be removed as +the gossip_store is rewritten. + +The push flag indicates gossip which is generated locally: this is +important for gossip timestamp filtering, where peers request gossip +and we always send our own gossip messages even if the timestamp +wasn't within their request. + +The ratelimit flag indicates that this gossip message came too fast: +we record it, but don't relay it to peers. + +Other flags should be ignored. + +## The Message + +Each messages consists of a 16-bit big-endian "type" field (for +efficiency, an implementation may read this along with the header), +and optional data. Some messages are defined by the BOLT 7 gossip +protocol, others are for internal use. Unknown message types should be +skipped over. + +### BOLT 7 Messages + +These are the messages which gossipd has validated, and ensured are in +order. + +* `channel_announcement` (256): a complete, validated channel announcement. This will always come before any `channel_update` which refers to it, or `node_announcement` which refers to a node. +* `channel_update` (258): a complete, validated channel update. Note that you can see multiple of these (old ones will be deleted as they are replaced though). +* `node_announcement` (257): a complete, validated node announcement. Note that you can also see multiple of these (old ones will be deleted as they are replaced). + +### Internal Gossip Daemon Messages + +These messages contain additional data, which may be useful. + +* `gossip_store_channel_amount` (4101) + * `satoshis`: u64 + +This always immediately follows `channel_announcement` messages, and +contains the actual capacity of the channel. + +* `gossip_store_private_channel` (4104) + * `amount_sat`: u64 + * `len`: u16 + * `announcement`: u8[len] + +This contains information about a private (could be made public +later!) channel, with announcement in the same format as a normal +`channel_announcement` with invalid signatures. + +* `gossip_store_private_update` (4102) + * `len`: u16 + * `update`: u8[len] + +This contains a private `channel_update` (i.e. for a channel described +by `gossip_store_private_channel`. + +* `gossip_store_delete_chan` (4103) + * `scid`: u64 + +This is added when a channel is deleted. You won't often see this if +you're reading the file once (as the channel record header will have +been marked `deleted` first), but useful if you are polling the file +for updates. + +* `gossip_store_ended` (4105) + * `equivalent_offset`: u64 + +This is only ever added as the final entry in the gossip_store. It +means the file has been deleted (usually because lightningd has been +restarted), and you should re-open it. As an optimization, the +`equivalent_offset` in the new file reflects the point at which the +new gossip_store is equivalent to this one (with deleted records +removed). However, if lightningd has been restarted multiple times it +is possible that this offset is not valid, so it's really only useful +if you're actively monitoring the file. + +* `gossip_store_chan_dying` (4106) + * `scid`: u64 + * `blockheight`: u32 + +This is placed in the gossip_store file when a funding transaction is +spent. `blockheight` is set to 12 blocks beyond the block containing +the spend: at this point, gossipd will delete the channel. + +## Using the Gossip Store File + +- Always check the major version number! We will increment it if the format + changes in a way that breaks readers. +- Ignore unknown flags in the header. +- Ignore message types you don't know. +- You don't need to check the messages, as they have been validated. +- It is possible to see a partially-written record at the end. Ignore it. + +If you are keeping the file open to watch for changes: + +- The file is append-only, so you can simply try reading more records + using inotify (or equivalent) or simply checking every few seconds. +- If you see a `gossip_store_ended` message, reopen the file. + +Happy hacking! +Rusty. From 8898511cf6b6612bb15127bc7c8e54aa551517ce Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 20 Jul 2022 16:56:27 +0200 Subject: [PATCH 1484/1530] cln-plugin: Defer binding the plugin state until after configuring We had a bit of a chicken-and-egg problem, where we instantiated the `state` to be managed by the `Plugin` during the very first step when creating the `Builder`, but then the state might depend on the configuration we only get later. This would force developers to add placeholders in the form of `Option` into the state, when really they'd never be none after configuring. This defers the binding until after we get the configuration and cleans up the semantics: - `Builder`: declare options, hooks, etc - `ConfiguredPlugin`: we have exchanged the handshake with `lightningd`, now we can construct the `state` accordingly - `Plugin`: Running instance of the plugin Changelog-Changed: cln-plugin: Moved the state binding to the plugin until after the configuration step --- cln-grpc/src/test.rs | 3 +- plugins/examples/cln-plugin-startup.rs | 6 +- plugins/grpc-plugin/src/main.rs | 16 +- plugins/src/lib.rs | 200 ++++++++++++------------- 4 files changed, 114 insertions(+), 111 deletions(-) diff --git a/cln-grpc/src/test.rs b/cln-grpc/src/test.rs index 4ed4d71b919b..78f49cdc5c5f 100644 --- a/cln-grpc/src/test.rs +++ b/cln-grpc/src/test.rs @@ -248,7 +248,8 @@ fn test_keysend() { "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", ) .unwrap(), - msatoshi: Some(Amount { msat: 10000 }), + amount_msat: Some(Amount { msat: 10000 }), + label: Some("hello".to_string()), exemptfee: None, maxdelay: None, diff --git a/plugins/examples/cln-plugin-startup.rs b/plugins/examples/cln-plugin-startup.rs index ff152235ffdb..3fdd6bdcf358 100644 --- a/plugins/examples/cln-plugin-startup.rs +++ b/plugins/examples/cln-plugin-startup.rs @@ -6,7 +6,9 @@ use cln_plugin::{options, Builder, Error, Plugin}; use tokio; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { - if let Some(plugin) = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) + let state = (); + + if let Some(plugin) = Builder::new(tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "test-option", options::Value::Integer(42), @@ -15,7 +17,7 @@ async fn main() -> Result<(), anyhow::Error> { .rpcmethod("testmethod", "This is a test", testmethod) .subscribe("connect", connect_handler) .hook("peer_connected", peer_connected_handler) - .start() + .start(state) .await? { plugin.join().await diff --git a/plugins/grpc-plugin/src/main.rs b/plugins/grpc-plugin/src/main.rs index 70dffa5d336f..926f7cefb612 100644 --- a/plugins/grpc-plugin/src/main.rs +++ b/plugins/grpc-plugin/src/main.rs @@ -22,13 +22,7 @@ async fn main() -> Result<()> { let directory = std::env::current_dir()?; let (identity, ca_cert) = tls::init(&directory)?; - let state = PluginState { - rpc_path: path.into(), - identity, - ca_cert, - }; - - let plugin = match Builder::new(state.clone(), tokio::io::stdin(), tokio::io::stdout()) + let plugin = match Builder::new(tokio::io::stdin(), tokio::io::stdout()) .option(options::ConfigOption::new( "grpc-port", options::Value::Integer(-1), @@ -54,7 +48,13 @@ async fn main() -> Result<()> { Some(o) => return Err(anyhow!("grpc-port is not a valid integer: {:?}", o)), }; - let plugin = plugin.start().await?; + let state = PluginState { + rpc_path: path.into(), + identity, + ca_cert, + }; + + let plugin = plugin.start(state.clone()).await?; let bind_addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap(); diff --git a/plugins/src/lib.rs b/plugins/src/lib.rs index 56237d5c0928..8c034a4a6343 100644 --- a/plugins/src/lib.rs +++ b/plugins/src/lib.rs @@ -1,6 +1,7 @@ use crate::codec::{JsonCodec, JsonRpcCodec}; pub use anyhow::{anyhow, Context}; use futures::sink::SinkExt; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; extern crate log; use log::trace; use messages::Configuration; @@ -13,6 +14,7 @@ use tokio::sync::Mutex; use tokio_stream::StreamExt; use tokio_util::codec::FramedRead; use tokio_util::codec::FramedWrite; +use options::ConfigOption; pub mod codec; pub mod logging; @@ -23,7 +25,6 @@ extern crate serde_json; pub mod options; -use options::ConfigOption; /// Need to tell us about something that went wrong? Use this error /// type to do that. Use this alias to be safe from future changes in @@ -38,34 +39,81 @@ where O: Send + AsyncWrite + Unpin, S: Clone + Send, { - state: S, - input: Option, output: Option, hooks: HashMap>, options: Vec, - configuration: Option, rpcmethods: HashMap>, subscriptions: HashMap>, dynamic: bool, } +/// A plugin that has registered with the lightning daemon, and gotten +/// its options filled, however has not yet acknowledged the `init` +/// message. This is a mid-state allowing a plugin to disable itself, +/// based on the options. +pub struct ConfiguredPlugin +where + S: Clone + Send, +{ + init_id: serde_json::Value, + input: FramedRead, + output: Arc>>, + options: Vec, + configuration: Configuration, + rpcmethods: HashMap>, + hooks: HashMap>, + subscriptions: HashMap>, +} + +/// The [PluginDriver] is used to run the IO loop, reading messages +/// from the Lightning daemon, dispatching calls and notifications to +/// the plugin, and returning responses to the the daemon. We also use +/// it to handle spontaneous messages like Notifications and logging +/// events. +struct PluginDriver +where + S: Send + Clone, +{ + plugin: Plugin, + rpcmethods: HashMap>, + + #[allow(dead_code)] // Unused until we fill in the Hook structs. + hooks: HashMap>, + subscriptions: HashMap>, +} + +#[derive(Clone)] +pub struct Plugin +where + S: Clone + Send, +{ + /// The state gets cloned for each request + state: S, + /// "options" field of "init" message sent by cln + options: Vec, + /// "configuration" field of "init" message sent by cln + configuration: Configuration, + /// A signal that allows us to wait on the plugin's shutdown. + wait_handle: tokio::sync::broadcast::Sender<()>, + + sender: tokio::sync::mpsc::Sender, +} + impl Builder where O: Send + AsyncWrite + Unpin + 'static, S: Clone + Sync + Send + Clone + 'static, I: AsyncRead + Send + Unpin + 'static, { - pub fn new(state: S, input: I, output: O) -> Self { + pub fn new(input: I, output: O) -> Self { Self { - state, input: Some(input), output: Some(output), hooks: HashMap::new(), subscriptions: HashMap::new(), options: vec![], - configuration: None, rpcmethods: HashMap::new(), dynamic: false, } @@ -91,7 +139,7 @@ where /// Ok(()) /// } /// - /// let b = Builder::new((), tokio::io::stdin(), tokio::io::stdout()) + /// let b = Builder::new(tokio::io::stdin(), tokio::io::stdout()) /// .subscribe("connect", connect_handler); /// ``` pub fn subscribe(mut self, topic: &str, callback: C) -> Builder @@ -195,10 +243,9 @@ where )) } }; - let init_id = match input.next().await { + let (init_id, configuration) = match input.next().await { Some(Ok(messages::JsonRpc::Request(id, messages::Request::Init(m)))) => { - self.handle_init(m)?; - id + (id, self.handle_init(m)?) } Some(o) => return Err(anyhow!("Got unexpected message {:?} from lightningd", o)), @@ -210,27 +257,15 @@ where } }; - let (wait_handle, _) = tokio::sync::broadcast::channel(1); - - // An MPSC pair used by anything that needs to send messages - // to the main daemon. - let (sender, receiver) = tokio::sync::mpsc::channel(4); - let plugin = Plugin { - state: self.state, - options: self.options, - configuration: self - .configuration - .ok_or(anyhow!("Plugin configuration missing"))?, - wait_handle, - sender, - }; - // TODO Split the two hashmaps once we fill in the hook // payload structs in messages.rs let mut rpcmethods: HashMap> = HashMap::from_iter(self.rpcmethods.drain().map(|(k, v)| (k, v.callback))); rpcmethods.extend(self.hooks.drain().map(|(k, v)| (k, v.callback))); + let subscriptions = + HashMap::from_iter(self.subscriptions.drain().map(|(k, v)| (k, v.callback))); + // Leave the `init` reply pending, so we can disable based on // the options if required. Ok(Some(ConfiguredPlugin { @@ -238,16 +273,11 @@ where init_id, input, output, - receiver, - driver: PluginDriver { - plugin: plugin.clone(), - rpcmethods, - hooks: HashMap::new(), - subscriptions: HashMap::from_iter( - self.subscriptions.drain().map(|(k, v)| (k, v.callback)), - ), - }, - plugin, + rpcmethods, + subscriptions, + options: self.options, + configuration, + hooks: HashMap::new(), })) } @@ -261,9 +291,9 @@ where /// `Plugin` instance and return `None` instead. This signals that /// we should exit, and not continue running. `start()` returns in /// order to allow user code to perform cleanup if necessary. - pub async fn start(self) -> Result>, anyhow::Error> { + pub async fn start(self, state: S) -> Result>, anyhow::Error> { if let Some(cp) = self.configure().await? { - Ok(Some(cp.start().await?)) + Ok(Some(cp.start(state).await?)) } else { Ok(None) } @@ -292,7 +322,7 @@ where } } - fn handle_init(&mut self, call: messages::InitCall) -> Result { + fn handle_init(&mut self, call: messages::InitCall) -> Result { use options::Value as OValue; use serde_json::Value as JValue; @@ -312,9 +342,7 @@ where } } - self.configuration = Some(call.configuration); - - Ok(messages::InitResponse::default()) + Ok(call.configuration) } } @@ -356,39 +384,6 @@ where callback: AsyncCallback, } -/// A plugin that has registered with the lightning daemon, and gotten -/// its options filled, however has not yet acknowledged the `init` -/// message. This is a mid-state allowing a plugin to disable itself, -/// based on the options. -pub struct ConfiguredPlugin -where - S: Clone + Send, -{ - init_id: serde_json::Value, - input: FramedRead, - output: Arc>>, - plugin: Plugin, - driver: PluginDriver, - receiver: tokio::sync::mpsc::Receiver, -} - -#[derive(Clone)] -pub struct Plugin -where - S: Clone + Send, -{ - /// The state gets cloned for each request - state: S, - /// "options" field of "init" message sent by cln - options: Vec, - /// "configuration" field of "init" message sent by cln - configuration: Configuration, - /// A signal that allows us to wait on the plugin's shutdown. - wait_handle: tokio::sync::broadcast::Sender<()>, - - sender: tokio::sync::mpsc::Sender, -} - impl Plugin where S: Clone + Send, @@ -409,12 +404,30 @@ where O: Send + AsyncWrite + Unpin + 'static, { #[allow(unused_mut)] - pub async fn start(mut self) -> Result, anyhow::Error> { - let driver = self.driver; - let plugin = self.plugin; + pub async fn start(mut self, state: S) -> Result, anyhow::Error> { let output = self.output; let input = self.input; - let receiver = self.receiver; // Now reply to the `init` message that `configure` left pending. + let (wait_handle, _) = tokio::sync::broadcast::channel(1); + + // An MPSC pair used by anything that needs to send messages + // to the main daemon. + let (sender, receiver) = tokio::sync::mpsc::channel(4); + + let plugin = Plugin { + state, + options: self.options, + configuration: self.configuration, + wait_handle, + sender, + }; + + let driver = PluginDriver { + plugin: plugin.clone(), + rpcmethods: self.rpcmethods, + hooks: self.hooks, + subscriptions: self.subscriptions, + }; + output .lock() .await @@ -465,28 +478,14 @@ where } pub fn option(&self, name: &str) -> Option { - self.plugin.option(name) + self.options + .iter() + .filter(|o| o.name() == name) + .next() + .map(|co| co.value.clone().unwrap_or(co.default().clone())) } } -/// The [PluginDriver] is used to run the IO loop, reading messages -/// from the Lightning daemon, dispatching calls and notifications to -/// the plugin, and returning responses to the the daemon. We also use -/// it to handle spontaneous messages like Notifications and logging -/// events. -struct PluginDriver -where - S: Send + Clone, -{ - plugin: Plugin, - rpcmethods: HashMap>, - - #[allow(dead_code)] // Unused until we fill in the Hook structs. - hooks: HashMap>, - subscriptions: HashMap>, -} - -use tokio::io::{AsyncReadExt, AsyncWriteExt}; impl PluginDriver where S: Send + Clone, @@ -688,7 +687,8 @@ mod test { #[tokio::test] async fn init() { - let builder = Builder::new((), tokio::io::stdin(), tokio::io::stdout()); - let _ = builder.start(); + let state = (); + let builder = Builder::new(tokio::io::stdin(), tokio::io::stdout()); + let _ = builder.start(state); } } From 064a5a69406d8d44e005c94e914126626d1bf460 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 29 Jul 2022 15:06:11 +0200 Subject: [PATCH 1485/1530] cln-plugin: Add log filtering support We filter based on the environment variable `CLN_PLUGIN_LOG`, defaulting to `info` as that is not as noisy as `debug` or `trace`, at least libraries will not spam us too heavily. Changelog-Added cln-plugin: The logs level from cln-plugins can be configured by the `CLN_PLUGIN_LOG` environment variable. --- plugins/Cargo.toml | 2 +- plugins/src/logging.rs | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/Cargo.toml b/plugins/Cargo.toml index 44cca27f956d..88e96b795660 100644 --- a/plugins/Cargo.toml +++ b/plugins/Cargo.toml @@ -21,8 +21,8 @@ tokio = { version="1", features = ['io-std', 'rt', 'sync', 'macros', 'io-util'] tokio-stream = "0.1" futures = "0.3" cln-rpc = { path = "../cln-rpc", version = "0.1.0" } +env_logger = "0.9" [dev-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread", ] } -env_logger = "0.9" cln-grpc = { path = "../cln-grpc" } diff --git a/plugins/src/logging.rs b/plugins/src/logging.rs index 7a90b84aa1ca..365648463595 100644 --- a/plugins/src/logging.rs +++ b/plugins/src/logging.rs @@ -1,6 +1,7 @@ use crate::codec::JsonCodec; +use env_logger::filter; use futures::SinkExt; -use log::{Level, Metadata, Record}; +use log::{Metadata, Record}; use serde::Serialize; use std::sync::Arc; use tokio::io::AsyncWrite; @@ -42,6 +43,7 @@ struct PluginLogger { // happen to emit a log record while holding the lock on the // plugin connection. sender: tokio::sync::mpsc::UnboundedSender, + filter: filter::Filter, } /// Initialize the logger starting a flusher to the passed in sink. @@ -50,6 +52,10 @@ where O: AsyncWrite + Send + Unpin + 'static, { let out = out.clone(); + + let filter_str = std::env::var("CLN_PLUGIN_LOG").unwrap_or("info".to_string()); + let filter = filter::Builder::new().parse(&filter_str).build(); + let (sender, mut receiver) = tokio::sync::mpsc::unbounded_channel::(); tokio::spawn(async move { while let Some(i) = receiver.recv().await { @@ -68,17 +74,17 @@ where .await; } }); - log::set_boxed_logger(Box::new(PluginLogger { sender })) + log::set_boxed_logger(Box::new(PluginLogger { sender, filter })) .map(|()| log::set_max_level(log::LevelFilter::Debug)) } impl log::Log for PluginLogger { fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= Level::Debug + self.filter.enabled(metadata) } fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { + if self.filter.matches(record) { self.sender .send(LogEntry { level: record.level().into(), From e1fc88ff700aeae3123e6bbc9d8152562788f86e Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 27 Jul 2022 15:31:46 +0200 Subject: [PATCH 1486/1530] cln-plugin: Prep the logging payload in a let I wanted to debug why we weren't getting some log messages, and this makes that a bit easier in future. --- plugins/src/logging.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/plugins/src/logging.rs b/plugins/src/logging.rs index 365648463595..b752c42916ce 100644 --- a/plugins/src/logging.rs +++ b/plugins/src/logging.rs @@ -63,15 +63,13 @@ where // errors when forwarding. Forwarding could break due to // an interrupted connection or stdout being closed, but // keeping the messages in the queue is a memory leak. - let _ = out - .lock() - .await - .send(json!({ - "jsonrpc": "2.0", - "method": "log", - "params": i - })) - .await; + let payload = json!({ + "jsonrpc": "2.0", + "method": "log", + "params": i + }); + + let _ = out.lock().await.send(payload).await; } }); log::set_boxed_logger(Box::new(PluginLogger { sender, filter })) From b13ab8de3ae21fd22d09dcfb3ac6a2999764b2ba Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 27 Jul 2022 14:33:48 +0200 Subject: [PATCH 1487/1530] msggen: Use owned versions to convert from cln-rpc to cln-grpc We needed to clone a lot since we were using a non-mutable borrow of the cln-rpc version to then create the cln-grpc version. This alone reduces the number of allocations of the `listpeers` test from 52 allocations to 38. --- cln-grpc/src/convert.rs | 1322 ++++++++++++++--------------- cln-grpc/src/pb.rs | 38 +- cln-grpc/src/server.rs | 184 ++-- cln-grpc/src/test.rs | 8 +- contrib/msggen/msggen/gen/grpc.py | 82 +- 5 files changed, 817 insertions(+), 817 deletions(-) diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index e5545c85cab7..a8cfec26ee72 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -10,74 +10,74 @@ use crate::pb; use std::str::FromStr; #[allow(unused_variables)] -impl From<&responses::GetinfoAddress> for pb::GetinfoAddress { - fn from(c: &responses::GetinfoAddress) -> Self { +impl From for pb::GetinfoAddress { + fn from(c: responses::GetinfoAddress) -> Self { Self { item_type: c.item_type as i32, port: c.port.into(), // Rule #2 for type u16 - address: c.address.clone(), // Rule #2 for type string? + address: c.address, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::GetinfoBinding> for pb::GetinfoBinding { - fn from(c: &responses::GetinfoBinding) -> Self { +impl From for pb::GetinfoBinding { + fn from(c: responses::GetinfoBinding) -> Self { Self { item_type: c.item_type as i32, - address: c.address.clone(), // Rule #2 for type string? + address: c.address, // Rule #2 for type string? port: c.port.map(|v| v.into()), // Rule #2 for type u16? - socket: c.socket.clone(), // Rule #2 for type string? + socket: c.socket, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::GetinfoResponse> for pb::GetinfoResponse { - fn from(c: &responses::GetinfoResponse) -> Self { +impl From for pb::GetinfoResponse { + fn from(c: responses::GetinfoResponse) -> Self { Self { id: c.id.to_vec(), // Rule #2 for type pubkey - alias: c.alias.clone(), // Rule #2 for type string + alias: c.alias, // Rule #2 for type string color: hex::decode(&c.color).unwrap(), // Rule #2 for type hex - num_peers: c.num_peers.clone(), // Rule #2 for type u32 - num_pending_channels: c.num_pending_channels.clone(), // Rule #2 for type u32 - num_active_channels: c.num_active_channels.clone(), // Rule #2 for type u32 - num_inactive_channels: c.num_inactive_channels.clone(), // Rule #2 for type u32 - version: c.version.clone(), // Rule #2 for type string - lightning_dir: c.lightning_dir.clone(), // Rule #2 for type string - blockheight: c.blockheight.clone(), // Rule #2 for type u32 - network: c.network.clone(), // Rule #2 for type string + num_peers: c.num_peers, // Rule #2 for type u32 + num_pending_channels: c.num_pending_channels, // Rule #2 for type u32 + num_active_channels: c.num_active_channels, // Rule #2 for type u32 + num_inactive_channels: c.num_inactive_channels, // Rule #2 for type u32 + version: c.version, // Rule #2 for type string + lightning_dir: c.lightning_dir, // Rule #2 for type string + blockheight: c.blockheight, // Rule #2 for type u32 + network: c.network, // Rule #2 for type string fees_collected_msat: Some(c.fees_collected_msat.into()), // Rule #2 for type msat - address: c.address.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - binding: c.binding.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - warning_bitcoind_sync: c.warning_bitcoind_sync.clone(), // Rule #2 for type string? - warning_lightningd_sync: c.warning_lightningd_sync.clone(), // Rule #2 for type string? + address: c.address.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + binding: c.binding.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + warning_bitcoind_sync: c.warning_bitcoind_sync, // Rule #2 for type string? + warning_lightningd_sync: c.warning_lightningd_sync, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListpeersPeersLog> for pb::ListpeersPeersLog { - fn from(c: &responses::ListpeersPeersLog) -> Self { +impl From for pb::ListpeersPeersLog { + fn from(c: responses::ListpeersPeersLog) -> Self { Self { item_type: c.item_type as i32, - num_skipped: c.num_skipped.clone(), // Rule #2 for type u32? - time: c.time.clone(), // Rule #2 for type string? - source: c.source.clone(), // Rule #2 for type string? - log: c.log.clone(), // Rule #2 for type string? - node_id: c.node_id.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - data: c.data.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + num_skipped: c.num_skipped, // Rule #2 for type u32? + time: c.time, // Rule #2 for type string? + source: c.source, // Rule #2 for type string? + log: c.log, // Rule #2 for type string? + node_id: c.node_id.map(|v| v.to_vec()), // Rule #2 for type pubkey? + data: c.data.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? } } } #[allow(unused_variables)] -impl From<&responses::ListpeersPeersChannelsInflight> for pb::ListpeersPeersChannelsInflight { - fn from(c: &responses::ListpeersPeersChannelsInflight) -> Self { +impl From for pb::ListpeersPeersChannelsInflight { + fn from(c: responses::ListpeersPeersChannelsInflight) -> Self { Self { funding_txid: hex::decode(&c.funding_txid).unwrap(), // Rule #2 for type txid - funding_outnum: c.funding_outnum.clone(), // Rule #2 for type u32 - feerate: c.feerate.clone(), // Rule #2 for type string + funding_outnum: c.funding_outnum, // Rule #2 for type u32 + feerate: c.feerate, // Rule #2 for type string total_funding_msat: Some(c.total_funding_msat.into()), // Rule #2 for type msat our_funding_msat: Some(c.our_funding_msat.into()), // Rule #2 for type msat scratch_txid: hex::decode(&c.scratch_txid).unwrap(), // Rule #2 for type txid @@ -86,47 +86,47 @@ impl From<&responses::ListpeersPeersChannelsInflight> for pb::ListpeersPeersChan } #[allow(unused_variables)] -impl From<&responses::ListpeersPeersChannelsHtlcs> for pb::ListpeersPeersChannelsHtlcs { - fn from(c: &responses::ListpeersPeersChannelsHtlcs) -> Self { +impl From for pb::ListpeersPeersChannelsHtlcs { + fn from(c: responses::ListpeersPeersChannelsHtlcs) -> Self { Self { direction: c.direction as i32, - id: c.id.clone(), // Rule #2 for type u64 + id: c.id, // Rule #2 for type u64 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat - expiry: c.expiry.clone(), // Rule #2 for type u32 - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash - local_trimmed: c.local_trimmed.clone(), // Rule #2 for type boolean? - status: c.status.clone(), // Rule #2 for type string? + expiry: c.expiry, // Rule #2 for type u32 + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash + local_trimmed: c.local_trimmed, // Rule #2 for type boolean? + status: c.status, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { - fn from(c: &responses::ListpeersPeersChannels) -> Self { +impl From for pb::ListpeersPeersChannels { + fn from(c: responses::ListpeersPeersChannels) -> Self { Self { state: c.state as i32, - scratch_txid: c.scratch_txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? - owner: c.owner.clone(), // Rule #2 for type string? - short_channel_id: c.short_channel_id.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? - channel_id: c.channel_id.clone().map(|v| v.to_vec()), // Rule #2 for type hash? - funding_txid: c.funding_txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? - funding_outnum: c.funding_outnum.clone(), // Rule #2 for type u32? - initial_feerate: c.initial_feerate.clone(), // Rule #2 for type string? - last_feerate: c.last_feerate.clone(), // Rule #2 for type string? - next_feerate: c.next_feerate.clone(), // Rule #2 for type string? - next_fee_step: c.next_fee_step.clone(), // Rule #2 for type u32? - inflight: c.inflight.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - private: c.private.clone(), // Rule #2 for type boolean? + scratch_txid: c.scratch_txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid? + owner: c.owner, // Rule #2 for type string? + short_channel_id: c.short_channel_id.map(|v| v.to_string()), // Rule #2 for type short_channel_id? + channel_id: c.channel_id.map(|v| v.to_vec()), // Rule #2 for type hash? + funding_txid: c.funding_txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid? + funding_outnum: c.funding_outnum, // Rule #2 for type u32? + initial_feerate: c.initial_feerate, // Rule #2 for type string? + last_feerate: c.last_feerate, // Rule #2 for type string? + next_feerate: c.next_feerate, // Rule #2 for type string? + next_fee_step: c.next_fee_step, // Rule #2 for type u32? + inflight: c.inflight.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + close_to: c.close_to.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + private: c.private, // Rule #2 for type boolean? opener: c.opener as i32, closer: c.closer.map(|v| v as i32), - features: c.features.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannelsFeatures + features: c.features.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannelsFeatures to_us_msat: c.to_us_msat.map(|f| f.into()), // Rule #2 for type msat? min_to_us_msat: c.min_to_us_msat.map(|f| f.into()), // Rule #2 for type msat? max_to_us_msat: c.max_to_us_msat.map(|f| f.into()), // Rule #2 for type msat? total_msat: c.total_msat.map(|f| f.into()), // Rule #2 for type msat? fee_base_msat: c.fee_base_msat.map(|f| f.into()), // Rule #2 for type msat? - fee_proportional_millionths: c.fee_proportional_millionths.clone(), // Rule #2 for type u32? + fee_proportional_millionths: c.fee_proportional_millionths, // Rule #2 for type u32? dust_limit_msat: c.dust_limit_msat.map(|f| f.into()), // Rule #2 for type msat? max_total_htlc_in_msat: c.max_total_htlc_in_msat.map(|f| f.into()), // Rule #2 for type msat? their_reserve_msat: c.their_reserve_msat.map(|f| f.into()), // Rule #2 for type msat? @@ -136,130 +136,130 @@ impl From<&responses::ListpeersPeersChannels> for pb::ListpeersPeersChannels { minimum_htlc_in_msat: c.minimum_htlc_in_msat.map(|f| f.into()), // Rule #2 for type msat? minimum_htlc_out_msat: c.minimum_htlc_out_msat.map(|f| f.into()), // Rule #2 for type msat? maximum_htlc_out_msat: c.maximum_htlc_out_msat.map(|f| f.into()), // Rule #2 for type msat? - their_to_self_delay: c.their_to_self_delay.clone(), // Rule #2 for type u32? - our_to_self_delay: c.our_to_self_delay.clone(), // Rule #2 for type u32? - max_accepted_htlcs: c.max_accepted_htlcs.clone(), // Rule #2 for type u32? - status: c.status.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - in_payments_offered: c.in_payments_offered.clone(), // Rule #2 for type u64? + their_to_self_delay: c.their_to_self_delay, // Rule #2 for type u32? + our_to_self_delay: c.our_to_self_delay, // Rule #2 for type u32? + max_accepted_htlcs: c.max_accepted_htlcs, // Rule #2 for type u32? + status: c.status.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + in_payments_offered: c.in_payments_offered, // Rule #2 for type u64? in_offered_msat: c.in_offered_msat.map(|f| f.into()), // Rule #2 for type msat? - in_payments_fulfilled: c.in_payments_fulfilled.clone(), // Rule #2 for type u64? + in_payments_fulfilled: c.in_payments_fulfilled, // Rule #2 for type u64? in_fulfilled_msat: c.in_fulfilled_msat.map(|f| f.into()), // Rule #2 for type msat? - out_payments_offered: c.out_payments_offered.clone(), // Rule #2 for type u64? + out_payments_offered: c.out_payments_offered, // Rule #2 for type u64? out_offered_msat: c.out_offered_msat.map(|f| f.into()), // Rule #2 for type msat? - out_payments_fulfilled: c.out_payments_fulfilled.clone(), // Rule #2 for type u64? + out_payments_fulfilled: c.out_payments_fulfilled, // Rule #2 for type u64? out_fulfilled_msat: c.out_fulfilled_msat.map(|f| f.into()), // Rule #2 for type msat? - htlcs: c.htlcs.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - close_to_addr: c.close_to_addr.clone(), // Rule #2 for type string? + htlcs: c.htlcs.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + close_to_addr: c.close_to_addr, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListpeersPeers> for pb::ListpeersPeers { - fn from(c: &responses::ListpeersPeers) -> Self { +impl From for pb::ListpeersPeers { + fn from(c: responses::ListpeersPeers) -> Self { Self { id: c.id.to_vec(), // Rule #2 for type pubkey - connected: c.connected.clone(), // Rule #2 for type boolean - log: c.log.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannels - netaddr: c.netaddr.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 - remote_addr: c.remote_addr.clone(), // Rule #2 for type string? - features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + connected: c.connected, // Rule #2 for type boolean + log: c.log.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + channels: c.channels.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannels + netaddr: c.netaddr.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + remote_addr: c.remote_addr, // Rule #2 for type string? + features: c.features.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? } } } #[allow(unused_variables)] -impl From<&responses::ListpeersResponse> for pb::ListpeersResponse { - fn from(c: &responses::ListpeersResponse) -> Self { +impl From for pb::ListpeersResponse { + fn from(c: responses::ListpeersResponse) -> Self { Self { - peers: c.peers.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeers + peers: c.peers.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeers } } } #[allow(unused_variables)] -impl From<&responses::ListfundsOutputs> for pb::ListfundsOutputs { - fn from(c: &responses::ListfundsOutputs) -> Self { +impl From for pb::ListfundsOutputs { + fn from(c: responses::ListfundsOutputs) -> Self { Self { txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid - output: c.output.clone(), // Rule #2 for type u32 + output: c.output, // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat scriptpubkey: hex::decode(&c.scriptpubkey).unwrap(), // Rule #2 for type hex - address: c.address.clone(), // Rule #2 for type string? - redeemscript: c.redeemscript.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + address: c.address, // Rule #2 for type string? + redeemscript: c.redeemscript.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? status: c.status as i32, - reserved: c.reserved.clone(), // Rule #2 for type boolean - blockheight: c.blockheight.clone(), // Rule #2 for type u32? + reserved: c.reserved, // Rule #2 for type boolean + blockheight: c.blockheight, // Rule #2 for type u32? } } } #[allow(unused_variables)] -impl From<&responses::ListfundsChannels> for pb::ListfundsChannels { - fn from(c: &responses::ListfundsChannels) -> Self { +impl From for pb::ListfundsChannels { + fn from(c: responses::ListfundsChannels) -> Self { Self { peer_id: c.peer_id.to_vec(), // Rule #2 for type pubkey our_amount_msat: Some(c.our_amount_msat.into()), // Rule #2 for type msat amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat funding_txid: hex::decode(&c.funding_txid).unwrap(), // Rule #2 for type txid - funding_output: c.funding_output.clone(), // Rule #2 for type u32 - connected: c.connected.clone(), // Rule #2 for type boolean + funding_output: c.funding_output, // Rule #2 for type u32 + connected: c.connected, // Rule #2 for type boolean state: c.state as i32, - short_channel_id: c.short_channel_id.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? + short_channel_id: c.short_channel_id.map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } #[allow(unused_variables)] -impl From<&responses::ListfundsResponse> for pb::ListfundsResponse { - fn from(c: &responses::ListfundsResponse) -> Self { +impl From for pb::ListfundsResponse { + fn from(c: responses::ListfundsResponse) -> Self { Self { - outputs: c.outputs.iter().map(|i| i.into()).collect(), // Rule #3 for type ListfundsOutputs - channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type ListfundsChannels + outputs: c.outputs.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListfundsOutputs + channels: c.channels.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListfundsChannels } } } #[allow(unused_variables)] -impl From<&responses::SendpayResponse> for pb::SendpayResponse { - fn from(c: &responses::SendpayResponse) -> Self { +impl From for pb::SendpayResponse { + fn from(c: responses::SendpayResponse) -> Self { Self { - id: c.id.clone(), // Rule #2 for type u64 - groupid: c.groupid.clone(), // Rule #2 for type u64? - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + id: c.id, // Rule #2 for type u64 + groupid: c.groupid, // Rule #2 for type u64? + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - created_at: c.created_at.clone(), // Rule #2 for type u64 - completed_at: c.completed_at.clone(), // Rule #2 for type u64? + destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + created_at: c.created_at, // Rule #2 for type u64 + completed_at: c.completed_at, // Rule #2 for type u64? amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat - label: c.label.clone(), // Rule #2 for type string? - partid: c.partid.clone(), // Rule #2 for type u64? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? - message: c.message.clone(), // Rule #2 for type string? + label: c.label, // Rule #2 for type string? + partid: c.partid, // Rule #2 for type u64? + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? + message: c.message, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { - fn from(c: &responses::ListchannelsChannels) -> Self { +impl From for pb::ListchannelsChannels { + fn from(c: responses::ListchannelsChannels) -> Self { Self { source: c.source.to_vec(), // Rule #2 for type pubkey destination: c.destination.to_vec(), // Rule #2 for type pubkey short_channel_id: c.short_channel_id.to_string(), // Rule #2 for type short_channel_id - public: c.public.clone(), // Rule #2 for type boolean + public: c.public, // Rule #2 for type boolean amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat message_flags: c.message_flags.into(), // Rule #2 for type u8 channel_flags: c.channel_flags.into(), // Rule #2 for type u8 - active: c.active.clone(), // Rule #2 for type boolean - last_update: c.last_update.clone(), // Rule #2 for type u32 - base_fee_millisatoshi: c.base_fee_millisatoshi.clone(), // Rule #2 for type u32 - fee_per_millionth: c.fee_per_millionth.clone(), // Rule #2 for type u32 - delay: c.delay.clone(), // Rule #2 for type u32 + active: c.active, // Rule #2 for type boolean + last_update: c.last_update, // Rule #2 for type u32 + base_fee_millisatoshi: c.base_fee_millisatoshi, // Rule #2 for type u32 + fee_per_millionth: c.fee_per_millionth, // Rule #2 for type u32 + delay: c.delay, // Rule #2 for type u32 htlc_minimum_msat: Some(c.htlc_minimum_msat.into()), // Rule #2 for type msat htlc_maximum_msat: c.htlc_maximum_msat.map(|f| f.into()), // Rule #2 for type msat? features: hex::decode(&c.features).unwrap(), // Rule #2 for type hex @@ -268,57 +268,57 @@ impl From<&responses::ListchannelsChannels> for pb::ListchannelsChannels { } #[allow(unused_variables)] -impl From<&responses::ListchannelsResponse> for pb::ListchannelsResponse { - fn from(c: &responses::ListchannelsResponse) -> Self { +impl From for pb::ListchannelsResponse { + fn from(c: responses::ListchannelsResponse) -> Self { Self { - channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type ListchannelsChannels + channels: c.channels.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListchannelsChannels } } } #[allow(unused_variables)] -impl From<&responses::AddgossipResponse> for pb::AddgossipResponse { - fn from(c: &responses::AddgossipResponse) -> Self { +impl From for pb::AddgossipResponse { + fn from(c: responses::AddgossipResponse) -> Self { Self { } } } #[allow(unused_variables)] -impl From<&responses::AutocleaninvoiceResponse> for pb::AutocleaninvoiceResponse { - fn from(c: &responses::AutocleaninvoiceResponse) -> Self { +impl From for pb::AutocleaninvoiceResponse { + fn from(c: responses::AutocleaninvoiceResponse) -> Self { Self { - enabled: c.enabled.clone(), // Rule #2 for type boolean - expired_by: c.expired_by.clone(), // Rule #2 for type u64? - cycle_seconds: c.cycle_seconds.clone(), // Rule #2 for type u64? + enabled: c.enabled, // Rule #2 for type boolean + expired_by: c.expired_by, // Rule #2 for type u64? + cycle_seconds: c.cycle_seconds, // Rule #2 for type u64? } } } #[allow(unused_variables)] -impl From<&responses::CheckmessageResponse> for pb::CheckmessageResponse { - fn from(c: &responses::CheckmessageResponse) -> Self { +impl From for pb::CheckmessageResponse { + fn from(c: responses::CheckmessageResponse) -> Self { Self { - verified: c.verified.clone(), // Rule #2 for type boolean + verified: c.verified, // Rule #2 for type boolean pubkey: c.pubkey.to_vec(), // Rule #2 for type pubkey } } } #[allow(unused_variables)] -impl From<&responses::CloseResponse> for pb::CloseResponse { - fn from(c: &responses::CloseResponse) -> Self { +impl From for pb::CloseResponse { + fn from(c: responses::CloseResponse) -> Self { Self { item_type: c.item_type as i32, - tx: c.tx.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - txid: c.txid.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type txid? + tx: c.tx.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + txid: c.txid.map(|v| hex::decode(v).unwrap()), // Rule #2 for type txid? } } } #[allow(unused_variables)] -impl From<&responses::ConnectResponse> for pb::ConnectResponse { - fn from(c: &responses::ConnectResponse) -> Self { +impl From for pb::ConnectResponse { + fn from(c: responses::ConnectResponse) -> Self { Self { id: c.id.to_vec(), // Rule #2 for type pubkey features: hex::decode(&c.features).unwrap(), // Rule #2 for type hex @@ -328,441 +328,441 @@ impl From<&responses::ConnectResponse> for pb::ConnectResponse { } #[allow(unused_variables)] -impl From<&responses::CreateinvoiceResponse> for pb::CreateinvoiceResponse { - fn from(c: &responses::CreateinvoiceResponse) -> Self { +impl From for pb::CreateinvoiceResponse { + fn from(c: responses::CreateinvoiceResponse) -> Self { Self { - label: c.label.clone(), // Rule #2 for type string - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + label: c.label, // Rule #2 for type string + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? status: c.status as i32, - description: c.description.clone(), // Rule #2 for type string - expires_at: c.expires_at.clone(), // Rule #2 for type u64 - pay_index: c.pay_index.clone(), // Rule #2 for type u64? + description: c.description, // Rule #2 for type string + expires_at: c.expires_at, // Rule #2 for type u64 + pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? - paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? - local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - payer_note: c.payer_note.clone(), // Rule #2 for type string? + paid_at: c.paid_at, // Rule #2 for type u64? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? + local_offer_id: c.local_offer_id.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + payer_note: c.payer_note, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::DatastoreResponse> for pb::DatastoreResponse { - fn from(c: &responses::DatastoreResponse) -> Self { +impl From for pb::DatastoreResponse { + fn from(c: responses::DatastoreResponse) -> Self { Self { - key: c.key.iter().map(|i| i.into()).collect(), // Rule #3 for type string - generation: c.generation.clone(), // Rule #2 for type u64? - hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - string: c.string.clone(), // Rule #2 for type string? + key: c.key.into_iter().map(|i| i.into()).collect(), // Rule #3 for type string + generation: c.generation, // Rule #2 for type u64? + hex: c.hex.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + string: c.string, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::CreateonionResponse> for pb::CreateonionResponse { - fn from(c: &responses::CreateonionResponse) -> Self { +impl From for pb::CreateonionResponse { + fn from(c: responses::CreateonionResponse) -> Self { Self { onion: hex::decode(&c.onion).unwrap(), // Rule #2 for type hex - shared_secrets: c.shared_secrets.iter().map(|i| i.clone().to_vec()).collect(), // Rule #3 for type secret + shared_secrets: c.shared_secrets.into_iter().map(|i| i.to_vec()).collect(), // Rule #3 for type secret } } } #[allow(unused_variables)] -impl From<&responses::DeldatastoreResponse> for pb::DeldatastoreResponse { - fn from(c: &responses::DeldatastoreResponse) -> Self { +impl From for pb::DeldatastoreResponse { + fn from(c: responses::DeldatastoreResponse) -> Self { Self { - key: c.key.iter().map(|i| i.into()).collect(), // Rule #3 for type string - generation: c.generation.clone(), // Rule #2 for type u64? - hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - string: c.string.clone(), // Rule #2 for type string? + key: c.key.into_iter().map(|i| i.into()).collect(), // Rule #3 for type string + generation: c.generation, // Rule #2 for type u64? + hex: c.hex.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + string: c.string, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::DelexpiredinvoiceResponse> for pb::DelexpiredinvoiceResponse { - fn from(c: &responses::DelexpiredinvoiceResponse) -> Self { +impl From for pb::DelexpiredinvoiceResponse { + fn from(c: responses::DelexpiredinvoiceResponse) -> Self { Self { } } } #[allow(unused_variables)] -impl From<&responses::DelinvoiceResponse> for pb::DelinvoiceResponse { - fn from(c: &responses::DelinvoiceResponse) -> Self { +impl From for pb::DelinvoiceResponse { + fn from(c: responses::DelinvoiceResponse) -> Self { Self { - label: c.label.clone(), // Rule #2 for type string - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? + label: c.label, // Rule #2 for type string + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - description: c.description.clone(), // Rule #2 for type string? - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + description: c.description, // Rule #2 for type string? + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, - expires_at: c.expires_at.clone(), // Rule #2 for type u64 - local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - payer_note: c.payer_note.clone(), // Rule #2 for type string? + expires_at: c.expires_at, // Rule #2 for type u64 + local_offer_id: c.local_offer_id.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + payer_note: c.payer_note, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::InvoiceResponse> for pb::InvoiceResponse { - fn from(c: &responses::InvoiceResponse) -> Self { +impl From for pb::InvoiceResponse { + fn from(c: responses::InvoiceResponse) -> Self { Self { - bolt11: c.bolt11.clone(), // Rule #2 for type string - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash - payment_secret: c.payment_secret.clone().to_vec(), // Rule #2 for type secret - expires_at: c.expires_at.clone(), // Rule #2 for type u64 - warning_capacity: c.warning_capacity.clone(), // Rule #2 for type string? - warning_offline: c.warning_offline.clone(), // Rule #2 for type string? - warning_deadends: c.warning_deadends.clone(), // Rule #2 for type string? - warning_private_unused: c.warning_private_unused.clone(), // Rule #2 for type string? - warning_mpp: c.warning_mpp.clone(), // Rule #2 for type string? + bolt11: c.bolt11, // Rule #2 for type string + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash + payment_secret: c.payment_secret.to_vec(), // Rule #2 for type secret + expires_at: c.expires_at, // Rule #2 for type u64 + warning_capacity: c.warning_capacity, // Rule #2 for type string? + warning_offline: c.warning_offline, // Rule #2 for type string? + warning_deadends: c.warning_deadends, // Rule #2 for type string? + warning_private_unused: c.warning_private_unused, // Rule #2 for type string? + warning_mpp: c.warning_mpp, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListdatastoreDatastore> for pb::ListdatastoreDatastore { - fn from(c: &responses::ListdatastoreDatastore) -> Self { +impl From for pb::ListdatastoreDatastore { + fn from(c: responses::ListdatastoreDatastore) -> Self { Self { - key: c.key.iter().map(|i| i.into()).collect(), // Rule #3 for type string - generation: c.generation.clone(), // Rule #2 for type u64? - hex: c.hex.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - string: c.string.clone(), // Rule #2 for type string? + key: c.key.into_iter().map(|i| i.into()).collect(), // Rule #3 for type string + generation: c.generation, // Rule #2 for type u64? + hex: c.hex.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + string: c.string, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListdatastoreResponse> for pb::ListdatastoreResponse { - fn from(c: &responses::ListdatastoreResponse) -> Self { +impl From for pb::ListdatastoreResponse { + fn from(c: responses::ListdatastoreResponse) -> Self { Self { - datastore: c.datastore.iter().map(|i| i.into()).collect(), // Rule #3 for type ListdatastoreDatastore + datastore: c.datastore.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListdatastoreDatastore } } } #[allow(unused_variables)] -impl From<&responses::ListinvoicesInvoices> for pb::ListinvoicesInvoices { - fn from(c: &responses::ListinvoicesInvoices) -> Self { +impl From for pb::ListinvoicesInvoices { + fn from(c: responses::ListinvoicesInvoices) -> Self { Self { - label: c.label.clone(), // Rule #2 for type string - description: c.description.clone(), // Rule #2 for type string? - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + label: c.label, // Rule #2 for type string + description: c.description, // Rule #2 for type string? + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, - expires_at: c.expires_at.clone(), // Rule #2 for type u64 + expires_at: c.expires_at, // Rule #2 for type u64 amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - local_offer_id: c.local_offer_id.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - payer_note: c.payer_note.clone(), // Rule #2 for type string? - pay_index: c.pay_index.clone(), // Rule #2 for type u64? + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + local_offer_id: c.local_offer_id.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + payer_note: c.payer_note, // Rule #2 for type string? + pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? - paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? + paid_at: c.paid_at, // Rule #2 for type u64? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? } } } #[allow(unused_variables)] -impl From<&responses::ListinvoicesResponse> for pb::ListinvoicesResponse { - fn from(c: &responses::ListinvoicesResponse) -> Self { +impl From for pb::ListinvoicesResponse { + fn from(c: responses::ListinvoicesResponse) -> Self { Self { - invoices: c.invoices.iter().map(|i| i.into()).collect(), // Rule #3 for type ListinvoicesInvoices + invoices: c.invoices.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListinvoicesInvoices } } } #[allow(unused_variables)] -impl From<&responses::SendonionResponse> for pb::SendonionResponse { - fn from(c: &responses::SendonionResponse) -> Self { +impl From for pb::SendonionResponse { + fn from(c: responses::SendonionResponse) -> Self { Self { - id: c.id.clone(), // Rule #2 for type u64 - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + id: c.id, // Rule #2 for type u64 + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - created_at: c.created_at.clone(), // Rule #2 for type u64 + destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + created_at: c.created_at, // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat - label: c.label.clone(), // Rule #2 for type string? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - partid: c.partid.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? - message: c.message.clone(), // Rule #2 for type string? + label: c.label, // Rule #2 for type string? + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + partid: c.partid, // Rule #2 for type u64? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? + message: c.message, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListsendpaysPayments> for pb::ListsendpaysPayments { - fn from(c: &responses::ListsendpaysPayments) -> Self { +impl From for pb::ListsendpaysPayments { + fn from(c: responses::ListsendpaysPayments) -> Self { Self { - id: c.id.clone(), // Rule #2 for type u64 - groupid: c.groupid.clone(), // Rule #2 for type u64 - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + id: c.id, // Rule #2 for type u64 + groupid: c.groupid, // Rule #2 for type u64 + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - created_at: c.created_at.clone(), // Rule #2 for type u64 + destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + created_at: c.created_at, // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat - label: c.label.clone(), // Rule #2 for type string? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - description: c.description.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? - erroronion: c.erroronion.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + label: c.label, // Rule #2 for type string? + bolt11: c.bolt11, // Rule #2 for type string? + description: c.description, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? + erroronion: c.erroronion.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? } } } #[allow(unused_variables)] -impl From<&responses::ListsendpaysResponse> for pb::ListsendpaysResponse { - fn from(c: &responses::ListsendpaysResponse) -> Self { +impl From for pb::ListsendpaysResponse { + fn from(c: responses::ListsendpaysResponse) -> Self { Self { - payments: c.payments.iter().map(|i| i.into()).collect(), // Rule #3 for type ListsendpaysPayments + payments: c.payments.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListsendpaysPayments } } } #[allow(unused_variables)] -impl From<&responses::ListtransactionsTransactionsInputs> for pb::ListtransactionsTransactionsInputs { - fn from(c: &responses::ListtransactionsTransactionsInputs) -> Self { +impl From for pb::ListtransactionsTransactionsInputs { + fn from(c: responses::ListtransactionsTransactionsInputs) -> Self { Self { txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid - index: c.index.clone(), // Rule #2 for type u32 - sequence: c.sequence.clone(), // Rule #2 for type u32 + index: c.index, // Rule #2 for type u32 + sequence: c.sequence, // Rule #2 for type u32 item_type: c.item_type.map(|v| v as i32), - channel: c.channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? + channel: c.channel.map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } #[allow(unused_variables)] -impl From<&responses::ListtransactionsTransactionsOutputs> for pb::ListtransactionsTransactionsOutputs { - fn from(c: &responses::ListtransactionsTransactionsOutputs) -> Self { +impl From for pb::ListtransactionsTransactionsOutputs { + fn from(c: responses::ListtransactionsTransactionsOutputs) -> Self { Self { - index: c.index.clone(), // Rule #2 for type u32 + index: c.index, // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat script_pub_key: hex::decode(&c.script_pub_key).unwrap(), // Rule #2 for type hex item_type: c.item_type.map(|v| v as i32), - channel: c.channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? + channel: c.channel.map(|v| v.to_string()), // Rule #2 for type short_channel_id? } } } #[allow(unused_variables)] -impl From<&responses::ListtransactionsTransactions> for pb::ListtransactionsTransactions { - fn from(c: &responses::ListtransactionsTransactions) -> Self { +impl From for pb::ListtransactionsTransactions { + fn from(c: responses::ListtransactionsTransactions) -> Self { Self { hash: hex::decode(&c.hash).unwrap(), // Rule #2 for type txid rawtx: hex::decode(&c.rawtx).unwrap(), // Rule #2 for type hex - blockheight: c.blockheight.clone(), // Rule #2 for type u32 - txindex: c.txindex.clone(), // Rule #2 for type u32 - channel: c.channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? - locktime: c.locktime.clone(), // Rule #2 for type u32 - version: c.version.clone(), // Rule #2 for type u32 - inputs: c.inputs.iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactionsInputs - outputs: c.outputs.iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactionsOutputs + blockheight: c.blockheight, // Rule #2 for type u32 + txindex: c.txindex, // Rule #2 for type u32 + channel: c.channel.map(|v| v.to_string()), // Rule #2 for type short_channel_id? + locktime: c.locktime, // Rule #2 for type u32 + version: c.version, // Rule #2 for type u32 + inputs: c.inputs.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactionsInputs + outputs: c.outputs.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactionsOutputs } } } #[allow(unused_variables)] -impl From<&responses::ListtransactionsResponse> for pb::ListtransactionsResponse { - fn from(c: &responses::ListtransactionsResponse) -> Self { +impl From for pb::ListtransactionsResponse { + fn from(c: responses::ListtransactionsResponse) -> Self { Self { - transactions: c.transactions.iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactions + transactions: c.transactions.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListtransactionsTransactions } } } #[allow(unused_variables)] -impl From<&responses::PayResponse> for pb::PayResponse { - fn from(c: &responses::PayResponse) -> Self { +impl From for pb::PayResponse { + fn from(c: responses::PayResponse) -> Self { Self { - payment_preimage: c.payment_preimage.clone().to_vec(), // Rule #2 for type secret - destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash - created_at: c.created_at.clone(), // Rule #2 for type number - parts: c.parts.clone(), // Rule #2 for type u32 + payment_preimage: c.payment_preimage.to_vec(), // Rule #2 for type secret + destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash + created_at: c.created_at, // Rule #2 for type number + parts: c.parts, // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat - warning_partial_completion: c.warning_partial_completion.clone(), // Rule #2 for type string? + warning_partial_completion: c.warning_partial_completion, // Rule #2 for type string? status: c.status as i32, } } } #[allow(unused_variables)] -impl From<&responses::ListnodesNodesAddresses> for pb::ListnodesNodesAddresses { - fn from(c: &responses::ListnodesNodesAddresses) -> Self { +impl From for pb::ListnodesNodesAddresses { + fn from(c: responses::ListnodesNodesAddresses) -> Self { Self { item_type: c.item_type as i32, port: c.port.into(), // Rule #2 for type u16 - address: c.address.clone(), // Rule #2 for type string? + address: c.address, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::ListnodesNodes> for pb::ListnodesNodes { - fn from(c: &responses::ListnodesNodes) -> Self { +impl From for pb::ListnodesNodes { + fn from(c: responses::ListnodesNodes) -> Self { Self { nodeid: c.nodeid.to_vec(), // Rule #2 for type pubkey - last_timestamp: c.last_timestamp.clone(), // Rule #2 for type u32? - alias: c.alias.clone(), // Rule #2 for type string? - color: c.color.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - features: c.features.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - addresses: c.addresses.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + last_timestamp: c.last_timestamp, // Rule #2 for type u32? + alias: c.alias, // Rule #2 for type string? + color: c.color.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + features: c.features.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + addresses: c.addresses.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 } } } #[allow(unused_variables)] -impl From<&responses::ListnodesResponse> for pb::ListnodesResponse { - fn from(c: &responses::ListnodesResponse) -> Self { +impl From for pb::ListnodesResponse { + fn from(c: responses::ListnodesResponse) -> Self { Self { - nodes: c.nodes.iter().map(|i| i.into()).collect(), // Rule #3 for type ListnodesNodes + nodes: c.nodes.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListnodesNodes } } } #[allow(unused_variables)] -impl From<&responses::WaitanyinvoiceResponse> for pb::WaitanyinvoiceResponse { - fn from(c: &responses::WaitanyinvoiceResponse) -> Self { +impl From for pb::WaitanyinvoiceResponse { + fn from(c: responses::WaitanyinvoiceResponse) -> Self { Self { - label: c.label.clone(), // Rule #2 for type string - description: c.description.clone(), // Rule #2 for type string - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + label: c.label, // Rule #2 for type string + description: c.description, // Rule #2 for type string + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, - expires_at: c.expires_at.clone(), // Rule #2 for type u64 + expires_at: c.expires_at, // Rule #2 for type u64 amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - pay_index: c.pay_index.clone(), // Rule #2 for type u64? + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? - paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? + paid_at: c.paid_at, // Rule #2 for type u64? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? } } } #[allow(unused_variables)] -impl From<&responses::WaitinvoiceResponse> for pb::WaitinvoiceResponse { - fn from(c: &responses::WaitinvoiceResponse) -> Self { +impl From for pb::WaitinvoiceResponse { + fn from(c: responses::WaitinvoiceResponse) -> Self { Self { - label: c.label.clone(), // Rule #2 for type string - description: c.description.clone(), // Rule #2 for type string - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + label: c.label, // Rule #2 for type string + description: c.description, // Rule #2 for type string + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, - expires_at: c.expires_at.clone(), // Rule #2 for type u64 + expires_at: c.expires_at, // Rule #2 for type u64 amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - pay_index: c.pay_index.clone(), // Rule #2 for type u64? + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + pay_index: c.pay_index, // Rule #2 for type u64? amount_received_msat: c.amount_received_msat.map(|f| f.into()), // Rule #2 for type msat? - paid_at: c.paid_at.clone(), // Rule #2 for type u64? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? + paid_at: c.paid_at, // Rule #2 for type u64? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? } } } #[allow(unused_variables)] -impl From<&responses::WaitsendpayResponse> for pb::WaitsendpayResponse { - fn from(c: &responses::WaitsendpayResponse) -> Self { +impl From for pb::WaitsendpayResponse { + fn from(c: responses::WaitsendpayResponse) -> Self { Self { - id: c.id.clone(), // Rule #2 for type u64 - groupid: c.groupid.clone(), // Rule #2 for type u64? - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash + id: c.id, // Rule #2 for type u64 + groupid: c.groupid, // Rule #2 for type u64? + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - created_at: c.created_at.clone(), // Rule #2 for type u64 - completed_at: c.completed_at.clone(), // Rule #2 for type number? + destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + created_at: c.created_at, // Rule #2 for type u64 + completed_at: c.completed_at, // Rule #2 for type number? amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat - label: c.label.clone(), // Rule #2 for type string? - partid: c.partid.clone(), // Rule #2 for type u64? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - payment_preimage: c.payment_preimage.clone().map(|v| v.to_vec()), // Rule #2 for type secret? + label: c.label, // Rule #2 for type string? + partid: c.partid, // Rule #2 for type u64? + bolt11: c.bolt11, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + payment_preimage: c.payment_preimage.map(|v| v.to_vec()), // Rule #2 for type secret? } } } #[allow(unused_variables)] -impl From<&responses::NewaddrResponse> for pb::NewaddrResponse { - fn from(c: &responses::NewaddrResponse) -> Self { +impl From for pb::NewaddrResponse { + fn from(c: responses::NewaddrResponse) -> Self { Self { - bech32: c.bech32.clone(), // Rule #2 for type string? - p2sh_segwit: c.p2sh_segwit.clone(), // Rule #2 for type string? + bech32: c.bech32, // Rule #2 for type string? + p2sh_segwit: c.p2sh_segwit, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::WithdrawResponse> for pb::WithdrawResponse { - fn from(c: &responses::WithdrawResponse) -> Self { +impl From for pb::WithdrawResponse { + fn from(c: responses::WithdrawResponse) -> Self { Self { tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid - psbt: c.psbt.clone(), // Rule #2 for type string + psbt: c.psbt, // Rule #2 for type string } } } #[allow(unused_variables)] -impl From<&responses::KeysendResponse> for pb::KeysendResponse { - fn from(c: &responses::KeysendResponse) -> Self { +impl From for pb::KeysendResponse { + fn from(c: responses::KeysendResponse) -> Self { Self { - payment_preimage: c.payment_preimage.clone().to_vec(), // Rule #2 for type secret - destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - payment_hash: c.payment_hash.clone().to_vec(), // Rule #2 for type hash - created_at: c.created_at.clone(), // Rule #2 for type number - parts: c.parts.clone(), // Rule #2 for type u32 + payment_preimage: c.payment_preimage.to_vec(), // Rule #2 for type secret + destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash + created_at: c.created_at, // Rule #2 for type number + parts: c.parts, // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat - warning_partial_completion: c.warning_partial_completion.clone(), // Rule #2 for type string? + warning_partial_completion: c.warning_partial_completion, // Rule #2 for type string? status: c.status as i32, } } } #[allow(unused_variables)] -impl From<&responses::FundpsbtReservations> for pb::FundpsbtReservations { - fn from(c: &responses::FundpsbtReservations) -> Self { +impl From for pb::FundpsbtReservations { + fn from(c: responses::FundpsbtReservations) -> Self { Self { txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid - vout: c.vout.clone(), // Rule #2 for type u32 - was_reserved: c.was_reserved.clone(), // Rule #2 for type boolean - reserved: c.reserved.clone(), // Rule #2 for type boolean - reserved_to_block: c.reserved_to_block.clone(), // Rule #2 for type u32 + vout: c.vout, // Rule #2 for type u32 + was_reserved: c.was_reserved, // Rule #2 for type boolean + reserved: c.reserved, // Rule #2 for type boolean + reserved_to_block: c.reserved_to_block, // Rule #2 for type u32 } } } #[allow(unused_variables)] -impl From<&responses::FundpsbtResponse> for pb::FundpsbtResponse { - fn from(c: &responses::FundpsbtResponse) -> Self { +impl From for pb::FundpsbtResponse { + fn from(c: responses::FundpsbtResponse) -> Self { Self { - psbt: c.psbt.clone(), // Rule #2 for type string - feerate_per_kw: c.feerate_per_kw.clone(), // Rule #2 for type u32 - estimated_final_weight: c.estimated_final_weight.clone(), // Rule #2 for type u32 + psbt: c.psbt, // Rule #2 for type string + feerate_per_kw: c.feerate_per_kw, // Rule #2 for type u32 + estimated_final_weight: c.estimated_final_weight, // Rule #2 for type u32 excess_msat: Some(c.excess_msat.into()), // Rule #2 for type msat - change_outnum: c.change_outnum.clone(), // Rule #2 for type u32? - reservations: c.reservations.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + change_outnum: c.change_outnum, // Rule #2 for type u32? + reservations: c.reservations.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 } } } #[allow(unused_variables)] -impl From<&responses::SendpsbtResponse> for pb::SendpsbtResponse { - fn from(c: &responses::SendpsbtResponse) -> Self { +impl From for pb::SendpsbtResponse { + fn from(c: responses::SendpsbtResponse) -> Self { Self { tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid @@ -771,44 +771,44 @@ impl From<&responses::SendpsbtResponse> for pb::SendpsbtResponse { } #[allow(unused_variables)] -impl From<&responses::SignpsbtResponse> for pb::SignpsbtResponse { - fn from(c: &responses::SignpsbtResponse) -> Self { +impl From for pb::SignpsbtResponse { + fn from(c: responses::SignpsbtResponse) -> Self { Self { - signed_psbt: c.signed_psbt.clone(), // Rule #2 for type string + signed_psbt: c.signed_psbt, // Rule #2 for type string } } } #[allow(unused_variables)] -impl From<&responses::UtxopsbtReservations> for pb::UtxopsbtReservations { - fn from(c: &responses::UtxopsbtReservations) -> Self { +impl From for pb::UtxopsbtReservations { + fn from(c: responses::UtxopsbtReservations) -> Self { Self { txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid - vout: c.vout.clone(), // Rule #2 for type u32 - was_reserved: c.was_reserved.clone(), // Rule #2 for type boolean - reserved: c.reserved.clone(), // Rule #2 for type boolean - reserved_to_block: c.reserved_to_block.clone(), // Rule #2 for type u32 + vout: c.vout, // Rule #2 for type u32 + was_reserved: c.was_reserved, // Rule #2 for type boolean + reserved: c.reserved, // Rule #2 for type boolean + reserved_to_block: c.reserved_to_block, // Rule #2 for type u32 } } } #[allow(unused_variables)] -impl From<&responses::UtxopsbtResponse> for pb::UtxopsbtResponse { - fn from(c: &responses::UtxopsbtResponse) -> Self { +impl From for pb::UtxopsbtResponse { + fn from(c: responses::UtxopsbtResponse) -> Self { Self { - psbt: c.psbt.clone(), // Rule #2 for type string - feerate_per_kw: c.feerate_per_kw.clone(), // Rule #2 for type u32 - estimated_final_weight: c.estimated_final_weight.clone(), // Rule #2 for type u32 + psbt: c.psbt, // Rule #2 for type string + feerate_per_kw: c.feerate_per_kw, // Rule #2 for type u32 + estimated_final_weight: c.estimated_final_weight, // Rule #2 for type u32 excess_msat: Some(c.excess_msat.into()), // Rule #2 for type msat - change_outnum: c.change_outnum.clone(), // Rule #2 for type u32? - reservations: c.reservations.as_ref().map(|arr| arr.iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 + change_outnum: c.change_outnum, // Rule #2 for type u32? + reservations: c.reservations.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 } } } #[allow(unused_variables)] -impl From<&responses::TxdiscardResponse> for pb::TxdiscardResponse { - fn from(c: &responses::TxdiscardResponse) -> Self { +impl From for pb::TxdiscardResponse { + fn from(c: responses::TxdiscardResponse) -> Self { Self { unsigned_tx: hex::decode(&c.unsigned_tx).unwrap(), // Rule #2 for type hex txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid @@ -817,10 +817,10 @@ impl From<&responses::TxdiscardResponse> for pb::TxdiscardResponse { } #[allow(unused_variables)] -impl From<&responses::TxprepareResponse> for pb::TxprepareResponse { - fn from(c: &responses::TxprepareResponse) -> Self { +impl From for pb::TxprepareResponse { + fn from(c: responses::TxprepareResponse) -> Self { Self { - psbt: c.psbt.clone(), // Rule #2 for type string + psbt: c.psbt, // Rule #2 for type string unsigned_tx: hex::decode(&c.unsigned_tx).unwrap(), // Rule #2 for type hex txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid } @@ -828,10 +828,10 @@ impl From<&responses::TxprepareResponse> for pb::TxprepareResponse { } #[allow(unused_variables)] -impl From<&responses::TxsendResponse> for pb::TxsendResponse { - fn from(c: &responses::TxsendResponse) -> Self { +impl From for pb::TxsendResponse { + fn from(c: responses::TxsendResponse) -> Self { Self { - psbt: c.psbt.clone(), // Rule #2 for type string + psbt: c.psbt, // Rule #2 for type string tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid } @@ -839,70 +839,70 @@ impl From<&responses::TxsendResponse> for pb::TxsendResponse { } #[allow(unused_variables)] -impl From<&responses::DisconnectResponse> for pb::DisconnectResponse { - fn from(c: &responses::DisconnectResponse) -> Self { +impl From for pb::DisconnectResponse { + fn from(c: responses::DisconnectResponse) -> Self { Self { } } } #[allow(unused_variables)] -impl From<&responses::FeeratesResponse> for pb::FeeratesResponse { - fn from(c: &responses::FeeratesResponse) -> Self { +impl From for pb::FeeratesResponse { + fn from(c: responses::FeeratesResponse) -> Self { Self { - warning_missing_feerates: c.warning_missing_feerates.clone(), // Rule #2 for type string? + warning_missing_feerates: c.warning_missing_feerates, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::FundchannelResponse> for pb::FundchannelResponse { - fn from(c: &responses::FundchannelResponse) -> Self { +impl From for pb::FundchannelResponse { + fn from(c: responses::FundchannelResponse) -> Self { Self { tx: hex::decode(&c.tx).unwrap(), // Rule #2 for type hex txid: hex::decode(&c.txid).unwrap(), // Rule #2 for type txid - outnum: c.outnum.clone(), // Rule #2 for type u32 + outnum: c.outnum, // Rule #2 for type u32 channel_id: hex::decode(&c.channel_id).unwrap(), // Rule #2 for type hex - close_to: c.close_to.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - mindepth: c.mindepth.clone(), // Rule #2 for type u32? + close_to: c.close_to.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + mindepth: c.mindepth, // Rule #2 for type u32? } } } #[allow(unused_variables)] -impl From<&responses::GetrouteRoute> for pb::GetrouteRoute { - fn from(c: &responses::GetrouteRoute) -> Self { +impl From for pb::GetrouteRoute { + fn from(c: responses::GetrouteRoute) -> Self { Self { id: c.id.to_vec(), // Rule #2 for type pubkey channel: c.channel.to_string(), // Rule #2 for type short_channel_id - direction: c.direction.clone(), // Rule #2 for type u32 + direction: c.direction, // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat - delay: c.delay.clone(), // Rule #2 for type u32 + delay: c.delay, // Rule #2 for type u32 style: c.style as i32, } } } #[allow(unused_variables)] -impl From<&responses::GetrouteResponse> for pb::GetrouteResponse { - fn from(c: &responses::GetrouteResponse) -> Self { +impl From for pb::GetrouteResponse { + fn from(c: responses::GetrouteResponse) -> Self { Self { - route: c.route.iter().map(|i| i.into()).collect(), // Rule #3 for type GetrouteRoute + route: c.route.into_iter().map(|i| i.into()).collect(), // Rule #3 for type GetrouteRoute } } } #[allow(unused_variables)] -impl From<&responses::ListforwardsForwards> for pb::ListforwardsForwards { - fn from(c: &responses::ListforwardsForwards) -> Self { +impl From for pb::ListforwardsForwards { + fn from(c: responses::ListforwardsForwards) -> Self { Self { in_channel: c.in_channel.to_string(), // Rule #2 for type short_channel_id - in_htlc_id: c.in_htlc_id.clone(), // Rule #2 for type u64 + in_htlc_id: c.in_htlc_id, // Rule #2 for type u64 in_msat: Some(c.in_msat.into()), // Rule #2 for type msat status: c.status as i32, - received_time: c.received_time.clone(), // Rule #2 for type number - out_channel: c.out_channel.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? - out_htlc_id: c.out_htlc_id.clone(), // Rule #2 for type u64? + received_time: c.received_time, // Rule #2 for type number + out_channel: c.out_channel.map(|v| v.to_string()), // Rule #2 for type short_channel_id? + out_htlc_id: c.out_htlc_id, // Rule #2 for type u64? style: c.style.map(|v| v as i32), fee_msat: c.fee_msat.map(|f| f.into()), // Rule #2 for type msat? out_msat: c.out_msat.map(|f| f.into()), // Rule #2 for type msat? @@ -911,46 +911,46 @@ impl From<&responses::ListforwardsForwards> for pb::ListforwardsForwards { } #[allow(unused_variables)] -impl From<&responses::ListforwardsResponse> for pb::ListforwardsResponse { - fn from(c: &responses::ListforwardsResponse) -> Self { +impl From for pb::ListforwardsResponse { + fn from(c: responses::ListforwardsResponse) -> Self { Self { - forwards: c.forwards.iter().map(|i| i.into()).collect(), // Rule #3 for type ListforwardsForwards + forwards: c.forwards.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListforwardsForwards } } } #[allow(unused_variables)] -impl From<&responses::ListpaysPays> for pb::ListpaysPays { - fn from(c: &responses::ListpaysPays) -> Self { +impl From for pb::ListpaysPays { + fn from(c: responses::ListpaysPays) -> Self { Self { payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex status: c.status as i32, - destination: c.destination.as_ref().map(|v| v.to_vec()), // Rule #2 for type pubkey? - created_at: c.created_at.clone(), // Rule #2 for type u64 - completed_at: c.completed_at.clone(), // Rule #2 for type u64? - label: c.label.clone(), // Rule #2 for type string? - bolt11: c.bolt11.clone(), // Rule #2 for type string? - description: c.description.clone(), // Rule #2 for type string? - bolt12: c.bolt12.clone(), // Rule #2 for type string? - preimage: c.preimage.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? - number_of_parts: c.number_of_parts.clone(), // Rule #2 for type u64? - erroronion: c.erroronion.as_ref().map(|v| hex::decode(&v).unwrap()), // Rule #2 for type hex? + destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + created_at: c.created_at, // Rule #2 for type u64 + completed_at: c.completed_at, // Rule #2 for type u64? + label: c.label, // Rule #2 for type string? + bolt11: c.bolt11, // Rule #2 for type string? + description: c.description, // Rule #2 for type string? + bolt12: c.bolt12, // Rule #2 for type string? + preimage: c.preimage.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? + number_of_parts: c.number_of_parts, // Rule #2 for type u64? + erroronion: c.erroronion.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? } } } #[allow(unused_variables)] -impl From<&responses::ListpaysResponse> for pb::ListpaysResponse { - fn from(c: &responses::ListpaysResponse) -> Self { +impl From for pb::ListpaysResponse { + fn from(c: responses::ListpaysResponse) -> Self { Self { - pays: c.pays.iter().map(|i| i.into()).collect(), // Rule #3 for type ListpaysPays + pays: c.pays.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListpaysPays } } } #[allow(unused_variables)] -impl From<&responses::PingResponse> for pb::PingResponse { - fn from(c: &responses::PingResponse) -> Self { +impl From for pb::PingResponse { + fn from(c: responses::PingResponse) -> Self { Self { totlen: c.totlen.into(), // Rule #2 for type u16 } @@ -958,56 +958,56 @@ impl From<&responses::PingResponse> for pb::PingResponse { } #[allow(unused_variables)] -impl From<&responses::SignmessageResponse> for pb::SignmessageResponse { - fn from(c: &responses::SignmessageResponse) -> Self { +impl From for pb::SignmessageResponse { + fn from(c: responses::SignmessageResponse) -> Self { Self { signature: hex::decode(&c.signature).unwrap(), // Rule #2 for type hex recid: hex::decode(&c.recid).unwrap(), // Rule #2 for type hex - zbase: c.zbase.clone(), // Rule #2 for type string + zbase: c.zbase, // Rule #2 for type string } } } #[allow(unused_variables)] -impl From<&responses::StopResponse> for pb::StopResponse { - fn from(c: &responses::StopResponse) -> Self { +impl From for pb::StopResponse { + fn from(c: responses::StopResponse) -> Self { Self { } } } #[allow(unused_variables)] -impl From<&pb::GetinfoRequest> for requests::GetinfoRequest { - fn from(c: &pb::GetinfoRequest) -> Self { +impl From for requests::GetinfoRequest { + fn from(c: pb::GetinfoRequest) -> Self { Self { } } } #[allow(unused_variables)] -impl From<&pb::ListpeersRequest> for requests::ListpeersRequest { - fn from(c: &pb::ListpeersRequest) -> Self { +impl From for requests::ListpeersRequest { + fn from(c: pb::ListpeersRequest) -> Self { Self { - id: c.id.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? - level: c.level.clone(), // Rule #1 for type string? + id: c.id.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + level: c.level, // Rule #1 for type string? } } } #[allow(unused_variables)] -impl From<&pb::ListfundsRequest> for requests::ListfundsRequest { - fn from(c: &pb::ListfundsRequest) -> Self { +impl From for requests::ListfundsRequest { + fn from(c: pb::ListfundsRequest) -> Self { Self { - spent: c.spent.clone(), // Rule #1 for type boolean? + spent: c.spent, // Rule #1 for type boolean? } } } #[allow(unused_variables)] -impl From<&pb::SendpayRoute> for requests::SendpayRoute { - fn from(c: &pb::SendpayRoute) -> Self { +impl From for requests::SendpayRoute { + fn from(c: pb::SendpayRoute) -> Self { Self { - amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat + amount_msat: c.amount_msat.unwrap().into(), // Rule #1 for type msat id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey delay: c.delay as u16, // Rule #1 for type u16 channel: cln_rpc::primitives::ShortChannelId::from_str(&c.channel).unwrap(), // Rule #1 for type short_channel_id @@ -1016,36 +1016,36 @@ impl From<&pb::SendpayRoute> for requests::SendpayRoute { } #[allow(unused_variables)] -impl From<&pb::SendpayRequest> for requests::SendpayRequest { - fn from(c: &pb::SendpayRequest) -> Self { +impl From for requests::SendpayRequest { + fn from(c: pb::SendpayRequest) -> Self { Self { - route: c.route.iter().map(|s| s.into()).collect(), // Rule #4 - payment_hash: c.payment_hash.clone().try_into().unwrap(), // Rule #1 for type hash - label: c.label.clone(), // Rule #1 for type string? - amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? - bolt11: c.bolt11.clone(), // Rule #1 for type string? - payment_secret: c.payment_secret.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type secret? + route: c.route.into_iter().map(|s| s.into()).collect(), // Rule #4 + payment_hash: c.payment_hash.try_into().unwrap(), // Rule #1 for type hash + label: c.label, // Rule #1 for type string? + amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? + bolt11: c.bolt11, // Rule #1 for type string? + payment_secret: c.payment_secret.map(|v| v.try_into().unwrap()), // Rule #1 for type secret? partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? - localofferid: c.localofferid.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? - groupid: c.groupid.clone(), // Rule #1 for type u64? + localofferid: c.localofferid.map(|v| hex::encode(v)), // Rule #1 for type hex? + groupid: c.groupid, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::ListchannelsRequest> for requests::ListchannelsRequest { - fn from(c: &pb::ListchannelsRequest) -> Self { +impl From for requests::ListchannelsRequest { + fn from(c: pb::ListchannelsRequest) -> Self { Self { - short_channel_id: c.short_channel_id.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? - source: c.source.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? - destination: c.destination.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? + short_channel_id: c.short_channel_id.map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? + source: c.source.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + destination: c.destination.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? } } } #[allow(unused_variables)] -impl From<&pb::AddgossipRequest> for requests::AddgossipRequest { - fn from(c: &pb::AddgossipRequest) -> Self { +impl From for requests::AddgossipRequest { + fn from(c: pb::AddgossipRequest) -> Self { Self { message: hex::encode(&c.message), // Rule #1 for type hex } @@ -1053,79 +1053,79 @@ impl From<&pb::AddgossipRequest> for requests::AddgossipRequest { } #[allow(unused_variables)] -impl From<&pb::AutocleaninvoiceRequest> for requests::AutocleaninvoiceRequest { - fn from(c: &pb::AutocleaninvoiceRequest) -> Self { +impl From for requests::AutocleaninvoiceRequest { + fn from(c: pb::AutocleaninvoiceRequest) -> Self { Self { - expired_by: c.expired_by.clone(), // Rule #1 for type u64? - cycle_seconds: c.cycle_seconds.clone(), // Rule #1 for type u64? + expired_by: c.expired_by, // Rule #1 for type u64? + cycle_seconds: c.cycle_seconds, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::CheckmessageRequest> for requests::CheckmessageRequest { - fn from(c: &pb::CheckmessageRequest) -> Self { +impl From for requests::CheckmessageRequest { + fn from(c: pb::CheckmessageRequest) -> Self { Self { - message: c.message.clone(), // Rule #1 for type string - zbase: c.zbase.clone(), // Rule #1 for type string - pubkey: c.pubkey.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? + message: c.message, // Rule #1 for type string + zbase: c.zbase, // Rule #1 for type string + pubkey: c.pubkey.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? } } } #[allow(unused_variables)] -impl From<&pb::CloseRequest> for requests::CloseRequest { - fn from(c: &pb::CloseRequest) -> Self { +impl From for requests::CloseRequest { + fn from(c: pb::CloseRequest) -> Self { Self { - id: c.id.clone(), // Rule #1 for type string - unilateraltimeout: c.unilateraltimeout.clone(), // Rule #1 for type u32? - destination: c.destination.clone(), // Rule #1 for type string? - fee_negotiation_step: c.fee_negotiation_step.clone(), // Rule #1 for type string? - wrong_funding: c.wrong_funding.as_ref().map(|a| a.into()), // Rule #1 for type outpoint? - force_lease_closed: c.force_lease_closed.clone(), // Rule #1 for type boolean? - feerange: Some(c.feerange.iter().map(|s| s.into()).collect()), // Rule #4 + id: c.id, // Rule #1 for type string + unilateraltimeout: c.unilateraltimeout, // Rule #1 for type u32? + destination: c.destination, // Rule #1 for type string? + fee_negotiation_step: c.fee_negotiation_step, // Rule #1 for type string? + wrong_funding: c.wrong_funding.map(|a| a.into()), // Rule #1 for type outpoint? + force_lease_closed: c.force_lease_closed, // Rule #1 for type boolean? + feerange: Some(c.feerange.into_iter().map(|s| s.into()).collect()), // Rule #4 } } } #[allow(unused_variables)] -impl From<&pb::ConnectRequest> for requests::ConnectRequest { - fn from(c: &pb::ConnectRequest) -> Self { +impl From for requests::ConnectRequest { + fn from(c: pb::ConnectRequest) -> Self { Self { - id: c.id.clone(), // Rule #1 for type string - host: c.host.clone(), // Rule #1 for type string? + id: c.id, // Rule #1 for type string + host: c.host, // Rule #1 for type string? port: c.port.map(|v| v as u16), // Rule #1 for type u16? } } } #[allow(unused_variables)] -impl From<&pb::CreateinvoiceRequest> for requests::CreateinvoiceRequest { - fn from(c: &pb::CreateinvoiceRequest) -> Self { +impl From for requests::CreateinvoiceRequest { + fn from(c: pb::CreateinvoiceRequest) -> Self { Self { - invstring: c.invstring.clone(), // Rule #1 for type string - label: c.label.clone(), // Rule #1 for type string + invstring: c.invstring, // Rule #1 for type string + label: c.label, // Rule #1 for type string preimage: hex::encode(&c.preimage), // Rule #1 for type hex } } } #[allow(unused_variables)] -impl From<&pb::DatastoreRequest> for requests::DatastoreRequest { - fn from(c: &pb::DatastoreRequest) -> Self { +impl From for requests::DatastoreRequest { + fn from(c: pb::DatastoreRequest) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), // Rule #4 - string: c.string.clone(), // Rule #1 for type string? - hex: c.hex.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? + key: c.key.into_iter().map(|s| s.into()).collect(), // Rule #4 + string: c.string, // Rule #1 for type string? + hex: c.hex.map(|v| hex::encode(v)), // Rule #1 for type hex? mode: c.mode.map(|v| v.try_into().unwrap()), - generation: c.generation.clone(), // Rule #1 for type u64? + generation: c.generation, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::CreateonionHops> for requests::CreateonionHops { - fn from(c: &pb::CreateonionHops) -> Self { +impl From for requests::CreateonionHops { + fn from(c: pb::CreateonionHops) -> Self { Self { pubkey: cln_rpc::primitives::Pubkey::from_slice(&c.pubkey).unwrap(), // Rule #1 for type pubkey payload: hex::encode(&c.payload), // Rule #1 for type hex @@ -1134,185 +1134,185 @@ impl From<&pb::CreateonionHops> for requests::CreateonionHops { } #[allow(unused_variables)] -impl From<&pb::CreateonionRequest> for requests::CreateonionRequest { - fn from(c: &pb::CreateonionRequest) -> Self { +impl From for requests::CreateonionRequest { + fn from(c: pb::CreateonionRequest) -> Self { Self { - hops: c.hops.iter().map(|s| s.into()).collect(), // Rule #4 + hops: c.hops.into_iter().map(|s| s.into()).collect(), // Rule #4 assocdata: hex::encode(&c.assocdata), // Rule #1 for type hex - session_key: c.session_key.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type secret? + session_key: c.session_key.map(|v| v.try_into().unwrap()), // Rule #1 for type secret? onion_size: c.onion_size.map(|v| v as u16), // Rule #1 for type u16? } } } #[allow(unused_variables)] -impl From<&pb::DeldatastoreRequest> for requests::DeldatastoreRequest { - fn from(c: &pb::DeldatastoreRequest) -> Self { +impl From for requests::DeldatastoreRequest { + fn from(c: pb::DeldatastoreRequest) -> Self { Self { - key: c.key.iter().map(|s| s.into()).collect(), // Rule #4 - generation: c.generation.clone(), // Rule #1 for type u64? + key: c.key.into_iter().map(|s| s.into()).collect(), // Rule #4 + generation: c.generation, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::DelexpiredinvoiceRequest> for requests::DelexpiredinvoiceRequest { - fn from(c: &pb::DelexpiredinvoiceRequest) -> Self { +impl From for requests::DelexpiredinvoiceRequest { + fn from(c: pb::DelexpiredinvoiceRequest) -> Self { Self { - maxexpirytime: c.maxexpirytime.clone(), // Rule #1 for type u64? + maxexpirytime: c.maxexpirytime, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::DelinvoiceRequest> for requests::DelinvoiceRequest { - fn from(c: &pb::DelinvoiceRequest) -> Self { +impl From for requests::DelinvoiceRequest { + fn from(c: pb::DelinvoiceRequest) -> Self { Self { - label: c.label.clone(), // Rule #1 for type string + label: c.label, // Rule #1 for type string status: c.status.try_into().unwrap(), - desconly: c.desconly.clone(), // Rule #1 for type boolean? + desconly: c.desconly, // Rule #1 for type boolean? } } } #[allow(unused_variables)] -impl From<&pb::InvoiceRequest> for requests::InvoiceRequest { - fn from(c: &pb::InvoiceRequest) -> Self { +impl From for requests::InvoiceRequest { + fn from(c: pb::InvoiceRequest) -> Self { Self { - amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat_or_any - description: c.description.clone(), // Rule #1 for type string - label: c.label.clone(), // Rule #1 for type string - expiry: c.expiry.clone(), // Rule #1 for type u64? - fallbacks: Some(c.fallbacks.iter().map(|s| s.into()).collect()), // Rule #4 - preimage: c.preimage.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? - exposeprivatechannels: c.exposeprivatechannels.clone(), // Rule #1 for type boolean? - cltv: c.cltv.clone(), // Rule #1 for type u32? - deschashonly: c.deschashonly.clone(), // Rule #1 for type boolean? + amount_msat: c.amount_msat.unwrap().into(), // Rule #1 for type msat_or_any + description: c.description, // Rule #1 for type string + label: c.label, // Rule #1 for type string + expiry: c.expiry, // Rule #1 for type u64? + fallbacks: Some(c.fallbacks.into_iter().map(|s| s.into()).collect()), // Rule #4 + preimage: c.preimage.map(|v| hex::encode(v)), // Rule #1 for type hex? + exposeprivatechannels: c.exposeprivatechannels, // Rule #1 for type boolean? + cltv: c.cltv, // Rule #1 for type u32? + deschashonly: c.deschashonly, // Rule #1 for type boolean? } } } #[allow(unused_variables)] -impl From<&pb::ListdatastoreRequest> for requests::ListdatastoreRequest { - fn from(c: &pb::ListdatastoreRequest) -> Self { +impl From for requests::ListdatastoreRequest { + fn from(c: pb::ListdatastoreRequest) -> Self { Self { - key: Some(c.key.iter().map(|s| s.into()).collect()), // Rule #4 + key: Some(c.key.into_iter().map(|s| s.into()).collect()), // Rule #4 } } } #[allow(unused_variables)] -impl From<&pb::ListinvoicesRequest> for requests::ListinvoicesRequest { - fn from(c: &pb::ListinvoicesRequest) -> Self { +impl From for requests::ListinvoicesRequest { + fn from(c: pb::ListinvoicesRequest) -> Self { Self { - label: c.label.clone(), // Rule #1 for type string? - invstring: c.invstring.clone(), // Rule #1 for type string? - payment_hash: c.payment_hash.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? - offer_id: c.offer_id.clone(), // Rule #1 for type string? + label: c.label, // Rule #1 for type string? + invstring: c.invstring, // Rule #1 for type string? + payment_hash: c.payment_hash.map(|v| hex::encode(v)), // Rule #1 for type hex? + offer_id: c.offer_id, // Rule #1 for type string? } } } #[allow(unused_variables)] -impl From<&pb::SendonionRequest> for requests::SendonionRequest { - fn from(c: &pb::SendonionRequest) -> Self { +impl From for requests::SendonionRequest { + fn from(c: pb::SendonionRequest) -> Self { Self { onion: hex::encode(&c.onion), // Rule #1 for type hex - payment_hash: c.payment_hash.clone().try_into().unwrap(), // Rule #1 for type hash - label: c.label.clone(), // Rule #1 for type string? - shared_secrets: Some(c.shared_secrets.iter().map(|s| s.clone().try_into().unwrap()).collect()), // Rule #4 + payment_hash: c.payment_hash.try_into().unwrap(), // Rule #1 for type hash + label: c.label, // Rule #1 for type string? + shared_secrets: Some(c.shared_secrets.into_iter().map(|s| s.try_into().unwrap()).collect()), // Rule #4 partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? - bolt11: c.bolt11.clone(), // Rule #1 for type string? - amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? - destination: c.destination.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? - localofferid: c.localofferid.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type hash? - groupid: c.groupid.clone(), // Rule #1 for type u64? + bolt11: c.bolt11, // Rule #1 for type string? + amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? + destination: c.destination.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + localofferid: c.localofferid.map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + groupid: c.groupid, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::ListsendpaysRequest> for requests::ListsendpaysRequest { - fn from(c: &pb::ListsendpaysRequest) -> Self { +impl From for requests::ListsendpaysRequest { + fn from(c: pb::ListsendpaysRequest) -> Self { Self { - bolt11: c.bolt11.clone(), // Rule #1 for type string? - payment_hash: c.payment_hash.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + bolt11: c.bolt11, // Rule #1 for type string? + payment_hash: c.payment_hash.map(|v| v.try_into().unwrap()), // Rule #1 for type hash? status: c.status.map(|v| v.try_into().unwrap()), } } } #[allow(unused_variables)] -impl From<&pb::ListtransactionsRequest> for requests::ListtransactionsRequest { - fn from(c: &pb::ListtransactionsRequest) -> Self { +impl From for requests::ListtransactionsRequest { + fn from(c: pb::ListtransactionsRequest) -> Self { Self { } } } #[allow(unused_variables)] -impl From<&pb::PayRequest> for requests::PayRequest { - fn from(c: &pb::PayRequest) -> Self { +impl From for requests::PayRequest { + fn from(c: pb::PayRequest) -> Self { Self { - bolt11: c.bolt11.clone(), // Rule #1 for type string - amount_msat: c.amount_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? - label: c.label.clone(), // Rule #1 for type string? - riskfactor: c.riskfactor.clone(), // Rule #1 for type number? - maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type number? + bolt11: c.bolt11, // Rule #1 for type string + amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? + label: c.label, // Rule #1 for type string? + riskfactor: c.riskfactor, // Rule #1 for type number? + maxfeepercent: c.maxfeepercent, // Rule #1 for type number? retry_for: c.retry_for.map(|v| v as u16), // Rule #1 for type u16? maxdelay: c.maxdelay.map(|v| v as u16), // Rule #1 for type u16? - exemptfee: c.exemptfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? - localofferid: c.localofferid.clone().map(|v| hex::encode(v)), // Rule #1 for type hex? - exclude: Some(c.exclude.iter().map(|s| s.into()).collect()), // Rule #4 - maxfee: c.maxfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? - description: c.description.clone(), // Rule #1 for type string? + exemptfee: c.exemptfee.map(|a| a.into()), // Rule #1 for type msat? + localofferid: c.localofferid.map(|v| hex::encode(v)), // Rule #1 for type hex? + exclude: Some(c.exclude.into_iter().map(|s| s.into()).collect()), // Rule #4 + maxfee: c.maxfee.map(|a| a.into()), // Rule #1 for type msat? + description: c.description, // Rule #1 for type string? } } } #[allow(unused_variables)] -impl From<&pb::ListnodesRequest> for requests::ListnodesRequest { - fn from(c: &pb::ListnodesRequest) -> Self { +impl From for requests::ListnodesRequest { + fn from(c: pb::ListnodesRequest) -> Self { Self { - id: c.id.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? + id: c.id.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? } } } #[allow(unused_variables)] -impl From<&pb::WaitanyinvoiceRequest> for requests::WaitanyinvoiceRequest { - fn from(c: &pb::WaitanyinvoiceRequest) -> Self { +impl From for requests::WaitanyinvoiceRequest { + fn from(c: pb::WaitanyinvoiceRequest) -> Self { Self { - lastpay_index: c.lastpay_index.clone(), // Rule #1 for type u64? - timeout: c.timeout.clone(), // Rule #1 for type u64? + lastpay_index: c.lastpay_index, // Rule #1 for type u64? + timeout: c.timeout, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::WaitinvoiceRequest> for requests::WaitinvoiceRequest { - fn from(c: &pb::WaitinvoiceRequest) -> Self { +impl From for requests::WaitinvoiceRequest { + fn from(c: pb::WaitinvoiceRequest) -> Self { Self { - label: c.label.clone(), // Rule #1 for type string + label: c.label, // Rule #1 for type string } } } #[allow(unused_variables)] -impl From<&pb::WaitsendpayRequest> for requests::WaitsendpayRequest { - fn from(c: &pb::WaitsendpayRequest) -> Self { +impl From for requests::WaitsendpayRequest { + fn from(c: pb::WaitsendpayRequest) -> Self { Self { - payment_hash: c.payment_hash.clone().try_into().unwrap(), // Rule #1 for type hash - timeout: c.timeout.clone(), // Rule #1 for type u32? - partid: c.partid.clone(), // Rule #1 for type u64? - groupid: c.groupid.clone(), // Rule #1 for type u64? + payment_hash: c.payment_hash.try_into().unwrap(), // Rule #1 for type hash + timeout: c.timeout, // Rule #1 for type u32? + partid: c.partid, // Rule #1 for type u64? + groupid: c.groupid, // Rule #1 for type u64? } } } #[allow(unused_variables)] -impl From<&pb::NewaddrRequest> for requests::NewaddrRequest { - fn from(c: &pb::NewaddrRequest) -> Self { +impl From for requests::NewaddrRequest { + fn from(c: pb::NewaddrRequest) -> Self { Self { addresstype: c.addresstype.map(|v| v.try_into().unwrap()), } @@ -1320,90 +1320,90 @@ impl From<&pb::NewaddrRequest> for requests::NewaddrRequest { } #[allow(unused_variables)] -impl From<&pb::WithdrawRequest> for requests::WithdrawRequest { - fn from(c: &pb::WithdrawRequest) -> Self { +impl From for requests::WithdrawRequest { + fn from(c: pb::WithdrawRequest) -> Self { Self { - destination: c.destination.clone(), // Rule #1 for type string - satoshi: c.satoshi.as_ref().map(|a| a.into()), // Rule #1 for type msat_or_all? - feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? + destination: c.destination, // Rule #1 for type string + satoshi: c.satoshi.map(|a| a.into()), // Rule #1 for type msat_or_all? + feerate: c.feerate.map(|a| a.into()), // Rule #1 for type feerate? minconf: c.minconf.map(|v| v as u16), // Rule #1 for type u16? - utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 + utxos: Some(c.utxos.into_iter().map(|s| s.into()).collect()), // Rule #4 } } } #[allow(unused_variables)] -impl From<&pb::KeysendRequest> for requests::KeysendRequest { - fn from(c: &pb::KeysendRequest) -> Self { +impl From for requests::KeysendRequest { + fn from(c: pb::KeysendRequest) -> Self { Self { destination: cln_rpc::primitives::Pubkey::from_slice(&c.destination).unwrap(), // Rule #1 for type pubkey - amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat - label: c.label.clone(), // Rule #1 for type string? - maxfeepercent: c.maxfeepercent.clone(), // Rule #1 for type number? - retry_for: c.retry_for.clone(), // Rule #1 for type u32? - maxdelay: c.maxdelay.clone(), // Rule #1 for type u32? - exemptfee: c.exemptfee.as_ref().map(|a| a.into()), // Rule #1 for type msat? - routehints: c.routehints.clone().map(|rl| rl.into()), // Rule #1 for type RoutehintList? + amount_msat: c.amount_msat.unwrap().into(), // Rule #1 for type msat + label: c.label, // Rule #1 for type string? + maxfeepercent: c.maxfeepercent, // Rule #1 for type number? + retry_for: c.retry_for, // Rule #1 for type u32? + maxdelay: c.maxdelay, // Rule #1 for type u32? + exemptfee: c.exemptfee.map(|a| a.into()), // Rule #1 for type msat? + routehints: c.routehints.map(|rl| rl.into()), // Rule #1 for type RoutehintList? } } } #[allow(unused_variables)] -impl From<&pb::FundpsbtRequest> for requests::FundpsbtRequest { - fn from(c: &pb::FundpsbtRequest) -> Self { +impl From for requests::FundpsbtRequest { + fn from(c: pb::FundpsbtRequest) -> Self { Self { - satoshi: c.satoshi.as_ref().unwrap().into(), // Rule #1 for type msat - feerate: c.feerate.as_ref().unwrap().into(), // Rule #1 for type feerate - startweight: c.startweight.clone(), // Rule #1 for type u32 - minconf: c.minconf.clone(), // Rule #1 for type u32? - reserve: c.reserve.clone(), // Rule #1 for type u32? - locktime: c.locktime.clone(), // Rule #1 for type u32? - min_witness_weight: c.min_witness_weight.clone(), // Rule #1 for type u32? - excess_as_change: c.excess_as_change.clone(), // Rule #1 for type boolean? + satoshi: c.satoshi.unwrap().into(), // Rule #1 for type msat + feerate: c.feerate.unwrap().into(), // Rule #1 for type feerate + startweight: c.startweight, // Rule #1 for type u32 + minconf: c.minconf, // Rule #1 for type u32? + reserve: c.reserve, // Rule #1 for type u32? + locktime: c.locktime, // Rule #1 for type u32? + min_witness_weight: c.min_witness_weight, // Rule #1 for type u32? + excess_as_change: c.excess_as_change, // Rule #1 for type boolean? } } } #[allow(unused_variables)] -impl From<&pb::SendpsbtRequest> for requests::SendpsbtRequest { - fn from(c: &pb::SendpsbtRequest) -> Self { +impl From for requests::SendpsbtRequest { + fn from(c: pb::SendpsbtRequest) -> Self { Self { - psbt: c.psbt.clone(), // Rule #1 for type string - reserve: c.reserve.clone(), // Rule #1 for type boolean? + psbt: c.psbt, // Rule #1 for type string + reserve: c.reserve, // Rule #1 for type boolean? } } } #[allow(unused_variables)] -impl From<&pb::SignpsbtRequest> for requests::SignpsbtRequest { - fn from(c: &pb::SignpsbtRequest) -> Self { +impl From for requests::SignpsbtRequest { + fn from(c: pb::SignpsbtRequest) -> Self { Self { - psbt: c.psbt.clone(), // Rule #1 for type string - signonly: Some(c.signonly.iter().map(|s| s.clone()).collect()), // Rule #4 + psbt: c.psbt, // Rule #1 for type string + signonly: Some(c.signonly.into_iter().map(|s| s).collect()), // Rule #4 } } } #[allow(unused_variables)] -impl From<&pb::UtxopsbtRequest> for requests::UtxopsbtRequest { - fn from(c: &pb::UtxopsbtRequest) -> Self { +impl From for requests::UtxopsbtRequest { + fn from(c: pb::UtxopsbtRequest) -> Self { Self { - satoshi: c.satoshi.as_ref().unwrap().into(), // Rule #1 for type msat - feerate: c.feerate.as_ref().unwrap().into(), // Rule #1 for type feerate - startweight: c.startweight.clone(), // Rule #1 for type u32 - utxos: c.utxos.iter().map(|s| s.into()).collect(), // Rule #4 - reserve: c.reserve.clone(), // Rule #1 for type u32? - reservedok: c.reservedok.clone(), // Rule #1 for type boolean? - locktime: c.locktime.clone(), // Rule #1 for type u32? - min_witness_weight: c.min_witness_weight.clone(), // Rule #1 for type u32? - excess_as_change: c.excess_as_change.clone(), // Rule #1 for type boolean? + satoshi: c.satoshi.unwrap().into(), // Rule #1 for type msat + feerate: c.feerate.unwrap().into(), // Rule #1 for type feerate + startweight: c.startweight, // Rule #1 for type u32 + utxos: c.utxos.into_iter().map(|s| s.into()).collect(), // Rule #4 + reserve: c.reserve, // Rule #1 for type u32? + reservedok: c.reservedok, // Rule #1 for type boolean? + locktime: c.locktime, // Rule #1 for type u32? + min_witness_weight: c.min_witness_weight, // Rule #1 for type u32? + excess_as_change: c.excess_as_change, // Rule #1 for type boolean? } } } #[allow(unused_variables)] -impl From<&pb::TxdiscardRequest> for requests::TxdiscardRequest { - fn from(c: &pb::TxdiscardRequest) -> Self { +impl From for requests::TxdiscardRequest { + fn from(c: pb::TxdiscardRequest) -> Self { Self { txid: hex::encode(&c.txid), // Rule #1 for type txid } @@ -1411,20 +1411,20 @@ impl From<&pb::TxdiscardRequest> for requests::TxdiscardRequest { } #[allow(unused_variables)] -impl From<&pb::TxprepareRequest> for requests::TxprepareRequest { - fn from(c: &pb::TxprepareRequest) -> Self { +impl From for requests::TxprepareRequest { + fn from(c: pb::TxprepareRequest) -> Self { Self { - outputs: c.outputs.iter().map(|s| s.into()).collect(), // Rule #4 - feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? - minconf: c.minconf.clone(), // Rule #1 for type u32? - utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 + outputs: c.outputs.into_iter().map(|s| s.into()).collect(), // Rule #4 + feerate: c.feerate.map(|a| a.into()), // Rule #1 for type feerate? + minconf: c.minconf, // Rule #1 for type u32? + utxos: Some(c.utxos.into_iter().map(|s| s.into()).collect()), // Rule #4 } } } #[allow(unused_variables)] -impl From<&pb::TxsendRequest> for requests::TxsendRequest { - fn from(c: &pb::TxsendRequest) -> Self { +impl From for requests::TxsendRequest { + fn from(c: pb::TxsendRequest) -> Self { Self { txid: hex::encode(&c.txid), // Rule #1 for type txid } @@ -1432,18 +1432,18 @@ impl From<&pb::TxsendRequest> for requests::TxsendRequest { } #[allow(unused_variables)] -impl From<&pb::DisconnectRequest> for requests::DisconnectRequest { - fn from(c: &pb::DisconnectRequest) -> Self { +impl From for requests::DisconnectRequest { + fn from(c: pb::DisconnectRequest) -> Self { Self { id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey - force: c.force.clone(), // Rule #1 for type boolean? + force: c.force, // Rule #1 for type boolean? } } } #[allow(unused_variables)] -impl From<&pb::FeeratesRequest> for requests::FeeratesRequest { - fn from(c: &pb::FeeratesRequest) -> Self { +impl From for requests::FeeratesRequest { + fn from(c: pb::FeeratesRequest) -> Self { Self { style: c.style.try_into().unwrap(), } @@ -1451,86 +1451,86 @@ impl From<&pb::FeeratesRequest> for requests::FeeratesRequest { } #[allow(unused_variables)] -impl From<&pb::FundchannelRequest> for requests::FundchannelRequest { - fn from(c: &pb::FundchannelRequest) -> Self { +impl From for requests::FundchannelRequest { + fn from(c: pb::FundchannelRequest) -> Self { Self { id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey - amount: c.amount.as_ref().unwrap().into(), // Rule #1 for type msat_or_all - feerate: c.feerate.as_ref().map(|a| a.into()), // Rule #1 for type feerate? - announce: c.announce.clone(), // Rule #1 for type boolean? - minconf: c.minconf.clone(), // Rule #1 for type u32? - push_msat: c.push_msat.as_ref().map(|a| a.into()), // Rule #1 for type msat? - close_to: c.close_to.clone(), // Rule #1 for type string? - request_amt: c.request_amt.as_ref().map(|a| a.into()), // Rule #1 for type msat? - compact_lease: c.compact_lease.clone(), // Rule #1 for type string? - utxos: Some(c.utxos.iter().map(|s| s.into()).collect()), // Rule #4 - mindepth: c.mindepth.clone(), // Rule #1 for type u32? - reserve: c.reserve.as_ref().map(|a| a.into()), // Rule #1 for type msat? + amount: c.amount.unwrap().into(), // Rule #1 for type msat_or_all + feerate: c.feerate.map(|a| a.into()), // Rule #1 for type feerate? + announce: c.announce, // Rule #1 for type boolean? + minconf: c.minconf, // Rule #1 for type u32? + push_msat: c.push_msat.map(|a| a.into()), // Rule #1 for type msat? + close_to: c.close_to, // Rule #1 for type string? + request_amt: c.request_amt.map(|a| a.into()), // Rule #1 for type msat? + compact_lease: c.compact_lease, // Rule #1 for type string? + utxos: Some(c.utxos.into_iter().map(|s| s.into()).collect()), // Rule #4 + mindepth: c.mindepth, // Rule #1 for type u32? + reserve: c.reserve.map(|a| a.into()), // Rule #1 for type msat? } } } #[allow(unused_variables)] -impl From<&pb::GetrouteRequest> for requests::GetrouteRequest { - fn from(c: &pb::GetrouteRequest) -> Self { +impl From for requests::GetrouteRequest { + fn from(c: pb::GetrouteRequest) -> Self { Self { id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey - amount_msat: c.amount_msat.as_ref().unwrap().into(), // Rule #1 for type msat - riskfactor: c.riskfactor.clone(), // Rule #1 for type u64 - cltv: c.cltv.clone(), // Rule #1 for type number? - fromid: c.fromid.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap()), // Rule #1 for type pubkey? - fuzzpercent: c.fuzzpercent.clone(), // Rule #1 for type u32? - exclude: Some(c.exclude.iter().map(|s| s.into()).collect()), // Rule #4 - maxhops: c.maxhops.clone(), // Rule #1 for type u32? + amount_msat: c.amount_msat.unwrap().into(), // Rule #1 for type msat + riskfactor: c.riskfactor, // Rule #1 for type u64 + cltv: c.cltv, // Rule #1 for type number? + fromid: c.fromid.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + fuzzpercent: c.fuzzpercent, // Rule #1 for type u32? + exclude: Some(c.exclude.into_iter().map(|s| s.into()).collect()), // Rule #4 + maxhops: c.maxhops, // Rule #1 for type u32? } } } #[allow(unused_variables)] -impl From<&pb::ListforwardsRequest> for requests::ListforwardsRequest { - fn from(c: &pb::ListforwardsRequest) -> Self { +impl From for requests::ListforwardsRequest { + fn from(c: pb::ListforwardsRequest) -> Self { Self { status: c.status.map(|v| v.try_into().unwrap()), - in_channel: c.in_channel.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? - out_channel: c.out_channel.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? + in_channel: c.in_channel.map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? + out_channel: c.out_channel.map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? } } } #[allow(unused_variables)] -impl From<&pb::ListpaysRequest> for requests::ListpaysRequest { - fn from(c: &pb::ListpaysRequest) -> Self { +impl From for requests::ListpaysRequest { + fn from(c: pb::ListpaysRequest) -> Self { Self { - bolt11: c.bolt11.clone(), // Rule #1 for type string? - payment_hash: c.payment_hash.clone().map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + bolt11: c.bolt11, // Rule #1 for type string? + payment_hash: c.payment_hash.map(|v| v.try_into().unwrap()), // Rule #1 for type hash? status: c.status.map(|v| v.try_into().unwrap()), } } } #[allow(unused_variables)] -impl From<&pb::PingRequest> for requests::PingRequest { - fn from(c: &pb::PingRequest) -> Self { +impl From for requests::PingRequest { + fn from(c: pb::PingRequest) -> Self { Self { id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey - len: c.len.clone(), // Rule #1 for type number? - pongbytes: c.pongbytes.clone(), // Rule #1 for type number? + len: c.len, // Rule #1 for type number? + pongbytes: c.pongbytes, // Rule #1 for type number? } } } #[allow(unused_variables)] -impl From<&pb::SignmessageRequest> for requests::SignmessageRequest { - fn from(c: &pb::SignmessageRequest) -> Self { +impl From for requests::SignmessageRequest { + fn from(c: pb::SignmessageRequest) -> Self { Self { - message: c.message.clone(), // Rule #1 for type string + message: c.message, // Rule #1 for type string } } } #[allow(unused_variables)] -impl From<&pb::StopRequest> for requests::StopRequest { - fn from(c: &pb::StopRequest) -> Self { +impl From for requests::StopRequest { + fn from(c: pb::StopRequest) -> Self { Self { } } diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index cfec44b655f1..00291d19813f 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -12,8 +12,8 @@ impl From for Amount { } } -impl From<&Amount> for JAmount { - fn from(a: &Amount) -> Self { +impl From for JAmount { + fn from(a: Amount) -> Self { JAmount::from_msat(a.msat) } } @@ -27,19 +27,19 @@ impl From for Outpoint { } } -impl From<&Outpoint> for JOutpoint { - fn from(a: &Outpoint) -> Self { +impl From for JOutpoint { + fn from(a: Outpoint) -> Self { JOutpoint { - txid: a.txid.clone(), + txid: a.txid, outnum: a.outnum, } } } -impl From<&Feerate> for cln_rpc::primitives::Feerate { - fn from(f: &Feerate) -> cln_rpc::primitives::Feerate { +impl From for cln_rpc::primitives::Feerate { + fn from(f: Feerate) -> cln_rpc::primitives::Feerate { use feerate::Style; - match f.style.clone().unwrap() { + match f.style.unwrap() { Style::Slow(_) => JFeerate::Slow, Style::Normal(_) => JFeerate::Normal, Style::Urgent(_) => JFeerate::Urgent, @@ -49,11 +49,11 @@ impl From<&Feerate> for cln_rpc::primitives::Feerate { } } -impl From<&OutputDesc> for JOutputDesc { - fn from(od: &OutputDesc) -> JOutputDesc { +impl From for JOutputDesc { + fn from(od: OutputDesc) -> JOutputDesc { JOutputDesc { - address: od.address.clone(), - amount: od.amount.as_ref().unwrap().into(), + address: od.address, + amount: od.amount.unwrap().into(), } } } @@ -71,9 +71,9 @@ impl From for AmountOrAll { } } -impl From<&AmountOrAll> for JAmountOrAll { - fn from(a: &AmountOrAll) -> Self { - match &a.value { +impl From for JAmountOrAll { + fn from(a: AmountOrAll) -> Self { + match a.value { Some(amount_or_all::Value::Amount(a)) => JAmountOrAll::Amount(a.into()), Some(amount_or_all::Value::All(_)) => JAmountOrAll::All, None => panic!("AmountOrAll is neither amount nor all: {:?}", a), @@ -93,9 +93,9 @@ impl From for AmountOrAny { } } } -impl From<&AmountOrAny> for JAmountOrAny { - fn from(a: &AmountOrAny) -> Self { - match &a.value { +impl From for JAmountOrAny { + fn from(a: AmountOrAny) -> Self { + match a.value { Some(amount_or_any::Value::Amount(a)) => JAmountOrAny::Amount(a.into()), Some(amount_or_any::Value::Any(_)) => JAmountOrAny::Any, None => panic!("AmountOrAll is neither amount nor any: {:?}", a), @@ -107,7 +107,7 @@ impl From for cln_rpc::primitives::Routehop { Self { id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), scid: cln_rpc::primitives::ShortChannelId::from_str(&c.short_channel_id).unwrap(), - feebase: c.feebase.as_ref().unwrap().into(), + feebase: c.feebase.unwrap().into(), feeprop: c.feeprop, expirydelta: c.expirydelta as u16, } diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 16f75b5613df..22b6a5c09529 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -31,7 +31,7 @@ async fn getinfo( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::GetinfoRequest = (&req).into(); + let req: requests::GetinfoRequest = req.into(); debug!("Client asked for getinfo"); trace!("getinfo request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -45,7 +45,7 @@ async fn getinfo( match result { Response::Getinfo(r) => { trace!("getinfo response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -63,7 +63,7 @@ async fn list_peers( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListpeersRequest = (&req).into(); + let req: requests::ListpeersRequest = req.into(); debug!("Client asked for list_peers"); trace!("list_peers request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -77,7 +77,7 @@ async fn list_peers( match result { Response::ListPeers(r) => { trace!("list_peers response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -95,7 +95,7 @@ async fn list_funds( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListfundsRequest = (&req).into(); + let req: requests::ListfundsRequest = req.into(); debug!("Client asked for list_funds"); trace!("list_funds request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -109,7 +109,7 @@ async fn list_funds( match result { Response::ListFunds(r) => { trace!("list_funds response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -127,7 +127,7 @@ async fn send_pay( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::SendpayRequest = (&req).into(); + let req: requests::SendpayRequest = req.into(); debug!("Client asked for send_pay"); trace!("send_pay request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -141,7 +141,7 @@ async fn send_pay( match result { Response::SendPay(r) => { trace!("send_pay response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -159,7 +159,7 @@ async fn list_channels( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListchannelsRequest = (&req).into(); + let req: requests::ListchannelsRequest = req.into(); debug!("Client asked for list_channels"); trace!("list_channels request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -173,7 +173,7 @@ async fn list_channels( match result { Response::ListChannels(r) => { trace!("list_channels response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -191,7 +191,7 @@ async fn add_gossip( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::AddgossipRequest = (&req).into(); + let req: requests::AddgossipRequest = req.into(); debug!("Client asked for add_gossip"); trace!("add_gossip request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -205,7 +205,7 @@ async fn add_gossip( match result { Response::AddGossip(r) => { trace!("add_gossip response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -223,7 +223,7 @@ async fn auto_clean_invoice( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::AutocleaninvoiceRequest = (&req).into(); + let req: requests::AutocleaninvoiceRequest = req.into(); debug!("Client asked for auto_clean_invoice"); trace!("auto_clean_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -237,7 +237,7 @@ async fn auto_clean_invoice( match result { Response::AutoCleanInvoice(r) => { trace!("auto_clean_invoice response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -255,7 +255,7 @@ async fn check_message( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::CheckmessageRequest = (&req).into(); + let req: requests::CheckmessageRequest = req.into(); debug!("Client asked for check_message"); trace!("check_message request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -269,7 +269,7 @@ async fn check_message( match result { Response::CheckMessage(r) => { trace!("check_message response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -287,7 +287,7 @@ async fn close( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::CloseRequest = (&req).into(); + let req: requests::CloseRequest = req.into(); debug!("Client asked for close"); trace!("close request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -301,7 +301,7 @@ async fn close( match result { Response::Close(r) => { trace!("close response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -319,7 +319,7 @@ async fn connect_peer( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ConnectRequest = (&req).into(); + let req: requests::ConnectRequest = req.into(); debug!("Client asked for connect_peer"); trace!("connect_peer request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -333,7 +333,7 @@ async fn connect_peer( match result { Response::Connect(r) => { trace!("connect_peer response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -351,7 +351,7 @@ async fn create_invoice( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::CreateinvoiceRequest = (&req).into(); + let req: requests::CreateinvoiceRequest = req.into(); debug!("Client asked for create_invoice"); trace!("create_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -365,7 +365,7 @@ async fn create_invoice( match result { Response::CreateInvoice(r) => { trace!("create_invoice response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -383,7 +383,7 @@ async fn datastore( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::DatastoreRequest = (&req).into(); + let req: requests::DatastoreRequest = req.into(); debug!("Client asked for datastore"); trace!("datastore request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -397,7 +397,7 @@ async fn datastore( match result { Response::Datastore(r) => { trace!("datastore response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -415,7 +415,7 @@ async fn create_onion( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::CreateonionRequest = (&req).into(); + let req: requests::CreateonionRequest = req.into(); debug!("Client asked for create_onion"); trace!("create_onion request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -429,7 +429,7 @@ async fn create_onion( match result { Response::CreateOnion(r) => { trace!("create_onion response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -447,7 +447,7 @@ async fn del_datastore( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::DeldatastoreRequest = (&req).into(); + let req: requests::DeldatastoreRequest = req.into(); debug!("Client asked for del_datastore"); trace!("del_datastore request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -461,7 +461,7 @@ async fn del_datastore( match result { Response::DelDatastore(r) => { trace!("del_datastore response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -479,7 +479,7 @@ async fn del_expired_invoice( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::DelexpiredinvoiceRequest = (&req).into(); + let req: requests::DelexpiredinvoiceRequest = req.into(); debug!("Client asked for del_expired_invoice"); trace!("del_expired_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -493,7 +493,7 @@ async fn del_expired_invoice( match result { Response::DelExpiredInvoice(r) => { trace!("del_expired_invoice response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -511,7 +511,7 @@ async fn del_invoice( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::DelinvoiceRequest = (&req).into(); + let req: requests::DelinvoiceRequest = req.into(); debug!("Client asked for del_invoice"); trace!("del_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -525,7 +525,7 @@ async fn del_invoice( match result { Response::DelInvoice(r) => { trace!("del_invoice response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -543,7 +543,7 @@ async fn invoice( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::InvoiceRequest = (&req).into(); + let req: requests::InvoiceRequest = req.into(); debug!("Client asked for invoice"); trace!("invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -557,7 +557,7 @@ async fn invoice( match result { Response::Invoice(r) => { trace!("invoice response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -575,7 +575,7 @@ async fn list_datastore( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListdatastoreRequest = (&req).into(); + let req: requests::ListdatastoreRequest = req.into(); debug!("Client asked for list_datastore"); trace!("list_datastore request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -589,7 +589,7 @@ async fn list_datastore( match result { Response::ListDatastore(r) => { trace!("list_datastore response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -607,7 +607,7 @@ async fn list_invoices( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListinvoicesRequest = (&req).into(); + let req: requests::ListinvoicesRequest = req.into(); debug!("Client asked for list_invoices"); trace!("list_invoices request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -621,7 +621,7 @@ async fn list_invoices( match result { Response::ListInvoices(r) => { trace!("list_invoices response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -639,7 +639,7 @@ async fn send_onion( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::SendonionRequest = (&req).into(); + let req: requests::SendonionRequest = req.into(); debug!("Client asked for send_onion"); trace!("send_onion request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -653,7 +653,7 @@ async fn send_onion( match result { Response::SendOnion(r) => { trace!("send_onion response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -671,7 +671,7 @@ async fn list_send_pays( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListsendpaysRequest = (&req).into(); + let req: requests::ListsendpaysRequest = req.into(); debug!("Client asked for list_send_pays"); trace!("list_send_pays request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -685,7 +685,7 @@ async fn list_send_pays( match result { Response::ListSendPays(r) => { trace!("list_send_pays response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -703,7 +703,7 @@ async fn list_transactions( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListtransactionsRequest = (&req).into(); + let req: requests::ListtransactionsRequest = req.into(); debug!("Client asked for list_transactions"); trace!("list_transactions request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -717,7 +717,7 @@ async fn list_transactions( match result { Response::ListTransactions(r) => { trace!("list_transactions response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -735,7 +735,7 @@ async fn pay( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::PayRequest = (&req).into(); + let req: requests::PayRequest = req.into(); debug!("Client asked for pay"); trace!("pay request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -749,7 +749,7 @@ async fn pay( match result { Response::Pay(r) => { trace!("pay response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -767,7 +767,7 @@ async fn list_nodes( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListnodesRequest = (&req).into(); + let req: requests::ListnodesRequest = req.into(); debug!("Client asked for list_nodes"); trace!("list_nodes request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -781,7 +781,7 @@ async fn list_nodes( match result { Response::ListNodes(r) => { trace!("list_nodes response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -799,7 +799,7 @@ async fn wait_any_invoice( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::WaitanyinvoiceRequest = (&req).into(); + let req: requests::WaitanyinvoiceRequest = req.into(); debug!("Client asked for wait_any_invoice"); trace!("wait_any_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -813,7 +813,7 @@ async fn wait_any_invoice( match result { Response::WaitAnyInvoice(r) => { trace!("wait_any_invoice response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -831,7 +831,7 @@ async fn wait_invoice( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::WaitinvoiceRequest = (&req).into(); + let req: requests::WaitinvoiceRequest = req.into(); debug!("Client asked for wait_invoice"); trace!("wait_invoice request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -845,7 +845,7 @@ async fn wait_invoice( match result { Response::WaitInvoice(r) => { trace!("wait_invoice response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -863,7 +863,7 @@ async fn wait_send_pay( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::WaitsendpayRequest = (&req).into(); + let req: requests::WaitsendpayRequest = req.into(); debug!("Client asked for wait_send_pay"); trace!("wait_send_pay request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -877,7 +877,7 @@ async fn wait_send_pay( match result { Response::WaitSendPay(r) => { trace!("wait_send_pay response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -895,7 +895,7 @@ async fn new_addr( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::NewaddrRequest = (&req).into(); + let req: requests::NewaddrRequest = req.into(); debug!("Client asked for new_addr"); trace!("new_addr request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -909,7 +909,7 @@ async fn new_addr( match result { Response::NewAddr(r) => { trace!("new_addr response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -927,7 +927,7 @@ async fn withdraw( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::WithdrawRequest = (&req).into(); + let req: requests::WithdrawRequest = req.into(); debug!("Client asked for withdraw"); trace!("withdraw request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -941,7 +941,7 @@ async fn withdraw( match result { Response::Withdraw(r) => { trace!("withdraw response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -959,7 +959,7 @@ async fn key_send( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::KeysendRequest = (&req).into(); + let req: requests::KeysendRequest = req.into(); debug!("Client asked for key_send"); trace!("key_send request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -973,7 +973,7 @@ async fn key_send( match result { Response::KeySend(r) => { trace!("key_send response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -991,7 +991,7 @@ async fn fund_psbt( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::FundpsbtRequest = (&req).into(); + let req: requests::FundpsbtRequest = req.into(); debug!("Client asked for fund_psbt"); trace!("fund_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1005,7 +1005,7 @@ async fn fund_psbt( match result { Response::FundPsbt(r) => { trace!("fund_psbt response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1023,7 +1023,7 @@ async fn send_psbt( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::SendpsbtRequest = (&req).into(); + let req: requests::SendpsbtRequest = req.into(); debug!("Client asked for send_psbt"); trace!("send_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1037,7 +1037,7 @@ async fn send_psbt( match result { Response::SendPsbt(r) => { trace!("send_psbt response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1055,7 +1055,7 @@ async fn sign_psbt( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::SignpsbtRequest = (&req).into(); + let req: requests::SignpsbtRequest = req.into(); debug!("Client asked for sign_psbt"); trace!("sign_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1069,7 +1069,7 @@ async fn sign_psbt( match result { Response::SignPsbt(r) => { trace!("sign_psbt response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1087,7 +1087,7 @@ async fn utxo_psbt( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::UtxopsbtRequest = (&req).into(); + let req: requests::UtxopsbtRequest = req.into(); debug!("Client asked for utxo_psbt"); trace!("utxo_psbt request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1101,7 +1101,7 @@ async fn utxo_psbt( match result { Response::UtxoPsbt(r) => { trace!("utxo_psbt response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1119,7 +1119,7 @@ async fn tx_discard( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::TxdiscardRequest = (&req).into(); + let req: requests::TxdiscardRequest = req.into(); debug!("Client asked for tx_discard"); trace!("tx_discard request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1133,7 +1133,7 @@ async fn tx_discard( match result { Response::TxDiscard(r) => { trace!("tx_discard response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1151,7 +1151,7 @@ async fn tx_prepare( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::TxprepareRequest = (&req).into(); + let req: requests::TxprepareRequest = req.into(); debug!("Client asked for tx_prepare"); trace!("tx_prepare request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1165,7 +1165,7 @@ async fn tx_prepare( match result { Response::TxPrepare(r) => { trace!("tx_prepare response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1183,7 +1183,7 @@ async fn tx_send( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::TxsendRequest = (&req).into(); + let req: requests::TxsendRequest = req.into(); debug!("Client asked for tx_send"); trace!("tx_send request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1197,7 +1197,7 @@ async fn tx_send( match result { Response::TxSend(r) => { trace!("tx_send response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1215,7 +1215,7 @@ async fn disconnect( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::DisconnectRequest = (&req).into(); + let req: requests::DisconnectRequest = req.into(); debug!("Client asked for disconnect"); trace!("disconnect request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1229,7 +1229,7 @@ async fn disconnect( match result { Response::Disconnect(r) => { trace!("disconnect response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1247,7 +1247,7 @@ async fn feerates( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::FeeratesRequest = (&req).into(); + let req: requests::FeeratesRequest = req.into(); debug!("Client asked for feerates"); trace!("feerates request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1261,7 +1261,7 @@ async fn feerates( match result { Response::Feerates(r) => { trace!("feerates response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1279,7 +1279,7 @@ async fn fund_channel( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::FundchannelRequest = (&req).into(); + let req: requests::FundchannelRequest = req.into(); debug!("Client asked for fund_channel"); trace!("fund_channel request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1293,7 +1293,7 @@ async fn fund_channel( match result { Response::FundChannel(r) => { trace!("fund_channel response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1311,7 +1311,7 @@ async fn get_route( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::GetrouteRequest = (&req).into(); + let req: requests::GetrouteRequest = req.into(); debug!("Client asked for get_route"); trace!("get_route request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1325,7 +1325,7 @@ async fn get_route( match result { Response::GetRoute(r) => { trace!("get_route response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1343,7 +1343,7 @@ async fn list_forwards( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListforwardsRequest = (&req).into(); + let req: requests::ListforwardsRequest = req.into(); debug!("Client asked for list_forwards"); trace!("list_forwards request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1357,7 +1357,7 @@ async fn list_forwards( match result { Response::ListForwards(r) => { trace!("list_forwards response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1375,7 +1375,7 @@ async fn list_pays( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::ListpaysRequest = (&req).into(); + let req: requests::ListpaysRequest = req.into(); debug!("Client asked for list_pays"); trace!("list_pays request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1389,7 +1389,7 @@ async fn list_pays( match result { Response::ListPays(r) => { trace!("list_pays response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1407,7 +1407,7 @@ async fn ping( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::PingRequest = (&req).into(); + let req: requests::PingRequest = req.into(); debug!("Client asked for ping"); trace!("ping request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1421,7 +1421,7 @@ async fn ping( match result { Response::Ping(r) => { trace!("ping response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1439,7 +1439,7 @@ async fn sign_message( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::SignmessageRequest = (&req).into(); + let req: requests::SignmessageRequest = req.into(); debug!("Client asked for sign_message"); trace!("sign_message request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1453,7 +1453,7 @@ async fn sign_message( match result { Response::SignMessage(r) => { trace!("sign_message response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, @@ -1471,7 +1471,7 @@ async fn stop( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::StopRequest = (&req).into(); + let req: requests::StopRequest = req.into(); debug!("Client asked for stop"); trace!("stop request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1485,7 +1485,7 @@ async fn stop( match result { Response::Stop(r) => { trace!("stop response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, diff --git a/cln-grpc/src/test.rs b/cln-grpc/src/test.rs index 78f49cdc5c5f..1a600dee60e8 100644 --- a/cln-grpc/src/test.rs +++ b/cln-grpc/src/test.rs @@ -215,7 +215,7 @@ fn test_listpeers() { ] }); let u: cln_rpc::model::ListpeersResponse = serde_json::from_value(j).unwrap(); - let _: ListpeersResponse = (&u).into(); + let _: ListpeersResponse = u.into(); } #[test] @@ -237,7 +237,7 @@ fn test_getinfo() { "fees_collected_msat": "0msat", "lightning-dir": "/tmp/ltests-20irp76f/test_pay_variants_1/lightning-1/regtest", "our_features": {"init": "8808226aa2", "node": "80008808226aa2", "channel": "", "invoice": "024200"}}); let u: cln_rpc::model::GetinfoResponse = serde_json::from_value(j).unwrap(); - let _g: GetinfoResponse = (&u).into(); + let _g: GetinfoResponse = u.into(); } #[test] @@ -280,7 +280,7 @@ fn test_keysend() { }), }; - let u: cln_rpc::model::KeysendRequest = (&g).into(); + let u: cln_rpc::model::KeysendRequest = g.into(); let _ser = serde_json::to_string(&u); let j = r#"{ @@ -296,6 +296,6 @@ fn test_keysend() { "status": "complete" }"#; let u: cln_rpc::model::KeysendResponse = serde_json::from_str(j).unwrap(); - let g: KeysendResponse = (&u).into(); + let g: KeysendResponse = u.into(); println!("{:?}", g); } diff --git a/contrib/msggen/msggen/gen/grpc.py b/contrib/msggen/msggen/gen/grpc.py index 9f5c963ba80f..074b435cebac 100644 --- a/contrib/msggen/msggen/gen/grpc.py +++ b/contrib/msggen/msggen/gen/grpc.py @@ -260,8 +260,8 @@ def generate_composite(self, prefix, field: CompositeField): # And now we can convert the current field: self.write(f"""\ #[allow(unused_variables)] - impl From<&{prefix}::{field.typename}> for pb::{field.typename} {{ - fn from(c: &{prefix}::{field.typename}) -> Self {{ + impl From<{prefix}::{field.typename}> for pb::{field.typename} {{ + fn from(c: {prefix}::{field.typename}) -> Self {{ Self {{ """) @@ -277,13 +277,13 @@ def generate_composite(self, prefix, field: CompositeField): # array. The current item is called `i` mapping = { 'hex': f'hex::decode(i).unwrap()', - 'secret': f'i.clone().to_vec()', + 'secret': f'i.to_vec()', }.get(typ, f'i.into()') if f.required: - self.write(f"{name}: c.{name}.iter().map(|i| {mapping}).collect(), // Rule #3 for type {typ} \n", numindent=3) + self.write(f"{name}: c.{name}.into_iter().map(|i| {mapping}).collect(), // Rule #3 for type {typ} \n", numindent=3) else: - self.write(f"{name}: c.{name}.as_ref().map(|arr| arr.iter().map(|i| {mapping}).collect()).unwrap_or(vec![]), // Rule #3 \n", numindent=3) + self.write(f"{name}: c.{name}.map(|arr| arr.into_iter().map(|i| {mapping}).collect()).unwrap_or(vec![]), // Rule #3 \n", numindent=3) elif isinstance(f, EnumField): if f.required: self.write(f"{name}: c.{name} as i32,\n", numindent=3) @@ -303,20 +303,20 @@ def generate_composite(self, prefix, field: CompositeField): 'msat': f'Some(c.{name}.into())', 'msat?': f'c.{name}.map(|f| f.into())', 'pubkey': f'c.{name}.to_vec()', - 'pubkey?': f'c.{name}.as_ref().map(|v| v.to_vec())', + 'pubkey?': f'c.{name}.map(|v| v.to_vec())', 'hex': f'hex::decode(&c.{name}).unwrap()', - 'hex?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', + 'hex?': f'c.{name}.map(|v| hex::decode(v).unwrap())', 'txid': f'hex::decode(&c.{name}).unwrap()', - 'txid?': f'c.{name}.as_ref().map(|v| hex::decode(&v).unwrap())', + 'txid?': f'c.{name}.map(|v| hex::decode(v).unwrap())', 'short_channel_id': f'c.{name}.to_string()', - 'short_channel_id?': f'c.{name}.as_ref().map(|v| v.to_string())', - 'hash': f'c.{name}.clone().to_vec()', - 'hash?': f'c.{name}.clone().map(|v| v.to_vec())', - 'secret': f'c.{name}.clone().to_vec()', - 'secret?': f'c.{name}.clone().map(|v| v.to_vec())', + 'short_channel_id?': f'c.{name}.map(|v| v.to_string())', + 'hash': f'c.{name}.to_vec()', + 'hash?': f'c.{name}.map(|v| v.to_vec())', + 'secret': f'c.{name}.to_vec()', + 'secret?': f'c.{name}.map(|v| v.to_vec())', }.get( typ, - f'c.{name}.clone()' # default to just assignment + f'c.{name}' # default to just assignment ) self.write(f"{name}: {rhs}, // Rule #2 for type {typ}\n", numindent=3) @@ -381,8 +381,8 @@ def generate_composite(self, prefix, field: CompositeField) -> None: # And now we can convert the current field: self.write(f"""\ #[allow(unused_variables)] - impl From<&pb::{field.typename}> for {prefix}::{field.typename} {{ - fn from(c: &pb::{field.typename}) -> Self {{ + impl From for {prefix}::{field.typename} {{ + fn from(c: pb::{field.typename}) -> Self {{ Self {{ """) @@ -392,13 +392,13 @@ def generate_composite(self, prefix, field: CompositeField) -> None: typ = f.itemtype.typename mapping = { 'hex': f'hex::encode(s)', - 'u32': f's.clone()', - 'secret': f's.clone().try_into().unwrap()' + 'u32': f's', + 'secret': f's.try_into().unwrap()' }.get(typ, f's.into()') if f.required: - self.write(f"{name}: c.{name}.iter().map(|s| {mapping}).collect(), // Rule #4\n", numindent=3) + self.write(f"{name}: c.{name}.into_iter().map(|s| {mapping}).collect(), // Rule #4\n", numindent=3) else: - self.write(f"{name}: Some(c.{name}.iter().map(|s| {mapping}).collect()), // Rule #4\n", numindent=3) + self.write(f"{name}: Some(c.{name}.into_iter().map(|s| {mapping}).collect()), // Rule #4\n", numindent=3) elif isinstance(f, EnumField): if f.required: @@ -416,30 +416,30 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'u16': f'c.{name} as u16', 'u16?': f'c.{name}.map(|v| v as u16)', 'hex': f'hex::encode(&c.{name})', - 'hex?': f'c.{name}.clone().map(|v| hex::encode(v))', - 'txid?': f'c.{name}.clone().map(|v| hex::encode(v))', + 'hex?': f'c.{name}.map(|v| hex::encode(v))', + 'txid?': f'c.{name}.map(|v| hex::encode(v))', 'pubkey': f'cln_rpc::primitives::Pubkey::from_slice(&c.{name}).unwrap()', - 'pubkey?': f'c.{name}.as_ref().map(|v| cln_rpc::primitives::Pubkey::from_slice(v).unwrap())', - 'msat': f'c.{name}.as_ref().unwrap().into()', - 'msat?': f'c.{name}.as_ref().map(|a| a.into())', - 'msat_or_all': f'c.{name}.as_ref().unwrap().into()', - 'msat_or_all?': f'c.{name}.as_ref().map(|a| a.into())', - 'msat_or_any': f'c.{name}.as_ref().unwrap().into()', - 'msat_or_any?': f'c.{name}.as_ref().map(|a| a.into())', - 'feerate': f'c.{name}.as_ref().unwrap().into()', - 'feerate?': f'c.{name}.as_ref().map(|a| a.into())', - 'outpoint?': f'c.{name}.as_ref().map(|a| a.into())', - 'RoutehintList?': f'c.{name}.clone().map(|rl| rl.into())', + 'pubkey?': f'c.{name}.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap())', + 'msat': f'c.{name}.unwrap().into()', + 'msat?': f'c.{name}.map(|a| a.into())', + 'msat_or_all': f'c.{name}.unwrap().into()', + 'msat_or_all?': f'c.{name}.map(|a| a.into())', + 'msat_or_any': f'c.{name}.unwrap().into()', + 'msat_or_any?': f'c.{name}.map(|a| a.into())', + 'feerate': f'c.{name}.unwrap().into()', + 'feerate?': f'c.{name}.map(|a| a.into())', + 'outpoint?': f'c.{name}.map(|a| a.into())', + 'RoutehintList?': f'c.{name}.map(|rl| rl.into())', 'short_channel_id': f'cln_rpc::primitives::ShortChannelId::from_str(&c.{name}).unwrap()', - 'short_channel_id?': f'c.{name}.as_ref().map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap())', - 'secret': f'c.{name}.clone().try_into().unwrap()', - 'secret?': f'c.{name}.clone().map(|v| v.try_into().unwrap())', - 'hash': f'c.{name}.clone().try_into().unwrap()', - 'hash?': f'c.{name}.clone().map(|v| v.try_into().unwrap())', + 'short_channel_id?': f'c.{name}.map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap())', + 'secret': f'c.{name}.try_into().unwrap()', + 'secret?': f'c.{name}.map(|v| v.try_into().unwrap())', + 'hash': f'c.{name}.try_into().unwrap()', + 'hash?': f'c.{name}.map(|v| v.try_into().unwrap())', 'txid': f'hex::encode(&c.{name})', }.get( typ, - f'c.{name}.clone()' # default to just assignment + f'c.{name}' # default to just assignment ) self.write(f"{name}: {rhs}, // Rule #1 for type {typ}\n", numindent=3) @@ -494,7 +494,7 @@ def generate(self, service: Service) -> None: request: tonic::Request, ) -> Result, tonic::Status> {{ let req = request.into_inner(); - let req: requests::{method.request.typename} = (&req).into(); + let req: requests::{method.request.typename} = req.into(); debug!("Client asked for {name}"); trace!("{name} request: {{:?}}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -508,7 +508,7 @@ def generate(self, service: Service) -> None: match result {{ Response::{method.name}(r) => {{ trace!("{name} response: {{:?}}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }}, r => Err(Status::new( Code::Internal, From 437ae11610fda83f26d56a87669ea650ab185c99 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Thu, 22 Sep 2022 19:31:38 +0200 Subject: [PATCH 1488/1530] pytest: Configure the plugin logging to debug --- contrib/pyln-testing/pyln/testing/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index f9e8c639f841..927f6e8ceba8 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -579,6 +579,7 @@ def __init__( self.disconnect_file = None self.rpcproxy = bitcoindproxy + self.env['CLN_PLUGIN_LOG'] = "gl_plugin=trace,gl_rpc=trace,gl_grpc=trace,debug" self.opts = LIGHTNINGD_CONFIG.copy() opts = { From b698a5a5ef4acb8d90aec0f6dccc52fb6c37afb2 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Fri, 1 Apr 2022 12:00:13 +1030 Subject: [PATCH 1489/1530] channeld: send error, not warning, if peer has old commitment number. This is the minimal change to meet the desired outcome of https://github.com/lightning/bolts/issues/934 which wants to give obsolete-db nodes a chance to fix things up, before we close the channel. We need to dance around a bit here, since we *will* close the channel if we receive an ERROR, so we suppress that. Signed-off-by: Rusty Russell --- channeld/channeld.c | 14 ++++---- tests/test_connection.py | 70 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/channeld/channeld.c b/channeld/channeld.c index 51a39c6fe99f..d0426f15aaab 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -3002,12 +3002,14 @@ static void peer_reconnect(struct peer *peer, } retransmit_revoke_and_ack = true; } else if (next_revocation_number < peer->next_index[LOCAL] - 1) { - peer_failed_err(peer->pps, - &peer->channel_id, - "bad reestablish revocation_number: %"PRIu64 - " vs %"PRIu64, - next_revocation_number, - peer->next_index[LOCAL]); + /* Send a warning here! Because this is what it looks like if peer is + * in the past, and they might still recover. */ + peer_failed_warn(peer->pps, + &peer->channel_id, + "bad reestablish revocation_number: %"PRIu64 + " vs %"PRIu64, + next_revocation_number, + peer->next_index[LOCAL]); } else if (next_revocation_number > peer->next_index[LOCAL] - 1) { if (!check_extra_fields) /* They don't support option_data_loss_protect or diff --git a/tests/test_connection.py b/tests/test_connection.py index 38884edd5082..a0860386eaaa 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -2926,11 +2926,11 @@ def test_opener_simple_reconnect(node_factory, bitcoind): @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "sqlite3-specific DB rollback") -@pytest.mark.developer("needs LIGHTNINGD_DEV_LOG_IO") @pytest.mark.openchannel('v1') @pytest.mark.openchannel('v2') def test_dataloss_protection(node_factory, bitcoind): l1 = node_factory.get_node(may_reconnect=True, options={'log-level': 'io'}, + allow_warning=True, feerates=(7500, 7500, 7500, 7500)) l2 = node_factory.get_node(may_reconnect=True, options={'log-level': 'io'}, feerates=(7500, 7500, 7500, 7500), allow_broken_log=True) @@ -2998,14 +2998,16 @@ def test_dataloss_protection(node_factory, bitcoind): # l2 should freak out! l2.daemon.wait_for_log("Peer permanent failure in CHANNELD_NORMAL: Awaiting unilateral close") - # l1 should drop to chain. - l1.wait_for_channel_onchain(l2.info['id']) - # l2 must NOT drop to chain. l2.daemon.wait_for_log("Cannot broadcast our commitment tx: they have a future one") assert not l2.daemon.is_in_log('sendrawtx exit 0', start=l2.daemon.logsearch_start) + # l1 should drop to chain, but doesn't always receive ERROR before it sends warning. + # We have to reconnect once + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.wait_for_channel_onchain(l2.info['id']) + closetxid = only_one(bitcoind.rpc.getrawmempool(False)) # l2 should still recover something! @@ -3024,6 +3026,66 @@ def test_dataloss_protection(node_factory, bitcoind): assert (closetxid, "confirmed") in set([(o['txid'], o['status']) for o in l2.rpc.listfunds()['outputs']]) +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "sqlite3-specific DB rollback") +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +@pytest.mark.developer("needs dev-disconnect, dev-no-reconnect") +def test_dataloss_protection_no_broadcast(node_factory, bitcoind): + # If l2 sends an old version, but *doesn't* send an error, l1 should not broadcast tx. + # (https://github.com/lightning/bolts/issues/934) + l1 = node_factory.get_node(may_reconnect=True, + feerates=(7500, 7500, 7500, 7500), + allow_warning=True, + options={'dev-no-reconnect': None}) + l2 = node_factory.get_node(may_reconnect=True, + feerates=(7500, 7500, 7500, 7500), allow_broken_log=True, + disconnect=['-WIRE_ERROR'], + options={'dev-no-reconnect': None}) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fundchannel(l2, 10**6) + l2.stop() + + # Save copy of the db. + dbpath = os.path.join(l2.daemon.lightning_dir, TEST_NETWORK, "lightningd.sqlite3") + orig_db = open(dbpath, "rb").read() + l2.start() + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # After an htlc, we should get different results (two more commits) + l1.pay(l2, 200000000) + + # Make sure both sides consider it completely settled (has received both + # REVOKE_AND_ACK) + l1.daemon.wait_for_logs(["peer_in WIRE_REVOKE_AND_ACK"] * 2) + l2.daemon.wait_for_logs(["peer_in WIRE_REVOKE_AND_ACK"] * 2) + + # Now, move l2 back in time. + l2.stop() + # Save new db + new_db = open(dbpath, "rb").read() + # Overwrite with OLD db. + open(dbpath, "wb").write(orig_db) + l2.start() + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # l2 should freak out! + l2.daemon.wait_for_logs(["Peer permanent failure in CHANNELD_NORMAL: Awaiting unilateral close"]) + + # l1 should NOT drop to chain, since it didn't receive an error. + time.sleep(5) + assert bitcoind.rpc.getrawmempool(False) == [] + + # fix up l2. + l2.stop() + open(dbpath, "wb").write(new_db) + l2.start() + + # All is forgiven + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.pay(l2, 200000000) + + @pytest.mark.developer("needs dev_disconnect") def test_restart_multi_htlc_rexmit(node_factory, bitcoind, executor): # l1 disables commit timer once we send first htlc, dies on commit From 6e86fa92206eb6e935a8dcba37b9e7849d2cc816 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 25 Sep 2022 22:44:12 +0930 Subject: [PATCH 1490/1530] lightningd: figure out optimal channel *before* forward_htlc hook. Otherwise what the hook sees is actually a lie, and if it sets it we might override it. The side effect is that we add an explicit "forward_to" field, and allow hooks to override it. This lets a *hook* control channel choice explicitly. Changelod-Added: Plugins: `htlc_accepted_hook` return can specify what channel to forward htlc to. Signed-off-by: Rusty Russell --- doc/PLUGINS.md | 5 +- lightningd/channel.h | 1 - lightningd/peer_htlcs.c | 154 +++++++++++++++++++-------- tests/plugins/htlc_accepted-fwdto.py | 31 ++++++ tests/test_opening.py | 12 ++- tests/test_plugin.py | 18 ++++ wallet/test/run-wallet.c | 4 + 7 files changed, 178 insertions(+), 47 deletions(-) create mode 100755 tests/plugins/htlc_accepted-fwdto.py diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index 6152bed954f8..dcefa4edc5a7 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1432,7 +1432,8 @@ The payload of the hook call has the following format: "cltv_expiry": 500028, "cltv_expiry_relative": 10, "payment_hash": "0000000000000000000000000000000000000000000000000000000000000000" - } + }, + "forward_to": "0000000000000000000000000000000000000000000000000000000000000000" } ``` @@ -1469,6 +1470,7 @@ For detailed information about each field please refer to [BOLT 04 of the specif blockheight. - `payment_hash` is the hash whose `payment_preimage` will unlock the funds and allow us to claim the HTLC. + - `forward_to`: if set, the channel_id we intend to forward this to (will not be present if the short_channel_id was invalid or we were the final destination). The hook response must have one of the following formats: @@ -1489,6 +1491,7 @@ the response. Note that this is always a TLV-style payload, so unlike hex digits long). This will be re-parsed; it's useful for removing onion fields which a plugin doesn't want lightningd to consider. +It can also specify `forward_to` in the response, replacing the destination. This usually only makes sense if it wants to choose an alternate channel to the same next peer, but is useful if the `payload` is also replaced. ```json { diff --git a/lightningd/channel.h b/lightningd/channel.h index 9f8ec75e52e8..9a31157c6903 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -9,7 +9,6 @@ #include #include -struct channel_id; struct uncommitted_channel; struct wally_psbt; diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index e1764bb27c62..043510208a1a 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -648,11 +648,51 @@ const u8 *send_htlc_out(const tal_t *ctx, return NULL; } +/* What's the best channel to this peer? + * If @hint is set, channel must match that one. */ +static struct channel *best_channel(struct lightningd *ld, + const struct peer *next_peer, + struct amount_msat amt_to_forward, + struct channel *hint) +{ + struct amount_msat best_spendable = AMOUNT_MSAT(0); + struct channel *channel, *best = hint; + + /* Seek channel with largest spendable! */ + list_for_each(&next_peer->channels, channel, list) { + struct amount_msat spendable; + if (!channel_can_add_htlc(channel)) + continue; + spendable = channel_amount_spendable(channel); + if (!amount_msat_greater(spendable, best_spendable)) + continue; + + /* Don't override if fees differ... */ + if (hint) { + if (hint->feerate_base != channel->feerate_base + || hint->feerate_ppm != channel->feerate_ppm) + continue; + } + + /* Or if this would be below min for channel! */ + if (amount_msat_less(amt_to_forward, + channel->channel_info.their_config.htlc_minimum)) + continue; + + best = channel; + best_spendable = spendable; + } + return best; +} + +/* forward_to is where we're actually sending it (or NULL), and + * forward_scid is where they asked to send it (or NULL). */ static void forward_htlc(struct htlc_in *hin, u32 cltv_expiry, struct amount_msat amt_to_forward, u32 outgoing_cltv_value, - const struct short_channel_id *scid, + const struct short_channel_id *forward_scid, + const struct channel_id *forward_to, const u8 next_onion[TOTAL_PACKET_SIZE(ROUTING_INFO_SIZE)], const struct pubkey *next_blinding) { @@ -660,52 +700,21 @@ static void forward_htlc(struct htlc_in *hin, struct lightningd *ld = hin->key.channel->peer->ld; struct channel *next; struct htlc_out *hout = NULL; - struct short_channel_id *altscid; - - /* This is a shortcut for specifying next peer; doesn't mean - * the actual channel! */ - next = any_channel_by_scid(ld, scid, false); - if (next) { - struct peer *peer = next->peer; - struct channel *channel; - struct amount_msat best_spendable = channel_amount_spendable(next); - - /* Seek channel with largest spendable! */ - list_for_each(&peer->channels, channel, list) { - struct amount_msat spendable; - if (!channel_can_add_htlc(channel)) - continue; - spendable = channel_amount_spendable(channel); - if (!amount_msat_greater(spendable, best_spendable)) - continue; - - /* Don't override if fees differ... */ - if (channel->feerate_base != next->feerate_base - || channel->feerate_ppm != next->feerate_ppm) - continue; - /* Or if this would be below min for channel! */ - if (amount_msat_less(amt_to_forward, - channel->channel_info.their_config.htlc_minimum)) - continue; - altscid = channel->scid != NULL ? channel->scid - : channel->alias[LOCAL]; - - /* OK, it's better! */ - log_debug(next->log, "Chose a better channel: %s", - type_to_string(tmpctx, - struct short_channel_id, - altscid)); - next = channel; - } - } + if (forward_to) { + next = channel_by_cid(ld, forward_to); + /* Update this to where we're actually trying to send. */ + if (next) + forward_scid = channel_scid_or_local_alias(next); + }else + next = NULL; /* Unknown peer, or peer not ready. */ if (!next || !channel_active(next)) { local_fail_in_htlc(hin, take(towire_unknown_next_peer(NULL))); wallet_forwarded_payment_add(hin->key.channel->peer->ld->wallet, hin, get_onion_style(hin), - scid, NULL, + forward_scid, NULL, FORWARD_LOCAL_FAILED, WIRE_UNKNOWN_NEXT_PEER); return; @@ -803,7 +812,7 @@ static void forward_htlc(struct htlc_in *hin, fail: local_fail_in_htlc(hin, failmsg); wallet_forwarded_payment_add(ld->wallet, - hin, get_onion_style(hin), scid, hout, + hin, get_onion_style(hin), forward_scid, hout, FORWARD_LOCAL_FAILED, fromwire_peektype(failmsg)); } @@ -819,6 +828,8 @@ struct htlc_accepted_hook_payload { struct channel *channel; struct lightningd *ld; struct pubkey *next_blinding; + /* NULL if we couldn't find it */ + struct channel_id *fwd_channel_id; u8 *next_onion; u64 failtlvtype; size_t failtlvpos; @@ -907,7 +918,7 @@ static bool htlc_accepted_hook_deserialize(struct htlc_accepted_hook_payload *re struct htlc_in *hin = request->hin; struct lightningd *ld = request->ld; struct preimage payment_preimage; - const jsmntok_t *resulttok, *paykeytok, *payloadtok; + const jsmntok_t *resulttok, *paykeytok, *payloadtok, *fwdtok; u8 *payload, *failonion; if (!toks || !buffer) @@ -939,10 +950,22 @@ static bool htlc_accepted_hook_deserialize(struct htlc_accepted_hook_payload *re ld->accept_extra_tlv_types, &request->failtlvtype, &request->failtlvpos); - } else payload = NULL; + fwdtok = json_get_member(buffer, toks, "forward_to"); + if (fwdtok) { + tal_free(request->fwd_channel_id); + request->fwd_channel_id = tal(request, struct channel_id); + if (!json_to_channel_id(buffer, fwdtok, + request->fwd_channel_id)) { + fatal("Bad forward_to for htlc_accepted" + " hook: %.*s", + fwdtok->end - fwdtok->start, + buffer + fwdtok->start); + } + } + if (json_tok_streq(buffer, resulttok, "continue")) { return true; } @@ -1074,6 +1097,9 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_add_secret(s, "shared_secret", hin->shared_secret); json_object_end(s); + if (p->fwd_channel_id) + json_add_channel_id(s, "forward_to", p->fwd_channel_id); + json_object_start(s, "htlc"); json_add_short_channel_id( s, "short_channel_id", @@ -1117,6 +1143,7 @@ htlc_accepted_hook_final(struct htlc_accepted_hook_payload *request STEALS) request->payload->amt_to_forward, request->payload->outgoing_cltv, request->payload->forward_channel, + request->fwd_channel_id, serialize_onionpacket(tmpctx, rs->next), request->next_blinding); } else @@ -1166,6 +1193,37 @@ REGISTER_PLUGIN_HOOK(htlc_accepted, struct htlc_accepted_hook_payload *); +/* Figures out how to fwd, allocating return off hp */ +static struct channel_id *calc_forwarding_channel(struct lightningd *ld, + struct htlc_accepted_hook_payload *hp, + const struct route_step *rs) +{ + const struct onion_payload *p = hp->payload; + struct channel *c, *best; + + if (rs->nextcase != ONION_FORWARD) + return NULL; + + if (!p || !p->forward_channel) + return NULL; + + c = any_channel_by_scid(ld, p->forward_channel, false); + if (!c) + return NULL; + + best = best_channel(ld, c->peer, p->amt_to_forward, c); + if (best != c) { + log_debug(hp->channel->log, + "Chose a better channel than %s: %s", + type_to_string(tmpctx, struct short_channel_id, + p->forward_channel), + type_to_string(tmpctx, struct short_channel_id, + channel_scid_or_local_alias(best))); + } + + return tal_dup(hp, struct channel_id, &best->cid); +} + /** * Everyone is committed to this htlc of theirs * @@ -1300,6 +1358,16 @@ static bool peer_accepted_htlc(const tal_t *ctx, #endif hook_payload->next_blinding = NULL; + /* The scid is merely used to indicate the next peer, it is not + * a requirement (nor, ideally, observable anyway). We can change + * to a more-preferred one now, that way the hook sees the value + * we're actually going to (try to) use */ + + /* We don't store actual channel as it could vanish while + * we're in hook */ + hook_payload->fwd_channel_id + = calc_forwarding_channel(ld, hook_payload, rs); + plugin_hook_call_htlc_accepted(ld, NULL, hook_payload); /* Falling through here is ok, after all the HTLC locked */ diff --git a/tests/plugins/htlc_accepted-fwdto.py b/tests/plugins/htlc_accepted-fwdto.py new file mode 100755 index 000000000000..5e53d78d14e6 --- /dev/null +++ b/tests/plugins/htlc_accepted-fwdto.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +"""A plugin that tells us to forward HTLCs to a specific channel. + +""" +from pyln.client import Plugin + + +plugin = Plugin() + + +@plugin.hook("htlc_accepted") +def on_htlc_accepted(htlc, onion, plugin, **kwargs): + if plugin.fwdto is None: + return {"result": "continue"} + + return {"result": "continue", "forward_to": plugin.fwdto} + + +@plugin.method("setfwdto") +def setfailonion(plugin, fwdto): + """Sets the channel_id to forward to when receiving an incoming HTLC. + """ + plugin.fwdto = fwdto + + +@plugin.init() +def on_init(**kwargs): + plugin.fwdto = None + + +plugin.run() diff --git a/tests/test_opening.py b/tests/test_opening.py index c182fefcf547..574f82c7f5a4 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1722,14 +1722,22 @@ def test_zeroconf_multichan_forward(node_factory): # Now create a channel that is twice as large as the real channel, # and don't announce it. l2.fundwallet(10**7) - l2.rpc.fundchannel(l3.info['id'], 2 * 10**6, mindepth=0) + zeroconf_cid = l2.rpc.fundchannel(l3.info['id'], 2 * 10**6, mindepth=0)['channel_id'] l2.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_READY') l3.daemon.wait_for_log(r'peer_in WIRE_CHANNEL_READY') inv = l3.rpc.invoice(amount_msat=10000, label='lbl1', description='desc')['bolt11'] l1.rpc.pay(inv) - assert l2.daemon.is_in_log(r'Chose a better channel: .*') + + for c in only_one(l2.rpc.listpeers(l3.info['id'])['peers'])['channels']: + if c['channel_id'] == zeroconf_cid: + zeroconf_scid = c['alias']['local'] + else: + normal_scid = c['short_channel_id'] + + assert l2.daemon.is_in_log(r'Chose a better channel than {}: {}' + .format(normal_scid, zeroconf_scid)) def test_zeroreserve(node_factory, bitcoind): diff --git a/tests/test_plugin.py b/tests/test_plugin.py index a4a488608fed..e463144196e4 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -2381,6 +2381,24 @@ def test_htlc_accepted_hook_failonion(node_factory): l1.rpc.pay(inv) +@pytest.mark.developer("Gossip without developer is slow.") +def test_htlc_accepted_hook_fwdto(node_factory): + plugin = os.path.join(os.path.dirname(__file__), 'plugins/htlc_accepted-fwdto.py') + l1, l2, l3 = node_factory.line_graph(3, opts=[{}, {'plugin': plugin}, {}], wait_for_announce=True) + + # Add some balance + l1.rpc.pay(l2.rpc.invoice(10**9 // 2, 'balance', '')['bolt11']) + wait_for(lambda: only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['htlcs'] == []) + + # make it forward back down same channel. + l2.rpc.setfwdto(only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['channel_id']) + inv = l3.rpc.invoice(42, 'fwdto', '')['bolt11'] + with pytest.raises(RpcError, match="WIRE_INVALID_ONION_HMAC"): + l1.rpc.pay(inv) + + assert l2.rpc.listforwards()['forwards'][0]['out_channel'] == only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['short_channel_id'] + + def test_dynamic_args(node_factory): plugin_path = os.path.join(os.getcwd(), 'contrib/plugins/helloworld.py') diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 7157358acf1e..fcd85fd3211a 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -426,6 +426,10 @@ char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const /* Generated stub for json_stream_success */ struct json_stream *json_stream_success(struct command *cmd UNNEEDED) { fprintf(stderr, "json_stream_success called!\n"); abort(); } +/* Generated stub for json_to_channel_id */ +bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct channel_id *cid UNNEEDED) +{ fprintf(stderr, "json_to_channel_id called!\n"); abort(); } /* Generated stub for json_to_node_id */ bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct node_id *id UNNEEDED) From a7ce03bae6cd763e7796f7b961d40a83a7e64a4a Mon Sep 17 00:00:00 2001 From: Melroy van den Berg Date: Mon, 26 Sep 2022 00:03:52 +0200 Subject: [PATCH 1491/1530] The project is called Core Lightning Rename project in the description of the service file. --- contrib/init/lightningd.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/init/lightningd.service b/contrib/init/lightningd.service index 6047078b1590..dc115eef6f9c 100644 --- a/contrib/init/lightningd.service +++ b/contrib/init/lightningd.service @@ -8,7 +8,7 @@ # /etc/lightningd/lightningd.conf [Unit] -Description=C-Lightning daemon +Description=Core Lightning daemon Requires=bitcoind.service After=bitcoind.service Wants=network-online.target From 87bab2b85138bf99a29bb9b881d110e90e3a4e88 Mon Sep 17 00:00:00 2001 From: Melroy van den Berg Date: Mon, 26 Sep 2022 00:58:40 +0200 Subject: [PATCH 1492/1530] Add ConfigurationDirectory Add ConfigurationDirectory for `/etc/lightningd ` as well. --- contrib/init/lightningd.service | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/init/lightningd.service b/contrib/init/lightningd.service index dc115eef6f9c..2341d2119b1b 100644 --- a/contrib/init/lightningd.service +++ b/contrib/init/lightningd.service @@ -19,6 +19,8 @@ ExecStart=/usr/bin/lightningd --daemon --conf /etc/lightningd/lightningd.conf -- # Creates /run/lightningd owned by bitcoin RuntimeDirectory=lightningd +# Creates /etc/lightningd owned by bitcoin +ConfigurationDirectory=lightningd User=bitcoin Group=bitcoin From 7046252f96c0436500a1b45c379a6aab56ee9612 Mon Sep 17 00:00:00 2001 From: elsirion Date: Fri, 13 May 2022 16:42:32 +0000 Subject: [PATCH 1493/1530] Impl `std::error::Error` for `RpcError` to make it anyhow compatible --- cln-rpc/src/primitives.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 460a2ac8ba38..e8a5c504b36b 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -1,3 +1,4 @@ +use std::fmt::{Display, Formatter}; use anyhow::Context; use anyhow::{anyhow, Error, Result}; use serde::{Deserialize, Serialize}; @@ -671,3 +672,15 @@ pub struct RpcError { pub code: Option, pub message: String, } + +impl Display for RpcError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if let Some(code) = self.code { + write!(f, "Error code {}: {}", code, self.message) + } else { + write!(f, "Error: {}", self.message) + } + } +} + +impl std::error::Error for RpcError {} \ No newline at end of file From 10917743fe3fedd9d00b69ef1fa42d47989955ae Mon Sep 17 00:00:00 2001 From: elsirion Date: Fri, 13 May 2022 19:43:36 +0000 Subject: [PATCH 1494/1530] Implement a typed version of `call` to avoid useless matching --- cln-rpc/src/lib.rs | 27 +++++++++++++++++ contrib/msggen/msggen/gen/rust.py | 48 ++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/cln-rpc/src/lib.rs b/cln-rpc/src/lib.rs index cd2758d0a3d5..1e603d1e264f 100644 --- a/cln-rpc/src/lib.rs +++ b/cln-rpc/src/lib.rs @@ -24,6 +24,7 @@ pub use crate::{ notifications::Notification, primitives::RpcError, }; +use crate::model::IntoRequest; /// pub struct ClnRpc { @@ -105,6 +106,13 @@ impl ClnRpc { }) } } + + pub async fn call_typed(&mut self, request: R) -> Result { + Ok(self.call(request.into()) + .await? + .try_into() + .expect("CLN will reply correctly")) + } } /// Used to skip optional arrays when serializing requests. @@ -142,4 +150,23 @@ mod test { read_req ); } + + #[tokio::test] + async fn test_typed_call() { + let req = requests::GetinfoRequest {}; + let (uds1, uds2) = UnixStream::pair().unwrap(); + let mut cln = ClnRpc::from_stream(uds1).unwrap(); + + let mut read = FramedRead::new(uds2, JsonCodec::default()); + tokio::task::spawn(async move { + let _: GetinfoResponse = cln.call_typed(req).await.unwrap(); + }); + + let read_req = dbg!(read.next().await.unwrap().unwrap()); + + assert_eq!( + json!({"id": 1, "method": "getinfo", "params": {}, "jsonrpc": "2.0"}), + read_req + ); + } } diff --git a/contrib/msggen/msggen/gen/rust.py b/contrib/msggen/msggen/gen/rust.py index 7eac8ecf669a..88d0d6bd9371 100644 --- a/contrib/msggen/msggen/gen/rust.py +++ b/contrib/msggen/msggen/gen/rust.py @@ -6,7 +6,7 @@ import re from msggen.model import (ArrayField, CompositeField, EnumField, - PrimitiveField, Service) + PrimitiveField, Service, Method) from msggen.gen.generator import IGenerator logger = logging.getLogger(__name__) @@ -214,6 +214,7 @@ def generate_requests(self, service: Service): use crate::primitives::*; #[allow(unused_imports)] use serde::{{Deserialize, Serialize}}; + use super::{IntoRequest, Request}; """) @@ -221,9 +222,24 @@ def generate_requests(self, service: Service): req = meth.request _, decl = gen_composite(req) self.write(decl, numindent=1) + self.generate_request_trait_impl(meth) self.write("}\n\n") + def generate_request_trait_impl(self, method: Method): + self.write(dedent(f"""\ + impl From<{method.request.typename}> for Request {{ + fn from(r: {method.request.typename}) -> Self {{ + Request::{method.name}(r) + }} + }} + + impl IntoRequest for {method.request.typename} {{ + type Response = super::responses::{method.response.typename}; + }} + + """), numindent=1) + def generate_responses(self, service: Service): self.write(""" pub mod responses { @@ -231,6 +247,7 @@ def generate_responses(self, service: Service): use crate::primitives::*; #[allow(unused_imports)] use serde::{{Deserialize, Serialize}}; + use super::{TryFromResponseError, Response}; """) @@ -238,9 +255,25 @@ def generate_responses(self, service: Service): res = meth.response _, decl = gen_composite(res) self.write(decl, numindent=1) + self.generate_response_trait_impl(meth) self.write("}\n\n") + def generate_response_trait_impl(self, method: Method): + self.write(dedent(f"""\ + impl TryFrom for {method.response.typename} {{ + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result {{ + match response {{ + Response::{method.name}(response) => Ok(response), + _ => Err(TryFromResponseError) + }} + }} + }} + + """), numindent=1) + def generate_enums(self, service: Service): """The Request and Response enums serve as parsing primitives. """ @@ -275,10 +308,23 @@ def generate_enums(self, service: Service): """) + def generate_request_trait(self): + self.write(""" + pub trait IntoRequest: Into { + type Response: TryFrom; + } + + #[derive(Debug)] + pub struct TryFromResponseError; + + """) + def generate(self, service: Service) -> None: self.write(header) self.generate_enums(service) + self.generate_request_trait() + self.generate_requests(service) self.generate_responses(service) From e272c93a88c7302dad026dbe06627a569487a25c Mon Sep 17 00:00:00 2001 From: elsirion Date: Fri, 13 May 2022 21:22:45 +0000 Subject: [PATCH 1495/1530] Use `bitcoin_hashes` for `Sha256` --- Cargo.lock | 11 + cln-grpc/Cargo.toml | 1 + cln-grpc/src/convert.rs | 14 +- cln-grpc/src/pb.rs | 5 +- cln-rpc/Cargo.toml | 1 + cln-rpc/src/model.rs | 976 ++++++++++++++++++++++++++++++ cln-rpc/src/primitives.rs | 52 +- contrib/msggen/msggen/gen/grpc.py | 6 +- 8 files changed, 1012 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22686ed31a04..83b287a1d5a9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,15 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bitcoin_hashes" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006cc91e1a1d99819bc5b8214be3555c1f0611b169f527a1fdc54ed1f2b745b0" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -117,6 +126,7 @@ name = "cln-grpc" version = "0.0.1" dependencies = [ "anyhow", + "bitcoin_hashes", "cln-rpc", "hex", "log", @@ -164,6 +174,7 @@ name = "cln-rpc" version = "0.1.0" dependencies = [ "anyhow", + "bitcoin_hashes", "bytes", "env_logger", "futures-util", diff --git a/cln-grpc/Cargo.toml b/cln-grpc/Cargo.toml index 0d620dc0edae..d76886919940 100644 --- a/cln-grpc/Cargo.toml +++ b/cln-grpc/Cargo.toml @@ -10,6 +10,7 @@ cln-rpc = { path="../cln-rpc" } tonic = { version = "^0.5", features = ["tls", "transport"] } prost = "0.8" hex = "0.4.3" +bitcoin_hashes = { version = "0.10.0", features = [ "serde" ] } [dev-dependencies] serde_json = "1.0.72" diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index a8cfec26ee72..4543f4f1d9db 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -8,6 +8,8 @@ use std::convert::From; use cln_rpc::model::{responses,requests}; use crate::pb; use std::str::FromStr; +use bitcoin_hashes::sha256::Hash as Sha256; +use bitcoin_hashes::Hash; #[allow(unused_variables)] impl From for pb::GetinfoAddress { @@ -1020,7 +1022,7 @@ impl From for requests::SendpayRequest { fn from(c: pb::SendpayRequest) -> Self { Self { route: c.route.into_iter().map(|s| s.into()).collect(), // Rule #4 - payment_hash: c.payment_hash.try_into().unwrap(), // Rule #1 for type hash + payment_hash: Sha256::from_slice(&c.payment_hash).unwrap(), // Rule #1 for type hash label: c.label, // Rule #1 for type string? amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? bolt11: c.bolt11, // Rule #1 for type string? @@ -1218,14 +1220,14 @@ impl From for requests::SendonionRequest { fn from(c: pb::SendonionRequest) -> Self { Self { onion: hex::encode(&c.onion), // Rule #1 for type hex - payment_hash: c.payment_hash.try_into().unwrap(), // Rule #1 for type hash + payment_hash: Sha256::from_slice(&c.payment_hash).unwrap(), // Rule #1 for type hash label: c.label, // Rule #1 for type string? shared_secrets: Some(c.shared_secrets.into_iter().map(|s| s.try_into().unwrap()).collect()), // Rule #4 partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? bolt11: c.bolt11, // Rule #1 for type string? amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? destination: c.destination.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? - localofferid: c.localofferid.map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + localofferid: c.localofferid.map(|v| Sha256::from_slice(&v).unwrap()), // Rule #1 for type hash? groupid: c.groupid, // Rule #1 for type u64? } } @@ -1236,7 +1238,7 @@ impl From for requests::ListsendpaysRequest { fn from(c: pb::ListsendpaysRequest) -> Self { Self { bolt11: c.bolt11, // Rule #1 for type string? - payment_hash: c.payment_hash.map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + payment_hash: c.payment_hash.map(|v| Sha256::from_slice(&v).unwrap()), // Rule #1 for type hash? status: c.status.map(|v| v.try_into().unwrap()), } } @@ -1302,7 +1304,7 @@ impl From for requests::WaitinvoiceRequest { impl From for requests::WaitsendpayRequest { fn from(c: pb::WaitsendpayRequest) -> Self { Self { - payment_hash: c.payment_hash.try_into().unwrap(), // Rule #1 for type hash + payment_hash: Sha256::from_slice(&c.payment_hash).unwrap(), // Rule #1 for type hash timeout: c.timeout, // Rule #1 for type u32? partid: c.partid, // Rule #1 for type u64? groupid: c.groupid, // Rule #1 for type u64? @@ -1502,7 +1504,7 @@ impl From for requests::ListpaysRequest { fn from(c: pb::ListpaysRequest) -> Self { Self { bolt11: c.bolt11, // Rule #1 for type string? - payment_hash: c.payment_hash.map(|v| v.try_into().unwrap()), // Rule #1 for type hash? + payment_hash: c.payment_hash.map(|v| Sha256::from_slice(&v).unwrap()), // Rule #1 for type hash? status: c.status.map(|v| v.try_into().unwrap()), } } diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 00291d19813f..734f91fc72c2 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -1,5 +1,6 @@ tonic::include_proto!("cln"); use std::str::FromStr; +use bitcoin_hashes::Hash; use cln_rpc::primitives::{ Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, @@ -21,7 +22,7 @@ impl From for JAmount { impl From for Outpoint { fn from(a: JOutpoint) -> Self { Outpoint { - txid: a.txid, + txid: a.txid.to_vec(), outnum: a.outnum, } } @@ -30,7 +31,7 @@ impl From for Outpoint { impl From for JOutpoint { fn from(a: Outpoint) -> Self { JOutpoint { - txid: a.txid, + txid: bitcoin_hashes::sha256::Hash::from_slice(&a.txid).unwrap(), outnum: a.outnum, } } diff --git a/cln-rpc/Cargo.toml b/cln-rpc/Cargo.toml index 6ba034df60f1..4346641b64f6 100644 --- a/cln-rpc/Cargo.toml +++ b/cln-rpc/Cargo.toml @@ -9,6 +9,7 @@ path = "examples/getinfo.rs" [dependencies] anyhow = "1.0.51" +bitcoin_hashes = { version = "0.10.0", features = [ "serde" ] } bytes = "1.1.0" log = "0.4.14" serde = { version = "1.0.131", features = ["derive"] } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index dcad0497f0cb..06f004aa704f 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -116,16 +116,35 @@ pub enum Response { Stop(responses::StopResponse), } + +pub trait IntoRequest: Into { + type Response: TryFrom; +} + +#[derive(Debug)] +pub struct TryFromResponseError; + pub mod requests { #[allow(unused_imports)] use crate::primitives::*; #[allow(unused_imports)] use serde::{{Deserialize, Serialize}}; + use super::{IntoRequest, Request}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoRequest { } + impl From for Request { + fn from(r: GetinfoRequest) -> Self { + Request::Getinfo(r) + } + } + + impl IntoRequest for GetinfoRequest { + type Response = super::responses::GetinfoResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersRequest { #[serde(alias = "id", skip_serializing_if = "Option::is_none")] @@ -134,12 +153,32 @@ pub mod requests { pub level: Option, } + impl From for Request { + fn from(r: ListpeersRequest) -> Self { + Request::ListPeers(r) + } + } + + impl IntoRequest for ListpeersRequest { + type Response = super::responses::ListpeersResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListfundsRequest { #[serde(alias = "spent", skip_serializing_if = "Option::is_none")] pub spent: Option, } + impl From for Request { + fn from(r: ListfundsRequest) -> Self { + Request::ListFunds(r) + } + } + + impl IntoRequest for ListfundsRequest { + type Response = super::responses::ListfundsResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendpayRoute { #[serde(alias = "amount_msat")] @@ -174,6 +213,16 @@ pub mod requests { pub groupid: Option, } + impl From for Request { + fn from(r: SendpayRequest) -> Self { + Request::SendPay(r) + } + } + + impl IntoRequest for SendpayRequest { + type Response = super::responses::SendpayResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListchannelsRequest { #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] @@ -184,12 +233,32 @@ pub mod requests { pub destination: Option, } + impl From for Request { + fn from(r: ListchannelsRequest) -> Self { + Request::ListChannels(r) + } + } + + impl IntoRequest for ListchannelsRequest { + type Response = super::responses::ListchannelsResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AddgossipRequest { #[serde(alias = "message")] pub message: String, } + impl From for Request { + fn from(r: AddgossipRequest) -> Self { + Request::AddGossip(r) + } + } + + impl IntoRequest for AddgossipRequest { + type Response = super::responses::AddgossipResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AutocleaninvoiceRequest { #[serde(alias = "expired_by", skip_serializing_if = "Option::is_none")] @@ -198,6 +267,16 @@ pub mod requests { pub cycle_seconds: Option, } + impl From for Request { + fn from(r: AutocleaninvoiceRequest) -> Self { + Request::AutoCleanInvoice(r) + } + } + + impl IntoRequest for AutocleaninvoiceRequest { + type Response = super::responses::AutocleaninvoiceResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CheckmessageRequest { #[serde(alias = "message")] @@ -208,6 +287,16 @@ pub mod requests { pub pubkey: Option, } + impl From for Request { + fn from(r: CheckmessageRequest) -> Self { + Request::CheckMessage(r) + } + } + + impl IntoRequest for CheckmessageRequest { + type Response = super::responses::CheckmessageResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CloseRequest { #[serde(alias = "id")] @@ -226,6 +315,16 @@ pub mod requests { pub feerange: Option>, } + impl From for Request { + fn from(r: CloseRequest) -> Self { + Request::Close(r) + } + } + + impl IntoRequest for CloseRequest { + type Response = super::responses::CloseResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ConnectRequest { #[serde(alias = "id")] @@ -236,6 +335,16 @@ pub mod requests { pub port: Option, } + impl From for Request { + fn from(r: ConnectRequest) -> Self { + Request::Connect(r) + } + } + + impl IntoRequest for ConnectRequest { + type Response = super::responses::ConnectResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreateinvoiceRequest { #[serde(alias = "invstring")] @@ -246,6 +355,16 @@ pub mod requests { pub preimage: String, } + impl From for Request { + fn from(r: CreateinvoiceRequest) -> Self { + Request::CreateInvoice(r) + } + } + + impl IntoRequest for CreateinvoiceRequest { + type Response = super::responses::CreateinvoiceResponse; + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum DatastoreMode { #[serde(rename = "must-create")] @@ -287,6 +406,16 @@ pub mod requests { pub generation: Option, } + impl From for Request { + fn from(r: DatastoreRequest) -> Self { + Request::Datastore(r) + } + } + + impl IntoRequest for DatastoreRequest { + type Response = super::responses::DatastoreResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreateonionHops { #[serde(alias = "pubkey")] @@ -307,6 +436,16 @@ pub mod requests { pub onion_size: Option, } + impl From for Request { + fn from(r: CreateonionRequest) -> Self { + Request::CreateOnion(r) + } + } + + impl IntoRequest for CreateonionRequest { + type Response = super::responses::CreateonionResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DeldatastoreRequest { #[serde(alias = "key")] @@ -315,12 +454,32 @@ pub mod requests { pub generation: Option, } + impl From for Request { + fn from(r: DeldatastoreRequest) -> Self { + Request::DelDatastore(r) + } + } + + impl IntoRequest for DeldatastoreRequest { + type Response = super::responses::DeldatastoreResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DelexpiredinvoiceRequest { #[serde(alias = "maxexpirytime", skip_serializing_if = "Option::is_none")] pub maxexpirytime: Option, } + impl From for Request { + fn from(r: DelexpiredinvoiceRequest) -> Self { + Request::DelExpiredInvoice(r) + } + } + + impl IntoRequest for DelexpiredinvoiceRequest { + type Response = super::responses::DelexpiredinvoiceResponse; + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum DelinvoiceStatus { #[serde(rename = "paid")] @@ -353,6 +512,16 @@ pub mod requests { pub desconly: Option, } + impl From for Request { + fn from(r: DelinvoiceRequest) -> Self { + Request::DelInvoice(r) + } + } + + impl IntoRequest for DelinvoiceRequest { + type Response = super::responses::DelinvoiceResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct InvoiceRequest { #[serde(alias = "amount_msat")] @@ -375,12 +544,32 @@ pub mod requests { pub deschashonly: Option, } + impl From for Request { + fn from(r: InvoiceRequest) -> Self { + Request::Invoice(r) + } + } + + impl IntoRequest for InvoiceRequest { + type Response = super::responses::InvoiceResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListdatastoreRequest { #[serde(alias = "key", skip_serializing_if = "crate::is_none_or_empty")] pub key: Option>, } + impl From for Request { + fn from(r: ListdatastoreRequest) -> Self { + Request::ListDatastore(r) + } + } + + impl IntoRequest for ListdatastoreRequest { + type Response = super::responses::ListdatastoreResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListinvoicesRequest { #[serde(alias = "label", skip_serializing_if = "Option::is_none")] @@ -393,6 +582,16 @@ pub mod requests { pub offer_id: Option, } + impl From for Request { + fn from(r: ListinvoicesRequest) -> Self { + Request::ListInvoices(r) + } + } + + impl IntoRequest for ListinvoicesRequest { + type Response = super::responses::ListinvoicesResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendonionFirst_hop { #[serde(alias = "id")] @@ -427,6 +626,16 @@ pub mod requests { pub groupid: Option, } + impl From for Request { + fn from(r: SendonionRequest) -> Self { + Request::SendOnion(r) + } + } + + impl IntoRequest for SendonionRequest { + type Response = super::responses::SendonionResponse; + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListsendpaysStatus { #[serde(rename = "pending")] @@ -458,10 +667,30 @@ pub mod requests { pub status: Option, } + impl From for Request { + fn from(r: ListsendpaysRequest) -> Self { + Request::ListSendPays(r) + } + } + + impl IntoRequest for ListsendpaysRequest { + type Response = super::responses::ListsendpaysResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListtransactionsRequest { } + impl From for Request { + fn from(r: ListtransactionsRequest) -> Self { + Request::ListTransactions(r) + } + } + + impl IntoRequest for ListtransactionsRequest { + type Response = super::responses::ListtransactionsResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PayRequest { #[serde(alias = "bolt11")] @@ -490,12 +719,32 @@ pub mod requests { pub description: Option, } + impl From for Request { + fn from(r: PayRequest) -> Self { + Request::Pay(r) + } + } + + impl IntoRequest for PayRequest { + type Response = super::responses::PayResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListnodesRequest { #[serde(alias = "id", skip_serializing_if = "Option::is_none")] pub id: Option, } + impl From for Request { + fn from(r: ListnodesRequest) -> Self { + Request::ListNodes(r) + } + } + + impl IntoRequest for ListnodesRequest { + type Response = super::responses::ListnodesResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitanyinvoiceRequest { #[serde(alias = "lastpay_index", skip_serializing_if = "Option::is_none")] @@ -504,12 +753,32 @@ pub mod requests { pub timeout: Option, } + impl From for Request { + fn from(r: WaitanyinvoiceRequest) -> Self { + Request::WaitAnyInvoice(r) + } + } + + impl IntoRequest for WaitanyinvoiceRequest { + type Response = super::responses::WaitanyinvoiceResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitinvoiceRequest { #[serde(alias = "label")] pub label: String, } + impl From for Request { + fn from(r: WaitinvoiceRequest) -> Self { + Request::WaitInvoice(r) + } + } + + impl IntoRequest for WaitinvoiceRequest { + type Response = super::responses::WaitinvoiceResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WaitsendpayRequest { #[serde(alias = "payment_hash")] @@ -522,6 +791,16 @@ pub mod requests { pub groupid: Option, } + impl From for Request { + fn from(r: WaitsendpayRequest) -> Self { + Request::WaitSendPay(r) + } + } + + impl IntoRequest for WaitsendpayRequest { + type Response = super::responses::WaitsendpayResponse; + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum NewaddrAddresstype { #[serde(rename = "bech32")] @@ -549,6 +828,16 @@ pub mod requests { pub addresstype: Option, } + impl From for Request { + fn from(r: NewaddrRequest) -> Self { + Request::NewAddr(r) + } + } + + impl IntoRequest for NewaddrRequest { + type Response = super::responses::NewaddrResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WithdrawRequest { #[serde(alias = "destination")] @@ -563,6 +852,16 @@ pub mod requests { pub utxos: Option>, } + impl From for Request { + fn from(r: WithdrawRequest) -> Self { + Request::Withdraw(r) + } + } + + impl IntoRequest for WithdrawRequest { + type Response = super::responses::WithdrawResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct KeysendExtratlvs { } @@ -587,6 +886,16 @@ pub mod requests { pub routehints: Option, } + impl From for Request { + fn from(r: KeysendRequest) -> Self { + Request::KeySend(r) + } + } + + impl IntoRequest for KeysendRequest { + type Response = super::responses::KeysendResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundpsbtRequest { #[serde(alias = "satoshi")] @@ -607,6 +916,16 @@ pub mod requests { pub excess_as_change: Option, } + impl From for Request { + fn from(r: FundpsbtRequest) -> Self { + Request::FundPsbt(r) + } + } + + impl IntoRequest for FundpsbtRequest { + type Response = super::responses::FundpsbtResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendpsbtRequest { #[serde(alias = "psbt")] @@ -615,6 +934,16 @@ pub mod requests { pub reserve: Option, } + impl From for Request { + fn from(r: SendpsbtRequest) -> Self { + Request::SendPsbt(r) + } + } + + impl IntoRequest for SendpsbtRequest { + type Response = super::responses::SendpsbtResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignpsbtRequest { #[serde(alias = "psbt")] @@ -623,6 +952,16 @@ pub mod requests { pub signonly: Option>, } + impl From for Request { + fn from(r: SignpsbtRequest) -> Self { + Request::SignPsbt(r) + } + } + + impl IntoRequest for SignpsbtRequest { + type Response = super::responses::SignpsbtResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct UtxopsbtRequest { #[serde(alias = "satoshi")] @@ -645,12 +984,32 @@ pub mod requests { pub excess_as_change: Option, } + impl From for Request { + fn from(r: UtxopsbtRequest) -> Self { + Request::UtxoPsbt(r) + } + } + + impl IntoRequest for UtxopsbtRequest { + type Response = super::responses::UtxopsbtResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TxdiscardRequest { #[serde(alias = "txid")] pub txid: String, } + impl From for Request { + fn from(r: TxdiscardRequest) -> Self { + Request::TxDiscard(r) + } + } + + impl IntoRequest for TxdiscardRequest { + type Response = super::responses::TxdiscardResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TxprepareRequest { #[serde(alias = "outputs")] @@ -663,12 +1022,32 @@ pub mod requests { pub utxos: Option>, } + impl From for Request { + fn from(r: TxprepareRequest) -> Self { + Request::TxPrepare(r) + } + } + + impl IntoRequest for TxprepareRequest { + type Response = super::responses::TxprepareResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TxsendRequest { #[serde(alias = "txid")] pub txid: String, } + impl From for Request { + fn from(r: TxsendRequest) -> Self { + Request::TxSend(r) + } + } + + impl IntoRequest for TxsendRequest { + type Response = super::responses::TxsendResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DisconnectRequest { #[serde(alias = "id")] @@ -677,6 +1056,16 @@ pub mod requests { pub force: Option, } + impl From for Request { + fn from(r: DisconnectRequest) -> Self { + Request::Disconnect(r) + } + } + + impl IntoRequest for DisconnectRequest { + type Response = super::responses::DisconnectResponse; + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum FeeratesStyle { #[serde(rename = "perkb")] @@ -702,6 +1091,16 @@ pub mod requests { pub style: FeeratesStyle, } + impl From for Request { + fn from(r: FeeratesRequest) -> Self { + Request::Feerates(r) + } + } + + impl IntoRequest for FeeratesRequest { + type Response = super::responses::FeeratesResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundchannelRequest { #[serde(alias = "id")] @@ -730,6 +1129,16 @@ pub mod requests { pub reserve: Option, } + impl From for Request { + fn from(r: FundchannelRequest) -> Self { + Request::FundChannel(r) + } + } + + impl IntoRequest for FundchannelRequest { + type Response = super::responses::FundchannelResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetrouteRequest { #[serde(alias = "id")] @@ -750,6 +1159,16 @@ pub mod requests { pub maxhops: Option, } + impl From for Request { + fn from(r: GetrouteRequest) -> Self { + Request::GetRoute(r) + } + } + + impl IntoRequest for GetrouteRequest { + type Response = super::responses::GetrouteResponse; + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListforwardsStatus { #[serde(rename = "offered")] @@ -784,6 +1203,16 @@ pub mod requests { pub out_channel: Option, } + impl From for Request { + fn from(r: ListforwardsRequest) -> Self { + Request::ListForwards(r) + } + } + + impl IntoRequest for ListforwardsRequest { + type Response = super::responses::ListforwardsResponse; + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListpaysStatus { #[serde(rename = "pending")] @@ -815,6 +1244,16 @@ pub mod requests { pub status: Option, } + impl From for Request { + fn from(r: ListpaysRequest) -> Self { + Request::ListPays(r) + } + } + + impl IntoRequest for ListpaysRequest { + type Response = super::responses::ListpaysResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PingRequest { #[serde(alias = "id")] @@ -825,16 +1264,46 @@ pub mod requests { pub pongbytes: Option, } + impl From for Request { + fn from(r: PingRequest) -> Self { + Request::Ping(r) + } + } + + impl IntoRequest for PingRequest { + type Response = super::responses::PingResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignmessageRequest { #[serde(alias = "message")] pub message: String, } + impl From for Request { + fn from(r: SignmessageRequest) -> Self { + Request::SignMessage(r) + } + } + + impl IntoRequest for SignmessageRequest { + type Response = super::responses::SignmessageResponse; + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct StopRequest { } + impl From for Request { + fn from(r: StopRequest) -> Self { + Request::Stop(r) + } + } + + impl IntoRequest for StopRequest { + type Response = super::responses::StopResponse; + } + } @@ -843,6 +1312,7 @@ pub mod responses { use crate::primitives::*; #[allow(unused_imports)] use serde::{{Deserialize, Serialize}}; + use super::{TryFromResponseError, Response}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoOur_features { @@ -975,6 +1445,17 @@ pub mod responses { pub warning_lightningd_sync: Option, } + impl TryFrom for GetinfoResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Getinfo(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListpeersPeersLogType { #[serde(rename = "SKIPPED")] @@ -1302,6 +1783,17 @@ pub mod responses { pub peers: Vec, } + impl TryFrom for ListpeersResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListPeers(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListfundsOutputsStatus { #[serde(rename = "unconfirmed")] @@ -1375,6 +1867,17 @@ pub mod responses { pub channels: Vec, } + impl TryFrom for ListfundsResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListFunds(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// status of the payment (could be complete if already sent previously) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum SendpayStatus { @@ -1429,6 +1932,17 @@ pub mod responses { pub message: Option, } + impl TryFrom for SendpayResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::SendPay(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListchannelsChannels { #[serde(alias = "source")] @@ -1469,10 +1983,32 @@ pub mod responses { pub channels: Vec, } + impl TryFrom for ListchannelsResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListChannels(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AddgossipResponse { } + impl TryFrom for AddgossipResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::AddGossip(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct AutocleaninvoiceResponse { #[serde(alias = "enabled")] @@ -1483,6 +2019,17 @@ pub mod responses { pub cycle_seconds: Option, } + impl TryFrom for AutocleaninvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::AutoCleanInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CheckmessageResponse { #[serde(alias = "verified")] @@ -1491,6 +2038,17 @@ pub mod responses { pub pubkey: Pubkey, } + impl TryFrom for CheckmessageResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::CheckMessage(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum CloseType { @@ -1524,6 +2082,17 @@ pub mod responses { pub txid: Option, } + impl TryFrom for CloseResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Close(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// Whether they initiated connection or we did #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ConnectDirection { @@ -1595,6 +2164,17 @@ pub mod responses { pub direction: ConnectDirection, } + impl TryFrom for ConnectResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Connect(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// Whether it has been paid, or can no longer be paid #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum CreateinvoiceStatus { @@ -1650,6 +2230,17 @@ pub mod responses { pub payer_note: Option, } + impl TryFrom for CreateinvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::CreateInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DatastoreResponse { #[serde(alias = "key")] @@ -1662,6 +2253,17 @@ pub mod responses { pub string: Option, } + impl TryFrom for DatastoreResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Datastore(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreateonionResponse { #[serde(alias = "onion")] @@ -1670,6 +2272,17 @@ pub mod responses { pub shared_secrets: Vec, } + impl TryFrom for CreateonionResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::CreateOnion(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DeldatastoreResponse { #[serde(alias = "key")] @@ -1682,10 +2295,32 @@ pub mod responses { pub string: Option, } + impl TryFrom for DeldatastoreResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::DelDatastore(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DelexpiredinvoiceResponse { } + impl TryFrom for DelexpiredinvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::DelExpiredInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// State of invoice #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum DelinvoiceStatus { @@ -1733,6 +2368,17 @@ pub mod responses { pub payer_note: Option, } + impl TryFrom for DelinvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::DelInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct InvoiceResponse { #[serde(alias = "bolt11")] @@ -1755,6 +2401,17 @@ pub mod responses { pub warning_mpp: Option, } + impl TryFrom for InvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Invoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListdatastoreDatastore { #[serde(alias = "key")] @@ -1773,6 +2430,17 @@ pub mod responses { pub datastore: Vec, } + impl TryFrom for ListdatastoreResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListDatastore(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// Whether it's paid, unpaid or unpayable #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListinvoicesInvoicesStatus { @@ -1834,6 +2502,17 @@ pub mod responses { pub invoices: Vec, } + impl TryFrom for ListinvoicesResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListInvoices(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// status of the payment (could be complete if already sent previously) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum SendonionStatus { @@ -1884,6 +2563,17 @@ pub mod responses { pub message: Option, } + impl TryFrom for SendonionResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::SendOnion(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// status of the payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListsendpaysPaymentsStatus { @@ -1945,6 +2635,17 @@ pub mod responses { pub payments: Vec, } + impl TryFrom for ListsendpaysResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListSendPays(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// the purpose of this input (*EXPERIMENTAL_FEATURES* only) #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListtransactionsTransactionsInputsType { @@ -2093,6 +2794,17 @@ pub mod responses { pub transactions: Vec, } + impl TryFrom for ListtransactionsResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListTransactions(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// status of payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum PayStatus { @@ -2138,6 +2850,17 @@ pub mod responses { pub status: PayStatus, } + impl TryFrom for PayResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Pay(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// Type of connection #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListnodesNodesAddressesType { @@ -2202,6 +2925,17 @@ pub mod responses { pub nodes: Vec, } + impl TryFrom for ListnodesResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListNodes(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// Whether it's paid or expired #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum WaitanyinvoiceStatus { @@ -2250,6 +2984,17 @@ pub mod responses { pub payment_preimage: Option, } + impl TryFrom for WaitanyinvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::WaitAnyInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// Whether it's paid or expired #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum WaitinvoiceStatus { @@ -2298,6 +3043,17 @@ pub mod responses { pub payment_preimage: Option, } + impl TryFrom for WaitinvoiceResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::WaitInvoice(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// status of the payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum WaitsendpayStatus { @@ -2347,6 +3103,17 @@ pub mod responses { pub payment_preimage: Option, } + impl TryFrom for WaitsendpayResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::WaitSendPay(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct NewaddrResponse { #[serde(alias = "bech32", skip_serializing_if = "Option::is_none")] @@ -2355,6 +3122,17 @@ pub mod responses { pub p2sh_segwit: Option, } + impl TryFrom for NewaddrResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::NewAddr(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct WithdrawResponse { #[serde(alias = "tx")] @@ -2365,6 +3143,17 @@ pub mod responses { pub psbt: String, } + impl TryFrom for WithdrawResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Withdraw(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// status of payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum KeysendStatus { @@ -2404,6 +3193,17 @@ pub mod responses { pub status: KeysendStatus, } + impl TryFrom for KeysendResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::KeySend(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundpsbtReservations { #[serde(alias = "txid")] @@ -2434,6 +3234,17 @@ pub mod responses { pub reservations: Option>, } + impl TryFrom for FundpsbtResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::FundPsbt(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendpsbtResponse { #[serde(alias = "tx")] @@ -2442,12 +3253,34 @@ pub mod responses { pub txid: String, } + impl TryFrom for SendpsbtResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::SendPsbt(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignpsbtResponse { #[serde(alias = "signed_psbt")] pub signed_psbt: String, } + impl TryFrom for SignpsbtResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::SignPsbt(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct UtxopsbtReservations { #[serde(alias = "txid")] @@ -2478,6 +3311,17 @@ pub mod responses { pub reservations: Option>, } + impl TryFrom for UtxopsbtResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::UtxoPsbt(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TxdiscardResponse { #[serde(alias = "unsigned_tx")] @@ -2486,6 +3330,17 @@ pub mod responses { pub txid: String, } + impl TryFrom for TxdiscardResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::TxDiscard(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TxprepareResponse { #[serde(alias = "psbt")] @@ -2496,6 +3351,17 @@ pub mod responses { pub txid: String, } + impl TryFrom for TxprepareResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::TxPrepare(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct TxsendResponse { #[serde(alias = "psbt")] @@ -2506,10 +3372,32 @@ pub mod responses { pub txid: String, } + impl TryFrom for TxsendResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::TxSend(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DisconnectResponse { } + impl TryFrom for DisconnectResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Disconnect(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FeeratesPerkb { #[serde(alias = "min_acceptable")] @@ -2570,6 +3458,17 @@ pub mod responses { pub warning_missing_feerates: Option, } + impl TryFrom for FeeratesResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Feerates(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundchannelResponse { #[serde(alias = "tx")] @@ -2586,6 +3485,17 @@ pub mod responses { pub mindepth: Option, } + impl TryFrom for FundchannelResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::FundChannel(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// The features understood by the destination node #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum GetrouteRouteStyle { @@ -2625,6 +3535,17 @@ pub mod responses { pub route: Vec, } + impl TryFrom for GetrouteResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::GetRoute(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// still ongoing, completed, failed locally, or failed after forwarding #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListforwardsForwardsStatus { @@ -2700,6 +3621,17 @@ pub mod responses { pub forwards: Vec, } + impl TryFrom for ListforwardsResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListForwards(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + /// status of the payment #[derive(Copy, Clone, Debug, Deserialize, Serialize)] pub enum ListpaysPaysStatus { @@ -2757,12 +3689,34 @@ pub mod responses { pub pays: Vec, } + impl TryFrom for ListpaysResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::ListPays(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PingResponse { #[serde(alias = "totlen")] pub totlen: u16, } + impl TryFrom for PingResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Ping(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignmessageResponse { #[serde(alias = "signature")] @@ -2773,9 +3727,31 @@ pub mod responses { pub zbase: String, } + impl TryFrom for SignmessageResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::SignMessage(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct StopResponse { } + impl TryFrom for StopResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::Stop(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + } diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index e8a5c504b36b..1dcc3e0ac694 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -5,6 +5,9 @@ use serde::{Deserialize, Serialize}; use serde::{Deserializer, Serializer}; use std::str::FromStr; use std::string::ToString; +use bitcoin_hashes::Hash as BitcoinHash; + +pub use bitcoin_hashes::sha256::Hash as Sha256; #[derive(Copy, Clone, Serialize, Deserialize, Debug)] #[allow(non_camel_case_types)] @@ -224,51 +227,9 @@ impl Secret { } } -#[derive(Clone, Debug)] -pub struct Sha256([u8; 32]); -impl Sha256 { - pub fn to_vec(self) -> Vec { - self.0.to_vec() - } -} - -impl TryFrom> for Sha256 { - type Error = crate::Error; - fn try_from(v: Vec) -> Result { - if v.len() != 32 { - Err(anyhow!("Unexpected hash length: {}", hex::encode(v))) - } else { - Ok(Sha256(v.try_into().unwrap())) - } - } -} - -impl Serialize for Sha256 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&hex::encode(&self.0)) - } -} - -impl<'de> Deserialize<'de> for Sha256 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - let s: String = Deserialize::deserialize(deserializer)?; - let h = hex::decode(s).map_err(|_| Error::custom("not a valid hex string"))?; - Ok(Sha256(h.try_into().map_err(|_| { - Error::custom("not a valid hex-encoded hash") - })?)) - } -} - #[derive(Clone, Debug, PartialEq)] pub struct Outpoint { - pub txid: Vec, + pub txid: Sha256, pub outnum: u32, } @@ -294,8 +255,11 @@ impl<'de> Deserialize<'de> for Outpoint { return Err(Error::custom("not a valid txid:output tuple")); } - let txid = + let txid_bytes = hex::decode(splits[0]).map_err(|_| Error::custom("not a valid hex encoded txid"))?; + + let txid= Sha256::from_slice(&txid_bytes).map_err(|e| Error::custom(format!("Invalid TxId: {}", e)))?; + let outnum: u32 = splits[1] .parse() .map_err(|e| Error::custom(format!("{} is not a valid number: {}", s, e)))?; diff --git a/contrib/msggen/msggen/gen/grpc.py b/contrib/msggen/msggen/gen/grpc.py index 074b435cebac..def075955990 100644 --- a/contrib/msggen/msggen/gen/grpc.py +++ b/contrib/msggen/msggen/gen/grpc.py @@ -349,6 +349,8 @@ def generate(self, service: Service) -> None: use cln_rpc::model::{responses,requests}; use crate::pb; use std::str::FromStr; + use bitcoin_hashes::sha256::Hash as Sha256; + use bitcoin_hashes::Hash; """) @@ -434,8 +436,8 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'short_channel_id?': f'c.{name}.map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap())', 'secret': f'c.{name}.try_into().unwrap()', 'secret?': f'c.{name}.map(|v| v.try_into().unwrap())', - 'hash': f'c.{name}.try_into().unwrap()', - 'hash?': f'c.{name}.map(|v| v.try_into().unwrap())', + 'hash': f'Sha256::from_slice(&c.{name}).unwrap()', + 'hash?': f'c.{name}.map(|v| Sha256::from_slice(&v).unwrap())', 'txid': f'hex::encode(&c.{name})', }.get( typ, From b4b0b479ac1c60111a4eadda6a3b68f158f5f11c Mon Sep 17 00:00:00 2001 From: elsirion Date: Fri, 13 May 2022 21:30:07 +0000 Subject: [PATCH 1496/1530] Use `secp256k1` for public key type --- Cargo.lock | 338 ++++++++++++++++-------------- cln-grpc/Cargo.toml | 2 +- cln-grpc/src/convert.rs | 63 +++--- cln-grpc/src/pb.rs | 4 +- cln-rpc/Cargo.toml | 1 + cln-rpc/src/model.rs | 156 +++++++------- cln-rpc/src/primitives.rs | 54 +---- contrib/msggen/msggen/gen/grpc.py | 9 +- contrib/msggen/msggen/gen/rust.py | 2 +- 9 files changed, 300 insertions(+), 329 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83b287a1d5a9..15d0c1185d25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.57" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "async-stream" @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.53" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -89,15 +89,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cc" @@ -113,9 +113,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ "num-integer", "num-traits", @@ -166,7 +166,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tokio-util 0.6.9", + "tokio-util 0.6.10", ] [[package]] @@ -180,10 +180,11 @@ dependencies = [ "futures-util", "hex", "log", + "secp256k1", "serde", "serde_json", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.6.10", ] [[package]] @@ -218,15 +219,15 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272" dependencies = [ "atty", "humantime", @@ -237,9 +238,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -258,9 +259,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "futures" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -273,9 +274,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -283,15 +284,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -300,15 +301,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -317,21 +318,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ "futures-channel", "futures-core", @@ -347,20 +348,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "h2" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" dependencies = [ "bytes", "fnv", @@ -371,15 +372,15 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.4", "tracing", ] [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -407,9 +408,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", @@ -418,9 +419,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -429,9 +430,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -447,9 +448,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -483,9 +484,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", @@ -502,24 +503,24 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -532,9 +533,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.125" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "log" @@ -559,13 +560,13 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys", ] @@ -636,24 +637,24 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "pem" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947" +checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" dependencies = [ "base64", ] [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "petgraph" @@ -667,18 +668,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", @@ -705,11 +706,11 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro2" -version = "1.0.38" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -765,9 +766,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -795,9 +796,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -817,18 +818,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -837,9 +838,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -889,9 +890,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "sct" @@ -903,20 +904,39 @@ dependencies = [ "untrusted", ] +[[package]] +name = "secp256k1" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26947345339603ae8395f68e2f3d85a6b0a8ddfe6315818e80b8504415099db0" +dependencies = [ + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "152e20a0fd0519390fc43ab404663af8a0b794273d2a91d60ad4a39f13ffe110" +dependencies = [ + "cc", +] + [[package]] name = "serde" -version = "1.0.137" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -925,9 +945,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ "itoa", "ryu", @@ -936,15 +956,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -958,13 +981,13 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "syn" -version = "1.0.94" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" +checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -992,18 +1015,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43" dependencies = [ "proc-macro2", "quote", @@ -1012,10 +1035,11 @@ dependencies = [ [[package]] name = "tokio" -version = "1.18.2" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" +checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" dependencies = [ + "autocfg", "bytes", "libc", "memchr", @@ -1040,9 +1064,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ "proc-macro2", "quote", @@ -1062,9 +1086,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af" dependencies = [ "futures-core", "pin-project-lite", @@ -1073,9 +1097,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", @@ -1087,9 +1111,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" dependencies = [ "bytes", "futures-core", @@ -1123,7 +1147,7 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-stream", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "tower", "tower-layer", "tower-service", @@ -1145,9 +1169,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a89fd63ad6adf737582df5db40d286574513c69a11dac5214dc3b5603d6713e" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", @@ -1157,7 +1181,7 @@ dependencies = [ "rand", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.4", "tower-layer", "tower-service", "tracing", @@ -1171,15 +1195,15 @@ checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" dependencies = [ "cfg-if", "log", @@ -1190,9 +1214,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" dependencies = [ "proc-macro2", "quote", @@ -1201,11 +1225,11 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] @@ -1225,16 +1249,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] -name = "unicode-segmentation" -version = "1.9.0" +name = "unicode-ident" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] -name = "unicode-xid" -version = "0.2.3" +name = "unicode-segmentation" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a" [[package]] name = "untrusted" @@ -1252,12 +1276,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1266,9 +1284,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1276,13 +1294,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -1291,9 +1309,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1301,9 +1319,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -1314,15 +1332,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -1340,13 +1358,13 @@ dependencies = [ [[package]] name = "which" -version = "4.2.5" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] diff --git a/cln-grpc/Cargo.toml b/cln-grpc/Cargo.toml index d76886919940..dd96e8fa8043 100644 --- a/cln-grpc/Cargo.toml +++ b/cln-grpc/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" log = "0.4" -cln-rpc = { path="../cln-rpc" } +cln-rpc = { path="../cln-rpc/" } tonic = { version = "^0.5", features = ["tls", "transport"] } prost = "0.8" hex = "0.4.3" diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 4543f4f1d9db..538d5b4194fc 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -10,6 +10,7 @@ use crate::pb; use std::str::FromStr; use bitcoin_hashes::sha256::Hash as Sha256; use bitcoin_hashes::Hash; +use cln_rpc::primitives::PublicKey; #[allow(unused_variables)] impl From for pb::GetinfoAddress { @@ -38,7 +39,7 @@ impl From for pb::GetinfoBinding { impl From for pb::GetinfoResponse { fn from(c: responses::GetinfoResponse) -> Self { Self { - id: c.id.to_vec(), // Rule #2 for type pubkey + id: c.id.serialize().to_vec(), // Rule #2 for type pubkey alias: c.alias, // Rule #2 for type string color: hex::decode(&c.color).unwrap(), // Rule #2 for type hex num_peers: c.num_peers, // Rule #2 for type u32 @@ -67,7 +68,7 @@ impl From for pb::ListpeersPeersLog { time: c.time, // Rule #2 for type string? source: c.source, // Rule #2 for type string? log: c.log, // Rule #2 for type string? - node_id: c.node_id.map(|v| v.to_vec()), // Rule #2 for type pubkey? + node_id: c.node_id.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? data: c.data.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? } } @@ -160,7 +161,7 @@ impl From for pb::ListpeersPeersChannels { impl From for pb::ListpeersPeers { fn from(c: responses::ListpeersPeers) -> Self { Self { - id: c.id.to_vec(), // Rule #2 for type pubkey + id: c.id.serialize().to_vec(), // Rule #2 for type pubkey connected: c.connected, // Rule #2 for type boolean log: c.log.map(|arr| arr.into_iter().map(|i| i.into()).collect()).unwrap_or(vec![]), // Rule #3 channels: c.channels.into_iter().map(|i| i.into()).collect(), // Rule #3 for type ListpeersPeersChannels @@ -201,7 +202,7 @@ impl From for pb::ListfundsOutputs { impl From for pb::ListfundsChannels { fn from(c: responses::ListfundsChannels) -> Self { Self { - peer_id: c.peer_id.to_vec(), // Rule #2 for type pubkey + peer_id: c.peer_id.serialize().to_vec(), // Rule #2 for type pubkey our_amount_msat: Some(c.our_amount_msat.into()), // Rule #2 for type msat amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat funding_txid: hex::decode(&c.funding_txid).unwrap(), // Rule #2 for type txid @@ -232,7 +233,7 @@ impl From for pb::SendpayResponse { payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? created_at: c.created_at, // Rule #2 for type u64 completed_at: c.completed_at, // Rule #2 for type u64? amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat @@ -250,8 +251,8 @@ impl From for pb::SendpayResponse { impl From for pb::ListchannelsChannels { fn from(c: responses::ListchannelsChannels) -> Self { Self { - source: c.source.to_vec(), // Rule #2 for type pubkey - destination: c.destination.to_vec(), // Rule #2 for type pubkey + source: c.source.serialize().to_vec(), // Rule #2 for type pubkey + destination: c.destination.serialize().to_vec(), // Rule #2 for type pubkey short_channel_id: c.short_channel_id.to_string(), // Rule #2 for type short_channel_id public: c.public, // Rule #2 for type boolean amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat @@ -302,7 +303,7 @@ impl From for pb::CheckmessageResponse { fn from(c: responses::CheckmessageResponse) -> Self { Self { verified: c.verified, // Rule #2 for type boolean - pubkey: c.pubkey.to_vec(), // Rule #2 for type pubkey + pubkey: c.pubkey.serialize().to_vec(), // Rule #2 for type pubkey } } } @@ -322,7 +323,7 @@ impl From for pb::CloseResponse { impl From for pb::ConnectResponse { fn from(c: responses::ConnectResponse) -> Self { Self { - id: c.id.to_vec(), // Rule #2 for type pubkey + id: c.id.serialize().to_vec(), // Rule #2 for type pubkey features: hex::decode(&c.features).unwrap(), // Rule #2 for type hex direction: c.direction as i32, } @@ -488,7 +489,7 @@ impl From for pb::SendonionResponse { payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? created_at: c.created_at, // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label, // Rule #2 for type string? @@ -510,7 +511,7 @@ impl From for pb::ListsendpaysPayments { payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? created_at: c.created_at, // Rule #2 for type u64 amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat label: c.label, // Rule #2 for type string? @@ -589,7 +590,7 @@ impl From for pb::PayResponse { fn from(c: responses::PayResponse) -> Self { Self { payment_preimage: c.payment_preimage.to_vec(), // Rule #2 for type secret - destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash created_at: c.created_at, // Rule #2 for type number parts: c.parts, // Rule #2 for type u32 @@ -616,7 +617,7 @@ impl From for pb::ListnodesNodesAddresses { impl From for pb::ListnodesNodes { fn from(c: responses::ListnodesNodes) -> Self { Self { - nodeid: c.nodeid.to_vec(), // Rule #2 for type pubkey + nodeid: c.nodeid.serialize().to_vec(), // Rule #2 for type pubkey last_timestamp: c.last_timestamp, // Rule #2 for type u32? alias: c.alias, // Rule #2 for type string? color: c.color.map(|v| hex::decode(v).unwrap()), // Rule #2 for type hex? @@ -684,7 +685,7 @@ impl From for pb::WaitsendpayResponse { payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash status: c.status as i32, amount_msat: c.amount_msat.map(|f| f.into()), // Rule #2 for type msat? - destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? created_at: c.created_at, // Rule #2 for type u64 completed_at: c.completed_at, // Rule #2 for type number? amount_sent_msat: Some(c.amount_sent_msat.into()), // Rule #2 for type msat @@ -723,7 +724,7 @@ impl From for pb::KeysendResponse { fn from(c: responses::KeysendResponse) -> Self { Self { payment_preimage: c.payment_preimage.to_vec(), // Rule #2 for type secret - destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? payment_hash: c.payment_hash.to_vec(), // Rule #2 for type hash created_at: c.created_at, // Rule #2 for type number parts: c.parts, // Rule #2 for type u32 @@ -875,7 +876,7 @@ impl From for pb::FundchannelResponse { impl From for pb::GetrouteRoute { fn from(c: responses::GetrouteRoute) -> Self { Self { - id: c.id.to_vec(), // Rule #2 for type pubkey + id: c.id.serialize().to_vec(), // Rule #2 for type pubkey channel: c.channel.to_string(), // Rule #2 for type short_channel_id direction: c.direction, // Rule #2 for type u32 amount_msat: Some(c.amount_msat.into()), // Rule #2 for type msat @@ -927,7 +928,7 @@ impl From for pb::ListpaysPays { Self { payment_hash: hex::decode(&c.payment_hash).unwrap(), // Rule #2 for type hex status: c.status as i32, - destination: c.destination.map(|v| v.to_vec()), // Rule #2 for type pubkey? + destination: c.destination.map(|v| v.serialize().to_vec()), // Rule #2 for type pubkey? created_at: c.created_at, // Rule #2 for type u64 completed_at: c.completed_at, // Rule #2 for type u64? label: c.label, // Rule #2 for type string? @@ -990,7 +991,7 @@ impl From for requests::GetinfoRequest { impl From for requests::ListpeersRequest { fn from(c: pb::ListpeersRequest) -> Self { Self { - id: c.id.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + id: c.id.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? level: c.level, // Rule #1 for type string? } } @@ -1010,7 +1011,7 @@ impl From for requests::SendpayRoute { fn from(c: pb::SendpayRoute) -> Self { Self { amount_msat: c.amount_msat.unwrap().into(), // Rule #1 for type msat - id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + id: PublicKey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey delay: c.delay as u16, // Rule #1 for type u16 channel: cln_rpc::primitives::ShortChannelId::from_str(&c.channel).unwrap(), // Rule #1 for type short_channel_id } @@ -1039,8 +1040,8 @@ impl From for requests::ListchannelsRequest { fn from(c: pb::ListchannelsRequest) -> Self { Self { short_channel_id: c.short_channel_id.map(|v| cln_rpc::primitives::ShortChannelId::from_str(&v).unwrap()), // Rule #1 for type short_channel_id? - source: c.source.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? - destination: c.destination.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + source: c.source.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? + destination: c.destination.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? } } } @@ -1070,7 +1071,7 @@ impl From for requests::CheckmessageRequest { Self { message: c.message, // Rule #1 for type string zbase: c.zbase, // Rule #1 for type string - pubkey: c.pubkey.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + pubkey: c.pubkey.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? } } } @@ -1129,7 +1130,7 @@ impl From for requests::DatastoreRequest { impl From for requests::CreateonionHops { fn from(c: pb::CreateonionHops) -> Self { Self { - pubkey: cln_rpc::primitives::Pubkey::from_slice(&c.pubkey).unwrap(), // Rule #1 for type pubkey + pubkey: PublicKey::from_slice(&c.pubkey).unwrap(), // Rule #1 for type pubkey payload: hex::encode(&c.payload), // Rule #1 for type hex } } @@ -1226,7 +1227,7 @@ impl From for requests::SendonionRequest { partid: c.partid.map(|v| v as u16), // Rule #1 for type u16? bolt11: c.bolt11, // Rule #1 for type string? amount_msat: c.amount_msat.map(|a| a.into()), // Rule #1 for type msat? - destination: c.destination.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + destination: c.destination.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? localofferid: c.localofferid.map(|v| Sha256::from_slice(&v).unwrap()), // Rule #1 for type hash? groupid: c.groupid, // Rule #1 for type u64? } @@ -1276,7 +1277,7 @@ impl From for requests::PayRequest { impl From for requests::ListnodesRequest { fn from(c: pb::ListnodesRequest) -> Self { Self { - id: c.id.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + id: c.id.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? } } } @@ -1338,7 +1339,7 @@ impl From for requests::WithdrawRequest { impl From for requests::KeysendRequest { fn from(c: pb::KeysendRequest) -> Self { Self { - destination: cln_rpc::primitives::Pubkey::from_slice(&c.destination).unwrap(), // Rule #1 for type pubkey + destination: PublicKey::from_slice(&c.destination).unwrap(), // Rule #1 for type pubkey amount_msat: c.amount_msat.unwrap().into(), // Rule #1 for type msat label: c.label, // Rule #1 for type string? maxfeepercent: c.maxfeepercent, // Rule #1 for type number? @@ -1437,7 +1438,7 @@ impl From for requests::TxsendRequest { impl From for requests::DisconnectRequest { fn from(c: pb::DisconnectRequest) -> Self { Self { - id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + id: PublicKey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey force: c.force, // Rule #1 for type boolean? } } @@ -1456,7 +1457,7 @@ impl From for requests::FeeratesRequest { impl From for requests::FundchannelRequest { fn from(c: pb::FundchannelRequest) -> Self { Self { - id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + id: PublicKey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey amount: c.amount.unwrap().into(), // Rule #1 for type msat_or_all feerate: c.feerate.map(|a| a.into()), // Rule #1 for type feerate? announce: c.announce, // Rule #1 for type boolean? @@ -1476,11 +1477,11 @@ impl From for requests::FundchannelRequest { impl From for requests::GetrouteRequest { fn from(c: pb::GetrouteRequest) -> Self { Self { - id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + id: PublicKey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey amount_msat: c.amount_msat.unwrap().into(), // Rule #1 for type msat riskfactor: c.riskfactor, // Rule #1 for type u64 cltv: c.cltv, // Rule #1 for type number? - fromid: c.fromid.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap()), // Rule #1 for type pubkey? + fromid: c.fromid.map(|v| PublicKey::from_slice(&v).unwrap()), // Rule #1 for type pubkey? fuzzpercent: c.fuzzpercent, // Rule #1 for type u32? exclude: Some(c.exclude.into_iter().map(|s| s.into()).collect()), // Rule #4 maxhops: c.maxhops, // Rule #1 for type u32? @@ -1514,7 +1515,7 @@ impl From for requests::ListpaysRequest { impl From for requests::PingRequest { fn from(c: pb::PingRequest) -> Self { Self { - id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey + id: PublicKey::from_slice(&c.id).unwrap(), // Rule #1 for type pubkey len: c.len, // Rule #1 for type number? pongbytes: c.pongbytes, // Rule #1 for type number? } diff --git a/cln-grpc/src/pb.rs b/cln-grpc/src/pb.rs index 734f91fc72c2..9a9817660329 100644 --- a/cln-grpc/src/pb.rs +++ b/cln-grpc/src/pb.rs @@ -1,6 +1,6 @@ tonic::include_proto!("cln"); -use std::str::FromStr; use bitcoin_hashes::Hash; +use std::str::FromStr; use cln_rpc::primitives::{ Amount as JAmount, AmountOrAll as JAmountOrAll, AmountOrAny as JAmountOrAny, @@ -106,7 +106,7 @@ impl From for JAmountOrAny { impl From for cln_rpc::primitives::Routehop { fn from(c: RouteHop) -> Self { Self { - id: cln_rpc::primitives::Pubkey::from_slice(&c.id).unwrap(), + id: cln_rpc::primitives::PublicKey::from_slice(&c.id).unwrap(), scid: cln_rpc::primitives::ShortChannelId::from_str(&c.short_channel_id).unwrap(), feebase: c.feebase.unwrap().into(), feeprop: c.feeprop, diff --git a/cln-rpc/Cargo.toml b/cln-rpc/Cargo.toml index 4346641b64f6..086a106ac727 100644 --- a/cln-rpc/Cargo.toml +++ b/cln-rpc/Cargo.toml @@ -12,6 +12,7 @@ anyhow = "1.0.51" bitcoin_hashes = { version = "0.10.0", features = [ "serde" ] } bytes = "1.1.0" log = "0.4.14" +secp256k1 = { version = "0.22.1", features = [ "serde" ] } serde = { version = "1.0.131", features = ["derive"] } serde_json = "1.0.72" tokio-util = { version = "0.6.9", features = ["codec"] } diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 06f004aa704f..a577f2480a51 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -148,7 +148,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersRequest { #[serde(alias = "id", skip_serializing_if = "Option::is_none")] - pub id: Option, + pub id: Option, #[serde(alias = "level", skip_serializing_if = "Option::is_none")] pub level: Option, } @@ -184,7 +184,7 @@ pub mod requests { #[serde(alias = "amount_msat")] pub amount_msat: Amount, #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "delay")] pub delay: u16, #[serde(alias = "channel")] @@ -228,9 +228,9 @@ pub mod requests { #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] pub short_channel_id: Option, #[serde(alias = "source", skip_serializing_if = "Option::is_none")] - pub source: Option, + pub source: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, } impl From for Request { @@ -284,7 +284,7 @@ pub mod requests { #[serde(alias = "zbase")] pub zbase: String, #[serde(alias = "pubkey", skip_serializing_if = "Option::is_none")] - pub pubkey: Option, + pub pubkey: Option, } impl From for Request { @@ -419,7 +419,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct CreateonionHops { #[serde(alias = "pubkey")] - pub pubkey: Pubkey, + pub pubkey: PublicKey, #[serde(alias = "payload")] pub payload: String, } @@ -595,7 +595,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SendonionFirst_hop { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "amount_msat")] pub amount_msat: Amount, #[serde(alias = "delay")] @@ -619,7 +619,7 @@ pub mod requests { #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "localofferid", skip_serializing_if = "Option::is_none")] pub localofferid: Option, #[serde(alias = "groupid", skip_serializing_if = "Option::is_none")] @@ -732,7 +732,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListnodesRequest { #[serde(alias = "id", skip_serializing_if = "Option::is_none")] - pub id: Option, + pub id: Option, } impl From for Request { @@ -869,7 +869,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct KeysendRequest { #[serde(alias = "destination")] - pub destination: Pubkey, + pub destination: PublicKey, #[serde(alias = "amount_msat")] pub amount_msat: Amount, #[serde(alias = "label", skip_serializing_if = "Option::is_none")] @@ -1051,7 +1051,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct DisconnectRequest { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "force", skip_serializing_if = "Option::is_none")] pub force: Option, } @@ -1104,7 +1104,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundchannelRequest { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "amount")] pub amount: AmountOrAll, #[serde(alias = "feerate", skip_serializing_if = "Option::is_none")] @@ -1142,7 +1142,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetrouteRequest { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "amount_msat")] pub amount_msat: Amount, #[serde(alias = "riskfactor")] @@ -1150,7 +1150,7 @@ pub mod requests { #[serde(alias = "cltv", skip_serializing_if = "Option::is_none")] pub cltv: Option, #[serde(alias = "fromid", skip_serializing_if = "Option::is_none")] - pub fromid: Option, + pub fromid: Option, #[serde(alias = "fuzzpercent", skip_serializing_if = "Option::is_none")] pub fuzzpercent: Option, #[serde(alias = "exclude", skip_serializing_if = "crate::is_none_or_empty")] @@ -1257,7 +1257,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct PingRequest { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "len", skip_serializing_if = "Option::is_none")] pub len: Option, #[serde(alias = "pongbytes", skip_serializing_if = "Option::is_none")] @@ -1412,7 +1412,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetinfoResponse { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "alias")] pub alias: String, #[serde(alias = "color")] @@ -1453,7 +1453,7 @@ pub mod responses { Response::Getinfo(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] @@ -1503,7 +1503,7 @@ pub mod responses { #[serde(alias = "log", skip_serializing_if = "Option::is_none")] pub log: Option, #[serde(alias = "node_id", skip_serializing_if = "Option::is_none")] - pub node_id: Option, + pub node_id: Option, #[serde(alias = "data", skip_serializing_if = "Option::is_none")] pub data: Option, } @@ -1762,7 +1762,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListpeersPeers { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "connected")] pub connected: bool, #[serde(alias = "log", skip_serializing_if = "crate::is_none_or_empty")] @@ -1791,7 +1791,7 @@ pub mod responses { Response::ListPeers(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Copy, Clone, Debug, Deserialize, Serialize)] @@ -1841,7 +1841,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListfundsChannels { #[serde(alias = "peer_id")] - pub peer_id: Pubkey, + pub peer_id: PublicKey, #[serde(alias = "our_amount_msat")] pub our_amount_msat: Amount, #[serde(alias = "amount_msat")] @@ -1875,7 +1875,7 @@ pub mod responses { Response::ListFunds(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// status of the payment (could be complete if already sent previously) @@ -1911,7 +1911,7 @@ pub mod responses { #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "completed_at", skip_serializing_if = "Option::is_none")] @@ -1940,15 +1940,15 @@ pub mod responses { Response::SendPay(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListchannelsChannels { #[serde(alias = "source")] - pub source: Pubkey, + pub source: PublicKey, #[serde(alias = "destination")] - pub destination: Pubkey, + pub destination: PublicKey, #[serde(alias = "short_channel_id")] pub short_channel_id: ShortChannelId, #[serde(alias = "public")] @@ -1991,7 +1991,7 @@ pub mod responses { Response::ListChannels(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2006,7 +2006,7 @@ pub mod responses { Response::AddGossip(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2027,7 +2027,7 @@ pub mod responses { Response::AutoCleanInvoice(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2035,7 +2035,7 @@ pub mod responses { #[serde(alias = "verified")] pub verified: bool, #[serde(alias = "pubkey")] - pub pubkey: Pubkey, + pub pubkey: PublicKey, } impl TryFrom for CheckmessageResponse { @@ -2046,7 +2046,7 @@ pub mod responses { Response::CheckMessage(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// Whether we successfully negotiated a mutual close, closed without them, or discarded not-yet-opened channel @@ -2090,7 +2090,7 @@ pub mod responses { Response::Close(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// Whether they initiated connection or we did @@ -2156,7 +2156,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ConnectResponse { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "features")] pub features: String, // Path `Connect.direction` @@ -2172,7 +2172,7 @@ pub mod responses { Response::Connect(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// Whether it has been paid, or can no longer be paid @@ -2238,7 +2238,7 @@ pub mod responses { Response::CreateInvoice(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2261,7 +2261,7 @@ pub mod responses { Response::Datastore(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2280,7 +2280,7 @@ pub mod responses { Response::CreateOnion(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2303,7 +2303,7 @@ pub mod responses { Response::DelDatastore(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2318,7 +2318,7 @@ pub mod responses { Response::DelExpiredInvoice(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// State of invoice @@ -2376,7 +2376,7 @@ pub mod responses { Response::DelInvoice(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2409,7 +2409,7 @@ pub mod responses { Response::Invoice(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -2438,7 +2438,7 @@ pub mod responses { Response::ListDatastore(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// Whether it's paid, unpaid or unpayable @@ -2510,7 +2510,7 @@ pub mod responses { Response::ListInvoices(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// status of the payment (could be complete if already sent previously) @@ -2544,7 +2544,7 @@ pub mod responses { #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "amount_sent_msat")] @@ -2571,7 +2571,7 @@ pub mod responses { Response::SendOnion(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// status of the payment @@ -2610,7 +2610,7 @@ pub mod responses { #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "amount_sent_msat")] @@ -2643,7 +2643,7 @@ pub mod responses { Response::ListSendPays(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// the purpose of this input (*EXPERIMENTAL_FEATURES* only) @@ -2802,7 +2802,7 @@ pub mod responses { Response::ListTransactions(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// status of payment @@ -2832,7 +2832,7 @@ pub mod responses { #[serde(alias = "payment_preimage")] pub payment_preimage: Secret, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "payment_hash")] pub payment_hash: Sha256, #[serde(alias = "created_at")] @@ -2858,7 +2858,7 @@ pub mod responses { Response::Pay(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// Type of connection @@ -2906,7 +2906,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ListnodesNodes { #[serde(alias = "nodeid")] - pub nodeid: Pubkey, + pub nodeid: PublicKey, #[serde(alias = "last_timestamp", skip_serializing_if = "Option::is_none")] pub last_timestamp: Option, #[serde(alias = "alias", skip_serializing_if = "Option::is_none")] @@ -2933,7 +2933,7 @@ pub mod responses { Response::ListNodes(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// Whether it's paid or expired @@ -2992,7 +2992,7 @@ pub mod responses { Response::WaitAnyInvoice(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// Whether it's paid or expired @@ -3051,7 +3051,7 @@ pub mod responses { Response::WaitInvoice(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// status of the payment @@ -3084,7 +3084,7 @@ pub mod responses { #[serde(alias = "amount_msat", skip_serializing_if = "Option::is_none")] pub amount_msat: Option, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "completed_at", skip_serializing_if = "Option::is_none")] @@ -3111,7 +3111,7 @@ pub mod responses { Response::WaitSendPay(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3130,7 +3130,7 @@ pub mod responses { Response::NewAddr(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3151,7 +3151,7 @@ pub mod responses { Response::Withdraw(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// status of payment @@ -3175,7 +3175,7 @@ pub mod responses { #[serde(alias = "payment_preimage")] pub payment_preimage: Secret, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "payment_hash")] pub payment_hash: Sha256, #[serde(alias = "created_at")] @@ -3201,7 +3201,7 @@ pub mod responses { Response::KeySend(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3242,7 +3242,7 @@ pub mod responses { Response::FundPsbt(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3261,7 +3261,7 @@ pub mod responses { Response::SendPsbt(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3278,7 +3278,7 @@ pub mod responses { Response::SignPsbt(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3319,7 +3319,7 @@ pub mod responses { Response::UtxoPsbt(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3338,7 +3338,7 @@ pub mod responses { Response::TxDiscard(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3359,7 +3359,7 @@ pub mod responses { Response::TxPrepare(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3380,7 +3380,7 @@ pub mod responses { Response::TxSend(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3395,7 +3395,7 @@ pub mod responses { Response::Disconnect(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3466,7 +3466,7 @@ pub mod responses { Response::Feerates(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3493,7 +3493,7 @@ pub mod responses { Response::FundChannel(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// The features understood by the destination node @@ -3515,7 +3515,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct GetrouteRoute { #[serde(alias = "id")] - pub id: Pubkey, + pub id: PublicKey, #[serde(alias = "channel")] pub channel: ShortChannelId, #[serde(alias = "direction")] @@ -3543,7 +3543,7 @@ pub mod responses { Response::GetRoute(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// still ongoing, completed, failed locally, or failed after forwarding @@ -3629,7 +3629,7 @@ pub mod responses { Response::ListForwards(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } /// status of the payment @@ -3662,7 +3662,7 @@ pub mod responses { #[serde(rename = "status")] pub status: ListpaysPaysStatus, #[serde(alias = "destination", skip_serializing_if = "Option::is_none")] - pub destination: Option, + pub destination: Option, #[serde(alias = "created_at")] pub created_at: u64, #[serde(alias = "completed_at", skip_serializing_if = "Option::is_none")] @@ -3697,7 +3697,7 @@ pub mod responses { Response::ListPays(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3714,7 +3714,7 @@ pub mod responses { Response::Ping(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3735,7 +3735,7 @@ pub mod responses { Response::SignMessage(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3750,7 +3750,7 @@ pub mod responses { Response::Stop(response) => Ok(response), _ => Err(TryFromResponseError) } - } + } } } diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 1dcc3e0ac694..4db602da1e44 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -8,6 +8,7 @@ use std::string::ToString; use bitcoin_hashes::Hash as BitcoinHash; pub use bitcoin_hashes::sha256::Hash as Sha256; +pub use secp256k1::PublicKey; #[derive(Copy, Clone, Serialize, Deserialize, Debug)] #[allow(non_camel_case_types)] @@ -77,57 +78,6 @@ impl Amount { } } -#[derive(Clone, Debug)] -pub struct Pubkey([u8; 33]); - -impl Serialize for Pubkey { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&hex::encode(&self.0)) - } -} - -impl<'de> Deserialize<'de> for Pubkey { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - use serde::de::Error; - let s: String = Deserialize::deserialize(deserializer)?; - Ok(Self::from_str(&s).map_err(|e| Error::custom(e.to_string()))?) - } -} - -impl FromStr for Pubkey { - type Err = crate::Error; - fn from_str(s: &str) -> Result { - let raw = - hex::decode(&s).with_context(|| format!("{} is not a valid hex-encoded pubkey", s))?; - - Ok(Pubkey(raw.try_into().map_err(|_| { - anyhow!("could not convert {} into pubkey", s) - })?)) - } -} -impl ToString for Pubkey { - fn to_string(&self) -> String { - hex::encode(self.0) - } -} -impl Pubkey { - pub fn from_slice(data: &[u8]) -> Result { - Ok(Pubkey( - data.try_into().with_context(|| "Not a valid pubkey")?, - )) - } - - pub fn to_vec(&self) -> Vec { - self.0.to_vec() - } -} - #[derive(Clone, Debug)] pub struct ShortChannelId(u64); @@ -612,7 +562,7 @@ impl Serialize for OutputDesc { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Routehop { - pub id: Pubkey, + pub id: PublicKey, pub scid: ShortChannelId, pub feebase: Amount, pub feeprop: u32, diff --git a/contrib/msggen/msggen/gen/grpc.py b/contrib/msggen/msggen/gen/grpc.py index def075955990..b88403fb0d58 100644 --- a/contrib/msggen/msggen/gen/grpc.py +++ b/contrib/msggen/msggen/gen/grpc.py @@ -302,8 +302,8 @@ def generate_composite(self, prefix, field: CompositeField): 'u16?': f'c.{name}.map(|v| v.into())', 'msat': f'Some(c.{name}.into())', 'msat?': f'c.{name}.map(|f| f.into())', - 'pubkey': f'c.{name}.to_vec()', - 'pubkey?': f'c.{name}.map(|v| v.to_vec())', + 'pubkey': f'c.{name}.serialize().to_vec()', + 'pubkey?': f'c.{name}.map(|v| v.serialize().to_vec())', 'hex': f'hex::decode(&c.{name}).unwrap()', 'hex?': f'c.{name}.map(|v| hex::decode(v).unwrap())', 'txid': f'hex::decode(&c.{name}).unwrap()', @@ -351,6 +351,7 @@ def generate(self, service: Service) -> None: use std::str::FromStr; use bitcoin_hashes::sha256::Hash as Sha256; use bitcoin_hashes::Hash; + use cln_rpc::primitives::PublicKey; """) @@ -420,8 +421,8 @@ def generate_composite(self, prefix, field: CompositeField) -> None: 'hex': f'hex::encode(&c.{name})', 'hex?': f'c.{name}.map(|v| hex::encode(v))', 'txid?': f'c.{name}.map(|v| hex::encode(v))', - 'pubkey': f'cln_rpc::primitives::Pubkey::from_slice(&c.{name}).unwrap()', - 'pubkey?': f'c.{name}.map(|v| cln_rpc::primitives::Pubkey::from_slice(&v[..]).unwrap())', + 'pubkey': f'PublicKey::from_slice(&c.{name}).unwrap()', + 'pubkey?': f'c.{name}.map(|v| PublicKey::from_slice(&v).unwrap())', 'msat': f'c.{name}.unwrap().into()', 'msat?': f'c.{name}.map(|a| a.into())', 'msat_or_all': f'c.{name}.unwrap().into()', diff --git a/contrib/msggen/msggen/gen/rust.py b/contrib/msggen/msggen/gen/rust.py index 88d0d6bd9371..f4f595602664 100644 --- a/contrib/msggen/msggen/gen/rust.py +++ b/contrib/msggen/msggen/gen/rust.py @@ -38,7 +38,7 @@ 'msat_or_all': 'AmountOrAll', 'msat_or_any': 'AmountOrAny', 'number': 'f64', - 'pubkey': 'Pubkey', + 'pubkey': 'PublicKey', 'short_channel_id': 'ShortChannelId', 'signature': 'String', 'string': 'String', From 09dfe3931dd0d14707b52d2b2f0189da3b5c0270 Mon Sep 17 00:00:00 2001 From: elsirion Date: Fri, 13 May 2022 21:51:13 +0000 Subject: [PATCH 1497/1530] Make eligible types `Copy` --- cln-rpc/src/primitives.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 4db602da1e44..0987fafdea02 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -78,7 +78,7 @@ impl Amount { } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct ShortChannelId(u64); impl Serialize for ShortChannelId { @@ -134,7 +134,7 @@ impl ShortChannelId { } } -#[derive(Clone, Debug)] +#[derive(Clone, Copy, Debug)] pub struct Secret([u8; 32]); impl TryFrom> for Secret { @@ -177,7 +177,7 @@ impl Secret { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Outpoint { pub txid: Sha256, pub outnum: u32, From 6abcb181457d1b52da8374ca714ff8ff0ba354c7 Mon Sep 17 00:00:00 2001 From: elsirion Date: Fri, 13 May 2022 22:23:34 +0000 Subject: [PATCH 1498/1530] Add basic arithmetic to `Amount` type --- cln-rpc/src/primitives.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cln-rpc/src/primitives.rs b/cln-rpc/src/primitives.rs index 0987fafdea02..ea6a03433d92 100644 --- a/cln-rpc/src/primitives.rs +++ b/cln-rpc/src/primitives.rs @@ -78,6 +78,26 @@ impl Amount { } } +impl std::ops::Add for Amount { + type Output = Amount; + + fn add(self, rhs: Self) -> Self::Output { + Amount { + msat: self.msat + rhs.msat + } + } +} + +impl std::ops::Sub for Amount { + type Output = Amount; + + fn sub(self, rhs: Self) -> Self::Output { + Amount { + msat: self.msat - rhs.msat + } + } +} + #[derive(Clone, Copy, Debug)] pub struct ShortChannelId(u64); From 657b315f1cb3c25c981d0cc9ca367a96ec547e09 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 23 Sep 2022 14:05:30 +0200 Subject: [PATCH 1499/1530] pyln: Bump versions to v0.12.1 This is just before the introduction of `get_json_id`, but has the correct dependency constraints such that all packages can be updated to >=v0.12 and we don't mix minor versions. --- contrib/pyln-client/pyln/client/__init__.py | 2 +- contrib/pyln-client/pyproject.toml | 4 ++-- contrib/pyln-proto/pyproject.toml | 2 +- contrib/pyln-testing/pyln/testing/__init__.py | 2 +- contrib/pyln-testing/pyproject.toml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contrib/pyln-client/pyln/client/__init__.py b/contrib/pyln-client/pyln/client/__init__.py index 382c32991fda..399ec41417a5 100644 --- a/contrib/pyln-client/pyln/client/__init__.py +++ b/contrib/pyln-client/pyln/client/__init__.py @@ -2,7 +2,7 @@ from .plugin import Plugin, monkey_patch, RpcException from .gossmap import Gossmap, GossmapNode, GossmapChannel, GossmapNodeId -__version__ = "0.12.0" +__version__ = "0.12.1" __all__ = [ "LightningRpc", diff --git a/contrib/pyln-client/pyproject.toml b/contrib/pyln-client/pyproject.toml index a9b5c25d2497..e9b742aad089 100644 --- a/contrib/pyln-client/pyproject.toml +++ b/contrib/pyln-client/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-client" -version = "0.12.0" +version = "0.12.1" description = "Client library and plugin library for Core Lightning" authors = ["Christian Decker "] license = "BSD-MIT" @@ -12,8 +12,8 @@ packages = [ [tool.poetry.dependencies] python = "^3.7" -pyln-bolt7 = "^1.0" pyln-proto = ">=0.12" +pyln-bolt7 = ">=1.0" [tool.poetry.dev-dependencies] pytest = "^7.0.1" diff --git a/contrib/pyln-proto/pyproject.toml b/contrib/pyln-proto/pyproject.toml index 0ccb597516f0..407dc5ab21ee 100644 --- a/contrib/pyln-proto/pyproject.toml +++ b/contrib/pyln-proto/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-proto" -version = "0.12.0" +version = "0.12.1" description = "This package implements some of the Lightning Network protocol in pure python. It is intended for protocol testing and some minor tooling only. It is not deemed secure enough to handle any amount of real funds (you have been warned!)." authors = ["Christian Decker "] license = "BSD-MIT" diff --git a/contrib/pyln-testing/pyln/testing/__init__.py b/contrib/pyln-testing/pyln/testing/__init__.py index 6466edc599df..882bcfc5cc3d 100644 --- a/contrib/pyln-testing/pyln/testing/__init__.py +++ b/contrib/pyln-testing/pyln/testing/__init__.py @@ -1,4 +1,4 @@ -__version__ = "0.12.0.post1" +__version__ = "0.12.1" __all__ = [ "__version__", diff --git a/contrib/pyln-testing/pyproject.toml b/contrib/pyln-testing/pyproject.toml index f27c1e009d04..d0037261ec15 100644 --- a/contrib/pyln-testing/pyproject.toml +++ b/contrib/pyln-testing/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyln-testing" -version = "0.12.0.post1" +version = "0.12.1" description = "Test your Core Lightning integration, plugins or whatever you want" authors = ["Christian Decker "] license = "BSD-MIT" @@ -17,7 +17,7 @@ ephemeral-port-reserve = "^1.1.4" psycopg2-binary = "^2.9.3" python-bitcoinlib = "^0.11.0" jsonschema = "^4.4.0" -pyln-client = "^0.12" +pyln-client = ">=0.12.1" Flask = "^2.0.3" cheroot = "^8.6.0" psutil = "^5.9.0" From 9023bd9334c16cc66d3731aa31f57818971d3fdc Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 27 Sep 2022 09:13:33 +0930 Subject: [PATCH 1500/1530] pytest: add test for migrations upgrade which breaks 'fees_collected_msat'. Signed-off-by: Rusty Russell --- tests/data/v0.12.1-forward.sqlite3.xz | Bin 0 -> 16640 bytes tests/test_db.py | 41 ++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/data/v0.12.1-forward.sqlite3.xz diff --git a/tests/data/v0.12.1-forward.sqlite3.xz b/tests/data/v0.12.1-forward.sqlite3.xz new file mode 100644 index 0000000000000000000000000000000000000000..47229a748f84fac1b72c4f71fa4723dce482acdc GIT binary patch literal 16640 zcmV(nK=Qx+H+ooF000E$*0e?f03iV!0000G&sfak|NlV0T>vSRMV(;C8Tck-h>j-y zF_rzthkyo`yX=3c36|17Q5D#AvfsINET@Ii3=q;wuzmi+H-$t$t@)MZ;J3E-;=YWgoRAS0IvXiJy ztDzPt5eQTEzcBaATi^vHXc1Pw-keM<)YyPkRlcPAy+QgTJK-~pGw%}3uw2fYl~{D$ zXh*Co6hYbH8bU9#239I3wO1jc26<$*gH?NWZ zpaqG+q%%H6#vRecKr3vwqs!S{FBa~cGk5$6igBl(S|9OfTw{F#Tp6NfRfN!G@J=Z% z$=4(uBBfx=rj-P+`Zxk;nzzp6-y{3;?dNiQP9N|^=5wWmYd)i^*u-H|Z&6Qlu7|Ja z{QT8sMWoFAC~%;D04mD$Q7d^B6U!kw4CxYOz7hRo1E@C}ouhJl>8kzCQdtYTyre5GjIZSvTW5c;!(!nl@ccWsehzL#bAJnFV6cHAZvE44w+xu z0tcB3S*~WWyIOL+qmi-VXhA~F=&K(1ZUM{=N~%e7tPE?V*A!sk=APojnl;jo+Tc9^ zse%_Gbqi6c@W7Ly;Pf%J_qYTRYP+17;zmTe6ibLK#oWPJ?CI6~yfJ|7LU@HC&xeEh zG6}kRg=--Hg_qSIj2>^gZl(%RP!z<*lN1vxRfE(~QtQ|$qV*@AsemI+o-9h2{uA=M zRTeo%*Idm@!a<%(wBABq*S3RE=cb`z?zzb&fV)fnH&&C;dR4m>CPw45AaLL=XOkN; z2~>6>;~KFl;u1F2Z3Z&vXoetVyvnLxr_23!ZFe?J8xKHP9*j0~<|S$2BGr#mk7Yh6 z+q6lxy~baClF*RWtOMgzI>fxpb=-pIkIMMqyim}0*%Lr%X6&tc4)3c4;ZtAed-zpN z72n)$J@P)74UixDuMWLZKN6JeqV~cRI_BkaNz&)whAZb@2w*vGiiKV1gIs=~L|hJVrhj zzzJ;cg2P~&e3EfxKZ{X5-B!A}bz5o?iyII1s5L2y zebb%@<@c((u3nTKvwgwm29P13atvfdkftZ>CH82M|7N5OXi3icW_HKh#NxgRFjWJ+ zgZ$TS%c?3XQt(_FFRA#@@4bPvw7344=AEl>8hlu8yRolKuT%_w%?0Sz$)hMxZ``1( zY*Wb*ftK=g7XIyrC~89%YDd*RvywT}_&}rM3C2$v@yr_jOv}+yAbF#D{4^u-DW!r& zl)nY8pyqIzHKo;uJ(GHs8%A@5YNl$2H&mh=!|Y^3kwvmJwIfXvrW3o?&Y@%sy8bU@ z4q7nAWTu~Rc~9$CgR{IlI8qSWQ+02bC@niPqs-xZWXuH6 zq{2V#;PgCb!91rMMeHIYRP3jKG&_QN6z@;Vn&G>ck8j0aS`zIb%Q*JvuTq>b6+rN; ziYo6Wr<@I`D1R2C)L?!E)x9cJ_ys(Uj|Da)+QZEiD773$q|G_%(>TZhbkCR;UD>Z~ zPkq^7;S65~CX;MBRx5}91SGpXa1Z^p#^C|b6FebhuX1T^ZmkK%e8*cmAma{ z3))kE%(G;B$#u>VlS?mby&pIAnSQ}RoB;L2eDuvtmB9Iut0t?ai;GSIyVGbBSHW(a zWfHvV{b!2*1_+fHLF?gp{5O47H~QhS=g&vLjCQ7g`9f%D4oqZq{;wDIBrutD-?I!+ zu@G<6n5VBGl*UdhwG(IGx5kiBDd8}k61cF5i+<(1MOM=Q*xT)UI_urKkoZ=~q7N8x z`)LfyJ93MYVDI}m<3gX0uJ9Z=+!z9kCbfPDum}c9Bj~i>)xXBb^`cld(epJ|Btxf< zE|NM#dHgKpC3>vJLKfyyHQ4^bVn|v{aO0sZ*5If{5WR*i`~HK*gdsq06n&%2el+b6 zr3prOux#0k;_yfabi5}@ea_28sCp>S64BSF(AOHT;>afUV719)PxiJxq9AF`eVQdg zFtEA7KG|bYR9GwdCO;4!9%@TFMOQAUPp_shdQH?2+pWzV>yz^uS>n<7s6wKOpwNJC zL2)7?zOb^R5xIb3g_O|Hunnh>bBqVxk=ZF_opEezG)KKvg2k~^YZE8A5(TlvFZ;CI z%v_4XmvH0`9Wa22^IGywbuWsf9L9#_+}0IsYv8Va-!qluP3T$_pPwT+3;Afs6d=p) zt-l|kqjDUK9>1kYqAGh%@OR$iyC99H3ms1 zG+(FJFeK(+k=f{U`O47}YM_szp9s z(iuN3(X;pHgKFC=$d+aBYn1gt8<@%e=k!T_4_JKLF=uMxaf3b1x?UCVI#Kpn$bD$L}1#Qjv@#6Maq4Mw63- zVDE9==*5#1I{$1=sFT77DpCibj@$p-7N8jOwsMTlK`F=zG z=0@lS!~ZQ1blm+xdF-7q=ZWq%!E9a7b)9xW#R3OmW8l#tX=3FJ;=y%;16;QVxPN#G z=*x;kuZzl3bhIts8z5D8I&SbaF>Pa)xueo=>wC}6R;6f{##YY;#O7H4#7%;Mn1`Dl zHdp~pf74;reUN?s1s{;>!7)P5nJ)@i$EFWNebna+YFslcnc58vY`hu)sqtFxTNsHB zZ`V!)>f93(Exd+bf>AQ8t;@Y^!G+iXnfIGuk-+#UT7SAdK8`E+WwjKAk4r!?!im6Z z0mL$_BiF(iWj^(`b$%zp_mFnZedKc}4D|aGIP?VE=8-X2tp%rtAA=XI=TA= zkow4+9jNz1!oGY^(ReE+$6LgCzoIyN$)UWp!@hz65Gm)Rx%eH#*z%h`95RhO4@LkK z-xd`N`&%HW$qoH_Qs*ZV@57Za-0 zZr{(oTbkvJ21noiaGFkmo@mLJ=T(~5HMHm(EG!NZD0bsf5YA^JVdH;`eO}2#{xtRM z6P-&{n|)G6#V}JVdJ6IcF2nGdfh>f#w%#hPjr#7wFsn{JV*h@Cl3aJyct-*na!2O; z8=An%gJ?VJ(u-^N&3v{O;pC>9WQRi(d3oi{qAa@K6`RhqkL{T(F`i$Mesd@hF;rxjQy+!2@R%qJ>O ztJ7s{+n|8=*7S?1XTn>!2xvO;(}M2E_1)@LJPvQ&v>;*=-$1Y0VK;?Tr1b=TTKC)O z+{E!a<#K-Yybaw)x*YM0?!#c%Q5Ehv<9rZwsKb7w?b5!NHoYjwd3LB`DI*me{v z=soCR?!{-NR=fcVPr64pTN**IaNQLXZQ2L{Cb8t(9hWkTM@WzmFI{g zYe#K-OU_YzG)bP`=&58q?&hq0U>wwh@kgtWR4@&}aAxX=ODYBIndt*9pLi7K6i{RP zvd8_tuG;ZA3x+$48fiR~czYhPkHDC!?z2EKd4;(4V-9fcM4&L1#vEck?xF;Yt)8<` zRPbFhY#igBij)Bc?tVVDIRah-9~kGN0iTm_hST@oZz~umTNGoL(h9BucWQDG;v(33 z@tu-VW@TS66YbB#tbxY+*8ziqmoo(sI|pU{JJ8wY7qrHV+k!&Rq-$+&E85-0M@^Ry zo2Jh)r6d70t>b4}K@f9zg%ixDwk_X!%K4?LKFV>_V@lP=|CW6-`-ky*MPo%A9stCf zkR64=k*TIz!7&g!Rt{>Y6GZl2TaZ%t3x;j#Hw&0<|k!vJz=+wtxKV^sd4wj2J_1d2{cg7XN)`p_o0!{n%HQL0%e_C00gwd!l{8{B(Vu?Js0lMJXZwBz**qq< zRE%L5jU5I3#OIG{1-Cn;#zsx+h1-%GVhRvZ3Y^Z+9Yv@-n1h!Q`F5+p1&(~?bH`s@ zWpHq@98eZnGliYnzJ3!fAGQDTQ};4N_JTB7bY2|{_iQGY`9prKp$B)xjbuv0?`b9p zvWm?n6rw>}VN?KmuDx0><7OYNIZ;S!7_LNw%Zf=QSm+3yJE^bY;BegNfGPo%sjfYF zGwjDQFpYUP?OLLG=!L%wM`6YO)5e;+-yLOP%2q@5d zo^R;xADE_d|3Dkz+5hZt?Mka?)r>2GyR8Nk{h%MVrI>^te6u=|K&0Roq1D!X5Eujm zZ_TD57E|&$#)3@+X@#|TlGQ_Sf#MlgG4knf085e?aOjCe!6>xazpq2jB8KaH2D2YE zd=nL2<|gBUjD6f2Tv`HZDPePgX6jgQJ0`qVi{RU%Wr5TetlqHpwH&K-c~zC(jtv_$ zv4oo#W@Okegcn%Sw%EqsopC%JzyWM6CP#zwbU2kXU#r?%l#M249t}D=bJ)4w0NN5$ z?W^gxQ*s=NH<#*nKbkH^`(PC~B6i7D!8iZx$l@Iy;juvSH*;wJ(*mEdhm+Dh}YevN2!Vh?&TnZ=)_`vr8 zVrAnw7Aw#hy@aqmC{TB4ANryzEPhs=+RX!z#|(9Vv3ZXL2sOyU))ESi3e0_i#LW1Y4EH=UwE-2UQd-}^qOdlJ;>~6g&SPe6*$_^MJ6$OJo zg&faz0ETfIaV&`}o?q_WyD-r#;O2LR(Y~djqcvd*9dt1EUY(-uc@D-3#;|2_iD6H7 z0bL;GS(nmF(rF2x4*@ty_W~oBh#(MwYO1y+N4on33IN9C?ymLZc@j7_JkSlNCsNF~ zt&dcu&_IR&@9w&_BHHhLnvfF>=SLfc!KDS+x=FV5X26tW-~FES78b6#5TqUQE>2vZ zk!8{+2CIXQ^1=f!peZMqOgNeft)iIKyxd!%AsW^V_dPiMJ|f6?9_}nOinRCeGovYK zfl+}(6w_m~>o~2T>l-bMW0|GOiXepb-*>pfc?j0H{*ok$nac&~m)x_<@LSAvuiWn^ z`^KGt-!j)tY_@FmBFw@?L1Dr6vB&iv3>X62p-#O11A7ed4`ZFKxQ@iE55`_UoybD` z!h5?Q6V?_|@FYt!`e}(Gsxd&eu-KvCX>iGq^ornOYi80WiN=4i^g?e9o} zIfL%ST_Q|{1O%pZ7--(?Q+8)u0%`xS1Umhx-=y13&!7xKHPyBT&IFK>8J zueW*<&yF-OfQopLPVUG^qMK#wHba%MnAw=CG1B(Z%%Nt6S}Ei{=n4Gx+hWrK+Q@)Q1dLhFmt2Y0Sr|4y!{1XeLCtLYQcg#Q84q%Q`=4@|2}GD=06!V;T%a`Z5#KLHgnfA<26;TE zJ;O*>SA^5P%w77$_c<&nk|NekQTYomTr{GV5+ul6Y(c##Hm>BzD!Wv@i!uL~2W!BI zu%*46Hm4X=XtOatRQoGwgscvMtC{v1WGTK4vw9}Ax)n|fi)tCKDi~LjaMnWU#TdnL z_`zN*g&g+^jE7ZqBv7ph$lzwM4khe|S4e#g2P(3@v~jV?*(-A?#8aNM^@J8YRnd34 zBh8^-RpH^Jho0uyJ2A0m^F${U-JJ@&ec-70*ZnK*PsTfxNL^ge#(0_CldQC@n z-{^XlR~d!_Y1r{`Q6spJ=PVRj@V!7{1VpCKuzBD_vTr9r=YSzR7RZa;fw|Yg$XmI= ztq=f9op!G?!4m{SY2vBMFxCwWn1Q9=9ZG^|>Ii%+zoZt|ooQ3lzbQQq>op>c>EAzc ziO*XUl>xp?;2qA7`PgoJforN}queSuIAqWUXHw!NhjwfMVq45-pGw228WUv{pRNP4 zaVPkz*fWVtM_n{IjBGo*J)kKBwor26+0Q_O2Rnl`gZ)FU!9S;A;=-;L~qNDxvH)Lz3JJwhV7>L`6_ zo0R5USUP|5>i==tT3i91*TkMkNqHW=6bclJpRw3SZmZWty*95@5y4gs=3Yboy7Frg_r#|RHpC$Nti1X2QQ)!63 zyH7AitSqb~#HA5HZfn0M^}15Zp0s9ygd>-Q;fbiAOBiU)Q>DtSgfGdS))3?SSR-aU6CA$n@zpeslqNJL!9wkaW69y~H%W>uk*>6) zrenSX(OZ6)_?%dpNTB93ouSt->`##A0#&J+Uc>_cSUvxIL*EVzR?;rOn(ltL*OInY zbIO{jFQi1`0njEv_FP=@P`IwOz&zcxjcen-&jdngmNoyZB%oZZPZ3o4JsD3&)R|XO zmB&@ksoM1ZDM=E+UGKc8xrR=sP)oD^++Z{m&~_{Buo9yTC6j?1kreMfpvB-?=UQgc_FH9YcQAr z$$}^{P4}7}G8VXdqmSS3zEQ4KVrw1gGa9x|HH&7MitFSchMuoANVX@Pi>|d7H7xuv z7E6d?!0Na#hm|#i7JIzf&?*Y^6v0$D(&`pNU#iW^EILloZ=H$6B{I-Do+NdrRqU4d z3RaStm%XH26C_mRXb>M2y2>a()5Wi#ZZxQ(NdBqYI6cD$YI*ROop)0=cxCAQQmr__ zmau3){S-Qva(;Dw?h1vt6oP;8+WS1ljzqATcdkxXocmJ8ov=2DI{N(^Kw<6$%wWE= zOc8{7){-9Hnktp_4ofi#Knl@8lPuQH@-axpt1+!{OlOrP9{pGll+!`r;j)Fqu6WsP z@mebLnIIi5IhIb zFzEjH&V+}CZmxv| z=Plgq@Kwa@q_XF1iv)rS^~fVNi9I)+!z=8Wi}g?AZEf$HKpTtEw>i=Fpzx>ooG@ce z7ulQeX{fe&Ui8uMrTYwBC@$lI$``065^lWDW*(D1;sc&K^COb|oaWo}-Ijt!jx4FP zg&aGW2=-*r=}OuWkWap1FPjgZD*>3D|n zA`Pa{YNH6t{$9~HvQV<=OE&KhoBd19Z5T~usf3WldOW(W(C8}OcLe4c4hBQV(o+T% zwklzqq8G;RwC9WJoAFe!ne|E6HRaD|Q}#p|W4-z2gWNNOf4-owPinl#0?@-q9kVcc zAbiMtc6B89#|>V)d2$VujWa)8f3Klf@lKW=9Yih_$!amOUH}!N62U$jc*2{W`eNm} z1kA~IQM$B05oZ!L8a%6)=Sz%;pr0Lg#Eq{Be>B*Uuz-DM_-(lz8mJHFupQb z+6YUwYH<+6XqDdb#&nwjnniS>StFQo^+36Ocw%*%bV^1Gr4%*3C@w!DF6Jd?iOO2Z z_=txQms0t%jM0K&dk)ggGt$P|r+lfWaLW@1Lq1#Q7&?LX#b#n=?#?r!a8Q~*1|s?% zc6sv}0F5PByU=CR>s=&%iT_;+te&?ge3_+3 zyG)91&^kuFP|{_CA=MY-SX;uh16_ISS)F;TopsVin*oAeK|lrG;;!@(Og72nk58i1 zrqrsC$NHdliG5ppkf2ke2$@(QxMT^3@5N_MQUc!Ol`v*6gPE_@zz>T8ZVJm7%o{>%~4bTa^Y7r)zPG+y=7wO1*kwxU|f}C zH87pe@t)rod4^VReo8t2pGJ$K@I2Un%{W9gJl_A=#r$mTnjuy(Ngm*6A``)3ZrFpVM{t+N9x)wXfVR%%R+*`* z7+RMhSURjy2CE?vxavmux7`9fF2bX1!U+hXCSo72b7&*U!Ms<0jpZLC9S&A=8 zps_r#ka=o9%+3*lAHrzs#x0`h3*ZBUmyx=fduv%5Dvym{Q;p5C8S9Xxt+tKC3e<#s z8Lj;FV@x5Wlhlf-K%k)kzpwda{%JS5ftmIUK|!p*PbRw)!+gHNp2vAcK$HOjA=03E zEWvF{$(AGBq*eR~%C{fZmzJkDl)P&&V2H5l>>VU|YCD zB;j`h2#$TSIVmNH?^<0wvB((Poh;Boksx3XtUeUvQbw!vqZ~sEC{Rj5mC@8xDR4cTYg7XOSoPo`f}33Mgg&$j85vw_-Xv_6+gCNom8$k)B%F zpUbK%f1PL$Kr2iwd_|Eu(L1)df&G&05=$HU4K^)exz@qa!SN^jX}A)6Vrt>X@an6zu3$F_rQ$i+USVj*J+3sz z6$xahG*UKE$*&7q@-6)=u)yy7#GvT`zaxR-MaVW8V1Gy2G`TS9T+AC6;BbPX(p2{@ zox9`mAF2~nCH2VkQ@*0=ko6*Ug$q#Kum$Yc39GU$U zUlD&~;Rx}nBC`5+ICQdbC7FZQNg8yG-&UonP8m!YIjBJB?*17}cAWHB-P`iTHJT02 z>Z$(-o;|r#$%QTDWPmq9r1-MBqaMuq4^!QB!_?Bq)&J~nae9twh8D%;_&X_9BbHdV zu$~DtW&suaO1~|D$K=+otrN&|%;}BeE>jcLw2TIs;TvzpC}Z+=dAcNoY$-O4Mov2k zdd>G$XSqSHhDGYkS97X>Ry})+q!uSAg^;{Rts7hCZ@%T3*3c{DX&bW0B{b7jkRbzo96wjjM+wzkI>z#Xe zKh|v4{q|I|a=!}+nNU)*IIU?6bnsXq5+X@E~iIOVK~? zooUR%Ic;A2TXc?6n8=;5!db--KzWJ!6kAg)>Bpe!(Ow2~szdS_RIW)k`X6ZbLI@G* zqb+hAiV|Nzy?kU@7BUUK9dT{3$RjkH)d0;!4B>#pHs$-jm@yvEv_-8DgyqIeuyvNx zyP`T7d8Pt&JS&yLc=hP!pdmtJtP7#Rxt0WaoNLm>c~;H?DwXQ&dD7(*qK4WQ2XS2z z#&03Fl^r%_M{GHmQ3h`+<5AAPkh2w(1JX*Gy^DE{DDa90!r&w_d6CNrNg6CdLi4)O z9DkuH4DS@nf=LRt|HDKhrxa58DN(&PBso`e;0=6rfPRmfw-Jm{`Kz@46<2w!7B!?~ zEbX8Td(BaQ9RJ4G|G!98?}MQhrH@m=2vR1+h-TV&S1TF)B|70r;Z!RYW+r`}wtb(= z$T?H2g1%N>!}65=aG0QIiIEvoF$8c|%D}q)Je=5Ve_VZL z9K9PK@4ODH5Rdecpc&s-%IOibx*8mCIaj=}GzB7|@_L(5Zh*(v$xs9l4WuF2%yH^M!LWnTQd94te(w%Z=zZ|w6tdL45s;>UT=$OB|5R2$gna) z1wx?Z%DP`hmAe-U2vCb`WeUIfJ1<>0eK#t#V%PzS+uVK(eG=vq)fiEsiimZ+sA3IlDZVkgR!=uXa!Ik#k0Py)25f*-N?LE&~z1z^dNHqhyi@$FaGA}1yFq+sMl+nN|5 z*>S4wMr)#eR5JJE8``j)7k6`gTLIu<5oxJlM!aa`msKH=?I+PiJ(AP~HQWJxs)9$J zVPsCJVORI&%^8YibQJq{R-#7CsE>P%%8C!>gAf81OTlb75GdYq!K&zwn%?Q<6d1K5 zC;pS@dV|q+nA+5)0IAa&(MPjsnrQjAAgi321IT>6-kV(48PJbz^DsH8!lQw~2R=hO zT8z3UDI$`9X`YF)$xYhAL1eCzIOc&50gUV6zj|PtfzGG^V$mTPiwIYCGfXO9iGM|9 zHW8@KXCJI?t-`@S@&X7mFz#hVh|zl}96{!Gv5Kpo@+nMRCXeyW>WM;}i~!Ht*R81%d}`71snclPC6PIGu(cXY7aI28KL|e zX`en<3MMmeHAjHF6#~^b4KR=(yk*?(PqtI&A_xzKp>pM2ib0qN>^2dyi`-*=m|>I- zn(yk~Q(Jut1%+oezb+z^f9MHHub9*!(kjf9vRsTOTOdxP9!iGW;^w>$8Kmh5m!?>P z)t}MIV3?9D8$5^Q*3M?F$J407OAXPC;#7yr5!M$8|NNxs!!16LbzVsgDu7jUyn;G8 zDU{#?cX^_`@dO(=50yDp+v8Yr_Sc8HJ5>S;eL?#RUd~DquUM(O%Uxh+Cg#FY$C_Ak|L7SHu#?%z0V#2)FwbM8XW(8i{r5ju& zx&rD513rz4n^a^3<(19I2Bu+sq?dB-y%=mtEYjHAqfzw1A85ClRAItZkR+fjNTI9% zBY8srf<}R-hoiId6BD?kd5@j`MVrG+qr;$>l9WFtf(< z;Jj*{|48}x0bE{KaYD@H#@I23=T-fO*?DILNR>h3ina_>9Xz}#;YAC~ep>WKRuSRFM_ zJz!A;W}6IEAvzIwg&nyCx{3v=<0KY~s(9L9<0^sSk0fPB)Y6Qg_U&z!+V}!hXKsVw5G^| zH@Q#qP5$bq`&xxPysa&2VIE(QKq(}uIAWyXu+G^|g6#&!9;!W>mHB%y@KRl4WeSt9 z`P!F!&X9`T^7KfW!e+T*#mc7LDXB_!4>@Tp*ScoQ5zOMPWxIj2OcGY20tg)FnoymbX)nNi4x}-qA6}UttUq!<$q#fZqqFR1yhiM^eJ6D@pSov|AHrs*9OnMMAJCYw6Xo$B{j#O+urx|4je&A zsX4X{%~HEz07wY^m$nLhx%5!T>tdDK2TkC+;vusR;%XpnF8F+K9nJodOx-}18;VPH zVTCvZHDBW*Us6sgeBc77d!Hg({zphIaBd_!uj|2~Wr**$bYq9r-|uIek@-Gz(8zvs z;M32KC{<03zpzyzsrA*JJpzw`Blg_X*>XgY{Bqm4m^m5V)kgwX2Ce+CJ+=MJ!eEsM zeMwS%*jUt~uS9A{)r9!v?4u<>tKD+J#q-|3egf{@XvD|auQ7I_vK_#9j6PcnXYbKp zo}{9)1wCI4|3C}B4n<@4?7DFYy#08v7j-Ml6cZqWGp(jN{C&L`uGUt+8c|-aqFTSh zf~%htlqEIr7M`iu#gz`R;k<77T-iZLxU$d!Ph@ACQ{KT|Z*Q~YX9@lK21aB8qh1q(bJ%lR}MWd%rg%> zS^3<&%q`-Wy9rf>k^`@ANa%k-ESLN~E>5+`Wr@Fi7c>v5({Xfq|6D^rA8>DmUkK#I z7v<<65KV^G0)ln9p|&G_@%UG^CE?*(r}jUKh(;7`jPGXBekO(3VR007Ig?1IgAmlf zaX!GvyQN3br^I2FVd!`Y-iY>x=zX~5aSSr_D*7p+XTx#n`yLbBsjVxq>OVLeFN?DZ zzp%lLM{!n6n1To}QyR2(F2WW|`(U`Q^Y5Ij6cqiPsgeS23lzG0^=K}8wYd+fC=jyS zZJFB&z#t(F(YF2GIN56}@MpGDhEmHyjrVq+9+AfNrA|jB-G$uktcvZv(06Nf4*9}}AvC*a8Wl-o9^}Gi^YT+&eo@m>!%{twGlpQj!zVhV z_puz6t+oq~(*x_Nvq+@1Sn%)iM*q}h#3zp;C{lel7Md~QDpUr+eCiU{TCA!|fYJM& zw=IEb4Lvf;#r3J1Y{Rx4>G0w=6w)^5V!*{goSyj8h35eyc+ zkB#_^a@~$is(spAeM)iqH?xj@Tz(!+o6vmZV|kSo@uYpNYX+A}U;PKOx?{HjBHYQG zpq$L%(sYvesilbTb{B)84*gQpun*Y58wQdQxJrCy)S0HEy#OUL7hRlX_=aj$jXcp5a z$QD`Yf}s#5yNRdDV$;_3S9`2_%C#5eT{=vg8;0-p^^hOR)x=5c(W-XlF zs%w{|JH>m-apzEsilAB@nyT^E+tESBf~7m@mR^uWv2r7o_5U)MYBqPAE!N4hcq0IAN0-ka$-74aj z_G z3dn(e4kr`VZ3Xh)Ob}9De&{=KL)tXuXMM_UocI|}i_MjXq{7i`RgItS%^%|CS>vA8 zx6-7jWZe?2$F@PYeWaCg@=kBKq3#`~FWpLn_`D)qHt?5KI}iCK8W#Z7DuvzWA*R9k z(Z!gy70H2CFvew&O)GzsSd_(&?-}hT-(XM_*EilPCdwfyPLgUC@?_yE2uVGl>0Afv z;o5QWWZ3+g`+=EXxBQ1ps6-|8Wtfu!(OAVY^~lFx3n-U|KtDa_pTZzpx8&@W5+7ZASuJP|=e z%S~ZZv0wiMa|1MgsN~$kjG8|$@7!ZAUKL?NpLNi+xuLqtENW(^aiYhax9JzJl9t5B zWVmx)kHIx17M0uii_u^E(sA+8@V+!^Du*U4@95Kmu6I zVM-#+QJutX0rS^NjNiwA^RHGCmx8duDQ!NPk!^ZyA|q+a=uc|s&0+*626tzF$IrN!5y5w>RzUQuzTr{eh`EG1Re_3|n$50m3c4Xt&HNMy0g^{i8OmlxfQ? zkrU=9oZZR@q1+gyQMsDo2`%~rG`uJyH%ai~@Q=KqRR8Uw<-asLrOn!@4iQ=~N`f36{H}_MLhqOL)oZ zwywW3GdV4EAlr>Jb|Rozi05aQ)y!KNQ!hWK)JIBrg-&TtiX@2|L$>ZJ8srZ~jm@JQqvwI4|vZn<4v0hyD+bDSpG zLrLN06_!5XqBD+c8RdWc7DVumlJ2-9oH*e>89C z-j>`^+IIf+JI2+x-qVAsa7@sdxbU~)7|2>}^Wcb#)~KVNH?1SPix8kqQFF}a8Y8TK zcmQ0ZrccwMgY%SV<5T5%RST-YFw7jLZ@pVXQ*AKZ>cnNW9{4agzToj@A;t~+c%Ue% z<=|U=u(9EoiFPvMfNC|K zwv3JU%43*vu2cW3{oD06(1X>3SZ&oLQ*uT5E6VxG0?!%%;xu_MsI7n=@Av%quXz1D zCmwtgDN;-?Nzt^0q|x^X5)A@#oj3HpshLO3Wb_yVnuju_V|!WaVr7A1k!B2^J{lxe zTfJ)z9y;z&%6H~1ir8*n*Ba}rZv`FH6Nhwd=?PWQpHCM>*01~+GWl9zGAZI(gzZHi z9+TP7DOs8vDt7puCuJ8fZv)fS%p2Pc@E>l;_yZ@ml!LG3h2jP$!rfV@wPI9i{3<5N zWVDp&4~`d*3+3>>EPqv)@AHsHbl|^P;xz+N%k)~7^qX3SPIU`sJK%(-$;|Q<3W@s^ zU@ksMP8?|}xuWAwPxlz_Tm*I?4(*x*QT<_BK3d=mv&^n4J1bbzz?+aa**#u*$X%T1y(^AfZ8 z+n$a>TvUQ36&91v-cef#JI-OAQk~NzTkF$icjlzp^p$GaOtdHM_gHC?^JV=$Dm`*k zV7}ILm1r`Ex_7}tXVxPLibcf_GRMdf{jclD^cEpUWw|QYlRu{(8)8t(Xl2Av2hFV2 zIoxP4A=GR(Tztn6tzh()75_wZo~WCaDP>ZFjI|hP+^V`05=PI{#F=JzDG49FY!pw!-_U z*@u__X1}Bski)DIt+N>|^@wuw&p9oy5AWo+`=<6ir47QVBf7^x4~!9)@`7#tD(;H<}33QBBO`Tugxb2AMkoBvDDRp2=7O&lgP#e2){t|&w!UP zRQrq#M;+yBu5lMMT8$*U!!b0d?F})dsU>3ml`m(uWgC?c8Zwc};^Kdp=fA>-`px-7dudWrL)%cO zaMpNPfqTkgwRqYP2Fi~%CDNjzR^YF;bvGe1dgq* zb$`(J)4;jz*O;#agu35*yI4AKZusHBFJz!1vA5o$+fF(qR(1125s1NKjrIik`xrkK zr0Fr)S*oKj126PkAI`Ld+LXtU0B!JD6%(j*U@9X6GB*Nks(zWg&_tl-|K>U$v6%d;+Q zV0d;&+3#1FFqxu;;PP&%I$Et$uths{#F?&SZN2~iG|ra*tZR~J00G;90f2xI*;SWF PvBYQl0ssI200dcDj*FZN literal 0 HcmV?d00001 diff --git a/tests/test_db.py b/tests/test_db.py index 414bc066c89e..c539c270ec81 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -467,3 +467,44 @@ def test_db_sanity_checks(bitcoind, node_factory): # Will have exited with non-zero status. assert l1.daemon.proc.wait(TIMEOUT) != 0 assert l1.daemon.is_in_stderr('Wallet sanity check failed') + + +@pytest.mark.xfail(strict=True) +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Canned db used") +@unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete db") +@unittest.skipIf(TEST_NETWORK != 'regtest', "The DB migration is network specific due to the chain var.") +def test_db_forward_migrate(bitcoind, node_factory): + # For posterity, here is how I generated the db, in v0.12.1: + # l1, l2, l3, l4, l5 = node_factory.get_nodes(5) + + # node_factory.join_nodes([l2, l1, l3], True, FUNDAMOUNT, True, True) + # node_factory.join_nodes([l4, l1, l5], True, FUNDAMOUNT, True, True) + + # # Both ends remembered + # l2.rpc.pay(l3.rpc.invoice(10000, 'test_db_forward_migrate', 'test_db_forward_migrate')['bolt11']) + + # # Both ends forgotten + # l4.rpc.pay(l5.rpc.invoice(10000, 'test_db_forward_migrate', 'test_db_forward_migrate')['bolt11']) + + # # Outgoing forgotten + # l2.rpc.pay(l5.rpc.invoice(10000, 'test_db_forward_migrate2', 'test_db_forward_migrate2')['bolt11']) + + # # Incoming forgotten + # l4.rpc.pay(l3.rpc.invoice(10000, 'test_db_forward_migrate2', 'test_db_forward_migrate2')['bolt11']) + + # time.sleep(5) + # l4.rpc.close(l1.info['id']) + # l5.rpc.close(l1.info['id']) + # bitcoind.generate_block(100, wait_for_mempool=2) + # l4.rpc.disconnect(l1.info['id']) + # l5.rpc.disconnect(l1.info['id']) + + # wait_for(lambda: l1.rpc.listpeers(l4.info['id'])['peers'] == []) + # wait_for(lambda: l1.rpc.listpeers(l5.info['id'])['peers'] == []) + # assert False + bitcoind.generate_block(113) + l1 = node_factory.get_node(dbfile='v0.12.1-forward.sqlite3.xz', + options={'database-upgrade': True}) + + assert l1.rpc.getinfo()['fees_collected_msat'] == 4 + assert len(l1.rpc.listforwards()['forwards']) == 4 From cafa1a8c65117833d70cf7a1f000b41605abe1f6 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 27 Sep 2022 09:13:35 +0930 Subject: [PATCH 1501/1530] db: correctly migrate forwards for closed incoming channels. We have to allow them (as otherwise `fees_collected_msat` in getinfo breaks), but it means that actually, in_htlc_id might be missing in listforwards (also, out_htlc_id might be missing, which we didn't catch before). Signed-off-by: Rusty Russell Fixes: #5628 --- Makefile | 2 +- cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 4 +- contrib/pyln-testing/pyln/testing/node_pb2.py | 60 +++++++++---------- doc/lightning-listforwards.7.md | 6 +- doc/schemas/listforwards.schema.json | 6 +- lightningd/peer_htlcs.c | 6 +- tests/test_db.py | 1 - wallet/db.c | 4 +- wallet/wallet.c | 9 +++ 11 files changed, 55 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index 54913ec021bc..bc7983ec17c5 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ endif ifeq ($(COMPAT),1) # We support compatibility with pre-0.6. -COMPAT_CFLAGS=-DCOMPAT_V052=1 -DCOMPAT_V060=1 -DCOMPAT_V061=1 -DCOMPAT_V062=1 -DCOMPAT_V070=1 -DCOMPAT_V072=1 -DCOMPAT_V073=1 -DCOMPAT_V080=1 -DCOMPAT_V081=1 -DCOMPAT_V082=1 -DCOMPAT_V090=1 -DCOMPAT_V0100=1 +COMPAT_CFLAGS=-DCOMPAT_V052=1 -DCOMPAT_V060=1 -DCOMPAT_V061=1 -DCOMPAT_V062=1 -DCOMPAT_V070=1 -DCOMPAT_V072=1 -DCOMPAT_V073=1 -DCOMPAT_V080=1 -DCOMPAT_V081=1 -DCOMPAT_V082=1 -DCOMPAT_V090=1 -DCOMPAT_V0100=1 -DCOMPAT_V0121=1 endif # (method=thread to support xdist) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index fcbea785fa6c..0f773afc5312 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1229,7 +1229,7 @@ message ListforwardsForwards { TLV = 1; } string in_channel = 1; - uint64 in_htlc_id = 10; + optional uint64 in_htlc_id = 10; Amount in_msat = 2; ListforwardsForwardsStatus status = 3; double received_time = 4; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 538d5b4194fc..bdfbf503a8af 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -900,7 +900,7 @@ impl From for pb::ListforwardsForwards { fn from(c: responses::ListforwardsForwards) -> Self { Self { in_channel: c.in_channel.to_string(), // Rule #2 for type short_channel_id - in_htlc_id: c.in_htlc_id, // Rule #2 for type u64 + in_htlc_id: c.in_htlc_id, // Rule #2 for type u64? in_msat: Some(c.in_msat.into()), // Rule #2 for type msat status: c.status as i32, received_time: c.received_time, // Rule #2 for type number diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index a577f2480a51..b63f6c16e3a4 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -3594,8 +3594,8 @@ pub mod responses { pub struct ListforwardsForwards { #[serde(alias = "in_channel")] pub in_channel: ShortChannelId, - #[serde(alias = "in_htlc_id")] - pub in_htlc_id: u64, + #[serde(alias = "in_htlc_id", skip_serializing_if = "Option::is_none")] + pub in_htlc_id: Option, #[serde(alias = "in_msat")] pub in_msat: Amount, // Path `ListForwards.forwards[].status` diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index aa2f78030f3b..5afaed222aa2 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xca\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x12\n\nin_htlc_id\x18\n \x01(\x04\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x01\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x02\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1368,33 +1368,33 @@ _LISTFORWARDSRESPONSE._serialized_start=26286 _LISTFORWARDSRESPONSE._serialized_end=26353 _LISTFORWARDSFORWARDS._serialized_start=26356 - _LISTFORWARDSFORWARDS._serialized_end=26942 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26740 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26824 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26826 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26874 - _LISTPAYSREQUEST._serialized_start=26945 - _LISTPAYSREQUEST._serialized_end=27164 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27070 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27125 - _LISTPAYSRESPONSE._serialized_start=27166 - _LISTPAYSRESPONSE._serialized_end=27217 - _LISTPAYSPAYS._serialized_start=27220 - _LISTPAYSPAYS._serialized_end=27739 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27551 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27610 - _PINGREQUEST._serialized_start=27741 - _PINGREQUEST._serialized_end=27830 - _PINGRESPONSE._serialized_start=27832 - _PINGRESPONSE._serialized_end=27862 - _SIGNMESSAGEREQUEST._serialized_start=27864 - _SIGNMESSAGEREQUEST._serialized_end=27901 - _SIGNMESSAGERESPONSE._serialized_start=27903 - _SIGNMESSAGERESPONSE._serialized_end=27973 - _STOPREQUEST._serialized_start=27975 - _STOPREQUEST._serialized_end=27988 - _STOPRESPONSE._serialized_start=27990 - _STOPRESPONSE._serialized_end=28004 - _NODE._serialized_start=28007 - _NODE._serialized_end=30935 + _LISTFORWARDSFORWARDS._serialized_end=26962 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26745 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26829 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26831 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26879 + _LISTPAYSREQUEST._serialized_start=26965 + _LISTPAYSREQUEST._serialized_end=27184 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27090 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27145 + _LISTPAYSRESPONSE._serialized_start=27186 + _LISTPAYSRESPONSE._serialized_end=27237 + _LISTPAYSPAYS._serialized_start=27240 + _LISTPAYSPAYS._serialized_end=27759 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27571 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27630 + _PINGREQUEST._serialized_start=27761 + _PINGREQUEST._serialized_end=27850 + _PINGRESPONSE._serialized_start=27852 + _PINGRESPONSE._serialized_end=27882 + _SIGNMESSAGEREQUEST._serialized_start=27884 + _SIGNMESSAGEREQUEST._serialized_end=27921 + _SIGNMESSAGERESPONSE._serialized_start=27923 + _SIGNMESSAGERESPONSE._serialized_end=27993 + _STOPREQUEST._serialized_start=27995 + _STOPREQUEST._serialized_end=28008 + _STOPRESPONSE._serialized_start=28010 + _STOPRESPONSE._serialized_end=28024 + _NODE._serialized_start=28027 + _NODE._serialized_end=30955 # @@protoc_insertion_point(module_scope) diff --git a/doc/lightning-listforwards.7.md b/doc/lightning-listforwards.7.md index d4b7cefe4ba8..87c5b723c899 100644 --- a/doc/lightning-listforwards.7.md +++ b/doc/lightning-listforwards.7.md @@ -25,12 +25,12 @@ RETURN VALUE On success, an object containing **forwards** is returned. It is an array of objects, where each object contains: - **in\_channel** (short\_channel\_id): the channel that received the HTLC -- **in\_htlc\_id** (u64): the unique HTLC id the sender gave this - **in\_msat** (msat): the value of the incoming HTLC - **status** (string): still ongoing, completed, failed locally, or failed after forwarding (one of "offered", "settled", "local_failed", "failed") - **received\_time** (number): the UNIX timestamp when this was received +- **in\_htlc\_id** (u64, optional): the unique HTLC id the sender gave this (not present if incoming channel was closed before ugprade to v22.11) - **out\_channel** (short\_channel\_id, optional): the channel that the HTLC (trying to) forward to -- **out\_htlc\_id** (u64, optional): the unique HTLC id we gave this when sending +- **out\_htlc\_id** (u64, optional): the unique HTLC id we gave this when sending (may be missing even if out_channel is present, for old forwards before v22.11) - **style** (string, optional): Either a legacy onion format or a modern tlv format (one of "legacy", "tlv") If **out\_msat** is present: @@ -64,4 +64,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:5b2da52b7f3a28563d0103d3853b9d8f717dc41a9e9c6b395ff19f1b975ca5fd) +[comment]: # ( SHA256STAMP:15bf997ae8e93ab28b0084d9cc45fc80fb18b2bcf705f690f77617f0b66b069d) diff --git a/doc/schemas/listforwards.schema.json b/doc/schemas/listforwards.schema.json index f133c2dabbb8..9726a5dc34f1 100644 --- a/doc/schemas/listforwards.schema.json +++ b/doc/schemas/listforwards.schema.json @@ -14,7 +14,6 @@ "required": [ "in_channel", "in_msat", - "in_htlc_id", "status", "received_time" ], @@ -25,7 +24,7 @@ }, "in_htlc_id": { "type": "u64", - "description": "the unique HTLC id the sender gave this" + "description": "the unique HTLC id the sender gave this (not present if incoming channel was closed before ugprade to v22.11)" }, "in_msatoshi": { "deprecated": true @@ -54,7 +53,7 @@ }, "out_htlc_id": { "type": "u64", - "description": "the unique HTLC id we gave this when sending" + "description": "the unique HTLC id we gave this when sending (may be missing even if out_channel is present, for old forwards before v22.11)" }, "style": { "type": "string", @@ -77,7 +76,6 @@ "required": [ "fee_msat", "out_msat", - "out_htlc_id", "out_channel" ], "properties": { diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 043510208a1a..16625d1726ff 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2844,7 +2844,11 @@ void json_add_forwarding_object(struct json_stream *response, if (payment_hash) json_add_sha256(response, "payment_hash", payment_hash); json_add_short_channel_id(response, "in_channel", &cur->channel_in); - json_add_u64(response, "in_htlc_id", cur->htlc_id_in); + +#ifdef COMPAT_V0121 + if (cur->htlc_id_in != HTLC_INVALID_ID) +#endif + json_add_u64(response, "in_htlc_id", cur->htlc_id_in); /* This can be unknown if we failed before channel lookup */ if (cur->channel_out.u64 != 0) { diff --git a/tests/test_db.py b/tests/test_db.py index c539c270ec81..c08f5daeb655 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -469,7 +469,6 @@ def test_db_sanity_checks(bitcoind, node_factory): assert l1.daemon.is_in_stderr('Wallet sanity check failed') -@pytest.mark.xfail(strict=True) @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Canned db used") @unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete db") @unittest.skipIf(TEST_NETWORK != 'regtest', "The DB migration is network specific due to the chain var.") diff --git a/wallet/db.c b/wallet/db.c index 9c56412d660f..3146d03829a7 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -922,9 +922,7 @@ static struct migration dbmigrations[] = { ", resolved_time" ", failcode" ", forward_style" - " FROM forwarded_payments" - " WHERE" - " in_htlc_id IS NOT NULL"), NULL}, + " FROM forwarded_payments"), NULL}, {SQL("DROP INDEX forwarded_payments_state;"), NULL}, {SQL("DROP INDEX forwarded_payments_out_htlc_id;"), NULL}, {SQL("DROP TABLE forwarded_payments;"), NULL}, diff --git a/wallet/wallet.c b/wallet/wallet.c index a5f6b81a6f57..5bd67b03b202 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4635,7 +4635,16 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, } db_col_scid(stmt, "in_channel_scid", &cur->channel_in); + +#ifdef COMPAT_V0121 + /* This can happen due to migration! */ + if (!db_col_is_null(stmt, "in_htlc_id")) + cur->htlc_id_in = db_col_u64(stmt, "in_htlc_id"); + else + cur->htlc_id_in = HTLC_INVALID_ID; +#else cur->htlc_id_in = db_col_u64(stmt, "in_htlc_id"); +#endif if (!db_col_is_null(stmt, "out_channel_scid")) { db_col_scid(stmt, "out_channel_scid", &cur->channel_out); From 6eac8dfe3c70c3b7b94cce434807b8b7fed9a920 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 27 Sep 2022 09:13:36 +0930 Subject: [PATCH 1502/1530] delforward: tally up deleted forwards so that getinfo's `fees_collected_msat` doesn't change. Signed-off-by: Rusty Russell Fixes: #5627 --- tests/test_plugin.py | 5 +++++ wallet/wallet.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index e463144196e4..f75b4fcc1484 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -3089,6 +3089,8 @@ def test_autoclean(node_factory): assert l2.rpc.autoclean_status()['autoclean']['failedforwards']['cleaned'] == 1 assert l2.rpc.autoclean_status()['autoclean']['succeededforwards']['cleaned'] == 0 + amt_before = l2.rpc.getinfo()['fees_collected_msat'] + # Clean succeeded ones l2.stop() l2.daemon.opts['autoclean-succeededforwards-age'] = 2 @@ -3098,6 +3100,9 @@ def test_autoclean(node_factory): assert l2.rpc.autoclean_status()['autoclean']['failedforwards']['cleaned'] == 1 assert l2.rpc.autoclean_status()['autoclean']['succeededforwards']['cleaned'] == 1 + # We still see correct total in getinfo! + assert l2.rpc.getinfo()['fees_collected_msat'] == amt_before + def test_autoclean_once(node_factory): l1, l2, l3 = node_factory.line_graph(3, opts={'may_reconnect': True}, diff --git a/wallet/wallet.c b/wallet/wallet.c index 5bd67b03b202..452154e7600a 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4506,7 +4506,7 @@ void wallet_forwarded_payment_add(struct wallet *w, const struct htlc_in *in, struct amount_msat wallet_total_forward_fees(struct wallet *w) { struct db_stmt *stmt; - struct amount_msat total; + struct amount_msat total, deleted; bool res; stmt = db_prepare_v2(w->db, SQL("SELECT" @@ -4522,6 +4522,12 @@ struct amount_msat wallet_total_forward_fees(struct wallet *w) db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)", &total); tal_free(stmt); + deleted = amount_msat(db_get_intvar(w->db, "deleted_forward_fees", 0)); + if (!amount_msat_add(&total, total, deleted)) + db_fatal("Adding forward fees %s + %s overflowed", + type_to_string(tmpctx, struct amount_msat, &total), + type_to_string(tmpctx, struct amount_msat, &deleted)); + return total; } @@ -4694,6 +4700,32 @@ bool wallet_forward_delete(struct wallet *w, struct db_stmt *stmt; bool changed; + /* When deleting settled ones, we have to add to deleted_forward_fees! */ + if (state == FORWARD_SETTLED) { + /* Of course, it might not be settled: don't add if they're wrong! */ + stmt = db_prepare_v2(w->db, SQL("SELECT" + " in_msatoshi - out_msatoshi" + " FROM forwards " + " WHERE in_channel_scid = ?" + " AND in_htlc_id = ?" + " AND state = ?;")); + db_bind_scid(stmt, 0, chan_in); + db_bind_u64(stmt, 1, htlc_id); + db_bind_int(stmt, 2, wallet_forward_status_in_db(FORWARD_SETTLED)); + db_query_prepared(stmt); + + if (db_step(stmt)) { + struct amount_msat deleted; + + db_col_amount_msat(stmt, "in_msatoshi - out_msatoshi", &deleted); + deleted.millisatoshis += /* Raw: db access */ + db_get_intvar(w->db, "deleted_forward_fees", 0); + db_set_intvar(w->db, "deleted_forward_fees", + deleted.millisatoshis); /* Raw: db access */ + } + tal_free(stmt); + } + stmt = db_prepare_v2(w->db, SQL("DELETE FROM forwards" " WHERE in_channel_scid = ?" From 68f15f17bb35919ca6a32f3cc95041008135c8a9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 27 Sep 2022 09:13:36 +0930 Subject: [PATCH 1503/1530] delforward: allow deletion of "unknown in_htlc_id" and fix autoclean to use it. Note the caveats: we will delete *all* of them at once! Signed-off-by: Rusty Russell --- doc/lightning-delforward.7.md | 5 +++ doc/lightningd-config.5.md | 5 +++ lightningd/peer_htlcs.c | 8 ++++- plugins/autoclean.c | 10 +++++- tests/test_db.py | 7 +++++ wallet/wallet.c | 59 ++++++++++++++++++++++++----------- wallet/wallet.h | 2 +- 7 files changed, 74 insertions(+), 22 deletions(-) diff --git a/doc/lightning-delforward.7.md b/doc/lightning-delforward.7.md index 5ae6ee51b7e0..c8ee5e2f33e9 100644 --- a/doc/lightning-delforward.7.md +++ b/doc/lightning-delforward.7.md @@ -20,6 +20,11 @@ has no effect on the running of your node. You cannot delete forwards which have status *offered* (i.e. are currently active). +Note: for **listforwards** entries without an *in_htlc_id* entry (no +longer created in v22.11, but can exist from older versions), a value +of 18446744073709551615 can be used, but then it will delete *all* +entries without *in_htlc_id* for this *in_channel* and *status*. + RETURN VALUE ------------ diff --git a/doc/lightningd-config.5.md b/doc/lightningd-config.5.md index 61c038b823aa..d0ce63b2b532 100644 --- a/doc/lightningd-config.5.md +++ b/doc/lightningd-config.5.md @@ -461,6 +461,11 @@ accepted, and ignored. How old invoices which were not paid (and cannot be) (`expired` in listinvoices `status`) before deletion (default 0, meaning never). +Note: prior to v22.11, forwards for channels which were closed were +not easily distinguishable. As a result, autoclean may delete more +than one of these at once, and then suffer failures when it fails to +delete the others. + ### Payment control options: * **disable-mpp** [plugin `pay`] diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 16625d1726ff..25ca2f04998d 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -3010,8 +3010,14 @@ static struct command_result *json_delforward(struct command *cmd, NULL)) return command_param_failed(); +#ifdef COMPAT_V0121 + /* Special value used if in_htlc_id is missing */ + if (*htlc_id == HTLC_INVALID_ID) + htlc_id = NULL; +#endif + if (!wallet_forward_delete(cmd->ld->wallet, - chan_in, *htlc_id, *status)) + chan_in, htlc_id, *status)) return command_fail(cmd, DELFORWARD_NOT_FOUND, "Could not find that forward"); diff --git a/plugins/autoclean.c b/plugins/autoclean.c index 1f3de4e8cc02..81e50352afd0 100644 --- a/plugins/autoclean.c +++ b/plugins/autoclean.c @@ -345,7 +345,15 @@ static struct command_result *listforwards_done(struct command *cmd, req = del_request_start("delforward", cinfo, subsys); json_add_tok(req->js, "in_channel", inchan, buf); - json_add_tok(req->js, "in_htlc_id", inid, buf); + /* This can be missing if it was a forwards record from an old + * closed channel in version <= 0.12.1. This is a special value + * but we will delete them *all*, resulting in some failures! */ +#ifdef COMPAT_V0121 + if (!inid) + json_add_u64(req->js, "in_htlc_id", -1ULL); + else +#endif + json_add_tok(req->js, "in_htlc_id", inid, buf); json_add_tok(req->js, "status", status, buf); send_outreq(plugin, req); } diff --git a/tests/test_db.py b/tests/test_db.py index c08f5daeb655..8b1ac2969d4b 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -507,3 +507,10 @@ def test_db_forward_migrate(bitcoind, node_factory): assert l1.rpc.getinfo()['fees_collected_msat'] == 4 assert len(l1.rpc.listforwards()['forwards']) == 4 + + # Make sure autoclean can handle these! + l1.stop() + l1.daemon.opts['autoclean-succeededforwards-age'] = 2 + l1.daemon.opts['autoclean-cycle'] = 1 + l1.start() + wait_for(lambda: l1.rpc.listforwards()['forwards'] == []) diff --git a/wallet/wallet.c b/wallet/wallet.c index 452154e7600a..bb07fbd558c9 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -4694,7 +4694,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, bool wallet_forward_delete(struct wallet *w, const struct short_channel_id *chan_in, - u64 htlc_id, + const u64 *htlc_id, enum forward_status state) { struct db_stmt *stmt; @@ -4703,21 +4703,32 @@ bool wallet_forward_delete(struct wallet *w, /* When deleting settled ones, we have to add to deleted_forward_fees! */ if (state == FORWARD_SETTLED) { /* Of course, it might not be settled: don't add if they're wrong! */ - stmt = db_prepare_v2(w->db, SQL("SELECT" - " in_msatoshi - out_msatoshi" - " FROM forwards " - " WHERE in_channel_scid = ?" - " AND in_htlc_id = ?" - " AND state = ?;")); - db_bind_scid(stmt, 0, chan_in); - db_bind_u64(stmt, 1, htlc_id); - db_bind_int(stmt, 2, wallet_forward_status_in_db(FORWARD_SETTLED)); + if (htlc_id) { + stmt = db_prepare_v2(w->db, SQL("SELECT" + " CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)" + " FROM forwards " + " WHERE in_channel_scid = ?" + " AND in_htlc_id = ?" + " AND state = ?;")); + db_bind_scid(stmt, 0, chan_in); + db_bind_u64(stmt, 1, *htlc_id); + db_bind_int(stmt, 2, wallet_forward_status_in_db(FORWARD_SETTLED)); + } else { + stmt = db_prepare_v2(w->db, SQL("SELECT" + " CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)" + " FROM forwards " + " WHERE in_channel_scid = ?" + " AND in_htlc_id IS NULL" + " AND state = ?;")); + db_bind_scid(stmt, 0, chan_in); + db_bind_int(stmt, 1, wallet_forward_status_in_db(FORWARD_SETTLED)); + } db_query_prepared(stmt); if (db_step(stmt)) { struct amount_msat deleted; - db_col_amount_msat(stmt, "in_msatoshi - out_msatoshi", &deleted); + db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)", &deleted); deleted.millisatoshis += /* Raw: db access */ db_get_intvar(w->db, "deleted_forward_fees", 0); db_set_intvar(w->db, "deleted_forward_fees", @@ -4726,14 +4737,24 @@ bool wallet_forward_delete(struct wallet *w, tal_free(stmt); } - stmt = db_prepare_v2(w->db, - SQL("DELETE FROM forwards" - " WHERE in_channel_scid = ?" - " AND in_htlc_id = ?" - " AND state = ?")); - db_bind_scid(stmt, 0, chan_in); - db_bind_u64(stmt, 1, htlc_id); - db_bind_int(stmt, 2, wallet_forward_status_in_db(state)); + if (htlc_id) { + stmt = db_prepare_v2(w->db, + SQL("DELETE FROM forwards" + " WHERE in_channel_scid = ?" + " AND in_htlc_id = ?" + " AND state = ?")); + db_bind_scid(stmt, 0, chan_in); + db_bind_u64(stmt, 1, *htlc_id); + db_bind_int(stmt, 2, wallet_forward_status_in_db(state)); + } else { + stmt = db_prepare_v2(w->db, + SQL("DELETE FROM forwards" + " WHERE in_channel_scid = ?" + " AND in_htlc_id IS NULL" + " AND state = ?")); + db_bind_scid(stmt, 0, chan_in); + db_bind_int(stmt, 1, wallet_forward_status_in_db(state)); + } db_exec_prepared_v2(stmt); changed = db_count_changes(stmt) != 0; tal_free(stmt); diff --git a/wallet/wallet.h b/wallet/wallet.h index 917d7f2aa9f9..4983e4e22dd3 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -1389,7 +1389,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, */ bool wallet_forward_delete(struct wallet *w, const struct short_channel_id *chan_in, - u64 htlc_id, + const u64 *htlc_id, enum forward_status state); /** From 695f0011619d758dc53b2c6463ca08d246327edb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 24 Sep 2022 13:57:09 +0930 Subject: [PATCH 1504/1530] pytest: fix flake in test_zeroconf_forward Second pay can fail if first is not completely settled: ``` def test_zeroconf_forward(node_factory, bitcoind): """Ensure that we can use zeroconf channels in forwards. ... # Make sure (esp in non-dev-mode) blockheights agree so we don't WIRE_EXPIRY_TOO_SOON... sync_blockheight(bitcoind, [l1, l2, l3]) inv = l3.rpc.invoice(42 * 10**6, 'inv1', 'desc')['bolt11'] l1.rpc.pay(inv) # And now try the other way around: zeroconf channel first # followed by a public one. wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4) inv = l1.rpc.invoice(42, 'back1', 'desc')['bolt11'] > l3.rpc.pay(inv) ... > raise RpcError(method, payload, resp['error']) E pyln.client.lightning.RpcError: RPC call failed: method: pay, payload: {'bolt11': 'lnbcrt420p1p3junrssp588gtjzmlrr4pfj7ssmdlulzhhushrpq3rdqxjuz2m33scsvzdjlspp5fk5yhu6netc0d0sgp8es52vjk6akhd3uayr08u8max4d8rwzpjuqdq8v3jhxccxqyjw5qcqp99qyysgqcpyyugejv4nya97v6gw8fhtr0ru3vq87jjlltav99wlat436a95n0z8yzdp699p9md0zz9tmnsjpvfj622n9g9fh7r6ldhpgh9wmr4qpcru3rk'}, error: {'code': 210, 'message': 'Destination 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518 is not reachable directly and all routehints were unusable.', 'attempts': [{'status': 'failed', 'failreason': 'Destination 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518 is not reachable directly and all routehints were unusable.', 'partid': 0, 'amount_msat': 42msat}]} ``` Signed-off-by: Rusty Russell --- tests/test_opening.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_opening.py b/tests/test_opening.py index 574f82c7f5a4..ef4c5bdd939b 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1490,6 +1490,10 @@ def test_zeroconf_forward(node_factory, bitcoind): # And now try the other way around: zeroconf channel first # followed by a public one. wait_for(lambda: len(l3.rpc.listchannels()['channels']) == 4) + + # Make sure all htlcs completely settled! + wait_for(lambda: all(only_one(p['channels'])['htlcs'] == [] for p in l2.rpc.listpeers()['peers'])) + inv = l1.rpc.invoice(42, 'back1', 'desc')['bolt11'] l3.rpc.pay(inv) From d4ef20d54a7db05d96e4df8c0906dae475697218 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 27 Sep 2022 09:48:33 +0930 Subject: [PATCH 1505/1530] pytest: fix flake in test_gossip_persistence. We used to ensure the l3<->l4 channel was private by simply not mining enough blocks to announce. Then we started mining 13 blocks to close the channel, and it will get announced, causing a failure: ``` assert non_public(l2) == [] > wait_for(lambda: non_public(l3) == [scid34, scid34]) ... > raise ValueError("Timeout while waiting for {}", success) E ValueError: ('Timeout while waiting for {}', . at 0x7f6cc69b4170>) ``` Signed-off-by: Rusty Russell --- tests/test_gossip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_gossip.py b/tests/test_gossip.py index a1cccf215630..f2e0a34cafbe 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -526,9 +526,9 @@ def test_gossip_persistence(node_factory, bitcoind): scid12, _ = l1.fundchannel(l2, 10**6) scid23, _ = l2.fundchannel(l3, 10**6) - # Make channels public, except for l3 -> l4, which is kept local-only for now + # Make channels public, except for l3 -> l4, which is kept local-only mine_funding_to_announce(bitcoind, [l1, l2, l3, l4]) - scid34, _ = l3.fundchannel(l4, 10**6) + scid34, _ = l3.fundchannel(l4, 10**6, announce_channel=False) bitcoind.generate_block(1) def active(node): From 836a2aa2616dff3bd78267d276c0902b26f7f33c Mon Sep 17 00:00:00 2001 From: joemphilips Date: Fri, 26 Aug 2022 18:46:43 +0900 Subject: [PATCH 1506/1530] use msat_or_all for fundpsbt request amount --- cln-grpc/proto/node.proto | 2 +- cln-grpc/src/convert.rs | 2 +- cln-rpc/src/model.rs | 2 +- contrib/pyln-testing/pyln/testing/node_pb2.py | 192 +++++++++--------- doc/schemas/fundpsbt.request.json | 2 +- 5 files changed, 100 insertions(+), 100 deletions(-) diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 0f773afc5312..d939cab59807 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -988,7 +988,7 @@ message KeysendExtratlvs { } message FundpsbtRequest { - Amount satoshi = 1; + AmountOrAll satoshi = 1; Feerate feerate = 2; uint32 startweight = 3; optional uint32 minconf = 4; diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index bdfbf503a8af..79a899b4c2ca 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -1355,7 +1355,7 @@ impl From for requests::KeysendRequest { impl From for requests::FundpsbtRequest { fn from(c: pb::FundpsbtRequest) -> Self { Self { - satoshi: c.satoshi.unwrap().into(), // Rule #1 for type msat + satoshi: c.satoshi.unwrap().into(), // Rule #1 for type msat_or_all feerate: c.feerate.unwrap().into(), // Rule #1 for type feerate startweight: c.startweight, // Rule #1 for type u32 minconf: c.minconf, // Rule #1 for type u32? diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index b63f6c16e3a4..3ac21159fd7c 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -899,7 +899,7 @@ pub mod requests { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct FundpsbtRequest { #[serde(alias = "satoshi")] - pub satoshi: Amount, + pub satoshi: AmountOrAll, #[serde(alias = "feerate")] pub feerate: Feerate, #[serde(alias = "startweight")] diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index 5afaed222aa2..b1550f73775a 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xb7\x02\n\x0f\x46undpsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xbc\x02\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1302,99 +1302,99 @@ _KEYSENDEXTRATLVS._serialized_start=21772 _KEYSENDEXTRATLVS._serialized_end=21790 _FUNDPSBTREQUEST._serialized_start=21793 - _FUNDPSBTREQUEST._serialized_end=22104 - _FUNDPSBTRESPONSE._serialized_start=22107 - _FUNDPSBTRESPONSE._serialized_end=22324 - _FUNDPSBTRESERVATIONS._serialized_start=22326 - _FUNDPSBTRESERVATIONS._serialized_end=22443 - _SENDPSBTREQUEST._serialized_start=22445 - _SENDPSBTREQUEST._serialized_end=22510 - _SENDPSBTRESPONSE._serialized_start=22512 - _SENDPSBTRESPONSE._serialized_end=22556 - _SIGNPSBTREQUEST._serialized_start=22558 - _SIGNPSBTREQUEST._serialized_end=22607 - _SIGNPSBTRESPONSE._serialized_start=22609 - _SIGNPSBTRESPONSE._serialized_end=22648 - _UTXOPSBTREQUEST._serialized_start=22651 - _UTXOPSBTREQUEST._serialized_end=22998 - _UTXOPSBTRESPONSE._serialized_start=23001 - _UTXOPSBTRESPONSE._serialized_end=23218 - _UTXOPSBTRESERVATIONS._serialized_start=23220 - _UTXOPSBTRESERVATIONS._serialized_end=23337 - _TXDISCARDREQUEST._serialized_start=23339 - _TXDISCARDREQUEST._serialized_end=23371 - _TXDISCARDRESPONSE._serialized_start=23373 - _TXDISCARDRESPONSE._serialized_end=23427 - _TXPREPAREREQUEST._serialized_start=23430 - _TXPREPAREREQUEST._serialized_end=23594 - _TXPREPARERESPONSE._serialized_start=23596 - _TXPREPARERESPONSE._serialized_end=23664 - _TXSENDREQUEST._serialized_start=23666 - _TXSENDREQUEST._serialized_end=23695 - _TXSENDRESPONSE._serialized_start=23697 - _TXSENDRESPONSE._serialized_end=23753 - _DISCONNECTREQUEST._serialized_start=23755 - _DISCONNECTREQUEST._serialized_end=23816 - _DISCONNECTRESPONSE._serialized_start=23818 - _DISCONNECTRESPONSE._serialized_end=23838 - _FEERATESREQUEST._serialized_start=23840 - _FEERATESREQUEST._serialized_end=23947 - _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23910 - _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23947 - _FEERATESRESPONSE._serialized_start=23949 - _FEERATESRESPONSE._serialized_end=24035 - _FEERATESPERKB._serialized_start=24038 - _FEERATESPERKB._serialized_end=24361 - _FEERATESPERKW._serialized_start=24364 - _FEERATESPERKW._serialized_end=24687 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24690 - _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24883 - _FUNDCHANNELREQUEST._serialized_start=24886 - _FUNDCHANNELREQUEST._serialized_end=25371 - _FUNDCHANNELRESPONSE._serialized_start=25374 - _FUNDCHANNELRESPONSE._serialized_end=25529 - _GETROUTEREQUEST._serialized_start=25532 - _GETROUTEREQUEST._serialized_end=25768 - _GETROUTERESPONSE._serialized_start=25770 - _GETROUTERESPONSE._serialized_end=25823 - _GETROUTEROUTE._serialized_start=25826 - _GETROUTEROUTE._serialized_end=26023 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25994 - _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=26023 - _LISTFORWARDSREQUEST._serialized_start=26026 - _LISTFORWARDSREQUEST._serialized_end=26284 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26166 - _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26242 - _LISTFORWARDSRESPONSE._serialized_start=26286 - _LISTFORWARDSRESPONSE._serialized_end=26353 - _LISTFORWARDSFORWARDS._serialized_start=26356 - _LISTFORWARDSFORWARDS._serialized_end=26962 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26745 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26829 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26831 - _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26879 - _LISTPAYSREQUEST._serialized_start=26965 - _LISTPAYSREQUEST._serialized_end=27184 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27090 - _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27145 - _LISTPAYSRESPONSE._serialized_start=27186 - _LISTPAYSRESPONSE._serialized_end=27237 - _LISTPAYSPAYS._serialized_start=27240 - _LISTPAYSPAYS._serialized_end=27759 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27571 - _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27630 - _PINGREQUEST._serialized_start=27761 - _PINGREQUEST._serialized_end=27850 - _PINGRESPONSE._serialized_start=27852 - _PINGRESPONSE._serialized_end=27882 - _SIGNMESSAGEREQUEST._serialized_start=27884 - _SIGNMESSAGEREQUEST._serialized_end=27921 - _SIGNMESSAGERESPONSE._serialized_start=27923 - _SIGNMESSAGERESPONSE._serialized_end=27993 - _STOPREQUEST._serialized_start=27995 - _STOPREQUEST._serialized_end=28008 - _STOPRESPONSE._serialized_start=28010 - _STOPRESPONSE._serialized_end=28024 - _NODE._serialized_start=28027 - _NODE._serialized_end=30955 + _FUNDPSBTREQUEST._serialized_end=22109 + _FUNDPSBTRESPONSE._serialized_start=22112 + _FUNDPSBTRESPONSE._serialized_end=22329 + _FUNDPSBTRESERVATIONS._serialized_start=22331 + _FUNDPSBTRESERVATIONS._serialized_end=22448 + _SENDPSBTREQUEST._serialized_start=22450 + _SENDPSBTREQUEST._serialized_end=22515 + _SENDPSBTRESPONSE._serialized_start=22517 + _SENDPSBTRESPONSE._serialized_end=22561 + _SIGNPSBTREQUEST._serialized_start=22563 + _SIGNPSBTREQUEST._serialized_end=22612 + _SIGNPSBTRESPONSE._serialized_start=22614 + _SIGNPSBTRESPONSE._serialized_end=22653 + _UTXOPSBTREQUEST._serialized_start=22656 + _UTXOPSBTREQUEST._serialized_end=23003 + _UTXOPSBTRESPONSE._serialized_start=23006 + _UTXOPSBTRESPONSE._serialized_end=23223 + _UTXOPSBTRESERVATIONS._serialized_start=23225 + _UTXOPSBTRESERVATIONS._serialized_end=23342 + _TXDISCARDREQUEST._serialized_start=23344 + _TXDISCARDREQUEST._serialized_end=23376 + _TXDISCARDRESPONSE._serialized_start=23378 + _TXDISCARDRESPONSE._serialized_end=23432 + _TXPREPAREREQUEST._serialized_start=23435 + _TXPREPAREREQUEST._serialized_end=23599 + _TXPREPARERESPONSE._serialized_start=23601 + _TXPREPARERESPONSE._serialized_end=23669 + _TXSENDREQUEST._serialized_start=23671 + _TXSENDREQUEST._serialized_end=23700 + _TXSENDRESPONSE._serialized_start=23702 + _TXSENDRESPONSE._serialized_end=23758 + _DISCONNECTREQUEST._serialized_start=23760 + _DISCONNECTREQUEST._serialized_end=23821 + _DISCONNECTRESPONSE._serialized_start=23823 + _DISCONNECTRESPONSE._serialized_end=23843 + _FEERATESREQUEST._serialized_start=23845 + _FEERATESREQUEST._serialized_end=23952 + _FEERATESREQUEST_FEERATESSTYLE._serialized_start=23915 + _FEERATESREQUEST_FEERATESSTYLE._serialized_end=23952 + _FEERATESRESPONSE._serialized_start=23954 + _FEERATESRESPONSE._serialized_end=24040 + _FEERATESPERKB._serialized_start=24043 + _FEERATESPERKB._serialized_end=24366 + _FEERATESPERKW._serialized_start=24369 + _FEERATESPERKW._serialized_end=24692 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_start=24695 + _FEERATESONCHAIN_FEE_ESTIMATES._serialized_end=24888 + _FUNDCHANNELREQUEST._serialized_start=24891 + _FUNDCHANNELREQUEST._serialized_end=25376 + _FUNDCHANNELRESPONSE._serialized_start=25379 + _FUNDCHANNELRESPONSE._serialized_end=25534 + _GETROUTEREQUEST._serialized_start=25537 + _GETROUTEREQUEST._serialized_end=25773 + _GETROUTERESPONSE._serialized_start=25775 + _GETROUTERESPONSE._serialized_end=25828 + _GETROUTEROUTE._serialized_start=25831 + _GETROUTEROUTE._serialized_end=26028 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_start=25999 + _GETROUTEROUTE_GETROUTEROUTESTYLE._serialized_end=26028 + _LISTFORWARDSREQUEST._serialized_start=26031 + _LISTFORWARDSREQUEST._serialized_end=26289 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_start=26171 + _LISTFORWARDSREQUEST_LISTFORWARDSSTATUS._serialized_end=26247 + _LISTFORWARDSRESPONSE._serialized_start=26291 + _LISTFORWARDSRESPONSE._serialized_end=26358 + _LISTFORWARDSFORWARDS._serialized_start=26361 + _LISTFORWARDSFORWARDS._serialized_end=26967 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_start=26750 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTATUS._serialized_end=26834 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_start=26836 + _LISTFORWARDSFORWARDS_LISTFORWARDSFORWARDSSTYLE._serialized_end=26884 + _LISTPAYSREQUEST._serialized_start=26970 + _LISTPAYSREQUEST._serialized_end=27189 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_start=27095 + _LISTPAYSREQUEST_LISTPAYSSTATUS._serialized_end=27150 + _LISTPAYSRESPONSE._serialized_start=27191 + _LISTPAYSRESPONSE._serialized_end=27242 + _LISTPAYSPAYS._serialized_start=27245 + _LISTPAYSPAYS._serialized_end=27764 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_start=27576 + _LISTPAYSPAYS_LISTPAYSPAYSSTATUS._serialized_end=27635 + _PINGREQUEST._serialized_start=27766 + _PINGREQUEST._serialized_end=27855 + _PINGRESPONSE._serialized_start=27857 + _PINGRESPONSE._serialized_end=27887 + _SIGNMESSAGEREQUEST._serialized_start=27889 + _SIGNMESSAGEREQUEST._serialized_end=27926 + _SIGNMESSAGERESPONSE._serialized_start=27928 + _SIGNMESSAGERESPONSE._serialized_end=27998 + _STOPREQUEST._serialized_start=28000 + _STOPREQUEST._serialized_end=28013 + _STOPRESPONSE._serialized_start=28015 + _STOPRESPONSE._serialized_end=28029 + _NODE._serialized_start=28032 + _NODE._serialized_end=30960 # @@protoc_insertion_point(module_scope) diff --git a/doc/schemas/fundpsbt.request.json b/doc/schemas/fundpsbt.request.json index f53d3f2d8578..25aba2bede53 100644 --- a/doc/schemas/fundpsbt.request.json +++ b/doc/schemas/fundpsbt.request.json @@ -9,7 +9,7 @@ ], "properties": { "satoshi": { - "type": "msat" + "type": "msat_or_all" }, "feerate": { "type": "feerate" From 1de4e4627623d488836cffc93537d124ec1487c0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 28 Sep 2022 13:01:09 +0930 Subject: [PATCH 1507/1530] tests: add onion-test-vector from "BOLT 4: Remove legacy format, make var_onion_optin compulsory." Signed-off-by: Rusty Russell --- common/test/run-onion-test-vector.c | 195 ++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 common/test/run-onion-test-vector.c diff --git a/common/test/run-onion-test-vector.c b/common/test/run-onion-test-vector.c new file mode 100644 index 000000000000..2595ee9975d3 --- /dev/null +++ b/common/test/run-onion-test-vector.c @@ -0,0 +1,195 @@ +#include "config.h" +#include "../bigsize.c" +#include "../json_parse.c" +#include "../json_parse_simple.c" +#include "../onion.c" +#include "../sphinx.c" +#include "../hmac.c" +#include "../type_to_string.c" +#include "../../wire/towire.c" +#include "../../wire/fromwire.c" +#if EXPERIMENTAL_FEATURES +#include "../../wire/onion_exp_wiregen.c" +#else +#include "../../wire/onion_wiregen.c" +#endif +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_msat_eq */ +bool amount_msat_eq(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED) +{ fprintf(stderr, "amount_msat_eq called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_tlv */ +bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, + void *record UNNEEDED, struct tlv_field **fields UNNEEDED, + const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) +{ fprintf(stderr, "fromwire_tlv called!\n"); abort(); } +/* Generated stub for mvt_tag_str */ +const char *mvt_tag_str(enum mvt_tag tag UNNEEDED) +{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for pubkey_from_node_id */ +bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } +/* Generated stub for tlv_field_offset */ +size_t tlv_field_offset(const u8 *tlvstream UNNEEDED, size_t tlvlen UNNEEDED, u64 fieldtype UNNEEDED) +{ fprintf(stderr, "tlv_field_offset called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_tlv */ +void towire_tlv(u8 **pptr UNNEEDED, + const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, + const void *record UNNEEDED) +{ fprintf(stderr, "towire_tlv called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static struct secret mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey.data, NULL, NULL) != 1) + abort(); +} + +int main(int argc, char *argv[]) +{ + char *json; + size_t i; + jsmn_parser parser; + jsmntok_t toks[5000]; + const jsmntok_t *t, *generate_tok; + struct sphinx_path *sp; + struct secret session_key; + struct onionpacket *op; + u8 *assoc_data, *expected, *actual; + struct secret *unused_path_secrets; + u8 *payloads[5]; + + common_setup(argv[0]); + + if (argv[1]) + json = grab_file(tmpctx, argv[1]); + else { + char *dir = getenv("BOLTDIR"); + json = grab_file(tmpctx, + path_join(tmpctx, + dir ? dir : "../bolts", + "bolt04/onion-test.json")); + if (!json) { + printf("test file not found, skipping\n"); + goto out; + } + } + + jsmn_init(&parser); + if (jsmn_parse(&parser, json, strlen(json), toks, ARRAY_SIZE(toks)) < 0) + abort(); + + generate_tok = json_get_member(json, toks, "generate"); + json_to_secret(json, json_get_member(json, generate_tok, "session_key"), &session_key); + assoc_data = json_tok_bin_from_hex(tmpctx, json, json_get_member(json, generate_tok, "associated_data")); + sp = sphinx_path_new_with_key(tmpctx, assoc_data, &session_key); + json_for_each_arr(i, t, json_get_member(json, generate_tok, "hops")) { + struct pubkey k; + const u8 *cursor; + size_t max, len; + + json_to_pubkey(json, json_get_member(json, t, "pubkey"), &k); + payloads[i] = json_tok_bin_from_hex(NULL, json, json_get_member(json, t, "payload")); + /* First byte(s) are length: check and remove them for our API. */ + cursor = payloads[i]; + max = tal_bytelen(payloads[i]); + len = fromwire_bigsize(&cursor, &max); + assert(len == max); + sphinx_add_modern_hop(sp, &k, take(tal_dup_arr(NULL, u8, cursor, max, 0))); + } + assert(i == ARRAY_SIZE(payloads)); + + op = create_onionpacket(tmpctx, sp, ROUTING_INFO_SIZE, &unused_path_secrets); + + expected = json_tok_bin_from_hex(tmpctx, json, json_get_member(json, toks, "onion")); + actual = serialize_onionpacket(tmpctx, op); + assert(memeq(expected, tal_bytelen(expected), actual, tal_bytelen(actual))); + + /* Now decode! */ + op = parse_onionpacket(tmpctx, actual, tal_bytelen(actual), NULL); + json_for_each_arr(i, t, json_get_member(json, toks, "decode")) { + struct route_step *rs; + struct secret ss; + + json_to_secret(json, t, &mykey); + test_ecdh(&op->ephemeralkey, &ss); + rs = process_onionpacket(tmpctx, op, &ss, assoc_data, tal_bytelen(assoc_data), true); + assert(memeq(rs->raw_payload, tal_bytelen(rs->raw_payload), + payloads[i], tal_bytelen(payloads[i]))); + if (rs->nextcase == ONION_FORWARD) + op = rs->next; + else + op = NULL; + } + assert(!op); + +out: + common_shutdown(); +} From c8ad9e18a9ff47dd047292ccc1b1a8a21e2a6712 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 28 Sep 2022 13:39:41 +0930 Subject: [PATCH 1508/1530] common/onion: remove all trace of legacy parsing. We still have an "enum forward_style" for the database, where old-style forwards can still exist. Signed-off-by: Rusty Russell Changelog-Removed: Protocol: we no longer forward HTLCs with legacy onions. --- common/onion.c | 57 ++++++-------------------------------- common/onion.h | 1 - common/sphinx.h | 21 -------------- lightningd/peer_htlcs.c | 8 ++---- tests/test_pay.py | 61 +++++------------------------------------ wallet/wallet.h | 2 +- 6 files changed, 19 insertions(+), 131 deletions(-) diff --git a/common/onion.c b/common/onion.c index 45325e25ec17..9da6683f8430 100644 --- a/common/onion.c +++ b/common/onion.c @@ -130,41 +130,22 @@ static bool pull_payload_length(const u8 **cursor, if (!cursor) return false; - /* BOLT #4: - * - Legacy `hop_data` format, identified by a single `0x00` byte for - * length. In this case the `hop_payload_length` is defined to be 32 - * bytes. - */ - if (has_realm && *len == 0) { - if (type) - *type = ONION_V0_PAYLOAD; - assert(*cursor - start == 1); - *len = 1 + 32; - return true; - } - /* BOLT #4: * - `tlv_payload` format, identified by any length over `1`. In this * case the `hop_payload_length` is equal to the numeric value of * `length`. */ - if (!has_realm || *len > 1) { - /* It's still invalid if it claims to be too long! */ - if (has_realm) { - if (*len > ROUTING_INFO_SIZE - HMAC_SIZE) - return false; - } else { - if (*len > *max) - return false; - } + if (*len <= 1) + return false; - if (type) - *type = ONION_TLV_PAYLOAD; - *len += (*cursor - start); - return true; - } + /* It's invalid if it claims to be too long! */ + if (*len > *max) + return false; - return false; + if (type) + *type = ONION_TLV_PAYLOAD; + *len += (*cursor - start); + return true; } size_t onion_payload_length(const u8 *raw_payload, size_t len, bool has_realm, @@ -235,26 +216,6 @@ struct onion_payload *onion_decode(const tal_t *ctx, goto fail_no_tlv; } - /* Very limited legacy handling: forward only. */ - if (p->type == ONION_V0_PAYLOAD && rs->nextcase == ONION_FORWARD) { - p->forward_channel = tal(p, struct short_channel_id); - fromwire_short_channel_id(&cursor, &max, p->forward_channel); - p->total_msat = NULL; - p->amt_to_forward = fromwire_amount_msat(&cursor, &max); - p->outgoing_cltv = fromwire_u32(&cursor, &max); - p->payment_secret = NULL; - p->payment_metadata = NULL; - p->blinding = NULL; - /* We can't handle blinding with a legacy payload */ - if (blinding) - return tal_free(p); - /* If they somehow got an invalid onion this far, fail. */ - if (!cursor) - return tal_free(p); - p->tlv = NULL; - return p; - } - /* We do this manually so we can accept extra types, and get * error off and type. */ tlv = tlv_tlv_payload_new(p); diff --git a/common/onion.h b/common/onion.h index 839fa5048f03..3476acde4bde 100644 --- a/common/onion.h +++ b/common/onion.h @@ -7,7 +7,6 @@ struct route_step; enum onion_payload_type { - ONION_V0_PAYLOAD = 0, ONION_TLV_PAYLOAD = 1, }; diff --git a/common/sphinx.h b/common/sphinx.h index 2d7c3727b6c0..142cb3d6fa83 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -53,27 +53,6 @@ enum route_next_case { */ struct sphinx_path; -/* BOLT #4: - * - * ## Legacy `hop_data` payload format - * - * The `hop_data` format is identified by a single `0x00`-byte length, - * for backward compatibility. Its payload is defined as: - * - * 1. type: `hop_data` (for `realm` 0) - * 2. data: - * * [`short_channel_id`:`short_channel_id`] - * * [`u64`:`amt_to_forward`] - * * [`u32`:`outgoing_cltv_value`] - * * [`12*byte`:`padding`] - */ -struct hop_data_legacy { - u8 realm; - struct short_channel_id channel_id; - struct amount_msat amt_forward; - u32 outgoing_cltv; -}; - /* * All the necessary information to generate a valid onion for this hop on a * sphinx path. The payload is preserialized in order since the onion diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 25ca2f04998d..f9f609a10912 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -491,8 +491,8 @@ static enum forward_style get_onion_style(const struct htlc_in *hin) if (!hin->payload) return FORWARD_STYLE_UNKNOWN; - switch (hin->payload->type) { - case ONION_V0_PAYLOAD: + switch ((int)hin->payload->type) { + case 0: return FORWARD_STYLE_LEGACY; case ONION_TLV_PAYLOAD: return FORWARD_STYLE_TLV; @@ -1061,10 +1061,6 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_add_hex_talarr(s, "payload", rs->raw_payload); if (p->payload) { switch (p->payload->type) { - case ONION_V0_PAYLOAD: - json_add_string(s, "type", "legacy"); - break; - case ONION_TLV_PAYLOAD: json_add_string(s, "type", "tlv"); break; diff --git a/tests/test_pay.py b/tests/test_pay.py index 2eec94c914df..c6cf0844ebf8 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2837,26 +2837,22 @@ def test_shadow_routing(node_factory): def test_createonion_rpc(node_factory): l1 = node_factory.get_node() + # From bolt04/onion-test.json: hops = [{ "pubkey": "02eec7245d6b7d2ccb30380bfbe2a3648cd7a942653f5aa340edcea1f283686619", - # legacy - "payload": "000000000000000000000000000000000000000000000000000000000000000000" + "payload": "1202023a98040205dc06080000000000000001" }, { "pubkey": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", - # tlv (20 bytes) - "payload": "140101010101010101000000000000000100000001" + "payload": "52020236b00402057806080000000000000002fd02013c0102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f0102030405060708090a0b0c0d0e0f" }, { "pubkey": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007", - # TLV (256 bytes) - "payload": "fd0100000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" + "payload": "12020230d4040204e206080000000000000003" }, { "pubkey": "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991", - # tlv (20 bytes) - "payload": "140303030303030303000000000000000300000003" + "payload": "1202022710040203e806080000000000000004" }, { "pubkey": "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145", - # legacy - "payload": "000404040404040404000000000000000400000004000000000000000000000000" + "payload": "fd011002022710040203e8082224a33562c54507a9334e79f0dc4f17d407e6d7c61f0e2f3d0d38599502f617042710fd012de02a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a" }] res = l1.rpc.createonion(hops=hops, assocdata="BB" * 32) @@ -2867,8 +2863,7 @@ def test_createonion_rpc(node_factory): session_key="41" * 32) # The trailer is generated using the filler and can be ued as a # checksum. This trailer is from the test-vector in the specs. - print(res) - assert(res['onion'].endswith('9400f45a48e6dc8ddbaeb3')) + assert(res['onion'].endswith('9126aaefb627719f421e20')) @pytest.mark.developer("gossip propagation is slow without DEVELOPER=1") @@ -5233,48 +5228,6 @@ def test_sendpay_grouping(node_factory, bitcoind): assert([p['status'] for p in pays] == ['failed', 'failed', 'complete']) -def test_legacyonion(node_factory, bitcoind): - # We have to replicate the topology we created onion with, exactly. - l1, l2, l3 = node_factory.line_graph(3, fundchannel=False) - node_factory.join_nodes([l1, l2], wait_for_announce=True) - - # We need this scid to match canned onion (110x1x0), so no change. - addr = l2.rpc.newaddr()['bech32'] - bitcoind.rpc.sendtoaddress(addr, 10**6 / 10**8) - - bitcoind.generate_block(1) - sync_blockheight(bitcoind, [l2]) - l2.rpc.fundchannel(l3.info['id'], "all")['txid'] - bitcoind.generate_block(6, wait_for_mempool=1) - - # We don't actually need gossip! But make sure blockheight is correct. - sync_blockheight(bitcoind, [l1, l2, l3]) - - l3.rpc.invoice(10000, 'test_invoice', 'description', preimage='00' * 32)['bolt11'] - - # Here's one I created using an old c-lightning: l2 gets legacy. - l1.rpc.call('sendonion', - {"onion": "000279f8e85e661936e01c91d49ed42e776a80741047403b9292e2ef66d977bce0adaf4e773d4750cc8e7359a53107413ec2c9956fb7c7ef2ffba6b588880557efb9cdb6cbc2231c517c1ed83c8aa136e2294b82cd2df382b794128792d86f8ac155966ad0bc7610df86bf5c2dfaf685be237a5056db0cabbf09ac6ca9686a8152b3a2157c1b3bb08b29321ef0d8970ef761d29b1e3305ee3b96195911c0486dbfdd48fa747b802ff7aafbcb396f4bdf3bbc2c72aeea9cd621767d883d6206aec82cd744c571157cf367680f5a91106bfd49e1febf7190cba2868b50cdebefcaaace9ec00083d5b32fc2b4f8db0022c59d361296ebf5c47a09dd633da15cf21bc24b6efbcf7202127fda27b704d6c255e4d240bb56dbd7351d0b6299682f4b4f98dee4aa185b757948a7c80053bec14fc91d813e913c79253de25a1b1fa031eccf0abe52db4f4562a797bc5066202284b070ca35f92cae9d0ea22f5a88aaae9e63b775e10f3f54fa0069a03e554e72bb0c3ead07eb3bf0895531b580123c308fd2450a3698328e71756dee32ae90c4872c296183bbfb08bb2bedbbab9a46ebab05674d53d06dcec6879b994fbd8f828fbccd6766c76255553bb4605f7fe3767f1bb6e8e4341a120574d30de715b27fbcdfec4f590877653bec0ad410d243ebd09e1b1e9929bba99dd8c7bff26201970c8ffeb1e2773eb18ec29bc9b13c8f3dcfc77935b5afd788e1e4ca4a31102d645db575211799bf54c42f2152f227796fdafc7ad6708dd18553d7a9c8bd582655230a3920c25286705353b513de4e2ef3dd84335b9236560f503a9198dc2d730a950c01cd6cf0aee5bc5b2c060de23cd8f2dda92dcfaee0c4e29d5efe12ca1139d0c86b6d22ea58bb3076b953577d86969702176010690e9eb3afda4db3c8e2304494c5f43cdf1487ab6daa740d4645c6d9ed796f0b210e18f751249fa87137bbfb8b332e97ad7e6d73130838b94680a0c999a0ec0c90c3807af60c6db7a3ed2a7ddfbe7c7b293870d9bc003512a3b64607661fcc94df7e9f2bfc875b447b8cedc38d8a55566246761e02932e9308e1f26cf9724f5432fe391b88bd4af4f7b9f7599529035791b475b10eb4cf1efb56bfab75fc75a1cb73308120dbd52e635dd0207df882488ee005eedae5cc3245e6ccb49770ace2868f205f76640b38e55b23e23ca94559c200f94b06d498332675a0ee0fe57551d1dd2343934f20daa60b025992350c7d8de192139d786b5db3ec21c7a772571c61a66b3eff2a424464291e183bd382b0560efab3fc3ccf88cb4b9a3695f35590937b611d98856db8bf46d6716672a1f0efa8cd3e4b49bf1dc0cea35dce1465dc36fd94db0592cfee01a90d96766c8b08a6e79d2b3b3f8a133f3a01aad6d8adbf8aee4dba09232424faae516f0498705c1514f570d3e3e6c9f0a9457a6230847bbdce1d6a427538a82e9025ee7f90901bb73c6819fc02cf8d14c0246a44af8efa2a49ba11cf61a09204dd4a1d925b32ac327d525ff189ffa833d0e1c5e6999a9268a2526c4e9a6fdb95ab6b134e30946923af361a5ef543e77621675d25a60a0e0d691088e68112f18be4fa66d10cf68c65f8c4034fab58aaff2d02076078b7d392a3a72268da5124884fabd0880072d080fa6235d2155c4160c7d1d4a5054a1e27be381d1048d47a4e7abb936f170ff1b210f09214ecb645b1c2d5d77f286afcc4f6716b788e223e82501ee544836605e32074e465923858cde71163ad700947c94381611eda017a6632c782a377a2c506d0392a6e1a7f6e5988f7893776bdadf28e4648ef48672190d9de0e94523c208dbdaee274c6d0ce9a27a40afd1cc3e86936c2e2e96565a052a7581a807a4fbc8abdaac9e8f32dc04789b47fd8d5e874112e2308dedff5a76b6e092cb42c1bd9a082", - "first_hop": { - "amount_msat": "10001msat", - "delay": 29, - "id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59" - }, - "payment_hash": "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925", - "amount_msat": "10000msat", - "shared_secrets": [ - "bd29a24ea1fa5cab1677b22f4980f6aa115d470c2e3052cb08ca7d636441bfd5", - "7d70d337a6b898373386d7d2cff05ca8f4d81123f63cf911aa064a6d8849f972" - ], - "partid": 1, - "groupid": 1, - "bolt11": "lnbcrt100n1p3y8xl3pp5ve584t0cv27hwmy0cx9ca8uwyqyfw9y9dm3r8vus9fv36r2l9yjsdqjv3jhxcmjd9c8g6t0dcxqyjw5qcqp9sp5q8g040f9rl9mu2unkjuj0vn262s6nyrhz5hythk3ueu2lfzahmzsrzjqgkjyd3q5dv6gllh77kygly9c3kfy0d9xwyjyxsq2nq3c83u5vw4jqqqdcqqqqgqqqqqqqqpqqqqqzsqqc9qgsqqqyssq47lyzhlmfw6hdcrklz3nw774p6nueggm6sxvrg734yrzdl0rn4p8rfl4ql2hexcufafgpstjkag2ywfycg08kuz5k5qszz7ecypqeugpnapu7t", - "destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d"}) - - wait_for(lambda: only_one(l3.rpc.listinvoices()['invoices'])['status'] == 'paid') - assert only_one(l2.rpc.listforwards()['forwards'])['style'] == 'legacy' - - def test_pay_manual_exclude(node_factory, bitcoind): l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True) l1_id = l1.rpc.getinfo()['id'] diff --git a/wallet/wallet.h b/wallet/wallet.h index 4983e4e22dd3..0522274eb108 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -164,7 +164,7 @@ bool string_to_forward_status(const char *status_str, size_t len, /* /!\ This is a DB ENUM, please do not change the numbering of any * already defined elements (adding is ok) /!\ */ enum forward_style { - FORWARD_STYLE_LEGACY = ONION_V0_PAYLOAD, + FORWARD_STYLE_LEGACY = 0, FORWARD_STYLE_TLV = ONION_TLV_PAYLOAD, FORWARD_STYLE_UNKNOWN = 2, /* Not actually in db, safe to renumber! */ }; From 8771c863794d2774e93ea759f4eb23873a236112 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 28 Sep 2022 14:19:35 +0930 Subject: [PATCH 1509/1530] common/onion: expunge all trace of different onion styles. In particular, remove special routines to pull length: it's there, take it and check it yourself. Signed-off-by: Rusty Russell --- common/onion.c | 60 +++------------------- common/onion.h | 23 --------- common/sphinx.c | 41 +++++++++------ common/test/run-sphinx-xor_cipher_stream.c | 12 ++--- devtools/onion.c | 6 --- lightningd/peer_htlcs.c | 35 +++---------- plugins/bkpr/test/run-bkpr_db.c | 3 -- plugins/bkpr/test/run-recorder.c | 3 -- wallet/wallet.h | 2 +- 9 files changed, 47 insertions(+), 138 deletions(-) diff --git a/common/onion.c b/common/onion.c index 9da6683f8430..193920a32cc5 100644 --- a/common/onion.c +++ b/common/onion.c @@ -111,57 +111,6 @@ u8 *onion_final_hop(const tal_t *ctx, return make_tlv_hop(ctx, tlv); } -/* Returns true if valid, and fills in type. */ -static bool pull_payload_length(const u8 **cursor, - size_t *max, - bool has_realm, - enum onion_payload_type *type, - size_t *len) -{ - /* *len will incorporate bytes we read from cursor */ - const u8 *start = *cursor; - - /* BOLT #4: - * - * The `length` field determines both the length and the format of the - * `hop_payload` field; the following formats are defined: - */ - *len = fromwire_bigsize(cursor, max); - if (!cursor) - return false; - - /* BOLT #4: - * - `tlv_payload` format, identified by any length over `1`. In this - * case the `hop_payload_length` is equal to the numeric value of - * `length`. - */ - if (*len <= 1) - return false; - - /* It's invalid if it claims to be too long! */ - if (*len > *max) - return false; - - if (type) - *type = ONION_TLV_PAYLOAD; - *len += (*cursor - start); - return true; -} - -size_t onion_payload_length(const u8 *raw_payload, size_t len, bool has_realm, - bool *valid, - enum onion_payload_type *type) -{ - size_t max = len, payload_len; - *valid = pull_payload_length(&raw_payload, &max, has_realm, type, &payload_len); - - /* If it's not valid, copy the entire thing. */ - if (!*valid) - return len; - - return payload_len; -} - #if EXPERIMENTAL_FEATURES static struct tlv_tlv_payload *decrypt_tlv(const tal_t *ctx, const struct secret *blinding_ss, @@ -210,7 +159,14 @@ struct onion_payload *onion_decode(const tal_t *ctx, size_t max = tal_bytelen(cursor), len; struct tlv_tlv_payload *tlv; - if (!pull_payload_length(&cursor, &max, true, &p->type, &len)) { + /* BOLT-remove-legacy-onion #4: + * 1. type: `hop_payloads` + * 2. data: + * * [`bigsize`:`length`] + * * [`length*byte`:`payload`] + */ + len = fromwire_bigsize(&cursor, &max); + if (!cursor || len > max) { *failtlvtype = 0; *failtlvpos = tal_bytelen(rs->raw_payload); goto fail_no_tlv; diff --git a/common/onion.h b/common/onion.h index 3476acde4bde..23dd2c92ceef 100644 --- a/common/onion.h +++ b/common/onion.h @@ -6,13 +6,7 @@ struct route_step; -enum onion_payload_type { - ONION_TLV_PAYLOAD = 1, -}; - struct onion_payload { - enum onion_payload_type type; - struct amount_msat amt_to_forward; u32 outgoing_cltv; struct amount_msat *total_msat; @@ -45,23 +39,6 @@ u8 *onion_final_hop(const tal_t *ctx, const struct secret *payment_secret, const u8 *payment_metadata); -/** - * onion_payload_length: measure payload length in decrypted onion. - * @raw_payload: payload to look at. - * @len: length of @raw_payload in bytes. - * @has_realm: used for HTLCs, where first byte 0 is magical. - * @valid: set to true if it is valid, false otherwise. - * @type: if non-NULL, set to type of payload if *@valid is true. - * - * If @valid is set, there is room for the HMAC immediately following, - * as the return value is <= ROUTING_INFO_SIZE - HMAC_SIZE. Otherwise, - * the return value is @len (i.e. the entire payload). - */ -size_t onion_payload_length(const u8 *raw_payload, size_t len, - bool has_realm, - bool *valid, - enum onion_payload_type *type); - /** * onion_decode: decode payload from a decrypted onion. * @ctx: context to allocate onion_contents off. diff --git a/common/sphinx.c b/common/sphinx.c index 1eb4496ea77e..bf25c01aa947 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -601,7 +601,8 @@ struct route_step *process_onionpacket( u8 *paddedheader; size_t payload_size; bigsize_t shift_size; - bool valid; + const u8 *cursor; + size_t max; step->next = talz(step, struct onionpacket); step->next->version = msg->version; @@ -624,23 +625,29 @@ struct route_step *process_onionpacket( if (!blind_group_element(&step->next->ephemeralkey, &msg->ephemeralkey, blind)) return tal_free(step); - payload_size = onion_payload_length(paddedheader, - tal_bytelen(msg->routinginfo), - has_realm, - &valid, NULL); + /* Now, try to pull data out. */ + cursor = paddedheader; + max = tal_bytelen(msg->routinginfo); + + /* Any of these could fail, falling thru with cursor == NULL */ + payload_size = fromwire_bigsize(&cursor, &max); + /* FIXME: raw_payload *includes* the length, which is redundant and + * means we can't just ust fromwire_tal_arrn. */ + fromwire_pad(&cursor, &max, payload_size); + if (cursor != NULL) + step->raw_payload = tal_dup_arr(step, u8, paddedheader, + cursor - paddedheader, 0); + fromwire_hmac(&cursor, &max, &step->next->hmac); + + /* BOLT-remove-legacy-onion #4: + * Since no `payload` TLV value can ever be shorter than 2 bytes, `length` values of 0 and 1 are + * reserved. (`0` indicated a legacy format no longer supported, and `1` is reserved for future + * use). */ + if (payload_size < 2 || !cursor) + return tal_free(step); - /* Can't decode? Treat it as terminal. */ - if (!valid) { - shift_size = payload_size; - memset(step->next->hmac.bytes, 0, sizeof(step->next->hmac.bytes)); - } else { - assert(payload_size <= tal_bytelen(msg->routinginfo) - HMAC_SIZE); - /* Copy hmac */ - shift_size = payload_size + HMAC_SIZE; - memcpy(step->next->hmac.bytes, - paddedheader + payload_size, HMAC_SIZE); - } - step->raw_payload = tal_dup_arr(step, u8, paddedheader, payload_size, 0); + /* This includes length field and hmac */ + shift_size = cursor - paddedheader; /* Left shift the current payload out and make the remainder the new onion */ step->next->routinginfo = tal_dup_arr(step->next, diff --git a/common/test/run-sphinx-xor_cipher_stream.c b/common/test/run-sphinx-xor_cipher_stream.c index dc1bd585c504..4212fab5d622 100644 --- a/common/test/run-sphinx-xor_cipher_stream.c +++ b/common/test/run-sphinx-xor_cipher_stream.c @@ -38,6 +38,9 @@ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bigsize */ +bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_bool */ bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_bool called!\n"); abort(); } @@ -47,6 +50,9 @@ void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_hmac */ void fromwire_hmac(const u8 **ptr UNNEEDED, size_t *max UNNEEDED, struct hmac *hmac UNNEEDED) { fprintf(stderr, "fromwire_hmac called!\n"); abort(); } +/* Generated stub for fromwire_pad */ +void fromwire_pad(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_pad called!\n"); abort(); } /* Generated stub for fromwire_secp256k1_ecdsa_signature */ void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) @@ -88,12 +94,6 @@ void hmac_update(crypto_auth_hmacsha256_state *state UNNEEDED, /* Generated stub for new_onionreply */ struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) { fprintf(stderr, "new_onionreply called!\n"); abort(); } -/* Generated stub for onion_payload_length */ -size_t onion_payload_length(const u8 *raw_payload UNNEEDED, size_t len UNNEEDED, - bool has_realm UNNEEDED, - bool *valid UNNEEDED, - enum onion_payload_type *type UNNEEDED) -{ fprintf(stderr, "onion_payload_length called!\n"); abort(); } /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } diff --git a/devtools/onion.c b/devtools/onion.c index 0acbbf498d68..31bbcc56e7d4 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -270,18 +270,12 @@ static void runtest(const char *filename) decodetok = json_get_member(buffer, toks, "decode"); json_for_each_arr(i, hop, decodetok) { - bool valid; - hexprivkey = json_strdup(ctx, buffer, hop); printf("Processing at hop %zu\n", i); step = decode_with_privkey(ctx, serialized, hexprivkey, associated_data); serialized = serialize_onionpacket(ctx, step->next); if (!serialized) errx(1, "Error serializing message."); - onion_payload_length(step->raw_payload, - tal_bytelen(step->raw_payload), - true, &valid, NULL); - assert(valid); printf(" Payload: %s\n", tal_hex(ctx, step->raw_payload)); printf(" Next onion: %s\n", tal_hex(ctx, serialized)); printf(" Next HMAC: %s\n", diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index f9f609a10912..dfea74ca5efb 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -485,21 +485,6 @@ static void destroy_hout_subd_died(struct htlc_out *hout) db_commit_transaction(db); } -static enum forward_style get_onion_style(const struct htlc_in *hin) -{ - /* This happens on reload from db; don't try too hard! */ - if (!hin->payload) - return FORWARD_STYLE_UNKNOWN; - - switch ((int)hin->payload->type) { - case 0: - return FORWARD_STYLE_LEGACY; - case ONION_TLV_PAYLOAD: - return FORWARD_STYLE_TLV; - } - abort(); -} - /* This is where channeld gives us the HTLC id, and also reports if it * failed immediately. */ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds UNUSED, @@ -539,7 +524,7 @@ static void rcvd_htlc_reply(struct subd *subd, const u8 *msg, const int *fds UNU * so set htlc field with NULL (db wants it to exist!) */ wallet_forwarded_payment_add(ld->wallet, hout->in, - get_onion_style(hout->in), + FORWARD_STYLE_TLV, channel_scid_or_local_alias(hout->key.channel), NULL, FORWARD_LOCAL_FAILED, fromwire_peektype(hout->failmsg)); @@ -713,7 +698,7 @@ static void forward_htlc(struct htlc_in *hin, if (!next || !channel_active(next)) { local_fail_in_htlc(hin, take(towire_unknown_next_peer(NULL))); wallet_forwarded_payment_add(hin->key.channel->peer->ld->wallet, - hin, get_onion_style(hin), + hin, FORWARD_STYLE_TLV, forward_scid, NULL, FORWARD_LOCAL_FAILED, WIRE_UNKNOWN_NEXT_PEER); @@ -812,7 +797,7 @@ static void forward_htlc(struct htlc_in *hin, fail: local_fail_in_htlc(hin, failmsg); wallet_forwarded_payment_add(ld->wallet, - hin, get_onion_style(hin), forward_scid, hout, + hin, FORWARD_STYLE_TLV, forward_scid, hout, FORWARD_LOCAL_FAILED, fromwire_peektype(failmsg)); } @@ -1060,11 +1045,7 @@ static void htlc_accepted_hook_serialize(struct htlc_accepted_hook_payload *p, json_add_hex_talarr(s, "payload", rs->raw_payload); if (p->payload) { - switch (p->payload->type) { - case ONION_TLV_PAYLOAD: - json_add_string(s, "type", "tlv"); - break; - } + json_add_string(s, "type", "tlv"); if (p->payload->forward_channel) json_add_short_channel_id(s, "short_channel_id", @@ -1406,7 +1387,7 @@ static void fulfill_our_htlc_out(struct channel *channel, struct htlc_out *hout, else if (hout->in) { fulfill_htlc(hout->in, preimage); wallet_forwarded_payment_add(ld->wallet, hout->in, - get_onion_style(hout->in), + FORWARD_STYLE_TLV, channel_scid_or_local_alias(hout->key.channel), hout, FORWARD_SETTLED, 0); } @@ -1534,7 +1515,7 @@ static bool peer_failed_our_htlc(struct channel *channel, if (hout->in) wallet_forwarded_payment_add(ld->wallet, hout->in, - get_onion_style(hout->in), + FORWARD_STYLE_TLV, channel_scid_or_local_alias(channel), hout, FORWARD_FAILED, hout->failmsg @@ -1697,7 +1678,7 @@ void onchain_failed_our_htlc(const struct channel *channel, local_fail_in_htlc(hout->in, take(towire_permanent_channel_failure(NULL))); wallet_forwarded_payment_add(hout->key.channel->peer->ld->wallet, - hout->in, get_onion_style(hout->in), + hout->in, FORWARD_STYLE_TLV, channel_scid_or_local_alias(channel), hout, FORWARD_LOCAL_FAILED, hout->failmsg @@ -1864,7 +1845,7 @@ static bool update_out_htlc(struct channel *channel, if (hout->in) { wallet_forwarded_payment_add(ld->wallet, hout->in, - get_onion_style(hout->in), + FORWARD_STYLE_TLV, channel_scid_or_local_alias(channel), hout, FORWARD_OFFERED, 0); } diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index 9cfccc5398e7..a27cb93ec442 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -163,9 +163,6 @@ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "json_to_txid called!\n"); abort(); } -/* Generated stub for json_to_u64 */ -bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) -{ fprintf(stderr, "json_to_u64 called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index b569eeeb7e4c..c3e4494264bc 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -169,9 +169,6 @@ bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "json_to_txid called!\n"); abort(); } -/* Generated stub for json_to_u64 */ -bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) -{ fprintf(stderr, "json_to_u64 called!\n"); abort(); } /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } diff --git a/wallet/wallet.h b/wallet/wallet.h index 0522274eb108..22bbfbf8fa34 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -165,7 +165,7 @@ bool string_to_forward_status(const char *status_str, size_t len, * already defined elements (adding is ok) /!\ */ enum forward_style { FORWARD_STYLE_LEGACY = 0, - FORWARD_STYLE_TLV = ONION_TLV_PAYLOAD, + FORWARD_STYLE_TLV = 1, FORWARD_STYLE_UNKNOWN = 2, /* Not actually in db, safe to renumber! */ }; From f00cc23f671643446577ee9c0da3e5b9a266fbc0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 28 Sep 2022 14:19:37 +0930 Subject: [PATCH 1510/1530] sphinx: rename confusing functions, ensure valid payloads. "sphinx_add_hop" takes a literal hop to include, "sphinx_add_modern_hop" prepends the length. Now we always prepend a length, make it clear that the literal version is a shortcut: * sphinx_add_hop -> sphinx_add_hop_has_length * sphinx_add_modern_hop -> sphinx_add_hop In addition, we check that length is actually correct! This means `createonion` can no longer create legacy or otherwise-invalid onions: fix tests and update man page to remove legacy usage. Signed-off-by: Rusty Russell Changelog-Changed: JSON-RPC: `createonion` no longer allows non-TLV-style payloads. --- common/sphinx.c | 24 +++++++++++++++++----- common/sphinx.h | 12 ++++++----- common/test/run-blindedpath_onion.c | 3 +-- common/test/run-onion-test-vector.c | 2 +- common/test/run-sphinx-xor_cipher_stream.c | 3 +++ common/test/run-sphinx.c | 3 +++ devtools/onion.c | 24 +++++----------------- doc/lightning-createonion.7.md | 11 +++++----- lightningd/onion_message.c | 2 +- lightningd/pay.c | 12 +++++++---- tests/test_pay.py | 12 +++++------ 11 files changed, 59 insertions(+), 49 deletions(-) diff --git a/common/sphinx.c b/common/sphinx.c index bf25c01aa947..fff2a2d297b5 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -103,17 +104,29 @@ size_t sphinx_path_payloads_size(const struct sphinx_path *path) return size; } -void sphinx_add_hop(struct sphinx_path *path, const struct pubkey *pubkey, - const u8 *payload TAKES) +bool sphinx_add_hop_has_length(struct sphinx_path *path, const struct pubkey *pubkey, + const u8 *payload TAKES) { struct sphinx_hop sp; + bigsize_t lenlen, prepended_len; + + /* You promised size was prepended! */ + if (tal_bytelen(payload) == 0) + return false; + lenlen = bigsize_get(payload, tal_bytelen(payload), &prepended_len); + if (add_overflows_u64(lenlen, prepended_len)) + return false; + if (lenlen + prepended_len != tal_bytelen(payload)) + return false; + sp.raw_payload = tal_dup_talarr(path, u8, payload); sp.pubkey = *pubkey; tal_arr_expand(&path->hops, sp); + return true; } -void sphinx_add_modern_hop(struct sphinx_path *path, const struct pubkey *pubkey, - const u8 *payload TAKES) +void sphinx_add_hop(struct sphinx_path *path, const struct pubkey *pubkey, + const u8 *payload TAKES) { u8 *with_len = tal_arr(NULL, u8, 0); size_t len = tal_bytelen(payload); @@ -122,7 +135,8 @@ void sphinx_add_modern_hop(struct sphinx_path *path, const struct pubkey *pubkey if (taken(payload)) tal_free(payload); - sphinx_add_hop(path, pubkey, take(with_len)); + if (!sphinx_add_hop_has_length(path, pubkey, take(with_len))) + abort(); } /* Small helper to append data to a buffer and update the position diff --git a/common/sphinx.h b/common/sphinx.h index 142cb3d6fa83..6ba537cf36d3 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -202,17 +202,19 @@ struct sphinx_path *sphinx_path_new_with_key(const tal_t *ctx, const struct secret *session_key); /** - * Add a payload hop to the path. + * Add a payload hop to the path (already has length prepended). + * + * Fails if length actually isn't prepended! */ -void sphinx_add_hop(struct sphinx_path *path, const struct pubkey *pubkey, - const u8 *payload TAKES); +bool sphinx_add_hop_has_length(struct sphinx_path *path, const struct pubkey *pubkey, + const u8 *payload TAKES); /** * Prepend length to payload and add: for onionmessage, any size is OK, * for HTLC onions tal_bytelen(payload) must be > 1. */ -void sphinx_add_modern_hop(struct sphinx_path *path, const struct pubkey *pubkey, - const u8 *payload TAKES); +void sphinx_add_hop(struct sphinx_path *path, const struct pubkey *pubkey, + const u8 *payload TAKES); /** * Compute the size of the serialized payloads. diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c index 3c3f44841c40..ef9711dca824 100644 --- a/common/test/run-blindedpath_onion.c +++ b/common/test/run-blindedpath_onion.c @@ -204,8 +204,7 @@ int main(int argc, char *argv[]) payload->encrypted_data_tlv = enctlv[i]; onionmsg_payload[i] = tal_arr(tmpctx, u8, 0); towire_tlv_onionmsg_payload(&onionmsg_payload[i], payload); - sphinx_add_modern_hop(sphinx_path, &alias[i], - onionmsg_payload[i]); + sphinx_add_hop(sphinx_path, &alias[i], onionmsg_payload[i]); } op = create_onionpacket(tmpctx, sphinx_path, ROUTING_INFO_SIZE, &path_secrets); diff --git a/common/test/run-onion-test-vector.c b/common/test/run-onion-test-vector.c index 2595ee9975d3..3effdb5b5b1b 100644 --- a/common/test/run-onion-test-vector.c +++ b/common/test/run-onion-test-vector.c @@ -162,7 +162,7 @@ int main(int argc, char *argv[]) max = tal_bytelen(payloads[i]); len = fromwire_bigsize(&cursor, &max); assert(len == max); - sphinx_add_modern_hop(sp, &k, take(tal_dup_arr(NULL, u8, cursor, max, 0))); + sphinx_add_hop(sp, &k, take(tal_dup_arr(NULL, u8, cursor, max, 0))); } assert(i == ARRAY_SIZE(payloads)); diff --git a/common/test/run-sphinx-xor_cipher_stream.c b/common/test/run-sphinx-xor_cipher_stream.c index 4212fab5d622..7875d5ed8d3c 100644 --- a/common/test/run-sphinx-xor_cipher_stream.c +++ b/common/test/run-sphinx-xor_cipher_stream.c @@ -35,6 +35,9 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for bigsize_get */ +size_t bigsize_get(const u8 *p UNNEEDED, size_t max UNNEEDED, bigsize_t *val UNNEEDED) +{ fprintf(stderr, "bigsize_get called!\n"); abort(); } /* Generated stub for fromwire */ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) { fprintf(stderr, "fromwire called!\n"); abort(); } diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index 7af35453bf12..8a1e07b445fc 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -45,6 +45,9 @@ struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u /* Generated stub for amount_tx_fee */ struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) { fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for bigsize_get */ +size_t bigsize_get(const u8 *p UNNEEDED, size_t max UNNEEDED, bigsize_t *val UNNEEDED) +{ fprintf(stderr, "bigsize_get called!\n"); abort(); } /* Generated stub for bigsize_put */ size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED) { fprintf(stderr, "bigsize_put called!\n"); abort(); } diff --git a/devtools/onion.c b/devtools/onion.c index 31bbcc56e7d4..58c76c85197b 100644 --- a/devtools/onion.c +++ b/devtools/onion.c @@ -67,7 +67,7 @@ static void do_generate(int argc, char **argv, if (!data) errx(1, "bad hex after / in %s", argv[1 + i]); - sphinx_add_hop(sp, &path[i], data); + sphinx_add_hop_has_length(sp, &path[i], data); } else { struct short_channel_id scid; struct amount_msat amt; @@ -76,13 +76,13 @@ static void do_generate(int argc, char **argv, memset(&scid, i, sizeof(scid)); amt = amount_msat(i); if (i == num_hops - 1) - sphinx_add_hop(sp, &path[i], + sphinx_add_hop_has_length(sp, &path[i], take(onion_final_hop(NULL, amt, i, amt, NULL, NULL, NULL, NULL))); else - sphinx_add_hop(sp, &path[i], + sphinx_add_hop_has_length(sp, &path[i], take(onion_nonfinal_hop(NULL, &scid, amt, i, @@ -225,27 +225,13 @@ static void runtest(const char *filename) /* Unpack the hops and build up the path */ hopstok = json_get_member(buffer, gentok, "hops"); json_for_each_arr(i, hop, hopstok) { - u8 *full; - size_t prepended; - payloadtok = json_get_member(buffer, hop, "payload"); typetok = json_get_member(buffer, hop, "type"); pubkeytok = json_get_member(buffer, hop, "pubkey"); payload = json_tok_bin_from_hex(ctx, buffer, payloadtok); json_to_pubkey(buffer, pubkeytok, &pubkey); - if (!typetok || json_tok_streq(buffer, typetok, "legacy")) { - /* Legacy has a single 0 prepended as "realm" byte */ - full = tal_arrz(ctx, u8, 33); - memcpy(full + 1, payload, tal_bytelen(payload)); - } else { - /* TLV has length prepended */ - full = tal_arr(ctx, u8, 0); - towire_bigsize(&full, tal_bytelen(payload)); - prepended = tal_bytelen(full); - tal_resize(&full, prepended + tal_bytelen(payload)); - memcpy(full + prepended, payload, tal_bytelen(payload)); - } - sphinx_add_hop(path, &pubkey, full); + assert(json_tok_streq(buffer, typetok, "tlv")); + sphinx_add_hop(path, &pubkey, take(payload)); } res = create_onionpacket(ctx, path, ROUTING_INFO_SIZE, &shared_secrets); serialized = serialize_onionpacket(ctx, res); diff --git a/doc/lightning-createonion.7.md b/doc/lightning-createonion.7.md index 75aa660ccd7f..bbeace8f2055 100644 --- a/doc/lightning-createonion.7.md +++ b/doc/lightning-createonion.7.md @@ -20,14 +20,13 @@ payload destined for that node. The following is an example of a 3 hop onion: [ { "pubkey": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", - "payload": "00000067000001000100000000000003e90000007b000000000000000000000000000000000000000000000000" + "payload": "11020203e904017b06080000670000010001" }, { "pubkey": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", - "payload": "00000067000003000100000000000003e800000075000000000000000000000000000000000000000000000000" + "payload": "11020203e804017506080000670000030001" }, { - "style": "legacy", "pubkey": "0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199", - "payload": "00000067000003000100000000000003e800000075000000000000000000000000000000000000000000000000" + "payload": "07020203e8040175" } ] ``` @@ -63,8 +62,8 @@ which the above *hops* parameter was generated: ] ``` - - Notice that the payload in the *hops* parameter is the hex-encoded version - of the parameters in the `getroute` response. + - Notice that the payload in the *hops* parameter is the hex-encoded TLV + of the parameters in the `getroute` response, with length prepended as a `bigsize_t`. - Except for the pubkey, the values are shifted left by one, i.e., the 1st payload in `createonion` corresponds to the 2nd set of values from `getroute`. - The final payload is a copy of the last payload sans `channel` diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 66cbafa70c3a..310ae68f22dc 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -281,7 +281,7 @@ static struct command_result *json_sendonionmessage2(struct command *cmd, /* Create an onion which encodes this. */ sphinx_path = sphinx_path_new(cmd, NULL); for (size_t i = 0; i < tal_count(hops); i++) - sphinx_add_modern_hop(sphinx_path, &hops[i].node, hops[i].tlv); + sphinx_add_hop(sphinx_path, &hops[i].node, hops[i].tlv); /* BOLT-onion-message #4: * - SHOULD set `len` to 1366 or 32834. diff --git a/lightningd/pay.c b/lightningd/pay.c index 07b2d09820fd..e8fe8abee64e 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1168,7 +1168,7 @@ send_payment(struct lightningd *ld, ret = pubkey_from_node_id(&pubkey, &ids[i]); assert(ret); - sphinx_add_hop(path, &pubkey, + sphinx_add_hop_has_length(path, &pubkey, take(onion_nonfinal_hop(NULL, &route[i + 1].scid, route[i + 1].amount, @@ -1200,7 +1200,7 @@ send_payment(struct lightningd *ld, "Destination does not support" " payment_secret"); } - sphinx_add_hop(path, &pubkey, onion); + sphinx_add_hop_has_length(path, &pubkey, onion); /* Copy channels used along the route. */ channels = tal_arr(tmpctx, struct short_channel_id, n_hops); @@ -1760,8 +1760,12 @@ static struct command_result *json_createonion(struct command *cmd, else sp = sphinx_path_new_with_key(cmd, assocdata, session_key); - for (size_t i=0; i *packet_size) return command_fail( diff --git a/tests/test_pay.py b/tests/test_pay.py index c6cf0844ebf8..f760b5f797c9 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3297,19 +3297,19 @@ def test_createonion_limits(node_factory): hops = [{ # privkey: 41bfd2660762506c9933ade59f1debf7e6495b10c14a92dbcd2d623da2507d3d "pubkey": "0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", - "payload": "00" * 228 + "payload": bytes([227] + [0] * 227).hex(), }, { "pubkey": "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c", - "payload": "00" * 228 + "payload": bytes([227] + [0] * 227).hex(), }, { "pubkey": "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007", - "payload": "00" * 228 + "payload": bytes([227] + [0] * 227).hex(), }, { "pubkey": "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991", - "payload": "00" * 228 + "payload": bytes([227] + [0] * 227).hex(), }, { "pubkey": "02edabbd16b41c8371b92ef2f04c1185b4f03b6dcd52ba9b78d9d7c89c8f221145", - "payload": "00" * 228 + "payload": bytes([227] + [0] * 227).hex(), }] # This should success since it's right at the edge @@ -3317,7 +3317,7 @@ def test_createonion_limits(node_factory): # This one should fail however with pytest.raises(RpcError, match=r'Payloads exceed maximum onion packet size.'): - hops[0]['payload'] += '01' + hops[0]['payload'] = bytes([228] + [0] * 228).hex() l1.rpc.createonion(hops=hops, assocdata="BB" * 32) # But with a larger onion, it will work! From 6324980484cdfe68e5228a56b74ac60542dc46d7 Mon Sep 17 00:00:00 2001 From: Matt Whitlock Date: Tue, 27 Sep 2022 00:37:38 -0400 Subject: [PATCH 1511/1530] channeld: do not enforce max_accepted_htlcs on LOCAL in channel reinit If a channel goes offline while the count of outstanding outgoing HTLCs exceeds the limit that we enforce against the peer, then the channel could never be brought online again because `add_htlc` called by `channel_force_htlcs` in `channeld/full_channel.c` would return `CHANNEL_ERR_TOO_MANY_HTLCS`. The protocol specification actually does allow us to exceed the limits that we are enforcing against the peer; we are only prohibited from exceeding the limits that the peer is enforcing against us. `add_htlc` takes an `enforce_aggregate_limits` parameter that appears to have been intended for `channel_force_htlcs` to exempt the local node from obeying the limits that it is enforcing against the peer, but this parameter was only being respected for the total HTLC value-in-flight check but not for the HTLC count check. This commit respects the parameter for the HTLC count check as well and resolves the problem of "Could not restore HTLCs". Fixes: #5636 Changelog-Fixed: channeld: Channel reinitialization no longer fails when the number of outstanding outgoing HTLCs exceeds `max_accepted_htlcs`. --- channeld/full_channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channeld/full_channel.c b/channeld/full_channel.c index c991315cd407..0395aa180f77 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -629,7 +629,7 @@ static enum channel_add_err add_htlc(struct channel *channel, * the channel to pay unnecessary onchain fees during a fee * spike with large commitment transactions. */ - if (sender == LOCAL + if (enforce_aggregate_limits && sender == LOCAL && htlc_count + 1 > channel->config[LOCAL].max_accepted_htlcs) { return CHANNEL_ERR_TOO_MANY_HTLCS; } From 51e243308761e056d0da23e596b7dfedbb909db1 Mon Sep 17 00:00:00 2001 From: Justin Litchfield Date: Thu, 15 Sep 2022 10:48:05 -0500 Subject: [PATCH 1512/1530] Setchannel request is provided --- .msggen.json | 21 ++++++++ cln-grpc/proto/node.proto | 25 +++++++++ cln-grpc/src/convert.rs | 49 +++++++++++++++++ cln-grpc/src/server.rs | 32 ++++++++++++ cln-rpc/src/model.rs | 44 ++++++++++++++++ contrib/msggen/msggen/utils/utils.py | 1 + contrib/pyln-testing/pyln/testing/grpc2py.py | 20 +++++++ contrib/pyln-testing/pyln/testing/node_pb2.py | 52 +++++++++++++++---- .../pyln/testing/node_pb2_grpc.py | 33 ++++++++++++ doc/schemas/setchannel.request.json | 25 +++++++++ 10 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 doc/schemas/setchannel.request.json diff --git a/.msggen.json b/.msggen.json index 0b77b209d9ae..91f5fd80985e 100644 --- a/.msggen.json +++ b/.msggen.json @@ -973,6 +973,27 @@ "SendPsbt.tx": 1, "SendPsbt.txid": 2 }, + "SetchannelChannels": { + "SetChannel.channels[].channel_id": 2, + "SetChannel.channels[].fee_base_msat": 4, + "SetChannel.channels[].fee_proportional_millionths": 5, + "SetChannel.channels[].maximum_htlc_out_msat": 8, + "SetChannel.channels[].minimum_htlc_out_msat": 6, + "SetChannel.channels[].peer_id": 1, + "SetChannel.channels[].short_channel_id": 3, + "SetChannel.channels[].warning_htlcmax_too_high": 9, + "SetChannel.channels[].warning_htlcmin_too_low": 7 + }, + "SetchannelRequest": { + "SetChannel.feebase": 2, + "SetChannel.feeppm": 3, + "SetChannel.htlcmax": 5, + "SetChannel.htlcmin": 4, + "SetChannel.id": 1 + }, + "SetchannelResponse": { + "SetChannel.channels[]": 1 + }, "SignmessageRequest": { "SignMessage.message": 1 }, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index d939cab59807..1e7ded28097b 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -52,6 +52,7 @@ service Node { rpc ListForwards(ListforwardsRequest) returns (ListforwardsResponse) {} rpc ListPays(ListpaysRequest) returns (ListpaysResponse) {} rpc Ping(PingRequest) returns (PingResponse) {} + rpc SetChannel(SetchannelRequest) returns (SetchannelResponse) {} rpc SignMessage(SignmessageRequest) returns (SignmessageResponse) {} rpc Stop(StopRequest) returns (StopResponse) {} } @@ -1287,6 +1288,30 @@ message PingResponse { uint32 totlen = 1; } +message SetchannelRequest { + string id = 1; + optional Amount feebase = 2; + optional uint32 feeppm = 3; + optional Amount htlcmin = 4; + optional Amount htlcmax = 5; +} + +message SetchannelResponse { + repeated SetchannelChannels channels = 1; +} + +message SetchannelChannels { + bytes peer_id = 1; + bytes channel_id = 2; + optional string short_channel_id = 3; + Amount fee_base_msat = 4; + uint32 fee_proportional_millionths = 5; + Amount minimum_htlc_out_msat = 6; + optional string warning_htlcmin_too_low = 7; + Amount maximum_htlc_out_msat = 8; + optional string warning_htlcmax_too_high = 9; +} + message SignmessageRequest { string message = 1; } diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index 79a899b4c2ca..f7b9c48bb9e2 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -961,8 +961,39 @@ impl From for pb::PingResponse { } #[allow(unused_variables)] +<<<<<<< HEAD impl From for pb::SignmessageResponse { fn from(c: responses::SignmessageResponse) -> Self { +======= +impl From<&responses::SetchannelChannels> for pb::SetchannelChannels { + fn from(c: &responses::SetchannelChannels) -> Self { + Self { + peer_id: c.peer_id.to_vec(), // Rule #2 for type pubkey + channel_id: hex::decode(&c.channel_id).unwrap(), // Rule #2 for type hex + short_channel_id: c.short_channel_id.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? + fee_base_msat: Some(c.fee_base_msat.into()), // Rule #2 for type msat + fee_proportional_millionths: c.fee_proportional_millionths.clone(), // Rule #2 for type u32 + minimum_htlc_out_msat: Some(c.minimum_htlc_out_msat.into()), // Rule #2 for type msat + warning_htlcmin_too_low: c.warning_htlcmin_too_low.clone(), // Rule #2 for type string? + maximum_htlc_out_msat: Some(c.maximum_htlc_out_msat.into()), // Rule #2 for type msat + warning_htlcmax_too_high: c.warning_htlcmax_too_high.clone(), // Rule #2 for type string? + } + } +} + +#[allow(unused_variables)] +impl From<&responses::SetchannelResponse> for pb::SetchannelResponse { + fn from(c: &responses::SetchannelResponse) -> Self { + Self { + channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type SetchannelChannels + } + } +} + +#[allow(unused_variables)] +impl From<&responses::SignmessageResponse> for pb::SignmessageResponse { + fn from(c: &responses::SignmessageResponse) -> Self { +>>>>>>> bd301acdc... Setchannel request is provided Self { signature: hex::decode(&c.signature).unwrap(), // Rule #2 for type hex recid: hex::decode(&c.recid).unwrap(), // Rule #2 for type hex @@ -1523,8 +1554,26 @@ impl From for requests::PingRequest { } #[allow(unused_variables)] +<<<<<<< HEAD impl From for requests::SignmessageRequest { fn from(c: pb::SignmessageRequest) -> Self { +======= +impl From<&pb::SetchannelRequest> for requests::SetchannelRequest { + fn from(c: &pb::SetchannelRequest) -> Self { + Self { + id: c.id.clone(), // Rule #1 for type string + feebase: c.feebase.as_ref().map(|a| a.into()), // Rule #1 for type msat? + feeppm: c.feeppm.clone(), // Rule #1 for type u32? + htlcmin: c.htlcmin.as_ref().map(|a| a.into()), // Rule #1 for type msat? + htlcmax: c.htlcmax.as_ref().map(|a| a.into()), // Rule #1 for type msat? + } + } +} + +#[allow(unused_variables)] +impl From<&pb::SignmessageRequest> for requests::SignmessageRequest { + fn from(c: &pb::SignmessageRequest) -> Self { +>>>>>>> bd301acdc... Setchannel request is provided Self { message: c.message, // Rule #1 for type string } diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 22b6a5c09529..3196023e3db5 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -1434,6 +1434,38 @@ async fn ping( } +async fn set_channel( + &self, + request: tonic::Request, +) -> Result, tonic::Status> { + let req = request.into_inner(); + let req: requests::SetchannelRequest = (&req).into(); + debug!("Client asked for set_channel"); + trace!("set_channel request: {:?}", req); + let mut rpc = ClnRpc::new(&self.rpc_path) + .await + .map_err(|e| Status::new(Code::Internal, e.to_string()))?; + let result = rpc.call(Request::SetChannel(req)) + .await + .map_err(|e| Status::new( + Code::Unknown, + format!("Error calling method SetChannel: {:?}", e)))?; + match result { + Response::SetChannel(r) => { + trace!("set_channel response: {:?}", r); + Ok(tonic::Response::new((&r).into())) + }, + r => Err(Status::new( + Code::Internal, + format!( + "Unexpected result {:?} to method call SetChannel", + r + ) + )), + } + +} + async fn sign_message( &self, request: tonic::Request, diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 3ac21159fd7c..3875d8157bc0 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -60,6 +60,7 @@ pub enum Request { ListForwards(requests::ListforwardsRequest), ListPays(requests::ListpaysRequest), Ping(requests::PingRequest), + SetChannel(requests::SetchannelRequest), SignMessage(requests::SignmessageRequest), Stop(requests::StopRequest), } @@ -112,6 +113,7 @@ pub enum Response { ListForwards(responses::ListforwardsResponse), ListPays(responses::ListpaysResponse), Ping(responses::PingResponse), + SetChannel(responses::SetchannelResponse), SignMessage(responses::SignmessageResponse), Stop(responses::StopResponse), } @@ -1274,6 +1276,20 @@ pub mod requests { type Response = super::responses::PingResponse; } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetchannelRequest { + #[serde(alias = "id")] + pub id: String, + #[serde(alias = "feebase", skip_serializing_if = "Option::is_none")] + pub feebase: Option, + #[serde(alias = "feeppm", skip_serializing_if = "Option::is_none")] + pub feeppm: Option, + #[serde(alias = "htlcmin", skip_serializing_if = "Option::is_none")] + pub htlcmin: Option, + #[serde(alias = "htlcmax", skip_serializing_if = "Option::is_none")] + pub htlcmax: Option, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignmessageRequest { #[serde(alias = "message")] @@ -3717,6 +3733,34 @@ pub mod responses { } } + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetchannelChannels { + #[serde(alias = "peer_id")] + pub peer_id: Pubkey, + #[serde(alias = "channel_id")] + pub channel_id: String, + #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] + pub short_channel_id: Option, + #[serde(alias = "fee_base_msat")] + pub fee_base_msat: Amount, + #[serde(alias = "fee_proportional_millionths")] + pub fee_proportional_millionths: u32, + #[serde(alias = "minimum_htlc_out_msat")] + pub minimum_htlc_out_msat: Amount, + #[serde(alias = "warning_htlcmin_too_low", skip_serializing_if = "Option::is_none")] + pub warning_htlcmin_too_low: Option, + #[serde(alias = "maximum_htlc_out_msat")] + pub maximum_htlc_out_msat: Amount, + #[serde(alias = "warning_htlcmax_too_high", skip_serializing_if = "Option::is_none")] + pub warning_htlcmax_too_high: Option, + } + + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct SetchannelResponse { + #[serde(alias = "channels")] + pub channels: Vec, + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignmessageResponse { #[serde(alias = "signature")] diff --git a/contrib/msggen/msggen/utils/utils.py b/contrib/msggen/msggen/utils/utils.py index bc13d4c165b6..2290b89d0091 100644 --- a/contrib/msggen/msggen/utils/utils.py +++ b/contrib/msggen/msggen/utils/utils.py @@ -107,6 +107,7 @@ def load_jsonrpc_service(schema_dir: str = None): # "sendinvoice", # "sendonionmessage", # "setchannelfee", + "SetChannel", "SignMessage", # "unreserveinputs", # "waitblockheight", diff --git a/contrib/pyln-testing/pyln/testing/grpc2py.py b/contrib/pyln-testing/pyln/testing/grpc2py.py index 162f47ba1bee..a2ccffdc3960 100644 --- a/contrib/pyln-testing/pyln/testing/grpc2py.py +++ b/contrib/pyln-testing/pyln/testing/grpc2py.py @@ -849,6 +849,26 @@ def ping2py(m): }) +def setchannel_channels2py(m): + return remove_default({ + "peer_id": hexlify(m.peer_id), # PrimitiveField in generate_composite + "channel_id": hexlify(m.channel_id), # PrimitiveField in generate_composite + "short_channel_id": m.short_channel_id, # PrimitiveField in generate_composite + "fee_base_msat": amount2msat(m.fee_base_msat), # PrimitiveField in generate_composite + "fee_proportional_millionths": m.fee_proportional_millionths, # PrimitiveField in generate_composite + "minimum_htlc_out_msat": amount2msat(m.minimum_htlc_out_msat), # PrimitiveField in generate_composite + "warning_htlcmin_too_low": m.warning_htlcmin_too_low, # PrimitiveField in generate_composite + "maximum_htlc_out_msat": amount2msat(m.maximum_htlc_out_msat), # PrimitiveField in generate_composite + "warning_htlcmax_too_high": m.warning_htlcmax_too_high, # PrimitiveField in generate_composite + }) + + +def setchannel2py(m): + return remove_default({ + "channels": [setchannel_channels2py(i) for i in m.channels], # ArrayField[composite] in generate_composite + }) + + def signmessage2py(m): return remove_default({ "signature": hexlify(m.signature), # PrimitiveField in generate_composite diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index b1550f73775a..a370de42415c 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xbc\x02\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xf0\x16\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xbc\x02\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"\xcc\x01\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmax\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\x94\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x01\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xb1\x17\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -142,6 +142,9 @@ _LISTPAYSPAYS = DESCRIPTOR.message_types_by_name['ListpaysPays'] _PINGREQUEST = DESCRIPTOR.message_types_by_name['PingRequest'] _PINGRESPONSE = DESCRIPTOR.message_types_by_name['PingResponse'] +_SETCHANNELREQUEST = DESCRIPTOR.message_types_by_name['SetchannelRequest'] +_SETCHANNELRESPONSE = DESCRIPTOR.message_types_by_name['SetchannelResponse'] +_SETCHANNELCHANNELS = DESCRIPTOR.message_types_by_name['SetchannelChannels'] _SIGNMESSAGEREQUEST = DESCRIPTOR.message_types_by_name['SignmessageRequest'] _SIGNMESSAGERESPONSE = DESCRIPTOR.message_types_by_name['SignmessageResponse'] _STOPREQUEST = DESCRIPTOR.message_types_by_name['StopRequest'] @@ -1041,6 +1044,27 @@ }) _sym_db.RegisterMessage(PingResponse) +SetchannelRequest = _reflection.GeneratedProtocolMessageType('SetchannelRequest', (_message.Message,), { + 'DESCRIPTOR' : _SETCHANNELREQUEST, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SetchannelRequest) + }) +_sym_db.RegisterMessage(SetchannelRequest) + +SetchannelResponse = _reflection.GeneratedProtocolMessageType('SetchannelResponse', (_message.Message,), { + 'DESCRIPTOR' : _SETCHANNELRESPONSE, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SetchannelResponse) + }) +_sym_db.RegisterMessage(SetchannelResponse) + +SetchannelChannels = _reflection.GeneratedProtocolMessageType('SetchannelChannels', (_message.Message,), { + 'DESCRIPTOR' : _SETCHANNELCHANNELS, + '__module__' : 'node_pb2' + # @@protoc_insertion_point(class_scope:cln.SetchannelChannels) + }) +_sym_db.RegisterMessage(SetchannelChannels) + SignmessageRequest = _reflection.GeneratedProtocolMessageType('SignmessageRequest', (_message.Message,), { 'DESCRIPTOR' : _SIGNMESSAGEREQUEST, '__module__' : 'node_pb2' @@ -1387,14 +1411,20 @@ _PINGREQUEST._serialized_end=27855 _PINGRESPONSE._serialized_start=27857 _PINGRESPONSE._serialized_end=27887 - _SIGNMESSAGEREQUEST._serialized_start=27889 - _SIGNMESSAGEREQUEST._serialized_end=27926 - _SIGNMESSAGERESPONSE._serialized_start=27928 - _SIGNMESSAGERESPONSE._serialized_end=27998 - _STOPREQUEST._serialized_start=28000 - _STOPREQUEST._serialized_end=28013 - _STOPRESPONSE._serialized_start=28015 - _STOPRESPONSE._serialized_end=28029 - _NODE._serialized_start=28032 - _NODE._serialized_end=30960 + _SETCHANNELREQUEST._serialized_start=27890 + _SETCHANNELREQUEST._serialized_end=28094 + _SETCHANNELRESPONSE._serialized_start=28096 + _SETCHANNELRESPONSE._serialized_end=28159 + _SETCHANNELCHANNELS._serialized_start=28162 + _SETCHANNELCHANNELS._serialized_end=28566 + _SIGNMESSAGEREQUEST._serialized_start=28568 + _SIGNMESSAGEREQUEST._serialized_end=28605 + _SIGNMESSAGERESPONSE._serialized_start=28607 + _SIGNMESSAGERESPONSE._serialized_end=28677 + _STOPREQUEST._serialized_start=28679 + _STOPREQUEST._serialized_end=28692 + _STOPRESPONSE._serialized_start=28694 + _STOPRESPONSE._serialized_end=28708 + _NODE._serialized_start=28711 + _NODE._serialized_end=31704 # @@protoc_insertion_point(module_scope) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py index f9a0e10955c8..c682d92b6bb9 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2_grpc.py @@ -234,6 +234,11 @@ def __init__(self, channel): request_serializer=node__pb2.PingRequest.SerializeToString, response_deserializer=node__pb2.PingResponse.FromString, ) + self.SetChannel = channel.unary_unary( + '/cln.Node/SetChannel', + request_serializer=node__pb2.SetchannelRequest.SerializeToString, + response_deserializer=node__pb2.SetchannelResponse.FromString, + ) self.SignMessage = channel.unary_unary( '/cln.Node/SignMessage', request_serializer=node__pb2.SignmessageRequest.SerializeToString, @@ -513,6 +518,12 @@ def Ping(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def SetChannel(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def SignMessage(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -748,6 +759,11 @@ def add_NodeServicer_to_server(servicer, server): request_deserializer=node__pb2.PingRequest.FromString, response_serializer=node__pb2.PingResponse.SerializeToString, ), + 'SetChannel': grpc.unary_unary_rpc_method_handler( + servicer.SetChannel, + request_deserializer=node__pb2.SetchannelRequest.FromString, + response_serializer=node__pb2.SetchannelResponse.SerializeToString, + ), 'SignMessage': grpc.unary_unary_rpc_method_handler( servicer.SignMessage, request_deserializer=node__pb2.SignmessageRequest.FromString, @@ -1516,6 +1532,23 @@ def Ping(request, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def SetChannel(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/cln.Node/SetChannel', + node__pb2.SetchannelRequest.SerializeToString, + node__pb2.SetchannelResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def SignMessage(request, target, diff --git a/doc/schemas/setchannel.request.json b/doc/schemas/setchannel.request.json new file mode 100644 index 000000000000..871e2e416dd9 --- /dev/null +++ b/doc/schemas/setchannel.request.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ + "id" + ], + "properties": { + "id": { + "type": "string" + }, + "feebase": { + "type": "msat" + }, + "feeppm": { + "type": "u32" + }, + "htlcmin": { + "type": "msat" + }, + "htlcmax": { + "type": "msat" + } + } +} From 93b315756f80cbd9a112c0ca627243017c55213f Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 21 Sep 2022 15:53:42 +0200 Subject: [PATCH 1513/1530] schema: Add `enforcedelay` to `setchannel` This was missing in the last version --- .msggen.json | 1 + cln-grpc/proto/node.proto | 1 + cln-grpc/src/convert.rs | 53 ++++++++++++----------------- cln-grpc/src/server.rs | 4 +-- cln-rpc/src/model.rs | 25 +++++++++++++- doc/schemas/setchannel.request.json | 3 ++ 6 files changed, 53 insertions(+), 34 deletions(-) diff --git a/.msggen.json b/.msggen.json index 91f5fd80985e..3fde731f9752 100644 --- a/.msggen.json +++ b/.msggen.json @@ -985,6 +985,7 @@ "SetChannel.channels[].warning_htlcmin_too_low": 7 }, "SetchannelRequest": { + "SetChannel.enforcedelay": 6, "SetChannel.feebase": 2, "SetChannel.feeppm": 3, "SetChannel.htlcmax": 5, diff --git a/cln-grpc/proto/node.proto b/cln-grpc/proto/node.proto index 1e7ded28097b..84d513e43a43 100644 --- a/cln-grpc/proto/node.proto +++ b/cln-grpc/proto/node.proto @@ -1294,6 +1294,7 @@ message SetchannelRequest { optional uint32 feeppm = 3; optional Amount htlcmin = 4; optional Amount htlcmax = 5; + optional uint32 enforcedelay = 6; } message SetchannelResponse { diff --git a/cln-grpc/src/convert.rs b/cln-grpc/src/convert.rs index f7b9c48bb9e2..3fe2ac0c5a1c 100644 --- a/cln-grpc/src/convert.rs +++ b/cln-grpc/src/convert.rs @@ -961,39 +961,34 @@ impl From for pb::PingResponse { } #[allow(unused_variables)] -<<<<<<< HEAD -impl From for pb::SignmessageResponse { - fn from(c: responses::SignmessageResponse) -> Self { -======= -impl From<&responses::SetchannelChannels> for pb::SetchannelChannels { - fn from(c: &responses::SetchannelChannels) -> Self { +impl From for pb::SetchannelChannels { + fn from(c: responses::SetchannelChannels) -> Self { Self { - peer_id: c.peer_id.to_vec(), // Rule #2 for type pubkey + peer_id: c.peer_id.serialize().to_vec(), // Rule #2 for type pubkey channel_id: hex::decode(&c.channel_id).unwrap(), // Rule #2 for type hex - short_channel_id: c.short_channel_id.as_ref().map(|v| v.to_string()), // Rule #2 for type short_channel_id? + short_channel_id: c.short_channel_id.map(|v| v.to_string()), // Rule #2 for type short_channel_id? fee_base_msat: Some(c.fee_base_msat.into()), // Rule #2 for type msat - fee_proportional_millionths: c.fee_proportional_millionths.clone(), // Rule #2 for type u32 + fee_proportional_millionths: c.fee_proportional_millionths, // Rule #2 for type u32 minimum_htlc_out_msat: Some(c.minimum_htlc_out_msat.into()), // Rule #2 for type msat - warning_htlcmin_too_low: c.warning_htlcmin_too_low.clone(), // Rule #2 for type string? + warning_htlcmin_too_low: c.warning_htlcmin_too_low, // Rule #2 for type string? maximum_htlc_out_msat: Some(c.maximum_htlc_out_msat.into()), // Rule #2 for type msat - warning_htlcmax_too_high: c.warning_htlcmax_too_high.clone(), // Rule #2 for type string? + warning_htlcmax_too_high: c.warning_htlcmax_too_high, // Rule #2 for type string? } } } #[allow(unused_variables)] -impl From<&responses::SetchannelResponse> for pb::SetchannelResponse { - fn from(c: &responses::SetchannelResponse) -> Self { +impl From for pb::SetchannelResponse { + fn from(c: responses::SetchannelResponse) -> Self { Self { - channels: c.channels.iter().map(|i| i.into()).collect(), // Rule #3 for type SetchannelChannels + channels: c.channels.into_iter().map(|i| i.into()).collect(), // Rule #3 for type SetchannelChannels } } } #[allow(unused_variables)] -impl From<&responses::SignmessageResponse> for pb::SignmessageResponse { - fn from(c: &responses::SignmessageResponse) -> Self { ->>>>>>> bd301acdc... Setchannel request is provided +impl From for pb::SignmessageResponse { + fn from(c: responses::SignmessageResponse) -> Self { Self { signature: hex::decode(&c.signature).unwrap(), // Rule #2 for type hex recid: hex::decode(&c.recid).unwrap(), // Rule #2 for type hex @@ -1554,26 +1549,22 @@ impl From for requests::PingRequest { } #[allow(unused_variables)] -<<<<<<< HEAD -impl From for requests::SignmessageRequest { - fn from(c: pb::SignmessageRequest) -> Self { -======= -impl From<&pb::SetchannelRequest> for requests::SetchannelRequest { - fn from(c: &pb::SetchannelRequest) -> Self { +impl From for requests::SetchannelRequest { + fn from(c: pb::SetchannelRequest) -> Self { Self { - id: c.id.clone(), // Rule #1 for type string - feebase: c.feebase.as_ref().map(|a| a.into()), // Rule #1 for type msat? - feeppm: c.feeppm.clone(), // Rule #1 for type u32? - htlcmin: c.htlcmin.as_ref().map(|a| a.into()), // Rule #1 for type msat? - htlcmax: c.htlcmax.as_ref().map(|a| a.into()), // Rule #1 for type msat? + id: c.id, // Rule #1 for type string + feebase: c.feebase.map(|a| a.into()), // Rule #1 for type msat? + feeppm: c.feeppm, // Rule #1 for type u32? + htlcmin: c.htlcmin.map(|a| a.into()), // Rule #1 for type msat? + htlcmax: c.htlcmax.map(|a| a.into()), // Rule #1 for type msat? + enforcedelay: c.enforcedelay, // Rule #1 for type u32? } } } #[allow(unused_variables)] -impl From<&pb::SignmessageRequest> for requests::SignmessageRequest { - fn from(c: &pb::SignmessageRequest) -> Self { ->>>>>>> bd301acdc... Setchannel request is provided +impl From for requests::SignmessageRequest { + fn from(c: pb::SignmessageRequest) -> Self { Self { message: c.message, // Rule #1 for type string } diff --git a/cln-grpc/src/server.rs b/cln-grpc/src/server.rs index 3196023e3db5..7759ca0a6a3a 100644 --- a/cln-grpc/src/server.rs +++ b/cln-grpc/src/server.rs @@ -1439,7 +1439,7 @@ async fn set_channel( request: tonic::Request, ) -> Result, tonic::Status> { let req = request.into_inner(); - let req: requests::SetchannelRequest = (&req).into(); + let req: requests::SetchannelRequest = req.into(); debug!("Client asked for set_channel"); trace!("set_channel request: {:?}", req); let mut rpc = ClnRpc::new(&self.rpc_path) @@ -1453,7 +1453,7 @@ async fn set_channel( match result { Response::SetChannel(r) => { trace!("set_channel response: {:?}", r); - Ok(tonic::Response::new((&r).into())) + Ok(tonic::Response::new(r.into())) }, r => Err(Status::new( Code::Internal, diff --git a/cln-rpc/src/model.rs b/cln-rpc/src/model.rs index 3875d8157bc0..25e23fdbef30 100644 --- a/cln-rpc/src/model.rs +++ b/cln-rpc/src/model.rs @@ -1288,6 +1288,18 @@ pub mod requests { pub htlcmin: Option, #[serde(alias = "htlcmax", skip_serializing_if = "Option::is_none")] pub htlcmax: Option, + #[serde(alias = "enforcedelay", skip_serializing_if = "Option::is_none")] + pub enforcedelay: Option, + } + + impl From for Request { + fn from(r: SetchannelRequest) -> Self { + Request::SetChannel(r) + } + } + + impl IntoRequest for SetchannelRequest { + type Response = super::responses::SetchannelResponse; } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -3736,7 +3748,7 @@ pub mod responses { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SetchannelChannels { #[serde(alias = "peer_id")] - pub peer_id: Pubkey, + pub peer_id: PublicKey, #[serde(alias = "channel_id")] pub channel_id: String, #[serde(alias = "short_channel_id", skip_serializing_if = "Option::is_none")] @@ -3761,6 +3773,17 @@ pub mod responses { pub channels: Vec, } + impl TryFrom for SetchannelResponse { + type Error = super::TryFromResponseError; + + fn try_from(response: Response) -> Result { + match response { + Response::SetChannel(response) => Ok(response), + _ => Err(TryFromResponseError) + } + } + } + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct SignmessageResponse { #[serde(alias = "signature")] diff --git a/doc/schemas/setchannel.request.json b/doc/schemas/setchannel.request.json index 871e2e416dd9..fd47ffbc91b2 100644 --- a/doc/schemas/setchannel.request.json +++ b/doc/schemas/setchannel.request.json @@ -20,6 +20,9 @@ }, "htlcmax": { "type": "msat" + }, + "enforcedelay": { + "type": "u32" } } } From 6adb1e0b4b419e723d53b1e54e16593f58656986 Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Wed, 21 Sep 2022 15:54:11 +0200 Subject: [PATCH 1514/1530] pytest: Bypass schema verification for some RPC calls The goal here is to test the node validation, not whether we can trigger the schema validation with bogus values. So we bypass the verifying RPC wrapper. --- contrib/pyln-testing/pyln/testing/node_pb2.py | 32 +++++++++---------- tests/test_pay.py | 22 +++++++++++-- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/contrib/pyln-testing/pyln/testing/node_pb2.py b/contrib/pyln-testing/pyln/testing/node_pb2.py index a370de42415c..a5f035c8b1a4 100644 --- a/contrib/pyln-testing/pyln/testing/node_pb2.py +++ b/contrib/pyln-testing/pyln/testing/node_pb2.py @@ -15,7 +15,7 @@ from . import primitives_pb2 as primitives__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xbc\x02\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"\xcc\x01\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmax\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\x94\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x01\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xb1\x17\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nnode.proto\x12\x03\x63ln\x1a\x10primitives.proto\"\x10\n\x0eGetinfoRequest\"\xec\x03\n\x0fGetinfoResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\r\n\x05\x61lias\x18\x02 \x01(\t\x12\r\n\x05\x63olor\x18\x03 \x01(\x0c\x12\x11\n\tnum_peers\x18\x04 \x01(\r\x12\x1c\n\x14num_pending_channels\x18\x05 \x01(\r\x12\x1b\n\x13num_active_channels\x18\x06 \x01(\r\x12\x1d\n\x15num_inactive_channels\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\t\x12\x15\n\rlightning_dir\x18\t \x01(\t\x12\x13\n\x0b\x62lockheight\x18\x0b \x01(\r\x12\x0f\n\x07network\x18\x0c \x01(\t\x12(\n\x13\x66\x65\x65s_collected_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x07\x61\x64\x64ress\x18\x0e \x03(\x0b\x32\x13.cln.GetinfoAddress\x12$\n\x07\x62inding\x18\x0f \x03(\x0b\x32\x13.cln.GetinfoBinding\x12\"\n\x15warning_bitcoind_sync\x18\x10 \x01(\tH\x00\x88\x01\x01\x12$\n\x17warning_lightningd_sync\x18\x11 \x01(\tH\x01\x88\x01\x01\x42\x18\n\x16_warning_bitcoind_syncB\x1a\n\x18_warning_lightningd_sync\"S\n\x13GetinfoOur_features\x12\x0c\n\x04init\x18\x01 \x01(\x0c\x12\x0c\n\x04node\x18\x02 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x03 \x01(\x0c\x12\x0f\n\x07invoice\x18\x04 \x01(\x0c\"\xd3\x01\n\x0eGetinfoAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoAddress.GetinfoAddressType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"V\n\x12GetinfoAddressType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"\xfb\x01\n\x0eGetinfoBinding\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.GetinfoBinding.GetinfoBindingType\x12\x14\n\x07\x61\x64\x64ress\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06socket\x18\x04 \x01(\tH\x02\x88\x01\x01\"P\n\x12GetinfoBindingType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\n\n\x08_addressB\x07\n\x05_portB\t\n\x07_socket\"H\n\x10ListpeersRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05level\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x05\n\x03_idB\x08\n\x06_level\"7\n\x11ListpeersResponse\x12\"\n\x05peers\x18\x01 \x03(\x0b\x32\x13.cln.ListpeersPeers\"\xe2\x01\n\x0eListpeersPeers\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x11\n\tconnected\x18\x02 \x01(\x08\x12#\n\x03log\x18\x03 \x03(\x0b\x32\x16.cln.ListpeersPeersLog\x12-\n\x08\x63hannels\x18\x04 \x03(\x0b\x32\x1b.cln.ListpeersPeersChannels\x12\x0f\n\x07netaddr\x18\x05 \x03(\t\x12\x18\n\x0bremote_addr\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x42\x0e\n\x0c_remote_addrB\x0b\n\t_features\"\xfd\x02\n\x11ListpeersPeersLog\x12?\n\titem_type\x18\x01 \x01(\x0e\x32,.cln.ListpeersPeersLog.ListpeersPeersLogType\x12\x18\n\x0bnum_skipped\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x11\n\x04time\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06source\x18\x04 \x01(\tH\x02\x88\x01\x01\x12\x10\n\x03log\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x07node_id\x18\x06 \x01(\x0cH\x04\x88\x01\x01\x12\x11\n\x04\x64\x61ta\x18\x07 \x01(\x0cH\x05\x88\x01\x01\"i\n\x15ListpeersPeersLogType\x12\x0b\n\x07SKIPPED\x10\x00\x12\n\n\x06\x42ROKEN\x10\x01\x12\x0b\n\x07UNUSUAL\x10\x02\x12\x08\n\x04INFO\x10\x03\x12\t\n\x05\x44\x45\x42UG\x10\x04\x12\t\n\x05IO_IN\x10\x05\x12\n\n\x06IO_OUT\x10\x06\x42\x0e\n\x0c_num_skippedB\x07\n\x05_timeB\t\n\x07_sourceB\x06\n\x04_logB\n\n\x08_node_idB\x07\n\x05_data\"\x8a\x16\n\x16ListpeersPeersChannels\x12\x46\n\x05state\x18\x01 \x01(\x0e\x32\x37.cln.ListpeersPeersChannels.ListpeersPeersChannelsState\x12\x19\n\x0cscratch_txid\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\x05owner\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10short_channel_id\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x17\n\nchannel_id\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x19\n\x0c\x66unding_txid\x18\x07 \x01(\x0cH\x04\x88\x01\x01\x12\x1b\n\x0e\x66unding_outnum\x18\x08 \x01(\rH\x05\x88\x01\x01\x12\x1c\n\x0finitial_feerate\x18\t \x01(\tH\x06\x88\x01\x01\x12\x19\n\x0clast_feerate\x18\n \x01(\tH\x07\x88\x01\x01\x12\x19\n\x0cnext_feerate\x18\x0b \x01(\tH\x08\x88\x01\x01\x12\x1a\n\rnext_fee_step\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x35\n\x08inflight\x18\r \x03(\x0b\x32#.cln.ListpeersPeersChannelsInflight\x12\x15\n\x08\x63lose_to\x18\x0e \x01(\x0cH\n\x88\x01\x01\x12\x14\n\x07private\x18\x0f \x01(\x08H\x0b\x88\x01\x01\x12 \n\x06opener\x18\x10 \x01(\x0e\x32\x10.cln.ChannelSide\x12%\n\x06\x63loser\x18\x11 \x01(\x0e\x32\x10.cln.ChannelSideH\x0c\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x12 \x03(\t\x12$\n\nto_us_msat\x18\x14 \x01(\x0b\x32\x0b.cln.AmountH\r\x88\x01\x01\x12(\n\x0emin_to_us_msat\x18\x15 \x01(\x0b\x32\x0b.cln.AmountH\x0e\x88\x01\x01\x12(\n\x0emax_to_us_msat\x18\x16 \x01(\x0b\x32\x0b.cln.AmountH\x0f\x88\x01\x01\x12$\n\ntotal_msat\x18\x17 \x01(\x0b\x32\x0b.cln.AmountH\x10\x88\x01\x01\x12\'\n\rfee_base_msat\x18\x18 \x01(\x0b\x32\x0b.cln.AmountH\x11\x88\x01\x01\x12(\n\x1b\x66\x65\x65_proportional_millionths\x18\x19 \x01(\rH\x12\x88\x01\x01\x12)\n\x0f\x64ust_limit_msat\x18\x1a \x01(\x0b\x32\x0b.cln.AmountH\x13\x88\x01\x01\x12\x30\n\x16max_total_htlc_in_msat\x18\x1b \x01(\x0b\x32\x0b.cln.AmountH\x14\x88\x01\x01\x12,\n\x12their_reserve_msat\x18\x1c \x01(\x0b\x32\x0b.cln.AmountH\x15\x88\x01\x01\x12*\n\x10our_reserve_msat\x18\x1d \x01(\x0b\x32\x0b.cln.AmountH\x16\x88\x01\x01\x12(\n\x0espendable_msat\x18\x1e \x01(\x0b\x32\x0b.cln.AmountH\x17\x88\x01\x01\x12)\n\x0freceivable_msat\x18\x1f \x01(\x0b\x32\x0b.cln.AmountH\x18\x88\x01\x01\x12.\n\x14minimum_htlc_in_msat\x18 \x01(\x0b\x32\x0b.cln.AmountH\x19\x88\x01\x01\x12/\n\x15minimum_htlc_out_msat\x18\x30 \x01(\x0b\x32\x0b.cln.AmountH\x1a\x88\x01\x01\x12/\n\x15maximum_htlc_out_msat\x18\x31 \x01(\x0b\x32\x0b.cln.AmountH\x1b\x88\x01\x01\x12 \n\x13their_to_self_delay\x18! \x01(\rH\x1c\x88\x01\x01\x12\x1e\n\x11our_to_self_delay\x18\" \x01(\rH\x1d\x88\x01\x01\x12\x1f\n\x12max_accepted_htlcs\x18# \x01(\rH\x1e\x88\x01\x01\x12\x0e\n\x06status\x18% \x03(\t\x12 \n\x13in_payments_offered\x18& \x01(\x04H\x1f\x88\x01\x01\x12)\n\x0fin_offered_msat\x18\' \x01(\x0b\x32\x0b.cln.AmountH \x88\x01\x01\x12\"\n\x15in_payments_fulfilled\x18( \x01(\x04H!\x88\x01\x01\x12+\n\x11in_fulfilled_msat\x18) \x01(\x0b\x32\x0b.cln.AmountH\"\x88\x01\x01\x12!\n\x14out_payments_offered\x18* \x01(\x04H#\x88\x01\x01\x12*\n\x10out_offered_msat\x18+ \x01(\x0b\x32\x0b.cln.AmountH$\x88\x01\x01\x12#\n\x16out_payments_fulfilled\x18, \x01(\x04H%\x88\x01\x01\x12,\n\x12out_fulfilled_msat\x18- \x01(\x0b\x32\x0b.cln.AmountH&\x88\x01\x01\x12/\n\x05htlcs\x18. \x03(\x0b\x32 .cln.ListpeersPeersChannelsHtlcs\x12\x1a\n\rclose_to_addr\x18/ \x01(\tH\'\x88\x01\x01\"\xa1\x02\n\x1bListpeersPeersChannelsState\x12\x0c\n\x08OPENINGD\x10\x00\x12\x1c\n\x18\x43HANNELD_AWAITING_LOCKIN\x10\x01\x12\x13\n\x0f\x43HANNELD_NORMAL\x10\x02\x12\x1a\n\x16\x43HANNELD_SHUTTING_DOWN\x10\x03\x12\x18\n\x14\x43LOSINGD_SIGEXCHANGE\x10\x04\x12\x15\n\x11\x43LOSINGD_COMPLETE\x10\x05\x12\x17\n\x13\x41WAITING_UNILATERAL\x10\x06\x12\x16\n\x12\x46UNDING_SPEND_SEEN\x10\x07\x12\x0b\n\x07ONCHAIN\x10\x08\x12\x17\n\x13\x44UALOPEND_OPEN_INIT\x10\t\x12\x1d\n\x19\x44UALOPEND_AWAITING_LOCKIN\x10\nB\x0f\n\r_scratch_txidB\x08\n\x06_ownerB\x13\n\x11_short_channel_idB\r\n\x0b_channel_idB\x0f\n\r_funding_txidB\x11\n\x0f_funding_outnumB\x12\n\x10_initial_feerateB\x0f\n\r_last_feerateB\x0f\n\r_next_feerateB\x10\n\x0e_next_fee_stepB\x0b\n\t_close_toB\n\n\x08_privateB\t\n\x07_closerB\r\n\x0b_to_us_msatB\x11\n\x0f_min_to_us_msatB\x11\n\x0f_max_to_us_msatB\r\n\x0b_total_msatB\x10\n\x0e_fee_base_msatB\x1e\n\x1c_fee_proportional_millionthsB\x12\n\x10_dust_limit_msatB\x19\n\x17_max_total_htlc_in_msatB\x15\n\x13_their_reserve_msatB\x13\n\x11_our_reserve_msatB\x11\n\x0f_spendable_msatB\x12\n\x10_receivable_msatB\x17\n\x15_minimum_htlc_in_msatB\x18\n\x16_minimum_htlc_out_msatB\x18\n\x16_maximum_htlc_out_msatB\x16\n\x14_their_to_self_delayB\x14\n\x12_our_to_self_delayB\x15\n\x13_max_accepted_htlcsB\x16\n\x14_in_payments_offeredB\x12\n\x10_in_offered_msatB\x18\n\x16_in_payments_fulfilledB\x14\n\x12_in_fulfilled_msatB\x17\n\x15_out_payments_offeredB\x13\n\x11_out_offered_msatB\x19\n\x17_out_payments_fulfilledB\x15\n\x13_out_fulfilled_msatB\x10\n\x0e_close_to_addr\"=\n\x1dListpeersPeersChannelsFeerate\x12\r\n\x05perkw\x18\x01 \x01(\r\x12\r\n\x05perkb\x18\x02 \x01(\r\"\xc5\x01\n\x1eListpeersPeersChannelsInflight\x12\x14\n\x0c\x66unding_txid\x18\x01 \x01(\x0c\x12\x16\n\x0e\x66unding_outnum\x18\x02 \x01(\r\x12\x0f\n\x07\x66\x65\x65rate\x18\x03 \x01(\t\x12\'\n\x12total_funding_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10our_funding_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscratch_txid\x18\x06 \x01(\x0c\"\x87\x03\n\x1dListpeersPeersChannelsFunding\x12$\n\nlocal_msat\x18\x01 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12%\n\x0bremote_msat\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12%\n\x0bpushed_msat\x18\x03 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12%\n\x10local_funds_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12&\n\x11remote_funds_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\rfee_paid_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\'\n\rfee_rcvd_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x42\r\n\x0b_local_msatB\x0e\n\x0c_remote_msatB\x0e\n\x0c_pushed_msatB\x10\n\x0e_fee_paid_msatB\x10\n\x0e_fee_rcvd_msat\"[\n\x1bListpeersPeersChannelsAlias\x12\x12\n\x05local\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06remote\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\x08\n\x06_localB\t\n\x07_remote\"\xd2\x02\n\x1bListpeersPeersChannelsHtlcs\x12X\n\tdirection\x18\x01 \x01(\x0e\x32\x45.cln.ListpeersPeersChannelsHtlcs.ListpeersPeersChannelsHtlcsDirection\x12\n\n\x02id\x18\x02 \x01(\x04\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x0e\n\x06\x65xpiry\x18\x04 \x01(\r\x12\x14\n\x0cpayment_hash\x18\x05 \x01(\x0c\x12\x1a\n\rlocal_trimmed\x18\x06 \x01(\x08H\x00\x88\x01\x01\x12\x13\n\x06status\x18\x07 \x01(\tH\x01\x88\x01\x01\"7\n$ListpeersPeersChannelsHtlcsDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\x42\x10\n\x0e_local_trimmedB\t\n\x07_status\"0\n\x10ListfundsRequest\x12\x12\n\x05spent\x18\x01 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_spent\"e\n\x11ListfundsResponse\x12&\n\x07outputs\x18\x01 \x03(\x0b\x32\x15.cln.ListfundsOutputs\x12(\n\x08\x63hannels\x18\x02 \x03(\x0b\x32\x16.cln.ListfundsChannels\"\xf5\x02\n\x10ListfundsOutputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0e\n\x06output\x18\x02 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptpubkey\x18\x04 \x01(\x0c\x12\x14\n\x07\x61\x64\x64ress\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0credeemscript\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12<\n\x06status\x18\x07 \x01(\x0e\x32,.cln.ListfundsOutputs.ListfundsOutputsStatus\x12\x10\n\x08reserved\x18\t \x01(\x08\x12\x18\n\x0b\x62lockheight\x18\x08 \x01(\rH\x02\x88\x01\x01\"C\n\x16ListfundsOutputsStatus\x12\x0f\n\x0bUNCONFIRMED\x10\x00\x12\r\n\tCONFIRMED\x10\x01\x12\t\n\x05SPENT\x10\x02\x42\n\n\x08_addressB\x0f\n\r_redeemscriptB\x0e\n\x0c_blockheight\"\x83\x02\n\x11ListfundsChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12$\n\x0four_amount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12 \n\x0b\x61mount_msat\x18\x03 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0c\x66unding_txid\x18\x04 \x01(\x0c\x12\x16\n\x0e\x66unding_output\x18\x05 \x01(\r\x12\x11\n\tconnected\x18\x06 \x01(\x08\x12 \n\x05state\x18\x07 \x01(\x0e\x32\x11.cln.ChannelState\x12\x1d\n\x10short_channel_id\x18\x08 \x01(\tH\x00\x88\x01\x01\x42\x13\n\x11_short_channel_id\"\xdb\x02\n\x0eSendpayRequest\x12 \n\x05route\x18\x01 \x03(\x0b\x32\x11.cln.SendpayRoute\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x1b\n\x0epayment_secret\x18\x06 \x01(\x0cH\x03\x88\x01\x01\x12\x13\n\x06partid\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\x08 \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\t \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\x11\n\x0f_payment_secretB\t\n\x07_partidB\x0f\n\r_localofferidB\n\n\x08_groupid\"\xd1\x04\n\x0fSendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x32\n\x06status\x18\x04 \x01(\x0e\x32\".cln.SendpayResponse.SendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0f \x01(\x04H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\x12\x14\n\x07message\x18\x0e \x01(\tH\t\x88\x01\x01\"*\n\rSendpayStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\n\n\x08_message\"\\\n\x0cSendpayRoute\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\n\n\x02id\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\x12\x0f\n\x07\x63hannel\x18\x04 \x01(\t\"\x93\x01\n\x13ListchannelsRequest\x12\x1d\n\x10short_channel_id\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06source\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\t\n\x07_sourceB\x0e\n\x0c_destination\"C\n\x14ListchannelsResponse\x12+\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x19.cln.ListchannelsChannels\"\xa0\x03\n\x14ListchannelsChannels\x12\x0e\n\x06source\x18\x01 \x01(\x0c\x12\x13\n\x0b\x64\x65stination\x18\x02 \x01(\x0c\x12\x18\n\x10short_channel_id\x18\x03 \x01(\t\x12\x0e\n\x06public\x18\x04 \x01(\x08\x12 \n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.Amount\x12\x15\n\rmessage_flags\x18\x06 \x01(\r\x12\x15\n\rchannel_flags\x18\x07 \x01(\r\x12\x0e\n\x06\x61\x63tive\x18\x08 \x01(\x08\x12\x13\n\x0blast_update\x18\t \x01(\r\x12\x1d\n\x15\x62\x61se_fee_millisatoshi\x18\n \x01(\r\x12\x19\n\x11\x66\x65\x65_per_millionth\x18\x0b \x01(\r\x12\r\n\x05\x64\x65lay\x18\x0c \x01(\r\x12&\n\x11htlc_minimum_msat\x18\r \x01(\x0b\x32\x0b.cln.Amount\x12+\n\x11htlc_maximum_msat\x18\x0e \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x10\n\x08\x66\x65\x61tures\x18\x0f \x01(\x0c\x42\x14\n\x12_htlc_maximum_msat\"#\n\x10\x41\x64\x64gossipRequest\x12\x0f\n\x07message\x18\x01 \x01(\x0c\"\x13\n\x11\x41\x64\x64gossipResponse\"o\n\x17\x41utocleaninvoiceRequest\x12\x17\n\nexpired_by\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"\x81\x01\n\x18\x41utocleaninvoiceResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x17\n\nexpired_by\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x1a\n\rcycle_seconds\x18\x03 \x01(\x04H\x01\x88\x01\x01\x42\r\n\x0b_expired_byB\x10\n\x0e_cycle_seconds\"U\n\x13\x43heckmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\r\n\x05zbase\x18\x02 \x01(\t\x12\x13\n\x06pubkey\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x42\t\n\x07_pubkey\"8\n\x14\x43heckmessageResponse\x12\x10\n\x08verified\x18\x01 \x01(\x08\x12\x0e\n\x06pubkey\x18\x02 \x01(\x0c\"\xcb\x02\n\x0c\x43loseRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x11unilateraltimeout\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\tH\x01\x88\x01\x01\x12!\n\x14\x66\x65\x65_negotiation_step\x18\x04 \x01(\tH\x02\x88\x01\x01\x12)\n\rwrong_funding\x18\x05 \x01(\x0b\x32\r.cln.OutpointH\x03\x88\x01\x01\x12\x1f\n\x12\x66orce_lease_closed\x18\x06 \x01(\x08H\x04\x88\x01\x01\x12\x1e\n\x08\x66\x65\x65range\x18\x07 \x03(\x0b\x32\x0c.cln.FeerateB\x14\n\x12_unilateraltimeoutB\x0e\n\x0c_destinationB\x17\n\x15_fee_negotiation_stepB\x10\n\x0e_wrong_fundingB\x15\n\x13_force_lease_closed\"\xab\x01\n\rCloseResponse\x12/\n\titem_type\x18\x01 \x01(\x0e\x32\x1c.cln.CloseResponse.CloseType\x12\x0f\n\x02tx\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x11\n\x04txid\x18\x03 \x01(\x0cH\x01\x88\x01\x01\"5\n\tCloseType\x12\n\n\x06MUTUAL\x10\x00\x12\x0e\n\nUNILATERAL\x10\x01\x12\x0c\n\x08UNOPENED\x10\x02\x42\x05\n\x03_txB\x07\n\x05_txid\"T\n\x0e\x43onnectRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\x04host\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04port\x18\x03 \x01(\rH\x01\x88\x01\x01\x42\x07\n\x05_hostB\x07\n\x05_port\"\x8e\x01\n\x0f\x43onnectResponse\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x08\x66\x65\x61tures\x18\x02 \x01(\x0c\x12\x38\n\tdirection\x18\x03 \x01(\x0e\x32%.cln.ConnectResponse.ConnectDirection\"#\n\x10\x43onnectDirection\x12\x06\n\x02IN\x10\x00\x12\x07\n\x03OUT\x10\x01\"\xfb\x01\n\x0e\x43onnectAddress\x12\x39\n\titem_type\x18\x01 \x01(\x0e\x32&.cln.ConnectAddress.ConnectAddressType\x12\x13\n\x06socket\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04port\x18\x04 \x01(\rH\x02\x88\x01\x01\"P\n\x12\x43onnectAddressType\x12\x10\n\x0cLOCAL_SOCKET\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x42\t\n\x07_socketB\n\n\x08_addressB\x07\n\x05_port\"J\n\x14\x43reateinvoiceRequest\x12\x11\n\tinvstring\x18\x01 \x01(\t\x12\r\n\x05label\x18\x02 \x01(\t\x12\x10\n\x08preimage\x18\x03 \x01(\x0c\"\xf3\x04\n\x15\x43reateinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x04 \x01(\x0c\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12>\n\x06status\x18\x06 \x01(\x0e\x32..cln.CreateinvoiceResponse.CreateinvoiceStatus\x12\x13\n\x0b\x64\x65scription\x18\x07 \x01(\t\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\r \x01(\x0cH\x07\x88\x01\x01\x12\x17\n\npayer_note\x18\x0e \x01(\tH\x08\x88\x01\x01\"8\n\x13\x43reateinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimageB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb4\x02\n\x10\x44\x61tastoreRequest\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x13\n\x06string\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x36\n\x04mode\x18\x03 \x01(\x0e\x32#.cln.DatastoreRequest.DatastoreModeH\x02\x88\x01\x01\x12\x17\n\ngeneration\x18\x04 \x01(\x04H\x03\x88\x01\x01\"p\n\rDatastoreMode\x12\x0f\n\x0bMUST_CREATE\x10\x00\x12\x10\n\x0cMUST_REPLACE\x10\x01\x12\x15\n\x11\x43REATE_OR_REPLACE\x10\x02\x12\x0f\n\x0bMUST_APPEND\x10\x03\x12\x14\n\x10\x43REATE_OR_APPEND\x10\x04\x42\t\n\x07_stringB\x06\n\x04_hexB\x07\n\x05_modeB\r\n\x0b_generation\"\x82\x01\n\x11\x44\x61tastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\x9d\x01\n\x12\x43reateonionRequest\x12\"\n\x04hops\x18\x01 \x03(\x0b\x32\x14.cln.CreateonionHops\x12\x11\n\tassocdata\x18\x02 \x01(\x0c\x12\x18\n\x0bsession_key\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x17\n\nonion_size\x18\x04 \x01(\rH\x01\x88\x01\x01\x42\x0e\n\x0c_session_keyB\r\n\x0b_onion_size\"<\n\x13\x43reateonionResponse\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x16\n\x0eshared_secrets\x18\x02 \x03(\x0c\"2\n\x0f\x43reateonionHops\x12\x0e\n\x06pubkey\x18\x01 \x01(\x0c\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"J\n\x13\x44\x65ldatastoreRequest\x12\x0b\n\x03key\x18\x03 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x42\r\n\x0b_generation\"\x85\x01\n\x14\x44\x65ldatastoreResponse\x12\x0b\n\x03key\x18\x05 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"H\n\x18\x44\x65lexpiredinvoiceRequest\x12\x1a\n\rmaxexpirytime\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x10\n\x0e_maxexpirytime\"\x1b\n\x19\x44\x65lexpiredinvoiceResponse\"\xb6\x01\n\x11\x44\x65linvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\x12\x37\n\x06status\x18\x02 \x01(\x0e\x32\'.cln.DelinvoiceRequest.DelinvoiceStatus\x12\x15\n\x08\x64\x65sconly\x18\x03 \x01(\x08H\x00\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\x0b\n\t_desconly\"\xb7\x03\n\x12\x44\x65linvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x06\x62olt11\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x03 \x01(\tH\x01\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x05 \x01(\tH\x03\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x06 \x01(\x0c\x12\x38\n\x06status\x18\x07 \x01(\x0e\x32(.cln.DelinvoiceResponse.DelinvoiceStatus\x12\x12\n\nexpires_at\x18\x08 \x01(\x04\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\"5\n\x10\x44\x65linvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x12\n\n\x06UNPAID\x10\x02\x42\t\n\x07_bolt11B\t\n\x07_bolt12B\x0e\n\x0c_amount_msatB\x0e\n\x0c_descriptionB\x11\n\x0f_local_offer_idB\r\n\x0b_payer_note\"\xb8\x02\n\x0eInvoiceRequest\x12%\n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x10.cln.AmountOrAny\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\x12\x13\n\x06\x65xpiry\x18\x07 \x01(\x04H\x00\x88\x01\x01\x12\x11\n\tfallbacks\x18\x04 \x03(\t\x12\x15\n\x08preimage\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\"\n\x15\x65xposeprivatechannels\x18\x08 \x01(\x08H\x02\x88\x01\x01\x12\x11\n\x04\x63ltv\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x19\n\x0c\x64\x65schashonly\x18\t \x01(\x08H\x04\x88\x01\x01\x42\t\n\x07_expiryB\x0b\n\t_preimageB\x18\n\x16_exposeprivatechannelsB\x07\n\x05_cltvB\x0f\n\r_deschashonly\"\xe7\x02\n\x0fInvoiceResponse\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x16\n\x0epayment_secret\x18\x03 \x01(\x0c\x12\x12\n\nexpires_at\x18\x04 \x01(\x04\x12\x1d\n\x10warning_capacity\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\x1c\n\x0fwarning_offline\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x1d\n\x10warning_deadends\x18\x07 \x01(\tH\x02\x88\x01\x01\x12#\n\x16warning_private_unused\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0bwarning_mpp\x18\t \x01(\tH\x04\x88\x01\x01\x42\x13\n\x11_warning_capacityB\x12\n\x10_warning_offlineB\x13\n\x11_warning_deadendsB\x19\n\x17_warning_private_unusedB\x0e\n\x0c_warning_mpp\"#\n\x14ListdatastoreRequest\x12\x0b\n\x03key\x18\x02 \x03(\t\"G\n\x15ListdatastoreResponse\x12.\n\tdatastore\x18\x01 \x03(\x0b\x32\x1b.cln.ListdatastoreDatastore\"\x87\x01\n\x16ListdatastoreDatastore\x12\x0b\n\x03key\x18\x01 \x03(\t\x12\x17\n\ngeneration\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x10\n\x03hex\x18\x03 \x01(\x0cH\x01\x88\x01\x01\x12\x13\n\x06string\x18\x04 \x01(\tH\x02\x88\x01\x01\x42\r\n\x0b_generationB\x06\n\x04_hexB\t\n\x07_string\"\xa9\x01\n\x13ListinvoicesRequest\x12\x12\n\x05label\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x16\n\tinvstring\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x03 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08offer_id\x18\x04 \x01(\tH\x03\x88\x01\x01\x42\x08\n\x06_labelB\x0c\n\n_invstringB\x0f\n\r_payment_hashB\x0b\n\t_offer_id\"C\n\x14ListinvoicesResponse\x12+\n\x08invoices\x18\x01 \x03(\x0b\x32\x19.cln.ListinvoicesInvoices\"\x94\x05\n\x14ListinvoicesInvoices\x12\r\n\x05label\x18\x01 \x01(\t\x12\x18\n\x0b\x64\x65scription\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListinvoicesInvoices.ListinvoicesInvoicesStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x03\x88\x01\x01\x12\x1b\n\x0elocal_offer_id\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x17\n\npayer_note\x18\n \x01(\tH\x05\x88\x01\x01\x12\x16\n\tpay_index\x18\x0b \x01(\x04H\x06\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x07\x88\x01\x01\x12\x14\n\x07paid_at\x18\r \x01(\x04H\x08\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0e \x01(\x0cH\t\x88\x01\x01\"?\n\x1aListinvoicesInvoicesStatus\x12\n\n\x06UNPAID\x10\x00\x12\x08\n\x04PAID\x10\x01\x12\x0b\n\x07\x45XPIRED\x10\x02\x42\x0e\n\x0c_descriptionB\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x11\n\x0f_local_offer_idB\r\n\x0b_payer_noteB\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\xdc\x02\n\x10SendonionRequest\x12\r\n\x05onion\x18\x01 \x01(\x0c\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\x05label\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x16\n\x0eshared_secrets\x18\x05 \x03(\x0c\x12\x13\n\x06partid\x18\x06 \x01(\rH\x01\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x02\x88\x01\x01\x12%\n\x0b\x61mount_msat\x18\x0c \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\t \x01(\x0cH\x04\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\n \x01(\x0cH\x05\x88\x01\x01\x12\x14\n\x07groupid\x18\x0b \x01(\x04H\x06\x88\x01\x01\x42\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_localofferidB\n\n\x08_groupid\"\x8b\x04\n\x11SendonionResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x02 \x01(\x0c\x12\x36\n\x06status\x18\x03 \x01(\x0e\x32&.cln.SendonionResponse.SendonionStatus\x12%\n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x06 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\t \x01(\tH\x03\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\n \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\r \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0b \x01(\x0cH\x06\x88\x01\x01\x12\x14\n\x07message\x18\x0c \x01(\tH\x07\x88\x01\x01\",\n\x0fSendonionStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\t\n\x07_bolt12B\t\n\x07_partidB\x13\n\x11_payment_preimageB\n\n\x08_message\"Q\n\x12SendonionFirst_hop\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x03 \x01(\r\"\xeb\x01\n\x13ListsendpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12@\n\x06status\x18\x03 \x01(\x0e\x32+.cln.ListsendpaysRequest.ListsendpaysStatusH\x02\x88\x01\x01\";\n\x12ListsendpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"C\n\x14ListsendpaysResponse\x12+\n\x08payments\x18\x01 \x03(\x0b\x32\x19.cln.ListsendpaysPayments\"\xd4\x04\n\x14ListsendpaysPayments\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0f\n\x07groupid\x18\x02 \x01(\x04\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x44\n\x06status\x18\x04 \x01(\x0e\x32\x34.cln.ListsendpaysPayments.ListsendpaysPaymentsStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x01\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\n \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0e \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0b \x01(\tH\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\x12\x17\n\nerroronion\x18\r \x01(\x0cH\x07\x88\x01\x01\"C\n\x1aListsendpaysPaymentsStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x13\n\x11_payment_preimageB\r\n\x0b_erroronion\"\x19\n\x17ListtransactionsRequest\"S\n\x18ListtransactionsResponse\x12\x37\n\x0ctransactions\x18\x01 \x03(\x0b\x32!.cln.ListtransactionsTransactions\"\x9a\x02\n\x1cListtransactionsTransactions\x12\x0c\n\x04hash\x18\x01 \x01(\x0c\x12\r\n\x05rawtx\x18\x02 \x01(\x0c\x12\x13\n\x0b\x62lockheight\x18\x03 \x01(\r\x12\x0f\n\x07txindex\x18\x04 \x01(\r\x12\x14\n\x07\x63hannel\x18\x06 \x01(\tH\x00\x88\x01\x01\x12\x10\n\x08locktime\x18\x07 \x01(\r\x12\x0f\n\x07version\x18\x08 \x01(\r\x12\x37\n\x06inputs\x18\t \x03(\x0b\x32\'.cln.ListtransactionsTransactionsInputs\x12\x39\n\x07outputs\x18\n \x03(\x0b\x32(.cln.ListtransactionsTransactionsOutputsB\n\n\x08_channel\"\x84\x04\n\"ListtransactionsTransactionsInputs\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\r\n\x05index\x18\x02 \x01(\r\x12\x10\n\x08sequence\x18\x03 \x01(\r\x12\x66\n\titem_type\x18\x04 \x01(\x0e\x32N.cln.ListtransactionsTransactionsInputs.ListtransactionsTransactionsInputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x96\x02\n&ListtransactionsTransactionsInputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xa0\x04\n#ListtransactionsTransactionsOutputs\x12\r\n\x05index\x18\x01 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12\x14\n\x0cscriptPubKey\x18\x03 \x01(\x0c\x12h\n\titem_type\x18\x04 \x01(\x0e\x32P.cln.ListtransactionsTransactionsOutputs.ListtransactionsTransactionsOutputsTypeH\x00\x88\x01\x01\x12\x14\n\x07\x63hannel\x18\x05 \x01(\tH\x01\x88\x01\x01\"\x97\x02\n\'ListtransactionsTransactionsOutputsType\x12\n\n\x06THEIRS\x10\x00\x12\x0b\n\x07\x44\x45POSIT\x10\x01\x12\x0c\n\x08WITHDRAW\x10\x02\x12\x13\n\x0f\x43HANNEL_FUNDING\x10\x03\x12\x18\n\x14\x43HANNEL_MUTUAL_CLOSE\x10\x04\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CLOSE\x10\x05\x12\x11\n\rCHANNEL_SWEEP\x10\x06\x12\x18\n\x14\x43HANNEL_HTLC_SUCCESS\x10\x07\x12\x18\n\x14\x43HANNEL_HTLC_TIMEOUT\x10\x08\x12\x13\n\x0f\x43HANNEL_PENALTY\x10\t\x12\x1c\n\x18\x43HANNEL_UNILATERAL_CHEAT\x10\nB\x0c\n\n_item_typeB\n\n\x08_channel\"\xd8\x03\n\nPayRequest\x12\x0e\n\x06\x62olt11\x18\x01 \x01(\t\x12%\n\x0b\x61mount_msat\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x12\n\x05label\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nriskfactor\x18\x08 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x03\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x04\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x05\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x06\x88\x01\x01\x12\x19\n\x0clocalofferid\x18\t \x01(\x0cH\x07\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\n \x03(\t\x12 \n\x06maxfee\x18\x0b \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0c \x01(\tH\t\x88\x01\x01\x42\x0e\n\x0c_amount_msatB\x08\n\x06_labelB\r\n\x0b_riskfactorB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\x0f\n\r_localofferidB\t\n\x07_maxfeeB\x0e\n\x0c_description\"\xfb\x02\n\x0bPayResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12*\n\x06status\x18\t \x01(\x0e\x32\x1a.cln.PayResponse.PayStatus\"2\n\tPayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x12\x0b\n\x07PENDING\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"*\n\x10ListnodesRequest\x12\x0f\n\x02id\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x05\n\x03_id\"7\n\x11ListnodesResponse\x12\"\n\x05nodes\x18\x01 \x03(\x0b\x32\x13.cln.ListnodesNodes\"\xe1\x01\n\x0eListnodesNodes\x12\x0e\n\x06nodeid\x18\x01 \x01(\x0c\x12\x1b\n\x0elast_timestamp\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x12\n\x05\x61lias\x18\x03 \x01(\tH\x01\x88\x01\x01\x12\x12\n\x05\x63olor\x18\x04 \x01(\x0cH\x02\x88\x01\x01\x12\x15\n\x08\x66\x65\x61tures\x18\x05 \x01(\x0cH\x03\x88\x01\x01\x12/\n\taddresses\x18\x06 \x03(\x0b\x32\x1c.cln.ListnodesNodesAddressesB\x11\n\x0f_last_timestampB\x08\n\x06_aliasB\x08\n\x06_colorB\x0b\n\t_features\"\xf7\x01\n\x17ListnodesNodesAddresses\x12K\n\titem_type\x18\x01 \x01(\x0e\x32\x38.cln.ListnodesNodesAddresses.ListnodesNodesAddressesType\x12\x0c\n\x04port\x18\x02 \x01(\r\x12\x14\n\x07\x61\x64\x64ress\x18\x03 \x01(\tH\x00\x88\x01\x01\"_\n\x1bListnodesNodesAddressesType\x12\x07\n\x03\x44NS\x10\x00\x12\x08\n\x04IPV4\x10\x01\x12\x08\n\x04IPV6\x10\x02\x12\t\n\x05TORV2\x10\x03\x12\t\n\x05TORV3\x10\x04\x12\r\n\tWEBSOCKET\x10\x05\x42\n\n\x08_address\"g\n\x15WaitanyinvoiceRequest\x12\x1a\n\rlastpay_index\x18\x01 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x07timeout\x18\x02 \x01(\x04H\x01\x88\x01\x01\x42\x10\n\x0e_lastpay_indexB\n\n\x08_timeout\"\x93\x04\n\x16WaitanyinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12@\n\x06status\x18\x04 \x01(\x0e\x32\x30.cln.WaitanyinvoiceResponse.WaitanyinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"-\n\x14WaitanyinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"#\n\x12WaitinvoiceRequest\x12\r\n\x05label\x18\x01 \x01(\t\"\x87\x04\n\x13WaitinvoiceResponse\x12\r\n\x05label\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitinvoiceResponse.WaitinvoiceStatus\x12\x12\n\nexpires_at\x18\x05 \x01(\x04\x12%\n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x07 \x01(\tH\x01\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x08 \x01(\tH\x02\x88\x01\x01\x12\x16\n\tpay_index\x18\t \x01(\x04H\x03\x88\x01\x01\x12.\n\x14\x61mount_received_msat\x18\n \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\x14\n\x07paid_at\x18\x0b \x01(\x04H\x05\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\x0c \x01(\x0cH\x06\x88\x01\x01\"*\n\x11WaitinvoiceStatus\x12\x08\n\x04PAID\x10\x00\x12\x0b\n\x07\x45XPIRED\x10\x01\x42\x0e\n\x0c_amount_msatB\t\n\x07_bolt11B\t\n\x07_bolt12B\x0c\n\n_pay_indexB\x17\n\x15_amount_received_msatB\n\n\x08_paid_atB\x13\n\x11_payment_preimage\"\x8e\x01\n\x12WaitsendpayRequest\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x14\n\x07timeout\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06partid\x18\x02 \x01(\x04H\x01\x88\x01\x01\x12\x14\n\x07groupid\x18\x04 \x01(\x04H\x02\x88\x01\x01\x42\n\n\x08_timeoutB\t\n\x07_partidB\n\n\x08_groupid\"\xb2\x04\n\x13WaitsendpayResponse\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x07groupid\x18\x02 \x01(\x04H\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12:\n\x06status\x18\x04 \x01(\x0e\x32*.cln.WaitsendpayResponse.WaitsendpayStatus\x12%\n\x0b\x61mount_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x01\x88\x01\x01\x12\x18\n\x0b\x64\x65stination\x18\x06 \x01(\x0cH\x02\x88\x01\x01\x12\x12\n\ncreated_at\x18\x07 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0e \x01(\x01H\x03\x88\x01\x01\x12%\n\x10\x61mount_sent_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\t \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06partid\x18\n \x01(\x04H\x05\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x0b \x01(\tH\x06\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x0c \x01(\tH\x07\x88\x01\x01\x12\x1d\n\x10payment_preimage\x18\r \x01(\x0cH\x08\x88\x01\x01\"!\n\x11WaitsendpayStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\n\n\x08_groupidB\x0e\n\x0c_amount_msatB\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_partidB\t\n\x07_bolt11B\t\n\x07_bolt12B\x13\n\x11_payment_preimage\"\x9e\x01\n\x0eNewaddrRequest\x12@\n\x0b\x61\x64\x64resstype\x18\x01 \x01(\x0e\x32&.cln.NewaddrRequest.NewaddrAddresstypeH\x00\x88\x01\x01\":\n\x12NewaddrAddresstype\x12\n\n\x06\x42\x45\x43H32\x10\x00\x12\x0f\n\x0bP2SH_SEGWIT\x10\x01\x12\x07\n\x03\x41LL\x10\x02\x42\x0e\n\x0c_addresstype\"[\n\x0fNewaddrResponse\x12\x13\n\x06\x62\x65\x63h32\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x18\n\x0bp2sh_segwit\x18\x02 \x01(\tH\x01\x88\x01\x01\x42\t\n\x07_bech32B\x0e\n\x0c_p2sh_segwit\"\xca\x01\n\x0fWithdrawRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\t\x12&\n\x07satoshi\x18\x02 \x01(\x0b\x32\x10.cln.AmountOrAllH\x00\x88\x01\x01\x12\"\n\x07\x66\x65\x65rate\x18\x05 \x01(\x0b\x32\x0c.cln.FeerateH\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_satoshiB\n\n\x08_feerateB\n\n\x08_minconf\":\n\x10WithdrawResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0c\n\x04psbt\x18\x03 \x01(\t\"\xcc\x02\n\x0eKeysendRequest\x12\x13\n\x0b\x64\x65stination\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\n \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\x05label\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x1a\n\rmaxfeepercent\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12\x16\n\tretry_for\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x15\n\x08maxdelay\x18\x06 \x01(\rH\x03\x88\x01\x01\x12#\n\texemptfee\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12+\n\nroutehints\x18\x08 \x01(\x0b\x32\x12.cln.RoutehintListH\x05\x88\x01\x01\x42\x08\n\x06_labelB\x10\n\x0e_maxfeepercentB\x0c\n\n_retry_forB\x0b\n\t_maxdelayB\x0c\n\n_exemptfeeB\r\n\x0b_routehints\"\xf2\x02\n\x0fKeysendResponse\x12\x18\n\x10payment_preimage\x18\x01 \x01(\x0c\x12\x18\n\x0b\x64\x65stination\x18\x02 \x01(\x0cH\x00\x88\x01\x01\x12\x14\n\x0cpayment_hash\x18\x03 \x01(\x0c\x12\x12\n\ncreated_at\x18\x04 \x01(\x01\x12\r\n\x05parts\x18\x05 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x10\x61mount_sent_msat\x18\x07 \x01(\x0b\x32\x0b.cln.Amount\x12\'\n\x1awarning_partial_completion\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x32\n\x06status\x18\t \x01(\x0e\x32\".cln.KeysendResponse.KeysendStatus\"\x1d\n\rKeysendStatus\x12\x0c\n\x08\x43OMPLETE\x10\x00\x42\x0e\n\x0c_destinationB\x1d\n\x1b_warning_partial_completion\"\x12\n\x10KeysendExtratlvs\"\xbc\x02\n\x0f\x46undpsbtRequest\x12!\n\x07satoshi\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x14\n\x07minconf\x18\x04 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\x08 \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_minconfB\n\n\x08_reserveB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10\x46undpsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.FundpsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14\x46undpsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\"A\n\x0fSendpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x14\n\x07reserve\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\n\n\x08_reserve\",\n\x10SendpsbtResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"1\n\x0fSignpsbtRequest\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x10\n\x08signonly\x18\x02 \x03(\r\"\'\n\x10SignpsbtResponse\x12\x13\n\x0bsigned_psbt\x18\x01 \x01(\t\"\xdb\x02\n\x0fUtxopsbtRequest\x12\x1c\n\x07satoshi\x18\x01 \x01(\x0b\x32\x0b.cln.Amount\x12\x1d\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.Feerate\x12\x13\n\x0bstartweight\x18\x03 \x01(\r\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.Outpoint\x12\x14\n\x07reserve\x18\x05 \x01(\rH\x00\x88\x01\x01\x12\x17\n\nreservedok\x18\x08 \x01(\x08H\x01\x88\x01\x01\x12\x15\n\x08locktime\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x1f\n\x12min_witness_weight\x18\x07 \x01(\rH\x03\x88\x01\x01\x12\x1d\n\x10\x65xcess_as_change\x18\t \x01(\x08H\x04\x88\x01\x01\x42\n\n\x08_reserveB\r\n\x0b_reservedokB\x0b\n\t_locktimeB\x15\n\x13_min_witness_weightB\x13\n\x11_excess_as_change\"\xd9\x01\n\x10UtxopsbtResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x16\n\x0e\x66\x65\x65rate_per_kw\x18\x02 \x01(\r\x12\x1e\n\x16\x65stimated_final_weight\x18\x03 \x01(\r\x12 \n\x0b\x65xcess_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\x1a\n\rchange_outnum\x18\x05 \x01(\rH\x00\x88\x01\x01\x12/\n\x0creservations\x18\x06 \x03(\x0b\x32\x19.cln.UtxopsbtReservationsB\x10\n\x0e_change_outnum\"u\n\x14UtxopsbtReservations\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\x12\x0c\n\x04vout\x18\x02 \x01(\r\x12\x14\n\x0cwas_reserved\x18\x03 \x01(\x08\x12\x10\n\x08reserved\x18\x04 \x01(\x08\x12\x19\n\x11reserved_to_block\x18\x05 \x01(\r\" \n\x10TxdiscardRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"6\n\x11TxdiscardResponse\x12\x13\n\x0bunsigned_tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\"\xa4\x01\n\x10TxprepareRequest\x12 \n\x07outputs\x18\x05 \x03(\x0b\x32\x0f.cln.OutputDesc\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x14\n\x07minconf\x18\x03 \x01(\rH\x01\x88\x01\x01\x12\x1c\n\x05utxos\x18\x04 \x03(\x0b\x32\r.cln.OutpointB\n\n\x08_feerateB\n\n\x08_minconf\"D\n\x11TxprepareResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\x13\n\x0bunsigned_tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"\x1d\n\rTxsendRequest\x12\x0c\n\x04txid\x18\x01 \x01(\x0c\"8\n\x0eTxsendResponse\x12\x0c\n\x04psbt\x18\x01 \x01(\t\x12\n\n\x02tx\x18\x02 \x01(\x0c\x12\x0c\n\x04txid\x18\x03 \x01(\x0c\"=\n\x11\x44isconnectRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x12\n\x05\x66orce\x18\x02 \x01(\x08H\x00\x88\x01\x01\x42\x08\n\x06_force\"\x14\n\x12\x44isconnectResponse\"k\n\x0f\x46\x65\x65ratesRequest\x12\x31\n\x05style\x18\x01 \x01(\x0e\x32\".cln.FeeratesRequest.FeeratesStyle\"%\n\rFeeratesStyle\x12\t\n\x05PERKB\x10\x00\x12\t\n\x05PERKW\x10\x01\"V\n\x10\x46\x65\x65ratesResponse\x12%\n\x18warning_missing_feerates\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x1b\n\x19_warning_missing_feerates\"\xc3\x02\n\rFeeratesPerkb\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc3\x02\n\rFeeratesPerkw\x12\x16\n\x0emin_acceptable\x18\x01 \x01(\r\x12\x16\n\x0emax_acceptable\x18\x02 \x01(\r\x12\x14\n\x07opening\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x19\n\x0cmutual_close\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x1d\n\x10unilateral_close\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x1a\n\rdelayed_to_us\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x1c\n\x0fhtlc_resolution\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07penalty\x18\x08 \x01(\rH\x05\x88\x01\x01\x42\n\n\x08_openingB\x0f\n\r_mutual_closeB\x13\n\x11_unilateral_closeB\x10\n\x0e_delayed_to_usB\x12\n\x10_htlc_resolutionB\n\n\x08_penalty\"\xc1\x01\n\x1d\x46\x65\x65ratesOnchain_fee_estimates\x12 \n\x18opening_channel_satoshis\x18\x01 \x01(\x04\x12\x1d\n\x15mutual_close_satoshis\x18\x02 \x01(\x04\x12!\n\x19unilateral_close_satoshis\x18\x03 \x01(\x04\x12\x1d\n\x15htlc_timeout_satoshis\x18\x04 \x01(\x04\x12\x1d\n\x15htlc_success_satoshis\x18\x05 \x01(\x04\"\xe5\x03\n\x12\x46undchannelRequest\x12\n\n\x02id\x18\t \x01(\x0c\x12 \n\x06\x61mount\x18\x01 \x01(\x0b\x32\x10.cln.AmountOrAll\x12\"\n\x07\x66\x65\x65rate\x18\x02 \x01(\x0b\x32\x0c.cln.FeerateH\x00\x88\x01\x01\x12\x15\n\x08\x61nnounce\x18\x03 \x01(\x08H\x01\x88\x01\x01\x12\x14\n\x07minconf\x18\n \x01(\rH\x02\x88\x01\x01\x12#\n\tpush_msat\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x15\n\x08\x63lose_to\x18\x06 \x01(\tH\x04\x88\x01\x01\x12%\n\x0brequest_amt\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\x12\x1a\n\rcompact_lease\x18\x08 \x01(\tH\x06\x88\x01\x01\x12\x1c\n\x05utxos\x18\x0b \x03(\x0b\x32\r.cln.Outpoint\x12\x15\n\x08mindepth\x18\x0c \x01(\rH\x07\x88\x01\x01\x12!\n\x07reserve\x18\r \x01(\x0b\x32\x0b.cln.AmountH\x08\x88\x01\x01\x42\n\n\x08_feerateB\x0b\n\t_announceB\n\n\x08_minconfB\x0c\n\n_push_msatB\x0b\n\t_close_toB\x0e\n\x0c_request_amtB\x10\n\x0e_compact_leaseB\x0b\n\t_mindepthB\n\n\x08_reserve\"\x9b\x01\n\x13\x46undchannelResponse\x12\n\n\x02tx\x18\x01 \x01(\x0c\x12\x0c\n\x04txid\x18\x02 \x01(\x0c\x12\x0e\n\x06outnum\x18\x03 \x01(\r\x12\x12\n\nchannel_id\x18\x04 \x01(\x0c\x12\x15\n\x08\x63lose_to\x18\x05 \x01(\x0cH\x00\x88\x01\x01\x12\x15\n\x08mindepth\x18\x06 \x01(\rH\x01\x88\x01\x01\x42\x0b\n\t_close_toB\x0b\n\t_mindepth\"\xec\x01\n\x0fGetrouteRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12 \n\x0b\x61mount_msat\x18\t \x01(\x0b\x32\x0b.cln.Amount\x12\x12\n\nriskfactor\x18\x03 \x01(\x04\x12\x11\n\x04\x63ltv\x18\x04 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06\x66romid\x18\x05 \x01(\x0cH\x01\x88\x01\x01\x12\x18\n\x0b\x66uzzpercent\x18\x06 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07\x65xclude\x18\x07 \x03(\t\x12\x14\n\x07maxhops\x18\x08 \x01(\rH\x03\x88\x01\x01\x42\x07\n\x05_cltvB\t\n\x07_fromidB\x0e\n\x0c_fuzzpercentB\n\n\x08_maxhops\"5\n\x10GetrouteResponse\x12!\n\x05route\x18\x01 \x03(\x0b\x32\x12.cln.GetrouteRoute\"\xc5\x01\n\rGetrouteRoute\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63hannel\x18\x02 \x01(\t\x12\x11\n\tdirection\x18\x03 \x01(\r\x12 \n\x0b\x61mount_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12\r\n\x05\x64\x65lay\x18\x05 \x01(\r\x12\x34\n\x05style\x18\x06 \x01(\x0e\x32%.cln.GetrouteRoute.GetrouteRouteStyle\"\x1d\n\x12GetrouteRouteStyle\x12\x07\n\x03TLV\x10\x00\"\x82\x02\n\x13ListforwardsRequest\x12@\n\x06status\x18\x01 \x01(\x0e\x32+.cln.ListforwardsRequest.ListforwardsStatusH\x00\x88\x01\x01\x12\x17\n\nin_channel\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_channel\x18\x03 \x01(\tH\x02\x88\x01\x01\"L\n\x12ListforwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\x42\t\n\x07_statusB\r\n\x0b_in_channelB\x0e\n\x0c_out_channel\"C\n\x14ListforwardsResponse\x12+\n\x08\x66orwards\x18\x01 \x03(\x0b\x32\x19.cln.ListforwardsForwards\"\xde\x04\n\x14ListforwardsForwards\x12\x12\n\nin_channel\x18\x01 \x01(\t\x12\x17\n\nin_htlc_id\x18\n \x01(\x04H\x00\x88\x01\x01\x12\x1c\n\x07in_msat\x18\x02 \x01(\x0b\x32\x0b.cln.Amount\x12\x44\n\x06status\x18\x03 \x01(\x0e\x32\x34.cln.ListforwardsForwards.ListforwardsForwardsStatus\x12\x15\n\rreceived_time\x18\x04 \x01(\x01\x12\x18\n\x0bout_channel\x18\x05 \x01(\tH\x01\x88\x01\x01\x12\x18\n\x0bout_htlc_id\x18\x0b \x01(\x04H\x02\x88\x01\x01\x12G\n\x05style\x18\t \x01(\x0e\x32\x33.cln.ListforwardsForwards.ListforwardsForwardsStyleH\x03\x88\x01\x01\x12\"\n\x08\x66\x65\x65_msat\x18\x07 \x01(\x0b\x32\x0b.cln.AmountH\x04\x88\x01\x01\x12\"\n\x08out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.AmountH\x05\x88\x01\x01\"T\n\x1aListforwardsForwardsStatus\x12\x0b\n\x07OFFERED\x10\x00\x12\x0b\n\x07SETTLED\x10\x01\x12\x10\n\x0cLOCAL_FAILED\x10\x02\x12\n\n\x06\x46\x41ILED\x10\x03\"0\n\x19ListforwardsForwardsStyle\x12\n\n\x06LEGACY\x10\x00\x12\x07\n\x03TLV\x10\x01\x42\r\n\x0b_in_htlc_idB\x0e\n\x0c_out_channelB\x0e\n\x0c_out_htlc_idB\x08\n\x06_styleB\x0b\n\t_fee_msatB\x0b\n\t_out_msat\"\xdb\x01\n\x0fListpaysRequest\x12\x13\n\x06\x62olt11\x18\x01 \x01(\tH\x00\x88\x01\x01\x12\x19\n\x0cpayment_hash\x18\x02 \x01(\x0cH\x01\x88\x01\x01\x12\x38\n\x06status\x18\x03 \x01(\x0e\x32#.cln.ListpaysRequest.ListpaysStatusH\x02\x88\x01\x01\"7\n\x0eListpaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\x0c\n\x08\x43OMPLETE\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\x42\t\n\x07_bolt11B\x0f\n\r_payment_hashB\t\n\x07_status\"3\n\x10ListpaysResponse\x12\x1f\n\x04pays\x18\x01 \x03(\x0b\x32\x11.cln.ListpaysPays\"\x87\x04\n\x0cListpaysPays\x12\x14\n\x0cpayment_hash\x18\x01 \x01(\x0c\x12\x34\n\x06status\x18\x02 \x01(\x0e\x32$.cln.ListpaysPays.ListpaysPaysStatus\x12\x18\n\x0b\x64\x65stination\x18\x03 \x01(\x0cH\x00\x88\x01\x01\x12\x12\n\ncreated_at\x18\x04 \x01(\x04\x12\x19\n\x0c\x63ompleted_at\x18\x0c \x01(\x04H\x01\x88\x01\x01\x12\x12\n\x05label\x18\x05 \x01(\tH\x02\x88\x01\x01\x12\x13\n\x06\x62olt11\x18\x06 \x01(\tH\x03\x88\x01\x01\x12\x18\n\x0b\x64\x65scription\x18\x0b \x01(\tH\x04\x88\x01\x01\x12\x13\n\x06\x62olt12\x18\x07 \x01(\tH\x05\x88\x01\x01\x12\x15\n\x08preimage\x18\r \x01(\x0cH\x06\x88\x01\x01\x12\x1c\n\x0fnumber_of_parts\x18\x0e \x01(\x04H\x07\x88\x01\x01\x12\x17\n\nerroronion\x18\n \x01(\x0cH\x08\x88\x01\x01\";\n\x12ListpaysPaysStatus\x12\x0b\n\x07PENDING\x10\x00\x12\n\n\x06\x46\x41ILED\x10\x01\x12\x0c\n\x08\x43OMPLETE\x10\x02\x42\x0e\n\x0c_destinationB\x0f\n\r_completed_atB\x08\n\x06_labelB\t\n\x07_bolt11B\x0e\n\x0c_descriptionB\t\n\x07_bolt12B\x0b\n\t_preimageB\x12\n\x10_number_of_partsB\r\n\x0b_erroronion\"Y\n\x0bPingRequest\x12\n\n\x02id\x18\x01 \x01(\x0c\x12\x10\n\x03len\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x16\n\tpongbytes\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x06\n\x04_lenB\x0c\n\n_pongbytes\"\x1e\n\x0cPingResponse\x12\x0e\n\x06totlen\x18\x01 \x01(\r\"\xf8\x01\n\x11SetchannelRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12!\n\x07\x66\x65\x65\x62\x61se\x18\x02 \x01(\x0b\x32\x0b.cln.AmountH\x00\x88\x01\x01\x12\x13\n\x06\x66\x65\x65ppm\x18\x03 \x01(\rH\x01\x88\x01\x01\x12!\n\x07htlcmin\x18\x04 \x01(\x0b\x32\x0b.cln.AmountH\x02\x88\x01\x01\x12!\n\x07htlcmax\x18\x05 \x01(\x0b\x32\x0b.cln.AmountH\x03\x88\x01\x01\x12\x19\n\x0c\x65nforcedelay\x18\x06 \x01(\rH\x04\x88\x01\x01\x42\n\n\x08_feebaseB\t\n\x07_feeppmB\n\n\x08_htlcminB\n\n\x08_htlcmaxB\x0f\n\r_enforcedelay\"?\n\x12SetchannelResponse\x12)\n\x08\x63hannels\x18\x01 \x03(\x0b\x32\x17.cln.SetchannelChannels\"\x94\x03\n\x12SetchannelChannels\x12\x0f\n\x07peer_id\x18\x01 \x01(\x0c\x12\x12\n\nchannel_id\x18\x02 \x01(\x0c\x12\x1d\n\x10short_channel_id\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\"\n\rfee_base_msat\x18\x04 \x01(\x0b\x32\x0b.cln.Amount\x12#\n\x1b\x66\x65\x65_proportional_millionths\x18\x05 \x01(\r\x12*\n\x15minimum_htlc_out_msat\x18\x06 \x01(\x0b\x32\x0b.cln.Amount\x12$\n\x17warning_htlcmin_too_low\x18\x07 \x01(\tH\x01\x88\x01\x01\x12*\n\x15maximum_htlc_out_msat\x18\x08 \x01(\x0b\x32\x0b.cln.Amount\x12%\n\x18warning_htlcmax_too_high\x18\t \x01(\tH\x02\x88\x01\x01\x42\x13\n\x11_short_channel_idB\x1a\n\x18_warning_htlcmin_too_lowB\x1b\n\x19_warning_htlcmax_too_high\"%\n\x12SignmessageRequest\x12\x0f\n\x07message\x18\x01 \x01(\t\"F\n\x13SignmessageResponse\x12\x11\n\tsignature\x18\x01 \x01(\x0c\x12\r\n\x05recid\x18\x02 \x01(\x0c\x12\r\n\x05zbase\x18\x03 \x01(\t\"\r\n\x0bStopRequest\"\x0e\n\x0cStopResponse2\xb1\x17\n\x04Node\x12\x36\n\x07Getinfo\x12\x13.cln.GetinfoRequest\x1a\x14.cln.GetinfoResponse\"\x00\x12<\n\tListPeers\x12\x15.cln.ListpeersRequest\x1a\x16.cln.ListpeersResponse\"\x00\x12<\n\tListFunds\x12\x15.cln.ListfundsRequest\x1a\x16.cln.ListfundsResponse\"\x00\x12\x36\n\x07SendPay\x12\x13.cln.SendpayRequest\x1a\x14.cln.SendpayResponse\"\x00\x12\x45\n\x0cListChannels\x12\x18.cln.ListchannelsRequest\x1a\x19.cln.ListchannelsResponse\"\x00\x12<\n\tAddGossip\x12\x15.cln.AddgossipRequest\x1a\x16.cln.AddgossipResponse\"\x00\x12Q\n\x10\x41utoCleanInvoice\x12\x1c.cln.AutocleaninvoiceRequest\x1a\x1d.cln.AutocleaninvoiceResponse\"\x00\x12\x45\n\x0c\x43heckMessage\x12\x18.cln.CheckmessageRequest\x1a\x19.cln.CheckmessageResponse\"\x00\x12\x30\n\x05\x43lose\x12\x11.cln.CloseRequest\x1a\x12.cln.CloseResponse\"\x00\x12:\n\x0b\x43onnectPeer\x12\x13.cln.ConnectRequest\x1a\x14.cln.ConnectResponse\"\x00\x12H\n\rCreateInvoice\x12\x19.cln.CreateinvoiceRequest\x1a\x1a.cln.CreateinvoiceResponse\"\x00\x12<\n\tDatastore\x12\x15.cln.DatastoreRequest\x1a\x16.cln.DatastoreResponse\"\x00\x12\x42\n\x0b\x43reateOnion\x12\x17.cln.CreateonionRequest\x1a\x18.cln.CreateonionResponse\"\x00\x12\x45\n\x0c\x44\x65lDatastore\x12\x18.cln.DeldatastoreRequest\x1a\x19.cln.DeldatastoreResponse\"\x00\x12T\n\x11\x44\x65lExpiredInvoice\x12\x1d.cln.DelexpiredinvoiceRequest\x1a\x1e.cln.DelexpiredinvoiceResponse\"\x00\x12?\n\nDelInvoice\x12\x16.cln.DelinvoiceRequest\x1a\x17.cln.DelinvoiceResponse\"\x00\x12\x36\n\x07Invoice\x12\x13.cln.InvoiceRequest\x1a\x14.cln.InvoiceResponse\"\x00\x12H\n\rListDatastore\x12\x19.cln.ListdatastoreRequest\x1a\x1a.cln.ListdatastoreResponse\"\x00\x12\x45\n\x0cListInvoices\x12\x18.cln.ListinvoicesRequest\x1a\x19.cln.ListinvoicesResponse\"\x00\x12<\n\tSendOnion\x12\x15.cln.SendonionRequest\x1a\x16.cln.SendonionResponse\"\x00\x12\x45\n\x0cListSendPays\x12\x18.cln.ListsendpaysRequest\x1a\x19.cln.ListsendpaysResponse\"\x00\x12Q\n\x10ListTransactions\x12\x1c.cln.ListtransactionsRequest\x1a\x1d.cln.ListtransactionsResponse\"\x00\x12*\n\x03Pay\x12\x0f.cln.PayRequest\x1a\x10.cln.PayResponse\"\x00\x12<\n\tListNodes\x12\x15.cln.ListnodesRequest\x1a\x16.cln.ListnodesResponse\"\x00\x12K\n\x0eWaitAnyInvoice\x12\x1a.cln.WaitanyinvoiceRequest\x1a\x1b.cln.WaitanyinvoiceResponse\"\x00\x12\x42\n\x0bWaitInvoice\x12\x17.cln.WaitinvoiceRequest\x1a\x18.cln.WaitinvoiceResponse\"\x00\x12\x42\n\x0bWaitSendPay\x12\x17.cln.WaitsendpayRequest\x1a\x18.cln.WaitsendpayResponse\"\x00\x12\x36\n\x07NewAddr\x12\x13.cln.NewaddrRequest\x1a\x14.cln.NewaddrResponse\"\x00\x12\x39\n\x08Withdraw\x12\x14.cln.WithdrawRequest\x1a\x15.cln.WithdrawResponse\"\x00\x12\x36\n\x07KeySend\x12\x13.cln.KeysendRequest\x1a\x14.cln.KeysendResponse\"\x00\x12\x39\n\x08\x46undPsbt\x12\x14.cln.FundpsbtRequest\x1a\x15.cln.FundpsbtResponse\"\x00\x12\x39\n\x08SendPsbt\x12\x14.cln.SendpsbtRequest\x1a\x15.cln.SendpsbtResponse\"\x00\x12\x39\n\x08SignPsbt\x12\x14.cln.SignpsbtRequest\x1a\x15.cln.SignpsbtResponse\"\x00\x12\x39\n\x08UtxoPsbt\x12\x14.cln.UtxopsbtRequest\x1a\x15.cln.UtxopsbtResponse\"\x00\x12<\n\tTxDiscard\x12\x15.cln.TxdiscardRequest\x1a\x16.cln.TxdiscardResponse\"\x00\x12<\n\tTxPrepare\x12\x15.cln.TxprepareRequest\x1a\x16.cln.TxprepareResponse\"\x00\x12\x33\n\x06TxSend\x12\x12.cln.TxsendRequest\x1a\x13.cln.TxsendResponse\"\x00\x12?\n\nDisconnect\x12\x16.cln.DisconnectRequest\x1a\x17.cln.DisconnectResponse\"\x00\x12\x39\n\x08\x46\x65\x65rates\x12\x14.cln.FeeratesRequest\x1a\x15.cln.FeeratesResponse\"\x00\x12\x42\n\x0b\x46undChannel\x12\x17.cln.FundchannelRequest\x1a\x18.cln.FundchannelResponse\"\x00\x12\x39\n\x08GetRoute\x12\x14.cln.GetrouteRequest\x1a\x15.cln.GetrouteResponse\"\x00\x12\x45\n\x0cListForwards\x12\x18.cln.ListforwardsRequest\x1a\x19.cln.ListforwardsResponse\"\x00\x12\x39\n\x08ListPays\x12\x14.cln.ListpaysRequest\x1a\x15.cln.ListpaysResponse\"\x00\x12-\n\x04Ping\x12\x10.cln.PingRequest\x1a\x11.cln.PingResponse\"\x00\x12?\n\nSetChannel\x12\x16.cln.SetchannelRequest\x1a\x17.cln.SetchannelResponse\"\x00\x12\x42\n\x0bSignMessage\x12\x17.cln.SignmessageRequest\x1a\x18.cln.SignmessageResponse\"\x00\x12-\n\x04Stop\x12\x10.cln.StopRequest\x1a\x11.cln.StopResponse\"\x00\x62\x06proto3') @@ -1412,19 +1412,19 @@ _PINGRESPONSE._serialized_start=27857 _PINGRESPONSE._serialized_end=27887 _SETCHANNELREQUEST._serialized_start=27890 - _SETCHANNELREQUEST._serialized_end=28094 - _SETCHANNELRESPONSE._serialized_start=28096 - _SETCHANNELRESPONSE._serialized_end=28159 - _SETCHANNELCHANNELS._serialized_start=28162 - _SETCHANNELCHANNELS._serialized_end=28566 - _SIGNMESSAGEREQUEST._serialized_start=28568 - _SIGNMESSAGEREQUEST._serialized_end=28605 - _SIGNMESSAGERESPONSE._serialized_start=28607 - _SIGNMESSAGERESPONSE._serialized_end=28677 - _STOPREQUEST._serialized_start=28679 - _STOPREQUEST._serialized_end=28692 - _STOPRESPONSE._serialized_start=28694 - _STOPRESPONSE._serialized_end=28708 - _NODE._serialized_start=28711 - _NODE._serialized_end=31704 + _SETCHANNELREQUEST._serialized_end=28138 + _SETCHANNELRESPONSE._serialized_start=28140 + _SETCHANNELRESPONSE._serialized_end=28203 + _SETCHANNELCHANNELS._serialized_start=28206 + _SETCHANNELCHANNELS._serialized_end=28610 + _SIGNMESSAGEREQUEST._serialized_start=28612 + _SIGNMESSAGEREQUEST._serialized_end=28649 + _SIGNMESSAGERESPONSE._serialized_start=28651 + _SIGNMESSAGERESPONSE._serialized_end=28721 + _STOPREQUEST._serialized_start=28723 + _STOPREQUEST._serialized_end=28736 + _STOPRESPONSE._serialized_start=28738 + _STOPRESPONSE._serialized_end=28752 + _NODE._serialized_start=28755 + _NODE._serialized_end=31748 # @@protoc_insertion_point(module_scope) diff --git a/tests/test_pay.py b/tests/test_pay.py index f760b5f797c9..d348496fdf3f 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2046,8 +2046,17 @@ def channel_get_config(scid): # check if negative fees raise error and DB keeps values # JSONRPC2_INVALID_PARAMS := -32602 + from pyln.client import LightningRpc with pytest.raises(RpcError, match=r'-32602'): - l1.rpc.setchannel(scid, -1, -1) + # Need to bypass pyln since it'd check args locally. We also + # have to sidestep the schema validation, it attempts to + # instantiate Millisatoshis and fails due to the non-negative + # constraint. + LightningRpc.call(l1.rpc, 'setchannel', { + "id": scid, + "feebase": -1, + "feeppm": -1 + }) # test if zero fees is possible result = l1.rpc.setchannel(scid, 0, 0) @@ -2092,11 +2101,18 @@ def channel_get_config(scid): # check if 'ppm' values greater than u32_max fail with pytest.raises(RpcError, match=r'-32602.*ppm: should be an integer: invalid token'): - l1.rpc.setchannel(scid, 0, 2**32) + LightningRpc.call(l1.rpc, 'setchannel', payload={ + "id": scid, + 'feebase': 0, + 'feeppm': 2**32, + }) # check if 'base' values greater than u32_max fail with pytest.raises(RpcError, match=r'-32602.*base: exceeds u32 max: invalid token'): - l1.rpc.setchannel(scid, 2**32) + LightningRpc.call(l1.rpc, 'setchannel', payload={ + "id": scid, + "feebase": 2**32, + }) @pytest.mark.developer("gossip without DEVELOPER=1 is slow") From 49fe1c8ed7aea1f109b4bcc4944ddcd0cc30117b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 29 Sep 2022 13:18:53 +0930 Subject: [PATCH 1515/1530] lightningd: have `makesecret` take `hex` or `string` (just like `datastore`) Changelog-Added: JSON-RPC: `makesecret` can take a string argument instead of hex. Signed-off-by: Rusty Russell --- doc/lightning-makesecret.7.md | 10 +++------- doc/schemas/makesecret.request.json | 8 +++++--- lightningd/hsm_control.c | 20 +++++++++++++++++--- tests/test_misc.py | 4 ++++ 4 files changed, 29 insertions(+), 13 deletions(-) diff --git a/doc/lightning-makesecret.7.md b/doc/lightning-makesecret.7.md index 2a713ded4c2c..fc54dd514e5a 100644 --- a/doc/lightning-makesecret.7.md +++ b/doc/lightning-makesecret.7.md @@ -4,14 +4,15 @@ lightning-makesecret -- Command for deriving pseudorandom key from HSM SYNOPSIS -------- -**makesecret** *hex* +**makesecret** [*hex*] [*string*] DESCRIPTION ----------- The **makesecret** RPC command derives a secret key from the HSM_secret. -The *hex* can be any hex data. +One of *hex* or *string* must be specified: *hex* can be any hex data, +*string* is a UTF-8 string interpreted literally. RETURN VALUE ------------ @@ -32,11 +33,6 @@ AUTHOR Aditya <> is mainly responsible. -SEE ALSO --------- - -lightning-getsharedsecret(7) - RESOURCES --------- diff --git a/doc/schemas/makesecret.request.json b/doc/schemas/makesecret.request.json index c26e3ce4b944..5059babd01d5 100644 --- a/doc/schemas/makesecret.request.json +++ b/doc/schemas/makesecret.request.json @@ -2,13 +2,15 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, - "required": [ - "hex" - ], + "required": [], "properties": { "hex": { "type": "hex", "description": "This will be used for deriving the secret" + }, + "string": { + "type": "string", + "description": "This will be used for deriving the secret" } } } diff --git a/lightningd/hsm_control.c b/lightningd/hsm_control.c index c5252fc3bd09..f360d813a649 100644 --- a/lightningd/hsm_control.c +++ b/lightningd/hsm_control.c @@ -131,16 +131,30 @@ static struct command_result *json_makesecret(struct command *cmd, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { - u8 *info; + u8 *data; + const char *strdata; struct json_stream *response; struct secret secret; if (!param(cmd, buffer, params, - p_req("hex", param_bin_from_hex, &info), + p_opt("hex", param_bin_from_hex, &data), + p_opt("string", param_string, &strdata), NULL)) return command_param_failed(); - u8 *msg = towire_hsmd_derive_secret(cmd, info); + if (strdata) { + if (data) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Cannot have both hex and string"); + data = tal_dup_arr(cmd, u8, (u8 *)strdata, strlen(strdata), 0); + } else { + if (!data) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Must have either hex or string"); + } + + + u8 *msg = towire_hsmd_derive_secret(cmd, data); if (!wire_sync_write(cmd->ld->hsm_fd, take(msg))) return command_fail(cmd, LIGHTNINGD, "Could not write to HSM: %s", strerror(errno)); diff --git a/tests/test_misc.py b/tests/test_misc.py index cb54b58d3b7f..b301286a3a0c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2281,6 +2281,10 @@ def test_makesecret(node_factory): assert l1.rpc.makesecret(hex="736362207365637265")["secret"] != secret assert l1.rpc.makesecret(hex="7363622073656372657401")["secret"] != secret + # Using string works! + assert l1.rpc.makesecret(string="scb secret")["secret"] == secret + assert l1.rpc.makesecret(None, "scb secret")["secret"] == secret + def test_staticbackup(node_factory): """ From 342e330b565fd3326f8a046dfa2c0e63e8785c24 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 29 Sep 2022 13:19:03 +0930 Subject: [PATCH 1516/1530] doc: update references to old BOLTs repo. This reveals that common/test/run-bolt12_merkle-json.c was broken! Signed-off-by: Rusty Russell --- .github/workflows/bsd.yml | 2 +- README.md | 4 +-- common/blindedpath.c | 2 +- common/features.c | 12 +++---- common/test/run-bolt12_decode.c | 2 +- common/test/run-bolt12_merkle-json.c | 36 ++----------------- common/test/run-bolt12_period.c | 2 +- .../test/run-route_blinding_override_test.c | 2 +- common/test/run-route_blinding_test.c | 2 +- contrib/docker/Dockerfile.tester | 2 +- contrib/docker/scripts/build.sh | 4 +-- contrib/pyln-proto/tests/test_wire.py | 2 +- doc/FAQ.md | 8 ++--- doc/HACKING.md | 4 +-- doc/PLUGINS.md | 14 ++++---- doc/lightning-decodepay.7.md | 2 +- doc/lightning-listchannels.7.md | 2 +- doc/lightning-listpeers.7.md | 2 +- doc/lightning-sendonion.7.md | 2 +- doc/lightning-sendonionmessage.7.md | 2 +- 20 files changed, 38 insertions(+), 70 deletions(-) diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index dbc59fe085e1..fee739519814 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -69,7 +69,7 @@ jobs: pytest-custom-exit-code==0.3.0 \ pytest-json-report - git clone https://github.com/lightningnetwork/lightning-rfc.git ../lightning-rfc + git clone https://github.com/lightning/bolts.git ../bolts # fatal: unsafe repository ('/Users/runner/work/lightning/lightning' is owned by someone else) git config --global --add safe.directory `pwd` git submodule update --init --recursive diff --git a/README.md b/README.md index e445bbbdab8a..325a99b49f55 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ lightning-cli invoice